Repository: siderolabs/talos Branch: main Commit: 0a47f40b3cdf Files: 3195 Total size: 19.7 MB Directory structure: gitextract_l8ced00e/ ├── .codecov.yml ├── .conform.yaml ├── .dockerignore ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── BUG_REPORT.md │ │ ├── FEATURE_REQUEST.md │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ ├── renovate.json │ └── workflows/ │ ├── ci.yaml │ ├── dispatch.yaml │ ├── grype-scan-cron.yaml │ ├── integration-airgapped-cron.yaml │ ├── integration-aws-cron.yaml │ ├── integration-aws-nvidia-nonfree-lts-cron.yaml │ ├── integration-aws-nvidia-nonfree-production-cron.yaml │ ├── integration-aws-nvidia-oss-lts-cron.yaml │ ├── integration-aws-nvidia-oss-production-cron.yaml │ ├── integration-cilium-cron.yaml │ ├── integration-conformance-cron.yaml │ ├── integration-conformance-enforcing-cron.yaml │ ├── integration-embedded-cron.yaml │ ├── integration-extensions-cron.yaml │ ├── integration-gcp-cron.yaml │ ├── integration-image-cache-cron.yaml │ ├── integration-image-factory-cron.yaml │ ├── integration-images-cron.yaml │ ├── integration-misc-0-cron.yaml │ ├── integration-misc-1-cron.yaml │ ├── integration-misc-1-enforcing-cron.yaml │ ├── integration-misc-2-cron.yaml │ ├── integration-misc-3-cron.yaml │ ├── integration-misc-3-enforcing-cron.yaml │ ├── integration-misc-4-cron.yaml │ ├── integration-misc-4-enforcing-cron.yaml │ ├── integration-provision-0-cron.yaml │ ├── integration-provision-1-cron.yaml │ ├── integration-provision-2-cron.yaml │ ├── integration-provision-3-cron.yaml │ ├── integration-qemu-cron.yaml │ ├── integration-qemu-csi-longhorn-cron.yaml │ ├── integration-qemu-csi-openebs-cron.yaml │ ├── integration-qemu-csi-rook-ceph-cron.yaml │ ├── integration-qemu-encrypted-vip-cron.yaml │ ├── integration-qemu-enforcing-cron.yaml │ ├── integration-qemu-race-cron.yaml │ ├── integration-reproducibility-test-cron.yaml │ ├── integration-trusted-boot-cron.yaml │ ├── integration-trusted-boot-enforcing-cron.yaml │ ├── lock.yaml │ ├── lock.yml │ ├── slack-notify-ci-failure.yaml │ ├── slack-notify.yaml │ ├── stale.yaml │ ├── stale.yml │ └── update-homebrew.yaml ├── .gitignore ├── .golangci.yml ├── .kres.yaml ├── .markdownlint.json ├── .secrets.yaml ├── .sops.yaml ├── .textlintrc.json ├── ADOPTERS.md ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── api/ │ ├── buf.gen.yaml │ ├── buf.yaml │ ├── cluster/ │ │ └── cluster.proto │ ├── common/ │ │ └── common.proto │ ├── docs.markdown.tmpl │ ├── inspect/ │ │ └── inspect.proto │ ├── lock.binpb │ ├── machine/ │ │ ├── debug.proto │ │ ├── image.proto │ │ ├── lifecycle.proto │ │ └── machine.proto │ ├── resource/ │ │ ├── config/ │ │ │ └── config.proto │ │ ├── definitions/ │ │ │ ├── block/ │ │ │ │ └── block.proto │ │ │ ├── cluster/ │ │ │ │ └── cluster.proto │ │ │ ├── cri/ │ │ │ │ └── cri.proto │ │ │ ├── enums/ │ │ │ │ └── enums.proto │ │ │ ├── etcd/ │ │ │ │ └── etcd.proto │ │ │ ├── extensions/ │ │ │ │ └── extensions.proto │ │ │ ├── files/ │ │ │ │ └── files.proto │ │ │ ├── hardware/ │ │ │ │ └── hardware.proto │ │ │ ├── k8s/ │ │ │ │ └── k8s.proto │ │ │ ├── kubeaccess/ │ │ │ │ └── kubeaccess.proto │ │ │ ├── kubespan/ │ │ │ │ └── kubespan.proto │ │ │ ├── network/ │ │ │ │ └── network.proto │ │ │ ├── perf/ │ │ │ │ └── perf.proto │ │ │ ├── proto/ │ │ │ │ └── proto.proto │ │ │ ├── runtime/ │ │ │ │ └── runtime.proto │ │ │ ├── secrets/ │ │ │ │ └── secrets.proto │ │ │ ├── security/ │ │ │ │ └── security.proto │ │ │ ├── siderolink/ │ │ │ │ └── siderolink.proto │ │ │ ├── time/ │ │ │ │ └── time.proto │ │ │ └── v1alpha1/ │ │ │ └── v1alpha1.proto │ │ └── network/ │ │ └── device_config.proto │ ├── security/ │ │ └── security.proto │ ├── storage/ │ │ └── storage.proto │ ├── time/ │ │ └── time.proto │ └── vendor/ │ └── google/ │ ├── api/ │ │ └── expr/ │ │ └── v1alpha1/ │ │ ├── checked.proto │ │ ├── eval.proto │ │ ├── explain.proto │ │ ├── syntax.proto │ │ └── value.proto │ └── rpc/ │ └── status.proto ├── cmd/ │ ├── installer/ │ │ ├── cmd/ │ │ │ ├── imager/ │ │ │ │ └── root.go │ │ │ └── installer/ │ │ │ ├── install.go │ │ │ └── root.go │ │ ├── main.go │ │ └── pkg/ │ │ └── install/ │ │ ├── errata.go │ │ ├── install.go │ │ ├── meta_value.go │ │ ├── meta_value_test.go │ │ └── preflight.go │ └── talosctl/ │ ├── acompat/ │ │ └── acompat.go │ ├── cmd/ │ │ ├── common/ │ │ │ └── common.go │ │ ├── completion.go │ │ ├── constants/ │ │ │ └── constants.go │ │ ├── docs.go │ │ ├── mgmt/ │ │ │ ├── cluster/ │ │ │ │ ├── cluster.go │ │ │ │ ├── create/ │ │ │ │ │ ├── clusterops/ │ │ │ │ │ │ ├── configmaker/ │ │ │ │ │ │ │ ├── config_maker.go │ │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ │ ├── makers/ │ │ │ │ │ │ │ │ │ ├── common.go │ │ │ │ │ │ │ │ │ ├── common_test.go │ │ │ │ │ │ │ │ │ ├── docker.go │ │ │ │ │ │ │ │ │ ├── docker_test.go │ │ │ │ │ │ │ │ │ ├── maker.go │ │ │ │ │ │ │ │ │ ├── qemu.go │ │ │ │ │ │ │ │ │ └── qemu_test.go │ │ │ │ │ │ │ │ └── siderolinkbuilder/ │ │ │ │ │ │ │ │ ├── builder.go │ │ │ │ │ │ │ │ ├── helpers_other.go │ │ │ │ │ │ │ │ └── helpers_supported.go │ │ │ │ │ │ │ └── preset/ │ │ │ │ │ │ │ ├── disk_image_preset.go │ │ │ │ │ │ │ ├── iso_preset.go │ │ │ │ │ │ │ ├── iso_secureboot_preset.go │ │ │ │ │ │ │ ├── maintenance_preset.go │ │ │ │ │ │ │ ├── preset.go │ │ │ │ │ │ │ ├── preset_test.go │ │ │ │ │ │ │ └── pxe_preset.go │ │ │ │ │ │ ├── options.go │ │ │ │ │ │ ├── options_linux.go │ │ │ │ │ │ └── options_other.go │ │ │ │ │ ├── cmd.go │ │ │ │ │ ├── cmd_dev.go │ │ │ │ │ ├── cmd_docker.go │ │ │ │ │ ├── cmd_qemu.go │ │ │ │ │ ├── create.go │ │ │ │ │ ├── create_dev.go │ │ │ │ │ ├── create_docker.go │ │ │ │ │ ├── create_qemu.go │ │ │ │ │ └── flags/ │ │ │ │ │ ├── agent.go │ │ │ │ │ ├── disks.go │ │ │ │ │ ├── disks_test.go │ │ │ │ │ ├── virtiofs.go │ │ │ │ │ └── virtiofs_test.go │ │ │ │ ├── destroy.go │ │ │ │ ├── internal/ │ │ │ │ │ └── firewallpatch/ │ │ │ │ │ └── firewallpatch.go │ │ │ │ └── show.go │ │ │ ├── debug/ │ │ │ │ ├── air-gapped.go │ │ │ │ ├── debug.go │ │ │ │ └── httproot/ │ │ │ │ └── debug.yaml │ │ │ ├── dhcpd_launch.go │ │ │ ├── dnsd_launch.go │ │ │ ├── gen/ │ │ │ │ ├── ca.go │ │ │ │ ├── config.go │ │ │ │ ├── crt.go │ │ │ │ ├── csr.go │ │ │ │ ├── gen.go │ │ │ │ ├── key.go │ │ │ │ ├── keypair.go │ │ │ │ ├── secrets.go │ │ │ │ └── secureboot.go │ │ │ ├── inject/ │ │ │ │ ├── inject.go │ │ │ │ └── serviceaccount.go │ │ │ ├── json_logs_launch.go │ │ │ ├── kms_launch.go │ │ │ ├── loadbalancer_launch.go │ │ │ ├── machineconfig/ │ │ │ │ ├── gen.go │ │ │ │ ├── machineconfig.go │ │ │ │ └── patch.go │ │ │ ├── qemu_launch.go │ │ │ ├── root.go │ │ │ ├── siderolink_launch.go │ │ │ ├── validate.go │ │ │ └── virtiofsd_launch.go │ │ ├── root.go │ │ └── talos/ │ │ ├── apply-config.go │ │ ├── bootstrap.go │ │ ├── cgroups.go │ │ ├── cgroupsprinter/ │ │ │ ├── presets/ │ │ │ │ ├── cpu.yaml │ │ │ │ ├── cpuset.yaml │ │ │ │ ├── io.yaml │ │ │ │ ├── memory.yaml │ │ │ │ ├── process.yaml │ │ │ │ ├── psi.yaml │ │ │ │ └── swap.yaml │ │ │ ├── presets.go │ │ │ ├── presets_test.go │ │ │ ├── schema.go │ │ │ └── tree.go │ │ ├── config.go │ │ ├── config_test.go │ │ ├── conformance.go │ │ ├── containers.go │ │ ├── copy.go │ │ ├── crashdump.go │ │ ├── dashboard.go │ │ ├── debug.go │ │ ├── disks.go │ │ ├── diskusage.go │ │ ├── dmesg.go │ │ ├── edit.go │ │ ├── etcd.go │ │ ├── events.go │ │ ├── get.go │ │ ├── health.go │ │ ├── image.go │ │ ├── inspect.go │ │ ├── install.go │ │ ├── interfaces.go │ │ ├── kubeconfig.go │ │ ├── lifecycle/ │ │ │ └── lifecycle.go │ │ ├── list.go │ │ ├── logs.go │ │ ├── memory.go │ │ ├── meta.go │ │ ├── mounts.go │ │ ├── multiplex/ │ │ │ ├── multiplex.go │ │ │ ├── stream.go │ │ │ └── unary.go │ │ ├── netstat.go │ │ ├── output/ │ │ │ ├── json.go │ │ │ ├── jsonpath.go │ │ │ ├── jsonpath_test.go │ │ │ ├── output.go │ │ │ ├── table.go │ │ │ └── yaml.go │ │ ├── patch.go │ │ ├── pcap.go │ │ ├── processes.go │ │ ├── pull/ │ │ │ └── pull.go │ │ ├── read.go │ │ ├── reboot.go │ │ ├── reset.go │ │ ├── restart.go │ │ ├── rollback.go │ │ ├── root.go │ │ ├── rotate-ca.go │ │ ├── routes.go │ │ ├── service.go │ │ ├── shutdown.go │ │ ├── stats.go │ │ ├── support.go │ │ ├── time.go │ │ ├── track.go │ │ ├── upgrade-k8s.go │ │ ├── upgrade.go │ │ ├── version.go │ │ └── wipe.go │ ├── main.go │ └── pkg/ │ ├── mgmt/ │ │ └── helpers/ │ │ ├── airgapped.go │ │ ├── artifacts.go │ │ ├── helpers_test.go │ │ ├── image.go │ │ └── wireguard.go │ └── talos/ │ ├── action/ │ │ ├── node.go │ │ └── tracker.go │ ├── artifacts/ │ │ ├── arch.go │ │ ├── fetch.go │ │ └── images.go │ ├── global/ │ │ └── client.go │ ├── helpers/ │ │ ├── archive.go │ │ ├── checks.go │ │ ├── confirm.go │ │ ├── error.go │ │ ├── helpers_test.go │ │ ├── labels.go │ │ ├── mode.go │ │ ├── resources.go │ │ └── stream.go │ └── yamlstrip/ │ ├── testdata/ │ │ ├── malformed.in.yaml │ │ ├── malformed.out.yaml │ │ ├── multidoc.in.yaml │ │ └── multidoc.out.yaml │ ├── yamlstrip.go │ └── yamlstrip_test.go ├── go.mod ├── go.sum ├── go.work ├── hack/ │ ├── boilerplate.txt │ ├── cleanup.sh │ ├── cloud-image-uploader/ │ │ ├── README.md │ │ ├── aws.go │ │ ├── factory.go │ │ ├── gcp.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── options.go │ │ ├── role-policy.json │ │ └── trust-policy.json │ ├── cloud-image-uploader.sh │ ├── containerd.toml │ ├── cri-containerd.toml │ ├── cri-plugin.part │ ├── extra-modules.conf │ ├── fix-artifacts.sh │ ├── labeled-squashfs.sh │ ├── lvm.conf │ ├── modules-amd64.txt │ ├── modules-arm64.txt │ ├── nfsmount.conf │ ├── release.sh │ ├── release.toml │ ├── sbom.sh │ ├── start-registry-proxies.sh │ ├── test/ │ │ ├── cis/ │ │ │ ├── kube-bench-master.yaml │ │ │ └── kube-bench-node.yaml │ │ ├── e2e-aws-prepare.sh │ │ ├── e2e-aws.sh │ │ ├── e2e-azure.sh │ │ ├── e2e-cloud-tf.sh │ │ ├── e2e-docker.sh │ │ ├── e2e-embedded.sh │ │ ├── e2e-gcp-prepare.sh │ │ ├── e2e-gcp.sh │ │ ├── e2e-image-factory.sh │ │ ├── e2e-iso.sh │ │ ├── e2e-qemu.sh │ │ ├── e2e.sh │ │ ├── patches/ │ │ │ ├── airgapped-timesync.yaml │ │ │ ├── cilium-kubeproxy.yaml │ │ │ ├── cilium-no-kubeproxy.yaml │ │ │ ├── dm-raid-module.yaml │ │ │ ├── ephemeral-min-max.yaml │ │ │ ├── ephemeral-nvme.yaml │ │ │ ├── extensions.yaml │ │ │ ├── flannel-netpol.yaml │ │ │ ├── image-cache-encrypted.yaml │ │ │ ├── image-cache.yaml │ │ │ ├── image-verification.yaml │ │ │ ├── longhorn-cp.yaml │ │ │ ├── longhorn.yaml │ │ │ ├── node-address-v2.yaml │ │ │ ├── openebs-cp.yaml │ │ │ ├── openebs.yaml │ │ │ ├── proxied-registry.yaml │ │ │ ├── rook-ceph.yaml │ │ │ ├── usernamespace.yaml │ │ │ └── watchdog.yaml │ │ ├── provision-tests.sh │ │ └── tfvars/ │ │ ├── aws.jq │ │ ├── azure.jq │ │ ├── gcp.jq │ │ └── nvidia.yaml │ ├── udevd/ │ │ ├── 40-vm-hotadd.rules │ │ ├── 90-selinux.rules │ │ └── 99-default.link │ └── zoneinfo/ │ └── Etc/ │ └── UTC ├── internal/ │ ├── app/ │ │ ├── apid/ │ │ │ ├── debug.go │ │ │ ├── main.go │ │ │ ├── pkg/ │ │ │ │ ├── backend/ │ │ │ │ │ ├── apid.go │ │ │ │ │ ├── apid_factory.go │ │ │ │ │ ├── apid_factory_test.go │ │ │ │ │ ├── apid_test.go │ │ │ │ │ └── backend.go │ │ │ │ ├── director/ │ │ │ │ │ ├── director.go │ │ │ │ │ ├── director_test.go │ │ │ │ │ ├── local_address.go │ │ │ │ │ └── mocks_test.go │ │ │ │ └── provider/ │ │ │ │ ├── provider.go │ │ │ │ └── provider_test.go │ │ │ └── service.go │ │ ├── auditd/ │ │ │ └── auditd.go │ │ ├── dashboard/ │ │ │ └── main.go │ │ ├── debug/ │ │ │ ├── container_streams.go │ │ │ └── debug.go │ │ ├── images/ │ │ │ ├── images.go │ │ │ └── verify.go │ │ ├── init/ │ │ │ └── main.go │ │ ├── internal/ │ │ │ ├── ctrhelper/ │ │ │ │ └── ctrhelper.go │ │ │ └── machinehelper/ │ │ │ └── machinehelper.go │ │ ├── lifecycle/ │ │ │ ├── container.go │ │ │ └── lifecycle.go │ │ ├── machined/ │ │ │ ├── internal/ │ │ │ │ └── server/ │ │ │ │ └── v1alpha1/ │ │ │ │ ├── v1alpha1_cluster.go │ │ │ │ ├── v1alpha1_images.go │ │ │ │ ├── v1alpha1_inspect.go │ │ │ │ ├── v1alpha1_meta.go │ │ │ │ ├── v1alpha1_monitoring.go │ │ │ │ ├── v1alpha1_server.go │ │ │ │ ├── v1alpha1_time.go │ │ │ │ └── v1alpha1_time_test.go │ │ │ ├── main.go │ │ │ ├── pkg/ │ │ │ │ ├── adapters/ │ │ │ │ │ ├── block/ │ │ │ │ │ │ ├── mount.go │ │ │ │ │ │ ├── volume_config.go │ │ │ │ │ │ └── volume_mount_status.go │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── cluster.go │ │ │ │ │ │ ├── identity.go │ │ │ │ │ │ └── identity_test.go │ │ │ │ │ ├── hardware/ │ │ │ │ │ │ ├── hardware.go │ │ │ │ │ │ ├── memorymodule.go │ │ │ │ │ │ ├── processor.go │ │ │ │ │ │ └── system_information.go │ │ │ │ │ ├── k8s/ │ │ │ │ │ │ ├── k8s.go │ │ │ │ │ │ ├── manifest.go │ │ │ │ │ │ ├── manifest_test.go │ │ │ │ │ │ ├── static_pod.go │ │ │ │ │ │ ├── static_pod_status.go │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ └── list.yaml │ │ │ │ │ ├── kubespan/ │ │ │ │ │ │ ├── identity.go │ │ │ │ │ │ ├── identity_test.go │ │ │ │ │ │ ├── kubespan.go │ │ │ │ │ │ ├── peer_status.go │ │ │ │ │ │ └── peer_status_test.go │ │ │ │ │ ├── network/ │ │ │ │ │ │ ├── bond_master_spec.go │ │ │ │ │ │ ├── bond_master_spec_test.go │ │ │ │ │ │ ├── bridge_master_spec.go │ │ │ │ │ │ ├── bridge_master_spec_test.go │ │ │ │ │ │ ├── ipset.go │ │ │ │ │ │ ├── ipset_test.go │ │ │ │ │ │ ├── network.go │ │ │ │ │ │ ├── nftables_rule.go │ │ │ │ │ │ ├── nftables_rule_test.go │ │ │ │ │ │ ├── vlan_spec.go │ │ │ │ │ │ ├── vlan_spec_test.go │ │ │ │ │ │ ├── vrf_master_spec.go │ │ │ │ │ │ ├── vrf_master_spec_test.go │ │ │ │ │ │ ├── wireguard_spec.go │ │ │ │ │ │ └── wireguard_spec_test.go │ │ │ │ │ ├── perf/ │ │ │ │ │ │ ├── cpu.go │ │ │ │ │ │ ├── mem.go │ │ │ │ │ │ └── perf.go │ │ │ │ │ ├── secrets/ │ │ │ │ │ │ ├── encryption_salt.go │ │ │ │ │ │ ├── encryption_salt_test.go │ │ │ │ │ │ └── secrets.go │ │ │ │ │ └── wireguard/ │ │ │ │ │ └── wireguard.go │ │ │ │ ├── automaton/ │ │ │ │ │ ├── blockautomaton/ │ │ │ │ │ │ ├── blockmachine.go │ │ │ │ │ │ ├── volume_mount.go │ │ │ │ │ │ └── volume_mount_test.go │ │ │ │ │ └── machine.go │ │ │ │ ├── controllers/ │ │ │ │ │ ├── block/ │ │ │ │ │ │ ├── block.go │ │ │ │ │ │ ├── devices.go │ │ │ │ │ │ ├── devices_test.go │ │ │ │ │ │ ├── discovery.go │ │ │ │ │ │ ├── disks.go │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ ├── inotify/ │ │ │ │ │ │ │ │ ├── inotify.go │ │ │ │ │ │ │ │ └── inotify_test.go │ │ │ │ │ │ │ ├── kobject/ │ │ │ │ │ │ │ │ ├── kobject.go │ │ │ │ │ │ │ │ └── kobject_test.go │ │ │ │ │ │ │ ├── sysblock/ │ │ │ │ │ │ │ │ ├── sysblock.go │ │ │ │ │ │ │ │ └── sysblock_test.go │ │ │ │ │ │ │ └── volumes/ │ │ │ │ │ │ │ ├── close.go │ │ │ │ │ │ │ ├── disk.go │ │ │ │ │ │ │ ├── disk_test.go │ │ │ │ │ │ │ ├── encrypt.go │ │ │ │ │ │ │ ├── encryption_meta.go │ │ │ │ │ │ │ ├── encryption_meta_test.go │ │ │ │ │ │ │ ├── format.go │ │ │ │ │ │ │ ├── grow.go │ │ │ │ │ │ │ ├── grow_test.go │ │ │ │ │ │ │ ├── helpers_test.go │ │ │ │ │ │ │ ├── locate.go │ │ │ │ │ │ │ ├── locate_test.go │ │ │ │ │ │ │ ├── partition.go │ │ │ │ │ │ │ ├── partition_test.go │ │ │ │ │ │ │ ├── volumeconfig/ │ │ │ │ │ │ │ │ ├── system_volumes.go │ │ │ │ │ │ │ │ ├── system_volumes_test.go │ │ │ │ │ │ │ │ ├── types.go │ │ │ │ │ │ │ │ ├── user_volumes.go │ │ │ │ │ │ │ │ ├── user_volumes_test.go │ │ │ │ │ │ │ │ ├── volume_config_builder.go │ │ │ │ │ │ │ │ └── volume_config_builder_test.go │ │ │ │ │ │ │ ├── volumes.go │ │ │ │ │ │ │ └── volumes_test.go │ │ │ │ │ │ ├── lvm.go │ │ │ │ │ │ ├── mount.go │ │ │ │ │ │ ├── mount_request.go │ │ │ │ │ │ ├── mount_request_test.go │ │ │ │ │ │ ├── mount_status.go │ │ │ │ │ │ ├── mount_status_test.go │ │ │ │ │ │ ├── mount_test.go │ │ │ │ │ │ ├── swap_status.go │ │ │ │ │ │ ├── swap_status_test.go │ │ │ │ │ │ ├── symlinks.go │ │ │ │ │ │ ├── system_disk.go │ │ │ │ │ │ ├── system_disk_test.go │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ └── procswaps.txt │ │ │ │ │ │ ├── user_disk_config.go │ │ │ │ │ │ ├── user_disk_config_test.go │ │ │ │ │ │ ├── volume_config.go │ │ │ │ │ │ ├── volume_config_test.go │ │ │ │ │ │ ├── volume_manager.go │ │ │ │ │ │ ├── zswap_config.go │ │ │ │ │ │ └── zswap_status.go │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── affiliate_merge.go │ │ │ │ │ │ ├── affiliate_merge_test.go │ │ │ │ │ │ ├── cluster.go │ │ │ │ │ │ ├── cluster_test.go │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── config_test.go │ │ │ │ │ │ ├── discovery_service.go │ │ │ │ │ │ ├── discovery_service_test.go │ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ │ ├── endpoint_test.go │ │ │ │ │ │ ├── info.go │ │ │ │ │ │ ├── info_test.go │ │ │ │ │ │ ├── kubernetes_pull.go │ │ │ │ │ │ ├── kubernetes_push.go │ │ │ │ │ │ ├── local_affiliate.go │ │ │ │ │ │ ├── local_affiliate_test.go │ │ │ │ │ │ ├── member.go │ │ │ │ │ │ ├── member_test.go │ │ │ │ │ │ ├── node_identity.go │ │ │ │ │ │ └── node_identity_test.go │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── acquire.go │ │ │ │ │ │ ├── acquire_test.go │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── machine_type.go │ │ │ │ │ │ ├── persistence.go │ │ │ │ │ │ └── persistence_test.go │ │ │ │ │ ├── cri/ │ │ │ │ │ │ ├── cri.go │ │ │ │ │ │ ├── cri_test.go │ │ │ │ │ │ ├── export_test.go │ │ │ │ │ │ ├── image_cache_config.go │ │ │ │ │ │ ├── image_cache_config_test.go │ │ │ │ │ │ ├── image_gc.go │ │ │ │ │ │ ├── image_gc_test.go │ │ │ │ │ │ ├── registries_config.go │ │ │ │ │ │ ├── registries_config_test.go │ │ │ │ │ │ ├── seccomp_profile.go │ │ │ │ │ │ ├── seccomp_profile_file.go │ │ │ │ │ │ ├── seccomp_profile_file_test.go │ │ │ │ │ │ └── seccomp_profile_test.go │ │ │ │ │ ├── ctest/ │ │ │ │ │ │ ├── assert.go │ │ │ │ │ │ └── ctest.go │ │ │ │ │ ├── etcd/ │ │ │ │ │ │ ├── advertised_peer.go │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── config_test.go │ │ │ │ │ │ ├── etcd.go │ │ │ │ │ │ ├── member.go │ │ │ │ │ │ ├── member_test.go │ │ │ │ │ │ ├── pki.go │ │ │ │ │ │ ├── spec.go │ │ │ │ │ │ └── spec_test.go │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── cri_base_runtime_spec.go │ │ │ │ │ │ ├── cri_base_runtime_spec_test.go │ │ │ │ │ │ ├── cri_config_parts.go │ │ │ │ │ │ ├── cri_registry_config.go │ │ │ │ │ │ ├── etcfile.go │ │ │ │ │ │ ├── etcfile_test.go │ │ │ │ │ │ ├── files.go │ │ │ │ │ │ ├── iqn.go │ │ │ │ │ │ └── nqn.go │ │ │ │ │ ├── hardware/ │ │ │ │ │ │ ├── hardware.go │ │ │ │ │ │ ├── hardware_test.go │ │ │ │ │ │ ├── pci_driver_rebind.go │ │ │ │ │ │ ├── pci_driver_rebind_config.go │ │ │ │ │ │ ├── pci_driver_rebind_config_test.go │ │ │ │ │ │ ├── pcidevices.go │ │ │ │ │ │ ├── pcr_status.go │ │ │ │ │ │ ├── system.go │ │ │ │ │ │ ├── system_test.go │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ └── SuperMicro-Dual-Xeon.dmi │ │ │ │ │ ├── k8s/ │ │ │ │ │ │ ├── address_filter.go │ │ │ │ │ │ ├── address_filter_test.go │ │ │ │ │ │ ├── control_plane.go │ │ │ │ │ │ ├── control_plane_static_pod.go │ │ │ │ │ │ ├── control_plane_static_pod_test.go │ │ │ │ │ │ ├── control_plane_test.go │ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ │ ├── extra_manifest.go │ │ │ │ │ │ ├── extra_manifest_test.go │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ ├── k8stemplates/ │ │ │ │ │ │ │ │ ├── apiserver.go │ │ │ │ │ │ │ │ ├── coredns.go │ │ │ │ │ │ │ │ ├── crds.go │ │ │ │ │ │ │ │ ├── csr.go │ │ │ │ │ │ │ │ ├── flannel.go │ │ │ │ │ │ │ │ ├── k8stemplates.go │ │ │ │ │ │ │ │ ├── k8stemplates_test.go │ │ │ │ │ │ │ │ ├── kube-proxy.go │ │ │ │ │ │ │ │ ├── kubeconfig.go │ │ │ │ │ │ │ │ ├── kubelet.go │ │ │ │ │ │ │ │ ├── talos.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── apiserver-encryption-aescbc.yaml │ │ │ │ │ │ │ │ ├── apiserver-encryption-secretbox.yaml │ │ │ │ │ │ │ │ ├── coredns-cluster-role-binding.yaml │ │ │ │ │ │ │ │ ├── coredns-cluster-role.yaml │ │ │ │ │ │ │ │ ├── coredns-configmap-cluster-domain.yaml │ │ │ │ │ │ │ │ ├── coredns-configmap-no-cluster-domain.yaml │ │ │ │ │ │ │ │ ├── coredns-deployment.yaml │ │ │ │ │ │ │ │ ├── coredns-service-account.yaml │ │ │ │ │ │ │ │ ├── coredns-service-dual.yaml │ │ │ │ │ │ │ │ ├── coredns-service-ipv4.yaml │ │ │ │ │ │ │ │ ├── coredns-service-ipv6.yaml │ │ │ │ │ │ │ │ ├── csr-approver-role-binding.yaml │ │ │ │ │ │ │ │ ├── csr-node-bootstrap.yaml │ │ │ │ │ │ │ │ ├── csr-renewal-role-binding.yaml │ │ │ │ │ │ │ │ ├── flannel-cluster-role-binding.yaml │ │ │ │ │ │ │ │ ├── flannel-cluster-role-with-network-policies.yaml │ │ │ │ │ │ │ │ ├── flannel-cluster-role.yaml │ │ │ │ │ │ │ │ ├── flannel-configmap-dual.yaml │ │ │ │ │ │ │ │ ├── flannel-configmap-v4.yaml │ │ │ │ │ │ │ │ ├── flannel-configmap-v6.yaml │ │ │ │ │ │ │ │ ├── flannel-daemonset-with-network-policies.yaml │ │ │ │ │ │ │ │ ├── flannel-daemonset.yaml │ │ │ │ │ │ │ │ ├── flannel-service-account.yaml │ │ │ │ │ │ │ │ ├── kube-proxy-cluster-role-binding.yaml │ │ │ │ │ │ │ │ ├── kube-proxy-daemonset.yaml │ │ │ │ │ │ │ │ ├── kube-proxy-service-account.yaml │ │ │ │ │ │ │ │ ├── kubeconfig-in-cluster.yaml │ │ │ │ │ │ │ │ ├── kubelet-bootstrapping-token.yaml │ │ │ │ │ │ │ │ ├── talos-nodes-rbac-cluster-role-binding.yaml │ │ │ │ │ │ │ │ ├── talos-nodes-rbac-cluster-role.yaml │ │ │ │ │ │ │ │ └── talos-service-account-crd.yaml │ │ │ │ │ │ │ ├── nodename/ │ │ │ │ │ │ │ │ ├── nodename.go │ │ │ │ │ │ │ │ └── nodename_test.go │ │ │ │ │ │ │ └── nodewatch/ │ │ │ │ │ │ │ └── nodewatch.go │ │ │ │ │ │ ├── k8s.go │ │ │ │ │ │ ├── kubelet_config.go │ │ │ │ │ │ ├── kubelet_config_test.go │ │ │ │ │ │ ├── kubelet_service.go │ │ │ │ │ │ ├── kubelet_spec.go │ │ │ │ │ │ ├── kubelet_spec_test.go │ │ │ │ │ │ ├── kubelet_static_pod.go │ │ │ │ │ │ ├── kubeprism.go │ │ │ │ │ │ ├── kubeprism_config.go │ │ │ │ │ │ ├── kubeprism_config_test.go │ │ │ │ │ │ ├── kubeprism_endpoints.go │ │ │ │ │ │ ├── kubeprism_endpoints_test.go │ │ │ │ │ │ ├── manifest.go │ │ │ │ │ │ ├── manifest_apply.go │ │ │ │ │ │ ├── manifest_test.go │ │ │ │ │ │ ├── node_annotation_spec.go │ │ │ │ │ │ ├── node_annotation_spec_test.go │ │ │ │ │ │ ├── node_apply.go │ │ │ │ │ │ ├── node_apply_test.go │ │ │ │ │ │ ├── node_cordoned_spec.go │ │ │ │ │ │ ├── node_cordoned_spec_test.go │ │ │ │ │ │ ├── node_label_spec.go │ │ │ │ │ │ ├── node_label_spec_test.go │ │ │ │ │ │ ├── node_status.go │ │ │ │ │ │ ├── node_taint_spec.go │ │ │ │ │ │ ├── node_taint_spec_test.go │ │ │ │ │ │ ├── nodeip.go │ │ │ │ │ │ ├── nodeip_config.go │ │ │ │ │ │ ├── nodeip_config_test.go │ │ │ │ │ │ ├── nodeip_test.go │ │ │ │ │ │ ├── nodename.go │ │ │ │ │ │ ├── nodename_test.go │ │ │ │ │ │ ├── render_config_static_pods.go │ │ │ │ │ │ ├── render_secrets_static_pod.go │ │ │ │ │ │ ├── static_endpoint.go │ │ │ │ │ │ ├── static_endpoint_test.go │ │ │ │ │ │ ├── static_pod_config.go │ │ │ │ │ │ ├── static_pod_config_test.go │ │ │ │ │ │ ├── static_pod_server.go │ │ │ │ │ │ └── static_pod_server_test.go │ │ │ │ │ ├── kubeaccess/ │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── config_test.go │ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ │ ├── endpoint_test.go │ │ │ │ │ │ ├── kubeaccess.go │ │ │ │ │ │ ├── serviceaccount/ │ │ │ │ │ │ │ └── crd_controller.go │ │ │ │ │ │ └── serviceaccount.go │ │ │ │ │ ├── kubespan/ │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── config_test.go │ │ │ │ │ │ ├── endpoint.go │ │ │ │ │ │ ├── endpoint_test.go │ │ │ │ │ │ ├── identity.go │ │ │ │ │ │ ├── identity_test.go │ │ │ │ │ │ ├── kubespan.go │ │ │ │ │ │ ├── manager.go │ │ │ │ │ │ ├── manager_test.go │ │ │ │ │ │ ├── peer_spec.go │ │ │ │ │ │ └── peer_spec_test.go │ │ │ │ │ ├── network/ │ │ │ │ │ │ ├── address_config.go │ │ │ │ │ │ ├── address_config_test.go │ │ │ │ │ │ ├── address_event.go │ │ │ │ │ │ ├── address_event_test.go │ │ │ │ │ │ ├── address_merge.go │ │ │ │ │ │ ├── address_merge_test.go │ │ │ │ │ │ ├── address_spec.go │ │ │ │ │ │ ├── address_spec_test.go │ │ │ │ │ │ ├── address_status.go │ │ │ │ │ │ ├── address_status_test.go │ │ │ │ │ │ ├── cmdline.go │ │ │ │ │ │ ├── cmdline_test.go │ │ │ │ │ │ ├── device_config.go │ │ │ │ │ │ ├── device_config_test.go │ │ │ │ │ │ ├── dns_resolve_cache.go │ │ │ │ │ │ ├── dns_resolve_cache_test.go │ │ │ │ │ │ ├── dns_upstream.go │ │ │ │ │ │ ├── etcfile.go │ │ │ │ │ │ ├── etcfile_test.go │ │ │ │ │ │ ├── ethernet_config.go │ │ │ │ │ │ ├── ethernet_config_test.go │ │ │ │ │ │ ├── ethernet_spec.go │ │ │ │ │ │ ├── ethernet_status.go │ │ │ │ │ │ ├── generic_merge.go │ │ │ │ │ │ ├── hardware_addr.go │ │ │ │ │ │ ├── hardware_addr_test.go │ │ │ │ │ │ ├── hostdns_config.go │ │ │ │ │ │ ├── hostname_config.go │ │ │ │ │ │ ├── hostname_config_test.go │ │ │ │ │ │ ├── hostname_merge.go │ │ │ │ │ │ ├── hostname_merge_test.go │ │ │ │ │ │ ├── hostname_spec.go │ │ │ │ │ │ ├── hostname_spec_test.go │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ ├── addressutil/ │ │ │ │ │ │ │ │ ├── addressutil.go │ │ │ │ │ │ │ │ ├── addressutil_test.go │ │ │ │ │ │ │ │ ├── broadcast.go │ │ │ │ │ │ │ │ ├── broadcast_test.go │ │ │ │ │ │ │ │ ├── compare.go │ │ │ │ │ │ │ │ └── compare_test.go │ │ │ │ │ │ │ └── probe/ │ │ │ │ │ │ │ ├── probe.go │ │ │ │ │ │ │ └── probe_test.go │ │ │ │ │ │ ├── link_alias_config.go │ │ │ │ │ │ ├── link_alias_config_test.go │ │ │ │ │ │ ├── link_alias_spec.go │ │ │ │ │ │ ├── link_config.go │ │ │ │ │ │ ├── link_config_test.go │ │ │ │ │ │ ├── link_merge.go │ │ │ │ │ │ ├── link_merge_test.go │ │ │ │ │ │ ├── link_spec.go │ │ │ │ │ │ ├── link_spec_test.go │ │ │ │ │ │ ├── link_status.go │ │ │ │ │ │ ├── link_status_test.go │ │ │ │ │ │ ├── network.go │ │ │ │ │ │ ├── network_test.go │ │ │ │ │ │ ├── nftables_chain.go │ │ │ │ │ │ ├── nftables_chain_config.go │ │ │ │ │ │ ├── nftables_chain_config_test.go │ │ │ │ │ │ ├── nftables_chain_test.go │ │ │ │ │ │ ├── node_address.go │ │ │ │ │ │ ├── node_address_sort_algorithm.go │ │ │ │ │ │ ├── node_address_test.go │ │ │ │ │ │ ├── operator/ │ │ │ │ │ │ │ ├── client_identifier.go │ │ │ │ │ │ │ ├── dhcp4.go │ │ │ │ │ │ │ ├── dhcp6.go │ │ │ │ │ │ │ ├── operator.go │ │ │ │ │ │ │ ├── vip/ │ │ │ │ │ │ │ │ ├── equinix_metal.go │ │ │ │ │ │ │ │ ├── equinix_metal_test.go │ │ │ │ │ │ │ │ ├── hcloud.go │ │ │ │ │ │ │ │ ├── nop.go │ │ │ │ │ │ │ │ └── vip.go │ │ │ │ │ │ │ └── vip.go │ │ │ │ │ │ ├── operator_config.go │ │ │ │ │ │ ├── operator_config_test.go │ │ │ │ │ │ ├── operator_merge.go │ │ │ │ │ │ ├── operator_merge_test.go │ │ │ │ │ │ ├── operator_spec.go │ │ │ │ │ │ ├── operator_spec_test.go │ │ │ │ │ │ ├── operator_vip_config.go │ │ │ │ │ │ ├── operator_vip_config_test.go │ │ │ │ │ │ ├── platform_config.go │ │ │ │ │ │ ├── platform_config_apply.go │ │ │ │ │ │ ├── platform_config_apply_test.go │ │ │ │ │ │ ├── platform_config_load.go │ │ │ │ │ │ ├── platform_config_load_test.go │ │ │ │ │ │ ├── platform_config_store.go │ │ │ │ │ │ ├── platform_config_store_test.go │ │ │ │ │ │ ├── platform_config_test.go │ │ │ │ │ │ ├── probe.go │ │ │ │ │ │ ├── probe_config.go │ │ │ │ │ │ ├── probe_config_test.go │ │ │ │ │ │ ├── probe_merge.go │ │ │ │ │ │ ├── probe_merge_test.go │ │ │ │ │ │ ├── probe_test.go │ │ │ │ │ │ ├── resolver_config.go │ │ │ │ │ │ ├── resolver_config_test.go │ │ │ │ │ │ ├── resolver_merge.go │ │ │ │ │ │ ├── resolver_merge_test.go │ │ │ │ │ │ ├── resolver_spec.go │ │ │ │ │ │ ├── resolver_spec_test.go │ │ │ │ │ │ ├── route_config.go │ │ │ │ │ │ ├── route_config_test.go │ │ │ │ │ │ ├── route_merge.go │ │ │ │ │ │ ├── route_merge_test.go │ │ │ │ │ │ ├── route_spec.go │ │ │ │ │ │ ├── route_spec_test.go │ │ │ │ │ │ ├── route_status.go │ │ │ │ │ │ ├── route_status_test.go │ │ │ │ │ │ ├── routing_rule_config.go │ │ │ │ │ │ ├── routing_rule_config_test.go │ │ │ │ │ │ ├── routing_rule_merge.go │ │ │ │ │ │ ├── routing_rule_merge_test.go │ │ │ │ │ │ ├── routing_rule_spec.go │ │ │ │ │ │ ├── routing_rule_spec_test.go │ │ │ │ │ │ ├── routing_rule_status.go │ │ │ │ │ │ ├── routing_rule_status_test.go │ │ │ │ │ │ ├── status.go │ │ │ │ │ │ ├── status_test.go │ │ │ │ │ │ ├── timeserver_config.go │ │ │ │ │ │ ├── timeserver_config_test.go │ │ │ │ │ │ ├── timeserver_merge.go │ │ │ │ │ │ ├── timeserver_merge_test.go │ │ │ │ │ │ ├── timeserver_spec.go │ │ │ │ │ │ ├── timeserver_spec_test.go │ │ │ │ │ │ ├── utils/ │ │ │ │ │ │ │ └── utils.go │ │ │ │ │ │ └── watch/ │ │ │ │ │ │ ├── ethtool.go │ │ │ │ │ │ ├── rtnetlink.go │ │ │ │ │ │ ├── trigger.go │ │ │ │ │ │ ├── trigger_test.go │ │ │ │ │ │ └── watch.go │ │ │ │ │ ├── perf/ │ │ │ │ │ │ ├── perf.go │ │ │ │ │ │ └── perf_test.go │ │ │ │ │ ├── runtime/ │ │ │ │ │ │ ├── api_service_config.go │ │ │ │ │ │ ├── api_service_config_test.go │ │ │ │ │ │ ├── booted_entry.go │ │ │ │ │ │ ├── common_test.go │ │ │ │ │ │ ├── devices_status.go │ │ │ │ │ │ ├── diagnostics.go │ │ │ │ │ │ ├── diagnostics_logger.go │ │ │ │ │ │ ├── drop_upgrade_fallback.go │ │ │ │ │ │ ├── drop_upgrade_fallback_test.go │ │ │ │ │ │ ├── environment.go │ │ │ │ │ │ ├── environment_test.go │ │ │ │ │ │ ├── events_sink.go │ │ │ │ │ │ ├── events_sink_config.go │ │ │ │ │ │ ├── events_sink_config_test.go │ │ │ │ │ │ ├── events_sink_test.go │ │ │ │ │ │ ├── extension_service.go │ │ │ │ │ │ ├── extension_service_config.go │ │ │ │ │ │ ├── extension_service_config_files.go │ │ │ │ │ │ ├── extension_service_config_files_test.go │ │ │ │ │ │ ├── extension_service_config_test.go │ │ │ │ │ │ ├── extension_service_test.go │ │ │ │ │ │ ├── extension_status.go │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ ├── diagnostics/ │ │ │ │ │ │ │ │ ├── address_overlap.go │ │ │ │ │ │ │ │ ├── address_overlap_test.go │ │ │ │ │ │ │ │ ├── diagnostic.go │ │ │ │ │ │ │ │ └── kubelet_csr_not_approved.go │ │ │ │ │ │ │ ├── filehash/ │ │ │ │ │ │ │ │ ├── filehash.go │ │ │ │ │ │ │ │ └── filehash_test.go │ │ │ │ │ │ │ ├── logfile/ │ │ │ │ │ │ │ │ ├── logfile.go │ │ │ │ │ │ │ │ └── logfile_test.go │ │ │ │ │ │ │ └── oom/ │ │ │ │ │ │ │ ├── oom.go │ │ │ │ │ │ │ ├── oom_test.go │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ ├── rank1/ │ │ │ │ │ │ │ │ └── kubepods/ │ │ │ │ │ │ │ │ ├── besteffort/ │ │ │ │ │ │ │ │ │ └── pod123/ │ │ │ │ │ │ │ │ │ ├── cgroup.procs │ │ │ │ │ │ │ │ │ ├── memory.current │ │ │ │ │ │ │ │ │ ├── memory.max │ │ │ │ │ │ │ │ │ └── memory.peak │ │ │ │ │ │ │ │ └── burstable/ │ │ │ │ │ │ │ │ └── podABC/ │ │ │ │ │ │ │ │ ├── cgroup.procs │ │ │ │ │ │ │ │ ├── memory.current │ │ │ │ │ │ │ │ ├── memory.max │ │ │ │ │ │ │ │ └── memory.peak │ │ │ │ │ │ │ ├── trigger-false/ │ │ │ │ │ │ │ │ ├── init/ │ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ │ ├── memory.pressure │ │ │ │ │ │ │ │ ├── podruntime/ │ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ │ └── system/ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ └── trigger-true/ │ │ │ │ │ │ │ ├── init/ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ ├── kubepods/ │ │ │ │ │ │ │ │ └── besteffort/ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ ├── memory.pressure │ │ │ │ │ │ │ ├── podruntime/ │ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ │ └── system/ │ │ │ │ │ │ │ └── memory.pressure │ │ │ │ │ │ ├── kernel_cmdline.go │ │ │ │ │ │ ├── kernel_cmdline_test.go │ │ │ │ │ │ ├── kernel_module_config.go │ │ │ │ │ │ ├── kernel_module_config_test.go │ │ │ │ │ │ ├── kernel_module_spec.go │ │ │ │ │ │ ├── kernel_param_config.go │ │ │ │ │ │ ├── kernel_param_config_test.go │ │ │ │ │ │ ├── kernel_param_defaults.go │ │ │ │ │ │ ├── kernel_param_defaults_test.go │ │ │ │ │ │ ├── kernel_param_spec.go │ │ │ │ │ │ ├── kernel_param_spec_test.go │ │ │ │ │ │ ├── kmsg_log.go │ │ │ │ │ │ ├── kmsg_log_config.go │ │ │ │ │ │ ├── kmsg_log_config_test.go │ │ │ │ │ │ ├── kmsg_log_storage.go │ │ │ │ │ │ ├── kmsg_log_test.go │ │ │ │ │ │ ├── loaded_kernel_module.go │ │ │ │ │ │ ├── loaded_kernel_module_test.go │ │ │ │ │ │ ├── log_persistence.go │ │ │ │ │ │ ├── log_persistence_test.go │ │ │ │ │ │ ├── machine_status.go │ │ │ │ │ │ ├── machine_status_publisher.go │ │ │ │ │ │ ├── machine_status_test.go │ │ │ │ │ │ ├── maintenance_config.go │ │ │ │ │ │ ├── maintenance_config_test.go │ │ │ │ │ │ ├── maintenance_service_inform.go │ │ │ │ │ │ ├── mount_status.go │ │ │ │ │ │ ├── oom.go │ │ │ │ │ │ ├── runtime.go │ │ │ │ │ │ ├── sbom_item.go │ │ │ │ │ │ ├── sbom_item_test.go │ │ │ │ │ │ ├── security_state.go │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ ├── ext-spdx/ │ │ │ │ │ │ │ │ └── tailscale.spdx.json │ │ │ │ │ │ │ ├── extservices/ │ │ │ │ │ │ │ │ ├── foo.bar │ │ │ │ │ │ │ │ ├── frr.yaml │ │ │ │ │ │ │ │ ├── hello.yaml │ │ │ │ │ │ │ │ ├── invalid.yaml │ │ │ │ │ │ │ │ └── zduplicate.yaml │ │ │ │ │ │ │ └── spdx/ │ │ │ │ │ │ │ └── test.spdx.json │ │ │ │ │ │ ├── unique_token.go │ │ │ │ │ │ ├── unique_token_test.go │ │ │ │ │ │ ├── utils.go │ │ │ │ │ │ ├── version.go │ │ │ │ │ │ ├── watchdog_timer.go │ │ │ │ │ │ ├── watchdog_timer_config.go │ │ │ │ │ │ └── watchdog_timer_config_test.go │ │ │ │ │ ├── secrets/ │ │ │ │ │ │ ├── api.go │ │ │ │ │ │ ├── api_cert_sans.go │ │ │ │ │ │ ├── api_cert_sans_test.go │ │ │ │ │ │ ├── api_test.go │ │ │ │ │ │ ├── data/ │ │ │ │ │ │ │ └── ca-certificates │ │ │ │ │ │ ├── encryption_salt.go │ │ │ │ │ │ ├── encryption_salt_test.go │ │ │ │ │ │ ├── etcd.go │ │ │ │ │ │ ├── etcd_test.go │ │ │ │ │ │ ├── kubelet.go │ │ │ │ │ │ ├── kubelet_test.go │ │ │ │ │ │ ├── kubernetes.go │ │ │ │ │ │ ├── kubernetes_cert_sans.go │ │ │ │ │ │ ├── kubernetes_cert_sans_test.go │ │ │ │ │ │ ├── kubernetes_dynamic_certs.go │ │ │ │ │ │ ├── kubernetes_dynamic_certs_test.go │ │ │ │ │ │ ├── kubernetes_test.go │ │ │ │ │ │ ├── maintenance_cert_sans.go │ │ │ │ │ │ ├── maintenance_cert_sans_test.go │ │ │ │ │ │ ├── maintenance_root.go │ │ │ │ │ │ ├── maintenance_root_test.go │ │ │ │ │ │ ├── root.go │ │ │ │ │ │ ├── root_test.go │ │ │ │ │ │ ├── secrets.go │ │ │ │ │ │ ├── trustd.go │ │ │ │ │ │ ├── trustd_test.go │ │ │ │ │ │ ├── trusted_roots.go │ │ │ │ │ │ └── trusted_roots_test.go │ │ │ │ │ ├── security/ │ │ │ │ │ │ ├── image_verification_config.go │ │ │ │ │ │ ├── image_verification_config_test.go │ │ │ │ │ │ ├── tuf_trusted_root.go │ │ │ │ │ │ └── tuf_trusted_root_test.go │ │ │ │ │ ├── siderolink/ │ │ │ │ │ │ ├── config.go │ │ │ │ │ │ ├── config_test.go │ │ │ │ │ │ ├── manager.go │ │ │ │ │ │ ├── manager_test.go │ │ │ │ │ │ ├── siderolink.go │ │ │ │ │ │ ├── status.go │ │ │ │ │ │ ├── status_test.go │ │ │ │ │ │ └── userspace.go │ │ │ │ │ ├── time/ │ │ │ │ │ │ ├── adjtime_status.go │ │ │ │ │ │ ├── sync.go │ │ │ │ │ │ ├── sync_test.go │ │ │ │ │ │ └── time.go │ │ │ │ │ ├── utils.go │ │ │ │ │ └── v1alpha1/ │ │ │ │ │ ├── service.go │ │ │ │ │ ├── utils.go │ │ │ │ │ └── v1alpha1.go │ │ │ │ ├── runtime/ │ │ │ │ │ ├── controller.go │ │ │ │ │ ├── disk/ │ │ │ │ │ │ ├── disk.go │ │ │ │ │ │ └── options.go │ │ │ │ │ ├── doc.go │ │ │ │ │ ├── drainer.go │ │ │ │ │ ├── drainer_test.go │ │ │ │ │ ├── emergency/ │ │ │ │ │ │ └── emergency.go │ │ │ │ │ ├── errors.go │ │ │ │ │ ├── events.go │ │ │ │ │ ├── kernel_linux.go │ │ │ │ │ ├── logging/ │ │ │ │ │ │ ├── circular.go │ │ │ │ │ │ ├── extract.go │ │ │ │ │ │ ├── extract_test.go │ │ │ │ │ │ ├── file.go │ │ │ │ │ │ ├── logging.go │ │ │ │ │ │ ├── null.go │ │ │ │ │ │ ├── sender_jsonlines.go │ │ │ │ │ │ └── sender_jsonlines_test.go │ │ │ │ │ ├── logging.go │ │ │ │ │ ├── mode.go │ │ │ │ │ ├── mode_test.go │ │ │ │ │ ├── platform.go │ │ │ │ │ ├── runtime.go │ │ │ │ │ ├── sequencer.go │ │ │ │ │ ├── sequencer_test.go │ │ │ │ │ ├── state.go │ │ │ │ │ ├── v1alpha1/ │ │ │ │ │ │ ├── acpi/ │ │ │ │ │ │ │ ├── acpi.go │ │ │ │ │ │ │ └── acpi_test.go │ │ │ │ │ │ ├── bootloader/ │ │ │ │ │ │ │ ├── bootloader.go │ │ │ │ │ │ │ ├── bootloader_test.go │ │ │ │ │ │ │ ├── dual/ │ │ │ │ │ │ │ │ └── dual.go │ │ │ │ │ │ │ ├── efiutils/ │ │ │ │ │ │ │ │ └── efiutils.go │ │ │ │ │ │ │ ├── grub/ │ │ │ │ │ │ │ │ ├── blocklist.go │ │ │ │ │ │ │ │ ├── boot_label.go │ │ │ │ │ │ │ │ ├── constants.go │ │ │ │ │ │ │ │ ├── decode.go │ │ │ │ │ │ │ │ ├── encode.go │ │ │ │ │ │ │ │ ├── grub.go │ │ │ │ │ │ │ │ ├── grub_test.go │ │ │ │ │ │ │ │ ├── install.go │ │ │ │ │ │ │ │ ├── probe.go │ │ │ │ │ │ │ │ ├── quote.go │ │ │ │ │ │ │ │ ├── quote_test.go │ │ │ │ │ │ │ │ ├── revert.go │ │ │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ │ │ ├── grub_parse_test.cfg │ │ │ │ │ │ │ │ │ ├── grub_write_no_reset_test.cfg │ │ │ │ │ │ │ │ │ └── grub_write_test.cfg │ │ │ │ │ │ │ │ └── upgrade.go │ │ │ │ │ │ │ ├── kexec/ │ │ │ │ │ │ │ │ └── kexec.go │ │ │ │ │ │ │ ├── mount/ │ │ │ │ │ │ │ │ └── mount.go │ │ │ │ │ │ │ ├── options/ │ │ │ │ │ │ │ │ └── options.go │ │ │ │ │ │ │ └── sdboot/ │ │ │ │ │ │ │ ├── efivars.go │ │ │ │ │ │ │ ├── efivars_test.go │ │ │ │ │ │ │ ├── export_test.go │ │ │ │ │ │ │ ├── loader.conf │ │ │ │ │ │ │ ├── sdboot.go │ │ │ │ │ │ │ └── sdboot_test.go │ │ │ │ │ │ ├── doc.go │ │ │ │ │ │ ├── platform/ │ │ │ │ │ │ │ ├── akamai/ │ │ │ │ │ │ │ │ ├── akamai.go │ │ │ │ │ │ │ │ ├── akamai_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── instance.json │ │ │ │ │ │ │ │ └── network.json │ │ │ │ │ │ │ ├── aws/ │ │ │ │ │ │ │ │ ├── aws.go │ │ │ │ │ │ │ │ ├── aws_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── azure/ │ │ │ │ │ │ │ │ ├── azure.go │ │ │ │ │ │ │ │ ├── azure_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── register.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── compute.json │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── interfaces.json │ │ │ │ │ │ │ │ └── loadbalancer.json │ │ │ │ │ │ │ ├── cloudstack/ │ │ │ │ │ │ │ │ ├── cloudstack.go │ │ │ │ │ │ │ │ ├── cloudstack_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── container/ │ │ │ │ │ │ │ │ ├── container.go │ │ │ │ │ │ │ │ └── internal/ │ │ │ │ │ │ │ │ └── files/ │ │ │ │ │ │ │ │ ├── hostname.go │ │ │ │ │ │ │ │ ├── hostname_test.go │ │ │ │ │ │ │ │ ├── resolv.go │ │ │ │ │ │ │ │ ├── resolv_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── hostname │ │ │ │ │ │ │ │ └── resolv.conf │ │ │ │ │ │ │ ├── digitalocean/ │ │ │ │ │ │ │ │ ├── digitalocean.go │ │ │ │ │ │ │ │ ├── digitalocean_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── equinixmetal/ │ │ │ │ │ │ │ │ ├── equinix.go │ │ │ │ │ │ │ │ ├── equinix_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected-2bonds.yaml │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── metadata-2bonds.json │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── errors/ │ │ │ │ │ │ │ │ └── errors.go │ │ │ │ │ │ │ ├── exoscale/ │ │ │ │ │ │ │ │ ├── exoscale.go │ │ │ │ │ │ │ │ ├── exoscale_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── gcp/ │ │ │ │ │ │ │ │ ├── gcp.go │ │ │ │ │ │ │ │ ├── gcp_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── interfaces.json │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── hcloud/ │ │ │ │ │ │ │ │ ├── export_test.go │ │ │ │ │ │ │ │ ├── hcloud.go │ │ │ │ │ │ │ │ ├── hcloud_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── metadata.yaml │ │ │ │ │ │ │ │ ├── userdata-base64.txt │ │ │ │ │ │ │ │ └── userdata-plain.yaml │ │ │ │ │ │ │ ├── internal/ │ │ │ │ │ │ │ │ ├── address/ │ │ │ │ │ │ │ │ │ └── address.go │ │ │ │ │ │ │ │ ├── blockutils/ │ │ │ │ │ │ │ │ │ ├── blockutils.go │ │ │ │ │ │ │ │ │ └── blockutils_test.go │ │ │ │ │ │ │ │ └── netutils/ │ │ │ │ │ │ │ │ └── netutils.go │ │ │ │ │ │ │ ├── metal/ │ │ │ │ │ │ │ │ ├── metal.go │ │ │ │ │ │ │ │ ├── metal_test.go │ │ │ │ │ │ │ │ ├── oauth2/ │ │ │ │ │ │ │ │ │ ├── oauth2.go │ │ │ │ │ │ │ │ │ └── oauth2_test.go │ │ │ │ │ │ │ │ ├── url/ │ │ │ │ │ │ │ │ │ ├── map.go │ │ │ │ │ │ │ │ │ ├── map_test.go │ │ │ │ │ │ │ │ │ ├── url.go │ │ │ │ │ │ │ │ │ ├── url_test.go │ │ │ │ │ │ │ │ │ ├── value.go │ │ │ │ │ │ │ │ │ ├── variable.go │ │ │ │ │ │ │ │ │ └── variable_test.go │ │ │ │ │ │ │ │ └── url_test.go │ │ │ │ │ │ │ ├── nocloud/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── nocloud.go │ │ │ │ │ │ │ │ ├── nocloud_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected-v1-pnap.yaml │ │ │ │ │ │ │ │ ├── expected-v1.yaml │ │ │ │ │ │ │ │ ├── expected-v2-serverscom.yaml │ │ │ │ │ │ │ │ ├── expected-v2.yaml │ │ │ │ │ │ │ │ ├── in-v1-pnap.yaml │ │ │ │ │ │ │ │ ├── in-v1.yaml │ │ │ │ │ │ │ │ ├── in-v2-cloud-init.yaml │ │ │ │ │ │ │ │ ├── in-v2-nocloud.yaml │ │ │ │ │ │ │ │ ├── in-v2-serverscom.yaml │ │ │ │ │ │ │ │ └── metadata-nocloud.yaml │ │ │ │ │ │ │ ├── opennebula/ │ │ │ │ │ │ │ │ ├── aliases_test.go │ │ │ │ │ │ │ │ ├── dns_test.go │ │ │ │ │ │ │ │ ├── hostname_test.go │ │ │ │ │ │ │ │ ├── ipv6_test.go │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── onegate_test.go │ │ │ │ │ │ │ │ ├── opennebula.go │ │ │ │ │ │ │ │ ├── opennebula_test.go │ │ │ │ │ │ │ │ ├── routes_test.go │ │ │ │ │ │ │ │ ├── skip_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── expected_no_network_flag.yaml │ │ │ │ │ │ │ │ ├── metadata.yaml │ │ │ │ │ │ │ │ └── metadata_no_network_flag.yaml │ │ │ │ │ │ │ ├── openstack/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── openstack.go │ │ │ │ │ │ │ │ ├── openstack_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── metadata.json │ │ │ │ │ │ │ │ └── network.json │ │ │ │ │ │ │ ├── oracle/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── oracle.go │ │ │ │ │ │ │ │ ├── oracle_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ ├── metadata.json │ │ │ │ │ │ │ │ └── metadatanetwork.json │ │ │ │ │ │ │ ├── platform.go │ │ │ │ │ │ │ ├── scaleway/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── scaleway.go │ │ │ │ │ │ │ │ ├── scaleway_test.go │ │ │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ │ │ ├── expected-v1.yaml │ │ │ │ │ │ │ │ ├── expected-v2.yaml │ │ │ │ │ │ │ │ ├── expected-v3.yaml │ │ │ │ │ │ │ │ ├── metadata-v1.json │ │ │ │ │ │ │ │ ├── metadata-v2.json │ │ │ │ │ │ │ │ └── metadata-v3.json │ │ │ │ │ │ │ ├── upcloud/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ │ ├── upcloud.go │ │ │ │ │ │ │ │ └── upcloud_test.go │ │ │ │ │ │ │ ├── vmware/ │ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ │ │ ├── expected-match-by-mac.yaml │ │ │ │ │ │ │ │ │ ├── expected-match-by-name.yaml │ │ │ │ │ │ │ │ │ ├── metadata-match-by-mac.yaml │ │ │ │ │ │ │ │ │ └── metadata-match-by-name.yaml │ │ │ │ │ │ │ │ ├── vmware.go │ │ │ │ │ │ │ │ ├── vmware_other.go │ │ │ │ │ │ │ │ ├── vmware_supported.go │ │ │ │ │ │ │ │ └── vmware_test.go │ │ │ │ │ │ │ └── vultr/ │ │ │ │ │ │ │ ├── metadata.go │ │ │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ │ │ └── metadata.json │ │ │ │ │ │ │ ├── vultr.go │ │ │ │ │ │ │ └── vultr_test.go │ │ │ │ │ │ ├── v1alpha1_controller.go │ │ │ │ │ │ ├── v1alpha1_controller_test.go │ │ │ │ │ │ ├── v1alpha1_dbus.go │ │ │ │ │ │ ├── v1alpha1_events.go │ │ │ │ │ │ ├── v1alpha1_events_test.go │ │ │ │ │ │ ├── v1alpha1_priority_lock.go │ │ │ │ │ │ ├── v1alpha1_priority_lock_test.go │ │ │ │ │ │ ├── v1alpha1_runtime.go │ │ │ │ │ │ ├── v1alpha1_sequencer.go │ │ │ │ │ │ ├── v1alpha1_sequencer_tasks.go │ │ │ │ │ │ ├── v1alpha1_sequencer_test.go │ │ │ │ │ │ └── v1alpha1_state.go │ │ │ │ │ └── v1alpha2/ │ │ │ │ │ ├── adapters.go │ │ │ │ │ ├── v1alpha2.go │ │ │ │ │ ├── v1alpha2_controller.go │ │ │ │ │ └── v1alpha2_state.go │ │ │ │ ├── startup/ │ │ │ │ │ ├── cgroups.go │ │ │ │ │ ├── startup.go │ │ │ │ │ └── tasks.go │ │ │ │ └── system/ │ │ │ │ ├── events/ │ │ │ │ │ ├── events.go │ │ │ │ │ └── events_test.go │ │ │ │ ├── export_test.go │ │ │ │ ├── health/ │ │ │ │ │ ├── check.go │ │ │ │ │ ├── health_test.go │ │ │ │ │ ├── settings.go │ │ │ │ │ └── status.go │ │ │ │ ├── integration_test.go │ │ │ │ ├── mocks_test.go │ │ │ │ ├── runner/ │ │ │ │ │ ├── containerd/ │ │ │ │ │ │ ├── containerd.go │ │ │ │ │ │ ├── containerd_test.go │ │ │ │ │ │ ├── opts.go │ │ │ │ │ │ └── stdin.go │ │ │ │ │ ├── goroutine/ │ │ │ │ │ │ ├── goroutine.go │ │ │ │ │ │ └── goroutine_test.go │ │ │ │ │ ├── internal/ │ │ │ │ │ │ └── lastlog/ │ │ │ │ │ │ ├── lastlog.go │ │ │ │ │ │ ├── lastlog_test.go │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ └── kubelet.log │ │ │ │ │ ├── process/ │ │ │ │ │ │ ├── process.go │ │ │ │ │ │ └── process_test.go │ │ │ │ │ ├── restart/ │ │ │ │ │ │ ├── restart.go │ │ │ │ │ │ └── restart_test.go │ │ │ │ │ ├── runner.go │ │ │ │ │ └── runner_test.go │ │ │ │ ├── service.go │ │ │ │ ├── service_events.go │ │ │ │ ├── service_runner.go │ │ │ │ ├── service_runner_test.go │ │ │ │ ├── services/ │ │ │ │ │ ├── apid.go │ │ │ │ │ ├── auditd.go │ │ │ │ │ ├── containerd.go │ │ │ │ │ ├── cri.go │ │ │ │ │ ├── dashboard.go │ │ │ │ │ ├── etcd.go │ │ │ │ │ ├── export_test.go │ │ │ │ │ ├── extension.go │ │ │ │ │ ├── extension_test.go │ │ │ │ │ ├── kubelet.go │ │ │ │ │ ├── machined.go │ │ │ │ │ ├── machined_test.go │ │ │ │ │ ├── mocks/ │ │ │ │ │ │ └── snapshotter.go │ │ │ │ │ ├── registry/ │ │ │ │ │ │ ├── app/ │ │ │ │ │ │ │ └── main.go │ │ │ │ │ │ ├── fs.go │ │ │ │ │ │ ├── params.go │ │ │ │ │ │ ├── readers.go │ │ │ │ │ │ ├── registry.go │ │ │ │ │ │ ├── registry_test.go │ │ │ │ │ │ ├── store.go │ │ │ │ │ │ ├── store_linux.go │ │ │ │ │ │ ├── store_unix.go │ │ │ │ │ │ └── store_windows.go │ │ │ │ │ ├── registryd.go │ │ │ │ │ ├── syslogd.go │ │ │ │ │ ├── trustd.go │ │ │ │ │ ├── udevd.go │ │ │ │ │ └── utils.go │ │ │ │ ├── system.go │ │ │ │ ├── system_test.go │ │ │ │ └── volumes.go │ │ │ └── revert.go │ │ ├── poweroff/ │ │ │ ├── main.go │ │ │ └── poweroff_test.go │ │ ├── resources/ │ │ │ └── access.go │ │ ├── storaged/ │ │ │ └── server.go │ │ ├── syslogd/ │ │ │ ├── internal/ │ │ │ │ └── parser/ │ │ │ │ ├── parse.go │ │ │ │ └── parse_test.go │ │ │ ├── syslogd.go │ │ │ └── syslogd_test.go │ │ └── trustd/ │ │ ├── internal/ │ │ │ ├── provider/ │ │ │ │ └── provider.go │ │ │ └── reg/ │ │ │ ├── reg.go │ │ │ └── reg_test.go │ │ └── main.go │ ├── integration/ │ │ ├── api/ │ │ │ ├── api.go │ │ │ ├── apid.go │ │ │ ├── apply-config.go │ │ │ ├── cgroups.go │ │ │ ├── common.go │ │ │ ├── constants.go │ │ │ ├── containers.go │ │ │ ├── debug.go │ │ │ ├── discovery.go │ │ │ ├── diskusage.go │ │ │ ├── dmesg.go │ │ │ ├── environment.go │ │ │ ├── etcd-recover.go │ │ │ ├── etcd.go │ │ │ ├── ethernet.go │ │ │ ├── events.go │ │ │ ├── extensions_nvidia.go │ │ │ ├── extensions_qemu.go │ │ │ ├── firewall.go │ │ │ ├── hardware.go │ │ │ ├── images.go │ │ │ ├── kernel.go │ │ │ ├── logs.go │ │ │ ├── machine-status.go │ │ │ ├── monitoring.go │ │ │ ├── network-config.go │ │ │ ├── node-annotations.go │ │ │ ├── node-labels.go │ │ │ ├── node-taints.go │ │ │ ├── pci_driver_rebind.go │ │ │ ├── platform.go │ │ │ ├── probe-config.go │ │ │ ├── process.go │ │ │ ├── reboot.go │ │ │ ├── reset.go │ │ │ ├── resources.go │ │ │ ├── rotate.go │ │ │ ├── sbom.go │ │ │ ├── security.go │ │ │ ├── selinux.go │ │ │ ├── serviceaccount.go │ │ │ ├── siderolink.go │ │ │ ├── testdata/ │ │ │ │ ├── nodeport.yaml │ │ │ │ └── nvidia-gpu-operator.yaml │ │ │ ├── trusted-roots.go │ │ │ ├── trustedboot.go │ │ │ ├── uki.go │ │ │ ├── update-hostname.go │ │ │ ├── version.go │ │ │ ├── volumes.go │ │ │ ├── watchdog.go │ │ │ └── wipe.go │ │ ├── base/ │ │ │ ├── api.go │ │ │ ├── base.go │ │ │ ├── cli.go │ │ │ ├── cluster.go │ │ │ ├── discovery_k8s.go │ │ │ ├── discovery_nok8s.go │ │ │ ├── errors.go │ │ │ ├── flags.go │ │ │ ├── k8s.go │ │ │ └── run.go │ │ ├── cli/ │ │ │ ├── apply-config.go │ │ │ ├── cgroups.go │ │ │ ├── cli.go │ │ │ ├── completion.go │ │ │ ├── config.go │ │ │ ├── containers.go │ │ │ ├── copy.go │ │ │ ├── debug.go │ │ │ ├── diskusage.go │ │ │ ├── dmesg.go │ │ │ ├── etcd.go │ │ │ ├── gen.go │ │ │ ├── health.go │ │ │ ├── image.go │ │ │ ├── inject.go │ │ │ ├── jsonpath.go │ │ │ ├── kubeconfig.go │ │ │ ├── list.go │ │ │ ├── logs.go │ │ │ ├── machineconfig.go │ │ │ ├── memory.go │ │ │ ├── meta.go │ │ │ ├── mounts.go │ │ │ ├── netstat.go │ │ │ ├── patch.go │ │ │ ├── pcap.go │ │ │ ├── pki.go │ │ │ ├── processes.go │ │ │ ├── read.go │ │ │ ├── reboot.go │ │ │ ├── restart.go │ │ │ ├── services.go │ │ │ ├── stats.go │ │ │ ├── support.go │ │ │ ├── testdata/ │ │ │ │ ├── inject/ │ │ │ │ │ ├── talosconfig-expected.yaml │ │ │ │ │ └── talosconfig-input.yaml │ │ │ │ ├── patches/ │ │ │ │ │ ├── delete-dummy-ap.yaml │ │ │ │ │ └── dummy-ap.yaml │ │ │ │ └── pki/ │ │ │ │ ├── ca.crt │ │ │ │ ├── ca.key │ │ │ │ ├── etcd/ │ │ │ │ │ ├── ca.crt │ │ │ │ │ └── ca.key │ │ │ │ ├── front-proxy-ca.crt │ │ │ │ ├── front-proxy-ca.key │ │ │ │ └── sa.key │ │ │ ├── time.go │ │ │ ├── validate.go │ │ │ └── version.go │ │ ├── integration_test.go │ │ ├── k8s/ │ │ │ ├── apparmor.go │ │ │ ├── constants.go │ │ │ ├── k8s.go │ │ │ ├── longhorn.go │ │ │ ├── manifests.go │ │ │ ├── oom.go │ │ │ ├── openebs.go │ │ │ ├── rook.go │ │ │ ├── testdata/ │ │ │ │ ├── apparmor.yaml │ │ │ │ ├── local-path-storage.yaml │ │ │ │ ├── longhorn-iscsi-volume.yaml │ │ │ │ ├── longhorn-v2-disk-patch.yaml │ │ │ │ ├── longhorn-v2-engine-values.yaml │ │ │ │ ├── longhorn-v2-storageclass.yaml │ │ │ │ ├── longhorn-volumeattachment.yaml │ │ │ │ ├── oom.yaml │ │ │ │ ├── openebs-diskpool.yaml │ │ │ │ ├── openebs-values.yaml │ │ │ │ ├── pod-iscsi-volume.yaml │ │ │ │ ├── rook-ceph-cluster-values.yaml │ │ │ │ └── usernamespace.yaml │ │ │ ├── tink.go │ │ │ ├── usernamespace.go │ │ │ └── version.go │ │ ├── provision/ │ │ │ ├── k8s_compatibility.go │ │ │ ├── maintenance_basic.go │ │ │ ├── provision.go │ │ │ └── upgrade.go │ │ └── version_test.go │ └── pkg/ │ ├── capability/ │ │ └── capability.go │ ├── cgroup/ │ │ ├── cgroup.go │ │ ├── cpu.go │ │ └── cpu_test.go │ ├── cgroups/ │ │ ├── cgroups.go │ │ ├── raw.go │ │ ├── reader.go │ │ ├── tar.go │ │ ├── tar_test.go │ │ ├── value.go │ │ └── value_test.go │ ├── console/ │ │ └── console.go │ ├── containermode/ │ │ └── containermode.go │ ├── containers/ │ │ ├── container.go │ │ ├── containerd/ │ │ │ ├── containerd.go │ │ │ └── containerd_test.go │ │ ├── containers_test.go │ │ ├── cri/ │ │ │ ├── containerd/ │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── containerd.go │ │ │ │ ├── hosts.go │ │ │ │ ├── hosts_test.go │ │ │ │ └── testdata/ │ │ │ │ └── cri.toml │ │ │ ├── cri.go │ │ │ └── cri_test.go │ │ ├── image/ │ │ │ ├── console/ │ │ │ │ └── progress.go │ │ │ ├── image.go │ │ │ ├── progress/ │ │ │ │ └── pull_progress.go │ │ │ ├── resolver.go │ │ │ ├── resolver_test.go │ │ │ └── verify/ │ │ │ ├── internal/ │ │ │ │ └── cosign/ │ │ │ │ ├── cosign.go │ │ │ │ └── cosign_test.go │ │ │ └── verify.go │ │ ├── inspector.go │ │ └── pod.go │ ├── cri/ │ │ ├── client.go │ │ ├── containers.go │ │ ├── cri.go │ │ ├── cri_test.go │ │ ├── images.go │ │ └── pods.go │ ├── ctxutil/ │ │ └── ctxutil.go │ ├── dashboard/ │ │ ├── apidata/ │ │ │ ├── apidata.go │ │ │ ├── data.go │ │ │ ├── diff.go │ │ │ ├── node.go │ │ │ └── source.go │ │ ├── components/ │ │ │ ├── components.go │ │ │ ├── diagnostics.go │ │ │ ├── footer.go │ │ │ ├── gauges.go │ │ │ ├── graphs.go │ │ │ ├── header.go │ │ │ ├── horizontalline.go │ │ │ ├── info.go │ │ │ ├── kubernetesinfo.go │ │ │ ├── logviewer.go │ │ │ ├── networkinfo.go │ │ │ ├── sparklines.go │ │ │ ├── tables.go │ │ │ ├── tables_test.go │ │ │ └── talosinfo.go │ │ ├── configurl.go │ │ ├── context.go │ │ ├── dashboard.go │ │ ├── formdata.go │ │ ├── formdata_test.go │ │ ├── logdata/ │ │ │ └── logdata.go │ │ ├── monitor.go │ │ ├── networkconfig.go │ │ ├── options.go │ │ ├── resolver/ │ │ │ └── resolver.go │ │ ├── resourcedata/ │ │ │ └── resourcedata.go │ │ ├── summary.go │ │ └── util/ │ │ └── util.go │ ├── discovery/ │ │ └── registry/ │ │ ├── kubernetes.go │ │ ├── kubernetes_test.go │ │ └── registry.go │ ├── dns/ │ │ ├── dns.go │ │ ├── dns_test.go │ │ ├── manager.go │ │ └── runnner.go │ ├── efivarfs/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── boot.go │ │ ├── boot_test.go │ │ ├── devicepath.go │ │ ├── devicepath_test.go │ │ ├── efivarfs.go │ │ ├── efivarfs_test.go │ │ ├── mock.go │ │ └── variables.go │ ├── encryption/ │ │ ├── encryption.go │ │ ├── encryption_test.go │ │ ├── helpers/ │ │ │ └── helpers.go │ │ ├── keys/ │ │ │ ├── keys.go │ │ │ ├── kms.go │ │ │ ├── nodeid.go │ │ │ ├── nodeid_test.go │ │ │ ├── options.go │ │ │ ├── salted.go │ │ │ ├── salted_test.go │ │ │ ├── static.go │ │ │ ├── static_test.go │ │ │ ├── token.go │ │ │ └── tpm2.go │ │ └── node_params.go │ ├── endpoint/ │ │ ├── endpoint.go │ │ └── endpoint_test.go │ ├── environment/ │ │ ├── environment.go │ │ └── environment_test.go │ ├── etcd/ │ │ ├── certs.go │ │ ├── endpoints.go │ │ ├── etcd.go │ │ ├── local.go │ │ └── lock.go │ ├── extensions/ │ │ ├── compress.go │ │ ├── discarder.go │ │ ├── extensions.go │ │ ├── extensions_test.go │ │ ├── kernel_modules.go │ │ ├── list.go │ │ ├── list_test.go │ │ └── testdata/ │ │ └── good/ │ │ └── extension1/ │ │ ├── manifest.yaml │ │ └── rootfs/ │ │ └── usr/ │ │ └── lib/ │ │ ├── firmware/ │ │ │ └── amd/ │ │ │ └── cpu │ │ └── ld-linux-x86-64.so.2 │ ├── install/ │ │ ├── install.go │ │ ├── options.go │ │ └── pull.go │ ├── logind/ │ │ ├── broker.go │ │ ├── dbus.go │ │ ├── kubelet_mock_test.go │ │ ├── logind.go │ │ ├── logind_test.go │ │ └── service.go │ ├── measure/ │ │ ├── internal/ │ │ │ └── pcr/ │ │ │ ├── bank_data.go │ │ │ ├── bank_data_test.go │ │ │ ├── extend.go │ │ │ ├── extend_test.go │ │ │ ├── sections.go │ │ │ ├── sign.go │ │ │ ├── sign_test.go │ │ │ └── testdata/ │ │ │ ├── a │ │ │ ├── b │ │ │ └── c │ │ ├── measure.go │ │ ├── measure_test.go │ │ └── testdata/ │ │ └── pcr-signing-key.pem │ ├── meta/ │ │ ├── internal/ │ │ │ └── adv/ │ │ │ ├── adv.go │ │ │ ├── syslinux/ │ │ │ │ ├── syslinux.go │ │ │ │ ├── syslinux_test.go │ │ │ │ └── testdata/ │ │ │ │ └── adv.sys │ │ │ └── talos/ │ │ │ ├── talos.go │ │ │ └── talos_test.go │ │ ├── meta.go │ │ └── meta_test.go │ ├── miniprocfs/ │ │ ├── miniprocfs.go │ │ ├── processes.go │ │ ├── processes_test.go │ │ └── testdata/ │ │ ├── 1920080/ │ │ │ ├── cmdline │ │ │ ├── comm │ │ │ └── stat │ │ ├── 3731034/ │ │ │ ├── cmdline │ │ │ ├── comm │ │ │ └── stat │ │ ├── keys │ │ └── kmsg │ ├── mount/ │ │ ├── switchroot/ │ │ │ ├── switchroot.go │ │ │ └── switchroot_test.go │ │ └── v3/ │ │ ├── all.go │ │ ├── helpers.go │ │ ├── manager.go │ │ ├── managers.go │ │ ├── mount.go │ │ ├── point.go │ │ └── unmount.go │ ├── msguid/ │ │ ├── LICENSE │ │ ├── README.md │ │ ├── msguid.go │ │ └── msguid_test.go │ ├── ntp/ │ │ ├── consts.go │ │ ├── interfaces.go │ │ ├── internal/ │ │ │ └── spike/ │ │ │ ├── spike.go │ │ │ └── spike_test.go │ │ ├── ntp.go │ │ └── ntp_test.go │ ├── partition/ │ │ ├── constants.go │ │ ├── format.go │ │ ├── helpers.go │ │ ├── partition.go │ │ └── wipe.go │ ├── pcap/ │ │ └── pcap.go │ ├── pci/ │ │ ├── pci.go │ │ └── sysfs.go │ ├── rng/ │ │ ├── pool.go │ │ ├── rng.go │ │ └── tpm.go │ ├── rootfs/ │ │ └── rootfs_test.go │ ├── secureboot/ │ │ ├── database/ │ │ │ ├── certs/ │ │ │ │ ├── db/ │ │ │ │ │ ├── MicCorUEFCA2011_2011-06-27.der │ │ │ │ │ ├── microsoft option rom uefi ca 2023.der │ │ │ │ │ └── microsoft uefi ca 2023.der │ │ │ │ └── kek/ │ │ │ │ ├── MicCorKEKCA2011_2011-06-24.der │ │ │ │ └── microsoft corporation kek 2k ca 2023.der │ │ │ └── database.go │ │ ├── pesign/ │ │ │ ├── pesign.go │ │ │ ├── pesign_test.go │ │ │ └── testdata/ │ │ │ └── systemd-bootx64.efi │ │ ├── secureboot.go │ │ └── tpm2/ │ │ ├── keys.go │ │ ├── pcr.go │ │ ├── pcr_test.go │ │ ├── policy.go │ │ ├── policy_test.go │ │ ├── seal.go │ │ ├── signature.go │ │ ├── testdata/ │ │ │ └── pcr-signing-crt.pem │ │ ├── tpm2.go │ │ └── unseal.go │ ├── selinux/ │ │ ├── policy/ │ │ │ ├── file_contexts │ │ │ ├── policy.33 │ │ │ └── selinux/ │ │ │ ├── common/ │ │ │ │ ├── classmaps.cil │ │ │ │ ├── files.cil │ │ │ │ ├── network.cil │ │ │ │ ├── processes.cil │ │ │ │ └── typeattributes.cil │ │ │ ├── immutable/ │ │ │ │ ├── classes.cil │ │ │ │ ├── fs.cil │ │ │ │ ├── preamble.cil │ │ │ │ ├── roles.cil │ │ │ │ └── sids.cil │ │ │ └── services/ │ │ │ ├── cri.cil │ │ │ ├── dashboard.cil │ │ │ ├── kubelet.cil │ │ │ ├── machined.cil │ │ │ ├── selinux.cil │ │ │ ├── system-containerd.cil │ │ │ ├── system-containers.cil │ │ │ └── udev.cil │ │ └── selinux.go │ ├── smbios/ │ │ ├── oem.go │ │ └── smbios.go │ ├── timex/ │ │ └── timex.go │ ├── toml/ │ │ ├── merge.go │ │ ├── merge_test.go │ │ ├── testdata/ │ │ │ ├── 1.toml │ │ │ ├── 2.toml │ │ │ ├── 3.toml │ │ │ └── expected.toml │ │ └── toml.go │ ├── tpm/ │ │ ├── tpm_linux.go │ │ └── tpm_other.go │ ├── uki/ │ │ ├── assemble.go │ │ ├── generate.go │ │ ├── internal/ │ │ │ └── pe/ │ │ │ ├── extract.go │ │ │ ├── extract_test.go │ │ │ ├── native.go │ │ │ ├── objcopy.go │ │ │ ├── pe.go │ │ │ ├── pe_test.go │ │ │ └── testdata/ │ │ │ ├── addonx64.efi.stub │ │ │ └── linuxx64.efi.stub │ │ ├── kernel.go │ │ ├── kernel_test.go │ │ ├── sbat.go │ │ ├── sbat_test.go │ │ ├── testdata/ │ │ │ └── kernel │ │ ├── uki.go │ │ └── uki_test.go │ └── zboot/ │ └── zboot.go ├── pkg/ │ ├── archiver/ │ │ ├── archiver.go │ │ ├── archiver_test.go │ │ ├── tar.go │ │ ├── tar_test.go │ │ ├── untar.go │ │ ├── walker.go │ │ └── walker_test.go │ ├── argsbuilder/ │ │ ├── argsbuilder_args.go │ │ ├── argsbuilder_interface.go │ │ └── argsbuilder_test.go │ ├── bytesize/ │ │ ├── bytesize.go │ │ └── bytesize_test.go │ ├── chunker/ │ │ ├── chunker.go │ │ ├── file/ │ │ │ ├── file.go │ │ │ └── file_test.go │ │ └── stream/ │ │ ├── stream.go │ │ └── stream_test.go │ ├── cli/ │ │ ├── cli.go │ │ ├── context.go │ │ ├── output.go │ │ └── should.go │ ├── cluster/ │ │ ├── apply-config.go │ │ ├── bootstrap.go │ │ ├── check/ │ │ │ ├── apid.go │ │ │ ├── check.go │ │ │ ├── check_test.go │ │ │ ├── default.go │ │ │ ├── diagnostics.go │ │ │ ├── discovery.go │ │ │ ├── etcd.go │ │ │ ├── events.go │ │ │ ├── kubernetes.go │ │ │ ├── nodes.go │ │ │ ├── options.go │ │ │ ├── reporter.go │ │ │ └── service.go │ │ ├── cluster.go │ │ ├── cluster_test.go │ │ ├── config.go │ │ ├── crashdump.go │ │ ├── hydrophone/ │ │ │ ├── hydrophone.go │ │ │ └── product.go │ │ ├── kubelet.go │ │ ├── kubernetes/ │ │ │ ├── compat.go │ │ │ ├── compat_test.go │ │ │ ├── detect.go │ │ │ ├── kubelet.go │ │ │ ├── kubernetes.go │ │ │ ├── kubernetes_test.go │ │ │ ├── patch.go │ │ │ ├── talos_managed.go │ │ │ ├── upgrade.go │ │ │ └── upgrade_test.go │ │ ├── kubernetes.go │ │ ├── local.go │ │ ├── logsaarchive.go │ │ └── provision.go │ ├── conditions/ │ │ ├── all.go │ │ ├── all_test.go │ │ ├── conditions.go │ │ ├── files.go │ │ ├── files_test.go │ │ ├── kubeconfig.go │ │ ├── none.go │ │ ├── poll.go │ │ └── poll_test.go │ ├── download/ │ │ ├── download.go │ │ ├── download_test.go │ │ └── tftp.go │ ├── filetree/ │ │ ├── chown.go │ │ └── filetree.go │ ├── flags/ │ │ └── flags.go │ ├── follow/ │ │ ├── follow.go │ │ └── follow_test.go │ ├── grpc/ │ │ ├── factory/ │ │ │ ├── factory.go │ │ │ └── factory_test.go │ │ ├── gen/ │ │ │ ├── gen_test.go │ │ │ ├── local.go │ │ │ └── remote.go │ │ ├── middleware/ │ │ │ ├── auth/ │ │ │ │ └── basic/ │ │ │ │ ├── basic.go │ │ │ │ ├── token.go │ │ │ │ ├── username_and_password.go │ │ │ │ └── username_and_password_test.go │ │ │ ├── authz/ │ │ │ │ ├── authorizer.go │ │ │ │ ├── authorizer_test.go │ │ │ │ ├── context.go │ │ │ │ ├── injector.go │ │ │ │ └── metadata.go │ │ │ └── log/ │ │ │ ├── log.go │ │ │ └── log_test.go │ │ └── proxy/ │ │ └── backend/ │ │ ├── backend.go │ │ ├── local.go │ │ └── local_test.go │ ├── httpdefaults/ │ │ ├── httpdefaults.go │ │ ├── tls.go │ │ └── useragent.go │ ├── imager/ │ │ ├── cache/ │ │ │ └── cache.go │ │ ├── embed.go │ │ ├── embed_test.go │ │ ├── extensions/ │ │ │ ├── contents.go │ │ │ ├── extensions.go │ │ │ ├── printer.go │ │ │ └── rebuild.go │ │ ├── filemap/ │ │ │ ├── filemap.go │ │ │ └── filemap_test.go │ │ ├── imager.go │ │ ├── imager_test.go │ │ ├── iso/ │ │ │ ├── grub.cfg │ │ │ ├── grub.go │ │ │ ├── hybrid.go │ │ │ ├── iso.go │ │ │ ├── iso_test.go │ │ │ ├── loader.conf.tmpl │ │ │ └── uefi.go │ │ ├── out.go │ │ ├── ova/ │ │ │ └── ova.go │ │ ├── overlay/ │ │ │ └── executor/ │ │ │ └── executor.go │ │ ├── post.go │ │ ├── profile/ │ │ │ ├── customization.go │ │ │ ├── deep_copy.generated.go │ │ │ ├── default.go │ │ │ ├── input.go │ │ │ ├── internal/ │ │ │ │ └── signer/ │ │ │ │ ├── aws/ │ │ │ │ │ ├── aws.go │ │ │ │ │ ├── aws_test.go │ │ │ │ │ ├── pcr.go │ │ │ │ │ └── secureboot.go │ │ │ │ ├── azure/ │ │ │ │ │ ├── azure.go │ │ │ │ │ ├── azure_test.go │ │ │ │ │ ├── pcr.go │ │ │ │ │ └── secureboot.go │ │ │ │ └── file/ │ │ │ │ ├── pcr.go │ │ │ │ └── secureboot.go │ │ │ ├── output.go │ │ │ ├── output_test.go │ │ │ ├── outputkind_enumer.go │ │ │ ├── profile.go │ │ │ ├── profile_test.go │ │ │ └── testdata/ │ │ │ ├── akamai-amd64-1.10.0.yaml │ │ │ ├── akamai-amd64-1.11.0.yaml │ │ │ ├── akamai-amd64-1.12.0.yaml │ │ │ ├── akamai-amd64-1.13.0.yaml │ │ │ ├── akamai-amd64-1.9.0.yaml │ │ │ ├── akamai-arm64-1.10.0.yaml │ │ │ ├── akamai-arm64-1.11.0.yaml │ │ │ ├── akamai-arm64-1.12.0.yaml │ │ │ ├── akamai-arm64-1.13.0.yaml │ │ │ ├── akamai-arm64-1.9.0.yaml │ │ │ ├── aws-amd64-1.10.0.yaml │ │ │ ├── aws-amd64-1.11.0.yaml │ │ │ ├── aws-amd64-1.12.0.yaml │ │ │ ├── aws-amd64-1.13.0.yaml │ │ │ ├── aws-amd64-1.9.0.yaml │ │ │ ├── aws-arm64-1.10.0.yaml │ │ │ ├── aws-arm64-1.11.0.yaml │ │ │ ├── aws-arm64-1.12.0.yaml │ │ │ ├── aws-arm64-1.13.0.yaml │ │ │ ├── aws-arm64-1.9.0.yaml │ │ │ ├── azure-amd64-1.10.0.yaml │ │ │ ├── azure-amd64-1.11.0.yaml │ │ │ ├── azure-amd64-1.12.0.yaml │ │ │ ├── azure-amd64-1.13.0.yaml │ │ │ ├── azure-amd64-1.9.0.yaml │ │ │ ├── azure-arm64-1.10.0.yaml │ │ │ ├── azure-arm64-1.11.0.yaml │ │ │ ├── azure-arm64-1.12.0.yaml │ │ │ ├── azure-arm64-1.13.0.yaml │ │ │ ├── azure-arm64-1.9.0.yaml │ │ │ ├── cloudstack-amd64-1.10.0.yaml │ │ │ ├── cloudstack-amd64-1.11.0.yaml │ │ │ ├── cloudstack-amd64-1.12.0.yaml │ │ │ ├── cloudstack-amd64-1.13.0.yaml │ │ │ ├── cloudstack-amd64-1.9.0.yaml │ │ │ ├── cloudstack-arm64-1.10.0.yaml │ │ │ ├── cloudstack-arm64-1.11.0.yaml │ │ │ ├── cloudstack-arm64-1.12.0.yaml │ │ │ ├── cloudstack-arm64-1.13.0.yaml │ │ │ ├── cloudstack-arm64-1.9.0.yaml │ │ │ ├── digital-ocean-amd64-1.10.0.yaml │ │ │ ├── digital-ocean-amd64-1.11.0.yaml │ │ │ ├── digital-ocean-amd64-1.12.0.yaml │ │ │ ├── digital-ocean-amd64-1.13.0.yaml │ │ │ ├── digital-ocean-amd64-1.9.0.yaml │ │ │ ├── digital-ocean-arm64-1.10.0.yaml │ │ │ ├── digital-ocean-arm64-1.11.0.yaml │ │ │ ├── digital-ocean-arm64-1.12.0.yaml │ │ │ ├── digital-ocean-arm64-1.13.0.yaml │ │ │ ├── digital-ocean-arm64-1.9.0.yaml │ │ │ ├── exoscale-amd64-1.10.0.yaml │ │ │ ├── exoscale-amd64-1.11.0.yaml │ │ │ ├── exoscale-amd64-1.12.0.yaml │ │ │ ├── exoscale-amd64-1.13.0.yaml │ │ │ ├── exoscale-amd64-1.9.0.yaml │ │ │ ├── exoscale-arm64-1.10.0.yaml │ │ │ ├── exoscale-arm64-1.11.0.yaml │ │ │ ├── exoscale-arm64-1.12.0.yaml │ │ │ ├── exoscale-arm64-1.13.0.yaml │ │ │ ├── exoscale-arm64-1.9.0.yaml │ │ │ ├── gcp-amd64-1.10.0.yaml │ │ │ ├── gcp-amd64-1.11.0.yaml │ │ │ ├── gcp-amd64-1.12.0.yaml │ │ │ ├── gcp-amd64-1.13.0.yaml │ │ │ ├── gcp-amd64-1.9.0.yaml │ │ │ ├── gcp-arm64-1.10.0.yaml │ │ │ ├── gcp-arm64-1.11.0.yaml │ │ │ ├── gcp-arm64-1.12.0.yaml │ │ │ ├── gcp-arm64-1.13.0.yaml │ │ │ ├── gcp-arm64-1.9.0.yaml │ │ │ ├── hcloud-amd64-1.10.0.yaml │ │ │ ├── hcloud-amd64-1.11.0.yaml │ │ │ ├── hcloud-amd64-1.12.0.yaml │ │ │ ├── hcloud-amd64-1.13.0.yaml │ │ │ ├── hcloud-amd64-1.9.0.yaml │ │ │ ├── hcloud-arm64-1.10.0.yaml │ │ │ ├── hcloud-arm64-1.11.0.yaml │ │ │ ├── hcloud-arm64-1.12.0.yaml │ │ │ ├── hcloud-arm64-1.13.0.yaml │ │ │ ├── hcloud-arm64-1.9.0.yaml │ │ │ ├── installer-amd64-1.10.0.yaml │ │ │ ├── installer-amd64-1.11.0.yaml │ │ │ ├── installer-amd64-1.12.0.yaml │ │ │ ├── installer-amd64-1.13.0.yaml │ │ │ ├── installer-amd64-1.9.0.yaml │ │ │ ├── installer-arm64-1.10.0.yaml │ │ │ ├── installer-arm64-1.11.0.yaml │ │ │ ├── installer-arm64-1.12.0.yaml │ │ │ ├── installer-arm64-1.13.0.yaml │ │ │ ├── installer-arm64-1.9.0.yaml │ │ │ ├── iso-amd64-1.10.0.yaml │ │ │ ├── iso-amd64-1.11.0.yaml │ │ │ ├── iso-amd64-1.12.0.yaml │ │ │ ├── iso-amd64-1.13.0.yaml │ │ │ ├── iso-amd64-1.9.0.yaml │ │ │ ├── iso-arm64-1.10.0.yaml │ │ │ ├── iso-arm64-1.11.0.yaml │ │ │ ├── iso-arm64-1.12.0.yaml │ │ │ ├── iso-arm64-1.13.0.yaml │ │ │ ├── iso-arm64-1.9.0.yaml │ │ │ ├── metal-amd64-1.10.0.yaml │ │ │ ├── metal-amd64-1.11.0.yaml │ │ │ ├── metal-amd64-1.12.0.yaml │ │ │ ├── metal-amd64-1.13.0.yaml │ │ │ ├── metal-amd64-1.9.0.yaml │ │ │ ├── metal-arm64-1.10.0.yaml │ │ │ ├── metal-arm64-1.11.0.yaml │ │ │ ├── metal-arm64-1.12.0.yaml │ │ │ ├── metal-arm64-1.13.0.yaml │ │ │ ├── metal-arm64-1.9.0.yaml │ │ │ ├── metal-uki-amd64-1.10.0.yaml │ │ │ ├── metal-uki-amd64-1.11.0.yaml │ │ │ ├── metal-uki-amd64-1.12.0.yaml │ │ │ ├── metal-uki-amd64-1.13.0.yaml │ │ │ ├── metal-uki-amd64-1.9.0.yaml │ │ │ ├── metal-uki-arm64-1.10.0.yaml │ │ │ ├── metal-uki-arm64-1.11.0.yaml │ │ │ ├── metal-uki-arm64-1.12.0.yaml │ │ │ ├── metal-uki-arm64-1.13.0.yaml │ │ │ ├── metal-uki-arm64-1.9.0.yaml │ │ │ ├── nocloud-amd64-1.10.0.yaml │ │ │ ├── nocloud-amd64-1.11.0.yaml │ │ │ ├── nocloud-amd64-1.12.0.yaml │ │ │ ├── nocloud-amd64-1.13.0.yaml │ │ │ ├── nocloud-amd64-1.9.0.yaml │ │ │ ├── nocloud-arm64-1.10.0.yaml │ │ │ ├── nocloud-arm64-1.11.0.yaml │ │ │ ├── nocloud-arm64-1.12.0.yaml │ │ │ ├── nocloud-arm64-1.13.0.yaml │ │ │ ├── nocloud-arm64-1.9.0.yaml │ │ │ ├── opennebula-amd64-1.10.0.yaml │ │ │ ├── opennebula-amd64-1.11.0.yaml │ │ │ ├── opennebula-amd64-1.12.0.yaml │ │ │ ├── opennebula-amd64-1.13.0.yaml │ │ │ ├── opennebula-amd64-1.9.0.yaml │ │ │ ├── opennebula-arm64-1.10.0.yaml │ │ │ ├── opennebula-arm64-1.11.0.yaml │ │ │ ├── opennebula-arm64-1.12.0.yaml │ │ │ ├── opennebula-arm64-1.13.0.yaml │ │ │ ├── opennebula-arm64-1.9.0.yaml │ │ │ ├── openstack-amd64-1.10.0.yaml │ │ │ ├── openstack-amd64-1.11.0.yaml │ │ │ ├── openstack-amd64-1.12.0.yaml │ │ │ ├── openstack-amd64-1.13.0.yaml │ │ │ ├── openstack-amd64-1.9.0.yaml │ │ │ ├── openstack-arm64-1.10.0.yaml │ │ │ ├── openstack-arm64-1.11.0.yaml │ │ │ ├── openstack-arm64-1.12.0.yaml │ │ │ ├── openstack-arm64-1.13.0.yaml │ │ │ ├── openstack-arm64-1.9.0.yaml │ │ │ ├── oracle-amd64-1.10.0.yaml │ │ │ ├── oracle-amd64-1.11.0.yaml │ │ │ ├── oracle-amd64-1.12.0.yaml │ │ │ ├── oracle-amd64-1.13.0.yaml │ │ │ ├── oracle-amd64-1.9.0.yaml │ │ │ ├── oracle-arm64-1.10.0.yaml │ │ │ ├── oracle-arm64-1.11.0.yaml │ │ │ ├── oracle-arm64-1.12.0.yaml │ │ │ ├── oracle-arm64-1.13.0.yaml │ │ │ ├── oracle-arm64-1.9.0.yaml │ │ │ ├── scaleway-amd64-1.10.0.yaml │ │ │ ├── scaleway-amd64-1.11.0.yaml │ │ │ ├── scaleway-amd64-1.12.0.yaml │ │ │ ├── scaleway-amd64-1.13.0.yaml │ │ │ ├── scaleway-amd64-1.9.0.yaml │ │ │ ├── scaleway-arm64-1.10.0.yaml │ │ │ ├── scaleway-arm64-1.11.0.yaml │ │ │ ├── scaleway-arm64-1.12.0.yaml │ │ │ ├── scaleway-arm64-1.13.0.yaml │ │ │ ├── scaleway-arm64-1.9.0.yaml │ │ │ ├── secureboot-installer-amd64-1.10.0.yaml │ │ │ ├── secureboot-installer-amd64-1.11.0.yaml │ │ │ ├── secureboot-installer-amd64-1.12.0.yaml │ │ │ ├── secureboot-installer-amd64-1.13.0.yaml │ │ │ ├── secureboot-installer-amd64-1.9.0.yaml │ │ │ ├── secureboot-installer-arm64-1.10.0.yaml │ │ │ ├── secureboot-installer-arm64-1.11.0.yaml │ │ │ ├── secureboot-installer-arm64-1.12.0.yaml │ │ │ ├── secureboot-installer-arm64-1.13.0.yaml │ │ │ ├── secureboot-installer-arm64-1.9.0.yaml │ │ │ ├── secureboot-iso-amd64-1.10.0.yaml │ │ │ ├── secureboot-iso-amd64-1.11.0.yaml │ │ │ ├── secureboot-iso-amd64-1.12.0.yaml │ │ │ ├── secureboot-iso-amd64-1.13.0.yaml │ │ │ ├── secureboot-iso-amd64-1.9.0.yaml │ │ │ ├── secureboot-iso-arm64-1.10.0.yaml │ │ │ ├── secureboot-iso-arm64-1.11.0.yaml │ │ │ ├── secureboot-iso-arm64-1.12.0.yaml │ │ │ ├── secureboot-iso-arm64-1.13.0.yaml │ │ │ ├── secureboot-iso-arm64-1.9.0.yaml │ │ │ ├── secureboot-metal-amd64-1.10.0.yaml │ │ │ ├── secureboot-metal-amd64-1.11.0.yaml │ │ │ ├── secureboot-metal-amd64-1.12.0.yaml │ │ │ ├── secureboot-metal-amd64-1.13.0.yaml │ │ │ ├── secureboot-metal-amd64-1.9.0.yaml │ │ │ ├── secureboot-metal-arm64-1.10.0.yaml │ │ │ ├── secureboot-metal-arm64-1.11.0.yaml │ │ │ ├── secureboot-metal-arm64-1.12.0.yaml │ │ │ ├── secureboot-metal-arm64-1.13.0.yaml │ │ │ ├── secureboot-metal-arm64-1.9.0.yaml │ │ │ ├── secureboot-metal-uki-amd64-1.10.0.yaml │ │ │ ├── secureboot-metal-uki-amd64-1.11.0.yaml │ │ │ ├── secureboot-metal-uki-amd64-1.12.0.yaml │ │ │ ├── secureboot-metal-uki-amd64-1.13.0.yaml │ │ │ ├── secureboot-metal-uki-amd64-1.9.0.yaml │ │ │ ├── secureboot-metal-uki-arm64-1.10.0.yaml │ │ │ ├── secureboot-metal-uki-arm64-1.11.0.yaml │ │ │ ├── secureboot-metal-uki-arm64-1.12.0.yaml │ │ │ ├── secureboot-metal-uki-arm64-1.13.0.yaml │ │ │ ├── secureboot-metal-uki-arm64-1.9.0.yaml │ │ │ ├── upcloud-amd64-1.10.0.yaml │ │ │ ├── upcloud-amd64-1.11.0.yaml │ │ │ ├── upcloud-amd64-1.12.0.yaml │ │ │ ├── upcloud-amd64-1.13.0.yaml │ │ │ ├── upcloud-amd64-1.9.0.yaml │ │ │ ├── upcloud-arm64-1.10.0.yaml │ │ │ ├── upcloud-arm64-1.11.0.yaml │ │ │ ├── upcloud-arm64-1.12.0.yaml │ │ │ ├── upcloud-arm64-1.13.0.yaml │ │ │ ├── upcloud-arm64-1.9.0.yaml │ │ │ ├── vmware-amd64-1.10.0.yaml │ │ │ ├── vmware-amd64-1.11.0.yaml │ │ │ ├── vmware-amd64-1.12.0.yaml │ │ │ ├── vmware-amd64-1.13.0.yaml │ │ │ ├── vmware-amd64-1.9.0.yaml │ │ │ ├── vmware-arm64-1.10.0.yaml │ │ │ ├── vmware-arm64-1.11.0.yaml │ │ │ ├── vmware-arm64-1.12.0.yaml │ │ │ ├── vmware-arm64-1.13.0.yaml │ │ │ ├── vmware-arm64-1.9.0.yaml │ │ │ ├── vultr-amd64-1.10.0.yaml │ │ │ ├── vultr-amd64-1.11.0.yaml │ │ │ ├── vultr-amd64-1.12.0.yaml │ │ │ ├── vultr-amd64-1.13.0.yaml │ │ │ ├── vultr-amd64-1.9.0.yaml │ │ │ ├── vultr-arm64-1.10.0.yaml │ │ │ ├── vultr-arm64-1.11.0.yaml │ │ │ ├── vultr-arm64-1.12.0.yaml │ │ │ ├── vultr-arm64-1.13.0.yaml │ │ │ └── vultr-arm64-1.9.0.yaml │ │ ├── progress.go │ │ ├── qemuimg/ │ │ │ └── qemuimg.go │ │ ├── utils/ │ │ │ ├── copy.go │ │ │ ├── epoch.go │ │ │ ├── raw.go │ │ │ ├── touch.go │ │ │ └── utils.go │ │ └── vmdkconvert/ │ │ └── vmdkconvert.go │ ├── images/ │ │ ├── images.go │ │ └── list.go │ ├── kernel/ │ │ ├── kernel.go │ │ └── kspp/ │ │ └── kspp.go │ ├── kubeconfig/ │ │ ├── generate.go │ │ ├── generate_test.go │ │ └── kubeconfig.go │ ├── kubernetes/ │ │ ├── errors.go │ │ ├── inject/ │ │ │ └── serviceaccount.go │ │ ├── klog.go │ │ ├── kubelet/ │ │ │ └── kubelet.go │ │ ├── kubernetes.go │ │ ├── version.go │ │ └── version_test.go │ ├── logging/ │ │ ├── error_suppress.go │ │ ├── error_suppress_test.go │ │ ├── logging.go │ │ └── zap.go │ ├── machinery/ │ │ ├── api/ │ │ │ ├── api.go │ │ │ ├── cluster/ │ │ │ │ ├── cluster.pb.go │ │ │ │ ├── cluster_grpc.pb.go │ │ │ │ └── cluster_vtproto.pb.go │ │ │ ├── common/ │ │ │ │ ├── common.pb.go │ │ │ │ └── common_vtproto.pb.go │ │ │ ├── inspect/ │ │ │ │ ├── inspect.pb.go │ │ │ │ ├── inspect_grpc.pb.go │ │ │ │ └── inspect_vtproto.pb.go │ │ │ ├── machine/ │ │ │ │ ├── debug.pb.go │ │ │ │ ├── debug_grpc.pb.go │ │ │ │ ├── debug_vtproto.pb.go │ │ │ │ ├── image.go │ │ │ │ ├── image.pb.go │ │ │ │ ├── image_grpc.pb.go │ │ │ │ ├── image_vtproto.pb.go │ │ │ │ ├── lifecycle.go │ │ │ │ ├── lifecycle.pb.go │ │ │ │ ├── lifecycle_grpc.pb.go │ │ │ │ ├── lifecycle_vtproto.pb.go │ │ │ │ ├── machine.pb.go │ │ │ │ ├── machine_grpc.pb.go │ │ │ │ └── machine_vtproto.pb.go │ │ │ ├── resource/ │ │ │ │ ├── config/ │ │ │ │ │ ├── config.pb.go │ │ │ │ │ └── config_vtproto.pb.go │ │ │ │ ├── definitions/ │ │ │ │ │ ├── block/ │ │ │ │ │ │ ├── block.pb.go │ │ │ │ │ │ └── block_vtproto.pb.go │ │ │ │ │ ├── cluster/ │ │ │ │ │ │ ├── cluster.pb.go │ │ │ │ │ │ └── cluster_vtproto.pb.go │ │ │ │ │ ├── cri/ │ │ │ │ │ │ ├── cri.pb.go │ │ │ │ │ │ └── cri_vtproto.pb.go │ │ │ │ │ ├── enums/ │ │ │ │ │ │ └── enums.pb.go │ │ │ │ │ ├── etcd/ │ │ │ │ │ │ ├── etcd.pb.go │ │ │ │ │ │ └── etcd_vtproto.pb.go │ │ │ │ │ ├── extensions/ │ │ │ │ │ │ ├── extensions.pb.go │ │ │ │ │ │ └── extensions_vtproto.pb.go │ │ │ │ │ ├── files/ │ │ │ │ │ │ ├── files.pb.go │ │ │ │ │ │ └── files_vtproto.pb.go │ │ │ │ │ ├── hardware/ │ │ │ │ │ │ ├── hardware.pb.go │ │ │ │ │ │ └── hardware_vtproto.pb.go │ │ │ │ │ ├── k8s/ │ │ │ │ │ │ ├── k8s.pb.go │ │ │ │ │ │ └── k8s_vtproto.pb.go │ │ │ │ │ ├── kubeaccess/ │ │ │ │ │ │ ├── kubeaccess.pb.go │ │ │ │ │ │ └── kubeaccess_vtproto.pb.go │ │ │ │ │ ├── kubespan/ │ │ │ │ │ │ ├── kubespan.pb.go │ │ │ │ │ │ └── kubespan_vtproto.pb.go │ │ │ │ │ ├── network/ │ │ │ │ │ │ ├── network.pb.go │ │ │ │ │ │ └── network_vtproto.pb.go │ │ │ │ │ ├── perf/ │ │ │ │ │ │ ├── perf.pb.go │ │ │ │ │ │ └── perf_vtproto.pb.go │ │ │ │ │ ├── proto/ │ │ │ │ │ │ ├── proto.pb.go │ │ │ │ │ │ └── proto_vtproto.pb.go │ │ │ │ │ ├── runtime/ │ │ │ │ │ │ ├── runtime.pb.go │ │ │ │ │ │ └── runtime_vtproto.pb.go │ │ │ │ │ ├── secrets/ │ │ │ │ │ │ ├── secrets.pb.go │ │ │ │ │ │ └── secrets_vtproto.pb.go │ │ │ │ │ ├── security/ │ │ │ │ │ │ ├── security.pb.go │ │ │ │ │ │ └── security_vtproto.pb.go │ │ │ │ │ ├── siderolink/ │ │ │ │ │ │ ├── siderolink.pb.go │ │ │ │ │ │ └── siderolink_vtproto.pb.go │ │ │ │ │ ├── time/ │ │ │ │ │ │ ├── time.pb.go │ │ │ │ │ │ └── time_vtproto.pb.go │ │ │ │ │ └── v1alpha1/ │ │ │ │ │ ├── v1alpha1.pb.go │ │ │ │ │ └── v1alpha1_vtproto.pb.go │ │ │ │ └── network/ │ │ │ │ ├── device_config.pb.go │ │ │ │ └── device_config_vtproto.pb.go │ │ │ ├── security/ │ │ │ │ ├── security.pb.go │ │ │ │ ├── security_grpc.pb.go │ │ │ │ └── security_vtproto.pb.go │ │ │ ├── storage/ │ │ │ │ ├── storage.pb.go │ │ │ │ ├── storage_grpc.pb.go │ │ │ │ └── storage_vtproto.pb.go │ │ │ └── time/ │ │ │ ├── time.pb.go │ │ │ ├── time_grpc.pb.go │ │ │ └── time_vtproto.pb.go │ │ ├── cel/ │ │ │ ├── build.go │ │ │ ├── build_test.go │ │ │ ├── cel.go │ │ │ ├── cel_test.go │ │ │ └── celenv/ │ │ │ ├── celenv.go │ │ │ └── celenv_test.go │ │ ├── client/ │ │ │ ├── basic_auth.go │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── config/ │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ └── path.go │ │ │ ├── connection.go │ │ │ ├── connection_test.go │ │ │ ├── context.go │ │ │ ├── dialer/ │ │ │ │ ├── dialer.go │ │ │ │ ├── dialer_test.go │ │ │ │ └── proxy.go │ │ │ ├── events.go │ │ │ ├── export_test.go │ │ │ ├── grpc_connection_wrapper.go │ │ │ ├── insecure_credentials.go │ │ │ ├── inspect.go │ │ │ ├── options.go │ │ │ ├── peer.go │ │ │ ├── reply.go │ │ │ ├── reply_test.go │ │ │ ├── resolver/ │ │ │ │ ├── resolver.go │ │ │ │ ├── roundrobin.go │ │ │ │ └── roundrobin_test.go │ │ │ ├── resources.go │ │ │ ├── secure_credentials.go │ │ │ ├── status.go │ │ │ └── status_test.go │ │ ├── compatibility/ │ │ │ ├── compatibility.go │ │ │ ├── kubernetes_version.go │ │ │ ├── kubernetes_version_test.go │ │ │ ├── talos110/ │ │ │ │ └── talos110.go │ │ │ ├── talos111/ │ │ │ │ └── talos111.go │ │ │ ├── talos112/ │ │ │ │ └── talos112.go │ │ │ ├── talos113/ │ │ │ │ └── talos113.go │ │ │ ├── talos12/ │ │ │ │ └── talos12.go │ │ │ ├── talos13/ │ │ │ │ └── talos13.go │ │ │ ├── talos14/ │ │ │ │ └── talos14.go │ │ │ ├── talos15/ │ │ │ │ └── talos15.go │ │ │ ├── talos16/ │ │ │ │ └── talos16.go │ │ │ ├── talos17/ │ │ │ │ └── talos17.go │ │ │ ├── talos18/ │ │ │ │ └── talos18.go │ │ │ ├── talos19/ │ │ │ │ └── talos19.go │ │ │ ├── talos_version.go │ │ │ └── talos_version_test.go │ │ ├── config/ │ │ │ ├── bundle/ │ │ │ │ ├── bundle.go │ │ │ │ ├── bundle_test.go │ │ │ │ └── options.go │ │ │ ├── config/ │ │ │ │ ├── cluster.go │ │ │ │ ├── config.go │ │ │ │ ├── cri.go │ │ │ │ ├── document.go │ │ │ │ ├── encoder.go │ │ │ │ ├── extension_service_config.go │ │ │ │ ├── helpers.go │ │ │ │ ├── kubespan.go │ │ │ │ ├── machine.go │ │ │ │ ├── network.go │ │ │ │ ├── pci_driver_rebind.go │ │ │ │ ├── runtime.go │ │ │ │ ├── security.go │ │ │ │ ├── siderolink.go │ │ │ │ ├── validate.go │ │ │ │ └── volume.go │ │ │ ├── config.go │ │ │ ├── config_schema_test.go │ │ │ ├── configdiff/ │ │ │ │ ├── configdiff.go │ │ │ │ ├── configdiff_test.go │ │ │ │ ├── mergepatch.go │ │ │ │ ├── mergepatch_test.go │ │ │ │ └── testdata/ │ │ │ │ ├── modified.yaml │ │ │ │ └── original.yaml │ │ │ ├── configloader/ │ │ │ │ ├── configloader.go │ │ │ │ ├── configloader_fuzz_test.go │ │ │ │ ├── configloader_test.go │ │ │ │ ├── internal/ │ │ │ │ │ └── decoder/ │ │ │ │ │ ├── decoder.go │ │ │ │ │ ├── decoder_test.go │ │ │ │ │ ├── delete.go │ │ │ │ │ ├── delete_test.go │ │ │ │ │ ├── selector.go │ │ │ │ │ └── testdata/ │ │ │ │ │ ├── delete/ │ │ │ │ │ │ ├── delete.yaml │ │ │ │ │ │ └── delete_expected.yaml │ │ │ │ │ └── double/ │ │ │ │ │ └── v1alpha1.yaml │ │ │ │ └── testdata/ │ │ │ │ ├── controlplane.test │ │ │ │ ├── empty1.test │ │ │ │ ├── empty2.test │ │ │ │ ├── fuzz/ │ │ │ │ │ └── FuzzConfigLoader/ │ │ │ │ │ └── dfd1adfe630cffc2 │ │ │ │ ├── multidoc1.test │ │ │ │ ├── multidoc2.test │ │ │ │ ├── multidoc3.test │ │ │ │ └── worker.test │ │ │ ├── configpatcher/ │ │ │ │ ├── apply.go │ │ │ │ ├── apply_test.go │ │ │ │ ├── configpatcher.go │ │ │ │ ├── configpatcher_test.go │ │ │ │ ├── json6902.go │ │ │ │ ├── load.go │ │ │ │ ├── load_test.go │ │ │ │ ├── strategic.go │ │ │ │ └── testdata/ │ │ │ │ ├── apply/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ ├── expected.yaml │ │ │ │ │ ├── expected_manifests.yaml │ │ │ │ │ ├── jsonpatch1.yaml │ │ │ │ │ ├── jsonpatch2.yaml │ │ │ │ │ ├── strategic1.yaml │ │ │ │ │ ├── strategic2.yaml │ │ │ │ │ ├── strategic3.yaml │ │ │ │ │ └── strategic4.yaml │ │ │ │ ├── auditpolicy/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ ├── expected.yaml │ │ │ │ │ └── patch1.yaml │ │ │ │ ├── multidoc/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ ├── expected.yaml │ │ │ │ │ ├── jsonpatch.yaml │ │ │ │ │ ├── strategic1.yaml │ │ │ │ │ └── strategic2.yaml │ │ │ │ ├── patch.json │ │ │ │ ├── patch.yaml │ │ │ │ ├── patchdelete/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ ├── controlplane_expected.yaml │ │ │ │ │ ├── controlplane_orig.yaml │ │ │ │ │ ├── expected.yaml │ │ │ │ │ ├── strategic1.yaml │ │ │ │ │ ├── strategic2.yaml │ │ │ │ │ ├── strategic3.yaml │ │ │ │ │ └── strategic4.yaml │ │ │ │ ├── patchdeletemissing/ │ │ │ │ │ ├── config.yaml │ │ │ │ │ └── strategic1.yaml │ │ │ │ ├── patchlink/ │ │ │ │ │ ├── base.yaml │ │ │ │ │ ├── expected.yaml │ │ │ │ │ └── patch.yaml │ │ │ │ └── strategic.yaml │ │ │ ├── container/ │ │ │ │ ├── container.go │ │ │ │ └── container_test.go │ │ │ ├── contract.go │ │ │ ├── contract_test.go │ │ │ ├── encoder/ │ │ │ │ ├── documentation.go │ │ │ │ ├── encoder.go │ │ │ │ ├── encoder_test.go │ │ │ │ ├── markdown.go │ │ │ │ ├── markdown.tmpl │ │ │ │ └── options.go │ │ │ ├── generate/ │ │ │ │ ├── controlplane.go │ │ │ │ ├── example_test.go │ │ │ │ ├── generate.go │ │ │ │ ├── generate_test.go │ │ │ │ ├── init.go │ │ │ │ ├── network.go │ │ │ │ ├── options.go │ │ │ │ ├── registry.go │ │ │ │ ├── secrets/ │ │ │ │ │ ├── bundle.go │ │ │ │ │ ├── ca.go │ │ │ │ │ ├── clock.go │ │ │ │ │ ├── generate_test.go │ │ │ │ │ ├── marshal_test.go │ │ │ │ │ ├── secrets.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── invalid-secrets.yaml │ │ │ │ │ │ └── secrets.yaml │ │ │ │ │ ├── utils.go │ │ │ │ │ └── validate_test.go │ │ │ │ ├── stdpatches/ │ │ │ │ │ ├── stdpatches.go │ │ │ │ │ └── stdpatches_test.go │ │ │ │ ├── talosconfig.go │ │ │ │ └── worker.go │ │ │ ├── internal/ │ │ │ │ ├── cis/ │ │ │ │ │ ├── cis.go │ │ │ │ │ └── cis_test.go │ │ │ │ ├── documentid/ │ │ │ │ │ └── documentid.go │ │ │ │ └── registry/ │ │ │ │ ├── registry.go │ │ │ │ └── registry_test.go │ │ │ ├── machine/ │ │ │ │ ├── machine.go │ │ │ │ ├── machine_test.go │ │ │ │ └── type_string.go │ │ │ ├── merge/ │ │ │ │ ├── merge.go │ │ │ │ └── merge_test.go │ │ │ ├── provider.go │ │ │ ├── schemas/ │ │ │ │ ├── README.md │ │ │ │ ├── config.schema.json │ │ │ │ └── v1alpha1_config.schema.json │ │ │ ├── types/ │ │ │ │ ├── block/ │ │ │ │ │ ├── block.go │ │ │ │ │ ├── block_doc.go │ │ │ │ │ ├── blockhelpers/ │ │ │ │ │ │ └── blockhelpers.go │ │ │ │ │ ├── byte_size.go │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── encryption.go │ │ │ │ │ ├── existing_volume_config.go │ │ │ │ │ ├── existing_volume_config_test.go │ │ │ │ │ ├── external_volume_config.go │ │ │ │ │ ├── external_volume_config_test.go │ │ │ │ │ ├── percentage_size.go │ │ │ │ │ ├── raw_volume_config.go │ │ │ │ │ ├── raw_volume_config_test.go │ │ │ │ │ ├── size.go │ │ │ │ │ ├── size_test.go │ │ │ │ │ ├── swap_volume_config.go │ │ │ │ │ ├── swap_volume_config_test.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── existingvolumeconfig_selector.yaml │ │ │ │ │ │ ├── externalvolumeconfig_basicvirtiofs.yaml │ │ │ │ │ │ ├── rawvolumeconfig_diskselector.yaml │ │ │ │ │ │ ├── rawvolumeconfig_encrypted.yaml │ │ │ │ │ │ ├── swapvolumeconfig_diskselector.yaml │ │ │ │ │ │ ├── swapvolumeconfig_encrypted.yaml │ │ │ │ │ │ ├── uservolumeconfig_diskselector.yaml │ │ │ │ │ │ ├── uservolumeconfig_encrypted.yaml │ │ │ │ │ │ ├── uservolumeconfig_prjquota.yaml │ │ │ │ │ │ ├── volumeconfig_diskselector.yaml │ │ │ │ │ │ ├── volumeconfig_empty.yaml │ │ │ │ │ │ ├── volumeconfig_maxsize.yaml │ │ │ │ │ │ ├── volumeconfig_negativemaxsize.yaml │ │ │ │ │ │ ├── volumeconfig_state.yaml │ │ │ │ │ │ ├── volumeconfig_tpm_encryption_no_options.yaml │ │ │ │ │ │ ├── volumeconfig_tpm_encryption_with_pcr_settings.yaml │ │ │ │ │ │ ├── volumeconfig_tpm_encryption_with_pcrs_disabled.yaml │ │ │ │ │ │ ├── zswapconfig_full.yaml │ │ │ │ │ │ └── zswapconfig_min.yaml │ │ │ │ │ ├── user_volume_config.go │ │ │ │ │ ├── user_volume_config_test.go │ │ │ │ │ ├── volume_config.go │ │ │ │ │ ├── volume_config_test.go │ │ │ │ │ ├── zswap_config.go │ │ │ │ │ └── zswap_config_test.go │ │ │ │ ├── cri/ │ │ │ │ │ ├── cri.go │ │ │ │ │ ├── cri_doc.go │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── registry_auth.go │ │ │ │ │ ├── registry_auth_test.go │ │ │ │ │ ├── registry_mirror.go │ │ │ │ │ ├── registry_mirror_test.go │ │ │ │ │ ├── registry_tls.go │ │ │ │ │ ├── registry_tls_test.go │ │ │ │ │ └── testdata/ │ │ │ │ │ ├── registryauthconfig.yaml │ │ │ │ │ ├── registrymirrorconfig.yaml │ │ │ │ │ └── registrytlsconfig.yaml │ │ │ │ ├── hardware/ │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── hardware.go │ │ │ │ │ ├── hardware_doc.go │ │ │ │ │ ├── pci_driver_rebind_config.go │ │ │ │ │ ├── pci_driver_rebind_config_test.go │ │ │ │ │ └── testdata/ │ │ │ │ │ └── pcidriverrebindconfig.yaml │ │ │ │ ├── meta/ │ │ │ │ │ ├── certificate_key.go │ │ │ │ │ ├── meta.go │ │ │ │ │ └── url.go │ │ │ │ ├── network/ │ │ │ │ │ ├── blackhole_route.go │ │ │ │ │ ├── blackhole_route_test.go │ │ │ │ │ ├── bond.go │ │ │ │ │ ├── bond_test.go │ │ │ │ │ ├── bridge.go │ │ │ │ │ ├── bridge_test.go │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── default_action_config.go │ │ │ │ │ ├── default_action_config_test.go │ │ │ │ │ ├── dhcp4.go │ │ │ │ │ ├── dhcp4_test.go │ │ │ │ │ ├── dhcp6.go │ │ │ │ │ ├── dhcp6_test.go │ │ │ │ │ ├── dummy.go │ │ │ │ │ ├── dummy_test.go │ │ │ │ │ ├── ethernet.go │ │ │ │ │ ├── ethernet_test.go │ │ │ │ │ ├── hcloud_vip.go │ │ │ │ │ ├── hcloud_vip_test.go │ │ │ │ │ ├── hostname.go │ │ │ │ │ ├── hostname_test.go │ │ │ │ │ ├── kubespan.go │ │ │ │ │ ├── kubespan_endpoints.go │ │ │ │ │ ├── kubespan_endpoints_test.go │ │ │ │ │ ├── kubespan_test.go │ │ │ │ │ ├── layer2_vip.go │ │ │ │ │ ├── layer2_vip_test.go │ │ │ │ │ ├── link.go │ │ │ │ │ ├── link_alias.go │ │ │ │ │ ├── link_alias_test.go │ │ │ │ │ ├── link_test.go │ │ │ │ │ ├── network.go │ │ │ │ │ ├── network_doc.go │ │ │ │ │ ├── port_range.go │ │ │ │ │ ├── port_range_test.go │ │ │ │ │ ├── resolver.go │ │ │ │ │ ├── resolver_test.go │ │ │ │ │ ├── routing_rule.go │ │ │ │ │ ├── routing_rule_test.go │ │ │ │ │ ├── rule_config.go │ │ │ │ │ ├── rule_config_test.go │ │ │ │ │ ├── static_host.go │ │ │ │ │ ├── static_host_test.go │ │ │ │ │ ├── tcp_probe.go │ │ │ │ │ ├── tcp_probe_test.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── blackholerouteconfig.yaml │ │ │ │ │ │ ├── bondconfig.yaml │ │ │ │ │ │ ├── bridgeconfig.yaml │ │ │ │ │ │ ├── defaultactionconfig.yaml │ │ │ │ │ │ ├── dhcpv4config.yaml │ │ │ │ │ │ ├── dhcpv6config.yaml │ │ │ │ │ │ ├── dummylinkconfig.yaml │ │ │ │ │ │ ├── ethernetconfig.yaml │ │ │ │ │ │ ├── hcloudvipconfig.yaml │ │ │ │ │ │ ├── hostnameconfig.yaml │ │ │ │ │ │ ├── kubespanconfig.yaml │ │ │ │ │ │ ├── kubespanendpointsconfig.yaml │ │ │ │ │ │ ├── layer2vipconfig.yaml │ │ │ │ │ │ ├── linkaliasconfig.yaml │ │ │ │ │ │ ├── linkconfig.yaml │ │ │ │ │ │ ├── resolverconfig.yaml │ │ │ │ │ │ ├── routingruleconfig.yaml │ │ │ │ │ │ ├── ruleconfig.yaml │ │ │ │ │ │ ├── statichostconfig.yaml │ │ │ │ │ │ ├── tcpprobeconfig.yaml │ │ │ │ │ │ ├── timesyncconfig.yaml │ │ │ │ │ │ ├── vlanconfig.yaml │ │ │ │ │ │ ├── vrfconfig.yaml │ │ │ │ │ │ ├── wireguardconfig.yaml │ │ │ │ │ │ └── wireguardconfig_redacted.yaml │ │ │ │ │ ├── time_sync.go │ │ │ │ │ ├── time_sync_test.go │ │ │ │ │ ├── vlan.go │ │ │ │ │ ├── vlan_test.go │ │ │ │ │ ├── vrf.go │ │ │ │ │ ├── vrf_test.go │ │ │ │ │ ├── wireguard.go │ │ │ │ │ ├── wireguard_test.go │ │ │ │ │ └── yaml.go │ │ │ │ ├── runtime/ │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── environment.go │ │ │ │ │ ├── environment_test.go │ │ │ │ │ ├── event_sink.go │ │ │ │ │ ├── event_sink_test.go │ │ │ │ │ ├── extensions/ │ │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ │ ├── extensions.go │ │ │ │ │ │ ├── extensions_doc.go │ │ │ │ │ │ ├── service_config.go │ │ │ │ │ │ ├── service_config_test.go │ │ │ │ │ │ └── testdata/ │ │ │ │ │ │ └── extension_service_config.yaml │ │ │ │ │ ├── kmsg_log.go │ │ │ │ │ ├── kmsg_log_test.go │ │ │ │ │ ├── oom.go │ │ │ │ │ ├── oom_test.go │ │ │ │ │ ├── runtime.go │ │ │ │ │ ├── runtime_doc.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── environment.yaml │ │ │ │ │ │ ├── eventsink.yaml │ │ │ │ │ │ ├── kmsglog.yaml │ │ │ │ │ │ ├── oom.yaml │ │ │ │ │ │ └── watchdogtimer.yaml │ │ │ │ │ ├── watchdog_timer.go │ │ │ │ │ └── watchdog_timer_test.go │ │ │ │ ├── security/ │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── image_verification.go │ │ │ │ │ ├── image_verification_test.go │ │ │ │ │ ├── security.go │ │ │ │ │ ├── security_doc.go │ │ │ │ │ ├── testdata/ │ │ │ │ │ │ ├── imageverificationconfig.yaml │ │ │ │ │ │ └── trustedrootsconfig.yaml │ │ │ │ │ ├── trusted_roots.go │ │ │ │ │ └── trusted_roots_test.go │ │ │ │ ├── siderolink/ │ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ │ ├── siderolink.go │ │ │ │ │ ├── siderolink_doc.go │ │ │ │ │ ├── siderolink_test.go │ │ │ │ │ └── testdata/ │ │ │ │ │ └── document.yaml │ │ │ │ ├── types.go │ │ │ │ └── v1alpha1/ │ │ │ │ ├── doc.go │ │ │ │ ├── testdata/ │ │ │ │ │ ├── stability/ │ │ │ │ │ │ ├── patch.yaml │ │ │ │ │ │ ├── secrets.yaml │ │ │ │ │ │ ├── v1.10/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.11/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.12/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.13/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.3/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.4/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.5/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.6/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.7/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ ├── v1.8/ │ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ │ └── v1.9/ │ │ │ │ │ │ ├── base-controlplane.yaml │ │ │ │ │ │ ├── base-worker.yaml │ │ │ │ │ │ ├── overrides-controlplane.yaml │ │ │ │ │ │ └── overrides-worker.yaml │ │ │ │ │ └── strategic/ │ │ │ │ │ ├── 001/ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ ├── left.yaml │ │ │ │ │ │ └── right.yaml │ │ │ │ │ ├── 002/ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ ├── left.yaml │ │ │ │ │ │ └── right.yaml │ │ │ │ │ ├── 003/ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ ├── left.yaml │ │ │ │ │ │ └── right.yaml │ │ │ │ │ ├── 004/ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ ├── left.yaml │ │ │ │ │ │ └── right.yaml │ │ │ │ │ ├── 005/ │ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ │ ├── left.yaml │ │ │ │ │ │ └── right.yaml │ │ │ │ │ └── 006/ │ │ │ │ │ ├── expected.yaml │ │ │ │ │ ├── left.yaml │ │ │ │ │ └── right.yaml │ │ │ │ ├── v1alpha1_admissionplugin.go │ │ │ │ ├── v1alpha1_apiserverconfig.go │ │ │ │ ├── v1alpha1_authorizaationconfigauthorizer.go │ │ │ │ ├── v1alpha1_clusterconfig.go │ │ │ │ ├── v1alpha1_cniconfig.go │ │ │ │ ├── v1alpha1_controllermanagerconfig.go │ │ │ │ ├── v1alpha1_discoveryconfig.go │ │ │ │ ├── v1alpha1_etcdconfig.go │ │ │ │ ├── v1alpha1_examples.go │ │ │ │ ├── v1alpha1_externalcloudproviderconfig.go │ │ │ │ ├── v1alpha1_features.go │ │ │ │ ├── v1alpha1_inlinemanifest.go │ │ │ │ ├── v1alpha1_kernel.go │ │ │ │ ├── v1alpha1_kubernetestalosapiaccess.go │ │ │ │ ├── v1alpha1_logging.go │ │ │ │ ├── v1alpha1_marshal.go │ │ │ │ ├── v1alpha1_marshal_test.go │ │ │ │ ├── v1alpha1_network_bridge.go │ │ │ │ ├── v1alpha1_network_bridge_test.go │ │ │ │ ├── v1alpha1_network_options.go │ │ │ │ ├── v1alpha1_provider.go │ │ │ │ ├── v1alpha1_provider_test.go │ │ │ │ ├── v1alpha1_proxyconfig.go │ │ │ │ ├── v1alpha1_redact_test.go │ │ │ │ ├── v1alpha1_resourcesconfig.go │ │ │ │ ├── v1alpha1_schedulerconfig.go │ │ │ │ ├── v1alpha1_stability_test.go │ │ │ │ ├── v1alpha1_strategic_merge_test.go │ │ │ │ ├── v1alpha1_types.go │ │ │ │ ├── v1alpha1_types_doc.go │ │ │ │ ├── v1alpha1_unstructured.go │ │ │ │ ├── v1alpha1_unstructured_test.go │ │ │ │ ├── v1alpha1_validation.go │ │ │ │ ├── v1alpha1_validation_test.go │ │ │ │ └── zz_generated.deepcopy.go │ │ │ └── validation/ │ │ │ ├── mode.go │ │ │ └── validation.go │ │ ├── constants/ │ │ │ ├── amd64.go │ │ │ ├── amd64_v1.go │ │ │ ├── constants.go │ │ │ └── environment.go │ │ ├── extensions/ │ │ │ ├── config.go │ │ │ ├── deep_copy.generated.go │ │ │ ├── extensions.go │ │ │ ├── extensions_test.go │ │ │ ├── load.go │ │ │ ├── metadata.go │ │ │ ├── services/ │ │ │ │ ├── restart_kind.go │ │ │ │ ├── restartkind_enumer.go │ │ │ │ ├── services.go │ │ │ │ ├── services_test.go │ │ │ │ └── testdata/ │ │ │ │ └── hello.yaml │ │ │ ├── testdata/ │ │ │ │ ├── bad/ │ │ │ │ │ ├── badpaths/ │ │ │ │ │ │ ├── manifest.yaml │ │ │ │ │ │ └── rootfs/ │ │ │ │ │ │ └── boot/ │ │ │ │ │ │ └── vmlinuz │ │ │ │ │ ├── emptymanifest/ │ │ │ │ │ │ └── manifest.yaml │ │ │ │ │ ├── norootfs/ │ │ │ │ │ │ └── manifest.yaml │ │ │ │ │ ├── usrmerge/ │ │ │ │ │ │ └── manifest.yaml │ │ │ │ │ └── wrongfiles/ │ │ │ │ │ └── a │ │ │ │ └── good/ │ │ │ │ └── extension1/ │ │ │ │ ├── manifest.yaml │ │ │ │ └── rootfs/ │ │ │ │ └── usr/ │ │ │ │ └── lib/ │ │ │ │ ├── firmware/ │ │ │ │ │ └── amd/ │ │ │ │ │ └── cpu │ │ │ │ └── ld-linux-x86-64.so.2 │ │ │ └── validate.go │ │ ├── fipsmode/ │ │ │ ├── fipsmode.go │ │ │ └── fipsmode_test.go │ │ ├── formatters/ │ │ │ └── formatters.go │ │ ├── gendata/ │ │ │ ├── data/ │ │ │ │ ├── artifacts │ │ │ │ ├── name │ │ │ │ ├── pkgs │ │ │ │ ├── registry │ │ │ │ ├── sha │ │ │ │ ├── tag │ │ │ │ ├── tools │ │ │ │ └── username │ │ │ └── gendata.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── imager/ │ │ │ ├── imageropts/ │ │ │ │ ├── bootloaderkind_enumer.go │ │ │ │ └── imageropts.go │ │ │ └── quirks/ │ │ │ ├── partitions.go │ │ │ ├── quirks.go │ │ │ └── quirks_test.go │ │ ├── kernel/ │ │ │ ├── kernel.go │ │ │ └── kernel_test.go │ │ ├── kubelet/ │ │ │ └── kubelet.go │ │ ├── labels/ │ │ │ ├── validate.go │ │ │ └── validate_test.go │ │ ├── meta/ │ │ │ ├── constants.go │ │ │ ├── meta.go │ │ │ └── meta_test.go │ │ ├── nethelpers/ │ │ │ ├── address_flags.go │ │ │ ├── addresssortalgorithm.go │ │ │ ├── adlacpactive.go │ │ │ ├── adselect.go │ │ │ ├── arpalltargets.go │ │ │ ├── arpalltargets_enumer.go │ │ │ ├── arpvalidate.go │ │ │ ├── autohostnamekind.go │ │ │ ├── bondmode.go │ │ │ ├── bondxmithashpolicy.go │ │ │ ├── client_identifier.go │ │ │ ├── conntrack_state.go │ │ │ ├── default_action.go │ │ │ ├── device.go │ │ │ ├── duplex.go │ │ │ ├── failovermac.go │ │ │ ├── failovermac_enumer.go │ │ │ ├── failovermac_test.go │ │ │ ├── family.go │ │ │ ├── host_port.go │ │ │ ├── hwaddr.go │ │ │ ├── icmp_type.go │ │ │ ├── lacprate.go │ │ │ ├── linkflag.go │ │ │ ├── linktype.go │ │ │ ├── match_operator.go │ │ │ ├── nethelpers.go │ │ │ ├── nftables_chain_hook.go │ │ │ ├── nftables_chain_priority.go │ │ │ ├── nftables_chain_type.go │ │ │ ├── nftables_verdict.go │ │ │ ├── operstate.go │ │ │ ├── port.go │ │ │ ├── primaryreselect.go │ │ │ ├── protocol.go │ │ │ ├── routeflags.go │ │ │ ├── routeflags_test.go │ │ │ ├── routeprotocol.go │ │ │ ├── routetype.go │ │ │ ├── routingruleaction.go │ │ │ ├── routingtable.go │ │ │ ├── scope.go │ │ │ ├── status.go │ │ │ ├── vlan.go │ │ │ ├── vlan_test.go │ │ │ ├── vlanprotocol.go │ │ │ └── wol.go │ │ ├── overlay/ │ │ │ ├── adapter/ │ │ │ │ └── adapter.go │ │ │ └── overlay.go │ │ ├── platforms/ │ │ │ ├── platforms.go │ │ │ ├── platforms_test.go │ │ │ ├── sbcs.go │ │ │ └── sbcs_test.go │ │ ├── proto/ │ │ │ ├── proto.go │ │ │ ├── proto_test.go │ │ │ └── resource.go │ │ ├── resources/ │ │ │ ├── block/ │ │ │ │ ├── block.go │ │ │ │ ├── block_test.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── device.go │ │ │ │ ├── discovered_volume.go │ │ │ │ ├── discovery_refresh_request.go │ │ │ │ ├── discovery_refresh_status.go │ │ │ │ ├── disk.go │ │ │ │ ├── encryptionkeytype.go │ │ │ │ ├── encryptionprovidertype.go │ │ │ │ ├── filesystemtype.go │ │ │ │ ├── fsparametertype.go │ │ │ │ ├── mount_request.go │ │ │ │ ├── mount_status.go │ │ │ │ ├── swap_status.go │ │ │ │ ├── symlink.go │ │ │ │ ├── system_disk.go │ │ │ │ ├── user_disk_config_status.go │ │ │ │ ├── volume_config.go │ │ │ │ ├── volume_config_test.go │ │ │ │ ├── volume_lifecycle.go │ │ │ │ ├── volume_mount_request.go │ │ │ │ ├── volume_mount_status.go │ │ │ │ ├── volume_status.go │ │ │ │ ├── volumephase.go │ │ │ │ ├── volumetype.go │ │ │ │ ├── volumetype_enumer.go │ │ │ │ └── zswap_status.go │ │ │ ├── cluster/ │ │ │ │ ├── affiliate.go │ │ │ │ ├── affiliate_test.go │ │ │ │ ├── cluster.go │ │ │ │ ├── cluster_test.go │ │ │ │ ├── config.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── identity.go │ │ │ │ ├── info.go │ │ │ │ └── member.go │ │ │ ├── config/ │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── machine_config.go │ │ │ │ ├── machine_config_test.go │ │ │ │ ├── machine_type.go │ │ │ │ └── machine_type_test.go │ │ │ ├── cri/ │ │ │ │ ├── cri.go │ │ │ │ ├── cri_test.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── image_cache_config.go │ │ │ │ ├── imagecachecopystatus_enumer.go │ │ │ │ ├── imagecachestatus.go │ │ │ │ ├── imagecachestatus_enumer.go │ │ │ │ ├── registries_config.go │ │ │ │ └── seccomp_profile.go │ │ │ ├── etcd/ │ │ │ │ ├── condition.go │ │ │ │ ├── config.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── etcd.go │ │ │ │ ├── etcd_test.go │ │ │ │ ├── member.go │ │ │ │ ├── pki_status.go │ │ │ │ └── spec.go │ │ │ ├── files/ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── etcfile_spec.go │ │ │ │ ├── etcfile_status.go │ │ │ │ ├── files.go │ │ │ │ └── files_test.go │ │ │ ├── hardware/ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── hardware.go │ │ │ │ ├── hardware_test.go │ │ │ │ ├── memorymodule.go │ │ │ │ ├── pci_driver_rebind_config.go │ │ │ │ ├── pci_driver_rebind_status.go │ │ │ │ ├── pcidevice.go │ │ │ │ ├── pcr_status.go │ │ │ │ ├── processor.go │ │ │ │ └── system_information.go │ │ │ ├── k8s/ │ │ │ │ ├── admissioncontrol_config.go │ │ │ │ ├── apiserver_config.go │ │ │ │ ├── auditpolicy_config.go │ │ │ │ ├── authorization_config.go │ │ │ │ ├── config_status.go │ │ │ │ ├── controllermanager_config.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── endpoint.go │ │ │ │ ├── endpoint_test.go │ │ │ │ ├── extramanifests_config.go │ │ │ │ ├── k8s.go │ │ │ │ ├── k8s_test.go │ │ │ │ ├── kubelet_config.go │ │ │ │ ├── kubelet_lifecycle.go │ │ │ │ ├── kubelet_spec.go │ │ │ │ ├── kubeprism.go │ │ │ │ ├── kubeprism_config.go │ │ │ │ ├── kubeprism_endpoints.go │ │ │ │ ├── manifest.go │ │ │ │ ├── manifest_status.go │ │ │ │ ├── manifests_config.go │ │ │ │ ├── node_annotation_spec.go │ │ │ │ ├── node_cordoned_spec.go │ │ │ │ ├── node_label_spec.go │ │ │ │ ├── node_status.go │ │ │ │ ├── node_taint_spec.go │ │ │ │ ├── nodeip.go │ │ │ │ ├── nodeip_config.go │ │ │ │ ├── nodename.go │ │ │ │ ├── scheduler_config.go │ │ │ │ ├── secrets_status.go │ │ │ │ ├── static_pod.go │ │ │ │ ├── static_pod_server_status.go │ │ │ │ └── static_pod_status.go │ │ │ ├── kubeaccess/ │ │ │ │ ├── config.go │ │ │ │ ├── kubeaccess.go │ │ │ │ └── kubeaccess_test.go │ │ │ ├── kubespan/ │ │ │ │ ├── config.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── endpoint.go │ │ │ │ ├── identity.go │ │ │ │ ├── kubespan.go │ │ │ │ ├── kubespan_test.go │ │ │ │ ├── peer_spec.go │ │ │ │ ├── peer_state.go │ │ │ │ ├── peer_status.go │ │ │ │ └── peerstate_string.go │ │ │ ├── network/ │ │ │ │ ├── address_spec.go │ │ │ │ ├── address_spec_test.go │ │ │ │ ├── address_status.go │ │ │ │ ├── condition.go │ │ │ │ ├── condition_test.go │ │ │ │ ├── configlayer.go │ │ │ │ ├── configlayer_enumer.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── device_config_spec.go │ │ │ │ ├── device_config_spec_test.go │ │ │ │ ├── dns_resolve_cache.go │ │ │ │ ├── dns_upstream.go │ │ │ │ ├── ethernet_spec.go │ │ │ │ ├── ethernet_status.go │ │ │ │ ├── hardrware_addr.go │ │ │ │ ├── hostdns_config.go │ │ │ │ ├── hostname_spec.go │ │ │ │ ├── hostname_spec_test.go │ │ │ │ ├── hostname_status.go │ │ │ │ ├── link.go │ │ │ │ ├── link_alias_spec.go │ │ │ │ ├── link_name_resolver.go │ │ │ │ ├── link_name_resolver_test.go │ │ │ │ ├── link_refresh.go │ │ │ │ ├── link_spec.go │ │ │ │ ├── link_spec_test.go │ │ │ │ ├── link_status.go │ │ │ │ ├── link_status_test.go │ │ │ │ ├── link_test.go │ │ │ │ ├── network.go │ │ │ │ ├── network_test.go │ │ │ │ ├── nftables_chain.go │ │ │ │ ├── node_address.go │ │ │ │ ├── node_address_filter.go │ │ │ │ ├── node_address_sort_algorithm.go │ │ │ │ ├── operator.go │ │ │ │ ├── operator_spec.go │ │ │ │ ├── operator_spec_test.go │ │ │ │ ├── platform_config.go │ │ │ │ ├── platform_config_test.go │ │ │ │ ├── probe_spec.go │ │ │ │ ├── probe_status.go │ │ │ │ ├── resolver_spec.go │ │ │ │ ├── resolver_spec_test.go │ │ │ │ ├── resolver_status.go │ │ │ │ ├── route_spec.go │ │ │ │ ├── route_spec_test.go │ │ │ │ ├── route_status.go │ │ │ │ ├── routing_rule_spec.go │ │ │ │ ├── routing_rule_status.go │ │ │ │ ├── status.go │ │ │ │ ├── timeserver_spec.go │ │ │ │ ├── timeserver_spec_test.go │ │ │ │ ├── timeserver_status.go │ │ │ │ ├── ula.go │ │ │ │ └── ula_test.go │ │ │ ├── perf/ │ │ │ │ ├── cpu.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── mem.go │ │ │ │ ├── perf.go │ │ │ │ └── perf_test.go │ │ │ ├── resources.go │ │ │ ├── runtime/ │ │ │ │ ├── api_service_config.go │ │ │ │ ├── booted_entry_status.go │ │ │ │ ├── condition.go │ │ │ │ ├── condition_test.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── devices_status.go │ │ │ │ ├── diagnostic.go │ │ │ │ ├── environment.go │ │ │ │ ├── event_sink_config.go │ │ │ │ ├── extension_services_config.go │ │ │ │ ├── extension_services_config_status.go │ │ │ │ ├── extension_status.go │ │ │ │ ├── fipsstate_enumer.go │ │ │ │ ├── kernel_cmdline.go │ │ │ │ ├── kernel_module_spec.go │ │ │ │ ├── kernel_params_spec.go │ │ │ │ ├── kernel_params_status.go │ │ │ │ ├── kmsg_log_config.go │ │ │ │ ├── loaded_kernel_module.go │ │ │ │ ├── machine_reset_signal.go │ │ │ │ ├── machine_status.go │ │ │ │ ├── machinestage.go │ │ │ │ ├── machinestage_enumer.go │ │ │ │ ├── machinestage_test.go │ │ │ │ ├── maintenance_config.go │ │ │ │ ├── maintenance_request.go │ │ │ │ ├── meta_key.go │ │ │ │ ├── meta_loaded.go │ │ │ │ ├── mount_status.go │ │ │ │ ├── oom.go │ │ │ │ ├── oom_action.go │ │ │ │ ├── pcirebind_status.go │ │ │ │ ├── platform_metadata.go │ │ │ │ ├── runtime.go │ │ │ │ ├── runtime_test.go │ │ │ │ ├── sbom_item.go │ │ │ │ ├── security_status.go │ │ │ │ ├── selinuxstate_enumer.go │ │ │ │ ├── unique_machine_token.go │ │ │ │ ├── version.go │ │ │ │ ├── watchdog_timer_config.go │ │ │ │ └── watchdog_timer_status.go │ │ │ ├── secrets/ │ │ │ │ ├── api.go │ │ │ │ ├── api_test.go │ │ │ │ ├── cert_sans.go │ │ │ │ ├── condition.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── encryption_salt.go │ │ │ │ ├── etcd.go │ │ │ │ ├── etcd_root.go │ │ │ │ ├── kubelet.go │ │ │ │ ├── kubernetes.go │ │ │ │ ├── kubernetes_certs.go │ │ │ │ ├── kubernetes_root.go │ │ │ │ ├── maintenance_root.go │ │ │ │ ├── os_root.go │ │ │ │ ├── secrets.go │ │ │ │ ├── secrets_test.go │ │ │ │ ├── trustd.go │ │ │ │ └── trustd_test.go │ │ │ ├── security/ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── image_verification_rule.go │ │ │ │ ├── security.go │ │ │ │ ├── security_test.go │ │ │ │ └── tuf_trusted_root.go │ │ │ ├── siderolink/ │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── siderolink.go │ │ │ │ ├── siderolink_status.go │ │ │ │ ├── siderolink_test.go │ │ │ │ └── siderolink_tunnel.go │ │ │ ├── time/ │ │ │ │ ├── adjtime_status.go │ │ │ │ ├── condition.go │ │ │ │ ├── deep_copy.generated.go │ │ │ │ ├── status.go │ │ │ │ ├── time.go │ │ │ │ └── time_test.go │ │ │ └── v1alpha1/ │ │ │ ├── acquire_config_spec.go │ │ │ ├── acquire_config_status.go │ │ │ ├── deep_copy.generated.go │ │ │ ├── service.go │ │ │ ├── v1alpha1.go │ │ │ └── v1alpha1_test.go │ │ ├── role/ │ │ │ ├── role.go │ │ │ └── role_test.go │ │ ├── textdiff/ │ │ │ ├── testdata/ │ │ │ │ └── fuzz/ │ │ │ │ └── FuzzDiff/ │ │ │ │ ├── 4436c698e19c9ea6 │ │ │ │ ├── 72f7d10cdb1b4c78 │ │ │ │ └── e8e3f58f18e0ab28 │ │ │ ├── textdiff.go │ │ │ └── textdiff_test.go │ │ ├── version/ │ │ │ ├── gen.go │ │ │ ├── os-release │ │ │ ├── osrelease.go │ │ │ ├── osrelease_test.go │ │ │ └── version.go │ │ └── yamlutils/ │ │ ├── yamlutils.go │ │ └── yamlutils_test.go │ ├── makefs/ │ │ ├── ext4.go │ │ ├── ext4_test.go │ │ ├── makefs.go │ │ ├── makefs_test.go │ │ ├── protofile.go │ │ ├── protofile_test.go │ │ ├── testdata/ │ │ │ ├── deeply_nested.proto │ │ │ ├── nested_directories.proto │ │ │ ├── simple_directory.proto │ │ │ └── symlinks.proto │ │ ├── vfat.go │ │ ├── vfat_test.go │ │ ├── xfs.go │ │ └── xfs_test.go │ ├── minimal/ │ │ └── limits.go │ ├── provision/ │ │ ├── access/ │ │ │ ├── access.go │ │ │ ├── access_test.go │ │ │ └── adapter.go │ │ ├── internal/ │ │ │ ├── cniutils/ │ │ │ │ └── cniutils.go │ │ │ └── inmemhttp/ │ │ │ ├── inmemhttp.go │ │ │ └── inmemhttp_test.go │ │ ├── options.go │ │ ├── providers/ │ │ │ ├── docker/ │ │ │ │ ├── create.go │ │ │ │ ├── destroy.go │ │ │ │ ├── docker.go │ │ │ │ ├── docker_test.go │ │ │ │ ├── image.go │ │ │ │ ├── network.go │ │ │ │ ├── node.go │ │ │ │ ├── reflect.go │ │ │ │ └── result.go │ │ │ ├── factory.go │ │ │ ├── qemu/ │ │ │ │ ├── arch.go │ │ │ │ ├── arch_darwin.go │ │ │ │ ├── arch_linux.go │ │ │ │ ├── controller.go │ │ │ │ ├── create.go │ │ │ │ ├── destroy.go │ │ │ │ ├── launch.go │ │ │ │ ├── launch_darwin.go │ │ │ │ ├── launch_linux.go │ │ │ │ ├── node.go │ │ │ │ ├── node_darwin.go │ │ │ │ ├── node_linux.go │ │ │ │ ├── pflash.go │ │ │ │ ├── preflight.go │ │ │ │ ├── preflight_darwin.go │ │ │ │ ├── preflight_linux.go │ │ │ │ ├── qemu.go │ │ │ │ └── tpm2.go │ │ │ ├── qemu.go │ │ │ ├── qemu_other.go │ │ │ └── vm/ │ │ │ ├── controller.go │ │ │ ├── dhcpd.go │ │ │ ├── dhcpd_darwin.go │ │ │ ├── dhcpd_linux.go │ │ │ ├── disk.go │ │ │ ├── dnsd.go │ │ │ ├── dnsd_darwin.go │ │ │ ├── dnsd_linux.go │ │ │ ├── image_cache.go │ │ │ ├── internal/ │ │ │ │ ├── ethtool/ │ │ │ │ │ ├── ethtool_linux.go │ │ │ │ │ └── ethtool_other.go │ │ │ │ └── ipxe/ │ │ │ │ ├── data/ │ │ │ │ │ └── ipxe/ │ │ │ │ │ ├── amd64/ │ │ │ │ │ │ └── snp.efi │ │ │ │ │ └── arm64/ │ │ │ │ │ └── snp.efi │ │ │ │ └── ipxe.go │ │ │ ├── ipam.go │ │ │ ├── json-logs.go │ │ │ ├── kms.go │ │ │ ├── launch.go │ │ │ ├── loadbalancer.go │ │ │ ├── loadbalancer_darwin.go │ │ │ ├── loadbalancer_linux.go │ │ │ ├── network.go │ │ │ ├── network_darwin.go │ │ │ ├── network_linux.go │ │ │ ├── network_test.go │ │ │ ├── node.go │ │ │ ├── process.go │ │ │ ├── reflect.go │ │ │ ├── siderolink-agent.go │ │ │ ├── state.go │ │ │ ├── tftpd.go │ │ │ ├── virtiofsd.go │ │ │ ├── virtiofsd_darwin.go │ │ │ ├── virtiofsd_linux.go │ │ │ └── vm.go │ │ ├── provision.go │ │ ├── provision_test.go │ │ ├── request.go │ │ ├── result.go │ │ ├── state.go │ │ ├── state_linux.go │ │ └── state_other.go │ ├── reporter/ │ │ └── reporter.go │ ├── rotate/ │ │ └── pki/ │ │ ├── internal/ │ │ │ └── helpers/ │ │ │ └── helpers.go │ │ ├── kubernetes/ │ │ │ └── kubernetes.go │ │ └── talos/ │ │ └── talos.go │ ├── safepath/ │ │ ├── safepath.go │ │ └── safepath_test.go │ ├── splash/ │ │ └── splash.go │ ├── startup/ │ │ ├── maxprocs.go │ │ └── startup.go │ └── xfs/ │ ├── fsopen/ │ │ ├── fsopen_linux.go │ │ ├── fsopen_other.go │ │ └── options_linux.go │ ├── fsopen_test.go │ ├── helpers_linux.go │ ├── opentree/ │ │ ├── opentree_linux.go │ │ └── opentree_other.go │ ├── opentree_test.go │ ├── osroot.go │ ├── osroot_test.go │ ├── root.go │ ├── root_linux.go │ └── root_test.go ├── tools/ │ ├── docgen/ │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── main_test.go │ │ └── schema.go │ ├── go.mod │ ├── go.sum │ ├── gotagsrewrite/ │ │ ├── ast.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── main.go │ │ ├── main_test.go │ │ └── testdata/ │ │ ├── a.golden │ │ └── a.orig │ └── structprotogen/ │ ├── ast/ │ │ └── ast.go │ ├── consts/ │ │ └── consts.go │ ├── go.mod │ ├── go.sum │ ├── loader/ │ │ └── loader.go │ ├── main.go │ ├── proto/ │ │ └── proto.go │ ├── sliceutil/ │ │ └── sliceutils.go │ └── types/ │ └── types.go └── website/ ├── content/ │ └── v1.13/ │ ├── reference/ │ │ ├── _index.md │ │ ├── api.md │ │ ├── cli.md │ │ ├── configuration/ │ │ │ ├── _index.md │ │ │ ├── block/ │ │ │ │ ├── _index.md │ │ │ │ ├── existingvolumeconfig.md │ │ │ │ ├── externalvolumeconfig.md │ │ │ │ ├── rawvolumeconfig.md │ │ │ │ ├── swapvolumeconfig.md │ │ │ │ ├── uservolumeconfig.md │ │ │ │ ├── volumeconfig.md │ │ │ │ └── zswapconfig.md │ │ │ ├── cri/ │ │ │ │ ├── _index.md │ │ │ │ ├── registryauthconfig.md │ │ │ │ ├── registrymirrorconfig.md │ │ │ │ └── registrytlsconfig.md │ │ │ ├── extensions/ │ │ │ │ ├── _index.md │ │ │ │ └── extensionserviceconfig.md │ │ │ ├── hardware/ │ │ │ │ ├── _index.md │ │ │ │ └── pcidriverrebindconfig.md │ │ │ ├── network/ │ │ │ │ ├── _index.md │ │ │ │ ├── blackholerouteconfig.md │ │ │ │ ├── bondconfig.md │ │ │ │ ├── bridgeconfig.md │ │ │ │ ├── dhcpv4config.md │ │ │ │ ├── dhcpv6config.md │ │ │ │ ├── dummylinkconfig.md │ │ │ │ ├── ethernetconfig.md │ │ │ │ ├── hcloudvipconfig.md │ │ │ │ ├── hostnameconfig.md │ │ │ │ ├── kubespanconfig.md │ │ │ │ ├── kubespanendpointsconfig.md │ │ │ │ ├── layer2vipconfig.md │ │ │ │ ├── linkaliasconfig.md │ │ │ │ ├── linkconfig.md │ │ │ │ ├── networkdefaultactionconfig.md │ │ │ │ ├── networkruleconfig.md │ │ │ │ ├── resolverconfig.md │ │ │ │ ├── routingruleconfig.md │ │ │ │ ├── statichostconfig.md │ │ │ │ ├── tcpprobeconfig.md │ │ │ │ ├── timesyncconfig.md │ │ │ │ ├── vlanconfig.md │ │ │ │ ├── vrfconfig.md │ │ │ │ └── wireguardconfig.md │ │ │ ├── runtime/ │ │ │ │ ├── _index.md │ │ │ │ ├── environmentconfig.md │ │ │ │ ├── eventsinkconfig.md │ │ │ │ ├── kmsglogconfig.md │ │ │ │ ├── oomconfig.md │ │ │ │ └── watchdogtimerconfig.md │ │ │ ├── security/ │ │ │ │ ├── _index.md │ │ │ │ ├── imageverificationconfig.md │ │ │ │ └── trustedrootsconfig.md │ │ │ ├── siderolink/ │ │ │ │ ├── _index.md │ │ │ │ └── siderolinkconfig.md │ │ │ └── v1alpha1/ │ │ │ ├── _index.md │ │ │ └── config.md │ │ └── kernel.md │ └── schemas/ │ ├── config.schema.json │ └── v1alpha1_config.schema.json └── static/ └── install ================================================ FILE CONTENTS ================================================ ================================================ FILE: .codecov.yml ================================================ codecov: require_ci_to_pass: false coverage: status: project: default: target: 15% threshold: 0.5% base: auto if_ci_failed: success patch: off comment: false ================================================ FILE: .conform.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2025-12-25T11:36:39Z by kres 26be706. policies: - type: commit spec: dco: true gpg: required: true identity: gitHubOrganization: siderolabs spellcheck: locale: US maximumOfOneCommit: false header: length: 89 imperative: true case: lower invalidLastCharacters: . body: required: true conventional: types: - chore - docs - perf - refactor - style - test - release scopes: - apid - machined - networkd - talosctl - trustd - talosctl - kernel - security - ci - ^v1.13 - type: license spec: skipPaths: - .git/ - testdata/ includeSuffixes: - .go excludeSuffixes: - .pb.go - .pb.gw.go - _string.go - _enumer.go - _string_linux.go - zz_generated.deepcopy.go header: | // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. ================================================ FILE: .dockerignore ================================================ ** !api !selinux !cmd !docs !hack !internal !pkg !website !tools !.golangci.yml !.markdownlint.json !.textlintrc.json !go.mod !go.sum !go.work !README.md !CONTRIBUTING.md !_out/uki-certs ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [siderolabs] ================================================ FILE: .github/ISSUE_TEMPLATE/BUG_REPORT.md ================================================ --- name: Bug Report about: Report a bug. title: "" labels: "" assignees: "" --- ## Bug Report ### Description ### Logs ### Environment - Talos version: [`talosctl version --nodes `] - Kubernetes version: [`kubectl version`] - Platform: ================================================ FILE: .github/ISSUE_TEMPLATE/FEATURE_REQUEST.md ================================================ --- name: Feature Requests about: Create a feature request. title: "" labels: "" assignees: "" --- ## Feature Request ### Description ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Question url: https://github.com/siderolabs/talos/discussions about: Ask a question about Talos Linux. - name: Community url: https://taloscommunity.slack.com about: Join the community. (Not an official support channel.) Get your invite by visiting https://inviter.co/sidero-labs-community. - name: Get Enterprise Support url: https://www.siderolabs.com/support/ about: Get commercial support from the Sidero Labs team, with SLAs for every issue. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ # Pull Request ## What? (description) ## Why? (reasoning) ## Acceptance Please use the following checklist: - [ ] you linked an issue (if applicable) - [ ] you included tests (if applicable) - [ ] you ran conformance (`make conformance`) - [ ] you formatted your code (`make fmt`) - [ ] you linted your code (`make lint`) - [ ] you generated documentation (`make docs`) - [ ] you ran unit-tests (`make unit-tests`) > See `make help` for a description of the available targets. ================================================ FILE: .github/renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "description": "THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.", "prHeader": "Update Request | Renovate Bot", "extends": [ ":dependencyDashboard", ":gitSignOff", ":semanticCommitScopeDisabled", "schedule:earlyMondays" ], "customManagers": [ { "customType": "regex", "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}", "managerFilePatterns": [ "/Makefile/" ], "matchStrings": [ "# renovate: datasource=(?.*?)(?:\\s+extractVersion=(?.+?))?\\s+depName=(?.+?)\\s.*_VERSION\\s+\\?=\\s+(?.+)" ] }, { "customType": "regex", "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}", "managerFilePatterns": [ "/pkg/machinery/constants/constants.go/" ], "matchStrings": [ "\\/\\/\\s+renovate: datasource=(?.*?)(?:\\s+extractVersion=(?.+?))?(?:\\s+versioning=(?.+?))?\\s+depName=(?.+?)?\\s.*Version\\s+=\\s+\\\"(?.+?)\\\"" ] }, { "customType": "regex", "versioningTemplate": "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}", "managerFilePatterns": [ "/internal/integration/k8s/constants.go/", "/internal/integration/api/constants.go/" ], "matchStrings": [ "\\/\\/\\s+renovate: datasource=(?.*?)(?:\\s+extractVersion=(?.+?))?(?:\\s+versioning=(?.+?))?\\s+depName=(?.+?)?(?:\\s+registryUrl=(?.+?))?\\s.*Version\\s+=\\s+\\\"(?.+?)\\\"" ] }, { "customType": "regex", "datasourceTemplate": "docker", "depNameTemplate": "docker/dockerfile-upstream", "versioningTemplate": "docker", "managerFilePatterns": [ "/Dockerfile/" ], "matchStrings": [ "# syntax = docker\\/dockerfile-upstream:(?.*)" ] } ], "packageRules": [ { "groupName": "dependencies", "matchUpdateTypes": [ "major", "minor", "patch", "pin", "digest" ] }, { "enabled": false, "matchFileNames": [ "website/**" ] }, { "versioning": "regex:^(?\\d+)\\.(?\\d+)", "matchPackageNames": [ "golang/go" ] } ], "separateMajorMinor": false } ================================================ FILE: .github/workflows/ci.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-16T15:20:50Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": push: branches: - main - release-* tags: - v* pull_request: branches: - main - release-* name: default jobs: base-lint: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: lint run: | make lint base-unit-tests: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: unit-tests run: | make unit-tests - name: unit-tests-fips run: | make unit-tests-fips - name: unit-tests-race run: | make unit-tests-race - name: coverage uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # version: v5.5.2 with: files: _out/coverage.txt token: ${{ secrets.CODECOV_TOKEN }} timeout-minutes: 3 default: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) && github.event_name == 'pull_request' outputs: labels: ${{ steps.retrieve-pr-labels.outputs.result }} steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: external-artifacts run: | make external-artifacts - name: generate run: | make generate docs - name: uki-certs env: PLATFORM: linux/amd64 run: | make uki-certs - name: check-dirty run: | make check-dirty - name: build env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle run: | make talosctl-cni-bundle - name: sbom run: | make sbom - name: iso env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 run: | make iso secureboot-iso - name: images-essential env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: Generate executable list run: | find _out -type f -executable > _out/executable-artifacts - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-artifacts path: | _out retention-days: "5" - name: Retrieve PR labels id: retrieve-pr-labels uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # version: v8.0.0 with: retries: "3" script: | if (context.eventName != "pull_request") { return "[]" } const resp = await github.rest.issues.get({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, }) return resp.data.labels.map(label => label.name) e2e-docker-short: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Download artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: e2e-docker env: IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" run: | make e2e-docker - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-e2e-docker-short path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" e2e-iso: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Download artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: e2e-iso env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make e2e-iso - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-e2e-iso path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" e2e-qemu-short: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Download artifacts uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag run: | make ci-temp-release-tag - name: e2e-qemu env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-short IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-e2e-qemu-short path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" grype-scan: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: login-to-registry uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # version: v4.0.0 with: password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io username: ${{ github.repository_owner }} - name: local-grype-scan-result env: DEST: _out run: | make local-grype-scan-result - name: target-grype-validate run: | make target-grype-validate - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-grype-scan-result path: | _out/grype-scan.log retention-days: "5" integration-airgapped: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/airgapped') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: integration-images-list env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make integration-images-list - name: e2e-airgapped-no-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: no-proxy WITH_CLUSTER_DISCOVERY: "false" run: | sudo -E make e2e-qemu - name: e2e-airgapped-http-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-http-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: http-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: e2e-airgapped-secure-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-secure-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: secure-http-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: e2e-airgapped-reverse-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-reverse-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: https-reverse-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-airgapped path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip /tmp/airgapped*.log retention-days: "5" integration-aws: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/aws') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: e2e-aws-prepare env: E2E_AWS_TARGET: default IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-aws-nvidia-nonfree-lts: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-nonfree-lts') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-nonfree') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-lts nonfree-kmod-nvidia-lts extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-nonfree-lts EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-nonfree-lts env: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-aws-nvidia-nonfree-production: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-nonfree-production') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-nonfree') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-production nonfree-kmod-nvidia-production extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-nonfree-production EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-nonfree-production env: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-aws-nvidia-oss-lts: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-oss-lts') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-oss') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-lts nvidia-open-gpu-kernel-modules-lts extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-oss-lts EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-oss-lts env: EXTRA_TEST_ARGS: -talos.extensions.nvidia -talos.verifyukibooted=false INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-aws-nvidia-oss-production: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-oss-production') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia-oss') || contains(fromJSON(needs.default.outputs.labels), 'integration/aws-nvidia') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-production nvidia-open-gpu-kernel-modules-production extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-oss-production EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-oss-production env: EXTRA_TEST_ARGS: -talos.extensions.nvidia -talos.verifyukibooted=false INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-cilium: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/cilium') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-cilium env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-no-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept run: | sudo -E make e2e-qemu - name: e2e-cilium-strict env: CILIUM_INSTALL_TYPE: strict GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept run: | sudo -E make e2e-qemu - name: e2e-cilium-strict-kubespan env: CILIUM_INSTALL_TYPE: strict GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-cilium path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-cloud-images: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/cloud-images') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: images env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images - name: cloud-images run: | make cloud-images integration-conformance: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/conformance') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: conformance-qemu env: GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_MEMORY_WORKERS: "4096" TEST_MODE: fast-conformance WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-conformance path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-conformance-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/conformance-enforcing') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: conformance-qemu env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_MEMORY_WORKERS: "4096" TAG_SUFFIX_IN: -enforcing TEST_MODE: fast-conformance WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-conformance-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-embedded: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/embedded') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-embedded env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make e2e-embedded - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-embedded path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-extensions: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/extensions') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: unshallow-extensions run: | git -C _out/extensions fetch --prune --unshallow - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make all extensions-metadata -C _out/extensions - name: installer extensions env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make installer-with-extensions - name: e2e-extensions env: EXTRA_TEST_ARGS: -talos.extensions.qemu GITHUB_STEP_NAME: ${{ github.job}}-e2e-extensions IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "3" QEMU_MEMORY_WORKERS: "4096" QEMU_WORKERS: "1" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_WORKER: '@_out/installer-extensions-patch.yaml:@hack/test/patches/extensions.yaml:@hack/test/patches/dm-raid-module.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-extensions path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-gcp: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/gcp') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: image-gcp env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-gcp - name: e2e-gcp-prepare run: | make e2e-gcp-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: gcp TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-gcp run: | make e2e-gcp - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: gcp TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf integration-image-cache: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/image-cache') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-cache env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make cache-create - name: e2e-image-cache env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: "no" SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: "true" run: | sudo -E make e2e-qemu - name: e2e-image-cache-encrypted env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache-encrypted IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: "no" SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-cache-encrypted.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-image-cache path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-image-factory: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/image-factory') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: factory-1.11-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-iso KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-image env: FACTORY_BOOT_METHOD: disk-image FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-image KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-pxe env: FACTORY_BOOT_METHOD: ipxe FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_VERSION: v1.11.6 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-pxe KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-secureboot env: FACTORY_BOOT_METHOD: secureboot-iso FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-secureboot KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.10-secureboot env: FACTORY_BOOT_METHOD: secureboot-iso FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.10.9 FACTORY_VERSION: v1.10.8 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-secureboot KUBERNETES_VERSION: 1.33.7 run: | sudo -E make e2e-image-factory - name: factory-1.10-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.10.9 FACTORY_VERSION: v1.10.8 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-iso KUBERNETES_VERSION: 1.33.7 run: | sudo -E make e2e-image-factory - name: factory-1.9-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.9.6 FACTORY_VERSION: v1.9.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.9-iso KUBERNETES_VERSION: 1.32.11 run: | sudo -E make e2e-image-factory - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-image-factory path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-images: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: contains(fromJSON(needs.default.outputs.labels), 'integration/images') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images integration-misc-0: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-0') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-metal-uki if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-metal-uki - name: e2e-firewall env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-firewall IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_FIREWALL: block run: | sudo -E make e2e-qemu - name: e2e-canal-reset env: CUSTOM_CNI_URL: https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/canal.yaml GITHUB_STEP_NAME: ${{ github.job}}-e2e-canal-reset IMAGE_REGISTRY: registry.dev.siderolabs.io INTEGRATION_TEST_RUN: TestIntegration/api.ResetSuite/TestResetWithSpec run: | sudo -E make e2e-qemu - name: e2e-controlplane-port env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-controlplane-port IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/ephemeral-min-max.yaml' WITH_CONTROL_PLANE_PORT: "443" run: | sudo -E make e2e-qemu - name: e2e-uki-4k env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-uki-4k IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_4K_DISK: "true" WITH_UKI_BOOT: "true" run: | sudo -E make e2e-qemu - name: e2e-flannel-netpol env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-flannel-netpol IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TEST_MODE: network-policy WITH_CONFIG_PATCH: '@hack/test/patches/flannel-netpol.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-0 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-1: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-1') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-no-cluster-discovery env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CLUSTER_DISCOVERY: "false" run: | sudo -E make e2e-qemu - name: e2e-kubespan env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CLUSTER_DISCOVERY: "true" WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: e2e-default-hostname env: DISABLE_DHCP_HOSTNAME: "true" GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" run: | sudo -E make e2e-qemu - name: e2e-min-requirements env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_MEMORY_CONTROLPLANES: "2048" QEMU_MEMORY_WORKERS: "1024" QEMU_SYSTEM_DISK_SIZE: "10240" SHORT_INTEGRATION_TEST: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-1 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-1-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-1-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-no-cluster-discovery env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CLUSTER_DISCOVERY: "false" WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-kubespan env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CLUSTER_DISCOVERY: "true" WITH_ENFORCING: "true" WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: e2e-default-hostname env: DISABLE_DHCP_HOSTNAME: "true" EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-min-requirements env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_MEMORY_CONTROLPLANES: "2048" QEMU_MEMORY_WORKERS: "1024" QEMU_SYSTEM_DISK_SIZE: "10240" SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-1-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-2: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-2') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 run: | make iso - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: e2e-bios env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-bios-iso env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_ISO: "true" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-disk-image env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" USE_DISK_IMAGE: "true" VIA_MAINTENANCE_MODE: "true" WITH_DISK_ENCRYPTION: "true" WITH_JSON_LOGS: "false" run: | sudo -E make e2e-qemu - name: e2e-disk-image-bios env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image-bios IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" USE_DISK_IMAGE: "true" VIA_MAINTENANCE_MODE: "true" WITH_DISK_ENCRYPTION: "true" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-node-address-v2 env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/node-address-v2.yaml' run: | sudo -E make e2e-qemu - name: e2e-tpm1_2 env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-tpm1_2 IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_TPM1_2: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-2 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-3: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-3') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-network-chaos env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_NETWORK_CHAOS: "yes" run: | sudo -E make e2e-qemu - name: e2e-metal-iso env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_INJECTION_METHOD: metal-iso run: | sudo -E make e2e-qemu - name: e2e-iommu-pcidriverrebind env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_IOMMU: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-3 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-3-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-3-enforcing') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-network-chaos env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" WITH_NETWORK_CHAOS: "yes" run: | sudo -E make e2e-qemu - name: e2e-metal-iso env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CONFIG_INJECTION_METHOD: metal-iso WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-iommu-pcidriverrebind env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" WITH_IOMMU: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-3-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-4: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-4') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-siderolink env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: "true" run: | sudo -E make e2e-qemu - name: e2e-siderolink-tunnel env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: tunnel run: | sudo -E make e2e-qemu - name: e2e-siderolink-tls env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: wireguard+tls run: | sudo -E make e2e-qemu - name: e2e-apparmor env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_APPARMOR_LSM_ENABLED: "yes" run: | sudo -E make e2e-qemu - name: e2e-k8s-user-namespace env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/usernamespace.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-4 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-misc-4-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/misc-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/misc-4-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-siderolink env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: "true" run: | sudo -E make e2e-qemu - name: e2e-siderolink-tunnel env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: tunnel run: | sudo -E make e2e-qemu - name: e2e-siderolink-tls env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: wireguard+tls run: | sudo -E make e2e-qemu - name: e2e-apparmor env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_APPARMOR_LSM_ENABLED: "yes" run: | sudo -E make e2e-qemu - name: e2e-k8s-user-namespace env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CONFIG_PATCH: '@hack/test/patches/usernamespace.yaml' WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-4-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-provision-0: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/provision') || contains(fromJSON(needs.default.outputs.labels), 'integration/provision-0') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-0 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-0 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-0 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-provision-1: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/provision') || contains(fromJSON(needs.default.outputs.labels), 'integration/provision-1') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-1 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-1 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-1 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-provision-2: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/provision') || contains(fromJSON(needs.default.outputs.labels), 'integration/provision-2') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: installer env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=talos.extra_cmdline=extra-super-cmdline PLATFORM: linux/amd64,linux/arm64 TAG_SUFFIX_OUT: -extra-cmdline run: | make installer - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-2 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-2 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-2 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-provision-3: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/provision') || contains(fromJSON(needs.default.outputs.labels), 'integration/provision-3') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-3 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-3 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-3 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu env: IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 USER_DISKS_MOUNTS: /var/mnt/extra,/var/mnt/p1,/var/mnt/p2 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_USER_DISK: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-csi-longhorn: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi') || contains(fromJSON(needs.default.outputs.labels), 'integration/extensions') || contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi-longhorn') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make iscsi-tools util-linux-tools extensions-metadata -C _out/extensions - name: installer extensions env: EXTENSIONS_FILTER_COMMAND: grep -E '/iscsi-tools|util-linux-tools' IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make installer-with-extensions - name: kubelet-fat-patch run: | make kubelet-fat-patch - name: e2e-qemu-csi-longhorn env: EXTRA_TEST_ARGS: -talos.csi=longhorn GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-longhorn IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_DRIVERS: nvme QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_SYSTEM_DISK_SIZE: "20480" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/longhorn-cp.yaml' WITH_CONFIG_PATCH_WORKER: '@_out/installer-extensions-patch.yaml:@_out/kubelet-fat-patch.yaml:@hack/test/patches/longhorn.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-longhorn path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-longhorn path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-csi-openebs: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi') || contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi-openebs') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu-csi-openebs env: EXTRA_TEST_ARGS: -talos.csi=openebs GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-openebs IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "4" QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/openebs-cp.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/openebs.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-openebs path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-openebs path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-csi-rook-ceph: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi') || contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-csi-rook-ceph') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu-csi-rook-ceph env: EXTRA_TEST_ARGS: -talos.csi=rook-ceph GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-rook-ceph IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/rook-ceph.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-rook-ceph path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-rook-ceph path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-encrypted-vip: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-encrypted-vip') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu env: IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_DISK_ENCRYPTION: "true" WITH_KUBESPAN: "true" WITH_VIRTUAL_IP: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-encrypted-vip path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-enforcing') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-qemu env: EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "3" QEMU_EXTRA_DISKS_DRIVERS: ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" TAG_SUFFIX_IN: -enforcing USER_DISKS_MOUNTS: /var/mnt/extra,/var/mnt/p1,/var/mnt/p2 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_ENFORCING: "true" WITH_USER_DISK: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-qemu-race: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/qemu-race') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: build-race env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64 PUSH: "true" TAG_SUFFIX: -race WITH_RACE: "1" run: | make initramfs installer-base imager installer - name: e2e-qemu-race env: EXTRA_TEST_ARGS: -talos.race GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-race IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 QEMU_MEMORY_CONTROLPLANES: "4096" QEMU_MEMORY_WORKERS: "4096" TAG_SUFFIX: -race WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-race path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-reproducibility-test: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/reproducibility-test') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: reproducibility-test env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make reproducibility-test integration-trusted-boot: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/trusted-boot') || contains(fromJSON(needs.default.outputs.labels), 'integration/release-gate') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: secureboot-iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make secureboot-iso - name: integration-trusted-boot env: EXTRA_TEST_ARGS: -talos.trustedboot GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot IMAGE_REGISTRY: registry.dev.siderolabs.io VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_TRUSTED_BOOT_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-trusted-boot path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" integration-trusted-boot-enforcing: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: contains(fromJSON(needs.default.outputs.labels), 'integration/trusted-boot-enforcing') needs: - default steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: secureboot-iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 run: | make secureboot-iso - name: integration-trusted-boot-enforcing env: EXTRA_TEST_ARGS: -talos.trustedboot -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot-enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_TRUSTED_BOOT_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-trusted-boot-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" push: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) && github.event_name != 'pull_request' && !startsWith(github.ref, 'refs/tags/') steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: build env: PLATFORM: linux/amd64,linux/arm64 run: | make talosctl-all kernel sd-boot sd-stub initramfs installer-base imager talos - name: release-notes run: | make release-notes - name: login-to-registry uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # version: v4.0.0 with: password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io username: ${{ github.repository_owner }} - name: push env: PLATFORM: linux/amd64,linux/arm64 run: | make push - name: push-latest if: github.ref == 'refs/heads/main' env: PLATFORM: linux/amd64,linux/arm64 run: | make push-latest tag: permissions: actions: read contents: write id-token: write issues: read packages: write pull-requests: read runs-on: group: large if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) && startsWith(github.ref, 'refs/tags/') steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: build env: PLATFORM: linux/amd64,linux/arm64 run: | make talosctl-all kernel sd-boot sd-stub initramfs installer-base imager talos talosctl-cni-bundle - name: release-notes run: | make release-notes - name: sbom run: | make sbom - name: login-to-registry uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # version: v4.0.0 with: password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io username: ${{ github.repository_owner }} - name: push env: PLATFORM: linux/amd64,linux/arm64 run: | make push - name: images env: PLATFORM: linux/amd64,linux/arm64 run: | make images - name: Install Cosign uses: sigstore/cosign-installer@ba7bc0a3fef59531c69a25acd34668d6d3fe6f22 # version: v4.1.0 - name: Sign artifacts run: | cosign sign-blob --bundle _out/initramfs-amd64.xz.bundle --yes _out/initramfs-amd64.xz cosign sign-blob --bundle _out/initramfs-arm64.xz.bundle --yes _out/initramfs-arm64.xz cosign sign-blob --bundle _out/metal-amd64.iso.bundle --yes _out/metal-amd64.iso cosign sign-blob --bundle _out/metal-arm64.iso.bundle --yes _out/metal-arm64.iso cosign sign-blob --bundle _out/metal-amd64-uki.efi.bundle --yes _out/metal-amd64-uki.efi cosign sign-blob --bundle _out/metal-arm64-uki.efi.bundle --yes _out/metal-arm64-uki.efi cosign sign-blob --bundle _out/metal-amd64.raw.zst.bundle --yes _out/metal-amd64.raw.zst cosign sign-blob --bundle _out/metal-arm64.raw.zst.bundle --yes _out/metal-arm64.raw.zst cosign sign-blob --bundle _out/talos-arm64.spdx.json.bundle --yes _out/talos-arm64.spdx.json cosign sign-blob --bundle _out/talos-amd64.spdx.json.bundle --yes _out/talos-amd64.spdx.json cosign sign-blob --bundle _out/talos-container-arm64.spdx.json.bundle --yes _out/talos-container-arm64.spdx.json cosign sign-blob --bundle _out/talos-container-amd64.spdx.json.bundle --yes _out/talos-container-amd64.spdx.json cosign sign-blob --bundle _out/talosctl-cni-bundle-amd64.tar.gz.bundle --yes _out/talosctl-cni-bundle-amd64.tar.gz cosign sign-blob --bundle _out/talosctl-cni-bundle-arm64.tar.gz.bundle --yes _out/talosctl-cni-bundle-arm64.tar.gz cosign sign-blob --bundle _out/talosctl-darwin-amd64.bundle --yes _out/talosctl-darwin-amd64 cosign sign-blob --bundle _out/talosctl-darwin-arm64.bundle --yes _out/talosctl-darwin-arm64 cosign sign-blob --bundle _out/talosctl-freebsd-amd64.bundle --yes _out/talosctl-freebsd-amd64 cosign sign-blob --bundle _out/talosctl-freebsd-arm64.bundle --yes _out/talosctl-freebsd-arm64 cosign sign-blob --bundle _out/talosctl-linux-amd64.bundle --yes _out/talosctl-linux-amd64 cosign sign-blob --bundle _out/talosctl-linux-arm64.bundle --yes _out/talosctl-linux-arm64 cosign sign-blob --bundle _out/talosctl-linux-armv7.bundle --yes _out/talosctl-linux-armv7 cosign sign-blob --bundle _out/talosctl-linux-riscv64.bundle --yes _out/talosctl-linux-riscv64 cosign sign-blob --bundle _out/talosctl-windows-amd64.exe.bundle --yes _out/talosctl-windows-amd64.exe cosign sign-blob --bundle _out/talosctl-windows-arm64.exe.bundle --yes _out/talosctl-windows-arm64.exe cosign sign-blob --bundle _out/vmlinuz-amd64.bundle --yes _out/vmlinuz-amd64 cosign sign-blob --bundle _out/vmlinuz-arm64.bundle --yes _out/vmlinuz-arm64 - name: Generate Checksums run: | cd _out sha256sum initramfs-amd64.xz initramfs-arm64.xz metal-amd64.iso metal-arm64.iso metal-amd64-uki.efi metal-arm64-uki.efi metal-amd64.raw.zst metal-arm64.raw.zst talos-arm64.spdx.json talos-amd64.spdx.json talos-container-arm64.spdx.json talos-container-amd64.spdx.json talosctl-cni-bundle-amd64.tar.gz talosctl-cni-bundle-arm64.tar.gz talosctl-darwin-amd64 talosctl-darwin-arm64 talosctl-freebsd-amd64 talosctl-freebsd-arm64 talosctl-linux-amd64 talosctl-linux-arm64 talosctl-linux-armv7 talosctl-linux-riscv64 talosctl-windows-amd64.exe talosctl-windows-arm64.exe vmlinuz-amd64 vmlinuz-arm64 > sha256sum.txt sha512sum initramfs-amd64.xz initramfs-arm64.xz metal-amd64.iso metal-arm64.iso metal-amd64-uki.efi metal-arm64-uki.efi metal-amd64.raw.zst metal-arm64.raw.zst talos-arm64.spdx.json talos-amd64.spdx.json talos-container-arm64.spdx.json talos-container-amd64.spdx.json talosctl-cni-bundle-amd64.tar.gz talosctl-cni-bundle-arm64.tar.gz talosctl-darwin-amd64 talosctl-darwin-arm64 talosctl-freebsd-amd64 talosctl-freebsd-arm64 talosctl-linux-amd64 talosctl-linux-arm64 talosctl-linux-armv7 talosctl-linux-riscv64 talosctl-windows-amd64.exe talosctl-windows-arm64.exe vmlinuz-amd64 vmlinuz-arm64 > sha512sum.txt - name: Sign checksums run: | cd _out cosign sign-blob --bundle sha256sum.txt.bundle --yes sha256sum.txt cosign sign-blob --bundle sha512sum.txt.bundle --yes sha512sum.txt - name: release uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # version: v2.5.0 with: body_path: _out/RELEASE_NOTES.md draft: "true" files: |- _out/initramfs-amd64.xz _out/initramfs-arm64.xz _out/metal-amd64.iso _out/metal-arm64.iso _out/metal-amd64-uki.efi _out/metal-arm64-uki.efi _out/metal-amd64.raw.zst _out/metal-arm64.raw.zst _out/talos-arm64.spdx.json _out/talos-amd64.spdx.json _out/talos-container-arm64.spdx.json _out/talos-container-amd64.spdx.json _out/talosctl-cni-bundle-amd64.tar.gz _out/talosctl-cni-bundle-arm64.tar.gz _out/talosctl-darwin-amd64 _out/talosctl-darwin-arm64 _out/talosctl-freebsd-amd64 _out/talosctl-freebsd-arm64 _out/talosctl-linux-amd64 _out/talosctl-linux-arm64 _out/talosctl-linux-armv7 _out/talosctl-linux-riscv64 _out/talosctl-windows-amd64.exe _out/talosctl-windows-arm64.exe _out/vmlinuz-amd64 _out/vmlinuz-arm64 _out/sha*.txt _out/*.bundle ================================================ FILE: .github/workflows/dispatch.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-02-17T20:54:45Z by kres 6458cfd. "on": workflow_dispatch: inputs: release: description: "" type: string required: false tag: description: "" type: string required: false name: dispatch jobs: cloud-images: permissions: actions: read contents: write issues: read packages: write pull-requests: read runs-on: group: generic if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: cloud-images env: CLOUD_IMAGES_EXTRA_ARGS: --use-factory PLATFORM: linux/amd64,linux/arm64 TAG: ${{ github.event.inputs.tag }} run: | make cloud-images - name: attach-images env: GH_TOKEN: ${{ github.token }} RELEASE: ${{ github.event.inputs.release }} run: | gh release upload "${RELEASE}" --clobber \ _out/cloud-images.json ================================================ FILE: .github/workflows/grype-scan-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: grype-scan-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: login-to-registry uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # version: v4.0.0 with: password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io username: ${{ github.repository_owner }} - name: local-grype-scan-result env: DEST: _out run: | make local-grype-scan-result - name: target-grype-validate run: | make target-grype-validate - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-grype-scan-result path: | _out/grype-scan.log retention-days: "5" ================================================ FILE: .github/workflows/integration-airgapped-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-airgapped-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: integration-images-list env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make integration-images-list - name: e2e-airgapped-no-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: no-proxy WITH_CLUSTER_DISCOVERY: "false" run: | sudo -E make e2e-qemu - name: e2e-airgapped-http-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-http-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: http-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: e2e-airgapped-secure-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-secure-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: secure-http-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: e2e-airgapped-reverse-proxy env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-reverse-proxy IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_AIRGAPPED: https-reverse-proxy WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-airgapped path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip /tmp/airgapped*.log retention-days: "5" ================================================ FILE: .github/workflows/integration-aws-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-aws-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: e2e-aws-prepare env: E2E_AWS_TARGET: default IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-aws-nvidia-nonfree-lts-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-aws-nvidia-nonfree-lts-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-lts nonfree-kmod-nvidia-lts extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-nonfree-lts EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-nonfree-lts env: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-aws-nvidia-nonfree-production-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-aws-nvidia-nonfree-production-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-production nonfree-kmod-nvidia-production extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-nonfree-production EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-nonfree-production env: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-aws-nvidia-oss-lts-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-aws-nvidia-oss-lts-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-lts nvidia-open-gpu-kernel-modules-lts extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-oss-lts EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-oss-lts env: EXTRA_TEST_ARGS: -talos.extensions.nvidia -talos.verifyukibooted=false INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-aws-nvidia-oss-production-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-aws-nvidia-oss-production-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-aws env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-aws - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make nvidia-container-toolkit-production nvidia-open-gpu-kernel-modules-production extensions-metadata -C _out/extensions - name: e2e-aws-prepare env: E2E_AWS_TARGET: nvidia-oss-production EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make e2e-aws-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-aws-nvidia-oss-production env: EXTRA_TEST_ARGS: -talos.extensions.nvidia -talos.verifyukibooted=false INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA run: | make e2e-aws - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: aws TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-cilium-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-cilium-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-cilium env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-no-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept run: | sudo -E make e2e-qemu - name: e2e-cilium-strict env: CILIUM_INSTALL_TYPE: strict GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept run: | sudo -E make e2e-qemu - name: e2e-cilium-strict-kubespan env: CILIUM_INSTALL_TYPE: strict GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/cilium-kubeproxy.yaml' WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-cilium path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-conformance-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-conformance-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: conformance-qemu env: GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_MEMORY_WORKERS: "4096" TEST_MODE: fast-conformance WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-conformance path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-conformance-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-conformance-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: conformance-qemu env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_MEMORY_WORKERS: "4096" TAG_SUFFIX_IN: -enforcing TEST_MODE: fast-conformance WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-conformance-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-embedded-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-embedded-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-embedded env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make e2e-embedded - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-embedded path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-extensions-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 6 * * * name: integration-extensions-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: unshallow-extensions run: | git -C _out/extensions fetch --prune --unshallow - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make all extensions-metadata -C _out/extensions - name: installer extensions env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make installer-with-extensions - name: e2e-extensions env: EXTRA_TEST_ARGS: -talos.extensions.qemu GITHUB_STEP_NAME: ${{ github.job}}-e2e-extensions IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "3" QEMU_MEMORY_WORKERS: "4096" QEMU_WORKERS: "1" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_WORKER: '@_out/installer-extensions-patch.yaml:@hack/test/patches/extensions.yaml:@hack/test/patches/dm-raid-module.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-extensions path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-gcp-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 7 * * * name: integration-gcp-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Mask secrets run: | echo "$(sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | "::add-mask::" + .value')" - name: Set secrets for job run: | sops -d .secrets.yaml | yq -e '.secrets | to_entries[] | .key + "=" + .value' >> "$GITHUB_ENV" - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: image-gcp env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-gcp - name: e2e-gcp-prepare run: | make e2e-gcp-prepare - name: checkout contrib uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/contrib ref: main repository: siderolabs/contrib - name: setup tf uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # version: v4.0.0 with: terraform_wrapper: "false" - name: tf apply env: TF_E2E_ACTION: apply TF_E2E_TEST_TYPE: gcp TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf - name: e2e-gcp run: | make e2e-gcp - name: tf destroy if: always() env: TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: "false" TF_E2E_TEST_TYPE: gcp TF_SCRIPT_DIR: _out/contrib run: | make e2e-cloud-tf ================================================ FILE: .github/workflows/integration-image-cache-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-16T10:48:51Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 2 * * * name: integration-image-cache-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-cache env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make cache-create - name: e2e-image-cache env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: "no" SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: "true" run: | sudo -E make e2e-qemu - name: e2e-image-cache-encrypted env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache-encrypted IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: "no" SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-cache-encrypted.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-image-cache path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-image-factory-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 6 * * * name: integration-image-factory-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: factory-1.11-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-iso KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-image env: FACTORY_BOOT_METHOD: disk-image FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-image KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-pxe env: FACTORY_BOOT_METHOD: ipxe FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_VERSION: v1.11.6 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-pxe KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.11-secureboot env: FACTORY_BOOT_METHOD: secureboot-iso FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.11.6 FACTORY_VERSION: v1.11.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-secureboot KUBERNETES_VERSION: 1.34.3 run: | sudo -E make e2e-image-factory - name: factory-1.10-secureboot env: FACTORY_BOOT_METHOD: secureboot-iso FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.10.9 FACTORY_VERSION: v1.10.8 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-secureboot KUBERNETES_VERSION: 1.33.7 run: | sudo -E make e2e-image-factory - name: factory-1.10-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.10.9 FACTORY_VERSION: v1.10.8 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-iso KUBERNETES_VERSION: 1.33.7 run: | sudo -E make e2e-image-factory - name: factory-1.9-iso env: FACTORY_BOOT_METHOD: iso FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE: "true" FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.9.6 FACTORY_VERSION: v1.9.5 GITHUB_STEP_NAME: ${{ github.job}}-factory-1.9-iso KUBERNETES_VERSION: 1.32.11 run: | sudo -E make e2e-image-factory - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-image-factory path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-images-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 2 * * * name: integration-images-cron jobs: default: runs-on: group: generic steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images ================================================ FILE: .github/workflows/integration-misc-0-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-0-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: image-metal-uki if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make image-metal-uki - name: e2e-firewall env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-firewall IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_FIREWALL: block run: | sudo -E make e2e-qemu - name: e2e-canal-reset env: CUSTOM_CNI_URL: https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/canal.yaml GITHUB_STEP_NAME: ${{ github.job}}-e2e-canal-reset IMAGE_REGISTRY: registry.dev.siderolabs.io INTEGRATION_TEST_RUN: TestIntegration/api.ResetSuite/TestResetWithSpec run: | sudo -E make e2e-qemu - name: e2e-controlplane-port env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-controlplane-port IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/ephemeral-min-max.yaml' WITH_CONTROL_PLANE_PORT: "443" run: | sudo -E make e2e-qemu - name: e2e-uki-4k env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-uki-4k IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_4K_DISK: "true" WITH_UKI_BOOT: "true" run: | sudo -E make e2e-qemu - name: e2e-flannel-netpol env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-flannel-netpol IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TEST_MODE: network-policy WITH_CONFIG_PATCH: '@hack/test/patches/flannel-netpol.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-0 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-1-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-1-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-no-cluster-discovery env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CLUSTER_DISCOVERY: "false" run: | sudo -E make e2e-qemu - name: e2e-kubespan env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CLUSTER_DISCOVERY: "true" WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: e2e-default-hostname env: DISABLE_DHCP_HOSTNAME: "true" GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" run: | sudo -E make e2e-qemu - name: e2e-min-requirements env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_MEMORY_CONTROLPLANES: "2048" QEMU_MEMORY_WORKERS: "1024" QEMU_SYSTEM_DISK_SIZE: "10240" SHORT_INTEGRATION_TEST: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-1 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-1-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-1-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-no-cluster-discovery env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CLUSTER_DISCOVERY: "false" WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-kubespan env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CLUSTER_DISCOVERY: "true" WITH_ENFORCING: "true" WITH_KUBESPAN: "true" run: | sudo -E make e2e-qemu - name: e2e-default-hostname env: DISABLE_DHCP_HOSTNAME: "true" EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-min-requirements env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_MEMORY_CONTROLPLANES: "2048" QEMU_MEMORY_WORKERS: "1024" QEMU_SYSTEM_DISK_SIZE: "10240" SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-1-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-2-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-2-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 run: | make iso - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: e2e-bios env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-bios-iso env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_ISO: "true" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-disk-image env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" USE_DISK_IMAGE: "true" VIA_MAINTENANCE_MODE: "true" WITH_DISK_ENCRYPTION: "true" WITH_JSON_LOGS: "false" run: | sudo -E make e2e-qemu - name: e2e-disk-image-bios env: EXTRA_TEST_ARGS: -talos.verifyukibooted=false GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image-bios IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" USE_DISK_IMAGE: "true" VIA_MAINTENANCE_MODE: "true" WITH_DISK_ENCRYPTION: "true" WITH_UEFI: "false" run: | sudo -E make e2e-qemu - name: e2e-node-address-v2 env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/node-address-v2.yaml' run: | sudo -E make e2e-qemu - name: e2e-tpm1_2 env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-tpm1_2 IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_TPM1_2: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-2 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-3-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-3-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-network-chaos env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_NETWORK_CHAOS: "yes" run: | sudo -E make e2e-qemu - name: e2e-metal-iso env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_INJECTION_METHOD: metal-iso run: | sudo -E make e2e-qemu - name: e2e-iommu-pcidriverrebind env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_IOMMU: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-3 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-3-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-3-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-network-chaos env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" WITH_NETWORK_CHAOS: "yes" run: | sudo -E make e2e-qemu - name: e2e-metal-iso env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CONFIG_INJECTION_METHOD: metal-iso WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: e2e-iommu-pcidriverrebind env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: "true" WITH_IOMMU: "yes" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-3-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-4-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-4-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-siderolink env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: "true" run: | sudo -E make e2e-qemu - name: e2e-siderolink-tunnel env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: tunnel run: | sudo -E make e2e-qemu - name: e2e-siderolink-tls env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" VIA_MAINTENANCE_MODE: "true" WITH_SIDEROLINK_AGENT: wireguard+tls run: | sudo -E make e2e-qemu - name: e2e-apparmor env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_APPARMOR_LSM_ENABLED: "yes" run: | sudo -E make e2e-qemu - name: e2e-k8s-user-namespace env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH: '@hack/test/patches/usernamespace.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-4 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-misc-4-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 5 * * * name: integration-misc-4-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-siderolink env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: "true" run: | sudo -E make e2e-qemu - name: e2e-siderolink-tunnel env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: tunnel run: | sudo -E make e2e-qemu - name: e2e-siderolink-tls env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_SIDEROLINK_AGENT: wireguard+tls run: | sudo -E make e2e-qemu - name: e2e-apparmor env: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" WITH_APPARMOR_LSM_ENABLED: "yes" run: | sudo -E make e2e-qemu - name: e2e-k8s-user-namespace env: EXTRA_TEST_ARGS: -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: "yes" TAG_SUFFIX_IN: -enforcing WITH_CONFIG_PATCH: '@hack/test/patches/usernamespace.yaml' WITH_ENFORCING: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-misc-4-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-provision-0-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-provision-0-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-0 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-0 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-0 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-provision-1-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-provision-1-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-1 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-1 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-1 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-provision-2-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-provision-2-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: installer env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=talos.extra_cmdline=extra-super-cmdline PLATFORM: linux/amd64,linux/arm64 TAG_SUFFIX_OUT: -extra-cmdline run: | make installer - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-2 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-2 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-2 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-provision-3-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-16T15:20:50Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 4 * * * name: integration-provision-3-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: provision-tests-prepare run: | make provision-tests-prepare - name: provision-tests-track-3 env: GRPC_ENFORCE_ALPN_ENABLED: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io run: | sudo -E make provision-tests-track-3 - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-provision-3 path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu env: IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 USER_DISKS_MOUNTS: /var/mnt/extra,/var/mnt/p1,/var/mnt/p2 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_USER_DISK: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-csi-longhorn-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-csi-longhorn-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: generate if: github.event_name == 'schedule' run: | make generate - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: checkout extensions uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 with: path: _out/extensions ref: main repository: siderolabs/extensions - name: set variables run: | cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions env: PLATFORM: linux/amd64 PUSH: "true" REGISTRY: registry.dev.siderolabs.io run: | make iscsi-tools util-linux-tools extensions-metadata -C _out/extensions - name: installer extensions env: EXTENSIONS_FILTER_COMMAND: grep -E '/iscsi-tools|util-linux-tools' IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make installer-with-extensions - name: kubelet-fat-patch run: | make kubelet-fat-patch - name: e2e-qemu-csi-longhorn env: EXTRA_TEST_ARGS: -talos.csi=longhorn GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-longhorn IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_DRIVERS: nvme QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_SYSTEM_DISK_SIZE: "20480" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/longhorn-cp.yaml' WITH_CONFIG_PATCH_WORKER: '@_out/installer-extensions-patch.yaml:@_out/kubelet-fat-patch.yaml:@hack/test/patches/longhorn.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-longhorn path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-longhorn path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-csi-openebs-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-csi-openebs-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu-csi-openebs env: EXTRA_TEST_ARGS: -talos.csi=openebs GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-openebs IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "4" QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/openebs-cp.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/openebs.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-openebs path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-openebs path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-csi-rook-ceph-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-csi-rook-ceph-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu-csi-rook-ceph env: EXTRA_TEST_ARGS: -talos.csi=rook-ceph GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-rook-ceph IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_CPUS_WORKERS: "6" QEMU_EXTRA_DISKS: "1" QEMU_EXTRA_DISKS_SIZE: "12288" QEMU_MEMORY_WORKERS: "8192" QEMU_WORKERS: "3" SHORT_INTEGRATION_TEST: "yes" WITH_CONFIG_PATCH_CONTROLPLANE: '@hack/test/patches/rook-ceph.yaml' run: | sudo -E make e2e-qemu - name: save artifacts uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: fio-integration-qemu-csi-rook-ceph path: | /tmp/fio-*.json retention-days: "180" - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-csi-rook-ceph path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-encrypted-vip-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-encrypted-vip-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: e2e-qemu env: IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_DISK_ENCRYPTION: "true" WITH_KUBESPAN: "true" WITH_VIRTUAL_IP: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-encrypted-vip path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: e2e-qemu env: EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "3" QEMU_EXTRA_DISKS_DRIVERS: ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" TAG_SUFFIX_IN: -enforcing USER_DISKS_MOUNTS: /var/mnt/extra,/var/mnt/p1,/var/mnt/p2 WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' WITH_ENFORCING: "true" WITH_USER_DISK: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-qemu-race-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-qemu-race-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: build-race env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64 PUSH: "true" TAG_SUFFIX: -race WITH_RACE: "1" run: | make initramfs installer-base imager installer - name: e2e-qemu-race env: EXTRA_TEST_ARGS: -talos.race GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-race IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_DRIVERS: virtiofs,ide,nvme QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_TAGS: disk0 QEMU_MEMORY_CONTROLPLANES: "4096" QEMU_MEMORY_WORKERS: "4096" TAG_SUFFIX: -race WITH_CONFIG_PATCH_WORKER: '@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml' run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-qemu-race path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-reproducibility-test-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 2 * * * name: integration-reproducibility-test-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: reproducibility-test env: IMAGE_REGISTRY: registry.dev.siderolabs.io run: | make reproducibility-test ================================================ FILE: .github/workflows/integration-trusted-boot-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-trusted-boot-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make images-essential - name: secureboot-iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 PLATFORM: linux/amd64,linux/arm64 run: | make secureboot-iso - name: integration-trusted-boot env: EXTRA_TEST_ARGS: -talos.trustedboot GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot IMAGE_REGISTRY: registry.dev.siderolabs.io VIA_MAINTENANCE_MODE: "true" WITH_CONFIG_PATCH: '@hack/test/patches/image-verification.yaml' WITH_TRUSTED_BOOT_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-trusted-boot path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/integration-trusted-boot-enforcing-cron.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-15T13:42:44Z by kres e68c408. concurrency: group: ${{ github.head_ref || github.run_id }} cancel-in-progress: true "on": schedule: - cron: 30 3 * * * name: integration-trusted-boot-enforcing-cron jobs: default: runs-on: group: large steps: - name: gather-system-info id: system-info uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0 continue-on-error: true - name: print-system-info run: | MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) OUTPUTS=( "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" "Hostname: ${{ steps.system-info.outputs.hostname }}" "NodeName: ${NODE_NAME}" "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" "Name: ${{ steps.system-info.outputs.name }}" "Platform: ${{ steps.system-info.outputs.platform }}" "Release: ${{ steps.system-info.outputs.release }}" "Total memory: ${MEMORY_GB} GB" ) for OUTPUT in "${OUTPUTS[@]}";do echo "${OUTPUT}" done continue-on-error: true - name: checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2 - name: Unshallow run: | git fetch --prune --unshallow - name: Set up Docker Buildx id: setup-buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0 with: driver: remote endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 timeout-minutes: 10 - name: Download artifacts if: github.event_name != 'schedule' uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # version: v8.0.0 with: name: talos-artifacts path: _out - name: Fix artifact permissions if: github.event_name != 'schedule' run: | xargs -a _out/executable-artifacts -I {} chmod +x {} - name: ci-temp-release-tag if: github.event_name != 'schedule' run: | make ci-temp-release-tag - name: uki-certs if: github.event_name == 'schedule' env: PLATFORM: linux/amd64 run: | make uki-certs - name: build if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io PLATFORM: linux/amd64,linux/arm64 PUSH: "true" run: | make talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 - name: talosctl-cni-bundle if: github.event_name == 'schedule' run: | make talosctl-cni-bundle - name: images-essential-enforcing env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 PUSH: "true" TAG_SUFFIX_OUT: -enforcing run: | make images-essential - name: secureboot-iso if: github.event_name == 'schedule' env: IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: --extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1 PLATFORM: linux/amd64,linux/arm64 run: | make secureboot-iso - name: integration-trusted-boot-enforcing env: EXTRA_TEST_ARGS: -talos.trustedboot -talos.enforcing GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot-enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io TAG_SUFFIX_IN: -enforcing VIA_MAINTENANCE_MODE: "true" WITH_ENFORCING: "true" WITH_TRUSTED_BOOT_ISO: "true" run: | sudo -E make e2e-qemu - name: save artifacts if: always() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # version: v7.0.0 with: name: talos-logs-integration-trusted-boot-enforcing path: |- /tmp/logs-*.tar.gz /tmp/support-*.zip retention-days: "5" ================================================ FILE: .github/workflows/lock.yaml ================================================ name: 'Lock old issues' on: schedule: - cron: '0 2 * * *' workflow_dispatch: permissions: issues: write jobs: action: runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v5 with: issue-inactive-days: '60' process-only: 'issues' log-output: true ================================================ FILE: .github/workflows/lock.yml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2025-12-18T09:21:29Z by kres 26be706. "on": schedule: - cron: 0 2 * * * name: Lock old issues permissions: issues: write jobs: action: runs-on: - ubuntu-latest steps: - name: Lock old issues uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # version: v6.0.0 with: issue-inactive-days: "60" log-output: "true" process-only: issues ================================================ FILE: .github/workflows/slack-notify-ci-failure.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-16T15:20:50Z by kres e68c408. "on": workflow_run: workflows: - default - grype-scan-cron - integration-qemu-cron - integration-qemu-enforcing-cron - integration-embedded-cron - integration-conformance-cron - integration-conformance-enforcing-cron - integration-trusted-boot-cron - integration-trusted-boot-enforcing-cron - integration-provision-0-cron - integration-provision-1-cron - integration-provision-2-cron - integration-provision-3-cron - integration-airgapped-cron - integration-misc-0-cron - integration-misc-1-cron - integration-misc-1-enforcing-cron - integration-misc-2-cron - integration-misc-3-cron - integration-misc-3-enforcing-cron - integration-misc-4-cron - integration-misc-4-enforcing-cron - integration-extensions-cron - integration-cilium-cron - integration-qemu-encrypted-vip-cron - integration-qemu-race-cron - integration-qemu-csi-rook-ceph-cron - integration-qemu-csi-longhorn-cron - integration-qemu-csi-openebs-cron - integration-images-cron - integration-reproducibility-test-cron - integration-image-cache-cron - integration-image-factory-cron - integration-aws-cron - integration-aws-nvidia-oss-lts-cron - integration-aws-nvidia-oss-production-cron - integration-aws-nvidia-nonfree-lts-cron - integration-aws-nvidia-nonfree-production-cron - integration-gcp-cron types: - completed branches: - main name: slack-notify-failure jobs: slack-notify: runs-on: group: generic if: github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event != 'pull_request' steps: - name: Slack Notify uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # version: v2.1.1 with: method: chat.postMessage payload: | { "channel": "ci-failure", "text": "${{ github.event.workflow_run.conclusion }} - ${{ github.repository }}", "icon_emoji": "${{ github.event.workflow_run.conclusion == 'success' && ':white_check_mark:' || github.event.workflow_run.conclusion == 'failure' && ':x:' || ':warning:' }}", "username": "GitHub Actions", "attachments": [ { "blocks": [ { "fields": [ { "text": "${{ github.event.workflow_run.event == 'pull_request' && format('*Pull Request:* {0} (`{1}`)\n<{2}/pull/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, steps.get-pr-number.outputs.pull_request_number, github.event.workflow_run.display_title) || format('*Build:* {0} (`{1}`)\n<{2}/commit/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, github.sha, github.event.workflow_run.display_title) }}", "type": "mrkdwn" }, { "text": "*Status:*\n`${{ github.event.workflow_run.conclusion }}`", "type": "mrkdwn" } ], "type": "section" }, { "fields": [ { "text": "*Author:*\n`${{ github.actor }}`", "type": "mrkdwn" }, { "text": "*Event:*\n`${{ github.event.workflow_run.event }}`", "type": "mrkdwn" } ], "type": "section" }, { "type": "divider" }, { "elements": [ { "text": { "text": "Logs", "type": "plain_text" }, "type": "button", "url": "${{ github.event.workflow_run.html_url }}" }, { "text": { "text": "Commit", "type": "plain_text" }, "type": "button", "url": "${{ github.event.repository.html_url }}/commit/${{ github.sha }}" } ], "type": "actions" } ], "color": "${{ github.event.workflow_run.conclusion == 'success' && '#2EB886' || github.event.workflow_run.conclusion == 'failure' && '#A30002' || '#FFCC00' }}" } ] } token: ${{ secrets.SLACK_BOT_TOKEN_V2 }} ================================================ FILE: .github/workflows/slack-notify.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-16T15:20:50Z by kres e68c408. "on": workflow_run: workflows: - default - grype-scan-cron - integration-qemu-cron - integration-qemu-enforcing-cron - integration-embedded-cron - integration-conformance-cron - integration-conformance-enforcing-cron - integration-trusted-boot-cron - integration-trusted-boot-enforcing-cron - integration-provision-0-cron - integration-provision-1-cron - integration-provision-2-cron - integration-provision-3-cron - integration-airgapped-cron - integration-misc-0-cron - integration-misc-1-cron - integration-misc-1-enforcing-cron - integration-misc-2-cron - integration-misc-3-cron - integration-misc-3-enforcing-cron - integration-misc-4-cron - integration-misc-4-enforcing-cron - integration-extensions-cron - integration-cilium-cron - integration-qemu-encrypted-vip-cron - integration-qemu-race-cron - integration-qemu-csi-rook-ceph-cron - integration-qemu-csi-longhorn-cron - integration-qemu-csi-openebs-cron - integration-images-cron - integration-reproducibility-test-cron - integration-image-cache-cron - integration-image-factory-cron - integration-aws-cron - integration-aws-nvidia-oss-lts-cron - integration-aws-nvidia-oss-production-cron - integration-aws-nvidia-nonfree-lts-cron - integration-aws-nvidia-nonfree-production-cron - integration-gcp-cron types: - completed name: slack-notify jobs: slack-notify: runs-on: group: generic if: github.event.workflow_run.conclusion != 'skipped' steps: - name: Get PR number id: get-pr-number if: github.event.workflow_run.event == 'pull_request' env: GH_TOKEN: ${{ github.token }} run: | echo pull_request_number=$(gh pr view -R ${{ github.repository }} ${{ github.event.workflow_run.head_repository.owner.login }}:${{ github.event.workflow_run.head_branch }} --json number --jq .number) >> $GITHUB_OUTPUT - name: Slack Notify uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # version: v2.1.1 with: method: chat.postMessage payload: | { "channel": "ci-all", "text": "${{ github.event.workflow_run.conclusion }} - ${{ github.repository }}", "icon_emoji": "${{ github.event.workflow_run.conclusion == 'success' && ':white_check_mark:' || github.event.workflow_run.conclusion == 'failure' && ':x:' || ':warning:' }}", "username": "GitHub Actions", "attachments": [ { "blocks": [ { "fields": [ { "text": "${{ github.event.workflow_run.event == 'pull_request' && format('*Pull Request:* {0} (`{1}`)\n<{2}/pull/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, steps.get-pr-number.outputs.pull_request_number, github.event.workflow_run.display_title) || format('*Build:* {0} (`{1}`)\n<{2}/commit/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, github.sha, github.event.workflow_run.display_title) }}", "type": "mrkdwn" }, { "text": "*Status:*\n`${{ github.event.workflow_run.conclusion }}`", "type": "mrkdwn" } ], "type": "section" }, { "fields": [ { "text": "*Author:*\n`${{ github.actor }}`", "type": "mrkdwn" }, { "text": "*Event:*\n`${{ github.event.workflow_run.event }}`", "type": "mrkdwn" } ], "type": "section" }, { "type": "divider" }, { "elements": [ { "text": { "text": "Logs", "type": "plain_text" }, "type": "button", "url": "${{ github.event.workflow_run.html_url }}" }, { "text": { "text": "Commit", "type": "plain_text" }, "type": "button", "url": "${{ github.event.repository.html_url }}/commit/${{ github.sha }}" } ], "type": "actions" } ], "color": "${{ github.event.workflow_run.conclusion == 'success' && '#2EB886' || github.event.workflow_run.conclusion == 'failure' && '#A30002' || '#FFCC00' }}" } ] } token: ${{ secrets.SLACK_BOT_TOKEN_V2 }} ================================================ FILE: .github/workflows/stale.yaml ================================================ name: 'Close stale issues and PRs' on: schedule: - cron: '30 1 * * *' permissions: issues: write pull-requests: write jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v9 with: stale-issue-message: 'This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.' stale-pr-message: 'This PR is stale because it has been open 45 days with no activity.' close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' days-before-issue-stale: 180 days-before-pr-stale: 45 days-before-issue-close: 5 days-before-pr-close: -1 # never close PRs operations-per-run: 2000 # the maximum number of operations to perform per run ================================================ FILE: .github/workflows/stale.yml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2026-03-05T15:46:00Z by kres 1dd7316. "on": schedule: - cron: 30 1 * * * name: Close stale issues and PRs permissions: issues: write pull-requests: write jobs: stale: runs-on: - ubuntu-latest steps: - name: Close stale issues and PRs uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # version: v10.2.0 with: close-issue-message: This issue was closed because it has been stalled for 7 days with no activity. days-before-issue-close: "5" days-before-issue-stale: "180" days-before-pr-close: "-1" days-before-pr-stale: "45" operations-per-run: "2000" stale-issue-message: This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days. stale-pr-message: This PR is stale because it has been open 45 days with no activity. ================================================ FILE: .github/workflows/update-homebrew.yaml ================================================ name: update-homebrew on: release: types: [published] jobs: update-formula: runs-on: ubuntu-latest steps: - name: Extract version from tag id: version run: | VERSION="${GITHUB_REF#refs/tags/v}" echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "Version: ${VERSION}" - name: Check if release is latest id: check-latest env: GH_TOKEN: ${{ github.token }} run: | LATEST_TAG=$(gh release view --repo ${{ github.repository }} --json tagName -q '.tagName') CURRENT_TAG="${GITHUB_REF#refs/tags/}" echo "Latest release tag: ${LATEST_TAG}" echo "Current release tag: ${CURRENT_TAG}" if [ "${CURRENT_TAG}" != "${LATEST_TAG}" ]; then echo "This release (${CURRENT_TAG}) is not the latest (${LATEST_TAG}). Skipping update." echo "is_latest=false" >> $GITHUB_OUTPUT else echo "This release is the latest. Proceeding with update." echo "is_latest=true" >> $GITHUB_OUTPUT fi - name: Checkout homebrew-tap repository if: steps.check-latest.outputs.is_latest == 'true' uses: actions/checkout@v6 with: repository: siderolabs/homebrew-tap token: ${{ secrets.HOMEBREW_TAP_TOKEN }} path: homebrew-tap - name: Update Homebrew formula if: steps.check-latest.outputs.is_latest == 'true' env: VERSION: ${{ steps.version.outputs.version }} run: | cd homebrew-tap FORMULA="Formula/talosctl.rb" CURRENT_VERSION=$(grep -Po 'version "\K(\d+\.\d+\.\d+)' "${FORMULA}") echo "Current version: ${CURRENT_VERSION}" echo "New version: ${VERSION}" grep -Po '(url "\K[^"]+)|(sha256 "\K[^"]+)' "${FORMULA}" | while IFS= read -r url && read -r old_hash; do new_url="${url//\#\{version\}/${VERSION}}" echo "===> Downloading ${new_url}..." wget -q -O /tmp/talosctl-binary "${new_url}" echo "=====> Calculating SHA256 checksum..." new_hash=$(sha256sum /tmp/talosctl-binary | awk '{print $1}') echo "=====> Old hash: ${old_hash}" echo "=====> New hash: ${new_hash}" echo "=====> Updating hash in formula..." sed -i "s/${old_hash}/${new_hash}/" "${FORMULA}" rm /tmp/talosctl-binary done echo "===> Updating version in formula: ${CURRENT_VERSION} -> ${VERSION}" sed -i "s/${CURRENT_VERSION}/${VERSION}/g" "${FORMULA}" echo "===> Updated formula:" cat "${FORMULA}" - name: Commit and push changes if: steps.check-latest.outputs.is_latest == 'true' env: VERSION: ${{ steps.version.outputs.version }} run: | cd homebrew-tap git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git checkout -b "update-talosctl-v${VERSION}" git add Formula/talosctl.rb git commit -m "chore: update talosctl to v${VERSION}" git push origin "update-talosctl-v${VERSION}" - name: Create Pull Request if: steps.check-latest.outputs.is_latest == 'true' env: VERSION: ${{ steps.version.outputs.version }} GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} run: | cd homebrew-tap gh pr create \ --title "chore: update talosctl to v${VERSION}" \ --body "Automated update of talosctl formula to version v${VERSION} This PR was automatically generated by the update-homebrew workflow. Release: https://github.com/${{ github.repository }}/releases/tag/v${VERSION}" \ --head "update-talosctl-v${VERSION}" \ --base main ================================================ FILE: .gitignore ================================================ .env .envrc bin _out .vscode .idea *.code-workspace init.yaml controlplane.yaml join.yaml worker.yaml machineconfig.yaml talosconfig /kubeconfig hack/test/libvirt/matchbox/assets/* !hack/test/libvirt/matchbox/assets/.gitkeep # vim Swap [._]*.s[a-v][a-z] [._]*.sw[a-p] [._]s[a-rt-v][a-z] [._]ss[a-gi-z] [._]sw[a-p] # Go .artifacts/ go.work.sum sha256sum.txt sha512sum.txt **/packet-cluster.yaml # Local Netlify folder .netlify *.lock node_modules website/resources website/public website/.hugo_build.lock ================================================ FILE: .golangci.yml ================================================ version: "2" run: build-tags: - integration - integration_api - integration_cli - integration_k8s - integration_provision issues-exit-code: 1 tests: true output: formats: text: path: stdout print-linter-name: true print-issued-lines: true colors: false linters: default: all disable: - contextcheck - depguard - dupword - err113 - errorlint - exhaustruct - forbidigo - forcetypeassert - funcorder - funlen - gochecknoglobals - gochecknoinits - gocognit - godoclint - godox - gosec - inamedparam - ireturn - maintidx - mnd - musttag - nakedret - nestif - nilnil - noinlineerr - nolintlint - nonamedreturns - paralleltest - perfsprint - promlinter - protogetter - recvcheck - tagalign - tagliatelle - testifylint - thelper - varnamelen - wrapcheck - wsl # replaced by wsl_v5 settings: cyclop: max-complexity: 20 dupl: threshold: 100 errcheck: check-type-assertions: false check-blank: true exclude-functions: - fmt.Fprintln - fmt.Fprintf - fmt.Fprint goconst: min-len: 3 min-occurrences: 3 gocyclo: min-complexity: 10 gomoddirectives: replace-allow-list: - gopkg.in/yaml.v3 - github.com/coredns/coredns - github.com/mdlayher/kobject - golang.zx2c4.com/wireguard - golang.zx2c4.com/wireguard/wgctrl - cloud.google.com/go - github.com/mdlayher/ethtool - github.com/containerd/containerd/v2 - github.com/jsimonetti/rtnetlink/v2 - github.com/neticdk/go-stdlib replace-local: true exclude-forbidden: false retract-allow-no-explanation: false lll: line-length: 200 tab-width: 1 misspell: locale: US nolintlint: require-explanation: false require-specific: true allow-unused: false prealloc: simple: true range-loops: true for-loops: false staticcheck: checks: ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-QF1001", "-QF1008"] unused: local-variables-are-used: false exclusions: generated: lax rules: - linters: - dupl path: cmd/talosctl/cmd - linters: - dupl path: internal/app/machined/internal/phase - linters: - dupl path: internal/app/machined/pkg/system/services - linters: - revive path: cmd/talosctl/cmd/mgmt text: should have a package comment - linters: - revive path: cmd/talosctl/cmd/mgmt/inject text: should have a package comment - linters: - revive path: cmd/talosctl/cmd/talos text: should have a package comment - linters: - revive path: cmd/talosctl/pkg/talos/action text: should have a package comment - linters: - revive path: cmd/talosctl/pkg/talos/global text: should have a package comment - linters: - revive path: cmd/talosctl/pkg/talos/helpers text: should have a package comment - linters: - revive path: internal/app/machined/pkg/controllers/cri text: should have a package comment - linters: - revive path: internal/app/machined/pkg/controllers/kubeaccess/serviceaccount text: should have a package comment - linters: - revive path: internal/app/machined/pkg/controllers/perf text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/events text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/health text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/runner/containerd text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/runner/goroutine text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/runner/process text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system/runner/restart text: should have a package comment - linters: - revive path: internal/app/machined/pkg/system text: should have a package comment - linters: - revive path: internal/app/maintenance text: should have a package comment - linters: - revive path: internal/app/maintenance/server text: should have a package comment - linters: - revive path: internal/app/poweroff text: should have a package comment - linters: - revive path: internal/app/trustd/internal/reg text: should have a package comment - linters: - revive path: internal/app/trustd text: should have a package comment - linters: - revive path: internal/pkg/containers/image text: should have a package comment - linters: - revive path: internal/pkg/etcd text: should have a package comment - linters: - revive path: internal/pkg/install text: should have a package comment - linters: - revive path: internal/pkg/mount text: should have a package comment - linters: - revive path: internal/pkg/mount/switchroot text: should have a package comment - linters: - revive path: internal/pkg/tui/components text: should have a package comment - linters: - revive path: pkg/argsbuilder text: should have a package comment - linters: - revive path: pkg/chunker text: should have a package comment - linters: - revive path: pkg/chunker/file text: should have a package comment - linters: - revive path: pkg/chunker/stream text: should have a package comment - linters: - revive path: pkg/download text: should have a package comment - linters: - revive path: pkg/grpc/dialer text: should have a package comment - linters: - revive path: pkg/grpc/factory text: should have a package comment - linters: - revive path: pkg/grpc/gen text: should have a package comment - linters: - revive path: pkg/grpc/middleware/auth/basic text: should have a package comment - linters: - revive path: pkg/grpc/middleware/authz text: should have a package comment - linters: - revive path: pkg/kubernetes text: should have a package comment - linters: - revive path: pkg/kubernetes/inject text: should have a package comment - linters: - revive path: pkg/provision/providers text: should have a package comment - linters: - revive path: pkg/provision/providers/qemu text: should have a package comment - linters: - revive path: config/encoder text: should have a package comment - linters: - revive path: resources/kubespan text: should have a package comment - linters: - revive path: client/config text: should have a package comment - linters: - revive path: config/merge text: should have a package comment - linters: - revive path: config/types/v1alpha1/bundle text: should have a package comment - linters: - revive path: resources/cri text: should have a package comment - linters: - revive path: resources/runtime text: should have a package comment - linters: - revive path: kernel text: should have a package comment - linters: - revive path: constants text: should have a package comment - linters: - revive path: resources/perf text: should have a package comment - linters: - revive path: resources/cluster text: should have a package comment - linters: - revive path: role text: should have a package comment - linters: - revive path: resources/hardware text: should have a package comment - linters: - revive path: config/decoder text: should have a package comment - linters: - revive path: config/internal/cis text: should have a package comment - linters: - revive path: config/types/v1alpha1/machine text: should have a package comment - linters: - revive path: internal/integration/api text: avoid meaningless package names - linters: - revive path: internal/app/machined/pkg/runtime/v1alpha1/platform/errors text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/machined/pkg/controllers/time text: avoid package names that conflict with Go standard library package names - linters: - revive path: pkg/machinery/resources/time text: avoid package names that conflict with Go standard library package names - linters: - revive path: pkg/machinery/resources/runtime text: avoid package names that conflict with Go standard library package names - linters: - revive path: pkg/machinery/version text: avoid package names that conflict with Go standard library package names - linters: - revive path: pkg/machinery/config/types/runtime text: avoid package names that conflict with Go standard library package names - linters: - revive path: cmd/talosctl/cmd/mgmt/debug text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/debug text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/machined/internal/server/v1alpha1 text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/machined/pkg/controllers/runtime text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/machined/pkg/runtime text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/app/syslogd/internal/parser text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/pkg/containers/image text: avoid package names that conflict with Go standard library package names - linters: - revive path: internal/pkg/uki/internal/pe text: avoid package names that conflict with Go standard library package names - linters: - revive path: pkg/grpc/middleware/log text: avoid package names that conflict with Go standard library package names - linters: - dupl path: config/types/v1alpha1 - linters: - unused path: specs-go/config.go - path: (.+)\.go$ text: package comment should be of the form "Package services ..." - path: (.+)\.go$ text: ^ST1000 - path: (.+)\.go$ text: parameter '\w+' seems to be unused, consider removing or renaming it as _ paths: - .*\\.pb\\.go$ - third_party$ - builtin$ - examples$ issues: max-issues-per-linter: 0 max-same-issues: 0 uniq-by-line: true new: false formatters: enable: - gci - gofmt - gofumpt - goimports settings: gci: sections: - standard - default - prefix(github.com/siderolabs/talos) exclusions: generated: lax paths: - .*\\.pb\\.go$ - third_party$ - builtin$ - examples$ ================================================ FILE: .kres.yaml ================================================ --- kind: auto.CI spec: compileGHWorkflowsOnly: true --- kind: common.SOPS spec: enabled: true config: |- creation_rules: - age: age1xrpa9ujxxcj2u2gzfrzv8mxak4rts94a6y60ypurv6rs5cpr4e4sg95f0k # order: Andrey, Noel, Artem, Utku, Tim, Andy pgp: >- 15D5721F5F5BAF121495363EFE042E3D4085A811, CC51116A94490FA6FB3C18EB2401FCAE863A06CA, 4919F560F0D35F80CF382D76E084A2DF1143C14D, 11177A43C6E3752E682AC690DBD13117B0A14E93, 5D7964FF2DB426ACB3C3505AA2A702DD5B689F45, E77ED9F8451E10BD242D07415444881046F92733 --- kind: common.Repository spec: conformScopes: - apid - machined - networkd - talosctl - trustd - talosctl - kernel - security - ci - ^v1.13 licenseChecks: - skipPaths: - .git/ - testdata/ includeSuffixes: - .go excludeSuffixes: - .pb.go - .pb.gw.go - _string.go - _enumer.go - _string_linux.go - zz_generated.deepcopy.go header: | // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. enforceContexts: - default - base-lint - base-unit-tests - e2e-iso - e2e-qemu-short - e2e-docker-short - integration-qemu - integration-qemu-enforcing - integration-embedded - integration-trusted-boot - integration-trusted-boot-enforcing - integration-provision-0 - integration-provision-1 - integration-provision-2 - integration-provision-3 - integration-airgapped - integration-misc-0 - integration-misc-1 - integration-misc-1-enforcing - integration-misc-2 - integration-misc-3 - integration-misc-3-enforcing - integration-misc-4 - integration-misc-4-enforcing - integration-extensions - integration-cilium - integration-conformance - integration-conformance-enforcing - integration-qemu-encrypted-vip - integration-qemu-race - integration-qemu-csi-rook-ceph - integration-qemu-csi-longhorn - integration-qemu-csi-openebs - integration-images - integration-reproducibility-test - integration-cloud-images - integration-image-cache - integration-image-factory - integration-aws - integration-aws-nvidia-oss-lts - integration-aws-nvidia-oss-production - integration-aws-nvidia-nonfree-lts - integration-aws-nvidia-nonfree-production - integration-gcp --- kind: common.GHWorkflow spec: jobs: - name: default buildxOptions: enabled: true runnerGroup: large conditions: - on-pull-request steps: - name: ci-temp-release-tag - name: external-artifacts - name: generate command: generate docs - name: uki-certs environment: PLATFORM: linux/amd64 - name: check-dirty - name: build command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle - name: sbom - name: iso command: iso secureboot-iso environment: IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-artifacts artifactStep: type: upload artifactName: talos-artifacts artifactPath: _out - name: push buildxOptions: enabled: true runnerGroup: large conditions: - except-pull-request - not-on-tag steps: - name: build command: talosctl-all kernel sd-boot sd-stub initramfs installer-base imager talos environment: PLATFORM: linux/amd64,linux/arm64 - name: release-notes - name: login-to-registry registryLoginStep: registry: ghcr.io - name: push environment: PLATFORM: linux/amd64,linux/arm64 - name: push-latest environment: PLATFORM: linux/amd64,linux/arm64 conditions: - only-on-main-branch - name: tag sops: true buildxOptions: enabled: true runnerGroup: large conditions: - only-on-tag steps: - name: build command: talosctl-all kernel sd-boot sd-stub initramfs installer-base imager talos talosctl-cni-bundle environment: PLATFORM: linux/amd64,linux/arm64 - name: release-notes - name: sbom - name: login-to-registry registryLoginStep: registry: ghcr.io - name: push environment: PLATFORM: linux/amd64,linux/arm64 - name: images environment: PLATFORM: linux/amd64,linux/arm64 - name: release releaseStep: baseDirectory: _out artifacts: - initramfs-amd64.xz - initramfs-arm64.xz - metal-amd64.iso - metal-arm64.iso - metal-amd64-uki.efi - metal-arm64-uki.efi - metal-amd64.raw.zst - metal-arm64.raw.zst - talos-arm64.spdx.json - talos-amd64.spdx.json - talos-container-arm64.spdx.json - talos-container-amd64.spdx.json - talosctl-cni-bundle-amd64.tar.gz - talosctl-cni-bundle-arm64.tar.gz - talosctl-darwin-amd64 - talosctl-darwin-arm64 - talosctl-freebsd-amd64 - talosctl-freebsd-arm64 - talosctl-linux-amd64 - talosctl-linux-arm64 - talosctl-linux-armv7 - talosctl-linux-riscv64 - talosctl-windows-amd64.exe - talosctl-windows-arm64.exe - vmlinuz-amd64 - vmlinuz-arm64 generateChecksums: true generateSignatures: true releaseNotes: RELEASE_NOTES.md - name: cloud-images dispatchable: true sops: true runnerGroup: generic # this is not compute intensive - we are pulling images from factory inputs: - tag - release steps: - name: cloud-images environment: CLOUD_IMAGES_EXTRA_ARGS: |- --use-factory PLATFORM: linux/amd64,linux/arm64 TAG: ${{ github.event.inputs.tag }} - name: attach-images nonMakeStep: true environment: RELEASE: ${{ github.event.inputs.release }} GH_TOKEN: ${{ github.token }} command: |- gh release upload "${RELEASE}" --clobber \ _out/cloud-images.json - name: base-unit-tests buildxOptions: enabled: true depends: - default runnerGroup: large steps: - name: download-artifacts artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag - name: unit-tests - name: unit-tests-fips - name: unit-tests-race - name: coverage coverageStep: files: - _out/coverage.txt timeoutMinutes: 3 - name: base-lint buildxOptions: enabled: true depends: - default runnerGroup: large steps: - name: download-artifacts artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag - name: lint - name: grype-scan buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 7 * * *' steps: - name: login-to-registry registryLoginStep: registry: ghcr.io - name: local-grype-scan-result environment: DEST: _out # Fail the run if found vulnerabilities which do not have a statement - name: target-grype-validate - name: save-scan-result conditions: - always artifactStep: type: upload artifactName: talos-grype-scan-result disableExecutableListGeneration: true artifactPath: _out/grype-scan.log - name: e2e-iso depends: - default runnerGroup: large steps: - name: download-artifacts artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag - name: e2e-iso withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-e2e-iso disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: e2e-qemu-short depends: - default runnerGroup: large steps: - name: download-artifacts artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag - name: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-short IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" SHORT_INTEGRATION_TEST: yes - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-e2e-qemu-short disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: e2e-docker-short depends: - default runnerGroup: large steps: - name: download-artifacts artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag - name: e2e-docker withSudo: false environment: IMAGE_REGISTRY: registry.dev.siderolabs.io SHORT_INTEGRATION_TEST: yes - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-e2e-docker-short disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-qemu withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_DRIVERS: "virtiofs,ide,nvme" QEMU_EXTRA_DISKS_TAGS: "disk0" WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" WITH_CONFIG_PATCH_WORKER: "@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml" WITH_USER_DISK: "true" USER_DISKS_MOUNTS: "/var/mnt/extra,/var/mnt/p1,/var/mnt/p2" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-enforcing - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: e2e-qemu withSudo: true environment: TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "3" QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_DRIVERS: "ide,nvme" WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" WITH_CONFIG_PATCH_WORKER: "@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml" WITH_USER_DISK: "true" WITH_ENFORCING: true USER_DISKS_MOUNTS: "/var/mnt/extra,/var/mnt/p1,/var/mnt/p2" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-embedded buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/embedded - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-embedded withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-embedded disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-conformance buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/conformance - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: conformance-qemu command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu QEMU_CPUS_WORKERS: 6 QEMU_MEMORY_WORKERS: 4096 TEST_MODE: fast-conformance IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-conformance disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-conformance-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/conformance-enforcing steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: conformance-qemu command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-conformance-qemu QEMU_CPUS_WORKERS: 6 QEMU_MEMORY_WORKERS: 4096 TEST_MODE: fast-conformance TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-conformance-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-trusted-boot buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/trusted-boot - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: secureboot-iso conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: integration-trusted-boot command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot VIA_MAINTENANCE_MODE: true WITH_TRUSTED_BOOT_ISO: true WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" EXTRA_TEST_ARGS: "-talos.trustedboot" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-trusted-boot disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-trusted-boot-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/trusted-boot-enforcing steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: secureboot-iso conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: integration-trusted-boot-enforcing command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-integration-trusted-boot-enforcing VIA_MAINTENANCE_MODE: true WITH_TRUSTED_BOOT_ISO: true TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: "-talos.trustedboot -talos.enforcing" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-trusted-boot-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-provision-0 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/provision - integration/provision-0 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: provision-tests-prepare - name: provision-tests-track-0 withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io GRPC_ENFORCE_ALPN_ENABLED: "false" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-provision-0 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-provision-1 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/provision - integration/provision-1 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: provision-tests-prepare - name: provision-tests-track-1 withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io GRPC_ENFORCE_ALPN_ENABLED: "false" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-provision-1 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-provision-2 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/provision - integration/provision-2 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: installer environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io IMAGER_ARGS: "--extra-kernel-arg=talos.extra_cmdline=extra-super-cmdline" TAG_SUFFIX_OUT: -extra-cmdline - name: provision-tests-prepare - name: provision-tests-track-2 withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io GRPC_ENFORCE_ALPN_ENABLED: "false" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-provision-2 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-provision-3 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 4 * * *' triggerLabels: - integration/provision - integration/provision-3 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: provision-tests-prepare - name: provision-tests-track-3 withSudo: true environment: IMAGE_REGISTRY: registry.dev.siderolabs.io GRPC_ENFORCE_ALPN_ENABLED: "false" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-provision-3 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-airgapped buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/airgapped - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: integration-images-list command: integration-images-list environment: IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-airgapped-no-proxy command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-proxy SHORT_INTEGRATION_TEST: yes WITH_AIRGAPPED: no-proxy WITH_CLUSTER_DISCOVERY: "false" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-airgapped-http-proxy command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-http-proxy SHORT_INTEGRATION_TEST: yes WITH_AIRGAPPED: http-proxy WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-airgapped-secure-proxy command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-secure-proxy SHORT_INTEGRATION_TEST: yes WITH_AIRGAPPED: secure-http-proxy WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-airgapped-reverse-proxy command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-reverse-proxy SHORT_INTEGRATION_TEST: yes WITH_AIRGAPPED: https-reverse-proxy WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-airgapped disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - "/tmp/airgapped*.log" - name: integration-misc-0 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/misc-0 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-metal-uki # needed for e2e-uki-4k conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-firewall command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-firewall SHORT_INTEGRATION_TEST: yes WITH_FIREWALL: block IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-canal-reset command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-canal-reset INTEGRATION_TEST_RUN: TestIntegration/api.ResetSuite/TestResetWithSpec CUSTOM_CNI_URL: https://raw.githubusercontent.com/projectcalico/calico/v3.30.3/manifests/canal.yaml IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-controlplane-port command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-controlplane-port SHORT_INTEGRATION_TEST: yes WITH_CONTROL_PLANE_PORT: 443 IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_CONFIG_PATCH: "@hack/test/patches/ephemeral-min-max.yaml" - name: e2e-uki-4k command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-uki-4k SHORT_INTEGRATION_TEST: yes IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_UKI_BOOT: true WITH_4K_DISK: true - name: e2e-flannel-netpol command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-flannel-netpol SHORT_INTEGRATION_TEST: yes IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_CONFIG_PATCH: "@hack/test/patches/flannel-netpol.yaml" TEST_MODE: network-policy - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-0 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-1 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/misc-1 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-no-cluster-discovery command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery SHORT_INTEGRATION_TEST: yes WITH_CLUSTER_DISCOVERY: false IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-kubespan command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan SHORT_INTEGRATION_TEST: yes WITH_CLUSTER_DISCOVERY: true WITH_KUBESPAN: "true" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-default-hostname command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname SHORT_INTEGRATION_TEST: yes VIA_MAINTENANCE_MODE: true DISABLE_DHCP_HOSTNAME: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-min-requirements command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements SHORT_INTEGRATION_TEST: yes QEMU_MEMORY_WORKERS: 1024 QEMU_MEMORY_CONTROLPLANES: 2048 QEMU_SYSTEM_DISK_SIZE: 10240 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-1 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-1-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc-enforcing - integration/misc-1-enforcing - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: e2e-no-cluster-discovery command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-no-cluster-discovery SHORT_INTEGRATION_TEST: yes WITH_CLUSTER_DISCOVERY: false TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-kubespan command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-kubespan SHORT_INTEGRATION_TEST: yes WITH_CLUSTER_DISCOVERY: true WITH_KUBESPAN: true TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-default-hostname command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-default-hostname SHORT_INTEGRATION_TEST: yes VIA_MAINTENANCE_MODE: true DISABLE_DHCP_HOSTNAME: true TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-min-requirements command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-min-requirements SHORT_INTEGRATION_TEST: yes QEMU_MEMORY_WORKERS: 1024 QEMU_MEMORY_CONTROLPLANES: 2048 QEMU_SYSTEM_DISK_SIZE: 10240 TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-1-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-2 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/misc-2 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: iso # needed for e2e-bios-iso conditions: - only-on-schedule command: iso environment: IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: images-essential conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-bios command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios SHORT_INTEGRATION_TEST: yes WITH_UEFI: false IMAGE_REGISTRY: registry.dev.siderolabs.io EXTRA_TEST_ARGS: "-talos.verifyukibooted=false" - name: e2e-bios-iso command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-bios-iso SHORT_INTEGRATION_TEST: yes WITH_UEFI: false VIA_MAINTENANCE_MODE: true WITH_ISO: true IMAGE_REGISTRY: registry.dev.siderolabs.io EXTRA_TEST_ARGS: "-talos.verifyukibooted=false" - name: e2e-disk-image command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image SHORT_INTEGRATION_TEST: yes USE_DISK_IMAGE: true VIA_MAINTENANCE_MODE: true WITH_DISK_ENCRYPTION: true IMAGE_REGISTRY: registry.dev.siderolabs.io WITH_JSON_LOGS: "false" # test this path as well - name: e2e-disk-image-bios command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image-bios SHORT_INTEGRATION_TEST: yes USE_DISK_IMAGE: true VIA_MAINTENANCE_MODE: true WITH_DISK_ENCRYPTION: true WITH_UEFI: false IMAGE_REGISTRY: registry.dev.siderolabs.io EXTRA_TEST_ARGS: "-talos.verifyukibooted=false" - name: e2e-node-address-v2 command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-disk-image SHORT_INTEGRATION_TEST: yes WITH_CONFIG_PATCH: "@hack/test/patches/node-address-v2.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-tpm1_2 command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-tpm1_2 SHORT_INTEGRATION_TEST: yes WITH_TPM1_2: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-2 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-3 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/misc-3 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-network-chaos command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos SHORT_INTEGRATION_TEST: yes WITH_NETWORK_CHAOS: yes IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-metal-iso command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso SHORT_INTEGRATION_TEST: yes WITH_CONFIG_INJECTION_METHOD: "metal-iso" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-iommu-pcidriverrebind command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind SHORT_INTEGRATION_TEST: yes WITH_IOMMU: yes IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-3 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-3-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc-enforcing - integration/misc-3-enforcing steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: e2e-network-chaos command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-network-chaos SHORT_INTEGRATION_TEST: yes WITH_NETWORK_CHAOS: yes TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-metal-iso command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-metal-iso SHORT_INTEGRATION_TEST: yes WITH_CONFIG_INJECTION_METHOD: "metal-iso" TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-iommu-pcidriverrebind command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-iommu-pcidriverrebind SHORT_INTEGRATION_TEST: yes WITH_IOMMU: yes TAG_SUFFIX_IN: -enforcing EXTRA_TEST_ARGS: -talos.enforcing WITH_ENFORCING: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-3-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-4 buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc - integration/misc-4 - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-siderolink command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: true VIA_MAINTENANCE_MODE: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-siderolink-tunnel command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: tunnel VIA_MAINTENANCE_MODE: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-siderolink-tls command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: wireguard+tls VIA_MAINTENANCE_MODE: true IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-apparmor command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor SHORT_INTEGRATION_TEST: yes WITH_APPARMOR_LSM_ENABLED: yes IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-k8s-user-namespace command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace SHORT_INTEGRATION_TEST: yes WITH_CONFIG_PATCH: "@hack/test/patches/usernamespace.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-4 disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-misc-4-enforcing buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 5 * * *' triggerLabels: - integration/misc-enforcing - integration/misc-4-enforcing - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: uki-certs environment: PLATFORM: linux/amd64 conditions: - only-on-schedule - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential-enforcing command: images-essential environment: PLATFORM: linux/amd64,linux/arm64 IMAGER_ARGS: "--extra-kernel-arg=console=ttyS0 --extra-kernel-arg=enforcing=1" TAG_SUFFIX_OUT: -enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: e2e-siderolink command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: true VIA_MAINTENANCE_MODE: true TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-siderolink-tunnel command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tunnel SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: tunnel VIA_MAINTENANCE_MODE: true TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-siderolink-tls command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-siderolink-tls SHORT_INTEGRATION_TEST: yes WITH_SIDEROLINK_AGENT: wireguard+tls VIA_MAINTENANCE_MODE: true TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-apparmor command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-apparmor SHORT_INTEGRATION_TEST: yes WITH_APPARMOR_LSM_ENABLED: yes IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-k8s-user-namespace command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-k8s-user-namespace SHORT_INTEGRATION_TEST: yes WITH_CONFIG_PATCH: "@hack/test/patches/usernamespace.yaml" TAG_SUFFIX_IN: -enforcing WITH_ENFORCING: true EXTRA_TEST_ARGS: -talos.enforcing IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-misc-4-enforcing disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-extensions buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 6 * * *' triggerLabels: - integration/extensions - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: unshallow-extensions nonMakeStep: true command: git -C _out/extensions fetch --prune --unshallow - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: all extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: installer extensions command: installer-with-extensions environment: IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-extensions command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-extensions QEMU_WORKERS: 1 QEMU_MEMORY_WORKERS: 4096 WITH_CONFIG_PATCH_WORKER: "@_out/installer-extensions-patch.yaml:@hack/test/patches/extensions.yaml:@hack/test/patches/dm-raid-module.yaml" QEMU_EXTRA_DISKS: 3 SHORT_INTEGRATION_TEST: yes EXTRA_TEST_ARGS: -talos.extensions.qemu IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-extensions disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-cilium buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/cilium - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-cilium command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium SHORT_INTEGRATION_TEST: yes WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept WITH_CONFIG_PATCH: "@hack/test/patches/cilium-no-kubeproxy.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-cilium-strict command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict SHORT_INTEGRATION_TEST: yes WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept CILIUM_INSTALL_TYPE: strict WITH_CONFIG_PATCH: "@hack/test/patches/cilium-kubeproxy.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-cilium-strict-kubespan command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-cilium-strict-kubespan SHORT_INTEGRATION_TEST: yes WITH_CUSTOM_CNI: cilium WITH_FIREWALL: accept WITH_KUBESPAN: true CILIUM_INSTALL_TYPE: strict WITH_CONFIG_PATCH: "@hack/test/patches/cilium-kubeproxy.yaml" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-cilium disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-encrypted-vip buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-encrypted-vip - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-qemu withSudo: true environment: WITH_DISK_ENCRYPTION: true WITH_VIRTUAL_IP: true WITH_KUBESPAN: true IMAGE_REGISTRY: registry.dev.siderolabs.io QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_DRIVERS: "virtiofs,ide,nvme" QEMU_EXTRA_DISKS_TAGS: "disk0" WITH_CONFIG_PATCH: "@hack/test/patches/image-verification.yaml" WITH_CONFIG_PATCH_WORKER: "@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-encrypted-vip disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-race buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-race - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: build-race command: initramfs installer-base imager installer environment: PUSH: true TAG_SUFFIX: -race WITH_RACE: 1 PLATFORM: linux/amd64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-qemu-race command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-race QEMU_EXTRA_DISKS: "4" QEMU_EXTRA_DISKS_SIZE: "10240" QEMU_EXTRA_DISKS_DRIVERS: "virtiofs,ide,nvme" QEMU_EXTRA_DISKS_TAGS: "disk0" WITH_CONFIG_PATCH_WORKER: "@hack/test/patches/ephemeral-nvme.yaml:@hack/test/patches/dm-raid-module.yaml" QEMU_MEMORY_CONTROLPLANES: 4096 # race-enabled Talos consumes lots of RAM QEMU_MEMORY_WORKERS: 4096 EXTRA_TEST_ARGS: "-talos.race" TAG_SUFFIX: -race IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-race disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-csi-rook-ceph buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-csi - integration/qemu-csi-rook-ceph - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-qemu-csi-rook-ceph command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-rook-ceph SHORT_INTEGRATION_TEST: yes QEMU_WORKERS: 3 QEMU_CPUS_WORKERS: 6 QEMU_MEMORY_WORKERS: 8192 QEMU_EXTRA_DISKS: 1 QEMU_EXTRA_DISKS_SIZE: 12288 WITH_CONFIG_PATCH_CONTROLPLANE: "@hack/test/patches/rook-ceph.yaml" EXTRA_TEST_ARGS: -talos.csi=rook-ceph IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-fio-benchmark artifactStep: type: upload artifactName: fio-integration-qemu-csi-rook-ceph disableExecutableListGeneration: true artifactPath: /tmp/fio-*.json retentionDays: "180" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-csi-rook-ceph disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-csi-longhorn buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-csi - integration/extensions # since iscsi is tested with longhorn - integration/qemu-csi-longhorn - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: iscsi-tools util-linux-tools extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: installer extensions command: installer-with-extensions environment: EXTENSIONS_FILTER_COMMAND: "grep -E '/iscsi-tools|util-linux-tools'" IMAGE_REGISTRY: registry.dev.siderolabs.io - name: kubelet-fat-patch - name: e2e-qemu-csi-longhorn command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-longhorn SHORT_INTEGRATION_TEST: yes QEMU_WORKERS: 3 QEMU_MEMORY_WORKERS: 8192 QEMU_SYSTEM_DISK_SIZE: 20480 QEMU_EXTRA_DISKS: 1 QEMU_EXTRA_DISKS_SIZE: 12288 QEMU_EXTRA_DISKS_DRIVERS: nvme WITH_CONFIG_PATCH_CONTROLPLANE: "@hack/test/patches/longhorn-cp.yaml" WITH_CONFIG_PATCH_WORKER: "@_out/installer-extensions-patch.yaml:@_out/kubelet-fat-patch.yaml:@hack/test/patches/longhorn.yaml" EXTRA_TEST_ARGS: -talos.csi=longhorn IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-fio-benchmark artifactStep: type: upload artifactName: fio-integration-qemu-csi-longhorn disableExecutableListGeneration: true artifactPath: /tmp/fio-*.json retentionDays: "180" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-csi-longhorn disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-qemu-csi-openebs buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 3 * * *' triggerLabels: - integration/qemu-csi - integration/qemu-csi-openebs - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: e2e-qemu-csi-openebs command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-qemu-csi-openebs SHORT_INTEGRATION_TEST: yes QEMU_WORKERS: 3 QEMU_CPUS_WORKERS: 4 QEMU_MEMORY_WORKERS: 8192 QEMU_EXTRA_DISKS: 1 QEMU_EXTRA_DISKS_SIZE: 12288 WITH_CONFIG_PATCH_CONTROLPLANE: "@hack/test/patches/openebs-cp.yaml" WITH_CONFIG_PATCH_WORKER: "@hack/test/patches/openebs.yaml" EXTRA_TEST_ARGS: -talos.csi=openebs IMAGE_REGISTRY: registry.dev.siderolabs.io - name: save-fio-benchmark artifactStep: type: upload artifactName: fio-integration-qemu-csi-openebs disableExecutableListGeneration: true artifactPath: /tmp/fio-*.json retentionDays: "180" - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-qemu-csi-openebs disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-images buildxOptions: enabled: true depends: - default runnerGroup: generic # this is pretty fast, so we can use generic crons: - '30 2 * * *' triggerLabels: - integration/images - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: integration-reproducibility-test buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 2 * * *' triggerLabels: - integration/reproducibility-test - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: reproducibility-test environment: IMAGE_REGISTRY: registry.dev.siderolabs.io - name: integration-cloud-images buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # this is not compute intensive, so we can use generic triggerLabels: - integration/cloud-images steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: images environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: cloud-images - name: integration-image-cache buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 2 * * *' triggerLabels: - integration/image-cache - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-cache command: cache-create environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: e2e-image-cache command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: no SHORT_INTEGRATION_TEST: yes VIA_MAINTENANCE_MODE: true WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: true - name: e2e-image-cache-encrypted command: e2e-qemu withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-e2e-image-cache-encrypted IMAGE_REGISTRY: registry.dev.siderolabs.io REGISTRY_MIRROR_FLAGS: no SHORT_INTEGRATION_TEST: yes VIA_MAINTENANCE_MODE: true WITH_CONFIG_PATCH: '@hack/test/patches/image-cache.yaml:@hack/test/patches/image-cache-encrypted.yaml:@hack/test/patches/image-verification.yaml' WITH_ISO: true - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-image-cache disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-image-factory buildxOptions: enabled: true depends: - default runnerGroup: large crons: - '30 6 * * *' triggerLabels: - integration/image-factory steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: factory-1.11-iso command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-iso FACTORY_BOOT_METHOD: iso FACTORY_VERSION: v1.11.5 FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba KUBERNETES_VERSION: 1.34.3 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 - name: factory-1.11-image command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-image FACTORY_BOOT_METHOD: disk-image FACTORY_VERSION: v1.11.5 FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba KUBERNETES_VERSION: 1.34.3 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.11.6 - name: factory-1.11-pxe command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-pxe FACTORY_BOOT_METHOD: ipxe FACTORY_VERSION: v1.11.6 FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba KUBERNETES_VERSION: 1.34.3 - name: factory-1.11-secureboot command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.11-secureboot FACTORY_BOOT_METHOD: secureboot-iso FACTORY_VERSION: v1.11.5 FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f KUBERNETES_VERSION: 1.34.3 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.11.6 - name: factory-1.10-secureboot command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-secureboot FACTORY_BOOT_METHOD: secureboot-iso FACTORY_VERSION: v1.10.8 FACTORY_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f KUBERNETES_VERSION: 1.33.7 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba FACTORY_UPGRADE_VERSION: v1.10.9 - name: factory-1.10-iso command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.10-iso FACTORY_BOOT_METHOD: iso FACTORY_VERSION: v1.10.8 FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba KUBERNETES_VERSION: 1.33.7 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.10.9 - name: factory-1.9-iso command: e2e-image-factory withSudo: true environment: GITHUB_STEP_NAME: ${{ github.job}}-factory-1.9-iso FACTORY_BOOT_METHOD: iso FACTORY_VERSION: v1.9.5 FACTORY_SCHEMATIC: 376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba KUBERNETES_VERSION: 1.32.11 FACTORY_UPGRADE: true FACTORY_UPGRADE_SCHEMATIC: cf9b7aab9ed7c365d5384509b4d31c02fdaa06d2b3ac6cc0bc806f28130eff1f FACTORY_UPGRADE_VERSION: v1.9.6 - name: save-talos-logs conditions: - always artifactStep: type: upload artifactName: talos-logs-integration-image-factory disableExecutableListGeneration: true artifactPath: /tmp/logs-*.tar.gz additionalArtifacts: - "/tmp/support-*.zip" - name: integration-aws buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/aws - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: image-aws environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-aws-prepare environment: E2E_AWS_TARGET: default IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: apply - name: e2e-aws - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false - name: integration-aws-nvidia-oss-lts buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/aws-nvidia-oss-lts - integration/aws-nvidia-oss - integration/aws-nvidia steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-aws environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: nvidia-container-toolkit-lts nvidia-open-gpu-kernel-modules-lts extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: e2e-aws-prepare environment: EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata E2E_AWS_TARGET: nvidia-oss-lts IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: apply - name: e2e-aws-nvidia-oss-lts command: e2e-aws environment: EXTRA_TEST_ARGS: "-talos.extensions.nvidia -talos.verifyukibooted=false" INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false - name: integration-aws-nvidia-oss-production buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/aws-nvidia-oss-production - integration/aws-nvidia-oss - integration/aws-nvidia steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-aws environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: nvidia-container-toolkit-production nvidia-open-gpu-kernel-modules-production extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: e2e-aws-prepare environment: EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata E2E_AWS_TARGET: nvidia-oss-production IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: apply - name: e2e-aws-nvidia-oss-production command: e2e-aws environment: EXTRA_TEST_ARGS: "-talos.extensions.nvidia -talos.verifyukibooted=false" INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false - name: integration-aws-nvidia-nonfree-lts buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/aws-nvidia-nonfree-lts - integration/aws-nvidia-nonfree - integration/aws-nvidia steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-aws environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: nvidia-container-toolkit-lts nonfree-kmod-nvidia-lts extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: e2e-aws-prepare environment: EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata E2E_AWS_TARGET: nvidia-nonfree-lts IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: apply - name: e2e-aws-nvidia-nonfree-lts command: e2e-aws environment: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false - name: integration-aws-nvidia-nonfree-production buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/aws-nvidia-nonfree-production - integration/aws-nvidia-nonfree - integration/aws-nvidia steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: generate conditions: - only-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: image-aws environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout extensions checkoutStep: repository: siderolabs/extensions ref: main path: _out/extensions - name: set variables nonMakeStep: true command: cat _out/talos-metadata >> "$GITHUB_ENV" - name: build extensions command: nvidia-container-toolkit-production nonfree-kmod-nvidia-production extensions-metadata arguments: - -C - _out/extensions environment: PLATFORM: linux/amd64 PUSH: true REGISTRY: registry.dev.siderolabs.io - name: e2e-aws-prepare environment: EXTENSIONS_METADATA_FILE: _out/extensions/_out/extensions-metadata E2E_AWS_TARGET: nvidia-nonfree-production IMAGE_REGISTRY: registry.dev.siderolabs.io - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: apply - name: e2e-aws-nvidia-nonfree-production command: e2e-aws environment: EXTRA_TEST_ARGS: -talos.extensions.nvidia INTEGRATION_TEST_RUN: TestIntegration/api.ExtensionsSuiteNVIDIA - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: aws TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false - name: integration-gcp buildxOptions: enabled: true sops: true depends: - default runnerGroup: generic # we can use generic here since the tests run against a remote talos cluster crons: - '30 7 * * *' triggerLabels: - integration/gcp - integration/release-gate steps: - name: download-artifacts conditions: - not-on-schedule artifactStep: type: download artifactName: talos-artifacts artifactPath: _out - name: ci-temp-release-tag conditions: - not-on-schedule - name: uki-certs conditions: - only-on-schedule environment: PLATFORM: linux/amd64 - name: build conditions: - only-on-schedule command: talosctl-linux-amd64 kernel sd-boot sd-stub initramfs installer-base imager talos _out/integration-test-linux-amd64 environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io PUSH: true - name: talosctl-cni-bundle conditions: - only-on-schedule - name: images-essential conditions: - only-on-schedule environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: image-gcp environment: PLATFORM: linux/amd64,linux/arm64 IMAGE_REGISTRY: registry.dev.siderolabs.io - name: e2e-gcp-prepare - name: checkout contrib checkoutStep: repository: siderolabs/contrib ref: main path: _out/contrib - name: setup tf terraformStep: true - name: tf apply command: e2e-cloud-tf environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: gcp TF_E2E_ACTION: apply - name: e2e-gcp - name: tf destroy command: e2e-cloud-tf conditions: - always environment: TF_SCRIPT_DIR: _out/contrib TF_E2E_TEST_TYPE: gcp TF_E2E_ACTION: destroy TF_E2E_REFRESH_ON_DESTROY: false --- kind: common.Renovate spec: customManagers: - customType: regex managerFilePatterns: - Makefile matchStrings: - '# renovate: datasource=(?.*?)(?:\s+extractVersion=(?.+?))?\s+depName=(?.+?)\s.*_VERSION\s+\?=\s+(?.+)' versioningTemplate: "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" - customType: regex managerFilePatterns: - pkg/machinery/constants/constants.go matchStrings: - '\/\/\s+renovate: datasource=(?.*?)(?:\s+extractVersion=(?.+?))?(?:\s+versioning=(?.+?))?\s+depName=(?.+?)?\s.*Version\s+=\s+\"(?.+?)\"' versioningTemplate: "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" - customType: regex managerFilePatterns: - internal/integration/k8s/constants.go - internal/integration/api/constants.go matchStrings: - '\/\/\s+renovate: datasource=(?.*?)(?:\s+extractVersion=(?.+?))?(?:\s+versioning=(?.+?))?\s+depName=(?.+?)?(?:\s+registryUrl=(?.+?))?\s.*Version\s+=\s+\"(?.+?)\"' versioningTemplate: "{{#if versioning}}{{versioning}}{{else}}semver{{/if}}" - customType: regex managerFilePatterns: - Dockerfile matchStrings: - '# syntax = docker\/dockerfile-upstream:(?.*)' depNameTemplate: docker/dockerfile-upstream datasourceTemplate: docker versioningTemplate: docker packageRules: - enabled: false matchFileNames: - "website/**" - matchPackageNames: - golang/go versioning: 'regex:^(?\d+)\.(?\d+)' ================================================ FILE: .markdownlint.json ================================================ { "default": true, "MD013": false, "MD033": false, "MD034": false, "MD059": false } ================================================ FILE: .secrets.yaml ================================================ secrets: AWS_ACCESS_KEY_ID: ENC[AES256_GCM,data:WHaM6pwvIE3K619sS1o1eXGMZys=,iv:xsVmRsi682Y9UJfokxoQZomlztyMGh5z0cJOltYOgXk=,tag:kPd9IICZ5PO+iK/gA5D+Ww==,type:str] AWS_SECRET_ACCESS_KEY: ENC[AES256_GCM,data:2Uh2LDkjJoS0/wB2eboeGq6xdb9jIi4D5D7yxGvPE4h+DH1rl/a2DA==,iv:804kZ8I2DtNL1NR4171WClJElaQgRPFKSgIKzAZHCHQ=,tag:Mp9bZVP3cMGA+UeKRIYqiA==,type:str] AWS_DEFAULT_REGION: ENC[AES256_GCM,data:Q7KZm7eenYFn,iv:VuzTTfAHe9FOTcqrGiJU9qdNixTxszm23rSXuNDB3cA=,tag:eGbIm8M/1Lsl0aoa9NanOQ==,type:str] GOOGLE_PROJECT_ID: ENC[AES256_GCM,data:egcG5hIa5aq6tSRjhA==,iv:g/6pkcSJIQNNgoon6X+6DH2JaQgLKfTDpPUNFjlJ6Xg=,tag:ygF0I8bLRRbj2RdogQqxmw==,type:str] GOOGLE_CREDENTIALS: ENC[AES256_GCM,data:ucHNQ0fygvZnOfa5k3J1zO99122malyCB1aDDwTSOuQZsUHrnCg5FYsxJ5RgTl5mbpityJjKQtHLcMetRVLQ/S2/aPjhbx+OgYLmlFMnJR/NmqEQzuwe8YsPSjOg9NCqucE7BBfbKItQ/PD0+x6LRKxeMO/gr6BXTkL4dwPinCV9IgVZU2CEAdbvcGvxBGKWZ0mHEIvdznN3dK2OL27K+Q+VXZDhzUPfhR5V7u5WUu+PG0QsSby/lZNYdAHpxfkS0YjfTCpukCB0Zvix6cUaorA+BHNYA1qV3nzHDuHHdR6byoGYXMWU209RVG2HKiNAQRlmTsbjr641OWHustGo+Ann9Rv2pZ7r6Q08p0M8cvn5o42TB5H3lv72Iiiru2aV9ZQHpzy1PG4PH9vfebowO2VbZ/wKpRWvc6cpPrAQfoXZCLlaiLoJ1KaVZb9Br8vjlGCHCFzytb61R8BT36M687fRerwVQSu6F5cRzfW6W3Jh5Ufo31wYqdEe2XdN0f/jDG1w0rF2xX4oScgXI/uaVrJ3zCYlVkdbnQjSmu059SmRRkfYJqri+hyDilWKKDPK3+qmBbyeJ+lxcNNugksRMfq5ms5HV70JoUCbiEQF5KzsTCw9s7O4xFYjP5ELXgwHqQbXeOxMS4+GAaN9k+p4owLkVRkO3PkWHBvs+aqCBQvsdZW8XjikNeHWHTZLq4DpOuBrtpvwID5bEPqN70ah1hU9gc9oXC4ArUU5QlqGxNkzdea/cymO7W+0JeRKKRW4G984b1xbLjjjL0Zx8VCDDDx7BTd+DrBsJUZ2CO+u2XIZX6+LNqPt9n/DQUARv13KM4hUmugjhZxx0f0mro1bXMfEfbs8micuk7qDQtKVkRApUlCEb+kEVDXMQUeetLQfUhU7c23QA8tMBHz2IoEmY5yf6CI5PfcUSI9Oldez4+JlCFPf3a2UyVR0+4VITYdBFW0dKLhjR/eYNOh3kEYERksALRYoS2XtLLquHSxRgAPki62RQOrZclfdi1+IGT17l+PyoSKOusIUkHd/y0jUHBSIBjYAZrqZ2WolYpwTMzE3hz3lMys9RbrJV653QgLCndWDXD7k+imXsyyYy1me/JHssYu6ZZ7EsPTGNezdG1/9j7X1VHYKw0DMsYXwIVEI+7/flwHExVTT10+gO+WIVnf7QdUpvsqJNsniCS82Rq9tRhIwLIiePOdV1azMBVqbI8LUBm/E9iFW+iYEuezLL5YsQFvItrNuH5wwhdFNiWrdBWz7klxb0R61/EBPPr6UvVf5d5WyCYonn9UUmUfQrzhgplfr3Qk8a5IAoaT1SW+iS1e15O8PsARGKy/AcX+mTf2v1qgiPnk5IucsZGlH9dwl2gIBbJ8d8SZPl4mGjW9UMesyZxoBTyY1OOYZ3NQSCZnvEggiI+BpIt35nwMLf71LZrXJOY/Q/s2eQBih4FsNb32wlEFcfpxrvT42deh1fatDTZP9NLldbyXWJkyxdAiziUiFtTM3rBly6mNG5hBsbKQpjzN94Tb/9oMD9RbCBcyj1e96DAsRNamqkPlRecZtdh9OuAuq2EjfsEFE9sodtbdxHwDdnR40SElw4jrJ0Ga0QNq04FmXMP86dz/h+MRUO3ldDvax/kjAjR5MRJZXr117sW6fuls268Qo14zdL/nbd9jYdHn9hheCAdM0FvohuCXSWgvoVcpKKCD8kKvgIaXvaFxxAKqWisrxTbtSFoflYzMjaP7L3lY5LlcadTSb+KJl5RVY3ERuUnirMrxVKYRZFsug+Smr5hlcbup/wEESv2aFp3JkEqQxfGxD+g3tHwW9U3KvJ7iv/GQFsikZ2UfLJ0EVAuiVu4Dv25CcxlWx/NGJ3e/FC9nWLgpwnzezsuithZZXci2bH4NvnDxA669Jq3LjwTcKMguEbWOuDBDlO9JcvZsa+0vlQKzZmrzirwLIi8hXQyTeKaHk1ahcblbaZGYoI3E13u7DiGvaQ/+4Y65UFtGusR/NmXG1B4NrGb/EG3mc60JFfIi6CBhtugwEdGYGDkymgHiIM/fb6b8UHXV/kcfnTsGweH2Lc7sR5eMP5a8D7O2P4uvwp3r2NRLwrrGunOARpo835Kx/ZCheS5XCDuiy+LEaTHjDD5cDl2RlgheMjVAcXF/pc3+Q+JBccKdw1D/aN8tYTsPiJiCW6bAOVLwOm1vaa9ecQiNxnx8gym4bYERrx+hlDExS/I+xtmmdJe/s5EqGrVImZjXSkb3LON+ktATByoQVpNjsBpVfnyR7rFcz/UdLDIX3l9IS1cZhIBtkqGSylbOXMUEhhq70qcHpuNTpGxGP30K1c5A63BQB0hpM/wyV629WHUYD1SM48niVT36wVxj356secwI6JVA3hlZp5t7F/KcIuAG1WVy/GUf+cKBWNSnniRLUJdBXSXNamG5o+CN0AXvDNyP9I/xQsrbWzPC5eBNqRL0MEJsfZlWFR6fyI+5UyLlfIh0rwYTz7BK9l1wq1TRV7YqiFpALObtGVxclfKxdFymWwEfjUCwVqw8RAETGqOwfVLvvDlZp/RGup8M2ghsql1Uu/BlrCAoaiJbF8eeuIwNx6K/sq5P8RIFXRZBmWMqewg93Ea/hUIAhubz4fZtHvlh1mbOPWUkzlPCyBPd3uM93mw6wCp6xQM3DbZfQRnKD8HPr6+CFPIn1YGZ1oftBM9mEpH7PtpwJWwDA+Wdl1VbBjUjFG4XVdWxsqbS0W7pePEW1rnqv/bGuPHctEUAXN9IGKSW953T4ChFgYXGcNEURtkknE7bMlQSDZvLS/vtn89aaaL8gnfBF8NvFkB8uQSkQfn/CvQ00GdlhI1l0CyV0+xMrerUw+20nlhC7C7aNKsyx7S8HatUIEkvD6/XPRNoGa9M6w+81JYWbbDF6DFljiiNKtWmv5BkVhWFTwimEOmqKQc6DmXbvtGqH7Pts7PXS/5k83D8eV4kPHuiKNwdU0wsdo+PGSLP+OMWF3Ygbb71Bqq6dwnfciXQi/6yCUv3WdQf2MbMzyqEy9ugXeWI4uvbh+/u5HHzhwMTzhZVoGgwXuyX+nCFPCsqdzqzn8s+TSoU=,iv:nLXpwEqTJaQnZlE0CiQl+hWlBIJGSa8tEUebXpkMWec=,tag:MZ/FJeksYkqCsLaELJDq0A==,type:str] sops: age: - recipient: age1xrpa9ujxxcj2u2gzfrzv8mxak4rts94a6y60ypurv6rs5cpr4e4sg95f0k enc: | -----BEGIN AGE ENCRYPTED FILE----- YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCa25HZSt1b05HTkxoWU41 anB3cm9MWGdRZkNhNENOd2RpSXdMS3ZKMjNVCkxDVEZqQ3U2YnlDdk85V29zQTdw bUZOWnJPUFFZeS8yWmlaWjcxQzNwUGMKLS0tIGdydkVBbXdrQnNMejJZMjN5VS9r TnhVWjdiWTFjN1RDTjVJVm9NNFl2TU0KAYNYtgdxBjEBJYRtT4dyISPPP8U0yXBn zTKgwZePoa62YO1hM/yweMxsmdWxLg1yI4oVWltGwuIgd+WNOQivHQ== -----END AGE ENCRYPTED FILE----- lastmodified: "2025-05-15T11:14:12Z" mac: ENC[AES256_GCM,data:btPhRw73wNtyNJKYXh8hMjiARs2uXUZDf3rtjBqmYUigXqRj+8binPWlySqHnccKwf9HBUWTp73++Fzuei+U5yGfk4pC8CXdZTDX5t6sLNLEWSjbdaiiT2dw6yqVp5O0yerp4pjRRf6Kl4QgGYgYEG6z/kDIbi7t05nm4FLcXpU=,iv:RRS84CcxvSjyDGwVWCOLXa6PUrfFumR4CT5oa9M5SEs=,tag:3ZJYfMcAUqzUGfu/gK5oNg==,type:str] pgp: - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4D/dYBJRlWfQISAQdAgHuuKQrCedfoxAHg5zxM6E0s11EzwU0SXbu8ETcFFyIw Q1EDd0T/ZW4VnjIDto8kUC0KMtUvrvR1s3FCG9mx2ZKMvIXwTHqMkLomjaXRHn4v 0l4BrQKkyX5Bm4mCzg8KC2e+JsvNdwNnkMJQHhU45q5TgBDV9FTA7Dri5IpNYt1L AyGgUis+oRy4LBIN2vtjr5mV8nN71yWo6fpP0WsAh+ZHMumBiFc2ZklC6u5DIz/F =M1T4 -----END PGP MESSAGE----- fp: 15D5721F5F5BAF121495363EFE042E3D4085A811 - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4D+EORkHurkvgSAQdAQpNEHt3PLkCDNl4vym+srqWPglF8+UG8DL/XPRXgh0Uw SlMoGpu6qinl5bPFSJKbZnuYKj8dWtWoFfK9c1xURgbaqd3mckjbofI/5BrqPJJC 0l4BeyS+LrRZsFr0Ivyh/bI/0NTAw4WjSup6cQKO88XH+Q3Rhm6Kdzv57ZzIMpDd 0gAgFTL7uGz4at5VhZoZLjuVff0jkW0tw38i27jyTgGUXTfJhiNXjJXET3LnUgIQ =OgdO -----END PGP MESSAGE----- fp: CC51116A94490FA6FB3C18EB2401FCAE863A06CA - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4DCsA/BhMt3V4SAQdAXEdFClqD2cSkNVYPqMSenULknpKAhaIgFvDD2j2WI18w bq+Wxcz300c4bZNXROc8+hXNBzXW92MJ8uoNbD7hdefciF0UjcBf61mSQvYl/ueb 0l4B3k56RagBSmwUl8LytAUgF5OzY/6ICpe48d/+rguxBHvcI+KO3jBc6Da4FiAb AGOVk2VdzVIeOBgc/eytAWjRlKYHk3mkXIfMlOYx2LFbFmrex5BWnrgVqoxo3hZU =d+Ec -----END PGP MESSAGE----- fp: 4919F560F0D35F80CF382D76E084A2DF1143C14D - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4DRbry8yWl6IgSAQdAtRcYFWErls6e/h+TrDBuEQ+lECv2bfNEaZpg8yMIvRAw G7Cm4X+lWBgeLD1mgrlF+kPsIxaolnXqM4V4dZAcAttrzffT1ssCtLe7dCRHZvfv 1GgBCQIQ+RzUnceJJToXtV98yJlxWCnu38pM8hL9PCBfFI0XEftKvdlScxRzBK/R 4nAIB29rAf9NHFeYDhIBjBdHQ9ju3T9kY0YH+585jsBCrWjMU+Hvj1JY7bVufrnV ycoN0UtZ9f5Yag== =H+8C -----END PGP MESSAGE----- fp: 11177A43C6E3752E682AC690DBD13117B0A14E93 - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4DvbozpGPUDxgSAQdA/bTj9TEnbetB0jMR6ZNyQwzOJ7tvvPKUPbE7s4qrkmUw GZBOamgGXU0UnnQ1Rk1hJ9rXXe4A6DqUAkOelaS/qUqCzRSNZboEijizAApaXGZe 0l4BbaCTNlLeJyI2jKbnO5hyv2DpHAFkKQ4CJna5KZj9TOrSrxnSkAJHLv0t0q9Z M9weECX8RW3qvR75/H2T/ePfUYbGhB+7NpwN2BlU1wnTj7I9RkPetT8skCPxWY8G =s/Bm -----END PGP MESSAGE----- fp: 5D7964FF2DB426ACB3C3505AA2A702DD5B689F45 - created_at: "2025-05-13T14:07:57Z" enc: |- -----BEGIN PGP MESSAGE----- hF4D44lO7/xrMMASAQdAFbcv3Hu/AdGGSWC6kbPJcqbSlD1cTPX8sg8kb0oCpCcw 2/JLu9kJAEsO35DoyiYdRzBc2UVD26WSKtiNbD0XJOUo1yp/JTZ+xTH3fppE3Enf 1GgBCQIQtHg1HF6qEOfhnGrrW1ZZXEheX6+BPfOnQmN6BUcoevD4RrkbKS/x3mh3 A+y3MrdcaVtIQEWOMS1iuEKgJYeqYq6eOP0Y8B1Bbn3nKGK6G/zS7lqFDLUqWpMf VT6LE5drfEXy5w== =duo7 -----END PGP MESSAGE----- fp: E77ED9F8451E10BD242D07415444881046F92733 unencrypted_suffix: _unencrypted version: 3.10.2 ================================================ FILE: .sops.yaml ================================================ # THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. # # Generated on 2025-05-13T14:08:31Z by kres 5ad3e5f. creation_rules: - age: age1xrpa9ujxxcj2u2gzfrzv8mxak4rts94a6y60ypurv6rs5cpr4e4sg95f0k # order: Andrey, Noel, Artem, Utku, Tim, Andy pgp: >- 15D5721F5F5BAF121495363EFE042E3D4085A811, CC51116A94490FA6FB3C18EB2401FCAE863A06CA, 4919F560F0D35F80CF382D76E084A2DF1143C14D, 11177A43C6E3752E682AC690DBD13117B0A14E93, 5D7964FF2DB426ACB3C3505AA2A702DD5B689F45, E77ED9F8451E10BD242D07415444881046F92733 ================================================ FILE: .textlintrc.json ================================================ { "rules": { "one-sentence-per-line": true }, "filters": { "comments": true } } ================================================ FILE: ADOPTERS.md ================================================ # Adopters A list of adopters of Talos Linux, and the use ## Adopters (listed alphabetically) - **[Ænix](https://aenix.io/)** Ænix provides consulting services for cloud providers and uses Talos Linux as base disto for free PaaS platform [Cozystack](https://cozystack.io) for running managed services, virtual machines and Kubernetes clusters. - **[DreeBot](https://dreebot.com)** DreeBot is a cloud host for on-demand multiplayer game servers. Talos is an integral part of our infrastructure, easily and efficiently managing all of our enterprise tools and services so we can keep our developers focused on development. - **[Equinix Managed Services NL](https://www.equinix.nl/services/managed-services/netherlands)** Equinix Managed Services NL offers various infrastructure services, amongst which [EQAP](https://www.equinix.nl/services/managed-services/netherlands/application-platform), which is a managed Kubernetes, based on Talos Linux. - **[Lofty](https://hirelofty.com)** Lofty makes amazing custom software for our clients that our employees are passionate about. Lofty's specialization in software development around GIS/geospatial applications, field data collection, and data science operations makes Lofty an ideal partner for our key industries. Lofty is using Talos as a secure and portable base Operating System for deploying our applications. - **[Mynewsdesk](https://mynewsdesk.com)** Mynewsdesk provides a SaaS product for PR work. In 2022 they created [Reclaim the Stack](https://reclaim-the-stack.com), a Kubernetes based open source deployment platform to replace Heroku. They are now running their application on this platform using Talos Linux on Hetzner bare metal servers with 30+ nodes in the cluster. - **[Nedap Security Atlas](https://nedapsecurityatlas.com)** Nedap Security Atlas does all on-premise development, unit and integration testing in Gitlab running on Talos clusters. - **[Oceanbox.io](https://oceanbox.io)** Oceanbox leverages talos to deliver oceanography as a service. It is used on-premise in a rook-ceph storage cluster, and to host frontend and supporting services for the HPC cluster. - **[Redpill Linpro](https://redpill-linpro.com)** Redpill Linpro uses Talos Linux for both internal and customer specific Kubernetes services. It is for example used to provide a triple-site platform for Norway's biggest online publisher. - **[SCHULZ Systemtechnik GmbH](https://schulz.st/en/page/schulz-systemtechnik)** SCHULZ is a provider of integrated automation solutions for a wide range of industries. We use Talos as a base for our edge devices that run IoT and ML applications. - **[SenseLabs](https://senselabs.de)** SenseLabs offers custom cloud-native geo applications and uses Talos-backed clusters for development and testing. - **[Sidero Labs](https://www.siderolabs.com)** Sidero Labs uses Talos to build & test Talos! All the unit and end-to-end testing happens on our CI platform running in Talos-backed Kubernetes clusters. - **[Nexxen](https://www.nexxen.com)** Nexxen is the flexible advertising platform that connects data to deliver desired outcomes. - **[TrueFullstaq](https://truefullstaq.com)** As a leading cloud native consultancy, TrueFullStaq designs, builds, and manages scalable and secure custom infrastructure for their clients. They have adopted Talos Linux as a foundational component for their managed Kubernetes offerings, leveraging its immutable nature, API-driven management, and minimal footprint to deliver highly secure and easily maintainable Kubernetes environments. By building on Talos, TrueFullStaq ensures their customers benefit from a declarative, robust, and production-ready foundation for their cloud native journey. - **[Vandebron](https://www.vandebron.nl)** Vandebron is a Dutch green-tech energy company on a mission to accelerate the transition towards 100% sustainable energy, 100% of the time. We use TalosOS on our edge devices contributing to stabilize the Dutch energy grid with curtailment and flexibility services. ## Contributing If you use Talos Linux and want to add your organization to this list, simply [edit](https://github.com/siderolabs/talos/edit/main/ADOPTERS.md) this file using the template below, then submit the changes in a pull request! ### Template - **[ORG NAME](ORG URL)** ORG DESCRIPTION & TALOS USE ================================================ FILE: CHANGELOG.md ================================================ ## [Talos 1.13.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.13.0-alpha.2) (2026-02-25) Welcome to the v1.13.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Clang built kernel and ThinLTO Talos now uses a kernel built using Clang compiler, and optimized using ThinLTO. This should bring a small performance improvement, alongside some hardening features, such as BTI on supported ARM systems. ### talosctl debug Talos Linux now provides a way to run and attach to the privileged debug container with a user-provided container image. The debug container might be used for troubleshooting and debugging purposes. ### Environment Configuration Document A new `EnvironmentConfig` document has been introduced to allow users to specify environment variables for Talos components. It replaces and deprecates the previous method of setting environment variables via the `.machine.env` field. Multiple values for the same environment variable will replace previous values, with the last one taking precedence. To remove an environment variable, remove it from the `EnvironmentConfig` document and restart the node. ### External Volumes Talos now supports virtiofs-based external volumes via the new [ExternalVolumeConfig](https://www.talos.dev/v1.13/reference/configuration/block/externalvolumeconfig/) document. These virtiofs external volumes are not supported when SELinux is running in enforcing mode. ### Extra Arguments accept slices in addition to strings Several Talos configuration fields that previously accepted single string values for extra arguments have been updated to accept slices of strings as well. This includes fields such as `.cluster.apiServer.extraArgs`. BREAKING: If you were relying on the resources EtcdConfigs, KubeletConfigs, ControllerManagerConfigs, SchedulerConfigs or APIServerConfigs, the protobuf format has changed from `map` to `map`. ### Talos Imager Enhancements Talos imager now supports running rootless. `--privileged` and `-v /dev:/dev` are no longer required. ### Image APIs Updated Talos Linux provides new APIs to manage container images on the node: listing, pulling, importing and removing images. The new pull APIs provides pull progress notifications. The CLI commands `talosctl image pull`, `talosctl image list` and `talosctl image remove` have been updated to interact with the new APIs. ### Talosctl images k8s-bundle subcommand accepts version parameter The `talosctl images k8s-bundle` command now accepts an optional version overrides arguments. ### Kubernetes server-side apply Talos now uses inventory backed server-side apply when applying bootsrap manifests (including `extraManifests` and `inlineManifests`). Purging of unneeded manifests is automatically performed. The switch and inventory backfill is automatic and no action is needed from the user. ### KubeSpan Configuration A new `KubeSpanConfig` document has been introduced to configure KubeSpan settings. It replaces and deprecates the previous method of configuring KubeSpan via the `.machine.network.kubespan` field. The old configuration field will continue to work for backward compatibility. ### KubeSpan Advertised Network Filters KubeSpan now supports filtering of advertised networks using the `excludeAdvertisedNetworks` field in the `KubeSpanConfig` document. This allows users to specify a list of CIDRs to exclude from the advertised networks. Please note that routing must be symmetric for any pair of peers, so if one peer excludes a certain network, the other peer must also exclude it. In other words, for any given pair of peers, and any pair of their addresses, the traffic should either go through KubeSpan or not, but not one way or the other. ### LinkAliasConfig Pattern-Based Multi-Alias `LinkAliasConfig` now supports pattern-based alias names using `%d` format verb (e.g. `net%d`). When the alias name contains a `%d` format verb, the selector is allowed to match multiple links. Each matched link receives a sequential alias (e.g. `net0`, `net1`, ...) based on hardware address order of the links. Links already aliased by a previous config are automatically skipped. This enables creating stable aliases from any N links using a single config document, useful for `BondConfig` and `BridgeConfig` member interfaces on varying hardware. ### Negative Max Volume Size Negative max size represents the amount of space to be left free on the device, rather than the size the volume should consume. For example: * a max size of "-10GiB" means the volume can grow to the available space minus 10GiB. * a max size of "-25%" means the volume can grow to the available space minus 25%. ### Flannel CNI with Network Policy Support Talos Linux now supports optionally deploying Flannel CNI with [network policy support](https://kubernetes.io/docs/concepts/services-networking/network-policies/) enabled. The network policy implementation is [kube-network-policies](https://github.com/kubernetes-sigs/kube-network-policies/). To enable Flannel CNI with network policy support, use the following machine configuration patch: ```yaml cluster: network: cni: name: flannel flannel: kubeNetworkPoliciesEnabled: true ``` (If the cluster is already running, sync the bootstrap manifests after applying the patch to deploy the new CNI configuration.) ### Container Image Decompression Talos now ships with `igzip` (amd64) and `pigz` (arm64) to speed up container image decompression. ### ProbeConfig The TCPProbeConfig configuration document allows to configure TCP probes for network reachability checks. This allows to define a custom connectivity condition. ### /proc/PID/mem Access Hardening A new kernel parameter `proc_mem.force_override=never` has been introduced by default to enhance system security by preventing unwanted writes to protected process memory via `/proc/PID/mem`. If the kernel parameter is removed, default behavior is restored, allowing access only if the process is traced. ### Reproducible Disk Images Talos disk images are now reproducible. Building the same version of Talos multiple times will yield identical disk images. Note: VHD and VMDK (Azure and VMware) images are not currently reproducible due to limitations in the underlying image creation tools. Users verifying reproducible images should use raw images, verify checksums, and convert them to VHD/VMDK as needed. ### ResolverConfig The nameservers configuration in machine configuration now overwrites any previous layers (defaults, platform, etc.) when specified. Previously a smart merge was performed to keep IPv4/IPv6 nameservers from lower layers if the machine configuration specified only one type. ### Service Account Issuer configuration In API Server, passing extra args with `service-account-issuer` will append them after default value. This allows easy migration, e.g. by changing `.cluster.controlPlane.endpoint` to new value, and keeping the old value in `.cluster.apiServer.extraArgs["service-account-issuer"]`. ### `talosctl images talos-bundle` can ignore reaching to the registry The `talosctl images talos-bundle` command now accepts optional `--overlays` and `--extensions` flags. If those are set to `false`, the command will not attempt to reach out to the container registry to fetch the latest versions and digests of the overlays and extensions. ### Component Updates Linux: 6.18.13 containerd: 2.2.1 etcd: 3.6.8 CoreDNS: 1.14.1 Kubernetes: 1.36.0-alpha.1 Flannel CNI plugin: v1.9.0-flannel1 Flannel: 0.28.1 LVM2: 2_03_38 runc: 1.4.0 systemd: 259.1 cryptsetup: 2.8.3 Tenstorrent: 2.7.0 iptables: 1.8.12 Talos is built with Go 1.26.0. ### VM Hot-Add Support Talos now includes udev rules to support hot-adding of CPUs in virtualized environments. ### Contributors * Andrey Smirnov * Mateusz Urbanek * Noel Georgi * Dmitrii Sharshakov * Orzelius * Laura Brehm * Edward Sammut Alessi * Max Makarov * Andreas Freund * Artem Chernyshev * Bryan Lee * Fritz Schaal * Justin Garrison * Mickaël Canévet * Nico Berlee * Pranav Patil * Alexis La Goutte * Andras BALI * Andrei Kvapil * Birger Johan Nordølum * Camillo Rossi * Christopher Puschmann * Daniil Kivenko * Dmitrii Sharshakov * Florian Ströger * Gregor Gruener * Jaakko Sirén * Jan Paul * Jean-Francois Roy * Joakim Nohlgård * Jonas Lammler * Lennard Klein * Matthew Sanabria * Michal Baumgartner * Olav Thoresen * Serge van Ginderachter * Skye Soss * Spencer Smith * Sébastien Masset * Tim Jones * Utku Ozdemir * arita * dataprolet * drew * eseiker * greenpsi * lmacka * pranav767 ### Changes
221 commits

* [`009f0d6ca`](https://github.com/siderolabs/talos/commit/009f0d6ca0cf13e5778a7c46587ac0dc9d30d5e9) chore: update pkgs * [`ba56b0295`](https://github.com/siderolabs/talos/commit/ba56b02954fb275f8ff2ed20e38b51a75c3a8371) feat: include hid-multitouch.ko kernel module in rootfs * [`ae29a0dcc`](https://github.com/siderolabs/talos/commit/ae29a0dcce527b90553b25230abbb5a8d4bd504c) feat: update Linux to 6.18.13 * [`7cf1de279`](https://github.com/siderolabs/talos/commit/7cf1de2794a1d4838efca378aff433fad5e1823c) fix: bring in new version of go-cmd and go-blockdevice * [`c8800b41e`](https://github.com/siderolabs/talos/commit/c8800b41e511ce6bb4dda3e28b69c4d091177435) fix: update path handling on talosctl cgroups * [`0a7b6eb2c`](https://github.com/siderolabs/talos/commit/0a7b6eb2c98979aa8a604f677c4dd1d54f1285e5) chore: test extensions * [`8b1c974a2`](https://github.com/siderolabs/talos/commit/8b1c974a2a733c870f371ccb7a86ccc616dbc7ea) refactor: drop termui-widgets library * [`5baa0028e`](https://github.com/siderolabs/talos/commit/5baa0028e65765fc0fd1179f72377bf2a2085deb) fix: add owning inventory annotation to talos manifests * [`d3e793d14`](https://github.com/siderolabs/talos/commit/d3e793d14117891103ca4df8507124b18913a56c) fix: stop Kubernetes client from dynamically reloading the certs * [`6a5a0e3bd`](https://github.com/siderolabs/talos/commit/6a5a0e3bd4197a4fadfcfe094876e46d4b878a0a) feat: support pattern link aliases * [`9758bd4fe`](https://github.com/siderolabs/talos/commit/9758bd4fe0e28803acf11f3b9c9da744883aa9dc) feat: update Go to 1.26 * [`e00aed0f6`](https://github.com/siderolabs/talos/commit/e00aed0f6694bb3c8e14a0ef413ef0e62ae02981) feat: update Kubernetes v1.36.0-alpha.1 * [`f20445ad0`](https://github.com/siderolabs/talos/commit/f20445ad0981175d6444340325af5fc747993559) chore: improve logging of disk encryption handling * [`f018fbe7b`](https://github.com/siderolabs/talos/commit/f018fbe7ba145ff86ebe0d4d09b323b9715ef1a9) fix: handle raw encryption keys with `\n` properly * [`e5b0eb017`](https://github.com/siderolabs/talos/commit/e5b0eb017ff989e812d6444f668bf17723bb7ec4) fix: hold user volumes root mountpoint * [`8a0e79774`](https://github.com/siderolabs/talos/commit/8a0e79774409ce7605f9cd21d769f47e5db656db) refactor: split locate and provision * [`a59db0e92`](https://github.com/siderolabs/talos/commit/a59db0e92213296c4c9599fb0d230908caabdf30) fix: improve OpenStack bare metal network configuration reliability * [`659009ad8`](https://github.com/siderolabs/talos/commit/659009ad875c0625ac24094dc44020b015ab8b50) fix: remove stale endpoints * [`dab0d4783`](https://github.com/siderolabs/talos/commit/dab0d478378dfc6c2862c38633ca4494a41e7ecd) fix: allow static hosts in `/etc/hosts` without hostname * [`45f214154`](https://github.com/siderolabs/talos/commit/45f214154cea364d86bfbba81a5ad4f272a4c8fd) feat: update go-kubernetes to use new Myers diff * [`35ad0448c`](https://github.com/siderolabs/talos/commit/35ad0448c9ae93cd642d80ebb7d95b768ba0ab9b) fix: switch to better Myers algorithm implementation * [`0048464be`](https://github.com/siderolabs/talos/commit/0048464be854d94fb607e38daa83e00767fe8cbc) feat: update etcd to v3.6.8 * [`5df10f260`](https://github.com/siderolabs/talos/commit/5df10f2604b537504f76b14e028f88a946aacbd7) fix: use mcopy instead of diskfs to populate VFAT * [`ce53ffa90`](https://github.com/siderolabs/talos/commit/ce53ffa900a438f6669460a2ce9af874c1f87708) fix: disks flag parsing and handling in create qemu command * [`3bd3dd7ca`](https://github.com/siderolabs/talos/commit/3bd3dd7ca92401312079e37584bfbf7942eab93a) fix: memory overuse in imager VFAT * [`f118ee47e`](https://github.com/siderolabs/talos/commit/f118ee47eaba662dc161d37fae5ae8f2b3de9819) fix: read multi-doc machine config with newer talosctl * [`70c6c2154`](https://github.com/siderolabs/talos/commit/70c6c2154e87d4a6748aebdfa2c50cbc97a0dd89) feat: add filter for KubeSpan advertised networks * [`daf18abf4`](https://github.com/siderolabs/talos/commit/daf18abf419b21a6e70dcca0b5b83d33cfee6188) fix: fix talosctl debug in enforcing mode * [`33b5b2565`](https://github.com/siderolabs/talos/commit/33b5b25652360a114d0b2cea412bf018cbf84df3) fix: ignore volumes in wave calculation without provisioning * [`a16392559`](https://github.com/siderolabs/talos/commit/a16392559a488993c3e26810df57da3cae5c24c5) feat: add explicit service account support to Talos client * [`4d531884e`](https://github.com/siderolabs/talos/commit/4d531884e9c28d480f24b61a83f140df0ffbe4b3) chore: update dependencies * [`406b8c83c`](https://github.com/siderolabs/talos/commit/406b8c83c9b33b1917b9dd16aa1efeb2df189f0f) feat: update doc links to docs.siderolabs.com * [`87615f551`](https://github.com/siderolabs/talos/commit/87615f551183cd322dafebf368a347d928a14442) feat: implement network policies with Flannel CNI * [`6995bc1b1`](https://github.com/siderolabs/talos/commit/6995bc1b1ea54e1a8fd6426fef11293f35106ac7) chore: update homebrew formula on release * [`7942d5a98`](https://github.com/siderolabs/talos/commit/7942d5a98c1d689a94e78219be09a0fc69d07b08) fix: image gc controller config * [`52e8727d0`](https://github.com/siderolabs/talos/commit/52e8727d0112967a62a3d9ae6bf26d713db242e1) feat: add IPv6 GRE support * [`9690dbad0`](https://github.com/siderolabs/talos/commit/9690dbad02cfc8682d697679b655e753039c5254) chore: bump tools (including linter) * [`2628eb2ec`](https://github.com/siderolabs/talos/commit/2628eb2ece05d7f817fc42e12b979d3f8ca9710c) fix: typo with rpi_5 profile name * [`d5ebcd7ca`](https://github.com/siderolabs/talos/commit/d5ebcd7cae1a20c8000e2f4d5a02c81e4dbe5186) fix: stop building talosctl debug on Windows * [`8b85c7c63`](https://github.com/siderolabs/talos/commit/8b85c7c637cc08d35bbf6968abebb8c4cdfb82ad) chore: update deps * [`d905035b5`](https://github.com/siderolabs/talos/commit/d905035b5e5c7787a5171ba2e0127c89755e8774) fix: swap volume configuration for min/max size * [`d43a01ccb`](https://github.com/siderolabs/talos/commit/d43a01ccbdd318080b54e52d2f2fbec93042c458) feat: implement `talosctl debug` * [`34a31c979`](https://github.com/siderolabs/talos/commit/34a31c9797d5a7e1700c3d945a21367b81c79385) feat: add mount options support for existing volumes * [`1bf95eed1`](https://github.com/siderolabs/talos/commit/1bf95eed185152c38397cd3b43b6ff9d421678c5) feat: improve dashboard uptime display * [`055add7ae`](https://github.com/siderolabs/talos/commit/055add7aeb158b6f4e09ef06966de7622d1b3940) release(v1.13.0-alpha.1): prepare release * [`900516e68`](https://github.com/siderolabs/talos/commit/900516e68950e4b94696f6a9b481cefee44b3360) chore: update image signer * [`938de566e`](https://github.com/siderolabs/talos/commit/938de566eca30af3cc4355a94931186f19b682f2) feat: bump kernel * [`388cec727`](https://github.com/siderolabs/talos/commit/388cec72796d0ecd0c7103efcaab9066e9b62509) feat(overlays): add new overlays * [`9f2dd6312`](https://github.com/siderolabs/talos/commit/9f2dd6312f9d49e4d03347c98b100119f94cf807) refactor: api tests * [`a90783146`](https://github.com/siderolabs/talos/commit/a90783146fc2d475055bfce0f8b5120969f74dc7) feat: add a helper module to generate standard patches * [`1fec5b23d`](https://github.com/siderolabs/talos/commit/1fec5b23d0c10e53863a7c0f89f862708a7f4069) fix: implement merger for PercentageSize * [`8b245b8f2`](https://github.com/siderolabs/talos/commit/8b245b8f269b6c8cb463f2cf537d2ed2ab6924ec) feat: implement new image service APIs * [`d90c775b8`](https://github.com/siderolabs/talos/commit/d90c775b8441705003de3427b2e6831dcbfb449f) chore: rename internal `talosctl debug air-gapped` * [`2165280d0`](https://github.com/siderolabs/talos/commit/2165280d0eedf59899ad44e2f3289d81b3dab466) refactor: change the way one2many proxying is picked * [`b1b703dbe`](https://github.com/siderolabs/talos/commit/b1b703dbe2b25785ded0c77f23d674d9b9934975) chore: move sync logging code to go-kubernetes package * [`e48c6d7ab`](https://github.com/siderolabs/talos/commit/e48c6d7ab9c8a2e28ebe2115ac09f1557bbcca33) fix: allow to expose a port multiple times in Docker * [`410d8cb57`](https://github.com/siderolabs/talos/commit/410d8cb5727ccf054c9097f33bc916d87076a599) fix: undo CRLF on Windows (talosctl edit) * [`859d3f03c`](https://github.com/siderolabs/talos/commit/859d3f03c444d98b94a06adac3648562e3b1228b) feat: add RPi5 to the list of supported SBCs * [`0bd48bbc6`](https://github.com/siderolabs/talos/commit/0bd48bbc6f365770167ee753be563eb4179fcadb) fix(talosctl): pass --k8s-endpoint flag to rotate-ca kubernetes rotation * [`b9e27ebe7`](https://github.com/siderolabs/talos/commit/b9e27ebe72c4302c416fd8efb007c3966004ddd6) feat: update Linux kernel with dm-integrity * [`6aa9b0677`](https://github.com/siderolabs/talos/commit/6aa9b0677ed7ca4955fead474e36a533b3250ad9) fix: skip empty documents on config decoding * [`494492489`](https://github.com/siderolabs/talos/commit/494492489b29b615a8a874c0648690ed3b9adb58) fix: always set advertised peer URLs * [`782cc507d`](https://github.com/siderolabs/talos/commit/782cc507dc33c87caa5ff985eea5f4439c3e1012) fix: open the filesystem as read-only * [`28e61a740`](https://github.com/siderolabs/talos/commit/28e61a740a906fadfea098f38a9c9f4e8c32773e) fix: set GRUB prefix correctly on arm64 * [`a4f1c5239`](https://github.com/siderolabs/talos/commit/a4f1c5239ef7227856640c230e0d0364d9eedbd2) feat: update GRUB to 2.14 * [`562920701`](https://github.com/siderolabs/talos/commit/562920701e2999cbb6687e55de96719aba4064fd) fix: use node podCIDRs for kubespan advertiseKubernetesNetworks * [`39460365c`](https://github.com/siderolabs/talos/commit/39460365c1726095e20cf3cc7c079c234b8022d6) feat: implement layering for ProbeSpec * [`b5c760f70`](https://github.com/siderolabs/talos/commit/b5c760f7076570bc04be02af0ea493f95d8338d0) feat: add ProbeConfig for network connectivity probes * [`4b274f761`](https://github.com/siderolabs/talos/commit/4b274f76159495cc6c2977ec3bbade71e35aade8) feat: support aws cert manager in imager * [`417209512`](https://github.com/siderolabs/talos/commit/41720951251102f1c174e501a3103e55720a1d8b) fix: fallback to /proc/meminfo for memory modules * [`7f1147bed`](https://github.com/siderolabs/talos/commit/7f1147bed495a06d336f5be1da6073921b5e52dc) fix: add warnings to 802.3ad bond * [`ddd6b186e`](https://github.com/siderolabs/talos/commit/ddd6b186eb8f527324736576182dafbce3423da5) refactor: generate GRUB images * [`c7aa266ea`](https://github.com/siderolabs/talos/commit/c7aa266ea5c9d3fbd465dc651f2ebfec622612e7) fix: overwrite resolver config with machine config * [`cf70f05fa`](https://github.com/siderolabs/talos/commit/cf70f05fa40312c30d8345c2fb15ce8eda86a7a7) fix: oracle platform file format * [`8c7b8f5b7`](https://github.com/siderolabs/talos/commit/8c7b8f5b7d6dec144f7985a7c8a8a582c38f3154) feat: add support for negative max size * [`77bc3d21f`](https://github.com/siderolabs/talos/commit/77bc3d21fa40e188af4b5dd93e1cda289e858d56) fix: marshal of FailOverMac property * [`38e280c93`](https://github.com/siderolabs/talos/commit/38e280c9319ef1ecb1455b3cc8b8d0d1d7426ccd) fix: make OOM expression a bit less sensitive * [`3d1301640`](https://github.com/siderolabs/talos/commit/3d1301640d44d58303160400e4954c36f53341f9) fix: wipe the first/last 1MiB in addition to wiping by signatures * [`1aa6528ad`](https://github.com/siderolabs/talos/commit/1aa6528adcddfb6a5ed66cc26cac1a0fcdb37516) fix: make OOM controller more precise by considering separate cgroup PSI * [`f7072c050`](https://github.com/siderolabs/talos/commit/f7072c050e607de16781a65eb97ab2a1828b05fb) fix: check if the device is not mounted when wiping * [`743c3b94b`](https://github.com/siderolabs/talos/commit/743c3b94b958e4abcbf70d4064f2ae0e0bbb0712) fix: use correct containerd import path * [`f2dd08594`](https://github.com/siderolabs/talos/commit/f2dd08594e8e474c7b3891dc46c64f27c724dbc0) feat: report image pull progress in the console * [`72fe98a06`](https://github.com/siderolabs/talos/commit/72fe98a06f31536454f201d703f8ae6a071235b5) fix: boot with GRUB * [`d4ed13d93`](https://github.com/siderolabs/talos/commit/d4ed13d9394b087e8877eba25950f344894803a1) fix: add talos version to Hetzner Cloud client user agent * [`150c41c30`](https://github.com/siderolabs/talos/commit/150c41c30ed3f066f10bd2bdc2afa9b2c5a97597) feat: update Linux to 6.18.5 * [`01a367891`](https://github.com/siderolabs/talos/commit/01a3678913de0fa4d309a361428c117d24ce0d1e) fix: use append instead of prepend in service-account-issuer * [`d1954278a`](https://github.com/siderolabs/talos/commit/d1954278a1ba3470b2e5ccae90762078c18d69e9) feat: add extraArgs from service-account-issuer * [`91b88f7f9`](https://github.com/siderolabs/talos/commit/91b88f7f994cccad15cbec1aa8019bd19b84ae91) feat: support multiple values for extraArgs * [`96e604874`](https://github.com/siderolabs/talos/commit/96e604874b17e7aa8b62bfb25737f349e539bc5a) fix: add hostname to endpoints * [`7033275a7`](https://github.com/siderolabs/talos/commit/7033275a7a22d51e83c9e760ba37d2ad6ab22f28) refactor: move BootloaderKind into machinery * [`71adaf0ea`](https://github.com/siderolabs/talos/commit/71adaf0ea5b558c8a16e2acfdec3671611455985) fix: sort mirrors and tls configs when generating the machine config * [`34f09a300`](https://github.com/siderolabs/talos/commit/34f09a3004fe1b77c16dd33b04adca95fb6876a5) feat: add VLAN support to OpenStack platform * [`5127ef7c2`](https://github.com/siderolabs/talos/commit/5127ef7c28b360f9c7c033f77c58cef729e5278d) fix: wipe disk by signatures * [`415bfaedb`](https://github.com/siderolabs/talos/commit/415bfaedb6ae8d42b5927fdc5b7cfe8aa781a791) fix: panic in configpatcher when the whole section is missing * [`e5aca71cd`](https://github.com/siderolabs/talos/commit/e5aca71cd0557557e50c39d82eda2c938f627d62) fix: fix healthcheck timeout * [`634b71e2d`](https://github.com/siderolabs/talos/commit/634b71e2d028bf13d838acad8809c95384b6eed9) docs: move talosctl pcap example to Example Block * [`818492731`](https://github.com/siderolabs/talos/commit/8184927316c5de7d9b04f21474a60cc791c3d26d) feat: implement KubeSpan multi-document configuration * [`4d0604b9d`](https://github.com/siderolabs/talos/commit/4d0604b9d93851f444a00dbd84fcac76d21d35c2) chore: remove unrelated machineconfig * [`e36863470`](https://github.com/siderolabs/talos/commit/e36863470b14496c3d84417e63fef45e6060603b) feat: add it87 hwmon module * [`308c75090`](https://github.com/siderolabs/talos/commit/308c75090774d2510c2ec08e63e179a5c0fa6987) fix: resolve SideroLink Wireguard endpoint on reconnect * [`e4ef494de`](https://github.com/siderolabs/talos/commit/e4ef494decdf97664c4803aa3861015fce49760e) fix: drop the persist config flag from gen config * [`c3176adcf`](https://github.com/siderolabs/talos/commit/c3176adcf981811a326c971c81c4b591f54e116a) feat: add EnvironmentConfig document * [`c839b3880`](https://github.com/siderolabs/talos/commit/c839b38809b3a0029061d43477555ec31e283aa5) feat: expose more SSA options in the upgrade-k8s command * [`b8ff9677e`](https://github.com/siderolabs/talos/commit/b8ff9677e4f9a64908ae00bb1d80aa2442a00a60) fix: handle correctly incomplete RegistryTLSConfig * [`99f2ddada`](https://github.com/siderolabs/talos/commit/99f2ddada895011036af1435dd10bac3be0a9171) fix: bond config via platform * [`2449ffea4`](https://github.com/siderolabs/talos/commit/2449ffea45304459ea8895b535b6f070a9249172) fix: allow HostnameConfig to be used with incomplete machine config * [`35fc52087`](https://github.com/siderolabs/talos/commit/35fc5208728dbc3e0b139aff4c06f25208445637) fix: lock down etcd listen address to IPv4 localhost * [`27253d731`](https://github.com/siderolabs/talos/commit/27253d7317a473cbbc0f5c0eee634173bdd2eda7) feat: use new xfs config file * [`c9d84ae21`](https://github.com/siderolabs/talos/commit/c9d84ae21e203529a6952c165ff04d602a2a6ad6) fix: generate OCI-compliant image config * [`7a4b2b33a`](https://github.com/siderolabs/talos/commit/7a4b2b33abe8a3011f37f0a8f4848dd846d0396f) fix: update VIP config example * [`080efcbda`](https://github.com/siderolabs/talos/commit/080efcbda2c4334f9d8c70804a5a37f0cdb2df2d) feat: add k8s-version parameter to k8s-bundle * [`b764f5f72`](https://github.com/siderolabs/talos/commit/b764f5f724bf8af3acaac74942ea91a86e593322) fix: skip sync test when kube-proxy is disabled * [`70e67787d`](https://github.com/siderolabs/talos/commit/70e67787d6d34d93a34871b2d25d64f6a7575d76) feat: imager: populate filesystems with root owned files * [`7416dca59`](https://github.com/siderolabs/talos/commit/7416dca59378dc282e42ea30107cf40326cc593c) fix: print talosctl images to release notes * [`dc2009e47`](https://github.com/siderolabs/talos/commit/dc2009e4779684a6a4252d4dfd2aa02d1b60c2da) chore: use context when creating filesystems * [`85f7be6e3`](https://github.com/siderolabs/talos/commit/85f7be6e3f14bf160cf32bccf7418b31968d474f) chore: update slack links * [`154952175`](https://github.com/siderolabs/talos/commit/154952175ab73ac65722732b146a0ee1c56b2f4d) fix: disable swap for system services * [`d98b415af`](https://github.com/siderolabs/talos/commit/d98b415afea7b1820153151c0273df24a101742e) fix: drop more non-overlay SBC stuff * [`226cd6bc1`](https://github.com/siderolabs/talos/commit/226cd6bc1d70662cb7f7736ac6fad117170a36fb) fix: do not allocate for the actual disk image file * [`53f5bf8d2`](https://github.com/siderolabs/talos/commit/53f5bf8d2c97e91bee06bcb5948170015486ea77) fix: overlay installers * [`10d0cfd93`](https://github.com/siderolabs/talos/commit/10d0cfd93a083fb8b71b7c0297df52feb55e044b) fix: overlay install in image mode * [`77086694d`](https://github.com/siderolabs/talos/commit/77086694d18b69802e542156fc12cd7cf066efc2) fix: partition data population * [`4d5657b1a`](https://github.com/siderolabs/talos/commit/4d5657b1a34c939b63b2cc3ee11ed45ad1bf23c3) fix: drop SBC board code * [`c4f3f6d3e`](https://github.com/siderolabs/talos/commit/c4f3f6d3e59b58016ba8546c5bd3e8e465fbbf52) feat: implement kubernetes server-side apply * [`f12fd2b0a`](https://github.com/siderolabs/talos/commit/f12fd2b0a9fdf8f53ec5714d3ad18b695973e0b0) test: bump Image Factory tests * [`c76484e58`](https://github.com/siderolabs/talos/commit/c76484e5879a7e48197e442cf22044d3d0363846) release(v1.13.0-alpha.0): prepare release * [`f0d8a6851`](https://github.com/siderolabs/talos/commit/f0d8a685173354e5fd148786872062a342c4282a) test: skip the source bundle on exact tag * [`c57701d65`](https://github.com/siderolabs/talos/commit/c57701d6590388e7d6418af67e8237c7d60ccf54) fix: remove interactive installer * [`43937c1cd`](https://github.com/siderolabs/talos/commit/43937c1cd42758a15026261fe8f0e06daaebdcbd) feat: update Linux and systemd * [`72a194df8`](https://github.com/siderolabs/talos/commit/72a194df88f2800cee3372241fbad419b07f7bbf) feat: add VM CPU hot-add rules * [`f09ae1e0d`](https://github.com/siderolabs/talos/commit/f09ae1e0d2e1b7842d504b594b71a325af7733e5) fix: probe small images correctly * [`8f2b33799`](https://github.com/siderolabs/talos/commit/8f2b337994fdeff76a0ae9e1730b4b9f596ff1bb) feat: imager support rootless builds * [`c7525a97e`](https://github.com/siderolabs/talos/commit/c7525a97ef8615e903be183d7938b6d2a3b89464) feat: support creating filesystems from folder * [`e2bffb5ce`](https://github.com/siderolabs/talos/commit/e2bffb5cebaaf28f9dfff24f41ecbb2809fc60e5) chore: refactor imager code so it's more clear * [`0fb50dbd0`](https://github.com/siderolabs/talos/commit/0fb50dbd0a5b7b80187e50d501cec4b3fe434dc2) fix: invalid versions check in talos-bundle * [`b5dd56032`](https://github.com/siderolabs/talos/commit/b5dd5603207a46d8eed240173f06aeffd6a9c0e7) test: upgrade versions in upgrade tests * [`3dfa4d6e4`](https://github.com/siderolabs/talos/commit/3dfa4d6e40dcae2db47e89443568be3ae48b3ae1) fix: make upgrade work with SELinux enforcing=1 * [`786c8e2ee`](https://github.com/siderolabs/talos/commit/786c8e2ee757c2d7b30d5bded954e584af3a058e) feat: ship pigz/igzip in rootfs to speed up image decompression * [`48d242918`](https://github.com/siderolabs/talos/commit/48d242918bc97e6a01434bee6fcdcfa735fd1f5a) feat: update containerd to 2.2.1 * [`536541afe`](https://github.com/siderolabs/talos/commit/536541afe497d5f61cfcd0c01cf580ab5b3be164) fix: mount volume mount/unmount race * [`39117d457`](https://github.com/siderolabs/talos/commit/39117d45766b139ed6a0c1290f757e4b26d31d92) feat: update dependencies * [`f0f420725`](https://github.com/siderolabs/talos/commit/f0f420725c6a4f628cdc1b80d59713c375beb9b7) fix: bond setting change detection * [`8d6a7a867`](https://github.com/siderolabs/talos/commit/8d6a7a8677a5d1d61432fa94ca030351fd9852f2) feat: update Kubernetes to 1.35.0 * [`845a0d09c`](https://github.com/siderolabs/talos/commit/845a0d09cd770a15db762ddda4d3d27f58656cfe) feat: update etcd 3.6.7, CoreDNS 1.13.2 * [`b95912e04`](https://github.com/siderolabs/talos/commit/b95912e04907b78bd06987c6d3948f8f1804d844) feat: enforce `proc_mem.force_override=never` by default * [`681f3e84c`](https://github.com/siderolabs/talos/commit/681f3e84c85677f49ddbcd4a47e325d4a85af692) test: run virtiofs tests only when virtiofsd is running * [`0592ff0cd`](https://github.com/siderolabs/talos/commit/0592ff0cdbf54475dc91bfb7c9b9c3047bbe13da) fix: drop the Omni API URL check on IP address * [`a4879a5fa`](https://github.com/siderolabs/talos/commit/a4879a5fa2ded9b7b52ff7506b5493ae12939bba) feat: update Linux to 6.18.1 * [`43b43ff18`](https://github.com/siderolabs/talos/commit/43b43ff189b7e5f37eaa75f4926c26ee21ffa5cb) docs: split talosctl commands into groups * [`6d17c18bf`](https://github.com/siderolabs/talos/commit/6d17c18bf908d3cd69ff920d0cff67b653a385f3) feat: enable Powercap and Intel RAPL * [`884e76662`](https://github.com/siderolabs/talos/commit/884e76662af34448d9904372f1256f59ce161f99) docs: fix the talosctl cluster create help output * [`6dc31be4f`](https://github.com/siderolabs/talos/commit/6dc31be4f982f62ba4aeb1b3b4e65ce022447eb4) fix: exclude new Virtual IPs configured with new config * [`94905c73e`](https://github.com/siderolabs/talos/commit/94905c73e93fd7dac38d911dc4264e4d0fe0081d) feat(talosctl): support running qemu x86 on Mac * [`f871ab241`](https://github.com/siderolabs/talos/commit/f871ab241c0f034401fbf61e32e7201cced49441) fix: provide json support in `nft` binary * [`694f45413`](https://github.com/siderolabs/talos/commit/694f45413fec8cc4f58a79e76034bd4bcec2bbdf) feat: external volumes * [`39feb16d2`](https://github.com/siderolabs/talos/commit/39feb16d2ed3bcb65d66483c0729bcec29f7b93e) fix: update containerd 2.2.0 with cgroups patch * [`82027eb9b`](https://github.com/siderolabs/talos/commit/82027eb9b30aa128099b27f638098d78857ecb4b) fix: bond configuration with new settings * [`121b13b8f`](https://github.com/siderolabs/talos/commit/121b13b8f8d6e5a487971f727c6e028c7ffa20f3) fix: disable kexec on arm64 * [`7eaa725d0`](https://github.com/siderolabs/talos/commit/7eaa725d0dba18392279f5b43d167aaf18f43b99) fix: selection of boot entry * [`949bdb90a`](https://github.com/siderolabs/talos/commit/949bdb90ab2fd711c47583d96bd29a1ca90bbf41) feat: add Secure Boot to CloudStack platform config * [`798143a88`](https://github.com/siderolabs/talos/commit/798143a886e4055e764a9ad17cefe8ad4db0572e) fix: discard better klog message from Kubernetes client * [`008cd0986`](https://github.com/siderolabs/talos/commit/008cd0986cbbbd5527d91c01b951e311ba014b97) fix: disable kexec in talosctl cluster create on arm64 * [`bb62b29ed`](https://github.com/siderolabs/talos/commit/bb62b29edb2fb704846ceeed2019f0ebaced30be) chore: prepare talos for 1.13 * [`c0935030a`](https://github.com/siderolabs/talos/commit/c0935030ac3d966149591a3aaa8e430da768d678) chore: fork reference docs for 1.13.x * [`e387e48b3`](https://github.com/siderolabs/talos/commit/e387e48b30b3a3b991f1f611099f48fddefa851b) fix: do not override DNS on MacOS * [`1e7e87fb1`](https://github.com/siderolabs/talos/commit/1e7e87fb192521937b581ecd94a0aa0c861f2a5f) fix: rework NFT rules for KubeSpan * [`51bcfb567`](https://github.com/siderolabs/talos/commit/51bcfb567915d2b27e4b5321e080220bc618086b) feat: rename image default and source bundle * [`585abe944`](https://github.com/siderolabs/talos/commit/585abe94431f06b3ebf4b6a64ad1b5918708f866) feat: update Kubernetes to v1.35.0-rc.1 * [`f301e3e9b`](https://github.com/siderolabs/talos/commit/f301e3e9ba47d5f46f1990a9bd21fd4e671c38f3) fix: update KubeSpan MSS clamping * [`74c1df6f4`](https://github.com/siderolabs/talos/commit/74c1df6f4b2ac8d989d1e42d6c7c0016411638ee) test: propagate MTU size to QEMU in `talosctl cluster create` * [`d347ca1af`](https://github.com/siderolabs/talos/commit/d347ca1af162c8d948899d58fc3f76dd0a94f138) fix: update CNI plugins to 1.9.0 * [`e3f8196b4`](https://github.com/siderolabs/talos/commit/e3f8196b4c767ca68df9f6c85ed25c7e12fb4d87) chore: update Grype and Syft * [`e1b8ab323`](https://github.com/siderolabs/talos/commit/e1b8ab3236e956bc4b37e227423aea0f97612a5c) docs: add misssing period * [`cd04c3dde`](https://github.com/siderolabs/talos/commit/cd04c3dde70f604603fd7996c62adf5a17cfbd41) docs: update release notes * [`fc8ae3249`](https://github.com/siderolabs/talos/commit/fc8ae3249fac82cbdb5521ca8797a8451bdaa9fd) docs: add omni join token example to create qemu command * [`9fa00773c`](https://github.com/siderolabs/talos/commit/9fa00773caf2d092d953ff58d04cf94803039b94) chore: update go-blockdevice * [`ba13b6786`](https://github.com/siderolabs/talos/commit/ba13b678654e2896e1a99b1af8b51a9239b0a559) fix: correct condition to use UKI cmdline in GRUB * [`d2ce3f47f`](https://github.com/siderolabs/talos/commit/d2ce3f47f8515231f27983abaaf269a059e2e90d) docs: drop machine.network example * [`cf087c1e0`](https://github.com/siderolabs/talos/commit/cf087c1e01bc1226049a57186f48b2e6b5739c5c) test: bird2 extension * [`13df94388`](https://github.com/siderolabs/talos/commit/13df943884a59bd1d42721ba42bcb36349d40624) fix: adapt SELinuxSuite.TestNoPtrace to new strace version * [`861787c38`](https://github.com/siderolabs/talos/commit/861787c380bff3ba2fa29f49837bc173a2719578) fix: mark secureboot as supported for metal * [`04e3e87ad`](https://github.com/siderolabs/talos/commit/04e3e87adcbd24ee0d82dce4cc27121d34d316f4) fix: clean up kubelet mounts * [`21057903a`](https://github.com/siderolabs/talos/commit/21057903a2ca01d88cc5f97c084567d1981f73c5) fix: clear provisioning data on SideroLink config change * [`0f9f4c05f`](https://github.com/siderolabs/talos/commit/0f9f4c05ffad9413e1f1533c68eae38dc91c9716) feat: update Kubernetes to 1.35.0-rc.0 * [`d4309d7b1`](https://github.com/siderolabs/talos/commit/d4309d7b1aec9d2852173fd704b09dfabe2cf217) fix: add a timeout for DNS resolving for NTP * [`dd6c1089c`](https://github.com/siderolabs/talos/commit/dd6c1089c8f30d815c80ab10544a0fef27ddd14c) feat: update Linux to 6.18.0 * [`e9a30bf9a`](https://github.com/siderolabs/talos/commit/e9a30bf9a8ee55ab9ae5d9c9a18362434b0202ad) test: revert add direct connectivity CA rotation test * [`cc95562bc`](https://github.com/siderolabs/talos/commit/cc95562bc830496986a395cdde352d48d4a1d146) fix: don't disable LACP by default * [`c9fe4679b`](https://github.com/siderolabs/talos/commit/c9fe4679bf9c1dcdf175b95a02f1eaacab4ff085) test: add platform acquire/not valid config unit-test * [`5a03a7a20`](https://github.com/siderolabs/talos/commit/5a03a7a20acffa8eedf40524f8d070e37e41f24e) chore: fix longhorn test * [`a0cfc3527`](https://github.com/siderolabs/talos/commit/a0cfc3527481c4784edf87c3d7823b10a21d1e4d) feat: implement logs persistence * [`51b732bea`](https://github.com/siderolabs/talos/commit/51b732beabc9948e58f9aa4d81b79afb9bd61243) fix: selection of boot entry * [`18f8ac369`](https://github.com/siderolabs/talos/commit/18f8ac369ba52f2640508134d3983f006f698129) feat: update Kubernetes to 1.35.0-beta.0 * [`92fa7c5e4`](https://github.com/siderolabs/talos/commit/92fa7c5e43da96a492003a2c9184cf818fbbb9f0) chore: update pkgs for NVIDIA 580.105.08 * [`f489299b6`](https://github.com/siderolabs/talos/commit/f489299b603a2aff0f292fa941ae8925fdda3492) chore: correct condition for running k8s integration tests * [`ab149750d`](https://github.com/siderolabs/talos/commit/ab149750d475ef059debfc3730e9e0a32ad6e601) chore: update tools/pkgs to 1.13.0-alpha.0 * [`87ff9f860`](https://github.com/siderolabs/talos/commit/87ff9f8606e04fe99e23261418a762372647b077) test: fix the image-factory test to pass IF endpoint * [`2ffe538e7`](https://github.com/siderolabs/talos/commit/2ffe538e7307f0ac3dbac2eba4b36ea98162ec78) test: add direct connectivity CA rotation test * [`70f6b80e0`](https://github.com/siderolabs/talos/commit/70f6b80e03acd507580211724cc51b7867bf8a76) chore(ci): skip multipath extension tests * [`561cfb60c`](https://github.com/siderolabs/talos/commit/561cfb60c313a9bdc70ed2ff2729549bc8c50fcb) chore: update pkgs and tools version * [`2f42202a7`](https://github.com/siderolabs/talos/commit/2f42202a7ccee0e33e43b2081929b5510db5d713) fix: simplify OOM expression * [`7b06ae8c2`](https://github.com/siderolabs/talos/commit/7b06ae8c2cf1069cb77cddee0986afc5af837bcc) test: fix flaky LinkSpec/Wireguard test * [`e715f3871`](https://github.com/siderolabs/talos/commit/e715f387137fa566a4824c051b624e013a93c49f) feat: present kernel log as `talosctl logs kernel` * [`e2ee39b8a`](https://github.com/siderolabs/talos/commit/e2ee39b8ac54ada49dd0a7ffaab4b0ae5d684792) fix: support specifying patch file without '@' symbol * [`e202b1f9e`](https://github.com/siderolabs/talos/commit/e202b1f9e82823aa5b31625024bce65bcc53b29f) fix: trim trailing dots from certificate SANs * [`7f7079f9c`](https://github.com/siderolabs/talos/commit/7f7079f9c0fbb30ce781aa1223d7df1a175a6206) fix: assign value of multicast setting properly * [`eba96141e`](https://github.com/siderolabs/talos/commit/eba96141e0afc147af9a8f1969e207501232b1de) feat: update etcd to 3.6.6 * [`9945ceef3`](https://github.com/siderolabs/talos/commit/9945ceef37b13bc6e93637dcf395a8c9019e60ed) docs: add API Server Cipher Suites changelog * [`9ed488d09`](https://github.com/siderolabs/talos/commit/9ed488d09648c09a9a5c1ed6a5cd245b84cd415d) feat: update TLS cipher suites for API server * [`f1c04e4d6`](https://github.com/siderolabs/talos/commit/f1c04e4d6af14243a328d22bf810f27b13d83898) feat: generate mirrors patch * [`a89108995`](https://github.com/siderolabs/talos/commit/a89108995ff13fbbef0bf5cbf429cede5ff81078) fix: add CA subject to generated certificate * [`35dd612a5`](https://github.com/siderolabs/talos/commit/35dd612a5e59d8781e147fc36eb14f3e8bc66811) fix: add more resilient move * [`83675838f`](https://github.com/siderolabs/talos/commit/83675838f3655b44cbd850fd82b4d17acfb00c33) feat: extend flags of cache-cert-gen * [`80ab7a064`](https://github.com/siderolabs/talos/commit/80ab7a0643fc8057283a8ba3eb912d0ee453c143) chore: remove spammy 'clean up unused volumes' logs * [`74d35900a`](https://github.com/siderolabs/talos/commit/74d35900af0f6451426b70eec3b6db4b72eb993c) chore: disable k8s integration tests for 1GiB worker nodes * [`4f6218674`](https://github.com/siderolabs/talos/commit/4f621867407ec8f568f67833172ebaf2ff400346) feat: support TALOS_HOME env var * [`0c59b3ea3`](https://github.com/siderolabs/talos/commit/0c59b3ea3f6bc49cef409a1456b4ffa3bf1d28df) feat: add multicast to linkconfig * [`6db06f4d5`](https://github.com/siderolabs/talos/commit/6db06f4d5d51abd9e80ead6e4417f0f68856c569) feat: implement multicast setting * [`eeded98f5`](https://github.com/siderolabs/talos/commit/eeded98f527a230c65cb041a29fefc5f693d9879) fix: add riscv64 talosctl to release artifacts * [`a6bbae91b`](https://github.com/siderolabs/talos/commit/a6bbae91bad56328851fa91e01c17b8af7340b3c) fix: fix typos across the project * [`83f2bdb9c`](https://github.com/siderolabs/talos/commit/83f2bdb9ce6c9466716a6ac9c94dc2222e569ee8) feat: support relative voume size

### Changes since v1.13.0-alpha.1
44 commits

* [`009f0d6ca`](https://github.com/siderolabs/talos/commit/009f0d6ca0cf13e5778a7c46587ac0dc9d30d5e9) chore: update pkgs * [`ba56b0295`](https://github.com/siderolabs/talos/commit/ba56b02954fb275f8ff2ed20e38b51a75c3a8371) feat: include hid-multitouch.ko kernel module in rootfs * [`ae29a0dcc`](https://github.com/siderolabs/talos/commit/ae29a0dcce527b90553b25230abbb5a8d4bd504c) feat: update Linux to 6.18.13 * [`7cf1de279`](https://github.com/siderolabs/talos/commit/7cf1de2794a1d4838efca378aff433fad5e1823c) fix: bring in new version of go-cmd and go-blockdevice * [`c8800b41e`](https://github.com/siderolabs/talos/commit/c8800b41e511ce6bb4dda3e28b69c4d091177435) fix: update path handling on talosctl cgroups * [`0a7b6eb2c`](https://github.com/siderolabs/talos/commit/0a7b6eb2c98979aa8a604f677c4dd1d54f1285e5) chore: test extensions * [`8b1c974a2`](https://github.com/siderolabs/talos/commit/8b1c974a2a733c870f371ccb7a86ccc616dbc7ea) refactor: drop termui-widgets library * [`5baa0028e`](https://github.com/siderolabs/talos/commit/5baa0028e65765fc0fd1179f72377bf2a2085deb) fix: add owning inventory annotation to talos manifests * [`d3e793d14`](https://github.com/siderolabs/talos/commit/d3e793d14117891103ca4df8507124b18913a56c) fix: stop Kubernetes client from dynamically reloading the certs * [`6a5a0e3bd`](https://github.com/siderolabs/talos/commit/6a5a0e3bd4197a4fadfcfe094876e46d4b878a0a) feat: support pattern link aliases * [`9758bd4fe`](https://github.com/siderolabs/talos/commit/9758bd4fe0e28803acf11f3b9c9da744883aa9dc) feat: update Go to 1.26 * [`e00aed0f6`](https://github.com/siderolabs/talos/commit/e00aed0f6694bb3c8e14a0ef413ef0e62ae02981) feat: update Kubernetes v1.36.0-alpha.1 * [`f20445ad0`](https://github.com/siderolabs/talos/commit/f20445ad0981175d6444340325af5fc747993559) chore: improve logging of disk encryption handling * [`f018fbe7b`](https://github.com/siderolabs/talos/commit/f018fbe7ba145ff86ebe0d4d09b323b9715ef1a9) fix: handle raw encryption keys with `\n` properly * [`e5b0eb017`](https://github.com/siderolabs/talos/commit/e5b0eb017ff989e812d6444f668bf17723bb7ec4) fix: hold user volumes root mountpoint * [`8a0e79774`](https://github.com/siderolabs/talos/commit/8a0e79774409ce7605f9cd21d769f47e5db656db) refactor: split locate and provision * [`a59db0e92`](https://github.com/siderolabs/talos/commit/a59db0e92213296c4c9599fb0d230908caabdf30) fix: improve OpenStack bare metal network configuration reliability * [`659009ad8`](https://github.com/siderolabs/talos/commit/659009ad875c0625ac24094dc44020b015ab8b50) fix: remove stale endpoints * [`dab0d4783`](https://github.com/siderolabs/talos/commit/dab0d478378dfc6c2862c38633ca4494a41e7ecd) fix: allow static hosts in `/etc/hosts` without hostname * [`45f214154`](https://github.com/siderolabs/talos/commit/45f214154cea364d86bfbba81a5ad4f272a4c8fd) feat: update go-kubernetes to use new Myers diff * [`35ad0448c`](https://github.com/siderolabs/talos/commit/35ad0448c9ae93cd642d80ebb7d95b768ba0ab9b) fix: switch to better Myers algorithm implementation * [`0048464be`](https://github.com/siderolabs/talos/commit/0048464be854d94fb607e38daa83e00767fe8cbc) feat: update etcd to v3.6.8 * [`5df10f260`](https://github.com/siderolabs/talos/commit/5df10f2604b537504f76b14e028f88a946aacbd7) fix: use mcopy instead of diskfs to populate VFAT * [`ce53ffa90`](https://github.com/siderolabs/talos/commit/ce53ffa900a438f6669460a2ce9af874c1f87708) fix: disks flag parsing and handling in create qemu command * [`3bd3dd7ca`](https://github.com/siderolabs/talos/commit/3bd3dd7ca92401312079e37584bfbf7942eab93a) fix: memory overuse in imager VFAT * [`f118ee47e`](https://github.com/siderolabs/talos/commit/f118ee47eaba662dc161d37fae5ae8f2b3de9819) fix: read multi-doc machine config with newer talosctl * [`70c6c2154`](https://github.com/siderolabs/talos/commit/70c6c2154e87d4a6748aebdfa2c50cbc97a0dd89) feat: add filter for KubeSpan advertised networks * [`daf18abf4`](https://github.com/siderolabs/talos/commit/daf18abf419b21a6e70dcca0b5b83d33cfee6188) fix: fix talosctl debug in enforcing mode * [`33b5b2565`](https://github.com/siderolabs/talos/commit/33b5b25652360a114d0b2cea412bf018cbf84df3) fix: ignore volumes in wave calculation without provisioning * [`a16392559`](https://github.com/siderolabs/talos/commit/a16392559a488993c3e26810df57da3cae5c24c5) feat: add explicit service account support to Talos client * [`4d531884e`](https://github.com/siderolabs/talos/commit/4d531884e9c28d480f24b61a83f140df0ffbe4b3) chore: update dependencies * [`406b8c83c`](https://github.com/siderolabs/talos/commit/406b8c83c9b33b1917b9dd16aa1efeb2df189f0f) feat: update doc links to docs.siderolabs.com * [`87615f551`](https://github.com/siderolabs/talos/commit/87615f551183cd322dafebf368a347d928a14442) feat: implement network policies with Flannel CNI * [`6995bc1b1`](https://github.com/siderolabs/talos/commit/6995bc1b1ea54e1a8fd6426fef11293f35106ac7) chore: update homebrew formula on release * [`7942d5a98`](https://github.com/siderolabs/talos/commit/7942d5a98c1d689a94e78219be09a0fc69d07b08) fix: image gc controller config * [`52e8727d0`](https://github.com/siderolabs/talos/commit/52e8727d0112967a62a3d9ae6bf26d713db242e1) feat: add IPv6 GRE support * [`9690dbad0`](https://github.com/siderolabs/talos/commit/9690dbad02cfc8682d697679b655e753039c5254) chore: bump tools (including linter) * [`2628eb2ec`](https://github.com/siderolabs/talos/commit/2628eb2ece05d7f817fc42e12b979d3f8ca9710c) fix: typo with rpi_5 profile name * [`d5ebcd7ca`](https://github.com/siderolabs/talos/commit/d5ebcd7cae1a20c8000e2f4d5a02c81e4dbe5186) fix: stop building talosctl debug on Windows * [`8b85c7c63`](https://github.com/siderolabs/talos/commit/8b85c7c637cc08d35bbf6968abebb8c4cdfb82ad) chore: update deps * [`d905035b5`](https://github.com/siderolabs/talos/commit/d905035b5e5c7787a5171ba2e0127c89755e8774) fix: swap volume configuration for min/max size * [`d43a01ccb`](https://github.com/siderolabs/talos/commit/d43a01ccbdd318080b54e52d2f2fbec93042c458) feat: implement `talosctl debug` * [`34a31c979`](https://github.com/siderolabs/talos/commit/34a31c9797d5a7e1700c3d945a21367b81c79385) feat: add mount options support for existing volumes * [`1bf95eed1`](https://github.com/siderolabs/talos/commit/1bf95eed185152c38397cd3b43b6ff9d421678c5) feat: improve dashboard uptime display

### Changes from siderolabs/discovery-api
2 commits

* [`9c06846`](https://github.com/siderolabs/discovery-api/commit/9c06846e6f9f4f5765d5e431f8e25dc44a7ff337) feat: change the way excluded addresses are specified * [`f71a14a`](https://github.com/siderolabs/discovery-api/commit/f71a14a251c1e267d7a3701342563965947cc76f) feat: add advertised filters to discovery data

### Changes from siderolabs/go-cmd
2 commits

* [`5f31ba9`](https://github.com/siderolabs/go-cmd/commit/5f31ba92aa18c3f9a5c39b9f65b6beb9c55c6fac) chore: rekres and update * [`fff5698`](https://github.com/siderolabs/go-cmd/commit/fff56983373a4e3e37120fa159444e04a4ef580a) feat: allow capturing full output to stdout, modernize API

### Changes from siderolabs/go-debug
1 commit

* [`47fce68`](https://github.com/siderolabs/go-debug/commit/47fce68bb9d064757e11a7a3a81ed1a0b9d7124d) feat: support Go 1.26, rekres

### Changes from siderolabs/go-kubernetes
5 commits

* [`0a235c0`](https://github.com/siderolabs/go-kubernetes/commit/0a235c069d7d1cbf18a83cf73e23fed3e861a60b) feat: add early support for Kubernetes 1.36 * [`3bea212`](https://github.com/siderolabs/go-kubernetes/commit/3bea21294056bf7cd894c9fe257eae423e8e2a28) fix: use new Myers diff algorithm * [`604c56b`](https://github.com/siderolabs/go-kubernetes/commit/604c56b7251e8ec03b644b47c69ee08d6f25780b) chore: extract common code to the go-kubernetes package * [`ec0e3ae`](https://github.com/siderolabs/go-kubernetes/commit/ec0e3aefdeb332f4a44e669c9f7eb877b5f50963) chore: expose more ssa options * [`ad2fccd`](https://github.com/siderolabs/go-kubernetes/commit/ad2fccd09d137231f5a8187643782e0e1c661c44) feat: add SSA and pruning support

### Changes from siderolabs/kms-client
3 commits

* [`296bf9a`](https://github.com/siderolabs/kms-client/commit/296bf9a1085bd1a8dd06ba81b6969dddf196133c) feat: add logging to the KMS server * [`2d6b082`](https://github.com/siderolabs/kms-client/commit/2d6b08285a1506bcc3c866227790f2435c3f0f9c) feat: add TLS support for KMS server * [`4233ecd`](https://github.com/siderolabs/kms-client/commit/4233ecd1e8062da6c1131501fa6f2c80a3be686e) chore: bump deps, rekres

### Changes from siderolabs/pkgs
70 commits

* [`3c982f8`](https://github.com/siderolabs/pkgs/commit/3c982f8df278cf76a7fd421711eaf23bdbf3e948) chore: update deps * [`d065c59`](https://github.com/siderolabs/pkgs/commit/d065c5993c5994bc855c2894b5d8ab671c98ee28) feat: update Linux firmware to 20260221 * [`773ea3a`](https://github.com/siderolabs/pkgs/commit/773ea3a035cf01d01228cb95993dec17df77dd2c) feat: update Linux to 6.18.13 * [`6ca02b3`](https://github.com/siderolabs/pkgs/commit/6ca02b3129118749b2da47c5cfd6f25c377c0360) fix: make udev rules read only * [`520141c`](https://github.com/siderolabs/pkgs/commit/520141cd49b156e3db33496f187360acc85c3e1f) feat: enable kernel irq time accounting * [`8f6df51`](https://github.com/siderolabs/pkgs/commit/8f6df518459513a107786b7020df3d9546d64e27) feat: enable CONFIG_HID_MULTITOUCH * [`6934b50`](https://github.com/siderolabs/pkgs/commit/6934b5057f6996997420d43fbe620729c8cf22d5) feat: add patch for Cilium BPF verifier rejection by the kernel * [`5760aa7`](https://github.com/siderolabs/pkgs/commit/5760aa774e043d121921304863d335db7e9e9adf) feat: enable MLX5 Scalable Functions and TC offload in kernel * [`c0c8bc5`](https://github.com/siderolabs/pkgs/commit/c0c8bc56eb19aa4b4246c1813ab284b329ee9ffe) feat: enable CONFIG_DRM_ACCEL and IVPU on amd64 * [`b9cc39d`](https://github.com/siderolabs/pkgs/commit/b9cc39dcbbfb79141a644e614fb5e62da3fd93aa) feat: build kernel with Clang and ThinLTO, update Go to 1.26 * [`3327386`](https://github.com/siderolabs/pkgs/commit/33273866b175bd09a1d1bbfd47a41798537cb1b0) chore: drop mellanox-ofed * [`9013985`](https://github.com/siderolabs/pkgs/commit/9013985d859828105d9e3cd60c08e44fc4e11d07) feat: update dependencies * [`17196f5`](https://github.com/siderolabs/pkgs/commit/17196f595e7347e1f233cf5c9a1f16d90ce4e04d) feat: update NVIDIA LTS to 580.126.16 * [`8f53ad2`](https://github.com/siderolabs/pkgs/commit/8f53ad27bc7f70f4475cf71cabf7f86b1e20d794) feat: update Linux to 6.18.9 * [`eff5ba0`](https://github.com/siderolabs/pkgs/commit/eff5ba0d0e720ca4e1e2ed58c0719490b7f6826b) feat: enable ip6_gre * [`605ac0d`](https://github.com/siderolabs/pkgs/commit/605ac0d9cbb88be263618a553bbb1be785af2e97) chore: update deps * [`7670ff4`](https://github.com/siderolabs/pkgs/commit/7670ff45458bd39f5ca6076ba4eb65f0b68cf2e4) feat: enable NFT_BRIDGE config * [`dc737a6`](https://github.com/siderolabs/pkgs/commit/dc737a68c470c9498ec11bde09196809355d2463) chore: update kernel * [`9b118b3`](https://github.com/siderolabs/pkgs/commit/9b118b3d0fe7f0df06a069065b86ab307fef3375) chore: update deps * [`a63c227`](https://github.com/siderolabs/pkgs/commit/a63c2276eea0013463487cebf95ee35a37c5d9f6) feat: update OpenSSL to v3.6.1 * [`da7ab57`](https://github.com/siderolabs/pkgs/commit/da7ab5776bd1a6c551bfc6fe5919114721da0e1f) feat: add px-fuse pkg * [`553e0fb`](https://github.com/siderolabs/pkgs/commit/553e0fb70f076a8bc53e283253b30ff819e627ff) feat: enable dm-integrity * [`15a3cdf`](https://github.com/siderolabs/pkgs/commit/15a3cdf54884d5169895a1ff46682373688ac5e2) feat: update Linux to 6.18.6 * [`b518a19`](https://github.com/siderolabs/pkgs/commit/b518a196de93dd33e70faaff2342f67acb7dc49b) feat: update dependencies * [`1b4fbf5`](https://github.com/siderolabs/pkgs/commit/1b4fbf56b270d5669116fa0d8f91a3b9495e0d97) feat: update GRUB to 2.14 * [`30bc671`](https://github.com/siderolabs/pkgs/commit/30bc671d4be566ebf60b820edd54000616262e79) fix: enable pinctrl for Raspberry Pi 5 * [`375983f`](https://github.com/siderolabs/pkgs/commit/375983f4685484a8be5796f815629a9a0d8bd146) feat: update Go to 1.25.6 * [`d445c80`](https://github.com/siderolabs/pkgs/commit/d445c8076b7dd18b04f48e0a7e5cc2e50b3064d0) feat: update Linux to 6.18.5 * [`6994400`](https://github.com/siderolabs/pkgs/commit/69944002f9ee681220dcb23031c23ee327e6c1f2) feat: update NVIDIA LTS and production driver versions * [`05c3d85`](https://github.com/siderolabs/pkgs/commit/05c3d856b7de6eb64af718d7266a5adf15e1224b) feat: update Linux firmware to 20260110 * [`c61b466`](https://github.com/siderolabs/pkgs/commit/c61b466e130015b44962e7ef3bc1e9bec935b1df) feat: enable IT87 hwmon module * [`ae2572e`](https://github.com/siderolabs/pkgs/commit/ae2572e894a3d8d951418d447ec02f6cc65c8e72) feat: enable IPV6_MROUTE * [`d6b503e`](https://github.com/siderolabs/pkgs/commit/d6b503e0fe75d52f83d656a3460cb3614b352e51) feat: add RK3588 NPU Support * [`df4b4c8`](https://github.com/siderolabs/pkgs/commit/df4b4c885d4aabf702ce03bcb341f5b5f3641d76) feat: bump deps * [`a220898`](https://github.com/siderolabs/pkgs/commit/a2208985bd756ef6366497c5f9768e814b3f7583) feat: add libarchive * [`c2371b5`](https://github.com/siderolabs/pkgs/commit/c2371b5582836e27b3e80c4404c4ff5fbed90291) feat: enable ZRAM support * [`ab4d169`](https://github.com/siderolabs/pkgs/commit/ab4d169ad93203ba56b0677a10e78eb3e623762e) feat: add a patch to force uid when populating from a directory * [`972f44d`](https://github.com/siderolabs/pkgs/commit/972f44d5dae53809ef337544c52c835373439d34) feat: update dependencies * [`f8eb5b0`](https://github.com/siderolabs/pkgs/commit/f8eb5b02aaebaf76c59e71f57f4a689dc727e769) feat: update Linux to 6.18.2 * [`3fb6291`](https://github.com/siderolabs/pkgs/commit/3fb629109a7e5f9650d0e641ff5076a29c319448) feat: update systemd to 259 * [`59241bd`](https://github.com/siderolabs/pkgs/commit/59241bd58eeb07a18af1c9fc8fffff6365ecca0d) fix: add SBOMs for pigz/igzip * [`9377c78`](https://github.com/siderolabs/pkgs/commit/9377c786d112b4181f1e373f6e513130f11b7801) feat: optimize decompression for containerd * [`e8e61ce`](https://github.com/siderolabs/pkgs/commit/e8e61cedbbd687ed958db992e05b5d59e4a8ea60) feat: update containerd to 2.2.1 * [`daa74ba`](https://github.com/siderolabs/pkgs/commit/daa74bab83f91bbc4b6c42625d2953299d5fe20a) feat: support xfs filesystem reproducibility * [`1f66513`](https://github.com/siderolabs/pkgs/commit/1f665130fbda76478c261dd54e3843c15027c9cd) feat: update OpenZFS to 2.4.0 * [`b209af5`](https://github.com/siderolabs/pkgs/commit/b209af5baf1a67472ef431e5a8b7d48022392a1e) chore: rekres with latest changes * [`2b806b9`](https://github.com/siderolabs/pkgs/commit/2b806b9b2a7e05b97c2a7e8572e3a8edbd3721d3) feat: bump dependencies * [`65242fd`](https://github.com/siderolabs/pkgs/commit/65242fd0fef5c9c923aacce23d1655bad0d1b3e3) feat: enable CONFIG_MISC_RP1 in ARM64 config * [`4daecd8`](https://github.com/siderolabs/pkgs/commit/4daecd8e7b8d87110a9e552a60a5394014294e08) feat: update Linux to 6.18.1 * [`9868a66`](https://github.com/siderolabs/pkgs/commit/9868a66e3c000f505c97ff68e61abac9c9e8e4c9) feat: enable Powercap and Intel RAPL * [`07883ee`](https://github.com/siderolabs/pkgs/commit/07883eee3729d4d3adaaebcd825452934c3baebb) feat: build and package perf binary * [`47abca0`](https://github.com/siderolabs/pkgs/commit/47abca0852b9555d88eba61661c65a7f93ec3590) fix: add json support to nftables binary * [`b961ff8`](https://github.com/siderolabs/pkgs/commit/b961ff898fc9eae68d7f3cea2ca22ff4d0b9c99d) feat: patch containerd 2.2.0 with cgroups fix patch * [`b7dd7f6`](https://github.com/siderolabs/pkgs/commit/b7dd7f6c809f670f058b78fd3b84f4cb977771cb) feat: add mstflint module * [`ae53351`](https://github.com/siderolabs/pkgs/commit/ae5335198e009da7b06bc0f0d6f42b0947650fc0) feat: update ZFS to 2.4.0-rc5 * [`b8edf01`](https://github.com/siderolabs/pkgs/commit/b8edf0168171ffc5b87fcd962e37d5c2cd25b687) feat: update CNI plugins to v1.9.0 * [`a57c1b0`](https://github.com/siderolabs/pkgs/commit/a57c1b0c9d143559a87b64fe9570eec39c14a771) feat: enable amd sev-snp * [`68562c1`](https://github.com/siderolabs/pkgs/commit/68562c1b4cdba656287021a1694440b2a7e4d24d) feat: update Linux to 6.18 * [`6f4ff8c`](https://github.com/siderolabs/pkgs/commit/6f4ff8cc9f57452707588c05e5ca4e80c56548d2) feat: enable Amlogic Meson PCIe controller driver * [`c41127b`](https://github.com/siderolabs/pkgs/commit/c41127b94d22b9a5cb6b93f49b546f2ff477410c) feat: enable Intel GPIO/Pinctrl kernel modules * [`4a31ff7`](https://github.com/siderolabs/pkgs/commit/4a31ff7dd5c9266b68abded53a7399cb8102f4e3) feat: update NVIDIA LTS to 580.105.08 * [`3e858d3`](https://github.com/siderolabs/pkgs/commit/3e858d3fa5b2719d8d83397fb89c2ffc91f86615) chore: fork pkgs for Talos 1.13 * [`dcc5aa1`](https://github.com/siderolabs/pkgs/commit/dcc5aa1e71d6b2e9374d41029a2e6de22dbc61ce) feat: update runc to 1.3.4 * [`8b6ae5b`](https://github.com/siderolabs/pkgs/commit/8b6ae5b7fc22c3bb2df4bbe31190ff90b0986e6f) fix: regenerate configs * [`2992598`](https://github.com/siderolabs/pkgs/commit/29925980896df1978a020505b2b061ffdbd240c7) fix: add missing kernel config entries * [`c8ea18a`](https://github.com/siderolabs/pkgs/commit/c8ea18a0873f5b31c54d567ef97d8d05634eb506) feat: rekres to alow multiple commits * [`2ddef8b`](https://github.com/siderolabs/pkgs/commit/2ddef8b65755610fc6dbb3f1fb976a6bc572478f) chore: update dependencies * [`d1f28e0`](https://github.com/siderolabs/pkgs/commit/d1f28e058972174af9ac819783a69f5f6596b37d) chore: update dependencies * [`ab253f5`](https://github.com/siderolabs/pkgs/commit/ab253f521d95b30710e258ebb54adbb7b8de8970) feat: enable gpio-fan module * [`0b10666`](https://github.com/siderolabs/pkgs/commit/0b1066635d9dd255bf0ad936e21099fd4bd03f1e) chore: use ubuntu mirrors

### Changes from siderolabs/proto-codec
1 commit

* [`bd9c491`](https://github.com/siderolabs/proto-codec/commit/bd9c491b9e84d7274728ce7e3bde14009f5224bd) chore: bump and update dependencies

### Changes from siderolabs/tools
17 commits

* [`9de9770`](https://github.com/siderolabs/tools/commit/9de9770aeffac63ebe0cbd9e02ebe8f77b4a9635) feat: update to Go 1.26 * [`bd4ae8f`](https://github.com/siderolabs/tools/commit/bd4ae8f69c218c05e143a1a04c5710505b8559f2) feat: add LLVM+Clang+LLD toolchain * [`90bd70c`](https://github.com/siderolabs/tools/commit/90bd70c94cf434ff45dcd71d1f3d9435b8243093) feat: update dependencies * [`decb988`](https://github.com/siderolabs/tools/commit/decb9887929e8d3b3879d56a984244cfe8ae0213) chore: update deps * [`ca26e1c`](https://github.com/siderolabs/tools/commit/ca26e1c38cd0a76eb981db4dad2e6caccb0bbe4d) chore: update deps * [`0281af0`](https://github.com/siderolabs/tools/commit/0281af0545e17c409fb32d8db61a7d9b0ad8b1c2) feat: update OpenSSL to 3.6.1 * [`721ad07`](https://github.com/siderolabs/tools/commit/721ad073f18b41407882727a3f0061e594f6c955) feat: update dependencies * [`2b3f514`](https://github.com/siderolabs/tools/commit/2b3f514d42a343d98c79a487e80bd4f225a41b70) fix: reproducible build for nasm * [`98c699e`](https://github.com/siderolabs/tools/commit/98c699eb624d0846455f08db77cc14e446cb6db9) feat: update Go to 1.25.6 * [`cd5eb66`](https://github.com/siderolabs/tools/commit/cd5eb66bb0de4fb468a860e176267c3420b4a3a1) chore: run rekres and update dependencies * [`896f8b9`](https://github.com/siderolabs/tools/commit/896f8b9c1f88cd190d11b8ef3baa2c36e73d6dfe) fix: add sbom for zlib-ng * [`543a16f`](https://github.com/siderolabs/tools/commit/543a16fedf7170d8b015ea1391817328205e629a) feat: replace zlib -> zlib-ng, add nasm * [`b67c1a1`](https://github.com/siderolabs/tools/commit/b67c1a168b302539d2082a5513c4a0130c30e4df) chore: rekres with latest changes * [`5e087cb`](https://github.com/siderolabs/tools/commit/5e087cbcd158db1ce4f447145bd76a24d07159a1) feat: bump dependencies * [`da96a27`](https://github.com/siderolabs/tools/commit/da96a2771801627b4715f7a13199aa6846f87732) chore: rekres to fix reproducibility * [`e283ec8`](https://github.com/siderolabs/tools/commit/e283ec8d3831bb19b26938afb10f4955ea563ce2) feat: update Go to 1.25.5 * [`c38ff0c`](https://github.com/siderolabs/tools/commit/c38ff0c03be69e5cc3795d9dc055896604a3041c) chore: update to 1.13.0-alpha.0 toolchain

### Dependency Changes * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.20.0 -> v1.21.0 * **github.com/aws/aws-sdk-go-v2/config** v1.31.20 -> v1.32.7 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.18.13 -> v1.18.17 * **github.com/aws/aws-sdk-go-v2/service/acm** v1.37.19 **_new_** * **github.com/aws/aws-sdk-go-v2/service/kms** v1.46.0 -> v1.49.5 * **github.com/aws/smithy-go** v1.23.2 -> v1.24.0 * **github.com/containerd/cgroups/v3** v3.0.5 -> v3.1.2 * **github.com/containerd/containerd/api** v1.9.0 -> v1.10.0 * **github.com/containerd/containerd/v2** v2.1.5 -> v2.2.1 * **github.com/containerd/platforms** v1.0.0-rc.1 -> v1.0.0-rc.2 * **github.com/cosi-project/runtime** v1.12.0 -> v1.14.0 * **github.com/docker/cli** v29.0.0 -> v29.2.1 * **github.com/gdamore/tcell/v2** v2.9.0 -> v2.13.8 * **github.com/godbus/dbus/v5** v5.1.0 -> v5.2.2 * **github.com/google/cadvisor** v0.53.0 -> v0.56.2 * **github.com/google/cel-go** v0.26.1 -> v0.27.0 * **github.com/google/go-containerregistry** v0.20.6 -> v0.20.7 * **github.com/google/go-tpm** v0.9.7 -> v0.9.8 * **github.com/hetznercloud/hcloud-go/v2** v2.30.0 -> v2.36.0 * **github.com/jsimonetti/rtnetlink/v2** v2.1.0 -> v2.2.0 * **github.com/klauspost/compress** v1.18.1 -> v1.18.4 * **github.com/linode/go-metadata** v0.2.2 -> v0.2.4 * **github.com/mdlayher/ethtool** v0.4.0 -> v0.5.1 * **github.com/miekg/dns** v1.1.68 -> v1.1.72 * **github.com/moby/moby/api** v1.52.0 -> v1.53.0 * **github.com/moby/moby/client** v0.1.0 -> v0.2.2 * **github.com/navidys/tvxwidgets** v0.13.0 **_new_** * **github.com/opencontainers/runtime-spec** v1.2.1 -> v1.3.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.35 -> v1.0.0-beta.36 * **github.com/siderolabs/discovery-api** v0.1.6 -> v0.1.8 * **github.com/siderolabs/go-blockdevice/v2** v2.0.20 -> v2.0.25 * **github.com/siderolabs/go-cmd** v0.1.3 -> v0.2.0 * **github.com/siderolabs/go-debug** v0.6.1 -> v0.6.2 * **github.com/siderolabs/go-kubernetes** v0.2.28 -> v0.2.32 * **github.com/siderolabs/kms-client** v0.1.0 -> v0.2.0 * **github.com/siderolabs/pkgs** v1.12.0-23-ge0b78b8 -> v1.13.0-alpha.0-61-g3c982f8 * **github.com/siderolabs/proto-codec** v0.1.2 -> v0.1.3 * **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> 35ad0448c9ae * **github.com/siderolabs/tools** v1.12.0-2-g7d57df0 -> v1.13.0-alpha.0-16-g9de9770 * **github.com/sirupsen/logrus** v1.9.3 -> v1.9.4 * **github.com/spf13/cobra** v1.10.1 -> v1.10.2 * **go.etcd.io/etcd/api/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/pkg/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/etcdutl/v3** v3.6.6 -> v3.6.7 * **go.uber.org/zap** v1.27.0 -> v1.27.1 * **go.yaml.in/yaml/v4** v4.0.0-rc.3 -> v4.0.0-rc.4 * **golang.org/x/net** v0.47.0 -> v0.50.0 * **golang.org/x/oauth2** v0.33.0 -> v0.35.0 * **golang.org/x/sync** v0.18.0 -> v0.19.0 * **golang.org/x/sys** v0.38.0 -> v0.41.0 * **golang.org/x/term** v0.37.0 -> v0.40.0 * **golang.org/x/text** v0.31.0 -> v0.34.0 * **google.golang.org/grpc** v1.76.0 -> v1.79.1 * **google.golang.org/protobuf** v1.36.10 -> f2248ac996af * **sigs.k8s.io/cli-utils** 77c836a69463 **_new_** Previous release can be found at [v1.12.0](https://github.com/siderolabs/talos/releases/tag/v1.12.0) ## [Talos 1.13.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.13.0-alpha.1) (2026-02-03) Welcome to the v1.13.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Environment Configuration Document A new `EnvironmentConfig` document has been introduced to allow users to specify environment variables for Talos components. It replaces and deprecates the previous method of setting environment variables via the `.machine.env` field. Multiple values for the same environment variable will replace previous values, with the last one taking precedence. To remove an environment variable, remove it from the `EnvironmentConfig` document and restart the node. ### External Volumes Talos now supports virtiofs-based external volumes via the new [ExternalVolumeConfig](https://www.talos.dev/v1.13/reference/configuration/block/externalvolumeconfig/) document. These virtiofs external volumes are not supported when SELinux is running in enforcing mode. ### Extra Arguments accept slices in addition to strings Several Talos configuration fields that previously accepted single string values for extra arguments have been updated to accept slices of strings as well. This includes fields such as `.cluster.apiServer.extraArgs`. BREAKING: If you were relying on the resources EtcdConfigs, KubeletConfigs, ControllerManagerConfigs, SchedulerConfigs or APIServerConfigs, the protobuf format has changed from `map` to `map`. ### Talos Imager Enhancements Talos imager now supports running rootless. `--privileged` and `-v /dev:/dev` are no longer required. ### Talosctl images k8s-bundle subcommand accepts version parameter The `talosctl images k8s-bundle` command now accepts an optional version overrides arguments. ### Kubernetes server-side apply Talos now uses inventory backed server-side apply when applying bootsrap manifests (including `extraManifests` and `inlineManifests`). Purging of unneeded manifests is automatically performed. The switch and inventory backfill is automatic and no action is needed from the user. ### KubeSpan Configuration A new `KubeSpanConfig` document has been introduced to configure KubeSpan settings. It replaces and deprecates the previous method of configuring KubeSpan via the `.machine.network.kubespan` field. The old configuration field will continue to work for backward compatibility. ### Negative Max Volume Size Negative max size represents the amount of space to be left free on the device, rather than the size the volume should consume. For example: * a max size of "-10GiB" means the volume can grow to the available space minus 10GiB. * a max size of "-25%" means the volume can grow to the available space minus 25%. ### Container Image Decompression Talos now ships with `igzip` (amd64) and `pigz` (arm64) to speed up container image decompression. ### ProbeConfig The TCPProbeConfig configuration document allows to configure TCP probes for network reachability checks. This allows to define a custom connectivity condition. ### /proc/PID/mem Access Hardening A new kernel parameter `proc_mem.force_override=never` has been introduced by default to enhance system security by preventing unwanted writes to protected process memory via `/proc/PID/mem`. If the kernel parameter is removed, default behavior is restored, allowing access only if the process is traced. ### Reproducible Disk Images Talos disk images are now reproducible. Building the same version of Talos multiple times will yield identical disk images. Note: VHD and VMDK (Azure and VMware) images are not currently reproducible due to limitations in the underlying image creation tools. Users verifying reproducible images should use raw images, verify checksums, and convert them to VHD/VMDK as needed. ### ResolverConfig The nameservers configuration in machine configuration now overwrites any previous layers (defaults, platform, etc.) when specified. Previously a smart merge was performed to keep IPv4/IPv6 nameservers from lower layers if the machine configuration specified only one type. ### Service Account Issuer configuration In API Server, passing extra args with `service-account-issuer` will append them after default value. This allows easy migration, e.g. by changing `.cluster.controlPlane.endpoint` to new value, and keeping the old value in `.cluster.apiServer.extraArgs["service-account-issuer"]`. ### `talosctl images talos-bundle` can ignore reaching to the registry The `talosctl images talos-bundle` command now accepts optional `--overlays` and `--extensions` flags. If those are set to `false`, the command will not attempt to reach out to the container registry to fetch the latest versions and digests of the overlays and extensions. ### Component Updates Linux: 6.18.8 containerd: 2.2.1 etcd: 3.6.7 CoreDNS: 1.13.2 Kubernetes: 1.35.0 Flannel CNI plugin: v1.9.0-flannel1 LVM2: 2_03_38 runc: 1.4.0 systemd: 259 cryptsetup: 2.8.3 Talos is built with Go 1.25.6. ### VM Hot-Add Support Talos now includes udev rules to support hot-adding of CPUs in virtualized environments. ### Contributors * Andrey Smirnov * Mateusz Urbanek * Noel Georgi * Dmitrii Sharshakov * Orzelius * Laura Brehm * Bryan Lee * Edward Sammut Alessi * Alexis La Goutte * Andras BALI * Andrei Kvapil * Artem Chernyshev * Birger Johan Nordølum * Camillo Rossi * Christopher Puschmann * Florian Ströger * Gregor Gruener * Jaakko Sirén * Jean-Francois Roy * Joakim Nohlgård * Jonas Lammler * Justin Garrison * Lennard Klein * Matthew Sanabria * Max Makarov * Michal Baumgartner * Mickaël Canévet * Olav Thoresen * Pranav Patil * Serge van Ginderachter * Skye Soss * Spencer Smith * Tim Jones * dataprolet * eseiker * pranav767 ### Changes
176 commits

* [`900516e68`](https://github.com/siderolabs/talos/commit/900516e68950e4b94696f6a9b481cefee44b3360) chore: update image signer * [`938de566e`](https://github.com/siderolabs/talos/commit/938de566eca30af3cc4355a94931186f19b682f2) feat: bump kernel * [`388cec727`](https://github.com/siderolabs/talos/commit/388cec72796d0ecd0c7103efcaab9066e9b62509) feat(overlays): add new overlays * [`9f2dd6312`](https://github.com/siderolabs/talos/commit/9f2dd6312f9d49e4d03347c98b100119f94cf807) refactor: api tests * [`a90783146`](https://github.com/siderolabs/talos/commit/a90783146fc2d475055bfce0f8b5120969f74dc7) feat: add a helper module to generate standard patches * [`1fec5b23d`](https://github.com/siderolabs/talos/commit/1fec5b23d0c10e53863a7c0f89f862708a7f4069) fix: implement merger for PercentageSize * [`8b245b8f2`](https://github.com/siderolabs/talos/commit/8b245b8f269b6c8cb463f2cf537d2ed2ab6924ec) feat: implement new image service APIs * [`d90c775b8`](https://github.com/siderolabs/talos/commit/d90c775b8441705003de3427b2e6831dcbfb449f) chore: rename internal `talosctl debug air-gapped` * [`2165280d0`](https://github.com/siderolabs/talos/commit/2165280d0eedf59899ad44e2f3289d81b3dab466) refactor: change the way one2many proxying is picked * [`b1b703dbe`](https://github.com/siderolabs/talos/commit/b1b703dbe2b25785ded0c77f23d674d9b9934975) chore: move sync logging code to go-kubernetes package * [`e48c6d7ab`](https://github.com/siderolabs/talos/commit/e48c6d7ab9c8a2e28ebe2115ac09f1557bbcca33) fix: allow to expose a port multiple times in Docker * [`410d8cb57`](https://github.com/siderolabs/talos/commit/410d8cb5727ccf054c9097f33bc916d87076a599) fix: undo CRLF on Windows (talosctl edit) * [`859d3f03c`](https://github.com/siderolabs/talos/commit/859d3f03c444d98b94a06adac3648562e3b1228b) feat: add RPi5 to the list of supported SBCs * [`0bd48bbc6`](https://github.com/siderolabs/talos/commit/0bd48bbc6f365770167ee753be563eb4179fcadb) fix(talosctl): pass --k8s-endpoint flag to rotate-ca kubernetes rotation * [`b9e27ebe7`](https://github.com/siderolabs/talos/commit/b9e27ebe72c4302c416fd8efb007c3966004ddd6) feat: update Linux kernel with dm-integrity * [`6aa9b0677`](https://github.com/siderolabs/talos/commit/6aa9b0677ed7ca4955fead474e36a533b3250ad9) fix: skip empty documents on config decoding * [`494492489`](https://github.com/siderolabs/talos/commit/494492489b29b615a8a874c0648690ed3b9adb58) fix: always set advertised peer URLs * [`782cc507d`](https://github.com/siderolabs/talos/commit/782cc507dc33c87caa5ff985eea5f4439c3e1012) fix: open the filesystem as read-only * [`28e61a740`](https://github.com/siderolabs/talos/commit/28e61a740a906fadfea098f38a9c9f4e8c32773e) fix: set GRUB prefix correctly on arm64 * [`a4f1c5239`](https://github.com/siderolabs/talos/commit/a4f1c5239ef7227856640c230e0d0364d9eedbd2) feat: update GRUB to 2.14 * [`562920701`](https://github.com/siderolabs/talos/commit/562920701e2999cbb6687e55de96719aba4064fd) fix: use node podCIDRs for kubespan advertiseKubernetesNetworks * [`39460365c`](https://github.com/siderolabs/talos/commit/39460365c1726095e20cf3cc7c079c234b8022d6) feat: implement layering for ProbeSpec * [`b5c760f70`](https://github.com/siderolabs/talos/commit/b5c760f7076570bc04be02af0ea493f95d8338d0) feat: add ProbeConfig for network connectivity probes * [`4b274f761`](https://github.com/siderolabs/talos/commit/4b274f76159495cc6c2977ec3bbade71e35aade8) feat: support aws cert manager in imager * [`417209512`](https://github.com/siderolabs/talos/commit/41720951251102f1c174e501a3103e55720a1d8b) fix: fallback to /proc/meminfo for memory modules * [`7f1147bed`](https://github.com/siderolabs/talos/commit/7f1147bed495a06d336f5be1da6073921b5e52dc) fix: add warnings to 802.3ad bond * [`ddd6b186e`](https://github.com/siderolabs/talos/commit/ddd6b186eb8f527324736576182dafbce3423da5) refactor: generate GRUB images * [`c7aa266ea`](https://github.com/siderolabs/talos/commit/c7aa266ea5c9d3fbd465dc651f2ebfec622612e7) fix: overwrite resolver config with machine config * [`cf70f05fa`](https://github.com/siderolabs/talos/commit/cf70f05fa40312c30d8345c2fb15ce8eda86a7a7) fix: oracle platform file format * [`8c7b8f5b7`](https://github.com/siderolabs/talos/commit/8c7b8f5b7d6dec144f7985a7c8a8a582c38f3154) feat: add support for negative max size * [`77bc3d21f`](https://github.com/siderolabs/talos/commit/77bc3d21fa40e188af4b5dd93e1cda289e858d56) fix: marshal of FailOverMac property * [`38e280c93`](https://github.com/siderolabs/talos/commit/38e280c9319ef1ecb1455b3cc8b8d0d1d7426ccd) fix: make OOM expression a bit less sensitive * [`3d1301640`](https://github.com/siderolabs/talos/commit/3d1301640d44d58303160400e4954c36f53341f9) fix: wipe the first/last 1MiB in addition to wiping by signatures * [`1aa6528ad`](https://github.com/siderolabs/talos/commit/1aa6528adcddfb6a5ed66cc26cac1a0fcdb37516) fix: make OOM controller more precise by considering separate cgroup PSI * [`f7072c050`](https://github.com/siderolabs/talos/commit/f7072c050e607de16781a65eb97ab2a1828b05fb) fix: check if the device is not mounted when wiping * [`743c3b94b`](https://github.com/siderolabs/talos/commit/743c3b94b958e4abcbf70d4064f2ae0e0bbb0712) fix: use correct containerd import path * [`f2dd08594`](https://github.com/siderolabs/talos/commit/f2dd08594e8e474c7b3891dc46c64f27c724dbc0) feat: report image pull progress in the console * [`72fe98a06`](https://github.com/siderolabs/talos/commit/72fe98a06f31536454f201d703f8ae6a071235b5) fix: boot with GRUB * [`d4ed13d93`](https://github.com/siderolabs/talos/commit/d4ed13d9394b087e8877eba25950f344894803a1) fix: add talos version to Hetzner Cloud client user agent * [`150c41c30`](https://github.com/siderolabs/talos/commit/150c41c30ed3f066f10bd2bdc2afa9b2c5a97597) feat: update Linux to 6.18.5 * [`01a367891`](https://github.com/siderolabs/talos/commit/01a3678913de0fa4d309a361428c117d24ce0d1e) fix: use append instead of prepend in service-account-issuer * [`d1954278a`](https://github.com/siderolabs/talos/commit/d1954278a1ba3470b2e5ccae90762078c18d69e9) feat: add extraArgs from service-account-issuer * [`91b88f7f9`](https://github.com/siderolabs/talos/commit/91b88f7f994cccad15cbec1aa8019bd19b84ae91) feat: support multiple values for extraArgs * [`96e604874`](https://github.com/siderolabs/talos/commit/96e604874b17e7aa8b62bfb25737f349e539bc5a) fix: add hostname to endpoints * [`7033275a7`](https://github.com/siderolabs/talos/commit/7033275a7a22d51e83c9e760ba37d2ad6ab22f28) refactor: move BootloaderKind into machinery * [`71adaf0ea`](https://github.com/siderolabs/talos/commit/71adaf0ea5b558c8a16e2acfdec3671611455985) fix: sort mirrors and tls configs when generating the machine config * [`34f09a300`](https://github.com/siderolabs/talos/commit/34f09a3004fe1b77c16dd33b04adca95fb6876a5) feat: add VLAN support to OpenStack platform * [`5127ef7c2`](https://github.com/siderolabs/talos/commit/5127ef7c28b360f9c7c033f77c58cef729e5278d) fix: wipe disk by signatures * [`415bfaedb`](https://github.com/siderolabs/talos/commit/415bfaedb6ae8d42b5927fdc5b7cfe8aa781a791) fix: panic in configpatcher when the whole section is missing * [`e5aca71cd`](https://github.com/siderolabs/talos/commit/e5aca71cd0557557e50c39d82eda2c938f627d62) fix: fix healthcheck timeout * [`634b71e2d`](https://github.com/siderolabs/talos/commit/634b71e2d028bf13d838acad8809c95384b6eed9) docs: move talosctl pcap example to Example Block * [`818492731`](https://github.com/siderolabs/talos/commit/8184927316c5de7d9b04f21474a60cc791c3d26d) feat: implement KubeSpan multi-document configuration * [`4d0604b9d`](https://github.com/siderolabs/talos/commit/4d0604b9d93851f444a00dbd84fcac76d21d35c2) chore: remove unrelated machineconfig * [`e36863470`](https://github.com/siderolabs/talos/commit/e36863470b14496c3d84417e63fef45e6060603b) feat: add it87 hwmon module * [`308c75090`](https://github.com/siderolabs/talos/commit/308c75090774d2510c2ec08e63e179a5c0fa6987) fix: resolve SideroLink Wireguard endpoint on reconnect * [`e4ef494de`](https://github.com/siderolabs/talos/commit/e4ef494decdf97664c4803aa3861015fce49760e) fix: drop the persist config flag from gen config * [`c3176adcf`](https://github.com/siderolabs/talos/commit/c3176adcf981811a326c971c81c4b591f54e116a) feat: add EnvironmentConfig document * [`c839b3880`](https://github.com/siderolabs/talos/commit/c839b38809b3a0029061d43477555ec31e283aa5) feat: expose more SSA options in the upgrade-k8s command * [`b8ff9677e`](https://github.com/siderolabs/talos/commit/b8ff9677e4f9a64908ae00bb1d80aa2442a00a60) fix: handle correctly incomplete RegistryTLSConfig * [`99f2ddada`](https://github.com/siderolabs/talos/commit/99f2ddada895011036af1435dd10bac3be0a9171) fix: bond config via platform * [`2449ffea4`](https://github.com/siderolabs/talos/commit/2449ffea45304459ea8895b535b6f070a9249172) fix: allow HostnameConfig to be used with incomplete machine config * [`35fc52087`](https://github.com/siderolabs/talos/commit/35fc5208728dbc3e0b139aff4c06f25208445637) fix: lock down etcd listen address to IPv4 localhost * [`27253d731`](https://github.com/siderolabs/talos/commit/27253d7317a473cbbc0f5c0eee634173bdd2eda7) feat: use new xfs config file * [`c9d84ae21`](https://github.com/siderolabs/talos/commit/c9d84ae21e203529a6952c165ff04d602a2a6ad6) fix: generate OCI-compliant image config * [`7a4b2b33a`](https://github.com/siderolabs/talos/commit/7a4b2b33abe8a3011f37f0a8f4848dd846d0396f) fix: update VIP config example * [`080efcbda`](https://github.com/siderolabs/talos/commit/080efcbda2c4334f9d8c70804a5a37f0cdb2df2d) feat: add k8s-version parameter to k8s-bundle * [`b764f5f72`](https://github.com/siderolabs/talos/commit/b764f5f724bf8af3acaac74942ea91a86e593322) fix: skip sync test when kube-proxy is disabled * [`70e67787d`](https://github.com/siderolabs/talos/commit/70e67787d6d34d93a34871b2d25d64f6a7575d76) feat: imager: populate filesystems with root owned files * [`7416dca59`](https://github.com/siderolabs/talos/commit/7416dca59378dc282e42ea30107cf40326cc593c) fix: print talosctl images to release notes * [`dc2009e47`](https://github.com/siderolabs/talos/commit/dc2009e4779684a6a4252d4dfd2aa02d1b60c2da) chore: use context when creating filesystems * [`85f7be6e3`](https://github.com/siderolabs/talos/commit/85f7be6e3f14bf160cf32bccf7418b31968d474f) chore: update slack links * [`154952175`](https://github.com/siderolabs/talos/commit/154952175ab73ac65722732b146a0ee1c56b2f4d) fix: disable swap for system services * [`d98b415af`](https://github.com/siderolabs/talos/commit/d98b415afea7b1820153151c0273df24a101742e) fix: drop more non-overlay SBC stuff * [`226cd6bc1`](https://github.com/siderolabs/talos/commit/226cd6bc1d70662cb7f7736ac6fad117170a36fb) fix: do not allocate for the actual disk image file * [`53f5bf8d2`](https://github.com/siderolabs/talos/commit/53f5bf8d2c97e91bee06bcb5948170015486ea77) fix: overlay installers * [`10d0cfd93`](https://github.com/siderolabs/talos/commit/10d0cfd93a083fb8b71b7c0297df52feb55e044b) fix: overlay install in image mode * [`77086694d`](https://github.com/siderolabs/talos/commit/77086694d18b69802e542156fc12cd7cf066efc2) fix: partition data population * [`4d5657b1a`](https://github.com/siderolabs/talos/commit/4d5657b1a34c939b63b2cc3ee11ed45ad1bf23c3) fix: drop SBC board code * [`c4f3f6d3e`](https://github.com/siderolabs/talos/commit/c4f3f6d3e59b58016ba8546c5bd3e8e465fbbf52) feat: implement kubernetes server-side apply * [`f12fd2b0a`](https://github.com/siderolabs/talos/commit/f12fd2b0a9fdf8f53ec5714d3ad18b695973e0b0) test: bump Image Factory tests * [`c76484e58`](https://github.com/siderolabs/talos/commit/c76484e5879a7e48197e442cf22044d3d0363846) release(v1.13.0-alpha.0): prepare release * [`f0d8a6851`](https://github.com/siderolabs/talos/commit/f0d8a685173354e5fd148786872062a342c4282a) test: skip the source bundle on exact tag * [`c57701d65`](https://github.com/siderolabs/talos/commit/c57701d6590388e7d6418af67e8237c7d60ccf54) fix: remove interactive installer * [`43937c1cd`](https://github.com/siderolabs/talos/commit/43937c1cd42758a15026261fe8f0e06daaebdcbd) feat: update Linux and systemd * [`72a194df8`](https://github.com/siderolabs/talos/commit/72a194df88f2800cee3372241fbad419b07f7bbf) feat: add VM CPU hot-add rules * [`f09ae1e0d`](https://github.com/siderolabs/talos/commit/f09ae1e0d2e1b7842d504b594b71a325af7733e5) fix: probe small images correctly * [`8f2b33799`](https://github.com/siderolabs/talos/commit/8f2b337994fdeff76a0ae9e1730b4b9f596ff1bb) feat: imager support rootless builds * [`c7525a97e`](https://github.com/siderolabs/talos/commit/c7525a97ef8615e903be183d7938b6d2a3b89464) feat: support creating filesystems from folder * [`e2bffb5ce`](https://github.com/siderolabs/talos/commit/e2bffb5cebaaf28f9dfff24f41ecbb2809fc60e5) chore: refactor imager code so it's more clear * [`0fb50dbd0`](https://github.com/siderolabs/talos/commit/0fb50dbd0a5b7b80187e50d501cec4b3fe434dc2) fix: invalid versions check in talos-bundle * [`b5dd56032`](https://github.com/siderolabs/talos/commit/b5dd5603207a46d8eed240173f06aeffd6a9c0e7) test: upgrade versions in upgrade tests * [`3dfa4d6e4`](https://github.com/siderolabs/talos/commit/3dfa4d6e40dcae2db47e89443568be3ae48b3ae1) fix: make upgrade work with SELinux enforcing=1 * [`786c8e2ee`](https://github.com/siderolabs/talos/commit/786c8e2ee757c2d7b30d5bded954e584af3a058e) feat: ship pigz/igzip in rootfs to speed up image decompression * [`48d242918`](https://github.com/siderolabs/talos/commit/48d242918bc97e6a01434bee6fcdcfa735fd1f5a) feat: update containerd to 2.2.1 * [`536541afe`](https://github.com/siderolabs/talos/commit/536541afe497d5f61cfcd0c01cf580ab5b3be164) fix: mount volume mount/unmount race * [`39117d457`](https://github.com/siderolabs/talos/commit/39117d45766b139ed6a0c1290f757e4b26d31d92) feat: update dependencies * [`f0f420725`](https://github.com/siderolabs/talos/commit/f0f420725c6a4f628cdc1b80d59713c375beb9b7) fix: bond setting change detection * [`8d6a7a867`](https://github.com/siderolabs/talos/commit/8d6a7a8677a5d1d61432fa94ca030351fd9852f2) feat: update Kubernetes to 1.35.0 * [`845a0d09c`](https://github.com/siderolabs/talos/commit/845a0d09cd770a15db762ddda4d3d27f58656cfe) feat: update etcd 3.6.7, CoreDNS 1.13.2 * [`b95912e04`](https://github.com/siderolabs/talos/commit/b95912e04907b78bd06987c6d3948f8f1804d844) feat: enforce `proc_mem.force_override=never` by default * [`681f3e84c`](https://github.com/siderolabs/talos/commit/681f3e84c85677f49ddbcd4a47e325d4a85af692) test: run virtiofs tests only when virtiofsd is running * [`0592ff0cd`](https://github.com/siderolabs/talos/commit/0592ff0cdbf54475dc91bfb7c9b9c3047bbe13da) fix: drop the Omni API URL check on IP address * [`a4879a5fa`](https://github.com/siderolabs/talos/commit/a4879a5fa2ded9b7b52ff7506b5493ae12939bba) feat: update Linux to 6.18.1 * [`43b43ff18`](https://github.com/siderolabs/talos/commit/43b43ff189b7e5f37eaa75f4926c26ee21ffa5cb) docs: split talosctl commands into groups * [`6d17c18bf`](https://github.com/siderolabs/talos/commit/6d17c18bf908d3cd69ff920d0cff67b653a385f3) feat: enable Powercap and Intel RAPL * [`884e76662`](https://github.com/siderolabs/talos/commit/884e76662af34448d9904372f1256f59ce161f99) docs: fix the talosctl cluster create help output * [`6dc31be4f`](https://github.com/siderolabs/talos/commit/6dc31be4f982f62ba4aeb1b3b4e65ce022447eb4) fix: exclude new Virtual IPs configured with new config * [`94905c73e`](https://github.com/siderolabs/talos/commit/94905c73e93fd7dac38d911dc4264e4d0fe0081d) feat(talosctl): support running qemu x86 on Mac * [`f871ab241`](https://github.com/siderolabs/talos/commit/f871ab241c0f034401fbf61e32e7201cced49441) fix: provide json support in `nft` binary * [`694f45413`](https://github.com/siderolabs/talos/commit/694f45413fec8cc4f58a79e76034bd4bcec2bbdf) feat: external volumes * [`39feb16d2`](https://github.com/siderolabs/talos/commit/39feb16d2ed3bcb65d66483c0729bcec29f7b93e) fix: update containerd 2.2.0 with cgroups patch * [`82027eb9b`](https://github.com/siderolabs/talos/commit/82027eb9b30aa128099b27f638098d78857ecb4b) fix: bond configuration with new settings * [`121b13b8f`](https://github.com/siderolabs/talos/commit/121b13b8f8d6e5a487971f727c6e028c7ffa20f3) fix: disable kexec on arm64 * [`7eaa725d0`](https://github.com/siderolabs/talos/commit/7eaa725d0dba18392279f5b43d167aaf18f43b99) fix: selection of boot entry * [`949bdb90a`](https://github.com/siderolabs/talos/commit/949bdb90ab2fd711c47583d96bd29a1ca90bbf41) feat: add Secure Boot to CloudStack platform config * [`798143a88`](https://github.com/siderolabs/talos/commit/798143a886e4055e764a9ad17cefe8ad4db0572e) fix: discard better klog message from Kubernetes client * [`008cd0986`](https://github.com/siderolabs/talos/commit/008cd0986cbbbd5527d91c01b951e311ba014b97) fix: disable kexec in talosctl cluster create on arm64 * [`bb62b29ed`](https://github.com/siderolabs/talos/commit/bb62b29edb2fb704846ceeed2019f0ebaced30be) chore: prepare talos for 1.13 * [`c0935030a`](https://github.com/siderolabs/talos/commit/c0935030ac3d966149591a3aaa8e430da768d678) chore: fork reference docs for 1.13.x * [`e387e48b3`](https://github.com/siderolabs/talos/commit/e387e48b30b3a3b991f1f611099f48fddefa851b) fix: do not override DNS on MacOS * [`1e7e87fb1`](https://github.com/siderolabs/talos/commit/1e7e87fb192521937b581ecd94a0aa0c861f2a5f) fix: rework NFT rules for KubeSpan * [`51bcfb567`](https://github.com/siderolabs/talos/commit/51bcfb567915d2b27e4b5321e080220bc618086b) feat: rename image default and source bundle * [`585abe944`](https://github.com/siderolabs/talos/commit/585abe94431f06b3ebf4b6a64ad1b5918708f866) feat: update Kubernetes to v1.35.0-rc.1 * [`f301e3e9b`](https://github.com/siderolabs/talos/commit/f301e3e9ba47d5f46f1990a9bd21fd4e671c38f3) fix: update KubeSpan MSS clamping * [`74c1df6f4`](https://github.com/siderolabs/talos/commit/74c1df6f4b2ac8d989d1e42d6c7c0016411638ee) test: propagate MTU size to QEMU in `talosctl cluster create` * [`d347ca1af`](https://github.com/siderolabs/talos/commit/d347ca1af162c8d948899d58fc3f76dd0a94f138) fix: update CNI plugins to 1.9.0 * [`e3f8196b4`](https://github.com/siderolabs/talos/commit/e3f8196b4c767ca68df9f6c85ed25c7e12fb4d87) chore: update Grype and Syft * [`e1b8ab323`](https://github.com/siderolabs/talos/commit/e1b8ab3236e956bc4b37e227423aea0f97612a5c) docs: add misssing period * [`cd04c3dde`](https://github.com/siderolabs/talos/commit/cd04c3dde70f604603fd7996c62adf5a17cfbd41) docs: update release notes * [`fc8ae3249`](https://github.com/siderolabs/talos/commit/fc8ae3249fac82cbdb5521ca8797a8451bdaa9fd) docs: add omni join token example to create qemu command * [`9fa00773c`](https://github.com/siderolabs/talos/commit/9fa00773caf2d092d953ff58d04cf94803039b94) chore: update go-blockdevice * [`ba13b6786`](https://github.com/siderolabs/talos/commit/ba13b678654e2896e1a99b1af8b51a9239b0a559) fix: correct condition to use UKI cmdline in GRUB * [`d2ce3f47f`](https://github.com/siderolabs/talos/commit/d2ce3f47f8515231f27983abaaf269a059e2e90d) docs: drop machine.network example * [`cf087c1e0`](https://github.com/siderolabs/talos/commit/cf087c1e01bc1226049a57186f48b2e6b5739c5c) test: bird2 extension * [`13df94388`](https://github.com/siderolabs/talos/commit/13df943884a59bd1d42721ba42bcb36349d40624) fix: adapt SELinuxSuite.TestNoPtrace to new strace version * [`861787c38`](https://github.com/siderolabs/talos/commit/861787c380bff3ba2fa29f49837bc173a2719578) fix: mark secureboot as supported for metal * [`04e3e87ad`](https://github.com/siderolabs/talos/commit/04e3e87adcbd24ee0d82dce4cc27121d34d316f4) fix: clean up kubelet mounts * [`21057903a`](https://github.com/siderolabs/talos/commit/21057903a2ca01d88cc5f97c084567d1981f73c5) fix: clear provisioning data on SideroLink config change * [`0f9f4c05f`](https://github.com/siderolabs/talos/commit/0f9f4c05ffad9413e1f1533c68eae38dc91c9716) feat: update Kubernetes to 1.35.0-rc.0 * [`d4309d7b1`](https://github.com/siderolabs/talos/commit/d4309d7b1aec9d2852173fd704b09dfabe2cf217) fix: add a timeout for DNS resolving for NTP * [`dd6c1089c`](https://github.com/siderolabs/talos/commit/dd6c1089c8f30d815c80ab10544a0fef27ddd14c) feat: update Linux to 6.18.0 * [`e9a30bf9a`](https://github.com/siderolabs/talos/commit/e9a30bf9a8ee55ab9ae5d9c9a18362434b0202ad) test: revert add direct connectivity CA rotation test * [`cc95562bc`](https://github.com/siderolabs/talos/commit/cc95562bc830496986a395cdde352d48d4a1d146) fix: don't disable LACP by default * [`c9fe4679b`](https://github.com/siderolabs/talos/commit/c9fe4679bf9c1dcdf175b95a02f1eaacab4ff085) test: add platform acquire/not valid config unit-test * [`5a03a7a20`](https://github.com/siderolabs/talos/commit/5a03a7a20acffa8eedf40524f8d070e37e41f24e) chore: fix longhorn test * [`a0cfc3527`](https://github.com/siderolabs/talos/commit/a0cfc3527481c4784edf87c3d7823b10a21d1e4d) feat: implement logs persistence * [`51b732bea`](https://github.com/siderolabs/talos/commit/51b732beabc9948e58f9aa4d81b79afb9bd61243) fix: selection of boot entry * [`18f8ac369`](https://github.com/siderolabs/talos/commit/18f8ac369ba52f2640508134d3983f006f698129) feat: update Kubernetes to 1.35.0-beta.0 * [`92fa7c5e4`](https://github.com/siderolabs/talos/commit/92fa7c5e43da96a492003a2c9184cf818fbbb9f0) chore: update pkgs for NVIDIA 580.105.08 * [`f489299b6`](https://github.com/siderolabs/talos/commit/f489299b603a2aff0f292fa941ae8925fdda3492) chore: correct condition for running k8s integration tests * [`ab149750d`](https://github.com/siderolabs/talos/commit/ab149750d475ef059debfc3730e9e0a32ad6e601) chore: update tools/pkgs to 1.13.0-alpha.0 * [`87ff9f860`](https://github.com/siderolabs/talos/commit/87ff9f8606e04fe99e23261418a762372647b077) test: fix the image-factory test to pass IF endpoint * [`2ffe538e7`](https://github.com/siderolabs/talos/commit/2ffe538e7307f0ac3dbac2eba4b36ea98162ec78) test: add direct connectivity CA rotation test * [`70f6b80e0`](https://github.com/siderolabs/talos/commit/70f6b80e03acd507580211724cc51b7867bf8a76) chore(ci): skip multipath extension tests * [`561cfb60c`](https://github.com/siderolabs/talos/commit/561cfb60c313a9bdc70ed2ff2729549bc8c50fcb) chore: update pkgs and tools version * [`2f42202a7`](https://github.com/siderolabs/talos/commit/2f42202a7ccee0e33e43b2081929b5510db5d713) fix: simplify OOM expression * [`7b06ae8c2`](https://github.com/siderolabs/talos/commit/7b06ae8c2cf1069cb77cddee0986afc5af837bcc) test: fix flaky LinkSpec/Wireguard test * [`e715f3871`](https://github.com/siderolabs/talos/commit/e715f387137fa566a4824c051b624e013a93c49f) feat: present kernel log as `talosctl logs kernel` * [`e2ee39b8a`](https://github.com/siderolabs/talos/commit/e2ee39b8ac54ada49dd0a7ffaab4b0ae5d684792) fix: support specifying patch file without '@' symbol * [`e202b1f9e`](https://github.com/siderolabs/talos/commit/e202b1f9e82823aa5b31625024bce65bcc53b29f) fix: trim trailing dots from certificate SANs * [`7f7079f9c`](https://github.com/siderolabs/talos/commit/7f7079f9c0fbb30ce781aa1223d7df1a175a6206) fix: assign value of multicast setting properly * [`eba96141e`](https://github.com/siderolabs/talos/commit/eba96141e0afc147af9a8f1969e207501232b1de) feat: update etcd to 3.6.6 * [`9945ceef3`](https://github.com/siderolabs/talos/commit/9945ceef37b13bc6e93637dcf395a8c9019e60ed) docs: add API Server Cipher Suites changelog * [`9ed488d09`](https://github.com/siderolabs/talos/commit/9ed488d09648c09a9a5c1ed6a5cd245b84cd415d) feat: update TLS cipher suites for API server * [`f1c04e4d6`](https://github.com/siderolabs/talos/commit/f1c04e4d6af14243a328d22bf810f27b13d83898) feat: generate mirrors patch * [`a89108995`](https://github.com/siderolabs/talos/commit/a89108995ff13fbbef0bf5cbf429cede5ff81078) fix: add CA subject to generated certificate * [`35dd612a5`](https://github.com/siderolabs/talos/commit/35dd612a5e59d8781e147fc36eb14f3e8bc66811) fix: add more resilient move * [`83675838f`](https://github.com/siderolabs/talos/commit/83675838f3655b44cbd850fd82b4d17acfb00c33) feat: extend flags of cache-cert-gen * [`80ab7a064`](https://github.com/siderolabs/talos/commit/80ab7a0643fc8057283a8ba3eb912d0ee453c143) chore: remove spammy 'clean up unused volumes' logs * [`74d35900a`](https://github.com/siderolabs/talos/commit/74d35900af0f6451426b70eec3b6db4b72eb993c) chore: disable k8s integration tests for 1GiB worker nodes * [`4f6218674`](https://github.com/siderolabs/talos/commit/4f621867407ec8f568f67833172ebaf2ff400346) feat: support TALOS_HOME env var * [`0c59b3ea3`](https://github.com/siderolabs/talos/commit/0c59b3ea3f6bc49cef409a1456b4ffa3bf1d28df) feat: add multicast to linkconfig * [`6db06f4d5`](https://github.com/siderolabs/talos/commit/6db06f4d5d51abd9e80ead6e4417f0f68856c569) feat: implement multicast setting * [`eeded98f5`](https://github.com/siderolabs/talos/commit/eeded98f527a230c65cb041a29fefc5f693d9879) fix: add riscv64 talosctl to release artifacts * [`a6bbae91b`](https://github.com/siderolabs/talos/commit/a6bbae91bad56328851fa91e01c17b8af7340b3c) fix: fix typos across the project * [`83f2bdb9c`](https://github.com/siderolabs/talos/commit/83f2bdb9ce6c9466716a6ac9c94dc2222e569ee8) feat: support relative voume size

### Changes since v1.13.0-alpha.0
80 commits

* [`900516e68`](https://github.com/siderolabs/talos/commit/900516e68950e4b94696f6a9b481cefee44b3360) chore: update image signer * [`938de566e`](https://github.com/siderolabs/talos/commit/938de566eca30af3cc4355a94931186f19b682f2) feat: bump kernel * [`388cec727`](https://github.com/siderolabs/talos/commit/388cec72796d0ecd0c7103efcaab9066e9b62509) feat(overlays): add new overlays * [`9f2dd6312`](https://github.com/siderolabs/talos/commit/9f2dd6312f9d49e4d03347c98b100119f94cf807) refactor: api tests * [`a90783146`](https://github.com/siderolabs/talos/commit/a90783146fc2d475055bfce0f8b5120969f74dc7) feat: add a helper module to generate standard patches * [`1fec5b23d`](https://github.com/siderolabs/talos/commit/1fec5b23d0c10e53863a7c0f89f862708a7f4069) fix: implement merger for PercentageSize * [`8b245b8f2`](https://github.com/siderolabs/talos/commit/8b245b8f269b6c8cb463f2cf537d2ed2ab6924ec) feat: implement new image service APIs * [`d90c775b8`](https://github.com/siderolabs/talos/commit/d90c775b8441705003de3427b2e6831dcbfb449f) chore: rename internal `talosctl debug air-gapped` * [`2165280d0`](https://github.com/siderolabs/talos/commit/2165280d0eedf59899ad44e2f3289d81b3dab466) refactor: change the way one2many proxying is picked * [`b1b703dbe`](https://github.com/siderolabs/talos/commit/b1b703dbe2b25785ded0c77f23d674d9b9934975) chore: move sync logging code to go-kubernetes package * [`e48c6d7ab`](https://github.com/siderolabs/talos/commit/e48c6d7ab9c8a2e28ebe2115ac09f1557bbcca33) fix: allow to expose a port multiple times in Docker * [`410d8cb57`](https://github.com/siderolabs/talos/commit/410d8cb5727ccf054c9097f33bc916d87076a599) fix: undo CRLF on Windows (talosctl edit) * [`859d3f03c`](https://github.com/siderolabs/talos/commit/859d3f03c444d98b94a06adac3648562e3b1228b) feat: add RPi5 to the list of supported SBCs * [`0bd48bbc6`](https://github.com/siderolabs/talos/commit/0bd48bbc6f365770167ee753be563eb4179fcadb) fix(talosctl): pass --k8s-endpoint flag to rotate-ca kubernetes rotation * [`b9e27ebe7`](https://github.com/siderolabs/talos/commit/b9e27ebe72c4302c416fd8efb007c3966004ddd6) feat: update Linux kernel with dm-integrity * [`6aa9b0677`](https://github.com/siderolabs/talos/commit/6aa9b0677ed7ca4955fead474e36a533b3250ad9) fix: skip empty documents on config decoding * [`494492489`](https://github.com/siderolabs/talos/commit/494492489b29b615a8a874c0648690ed3b9adb58) fix: always set advertised peer URLs * [`782cc507d`](https://github.com/siderolabs/talos/commit/782cc507dc33c87caa5ff985eea5f4439c3e1012) fix: open the filesystem as read-only * [`28e61a740`](https://github.com/siderolabs/talos/commit/28e61a740a906fadfea098f38a9c9f4e8c32773e) fix: set GRUB prefix correctly on arm64 * [`a4f1c5239`](https://github.com/siderolabs/talos/commit/a4f1c5239ef7227856640c230e0d0364d9eedbd2) feat: update GRUB to 2.14 * [`562920701`](https://github.com/siderolabs/talos/commit/562920701e2999cbb6687e55de96719aba4064fd) fix: use node podCIDRs for kubespan advertiseKubernetesNetworks * [`39460365c`](https://github.com/siderolabs/talos/commit/39460365c1726095e20cf3cc7c079c234b8022d6) feat: implement layering for ProbeSpec * [`b5c760f70`](https://github.com/siderolabs/talos/commit/b5c760f7076570bc04be02af0ea493f95d8338d0) feat: add ProbeConfig for network connectivity probes * [`4b274f761`](https://github.com/siderolabs/talos/commit/4b274f76159495cc6c2977ec3bbade71e35aade8) feat: support aws cert manager in imager * [`417209512`](https://github.com/siderolabs/talos/commit/41720951251102f1c174e501a3103e55720a1d8b) fix: fallback to /proc/meminfo for memory modules * [`7f1147bed`](https://github.com/siderolabs/talos/commit/7f1147bed495a06d336f5be1da6073921b5e52dc) fix: add warnings to 802.3ad bond * [`ddd6b186e`](https://github.com/siderolabs/talos/commit/ddd6b186eb8f527324736576182dafbce3423da5) refactor: generate GRUB images * [`c7aa266ea`](https://github.com/siderolabs/talos/commit/c7aa266ea5c9d3fbd465dc651f2ebfec622612e7) fix: overwrite resolver config with machine config * [`cf70f05fa`](https://github.com/siderolabs/talos/commit/cf70f05fa40312c30d8345c2fb15ce8eda86a7a7) fix: oracle platform file format * [`8c7b8f5b7`](https://github.com/siderolabs/talos/commit/8c7b8f5b7d6dec144f7985a7c8a8a582c38f3154) feat: add support for negative max size * [`77bc3d21f`](https://github.com/siderolabs/talos/commit/77bc3d21fa40e188af4b5dd93e1cda289e858d56) fix: marshal of FailOverMac property * [`38e280c93`](https://github.com/siderolabs/talos/commit/38e280c9319ef1ecb1455b3cc8b8d0d1d7426ccd) fix: make OOM expression a bit less sensitive * [`3d1301640`](https://github.com/siderolabs/talos/commit/3d1301640d44d58303160400e4954c36f53341f9) fix: wipe the first/last 1MiB in addition to wiping by signatures * [`1aa6528ad`](https://github.com/siderolabs/talos/commit/1aa6528adcddfb6a5ed66cc26cac1a0fcdb37516) fix: make OOM controller more precise by considering separate cgroup PSI * [`f7072c050`](https://github.com/siderolabs/talos/commit/f7072c050e607de16781a65eb97ab2a1828b05fb) fix: check if the device is not mounted when wiping * [`743c3b94b`](https://github.com/siderolabs/talos/commit/743c3b94b958e4abcbf70d4064f2ae0e0bbb0712) fix: use correct containerd import path * [`f2dd08594`](https://github.com/siderolabs/talos/commit/f2dd08594e8e474c7b3891dc46c64f27c724dbc0) feat: report image pull progress in the console * [`72fe98a06`](https://github.com/siderolabs/talos/commit/72fe98a06f31536454f201d703f8ae6a071235b5) fix: boot with GRUB * [`d4ed13d93`](https://github.com/siderolabs/talos/commit/d4ed13d9394b087e8877eba25950f344894803a1) fix: add talos version to Hetzner Cloud client user agent * [`150c41c30`](https://github.com/siderolabs/talos/commit/150c41c30ed3f066f10bd2bdc2afa9b2c5a97597) feat: update Linux to 6.18.5 * [`01a367891`](https://github.com/siderolabs/talos/commit/01a3678913de0fa4d309a361428c117d24ce0d1e) fix: use append instead of prepend in service-account-issuer * [`d1954278a`](https://github.com/siderolabs/talos/commit/d1954278a1ba3470b2e5ccae90762078c18d69e9) feat: add extraArgs from service-account-issuer * [`91b88f7f9`](https://github.com/siderolabs/talos/commit/91b88f7f994cccad15cbec1aa8019bd19b84ae91) feat: support multiple values for extraArgs * [`96e604874`](https://github.com/siderolabs/talos/commit/96e604874b17e7aa8b62bfb25737f349e539bc5a) fix: add hostname to endpoints * [`7033275a7`](https://github.com/siderolabs/talos/commit/7033275a7a22d51e83c9e760ba37d2ad6ab22f28) refactor: move BootloaderKind into machinery * [`71adaf0ea`](https://github.com/siderolabs/talos/commit/71adaf0ea5b558c8a16e2acfdec3671611455985) fix: sort mirrors and tls configs when generating the machine config * [`34f09a300`](https://github.com/siderolabs/talos/commit/34f09a3004fe1b77c16dd33b04adca95fb6876a5) feat: add VLAN support to OpenStack platform * [`5127ef7c2`](https://github.com/siderolabs/talos/commit/5127ef7c28b360f9c7c033f77c58cef729e5278d) fix: wipe disk by signatures * [`415bfaedb`](https://github.com/siderolabs/talos/commit/415bfaedb6ae8d42b5927fdc5b7cfe8aa781a791) fix: panic in configpatcher when the whole section is missing * [`e5aca71cd`](https://github.com/siderolabs/talos/commit/e5aca71cd0557557e50c39d82eda2c938f627d62) fix: fix healthcheck timeout * [`634b71e2d`](https://github.com/siderolabs/talos/commit/634b71e2d028bf13d838acad8809c95384b6eed9) docs: move talosctl pcap example to Example Block * [`818492731`](https://github.com/siderolabs/talos/commit/8184927316c5de7d9b04f21474a60cc791c3d26d) feat: implement KubeSpan multi-document configuration * [`4d0604b9d`](https://github.com/siderolabs/talos/commit/4d0604b9d93851f444a00dbd84fcac76d21d35c2) chore: remove unrelated machineconfig * [`e36863470`](https://github.com/siderolabs/talos/commit/e36863470b14496c3d84417e63fef45e6060603b) feat: add it87 hwmon module * [`308c75090`](https://github.com/siderolabs/talos/commit/308c75090774d2510c2ec08e63e179a5c0fa6987) fix: resolve SideroLink Wireguard endpoint on reconnect * [`e4ef494de`](https://github.com/siderolabs/talos/commit/e4ef494decdf97664c4803aa3861015fce49760e) fix: drop the persist config flag from gen config * [`c3176adcf`](https://github.com/siderolabs/talos/commit/c3176adcf981811a326c971c81c4b591f54e116a) feat: add EnvironmentConfig document * [`c839b3880`](https://github.com/siderolabs/talos/commit/c839b38809b3a0029061d43477555ec31e283aa5) feat: expose more SSA options in the upgrade-k8s command * [`b8ff9677e`](https://github.com/siderolabs/talos/commit/b8ff9677e4f9a64908ae00bb1d80aa2442a00a60) fix: handle correctly incomplete RegistryTLSConfig * [`99f2ddada`](https://github.com/siderolabs/talos/commit/99f2ddada895011036af1435dd10bac3be0a9171) fix: bond config via platform * [`2449ffea4`](https://github.com/siderolabs/talos/commit/2449ffea45304459ea8895b535b6f070a9249172) fix: allow HostnameConfig to be used with incomplete machine config * [`35fc52087`](https://github.com/siderolabs/talos/commit/35fc5208728dbc3e0b139aff4c06f25208445637) fix: lock down etcd listen address to IPv4 localhost * [`27253d731`](https://github.com/siderolabs/talos/commit/27253d7317a473cbbc0f5c0eee634173bdd2eda7) feat: use new xfs config file * [`c9d84ae21`](https://github.com/siderolabs/talos/commit/c9d84ae21e203529a6952c165ff04d602a2a6ad6) fix: generate OCI-compliant image config * [`7a4b2b33a`](https://github.com/siderolabs/talos/commit/7a4b2b33abe8a3011f37f0a8f4848dd846d0396f) fix: update VIP config example * [`080efcbda`](https://github.com/siderolabs/talos/commit/080efcbda2c4334f9d8c70804a5a37f0cdb2df2d) feat: add k8s-version parameter to k8s-bundle * [`b764f5f72`](https://github.com/siderolabs/talos/commit/b764f5f724bf8af3acaac74942ea91a86e593322) fix: skip sync test when kube-proxy is disabled * [`70e67787d`](https://github.com/siderolabs/talos/commit/70e67787d6d34d93a34871b2d25d64f6a7575d76) feat: imager: populate filesystems with root owned files * [`7416dca59`](https://github.com/siderolabs/talos/commit/7416dca59378dc282e42ea30107cf40326cc593c) fix: print talosctl images to release notes * [`dc2009e47`](https://github.com/siderolabs/talos/commit/dc2009e4779684a6a4252d4dfd2aa02d1b60c2da) chore: use context when creating filesystems * [`85f7be6e3`](https://github.com/siderolabs/talos/commit/85f7be6e3f14bf160cf32bccf7418b31968d474f) chore: update slack links * [`154952175`](https://github.com/siderolabs/talos/commit/154952175ab73ac65722732b146a0ee1c56b2f4d) fix: disable swap for system services * [`d98b415af`](https://github.com/siderolabs/talos/commit/d98b415afea7b1820153151c0273df24a101742e) fix: drop more non-overlay SBC stuff * [`226cd6bc1`](https://github.com/siderolabs/talos/commit/226cd6bc1d70662cb7f7736ac6fad117170a36fb) fix: do not allocate for the actual disk image file * [`53f5bf8d2`](https://github.com/siderolabs/talos/commit/53f5bf8d2c97e91bee06bcb5948170015486ea77) fix: overlay installers * [`10d0cfd93`](https://github.com/siderolabs/talos/commit/10d0cfd93a083fb8b71b7c0297df52feb55e044b) fix: overlay install in image mode * [`77086694d`](https://github.com/siderolabs/talos/commit/77086694d18b69802e542156fc12cd7cf066efc2) fix: partition data population * [`4d5657b1a`](https://github.com/siderolabs/talos/commit/4d5657b1a34c939b63b2cc3ee11ed45ad1bf23c3) fix: drop SBC board code * [`c4f3f6d3e`](https://github.com/siderolabs/talos/commit/c4f3f6d3e59b58016ba8546c5bd3e8e465fbbf52) feat: implement kubernetes server-side apply * [`f12fd2b0a`](https://github.com/siderolabs/talos/commit/f12fd2b0a9fdf8f53ec5714d3ad18b695973e0b0) test: bump Image Factory tests

### Changes from siderolabs/go-kubernetes
3 commits

* [`604c56b`](https://github.com/siderolabs/go-kubernetes/commit/604c56b7251e8ec03b644b47c69ee08d6f25780b) chore: extract common code to the go-kubernetes package * [`ec0e3ae`](https://github.com/siderolabs/go-kubernetes/commit/ec0e3aefdeb332f4a44e669c9f7eb877b5f50963) chore: expose more ssa options * [`ad2fccd`](https://github.com/siderolabs/go-kubernetes/commit/ad2fccd09d137231f5a8187643782e0e1c661c44) feat: add SSA and pruning support

### Changes from siderolabs/pkgs
53 commits

* [`dc737a6`](https://github.com/siderolabs/pkgs/commit/dc737a68c470c9498ec11bde09196809355d2463) chore: update kernel * [`9b118b3`](https://github.com/siderolabs/pkgs/commit/9b118b3d0fe7f0df06a069065b86ab307fef3375) chore: update deps * [`a63c227`](https://github.com/siderolabs/pkgs/commit/a63c2276eea0013463487cebf95ee35a37c5d9f6) feat: update OpenSSL to v3.6.1 * [`da7ab57`](https://github.com/siderolabs/pkgs/commit/da7ab5776bd1a6c551bfc6fe5919114721da0e1f) feat: add px-fuse pkg * [`553e0fb`](https://github.com/siderolabs/pkgs/commit/553e0fb70f076a8bc53e283253b30ff819e627ff) feat: enable dm-integrity * [`15a3cdf`](https://github.com/siderolabs/pkgs/commit/15a3cdf54884d5169895a1ff46682373688ac5e2) feat: update Linux to 6.18.6 * [`b518a19`](https://github.com/siderolabs/pkgs/commit/b518a196de93dd33e70faaff2342f67acb7dc49b) feat: update dependencies * [`1b4fbf5`](https://github.com/siderolabs/pkgs/commit/1b4fbf56b270d5669116fa0d8f91a3b9495e0d97) feat: update GRUB to 2.14 * [`30bc671`](https://github.com/siderolabs/pkgs/commit/30bc671d4be566ebf60b820edd54000616262e79) fix: enable pinctrl for Raspberry Pi 5 * [`375983f`](https://github.com/siderolabs/pkgs/commit/375983f4685484a8be5796f815629a9a0d8bd146) feat: update Go to 1.25.6 * [`d445c80`](https://github.com/siderolabs/pkgs/commit/d445c8076b7dd18b04f48e0a7e5cc2e50b3064d0) feat: update Linux to 6.18.5 * [`6994400`](https://github.com/siderolabs/pkgs/commit/69944002f9ee681220dcb23031c23ee327e6c1f2) feat: update NVIDIA LTS and production driver versions * [`05c3d85`](https://github.com/siderolabs/pkgs/commit/05c3d856b7de6eb64af718d7266a5adf15e1224b) feat: update Linux firmware to 20260110 * [`c61b466`](https://github.com/siderolabs/pkgs/commit/c61b466e130015b44962e7ef3bc1e9bec935b1df) feat: enable IT87 hwmon module * [`ae2572e`](https://github.com/siderolabs/pkgs/commit/ae2572e894a3d8d951418d447ec02f6cc65c8e72) feat: enable IPV6_MROUTE * [`d6b503e`](https://github.com/siderolabs/pkgs/commit/d6b503e0fe75d52f83d656a3460cb3614b352e51) feat: add RK3588 NPU Support * [`df4b4c8`](https://github.com/siderolabs/pkgs/commit/df4b4c885d4aabf702ce03bcb341f5b5f3641d76) feat: bump deps * [`a220898`](https://github.com/siderolabs/pkgs/commit/a2208985bd756ef6366497c5f9768e814b3f7583) feat: add libarchive * [`c2371b5`](https://github.com/siderolabs/pkgs/commit/c2371b5582836e27b3e80c4404c4ff5fbed90291) feat: enable ZRAM support * [`ab4d169`](https://github.com/siderolabs/pkgs/commit/ab4d169ad93203ba56b0677a10e78eb3e623762e) feat: add a patch to force uid when populating from a directory * [`972f44d`](https://github.com/siderolabs/pkgs/commit/972f44d5dae53809ef337544c52c835373439d34) feat: update dependencies * [`f8eb5b0`](https://github.com/siderolabs/pkgs/commit/f8eb5b02aaebaf76c59e71f57f4a689dc727e769) feat: update Linux to 6.18.2 * [`3fb6291`](https://github.com/siderolabs/pkgs/commit/3fb629109a7e5f9650d0e641ff5076a29c319448) feat: update systemd to 259 * [`59241bd`](https://github.com/siderolabs/pkgs/commit/59241bd58eeb07a18af1c9fc8fffff6365ecca0d) fix: add SBOMs for pigz/igzip * [`9377c78`](https://github.com/siderolabs/pkgs/commit/9377c786d112b4181f1e373f6e513130f11b7801) feat: optimize decompression for containerd * [`e8e61ce`](https://github.com/siderolabs/pkgs/commit/e8e61cedbbd687ed958db992e05b5d59e4a8ea60) feat: update containerd to 2.2.1 * [`daa74ba`](https://github.com/siderolabs/pkgs/commit/daa74bab83f91bbc4b6c42625d2953299d5fe20a) feat: support xfs filesystem reproducibility * [`1f66513`](https://github.com/siderolabs/pkgs/commit/1f665130fbda76478c261dd54e3843c15027c9cd) feat: update OpenZFS to 2.4.0 * [`b209af5`](https://github.com/siderolabs/pkgs/commit/b209af5baf1a67472ef431e5a8b7d48022392a1e) chore: rekres with latest changes * [`2b806b9`](https://github.com/siderolabs/pkgs/commit/2b806b9b2a7e05b97c2a7e8572e3a8edbd3721d3) feat: bump dependencies * [`65242fd`](https://github.com/siderolabs/pkgs/commit/65242fd0fef5c9c923aacce23d1655bad0d1b3e3) feat: enable CONFIG_MISC_RP1 in ARM64 config * [`4daecd8`](https://github.com/siderolabs/pkgs/commit/4daecd8e7b8d87110a9e552a60a5394014294e08) feat: update Linux to 6.18.1 * [`9868a66`](https://github.com/siderolabs/pkgs/commit/9868a66e3c000f505c97ff68e61abac9c9e8e4c9) feat: enable Powercap and Intel RAPL * [`07883ee`](https://github.com/siderolabs/pkgs/commit/07883eee3729d4d3adaaebcd825452934c3baebb) feat: build and package perf binary * [`47abca0`](https://github.com/siderolabs/pkgs/commit/47abca0852b9555d88eba61661c65a7f93ec3590) fix: add json support to nftables binary * [`b961ff8`](https://github.com/siderolabs/pkgs/commit/b961ff898fc9eae68d7f3cea2ca22ff4d0b9c99d) feat: patch containerd 2.2.0 with cgroups fix patch * [`b7dd7f6`](https://github.com/siderolabs/pkgs/commit/b7dd7f6c809f670f058b78fd3b84f4cb977771cb) feat: add mstflint module * [`ae53351`](https://github.com/siderolabs/pkgs/commit/ae5335198e009da7b06bc0f0d6f42b0947650fc0) feat: update ZFS to 2.4.0-rc5 * [`b8edf01`](https://github.com/siderolabs/pkgs/commit/b8edf0168171ffc5b87fcd962e37d5c2cd25b687) feat: update CNI plugins to v1.9.0 * [`a57c1b0`](https://github.com/siderolabs/pkgs/commit/a57c1b0c9d143559a87b64fe9570eec39c14a771) feat: enable amd sev-snp * [`68562c1`](https://github.com/siderolabs/pkgs/commit/68562c1b4cdba656287021a1694440b2a7e4d24d) feat: update Linux to 6.18 * [`6f4ff8c`](https://github.com/siderolabs/pkgs/commit/6f4ff8cc9f57452707588c05e5ca4e80c56548d2) feat: enable Amlogic Meson PCIe controller driver * [`c41127b`](https://github.com/siderolabs/pkgs/commit/c41127b94d22b9a5cb6b93f49b546f2ff477410c) feat: enable Intel GPIO/Pinctrl kernel modules * [`4a31ff7`](https://github.com/siderolabs/pkgs/commit/4a31ff7dd5c9266b68abded53a7399cb8102f4e3) feat: update NVIDIA LTS to 580.105.08 * [`3e858d3`](https://github.com/siderolabs/pkgs/commit/3e858d3fa5b2719d8d83397fb89c2ffc91f86615) chore: fork pkgs for Talos 1.13 * [`dcc5aa1`](https://github.com/siderolabs/pkgs/commit/dcc5aa1e71d6b2e9374d41029a2e6de22dbc61ce) feat: update runc to 1.3.4 * [`8b6ae5b`](https://github.com/siderolabs/pkgs/commit/8b6ae5b7fc22c3bb2df4bbe31190ff90b0986e6f) fix: regenerate configs * [`2992598`](https://github.com/siderolabs/pkgs/commit/29925980896df1978a020505b2b061ffdbd240c7) fix: add missing kernel config entries * [`c8ea18a`](https://github.com/siderolabs/pkgs/commit/c8ea18a0873f5b31c54d567ef97d8d05634eb506) feat: rekres to alow multiple commits * [`2ddef8b`](https://github.com/siderolabs/pkgs/commit/2ddef8b65755610fc6dbb3f1fb976a6bc572478f) chore: update dependencies * [`d1f28e0`](https://github.com/siderolabs/pkgs/commit/d1f28e058972174af9ac819783a69f5f6596b37d) chore: update dependencies * [`ab253f5`](https://github.com/siderolabs/pkgs/commit/ab253f521d95b30710e258ebb54adbb7b8de8970) feat: enable gpio-fan module * [`0b10666`](https://github.com/siderolabs/pkgs/commit/0b1066635d9dd255bf0ad936e21099fd4bd03f1e) chore: use ubuntu mirrors

### Changes from siderolabs/proto-codec
1 commit

* [`bd9c491`](https://github.com/siderolabs/proto-codec/commit/bd9c491b9e84d7274728ce7e3bde14009f5224bd) chore: bump and update dependencies

### Changes from siderolabs/tools
13 commits

* [`ca26e1c`](https://github.com/siderolabs/tools/commit/ca26e1c38cd0a76eb981db4dad2e6caccb0bbe4d) chore: update deps * [`0281af0`](https://github.com/siderolabs/tools/commit/0281af0545e17c409fb32d8db61a7d9b0ad8b1c2) feat: update OpenSSL to 3.6.1 * [`721ad07`](https://github.com/siderolabs/tools/commit/721ad073f18b41407882727a3f0061e594f6c955) feat: update dependencies * [`2b3f514`](https://github.com/siderolabs/tools/commit/2b3f514d42a343d98c79a487e80bd4f225a41b70) fix: reproducible build for nasm * [`98c699e`](https://github.com/siderolabs/tools/commit/98c699eb624d0846455f08db77cc14e446cb6db9) feat: update Go to 1.25.6 * [`cd5eb66`](https://github.com/siderolabs/tools/commit/cd5eb66bb0de4fb468a860e176267c3420b4a3a1) chore: run rekres and update dependencies * [`896f8b9`](https://github.com/siderolabs/tools/commit/896f8b9c1f88cd190d11b8ef3baa2c36e73d6dfe) fix: add sbom for zlib-ng * [`543a16f`](https://github.com/siderolabs/tools/commit/543a16fedf7170d8b015ea1391817328205e629a) feat: replace zlib -> zlib-ng, add nasm * [`b67c1a1`](https://github.com/siderolabs/tools/commit/b67c1a168b302539d2082a5513c4a0130c30e4df) chore: rekres with latest changes * [`5e087cb`](https://github.com/siderolabs/tools/commit/5e087cbcd158db1ce4f447145bd76a24d07159a1) feat: bump dependencies * [`da96a27`](https://github.com/siderolabs/tools/commit/da96a2771801627b4715f7a13199aa6846f87732) chore: rekres to fix reproducibility * [`e283ec8`](https://github.com/siderolabs/tools/commit/e283ec8d3831bb19b26938afb10f4955ea563ce2) feat: update Go to 1.25.5 * [`c38ff0c`](https://github.com/siderolabs/tools/commit/c38ff0c03be69e5cc3795d9dc055896604a3041c) chore: update to 1.13.0-alpha.0 toolchain

### Dependency Changes * **github.com/aws/aws-sdk-go-v2/config** v1.31.20 -> v1.32.6 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.18.13 -> v1.18.16 * **github.com/aws/aws-sdk-go-v2/service/acm** v1.37.19 **_new_** * **github.com/aws/aws-sdk-go-v2/service/kms** v1.46.0 -> v1.49.4 * **github.com/aws/smithy-go** v1.23.2 -> v1.24.0 * **github.com/containerd/cgroups/v3** v3.0.5 -> v3.1.0 * **github.com/containerd/containerd/api** v1.9.0 -> v1.10.0 * **github.com/containerd/containerd/v2** v2.1.5 -> v2.2.0 * **github.com/containerd/platforms** v1.0.0-rc.1 -> v1.0.0-rc.2 * **github.com/cosi-project/runtime** v1.12.0 -> v1.13.0 * **github.com/diskfs/go-diskfs** fc569a00ea19 **_new_** * **github.com/docker/cli** v29.0.0 -> v29.1.3 * **github.com/gdamore/tcell/v2** v2.9.0 -> v2.13.4 * **github.com/godbus/dbus/v5** v5.1.0 -> v5.2.0 * **github.com/google/cadvisor** v0.53.0 -> v0.54.1 * **github.com/google/go-containerregistry** v0.20.6 -> v0.20.7 * **github.com/hetznercloud/hcloud-go/v2** v2.30.0 -> v2.32.0 * **github.com/klauspost/compress** v1.18.1 -> v1.18.3 * **github.com/linode/go-metadata** v0.2.2 -> v0.2.3 * **github.com/mdlayher/ethtool** v0.4.0 -> v0.5.0 * **github.com/miekg/dns** v1.1.68 -> v1.1.69 * **github.com/moby/moby/client** v0.1.0 -> v0.2.1 * **github.com/siderolabs/go-blockdevice/v2** v2.0.20 -> v2.0.23 * **github.com/siderolabs/go-kubernetes** v0.2.28 -> v0.2.31 * **github.com/siderolabs/pkgs** v1.12.0-23-ge0b78b8 -> v1.13.0-alpha.0-44-gdc737a6 * **github.com/siderolabs/proto-codec** v0.1.2 -> v0.1.3 * **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.13.0-alpha.0 * **github.com/siderolabs/tools** v1.12.0-2-g7d57df0 -> v1.13.0-alpha.0-12-gca26e1c * **github.com/sirupsen/logrus** v1.9.3 -> dd1b4c2e81af * **go.etcd.io/etcd/api/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/pkg/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/etcdutl/v3** v3.6.6 -> v3.6.7 * **go.uber.org/zap** v1.27.0 -> v1.27.1 * **golang.org/x/net** v0.47.0 -> v0.48.0 * **golang.org/x/oauth2** v0.33.0 -> v0.34.0 * **golang.org/x/sync** v0.18.0 -> v0.19.0 * **golang.org/x/sys** v0.38.0 -> v0.40.0 * **golang.org/x/term** v0.37.0 -> v0.38.0 * **golang.org/x/text** v0.31.0 -> v0.33.0 * **google.golang.org/grpc** v1.76.0 -> v1.77.0 * **google.golang.org/protobuf** v1.36.10 -> v1.36.11 * **sigs.k8s.io/cli-utils** 77c836a69463 **_new_** Previous release can be found at [v1.12.0](https://github.com/siderolabs/talos/releases/tag/v1.12.0) ## [Talos 1.13.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.13.0-alpha.0) (2025-12-25) Welcome to the v1.13.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### External Volumes Talos now supports virtiofs-based external volumes via the new [ExternalVolumeConfig](https://www.talos.dev/v1.13/reference/configuration/block/externalvolumeconfig/) document. These virtiofs external volumes are not supported when SELinux is running in enforcing mode. ### Talos Imager Enhancements Talos imager now supports running rootless. `--privileged` and `-v /dev:/dev` are no longer required. ### Container Image Decompression Talos now ships with `igzip` (amd64) and `pigz` (arm64) to speed up container image decompression. ### /proc/PID/mem Access Hardening A new kernel parameter `proc_mem.force_override=never` has been introduced by default to enhance system security by preventing unwanted writes to protected process memory via `/proc/PID/mem`. If the kernel parameter is removed, default behavior is restored, allowing access only if the process is traced. ### Reproducible Disk Images Talos disk images are now reproducible. Building the same version of Talos multiple times will yield identical disk images. Note: VHD and VMDK (Azure and VMware) images are not currently reproducible due to limitations in the underlying image creation tools. Users verifying reproducible images should use raw images, verify checksums, and convert them to VHD/VMDK as needed. ### Component Updates Linux: 6.18.2 containerd: 2.2.1 etcd: 3.6.7 CoreDNS: 1.13.2 Kubernetes: 1.35.0 Flannel CNI plugin: v1.9.0-flannel1 LVM2: 2_03_38 runc: 1.4.0 systemd: 259 cryptsetup: 2.8.3 Talos is built with Go 1.25.5. ### VM Hot-Add Support Talos now includes udev rules to support hot-adding of CPUs in virtualized environments. ### Contributors * Andrey Smirnov * Mateusz Urbanek * Noel Georgi * Dmitrii Sharshakov * Laura Brehm * Bryan Lee * Edward Sammut Alessi * Birger Johan Nordølum * Christopher Puschmann * Jaakko Sirén * Jean-Francois Roy * Joakim Nohlgård * Justin Garrison * Lennard Klein * Michal Baumgartner * Orzelius * Serge van Ginderachter * Skye Soss * dataprolet * eseiker * pranav767 ### Changes
95 commits

* [`f0d8a6851`](https://github.com/siderolabs/talos/commit/f0d8a685173354e5fd148786872062a342c4282a) test: skip the source bundle on exact tag * [`c57701d65`](https://github.com/siderolabs/talos/commit/c57701d6590388e7d6418af67e8237c7d60ccf54) fix: remove interactive installer * [`43937c1cd`](https://github.com/siderolabs/talos/commit/43937c1cd42758a15026261fe8f0e06daaebdcbd) feat: update Linux and systemd * [`72a194df8`](https://github.com/siderolabs/talos/commit/72a194df88f2800cee3372241fbad419b07f7bbf) feat: add VM CPU hot-add rules * [`f09ae1e0d`](https://github.com/siderolabs/talos/commit/f09ae1e0d2e1b7842d504b594b71a325af7733e5) fix: probe small images correctly * [`8f2b33799`](https://github.com/siderolabs/talos/commit/8f2b337994fdeff76a0ae9e1730b4b9f596ff1bb) feat: imager support rootless builds * [`c7525a97e`](https://github.com/siderolabs/talos/commit/c7525a97ef8615e903be183d7938b6d2a3b89464) feat: support creating filesystems from folder * [`e2bffb5ce`](https://github.com/siderolabs/talos/commit/e2bffb5cebaaf28f9dfff24f41ecbb2809fc60e5) chore: refactor imager code so it's more clear * [`0fb50dbd0`](https://github.com/siderolabs/talos/commit/0fb50dbd0a5b7b80187e50d501cec4b3fe434dc2) fix: invalid versions check in talos-bundle * [`b5dd56032`](https://github.com/siderolabs/talos/commit/b5dd5603207a46d8eed240173f06aeffd6a9c0e7) test: upgrade versions in upgrade tests * [`3dfa4d6e4`](https://github.com/siderolabs/talos/commit/3dfa4d6e40dcae2db47e89443568be3ae48b3ae1) fix: make upgrade work with SELinux enforcing=1 * [`786c8e2ee`](https://github.com/siderolabs/talos/commit/786c8e2ee757c2d7b30d5bded954e584af3a058e) feat: ship pigz/igzip in rootfs to speed up image decompression * [`48d242918`](https://github.com/siderolabs/talos/commit/48d242918bc97e6a01434bee6fcdcfa735fd1f5a) feat: update containerd to 2.2.1 * [`536541afe`](https://github.com/siderolabs/talos/commit/536541afe497d5f61cfcd0c01cf580ab5b3be164) fix: mount volume mount/unmount race * [`39117d457`](https://github.com/siderolabs/talos/commit/39117d45766b139ed6a0c1290f757e4b26d31d92) feat: update dependencies * [`f0f420725`](https://github.com/siderolabs/talos/commit/f0f420725c6a4f628cdc1b80d59713c375beb9b7) fix: bond setting change detection * [`8d6a7a867`](https://github.com/siderolabs/talos/commit/8d6a7a8677a5d1d61432fa94ca030351fd9852f2) feat: update Kubernetes to 1.35.0 * [`845a0d09c`](https://github.com/siderolabs/talos/commit/845a0d09cd770a15db762ddda4d3d27f58656cfe) feat: update etcd 3.6.7, CoreDNS 1.13.2 * [`b95912e04`](https://github.com/siderolabs/talos/commit/b95912e04907b78bd06987c6d3948f8f1804d844) feat: enforce `proc_mem.force_override=never` by default * [`681f3e84c`](https://github.com/siderolabs/talos/commit/681f3e84c85677f49ddbcd4a47e325d4a85af692) test: run virtiofs tests only when virtiofsd is running * [`0592ff0cd`](https://github.com/siderolabs/talos/commit/0592ff0cdbf54475dc91bfb7c9b9c3047bbe13da) fix: drop the Omni API URL check on IP address * [`a4879a5fa`](https://github.com/siderolabs/talos/commit/a4879a5fa2ded9b7b52ff7506b5493ae12939bba) feat: update Linux to 6.18.1 * [`43b43ff18`](https://github.com/siderolabs/talos/commit/43b43ff189b7e5f37eaa75f4926c26ee21ffa5cb) docs: split talosctl commands into groups * [`6d17c18bf`](https://github.com/siderolabs/talos/commit/6d17c18bf908d3cd69ff920d0cff67b653a385f3) feat: enable Powercap and Intel RAPL * [`884e76662`](https://github.com/siderolabs/talos/commit/884e76662af34448d9904372f1256f59ce161f99) docs: fix the talosctl cluster create help output * [`6dc31be4f`](https://github.com/siderolabs/talos/commit/6dc31be4f982f62ba4aeb1b3b4e65ce022447eb4) fix: exclude new Virtual IPs configured with new config * [`94905c73e`](https://github.com/siderolabs/talos/commit/94905c73e93fd7dac38d911dc4264e4d0fe0081d) feat(talosctl): support running qemu x86 on Mac * [`f871ab241`](https://github.com/siderolabs/talos/commit/f871ab241c0f034401fbf61e32e7201cced49441) fix: provide json support in `nft` binary * [`694f45413`](https://github.com/siderolabs/talos/commit/694f45413fec8cc4f58a79e76034bd4bcec2bbdf) feat: external volumes * [`39feb16d2`](https://github.com/siderolabs/talos/commit/39feb16d2ed3bcb65d66483c0729bcec29f7b93e) fix: update containerd 2.2.0 with cgroups patch * [`82027eb9b`](https://github.com/siderolabs/talos/commit/82027eb9b30aa128099b27f638098d78857ecb4b) fix: bond configuration with new settings * [`121b13b8f`](https://github.com/siderolabs/talos/commit/121b13b8f8d6e5a487971f727c6e028c7ffa20f3) fix: disable kexec on arm64 * [`7eaa725d0`](https://github.com/siderolabs/talos/commit/7eaa725d0dba18392279f5b43d167aaf18f43b99) fix: selection of boot entry * [`949bdb90a`](https://github.com/siderolabs/talos/commit/949bdb90ab2fd711c47583d96bd29a1ca90bbf41) feat: add Secure Boot to CloudStack platform config * [`798143a88`](https://github.com/siderolabs/talos/commit/798143a886e4055e764a9ad17cefe8ad4db0572e) fix: discard better klog message from Kubernetes client * [`008cd0986`](https://github.com/siderolabs/talos/commit/008cd0986cbbbd5527d91c01b951e311ba014b97) fix: disable kexec in talosctl cluster create on arm64 * [`bb62b29ed`](https://github.com/siderolabs/talos/commit/bb62b29edb2fb704846ceeed2019f0ebaced30be) chore: prepare talos for 1.13 * [`c0935030a`](https://github.com/siderolabs/talos/commit/c0935030ac3d966149591a3aaa8e430da768d678) chore: fork reference docs for 1.13.x * [`e387e48b3`](https://github.com/siderolabs/talos/commit/e387e48b30b3a3b991f1f611099f48fddefa851b) fix: do not override DNS on MacOS * [`1e7e87fb1`](https://github.com/siderolabs/talos/commit/1e7e87fb192521937b581ecd94a0aa0c861f2a5f) fix: rework NFT rules for KubeSpan * [`51bcfb567`](https://github.com/siderolabs/talos/commit/51bcfb567915d2b27e4b5321e080220bc618086b) feat: rename image default and source bundle * [`585abe944`](https://github.com/siderolabs/talos/commit/585abe94431f06b3ebf4b6a64ad1b5918708f866) feat: update Kubernetes to v1.35.0-rc.1 * [`f301e3e9b`](https://github.com/siderolabs/talos/commit/f301e3e9ba47d5f46f1990a9bd21fd4e671c38f3) fix: update KubeSpan MSS clamping * [`74c1df6f4`](https://github.com/siderolabs/talos/commit/74c1df6f4b2ac8d989d1e42d6c7c0016411638ee) test: propagate MTU size to QEMU in `talosctl cluster create` * [`d347ca1af`](https://github.com/siderolabs/talos/commit/d347ca1af162c8d948899d58fc3f76dd0a94f138) fix: update CNI plugins to 1.9.0 * [`e3f8196b4`](https://github.com/siderolabs/talos/commit/e3f8196b4c767ca68df9f6c85ed25c7e12fb4d87) chore: update Grype and Syft * [`e1b8ab323`](https://github.com/siderolabs/talos/commit/e1b8ab3236e956bc4b37e227423aea0f97612a5c) docs: add misssing period * [`cd04c3dde`](https://github.com/siderolabs/talos/commit/cd04c3dde70f604603fd7996c62adf5a17cfbd41) docs: update release notes * [`fc8ae3249`](https://github.com/siderolabs/talos/commit/fc8ae3249fac82cbdb5521ca8797a8451bdaa9fd) docs: add omni join token example to create qemu command * [`9fa00773c`](https://github.com/siderolabs/talos/commit/9fa00773caf2d092d953ff58d04cf94803039b94) chore: update go-blockdevice * [`ba13b6786`](https://github.com/siderolabs/talos/commit/ba13b678654e2896e1a99b1af8b51a9239b0a559) fix: correct condition to use UKI cmdline in GRUB * [`d2ce3f47f`](https://github.com/siderolabs/talos/commit/d2ce3f47f8515231f27983abaaf269a059e2e90d) docs: drop machine.network example * [`cf087c1e0`](https://github.com/siderolabs/talos/commit/cf087c1e01bc1226049a57186f48b2e6b5739c5c) test: bird2 extension * [`13df94388`](https://github.com/siderolabs/talos/commit/13df943884a59bd1d42721ba42bcb36349d40624) fix: adapt SELinuxSuite.TestNoPtrace to new strace version * [`861787c38`](https://github.com/siderolabs/talos/commit/861787c380bff3ba2fa29f49837bc173a2719578) fix: mark secureboot as supported for metal * [`04e3e87ad`](https://github.com/siderolabs/talos/commit/04e3e87adcbd24ee0d82dce4cc27121d34d316f4) fix: clean up kubelet mounts * [`21057903a`](https://github.com/siderolabs/talos/commit/21057903a2ca01d88cc5f97c084567d1981f73c5) fix: clear provisioning data on SideroLink config change * [`0f9f4c05f`](https://github.com/siderolabs/talos/commit/0f9f4c05ffad9413e1f1533c68eae38dc91c9716) feat: update Kubernetes to 1.35.0-rc.0 * [`d4309d7b1`](https://github.com/siderolabs/talos/commit/d4309d7b1aec9d2852173fd704b09dfabe2cf217) fix: add a timeout for DNS resolving for NTP * [`dd6c1089c`](https://github.com/siderolabs/talos/commit/dd6c1089c8f30d815c80ab10544a0fef27ddd14c) feat: update Linux to 6.18.0 * [`e9a30bf9a`](https://github.com/siderolabs/talos/commit/e9a30bf9a8ee55ab9ae5d9c9a18362434b0202ad) test: revert add direct connectivity CA rotation test * [`cc95562bc`](https://github.com/siderolabs/talos/commit/cc95562bc830496986a395cdde352d48d4a1d146) fix: don't disable LACP by default * [`c9fe4679b`](https://github.com/siderolabs/talos/commit/c9fe4679bf9c1dcdf175b95a02f1eaacab4ff085) test: add platform acquire/not valid config unit-test * [`5a03a7a20`](https://github.com/siderolabs/talos/commit/5a03a7a20acffa8eedf40524f8d070e37e41f24e) chore: fix longhorn test * [`a0cfc3527`](https://github.com/siderolabs/talos/commit/a0cfc3527481c4784edf87c3d7823b10a21d1e4d) feat: implement logs persistence * [`51b732bea`](https://github.com/siderolabs/talos/commit/51b732beabc9948e58f9aa4d81b79afb9bd61243) fix: selection of boot entry * [`18f8ac369`](https://github.com/siderolabs/talos/commit/18f8ac369ba52f2640508134d3983f006f698129) feat: update Kubernetes to 1.35.0-beta.0 * [`92fa7c5e4`](https://github.com/siderolabs/talos/commit/92fa7c5e43da96a492003a2c9184cf818fbbb9f0) chore: update pkgs for NVIDIA 580.105.08 * [`f489299b6`](https://github.com/siderolabs/talos/commit/f489299b603a2aff0f292fa941ae8925fdda3492) chore: correct condition for running k8s integration tests * [`ab149750d`](https://github.com/siderolabs/talos/commit/ab149750d475ef059debfc3730e9e0a32ad6e601) chore: update tools/pkgs to 1.13.0-alpha.0 * [`87ff9f860`](https://github.com/siderolabs/talos/commit/87ff9f8606e04fe99e23261418a762372647b077) test: fix the image-factory test to pass IF endpoint * [`2ffe538e7`](https://github.com/siderolabs/talos/commit/2ffe538e7307f0ac3dbac2eba4b36ea98162ec78) test: add direct connectivity CA rotation test * [`70f6b80e0`](https://github.com/siderolabs/talos/commit/70f6b80e03acd507580211724cc51b7867bf8a76) chore(ci): skip multipath extension tests * [`561cfb60c`](https://github.com/siderolabs/talos/commit/561cfb60c313a9bdc70ed2ff2729549bc8c50fcb) chore: update pkgs and tools version * [`2f42202a7`](https://github.com/siderolabs/talos/commit/2f42202a7ccee0e33e43b2081929b5510db5d713) fix: simplify OOM expression * [`7b06ae8c2`](https://github.com/siderolabs/talos/commit/7b06ae8c2cf1069cb77cddee0986afc5af837bcc) test: fix flaky LinkSpec/Wireguard test * [`e715f3871`](https://github.com/siderolabs/talos/commit/e715f387137fa566a4824c051b624e013a93c49f) feat: present kernel log as `talosctl logs kernel` * [`e2ee39b8a`](https://github.com/siderolabs/talos/commit/e2ee39b8ac54ada49dd0a7ffaab4b0ae5d684792) fix: support specifying patch file without '@' symbol * [`e202b1f9e`](https://github.com/siderolabs/talos/commit/e202b1f9e82823aa5b31625024bce65bcc53b29f) fix: trim trailing dots from certificate SANs * [`7f7079f9c`](https://github.com/siderolabs/talos/commit/7f7079f9c0fbb30ce781aa1223d7df1a175a6206) fix: assign value of multicast setting properly * [`eba96141e`](https://github.com/siderolabs/talos/commit/eba96141e0afc147af9a8f1969e207501232b1de) feat: update etcd to 3.6.6 * [`9945ceef3`](https://github.com/siderolabs/talos/commit/9945ceef37b13bc6e93637dcf395a8c9019e60ed) docs: add API Server Cipher Suites changelog * [`9ed488d09`](https://github.com/siderolabs/talos/commit/9ed488d09648c09a9a5c1ed6a5cd245b84cd415d) feat: update TLS cipher suites for API server * [`f1c04e4d6`](https://github.com/siderolabs/talos/commit/f1c04e4d6af14243a328d22bf810f27b13d83898) feat: generate mirrors patch * [`a89108995`](https://github.com/siderolabs/talos/commit/a89108995ff13fbbef0bf5cbf429cede5ff81078) fix: add CA subject to generated certificate * [`35dd612a5`](https://github.com/siderolabs/talos/commit/35dd612a5e59d8781e147fc36eb14f3e8bc66811) fix: add more resilient move * [`83675838f`](https://github.com/siderolabs/talos/commit/83675838f3655b44cbd850fd82b4d17acfb00c33) feat: extend flags of cache-cert-gen * [`80ab7a064`](https://github.com/siderolabs/talos/commit/80ab7a0643fc8057283a8ba3eb912d0ee453c143) chore: remove spammy 'clean up unused volumes' logs * [`74d35900a`](https://github.com/siderolabs/talos/commit/74d35900af0f6451426b70eec3b6db4b72eb993c) chore: disable k8s integration tests for 1GiB worker nodes * [`4f6218674`](https://github.com/siderolabs/talos/commit/4f621867407ec8f568f67833172ebaf2ff400346) feat: support TALOS_HOME env var * [`0c59b3ea3`](https://github.com/siderolabs/talos/commit/0c59b3ea3f6bc49cef409a1456b4ffa3bf1d28df) feat: add multicast to linkconfig * [`6db06f4d5`](https://github.com/siderolabs/talos/commit/6db06f4d5d51abd9e80ead6e4417f0f68856c569) feat: implement multicast setting * [`eeded98f5`](https://github.com/siderolabs/talos/commit/eeded98f527a230c65cb041a29fefc5f693d9879) fix: add riscv64 talosctl to release artifacts * [`a6bbae91b`](https://github.com/siderolabs/talos/commit/a6bbae91bad56328851fa91e01c17b8af7340b3c) fix: fix typos across the project * [`83f2bdb9c`](https://github.com/siderolabs/talos/commit/83f2bdb9ce6c9466716a6ac9c94dc2222e569ee8) feat: support relative voume size

### Changes from siderolabs/pkgs
33 commits

* [`972f44d`](https://github.com/siderolabs/pkgs/commit/972f44d5dae53809ef337544c52c835373439d34) feat: update dependencies * [`f8eb5b0`](https://github.com/siderolabs/pkgs/commit/f8eb5b02aaebaf76c59e71f57f4a689dc727e769) feat: update Linux to 6.18.2 * [`3fb6291`](https://github.com/siderolabs/pkgs/commit/3fb629109a7e5f9650d0e641ff5076a29c319448) feat: update systemd to 259 * [`59241bd`](https://github.com/siderolabs/pkgs/commit/59241bd58eeb07a18af1c9fc8fffff6365ecca0d) fix: add SBOMs for pigz/igzip * [`9377c78`](https://github.com/siderolabs/pkgs/commit/9377c786d112b4181f1e373f6e513130f11b7801) feat: optimize decompression for containerd * [`e8e61ce`](https://github.com/siderolabs/pkgs/commit/e8e61cedbbd687ed958db992e05b5d59e4a8ea60) feat: update containerd to 2.2.1 * [`daa74ba`](https://github.com/siderolabs/pkgs/commit/daa74bab83f91bbc4b6c42625d2953299d5fe20a) feat: support xfs filesystem reproducibility * [`1f66513`](https://github.com/siderolabs/pkgs/commit/1f665130fbda76478c261dd54e3843c15027c9cd) feat: update OpenZFS to 2.4.0 * [`b209af5`](https://github.com/siderolabs/pkgs/commit/b209af5baf1a67472ef431e5a8b7d48022392a1e) chore: rekres with latest changes * [`2b806b9`](https://github.com/siderolabs/pkgs/commit/2b806b9b2a7e05b97c2a7e8572e3a8edbd3721d3) feat: bump dependencies * [`65242fd`](https://github.com/siderolabs/pkgs/commit/65242fd0fef5c9c923aacce23d1655bad0d1b3e3) feat: enable CONFIG_MISC_RP1 in ARM64 config * [`4daecd8`](https://github.com/siderolabs/pkgs/commit/4daecd8e7b8d87110a9e552a60a5394014294e08) feat: update Linux to 6.18.1 * [`9868a66`](https://github.com/siderolabs/pkgs/commit/9868a66e3c000f505c97ff68e61abac9c9e8e4c9) feat: enable Powercap and Intel RAPL * [`07883ee`](https://github.com/siderolabs/pkgs/commit/07883eee3729d4d3adaaebcd825452934c3baebb) feat: build and package perf binary * [`47abca0`](https://github.com/siderolabs/pkgs/commit/47abca0852b9555d88eba61661c65a7f93ec3590) fix: add json support to nftables binary * [`b961ff8`](https://github.com/siderolabs/pkgs/commit/b961ff898fc9eae68d7f3cea2ca22ff4d0b9c99d) feat: patch containerd 2.2.0 with cgroups fix patch * [`b7dd7f6`](https://github.com/siderolabs/pkgs/commit/b7dd7f6c809f670f058b78fd3b84f4cb977771cb) feat: add mstflint module * [`ae53351`](https://github.com/siderolabs/pkgs/commit/ae5335198e009da7b06bc0f0d6f42b0947650fc0) feat: update ZFS to 2.4.0-rc5 * [`b8edf01`](https://github.com/siderolabs/pkgs/commit/b8edf0168171ffc5b87fcd962e37d5c2cd25b687) feat: update CNI plugins to v1.9.0 * [`a57c1b0`](https://github.com/siderolabs/pkgs/commit/a57c1b0c9d143559a87b64fe9570eec39c14a771) feat: enable amd sev-snp * [`68562c1`](https://github.com/siderolabs/pkgs/commit/68562c1b4cdba656287021a1694440b2a7e4d24d) feat: update Linux to 6.18 * [`6f4ff8c`](https://github.com/siderolabs/pkgs/commit/6f4ff8cc9f57452707588c05e5ca4e80c56548d2) feat: enable Amlogic Meson PCIe controller driver * [`c41127b`](https://github.com/siderolabs/pkgs/commit/c41127b94d22b9a5cb6b93f49b546f2ff477410c) feat: enable Intel GPIO/Pinctrl kernel modules * [`4a31ff7`](https://github.com/siderolabs/pkgs/commit/4a31ff7dd5c9266b68abded53a7399cb8102f4e3) feat: update NVIDIA LTS to 580.105.08 * [`3e858d3`](https://github.com/siderolabs/pkgs/commit/3e858d3fa5b2719d8d83397fb89c2ffc91f86615) chore: fork pkgs for Talos 1.13 * [`dcc5aa1`](https://github.com/siderolabs/pkgs/commit/dcc5aa1e71d6b2e9374d41029a2e6de22dbc61ce) feat: update runc to 1.3.4 * [`8b6ae5b`](https://github.com/siderolabs/pkgs/commit/8b6ae5b7fc22c3bb2df4bbe31190ff90b0986e6f) fix: regenerate configs * [`2992598`](https://github.com/siderolabs/pkgs/commit/29925980896df1978a020505b2b061ffdbd240c7) fix: add missing kernel config entries * [`c8ea18a`](https://github.com/siderolabs/pkgs/commit/c8ea18a0873f5b31c54d567ef97d8d05634eb506) feat: rekres to alow multiple commits * [`2ddef8b`](https://github.com/siderolabs/pkgs/commit/2ddef8b65755610fc6dbb3f1fb976a6bc572478f) chore: update dependencies * [`d1f28e0`](https://github.com/siderolabs/pkgs/commit/d1f28e058972174af9ac819783a69f5f6596b37d) chore: update dependencies * [`ab253f5`](https://github.com/siderolabs/pkgs/commit/ab253f521d95b30710e258ebb54adbb7b8de8970) feat: enable gpio-fan module * [`0b10666`](https://github.com/siderolabs/pkgs/commit/0b1066635d9dd255bf0ad936e21099fd4bd03f1e) chore: use ubuntu mirrors

### Changes from siderolabs/proto-codec
1 commit

* [`bd9c491`](https://github.com/siderolabs/proto-codec/commit/bd9c491b9e84d7274728ce7e3bde14009f5224bd) chore: bump and update dependencies

### Changes from siderolabs/tools
7 commits

* [`896f8b9`](https://github.com/siderolabs/tools/commit/896f8b9c1f88cd190d11b8ef3baa2c36e73d6dfe) fix: add sbom for zlib-ng * [`543a16f`](https://github.com/siderolabs/tools/commit/543a16fedf7170d8b015ea1391817328205e629a) feat: replace zlib -> zlib-ng, add nasm * [`b67c1a1`](https://github.com/siderolabs/tools/commit/b67c1a168b302539d2082a5513c4a0130c30e4df) chore: rekres with latest changes * [`5e087cb`](https://github.com/siderolabs/tools/commit/5e087cbcd158db1ce4f447145bd76a24d07159a1) feat: bump dependencies * [`da96a27`](https://github.com/siderolabs/tools/commit/da96a2771801627b4715f7a13199aa6846f87732) chore: rekres to fix reproducibility * [`e283ec8`](https://github.com/siderolabs/tools/commit/e283ec8d3831bb19b26938afb10f4955ea563ce2) feat: update Go to 1.25.5 * [`c38ff0c`](https://github.com/siderolabs/tools/commit/c38ff0c03be69e5cc3795d9dc055896604a3041c) chore: update to 1.13.0-alpha.0 toolchain

### Dependency Changes * **github.com/aws/aws-sdk-go-v2/config** v1.31.20 -> v1.32.6 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.18.13 -> v1.18.16 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.46.0 -> v1.49.4 * **github.com/aws/smithy-go** v1.23.2 -> v1.24.0 * **github.com/containerd/cgroups/v3** v3.0.5 -> v3.1.0 * **github.com/containerd/containerd/api** v1.9.0 -> v1.10.0 * **github.com/containerd/containerd/v2** v2.1.5 -> v2.2.0 * **github.com/containerd/platforms** v1.0.0-rc.1 -> v1.0.0-rc.2 * **github.com/cosi-project/runtime** v1.12.0 -> v1.13.0 * **github.com/diskfs/go-diskfs** fc569a00ea19 **_new_** * **github.com/docker/cli** v29.0.0 -> v29.1.3 * **github.com/gdamore/tcell/v2** v2.9.0 -> v2.13.4 * **github.com/godbus/dbus/v5** v5.1.0 -> v5.2.0 * **github.com/google/cadvisor** v0.53.0 -> v0.54.1 * **github.com/google/go-containerregistry** v0.20.6 -> v0.20.7 * **github.com/hetznercloud/hcloud-go/v2** v2.30.0 -> v2.32.0 * **github.com/klauspost/compress** v1.18.1 -> v1.18.2 * **github.com/linode/go-metadata** v0.2.2 -> v0.2.3 * **github.com/mdlayher/ethtool** v0.4.0 -> v0.5.0 * **github.com/miekg/dns** v1.1.68 -> v1.1.69 * **github.com/moby/moby/client** v0.1.0 -> v0.2.1 * **github.com/siderolabs/go-blockdevice/v2** v2.0.20 -> v2.0.22 * **github.com/siderolabs/pkgs** v1.12.0-23-ge0b78b8 -> v1.13.0-alpha.0-24-g972f44d * **github.com/siderolabs/proto-codec** v0.1.2 -> v0.1.3 * **github.com/siderolabs/talos/pkg/machinery** v1.12.0 -> v1.12.0-alpha.2 * **github.com/siderolabs/tools** v1.12.0-2-g7d57df0 -> v1.13.0-alpha.0-6-g896f8b9 * **github.com/sirupsen/logrus** v1.9.3 -> dd1b4c2e81af * **go.etcd.io/etcd/api/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/pkg/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/client/v3** v3.6.6 -> v3.6.7 * **go.etcd.io/etcd/etcdutl/v3** v3.6.6 -> v3.6.7 * **go.uber.org/zap** v1.27.0 -> v1.27.1 * **golang.org/x/net** v0.47.0 -> v0.48.0 * **golang.org/x/oauth2** v0.33.0 -> v0.34.0 * **golang.org/x/sync** v0.18.0 -> v0.19.0 * **golang.org/x/sys** v0.38.0 -> v0.39.0 * **golang.org/x/term** v0.37.0 -> v0.38.0 * **golang.org/x/text** v0.31.0 -> v0.32.0 * **google.golang.org/grpc** v1.76.0 -> v1.77.0 * **google.golang.org/protobuf** v1.36.10 -> v1.36.11 Previous release can be found at [v1.12.0](https://github.com/siderolabs/talos/releases/tag/v1.12.0) ## [Talos 1.12.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.12.0-alpha.2) (2025-10-28) Welcome to the v1.12.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Disk Encryption Talos versions prior to v1.12 used the state of PCR 7 and signed policies locked to PCR 11 for TPM based disk encryption. Talos now supports configuring which PCRs states are to be used for TPM based disk encryption via the `options.pcrs` field in the `tpm` section of the disk encryption configuration. If user doesn't specify any options Talos defaults to using PCR 7 for backwards compatibility with existing installations. This change was made to improve compatibility with systems that may have varying states in PCR 7 due to UEFI Secure Boot configurations and users may wish to disable locking to PCR 7 state entirely. Signed PCR policies will still be bound to PCR 11. The currently used PCR's can be seen with `talosctl get volumestatus -o yaml` command. ### Embedded Config Talos Linux now supports [embedding the machine configuration](https://www.talos.dev/v1.12/talos-guides/configuration/acquire/) directly into the boot image. ### etcd etcd container image is now pulled from `registry.k8s.io/etcd` instead of `gcr.io/etcd-development/etcd`. ### Ethernet Configuration The Ethernet configuration now includes a `wakeOnLAN` field to enable Wake-on-LAN (WOL) support. This field can be set to enable WOL and specify the desired WOL modes. ### Extra Binaries Talos Linux now ships with `nft` binary in the rootfs to support CNIs which shell out to `nft` command. ### Feature Lock Talos now ignores the following machine configuration fields: - `machine.features.rbac` (locked to true) - `machine.features.apidCheckExtKeyUsage` (locked to true) - `cluster.apiServer.disablePodSecurityPolicy` (locked to false) These fields were removed from the default machine configuration schema in v1.12 and are now always set to the locked values above. ### GRUB Talos Linux introduces new machine configuration option `.machine.install.grubUseUKICmdline` to control whether GRUB should use the kernel command line provided by the boot assets (UKI) or to use the command line constructed by Talos itself (legacy behavior). This option defaults to `true` for new installations, which means that GRUB will use the command line from the UKI, making it easier to customize kernel parameters via boot asset generation. For existing installations upgrading to v1.12, this option will default to `false` to preserve the legacy behavior. ### Kernel Module Talos now supports optionally disabling kernel module signature verification by setting `module.sig_enforce=0` kernel parameter. By default module signature verification is enabled (`module.sig_enforce=1`). When using Factory or Imager supply as `-module.sig_enfore module.sig_enforce=0` kernel parameters to disable module signature enforcement. ### Kernel Security Posture Profile (KSPP) Talos now enables a stricter set of KSPP sysctl settings by default. The list of overridden settings is available with `talosctl get kernelparamstatus` command. ### Encrypted Volumes Talos Linux now consistently provides mapped names for encrypted volumes in the format `/dev/mapper/luks2-`. This change should not affect system or user volumes, but might allow easier identification of encrypted volumes, and specifically for raw encrypted volumes. ### talosctl image cache-serve `talosctl` includes new subcommand `image cache-serve`. It allows serving the created OCI image registry over HTTP/HTTPS. It is a read-only registry, meaning images cannot be pushed to it, but the backing storage can be updated by re-running the `cache-create` command; Additionally `talosctl image cache-create` has some changes: * new flag `--layout`: `oci` (_default_), `flat`: * `oci` preserves current behavior; * `flat` does not repack artifact layer, but moves it to a destination directory, allowing it to be served by `talosctl image cache-serve`; * changed flag `--platform`: now can accept multiple os/arch combinations: * comma separated (`--platform=linux/amd64,linux/arm64`); * multiple instances (`--platform=linux/amd64 --platform=linux/arm64`); ### Component Updates Linux: 6.17.5 Kubernetes: 1.35.0-alpha.2 CNI Plugins: 1.8.0 cryptsetup: 2.8.1 LVM2: 2_03_34 systemd-udevd: 257.8 runc: 1.3.2 CoreDNS: 1.13.0 etcd: 3.6.5 Flannel: 0.27.4 Flannel CNI plugin: v1.8.0-flannel1 Talos is built with Go 1.25.3. ### Contributors * Andrey Smirnov * Noel Georgi * Mateusz Urbanek * Dmitrii Sharshakov * Amarachi Iheanacho * Orzelius * Oguz Kilcan * Utku Ozdemir * George Gaál * Jorik Jonker * Justin Garrison * Michael Smith * 459below * Alp Celik * Andrew Longwill * Chris Sanders * Dmitry * Febrian * Florian Grignon * Fred Heinecke * Giau. Tran Minh * Grzegorz Rozniecki * Guillaume LEGRAIN * Markus Freitag * Max Makarov * Mike Beaumont * Misha Aksenov * MrMrRubic * Olivier Doucet * Sammy ETUR * Serge Logvinov * Skyler Mäntysaari * SuitDeer * Tom * aurh1l * frozenprocess * frozensprocess * kassad * leppeK * samoreno * theschles * winnie ### Changes
245 commits

* [`3d811a4c8`](https://github.com/siderolabs/talos/commit/3d811a4c81e010b157b277499d272dc0e934baa9) release(v1.12.0-alpha.2): prepare release * [`fb4bfe851`](https://github.com/siderolabs/talos/commit/fb4bfe851c7c308eeaf4a11e0ac5c944f66dc0c4) chore: fix LVM test * [`f4ee0d112`](https://github.com/siderolabs/talos/commit/f4ee0d1128ba2f35d54ec3d35a83fc62fd222f2e) chore: disable VIP operator test * [`288f63872`](https://github.com/siderolabs/talos/commit/288f6387260843570d53d28a4d77e564b3182979) feat: bump deps * [`b66482c52`](https://github.com/siderolabs/talos/commit/b66482c529beda8b1abf9ed6b71ece354c1540be) feat: allow disabling injection of extra cmdline in cluster create * [`704b5f99e`](https://github.com/siderolabs/talos/commit/704b5f99e6bef4410629427ac65fd2742ddb335d) feat: update Kubernetes to 1.35.0-alpha.2 * [`1dffa5d99`](https://github.com/siderolabs/talos/commit/1dffa5d9965a6c7d872f052bfb1750ea550671c2) feat: implement virtual IP operator config * [`43b1d7537`](https://github.com/siderolabs/talos/commit/43b1d7537507a916629cc2d6db7440a99ffcb748) fix: validate provisioner when destroying local clusters * [`b494c54c8`](https://github.com/siderolabs/talos/commit/b494c54c81e6ca81cef8ce26da772c1fc336ea8d) fix: talos import on non-linux * [`61e95cb4b`](https://github.com/siderolabs/talos/commit/61e95cb4b7b354d175d1dfce3d0fa43deefad187) feat: support bootloader option for ISO * [`d11072726`](https://github.com/siderolabs/talos/commit/d110727263c57c02392f201938d2b71976b8c4d6) fix: provide offset for partitions in discovered volumes * [`39eeae963`](https://github.com/siderolabs/talos/commit/39eeae96311be2b8e2d3660d878f852ba92ca064) feat: update dependencies * [`9890a9a31`](https://github.com/siderolabs/talos/commit/9890a9a31deb11ab170b94c667143314db08f76f) test: fix OOM test * [`c0772b8ed`](https://github.com/siderolabs/talos/commit/c0772b8eda429675a06899b9c4a4d1dd7d5f6a5f) feat: add airgapped mode to QEMU backed talos * [`ac60a9e27`](https://github.com/siderolabs/talos/commit/ac60a9e27deed63db0e4e61ffa30d46f4cab590a) fix: update test for PCI driver rebind/IOMMU * [`6c98f4cdb`](https://github.com/siderolabs/talos/commit/6c98f4cdb049c58ef4f6e8193ef66c2338a2877d) feat: implement new DHCP network configuration * [`da92a756d`](https://github.com/siderolabs/talos/commit/da92a756d9668fa043b4794db45d5c985d8ea4a6) fix: drop 'ro' falg from defaults * [`28fd2390c`](https://github.com/siderolabs/talos/commit/28fd2390cb6e02f400bb237dd674c7d0d40f8ed3) fix: imager build on arm64 * [`4e12df8c5`](https://github.com/siderolabs/talos/commit/4e12df8c5c27ae115c4eac70a7e2fceb03dac5f5) test: integration test for OOM controller * [`7e498faba`](https://github.com/siderolabs/talos/commit/7e498faba93f972ba82edf41550d3b94256e83e9) feat: use image signer * [`eccb21dd3`](https://github.com/siderolabs/talos/commit/eccb21dd3ba03eb4ab03c4da87a51a4e3d8da49a) feat: add presets to the 'cluster create qemu' command * [`ec0a813fa`](https://github.com/siderolabs/talos/commit/ec0a813facf5be5ca3e9ba65924ae18b2b05a7d9) feat: unify cmdline handling GRUB/systemd-boot * [`37e4c40c6`](https://github.com/siderolabs/talos/commit/37e4c40c6a2477e45bbf067effc4389d4639c905) fix: skip module signature tests on docker provisioner only * [`8124efb42`](https://github.com/siderolabs/talos/commit/8124efb42fd5a3eb81f41e84974e4242246ca7c4) fix: cache e2e * [`4adcda0f5`](https://github.com/siderolabs/talos/commit/4adcda0f5427e1bae49f6dda58318324a3b24ac5) fix: reserve the apid and trustd ports from the ephemeral port range * [`ced57b047`](https://github.com/siderolabs/talos/commit/ced57b047a389e26f7e5bfa3efab5b64f3fced87) feat: support optionally disabling module sig verification * [`1e5c4ed64`](https://github.com/siderolabs/talos/commit/1e5c4ed644cbc60d8518fe4298e63a5cf5dc8cf5) fix: build talosctl image cache-serve non-linux * [`dbdd2b237`](https://github.com/siderolabs/talos/commit/dbdd2b237e0aefbba439b90472abf9ec7eea6aa6) feat: add static registry to talosctl * [`77d8cc7c5`](https://github.com/siderolabs/talos/commit/77d8cc7c589a190c8cb86e6e1684233129b648a1) chore: push `latest` tag only on main * [`59d9b1c75`](https://github.com/siderolabs/talos/commit/59d9b1c75dbff09e405906ebcfb3ad1a69cb8f4b) feat: update dependencies * [`bf6ad5171`](https://github.com/siderolabs/talos/commit/bf6ad51710c367764e582ccc1fb77b4d989c874d) feat: add back install script * [`da451c5ba`](https://github.com/siderolabs/talos/commit/da451c5ba4ee97e7ef108bb6d73d5aa8bc7c72fd) chore: drop documentation except for fresh reference * [`2f23fedeb`](https://github.com/siderolabs/talos/commit/2f23fedeb725a5786b6ffac2aef8125eecd6cb6e) fix: file leak in reading cgroups * [`b412ffdbc`](https://github.com/siderolabs/talos/commit/b412ffdbc29d77a81aed88be62f21bc2999afcde) docs: update README.md for docs link * [`8dc51bae7`](https://github.com/siderolabs/talos/commit/8dc51bae79a37b56c058d40787dbda6e828fd0d3) feat: add drm_gpuvm and drm_gpusvm_helper modules * [`4ca58aeb8`](https://github.com/siderolabs/talos/commit/4ca58aeb81145cb7ebef071865b3d853a4712729) fix: make Akamai platform usable * [`061f8e76f`](https://github.com/siderolabs/talos/commit/061f8e76fd58906ff823a0e467d6efcf5161ed9f) feat: bump pkgs * [`a9fa852da`](https://github.com/siderolabs/talos/commit/a9fa852dadd75740d73588fd2156f6f1ad782fdd) feat: update uefi image to talos linux logo * [`04753ba69`](https://github.com/siderolabs/talos/commit/04753ba6983b6ff2754cf62b8d60cc6065921dbd) feat: update go to 1.25.2 * [`9a42b05bd`](https://github.com/siderolabs/talos/commit/9a42b05bdac2bf0cbbc97d040be7860f48c69386) feat: implement link aliasing * [`d732bd0be`](https://github.com/siderolabs/talos/commit/d732bd0be73c3d17d140c00be0e9d27ea621909b) chore(ci): run only nvidia tests for NVIDIA workflows * [`8d1468209`](https://github.com/siderolabs/talos/commit/8d1468209aa28f59df9dc52466c506defa8c3cc3) fix: stop populating apiserver cert SANs * [`02473244c`](https://github.com/siderolabs/talos/commit/02473244c17ef0149515f300bcd201f9347acabc) fix: wait for mount status to be proper mode * [`825622d90`](https://github.com/siderolabs/talos/commit/825622d90a7716f7b6027651a5b9389173432393) fix: resource proto definitions * [`2c6003e79`](https://github.com/siderolabs/talos/commit/2c6003e790003f6ef1a03b8d2af8030fb57c5d02) docs: add Project Calico installation in two mode * [`4fb4c8678`](https://github.com/siderolabs/talos/commit/4fb4c86780def54eed4d999b1f0ce93042269076) feat: add disk.EnableUUID to generated ova * [`33fb48f8f`](https://github.com/siderolabs/talos/commit/33fb48f8f90ccf44e95c93ac7ec1adcd1b4e0373) fix: add dashboard spinner * [`053fd0bd4`](https://github.com/siderolabs/talos/commit/053fd0bd4d324bc21e076b3a30466ed61c7684e1) feat: update Linux to 6.17 * [`34e107e1b`](https://github.com/siderolabs/talos/commit/34e107e1bd14b0a56ebfa0c65e0c7da715976d99) docs: fix broken link * [`dfbece56b`](https://github.com/siderolabs/talos/commit/dfbece56bd45e95c9ec477af4b53ffcefdfec66c) docs: update the kubespan docs * [`8b041a72c`](https://github.com/siderolabs/talos/commit/8b041a72ca9c07985c024c1136c85c85df92beda) docs: update scaleway.md * [`435dcbf82`](https://github.com/siderolabs/talos/commit/435dcbf820cd9f8cc9fecc0f7d42819acef36106) fix: provide nocloud metadata with missing network config * [`ec3bd878f`](https://github.com/siderolabs/talos/commit/ec3bd878f9770ceb932b654aabad1711880da829) refactor: remove the go-blockdevice v1 completely * [`33544bde9`](https://github.com/siderolabs/talos/commit/33544bde9c15745f4ae692c7647d661b32d4bed4) fix: minor improvements to fs * [`fd2eebf7f`](https://github.com/siderolabs/talos/commit/fd2eebf7fa4831d33383a53d6d058c74789553e4) feat: create merge patch from diff of two machine configs * [`eadbdda94`](https://github.com/siderolabs/talos/commit/eadbdda9471289fae5159c8cc024a735a1547807) fix: uefi boot order setting * [`cd9fb2743`](https://github.com/siderolabs/talos/commit/cd9fb274342c5a973b3d087b991a7eea5df4142a) fix: support secure HTTP proxy with gRPC dial * [`adf87b4b9`](https://github.com/siderolabs/talos/commit/adf87b4b931ded1edeb64217b0e9d5edfd046004) feat: update Flannel to v0.27.4 * [`5dfb7e1fe`](https://github.com/siderolabs/talos/commit/5dfb7e1fe7d9cc6db3e4c2b6f587e641b4a0842b) feat: serve etcd image from registry.k8s.io * [`5ca841804`](https://github.com/siderolabs/talos/commit/5ca8418049e3b878585014a3764021f2d30a0df7) fix: nftables flaky test * [`a940e45a7`](https://github.com/siderolabs/talos/commit/a940e45a7fe041b17437f774eb52b9f3a42e3633) feat: generate list of images required to build talos * [`3472d6e79`](https://github.com/siderolabs/talos/commit/3472d6e79caa13fd42df7774101397b0a30f62f5) fix: revert "chore: use new mount/v3 package in efivarfs" * [`42c0bdbf3`](https://github.com/siderolabs/talos/commit/42c0bdbf320bf24311b2d56b2e0f7155e86b3713) feat: add provisioner flag to images default command * [`6bc0b1bcf`](https://github.com/siderolabs/talos/commit/6bc0b1bcf7d9dc9f2417a7db63d1e76e7ddc6aa3) feat: drop and lock deprecated features * [`362a8e63b`](https://github.com/siderolabs/talos/commit/362a8e63b798c4a4fc31fe5e728d2429fc953166) fix: change the compression format * [`6e58f58aa`](https://github.com/siderolabs/talos/commit/6e58f58aaeb6e16883d8dc8757ad92b6b6da7e84) fix: mkdir artifacts path * [`3165a2b84`](https://github.com/siderolabs/talos/commit/3165a2b84cb80dd5fd09bf496fdccaf1628593d0) release(v1.12.0-alpha.1): prepare release * [`e455c7ea9`](https://github.com/siderolabs/talos/commit/e455c7ea9c919a2f70ddecceaa8f3b4e25566048) chore: use testing/synctest in tests * [`7f048e962`](https://github.com/siderolabs/talos/commit/7f048e962e217687ab67ed7027c5228e8ccb7d16) feat: update dependencies * [`fe36b3d32`](https://github.com/siderolabs/talos/commit/fe36b3d3200db57f3e21017ff7a4808b330a1d55) fix: stop returning EINVAL on remount of detached mounts * [`c6279e04c`](https://github.com/siderolabs/talos/commit/c6279e04c45504af243c0aef9f255317426b4ca0) chore: use new mount/v3 package in efivarfs * [`d5197effb`](https://github.com/siderolabs/talos/commit/d5197effb0b48290d613140b68796cb8f30b9a70) feat: update etcd 3.6.5, CoreDNS 1.12.4 * [`33714b715`](https://github.com/siderolabs/talos/commit/33714b7158a0d569be1d0b1d7b012280856db484) feat: release cloud image using factory * [`d10a2747e`](https://github.com/siderolabs/talos/commit/d10a2747e0e835876aff158e6b6f7882cef9fa44) docs: deprecate JSON6902 patches and interactive installer * [`1e604cbf5`](https://github.com/siderolabs/talos/commit/1e604cbf514bece1e112d8afd5d1cd6ccb1045c3) fix: don't set broadcast for /31 and /32 addresses * [`65a66097a`](https://github.com/siderolabs/talos/commit/65a66097a05e5c0e2334d5eff494a0e71534716f) refactor: split cluster create logic into smaller parts * [`ab847310e`](https://github.com/siderolabs/talos/commit/ab847310efde540b5bfe17570b99af1bb705832b) fix: provide refreshing CA pool (resolvers) * [`d63c3ed7d`](https://github.com/siderolabs/talos/commit/d63c3ed7db2b22f7e394fc45d101d03cba463177) docs: update secureboot docs * [`493f7ed9d`](https://github.com/siderolabs/talos/commit/493f7ed9d2710eb240eab6b6ab532f41abc818c1) feat: support embedded config * [`251df70f6`](https://github.com/siderolabs/talos/commit/251df70f6d33f1d5a3b1b9e4c0c249d8bc85c4b3) feat: add a userspace OOM controller * [`7bae5b40b`](https://github.com/siderolabs/talos/commit/7bae5b40b4f22f0f07a586ebd9cda9436086a5f8) feat: implement link configuration * [`724857dec`](https://github.com/siderolabs/talos/commit/724857decb95ddeebb2ac5d33c38a71bf7512805) fix(ci): skip netbird extension for tests * [`e06a08698`](https://github.com/siderolabs/talos/commit/e06a086989331f28406e8d4234e02d9a6b83f87d) fix: default gateway as string * [`7ed07412e`](https://github.com/siderolabs/talos/commit/7ed07412e963e6ee91615adbea095944aa6a56e5) fix: uefi boot entry handling logic * [`ea4ed165a`](https://github.com/siderolabs/talos/commit/ea4ed165ad860a5beea17ca2d404bdaa6e5ad933) refactor: efivarfs mock and tests * [`1fca111e2`](https://github.com/siderolabs/talos/commit/1fca111e24bcae81b78f007e67b71c9155c0169f) feat: support setting wake-on-lan for Ethernet * [`94f78dbe7`](https://github.com/siderolabs/talos/commit/94f78dbe798cb227a0c38b70a1d6840803989290) docs: add a documentation for running Talos in KVM * [`46902f8fd`](https://github.com/siderolabs/talos/commit/46902f8fdee257a09be4bc1753c6b3f845ef8089) docs: add TrueFullstaq to adopters * [`a28e5cbd5`](https://github.com/siderolabs/talos/commit/a28e5cbd50d11aa6c253a6a9ce1999b9d45effad) chore: update pkgs and tools * [`7cf403db8`](https://github.com/siderolabs/talos/commit/7cf403db8ca0e1719195001895cfbc12835b0fdd) docs: step-by-step scaleway documentation to get an image * [`687285fa2`](https://github.com/siderolabs/talos/commit/687285fa26ec42dadbfb72580099f6e20bbaf85e) docs: remove 'curl' in wget command * [`9db6dc06c`](https://github.com/siderolabs/talos/commit/9db6dc06c3010cd89ce4cb0ec0bde178db0447a4) feat: stop mounting state partition * [`53ce93aae`](https://github.com/siderolabs/talos/commit/53ce93aaed3bd5bfcbe926fa69ca3b4b8b45c74f) test: try to clear connection refused more aggressively * [`51db5279c`](https://github.com/siderolabs/talos/commit/51db5279c423e4b8637a05e52b26dfc5aa719cbc) fix: bump trustd memory limit * [`25204dc8a`](https://github.com/siderolabs/talos/commit/25204dc8a8df79bc876a0bec2492e1147a81d954) fix(machined): change `constants.MinimumGOAMD64Level` using build tag * [`9cd2d794d`](https://github.com/siderolabs/talos/commit/9cd2d794d060b637dbac5263ae417a4e83d54efe) feat: ship nft binary with Talos rootfs * [`b1416c9fe`](https://github.com/siderolabs/talos/commit/b1416c9fe1d5ea9cd68f9b6b766a288a267cee61) feat: record last log the failed service * [`0b129f9ef`](https://github.com/siderolabs/talos/commit/0b129f9efdf57dd9692f7cece6b97719a7ccf80e) feat: enforce more KSPP and hardening sysctls * [`11872643c`](https://github.com/siderolabs/talos/commit/11872643c310212c52b4fd7e13b6cc7d6ec7e4fc) chore: drop docs folder * [`d30fdcd88`](https://github.com/siderolabs/talos/commit/d30fdcd88f421824cf17b9ecec25be7c8044e857) chore: pass in github token to imager * [`b88f27d80`](https://github.com/siderolabs/talos/commit/b88f27d804d60a706f598b50676dad5dd2a9726a) chore: make reset test code a bit better * [`1cde53d01`](https://github.com/siderolabs/talos/commit/1cde53d0173fd1ae637855e15fe34bb74bb027a0) test: fix several issues with tests * [`16cd127a0`](https://github.com/siderolabs/talos/commit/16cd127a04bb5fc907b7ca04f1c81d4c7150eab2) docs: add docs on updating image cache * [`c3ae92b14`](https://github.com/siderolabs/talos/commit/c3ae92b1424d4a2c9bc18cfa394b10eda6c9a20f) fix: build kernel checks only on linux * [`2120904ec`](https://github.com/siderolabs/talos/commit/2120904ec534a91f66dcea419b5a29e36a16f6e4) feat: create detached tmpfs * [`6bbee6de5`](https://github.com/siderolabs/talos/commit/6bbee6de5b18b25deb4e6f515251187e259aa424) docs: remove 'ceph-data' from volume examples/docs * [`07acb3bd2`](https://github.com/siderolabs/talos/commit/07acb3bd2d4f92e80706d1835130bbe6e944d096) fix: use correct order to determine SideroV1 keys directory path * [`2d57fa002`](https://github.com/siderolabs/talos/commit/2d57fa00281f8090b85097c66df634101b0cde79) fix: trim zero bytes in the DHCP host & domain response * [`451cb5f78`](https://github.com/siderolabs/talos/commit/451cb5f78fac3b2ddfec7d545629fe8c88ea2367) docs: clarify disk partition confusion * [`a2122ee5c`](https://github.com/siderolabs/talos/commit/a2122ee5cb9c84f33e0c4b30e9223bb239621d55) feat: implement HostConfig multi-doc * [`69ab076b4`](https://github.com/siderolabs/talos/commit/69ab076b4d6e52484677ee7f68a853dc4edfe2bc) fix: re-create cgroups when restarting runners * [`297b5cc28`](https://github.com/siderolabs/talos/commit/297b5cc2856710b74b4e0e46b00ae33aea4c1bf7) docs: add docs on node labels * [`e168512dd`](https://github.com/siderolabs/talos/commit/e168512dd020da9eac654dae2ba891cf33415c44) fix: apply 'ro' flag to iso9660 filesystems * [`7f7acfbb9`](https://github.com/siderolabs/talos/commit/7f7acfbb9f10c243d0b132c1ef079cb77d2727e0) docs: fix typo in doc * [`d57882b18`](https://github.com/siderolabs/talos/commit/d57882b1830504fe4bfd5344edae613168db7f0e) feat: update Kubernetes to 1.34.1 * [`f85f82f32`](https://github.com/siderolabs/talos/commit/f85f82f32f098f97588f404550f72d64786fe329) test: fix flakiness in RawVolumes test * [`82569e319`](https://github.com/siderolabs/talos/commit/82569e319eb57b1199db6bfd3e612fb771c8c7cd) feat: update Linux 6.16.6 * [`2fd2ab4e4`](https://github.com/siderolabs/talos/commit/2fd2ab4e43e06910154705d6ef1d0576a7c04a2b) fix: remove CoreDNS cpu limit * [`ce9bc32a0`](https://github.com/siderolabs/talos/commit/ce9bc32a08695873d9054afe2608a76cf7c6088a) chore(ci): rekres to use new runner groups * [`8b64f68f6`](https://github.com/siderolabs/talos/commit/8b64f68f6946c2979f6fe2bf617f31639a927bf8) test: improve test stability * [`272cb860d`](https://github.com/siderolabs/talos/commit/272cb860d4cfb8464b29ff31567e25fe6c275849) chore: drop the --input-dir flag from the cluster create command * [`1b6533675`](https://github.com/siderolabs/talos/commit/1b65336752933acdcbf681767785157714866f88) docs: add note about ca-signed certs for secureboot * [`d3f88f50c`](https://github.com/siderolabs/talos/commit/d3f88f50c5394536ee80d19464359408a37d81ff) docs: document talos vip failover behavior * [`005fc8bd5`](https://github.com/siderolabs/talos/commit/005fc8bd50fbc4b15b26032b43d1d32c1da22f11) docs: add docs on syncing configs after a kube upgrade * [`4d876d9af`](https://github.com/siderolabs/talos/commit/4d876d9af9fcc9828f09d05db124fbdce9c17785) feat: update Go to 1.25.1 * [`2b556cd22`](https://github.com/siderolabs/talos/commit/2b556cd22a3563f1d86a648ea6c69a4d45edad76) feat: implement multi-doc StaticHostConfig * [`a7b776842`](https://github.com/siderolabs/talos/commit/a7b7768420566b6840fc52bb2152e9bf165f8cd3) docs: replace Raspberry Pi 5 links with Talos builder * [`a349b20ed`](https://github.com/siderolabs/talos/commit/a349b20ed4b3c05dcd0175541b795331f0f7c64d) docs: clarify that talos does not support intermediate ca * [`895133de9`](https://github.com/siderolabs/talos/commit/895133de99158ce3f50b557b77c81d4f0f9d6b40) feat: support configuring PCR states to bind disk encryption * [`c1360103b`](https://github.com/siderolabs/talos/commit/c1360103b5e037cf713b7d787436f01e7182821c) docs: fix command for uploading image on Hetzner * [`43b5b9d89`](https://github.com/siderolabs/talos/commit/43b5b9d8992ad6df37619b3719b57948e4bd9671) fix: correctly handle status-code 204 * [`feeb0d312`](https://github.com/siderolabs/talos/commit/feeb0d312ecacb451e5313390939c7c9349d2ba6) feat: update runc to 1.3.1 * [`421634a14`](https://github.com/siderolabs/talos/commit/421634a1417f529551a75d0bb9be08b73f1120b1) docs: add docs on multihoming * [`41af2d230`](https://github.com/siderolabs/talos/commit/41af2d230c2dd5dce5bc931f76a2eb69405dc554) refactor: clean up internal cluster creation code * [`3000d9e43`](https://github.com/siderolabs/talos/commit/3000d9e431deaf952d08da724da40789cd743f2c) fix: don't bootstrap talos cluster if there's no config present * [`79cb871d0`](https://github.com/siderolabs/talos/commit/79cb871d088e5b1c3a3488610ded14e7a28cec29) feat: use the id of the volume in the mapped luks2 name * [`6c322710d`](https://github.com/siderolabs/talos/commit/6c322710d64786f19e2e0e39d65596c8dce71952) chore: refactor mount package * [`ced7186e2`](https://github.com/siderolabs/talos/commit/ced7186e2a5f0634d9441b12a5340f5ca4c451ff) refactor: update COSI to 1.11.0 * [`de2e24fcd`](https://github.com/siderolabs/talos/commit/de2e24fcda590a1ef3f80a5372bb70865a2f47c3) docs: clarify that install-cni image is deprecated * [`bef8ef509`](https://github.com/siderolabs/talos/commit/bef8ef509380aba259efcc2f5d1f6632e034160b) docs: add docs on cilium's compatibility with kubespan * [`e5acb10fc`](https://github.com/siderolabs/talos/commit/e5acb10fcceba69060507a35caea21281bdc71cc) feat: update pkgs * [`c4c1daf0e`](https://github.com/siderolabs/talos/commit/c4c1daf0e2e6675626b974b0c008e101d919c8b5) docs: add info about br_netfilter * [`5c52ecac3`](https://github.com/siderolabs/talos/commit/5c52ecac364f917e5f45859f680494a08f85cb90) docs: clarify interactive dashboard resolution control * [`15ecb02a4`](https://github.com/siderolabs/talos/commit/15ecb02a4545639ffb8ba5c6e5a413e53129b619) feat: update Linux kernel (memcg_v1, ublk) * [`53f18c2f6`](https://github.com/siderolabs/talos/commit/53f18c2f60c84c4b0f944cc343ae1f538e8d1236) fix: enable support for VMWare arm64 * [`3bbe1c0da`](https://github.com/siderolabs/talos/commit/3bbe1c0da5485b6cd3e7fadd8f020e0d0aca406a) docs: add docs on grow flag * [`b9fb09dcd`](https://github.com/siderolabs/talos/commit/b9fb09dcdbcca60f695ac317c45e18fa092541a8) release(v1.12.0-alpha.0): prepare release * [`6a389cad3`](https://github.com/siderolabs/talos/commit/6a389cad35f80b27fe9c43db9e701ee9f6f6142a) chore: update dependencies * [`9d98c2e89`](https://github.com/siderolabs/talos/commit/9d98c2e891258dcf2ef90519d38d0aefb77cd0db) feat: add a cgroup preset for PSI and --skip-cri-resolve * [`072f77b16`](https://github.com/siderolabs/talos/commit/072f77b1623cdc838093465b7266b26e20a248ea) chore: prepare for future Talos 1.12-alpha.0 release * [`96f41ce88`](https://github.com/siderolabs/talos/commit/96f41ce8840783f783fcc8e0fd6b43302b9bfe43) docs: update qemu and docker docs * [`a751cd6b7`](https://github.com/siderolabs/talos/commit/a751cd6b7474a4dc20137e917dbb2229fe9cc8bd) docs: activate Talos v1.11 docs by default * [`e8f1ec1c5`](https://github.com/siderolabs/talos/commit/e8f1ec1c5bbd8a6cfb68886e6283e7caaf5fb063) docs: fix broken create qemu command v1.11 docs * [`639f0dfdd`](https://github.com/siderolabs/talos/commit/639f0dfdd88c5596439601f3f9600b3aafb24227) feat: update Linux to 6.16.4 * [`8aa7b3933`](https://github.com/siderolabs/talos/commit/8aa7b3933d07ea45a96844b9c91347a08950e243) fix: bring back linux/armv7 build and update xz * [`9cae7ba6b`](https://github.com/siderolabs/talos/commit/9cae7ba6b97a67a5d282c6f667ccb4c3e2111447) feat: update CoreDNS to 1.12.3 * [`cfef3ad45`](https://github.com/siderolabs/talos/commit/cfef3ad4544498a47de17f6b05fb8374c35e3dd8) fix: drop linux/armv7 build * [`42ea2ac50`](https://github.com/siderolabs/talos/commit/42ea2ac5058457dafe666f8d79f08d3c8ee60cfb) fix: update xz module (security) * [`4fcfd35b9`](https://github.com/siderolabs/talos/commit/4fcfd35b9510f45d0ef7ae3657eb0916d549d2dd) docs: fix module name example * [`50824599a`](https://github.com/siderolabs/talos/commit/50824599a4fa7b72d563a35a4746ca063becf672) chore: update some tools * [`bcd297490`](https://github.com/siderolabs/talos/commit/bcd297490c608f593b6dd274945aa2b73c3fd3ee) feat: allow Ed25119 in FIPS mode * [`5992138bb`](https://github.com/siderolabs/talos/commit/5992138bb981e84dae917f0f0fdafee4049bc5ec) test: ignore one leaking goroutine * [`d155326c1`](https://github.com/siderolabs/talos/commit/d155326c1206979f30a5355f7bdb23cb051e9b78) docs: add sbc unofficial ports docs * [`285fa7d22`](https://github.com/siderolabs/talos/commit/285fa7d222be1f5e63c0bb725b206966e2722a3b) docs: add the deploy application docs * [`527791f09`](https://github.com/siderolabs/talos/commit/527791f0974afe9c8558b82fa19f4354487693ed) feat: update Kubernetes to 1.34.0 * [`a1c0e237d`](https://github.com/siderolabs/talos/commit/a1c0e237d6e047bb59c4fbd48e2c2b9e36dd4808) feat: update Linux to 6.15.11, Go to 1.25 * [`4d7fc25f8`](https://github.com/siderolabs/talos/commit/4d7fc25f8bf20d4489080795a3d0ce0dfb1bc6b8) docs: switch order of wipe disk command * [`7368a994d`](https://github.com/siderolabs/talos/commit/7368a994df07cc4e50e3709ac766d8062db070a0) feat: add SOCKS5 proxy support to dynamic proxy dialer * [`d63591069`](https://github.com/siderolabs/talos/commit/d635910697b221aee3e9afa6d9e5b398236b6a21) chore: silence linter warnings * [`07eb4d7ec`](https://github.com/siderolabs/talos/commit/07eb4d7ec148a7e3c4c6dde080469c1a2fb410fb) fix: set default ram unit to MiB instead of MB * [`6b732adc4`](https://github.com/siderolabs/talos/commit/6b732adc43684facfd329f424a34a7e4df36d77b) feat: update Linux to 6.12.43 * [`b6410914f`](https://github.com/siderolabs/talos/commit/b6410914f74ce01672fdef7e912e37970909281c) feat: add human readable byte size cli flags * [`ec70cef99`](https://github.com/siderolabs/talos/commit/ec70cef99005fd7e383fea63b5c23774882fcf28) feat: update NVIDIA drivers and kernel * [`0879efa69`](https://github.com/siderolabs/talos/commit/0879efa690ad657e4aed251fcbeba8f5645d73ce) feat: update Kubernetes default to v1.34.0-rc.2 * [`f504639df`](https://github.com/siderolabs/talos/commit/f504639df4388619f731196ed8e79a6818b6ed5f) feat: add a user-facing create qemu command * [`558e0b09a`](https://github.com/siderolabs/talos/commit/558e0b09ab65b353e83b98c9ddf6cb2b67fd060e) test: fix the Image Factory PXE boot test * [`d73f0a2e5`](https://github.com/siderolabs/talos/commit/d73f0a2e5b788c7b69c2fb827f7111d5f9c8e706) docs: make readme badges consistent * [`f1369af98`](https://github.com/siderolabs/talos/commit/f1369af98e1f6d48fed137e31237956abbd28b0f) chore: use new filesystem api on STATE partition * [`366cedbe7`](https://github.com/siderolabs/talos/commit/366cedbe7495ce15bcd0e6c6f7f0add65a41a861) docs: link to kubernetes linux swap tuning * [`2f5a16f5e`](https://github.com/siderolabs/talos/commit/2f5a16f5e4ae186a309aef5e3d285897d0fe2df1) fix: make --with-uuid-hostnames functionality available to qemu provider * [`70612c1f9`](https://github.com/siderolabs/talos/commit/70612c1f9fc9056e8a3669ff10a385c4e8e03350) refactor: split the PlatformConfigController * [`511748339`](https://github.com/siderolabs/talos/commit/51174833997fd9a0a599ab1dde947834b682ab14) docs: add system extension tier documentation * [`009fb1540`](https://github.com/siderolabs/talos/commit/009fb1540e0b9f5daac6302f42e8813e596fc87c) test: don't run nvidia tests on integration/aws * [`99674ef20`](https://github.com/siderolabs/talos/commit/99674ef20d34166d60563d4bf46fbbfc57399509) docs: apply fixes for what is new * [`92db677b5`](https://github.com/siderolabs/talos/commit/92db677b5d32de32ec7e785531b32202e03283b4) fix: image cache lockup on a missing volume * [`9c97ed886`](https://github.com/siderolabs/talos/commit/9c97ed886b89b2fb84f47866abdf1000839143c4) fix: version contract parsing in encryption keys handling * [`1fc670a08`](https://github.com/siderolabs/talos/commit/1fc670a08dc7af8eaeabdc7134eb77a5c939df40) fix: dial with proxy * [`18447d0af`](https://github.com/siderolabs/talos/commit/18447d0afdbcc8fa7db6ae008e4bc4d5b0a0b00a) feat: update Linux to 6.12.41 * [`f65f39b78`](https://github.com/siderolabs/talos/commit/f65f39b78b0c7881e5f51c66ad022c17c2cd4960) fix: provide mitigation CVE-1999-0524 * [`8817cc60c`](https://github.com/siderolabs/talos/commit/8817cc60cfaf4b50f11c38d3b25df7df48382033) fix: actually use SIDEROV1_KEYS_DIR env var if it's provided * [`b08b20a10`](https://github.com/siderolabs/talos/commit/b08b20a1005256a9e3fc7cae8bcf8eea87f6ac09) feat: use key provider with fallback option for auth type SideroV1 * [`7a52d7489`](https://github.com/siderolabs/talos/commit/7a52d7489c9709708d55f8f001d70700addc7e1e) fix: kubernetes upgrade options for kubelet * [`ea8289f55`](https://github.com/siderolabs/talos/commit/ea8289f550787593b1cd35f2d8da59aa5311880e) feat: add a user facing docker command * [`54ad64765`](https://github.com/siderolabs/talos/commit/54ad64765090d90013e4917d1bf494592069beec) chore: re-enable vulncheck * [`26bbddea9`](https://github.com/siderolabs/talos/commit/26bbddea95669278363c604316ed85986f312d71) fix: darwin build * [`b5d5ef79e`](https://github.com/siderolabs/talos/commit/b5d5ef79e7a2d76e29a7c872c1c418fffc63b0df) fix: set secs field in DHCPv4 packets * [`c07911933`](https://github.com/siderolabs/talos/commit/c0791193373e36c35f29c70318432331b4c6ab2a) chore: refactor how tools are being installed * [`34f25815c`](https://github.com/siderolabs/talos/commit/34f25815c036d2c91bdfddc9c7d40ca2edf677bd) docs: fork docs for v1.12 * [`b66b995d3`](https://github.com/siderolabs/talos/commit/b66b995d34306192cbaa4ef68fe39f821b37d1f0) feat: update default Kubernetes to v1.34.0-rc.1 * [`b967c587d`](https://github.com/siderolabs/talos/commit/b967c587d9f217f25798e0bee0c90393e55dc085) docs: fix clone URL to include `.git` * [`b72c68398`](https://github.com/siderolabs/talos/commit/b72c6839806103ac0a76acd46f30eabea0375790) docs: edit the insecure, etcd-metrics, inline and extramanifests * [`e5b9c1fff`](https://github.com/siderolabs/talos/commit/e5b9c1ffffec9fd49ffb84a36c918e75eaa8f1ef) docs: remov RAS Syndrome * [`701fe774b`](https://github.com/siderolabs/talos/commit/701fe774bd19de7c9f21e043e1520161a8c5fff7) docs: fix cilium links and bump to 1.18.0 * [`d306713a1`](https://github.com/siderolabs/talos/commit/d306713a13a18d7af6caffd5890d54d91d22cad7) feat: update Go to 1.24.6 * [`721595a00`](https://github.com/siderolabs/talos/commit/721595a0009f78a2722802ab665957fd767c4d1e) chore: add deadcode elimination linter * [`dc4865915`](https://github.com/siderolabs/talos/commit/dc4865915d567942adea3efa66f8ad360f9c4cce) refactor: stop using `text/template` in `machined` code paths * [`545be55ed`](https://github.com/siderolabs/talos/commit/545be55edc863245638d4387cb9ee7e7b068f2ba) feat: add a pause function to dashboard * [`06a6c0fe3`](https://github.com/siderolabs/talos/commit/06a6c0fe332940b7a70ea2652bc2a5e7bc51bbf3) refactor: fix deadcode elimination with godbus * [`2dce8f8d4`](https://github.com/siderolabs/talos/commit/2dce8f8d4693a85d2f3bf46169af8cf502d49f9d) refactor: replace containerd/containerd/v2 module for proper DCE * [`9b11d8608`](https://github.com/siderolabs/talos/commit/9b11d86081df8cf77860d2d27eed5d8001ff721e) chore: rekres to configure slack notify workflow for CI failures * [`5ce6a660f`](https://github.com/siderolabs/talos/commit/5ce6a660f67f4e2776550a1e621179beb8a6788c) docs: augment the pod security docs * [`ada51ff69`](https://github.com/siderolabs/talos/commit/ada51ff696011e15dcd9c661da1d839bdc341745) fix: unmarshal encryption STATE from META * [`b9e9b2e07`](https://github.com/siderolabs/talos/commit/b9e9b2e07a645f53ca23355810d485a2622870c9) docs: add what is new notes for 1.11 * [`53055bdf4`](https://github.com/siderolabs/talos/commit/53055bdf49ce4c81f63c159cdbaa8ea85d9ca2b8) docs: fix typo in kubevirt page * [`8d12db480`](https://github.com/siderolabs/talos/commit/8d12db480c38ec37aee5ae7721b2e5ca55ad733e) fix: one more attempt to fix volume mount race on restart * [`34d37a268`](https://github.com/siderolabs/talos/commit/34d37a268a9e0098179369af128261dbfc956d1d) chore: rekres to use correct slack channel for slack-notify * [`326a00538`](https://github.com/siderolabs/talos/commit/326a00538210bf98b01795d314c1e154a74d2d58) feat: implement `talos.config.early` command line arg * [`a5f3000f2`](https://github.com/siderolabs/talos/commit/a5f3000f2e8a79d4e9a5be95fbcac91a2d78675b) feat: implement encryption locking to STATE * [`c1e65a342`](https://github.com/siderolabs/talos/commit/c1e65a34256944743e768613b119c0caa517b54d) docs: remove talos API flags from mgmt commands * [`181d0bbf5`](https://github.com/siderolabs/talos/commit/181d0bbf5381343d35a01190da45e3442320d7c5) feat: bootedentry resource * [`7ad439ac3`](https://github.com/siderolabs/talos/commit/7ad439ac35859695074d3a3efdcdb5c0cab1a5c6) fix: enforce minimum size on user volumes if not set explicitly * [`50e37aefd`](https://github.com/siderolabs/talos/commit/50e37aefdbde973bcc8aa352639946490fbe7d94) fix: live reload of TLS client config for discovery client * [`87efd75ef`](https://github.com/siderolabs/talos/commit/87efd75efb3e62b88b4f65a221f9fbdd4b4d6ef9) feat: update containerd to 2.1.4 * [`724b9de6d`](https://github.com/siderolabs/talos/commit/724b9de6d5195bcccc5f484c696429b2f09ab16e) feat: add F71808E watchdog driver * [`8af96f7af`](https://github.com/siderolabs/talos/commit/8af96f7afdac1c4d5e2697b897b81e2bddd15f66) docs: add ETCD downgrade documentation * [`44edd205d`](https://github.com/siderolabs/talos/commit/44edd205d5fdffab39b65ee62695a40e22ef188c) docs: add remark about 'exclude-from-external-load-balancers' label * [`727101926`](https://github.com/siderolabs/talos/commit/7271019263b0dc5b28d2764d19fe531e473222fc) fix(ci): use a random suffix for ami names * [`d621ce372`](https://github.com/siderolabs/talos/commit/d621ce3726f20ee568ea3b6ac57d9e8dfa0580cc) fix: grype scan * [`d62e255c2`](https://github.com/siderolabs/talos/commit/d62e255c260810a5f0f2959e32592a3331df28d3) fix: issues with reading GPT * [`5d0883e14`](https://github.com/siderolabs/talos/commit/5d0883e147163c12a77cd926db799ffed854aedf) feat: update PCI DB module to v0.3.2 * [`3751c8ccf`](https://github.com/siderolabs/talos/commit/3751c8ccfa1bab9fcd435290f36e9012a5626e40) test: wait for service account test job longer * [`a592eb9f9`](https://github.com/siderolabs/talos/commit/a592eb9f98788883a7ec6d17772e10707230a0d8) feat: update Linux to 6.12.40 * [`4c40e6d3f`](https://github.com/siderolabs/talos/commit/4c40e6d3fb4c2f451a8d7a671df5f6254161bd5d) feat: update etcd to 3.6.4 * [`2bc37bd2c`](https://github.com/siderolabs/talos/commit/2bc37bd2c9679c8055fd7b52eb310f23a329af4e) docs: fix error in kernel module guide * [`bfc57fb86`](https://github.com/siderolabs/talos/commit/bfc57fb863224f7626f49e5b26be06f77bea2e40) chore: tag aws snapshots created via ci with the image name * [`06ef7108a`](https://github.com/siderolabs/talos/commit/06ef7108a6050b3a8fd7535f01a469f09042bf56) fix: issue with volume remount on service restart * [`03efbff18`](https://github.com/siderolabs/talos/commit/03efbff18e420c4fe960f490f91dd9f4751ece04) docs: add SBOM documentation * [`af8a2869d`](https://github.com/siderolabs/talos/commit/af8a2869dbbec073ffaf72a1378682e109b053ec) fix: do not download artifacts for cron Grype scan * [`5f442159b`](https://github.com/siderolabs/talos/commit/5f442159b224c96c90badc7176fed17bfb561709) feat: unify disk encryption configuration * [`38e176e59`](https://github.com/siderolabs/talos/commit/38e176e594edb3d271d98f78417b9fd5ba0c5288) chore(ci): fix datasource versioning * [`85d6b9198`](https://github.com/siderolabs/talos/commit/85d6b919890a1aa9c4f94d5b18861cc617134ff9) feat: update etcd to v3.5.22 * [`dd7bd2dab`](https://github.com/siderolabs/talos/commit/dd7bd2dab8cf09334e3e353d6a477509bbaa303e) docs: rewrite the getting started and prod docs for v1.10 and v1.11 * [`136a899aa`](https://github.com/siderolabs/talos/commit/136a899aa25b3fdcdd771594668278d563f09192) chore: regenerate release step with signing fixes * [`450b30d5a`](https://github.com/siderolabs/talos/commit/450b30d5a986563869efdbaa074e82d612f6f2ef) chore(ci): add more nvidia test matrix * [`451c2c4c3`](https://github.com/siderolabs/talos/commit/451c2c4c39e70c20df58fc31459cd5c789a0e46f) test: add talosctl:latest to the image cache

### Changes since v1.12.0-alpha.1
66 commits

* [`3d811a4c8`](https://github.com/siderolabs/talos/commit/3d811a4c81e010b157b277499d272dc0e934baa9) release(v1.12.0-alpha.2): prepare release * [`fb4bfe851`](https://github.com/siderolabs/talos/commit/fb4bfe851c7c308eeaf4a11e0ac5c944f66dc0c4) chore: fix LVM test * [`f4ee0d112`](https://github.com/siderolabs/talos/commit/f4ee0d1128ba2f35d54ec3d35a83fc62fd222f2e) chore: disable VIP operator test * [`288f63872`](https://github.com/siderolabs/talos/commit/288f6387260843570d53d28a4d77e564b3182979) feat: bump deps * [`b66482c52`](https://github.com/siderolabs/talos/commit/b66482c529beda8b1abf9ed6b71ece354c1540be) feat: allow disabling injection of extra cmdline in cluster create * [`704b5f99e`](https://github.com/siderolabs/talos/commit/704b5f99e6bef4410629427ac65fd2742ddb335d) feat: update Kubernetes to 1.35.0-alpha.2 * [`1dffa5d99`](https://github.com/siderolabs/talos/commit/1dffa5d9965a6c7d872f052bfb1750ea550671c2) feat: implement virtual IP operator config * [`43b1d7537`](https://github.com/siderolabs/talos/commit/43b1d7537507a916629cc2d6db7440a99ffcb748) fix: validate provisioner when destroying local clusters * [`b494c54c8`](https://github.com/siderolabs/talos/commit/b494c54c81e6ca81cef8ce26da772c1fc336ea8d) fix: talos import on non-linux * [`61e95cb4b`](https://github.com/siderolabs/talos/commit/61e95cb4b7b354d175d1dfce3d0fa43deefad187) feat: support bootloader option for ISO * [`d11072726`](https://github.com/siderolabs/talos/commit/d110727263c57c02392f201938d2b71976b8c4d6) fix: provide offset for partitions in discovered volumes * [`39eeae963`](https://github.com/siderolabs/talos/commit/39eeae96311be2b8e2d3660d878f852ba92ca064) feat: update dependencies * [`9890a9a31`](https://github.com/siderolabs/talos/commit/9890a9a31deb11ab170b94c667143314db08f76f) test: fix OOM test * [`c0772b8ed`](https://github.com/siderolabs/talos/commit/c0772b8eda429675a06899b9c4a4d1dd7d5f6a5f) feat: add airgapped mode to QEMU backed talos * [`ac60a9e27`](https://github.com/siderolabs/talos/commit/ac60a9e27deed63db0e4e61ffa30d46f4cab590a) fix: update test for PCI driver rebind/IOMMU * [`6c98f4cdb`](https://github.com/siderolabs/talos/commit/6c98f4cdb049c58ef4f6e8193ef66c2338a2877d) feat: implement new DHCP network configuration * [`da92a756d`](https://github.com/siderolabs/talos/commit/da92a756d9668fa043b4794db45d5c985d8ea4a6) fix: drop 'ro' falg from defaults * [`28fd2390c`](https://github.com/siderolabs/talos/commit/28fd2390cb6e02f400bb237dd674c7d0d40f8ed3) fix: imager build on arm64 * [`4e12df8c5`](https://github.com/siderolabs/talos/commit/4e12df8c5c27ae115c4eac70a7e2fceb03dac5f5) test: integration test for OOM controller * [`7e498faba`](https://github.com/siderolabs/talos/commit/7e498faba93f972ba82edf41550d3b94256e83e9) feat: use image signer * [`eccb21dd3`](https://github.com/siderolabs/talos/commit/eccb21dd3ba03eb4ab03c4da87a51a4e3d8da49a) feat: add presets to the 'cluster create qemu' command * [`ec0a813fa`](https://github.com/siderolabs/talos/commit/ec0a813facf5be5ca3e9ba65924ae18b2b05a7d9) feat: unify cmdline handling GRUB/systemd-boot * [`37e4c40c6`](https://github.com/siderolabs/talos/commit/37e4c40c6a2477e45bbf067effc4389d4639c905) fix: skip module signature tests on docker provisioner only * [`8124efb42`](https://github.com/siderolabs/talos/commit/8124efb42fd5a3eb81f41e84974e4242246ca7c4) fix: cache e2e * [`4adcda0f5`](https://github.com/siderolabs/talos/commit/4adcda0f5427e1bae49f6dda58318324a3b24ac5) fix: reserve the apid and trustd ports from the ephemeral port range * [`ced57b047`](https://github.com/siderolabs/talos/commit/ced57b047a389e26f7e5bfa3efab5b64f3fced87) feat: support optionally disabling module sig verification * [`1e5c4ed64`](https://github.com/siderolabs/talos/commit/1e5c4ed644cbc60d8518fe4298e63a5cf5dc8cf5) fix: build talosctl image cache-serve non-linux * [`dbdd2b237`](https://github.com/siderolabs/talos/commit/dbdd2b237e0aefbba439b90472abf9ec7eea6aa6) feat: add static registry to talosctl * [`77d8cc7c5`](https://github.com/siderolabs/talos/commit/77d8cc7c589a190c8cb86e6e1684233129b648a1) chore: push `latest` tag only on main * [`59d9b1c75`](https://github.com/siderolabs/talos/commit/59d9b1c75dbff09e405906ebcfb3ad1a69cb8f4b) feat: update dependencies * [`bf6ad5171`](https://github.com/siderolabs/talos/commit/bf6ad51710c367764e582ccc1fb77b4d989c874d) feat: add back install script * [`da451c5ba`](https://github.com/siderolabs/talos/commit/da451c5ba4ee97e7ef108bb6d73d5aa8bc7c72fd) chore: drop documentation except for fresh reference * [`2f23fedeb`](https://github.com/siderolabs/talos/commit/2f23fedeb725a5786b6ffac2aef8125eecd6cb6e) fix: file leak in reading cgroups * [`b412ffdbc`](https://github.com/siderolabs/talos/commit/b412ffdbc29d77a81aed88be62f21bc2999afcde) docs: update README.md for docs link * [`8dc51bae7`](https://github.com/siderolabs/talos/commit/8dc51bae79a37b56c058d40787dbda6e828fd0d3) feat: add drm_gpuvm and drm_gpusvm_helper modules * [`4ca58aeb8`](https://github.com/siderolabs/talos/commit/4ca58aeb81145cb7ebef071865b3d853a4712729) fix: make Akamai platform usable * [`061f8e76f`](https://github.com/siderolabs/talos/commit/061f8e76fd58906ff823a0e467d6efcf5161ed9f) feat: bump pkgs * [`a9fa852da`](https://github.com/siderolabs/talos/commit/a9fa852dadd75740d73588fd2156f6f1ad782fdd) feat: update uefi image to talos linux logo * [`04753ba69`](https://github.com/siderolabs/talos/commit/04753ba6983b6ff2754cf62b8d60cc6065921dbd) feat: update go to 1.25.2 * [`9a42b05bd`](https://github.com/siderolabs/talos/commit/9a42b05bdac2bf0cbbc97d040be7860f48c69386) feat: implement link aliasing * [`d732bd0be`](https://github.com/siderolabs/talos/commit/d732bd0be73c3d17d140c00be0e9d27ea621909b) chore(ci): run only nvidia tests for NVIDIA workflows * [`8d1468209`](https://github.com/siderolabs/talos/commit/8d1468209aa28f59df9dc52466c506defa8c3cc3) fix: stop populating apiserver cert SANs * [`02473244c`](https://github.com/siderolabs/talos/commit/02473244c17ef0149515f300bcd201f9347acabc) fix: wait for mount status to be proper mode * [`825622d90`](https://github.com/siderolabs/talos/commit/825622d90a7716f7b6027651a5b9389173432393) fix: resource proto definitions * [`2c6003e79`](https://github.com/siderolabs/talos/commit/2c6003e790003f6ef1a03b8d2af8030fb57c5d02) docs: add Project Calico installation in two mode * [`4fb4c8678`](https://github.com/siderolabs/talos/commit/4fb4c86780def54eed4d999b1f0ce93042269076) feat: add disk.EnableUUID to generated ova * [`33fb48f8f`](https://github.com/siderolabs/talos/commit/33fb48f8f90ccf44e95c93ac7ec1adcd1b4e0373) fix: add dashboard spinner * [`053fd0bd4`](https://github.com/siderolabs/talos/commit/053fd0bd4d324bc21e076b3a30466ed61c7684e1) feat: update Linux to 6.17 * [`34e107e1b`](https://github.com/siderolabs/talos/commit/34e107e1bd14b0a56ebfa0c65e0c7da715976d99) docs: fix broken link * [`dfbece56b`](https://github.com/siderolabs/talos/commit/dfbece56bd45e95c9ec477af4b53ffcefdfec66c) docs: update the kubespan docs * [`8b041a72c`](https://github.com/siderolabs/talos/commit/8b041a72ca9c07985c024c1136c85c85df92beda) docs: update scaleway.md * [`435dcbf82`](https://github.com/siderolabs/talos/commit/435dcbf820cd9f8cc9fecc0f7d42819acef36106) fix: provide nocloud metadata with missing network config * [`ec3bd878f`](https://github.com/siderolabs/talos/commit/ec3bd878f9770ceb932b654aabad1711880da829) refactor: remove the go-blockdevice v1 completely * [`33544bde9`](https://github.com/siderolabs/talos/commit/33544bde9c15745f4ae692c7647d661b32d4bed4) fix: minor improvements to fs * [`fd2eebf7f`](https://github.com/siderolabs/talos/commit/fd2eebf7fa4831d33383a53d6d058c74789553e4) feat: create merge patch from diff of two machine configs * [`eadbdda94`](https://github.com/siderolabs/talos/commit/eadbdda9471289fae5159c8cc024a735a1547807) fix: uefi boot order setting * [`cd9fb2743`](https://github.com/siderolabs/talos/commit/cd9fb274342c5a973b3d087b991a7eea5df4142a) fix: support secure HTTP proxy with gRPC dial * [`adf87b4b9`](https://github.com/siderolabs/talos/commit/adf87b4b931ded1edeb64217b0e9d5edfd046004) feat: update Flannel to v0.27.4 * [`5dfb7e1fe`](https://github.com/siderolabs/talos/commit/5dfb7e1fe7d9cc6db3e4c2b6f587e641b4a0842b) feat: serve etcd image from registry.k8s.io * [`5ca841804`](https://github.com/siderolabs/talos/commit/5ca8418049e3b878585014a3764021f2d30a0df7) fix: nftables flaky test * [`a940e45a7`](https://github.com/siderolabs/talos/commit/a940e45a7fe041b17437f774eb52b9f3a42e3633) feat: generate list of images required to build talos * [`3472d6e79`](https://github.com/siderolabs/talos/commit/3472d6e79caa13fd42df7774101397b0a30f62f5) fix: revert "chore: use new mount/v3 package in efivarfs" * [`42c0bdbf3`](https://github.com/siderolabs/talos/commit/42c0bdbf320bf24311b2d56b2e0f7155e86b3713) feat: add provisioner flag to images default command * [`6bc0b1bcf`](https://github.com/siderolabs/talos/commit/6bc0b1bcf7d9dc9f2417a7db63d1e76e7ddc6aa3) feat: drop and lock deprecated features * [`362a8e63b`](https://github.com/siderolabs/talos/commit/362a8e63b798c4a4fc31fe5e728d2429fc953166) fix: change the compression format * [`6e58f58aa`](https://github.com/siderolabs/talos/commit/6e58f58aaeb6e16883d8dc8757ad92b6b6da7e84) fix: mkdir artifacts path

### Changes from siderolabs/crypto
2 commits

* [`4154a77`](https://github.com/siderolabs/crypto/commit/4154a771b09f0023e0d258bba6aecc29febabecb) feat: implement dynamic certificate reloader * [`dae07fa`](https://github.com/siderolabs/crypto/commit/dae07fa14f963b34ea67abf0cbc50ba24f280524) chore: update to Go 1.25

### Changes from siderolabs/go-api-signature
2 commits

* [`184f94d`](https://github.com/siderolabs/go-api-signature/commit/184f94d36cdd4d8bf8770ef629191f63187d63da) chore: rekres and bump go to 1.25.2 * [`68478e2`](https://github.com/siderolabs/go-api-signature/commit/68478e2f57a3bca4345c6e189c0a4216dfb9b1ed) fix: return `invalid signature` error when a signature is required

### Changes from siderolabs/go-debug
2 commits

* [`d51e25a`](https://github.com/siderolabs/go-debug/commit/d51e25a0f0b97c3427ff9f7bff4d60418be14d5d) chore: rekres, bump deps and go * [`e21721b`](https://github.com/siderolabs/go-debug/commit/e21721bc4faba9072b5e4e33af60a1f0292547af) chore: add support for Go 1.25

### Changes from siderolabs/go-kubernetes
1 commit

* [`8454fe9`](https://github.com/siderolabs/go-kubernetes/commit/8454fe9977f5240a1251c2df1b4f93ca73b869a7) feat: add upgrade path for 1.35

### Changes from siderolabs/go-loadbalancer
1 commit

* [`5e7a8b2`](https://github.com/siderolabs/go-loadbalancer/commit/5e7a8b21cbdb156c6fe6f9fd98b8a1bb1186c21c) feat: add jitter and initial health check wait support to upstreams

### Changes from siderolabs/pkgs
47 commits

* [`da97c36`](https://github.com/siderolabs/pkgs/commit/da97c368be82a0d8effd8eae257a87e5f2e29079) feat: update linux-firmware * [`6d58d7f`](https://github.com/siderolabs/pkgs/commit/6d58d7f86e6499ac5c229d93e0c370ea6370c5e6) feat: bump deps * [`b535af8`](https://github.com/siderolabs/pkgs/commit/b535af8b28844dfb88904db531cffc5ec86aa490) feat: update dependencies * [`a098092`](https://github.com/siderolabs/pkgs/commit/a0980927d55c05aaab3bf3d8439d29304ad88972) feat: update Linux to 6.17.3, tt-kmd to 2.4.1 * [`661e578`](https://github.com/siderolabs/pkgs/commit/661e5788713e20df7be51ae90f76f94e45039bf8) feat: add xe extension * [`8ddac2d`](https://github.com/siderolabs/pkgs/commit/8ddac2da15d625144cfee4613f04bcdec2224297) feat: bump go * [`332303e`](https://github.com/siderolabs/pkgs/commit/332303ee2228694fe12979aa3fe03748d4eb4bd0) fix: rollback libseccomp version * [`f62ebca`](https://github.com/siderolabs/pkgs/commit/f62ebca758af069f23c46817e248aaf1674f39be) chore: update dependencies * [`56f8ae3`](https://github.com/siderolabs/pkgs/commit/56f8ae370d784ef61420bdd053eceefeaa12e194) feat: update Linux to 6.17.1, NVIDIA LTS to 580.95.05 * [`20b1849`](https://github.com/siderolabs/pkgs/commit/20b1849146c50bce39582455717776d330973579) fix: revert "feat" support adding extra trusted certificates in the kernel" * [`1e3d375`](https://github.com/siderolabs/pkgs/commit/1e3d37514394b4ed9c7b86b1a55b7ff633252142) feat: bump go * [`ddfd7af`](https://github.com/siderolabs/pkgs/commit/ddfd7afebe9a73c1abe291c03ec3b2e0cb41c0c5) feat: bump dependencies * [`4dc7709`](https://github.com/siderolabs/pkgs/commit/4dc770905a581c471c7b6eb0eca85177f5cbdd24) feat: update runc to 1.3.2 * [`61d8b44`](https://github.com/siderolabs/pkgs/commit/61d8b44a153211521c1787ebd2478d05232d9a97) chore: fix renovate config for urcu & hailort * [`5bda512`](https://github.com/siderolabs/pkgs/commit/5bda51205cee2090e83c33697812d2b80a3c6b67) feat: upgrade Linux to 6.17 * [`202a8e6`](https://github.com/siderolabs/pkgs/commit/202a8e663efaf24b662f349c82c5b6addeb6b3a2) feat: update Linux to 6.16.9 * [`3a0900f`](https://github.com/siderolabs/pkgs/commit/3a0900f8b464f22773a153c65d85c6dd075cadba) feat: enable SRv6 LWTUNNEL and BPF support * [`628efc8`](https://github.com/siderolabs/pkgs/commit/628efc86c2020464c4ee58f419b8b17c8f87aa45) chore: update linuxfirmware and rekres * [`9d1fb02`](https://github.com/siderolabs/pkgs/commit/9d1fb029db9837af5529f7de371cc9bc709e8cbe) feat: support adding extra trusted certificates in the kernel * [`7fe686d`](https://github.com/siderolabs/pkgs/commit/7fe686dec6959a32a6a6d6610cedd18d963412ad) fix: build nftables with embedded gmp * [`fede0a7`](https://github.com/siderolabs/pkgs/commit/fede0a7ec672e835372b8915896fd0e775da0297) feat: add nft binary * [`0dae01a`](https://github.com/siderolabs/pkgs/commit/0dae01a735bae635ab987febac42c382d851d7d0) feat: update NVIDIA to 580.82.07 * [`9ac2392`](https://github.com/siderolabs/pkgs/commit/9ac23925cf350075a4931a54176fdd3d9b9b7cb7) feat: enable Kernel config options for IPVS Maglev hashing scheduler support * [`3c5315c`](https://github.com/siderolabs/pkgs/commit/3c5315cd2ae931eb76233d26283f861ace932b1d) feat: update dependencies * [`122fa66`](https://github.com/siderolabs/pkgs/commit/122fa6626ff8c300623caa625bdc6bc72e866494) feat: update Linux to 6.16.6 * [`ab1e866`](https://github.com/siderolabs/pkgs/commit/ab1e86612ac903b811ecd3b76d55e85951190565) feat: update Go to 1.25.1 * [`7d6ef1b`](https://github.com/siderolabs/pkgs/commit/7d6ef1b187956a8cb2c837f339169b6cb7eb9c12) feat: update runc to 1.3.1 * [`e067c20`](https://github.com/siderolabs/pkgs/commit/e067c2015302c5069676727f047b6aef9ce0dc0c) feat: enable USB audio support * [`c4faa38`](https://github.com/siderolabs/pkgs/commit/c4faa389341f6be5dad5fdd575acf61e9eb170f8) feat: bump dependencies * [`453cdfc`](https://github.com/siderolabs/pkgs/commit/453cdfc2fdea9945c8e3531ef190971211eaaf13) feat: enable ublk support * [`9824684`](https://github.com/siderolabs/pkgs/commit/982468471d8118ac4653b704512e5f847a148dd7) fix: enable memcg v1 * [`2447e11`](https://github.com/siderolabs/pkgs/commit/2447e11dcbcb5dc10703515e2185f753b04e20e0) feat: update Linux to 6.16, GCC to 15 * [`2cfb920`](https://github.com/siderolabs/pkgs/commit/2cfb920acd88d63c3d3ced3a5760549aa180208c) feat: update Linux to 6.15.11, update tools, rekres * [`ab4e975`](https://github.com/siderolabs/pkgs/commit/ab4e9755b0e2dbf38c75db5d2ff7720f511fd50c) feat: update Linux to 6.12.43 * [`cd67e36`](https://github.com/siderolabs/pkgs/commit/cd67e3660fa0a2ad25ca4b8dcd3c1ce9b96b0b72) chore: update kernel config to support max SMP CPUs * [`e3b2094`](https://github.com/siderolabs/pkgs/commit/e3b209474060f5a67e36c9239a3a066ee8ace2fe) fix: fix build for new NVIDIA drivers * [`fd5fdfd`](https://github.com/siderolabs/pkgs/commit/fd5fdfde0bdc4dfc1e9990300df46b9af23c0dfd) feat: update Nvidia LTS to 580.65.06 and production to 570.172.08 * [`0edf426`](https://github.com/siderolabs/pkgs/commit/0edf426d758d67f7baaaa42facdc658396f02f9f) fix: backport CVE kernel patches to 6.12 * [`26d8fef`](https://github.com/siderolabs/pkgs/commit/26d8fefe10329e8d1c285014af0bffe1b9a65431) feat: enable Infiniband IRDMA support * [`16b5fac`](https://github.com/siderolabs/pkgs/commit/16b5facdbb37f2ad0329bf131ded62cc9b1239a9) fix: re-enable CPUSETS_V1 cgroups controller * [`fd53886`](https://github.com/siderolabs/pkgs/commit/fd53886f4f36e73181b7b1a0718801bf8e2aadb9) feat: update backportable dependencies * [`d5f7467`](https://github.com/siderolabs/pkgs/commit/d5f746715727ec34fca7a87ab9f1fac2051f0f75) feat: update Go to 1.24.6 * [`0bd019f`](https://github.com/siderolabs/pkgs/commit/0bd019f29031b7461fbe49552b88d0e26861f955) feat: update containerd to 2.1.4 * [`0ba8b5b`](https://github.com/siderolabs/pkgs/commit/0ba8b5b49f3dedcc49f4040a6f5c57329f5c5605) feat: enable F71808E watchdog driver * [`895a86b`](https://github.com/siderolabs/pkgs/commit/895a86bcdfedfd9ca1a698d8f8aa71e3600a22c2) fix: enable ISCSI IBFT * [`a76a67c`](https://github.com/siderolabs/pkgs/commit/a76a67c860a5100f41223fea936712760b33a4cd) feat: update Linux to 6.12.40 * [`8b0a561`](https://github.com/siderolabs/pkgs/commit/8b0a56180198d360ea71b2c62669545b867f9a67) feat: enable bootloader control on amd64

### Changes from siderolabs/tools
17 commits

* [`a08cc1f`](https://github.com/siderolabs/tools/commit/a08cc1ff80c045b3683ae06d27d2ea2abcabfad2) feat: update git to 2.51.1 * [`e62d613`](https://github.com/siderolabs/tools/commit/e62d613d3113857052acd0eda598805ab73e3dcd) feat: bump go * [`916b464`](https://github.com/siderolabs/tools/commit/916b4646151ec97a26ef3d1701d084820c41479c) fix: add pkgconf for ncurses, fix Renovate configs, bump deps * [`11f0337`](https://github.com/siderolabs/tools/commit/11f0337f796f3e39b60ae2ade4babdf5fb67534f) feat: update Go * [`2c56d7a`](https://github.com/siderolabs/tools/commit/2c56d7ae2e68295ce4820b19e9dc96d108b76632) feat: update OpenSSL to 3.5.4 * [`8f27cfa`](https://github.com/siderolabs/tools/commit/8f27cfab6454aea38a70a9500bf8859f4d04cc21) feat: update dependencies * [`1c1420e`](https://github.com/siderolabs/tools/commit/1c1420e33eea2ae3c6b0d054474d9bfe1b34c88f) feat: add tinfo to ncurses * [`7c7328b`](https://github.com/siderolabs/tools/commit/7c7328b599b4cbac35ed7140798dd61c372e628a) fix: set regex in renovate config directly * [`3ab353b`](https://github.com/siderolabs/tools/commit/3ab353bc82f0d0c76d5bcc4022c851ae5e802d77) fix: modify renovate regex on ca_certificates * [`4f90801`](https://github.com/siderolabs/tools/commit/4f908016475257651dc41a4250c2c843b5373d08) chore: update openssl, curl, libexpat and rekres * [`c37ac80`](https://github.com/siderolabs/tools/commit/c37ac805a17daa8c0dc26da18b768864b821920b) feat: update Go to 1.25.1 * [`7c659e9`](https://github.com/siderolabs/tools/commit/7c659e92db3884737abda95e643995107aa14010) feat: update to GCC 15 * [`83fd7b7`](https://github.com/siderolabs/tools/commit/83fd7b7be62f2f59abeb24c971699895759ebb88) feat: migrate from pkg-config to pkgconf * [`edafd5f`](https://github.com/siderolabs/tools/commit/edafd5f395b1fd31650270332a871c830a5fd781) feat: update toolchain for new Go and Linux headers * [`65789c7`](https://github.com/siderolabs/tools/commit/65789c75ebd4020a444789cfd74d35fefc2497c2) chore: drop unused vars from Pkgfile * [`52db66e`](https://github.com/siderolabs/tools/commit/52db66e8d9e1d83e4872a504d45fdabed31504f7) chore: drop protobuf-related stuff from tools * [`e3c3ef2`](https://github.com/siderolabs/tools/commit/e3c3ef2b604fb80143a17879eeec9f30ca7b07dd) feat: update Go to 1.24.6

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.7.0 -> v0.9.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.18.1 -> v1.19.1 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.10.1 -> v1.13.0 * **github.com/aws/aws-sdk-go-v2/config** v1.29.17 -> v1.31.13 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.32 -> v1.18.10 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.41.2 -> v1.46.0 * **github.com/aws/smithy-go** v1.22.4 -> v1.23.1 * **github.com/beevik/ntp** v1.4.3 -> v1.5.0 * **github.com/containernetworking/plugins** v1.7.1 -> v1.8.0 * **github.com/cosi-project/runtime** v1.10.7 -> v1.11.0 * **github.com/docker/cli** v28.3.3 -> v28.5.1 * **github.com/docker/docker** v28.3.3 -> v28.5.1 * **github.com/docker/go-connections** v0.5.0 -> v0.6.0 * **github.com/equinix-ms/go-vmw-guestrpc** v0.1.1 -> v1.0.0 * **github.com/florianl/go-tc** v0.4.5 -> v0.4.7 * **github.com/foxboron/go-uefi** a3183a1bfc84 -> d29549a44f29 * **github.com/gdamore/tcell/v2** v2.8.1 -> v2.9.0 * **github.com/google/cel-go** v0.26.0 -> v0.26.1 * **github.com/google/go-tpm** v0.9.5 -> v0.9.6 * **github.com/gopacket/gopacket** v1.3.1 -> v1.4.0 * **github.com/hetznercloud/hcloud-go/v2** v2.22.0 -> v2.28.0 * **github.com/insomniacslk/dhcp** 8abf58130905 -> 175e84fbb167 * **github.com/mdlayher/netlink** fbb4dce95f42 -> v1.8.0 * **github.com/miekg/dns** v1.1.67 -> v1.1.68 * **github.com/rivo/tview** a4a78f1e05cb -> v0.42.0 * **github.com/safchain/ethtool** v0.6.1 -> v0.6.2 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.34 -> v1.0.0-beta.35 * **github.com/siderolabs/crypto** v0.6.3 -> v0.6.4 * **github.com/siderolabs/go-api-signature** v0.3.7 -> v0.3.9 * **github.com/siderolabs/go-debug** v0.5.0 -> v0.6.1 * **github.com/siderolabs/go-kubernetes** v0.2.26 -> v0.2.27 * **github.com/siderolabs/go-loadbalancer** v0.4.0 -> v0.5.0 * **github.com/siderolabs/pkgs** v1.11.0-15-g2ac857a -> v1.12.0-alpha.0-45-gda97c36 * **github.com/siderolabs/talos/pkg/machinery** v1.11.0 -> v1.12.0-alpha.1 * **github.com/siderolabs/tools** v1.11.0-2-g8556c73 -> v1.12.0-alpha.0-16-ga08cc1f * **github.com/spf13/cobra** v1.9.1 -> v1.10.1 * **github.com/spf13/pflag** v1.0.7 -> v1.0.10 * **github.com/stretchr/testify** v1.10.0 -> v1.11.1 * **github.com/u-root/u-root** v0.14.0 -> v0.15.0 * **go.etcd.io/etcd/api/v3** v3.6.4 -> v3.6.5 * **go.etcd.io/etcd/client/pkg/v3** v3.6.4 -> v3.6.5 * **go.etcd.io/etcd/client/v3** v3.6.4 -> v3.6.5 * **go.etcd.io/etcd/etcdutl/v3** v3.6.4 -> v3.6.5 * **golang.org/x/net** v0.42.0 -> v0.46.0 * **golang.org/x/oauth2** v0.30.0 -> v0.32.0 * **golang.org/x/sync** v0.16.0 -> v0.17.0 * **golang.org/x/sys** v0.34.0 -> v0.37.0 * **golang.org/x/term** v0.33.0 -> v0.36.0 * **golang.org/x/text** v0.27.0 -> v0.30.0 * **golang.org/x/time** v0.12.0 -> v0.14.0 * **google.golang.org/grpc** v1.73.0 -> v1.76.0 * **google.golang.org/protobuf** v1.36.6 -> v1.36.10 * **gopkg.in/typ.v4** v4.4.0 **_new_** * **k8s.io/api** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/apiextensions-apiserver** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/apimachinery** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/apiserver** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/client-go** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/component-base** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/cri-api** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/kube-scheduler** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/kubectl** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/kubelet** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/pod-security-admission** v0.34.0 -> v0.35.0-alpha.2 * **k8s.io/utils** 4c0f3b243397 -> bc988d571ff4 Previous release can be found at [v1.11.0](https://github.com/siderolabs/talos/releases/tag/v1.11.0) ## [Talos 1.12.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.12.0-alpha.1) (2025-10-01) Welcome to the v1.12.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Disk Encryption Talos versions prior to v1.12 used the state of PCR 7 and signed policies locked to PCR 11 for TPM based disk encryption. Talos now supports configuring which PCRs states are to be used for TPM based disk encryption via the `options.pcrs` field in the `tpm` section of the disk encryption configuration. If user doesn't specify any options Talos defaults to using PCR 7 for backwards compatibility with existing installations. This change was made to improve compatibility with systems that may have varying states in PCR 7 due to UEFI Secure Boot configurations and users may wish to disable locking to PCR 7 state entirely. Signed PCR policies will still be bound to PCR 11. The currently used PCR's can be seen with `talosctl get volumestatus -o yaml` command. ### Embedded Config Talos Linux now supports [embedding the machine configuration](https://www.talos.dev/v1.12/talos-guides/configuration/acquire/) directly into the boot image. ### Ethernet Configuration The Ethernet configuration now includes a `wakeOnLAN` field to enable Wake-on-LAN (WOL) support. This field can be set to enable WOL and specify the desired WOL modes. ### Extra Binaries Talos Linux now ships with `nft` binary in the rootfs to support CNIs which shell out to `nft` command. ### Kernel Security Posture Profile (KSPP) Talos now enables a stricter set of KSPP sysctl settings by default. The list of overridden settings is available with `talosctl get kernelparamstatus` command. ### Encrypted Volumes Talos Linux now consistently provides mapped names for encrypted volumes in the format `/dev/mapper/luks2-`. This change should not affect system or user volumes, but might allow easier identification of encrypted volumes, and specifically for raw encrypted volumes. ### Component Updates Linux: 6.16.9 Kubernetes: 1.34.1 CNI Plugins: 1.8.0 cryptsetup: 2.8.1 LVM2: 2_03_34 systemd-udevd: 257.8 runc: 1.3.1 CoreDNS: 1.12.4 etcd: 3.6.5 Talos is built with Go 1.25.1. ### Contributors * Andrey Smirnov * Noel Georgi * Amarachi Iheanacho * Dmitrii Sharshakov * Mateusz Urbanek * Orzelius * Oguz Kilcan * George Gaál * Utku Ozdemir * 459below * Alp Celik * Andrew Longwill * Chris Sanders * Dmitry * Febrian * Fred Heinecke * Giau. Tran Minh * Guillaume LEGRAIN * Jorik Jonker * Justin Garrison * Markus Freitag * Max Makarov * Mike Beaumont * Misha Aksenov * MrMrRubic * Olivier Doucet * Sammy ETUR * Serge Logvinov * Skyler Mäntysaari * Tom * aurh1l * frozenprocess * kassad * leppeK * winnie ### Changes
178 commits

* [`e455c7ea9`](https://github.com/siderolabs/talos/commit/e455c7ea9c919a2f70ddecceaa8f3b4e25566048) chore: use testing/synctest in tests * [`7f048e962`](https://github.com/siderolabs/talos/commit/7f048e962e217687ab67ed7027c5228e8ccb7d16) feat: update dependencies * [`fe36b3d32`](https://github.com/siderolabs/talos/commit/fe36b3d3200db57f3e21017ff7a4808b330a1d55) fix: stop returning EINVAL on remount of detached mounts * [`c6279e04c`](https://github.com/siderolabs/talos/commit/c6279e04c45504af243c0aef9f255317426b4ca0) chore: use new mount/v3 package in efivarfs * [`d5197effb`](https://github.com/siderolabs/talos/commit/d5197effb0b48290d613140b68796cb8f30b9a70) feat: update etcd 3.6.5, CoreDNS 1.12.4 * [`33714b715`](https://github.com/siderolabs/talos/commit/33714b7158a0d569be1d0b1d7b012280856db484) feat: release cloud image using factory * [`d10a2747e`](https://github.com/siderolabs/talos/commit/d10a2747e0e835876aff158e6b6f7882cef9fa44) docs: deprecate JSON6902 patches and interactive installer * [`1e604cbf5`](https://github.com/siderolabs/talos/commit/1e604cbf514bece1e112d8afd5d1cd6ccb1045c3) fix: don't set broadcast for /31 and /32 addresses * [`65a66097a`](https://github.com/siderolabs/talos/commit/65a66097a05e5c0e2334d5eff494a0e71534716f) refactor: split cluster create logic into smaller parts * [`ab847310e`](https://github.com/siderolabs/talos/commit/ab847310efde540b5bfe17570b99af1bb705832b) fix: provide refreshing CA pool (resolvers) * [`d63c3ed7d`](https://github.com/siderolabs/talos/commit/d63c3ed7db2b22f7e394fc45d101d03cba463177) docs: update secureboot docs * [`493f7ed9d`](https://github.com/siderolabs/talos/commit/493f7ed9d2710eb240eab6b6ab532f41abc818c1) feat: support embedded config * [`251df70f6`](https://github.com/siderolabs/talos/commit/251df70f6d33f1d5a3b1b9e4c0c249d8bc85c4b3) feat: add a userspace OOM controller * [`7bae5b40b`](https://github.com/siderolabs/talos/commit/7bae5b40b4f22f0f07a586ebd9cda9436086a5f8) feat: implement link configuration * [`724857dec`](https://github.com/siderolabs/talos/commit/724857decb95ddeebb2ac5d33c38a71bf7512805) fix(ci): skip netbird extension for tests * [`e06a08698`](https://github.com/siderolabs/talos/commit/e06a086989331f28406e8d4234e02d9a6b83f87d) fix: default gateway as string * [`7ed07412e`](https://github.com/siderolabs/talos/commit/7ed07412e963e6ee91615adbea095944aa6a56e5) fix: uefi boot entry handling logic * [`ea4ed165a`](https://github.com/siderolabs/talos/commit/ea4ed165ad860a5beea17ca2d404bdaa6e5ad933) refactor: efivarfs mock and tests * [`1fca111e2`](https://github.com/siderolabs/talos/commit/1fca111e24bcae81b78f007e67b71c9155c0169f) feat: support setting wake-on-lan for Ethernet * [`94f78dbe7`](https://github.com/siderolabs/talos/commit/94f78dbe798cb227a0c38b70a1d6840803989290) docs: add a documentation for running Talos in KVM * [`46902f8fd`](https://github.com/siderolabs/talos/commit/46902f8fdee257a09be4bc1753c6b3f845ef8089) docs: add TrueFullstaq to adopters * [`a28e5cbd5`](https://github.com/siderolabs/talos/commit/a28e5cbd50d11aa6c253a6a9ce1999b9d45effad) chore: update pkgs and tools * [`7cf403db8`](https://github.com/siderolabs/talos/commit/7cf403db8ca0e1719195001895cfbc12835b0fdd) docs: step-by-step scaleway documentation to get an image * [`687285fa2`](https://github.com/siderolabs/talos/commit/687285fa26ec42dadbfb72580099f6e20bbaf85e) docs: remove 'curl' in wget command * [`9db6dc06c`](https://github.com/siderolabs/talos/commit/9db6dc06c3010cd89ce4cb0ec0bde178db0447a4) feat: stop mounting state partition * [`53ce93aae`](https://github.com/siderolabs/talos/commit/53ce93aaed3bd5bfcbe926fa69ca3b4b8b45c74f) test: try to clear connection refused more aggressively * [`51db5279c`](https://github.com/siderolabs/talos/commit/51db5279c423e4b8637a05e52b26dfc5aa719cbc) fix: bump trustd memory limit * [`25204dc8a`](https://github.com/siderolabs/talos/commit/25204dc8a8df79bc876a0bec2492e1147a81d954) fix(machined): change `constants.MinimumGOAMD64Level` using build tag * [`9cd2d794d`](https://github.com/siderolabs/talos/commit/9cd2d794d060b637dbac5263ae417a4e83d54efe) feat: ship nft binary with Talos rootfs * [`b1416c9fe`](https://github.com/siderolabs/talos/commit/b1416c9fe1d5ea9cd68f9b6b766a288a267cee61) feat: record last log the failed service * [`0b129f9ef`](https://github.com/siderolabs/talos/commit/0b129f9efdf57dd9692f7cece6b97719a7ccf80e) feat: enforce more KSPP and hardening sysctls * [`11872643c`](https://github.com/siderolabs/talos/commit/11872643c310212c52b4fd7e13b6cc7d6ec7e4fc) chore: drop docs folder * [`d30fdcd88`](https://github.com/siderolabs/talos/commit/d30fdcd88f421824cf17b9ecec25be7c8044e857) chore: pass in github token to imager * [`b88f27d80`](https://github.com/siderolabs/talos/commit/b88f27d804d60a706f598b50676dad5dd2a9726a) chore: make reset test code a bit better * [`1cde53d01`](https://github.com/siderolabs/talos/commit/1cde53d0173fd1ae637855e15fe34bb74bb027a0) test: fix several issues with tests * [`16cd127a0`](https://github.com/siderolabs/talos/commit/16cd127a04bb5fc907b7ca04f1c81d4c7150eab2) docs: add docs on updating image cache * [`c3ae92b14`](https://github.com/siderolabs/talos/commit/c3ae92b1424d4a2c9bc18cfa394b10eda6c9a20f) fix: build kernel checks only on linux * [`2120904ec`](https://github.com/siderolabs/talos/commit/2120904ec534a91f66dcea419b5a29e36a16f6e4) feat: create detached tmpfs * [`6bbee6de5`](https://github.com/siderolabs/talos/commit/6bbee6de5b18b25deb4e6f515251187e259aa424) docs: remove 'ceph-data' from volume examples/docs * [`07acb3bd2`](https://github.com/siderolabs/talos/commit/07acb3bd2d4f92e80706d1835130bbe6e944d096) fix: use correct order to determine SideroV1 keys directory path * [`2d57fa002`](https://github.com/siderolabs/talos/commit/2d57fa00281f8090b85097c66df634101b0cde79) fix: trim zero bytes in the DHCP host & domain response * [`451cb5f78`](https://github.com/siderolabs/talos/commit/451cb5f78fac3b2ddfec7d545629fe8c88ea2367) docs: clarify disk partition confusion * [`a2122ee5c`](https://github.com/siderolabs/talos/commit/a2122ee5cb9c84f33e0c4b30e9223bb239621d55) feat: implement HostConfig multi-doc * [`69ab076b4`](https://github.com/siderolabs/talos/commit/69ab076b4d6e52484677ee7f68a853dc4edfe2bc) fix: re-create cgroups when restarting runners * [`297b5cc28`](https://github.com/siderolabs/talos/commit/297b5cc2856710b74b4e0e46b00ae33aea4c1bf7) docs: add docs on node labels * [`e168512dd`](https://github.com/siderolabs/talos/commit/e168512dd020da9eac654dae2ba891cf33415c44) fix: apply 'ro' flag to iso9660 filesystems * [`7f7acfbb9`](https://github.com/siderolabs/talos/commit/7f7acfbb9f10c243d0b132c1ef079cb77d2727e0) docs: fix typo in doc * [`d57882b18`](https://github.com/siderolabs/talos/commit/d57882b1830504fe4bfd5344edae613168db7f0e) feat: update Kubernetes to 1.34.1 * [`f85f82f32`](https://github.com/siderolabs/talos/commit/f85f82f32f098f97588f404550f72d64786fe329) test: fix flakiness in RawVolumes test * [`82569e319`](https://github.com/siderolabs/talos/commit/82569e319eb57b1199db6bfd3e612fb771c8c7cd) feat: update Linux 6.16.6 * [`2fd2ab4e4`](https://github.com/siderolabs/talos/commit/2fd2ab4e43e06910154705d6ef1d0576a7c04a2b) fix: remove CoreDNS cpu limit * [`ce9bc32a0`](https://github.com/siderolabs/talos/commit/ce9bc32a08695873d9054afe2608a76cf7c6088a) chore(ci): rekres to use new runner groups * [`8b64f68f6`](https://github.com/siderolabs/talos/commit/8b64f68f6946c2979f6fe2bf617f31639a927bf8) test: improve test stability * [`272cb860d`](https://github.com/siderolabs/talos/commit/272cb860d4cfb8464b29ff31567e25fe6c275849) chore: drop the --input-dir flag from the cluster create command * [`1b6533675`](https://github.com/siderolabs/talos/commit/1b65336752933acdcbf681767785157714866f88) docs: add note about ca-signed certs for secureboot * [`d3f88f50c`](https://github.com/siderolabs/talos/commit/d3f88f50c5394536ee80d19464359408a37d81ff) docs: document talos vip failover behavior * [`005fc8bd5`](https://github.com/siderolabs/talos/commit/005fc8bd50fbc4b15b26032b43d1d32c1da22f11) docs: add docs on syncing configs after a kube upgrade * [`4d876d9af`](https://github.com/siderolabs/talos/commit/4d876d9af9fcc9828f09d05db124fbdce9c17785) feat: update Go to 1.25.1 * [`2b556cd22`](https://github.com/siderolabs/talos/commit/2b556cd22a3563f1d86a648ea6c69a4d45edad76) feat: implement multi-doc StaticHostConfig * [`a7b776842`](https://github.com/siderolabs/talos/commit/a7b7768420566b6840fc52bb2152e9bf165f8cd3) docs: replace Raspberry Pi 5 links with Talos builder * [`a349b20ed`](https://github.com/siderolabs/talos/commit/a349b20ed4b3c05dcd0175541b795331f0f7c64d) docs: clarify that talos does not support intermediate ca * [`895133de9`](https://github.com/siderolabs/talos/commit/895133de99158ce3f50b557b77c81d4f0f9d6b40) feat: support configuring PCR states to bind disk encryption * [`c1360103b`](https://github.com/siderolabs/talos/commit/c1360103b5e037cf713b7d787436f01e7182821c) docs: fix command for uploading image on Hetzner * [`43b5b9d89`](https://github.com/siderolabs/talos/commit/43b5b9d8992ad6df37619b3719b57948e4bd9671) fix: correctly handle status-code 204 * [`feeb0d312`](https://github.com/siderolabs/talos/commit/feeb0d312ecacb451e5313390939c7c9349d2ba6) feat: update runc to 1.3.1 * [`421634a14`](https://github.com/siderolabs/talos/commit/421634a1417f529551a75d0bb9be08b73f1120b1) docs: add docs on multihoming * [`41af2d230`](https://github.com/siderolabs/talos/commit/41af2d230c2dd5dce5bc931f76a2eb69405dc554) refactor: clean up internal cluster creation code * [`3000d9e43`](https://github.com/siderolabs/talos/commit/3000d9e431deaf952d08da724da40789cd743f2c) fix: don't bootstrap talos cluster if there's no config present * [`79cb871d0`](https://github.com/siderolabs/talos/commit/79cb871d088e5b1c3a3488610ded14e7a28cec29) feat: use the id of the volume in the mapped luks2 name * [`6c322710d`](https://github.com/siderolabs/talos/commit/6c322710d64786f19e2e0e39d65596c8dce71952) chore: refactor mount package * [`ced7186e2`](https://github.com/siderolabs/talos/commit/ced7186e2a5f0634d9441b12a5340f5ca4c451ff) refactor: update COSI to 1.11.0 * [`de2e24fcd`](https://github.com/siderolabs/talos/commit/de2e24fcda590a1ef3f80a5372bb70865a2f47c3) docs: clarify that install-cni image is deprecated * [`bef8ef509`](https://github.com/siderolabs/talos/commit/bef8ef509380aba259efcc2f5d1f6632e034160b) docs: add docs on cilium's compatibility with kubespan * [`e5acb10fc`](https://github.com/siderolabs/talos/commit/e5acb10fcceba69060507a35caea21281bdc71cc) feat: update pkgs * [`c4c1daf0e`](https://github.com/siderolabs/talos/commit/c4c1daf0e2e6675626b974b0c008e101d919c8b5) docs: add info about br_netfilter * [`5c52ecac3`](https://github.com/siderolabs/talos/commit/5c52ecac364f917e5f45859f680494a08f85cb90) docs: clarify interactive dashboard resolution control * [`15ecb02a4`](https://github.com/siderolabs/talos/commit/15ecb02a4545639ffb8ba5c6e5a413e53129b619) feat: update Linux kernel (memcg_v1, ublk) * [`53f18c2f6`](https://github.com/siderolabs/talos/commit/53f18c2f60c84c4b0f944cc343ae1f538e8d1236) fix: enable support for VMWare arm64 * [`3bbe1c0da`](https://github.com/siderolabs/talos/commit/3bbe1c0da5485b6cd3e7fadd8f020e0d0aca406a) docs: add docs on grow flag * [`b9fb09dcd`](https://github.com/siderolabs/talos/commit/b9fb09dcdbcca60f695ac317c45e18fa092541a8) release(v1.12.0-alpha.0): prepare release * [`6a389cad3`](https://github.com/siderolabs/talos/commit/6a389cad35f80b27fe9c43db9e701ee9f6f6142a) chore: update dependencies * [`9d98c2e89`](https://github.com/siderolabs/talos/commit/9d98c2e891258dcf2ef90519d38d0aefb77cd0db) feat: add a cgroup preset for PSI and --skip-cri-resolve * [`072f77b16`](https://github.com/siderolabs/talos/commit/072f77b1623cdc838093465b7266b26e20a248ea) chore: prepare for future Talos 1.12-alpha.0 release * [`96f41ce88`](https://github.com/siderolabs/talos/commit/96f41ce8840783f783fcc8e0fd6b43302b9bfe43) docs: update qemu and docker docs * [`a751cd6b7`](https://github.com/siderolabs/talos/commit/a751cd6b7474a4dc20137e917dbb2229fe9cc8bd) docs: activate Talos v1.11 docs by default * [`e8f1ec1c5`](https://github.com/siderolabs/talos/commit/e8f1ec1c5bbd8a6cfb68886e6283e7caaf5fb063) docs: fix broken create qemu command v1.11 docs * [`639f0dfdd`](https://github.com/siderolabs/talos/commit/639f0dfdd88c5596439601f3f9600b3aafb24227) feat: update Linux to 6.16.4 * [`8aa7b3933`](https://github.com/siderolabs/talos/commit/8aa7b3933d07ea45a96844b9c91347a08950e243) fix: bring back linux/armv7 build and update xz * [`9cae7ba6b`](https://github.com/siderolabs/talos/commit/9cae7ba6b97a67a5d282c6f667ccb4c3e2111447) feat: update CoreDNS to 1.12.3 * [`cfef3ad45`](https://github.com/siderolabs/talos/commit/cfef3ad4544498a47de17f6b05fb8374c35e3dd8) fix: drop linux/armv7 build * [`42ea2ac50`](https://github.com/siderolabs/talos/commit/42ea2ac5058457dafe666f8d79f08d3c8ee60cfb) fix: update xz module (security) * [`4fcfd35b9`](https://github.com/siderolabs/talos/commit/4fcfd35b9510f45d0ef7ae3657eb0916d549d2dd) docs: fix module name example * [`50824599a`](https://github.com/siderolabs/talos/commit/50824599a4fa7b72d563a35a4746ca063becf672) chore: update some tools * [`bcd297490`](https://github.com/siderolabs/talos/commit/bcd297490c608f593b6dd274945aa2b73c3fd3ee) feat: allow Ed25119 in FIPS mode * [`5992138bb`](https://github.com/siderolabs/talos/commit/5992138bb981e84dae917f0f0fdafee4049bc5ec) test: ignore one leaking goroutine * [`d155326c1`](https://github.com/siderolabs/talos/commit/d155326c1206979f30a5355f7bdb23cb051e9b78) docs: add sbc unofficial ports docs * [`285fa7d22`](https://github.com/siderolabs/talos/commit/285fa7d222be1f5e63c0bb725b206966e2722a3b) docs: add the deploy application docs * [`527791f09`](https://github.com/siderolabs/talos/commit/527791f0974afe9c8558b82fa19f4354487693ed) feat: update Kubernetes to 1.34.0 * [`a1c0e237d`](https://github.com/siderolabs/talos/commit/a1c0e237d6e047bb59c4fbd48e2c2b9e36dd4808) feat: update Linux to 6.15.11, Go to 1.25 * [`4d7fc25f8`](https://github.com/siderolabs/talos/commit/4d7fc25f8bf20d4489080795a3d0ce0dfb1bc6b8) docs: switch order of wipe disk command * [`7368a994d`](https://github.com/siderolabs/talos/commit/7368a994df07cc4e50e3709ac766d8062db070a0) feat: add SOCKS5 proxy support to dynamic proxy dialer * [`d63591069`](https://github.com/siderolabs/talos/commit/d635910697b221aee3e9afa6d9e5b398236b6a21) chore: silence linter warnings * [`07eb4d7ec`](https://github.com/siderolabs/talos/commit/07eb4d7ec148a7e3c4c6dde080469c1a2fb410fb) fix: set default ram unit to MiB instead of MB * [`6b732adc4`](https://github.com/siderolabs/talos/commit/6b732adc43684facfd329f424a34a7e4df36d77b) feat: update Linux to 6.12.43 * [`b6410914f`](https://github.com/siderolabs/talos/commit/b6410914f74ce01672fdef7e912e37970909281c) feat: add human readable byte size cli flags * [`ec70cef99`](https://github.com/siderolabs/talos/commit/ec70cef99005fd7e383fea63b5c23774882fcf28) feat: update NVIDIA drivers and kernel * [`0879efa69`](https://github.com/siderolabs/talos/commit/0879efa690ad657e4aed251fcbeba8f5645d73ce) feat: update Kubernetes default to v1.34.0-rc.2 * [`f504639df`](https://github.com/siderolabs/talos/commit/f504639df4388619f731196ed8e79a6818b6ed5f) feat: add a user-facing create qemu command * [`558e0b09a`](https://github.com/siderolabs/talos/commit/558e0b09ab65b353e83b98c9ddf6cb2b67fd060e) test: fix the Image Factory PXE boot test * [`d73f0a2e5`](https://github.com/siderolabs/talos/commit/d73f0a2e5b788c7b69c2fb827f7111d5f9c8e706) docs: make readme badges consistent * [`f1369af98`](https://github.com/siderolabs/talos/commit/f1369af98e1f6d48fed137e31237956abbd28b0f) chore: use new filesystem api on STATE partition * [`366cedbe7`](https://github.com/siderolabs/talos/commit/366cedbe7495ce15bcd0e6c6f7f0add65a41a861) docs: link to kubernetes linux swap tuning * [`2f5a16f5e`](https://github.com/siderolabs/talos/commit/2f5a16f5e4ae186a309aef5e3d285897d0fe2df1) fix: make --with-uuid-hostnames functionality available to qemu provider * [`70612c1f9`](https://github.com/siderolabs/talos/commit/70612c1f9fc9056e8a3669ff10a385c4e8e03350) refactor: split the PlatformConfigController * [`511748339`](https://github.com/siderolabs/talos/commit/51174833997fd9a0a599ab1dde947834b682ab14) docs: add system extension tier documentation * [`009fb1540`](https://github.com/siderolabs/talos/commit/009fb1540e0b9f5daac6302f42e8813e596fc87c) test: don't run nvidia tests on integration/aws * [`99674ef20`](https://github.com/siderolabs/talos/commit/99674ef20d34166d60563d4bf46fbbfc57399509) docs: apply fixes for what is new * [`92db677b5`](https://github.com/siderolabs/talos/commit/92db677b5d32de32ec7e785531b32202e03283b4) fix: image cache lockup on a missing volume * [`9c97ed886`](https://github.com/siderolabs/talos/commit/9c97ed886b89b2fb84f47866abdf1000839143c4) fix: version contract parsing in encryption keys handling * [`1fc670a08`](https://github.com/siderolabs/talos/commit/1fc670a08dc7af8eaeabdc7134eb77a5c939df40) fix: dial with proxy * [`18447d0af`](https://github.com/siderolabs/talos/commit/18447d0afdbcc8fa7db6ae008e4bc4d5b0a0b00a) feat: update Linux to 6.12.41 * [`f65f39b78`](https://github.com/siderolabs/talos/commit/f65f39b78b0c7881e5f51c66ad022c17c2cd4960) fix: provide mitigation CVE-1999-0524 * [`8817cc60c`](https://github.com/siderolabs/talos/commit/8817cc60cfaf4b50f11c38d3b25df7df48382033) fix: actually use SIDEROV1_KEYS_DIR env var if it's provided * [`b08b20a10`](https://github.com/siderolabs/talos/commit/b08b20a1005256a9e3fc7cae8bcf8eea87f6ac09) feat: use key provider with fallback option for auth type SideroV1 * [`7a52d7489`](https://github.com/siderolabs/talos/commit/7a52d7489c9709708d55f8f001d70700addc7e1e) fix: kubernetes upgrade options for kubelet * [`ea8289f55`](https://github.com/siderolabs/talos/commit/ea8289f550787593b1cd35f2d8da59aa5311880e) feat: add a user facing docker command * [`54ad64765`](https://github.com/siderolabs/talos/commit/54ad64765090d90013e4917d1bf494592069beec) chore: re-enable vulncheck * [`26bbddea9`](https://github.com/siderolabs/talos/commit/26bbddea95669278363c604316ed85986f312d71) fix: darwin build * [`b5d5ef79e`](https://github.com/siderolabs/talos/commit/b5d5ef79e7a2d76e29a7c872c1c418fffc63b0df) fix: set secs field in DHCPv4 packets * [`c07911933`](https://github.com/siderolabs/talos/commit/c0791193373e36c35f29c70318432331b4c6ab2a) chore: refactor how tools are being installed * [`34f25815c`](https://github.com/siderolabs/talos/commit/34f25815c036d2c91bdfddc9c7d40ca2edf677bd) docs: fork docs for v1.12 * [`b66b995d3`](https://github.com/siderolabs/talos/commit/b66b995d34306192cbaa4ef68fe39f821b37d1f0) feat: update default Kubernetes to v1.34.0-rc.1 * [`b967c587d`](https://github.com/siderolabs/talos/commit/b967c587d9f217f25798e0bee0c90393e55dc085) docs: fix clone URL to include `.git` * [`b72c68398`](https://github.com/siderolabs/talos/commit/b72c6839806103ac0a76acd46f30eabea0375790) docs: edit the insecure, etcd-metrics, inline and extramanifests * [`e5b9c1fff`](https://github.com/siderolabs/talos/commit/e5b9c1ffffec9fd49ffb84a36c918e75eaa8f1ef) docs: remov RAS Syndrome * [`701fe774b`](https://github.com/siderolabs/talos/commit/701fe774bd19de7c9f21e043e1520161a8c5fff7) docs: fix cilium links and bump to 1.18.0 * [`d306713a1`](https://github.com/siderolabs/talos/commit/d306713a13a18d7af6caffd5890d54d91d22cad7) feat: update Go to 1.24.6 * [`721595a00`](https://github.com/siderolabs/talos/commit/721595a0009f78a2722802ab665957fd767c4d1e) chore: add deadcode elimination linter * [`dc4865915`](https://github.com/siderolabs/talos/commit/dc4865915d567942adea3efa66f8ad360f9c4cce) refactor: stop using `text/template` in `machined` code paths * [`545be55ed`](https://github.com/siderolabs/talos/commit/545be55edc863245638d4387cb9ee7e7b068f2ba) feat: add a pause function to dashboard * [`06a6c0fe3`](https://github.com/siderolabs/talos/commit/06a6c0fe332940b7a70ea2652bc2a5e7bc51bbf3) refactor: fix deadcode elimination with godbus * [`2dce8f8d4`](https://github.com/siderolabs/talos/commit/2dce8f8d4693a85d2f3bf46169af8cf502d49f9d) refactor: replace containerd/containerd/v2 module for proper DCE * [`9b11d8608`](https://github.com/siderolabs/talos/commit/9b11d86081df8cf77860d2d27eed5d8001ff721e) chore: rekres to configure slack notify workflow for CI failures * [`5ce6a660f`](https://github.com/siderolabs/talos/commit/5ce6a660f67f4e2776550a1e621179beb8a6788c) docs: augment the pod security docs * [`ada51ff69`](https://github.com/siderolabs/talos/commit/ada51ff696011e15dcd9c661da1d839bdc341745) fix: unmarshal encryption STATE from META * [`b9e9b2e07`](https://github.com/siderolabs/talos/commit/b9e9b2e07a645f53ca23355810d485a2622870c9) docs: add what is new notes for 1.11 * [`53055bdf4`](https://github.com/siderolabs/talos/commit/53055bdf49ce4c81f63c159cdbaa8ea85d9ca2b8) docs: fix typo in kubevirt page * [`8d12db480`](https://github.com/siderolabs/talos/commit/8d12db480c38ec37aee5ae7721b2e5ca55ad733e) fix: one more attempt to fix volume mount race on restart * [`34d37a268`](https://github.com/siderolabs/talos/commit/34d37a268a9e0098179369af128261dbfc956d1d) chore: rekres to use correct slack channel for slack-notify * [`326a00538`](https://github.com/siderolabs/talos/commit/326a00538210bf98b01795d314c1e154a74d2d58) feat: implement `talos.config.early` command line arg * [`a5f3000f2`](https://github.com/siderolabs/talos/commit/a5f3000f2e8a79d4e9a5be95fbcac91a2d78675b) feat: implement encryption locking to STATE * [`c1e65a342`](https://github.com/siderolabs/talos/commit/c1e65a34256944743e768613b119c0caa517b54d) docs: remove talos API flags from mgmt commands * [`181d0bbf5`](https://github.com/siderolabs/talos/commit/181d0bbf5381343d35a01190da45e3442320d7c5) feat: bootedentry resource * [`7ad439ac3`](https://github.com/siderolabs/talos/commit/7ad439ac35859695074d3a3efdcdb5c0cab1a5c6) fix: enforce minimum size on user volumes if not set explicitly * [`50e37aefd`](https://github.com/siderolabs/talos/commit/50e37aefdbde973bcc8aa352639946490fbe7d94) fix: live reload of TLS client config for discovery client * [`87efd75ef`](https://github.com/siderolabs/talos/commit/87efd75efb3e62b88b4f65a221f9fbdd4b4d6ef9) feat: update containerd to 2.1.4 * [`724b9de6d`](https://github.com/siderolabs/talos/commit/724b9de6d5195bcccc5f484c696429b2f09ab16e) feat: add F71808E watchdog driver * [`8af96f7af`](https://github.com/siderolabs/talos/commit/8af96f7afdac1c4d5e2697b897b81e2bddd15f66) docs: add ETCD downgrade documentation * [`44edd205d`](https://github.com/siderolabs/talos/commit/44edd205d5fdffab39b65ee62695a40e22ef188c) docs: add remark about 'exclude-from-external-load-balancers' label * [`727101926`](https://github.com/siderolabs/talos/commit/7271019263b0dc5b28d2764d19fe531e473222fc) fix(ci): use a random suffix for ami names * [`d621ce372`](https://github.com/siderolabs/talos/commit/d621ce3726f20ee568ea3b6ac57d9e8dfa0580cc) fix: grype scan * [`d62e255c2`](https://github.com/siderolabs/talos/commit/d62e255c260810a5f0f2959e32592a3331df28d3) fix: issues with reading GPT * [`5d0883e14`](https://github.com/siderolabs/talos/commit/5d0883e147163c12a77cd926db799ffed854aedf) feat: update PCI DB module to v0.3.2 * [`3751c8ccf`](https://github.com/siderolabs/talos/commit/3751c8ccfa1bab9fcd435290f36e9012a5626e40) test: wait for service account test job longer * [`a592eb9f9`](https://github.com/siderolabs/talos/commit/a592eb9f98788883a7ec6d17772e10707230a0d8) feat: update Linux to 6.12.40 * [`4c40e6d3f`](https://github.com/siderolabs/talos/commit/4c40e6d3fb4c2f451a8d7a671df5f6254161bd5d) feat: update etcd to 3.6.4 * [`2bc37bd2c`](https://github.com/siderolabs/talos/commit/2bc37bd2c9679c8055fd7b52eb310f23a329af4e) docs: fix error in kernel module guide * [`bfc57fb86`](https://github.com/siderolabs/talos/commit/bfc57fb863224f7626f49e5b26be06f77bea2e40) chore: tag aws snapshots created via ci with the image name * [`06ef7108a`](https://github.com/siderolabs/talos/commit/06ef7108a6050b3a8fd7535f01a469f09042bf56) fix: issue with volume remount on service restart * [`03efbff18`](https://github.com/siderolabs/talos/commit/03efbff18e420c4fe960f490f91dd9f4751ece04) docs: add SBOM documentation * [`af8a2869d`](https://github.com/siderolabs/talos/commit/af8a2869dbbec073ffaf72a1378682e109b053ec) fix: do not download artifacts for cron Grype scan * [`5f442159b`](https://github.com/siderolabs/talos/commit/5f442159b224c96c90badc7176fed17bfb561709) feat: unify disk encryption configuration * [`38e176e59`](https://github.com/siderolabs/talos/commit/38e176e594edb3d271d98f78417b9fd5ba0c5288) chore(ci): fix datasource versioning * [`85d6b9198`](https://github.com/siderolabs/talos/commit/85d6b919890a1aa9c4f94d5b18861cc617134ff9) feat: update etcd to v3.5.22 * [`dd7bd2dab`](https://github.com/siderolabs/talos/commit/dd7bd2dab8cf09334e3e353d6a477509bbaa303e) docs: rewrite the getting started and prod docs for v1.10 and v1.11 * [`136a899aa`](https://github.com/siderolabs/talos/commit/136a899aa25b3fdcdd771594668278d563f09192) chore: regenerate release step with signing fixes * [`450b30d5a`](https://github.com/siderolabs/talos/commit/450b30d5a986563869efdbaa074e82d612f6f2ef) chore(ci): add more nvidia test matrix * [`451c2c4c3`](https://github.com/siderolabs/talos/commit/451c2c4c39e70c20df58fc31459cd5c789a0e46f) test: add talosctl:latest to the image cache

### Changes since v1.12.0-alpha.0
79 commits

* [`e455c7ea9`](https://github.com/siderolabs/talos/commit/e455c7ea9c919a2f70ddecceaa8f3b4e25566048) chore: use testing/synctest in tests * [`7f048e962`](https://github.com/siderolabs/talos/commit/7f048e962e217687ab67ed7027c5228e8ccb7d16) feat: update dependencies * [`fe36b3d32`](https://github.com/siderolabs/talos/commit/fe36b3d3200db57f3e21017ff7a4808b330a1d55) fix: stop returning EINVAL on remount of detached mounts * [`c6279e04c`](https://github.com/siderolabs/talos/commit/c6279e04c45504af243c0aef9f255317426b4ca0) chore: use new mount/v3 package in efivarfs * [`d5197effb`](https://github.com/siderolabs/talos/commit/d5197effb0b48290d613140b68796cb8f30b9a70) feat: update etcd 3.6.5, CoreDNS 1.12.4 * [`33714b715`](https://github.com/siderolabs/talos/commit/33714b7158a0d569be1d0b1d7b012280856db484) feat: release cloud image using factory * [`d10a2747e`](https://github.com/siderolabs/talos/commit/d10a2747e0e835876aff158e6b6f7882cef9fa44) docs: deprecate JSON6902 patches and interactive installer * [`1e604cbf5`](https://github.com/siderolabs/talos/commit/1e604cbf514bece1e112d8afd5d1cd6ccb1045c3) fix: don't set broadcast for /31 and /32 addresses * [`65a66097a`](https://github.com/siderolabs/talos/commit/65a66097a05e5c0e2334d5eff494a0e71534716f) refactor: split cluster create logic into smaller parts * [`ab847310e`](https://github.com/siderolabs/talos/commit/ab847310efde540b5bfe17570b99af1bb705832b) fix: provide refreshing CA pool (resolvers) * [`d63c3ed7d`](https://github.com/siderolabs/talos/commit/d63c3ed7db2b22f7e394fc45d101d03cba463177) docs: update secureboot docs * [`493f7ed9d`](https://github.com/siderolabs/talos/commit/493f7ed9d2710eb240eab6b6ab532f41abc818c1) feat: support embedded config * [`251df70f6`](https://github.com/siderolabs/talos/commit/251df70f6d33f1d5a3b1b9e4c0c249d8bc85c4b3) feat: add a userspace OOM controller * [`7bae5b40b`](https://github.com/siderolabs/talos/commit/7bae5b40b4f22f0f07a586ebd9cda9436086a5f8) feat: implement link configuration * [`724857dec`](https://github.com/siderolabs/talos/commit/724857decb95ddeebb2ac5d33c38a71bf7512805) fix(ci): skip netbird extension for tests * [`e06a08698`](https://github.com/siderolabs/talos/commit/e06a086989331f28406e8d4234e02d9a6b83f87d) fix: default gateway as string * [`7ed07412e`](https://github.com/siderolabs/talos/commit/7ed07412e963e6ee91615adbea095944aa6a56e5) fix: uefi boot entry handling logic * [`ea4ed165a`](https://github.com/siderolabs/talos/commit/ea4ed165ad860a5beea17ca2d404bdaa6e5ad933) refactor: efivarfs mock and tests * [`1fca111e2`](https://github.com/siderolabs/talos/commit/1fca111e24bcae81b78f007e67b71c9155c0169f) feat: support setting wake-on-lan for Ethernet * [`94f78dbe7`](https://github.com/siderolabs/talos/commit/94f78dbe798cb227a0c38b70a1d6840803989290) docs: add a documentation for running Talos in KVM * [`46902f8fd`](https://github.com/siderolabs/talos/commit/46902f8fdee257a09be4bc1753c6b3f845ef8089) docs: add TrueFullstaq to adopters * [`a28e5cbd5`](https://github.com/siderolabs/talos/commit/a28e5cbd50d11aa6c253a6a9ce1999b9d45effad) chore: update pkgs and tools * [`7cf403db8`](https://github.com/siderolabs/talos/commit/7cf403db8ca0e1719195001895cfbc12835b0fdd) docs: step-by-step scaleway documentation to get an image * [`687285fa2`](https://github.com/siderolabs/talos/commit/687285fa26ec42dadbfb72580099f6e20bbaf85e) docs: remove 'curl' in wget command * [`9db6dc06c`](https://github.com/siderolabs/talos/commit/9db6dc06c3010cd89ce4cb0ec0bde178db0447a4) feat: stop mounting state partition * [`53ce93aae`](https://github.com/siderolabs/talos/commit/53ce93aaed3bd5bfcbe926fa69ca3b4b8b45c74f) test: try to clear connection refused more aggressively * [`51db5279c`](https://github.com/siderolabs/talos/commit/51db5279c423e4b8637a05e52b26dfc5aa719cbc) fix: bump trustd memory limit * [`25204dc8a`](https://github.com/siderolabs/talos/commit/25204dc8a8df79bc876a0bec2492e1147a81d954) fix(machined): change `constants.MinimumGOAMD64Level` using build tag * [`9cd2d794d`](https://github.com/siderolabs/talos/commit/9cd2d794d060b637dbac5263ae417a4e83d54efe) feat: ship nft binary with Talos rootfs * [`b1416c9fe`](https://github.com/siderolabs/talos/commit/b1416c9fe1d5ea9cd68f9b6b766a288a267cee61) feat: record last log the failed service * [`0b129f9ef`](https://github.com/siderolabs/talos/commit/0b129f9efdf57dd9692f7cece6b97719a7ccf80e) feat: enforce more KSPP and hardening sysctls * [`11872643c`](https://github.com/siderolabs/talos/commit/11872643c310212c52b4fd7e13b6cc7d6ec7e4fc) chore: drop docs folder * [`d30fdcd88`](https://github.com/siderolabs/talos/commit/d30fdcd88f421824cf17b9ecec25be7c8044e857) chore: pass in github token to imager * [`b88f27d80`](https://github.com/siderolabs/talos/commit/b88f27d804d60a706f598b50676dad5dd2a9726a) chore: make reset test code a bit better * [`1cde53d01`](https://github.com/siderolabs/talos/commit/1cde53d0173fd1ae637855e15fe34bb74bb027a0) test: fix several issues with tests * [`16cd127a0`](https://github.com/siderolabs/talos/commit/16cd127a04bb5fc907b7ca04f1c81d4c7150eab2) docs: add docs on updating image cache * [`c3ae92b14`](https://github.com/siderolabs/talos/commit/c3ae92b1424d4a2c9bc18cfa394b10eda6c9a20f) fix: build kernel checks only on linux * [`2120904ec`](https://github.com/siderolabs/talos/commit/2120904ec534a91f66dcea419b5a29e36a16f6e4) feat: create detached tmpfs * [`6bbee6de5`](https://github.com/siderolabs/talos/commit/6bbee6de5b18b25deb4e6f515251187e259aa424) docs: remove 'ceph-data' from volume examples/docs * [`07acb3bd2`](https://github.com/siderolabs/talos/commit/07acb3bd2d4f92e80706d1835130bbe6e944d096) fix: use correct order to determine SideroV1 keys directory path * [`2d57fa002`](https://github.com/siderolabs/talos/commit/2d57fa00281f8090b85097c66df634101b0cde79) fix: trim zero bytes in the DHCP host & domain response * [`451cb5f78`](https://github.com/siderolabs/talos/commit/451cb5f78fac3b2ddfec7d545629fe8c88ea2367) docs: clarify disk partition confusion * [`a2122ee5c`](https://github.com/siderolabs/talos/commit/a2122ee5cb9c84f33e0c4b30e9223bb239621d55) feat: implement HostConfig multi-doc * [`69ab076b4`](https://github.com/siderolabs/talos/commit/69ab076b4d6e52484677ee7f68a853dc4edfe2bc) fix: re-create cgroups when restarting runners * [`297b5cc28`](https://github.com/siderolabs/talos/commit/297b5cc2856710b74b4e0e46b00ae33aea4c1bf7) docs: add docs on node labels * [`e168512dd`](https://github.com/siderolabs/talos/commit/e168512dd020da9eac654dae2ba891cf33415c44) fix: apply 'ro' flag to iso9660 filesystems * [`7f7acfbb9`](https://github.com/siderolabs/talos/commit/7f7acfbb9f10c243d0b132c1ef079cb77d2727e0) docs: fix typo in doc * [`d57882b18`](https://github.com/siderolabs/talos/commit/d57882b1830504fe4bfd5344edae613168db7f0e) feat: update Kubernetes to 1.34.1 * [`f85f82f32`](https://github.com/siderolabs/talos/commit/f85f82f32f098f97588f404550f72d64786fe329) test: fix flakiness in RawVolumes test * [`82569e319`](https://github.com/siderolabs/talos/commit/82569e319eb57b1199db6bfd3e612fb771c8c7cd) feat: update Linux 6.16.6 * [`2fd2ab4e4`](https://github.com/siderolabs/talos/commit/2fd2ab4e43e06910154705d6ef1d0576a7c04a2b) fix: remove CoreDNS cpu limit * [`ce9bc32a0`](https://github.com/siderolabs/talos/commit/ce9bc32a08695873d9054afe2608a76cf7c6088a) chore(ci): rekres to use new runner groups * [`8b64f68f6`](https://github.com/siderolabs/talos/commit/8b64f68f6946c2979f6fe2bf617f31639a927bf8) test: improve test stability * [`272cb860d`](https://github.com/siderolabs/talos/commit/272cb860d4cfb8464b29ff31567e25fe6c275849) chore: drop the --input-dir flag from the cluster create command * [`1b6533675`](https://github.com/siderolabs/talos/commit/1b65336752933acdcbf681767785157714866f88) docs: add note about ca-signed certs for secureboot * [`d3f88f50c`](https://github.com/siderolabs/talos/commit/d3f88f50c5394536ee80d19464359408a37d81ff) docs: document talos vip failover behavior * [`005fc8bd5`](https://github.com/siderolabs/talos/commit/005fc8bd50fbc4b15b26032b43d1d32c1da22f11) docs: add docs on syncing configs after a kube upgrade * [`4d876d9af`](https://github.com/siderolabs/talos/commit/4d876d9af9fcc9828f09d05db124fbdce9c17785) feat: update Go to 1.25.1 * [`2b556cd22`](https://github.com/siderolabs/talos/commit/2b556cd22a3563f1d86a648ea6c69a4d45edad76) feat: implement multi-doc StaticHostConfig * [`a7b776842`](https://github.com/siderolabs/talos/commit/a7b7768420566b6840fc52bb2152e9bf165f8cd3) docs: replace Raspberry Pi 5 links with Talos builder * [`a349b20ed`](https://github.com/siderolabs/talos/commit/a349b20ed4b3c05dcd0175541b795331f0f7c64d) docs: clarify that talos does not support intermediate ca * [`895133de9`](https://github.com/siderolabs/talos/commit/895133de99158ce3f50b557b77c81d4f0f9d6b40) feat: support configuring PCR states to bind disk encryption * [`c1360103b`](https://github.com/siderolabs/talos/commit/c1360103b5e037cf713b7d787436f01e7182821c) docs: fix command for uploading image on Hetzner * [`43b5b9d89`](https://github.com/siderolabs/talos/commit/43b5b9d8992ad6df37619b3719b57948e4bd9671) fix: correctly handle status-code 204 * [`feeb0d312`](https://github.com/siderolabs/talos/commit/feeb0d312ecacb451e5313390939c7c9349d2ba6) feat: update runc to 1.3.1 * [`421634a14`](https://github.com/siderolabs/talos/commit/421634a1417f529551a75d0bb9be08b73f1120b1) docs: add docs on multihoming * [`41af2d230`](https://github.com/siderolabs/talos/commit/41af2d230c2dd5dce5bc931f76a2eb69405dc554) refactor: clean up internal cluster creation code * [`3000d9e43`](https://github.com/siderolabs/talos/commit/3000d9e431deaf952d08da724da40789cd743f2c) fix: don't bootstrap talos cluster if there's no config present * [`79cb871d0`](https://github.com/siderolabs/talos/commit/79cb871d088e5b1c3a3488610ded14e7a28cec29) feat: use the id of the volume in the mapped luks2 name * [`6c322710d`](https://github.com/siderolabs/talos/commit/6c322710d64786f19e2e0e39d65596c8dce71952) chore: refactor mount package * [`ced7186e2`](https://github.com/siderolabs/talos/commit/ced7186e2a5f0634d9441b12a5340f5ca4c451ff) refactor: update COSI to 1.11.0 * [`de2e24fcd`](https://github.com/siderolabs/talos/commit/de2e24fcda590a1ef3f80a5372bb70865a2f47c3) docs: clarify that install-cni image is deprecated * [`bef8ef509`](https://github.com/siderolabs/talos/commit/bef8ef509380aba259efcc2f5d1f6632e034160b) docs: add docs on cilium's compatibility with kubespan * [`e5acb10fc`](https://github.com/siderolabs/talos/commit/e5acb10fcceba69060507a35caea21281bdc71cc) feat: update pkgs * [`c4c1daf0e`](https://github.com/siderolabs/talos/commit/c4c1daf0e2e6675626b974b0c008e101d919c8b5) docs: add info about br_netfilter * [`5c52ecac3`](https://github.com/siderolabs/talos/commit/5c52ecac364f917e5f45859f680494a08f85cb90) docs: clarify interactive dashboard resolution control * [`15ecb02a4`](https://github.com/siderolabs/talos/commit/15ecb02a4545639ffb8ba5c6e5a413e53129b619) feat: update Linux kernel (memcg_v1, ublk) * [`53f18c2f6`](https://github.com/siderolabs/talos/commit/53f18c2f60c84c4b0f944cc343ae1f538e8d1236) fix: enable support for VMWare arm64 * [`3bbe1c0da`](https://github.com/siderolabs/talos/commit/3bbe1c0da5485b6cd3e7fadd8f020e0d0aca406a) docs: add docs on grow flag

### Changes from siderolabs/crypto
2 commits

* [`4154a77`](https://github.com/siderolabs/crypto/commit/4154a771b09f0023e0d258bba6aecc29febabecb) feat: implement dynamic certificate reloader * [`dae07fa`](https://github.com/siderolabs/crypto/commit/dae07fa14f963b34ea67abf0cbc50ba24f280524) chore: update to Go 1.25

### Changes from siderolabs/go-api-signature
1 commit

* [`68478e2`](https://github.com/siderolabs/go-api-signature/commit/68478e2f57a3bca4345c6e189c0a4216dfb9b1ed) fix: return `invalid signature` error when a signature is required

### Changes from siderolabs/go-debug
1 commit

* [`e21721b`](https://github.com/siderolabs/go-debug/commit/e21721bc4faba9072b5e4e33af60a1f0292547af) chore: add support for Go 1.25

### Changes from siderolabs/go-loadbalancer
1 commit

* [`5e7a8b2`](https://github.com/siderolabs/go-loadbalancer/commit/5e7a8b21cbdb156c6fe6f9fd98b8a1bb1186c21c) feat: add jitter and initial health check wait support to upstreams

### Changes from siderolabs/pkgs
32 commits

* [`202a8e6`](https://github.com/siderolabs/pkgs/commit/202a8e663efaf24b662f349c82c5b6addeb6b3a2) feat: update Linux to 6.16.9 * [`3a0900f`](https://github.com/siderolabs/pkgs/commit/3a0900f8b464f22773a153c65d85c6dd075cadba) feat: enable SRv6 LWTUNNEL and BPF support * [`628efc8`](https://github.com/siderolabs/pkgs/commit/628efc86c2020464c4ee58f419b8b17c8f87aa45) chore: update linuxfirmware and rekres * [`9d1fb02`](https://github.com/siderolabs/pkgs/commit/9d1fb029db9837af5529f7de371cc9bc709e8cbe) feat: support adding extra trusted certificates in the kernel * [`7fe686d`](https://github.com/siderolabs/pkgs/commit/7fe686dec6959a32a6a6d6610cedd18d963412ad) fix: build nftables with embedded gmp * [`fede0a7`](https://github.com/siderolabs/pkgs/commit/fede0a7ec672e835372b8915896fd0e775da0297) feat: add nft binary * [`0dae01a`](https://github.com/siderolabs/pkgs/commit/0dae01a735bae635ab987febac42c382d851d7d0) feat: update NVIDIA to 580.82.07 * [`9ac2392`](https://github.com/siderolabs/pkgs/commit/9ac23925cf350075a4931a54176fdd3d9b9b7cb7) feat: enable Kernel config options for IPVS Maglev hashing scheduler support * [`3c5315c`](https://github.com/siderolabs/pkgs/commit/3c5315cd2ae931eb76233d26283f861ace932b1d) feat: update dependencies * [`122fa66`](https://github.com/siderolabs/pkgs/commit/122fa6626ff8c300623caa625bdc6bc72e866494) feat: update Linux to 6.16.6 * [`ab1e866`](https://github.com/siderolabs/pkgs/commit/ab1e86612ac903b811ecd3b76d55e85951190565) feat: update Go to 1.25.1 * [`7d6ef1b`](https://github.com/siderolabs/pkgs/commit/7d6ef1b187956a8cb2c837f339169b6cb7eb9c12) feat: update runc to 1.3.1 * [`e067c20`](https://github.com/siderolabs/pkgs/commit/e067c2015302c5069676727f047b6aef9ce0dc0c) feat: enable USB audio support * [`c4faa38`](https://github.com/siderolabs/pkgs/commit/c4faa389341f6be5dad5fdd575acf61e9eb170f8) feat: bump dependencies * [`453cdfc`](https://github.com/siderolabs/pkgs/commit/453cdfc2fdea9945c8e3531ef190971211eaaf13) feat: enable ublk support * [`9824684`](https://github.com/siderolabs/pkgs/commit/982468471d8118ac4653b704512e5f847a148dd7) fix: enable memcg v1 * [`2447e11`](https://github.com/siderolabs/pkgs/commit/2447e11dcbcb5dc10703515e2185f753b04e20e0) feat: update Linux to 6.16, GCC to 15 * [`2cfb920`](https://github.com/siderolabs/pkgs/commit/2cfb920acd88d63c3d3ced3a5760549aa180208c) feat: update Linux to 6.15.11, update tools, rekres * [`ab4e975`](https://github.com/siderolabs/pkgs/commit/ab4e9755b0e2dbf38c75db5d2ff7720f511fd50c) feat: update Linux to 6.12.43 * [`cd67e36`](https://github.com/siderolabs/pkgs/commit/cd67e3660fa0a2ad25ca4b8dcd3c1ce9b96b0b72) chore: update kernel config to support max SMP CPUs * [`e3b2094`](https://github.com/siderolabs/pkgs/commit/e3b209474060f5a67e36c9239a3a066ee8ace2fe) fix: fix build for new NVIDIA drivers * [`fd5fdfd`](https://github.com/siderolabs/pkgs/commit/fd5fdfde0bdc4dfc1e9990300df46b9af23c0dfd) feat: update Nvidia LTS to 580.65.06 and production to 570.172.08 * [`0edf426`](https://github.com/siderolabs/pkgs/commit/0edf426d758d67f7baaaa42facdc658396f02f9f) fix: backport CVE kernel patches to 6.12 * [`26d8fef`](https://github.com/siderolabs/pkgs/commit/26d8fefe10329e8d1c285014af0bffe1b9a65431) feat: enable Infiniband IRDMA support * [`16b5fac`](https://github.com/siderolabs/pkgs/commit/16b5facdbb37f2ad0329bf131ded62cc9b1239a9) fix: re-enable CPUSETS_V1 cgroups controller * [`fd53886`](https://github.com/siderolabs/pkgs/commit/fd53886f4f36e73181b7b1a0718801bf8e2aadb9) feat: update backportable dependencies * [`d5f7467`](https://github.com/siderolabs/pkgs/commit/d5f746715727ec34fca7a87ab9f1fac2051f0f75) feat: update Go to 1.24.6 * [`0bd019f`](https://github.com/siderolabs/pkgs/commit/0bd019f29031b7461fbe49552b88d0e26861f955) feat: update containerd to 2.1.4 * [`0ba8b5b`](https://github.com/siderolabs/pkgs/commit/0ba8b5b49f3dedcc49f4040a6f5c57329f5c5605) feat: enable F71808E watchdog driver * [`895a86b`](https://github.com/siderolabs/pkgs/commit/895a86bcdfedfd9ca1a698d8f8aa71e3600a22c2) fix: enable ISCSI IBFT * [`a76a67c`](https://github.com/siderolabs/pkgs/commit/a76a67c860a5100f41223fea936712760b33a4cd) feat: update Linux to 6.12.40 * [`8b0a561`](https://github.com/siderolabs/pkgs/commit/8b0a56180198d360ea71b2c62669545b867f9a67) feat: enable bootloader control on amd64

### Changes from siderolabs/tools
8 commits

* [`4f90801`](https://github.com/siderolabs/tools/commit/4f908016475257651dc41a4250c2c843b5373d08) chore: update openssl, curl, libexpat and rekres * [`c37ac80`](https://github.com/siderolabs/tools/commit/c37ac805a17daa8c0dc26da18b768864b821920b) feat: update Go to 1.25.1 * [`7c659e9`](https://github.com/siderolabs/tools/commit/7c659e92db3884737abda95e643995107aa14010) feat: update to GCC 15 * [`83fd7b7`](https://github.com/siderolabs/tools/commit/83fd7b7be62f2f59abeb24c971699895759ebb88) feat: migrate from pkg-config to pkgconf * [`edafd5f`](https://github.com/siderolabs/tools/commit/edafd5f395b1fd31650270332a871c830a5fd781) feat: update toolchain for new Go and Linux headers * [`65789c7`](https://github.com/siderolabs/tools/commit/65789c75ebd4020a444789cfd74d35fefc2497c2) chore: drop unused vars from Pkgfile * [`52db66e`](https://github.com/siderolabs/tools/commit/52db66e8d9e1d83e4872a504d45fdabed31504f7) chore: drop protobuf-related stuff from tools * [`e3c3ef2`](https://github.com/siderolabs/tools/commit/e3c3ef2b604fb80143a17879eeec9f30ca7b07dd) feat: update Go to 1.24.6

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.7.0 -> v0.9.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.18.1 -> v1.19.1 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.10.1 -> v1.12.0 * **github.com/aws/aws-sdk-go-v2/config** v1.29.17 -> v1.31.12 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.32 -> v1.18.9 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.41.2 -> v1.45.6 * **github.com/aws/smithy-go** v1.22.4 -> v1.23.0 * **github.com/containernetworking/plugins** v1.7.1 -> v1.8.0 * **github.com/cosi-project/runtime** v1.10.7 -> v1.11.0 * **github.com/docker/cli** v28.3.3 -> v28.4.0 * **github.com/docker/docker** v28.3.3 -> v28.4.0 * **github.com/docker/go-connections** v0.5.0 -> v0.6.0 * **github.com/equinix-ms/go-vmw-guestrpc** v0.1.1 -> v1.0.0 * **github.com/foxboron/go-uefi** a3183a1bfc84 -> bf180abb62ac * **github.com/gdamore/tcell/v2** v2.8.1 -> v2.9.0 * **github.com/google/cel-go** v0.26.0 -> v0.26.1 * **github.com/google/go-tpm** v0.9.5 -> v0.9.6 * **github.com/gopacket/gopacket** v1.3.1 -> v1.4.0 * **github.com/hetznercloud/hcloud-go/v2** v2.22.0 -> v2.25.1 * **github.com/mdlayher/netlink** fbb4dce95f42 -> v1.8.0 * **github.com/miekg/dns** v1.1.67 -> v1.1.68 * **github.com/rivo/tview** a4a78f1e05cb -> v0.42.0 * **github.com/safchain/ethtool** v0.6.1 -> v0.6.2 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.34 -> v1.0.0-beta.35 * **github.com/siderolabs/crypto** v0.6.3 -> v0.6.4 * **github.com/siderolabs/go-api-signature** v0.3.7 -> v0.3.8 * **github.com/siderolabs/go-debug** v0.5.0 -> v0.6.0 * **github.com/siderolabs/go-loadbalancer** v0.4.0 -> v0.5.0 * **github.com/siderolabs/pkgs** v1.11.0-15-g2ac857a -> v1.12.0-alpha.0-30-g202a8e6 * **github.com/siderolabs/talos/pkg/machinery** v1.11.0 -> v1.12.0-alpha.0 * **github.com/siderolabs/tools** v1.11.0-2-g8556c73 -> v1.12.0-alpha.0-7-g4f90801 * **github.com/spf13/cobra** v1.9.1 -> v1.10.1 * **github.com/spf13/pflag** v1.0.7 -> v1.0.10 * **github.com/stretchr/testify** v1.10.0 -> v1.11.1 * **github.com/u-root/u-root** v0.14.0 -> v0.15.0 * **go.etcd.io/etcd/api/v3** v3.6.4 -> v3.6.5 * **go.etcd.io/etcd/client/pkg/v3** v3.6.4 -> v3.6.5 * **go.etcd.io/etcd/client/v3** v3.6.4 -> v3.6.5 * **golang.org/x/net** v0.42.0 -> v0.44.0 * **golang.org/x/oauth2** v0.30.0 -> v0.31.0 * **golang.org/x/sync** v0.16.0 -> v0.17.0 * **golang.org/x/sys** v0.34.0 -> v0.36.0 * **golang.org/x/term** v0.33.0 -> v0.35.0 * **golang.org/x/text** v0.27.0 -> v0.29.0 * **golang.org/x/time** v0.12.0 -> v0.13.0 * **google.golang.org/grpc** v1.73.0 -> v1.75.1 * **google.golang.org/protobuf** v1.36.6 -> v1.36.9 * **k8s.io/api** v0.34.0 -> v0.34.1 * **k8s.io/apiextensions-apiserver** v0.34.0 -> v0.34.1 * **k8s.io/apiserver** v0.34.0 -> v0.34.1 * **k8s.io/client-go** v0.34.0 -> v0.34.1 * **k8s.io/component-base** v0.34.0 -> v0.34.1 * **k8s.io/kube-scheduler** v0.34.0 -> v0.34.1 * **k8s.io/kubectl** v0.34.0 -> v0.34.1 * **k8s.io/kubelet** v0.34.0 -> v0.34.1 * **k8s.io/pod-security-admission** v0.34.0 -> v0.34.1 * **k8s.io/utils** 4c0f3b243397 -> 0af2bda4dd1d Previous release can be found at [v1.11.0](https://github.com/siderolabs/talos/releases/tag/v1.11.0) ## [Talos 1.12.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.12.0-alpha.0) (2025-09-02) Welcome to the v1.12.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates Linux: 6.16.4 Talos is built with Go 1.25.0. ### Contributors * Andrey Smirnov * Dmitrii Sharshakov * Noel Georgi * Orzelius * Oguz Kilcan * Amarachi Iheanacho * Mateusz Urbanek * 459below * Alp Celik * Andrew Longwill * Dmitry * George Gaál * Guillaume LEGRAIN * Justin Garrison * Misha Aksenov * MrMrRubic * Olivier Doucet * Tom * Utku Ozdemir * kassad ### Changes
98 commits

* [`6a389cad3`](https://github.com/siderolabs/talos/commit/6a389cad35f80b27fe9c43db9e701ee9f6f6142a) chore: update dependencies * [`9d98c2e89`](https://github.com/siderolabs/talos/commit/9d98c2e891258dcf2ef90519d38d0aefb77cd0db) feat: add a cgroup preset for PSI and --skip-cri-resolve * [`072f77b16`](https://github.com/siderolabs/talos/commit/072f77b1623cdc838093465b7266b26e20a248ea) chore: prepare for future Talos 1.12-alpha.0 release * [`96f41ce88`](https://github.com/siderolabs/talos/commit/96f41ce8840783f783fcc8e0fd6b43302b9bfe43) docs: update qemu and docker docs * [`a751cd6b7`](https://github.com/siderolabs/talos/commit/a751cd6b7474a4dc20137e917dbb2229fe9cc8bd) docs: activate Talos v1.11 docs by default * [`e8f1ec1c5`](https://github.com/siderolabs/talos/commit/e8f1ec1c5bbd8a6cfb68886e6283e7caaf5fb063) docs: fix broken create qemu command v1.11 docs * [`639f0dfdd`](https://github.com/siderolabs/talos/commit/639f0dfdd88c5596439601f3f9600b3aafb24227) feat: update Linux to 6.16.4 * [`8aa7b3933`](https://github.com/siderolabs/talos/commit/8aa7b3933d07ea45a96844b9c91347a08950e243) fix: bring back linux/armv7 build and update xz * [`9cae7ba6b`](https://github.com/siderolabs/talos/commit/9cae7ba6b97a67a5d282c6f667ccb4c3e2111447) feat: update CoreDNS to 1.12.3 * [`cfef3ad45`](https://github.com/siderolabs/talos/commit/cfef3ad4544498a47de17f6b05fb8374c35e3dd8) fix: drop linux/armv7 build * [`42ea2ac50`](https://github.com/siderolabs/talos/commit/42ea2ac5058457dafe666f8d79f08d3c8ee60cfb) fix: update xz module (security) * [`4fcfd35b9`](https://github.com/siderolabs/talos/commit/4fcfd35b9510f45d0ef7ae3657eb0916d549d2dd) docs: fix module name example * [`50824599a`](https://github.com/siderolabs/talos/commit/50824599a4fa7b72d563a35a4746ca063becf672) chore: update some tools * [`bcd297490`](https://github.com/siderolabs/talos/commit/bcd297490c608f593b6dd274945aa2b73c3fd3ee) feat: allow Ed25119 in FIPS mode * [`5992138bb`](https://github.com/siderolabs/talos/commit/5992138bb981e84dae917f0f0fdafee4049bc5ec) test: ignore one leaking goroutine * [`d155326c1`](https://github.com/siderolabs/talos/commit/d155326c1206979f30a5355f7bdb23cb051e9b78) docs: add sbc unofficial ports docs * [`285fa7d22`](https://github.com/siderolabs/talos/commit/285fa7d222be1f5e63c0bb725b206966e2722a3b) docs: add the deploy application docs * [`527791f09`](https://github.com/siderolabs/talos/commit/527791f0974afe9c8558b82fa19f4354487693ed) feat: update Kubernetes to 1.34.0 * [`a1c0e237d`](https://github.com/siderolabs/talos/commit/a1c0e237d6e047bb59c4fbd48e2c2b9e36dd4808) feat: update Linux to 6.15.11, Go to 1.25 * [`4d7fc25f8`](https://github.com/siderolabs/talos/commit/4d7fc25f8bf20d4489080795a3d0ce0dfb1bc6b8) docs: switch order of wipe disk command * [`7368a994d`](https://github.com/siderolabs/talos/commit/7368a994df07cc4e50e3709ac766d8062db070a0) feat: add SOCKS5 proxy support to dynamic proxy dialer * [`d63591069`](https://github.com/siderolabs/talos/commit/d635910697b221aee3e9afa6d9e5b398236b6a21) chore: silence linter warnings * [`07eb4d7ec`](https://github.com/siderolabs/talos/commit/07eb4d7ec148a7e3c4c6dde080469c1a2fb410fb) fix: set default ram unit to MiB instead of MB * [`6b732adc4`](https://github.com/siderolabs/talos/commit/6b732adc43684facfd329f424a34a7e4df36d77b) feat: update Linux to 6.12.43 * [`b6410914f`](https://github.com/siderolabs/talos/commit/b6410914f74ce01672fdef7e912e37970909281c) feat: add human readable byte size cli flags * [`ec70cef99`](https://github.com/siderolabs/talos/commit/ec70cef99005fd7e383fea63b5c23774882fcf28) feat: update NVIDIA drivers and kernel * [`0879efa69`](https://github.com/siderolabs/talos/commit/0879efa690ad657e4aed251fcbeba8f5645d73ce) feat: update Kubernetes default to v1.34.0-rc.2 * [`f504639df`](https://github.com/siderolabs/talos/commit/f504639df4388619f731196ed8e79a6818b6ed5f) feat: add a user-facing create qemu command * [`558e0b09a`](https://github.com/siderolabs/talos/commit/558e0b09ab65b353e83b98c9ddf6cb2b67fd060e) test: fix the Image Factory PXE boot test * [`d73f0a2e5`](https://github.com/siderolabs/talos/commit/d73f0a2e5b788c7b69c2fb827f7111d5f9c8e706) docs: make readme badges consistent * [`f1369af98`](https://github.com/siderolabs/talos/commit/f1369af98e1f6d48fed137e31237956abbd28b0f) chore: use new filesystem api on STATE partition * [`366cedbe7`](https://github.com/siderolabs/talos/commit/366cedbe7495ce15bcd0e6c6f7f0add65a41a861) docs: link to kubernetes linux swap tuning * [`2f5a16f5e`](https://github.com/siderolabs/talos/commit/2f5a16f5e4ae186a309aef5e3d285897d0fe2df1) fix: make --with-uuid-hostnames functionality available to qemu provider * [`70612c1f9`](https://github.com/siderolabs/talos/commit/70612c1f9fc9056e8a3669ff10a385c4e8e03350) refactor: split the PlatformConfigController * [`511748339`](https://github.com/siderolabs/talos/commit/51174833997fd9a0a599ab1dde947834b682ab14) docs: add system extension tier documentation * [`009fb1540`](https://github.com/siderolabs/talos/commit/009fb1540e0b9f5daac6302f42e8813e596fc87c) test: don't run nvidia tests on integration/aws * [`99674ef20`](https://github.com/siderolabs/talos/commit/99674ef20d34166d60563d4bf46fbbfc57399509) docs: apply fixes for what is new * [`92db677b5`](https://github.com/siderolabs/talos/commit/92db677b5d32de32ec7e785531b32202e03283b4) fix: image cache lockup on a missing volume * [`9c97ed886`](https://github.com/siderolabs/talos/commit/9c97ed886b89b2fb84f47866abdf1000839143c4) fix: version contract parsing in encryption keys handling * [`1fc670a08`](https://github.com/siderolabs/talos/commit/1fc670a08dc7af8eaeabdc7134eb77a5c939df40) fix: dial with proxy * [`18447d0af`](https://github.com/siderolabs/talos/commit/18447d0afdbcc8fa7db6ae008e4bc4d5b0a0b00a) feat: update Linux to 6.12.41 * [`f65f39b78`](https://github.com/siderolabs/talos/commit/f65f39b78b0c7881e5f51c66ad022c17c2cd4960) fix: provide mitigation CVE-1999-0524 * [`8817cc60c`](https://github.com/siderolabs/talos/commit/8817cc60cfaf4b50f11c38d3b25df7df48382033) fix: actually use SIDEROV1_KEYS_DIR env var if it's provided * [`b08b20a10`](https://github.com/siderolabs/talos/commit/b08b20a1005256a9e3fc7cae8bcf8eea87f6ac09) feat: use key provider with fallback option for auth type SideroV1 * [`7a52d7489`](https://github.com/siderolabs/talos/commit/7a52d7489c9709708d55f8f001d70700addc7e1e) fix: kubernetes upgrade options for kubelet * [`ea8289f55`](https://github.com/siderolabs/talos/commit/ea8289f550787593b1cd35f2d8da59aa5311880e) feat: add a user facing docker command * [`54ad64765`](https://github.com/siderolabs/talos/commit/54ad64765090d90013e4917d1bf494592069beec) chore: re-enable vulncheck * [`26bbddea9`](https://github.com/siderolabs/talos/commit/26bbddea95669278363c604316ed85986f312d71) fix: darwin build * [`b5d5ef79e`](https://github.com/siderolabs/talos/commit/b5d5ef79e7a2d76e29a7c872c1c418fffc63b0df) fix: set secs field in DHCPv4 packets * [`c07911933`](https://github.com/siderolabs/talos/commit/c0791193373e36c35f29c70318432331b4c6ab2a) chore: refactor how tools are being installed * [`34f25815c`](https://github.com/siderolabs/talos/commit/34f25815c036d2c91bdfddc9c7d40ca2edf677bd) docs: fork docs for v1.12 * [`b66b995d3`](https://github.com/siderolabs/talos/commit/b66b995d34306192cbaa4ef68fe39f821b37d1f0) feat: update default Kubernetes to v1.34.0-rc.1 * [`b967c587d`](https://github.com/siderolabs/talos/commit/b967c587d9f217f25798e0bee0c90393e55dc085) docs: fix clone URL to include `.git` * [`b72c68398`](https://github.com/siderolabs/talos/commit/b72c6839806103ac0a76acd46f30eabea0375790) docs: edit the insecure, etcd-metrics, inline and extramanifests * [`e5b9c1fff`](https://github.com/siderolabs/talos/commit/e5b9c1ffffec9fd49ffb84a36c918e75eaa8f1ef) docs: remov RAS Syndrome * [`701fe774b`](https://github.com/siderolabs/talos/commit/701fe774bd19de7c9f21e043e1520161a8c5fff7) docs: fix cilium links and bump to 1.18.0 * [`d306713a1`](https://github.com/siderolabs/talos/commit/d306713a13a18d7af6caffd5890d54d91d22cad7) feat: update Go to 1.24.6 * [`721595a00`](https://github.com/siderolabs/talos/commit/721595a0009f78a2722802ab665957fd767c4d1e) chore: add deadcode elimination linter * [`dc4865915`](https://github.com/siderolabs/talos/commit/dc4865915d567942adea3efa66f8ad360f9c4cce) refactor: stop using `text/template` in `machined` code paths * [`545be55ed`](https://github.com/siderolabs/talos/commit/545be55edc863245638d4387cb9ee7e7b068f2ba) feat: add a pause function to dashboard * [`06a6c0fe3`](https://github.com/siderolabs/talos/commit/06a6c0fe332940b7a70ea2652bc2a5e7bc51bbf3) refactor: fix deadcode elimination with godbus * [`2dce8f8d4`](https://github.com/siderolabs/talos/commit/2dce8f8d4693a85d2f3bf46169af8cf502d49f9d) refactor: replace containerd/containerd/v2 module for proper DCE * [`9b11d8608`](https://github.com/siderolabs/talos/commit/9b11d86081df8cf77860d2d27eed5d8001ff721e) chore: rekres to configure slack notify workflow for CI failures * [`5ce6a660f`](https://github.com/siderolabs/talos/commit/5ce6a660f67f4e2776550a1e621179beb8a6788c) docs: augment the pod security docs * [`ada51ff69`](https://github.com/siderolabs/talos/commit/ada51ff696011e15dcd9c661da1d839bdc341745) fix: unmarshal encryption STATE from META * [`b9e9b2e07`](https://github.com/siderolabs/talos/commit/b9e9b2e07a645f53ca23355810d485a2622870c9) docs: add what is new notes for 1.11 * [`53055bdf4`](https://github.com/siderolabs/talos/commit/53055bdf49ce4c81f63c159cdbaa8ea85d9ca2b8) docs: fix typo in kubevirt page * [`8d12db480`](https://github.com/siderolabs/talos/commit/8d12db480c38ec37aee5ae7721b2e5ca55ad733e) fix: one more attempt to fix volume mount race on restart * [`34d37a268`](https://github.com/siderolabs/talos/commit/34d37a268a9e0098179369af128261dbfc956d1d) chore: rekres to use correct slack channel for slack-notify * [`326a00538`](https://github.com/siderolabs/talos/commit/326a00538210bf98b01795d314c1e154a74d2d58) feat: implement `talos.config.early` command line arg * [`a5f3000f2`](https://github.com/siderolabs/talos/commit/a5f3000f2e8a79d4e9a5be95fbcac91a2d78675b) feat: implement encryption locking to STATE * [`c1e65a342`](https://github.com/siderolabs/talos/commit/c1e65a34256944743e768613b119c0caa517b54d) docs: remove talos API flags from mgmt commands * [`181d0bbf5`](https://github.com/siderolabs/talos/commit/181d0bbf5381343d35a01190da45e3442320d7c5) feat: bootedentry resource * [`7ad439ac3`](https://github.com/siderolabs/talos/commit/7ad439ac35859695074d3a3efdcdb5c0cab1a5c6) fix: enforce minimum size on user volumes if not set explicitly * [`50e37aefd`](https://github.com/siderolabs/talos/commit/50e37aefdbde973bcc8aa352639946490fbe7d94) fix: live reload of TLS client config for discovery client * [`87efd75ef`](https://github.com/siderolabs/talos/commit/87efd75efb3e62b88b4f65a221f9fbdd4b4d6ef9) feat: update containerd to 2.1.4 * [`724b9de6d`](https://github.com/siderolabs/talos/commit/724b9de6d5195bcccc5f484c696429b2f09ab16e) feat: add F71808E watchdog driver * [`8af96f7af`](https://github.com/siderolabs/talos/commit/8af96f7afdac1c4d5e2697b897b81e2bddd15f66) docs: add ETCD downgrade documentation * [`44edd205d`](https://github.com/siderolabs/talos/commit/44edd205d5fdffab39b65ee62695a40e22ef188c) docs: add remark about 'exclude-from-external-load-balancers' label * [`727101926`](https://github.com/siderolabs/talos/commit/7271019263b0dc5b28d2764d19fe531e473222fc) fix(ci): use a random suffix for ami names * [`d621ce372`](https://github.com/siderolabs/talos/commit/d621ce3726f20ee568ea3b6ac57d9e8dfa0580cc) fix: grype scan * [`d62e255c2`](https://github.com/siderolabs/talos/commit/d62e255c260810a5f0f2959e32592a3331df28d3) fix: issues with reading GPT * [`5d0883e14`](https://github.com/siderolabs/talos/commit/5d0883e147163c12a77cd926db799ffed854aedf) feat: update PCI DB module to v0.3.2 * [`3751c8ccf`](https://github.com/siderolabs/talos/commit/3751c8ccfa1bab9fcd435290f36e9012a5626e40) test: wait for service account test job longer * [`a592eb9f9`](https://github.com/siderolabs/talos/commit/a592eb9f98788883a7ec6d17772e10707230a0d8) feat: update Linux to 6.12.40 * [`4c40e6d3f`](https://github.com/siderolabs/talos/commit/4c40e6d3fb4c2f451a8d7a671df5f6254161bd5d) feat: update etcd to 3.6.4 * [`2bc37bd2c`](https://github.com/siderolabs/talos/commit/2bc37bd2c9679c8055fd7b52eb310f23a329af4e) docs: fix error in kernel module guide * [`bfc57fb86`](https://github.com/siderolabs/talos/commit/bfc57fb863224f7626f49e5b26be06f77bea2e40) chore: tag aws snapshots created via ci with the image name * [`06ef7108a`](https://github.com/siderolabs/talos/commit/06ef7108a6050b3a8fd7535f01a469f09042bf56) fix: issue with volume remount on service restart * [`03efbff18`](https://github.com/siderolabs/talos/commit/03efbff18e420c4fe960f490f91dd9f4751ece04) docs: add SBOM documentation * [`af8a2869d`](https://github.com/siderolabs/talos/commit/af8a2869dbbec073ffaf72a1378682e109b053ec) fix: do not download artifacts for cron Grype scan * [`5f442159b`](https://github.com/siderolabs/talos/commit/5f442159b224c96c90badc7176fed17bfb561709) feat: unify disk encryption configuration * [`38e176e59`](https://github.com/siderolabs/talos/commit/38e176e594edb3d271d98f78417b9fd5ba0c5288) chore(ci): fix datasource versioning * [`85d6b9198`](https://github.com/siderolabs/talos/commit/85d6b919890a1aa9c4f94d5b18861cc617134ff9) feat: update etcd to v3.5.22 * [`dd7bd2dab`](https://github.com/siderolabs/talos/commit/dd7bd2dab8cf09334e3e353d6a477509bbaa303e) docs: rewrite the getting started and prod docs for v1.10 and v1.11 * [`136a899aa`](https://github.com/siderolabs/talos/commit/136a899aa25b3fdcdd771594668278d563f09192) chore: regenerate release step with signing fixes * [`450b30d5a`](https://github.com/siderolabs/talos/commit/450b30d5a986563869efdbaa074e82d612f6f2ef) chore(ci): add more nvidia test matrix * [`451c2c4c3`](https://github.com/siderolabs/talos/commit/451c2c4c39e70c20df58fc31459cd5c789a0e46f) test: add talosctl:latest to the image cache

### Changes from siderolabs/go-debug
1 commit

* [`e21721b`](https://github.com/siderolabs/go-debug/commit/e21721bc4faba9072b5e4e33af60a1f0292547af) chore: add support for Go 1.25

### Changes from siderolabs/go-loadbalancer
1 commit

* [`5e7a8b2`](https://github.com/siderolabs/go-loadbalancer/commit/5e7a8b21cbdb156c6fe6f9fd98b8a1bb1186c21c) feat: add jitter and initial health check wait support to upstreams

### Changes from siderolabs/pkgs
16 commits

* [`2447e11`](https://github.com/siderolabs/pkgs/commit/2447e11dcbcb5dc10703515e2185f753b04e20e0) feat: update Linux to 6.16, GCC to 15 * [`2cfb920`](https://github.com/siderolabs/pkgs/commit/2cfb920acd88d63c3d3ced3a5760549aa180208c) feat: update Linux to 6.15.11, update tools, rekres * [`ab4e975`](https://github.com/siderolabs/pkgs/commit/ab4e9755b0e2dbf38c75db5d2ff7720f511fd50c) feat: update Linux to 6.12.43 * [`cd67e36`](https://github.com/siderolabs/pkgs/commit/cd67e3660fa0a2ad25ca4b8dcd3c1ce9b96b0b72) chore: update kernel config to support max SMP CPUs * [`e3b2094`](https://github.com/siderolabs/pkgs/commit/e3b209474060f5a67e36c9239a3a066ee8ace2fe) fix: fix build for new NVIDIA drivers * [`fd5fdfd`](https://github.com/siderolabs/pkgs/commit/fd5fdfde0bdc4dfc1e9990300df46b9af23c0dfd) feat: update Nvidia LTS to 580.65.06 and production to 570.172.08 * [`0edf426`](https://github.com/siderolabs/pkgs/commit/0edf426d758d67f7baaaa42facdc658396f02f9f) fix: backport CVE kernel patches to 6.12 * [`26d8fef`](https://github.com/siderolabs/pkgs/commit/26d8fefe10329e8d1c285014af0bffe1b9a65431) feat: enable Infiniband IRDMA support * [`16b5fac`](https://github.com/siderolabs/pkgs/commit/16b5facdbb37f2ad0329bf131ded62cc9b1239a9) fix: re-enable CPUSETS_V1 cgroups controller * [`fd53886`](https://github.com/siderolabs/pkgs/commit/fd53886f4f36e73181b7b1a0718801bf8e2aadb9) feat: update backportable dependencies * [`d5f7467`](https://github.com/siderolabs/pkgs/commit/d5f746715727ec34fca7a87ab9f1fac2051f0f75) feat: update Go to 1.24.6 * [`0bd019f`](https://github.com/siderolabs/pkgs/commit/0bd019f29031b7461fbe49552b88d0e26861f955) feat: update containerd to 2.1.4 * [`0ba8b5b`](https://github.com/siderolabs/pkgs/commit/0ba8b5b49f3dedcc49f4040a6f5c57329f5c5605) feat: enable F71808E watchdog driver * [`895a86b`](https://github.com/siderolabs/pkgs/commit/895a86bcdfedfd9ca1a698d8f8aa71e3600a22c2) fix: enable ISCSI IBFT * [`a76a67c`](https://github.com/siderolabs/pkgs/commit/a76a67c860a5100f41223fea936712760b33a4cd) feat: update Linux to 6.12.40 * [`8b0a561`](https://github.com/siderolabs/pkgs/commit/8b0a56180198d360ea71b2c62669545b867f9a67) feat: enable bootloader control on amd64

### Changes from siderolabs/tools
6 commits

* [`7c659e9`](https://github.com/siderolabs/tools/commit/7c659e92db3884737abda95e643995107aa14010) feat: update to GCC 15 * [`83fd7b7`](https://github.com/siderolabs/tools/commit/83fd7b7be62f2f59abeb24c971699895759ebb88) feat: migrate from pkg-config to pkgconf * [`edafd5f`](https://github.com/siderolabs/tools/commit/edafd5f395b1fd31650270332a871c830a5fd781) feat: update toolchain for new Go and Linux headers * [`65789c7`](https://github.com/siderolabs/tools/commit/65789c75ebd4020a444789cfd74d35fefc2497c2) chore: drop unused vars from Pkgfile * [`52db66e`](https://github.com/siderolabs/tools/commit/52db66e8d9e1d83e4872a504d45fdabed31504f7) chore: drop protobuf-related stuff from tools * [`e3c3ef2`](https://github.com/siderolabs/tools/commit/e3c3ef2b604fb80143a17879eeec9f30ca7b07dd) feat: update Go to 1.24.6

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.7.0 -> v0.8.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.18.1 -> v1.19.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.10.1 -> v1.11.0 * **github.com/aws/aws-sdk-go-v2/config** v1.29.17 -> v1.31.6 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.32 -> v1.18.6 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.41.2 -> v1.45.1 * **github.com/aws/smithy-go** v1.22.4 -> v1.23.0 * **github.com/containernetworking/plugins** v1.7.1 -> v1.8.0 * **github.com/docker/go-connections** v0.5.0 -> v0.6.0 * **github.com/gdamore/tcell/v2** v2.8.1 -> v2.9.0 * **github.com/google/cel-go** v0.26.0 -> v0.26.1 * **github.com/gopacket/gopacket** v1.3.1 -> v1.4.0 * **github.com/mdlayher/netlink** fbb4dce95f42 -> v1.8.0 * **github.com/miekg/dns** v1.1.67 -> v1.1.68 * **github.com/rivo/tview** a4a78f1e05cb -> v0.42.0 * **github.com/safchain/ethtool** v0.6.1 -> v0.6.2 * **github.com/siderolabs/go-debug** v0.5.0 -> v0.6.0 * **github.com/siderolabs/go-loadbalancer** v0.4.0 -> v0.5.0 * **github.com/siderolabs/pkgs** v1.11.0-15-g2ac857a -> v1.12.0-alpha.0-14-g2447e11 * **github.com/siderolabs/talos/pkg/machinery** v1.11.0 -> v1.11.0-alpha.3 * **github.com/siderolabs/tools** v1.11.0-2-g8556c73 -> v1.12.0-alpha.0-5-g7c659e9 * **github.com/spf13/cobra** v1.9.1 -> v1.10.1 * **github.com/spf13/pflag** v1.0.7 -> v1.0.9 * **github.com/stretchr/testify** v1.10.0 -> v1.11.1 * **github.com/u-root/u-root** v0.14.0 -> v0.15.0 * **golang.org/x/net** v0.42.0 -> v0.43.0 * **golang.org/x/sys** v0.34.0 -> v0.35.0 * **golang.org/x/term** v0.33.0 -> v0.34.0 * **golang.org/x/text** v0.27.0 -> v0.28.0 * **google.golang.org/grpc** v1.73.0 -> v1.75.0 * **google.golang.org/protobuf** v1.36.6 -> v1.36.8 * **k8s.io/utils** 4c0f3b243397 -> 0af2bda4dd1d Previous release can be found at [v1.11.0](https://github.com/siderolabs/talos/releases/tag/v1.11.0) ## [Talos 1.11.0-alpha.3](https://github.com/siderolabs/talos/releases/tag/v1.11.0-alpha.3) (2025-07-02) Welcome to the v1.11.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Azure Talos on Azure now defaults to MTU of 1400 bytes for the `eth0` interface to avoid packet fragmentation issues. The default MTU can be overriden with machine configuration. ### IMA support removed Talos now drops the IMA (Integrity Measurement Architecture) support. This feature was not used in Talos for any meaningful security purpose and has historically caused performance issues. See #11133 for more details. ### Kubernetes Version Validation Talos now validates Kubernetes version in the image submitted in the machine configuration. Previously this check was performed only on upgrade, but now it is consistently applied to upgrade, initial provisioning, and machine configuration updates. This implies that all image references should contain the tag, even if the image is pinned by digest. ### Qemu provisioner on MacOS On MacOS `talosctl cluster create` command now supports the Qemu provisioner in addition to the Docker provisioner. ### Swap Suport Talos now supports swap on block devices. This feature can be enable by using [SwapVolumeConfig](https://www.talos.dev/v1.11/reference/configuration/block/swapvolumeconfig/) document in the machine configuration. ### Component Updates Linux: 6.12.35 Kubernetes: 1.34.0-alpha.2 runc: 1.3.0 containerd: 2.1.3 Flannel CNI plugin: 1.7.1-flannel1 Flannel: 0.27.0 CoreDNS: 1.12.2 Talos is built with Go 1.24.4. ### VMware Talos VMWare platform now supports `arm64` architecture in addition to `amd64`. ### Zswap Support Talos now supports zswap, a compressed cache for swap pages. This feature can be enabled by using [ZswapConfig](https://www.talos.dev/v1.11/reference/configuration/block/zswapconfig/) document in the machine configuration. ### Contributors * Andrey Smirnov * Noel Georgi * Orzelius * Orzelius * Justin Garrison * Spencer Smith * Till Hoffmann * Utku Ozdemir * Artem Chernyshev * Dmitrii Sharshakov * Michael Robbins * Steve Francis * Andrew Longwill * Marat Bakeev * Olav Thoresen * Thibault VINCENT * Alvaro "Chamo" Linares Cabre * Brian Brookman * Bryan Mora * Clément Nussbaumer * Damien * David R * Dennis Marttinen * Dmitriy Matrenichev * Joakim Nohlgård * Jorik Jonker * Justin Seely * Luke Cousins * Marco Mihai Condrache * Markus Reiter * Martyn Ranyard * Michael Moerz * Mike * Tan Siewert * Tom Keur * jvanthienen-gluo * killcity * yashutanu ### Changes
170 commits

* [`777335f23`](https://github.com/siderolabs/talos/commit/777335f2342abf1c04a738456678980fcc375e1b) chore: improve cloud image uploader resilience * [`14e5eee7d`](https://github.com/siderolabs/talos/commit/14e5eee7d14bdb95e7e632c54705d8753627ab2a) release(v1.11.0-alpha.2): prepare release * [`1e5a008f5`](https://github.com/siderolabs/talos/commit/1e5a008f5740af9dd9297ec5616bde9fd102f21f) fix: hold user volume mount point across kubelet restarts * [`cdad50590`](https://github.com/siderolabs/talos/commit/cdad50590d4436eb12b959f2ff04457d5632f941) docs: user volumes and kubernetes upgrade updates * [`c880835c8`](https://github.com/siderolabs/talos/commit/c880835c809c2a02f0bb6d0450d15df042a50781) feat: implement zswap support * [`7f0300f10`](https://github.com/siderolabs/talos/commit/7f0300f108e7f2e9192214f87a13c8ff2ea25866) feat: update dependencies, Kubernetes 1.34.0-alpha.2 * [`61afbe3d2`](https://github.com/siderolabs/talos/commit/61afbe3d216862a9b9a5c8f521475a0f39cd710e) docs: add vc4 documentation * [`b9dbdc8e7`](https://github.com/siderolabs/talos/commit/b9dbdc8e7213c305e4de71516b990641e0fed706) fix: etcd recover with multiple advertised addresses * [`19d94c357`](https://github.com/siderolabs/talos/commit/19d94c3574b7b3ee3fbe21fdb56cff5a18e7b91e) feat: update Linux to 6.12.35, containerd to 2.1.3 * [`44a1fc3b7`](https://github.com/siderolabs/talos/commit/44a1fc3b78589540f5a0d9b8ea4d898474da3a80) fix: treat context canceled as expected error on image pull * [`4da2dd537`](https://github.com/siderolabs/talos/commit/4da2dd537d5dae884f47bd3f04ddcd05ac6cd222) feat: enforce Kubernetes version compatibility * [`6c7f8201a`](https://github.com/siderolabs/talos/commit/6c7f8201a9ceeec6ecfd0a35b308805ec149f3de) fix: set default MTU on Azure to 1400 * [`091cd6989`](https://github.com/siderolabs/talos/commit/091cd6989ce8c09885b3ae3e8c594c4770bd0748) docs: small yaml typo fix * [`66ecbd48f`](https://github.com/siderolabs/talos/commit/66ecbd48fdaf509bbb2b37327eb0e0891dd81910) docs: update support matrix with omni version * [`c948d7617`](https://github.com/siderolabs/talos/commit/c948d7617d1579c462a809b37956fc98270fcce4) docs: minor fixes for creating kernel modules * [`cc14c4a25`](https://github.com/siderolabs/talos/commit/cc14c4a25d355910a00e60c69ed641abbb7b40f6) docs: add docs for creating kernel modules * [`93bcd3b56`](https://github.com/siderolabs/talos/commit/93bcd3b5623d900a0f731c0f60d3ce0d69c9c32c) docs: create SBOM for Go dependencies * [`38c4ce415`](https://github.com/siderolabs/talos/commit/38c4ce415dc8535b4a7403f7a35c5440f2f4aeb6) feat: add user-space InfiniBand modules * [`251dc934f`](https://github.com/siderolabs/talos/commit/251dc934f3f4d9d81d6d11fd66cf4e52517d9878) feat: arm64 support for platform vmware * [`09b3ad577`](https://github.com/siderolabs/talos/commit/09b3ad5771b4ee813dcb4d53ad8d291b74b8d8fa) feat: update containerd to 2.1.2 * [`0767dd07b`](https://github.com/siderolabs/talos/commit/0767dd07b9067aeb3470d463ff32874c69082853) chore: enable --with-siderolink-agent on Darwin * [`9642198d7`](https://github.com/siderolabs/talos/commit/9642198d76963bd9f6bdda03fb31c165f31f8087) fix: userspace wireguard library overrides * [`208f0763e`](https://github.com/siderolabs/talos/commit/208f0763ef2db94a913606051b5d223d1de61f24) chore: fix talosctl build on non-Linux hosts * [`87421af87`](https://github.com/siderolabs/talos/commit/87421af87a88851b78e576b2f9b4af9a48f0acb8) docs: expand documentation description * [`d32ccfa59`](https://github.com/siderolabs/talos/commit/d32ccfa598284450477af166734595dc952021fa) feat: implement swap support * [`8f5cf81db`](https://github.com/siderolabs/talos/commit/8f5cf81dba80015f66037ee181f17eb2294bb8a2) docs: update kvm documentation * [`8e84c8b0f`](https://github.com/siderolabs/talos/commit/8e84c8b0f8405be519a9f0530e34a612ff054373) fix: nil pointer deref in quirk * [`6e74a3676`](https://github.com/siderolabs/talos/commit/6e74a367636dc21e2bf017d6284bbf998a4bad7d) docs: aad ery basic details on how to run on scaleway * [`260d1bc9a`](https://github.com/siderolabs/talos/commit/260d1bc9a93f5f6added5e6998f3d2f08fedb770) fix: correctl close encrypted volumes * [`034ef42af`](https://github.com/siderolabs/talos/commit/034ef42af25ee3dacf5dd0391385ea881b6d5d32) fix: update siderolink library for wgtunnel panic fix * [`3035744a8`](https://github.com/siderolabs/talos/commit/3035744a8096270691f6bdccfabe34ad53da489c) fix: correctly predict interface name on darwin * [`cfcfad3c4`](https://github.com/siderolabs/talos/commit/cfcfad3c45376b8ebb989b865f3c13729c87d388) chore: move `checkUnknownKeys` function to `github.com/siderolabs/gen` * [`5ecc53c69`](https://github.com/siderolabs/talos/commit/5ecc53c695ec578dbc32f00fa7df65b31a5e77aa) docs: add macos section to developing-talos.md * [`b5b35307f`](https://github.com/siderolabs/talos/commit/b5b35307fe950d0de9ee2ff1d5686af858db13b4) chore: update Go to 1.24.4 * [`fde772d8d`](https://github.com/siderolabs/talos/commit/fde772d8d82e9d6bc7e63b49c965b8d924e308ab) feat: update Flannel to 0.27.0 * [`81ca27949`](https://github.com/siderolabs/talos/commit/81ca27949427c546f43b0409b56f733becabc2f6) release(v1.11.0-alpha.1): prepare release * [`58a868e68`](https://github.com/siderolabs/talos/commit/58a868e68833e94d691e7ed029dce629446fecc3) chore: fix renovate config, add release-gate label * [`a59aaee84`](https://github.com/siderolabs/talos/commit/a59aaee84bcceb20792bc4782748449ad93b0530) feat: bump dependencies, Linux 6.12.31 * [`e954ee30a`](https://github.com/siderolabs/talos/commit/e954ee30add42de6f42cbb7d96927722102afdb7) docs: typo correction: LongHorn -> Longhorn * [`aab053394`](https://github.com/siderolabs/talos/commit/aab053394bafdf718196133e38be010d847db0ad) fix: mashal resource byte slices as strings in YAML * [`c7d4191e7`](https://github.com/siderolabs/talos/commit/c7d4191e78bf0a455ab596f46d4cf212dce694a4) fix: rework the way CRI config generation is waited for * [`0114183de`](https://github.com/siderolabs/talos/commit/0114183de62e4ab930ff0f10dd156f935d57cf10) docs: update `lastRelease` to 1.10.3 * [`938b0760a`](https://github.com/siderolabs/talos/commit/938b0760abdb41be1be4da02b877e2c902d594be) docs: update issue template * [`2a7b735b2`](https://github.com/siderolabs/talos/commit/2a7b735b264ebcfa22dc2d6044c9d5cd3057b5c2) feat: drop IMA support * [`2d5a805b0`](https://github.com/siderolabs/talos/commit/2d5a805b0ebabb804b3c32be18db1d718a91070f) fix: typo in DiscoverdVolume spec * [`60c12bad9`](https://github.com/siderolabs/talos/commit/60c12bad93b422db2784b0203d94ca69fa31957c) feat: support nocloud include url userdata directive * [`0fd622c82`](https://github.com/siderolabs/talos/commit/0fd622c825ba1fbb833a4b8920ac4c4e56f08a1f) fix(talosctl): correct --help output for dashboard command * [`a90c936a1`](https://github.com/siderolabs/talos/commit/a90c936a16756cfe5fe451258f0022b808be17d2) feat: support qemu provisioner on darwin * [`5322ca0d3`](https://github.com/siderolabs/talos/commit/5322ca0d372aa20ad90e66f04699b75debb0ab80) docs: update overlay docs * [`a60b6322d`](https://github.com/siderolabs/talos/commit/a60b6322d1e8fbd75394e0bdb4435af605b32bbb) fix(ci): drop nebula from extensions test * [`dbbb59a67`](https://github.com/siderolabs/talos/commit/dbbb59a6781f79ee34a6e91a72575802561c58b6) docs: add note for default `dataDirHostPath` for Rook * [`e26054378`](https://github.com/siderolabs/talos/commit/e2605437826911cd60a6a4d9ee760a6a242e244b) docs: macos qemu provider * [`5d0224093`](https://github.com/siderolabs/talos/commit/5d022409357d41831fa1bfd34ccdcfceecca42df) docs: use the cilium-cli image repo in the job installation manifest * [`ff80e4cca`](https://github.com/siderolabs/talos/commit/ff80e4cca086fa01d84ceb750111dc9e31ccc978) docs: fix CIDR name * [`a5fd15e8b`](https://github.com/siderolabs/talos/commit/a5fd15e8bd4a4547e3658981543401fd9eb8cd80) fix(ci): reproducibility test * [`8f8963e50`](https://github.com/siderolabs/talos/commit/8f8963e50d7b05d1361fd44040c0f1ffb94693af) docs: update Nexxen brand * [`c6b86872d`](https://github.com/siderolabs/talos/commit/c6b86872dc0d62aef5ad70fce00c411080911ace) fix(ci): iso reproducibility file permissions * [`995a1dec4`](https://github.com/siderolabs/talos/commit/995a1dec4a34f49d84daff16b30f8920275a439d) chore: add a check for unsupported darwin flags * [`9db5d0c97`](https://github.com/siderolabs/talos/commit/9db5d0c97ac31c7f6ce0b23d999126fc6cc094ec) fix: nocloud metadata for hostname * [`3cf325654`](https://github.com/siderolabs/talos/commit/3cf325654e4a7f73196241e59e3ca6b5f24c3e19) feat: modularize more arm64 kernel * [`3524745cc`](https://github.com/siderolabs/talos/commit/3524745cc49c51e4f13da954a57ab56d467fd26e) fix: allow any PKI in Talos API * [`f438cdb09`](https://github.com/siderolabs/talos/commit/f438cdb0993b17f0e540ecefa39cde09f89730f4) chore: use custom dhcpd server on macos qemu * [`11c17fb9a`](https://github.com/siderolabs/talos/commit/11c17fb9aad2443b10e15295069b8e24e0d514e2) fix: metal-iso reproducibility * [`7fcb89ee3`](https://github.com/siderolabs/talos/commit/7fcb89ee385fdbf47dae4a8308299c00488df84a) chore: add darwin vmnet qemu support * [`fc1237343`](https://github.com/siderolabs/talos/commit/fc1237343f79a1be907c43ac3ce116168409ed17) chore: clean up `/usr/bin` * [`b551f32ce`](https://github.com/siderolabs/talos/commit/b551f32ce550f2bc3c679a9857f28d604a297bbf) feat: update containerd to v2.1.1 * [`67f4154f9`](https://github.com/siderolabs/talos/commit/67f4154f920fc0c58a9a832e14fbc7f9430747b3) docs: update disk-management.md * [`0cb137ad7`](https://github.com/siderolabs/talos/commit/0cb137ad7366e2386f49a99aee0a3c5ffb7223f6) fix: make disk size check work on old Talos * [`7c057edd5`](https://github.com/siderolabs/talos/commit/7c057edd5f3636dff6932ad9fbd7c51867b0c2c8) fix: use vmdk-convert istead of qemu-img to create VMDK for OVA files * [`cd618dad0`](https://github.com/siderolabs/talos/commit/cd618dad0feb1390e5945e2bba1d20bcecf30c2a) chore: update the go-blockdevice package * [`0b99631a0`](https://github.com/siderolabs/talos/commit/0b99631a0b64ce8d65ddcf7f40b2168debf11a62) fix: bump apid memory limit * [`5451f35b1`](https://github.com/siderolabs/talos/commit/5451f35b148a630c6ab011dce44b52fd2ad327ba) docs: update virtualbox * [`bd4d202a5`](https://github.com/siderolabs/talos/commit/bd4d202a5a67c56b6c6e6bc962f6bd51c729759f) refactor: bring owned.State from COSI to simplify tests * [`0b96df574`](https://github.com/siderolabs/talos/commit/0b96df57476af86a37bcfdbf28a479444a9e6e5c) feat: update containerd to 2.1.0 * [`e1a939144`](https://github.com/siderolabs/talos/commit/e1a939144f25acc6a2715feedb30a56a47f6793d) docs: fix formatting in disk encryption * [`7a817df1c`](https://github.com/siderolabs/talos/commit/7a817df1cce58de2a16b72b37a54ffc0103af79a) docs: fix typo * [`f35b213b2`](https://github.com/siderolabs/talos/commit/f35b213b2b448c2e0065d4698095a843dd2f5268) test: fix DHCP unicast failures in QEMU environment * [`7064bbf05`](https://github.com/siderolabs/talos/commit/7064bbf056f083de0f7174c9d3c600871189b4e5) docs: fix vmware factory URL * [`78c33bcdb`](https://github.com/siderolabs/talos/commit/78c33bcdb9a30195ce401311e82b2e189faf33f3) feat: update default Kubernetes to v1.33.1 * [`da6795266`](https://github.com/siderolabs/talos/commit/da67952666d2db2b8b5636bd4cae8af09a139410) fix: disable automatic MAC assignment to bridge interfaces * [`ca34adf58`](https://github.com/siderolabs/talos/commit/ca34adf585bfe04d2d1b84f186cb87aa77fc8e00) chore(ci): drop azure keys * [`ea5de19fa`](https://github.com/siderolabs/talos/commit/ea5de19fad3f62889899c0d89d08b8b73dfa75da) fix: selinux detection * [`52c76ea3a`](https://github.com/siderolabs/talos/commit/52c76ea3a61a4a3cbd963dc2ff0d6d21b4210bcd) fix: consistently apply dynamic grpc proxy dialer * [`aa9569e5d`](https://github.com/siderolabs/talos/commit/aa9569e5d8c59b762dfd64a4e9ef42cfdc6f9d51) chore: refactor cluster create cmd flags * [`1161faa05`](https://github.com/siderolabs/talos/commit/1161faa0594c033bf032852b880439b2082c9722) docs: fix typo in Cilium docs * [`164745e44`](https://github.com/siderolabs/talos/commit/164745e44334146b8a6f696640692c25b731414a) docs: remove `preserve` flag mention in upgrade notes * [`9a2ecbaaf`](https://github.com/siderolabs/talos/commit/9a2ecbaaf7b7a3f393dd29272aca34e069a24c6e) fix: makefile operating system param * [`118aa69d6`](https://github.com/siderolabs/talos/commit/118aa69d6f6e71b88747db1e8234d478daa54ab4) chore: update cloud-image-uploader dependencies * [`acdd721cf`](https://github.com/siderolabs/talos/commit/acdd721cfa62f9888a9ceea1693c17348c0d663a) chore: dump qemu pachine ipam records on darwin * [`bb9094534`](https://github.com/siderolabs/talos/commit/bb90945344f02b9cdae6e0e01821792dca25096b) chore: rotate aws iam credentials * [`0bfa4ae1b`](https://github.com/siderolabs/talos/commit/0bfa4ae1b06e1e6330adf331e1a97651bbe39b4a) chore: update deps for cloud-image-uploader * [`956d7c71b`](https://github.com/siderolabs/talos/commit/956d7c71bcdff639b8261cf6cf1a5d19cf702f75) chore: update sops keys * [`e2f819d88`](https://github.com/siderolabs/talos/commit/e2f819d880373102f8a8c7f0ff549e37ba75a08e) test: fix the process runner log collection * [`fdac4cfb9`](https://github.com/siderolabs/talos/commit/fdac4cfb9143853eb21d38e1b3d517455b0ba0f2) fix: upgrade go-kubernetes for DRA flag bug * [`09d88e1e8`](https://github.com/siderolabs/talos/commit/09d88e1e8374ef19e5730994d9b098333347f0b7) test: fix some flaky tests * [`ec1f41a94`](https://github.com/siderolabs/talos/commit/ec1f41a948b1bda02096434e47f2a2a767951fe9) chore: make qemu config server bind work on darwin * [`980f4d2b9`](https://github.com/siderolabs/talos/commit/980f4d2b936cfdc3ebc9882f7c25fbf2d2aa49f8) feat: bump dependencies * [`95259337e`](https://github.com/siderolabs/talos/commit/95259337ee0ccb22d7e9125074818ac8f9afa7af) fix: k8s 1.32->1.33 upgrade check * [`c3c326b40`](https://github.com/siderolabs/talos/commit/c3c326b405804c258b68f19b8d7dacca32535e9b) fix: improve volume mounter automaton * [`918b94d9a`](https://github.com/siderolabs/talos/commit/918b94d9a0b71b759073f8f7eb0f5dc7fdff413f) refactor: rewrite disk size check * [`ab7e693d7`](https://github.com/siderolabs/talos/commit/ab7e693d76500b6cdc2068221bdfce16633a8b01) chore: make qemu lb address bind work on darwin * [`97ceab001`](https://github.com/siderolabs/talos/commit/97ceab001c1bb79407c40d8fff867342656187b9) fix: multiple logic issues in platform network config controller * [`46349a9df`](https://github.com/siderolabs/talos/commit/46349a9df5d026a4e4b807a94865d5b3c371d32a) docs: remove azure image gallery instructions * [`0cfcdd3de`](https://github.com/siderolabs/talos/commit/0cfcdd3de1a20690ce47d63bb56b3d33d11c1474) docs: fix search on base talos.dev * [`78646b4e0`](https://github.com/siderolabs/talos/commit/78646b4e050358b930d27e4eddcfb22c4c825b0c) docs: add registryd debug command * [`c6824c211`](https://github.com/siderolabs/talos/commit/c6824c211438a3fb663f4233e8663732ab2ddf44) fix: deny apply config requests without v1alpha1 in "normal" mode * [`7df0408e4`](https://github.com/siderolabs/talos/commit/7df0408e460ebc392c6927c7b23e3795b9bd2140) fix: interactive installer config gen * [`881c5d62b`](https://github.com/siderolabs/talos/commit/881c5d62bf0d1f3311b3cf946b7801f97c1fb94b) fix: suppress duplicate platform config updates * [`66d77888e`](https://github.com/siderolabs/talos/commit/66d77888e42798995ddc73db3869d16959e53376) fix: replace downloaded asset paths correctly in cluster create cmd * [`6bd6c9b5a`](https://github.com/siderolabs/talos/commit/6bd6c9b5a08ca3b0e9574e1a61edc54c6ff722bb) fix: generate iso greater than 4 gig * [`ac140324e`](https://github.com/siderolabs/talos/commit/ac140324ebfb54f580c9b9bbbb55549bd5ffa11e) fix: skip PCR extension if TPM1.2 is found * [`09ef1f8a4`](https://github.com/siderolabs/talos/commit/09ef1f8a41c84e6a16729e6b6aff81788da0e3f5) fix: ignore http proxy on grpc socket dial * [`22a72dc80`](https://github.com/siderolabs/talos/commit/22a72dc80f2037a4cc7ad696d8dff504deb22630) chore: split options between three structs * [`22c34a50f`](https://github.com/siderolabs/talos/commit/22c34a50fc66edd174ab4a65961257de28a6daa0) fix(ci): provision cron jobs * [`b3b20eff3`](https://github.com/siderolabs/talos/commit/b3b20eff3a29f74d18df634cbb01f41bde17f2c8) fix: containerd crashing with sigsegv * [`f7891c301`](https://github.com/siderolabs/talos/commit/f7891c3018de248c7c66483562227b614689413c) chore: calculate vmnet interface name preemptively * [`ae87edffb`](https://github.com/siderolabs/talos/commit/ae87edffbcdaed12fef41541622f27882ed63755) fix: drop libseccomp from rootfs * [`f74a805bb`](https://github.com/siderolabs/talos/commit/f74a805bb067f55619cae7aebb92f00bb8173c92) fix: do correct backoff for nocloud reconcile * [`01bb294af`](https://github.com/siderolabs/talos/commit/01bb294af63f193dafa12cb623ea77ad67b698fb) fix(ci): provision tests * [`e4945be3b`](https://github.com/siderolabs/talos/commit/e4945be3bc43cbc275e2ea5f399a0188c5e16ad8) docs: add registryd debug command * [`d8c670ad3`](https://github.com/siderolabs/talos/commit/d8c670ad3ecba32c70ff365eaf7a5a4ccb5d721a) release(v1.11.0-alpha.0): prepare release * [`ace44ea61`](https://github.com/siderolabs/talos/commit/ace44ea6169d419f188e0a2456c31f420e61ae77) test: update hydrophone to 0.7.0 * [`3a1163692`](https://github.com/siderolabs/talos/commit/3a1163692da7b41b17f263ab43d0fd81abafc4f8) chore: cross platform qemu preflight checks * [`7914fb104`](https://github.com/siderolabs/talos/commit/7914fb10412d31a1b75c74b0c66578e55fb77bc7) chore: move the create command to it's own package * [`c8e619608`](https://github.com/siderolabs/talos/commit/c8e619608dc8898be71a17c54503085ef38abf37) chore: prepare for release 1.11 * [`1299aaa45`](https://github.com/siderolabs/talos/commit/1299aaa45d997dd23aed380f858cec3bc6b975e4) chore(ci): add extensions test for Youki runtime * [`e50ceb221`](https://github.com/siderolabs/talos/commit/e50ceb221e56f0760d5f2fc9e4b821d6b29add05) docs: activate Talos 1.10 docs * [`9d12aaeb1`](https://github.com/siderolabs/talos/commit/9d12aaeb19d68c5e692921b938d72347f6129f65) test: improve config patch test * [`106a656b6`](https://github.com/siderolabs/talos/commit/106a656b6132e766e9e9ef7b1c12b97a413b5de6) chore: make qemu provider build on darwin * [`8013aa06c`](https://github.com/siderolabs/talos/commit/8013aa06cd338f1dd11061d3455767fee4b9783c) test: replace platform metadata test * [`2b89c2810`](https://github.com/siderolabs/talos/commit/2b89c2810551ab52678e62fcbf5355dd05c72030) fix: relax etcd APIs RBAC requirements * [`1e677587c`](https://github.com/siderolabs/talos/commit/1e677587c0e6c61f724a85f18ee9d436ae6da038) fix: preserve kubelet image suffix * [`62ab8af45`](https://github.com/siderolabs/talos/commit/62ab8af459475cbd24a2f34d8923ce70d1fda3db) fix: disk image generation with image cache * [`d60626f01`](https://github.com/siderolabs/talos/commit/d60626f017ef495210939ee4f8ef7f623dd325f9) fix: handle encryption type mismatch * [`a9109ebd0`](https://github.com/siderolabs/talos/commit/a9109ebd00fcd300bf4262142ade77df6788852b) feat: allow SideroLink unique token in machine config * [`2ff3a6e40`](https://github.com/siderolabs/talos/commit/2ff3a6e4079a29b6b45770204fd8cb30369518e9) feat(kernel): add bcache kernel module to core talos * [`fa95a2146`](https://github.com/siderolabs/talos/commit/fa95a2146056bfe1ae322cb574fd8d432745b5c9) fix(ci): bios provision test * [`f7c5b86be`](https://github.com/siderolabs/talos/commit/f7c5b86be7e2b28906cb66b466a017887ac5e2b6) fix: sync PCR extension with volume provisioning lifecycle * [`f90c79474`](https://github.com/siderolabs/talos/commit/f90c79474b50da35ab8e285ee9723957e4b6cf00) chore: show bound driver in pcidevices info * [`8db34624c`](https://github.com/siderolabs/talos/commit/8db34624c6ed9707ba1165da790f5b389bd1c92f) fix: handle correctly changing platform network config * [`77c7a075b`](https://github.com/siderolabs/talos/commit/77c7a075bbba7ffd24dbd9d5e069ccb50f8143b4) feat: update Kubernetes to 1.33.0 * [`74f0c48c7`](https://github.com/siderolabs/talos/commit/74f0c48c738b0b80278667c3e5a1c5e1ecd5a078) feat: add version compatibility for Talos 1.11 * [`c4fb7dad0`](https://github.com/siderolabs/talos/commit/c4fb7dad0ec390781cca54e2348f116cb1cf1866) fix: force DNS runner shutdown on timeout * [`c49b4836e`](https://github.com/siderolabs/talos/commit/c49b4836e46725940f4731e182475905ebee6019) docs: hetzner: add note about public iso * [`16ea2b113`](https://github.com/siderolabs/talos/commit/16ea2b113fad0c81a96dbcfdf4fd1b9f43bb1282) docs: add what is new for 1.10 * [`be3f0c018`](https://github.com/siderolabs/talos/commit/be3f0c018c50da3d920ed8fe36d4f31c5d3edfac) fix: fix Gvisor tests with containerd patch * [`37db132b3`](https://github.com/siderolabs/talos/commit/37db132b3b3e6c58f15228c64b023e77c15cf012) chore(ci): add provision test with bios * [`ec60b70e7`](https://github.com/siderolabs/talos/commit/ec60b70e7245f49f6ac1d48cd4292b85f1d6f79e) fix: set media type to OCI for image cache layer * [`a471eb31b`](https://github.com/siderolabs/talos/commit/a471eb31b87b393ee9fc57fbc725801d08386ad4) feat: update Linux 6.12.24, containerd 2.0.5 * [`54ad5b872`](https://github.com/siderolabs/talos/commit/54ad5b8729c7d54da2efa6baf7886163741176ed) fix: extension services logging to console * [`601f036ba`](https://github.com/siderolabs/talos/commit/601f036ba9cc762d6a3c6ae819654005f1d49527) docs: correct flannel extra args example * [`ae94377d1`](https://github.com/siderolabs/talos/commit/ae94377d15a3b70248fbb446d13d7ae96bb04e82) feat: support encryption config for user volumes * [`9616f6e8d`](https://github.com/siderolabs/talos/commit/9616f6e8d280e64815fe3e1ba324df1dd5d2122d) docs: add caveat for kubespan and host ports * [`a1d08a362`](https://github.com/siderolabs/talos/commit/a1d08a3624c7c8b5213b8e9dee1cf9289d6719dc) docs: fixes typo at OpenEBS Mayastor worker patches * [`a91e8726e`](https://github.com/siderolabs/talos/commit/a91e8726e433be9db58f1a7a09a4cca422b2b50c) docs: add a dark theme * [`c76189c58`](https://github.com/siderolabs/talos/commit/c76189c58a2fe65954924168d7077350974829dd) fix: grub EFI mount point * [`4ca985c65`](https://github.com/siderolabs/talos/commit/4ca985c656c1924e550d06c073a7c1b6cb03f392) fix: grub efi platform install * [`b31260281`](https://github.com/siderolabs/talos/commit/b31260281dba752e06fcfc645bb020872602d898) docs: update storage.md * [`396a29040`](https://github.com/siderolabs/talos/commit/396a290408eff5bda4ad31fafc33496bea9aa899) feat: add new SBCs * [`a902f6580`](https://github.com/siderolabs/talos/commit/a902f6580f8e104977521a335a41c0cd70256906) feat: update Flannel to v0.26.7 * [`2bbefec1a`](https://github.com/siderolabs/talos/commit/2bbefec1abacae2952782fbd163ef52d34f09858) docs: use cache in preview * [`6028a8d2d`](https://github.com/siderolabs/talos/commit/6028a8d2da571a8a37712f9917e24372cf5af919) docs: update kubeprism.md * [`e51a8ef8c`](https://github.com/siderolabs/talos/commit/e51a8ef8c68bb1cfab2ac845a0b6792d7e000324) fix: prefer new `MountStatus` resource * [`d9c7e7946`](https://github.com/siderolabs/talos/commit/d9c7e79462496d6756c55b0672994aa262eaed4f) docs: fix search * [`b32fa029b`](https://github.com/siderolabs/talos/commit/b32fa029b3f550b3403e25e23aac889d61366389) feat: update Kubernetes to 1.33.0-rc.1 * [`f0ea478cb`](https://github.com/siderolabs/talos/commit/f0ea478cb811675a450839b8dcd351e43404efd4) feat: support address priority * [`8cd3c8dc7`](https://github.com/siderolabs/talos/commit/8cd3c8dc77b25270ed8dea65cbbd4e87c203ee74) test: fix NVIDIA OSS tests * [`62f2d27cd`](https://github.com/siderolabs/talos/commit/62f2d27cd44de5112055b5b47f23b001cadccaae) docs: update virtualbox.md * [`141326ea3`](https://github.com/siderolabs/talos/commit/141326ea3bb2e471a5cb51fd565521683a9792fc) docs: fix tabpane styling * [`134aa53cc`](https://github.com/siderolabs/talos/commit/134aa53ccaba55754544977d695ad3ca5d34e604) feat: update base CoreDNS code in host DNS to 1.12.1

### Changes since v1.11.0-alpha.2
1 commit

* [`777335f23`](https://github.com/siderolabs/talos/commit/777335f2342abf1c04a738456678980fcc375e1b) chore: improve cloud image uploader resilience

### Changes from siderolabs/crypto
2 commits

* [`17107ae`](https://github.com/siderolabs/crypto/commit/17107ae45403a2bcd4fecfb4660b60276652b00d) fix: add generic CSR generator and OpenSSL interop * [`53659fc`](https://github.com/siderolabs/crypto/commit/53659fc35f6abd4ada7ffa22ef1b148cf93c0f28) refactor: split into files

### Changes from siderolabs/gen
4 commits

* [`dcb2b74`](https://github.com/siderolabs/gen/commit/dcb2b7417879f230a569ce834dad5c89bd09d6bf) feat: add `panicsafe` package * [`b36ee43`](https://github.com/siderolabs/gen/commit/b36ee43f667a7a56b340a3e769868ff2a609bb5b) feat: make `xyaml.CheckUnknownKeys` public * [`3e319e7`](https://github.com/siderolabs/gen/commit/3e319e7e52c5a74d1730be8e47952b3d16d91148) feat: implement `xyaml.UnmarshalStrict` * [`7c0324f`](https://github.com/siderolabs/gen/commit/7c0324fee9a7cfbdd117f43702fa273689f0db97) chore: future-proof HashTrieMap

### Changes from siderolabs/go-circular
1 commit

* [`5b39ef8`](https://github.com/siderolabs/go-circular/commit/5b39ef87df04efeaa47fe6374a8114f39c126122) fix: do not log error if chunk zero was never written

### Changes from siderolabs/go-kubernetes
3 commits

* [`657a74b`](https://github.com/siderolabs/go-kubernetes/commit/657a74b7163de7886a9581c446b1de6f21264fd2) feat: prepare for Kubernetes 1.34 * [`9070be4`](https://github.com/siderolabs/go-kubernetes/commit/9070be4308e23d969ec4fc49b25dab4a27d512e7) fix: remove DynamicResourceAllocation feature gate * [`8cb588b`](https://github.com/siderolabs/go-kubernetes/commit/8cb588bc4c93d812de901a6a33e599ba2169cd96) fix: k8s 1.32->1.33 upgrade check

### Changes from siderolabs/pkgs
41 commits

* [`03bb94c`](https://github.com/siderolabs/pkgs/commit/03bb94c39c02b7028f5d595cb758f59b132fa1d3) feat: update dependencies * [`c613abd`](https://github.com/siderolabs/pkgs/commit/c613abd8c4f777ef588cce4ae5563d4024e50507) fix: iptables url * [`fae59df`](https://github.com/siderolabs/pkgs/commit/fae59df236da122c84990a187f4648878f2e4bf7) fix: download and copy hailo8 firmware * [`fadf1e2`](https://github.com/siderolabs/pkgs/commit/fadf1e22a263b3429fa8fd540b4ff5a71ce8ded2) feat: update containerd to 2.1.2 * [`a0b0da1`](https://github.com/siderolabs/pkgs/commit/a0b0da10b5745616651d0bcd4b3aa5a06690fd5a) feat: enable io.latency cgroup controller * [`0aaa07a`](https://github.com/siderolabs/pkgs/commit/0aaa07a2a1af852efbc65a476cdcc17829e33a99) feat: add hailort package * [`8555e94`](https://github.com/siderolabs/pkgs/commit/8555e94f1ed54210ae7768e8ef977e5baec4b2cb) chore: use ftpmirror for GNU sources * [`9fbe2b4`](https://github.com/siderolabs/pkgs/commit/9fbe2b43874b701e04e5817f8a9d485139e96d50) feat: update Go to 1.24.4 * [`79bfa9e`](https://github.com/siderolabs/pkgs/commit/79bfa9e06e5e69955236ffd58323c9936d638d45) feat: update NVIDIA drivers to 570.148.08 * [`c8b8bd8`](https://github.com/siderolabs/pkgs/commit/c8b8bd8b5eb265f8e8c8955998e428b86d177ab5) feat: bump dependencies * [`54bf03e`](https://github.com/siderolabs/pkgs/commit/54bf03ebf24d9ef70a47d4b3b4f30d92191085da) feat: update Linux to 6.12.31 * [`93b3aaa`](https://github.com/siderolabs/pkgs/commit/93b3aaae5369140058e6a5cbdf83d1da235eb735) feat: add patch for CephFS IMA performance regression * [`ebd6627`](https://github.com/siderolabs/pkgs/commit/ebd6627c68406076ed95b2cd629d2ace51bb49b6) feat: disable IMA support * [`8aad53b`](https://github.com/siderolabs/pkgs/commit/8aad53bab3201d7f87d39ab61953e04392402efc) feat: add CONFIG_NFT_CONNLIMIT to kernel * [`7a299fa`](https://github.com/siderolabs/pkgs/commit/7a299fa02106a7216926d6bcff21fb1cd2da7d73) feat: update Linux to 6.12.30 * [`8c4603e`](https://github.com/siderolabs/pkgs/commit/8c4603e90335b9aaf180b954ebc43f65dcb2b7b6) feat: move more configs to modules on arm64 * [`7b1183b`](https://github.com/siderolabs/pkgs/commit/7b1183bea84e46cd8f1a775f95683b8a0039c2d7) feat(kernel): enable IB user-space management and RDMA * [`1b1430e`](https://github.com/siderolabs/pkgs/commit/1b1430e82ef62efdd538588183ed27def2bebbaa) fix: drop pcre2 binaries * [`487610c`](https://github.com/siderolabs/pkgs/commit/487610c4f286210c22cd813427380af654297791) fix: drop broken symlinks * [`f31d518`](https://github.com/siderolabs/pkgs/commit/f31d518eefec0cb672760d00a5c2de37b45dfb45) fix: clean up some binaries * [`0f74b9b`](https://github.com/siderolabs/pkgs/commit/0f74b9bd1d097a283f3edd6165161e4e0688a79f) feat: update containerd to v2.1.1 * [`89b4037`](https://github.com/siderolabs/pkgs/commit/89b40372b8964a9dc9ad3db17a46a9d9c797f60f) fix: tenstorrent pkg name * [`a14b544`](https://github.com/siderolabs/pkgs/commit/a14b54409704c1f3beb0f51089dadd3f3e8dc441) chore: drop qemu-tools vmdk support * [`2563e47`](https://github.com/siderolabs/pkgs/commit/2563e47ca1bfc755ee4ecf2b470cfed081b54e6f) feat: add tenstorrent package * [`2a1c42f`](https://github.com/siderolabs/pkgs/commit/2a1c42fde5fe4009c33d50d571d7d3cfe3a09888) fix(renovate): flannel config * [`bfa69a8`](https://github.com/siderolabs/pkgs/commit/bfa69a820e8190aed3a45c00dff5f4f1cc42b7a6) feat: add open-vmdk package * [`9f1ba1f`](https://github.com/siderolabs/pkgs/commit/9f1ba1f047c835abdf882540d316055a3e2d1bfc) fix: bring back updated containerd gvisor patch * [`1567cb6`](https://github.com/siderolabs/pkgs/commit/1567cb616691dc22fbc3374cdeac11cdbe51bb94) feat: update Linux 6.12.28, firmware * [`9bc66e6`](https://github.com/siderolabs/pkgs/commit/9bc66e6bd355f8a86c4becbd78aede1323e3681e) feat: update containerd to 2.1.0 * [`c6b54e0`](https://github.com/siderolabs/pkgs/commit/c6b54e04fb5d943ff31f05b1e095af65eb901604) feat: enable zswap * [`4cd7084`](https://github.com/siderolabs/pkgs/commit/4cd7084634c2b79541da8c6f95c047d4eb0e66a2) feat: update dependencies * [`a3fcbf8`](https://github.com/siderolabs/pkgs/commit/a3fcbf812632aaa8e8f9027a88181c284e7d919d) feat(kernel): enable panthor driver * [`74d1665`](https://github.com/siderolabs/pkgs/commit/74d16657fd53c30249c3eba75769f90dd84366ce) feat: update ZFS to 2.3.2 * [`ddc866b`](https://github.com/siderolabs/pkgs/commit/ddc866bc9dd0557c2e9d5d0b234348767769cfd3) feat: update Linux to 6.12.27 * [`a347857`](https://github.com/siderolabs/pkgs/commit/a347857b33a6a41fe2661a7451c3af65a51404c9) fix: build containerd with Go 1.23 * [`74da85c`](https://github.com/siderolabs/pkgs/commit/74da85c2cf61b8006af38b3d0d38dc13098d5227) fix: containerd build doesn't need seccomp * [`4effa05`](https://github.com/siderolabs/pkgs/commit/4effa0525dc87974052e9dec2685a0ad411773dd) fix: downgrade libseccomp to 2.5.5 * [`9cea00b`](https://github.com/siderolabs/pkgs/commit/9cea00b4601d7bedf49606b647003f3c6cb0787b) feat: update Linux to 6.12.25 * [`cb108a5`](https://github.com/siderolabs/pkgs/commit/cb108a514b55a302008fb4c1ce6d88ce0d769b58) feat(kernel): enable bcache module * [`d042432`](https://github.com/siderolabs/pkgs/commit/d04243270a4f10f9ecb889883ab42687e5ae6351) fix: backport sandbox fix for Gvisor * [`fa625dc`](https://github.com/siderolabs/pkgs/commit/fa625dc6dd97a61cb8479b8b0ab82126650de11b) feat: update Linux 6.12.24, containerd 2.0.5

### Changes from siderolabs/siderolink
3 commits

* [`5f46f65`](https://github.com/siderolabs/siderolink/commit/5f46f6583b9d03f91c9bb5f637149fe466d17bfc) feat: handle panics in goroutines * [`d09ff45`](https://github.com/siderolabs/siderolink/commit/d09ff45b450a37aa84652fa70b5cd3467ee8243d) fix: race in wait value * [`d2a79e0`](https://github.com/siderolabs/siderolink/commit/d2a79e0263806b68ff0a44ea9efa58b83fb269ec) fix: clean up device on failure

### Changes from siderolabs/tools
4 commits

* [`1dfd14b`](https://github.com/siderolabs/tools/commit/1dfd14bd4f2573d1070008c8f9d6a05ca064081e) feat: update Go to 1.24.4 * [`af3fd64`](https://github.com/siderolabs/tools/commit/af3fd645d48a373396f8346af411c1c827c87376) feat: update dependencies * [`e35234b`](https://github.com/siderolabs/tools/commit/e35234bd94c3c16daf06d00848d7752f5e4c7d15) feat: update dependencies * [`c96a4e6`](https://github.com/siderolabs/tools/commit/c96a4e671e378f80f161e45942f80b10adfd562d) chore: update toolchain to the latest version

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.6.0 -> v0.7.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.9.0 -> v1.10.1 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.3.1 -> v1.4.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.3.1 -> v1.4.0 * **github.com/aws/aws-sdk-go-v2/config** v1.29.14 -> v1.29.17 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.30 -> v1.16.32 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.38.3 -> v1.41.2 * **github.com/aws/smithy-go** v1.22.3 -> v1.22.4 * **github.com/containerd/containerd/api** v1.8.0 -> v1.9.0 * **github.com/containerd/containerd/v2** v2.0.5 -> v2.1.3 * **github.com/containernetworking/plugins** v1.6.2 -> v1.7.1 * **github.com/cosi-project/runtime** v0.10.2 -> v0.10.6 * **github.com/detailyang/go-fallocate** 432fa640bd2e **_new_** * **github.com/docker/cli** v28.0.4 -> v28.3.0 * **github.com/docker/docker** v28.0.4 -> v28.3.0 * **github.com/equinix-ms/go-vmw-guestrpc** v0.1.1 **_new_** * **github.com/foxboron/go-uefi** 69fb7dba244f -> a3183a1bfc84 * **github.com/google/cadvisor** v0.52.1 -> v0.53.0 * **github.com/google/cel-go** v0.24.1 -> v0.25.0 * **github.com/google/go-containerregistry** v0.20.3 -> v0.20.6 * **github.com/google/go-tpm** v0.9.3 -> v0.9.5 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.3.1 -> v2.3.2 * **github.com/hetznercloud/hcloud-go/v2** v2.21.0 -> v2.21.1 * **github.com/jsimonetti/rtnetlink/v2** v2.0.3 -> v2.0.5 * **github.com/klauspost/cpuid/v2** v2.2.10 -> v2.2.11 * **github.com/linode/go-metadata** v0.2.1 -> v0.2.2 * **github.com/miekg/dns** v1.1.65 -> v1.1.66 * **github.com/pkg/xattr** v0.4.10 -> v0.4.11 * **github.com/prometheus/procfs** v0.16.0 -> v0.16.1 * **github.com/rivo/tview** 949945f8d922 -> a4a78f1e05cb * **github.com/safchain/ethtool** v0.5.10 -> v0.6.1 * **github.com/siderolabs/crypto** v0.5.1 -> v0.6.0 * **github.com/siderolabs/gen** v0.8.0 -> v0.8.4 * **github.com/siderolabs/go-blockdevice/v2** v2.0.16 -> v2.0.18 * **github.com/siderolabs/go-circular** v0.2.2 -> v0.2.3 * **github.com/siderolabs/go-kubernetes** v0.2.21 -> v0.2.24 * **github.com/siderolabs/pkgs** v1.10.0-5-g48dba3e -> v1.11.0-alpha.0-40-g03bb94c * **github.com/siderolabs/siderolink** v0.3.13 -> v0.3.15 * **github.com/siderolabs/talos/pkg/machinery** v1.10.0 -> v1.11.0-alpha.2 * **github.com/siderolabs/tools** v1.10.0 -> v1.11.0-alpha.0-3-g1dfd14b * **go.etcd.io/etcd/api/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/client/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.21 -> v3.6.1 * **golang.org/x/net** v0.39.0 -> v0.41.0 * **golang.org/x/oauth2** v0.29.0 -> v0.30.0 * **golang.org/x/sync** v0.13.0 -> v0.15.0 * **golang.org/x/sys** v0.32.0 -> v0.33.0 * **golang.org/x/term** v0.31.0 -> v0.32.0 * **golang.org/x/text** v0.24.0 -> v0.26.0 * **golang.org/x/time** v0.11.0 -> v0.12.0 * **google.golang.org/grpc** v1.71.1 -> v1.73.0 * **k8s.io/api** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/apimachinery** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/apiserver** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/client-go** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/component-base** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/cri-api** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kube-scheduler** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kubectl** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kubelet** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/pod-security-admission** v0.33.0 -> v0.34.0-alpha.2 * **sigs.k8s.io/hydrophone** b92baf7e0b04 -> v0.7.0 * **sigs.k8s.io/yaml** v1.4.0 -> v1.5.0 Previous release can be found at [v1.10.0](https://github.com/siderolabs/talos/releases/tag/v1.10.0) ## [Talos 1.11.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.11.0-alpha.2) (2025-07-01) Welcome to the v1.11.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Azure Talos on Azure now defaults to MTU of 1400 bytes for the `eth0` interface to avoid packet fragmentation issues. The default MTU can be overriden with machine configuration. ### IMA support removed Talos now drops the IMA (Integrity Measurement Architecture) support. This feature was not used in Talos for any meaningful security purpose and has historically caused performance issues. See #11133 for more details. ### Kubernetes Version Validation Talos now validates Kubernetes version in the image submitted in the machine configuration. Previously this check was performed only on upgrade, but now it is consistently applied to upgrade, initial provisioning, and machine configuration updates. This implies that all image references should contain the tag, even if the image is pinned by digest. ### Qemu provisioner on MacOS On MacOS `talosctl cluster create` command now supports the Qemu provisioner in addition to the Docker provisioner. ### Swap Suport Talos now supports swap on block devices. This feature can be enable by using [SwapVolumeConfig](https://www.talos.dev/v1.11/reference/configuration/block/swapvolumeconfig/) document in the machine configuration. ### Component Updates Linux: 6.12.35 Kubernetes: 1.34.0-alpha.2 runc: 1.3.0 containerd: 2.1.3 Flannel CNI plugin: 1.7.1-flannel1 Flannel: 0.27.0 CoreDNS: 1.12.2 Talos is built with Go 1.24.4. ### VMware Talos VMWare platform now supports `arm64` architecture in addition to `amd64`. ### Zswap Support Talos now supports zswap, a compressed cache for swap pages. This feature can be enabled by using [ZswapConfig](https://www.talos.dev/v1.11/reference/configuration/block/zswapconfig/) document in the machine configuration. ### Contributors * Andrey Smirnov * Noel Georgi * Orzelius * Orzelius * Justin Garrison * Spencer Smith * Till Hoffmann * Utku Ozdemir * Artem Chernyshev * Dmitrii Sharshakov * Michael Robbins * Steve Francis * Andrew Longwill * Marat Bakeev * Olav Thoresen * Thibault VINCENT * Alvaro "Chamo" Linares Cabre * Brian Brookman * Bryan Mora * Clément Nussbaumer * Damien * David R * Dennis Marttinen * Dmitriy Matrenichev * Joakim Nohlgård * Jorik Jonker * Justin Seely * Luke Cousins * Marco Mihai Condrache * Markus Reiter * Martyn Ranyard * Michael Moerz * Mike * Tan Siewert * Tom Keur * jvanthienen-gluo * killcity * yashutanu ### Changes
168 commits

* [`1e5a008f5`](https://github.com/siderolabs/talos/commit/1e5a008f5740af9dd9297ec5616bde9fd102f21f) fix: hold user volume mount point across kubelet restarts * [`cdad50590`](https://github.com/siderolabs/talos/commit/cdad50590d4436eb12b959f2ff04457d5632f941) docs: user volumes and kubernetes upgrade updates * [`c880835c8`](https://github.com/siderolabs/talos/commit/c880835c809c2a02f0bb6d0450d15df042a50781) feat: implement zswap support * [`7f0300f10`](https://github.com/siderolabs/talos/commit/7f0300f108e7f2e9192214f87a13c8ff2ea25866) feat: update dependencies, Kubernetes 1.34.0-alpha.2 * [`61afbe3d2`](https://github.com/siderolabs/talos/commit/61afbe3d216862a9b9a5c8f521475a0f39cd710e) docs: add vc4 documentation * [`b9dbdc8e7`](https://github.com/siderolabs/talos/commit/b9dbdc8e7213c305e4de71516b990641e0fed706) fix: etcd recover with multiple advertised addresses * [`19d94c357`](https://github.com/siderolabs/talos/commit/19d94c3574b7b3ee3fbe21fdb56cff5a18e7b91e) feat: update Linux to 6.12.35, containerd to 2.1.3 * [`44a1fc3b7`](https://github.com/siderolabs/talos/commit/44a1fc3b78589540f5a0d9b8ea4d898474da3a80) fix: treat context canceled as expected error on image pull * [`4da2dd537`](https://github.com/siderolabs/talos/commit/4da2dd537d5dae884f47bd3f04ddcd05ac6cd222) feat: enforce Kubernetes version compatibility * [`6c7f8201a`](https://github.com/siderolabs/talos/commit/6c7f8201a9ceeec6ecfd0a35b308805ec149f3de) fix: set default MTU on Azure to 1400 * [`091cd6989`](https://github.com/siderolabs/talos/commit/091cd6989ce8c09885b3ae3e8c594c4770bd0748) docs: small yaml typo fix * [`66ecbd48f`](https://github.com/siderolabs/talos/commit/66ecbd48fdaf509bbb2b37327eb0e0891dd81910) docs: update support matrix with omni version * [`c948d7617`](https://github.com/siderolabs/talos/commit/c948d7617d1579c462a809b37956fc98270fcce4) docs: minor fixes for creating kernel modules * [`cc14c4a25`](https://github.com/siderolabs/talos/commit/cc14c4a25d355910a00e60c69ed641abbb7b40f6) docs: add docs for creating kernel modules * [`93bcd3b56`](https://github.com/siderolabs/talos/commit/93bcd3b5623d900a0f731c0f60d3ce0d69c9c32c) docs: create SBOM for Go dependencies * [`38c4ce415`](https://github.com/siderolabs/talos/commit/38c4ce415dc8535b4a7403f7a35c5440f2f4aeb6) feat: add user-space InfiniBand modules * [`251dc934f`](https://github.com/siderolabs/talos/commit/251dc934f3f4d9d81d6d11fd66cf4e52517d9878) feat: arm64 support for platform vmware * [`09b3ad577`](https://github.com/siderolabs/talos/commit/09b3ad5771b4ee813dcb4d53ad8d291b74b8d8fa) feat: update containerd to 2.1.2 * [`0767dd07b`](https://github.com/siderolabs/talos/commit/0767dd07b9067aeb3470d463ff32874c69082853) chore: enable --with-siderolink-agent on Darwin * [`9642198d7`](https://github.com/siderolabs/talos/commit/9642198d76963bd9f6bdda03fb31c165f31f8087) fix: userspace wireguard library overrides * [`208f0763e`](https://github.com/siderolabs/talos/commit/208f0763ef2db94a913606051b5d223d1de61f24) chore: fix talosctl build on non-Linux hosts * [`87421af87`](https://github.com/siderolabs/talos/commit/87421af87a88851b78e576b2f9b4af9a48f0acb8) docs: expand documentation description * [`d32ccfa59`](https://github.com/siderolabs/talos/commit/d32ccfa598284450477af166734595dc952021fa) feat: implement swap support * [`8f5cf81db`](https://github.com/siderolabs/talos/commit/8f5cf81dba80015f66037ee181f17eb2294bb8a2) docs: update kvm documentation * [`8e84c8b0f`](https://github.com/siderolabs/talos/commit/8e84c8b0f8405be519a9f0530e34a612ff054373) fix: nil pointer deref in quirk * [`6e74a3676`](https://github.com/siderolabs/talos/commit/6e74a367636dc21e2bf017d6284bbf998a4bad7d) docs: aad ery basic details on how to run on scaleway * [`260d1bc9a`](https://github.com/siderolabs/talos/commit/260d1bc9a93f5f6added5e6998f3d2f08fedb770) fix: correctl close encrypted volumes * [`034ef42af`](https://github.com/siderolabs/talos/commit/034ef42af25ee3dacf5dd0391385ea881b6d5d32) fix: update siderolink library for wgtunnel panic fix * [`3035744a8`](https://github.com/siderolabs/talos/commit/3035744a8096270691f6bdccfabe34ad53da489c) fix: correctly predict interface name on darwin * [`cfcfad3c4`](https://github.com/siderolabs/talos/commit/cfcfad3c45376b8ebb989b865f3c13729c87d388) chore: move `checkUnknownKeys` function to `github.com/siderolabs/gen` * [`5ecc53c69`](https://github.com/siderolabs/talos/commit/5ecc53c695ec578dbc32f00fa7df65b31a5e77aa) docs: add macos section to developing-talos.md * [`b5b35307f`](https://github.com/siderolabs/talos/commit/b5b35307fe950d0de9ee2ff1d5686af858db13b4) chore: update Go to 1.24.4 * [`fde772d8d`](https://github.com/siderolabs/talos/commit/fde772d8d82e9d6bc7e63b49c965b8d924e308ab) feat: update Flannel to 0.27.0 * [`81ca27949`](https://github.com/siderolabs/talos/commit/81ca27949427c546f43b0409b56f733becabc2f6) release(v1.11.0-alpha.1): prepare release * [`58a868e68`](https://github.com/siderolabs/talos/commit/58a868e68833e94d691e7ed029dce629446fecc3) chore: fix renovate config, add release-gate label * [`a59aaee84`](https://github.com/siderolabs/talos/commit/a59aaee84bcceb20792bc4782748449ad93b0530) feat: bump dependencies, Linux 6.12.31 * [`e954ee30a`](https://github.com/siderolabs/talos/commit/e954ee30add42de6f42cbb7d96927722102afdb7) docs: typo correction: LongHorn -> Longhorn * [`aab053394`](https://github.com/siderolabs/talos/commit/aab053394bafdf718196133e38be010d847db0ad) fix: mashal resource byte slices as strings in YAML * [`c7d4191e7`](https://github.com/siderolabs/talos/commit/c7d4191e78bf0a455ab596f46d4cf212dce694a4) fix: rework the way CRI config generation is waited for * [`0114183de`](https://github.com/siderolabs/talos/commit/0114183de62e4ab930ff0f10dd156f935d57cf10) docs: update `lastRelease` to 1.10.3 * [`938b0760a`](https://github.com/siderolabs/talos/commit/938b0760abdb41be1be4da02b877e2c902d594be) docs: update issue template * [`2a7b735b2`](https://github.com/siderolabs/talos/commit/2a7b735b264ebcfa22dc2d6044c9d5cd3057b5c2) feat: drop IMA support * [`2d5a805b0`](https://github.com/siderolabs/talos/commit/2d5a805b0ebabb804b3c32be18db1d718a91070f) fix: typo in DiscoverdVolume spec * [`60c12bad9`](https://github.com/siderolabs/talos/commit/60c12bad93b422db2784b0203d94ca69fa31957c) feat: support nocloud include url userdata directive * [`0fd622c82`](https://github.com/siderolabs/talos/commit/0fd622c825ba1fbb833a4b8920ac4c4e56f08a1f) fix(talosctl): correct --help output for dashboard command * [`a90c936a1`](https://github.com/siderolabs/talos/commit/a90c936a16756cfe5fe451258f0022b808be17d2) feat: support qemu provisioner on darwin * [`5322ca0d3`](https://github.com/siderolabs/talos/commit/5322ca0d372aa20ad90e66f04699b75debb0ab80) docs: update overlay docs * [`a60b6322d`](https://github.com/siderolabs/talos/commit/a60b6322d1e8fbd75394e0bdb4435af605b32bbb) fix(ci): drop nebula from extensions test * [`dbbb59a67`](https://github.com/siderolabs/talos/commit/dbbb59a6781f79ee34a6e91a72575802561c58b6) docs: add note for default `dataDirHostPath` for Rook * [`e26054378`](https://github.com/siderolabs/talos/commit/e2605437826911cd60a6a4d9ee760a6a242e244b) docs: macos qemu provider * [`5d0224093`](https://github.com/siderolabs/talos/commit/5d022409357d41831fa1bfd34ccdcfceecca42df) docs: use the cilium-cli image repo in the job installation manifest * [`ff80e4cca`](https://github.com/siderolabs/talos/commit/ff80e4cca086fa01d84ceb750111dc9e31ccc978) docs: fix CIDR name * [`a5fd15e8b`](https://github.com/siderolabs/talos/commit/a5fd15e8bd4a4547e3658981543401fd9eb8cd80) fix(ci): reproducibility test * [`8f8963e50`](https://github.com/siderolabs/talos/commit/8f8963e50d7b05d1361fd44040c0f1ffb94693af) docs: update Nexxen brand * [`c6b86872d`](https://github.com/siderolabs/talos/commit/c6b86872dc0d62aef5ad70fce00c411080911ace) fix(ci): iso reproducibility file permissions * [`995a1dec4`](https://github.com/siderolabs/talos/commit/995a1dec4a34f49d84daff16b30f8920275a439d) chore: add a check for unsupported darwin flags * [`9db5d0c97`](https://github.com/siderolabs/talos/commit/9db5d0c97ac31c7f6ce0b23d999126fc6cc094ec) fix: nocloud metadata for hostname * [`3cf325654`](https://github.com/siderolabs/talos/commit/3cf325654e4a7f73196241e59e3ca6b5f24c3e19) feat: modularize more arm64 kernel * [`3524745cc`](https://github.com/siderolabs/talos/commit/3524745cc49c51e4f13da954a57ab56d467fd26e) fix: allow any PKI in Talos API * [`f438cdb09`](https://github.com/siderolabs/talos/commit/f438cdb0993b17f0e540ecefa39cde09f89730f4) chore: use custom dhcpd server on macos qemu * [`11c17fb9a`](https://github.com/siderolabs/talos/commit/11c17fb9aad2443b10e15295069b8e24e0d514e2) fix: metal-iso reproducibility * [`7fcb89ee3`](https://github.com/siderolabs/talos/commit/7fcb89ee385fdbf47dae4a8308299c00488df84a) chore: add darwin vmnet qemu support * [`fc1237343`](https://github.com/siderolabs/talos/commit/fc1237343f79a1be907c43ac3ce116168409ed17) chore: clean up `/usr/bin` * [`b551f32ce`](https://github.com/siderolabs/talos/commit/b551f32ce550f2bc3c679a9857f28d604a297bbf) feat: update containerd to v2.1.1 * [`67f4154f9`](https://github.com/siderolabs/talos/commit/67f4154f920fc0c58a9a832e14fbc7f9430747b3) docs: update disk-management.md * [`0cb137ad7`](https://github.com/siderolabs/talos/commit/0cb137ad7366e2386f49a99aee0a3c5ffb7223f6) fix: make disk size check work on old Talos * [`7c057edd5`](https://github.com/siderolabs/talos/commit/7c057edd5f3636dff6932ad9fbd7c51867b0c2c8) fix: use vmdk-convert istead of qemu-img to create VMDK for OVA files * [`cd618dad0`](https://github.com/siderolabs/talos/commit/cd618dad0feb1390e5945e2bba1d20bcecf30c2a) chore: update the go-blockdevice package * [`0b99631a0`](https://github.com/siderolabs/talos/commit/0b99631a0b64ce8d65ddcf7f40b2168debf11a62) fix: bump apid memory limit * [`5451f35b1`](https://github.com/siderolabs/talos/commit/5451f35b148a630c6ab011dce44b52fd2ad327ba) docs: update virtualbox * [`bd4d202a5`](https://github.com/siderolabs/talos/commit/bd4d202a5a67c56b6c6e6bc962f6bd51c729759f) refactor: bring owned.State from COSI to simplify tests * [`0b96df574`](https://github.com/siderolabs/talos/commit/0b96df57476af86a37bcfdbf28a479444a9e6e5c) feat: update containerd to 2.1.0 * [`e1a939144`](https://github.com/siderolabs/talos/commit/e1a939144f25acc6a2715feedb30a56a47f6793d) docs: fix formatting in disk encryption * [`7a817df1c`](https://github.com/siderolabs/talos/commit/7a817df1cce58de2a16b72b37a54ffc0103af79a) docs: fix typo * [`f35b213b2`](https://github.com/siderolabs/talos/commit/f35b213b2b448c2e0065d4698095a843dd2f5268) test: fix DHCP unicast failures in QEMU environment * [`7064bbf05`](https://github.com/siderolabs/talos/commit/7064bbf056f083de0f7174c9d3c600871189b4e5) docs: fix vmware factory URL * [`78c33bcdb`](https://github.com/siderolabs/talos/commit/78c33bcdb9a30195ce401311e82b2e189faf33f3) feat: update default Kubernetes to v1.33.1 * [`da6795266`](https://github.com/siderolabs/talos/commit/da67952666d2db2b8b5636bd4cae8af09a139410) fix: disable automatic MAC assignment to bridge interfaces * [`ca34adf58`](https://github.com/siderolabs/talos/commit/ca34adf585bfe04d2d1b84f186cb87aa77fc8e00) chore(ci): drop azure keys * [`ea5de19fa`](https://github.com/siderolabs/talos/commit/ea5de19fad3f62889899c0d89d08b8b73dfa75da) fix: selinux detection * [`52c76ea3a`](https://github.com/siderolabs/talos/commit/52c76ea3a61a4a3cbd963dc2ff0d6d21b4210bcd) fix: consistently apply dynamic grpc proxy dialer * [`aa9569e5d`](https://github.com/siderolabs/talos/commit/aa9569e5d8c59b762dfd64a4e9ef42cfdc6f9d51) chore: refactor cluster create cmd flags * [`1161faa05`](https://github.com/siderolabs/talos/commit/1161faa0594c033bf032852b880439b2082c9722) docs: fix typo in Cilium docs * [`164745e44`](https://github.com/siderolabs/talos/commit/164745e44334146b8a6f696640692c25b731414a) docs: remove `preserve` flag mention in upgrade notes * [`9a2ecbaaf`](https://github.com/siderolabs/talos/commit/9a2ecbaaf7b7a3f393dd29272aca34e069a24c6e) fix: makefile operating system param * [`118aa69d6`](https://github.com/siderolabs/talos/commit/118aa69d6f6e71b88747db1e8234d478daa54ab4) chore: update cloud-image-uploader dependencies * [`acdd721cf`](https://github.com/siderolabs/talos/commit/acdd721cfa62f9888a9ceea1693c17348c0d663a) chore: dump qemu pachine ipam records on darwin * [`bb9094534`](https://github.com/siderolabs/talos/commit/bb90945344f02b9cdae6e0e01821792dca25096b) chore: rotate aws iam credentials * [`0bfa4ae1b`](https://github.com/siderolabs/talos/commit/0bfa4ae1b06e1e6330adf331e1a97651bbe39b4a) chore: update deps for cloud-image-uploader * [`956d7c71b`](https://github.com/siderolabs/talos/commit/956d7c71bcdff639b8261cf6cf1a5d19cf702f75) chore: update sops keys * [`e2f819d88`](https://github.com/siderolabs/talos/commit/e2f819d880373102f8a8c7f0ff549e37ba75a08e) test: fix the process runner log collection * [`fdac4cfb9`](https://github.com/siderolabs/talos/commit/fdac4cfb9143853eb21d38e1b3d517455b0ba0f2) fix: upgrade go-kubernetes for DRA flag bug * [`09d88e1e8`](https://github.com/siderolabs/talos/commit/09d88e1e8374ef19e5730994d9b098333347f0b7) test: fix some flaky tests * [`ec1f41a94`](https://github.com/siderolabs/talos/commit/ec1f41a948b1bda02096434e47f2a2a767951fe9) chore: make qemu config server bind work on darwin * [`980f4d2b9`](https://github.com/siderolabs/talos/commit/980f4d2b936cfdc3ebc9882f7c25fbf2d2aa49f8) feat: bump dependencies * [`95259337e`](https://github.com/siderolabs/talos/commit/95259337ee0ccb22d7e9125074818ac8f9afa7af) fix: k8s 1.32->1.33 upgrade check * [`c3c326b40`](https://github.com/siderolabs/talos/commit/c3c326b405804c258b68f19b8d7dacca32535e9b) fix: improve volume mounter automaton * [`918b94d9a`](https://github.com/siderolabs/talos/commit/918b94d9a0b71b759073f8f7eb0f5dc7fdff413f) refactor: rewrite disk size check * [`ab7e693d7`](https://github.com/siderolabs/talos/commit/ab7e693d76500b6cdc2068221bdfce16633a8b01) chore: make qemu lb address bind work on darwin * [`97ceab001`](https://github.com/siderolabs/talos/commit/97ceab001c1bb79407c40d8fff867342656187b9) fix: multiple logic issues in platform network config controller * [`46349a9df`](https://github.com/siderolabs/talos/commit/46349a9df5d026a4e4b807a94865d5b3c371d32a) docs: remove azure image gallery instructions * [`0cfcdd3de`](https://github.com/siderolabs/talos/commit/0cfcdd3de1a20690ce47d63bb56b3d33d11c1474) docs: fix search on base talos.dev * [`78646b4e0`](https://github.com/siderolabs/talos/commit/78646b4e050358b930d27e4eddcfb22c4c825b0c) docs: add registryd debug command * [`c6824c211`](https://github.com/siderolabs/talos/commit/c6824c211438a3fb663f4233e8663732ab2ddf44) fix: deny apply config requests without v1alpha1 in "normal" mode * [`7df0408e4`](https://github.com/siderolabs/talos/commit/7df0408e460ebc392c6927c7b23e3795b9bd2140) fix: interactive installer config gen * [`881c5d62b`](https://github.com/siderolabs/talos/commit/881c5d62bf0d1f3311b3cf946b7801f97c1fb94b) fix: suppress duplicate platform config updates * [`66d77888e`](https://github.com/siderolabs/talos/commit/66d77888e42798995ddc73db3869d16959e53376) fix: replace downloaded asset paths correctly in cluster create cmd * [`6bd6c9b5a`](https://github.com/siderolabs/talos/commit/6bd6c9b5a08ca3b0e9574e1a61edc54c6ff722bb) fix: generate iso greater than 4 gig * [`ac140324e`](https://github.com/siderolabs/talos/commit/ac140324ebfb54f580c9b9bbbb55549bd5ffa11e) fix: skip PCR extension if TPM1.2 is found * [`09ef1f8a4`](https://github.com/siderolabs/talos/commit/09ef1f8a41c84e6a16729e6b6aff81788da0e3f5) fix: ignore http proxy on grpc socket dial * [`22a72dc80`](https://github.com/siderolabs/talos/commit/22a72dc80f2037a4cc7ad696d8dff504deb22630) chore: split options between three structs * [`22c34a50f`](https://github.com/siderolabs/talos/commit/22c34a50fc66edd174ab4a65961257de28a6daa0) fix(ci): provision cron jobs * [`b3b20eff3`](https://github.com/siderolabs/talos/commit/b3b20eff3a29f74d18df634cbb01f41bde17f2c8) fix: containerd crashing with sigsegv * [`f7891c301`](https://github.com/siderolabs/talos/commit/f7891c3018de248c7c66483562227b614689413c) chore: calculate vmnet interface name preemptively * [`ae87edffb`](https://github.com/siderolabs/talos/commit/ae87edffbcdaed12fef41541622f27882ed63755) fix: drop libseccomp from rootfs * [`f74a805bb`](https://github.com/siderolabs/talos/commit/f74a805bb067f55619cae7aebb92f00bb8173c92) fix: do correct backoff for nocloud reconcile * [`01bb294af`](https://github.com/siderolabs/talos/commit/01bb294af63f193dafa12cb623ea77ad67b698fb) fix(ci): provision tests * [`e4945be3b`](https://github.com/siderolabs/talos/commit/e4945be3bc43cbc275e2ea5f399a0188c5e16ad8) docs: add registryd debug command * [`d8c670ad3`](https://github.com/siderolabs/talos/commit/d8c670ad3ecba32c70ff365eaf7a5a4ccb5d721a) release(v1.11.0-alpha.0): prepare release * [`ace44ea61`](https://github.com/siderolabs/talos/commit/ace44ea6169d419f188e0a2456c31f420e61ae77) test: update hydrophone to 0.7.0 * [`3a1163692`](https://github.com/siderolabs/talos/commit/3a1163692da7b41b17f263ab43d0fd81abafc4f8) chore: cross platform qemu preflight checks * [`7914fb104`](https://github.com/siderolabs/talos/commit/7914fb10412d31a1b75c74b0c66578e55fb77bc7) chore: move the create command to it's own package * [`c8e619608`](https://github.com/siderolabs/talos/commit/c8e619608dc8898be71a17c54503085ef38abf37) chore: prepare for release 1.11 * [`1299aaa45`](https://github.com/siderolabs/talos/commit/1299aaa45d997dd23aed380f858cec3bc6b975e4) chore(ci): add extensions test for Youki runtime * [`e50ceb221`](https://github.com/siderolabs/talos/commit/e50ceb221e56f0760d5f2fc9e4b821d6b29add05) docs: activate Talos 1.10 docs * [`9d12aaeb1`](https://github.com/siderolabs/talos/commit/9d12aaeb19d68c5e692921b938d72347f6129f65) test: improve config patch test * [`106a656b6`](https://github.com/siderolabs/talos/commit/106a656b6132e766e9e9ef7b1c12b97a413b5de6) chore: make qemu provider build on darwin * [`8013aa06c`](https://github.com/siderolabs/talos/commit/8013aa06cd338f1dd11061d3455767fee4b9783c) test: replace platform metadata test * [`2b89c2810`](https://github.com/siderolabs/talos/commit/2b89c2810551ab52678e62fcbf5355dd05c72030) fix: relax etcd APIs RBAC requirements * [`1e677587c`](https://github.com/siderolabs/talos/commit/1e677587c0e6c61f724a85f18ee9d436ae6da038) fix: preserve kubelet image suffix * [`62ab8af45`](https://github.com/siderolabs/talos/commit/62ab8af459475cbd24a2f34d8923ce70d1fda3db) fix: disk image generation with image cache * [`d60626f01`](https://github.com/siderolabs/talos/commit/d60626f017ef495210939ee4f8ef7f623dd325f9) fix: handle encryption type mismatch * [`a9109ebd0`](https://github.com/siderolabs/talos/commit/a9109ebd00fcd300bf4262142ade77df6788852b) feat: allow SideroLink unique token in machine config * [`2ff3a6e40`](https://github.com/siderolabs/talos/commit/2ff3a6e4079a29b6b45770204fd8cb30369518e9) feat(kernel): add bcache kernel module to core talos * [`fa95a2146`](https://github.com/siderolabs/talos/commit/fa95a2146056bfe1ae322cb574fd8d432745b5c9) fix(ci): bios provision test * [`f7c5b86be`](https://github.com/siderolabs/talos/commit/f7c5b86be7e2b28906cb66b466a017887ac5e2b6) fix: sync PCR extension with volume provisioning lifecycle * [`f90c79474`](https://github.com/siderolabs/talos/commit/f90c79474b50da35ab8e285ee9723957e4b6cf00) chore: show bound driver in pcidevices info * [`8db34624c`](https://github.com/siderolabs/talos/commit/8db34624c6ed9707ba1165da790f5b389bd1c92f) fix: handle correctly changing platform network config * [`77c7a075b`](https://github.com/siderolabs/talos/commit/77c7a075bbba7ffd24dbd9d5e069ccb50f8143b4) feat: update Kubernetes to 1.33.0 * [`74f0c48c7`](https://github.com/siderolabs/talos/commit/74f0c48c738b0b80278667c3e5a1c5e1ecd5a078) feat: add version compatibility for Talos 1.11 * [`c4fb7dad0`](https://github.com/siderolabs/talos/commit/c4fb7dad0ec390781cca54e2348f116cb1cf1866) fix: force DNS runner shutdown on timeout * [`c49b4836e`](https://github.com/siderolabs/talos/commit/c49b4836e46725940f4731e182475905ebee6019) docs: hetzner: add note about public iso * [`16ea2b113`](https://github.com/siderolabs/talos/commit/16ea2b113fad0c81a96dbcfdf4fd1b9f43bb1282) docs: add what is new for 1.10 * [`be3f0c018`](https://github.com/siderolabs/talos/commit/be3f0c018c50da3d920ed8fe36d4f31c5d3edfac) fix: fix Gvisor tests with containerd patch * [`37db132b3`](https://github.com/siderolabs/talos/commit/37db132b3b3e6c58f15228c64b023e77c15cf012) chore(ci): add provision test with bios * [`ec60b70e7`](https://github.com/siderolabs/talos/commit/ec60b70e7245f49f6ac1d48cd4292b85f1d6f79e) fix: set media type to OCI for image cache layer * [`a471eb31b`](https://github.com/siderolabs/talos/commit/a471eb31b87b393ee9fc57fbc725801d08386ad4) feat: update Linux 6.12.24, containerd 2.0.5 * [`54ad5b872`](https://github.com/siderolabs/talos/commit/54ad5b8729c7d54da2efa6baf7886163741176ed) fix: extension services logging to console * [`601f036ba`](https://github.com/siderolabs/talos/commit/601f036ba9cc762d6a3c6ae819654005f1d49527) docs: correct flannel extra args example * [`ae94377d1`](https://github.com/siderolabs/talos/commit/ae94377d15a3b70248fbb446d13d7ae96bb04e82) feat: support encryption config for user volumes * [`9616f6e8d`](https://github.com/siderolabs/talos/commit/9616f6e8d280e64815fe3e1ba324df1dd5d2122d) docs: add caveat for kubespan and host ports * [`a1d08a362`](https://github.com/siderolabs/talos/commit/a1d08a3624c7c8b5213b8e9dee1cf9289d6719dc) docs: fixes typo at OpenEBS Mayastor worker patches * [`a91e8726e`](https://github.com/siderolabs/talos/commit/a91e8726e433be9db58f1a7a09a4cca422b2b50c) docs: add a dark theme * [`c76189c58`](https://github.com/siderolabs/talos/commit/c76189c58a2fe65954924168d7077350974829dd) fix: grub EFI mount point * [`4ca985c65`](https://github.com/siderolabs/talos/commit/4ca985c656c1924e550d06c073a7c1b6cb03f392) fix: grub efi platform install * [`b31260281`](https://github.com/siderolabs/talos/commit/b31260281dba752e06fcfc645bb020872602d898) docs: update storage.md * [`396a29040`](https://github.com/siderolabs/talos/commit/396a290408eff5bda4ad31fafc33496bea9aa899) feat: add new SBCs * [`a902f6580`](https://github.com/siderolabs/talos/commit/a902f6580f8e104977521a335a41c0cd70256906) feat: update Flannel to v0.26.7 * [`2bbefec1a`](https://github.com/siderolabs/talos/commit/2bbefec1abacae2952782fbd163ef52d34f09858) docs: use cache in preview * [`6028a8d2d`](https://github.com/siderolabs/talos/commit/6028a8d2da571a8a37712f9917e24372cf5af919) docs: update kubeprism.md * [`e51a8ef8c`](https://github.com/siderolabs/talos/commit/e51a8ef8c68bb1cfab2ac845a0b6792d7e000324) fix: prefer new `MountStatus` resource * [`d9c7e7946`](https://github.com/siderolabs/talos/commit/d9c7e79462496d6756c55b0672994aa262eaed4f) docs: fix search * [`b32fa029b`](https://github.com/siderolabs/talos/commit/b32fa029b3f550b3403e25e23aac889d61366389) feat: update Kubernetes to 1.33.0-rc.1 * [`f0ea478cb`](https://github.com/siderolabs/talos/commit/f0ea478cb811675a450839b8dcd351e43404efd4) feat: support address priority * [`8cd3c8dc7`](https://github.com/siderolabs/talos/commit/8cd3c8dc77b25270ed8dea65cbbd4e87c203ee74) test: fix NVIDIA OSS tests * [`62f2d27cd`](https://github.com/siderolabs/talos/commit/62f2d27cd44de5112055b5b47f23b001cadccaae) docs: update virtualbox.md * [`141326ea3`](https://github.com/siderolabs/talos/commit/141326ea3bb2e471a5cb51fd565521683a9792fc) docs: fix tabpane styling * [`134aa53cc`](https://github.com/siderolabs/talos/commit/134aa53ccaba55754544977d695ad3ca5d34e604) feat: update base CoreDNS code in host DNS to 1.12.1

### Changes since v1.11.0-alpha.1
33 commits

* [`1e5a008f5`](https://github.com/siderolabs/talos/commit/1e5a008f5740af9dd9297ec5616bde9fd102f21f) fix: hold user volume mount point across kubelet restarts * [`cdad50590`](https://github.com/siderolabs/talos/commit/cdad50590d4436eb12b959f2ff04457d5632f941) docs: user volumes and kubernetes upgrade updates * [`c880835c8`](https://github.com/siderolabs/talos/commit/c880835c809c2a02f0bb6d0450d15df042a50781) feat: implement zswap support * [`7f0300f10`](https://github.com/siderolabs/talos/commit/7f0300f108e7f2e9192214f87a13c8ff2ea25866) feat: update dependencies, Kubernetes 1.34.0-alpha.2 * [`61afbe3d2`](https://github.com/siderolabs/talos/commit/61afbe3d216862a9b9a5c8f521475a0f39cd710e) docs: add vc4 documentation * [`b9dbdc8e7`](https://github.com/siderolabs/talos/commit/b9dbdc8e7213c305e4de71516b990641e0fed706) fix: etcd recover with multiple advertised addresses * [`19d94c357`](https://github.com/siderolabs/talos/commit/19d94c3574b7b3ee3fbe21fdb56cff5a18e7b91e) feat: update Linux to 6.12.35, containerd to 2.1.3 * [`44a1fc3b7`](https://github.com/siderolabs/talos/commit/44a1fc3b78589540f5a0d9b8ea4d898474da3a80) fix: treat context canceled as expected error on image pull * [`4da2dd537`](https://github.com/siderolabs/talos/commit/4da2dd537d5dae884f47bd3f04ddcd05ac6cd222) feat: enforce Kubernetes version compatibility * [`6c7f8201a`](https://github.com/siderolabs/talos/commit/6c7f8201a9ceeec6ecfd0a35b308805ec149f3de) fix: set default MTU on Azure to 1400 * [`091cd6989`](https://github.com/siderolabs/talos/commit/091cd6989ce8c09885b3ae3e8c594c4770bd0748) docs: small yaml typo fix * [`66ecbd48f`](https://github.com/siderolabs/talos/commit/66ecbd48fdaf509bbb2b37327eb0e0891dd81910) docs: update support matrix with omni version * [`c948d7617`](https://github.com/siderolabs/talos/commit/c948d7617d1579c462a809b37956fc98270fcce4) docs: minor fixes for creating kernel modules * [`cc14c4a25`](https://github.com/siderolabs/talos/commit/cc14c4a25d355910a00e60c69ed641abbb7b40f6) docs: add docs for creating kernel modules * [`93bcd3b56`](https://github.com/siderolabs/talos/commit/93bcd3b5623d900a0f731c0f60d3ce0d69c9c32c) docs: create SBOM for Go dependencies * [`38c4ce415`](https://github.com/siderolabs/talos/commit/38c4ce415dc8535b4a7403f7a35c5440f2f4aeb6) feat: add user-space InfiniBand modules * [`251dc934f`](https://github.com/siderolabs/talos/commit/251dc934f3f4d9d81d6d11fd66cf4e52517d9878) feat: arm64 support for platform vmware * [`09b3ad577`](https://github.com/siderolabs/talos/commit/09b3ad5771b4ee813dcb4d53ad8d291b74b8d8fa) feat: update containerd to 2.1.2 * [`0767dd07b`](https://github.com/siderolabs/talos/commit/0767dd07b9067aeb3470d463ff32874c69082853) chore: enable --with-siderolink-agent on Darwin * [`9642198d7`](https://github.com/siderolabs/talos/commit/9642198d76963bd9f6bdda03fb31c165f31f8087) fix: userspace wireguard library overrides * [`208f0763e`](https://github.com/siderolabs/talos/commit/208f0763ef2db94a913606051b5d223d1de61f24) chore: fix talosctl build on non-Linux hosts * [`87421af87`](https://github.com/siderolabs/talos/commit/87421af87a88851b78e576b2f9b4af9a48f0acb8) docs: expand documentation description * [`d32ccfa59`](https://github.com/siderolabs/talos/commit/d32ccfa598284450477af166734595dc952021fa) feat: implement swap support * [`8f5cf81db`](https://github.com/siderolabs/talos/commit/8f5cf81dba80015f66037ee181f17eb2294bb8a2) docs: update kvm documentation * [`8e84c8b0f`](https://github.com/siderolabs/talos/commit/8e84c8b0f8405be519a9f0530e34a612ff054373) fix: nil pointer deref in quirk * [`6e74a3676`](https://github.com/siderolabs/talos/commit/6e74a367636dc21e2bf017d6284bbf998a4bad7d) docs: aad ery basic details on how to run on scaleway * [`260d1bc9a`](https://github.com/siderolabs/talos/commit/260d1bc9a93f5f6added5e6998f3d2f08fedb770) fix: correctl close encrypted volumes * [`034ef42af`](https://github.com/siderolabs/talos/commit/034ef42af25ee3dacf5dd0391385ea881b6d5d32) fix: update siderolink library for wgtunnel panic fix * [`3035744a8`](https://github.com/siderolabs/talos/commit/3035744a8096270691f6bdccfabe34ad53da489c) fix: correctly predict interface name on darwin * [`cfcfad3c4`](https://github.com/siderolabs/talos/commit/cfcfad3c45376b8ebb989b865f3c13729c87d388) chore: move `checkUnknownKeys` function to `github.com/siderolabs/gen` * [`5ecc53c69`](https://github.com/siderolabs/talos/commit/5ecc53c695ec578dbc32f00fa7df65b31a5e77aa) docs: add macos section to developing-talos.md * [`b5b35307f`](https://github.com/siderolabs/talos/commit/b5b35307fe950d0de9ee2ff1d5686af858db13b4) chore: update Go to 1.24.4 * [`fde772d8d`](https://github.com/siderolabs/talos/commit/fde772d8d82e9d6bc7e63b49c965b8d924e308ab) feat: update Flannel to 0.27.0

### Changes from siderolabs/crypto
2 commits

* [`17107ae`](https://github.com/siderolabs/crypto/commit/17107ae45403a2bcd4fecfb4660b60276652b00d) fix: add generic CSR generator and OpenSSL interop * [`53659fc`](https://github.com/siderolabs/crypto/commit/53659fc35f6abd4ada7ffa22ef1b148cf93c0f28) refactor: split into files

### Changes from siderolabs/gen
4 commits

* [`dcb2b74`](https://github.com/siderolabs/gen/commit/dcb2b7417879f230a569ce834dad5c89bd09d6bf) feat: add `panicsafe` package * [`b36ee43`](https://github.com/siderolabs/gen/commit/b36ee43f667a7a56b340a3e769868ff2a609bb5b) feat: make `xyaml.CheckUnknownKeys` public * [`3e319e7`](https://github.com/siderolabs/gen/commit/3e319e7e52c5a74d1730be8e47952b3d16d91148) feat: implement `xyaml.UnmarshalStrict` * [`7c0324f`](https://github.com/siderolabs/gen/commit/7c0324fee9a7cfbdd117f43702fa273689f0db97) chore: future-proof HashTrieMap

### Changes from siderolabs/go-circular
1 commit

* [`5b39ef8`](https://github.com/siderolabs/go-circular/commit/5b39ef87df04efeaa47fe6374a8114f39c126122) fix: do not log error if chunk zero was never written

### Changes from siderolabs/go-kubernetes
3 commits

* [`657a74b`](https://github.com/siderolabs/go-kubernetes/commit/657a74b7163de7886a9581c446b1de6f21264fd2) feat: prepare for Kubernetes 1.34 * [`9070be4`](https://github.com/siderolabs/go-kubernetes/commit/9070be4308e23d969ec4fc49b25dab4a27d512e7) fix: remove DynamicResourceAllocation feature gate * [`8cb588b`](https://github.com/siderolabs/go-kubernetes/commit/8cb588bc4c93d812de901a6a33e599ba2169cd96) fix: k8s 1.32->1.33 upgrade check

### Changes from siderolabs/pkgs
41 commits

* [`03bb94c`](https://github.com/siderolabs/pkgs/commit/03bb94c39c02b7028f5d595cb758f59b132fa1d3) feat: update dependencies * [`c613abd`](https://github.com/siderolabs/pkgs/commit/c613abd8c4f777ef588cce4ae5563d4024e50507) fix: iptables url * [`fae59df`](https://github.com/siderolabs/pkgs/commit/fae59df236da122c84990a187f4648878f2e4bf7) fix: download and copy hailo8 firmware * [`fadf1e2`](https://github.com/siderolabs/pkgs/commit/fadf1e22a263b3429fa8fd540b4ff5a71ce8ded2) feat: update containerd to 2.1.2 * [`a0b0da1`](https://github.com/siderolabs/pkgs/commit/a0b0da10b5745616651d0bcd4b3aa5a06690fd5a) feat: enable io.latency cgroup controller * [`0aaa07a`](https://github.com/siderolabs/pkgs/commit/0aaa07a2a1af852efbc65a476cdcc17829e33a99) feat: add hailort package * [`8555e94`](https://github.com/siderolabs/pkgs/commit/8555e94f1ed54210ae7768e8ef977e5baec4b2cb) chore: use ftpmirror for GNU sources * [`9fbe2b4`](https://github.com/siderolabs/pkgs/commit/9fbe2b43874b701e04e5817f8a9d485139e96d50) feat: update Go to 1.24.4 * [`79bfa9e`](https://github.com/siderolabs/pkgs/commit/79bfa9e06e5e69955236ffd58323c9936d638d45) feat: update NVIDIA drivers to 570.148.08 * [`c8b8bd8`](https://github.com/siderolabs/pkgs/commit/c8b8bd8b5eb265f8e8c8955998e428b86d177ab5) feat: bump dependencies * [`54bf03e`](https://github.com/siderolabs/pkgs/commit/54bf03ebf24d9ef70a47d4b3b4f30d92191085da) feat: update Linux to 6.12.31 * [`93b3aaa`](https://github.com/siderolabs/pkgs/commit/93b3aaae5369140058e6a5cbdf83d1da235eb735) feat: add patch for CephFS IMA performance regression * [`ebd6627`](https://github.com/siderolabs/pkgs/commit/ebd6627c68406076ed95b2cd629d2ace51bb49b6) feat: disable IMA support * [`8aad53b`](https://github.com/siderolabs/pkgs/commit/8aad53bab3201d7f87d39ab61953e04392402efc) feat: add CONFIG_NFT_CONNLIMIT to kernel * [`7a299fa`](https://github.com/siderolabs/pkgs/commit/7a299fa02106a7216926d6bcff21fb1cd2da7d73) feat: update Linux to 6.12.30 * [`8c4603e`](https://github.com/siderolabs/pkgs/commit/8c4603e90335b9aaf180b954ebc43f65dcb2b7b6) feat: move more configs to modules on arm64 * [`7b1183b`](https://github.com/siderolabs/pkgs/commit/7b1183bea84e46cd8f1a775f95683b8a0039c2d7) feat(kernel): enable IB user-space management and RDMA * [`1b1430e`](https://github.com/siderolabs/pkgs/commit/1b1430e82ef62efdd538588183ed27def2bebbaa) fix: drop pcre2 binaries * [`487610c`](https://github.com/siderolabs/pkgs/commit/487610c4f286210c22cd813427380af654297791) fix: drop broken symlinks * [`f31d518`](https://github.com/siderolabs/pkgs/commit/f31d518eefec0cb672760d00a5c2de37b45dfb45) fix: clean up some binaries * [`0f74b9b`](https://github.com/siderolabs/pkgs/commit/0f74b9bd1d097a283f3edd6165161e4e0688a79f) feat: update containerd to v2.1.1 * [`89b4037`](https://github.com/siderolabs/pkgs/commit/89b40372b8964a9dc9ad3db17a46a9d9c797f60f) fix: tenstorrent pkg name * [`a14b544`](https://github.com/siderolabs/pkgs/commit/a14b54409704c1f3beb0f51089dadd3f3e8dc441) chore: drop qemu-tools vmdk support * [`2563e47`](https://github.com/siderolabs/pkgs/commit/2563e47ca1bfc755ee4ecf2b470cfed081b54e6f) feat: add tenstorrent package * [`2a1c42f`](https://github.com/siderolabs/pkgs/commit/2a1c42fde5fe4009c33d50d571d7d3cfe3a09888) fix(renovate): flannel config * [`bfa69a8`](https://github.com/siderolabs/pkgs/commit/bfa69a820e8190aed3a45c00dff5f4f1cc42b7a6) feat: add open-vmdk package * [`9f1ba1f`](https://github.com/siderolabs/pkgs/commit/9f1ba1f047c835abdf882540d316055a3e2d1bfc) fix: bring back updated containerd gvisor patch * [`1567cb6`](https://github.com/siderolabs/pkgs/commit/1567cb616691dc22fbc3374cdeac11cdbe51bb94) feat: update Linux 6.12.28, firmware * [`9bc66e6`](https://github.com/siderolabs/pkgs/commit/9bc66e6bd355f8a86c4becbd78aede1323e3681e) feat: update containerd to 2.1.0 * [`c6b54e0`](https://github.com/siderolabs/pkgs/commit/c6b54e04fb5d943ff31f05b1e095af65eb901604) feat: enable zswap * [`4cd7084`](https://github.com/siderolabs/pkgs/commit/4cd7084634c2b79541da8c6f95c047d4eb0e66a2) feat: update dependencies * [`a3fcbf8`](https://github.com/siderolabs/pkgs/commit/a3fcbf812632aaa8e8f9027a88181c284e7d919d) feat(kernel): enable panthor driver * [`74d1665`](https://github.com/siderolabs/pkgs/commit/74d16657fd53c30249c3eba75769f90dd84366ce) feat: update ZFS to 2.3.2 * [`ddc866b`](https://github.com/siderolabs/pkgs/commit/ddc866bc9dd0557c2e9d5d0b234348767769cfd3) feat: update Linux to 6.12.27 * [`a347857`](https://github.com/siderolabs/pkgs/commit/a347857b33a6a41fe2661a7451c3af65a51404c9) fix: build containerd with Go 1.23 * [`74da85c`](https://github.com/siderolabs/pkgs/commit/74da85c2cf61b8006af38b3d0d38dc13098d5227) fix: containerd build doesn't need seccomp * [`4effa05`](https://github.com/siderolabs/pkgs/commit/4effa0525dc87974052e9dec2685a0ad411773dd) fix: downgrade libseccomp to 2.5.5 * [`9cea00b`](https://github.com/siderolabs/pkgs/commit/9cea00b4601d7bedf49606b647003f3c6cb0787b) feat: update Linux to 6.12.25 * [`cb108a5`](https://github.com/siderolabs/pkgs/commit/cb108a514b55a302008fb4c1ce6d88ce0d769b58) feat(kernel): enable bcache module * [`d042432`](https://github.com/siderolabs/pkgs/commit/d04243270a4f10f9ecb889883ab42687e5ae6351) fix: backport sandbox fix for Gvisor * [`fa625dc`](https://github.com/siderolabs/pkgs/commit/fa625dc6dd97a61cb8479b8b0ab82126650de11b) feat: update Linux 6.12.24, containerd 2.0.5

### Changes from siderolabs/siderolink
3 commits

* [`5f46f65`](https://github.com/siderolabs/siderolink/commit/5f46f6583b9d03f91c9bb5f637149fe466d17bfc) feat: handle panics in goroutines * [`d09ff45`](https://github.com/siderolabs/siderolink/commit/d09ff45b450a37aa84652fa70b5cd3467ee8243d) fix: race in wait value * [`d2a79e0`](https://github.com/siderolabs/siderolink/commit/d2a79e0263806b68ff0a44ea9efa58b83fb269ec) fix: clean up device on failure

### Changes from siderolabs/tools
4 commits

* [`1dfd14b`](https://github.com/siderolabs/tools/commit/1dfd14bd4f2573d1070008c8f9d6a05ca064081e) feat: update Go to 1.24.4 * [`af3fd64`](https://github.com/siderolabs/tools/commit/af3fd645d48a373396f8346af411c1c827c87376) feat: update dependencies * [`e35234b`](https://github.com/siderolabs/tools/commit/e35234bd94c3c16daf06d00848d7752f5e4c7d15) feat: update dependencies * [`c96a4e6`](https://github.com/siderolabs/tools/commit/c96a4e671e378f80f161e45942f80b10adfd562d) chore: update toolchain to the latest version

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.6.0 -> v0.7.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.9.0 -> v1.10.1 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.3.1 -> v1.4.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.3.1 -> v1.4.0 * **github.com/aws/aws-sdk-go-v2/config** v1.29.14 -> v1.29.17 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.30 -> v1.16.32 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.38.3 -> v1.41.2 * **github.com/aws/smithy-go** v1.22.3 -> v1.22.4 * **github.com/containerd/containerd/api** v1.8.0 -> v1.9.0 * **github.com/containerd/containerd/v2** v2.0.5 -> v2.1.3 * **github.com/containernetworking/plugins** v1.6.2 -> v1.7.1 * **github.com/cosi-project/runtime** v0.10.2 -> v0.10.6 * **github.com/detailyang/go-fallocate** 432fa640bd2e **_new_** * **github.com/docker/cli** v28.0.4 -> v28.3.0 * **github.com/docker/docker** v28.0.4 -> v28.3.0 * **github.com/equinix-ms/go-vmw-guestrpc** v0.1.1 **_new_** * **github.com/foxboron/go-uefi** 69fb7dba244f -> a3183a1bfc84 * **github.com/google/cadvisor** v0.52.1 -> v0.53.0 * **github.com/google/cel-go** v0.24.1 -> v0.25.0 * **github.com/google/go-containerregistry** v0.20.3 -> v0.20.6 * **github.com/google/go-tpm** v0.9.3 -> v0.9.5 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.3.1 -> v2.3.2 * **github.com/hetznercloud/hcloud-go/v2** v2.21.0 -> v2.21.1 * **github.com/jsimonetti/rtnetlink/v2** v2.0.3 -> v2.0.5 * **github.com/klauspost/cpuid/v2** v2.2.10 -> v2.2.11 * **github.com/linode/go-metadata** v0.2.1 -> v0.2.2 * **github.com/miekg/dns** v1.1.65 -> v1.1.66 * **github.com/pkg/xattr** v0.4.10 -> v0.4.11 * **github.com/prometheus/procfs** v0.16.0 -> v0.16.1 * **github.com/rivo/tview** 949945f8d922 -> a4a78f1e05cb * **github.com/safchain/ethtool** v0.5.10 -> v0.6.1 * **github.com/siderolabs/crypto** v0.5.1 -> v0.6.0 * **github.com/siderolabs/gen** v0.8.0 -> v0.8.4 * **github.com/siderolabs/go-blockdevice/v2** v2.0.16 -> v2.0.18 * **github.com/siderolabs/go-circular** v0.2.2 -> v0.2.3 * **github.com/siderolabs/go-kubernetes** v0.2.21 -> v0.2.24 * **github.com/siderolabs/pkgs** v1.10.0-5-g48dba3e -> v1.11.0-alpha.0-40-g03bb94c * **github.com/siderolabs/siderolink** v0.3.13 -> v0.3.15 * **github.com/siderolabs/talos/pkg/machinery** v1.10.0 -> v1.11.0-alpha.1 * **github.com/siderolabs/tools** v1.10.0 -> v1.11.0-alpha.0-3-g1dfd14b * **go.etcd.io/etcd/api/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/client/v3** v3.5.21 -> v3.6.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.21 -> v3.6.1 * **golang.org/x/net** v0.39.0 -> v0.41.0 * **golang.org/x/oauth2** v0.29.0 -> v0.30.0 * **golang.org/x/sync** v0.13.0 -> v0.15.0 * **golang.org/x/sys** v0.32.0 -> v0.33.0 * **golang.org/x/term** v0.31.0 -> v0.32.0 * **golang.org/x/text** v0.24.0 -> v0.26.0 * **golang.org/x/time** v0.11.0 -> v0.12.0 * **google.golang.org/grpc** v1.71.1 -> v1.73.0 * **k8s.io/api** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/apimachinery** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/apiserver** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/client-go** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/component-base** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/cri-api** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kube-scheduler** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kubectl** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/kubelet** v0.33.0 -> v0.34.0-alpha.2 * **k8s.io/pod-security-admission** v0.33.0 -> v0.34.0-alpha.2 * **sigs.k8s.io/hydrophone** b92baf7e0b04 -> v0.7.0 * **sigs.k8s.io/yaml** v1.4.0 -> v1.5.0 Previous release can be found at [v1.10.0](https://github.com/siderolabs/talos/releases/tag/v1.10.0) ## [Talos 1.11.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.11.0-alpha.1) (2025-06-05) Welcome to the v1.11.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### IMA support removed Talos now drops the IMA (Integrity Measurement Architecture) support. This feature was not used in Talos for any meaningful security purpose and has historically caused performance issues. See #11133 for more details. ### Qemu provisioner on MacOS On MacOS `talosctl cluster create` command now supports the Qemu provisioner in addition to the Docker provisioner. ### Component Updates Linux: 6.12.31 Kubernetes: 1.33.1 runc: 1.3.0 containerd: 2.1.1 Flannel CNI plugin: 1.7.1-flannel1 Talos is built with Go 1.24.3. ### Contributors * Andrey Smirnov * Noel Georgi * Orzelius * Orzelius * Spencer Smith * Till Hoffmann * Justin Garrison * Steve Francis * Andrew Longwill * Dmitrii Sharshakov * Marat Bakeev * Olav Thoresen * Utku Ozdemir * Alvaro "Chamo" Linares Cabre * Brian Brookman * Bryan Mora * Clément Nussbaumer * Dennis Marttinen * Dmitriy Matrenichev * Joakim Nohlgård * Justin Seely * Luke Cousins * Marco Mihai Condrache * Markus Reiter * Michael Moerz * Mike * Tan Siewert * Thibault VINCENT * Tom Keur * killcity * yashutanu ### Changes
134 commits

* [`58a868e68`](https://github.com/siderolabs/talos/commit/58a868e68833e94d691e7ed029dce629446fecc3) chore: fix renovate config, add release-gate label * [`a59aaee84`](https://github.com/siderolabs/talos/commit/a59aaee84bcceb20792bc4782748449ad93b0530) feat: bump dependencies, Linux 6.12.31 * [`e954ee30a`](https://github.com/siderolabs/talos/commit/e954ee30add42de6f42cbb7d96927722102afdb7) docs: typo correction: LongHorn -> Longhorn * [`aab053394`](https://github.com/siderolabs/talos/commit/aab053394bafdf718196133e38be010d847db0ad) fix: mashal resource byte slices as strings in YAML * [`c7d4191e7`](https://github.com/siderolabs/talos/commit/c7d4191e78bf0a455ab596f46d4cf212dce694a4) fix: rework the way CRI config generation is waited for * [`0114183de`](https://github.com/siderolabs/talos/commit/0114183de62e4ab930ff0f10dd156f935d57cf10) docs: update `lastRelease` to 1.10.3 * [`938b0760a`](https://github.com/siderolabs/talos/commit/938b0760abdb41be1be4da02b877e2c902d594be) docs: update issue template * [`2a7b735b2`](https://github.com/siderolabs/talos/commit/2a7b735b264ebcfa22dc2d6044c9d5cd3057b5c2) feat: drop IMA support * [`2d5a805b0`](https://github.com/siderolabs/talos/commit/2d5a805b0ebabb804b3c32be18db1d718a91070f) fix: typo in DiscoverdVolume spec * [`60c12bad9`](https://github.com/siderolabs/talos/commit/60c12bad93b422db2784b0203d94ca69fa31957c) feat: support nocloud include url userdata directive * [`0fd622c82`](https://github.com/siderolabs/talos/commit/0fd622c825ba1fbb833a4b8920ac4c4e56f08a1f) fix(talosctl): correct --help output for dashboard command * [`a90c936a1`](https://github.com/siderolabs/talos/commit/a90c936a16756cfe5fe451258f0022b808be17d2) feat: support qemu provisioner on darwin * [`5322ca0d3`](https://github.com/siderolabs/talos/commit/5322ca0d372aa20ad90e66f04699b75debb0ab80) docs: update overlay docs * [`a60b6322d`](https://github.com/siderolabs/talos/commit/a60b6322d1e8fbd75394e0bdb4435af605b32bbb) fix(ci): drop nebula from extensions test * [`dbbb59a67`](https://github.com/siderolabs/talos/commit/dbbb59a6781f79ee34a6e91a72575802561c58b6) docs: add note for default `dataDirHostPath` for Rook * [`e26054378`](https://github.com/siderolabs/talos/commit/e2605437826911cd60a6a4d9ee760a6a242e244b) docs: macos qemu provider * [`5d0224093`](https://github.com/siderolabs/talos/commit/5d022409357d41831fa1bfd34ccdcfceecca42df) docs: use the cilium-cli image repo in the job installation manifest * [`ff80e4cca`](https://github.com/siderolabs/talos/commit/ff80e4cca086fa01d84ceb750111dc9e31ccc978) docs: fix CIDR name * [`a5fd15e8b`](https://github.com/siderolabs/talos/commit/a5fd15e8bd4a4547e3658981543401fd9eb8cd80) fix(ci): reproducibility test * [`8f8963e50`](https://github.com/siderolabs/talos/commit/8f8963e50d7b05d1361fd44040c0f1ffb94693af) docs: update Nexxen brand * [`c6b86872d`](https://github.com/siderolabs/talos/commit/c6b86872dc0d62aef5ad70fce00c411080911ace) fix(ci): iso reproducibility file permissions * [`995a1dec4`](https://github.com/siderolabs/talos/commit/995a1dec4a34f49d84daff16b30f8920275a439d) chore: add a check for unsupported darwin flags * [`9db5d0c97`](https://github.com/siderolabs/talos/commit/9db5d0c97ac31c7f6ce0b23d999126fc6cc094ec) fix: nocloud metadata for hostname * [`3cf325654`](https://github.com/siderolabs/talos/commit/3cf325654e4a7f73196241e59e3ca6b5f24c3e19) feat: modularize more arm64 kernel * [`3524745cc`](https://github.com/siderolabs/talos/commit/3524745cc49c51e4f13da954a57ab56d467fd26e) fix: allow any PKI in Talos API * [`f438cdb09`](https://github.com/siderolabs/talos/commit/f438cdb0993b17f0e540ecefa39cde09f89730f4) chore: use custom dhcpd server on macos qemu * [`11c17fb9a`](https://github.com/siderolabs/talos/commit/11c17fb9aad2443b10e15295069b8e24e0d514e2) fix: metal-iso reproducibility * [`7fcb89ee3`](https://github.com/siderolabs/talos/commit/7fcb89ee385fdbf47dae4a8308299c00488df84a) chore: add darwin vmnet qemu support * [`fc1237343`](https://github.com/siderolabs/talos/commit/fc1237343f79a1be907c43ac3ce116168409ed17) chore: clean up `/usr/bin` * [`b551f32ce`](https://github.com/siderolabs/talos/commit/b551f32ce550f2bc3c679a9857f28d604a297bbf) feat: update containerd to v2.1.1 * [`67f4154f9`](https://github.com/siderolabs/talos/commit/67f4154f920fc0c58a9a832e14fbc7f9430747b3) docs: update disk-management.md * [`0cb137ad7`](https://github.com/siderolabs/talos/commit/0cb137ad7366e2386f49a99aee0a3c5ffb7223f6) fix: make disk size check work on old Talos * [`7c057edd5`](https://github.com/siderolabs/talos/commit/7c057edd5f3636dff6932ad9fbd7c51867b0c2c8) fix: use vmdk-convert istead of qemu-img to create VMDK for OVA files * [`cd618dad0`](https://github.com/siderolabs/talos/commit/cd618dad0feb1390e5945e2bba1d20bcecf30c2a) chore: update the go-blockdevice package * [`0b99631a0`](https://github.com/siderolabs/talos/commit/0b99631a0b64ce8d65ddcf7f40b2168debf11a62) fix: bump apid memory limit * [`5451f35b1`](https://github.com/siderolabs/talos/commit/5451f35b148a630c6ab011dce44b52fd2ad327ba) docs: update virtualbox * [`bd4d202a5`](https://github.com/siderolabs/talos/commit/bd4d202a5a67c56b6c6e6bc962f6bd51c729759f) refactor: bring owned.State from COSI to simplify tests * [`0b96df574`](https://github.com/siderolabs/talos/commit/0b96df57476af86a37bcfdbf28a479444a9e6e5c) feat: update containerd to 2.1.0 * [`e1a939144`](https://github.com/siderolabs/talos/commit/e1a939144f25acc6a2715feedb30a56a47f6793d) docs: fix formatting in disk encryption * [`7a817df1c`](https://github.com/siderolabs/talos/commit/7a817df1cce58de2a16b72b37a54ffc0103af79a) docs: fix typo * [`f35b213b2`](https://github.com/siderolabs/talos/commit/f35b213b2b448c2e0065d4698095a843dd2f5268) test: fix DHCP unicast failures in QEMU environment * [`7064bbf05`](https://github.com/siderolabs/talos/commit/7064bbf056f083de0f7174c9d3c600871189b4e5) docs: fix vmware factory URL * [`78c33bcdb`](https://github.com/siderolabs/talos/commit/78c33bcdb9a30195ce401311e82b2e189faf33f3) feat: update default Kubernetes to v1.33.1 * [`da6795266`](https://github.com/siderolabs/talos/commit/da67952666d2db2b8b5636bd4cae8af09a139410) fix: disable automatic MAC assignment to bridge interfaces * [`ca34adf58`](https://github.com/siderolabs/talos/commit/ca34adf585bfe04d2d1b84f186cb87aa77fc8e00) chore(ci): drop azure keys * [`ea5de19fa`](https://github.com/siderolabs/talos/commit/ea5de19fad3f62889899c0d89d08b8b73dfa75da) fix: selinux detection * [`52c76ea3a`](https://github.com/siderolabs/talos/commit/52c76ea3a61a4a3cbd963dc2ff0d6d21b4210bcd) fix: consistently apply dynamic grpc proxy dialer * [`aa9569e5d`](https://github.com/siderolabs/talos/commit/aa9569e5d8c59b762dfd64a4e9ef42cfdc6f9d51) chore: refactor cluster create cmd flags * [`1161faa05`](https://github.com/siderolabs/talos/commit/1161faa0594c033bf032852b880439b2082c9722) docs: fix typo in Cilium docs * [`164745e44`](https://github.com/siderolabs/talos/commit/164745e44334146b8a6f696640692c25b731414a) docs: remove `preserve` flag mention in upgrade notes * [`9a2ecbaaf`](https://github.com/siderolabs/talos/commit/9a2ecbaaf7b7a3f393dd29272aca34e069a24c6e) fix: makefile operating system param * [`118aa69d6`](https://github.com/siderolabs/talos/commit/118aa69d6f6e71b88747db1e8234d478daa54ab4) chore: update cloud-image-uploader dependencies * [`acdd721cf`](https://github.com/siderolabs/talos/commit/acdd721cfa62f9888a9ceea1693c17348c0d663a) chore: dump qemu pachine ipam records on darwin * [`bb9094534`](https://github.com/siderolabs/talos/commit/bb90945344f02b9cdae6e0e01821792dca25096b) chore: rotate aws iam credentials * [`0bfa4ae1b`](https://github.com/siderolabs/talos/commit/0bfa4ae1b06e1e6330adf331e1a97651bbe39b4a) chore: update deps for cloud-image-uploader * [`956d7c71b`](https://github.com/siderolabs/talos/commit/956d7c71bcdff639b8261cf6cf1a5d19cf702f75) chore: update sops keys * [`e2f819d88`](https://github.com/siderolabs/talos/commit/e2f819d880373102f8a8c7f0ff549e37ba75a08e) test: fix the process runner log collection * [`fdac4cfb9`](https://github.com/siderolabs/talos/commit/fdac4cfb9143853eb21d38e1b3d517455b0ba0f2) fix: upgrade go-kubernetes for DRA flag bug * [`09d88e1e8`](https://github.com/siderolabs/talos/commit/09d88e1e8374ef19e5730994d9b098333347f0b7) test: fix some flaky tests * [`ec1f41a94`](https://github.com/siderolabs/talos/commit/ec1f41a948b1bda02096434e47f2a2a767951fe9) chore: make qemu config server bind work on darwin * [`980f4d2b9`](https://github.com/siderolabs/talos/commit/980f4d2b936cfdc3ebc9882f7c25fbf2d2aa49f8) feat: bump dependencies * [`95259337e`](https://github.com/siderolabs/talos/commit/95259337ee0ccb22d7e9125074818ac8f9afa7af) fix: k8s 1.32->1.33 upgrade check * [`c3c326b40`](https://github.com/siderolabs/talos/commit/c3c326b405804c258b68f19b8d7dacca32535e9b) fix: improve volume mounter automaton * [`918b94d9a`](https://github.com/siderolabs/talos/commit/918b94d9a0b71b759073f8f7eb0f5dc7fdff413f) refactor: rewrite disk size check * [`ab7e693d7`](https://github.com/siderolabs/talos/commit/ab7e693d76500b6cdc2068221bdfce16633a8b01) chore: make qemu lb address bind work on darwin * [`97ceab001`](https://github.com/siderolabs/talos/commit/97ceab001c1bb79407c40d8fff867342656187b9) fix: multiple logic issues in platform network config controller * [`46349a9df`](https://github.com/siderolabs/talos/commit/46349a9df5d026a4e4b807a94865d5b3c371d32a) docs: remove azure image gallery instructions * [`0cfcdd3de`](https://github.com/siderolabs/talos/commit/0cfcdd3de1a20690ce47d63bb56b3d33d11c1474) docs: fix search on base talos.dev * [`78646b4e0`](https://github.com/siderolabs/talos/commit/78646b4e050358b930d27e4eddcfb22c4c825b0c) docs: add registryd debug command * [`c6824c211`](https://github.com/siderolabs/talos/commit/c6824c211438a3fb663f4233e8663732ab2ddf44) fix: deny apply config requests without v1alpha1 in "normal" mode * [`7df0408e4`](https://github.com/siderolabs/talos/commit/7df0408e460ebc392c6927c7b23e3795b9bd2140) fix: interactive installer config gen * [`881c5d62b`](https://github.com/siderolabs/talos/commit/881c5d62bf0d1f3311b3cf946b7801f97c1fb94b) fix: suppress duplicate platform config updates * [`66d77888e`](https://github.com/siderolabs/talos/commit/66d77888e42798995ddc73db3869d16959e53376) fix: replace downloaded asset paths correctly in cluster create cmd * [`6bd6c9b5a`](https://github.com/siderolabs/talos/commit/6bd6c9b5a08ca3b0e9574e1a61edc54c6ff722bb) fix: generate iso greater than 4 gig * [`ac140324e`](https://github.com/siderolabs/talos/commit/ac140324ebfb54f580c9b9bbbb55549bd5ffa11e) fix: skip PCR extension if TPM1.2 is found * [`09ef1f8a4`](https://github.com/siderolabs/talos/commit/09ef1f8a41c84e6a16729e6b6aff81788da0e3f5) fix: ignore http proxy on grpc socket dial * [`22a72dc80`](https://github.com/siderolabs/talos/commit/22a72dc80f2037a4cc7ad696d8dff504deb22630) chore: split options between three structs * [`22c34a50f`](https://github.com/siderolabs/talos/commit/22c34a50fc66edd174ab4a65961257de28a6daa0) fix(ci): provision cron jobs * [`b3b20eff3`](https://github.com/siderolabs/talos/commit/b3b20eff3a29f74d18df634cbb01f41bde17f2c8) fix: containerd crashing with sigsegv * [`f7891c301`](https://github.com/siderolabs/talos/commit/f7891c3018de248c7c66483562227b614689413c) chore: calculate vmnet interface name preemptively * [`ae87edffb`](https://github.com/siderolabs/talos/commit/ae87edffbcdaed12fef41541622f27882ed63755) fix: drop libseccomp from rootfs * [`f74a805bb`](https://github.com/siderolabs/talos/commit/f74a805bb067f55619cae7aebb92f00bb8173c92) fix: do correct backoff for nocloud reconcile * [`01bb294af`](https://github.com/siderolabs/talos/commit/01bb294af63f193dafa12cb623ea77ad67b698fb) fix(ci): provision tests * [`e4945be3b`](https://github.com/siderolabs/talos/commit/e4945be3bc43cbc275e2ea5f399a0188c5e16ad8) docs: add registryd debug command * [`d8c670ad3`](https://github.com/siderolabs/talos/commit/d8c670ad3ecba32c70ff365eaf7a5a4ccb5d721a) release(v1.11.0-alpha.0): prepare release * [`ace44ea61`](https://github.com/siderolabs/talos/commit/ace44ea6169d419f188e0a2456c31f420e61ae77) test: update hydrophone to 0.7.0 * [`3a1163692`](https://github.com/siderolabs/talos/commit/3a1163692da7b41b17f263ab43d0fd81abafc4f8) chore: cross platform qemu preflight checks * [`7914fb104`](https://github.com/siderolabs/talos/commit/7914fb10412d31a1b75c74b0c66578e55fb77bc7) chore: move the create command to it's own package * [`c8e619608`](https://github.com/siderolabs/talos/commit/c8e619608dc8898be71a17c54503085ef38abf37) chore: prepare for release 1.11 * [`1299aaa45`](https://github.com/siderolabs/talos/commit/1299aaa45d997dd23aed380f858cec3bc6b975e4) chore(ci): add extensions test for Youki runtime * [`e50ceb221`](https://github.com/siderolabs/talos/commit/e50ceb221e56f0760d5f2fc9e4b821d6b29add05) docs: activate Talos 1.10 docs * [`9d12aaeb1`](https://github.com/siderolabs/talos/commit/9d12aaeb19d68c5e692921b938d72347f6129f65) test: improve config patch test * [`106a656b6`](https://github.com/siderolabs/talos/commit/106a656b6132e766e9e9ef7b1c12b97a413b5de6) chore: make qemu provider build on darwin * [`8013aa06c`](https://github.com/siderolabs/talos/commit/8013aa06cd338f1dd11061d3455767fee4b9783c) test: replace platform metadata test * [`2b89c2810`](https://github.com/siderolabs/talos/commit/2b89c2810551ab52678e62fcbf5355dd05c72030) fix: relax etcd APIs RBAC requirements * [`1e677587c`](https://github.com/siderolabs/talos/commit/1e677587c0e6c61f724a85f18ee9d436ae6da038) fix: preserve kubelet image suffix * [`62ab8af45`](https://github.com/siderolabs/talos/commit/62ab8af459475cbd24a2f34d8923ce70d1fda3db) fix: disk image generation with image cache * [`d60626f01`](https://github.com/siderolabs/talos/commit/d60626f017ef495210939ee4f8ef7f623dd325f9) fix: handle encryption type mismatch * [`a9109ebd0`](https://github.com/siderolabs/talos/commit/a9109ebd00fcd300bf4262142ade77df6788852b) feat: allow SideroLink unique token in machine config * [`2ff3a6e40`](https://github.com/siderolabs/talos/commit/2ff3a6e4079a29b6b45770204fd8cb30369518e9) feat(kernel): add bcache kernel module to core talos * [`fa95a2146`](https://github.com/siderolabs/talos/commit/fa95a2146056bfe1ae322cb574fd8d432745b5c9) fix(ci): bios provision test * [`f7c5b86be`](https://github.com/siderolabs/talos/commit/f7c5b86be7e2b28906cb66b466a017887ac5e2b6) fix: sync PCR extension with volume provisioning lifecycle * [`f90c79474`](https://github.com/siderolabs/talos/commit/f90c79474b50da35ab8e285ee9723957e4b6cf00) chore: show bound driver in pcidevices info * [`8db34624c`](https://github.com/siderolabs/talos/commit/8db34624c6ed9707ba1165da790f5b389bd1c92f) fix: handle correctly changing platform network config * [`77c7a075b`](https://github.com/siderolabs/talos/commit/77c7a075bbba7ffd24dbd9d5e069ccb50f8143b4) feat: update Kubernetes to 1.33.0 * [`74f0c48c7`](https://github.com/siderolabs/talos/commit/74f0c48c738b0b80278667c3e5a1c5e1ecd5a078) feat: add version compatibility for Talos 1.11 * [`c4fb7dad0`](https://github.com/siderolabs/talos/commit/c4fb7dad0ec390781cca54e2348f116cb1cf1866) fix: force DNS runner shutdown on timeout * [`c49b4836e`](https://github.com/siderolabs/talos/commit/c49b4836e46725940f4731e182475905ebee6019) docs: hetzner: add note about public iso * [`16ea2b113`](https://github.com/siderolabs/talos/commit/16ea2b113fad0c81a96dbcfdf4fd1b9f43bb1282) docs: add what is new for 1.10 * [`be3f0c018`](https://github.com/siderolabs/talos/commit/be3f0c018c50da3d920ed8fe36d4f31c5d3edfac) fix: fix Gvisor tests with containerd patch * [`37db132b3`](https://github.com/siderolabs/talos/commit/37db132b3b3e6c58f15228c64b023e77c15cf012) chore(ci): add provision test with bios * [`ec60b70e7`](https://github.com/siderolabs/talos/commit/ec60b70e7245f49f6ac1d48cd4292b85f1d6f79e) fix: set media type to OCI for image cache layer * [`a471eb31b`](https://github.com/siderolabs/talos/commit/a471eb31b87b393ee9fc57fbc725801d08386ad4) feat: update Linux 6.12.24, containerd 2.0.5 * [`54ad5b872`](https://github.com/siderolabs/talos/commit/54ad5b8729c7d54da2efa6baf7886163741176ed) fix: extension services logging to console * [`601f036ba`](https://github.com/siderolabs/talos/commit/601f036ba9cc762d6a3c6ae819654005f1d49527) docs: correct flannel extra args example * [`ae94377d1`](https://github.com/siderolabs/talos/commit/ae94377d15a3b70248fbb446d13d7ae96bb04e82) feat: support encryption config for user volumes * [`9616f6e8d`](https://github.com/siderolabs/talos/commit/9616f6e8d280e64815fe3e1ba324df1dd5d2122d) docs: add caveat for kubespan and host ports * [`a1d08a362`](https://github.com/siderolabs/talos/commit/a1d08a3624c7c8b5213b8e9dee1cf9289d6719dc) docs: fixes typo at OpenEBS Mayastor worker patches * [`a91e8726e`](https://github.com/siderolabs/talos/commit/a91e8726e433be9db58f1a7a09a4cca422b2b50c) docs: add a dark theme * [`c76189c58`](https://github.com/siderolabs/talos/commit/c76189c58a2fe65954924168d7077350974829dd) fix: grub EFI mount point * [`4ca985c65`](https://github.com/siderolabs/talos/commit/4ca985c656c1924e550d06c073a7c1b6cb03f392) fix: grub efi platform install * [`b31260281`](https://github.com/siderolabs/talos/commit/b31260281dba752e06fcfc645bb020872602d898) docs: update storage.md * [`396a29040`](https://github.com/siderolabs/talos/commit/396a290408eff5bda4ad31fafc33496bea9aa899) feat: add new SBCs * [`a902f6580`](https://github.com/siderolabs/talos/commit/a902f6580f8e104977521a335a41c0cd70256906) feat: update Flannel to v0.26.7 * [`2bbefec1a`](https://github.com/siderolabs/talos/commit/2bbefec1abacae2952782fbd163ef52d34f09858) docs: use cache in preview * [`6028a8d2d`](https://github.com/siderolabs/talos/commit/6028a8d2da571a8a37712f9917e24372cf5af919) docs: update kubeprism.md * [`e51a8ef8c`](https://github.com/siderolabs/talos/commit/e51a8ef8c68bb1cfab2ac845a0b6792d7e000324) fix: prefer new `MountStatus` resource * [`d9c7e7946`](https://github.com/siderolabs/talos/commit/d9c7e79462496d6756c55b0672994aa262eaed4f) docs: fix search * [`b32fa029b`](https://github.com/siderolabs/talos/commit/b32fa029b3f550b3403e25e23aac889d61366389) feat: update Kubernetes to 1.33.0-rc.1 * [`f0ea478cb`](https://github.com/siderolabs/talos/commit/f0ea478cb811675a450839b8dcd351e43404efd4) feat: support address priority * [`8cd3c8dc7`](https://github.com/siderolabs/talos/commit/8cd3c8dc77b25270ed8dea65cbbd4e87c203ee74) test: fix NVIDIA OSS tests * [`62f2d27cd`](https://github.com/siderolabs/talos/commit/62f2d27cd44de5112055b5b47f23b001cadccaae) docs: update virtualbox.md * [`141326ea3`](https://github.com/siderolabs/talos/commit/141326ea3bb2e471a5cb51fd565521683a9792fc) docs: fix tabpane styling * [`134aa53cc`](https://github.com/siderolabs/talos/commit/134aa53ccaba55754544977d695ad3ca5d34e604) feat: update base CoreDNS code in host DNS to 1.12.1

### Changes since v1.11.0-alpha.0
84 commits

* [`58a868e68`](https://github.com/siderolabs/talos/commit/58a868e68833e94d691e7ed029dce629446fecc3) chore: fix renovate config, add release-gate label * [`a59aaee84`](https://github.com/siderolabs/talos/commit/a59aaee84bcceb20792bc4782748449ad93b0530) feat: bump dependencies, Linux 6.12.31 * [`e954ee30a`](https://github.com/siderolabs/talos/commit/e954ee30add42de6f42cbb7d96927722102afdb7) docs: typo correction: LongHorn -> Longhorn * [`aab053394`](https://github.com/siderolabs/talos/commit/aab053394bafdf718196133e38be010d847db0ad) fix: mashal resource byte slices as strings in YAML * [`c7d4191e7`](https://github.com/siderolabs/talos/commit/c7d4191e78bf0a455ab596f46d4cf212dce694a4) fix: rework the way CRI config generation is waited for * [`0114183de`](https://github.com/siderolabs/talos/commit/0114183de62e4ab930ff0f10dd156f935d57cf10) docs: update `lastRelease` to 1.10.3 * [`938b0760a`](https://github.com/siderolabs/talos/commit/938b0760abdb41be1be4da02b877e2c902d594be) docs: update issue template * [`2a7b735b2`](https://github.com/siderolabs/talos/commit/2a7b735b264ebcfa22dc2d6044c9d5cd3057b5c2) feat: drop IMA support * [`2d5a805b0`](https://github.com/siderolabs/talos/commit/2d5a805b0ebabb804b3c32be18db1d718a91070f) fix: typo in DiscoverdVolume spec * [`60c12bad9`](https://github.com/siderolabs/talos/commit/60c12bad93b422db2784b0203d94ca69fa31957c) feat: support nocloud include url userdata directive * [`0fd622c82`](https://github.com/siderolabs/talos/commit/0fd622c825ba1fbb833a4b8920ac4c4e56f08a1f) fix(talosctl): correct --help output for dashboard command * [`a90c936a1`](https://github.com/siderolabs/talos/commit/a90c936a16756cfe5fe451258f0022b808be17d2) feat: support qemu provisioner on darwin * [`5322ca0d3`](https://github.com/siderolabs/talos/commit/5322ca0d372aa20ad90e66f04699b75debb0ab80) docs: update overlay docs * [`a60b6322d`](https://github.com/siderolabs/talos/commit/a60b6322d1e8fbd75394e0bdb4435af605b32bbb) fix(ci): drop nebula from extensions test * [`dbbb59a67`](https://github.com/siderolabs/talos/commit/dbbb59a6781f79ee34a6e91a72575802561c58b6) docs: add note for default `dataDirHostPath` for Rook * [`e26054378`](https://github.com/siderolabs/talos/commit/e2605437826911cd60a6a4d9ee760a6a242e244b) docs: macos qemu provider * [`5d0224093`](https://github.com/siderolabs/talos/commit/5d022409357d41831fa1bfd34ccdcfceecca42df) docs: use the cilium-cli image repo in the job installation manifest * [`ff80e4cca`](https://github.com/siderolabs/talos/commit/ff80e4cca086fa01d84ceb750111dc9e31ccc978) docs: fix CIDR name * [`a5fd15e8b`](https://github.com/siderolabs/talos/commit/a5fd15e8bd4a4547e3658981543401fd9eb8cd80) fix(ci): reproducibility test * [`8f8963e50`](https://github.com/siderolabs/talos/commit/8f8963e50d7b05d1361fd44040c0f1ffb94693af) docs: update Nexxen brand * [`c6b86872d`](https://github.com/siderolabs/talos/commit/c6b86872dc0d62aef5ad70fce00c411080911ace) fix(ci): iso reproducibility file permissions * [`995a1dec4`](https://github.com/siderolabs/talos/commit/995a1dec4a34f49d84daff16b30f8920275a439d) chore: add a check for unsupported darwin flags * [`9db5d0c97`](https://github.com/siderolabs/talos/commit/9db5d0c97ac31c7f6ce0b23d999126fc6cc094ec) fix: nocloud metadata for hostname * [`3cf325654`](https://github.com/siderolabs/talos/commit/3cf325654e4a7f73196241e59e3ca6b5f24c3e19) feat: modularize more arm64 kernel * [`3524745cc`](https://github.com/siderolabs/talos/commit/3524745cc49c51e4f13da954a57ab56d467fd26e) fix: allow any PKI in Talos API * [`f438cdb09`](https://github.com/siderolabs/talos/commit/f438cdb0993b17f0e540ecefa39cde09f89730f4) chore: use custom dhcpd server on macos qemu * [`11c17fb9a`](https://github.com/siderolabs/talos/commit/11c17fb9aad2443b10e15295069b8e24e0d514e2) fix: metal-iso reproducibility * [`7fcb89ee3`](https://github.com/siderolabs/talos/commit/7fcb89ee385fdbf47dae4a8308299c00488df84a) chore: add darwin vmnet qemu support * [`fc1237343`](https://github.com/siderolabs/talos/commit/fc1237343f79a1be907c43ac3ce116168409ed17) chore: clean up `/usr/bin` * [`b551f32ce`](https://github.com/siderolabs/talos/commit/b551f32ce550f2bc3c679a9857f28d604a297bbf) feat: update containerd to v2.1.1 * [`67f4154f9`](https://github.com/siderolabs/talos/commit/67f4154f920fc0c58a9a832e14fbc7f9430747b3) docs: update disk-management.md * [`0cb137ad7`](https://github.com/siderolabs/talos/commit/0cb137ad7366e2386f49a99aee0a3c5ffb7223f6) fix: make disk size check work on old Talos * [`7c057edd5`](https://github.com/siderolabs/talos/commit/7c057edd5f3636dff6932ad9fbd7c51867b0c2c8) fix: use vmdk-convert istead of qemu-img to create VMDK for OVA files * [`cd618dad0`](https://github.com/siderolabs/talos/commit/cd618dad0feb1390e5945e2bba1d20bcecf30c2a) chore: update the go-blockdevice package * [`0b99631a0`](https://github.com/siderolabs/talos/commit/0b99631a0b64ce8d65ddcf7f40b2168debf11a62) fix: bump apid memory limit * [`5451f35b1`](https://github.com/siderolabs/talos/commit/5451f35b148a630c6ab011dce44b52fd2ad327ba) docs: update virtualbox * [`bd4d202a5`](https://github.com/siderolabs/talos/commit/bd4d202a5a67c56b6c6e6bc962f6bd51c729759f) refactor: bring owned.State from COSI to simplify tests * [`0b96df574`](https://github.com/siderolabs/talos/commit/0b96df57476af86a37bcfdbf28a479444a9e6e5c) feat: update containerd to 2.1.0 * [`e1a939144`](https://github.com/siderolabs/talos/commit/e1a939144f25acc6a2715feedb30a56a47f6793d) docs: fix formatting in disk encryption * [`7a817df1c`](https://github.com/siderolabs/talos/commit/7a817df1cce58de2a16b72b37a54ffc0103af79a) docs: fix typo * [`f35b213b2`](https://github.com/siderolabs/talos/commit/f35b213b2b448c2e0065d4698095a843dd2f5268) test: fix DHCP unicast failures in QEMU environment * [`7064bbf05`](https://github.com/siderolabs/talos/commit/7064bbf056f083de0f7174c9d3c600871189b4e5) docs: fix vmware factory URL * [`78c33bcdb`](https://github.com/siderolabs/talos/commit/78c33bcdb9a30195ce401311e82b2e189faf33f3) feat: update default Kubernetes to v1.33.1 * [`da6795266`](https://github.com/siderolabs/talos/commit/da67952666d2db2b8b5636bd4cae8af09a139410) fix: disable automatic MAC assignment to bridge interfaces * [`ca34adf58`](https://github.com/siderolabs/talos/commit/ca34adf585bfe04d2d1b84f186cb87aa77fc8e00) chore(ci): drop azure keys * [`ea5de19fa`](https://github.com/siderolabs/talos/commit/ea5de19fad3f62889899c0d89d08b8b73dfa75da) fix: selinux detection * [`52c76ea3a`](https://github.com/siderolabs/talos/commit/52c76ea3a61a4a3cbd963dc2ff0d6d21b4210bcd) fix: consistently apply dynamic grpc proxy dialer * [`aa9569e5d`](https://github.com/siderolabs/talos/commit/aa9569e5d8c59b762dfd64a4e9ef42cfdc6f9d51) chore: refactor cluster create cmd flags * [`1161faa05`](https://github.com/siderolabs/talos/commit/1161faa0594c033bf032852b880439b2082c9722) docs: fix typo in Cilium docs * [`164745e44`](https://github.com/siderolabs/talos/commit/164745e44334146b8a6f696640692c25b731414a) docs: remove `preserve` flag mention in upgrade notes * [`9a2ecbaaf`](https://github.com/siderolabs/talos/commit/9a2ecbaaf7b7a3f393dd29272aca34e069a24c6e) fix: makefile operating system param * [`118aa69d6`](https://github.com/siderolabs/talos/commit/118aa69d6f6e71b88747db1e8234d478daa54ab4) chore: update cloud-image-uploader dependencies * [`acdd721cf`](https://github.com/siderolabs/talos/commit/acdd721cfa62f9888a9ceea1693c17348c0d663a) chore: dump qemu pachine ipam records on darwin * [`bb9094534`](https://github.com/siderolabs/talos/commit/bb90945344f02b9cdae6e0e01821792dca25096b) chore: rotate aws iam credentials * [`0bfa4ae1b`](https://github.com/siderolabs/talos/commit/0bfa4ae1b06e1e6330adf331e1a97651bbe39b4a) chore: update deps for cloud-image-uploader * [`956d7c71b`](https://github.com/siderolabs/talos/commit/956d7c71bcdff639b8261cf6cf1a5d19cf702f75) chore: update sops keys * [`e2f819d88`](https://github.com/siderolabs/talos/commit/e2f819d880373102f8a8c7f0ff549e37ba75a08e) test: fix the process runner log collection * [`fdac4cfb9`](https://github.com/siderolabs/talos/commit/fdac4cfb9143853eb21d38e1b3d517455b0ba0f2) fix: upgrade go-kubernetes for DRA flag bug * [`09d88e1e8`](https://github.com/siderolabs/talos/commit/09d88e1e8374ef19e5730994d9b098333347f0b7) test: fix some flaky tests * [`ec1f41a94`](https://github.com/siderolabs/talos/commit/ec1f41a948b1bda02096434e47f2a2a767951fe9) chore: make qemu config server bind work on darwin * [`980f4d2b9`](https://github.com/siderolabs/talos/commit/980f4d2b936cfdc3ebc9882f7c25fbf2d2aa49f8) feat: bump dependencies * [`95259337e`](https://github.com/siderolabs/talos/commit/95259337ee0ccb22d7e9125074818ac8f9afa7af) fix: k8s 1.32->1.33 upgrade check * [`c3c326b40`](https://github.com/siderolabs/talos/commit/c3c326b405804c258b68f19b8d7dacca32535e9b) fix: improve volume mounter automaton * [`918b94d9a`](https://github.com/siderolabs/talos/commit/918b94d9a0b71b759073f8f7eb0f5dc7fdff413f) refactor: rewrite disk size check * [`ab7e693d7`](https://github.com/siderolabs/talos/commit/ab7e693d76500b6cdc2068221bdfce16633a8b01) chore: make qemu lb address bind work on darwin * [`97ceab001`](https://github.com/siderolabs/talos/commit/97ceab001c1bb79407c40d8fff867342656187b9) fix: multiple logic issues in platform network config controller * [`46349a9df`](https://github.com/siderolabs/talos/commit/46349a9df5d026a4e4b807a94865d5b3c371d32a) docs: remove azure image gallery instructions * [`0cfcdd3de`](https://github.com/siderolabs/talos/commit/0cfcdd3de1a20690ce47d63bb56b3d33d11c1474) docs: fix search on base talos.dev * [`78646b4e0`](https://github.com/siderolabs/talos/commit/78646b4e050358b930d27e4eddcfb22c4c825b0c) docs: add registryd debug command * [`c6824c211`](https://github.com/siderolabs/talos/commit/c6824c211438a3fb663f4233e8663732ab2ddf44) fix: deny apply config requests without v1alpha1 in "normal" mode * [`7df0408e4`](https://github.com/siderolabs/talos/commit/7df0408e460ebc392c6927c7b23e3795b9bd2140) fix: interactive installer config gen * [`881c5d62b`](https://github.com/siderolabs/talos/commit/881c5d62bf0d1f3311b3cf946b7801f97c1fb94b) fix: suppress duplicate platform config updates * [`66d77888e`](https://github.com/siderolabs/talos/commit/66d77888e42798995ddc73db3869d16959e53376) fix: replace downloaded asset paths correctly in cluster create cmd * [`6bd6c9b5a`](https://github.com/siderolabs/talos/commit/6bd6c9b5a08ca3b0e9574e1a61edc54c6ff722bb) fix: generate iso greater than 4 gig * [`ac140324e`](https://github.com/siderolabs/talos/commit/ac140324ebfb54f580c9b9bbbb55549bd5ffa11e) fix: skip PCR extension if TPM1.2 is found * [`09ef1f8a4`](https://github.com/siderolabs/talos/commit/09ef1f8a41c84e6a16729e6b6aff81788da0e3f5) fix: ignore http proxy on grpc socket dial * [`22a72dc80`](https://github.com/siderolabs/talos/commit/22a72dc80f2037a4cc7ad696d8dff504deb22630) chore: split options between three structs * [`22c34a50f`](https://github.com/siderolabs/talos/commit/22c34a50fc66edd174ab4a65961257de28a6daa0) fix(ci): provision cron jobs * [`b3b20eff3`](https://github.com/siderolabs/talos/commit/b3b20eff3a29f74d18df634cbb01f41bde17f2c8) fix: containerd crashing with sigsegv * [`f7891c301`](https://github.com/siderolabs/talos/commit/f7891c3018de248c7c66483562227b614689413c) chore: calculate vmnet interface name preemptively * [`ae87edffb`](https://github.com/siderolabs/talos/commit/ae87edffbcdaed12fef41541622f27882ed63755) fix: drop libseccomp from rootfs * [`f74a805bb`](https://github.com/siderolabs/talos/commit/f74a805bb067f55619cae7aebb92f00bb8173c92) fix: do correct backoff for nocloud reconcile * [`01bb294af`](https://github.com/siderolabs/talos/commit/01bb294af63f193dafa12cb623ea77ad67b698fb) fix(ci): provision tests * [`e4945be3b`](https://github.com/siderolabs/talos/commit/e4945be3bc43cbc275e2ea5f399a0188c5e16ad8) docs: add registryd debug command

### Changes from siderolabs/crypto
2 commits

* [`17107ae`](https://github.com/siderolabs/crypto/commit/17107ae45403a2bcd4fecfb4660b60276652b00d) fix: add generic CSR generator and OpenSSL interop * [`53659fc`](https://github.com/siderolabs/crypto/commit/53659fc35f6abd4ada7ffa22ef1b148cf93c0f28) refactor: split into files

### Changes from siderolabs/gen
1 commit

* [`7c0324f`](https://github.com/siderolabs/gen/commit/7c0324fee9a7cfbdd117f43702fa273689f0db97) chore: future-proof HashTrieMap

### Changes from siderolabs/go-circular
1 commit

* [`5b39ef8`](https://github.com/siderolabs/go-circular/commit/5b39ef87df04efeaa47fe6374a8114f39c126122) fix: do not log error if chunk zero was never written

### Changes from siderolabs/go-kubernetes
2 commits

* [`9070be4`](https://github.com/siderolabs/go-kubernetes/commit/9070be4308e23d969ec4fc49b25dab4a27d512e7) fix: remove DynamicResourceAllocation feature gate * [`8cb588b`](https://github.com/siderolabs/go-kubernetes/commit/8cb588bc4c93d812de901a6a33e599ba2169cd96) fix: k8s 1.32->1.33 upgrade check

### Changes from siderolabs/pkgs
33 commits

* [`79bfa9e`](https://github.com/siderolabs/pkgs/commit/79bfa9e06e5e69955236ffd58323c9936d638d45) feat: update NVIDIA drivers to 570.148.08 * [`c8b8bd8`](https://github.com/siderolabs/pkgs/commit/c8b8bd8b5eb265f8e8c8955998e428b86d177ab5) feat: bump dependencies * [`54bf03e`](https://github.com/siderolabs/pkgs/commit/54bf03ebf24d9ef70a47d4b3b4f30d92191085da) feat: update Linux to 6.12.31 * [`93b3aaa`](https://github.com/siderolabs/pkgs/commit/93b3aaae5369140058e6a5cbdf83d1da235eb735) feat: add patch for CephFS IMA performance regression * [`ebd6627`](https://github.com/siderolabs/pkgs/commit/ebd6627c68406076ed95b2cd629d2ace51bb49b6) feat: disable IMA support * [`8aad53b`](https://github.com/siderolabs/pkgs/commit/8aad53bab3201d7f87d39ab61953e04392402efc) feat: add CONFIG_NFT_CONNLIMIT to kernel * [`7a299fa`](https://github.com/siderolabs/pkgs/commit/7a299fa02106a7216926d6bcff21fb1cd2da7d73) feat: update Linux to 6.12.30 * [`8c4603e`](https://github.com/siderolabs/pkgs/commit/8c4603e90335b9aaf180b954ebc43f65dcb2b7b6) feat: move more configs to modules on arm64 * [`7b1183b`](https://github.com/siderolabs/pkgs/commit/7b1183bea84e46cd8f1a775f95683b8a0039c2d7) feat(kernel): enable IB user-space management and RDMA * [`1b1430e`](https://github.com/siderolabs/pkgs/commit/1b1430e82ef62efdd538588183ed27def2bebbaa) fix: drop pcre2 binaries * [`487610c`](https://github.com/siderolabs/pkgs/commit/487610c4f286210c22cd813427380af654297791) fix: drop broken symlinks * [`f31d518`](https://github.com/siderolabs/pkgs/commit/f31d518eefec0cb672760d00a5c2de37b45dfb45) fix: clean up some binaries * [`0f74b9b`](https://github.com/siderolabs/pkgs/commit/0f74b9bd1d097a283f3edd6165161e4e0688a79f) feat: update containerd to v2.1.1 * [`89b4037`](https://github.com/siderolabs/pkgs/commit/89b40372b8964a9dc9ad3db17a46a9d9c797f60f) fix: tenstorrent pkg name * [`a14b544`](https://github.com/siderolabs/pkgs/commit/a14b54409704c1f3beb0f51089dadd3f3e8dc441) chore: drop qemu-tools vmdk support * [`2563e47`](https://github.com/siderolabs/pkgs/commit/2563e47ca1bfc755ee4ecf2b470cfed081b54e6f) feat: add tenstorrent package * [`2a1c42f`](https://github.com/siderolabs/pkgs/commit/2a1c42fde5fe4009c33d50d571d7d3cfe3a09888) fix(renovate): flannel config * [`bfa69a8`](https://github.com/siderolabs/pkgs/commit/bfa69a820e8190aed3a45c00dff5f4f1cc42b7a6) feat: add open-vmdk package * [`9f1ba1f`](https://github.com/siderolabs/pkgs/commit/9f1ba1f047c835abdf882540d316055a3e2d1bfc) fix: bring back updated containerd gvisor patch * [`1567cb6`](https://github.com/siderolabs/pkgs/commit/1567cb616691dc22fbc3374cdeac11cdbe51bb94) feat: update Linux 6.12.28, firmware * [`9bc66e6`](https://github.com/siderolabs/pkgs/commit/9bc66e6bd355f8a86c4becbd78aede1323e3681e) feat: update containerd to 2.1.0 * [`c6b54e0`](https://github.com/siderolabs/pkgs/commit/c6b54e04fb5d943ff31f05b1e095af65eb901604) feat: enable zswap * [`4cd7084`](https://github.com/siderolabs/pkgs/commit/4cd7084634c2b79541da8c6f95c047d4eb0e66a2) feat: update dependencies * [`a3fcbf8`](https://github.com/siderolabs/pkgs/commit/a3fcbf812632aaa8e8f9027a88181c284e7d919d) feat(kernel): enable panthor driver * [`74d1665`](https://github.com/siderolabs/pkgs/commit/74d16657fd53c30249c3eba75769f90dd84366ce) feat: update ZFS to 2.3.2 * [`ddc866b`](https://github.com/siderolabs/pkgs/commit/ddc866bc9dd0557c2e9d5d0b234348767769cfd3) feat: update Linux to 6.12.27 * [`a347857`](https://github.com/siderolabs/pkgs/commit/a347857b33a6a41fe2661a7451c3af65a51404c9) fix: build containerd with Go 1.23 * [`74da85c`](https://github.com/siderolabs/pkgs/commit/74da85c2cf61b8006af38b3d0d38dc13098d5227) fix: containerd build doesn't need seccomp * [`4effa05`](https://github.com/siderolabs/pkgs/commit/4effa0525dc87974052e9dec2685a0ad411773dd) fix: downgrade libseccomp to 2.5.5 * [`9cea00b`](https://github.com/siderolabs/pkgs/commit/9cea00b4601d7bedf49606b647003f3c6cb0787b) feat: update Linux to 6.12.25 * [`cb108a5`](https://github.com/siderolabs/pkgs/commit/cb108a514b55a302008fb4c1ce6d88ce0d769b58) feat(kernel): enable bcache module * [`d042432`](https://github.com/siderolabs/pkgs/commit/d04243270a4f10f9ecb889883ab42687e5ae6351) fix: backport sandbox fix for Gvisor * [`fa625dc`](https://github.com/siderolabs/pkgs/commit/fa625dc6dd97a61cb8479b8b0ab82126650de11b) feat: update Linux 6.12.24, containerd 2.0.5

### Changes from siderolabs/siderolink
1 commit

* [`d2a79e0`](https://github.com/siderolabs/siderolink/commit/d2a79e0263806b68ff0a44ea9efa58b83fb269ec) fix: clean up device on failure

### Changes from siderolabs/tools
3 commits

* [`af3fd64`](https://github.com/siderolabs/tools/commit/af3fd645d48a373396f8346af411c1c827c87376) feat: update dependencies * [`e35234b`](https://github.com/siderolabs/tools/commit/e35234bd94c3c16daf06d00848d7752f5e4c7d15) feat: update dependencies * [`c96a4e6`](https://github.com/siderolabs/tools/commit/c96a4e671e378f80f161e45942f80b10adfd562d) chore: update toolchain to the latest version

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.6.0 -> v0.7.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.9.0 -> v1.10.0 * **github.com/containerd/containerd/api** v1.8.0 -> v1.9.0 * **github.com/containerd/containerd/v2** v2.0.5 -> v2.1.1 * **github.com/containernetworking/plugins** v1.6.2 -> v1.7.1 * **github.com/cosi-project/runtime** v0.10.2 -> v0.10.6 * **github.com/detailyang/go-fallocate** 432fa640bd2e **_new_** * **github.com/docker/cli** v28.0.4 -> v28.2.2 * **github.com/docker/docker** v28.0.4 -> v28.2.2 * **github.com/google/cel-go** v0.24.1 -> v0.25.0 * **github.com/google/go-containerregistry** v0.20.3 -> v0.20.5 * **github.com/google/go-tpm** v0.9.3 -> v0.9.5 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.3.1 -> v2.3.2 * **github.com/hetznercloud/hcloud-go/v2** v2.21.0 -> v2.21.1 * **github.com/linode/go-metadata** v0.2.1 -> v0.2.2 * **github.com/miekg/dns** v1.1.65 -> v1.1.66 * **github.com/prometheus/procfs** v0.16.0 -> v0.16.1 * **github.com/rivo/tview** 949945f8d922 -> 0c592cd31026 * **github.com/safchain/ethtool** v0.5.10 -> v0.6.1 * **github.com/siderolabs/crypto** v0.5.1 -> v0.6.0 * **github.com/siderolabs/gen** v0.8.0 -> v0.8.1 * **github.com/siderolabs/go-blockdevice/v2** v2.0.16 -> v2.0.18 * **github.com/siderolabs/go-circular** v0.2.2 -> v0.2.3 * **github.com/siderolabs/go-kubernetes** v0.2.21 -> v0.2.23 * **github.com/siderolabs/pkgs** v1.10.0-5-g48dba3e -> v1.11.0-alpha.0-32-g79bfa9e * **github.com/siderolabs/siderolink** v0.3.13 -> v0.3.14 * **github.com/siderolabs/talos/pkg/machinery** v1.10.0 -> v1.11.0-alpha.0 * **github.com/siderolabs/tools** v1.10.0 -> v1.11.0-alpha.0-2-gaf3fd64 * **golang.org/x/net** v0.39.0 -> v0.40.0 * **golang.org/x/oauth2** v0.29.0 -> v0.30.0 * **golang.org/x/sync** v0.13.0 -> v0.14.0 * **golang.org/x/sys** v0.32.0 -> v0.33.0 * **golang.org/x/term** v0.31.0 -> v0.32.0 * **golang.org/x/text** v0.24.0 -> v0.25.0 * **google.golang.org/grpc** v1.71.1 -> v1.72.2 * **k8s.io/api** v0.33.0 -> v0.33.1 * **k8s.io/apimachinery** v0.33.0 -> v0.33.1 * **k8s.io/apiserver** v0.33.0 -> v0.33.1 * **k8s.io/client-go** v0.33.0 -> v0.33.1 * **k8s.io/component-base** v0.33.0 -> v0.33.1 * **k8s.io/kube-scheduler** v0.33.0 -> v0.33.1 * **k8s.io/kubectl** v0.33.0 -> v0.33.1 * **k8s.io/kubelet** v0.33.0 -> v0.33.1 * **k8s.io/pod-security-admission** v0.33.0 -> v0.33.1 * **sigs.k8s.io/hydrophone** b92baf7e0b04 -> v0.7.0 Previous release can be found at [v1.10.0](https://github.com/siderolabs/talos/releases/tag/v1.10.0) ## [Talos 1.11.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.11.0-alpha.0) (2025-05-01) Welcome to the v1.11.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates ### Contributors * Andrey Smirnov * Noel Georgi * Orzelius * Dmitrii Sharshakov * Marat Bakeev * Steve Francis * Alvaro "Chamo" Linares Cabre * Andrew Longwill * Bryan Mora * Joakim Nohlgård * Spencer Smith * Tan Siewert ### Changes
49 commits

* [`ace44ea61`](https://github.com/siderolabs/talos/commit/ace44ea6169d419f188e0a2456c31f420e61ae77) test: update hydrophone to 0.7.0 * [`3a1163692`](https://github.com/siderolabs/talos/commit/3a1163692da7b41b17f263ab43d0fd81abafc4f8) chore: cross platform qemu preflight checks * [`7914fb104`](https://github.com/siderolabs/talos/commit/7914fb10412d31a1b75c74b0c66578e55fb77bc7) chore: move the create command to it's own package * [`c8e619608`](https://github.com/siderolabs/talos/commit/c8e619608dc8898be71a17c54503085ef38abf37) chore: prepare for release 1.11 * [`1299aaa45`](https://github.com/siderolabs/talos/commit/1299aaa45d997dd23aed380f858cec3bc6b975e4) chore(ci): add extensions test for Youki runtime * [`e50ceb221`](https://github.com/siderolabs/talos/commit/e50ceb221e56f0760d5f2fc9e4b821d6b29add05) docs: activate Talos 1.10 docs * [`9d12aaeb1`](https://github.com/siderolabs/talos/commit/9d12aaeb19d68c5e692921b938d72347f6129f65) test: improve config patch test * [`106a656b6`](https://github.com/siderolabs/talos/commit/106a656b6132e766e9e9ef7b1c12b97a413b5de6) chore: make qemu provider build on darwin * [`8013aa06c`](https://github.com/siderolabs/talos/commit/8013aa06cd338f1dd11061d3455767fee4b9783c) test: replace platform metadata test * [`2b89c2810`](https://github.com/siderolabs/talos/commit/2b89c2810551ab52678e62fcbf5355dd05c72030) fix: relax etcd APIs RBAC requirements * [`1e677587c`](https://github.com/siderolabs/talos/commit/1e677587c0e6c61f724a85f18ee9d436ae6da038) fix: preserve kubelet image suffix * [`62ab8af45`](https://github.com/siderolabs/talos/commit/62ab8af459475cbd24a2f34d8923ce70d1fda3db) fix: disk image generation with image cache * [`d60626f01`](https://github.com/siderolabs/talos/commit/d60626f017ef495210939ee4f8ef7f623dd325f9) fix: handle encryption type mismatch * [`a9109ebd0`](https://github.com/siderolabs/talos/commit/a9109ebd00fcd300bf4262142ade77df6788852b) feat: allow SideroLink unique token in machine config * [`2ff3a6e40`](https://github.com/siderolabs/talos/commit/2ff3a6e4079a29b6b45770204fd8cb30369518e9) feat(kernel): add bcache kernel module to core talos * [`fa95a2146`](https://github.com/siderolabs/talos/commit/fa95a2146056bfe1ae322cb574fd8d432745b5c9) fix(ci): bios provision test * [`f7c5b86be`](https://github.com/siderolabs/talos/commit/f7c5b86be7e2b28906cb66b466a017887ac5e2b6) fix: sync PCR extension with volume provisioning lifecycle * [`f90c79474`](https://github.com/siderolabs/talos/commit/f90c79474b50da35ab8e285ee9723957e4b6cf00) chore: show bound driver in pcidevices info * [`8db34624c`](https://github.com/siderolabs/talos/commit/8db34624c6ed9707ba1165da790f5b389bd1c92f) fix: handle correctly changing platform network config * [`77c7a075b`](https://github.com/siderolabs/talos/commit/77c7a075bbba7ffd24dbd9d5e069ccb50f8143b4) feat: update Kubernetes to 1.33.0 * [`74f0c48c7`](https://github.com/siderolabs/talos/commit/74f0c48c738b0b80278667c3e5a1c5e1ecd5a078) feat: add version compatibility for Talos 1.11 * [`c4fb7dad0`](https://github.com/siderolabs/talos/commit/c4fb7dad0ec390781cca54e2348f116cb1cf1866) fix: force DNS runner shutdown on timeout * [`c49b4836e`](https://github.com/siderolabs/talos/commit/c49b4836e46725940f4731e182475905ebee6019) docs: hetzner: add note about public iso * [`16ea2b113`](https://github.com/siderolabs/talos/commit/16ea2b113fad0c81a96dbcfdf4fd1b9f43bb1282) docs: add what is new for 1.10 * [`be3f0c018`](https://github.com/siderolabs/talos/commit/be3f0c018c50da3d920ed8fe36d4f31c5d3edfac) fix: fix Gvisor tests with containerd patch * [`37db132b3`](https://github.com/siderolabs/talos/commit/37db132b3b3e6c58f15228c64b023e77c15cf012) chore(ci): add provision test with bios * [`ec60b70e7`](https://github.com/siderolabs/talos/commit/ec60b70e7245f49f6ac1d48cd4292b85f1d6f79e) fix: set media type to OCI for image cache layer * [`a471eb31b`](https://github.com/siderolabs/talos/commit/a471eb31b87b393ee9fc57fbc725801d08386ad4) feat: update Linux 6.12.24, containerd 2.0.5 * [`54ad5b872`](https://github.com/siderolabs/talos/commit/54ad5b8729c7d54da2efa6baf7886163741176ed) fix: extension services logging to console * [`601f036ba`](https://github.com/siderolabs/talos/commit/601f036ba9cc762d6a3c6ae819654005f1d49527) docs: correct flannel extra args example * [`ae94377d1`](https://github.com/siderolabs/talos/commit/ae94377d15a3b70248fbb446d13d7ae96bb04e82) feat: support encryption config for user volumes * [`9616f6e8d`](https://github.com/siderolabs/talos/commit/9616f6e8d280e64815fe3e1ba324df1dd5d2122d) docs: add caveat for kubespan and host ports * [`a1d08a362`](https://github.com/siderolabs/talos/commit/a1d08a3624c7c8b5213b8e9dee1cf9289d6719dc) docs: fixes typo at OpenEBS Mayastor worker patches * [`a91e8726e`](https://github.com/siderolabs/talos/commit/a91e8726e433be9db58f1a7a09a4cca422b2b50c) docs: add a dark theme * [`c76189c58`](https://github.com/siderolabs/talos/commit/c76189c58a2fe65954924168d7077350974829dd) fix: grub EFI mount point * [`4ca985c65`](https://github.com/siderolabs/talos/commit/4ca985c656c1924e550d06c073a7c1b6cb03f392) fix: grub efi platform install * [`b31260281`](https://github.com/siderolabs/talos/commit/b31260281dba752e06fcfc645bb020872602d898) docs: update storage.md * [`396a29040`](https://github.com/siderolabs/talos/commit/396a290408eff5bda4ad31fafc33496bea9aa899) feat: add new SBCs * [`a902f6580`](https://github.com/siderolabs/talos/commit/a902f6580f8e104977521a335a41c0cd70256906) feat: update Flannel to v0.26.7 * [`2bbefec1a`](https://github.com/siderolabs/talos/commit/2bbefec1abacae2952782fbd163ef52d34f09858) docs: use cache in preview * [`6028a8d2d`](https://github.com/siderolabs/talos/commit/6028a8d2da571a8a37712f9917e24372cf5af919) docs: update kubeprism.md * [`e51a8ef8c`](https://github.com/siderolabs/talos/commit/e51a8ef8c68bb1cfab2ac845a0b6792d7e000324) fix: prefer new `MountStatus` resource * [`d9c7e7946`](https://github.com/siderolabs/talos/commit/d9c7e79462496d6756c55b0672994aa262eaed4f) docs: fix search * [`b32fa029b`](https://github.com/siderolabs/talos/commit/b32fa029b3f550b3403e25e23aac889d61366389) feat: update Kubernetes to 1.33.0-rc.1 * [`f0ea478cb`](https://github.com/siderolabs/talos/commit/f0ea478cb811675a450839b8dcd351e43404efd4) feat: support address priority * [`8cd3c8dc7`](https://github.com/siderolabs/talos/commit/8cd3c8dc77b25270ed8dea65cbbd4e87c203ee74) test: fix NVIDIA OSS tests * [`62f2d27cd`](https://github.com/siderolabs/talos/commit/62f2d27cd44de5112055b5b47f23b001cadccaae) docs: update virtualbox.md * [`141326ea3`](https://github.com/siderolabs/talos/commit/141326ea3bb2e471a5cb51fd565521683a9792fc) docs: fix tabpane styling * [`134aa53cc`](https://github.com/siderolabs/talos/commit/134aa53ccaba55754544977d695ad3ca5d34e604) feat: update base CoreDNS code in host DNS to 1.12.1

### Changes from siderolabs/pkgs
4 commits

* [`9cea00b`](https://github.com/siderolabs/pkgs/commit/9cea00b4601d7bedf49606b647003f3c6cb0787b) feat: update Linux to 6.12.25 * [`cb108a5`](https://github.com/siderolabs/pkgs/commit/cb108a514b55a302008fb4c1ce6d88ce0d769b58) feat(kernel): enable bcache module * [`d042432`](https://github.com/siderolabs/pkgs/commit/d04243270a4f10f9ecb889883ab42687e5ae6351) fix: backport sandbox fix for Gvisor * [`fa625dc`](https://github.com/siderolabs/pkgs/commit/fa625dc6dd97a61cb8479b8b0ab82126650de11b) feat: update Linux 6.12.24, containerd 2.0.5

### Dependency Changes * **github.com/detailyang/go-fallocate** 432fa640bd2e **_new_** * **github.com/siderolabs/pkgs** v1.10.0-5-g48dba3e -> v1.11.0-alpha.0-3-g9cea00b * **github.com/siderolabs/talos/pkg/machinery** v1.10.0 -> v1.10.0-alpha.3 * **sigs.k8s.io/hydrophone** b92baf7e0b04 -> v0.7.0 Previous release can be found at [v1.10.0](https://github.com/siderolabs/talos/releases/tag/v1.10.0) ## [Talos 1.10.0-alpha.3](https://github.com/siderolabs/talos/releases/tag/v1.10.0-alpha.3) (2025-03-24) Welcome to the v1.10.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### auditd Kernel parameter `talos.auditd.disabled=1` can be used to disable Talos built-in `auditd` service. ### cgroups v1 Talos Linux no longer supports `cgroupsv1` when running in non-container mode. The kernel argument `talos.unified_cgroup_hierarchy` is now ignored. ### Disk Image Talos starting with 1.10 will have disk images that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems. On first boot Talos determines the boot method and will wipe the unused bootloader. Secureboot disk-images will be sd-boot only. For ARM64 imager will still generate GRUB bootloader for Talos < 1.10 and for Talos >= 1.10 all ARM64 boot assets will use systemd-boot. Imager supports overwriting bootloader when generating a disk image via the Imager profile `output` option. Eg: ```yaml output: kind: image imageOptions: bootloader: sd-boot # supported options are sd-boot, grub, dual-boot ``` ### Driver Rebind Talos 1.10 now supports a new machine config document named `PCIDriverRebindConfig` that allows rebinding the driver of a PCI device to a different target driver. See the [documentation](https://www.talos.dev/v1.10/reference/configuration/hardware/pcidriverrebindconfig/) for more information. ### Ethernet Talos now provides `ethtool`-style Ethernet low-level configuration via `network/EthernetConfig` documents. Current status of the interface can be read by `talosctl get ethernetstatus`. ### Machine Install Extensions `.machine.install.extensions` will have no effect starting from Talos 1.10, the machine config document field is still kept so upgrades from older versions are possible. Use [Boot Assets](https://www.talos.dev/v1.10/talos-guides/install/boot-assets/) instead. ### Extra Kernel Args Talos 1.10 on fresh install on UEFI systems will now use systemd-boot and UKIs (Unified Kernel Images)[https://uapi-group.org/specifications/specs/unified_kernel_image/]. This means the kernel command line arguments are part of the UKI and cannot be modified without an upgrade to a new UKI. Upgrades to Talos 1.10 will preseve the existing bootloader (GRUB for non-secureboot) and sd-boot for Secureboot and this change will have no effect. To build a [boot asset](https://www.talos.dev/v1.10/talos-guides/install/boot-assets/) with extra kernel arguments whether an `installer` or a boot image use either [Image Factory](https://www.talos.dev/v1.10/talos-guides/install/boot-assets/#image-factory) or [Imager](https://www.talos.dev/v1.10/talos-guides/install/boot-assets/#imager). This means kernel arguments not part of the UKI will not be preserved across updates and a proper installer image generated via Imager Factory or Imager is required. ### Ingress Firewall Talos Ingress Firewall now filters access to Kubernetes NodePort services correctly. ### iSCSI Initiator Talos now generates `/etc/iscsi/initiatorname.iscsi` file based on the node identity which is tied to the lifecycle of the node. If using `iscsi-tools` extension, starting with Talos 1.10 would have a more deterministic IQN for the initiator node. Make sure to update any iSCSI targets to use the new initiator IQN. The iqn can be read by `talosctl read /etc/iscsi/initiatorname.iscsi` ### ISO Talos starting with 1.10 will have ISO's that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems. ### kube-apiserver Authorization Config When using `.cluster.apiServer.authorizationConfig` the user provided order for the authorizers is honoured and `Node` and `RBAC` authorizers are always added to the end if not explicitly specified. Eg: If user provides only `Webhook` authorizer, the final order will be `Webhook`, `Node`, `RBAC`. To provide a specific order for `Node` or `RBAC` explicitly, user can provide the authorizer in the order they want. Eg: ```yaml cluster: apiServer: authorizationConfig: - type: Node name: Node - type: Webhook name: Webhook webhook: connectionInfo: type: InClusterConfig ... - type: RBAC name: rbac ``` Usage of `authorization-mode` CLI argument will not support this form of customization. ### NVMe NQN Talos now generates `/etc/nvme/hostnqn` and `/etc/nvme/hostid` files based on the node identity which is tied to the lifecycle of the node. The NQN can be read by `talosctl read /etc/nvme/hostnqn` ### Fully bootstrapped builds Talos 1.10 is built with a toolchain based on [[Stageˣ]](https://stagex.tools/), which is a project building fully bootstrapped software. This change increases reproducibility, auditability and security of Talos builds. This also changes Talos root filesystem structure for unified /usr, with other directories symlinking to /usr/bin and /usr/lib. System extensions must move their directories accordingly for 1.10. ### Component Updates * Linux: 6.12.19 * CNI plugins: 1.6.2 * runc: 1.2.6 * containerd: 2.0.4 * etcd: 3.5.20 * Flannel: 0.26.4 * Kubernetes: 1.33.0-beta.0 Talos is built with Go 1.24.1. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitry Sharshakov * Dmitriy Matrenichev * Dmitrii Sharshakov * Joakim Nohlgård * 459below * Enrique Hernández Bello * Justin Garrison * Mathspy * Nico Berlee * Skyler Mäntysaari * Utku Ozdemir * ihelmer07 * Adam Cirillo * Alex Lubbock * Alexis La Goutte * Andrew Longwill * Andrew Symington * Artem Chernyshev * Cazmill13 * Christian Luetke-Stetzkamp * Christoph Hoopmann * Dennis * Devin Buhl * Dominik Masur * Ermeikin Sergei * Florian Grignon * Gabe Alford * Ganawa Juanah * Jason Benedicic * Joakim Nohlgård * Josef * K Birt * KillianCdP * L.J. Hanson * Louis SCHNEIDER * Marcel Hamer * Mikhail Petrov * Motte * Natalie Romana Albers * Omar * Orzelius * PRIHLOP * Ram * Robin Elfrink * Ryan Jacobs * Serge Logvinov * Shaderbug * Stepan Rabotkin * Thomas Gosteli * Tim Olson * Tine Jozelj * Tobias Kohlbau * TomyLobo * Valtteri Huuskonen * bzub * greenpsi * jvanthienen * mehlc * pphysch * sflotat2607 * suse-coder ### Changes
270 commits

* [`a834219ac`](https://github.com/siderolabs/talos/commit/a834219ace76663e13bfc0cd5e59b19f32b63ac7) chore: update dependencies * [`857779b90`](https://github.com/siderolabs/talos/commit/857779b902ca8c235eb42988b0a8a17c0de2ac21) docs: clarify custom CA certificate with KMS STATE encryption * [`39ed45ae6`](https://github.com/siderolabs/talos/commit/39ed45ae61f8ff6a7d7475b87b9d4ff61377be8b) docs: add information about Cilium exclusive CNI * [`087a85f40`](https://github.com/siderolabs/talos/commit/087a85f40920e84344451959dbd570b3d1d77a99) feat: support running with SELinux enforcing * [`d4aacb0d8`](https://github.com/siderolabs/talos/commit/d4aacb0d85c239f5960a4ff223b28f387a1023ef) refactor: mount operation for STATE and user disks * [`44f3c7248`](https://github.com/siderolabs/talos/commit/44f3c72489350aab64eef67f7d803853819d3a74) fix: kata extension * [`7ca5ab5e9`](https://github.com/siderolabs/talos/commit/7ca5ab5e933f1cd28fb74c24ae2a0f00030b763b) fix: shrink installer and imager images * [`ea0994cfe`](https://github.com/siderolabs/talos/commit/ea0994cfef497412fecd3c345c03f70b1ebd62b9) fix: kexec with smbios type 11 string * [`8e20a5d28`](https://github.com/siderolabs/talos/commit/8e20a5d280523279b3039ea02f5c30137087b3b5) fix: pass /usr/etc/in-container to apid, trustd and extension containers * [`9b9512ba8`](https://github.com/siderolabs/talos/commit/9b9512ba88e0f2ebd78b72f4abdccb3f46f9320b) feat: update Linux 6.12.19, containerd 2.0.4 * [`433b0237b`](https://github.com/siderolabs/talos/commit/433b0237bd11ac57aac8d6b8980337d37383c33d) fix: correct structprotogen example * [`6e68a522a`](https://github.com/siderolabs/talos/commit/6e68a522ab4303c316f6039e4beaaa7ea20a63f9) chore: fix conformance artifact name * [`f592730d9`](https://github.com/siderolabs/talos/commit/f592730d9db79b4d8a0823389dc35fce5c668920) fix(ci): fix image cache test * [`cc6c714ce`](https://github.com/siderolabs/talos/commit/cc6c714ce5fb7359028ecd4ef43c8bb0935250ae) feat: add Tegra modules to initrd * [`81d1fe0f8`](https://github.com/siderolabs/talos/commit/81d1fe0f807210346d59303ef05d30a1c5e53d02) fix: add missing TOOLS_PREFIX for WITH_DEBUG_SHELL builds * [`3e38bf6d4`](https://github.com/siderolabs/talos/commit/3e38bf6d44b6ea0abd065f7883b5320845a9d12e) fix: ignore missing config (nocloud) via cidata * [`27a4486a8`](https://github.com/siderolabs/talos/commit/27a4486a82151e3c28fe7f23521e3e3e7a5bf287) docs: fix typo cluser -> cluster * [`ac79b1ea0`](https://github.com/siderolabs/talos/commit/ac79b1ea0c033012ed3dd5a84190fb07de431c1c) feat: pull in Intel STTMAC network drivers * [`9bb5c060c`](https://github.com/siderolabs/talos/commit/9bb5c060c160ec2e1449f30c6ba85b92a50c1a9b) chore: bump go-kubernetes * [`2b8e08234`](https://github.com/siderolabs/talos/commit/2b8e082344d3bb62c3f5d83b9dc9edbe4f7fede8) feat: deprecate `.machine.install.extensions` * [`b7446372b`](https://github.com/siderolabs/talos/commit/b7446372b62152c235c30823b12a9e2df7a111a3) docs: add documentation on unofficial SBC forks * [`9bec765c4`](https://github.com/siderolabs/talos/commit/9bec765c41e49d872de29956cdae50ea7cd89d8a) feat: talosctl kubeconfig write to stdout option * [`11ebb1078`](https://github.com/siderolabs/talos/commit/11ebb107853a21d626d2c80a88f33bdc5df52641) fix: kexec when using sd-boot * [`61f1a32d2`](https://github.com/siderolabs/talos/commit/61f1a32d24be8a4d73f48d02fbd6281208bdf7ac) test: allocate more resources for conformance runs * [`b8b7b83f8`](https://github.com/siderolabs/talos/commit/b8b7b83f872ee4d4e8b52d51b42116d4bf8223ae) chore: extraKernelArgs validation for UKI's * [`e2df0c6d3`](https://github.com/siderolabs/talos/commit/e2df0c6d3628ea972e914a94af09d8606083825d) docs: update siderolink.md * [`f9b14e784`](https://github.com/siderolabs/talos/commit/f9b14e7848f2b08b6cc274a0ef7a01df98bcd48e) fix: reconnect on SideroLink tunnel on/off change * [`29f7b3bf3`](https://github.com/siderolabs/talos/commit/29f7b3bf37aead52f9e015aeeea20f4318f39dc4) test(ci): use k8s websocket executor for tests * [`9531c1c6d`](https://github.com/siderolabs/talos/commit/9531c1c6d2601b77febbdcc7bca8686f3163fd21) fix(ci): image-cache cron * [`90abdc489`](https://github.com/siderolabs/talos/commit/90abdc4893316516622b01e8c0d2d3bb8ba06416) feat: update Kubernetes to 1.33.0-beta.0 * [`9a5914048`](https://github.com/siderolabs/talos/commit/9a5914048c66ebcaef1614f9efc0c656d8dff63a) refactor: ephemeral mount * [`e4fb1c06a`](https://github.com/siderolabs/talos/commit/e4fb1c06a486d45afe6adba0d2290595d0f680f4) docs: update for predictable interface naming * [`729fce306`](https://github.com/siderolabs/talos/commit/729fce3065b4e23bc9a09739dc2eb86521ca3fe7) feat: update Linux to 6.12.18 * [`b4d2e1c3c`](https://github.com/siderolabs/talos/commit/b4d2e1c3c59db3d64fcb0fa10fbd250dcdd31d5b) fix: typo in machinery CloudPlatforms * [`7e0475488`](https://github.com/siderolabs/talos/commit/7e0475488d3a5c088fc02431c2ee55cf4a453b84) fix: qemu: archive cluster logs only after stopping VMs * [`dab30a8b9`](https://github.com/siderolabs/talos/commit/dab30a8b9fc48078a1cefca6cc6a9caaacef5a93) fix: ensure no goroutines escape in dns controller * [`fce824e2f`](https://github.com/siderolabs/talos/commit/fce824e2f3aa1c21a72e385a4daa2a546305cf0a) fix: change from "init6" to "inet6" in docs * [`f51ebd1bc`](https://github.com/siderolabs/talos/commit/f51ebd1bc8142b2ea649ee205f015dfde61e8ee5) chore: fix the mount cache ids in the Dockerfile * [`4365aecbd`](https://github.com/siderolabs/talos/commit/4365aecbd6dc4e54b22514938d6c41f7276c8e70) test: use standard installer for e2e-iso * [`431178327`](https://github.com/siderolabs/talos/commit/43117832725acf6fdf0b2ac6e0302de64958f85b) feat: update Kubernetes to v1.33.0-alpha.3 * [`1259345e4`](https://github.com/siderolabs/talos/commit/1259345e4240c0ec3d84b5fd982d98897a39cc62) fix(ci): image-cache cron * [`18871a7eb`](https://github.com/siderolabs/talos/commit/18871a7eb87db2c536a4620e2c3ba64951171140) chore: tidy labeled-squashfs.sh * [`d45259f89`](https://github.com/siderolabs/talos/commit/d45259f89dce282eaf6bc3ed4c2106aa8a054eba) feat: update Flannel to 0.26.5 * [`e83ef0e2e`](https://github.com/siderolabs/talos/commit/e83ef0e2ee8a564efecb1356d7e8246b2e9056f6) docs: update proxmox.md * [`3def5f9a6`](https://github.com/siderolabs/talos/commit/3def5f9a673ad2b57580ade483496c07d3945edc) feat: update etcd to 3.5.19 * [`c3c0d2e42`](https://github.com/siderolabs/talos/commit/c3c0d2e42f3913b64463f13fb63a5e0a78bed627) test: fix dns test in race mode * [`17965c32f`](https://github.com/siderolabs/talos/commit/17965c32fa1f2e3b12cb2cf9ebd4550eb4dca672) chore: update Go to 1.24.1 * [`1fbb2d1a7`](https://github.com/siderolabs/talos/commit/1fbb2d1a7cc658eedd8cdae2d0440324c709fe58) docs: update nvidia-gpu-proprietary.md * [`d60972bdf`](https://github.com/siderolabs/talos/commit/d60972bdf8c45f283e6e247b41053035eb6e1dbc) chore: add installer-base to the list of signed images * [`ab6cb3dfa`](https://github.com/siderolabs/talos/commit/ab6cb3dfa34a6a030a899cc0180b862c40c6fcb5) chore: disable azure upload * [`2355218e4`](https://github.com/siderolabs/talos/commit/2355218e4a24e8833d231bb08229ad2564046a45) release(v1.10.0-alpha.2): prepare release * [`d4e3e957c`](https://github.com/siderolabs/talos/commit/d4e3e957cb98d3fe6ee6685a807c25fafb128423) fix(ci): fix integration tests * [`1849b5388`](https://github.com/siderolabs/talos/commit/1849b53881e1ab12b28f9d8b537c8e43d607b4ea) feat: update dependencies * [`88fc6bbeb`](https://github.com/siderolabs/talos/commit/88fc6bbebeff1c0db0e43fb0a83d2b03a973da8a) test: fix UKI preserving talos.config and image cache * [`ba8cd304d`](https://github.com/siderolabs/talos/commit/ba8cd304d2029c93c31135b2003b1f2f064ff29f) test: enable image-cache in the cron * [`28b5dc738`](https://github.com/siderolabs/talos/commit/28b5dc738cd7af5bb06604b2778f808827544ee0) test: fix reproduciblity test * [`50998038b`](https://github.com/siderolabs/talos/commit/50998038bb45e33438cccdd8fba4c156f0f7b0b5) feat: prefer sd-boot for UEFI * [`e831e52e0`](https://github.com/siderolabs/talos/commit/e831e52e01a47f34e982e9cfa397ca9722094a82) feat: add support for qla2xx * [`ec5c049a5`](https://github.com/siderolabs/talos/commit/ec5c049a5a5063289a97271c2d145d298f5f1a43) feat: update Kubernetes to 1.33.0-alpha.2 * [`ebfa82f35`](https://github.com/siderolabs/talos/commit/ebfa82f3558e5a44a332a0576416ce61f8235407) docs: update deprecated command * [`d79059a2c`](https://github.com/siderolabs/talos/commit/d79059a2c96565b1524b3869ad6b28f1cd8351da) chore: fix shutdown typo in shutdown sequence * [`a3f88d2ef`](https://github.com/siderolabs/talos/commit/a3f88d2ef5b726e1256a070a961bd4931d453a6a) fix: block NodePort services with ingress firewall * [`fd8131cb8`](https://github.com/siderolabs/talos/commit/fd8131cb86714b450334508abc0891eeaa2da9c8) feat: generate unified installer * [`ebfdb91b4`](https://github.com/siderolabs/talos/commit/ebfdb91b4cd36b48c36c1523dc74bc6e1860f815) fix: handle dynamic HTTP proxy settings for discovery client * [`d45eaeb74`](https://github.com/siderolabs/talos/commit/d45eaeb74cc43cc3154fcbce474958a613bc561b) fix: correctly map link names/aliases when using VIP operator * [`7c4e47c0c`](https://github.com/siderolabs/talos/commit/7c4e47c0c00e740bf0d63521baa1231354bc1966) chore: stop doing generate on each build * [`b1d410cb6`](https://github.com/siderolabs/talos/commit/b1d410cb6203f8a3847472db3990d2634bab22e2) feat: dual boot disk image * [`468e318ba`](https://github.com/siderolabs/talos/commit/468e318ba4137e1f11d231fe3ed66f10543073e3) fix: multiple fixes for dashboard/no data * [`3dd8d9aed`](https://github.com/siderolabs/talos/commit/3dd8d9aed8d311f84c61f3030fbf2031ee1d3df9) docs: update resetting-a-machine.md to include example of reset * [`7af8f6b2f`](https://github.com/siderolabs/talos/commit/7af8f6b2fa98f1ed4ad5adbcea6d54802013a086) feat: validate docker image references in upgrade options * [`c949f55e6`](https://github.com/siderolabs/talos/commit/c949f55e61b8c74202f2da50829c2e034e43682e) docs: remove typo on resetting a machine page * [`f5c097041`](https://github.com/siderolabs/talos/commit/f5c097041faac04808636703c94fe5d3ee208947) feat: add description to schema object defs * [`79ee304e1`](https://github.com/siderolabs/talos/commit/79ee304e11df7cfb2ccc6eeeb39ab6112975db45) chore: update enumer to a version that fixes Go 1.24 compatibility * [`46d67fe44`](https://github.com/siderolabs/talos/commit/46d67fe446edfabe23e3e8a91cc1f07436827c5e) chore: update Go to 1.24, update pkgs * [`7f1dd2669`](https://github.com/siderolabs/talos/commit/7f1dd2669734f496afbec6812a814d70dbaee3b4) fix(ci): fix integration-misc crons * [`26a773d3f`](https://github.com/siderolabs/talos/commit/26a773d3f27572a01d146ed356be5e78f8dc23e0) docs: add a note about syslog sending messages to services * [`7ce053638`](https://github.com/siderolabs/talos/commit/7ce053638db9c9abd4d966d412986c07615a750e) fix: ignore digest part of images when checking version * [`ae1b00354`](https://github.com/siderolabs/talos/commit/ae1b003542d01fc565a8478c9de512c3ea929f3d) feat: support noclooud instance-id from dmi * [`58661dea7`](https://github.com/siderolabs/talos/commit/58661dea71a706eaf57f9813b9672395e820e756) docs: update getting-started.md * [`94cf9fb84`](https://github.com/siderolabs/talos/commit/94cf9fb8470b88fac6523953ebb083ecf31e4274) chore: fix spurious generate failures * [`32a34791e`](https://github.com/siderolabs/talos/commit/32a34791e2e61e77531ccc8f8be92c76c4b83514) fix: typo in Makefile target talosctl-freebsd-arm64 * [`1b4464c8a`](https://github.com/siderolabs/talos/commit/1b4464c8a65600b923d9790656f25e245db2e0aa) feat: update Kubernetes to 1.32.2 * [`9463ac23e`](https://github.com/siderolabs/talos/commit/9463ac23e77067f6dce2c22a33e3937357745303) fix: make ingress firewall filter traffic to nodeports * [`8531d91a1`](https://github.com/siderolabs/talos/commit/8531d91a1f20ecc587a1b76c13637ab3555718e9) fix: blockdevice transport detection * [`ce616d93a`](https://github.com/siderolabs/talos/commit/ce616d93a5799163ae278bac477c4f612197d109) fix: path for ca-certificates * [`f35b58779`](https://github.com/siderolabs/talos/commit/f35b58779e912aeec64e6fc0a9964e76e97f9a9f) fix: fix diff printing * [`bf0f910a1`](https://github.com/siderolabs/talos/commit/bf0f910a16ce3707cc5741b88a176671a0dd40b3) chore: provide more logging for dns requests * [`607998ba2`](https://github.com/siderolabs/talos/commit/607998ba20d62fa13233daf139eb3126ffa6569f) feat: support uki profiles via imager * [`711cf2d99`](https://github.com/siderolabs/talos/commit/711cf2d99ac9c16b7a48c20271ecc2c60a3f3d6d) fix: ignore errors to stop pods * [`142d75483`](https://github.com/siderolabs/talos/commit/142d754835785cd4edf088e2827854ffc8580262) fix: handle empty registry config * [`47f377b21`](https://github.com/siderolabs/talos/commit/47f377b21f546f1950ed43171d6b4f374ab7f721) feat: implement the last ethtool feature - channels * [`88cf69b8c`](https://github.com/siderolabs/talos/commit/88cf69b8c5c5f9fd47107289a717f1083ae12807) feat: multi profile UKIs * [`557faad75`](https://github.com/siderolabs/talos/commit/557faad759e4f21b7dedc3c69a61d2b3c31e6bc4) feat: update Linux to 6.12.13 * [`5dbf9e350`](https://github.com/siderolabs/talos/commit/5dbf9e35024192632aecda5fd817ab4558aced1a) refactor: implement volume mount controller * [`aa11e9abb`](https://github.com/siderolabs/talos/commit/aa11e9abb78d33ba66a167335b14fc79f4613ef9) fix: make image cache volume management less strict * [`26a62e342`](https://github.com/siderolabs/talos/commit/26a62e34211d642ddcdb0cff67013c0d4c640b78) docs: fix typo in Wireguard docs * [`0419f5d8b`](https://github.com/siderolabs/talos/commit/0419f5d8ba889faead5452af40fc70c8e1573084) feat: implement features in `ethtool`-like support * [`cd66fc6e8`](https://github.com/siderolabs/talos/commit/cd66fc6e8e7e5cf1a17c03de41c1d2e39cb71aa4) feat: use bootstrapped packages for building Talos * [`2b5bd5d1d`](https://github.com/siderolabs/talos/commit/2b5bd5d1dad65f653dfd77d363d0a76404099453) chore: upgrade siderolabs/go-loadbalancer * [`15191aa3e`](https://github.com/siderolabs/talos/commit/15191aa3e305feba6b5f8b084e6d9b7337e2143f) fix: extract cmdline multi profile UKIs * [`716f700da`](https://github.com/siderolabs/talos/commit/716f700da74608aa93c9d335ea17f0fea34865a6) feat: provide initial support for ethtool configuration * [`b726e2f9f`](https://github.com/siderolabs/talos/commit/b726e2f9f7057f1e7ed912bea28db3e4b63441cb) feat: update Flannel to 0.26.4 * [`98d56d4d6`](https://github.com/siderolabs/talos/commit/98d56d4d647d455acc7324d84df05881ebe46d34) chore: track opened grpc connections * [`5e28c8e03`](https://github.com/siderolabs/talos/commit/5e28c8e039aae14427571bdd9bf9813ee6220743) fix: image cache volume provisioning * [`c9667813d`](https://github.com/siderolabs/talos/commit/c9667813d2b515306a775dabbefad378dc74a0a9) chore: remove containerd importer * [`270ffb69a`](https://github.com/siderolabs/talos/commit/270ffb69a39a9b10e3d98c44579eec20de51ba67) fix: duplicate qemu drive ids * [`71ec41be1`](https://github.com/siderolabs/talos/commit/71ec41be18541c31e887037bad59a7a3395a2bb1) fix: build of Talos on non-Linux host * [`e2aa7c98c`](https://github.com/siderolabs/talos/commit/e2aa7c98ccebca727cac792e53db5722aa79e213) fix: installer with SecureBoot should contain UKIs * [`6e22c06c3`](https://github.com/siderolabs/talos/commit/6e22c06c3c4c96bb02d34c7f61633137cd03f6f5) release(v1.10.0-alpha.1): prepare release * [`3a2d9867b`](https://github.com/siderolabs/talos/commit/3a2d9867b5cc3236b1d1c7981e5794657f3c155e) fix: do not close client.Client.conn with finalizer * [`73f30ff25`](https://github.com/siderolabs/talos/commit/73f30ff25e0adb7a47e2153756e0ea94bd605568) feat: bump pkgs for udev update * [`aea90cb8f`](https://github.com/siderolabs/talos/commit/aea90cb8f1dbe7d5f67d35714825133728c1490d) docs: update hyper-v * [`b7165615f`](https://github.com/siderolabs/talos/commit/b7165615f86afd09ea85dc91090a40860ae6fc9a) fix: use local NTP for AWS platform * [`673ca4bcb`](https://github.com/siderolabs/talos/commit/673ca4bcb2448b3c252fccff0d243932c97fd893) fix: ensure proper closure of client.Client.conn with finalizer * [`19040ffd6`](https://github.com/siderolabs/talos/commit/19040ffd6ef128daaf48a820d8826186c82c68c5) fix: handle of PE sections with duplicate names * [`83489d348`](https://github.com/siderolabs/talos/commit/83489d348905352497da0f6dc042f3e7f05cd4d7) docs: add note about vmxnet and flannel conflict * [`f1292f5e7`](https://github.com/siderolabs/talos/commit/f1292f5e7af4110270475d8bcc4bd39519419e03) docs: add iscsi-tools extension to prerequisites * [`93b4a3740`](https://github.com/siderolabs/talos/commit/93b4a3740ba0c35e8b62cbf8c70058d1e53c3b8e) test: bump timeout on rotate CA test * [`42e166984`](https://github.com/siderolabs/talos/commit/42e16698453a687a4293e7cfeeb0e09d4f084217) feat: support kexec from uki * [`8da264946`](https://github.com/siderolabs/talos/commit/8da264946cda9b4803fd9f2f4dfd0ed25445843b) docs: add Orange Pi 5 to Image Factory platforms and documentation * [`c5fb62e2e`](https://github.com/siderolabs/talos/commit/c5fb62e2e32690aa0235b0911ded1888084496a8) feat: update Linux to 6.2.11 * [`83d007c16`](https://github.com/siderolabs/talos/commit/83d007c161e03311cede2153f35c32f608537290) feat: update etcd to 3.5.18 * [`edf7c3288`](https://github.com/siderolabs/talos/commit/edf7c328835273e2bc6dd23c646091e6a03aa2e9) fix: pe uki extract * [`70f72c5b0`](https://github.com/siderolabs/talos/commit/70f72c5b00bce791d692ec3a0e9a91aaf9d88031) docs: update multus.md * [`807a3cd29`](https://github.com/siderolabs/talos/commit/807a3cd291e2e2cb22946826bccb64671a29d901) refactor: all network merge controllers * [`ec8c4660e`](https://github.com/siderolabs/talos/commit/ec8c4660e277dc11b5e70c014a0238d48cf15bda) docs: update vmware.md * [`baf81cd49`](https://github.com/siderolabs/talos/commit/baf81cd4914470b06393d762f70d0a94f7a9fe32) fix(ci): k8s integration suite wait for resource * [`cd5e54903`](https://github.com/siderolabs/talos/commit/cd5e549039b17add0a2ce09713e1a034bb3efccf) feat: generate iso's with both UKI and grub * [`75673b6a3`](https://github.com/siderolabs/talos/commit/75673b6a38eeb6361c6e6aeb389e8dbaaacb8b0b) feat: provide stable symlinks in disk resources * [`f407c88e4`](https://github.com/siderolabs/talos/commit/f407c88e4678ff6d5edb940f5d54461104be3643) fix(ci): wait for longhorn node resource * [`601cdccb9`](https://github.com/siderolabs/talos/commit/601cdccb979640a6b2ffcba41cc698015b1dacde) feat: extract kernel/initrd from uki for grub * [`ff175b9fb`](https://github.com/siderolabs/talos/commit/ff175b9fbdb2ac92ac53351d32de130bd0676038) docs: update disk-encryption.md * [`a8d84e315`](https://github.com/siderolabs/talos/commit/a8d84e3155137a114ad00ad7ae321af033020e7d) docs: fix typos and add more explanations in docs * [`3a384240e`](https://github.com/siderolabs/talos/commit/3a384240ecf660d310f2df98327f018649ebaa6d) fix: invalid date field in iqn/nqn * [`82c9ec158`](https://github.com/siderolabs/talos/commit/82c9ec158e82efea80daaf76fef9fbd31c3eb823) chore(ci): add tests with longhorn v2 engine * [`689ea1dbf`](https://github.com/siderolabs/talos/commit/689ea1dbfe29d70d91e0b41d31fc696e2ff96665) fix: bring back disk UUID * [`7a712fad2`](https://github.com/siderolabs/talos/commit/7a712fad2abb916f397a8dd0aebf66e59ee75904) fix: disks with 4k sector size and systemd-boot * [`d62a34aaf`](https://github.com/siderolabs/talos/commit/d62a34aaf4e4ff7dad9f6dbeb59a67016c70fffb) feat: update tools/pkgs/extras * [`b9a8ad6ac`](https://github.com/siderolabs/talos/commit/b9a8ad6acafd64c4217ba914184592c0cfb97962) chore: de-hardcode list of extra images for image-cache test * [`683153a33`](https://github.com/siderolabs/talos/commit/683153a33c1069e7f7cadf4e3a70bde3f8ba3331) docs: remove the last mentions of `preserve` flag for Talos 1.8+ * [`33c7f4195`](https://github.com/siderolabs/talos/commit/33c7f4195816988af6f70199fdb4a31d027fa746) docs: fix typo an MacOS to on MacOS * [`21cff3919`](https://github.com/siderolabs/talos/commit/21cff3919b80f33f837b19728500fcb91e7caf8f) chore(ci): fio benchmark results as separate artifacts * [`0b7fc7cdf`](https://github.com/siderolabs/talos/commit/0b7fc7cdfea651a6f16db3f346473505d8df3e78) fix: abort node watch on hostname change * [`99ba53941`](https://github.com/siderolabs/talos/commit/99ba53941cecdc54c0ececa9876b25a7fc7668a5) docs: remove the mention of `preserve` flag for Talos 1.8+ * [`bde516fde`](https://github.com/siderolabs/talos/commit/bde516fde62a25dd60691a9a3b6f3d30de11dad1) chore(ci): rework iscsi-tools extensions test * [`e1efbf656`](https://github.com/siderolabs/talos/commit/e1efbf656ae96ecedba1c132608c3ad2d3ae4a66) refactor: extract platform metadata into Talos machinery * [`79987c05d`](https://github.com/siderolabs/talos/commit/79987c05dcd39ca646c2d73c1e25488504f13a60) feat: generate iqn and nqn files * [`0cab6ed17`](https://github.com/siderolabs/talos/commit/0cab6ed170708549d69c04b163744854de0aa8f2) docs: update troubleshooting.md * [`921e10254`](https://github.com/siderolabs/talos/commit/921e10254d443c459a9775368ca080ecba273321) chore: update Go to 1.23.5 * [`399d53b54`](https://github.com/siderolabs/talos/commit/399d53b543f6ca99f13d28313ae77b3472b0f728) fix: ignore forbidden error when waiting for pod eviction * [`8dea57a81`](https://github.com/siderolabs/talos/commit/8dea57a81b8393b518da60951713c711659291f9) fix: make etc binds read-only * [`63157dcb4`](https://github.com/siderolabs/talos/commit/63157dcb496ca767bfbff9e1b86f14277a44cdb7) docs: update SideroLinkConfig example * [`fc7080e34`](https://github.com/siderolabs/talos/commit/fc7080e34b990d2d50ec1e40734437ccd0ee95f7) chore: clear cache after updating upstreams * [`51e0f273f`](https://github.com/siderolabs/talos/commit/51e0f273f9199b8320cd5da247c702a4319a92c5) docs: update documentation for Talos 1.9.2 * [`e06b14112`](https://github.com/siderolabs/talos/commit/e06b14112d2c978e3f6b5c4446090a7ae533ead9) feat: update Kubernetes to 1.32.1 * [`4310b290d`](https://github.com/siderolabs/talos/commit/4310b290d5cff9697f86cc24f1c281e62cb7d72f) fix: generate UKI only if actually needed * [`a8cd99102`](https://github.com/siderolabs/talos/commit/a8cd991026fe7290013b7504a4e87af46c49d25b) docs: update OpenEBS Mayastor installation * [`cf45f4764`](https://github.com/siderolabs/talos/commit/cf45f4764ddd979fa81576833d9630eadea24f41) docs: add Radxa ROCK 5B docs to Single Board Computer section * [`b21bdc5e5`](https://github.com/siderolabs/talos/commit/b21bdc5e501bc2244e3e487827ffba79075f6642) chore(ci): save csi tests fio results * [`01c86832c`](https://github.com/siderolabs/talos/commit/01c86832cbbbe0b81b9500032f94298fd6e90b58) chore(ci): add test for OpenEBS MayaStor * [`c77483510`](https://github.com/siderolabs/talos/commit/c774835103ad139b44d7e4e13c003e2b13160347) test: update `talosctl debug air-gapped` * [`ddd695d93`](https://github.com/siderolabs/talos/commit/ddd695d933d39920da42219ba8b3d39b0681a3ea) feat: update containerd to 2.0.2 * [`da2e81120`](https://github.com/siderolabs/talos/commit/da2e81120f7336d9633a98523e05d91f5750434f) fix: add informer resync period for node status watcher * [`9b957df64`](https://github.com/siderolabs/talos/commit/9b957df64680a97a16575db67d4af27cfc0ef7d2) chore: uki code restructure * [`e41a99525`](https://github.com/siderolabs/talos/commit/e41a995253428dde437eecec52cabfb4c80f90ea) fix: kube-apiserver authorizers order * [`db4ca5668`](https://github.com/siderolabs/talos/commit/db4ca5668ac0d85a98a5ea022f6546526d20aff1) feat: add a kernel parameter to disable built-in auditd * [`faa149003`](https://github.com/siderolabs/talos/commit/faa1490033df0a843010fa7154096d84f415afce) feat: update Linux to 6.12.9 * [`8de19758d`](https://github.com/siderolabs/talos/commit/8de19758dafce802c0f93a63ae3083b5ad17162d) fix: a couple of imager panics/crashes * [`5bc3e34cb`](https://github.com/siderolabs/talos/commit/5bc3e34cb3a6fd8e3eb5d02dd612cf3cf9dc499f) fix: detect GPT before ZFS * [`ed7e47d15`](https://github.com/siderolabs/talos/commit/ed7e47d158e064204b2f14f9ff378bea70e9524e) refactor: drop usage of objcopy to generate UKIs * [`edf5c5e29`](https://github.com/siderolabs/talos/commit/edf5c5e29bc76299c63bb04f1d97a030ecb9b3f0) fix: extfs repair and resize * [`6e32ea5b7`](https://github.com/siderolabs/talos/commit/6e32ea5b7f1a22500014ecb365e13af36034187a) fix: merge of VolumeConfig documents with sizes * [`1be5f8ff2`](https://github.com/siderolabs/talos/commit/1be5f8ff25ac7042ee3334f657d6604ec5f8501d) feat: update Linux to 6.12.8 * [`e6a4583ba`](https://github.com/siderolabs/talos/commit/e6a4583ba862da9f49ab0bd0cb6bc8436723bc67) feat: support generating unsigned UKIs * [`bbd6067d4`](https://github.com/siderolabs/talos/commit/bbd6067d426fb2be22ff8935f415ab6d729d8f19) fix: partition alignment on disks with 4k sectors * [`84fcc976f`](https://github.com/siderolabs/talos/commit/84fcc976f8da5af310771e1835a0347df5bcc97d) fix: yet another dashboard panic * [`6d605fc85`](https://github.com/siderolabs/talos/commit/6d605fc8595e2f06e43529966e396f2ae403c76c) fix: disable NRI plugin in a different way * [`499695e24`](https://github.com/siderolabs/talos/commit/499695e24ea02ffc2fd8c92276d5de41b0d4919e) fix: request previous IP address in discovery * [`cc84caf8c`](https://github.com/siderolabs/talos/commit/cc84caf8c0dffd9d59f360f84967c524be9ba369) docs: update Cilium documentation * [`fa5300d91`](https://github.com/siderolabs/talos/commit/fa5300d910a537f03939fcbf6362abdd8fa607dd) chore: revert: drop deprecated allowSchedulingOnMasters * [`0abb3dabf`](https://github.com/siderolabs/talos/commit/0abb3dabf6d50b9c1176af683ad74234334f822d) docs: fix command to wait for ceph-rook HEALTH_OK * [`32c67c27c`](https://github.com/siderolabs/talos/commit/32c67c27c393c989f9d70ccb8506c4735f70d494) chore: drop deprecated allowSchedulingOnMasters * [`ae6d065be`](https://github.com/siderolabs/talos/commit/ae6d065beb4897a1b877ecb30b06be456befbf91) fix: mount selinuxfs only when SELinux is enabled * [`5ccbf4bcd`](https://github.com/siderolabs/talos/commit/5ccbf4bcdbe9aa2096320d17eb2deab6a062faf9) feat: enable `configfs` * [`59582496d`](https://github.com/siderolabs/talos/commit/59582496d5fe419f833703be8e956163b6241d15) feat: bring in partity with sd-257 * [`83d84a831`](https://github.com/siderolabs/talos/commit/83d84a831862c774b9bc2adc2e11e00bf2a79912) chore(ci): better zfs checks * [`650eb3a4f`](https://github.com/siderolabs/talos/commit/650eb3a4f2d89d173cdd6581a6d1232511a8e219) refactor: rewrite cloud uploader to use AWS SDK Go v2 * [`01bf8449b`](https://github.com/siderolabs/talos/commit/01bf8449b917ece76336ca7f0eb11fd877195025) fix: update field name for bus path disk selector * [`e915c98d5`](https://github.com/siderolabs/talos/commit/e915c98d583e5901c1c2efe38efa656b39d72360) fix: exclude disks with empty transport for disk selector * [`b7a7fdc4b`](https://github.com/siderolabs/talos/commit/b7a7fdc4b8a715157bfa2614c9541b96643cd2ba) refactor: generate /etc/os-release file static way * [`e79c9e127`](https://github.com/siderolabs/talos/commit/e79c9e12772c998ff5b3e401efd7f074f85e5cef) chore(ci): drop equinix metal e2e-test * [`418945444`](https://github.com/siderolabs/talos/commit/418945444135c6d9e2e5960e7b9cbd754084fea2) fix: build of talosctl on non-Linux platforms * [`4761a9e6a`](https://github.com/siderolabs/talos/commit/4761a9e6aa0bf619a564807d02ebce030384d6a1) chore: update dependencies * [`f98efb333`](https://github.com/siderolabs/talos/commit/f98efb333f89b8493c55b91698c917437b7af310) fix: ignore member not found error on leave cluster * [`b72bda0a4`](https://github.com/siderolabs/talos/commit/b72bda0a420f75ea0439cc0240dcf6d3363e5d48) fix: talosctl support and race tests * [`27233cf0f`](https://github.com/siderolabs/talos/commit/27233cf0fcf4031cbc8001504bed67b6d4a104f9) test: use node informer instead of raw watch * [`5dc15e8db`](https://github.com/siderolabs/talos/commit/5dc15e8db459ac632f0ae106e1cfc7eaab672adf) fix: update go-blockdevice to v2.0.9 * [`5f3acd0f2`](https://github.com/siderolabs/talos/commit/5f3acd0f26a35ac966d4ced01436f1dd3c03648b) fix: use correct default search domain * [`7e5d36d46`](https://github.com/siderolabs/talos/commit/7e5d36d469ff01153f40b16ab722f0ebe25d41ae) fix: pci driver rebind config validation * [`4b97bbc3f`](https://github.com/siderolabs/talos/commit/4b97bbc3fee1257d0d21be25e21493bfd1f45a80) fix: pull in containerd CNI deadlock fix * [`066480722`](https://github.com/siderolabs/talos/commit/0664807229e0688f092a453cbd3121dbe189ca39) test: fix apparmor tests * [`82ea44a6b`](https://github.com/siderolabs/talos/commit/82ea44a6b2aa0a35861ca454a09503a81332f824) fix: reduce installer image * [`78b3e7f4f`](https://github.com/siderolabs/talos/commit/78b3e7f4f1870085b719971c6f92dc866fe1e9d0) fix: get next rule number for IPv6 in the appropriate chain * [`675854aa0`](https://github.com/siderolabs/talos/commit/675854aa03b3913da3481337d995c206174cf004) docs: fix two typos * [`f70b7386a`](https://github.com/siderolabs/talos/commit/f70b7386ac3125f3b8ab6b1765338c7e3445ae5c) test: add a xfs makefs test * [`8212e4864`](https://github.com/siderolabs/talos/commit/8212e4864d11e69ed63be3f4e608e9ccbc788cc4) refactor: use quirks in kernel args * [`b4aa5189d`](https://github.com/siderolabs/talos/commit/b4aa5189d4d4565a42ad7ac8de24c424a215b42f) release(v1.10.0-alpha.0): prepare release * [`bd85bd5b7`](https://github.com/siderolabs/talos/commit/bd85bd5b731463a42b7c82c66e9add251a280d26) fix: fix `Failed to initialize SELinux labeling handle` udev error * [`73c82e3e5`](https://github.com/siderolabs/talos/commit/73c82e3e5625ec1899f93312a671dfe6dffaea61) feat: bring Linux 6.12.6, CNI plugins 1.6.1 * [`c12b52491`](https://github.com/siderolabs/talos/commit/c12b52491456d1e52204eb290d0686a317358c7c) docs: document Kubernetes service registry incompat with K8s 1.32 * [`a5660ed77`](https://github.com/siderolabs/talos/commit/a5660ed778108843fe15b2b1582dd6556cf52b6c) feat: pcirebind controller * [`4c3261626`](https://github.com/siderolabs/talos/commit/4c3261626fa3f5ac36df71ec878f103a7c85c5c5) docs: fix several typos * [`fb3675321`](https://github.com/siderolabs/talos/commit/fb36753216cba7740040f2ec117c783221f66192) fix: dashboard crash on CPU data * [`dec0185c8`](https://github.com/siderolabs/talos/commit/dec0185c8505a7d43244fdb01f7a5decc77d116d) chore: reduce memory usage for secureboot functions * [`cee6c60a0`](https://github.com/siderolabs/talos/commit/cee6c60a0fc301b22c50fdf8bd2fc1d2b7ba3d54) fix: make talosctl time work with PTP time sync * [`f75604313`](https://github.com/siderolabs/talos/commit/f75604313d535180c38b33df53253ad4acba2ec1) chore: support gcr.io auth for cache and image gen * [`6ef2596da`](https://github.com/siderolabs/talos/commit/6ef2596da7b7e8be90e5b981621461352be7b134) docs: improve Hetzner documentation * [`7d39b9ec2`](https://github.com/siderolabs/talos/commit/7d39b9ec2bdd7883116626bf889c1331717f8438) feat: remove cgroupsv1 in non-container mode * [`8003536c7`](https://github.com/siderolabs/talos/commit/8003536c7ca20356adcd900e64463bd166d445af) fix: restore previous disk serial fetching * [`03116ef9b`](https://github.com/siderolabs/talos/commit/03116ef9bd2a215c20a2c4c7db133dd857ce2b16) chore: prepare for Talos 1.10 * [`00682fdd6`](https://github.com/siderolabs/talos/commit/00682fdd6e8fa23c6f9782840ea3e2b8ef250f66) docs: activate 1.9 docs as default * [`bea05f5c9`](https://github.com/siderolabs/talos/commit/bea05f5c9b6ce6f5d067eb357d26e30a49154b21) docs: update deploying-cilium.md * [`284ab1179`](https://github.com/siderolabs/talos/commit/284ab11794b3b076aa9ab2bb756e02292d854751) feat: support link altnames/aliases * [`5bfd829bf`](https://github.com/siderolabs/talos/commit/5bfd829bf9c8e46b6c51174be4b764d4c94b3320) docs: fix 'containter' typo * [`8d151b771`](https://github.com/siderolabs/talos/commit/8d151b771debc51d3fa40dfafc7a2e43f955a634) docs: clarify TALOSCONFIG for AWS * [`0ef19171f`](https://github.com/siderolabs/talos/commit/0ef19171f738e46346dfae71f43b8f7b47bf257d) fix: renovate typo * [`c568adc7d`](https://github.com/siderolabs/talos/commit/c568adc7dcd52c34924acc1eae849a2ca5b5a4d5) fix: renovate config * [`ec2e24fd9`](https://github.com/siderolabs/talos/commit/ec2e24fd9617db34e3bec753b5fe720670fa31a4) fix: match MAC addresses case-insensitive (nocloud) * [`41a0c440a`](https://github.com/siderolabs/talos/commit/41a0c440ad3f4de2a2ba9198d22609c55bdaf61b) chore: rekres for renovate changes * [`a49bb9ee4`](https://github.com/siderolabs/talos/commit/a49bb9ee45346268b26d3b9cff4dd017bfb9c829) feat: update Linux to 6.12.5 * [`b15917ecc`](https://github.com/siderolabs/talos/commit/b15917ecc626781e13de0e84b794ab77c97b3159) chore: add more debugging logs for META and volumes * [`2b1b326f0`](https://github.com/siderolabs/talos/commit/2b1b326f08966615a5a2f8708f94e6d1355773a7) docs: mention different paths for OpenEBS * [`9470e842f`](https://github.com/siderolabs/talos/commit/9470e842fca2d7dd0dae185bff7210a8af355445) test: cleanup failed Kubernetes pods * [`c9c685150`](https://github.com/siderolabs/talos/commit/c9c6851504fcda7b66395fbbba1fbc8b0e085d4a) fix: node identity flip * [`590c01657`](https://github.com/siderolabs/talos/commit/590c0165712aee60e752766d6bd3875443c353cb) feat: update containerd to v2.0.1 * [`18fa5a258`](https://github.com/siderolabs/talos/commit/18fa5a25876f41760ce8da5e918222e04b81949a) docs: update image-cache doc for iso * [`ab5bb6884`](https://github.com/siderolabs/talos/commit/ab5bb688420986a356aed55513a1dbd25de323e2) fix: generate and serve registries with port * [`58236066d`](https://github.com/siderolabs/talos/commit/58236066ddbcd7c401e945b70555ff315a2458f7) fix: support image cache on VFAT USB stick * [`e193a5071`](https://github.com/siderolabs/talos/commit/e193a507149c05e341abe019de219fe0b1bc83e3) fix: image cache integration test * [`08ee400fd`](https://github.com/siderolabs/talos/commit/08ee400fdbde368a54d6777cc31ceb91e1968ad2) test: fix flaky test NodeAddressSort * [`d45e8d1d1`](https://github.com/siderolabs/talos/commit/d45e8d1d1da28ca1b311198588d723cb491527eb) feat: update Kubernetes to 1.32.0 * [`136b12912`](https://github.com/siderolabs/talos/commit/136b12912165d5eb5c7c716b7f7dfcfbc42b08d4) chore: drop semicolon for supporting vfat filesystems * [`3e9e027ef`](https://github.com/siderolabs/talos/commit/3e9e027efbd2988f72eb2da0c1ab0e83ba52b950) test: add an option to boot from an USB stick * [`ef8c3e3b3`](https://github.com/siderolabs/talos/commit/ef8c3e3b3b245f7ffefa6c19930d5a0925ce666b) docs: fix typo in multus.md * [`d54414add`](https://github.com/siderolabs/talos/commit/d54414add4e4df1b5a7b166f155cdcca512d4ee2) fix: authorization config gen * [`cce72cfe8`](https://github.com/siderolabs/talos/commit/cce72cfe86beeb7ada9641df611046f4789e3bd8) docs: replace deprecated Hetzner server plans * [`81805103d`](https://github.com/siderolabs/talos/commit/81805103deada24b12b7d7861b2df5a5c788c86b) chore: enable proper parallel usage of TestDepth * [`e1b824eba`](https://github.com/siderolabs/talos/commit/e1b824ebada3d3dad9d2793fd12b5a948d8b51b5) docs: update ceph-with-rook.md * [`470b75563`](https://github.com/siderolabs/talos/commit/470b75563add4ce5bbce312c1e3dc783e63af1fa) fix: use mtu network option for podman * [`61b1489a0`](https://github.com/siderolabs/talos/commit/61b1489a0f0868c5b7e124544520bc46badef85c) fix: order volume config by the requested size * [`bc3039acd`](https://github.com/siderolabs/talos/commit/bc3039acdbc57e6be16a1bc6555894dff2da65c9) feat: update runc to 1.2.3 * [`30016a0a8`](https://github.com/siderolabs/talos/commit/30016a0a8d98d42e01c4d32acf9e600777d72d57) fix: avoid nil-pointer-panic in `RegistriesConfigController` * [`fe0457152`](https://github.com/siderolabs/talos/commit/fe045715277a4678b8e8c9632ec71e86bf17ace0) fix: power on the machine on reboot request in qemu power api * [`10da553ef`](https://github.com/siderolabs/talos/commit/10da553ef0dde5f87f09321400239baa51929a36) docs: build what's new for 1.9 * [`d946ccae3`](https://github.com/siderolabs/talos/commit/d946ccae31b87559a06cb1cefcefe8f937b73d8b) feat: update Linux to 6.12.4 * [`707a77bf6`](https://github.com/siderolabs/talos/commit/707a77bf64190470bf84c91cdff185981e80a31b) test: fix user namespace test, TPM2 fixes * [`c3537b2f5`](https://github.com/siderolabs/talos/commit/c3537b2f5491a890f626ba8fc47034d5059808af) feat: update Linux to 6.12.3 * [`cb4d9d673`](https://github.com/siderolabs/talos/commit/cb4d9d673432e4a0fba0d87bc64fde620d991082) docs: fix a few mistakes in release notes * [`c4724fc97`](https://github.com/siderolabs/talos/commit/c4724fc97598d8764b00fb56971d997a349a92e5) chore: add integration tests for image-cache * [`07220fe7f`](https://github.com/siderolabs/talos/commit/07220fe7f5a22444f7a085f5868f628ddd912b6d) fix: install iptables-nft to the host * [`14841750b`](https://github.com/siderolabs/talos/commit/14841750bf2fc09a9de0b32a7af0dc3f76e1019a) chore: add version compatibility for Talos 1.10 * [`852baf819`](https://github.com/siderolabs/talos/commit/852baf819d453a3d8d58ae9f029e280ae75e0cb1) feat: support vlan/bond in v1, vlan in v2 for nocloud * [`dd61ad861`](https://github.com/siderolabs/talos/commit/dd61ad86105c07c1ff8a101a0542af61699f0df3) fix: lock provisioning order of user disk partitions * [`d0773ff09`](https://github.com/siderolabs/talos/commit/d0773ff09df84b2dac8ecadc91023596050ce098) chore: update Go to 1.23.4 * [`7d6507189`](https://github.com/siderolabs/talos/commit/7d6507189ff9a99b3b05ee9528701b65af4ad147) feat: implement new address sorting algorithm * [`9081506d6`](https://github.com/siderolabs/talos/commit/9081506d6cde26d60a29f08a090e28da501e4bd1) feat: add process scheduling options * [`77e9db4ab`](https://github.com/siderolabs/talos/commit/77e9db4abf9c9b694d60c8803b436121dfe30ccd) test: use two workers in qemu tests by default * [`5a4bdf62a`](https://github.com/siderolabs/talos/commit/5a4bdf62a9bf1387b6489eaf2c9cc0770aa0b68c) feat: update Kubernetes to 1.32.0-rc.1 * [`d99bcc950`](https://github.com/siderolabs/talos/commit/d99bcc95031037f4b0990419d2ce1fd4280cbde9) chore: refactor mergeDNSServers func * [`0cde08d8b`](https://github.com/siderolabs/talos/commit/0cde08d8be1ad62c49fed148fd331ea5a212df4c) docs: add Turing RK1 docs to Single Board Computer section

### Changes since v1.10.0-alpha.2
49 commits

* [`a834219ac`](https://github.com/siderolabs/talos/commit/a834219ace76663e13bfc0cd5e59b19f32b63ac7) chore: update dependencies * [`857779b90`](https://github.com/siderolabs/talos/commit/857779b902ca8c235eb42988b0a8a17c0de2ac21) docs: clarify custom CA certificate with KMS STATE encryption * [`39ed45ae6`](https://github.com/siderolabs/talos/commit/39ed45ae61f8ff6a7d7475b87b9d4ff61377be8b) docs: add information about Cilium exclusive CNI * [`087a85f40`](https://github.com/siderolabs/talos/commit/087a85f40920e84344451959dbd570b3d1d77a99) feat: support running with SELinux enforcing * [`d4aacb0d8`](https://github.com/siderolabs/talos/commit/d4aacb0d85c239f5960a4ff223b28f387a1023ef) refactor: mount operation for STATE and user disks * [`44f3c7248`](https://github.com/siderolabs/talos/commit/44f3c72489350aab64eef67f7d803853819d3a74) fix: kata extension * [`7ca5ab5e9`](https://github.com/siderolabs/talos/commit/7ca5ab5e933f1cd28fb74c24ae2a0f00030b763b) fix: shrink installer and imager images * [`ea0994cfe`](https://github.com/siderolabs/talos/commit/ea0994cfef497412fecd3c345c03f70b1ebd62b9) fix: kexec with smbios type 11 string * [`8e20a5d28`](https://github.com/siderolabs/talos/commit/8e20a5d280523279b3039ea02f5c30137087b3b5) fix: pass /usr/etc/in-container to apid, trustd and extension containers * [`9b9512ba8`](https://github.com/siderolabs/talos/commit/9b9512ba88e0f2ebd78b72f4abdccb3f46f9320b) feat: update Linux 6.12.19, containerd 2.0.4 * [`433b0237b`](https://github.com/siderolabs/talos/commit/433b0237bd11ac57aac8d6b8980337d37383c33d) fix: correct structprotogen example * [`6e68a522a`](https://github.com/siderolabs/talos/commit/6e68a522ab4303c316f6039e4beaaa7ea20a63f9) chore: fix conformance artifact name * [`f592730d9`](https://github.com/siderolabs/talos/commit/f592730d9db79b4d8a0823389dc35fce5c668920) fix(ci): fix image cache test * [`cc6c714ce`](https://github.com/siderolabs/talos/commit/cc6c714ce5fb7359028ecd4ef43c8bb0935250ae) feat: add Tegra modules to initrd * [`81d1fe0f8`](https://github.com/siderolabs/talos/commit/81d1fe0f807210346d59303ef05d30a1c5e53d02) fix: add missing TOOLS_PREFIX for WITH_DEBUG_SHELL builds * [`3e38bf6d4`](https://github.com/siderolabs/talos/commit/3e38bf6d44b6ea0abd065f7883b5320845a9d12e) fix: ignore missing config (nocloud) via cidata * [`27a4486a8`](https://github.com/siderolabs/talos/commit/27a4486a82151e3c28fe7f23521e3e3e7a5bf287) docs: fix typo cluser -> cluster * [`ac79b1ea0`](https://github.com/siderolabs/talos/commit/ac79b1ea0c033012ed3dd5a84190fb07de431c1c) feat: pull in Intel STTMAC network drivers * [`9bb5c060c`](https://github.com/siderolabs/talos/commit/9bb5c060c160ec2e1449f30c6ba85b92a50c1a9b) chore: bump go-kubernetes * [`2b8e08234`](https://github.com/siderolabs/talos/commit/2b8e082344d3bb62c3f5d83b9dc9edbe4f7fede8) feat: deprecate `.machine.install.extensions` * [`b7446372b`](https://github.com/siderolabs/talos/commit/b7446372b62152c235c30823b12a9e2df7a111a3) docs: add documentation on unofficial SBC forks * [`9bec765c4`](https://github.com/siderolabs/talos/commit/9bec765c41e49d872de29956cdae50ea7cd89d8a) feat: talosctl kubeconfig write to stdout option * [`11ebb1078`](https://github.com/siderolabs/talos/commit/11ebb107853a21d626d2c80a88f33bdc5df52641) fix: kexec when using sd-boot * [`61f1a32d2`](https://github.com/siderolabs/talos/commit/61f1a32d24be8a4d73f48d02fbd6281208bdf7ac) test: allocate more resources for conformance runs * [`b8b7b83f8`](https://github.com/siderolabs/talos/commit/b8b7b83f872ee4d4e8b52d51b42116d4bf8223ae) chore: extraKernelArgs validation for UKI's * [`e2df0c6d3`](https://github.com/siderolabs/talos/commit/e2df0c6d3628ea972e914a94af09d8606083825d) docs: update siderolink.md * [`f9b14e784`](https://github.com/siderolabs/talos/commit/f9b14e7848f2b08b6cc274a0ef7a01df98bcd48e) fix: reconnect on SideroLink tunnel on/off change * [`29f7b3bf3`](https://github.com/siderolabs/talos/commit/29f7b3bf37aead52f9e015aeeea20f4318f39dc4) test(ci): use k8s websocket executor for tests * [`9531c1c6d`](https://github.com/siderolabs/talos/commit/9531c1c6d2601b77febbdcc7bca8686f3163fd21) fix(ci): image-cache cron * [`90abdc489`](https://github.com/siderolabs/talos/commit/90abdc4893316516622b01e8c0d2d3bb8ba06416) feat: update Kubernetes to 1.33.0-beta.0 * [`9a5914048`](https://github.com/siderolabs/talos/commit/9a5914048c66ebcaef1614f9efc0c656d8dff63a) refactor: ephemeral mount * [`e4fb1c06a`](https://github.com/siderolabs/talos/commit/e4fb1c06a486d45afe6adba0d2290595d0f680f4) docs: update for predictable interface naming * [`729fce306`](https://github.com/siderolabs/talos/commit/729fce3065b4e23bc9a09739dc2eb86521ca3fe7) feat: update Linux to 6.12.18 * [`b4d2e1c3c`](https://github.com/siderolabs/talos/commit/b4d2e1c3c59db3d64fcb0fa10fbd250dcdd31d5b) fix: typo in machinery CloudPlatforms * [`7e0475488`](https://github.com/siderolabs/talos/commit/7e0475488d3a5c088fc02431c2ee55cf4a453b84) fix: qemu: archive cluster logs only after stopping VMs * [`dab30a8b9`](https://github.com/siderolabs/talos/commit/dab30a8b9fc48078a1cefca6cc6a9caaacef5a93) fix: ensure no goroutines escape in dns controller * [`fce824e2f`](https://github.com/siderolabs/talos/commit/fce824e2f3aa1c21a72e385a4daa2a546305cf0a) fix: change from "init6" to "inet6" in docs * [`f51ebd1bc`](https://github.com/siderolabs/talos/commit/f51ebd1bc8142b2ea649ee205f015dfde61e8ee5) chore: fix the mount cache ids in the Dockerfile * [`4365aecbd`](https://github.com/siderolabs/talos/commit/4365aecbd6dc4e54b22514938d6c41f7276c8e70) test: use standard installer for e2e-iso * [`431178327`](https://github.com/siderolabs/talos/commit/43117832725acf6fdf0b2ac6e0302de64958f85b) feat: update Kubernetes to v1.33.0-alpha.3 * [`1259345e4`](https://github.com/siderolabs/talos/commit/1259345e4240c0ec3d84b5fd982d98897a39cc62) fix(ci): image-cache cron * [`18871a7eb`](https://github.com/siderolabs/talos/commit/18871a7eb87db2c536a4620e2c3ba64951171140) chore: tidy labeled-squashfs.sh * [`d45259f89`](https://github.com/siderolabs/talos/commit/d45259f89dce282eaf6bc3ed4c2106aa8a054eba) feat: update Flannel to 0.26.5 * [`e83ef0e2e`](https://github.com/siderolabs/talos/commit/e83ef0e2ee8a564efecb1356d7e8246b2e9056f6) docs: update proxmox.md * [`3def5f9a6`](https://github.com/siderolabs/talos/commit/3def5f9a673ad2b57580ade483496c07d3945edc) feat: update etcd to 3.5.19 * [`c3c0d2e42`](https://github.com/siderolabs/talos/commit/c3c0d2e42f3913b64463f13fb63a5e0a78bed627) test: fix dns test in race mode * [`17965c32f`](https://github.com/siderolabs/talos/commit/17965c32fa1f2e3b12cb2cf9ebd4550eb4dca672) chore: update Go to 1.24.1 * [`1fbb2d1a7`](https://github.com/siderolabs/talos/commit/1fbb2d1a7cc658eedd8cdae2d0440324c709fe58) docs: update nvidia-gpu-proprietary.md * [`d60972bdf`](https://github.com/siderolabs/talos/commit/d60972bdf8c45f283e6e247b41053035eb6e1dbc) chore: add installer-base to the list of signed images

### Changes from siderolabs/crypto
1 commit

* [`0d45dee`](https://github.com/siderolabs/crypto/commit/0d45deefbcdd4bd6b6e549433b859083df55fc16) chore: bump deps

### Changes from siderolabs/discovery-api
1 commit

* [`64513a6`](https://github.com/siderolabs/discovery-api/commit/64513a6c4fb31c6a043159d5caea1d153ea133a4) feat: rekres, regenerate proto files

### Changes from siderolabs/discovery-client
1 commit

* [`b3632c4`](https://github.com/siderolabs/discovery-client/commit/b3632c4a8cd96ae36337e83308ef447361b51537) feat: support extra dial options in the client

### Changes from siderolabs/extras
7 commits

* [`c201b87`](https://github.com/siderolabs/extras/commit/c201b879d88095dd6c5de18490552e458357303f) feat: update dependencies * [`4102a78`](https://github.com/siderolabs/extras/commit/4102a783a23e298f3c7e600cb4dfb7a04888eaaf) feat: build hermetically using new bldr and pkgs * [`f4a110f`](https://github.com/siderolabs/extras/commit/f4a110f5f4b472743dc023413dca280bce491ec1) fix: build tc-redirect-tap as static binary * [`0840abb`](https://github.com/siderolabs/extras/commit/0840abb9b5e32560ff38577151fdc2f51812ce31) fix: pull in fixed CNI plugins from pkgs * [`52c217f`](https://github.com/siderolabs/extras/commit/52c217f693366bdf21772919ad94933fd160c5d4) feat: update dependencies * [`f755eb4`](https://github.com/siderolabs/extras/commit/f755eb483647d17e487f7cb62de8cc150a420c3c) chore: rekres to simplify `.kres.yaml` defaults * [`e5382fc`](https://github.com/siderolabs/extras/commit/e5382fc5f05d7ccfdb7c95819195caceac8ffcbf) chore: kresify renovate

### Changes from siderolabs/gen
1 commit

* [`5ae3afe`](https://github.com/siderolabs/gen/commit/5ae3afee65490ca9f4bd32ea41803ab3a17cad7e) chore: update hashtriemap implementation from the latest upstream

### Changes from siderolabs/go-circular
2 commits

* [`015a398`](https://github.com/siderolabs/go-circular/commit/015a398e79f2853714cd20d1135dc100f18b6c29) fix: replace static buffer allocation on growth * [`ed8685e`](https://github.com/siderolabs/go-circular/commit/ed8685e0cf9491d9a714e565e0e736439a94a73f) test: add more assertions for write length result

### Changes from siderolabs/go-debug
1 commit

* [`ea108ca`](https://github.com/siderolabs/go-debug/commit/ea108cacca8940426149e67ba00e414633e4ef3f) chore: add support for Go 1.24

### Changes from siderolabs/go-kubeconfig
1 commit

* [`cc42d09`](https://github.com/siderolabs/go-kubeconfig/commit/cc42d09846ec29c9f8ab8d6e5061bc037100756e) chore: rekres and update

### Changes from siderolabs/go-kubernetes
3 commits

* [`9ba5654`](https://github.com/siderolabs/go-kubernetes/commit/9ba5654fcec6061322530394e336b68a8c764a1b) fix: fix ignoring alpha/beta version parsing * [`0fe1db4`](https://github.com/siderolabs/go-kubernetes/commit/0fe1db4603b591883fac9ce4afcab911bc57922c) feat: update for new changes in Kubernetes 1.33.0-alpha.3 * [`804cb44`](https://github.com/siderolabs/go-kubernetes/commit/804cb440c2299488c7c68185c53b91ffdfb8bf32) feat: add support for Kubernetes to 1.33

### Changes from siderolabs/go-loadbalancer
1 commit

* [`589c33a`](https://github.com/siderolabs/go-loadbalancer/commit/589c33a96ac74a8c0e36b09f534fca62afd6de81) chore: upgrade `upstream.List` and `loadbalancer.TCP` to Go 1.23

### Changes from siderolabs/go-pointer
1 commit

* [`347ee9b`](https://github.com/siderolabs/go-pointer/commit/347ee9b78f625d420254f4ab01bb1d6174474bf4) chore: rekres, update dependencies

### Changes from siderolabs/go-talos-support
1 commit

* [`0f784bd`](https://github.com/siderolabs/go-talos-support/commit/0f784bd58b320543663679693c817515067f3021) fix: avoid deadlock on context cancel

### Changes from siderolabs/pkgs
69 commits

* [`55d99ea`](https://github.com/siderolabs/pkgs/commit/55d99ea9574d6f535b0cccf2173070844e028f07) feat: update dependencies * [`668d25b`](https://github.com/siderolabs/pkgs/commit/668d25bf8059e90534708ce0e8adcda003eb57c6) fix: trim qemu-tools * [`143f50d`](https://github.com/siderolabs/pkgs/commit/143f50db4742b2b3df77e54b668dbbc9880397c8) feat(containerd): provide ctr as part of the build * [`990a9e8`](https://github.com/siderolabs/pkgs/commit/990a9e8ab1bf1d21ee5003c55ba259e0e6be1f78) feat: update Linux 6.12.19, Linux firmware 20250311 * [`9af76d3`](https://github.com/siderolabs/pkgs/commit/9af76d3781a63dad3798246ae2b6edba2dfc443c) feat: update containerd 2.0.4, runc 1.2.6 * [`5a0d262`](https://github.com/siderolabs/pkgs/commit/5a0d26239c68982d1f3981d042e95c87440c60be) feat: add CONFIG_DWMAC_DWC_QOS_ETH to arm64 config * [`9e9a817`](https://github.com/siderolabs/pkgs/commit/9e9a81768bccd2233497b4e20ecb8b3270003bfe) feat(kernel): add support for intel based edge device * [`4d4aaad`](https://github.com/siderolabs/pkgs/commit/4d4aaadb73038f2b2c446fcb72360e3319a533cf) fix: patch containerd with restart fix * [`dbdeff4`](https://github.com/siderolabs/pkgs/commit/dbdeff4613efcccd8b6f180a4f7c3beb25d1aaa1) feat: enable kernel drivers for tegra194, tegra234 SoCs * [`499e56f`](https://github.com/siderolabs/pkgs/commit/499e56f5810cb637199cecbbde53c08d2f42a619) feat(kernel): enable v3d as kernel module * [`988ec60`](https://github.com/siderolabs/pkgs/commit/988ec60e7479751a127306f2eeb693d021465601) feat: add intel pmt/pmc * [`8e2c6d8`](https://github.com/siderolabs/pkgs/commit/8e2c6d8e4ffd8f1af4d5704e2da04ebfedcd6787) feat: update Linux to 6.12.18 * [`5246c32`](https://github.com/siderolabs/pkgs/commit/5246c3270d8d58269c16e8527d343554f0d5e31f) feat(kernel): add -@ to dtc flags for arm64 to enable overlay support * [`068171e`](https://github.com/siderolabs/pkgs/commit/068171ec0859c68adc5b5787114f7ee0cac24d44) chore: unify buildkits * [`a4ac508`](https://github.com/siderolabs/pkgs/commit/a4ac508531f6daff8719ff6ffab189c2a16bc690) feat: add panfrost kernel module * [`5d6ca21`](https://github.com/siderolabs/pkgs/commit/5d6ca21daa1c86510c0e88b0d6e14c99dbe7467c) fix: backport MGLRU patch from Linux 6.13 * [`1d84473`](https://github.com/siderolabs/pkgs/commit/1d844735bb4777f6e2b9b66d721b1ee0c6a1fe59) feat: update dependencies * [`831bc76`](https://github.com/siderolabs/pkgs/commit/831bc7601f0deb1959755d25083c2ea31cf4dd43) feat(kernel): enable TPM SPI support in kernel config for arm64 * [`023f092`](https://github.com/siderolabs/pkgs/commit/023f092857a5536dcb00f68e6fd81b84fc374b7f) feat: enable nfsd support in the kernel * [`347ad26`](https://github.com/siderolabs/pkgs/commit/347ad26815260d148a7aa42a20eafa5228cbc411) feat: update Linux 6.12.17, containerd 2.0.3 * [`40241af`](https://github.com/siderolabs/pkgs/commit/40241af0b4d3a34ba5b89fc3a815b9f401f0e203) feat: enable qla2xxx module * [`6fb00b4`](https://github.com/siderolabs/pkgs/commit/6fb00b45c1e4c50d26822f9bd0fd462ed0dfb712) fix: pull in kmod from tools * [`cc5317a`](https://github.com/siderolabs/pkgs/commit/cc5317adec817d406c1fad1b4871cd7319b56f97) fix: patch Linux with blackhole patch * [`08389dd`](https://github.com/siderolabs/pkgs/commit/08389dd2d97aa53e9ac5523a5512c5bbead371c5) chore: support vmdk and cp format for qemu-img * [`7774b08`](https://github.com/siderolabs/pkgs/commit/7774b08f03f5c096efdcc7863260916d78a7b8a9) feat: update Linux to 6.12.16, validate package structure * [`40d288c`](https://github.com/siderolabs/pkgs/commit/40d288c66d67cfb1d0073288179224d22bf6c41a) fix: imager deps * [`351a1a1`](https://github.com/siderolabs/pkgs/commit/351a1a1ece7a79226f46f03f9d904e1d5600716d) feat: add tools needed for imager * [`80351ca`](https://github.com/siderolabs/pkgs/commit/80351ca6201f5e5efb51b2a2a6a2058fa2512a90) fix: reproducibility tests * [`e1f11f0`](https://github.com/siderolabs/pkgs/commit/e1f11f0991c23f86694b49a9e0fc0f7f592d093d) fix: remove patches and other files from copy-only packages * [`8fff06b`](https://github.com/siderolabs/pkgs/commit/8fff06bac029313278c632321f511c2918585872) chore: bump xfsprogs to 6.12.0 * [`76a0316`](https://github.com/siderolabs/pkgs/commit/76a0316a84571c22eb0c6efd3ce51f3da54671c9) chore: systemd 257.3, runc 1.2.5, ipxe * [`359807b`](https://github.com/siderolabs/pkgs/commit/359807b4172e17fdcd1a1531070535d7ef772b20) feat: copy built packages, improve hermetic build * [`117a1d6`](https://github.com/siderolabs/pkgs/commit/117a1d6b48835310714166335d3821ac47b4c70a) feat: update Linux to 6.12.13 * [`85f8901`](https://github.com/siderolabs/pkgs/commit/85f890180058a0865515ed76ca39f76ff0fe20d7) feat: make pkgs build bootstrapped * [`5763e3e`](https://github.com/siderolabs/pkgs/commit/5763e3e0fe00cbd9010398e795085ba0377802e8) feat: update systemd to 257.2 * [`1e24b31`](https://github.com/siderolabs/pkgs/commit/1e24b31dc379251ad5248f94f548e5c7330f59ec) feat: update Linux to 6.12.11 * [`38749d1`](https://github.com/siderolabs/pkgs/commit/38749d1f08fcb46e522450c1ad530309a8fa327d) fix: build CNI plugins statically linked * [`5da83db`](https://github.com/siderolabs/pkgs/commit/5da83dbbe320768db8eb6175b1e7c5e8ff78389d) feat: bump NVIDIA driver versions * [`5934363`](https://github.com/siderolabs/pkgs/commit/59343630a024e48dfeba826eac45589d0bdcfb99) fix: certificates CA * [`57f492d`](https://github.com/siderolabs/pkgs/commit/57f492d4c3e51e01ab85d2727a7862b21ab21795) feat: bump dependencies * [`45b9ebe`](https://github.com/siderolabs/pkgs/commit/45b9ebed9437752c6516792678356a595f1ec62b) feat: update Linux to 6.2.10 * [`e00ad67`](https://github.com/siderolabs/pkgs/commit/e00ad677f0c7ef4005d26108143c3fe5e36aaab2) chore: rekres to fix reproducibility build * [`cfb4b0a`](https://github.com/siderolabs/pkgs/commit/cfb4b0a79490156864eab726debe20559d9c4240) feat: update Go to 1.23.5 * [`72f19a2`](https://github.com/siderolabs/pkgs/commit/72f19a2983e7abcb620ab57fae6e039158663f1a) feat: update containerd to v2.0.2 * [`17a80ee`](https://github.com/siderolabs/pkgs/commit/17a80eeb75b91211d4ffe8a910feb9fddcd1e585) feat: update Linux to 6.12.9 * [`c9d718d`](https://github.com/siderolabs/pkgs/commit/c9d718d3d6fd762ca3a649a14aa2d74e47d707e2) fix: adjust kernel options around ACPI/PCI/EFI * [`eb9d566`](https://github.com/siderolabs/pkgs/commit/eb9d56617faa56e42648a07b6756c18850e4a045) feat: update Linux to 6.12.8 * [`73e4353`](https://github.com/siderolabs/pkgs/commit/73e4353ad9e2dad6dc8544436776fd412c808d63) fix: update config-arm64 to add Rasperry Pi watchdog support * [`0ab2427`](https://github.com/siderolabs/pkgs/commit/0ab2427a8415d3f29cd4f52e3afd51f701aa5848) fix: dvb was missing I2C_MUX support and si2168 driver * [`c3ac8e2`](https://github.com/siderolabs/pkgs/commit/c3ac8e2d553b068dd982f5b9e48f6b1e0cfdd24d) chore: drop unused cert copy * [`e7eddcf`](https://github.com/siderolabs/pkgs/commit/e7eddcf9498634749a4241844660fd0e9d87fad4) feat: bump dependencies * [`0b00e86`](https://github.com/siderolabs/pkgs/commit/0b00e86ae92f821bdc19af73a5ba571b5051c89a) fix: patch containerd with CNI deadlock fix * [`9051c9a`](https://github.com/siderolabs/pkgs/commit/9051c9ac6f60e039c53248b52ba4ccd192e34b6b) feat: update Linux to 6.12.6 * [`6695012`](https://github.com/siderolabs/pkgs/commit/6695012e8d93d28ea70fc3ba32ed90770eea4363) chore: rekres to simplify `.kres.yaml` defaults * [`611ca38`](https://github.com/siderolabs/pkgs/commit/611ca38153fece4f2b34519325fbca22d34db7a0) chore: rekres to bring renovate under kres * [`a4c4215`](https://github.com/siderolabs/pkgs/commit/a4c4215e74b68765ada0745165b2e2fb5ee508f5) fix: drop cgroupsv1 controllers * [`28c909d`](https://github.com/siderolabs/pkgs/commit/28c909ddeaf0d33e0fc6c5fdf2333a18801cf178) feat: update Linux firmware to 20241210 * [`c40a9e9`](https://github.com/siderolabs/pkgs/commit/c40a9e9713b1fde14f7a967fd1be168bb905d7c9) feat: update Linux to 6.12.5 * [`d54ca83`](https://github.com/siderolabs/pkgs/commit/d54ca835a8868e5df55e2d0ffe3cb0dfa82a3395) feat: update containerd to v2.0.1 * [`86e3755`](https://github.com/siderolabs/pkgs/commit/86e3755deae2fc85d7e62bdcf82a54cb72fec6d5) fix: add CONFIG_INTEL_MEI_GSC_PROXY as module * [`8c31321`](https://github.com/siderolabs/pkgs/commit/8c3132135d5a0e01a9d66790b4b25c7c05e08fa5) feat: update ZFS to 2.2.7 * [`605f493`](https://github.com/siderolabs/pkgs/commit/605f493abfeac79151c02a776733011f19d6c43b) feat: update runc to v1.2.3 * [`1a55529`](https://github.com/siderolabs/pkgs/commit/1a555296764ab0ad83fb4eca6509bb64feff3b7b) feat: update Linux to 6.12.4 * [`52ba9a5`](https://github.com/siderolabs/pkgs/commit/52ba9a57358ef37ce3e4aa4033991dc77ad17fbb) feat: update Linux 6.12.3 * [`9cf35be`](https://github.com/siderolabs/pkgs/commit/9cf35bef274bb445e578f858a0a595b05b44a01f) feat: build host iptables with nftables support * [`71003a3`](https://github.com/siderolabs/pkgs/commit/71003a3c9bff00685917d6e272421a7206b1667e) feat: update Go to 1.23.4 * [`5b4d402`](https://github.com/siderolabs/pkgs/commit/5b4d402bd33f9313a21e4924be57aacce569f9ad) feat: build dvb kernel modules and CX23885 * [`b330af9`](https://github.com/siderolabs/pkgs/commit/b330af9b95d9115382c81f88b55c17b99f7ef355) chore: bring in KSPP recommendations * [`f81b190`](https://github.com/siderolabs/pkgs/commit/f81b190cc65dc93f9212d52cd95806ac79c170d2) feat: kernel driver support for RK3588 devices (Turing RK1)

### Changes from siderolabs/proto-codec
1 commit

* [`3235c29`](https://github.com/siderolabs/proto-codec/commit/3235c2984fa1bb3cd8d38c088127c46dd3d2860e) chore: bump deps

### Changes from siderolabs/siderolink
2 commits

* [`a7af143`](https://github.com/siderolabs/siderolink/commit/a7af1431e0798541f8d3db0aa70af0e15b2c3eb6) feat: support packets filtering before writing them to the tun device * [`38e459e`](https://github.com/siderolabs/siderolink/commit/38e459e50c467791c9670a60ef41f58db246715a) chore: bump deps

### Changes from siderolabs/tools
24 commits

* [`6d456ca`](https://github.com/siderolabs/tools/commit/6d456cab5b18b175571a1745cd684917656c1953) fix: revert util-linux to 2.40.4 * [`5bba094`](https://github.com/siderolabs/tools/commit/5bba094fe89752abe981c5d60d808a27d7aa5df2) feat: update dependencies * [`eeb1f9d`](https://github.com/siderolabs/tools/commit/eeb1f9d558f700f9ed391c1b769b028180c644c3) fix: revert swig update * [`6b082a6`](https://github.com/siderolabs/tools/commit/6b082a644db47678cdc48cf4c6c663a1a6d128d5) chore: unify buildkits * [`87acb27`](https://github.com/siderolabs/tools/commit/87acb27b9becc4947546907ef91b49fa1f8885ad) feat: update dependencies * [`fcee25b`](https://github.com/siderolabs/tools/commit/fcee25ba79e3663db2c0f20f371392e2b45c5f19) fix: revert kmod to 33 * [`6a71711`](https://github.com/siderolabs/tools/commit/6a7171177b5e9a4b579db3614d140a399430c3ab) fix: do not install man and locale for exported packages * [`3389ba2`](https://github.com/siderolabs/tools/commit/3389ba22509cb85e0625dcb6dbbee218fc56d33d) chore: move zlib to be an external package * [`d93b780`](https://github.com/siderolabs/tools/commit/d93b780e8f63cf20a524d3ea76bd4f79b787b5f3) chore: expose more tools * [`46be459`](https://github.com/siderolabs/tools/commit/46be459d3a46f1fa096a9e58cbf060404dd3cbe2) chore: remove systemd version * [`f33fbe4`](https://github.com/siderolabs/tools/commit/f33fbe42517d5a856b360133c6330692b09ba824) fix: install policycoreutils under correct prefix * [`758d61c`](https://github.com/siderolabs/tools/commit/758d61cd71c43ba2a65372dc75b811864e113a29) chore: update dependencies * [`f398a04`](https://github.com/siderolabs/tools/commit/f398a04953666fa468b02851187f3dc4a77c5a44) chore: update dependencies, hermetic build * [`9db33dd`](https://github.com/siderolabs/tools/commit/9db33dd7457e026176fdea964de6d489e67b5fa0) feat: update to Go 1.23.6 * [`ef0a679`](https://github.com/siderolabs/tools/commit/ef0a67955aa9191019e5ea2fe0fe572694606b02) fix: do not install anything to /usr/lib64 * [`35748ea`](https://github.com/siderolabs/tools/commit/35748eac6666b66099b16ccfcfe989e34ea16076) feat: fully bootstrapped build * [`7200845`](https://github.com/siderolabs/tools/commit/7200845be9d0318d23eb77a57e1b8992dd7e8187) feat: update dependencies * [`bc30a2a`](https://github.com/siderolabs/tools/commit/bc30a2a3ace873c80e4657b622e3142efb55cc28) feat: update Go to 1.23.5 * [`533b595`](https://github.com/siderolabs/tools/commit/533b5953d28213aae4d4ae576bedf5df84712458) chore: rekres to fix reproducibility * [`01568a5`](https://github.com/siderolabs/tools/commit/01568a5b42685c3ea19578a7f4d7ba07dc0f18cd) chore: use Make and Go from the toolchain image * [`0393558`](https://github.com/siderolabs/tools/commit/03935581049f82ff466defcc203c5bcc6db5b43a) feat: bump dependencies * [`7811a5f`](https://github.com/siderolabs/tools/commit/7811a5f2f23923cdfe5bfd47ee12ed9e88b29585) chore: rekres to simplify `.kres.yaml` defaults * [`0b8b905`](https://github.com/siderolabs/tools/commit/0b8b9054833d8187bb1f6209b2441719f6e62cfa) chore: kresify renovate config * [`fe34fb3`](https://github.com/siderolabs/tools/commit/fe34fb3d54ec9abe878a9304fbfc3e1e741c0ff4) feat: update Go to 1.23.4

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.2 -> v0.6.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.16.0 -> v1.17.1 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.8.0 -> v1.8.2 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.3.0 -> v1.3.1 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.3.0 -> v1.3.1 * **github.com/aws/aws-sdk-go-v2/config** v1.28.5 -> v1.29.9 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.20 -> v1.16.30 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.37.6 -> v1.38.1 * **github.com/aws/smithy-go** v1.22.1 -> v1.22.3 * **github.com/containerd/cgroups/v3** v3.0.4 -> v3.0.5 * **github.com/containerd/containerd/v2** v2.0.1 -> v2.0.4 * **github.com/containerd/platforms** v1.0.0-rc.0 -> v1.0.0-rc.1 * **github.com/containernetworking/plugins** v1.6.0 -> v1.6.2 * **github.com/cosi-project/runtime** v0.7.6 -> v0.10.1 * **github.com/docker/cli** v27.3.1 -> v28.0.2 * **github.com/docker/docker** v27.3.1 -> v28.0.2 * **github.com/elastic/go-libaudit/v2** v2.6.1 -> v2.6.2 * **github.com/florianl/go-tc** v0.4.4 -> v0.4.5 * **github.com/foxboron/go-uefi** fab4fdf2f2f3 -> 69fb7dba244f * **github.com/gdamore/tcell/v2** v2.7.4 -> v2.8.1 * **github.com/google/cadvisor** v0.51.0 -> v0.52.1 * **github.com/google/cel-go** v0.22.1 -> v0.24.1 * **github.com/google/go-containerregistry** v0.20.2 -> v0.20.3 * **github.com/google/go-tpm** v0.9.1 -> v0.9.3 * **github.com/google/nftables** v0.2.0 -> v0.3.0 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.1.0 -> v2.3.1 * **github.com/hetznercloud/hcloud-go/v2** v2.17.0 -> v2.20.1 * **github.com/insomniacslk/dhcp** a3a4c1f04475 -> 8abf58130905 * **github.com/klauspost/compress** v1.17.11 -> v1.18.0 * **github.com/klauspost/cpuid/v2** v2.2.9 -> v2.2.10 * **github.com/mdlayher/netlink** v1.7.2 -> fbb4dce95f42 * **github.com/mdp/qrterminal/v3** v3.2.0 -> v3.2.1 * **github.com/miekg/dns** v1.1.62 -> v1.1.64 * **github.com/opencontainers/image-spec** v1.1.0 -> v1.1.1 * **github.com/opencontainers/runc** v1.2.2 -> v1.2.6 * **github.com/opencontainers/runtime-spec** v1.2.0 -> v1.2.1 * **github.com/prometheus/procfs** v0.15.1 -> v0.16.0 * **github.com/rivo/tview** c76f7879f592 -> 73a5bd7d6839 * **github.com/safchain/ethtool** v0.5.9 -> v0.5.10 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.30 -> v1.0.0-beta.32 * **github.com/siderolabs/crypto** v0.5.0 -> v0.5.1 * **github.com/siderolabs/discovery-api** v0.1.5 -> v0.1.6 * **github.com/siderolabs/discovery-client** v0.1.10 -> v0.1.11 * **github.com/siderolabs/extras** v1.9.0 -> v1.10.0-alpha.0-4-gc201b87 * **github.com/siderolabs/gen** v0.7.0 -> v0.8.0 * **github.com/siderolabs/go-blockdevice/v2** v2.0.7 -> v2.0.16 * **github.com/siderolabs/go-circular** v0.2.1 -> v0.2.2 * **github.com/siderolabs/go-debug** v0.4.0 -> v0.5.0 * **github.com/siderolabs/go-kubeconfig** v0.1.0 -> v0.1.1 * **github.com/siderolabs/go-kubernetes** v0.2.17 -> v0.2.20 * **github.com/siderolabs/go-loadbalancer** v0.3.4 -> v0.4.0 * **github.com/siderolabs/go-pointer** v1.0.0 -> v1.0.1 * **github.com/siderolabs/go-talos-support** v0.1.1 -> v0.1.2 * **github.com/siderolabs/pkgs** v1.9.0-12-g9576b97 -> v1.10.0-alpha.0-68-g55d99ea * **github.com/siderolabs/proto-codec** v0.1.1 -> v0.1.2 * **github.com/siderolabs/siderolink** v0.3.11 -> v0.3.13 * **github.com/siderolabs/talos/pkg/machinery** v1.9.0 -> v1.10.0-alpha.2 * **github.com/siderolabs/tools** v1.9.0-1-geaad82f -> v1.10.0-alpha.0-23-g6d456ca * **github.com/spf13/cobra** v1.8.1 -> v1.9.1 * **github.com/spf13/pflag** v1.0.5 -> v1.0.6 * **github.com/thejerf/suture/v4** v4.0.5 -> v4.0.6 * **go.etcd.io/etcd/api/v3** v3.5.17 -> v3.5.20 * **go.etcd.io/etcd/client/pkg/v3** v3.5.17 -> v3.5.20 * **go.etcd.io/etcd/client/v3** v3.5.17 -> v3.5.20 * **go.etcd.io/etcd/etcdutl/v3** v3.5.17 -> v3.5.20 * **go.uber.org/goleak** v1.3.0 **_new_** * **golang.org/x/net** v0.32.0 -> v0.37.0 * **golang.org/x/oauth2** v0.24.0 -> v0.28.0 * **golang.org/x/sync** v0.10.0 -> v0.12.0 * **golang.org/x/sys** v0.28.0 -> v0.31.0 * **golang.org/x/term** v0.27.0 -> v0.30.0 * **golang.org/x/text** v0.21.0 -> v0.23.0 * **golang.org/x/time** v0.8.0 -> v0.11.0 * **golang.zx2c4.com/wireguard/wgctrl** 925a1e7659e6 -> a9ab2273dd10 * **google.golang.org/grpc** v1.68.1 -> v1.71.0 * **google.golang.org/protobuf** v1.35.2 -> v1.36.5 * **k8s.io/api** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/apimachinery** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/apiserver** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/client-go** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/component-base** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/cri-api** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/kube-scheduler** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/kubectl** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/kubelet** v0.32.0 -> v0.33.0-beta.0 * **k8s.io/pod-security-admission** v0.32.0 -> v0.33.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.72 -> v1.2.75 Previous release can be found at [v1.9.0](https://github.com/siderolabs/talos/releases/tag/v1.9.0) ## [Talos 1.10.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.10.0-alpha.2) (2025-03-05) Welcome to the v1.10.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### auditd Kernel parameter `talos.auditd.disabled=1` can be used to disable Talos built-in `auditd` service. ### cgroups v1 Talos Linux no longer supports `cgroupsv1` when running in non-container mode. The kernel argument `talos.unified_cgroup_hierarchy` is now ignored. ### Disk Image Talos starting with 1.10 will have disk images that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems. On first boot Talos determines the boot method and will wipe the unused bootloader. Secureboot disk-images will be sd-boot only. For ARM64 imager will still generate GRUB bootloader for Talos < 1.10 and for Talos >= 1.10 all ARM64 boot assets will use systemd-boot. Imager supports overwriting bootloader when generating a disk image via the Imager profile `output` option. Eg: ```yaml output: kind: image imageOptions: bootloader: sd-boot # supported options are sd-boot, grub, dual-boot ``` ### Driver Rebind Talos 1.10 now supports a new machine config document named `PCIDriverRebindConfig` that allows rebinding the driver of a PCI device to a different target driver. See the [documentation](https://www.talos.dev/v1.10/reference/configuration/hardware/pcidriverrebindconfig/) for more information. ### Ethernet Talos now provides `ethtool`-style Ethernet low-level configuration via `network/EthernetConfig` documents. Current status of the interface can be read by `talosctl get ethernetstatus`. ### Ingress Firewall Talos Ingress Firewall now filters access to Kubernetes NodePort services correctly. ### iSCSI Initiator Talos now generates `/etc/iscsi/initiatorname.iscsi` file based on the node identity which is tied to the lifecycle of the node. If using `iscsi-tools` extension, starting with Talos 1.10 would have a more deterministic IQN for the initiator node. Make sure to update any iSCSI targets to use the new initiator IQN. The iqn can be read by `talosctl read /etc/iscsi/initiatorname.iscsi` ### ISO Talos starting with 1.10 will have ISO's that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems. ### kube-apiserver Authorization Config When using `.cluster.apiServer.authorizationConfig` the user provided order for the authorizers is honoured and `Node` and `RBAC` authorizers are always added to the end if not explicitly specified. Eg: If user provides only `Webhook` authorizer, the final order will be `Webhook`, `Node`, `RBAC`. To provide a specific order for `Node` or `RBAC` explicitly, user can provide the authorizer in the order they want. Eg: ```yaml cluster: apiServer: authorizationConfig: - type: Node name: Node - type: Webhook name: Webhook webhook: connectionInfo: type: InClusterConfig ... - type: RBAC name: rbac ``` Usage of `authorization-mode` CLI argument will not support this form of customization. ### NVMe NQN Talos now generates `/etc/nvme/hostnqn` and `/etc/nvme/hostid` files based on the node identity which is tied to the lifecycle of the node. The NQN can be read by `talosctl read /etc/nvme/hostnqn` ### Fully bootstrapped builds Talos 1.10 is built with a toolchain based on [[Stageˣ]](https://stagex.tools/), which is a project building fully bootstrapped software. This change increases reproducibility, auditability and security of Talos builds. This also changes Talos root filesystem structure for unified /usr, with other directories symlinking to /usr/bin and /usr/lib. System extensions must move their directories accordingly for 1.10. ### Component Updates * Linux: 6.12.17 * CNI plugins: 1.6.2 * runc: 1.2.5 * containerd: 2.0.3 * etcd: 3.5.18 * Flannel: 0.26.4 * Kubernetes: 1.33.0-alpha.2 Talos is built with Go 1.24.0. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitry Sharshakov * Dmitriy Matrenichev * Dmitrii Sharshakov * Justin Garrison * Mathspy * Nico Berlee * Skyler Mäntysaari * Utku Ozdemir * ihelmer07 * 459below * Alexis La Goutte * Andrew Longwill * Andrew Symington * Christian Luetke-Stetzkamp * Christoph Hoopmann * Devin Buhl * Dominik Masur * Florian Grignon * Ganawa Juanah * Jason Benedicic * Joakim Nohlgård * K Birt * KillianCdP * L.J. Hanson * Louis SCHNEIDER * Marcel Hamer * Mikhail Petrov * Motte * Natalie Romana Albers * Orzelius * PRIHLOP * Ram * Robin Elfrink * Ryan Jacobs * Serge Logvinov * Thomas Gosteli * Tim Olson * Tine Jozelj * TomyLobo * bzub * greenpsi * sflotat2607 * suse-coder ### Changes
219 commits

* [`d4e3e957c`](https://github.com/siderolabs/talos/commit/d4e3e957cb98d3fe6ee6685a807c25fafb128423) fix(ci): fix integration tests * [`1849b5388`](https://github.com/siderolabs/talos/commit/1849b53881e1ab12b28f9d8b537c8e43d607b4ea) feat: update dependencies * [`88fc6bbeb`](https://github.com/siderolabs/talos/commit/88fc6bbebeff1c0db0e43fb0a83d2b03a973da8a) test: fix UKI preserving talos.config and image cache * [`ba8cd304d`](https://github.com/siderolabs/talos/commit/ba8cd304d2029c93c31135b2003b1f2f064ff29f) test: enable image-cache in the cron * [`28b5dc738`](https://github.com/siderolabs/talos/commit/28b5dc738cd7af5bb06604b2778f808827544ee0) test: fix reproduciblity test * [`50998038b`](https://github.com/siderolabs/talos/commit/50998038bb45e33438cccdd8fba4c156f0f7b0b5) feat: prefer sd-boot for UEFI * [`e831e52e0`](https://github.com/siderolabs/talos/commit/e831e52e01a47f34e982e9cfa397ca9722094a82) feat: add support for qla2xx * [`ec5c049a5`](https://github.com/siderolabs/talos/commit/ec5c049a5a5063289a97271c2d145d298f5f1a43) feat: update Kubernetes to 1.33.0-alpha.2 * [`ebfa82f35`](https://github.com/siderolabs/talos/commit/ebfa82f3558e5a44a332a0576416ce61f8235407) docs: update deprecated command * [`d79059a2c`](https://github.com/siderolabs/talos/commit/d79059a2c96565b1524b3869ad6b28f1cd8351da) chore: fix shutdown typo in shutdown sequence * [`a3f88d2ef`](https://github.com/siderolabs/talos/commit/a3f88d2ef5b726e1256a070a961bd4931d453a6a) fix: block NodePort services with ingress firewall * [`fd8131cb8`](https://github.com/siderolabs/talos/commit/fd8131cb86714b450334508abc0891eeaa2da9c8) feat: generate unified installer * [`ebfdb91b4`](https://github.com/siderolabs/talos/commit/ebfdb91b4cd36b48c36c1523dc74bc6e1860f815) fix: handle dynamic HTTP proxy settings for discovery client * [`d45eaeb74`](https://github.com/siderolabs/talos/commit/d45eaeb74cc43cc3154fcbce474958a613bc561b) fix: correctly map link names/aliases when using VIP operator * [`7c4e47c0c`](https://github.com/siderolabs/talos/commit/7c4e47c0c00e740bf0d63521baa1231354bc1966) chore: stop doing generate on each build * [`b1d410cb6`](https://github.com/siderolabs/talos/commit/b1d410cb6203f8a3847472db3990d2634bab22e2) feat: dual boot disk image * [`468e318ba`](https://github.com/siderolabs/talos/commit/468e318ba4137e1f11d231fe3ed66f10543073e3) fix: multiple fixes for dashboard/no data * [`3dd8d9aed`](https://github.com/siderolabs/talos/commit/3dd8d9aed8d311f84c61f3030fbf2031ee1d3df9) docs: update resetting-a-machine.md to include example of reset * [`7af8f6b2f`](https://github.com/siderolabs/talos/commit/7af8f6b2fa98f1ed4ad5adbcea6d54802013a086) feat: validate docker image references in upgrade options * [`c949f55e6`](https://github.com/siderolabs/talos/commit/c949f55e61b8c74202f2da50829c2e034e43682e) docs: remove typo on resetting a machine page * [`f5c097041`](https://github.com/siderolabs/talos/commit/f5c097041faac04808636703c94fe5d3ee208947) feat: add description to schema object defs * [`79ee304e1`](https://github.com/siderolabs/talos/commit/79ee304e11df7cfb2ccc6eeeb39ab6112975db45) chore: update enumer to a version that fixes Go 1.24 compatibility * [`46d67fe44`](https://github.com/siderolabs/talos/commit/46d67fe446edfabe23e3e8a91cc1f07436827c5e) chore: update Go to 1.24, update pkgs * [`7f1dd2669`](https://github.com/siderolabs/talos/commit/7f1dd2669734f496afbec6812a814d70dbaee3b4) fix(ci): fix integration-misc crons * [`26a773d3f`](https://github.com/siderolabs/talos/commit/26a773d3f27572a01d146ed356be5e78f8dc23e0) docs: add a note about syslog sending messages to services * [`7ce053638`](https://github.com/siderolabs/talos/commit/7ce053638db9c9abd4d966d412986c07615a750e) fix: ignore digest part of images when checking version * [`ae1b00354`](https://github.com/siderolabs/talos/commit/ae1b003542d01fc565a8478c9de512c3ea929f3d) feat: support noclooud instance-id from dmi * [`58661dea7`](https://github.com/siderolabs/talos/commit/58661dea71a706eaf57f9813b9672395e820e756) docs: update getting-started.md * [`94cf9fb84`](https://github.com/siderolabs/talos/commit/94cf9fb8470b88fac6523953ebb083ecf31e4274) chore: fix spurious generate failures * [`32a34791e`](https://github.com/siderolabs/talos/commit/32a34791e2e61e77531ccc8f8be92c76c4b83514) fix: typo in Makefile target talosctl-freebsd-arm64 * [`1b4464c8a`](https://github.com/siderolabs/talos/commit/1b4464c8a65600b923d9790656f25e245db2e0aa) feat: update Kubernetes to 1.32.2 * [`9463ac23e`](https://github.com/siderolabs/talos/commit/9463ac23e77067f6dce2c22a33e3937357745303) fix: make ingress firewall filter traffic to nodeports * [`8531d91a1`](https://github.com/siderolabs/talos/commit/8531d91a1f20ecc587a1b76c13637ab3555718e9) fix: blockdevice transport detection * [`ce616d93a`](https://github.com/siderolabs/talos/commit/ce616d93a5799163ae278bac477c4f612197d109) fix: path for ca-certificates * [`f35b58779`](https://github.com/siderolabs/talos/commit/f35b58779e912aeec64e6fc0a9964e76e97f9a9f) fix: fix diff printing * [`bf0f910a1`](https://github.com/siderolabs/talos/commit/bf0f910a16ce3707cc5741b88a176671a0dd40b3) chore: provide more logging for dns requests * [`607998ba2`](https://github.com/siderolabs/talos/commit/607998ba20d62fa13233daf139eb3126ffa6569f) feat: support uki profiles via imager * [`711cf2d99`](https://github.com/siderolabs/talos/commit/711cf2d99ac9c16b7a48c20271ecc2c60a3f3d6d) fix: ignore errors to stop pods * [`142d75483`](https://github.com/siderolabs/talos/commit/142d754835785cd4edf088e2827854ffc8580262) fix: handle empty registry config * [`47f377b21`](https://github.com/siderolabs/talos/commit/47f377b21f546f1950ed43171d6b4f374ab7f721) feat: implement the last ethtool feature - channels * [`88cf69b8c`](https://github.com/siderolabs/talos/commit/88cf69b8c5c5f9fd47107289a717f1083ae12807) feat: multi profile UKIs * [`557faad75`](https://github.com/siderolabs/talos/commit/557faad759e4f21b7dedc3c69a61d2b3c31e6bc4) feat: update Linux to 6.12.13 * [`5dbf9e350`](https://github.com/siderolabs/talos/commit/5dbf9e35024192632aecda5fd817ab4558aced1a) refactor: implement volume mount controller * [`aa11e9abb`](https://github.com/siderolabs/talos/commit/aa11e9abb78d33ba66a167335b14fc79f4613ef9) fix: make image cache volume management less strict * [`26a62e342`](https://github.com/siderolabs/talos/commit/26a62e34211d642ddcdb0cff67013c0d4c640b78) docs: fix typo in Wireguard docs * [`0419f5d8b`](https://github.com/siderolabs/talos/commit/0419f5d8ba889faead5452af40fc70c8e1573084) feat: implement features in `ethtool`-like support * [`cd66fc6e8`](https://github.com/siderolabs/talos/commit/cd66fc6e8e7e5cf1a17c03de41c1d2e39cb71aa4) feat: use bootstrapped packages for building Talos * [`2b5bd5d1d`](https://github.com/siderolabs/talos/commit/2b5bd5d1dad65f653dfd77d363d0a76404099453) chore: upgrade siderolabs/go-loadbalancer * [`15191aa3e`](https://github.com/siderolabs/talos/commit/15191aa3e305feba6b5f8b084e6d9b7337e2143f) fix: extract cmdline multi profile UKIs * [`716f700da`](https://github.com/siderolabs/talos/commit/716f700da74608aa93c9d335ea17f0fea34865a6) feat: provide initial support for ethtool configuration * [`b726e2f9f`](https://github.com/siderolabs/talos/commit/b726e2f9f7057f1e7ed912bea28db3e4b63441cb) feat: update Flannel to 0.26.4 * [`98d56d4d6`](https://github.com/siderolabs/talos/commit/98d56d4d647d455acc7324d84df05881ebe46d34) chore: track opened grpc connections * [`5e28c8e03`](https://github.com/siderolabs/talos/commit/5e28c8e039aae14427571bdd9bf9813ee6220743) fix: image cache volume provisioning * [`c9667813d`](https://github.com/siderolabs/talos/commit/c9667813d2b515306a775dabbefad378dc74a0a9) chore: remove containerd importer * [`270ffb69a`](https://github.com/siderolabs/talos/commit/270ffb69a39a9b10e3d98c44579eec20de51ba67) fix: duplicate qemu drive ids * [`71ec41be1`](https://github.com/siderolabs/talos/commit/71ec41be18541c31e887037bad59a7a3395a2bb1) fix: build of Talos on non-Linux host * [`e2aa7c98c`](https://github.com/siderolabs/talos/commit/e2aa7c98ccebca727cac792e53db5722aa79e213) fix: installer with SecureBoot should contain UKIs * [`6e22c06c3`](https://github.com/siderolabs/talos/commit/6e22c06c3c4c96bb02d34c7f61633137cd03f6f5) release(v1.10.0-alpha.1): prepare release * [`3a2d9867b`](https://github.com/siderolabs/talos/commit/3a2d9867b5cc3236b1d1c7981e5794657f3c155e) fix: do not close client.Client.conn with finalizer * [`73f30ff25`](https://github.com/siderolabs/talos/commit/73f30ff25e0adb7a47e2153756e0ea94bd605568) feat: bump pkgs for udev update * [`aea90cb8f`](https://github.com/siderolabs/talos/commit/aea90cb8f1dbe7d5f67d35714825133728c1490d) docs: update hyper-v * [`b7165615f`](https://github.com/siderolabs/talos/commit/b7165615f86afd09ea85dc91090a40860ae6fc9a) fix: use local NTP for AWS platform * [`673ca4bcb`](https://github.com/siderolabs/talos/commit/673ca4bcb2448b3c252fccff0d243932c97fd893) fix: ensure proper closure of client.Client.conn with finalizer * [`19040ffd6`](https://github.com/siderolabs/talos/commit/19040ffd6ef128daaf48a820d8826186c82c68c5) fix: handle of PE sections with duplicate names * [`83489d348`](https://github.com/siderolabs/talos/commit/83489d348905352497da0f6dc042f3e7f05cd4d7) docs: add note about vmxnet and flannel conflict * [`f1292f5e7`](https://github.com/siderolabs/talos/commit/f1292f5e7af4110270475d8bcc4bd39519419e03) docs: add iscsi-tools extension to prerequisites * [`93b4a3740`](https://github.com/siderolabs/talos/commit/93b4a3740ba0c35e8b62cbf8c70058d1e53c3b8e) test: bump timeout on rotate CA test * [`42e166984`](https://github.com/siderolabs/talos/commit/42e16698453a687a4293e7cfeeb0e09d4f084217) feat: support kexec from uki * [`8da264946`](https://github.com/siderolabs/talos/commit/8da264946cda9b4803fd9f2f4dfd0ed25445843b) docs: add Orange Pi 5 to Image Factory platforms and documentation * [`c5fb62e2e`](https://github.com/siderolabs/talos/commit/c5fb62e2e32690aa0235b0911ded1888084496a8) feat: update Linux to 6.2.11 * [`83d007c16`](https://github.com/siderolabs/talos/commit/83d007c161e03311cede2153f35c32f608537290) feat: update etcd to 3.5.18 * [`edf7c3288`](https://github.com/siderolabs/talos/commit/edf7c328835273e2bc6dd23c646091e6a03aa2e9) fix: pe uki extract * [`70f72c5b0`](https://github.com/siderolabs/talos/commit/70f72c5b00bce791d692ec3a0e9a91aaf9d88031) docs: update multus.md * [`807a3cd29`](https://github.com/siderolabs/talos/commit/807a3cd291e2e2cb22946826bccb64671a29d901) refactor: all network merge controllers * [`ec8c4660e`](https://github.com/siderolabs/talos/commit/ec8c4660e277dc11b5e70c014a0238d48cf15bda) docs: update vmware.md * [`baf81cd49`](https://github.com/siderolabs/talos/commit/baf81cd4914470b06393d762f70d0a94f7a9fe32) fix(ci): k8s integration suite wait for resource * [`cd5e54903`](https://github.com/siderolabs/talos/commit/cd5e549039b17add0a2ce09713e1a034bb3efccf) feat: generate iso's with both UKI and grub * [`75673b6a3`](https://github.com/siderolabs/talos/commit/75673b6a38eeb6361c6e6aeb389e8dbaaacb8b0b) feat: provide stable symlinks in disk resources * [`f407c88e4`](https://github.com/siderolabs/talos/commit/f407c88e4678ff6d5edb940f5d54461104be3643) fix(ci): wait for longhorn node resource * [`601cdccb9`](https://github.com/siderolabs/talos/commit/601cdccb979640a6b2ffcba41cc698015b1dacde) feat: extract kernel/initrd from uki for grub * [`ff175b9fb`](https://github.com/siderolabs/talos/commit/ff175b9fbdb2ac92ac53351d32de130bd0676038) docs: update disk-encryption.md * [`a8d84e315`](https://github.com/siderolabs/talos/commit/a8d84e3155137a114ad00ad7ae321af033020e7d) docs: fix typos and add more explanations in docs * [`3a384240e`](https://github.com/siderolabs/talos/commit/3a384240ecf660d310f2df98327f018649ebaa6d) fix: invalid date field in iqn/nqn * [`82c9ec158`](https://github.com/siderolabs/talos/commit/82c9ec158e82efea80daaf76fef9fbd31c3eb823) chore(ci): add tests with longhorn v2 engine * [`689ea1dbf`](https://github.com/siderolabs/talos/commit/689ea1dbfe29d70d91e0b41d31fc696e2ff96665) fix: bring back disk UUID * [`7a712fad2`](https://github.com/siderolabs/talos/commit/7a712fad2abb916f397a8dd0aebf66e59ee75904) fix: disks with 4k sector size and systemd-boot * [`d62a34aaf`](https://github.com/siderolabs/talos/commit/d62a34aaf4e4ff7dad9f6dbeb59a67016c70fffb) feat: update tools/pkgs/extras * [`b9a8ad6ac`](https://github.com/siderolabs/talos/commit/b9a8ad6acafd64c4217ba914184592c0cfb97962) chore: de-hardcode list of extra images for image-cache test * [`683153a33`](https://github.com/siderolabs/talos/commit/683153a33c1069e7f7cadf4e3a70bde3f8ba3331) docs: remove the last mentions of `preserve` flag for Talos 1.8+ * [`33c7f4195`](https://github.com/siderolabs/talos/commit/33c7f4195816988af6f70199fdb4a31d027fa746) docs: fix typo an MacOS to on MacOS * [`21cff3919`](https://github.com/siderolabs/talos/commit/21cff3919b80f33f837b19728500fcb91e7caf8f) chore(ci): fio benchmark results as separate artifacts * [`0b7fc7cdf`](https://github.com/siderolabs/talos/commit/0b7fc7cdfea651a6f16db3f346473505d8df3e78) fix: abort node watch on hostname change * [`99ba53941`](https://github.com/siderolabs/talos/commit/99ba53941cecdc54c0ececa9876b25a7fc7668a5) docs: remove the mention of `preserve` flag for Talos 1.8+ * [`bde516fde`](https://github.com/siderolabs/talos/commit/bde516fde62a25dd60691a9a3b6f3d30de11dad1) chore(ci): rework iscsi-tools extensions test * [`e1efbf656`](https://github.com/siderolabs/talos/commit/e1efbf656ae96ecedba1c132608c3ad2d3ae4a66) refactor: extract platform metadata into Talos machinery * [`79987c05d`](https://github.com/siderolabs/talos/commit/79987c05dcd39ca646c2d73c1e25488504f13a60) feat: generate iqn and nqn files * [`0cab6ed17`](https://github.com/siderolabs/talos/commit/0cab6ed170708549d69c04b163744854de0aa8f2) docs: update troubleshooting.md * [`921e10254`](https://github.com/siderolabs/talos/commit/921e10254d443c459a9775368ca080ecba273321) chore: update Go to 1.23.5 * [`399d53b54`](https://github.com/siderolabs/talos/commit/399d53b543f6ca99f13d28313ae77b3472b0f728) fix: ignore forbidden error when waiting for pod eviction * [`8dea57a81`](https://github.com/siderolabs/talos/commit/8dea57a81b8393b518da60951713c711659291f9) fix: make etc binds read-only * [`63157dcb4`](https://github.com/siderolabs/talos/commit/63157dcb496ca767bfbff9e1b86f14277a44cdb7) docs: update SideroLinkConfig example * [`fc7080e34`](https://github.com/siderolabs/talos/commit/fc7080e34b990d2d50ec1e40734437ccd0ee95f7) chore: clear cache after updating upstreams * [`51e0f273f`](https://github.com/siderolabs/talos/commit/51e0f273f9199b8320cd5da247c702a4319a92c5) docs: update documentation for Talos 1.9.2 * [`e06b14112`](https://github.com/siderolabs/talos/commit/e06b14112d2c978e3f6b5c4446090a7ae533ead9) feat: update Kubernetes to 1.32.1 * [`4310b290d`](https://github.com/siderolabs/talos/commit/4310b290d5cff9697f86cc24f1c281e62cb7d72f) fix: generate UKI only if actually needed * [`a8cd99102`](https://github.com/siderolabs/talos/commit/a8cd991026fe7290013b7504a4e87af46c49d25b) docs: update OpenEBS Mayastor installation * [`cf45f4764`](https://github.com/siderolabs/talos/commit/cf45f4764ddd979fa81576833d9630eadea24f41) docs: add Radxa ROCK 5B docs to Single Board Computer section * [`b21bdc5e5`](https://github.com/siderolabs/talos/commit/b21bdc5e501bc2244e3e487827ffba79075f6642) chore(ci): save csi tests fio results * [`01c86832c`](https://github.com/siderolabs/talos/commit/01c86832cbbbe0b81b9500032f94298fd6e90b58) chore(ci): add test for OpenEBS MayaStor * [`c77483510`](https://github.com/siderolabs/talos/commit/c774835103ad139b44d7e4e13c003e2b13160347) test: update `talosctl debug air-gapped` * [`ddd695d93`](https://github.com/siderolabs/talos/commit/ddd695d933d39920da42219ba8b3d39b0681a3ea) feat: update containerd to 2.0.2 * [`da2e81120`](https://github.com/siderolabs/talos/commit/da2e81120f7336d9633a98523e05d91f5750434f) fix: add informer resync period for node status watcher * [`9b957df64`](https://github.com/siderolabs/talos/commit/9b957df64680a97a16575db67d4af27cfc0ef7d2) chore: uki code restructure * [`e41a99525`](https://github.com/siderolabs/talos/commit/e41a995253428dde437eecec52cabfb4c80f90ea) fix: kube-apiserver authorizers order * [`db4ca5668`](https://github.com/siderolabs/talos/commit/db4ca5668ac0d85a98a5ea022f6546526d20aff1) feat: add a kernel parameter to disable built-in auditd * [`faa149003`](https://github.com/siderolabs/talos/commit/faa1490033df0a843010fa7154096d84f415afce) feat: update Linux to 6.12.9 * [`8de19758d`](https://github.com/siderolabs/talos/commit/8de19758dafce802c0f93a63ae3083b5ad17162d) fix: a couple of imager panics/crashes * [`5bc3e34cb`](https://github.com/siderolabs/talos/commit/5bc3e34cb3a6fd8e3eb5d02dd612cf3cf9dc499f) fix: detect GPT before ZFS * [`ed7e47d15`](https://github.com/siderolabs/talos/commit/ed7e47d158e064204b2f14f9ff378bea70e9524e) refactor: drop usage of objcopy to generate UKIs * [`edf5c5e29`](https://github.com/siderolabs/talos/commit/edf5c5e29bc76299c63bb04f1d97a030ecb9b3f0) fix: extfs repair and resize * [`6e32ea5b7`](https://github.com/siderolabs/talos/commit/6e32ea5b7f1a22500014ecb365e13af36034187a) fix: merge of VolumeConfig documents with sizes * [`1be5f8ff2`](https://github.com/siderolabs/talos/commit/1be5f8ff25ac7042ee3334f657d6604ec5f8501d) feat: update Linux to 6.12.8 * [`e6a4583ba`](https://github.com/siderolabs/talos/commit/e6a4583ba862da9f49ab0bd0cb6bc8436723bc67) feat: support generating unsigned UKIs * [`bbd6067d4`](https://github.com/siderolabs/talos/commit/bbd6067d426fb2be22ff8935f415ab6d729d8f19) fix: partition alignment on disks with 4k sectors * [`84fcc976f`](https://github.com/siderolabs/talos/commit/84fcc976f8da5af310771e1835a0347df5bcc97d) fix: yet another dashboard panic * [`6d605fc85`](https://github.com/siderolabs/talos/commit/6d605fc8595e2f06e43529966e396f2ae403c76c) fix: disable NRI plugin in a different way * [`499695e24`](https://github.com/siderolabs/talos/commit/499695e24ea02ffc2fd8c92276d5de41b0d4919e) fix: request previous IP address in discovery * [`cc84caf8c`](https://github.com/siderolabs/talos/commit/cc84caf8c0dffd9d59f360f84967c524be9ba369) docs: update Cilium documentation * [`fa5300d91`](https://github.com/siderolabs/talos/commit/fa5300d910a537f03939fcbf6362abdd8fa607dd) chore: revert: drop deprecated allowSchedulingOnMasters * [`0abb3dabf`](https://github.com/siderolabs/talos/commit/0abb3dabf6d50b9c1176af683ad74234334f822d) docs: fix command to wait for ceph-rook HEALTH_OK * [`32c67c27c`](https://github.com/siderolabs/talos/commit/32c67c27c393c989f9d70ccb8506c4735f70d494) chore: drop deprecated allowSchedulingOnMasters * [`ae6d065be`](https://github.com/siderolabs/talos/commit/ae6d065beb4897a1b877ecb30b06be456befbf91) fix: mount selinuxfs only when SELinux is enabled * [`5ccbf4bcd`](https://github.com/siderolabs/talos/commit/5ccbf4bcdbe9aa2096320d17eb2deab6a062faf9) feat: enable `configfs` * [`59582496d`](https://github.com/siderolabs/talos/commit/59582496d5fe419f833703be8e956163b6241d15) feat: bring in partity with sd-257 * [`83d84a831`](https://github.com/siderolabs/talos/commit/83d84a831862c774b9bc2adc2e11e00bf2a79912) chore(ci): better zfs checks * [`650eb3a4f`](https://github.com/siderolabs/talos/commit/650eb3a4f2d89d173cdd6581a6d1232511a8e219) refactor: rewrite cloud uploader to use AWS SDK Go v2 * [`01bf8449b`](https://github.com/siderolabs/talos/commit/01bf8449b917ece76336ca7f0eb11fd877195025) fix: update field name for bus path disk selector * [`e915c98d5`](https://github.com/siderolabs/talos/commit/e915c98d583e5901c1c2efe38efa656b39d72360) fix: exclude disks with empty transport for disk selector * [`b7a7fdc4b`](https://github.com/siderolabs/talos/commit/b7a7fdc4b8a715157bfa2614c9541b96643cd2ba) refactor: generate /etc/os-release file static way * [`e79c9e127`](https://github.com/siderolabs/talos/commit/e79c9e12772c998ff5b3e401efd7f074f85e5cef) chore(ci): drop equinix metal e2e-test * [`418945444`](https://github.com/siderolabs/talos/commit/418945444135c6d9e2e5960e7b9cbd754084fea2) fix: build of talosctl on non-Linux platforms * [`4761a9e6a`](https://github.com/siderolabs/talos/commit/4761a9e6aa0bf619a564807d02ebce030384d6a1) chore: update dependencies * [`f98efb333`](https://github.com/siderolabs/talos/commit/f98efb333f89b8493c55b91698c917437b7af310) fix: ignore member not found error on leave cluster * [`b72bda0a4`](https://github.com/siderolabs/talos/commit/b72bda0a420f75ea0439cc0240dcf6d3363e5d48) fix: talosctl support and race tests * [`27233cf0f`](https://github.com/siderolabs/talos/commit/27233cf0fcf4031cbc8001504bed67b6d4a104f9) test: use node informer instead of raw watch * [`5dc15e8db`](https://github.com/siderolabs/talos/commit/5dc15e8db459ac632f0ae106e1cfc7eaab672adf) fix: update go-blockdevice to v2.0.9 * [`5f3acd0f2`](https://github.com/siderolabs/talos/commit/5f3acd0f26a35ac966d4ced01436f1dd3c03648b) fix: use correct default search domain * [`7e5d36d46`](https://github.com/siderolabs/talos/commit/7e5d36d469ff01153f40b16ab722f0ebe25d41ae) fix: pci driver rebind config validation * [`4b97bbc3f`](https://github.com/siderolabs/talos/commit/4b97bbc3fee1257d0d21be25e21493bfd1f45a80) fix: pull in containerd CNI deadlock fix * [`066480722`](https://github.com/siderolabs/talos/commit/0664807229e0688f092a453cbd3121dbe189ca39) test: fix apparmor tests * [`82ea44a6b`](https://github.com/siderolabs/talos/commit/82ea44a6b2aa0a35861ca454a09503a81332f824) fix: reduce installer image * [`78b3e7f4f`](https://github.com/siderolabs/talos/commit/78b3e7f4f1870085b719971c6f92dc866fe1e9d0) fix: get next rule number for IPv6 in the appropriate chain * [`675854aa0`](https://github.com/siderolabs/talos/commit/675854aa03b3913da3481337d995c206174cf004) docs: fix two typos * [`f70b7386a`](https://github.com/siderolabs/talos/commit/f70b7386ac3125f3b8ab6b1765338c7e3445ae5c) test: add a xfs makefs test * [`8212e4864`](https://github.com/siderolabs/talos/commit/8212e4864d11e69ed63be3f4e608e9ccbc788cc4) refactor: use quirks in kernel args * [`b4aa5189d`](https://github.com/siderolabs/talos/commit/b4aa5189d4d4565a42ad7ac8de24c424a215b42f) release(v1.10.0-alpha.0): prepare release * [`bd85bd5b7`](https://github.com/siderolabs/talos/commit/bd85bd5b731463a42b7c82c66e9add251a280d26) fix: fix `Failed to initialize SELinux labeling handle` udev error * [`73c82e3e5`](https://github.com/siderolabs/talos/commit/73c82e3e5625ec1899f93312a671dfe6dffaea61) feat: bring Linux 6.12.6, CNI plugins 1.6.1 * [`c12b52491`](https://github.com/siderolabs/talos/commit/c12b52491456d1e52204eb290d0686a317358c7c) docs: document Kubernetes service registry incompat with K8s 1.32 * [`a5660ed77`](https://github.com/siderolabs/talos/commit/a5660ed778108843fe15b2b1582dd6556cf52b6c) feat: pcirebind controller * [`4c3261626`](https://github.com/siderolabs/talos/commit/4c3261626fa3f5ac36df71ec878f103a7c85c5c5) docs: fix several typos * [`fb3675321`](https://github.com/siderolabs/talos/commit/fb36753216cba7740040f2ec117c783221f66192) fix: dashboard crash on CPU data * [`dec0185c8`](https://github.com/siderolabs/talos/commit/dec0185c8505a7d43244fdb01f7a5decc77d116d) chore: reduce memory usage for secureboot functions * [`cee6c60a0`](https://github.com/siderolabs/talos/commit/cee6c60a0fc301b22c50fdf8bd2fc1d2b7ba3d54) fix: make talosctl time work with PTP time sync * [`f75604313`](https://github.com/siderolabs/talos/commit/f75604313d535180c38b33df53253ad4acba2ec1) chore: support gcr.io auth for cache and image gen * [`6ef2596da`](https://github.com/siderolabs/talos/commit/6ef2596da7b7e8be90e5b981621461352be7b134) docs: improve Hetzner documentation * [`7d39b9ec2`](https://github.com/siderolabs/talos/commit/7d39b9ec2bdd7883116626bf889c1331717f8438) feat: remove cgroupsv1 in non-container mode * [`8003536c7`](https://github.com/siderolabs/talos/commit/8003536c7ca20356adcd900e64463bd166d445af) fix: restore previous disk serial fetching * [`03116ef9b`](https://github.com/siderolabs/talos/commit/03116ef9bd2a215c20a2c4c7db133dd857ce2b16) chore: prepare for Talos 1.10 * [`00682fdd6`](https://github.com/siderolabs/talos/commit/00682fdd6e8fa23c6f9782840ea3e2b8ef250f66) docs: activate 1.9 docs as default * [`bea05f5c9`](https://github.com/siderolabs/talos/commit/bea05f5c9b6ce6f5d067eb357d26e30a49154b21) docs: update deploying-cilium.md * [`284ab1179`](https://github.com/siderolabs/talos/commit/284ab11794b3b076aa9ab2bb756e02292d854751) feat: support link altnames/aliases * [`5bfd829bf`](https://github.com/siderolabs/talos/commit/5bfd829bf9c8e46b6c51174be4b764d4c94b3320) docs: fix 'containter' typo * [`8d151b771`](https://github.com/siderolabs/talos/commit/8d151b771debc51d3fa40dfafc7a2e43f955a634) docs: clarify TALOSCONFIG for AWS * [`0ef19171f`](https://github.com/siderolabs/talos/commit/0ef19171f738e46346dfae71f43b8f7b47bf257d) fix: renovate typo * [`c568adc7d`](https://github.com/siderolabs/talos/commit/c568adc7dcd52c34924acc1eae849a2ca5b5a4d5) fix: renovate config * [`ec2e24fd9`](https://github.com/siderolabs/talos/commit/ec2e24fd9617db34e3bec753b5fe720670fa31a4) fix: match MAC addresses case-insensitive (nocloud) * [`41a0c440a`](https://github.com/siderolabs/talos/commit/41a0c440ad3f4de2a2ba9198d22609c55bdaf61b) chore: rekres for renovate changes * [`a49bb9ee4`](https://github.com/siderolabs/talos/commit/a49bb9ee45346268b26d3b9cff4dd017bfb9c829) feat: update Linux to 6.12.5 * [`b15917ecc`](https://github.com/siderolabs/talos/commit/b15917ecc626781e13de0e84b794ab77c97b3159) chore: add more debugging logs for META and volumes * [`2b1b326f0`](https://github.com/siderolabs/talos/commit/2b1b326f08966615a5a2f8708f94e6d1355773a7) docs: mention different paths for OpenEBS * [`9470e842f`](https://github.com/siderolabs/talos/commit/9470e842fca2d7dd0dae185bff7210a8af355445) test: cleanup failed Kubernetes pods * [`c9c685150`](https://github.com/siderolabs/talos/commit/c9c6851504fcda7b66395fbbba1fbc8b0e085d4a) fix: node identity flip * [`590c01657`](https://github.com/siderolabs/talos/commit/590c0165712aee60e752766d6bd3875443c353cb) feat: update containerd to v2.0.1 * [`18fa5a258`](https://github.com/siderolabs/talos/commit/18fa5a25876f41760ce8da5e918222e04b81949a) docs: update image-cache doc for iso * [`ab5bb6884`](https://github.com/siderolabs/talos/commit/ab5bb688420986a356aed55513a1dbd25de323e2) fix: generate and serve registries with port * [`58236066d`](https://github.com/siderolabs/talos/commit/58236066ddbcd7c401e945b70555ff315a2458f7) fix: support image cache on VFAT USB stick * [`e193a5071`](https://github.com/siderolabs/talos/commit/e193a507149c05e341abe019de219fe0b1bc83e3) fix: image cache integration test * [`08ee400fd`](https://github.com/siderolabs/talos/commit/08ee400fdbde368a54d6777cc31ceb91e1968ad2) test: fix flaky test NodeAddressSort * [`d45e8d1d1`](https://github.com/siderolabs/talos/commit/d45e8d1d1da28ca1b311198588d723cb491527eb) feat: update Kubernetes to 1.32.0 * [`136b12912`](https://github.com/siderolabs/talos/commit/136b12912165d5eb5c7c716b7f7dfcfbc42b08d4) chore: drop semicolon for supporting vfat filesystems * [`3e9e027ef`](https://github.com/siderolabs/talos/commit/3e9e027efbd2988f72eb2da0c1ab0e83ba52b950) test: add an option to boot from an USB stick * [`ef8c3e3b3`](https://github.com/siderolabs/talos/commit/ef8c3e3b3b245f7ffefa6c19930d5a0925ce666b) docs: fix typo in multus.md * [`d54414add`](https://github.com/siderolabs/talos/commit/d54414add4e4df1b5a7b166f155cdcca512d4ee2) fix: authorization config gen * [`cce72cfe8`](https://github.com/siderolabs/talos/commit/cce72cfe86beeb7ada9641df611046f4789e3bd8) docs: replace deprecated Hetzner server plans * [`81805103d`](https://github.com/siderolabs/talos/commit/81805103deada24b12b7d7861b2df5a5c788c86b) chore: enable proper parallel usage of TestDepth * [`e1b824eba`](https://github.com/siderolabs/talos/commit/e1b824ebada3d3dad9d2793fd12b5a948d8b51b5) docs: update ceph-with-rook.md * [`470b75563`](https://github.com/siderolabs/talos/commit/470b75563add4ce5bbce312c1e3dc783e63af1fa) fix: use mtu network option for podman * [`61b1489a0`](https://github.com/siderolabs/talos/commit/61b1489a0f0868c5b7e124544520bc46badef85c) fix: order volume config by the requested size * [`bc3039acd`](https://github.com/siderolabs/talos/commit/bc3039acdbc57e6be16a1bc6555894dff2da65c9) feat: update runc to 1.2.3 * [`30016a0a8`](https://github.com/siderolabs/talos/commit/30016a0a8d98d42e01c4d32acf9e600777d72d57) fix: avoid nil-pointer-panic in `RegistriesConfigController` * [`fe0457152`](https://github.com/siderolabs/talos/commit/fe045715277a4678b8e8c9632ec71e86bf17ace0) fix: power on the machine on reboot request in qemu power api * [`10da553ef`](https://github.com/siderolabs/talos/commit/10da553ef0dde5f87f09321400239baa51929a36) docs: build what's new for 1.9 * [`d946ccae3`](https://github.com/siderolabs/talos/commit/d946ccae31b87559a06cb1cefcefe8f937b73d8b) feat: update Linux to 6.12.4 * [`707a77bf6`](https://github.com/siderolabs/talos/commit/707a77bf64190470bf84c91cdff185981e80a31b) test: fix user namespace test, TPM2 fixes * [`c3537b2f5`](https://github.com/siderolabs/talos/commit/c3537b2f5491a890f626ba8fc47034d5059808af) feat: update Linux to 6.12.3 * [`cb4d9d673`](https://github.com/siderolabs/talos/commit/cb4d9d673432e4a0fba0d87bc64fde620d991082) docs: fix a few mistakes in release notes * [`c4724fc97`](https://github.com/siderolabs/talos/commit/c4724fc97598d8764b00fb56971d997a349a92e5) chore: add integration tests for image-cache * [`07220fe7f`](https://github.com/siderolabs/talos/commit/07220fe7f5a22444f7a085f5868f628ddd912b6d) fix: install iptables-nft to the host * [`14841750b`](https://github.com/siderolabs/talos/commit/14841750bf2fc09a9de0b32a7af0dc3f76e1019a) chore: add version compatibility for Talos 1.10 * [`852baf819`](https://github.com/siderolabs/talos/commit/852baf819d453a3d8d58ae9f029e280ae75e0cb1) feat: support vlan/bond in v1, vlan in v2 for nocloud * [`dd61ad861`](https://github.com/siderolabs/talos/commit/dd61ad86105c07c1ff8a101a0542af61699f0df3) fix: lock provisioning order of user disk partitions * [`d0773ff09`](https://github.com/siderolabs/talos/commit/d0773ff09df84b2dac8ecadc91023596050ce098) chore: update Go to 1.23.4 * [`7d6507189`](https://github.com/siderolabs/talos/commit/7d6507189ff9a99b3b05ee9528701b65af4ad147) feat: implement new address sorting algorithm * [`9081506d6`](https://github.com/siderolabs/talos/commit/9081506d6cde26d60a29f08a090e28da501e4bd1) feat: add process scheduling options * [`77e9db4ab`](https://github.com/siderolabs/talos/commit/77e9db4abf9c9b694d60c8803b436121dfe30ccd) test: use two workers in qemu tests by default * [`5a4bdf62a`](https://github.com/siderolabs/talos/commit/5a4bdf62a9bf1387b6489eaf2c9cc0770aa0b68c) feat: update Kubernetes to 1.32.0-rc.1 * [`d99bcc950`](https://github.com/siderolabs/talos/commit/d99bcc95031037f4b0990419d2ce1fd4280cbde9) chore: refactor mergeDNSServers func * [`0cde08d8b`](https://github.com/siderolabs/talos/commit/0cde08d8be1ad62c49fed148fd331ea5a212df4c) docs: add Turing RK1 docs to Single Board Computer section

### Changes since v1.10.0-alpha.1
57 commits

* [`d4e3e957c`](https://github.com/siderolabs/talos/commit/d4e3e957cb98d3fe6ee6685a807c25fafb128423) fix(ci): fix integration tests * [`1849b5388`](https://github.com/siderolabs/talos/commit/1849b53881e1ab12b28f9d8b537c8e43d607b4ea) feat: update dependencies * [`88fc6bbeb`](https://github.com/siderolabs/talos/commit/88fc6bbebeff1c0db0e43fb0a83d2b03a973da8a) test: fix UKI preserving talos.config and image cache * [`ba8cd304d`](https://github.com/siderolabs/talos/commit/ba8cd304d2029c93c31135b2003b1f2f064ff29f) test: enable image-cache in the cron * [`28b5dc738`](https://github.com/siderolabs/talos/commit/28b5dc738cd7af5bb06604b2778f808827544ee0) test: fix reproduciblity test * [`50998038b`](https://github.com/siderolabs/talos/commit/50998038bb45e33438cccdd8fba4c156f0f7b0b5) feat: prefer sd-boot for UEFI * [`e831e52e0`](https://github.com/siderolabs/talos/commit/e831e52e01a47f34e982e9cfa397ca9722094a82) feat: add support for qla2xx * [`ec5c049a5`](https://github.com/siderolabs/talos/commit/ec5c049a5a5063289a97271c2d145d298f5f1a43) feat: update Kubernetes to 1.33.0-alpha.2 * [`ebfa82f35`](https://github.com/siderolabs/talos/commit/ebfa82f3558e5a44a332a0576416ce61f8235407) docs: update deprecated command * [`d79059a2c`](https://github.com/siderolabs/talos/commit/d79059a2c96565b1524b3869ad6b28f1cd8351da) chore: fix shutdown typo in shutdown sequence * [`a3f88d2ef`](https://github.com/siderolabs/talos/commit/a3f88d2ef5b726e1256a070a961bd4931d453a6a) fix: block NodePort services with ingress firewall * [`fd8131cb8`](https://github.com/siderolabs/talos/commit/fd8131cb86714b450334508abc0891eeaa2da9c8) feat: generate unified installer * [`ebfdb91b4`](https://github.com/siderolabs/talos/commit/ebfdb91b4cd36b48c36c1523dc74bc6e1860f815) fix: handle dynamic HTTP proxy settings for discovery client * [`d45eaeb74`](https://github.com/siderolabs/talos/commit/d45eaeb74cc43cc3154fcbce474958a613bc561b) fix: correctly map link names/aliases when using VIP operator * [`7c4e47c0c`](https://github.com/siderolabs/talos/commit/7c4e47c0c00e740bf0d63521baa1231354bc1966) chore: stop doing generate on each build * [`b1d410cb6`](https://github.com/siderolabs/talos/commit/b1d410cb6203f8a3847472db3990d2634bab22e2) feat: dual boot disk image * [`468e318ba`](https://github.com/siderolabs/talos/commit/468e318ba4137e1f11d231fe3ed66f10543073e3) fix: multiple fixes for dashboard/no data * [`3dd8d9aed`](https://github.com/siderolabs/talos/commit/3dd8d9aed8d311f84c61f3030fbf2031ee1d3df9) docs: update resetting-a-machine.md to include example of reset * [`7af8f6b2f`](https://github.com/siderolabs/talos/commit/7af8f6b2fa98f1ed4ad5adbcea6d54802013a086) feat: validate docker image references in upgrade options * [`c949f55e6`](https://github.com/siderolabs/talos/commit/c949f55e61b8c74202f2da50829c2e034e43682e) docs: remove typo on resetting a machine page * [`f5c097041`](https://github.com/siderolabs/talos/commit/f5c097041faac04808636703c94fe5d3ee208947) feat: add description to schema object defs * [`79ee304e1`](https://github.com/siderolabs/talos/commit/79ee304e11df7cfb2ccc6eeeb39ab6112975db45) chore: update enumer to a version that fixes Go 1.24 compatibility * [`46d67fe44`](https://github.com/siderolabs/talos/commit/46d67fe446edfabe23e3e8a91cc1f07436827c5e) chore: update Go to 1.24, update pkgs * [`7f1dd2669`](https://github.com/siderolabs/talos/commit/7f1dd2669734f496afbec6812a814d70dbaee3b4) fix(ci): fix integration-misc crons * [`26a773d3f`](https://github.com/siderolabs/talos/commit/26a773d3f27572a01d146ed356be5e78f8dc23e0) docs: add a note about syslog sending messages to services * [`7ce053638`](https://github.com/siderolabs/talos/commit/7ce053638db9c9abd4d966d412986c07615a750e) fix: ignore digest part of images when checking version * [`ae1b00354`](https://github.com/siderolabs/talos/commit/ae1b003542d01fc565a8478c9de512c3ea929f3d) feat: support noclooud instance-id from dmi * [`58661dea7`](https://github.com/siderolabs/talos/commit/58661dea71a706eaf57f9813b9672395e820e756) docs: update getting-started.md * [`94cf9fb84`](https://github.com/siderolabs/talos/commit/94cf9fb8470b88fac6523953ebb083ecf31e4274) chore: fix spurious generate failures * [`32a34791e`](https://github.com/siderolabs/talos/commit/32a34791e2e61e77531ccc8f8be92c76c4b83514) fix: typo in Makefile target talosctl-freebsd-arm64 * [`1b4464c8a`](https://github.com/siderolabs/talos/commit/1b4464c8a65600b923d9790656f25e245db2e0aa) feat: update Kubernetes to 1.32.2 * [`9463ac23e`](https://github.com/siderolabs/talos/commit/9463ac23e77067f6dce2c22a33e3937357745303) fix: make ingress firewall filter traffic to nodeports * [`8531d91a1`](https://github.com/siderolabs/talos/commit/8531d91a1f20ecc587a1b76c13637ab3555718e9) fix: blockdevice transport detection * [`ce616d93a`](https://github.com/siderolabs/talos/commit/ce616d93a5799163ae278bac477c4f612197d109) fix: path for ca-certificates * [`f35b58779`](https://github.com/siderolabs/talos/commit/f35b58779e912aeec64e6fc0a9964e76e97f9a9f) fix: fix diff printing * [`bf0f910a1`](https://github.com/siderolabs/talos/commit/bf0f910a16ce3707cc5741b88a176671a0dd40b3) chore: provide more logging for dns requests * [`607998ba2`](https://github.com/siderolabs/talos/commit/607998ba20d62fa13233daf139eb3126ffa6569f) feat: support uki profiles via imager * [`711cf2d99`](https://github.com/siderolabs/talos/commit/711cf2d99ac9c16b7a48c20271ecc2c60a3f3d6d) fix: ignore errors to stop pods * [`142d75483`](https://github.com/siderolabs/talos/commit/142d754835785cd4edf088e2827854ffc8580262) fix: handle empty registry config * [`47f377b21`](https://github.com/siderolabs/talos/commit/47f377b21f546f1950ed43171d6b4f374ab7f721) feat: implement the last ethtool feature - channels * [`88cf69b8c`](https://github.com/siderolabs/talos/commit/88cf69b8c5c5f9fd47107289a717f1083ae12807) feat: multi profile UKIs * [`557faad75`](https://github.com/siderolabs/talos/commit/557faad759e4f21b7dedc3c69a61d2b3c31e6bc4) feat: update Linux to 6.12.13 * [`5dbf9e350`](https://github.com/siderolabs/talos/commit/5dbf9e35024192632aecda5fd817ab4558aced1a) refactor: implement volume mount controller * [`aa11e9abb`](https://github.com/siderolabs/talos/commit/aa11e9abb78d33ba66a167335b14fc79f4613ef9) fix: make image cache volume management less strict * [`26a62e342`](https://github.com/siderolabs/talos/commit/26a62e34211d642ddcdb0cff67013c0d4c640b78) docs: fix typo in Wireguard docs * [`0419f5d8b`](https://github.com/siderolabs/talos/commit/0419f5d8ba889faead5452af40fc70c8e1573084) feat: implement features in `ethtool`-like support * [`cd66fc6e8`](https://github.com/siderolabs/talos/commit/cd66fc6e8e7e5cf1a17c03de41c1d2e39cb71aa4) feat: use bootstrapped packages for building Talos * [`2b5bd5d1d`](https://github.com/siderolabs/talos/commit/2b5bd5d1dad65f653dfd77d363d0a76404099453) chore: upgrade siderolabs/go-loadbalancer * [`15191aa3e`](https://github.com/siderolabs/talos/commit/15191aa3e305feba6b5f8b084e6d9b7337e2143f) fix: extract cmdline multi profile UKIs * [`716f700da`](https://github.com/siderolabs/talos/commit/716f700da74608aa93c9d335ea17f0fea34865a6) feat: provide initial support for ethtool configuration * [`b726e2f9f`](https://github.com/siderolabs/talos/commit/b726e2f9f7057f1e7ed912bea28db3e4b63441cb) feat: update Flannel to 0.26.4 * [`98d56d4d6`](https://github.com/siderolabs/talos/commit/98d56d4d647d455acc7324d84df05881ebe46d34) chore: track opened grpc connections * [`5e28c8e03`](https://github.com/siderolabs/talos/commit/5e28c8e039aae14427571bdd9bf9813ee6220743) fix: image cache volume provisioning * [`c9667813d`](https://github.com/siderolabs/talos/commit/c9667813d2b515306a775dabbefad378dc74a0a9) chore: remove containerd importer * [`270ffb69a`](https://github.com/siderolabs/talos/commit/270ffb69a39a9b10e3d98c44579eec20de51ba67) fix: duplicate qemu drive ids * [`71ec41be1`](https://github.com/siderolabs/talos/commit/71ec41be18541c31e887037bad59a7a3395a2bb1) fix: build of Talos on non-Linux host * [`e2aa7c98c`](https://github.com/siderolabs/talos/commit/e2aa7c98ccebca727cac792e53db5722aa79e213) fix: installer with SecureBoot should contain UKIs

### Changes from siderolabs/crypto
1 commit

* [`0d45dee`](https://github.com/siderolabs/crypto/commit/0d45deefbcdd4bd6b6e549433b859083df55fc16) chore: bump deps

### Changes from siderolabs/discovery-api
1 commit

* [`64513a6`](https://github.com/siderolabs/discovery-api/commit/64513a6c4fb31c6a043159d5caea1d153ea133a4) feat: rekres, regenerate proto files

### Changes from siderolabs/discovery-client
1 commit

* [`b3632c4`](https://github.com/siderolabs/discovery-client/commit/b3632c4a8cd96ae36337e83308ef447361b51537) feat: support extra dial options in the client

### Changes from siderolabs/extras
6 commits

* [`4102a78`](https://github.com/siderolabs/extras/commit/4102a783a23e298f3c7e600cb4dfb7a04888eaaf) feat: build hermetically using new bldr and pkgs * [`f4a110f`](https://github.com/siderolabs/extras/commit/f4a110f5f4b472743dc023413dca280bce491ec1) fix: build tc-redirect-tap as static binary * [`0840abb`](https://github.com/siderolabs/extras/commit/0840abb9b5e32560ff38577151fdc2f51812ce31) fix: pull in fixed CNI plugins from pkgs * [`52c217f`](https://github.com/siderolabs/extras/commit/52c217f693366bdf21772919ad94933fd160c5d4) feat: update dependencies * [`f755eb4`](https://github.com/siderolabs/extras/commit/f755eb483647d17e487f7cb62de8cc150a420c3c) chore: rekres to simplify `.kres.yaml` defaults * [`e5382fc`](https://github.com/siderolabs/extras/commit/e5382fc5f05d7ccfdb7c95819195caceac8ffcbf) chore: kresify renovate

### Changes from siderolabs/gen
1 commit

* [`5ae3afe`](https://github.com/siderolabs/gen/commit/5ae3afee65490ca9f4bd32ea41803ab3a17cad7e) chore: update hashtriemap implementation from the latest upstream

### Changes from siderolabs/go-circular
2 commits

* [`015a398`](https://github.com/siderolabs/go-circular/commit/015a398e79f2853714cd20d1135dc100f18b6c29) fix: replace static buffer allocation on growth * [`ed8685e`](https://github.com/siderolabs/go-circular/commit/ed8685e0cf9491d9a714e565e0e736439a94a73f) test: add more assertions for write length result

### Changes from siderolabs/go-debug
1 commit

* [`ea108ca`](https://github.com/siderolabs/go-debug/commit/ea108cacca8940426149e67ba00e414633e4ef3f) chore: add support for Go 1.24

### Changes from siderolabs/go-kubernetes
1 commit

* [`804cb44`](https://github.com/siderolabs/go-kubernetes/commit/804cb440c2299488c7c68185c53b91ffdfb8bf32) feat: add support for Kubernetes to 1.33

### Changes from siderolabs/go-loadbalancer
1 commit

* [`589c33a`](https://github.com/siderolabs/go-loadbalancer/commit/589c33a96ac74a8c0e36b09f534fca62afd6de81) chore: upgrade `upstream.List` and `loadbalancer.TCP` to Go 1.23

### Changes from siderolabs/go-talos-support
1 commit

* [`0f784bd`](https://github.com/siderolabs/go-talos-support/commit/0f784bd58b320543663679693c817515067f3021) fix: avoid deadlock on context cancel

### Changes from siderolabs/pkgs
50 commits

* [`347ad26`](https://github.com/siderolabs/pkgs/commit/347ad26815260d148a7aa42a20eafa5228cbc411) feat: update Linux 6.12.17, containerd 2.0.3 * [`40241af`](https://github.com/siderolabs/pkgs/commit/40241af0b4d3a34ba5b89fc3a815b9f401f0e203) feat: enable qla2xxx module * [`6fb00b4`](https://github.com/siderolabs/pkgs/commit/6fb00b45c1e4c50d26822f9bd0fd462ed0dfb712) fix: pull in kmod from tools * [`cc5317a`](https://github.com/siderolabs/pkgs/commit/cc5317adec817d406c1fad1b4871cd7319b56f97) fix: patch Linux with blackhole patch * [`08389dd`](https://github.com/siderolabs/pkgs/commit/08389dd2d97aa53e9ac5523a5512c5bbead371c5) chore: support vmdk and cp format for qemu-img * [`7774b08`](https://github.com/siderolabs/pkgs/commit/7774b08f03f5c096efdcc7863260916d78a7b8a9) feat: update Linux to 6.12.16, validate package structure * [`40d288c`](https://github.com/siderolabs/pkgs/commit/40d288c66d67cfb1d0073288179224d22bf6c41a) fix: imager deps * [`351a1a1`](https://github.com/siderolabs/pkgs/commit/351a1a1ece7a79226f46f03f9d904e1d5600716d) feat: add tools needed for imager * [`80351ca`](https://github.com/siderolabs/pkgs/commit/80351ca6201f5e5efb51b2a2a6a2058fa2512a90) fix: reproducibility tests * [`e1f11f0`](https://github.com/siderolabs/pkgs/commit/e1f11f0991c23f86694b49a9e0fc0f7f592d093d) fix: remove patches and other files from copy-only packages * [`8fff06b`](https://github.com/siderolabs/pkgs/commit/8fff06bac029313278c632321f511c2918585872) chore: bump xfsprogs to 6.12.0 * [`76a0316`](https://github.com/siderolabs/pkgs/commit/76a0316a84571c22eb0c6efd3ce51f3da54671c9) chore: systemd 257.3, runc 1.2.5, ipxe * [`359807b`](https://github.com/siderolabs/pkgs/commit/359807b4172e17fdcd1a1531070535d7ef772b20) feat: copy built packages, improve hermetic build * [`117a1d6`](https://github.com/siderolabs/pkgs/commit/117a1d6b48835310714166335d3821ac47b4c70a) feat: update Linux to 6.12.13 * [`85f8901`](https://github.com/siderolabs/pkgs/commit/85f890180058a0865515ed76ca39f76ff0fe20d7) feat: make pkgs build bootstrapped * [`5763e3e`](https://github.com/siderolabs/pkgs/commit/5763e3e0fe00cbd9010398e795085ba0377802e8) feat: update systemd to 257.2 * [`1e24b31`](https://github.com/siderolabs/pkgs/commit/1e24b31dc379251ad5248f94f548e5c7330f59ec) feat: update Linux to 6.12.11 * [`38749d1`](https://github.com/siderolabs/pkgs/commit/38749d1f08fcb46e522450c1ad530309a8fa327d) fix: build CNI plugins statically linked * [`5da83db`](https://github.com/siderolabs/pkgs/commit/5da83dbbe320768db8eb6175b1e7c5e8ff78389d) feat: bump NVIDIA driver versions * [`5934363`](https://github.com/siderolabs/pkgs/commit/59343630a024e48dfeba826eac45589d0bdcfb99) fix: certificates CA * [`57f492d`](https://github.com/siderolabs/pkgs/commit/57f492d4c3e51e01ab85d2727a7862b21ab21795) feat: bump dependencies * [`45b9ebe`](https://github.com/siderolabs/pkgs/commit/45b9ebed9437752c6516792678356a595f1ec62b) feat: update Linux to 6.2.10 * [`e00ad67`](https://github.com/siderolabs/pkgs/commit/e00ad677f0c7ef4005d26108143c3fe5e36aaab2) chore: rekres to fix reproducibility build * [`cfb4b0a`](https://github.com/siderolabs/pkgs/commit/cfb4b0a79490156864eab726debe20559d9c4240) feat: update Go to 1.23.5 * [`72f19a2`](https://github.com/siderolabs/pkgs/commit/72f19a2983e7abcb620ab57fae6e039158663f1a) feat: update containerd to v2.0.2 * [`17a80ee`](https://github.com/siderolabs/pkgs/commit/17a80eeb75b91211d4ffe8a910feb9fddcd1e585) feat: update Linux to 6.12.9 * [`c9d718d`](https://github.com/siderolabs/pkgs/commit/c9d718d3d6fd762ca3a649a14aa2d74e47d707e2) fix: adjust kernel options around ACPI/PCI/EFI * [`eb9d566`](https://github.com/siderolabs/pkgs/commit/eb9d56617faa56e42648a07b6756c18850e4a045) feat: update Linux to 6.12.8 * [`73e4353`](https://github.com/siderolabs/pkgs/commit/73e4353ad9e2dad6dc8544436776fd412c808d63) fix: update config-arm64 to add Rasperry Pi watchdog support * [`0ab2427`](https://github.com/siderolabs/pkgs/commit/0ab2427a8415d3f29cd4f52e3afd51f701aa5848) fix: dvb was missing I2C_MUX support and si2168 driver * [`c3ac8e2`](https://github.com/siderolabs/pkgs/commit/c3ac8e2d553b068dd982f5b9e48f6b1e0cfdd24d) chore: drop unused cert copy * [`e7eddcf`](https://github.com/siderolabs/pkgs/commit/e7eddcf9498634749a4241844660fd0e9d87fad4) feat: bump dependencies * [`0b00e86`](https://github.com/siderolabs/pkgs/commit/0b00e86ae92f821bdc19af73a5ba571b5051c89a) fix: patch containerd with CNI deadlock fix * [`9051c9a`](https://github.com/siderolabs/pkgs/commit/9051c9ac6f60e039c53248b52ba4ccd192e34b6b) feat: update Linux to 6.12.6 * [`6695012`](https://github.com/siderolabs/pkgs/commit/6695012e8d93d28ea70fc3ba32ed90770eea4363) chore: rekres to simplify `.kres.yaml` defaults * [`611ca38`](https://github.com/siderolabs/pkgs/commit/611ca38153fece4f2b34519325fbca22d34db7a0) chore: rekres to bring renovate under kres * [`a4c4215`](https://github.com/siderolabs/pkgs/commit/a4c4215e74b68765ada0745165b2e2fb5ee508f5) fix: drop cgroupsv1 controllers * [`28c909d`](https://github.com/siderolabs/pkgs/commit/28c909ddeaf0d33e0fc6c5fdf2333a18801cf178) feat: update Linux firmware to 20241210 * [`c40a9e9`](https://github.com/siderolabs/pkgs/commit/c40a9e9713b1fde14f7a967fd1be168bb905d7c9) feat: update Linux to 6.12.5 * [`d54ca83`](https://github.com/siderolabs/pkgs/commit/d54ca835a8868e5df55e2d0ffe3cb0dfa82a3395) feat: update containerd to v2.0.1 * [`86e3755`](https://github.com/siderolabs/pkgs/commit/86e3755deae2fc85d7e62bdcf82a54cb72fec6d5) fix: add CONFIG_INTEL_MEI_GSC_PROXY as module * [`8c31321`](https://github.com/siderolabs/pkgs/commit/8c3132135d5a0e01a9d66790b4b25c7c05e08fa5) feat: update ZFS to 2.2.7 * [`605f493`](https://github.com/siderolabs/pkgs/commit/605f493abfeac79151c02a776733011f19d6c43b) feat: update runc to v1.2.3 * [`1a55529`](https://github.com/siderolabs/pkgs/commit/1a555296764ab0ad83fb4eca6509bb64feff3b7b) feat: update Linux to 6.12.4 * [`52ba9a5`](https://github.com/siderolabs/pkgs/commit/52ba9a57358ef37ce3e4aa4033991dc77ad17fbb) feat: update Linux 6.12.3 * [`9cf35be`](https://github.com/siderolabs/pkgs/commit/9cf35bef274bb445e578f858a0a595b05b44a01f) feat: build host iptables with nftables support * [`71003a3`](https://github.com/siderolabs/pkgs/commit/71003a3c9bff00685917d6e272421a7206b1667e) feat: update Go to 1.23.4 * [`5b4d402`](https://github.com/siderolabs/pkgs/commit/5b4d402bd33f9313a21e4924be57aacce569f9ad) feat: build dvb kernel modules and CX23885 * [`b330af9`](https://github.com/siderolabs/pkgs/commit/b330af9b95d9115382c81f88b55c17b99f7ef355) chore: bring in KSPP recommendations * [`f81b190`](https://github.com/siderolabs/pkgs/commit/f81b190cc65dc93f9212d52cd95806ac79c170d2) feat: kernel driver support for RK3588 devices (Turing RK1)

### Changes from siderolabs/proto-codec
1 commit

* [`3235c29`](https://github.com/siderolabs/proto-codec/commit/3235c2984fa1bb3cd8d38c088127c46dd3d2860e) chore: bump deps

### Changes from siderolabs/siderolink
1 commit

* [`38e459e`](https://github.com/siderolabs/siderolink/commit/38e459e50c467791c9670a60ef41f58db246715a) chore: bump deps

### Changes from siderolabs/tools
19 commits

* [`fcee25b`](https://github.com/siderolabs/tools/commit/fcee25ba79e3663db2c0f20f371392e2b45c5f19) fix: revert kmod to 33 * [`6a71711`](https://github.com/siderolabs/tools/commit/6a7171177b5e9a4b579db3614d140a399430c3ab) fix: do not install man and locale for exported packages * [`3389ba2`](https://github.com/siderolabs/tools/commit/3389ba22509cb85e0625dcb6dbbee218fc56d33d) chore: move zlib to be an external package * [`d93b780`](https://github.com/siderolabs/tools/commit/d93b780e8f63cf20a524d3ea76bd4f79b787b5f3) chore: expose more tools * [`46be459`](https://github.com/siderolabs/tools/commit/46be459d3a46f1fa096a9e58cbf060404dd3cbe2) chore: remove systemd version * [`f33fbe4`](https://github.com/siderolabs/tools/commit/f33fbe42517d5a856b360133c6330692b09ba824) fix: install policycoreutils under correct prefix * [`758d61c`](https://github.com/siderolabs/tools/commit/758d61cd71c43ba2a65372dc75b811864e113a29) chore: update dependencies * [`f398a04`](https://github.com/siderolabs/tools/commit/f398a04953666fa468b02851187f3dc4a77c5a44) chore: update dependencies, hermetic build * [`9db33dd`](https://github.com/siderolabs/tools/commit/9db33dd7457e026176fdea964de6d489e67b5fa0) feat: update to Go 1.23.6 * [`ef0a679`](https://github.com/siderolabs/tools/commit/ef0a67955aa9191019e5ea2fe0fe572694606b02) fix: do not install anything to /usr/lib64 * [`35748ea`](https://github.com/siderolabs/tools/commit/35748eac6666b66099b16ccfcfe989e34ea16076) feat: fully bootstrapped build * [`7200845`](https://github.com/siderolabs/tools/commit/7200845be9d0318d23eb77a57e1b8992dd7e8187) feat: update dependencies * [`bc30a2a`](https://github.com/siderolabs/tools/commit/bc30a2a3ace873c80e4657b622e3142efb55cc28) feat: update Go to 1.23.5 * [`533b595`](https://github.com/siderolabs/tools/commit/533b5953d28213aae4d4ae576bedf5df84712458) chore: rekres to fix reproducibility * [`01568a5`](https://github.com/siderolabs/tools/commit/01568a5b42685c3ea19578a7f4d7ba07dc0f18cd) chore: use Make and Go from the toolchain image * [`0393558`](https://github.com/siderolabs/tools/commit/03935581049f82ff466defcc203c5bcc6db5b43a) feat: bump dependencies * [`7811a5f`](https://github.com/siderolabs/tools/commit/7811a5f2f23923cdfe5bfd47ee12ed9e88b29585) chore: rekres to simplify `.kres.yaml` defaults * [`0b8b905`](https://github.com/siderolabs/tools/commit/0b8b9054833d8187bb1f6209b2441719f6e62cfa) chore: kresify renovate config * [`fe34fb3`](https://github.com/siderolabs/tools/commit/fe34fb3d54ec9abe878a9304fbfc3e1e741c0ff4) feat: update Go to 1.23.4

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.2 -> v0.6.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.16.0 -> v1.17.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.8.0 -> v1.8.2 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.3.0 -> v1.3.1 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.3.0 -> v1.3.1 * **github.com/aws/aws-sdk-go-v2/config** v1.28.5 -> v1.29.8 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.20 -> v1.16.30 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.37.6 -> v1.38.0 * **github.com/aws/smithy-go** v1.22.1 -> v1.22.3 * **github.com/containerd/cgroups/v3** v3.0.4 -> v3.0.5 * **github.com/containerd/containerd/v2** v2.0.1 -> v2.0.3 * **github.com/containerd/platforms** v1.0.0-rc.0 -> v1.0.0-rc.1 * **github.com/containernetworking/plugins** v1.6.0 -> v1.6.2 * **github.com/cosi-project/runtime** v0.7.6 -> v0.10.0 * **github.com/docker/cli** v27.3.1 -> v28.0.1 * **github.com/docker/docker** v27.3.1 -> v28.0.1 * **github.com/foxboron/go-uefi** fab4fdf2f2f3 -> 69fb7dba244f * **github.com/gdamore/tcell/v2** v2.7.4 -> v2.8.1 * **github.com/google/cel-go** v0.22.1 -> v0.24.1 * **github.com/google/go-containerregistry** v0.20.2 -> v0.20.3 * **github.com/google/go-tpm** v0.9.1 -> v0.9.3 * **github.com/google/nftables** v0.2.0 -> v0.3.0 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.1.0 -> v2.3.0 * **github.com/hetznercloud/hcloud-go/v2** v2.17.0 -> v2.19.1 * **github.com/insomniacslk/dhcp** a3a4c1f04475 -> 8abf58130905 * **github.com/klauspost/compress** v1.17.11 -> v1.18.0 * **github.com/klauspost/cpuid/v2** v2.2.9 -> v2.2.10 * **github.com/mdlayher/netlink** v1.7.2 -> fbb4dce95f42 * **github.com/miekg/dns** v1.1.62 -> v1.1.63 * **github.com/opencontainers/runc** v1.2.2 -> v1.2.5 * **github.com/opencontainers/runtime-spec** v1.2.0 -> v1.2.1 * **github.com/rivo/tview** c76f7879f592 -> 17b7edb88c57 * **github.com/safchain/ethtool** v0.5.9 -> v0.5.10 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.30 -> v1.0.0-beta.32 * **github.com/siderolabs/crypto** v0.5.0 -> v0.5.1 * **github.com/siderolabs/discovery-api** v0.1.5 -> v0.1.6 * **github.com/siderolabs/discovery-client** v0.1.10 -> v0.1.11 * **github.com/siderolabs/extras** v1.9.0 -> v1.10.0-alpha.0-3-g4102a78 * **github.com/siderolabs/gen** v0.7.0 -> v0.8.0 * **github.com/siderolabs/go-blockdevice/v2** v2.0.7 -> v2.0.16 * **github.com/siderolabs/go-circular** v0.2.1 -> v0.2.2 * **github.com/siderolabs/go-debug** v0.4.0 -> v0.5.0 * **github.com/siderolabs/go-kubernetes** v0.2.17 -> v0.2.18 * **github.com/siderolabs/go-loadbalancer** v0.3.4 -> v0.4.0 * **github.com/siderolabs/go-talos-support** v0.1.1 -> v0.1.2 * **github.com/siderolabs/pkgs** v1.9.0-12-g9576b97 -> v1.10.0-alpha.0-49-g347ad26 * **github.com/siderolabs/proto-codec** v0.1.1 -> v0.1.2 * **github.com/siderolabs/siderolink** v0.3.11 -> v0.3.12 * **github.com/siderolabs/talos/pkg/machinery** v1.9.0 -> v1.10.0-alpha.1 * **github.com/siderolabs/tools** v1.9.0-1-geaad82f -> v1.10.0-alpha.0-18-gfcee25b * **github.com/spf13/cobra** v1.8.1 -> v1.9.1 * **github.com/spf13/pflag** v1.0.5 -> v1.0.6 * **github.com/thejerf/suture/v4** v4.0.5 -> v4.0.6 * **go.etcd.io/etcd/api/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/client/pkg/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/client/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/etcdutl/v3** v3.5.17 -> v3.5.18 * **golang.org/x/net** v0.32.0 -> v0.35.0 * **golang.org/x/oauth2** v0.24.0 -> v0.27.0 * **golang.org/x/sync** v0.10.0 -> v0.11.0 * **golang.org/x/sys** v0.28.0 -> v0.30.0 * **golang.org/x/term** v0.27.0 -> v0.29.0 * **golang.org/x/text** v0.21.0 -> v0.22.0 * **golang.org/x/time** v0.8.0 -> v0.10.0 * **golang.zx2c4.com/wireguard/wgctrl** 925a1e7659e6 -> a9ab2273dd10 * **google.golang.org/grpc** v1.68.1 -> v1.70.0 * **google.golang.org/protobuf** v1.35.2 -> v1.36.5 * **k8s.io/api** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/apimachinery** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/apiserver** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/client-go** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/component-base** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/cri-api** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/kube-scheduler** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/kubectl** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/kubelet** v0.32.0 -> v0.33.0-alpha.2 * **k8s.io/pod-security-admission** v0.32.0 -> v0.33.0-alpha.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.72 -> v1.2.73 Previous release can be found at [v1.9.0](https://github.com/siderolabs/talos/releases/tag/v1.9.0) ## [Talos 1.10.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.10.0-alpha.1) (2025-01-31) Welcome to the v1.10.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### auditd Kernel parameter `talos.auditd.disabled=1` can be used to disable Talos built-in `auditd` service. ### cgroups v1 Talos Linux no longer supports `cgroupsv1` when running in non-container mode. The kernel argument `talos.unified_cgroup_hierarchy` is now ignored. ### Driver Rebind Talos 1.10 now supports a new machine config document named `PCIDriverRebindConfig` that allows rebinding the driver of a PCI device to a different target driver. See the [documentation](https://www.talos.dev/v1.10/reference/configuration/hardware/pcidriverrebindconfig/) for more information. ### iSCSI Initiator Talos now generates `/etc/iscsi/initiatorname.iscsi` file based on the node identity which is tied to the lifecycle of the node. If using `iscsi-tools` extension, starting with Talos 1.10 would have a more deterministic IQN for the initiator node. Make sure to update any iSCSI targets to use the new initiator IQN. The iqn can be read by `talosctl read /etc/iscsi/initiatorname.iscsi` ### ISO Talos starting with 1.10 will have ISO's that will use GRUB only for legacy BIOS and systemd-boot for modern UEFI systems. ### kube-apiserver Authorization Config When using `.cluster.apiServer.authorizationConfig` the user provided order for the authorizers is honoured and `Node` and `RBAC` authorizers are always added to the end if not explicitly specified. Eg: If user provides only `Webhook` authorizer, the final order will be `Webhook`, `Node`, `RBAC`. To provide a specific order for `Node` or `RBAC` explicitly, user can provide the authorizer in the order they want. Eg: ```yaml cluster: apiServer: authorizationConfig: - type: Node name: Node - type: Webhook name: Webhook webhook: connectionInfo: type: InClusterConfig ... - type: RBAC name: rbac ``` Usage of `authorization-mode` CLI argument will not support this form of customization. ### NVMe NQN Talos now generates `/etc/nvme/hostnqn` and `/etc/nvme/hostid` files based on the node identity which is tied to the lifecycle of the node. The NQN can be read by `talosctl read /etc/nvme/hostnqn` ### Component Updates * Linux: 6.12.11 * CNI plugins: 1.6.2 * runc: 1.2.4 * containerd: 2.0.2 * etcd: 3.5.18 Talos is built with Go 1.23.5. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Dmitry Sharshakov * Justin Garrison * Nico Berlee * Skyler Mäntysaari * Utku Ozdemir * Alexis La Goutte * Andrew Symington * Christian Luetke-Stetzkamp * Christoph Hoopmann * Devin Buhl * Florian Grignon * Ganawa Juanah * Jason Benedicic * K Birt * KillianCdP * L.J. Hanson * Louis SCHNEIDER * Marcel Hamer * Motte * Natalie Romana Albers * PRIHLOP * Ram * Tim Olson * Tine Jozelj * TomyLobo * bzub * greenpsi * sflotat2607 * suse-coder ### Changes
161 commits

* [`3a2d9867b`](https://github.com/siderolabs/talos/commit/3a2d9867b5cc3236b1d1c7981e5794657f3c155e) fix: do not close client.Client.conn with finalizer * [`73f30ff25`](https://github.com/siderolabs/talos/commit/73f30ff25e0adb7a47e2153756e0ea94bd605568) feat: bump pkgs for udev update * [`aea90cb8f`](https://github.com/siderolabs/talos/commit/aea90cb8f1dbe7d5f67d35714825133728c1490d) docs: update hyper-v * [`b7165615f`](https://github.com/siderolabs/talos/commit/b7165615f86afd09ea85dc91090a40860ae6fc9a) fix: use local NTP for AWS platform * [`673ca4bcb`](https://github.com/siderolabs/talos/commit/673ca4bcb2448b3c252fccff0d243932c97fd893) fix: ensure proper closure of client.Client.conn with finalizer * [`19040ffd6`](https://github.com/siderolabs/talos/commit/19040ffd6ef128daaf48a820d8826186c82c68c5) fix: handle of PE sections with duplicate names * [`83489d348`](https://github.com/siderolabs/talos/commit/83489d348905352497da0f6dc042f3e7f05cd4d7) docs: add note about vmxnet and flannel conflict * [`f1292f5e7`](https://github.com/siderolabs/talos/commit/f1292f5e7af4110270475d8bcc4bd39519419e03) docs: add iscsi-tools extension to prerequisites * [`93b4a3740`](https://github.com/siderolabs/talos/commit/93b4a3740ba0c35e8b62cbf8c70058d1e53c3b8e) test: bump timeout on rotate CA test * [`42e166984`](https://github.com/siderolabs/talos/commit/42e16698453a687a4293e7cfeeb0e09d4f084217) feat: support kexec from uki * [`8da264946`](https://github.com/siderolabs/talos/commit/8da264946cda9b4803fd9f2f4dfd0ed25445843b) docs: add Orange Pi 5 to Image Factory platforms and documentation * [`c5fb62e2e`](https://github.com/siderolabs/talos/commit/c5fb62e2e32690aa0235b0911ded1888084496a8) feat: update Linux to 6.2.11 * [`83d007c16`](https://github.com/siderolabs/talos/commit/83d007c161e03311cede2153f35c32f608537290) feat: update etcd to 3.5.18 * [`edf7c3288`](https://github.com/siderolabs/talos/commit/edf7c328835273e2bc6dd23c646091e6a03aa2e9) fix: pe uki extract * [`70f72c5b0`](https://github.com/siderolabs/talos/commit/70f72c5b00bce791d692ec3a0e9a91aaf9d88031) docs: update multus.md * [`807a3cd29`](https://github.com/siderolabs/talos/commit/807a3cd291e2e2cb22946826bccb64671a29d901) refactor: all network merge controllers * [`ec8c4660e`](https://github.com/siderolabs/talos/commit/ec8c4660e277dc11b5e70c014a0238d48cf15bda) docs: update vmware.md * [`baf81cd49`](https://github.com/siderolabs/talos/commit/baf81cd4914470b06393d762f70d0a94f7a9fe32) fix(ci): k8s integration suite wait for resource * [`cd5e54903`](https://github.com/siderolabs/talos/commit/cd5e549039b17add0a2ce09713e1a034bb3efccf) feat: generate iso's with both UKI and grub * [`75673b6a3`](https://github.com/siderolabs/talos/commit/75673b6a38eeb6361c6e6aeb389e8dbaaacb8b0b) feat: provide stable symlinks in disk resources * [`f407c88e4`](https://github.com/siderolabs/talos/commit/f407c88e4678ff6d5edb940f5d54461104be3643) fix(ci): wait for longhorn node resource * [`601cdccb9`](https://github.com/siderolabs/talos/commit/601cdccb979640a6b2ffcba41cc698015b1dacde) feat: extract kernel/initrd from uki for grub * [`ff175b9fb`](https://github.com/siderolabs/talos/commit/ff175b9fbdb2ac92ac53351d32de130bd0676038) docs: update disk-encryption.md * [`a8d84e315`](https://github.com/siderolabs/talos/commit/a8d84e3155137a114ad00ad7ae321af033020e7d) docs: fix typos and add more explanations in docs * [`3a384240e`](https://github.com/siderolabs/talos/commit/3a384240ecf660d310f2df98327f018649ebaa6d) fix: invalid date field in iqn/nqn * [`82c9ec158`](https://github.com/siderolabs/talos/commit/82c9ec158e82efea80daaf76fef9fbd31c3eb823) chore(ci): add tests with longhorn v2 engine * [`689ea1dbf`](https://github.com/siderolabs/talos/commit/689ea1dbfe29d70d91e0b41d31fc696e2ff96665) fix: bring back disk UUID * [`7a712fad2`](https://github.com/siderolabs/talos/commit/7a712fad2abb916f397a8dd0aebf66e59ee75904) fix: disks with 4k sector size and systemd-boot * [`d62a34aaf`](https://github.com/siderolabs/talos/commit/d62a34aaf4e4ff7dad9f6dbeb59a67016c70fffb) feat: update tools/pkgs/extras * [`b9a8ad6ac`](https://github.com/siderolabs/talos/commit/b9a8ad6acafd64c4217ba914184592c0cfb97962) chore: de-hardcode list of extra images for image-cache test * [`683153a33`](https://github.com/siderolabs/talos/commit/683153a33c1069e7f7cadf4e3a70bde3f8ba3331) docs: remove the last mentions of `preserve` flag for Talos 1.8+ * [`33c7f4195`](https://github.com/siderolabs/talos/commit/33c7f4195816988af6f70199fdb4a31d027fa746) docs: fix typo an MacOS to on MacOS * [`21cff3919`](https://github.com/siderolabs/talos/commit/21cff3919b80f33f837b19728500fcb91e7caf8f) chore(ci): fio benchmark results as separate artifacts * [`0b7fc7cdf`](https://github.com/siderolabs/talos/commit/0b7fc7cdfea651a6f16db3f346473505d8df3e78) fix: abort node watch on hostname change * [`99ba53941`](https://github.com/siderolabs/talos/commit/99ba53941cecdc54c0ececa9876b25a7fc7668a5) docs: remove the mention of `preserve` flag for Talos 1.8+ * [`bde516fde`](https://github.com/siderolabs/talos/commit/bde516fde62a25dd60691a9a3b6f3d30de11dad1) chore(ci): rework iscsi-tools extensions test * [`e1efbf656`](https://github.com/siderolabs/talos/commit/e1efbf656ae96ecedba1c132608c3ad2d3ae4a66) refactor: extract platform metadata into Talos machinery * [`79987c05d`](https://github.com/siderolabs/talos/commit/79987c05dcd39ca646c2d73c1e25488504f13a60) feat: generate iqn and nqn files * [`0cab6ed17`](https://github.com/siderolabs/talos/commit/0cab6ed170708549d69c04b163744854de0aa8f2) docs: update troubleshooting.md * [`921e10254`](https://github.com/siderolabs/talos/commit/921e10254d443c459a9775368ca080ecba273321) chore: update Go to 1.23.5 * [`399d53b54`](https://github.com/siderolabs/talos/commit/399d53b543f6ca99f13d28313ae77b3472b0f728) fix: ignore forbidden error when waiting for pod eviction * [`8dea57a81`](https://github.com/siderolabs/talos/commit/8dea57a81b8393b518da60951713c711659291f9) fix: make etc binds read-only * [`63157dcb4`](https://github.com/siderolabs/talos/commit/63157dcb496ca767bfbff9e1b86f14277a44cdb7) docs: update SideroLinkConfig example * [`fc7080e34`](https://github.com/siderolabs/talos/commit/fc7080e34b990d2d50ec1e40734437ccd0ee95f7) chore: clear cache after updating upstreams * [`51e0f273f`](https://github.com/siderolabs/talos/commit/51e0f273f9199b8320cd5da247c702a4319a92c5) docs: update documentation for Talos 1.9.2 * [`e06b14112`](https://github.com/siderolabs/talos/commit/e06b14112d2c978e3f6b5c4446090a7ae533ead9) feat: update Kubernetes to 1.32.1 * [`4310b290d`](https://github.com/siderolabs/talos/commit/4310b290d5cff9697f86cc24f1c281e62cb7d72f) fix: generate UKI only if actually needed * [`a8cd99102`](https://github.com/siderolabs/talos/commit/a8cd991026fe7290013b7504a4e87af46c49d25b) docs: update OpenEBS Mayastor installation * [`cf45f4764`](https://github.com/siderolabs/talos/commit/cf45f4764ddd979fa81576833d9630eadea24f41) docs: add Radxa ROCK 5B docs to Single Board Computer section * [`b21bdc5e5`](https://github.com/siderolabs/talos/commit/b21bdc5e501bc2244e3e487827ffba79075f6642) chore(ci): save csi tests fio results * [`01c86832c`](https://github.com/siderolabs/talos/commit/01c86832cbbbe0b81b9500032f94298fd6e90b58) chore(ci): add test for OpenEBS MayaStor * [`c77483510`](https://github.com/siderolabs/talos/commit/c774835103ad139b44d7e4e13c003e2b13160347) test: update `talosctl debug air-gapped` * [`ddd695d93`](https://github.com/siderolabs/talos/commit/ddd695d933d39920da42219ba8b3d39b0681a3ea) feat: update containerd to 2.0.2 * [`da2e81120`](https://github.com/siderolabs/talos/commit/da2e81120f7336d9633a98523e05d91f5750434f) fix: add informer resync period for node status watcher * [`9b957df64`](https://github.com/siderolabs/talos/commit/9b957df64680a97a16575db67d4af27cfc0ef7d2) chore: uki code restructure * [`e41a99525`](https://github.com/siderolabs/talos/commit/e41a995253428dde437eecec52cabfb4c80f90ea) fix: kube-apiserver authorizers order * [`db4ca5668`](https://github.com/siderolabs/talos/commit/db4ca5668ac0d85a98a5ea022f6546526d20aff1) feat: add a kernel parameter to disable built-in auditd * [`faa149003`](https://github.com/siderolabs/talos/commit/faa1490033df0a843010fa7154096d84f415afce) feat: update Linux to 6.12.9 * [`8de19758d`](https://github.com/siderolabs/talos/commit/8de19758dafce802c0f93a63ae3083b5ad17162d) fix: a couple of imager panics/crashes * [`5bc3e34cb`](https://github.com/siderolabs/talos/commit/5bc3e34cb3a6fd8e3eb5d02dd612cf3cf9dc499f) fix: detect GPT before ZFS * [`ed7e47d15`](https://github.com/siderolabs/talos/commit/ed7e47d158e064204b2f14f9ff378bea70e9524e) refactor: drop usage of objcopy to generate UKIs * [`edf5c5e29`](https://github.com/siderolabs/talos/commit/edf5c5e29bc76299c63bb04f1d97a030ecb9b3f0) fix: extfs repair and resize * [`6e32ea5b7`](https://github.com/siderolabs/talos/commit/6e32ea5b7f1a22500014ecb365e13af36034187a) fix: merge of VolumeConfig documents with sizes * [`1be5f8ff2`](https://github.com/siderolabs/talos/commit/1be5f8ff25ac7042ee3334f657d6604ec5f8501d) feat: update Linux to 6.12.8 * [`e6a4583ba`](https://github.com/siderolabs/talos/commit/e6a4583ba862da9f49ab0bd0cb6bc8436723bc67) feat: support generating unsigned UKIs * [`bbd6067d4`](https://github.com/siderolabs/talos/commit/bbd6067d426fb2be22ff8935f415ab6d729d8f19) fix: partition alignment on disks with 4k sectors * [`84fcc976f`](https://github.com/siderolabs/talos/commit/84fcc976f8da5af310771e1835a0347df5bcc97d) fix: yet another dashboard panic * [`6d605fc85`](https://github.com/siderolabs/talos/commit/6d605fc8595e2f06e43529966e396f2ae403c76c) fix: disable NRI plugin in a different way * [`499695e24`](https://github.com/siderolabs/talos/commit/499695e24ea02ffc2fd8c92276d5de41b0d4919e) fix: request previous IP address in discovery * [`cc84caf8c`](https://github.com/siderolabs/talos/commit/cc84caf8c0dffd9d59f360f84967c524be9ba369) docs: update Cilium documentation * [`fa5300d91`](https://github.com/siderolabs/talos/commit/fa5300d910a537f03939fcbf6362abdd8fa607dd) chore: revert: drop deprecated allowSchedulingOnMasters * [`0abb3dabf`](https://github.com/siderolabs/talos/commit/0abb3dabf6d50b9c1176af683ad74234334f822d) docs: fix command to wait for ceph-rook HEALTH_OK * [`32c67c27c`](https://github.com/siderolabs/talos/commit/32c67c27c393c989f9d70ccb8506c4735f70d494) chore: drop deprecated allowSchedulingOnMasters * [`ae6d065be`](https://github.com/siderolabs/talos/commit/ae6d065beb4897a1b877ecb30b06be456befbf91) fix: mount selinuxfs only when SELinux is enabled * [`5ccbf4bcd`](https://github.com/siderolabs/talos/commit/5ccbf4bcdbe9aa2096320d17eb2deab6a062faf9) feat: enable `configfs` * [`59582496d`](https://github.com/siderolabs/talos/commit/59582496d5fe419f833703be8e956163b6241d15) feat: bring in partity with sd-257 * [`83d84a831`](https://github.com/siderolabs/talos/commit/83d84a831862c774b9bc2adc2e11e00bf2a79912) chore(ci): better zfs checks * [`650eb3a4f`](https://github.com/siderolabs/talos/commit/650eb3a4f2d89d173cdd6581a6d1232511a8e219) refactor: rewrite cloud uploader to use AWS SDK Go v2 * [`01bf8449b`](https://github.com/siderolabs/talos/commit/01bf8449b917ece76336ca7f0eb11fd877195025) fix: update field name for bus path disk selector * [`e915c98d5`](https://github.com/siderolabs/talos/commit/e915c98d583e5901c1c2efe38efa656b39d72360) fix: exclude disks with empty transport for disk selector * [`b7a7fdc4b`](https://github.com/siderolabs/talos/commit/b7a7fdc4b8a715157bfa2614c9541b96643cd2ba) refactor: generate /etc/os-release file static way * [`e79c9e127`](https://github.com/siderolabs/talos/commit/e79c9e12772c998ff5b3e401efd7f074f85e5cef) chore(ci): drop equinix metal e2e-test * [`418945444`](https://github.com/siderolabs/talos/commit/418945444135c6d9e2e5960e7b9cbd754084fea2) fix: build of talosctl on non-Linux platforms * [`4761a9e6a`](https://github.com/siderolabs/talos/commit/4761a9e6aa0bf619a564807d02ebce030384d6a1) chore: update dependencies * [`f98efb333`](https://github.com/siderolabs/talos/commit/f98efb333f89b8493c55b91698c917437b7af310) fix: ignore member not found error on leave cluster * [`b72bda0a4`](https://github.com/siderolabs/talos/commit/b72bda0a420f75ea0439cc0240dcf6d3363e5d48) fix: talosctl support and race tests * [`27233cf0f`](https://github.com/siderolabs/talos/commit/27233cf0fcf4031cbc8001504bed67b6d4a104f9) test: use node informer instead of raw watch * [`5dc15e8db`](https://github.com/siderolabs/talos/commit/5dc15e8db459ac632f0ae106e1cfc7eaab672adf) fix: update go-blockdevice to v2.0.9 * [`5f3acd0f2`](https://github.com/siderolabs/talos/commit/5f3acd0f26a35ac966d4ced01436f1dd3c03648b) fix: use correct default search domain * [`7e5d36d46`](https://github.com/siderolabs/talos/commit/7e5d36d469ff01153f40b16ab722f0ebe25d41ae) fix: pci driver rebind config validation * [`4b97bbc3f`](https://github.com/siderolabs/talos/commit/4b97bbc3fee1257d0d21be25e21493bfd1f45a80) fix: pull in containerd CNI deadlock fix * [`066480722`](https://github.com/siderolabs/talos/commit/0664807229e0688f092a453cbd3121dbe189ca39) test: fix apparmor tests * [`82ea44a6b`](https://github.com/siderolabs/talos/commit/82ea44a6b2aa0a35861ca454a09503a81332f824) fix: reduce installer image * [`78b3e7f4f`](https://github.com/siderolabs/talos/commit/78b3e7f4f1870085b719971c6f92dc866fe1e9d0) fix: get next rule number for IPv6 in the appropriate chain * [`675854aa0`](https://github.com/siderolabs/talos/commit/675854aa03b3913da3481337d995c206174cf004) docs: fix two typos * [`f70b7386a`](https://github.com/siderolabs/talos/commit/f70b7386ac3125f3b8ab6b1765338c7e3445ae5c) test: add a xfs makefs test * [`8212e4864`](https://github.com/siderolabs/talos/commit/8212e4864d11e69ed63be3f4e608e9ccbc788cc4) refactor: use quirks in kernel args * [`b4aa5189d`](https://github.com/siderolabs/talos/commit/b4aa5189d4d4565a42ad7ac8de24c424a215b42f) release(v1.10.0-alpha.0): prepare release * [`bd85bd5b7`](https://github.com/siderolabs/talos/commit/bd85bd5b731463a42b7c82c66e9add251a280d26) fix: fix `Failed to initialize SELinux labeling handle` udev error * [`73c82e3e5`](https://github.com/siderolabs/talos/commit/73c82e3e5625ec1899f93312a671dfe6dffaea61) feat: bring Linux 6.12.6, CNI plugins 1.6.1 * [`c12b52491`](https://github.com/siderolabs/talos/commit/c12b52491456d1e52204eb290d0686a317358c7c) docs: document Kubernetes service registry incompat with K8s 1.32 * [`a5660ed77`](https://github.com/siderolabs/talos/commit/a5660ed778108843fe15b2b1582dd6556cf52b6c) feat: pcirebind controller * [`4c3261626`](https://github.com/siderolabs/talos/commit/4c3261626fa3f5ac36df71ec878f103a7c85c5c5) docs: fix several typos * [`fb3675321`](https://github.com/siderolabs/talos/commit/fb36753216cba7740040f2ec117c783221f66192) fix: dashboard crash on CPU data * [`dec0185c8`](https://github.com/siderolabs/talos/commit/dec0185c8505a7d43244fdb01f7a5decc77d116d) chore: reduce memory usage for secureboot functions * [`cee6c60a0`](https://github.com/siderolabs/talos/commit/cee6c60a0fc301b22c50fdf8bd2fc1d2b7ba3d54) fix: make talosctl time work with PTP time sync * [`f75604313`](https://github.com/siderolabs/talos/commit/f75604313d535180c38b33df53253ad4acba2ec1) chore: support gcr.io auth for cache and image gen * [`6ef2596da`](https://github.com/siderolabs/talos/commit/6ef2596da7b7e8be90e5b981621461352be7b134) docs: improve Hetzner documentation * [`7d39b9ec2`](https://github.com/siderolabs/talos/commit/7d39b9ec2bdd7883116626bf889c1331717f8438) feat: remove cgroupsv1 in non-container mode * [`8003536c7`](https://github.com/siderolabs/talos/commit/8003536c7ca20356adcd900e64463bd166d445af) fix: restore previous disk serial fetching * [`03116ef9b`](https://github.com/siderolabs/talos/commit/03116ef9bd2a215c20a2c4c7db133dd857ce2b16) chore: prepare for Talos 1.10 * [`00682fdd6`](https://github.com/siderolabs/talos/commit/00682fdd6e8fa23c6f9782840ea3e2b8ef250f66) docs: activate 1.9 docs as default * [`bea05f5c9`](https://github.com/siderolabs/talos/commit/bea05f5c9b6ce6f5d067eb357d26e30a49154b21) docs: update deploying-cilium.md * [`284ab1179`](https://github.com/siderolabs/talos/commit/284ab11794b3b076aa9ab2bb756e02292d854751) feat: support link altnames/aliases * [`5bfd829bf`](https://github.com/siderolabs/talos/commit/5bfd829bf9c8e46b6c51174be4b764d4c94b3320) docs: fix 'containter' typo * [`8d151b771`](https://github.com/siderolabs/talos/commit/8d151b771debc51d3fa40dfafc7a2e43f955a634) docs: clarify TALOSCONFIG for AWS * [`0ef19171f`](https://github.com/siderolabs/talos/commit/0ef19171f738e46346dfae71f43b8f7b47bf257d) fix: renovate typo * [`c568adc7d`](https://github.com/siderolabs/talos/commit/c568adc7dcd52c34924acc1eae849a2ca5b5a4d5) fix: renovate config * [`ec2e24fd9`](https://github.com/siderolabs/talos/commit/ec2e24fd9617db34e3bec753b5fe720670fa31a4) fix: match MAC addresses case-insensitive (nocloud) * [`41a0c440a`](https://github.com/siderolabs/talos/commit/41a0c440ad3f4de2a2ba9198d22609c55bdaf61b) chore: rekres for renovate changes * [`a49bb9ee4`](https://github.com/siderolabs/talos/commit/a49bb9ee45346268b26d3b9cff4dd017bfb9c829) feat: update Linux to 6.12.5 * [`b15917ecc`](https://github.com/siderolabs/talos/commit/b15917ecc626781e13de0e84b794ab77c97b3159) chore: add more debugging logs for META and volumes * [`2b1b326f0`](https://github.com/siderolabs/talos/commit/2b1b326f08966615a5a2f8708f94e6d1355773a7) docs: mention different paths for OpenEBS * [`9470e842f`](https://github.com/siderolabs/talos/commit/9470e842fca2d7dd0dae185bff7210a8af355445) test: cleanup failed Kubernetes pods * [`c9c685150`](https://github.com/siderolabs/talos/commit/c9c6851504fcda7b66395fbbba1fbc8b0e085d4a) fix: node identity flip * [`590c01657`](https://github.com/siderolabs/talos/commit/590c0165712aee60e752766d6bd3875443c353cb) feat: update containerd to v2.0.1 * [`18fa5a258`](https://github.com/siderolabs/talos/commit/18fa5a25876f41760ce8da5e918222e04b81949a) docs: update image-cache doc for iso * [`ab5bb6884`](https://github.com/siderolabs/talos/commit/ab5bb688420986a356aed55513a1dbd25de323e2) fix: generate and serve registries with port * [`58236066d`](https://github.com/siderolabs/talos/commit/58236066ddbcd7c401e945b70555ff315a2458f7) fix: support image cache on VFAT USB stick * [`e193a5071`](https://github.com/siderolabs/talos/commit/e193a507149c05e341abe019de219fe0b1bc83e3) fix: image cache integration test * [`08ee400fd`](https://github.com/siderolabs/talos/commit/08ee400fdbde368a54d6777cc31ceb91e1968ad2) test: fix flaky test NodeAddressSort * [`d45e8d1d1`](https://github.com/siderolabs/talos/commit/d45e8d1d1da28ca1b311198588d723cb491527eb) feat: update Kubernetes to 1.32.0 * [`136b12912`](https://github.com/siderolabs/talos/commit/136b12912165d5eb5c7c716b7f7dfcfbc42b08d4) chore: drop semicolon for supporting vfat filesystems * [`3e9e027ef`](https://github.com/siderolabs/talos/commit/3e9e027efbd2988f72eb2da0c1ab0e83ba52b950) test: add an option to boot from an USB stick * [`ef8c3e3b3`](https://github.com/siderolabs/talos/commit/ef8c3e3b3b245f7ffefa6c19930d5a0925ce666b) docs: fix typo in multus.md * [`d54414add`](https://github.com/siderolabs/talos/commit/d54414add4e4df1b5a7b166f155cdcca512d4ee2) fix: authorization config gen * [`cce72cfe8`](https://github.com/siderolabs/talos/commit/cce72cfe86beeb7ada9641df611046f4789e3bd8) docs: replace deprecated Hetzner server plans * [`81805103d`](https://github.com/siderolabs/talos/commit/81805103deada24b12b7d7861b2df5a5c788c86b) chore: enable proper parallel usage of TestDepth * [`e1b824eba`](https://github.com/siderolabs/talos/commit/e1b824ebada3d3dad9d2793fd12b5a948d8b51b5) docs: update ceph-with-rook.md * [`470b75563`](https://github.com/siderolabs/talos/commit/470b75563add4ce5bbce312c1e3dc783e63af1fa) fix: use mtu network option for podman * [`61b1489a0`](https://github.com/siderolabs/talos/commit/61b1489a0f0868c5b7e124544520bc46badef85c) fix: order volume config by the requested size * [`bc3039acd`](https://github.com/siderolabs/talos/commit/bc3039acdbc57e6be16a1bc6555894dff2da65c9) feat: update runc to 1.2.3 * [`30016a0a8`](https://github.com/siderolabs/talos/commit/30016a0a8d98d42e01c4d32acf9e600777d72d57) fix: avoid nil-pointer-panic in `RegistriesConfigController` * [`fe0457152`](https://github.com/siderolabs/talos/commit/fe045715277a4678b8e8c9632ec71e86bf17ace0) fix: power on the machine on reboot request in qemu power api * [`10da553ef`](https://github.com/siderolabs/talos/commit/10da553ef0dde5f87f09321400239baa51929a36) docs: build what's new for 1.9 * [`d946ccae3`](https://github.com/siderolabs/talos/commit/d946ccae31b87559a06cb1cefcefe8f937b73d8b) feat: update Linux to 6.12.4 * [`707a77bf6`](https://github.com/siderolabs/talos/commit/707a77bf64190470bf84c91cdff185981e80a31b) test: fix user namespace test, TPM2 fixes * [`c3537b2f5`](https://github.com/siderolabs/talos/commit/c3537b2f5491a890f626ba8fc47034d5059808af) feat: update Linux to 6.12.3 * [`cb4d9d673`](https://github.com/siderolabs/talos/commit/cb4d9d673432e4a0fba0d87bc64fde620d991082) docs: fix a few mistakes in release notes * [`c4724fc97`](https://github.com/siderolabs/talos/commit/c4724fc97598d8764b00fb56971d997a349a92e5) chore: add integration tests for image-cache * [`07220fe7f`](https://github.com/siderolabs/talos/commit/07220fe7f5a22444f7a085f5868f628ddd912b6d) fix: install iptables-nft to the host * [`14841750b`](https://github.com/siderolabs/talos/commit/14841750bf2fc09a9de0b32a7af0dc3f76e1019a) chore: add version compatibility for Talos 1.10 * [`852baf819`](https://github.com/siderolabs/talos/commit/852baf819d453a3d8d58ae9f029e280ae75e0cb1) feat: support vlan/bond in v1, vlan in v2 for nocloud * [`dd61ad861`](https://github.com/siderolabs/talos/commit/dd61ad86105c07c1ff8a101a0542af61699f0df3) fix: lock provisioning order of user disk partitions * [`d0773ff09`](https://github.com/siderolabs/talos/commit/d0773ff09df84b2dac8ecadc91023596050ce098) chore: update Go to 1.23.4 * [`7d6507189`](https://github.com/siderolabs/talos/commit/7d6507189ff9a99b3b05ee9528701b65af4ad147) feat: implement new address sorting algorithm * [`9081506d6`](https://github.com/siderolabs/talos/commit/9081506d6cde26d60a29f08a090e28da501e4bd1) feat: add process scheduling options * [`77e9db4ab`](https://github.com/siderolabs/talos/commit/77e9db4abf9c9b694d60c8803b436121dfe30ccd) test: use two workers in qemu tests by default * [`5a4bdf62a`](https://github.com/siderolabs/talos/commit/5a4bdf62a9bf1387b6489eaf2c9cc0770aa0b68c) feat: update Kubernetes to 1.32.0-rc.1 * [`d99bcc950`](https://github.com/siderolabs/talos/commit/d99bcc95031037f4b0990419d2ce1fd4280cbde9) chore: refactor mergeDNSServers func * [`0cde08d8b`](https://github.com/siderolabs/talos/commit/0cde08d8be1ad62c49fed148fd331ea5a212df4c) docs: add Turing RK1 docs to Single Board Computer section

### Changes since v1.10.0-alpha.0
97 commits

* [`3a2d9867b`](https://github.com/siderolabs/talos/commit/3a2d9867b5cc3236b1d1c7981e5794657f3c155e) fix: do not close client.Client.conn with finalizer * [`73f30ff25`](https://github.com/siderolabs/talos/commit/73f30ff25e0adb7a47e2153756e0ea94bd605568) feat: bump pkgs for udev update * [`aea90cb8f`](https://github.com/siderolabs/talos/commit/aea90cb8f1dbe7d5f67d35714825133728c1490d) docs: update hyper-v * [`b7165615f`](https://github.com/siderolabs/talos/commit/b7165615f86afd09ea85dc91090a40860ae6fc9a) fix: use local NTP for AWS platform * [`673ca4bcb`](https://github.com/siderolabs/talos/commit/673ca4bcb2448b3c252fccff0d243932c97fd893) fix: ensure proper closure of client.Client.conn with finalizer * [`19040ffd6`](https://github.com/siderolabs/talos/commit/19040ffd6ef128daaf48a820d8826186c82c68c5) fix: handle of PE sections with duplicate names * [`83489d348`](https://github.com/siderolabs/talos/commit/83489d348905352497da0f6dc042f3e7f05cd4d7) docs: add note about vmxnet and flannel conflict * [`f1292f5e7`](https://github.com/siderolabs/talos/commit/f1292f5e7af4110270475d8bcc4bd39519419e03) docs: add iscsi-tools extension to prerequisites * [`93b4a3740`](https://github.com/siderolabs/talos/commit/93b4a3740ba0c35e8b62cbf8c70058d1e53c3b8e) test: bump timeout on rotate CA test * [`42e166984`](https://github.com/siderolabs/talos/commit/42e16698453a687a4293e7cfeeb0e09d4f084217) feat: support kexec from uki * [`8da264946`](https://github.com/siderolabs/talos/commit/8da264946cda9b4803fd9f2f4dfd0ed25445843b) docs: add Orange Pi 5 to Image Factory platforms and documentation * [`c5fb62e2e`](https://github.com/siderolabs/talos/commit/c5fb62e2e32690aa0235b0911ded1888084496a8) feat: update Linux to 6.2.11 * [`83d007c16`](https://github.com/siderolabs/talos/commit/83d007c161e03311cede2153f35c32f608537290) feat: update etcd to 3.5.18 * [`edf7c3288`](https://github.com/siderolabs/talos/commit/edf7c328835273e2bc6dd23c646091e6a03aa2e9) fix: pe uki extract * [`70f72c5b0`](https://github.com/siderolabs/talos/commit/70f72c5b00bce791d692ec3a0e9a91aaf9d88031) docs: update multus.md * [`807a3cd29`](https://github.com/siderolabs/talos/commit/807a3cd291e2e2cb22946826bccb64671a29d901) refactor: all network merge controllers * [`ec8c4660e`](https://github.com/siderolabs/talos/commit/ec8c4660e277dc11b5e70c014a0238d48cf15bda) docs: update vmware.md * [`baf81cd49`](https://github.com/siderolabs/talos/commit/baf81cd4914470b06393d762f70d0a94f7a9fe32) fix(ci): k8s integration suite wait for resource * [`cd5e54903`](https://github.com/siderolabs/talos/commit/cd5e549039b17add0a2ce09713e1a034bb3efccf) feat: generate iso's with both UKI and grub * [`75673b6a3`](https://github.com/siderolabs/talos/commit/75673b6a38eeb6361c6e6aeb389e8dbaaacb8b0b) feat: provide stable symlinks in disk resources * [`f407c88e4`](https://github.com/siderolabs/talos/commit/f407c88e4678ff6d5edb940f5d54461104be3643) fix(ci): wait for longhorn node resource * [`601cdccb9`](https://github.com/siderolabs/talos/commit/601cdccb979640a6b2ffcba41cc698015b1dacde) feat: extract kernel/initrd from uki for grub * [`ff175b9fb`](https://github.com/siderolabs/talos/commit/ff175b9fbdb2ac92ac53351d32de130bd0676038) docs: update disk-encryption.md * [`a8d84e315`](https://github.com/siderolabs/talos/commit/a8d84e3155137a114ad00ad7ae321af033020e7d) docs: fix typos and add more explanations in docs * [`3a384240e`](https://github.com/siderolabs/talos/commit/3a384240ecf660d310f2df98327f018649ebaa6d) fix: invalid date field in iqn/nqn * [`82c9ec158`](https://github.com/siderolabs/talos/commit/82c9ec158e82efea80daaf76fef9fbd31c3eb823) chore(ci): add tests with longhorn v2 engine * [`689ea1dbf`](https://github.com/siderolabs/talos/commit/689ea1dbfe29d70d91e0b41d31fc696e2ff96665) fix: bring back disk UUID * [`7a712fad2`](https://github.com/siderolabs/talos/commit/7a712fad2abb916f397a8dd0aebf66e59ee75904) fix: disks with 4k sector size and systemd-boot * [`d62a34aaf`](https://github.com/siderolabs/talos/commit/d62a34aaf4e4ff7dad9f6dbeb59a67016c70fffb) feat: update tools/pkgs/extras * [`b9a8ad6ac`](https://github.com/siderolabs/talos/commit/b9a8ad6acafd64c4217ba914184592c0cfb97962) chore: de-hardcode list of extra images for image-cache test * [`683153a33`](https://github.com/siderolabs/talos/commit/683153a33c1069e7f7cadf4e3a70bde3f8ba3331) docs: remove the last mentions of `preserve` flag for Talos 1.8+ * [`33c7f4195`](https://github.com/siderolabs/talos/commit/33c7f4195816988af6f70199fdb4a31d027fa746) docs: fix typo an MacOS to on MacOS * [`21cff3919`](https://github.com/siderolabs/talos/commit/21cff3919b80f33f837b19728500fcb91e7caf8f) chore(ci): fio benchmark results as separate artifacts * [`0b7fc7cdf`](https://github.com/siderolabs/talos/commit/0b7fc7cdfea651a6f16db3f346473505d8df3e78) fix: abort node watch on hostname change * [`99ba53941`](https://github.com/siderolabs/talos/commit/99ba53941cecdc54c0ececa9876b25a7fc7668a5) docs: remove the mention of `preserve` flag for Talos 1.8+ * [`bde516fde`](https://github.com/siderolabs/talos/commit/bde516fde62a25dd60691a9a3b6f3d30de11dad1) chore(ci): rework iscsi-tools extensions test * [`e1efbf656`](https://github.com/siderolabs/talos/commit/e1efbf656ae96ecedba1c132608c3ad2d3ae4a66) refactor: extract platform metadata into Talos machinery * [`79987c05d`](https://github.com/siderolabs/talos/commit/79987c05dcd39ca646c2d73c1e25488504f13a60) feat: generate iqn and nqn files * [`0cab6ed17`](https://github.com/siderolabs/talos/commit/0cab6ed170708549d69c04b163744854de0aa8f2) docs: update troubleshooting.md * [`921e10254`](https://github.com/siderolabs/talos/commit/921e10254d443c459a9775368ca080ecba273321) chore: update Go to 1.23.5 * [`399d53b54`](https://github.com/siderolabs/talos/commit/399d53b543f6ca99f13d28313ae77b3472b0f728) fix: ignore forbidden error when waiting for pod eviction * [`8dea57a81`](https://github.com/siderolabs/talos/commit/8dea57a81b8393b518da60951713c711659291f9) fix: make etc binds read-only * [`63157dcb4`](https://github.com/siderolabs/talos/commit/63157dcb496ca767bfbff9e1b86f14277a44cdb7) docs: update SideroLinkConfig example * [`fc7080e34`](https://github.com/siderolabs/talos/commit/fc7080e34b990d2d50ec1e40734437ccd0ee95f7) chore: clear cache after updating upstreams * [`51e0f273f`](https://github.com/siderolabs/talos/commit/51e0f273f9199b8320cd5da247c702a4319a92c5) docs: update documentation for Talos 1.9.2 * [`e06b14112`](https://github.com/siderolabs/talos/commit/e06b14112d2c978e3f6b5c4446090a7ae533ead9) feat: update Kubernetes to 1.32.1 * [`4310b290d`](https://github.com/siderolabs/talos/commit/4310b290d5cff9697f86cc24f1c281e62cb7d72f) fix: generate UKI only if actually needed * [`a8cd99102`](https://github.com/siderolabs/talos/commit/a8cd991026fe7290013b7504a4e87af46c49d25b) docs: update OpenEBS Mayastor installation * [`cf45f4764`](https://github.com/siderolabs/talos/commit/cf45f4764ddd979fa81576833d9630eadea24f41) docs: add Radxa ROCK 5B docs to Single Board Computer section * [`b21bdc5e5`](https://github.com/siderolabs/talos/commit/b21bdc5e501bc2244e3e487827ffba79075f6642) chore(ci): save csi tests fio results * [`01c86832c`](https://github.com/siderolabs/talos/commit/01c86832cbbbe0b81b9500032f94298fd6e90b58) chore(ci): add test for OpenEBS MayaStor * [`c77483510`](https://github.com/siderolabs/talos/commit/c774835103ad139b44d7e4e13c003e2b13160347) test: update `talosctl debug air-gapped` * [`ddd695d93`](https://github.com/siderolabs/talos/commit/ddd695d933d39920da42219ba8b3d39b0681a3ea) feat: update containerd to 2.0.2 * [`da2e81120`](https://github.com/siderolabs/talos/commit/da2e81120f7336d9633a98523e05d91f5750434f) fix: add informer resync period for node status watcher * [`9b957df64`](https://github.com/siderolabs/talos/commit/9b957df64680a97a16575db67d4af27cfc0ef7d2) chore: uki code restructure * [`e41a99525`](https://github.com/siderolabs/talos/commit/e41a995253428dde437eecec52cabfb4c80f90ea) fix: kube-apiserver authorizers order * [`db4ca5668`](https://github.com/siderolabs/talos/commit/db4ca5668ac0d85a98a5ea022f6546526d20aff1) feat: add a kernel parameter to disable built-in auditd * [`faa149003`](https://github.com/siderolabs/talos/commit/faa1490033df0a843010fa7154096d84f415afce) feat: update Linux to 6.12.9 * [`8de19758d`](https://github.com/siderolabs/talos/commit/8de19758dafce802c0f93a63ae3083b5ad17162d) fix: a couple of imager panics/crashes * [`5bc3e34cb`](https://github.com/siderolabs/talos/commit/5bc3e34cb3a6fd8e3eb5d02dd612cf3cf9dc499f) fix: detect GPT before ZFS * [`ed7e47d15`](https://github.com/siderolabs/talos/commit/ed7e47d158e064204b2f14f9ff378bea70e9524e) refactor: drop usage of objcopy to generate UKIs * [`edf5c5e29`](https://github.com/siderolabs/talos/commit/edf5c5e29bc76299c63bb04f1d97a030ecb9b3f0) fix: extfs repair and resize * [`6e32ea5b7`](https://github.com/siderolabs/talos/commit/6e32ea5b7f1a22500014ecb365e13af36034187a) fix: merge of VolumeConfig documents with sizes * [`1be5f8ff2`](https://github.com/siderolabs/talos/commit/1be5f8ff25ac7042ee3334f657d6604ec5f8501d) feat: update Linux to 6.12.8 * [`e6a4583ba`](https://github.com/siderolabs/talos/commit/e6a4583ba862da9f49ab0bd0cb6bc8436723bc67) feat: support generating unsigned UKIs * [`bbd6067d4`](https://github.com/siderolabs/talos/commit/bbd6067d426fb2be22ff8935f415ab6d729d8f19) fix: partition alignment on disks with 4k sectors * [`84fcc976f`](https://github.com/siderolabs/talos/commit/84fcc976f8da5af310771e1835a0347df5bcc97d) fix: yet another dashboard panic * [`6d605fc85`](https://github.com/siderolabs/talos/commit/6d605fc8595e2f06e43529966e396f2ae403c76c) fix: disable NRI plugin in a different way * [`499695e24`](https://github.com/siderolabs/talos/commit/499695e24ea02ffc2fd8c92276d5de41b0d4919e) fix: request previous IP address in discovery * [`cc84caf8c`](https://github.com/siderolabs/talos/commit/cc84caf8c0dffd9d59f360f84967c524be9ba369) docs: update Cilium documentation * [`fa5300d91`](https://github.com/siderolabs/talos/commit/fa5300d910a537f03939fcbf6362abdd8fa607dd) chore: revert: drop deprecated allowSchedulingOnMasters * [`0abb3dabf`](https://github.com/siderolabs/talos/commit/0abb3dabf6d50b9c1176af683ad74234334f822d) docs: fix command to wait for ceph-rook HEALTH_OK * [`32c67c27c`](https://github.com/siderolabs/talos/commit/32c67c27c393c989f9d70ccb8506c4735f70d494) chore: drop deprecated allowSchedulingOnMasters * [`ae6d065be`](https://github.com/siderolabs/talos/commit/ae6d065beb4897a1b877ecb30b06be456befbf91) fix: mount selinuxfs only when SELinux is enabled * [`5ccbf4bcd`](https://github.com/siderolabs/talos/commit/5ccbf4bcdbe9aa2096320d17eb2deab6a062faf9) feat: enable `configfs` * [`59582496d`](https://github.com/siderolabs/talos/commit/59582496d5fe419f833703be8e956163b6241d15) feat: bring in partity with sd-257 * [`83d84a831`](https://github.com/siderolabs/talos/commit/83d84a831862c774b9bc2adc2e11e00bf2a79912) chore(ci): better zfs checks * [`650eb3a4f`](https://github.com/siderolabs/talos/commit/650eb3a4f2d89d173cdd6581a6d1232511a8e219) refactor: rewrite cloud uploader to use AWS SDK Go v2 * [`01bf8449b`](https://github.com/siderolabs/talos/commit/01bf8449b917ece76336ca7f0eb11fd877195025) fix: update field name for bus path disk selector * [`e915c98d5`](https://github.com/siderolabs/talos/commit/e915c98d583e5901c1c2efe38efa656b39d72360) fix: exclude disks with empty transport for disk selector * [`b7a7fdc4b`](https://github.com/siderolabs/talos/commit/b7a7fdc4b8a715157bfa2614c9541b96643cd2ba) refactor: generate /etc/os-release file static way * [`e79c9e127`](https://github.com/siderolabs/talos/commit/e79c9e12772c998ff5b3e401efd7f074f85e5cef) chore(ci): drop equinix metal e2e-test * [`418945444`](https://github.com/siderolabs/talos/commit/418945444135c6d9e2e5960e7b9cbd754084fea2) fix: build of talosctl on non-Linux platforms * [`4761a9e6a`](https://github.com/siderolabs/talos/commit/4761a9e6aa0bf619a564807d02ebce030384d6a1) chore: update dependencies * [`f98efb333`](https://github.com/siderolabs/talos/commit/f98efb333f89b8493c55b91698c917437b7af310) fix: ignore member not found error on leave cluster * [`b72bda0a4`](https://github.com/siderolabs/talos/commit/b72bda0a420f75ea0439cc0240dcf6d3363e5d48) fix: talosctl support and race tests * [`27233cf0f`](https://github.com/siderolabs/talos/commit/27233cf0fcf4031cbc8001504bed67b6d4a104f9) test: use node informer instead of raw watch * [`5dc15e8db`](https://github.com/siderolabs/talos/commit/5dc15e8db459ac632f0ae106e1cfc7eaab672adf) fix: update go-blockdevice to v2.0.9 * [`5f3acd0f2`](https://github.com/siderolabs/talos/commit/5f3acd0f26a35ac966d4ced01436f1dd3c03648b) fix: use correct default search domain * [`7e5d36d46`](https://github.com/siderolabs/talos/commit/7e5d36d469ff01153f40b16ab722f0ebe25d41ae) fix: pci driver rebind config validation * [`4b97bbc3f`](https://github.com/siderolabs/talos/commit/4b97bbc3fee1257d0d21be25e21493bfd1f45a80) fix: pull in containerd CNI deadlock fix * [`066480722`](https://github.com/siderolabs/talos/commit/0664807229e0688f092a453cbd3121dbe189ca39) test: fix apparmor tests * [`82ea44a6b`](https://github.com/siderolabs/talos/commit/82ea44a6b2aa0a35861ca454a09503a81332f824) fix: reduce installer image * [`78b3e7f4f`](https://github.com/siderolabs/talos/commit/78b3e7f4f1870085b719971c6f92dc866fe1e9d0) fix: get next rule number for IPv6 in the appropriate chain * [`675854aa0`](https://github.com/siderolabs/talos/commit/675854aa03b3913da3481337d995c206174cf004) docs: fix two typos * [`f70b7386a`](https://github.com/siderolabs/talos/commit/f70b7386ac3125f3b8ab6b1765338c7e3445ae5c) test: add a xfs makefs test * [`8212e4864`](https://github.com/siderolabs/talos/commit/8212e4864d11e69ed63be3f4e608e9ccbc788cc4) refactor: use quirks in kernel args

### Changes from siderolabs/crypto
1 commit

* [`0d45dee`](https://github.com/siderolabs/crypto/commit/0d45deefbcdd4bd6b6e549433b859083df55fc16) chore: bump deps

### Changes from siderolabs/extras
5 commits

* [`f4a110f`](https://github.com/siderolabs/extras/commit/f4a110f5f4b472743dc023413dca280bce491ec1) fix: build tc-redirect-tap as static binary * [`0840abb`](https://github.com/siderolabs/extras/commit/0840abb9b5e32560ff38577151fdc2f51812ce31) fix: pull in fixed CNI plugins from pkgs * [`52c217f`](https://github.com/siderolabs/extras/commit/52c217f693366bdf21772919ad94933fd160c5d4) feat: update dependencies * [`f755eb4`](https://github.com/siderolabs/extras/commit/f755eb483647d17e487f7cb62de8cc150a420c3c) chore: rekres to simplify `.kres.yaml` defaults * [`e5382fc`](https://github.com/siderolabs/extras/commit/e5382fc5f05d7ccfdb7c95819195caceac8ffcbf) chore: kresify renovate

### Changes from siderolabs/gen
1 commit

* [`5ae3afe`](https://github.com/siderolabs/gen/commit/5ae3afee65490ca9f4bd32ea41803ab3a17cad7e) chore: update hashtriemap implementation from the latest upstream

### Changes from siderolabs/go-talos-support
1 commit

* [`0f784bd`](https://github.com/siderolabs/go-talos-support/commit/0f784bd58b320543663679693c817515067f3021) fix: avoid deadlock on context cancel

### Changes from siderolabs/pkgs
35 commits

* [`5763e3e`](https://github.com/siderolabs/pkgs/commit/5763e3e0fe00cbd9010398e795085ba0377802e8) feat: update systemd to 257.2 * [`1e24b31`](https://github.com/siderolabs/pkgs/commit/1e24b31dc379251ad5248f94f548e5c7330f59ec) feat: update Linux to 6.12.11 * [`38749d1`](https://github.com/siderolabs/pkgs/commit/38749d1f08fcb46e522450c1ad530309a8fa327d) fix: build CNI plugins statically linked * [`5da83db`](https://github.com/siderolabs/pkgs/commit/5da83dbbe320768db8eb6175b1e7c5e8ff78389d) feat: bump NVIDIA driver versions * [`5934363`](https://github.com/siderolabs/pkgs/commit/59343630a024e48dfeba826eac45589d0bdcfb99) fix: certificates CA * [`57f492d`](https://github.com/siderolabs/pkgs/commit/57f492d4c3e51e01ab85d2727a7862b21ab21795) feat: bump dependencies * [`45b9ebe`](https://github.com/siderolabs/pkgs/commit/45b9ebed9437752c6516792678356a595f1ec62b) feat: update Linux to 6.2.10 * [`e00ad67`](https://github.com/siderolabs/pkgs/commit/e00ad677f0c7ef4005d26108143c3fe5e36aaab2) chore: rekres to fix reproducibility build * [`cfb4b0a`](https://github.com/siderolabs/pkgs/commit/cfb4b0a79490156864eab726debe20559d9c4240) feat: update Go to 1.23.5 * [`72f19a2`](https://github.com/siderolabs/pkgs/commit/72f19a2983e7abcb620ab57fae6e039158663f1a) feat: update containerd to v2.0.2 * [`17a80ee`](https://github.com/siderolabs/pkgs/commit/17a80eeb75b91211d4ffe8a910feb9fddcd1e585) feat: update Linux to 6.12.9 * [`c9d718d`](https://github.com/siderolabs/pkgs/commit/c9d718d3d6fd762ca3a649a14aa2d74e47d707e2) fix: adjust kernel options around ACPI/PCI/EFI * [`eb9d566`](https://github.com/siderolabs/pkgs/commit/eb9d56617faa56e42648a07b6756c18850e4a045) feat: update Linux to 6.12.8 * [`73e4353`](https://github.com/siderolabs/pkgs/commit/73e4353ad9e2dad6dc8544436776fd412c808d63) fix: update config-arm64 to add Rasperry Pi watchdog support * [`0ab2427`](https://github.com/siderolabs/pkgs/commit/0ab2427a8415d3f29cd4f52e3afd51f701aa5848) fix: dvb was missing I2C_MUX support and si2168 driver * [`c3ac8e2`](https://github.com/siderolabs/pkgs/commit/c3ac8e2d553b068dd982f5b9e48f6b1e0cfdd24d) chore: drop unused cert copy * [`e7eddcf`](https://github.com/siderolabs/pkgs/commit/e7eddcf9498634749a4241844660fd0e9d87fad4) feat: bump dependencies * [`0b00e86`](https://github.com/siderolabs/pkgs/commit/0b00e86ae92f821bdc19af73a5ba571b5051c89a) fix: patch containerd with CNI deadlock fix * [`9051c9a`](https://github.com/siderolabs/pkgs/commit/9051c9ac6f60e039c53248b52ba4ccd192e34b6b) feat: update Linux to 6.12.6 * [`6695012`](https://github.com/siderolabs/pkgs/commit/6695012e8d93d28ea70fc3ba32ed90770eea4363) chore: rekres to simplify `.kres.yaml` defaults * [`611ca38`](https://github.com/siderolabs/pkgs/commit/611ca38153fece4f2b34519325fbca22d34db7a0) chore: rekres to bring renovate under kres * [`a4c4215`](https://github.com/siderolabs/pkgs/commit/a4c4215e74b68765ada0745165b2e2fb5ee508f5) fix: drop cgroupsv1 controllers * [`28c909d`](https://github.com/siderolabs/pkgs/commit/28c909ddeaf0d33e0fc6c5fdf2333a18801cf178) feat: update Linux firmware to 20241210 * [`c40a9e9`](https://github.com/siderolabs/pkgs/commit/c40a9e9713b1fde14f7a967fd1be168bb905d7c9) feat: update Linux to 6.12.5 * [`d54ca83`](https://github.com/siderolabs/pkgs/commit/d54ca835a8868e5df55e2d0ffe3cb0dfa82a3395) feat: update containerd to v2.0.1 * [`86e3755`](https://github.com/siderolabs/pkgs/commit/86e3755deae2fc85d7e62bdcf82a54cb72fec6d5) fix: add CONFIG_INTEL_MEI_GSC_PROXY as module * [`8c31321`](https://github.com/siderolabs/pkgs/commit/8c3132135d5a0e01a9d66790b4b25c7c05e08fa5) feat: update ZFS to 2.2.7 * [`605f493`](https://github.com/siderolabs/pkgs/commit/605f493abfeac79151c02a776733011f19d6c43b) feat: update runc to v1.2.3 * [`1a55529`](https://github.com/siderolabs/pkgs/commit/1a555296764ab0ad83fb4eca6509bb64feff3b7b) feat: update Linux to 6.12.4 * [`52ba9a5`](https://github.com/siderolabs/pkgs/commit/52ba9a57358ef37ce3e4aa4033991dc77ad17fbb) feat: update Linux 6.12.3 * [`9cf35be`](https://github.com/siderolabs/pkgs/commit/9cf35bef274bb445e578f858a0a595b05b44a01f) feat: build host iptables with nftables support * [`71003a3`](https://github.com/siderolabs/pkgs/commit/71003a3c9bff00685917d6e272421a7206b1667e) feat: update Go to 1.23.4 * [`5b4d402`](https://github.com/siderolabs/pkgs/commit/5b4d402bd33f9313a21e4924be57aacce569f9ad) feat: build dvb kernel modules and CX23885 * [`b330af9`](https://github.com/siderolabs/pkgs/commit/b330af9b95d9115382c81f88b55c17b99f7ef355) chore: bring in KSPP recommendations * [`f81b190`](https://github.com/siderolabs/pkgs/commit/f81b190cc65dc93f9212d52cd95806ac79c170d2) feat: kernel driver support for RK3588 devices (Turing RK1)

### Changes from siderolabs/tools
8 commits

* [`7200845`](https://github.com/siderolabs/tools/commit/7200845be9d0318d23eb77a57e1b8992dd7e8187) feat: update dependencies * [`bc30a2a`](https://github.com/siderolabs/tools/commit/bc30a2a3ace873c80e4657b622e3142efb55cc28) feat: update Go to 1.23.5 * [`533b595`](https://github.com/siderolabs/tools/commit/533b5953d28213aae4d4ae576bedf5df84712458) chore: rekres to fix reproducibility * [`01568a5`](https://github.com/siderolabs/tools/commit/01568a5b42685c3ea19578a7f4d7ba07dc0f18cd) chore: use Make and Go from the toolchain image * [`0393558`](https://github.com/siderolabs/tools/commit/03935581049f82ff466defcc203c5bcc6db5b43a) feat: bump dependencies * [`7811a5f`](https://github.com/siderolabs/tools/commit/7811a5f2f23923cdfe5bfd47ee12ed9e88b29585) chore: rekres to simplify `.kres.yaml` defaults * [`0b8b905`](https://github.com/siderolabs/tools/commit/0b8b9054833d8187bb1f6209b2441719f6e62cfa) chore: kresify renovate config * [`fe34fb3`](https://github.com/siderolabs/tools/commit/fe34fb3d54ec9abe878a9304fbfc3e1e741c0ff4) feat: update Go to 1.23.4

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.2 -> v0.6.0 * **github.com/aws/aws-sdk-go-v2/config** v1.28.5 -> v1.28.7 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.20 -> v1.16.22 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.37.6 -> v1.37.8 * **github.com/containerd/cgroups/v3** v3.0.4 -> v3.0.5 * **github.com/containerd/containerd/v2** v2.0.1 -> v2.0.2 * **github.com/containerd/platforms** v1.0.0-rc.0 -> v1.0.0-rc.1 * **github.com/containernetworking/plugins** v1.6.0 -> v1.6.1 * **github.com/cosi-project/runtime** v0.7.6 -> v0.8.1 * **github.com/docker/cli** v27.3.1 -> v27.4.1 * **github.com/docker/docker** v27.3.1 -> v27.4.1 * **github.com/foxboron/go-uefi** fab4fdf2f2f3 -> 19dc140271bf * **github.com/google/go-tpm** v0.9.1 -> v0.9.3 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.1.0 -> v2.2.0 * **github.com/hetznercloud/hcloud-go/v2** v2.17.0 -> v2.17.1 * **github.com/opencontainers/runc** v1.2.2 -> v1.2.4 * **github.com/siderolabs/crypto** v0.5.0 -> v0.5.1 * **github.com/siderolabs/extras** v1.9.0 -> v1.10.0-alpha.0-2-gf4a110f * **github.com/siderolabs/gen** v0.7.0 -> v0.8.0 * **github.com/siderolabs/go-blockdevice/v2** v2.0.7 -> v2.0.13 * **github.com/siderolabs/go-talos-support** v0.1.1 -> v0.1.2 * **github.com/siderolabs/pkgs** v1.9.0-12-g9576b97 -> v1.10.0-alpha.0-34-g5763e3e * **github.com/siderolabs/talos/pkg/machinery** v1.9.0 -> v1.10.0-alpha.0 * **github.com/siderolabs/tools** v1.9.0-1-geaad82f -> v1.10.0-alpha.0-7-g7200845 * **github.com/thejerf/suture/v4** v4.0.5 -> v4.0.6 * **go.etcd.io/etcd/api/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/client/pkg/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/client/v3** v3.5.17 -> v3.5.18 * **go.etcd.io/etcd/etcdutl/v3** v3.5.17 -> v3.5.18 * **golang.org/x/net** v0.32.0 -> v0.34.0 * **golang.org/x/sys** v0.28.0 -> v0.29.0 * **golang.org/x/term** v0.27.0 -> v0.28.0 * **google.golang.org/grpc** v1.68.1 -> v1.69.2 * **google.golang.org/protobuf** v1.35.2 -> v1.36.1 * **k8s.io/api** v0.32.0 -> v0.32.1 * **k8s.io/apiserver** v0.32.0 -> v0.32.1 * **k8s.io/client-go** v0.32.0 -> v0.32.1 * **k8s.io/component-base** v0.32.0 -> v0.32.1 * **k8s.io/kube-scheduler** v0.32.0 -> v0.32.1 * **k8s.io/kubectl** v0.32.0 -> v0.32.1 * **k8s.io/kubelet** v0.32.0 -> v0.32.1 * **k8s.io/pod-security-admission** v0.32.0 -> v0.32.1 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.72 -> v1.2.73 Previous release can be found at [v1.9.0](https://github.com/siderolabs/talos/releases/tag/v1.9.0) ## [Talos 1.10.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.10.0-alpha.0) (2024-12-23) Welcome to the v1.10.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### cgroups v1 Talos Linux no longer supports `cgroupsv1` when running in non-container mode. The kernel argument `talos.unified_cgroup_hierarchy` is now ignored. ### Driver Rebind Talos 1.10 now supports a new machine config document named `PCIDriverRebindConfig` that allows rebinding the driver of a PCI device to a different target driver. See the [documentation](https://www.talos.dev/v1.10/reference/configuration/hardware/pcidriverrebindconfig/) for more information. ### Component Updates * Linux: 6.12.6 * CNI plugins: 1.6.1 Talos is built with Go 1.23.4. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Dmitry Sharshakov * Nico Berlee * Utku Ozdemir * Alexis La Goutte * Andrew Symington * Christian Luetke-Stetzkamp * Devin Buhl * Justin Garrison * KillianCdP * Marcel Hamer * PRIHLOP * Skyler Mäntysaari * Tine Jozelj * sflotat2607 ### Changes
63 commits

* [`bd85bd5b7`](https://github.com/siderolabs/talos/commit/bd85bd5b731463a42b7c82c66e9add251a280d26) fix: fix `Failed to initialize SELinux labeling handle` udev error * [`73c82e3e5`](https://github.com/siderolabs/talos/commit/73c82e3e5625ec1899f93312a671dfe6dffaea61) feat: bring Linux 6.12.6, CNI plugins 1.6.1 * [`c12b52491`](https://github.com/siderolabs/talos/commit/c12b52491456d1e52204eb290d0686a317358c7c) docs: document Kubernetes service registry incompat with K8s 1.32 * [`a5660ed77`](https://github.com/siderolabs/talos/commit/a5660ed778108843fe15b2b1582dd6556cf52b6c) feat: pcirebind controller * [`4c3261626`](https://github.com/siderolabs/talos/commit/4c3261626fa3f5ac36df71ec878f103a7c85c5c5) docs: fix several typos * [`fb3675321`](https://github.com/siderolabs/talos/commit/fb36753216cba7740040f2ec117c783221f66192) fix: dashboard crash on CPU data * [`dec0185c8`](https://github.com/siderolabs/talos/commit/dec0185c8505a7d43244fdb01f7a5decc77d116d) chore: reduce memory usage for secureboot functions * [`cee6c60a0`](https://github.com/siderolabs/talos/commit/cee6c60a0fc301b22c50fdf8bd2fc1d2b7ba3d54) fix: make talosctl time work with PTP time sync * [`f75604313`](https://github.com/siderolabs/talos/commit/f75604313d535180c38b33df53253ad4acba2ec1) chore: support gcr.io auth for cache and image gen * [`6ef2596da`](https://github.com/siderolabs/talos/commit/6ef2596da7b7e8be90e5b981621461352be7b134) docs: improve Hetzner documentation * [`7d39b9ec2`](https://github.com/siderolabs/talos/commit/7d39b9ec2bdd7883116626bf889c1331717f8438) feat: remove cgroupsv1 in non-container mode * [`8003536c7`](https://github.com/siderolabs/talos/commit/8003536c7ca20356adcd900e64463bd166d445af) fix: restore previous disk serial fetching * [`03116ef9b`](https://github.com/siderolabs/talos/commit/03116ef9bd2a215c20a2c4c7db133dd857ce2b16) chore: prepare for Talos 1.10 * [`00682fdd6`](https://github.com/siderolabs/talos/commit/00682fdd6e8fa23c6f9782840ea3e2b8ef250f66) docs: activate 1.9 docs as default * [`bea05f5c9`](https://github.com/siderolabs/talos/commit/bea05f5c9b6ce6f5d067eb357d26e30a49154b21) docs: update deploying-cilium.md * [`284ab1179`](https://github.com/siderolabs/talos/commit/284ab11794b3b076aa9ab2bb756e02292d854751) feat: support link altnames/aliases * [`5bfd829bf`](https://github.com/siderolabs/talos/commit/5bfd829bf9c8e46b6c51174be4b764d4c94b3320) docs: fix 'containter' typo * [`8d151b771`](https://github.com/siderolabs/talos/commit/8d151b771debc51d3fa40dfafc7a2e43f955a634) docs: clarify TALOSCONFIG for AWS * [`0ef19171f`](https://github.com/siderolabs/talos/commit/0ef19171f738e46346dfae71f43b8f7b47bf257d) fix: renovate typo * [`c568adc7d`](https://github.com/siderolabs/talos/commit/c568adc7dcd52c34924acc1eae849a2ca5b5a4d5) fix: renovate config * [`ec2e24fd9`](https://github.com/siderolabs/talos/commit/ec2e24fd9617db34e3bec753b5fe720670fa31a4) fix: match MAC addresses case-insensitive (nocloud) * [`41a0c440a`](https://github.com/siderolabs/talos/commit/41a0c440ad3f4de2a2ba9198d22609c55bdaf61b) chore: rekres for renovate changes * [`a49bb9ee4`](https://github.com/siderolabs/talos/commit/a49bb9ee45346268b26d3b9cff4dd017bfb9c829) feat: update Linux to 6.12.5 * [`b15917ecc`](https://github.com/siderolabs/talos/commit/b15917ecc626781e13de0e84b794ab77c97b3159) chore: add more debugging logs for META and volumes * [`2b1b326f0`](https://github.com/siderolabs/talos/commit/2b1b326f08966615a5a2f8708f94e6d1355773a7) docs: mention different paths for OpenEBS * [`9470e842f`](https://github.com/siderolabs/talos/commit/9470e842fca2d7dd0dae185bff7210a8af355445) test: cleanup failed Kubernetes pods * [`c9c685150`](https://github.com/siderolabs/talos/commit/c9c6851504fcda7b66395fbbba1fbc8b0e085d4a) fix: node identity flip * [`590c01657`](https://github.com/siderolabs/talos/commit/590c0165712aee60e752766d6bd3875443c353cb) feat: update containerd to v2.0.1 * [`18fa5a258`](https://github.com/siderolabs/talos/commit/18fa5a25876f41760ce8da5e918222e04b81949a) docs: update image-cache doc for iso * [`ab5bb6884`](https://github.com/siderolabs/talos/commit/ab5bb688420986a356aed55513a1dbd25de323e2) fix: generate and serve registries with port * [`58236066d`](https://github.com/siderolabs/talos/commit/58236066ddbcd7c401e945b70555ff315a2458f7) fix: support image cache on VFAT USB stick * [`e193a5071`](https://github.com/siderolabs/talos/commit/e193a507149c05e341abe019de219fe0b1bc83e3) fix: image cache integration test * [`08ee400fd`](https://github.com/siderolabs/talos/commit/08ee400fdbde368a54d6777cc31ceb91e1968ad2) test: fix flaky test NodeAddressSort * [`d45e8d1d1`](https://github.com/siderolabs/talos/commit/d45e8d1d1da28ca1b311198588d723cb491527eb) feat: update Kubernetes to 1.32.0 * [`136b12912`](https://github.com/siderolabs/talos/commit/136b12912165d5eb5c7c716b7f7dfcfbc42b08d4) chore: drop semicolon for supporting vfat filesystems * [`3e9e027ef`](https://github.com/siderolabs/talos/commit/3e9e027efbd2988f72eb2da0c1ab0e83ba52b950) test: add an option to boot from an USB stick * [`ef8c3e3b3`](https://github.com/siderolabs/talos/commit/ef8c3e3b3b245f7ffefa6c19930d5a0925ce666b) docs: fix typo in multus.md * [`d54414add`](https://github.com/siderolabs/talos/commit/d54414add4e4df1b5a7b166f155cdcca512d4ee2) fix: authorization config gen * [`cce72cfe8`](https://github.com/siderolabs/talos/commit/cce72cfe86beeb7ada9641df611046f4789e3bd8) docs: replace deprecated Hetzner server plans * [`81805103d`](https://github.com/siderolabs/talos/commit/81805103deada24b12b7d7861b2df5a5c788c86b) chore: enable proper parallel usage of TestDepth * [`e1b824eba`](https://github.com/siderolabs/talos/commit/e1b824ebada3d3dad9d2793fd12b5a948d8b51b5) docs: update ceph-with-rook.md * [`470b75563`](https://github.com/siderolabs/talos/commit/470b75563add4ce5bbce312c1e3dc783e63af1fa) fix: use mtu network option for podman * [`61b1489a0`](https://github.com/siderolabs/talos/commit/61b1489a0f0868c5b7e124544520bc46badef85c) fix: order volume config by the requested size * [`bc3039acd`](https://github.com/siderolabs/talos/commit/bc3039acdbc57e6be16a1bc6555894dff2da65c9) feat: update runc to 1.2.3 * [`30016a0a8`](https://github.com/siderolabs/talos/commit/30016a0a8d98d42e01c4d32acf9e600777d72d57) fix: avoid nil-pointer-panic in `RegistriesConfigController` * [`fe0457152`](https://github.com/siderolabs/talos/commit/fe045715277a4678b8e8c9632ec71e86bf17ace0) fix: power on the machine on reboot request in qemu power api * [`10da553ef`](https://github.com/siderolabs/talos/commit/10da553ef0dde5f87f09321400239baa51929a36) docs: build what's new for 1.9 * [`d946ccae3`](https://github.com/siderolabs/talos/commit/d946ccae31b87559a06cb1cefcefe8f937b73d8b) feat: update Linux to 6.12.4 * [`707a77bf6`](https://github.com/siderolabs/talos/commit/707a77bf64190470bf84c91cdff185981e80a31b) test: fix user namespace test, TPM2 fixes * [`c3537b2f5`](https://github.com/siderolabs/talos/commit/c3537b2f5491a890f626ba8fc47034d5059808af) feat: update Linux to 6.12.3 * [`cb4d9d673`](https://github.com/siderolabs/talos/commit/cb4d9d673432e4a0fba0d87bc64fde620d991082) docs: fix a few mistakes in release notes * [`c4724fc97`](https://github.com/siderolabs/talos/commit/c4724fc97598d8764b00fb56971d997a349a92e5) chore: add integration tests for image-cache * [`07220fe7f`](https://github.com/siderolabs/talos/commit/07220fe7f5a22444f7a085f5868f628ddd912b6d) fix: install iptables-nft to the host * [`14841750b`](https://github.com/siderolabs/talos/commit/14841750bf2fc09a9de0b32a7af0dc3f76e1019a) chore: add version compatibility for Talos 1.10 * [`852baf819`](https://github.com/siderolabs/talos/commit/852baf819d453a3d8d58ae9f029e280ae75e0cb1) feat: support vlan/bond in v1, vlan in v2 for nocloud * [`dd61ad861`](https://github.com/siderolabs/talos/commit/dd61ad86105c07c1ff8a101a0542af61699f0df3) fix: lock provisioning order of user disk partitions * [`d0773ff09`](https://github.com/siderolabs/talos/commit/d0773ff09df84b2dac8ecadc91023596050ce098) chore: update Go to 1.23.4 * [`7d6507189`](https://github.com/siderolabs/talos/commit/7d6507189ff9a99b3b05ee9528701b65af4ad147) feat: implement new address sorting algorithm * [`9081506d6`](https://github.com/siderolabs/talos/commit/9081506d6cde26d60a29f08a090e28da501e4bd1) feat: add process scheduling options * [`77e9db4ab`](https://github.com/siderolabs/talos/commit/77e9db4abf9c9b694d60c8803b436121dfe30ccd) test: use two workers in qemu tests by default * [`5a4bdf62a`](https://github.com/siderolabs/talos/commit/5a4bdf62a9bf1387b6489eaf2c9cc0770aa0b68c) feat: update Kubernetes to 1.32.0-rc.1 * [`d99bcc950`](https://github.com/siderolabs/talos/commit/d99bcc95031037f4b0990419d2ce1fd4280cbde9) chore: refactor mergeDNSServers func * [`0cde08d8b`](https://github.com/siderolabs/talos/commit/0cde08d8be1ad62c49fed148fd331ea5a212df4c) docs: add Turing RK1 docs to Single Board Computer section

### Changes from siderolabs/pkgs
17 commits

* [`9051c9a`](https://github.com/siderolabs/pkgs/commit/9051c9ac6f60e039c53248b52ba4ccd192e34b6b) feat: update Linux to 6.12.6 * [`6695012`](https://github.com/siderolabs/pkgs/commit/6695012e8d93d28ea70fc3ba32ed90770eea4363) chore: rekres to simplify `.kres.yaml` defaults * [`611ca38`](https://github.com/siderolabs/pkgs/commit/611ca38153fece4f2b34519325fbca22d34db7a0) chore: rekres to bring renovate under kres * [`a4c4215`](https://github.com/siderolabs/pkgs/commit/a4c4215e74b68765ada0745165b2e2fb5ee508f5) fix: drop cgroupsv1 controllers * [`28c909d`](https://github.com/siderolabs/pkgs/commit/28c909ddeaf0d33e0fc6c5fdf2333a18801cf178) feat: update Linux firmware to 20241210 * [`c40a9e9`](https://github.com/siderolabs/pkgs/commit/c40a9e9713b1fde14f7a967fd1be168bb905d7c9) feat: update Linux to 6.12.5 * [`d54ca83`](https://github.com/siderolabs/pkgs/commit/d54ca835a8868e5df55e2d0ffe3cb0dfa82a3395) feat: update containerd to v2.0.1 * [`86e3755`](https://github.com/siderolabs/pkgs/commit/86e3755deae2fc85d7e62bdcf82a54cb72fec6d5) fix: add CONFIG_INTEL_MEI_GSC_PROXY as module * [`8c31321`](https://github.com/siderolabs/pkgs/commit/8c3132135d5a0e01a9d66790b4b25c7c05e08fa5) feat: update ZFS to 2.2.7 * [`605f493`](https://github.com/siderolabs/pkgs/commit/605f493abfeac79151c02a776733011f19d6c43b) feat: update runc to v1.2.3 * [`1a55529`](https://github.com/siderolabs/pkgs/commit/1a555296764ab0ad83fb4eca6509bb64feff3b7b) feat: update Linux to 6.12.4 * [`52ba9a5`](https://github.com/siderolabs/pkgs/commit/52ba9a57358ef37ce3e4aa4033991dc77ad17fbb) feat: update Linux 6.12.3 * [`9cf35be`](https://github.com/siderolabs/pkgs/commit/9cf35bef274bb445e578f858a0a595b05b44a01f) feat: build host iptables with nftables support * [`71003a3`](https://github.com/siderolabs/pkgs/commit/71003a3c9bff00685917d6e272421a7206b1667e) feat: update Go to 1.23.4 * [`5b4d402`](https://github.com/siderolabs/pkgs/commit/5b4d402bd33f9313a21e4924be57aacce569f9ad) feat: build dvb kernel modules and CX23885 * [`b330af9`](https://github.com/siderolabs/pkgs/commit/b330af9b95d9115382c81f88b55c17b99f7ef355) chore: bring in KSPP recommendations * [`f81b190`](https://github.com/siderolabs/pkgs/commit/f81b190cc65dc93f9212d52cd95806ac79c170d2) feat: kernel driver support for RK3588 devices (Turing RK1)

### Changes from siderolabs/tools
1 commit

* [`fe34fb3`](https://github.com/siderolabs/tools/commit/fe34fb3d54ec9abe878a9304fbfc3e1e741c0ff4) feat: update Go to 1.23.4

### Dependency Changes * **github.com/containernetworking/plugins** v1.6.0 -> v1.6.1 * **github.com/foxboron/go-uefi** fab4fdf2f2f3 -> 19dc140271bf * **github.com/opencontainers/runc** v1.2.2 -> v1.2.3 * **github.com/siderolabs/go-blockdevice/v2** v2.0.7 -> v2.0.8 * **github.com/siderolabs/pkgs** v1.9.0-12-g9576b97 -> v1.10.0-alpha.0-16-g9051c9a * **github.com/siderolabs/talos/pkg/machinery** v1.9.0 -> v1.9.0-alpha.3 * **github.com/siderolabs/tools** v1.9.0-1-geaad82f -> v1.10.0-alpha.0 * **golang.org/x/net** v0.32.0 -> v0.33.0 Previous release can be found at [v1.9.0](https://github.com/siderolabs/talos/releases/tag/v1.9.0) ## [Talos 1.9.0-alpha.3](https://github.com/siderolabs/talos/releases/tag/v1.9.0-alpha.3) (2024-11-25) Welcome to the v1.9.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### AppArmor Talos Linux starting with v1.9 will ship with SELinux LSM enabled by default. If you need to use AppArmor LSM add the following to the machine configuration: ```yaml machine: install: extraKernelArgs: - -selinux - lsm=lockdown,capability,yama,apparmor,bpf - apparmor=1 ``` ### Auditd Talos Linux now starts a auditd service by default. Logs can be read with `talosctl logs auditd`. ### `talosctl cgroups` The `talosctl cgroups` command has been added to the `talosctl` tool. This command allows you to view the cgroup resource consumption and limits for a machine, e.g. `talosctl cgroups --preset memory`. ### Device Selectors Talos now supports matching on permanent hardware (MAC) address of the network interfaces. This is specifically useful to match bond members, as they change their hardware addresses when they become part of the bond. ### Direct Rendering Manager (DRM) Starting with Talos 1.9, the `i915` and `amdgpu` DRM drivers will be dropped from the Talos squashfs. There will be new system extensions named `i915` and `amdgpu` that would contain both the drivers and firmware packaged together. Upgrades via Image Factory will automatically include the new extensions if previously `i915-ucode` or `amdgpu-firmware` were used. ### Registry Mirrors In versions before Talos 1.9, there was a discrepancy between the way Talos itself and CRI plugin resolves registry mirrors: Talos will never fall back to the default registry if endpoints are configured, while CRI plugin will. > Note: Talos Linux pulls images for the `installer`, `kubelet`, `etcd`, while all workload images are pulled by the CRI plugin. In Talos 1.9 this was fixed, so that by default an upstream registry is used as a fallback in all cases, while new registry mirror configuration option `.skipFallback` can be used to disable this behavior both for Talos and CRI plugin. ### talosctl disks The command `talosctl disks` was removed, please use `talosctl get disks`, `talosctl get systemdisk`, and `talosctl get blockdevices` instead. ### talosctl wipe The new command `talosctl wipe disk` allows to wipe a disk or a partition which is not used as a volume. ### udevd Talos previously used `eudev` to provide `udevd`, now it uses `systemd-udevd` instead. ### Component Updates * Linux: 6.6.60 * containerd: 2.0.0 * Flannel: 0.26.0 * Kubernetes: 1.32.0-beta.0 * runc: 1.2.1 Talos is built with Go 1.23.3. ### User Namespaces Talos Linux now supports running Kubernetes pods with user namespaces enabled. Refer to the [documentation](https://www.talos.dev/v1.9/kubernetes-guides/configuration/usernamespace/) for more information. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitry Sharshakov * Dmitriy Matrenichev * Joakim Nohlgård * Jean-Francois Roy * Utku Ozdemir * blablu * Adolfo Ochagavía * Alessio Moiso * Dan Rue * David Backeus * Eddie Wang * Florian Ströger * Hexoplon * Jakob Maležič * KBAegis * Mike Beaumont * Nebula * Nico Berlee * OliviaBarrington * Philip Schmid * Philipp Kleber * Remko Molier * Robby Ciliberto * Roman Ivanov * Ryan Borstelmann * Sam Stelfox * Serge Logvinov * Sergey Melnik * Spencer Smith * SpiReCZ * Steven Cassamajor * Steven Kreitzer * Tim Jones * Variant9 * adilTepe * ekarlso * naed3r * nevermarine * solidDoWant * sophia-coldren ### Changes
189 commits

* [`af5d6b8c41`](https://github.com/siderolabs/talos/commit/af5d6b8c4166c7461f1774991e2896b11d503585) fix: show SELinux labels on pseudo-fs * [`f46922fa9a`](https://github.com/siderolabs/talos/commit/f46922fa9a815a2e9002c31dcc2793bf6dd93952) chore: fix dockerfile warnings * [`a13f82c594`](https://github.com/siderolabs/talos/commit/a13f82c59456574238a75959ff395746c93f1cfa) feat: udev: label device nodes * [`e899fb37fd`](https://github.com/siderolabs/talos/commit/e899fb37fde7ec2b39d4b0fa77c7a6b1ac9d9f16) feat: label created files in /etc * [`5f68c17eda`](https://github.com/siderolabs/talos/commit/5f68c17edab70451ec775e292222d9c634f4e863) feat: implement image cache configuration * [`0ffb2187a3`](https://github.com/siderolabs/talos/commit/0ffb2187a3e56744a6b67698882acf8281c147dd) feat: registry proxy * [`77cf84fb57`](https://github.com/siderolabs/talos/commit/77cf84fb572213e59880a05edcc1b1b365987ac4) feat: support generating iso with imagecache * [`5de6275b8f`](https://github.com/siderolabs/talos/commit/5de6275b8f883f6c3e7a17c04b427c0ff8b9e3f5) chore: image cache generator improvements * [`1a8cc5f8b2`](https://github.com/siderolabs/talos/commit/1a8cc5f8b277faec7cf00a3acc8e91a31f99ce85) feat: add SELinux labels to volumes * [`61b9129e0c`](https://github.com/siderolabs/talos/commit/61b9129e0c29b2402235065f27888459e6054e7a) fix: add directory entries and filemode to tarball * [`4caeae21e5`](https://github.com/siderolabs/talos/commit/4caeae21e548039a6a03471d35405788515a8751) refactor: optimize flags and SetLabel * [`6074a870ad`](https://github.com/siderolabs/talos/commit/6074a870ad7c63775a39c84b72f79699976a4a1f) feat: add e2fsprogs to talos rootfs * [`7ffcf5b932`](https://github.com/siderolabs/talos/commit/7ffcf5b932cc4064e5680efb20bf6fe6e7179cd4) docs: update getting started * [`c4c1a0d7c7`](https://github.com/siderolabs/talos/commit/c4c1a0d7c73d13e78bef935612cfdd593749a783) fix: make vmware platform common code build on all arches * [`cc768037f8`](https://github.com/siderolabs/talos/commit/cc768037f8d4bb022e98ddd4762f483ffd2a7a7f) feat: implement block device wipe * [`6fb518ae57`](https://github.com/siderolabs/talos/commit/6fb518ae57a7cea0cf0959309167805c6f2582a5) fix: don't activate LVM volumes in agent mode * [`0e3ed30723`](https://github.com/siderolabs/talos/commit/0e3ed307232eff54a42236489433eb8b6757cb6d) fix: no longer leak `Close` reader * [`4dc58cfdf3`](https://github.com/siderolabs/talos/commit/4dc58cfdf3ef99c67d6d1885216154d21294da74) chore: small fixes * [`f400ae911b`](https://github.com/siderolabs/talos/commit/f400ae911b49bd87b2c085ecc794ba44f0be0118) fix: small fixes for image cache generation * [`93754b7de6`](https://github.com/siderolabs/talos/commit/93754b7de6cec285e8e48d330be3c938dcd3e9c4) fix: config and platform manifest generation * [`95b2fc946e`](https://github.com/siderolabs/talos/commit/95b2fc946ec1d8166738d7bb2573e5d20d336afe) feat: image cache gen * [`e4c6186c63`](https://github.com/siderolabs/talos/commit/e4c6186c63ea8faabf2feb82a997a7dbdd35d966) chore: remove i915/amdgpu drivers * [`744ad12a6e`](https://github.com/siderolabs/talos/commit/744ad12a6e59c57b88869c055d598244f25e2e38) docs: update replicated-local-storage-with-openebs.md * [`fd713e4514`](https://github.com/siderolabs/talos/commit/fd713e45140f08b69bf0c08a28ca734685cb7672) feat: add permanent hardware addr to device selectors * [`d55a96e8cb`](https://github.com/siderolabs/talos/commit/d55a96e8cbbc8d013b6258da304d5c63590e2679) refactor: remove SELinux client_u and client_r * [`3a5b55fd22`](https://github.com/siderolabs/talos/commit/3a5b55fd2215f1b3b88868766f4220fcd1bdf1b5) fix: allow CEL expressions config merge * [`f1b15f580e`](https://github.com/siderolabs/talos/commit/f1b15f580eed5be808b6f657570540ae1906488a) chore: remove replace for safchain/ethtool * [`f9697a9a07`](https://github.com/siderolabs/talos/commit/f9697a9a07316226e13aa0d9a659f67fedaf7f47) fix: register controlplane node with NoSchedule taint * [`30f8b5a9f7`](https://github.com/siderolabs/talos/commit/30f8b5a9f76441d933341b91d753ae141a9f4d10) fix: registry mirror fallback handling * [`0f41e77434`](https://github.com/siderolabs/talos/commit/0f41e77434d6080270a3a18a8af0387791e8f282) feat: allow for onlink directive (nocloud) * [`e26d0043e0`](https://github.com/siderolabs/talos/commit/e26d0043e022eccf5ea9c9d9b4a57e4bff1f80cc) chore: code cleanup * [`43fe3807a8`](https://github.com/siderolabs/talos/commit/43fe3807a807a58ecf264f1628c5919f86d369b4) feat: implement tracking of blockdevice secondaries * [`8a7476c3ae`](https://github.com/siderolabs/talos/commit/8a7476c3ae1fdf5d4314d8915da72bc2cce19e38) fix: install on non-empty disk * [`8b4253d185`](https://github.com/siderolabs/talos/commit/8b4253d18544318e55f4886782a41b0e74155101) feat: update etcd to v3.5.17 * [`5a0fd5b882`](https://github.com/siderolabs/talos/commit/5a0fd5b8829657f487db594d90c5b31841c78a66) refactor: move early initialization functions to pre-initialize phase * [`9916e2cd8a`](https://github.com/siderolabs/talos/commit/9916e2cd8a32706a32572f408dc313bbfa159539) chore: update pkgs/tools/extras for Go 1.23.3 * [`20bbf02355`](https://github.com/siderolabs/talos/commit/20bbf02355c1ab12084ba36b3d1bea151029c2d6) docs: update vultr documentation * [`aea98940b7`](https://github.com/siderolabs/talos/commit/aea98940b7047c9e7f343efbbb8315c1bc6226d2) fix: arch linux search paths and names for QEMU provisioner * [`682718d4c9`](https://github.com/siderolabs/talos/commit/682718d4c99ff0bbae7be8b82eb6548e9b939f41) fix: use imager incoming version for extension validation * [`9a02ecc49f`](https://github.com/siderolabs/talos/commit/9a02ecc49fa801d7282bba4733698b8d7d6c221e) feat: rewrite install disk selector to use CEL expressions * [`eba35f4413`](https://github.com/siderolabs/talos/commit/eba35f4413c732d351d82d5b646fc3053e6239e3) docs: add note about PSP in Rook-Ceph guide * [`38b80fb1da`](https://github.com/siderolabs/talos/commit/38b80fb1dab586a9f14473d03415b793c2dfcda0) docs: add missing `--talosconfig` parameter to end of Hetzner guide * [`a07f66c918`](https://github.com/siderolabs/talos/commit/a07f66c9187b7a99211d3f9af286844511d66954) docs: gcp: fix controlplane nodes tags * [`4fe6dc8a0a`](https://github.com/siderolabs/talos/commit/4fe6dc8a0a86e0096797b3ce433a627fb973c962) chore: clean dns code * [`0290a38818`](https://github.com/siderolabs/talos/commit/0290a38818d5d1dd3327457fe5da3c2ba0328ce3) release(v1.9.0-alpha.2): prepare release * [`a309f6aa57`](https://github.com/siderolabs/talos/commit/a309f6aa57f4d99bbf17d0fef2fab4602e12b067) chore: fix nil pointer dereference in AWS uploader * [`333737f176`](https://github.com/siderolabs/talos/commit/333737f176f918ca3dd4217ddfed87c4da86bb9b) test: fix unpriviliged process runner test * [`2001167058`](https://github.com/siderolabs/talos/commit/200116705885b1f9935b719de71d661c695eae99) chore(ci): save support zip always after tests * [`6a42c3b8ed`](https://github.com/siderolabs/talos/commit/6a42c3b8ed58b6363a62710709f20266ca190b36) release(v1.9.0-alpha.1): prepare release * [`fb72e4b7b7`](https://github.com/siderolabs/talos/commit/fb72e4b7b74979acf743d20c7c099bc5513836e0) fix(ci): skip test if `UserNamespacesSupport` feature gate is not set * [`11380f933d`](https://github.com/siderolabs/talos/commit/11380f933ddd3fe42dc01d5ed09ceff0d62b417d) feat: display current CPU frequency on dashboard * [`fbce267aee`](https://github.com/siderolabs/talos/commit/fbce267aee98e3b4b6acace156aa22d75ad01d3d) feat: check bridged interfaces should not have addresses * [`942962bf00`](https://github.com/siderolabs/talos/commit/942962bf005a7036e04f4e572f3434f476cb567c) docs: add docs on usernamespace support in k8s * [`0406a05a98`](https://github.com/siderolabs/talos/commit/0406a05a986fabc3834c5a0de48362826268edbe) chore: update pkgs to ones built with gcc 14.2 * [`2e127627dc`](https://github.com/siderolabs/talos/commit/2e127627dce7251d5848718036780c91384c4396) docs: add apparmor enablement release notes * [`aa9311f3d8`](https://github.com/siderolabs/talos/commit/aa9311f3d840c7b5a69a1eb6ab4cb3b1a7bff135) fix: install disk matcher error * [`1800f81044`](https://github.com/siderolabs/talos/commit/1800f8104486f01e8a3437432e508893f02f809c) fix: selinux handling and apparmor tests * [`313bffadfb`](https://github.com/siderolabs/talos/commit/313bffadfb66b053f51046300764e94db088b18a) feat: update Kubernetes to v1.32.0-beta.0 * [`bbfa144510`](https://github.com/siderolabs/talos/commit/bbfa144510063fdcdebbc017b4fb382ac839370c) feat: update containerd to v2.0.0 * [`8e02b9fcbf`](https://github.com/siderolabs/talos/commit/8e02b9fcbfba421abd13ffe4fc8ea3892d4673eb) docs: update manual k8s upgrade docs * [`474949dc77`](https://github.com/siderolabs/talos/commit/474949dc77363123f0e8cf2c918ecacb82b4dbdd) feat: add dm-cache dm-cache-smq kernel modules * [`5112547d6b`](https://github.com/siderolabs/talos/commit/5112547d6b12b4ff40e7863f363cf519efb8c76c) chore: generate support zip for crashdump * [`a867f85e4c`](https://github.com/siderolabs/talos/commit/a867f85e4cb662a17b0738f1f0de4f1485ad925a) feat: label system socket and runtime files * [`398f714cff`](https://github.com/siderolabs/talos/commit/398f714cff04c524394933da17cbc21ad239cd42) feat: update Linux 6.6.59, runc 1.2.1 * [`05c620957c`](https://github.com/siderolabs/talos/commit/05c620957ca741451da395036e8eca59e631fe8d) feat: allow extra mounts for docker-based `talosctl cluster create` * [`cedabeddf7`](https://github.com/siderolabs/talos/commit/cedabeddf7d191f39525a61e65164f280b6807f8) chore: cleanup code * [`61d363e1d0`](https://github.com/siderolabs/talos/commit/61d363e1d093047886638d5bc5b9f2181c8bd894) chore: update go-auditlib * [`960a040491`](https://github.com/siderolabs/talos/commit/960a040491de5c95b104b4a39ea519095eb47931) feat: start enabling SELinux * [`7f3aaa21cd`](https://github.com/siderolabs/talos/commit/7f3aaa21cd8d969e26721235a4191ba3bdbc1f8f) fix: update permissions for logging directories in /var * [`0e6c983b84`](https://github.com/siderolabs/talos/commit/0e6c983b847f679a074c1794fbe77d21a5994233) fix: mount /sys/kernel/security conditionally * [`74b0e8c371`](https://github.com/siderolabs/talos/commit/74b0e8c3713a01f83758556672583880ce5c684a) fix: make route normalization keep family * [`0a3761c22f`](https://github.com/siderolabs/talos/commit/0a3761c22f98783c6696f143611d600287a471a3) fix: talosctl windows arm64 * [`4b10c5328b`](https://github.com/siderolabs/talos/commit/4b10c5328b861b4bcdcec3ca21bd55b91e969b44) chore: add Windows ARM64 build for talosctl * [`9abf16108e`](https://github.com/siderolabs/talos/commit/9abf16108ede75984845297d03673d56cb561c2f) feat: add auditd service * [`d464ca869f`](https://github.com/siderolabs/talos/commit/d464ca869f8949ffbb990c6fb02fbbcbe0abcbe1) chore: drop runc memfd bind added in #9069 * [`b54d26c2c3`](https://github.com/siderolabs/talos/commit/b54d26c2c3f3a52c6d1ec3fddb7a373175815de3) fix: mount pseudo sub-mountpoints in init * [`7aeb15f730`](https://github.com/siderolabs/talos/commit/7aeb15f73094a23aea1d6b263ca2eca061c8a257) chore: disable coredns cache for cluster domain * [`d8b652150c`](https://github.com/siderolabs/talos/commit/d8b652150cec408f2bf3307565b9db691b21bfe9) docs: add warning about NVMe bus path bug * [`3e16ab135e`](https://github.com/siderolabs/talos/commit/3e16ab135e2be8c9b652d67f9e7eadbc3691c5ca) feat: update Kubernetes to v1.32.0-alpha.3 * [`0b8b356777`](https://github.com/siderolabs/talos/commit/0b8b3567771fbe796926dc9a6e904e7102535170) feat: add BridgePort property to network machine configuration * [`b379506259`](https://github.com/siderolabs/talos/commit/b3795062596ef45dd309f1ca56aab31d2a1a0efc) fix: use more correct condition to skip generating hosts files * [`62ec7ec336`](https://github.com/siderolabs/talos/commit/62ec7ec3367233823c09befddc5ad312aa607822) refactor: replace the old v1 mount package with new one * [`0ece13c623`](https://github.com/siderolabs/talos/commit/0ece13c6236c7eda474d3734fcc4c4060299ac43) docs: update network-config.md (cont) * [`93827f0485`](https://github.com/siderolabs/talos/commit/93827f0485a92b46da83b80a2a55f2569f70fe57) docs: update network-config.md * [`423b1e5fb2`](https://github.com/siderolabs/talos/commit/423b1e5fb22d9e785a3832741d796120b84a5e38) fix: do not trim 0 from process SELinux label * [`2136358d65`](https://github.com/siderolabs/talos/commit/2136358d65ddf6ad040ed62c835b335f99a59399) feat: introduce metal agent mode * [`0e15955fcc`](https://github.com/siderolabs/talos/commit/0e15955fcc5d464c5f0ffd1a44eebf4bf32f4844) chore: small refactoring * [`66012a7f26`](https://github.com/siderolabs/talos/commit/66012a7f269010c5ed412d139b14c470063f2429) feat: remove wrapperd and launch processes directly * [`3a0a17ae66`](https://github.com/siderolabs/talos/commit/3a0a17ae66dab5c983571fab0f3eac3f87fbc17c) fix: prevent panic in nocloud platform code * [`dc0c6acbd7`](https://github.com/siderolabs/talos/commit/dc0c6acbd765b6e7838d6af4f1903242d5073782) refactor: remove unmaintained github.com/vishvananda/netlink * [`78353f7918`](https://github.com/siderolabs/talos/commit/78353f79188e81d064c354f6ef3fe3b2e023c644) feat: add parsing of vlanNNNN:ethX style VLAN cmdline args * [`9db7a36bfc`](https://github.com/siderolabs/talos/commit/9db7a36bfc45c9c15fd661fb2a6319dcf4fef210) fix: generation of SecureBoot iso * [`c755b6d7e4`](https://github.com/siderolabs/talos/commit/c755b6d7e4600fdfb32be50422b7efb0fdabef63) fix: update the CRI sandbox image reference * [`cec290b354`](https://github.com/siderolabs/talos/commit/cec290b354773b2b0f2c2ae9d57f36e06fe2654d) feat: allow extensions to log to console * [`b7801df827`](https://github.com/siderolabs/talos/commit/b7801df827d8e1e9a2db7dac0a62c3802de4d73c) fix: wait for udevd to be running before activating LVM * [`d4cb478a50`](https://github.com/siderolabs/talos/commit/d4cb478a50ce41c3699b7846388e537ddf18a703) docs: improve field description for BridgeSTP, BridgeVLAN * [`7329824b24`](https://github.com/siderolabs/talos/commit/7329824b2411fef3b23fd90380033441048f6512) docs: add Mynewsdesk to ADOPTERS.md * [`a13cf76a34`](https://github.com/siderolabs/talos/commit/a13cf76a3415f458ff3235981c1be8202e1800bb) chore: simplify `DNSUpstreamController` and `DNSUpstream` resource * [`62d185473e`](https://github.com/siderolabs/talos/commit/62d185473e258c0c34eff5aed4c18d81d4b92a89) fix: talosctl process null character * [`77d7368eae`](https://github.com/siderolabs/talos/commit/77d7368eae2da6d2c9aa896afc8013007909a958) feat: update containerd to v2.0.0-rc.6 * [`d39393879a`](https://github.com/siderolabs/talos/commit/d39393879a1f98ac3de7a96808301d1e07fd95f3) fix: rework the 'metal-iso' config acquisition * [`1993afca9f`](https://github.com/siderolabs/talos/commit/1993afca9fff7e889b497ec3241cfdca42294f18) chore: create /usr/etc in a different step * [`8680351c13`](https://github.com/siderolabs/talos/commit/8680351c131d29a76682569742dbd44c8ffe47d3) chore: move system extensions' udev rules * [`3067f64c84`](https://github.com/siderolabs/talos/commit/3067f64c8435ef2d5453100a1584dc3c6915ba0b) feat: update Flannel to v0.26.0 * [`8658d6865f`](https://github.com/siderolabs/talos/commit/8658d6865fa0bcbfcebe483b7332d3b56e239979) docs: typo in deploying cilium * [`49bbadc4bf`](https://github.com/siderolabs/talos/commit/49bbadc4bf1e79e48c057d473ae21426b273c588) docs: add documentation on performance tuning * [`534b0ce183`](https://github.com/siderolabs/talos/commit/534b0ce1833462b22f3761258e0e95813a355fb2) feat: update runc to 1.2.0 final * [`2172535237`](https://github.com/siderolabs/talos/commit/21725352373da7835d95f8f934847dab404782f8) docs: fix image factory links * [`375e3da73f`](https://github.com/siderolabs/talos/commit/375e3da73fcb02c7caea2576289fefdc395a1ed2) feat: update Kubernetes to 1.32.0-alpha.2 * [`9e6f64df04`](https://github.com/siderolabs/talos/commit/9e6f64df047527ecb42df5fdf5fd2f9767d21437) fix: improve error messages for invalid bridge/bond configuration * [`7c8c72c2b2`](https://github.com/siderolabs/talos/commit/7c8c72c2b2a4edb412e097a9e013ab21727339cf) fix: correct error message for invalid ip= * [`ead46997c9`](https://github.com/siderolabs/talos/commit/ead46997c918ab1139ca12e87beefbbda29614e1) chore: rename tpm2.PCRExtent -> tpm2.PCRExtend * [`867c4b8125`](https://github.com/siderolabs/talos/commit/867c4b8125ee738f9a82e5e87809eb95bdd2f778) docs: fix typo in prodnotes.md * [`1b22df48a4`](https://github.com/siderolabs/talos/commit/1b22df48a41578d19fb512bd8111a481b64011e2) chore: support debug shell for advanced development * [`c14b446229`](https://github.com/siderolabs/talos/commit/c14b4462292bd7e6088fce35d6880a9b2b56335c) feat: update Kubernetes to v1.32.0-alpha.1 * [`29780d35a0`](https://github.com/siderolabs/talos/commit/29780d35a052134d50576f6506c2728489a30506) test: add an integration test for verifying process parameters * [`3d342af447`](https://github.com/siderolabs/talos/commit/3d342af4479ed12e2af10021ec4e7ab9c2af6d75) fix: update incorrect alias for PCIDevice resource * [`f7d35a5e0b`](https://github.com/siderolabs/talos/commit/f7d35a5e0b4e3a04a639d663e5a580e22fea76db) release(v1.9.0-alpha.0): prepare release * [`e0434d77d7`](https://github.com/siderolabs/talos/commit/e0434d77d754f8834ba903f4c09b08634cfd3934) feat: update dependencies * [`5c5a248861`](https://github.com/siderolabs/talos/commit/5c5a248861c8e5848f9a23cd0cd7b3b749f21e4b) feat: add Talos 1.9 compatibility guarantees * [`bc4c21f41a`](https://github.com/siderolabs/talos/commit/bc4c21f41a0066ba6cefb5b753c52d76a6b0f629) test: add json logs test environment * [`71faa32942`](https://github.com/siderolabs/talos/commit/71faa3294246947f6bd212979ceb31e793ae0604) docs: nvidia proprietary/oss hardware requirement * [`59a78da42c`](https://github.com/siderolabs/talos/commit/59a78da42cdea8fbccc35d0851f9b0eef928261b) chore: add proto-codec/codec * [`7ff1cedfe3`](https://github.com/siderolabs/talos/commit/7ff1cedfe3eee51505c30439eec4a2df9b452b2e) chore: update siderolabs/crypto module and return proper ALPN * [`ccbd5aed39`](https://github.com/siderolabs/talos/commit/ccbd5aed39b360664d1f80c8b146050e9df9ff7b) feat: optionally decode hcloud userdata as base64 * [`34f652ce82`](https://github.com/siderolabs/talos/commit/34f652ce822fcb70a292289fe6ba5d1bd7a34f97) feat: add well-known app.kubernetes.io labels to control-plane pods * [`fc89dc2164`](https://github.com/siderolabs/talos/commit/fc89dc21643a923cb7d0d3944405521bf849631b) fix: support `extra-disks` when using iso * [`f2bff814de`](https://github.com/siderolabs/talos/commit/f2bff814de0b237fbed419234b935dc9f9637554) chore: add arm64 target for integration-test * [`5853bb0ea4`](https://github.com/siderolabs/talos/commit/5853bb0ea4d6a65635086bdef617d6d0800cabd0) fix: json logging panic * [`a859cff364`](https://github.com/siderolabs/talos/commit/a859cff364aa4dc9b4b880417b821f7ecf5602ac) chore: use virtio driver for disks in arm64 * [`db248de88d`](https://github.com/siderolabs/talos/commit/db248de88dec2467e4340f699cde98217979ba4b) chore(ci): add config for lldpd extension * [`9f0de9f43d`](https://github.com/siderolabs/talos/commit/9f0de9f43dc4467f0bdeda117b4946ae12db50ab) test: update provision upgrade tests for Talos 1.9 * [`39fe285e69`](https://github.com/siderolabs/talos/commit/39fe285e69691059f91d8c7c5506e156356263d9) fix: skip ram disks * [`a9bff3a1d0`](https://github.com/siderolabs/talos/commit/a9bff3a1d084c32a654555e71e2592e60edbdcb6) test: skip no error test in Cilium * [`4d902021bb`](https://github.com/siderolabs/talos/commit/4d902021bb3c55bc212cbb3e2443b6552400622f) fix: do not use pflag csv comma reader for config-patch * [`5371788ce1`](https://github.com/siderolabs/talos/commit/5371788ce169a0381e08f0d902ac81f3f89ba5bd) fix: typo in documentation * [`8a228ba6bc`](https://github.com/siderolabs/talos/commit/8a228ba6bc702f21fca06dc2ecb3e8e846839cd3) docs: add egress documentation * [`182325cb07`](https://github.com/siderolabs/talos/commit/182325cb0791da1d4dcd3914a643c44232502524) test: skip lvm test if not enough user disks available * [`519a48302e`](https://github.com/siderolabs/talos/commit/519a48302e771fd9b331913166d55c50fff4961a) fix: wipe system partitions correctly via kernel args * [`0a2b4556c5`](https://github.com/siderolabs/talos/commit/0a2b4556c55eda27536ee563f60bcf5d69379479) fix: volume encryption with failing keyslots * [`6affbd3182`](https://github.com/siderolabs/talos/commit/6affbd3182ebe0209ed5433c534062b7ad672b6a) fix: update grpc-go the latest patch release * [`77a4a4adc7`](https://github.com/siderolabs/talos/commit/77a4a4adc7232b4382f2a530f4056a1fff6c50b4) fix: scaleway metadata * [`7acadc0c8f`](https://github.com/siderolabs/talos/commit/7acadc0c8fa969e4de7f0d4f68b0fd0cd833b489) fix: do not stop udevd before unmounting volumes * [`6a081055b0`](https://github.com/siderolabs/talos/commit/6a081055b0dd4e3ce5c40392c8415a0a55b2591c) feat: update Flannel to v0.25.7 * [`2362f6d3ee`](https://github.com/siderolabs/talos/commit/2362f6d3ee51a0a8b541a872d39ac82892502e17) fix: improve container detection * [`b67bc73fd3`](https://github.com/siderolabs/talos/commit/b67bc73fd30a8e07f26c47a746ca53f2af41d366) fix: fix mdadm system extension * [`f08669c7a9`](https://github.com/siderolabs/talos/commit/f08669c7a9583a559dc53f233798305bbab07b8a) feat: bring in lpfc kernel module driver * [`6a014374be`](https://github.com/siderolabs/talos/commit/6a014374be26f0caf8faa90a34f2476e0e77a46a) feat: enable QEDF driver * [`f711907e03`](https://github.com/siderolabs/talos/commit/f711907e038cea20f6b831ea5ad8c3b18638c1b4) fix: make /var/run empty on reboots * [`7d02eb60f4`](https://github.com/siderolabs/talos/commit/7d02eb60f47652f4b72f170b28a8b964729af013) docs: fix typo in CloudStack docs * [`74861573a7`](https://github.com/siderolabs/talos/commit/74861573a793f9e143d7d2638990f37ec639aa88) fix: multiple fixes for LVM activation * [`74c12c20e0`](https://github.com/siderolabs/talos/commit/74c12c20e02e4ec29b2b374cebc996ddf8fa90c7) feat: replace eudev with systemd-udevd * [`0a4df4ef84`](https://github.com/siderolabs/talos/commit/0a4df4ef84467014d5be4b4ec57de0e778cfb21e) docs: fix nvidia CRI config example * [`afc1e1a46a`](https://github.com/siderolabs/talos/commit/afc1e1a46a559aac3aa5f4a2708ba8d2c9228929) docs: fix typo in extraMounts directory * [`a341bdb064`](https://github.com/siderolabs/talos/commit/a341bdb0640294a07939670919c56cbfa7a861c4) fix: prevent file descriptors leaks to child processes * [`dec653bfe1`](https://github.com/siderolabs/talos/commit/dec653bfe1feb84ea2ed1a779b1bfc783dc61160) chore: better lvm2 tests * [`908fd8789c`](https://github.com/siderolabs/talos/commit/908fd8789cc1b22e556a7ffe307409931976ba08) feat: support cgroup deep analysis in `talosctl` * [`aa846cc186`](https://github.com/siderolabs/talos/commit/aa846cc186c1c6125f8f39ea084fa2023512656f) feat: add support for CI Network config in nocloud * [`10f2539f23`](https://github.com/siderolabs/talos/commit/10f2539f237aeb3af2caeb3c349c062f203219b6) chore: disable cloud-images cron workflow * [`b07a8b36b2`](https://github.com/siderolabs/talos/commit/b07a8b36b24d57337323e72d6032304c4cade927) chore: ignore more plugins for system containerd * [`392c4798f0`](https://github.com/siderolabs/talos/commit/392c4798f0bff7cb4518609deae7c90581f013f5) feat: prepare for Talos 1.9 * [`ea7bf9fb43`](https://github.com/siderolabs/talos/commit/ea7bf9fb43dff8cf8ec4dfd4f629e8f826bc2ded) docs: update storage.md * [`4ab8dee69a`](https://github.com/siderolabs/talos/commit/4ab8dee69ac07c811cbe121ca9e2d9bd01148863) fix: build talosctl without `tcell_minimal` * [`2fa019bd97`](https://github.com/siderolabs/talos/commit/2fa019bd9751ad96085ade52628023adf17658d3) docs: enable 'edit on GitHub' link * [`d2ccbc2b15`](https://github.com/siderolabs/talos/commit/d2ccbc2b1512b6323d48a764c4af534d49b4bd27) docs: update hetzner documentation for CCM * [`d498f647cd`](https://github.com/siderolabs/talos/commit/d498f647cd9dfcd575f51005c9b78c2c1c7b51ca) docs: fix Kernel Self Protection Project (KSPP) references * [`0ec75463ee`](https://github.com/siderolabs/talos/commit/0ec75463eecebfb543a64b0c859ba0b2477e406f) docs: make Talos 1.8 current release * [`9b77698cf2`](https://github.com/siderolabs/talos/commit/9b77698cf2ff64c6f6d198d05c2012ab7fa858be) fix: update blockdevice library to v2.0.2 * [`e46227ab95`](https://github.com/siderolabs/talos/commit/e46227ab95a6d06132e82315f55b5ced533ddabb) docs: fix kubespan name inconsistency * [`6b15ca19cd`](https://github.com/siderolabs/talos/commit/6b15ca19cd1291b8a245d72d5153827945cad037) fix: audit and fix cgroup reservations * [`32b5d01ed3`](https://github.com/siderolabs/talos/commit/32b5d01ed3396e8f54a245cc6d9818119aec8291) chore: bump lvm2 * [`6484581eb8`](https://github.com/siderolabs/talos/commit/6484581eb888996a8dc829915439fb63606dd794) feat: allow /sbin/ldconfig in extensions * [`9fa08e8437`](https://github.com/siderolabs/talos/commit/9fa08e843728dbd85ed7e0035f59cdd6232de9a9) chore: refactor tests * [`d8ab4981b6`](https://github.com/siderolabs/talos/commit/d8ab4981b626ff41fbcdb526a032a5584519e3df) feat: support lvm auto activation * [`8166a58b36`](https://github.com/siderolabs/talos/commit/8166a58b364f760212b2a610ce0d764b8b4c5c46) fix: filter out non-printable characters in process line * [`806b6aaf52`](https://github.com/siderolabs/talos/commit/806b6aaf52f20ed0f32107b3d0372d6e3ff974be) docs: add SECURITY.md * [`7bd26df308`](https://github.com/siderolabs/talos/commit/7bd26df30803307e4eece3e382aafebc55e7b260) docs: document `/dev/net/tun` compatibility * [`18daedb511`](https://github.com/siderolabs/talos/commit/18daedb511e769717ba56eb05cccab72118a4813) fix: strategic merge patch delete for map keys * [`f3370529ac`](https://github.com/siderolabs/talos/commit/f3370529ac042865a4b2d793465916fcae2d4b33) docs: correct typo * [`8d6884a8e2`](https://github.com/siderolabs/talos/commit/8d6884a8e28e1bfa29f9a479e0f7179819cf70cd) test: add a test for inline machine config trusted roots * [`d4a6d017db`](https://github.com/siderolabs/talos/commit/d4a6d017dbb91e22c60787cdf64b242057b1ebef) fix: ignore invalid NTP responses * [`869f8379f2`](https://github.com/siderolabs/talos/commit/869f8379f2317175901e8cb3deec4b800e7ab603) feat: update default Kubernetes version to 1.31.1 * [`780a1f198a`](https://github.com/siderolabs/talos/commit/780a1f198a5eedd33a27060bdf116bd3a3b26426) fix: update CoreDNS health check * [`79cd031588`](https://github.com/siderolabs/talos/commit/79cd031588a0710b865414f919742ee3ffb998ed) chore: account for resource sorting in dns upstream resource * [`e17fafaca2`](https://github.com/siderolabs/talos/commit/e17fafaca2a16990bc424b54120c49ddbaf8cee1) chore: drop `activateLogicalVolumes` sequencer step * [`a294b366f2`](https://github.com/siderolabs/talos/commit/a294b366f24c6580d304c6c8ad34f481079dc795) fix: parse SideroLink API endpoint correctly * [`a9269ac7b1`](https://github.com/siderolabs/talos/commit/a9269ac7b1217aa2d247c0215c5f2755af468b44) fix: remove extra logging on ethtool ioctl failures * [`5c6277d171`](https://github.com/siderolabs/talos/commit/5c6277d171eea58878ce4fcb4d2fdb7154333ae7) feat: update etcd to 3.5.16 * [`c1ed2984b8`](https://github.com/siderolabs/talos/commit/c1ed2984b85dca791a5081c5da26bba75e3cd579) docs: add what's new for Talos 1.8

### Changes since v1.9.0-alpha.2
44 commits

* [`af5d6b8c4`](https://github.com/siderolabs/talos/commit/af5d6b8c4166c7461f1774991e2896b11d503585) fix: show SELinux labels on pseudo-fs * [`f46922fa9`](https://github.com/siderolabs/talos/commit/f46922fa9a815a2e9002c31dcc2793bf6dd93952) chore: fix dockerfile warnings * [`a13f82c59`](https://github.com/siderolabs/talos/commit/a13f82c59456574238a75959ff395746c93f1cfa) feat: udev: label device nodes * [`e899fb37f`](https://github.com/siderolabs/talos/commit/e899fb37fde7ec2b39d4b0fa77c7a6b1ac9d9f16) feat: label created files in /etc * [`5f68c17ed`](https://github.com/siderolabs/talos/commit/5f68c17edab70451ec775e292222d9c634f4e863) feat: implement image cache configuration * [`0ffb2187a`](https://github.com/siderolabs/talos/commit/0ffb2187a3e56744a6b67698882acf8281c147dd) feat: registry proxy * [`77cf84fb5`](https://github.com/siderolabs/talos/commit/77cf84fb572213e59880a05edcc1b1b365987ac4) feat: support generating iso with imagecache * [`5de6275b8`](https://github.com/siderolabs/talos/commit/5de6275b8f883f6c3e7a17c04b427c0ff8b9e3f5) chore: image cache generator improvements * [`1a8cc5f8b`](https://github.com/siderolabs/talos/commit/1a8cc5f8b277faec7cf00a3acc8e91a31f99ce85) feat: add SELinux labels to volumes * [`61b9129e0`](https://github.com/siderolabs/talos/commit/61b9129e0c29b2402235065f27888459e6054e7a) fix: add directory entries and filemode to tarball * [`4caeae21e`](https://github.com/siderolabs/talos/commit/4caeae21e548039a6a03471d35405788515a8751) refactor: optimize flags and SetLabel * [`6074a870a`](https://github.com/siderolabs/talos/commit/6074a870ad7c63775a39c84b72f79699976a4a1f) feat: add e2fsprogs to talos rootfs * [`7ffcf5b93`](https://github.com/siderolabs/talos/commit/7ffcf5b932cc4064e5680efb20bf6fe6e7179cd4) docs: update getting started * [`c4c1a0d7c`](https://github.com/siderolabs/talos/commit/c4c1a0d7c73d13e78bef935612cfdd593749a783) fix: make vmware platform common code build on all arches * [`cc768037f`](https://github.com/siderolabs/talos/commit/cc768037f8d4bb022e98ddd4762f483ffd2a7a7f) feat: implement block device wipe * [`6fb518ae5`](https://github.com/siderolabs/talos/commit/6fb518ae57a7cea0cf0959309167805c6f2582a5) fix: don't activate LVM volumes in agent mode * [`0e3ed3072`](https://github.com/siderolabs/talos/commit/0e3ed307232eff54a42236489433eb8b6757cb6d) fix: no longer leak `Close` reader * [`4dc58cfdf`](https://github.com/siderolabs/talos/commit/4dc58cfdf3ef99c67d6d1885216154d21294da74) chore: small fixes * [`f400ae911`](https://github.com/siderolabs/talos/commit/f400ae911b49bd87b2c085ecc794ba44f0be0118) fix: small fixes for image cache generation * [`93754b7de`](https://github.com/siderolabs/talos/commit/93754b7de6cec285e8e48d330be3c938dcd3e9c4) fix: config and platform manifest generation * [`95b2fc946`](https://github.com/siderolabs/talos/commit/95b2fc946ec1d8166738d7bb2573e5d20d336afe) feat: image cache gen * [`e4c6186c6`](https://github.com/siderolabs/talos/commit/e4c6186c63ea8faabf2feb82a997a7dbdd35d966) chore: remove i915/amdgpu drivers * [`744ad12a6`](https://github.com/siderolabs/talos/commit/744ad12a6e59c57b88869c055d598244f25e2e38) docs: update replicated-local-storage-with-openebs.md * [`fd713e451`](https://github.com/siderolabs/talos/commit/fd713e45140f08b69bf0c08a28ca734685cb7672) feat: add permanent hardware addr to device selectors * [`d55a96e8c`](https://github.com/siderolabs/talos/commit/d55a96e8cbbc8d013b6258da304d5c63590e2679) refactor: remove SELinux client_u and client_r * [`3a5b55fd2`](https://github.com/siderolabs/talos/commit/3a5b55fd2215f1b3b88868766f4220fcd1bdf1b5) fix: allow CEL expressions config merge * [`f1b15f580`](https://github.com/siderolabs/talos/commit/f1b15f580eed5be808b6f657570540ae1906488a) chore: remove replace for safchain/ethtool * [`f9697a9a0`](https://github.com/siderolabs/talos/commit/f9697a9a07316226e13aa0d9a659f67fedaf7f47) fix: register controlplane node with NoSchedule taint * [`30f8b5a9f`](https://github.com/siderolabs/talos/commit/30f8b5a9f76441d933341b91d753ae141a9f4d10) fix: registry mirror fallback handling * [`0f41e7743`](https://github.com/siderolabs/talos/commit/0f41e77434d6080270a3a18a8af0387791e8f282) feat: allow for onlink directive (nocloud) * [`e26d0043e`](https://github.com/siderolabs/talos/commit/e26d0043e022eccf5ea9c9d9b4a57e4bff1f80cc) chore: code cleanup * [`43fe3807a`](https://github.com/siderolabs/talos/commit/43fe3807a807a58ecf264f1628c5919f86d369b4) feat: implement tracking of blockdevice secondaries * [`8a7476c3a`](https://github.com/siderolabs/talos/commit/8a7476c3ae1fdf5d4314d8915da72bc2cce19e38) fix: install on non-empty disk * [`8b4253d18`](https://github.com/siderolabs/talos/commit/8b4253d18544318e55f4886782a41b0e74155101) feat: update etcd to v3.5.17 * [`5a0fd5b88`](https://github.com/siderolabs/talos/commit/5a0fd5b8829657f487db594d90c5b31841c78a66) refactor: move early initialization functions to pre-initialize phase * [`9916e2cd8`](https://github.com/siderolabs/talos/commit/9916e2cd8a32706a32572f408dc313bbfa159539) chore: update pkgs/tools/extras for Go 1.23.3 * [`20bbf0235`](https://github.com/siderolabs/talos/commit/20bbf02355c1ab12084ba36b3d1bea151029c2d6) docs: update vultr documentation * [`aea98940b`](https://github.com/siderolabs/talos/commit/aea98940b7047c9e7f343efbbb8315c1bc6226d2) fix: arch linux search paths and names for QEMU provisioner * [`682718d4c`](https://github.com/siderolabs/talos/commit/682718d4c99ff0bbae7be8b82eb6548e9b939f41) fix: use imager incoming version for extension validation * [`9a02ecc49`](https://github.com/siderolabs/talos/commit/9a02ecc49fa801d7282bba4733698b8d7d6c221e) feat: rewrite install disk selector to use CEL expressions * [`eba35f441`](https://github.com/siderolabs/talos/commit/eba35f4413c732d351d82d5b646fc3053e6239e3) docs: add note about PSP in Rook-Ceph guide * [`38b80fb1d`](https://github.com/siderolabs/talos/commit/38b80fb1dab586a9f14473d03415b793c2dfcda0) docs: add missing `--talosconfig` parameter to end of Hetzner guide * [`a07f66c91`](https://github.com/siderolabs/talos/commit/a07f66c9187b7a99211d3f9af286844511d66954) docs: gcp: fix controlplane nodes tags * [`4fe6dc8a0`](https://github.com/siderolabs/talos/commit/4fe6dc8a0a86e0096797b3ce433a627fb973c962) chore: clean dns code

### Changes from siderolabs/crypto
1 commit

* [`58b2f92`](https://github.com/siderolabs/crypto/commit/58b2f9291c7e763a7210cfa681f88a7fa2230bf3) chore: use HTTP/2 ALPN by default

### Changes from siderolabs/discovery-api
1 commit

* [`005e92c`](https://github.com/siderolabs/discovery-api/commit/005e92cf4ad0059334bfd35285a97c85f12aa263) chore: rekres and regen

### Changes from siderolabs/discovery-client
1 commit

* [`b74fb90`](https://github.com/siderolabs/discovery-client/commit/b74fb9039fcfd8db9d6becf3044f9f41f387ea27) fix: allow custom TLS config for the client

### Changes from siderolabs/extras
3 commits

* [`78ba66b`](https://github.com/siderolabs/extras/commit/78ba66b040e3288c425c10055068784a19bec804) feat: update Go to 1.23.3 * [`eab6e58`](https://github.com/siderolabs/extras/commit/eab6e58aa9bdf49789cd4d64d2e27f61023421ca) feat: update dependencies * [`1459d78`](https://github.com/siderolabs/extras/commit/1459d78cbeb297c023501a3eb785a27a5bdd4933) feat: update pkgs for 1.9

### Changes from siderolabs/gen
3 commits

* [`e847d2a`](https://github.com/siderolabs/gen/commit/e847d2ace9ede4a17283426dfbc8229121f2909b) chore: add more utilities to xiter * [`f3c5a2b`](https://github.com/siderolabs/gen/commit/f3c5a2b5aba74e4935d073a0135c4904ef3bbfef) chore: add `Empty` and `Empty2` iterators * [`c53b90b`](https://github.com/siderolabs/gen/commit/c53b90b4a418b8629d938af06900249ce5acd9e6) chore: add packages xiter/xstrings/xbytes

### Changes from siderolabs/go-blockdevice
1 commit

* [`134c41b`](https://github.com/siderolabs/go-blockdevice/commit/134c41be6f4c498a149b8098fa8d862c5c47ca54) fix: fast wipe also last 1MB of the device

### Changes from siderolabs/go-circular
1 commit

* [`9a0f7b0`](https://github.com/siderolabs/go-circular/commit/9a0f7b02c80ad6c2d953b2d3dd388c56e89363ea) fix: multiple data race issues

### Changes from siderolabs/go-cmd
3 commits

* [`d735250`](https://github.com/siderolabs/go-cmd/commit/d73525092a1bb135da54d538e5d64c4dcc80259e) fix: return an error on process nonzero exit code * [`5662c7f`](https://github.com/siderolabs/go-cmd/commit/5662c7f8d5cf475c57b3a23b8d8546d960ebc60a) feat: add an equivalent of WaitWrapper for os.Process * [`71fced6`](https://github.com/siderolabs/go-cmd/commit/71fced673e013423bba83064767a90372dd6cf51) chore: rekres and move to GHA

### Changes from siderolabs/go-kubernetes
4 commits

* [`0f62a7e`](https://github.com/siderolabs/go-kubernetes/commit/0f62a7e3c006d56601764088011d5dd20f70a7a5) feat: add one more deprecation/removal for v1.32 * [`87d2e8e`](https://github.com/siderolabs/go-kubernetes/commit/87d2e8e664c3e3e64403bcfcfe2f8691f60c6481) feat: add one more deprecation for 1.32.0-beta.0 * [`e56a7f6`](https://github.com/siderolabs/go-kubernetes/commit/e56a7f65808b90058df16a4133f19484beeedc31) fix: update deprecations based on Kubernetes 1.32.0-alpha.3 * [`381f251`](https://github.com/siderolabs/go-kubernetes/commit/381f251662eaae9b48470ce00f504c2c64187612) feat: update for Kubernetes 1.32

### Changes from siderolabs/grpc-proxy
2 commits

* [`de1c628`](https://github.com/siderolabs/grpc-proxy/commit/de1c6286b7d16d8485bf8bb55c8783c8773851a0) fix: copy data from big frame msg * [`ef47ec7`](https://github.com/siderolabs/grpc-proxy/commit/ef47ec77d2a9f0f42e713d456943dfe9ee86a629) chore: upgrade Codec implementations and usages to Codec2

### Changes from siderolabs/pkgs
46 commits

* [`a463a50`](https://github.com/siderolabs/pkgs/commit/a463a50df4b56dfa7a27ca5bd60db0a2937de736) feat: add e2fsprogs * [`bfd88f5`](https://github.com/siderolabs/pkgs/commit/bfd88f519a33da38d97e9cbce7dcca3e53ad4a41) chore: fix make kernel-menuconfig completely * [`cee356e`](https://github.com/siderolabs/pkgs/commit/cee356e919e271c63786f3964f3ffc8c733df871) chore: fix menuconfig build * [`a5530cf`](https://github.com/siderolabs/pkgs/commit/a5530cf13d299e2722015a512cd4134db575bfc7) feat: update Linux to 6.6.62, runc to 1.2.2 * [`ac329c9`](https://github.com/siderolabs/pkgs/commit/ac329c9251db54f3add6f5a24eb8c24fdd041995) feat: enable CONFIG_INTEL_HFI_THERMAL + CONFIG_INTEL_TURBO_MAX_3 * [`567a14a`](https://github.com/siderolabs/pkgs/commit/567a14adad773bd1ed44a4e965683cc88c0b96e6) fix: do not build unneeded utilities and man for SELinux libraries * [`b15a3d9`](https://github.com/siderolabs/pkgs/commit/b15a3d96113adbfe93bf004a9d870de4bfd00a7b) feat: bump dependencies * [`6bdba41`](https://github.com/siderolabs/pkgs/commit/6bdba415a78d399e90b3e5bc5294049bd24f5011) feat: update Linux to 6.6.60 * [`4699763`](https://github.com/siderolabs/pkgs/commit/4699763c6d745620aecd0219fc78962e4fa0a01e) feat: update gcc to 14.2 * [`9a98f73`](https://github.com/siderolabs/pkgs/commit/9a98f73de2c0353e9f8f194bd31c50eea1fb4d5b) feat: update containerd to v2.0.0 * [`20e1e08`](https://github.com/siderolabs/pkgs/commit/20e1e0857a7d0cf05983998df3160fe0607d5075) feat: enable CONFIG_DM_CACHE * [`df45e16`](https://github.com/siderolabs/pkgs/commit/df45e1676828e49d77718e717b2e0e425122c62c) feat: update Linux to 6.6.59 * [`2e733cc`](https://github.com/siderolabs/pkgs/commit/2e733cccfd225712eb7395cf04b6d8df0bf2b8d2) feat: bump dependencies * [`c92e123`](https://github.com/siderolabs/pkgs/commit/c92e123b40457f45e9fc0fe271804fa95c8d4f09) fix: enable nvme and 2.5gbit ethernet on nanopi-r5s * [`b160184`](https://github.com/siderolabs/pkgs/commit/b160184a479c85b8b19d2a874e5d6d52db9ed096) feat: update runc to v1.2.1 * [`e9950d9`](https://github.com/siderolabs/pkgs/commit/e9950d9097fa002e79e2933344f68bb09ad6d4df) chore: drop syslinux * [`fc2e8dc`](https://github.com/siderolabs/pkgs/commit/fc2e8dc07ad096d0394f8deacb20d423ef102c2f) feat: update containerd to v2.0.0-rc.6 * [`38304a6`](https://github.com/siderolabs/pkgs/commit/38304a60e3b32f0b3216ce8128df5f98d8be6812) feat: update Linux to 6.6.58 * [`84b8df8`](https://github.com/siderolabs/pkgs/commit/84b8df8baf408ab22649b02910294154e0ad5f3b) chore: do not use /usr/etc/udev * [`c9282c8`](https://github.com/siderolabs/pkgs/commit/c9282c8dc6a535b69a953c0b4f43fd0780c5bb30) feat: update runc to 1.2.0 * [`38ad08e`](https://github.com/siderolabs/pkgs/commit/38ad08ecb57d456b76f6d53a7d8a75c3b32f7d61) fix: default IOMMU mode to 'lazy' * [`be92da0`](https://github.com/siderolabs/pkgs/commit/be92da09f3196d96b1358efd6a7c667297d3ecfb) feat: update Linux to 6.6.57, update Linux firmware * [`0b67a13`](https://github.com/siderolabs/pkgs/commit/0b67a133b12c548ba6d28f2ea0c979cb10512812) feat: bump dependencies * [`dd5f928`](https://github.com/siderolabs/pkgs/commit/dd5f928266761215fc402085594493c9f9b329b4) feat: update Linux 6.6.56 and protect /proc/mem * [`b1bf972`](https://github.com/siderolabs/pkgs/commit/b1bf9725068029f34193b3abe1586a3d1f542b17) feat: enable CONFIG_XFRM_STATISTICS * [`c63beae`](https://github.com/siderolabs/pkgs/commit/c63beae426026c8ef1b3228b8d978ca5fcc9111b) feat: update Linux to 6.6.54 * [`f474a55`](https://github.com/siderolabs/pkgs/commit/f474a55176dca7ab88b5a29f8d97ce6f31282abd) fix: libselinux: support running without /etc/selinux * [`ba0341e`](https://github.com/siderolabs/pkgs/commit/ba0341e39dafb3fe39b5efbc8a8e8d04df96a0e7) fix: systemd-udevd: search for config in /usr/etc * [`2b193f1`](https://github.com/siderolabs/pkgs/commit/2b193f14e035fa7d7785f26a591debe6ac357f00) feat: add lpfc kernel module * [`1adb946`](https://github.com/siderolabs/pkgs/commit/1adb946b1bb256b30b7bddd517a10d68ce209ada) feat: enable QEDF driver * [`dbbe3d0`](https://github.com/siderolabs/pkgs/commit/dbbe3d0116b24b9d1c2df19ae73b76714a37704e) feat: update containerd to v2.0.0-rc.5 * [`f19590e`](https://github.com/siderolabs/pkgs/commit/f19590edb42a0247d5d509066b21ce35bfc42b93) feat: update Go to 1.23.2 * [`e2a561f`](https://github.com/siderolabs/pkgs/commit/e2a561f576ea7dbc55ebb403d648daa1561c3101) fix: drop the LVM2 udev lvm rule * [`ae205aa`](https://github.com/siderolabs/pkgs/commit/ae205aac9d827783352071f9447f9f7cbf70da20) fix: force LVM to use `/run` as state directory * [`232a153`](https://github.com/siderolabs/pkgs/commit/232a15318a2d47f34b0772663fc3f417905b5406) feat: replace eudev with systemd-udevd * [`40fb82a`](https://github.com/siderolabs/pkgs/commit/40fb82a27a840f3442d6f52374007afb0a5a3770) feat: add libselinux, libsepol, pcre2 and libcap * [`6f40fbb`](https://github.com/siderolabs/pkgs/commit/6f40fbb5e00e449c954d54990085353d061a62c8) feat: update xfsprogs 6.10.1 * [`a1709c7`](https://github.com/siderolabs/pkgs/commit/a1709c76db4ba70de526d7eec18c6b0637ebf7b0) feat: enable module unloading and memory hotplug (for NVIDIA UVM) * [`2c5785b`](https://github.com/siderolabs/pkgs/commit/2c5785b1639a22317a1f7775f0d1f4bd0b0a4b88) feat: enable transparent huge pages in madvise mode * [`ca2e8c8`](https://github.com/siderolabs/pkgs/commit/ca2e8c84b0881e7d1e359ceaf3b55c3b4bb384e7) fix: lvm2 modprobe path * [`6b334a6`](https://github.com/siderolabs/pkgs/commit/6b334a68fbd988ca69d05142a639aa3bcfd16721) feat: update Linux to 6.6.52 * [`e90ae7e`](https://github.com/siderolabs/pkgs/commit/e90ae7ec316f1b9b4d15897f825d3c2c4cefde5e) feat: update Linux firmware to 20240909 * [`79a4f92`](https://github.com/siderolabs/pkgs/commit/79a4f92c5aa4b8288a927351209542c274724475) feat: enable INET_DIAG * [`c9f7eb9`](https://github.com/siderolabs/pkgs/commit/c9f7eb94de2a8df5cfc41c6ea90596832894dc89) feat: update Linux to 6.6.51 * [`126b6a4`](https://github.com/siderolabs/pkgs/commit/126b6a4f7632b2400139e306a0dbb0a545a0dda1) fix: add mpt3sas UBSAN patches * [`a09bf93`](https://github.com/siderolabs/pkgs/commit/a09bf93ce81bde59fcb06d662bc79effc9efaca6) chore: drop UBSAN patch

### Changes from siderolabs/proto-codec
3 commits

* [`0d84c65`](https://github.com/siderolabs/proto-codec/commit/0d84c652784543012f43f8c8d4358c160b27577e) chore: add support for gogo protobuf generator * [`19f8d2e`](https://github.com/siderolabs/proto-codec/commit/19f8d2e5840c19937c60cee0c681343ab658f678) chore: add kres * [`e038bb4`](https://github.com/siderolabs/proto-codec/commit/e038bb42f2be8b80ca09e46bb8704be06a413919) Initial commit

### Changes from siderolabs/siderolink
1 commit

* [`1893385`](https://github.com/siderolabs/siderolink/commit/1893385fe45bf110357a770d31b06f5d79403065) fix: initialize tls listener properly

### Changes from siderolabs/tools
10 commits

* [`e061b6f`](https://github.com/siderolabs/tools/commit/e061b6fdc7ebbc9280d06433d99b9fc3c02773ef) feat: update dependencies * [`2704b85`](https://github.com/siderolabs/tools/commit/2704b8589fde4ea63f3063fdd1fc2ce0a8aa50e7) feat: update Go to 1.23.3 * [`3750064`](https://github.com/siderolabs/tools/commit/375006431abb204c275adab2fdc9128060bb32f7) fix: update for musl with close_range * [`0a443c6`](https://github.com/siderolabs/tools/commit/0a443c6d5a1ac6764b22990be0945ef4cae8c32e) feat: update toolchain for gcc 14.2 * [`63ecd80`](https://github.com/siderolabs/tools/commit/63ecd80a4709bcde5c6cc0f112c1faf43ab024ce) feat: bump depedendencies * [`2058296`](https://github.com/siderolabs/tools/commit/2058296cc223b683685f229a9a52de4db7171595) feat: bump dependencies * [`1151610`](https://github.com/siderolabs/tools/commit/1151610f5a5e70d07b715a2bdd76acd06d418595) feat: update Go to 1.23.2 * [`9f2189b`](https://github.com/siderolabs/tools/commit/9f2189b2b032ed283f38b20c53018b921fa06895) fix: bump gettext-tiny to the latest dev version * [`95069d6`](https://github.com/siderolabs/tools/commit/95069d6fd8fccde7ab93465e4e49a5a6ac5d4ed0) feat: update Go to 1.23.1 * [`eec0656`](https://github.com/siderolabs/tools/commit/eec0656aca652d0cc2e1973d5fab56bd4b54f64b) feat: replace gettext with gettext-tiny

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.0 -> v0.5.2 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.13.0 -> v1.16.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.7.0 -> v1.8.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.1.0 -> v1.3.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.1.0 -> v1.3.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.33 -> v1.28.3 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.13 -> v1.16.19 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.35.7 -> v1.37.5 * **github.com/aws/smithy-go** v1.20.4 -> v1.22.0 * **github.com/containerd/containerd/api** v1.8.0-rc.3 -> v1.8.0 * **github.com/containerd/containerd/v2** v2.0.0-rc.4 -> v2.0.0 * **github.com/containerd/errdefs** v0.1.0 -> v1.0.0 * **github.com/containerd/platforms** v0.2.1 -> v1.0.0-rc.0 * **github.com/containerd/typeurl/v2** v2.2.0 -> v2.2.3 * **github.com/containernetworking/plugins** v1.5.1 -> v1.6.0 * **github.com/cosi-project/runtime** v0.5.5 -> v0.7.1 * **github.com/docker/cli** v27.3.1 **_new_** * **github.com/docker/docker** v27.2.0 -> v27.3.1 * **github.com/elastic/go-libaudit/v2** v2.6.0 **_new_** * **github.com/fatih/color** v1.17.0 -> v1.18.0 * **github.com/florianl/go-tc** v0.4.4 **_new_** * **github.com/foxboron/go-uefi** e2076f0e58ca -> fab4fdf2f2f3 * **github.com/fsnotify/fsnotify** v1.7.0 -> v1.8.0 * **github.com/google/cadvisor** v0.50.0 -> v0.51.0 * **github.com/google/cel-go** v0.22.0 **_new_** * **github.com/gopacket/gopacket** v1.2.0 -> v1.3.1 * **github.com/hetznercloud/hcloud-go/v2** v2.13.1 -> v2.16.0 * **github.com/klauspost/compress** v1.17.9 -> v1.17.11 * **github.com/klauspost/cpuid/v2** v2.2.8 -> v2.2.9 * **github.com/linode/go-metadata** v0.2.0 -> v0.2.1 * **github.com/mdlayher/ethtool** v0.1.0 -> v0.2.0 * **github.com/opencontainers/runc** v1.2.0-rc.3 -> v1.2.1 * **github.com/rivo/tview** fd649dbf1223 -> c76f7879f592 * **github.com/safchain/ethtool** v0.4.1 -> 4e3aff457298 * **github.com/siderolabs/crypto** v0.4.4 -> v0.5.0 * **github.com/siderolabs/discovery-api** v0.1.4 -> v0.1.5 * **github.com/siderolabs/discovery-client** v0.1.9 -> v0.1.10 * **github.com/siderolabs/extras** v1.8.0 -> v1.9.0-alpha.0-2-g78ba66b * **github.com/siderolabs/gen** v0.5.0 -> v0.7.0 * **github.com/siderolabs/go-blockdevice** v0.4.7 -> v0.4.8 * **github.com/siderolabs/go-blockdevice/v2** v2.0.2 -> v2.0.6 * **github.com/siderolabs/go-circular** v0.2.0 -> v0.2.1 * **github.com/siderolabs/go-cmd** v0.1.1 -> v0.1.3 * **github.com/siderolabs/go-kubernetes** v0.2.12 -> v0.2.16 * **github.com/siderolabs/grpc-proxy** v0.4.1 -> v0.5.1 * **github.com/siderolabs/pkgs** v1.8.0-8-gdf1a1a5 -> v1.9.0-alpha.0-45-ga463a50 * **github.com/siderolabs/proto-codec** v0.1.1 **_new_** * **github.com/siderolabs/siderolink** v0.3.10 -> v0.3.11 * **github.com/siderolabs/talos/pkg/machinery** v1.8.0 -> v1.9.0-alpha.2 * **github.com/siderolabs/tools** v1.8.0-1-ga0c06c6 -> v1.9.0-alpha.0-9-ge061b6f * **github.com/thejerf/suture/v4** v4.0.5 **_new_** * **go.etcd.io/etcd/api/v3** v3.5.16 -> v3.5.17 * **go.etcd.io/etcd/client/pkg/v3** v3.5.16 -> v3.5.17 * **go.etcd.io/etcd/client/v3** v3.5.16 -> v3.5.17 * **go.etcd.io/etcd/etcdutl/v3** v3.5.16 -> v3.5.17 * **golang.org/x/net** v0.29.0 -> v0.31.0 * **golang.org/x/oauth2** v0.23.0 -> v0.24.0 * **golang.org/x/sync** v0.8.0 -> v0.9.0 * **golang.org/x/sys** v0.25.0 -> v0.27.0 * **golang.org/x/term** v0.24.0 -> v0.26.0 * **golang.org/x/text** v0.18.0 -> v0.20.0 * **golang.org/x/time** v0.6.0 -> v0.8.0 * **google.golang.org/grpc** v1.66.0 -> v1.68.0 * **google.golang.org/protobuf** v1.34.2 -> v1.35.1 * **k8s.io/api** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apimachinery** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apiserver** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/client-go** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/component-base** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/cri-api** v0.32.0-alpha.0 -> v0.32.0-beta.0 * **k8s.io/kube-scheduler** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubectl** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubelet** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/pod-security-admission** v0.31.1 -> v0.32.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.70 -> v1.2.72 Previous release can be found at [v1.8.0](https://github.com/siderolabs/talos/releases/tag/v1.8.0) ## [Talos 1.9.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.9.0-alpha.2) (2024-11-08) Welcome to the v1.9.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### AppArmor Talos Linux starting with v1.9 will ship with SELinux LSM enabled by default. If you need to use AppArmor LSM add the following to the machine configuration: ```yaml machine: install: extraKernelArgs: - -selinux - lsm=lockdown,capability,yama,apparmor,bpf - apparmor=1 ``` ### Auditd Talos Linux now starts a auditd service by default. Logs can be read with `talosctl logs auditd`. ### `talosctl cgroups` The `talosctl cgroups` command has been added to the `talosctl` tool. This command allows you to view the cgroup resource consumption and limits for a machine, e.g. `talosctl cgroups --preset memory`. ### udevd Talos previously used `udevd` to provide `udevd`, now it uses `systemd-udevd` instead. ### Component Updates Linux: 6.6.59 containerd: 2.0.0 Flannel: 0.26.0 Kubernetes: 1.32.0-beta.0 runc: 1.2.1 Talos is built with Go 1.23.2. ### User Namespaces Talos Linux now supports running Kubernetes pods with user namespaces enabled. Refer to the [documentation](https://www.talos.dev/v1.9/kubernetes-guides/configuration/usernamespace/) for more information. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Dmitry Sharshakov * Joakim Nohlgård * Jean-Francois Roy * Utku Ozdemir * blablu * Adolfo Ochagavía * Dan Rue * David Backeus * Eddie Wang * Florian Ströger * Hexoplon * Jakob Maležič * KBAegis * Mike Beaumont * Nebula * Nico Berlee * Philip Schmid * Philipp Kleber * Remko Molier * Robby Ciliberto * Ryan Borstelmann * Serge Logvinov * Spencer Smith * Steven Cassamajor * Tim Jones * adilTepe * ekarlso * naed3r * nevermarine * solidDoWant ### Changes
144 commits

* [`a309f6aa57`](https://github.com/siderolabs/talos/commit/a309f6aa57f4d99bbf17d0fef2fab4602e12b067) chore: fix nil pointer dereference in AWS uploader * [`333737f176`](https://github.com/siderolabs/talos/commit/333737f176f918ca3dd4217ddfed87c4da86bb9b) test: fix unpriviliged process runner test * [`2001167058`](https://github.com/siderolabs/talos/commit/200116705885b1f9935b719de71d661c695eae99) chore(ci): save support zip always after tests * [`6a42c3b8ed`](https://github.com/siderolabs/talos/commit/6a42c3b8ed58b6363a62710709f20266ca190b36) release(v1.9.0-alpha.1): prepare release * [`fb72e4b7b7`](https://github.com/siderolabs/talos/commit/fb72e4b7b74979acf743d20c7c099bc5513836e0) fix(ci): skip test if `UserNamespacesSupport` feature gate is not set * [`11380f933d`](https://github.com/siderolabs/talos/commit/11380f933ddd3fe42dc01d5ed09ceff0d62b417d) feat: display current CPU frequency on dashboard * [`fbce267aee`](https://github.com/siderolabs/talos/commit/fbce267aee98e3b4b6acace156aa22d75ad01d3d) feat: check bridged interfaces should not have addresses * [`942962bf00`](https://github.com/siderolabs/talos/commit/942962bf005a7036e04f4e572f3434f476cb567c) docs: add docs on usernamespace support in k8s * [`0406a05a98`](https://github.com/siderolabs/talos/commit/0406a05a986fabc3834c5a0de48362826268edbe) chore: update pkgs to ones built with gcc 14.2 * [`2e127627dc`](https://github.com/siderolabs/talos/commit/2e127627dce7251d5848718036780c91384c4396) docs: add apparmor enablement release notes * [`aa9311f3d8`](https://github.com/siderolabs/talos/commit/aa9311f3d840c7b5a69a1eb6ab4cb3b1a7bff135) fix: install disk matcher error * [`1800f81044`](https://github.com/siderolabs/talos/commit/1800f8104486f01e8a3437432e508893f02f809c) fix: selinux handling and apparmor tests * [`313bffadfb`](https://github.com/siderolabs/talos/commit/313bffadfb66b053f51046300764e94db088b18a) feat: update Kubernetes to v1.32.0-beta.0 * [`bbfa144510`](https://github.com/siderolabs/talos/commit/bbfa144510063fdcdebbc017b4fb382ac839370c) feat: update containerd to v2.0.0 * [`8e02b9fcbf`](https://github.com/siderolabs/talos/commit/8e02b9fcbfba421abd13ffe4fc8ea3892d4673eb) docs: update manual k8s upgrade docs * [`474949dc77`](https://github.com/siderolabs/talos/commit/474949dc77363123f0e8cf2c918ecacb82b4dbdd) feat: add dm-cache dm-cache-smq kernel modules * [`5112547d6b`](https://github.com/siderolabs/talos/commit/5112547d6b12b4ff40e7863f363cf519efb8c76c) chore: generate support zip for crashdump * [`a867f85e4c`](https://github.com/siderolabs/talos/commit/a867f85e4cb662a17b0738f1f0de4f1485ad925a) feat: label system socket and runtime files * [`398f714cff`](https://github.com/siderolabs/talos/commit/398f714cff04c524394933da17cbc21ad239cd42) feat: update Linux 6.6.59, runc 1.2.1 * [`05c620957c`](https://github.com/siderolabs/talos/commit/05c620957ca741451da395036e8eca59e631fe8d) feat: allow extra mounts for docker-based `talosctl cluster create` * [`cedabeddf7`](https://github.com/siderolabs/talos/commit/cedabeddf7d191f39525a61e65164f280b6807f8) chore: cleanup code * [`61d363e1d0`](https://github.com/siderolabs/talos/commit/61d363e1d093047886638d5bc5b9f2181c8bd894) chore: update go-auditlib * [`960a040491`](https://github.com/siderolabs/talos/commit/960a040491de5c95b104b4a39ea519095eb47931) feat: start enabling SELinux * [`7f3aaa21cd`](https://github.com/siderolabs/talos/commit/7f3aaa21cd8d969e26721235a4191ba3bdbc1f8f) fix: update permissions for logging directories in /var * [`0e6c983b84`](https://github.com/siderolabs/talos/commit/0e6c983b847f679a074c1794fbe77d21a5994233) fix: mount /sys/kernel/security conditionally * [`74b0e8c371`](https://github.com/siderolabs/talos/commit/74b0e8c3713a01f83758556672583880ce5c684a) fix: make route normalization keep family * [`0a3761c22f`](https://github.com/siderolabs/talos/commit/0a3761c22f98783c6696f143611d600287a471a3) fix: talosctl windows arm64 * [`4b10c5328b`](https://github.com/siderolabs/talos/commit/4b10c5328b861b4bcdcec3ca21bd55b91e969b44) chore: add Windows ARM64 build for talosctl * [`9abf16108e`](https://github.com/siderolabs/talos/commit/9abf16108ede75984845297d03673d56cb561c2f) feat: add auditd service * [`d464ca869f`](https://github.com/siderolabs/talos/commit/d464ca869f8949ffbb990c6fb02fbbcbe0abcbe1) chore: drop runc memfd bind added in #9069 * [`b54d26c2c3`](https://github.com/siderolabs/talos/commit/b54d26c2c3f3a52c6d1ec3fddb7a373175815de3) fix: mount pseudo sub-mountpoints in init * [`7aeb15f730`](https://github.com/siderolabs/talos/commit/7aeb15f73094a23aea1d6b263ca2eca061c8a257) chore: disable coredns cache for cluster domain * [`d8b652150c`](https://github.com/siderolabs/talos/commit/d8b652150cec408f2bf3307565b9db691b21bfe9) docs: add warning about NVMe bus path bug * [`3e16ab135e`](https://github.com/siderolabs/talos/commit/3e16ab135e2be8c9b652d67f9e7eadbc3691c5ca) feat: update Kubernetes to v1.32.0-alpha.3 * [`0b8b356777`](https://github.com/siderolabs/talos/commit/0b8b3567771fbe796926dc9a6e904e7102535170) feat: add BridgePort property to network machine configuration * [`b379506259`](https://github.com/siderolabs/talos/commit/b3795062596ef45dd309f1ca56aab31d2a1a0efc) fix: use more correct condition to skip generating hosts files * [`62ec7ec336`](https://github.com/siderolabs/talos/commit/62ec7ec3367233823c09befddc5ad312aa607822) refactor: replace the old v1 mount package with new one * [`0ece13c623`](https://github.com/siderolabs/talos/commit/0ece13c6236c7eda474d3734fcc4c4060299ac43) docs: update network-config.md (cont) * [`93827f0485`](https://github.com/siderolabs/talos/commit/93827f0485a92b46da83b80a2a55f2569f70fe57) docs: update network-config.md * [`423b1e5fb2`](https://github.com/siderolabs/talos/commit/423b1e5fb22d9e785a3832741d796120b84a5e38) fix: do not trim 0 from process SELinux label * [`2136358d65`](https://github.com/siderolabs/talos/commit/2136358d65ddf6ad040ed62c835b335f99a59399) feat: introduce metal agent mode * [`0e15955fcc`](https://github.com/siderolabs/talos/commit/0e15955fcc5d464c5f0ffd1a44eebf4bf32f4844) chore: small refactoring * [`66012a7f26`](https://github.com/siderolabs/talos/commit/66012a7f269010c5ed412d139b14c470063f2429) feat: remove wrapperd and launch processes directly * [`3a0a17ae66`](https://github.com/siderolabs/talos/commit/3a0a17ae66dab5c983571fab0f3eac3f87fbc17c) fix: prevent panic in nocloud platform code * [`dc0c6acbd7`](https://github.com/siderolabs/talos/commit/dc0c6acbd765b6e7838d6af4f1903242d5073782) refactor: remove unmaintained github.com/vishvananda/netlink * [`78353f7918`](https://github.com/siderolabs/talos/commit/78353f79188e81d064c354f6ef3fe3b2e023c644) feat: add parsing of vlanNNNN:ethX style VLAN cmdline args * [`9db7a36bfc`](https://github.com/siderolabs/talos/commit/9db7a36bfc45c9c15fd661fb2a6319dcf4fef210) fix: generation of SecureBoot iso * [`c755b6d7e4`](https://github.com/siderolabs/talos/commit/c755b6d7e4600fdfb32be50422b7efb0fdabef63) fix: update the CRI sandbox image reference * [`cec290b354`](https://github.com/siderolabs/talos/commit/cec290b354773b2b0f2c2ae9d57f36e06fe2654d) feat: allow extensions to log to console * [`b7801df827`](https://github.com/siderolabs/talos/commit/b7801df827d8e1e9a2db7dac0a62c3802de4d73c) fix: wait for udevd to be running before activating LVM * [`d4cb478a50`](https://github.com/siderolabs/talos/commit/d4cb478a50ce41c3699b7846388e537ddf18a703) docs: improve field description for BridgeSTP, BridgeVLAN * [`7329824b24`](https://github.com/siderolabs/talos/commit/7329824b2411fef3b23fd90380033441048f6512) docs: add Mynewsdesk to ADOPTERS.md * [`a13cf76a34`](https://github.com/siderolabs/talos/commit/a13cf76a3415f458ff3235981c1be8202e1800bb) chore: simplify `DNSUpstreamController` and `DNSUpstream` resource * [`62d185473e`](https://github.com/siderolabs/talos/commit/62d185473e258c0c34eff5aed4c18d81d4b92a89) fix: talosctl process null character * [`77d7368eae`](https://github.com/siderolabs/talos/commit/77d7368eae2da6d2c9aa896afc8013007909a958) feat: update containerd to v2.0.0-rc.6 * [`d39393879a`](https://github.com/siderolabs/talos/commit/d39393879a1f98ac3de7a96808301d1e07fd95f3) fix: rework the 'metal-iso' config acquisition * [`1993afca9f`](https://github.com/siderolabs/talos/commit/1993afca9fff7e889b497ec3241cfdca42294f18) chore: create /usr/etc in a different step * [`8680351c13`](https://github.com/siderolabs/talos/commit/8680351c131d29a76682569742dbd44c8ffe47d3) chore: move system extensions' udev rules * [`3067f64c84`](https://github.com/siderolabs/talos/commit/3067f64c8435ef2d5453100a1584dc3c6915ba0b) feat: update Flannel to v0.26.0 * [`8658d6865f`](https://github.com/siderolabs/talos/commit/8658d6865fa0bcbfcebe483b7332d3b56e239979) docs: typo in deploying cilium * [`49bbadc4bf`](https://github.com/siderolabs/talos/commit/49bbadc4bf1e79e48c057d473ae21426b273c588) docs: add documentation on performance tuning * [`534b0ce183`](https://github.com/siderolabs/talos/commit/534b0ce1833462b22f3761258e0e95813a355fb2) feat: update runc to 1.2.0 final * [`2172535237`](https://github.com/siderolabs/talos/commit/21725352373da7835d95f8f934847dab404782f8) docs: fix image factory links * [`375e3da73f`](https://github.com/siderolabs/talos/commit/375e3da73fcb02c7caea2576289fefdc395a1ed2) feat: update Kubernetes to 1.32.0-alpha.2 * [`9e6f64df04`](https://github.com/siderolabs/talos/commit/9e6f64df047527ecb42df5fdf5fd2f9767d21437) fix: improve error messages for invalid bridge/bond configuration * [`7c8c72c2b2`](https://github.com/siderolabs/talos/commit/7c8c72c2b2a4edb412e097a9e013ab21727339cf) fix: correct error message for invalid ip= * [`ead46997c9`](https://github.com/siderolabs/talos/commit/ead46997c918ab1139ca12e87beefbbda29614e1) chore: rename tpm2.PCRExtent -> tpm2.PCRExtend * [`867c4b8125`](https://github.com/siderolabs/talos/commit/867c4b8125ee738f9a82e5e87809eb95bdd2f778) docs: fix typo in prodnotes.md * [`1b22df48a4`](https://github.com/siderolabs/talos/commit/1b22df48a41578d19fb512bd8111a481b64011e2) chore: support debug shell for advanced development * [`c14b446229`](https://github.com/siderolabs/talos/commit/c14b4462292bd7e6088fce35d6880a9b2b56335c) feat: update Kubernetes to v1.32.0-alpha.1 * [`29780d35a0`](https://github.com/siderolabs/talos/commit/29780d35a052134d50576f6506c2728489a30506) test: add an integration test for verifying process parameters * [`3d342af447`](https://github.com/siderolabs/talos/commit/3d342af4479ed12e2af10021ec4e7ab9c2af6d75) fix: update incorrect alias for PCIDevice resource * [`f7d35a5e0b`](https://github.com/siderolabs/talos/commit/f7d35a5e0b4e3a04a639d663e5a580e22fea76db) release(v1.9.0-alpha.0): prepare release * [`e0434d77d7`](https://github.com/siderolabs/talos/commit/e0434d77d754f8834ba903f4c09b08634cfd3934) feat: update dependencies * [`5c5a248861`](https://github.com/siderolabs/talos/commit/5c5a248861c8e5848f9a23cd0cd7b3b749f21e4b) feat: add Talos 1.9 compatibility guarantees * [`bc4c21f41a`](https://github.com/siderolabs/talos/commit/bc4c21f41a0066ba6cefb5b753c52d76a6b0f629) test: add json logs test environment * [`71faa32942`](https://github.com/siderolabs/talos/commit/71faa3294246947f6bd212979ceb31e793ae0604) docs: nvidia proprietary/oss hardware requirement * [`59a78da42c`](https://github.com/siderolabs/talos/commit/59a78da42cdea8fbccc35d0851f9b0eef928261b) chore: add proto-codec/codec * [`7ff1cedfe3`](https://github.com/siderolabs/talos/commit/7ff1cedfe3eee51505c30439eec4a2df9b452b2e) chore: update siderolabs/crypto module and return proper ALPN * [`ccbd5aed39`](https://github.com/siderolabs/talos/commit/ccbd5aed39b360664d1f80c8b146050e9df9ff7b) feat: optionally decode hcloud userdata as base64 * [`34f652ce82`](https://github.com/siderolabs/talos/commit/34f652ce822fcb70a292289fe6ba5d1bd7a34f97) feat: add well-known app.kubernetes.io labels to control-plane pods * [`fc89dc2164`](https://github.com/siderolabs/talos/commit/fc89dc21643a923cb7d0d3944405521bf849631b) fix: support `extra-disks` when using iso * [`f2bff814de`](https://github.com/siderolabs/talos/commit/f2bff814de0b237fbed419234b935dc9f9637554) chore: add arm64 target for integration-test * [`5853bb0ea4`](https://github.com/siderolabs/talos/commit/5853bb0ea4d6a65635086bdef617d6d0800cabd0) fix: json logging panic * [`a859cff364`](https://github.com/siderolabs/talos/commit/a859cff364aa4dc9b4b880417b821f7ecf5602ac) chore: use virtio driver for disks in arm64 * [`db248de88d`](https://github.com/siderolabs/talos/commit/db248de88dec2467e4340f699cde98217979ba4b) chore(ci): add config for lldpd extension * [`9f0de9f43d`](https://github.com/siderolabs/talos/commit/9f0de9f43dc4467f0bdeda117b4946ae12db50ab) test: update provision upgrade tests for Talos 1.9 * [`39fe285e69`](https://github.com/siderolabs/talos/commit/39fe285e69691059f91d8c7c5506e156356263d9) fix: skip ram disks * [`a9bff3a1d0`](https://github.com/siderolabs/talos/commit/a9bff3a1d084c32a654555e71e2592e60edbdcb6) test: skip no error test in Cilium * [`4d902021bb`](https://github.com/siderolabs/talos/commit/4d902021bb3c55bc212cbb3e2443b6552400622f) fix: do not use pflag csv comma reader for config-patch * [`5371788ce1`](https://github.com/siderolabs/talos/commit/5371788ce169a0381e08f0d902ac81f3f89ba5bd) fix: typo in documentation * [`8a228ba6bc`](https://github.com/siderolabs/talos/commit/8a228ba6bc702f21fca06dc2ecb3e8e846839cd3) docs: add egress documentation * [`182325cb07`](https://github.com/siderolabs/talos/commit/182325cb0791da1d4dcd3914a643c44232502524) test: skip lvm test if not enough user disks available * [`519a48302e`](https://github.com/siderolabs/talos/commit/519a48302e771fd9b331913166d55c50fff4961a) fix: wipe system partitions correctly via kernel args * [`0a2b4556c5`](https://github.com/siderolabs/talos/commit/0a2b4556c55eda27536ee563f60bcf5d69379479) fix: volume encryption with failing keyslots * [`6affbd3182`](https://github.com/siderolabs/talos/commit/6affbd3182ebe0209ed5433c534062b7ad672b6a) fix: update grpc-go the latest patch release * [`77a4a4adc7`](https://github.com/siderolabs/talos/commit/77a4a4adc7232b4382f2a530f4056a1fff6c50b4) fix: scaleway metadata * [`7acadc0c8f`](https://github.com/siderolabs/talos/commit/7acadc0c8fa969e4de7f0d4f68b0fd0cd833b489) fix: do not stop udevd before unmounting volumes * [`6a081055b0`](https://github.com/siderolabs/talos/commit/6a081055b0dd4e3ce5c40392c8415a0a55b2591c) feat: update Flannel to v0.25.7 * [`2362f6d3ee`](https://github.com/siderolabs/talos/commit/2362f6d3ee51a0a8b541a872d39ac82892502e17) fix: improve container detection * [`b67bc73fd3`](https://github.com/siderolabs/talos/commit/b67bc73fd30a8e07f26c47a746ca53f2af41d366) fix: fix mdadm system extension * [`f08669c7a9`](https://github.com/siderolabs/talos/commit/f08669c7a9583a559dc53f233798305bbab07b8a) feat: bring in lpfc kernel module driver * [`6a014374be`](https://github.com/siderolabs/talos/commit/6a014374be26f0caf8faa90a34f2476e0e77a46a) feat: enable QEDF driver * [`f711907e03`](https://github.com/siderolabs/talos/commit/f711907e038cea20f6b831ea5ad8c3b18638c1b4) fix: make /var/run empty on reboots * [`7d02eb60f4`](https://github.com/siderolabs/talos/commit/7d02eb60f47652f4b72f170b28a8b964729af013) docs: fix typo in CloudStack docs * [`74861573a7`](https://github.com/siderolabs/talos/commit/74861573a793f9e143d7d2638990f37ec639aa88) fix: multiple fixes for LVM activation * [`74c12c20e0`](https://github.com/siderolabs/talos/commit/74c12c20e02e4ec29b2b374cebc996ddf8fa90c7) feat: replace eudev with systemd-udevd * [`0a4df4ef84`](https://github.com/siderolabs/talos/commit/0a4df4ef84467014d5be4b4ec57de0e778cfb21e) docs: fix nvidia CRI config example * [`afc1e1a46a`](https://github.com/siderolabs/talos/commit/afc1e1a46a559aac3aa5f4a2708ba8d2c9228929) docs: fix typo in extraMounts directory * [`a341bdb064`](https://github.com/siderolabs/talos/commit/a341bdb0640294a07939670919c56cbfa7a861c4) fix: prevent file descriptors leaks to child processes * [`dec653bfe1`](https://github.com/siderolabs/talos/commit/dec653bfe1feb84ea2ed1a779b1bfc783dc61160) chore: better lvm2 tests * [`908fd8789c`](https://github.com/siderolabs/talos/commit/908fd8789cc1b22e556a7ffe307409931976ba08) feat: support cgroup deep analysis in `talosctl` * [`aa846cc186`](https://github.com/siderolabs/talos/commit/aa846cc186c1c6125f8f39ea084fa2023512656f) feat: add support for CI Network config in nocloud * [`10f2539f23`](https://github.com/siderolabs/talos/commit/10f2539f237aeb3af2caeb3c349c062f203219b6) chore: disable cloud-images cron workflow * [`b07a8b36b2`](https://github.com/siderolabs/talos/commit/b07a8b36b24d57337323e72d6032304c4cade927) chore: ignore more plugins for system containerd * [`392c4798f0`](https://github.com/siderolabs/talos/commit/392c4798f0bff7cb4518609deae7c90581f013f5) feat: prepare for Talos 1.9 * [`ea7bf9fb43`](https://github.com/siderolabs/talos/commit/ea7bf9fb43dff8cf8ec4dfd4f629e8f826bc2ded) docs: update storage.md * [`4ab8dee69a`](https://github.com/siderolabs/talos/commit/4ab8dee69ac07c811cbe121ca9e2d9bd01148863) fix: build talosctl without `tcell_minimal` * [`2fa019bd97`](https://github.com/siderolabs/talos/commit/2fa019bd9751ad96085ade52628023adf17658d3) docs: enable 'edit on GitHub' link * [`d2ccbc2b15`](https://github.com/siderolabs/talos/commit/d2ccbc2b1512b6323d48a764c4af534d49b4bd27) docs: update hetzner documentation for CCM * [`d498f647cd`](https://github.com/siderolabs/talos/commit/d498f647cd9dfcd575f51005c9b78c2c1c7b51ca) docs: fix Kernel Self Protection Project (KSPP) references * [`0ec75463ee`](https://github.com/siderolabs/talos/commit/0ec75463eecebfb543a64b0c859ba0b2477e406f) docs: make Talos 1.8 current release * [`9b77698cf2`](https://github.com/siderolabs/talos/commit/9b77698cf2ff64c6f6d198d05c2012ab7fa858be) fix: update blockdevice library to v2.0.2 * [`e46227ab95`](https://github.com/siderolabs/talos/commit/e46227ab95a6d06132e82315f55b5ced533ddabb) docs: fix kubespan name inconsistency * [`6b15ca19cd`](https://github.com/siderolabs/talos/commit/6b15ca19cd1291b8a245d72d5153827945cad037) fix: audit and fix cgroup reservations * [`32b5d01ed3`](https://github.com/siderolabs/talos/commit/32b5d01ed3396e8f54a245cc6d9818119aec8291) chore: bump lvm2 * [`6484581eb8`](https://github.com/siderolabs/talos/commit/6484581eb888996a8dc829915439fb63606dd794) feat: allow /sbin/ldconfig in extensions * [`9fa08e8437`](https://github.com/siderolabs/talos/commit/9fa08e843728dbd85ed7e0035f59cdd6232de9a9) chore: refactor tests * [`d8ab4981b6`](https://github.com/siderolabs/talos/commit/d8ab4981b626ff41fbcdb526a032a5584519e3df) feat: support lvm auto activation * [`8166a58b36`](https://github.com/siderolabs/talos/commit/8166a58b364f760212b2a610ce0d764b8b4c5c46) fix: filter out non-printable characters in process line * [`806b6aaf52`](https://github.com/siderolabs/talos/commit/806b6aaf52f20ed0f32107b3d0372d6e3ff974be) docs: add SECURITY.md * [`7bd26df308`](https://github.com/siderolabs/talos/commit/7bd26df30803307e4eece3e382aafebc55e7b260) docs: document `/dev/net/tun` compatibility * [`18daedb511`](https://github.com/siderolabs/talos/commit/18daedb511e769717ba56eb05cccab72118a4813) fix: strategic merge patch delete for map keys * [`f3370529ac`](https://github.com/siderolabs/talos/commit/f3370529ac042865a4b2d793465916fcae2d4b33) docs: correct typo * [`8d6884a8e2`](https://github.com/siderolabs/talos/commit/8d6884a8e28e1bfa29f9a479e0f7179819cf70cd) test: add a test for inline machine config trusted roots * [`d4a6d017db`](https://github.com/siderolabs/talos/commit/d4a6d017dbb91e22c60787cdf64b242057b1ebef) fix: ignore invalid NTP responses * [`869f8379f2`](https://github.com/siderolabs/talos/commit/869f8379f2317175901e8cb3deec4b800e7ab603) feat: update default Kubernetes version to 1.31.1 * [`780a1f198a`](https://github.com/siderolabs/talos/commit/780a1f198a5eedd33a27060bdf116bd3a3b26426) fix: update CoreDNS health check * [`79cd031588`](https://github.com/siderolabs/talos/commit/79cd031588a0710b865414f919742ee3ffb998ed) chore: account for resource sorting in dns upstream resource * [`e17fafaca2`](https://github.com/siderolabs/talos/commit/e17fafaca2a16990bc424b54120c49ddbaf8cee1) chore: drop `activateLogicalVolumes` sequencer step * [`a294b366f2`](https://github.com/siderolabs/talos/commit/a294b366f24c6580d304c6c8ad34f481079dc795) fix: parse SideroLink API endpoint correctly * [`a9269ac7b1`](https://github.com/siderolabs/talos/commit/a9269ac7b1217aa2d247c0215c5f2755af468b44) fix: remove extra logging on ethtool ioctl failures * [`5c6277d171`](https://github.com/siderolabs/talos/commit/5c6277d171eea58878ce4fcb4d2fdb7154333ae7) feat: update etcd to 3.5.16 * [`c1ed2984b8`](https://github.com/siderolabs/talos/commit/c1ed2984b85dca791a5081c5da26bba75e3cd579) docs: add what's new for Talos 1.8

### Changes since v1.9.0-alpha.1
3 commits

* [`a309f6aa5`](https://github.com/siderolabs/talos/commit/a309f6aa57f4d99bbf17d0fef2fab4602e12b067) chore: fix nil pointer dereference in AWS uploader * [`333737f17`](https://github.com/siderolabs/talos/commit/333737f176f918ca3dd4217ddfed87c4da86bb9b) test: fix unpriviliged process runner test * [`200116705`](https://github.com/siderolabs/talos/commit/200116705885b1f9935b719de71d661c695eae99) chore(ci): save support zip always after tests

### Changes from siderolabs/crypto
1 commit

* [`58b2f92`](https://github.com/siderolabs/crypto/commit/58b2f9291c7e763a7210cfa681f88a7fa2230bf3) chore: use HTTP/2 ALPN by default

### Changes from siderolabs/discovery-api
1 commit

* [`005e92c`](https://github.com/siderolabs/discovery-api/commit/005e92cf4ad0059334bfd35285a97c85f12aa263) chore: rekres and regen

### Changes from siderolabs/discovery-client
1 commit

* [`b74fb90`](https://github.com/siderolabs/discovery-client/commit/b74fb9039fcfd8db9d6becf3044f9f41f387ea27) fix: allow custom TLS config for the client

### Changes from siderolabs/extras
2 commits

* [`eab6e58`](https://github.com/siderolabs/extras/commit/eab6e58aa9bdf49789cd4d64d2e27f61023421ca) feat: update dependencies * [`1459d78`](https://github.com/siderolabs/extras/commit/1459d78cbeb297c023501a3eb785a27a5bdd4933) feat: update pkgs for 1.9

### Changes from siderolabs/gen
3 commits

* [`e847d2a`](https://github.com/siderolabs/gen/commit/e847d2ace9ede4a17283426dfbc8229121f2909b) chore: add more utilities to xiter * [`f3c5a2b`](https://github.com/siderolabs/gen/commit/f3c5a2b5aba74e4935d073a0135c4904ef3bbfef) chore: add `Empty` and `Empty2` iterators * [`c53b90b`](https://github.com/siderolabs/gen/commit/c53b90b4a418b8629d938af06900249ce5acd9e6) chore: add packages xiter/xstrings/xbytes

### Changes from siderolabs/go-blockdevice
1 commit

* [`134c41b`](https://github.com/siderolabs/go-blockdevice/commit/134c41be6f4c498a149b8098fa8d862c5c47ca54) fix: fast wipe also last 1MB of the device

### Changes from siderolabs/go-circular
1 commit

* [`9a0f7b0`](https://github.com/siderolabs/go-circular/commit/9a0f7b02c80ad6c2d953b2d3dd388c56e89363ea) fix: multiple data race issues

### Changes from siderolabs/go-cmd
3 commits

* [`d735250`](https://github.com/siderolabs/go-cmd/commit/d73525092a1bb135da54d538e5d64c4dcc80259e) fix: return an error on process nonzero exit code * [`5662c7f`](https://github.com/siderolabs/go-cmd/commit/5662c7f8d5cf475c57b3a23b8d8546d960ebc60a) feat: add an equivalent of WaitWrapper for os.Process * [`71fced6`](https://github.com/siderolabs/go-cmd/commit/71fced673e013423bba83064767a90372dd6cf51) chore: rekres and move to GHA

### Changes from siderolabs/go-kubernetes
3 commits

* [`87d2e8e`](https://github.com/siderolabs/go-kubernetes/commit/87d2e8e664c3e3e64403bcfcfe2f8691f60c6481) feat: add one more deprecation for 1.32.0-beta.0 * [`e56a7f6`](https://github.com/siderolabs/go-kubernetes/commit/e56a7f65808b90058df16a4133f19484beeedc31) fix: update deprecations based on Kubernetes 1.32.0-alpha.3 * [`381f251`](https://github.com/siderolabs/go-kubernetes/commit/381f251662eaae9b48470ce00f504c2c64187612) feat: update for Kubernetes 1.32

### Changes from siderolabs/grpc-proxy
2 commits

* [`de1c628`](https://github.com/siderolabs/grpc-proxy/commit/de1c6286b7d16d8485bf8bb55c8783c8773851a0) fix: copy data from big frame msg * [`ef47ec7`](https://github.com/siderolabs/grpc-proxy/commit/ef47ec77d2a9f0f42e713d456943dfe9ee86a629) chore: upgrade Codec implementations and usages to Codec2

### Changes from siderolabs/pkgs
38 commits

* [`4699763`](https://github.com/siderolabs/pkgs/commit/4699763c6d745620aecd0219fc78962e4fa0a01e) feat: update gcc to 14.2 * [`9a98f73`](https://github.com/siderolabs/pkgs/commit/9a98f73de2c0353e9f8f194bd31c50eea1fb4d5b) feat: update containerd to v2.0.0 * [`20e1e08`](https://github.com/siderolabs/pkgs/commit/20e1e0857a7d0cf05983998df3160fe0607d5075) feat: enable CONFIG_DM_CACHE * [`df45e16`](https://github.com/siderolabs/pkgs/commit/df45e1676828e49d77718e717b2e0e425122c62c) feat: update Linux to 6.6.59 * [`2e733cc`](https://github.com/siderolabs/pkgs/commit/2e733cccfd225712eb7395cf04b6d8df0bf2b8d2) feat: bump dependencies * [`c92e123`](https://github.com/siderolabs/pkgs/commit/c92e123b40457f45e9fc0fe271804fa95c8d4f09) fix: enable nvme and 2.5gbit ethernet on nanopi-r5s * [`b160184`](https://github.com/siderolabs/pkgs/commit/b160184a479c85b8b19d2a874e5d6d52db9ed096) feat: update runc to v1.2.1 * [`e9950d9`](https://github.com/siderolabs/pkgs/commit/e9950d9097fa002e79e2933344f68bb09ad6d4df) chore: drop syslinux * [`fc2e8dc`](https://github.com/siderolabs/pkgs/commit/fc2e8dc07ad096d0394f8deacb20d423ef102c2f) feat: update containerd to v2.0.0-rc.6 * [`38304a6`](https://github.com/siderolabs/pkgs/commit/38304a60e3b32f0b3216ce8128df5f98d8be6812) feat: update Linux to 6.6.58 * [`84b8df8`](https://github.com/siderolabs/pkgs/commit/84b8df8baf408ab22649b02910294154e0ad5f3b) chore: do not use /usr/etc/udev * [`c9282c8`](https://github.com/siderolabs/pkgs/commit/c9282c8dc6a535b69a953c0b4f43fd0780c5bb30) feat: update runc to 1.2.0 * [`38ad08e`](https://github.com/siderolabs/pkgs/commit/38ad08ecb57d456b76f6d53a7d8a75c3b32f7d61) fix: default IOMMU mode to 'lazy' * [`be92da0`](https://github.com/siderolabs/pkgs/commit/be92da09f3196d96b1358efd6a7c667297d3ecfb) feat: update Linux to 6.6.57, update Linux firmware * [`0b67a13`](https://github.com/siderolabs/pkgs/commit/0b67a133b12c548ba6d28f2ea0c979cb10512812) feat: bump dependencies * [`dd5f928`](https://github.com/siderolabs/pkgs/commit/dd5f928266761215fc402085594493c9f9b329b4) feat: update Linux 6.6.56 and protect /proc/mem * [`b1bf972`](https://github.com/siderolabs/pkgs/commit/b1bf9725068029f34193b3abe1586a3d1f542b17) feat: enable CONFIG_XFRM_STATISTICS * [`c63beae`](https://github.com/siderolabs/pkgs/commit/c63beae426026c8ef1b3228b8d978ca5fcc9111b) feat: update Linux to 6.6.54 * [`f474a55`](https://github.com/siderolabs/pkgs/commit/f474a55176dca7ab88b5a29f8d97ce6f31282abd) fix: libselinux: support running without /etc/selinux * [`ba0341e`](https://github.com/siderolabs/pkgs/commit/ba0341e39dafb3fe39b5efbc8a8e8d04df96a0e7) fix: systemd-udevd: search for config in /usr/etc * [`2b193f1`](https://github.com/siderolabs/pkgs/commit/2b193f14e035fa7d7785f26a591debe6ac357f00) feat: add lpfc kernel module * [`1adb946`](https://github.com/siderolabs/pkgs/commit/1adb946b1bb256b30b7bddd517a10d68ce209ada) feat: enable QEDF driver * [`dbbe3d0`](https://github.com/siderolabs/pkgs/commit/dbbe3d0116b24b9d1c2df19ae73b76714a37704e) feat: update containerd to v2.0.0-rc.5 * [`f19590e`](https://github.com/siderolabs/pkgs/commit/f19590edb42a0247d5d509066b21ce35bfc42b93) feat: update Go to 1.23.2 * [`e2a561f`](https://github.com/siderolabs/pkgs/commit/e2a561f576ea7dbc55ebb403d648daa1561c3101) fix: drop the LVM2 udev lvm rule * [`ae205aa`](https://github.com/siderolabs/pkgs/commit/ae205aac9d827783352071f9447f9f7cbf70da20) fix: force LVM to use `/run` as state directory * [`232a153`](https://github.com/siderolabs/pkgs/commit/232a15318a2d47f34b0772663fc3f417905b5406) feat: replace eudev with systemd-udevd * [`40fb82a`](https://github.com/siderolabs/pkgs/commit/40fb82a27a840f3442d6f52374007afb0a5a3770) feat: add libselinux, libsepol, pcre2 and libcap * [`6f40fbb`](https://github.com/siderolabs/pkgs/commit/6f40fbb5e00e449c954d54990085353d061a62c8) feat: update xfsprogs 6.10.1 * [`a1709c7`](https://github.com/siderolabs/pkgs/commit/a1709c76db4ba70de526d7eec18c6b0637ebf7b0) feat: enable module unloading and memory hotplug (for NVIDIA UVM) * [`2c5785b`](https://github.com/siderolabs/pkgs/commit/2c5785b1639a22317a1f7775f0d1f4bd0b0a4b88) feat: enable transparent huge pages in madvise mode * [`ca2e8c8`](https://github.com/siderolabs/pkgs/commit/ca2e8c84b0881e7d1e359ceaf3b55c3b4bb384e7) fix: lvm2 modprobe path * [`6b334a6`](https://github.com/siderolabs/pkgs/commit/6b334a68fbd988ca69d05142a639aa3bcfd16721) feat: update Linux to 6.6.52 * [`e90ae7e`](https://github.com/siderolabs/pkgs/commit/e90ae7ec316f1b9b4d15897f825d3c2c4cefde5e) feat: update Linux firmware to 20240909 * [`79a4f92`](https://github.com/siderolabs/pkgs/commit/79a4f92c5aa4b8288a927351209542c274724475) feat: enable INET_DIAG * [`c9f7eb9`](https://github.com/siderolabs/pkgs/commit/c9f7eb94de2a8df5cfc41c6ea90596832894dc89) feat: update Linux to 6.6.51 * [`126b6a4`](https://github.com/siderolabs/pkgs/commit/126b6a4f7632b2400139e306a0dbb0a545a0dda1) fix: add mpt3sas UBSAN patches * [`a09bf93`](https://github.com/siderolabs/pkgs/commit/a09bf93ce81bde59fcb06d662bc79effc9efaca6) chore: drop UBSAN patch

### Changes from siderolabs/proto-codec
3 commits

* [`0d84c65`](https://github.com/siderolabs/proto-codec/commit/0d84c652784543012f43f8c8d4358c160b27577e) chore: add support for gogo protobuf generator * [`19f8d2e`](https://github.com/siderolabs/proto-codec/commit/19f8d2e5840c19937c60cee0c681343ab658f678) chore: add kres * [`e038bb4`](https://github.com/siderolabs/proto-codec/commit/e038bb42f2be8b80ca09e46bb8704be06a413919) Initial commit

### Changes from siderolabs/siderolink
1 commit

* [`1893385`](https://github.com/siderolabs/siderolink/commit/1893385fe45bf110357a770d31b06f5d79403065) fix: initialize tls listener properly

### Changes from siderolabs/tools
8 commits

* [`3750064`](https://github.com/siderolabs/tools/commit/375006431abb204c275adab2fdc9128060bb32f7) fix: update for musl with close_range * [`0a443c6`](https://github.com/siderolabs/tools/commit/0a443c6d5a1ac6764b22990be0945ef4cae8c32e) feat: update toolchain for gcc 14.2 * [`63ecd80`](https://github.com/siderolabs/tools/commit/63ecd80a4709bcde5c6cc0f112c1faf43ab024ce) feat: bump depedendencies * [`2058296`](https://github.com/siderolabs/tools/commit/2058296cc223b683685f229a9a52de4db7171595) feat: bump dependencies * [`1151610`](https://github.com/siderolabs/tools/commit/1151610f5a5e70d07b715a2bdd76acd06d418595) feat: update Go to 1.23.2 * [`9f2189b`](https://github.com/siderolabs/tools/commit/9f2189b2b032ed283f38b20c53018b921fa06895) fix: bump gettext-tiny to the latest dev version * [`95069d6`](https://github.com/siderolabs/tools/commit/95069d6fd8fccde7ab93465e4e49a5a6ac5d4ed0) feat: update Go to 1.23.1 * [`eec0656`](https://github.com/siderolabs/tools/commit/eec0656aca652d0cc2e1973d5fab56bd4b54f64b) feat: replace gettext with gettext-tiny

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.0 -> v0.5.2 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.13.0 -> v1.16.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.7.0 -> v1.8.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.1.0 -> v1.2.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.1.0 -> v1.2.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.33 -> v1.28.1 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.13 -> v1.16.18 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.35.7 -> v1.37.3 * **github.com/aws/smithy-go** v1.20.4 -> v1.22.0 * **github.com/containerd/containerd/api** v1.8.0-rc.3 -> v1.8.0 * **github.com/containerd/containerd/v2** v2.0.0-rc.4 -> v2.0.0 * **github.com/containerd/errdefs** v0.1.0 -> v1.0.0 * **github.com/containerd/platforms** v0.2.1 -> v1.0.0-rc.0 * **github.com/containerd/typeurl/v2** v2.2.0 -> v2.2.2 * **github.com/containernetworking/plugins** v1.5.1 -> v1.6.0 * **github.com/cosi-project/runtime** v0.5.5 -> v0.7.1 * **github.com/docker/cli** v27.3.1 **_new_** * **github.com/docker/docker** v27.2.0 -> v27.3.1 * **github.com/elastic/go-libaudit/v2** 1df86e79cca7 **_new_** * **github.com/fatih/color** v1.17.0 -> v1.18.0 * **github.com/florianl/go-tc** v0.4.4 **_new_** * **github.com/foxboron/go-uefi** e2076f0e58ca -> fab4fdf2f2f3 * **github.com/fsnotify/fsnotify** v1.7.0 -> v1.8.0 * **github.com/google/cadvisor** v0.50.0 -> v0.51.0 * **github.com/gopacket/gopacket** v1.2.0 -> v1.3.0 * **github.com/hetznercloud/hcloud-go/v2** v2.13.1 -> v2.15.0 * **github.com/klauspost/compress** v1.17.9 -> v1.17.11 * **github.com/linode/go-metadata** v0.2.0 -> v0.2.1 * **github.com/mdlayher/ethtool** v0.1.0 -> v0.2.0 * **github.com/opencontainers/runc** v1.2.0-rc.3 -> v1.2.1 * **github.com/rivo/tview** fd649dbf1223 -> c76f7879f592 * **github.com/siderolabs/crypto** v0.4.4 -> v0.5.0 * **github.com/siderolabs/discovery-api** v0.1.4 -> v0.1.5 * **github.com/siderolabs/discovery-client** v0.1.9 -> v0.1.10 * **github.com/siderolabs/extras** v1.8.0 -> v1.9.0-alpha.0-1-geab6e58 * **github.com/siderolabs/gen** v0.5.0 -> v0.7.0 * **github.com/siderolabs/go-blockdevice** v0.4.7 -> v0.4.8 * **github.com/siderolabs/go-blockdevice/v2** v2.0.2 -> v2.0.3 * **github.com/siderolabs/go-circular** v0.2.0 -> v0.2.1 * **github.com/siderolabs/go-cmd** v0.1.1 -> v0.1.3 * **github.com/siderolabs/go-kubernetes** v0.2.12 -> v0.2.15 * **github.com/siderolabs/grpc-proxy** v0.4.1 -> v0.5.1 * **github.com/siderolabs/pkgs** v1.8.0-8-gdf1a1a5 -> v1.9.0-alpha.0-37-g4699763 * **github.com/siderolabs/proto-codec** v0.1.1 **_new_** * **github.com/siderolabs/siderolink** v0.3.10 -> v0.3.11 * **github.com/siderolabs/talos/pkg/machinery** v1.8.0 -> v1.9.0-alpha.1 * **github.com/siderolabs/tools** v1.8.0-1-ga0c06c6 -> v1.9.0-alpha.0-7-g3750064 * **golang.org/x/net** v0.29.0 -> v0.30.0 * **golang.org/x/sys** v0.25.0 -> v0.26.0 * **golang.org/x/term** v0.24.0 -> v0.25.0 * **golang.org/x/text** v0.18.0 -> v0.19.0 * **golang.org/x/time** v0.6.0 -> v0.7.0 * **google.golang.org/grpc** v1.66.0 -> v1.67.1 * **google.golang.org/protobuf** v1.34.2 -> v1.35.1 * **k8s.io/api** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apimachinery** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apiserver** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/client-go** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/component-base** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/cri-api** v0.32.0-alpha.0 -> v0.32.0-beta.0 * **k8s.io/kube-scheduler** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubectl** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubelet** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/pod-security-admission** v0.31.1 -> v0.32.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.70 -> v1.2.71 Previous release can be found at [v1.8.0](https://github.com/siderolabs/talos/releases/tag/v1.8.0) ## [Talos 1.9.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.9.0-alpha.1) (2024-11-08) Welcome to the v1.9.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### AppArmor Talos Linux starting with v1.9 will ship with SELinux LSM enabled by default. If you need to use AppArmor LSM add the following to the machine configuration: ```yaml machine: install: extraKernelArgs: - -selinux - lsm=lockdown,capability,yama,apparmor,bpf - apparmor=1 ``` ### Auditd Talos Linux now starts a auditd service by default. Logs can be read with `talosctl logs auditd`. ### `talosctl cgroups` The `talosctl cgroups` command has been added to the `talosctl` tool. This command allows you to view the cgroup resource consumption and limits for a machine, e.g. `talosctl cgroups --preset memory`. ### udevd Talos previously used `udevd` to provide `udevd`, now it uses `systemd-udevd` instead. ### Component Updates Linux: 6.6.59 containerd: 2.0.0 Flannel: 0.26.0 Kubernetes: 1.32.0-beta.0 runc: 1.2.1 Talos is built with Go 1.23.2. ### User Namespaces Talos Linux now supports running Kubernetes pods with user namespaces enabled. Refer to the [documentation](https://www.talos.dev/v1.9/kubernetes-guides/configuration/usernamespace/) for more information. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Dmitry Sharshakov * Joakim Nohlgård * Jean-Francois Roy * Utku Ozdemir * blablu * Adolfo Ochagavía * Dan Rue * David Backeus * Eddie Wang * Florian Ströger * Hexoplon * Jakob Maležič * KBAegis * Mike Beaumont * Nebula * Nico Berlee * Philip Schmid * Philipp Kleber * Remko Molier * Robby Ciliberto * Ryan Borstelmann * Serge Logvinov * Spencer Smith * Steven Cassamajor * Tim Jones * adilTepe * ekarlso * naed3r * nevermarine * solidDoWant ### Changes
140 commits

* [`fb72e4b7b7`](https://github.com/siderolabs/talos/commit/fb72e4b7b74979acf743d20c7c099bc5513836e0) fix(ci): skip test if `UserNamespacesSupport` feature gate is not set * [`11380f933d`](https://github.com/siderolabs/talos/commit/11380f933ddd3fe42dc01d5ed09ceff0d62b417d) feat: display current CPU frequency on dashboard * [`fbce267aee`](https://github.com/siderolabs/talos/commit/fbce267aee98e3b4b6acace156aa22d75ad01d3d) feat: check bridged interfaces should not have addresses * [`942962bf00`](https://github.com/siderolabs/talos/commit/942962bf005a7036e04f4e572f3434f476cb567c) docs: add docs on usernamespace support in k8s * [`0406a05a98`](https://github.com/siderolabs/talos/commit/0406a05a986fabc3834c5a0de48362826268edbe) chore: update pkgs to ones built with gcc 14.2 * [`2e127627dc`](https://github.com/siderolabs/talos/commit/2e127627dce7251d5848718036780c91384c4396) docs: add apparmor enablement release notes * [`aa9311f3d8`](https://github.com/siderolabs/talos/commit/aa9311f3d840c7b5a69a1eb6ab4cb3b1a7bff135) fix: install disk matcher error * [`1800f81044`](https://github.com/siderolabs/talos/commit/1800f8104486f01e8a3437432e508893f02f809c) fix: selinux handling and apparmor tests * [`313bffadfb`](https://github.com/siderolabs/talos/commit/313bffadfb66b053f51046300764e94db088b18a) feat: update Kubernetes to v1.32.0-beta.0 * [`bbfa144510`](https://github.com/siderolabs/talos/commit/bbfa144510063fdcdebbc017b4fb382ac839370c) feat: update containerd to v2.0.0 * [`8e02b9fcbf`](https://github.com/siderolabs/talos/commit/8e02b9fcbfba421abd13ffe4fc8ea3892d4673eb) docs: update manual k8s upgrade docs * [`474949dc77`](https://github.com/siderolabs/talos/commit/474949dc77363123f0e8cf2c918ecacb82b4dbdd) feat: add dm-cache dm-cache-smq kernel modules * [`5112547d6b`](https://github.com/siderolabs/talos/commit/5112547d6b12b4ff40e7863f363cf519efb8c76c) chore: generate support zip for crashdump * [`a867f85e4c`](https://github.com/siderolabs/talos/commit/a867f85e4cb662a17b0738f1f0de4f1485ad925a) feat: label system socket and runtime files * [`398f714cff`](https://github.com/siderolabs/talos/commit/398f714cff04c524394933da17cbc21ad239cd42) feat: update Linux 6.6.59, runc 1.2.1 * [`05c620957c`](https://github.com/siderolabs/talos/commit/05c620957ca741451da395036e8eca59e631fe8d) feat: allow extra mounts for docker-based `talosctl cluster create` * [`cedabeddf7`](https://github.com/siderolabs/talos/commit/cedabeddf7d191f39525a61e65164f280b6807f8) chore: cleanup code * [`61d363e1d0`](https://github.com/siderolabs/talos/commit/61d363e1d093047886638d5bc5b9f2181c8bd894) chore: update go-auditlib * [`960a040491`](https://github.com/siderolabs/talos/commit/960a040491de5c95b104b4a39ea519095eb47931) feat: start enabling SELinux * [`7f3aaa21cd`](https://github.com/siderolabs/talos/commit/7f3aaa21cd8d969e26721235a4191ba3bdbc1f8f) fix: update permissions for logging directories in /var * [`0e6c983b84`](https://github.com/siderolabs/talos/commit/0e6c983b847f679a074c1794fbe77d21a5994233) fix: mount /sys/kernel/security conditionally * [`74b0e8c371`](https://github.com/siderolabs/talos/commit/74b0e8c3713a01f83758556672583880ce5c684a) fix: make route normalization keep family * [`0a3761c22f`](https://github.com/siderolabs/talos/commit/0a3761c22f98783c6696f143611d600287a471a3) fix: talosctl windows arm64 * [`4b10c5328b`](https://github.com/siderolabs/talos/commit/4b10c5328b861b4bcdcec3ca21bd55b91e969b44) chore: add Windows ARM64 build for talosctl * [`9abf16108e`](https://github.com/siderolabs/talos/commit/9abf16108ede75984845297d03673d56cb561c2f) feat: add auditd service * [`d464ca869f`](https://github.com/siderolabs/talos/commit/d464ca869f8949ffbb990c6fb02fbbcbe0abcbe1) chore: drop runc memfd bind added in #9069 * [`b54d26c2c3`](https://github.com/siderolabs/talos/commit/b54d26c2c3f3a52c6d1ec3fddb7a373175815de3) fix: mount pseudo sub-mountpoints in init * [`7aeb15f730`](https://github.com/siderolabs/talos/commit/7aeb15f73094a23aea1d6b263ca2eca061c8a257) chore: disable coredns cache for cluster domain * [`d8b652150c`](https://github.com/siderolabs/talos/commit/d8b652150cec408f2bf3307565b9db691b21bfe9) docs: add warning about NVMe bus path bug * [`3e16ab135e`](https://github.com/siderolabs/talos/commit/3e16ab135e2be8c9b652d67f9e7eadbc3691c5ca) feat: update Kubernetes to v1.32.0-alpha.3 * [`0b8b356777`](https://github.com/siderolabs/talos/commit/0b8b3567771fbe796926dc9a6e904e7102535170) feat: add BridgePort property to network machine configuration * [`b379506259`](https://github.com/siderolabs/talos/commit/b3795062596ef45dd309f1ca56aab31d2a1a0efc) fix: use more correct condition to skip generating hosts files * [`62ec7ec336`](https://github.com/siderolabs/talos/commit/62ec7ec3367233823c09befddc5ad312aa607822) refactor: replace the old v1 mount package with new one * [`0ece13c623`](https://github.com/siderolabs/talos/commit/0ece13c6236c7eda474d3734fcc4c4060299ac43) docs: update network-config.md (cont) * [`93827f0485`](https://github.com/siderolabs/talos/commit/93827f0485a92b46da83b80a2a55f2569f70fe57) docs: update network-config.md * [`423b1e5fb2`](https://github.com/siderolabs/talos/commit/423b1e5fb22d9e785a3832741d796120b84a5e38) fix: do not trim 0 from process SELinux label * [`2136358d65`](https://github.com/siderolabs/talos/commit/2136358d65ddf6ad040ed62c835b335f99a59399) feat: introduce metal agent mode * [`0e15955fcc`](https://github.com/siderolabs/talos/commit/0e15955fcc5d464c5f0ffd1a44eebf4bf32f4844) chore: small refactoring * [`66012a7f26`](https://github.com/siderolabs/talos/commit/66012a7f269010c5ed412d139b14c470063f2429) feat: remove wrapperd and launch processes directly * [`3a0a17ae66`](https://github.com/siderolabs/talos/commit/3a0a17ae66dab5c983571fab0f3eac3f87fbc17c) fix: prevent panic in nocloud platform code * [`dc0c6acbd7`](https://github.com/siderolabs/talos/commit/dc0c6acbd765b6e7838d6af4f1903242d5073782) refactor: remove unmaintained github.com/vishvananda/netlink * [`78353f7918`](https://github.com/siderolabs/talos/commit/78353f79188e81d064c354f6ef3fe3b2e023c644) feat: add parsing of vlanNNNN:ethX style VLAN cmdline args * [`9db7a36bfc`](https://github.com/siderolabs/talos/commit/9db7a36bfc45c9c15fd661fb2a6319dcf4fef210) fix: generation of SecureBoot iso * [`c755b6d7e4`](https://github.com/siderolabs/talos/commit/c755b6d7e4600fdfb32be50422b7efb0fdabef63) fix: update the CRI sandbox image reference * [`cec290b354`](https://github.com/siderolabs/talos/commit/cec290b354773b2b0f2c2ae9d57f36e06fe2654d) feat: allow extensions to log to console * [`b7801df827`](https://github.com/siderolabs/talos/commit/b7801df827d8e1e9a2db7dac0a62c3802de4d73c) fix: wait for udevd to be running before activating LVM * [`d4cb478a50`](https://github.com/siderolabs/talos/commit/d4cb478a50ce41c3699b7846388e537ddf18a703) docs: improve field description for BridgeSTP, BridgeVLAN * [`7329824b24`](https://github.com/siderolabs/talos/commit/7329824b2411fef3b23fd90380033441048f6512) docs: add Mynewsdesk to ADOPTERS.md * [`a13cf76a34`](https://github.com/siderolabs/talos/commit/a13cf76a3415f458ff3235981c1be8202e1800bb) chore: simplify `DNSUpstreamController` and `DNSUpstream` resource * [`62d185473e`](https://github.com/siderolabs/talos/commit/62d185473e258c0c34eff5aed4c18d81d4b92a89) fix: talosctl process null character * [`77d7368eae`](https://github.com/siderolabs/talos/commit/77d7368eae2da6d2c9aa896afc8013007909a958) feat: update containerd to v2.0.0-rc.6 * [`d39393879a`](https://github.com/siderolabs/talos/commit/d39393879a1f98ac3de7a96808301d1e07fd95f3) fix: rework the 'metal-iso' config acquisition * [`1993afca9f`](https://github.com/siderolabs/talos/commit/1993afca9fff7e889b497ec3241cfdca42294f18) chore: create /usr/etc in a different step * [`8680351c13`](https://github.com/siderolabs/talos/commit/8680351c131d29a76682569742dbd44c8ffe47d3) chore: move system extensions' udev rules * [`3067f64c84`](https://github.com/siderolabs/talos/commit/3067f64c8435ef2d5453100a1584dc3c6915ba0b) feat: update Flannel to v0.26.0 * [`8658d6865f`](https://github.com/siderolabs/talos/commit/8658d6865fa0bcbfcebe483b7332d3b56e239979) docs: typo in deploying cilium * [`49bbadc4bf`](https://github.com/siderolabs/talos/commit/49bbadc4bf1e79e48c057d473ae21426b273c588) docs: add documentation on performance tuning * [`534b0ce183`](https://github.com/siderolabs/talos/commit/534b0ce1833462b22f3761258e0e95813a355fb2) feat: update runc to 1.2.0 final * [`2172535237`](https://github.com/siderolabs/talos/commit/21725352373da7835d95f8f934847dab404782f8) docs: fix image factory links * [`375e3da73f`](https://github.com/siderolabs/talos/commit/375e3da73fcb02c7caea2576289fefdc395a1ed2) feat: update Kubernetes to 1.32.0-alpha.2 * [`9e6f64df04`](https://github.com/siderolabs/talos/commit/9e6f64df047527ecb42df5fdf5fd2f9767d21437) fix: improve error messages for invalid bridge/bond configuration * [`7c8c72c2b2`](https://github.com/siderolabs/talos/commit/7c8c72c2b2a4edb412e097a9e013ab21727339cf) fix: correct error message for invalid ip= * [`ead46997c9`](https://github.com/siderolabs/talos/commit/ead46997c918ab1139ca12e87beefbbda29614e1) chore: rename tpm2.PCRExtent -> tpm2.PCRExtend * [`867c4b8125`](https://github.com/siderolabs/talos/commit/867c4b8125ee738f9a82e5e87809eb95bdd2f778) docs: fix typo in prodnotes.md * [`1b22df48a4`](https://github.com/siderolabs/talos/commit/1b22df48a41578d19fb512bd8111a481b64011e2) chore: support debug shell for advanced development * [`c14b446229`](https://github.com/siderolabs/talos/commit/c14b4462292bd7e6088fce35d6880a9b2b56335c) feat: update Kubernetes to v1.32.0-alpha.1 * [`29780d35a0`](https://github.com/siderolabs/talos/commit/29780d35a052134d50576f6506c2728489a30506) test: add an integration test for verifying process parameters * [`3d342af447`](https://github.com/siderolabs/talos/commit/3d342af4479ed12e2af10021ec4e7ab9c2af6d75) fix: update incorrect alias for PCIDevice resource * [`f7d35a5e0b`](https://github.com/siderolabs/talos/commit/f7d35a5e0b4e3a04a639d663e5a580e22fea76db) release(v1.9.0-alpha.0): prepare release * [`e0434d77d7`](https://github.com/siderolabs/talos/commit/e0434d77d754f8834ba903f4c09b08634cfd3934) feat: update dependencies * [`5c5a248861`](https://github.com/siderolabs/talos/commit/5c5a248861c8e5848f9a23cd0cd7b3b749f21e4b) feat: add Talos 1.9 compatibility guarantees * [`bc4c21f41a`](https://github.com/siderolabs/talos/commit/bc4c21f41a0066ba6cefb5b753c52d76a6b0f629) test: add json logs test environment * [`71faa32942`](https://github.com/siderolabs/talos/commit/71faa3294246947f6bd212979ceb31e793ae0604) docs: nvidia proprietary/oss hardware requirement * [`59a78da42c`](https://github.com/siderolabs/talos/commit/59a78da42cdea8fbccc35d0851f9b0eef928261b) chore: add proto-codec/codec * [`7ff1cedfe3`](https://github.com/siderolabs/talos/commit/7ff1cedfe3eee51505c30439eec4a2df9b452b2e) chore: update siderolabs/crypto module and return proper ALPN * [`ccbd5aed39`](https://github.com/siderolabs/talos/commit/ccbd5aed39b360664d1f80c8b146050e9df9ff7b) feat: optionally decode hcloud userdata as base64 * [`34f652ce82`](https://github.com/siderolabs/talos/commit/34f652ce822fcb70a292289fe6ba5d1bd7a34f97) feat: add well-known app.kubernetes.io labels to control-plane pods * [`fc89dc2164`](https://github.com/siderolabs/talos/commit/fc89dc21643a923cb7d0d3944405521bf849631b) fix: support `extra-disks` when using iso * [`f2bff814de`](https://github.com/siderolabs/talos/commit/f2bff814de0b237fbed419234b935dc9f9637554) chore: add arm64 target for integration-test * [`5853bb0ea4`](https://github.com/siderolabs/talos/commit/5853bb0ea4d6a65635086bdef617d6d0800cabd0) fix: json logging panic * [`a859cff364`](https://github.com/siderolabs/talos/commit/a859cff364aa4dc9b4b880417b821f7ecf5602ac) chore: use virtio driver for disks in arm64 * [`db248de88d`](https://github.com/siderolabs/talos/commit/db248de88dec2467e4340f699cde98217979ba4b) chore(ci): add config for lldpd extension * [`9f0de9f43d`](https://github.com/siderolabs/talos/commit/9f0de9f43dc4467f0bdeda117b4946ae12db50ab) test: update provision upgrade tests for Talos 1.9 * [`39fe285e69`](https://github.com/siderolabs/talos/commit/39fe285e69691059f91d8c7c5506e156356263d9) fix: skip ram disks * [`a9bff3a1d0`](https://github.com/siderolabs/talos/commit/a9bff3a1d084c32a654555e71e2592e60edbdcb6) test: skip no error test in Cilium * [`4d902021bb`](https://github.com/siderolabs/talos/commit/4d902021bb3c55bc212cbb3e2443b6552400622f) fix: do not use pflag csv comma reader for config-patch * [`5371788ce1`](https://github.com/siderolabs/talos/commit/5371788ce169a0381e08f0d902ac81f3f89ba5bd) fix: typo in documentation * [`8a228ba6bc`](https://github.com/siderolabs/talos/commit/8a228ba6bc702f21fca06dc2ecb3e8e846839cd3) docs: add egress documentation * [`182325cb07`](https://github.com/siderolabs/talos/commit/182325cb0791da1d4dcd3914a643c44232502524) test: skip lvm test if not enough user disks available * [`519a48302e`](https://github.com/siderolabs/talos/commit/519a48302e771fd9b331913166d55c50fff4961a) fix: wipe system partitions correctly via kernel args * [`0a2b4556c5`](https://github.com/siderolabs/talos/commit/0a2b4556c55eda27536ee563f60bcf5d69379479) fix: volume encryption with failing keyslots * [`6affbd3182`](https://github.com/siderolabs/talos/commit/6affbd3182ebe0209ed5433c534062b7ad672b6a) fix: update grpc-go the latest patch release * [`77a4a4adc7`](https://github.com/siderolabs/talos/commit/77a4a4adc7232b4382f2a530f4056a1fff6c50b4) fix: scaleway metadata * [`7acadc0c8f`](https://github.com/siderolabs/talos/commit/7acadc0c8fa969e4de7f0d4f68b0fd0cd833b489) fix: do not stop udevd before unmounting volumes * [`6a081055b0`](https://github.com/siderolabs/talos/commit/6a081055b0dd4e3ce5c40392c8415a0a55b2591c) feat: update Flannel to v0.25.7 * [`2362f6d3ee`](https://github.com/siderolabs/talos/commit/2362f6d3ee51a0a8b541a872d39ac82892502e17) fix: improve container detection * [`b67bc73fd3`](https://github.com/siderolabs/talos/commit/b67bc73fd30a8e07f26c47a746ca53f2af41d366) fix: fix mdadm system extension * [`f08669c7a9`](https://github.com/siderolabs/talos/commit/f08669c7a9583a559dc53f233798305bbab07b8a) feat: bring in lpfc kernel module driver * [`6a014374be`](https://github.com/siderolabs/talos/commit/6a014374be26f0caf8faa90a34f2476e0e77a46a) feat: enable QEDF driver * [`f711907e03`](https://github.com/siderolabs/talos/commit/f711907e038cea20f6b831ea5ad8c3b18638c1b4) fix: make /var/run empty on reboots * [`7d02eb60f4`](https://github.com/siderolabs/talos/commit/7d02eb60f47652f4b72f170b28a8b964729af013) docs: fix typo in CloudStack docs * [`74861573a7`](https://github.com/siderolabs/talos/commit/74861573a793f9e143d7d2638990f37ec639aa88) fix: multiple fixes for LVM activation * [`74c12c20e0`](https://github.com/siderolabs/talos/commit/74c12c20e02e4ec29b2b374cebc996ddf8fa90c7) feat: replace eudev with systemd-udevd * [`0a4df4ef84`](https://github.com/siderolabs/talos/commit/0a4df4ef84467014d5be4b4ec57de0e778cfb21e) docs: fix nvidia CRI config example * [`afc1e1a46a`](https://github.com/siderolabs/talos/commit/afc1e1a46a559aac3aa5f4a2708ba8d2c9228929) docs: fix typo in extraMounts directory * [`a341bdb064`](https://github.com/siderolabs/talos/commit/a341bdb0640294a07939670919c56cbfa7a861c4) fix: prevent file descriptors leaks to child processes * [`dec653bfe1`](https://github.com/siderolabs/talos/commit/dec653bfe1feb84ea2ed1a779b1bfc783dc61160) chore: better lvm2 tests * [`908fd8789c`](https://github.com/siderolabs/talos/commit/908fd8789cc1b22e556a7ffe307409931976ba08) feat: support cgroup deep analysis in `talosctl` * [`aa846cc186`](https://github.com/siderolabs/talos/commit/aa846cc186c1c6125f8f39ea084fa2023512656f) feat: add support for CI Network config in nocloud * [`10f2539f23`](https://github.com/siderolabs/talos/commit/10f2539f237aeb3af2caeb3c349c062f203219b6) chore: disable cloud-images cron workflow * [`b07a8b36b2`](https://github.com/siderolabs/talos/commit/b07a8b36b24d57337323e72d6032304c4cade927) chore: ignore more plugins for system containerd * [`392c4798f0`](https://github.com/siderolabs/talos/commit/392c4798f0bff7cb4518609deae7c90581f013f5) feat: prepare for Talos 1.9 * [`ea7bf9fb43`](https://github.com/siderolabs/talos/commit/ea7bf9fb43dff8cf8ec4dfd4f629e8f826bc2ded) docs: update storage.md * [`4ab8dee69a`](https://github.com/siderolabs/talos/commit/4ab8dee69ac07c811cbe121ca9e2d9bd01148863) fix: build talosctl without `tcell_minimal` * [`2fa019bd97`](https://github.com/siderolabs/talos/commit/2fa019bd9751ad96085ade52628023adf17658d3) docs: enable 'edit on GitHub' link * [`d2ccbc2b15`](https://github.com/siderolabs/talos/commit/d2ccbc2b1512b6323d48a764c4af534d49b4bd27) docs: update hetzner documentation for CCM * [`d498f647cd`](https://github.com/siderolabs/talos/commit/d498f647cd9dfcd575f51005c9b78c2c1c7b51ca) docs: fix Kernel Self Protection Project (KSPP) references * [`0ec75463ee`](https://github.com/siderolabs/talos/commit/0ec75463eecebfb543a64b0c859ba0b2477e406f) docs: make Talos 1.8 current release * [`9b77698cf2`](https://github.com/siderolabs/talos/commit/9b77698cf2ff64c6f6d198d05c2012ab7fa858be) fix: update blockdevice library to v2.0.2 * [`e46227ab95`](https://github.com/siderolabs/talos/commit/e46227ab95a6d06132e82315f55b5ced533ddabb) docs: fix kubespan name inconsistency * [`6b15ca19cd`](https://github.com/siderolabs/talos/commit/6b15ca19cd1291b8a245d72d5153827945cad037) fix: audit and fix cgroup reservations * [`32b5d01ed3`](https://github.com/siderolabs/talos/commit/32b5d01ed3396e8f54a245cc6d9818119aec8291) chore: bump lvm2 * [`6484581eb8`](https://github.com/siderolabs/talos/commit/6484581eb888996a8dc829915439fb63606dd794) feat: allow /sbin/ldconfig in extensions * [`9fa08e8437`](https://github.com/siderolabs/talos/commit/9fa08e843728dbd85ed7e0035f59cdd6232de9a9) chore: refactor tests * [`d8ab4981b6`](https://github.com/siderolabs/talos/commit/d8ab4981b626ff41fbcdb526a032a5584519e3df) feat: support lvm auto activation * [`8166a58b36`](https://github.com/siderolabs/talos/commit/8166a58b364f760212b2a610ce0d764b8b4c5c46) fix: filter out non-printable characters in process line * [`806b6aaf52`](https://github.com/siderolabs/talos/commit/806b6aaf52f20ed0f32107b3d0372d6e3ff974be) docs: add SECURITY.md * [`7bd26df308`](https://github.com/siderolabs/talos/commit/7bd26df30803307e4eece3e382aafebc55e7b260) docs: document `/dev/net/tun` compatibility * [`18daedb511`](https://github.com/siderolabs/talos/commit/18daedb511e769717ba56eb05cccab72118a4813) fix: strategic merge patch delete for map keys * [`f3370529ac`](https://github.com/siderolabs/talos/commit/f3370529ac042865a4b2d793465916fcae2d4b33) docs: correct typo * [`8d6884a8e2`](https://github.com/siderolabs/talos/commit/8d6884a8e28e1bfa29f9a479e0f7179819cf70cd) test: add a test for inline machine config trusted roots * [`d4a6d017db`](https://github.com/siderolabs/talos/commit/d4a6d017dbb91e22c60787cdf64b242057b1ebef) fix: ignore invalid NTP responses * [`869f8379f2`](https://github.com/siderolabs/talos/commit/869f8379f2317175901e8cb3deec4b800e7ab603) feat: update default Kubernetes version to 1.31.1 * [`780a1f198a`](https://github.com/siderolabs/talos/commit/780a1f198a5eedd33a27060bdf116bd3a3b26426) fix: update CoreDNS health check * [`79cd031588`](https://github.com/siderolabs/talos/commit/79cd031588a0710b865414f919742ee3ffb998ed) chore: account for resource sorting in dns upstream resource * [`e17fafaca2`](https://github.com/siderolabs/talos/commit/e17fafaca2a16990bc424b54120c49ddbaf8cee1) chore: drop `activateLogicalVolumes` sequencer step * [`a294b366f2`](https://github.com/siderolabs/talos/commit/a294b366f24c6580d304c6c8ad34f481079dc795) fix: parse SideroLink API endpoint correctly * [`a9269ac7b1`](https://github.com/siderolabs/talos/commit/a9269ac7b1217aa2d247c0215c5f2755af468b44) fix: remove extra logging on ethtool ioctl failures * [`5c6277d171`](https://github.com/siderolabs/talos/commit/5c6277d171eea58878ce4fcb4d2fdb7154333ae7) feat: update etcd to 3.5.16 * [`c1ed2984b8`](https://github.com/siderolabs/talos/commit/c1ed2984b85dca791a5081c5da26bba75e3cd579) docs: add what's new for Talos 1.8

### Changes since v1.9.0-alpha.0
68 commits

* [`fb72e4b7b`](https://github.com/siderolabs/talos/commit/fb72e4b7b74979acf743d20c7c099bc5513836e0) fix(ci): skip test if `UserNamespacesSupport` feature gate is not set * [`11380f933`](https://github.com/siderolabs/talos/commit/11380f933ddd3fe42dc01d5ed09ceff0d62b417d) feat: display current CPU frequency on dashboard * [`fbce267ae`](https://github.com/siderolabs/talos/commit/fbce267aee98e3b4b6acace156aa22d75ad01d3d) feat: check bridged interfaces should not have addresses * [`942962bf0`](https://github.com/siderolabs/talos/commit/942962bf005a7036e04f4e572f3434f476cb567c) docs: add docs on usernamespace support in k8s * [`0406a05a9`](https://github.com/siderolabs/talos/commit/0406a05a986fabc3834c5a0de48362826268edbe) chore: update pkgs to ones built with gcc 14.2 * [`2e127627d`](https://github.com/siderolabs/talos/commit/2e127627dce7251d5848718036780c91384c4396) docs: add apparmor enablement release notes * [`aa9311f3d`](https://github.com/siderolabs/talos/commit/aa9311f3d840c7b5a69a1eb6ab4cb3b1a7bff135) fix: install disk matcher error * [`1800f8104`](https://github.com/siderolabs/talos/commit/1800f8104486f01e8a3437432e508893f02f809c) fix: selinux handling and apparmor tests * [`313bffadf`](https://github.com/siderolabs/talos/commit/313bffadfb66b053f51046300764e94db088b18a) feat: update Kubernetes to v1.32.0-beta.0 * [`bbfa14451`](https://github.com/siderolabs/talos/commit/bbfa144510063fdcdebbc017b4fb382ac839370c) feat: update containerd to v2.0.0 * [`8e02b9fcb`](https://github.com/siderolabs/talos/commit/8e02b9fcbfba421abd13ffe4fc8ea3892d4673eb) docs: update manual k8s upgrade docs * [`474949dc7`](https://github.com/siderolabs/talos/commit/474949dc77363123f0e8cf2c918ecacb82b4dbdd) feat: add dm-cache dm-cache-smq kernel modules * [`5112547d6`](https://github.com/siderolabs/talos/commit/5112547d6b12b4ff40e7863f363cf519efb8c76c) chore: generate support zip for crashdump * [`a867f85e4`](https://github.com/siderolabs/talos/commit/a867f85e4cb662a17b0738f1f0de4f1485ad925a) feat: label system socket and runtime files * [`398f714cf`](https://github.com/siderolabs/talos/commit/398f714cff04c524394933da17cbc21ad239cd42) feat: update Linux 6.6.59, runc 1.2.1 * [`05c620957`](https://github.com/siderolabs/talos/commit/05c620957ca741451da395036e8eca59e631fe8d) feat: allow extra mounts for docker-based `talosctl cluster create` * [`cedabeddf`](https://github.com/siderolabs/talos/commit/cedabeddf7d191f39525a61e65164f280b6807f8) chore: cleanup code * [`61d363e1d`](https://github.com/siderolabs/talos/commit/61d363e1d093047886638d5bc5b9f2181c8bd894) chore: update go-auditlib * [`960a04049`](https://github.com/siderolabs/talos/commit/960a040491de5c95b104b4a39ea519095eb47931) feat: start enabling SELinux * [`7f3aaa21c`](https://github.com/siderolabs/talos/commit/7f3aaa21cd8d969e26721235a4191ba3bdbc1f8f) fix: update permissions for logging directories in /var * [`0e6c983b8`](https://github.com/siderolabs/talos/commit/0e6c983b847f679a074c1794fbe77d21a5994233) fix: mount /sys/kernel/security conditionally * [`74b0e8c37`](https://github.com/siderolabs/talos/commit/74b0e8c3713a01f83758556672583880ce5c684a) fix: make route normalization keep family * [`0a3761c22`](https://github.com/siderolabs/talos/commit/0a3761c22f98783c6696f143611d600287a471a3) fix: talosctl windows arm64 * [`4b10c5328`](https://github.com/siderolabs/talos/commit/4b10c5328b861b4bcdcec3ca21bd55b91e969b44) chore: add Windows ARM64 build for talosctl * [`9abf16108`](https://github.com/siderolabs/talos/commit/9abf16108ede75984845297d03673d56cb561c2f) feat: add auditd service * [`d464ca869`](https://github.com/siderolabs/talos/commit/d464ca869f8949ffbb990c6fb02fbbcbe0abcbe1) chore: drop runc memfd bind added in #9069 * [`b54d26c2c`](https://github.com/siderolabs/talos/commit/b54d26c2c3f3a52c6d1ec3fddb7a373175815de3) fix: mount pseudo sub-mountpoints in init * [`7aeb15f73`](https://github.com/siderolabs/talos/commit/7aeb15f73094a23aea1d6b263ca2eca061c8a257) chore: disable coredns cache for cluster domain * [`d8b652150`](https://github.com/siderolabs/talos/commit/d8b652150cec408f2bf3307565b9db691b21bfe9) docs: add warning about NVMe bus path bug * [`3e16ab135`](https://github.com/siderolabs/talos/commit/3e16ab135e2be8c9b652d67f9e7eadbc3691c5ca) feat: update Kubernetes to v1.32.0-alpha.3 * [`0b8b35677`](https://github.com/siderolabs/talos/commit/0b8b3567771fbe796926dc9a6e904e7102535170) feat: add BridgePort property to network machine configuration * [`b37950625`](https://github.com/siderolabs/talos/commit/b3795062596ef45dd309f1ca56aab31d2a1a0efc) fix: use more correct condition to skip generating hosts files * [`62ec7ec33`](https://github.com/siderolabs/talos/commit/62ec7ec3367233823c09befddc5ad312aa607822) refactor: replace the old v1 mount package with new one * [`0ece13c62`](https://github.com/siderolabs/talos/commit/0ece13c6236c7eda474d3734fcc4c4060299ac43) docs: update network-config.md (cont) * [`93827f048`](https://github.com/siderolabs/talos/commit/93827f0485a92b46da83b80a2a55f2569f70fe57) docs: update network-config.md * [`423b1e5fb`](https://github.com/siderolabs/talos/commit/423b1e5fb22d9e785a3832741d796120b84a5e38) fix: do not trim 0 from process SELinux label * [`2136358d6`](https://github.com/siderolabs/talos/commit/2136358d65ddf6ad040ed62c835b335f99a59399) feat: introduce metal agent mode * [`0e15955fc`](https://github.com/siderolabs/talos/commit/0e15955fcc5d464c5f0ffd1a44eebf4bf32f4844) chore: small refactoring * [`66012a7f2`](https://github.com/siderolabs/talos/commit/66012a7f269010c5ed412d139b14c470063f2429) feat: remove wrapperd and launch processes directly * [`3a0a17ae6`](https://github.com/siderolabs/talos/commit/3a0a17ae66dab5c983571fab0f3eac3f87fbc17c) fix: prevent panic in nocloud platform code * [`dc0c6acbd`](https://github.com/siderolabs/talos/commit/dc0c6acbd765b6e7838d6af4f1903242d5073782) refactor: remove unmaintained github.com/vishvananda/netlink * [`78353f791`](https://github.com/siderolabs/talos/commit/78353f79188e81d064c354f6ef3fe3b2e023c644) feat: add parsing of vlanNNNN:ethX style VLAN cmdline args * [`9db7a36bf`](https://github.com/siderolabs/talos/commit/9db7a36bfc45c9c15fd661fb2a6319dcf4fef210) fix: generation of SecureBoot iso * [`c755b6d7e`](https://github.com/siderolabs/talos/commit/c755b6d7e4600fdfb32be50422b7efb0fdabef63) fix: update the CRI sandbox image reference * [`cec290b35`](https://github.com/siderolabs/talos/commit/cec290b354773b2b0f2c2ae9d57f36e06fe2654d) feat: allow extensions to log to console * [`b7801df82`](https://github.com/siderolabs/talos/commit/b7801df827d8e1e9a2db7dac0a62c3802de4d73c) fix: wait for udevd to be running before activating LVM * [`d4cb478a5`](https://github.com/siderolabs/talos/commit/d4cb478a50ce41c3699b7846388e537ddf18a703) docs: improve field description for BridgeSTP, BridgeVLAN * [`7329824b2`](https://github.com/siderolabs/talos/commit/7329824b2411fef3b23fd90380033441048f6512) docs: add Mynewsdesk to ADOPTERS.md * [`a13cf76a3`](https://github.com/siderolabs/talos/commit/a13cf76a3415f458ff3235981c1be8202e1800bb) chore: simplify `DNSUpstreamController` and `DNSUpstream` resource * [`62d185473`](https://github.com/siderolabs/talos/commit/62d185473e258c0c34eff5aed4c18d81d4b92a89) fix: talosctl process null character * [`77d7368ea`](https://github.com/siderolabs/talos/commit/77d7368eae2da6d2c9aa896afc8013007909a958) feat: update containerd to v2.0.0-rc.6 * [`d39393879`](https://github.com/siderolabs/talos/commit/d39393879a1f98ac3de7a96808301d1e07fd95f3) fix: rework the 'metal-iso' config acquisition * [`1993afca9`](https://github.com/siderolabs/talos/commit/1993afca9fff7e889b497ec3241cfdca42294f18) chore: create /usr/etc in a different step * [`8680351c1`](https://github.com/siderolabs/talos/commit/8680351c131d29a76682569742dbd44c8ffe47d3) chore: move system extensions' udev rules * [`3067f64c8`](https://github.com/siderolabs/talos/commit/3067f64c8435ef2d5453100a1584dc3c6915ba0b) feat: update Flannel to v0.26.0 * [`8658d6865`](https://github.com/siderolabs/talos/commit/8658d6865fa0bcbfcebe483b7332d3b56e239979) docs: typo in deploying cilium * [`49bbadc4b`](https://github.com/siderolabs/talos/commit/49bbadc4bf1e79e48c057d473ae21426b273c588) docs: add documentation on performance tuning * [`534b0ce18`](https://github.com/siderolabs/talos/commit/534b0ce1833462b22f3761258e0e95813a355fb2) feat: update runc to 1.2.0 final * [`217253523`](https://github.com/siderolabs/talos/commit/21725352373da7835d95f8f934847dab404782f8) docs: fix image factory links * [`375e3da73`](https://github.com/siderolabs/talos/commit/375e3da73fcb02c7caea2576289fefdc395a1ed2) feat: update Kubernetes to 1.32.0-alpha.2 * [`9e6f64df0`](https://github.com/siderolabs/talos/commit/9e6f64df047527ecb42df5fdf5fd2f9767d21437) fix: improve error messages for invalid bridge/bond configuration * [`7c8c72c2b`](https://github.com/siderolabs/talos/commit/7c8c72c2b2a4edb412e097a9e013ab21727339cf) fix: correct error message for invalid ip= * [`ead46997c`](https://github.com/siderolabs/talos/commit/ead46997c918ab1139ca12e87beefbbda29614e1) chore: rename tpm2.PCRExtent -> tpm2.PCRExtend * [`867c4b812`](https://github.com/siderolabs/talos/commit/867c4b8125ee738f9a82e5e87809eb95bdd2f778) docs: fix typo in prodnotes.md * [`1b22df48a`](https://github.com/siderolabs/talos/commit/1b22df48a41578d19fb512bd8111a481b64011e2) chore: support debug shell for advanced development * [`c14b44622`](https://github.com/siderolabs/talos/commit/c14b4462292bd7e6088fce35d6880a9b2b56335c) feat: update Kubernetes to v1.32.0-alpha.1 * [`29780d35a`](https://github.com/siderolabs/talos/commit/29780d35a052134d50576f6506c2728489a30506) test: add an integration test for verifying process parameters * [`3d342af44`](https://github.com/siderolabs/talos/commit/3d342af4479ed12e2af10021ec4e7ab9c2af6d75) fix: update incorrect alias for PCIDevice resource

### Changes from siderolabs/crypto
1 commit

* [`58b2f92`](https://github.com/siderolabs/crypto/commit/58b2f9291c7e763a7210cfa681f88a7fa2230bf3) chore: use HTTP/2 ALPN by default

### Changes from siderolabs/discovery-api
1 commit

* [`005e92c`](https://github.com/siderolabs/discovery-api/commit/005e92cf4ad0059334bfd35285a97c85f12aa263) chore: rekres and regen

### Changes from siderolabs/discovery-client
1 commit

* [`b74fb90`](https://github.com/siderolabs/discovery-client/commit/b74fb9039fcfd8db9d6becf3044f9f41f387ea27) fix: allow custom TLS config for the client

### Changes from siderolabs/extras
2 commits

* [`eab6e58`](https://github.com/siderolabs/extras/commit/eab6e58aa9bdf49789cd4d64d2e27f61023421ca) feat: update dependencies * [`1459d78`](https://github.com/siderolabs/extras/commit/1459d78cbeb297c023501a3eb785a27a5bdd4933) feat: update pkgs for 1.9

### Changes from siderolabs/gen
3 commits

* [`e847d2a`](https://github.com/siderolabs/gen/commit/e847d2ace9ede4a17283426dfbc8229121f2909b) chore: add more utilities to xiter * [`f3c5a2b`](https://github.com/siderolabs/gen/commit/f3c5a2b5aba74e4935d073a0135c4904ef3bbfef) chore: add `Empty` and `Empty2` iterators * [`c53b90b`](https://github.com/siderolabs/gen/commit/c53b90b4a418b8629d938af06900249ce5acd9e6) chore: add packages xiter/xstrings/xbytes

### Changes from siderolabs/go-blockdevice
1 commit

* [`134c41b`](https://github.com/siderolabs/go-blockdevice/commit/134c41be6f4c498a149b8098fa8d862c5c47ca54) fix: fast wipe also last 1MB of the device

### Changes from siderolabs/go-circular
1 commit

* [`9a0f7b0`](https://github.com/siderolabs/go-circular/commit/9a0f7b02c80ad6c2d953b2d3dd388c56e89363ea) fix: multiple data race issues

### Changes from siderolabs/go-cmd
3 commits

* [`d735250`](https://github.com/siderolabs/go-cmd/commit/d73525092a1bb135da54d538e5d64c4dcc80259e) fix: return an error on process nonzero exit code * [`5662c7f`](https://github.com/siderolabs/go-cmd/commit/5662c7f8d5cf475c57b3a23b8d8546d960ebc60a) feat: add an equivalent of WaitWrapper for os.Process * [`71fced6`](https://github.com/siderolabs/go-cmd/commit/71fced673e013423bba83064767a90372dd6cf51) chore: rekres and move to GHA

### Changes from siderolabs/go-kubernetes
3 commits

* [`87d2e8e`](https://github.com/siderolabs/go-kubernetes/commit/87d2e8e664c3e3e64403bcfcfe2f8691f60c6481) feat: add one more deprecation for 1.32.0-beta.0 * [`e56a7f6`](https://github.com/siderolabs/go-kubernetes/commit/e56a7f65808b90058df16a4133f19484beeedc31) fix: update deprecations based on Kubernetes 1.32.0-alpha.3 * [`381f251`](https://github.com/siderolabs/go-kubernetes/commit/381f251662eaae9b48470ce00f504c2c64187612) feat: update for Kubernetes 1.32

### Changes from siderolabs/grpc-proxy
2 commits

* [`de1c628`](https://github.com/siderolabs/grpc-proxy/commit/de1c6286b7d16d8485bf8bb55c8783c8773851a0) fix: copy data from big frame msg * [`ef47ec7`](https://github.com/siderolabs/grpc-proxy/commit/ef47ec77d2a9f0f42e713d456943dfe9ee86a629) chore: upgrade Codec implementations and usages to Codec2

### Changes from siderolabs/pkgs
38 commits

* [`4699763`](https://github.com/siderolabs/pkgs/commit/4699763c6d745620aecd0219fc78962e4fa0a01e) feat: update gcc to 14.2 * [`9a98f73`](https://github.com/siderolabs/pkgs/commit/9a98f73de2c0353e9f8f194bd31c50eea1fb4d5b) feat: update containerd to v2.0.0 * [`20e1e08`](https://github.com/siderolabs/pkgs/commit/20e1e0857a7d0cf05983998df3160fe0607d5075) feat: enable CONFIG_DM_CACHE * [`df45e16`](https://github.com/siderolabs/pkgs/commit/df45e1676828e49d77718e717b2e0e425122c62c) feat: update Linux to 6.6.59 * [`2e733cc`](https://github.com/siderolabs/pkgs/commit/2e733cccfd225712eb7395cf04b6d8df0bf2b8d2) feat: bump dependencies * [`c92e123`](https://github.com/siderolabs/pkgs/commit/c92e123b40457f45e9fc0fe271804fa95c8d4f09) fix: enable nvme and 2.5gbit ethernet on nanopi-r5s * [`b160184`](https://github.com/siderolabs/pkgs/commit/b160184a479c85b8b19d2a874e5d6d52db9ed096) feat: update runc to v1.2.1 * [`e9950d9`](https://github.com/siderolabs/pkgs/commit/e9950d9097fa002e79e2933344f68bb09ad6d4df) chore: drop syslinux * [`fc2e8dc`](https://github.com/siderolabs/pkgs/commit/fc2e8dc07ad096d0394f8deacb20d423ef102c2f) feat: update containerd to v2.0.0-rc.6 * [`38304a6`](https://github.com/siderolabs/pkgs/commit/38304a60e3b32f0b3216ce8128df5f98d8be6812) feat: update Linux to 6.6.58 * [`84b8df8`](https://github.com/siderolabs/pkgs/commit/84b8df8baf408ab22649b02910294154e0ad5f3b) chore: do not use /usr/etc/udev * [`c9282c8`](https://github.com/siderolabs/pkgs/commit/c9282c8dc6a535b69a953c0b4f43fd0780c5bb30) feat: update runc to 1.2.0 * [`38ad08e`](https://github.com/siderolabs/pkgs/commit/38ad08ecb57d456b76f6d53a7d8a75c3b32f7d61) fix: default IOMMU mode to 'lazy' * [`be92da0`](https://github.com/siderolabs/pkgs/commit/be92da09f3196d96b1358efd6a7c667297d3ecfb) feat: update Linux to 6.6.57, update Linux firmware * [`0b67a13`](https://github.com/siderolabs/pkgs/commit/0b67a133b12c548ba6d28f2ea0c979cb10512812) feat: bump dependencies * [`dd5f928`](https://github.com/siderolabs/pkgs/commit/dd5f928266761215fc402085594493c9f9b329b4) feat: update Linux 6.6.56 and protect /proc/mem * [`b1bf972`](https://github.com/siderolabs/pkgs/commit/b1bf9725068029f34193b3abe1586a3d1f542b17) feat: enable CONFIG_XFRM_STATISTICS * [`c63beae`](https://github.com/siderolabs/pkgs/commit/c63beae426026c8ef1b3228b8d978ca5fcc9111b) feat: update Linux to 6.6.54 * [`f474a55`](https://github.com/siderolabs/pkgs/commit/f474a55176dca7ab88b5a29f8d97ce6f31282abd) fix: libselinux: support running without /etc/selinux * [`ba0341e`](https://github.com/siderolabs/pkgs/commit/ba0341e39dafb3fe39b5efbc8a8e8d04df96a0e7) fix: systemd-udevd: search for config in /usr/etc * [`2b193f1`](https://github.com/siderolabs/pkgs/commit/2b193f14e035fa7d7785f26a591debe6ac357f00) feat: add lpfc kernel module * [`1adb946`](https://github.com/siderolabs/pkgs/commit/1adb946b1bb256b30b7bddd517a10d68ce209ada) feat: enable QEDF driver * [`dbbe3d0`](https://github.com/siderolabs/pkgs/commit/dbbe3d0116b24b9d1c2df19ae73b76714a37704e) feat: update containerd to v2.0.0-rc.5 * [`f19590e`](https://github.com/siderolabs/pkgs/commit/f19590edb42a0247d5d509066b21ce35bfc42b93) feat: update Go to 1.23.2 * [`e2a561f`](https://github.com/siderolabs/pkgs/commit/e2a561f576ea7dbc55ebb403d648daa1561c3101) fix: drop the LVM2 udev lvm rule * [`ae205aa`](https://github.com/siderolabs/pkgs/commit/ae205aac9d827783352071f9447f9f7cbf70da20) fix: force LVM to use `/run` as state directory * [`232a153`](https://github.com/siderolabs/pkgs/commit/232a15318a2d47f34b0772663fc3f417905b5406) feat: replace eudev with systemd-udevd * [`40fb82a`](https://github.com/siderolabs/pkgs/commit/40fb82a27a840f3442d6f52374007afb0a5a3770) feat: add libselinux, libsepol, pcre2 and libcap * [`6f40fbb`](https://github.com/siderolabs/pkgs/commit/6f40fbb5e00e449c954d54990085353d061a62c8) feat: update xfsprogs 6.10.1 * [`a1709c7`](https://github.com/siderolabs/pkgs/commit/a1709c76db4ba70de526d7eec18c6b0637ebf7b0) feat: enable module unloading and memory hotplug (for NVIDIA UVM) * [`2c5785b`](https://github.com/siderolabs/pkgs/commit/2c5785b1639a22317a1f7775f0d1f4bd0b0a4b88) feat: enable transparent huge pages in madvise mode * [`ca2e8c8`](https://github.com/siderolabs/pkgs/commit/ca2e8c84b0881e7d1e359ceaf3b55c3b4bb384e7) fix: lvm2 modprobe path * [`6b334a6`](https://github.com/siderolabs/pkgs/commit/6b334a68fbd988ca69d05142a639aa3bcfd16721) feat: update Linux to 6.6.52 * [`e90ae7e`](https://github.com/siderolabs/pkgs/commit/e90ae7ec316f1b9b4d15897f825d3c2c4cefde5e) feat: update Linux firmware to 20240909 * [`79a4f92`](https://github.com/siderolabs/pkgs/commit/79a4f92c5aa4b8288a927351209542c274724475) feat: enable INET_DIAG * [`c9f7eb9`](https://github.com/siderolabs/pkgs/commit/c9f7eb94de2a8df5cfc41c6ea90596832894dc89) feat: update Linux to 6.6.51 * [`126b6a4`](https://github.com/siderolabs/pkgs/commit/126b6a4f7632b2400139e306a0dbb0a545a0dda1) fix: add mpt3sas UBSAN patches * [`a09bf93`](https://github.com/siderolabs/pkgs/commit/a09bf93ce81bde59fcb06d662bc79effc9efaca6) chore: drop UBSAN patch

### Changes from siderolabs/proto-codec
3 commits

* [`0d84c65`](https://github.com/siderolabs/proto-codec/commit/0d84c652784543012f43f8c8d4358c160b27577e) chore: add support for gogo protobuf generator * [`19f8d2e`](https://github.com/siderolabs/proto-codec/commit/19f8d2e5840c19937c60cee0c681343ab658f678) chore: add kres * [`e038bb4`](https://github.com/siderolabs/proto-codec/commit/e038bb42f2be8b80ca09e46bb8704be06a413919) Initial commit

### Changes from siderolabs/siderolink
1 commit

* [`1893385`](https://github.com/siderolabs/siderolink/commit/1893385fe45bf110357a770d31b06f5d79403065) fix: initialize tls listener properly

### Changes from siderolabs/tools
8 commits

* [`3750064`](https://github.com/siderolabs/tools/commit/375006431abb204c275adab2fdc9128060bb32f7) fix: update for musl with close_range * [`0a443c6`](https://github.com/siderolabs/tools/commit/0a443c6d5a1ac6764b22990be0945ef4cae8c32e) feat: update toolchain for gcc 14.2 * [`63ecd80`](https://github.com/siderolabs/tools/commit/63ecd80a4709bcde5c6cc0f112c1faf43ab024ce) feat: bump depedendencies * [`2058296`](https://github.com/siderolabs/tools/commit/2058296cc223b683685f229a9a52de4db7171595) feat: bump dependencies * [`1151610`](https://github.com/siderolabs/tools/commit/1151610f5a5e70d07b715a2bdd76acd06d418595) feat: update Go to 1.23.2 * [`9f2189b`](https://github.com/siderolabs/tools/commit/9f2189b2b032ed283f38b20c53018b921fa06895) fix: bump gettext-tiny to the latest dev version * [`95069d6`](https://github.com/siderolabs/tools/commit/95069d6fd8fccde7ab93465e4e49a5a6ac5d4ed0) feat: update Go to 1.23.1 * [`eec0656`](https://github.com/siderolabs/tools/commit/eec0656aca652d0cc2e1973d5fab56bd4b54f64b) feat: replace gettext with gettext-tiny

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.0 -> v0.5.2 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.13.0 -> v1.16.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.7.0 -> v1.8.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.1.0 -> v1.2.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.1.0 -> v1.2.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.33 -> v1.28.1 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.13 -> v1.16.18 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.35.7 -> v1.37.3 * **github.com/aws/smithy-go** v1.20.4 -> v1.22.0 * **github.com/containerd/containerd/api** v1.8.0-rc.3 -> v1.8.0 * **github.com/containerd/containerd/v2** v2.0.0-rc.4 -> v2.0.0 * **github.com/containerd/errdefs** v0.1.0 -> v1.0.0 * **github.com/containerd/platforms** v0.2.1 -> v1.0.0-rc.0 * **github.com/containerd/typeurl/v2** v2.2.0 -> v2.2.2 * **github.com/containernetworking/plugins** v1.5.1 -> v1.6.0 * **github.com/cosi-project/runtime** v0.5.5 -> v0.7.1 * **github.com/docker/cli** v27.3.1 **_new_** * **github.com/docker/docker** v27.2.0 -> v27.3.1 * **github.com/elastic/go-libaudit/v2** 1df86e79cca7 **_new_** * **github.com/fatih/color** v1.17.0 -> v1.18.0 * **github.com/florianl/go-tc** v0.4.4 **_new_** * **github.com/foxboron/go-uefi** e2076f0e58ca -> fab4fdf2f2f3 * **github.com/fsnotify/fsnotify** v1.7.0 -> v1.8.0 * **github.com/google/cadvisor** v0.50.0 -> v0.51.0 * **github.com/gopacket/gopacket** v1.2.0 -> v1.3.0 * **github.com/hetznercloud/hcloud-go/v2** v2.13.1 -> v2.15.0 * **github.com/klauspost/compress** v1.17.9 -> v1.17.11 * **github.com/linode/go-metadata** v0.2.0 -> v0.2.1 * **github.com/mdlayher/ethtool** v0.1.0 -> v0.2.0 * **github.com/opencontainers/runc** v1.2.0-rc.3 -> v1.2.1 * **github.com/rivo/tview** fd649dbf1223 -> c76f7879f592 * **github.com/siderolabs/crypto** v0.4.4 -> v0.5.0 * **github.com/siderolabs/discovery-api** v0.1.4 -> v0.1.5 * **github.com/siderolabs/discovery-client** v0.1.9 -> v0.1.10 * **github.com/siderolabs/extras** v1.8.0 -> v1.9.0-alpha.0-1-geab6e58 * **github.com/siderolabs/gen** v0.5.0 -> v0.7.0 * **github.com/siderolabs/go-blockdevice** v0.4.7 -> v0.4.8 * **github.com/siderolabs/go-blockdevice/v2** v2.0.2 -> v2.0.3 * **github.com/siderolabs/go-circular** v0.2.0 -> v0.2.1 * **github.com/siderolabs/go-cmd** v0.1.1 -> v0.1.3 * **github.com/siderolabs/go-kubernetes** v0.2.12 -> v0.2.15 * **github.com/siderolabs/grpc-proxy** v0.4.1 -> v0.5.1 * **github.com/siderolabs/pkgs** v1.8.0-8-gdf1a1a5 -> v1.9.0-alpha.0-37-g4699763 * **github.com/siderolabs/proto-codec** v0.1.1 **_new_** * **github.com/siderolabs/siderolink** v0.3.10 -> v0.3.11 * **github.com/siderolabs/talos/pkg/machinery** v1.8.0 -> v1.9.0-alpha.0 * **github.com/siderolabs/tools** v1.8.0-1-ga0c06c6 -> v1.9.0-alpha.0-7-g3750064 * **golang.org/x/net** v0.29.0 -> v0.30.0 * **golang.org/x/sys** v0.25.0 -> v0.26.0 * **golang.org/x/term** v0.24.0 -> v0.25.0 * **golang.org/x/text** v0.18.0 -> v0.19.0 * **golang.org/x/time** v0.6.0 -> v0.7.0 * **google.golang.org/grpc** v1.66.0 -> v1.67.1 * **google.golang.org/protobuf** v1.34.2 -> v1.35.1 * **k8s.io/api** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apimachinery** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/apiserver** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/client-go** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/component-base** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/cri-api** v0.32.0-alpha.0 -> v0.32.0-beta.0 * **k8s.io/kube-scheduler** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubectl** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/kubelet** v0.31.1 -> v0.32.0-beta.0 * **k8s.io/pod-security-admission** v0.31.1 -> v0.32.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.70 -> v1.2.71 Previous release can be found at [v1.8.0](https://github.com/siderolabs/talos/releases/tag/v1.8.0) ## [Talos 1.9.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.9.0-alpha.0) (2024-10-18) Welcome to the v1.9.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### `talosctl cgroups` The `talosctl cgroups` command has been added to the `talosctl` tool. This command allows you to view the cgroup resource consumption and limits for a machine, e.g. `talosctl cgroups --preset memory`. ### udevd Talos previously used `udevd` to provide `udevd`, now it uses `systemd-udevd` instead. ### Component Updates Linux: 6.6.57 containerd: 2.0.0-rc.5 Flannel: 0.25.7 Talos is built with Go 1.23.2. ### Contributors * Andrey Smirnov * Dmitriy Matrenichev * Noel Georgi * Dmitry Sharshakov * Jean-Francois Roy * Adolfo Ochagavía * Dan Rue * Eddie Wang * Florian Ströger * Hexoplon * Mike Beaumont * Philip Schmid * Philipp Kleber * Robby Ciliberto * Ryan Borstelmann * Serge Logvinov * Spencer Smith * Steven Cassamajor * Tim Jones * adilTepe * ekarlso * naed3r ### Changes
72 commits

* [`4529cf52d`](https://github.com/siderolabs/talos/commit/4529cf52db76cd59d5240f6589f463b6fa5f70be) release(v1.9.0-alpha.0): prepare release * [`e0434d77d`](https://github.com/siderolabs/talos/commit/e0434d77d754f8834ba903f4c09b08634cfd3934) feat: update dependencies * [`5c5a24886`](https://github.com/siderolabs/talos/commit/5c5a248861c8e5848f9a23cd0cd7b3b749f21e4b) feat: add Talos 1.9 compatibility guarantees * [`bc4c21f41`](https://github.com/siderolabs/talos/commit/bc4c21f41a0066ba6cefb5b753c52d76a6b0f629) test: add json logs test environment * [`71faa3294`](https://github.com/siderolabs/talos/commit/71faa3294246947f6bd212979ceb31e793ae0604) docs: nvidia proprietary/oss hardware requirement * [`59a78da42`](https://github.com/siderolabs/talos/commit/59a78da42cdea8fbccc35d0851f9b0eef928261b) chore: add proto-codec/codec * [`7ff1cedfe`](https://github.com/siderolabs/talos/commit/7ff1cedfe3eee51505c30439eec4a2df9b452b2e) chore: update siderolabs/crypto module and return proper ALPN * [`ccbd5aed3`](https://github.com/siderolabs/talos/commit/ccbd5aed39b360664d1f80c8b146050e9df9ff7b) feat: optionally decode hcloud userdata as base64 * [`34f652ce8`](https://github.com/siderolabs/talos/commit/34f652ce822fcb70a292289fe6ba5d1bd7a34f97) feat: add well-known app.kubernetes.io labels to control-plane pods * [`fc89dc216`](https://github.com/siderolabs/talos/commit/fc89dc21643a923cb7d0d3944405521bf849631b) fix: support `extra-disks` when using iso * [`f2bff814d`](https://github.com/siderolabs/talos/commit/f2bff814de0b237fbed419234b935dc9f9637554) chore: add arm64 target for integration-test * [`5853bb0ea`](https://github.com/siderolabs/talos/commit/5853bb0ea4d6a65635086bdef617d6d0800cabd0) fix: json logging panic * [`a859cff36`](https://github.com/siderolabs/talos/commit/a859cff364aa4dc9b4b880417b821f7ecf5602ac) chore: use virtio driver for disks in arm64 * [`db248de88`](https://github.com/siderolabs/talos/commit/db248de88dec2467e4340f699cde98217979ba4b) chore(ci): add config for lldpd extension * [`9f0de9f43`](https://github.com/siderolabs/talos/commit/9f0de9f43dc4467f0bdeda117b4946ae12db50ab) test: update provision upgrade tests for Talos 1.9 * [`39fe285e6`](https://github.com/siderolabs/talos/commit/39fe285e69691059f91d8c7c5506e156356263d9) fix: skip ram disks * [`a9bff3a1d`](https://github.com/siderolabs/talos/commit/a9bff3a1d084c32a654555e71e2592e60edbdcb6) test: skip no error test in Cilium * [`4d902021b`](https://github.com/siderolabs/talos/commit/4d902021bb3c55bc212cbb3e2443b6552400622f) fix: do not use pflag csv comma reader for config-patch * [`5371788ce`](https://github.com/siderolabs/talos/commit/5371788ce169a0381e08f0d902ac81f3f89ba5bd) fix: typo in documentation * [`8a228ba6b`](https://github.com/siderolabs/talos/commit/8a228ba6bc702f21fca06dc2ecb3e8e846839cd3) docs: add egress documentation * [`182325cb0`](https://github.com/siderolabs/talos/commit/182325cb0791da1d4dcd3914a643c44232502524) test: skip lvm test if not enough user disks available * [`519a48302`](https://github.com/siderolabs/talos/commit/519a48302e771fd9b331913166d55c50fff4961a) fix: wipe system partitions correctly via kernel args * [`0a2b4556c`](https://github.com/siderolabs/talos/commit/0a2b4556c55eda27536ee563f60bcf5d69379479) fix: volume encryption with failing keyslots * [`6affbd318`](https://github.com/siderolabs/talos/commit/6affbd3182ebe0209ed5433c534062b7ad672b6a) fix: update grpc-go the latest patch release * [`77a4a4adc`](https://github.com/siderolabs/talos/commit/77a4a4adc7232b4382f2a530f4056a1fff6c50b4) fix: scaleway metadata * [`7acadc0c8`](https://github.com/siderolabs/talos/commit/7acadc0c8fa969e4de7f0d4f68b0fd0cd833b489) fix: do not stop udevd before unmounting volumes * [`6a081055b`](https://github.com/siderolabs/talos/commit/6a081055b0dd4e3ce5c40392c8415a0a55b2591c) feat: update Flannel to v0.25.7 * [`2362f6d3e`](https://github.com/siderolabs/talos/commit/2362f6d3ee51a0a8b541a872d39ac82892502e17) fix: improve container detection * [`b67bc73fd`](https://github.com/siderolabs/talos/commit/b67bc73fd30a8e07f26c47a746ca53f2af41d366) fix: fix mdadm system extension * [`f08669c7a`](https://github.com/siderolabs/talos/commit/f08669c7a9583a559dc53f233798305bbab07b8a) feat: bring in lpfc kernel module driver * [`6a014374b`](https://github.com/siderolabs/talos/commit/6a014374be26f0caf8faa90a34f2476e0e77a46a) feat: enable QEDF driver * [`f711907e0`](https://github.com/siderolabs/talos/commit/f711907e038cea20f6b831ea5ad8c3b18638c1b4) fix: make /var/run empty on reboots * [`7d02eb60f`](https://github.com/siderolabs/talos/commit/7d02eb60f47652f4b72f170b28a8b964729af013) docs: fix typo in CloudStack docs * [`74861573a`](https://github.com/siderolabs/talos/commit/74861573a793f9e143d7d2638990f37ec639aa88) fix: multiple fixes for LVM activation * [`74c12c20e`](https://github.com/siderolabs/talos/commit/74c12c20e02e4ec29b2b374cebc996ddf8fa90c7) feat: replace eudev with systemd-udevd * [`0a4df4ef8`](https://github.com/siderolabs/talos/commit/0a4df4ef84467014d5be4b4ec57de0e778cfb21e) docs: fix nvidia CRI config example * [`afc1e1a46`](https://github.com/siderolabs/talos/commit/afc1e1a46a559aac3aa5f4a2708ba8d2c9228929) docs: fix typo in extraMounts directory * [`a341bdb06`](https://github.com/siderolabs/talos/commit/a341bdb0640294a07939670919c56cbfa7a861c4) fix: prevent file descriptors leaks to child processes * [`dec653bfe`](https://github.com/siderolabs/talos/commit/dec653bfe1feb84ea2ed1a779b1bfc783dc61160) chore: better lvm2 tests * [`908fd8789`](https://github.com/siderolabs/talos/commit/908fd8789cc1b22e556a7ffe307409931976ba08) feat: support cgroup deep analysis in `talosctl` * [`aa846cc18`](https://github.com/siderolabs/talos/commit/aa846cc186c1c6125f8f39ea084fa2023512656f) feat: add support for CI Network config in nocloud * [`10f2539f2`](https://github.com/siderolabs/talos/commit/10f2539f237aeb3af2caeb3c349c062f203219b6) chore: disable cloud-images cron workflow * [`b07a8b36b`](https://github.com/siderolabs/talos/commit/b07a8b36b24d57337323e72d6032304c4cade927) chore: ignore more plugins for system containerd * [`392c4798f`](https://github.com/siderolabs/talos/commit/392c4798f0bff7cb4518609deae7c90581f013f5) feat: prepare for Talos 1.9 * [`ea7bf9fb4`](https://github.com/siderolabs/talos/commit/ea7bf9fb43dff8cf8ec4dfd4f629e8f826bc2ded) docs: update storage.md * [`4ab8dee69`](https://github.com/siderolabs/talos/commit/4ab8dee69ac07c811cbe121ca9e2d9bd01148863) fix: build talosctl without `tcell_minimal` * [`2fa019bd9`](https://github.com/siderolabs/talos/commit/2fa019bd9751ad96085ade52628023adf17658d3) docs: enable 'edit on GitHub' link * [`d2ccbc2b1`](https://github.com/siderolabs/talos/commit/d2ccbc2b1512b6323d48a764c4af534d49b4bd27) docs: update hetzner documentation for CCM * [`d498f647c`](https://github.com/siderolabs/talos/commit/d498f647cd9dfcd575f51005c9b78c2c1c7b51ca) docs: fix Kernel Self Protection Project (KSPP) references * [`0ec75463e`](https://github.com/siderolabs/talos/commit/0ec75463eecebfb543a64b0c859ba0b2477e406f) docs: make Talos 1.8 current release * [`9b77698cf`](https://github.com/siderolabs/talos/commit/9b77698cf2ff64c6f6d198d05c2012ab7fa858be) fix: update blockdevice library to v2.0.2 * [`e46227ab9`](https://github.com/siderolabs/talos/commit/e46227ab95a6d06132e82315f55b5ced533ddabb) docs: fix kubespan name inconsistency * [`6b15ca19c`](https://github.com/siderolabs/talos/commit/6b15ca19cd1291b8a245d72d5153827945cad037) fix: audit and fix cgroup reservations * [`32b5d01ed`](https://github.com/siderolabs/talos/commit/32b5d01ed3396e8f54a245cc6d9818119aec8291) chore: bump lvm2 * [`6484581eb`](https://github.com/siderolabs/talos/commit/6484581eb888996a8dc829915439fb63606dd794) feat: allow /sbin/ldconfig in extensions * [`9fa08e843`](https://github.com/siderolabs/talos/commit/9fa08e843728dbd85ed7e0035f59cdd6232de9a9) chore: refactor tests * [`d8ab4981b`](https://github.com/siderolabs/talos/commit/d8ab4981b626ff41fbcdb526a032a5584519e3df) feat: support lvm auto activation * [`8166a58b3`](https://github.com/siderolabs/talos/commit/8166a58b364f760212b2a610ce0d764b8b4c5c46) fix: filter out non-printable characters in process line * [`806b6aaf5`](https://github.com/siderolabs/talos/commit/806b6aaf52f20ed0f32107b3d0372d6e3ff974be) docs: add SECURITY.md * [`7bd26df30`](https://github.com/siderolabs/talos/commit/7bd26df30803307e4eece3e382aafebc55e7b260) docs: document `/dev/net/tun` compatibility * [`18daedb51`](https://github.com/siderolabs/talos/commit/18daedb511e769717ba56eb05cccab72118a4813) fix: strategic merge patch delete for map keys * [`f3370529a`](https://github.com/siderolabs/talos/commit/f3370529ac042865a4b2d793465916fcae2d4b33) docs: correct typo * [`8d6884a8e`](https://github.com/siderolabs/talos/commit/8d6884a8e28e1bfa29f9a479e0f7179819cf70cd) test: add a test for inline machine config trusted roots * [`d4a6d017d`](https://github.com/siderolabs/talos/commit/d4a6d017dbb91e22c60787cdf64b242057b1ebef) fix: ignore invalid NTP responses * [`869f8379f`](https://github.com/siderolabs/talos/commit/869f8379f2317175901e8cb3deec4b800e7ab603) feat: update default Kubernetes version to 1.31.1 * [`780a1f198`](https://github.com/siderolabs/talos/commit/780a1f198a5eedd33a27060bdf116bd3a3b26426) fix: update CoreDNS health check * [`79cd03158`](https://github.com/siderolabs/talos/commit/79cd031588a0710b865414f919742ee3ffb998ed) chore: account for resource sorting in dns upstream resource * [`e17fafaca`](https://github.com/siderolabs/talos/commit/e17fafaca2a16990bc424b54120c49ddbaf8cee1) chore: drop `activateLogicalVolumes` sequencer step * [`a294b366f`](https://github.com/siderolabs/talos/commit/a294b366f24c6580d304c6c8ad34f481079dc795) fix: parse SideroLink API endpoint correctly * [`a9269ac7b`](https://github.com/siderolabs/talos/commit/a9269ac7b1217aa2d247c0215c5f2755af468b44) fix: remove extra logging on ethtool ioctl failures * [`5c6277d17`](https://github.com/siderolabs/talos/commit/5c6277d171eea58878ce4fcb4d2fdb7154333ae7) feat: update etcd to 3.5.16 * [`c1ed2984b`](https://github.com/siderolabs/talos/commit/c1ed2984b85dca791a5081c5da26bba75e3cd579) docs: add what's new for Talos 1.8

### Changes from siderolabs/crypto
1 commit

* [`58b2f92`](https://github.com/siderolabs/crypto/commit/58b2f9291c7e763a7210cfa681f88a7fa2230bf3) chore: use HTTP/2 ALPN by default

### Changes from siderolabs/discovery-client
1 commit

* [`b74fb90`](https://github.com/siderolabs/discovery-client/commit/b74fb9039fcfd8db9d6becf3044f9f41f387ea27) fix: allow custom TLS config for the client

### Changes from siderolabs/extras
2 commits

* [`eab6e58`](https://github.com/siderolabs/extras/commit/eab6e58aa9bdf49789cd4d64d2e27f61023421ca) feat: update dependencies * [`1459d78`](https://github.com/siderolabs/extras/commit/1459d78cbeb297c023501a3eb785a27a5bdd4933) feat: update pkgs for 1.9

### Changes from siderolabs/go-blockdevice
1 commit

* [`134c41b`](https://github.com/siderolabs/go-blockdevice/commit/134c41be6f4c498a149b8098fa8d862c5c47ca54) fix: fast wipe also last 1MB of the device

### Changes from siderolabs/go-circular
1 commit

* [`9a0f7b0`](https://github.com/siderolabs/go-circular/commit/9a0f7b02c80ad6c2d953b2d3dd388c56e89363ea) fix: multiple data race issues

### Changes from siderolabs/go-kubernetes
1 commit

* [`381f251`](https://github.com/siderolabs/go-kubernetes/commit/381f251662eaae9b48470ce00f504c2c64187612) feat: update for Kubernetes 1.32

### Changes from siderolabs/grpc-proxy
2 commits

* [`de1c628`](https://github.com/siderolabs/grpc-proxy/commit/de1c6286b7d16d8485bf8bb55c8783c8773851a0) fix: copy data from big frame msg * [`ef47ec7`](https://github.com/siderolabs/grpc-proxy/commit/ef47ec77d2a9f0f42e713d456943dfe9ee86a629) chore: upgrade Codec implementations and usages to Codec2

### Changes from siderolabs/pkgs
25 commits

* [`be92da0`](https://github.com/siderolabs/pkgs/commit/be92da09f3196d96b1358efd6a7c667297d3ecfb) feat: update Linux to 6.6.57, update Linux firmware * [`0b67a13`](https://github.com/siderolabs/pkgs/commit/0b67a133b12c548ba6d28f2ea0c979cb10512812) feat: bump dependencies * [`dd5f928`](https://github.com/siderolabs/pkgs/commit/dd5f928266761215fc402085594493c9f9b329b4) feat: update Linux 6.6.56 and protect /proc/mem * [`b1bf972`](https://github.com/siderolabs/pkgs/commit/b1bf9725068029f34193b3abe1586a3d1f542b17) feat: enable CONFIG_XFRM_STATISTICS * [`c63beae`](https://github.com/siderolabs/pkgs/commit/c63beae426026c8ef1b3228b8d978ca5fcc9111b) feat: update Linux to 6.6.54 * [`f474a55`](https://github.com/siderolabs/pkgs/commit/f474a55176dca7ab88b5a29f8d97ce6f31282abd) fix: libselinux: support running without /etc/selinux * [`ba0341e`](https://github.com/siderolabs/pkgs/commit/ba0341e39dafb3fe39b5efbc8a8e8d04df96a0e7) fix: systemd-udevd: search for config in /usr/etc * [`2b193f1`](https://github.com/siderolabs/pkgs/commit/2b193f14e035fa7d7785f26a591debe6ac357f00) feat: add lpfc kernel module * [`1adb946`](https://github.com/siderolabs/pkgs/commit/1adb946b1bb256b30b7bddd517a10d68ce209ada) feat: enable QEDF driver * [`dbbe3d0`](https://github.com/siderolabs/pkgs/commit/dbbe3d0116b24b9d1c2df19ae73b76714a37704e) feat: update containerd to v2.0.0-rc.5 * [`f19590e`](https://github.com/siderolabs/pkgs/commit/f19590edb42a0247d5d509066b21ce35bfc42b93) feat: update Go to 1.23.2 * [`e2a561f`](https://github.com/siderolabs/pkgs/commit/e2a561f576ea7dbc55ebb403d648daa1561c3101) fix: drop the LVM2 udev lvm rule * [`ae205aa`](https://github.com/siderolabs/pkgs/commit/ae205aac9d827783352071f9447f9f7cbf70da20) fix: force LVM to use `/run` as state directory * [`232a153`](https://github.com/siderolabs/pkgs/commit/232a15318a2d47f34b0772663fc3f417905b5406) feat: replace eudev with systemd-udevd * [`40fb82a`](https://github.com/siderolabs/pkgs/commit/40fb82a27a840f3442d6f52374007afb0a5a3770) feat: add libselinux, libsepol, pcre2 and libcap * [`6f40fbb`](https://github.com/siderolabs/pkgs/commit/6f40fbb5e00e449c954d54990085353d061a62c8) feat: update xfsprogs 6.10.1 * [`a1709c7`](https://github.com/siderolabs/pkgs/commit/a1709c76db4ba70de526d7eec18c6b0637ebf7b0) feat: enable module unloading and memory hotplug (for NVIDIA UVM) * [`2c5785b`](https://github.com/siderolabs/pkgs/commit/2c5785b1639a22317a1f7775f0d1f4bd0b0a4b88) feat: enable transparent huge pages in madvise mode * [`ca2e8c8`](https://github.com/siderolabs/pkgs/commit/ca2e8c84b0881e7d1e359ceaf3b55c3b4bb384e7) fix: lvm2 modprobe path * [`6b334a6`](https://github.com/siderolabs/pkgs/commit/6b334a68fbd988ca69d05142a639aa3bcfd16721) feat: update Linux to 6.6.52 * [`e90ae7e`](https://github.com/siderolabs/pkgs/commit/e90ae7ec316f1b9b4d15897f825d3c2c4cefde5e) feat: update Linux firmware to 20240909 * [`79a4f92`](https://github.com/siderolabs/pkgs/commit/79a4f92c5aa4b8288a927351209542c274724475) feat: enable INET_DIAG * [`c9f7eb9`](https://github.com/siderolabs/pkgs/commit/c9f7eb94de2a8df5cfc41c6ea90596832894dc89) feat: update Linux to 6.6.51 * [`126b6a4`](https://github.com/siderolabs/pkgs/commit/126b6a4f7632b2400139e306a0dbb0a545a0dda1) fix: add mpt3sas UBSAN patches * [`a09bf93`](https://github.com/siderolabs/pkgs/commit/a09bf93ce81bde59fcb06d662bc79effc9efaca6) chore: drop UBSAN patch

### Changes from siderolabs/proto-codec
3 commits

* [`0d84c65`](https://github.com/siderolabs/proto-codec/commit/0d84c652784543012f43f8c8d4358c160b27577e) chore: add support for gogo protobuf generator * [`19f8d2e`](https://github.com/siderolabs/proto-codec/commit/19f8d2e5840c19937c60cee0c681343ab658f678) chore: add kres * [`e038bb4`](https://github.com/siderolabs/proto-codec/commit/e038bb42f2be8b80ca09e46bb8704be06a413919) Initial commit

### Changes from siderolabs/siderolink
1 commit

* [`1893385`](https://github.com/siderolabs/siderolink/commit/1893385fe45bf110357a770d31b06f5d79403065) fix: initialize tls listener properly

### Changes from siderolabs/tools
5 commits

* [`2058296`](https://github.com/siderolabs/tools/commit/2058296cc223b683685f229a9a52de4db7171595) feat: bump dependencies * [`1151610`](https://github.com/siderolabs/tools/commit/1151610f5a5e70d07b715a2bdd76acd06d418595) feat: update Go to 1.23.2 * [`9f2189b`](https://github.com/siderolabs/tools/commit/9f2189b2b032ed283f38b20c53018b921fa06895) fix: bump gettext-tiny to the latest dev version * [`95069d6`](https://github.com/siderolabs/tools/commit/95069d6fd8fccde7ab93465e4e49a5a6ac5d4ed0) feat: update Go to 1.23.1 * [`eec0656`](https://github.com/siderolabs/tools/commit/eec0656aca652d0cc2e1973d5fab56bd4b54f64b) feat: replace gettext with gettext-tiny

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.5.0 -> v0.5.2 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.13.0 -> v1.15.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.7.0 -> v1.8.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.33 -> v1.28.0 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.13 -> v1.16.17 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.35.7 -> v1.37.2 * **github.com/aws/smithy-go** v1.20.4 -> v1.22.0 * **github.com/containerd/containerd/v2** v2.0.0-rc.4 -> v2.0.0-rc.5 * **github.com/containernetworking/plugins** v1.5.1 -> v1.6.0 * **github.com/cosi-project/runtime** v0.5.5 -> v0.6.4 * **github.com/docker/docker** v27.2.0 -> v27.3.1 * **github.com/gopacket/gopacket** v1.2.0 -> v1.3.0 * **github.com/klauspost/compress** v1.17.9 -> v1.17.11 * **github.com/mdlayher/ethtool** v0.1.0 -> v0.2.0 * **github.com/rivo/tview** fd649dbf1223 -> c5e4fb24af13 * **github.com/siderolabs/crypto** v0.4.4 -> v0.5.0 * **github.com/siderolabs/discovery-client** v0.1.9 -> v0.1.10 * **github.com/siderolabs/extras** v1.8.0 -> v1.9.0-alpha.0-1-geab6e58 * **github.com/siderolabs/go-blockdevice** v0.4.7 -> v0.4.8 * **github.com/siderolabs/go-blockdevice/v2** v2.0.2 -> v2.0.3 * **github.com/siderolabs/go-circular** v0.2.0 -> v0.2.1 * **github.com/siderolabs/go-kubernetes** v0.2.12 -> v0.2.13 * **github.com/siderolabs/grpc-proxy** v0.4.1 -> v0.5.1 * **github.com/siderolabs/pkgs** v1.8.0-8-gdf1a1a5 -> v1.9.0-alpha.0-24-gbe92da0 * **github.com/siderolabs/proto-codec** v0.1.1 **_new_** * **github.com/siderolabs/siderolink** v0.3.10 -> v0.3.11 * **github.com/siderolabs/talos/pkg/machinery** v1.8.0 -> v1.8.1 * **github.com/siderolabs/tools** v1.8.0-1-ga0c06c6 -> v1.9.0-alpha.0-4-g2058296 * **golang.org/x/net** v0.29.0 -> v0.30.0 * **golang.org/x/sys** v0.25.0 -> v0.26.0 * **golang.org/x/term** v0.24.0 -> v0.25.0 * **golang.org/x/text** v0.18.0 -> v0.19.0 * **golang.org/x/time** v0.6.0 -> v0.7.0 * **google.golang.org/grpc** v1.66.0 -> v1.67.1 * **google.golang.org/protobuf** v1.34.2 -> v1.35.1 Previous release can be found at [v1.8.0](https://github.com/siderolabs/talos/releases/tag/v1.8.0) ## [Talos 1.8.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.8.0-alpha.2) (2024-09-02) Welcome to the v1.8.0-alpha.2 release of Talos! *This is a pre-release of Talos* Starting with Talos v1.8.0, only standard assets would be published as github release assets. These include: * `cloud-images.json` * `talosctl` binaries * `kernel` * `initramfs` * `metal` iso and disk images * `talosctl-cni-bundle` All other release assets can be downloaded from [Image Factory](https://www.talos.dev/latest/talos-guides/install/boot-assets/#image-factory). Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Node Annotations Talos Linux now supports configuring Kubernetes node annotations via machine configuration (`.machine.nodeAnnotations`) in a way similar to node labels. ### Workload Apparmor Profile Talos Linux can now apply the default AppArmor profiles to all workloads started via containerd, if the machine is installed with the AppArmor LSM enforced via the extraKernelArgs. Eg: ```yaml machine: install: extraKernelArgs: - security=apparmor ``` ### Bridge Interface Talos Linux now support configuring 'vlan_filtering' for bridge interfaces. ### CNI Plugins Talos Linux now bundles by default the following standard CNI plugins: * `bridge` * `firewall` * `flannel` * `host-local` * `loopback` * `portmap` The Talos bundled Flannel manifest was simplified to remove the `install-cni` step. ### Diagnostics Talos Linux now shows diagnostics information for common problems related to misconfiguration via `talosctl health` and Talos dashboard. ### Extensions in Kubernetes Nodes Talos Linux now publishes list of installed extensions as Kubernetes node labels/annotations. The key format is `extensions.talos.dev/` and the value is the extension version. If the extension name is not valid as a label key, it will be skipped. If the extension version is a valid label value, it will be put to the label; otherwise it will be put to the annotation. For Talos machines booted of the Image Factory artifacts, this means that the schematic ID will be published as the annotation `extensions.talos.dev/schematic` (as it is longer than 63 characters). ### DNS Forwarding for CoreDNS pods Usage of the host DNS resolver as upstream for Kubernetes CoreDNS pods is now enabled by default. You can disable it with: ```yaml machine: features: hostDNS: enabled: true forwardKubeDNSToHost: false ``` Please note that on running cluster you will have to kill CoreDNS pods for this change to apply. The IP address used to forward DNS queries has changed to the fixed `169.254.116.108` address. For those upgrading from Talos 1.7 with `forwardKubeDNSToHost` enabled, the old Kubernetes service can be cleaned up with `kubectl delete -n kube-system service host-dns`. ### Installer Talos Linux installer now never wipes the system disk on upgrades, which means that the flag `--preserve` is always set for `talosctl upgrade`. ### `talos.halt_if_installed` kernel argument Starting with Talos 1.8, ISO's generated from Boot Assets would have a new kernel argument `talos.halt_if_installed` which would pause the boot sequence until boot timeout if Talos is already installed on the disk. ISO generated for pre 1.8 versions would not have this kernel argument. This can be also explicitly enabled by setting `talos.halt_if_installed=1` in kernel argument. ### Slim Kubelet Image Kubelet container image includes various utilities that kubelet might use to perform various tasks. Starting with Kubernetes 1.31.0, `kubelet` image now includes less utilities, as the in-tree CSI plugins were removed in Kubernetes 1.31.0. This reduces `kubelet` image size and potential attack surface. For Kubernetes < 1.31.0, there will be two images built: * `v1.x.y` (default, fat) * `v1.x.y-slim` (slim) For Kubernetes >= 1.31.0, there will be same two images built, but the default tag would point to slim image: * `v1.x.y` (default, slim) * `v1.x.y-fat` (fat) ### Default Node Labels Talos Linux on config generation now adds a label `node.kubernetes.io/exclude-from-external-load-balancers` by default for the control plane nodes. ### PCI Devices A list of PCI devices can now be obtained via `PCIDevices` resource, e.g. `talosctl get pcidevices`. ### Metal images Starting with Talos 1.8, `console=ttyS0` kernel argument is removed from the metal images and installer. If running virtualized in QEMU (For eg: Proxmox), this can be added as an extra kernel argument if needed via Image Factory or using Imager. This should fix slow boot or no console output issues on most bare metal hardware. ### NVIDIA GPU Support Starting with Talos 1.8.0, SideroLabs would ships extensions for both LTS and Production versions of NVIDIA extensions. For more details see the CHANGELOG of [extensions](https://github.com/siderolabs/extensions/releases). Upgrades with an exisiting schematic id from Image Factory would keep the existing LTS version of the NVIDIA extension. ### Platform Support Talos Linux now supports Apache CloudStack platform. ### kube-proxy Talos Linux configures kube-proxy >= v1.31.0 to use 'nftables' backend by default. ### Secure Boot Talos Linux now can optionally include well-known UEFI (Microsoft) SecureBoot keys into the auto-enrollment UEFI database. ### Custom Trusted Roots Talos Linux now supports adding [custom trusted roots](https://www.talos.dev/v1.8/talos-guides/configuration/certificate-authorities/) (CA certificates) via `TrustedRootsConfig` configuration documents. ### Device Extra Settle Timeout Talos Linux now supports a kernel command line argument `talos.device.settle_time=3m` to set the device extra settle timeout to workaround issues with broken drivers. ### Component Updates Kubernetes: 1.31.0 Linux: 6.6.47 containerd: 2.0.0-rc.4 runc: 1.2.0-rc.2 etcd: 3.5.15 Flannel: 0.25.6 Flannel CNI plugin: 1.5.1 CoreDNS: 1.1.13 Talos is built with Go 1.22.6. ### ZSTD Compression Talos Linux now compresses kernel and initramfs using ZSTD. Linux arm64 kernel is now compressed (previously it was uncompressed). ### Contributors * Andrey Smirnov * Dmitriy Matrenichev * Noel Georgi * Utku Ozdemir * Artem Chernyshev * Dmitry Sharshakov * Justin Garrison * Spencer Smith * Steve Francis * Bernard Gütermann * Jean-Francois Roy * Konrad Eriksson * Serge Logvinov * doctor_ew * Amadeus Mader * Andrew Rynhard * Anthony ARNAUD * Attila Oláh * Birger J. Nordølum * Caleb Woodbine * Claus Albøge * Daniel Höxtermann * David Birks * Dean * Dennis Marttinen * Eddie Zaneski * Enrique Hernández Bello * EricMa * Evan Johnson * Fabian Topfstedt * Fredrik Lundhag * George Gaál * Grzegorz Rozniecki * Grzegorz Rożniecki * Igor Rzegocki * Josia Scheytt * Judah Rand * Marcel Richter * Marco Franssen * Marcus Förster * Matthias Riegler * Matthieu Mottet * Maxime Brunet * Michael Trip * Mike Beaumont * Nick Meyer * Nicklas Frahm * Ole-Magnus Sæther * Roman Ivanov * Ron Olson * Saravanan G * Simon-Boyer * Skyler Mäntysaari * Steve Fan * Steve Martinelli * Steven Fackler * Syoc * Tim Jones * USBAkimbo * Will Bush * cryptk * darox * dhaines-quera * leppeK * looklose ### Changes
279 commits

* [`6f7c3a8e5`](https://github.com/siderolabs/talos/commit/6f7c3a8e5c6311bf1a2f9b1cbc6cd02d48746e02) fix: build of talosctl on non-Linux arches * [`f0a59cec7`](https://github.com/siderolabs/talos/commit/f0a59cec71739dd377082b0279684bb6ce46a0db) release(v1.8.0-alpha.2): prepare release * [`c8aed3be4`](https://github.com/siderolabs/talos/commit/c8aed3be4db9f4a510ddddb4c4baeff83432ee1f) fix: correctly add console args for ttyS0 * [`b453385bd`](https://github.com/siderolabs/talos/commit/b453385bd960cacc4baf43ff274a5c88e46d5f79) feat: support volume configuration, provisioning, etc * [`b6b16b35f`](https://github.com/siderolabs/talos/commit/b6b16b35fbccc861410f53bc29ad4cade962f1d6) chore: pause sequencer when talos installed and iso booted * [`eade0a9f2`](https://github.com/siderolabs/talos/commit/eade0a9f22f606f28241dbbcc92b93bea25aec6f) chore: bring in `uio` modules * [`81f9fcd9c`](https://github.com/siderolabs/talos/commit/81f9fcd9ce83c632dbbcbc1594605888d31e3ca3) fix: report errors correctly when pulling, fix EEXIST * [`b309e87b4`](https://github.com/siderolabs/talos/commit/b309e87b409fe5dd4a5579bee23879bb83bcb433) docs: fix invalid input in field user_data * [`c7474877a`](https://github.com/siderolabs/talos/commit/c7474877a46279a9f6330486a77b103c13216dae) docs: kubeProxyReplacement from "disabled" to "false" * [`be2ebf6b4`](https://github.com/siderolabs/talos/commit/be2ebf6b4d146d91cdfd7ba081d244775241bda8) chore: bump dependencies * [`88601bff4`](https://github.com/siderolabs/talos/commit/88601bff4e172841015761a5e74f01c5cb128069) chore: drop calico from interactive installer * [`106c17d0b`](https://github.com/siderolabs/talos/commit/106c17d0b5cdf3fa8f81f029e306cfd96f7ccfaf) chore: aarch64 qemu local secureboot support * [`da6263506`](https://github.com/siderolabs/talos/commit/da6263506ac772abe555e5937e2d21a517dc46cb) feat: update Flannel to v0.25.6 * [`19a44c2b0`](https://github.com/siderolabs/talos/commit/19a44c2b0bd4a4f4a9910c49bfdd9838f1a2bc54) chore: drop console `ttyS0` argument * [`75cecb421`](https://github.com/siderolabs/talos/commit/75cecb4210ad0d6ef201dafd307b4d023ccd7d39) feat: add Apache Cloudstack support * [`951cf66fd`](https://github.com/siderolabs/talos/commit/951cf66fdc6201186ec44276b818136f2f19b3d6) feat: add Cisco fnic driver * [`2d3bc94bf`](https://github.com/siderolabs/talos/commit/2d3bc94bf1840848bfe7e9f814a9b523132349c2) fix(ci): fix broken tests * [`a9551b7ca`](https://github.com/siderolabs/talos/commit/a9551b7caa413b03d4ed9b249b0cc957dd7a6edc) fix: host DNS access with firewall enabled * [`4834a61a8`](https://github.com/siderolabs/talos/commit/4834a61a8e4e67f4da3d14708dc7c699a8d3bc7c) feat: report SELinux labels * [`8fe39eacb`](https://github.com/siderolabs/talos/commit/8fe39eacba0db6d9372047172cf68825d57d0195) chore: move csi tests as go test * [`e4f8cb854`](https://github.com/siderolabs/talos/commit/e4f8cb854fc47daaba0ba969c52cc39329ae2ae0) fix: merge extension service config files by `mountPath` * [`5ba1df469`](https://github.com/siderolabs/talos/commit/5ba1df469542df0d1971a8f5fdd686a7d274dfa3) chore: add java package to protos * [`823480800`](https://github.com/siderolabs/talos/commit/823480800480babe4460d4d1a7f6e2f0ba3ab904) fix: add missing host/nvme-rdma * [`5b4b64979`](https://github.com/siderolabs/talos/commit/5b4b64979e4563e981064749c1b161f748fd4ff2) fix: bump go-smbios for broken SMIOS tables * [`f57d1f07e`](https://github.com/siderolabs/talos/commit/f57d1f07e9a690237eeaaadc6314d6da225ed625) fix: add NVMe target kernel modules * [`5ff6cf82c`](https://github.com/siderolabs/talos/commit/5ff6cf82ca593a7b701584dd76abdd09d96eb22e) fix: drop /opt mount for containers/tink * [`3c0db34d8`](https://github.com/siderolabs/talos/commit/3c0db34d8507571e49c0c49b6b615cfbe9cc5195) docs: update kubespan docs * [`3041d9075`](https://github.com/siderolabs/talos/commit/3041d90751fde279fc4ea28e149c1057e50a6947) fix: always handle `PermissionDenied` in dashboard resource watches * [`36f83eea9`](https://github.com/siderolabs/talos/commit/36f83eea9f6baba358c1d98223a330b2cb26e988) chore: make qemu check flag consistent with code * [`fe52cb074`](https://github.com/siderolabs/talos/commit/fe52cb0749e2d6aaaf9dbd3fb2c134b94792c425) chore: update protoc-gen-doc * [`ee4290f68`](https://github.com/siderolabs/talos/commit/ee4290f6849722af82db3f6a62039d9a3316f840) fix: bind HostDNS to 169.254.x link-local address * [`c312a46f6`](https://github.com/siderolabs/talos/commit/c312a46f69940cf96ce6c52d840f9fa00a01b87b) chore: restructure k8s component health checks * [`e193e7db9`](https://github.com/siderolabs/talos/commit/e193e7db98cfa9bbb689513751a7da39d8db9d14) docs: fix incorrect path for openebs in documentation * [`beadbac21`](https://github.com/siderolabs/talos/commit/beadbac210da8da391d52e13fc096b28a2c2538a) docs: update Oracle Cloud Talos custom image docs * [`6f969e364`](https://github.com/siderolabs/talos/commit/6f969e3645edc9ba561d23e02383f2331064f8eb) chore: improve `cluster create` UX on aarch64 * [`45cc8688a`](https://github.com/siderolabs/talos/commit/45cc8688a1c6a85665efb70ebf63ef7a3eb53213) chore: replace `if` blocks with `min`/`max` functions * [`a5bd770bf`](https://github.com/siderolabs/talos/commit/a5bd770bf923b7bf72759f6565e4dfd97e8d9bc6) fix: retry with another upstream if the previous failed * [`82e19f38a`](https://github.com/siderolabs/talos/commit/82e19f38ac276693610655fa7a8708bdd4521cc2) docs: add high-level overlay development guide * [`872599c9a`](https://github.com/siderolabs/talos/commit/872599c9a9ec9fbddd4820ba453ff29933525f14) chore: drop image assets from release * [`3c36c41a9`](https://github.com/siderolabs/talos/commit/3c36c41a91c95d9df3701b595a7b09285a390b71) feat: provide device extra settle timeout * [`9e348ef35`](https://github.com/siderolabs/talos/commit/9e348ef3501e95dc7c906c7d4d6df63f3c86715e) feat: update Kubernetes to 1.31.0 * [`61a1c946b`](https://github.com/siderolabs/talos/commit/61a1c946bff11b2fb9f85dfe826dfd890eac4986) feat: bundle (some) CNI plugins with Talos core * [`091da163b`](https://github.com/siderolabs/talos/commit/091da163b77db1014048a56cba1acbb6264711fb) chore: support arm64 kexec from zboot kernel images * [`73511c1ef`](https://github.com/siderolabs/talos/commit/73511c1ef3600c813835d7afd852fda4280e2323) chore: fix release notes * [`2bf924c7b`](https://github.com/siderolabs/talos/commit/2bf924c7be8869f8da869850f1df0e4d82651960) feat: update ISO VolumeID with Talos version * [`9a33dce10`](https://github.com/siderolabs/talos/commit/9a33dce10502aa05826adcc9cd9b66d9781111b3) docs: fix the VMWare docs * [`12562c2d5`](https://github.com/siderolabs/talos/commit/12562c2d5eb5a92b199018383bde6af58795dd28) docs: fix talos version in vmware.sh * [`ee67da14c`](https://github.com/siderolabs/talos/commit/ee67da14c5c8ae3bedfb2d8e321c9e127d61f565) feat: scaleway routed ip * [`eba5dafb9`](https://github.com/siderolabs/talos/commit/eba5dafb9eba450863fb295a4215559f32576666) fix: add dns-resolve-cache to the support bundle * [`d4f8100bd`](https://github.com/siderolabs/talos/commit/d4f8100bd4fc7d4e14a070c3eff600a259684d9a) docs: fix default openebs folder * [`60e163d54`](https://github.com/siderolabs/talos/commit/60e163d545392d17639809980d3041ec6fd9af09) docs: fix typo in doc * [`98d9abdd0`](https://github.com/siderolabs/talos/commit/98d9abdd0eaef72c8964fc58551670a0ec78783c) chore(ci): fix cilium ci tests * [`beb9602e3`](https://github.com/siderolabs/talos/commit/beb9602e35cff1ff072d60c86e1bc3faa6f8c002) chore: bump github.com/docker/docker to v27.1.1+incompatible * [`0698a4921`](https://github.com/siderolabs/talos/commit/0698a4921ba29bd1088f89406dfc89744a47e175) docs: aws getting started re-write * [`4d7d7a589`](https://github.com/siderolabs/talos/commit/4d7d7a58955468b7bbe42bacd8f53c782d12e074) chore(ci): update nvidia integration tests * [`60e901c1d`](https://github.com/siderolabs/talos/commit/60e901c1dcfdd728c7497a3c0d0ae28e0adb0580) chore: document slim kubelet image * [`622d66a98`](https://github.com/siderolabs/talos/commit/622d66a98f4d4eb809ff8dcdb67563e1c6be9b68) chore: bump deps * [`f9f5e0ef5`](https://github.com/siderolabs/talos/commit/f9f5e0ef556c575acc1cab85fafc0d89a1a4b4cc) chore: fix k8s tests * [`2ac8d2274`](https://github.com/siderolabs/talos/commit/2ac8d2274fcc5c9fc398575da2ddabb36984455a) chore: support `unsupported` flag for mkfs * [`9b9159d1e`](https://github.com/siderolabs/talos/commit/9b9159d1e04d337dc3a51e41be57f4795e71255d) docs: update support matrix for nvidia drivers * [`9d3415850`](https://github.com/siderolabs/talos/commit/9d34158500a155a7065e259d68f588112c5834ea) fix: fix graph diffs in dashboard when node aliases are used * [`9a126d70e`](https://github.com/siderolabs/talos/commit/9a126d70e0adab35a028f219b872cfc90e8d70d6) chore: generate deepcopy for SecureBootAssets type * [`dff56d824`](https://github.com/siderolabs/talos/commit/dff56d8246a481b163e1f49477efef324a106334) chore: remove arch-specific etcd image tag * [`c9f1dece5`](https://github.com/siderolabs/talos/commit/c9f1dece5d967e210b699234d365c27b5c397788) feat: update Kubernetes to 1.31.0-rc.1 * [`49831c56f`](https://github.com/siderolabs/talos/commit/49831c56fb10506bb0ea2546b1b09d924571fc6d) docs: replace removed Cilium/kubeProxyReplacement value * [`33a316369`](https://github.com/siderolabs/talos/commit/33a3163698084da3c43a5ea41c6600ab883b2ec9) docs: update aws.md for loop * [`e02bd2093`](https://github.com/siderolabs/talos/commit/e02bd20933b300f3b89ab9e9f385e23a0946eec8) feat: update Kubernetes to 1.31.0-rc.0 * [`64914b086`](https://github.com/siderolabs/talos/commit/64914b086ca0d72720c2f416b4543a1ba250986e) chore: add test for crun extension * [`7a1c62b8b`](https://github.com/siderolabs/talos/commit/7a1c62b8bc63f10dbad7673c59b6f62a6c9497bd) feat: publish installed extensions as node labels/annotations * [`3f2058aba`](https://github.com/siderolabs/talos/commit/3f2058aba29c1e30c9daaadea54b0035811ce318) fix: update containerd configuration and settings * [`81bd20f5a`](https://github.com/siderolabs/talos/commit/81bd20f5ad007a5f9c464a2ec7f6ad863f1c7fa8) docs: remove deprecated jiva from openebs instructions * [`480ffb88a`](https://github.com/siderolabs/talos/commit/480ffb88aed33214f23d21c31130a63f7b66dafc) docs: fix the amd64 PXE boot script URL * [`20fe34dbd`](https://github.com/siderolabs/talos/commit/20fe34dbde2613ed2e95378c3ff637a62bc015e5) docs: fix docker getting started typo * [`0fd7dfd2a`](https://github.com/siderolabs/talos/commit/0fd7dfd2ae1d74a8d4ea9d7f130018e972fe6674) docs: update Equinix Guide * [`3d1474ac0`](https://github.com/siderolabs/talos/commit/3d1474ac0bb4df3184423a7dfa4f4d981799ac41) feat: update CoreDNS to 1.1.3 * [`50e5f37ef`](https://github.com/siderolabs/talos/commit/50e5f37efb99ac2df2c58f9f5a248350eea1b594) chore: add test for apparmor * [`96492c097`](https://github.com/siderolabs/talos/commit/96492c0977e3a292336eb84d4e14563921896cb2) docs: extend multus configuration for Cilium * [`19aa44c54`](https://github.com/siderolabs/talos/commit/19aa44c54975f9f4d6c92b86c4dfb95a75d1adb0) fix: generate kubeconfig using proper types * [`240104e45`](https://github.com/siderolabs/talos/commit/240104e45fae2d8f80a3a229648a80b19f4dcbd0) feat: update Linux to 6.6.43 * [`32db8db60`](https://github.com/siderolabs/talos/commit/32db8db606773daf2d75d261387e591da8477ef1) chore: lock microsoft secureboot certs * [`3ce5492f8`](https://github.com/siderolabs/talos/commit/3ce5492f852c4e4e07d02c9a93f0b0fffcb00184) feat: runc memfd-bind service * [`341b55cd3`](https://github.com/siderolabs/talos/commit/341b55cd37d2225b163d92aa920965a7bca5d0a4) docs: update vmware.sh * [`117628aa6`](https://github.com/siderolabs/talos/commit/117628aa60c16e5b7a4102b71965cb0e77f95279) chore: add test for gvisor extension with platform kvm * [`fd01571c4`](https://github.com/siderolabs/talos/commit/fd01571c4037513fdb6287a8769dfbe46e9ed4b9) feat: update Linux, enable Broadcom MPI3 driver * [`b333ec07d`](https://github.com/siderolabs/talos/commit/b333ec07d96a27c721c07fd5c3ac29daec58690c) feat: update etcd to 3.5.15, Flannel to 0.25.5 * [`087290178`](https://github.com/siderolabs/talos/commit/0872901783785239920d4f484a2ab1e224f84b6f) feat: use ethtool ioctl to get link status when netlink api not available * [`395c64290`](https://github.com/siderolabs/talos/commit/395c642909765da17ed44771a08290c15a8b052c) docs: update openebs-jiva helm repo * [`f132d3f40`](https://github.com/siderolabs/talos/commit/f132d3f40320904d3a420ca94b8f95718075c251) chore(ci): remove artifacts directory prefix for checksums * [`fd54dc191`](https://github.com/siderolabs/talos/commit/fd54dc191d06305d7b5fbfe71cd937e7f95d4f10) feat(talosctl): append microsoft secure boot certs * [`fd6ddd11e`](https://github.com/siderolabs/talos/commit/fd6ddd11ef810f92190fe0d7490f2314ce21d595) feat: provide POD_IP env var to scheduler and controller-manager * [`407347a7a`](https://github.com/siderolabs/talos/commit/407347a7a0a955d2ea610ca06ebab4593ff0c03c) feat: update Kubernetes to 1.31.0-beta.0 * [`1b8c9ccbb`](https://github.com/siderolabs/talos/commit/1b8c9ccbb0285b678466f2b8eb7e5931bc8d44e4) fix: enforce secureboot enroll option only for supported releases * [`d52b89cb9`](https://github.com/siderolabs/talos/commit/d52b89cb91be238da08dd50d0cdd2ee50d93ed45) chore: ensure tls required on s3 buckets * [`c288ace7b`](https://github.com/siderolabs/talos/commit/c288ace7b185cd3fad569c0848afbda7217ac269) fix: be more smart when merging DNS resolver config * [`d983e4430`](https://github.com/siderolabs/talos/commit/d983e44308b677b07d2d135f0e73349cfb7e0ca8) fix: panic on shutdown * [`01404edff`](https://github.com/siderolabs/talos/commit/01404edff970888c968ff1b77d7dbd76cb724094) chore: reduce memory requirement for contrplane nodes * [`980f9ebc0`](https://github.com/siderolabs/talos/commit/980f9ebc07256280c74c6da8d473b49d0739a420) fix: fix log format in cluster provisioning * [`ea626a963`](https://github.com/siderolabs/talos/commit/ea626a96313dc8b56bd6256e0aae4b3a6c69f5be) feat: add label 'exclude-from-external-load-balancers' for cp nodes * [`1cf76cfbc`](https://github.com/siderolabs/talos/commit/1cf76cfbc28af980665e57d756c2e3ac002f5d8e) docs: fix talosctl spelling * [`b07338f54`](https://github.com/siderolabs/talos/commit/b07338f5471363457da94286cae6ef8075561aa2) feat: provide machine config document to update trusted CA roots * [`f14c4795e`](https://github.com/siderolabs/talos/commit/f14c4795e5e60bf564d584a707e261bed78bcaf8) fix: sort ports and merge adjacent ones in the nft rule * [`cf5effabb`](https://github.com/siderolabs/talos/commit/cf5effabb209fb570f59ba305bdab0b6409c7b93) feat: provide an option to enforce SecureBoot for TPM enrollment * [`736c1485e`](https://github.com/siderolabs/talos/commit/736c1485e27a597b8bf720b2dba4f8664cb9321a) fix: change the UEFI firmware search path order * [`a727a1d97`](https://github.com/siderolabs/talos/commit/a727a1d97a22001eb8b1ef3f9f22fc39a653ad09) chore: make using action tracker easier * [`0aebeff35`](https://github.com/siderolabs/talos/commit/0aebeff3560e276fb7ee984b5362b80ad5873c0f) docs: add missing backslashes * [`398151e64`](https://github.com/siderolabs/talos/commit/398151e64fb6490a8dc3e828fcc8a191857e41d4) fix: remove host bind mount for `/tmp` for trustd * [`ce4c404e1`](https://github.com/siderolabs/talos/commit/ce4c404e144deffe8b6a52488453c157f23497dd) chore: redo FilterMessages as generic function * [`fbde9c556`](https://github.com/siderolabs/talos/commit/fbde9c556f0107734ff1216ea80d9156c35d4e3c) chore: bump deps * [`3bab15214`](https://github.com/siderolabs/talos/commit/3bab15214de985b7738250f2a6d84a796c5e9253) feat: update Kubernetes to 1.31.0-alpha.3 * [`c2a5213ee`](https://github.com/siderolabs/talos/commit/c2a5213eefa6dc977ded541316c96f516ea2ecfb) docs: add note about mayastor nvme_tcp init container check * [`dad9c40c7`](https://github.com/siderolabs/talos/commit/dad9c40c736d55dee05d4b74e94db610dd119ce2) chore: simplify code * [`963612bcc`](https://github.com/siderolabs/talos/commit/963612bccaead87d5bbb4b79014d5f9821eeb95e) chore: redo EncodeString and EncodeBytes using buffer interface * [`d9db360ab`](https://github.com/siderolabs/talos/commit/d9db360ab47b24dd5bccf3a36c938e5e648ff095) fix: properly output multi-doc machine config in `get mc` * [`31af6b3f8`](https://github.com/siderolabs/talos/commit/31af6b3f8cc11ae0336c6e7d65a460aff4a71a1f) chore: fix the release step to include CNI bundle * [`d7cd46643`](https://github.com/siderolabs/talos/commit/d7cd46643dc4461891af883fc86d2faff321855f) chore: fix the push/tag steps * [`c9aeeca3d`](https://github.com/siderolabs/talos/commit/c9aeeca3d47fb235cd013e10da55c296e532c1c3) chore: fix the Makefile * [`48cdbe0de`](https://github.com/siderolabs/talos/commit/48cdbe0de78041f97ca433ce7c8975ec56e262f2) release(v1.8.0-alpha.1): prepare release * [`2512ef435`](https://github.com/siderolabs/talos/commit/2512ef435f0bfb1ffcf7da12c57d7812d9ea207c) test: fix the integrtion tests for apply-config * [`076f3c4f2`](https://github.com/siderolabs/talos/commit/076f3c4f20006f732fa07ada14f45458dc65a9e8) chore: improve link spec controller code * [`0454130ad`](https://github.com/siderolabs/talos/commit/0454130ad97a61624fb0b916bf14a51dce8f199d) feat: suppress controller runtime first N failures on the console * [`3d35e5468`](https://github.com/siderolabs/talos/commit/3d35e54683b4930fa716c7afe6ecbad2af2f700b) chore: update hydrophone library * [`1f28726d4`](https://github.com/siderolabs/talos/commit/1f28726d46953262f33c91082528cd190f53b143) chore: support version with and without `v` prefix * [`9a56b8527`](https://github.com/siderolabs/talos/commit/9a56b8527b81c9653f5d01386c66ec1bde5d730a) chore(ci): fix parallel runs of tf pipelines * [`be35f380c`](https://github.com/siderolabs/talos/commit/be35f380ccf09d7667c3221765d6927546cffbca) chore: update pkgs/tools/extras * [`93df23444`](https://github.com/siderolabs/talos/commit/93df2344451e8f370f7f1d0f9590f65d6b02b936) docs: update opengraph image for main landing pages * [`d9d62d4da`](https://github.com/siderolabs/talos/commit/d9d62d4da6e30ac8f97a06dafd362a9e2ddc7006) feat: update Linux to 6.6.36 * [`6b0fe5b8c`](https://github.com/siderolabs/talos/commit/6b0fe5b8ca9aa11d195b4b66608ad179bca7be44) docs: update deploying cilium docs for v1.7 and v1.8 * [`52611a90d`](https://github.com/siderolabs/talos/commit/52611a90d870a131084375015d4d7270fa32cde8) feat: update Kubernetes to v1.30.2 * [`c19cc4ccb`](https://github.com/siderolabs/talos/commit/c19cc4ccbc8c37b6dde49853dfc442a0f5404ab4) docs: clarify direct access needed to nodes in insecure mode * [`b4c871e4b`](https://github.com/siderolabs/talos/commit/b4c871e4b74014553ab81f7ff593ff7fa736df2d) chore: bump dependencies * [`cc345c8c9`](https://github.com/siderolabs/talos/commit/cc345c8c9413692148360684390c910de9e94748) feat: add support for configuring vlan filtering on the bridge * [`2d054ad35`](https://github.com/siderolabs/talos/commit/2d054ad3551428d8b3d93c8356b38aec7e9225eb) chore: handle documents diff in `apply-config` dry run * [`bd34f71f3`](https://github.com/siderolabs/talos/commit/bd34f71f3e5eae34907951a6480e0559736bfd72) feat: add apparmor pkg * [`71857fd4d`](https://github.com/siderolabs/talos/commit/71857fd4d3a262a6b41cad3af7d3abb7355d8509) docs: fix typo: `messure` -> `measure` * [`f75f16b0a`](https://github.com/siderolabs/talos/commit/f75f16b0a8088ac47a47c9ebabdf4803db5a397e) chore(ci): fix cluster name generation * [`c603d2bf9`](https://github.com/siderolabs/talos/commit/c603d2bf9552ed169e5baf012ad44305a54056a4) chore: output more info when `ExecuteCommandInPod` fails * [`4b5a7445e`](https://github.com/siderolabs/talos/commit/4b5a7445e9c3f7f2f53e958f6c2e91a1a86c2641) docs: fix missing Akamai platform in supported matrix * [`4701498a1`](https://github.com/siderolabs/talos/commit/4701498a1b5a213816962fb1acb56192423f525f) chore(ci): run e2e-aws-nvidia with zfs extension enabled * [`86a3222ae`](https://github.com/siderolabs/talos/commit/86a3222aeecb895cab233a0cd2474189f79a6f12) chore: use new disks api for iscsi tests * [`5ffc3f14b`](https://github.com/siderolabs/talos/commit/5ffc3f14bd2b49a2ee09f36fe9e66bcf7b5283e8) feat: show siderolink status on dashboard * [`6f6a5d105`](https://github.com/siderolabs/talos/commit/6f6a5d10573028662448a57c66c2255bb7703319) chore: upgrade to rtnetlink/v2 library * [`1fb8453c2`](https://github.com/siderolabs/talos/commit/1fb8453c2db1659dd6c1670e4174125b26e777c5) chore: update Go modules * [`8e15621e8`](https://github.com/siderolabs/talos/commit/8e15621e83a1005c3b7d8d682652f984765996c1) chore(ci): add conformance pipelines * [`7fcb521a6`](https://github.com/siderolabs/talos/commit/7fcb521a6a2d14de02926489d7297cf9429c7b38) feat: use hydrophone instead of sonobuoy * [`d1a0c1f98`](https://github.com/siderolabs/talos/commit/d1a0c1f983281593b4e6a71e2110ae9f81890edc) test: fix the integration test for no META name * [`535006334`](https://github.com/siderolabs/talos/commit/5350063340a80b99a8866afb94ac8673dd4e7ace) chore: fix our dns server implementation * [`c6f90d014`](https://github.com/siderolabs/talos/commit/c6f90d01493454bcf3281c9532b61fcb7e3dbb24) chore: replace sync.Map with concurrent.HashTrieMap * [`e8ced2c2d`](https://github.com/siderolabs/talos/commit/e8ced2c2ddc9e3f61138dd566628f7d11cf90c76) chore: drop k8s timeout in the default kubeconfig * [`7cbdce73f`](https://github.com/siderolabs/talos/commit/7cbdce73f74351954e506303ed9964b9668a3b40) fix: detect CD devices, fix user disks wipe test * [`aca475c66`](https://github.com/siderolabs/talos/commit/aca475c66509fa1fa7e7a0ca1b2a29f6542637fc) chore: small usability fixes * [`26cf566dc`](https://github.com/siderolabs/talos/commit/26cf566dc8c53263cbaae72855995e418da0852b) chore: bump our coredns fork * [`5e66e117e`](https://github.com/siderolabs/talos/commit/5e66e117e2ec19527fe949bf2d689df90835d63f) fix: initial assignment of Hetzner Cloud Alias IP * [`f07b79f4a`](https://github.com/siderolabs/talos/commit/f07b79f4a8c647d358b8cd41b3704eccf0341d33) feat: provide disk detection based on new blockdevices * [`8ee087268`](https://github.com/siderolabs/talos/commit/8ee087268317a73dc240c2b7569c2dab8d9df142) chore(ci): drop crashdump, save logs as artifacts * [`7c9a14383`](https://github.com/siderolabs/talos/commit/7c9a14383ee034b05cb9bd1ff49f8078cbbf5e66) fix: volume discovery improvements * [`80ca8ff71`](https://github.com/siderolabs/talos/commit/80ca8ff7135b0950b83d2ceaa32ee1eacce049e0) fix: update the cgroups for Talos core services * [`fe317f1e1`](https://github.com/siderolabs/talos/commit/fe317f1e1611d2f48595bfaf67c5e4ea3cd692e3) docs: fix typo in QEMU guest agent support on Proxmox * [`8dbe2128a`](https://github.com/siderolabs/talos/commit/8dbe2128a909a38ead8b6dfe1cc99e1ae36078d2) feat: implement Talos diagnostics * [`357d7754f`](https://github.com/siderolabs/talos/commit/357d7754fd739e9e875d17e0f8e63c333553090e) fix: clean up VM runners on cluster destroy * [`41f92e0ba`](https://github.com/siderolabs/talos/commit/41f92e0ba46b8ad9ddc3a4eabe86be915dea6b8e) chore: update Go to 1.22.4, other updates * [`4621e9bb7`](https://github.com/siderolabs/talos/commit/4621e9bb770e2a45c7c1ea8da76cbdabf76a4671) chore: add stale and lock issue workflows * [`82d9cd322`](https://github.com/siderolabs/talos/commit/82d9cd32298431760aef67f553924e4b4f48e207) fix: add upgrade errata for arm64/zboot kernels * [`9a23d846c`](https://github.com/siderolabs/talos/commit/9a23d846c1f6a88c30ffe55d2bf5a21d6cee150e) fix: downgrade Azure IMDS required version * [`30860210c`](https://github.com/siderolabs/talos/commit/30860210cce628839e97b8ece7edf90300556ed7) test: fix hardware test not to require PCI devices * [`9fcc9b841`](https://github.com/siderolabs/talos/commit/9fcc9b84152cb186324c13e317575f6da8b7bfa6) feat: update Flannel to v0.25.3 * [`9d395b9de`](https://github.com/siderolabs/talos/commit/9d395b9de94f28fb9bf56bf795f916f783a847a0) chore: use bun instead of npm * [`a1684bdf8`](https://github.com/siderolabs/talos/commit/a1684bdf8f24858942cf61bee1efc81f7ef76f85) chore: speed up go generate for enumer * [`4dd0aa712`](https://github.com/siderolabs/talos/commit/4dd0aa7120b52cab5de219010f2b78b7dd9b73ce) feat: implement PCI device bus enumeration * [`b0466e0ab`](https://github.com/siderolabs/talos/commit/b0466e0abf2f8af43f3fb6c9661f44000fe1d54b) fix: disable kexec on GCP/Azure * [`911c25574`](https://github.com/siderolabs/talos/commit/911c255742d02440806e5f3df6967c091bb5288e) chore: fix go.work resolution * [`2f088ede0`](https://github.com/siderolabs/talos/commit/2f088ede0952d72dbb7bf33dd0510cb8ff8b8e3a) docs: add another example for installing cilium * [`3967e0777`](https://github.com/siderolabs/talos/commit/3967e07777707fa8af339f46596b678e1eaaa9f2) feat: update etcd to 3.5.14 * [`3367ded9f`](https://github.com/siderolabs/talos/commit/3367ded9feac84e9c6c1f3efcea9e61f3083b4ac) fix: correct time adjustment in `time.SyncController` * [`893e64fcb`](https://github.com/siderolabs/talos/commit/893e64fcb1f09efed990b9b642359d7bcabffd42) fix: replace `nslookup` with `dig` in integration tests * [`0359c8537`](https://github.com/siderolabs/talos/commit/0359c8537c1b3b01e94394604e16fd817b986f9e) chore: unify toml packages being used * [`4feb94ca0`](https://github.com/siderolabs/talos/commit/4feb94ca099746e3a90106522b920a77cfe77ce0) feat: add multidoc check to the Talos quirks module * [`0b4a9777f`](https://github.com/siderolabs/talos/commit/0b4a9777fc2ddcc61430db23837455ff383ba1a3) docs: update talosctl install instructions for 1.8 * [`da8305ffb`](https://github.com/siderolabs/talos/commit/da8305ffb46d285662bca12ec02760d6121342c8) test: add a test for watchdog timers * [`da7f27640`](https://github.com/siderolabs/talos/commit/da7f2764092b883bcdf5daf81b8f6f7ef997ac0a) fix: mount `tracefs` filesystem * [`7b37e5b63`](https://github.com/siderolabs/talos/commit/7b37e5b63d54c2d197336e4fbee941fa5f2423c0) chore(ci): fix integration extensions * [`de7553d77`](https://github.com/siderolabs/talos/commit/de7553d77f7e02a83f764820a71badbf0d851bc9) fix(ci): cron jobs * [`eb510d9fd`](https://github.com/siderolabs/talos/commit/eb510d9fdf3a40b2ae881e3dd19a94058d4ef529) chore: require enabled bootloader for docker provisioner * [`a9cf9b789`](https://github.com/siderolabs/talos/commit/a9cf9b78921bef76b66aa5fa5940977767124bfe) fix: correctly handle dns messages in our dns implementation * [`c2b19dcb9`](https://github.com/siderolabs/talos/commit/c2b19dcb978ab015bd9b3c5a4eb47a53ee25e297) chore: move to containerd 2.0 API * [`92a274e9a`](https://github.com/siderolabs/talos/commit/92a274e9a0a83b3e240784bf12817f08559ac8e8) fix: workaround problems with udevd races * [`31b24ea3d`](https://github.com/siderolabs/talos/commit/31b24ea3d70f88d031d81bd0f914754b0cee411e) chore(ci): split integration misc * [`8a1371337`](https://github.com/siderolabs/talos/commit/8a1371337faea406c9193e91c8de8ffc056b5135) fix: produce stable order of bonds with equinix * [`6406193f4`](https://github.com/siderolabs/talos/commit/6406193f4637157c3d31219dc2c39aca7fa736a4) test: add Equnix Metal sample metadata with two bonds * [`01ea82053`](https://github.com/siderolabs/talos/commit/01ea82053e0a2ffe4193243e235aae2ade0e2d88) fix: time sync over NTP from future era * [`5aea42427`](https://github.com/siderolabs/talos/commit/5aea4242782d4ff00ba51e85422fbdf7c2ceca64) fix(ci): fix crons by setting up buildx always * [`84706c3e2`](https://github.com/siderolabs/talos/commit/84706c3e2920b9bf68c7b6dcfb73f1e16f3f656b) docs: default to brew docs for talosctl * [`fcd65ff65`](https://github.com/siderolabs/talos/commit/fcd65ff65ce78aa5ebe7ca4b12aea2571bd54c49) feat: enable forwardKubeDNSToHost by default * [`2e64e9e4e`](https://github.com/siderolabs/talos/commit/2e64e9e4e026817f844765b4c8a7d346d85bf983) fix: require accepted CAs on worker nodes * [`23c1c4560`](https://github.com/siderolabs/talos/commit/23c1c4560ecd2084e505a64b0b701707aa79c5e6) fix(ci): fix crons fby rekres * [`2d50392c5`](https://github.com/siderolabs/talos/commit/2d50392c5a16a97a2daa47edcfd362b0891c4a06) feat: update containerd to 2.0.0-rc.2, runc to 1.2.0-rc.1 * [`a12e4bb24`](https://github.com/siderolabs/talos/commit/a12e4bb24e19701e926103753ec3ee0f98e8d3a2) chore(ci): fix github action crons * [`e7bd9cd2b`](https://github.com/siderolabs/talos/commit/e7bd9cd2bbbd337ef72adc2a3be5adc8b530cd6e) fix: decrease maximum negative ttl for dns responses * [`9c3ebad9f`](https://github.com/siderolabs/talos/commit/9c3ebad9fd7a62418fc6748364a23d27ff1c3ff7) chore(ci): kresify gh actions * [`ff60f6fde`](https://github.com/siderolabs/talos/commit/ff60f6fde6cb325b9f1f4801f658f4e9554c6c2b) refactor: make some of the extensions package public * [`ce8c86d64`](https://github.com/siderolabs/talos/commit/ce8c86d640949d24107d9057358b39c860fc1e70) fix: panic in osroot controller * [`e1711cd3c`](https://github.com/siderolabs/talos/commit/e1711cd3c9852137956f1cce7174b0a337d53b63) chore: stop using containerd package for cri namespace * [`d4307043f`](https://github.com/siderolabs/talos/commit/d4307043ffbfcadb5b67b12c95816c2a3a5819c3) fix: update go-tail library to fix 'short read' error * [`7cd13ef4a`](https://github.com/siderolabs/talos/commit/7cd13ef4a619fa5c13dc9ed147e6626ddcabbaf2) docs: add documentation on using Multus with Talos * [`4784da3ef`](https://github.com/siderolabs/talos/commit/4784da3ef88745d1ce38f1e49239c882c081e6fb) feat: use new circular buffer compressed chunks feature * [`78b48eb3a`](https://github.com/siderolabs/talos/commit/78b48eb3ae78ec9953104247ec73cafa26a61264) feat: include EDAC drivers * [`0bf2d69fb`](https://github.com/siderolabs/talos/commit/0bf2d69fbb2f2c1f693565243b46391da00d4dba) feat: update Kubernetes to 1.30.1 * [`53f548913`](https://github.com/siderolabs/talos/commit/53f54891302b193bf35ede52af235457396e91ce) fix: increase host dns packet ttl for pods * [`dedb6d360`](https://github.com/siderolabs/talos/commit/dedb6d360d25e6d00d560ddb40563c2a5a95bb1f) fix: update github.com/siderolabs/siderolink to v0.3.7 * [`43939f1a6`](https://github.com/siderolabs/talos/commit/43939f1a6e4b65cf9b64d1d09dc19df709a41275) docs: fix typos, add docker socket info * [`6663068bb`](https://github.com/siderolabs/talos/commit/6663068bbd1750fd57ddf9ca63b0f305d895b33b) chore: update project in GCP testing * [`b86edc677`](https://github.com/siderolabs/talos/commit/b86edc6776f77a65d3a254cf0f0d713ce7a9145e) chore: update office hours in talos repo * [`cfa25d22d`](https://github.com/siderolabs/talos/commit/cfa25d22dc30b877ea47ba1bfae3ca5f29977f1b) chore: remove docs prior to 1.0 from website navigation * [`120705459`](https://github.com/siderolabs/talos/commit/12070545996af3435454654500cd75a50111cca9) chore: handle I/O error for xfs_repair * [`b7afe2669`](https://github.com/siderolabs/talos/commit/b7afe2669b2a9a32ca37bbcc7a7e8af4879cf403) feat: update Linux 6.6.30 * [`26519ceed`](https://github.com/siderolabs/talos/commit/26519ceed0c790abd851de310409baf6af89e2b7) docs: update proxmox.md * [`851b91a0e`](https://github.com/siderolabs/talos/commit/851b91a0e22055443eabace9b89a566e0cbec679) fix: don't enable hostDNS for versions of Talos which do not have it * [`42ac5cd0c`](https://github.com/siderolabs/talos/commit/42ac5cd0c2ef610f055afb208384e60fc9389e82) fix: check for `nil` machine config during installation * [`1d29111d4`](https://github.com/siderolabs/talos/commit/1d29111d4310cc16078248e66817843e6e740821) chore: update Go to 1.22.3 * [`f4d7b9d9a`](https://github.com/siderolabs/talos/commit/f4d7b9d9a921cdaf33b9efdae1569dd921628270) feat: gather plaform dns names * [`0b0f9995a`](https://github.com/siderolabs/talos/commit/0b0f9995a6cd2b41f48dc867f4e0248284e53463) docs: add resource information, some grammar fixes * [`763dae250`](https://github.com/siderolabs/talos/commit/763dae2508242ee91a7e38e5962facb334691289) fix: add cluster name to the worker machine config * [`4aac5b4ec`](https://github.com/siderolabs/talos/commit/4aac5b4ec30f4a9ee0f2e4a4239b399357930b6c) feat: mount /sys/kernel/security into kubelet * [`817f18153`](https://github.com/siderolabs/talos/commit/817f18153f592f5bf38884f05aed2e4ce2fd3ad7) docs: remove mention of enabling KubePrism after v1.6 * [`c08d79732`](https://github.com/siderolabs/talos/commit/c08d797326686434dc035de3ca40200293d74701) docs: fix the variable name typo * [`478b862b4`](https://github.com/siderolabs/talos/commit/478b862b4c38bd5a5ba1313a3779f9395e4ba38d) fix: do not fail cli action tracker when boot id cannot be read * [`be510f9eb`](https://github.com/siderolabs/talos/commit/be510f9eb2b84a88ce730fab36bf575c976efa8b) docs: fix grpc_tunnel value to true * [`b7b8a8d8f`](https://github.com/siderolabs/talos/commit/b7b8a8d8fa6335d3f0036c50792971adefe5e240) docs: add logs example for the certificate errors troubleshooting * [`8df5b85ec`](https://github.com/siderolabs/talos/commit/8df5b85ec7e8ca53fd73c9c095ee5c453d5c4e51) release(v1.8.0-alpha.0): prepare release * [`07f78182c`](https://github.com/siderolabs/talos/commit/07f78182c621296e6c694b64ead8f14695b2e3b7) fix: use a fresh context for etcd unlock * [`84cd7dbec`](https://github.com/siderolabs/talos/commit/84cd7dbec4ce01a8f80a855267e1c44dfc6dcacc) feat: update Linux to 6.6.29 * [`70fdca6a4`](https://github.com/siderolabs/talos/commit/70fdca6a43abcb48030239047500fa8819f9346d) chore: update minimum hardware requirement for vmware ova * [`b690ffeb8`](https://github.com/siderolabs/talos/commit/b690ffeb899c4a133f98e212826830e3b320abe4) test: improve DNS resolver test stability * [`5aa0299b6`](https://github.com/siderolabs/talos/commit/5aa0299b6e3efefa7077aab5955526a5136b8761) style: use correct capitalization for openstack * [`4c0c626b7`](https://github.com/siderolabs/talos/commit/4c0c626b786f14c5eabdc65e88d2aae92829bf73) feat: use zstd compression in place of xz * [`98906ed6e`](https://github.com/siderolabs/talos/commit/98906ed6ea1afc5a758871a7c2d8251fccaef106) fix: use reboot delay only in case of error * [`05fd042bb`](https://github.com/siderolabs/talos/commit/05fd042bb3600541a8e2587b66b8b4c4e9f99c27) test: improve the reset integration tests * [`8cdf0f7cb`](https://github.com/siderolabs/talos/commit/8cdf0f7cb007790190197356355a16c8e427afab) docs: fix typo in Cilium instructions * [`dd1d279da`](https://github.com/siderolabs/talos/commit/dd1d279daa8c2a18c2477839b2c11e5f2f554693) fix: allow more flags in `talosctl cluster create --input-dir` * [`ef4394e58`](https://github.com/siderolabs/talos/commit/ef4394e586e42c4b5085299029a2aacb3b89502d) chore: update kernel and other packages * [`ccdb4c8b1`](https://github.com/siderolabs/talos/commit/ccdb4c8b10450aa7fb6c32b0559bda73746a03ed) chore: update google.golang.org/grpc to 1.63.2 * [`c5b59df69`](https://github.com/siderolabs/talos/commit/c5b59df6976095aca5c4bac367084874242e9e80) fix: wait for devices to be discovered before probing filesystems * [`0821b9c50`](https://github.com/siderolabs/talos/commit/0821b9c50b86bf9f7d08a1ba7b177abb7e2568c4) feat: add `--non-masquerade-cidrs` flag to `talosctl cluster create` * [`2bf613ad3`](https://github.com/siderolabs/talos/commit/2bf613ad3bd1582b520b2f661b7e0bfab4207eed) fix: add endpoints for "virtual" `host-dns` service * [`f4163aefe`](https://github.com/siderolabs/talos/commit/f4163aefeda2bf91be36af45239716c53ec982b1) fix: bump priority of OpenStack routes if IPv6 and default gateway * [`6fbd1263c`](https://github.com/siderolabs/talos/commit/6fbd1263ccbe20857cca90b5f69906651caa4f54) feat: report process MAC labels * [`d46032821`](https://github.com/siderolabs/talos/commit/d460328210ee3beea1b98ea5f23fcda5c2e2fd44) fix: return proper value from Bridge.STP instead of plain nil * [`bac1d00c3`](https://github.com/siderolabs/talos/commit/bac1d00c35cb6e1407884298118ee7b4ffc5fdfa) chore: prepare for Talos 1.8 * [`d6c8067e1`](https://github.com/siderolabs/talos/commit/d6c8067e15d8177c7394abad65b95ea98c597b9d) docs: make 1.7 docs the default * [`d7c3a0735`](https://github.com/siderolabs/talos/commit/d7c3a0735eab85dd24e86fe3e0872253067e8f10) docs: add what's new for v1.7 * [`908f67fa1`](https://github.com/siderolabs/talos/commit/908f67fa15e0de507c2f69fac0851d42376a66ce) feat: add host dns support for resolving member addrs * [`0d20b637d`](https://github.com/siderolabs/talos/commit/0d20b637d68a581354361bbceecb90395f24fedb) feat: update Kubernetes to 1.30.0 * [`ec69d7a78`](https://github.com/siderolabs/talos/commit/ec69d7a7855753e3e458f2cf7c211bf67e703220) chore: replace math/rand with math/rand/v2 * [`89040ce43`](https://github.com/siderolabs/talos/commit/89040ce4329743fa2037fb1cf65d978801753dbe) chore: update go-blockdevice/v2 library to the latest version * [`0a785802e`](https://github.com/siderolabs/talos/commit/0a785802ea22071e67d7ec85944513e73624b1ac) fix: overlay installer operations * [`b1b63f658`](https://github.com/siderolabs/talos/commit/b1b63f658eba5cbb08cbd05af959c6d397662e05) fix: mark overlay installer executable * [`3433fa13b`](https://github.com/siderolabs/talos/commit/3433fa13bf555a871e76f8ce726d5afd141a16e1) feat: use container DNS when in container mode * [`5d07ac5a7`](https://github.com/siderolabs/talos/commit/5d07ac5a7db9d2291a86ee966ee704b30afea342) fix: close apid inter-backend connections gracefully for real * [`7ba18555b`](https://github.com/siderolabs/talos/commit/7ba18555b098ba2617efce2438d6bfbec1dc0041) docs: fix typos in Akamai and AWS platform docs * [`3dd1f4e88`](https://github.com/siderolabs/talos/commit/3dd1f4e88c22734f03f7609791558b8bbbae3756) chore: extract `pkg/imager/quirks` to `pkg/machinery` * [`78bc3a433`](https://github.com/siderolabs/talos/commit/78bc3a433e8b10839034bd40b73fcc720438b943) docs: update Cilium docs * [`831f3d39e`](https://github.com/siderolabs/talos/commit/831f3d39e9b030cd1bcd3313246ebccf34f34205) feat: update Flannel to v0.25.1 * [`ea5b3ff0c`](https://github.com/siderolabs/talos/commit/ea5b3ff0c27cb033d525d172d4006e0645a924ba) feat: update Kubernetes to v1.30.0-rc.2 * [`54dac5ed4`](https://github.com/siderolabs/talos/commit/54dac5ed40698b8886096c620ac19ed55a4b99a1) feat: update Linux 6.6.24, containerd 1.7.15 * [`c51f146da`](https://github.com/siderolabs/talos/commit/c51f146daf3265bbeb4513c649938b2656ff1686) docs: update Akamai platform docs * [`9550f5ff7`](https://github.com/siderolabs/talos/commit/9550f5ff7a285df7c251df425e8f28d4c668224f) docs: fix getAuthenticationMethod and completePathFromNode docs * [`bfbd02abf`](https://github.com/siderolabs/talos/commit/bfbd02abfb1d84d14a73f1e247d62e728860d2f3) fix: assign different priority to IPv6 default gateway on OpenStack * [`c8f674bd3`](https://github.com/siderolabs/talos/commit/c8f674bd3d582f606848475bca3d22f309b2367c) test: add a test for 'spin' container runtime * [`5390ccd48`](https://github.com/siderolabs/talos/commit/5390ccd48c78e864f53cc45848772c931276380d) chore: replace []byte with string and use go:embed for templates * [`ba7cdc8c8`](https://github.com/siderolabs/talos/commit/ba7cdc8c8baf85e3015db4fa9e4446eaccf01115) chore: optimize DNSResolveCacheController * [`145f24063`](https://github.com/siderolabs/talos/commit/145f2406307e57a6f2eb1601d4f7d542d39a9f51) fix: don't modify a global map of profiles * [`6fe91ad9c`](https://github.com/siderolabs/talos/commit/6fe91ad9cf9f99401fc39a6ece24eed61f17b0e2) feat: provide Kubernets/Talos version compatibility for 1.8 * [`909a5800e`](https://github.com/siderolabs/talos/commit/909a5800e4a9ada42288ae15992579e9acf6c372) fix: generate secureboot ISO .der certificate correctly * [`b0fdc3c8c`](https://github.com/siderolabs/talos/commit/b0fdc3c8caaf6ef756cdc4440dae45891bd96d01) fix: make static pods check output consistent * [`c6ad0fcce`](https://github.com/siderolabs/talos/commit/c6ad0fcceb8220f0bf96a45e131ba999cb723f79) fix: validate that workers don't get cluster CA key * [`3735add87`](https://github.com/siderolabs/talos/commit/3735add87cec47038a88ba641322c26cd487ac58) fix: reconnect to the logs stream in dashboard after reboot * [`9aa1e1b79`](https://github.com/siderolabs/talos/commit/9aa1e1b79b4a02902e0573c10e1c0bf71a2341af) fix: present all accepted CAs to the kube-apiserver * [`336e61174`](https://github.com/siderolabs/talos/commit/336e61174624741f697c77b98dd84ab9a7a749f4) fix: close the apid connection to other machines gracefully * [`ff2c427b0`](https://github.com/siderolabs/talos/commit/ff2c427b04963d69ba2eaa1084a0a078d742b9ac) fix: pre-create nftables chain to make kubelet use nftables * [`5622f0e45`](https://github.com/siderolabs/talos/commit/5622f0e450eda589f4b9a2af28b8517d08c2aae2) docs: change localDNS to hostDNS in release notes yaml section

### Changes since v1.8.0-alpha.1
113 commits

* [`6f7c3a8e5`](https://github.com/siderolabs/talos/commit/6f7c3a8e5c6311bf1a2f9b1cbc6cd02d48746e02) fix: build of talosctl on non-Linux arches * [`f0a59cec7`](https://github.com/siderolabs/talos/commit/f0a59cec71739dd377082b0279684bb6ce46a0db) release(v1.8.0-alpha.2): prepare release * [`c8aed3be4`](https://github.com/siderolabs/talos/commit/c8aed3be4db9f4a510ddddb4c4baeff83432ee1f) fix: correctly add console args for ttyS0 * [`b453385bd`](https://github.com/siderolabs/talos/commit/b453385bd960cacc4baf43ff274a5c88e46d5f79) feat: support volume configuration, provisioning, etc * [`b6b16b35f`](https://github.com/siderolabs/talos/commit/b6b16b35fbccc861410f53bc29ad4cade962f1d6) chore: pause sequencer when talos installed and iso booted * [`eade0a9f2`](https://github.com/siderolabs/talos/commit/eade0a9f22f606f28241dbbcc92b93bea25aec6f) chore: bring in `uio` modules * [`81f9fcd9c`](https://github.com/siderolabs/talos/commit/81f9fcd9ce83c632dbbcbc1594605888d31e3ca3) fix: report errors correctly when pulling, fix EEXIST * [`b309e87b4`](https://github.com/siderolabs/talos/commit/b309e87b409fe5dd4a5579bee23879bb83bcb433) docs: fix invalid input in field user_data * [`c7474877a`](https://github.com/siderolabs/talos/commit/c7474877a46279a9f6330486a77b103c13216dae) docs: kubeProxyReplacement from "disabled" to "false" * [`be2ebf6b4`](https://github.com/siderolabs/talos/commit/be2ebf6b4d146d91cdfd7ba081d244775241bda8) chore: bump dependencies * [`88601bff4`](https://github.com/siderolabs/talos/commit/88601bff4e172841015761a5e74f01c5cb128069) chore: drop calico from interactive installer * [`106c17d0b`](https://github.com/siderolabs/talos/commit/106c17d0b5cdf3fa8f81f029e306cfd96f7ccfaf) chore: aarch64 qemu local secureboot support * [`da6263506`](https://github.com/siderolabs/talos/commit/da6263506ac772abe555e5937e2d21a517dc46cb) feat: update Flannel to v0.25.6 * [`19a44c2b0`](https://github.com/siderolabs/talos/commit/19a44c2b0bd4a4f4a9910c49bfdd9838f1a2bc54) chore: drop console `ttyS0` argument * [`75cecb421`](https://github.com/siderolabs/talos/commit/75cecb4210ad0d6ef201dafd307b4d023ccd7d39) feat: add Apache Cloudstack support * [`951cf66fd`](https://github.com/siderolabs/talos/commit/951cf66fdc6201186ec44276b818136f2f19b3d6) feat: add Cisco fnic driver * [`2d3bc94bf`](https://github.com/siderolabs/talos/commit/2d3bc94bf1840848bfe7e9f814a9b523132349c2) fix(ci): fix broken tests * [`a9551b7ca`](https://github.com/siderolabs/talos/commit/a9551b7caa413b03d4ed9b249b0cc957dd7a6edc) fix: host DNS access with firewall enabled * [`4834a61a8`](https://github.com/siderolabs/talos/commit/4834a61a8e4e67f4da3d14708dc7c699a8d3bc7c) feat: report SELinux labels * [`8fe39eacb`](https://github.com/siderolabs/talos/commit/8fe39eacba0db6d9372047172cf68825d57d0195) chore: move csi tests as go test * [`e4f8cb854`](https://github.com/siderolabs/talos/commit/e4f8cb854fc47daaba0ba969c52cc39329ae2ae0) fix: merge extension service config files by `mountPath` * [`5ba1df469`](https://github.com/siderolabs/talos/commit/5ba1df469542df0d1971a8f5fdd686a7d274dfa3) chore: add java package to protos * [`823480800`](https://github.com/siderolabs/talos/commit/823480800480babe4460d4d1a7f6e2f0ba3ab904) fix: add missing host/nvme-rdma * [`5b4b64979`](https://github.com/siderolabs/talos/commit/5b4b64979e4563e981064749c1b161f748fd4ff2) fix: bump go-smbios for broken SMIOS tables * [`f57d1f07e`](https://github.com/siderolabs/talos/commit/f57d1f07e9a690237eeaaadc6314d6da225ed625) fix: add NVMe target kernel modules * [`5ff6cf82c`](https://github.com/siderolabs/talos/commit/5ff6cf82ca593a7b701584dd76abdd09d96eb22e) fix: drop /opt mount for containers/tink * [`3c0db34d8`](https://github.com/siderolabs/talos/commit/3c0db34d8507571e49c0c49b6b615cfbe9cc5195) docs: update kubespan docs * [`3041d9075`](https://github.com/siderolabs/talos/commit/3041d90751fde279fc4ea28e149c1057e50a6947) fix: always handle `PermissionDenied` in dashboard resource watches * [`36f83eea9`](https://github.com/siderolabs/talos/commit/36f83eea9f6baba358c1d98223a330b2cb26e988) chore: make qemu check flag consistent with code * [`fe52cb074`](https://github.com/siderolabs/talos/commit/fe52cb0749e2d6aaaf9dbd3fb2c134b94792c425) chore: update protoc-gen-doc * [`ee4290f68`](https://github.com/siderolabs/talos/commit/ee4290f6849722af82db3f6a62039d9a3316f840) fix: bind HostDNS to 169.254.x link-local address * [`c312a46f6`](https://github.com/siderolabs/talos/commit/c312a46f69940cf96ce6c52d840f9fa00a01b87b) chore: restructure k8s component health checks * [`e193e7db9`](https://github.com/siderolabs/talos/commit/e193e7db98cfa9bbb689513751a7da39d8db9d14) docs: fix incorrect path for openebs in documentation * [`beadbac21`](https://github.com/siderolabs/talos/commit/beadbac210da8da391d52e13fc096b28a2c2538a) docs: update Oracle Cloud Talos custom image docs * [`6f969e364`](https://github.com/siderolabs/talos/commit/6f969e3645edc9ba561d23e02383f2331064f8eb) chore: improve `cluster create` UX on aarch64 * [`45cc8688a`](https://github.com/siderolabs/talos/commit/45cc8688a1c6a85665efb70ebf63ef7a3eb53213) chore: replace `if` blocks with `min`/`max` functions * [`a5bd770bf`](https://github.com/siderolabs/talos/commit/a5bd770bf923b7bf72759f6565e4dfd97e8d9bc6) fix: retry with another upstream if the previous failed * [`82e19f38a`](https://github.com/siderolabs/talos/commit/82e19f38ac276693610655fa7a8708bdd4521cc2) docs: add high-level overlay development guide * [`872599c9a`](https://github.com/siderolabs/talos/commit/872599c9a9ec9fbddd4820ba453ff29933525f14) chore: drop image assets from release * [`3c36c41a9`](https://github.com/siderolabs/talos/commit/3c36c41a91c95d9df3701b595a7b09285a390b71) feat: provide device extra settle timeout * [`9e348ef35`](https://github.com/siderolabs/talos/commit/9e348ef3501e95dc7c906c7d4d6df63f3c86715e) feat: update Kubernetes to 1.31.0 * [`61a1c946b`](https://github.com/siderolabs/talos/commit/61a1c946bff11b2fb9f85dfe826dfd890eac4986) feat: bundle (some) CNI plugins with Talos core * [`091da163b`](https://github.com/siderolabs/talos/commit/091da163b77db1014048a56cba1acbb6264711fb) chore: support arm64 kexec from zboot kernel images * [`73511c1ef`](https://github.com/siderolabs/talos/commit/73511c1ef3600c813835d7afd852fda4280e2323) chore: fix release notes * [`2bf924c7b`](https://github.com/siderolabs/talos/commit/2bf924c7be8869f8da869850f1df0e4d82651960) feat: update ISO VolumeID with Talos version * [`9a33dce10`](https://github.com/siderolabs/talos/commit/9a33dce10502aa05826adcc9cd9b66d9781111b3) docs: fix the VMWare docs * [`12562c2d5`](https://github.com/siderolabs/talos/commit/12562c2d5eb5a92b199018383bde6af58795dd28) docs: fix talos version in vmware.sh * [`ee67da14c`](https://github.com/siderolabs/talos/commit/ee67da14c5c8ae3bedfb2d8e321c9e127d61f565) feat: scaleway routed ip * [`eba5dafb9`](https://github.com/siderolabs/talos/commit/eba5dafb9eba450863fb295a4215559f32576666) fix: add dns-resolve-cache to the support bundle * [`d4f8100bd`](https://github.com/siderolabs/talos/commit/d4f8100bd4fc7d4e14a070c3eff600a259684d9a) docs: fix default openebs folder * [`60e163d54`](https://github.com/siderolabs/talos/commit/60e163d545392d17639809980d3041ec6fd9af09) docs: fix typo in doc * [`98d9abdd0`](https://github.com/siderolabs/talos/commit/98d9abdd0eaef72c8964fc58551670a0ec78783c) chore(ci): fix cilium ci tests * [`beb9602e3`](https://github.com/siderolabs/talos/commit/beb9602e35cff1ff072d60c86e1bc3faa6f8c002) chore: bump github.com/docker/docker to v27.1.1+incompatible * [`0698a4921`](https://github.com/siderolabs/talos/commit/0698a4921ba29bd1088f89406dfc89744a47e175) docs: aws getting started re-write * [`4d7d7a589`](https://github.com/siderolabs/talos/commit/4d7d7a58955468b7bbe42bacd8f53c782d12e074) chore(ci): update nvidia integration tests * [`60e901c1d`](https://github.com/siderolabs/talos/commit/60e901c1dcfdd728c7497a3c0d0ae28e0adb0580) chore: document slim kubelet image * [`622d66a98`](https://github.com/siderolabs/talos/commit/622d66a98f4d4eb809ff8dcdb67563e1c6be9b68) chore: bump deps * [`f9f5e0ef5`](https://github.com/siderolabs/talos/commit/f9f5e0ef556c575acc1cab85fafc0d89a1a4b4cc) chore: fix k8s tests * [`2ac8d2274`](https://github.com/siderolabs/talos/commit/2ac8d2274fcc5c9fc398575da2ddabb36984455a) chore: support `unsupported` flag for mkfs * [`9b9159d1e`](https://github.com/siderolabs/talos/commit/9b9159d1e04d337dc3a51e41be57f4795e71255d) docs: update support matrix for nvidia drivers * [`9d3415850`](https://github.com/siderolabs/talos/commit/9d34158500a155a7065e259d68f588112c5834ea) fix: fix graph diffs in dashboard when node aliases are used * [`9a126d70e`](https://github.com/siderolabs/talos/commit/9a126d70e0adab35a028f219b872cfc90e8d70d6) chore: generate deepcopy for SecureBootAssets type * [`dff56d824`](https://github.com/siderolabs/talos/commit/dff56d8246a481b163e1f49477efef324a106334) chore: remove arch-specific etcd image tag * [`c9f1dece5`](https://github.com/siderolabs/talos/commit/c9f1dece5d967e210b699234d365c27b5c397788) feat: update Kubernetes to 1.31.0-rc.1 * [`49831c56f`](https://github.com/siderolabs/talos/commit/49831c56fb10506bb0ea2546b1b09d924571fc6d) docs: replace removed Cilium/kubeProxyReplacement value * [`33a316369`](https://github.com/siderolabs/talos/commit/33a3163698084da3c43a5ea41c6600ab883b2ec9) docs: update aws.md for loop * [`e02bd2093`](https://github.com/siderolabs/talos/commit/e02bd20933b300f3b89ab9e9f385e23a0946eec8) feat: update Kubernetes to 1.31.0-rc.0 * [`64914b086`](https://github.com/siderolabs/talos/commit/64914b086ca0d72720c2f416b4543a1ba250986e) chore: add test for crun extension * [`7a1c62b8b`](https://github.com/siderolabs/talos/commit/7a1c62b8bc63f10dbad7673c59b6f62a6c9497bd) feat: publish installed extensions as node labels/annotations * [`3f2058aba`](https://github.com/siderolabs/talos/commit/3f2058aba29c1e30c9daaadea54b0035811ce318) fix: update containerd configuration and settings * [`81bd20f5a`](https://github.com/siderolabs/talos/commit/81bd20f5ad007a5f9c464a2ec7f6ad863f1c7fa8) docs: remove deprecated jiva from openebs instructions * [`480ffb88a`](https://github.com/siderolabs/talos/commit/480ffb88aed33214f23d21c31130a63f7b66dafc) docs: fix the amd64 PXE boot script URL * [`20fe34dbd`](https://github.com/siderolabs/talos/commit/20fe34dbde2613ed2e95378c3ff637a62bc015e5) docs: fix docker getting started typo * [`0fd7dfd2a`](https://github.com/siderolabs/talos/commit/0fd7dfd2ae1d74a8d4ea9d7f130018e972fe6674) docs: update Equinix Guide * [`3d1474ac0`](https://github.com/siderolabs/talos/commit/3d1474ac0bb4df3184423a7dfa4f4d981799ac41) feat: update CoreDNS to 1.1.3 * [`50e5f37ef`](https://github.com/siderolabs/talos/commit/50e5f37efb99ac2df2c58f9f5a248350eea1b594) chore: add test for apparmor * [`96492c097`](https://github.com/siderolabs/talos/commit/96492c0977e3a292336eb84d4e14563921896cb2) docs: extend multus configuration for Cilium * [`19aa44c54`](https://github.com/siderolabs/talos/commit/19aa44c54975f9f4d6c92b86c4dfb95a75d1adb0) fix: generate kubeconfig using proper types * [`240104e45`](https://github.com/siderolabs/talos/commit/240104e45fae2d8f80a3a229648a80b19f4dcbd0) feat: update Linux to 6.6.43 * [`32db8db60`](https://github.com/siderolabs/talos/commit/32db8db606773daf2d75d261387e591da8477ef1) chore: lock microsoft secureboot certs * [`3ce5492f8`](https://github.com/siderolabs/talos/commit/3ce5492f852c4e4e07d02c9a93f0b0fffcb00184) feat: runc memfd-bind service * [`341b55cd3`](https://github.com/siderolabs/talos/commit/341b55cd37d2225b163d92aa920965a7bca5d0a4) docs: update vmware.sh * [`117628aa6`](https://github.com/siderolabs/talos/commit/117628aa60c16e5b7a4102b71965cb0e77f95279) chore: add test for gvisor extension with platform kvm * [`fd01571c4`](https://github.com/siderolabs/talos/commit/fd01571c4037513fdb6287a8769dfbe46e9ed4b9) feat: update Linux, enable Broadcom MPI3 driver * [`b333ec07d`](https://github.com/siderolabs/talos/commit/b333ec07d96a27c721c07fd5c3ac29daec58690c) feat: update etcd to 3.5.15, Flannel to 0.25.5 * [`087290178`](https://github.com/siderolabs/talos/commit/0872901783785239920d4f484a2ab1e224f84b6f) feat: use ethtool ioctl to get link status when netlink api not available * [`395c64290`](https://github.com/siderolabs/talos/commit/395c642909765da17ed44771a08290c15a8b052c) docs: update openebs-jiva helm repo * [`f132d3f40`](https://github.com/siderolabs/talos/commit/f132d3f40320904d3a420ca94b8f95718075c251) chore(ci): remove artifacts directory prefix for checksums * [`fd54dc191`](https://github.com/siderolabs/talos/commit/fd54dc191d06305d7b5fbfe71cd937e7f95d4f10) feat(talosctl): append microsoft secure boot certs * [`fd6ddd11e`](https://github.com/siderolabs/talos/commit/fd6ddd11ef810f92190fe0d7490f2314ce21d595) feat: provide POD_IP env var to scheduler and controller-manager * [`407347a7a`](https://github.com/siderolabs/talos/commit/407347a7a0a955d2ea610ca06ebab4593ff0c03c) feat: update Kubernetes to 1.31.0-beta.0 * [`1b8c9ccbb`](https://github.com/siderolabs/talos/commit/1b8c9ccbb0285b678466f2b8eb7e5931bc8d44e4) fix: enforce secureboot enroll option only for supported releases * [`d52b89cb9`](https://github.com/siderolabs/talos/commit/d52b89cb91be238da08dd50d0cdd2ee50d93ed45) chore: ensure tls required on s3 buckets * [`c288ace7b`](https://github.com/siderolabs/talos/commit/c288ace7b185cd3fad569c0848afbda7217ac269) fix: be more smart when merging DNS resolver config * [`d983e4430`](https://github.com/siderolabs/talos/commit/d983e44308b677b07d2d135f0e73349cfb7e0ca8) fix: panic on shutdown * [`01404edff`](https://github.com/siderolabs/talos/commit/01404edff970888c968ff1b77d7dbd76cb724094) chore: reduce memory requirement for contrplane nodes * [`980f9ebc0`](https://github.com/siderolabs/talos/commit/980f9ebc07256280c74c6da8d473b49d0739a420) fix: fix log format in cluster provisioning * [`ea626a963`](https://github.com/siderolabs/talos/commit/ea626a96313dc8b56bd6256e0aae4b3a6c69f5be) feat: add label 'exclude-from-external-load-balancers' for cp nodes * [`1cf76cfbc`](https://github.com/siderolabs/talos/commit/1cf76cfbc28af980665e57d756c2e3ac002f5d8e) docs: fix talosctl spelling * [`b07338f54`](https://github.com/siderolabs/talos/commit/b07338f5471363457da94286cae6ef8075561aa2) feat: provide machine config document to update trusted CA roots * [`f14c4795e`](https://github.com/siderolabs/talos/commit/f14c4795e5e60bf564d584a707e261bed78bcaf8) fix: sort ports and merge adjacent ones in the nft rule * [`cf5effabb`](https://github.com/siderolabs/talos/commit/cf5effabb209fb570f59ba305bdab0b6409c7b93) feat: provide an option to enforce SecureBoot for TPM enrollment * [`736c1485e`](https://github.com/siderolabs/talos/commit/736c1485e27a597b8bf720b2dba4f8664cb9321a) fix: change the UEFI firmware search path order * [`a727a1d97`](https://github.com/siderolabs/talos/commit/a727a1d97a22001eb8b1ef3f9f22fc39a653ad09) chore: make using action tracker easier * [`0aebeff35`](https://github.com/siderolabs/talos/commit/0aebeff3560e276fb7ee984b5362b80ad5873c0f) docs: add missing backslashes * [`398151e64`](https://github.com/siderolabs/talos/commit/398151e64fb6490a8dc3e828fcc8a191857e41d4) fix: remove host bind mount for `/tmp` for trustd * [`ce4c404e1`](https://github.com/siderolabs/talos/commit/ce4c404e144deffe8b6a52488453c157f23497dd) chore: redo FilterMessages as generic function * [`fbde9c556`](https://github.com/siderolabs/talos/commit/fbde9c556f0107734ff1216ea80d9156c35d4e3c) chore: bump deps * [`3bab15214`](https://github.com/siderolabs/talos/commit/3bab15214de985b7738250f2a6d84a796c5e9253) feat: update Kubernetes to 1.31.0-alpha.3 * [`c2a5213ee`](https://github.com/siderolabs/talos/commit/c2a5213eefa6dc977ded541316c96f516ea2ecfb) docs: add note about mayastor nvme_tcp init container check * [`dad9c40c7`](https://github.com/siderolabs/talos/commit/dad9c40c736d55dee05d4b74e94db610dd119ce2) chore: simplify code * [`963612bcc`](https://github.com/siderolabs/talos/commit/963612bccaead87d5bbb4b79014d5f9821eeb95e) chore: redo EncodeString and EncodeBytes using buffer interface * [`d9db360ab`](https://github.com/siderolabs/talos/commit/d9db360ab47b24dd5bccf3a36c938e5e648ff095) fix: properly output multi-doc machine config in `get mc`

### Changes from siderolabs/discovery-client
2 commits

* [`ca662d2`](https://github.com/siderolabs/discovery-client/commit/ca662d218418eb50eb22d84560c290bef4369702) feat: export default GRPC dial options for the client * [`7a767fa`](https://github.com/siderolabs/discovery-client/commit/7a767fa89005209f5f39b2f5891ca7b169f52d89) chore: bump Go, deps and rekres

### Changes from siderolabs/extras
7 commits

* [`43a2821`](https://github.com/siderolabs/extras/commit/43a2821da1783c4431a0494e853435a75451d687) feat: bump deps * [`6f4a373`](https://github.com/siderolabs/extras/commit/6f4a373cf517926dc9ac62045c05b5434acfb9ec) chore: use Go 1.22.6 * [`e7d16d8`](https://github.com/siderolabs/extras/commit/e7d16d88e095a05b8ced99a272ece9d403452b45) chore: bump deps * [`cab51d8`](https://github.com/siderolabs/extras/commit/cab51d8f49fec77266b74d2535f61bf73bb8b2c4) feat: update dependencies * [`0efb05f`](https://github.com/siderolabs/extras/commit/0efb05f989d7e745f61955570992c54094d3fddf) feat: update Go to 1.22.4 * [`01ad9f5`](https://github.com/siderolabs/extras/commit/01ad9f5e2aa7e0ef2b6d9e0a19e7bf6a39dd5d94) feat: update Go to 1.22.3 * [`fa6663c`](https://github.com/siderolabs/extras/commit/fa6663c2abf90d82667a6c33cbc6f5edb2d1c525) feat: update Go to 1.22.2

### Changes from siderolabs/gen
2 commits

* [`7654108`](https://github.com/siderolabs/gen/commit/7654108fe6ae15d4765584342709bc0bced6b3d6) chore: add hashtriemap implementation * [`8485864`](https://github.com/siderolabs/gen/commit/84858640dc9c3032219380885283b995d4f2b0d1) chore: optimize maps.Values and maps.Keys

### Changes from siderolabs/go-api-signature
3 commits

* [`1b35ea8`](https://github.com/siderolabs/go-api-signature/commit/1b35ea8d3a334418aa273159ea5732ae0625a317) chore: bump deps and fix data race * [`4bf0f02`](https://github.com/siderolabs/go-api-signature/commit/4bf0f025dd94a8117997028d35c8b4497de497b4) fix: get rid of data race in the key sign interceptor * [`782aac0`](https://github.com/siderolabs/go-api-signature/commit/782aac0d69752fe7c6eba36bae8d1383ffdc0b04) chore: bump deps

### Changes from siderolabs/go-circular
3 commits

* [`cbce5c3`](https://github.com/siderolabs/go-circular/commit/cbce5c3e47d1c6a26a588cbb6f77af2f9bc3e5b7) feat: add persistence support * [`3c48c53`](https://github.com/siderolabs/go-circular/commit/3c48c53c1449b2b5e5ddde14e0351d93a351b021) feat: implement extra compressed chunks * [`835f04c`](https://github.com/siderolabs/go-circular/commit/835f04c9ba6083ef451b5bbba748200202d1a0a9) chore: rekres, update dependencies

### Changes from siderolabs/go-debug
1 commit

* [`c8f9b12`](https://github.com/siderolabs/go-debug/commit/c8f9b12c041a3242472ad56b970487432552d2be) chore: add support for Go 1.23

### Changes from siderolabs/go-kubernetes
2 commits

* [`ee8c6b8`](https://github.com/siderolabs/go-kubernetes/commit/ee8c6b8a5bb2c2c45e961d0f08faa5673905545c) fix: add one more removed feature gate for 1.31 * [`37dd61f`](https://github.com/siderolabs/go-kubernetes/commit/37dd61fad48b9f4bb6bce5a0a361a247228e86d2) feat: add support for Kubernetes 1.31

### Changes from siderolabs/go-loadbalancer
1 commit

* [`0639758`](https://github.com/siderolabs/go-loadbalancer/commit/0639758a06785c0c8c65e18774b81d85ab40acdf) chore: bump deps

### Changes from siderolabs/go-pcidb
1 commit

* [`2e79017`](https://github.com/siderolabs/go-pcidb/commit/2e7901711733e2d7e5e5a767a68cae08df148dc5) feat: rekres, update PCI IDs

### Changes from siderolabs/go-smbios
2 commits

* [`e781237`](https://github.com/siderolabs/go-smbios/commit/e781237bb6d0b04cfb9d380bc36b552f5ee53af2) fix: stop decoding without error if EOF encountered during header read * [`6a719a6`](https://github.com/siderolabs/go-smbios/commit/6a719a63dcd3b2c58ee14412973fa6a565e2905e) chore: rekres, bump deps

### Changes from siderolabs/go-tail
1 commit

* [`7cb7294`](https://github.com/siderolabs/go-tail/commit/7cb7294b8af33175bc463c84493776e6e4da9c4f) fix: remove unexpected short read error

### Changes from siderolabs/go-talos-support
3 commits

* [`58f4f0f`](https://github.com/siderolabs/go-talos-support/commit/58f4f0fde6be11e5d5da37ceaab52286b4b0be05) chore: bump Go dependencies * [`f9d46fd`](https://github.com/siderolabs/go-talos-support/commit/f9d46fd8a607a928dc0382f308ad577f36b0a8b8) fix: add `dns-resolve-cache` to the list of logs gathered * [`69891cf`](https://github.com/siderolabs/go-talos-support/commit/69891cf046628969e651fc751e433aad86ec22c4) chore: remove containerd dependency

### Changes from siderolabs/grpc-proxy
5 commits

* [`ec3b59c`](https://github.com/siderolabs/grpc-proxy/commit/ec3b59c869000243e9794d162354c83738475a32) fix: address all gRPC deprecations * [`02f82db`](https://github.com/siderolabs/grpc-proxy/commit/02f82db9c921eea3a48184bc4a4cf83a98b5b227) chore: rekres, bump deps * [`62b29be`](https://github.com/siderolabs/grpc-proxy/commit/62b29beccb302d80e7a1b25acf86d755a769970b) chore: rekres, update dependencies * [`2decdd1`](https://github.com/siderolabs/grpc-proxy/commit/2decdd1f77e64b61761e27c077ec3a420bfb2781) chore: add no-op github workflow * [`77d7adc`](https://github.com/siderolabs/grpc-proxy/commit/77d7adc7105b6132b1352bf9e737bacc47fba5e5) chore: bump deps

### Changes from siderolabs/pkgs
56 commits

* [`4ce5bc6`](https://github.com/siderolabs/pkgs/commit/4ce5bc6bbb87f1feeabadc90ef304e4f16c6da8f) feat: add uio_pci_generic kernel module * [`18d3b85`](https://github.com/siderolabs/pkgs/commit/18d3b85b1cff5d239f02b4b2bdaedbc8e7958dd4) feat: add `uinput` kernel module * [`4fd2541`](https://github.com/siderolabs/pkgs/commit/4fd254154408d1d25d54e96dbf6ae4739e7766ac) feat: bump dependencies * [`467d127`](https://github.com/siderolabs/pkgs/commit/467d127922d96b213d7f077e04924e438e7adadf) feat: enable Cisco FCoE HBA Driver (fnic) * [`4e6dec2`](https://github.com/siderolabs/pkgs/commit/4e6dec2ee54486b7f38565da3cd90665d9706ddb) feat: enable more PCI options * [`5f919c5`](https://github.com/siderolabs/pkgs/commit/5f919c50624a91308667dedeb007c3f501e1fcaa) fix: add virtio-net GSO issue patch * [`7b2e46b`](https://github.com/siderolabs/pkgs/commit/7b2e46bafdb9c68f44c271c7a9628b2926604d20) feat: update Linux to 6.6.45 * [`a6db229`](https://github.com/siderolabs/pkgs/commit/a6db229a8a9180695da0c2abbba074af193a79df) fix: strip CNI plugins * [`124d35b`](https://github.com/siderolabs/pkgs/commit/124d35b83988a9ab410fcef05fbb2f7379bddb41) chore: bump deps * [`af6b4e6`](https://github.com/siderolabs/pkgs/commit/af6b4e6ccfd37fec021892a434de75de02dca5d3) chore: bump nvidia drivers * [`5e8a15a`](https://github.com/siderolabs/pkgs/commit/5e8a15a85ac4c4d395a9e7fe5548576862f5e750) chore: bump deps * [`99650c8`](https://github.com/siderolabs/pkgs/commit/99650c8c7c0362477073dcd9cc598e0500c19c45) fix: enable TPROXY for nftables * [`75adbde`](https://github.com/siderolabs/pkgs/commit/75adbde1afac432b3674522bfdb88e75364bf7ce) feat: support lts and production nvidia modules * [`a97d58f`](https://github.com/siderolabs/pkgs/commit/a97d58f4b74a37604e8e330b4d4e0c79f7630d02) feat: add Intel management engine modules for Intel Arc support * [`4e940f8`](https://github.com/siderolabs/pkgs/commit/4e940f850745a0d6a934e06e4d425f11babf4b37) feat: update Linux to 6.6.43 * [`7f9c802`](https://github.com/siderolabs/pkgs/commit/7f9c8026e042735002724db98b2bfe2968823fca) fix(kernel): array-index-out-of-bounds error on bpf * [`8cc6455`](https://github.com/siderolabs/pkgs/commit/8cc6455e1ff1c601a67e4a8a7d90db45020d1a3d) feat: add driver for Broadcom MPI3 * [`d01fb35`](https://github.com/siderolabs/pkgs/commit/d01fb359b6ecbd6e8c9ee2ec9466c0ca5e0f51b5) feat: update Linux to 6.6.39 * [`25f3a99`](https://github.com/siderolabs/pkgs/commit/25f3a99c543a1f6cc6259aa0326b7bfaa1d120dc) fix: update ca-certificates in pkgs * [`60a91b2`](https://github.com/siderolabs/pkgs/commit/60a91b2fcf9415b2caaaf10b98c5793ff3d858a6) fix: enable CONFIG_PROC_CHILDREN for amd64 kernel * [`ce49757`](https://github.com/siderolabs/pkgs/commit/ce497578fd6911be16848df71156558565616ac1) feat: update flannel-cni plugin to v1.5.1 * [`289ed6b`](https://github.com/siderolabs/pkgs/commit/289ed6ba2de66c7230b154df9ca65581f7619055) feat: bump deps * [`8d6b19a`](https://github.com/siderolabs/pkgs/commit/8d6b19a8a15c6f0b8b76c0dc65657d10830bbf3a) feat: update Linux to 6.6.36 * [`b671d46`](https://github.com/siderolabs/pkgs/commit/b671d4604db736c7ac541c40ba2c5deeaf03baee) feat: update containerd/runc to the next rc versions * [`c7e9591`](https://github.com/siderolabs/pkgs/commit/c7e9591dcdd18f94a391a329789fa2ddf93a509f) feat: enable CONFIG_X86_AMD_PSTATE * [`84bad89`](https://github.com/siderolabs/pkgs/commit/84bad890a6eed3b1fa2d01df494c26e695d5a290) feat: add 'apparmor' package * [`4d9869a`](https://github.com/siderolabs/pkgs/commit/4d9869a06f06cab4ed56b42b93974804f33b6435) feat: update Linux to 6.6.33 * [`e5990e8`](https://github.com/siderolabs/pkgs/commit/e5990e87dc8e491adbe42df246f607eddd25af94) feat: enable CONFIG_KSM * [`a37f382`](https://github.com/siderolabs/pkgs/commit/a37f382b8c11a478d1015b9fd1042257684529bc) fix: network for Rockchip boards like Rock64 * [`95218c7`](https://github.com/siderolabs/pkgs/commit/95218c7868047d7075465fb4e112975460acff00) fix: enable PAGE_TABLE_CHECK * [`cbd9cd7`](https://github.com/siderolabs/pkgs/commit/cbd9cd79a73ada392bc03f04dca2a982878ce2b6) feat: enable SCTP support * [`c309452`](https://github.com/siderolabs/pkgs/commit/c309452aefee22fbc3d714781b4cc880881e0a5d) feat: bump dependencies * [`3a56032`](https://github.com/siderolabs/pkgs/commit/3a56032bf8e49296cf4a02655925767ab9c8b1d2) chore: rekres * [`db7f60c`](https://github.com/siderolabs/pkgs/commit/db7f60c77b2effcfc5640fd50b871052e842b1eb) feat: bump Linux to 6.6.32 * [`c647a05`](https://github.com/siderolabs/pkgs/commit/c647a0591741916e4bc28c35dc6a9cc36add65e0) feat: update ipxe to the latest * [`f350879`](https://github.com/siderolabs/pkgs/commit/f350879ba82443c662582d1b43e6d9fc06826c55) feat: update containerd to 2.0.0-rc.2, runc to 1.2.0-rc.1 * [`f8392fb`](https://github.com/siderolabs/pkgs/commit/f8392fb597559eaf3e12c4284acc7805667e7f8e) feat: update Linux firmware to 20240513 * [`f414bbd`](https://github.com/siderolabs/pkgs/commit/f414bbdb189e3ab880ee65efe2a030667aae77ec) fix: disable CONFIG_EFI_DISABLE_PCI_DMA option * [`9ebfd1b`](https://github.com/siderolabs/pkgs/commit/9ebfd1b90ed674a984eb69f03b6bc79f21573313) feat: enable EDAC drivers * [`f9559de`](https://github.com/siderolabs/pkgs/commit/f9559de4cb7961bd54745ddeb0ffb3414f7125aa) fix: drbd module installation * [`492638d`](https://github.com/siderolabs/pkgs/commit/492638d5d8242d733da4cf2a573380be1e780f2f) feat: update dependencies * [`bd70572`](https://github.com/siderolabs/pkgs/commit/bd70572339f6cc28dd88d0e4e28f079299268c8b) feat: update Go to 1.22.3 * [`edb600a`](https://github.com/siderolabs/pkgs/commit/edb600aa02ff620217cc430bdc4a699d9c9eba82) feat: update zfs package to v2.2.4 * [`6775002`](https://github.com/siderolabs/pkgs/commit/67750020042162af7fc01e5f14a678fc6eeaaf6b) feat: enable NFT FIB lookups * [`28c5696`](https://github.com/siderolabs/pkgs/commit/28c5696e7c97b12765e65bd1bb758f8cb19e6adc) feat: update Linux to 6.6.29 * [`9c8a02c`](https://github.com/siderolabs/pkgs/commit/9c8a02c234b52cf3624ebf79f7e76065cbc1eeff) feat: update containerd to 1.7.16 * [`ca6249b`](https://github.com/siderolabs/pkgs/commit/ca6249b4b7d00b6f16e1a7264f55a4814300df63) feat: compress amd64 Linux kernel using zstd * [`718a7da`](https://github.com/siderolabs/pkgs/commit/718a7da83fe843cd59745078fe1a814c75bc4384) feat: enable SELinux * [`207481f`](https://github.com/siderolabs/pkgs/commit/207481f7b16d2b0c98053432f4ad86484bf0b1ec) feat(intel): add support for power management and ACPI options for Intel CPUs * [`dfa7dce`](https://github.com/siderolabs/pkgs/commit/dfa7dceb5ae50af454f527ac7c774c93d00054cf) feat: update Linux to 6.6.28 * [`7b30b61`](https://github.com/siderolabs/pkgs/commit/7b30b61ef3ba104f3ea21469632d3d043c5fd6f6) fix: use proper EFI zBoot image * [`010913b`](https://github.com/siderolabs/pkgs/commit/010913b8bf2b7c7df2d16efcdf23a4efbb9913ab) feat: update Linux 6.6.26, containerd 1.7.15 * [`da397fa`](https://github.com/siderolabs/pkgs/commit/da397fa0e55284f466af982f98cf93e7075e6298) feat: enable BFQ IO scheduler * [`c839801`](https://github.com/siderolabs/pkgs/commit/c83980113db4aabbda4393d7aa8e6ab734a6069b) feat: enable zboot on arm64 with zstd compression * [`1b28e2c`](https://github.com/siderolabs/pkgs/commit/1b28e2ce58e5702bcbbd5ed13fbd7cf6420dc12d) feat: go 1.22.2, Linux 6.6.24 * [`05db2a8`](https://github.com/siderolabs/pkgs/commit/05db2a88e6985470f4e7dc6b21fbdd9df1e63aea) fix: revert musl to 1.2.4

### Changes from siderolabs/protoenc
19 commits

* [`684f268`](https://github.com/siderolabs/protoenc/commit/684f2683c83568076b1f7d573f40555c508df7a5) chore: bump deps, add repeated <-> single field example * [`82f0774`](https://github.com/siderolabs/protoenc/commit/82f07747c640f96ce03cc9f3efa3d337fdd553ac) fix: encode (u)int(16|8)s as varints * [`d8ddbd5`](https://github.com/siderolabs/protoenc/commit/d8ddbd5d49cd8fd80cf5f8cc1d719bf9e9ba22c9) chore: add more tests * [`dceb5a6`](https://github.com/siderolabs/protoenc/commit/dceb5a69a0d707d3bcd72098beca26c247bf734b) fix: proper order for custom EncoderDecoder * [`3617e19`](https://github.com/siderolabs/protoenc/commit/3617e19073cb4db7b8a018bb7227cae45054b626) fix: add missing test and proper check for `map[string]interface{}` * [`647e9da`](https://github.com/siderolabs/protoenc/commit/647e9da005a1d059e2078fdb8239c8c95f41ee75) chore: various additions * [`3e56913`](https://github.com/siderolabs/protoenc/commit/3e569130fb14c536952ea8e212d763680c84decc) fix: support pointer to structs in marshal/unmarshal * [`49a85fa`](https://github.com/siderolabs/protoenc/commit/49a85fa966f82025092615dc3900e5592fd78d9f) chore: add support for map[string]interface{} * [`bf5e39b`](https://github.com/siderolabs/protoenc/commit/bf5e39bc5ed0b316270f4f8aa492e48ca06c11b7) chore: support (u)int(8|16) fields ans slices, fix map issues, * [`d618d0d`](https://github.com/siderolabs/protoenc/commit/d618d0ded21d763fd56589feecc8674e115bd1f1) chore: no longer treat T and *T as the same types in RegisterEncoderDecoder * [`aa7ee6c`](https://github.com/siderolabs/protoenc/commit/aa7ee6c221e10a92c0f7c235f216b26fa087d31a) chore: add fast path for ints, fixed ints and floats * [`6427893`](https://github.com/siderolabs/protoenc/commit/64278935504606ae2d5ff984edeaaf68cf773a71) chore: bump Go and fix lint issues * [`94427a5`](https://github.com/siderolabs/protoenc/commit/94427a5723dd6f37c2bfd55c63861c97b2de524b) chore: even more various fixes and small refactorings * [`76e5695`](https://github.com/siderolabs/protoenc/commit/76e56952b611a270e356e60996a7b90a9a542ecc) chore: various fixes and small refactorings * [`8a48bf0`](https://github.com/siderolabs/protoenc/commit/8a48bf027476e8456478fcd03f9e9b4c37e05a48) feat: implement custom encoders/decoders * [`549761b`](https://github.com/siderolabs/protoenc/commit/549761b029e126ee8ba6ee6c967d67c1d7d119a4) chore: various embedding fixes * [`ab9b1ff`](https://github.com/siderolabs/protoenc/commit/ab9b1ffdc4582c3c6f152ba6883568c66326f816) chore: add side-by-side tests with official proto.Marshal and Unmarshal * [`2519db3`](https://github.com/siderolabs/protoenc/commit/2519db3bc80b9d2024cd0fb72e1ae7deed8b380a) feat: implement Marshal/Unmarshal functions for protobuf encoding * [`485db9f`](https://github.com/siderolabs/protoenc/commit/485db9f2005db2155d723711328c59026af84f9a) Initial commit

### Changes from siderolabs/siderolink
4 commits

* [`e76747b`](https://github.com/siderolabs/siderolink/commit/e76747ba523b336ab8b9143293c920ff64bc4f14) chore: migrate to rtnetlink/2 * [`3a587fc`](https://github.com/siderolabs/siderolink/commit/3a587fcf9dbb259e216495496a523faaea427d04) fix: do not ever skip updates which have remove flag * [`be00ff5`](https://github.com/siderolabs/siderolink/commit/be00ff59bac50e0da4cd0747f8e5f30c7b029ded) chore: redo event filtering as a sequence of iterators * [`a936b60`](https://github.com/siderolabs/siderolink/commit/a936b60645267d2e7320083b402df5ad19de76f5) chore: handle peer events in batches

### Changes from siderolabs/tools
14 commits

* [`50e55e6`](https://github.com/siderolabs/tools/commit/50e55e61e6d1c0d5b220d3cf5e7db0900f3022f6) feat: bump dependencies * [`2b8dab4`](https://github.com/siderolabs/tools/commit/2b8dab4c892e1755b068323758d8fc0952f28500) feat: add policycoreutils for building squashfs with SELinux * [`ef48079`](https://github.com/siderolabs/tools/commit/ef48079b3fbe0b414437728b411f7e033ea2f47f) feat: add fakeroot as a build dependency * [`86b5363`](https://github.com/siderolabs/tools/commit/86b5363b67b9dcfa2fabb093e95624e8c6190a89) feat: add secilc * [`41ed4b2`](https://github.com/siderolabs/tools/commit/41ed4b2ff91d273594716cd98a5f193fcb50dc85) fix: fix Tcl tag hashes * [`a764e8d`](https://github.com/siderolabs/tools/commit/a764e8dc4888601f30f1a2d09d37cbe3d00d78fc) chore: bump deps * [`7d807bd`](https://github.com/siderolabs/tools/commit/7d807bdc7532cc1f72b8288a0c36dd4f656a3af3) chore: bump deps * [`31ad71b`](https://github.com/siderolabs/tools/commit/31ad71bdb3b2b33ab1c74175ffc1eff0cae33866) feat: update dependencies * [`d2746e5`](https://github.com/siderolabs/tools/commit/d2746e5a7a60a22ad957c8bc04831bae8c191af6) feat: update Go to 1.22.4 * [`06ba64e`](https://github.com/siderolabs/tools/commit/06ba64ec3044c9c4ea51b8a624c46503a4f5fe26) feat: update dependencies * [`7e5a248`](https://github.com/siderolabs/tools/commit/7e5a2482284e00f60cd44a5d155fcdf2291f1fc9) feat: update dependencies * [`c34ec5b`](https://github.com/siderolabs/tools/commit/c34ec5bfd44faa4a5ccced07136246fb25858635) feat: update Go to 1.22.3 * [`3c25a6f`](https://github.com/siderolabs/tools/commit/3c25a6f164f3004d222bb13f5b663e01b80ff882) fix: update pkg-config configure flag * [`bd405ff`](https://github.com/siderolabs/tools/commit/bd405ff5d8d511eeef17f0a6126ad6cdd3a849bb) feat: update go to 1.22.2

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.3 -> v0.5.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.11.1 -> v1.13.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.5.1 -> v1.7.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.10 -> v1.27.31 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.1 -> v1.16.12 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.30.1 -> v1.35.5 * **github.com/aws/smithy-go** v1.20.2 -> v1.20.4 * **github.com/beevik/ntp** v1.3.1 -> v1.4.3 * **github.com/containerd/containerd/api** v1.8.0-rc.3 **_new_** * **github.com/containerd/containerd/v2** v2.0.0-rc.4 **_new_** * **github.com/containerd/errdefs** v0.1.0 **_new_** * **github.com/containerd/platforms** v0.2.1 **_new_** * **github.com/containerd/typeurl/v2** v2.1.1 -> v2.2.0 * **github.com/containernetworking/cni** v1.1.2 -> v1.2.3 * **github.com/containernetworking/plugins** v1.4.1 -> v1.5.1 * **github.com/coreos/go-iptables** v0.7.0 -> v0.8.0 * **github.com/cosi-project/runtime** v0.4.1 -> v0.5.5 * **github.com/docker/docker** v26.0.0 -> v27.2.0 * **github.com/fatih/color** v1.16.0 -> v1.17.0 * **github.com/foxboron/go-uefi** 48be911532c2 -> e2076f0e58ca * **github.com/google/go-containerregistry** v0.19.1 -> v0.20.2 * **github.com/google/go-tpm** ee6cbcd136f8 -> v0.9.1 * **github.com/hashicorp/go-getter/v2** v2.2.1 -> v2.2.3 * **github.com/hetznercloud/hcloud-go/v2** v2.7.0 -> v2.13.1 * **github.com/insomniacslk/dhcp** c728f5dd21c8 -> a3a4c1f04475 * **github.com/jsimonetti/rtnetlink/v2** v2.0.2 **_new_** * **github.com/klauspost/compress** v1.17.9 **_new_** * **github.com/klauspost/cpuid/v2** v2.2.7 -> v2.2.8 * **github.com/miekg/dns** v1.1.58 -> v1.1.62 * **github.com/opencontainers/runc** v1.2.0-rc.2 **_new_** * **github.com/pelletier/go-toml/v2** v2.2.3 **_new_** * **github.com/pkg/xattr** v0.4.10 **_new_** * **github.com/prometheus/procfs** v0.13.0 -> v0.15.1 * **github.com/rivo/tview** a22293bda944 -> fd649dbf1223 * **github.com/rs/xid** v1.5.0 -> v1.6.0 * **github.com/safchain/ethtool** v0.3.0 -> v0.4.1 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.25 -> v1.0.0-beta.29 * **github.com/siderolabs/discovery-client** v0.1.8 -> v0.1.9 * **github.com/siderolabs/extras** v1.7.0-1-gbb76755 -> v1.8.0-alpha.0-6-g43a2821 * **github.com/siderolabs/gen** v0.4.8 -> v0.5.0 * **github.com/siderolabs/go-api-signature** v0.3.2 -> v0.3.5 * **github.com/siderolabs/go-blockdevice/v2** 3265299b0192 -> v2.0.1 * **github.com/siderolabs/go-circular** v0.1.0 -> v0.2.0 * **github.com/siderolabs/go-debug** v0.3.0 -> v0.4.0 * **github.com/siderolabs/go-kubernetes** v0.2.9 -> v0.2.11 * **github.com/siderolabs/go-loadbalancer** v0.3.3 -> v0.3.4 * **github.com/siderolabs/go-pcidb** v0.2.0 -> v0.3.0 * **github.com/siderolabs/go-smbios** v0.3.2 -> v0.3.3 * **github.com/siderolabs/go-tail** v0.1.0 -> v0.1.1 * **github.com/siderolabs/go-talos-support** v0.1.0 -> v0.1.1 * **github.com/siderolabs/grpc-proxy** v0.4.0 -> v0.4.1 * **github.com/siderolabs/pkgs** v1.7.0-6-g29106c0 -> v1.8.0-alpha.0-54-g4ce5bc6 * **github.com/siderolabs/protoenc** v0.2.1 **_new_** * **github.com/siderolabs/siderolink** v0.3.5 -> v0.3.9 * **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.8.0-alpha.2 * **github.com/siderolabs/tools** v1.7.0-1-g10b2a69 -> v1.8.0 * **github.com/spf13/cobra** v1.8.0 -> v1.8.1 * **github.com/vishvananda/netlink** v1.2.1-beta.2 -> v1.3.0 * **go.etcd.io/etcd/api/v3** v3.5.13 -> v3.5.15 * **go.etcd.io/etcd/client/pkg/v3** v3.5.13 -> v3.5.15 * **go.etcd.io/etcd/client/v3** v3.5.13 -> v3.5.15 * **go.etcd.io/etcd/etcdutl/v3** v3.5.13 -> v3.5.15 * **golang.org/x/net** v0.23.0 -> v0.28.0 * **golang.org/x/oauth2** v0.18.0 -> v0.22.0 * **golang.org/x/sync** v0.6.0 -> v0.8.0 * **golang.org/x/sys** v0.18.0 -> v0.24.0 * **golang.org/x/term** v0.18.0 -> v0.23.0 * **golang.org/x/text** v0.14.0 -> v0.17.0 * **golang.org/x/time** v0.5.0 -> v0.6.0 * **google.golang.org/grpc** v1.62.1 -> v1.66.0 * **google.golang.org/protobuf** v1.33.0 -> v1.34.2 * **k8s.io/api** v0.30.0 -> v0.31.0 * **k8s.io/apimachinery** v0.30.0 -> v0.31.0 * **k8s.io/apiserver** v0.30.0 -> v0.31.0 * **k8s.io/client-go** v0.30.0 -> v0.31.0 * **k8s.io/component-base** v0.30.0 -> v0.31.0 * **k8s.io/cri-api** v0.30.0 -> v0.32.0-alpha.0 * **k8s.io/klog/v2** v2.120.1 -> v2.130.1 * **k8s.io/kube-scheduler** v0.30.0 -> v0.31.0 * **k8s.io/kubectl** v0.30.0 -> v0.31.0 * **k8s.io/kubelet** v0.30.0 -> v0.31.0 * **k8s.io/pod-security-admission** v0.30.0 -> v0.31.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.69 -> v1.2.70 * **sigs.k8s.io/hydrophone** b92baf7e0b04 **_new_** Previous release can be found at [v1.7.0](https://github.com/siderolabs/talos/releases/tag/v1.7.0) ## [Talos 1.8.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.8.0-alpha.1) (2024-07-05) Welcome to the v1.8.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Workload Apparmor Profile Talos Linux can now apply the default AppArmor profiles to all workloads started via containerd, if the machine is installed with the AppArmor LSM enforced via the extraKernelArgs. Eg: ```yaml machine: install: extraKernelArgs: - security=apparmor ``` ### Bridge Interface Talos Linux now support configuring 'vlan_filtering' for bridge interfaces. ### Diagnostics Talos Linux now shows diagnostics information for common problems related to misconfiguration via `talosctl health` and Talos dashboard. ### DNS Forwarding for CoreDNS pods Usage of the host DNS resolver as upstream for Kubernetes CoreDNS pods is now enabled by default. You can disable it with: ```yaml machine: features: hostDNS: enabled: true forwardKubeDNSToHost: false ``` Please note that on running cluster you will have to kill CoreDNS pods for this change to apply. ### PCI Devices A list of PCI devices can now be obtained via `PCIDevices` resource, e.g. `talosctl get pcidevices`. ### Component Updates Kubernetes: 1.30.2 Linux: 6.6.36 containerd: 2.0.0-rc.3 runc: 1.2.0-rc.2 etcd: 3.5.14 Flannel: 0.25.3 Flannel CNI plugin: 1.5.1 Talos is built with Go 1.22.5. ### ZSTD Compression Talos Linux now compresses kernel and initramfs using ZSTD. Linux arm64 kernel is now compressed (previously it was uncompressed). ### Contributors * Andrey Smirnov * Dmitriy Matrenichev * Noel Georgi * Utku Ozdemir * Artem Chernyshev * Dmitry Sharshakov * Spencer Smith * Justin Garrison * Steve Francis * Bernard Gütermann * Konrad Eriksson * Andrew Rynhard * Attila Oláh * Birger J. Nordølum * Dennis Marttinen * Enrique Hernández Bello * Evan Johnson * Fabian Topfstedt * Grzegorz Rozniecki * Grzegorz Rożniecki * Igor Rzegocki * Jean-Francois Roy * Marcel Richter * Marco Franssen * Michael Trip * Ron Olson * Serge Logvinov * Simon-Boyer * Steve Fan * USBAkimbo * Will Bush * darox * dhaines-quera * leppeK * looklose ### Changes
160 commits

* [`0454130ad`](https://github.com/siderolabs/talos/commit/0454130ad97a61624fb0b916bf14a51dce8f199d) feat: suppress controller runtime first N failures on the console * [`3d35e5468`](https://github.com/siderolabs/talos/commit/3d35e54683b4930fa716c7afe6ecbad2af2f700b) chore: update hydrophone library * [`1f28726d4`](https://github.com/siderolabs/talos/commit/1f28726d46953262f33c91082528cd190f53b143) chore: support version with and without `v` prefix * [`9a56b8527`](https://github.com/siderolabs/talos/commit/9a56b8527b81c9653f5d01386c66ec1bde5d730a) chore(ci): fix parallel runs of tf pipelines * [`be35f380c`](https://github.com/siderolabs/talos/commit/be35f380ccf09d7667c3221765d6927546cffbca) chore: update pkgs/tools/extras * [`93df23444`](https://github.com/siderolabs/talos/commit/93df2344451e8f370f7f1d0f9590f65d6b02b936) docs: update opengraph image for main landing pages * [`d9d62d4da`](https://github.com/siderolabs/talos/commit/d9d62d4da6e30ac8f97a06dafd362a9e2ddc7006) feat: update Linux to 6.6.36 * [`6b0fe5b8c`](https://github.com/siderolabs/talos/commit/6b0fe5b8ca9aa11d195b4b66608ad179bca7be44) docs: update deploying cilium docs for v1.7 and v1.8 * [`52611a90d`](https://github.com/siderolabs/talos/commit/52611a90d870a131084375015d4d7270fa32cde8) feat: update Kubernetes to v1.30.2 * [`c19cc4ccb`](https://github.com/siderolabs/talos/commit/c19cc4ccbc8c37b6dde49853dfc442a0f5404ab4) docs: clarify direct access needed to nodes in insecure mode * [`b4c871e4b`](https://github.com/siderolabs/talos/commit/b4c871e4b74014553ab81f7ff593ff7fa736df2d) chore: bump dependencies * [`cc345c8c9`](https://github.com/siderolabs/talos/commit/cc345c8c9413692148360684390c910de9e94748) feat: add support for configuring vlan filtering on the bridge * [`2d054ad35`](https://github.com/siderolabs/talos/commit/2d054ad3551428d8b3d93c8356b38aec7e9225eb) chore: handle documents diff in `apply-config` dry run * [`bd34f71f3`](https://github.com/siderolabs/talos/commit/bd34f71f3e5eae34907951a6480e0559736bfd72) feat: add apparmor pkg * [`71857fd4d`](https://github.com/siderolabs/talos/commit/71857fd4d3a262a6b41cad3af7d3abb7355d8509) docs: fix typo: `messure` -> `measure` * [`f75f16b0a`](https://github.com/siderolabs/talos/commit/f75f16b0a8088ac47a47c9ebabdf4803db5a397e) chore(ci): fix cluster name generation * [`c603d2bf9`](https://github.com/siderolabs/talos/commit/c603d2bf9552ed169e5baf012ad44305a54056a4) chore: output more info when `ExecuteCommandInPod` fails * [`4b5a7445e`](https://github.com/siderolabs/talos/commit/4b5a7445e9c3f7f2f53e958f6c2e91a1a86c2641) docs: fix missing Akamai platform in supported matrix * [`4701498a1`](https://github.com/siderolabs/talos/commit/4701498a1b5a213816962fb1acb56192423f525f) chore(ci): run e2e-aws-nvidia with zfs extension enabled * [`86a3222ae`](https://github.com/siderolabs/talos/commit/86a3222aeecb895cab233a0cd2474189f79a6f12) chore: use new disks api for iscsi tests * [`5ffc3f14b`](https://github.com/siderolabs/talos/commit/5ffc3f14bd2b49a2ee09f36fe9e66bcf7b5283e8) feat: show siderolink status on dashboard * [`6f6a5d105`](https://github.com/siderolabs/talos/commit/6f6a5d10573028662448a57c66c2255bb7703319) chore: upgrade to rtnetlink/v2 library * [`1fb8453c2`](https://github.com/siderolabs/talos/commit/1fb8453c2db1659dd6c1670e4174125b26e777c5) chore: update Go modules * [`8e15621e8`](https://github.com/siderolabs/talos/commit/8e15621e83a1005c3b7d8d682652f984765996c1) chore(ci): add conformance pipelines * [`7fcb521a6`](https://github.com/siderolabs/talos/commit/7fcb521a6a2d14de02926489d7297cf9429c7b38) feat: use hydrophone instead of sonobuoy * [`d1a0c1f98`](https://github.com/siderolabs/talos/commit/d1a0c1f983281593b4e6a71e2110ae9f81890edc) test: fix the integration test for no META name * [`535006334`](https://github.com/siderolabs/talos/commit/5350063340a80b99a8866afb94ac8673dd4e7ace) chore: fix our dns server implementation * [`c6f90d014`](https://github.com/siderolabs/talos/commit/c6f90d01493454bcf3281c9532b61fcb7e3dbb24) chore: replace sync.Map with concurrent.HashTrieMap * [`e8ced2c2d`](https://github.com/siderolabs/talos/commit/e8ced2c2ddc9e3f61138dd566628f7d11cf90c76) chore: drop k8s timeout in the default kubeconfig * [`7cbdce73f`](https://github.com/siderolabs/talos/commit/7cbdce73f74351954e506303ed9964b9668a3b40) fix: detect CD devices, fix user disks wipe test * [`aca475c66`](https://github.com/siderolabs/talos/commit/aca475c66509fa1fa7e7a0ca1b2a29f6542637fc) chore: small usability fixes * [`26cf566dc`](https://github.com/siderolabs/talos/commit/26cf566dc8c53263cbaae72855995e418da0852b) chore: bump our coredns fork * [`5e66e117e`](https://github.com/siderolabs/talos/commit/5e66e117e2ec19527fe949bf2d689df90835d63f) fix: initial assignment of Hetzner Cloud Alias IP * [`f07b79f4a`](https://github.com/siderolabs/talos/commit/f07b79f4a8c647d358b8cd41b3704eccf0341d33) feat: provide disk detection based on new blockdevices * [`8ee087268`](https://github.com/siderolabs/talos/commit/8ee087268317a73dc240c2b7569c2dab8d9df142) chore(ci): drop crashdump, save logs as artifacts * [`7c9a14383`](https://github.com/siderolabs/talos/commit/7c9a14383ee034b05cb9bd1ff49f8078cbbf5e66) fix: volume discovery improvements * [`80ca8ff71`](https://github.com/siderolabs/talos/commit/80ca8ff7135b0950b83d2ceaa32ee1eacce049e0) fix: update the cgroups for Talos core services * [`fe317f1e1`](https://github.com/siderolabs/talos/commit/fe317f1e1611d2f48595bfaf67c5e4ea3cd692e3) docs: fix typo in QEMU guest agent support on Proxmox * [`8dbe2128a`](https://github.com/siderolabs/talos/commit/8dbe2128a909a38ead8b6dfe1cc99e1ae36078d2) feat: implement Talos diagnostics * [`357d7754f`](https://github.com/siderolabs/talos/commit/357d7754fd739e9e875d17e0f8e63c333553090e) fix: clean up VM runners on cluster destroy * [`41f92e0ba`](https://github.com/siderolabs/talos/commit/41f92e0ba46b8ad9ddc3a4eabe86be915dea6b8e) chore: update Go to 1.22.4, other updates * [`4621e9bb7`](https://github.com/siderolabs/talos/commit/4621e9bb770e2a45c7c1ea8da76cbdabf76a4671) chore: add stale and lock issue workflows * [`82d9cd322`](https://github.com/siderolabs/talos/commit/82d9cd32298431760aef67f553924e4b4f48e207) fix: add upgrade errata for arm64/zboot kernels * [`9a23d846c`](https://github.com/siderolabs/talos/commit/9a23d846c1f6a88c30ffe55d2bf5a21d6cee150e) fix: downgrade Azure IMDS required version * [`30860210c`](https://github.com/siderolabs/talos/commit/30860210cce628839e97b8ece7edf90300556ed7) test: fix hardware test not to require PCI devices * [`9fcc9b841`](https://github.com/siderolabs/talos/commit/9fcc9b84152cb186324c13e317575f6da8b7bfa6) feat: update Flannel to v0.25.3 * [`9d395b9de`](https://github.com/siderolabs/talos/commit/9d395b9de94f28fb9bf56bf795f916f783a847a0) chore: use bun instead of npm * [`a1684bdf8`](https://github.com/siderolabs/talos/commit/a1684bdf8f24858942cf61bee1efc81f7ef76f85) chore: speed up go generate for enumer * [`4dd0aa712`](https://github.com/siderolabs/talos/commit/4dd0aa7120b52cab5de219010f2b78b7dd9b73ce) feat: implement PCI device bus enumeration * [`b0466e0ab`](https://github.com/siderolabs/talos/commit/b0466e0abf2f8af43f3fb6c9661f44000fe1d54b) fix: disable kexec on GCP/Azure * [`911c25574`](https://github.com/siderolabs/talos/commit/911c255742d02440806e5f3df6967c091bb5288e) chore: fix go.work resolution * [`2f088ede0`](https://github.com/siderolabs/talos/commit/2f088ede0952d72dbb7bf33dd0510cb8ff8b8e3a) docs: add another example for installing cilium * [`3967e0777`](https://github.com/siderolabs/talos/commit/3967e07777707fa8af339f46596b678e1eaaa9f2) feat: update etcd to 3.5.14 * [`3367ded9f`](https://github.com/siderolabs/talos/commit/3367ded9feac84e9c6c1f3efcea9e61f3083b4ac) fix: correct time adjustment in `time.SyncController` * [`893e64fcb`](https://github.com/siderolabs/talos/commit/893e64fcb1f09efed990b9b642359d7bcabffd42) fix: replace `nslookup` with `dig` in integration tests * [`0359c8537`](https://github.com/siderolabs/talos/commit/0359c8537c1b3b01e94394604e16fd817b986f9e) chore: unify toml packages being used * [`4feb94ca0`](https://github.com/siderolabs/talos/commit/4feb94ca099746e3a90106522b920a77cfe77ce0) feat: add multidoc check to the Talos quirks module * [`0b4a9777f`](https://github.com/siderolabs/talos/commit/0b4a9777fc2ddcc61430db23837455ff383ba1a3) docs: update talosctl install instructions for 1.8 * [`da8305ffb`](https://github.com/siderolabs/talos/commit/da8305ffb46d285662bca12ec02760d6121342c8) test: add a test for watchdog timers * [`da7f27640`](https://github.com/siderolabs/talos/commit/da7f2764092b883bcdf5daf81b8f6f7ef997ac0a) fix: mount `tracefs` filesystem * [`7b37e5b63`](https://github.com/siderolabs/talos/commit/7b37e5b63d54c2d197336e4fbee941fa5f2423c0) chore(ci): fix integration extensions * [`de7553d77`](https://github.com/siderolabs/talos/commit/de7553d77f7e02a83f764820a71badbf0d851bc9) fix(ci): cron jobs * [`eb510d9fd`](https://github.com/siderolabs/talos/commit/eb510d9fdf3a40b2ae881e3dd19a94058d4ef529) chore: require enabled bootloader for docker provisioner * [`a9cf9b789`](https://github.com/siderolabs/talos/commit/a9cf9b78921bef76b66aa5fa5940977767124bfe) fix: correctly handle dns messages in our dns implementation * [`c2b19dcb9`](https://github.com/siderolabs/talos/commit/c2b19dcb978ab015bd9b3c5a4eb47a53ee25e297) chore: move to containerd 2.0 API * [`92a274e9a`](https://github.com/siderolabs/talos/commit/92a274e9a0a83b3e240784bf12817f08559ac8e8) fix: workaround problems with udevd races * [`31b24ea3d`](https://github.com/siderolabs/talos/commit/31b24ea3d70f88d031d81bd0f914754b0cee411e) chore(ci): split integration misc * [`8a1371337`](https://github.com/siderolabs/talos/commit/8a1371337faea406c9193e91c8de8ffc056b5135) fix: produce stable order of bonds with equinix * [`6406193f4`](https://github.com/siderolabs/talos/commit/6406193f4637157c3d31219dc2c39aca7fa736a4) test: add Equnix Metal sample metadata with two bonds * [`01ea82053`](https://github.com/siderolabs/talos/commit/01ea82053e0a2ffe4193243e235aae2ade0e2d88) fix: time sync over NTP from future era * [`5aea42427`](https://github.com/siderolabs/talos/commit/5aea4242782d4ff00ba51e85422fbdf7c2ceca64) fix(ci): fix crons by setting up buildx always * [`84706c3e2`](https://github.com/siderolabs/talos/commit/84706c3e2920b9bf68c7b6dcfb73f1e16f3f656b) docs: default to brew docs for talosctl * [`fcd65ff65`](https://github.com/siderolabs/talos/commit/fcd65ff65ce78aa5ebe7ca4b12aea2571bd54c49) feat: enable forwardKubeDNSToHost by default * [`2e64e9e4e`](https://github.com/siderolabs/talos/commit/2e64e9e4e026817f844765b4c8a7d346d85bf983) fix: require accepted CAs on worker nodes * [`23c1c4560`](https://github.com/siderolabs/talos/commit/23c1c4560ecd2084e505a64b0b701707aa79c5e6) fix(ci): fix crons fby rekres * [`2d50392c5`](https://github.com/siderolabs/talos/commit/2d50392c5a16a97a2daa47edcfd362b0891c4a06) feat: update containerd to 2.0.0-rc.2, runc to 1.2.0-rc.1 * [`a12e4bb24`](https://github.com/siderolabs/talos/commit/a12e4bb24e19701e926103753ec3ee0f98e8d3a2) chore(ci): fix github action crons * [`e7bd9cd2b`](https://github.com/siderolabs/talos/commit/e7bd9cd2bbbd337ef72adc2a3be5adc8b530cd6e) fix: decrease maximum negative ttl for dns responses * [`9c3ebad9f`](https://github.com/siderolabs/talos/commit/9c3ebad9fd7a62418fc6748364a23d27ff1c3ff7) chore(ci): kresify gh actions * [`ff60f6fde`](https://github.com/siderolabs/talos/commit/ff60f6fde6cb325b9f1f4801f658f4e9554c6c2b) refactor: make some of the extensions package public * [`ce8c86d64`](https://github.com/siderolabs/talos/commit/ce8c86d640949d24107d9057358b39c860fc1e70) fix: panic in osroot controller * [`e1711cd3c`](https://github.com/siderolabs/talos/commit/e1711cd3c9852137956f1cce7174b0a337d53b63) chore: stop using containerd package for cri namespace * [`d4307043f`](https://github.com/siderolabs/talos/commit/d4307043ffbfcadb5b67b12c95816c2a3a5819c3) fix: update go-tail library to fix 'short read' error * [`7cd13ef4a`](https://github.com/siderolabs/talos/commit/7cd13ef4a619fa5c13dc9ed147e6626ddcabbaf2) docs: add documentation on using Multus with Talos * [`4784da3ef`](https://github.com/siderolabs/talos/commit/4784da3ef88745d1ce38f1e49239c882c081e6fb) feat: use new circular buffer compressed chunks feature * [`78b48eb3a`](https://github.com/siderolabs/talos/commit/78b48eb3ae78ec9953104247ec73cafa26a61264) feat: include EDAC drivers * [`0bf2d69fb`](https://github.com/siderolabs/talos/commit/0bf2d69fbb2f2c1f693565243b46391da00d4dba) feat: update Kubernetes to 1.30.1 * [`53f548913`](https://github.com/siderolabs/talos/commit/53f54891302b193bf35ede52af235457396e91ce) fix: increase host dns packet ttl for pods * [`dedb6d360`](https://github.com/siderolabs/talos/commit/dedb6d360d25e6d00d560ddb40563c2a5a95bb1f) fix: update github.com/siderolabs/siderolink to v0.3.7 * [`43939f1a6`](https://github.com/siderolabs/talos/commit/43939f1a6e4b65cf9b64d1d09dc19df709a41275) docs: fix typos, add docker socket info * [`6663068bb`](https://github.com/siderolabs/talos/commit/6663068bbd1750fd57ddf9ca63b0f305d895b33b) chore: update project in GCP testing * [`b86edc677`](https://github.com/siderolabs/talos/commit/b86edc6776f77a65d3a254cf0f0d713ce7a9145e) chore: update office hours in talos repo * [`cfa25d22d`](https://github.com/siderolabs/talos/commit/cfa25d22dc30b877ea47ba1bfae3ca5f29977f1b) chore: remove docs prior to 1.0 from website navigation * [`120705459`](https://github.com/siderolabs/talos/commit/12070545996af3435454654500cd75a50111cca9) chore: handle I/O error for xfs_repair * [`b7afe2669`](https://github.com/siderolabs/talos/commit/b7afe2669b2a9a32ca37bbcc7a7e8af4879cf403) feat: update Linux 6.6.30 * [`26519ceed`](https://github.com/siderolabs/talos/commit/26519ceed0c790abd851de310409baf6af89e2b7) docs: update proxmox.md * [`851b91a0e`](https://github.com/siderolabs/talos/commit/851b91a0e22055443eabace9b89a566e0cbec679) fix: don't enable hostDNS for versions of Talos which do not have it * [`42ac5cd0c`](https://github.com/siderolabs/talos/commit/42ac5cd0c2ef610f055afb208384e60fc9389e82) fix: check for `nil` machine config during installation * [`1d29111d4`](https://github.com/siderolabs/talos/commit/1d29111d4310cc16078248e66817843e6e740821) chore: update Go to 1.22.3 * [`f4d7b9d9a`](https://github.com/siderolabs/talos/commit/f4d7b9d9a921cdaf33b9efdae1569dd921628270) feat: gather plaform dns names * [`0b0f9995a`](https://github.com/siderolabs/talos/commit/0b0f9995a6cd2b41f48dc867f4e0248284e53463) docs: add resource information, some grammar fixes * [`763dae250`](https://github.com/siderolabs/talos/commit/763dae2508242ee91a7e38e5962facb334691289) fix: add cluster name to the worker machine config * [`4aac5b4ec`](https://github.com/siderolabs/talos/commit/4aac5b4ec30f4a9ee0f2e4a4239b399357930b6c) feat: mount /sys/kernel/security into kubelet * [`817f18153`](https://github.com/siderolabs/talos/commit/817f18153f592f5bf38884f05aed2e4ce2fd3ad7) docs: remove mention of enabling KubePrism after v1.6 * [`c08d79732`](https://github.com/siderolabs/talos/commit/c08d797326686434dc035de3ca40200293d74701) docs: fix the variable name typo * [`478b862b4`](https://github.com/siderolabs/talos/commit/478b862b4c38bd5a5ba1313a3779f9395e4ba38d) fix: do not fail cli action tracker when boot id cannot be read * [`be510f9eb`](https://github.com/siderolabs/talos/commit/be510f9eb2b84a88ce730fab36bf575c976efa8b) docs: fix grpc_tunnel value to true * [`b7b8a8d8f`](https://github.com/siderolabs/talos/commit/b7b8a8d8fa6335d3f0036c50792971adefe5e240) docs: add logs example for the certificate errors troubleshooting * [`8df5b85ec`](https://github.com/siderolabs/talos/commit/8df5b85ec7e8ca53fd73c9c095ee5c453d5c4e51) release(v1.8.0-alpha.0): prepare release * [`07f78182c`](https://github.com/siderolabs/talos/commit/07f78182c621296e6c694b64ead8f14695b2e3b7) fix: use a fresh context for etcd unlock * [`84cd7dbec`](https://github.com/siderolabs/talos/commit/84cd7dbec4ce01a8f80a855267e1c44dfc6dcacc) feat: update Linux to 6.6.29 * [`70fdca6a4`](https://github.com/siderolabs/talos/commit/70fdca6a43abcb48030239047500fa8819f9346d) chore: update minimum hardware requirement for vmware ova * [`b690ffeb8`](https://github.com/siderolabs/talos/commit/b690ffeb899c4a133f98e212826830e3b320abe4) test: improve DNS resolver test stability * [`5aa0299b6`](https://github.com/siderolabs/talos/commit/5aa0299b6e3efefa7077aab5955526a5136b8761) style: use correct capitalization for openstack * [`4c0c626b7`](https://github.com/siderolabs/talos/commit/4c0c626b786f14c5eabdc65e88d2aae92829bf73) feat: use zstd compression in place of xz * [`98906ed6e`](https://github.com/siderolabs/talos/commit/98906ed6ea1afc5a758871a7c2d8251fccaef106) fix: use reboot delay only in case of error * [`05fd042bb`](https://github.com/siderolabs/talos/commit/05fd042bb3600541a8e2587b66b8b4c4e9f99c27) test: improve the reset integration tests * [`8cdf0f7cb`](https://github.com/siderolabs/talos/commit/8cdf0f7cb007790190197356355a16c8e427afab) docs: fix typo in Cilium instructions * [`dd1d279da`](https://github.com/siderolabs/talos/commit/dd1d279daa8c2a18c2477839b2c11e5f2f554693) fix: allow more flags in `talosctl cluster create --input-dir` * [`ef4394e58`](https://github.com/siderolabs/talos/commit/ef4394e586e42c4b5085299029a2aacb3b89502d) chore: update kernel and other packages * [`ccdb4c8b1`](https://github.com/siderolabs/talos/commit/ccdb4c8b10450aa7fb6c32b0559bda73746a03ed) chore: update google.golang.org/grpc to 1.63.2 * [`c5b59df69`](https://github.com/siderolabs/talos/commit/c5b59df6976095aca5c4bac367084874242e9e80) fix: wait for devices to be discovered before probing filesystems * [`0821b9c50`](https://github.com/siderolabs/talos/commit/0821b9c50b86bf9f7d08a1ba7b177abb7e2568c4) feat: add `--non-masquerade-cidrs` flag to `talosctl cluster create` * [`2bf613ad3`](https://github.com/siderolabs/talos/commit/2bf613ad3bd1582b520b2f661b7e0bfab4207eed) fix: add endpoints for "virtual" `host-dns` service * [`f4163aefe`](https://github.com/siderolabs/talos/commit/f4163aefeda2bf91be36af45239716c53ec982b1) fix: bump priority of OpenStack routes if IPv6 and default gateway * [`6fbd1263c`](https://github.com/siderolabs/talos/commit/6fbd1263ccbe20857cca90b5f69906651caa4f54) feat: report process MAC labels * [`d46032821`](https://github.com/siderolabs/talos/commit/d460328210ee3beea1b98ea5f23fcda5c2e2fd44) fix: return proper value from Bridge.STP instead of plain nil * [`bac1d00c3`](https://github.com/siderolabs/talos/commit/bac1d00c35cb6e1407884298118ee7b4ffc5fdfa) chore: prepare for Talos 1.8 * [`d6c8067e1`](https://github.com/siderolabs/talos/commit/d6c8067e15d8177c7394abad65b95ea98c597b9d) docs: make 1.7 docs the default * [`d7c3a0735`](https://github.com/siderolabs/talos/commit/d7c3a0735eab85dd24e86fe3e0872253067e8f10) docs: add what's new for v1.7 * [`908f67fa1`](https://github.com/siderolabs/talos/commit/908f67fa15e0de507c2f69fac0851d42376a66ce) feat: add host dns support for resolving member addrs * [`0d20b637d`](https://github.com/siderolabs/talos/commit/0d20b637d68a581354361bbceecb90395f24fedb) feat: update Kubernetes to 1.30.0 * [`ec69d7a78`](https://github.com/siderolabs/talos/commit/ec69d7a7855753e3e458f2cf7c211bf67e703220) chore: replace math/rand with math/rand/v2 * [`89040ce43`](https://github.com/siderolabs/talos/commit/89040ce4329743fa2037fb1cf65d978801753dbe) chore: update go-blockdevice/v2 library to the latest version * [`0a785802e`](https://github.com/siderolabs/talos/commit/0a785802ea22071e67d7ec85944513e73624b1ac) fix: overlay installer operations * [`b1b63f658`](https://github.com/siderolabs/talos/commit/b1b63f658eba5cbb08cbd05af959c6d397662e05) fix: mark overlay installer executable * [`3433fa13b`](https://github.com/siderolabs/talos/commit/3433fa13bf555a871e76f8ce726d5afd141a16e1) feat: use container DNS when in container mode * [`5d07ac5a7`](https://github.com/siderolabs/talos/commit/5d07ac5a7db9d2291a86ee966ee704b30afea342) fix: close apid inter-backend connections gracefully for real * [`7ba18555b`](https://github.com/siderolabs/talos/commit/7ba18555b098ba2617efce2438d6bfbec1dc0041) docs: fix typos in Akamai and AWS platform docs * [`3dd1f4e88`](https://github.com/siderolabs/talos/commit/3dd1f4e88c22734f03f7609791558b8bbbae3756) chore: extract `pkg/imager/quirks` to `pkg/machinery` * [`78bc3a433`](https://github.com/siderolabs/talos/commit/78bc3a433e8b10839034bd40b73fcc720438b943) docs: update Cilium docs * [`831f3d39e`](https://github.com/siderolabs/talos/commit/831f3d39e9b030cd1bcd3313246ebccf34f34205) feat: update Flannel to v0.25.1 * [`ea5b3ff0c`](https://github.com/siderolabs/talos/commit/ea5b3ff0c27cb033d525d172d4006e0645a924ba) feat: update Kubernetes to v1.30.0-rc.2 * [`54dac5ed4`](https://github.com/siderolabs/talos/commit/54dac5ed40698b8886096c620ac19ed55a4b99a1) feat: update Linux 6.6.24, containerd 1.7.15 * [`c51f146da`](https://github.com/siderolabs/talos/commit/c51f146daf3265bbeb4513c649938b2656ff1686) docs: update Akamai platform docs * [`9550f5ff7`](https://github.com/siderolabs/talos/commit/9550f5ff7a285df7c251df425e8f28d4c668224f) docs: fix getAuthenticationMethod and completePathFromNode docs * [`bfbd02abf`](https://github.com/siderolabs/talos/commit/bfbd02abfb1d84d14a73f1e247d62e728860d2f3) fix: assign different priority to IPv6 default gateway on OpenStack * [`c8f674bd3`](https://github.com/siderolabs/talos/commit/c8f674bd3d582f606848475bca3d22f309b2367c) test: add a test for 'spin' container runtime * [`5390ccd48`](https://github.com/siderolabs/talos/commit/5390ccd48c78e864f53cc45848772c931276380d) chore: replace []byte with string and use go:embed for templates * [`ba7cdc8c8`](https://github.com/siderolabs/talos/commit/ba7cdc8c8baf85e3015db4fa9e4446eaccf01115) chore: optimize DNSResolveCacheController * [`145f24063`](https://github.com/siderolabs/talos/commit/145f2406307e57a6f2eb1601d4f7d542d39a9f51) fix: don't modify a global map of profiles * [`6fe91ad9c`](https://github.com/siderolabs/talos/commit/6fe91ad9cf9f99401fc39a6ece24eed61f17b0e2) feat: provide Kubernets/Talos version compatibility for 1.8 * [`909a5800e`](https://github.com/siderolabs/talos/commit/909a5800e4a9ada42288ae15992579e9acf6c372) fix: generate secureboot ISO .der certificate correctly * [`b0fdc3c8c`](https://github.com/siderolabs/talos/commit/b0fdc3c8caaf6ef756cdc4440dae45891bd96d01) fix: make static pods check output consistent * [`c6ad0fcce`](https://github.com/siderolabs/talos/commit/c6ad0fcceb8220f0bf96a45e131ba999cb723f79) fix: validate that workers don't get cluster CA key * [`3735add87`](https://github.com/siderolabs/talos/commit/3735add87cec47038a88ba641322c26cd487ac58) fix: reconnect to the logs stream in dashboard after reboot * [`9aa1e1b79`](https://github.com/siderolabs/talos/commit/9aa1e1b79b4a02902e0573c10e1c0bf71a2341af) fix: present all accepted CAs to the kube-apiserver * [`336e61174`](https://github.com/siderolabs/talos/commit/336e61174624741f697c77b98dd84ab9a7a749f4) fix: close the apid connection to other machines gracefully * [`ff2c427b0`](https://github.com/siderolabs/talos/commit/ff2c427b04963d69ba2eaa1084a0a078d742b9ac) fix: pre-create nftables chain to make kubelet use nftables * [`5622f0e45`](https://github.com/siderolabs/talos/commit/5622f0e450eda589f4b9a2af28b8517d08c2aae2) docs: change localDNS to hostDNS in release notes yaml section

### Changes since v1.8.0-alpha.0
108 commits

* [`0454130ad`](https://github.com/siderolabs/talos/commit/0454130ad97a61624fb0b916bf14a51dce8f199d) feat: suppress controller runtime first N failures on the console * [`3d35e5468`](https://github.com/siderolabs/talos/commit/3d35e54683b4930fa716c7afe6ecbad2af2f700b) chore: update hydrophone library * [`1f28726d4`](https://github.com/siderolabs/talos/commit/1f28726d46953262f33c91082528cd190f53b143) chore: support version with and without `v` prefix * [`9a56b8527`](https://github.com/siderolabs/talos/commit/9a56b8527b81c9653f5d01386c66ec1bde5d730a) chore(ci): fix parallel runs of tf pipelines * [`be35f380c`](https://github.com/siderolabs/talos/commit/be35f380ccf09d7667c3221765d6927546cffbca) chore: update pkgs/tools/extras * [`93df23444`](https://github.com/siderolabs/talos/commit/93df2344451e8f370f7f1d0f9590f65d6b02b936) docs: update opengraph image for main landing pages * [`d9d62d4da`](https://github.com/siderolabs/talos/commit/d9d62d4da6e30ac8f97a06dafd362a9e2ddc7006) feat: update Linux to 6.6.36 * [`6b0fe5b8c`](https://github.com/siderolabs/talos/commit/6b0fe5b8ca9aa11d195b4b66608ad179bca7be44) docs: update deploying cilium docs for v1.7 and v1.8 * [`52611a90d`](https://github.com/siderolabs/talos/commit/52611a90d870a131084375015d4d7270fa32cde8) feat: update Kubernetes to v1.30.2 * [`c19cc4ccb`](https://github.com/siderolabs/talos/commit/c19cc4ccbc8c37b6dde49853dfc442a0f5404ab4) docs: clarify direct access needed to nodes in insecure mode * [`b4c871e4b`](https://github.com/siderolabs/talos/commit/b4c871e4b74014553ab81f7ff593ff7fa736df2d) chore: bump dependencies * [`cc345c8c9`](https://github.com/siderolabs/talos/commit/cc345c8c9413692148360684390c910de9e94748) feat: add support for configuring vlan filtering on the bridge * [`2d054ad35`](https://github.com/siderolabs/talos/commit/2d054ad3551428d8b3d93c8356b38aec7e9225eb) chore: handle documents diff in `apply-config` dry run * [`bd34f71f3`](https://github.com/siderolabs/talos/commit/bd34f71f3e5eae34907951a6480e0559736bfd72) feat: add apparmor pkg * [`71857fd4d`](https://github.com/siderolabs/talos/commit/71857fd4d3a262a6b41cad3af7d3abb7355d8509) docs: fix typo: `messure` -> `measure` * [`f75f16b0a`](https://github.com/siderolabs/talos/commit/f75f16b0a8088ac47a47c9ebabdf4803db5a397e) chore(ci): fix cluster name generation * [`c603d2bf9`](https://github.com/siderolabs/talos/commit/c603d2bf9552ed169e5baf012ad44305a54056a4) chore: output more info when `ExecuteCommandInPod` fails * [`4b5a7445e`](https://github.com/siderolabs/talos/commit/4b5a7445e9c3f7f2f53e958f6c2e91a1a86c2641) docs: fix missing Akamai platform in supported matrix * [`4701498a1`](https://github.com/siderolabs/talos/commit/4701498a1b5a213816962fb1acb56192423f525f) chore(ci): run e2e-aws-nvidia with zfs extension enabled * [`86a3222ae`](https://github.com/siderolabs/talos/commit/86a3222aeecb895cab233a0cd2474189f79a6f12) chore: use new disks api for iscsi tests * [`5ffc3f14b`](https://github.com/siderolabs/talos/commit/5ffc3f14bd2b49a2ee09f36fe9e66bcf7b5283e8) feat: show siderolink status on dashboard * [`6f6a5d105`](https://github.com/siderolabs/talos/commit/6f6a5d10573028662448a57c66c2255bb7703319) chore: upgrade to rtnetlink/v2 library * [`1fb8453c2`](https://github.com/siderolabs/talos/commit/1fb8453c2db1659dd6c1670e4174125b26e777c5) chore: update Go modules * [`8e15621e8`](https://github.com/siderolabs/talos/commit/8e15621e83a1005c3b7d8d682652f984765996c1) chore(ci): add conformance pipelines * [`7fcb521a6`](https://github.com/siderolabs/talos/commit/7fcb521a6a2d14de02926489d7297cf9429c7b38) feat: use hydrophone instead of sonobuoy * [`d1a0c1f98`](https://github.com/siderolabs/talos/commit/d1a0c1f983281593b4e6a71e2110ae9f81890edc) test: fix the integration test for no META name * [`535006334`](https://github.com/siderolabs/talos/commit/5350063340a80b99a8866afb94ac8673dd4e7ace) chore: fix our dns server implementation * [`c6f90d014`](https://github.com/siderolabs/talos/commit/c6f90d01493454bcf3281c9532b61fcb7e3dbb24) chore: replace sync.Map with concurrent.HashTrieMap * [`e8ced2c2d`](https://github.com/siderolabs/talos/commit/e8ced2c2ddc9e3f61138dd566628f7d11cf90c76) chore: drop k8s timeout in the default kubeconfig * [`7cbdce73f`](https://github.com/siderolabs/talos/commit/7cbdce73f74351954e506303ed9964b9668a3b40) fix: detect CD devices, fix user disks wipe test * [`aca475c66`](https://github.com/siderolabs/talos/commit/aca475c66509fa1fa7e7a0ca1b2a29f6542637fc) chore: small usability fixes * [`26cf566dc`](https://github.com/siderolabs/talos/commit/26cf566dc8c53263cbaae72855995e418da0852b) chore: bump our coredns fork * [`5e66e117e`](https://github.com/siderolabs/talos/commit/5e66e117e2ec19527fe949bf2d689df90835d63f) fix: initial assignment of Hetzner Cloud Alias IP * [`f07b79f4a`](https://github.com/siderolabs/talos/commit/f07b79f4a8c647d358b8cd41b3704eccf0341d33) feat: provide disk detection based on new blockdevices * [`8ee087268`](https://github.com/siderolabs/talos/commit/8ee087268317a73dc240c2b7569c2dab8d9df142) chore(ci): drop crashdump, save logs as artifacts * [`7c9a14383`](https://github.com/siderolabs/talos/commit/7c9a14383ee034b05cb9bd1ff49f8078cbbf5e66) fix: volume discovery improvements * [`80ca8ff71`](https://github.com/siderolabs/talos/commit/80ca8ff7135b0950b83d2ceaa32ee1eacce049e0) fix: update the cgroups for Talos core services * [`fe317f1e1`](https://github.com/siderolabs/talos/commit/fe317f1e1611d2f48595bfaf67c5e4ea3cd692e3) docs: fix typo in QEMU guest agent support on Proxmox * [`8dbe2128a`](https://github.com/siderolabs/talos/commit/8dbe2128a909a38ead8b6dfe1cc99e1ae36078d2) feat: implement Talos diagnostics * [`357d7754f`](https://github.com/siderolabs/talos/commit/357d7754fd739e9e875d17e0f8e63c333553090e) fix: clean up VM runners on cluster destroy * [`41f92e0ba`](https://github.com/siderolabs/talos/commit/41f92e0ba46b8ad9ddc3a4eabe86be915dea6b8e) chore: update Go to 1.22.4, other updates * [`4621e9bb7`](https://github.com/siderolabs/talos/commit/4621e9bb770e2a45c7c1ea8da76cbdabf76a4671) chore: add stale and lock issue workflows * [`82d9cd322`](https://github.com/siderolabs/talos/commit/82d9cd32298431760aef67f553924e4b4f48e207) fix: add upgrade errata for arm64/zboot kernels * [`9a23d846c`](https://github.com/siderolabs/talos/commit/9a23d846c1f6a88c30ffe55d2bf5a21d6cee150e) fix: downgrade Azure IMDS required version * [`30860210c`](https://github.com/siderolabs/talos/commit/30860210cce628839e97b8ece7edf90300556ed7) test: fix hardware test not to require PCI devices * [`9fcc9b841`](https://github.com/siderolabs/talos/commit/9fcc9b84152cb186324c13e317575f6da8b7bfa6) feat: update Flannel to v0.25.3 * [`9d395b9de`](https://github.com/siderolabs/talos/commit/9d395b9de94f28fb9bf56bf795f916f783a847a0) chore: use bun instead of npm * [`a1684bdf8`](https://github.com/siderolabs/talos/commit/a1684bdf8f24858942cf61bee1efc81f7ef76f85) chore: speed up go generate for enumer * [`4dd0aa712`](https://github.com/siderolabs/talos/commit/4dd0aa7120b52cab5de219010f2b78b7dd9b73ce) feat: implement PCI device bus enumeration * [`b0466e0ab`](https://github.com/siderolabs/talos/commit/b0466e0abf2f8af43f3fb6c9661f44000fe1d54b) fix: disable kexec on GCP/Azure * [`911c25574`](https://github.com/siderolabs/talos/commit/911c255742d02440806e5f3df6967c091bb5288e) chore: fix go.work resolution * [`2f088ede0`](https://github.com/siderolabs/talos/commit/2f088ede0952d72dbb7bf33dd0510cb8ff8b8e3a) docs: add another example for installing cilium * [`3967e0777`](https://github.com/siderolabs/talos/commit/3967e07777707fa8af339f46596b678e1eaaa9f2) feat: update etcd to 3.5.14 * [`3367ded9f`](https://github.com/siderolabs/talos/commit/3367ded9feac84e9c6c1f3efcea9e61f3083b4ac) fix: correct time adjustment in `time.SyncController` * [`893e64fcb`](https://github.com/siderolabs/talos/commit/893e64fcb1f09efed990b9b642359d7bcabffd42) fix: replace `nslookup` with `dig` in integration tests * [`0359c8537`](https://github.com/siderolabs/talos/commit/0359c8537c1b3b01e94394604e16fd817b986f9e) chore: unify toml packages being used * [`4feb94ca0`](https://github.com/siderolabs/talos/commit/4feb94ca099746e3a90106522b920a77cfe77ce0) feat: add multidoc check to the Talos quirks module * [`0b4a9777f`](https://github.com/siderolabs/talos/commit/0b4a9777fc2ddcc61430db23837455ff383ba1a3) docs: update talosctl install instructions for 1.8 * [`da8305ffb`](https://github.com/siderolabs/talos/commit/da8305ffb46d285662bca12ec02760d6121342c8) test: add a test for watchdog timers * [`da7f27640`](https://github.com/siderolabs/talos/commit/da7f2764092b883bcdf5daf81b8f6f7ef997ac0a) fix: mount `tracefs` filesystem * [`7b37e5b63`](https://github.com/siderolabs/talos/commit/7b37e5b63d54c2d197336e4fbee941fa5f2423c0) chore(ci): fix integration extensions * [`de7553d77`](https://github.com/siderolabs/talos/commit/de7553d77f7e02a83f764820a71badbf0d851bc9) fix(ci): cron jobs * [`eb510d9fd`](https://github.com/siderolabs/talos/commit/eb510d9fdf3a40b2ae881e3dd19a94058d4ef529) chore: require enabled bootloader for docker provisioner * [`a9cf9b789`](https://github.com/siderolabs/talos/commit/a9cf9b78921bef76b66aa5fa5940977767124bfe) fix: correctly handle dns messages in our dns implementation * [`c2b19dcb9`](https://github.com/siderolabs/talos/commit/c2b19dcb978ab015bd9b3c5a4eb47a53ee25e297) chore: move to containerd 2.0 API * [`92a274e9a`](https://github.com/siderolabs/talos/commit/92a274e9a0a83b3e240784bf12817f08559ac8e8) fix: workaround problems with udevd races * [`31b24ea3d`](https://github.com/siderolabs/talos/commit/31b24ea3d70f88d031d81bd0f914754b0cee411e) chore(ci): split integration misc * [`8a1371337`](https://github.com/siderolabs/talos/commit/8a1371337faea406c9193e91c8de8ffc056b5135) fix: produce stable order of bonds with equinix * [`6406193f4`](https://github.com/siderolabs/talos/commit/6406193f4637157c3d31219dc2c39aca7fa736a4) test: add Equnix Metal sample metadata with two bonds * [`01ea82053`](https://github.com/siderolabs/talos/commit/01ea82053e0a2ffe4193243e235aae2ade0e2d88) fix: time sync over NTP from future era * [`5aea42427`](https://github.com/siderolabs/talos/commit/5aea4242782d4ff00ba51e85422fbdf7c2ceca64) fix(ci): fix crons by setting up buildx always * [`84706c3e2`](https://github.com/siderolabs/talos/commit/84706c3e2920b9bf68c7b6dcfb73f1e16f3f656b) docs: default to brew docs for talosctl * [`fcd65ff65`](https://github.com/siderolabs/talos/commit/fcd65ff65ce78aa5ebe7ca4b12aea2571bd54c49) feat: enable forwardKubeDNSToHost by default * [`2e64e9e4e`](https://github.com/siderolabs/talos/commit/2e64e9e4e026817f844765b4c8a7d346d85bf983) fix: require accepted CAs on worker nodes * [`23c1c4560`](https://github.com/siderolabs/talos/commit/23c1c4560ecd2084e505a64b0b701707aa79c5e6) fix(ci): fix crons fby rekres * [`2d50392c5`](https://github.com/siderolabs/talos/commit/2d50392c5a16a97a2daa47edcfd362b0891c4a06) feat: update containerd to 2.0.0-rc.2, runc to 1.2.0-rc.1 * [`a12e4bb24`](https://github.com/siderolabs/talos/commit/a12e4bb24e19701e926103753ec3ee0f98e8d3a2) chore(ci): fix github action crons * [`e7bd9cd2b`](https://github.com/siderolabs/talos/commit/e7bd9cd2bbbd337ef72adc2a3be5adc8b530cd6e) fix: decrease maximum negative ttl for dns responses * [`9c3ebad9f`](https://github.com/siderolabs/talos/commit/9c3ebad9fd7a62418fc6748364a23d27ff1c3ff7) chore(ci): kresify gh actions * [`ff60f6fde`](https://github.com/siderolabs/talos/commit/ff60f6fde6cb325b9f1f4801f658f4e9554c6c2b) refactor: make some of the extensions package public * [`ce8c86d64`](https://github.com/siderolabs/talos/commit/ce8c86d640949d24107d9057358b39c860fc1e70) fix: panic in osroot controller * [`e1711cd3c`](https://github.com/siderolabs/talos/commit/e1711cd3c9852137956f1cce7174b0a337d53b63) chore: stop using containerd package for cri namespace * [`d4307043f`](https://github.com/siderolabs/talos/commit/d4307043ffbfcadb5b67b12c95816c2a3a5819c3) fix: update go-tail library to fix 'short read' error * [`7cd13ef4a`](https://github.com/siderolabs/talos/commit/7cd13ef4a619fa5c13dc9ed147e6626ddcabbaf2) docs: add documentation on using Multus with Talos * [`4784da3ef`](https://github.com/siderolabs/talos/commit/4784da3ef88745d1ce38f1e49239c882c081e6fb) feat: use new circular buffer compressed chunks feature * [`78b48eb3a`](https://github.com/siderolabs/talos/commit/78b48eb3ae78ec9953104247ec73cafa26a61264) feat: include EDAC drivers * [`0bf2d69fb`](https://github.com/siderolabs/talos/commit/0bf2d69fbb2f2c1f693565243b46391da00d4dba) feat: update Kubernetes to 1.30.1 * [`53f548913`](https://github.com/siderolabs/talos/commit/53f54891302b193bf35ede52af235457396e91ce) fix: increase host dns packet ttl for pods * [`dedb6d360`](https://github.com/siderolabs/talos/commit/dedb6d360d25e6d00d560ddb40563c2a5a95bb1f) fix: update github.com/siderolabs/siderolink to v0.3.7 * [`43939f1a6`](https://github.com/siderolabs/talos/commit/43939f1a6e4b65cf9b64d1d09dc19df709a41275) docs: fix typos, add docker socket info * [`6663068bb`](https://github.com/siderolabs/talos/commit/6663068bbd1750fd57ddf9ca63b0f305d895b33b) chore: update project in GCP testing * [`b86edc677`](https://github.com/siderolabs/talos/commit/b86edc6776f77a65d3a254cf0f0d713ce7a9145e) chore: update office hours in talos repo * [`cfa25d22d`](https://github.com/siderolabs/talos/commit/cfa25d22dc30b877ea47ba1bfae3ca5f29977f1b) chore: remove docs prior to 1.0 from website navigation * [`120705459`](https://github.com/siderolabs/talos/commit/12070545996af3435454654500cd75a50111cca9) chore: handle I/O error for xfs_repair * [`b7afe2669`](https://github.com/siderolabs/talos/commit/b7afe2669b2a9a32ca37bbcc7a7e8af4879cf403) feat: update Linux 6.6.30 * [`26519ceed`](https://github.com/siderolabs/talos/commit/26519ceed0c790abd851de310409baf6af89e2b7) docs: update proxmox.md * [`851b91a0e`](https://github.com/siderolabs/talos/commit/851b91a0e22055443eabace9b89a566e0cbec679) fix: don't enable hostDNS for versions of Talos which do not have it * [`42ac5cd0c`](https://github.com/siderolabs/talos/commit/42ac5cd0c2ef610f055afb208384e60fc9389e82) fix: check for `nil` machine config during installation * [`1d29111d4`](https://github.com/siderolabs/talos/commit/1d29111d4310cc16078248e66817843e6e740821) chore: update Go to 1.22.3 * [`f4d7b9d9a`](https://github.com/siderolabs/talos/commit/f4d7b9d9a921cdaf33b9efdae1569dd921628270) feat: gather plaform dns names * [`0b0f9995a`](https://github.com/siderolabs/talos/commit/0b0f9995a6cd2b41f48dc867f4e0248284e53463) docs: add resource information, some grammar fixes * [`763dae250`](https://github.com/siderolabs/talos/commit/763dae2508242ee91a7e38e5962facb334691289) fix: add cluster name to the worker machine config * [`4aac5b4ec`](https://github.com/siderolabs/talos/commit/4aac5b4ec30f4a9ee0f2e4a4239b399357930b6c) feat: mount /sys/kernel/security into kubelet * [`817f18153`](https://github.com/siderolabs/talos/commit/817f18153f592f5bf38884f05aed2e4ce2fd3ad7) docs: remove mention of enabling KubePrism after v1.6 * [`c08d79732`](https://github.com/siderolabs/talos/commit/c08d797326686434dc035de3ca40200293d74701) docs: fix the variable name typo * [`478b862b4`](https://github.com/siderolabs/talos/commit/478b862b4c38bd5a5ba1313a3779f9395e4ba38d) fix: do not fail cli action tracker when boot id cannot be read * [`be510f9eb`](https://github.com/siderolabs/talos/commit/be510f9eb2b84a88ce730fab36bf575c976efa8b) docs: fix grpc_tunnel value to true * [`b7b8a8d8f`](https://github.com/siderolabs/talos/commit/b7b8a8d8fa6335d3f0036c50792971adefe5e240) docs: add logs example for the certificate errors troubleshooting

### Changes from siderolabs/discovery-client
2 commits

* [`ca662d2`](https://github.com/siderolabs/discovery-client/commit/ca662d218418eb50eb22d84560c290bef4369702) feat: export default GRPC dial options for the client * [`7a767fa`](https://github.com/siderolabs/discovery-client/commit/7a767fa89005209f5f39b2f5891ca7b169f52d89) chore: bump Go, deps and rekres

### Changes from siderolabs/extras
4 commits

* [`cab51d8`](https://github.com/siderolabs/extras/commit/cab51d8f49fec77266b74d2535f61bf73bb8b2c4) feat: update dependencies * [`0efb05f`](https://github.com/siderolabs/extras/commit/0efb05f989d7e745f61955570992c54094d3fddf) feat: update Go to 1.22.4 * [`01ad9f5`](https://github.com/siderolabs/extras/commit/01ad9f5e2aa7e0ef2b6d9e0a19e7bf6a39dd5d94) feat: update Go to 1.22.3 * [`fa6663c`](https://github.com/siderolabs/extras/commit/fa6663c2abf90d82667a6c33cbc6f5edb2d1c525) feat: update Go to 1.22.2

### Changes from siderolabs/gen
2 commits

* [`7654108`](https://github.com/siderolabs/gen/commit/7654108fe6ae15d4765584342709bc0bced6b3d6) chore: add hashtriemap implementation * [`8485864`](https://github.com/siderolabs/gen/commit/84858640dc9c3032219380885283b995d4f2b0d1) chore: optimize maps.Values and maps.Keys

### Changes from siderolabs/go-api-signature
1 commit

* [`782aac0`](https://github.com/siderolabs/go-api-signature/commit/782aac0d69752fe7c6eba36bae8d1383ffdc0b04) chore: bump deps

### Changes from siderolabs/go-circular
3 commits

* [`cbce5c3`](https://github.com/siderolabs/go-circular/commit/cbce5c3e47d1c6a26a588cbb6f77af2f9bc3e5b7) feat: add persistence support * [`3c48c53`](https://github.com/siderolabs/go-circular/commit/3c48c53c1449b2b5e5ddde14e0351d93a351b021) feat: implement extra compressed chunks * [`835f04c`](https://github.com/siderolabs/go-circular/commit/835f04c9ba6083ef451b5bbba748200202d1a0a9) chore: rekres, update dependencies

### Changes from siderolabs/go-loadbalancer
1 commit

* [`0639758`](https://github.com/siderolabs/go-loadbalancer/commit/0639758a06785c0c8c65e18774b81d85ab40acdf) chore: bump deps

### Changes from siderolabs/go-pcidb
1 commit

* [`2e79017`](https://github.com/siderolabs/go-pcidb/commit/2e7901711733e2d7e5e5a767a68cae08df148dc5) feat: rekres, update PCI IDs

### Changes from siderolabs/go-tail
1 commit

* [`7cb7294`](https://github.com/siderolabs/go-tail/commit/7cb7294b8af33175bc463c84493776e6e4da9c4f) fix: remove unexpected short read error

### Changes from siderolabs/go-talos-support
1 commit

* [`69891cf`](https://github.com/siderolabs/go-talos-support/commit/69891cf046628969e651fc751e433aad86ec22c4) chore: remove containerd dependency

### Changes from siderolabs/pkgs
36 commits

* [`ce49757`](https://github.com/siderolabs/pkgs/commit/ce497578fd6911be16848df71156558565616ac1) feat: update flannel-cni plugin to v1.5.1 * [`289ed6b`](https://github.com/siderolabs/pkgs/commit/289ed6ba2de66c7230b154df9ca65581f7619055) feat: bump deps * [`8d6b19a`](https://github.com/siderolabs/pkgs/commit/8d6b19a8a15c6f0b8b76c0dc65657d10830bbf3a) feat: update Linux to 6.6.36 * [`b671d46`](https://github.com/siderolabs/pkgs/commit/b671d4604db736c7ac541c40ba2c5deeaf03baee) feat: update containerd/runc to the next rc versions * [`c7e9591`](https://github.com/siderolabs/pkgs/commit/c7e9591dcdd18f94a391a329789fa2ddf93a509f) feat: enable CONFIG_X86_AMD_PSTATE * [`84bad89`](https://github.com/siderolabs/pkgs/commit/84bad890a6eed3b1fa2d01df494c26e695d5a290) feat: add 'apparmor' package * [`4d9869a`](https://github.com/siderolabs/pkgs/commit/4d9869a06f06cab4ed56b42b93974804f33b6435) feat: update Linux to 6.6.33 * [`e5990e8`](https://github.com/siderolabs/pkgs/commit/e5990e87dc8e491adbe42df246f607eddd25af94) feat: enable CONFIG_KSM * [`a37f382`](https://github.com/siderolabs/pkgs/commit/a37f382b8c11a478d1015b9fd1042257684529bc) fix: network for Rockchip boards like Rock64 * [`95218c7`](https://github.com/siderolabs/pkgs/commit/95218c7868047d7075465fb4e112975460acff00) fix: enable PAGE_TABLE_CHECK * [`cbd9cd7`](https://github.com/siderolabs/pkgs/commit/cbd9cd79a73ada392bc03f04dca2a982878ce2b6) feat: enable SCTP support * [`c309452`](https://github.com/siderolabs/pkgs/commit/c309452aefee22fbc3d714781b4cc880881e0a5d) feat: bump dependencies * [`3a56032`](https://github.com/siderolabs/pkgs/commit/3a56032bf8e49296cf4a02655925767ab9c8b1d2) chore: rekres * [`db7f60c`](https://github.com/siderolabs/pkgs/commit/db7f60c77b2effcfc5640fd50b871052e842b1eb) feat: bump Linux to 6.6.32 * [`c647a05`](https://github.com/siderolabs/pkgs/commit/c647a0591741916e4bc28c35dc6a9cc36add65e0) feat: update ipxe to the latest * [`f350879`](https://github.com/siderolabs/pkgs/commit/f350879ba82443c662582d1b43e6d9fc06826c55) feat: update containerd to 2.0.0-rc.2, runc to 1.2.0-rc.1 * [`f8392fb`](https://github.com/siderolabs/pkgs/commit/f8392fb597559eaf3e12c4284acc7805667e7f8e) feat: update Linux firmware to 20240513 * [`f414bbd`](https://github.com/siderolabs/pkgs/commit/f414bbdb189e3ab880ee65efe2a030667aae77ec) fix: disable CONFIG_EFI_DISABLE_PCI_DMA option * [`9ebfd1b`](https://github.com/siderolabs/pkgs/commit/9ebfd1b90ed674a984eb69f03b6bc79f21573313) feat: enable EDAC drivers * [`f9559de`](https://github.com/siderolabs/pkgs/commit/f9559de4cb7961bd54745ddeb0ffb3414f7125aa) fix: drbd module installation * [`492638d`](https://github.com/siderolabs/pkgs/commit/492638d5d8242d733da4cf2a573380be1e780f2f) feat: update dependencies * [`bd70572`](https://github.com/siderolabs/pkgs/commit/bd70572339f6cc28dd88d0e4e28f079299268c8b) feat: update Go to 1.22.3 * [`edb600a`](https://github.com/siderolabs/pkgs/commit/edb600aa02ff620217cc430bdc4a699d9c9eba82) feat: update zfs package to v2.2.4 * [`6775002`](https://github.com/siderolabs/pkgs/commit/67750020042162af7fc01e5f14a678fc6eeaaf6b) feat: enable NFT FIB lookups * [`28c5696`](https://github.com/siderolabs/pkgs/commit/28c5696e7c97b12765e65bd1bb758f8cb19e6adc) feat: update Linux to 6.6.29 * [`9c8a02c`](https://github.com/siderolabs/pkgs/commit/9c8a02c234b52cf3624ebf79f7e76065cbc1eeff) feat: update containerd to 1.7.16 * [`ca6249b`](https://github.com/siderolabs/pkgs/commit/ca6249b4b7d00b6f16e1a7264f55a4814300df63) feat: compress amd64 Linux kernel using zstd * [`718a7da`](https://github.com/siderolabs/pkgs/commit/718a7da83fe843cd59745078fe1a814c75bc4384) feat: enable SELinux * [`207481f`](https://github.com/siderolabs/pkgs/commit/207481f7b16d2b0c98053432f4ad86484bf0b1ec) feat(intel): add support for power management and ACPI options for Intel CPUs * [`dfa7dce`](https://github.com/siderolabs/pkgs/commit/dfa7dceb5ae50af454f527ac7c774c93d00054cf) feat: update Linux to 6.6.28 * [`7b30b61`](https://github.com/siderolabs/pkgs/commit/7b30b61ef3ba104f3ea21469632d3d043c5fd6f6) fix: use proper EFI zBoot image * [`010913b`](https://github.com/siderolabs/pkgs/commit/010913b8bf2b7c7df2d16efcdf23a4efbb9913ab) feat: update Linux 6.6.26, containerd 1.7.15 * [`da397fa`](https://github.com/siderolabs/pkgs/commit/da397fa0e55284f466af982f98cf93e7075e6298) feat: enable BFQ IO scheduler * [`c839801`](https://github.com/siderolabs/pkgs/commit/c83980113db4aabbda4393d7aa8e6ab734a6069b) feat: enable zboot on arm64 with zstd compression * [`1b28e2c`](https://github.com/siderolabs/pkgs/commit/1b28e2ce58e5702bcbbd5ed13fbd7cf6420dc12d) feat: go 1.22.2, Linux 6.6.24 * [`05db2a8`](https://github.com/siderolabs/pkgs/commit/05db2a88e6985470f4e7dc6b21fbdd9df1e63aea) fix: revert musl to 1.2.4

### Changes from siderolabs/siderolink
4 commits

* [`e76747b`](https://github.com/siderolabs/siderolink/commit/e76747ba523b336ab8b9143293c920ff64bc4f14) chore: migrate to rtnetlink/2 * [`3a587fc`](https://github.com/siderolabs/siderolink/commit/3a587fcf9dbb259e216495496a523faaea427d04) fix: do not ever skip updates which have remove flag * [`be00ff5`](https://github.com/siderolabs/siderolink/commit/be00ff59bac50e0da4cd0747f8e5f30c7b029ded) chore: redo event filtering as a sequence of iterators * [`a936b60`](https://github.com/siderolabs/siderolink/commit/a936b60645267d2e7320083b402df5ad19de76f5) chore: handle peer events in batches

### Changes from siderolabs/tools
7 commits

* [`31ad71b`](https://github.com/siderolabs/tools/commit/31ad71bdb3b2b33ab1c74175ffc1eff0cae33866) feat: update dependencies * [`d2746e5`](https://github.com/siderolabs/tools/commit/d2746e5a7a60a22ad957c8bc04831bae8c191af6) feat: update Go to 1.22.4 * [`06ba64e`](https://github.com/siderolabs/tools/commit/06ba64ec3044c9c4ea51b8a624c46503a4f5fe26) feat: update dependencies * [`7e5a248`](https://github.com/siderolabs/tools/commit/7e5a2482284e00f60cd44a5d155fcdf2291f1fc9) feat: update dependencies * [`c34ec5b`](https://github.com/siderolabs/tools/commit/c34ec5bfd44faa4a5ccced07136246fb25858635) feat: update Go to 1.22.3 * [`3c25a6f`](https://github.com/siderolabs/tools/commit/3c25a6f164f3004d222bb13f5b663e01b80ff882) fix: update pkg-config configure flag * [`bd405ff`](https://github.com/siderolabs/tools/commit/bd405ff5d8d511eeef17f0a6126ad6cdd3a849bb) feat: update go to 1.22.2

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.3 -> v0.4.0 * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.11.1 -> v1.12.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.5.1 -> v1.7.0 * **github.com/aws/aws-sdk-go-v2/config** v1.27.10 -> v1.27.23 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.16.1 -> v1.16.9 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.30.1 -> v1.35.1 * **github.com/aws/smithy-go** v1.20.2 -> v1.20.3 * **github.com/beevik/ntp** v1.3.1 -> v1.4.3 * **github.com/containerd/containerd/api** v1.8.0-rc.2 **_new_** * **github.com/containerd/containerd/v2** v2.0.0-rc.3 **_new_** * **github.com/containerd/errdefs** v0.1.0 **_new_** * **github.com/containerd/platforms** v0.2.1 **_new_** * **github.com/containernetworking/cni** v1.1.2 -> v1.2.2 * **github.com/containernetworking/plugins** v1.4.1 -> v1.5.1 * **github.com/cosi-project/runtime** v0.4.1 -> v0.5.0 * **github.com/docker/docker** v26.0.0 -> v27.0.3 * **github.com/fatih/color** v1.16.0 -> v1.17.0 * **github.com/foxboron/go-uefi** 48be911532c2 -> 205d5597883a * **github.com/google/go-containerregistry** v0.19.1 -> v0.19.2 * **github.com/google/go-tpm** ee6cbcd136f8 -> v0.9.1 * **github.com/hashicorp/go-getter/v2** v2.2.1 -> v2.2.2 * **github.com/hetznercloud/hcloud-go/v2** v2.7.0 -> v2.10.2 * **github.com/insomniacslk/dhcp** c728f5dd21c8 -> bf3278ac95c1 * **github.com/jsimonetti/rtnetlink/v2** v2.0.2 **_new_** * **github.com/klauspost/compress** v1.17.9 **_new_** * **github.com/klauspost/cpuid/v2** v2.2.7 -> v2.2.8 * **github.com/miekg/dns** v1.1.58 -> v1.1.61 * **github.com/pelletier/go-toml/v2** v2.2.2 **_new_** * **github.com/prometheus/procfs** v0.13.0 -> v0.15.1 * **github.com/rivo/tview** a22293bda944 -> b0a7293b8130 * **github.com/safchain/ethtool** v0.3.0 -> v0.4.1 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.25 -> v1.0.0-beta.28 * **github.com/siderolabs/discovery-client** v0.1.8 -> v0.1.9 * **github.com/siderolabs/extras** v1.7.0-1-gbb76755 -> v1.8.0-alpha.0-3-gcab51d8 * **github.com/siderolabs/gen** v0.4.8 -> v0.5.0 * **github.com/siderolabs/go-api-signature** v0.3.2 -> v0.3.3 * **github.com/siderolabs/go-blockdevice/v2** 3265299b0192 -> f4a4030394f4 * **github.com/siderolabs/go-circular** v0.1.0 -> v0.2.0 * **github.com/siderolabs/go-loadbalancer** v0.3.3 -> v0.3.4 * **github.com/siderolabs/go-pcidb** v0.2.0 -> v0.3.0 * **github.com/siderolabs/go-tail** v0.1.0 -> v0.1.1 * **github.com/siderolabs/go-talos-support** v0.1.0 -> 69891cf04662 * **github.com/siderolabs/pkgs** v1.7.0-6-g29106c0 -> v1.8.0-alpha.0-34-gce49757 * **github.com/siderolabs/siderolink** v0.3.5 -> v0.3.9 * **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> e1711cd3c985 * **github.com/siderolabs/tools** v1.7.0-1-g10b2a69 -> v1.8.0-alpha.0-6-g31ad71b * **github.com/spf13/cobra** v1.8.0 -> v1.8.1 * **go.etcd.io/etcd/api/v3** v3.5.13 -> v3.5.14 * **go.etcd.io/etcd/client/pkg/v3** v3.5.13 -> v3.5.14 * **go.etcd.io/etcd/client/v3** v3.5.13 -> v3.5.14 * **go.etcd.io/etcd/etcdutl/v3** v3.5.13 -> v3.5.14 * **golang.org/x/net** v0.23.0 -> v0.26.0 * **golang.org/x/oauth2** v0.18.0 -> v0.21.0 * **golang.org/x/sync** v0.6.0 -> v0.7.0 * **golang.org/x/sys** v0.18.0 -> v0.21.0 * **golang.org/x/term** v0.18.0 -> v0.21.0 * **golang.org/x/text** v0.14.0 -> v0.16.0 * **google.golang.org/grpc** v1.62.1 -> v1.64.0 * **google.golang.org/protobuf** v1.33.0 -> v1.34.2 * **k8s.io/api** v0.30.0 -> v0.30.2 * **k8s.io/apiserver** v0.30.0 -> v0.30.2 * **k8s.io/client-go** v0.30.0 -> v0.30.2 * **k8s.io/component-base** v0.30.0 -> v0.30.2 * **k8s.io/cri-api** v0.30.0 -> 3a66d9d86654 * **k8s.io/klog/v2** v2.120.1 -> v2.130.1 * **k8s.io/kube-scheduler** v0.30.0 -> v0.30.2 * **k8s.io/kubectl** v0.30.0 -> v0.30.2 * **k8s.io/kubelet** v0.30.0 -> v0.30.2 * **k8s.io/pod-security-admission** v0.30.0 -> v0.30.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.69 -> v1.2.70 * **sigs.k8s.io/hydrophone** 91065c9fe3a5 **_new_** Previous release can be found at [v1.7.0](https://github.com/siderolabs/talos/releases/tag/v1.7.0) ## [Talos 1.8.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.8.0-alpha.0) (2024-05-01) Welcome to the v1.8.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates Linux: 6.6.29 containerd: 1.7.16 Talos is built with Go 1.22.2. ### ZSTD Compression Talos Linux now compresses kernel and initramfs using ZSTD. Linux arm64 kernel is now compressed (previously it was uncompressed). ### Contributors * Andrey Smirnov * Dmitriy Matrenichev * Utku Ozdemir * Dmitry Sharshakov * Artem Chernyshev * Bernard Gütermann * Birger J. Nordølum * Dennis Marttinen * Evan Johnson * Grzegorz Rożniecki * Igor Rzegocki * Noel Georgi * Spencer Smith * darox * looklose ### Changes
51 commits

* [`07f78182c`](https://github.com/siderolabs/talos/commit/07f78182c621296e6c694b64ead8f14695b2e3b7) fix: use a fresh context for etcd unlock * [`84cd7dbec`](https://github.com/siderolabs/talos/commit/84cd7dbec4ce01a8f80a855267e1c44dfc6dcacc) feat: update Linux to 6.6.29 * [`70fdca6a4`](https://github.com/siderolabs/talos/commit/70fdca6a43abcb48030239047500fa8819f9346d) chore: update minimum hardware requirement for vmware ova * [`b690ffeb8`](https://github.com/siderolabs/talos/commit/b690ffeb899c4a133f98e212826830e3b320abe4) test: improve DNS resolver test stability * [`5aa0299b6`](https://github.com/siderolabs/talos/commit/5aa0299b6e3efefa7077aab5955526a5136b8761) style: use correct capitalization for openstack * [`4c0c626b7`](https://github.com/siderolabs/talos/commit/4c0c626b786f14c5eabdc65e88d2aae92829bf73) feat: use zstd compression in place of xz * [`98906ed6e`](https://github.com/siderolabs/talos/commit/98906ed6ea1afc5a758871a7c2d8251fccaef106) fix: use reboot delay only in case of error * [`05fd042bb`](https://github.com/siderolabs/talos/commit/05fd042bb3600541a8e2587b66b8b4c4e9f99c27) test: improve the reset integration tests * [`8cdf0f7cb`](https://github.com/siderolabs/talos/commit/8cdf0f7cb007790190197356355a16c8e427afab) docs: fix typo in Cilium instructions * [`dd1d279da`](https://github.com/siderolabs/talos/commit/dd1d279daa8c2a18c2477839b2c11e5f2f554693) fix: allow more flags in `talosctl cluster create --input-dir` * [`ef4394e58`](https://github.com/siderolabs/talos/commit/ef4394e586e42c4b5085299029a2aacb3b89502d) chore: update kernel and other packages * [`ccdb4c8b1`](https://github.com/siderolabs/talos/commit/ccdb4c8b10450aa7fb6c32b0559bda73746a03ed) chore: update google.golang.org/grpc to 1.63.2 * [`c5b59df69`](https://github.com/siderolabs/talos/commit/c5b59df6976095aca5c4bac367084874242e9e80) fix: wait for devices to be discovered before probing filesystems * [`0821b9c50`](https://github.com/siderolabs/talos/commit/0821b9c50b86bf9f7d08a1ba7b177abb7e2568c4) feat: add `--non-masquerade-cidrs` flag to `talosctl cluster create` * [`2bf613ad3`](https://github.com/siderolabs/talos/commit/2bf613ad3bd1582b520b2f661b7e0bfab4207eed) fix: add endpoints for "virtual" `host-dns` service * [`f4163aefe`](https://github.com/siderolabs/talos/commit/f4163aefeda2bf91be36af45239716c53ec982b1) fix: bump priority of OpenStack routes if IPv6 and default gateway * [`6fbd1263c`](https://github.com/siderolabs/talos/commit/6fbd1263ccbe20857cca90b5f69906651caa4f54) feat: report process MAC labels * [`d46032821`](https://github.com/siderolabs/talos/commit/d460328210ee3beea1b98ea5f23fcda5c2e2fd44) fix: return proper value from Bridge.STP instead of plain nil * [`bac1d00c3`](https://github.com/siderolabs/talos/commit/bac1d00c35cb6e1407884298118ee7b4ffc5fdfa) chore: prepare for Talos 1.8 * [`d6c8067e1`](https://github.com/siderolabs/talos/commit/d6c8067e15d8177c7394abad65b95ea98c597b9d) docs: make 1.7 docs the default * [`d7c3a0735`](https://github.com/siderolabs/talos/commit/d7c3a0735eab85dd24e86fe3e0872253067e8f10) docs: add what's new for v1.7 * [`908f67fa1`](https://github.com/siderolabs/talos/commit/908f67fa15e0de507c2f69fac0851d42376a66ce) feat: add host dns support for resolving member addrs * [`0d20b637d`](https://github.com/siderolabs/talos/commit/0d20b637d68a581354361bbceecb90395f24fedb) feat: update Kubernetes to 1.30.0 * [`ec69d7a78`](https://github.com/siderolabs/talos/commit/ec69d7a7855753e3e458f2cf7c211bf67e703220) chore: replace math/rand with math/rand/v2 * [`89040ce43`](https://github.com/siderolabs/talos/commit/89040ce4329743fa2037fb1cf65d978801753dbe) chore: update go-blockdevice/v2 library to the latest version * [`0a785802e`](https://github.com/siderolabs/talos/commit/0a785802ea22071e67d7ec85944513e73624b1ac) fix: overlay installer operations * [`b1b63f658`](https://github.com/siderolabs/talos/commit/b1b63f658eba5cbb08cbd05af959c6d397662e05) fix: mark overlay installer executable * [`3433fa13b`](https://github.com/siderolabs/talos/commit/3433fa13bf555a871e76f8ce726d5afd141a16e1) feat: use container DNS when in container mode * [`5d07ac5a7`](https://github.com/siderolabs/talos/commit/5d07ac5a7db9d2291a86ee966ee704b30afea342) fix: close apid inter-backend connections gracefully for real * [`7ba18555b`](https://github.com/siderolabs/talos/commit/7ba18555b098ba2617efce2438d6bfbec1dc0041) docs: fix typos in Akamai and AWS platform docs * [`3dd1f4e88`](https://github.com/siderolabs/talos/commit/3dd1f4e88c22734f03f7609791558b8bbbae3756) chore: extract `pkg/imager/quirks` to `pkg/machinery` * [`78bc3a433`](https://github.com/siderolabs/talos/commit/78bc3a433e8b10839034bd40b73fcc720438b943) docs: update Cilium docs * [`831f3d39e`](https://github.com/siderolabs/talos/commit/831f3d39e9b030cd1bcd3313246ebccf34f34205) feat: update Flannel to v0.25.1 * [`ea5b3ff0c`](https://github.com/siderolabs/talos/commit/ea5b3ff0c27cb033d525d172d4006e0645a924ba) feat: update Kubernetes to v1.30.0-rc.2 * [`54dac5ed4`](https://github.com/siderolabs/talos/commit/54dac5ed40698b8886096c620ac19ed55a4b99a1) feat: update Linux 6.6.24, containerd 1.7.15 * [`c51f146da`](https://github.com/siderolabs/talos/commit/c51f146daf3265bbeb4513c649938b2656ff1686) docs: update Akamai platform docs * [`9550f5ff7`](https://github.com/siderolabs/talos/commit/9550f5ff7a285df7c251df425e8f28d4c668224f) docs: fix getAuthenticationMethod and completePathFromNode docs * [`bfbd02abf`](https://github.com/siderolabs/talos/commit/bfbd02abfb1d84d14a73f1e247d62e728860d2f3) fix: assign different priority to IPv6 default gateway on OpenStack * [`c8f674bd3`](https://github.com/siderolabs/talos/commit/c8f674bd3d582f606848475bca3d22f309b2367c) test: add a test for 'spin' container runtime * [`5390ccd48`](https://github.com/siderolabs/talos/commit/5390ccd48c78e864f53cc45848772c931276380d) chore: replace []byte with string and use go:embed for templates * [`ba7cdc8c8`](https://github.com/siderolabs/talos/commit/ba7cdc8c8baf85e3015db4fa9e4446eaccf01115) chore: optimize DNSResolveCacheController * [`145f24063`](https://github.com/siderolabs/talos/commit/145f2406307e57a6f2eb1601d4f7d542d39a9f51) fix: don't modify a global map of profiles * [`6fe91ad9c`](https://github.com/siderolabs/talos/commit/6fe91ad9cf9f99401fc39a6ece24eed61f17b0e2) feat: provide Kubernets/Talos version compatibility for 1.8 * [`909a5800e`](https://github.com/siderolabs/talos/commit/909a5800e4a9ada42288ae15992579e9acf6c372) fix: generate secureboot ISO .der certificate correctly * [`b0fdc3c8c`](https://github.com/siderolabs/talos/commit/b0fdc3c8caaf6ef756cdc4440dae45891bd96d01) fix: make static pods check output consistent * [`c6ad0fcce`](https://github.com/siderolabs/talos/commit/c6ad0fcceb8220f0bf96a45e131ba999cb723f79) fix: validate that workers don't get cluster CA key * [`3735add87`](https://github.com/siderolabs/talos/commit/3735add87cec47038a88ba641322c26cd487ac58) fix: reconnect to the logs stream in dashboard after reboot * [`9aa1e1b79`](https://github.com/siderolabs/talos/commit/9aa1e1b79b4a02902e0573c10e1c0bf71a2341af) fix: present all accepted CAs to the kube-apiserver * [`336e61174`](https://github.com/siderolabs/talos/commit/336e61174624741f697c77b98dd84ab9a7a749f4) fix: close the apid connection to other machines gracefully * [`ff2c427b0`](https://github.com/siderolabs/talos/commit/ff2c427b04963d69ba2eaa1084a0a078d742b9ac) fix: pre-create nftables chain to make kubelet use nftables * [`5622f0e45`](https://github.com/siderolabs/talos/commit/5622f0e450eda589f4b9a2af28b8517d08c2aae2) docs: change localDNS to hostDNS in release notes yaml section

### Changes from siderolabs/discovery-client
2 commits

* [`ca662d2`](https://github.com/siderolabs/discovery-client/commit/ca662d218418eb50eb22d84560c290bef4369702) feat: export default GRPC dial options for the client * [`7a767fa`](https://github.com/siderolabs/discovery-client/commit/7a767fa89005209f5f39b2f5891ca7b169f52d89) chore: bump Go, deps and rekres

### Changes from siderolabs/extras
1 commit

* [`fa6663c`](https://github.com/siderolabs/extras/commit/fa6663c2abf90d82667a6c33cbc6f5edb2d1c525) feat: update Go to 1.22.2

### Changes from siderolabs/pkgs
12 commits

* [`28c5696`](https://github.com/siderolabs/pkgs/commit/28c5696e7c97b12765e65bd1bb758f8cb19e6adc) feat: update Linux to 6.6.29 * [`9c8a02c`](https://github.com/siderolabs/pkgs/commit/9c8a02c234b52cf3624ebf79f7e76065cbc1eeff) feat: update containerd to 1.7.16 * [`ca6249b`](https://github.com/siderolabs/pkgs/commit/ca6249b4b7d00b6f16e1a7264f55a4814300df63) feat: compress amd64 Linux kernel using zstd * [`718a7da`](https://github.com/siderolabs/pkgs/commit/718a7da83fe843cd59745078fe1a814c75bc4384) feat: enable SELinux * [`207481f`](https://github.com/siderolabs/pkgs/commit/207481f7b16d2b0c98053432f4ad86484bf0b1ec) feat(intel): add support for power management and ACPI options for Intel CPUs * [`dfa7dce`](https://github.com/siderolabs/pkgs/commit/dfa7dceb5ae50af454f527ac7c774c93d00054cf) feat: update Linux to 6.6.28 * [`7b30b61`](https://github.com/siderolabs/pkgs/commit/7b30b61ef3ba104f3ea21469632d3d043c5fd6f6) fix: use proper EFI zBoot image * [`010913b`](https://github.com/siderolabs/pkgs/commit/010913b8bf2b7c7df2d16efcdf23a4efbb9913ab) feat: update Linux 6.6.26, containerd 1.7.15 * [`da397fa`](https://github.com/siderolabs/pkgs/commit/da397fa0e55284f466af982f98cf93e7075e6298) feat: enable BFQ IO scheduler * [`c839801`](https://github.com/siderolabs/pkgs/commit/c83980113db4aabbda4393d7aa8e6ab734a6069b) feat: enable zboot on arm64 with zstd compression * [`1b28e2c`](https://github.com/siderolabs/pkgs/commit/1b28e2ce58e5702bcbbd5ed13fbd7cf6420dc12d) feat: go 1.22.2, Linux 6.6.24 * [`05db2a8`](https://github.com/siderolabs/pkgs/commit/05db2a88e6985470f4e7dc6b21fbdd9df1e63aea) fix: revert musl to 1.2.4

### Changes from siderolabs/tools
1 commit

* [`bd405ff`](https://github.com/siderolabs/tools/commit/bd405ff5d8d511eeef17f0a6126ad6cdd3a849bb) feat: update go to 1.22.2

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.3 -> v0.3.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.5.1 -> v1.5.2 * **github.com/aws/aws-sdk-go-v2/config** v1.27.10 -> v1.27.11 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.30.1 -> v1.31.0 * **github.com/containerd/containerd** v1.7.14 -> v1.7.16 * **github.com/containernetworking/cni** v1.1.2 -> v1.2.0 * **github.com/docker/docker** v26.0.0 -> v26.0.2 * **github.com/google/go-tpm** ee6cbcd136f8 -> 1fb84445f623 * **github.com/hetznercloud/hcloud-go/v2** v2.7.0 -> v2.7.2 * **github.com/insomniacslk/dhcp** c728f5dd21c8 -> f1cffa2c0c49 * **github.com/klauspost/compress** v1.17.7 **_new_** * **github.com/miekg/dns** v1.1.58 -> v1.1.59 * **github.com/prometheus/procfs** v0.13.0 -> v0.14.0 * **github.com/rivo/tview** a22293bda944 -> e119d15762fe * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.25 -> v1.0.0-beta.26 * **github.com/siderolabs/discovery-client** v0.1.8 -> v0.1.9 * **github.com/siderolabs/extras** v1.7.0-1-gbb76755 -> v1.8.0-alpha.0 * **github.com/siderolabs/pkgs** v1.7.0-6-g29106c0 -> v1.8.0-alpha.0-10-g28c5696 * **github.com/siderolabs/talos/pkg/machinery** v1.7.0 -> v1.7.0-alpha.1 * **github.com/siderolabs/tools** v1.7.0-1-g10b2a69 -> v1.8.0-alpha.0 * **golang.org/x/net** v0.23.0 -> v0.24.0 * **golang.org/x/oauth2** v0.18.0 -> v0.19.0 * **golang.org/x/sync** v0.6.0 -> v0.7.0 * **golang.org/x/sys** v0.18.0 -> v0.19.0 * **golang.org/x/term** v0.18.0 -> v0.19.0 * **google.golang.org/grpc** v1.62.1 -> v1.63.2 Previous release can be found at [v1.7.0](https://github.com/siderolabs/talos/releases/tag/v1.7.0) ## [Talos 1.7.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.7.0-alpha.1) (2024-03-14) Welcome to the v1.7.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Device Selectors Talos Linux now supports `physical: true` qualifier for device selectors, it selects non-virtual network interfaces (i.e. `en0` is selected, while `bond0` is not). ### DNS Caching Talos Linux now provides a caching DNS resolver for host workloads (including host networking pods). It can be disabled with: ```yaml machine: features: localDNS: false ``` ### Extension Services Config Talos now supports supplying configuration files and environment variables for extension services. The extension service configuration is a separate config document. An example is shown below: ```yaml --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: nut-client configFiles: - content: MONITOR ${upsmonHost} 1 remote pass password mountPath: /usr/local/etc/nut/upsmon.conf environment: - UPS_NAME=ups ``` For documentation, see [Extension Services Config Files](https://www.talos.dev/v1.7/reference/configuration/extensions/extensionserviceconfig/). **Note**: The use of `environmentFile` in extension service spec is now deprecated and will be removed in a future release of Talos. Use `ExtensionServiceConfig` instead. ### Kubernetes Upgrade The command `talosctl upgrade-k8s` now supports specifying custom image references for Kubernetes components via `--*-image` flags. The default behavior is unchanged, and the flags are optional. ### KubeSpan Talos Linux disables by default a KubeSpan feature to harvest additional endpoints from KubeSpan members. This feature turned out to be less helpful than expected and caused unnecessary performance issues. Previous behavior can be restored with: ```yaml machine: network: kubespan: harvestExtraEndpoints: true ``` ### NTP Default NTP server was updated to be `time.cloudflare.com` instead of `pool.ntp.org`. Default server is only used if the user does not specify any NTP servers in the configuration. ### OpenNebula Talos Linux now supports OpenNebula platform. ### Known Problems DRBD extension is disabled in this release due to incompatibility with the latest Linux kernel. ### Kubernetes API Server Service Account Key Talos Linux starting from this release uses RSA key for Kubernetes API Server Service Account instead of ECDSA key to provide better compatibility with external OpenID Connect implementations. ### SBC Talos core will drop support for SBC's and will not include the SBC binaries in the release. *Overlays* are being developed to support SBC's. ### Secure Boot Image Talos Linux now provides a way to configure systemd-boot ISO 'secure-boot-enroll' option while generating a SecureBoot ISO image: ```yaml output: kind: iso isoOptions: sdBootEnrollKeys: force # default is still if-safe outFormat: raw ``` ### Syslog Talos Linux now starts a basic syslog receiver listening on `/dev/log`. The receiver can mostly parse both RFC3164 and RFC5424 messages and writes them as JSON formatted message. The logs can be viewed via `talosctl logs syslogd`. This is mostly implemented for extension services that log to syslog. ### Component Updates Linux: 6.6.21 etcd: 3.5.11 Kubernetes: 1.30.0-beta.0 containerd: 1.7.14 runc: 1.1.12 Flannel: 0.24.1 Talos is built with Go 1.22.1. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Andrey Smirnov * Artem Chernyshev * Radosław Piliszek * Spencer Smith * Anthony ARNAUD * Justin Garrison * Steve Francis * Anastasios Papagiannis * Andrei Kvapil * Andrian Zubovic * AvnarJakob * Cas de Reuver * Christian Mohn * Christian WALDBILLIG * Dmitry Sharshakov * Dmitry Sharshakov * Drew Hess * ExtraClock * Fabiano Fidêncio * Henno Schooljan * Hervé Werner * JJGadgets * Jacob McSwain * Jonomir * Kai Hanssen * Louis SCHNEIDER * Matthieu S * Michael Stephenson * Nico Berlee * Pip Oomen * Saiyam Pathak * Sebastiaan Gerritsen * Sebastian Gaiser * Serge Logvinov * Tim Jones * bri * ebcrypto * edwinavalos * fazledyn-or * james-dreebot * pardomue * shurkys * stereobutter ### Changes
162 commits

* [`403ad93c3`](https://github.com/siderolabs/talos/commit/403ad93c35b4cee9c012addb4667cb04e23e1c61) feat: update dependencies * [`7376f34e8`](https://github.com/siderolabs/talos/commit/7376f34e823f6399ed2c66ae1296a8a47a0a00ef) fix: remove maintenance config when maintenance service is shut down * [`952801d8b`](https://github.com/siderolabs/talos/commit/952801d8b2af27a49531b8a19f8b74400b6d4eb8) fix: handle overlay partition options * [`465b9a4e6`](https://github.com/siderolabs/talos/commit/465b9a4e6ca9367326cb862b501f1146989b07d4) fix: update discovery client with the fix for keepalive interval * [`1e9f866ac`](https://github.com/siderolabs/talos/commit/1e9f866aca14ec5ecc4d5619f42e02d44b6968d1) feat: update Kubernetes to v1.30.0-beta.0 * [`d118a852b`](https://github.com/siderolabs/talos/commit/d118a852b995f13fc5160acb7c95d2186adaac41) feat: implement `Install` for imager overlays * [`cd5a5a447`](https://github.com/siderolabs/talos/commit/cd5a5a4474914cb64a23698b6656763b253a4d01) chore: migrate to go-grpc-middleware/v2 * [`e3c2a6398`](https://github.com/siderolabs/talos/commit/e3c2a639810ad325c2b5d1b1a92aa09d52ac6997) feat: set default NTP server to time.cloudflare.com * [`32e087760`](https://github.com/siderolabs/talos/commit/32e08776078f9ca78ed27a382665589229c0ccb4) chore: print all available logs containers in `logs` command completions * [`e89d755c5`](https://github.com/siderolabs/talos/commit/e89d755c523065a257d34dff9a88df97fc1908b3) fix: etcd config validation for worker * [`1aa3c9182`](https://github.com/siderolabs/talos/commit/1aa3c91821fb9889e9859c880d602457791f6a14) docs: add DreeBot to ADOPTERS.md * [`1bb6027cc`](https://github.com/siderolabs/talos/commit/1bb6027ccd7c63ae3a012eb310d1e05027ec1f80) fix: fix nil panic on maintenance upgrade with partial config * [`aa70bfb9d`](https://github.com/siderolabs/talos/commit/aa70bfb9dc4fc886a6c5b771947a146ee2f58ef7) docs: add Redpill Linpro to adopters list * [`f02aeec92`](https://github.com/siderolabs/talos/commit/f02aeec922b6327dad6d4fee917987b147abbf2a) fix: do not fail cluster create when input dir does not contain talosconfig * [`1ec6683e0`](https://github.com/siderolabs/talos/commit/1ec6683e0c1d60b55a25e495c2dfc18f5bbf05b0) chore: use go-copy * [`3c8f51d70`](https://github.com/siderolabs/talos/commit/3c8f51d707b897fb34ed3a9f7c32b7cd3e5ee5b0) chore: move cli formatters and version modules to machinery * [`8152a6dd6`](https://github.com/siderolabs/talos/commit/8152a6dd6b7484e3f313b7cc9dd84fefba84d106) feat: update Go to 1.22.1 * [`8c7953991`](https://github.com/siderolabs/talos/commit/8c79539914324eee64dbdaf1f535fc4e20da55e8) docs: update replicated-local-storage-with-openebs-jiva.md * [`f23bd8144`](https://github.com/siderolabs/talos/commit/f23bd81448b640b37006d6bfffa9315f84cad492) fix: syslog parser * [`bbed07e03`](https://github.com/siderolabs/talos/commit/bbed07e03a815869cbae5aaa2667864697fd5d65) feat: update Linux to 6.6.18 * [`8125e754b`](https://github.com/siderolabs/talos/commit/8125e754b8a4c8db891dcd2dbd6ee3702daa2393) feat: imager overlay * [`0b9b4da12`](https://github.com/siderolabs/talos/commit/0b9b4da12abe6bf19d9eaaa48b42cd1a794ca8fa) feat: update Kubernetes to 1.30.0-alpha.3 * [`3a764029e`](https://github.com/siderolabs/talos/commit/3a764029ea2d3f888c2d4d83ebffd6f97a46e3a9) docs: fix typo in word governor * [`d81d49000`](https://github.com/siderolabs/talos/commit/d81d4900030e93cacda34646732f24816dd3d85f) chore: update CoreDNS renovate source * [`b2ad5dc5f`](https://github.com/siderolabs/talos/commit/b2ad5dc5f809da9665b41c25d9ab6359a87ec942) fix: workaround a race in CNI setup (talosctl cluster create) * [`457507803`](https://github.com/siderolabs/talos/commit/457507803d302a31b47f5e386ce1e398861550bd) fix: provide auth when pulling images in the imager * [`e707175ab`](https://github.com/siderolabs/talos/commit/e707175ab5bdeb0f79ad242e2c81f36eec928342) docs: update config patch in cilium docs * [`f8c556a1c`](https://github.com/siderolabs/talos/commit/f8c556a1ce9aa49c1af1bfe97c3694c00fcc67bc) chore: listen for dns requests on 127.0.0.53 * [`8872a7a21`](https://github.com/siderolabs/talos/commit/8872a7a2105034d8d6550e628355fe5f09131691) fix: ignore 'no such device' in addition to 'no such file' * [`1cb544353`](https://github.com/siderolabs/talos/commit/1cb5443530abc2f6333566ec8e8429b2a784f791) chore: uki der certs in iso * [`67ac6933d`](https://github.com/siderolabs/talos/commit/67ac6933d3c23b8ea31f01bd45d0192573e64ef3) fix: handle errors to watch apid/trustd certs * [`c79d69c2e`](https://github.com/siderolabs/talos/commit/c79d69c2e25ee588f45a8978117300c31871f749) fix: only set gateway if set in context (opennebula) * [`4575dd8e7`](https://github.com/siderolabs/talos/commit/4575dd8e741e99ab92ac63afdf48d816562f744c) chore: allow not preallocated disks for QEMU cluster * [`0bddfea81`](https://github.com/siderolabs/talos/commit/0bddfea818994288285f442c27a339e6d1dc6cf0) chore: add oceanbox.io to adopters * [`136427592`](https://github.com/siderolabs/talos/commit/1364275926df312204e006751dacc7af8e7d6726) chore: use proper `talos_version_contract` for TF tests * [`6bf50fdc1`](https://github.com/siderolabs/talos/commit/6bf50fdc14ad97d97fd8fcec3132f0b183c93e5a) chore: disable x/net/trace in gRPC to enable dead code elimination * [`815a8e9cc`](https://github.com/siderolabs/talos/commit/815a8e9cc5ad2c22acf11f223d8a64abbbf4b3cb) feat: add partial config support to `talosctl cluster create` * [`64e9703f8`](https://github.com/siderolabs/talos/commit/64e9703f8648f997ff2e2e0fff932f74fd52d585) chore: add tests for the Kata Containers extension * [`9b6291925`](https://github.com/siderolabs/talos/commit/9b62919253f16cbbfec999da26f11e8751fbb345) feat: update pkgs * [`66f3ffdd4`](https://github.com/siderolabs/talos/commit/66f3ffdd4ad69ec690c680868cc95697eb1fba48) fix: ensure that Talos runs in a pod (container) * [`9dbc33972`](https://github.com/siderolabs/talos/commit/9dbc33972a2ded3818fabd9b157604d26926e3c9) feat: add basic syslog implementation * [`0b7a27e6a`](https://github.com/siderolabs/talos/commit/0b7a27e6a122e7cacb5ff82a7f6cae005435ae54) feat: allow access to all resources over siderolink in maintenance mode * [`53721883d`](https://github.com/siderolabs/talos/commit/53721883d50bd9979edeb4f94a0f1cfcf74d4d80) feat: support AWS KMS for the SecureBoot signing * [`7ee999f8a`](https://github.com/siderolabs/talos/commit/7ee999f8a3906eda23b7657da4c4212886a81626) fix: disable KubeSpan endpoint harvesting by default * [`7b87c7fe9`](https://github.com/siderolabs/talos/commit/7b87c7fe97d01f33eb621bb631d482f975da3feb) chore: bump Go dependencies * [`8e9596d3c`](https://github.com/siderolabs/talos/commit/8e9596d3c65246824e921f6cb9dfcda96b5ff52c) docs: rpi talosctl install update * [`493bb60f8`](https://github.com/siderolabs/talos/commit/493bb60f81075181c4f71af546674871f4616067) fix: correctly handle partial configs in `DNSUpstreamController` * [`6deb10ae2`](https://github.com/siderolabs/talos/commit/6deb10ae25efa1d96dd7416045c99b178b04e020) chore: deprecate `environmentFile` for extensions * [`f8b4ee82a`](https://github.com/siderolabs/talos/commit/f8b4ee82aeba990d8e34b7c95debf30c4a626298) chore: update extensions test * [`1366ce14a`](https://github.com/siderolabs/talos/commit/1366ce14a8b0bf72ac884147497e354fb33ef3fa) feat: update Kubernetes to v1.30.0-alpha.2 * [`559308ef7`](https://github.com/siderolabs/talos/commit/559308ef7e482786cc3554002bcd9fb05e0459c8) fix: use MachineStatus resource to check for boot done * [`15e8bca2b`](https://github.com/siderolabs/talos/commit/15e8bca2b2f839ee138faa14cb3931af173d258f) feat: support environment in `ExtensionServicesConfig` * [`3fe82ec46`](https://github.com/siderolabs/talos/commit/3fe82ec461995b680ecf060af75b47cd175a6342) feat: custom image settings for k8s upgrade * [`fa3b93370`](https://github.com/siderolabs/talos/commit/fa3b93370501009283e110b74876b18ce6bad4f9) chore: replace fmt.Errorf with errors.New where possible * [`d4521ee9c`](https://github.com/siderolabs/talos/commit/d4521ee9c472622fb2ef3c8570c1fa1c46332c16) feat: update kernel with sfc driver and LSM updates * [`2f0421b40`](https://github.com/siderolabs/talos/commit/2f0421b406ee252e9197c0b4589c0b33662bef34) fix: run xfs_repair on invalid argument error * [`f868fb8e8`](https://github.com/siderolabs/talos/commit/f868fb8e8f50e1acaa1743001d5b4f702bf29294) docs: update vmware tools url * [`fa2d34dd8`](https://github.com/siderolabs/talos/commit/fa2d34dd8875e6a09c257acfb9321c1230658b87) chore: enable v6 support on the same port * [`83e0b0c19`](https://github.com/siderolabs/talos/commit/83e0b0c19aaca7d413483b3a908c9dc3b4289203) chore: adjust dns sockets settings * [`a1ec1705b`](https://github.com/siderolabs/talos/commit/a1ec1705bc5d1f7c66dbb8549af42fc3b4778400) chore: update Go to 1.22.0 * [`76b50fcd4`](https://github.com/siderolabs/talos/commit/76b50fcd4ae2a5d602997cc360c9dcb45e4243e8) chore: add Ænix to the Adopters list * [`5324d3916`](https://github.com/siderolabs/talos/commit/5324d391671dfbf918aee1bd6b095adffadecf8e) chore: bump stuff * [`087b50f42`](https://github.com/siderolabs/talos/commit/087b50f42932e4da883de254984bce4ad7858b90) feat: support systemd-boot ISO enroll keys option * [`afa71d6b0`](https://github.com/siderolabs/talos/commit/afa71d6b028c33333db51495a3db41b758f38435) chore: use "handle-like" resource in `DNSResolveCacheController` * [`013e13070`](https://github.com/siderolabs/talos/commit/013e130702758dcd8f44c84de8090d624aa5c7b9) fix: error with decoding config document with wrong apiVersion * [`1e77bb1c3`](https://github.com/siderolabs/talos/commit/1e77bb1c3dde3c6a54bc4174eafc09846ff59e62) chore: allow custom pkgs to build talos * [`3f8a85f1b`](https://github.com/siderolabs/talos/commit/3f8a85f1b390936cf7d76a146f6b76973be1e474) fix: unlock the upgrade mutex properly * [`61c3331b1`](https://github.com/siderolabs/talos/commit/61c3331b148901a3137de6a087d561a6db8f4dfc) docs: update indentation in vip.md * [`383e528df`](https://github.com/siderolabs/talos/commit/383e528df8c52ad44402c830fb3611b66c71fc7a) chore: allow uuid-based hostnames in talosctl cluster create * [`1e6c8c4de`](https://github.com/siderolabs/talos/commit/1e6c8c4dec1e71f0d83914c3a0d7b907b21dc3b0) feat: extensions services config * [`989ca3ade`](https://github.com/siderolabs/talos/commit/989ca3ade194bb0cd5c162d5d8973c133e381501) feat: add OpenNebula platform support * [`914f88778`](https://github.com/siderolabs/talos/commit/914f88778838abe51f24ec3a9574e91836561e9e) docs: update nocloud.md Proxmox information * [`a04cc8015`](https://github.com/siderolabs/talos/commit/a04cc80154ed94e970615714fd8dff9cd8cf8ca9) fix: pass TTL when generating client certificate * [`3fe8c12ca`](https://github.com/siderolabs/talos/commit/3fe8c12ca654790695417b3d4f6bb5517e5902b5) fix: add log line about controller runtime failing * [`ddbabc7e5`](https://github.com/siderolabs/talos/commit/ddbabc7e58e476c95d7bb15f325f612a3d8fc86c) fix: use a separate cgroup for each extension service * [`6ccdd2c09`](https://github.com/siderolabs/talos/commit/6ccdd2c09c88eb2fe8b5b382dbd94816865381d3) chore: fix markdown-lint call * [`4184e617a`](https://github.com/siderolabs/talos/commit/4184e617ab92b8f41c2540bf55aa4d502778dcad) chore: add test for wasmedge runtime extension * [`95ea3a6c6`](https://github.com/siderolabs/talos/commit/95ea3a6c65a952fef533016b7116212c21609aac) chore: bump timeout in acquire tests * [`c19a505d8`](https://github.com/siderolabs/talos/commit/c19a505d8cde234e12f729183e8c7272ac049159) chore: bump docker dind image * [`d7d4154d5`](https://github.com/siderolabs/talos/commit/d7d4154d5dc817f91771b25b358825dae803de7f) chore: remove channel blocking in qemu launch * [`029d7f7b9`](https://github.com/siderolabs/talos/commit/029d7f7b9b2ba610b9bd68dd00a9d8a060bfd280) release(v1.7.0-alpha.0): prepare release * [`2ff81c06b`](https://github.com/siderolabs/talos/commit/2ff81c06bc1123af2fa7286fff15d9de0b8a868a) feat: update runc 1.1.12, containerd 1.7.13 * [`9d8cd4d05`](https://github.com/siderolabs/talos/commit/9d8cd4d058e73d30e4864e67377cf55390467725) chore: drop deprecated method EtcdRemoveMember * [`17567f19b`](https://github.com/siderolabs/talos/commit/17567f19be39eeaf0d9a9aa3cd773b73d537814a) fix: take into account the moment seen when cleaning up CRI images * [`aa03204b8`](https://github.com/siderolabs/talos/commit/aa03204b864d8d8ac5a7ee4986a06230863043fb) docs: document the process of building custom kernel packages * [`7af48bd55`](https://github.com/siderolabs/talos/commit/7af48bd5598e61357cdb9b31dd57de6479b1ce7c) feat: use RSA key for kube-apiserver service account key * [`a5e13c696`](https://github.com/siderolabs/talos/commit/a5e13c696d1e1cb8e894a4133791c74470687553) fix: retry blockdevice open in the installer * [`593afeea3`](https://github.com/siderolabs/talos/commit/593afeea38a75de01041e3126cb0ad3443f6e1a1) fix: run the interactive installer loop to report errors * [`87be76b87`](https://github.com/siderolabs/talos/commit/87be76b8788d179058be14c53e1092054b08c5dd) fix: be more tolerant to error handling in Mounts API * [`03add7503`](https://github.com/siderolabs/talos/commit/03add750309dcdeb7c2b87cd72da29a3e228e56e) docs: add section on using imager with extensions from tarball * [`ee0fb5eff`](https://github.com/siderolabs/talos/commit/ee0fb5effce82fec99860b5910e0fb6e5147b49b) docs: consolidate certificate management articles * [`9c14dea20`](https://github.com/siderolabs/talos/commit/9c14dea209bba69b471fd43eb2e8ba05de3ff549) chore: bump coredns * [`ebeef2852`](https://github.com/siderolabs/talos/commit/ebeef28525f71189727200115d62fe8d713d1d07) feat: implement local caching dns server * [`4a3691a27`](https://github.com/siderolabs/talos/commit/4a3691a2739871be5eff4b313c30d454a143fbc4) docs: fix broken links in metal-network-configuration.md * [`c4ed189a6`](https://github.com/siderolabs/talos/commit/c4ed189a6912238350efd5f0181a6ef45728fc63) docs: provide sane defaults for each release series in vmware script * [`8138d54c6`](https://github.com/siderolabs/talos/commit/8138d54c6c9bae4255216007595fa302bc418c1a) docs: clarify node taints/labels for worker nodes * [`b44551ccd`](https://github.com/siderolabs/talos/commit/b44551ccdb0dd0ceaffd2e484c86ce91b25fe841) feat: update Linux to 6.6.13 * [`385707c5f`](https://github.com/siderolabs/talos/commit/385707c5f39e733c8f27532435cd14f5f2ff067d) docs: update vmware.sh * [`d1a79b845`](https://github.com/siderolabs/talos/commit/d1a79b845f025defafb468fb6b5e86957cfad4fc) docs: fix small typo in etcd maintenance guide * [`cf0603330`](https://github.com/siderolabs/talos/commit/cf0603330a5c852163642a6b3844d1dcc3892cf6) docs: copy generated JSON schema to host * [`f11139c22`](https://github.com/siderolabs/talos/commit/f11139c229765cf82cadc84e6fa81d860005100b) docs: document local path provisioner install * [`e0dfbb8fb`](https://github.com/siderolabs/talos/commit/e0dfbb8fba3c50652d0ecbae1db0b0660d0766a6) fix: allow META encoded values to be compressed * [`d677901b6`](https://github.com/siderolabs/talos/commit/d677901b672eec46b8b5edf57c680813b8fcf697) feat: implement device selector for 'physical' * [`7d1117289`](https://github.com/siderolabs/talos/commit/7d1117289658ac04707b09f64a1dc70514a9fba9) docs: add missing talosconfig flag * [`8a1732bcb`](https://github.com/siderolabs/talos/commit/8a1732bcb12deb4444ae87d22cc15d8b968b867d) fix: pull in `mptspi` driver * [`c1e45071f`](https://github.com/siderolabs/talos/commit/c1e45071f0cb0e48ee35d2f87b483fffb05c6123) refactor: use etcd configuration from the EtcdSpec resource * [`4e9b688d3`](https://github.com/siderolabs/talos/commit/4e9b688d3f8bc809e0b2f012d5e58c27de85d1e0) fix: use correct TTL for talosconfig in `talosctl config new` * [`fb5ad0555`](https://github.com/siderolabs/talos/commit/fb5ad05551e08404cb8acde01202c4ae88ddd25a) feat: update Kubernetes default to 1.29.1 * [`fe24139f3`](https://github.com/siderolabs/talos/commit/fe24139f3c0b3f37c8266e5d6c5091950e3a647c) docs: fork docs for v1.7 * [`1c2d10ccc`](https://github.com/siderolabs/talos/commit/1c2d10ccccb84a6d1e008af23866fa13cc14d094) chore: bump dependencies * [`a599e3867`](https://github.com/siderolabs/talos/commit/a599e38674af448fe5cac210f5d80826d3b08a12) chore: allow custom registry to build installer/imager * [`3911ddf7b`](https://github.com/siderolabs/talos/commit/3911ddf7bd630286358f1696adf9bdac207e1b9d) docs: add how-to for cert management * [`b0ee0bfba`](https://github.com/siderolabs/talos/commit/b0ee0bfba3f4c9172c76422a8f8f10a4046c352b) fix: strategic patch merging for audit policy * [`474eccdc4`](https://github.com/siderolabs/talos/commit/474eccdc4cb1d0fab3ba0b370cc388bc8c9d363a) fix: watch bufer overrun for RouteStatus * [`cc06b5d7a`](https://github.com/siderolabs/talos/commit/cc06b5d7a659a7f5a35e86a82ee242344c303302) fix: fix .der output in `talosctl gen secureboot` * [`1dbb4abf4`](https://github.com/siderolabs/talos/commit/1dbb4abf43695d1dd18d51b0386cf644aba67d73) fix: update discovery service client to v0.1.6 * [`9782319c3`](https://github.com/siderolabs/talos/commit/9782319c31e496d998bdf9d505f32a4d8e6e937e) fix: support KubePrism settings in Kubernetes Discovery * [`6c5a0c281`](https://github.com/siderolabs/talos/commit/6c5a0c2811e3c0f3e1ca2a8fb871065df5bf9b46) feat: generate a single JSON schema for multidoc config * [`f70b47ddd`](https://github.com/siderolabs/talos/commit/f70b47dddc2599a618c68d8b403d9b37c61f2b71) fix: force KubePrism to connect using IPv4 * [`d5321e085`](https://github.com/siderolabs/talos/commit/d5321e085eb6c877b1b5b38d69eabb839b505297) fix: update kmsg with utf-8 fix * [`7fa7362dd`](https://github.com/siderolabs/talos/commit/7fa7362ddc0e8a0b85cffcaebc38abd772b355e2) fix: fix nodes on dashboard footer when node names are used in `--nodes` * [`ba88678f1`](https://github.com/siderolabs/talos/commit/ba88678f1a42b4e9f6c9de25bdc827330cfb254c) fix: merge ports and ingress configs correctly in NetworkRuleConfig * [`dea9bda2d`](https://github.com/siderolabs/talos/commit/dea9bda2d00feeb29bf4b2c91c2ca24b6cd362f2) fix: disk UUID & WWID always empty in `talosctl disks` * [`8dc112f36`](https://github.com/siderolabs/talos/commit/8dc112f36bd77ec72e5c501755aa4f056803efd0) chore: pull in NBD modules * [`f6926faab`](https://github.com/siderolabs/talos/commit/f6926faab5a8b878c600d60ef9d693026277f3ee) fix: default priority for ipv6 * [`e8758dcba`](https://github.com/siderolabs/talos/commit/e8758dcbad6d3188dfccd235dbab04c19dd1a6ed) chore: support http downloads for assets in talosctl cluster create * [`265f21be0`](https://github.com/siderolabs/talos/commit/265f21be09d68cc23764d690e9f9479b9d92d749) fix: replace the filemap implementation to not buffer in memory * [`8db3c5b3c`](https://github.com/siderolabs/talos/commit/8db3c5b3c63ad67043b876265ac4687cdcb0f0ff) fix: pick correctly base installer image layers * [`0a30ef784`](https://github.com/siderolabs/talos/commit/0a30ef78456e854419d0c593f9c97f40166102f3) fix: imager should support different Talos versions * [`d6342cda5`](https://github.com/siderolabs/talos/commit/d6342cda53027eb5d46dcb6f57fbb1cc31f920dd) docs: update latest version to v1.6.1 * [`e6e422b92`](https://github.com/siderolabs/talos/commit/e6e422b92ade5f24c898e09affdb6de8ee671cb0) chore: bump dependencies * [`5a19d078a`](https://github.com/siderolabs/talos/commit/5a19d078ad3205d201b11e0d60d5e07b379aba91) fix: properly overwrite files on install * [`9eb6cea78`](https://github.com/siderolabs/talos/commit/9eb6cea7890854173917a096bcffd6202487d38c) docs: secureboot sd-boot menu clarification * [`01f0cbe61`](https://github.com/siderolabs/talos/commit/01f0cbe61c32b3ff6e9d05f2c14c83223ce043fa) feat: support iPXE direct booting in `talosctl cluster create` * [`3ba84701d`](https://github.com/siderolabs/talos/commit/3ba84701d9f87f533b3039395d350b311f4a484f) feat: pull in kernel modules for mlx Infiniband and VFIO * [`ba993e0ed`](https://github.com/siderolabs/talos/commit/ba993e0edd20f927ff8d59f418e47c6cbf8a95b3) docs: announce that SecureBoot is available * [`241bc9312`](https://github.com/siderolabs/talos/commit/241bc9312edcadce83a64e92db807dbca74c80cc) fix: update the way secureboot signer fetches certificate (azure) * [`59b62398f`](https://github.com/siderolabs/talos/commit/59b62398f6265f310108954e9a775e4b8c080679) chore: modernize machined/pkg/controllers/k8s * [`760f793d5`](https://github.com/siderolabs/talos/commit/760f793d55f3965792f58fa3194977aea4f90e03) fix: use correct prefix when installing SBC files * [`0b94550c4`](https://github.com/siderolabs/talos/commit/0b94550c42730121c3d270758286dbefa95ea61c) chore: fix the gvisor test * [`3a787c1d6`](https://github.com/siderolabs/talos/commit/3a787c1d67ddca5102c7d9cbdab4ef1c17a605f4) docs: update 1.6 docs with Noel's feedback * [`d803e40ef`](https://github.com/siderolabs/talos/commit/d803e40ef2cf1030aab522006ba7287bac8b64c4) docs: provide documentation for Talos 1.6 * [`9a185a30f`](https://github.com/siderolabs/talos/commit/9a185a30f79a8d3481606235609c0e5a11c880cc) feat: update Kubernetes to v1.29.0 * [`5934815d2`](https://github.com/siderolabs/talos/commit/5934815d2fe975c4d8ddb2a26ef733d29565cdb2) chore: split more kernel modules on amd64 * [`10c59a6b9`](https://github.com/siderolabs/talos/commit/10c59a6b90310b8c58babf5beb108b59f4d74e4d) fix: leave discovery service later in the reset sequence * [`0c86ca1cc`](https://github.com/siderolabs/talos/commit/0c86ca1cc68e2646d63d19d96b01d3d5486dfc42) chore: enable kubespan+firewall for cilium tests * [`98fd722d5`](https://github.com/siderolabs/talos/commit/98fd722d5110b1422a15ede23873bcd15ab9562e) feat: provide compatibility for future Talos 1.7 * [`131a1b167`](https://github.com/siderolabs/talos/commit/131a1b1671899666d8676b5082cef39efb8f0fa1) fix: add a KubeSpan option to disable extra endpoint harvesting * [`4547ad9af`](https://github.com/siderolabs/talos/commit/4547ad9afa206405032618f9d94470d00ace8684) feat: send `actor id` to the SideroLink events sink * [`04e774547`](https://github.com/siderolabs/talos/commit/04e774547146f0733633b296c4432f4eef847265) docs: cap max heading level * [`6bb1e99aa`](https://github.com/siderolabs/talos/commit/6bb1e99aa3a8132508479b4ca8606522545d8d9a) chore: optimize pcap dump * [`4f9d3b975`](https://github.com/siderolabs/talos/commit/4f9d3b975fa689dc9eea4e44ff453d8b68ae54ef) feat: update Kubernetes to v1.29.0-rc.2 * [`46121c9fe`](https://github.com/siderolabs/talos/commit/46121c9fecb3603c2d2ae2de6152861ee7f19eaf) docs: rework machine config documentation generation * [`e128d3c82`](https://github.com/siderolabs/talos/commit/e128d3c827a406f96457322da87cbde2af233fa0) fix: talosctl cluster create not to enforce kubeprism always * [`320064c5a`](https://github.com/siderolabs/talos/commit/320064c5a869de6d52ba9a23394acaa5549e7aa1) feat: update Go 1.21.5, Linux 6.1.65, etcd 3.5.11 * [`270604bea`](https://github.com/siderolabs/talos/commit/270604bead50423697d6fabffa6bbd7c7b2fbe9e) fix: support user disks via symlinks * [`4f195dd27`](https://github.com/siderolabs/talos/commit/4f195dd271eb38446561f8708a9623324072a0e9) chore: fix the release.toml * [`474fa0480`](https://github.com/siderolabs/talos/commit/474fa0480dd68d112a608548e4d0a0c4efa39e20) fix: store and execute desired action on emergency action * [`515ae2a18`](https://github.com/siderolabs/talos/commit/515ae2a184374e0ac72e3321104265918e45e391) docs: extend hetzner-cloud docs for arm64 * [`eecc4dbd5`](https://github.com/siderolabs/talos/commit/eecc4dbd5198cca5b66e5c3018c407cd38b13c80) fix: trim leading spaces\newlines in inline manifest contents * [`dbf274ddf`](https://github.com/siderolabs/talos/commit/dbf274ddf7b819941c88932e28d2fe362876ec68) fix: skip writing the file if the contents haven't changed * [`6329222bd`](https://github.com/siderolabs/talos/commit/6329222bdcfd5ab29bc46ca03bb0b1d22ada9424) fix: do not panic in `merge.Merge` if map value is nil

### Changes since v1.7.0-alpha.0
80 commits

* [`403ad93c3`](https://github.com/siderolabs/talos/commit/403ad93c35b4cee9c012addb4667cb04e23e1c61) feat: update dependencies * [`7376f34e8`](https://github.com/siderolabs/talos/commit/7376f34e823f6399ed2c66ae1296a8a47a0a00ef) fix: remove maintenance config when maintenance service is shut down * [`952801d8b`](https://github.com/siderolabs/talos/commit/952801d8b2af27a49531b8a19f8b74400b6d4eb8) fix: handle overlay partition options * [`465b9a4e6`](https://github.com/siderolabs/talos/commit/465b9a4e6ca9367326cb862b501f1146989b07d4) fix: update discovery client with the fix for keepalive interval * [`1e9f866ac`](https://github.com/siderolabs/talos/commit/1e9f866aca14ec5ecc4d5619f42e02d44b6968d1) feat: update Kubernetes to v1.30.0-beta.0 * [`d118a852b`](https://github.com/siderolabs/talos/commit/d118a852b995f13fc5160acb7c95d2186adaac41) feat: implement `Install` for imager overlays * [`cd5a5a447`](https://github.com/siderolabs/talos/commit/cd5a5a4474914cb64a23698b6656763b253a4d01) chore: migrate to go-grpc-middleware/v2 * [`e3c2a6398`](https://github.com/siderolabs/talos/commit/e3c2a639810ad325c2b5d1b1a92aa09d52ac6997) feat: set default NTP server to time.cloudflare.com * [`32e087760`](https://github.com/siderolabs/talos/commit/32e08776078f9ca78ed27a382665589229c0ccb4) chore: print all available logs containers in `logs` command completions * [`e89d755c5`](https://github.com/siderolabs/talos/commit/e89d755c523065a257d34dff9a88df97fc1908b3) fix: etcd config validation for worker * [`1aa3c9182`](https://github.com/siderolabs/talos/commit/1aa3c91821fb9889e9859c880d602457791f6a14) docs: add DreeBot to ADOPTERS.md * [`1bb6027cc`](https://github.com/siderolabs/talos/commit/1bb6027ccd7c63ae3a012eb310d1e05027ec1f80) fix: fix nil panic on maintenance upgrade with partial config * [`aa70bfb9d`](https://github.com/siderolabs/talos/commit/aa70bfb9dc4fc886a6c5b771947a146ee2f58ef7) docs: add Redpill Linpro to adopters list * [`f02aeec92`](https://github.com/siderolabs/talos/commit/f02aeec922b6327dad6d4fee917987b147abbf2a) fix: do not fail cluster create when input dir does not contain talosconfig * [`1ec6683e0`](https://github.com/siderolabs/talos/commit/1ec6683e0c1d60b55a25e495c2dfc18f5bbf05b0) chore: use go-copy * [`3c8f51d70`](https://github.com/siderolabs/talos/commit/3c8f51d707b897fb34ed3a9f7c32b7cd3e5ee5b0) chore: move cli formatters and version modules to machinery * [`8152a6dd6`](https://github.com/siderolabs/talos/commit/8152a6dd6b7484e3f313b7cc9dd84fefba84d106) feat: update Go to 1.22.1 * [`8c7953991`](https://github.com/siderolabs/talos/commit/8c79539914324eee64dbdaf1f535fc4e20da55e8) docs: update replicated-local-storage-with-openebs-jiva.md * [`f23bd8144`](https://github.com/siderolabs/talos/commit/f23bd81448b640b37006d6bfffa9315f84cad492) fix: syslog parser * [`bbed07e03`](https://github.com/siderolabs/talos/commit/bbed07e03a815869cbae5aaa2667864697fd5d65) feat: update Linux to 6.6.18 * [`8125e754b`](https://github.com/siderolabs/talos/commit/8125e754b8a4c8db891dcd2dbd6ee3702daa2393) feat: imager overlay * [`0b9b4da12`](https://github.com/siderolabs/talos/commit/0b9b4da12abe6bf19d9eaaa48b42cd1a794ca8fa) feat: update Kubernetes to 1.30.0-alpha.3 * [`3a764029e`](https://github.com/siderolabs/talos/commit/3a764029ea2d3f888c2d4d83ebffd6f97a46e3a9) docs: fix typo in word governor * [`d81d49000`](https://github.com/siderolabs/talos/commit/d81d4900030e93cacda34646732f24816dd3d85f) chore: update CoreDNS renovate source * [`b2ad5dc5f`](https://github.com/siderolabs/talos/commit/b2ad5dc5f809da9665b41c25d9ab6359a87ec942) fix: workaround a race in CNI setup (talosctl cluster create) * [`457507803`](https://github.com/siderolabs/talos/commit/457507803d302a31b47f5e386ce1e398861550bd) fix: provide auth when pulling images in the imager * [`e707175ab`](https://github.com/siderolabs/talos/commit/e707175ab5bdeb0f79ad242e2c81f36eec928342) docs: update config patch in cilium docs * [`f8c556a1c`](https://github.com/siderolabs/talos/commit/f8c556a1ce9aa49c1af1bfe97c3694c00fcc67bc) chore: listen for dns requests on 127.0.0.53 * [`8872a7a21`](https://github.com/siderolabs/talos/commit/8872a7a2105034d8d6550e628355fe5f09131691) fix: ignore 'no such device' in addition to 'no such file' * [`1cb544353`](https://github.com/siderolabs/talos/commit/1cb5443530abc2f6333566ec8e8429b2a784f791) chore: uki der certs in iso * [`67ac6933d`](https://github.com/siderolabs/talos/commit/67ac6933d3c23b8ea31f01bd45d0192573e64ef3) fix: handle errors to watch apid/trustd certs * [`c79d69c2e`](https://github.com/siderolabs/talos/commit/c79d69c2e25ee588f45a8978117300c31871f749) fix: only set gateway if set in context (opennebula) * [`4575dd8e7`](https://github.com/siderolabs/talos/commit/4575dd8e741e99ab92ac63afdf48d816562f744c) chore: allow not preallocated disks for QEMU cluster * [`0bddfea81`](https://github.com/siderolabs/talos/commit/0bddfea818994288285f442c27a339e6d1dc6cf0) chore: add oceanbox.io to adopters * [`136427592`](https://github.com/siderolabs/talos/commit/1364275926df312204e006751dacc7af8e7d6726) chore: use proper `talos_version_contract` for TF tests * [`6bf50fdc1`](https://github.com/siderolabs/talos/commit/6bf50fdc14ad97d97fd8fcec3132f0b183c93e5a) chore: disable x/net/trace in gRPC to enable dead code elimination * [`815a8e9cc`](https://github.com/siderolabs/talos/commit/815a8e9cc5ad2c22acf11f223d8a64abbbf4b3cb) feat: add partial config support to `talosctl cluster create` * [`64e9703f8`](https://github.com/siderolabs/talos/commit/64e9703f8648f997ff2e2e0fff932f74fd52d585) chore: add tests for the Kata Containers extension * [`9b6291925`](https://github.com/siderolabs/talos/commit/9b62919253f16cbbfec999da26f11e8751fbb345) feat: update pkgs * [`66f3ffdd4`](https://github.com/siderolabs/talos/commit/66f3ffdd4ad69ec690c680868cc95697eb1fba48) fix: ensure that Talos runs in a pod (container) * [`9dbc33972`](https://github.com/siderolabs/talos/commit/9dbc33972a2ded3818fabd9b157604d26926e3c9) feat: add basic syslog implementation * [`0b7a27e6a`](https://github.com/siderolabs/talos/commit/0b7a27e6a122e7cacb5ff82a7f6cae005435ae54) feat: allow access to all resources over siderolink in maintenance mode * [`53721883d`](https://github.com/siderolabs/talos/commit/53721883d50bd9979edeb4f94a0f1cfcf74d4d80) feat: support AWS KMS for the SecureBoot signing * [`7ee999f8a`](https://github.com/siderolabs/talos/commit/7ee999f8a3906eda23b7657da4c4212886a81626) fix: disable KubeSpan endpoint harvesting by default * [`7b87c7fe9`](https://github.com/siderolabs/talos/commit/7b87c7fe97d01f33eb621bb631d482f975da3feb) chore: bump Go dependencies * [`8e9596d3c`](https://github.com/siderolabs/talos/commit/8e9596d3c65246824e921f6cb9dfcda96b5ff52c) docs: rpi talosctl install update * [`493bb60f8`](https://github.com/siderolabs/talos/commit/493bb60f81075181c4f71af546674871f4616067) fix: correctly handle partial configs in `DNSUpstreamController` * [`6deb10ae2`](https://github.com/siderolabs/talos/commit/6deb10ae25efa1d96dd7416045c99b178b04e020) chore: deprecate `environmentFile` for extensions * [`f8b4ee82a`](https://github.com/siderolabs/talos/commit/f8b4ee82aeba990d8e34b7c95debf30c4a626298) chore: update extensions test * [`1366ce14a`](https://github.com/siderolabs/talos/commit/1366ce14a8b0bf72ac884147497e354fb33ef3fa) feat: update Kubernetes to v1.30.0-alpha.2 * [`559308ef7`](https://github.com/siderolabs/talos/commit/559308ef7e482786cc3554002bcd9fb05e0459c8) fix: use MachineStatus resource to check for boot done * [`15e8bca2b`](https://github.com/siderolabs/talos/commit/15e8bca2b2f839ee138faa14cb3931af173d258f) feat: support environment in `ExtensionServicesConfig` * [`3fe82ec46`](https://github.com/siderolabs/talos/commit/3fe82ec461995b680ecf060af75b47cd175a6342) feat: custom image settings for k8s upgrade * [`fa3b93370`](https://github.com/siderolabs/talos/commit/fa3b93370501009283e110b74876b18ce6bad4f9) chore: replace fmt.Errorf with errors.New where possible * [`d4521ee9c`](https://github.com/siderolabs/talos/commit/d4521ee9c472622fb2ef3c8570c1fa1c46332c16) feat: update kernel with sfc driver and LSM updates * [`2f0421b40`](https://github.com/siderolabs/talos/commit/2f0421b406ee252e9197c0b4589c0b33662bef34) fix: run xfs_repair on invalid argument error * [`f868fb8e8`](https://github.com/siderolabs/talos/commit/f868fb8e8f50e1acaa1743001d5b4f702bf29294) docs: update vmware tools url * [`fa2d34dd8`](https://github.com/siderolabs/talos/commit/fa2d34dd8875e6a09c257acfb9321c1230658b87) chore: enable v6 support on the same port * [`83e0b0c19`](https://github.com/siderolabs/talos/commit/83e0b0c19aaca7d413483b3a908c9dc3b4289203) chore: adjust dns sockets settings * [`a1ec1705b`](https://github.com/siderolabs/talos/commit/a1ec1705bc5d1f7c66dbb8549af42fc3b4778400) chore: update Go to 1.22.0 * [`76b50fcd4`](https://github.com/siderolabs/talos/commit/76b50fcd4ae2a5d602997cc360c9dcb45e4243e8) chore: add Ænix to the Adopters list * [`5324d3916`](https://github.com/siderolabs/talos/commit/5324d391671dfbf918aee1bd6b095adffadecf8e) chore: bump stuff * [`087b50f42`](https://github.com/siderolabs/talos/commit/087b50f42932e4da883de254984bce4ad7858b90) feat: support systemd-boot ISO enroll keys option * [`afa71d6b0`](https://github.com/siderolabs/talos/commit/afa71d6b028c33333db51495a3db41b758f38435) chore: use "handle-like" resource in `DNSResolveCacheController` * [`013e13070`](https://github.com/siderolabs/talos/commit/013e130702758dcd8f44c84de8090d624aa5c7b9) fix: error with decoding config document with wrong apiVersion * [`1e77bb1c3`](https://github.com/siderolabs/talos/commit/1e77bb1c3dde3c6a54bc4174eafc09846ff59e62) chore: allow custom pkgs to build talos * [`3f8a85f1b`](https://github.com/siderolabs/talos/commit/3f8a85f1b390936cf7d76a146f6b76973be1e474) fix: unlock the upgrade mutex properly * [`61c3331b1`](https://github.com/siderolabs/talos/commit/61c3331b148901a3137de6a087d561a6db8f4dfc) docs: update indentation in vip.md * [`383e528df`](https://github.com/siderolabs/talos/commit/383e528df8c52ad44402c830fb3611b66c71fc7a) chore: allow uuid-based hostnames in talosctl cluster create * [`1e6c8c4de`](https://github.com/siderolabs/talos/commit/1e6c8c4dec1e71f0d83914c3a0d7b907b21dc3b0) feat: extensions services config * [`989ca3ade`](https://github.com/siderolabs/talos/commit/989ca3ade194bb0cd5c162d5d8973c133e381501) feat: add OpenNebula platform support * [`914f88778`](https://github.com/siderolabs/talos/commit/914f88778838abe51f24ec3a9574e91836561e9e) docs: update nocloud.md Proxmox information * [`a04cc8015`](https://github.com/siderolabs/talos/commit/a04cc80154ed94e970615714fd8dff9cd8cf8ca9) fix: pass TTL when generating client certificate * [`3fe8c12ca`](https://github.com/siderolabs/talos/commit/3fe8c12ca654790695417b3d4f6bb5517e5902b5) fix: add log line about controller runtime failing * [`ddbabc7e5`](https://github.com/siderolabs/talos/commit/ddbabc7e58e476c95d7bb15f325f612a3d8fc86c) fix: use a separate cgroup for each extension service * [`6ccdd2c09`](https://github.com/siderolabs/talos/commit/6ccdd2c09c88eb2fe8b5b382dbd94816865381d3) chore: fix markdown-lint call * [`4184e617a`](https://github.com/siderolabs/talos/commit/4184e617ab92b8f41c2540bf55aa4d502778dcad) chore: add test for wasmedge runtime extension * [`95ea3a6c6`](https://github.com/siderolabs/talos/commit/95ea3a6c65a952fef533016b7116212c21609aac) chore: bump timeout in acquire tests * [`c19a505d8`](https://github.com/siderolabs/talos/commit/c19a505d8cde234e12f729183e8c7272ac049159) chore: bump docker dind image * [`d7d4154d5`](https://github.com/siderolabs/talos/commit/d7d4154d5dc817f91771b25b358825dae803de7f) chore: remove channel blocking in qemu launch

### Changes from siderolabs/crypto
1 commit

* [`1c94bb3`](https://github.com/siderolabs/crypto/commit/1c94bb3967a427ba52c779a1b705f5aea466dc57) chore: bump dependencies

### Changes from siderolabs/discovery-api
1 commit

* [`e1dc7bb`](https://github.com/siderolabs/discovery-api/commit/e1dc7bbd44f52e799fe65a6bd43a40973d611a3c) chore: rekres, update dependencies

### Changes from siderolabs/discovery-client
3 commits

* [`f4095a1`](https://github.com/siderolabs/discovery-client/commit/f4095a109d3947d1a1f470446ef40e1b386aeaf1) chore: bump discovery API to v0.1.4 * [`fbb1cea`](https://github.com/siderolabs/discovery-client/commit/fbb1cea89609242e20f6cb35b4bfec12ade4144e) fix: keepalive interval calculation * [`ff8f4be`](https://github.com/siderolabs/discovery-client/commit/ff8f4be618f077f91ce1f9b8240c050719623582) fix: enable gRPC keepalives

### Changes from siderolabs/extras
4 commits

* [`47bb718`](https://github.com/siderolabs/extras/commit/47bb718838db5bda55c4c8fcf2f17424db4325a9) chore: update base pkgs * [`60793cd`](https://github.com/siderolabs/extras/commit/60793cdc4cc9ef1b0690a49ce0d8a79790de519b) feat: update Go to 1.22.1 * [`c4934e1`](https://github.com/siderolabs/extras/commit/c4934e1f88ba9e16d500239831ce8412c6f93c38) feat: update Go to 1.22 * [`8909d6f`](https://github.com/siderolabs/extras/commit/8909d6f7773542450c756ce4950c9725a05a8f65) chore: update Go to 1.21.5

### Changes from siderolabs/gen
1 commit

* [`238baf9`](https://github.com/siderolabs/gen/commit/238baf95e228d40f9f5b765b346688c704052715) chore: add typesafe `SyncMap` and bump stuff

### Changes from siderolabs/go-api-signature
21 commits

* [`cf2bd06`](https://github.com/siderolabs/go-api-signature/commit/cf2bd06af87c946d6cdd61e127528f89e6f50591) chore: bump dependencies * [`370cebf`](https://github.com/siderolabs/go-api-signature/commit/370cebf63d5b26a3b711ec05b0dedc283d94b136) fix: always print the login URL on key renew flow * [`d28609a`](https://github.com/siderolabs/go-api-signature/commit/d28609aa214f364166cf60533d03a811f9ce2af6) feat: move in the cli grpc interceptor logic, support service account in env * [`4602acc`](https://github.com/siderolabs/go-api-signature/commit/4602acc2f06134aed4940c6c45f3a5fbd9332a72) chore: add a dummy workflow * [`cfd21b6`](https://github.com/siderolabs/go-api-signature/commit/cfd21b6a51d21a344e98b7f434bf3e9198e12b42) fix: support validating signatures generated with the time in the future * [`74dd3dc`](https://github.com/siderolabs/go-api-signature/commit/74dd3dcc1d980837eced68e47d897b03945dd4ee) chore: bump deps * [`d78bedb`](https://github.com/siderolabs/go-api-signature/commit/d78bedb1a7d348832ba9db0438b1fc099aa2dd99) chore: bump deps * [`a034e9f`](https://github.com/siderolabs/go-api-signature/commit/a034e9ff315ba4a56115acc7ad0fb99d0dc77800) feat: replace scopes with roles * [`5b4f3bb`](https://github.com/siderolabs/go-api-signature/commit/5b4f3bb291b7bbec70b690f2969954255ccb8a22) chore: run rekres * [`9dba116`](https://github.com/siderolabs/go-api-signature/commit/9dba116c0838ecc0342a9af1e81e68e04b133623) chore: remove time.Sleep hack * [`e84e686`](https://github.com/siderolabs/go-api-signature/commit/e84e68658095aecead59982255b242ba8bef0fc5) chore: bump dependencies * [`8baaf8a`](https://github.com/siderolabs/go-api-signature/commit/8baaf8a99a28adda6dbdc0d7c38e78b290c84d96) chore: bump deps * [`5f27e1e`](https://github.com/siderolabs/go-api-signature/commit/5f27e1ebc06e26dea6a8102630a5b3529283eb9e) chore: add renovate bot and bump deps * [`69886dc`](https://github.com/siderolabs/go-api-signature/commit/69886dcc1343561add3b4b86ef160e0a1876d97f) feat: allow custom validations on PGP key * [`63d4da3`](https://github.com/siderolabs/go-api-signature/commit/63d4da31ae67052129c5ec795b61fb9c05a52441) fix: limit clock skew for short-lived keys * [`cdb9722`](https://github.com/siderolabs/go-api-signature/commit/cdb9722becf1aaeeaa1e9529dac19f3d5281f0a1) feat: add support for +-5 min clock skew * [`7b80a50`](https://github.com/siderolabs/go-api-signature/commit/7b80a50eea28d9273a49445cc3d39492db2e085b) refactor: use options pattern in RegisterPGPPublicKey * [`c647861`](https://github.com/siderolabs/go-api-signature/commit/c6478610d97a99967e903bdba1a4b7fab20e64b9) feat: add scopes to RegisterPublicKeyRequest * [`5d3647e`](https://github.com/siderolabs/go-api-signature/commit/5d3647e1d988e3162d0e851757fec951f6bb00c9) feat: provide more client PGP functions * [`2b682ec`](https://github.com/siderolabs/go-api-signature/commit/2b682ec61e83260b11cdf65d2f3723a89e4afa88) feat: initial version * [`a4c2943`](https://github.com/siderolabs/go-api-signature/commit/a4c294367c35d1234470d09c6151eed616a0c031) chore: initial commit

### Changes from siderolabs/go-copy
2 commits

* [`aa4ade4`](https://github.com/siderolabs/go-copy/commit/aa4ade4dfbac3695846016d4c3e49bae88da22a3) chore: add initial code * [`52a6d48`](https://github.com/siderolabs/go-copy/commit/52a6d485fc7dce9c3d1f00977ed61ea75c4a4e3b) chore: go-copy repo

### Changes from siderolabs/go-debug
1 commit

* [`0c2be80`](https://github.com/siderolabs/go-debug/commit/0c2be80d9d60034f3352a34841b615ef7bb0a62c) chore: run rekres (update to Go 1.22)

### Changes from siderolabs/go-kmsg
2 commits

* [`e358d13`](https://github.com/siderolabs/go-kmsg/commit/e358d13e5bdab79568d6ffea4b071c1530aa8e3d) fix: decode escape sequences while reading from kmsg * [`4297bd5`](https://github.com/siderolabs/go-kmsg/commit/4297bd599c918a5a874fb3b9f3119b394bd70899) feat: add BSD support

### Changes from siderolabs/go-kubernetes
1 commit

* [`ddd4c69`](https://github.com/siderolabs/go-kubernetes/commit/ddd4c69a16f173e080f24aeabb6b472f42d140b6) feat: add support for Kubernetes 1.30

### Changes from siderolabs/go-loadbalancer
1 commit

* [`aab4671`](https://github.com/siderolabs/go-loadbalancer/commit/aab4671fae0d14662a8d7167829c8c6725d28b38) chore: rekres, update dependencies

### Changes from siderolabs/pkgs
37 commits

* [`8804a60`](https://github.com/siderolabs/pkgs/commit/8804a608bfc19b64df03b2dab404b52c9b54c85d) chore: update dependencies * [`a587b42`](https://github.com/siderolabs/pkgs/commit/a587b423a9a0b9173631d588947703501522d4d0) feat: enable most common amd64 watchdog drivers * [`3aacf03`](https://github.com/siderolabs/pkgs/commit/3aacf0345329e60c49f3116882c56fa14856dec7) feat: update releases * [`e5c0c79`](https://github.com/siderolabs/pkgs/commit/e5c0c79b8d62448875bb21ca5701eacccd07c7e9) feat: build NVMe target module * [`cb39126`](https://github.com/siderolabs/pkgs/commit/cb3912640aa8acd5571b4883f385058dd2993724) chore: re-enable zfs pkg * [`d9c1540`](https://github.com/siderolabs/pkgs/commit/d9c15407b5b32a7c3cec91b560f2306fd8438d14) feat: update releases * [`1904994`](https://github.com/siderolabs/pkgs/commit/1904994b740a5990f0b41e0ff134628ffe41650d) feat: enable VRF module * [`87eb013`](https://github.com/siderolabs/pkgs/commit/87eb0134fab2aa7e6a3d52d541a84847dd265329) feat: disable PCI busmastering on bridges during boot * [`30f18c8`](https://github.com/siderolabs/pkgs/commit/30f18c8727885ae3957791bd030b3ce35cc3ffbb) chore: remove symlinks and broken binaries * [`7811e5e`](https://github.com/siderolabs/pkgs/commit/7811e5eee6620431bd3d2aaf588623600b50b4a2) chore: set `PREEMPT_NONE` as recommended for servers * [`65006ed`](https://github.com/siderolabs/pkgs/commit/65006ed198f31e97a77ddfded52043182f2c6e92) fix: enable KFD support in kernel * [`510a3f9`](https://github.com/siderolabs/pkgs/commit/510a3f9a0b09617c5a6350b16076ee5720c520d8) feat: add support for Solarflare SFC9100 and SFC9200 family * [`4340508`](https://github.com/siderolabs/pkgs/commit/4340508d59acb9ca6da2cdad0165910f7216a990) feat: enable CONFIG_SECURITY_PATH and CONFIG_BPF_LSM * [`0ec4cc3`](https://github.com/siderolabs/pkgs/commit/0ec4cc32a50cf6a98dc26dcccbc1f19fea0b2692) feat: update Go to 1.22 * [`36c08ae`](https://github.com/siderolabs/pkgs/commit/36c08ae1a6cff80d5571017aaadbb0d57faaa29b) feat: enable PSI (pressure stall information) * [`0853224`](https://github.com/siderolabs/pkgs/commit/08532249972b4a490ea8c4f08a34ebedffb4adda) feat: update Linux to 6.6.16 * [`96cc841`](https://github.com/siderolabs/pkgs/commit/96cc841fda61f4b91b3a6d57a3baa6650c223957) chore: bump deps * [`064fd58`](https://github.com/siderolabs/pkgs/commit/064fd581bb529db7cef32b7b4adb9b1a2f8fbd98) feat: update Linux to 6.6.14, enable XDP * [`efbbd23`](https://github.com/siderolabs/pkgs/commit/efbbd2382e1e5cfeef8717d2212b43a9b760feaf) feat: update Linux to 6.6.13 * [`dfb5026`](https://github.com/siderolabs/pkgs/commit/dfb5026b73f641ee3000e2460e250ec33a7e9a56) chore: switch to git ref for raspberrypi firmware * [`4af2d0f`](https://github.com/siderolabs/pkgs/commit/4af2d0fdfaf31c31d7f5d24fdc01d5d9d0b27e37) feat: update Linux to 6.1.74 * [`2358efe`](https://github.com/siderolabs/pkgs/commit/2358efe6701139f074213a32d39a314729821cf1) fix: enable FUSION_SPI driver * [`f376a53`](https://github.com/siderolabs/pkgs/commit/f376a539352926b402e7685700defedafa4c58b0) chore: bump dependencies * [`583e519`](https://github.com/siderolabs/pkgs/commit/583e519f2dad762bc63bc5b2bff963c61a7e1c16) feat: add v4l usb video class (webcam) drivers * [`2d3ca68`](https://github.com/siderolabs/pkgs/commit/2d3ca68152d39560b8f34087cdd2e6d386f020d2) feat: enable NBD * [`f647edd`](https://github.com/siderolabs/pkgs/commit/f647edd497852feb4d537d191ef895d33420b5c4) feat: update Linux to 6.1.69 * [`6af1691`](https://github.com/siderolabs/pkgs/commit/6af169101a8a5459706f45a9fe861f90ed766284) feat: enable VFIO also on amd64 * [`d633cd6`](https://github.com/siderolabs/pkgs/commit/d633cd657081623a3681a905c4b875ed84fb4e6f) feat: enable modules for mlx infiniband * [`4c59641`](https://github.com/siderolabs/pkgs/commit/4c596414acd8722c97f45e0839c6d772f3e045a8) fix: zfs module build * [`e325097`](https://github.com/siderolabs/pkgs/commit/e32509763b2953b920c73791ca157c2c1180f0be) feat: enable nct6683 sensors as module * [`d6185ec`](https://github.com/siderolabs/pkgs/commit/d6185ec3ad68e10a9545ca0528f7e517c803f1a8) feat: enable IRQ remapping on amd64 * [`814dc60`](https://github.com/siderolabs/pkgs/commit/814dc601fb79e2b973d41e82cd16872dee513161) feat: update containerd to 1.7.11 * [`dd71790`](https://github.com/siderolabs/pkgs/commit/dd717902792d68576f7ab799a1342138fae475b2) chore: rekres to fix 'failed' build on main * [`a36dec4`](https://github.com/siderolabs/pkgs/commit/a36dec48a1c0de4ca178222ee88251701a881bdb) feat: split more device drivers into modules * [`97270a2`](https://github.com/siderolabs/pkgs/commit/97270a2c26a9c61c2fff5fb104ff0a2bc9fbdd5d) feat: update Linux to 6.1.67 * [`8a73907`](https://github.com/siderolabs/pkgs/commit/8a73907886c003c33cbc03a2a81f0a473fa93c00) feat: update Go to 1.21.5 * [`8f0ffb9`](https://github.com/siderolabs/pkgs/commit/8f0ffb92f88b1a795cfc627abd3ca74cb910cb78) feat: update zfs to v2.2.2

### Changes from siderolabs/tools
12 commits

* [`cb5fd56`](https://github.com/siderolabs/tools/commit/cb5fd5627c87cc49a730d6b30fcdb61d6dcb7caf) chore: update xz to 5.6.1 * [`14bf457`](https://github.com/siderolabs/tools/commit/14bf457275686e64dad8c596e869b4e78e7c9494) fix: use musl 1.2.4 in tools, revert kmod back to 32 * [`6c1f73d`](https://github.com/siderolabs/tools/commit/6c1f73d4f51fae028a3aa3c97884b80801826a40) fix: revert kmod to version 31 * [`59fd552`](https://github.com/siderolabs/tools/commit/59fd5520aadd761943982ce7e52b005f8b60bc8b) feat: update releases * [`eff5d16`](https://github.com/siderolabs/tools/commit/eff5d16b3af4726b4757ce3a2c78372b4afaf7eb) feat: update Go to 1.22.1 * [`b6b4d9e`](https://github.com/siderolabs/tools/commit/b6b4d9eb8aba4648eb39001d25c3dd711b2d8fce) feat: update Go to 1.22 * [`f4b41d1`](https://github.com/siderolabs/tools/commit/f4b41d189844957a71814ee3e98983553f7ba0ad) fix: rust toolchain * [`8cc79e6`](https://github.com/siderolabs/tools/commit/8cc79e68499624d4dde77d5c7d12aab957aaa22a) feat: update dependencies * [`c7076eb`](https://github.com/siderolabs/tools/commit/c7076eb9f2246f1d6cca431968d2fcce6bd1f951) chore: bump dependencies * [`a80a2aa`](https://github.com/siderolabs/tools/commit/a80a2aa0307d90f07c8a239459191a3f68cdd5d3) feat: update Go to 1.21.6 * [`b677a2b`](https://github.com/siderolabs/tools/commit/b677a2b99fd658710c34f7472df350787346ea35) feat: add rust build stage * [`1659d82`](https://github.com/siderolabs/tools/commit/1659d82e78511522e2820efccb892235d6d7b279) feat: update Go to 1.21.5

### Dependency Changes * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.9.0 -> v1.10.0 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.4.0 -> v1.5.1 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.0.0 -> v1.1.0 * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.0.1 -> v1.1.0 * **github.com/alexflint/go-filemutex** v1.3.0 **_new_** * **github.com/aws/aws-sdk-go-v2/config** v1.25.6 -> v1.27.7 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.14.5 -> v1.15.3 * **github.com/aws/aws-sdk-go-v2/service/kms** v1.29.2 **_new_** * **github.com/aws/smithy-go** v1.17.0 -> v1.20.1 * **github.com/beevik/ntp** v1.3.0 -> v1.3.1 * **github.com/containerd/cgroups/v3** v3.0.2 -> v3.0.3 * **github.com/containerd/containerd** v1.7.9 -> v1.7.14 * **github.com/containernetworking/plugins** v1.3.0 -> v1.4.1 * **github.com/coredns/coredns** v1.11.1 **_new_** * **github.com/cosi-project/runtime** v0.3.19 -> v0.4.0-alpha.9 * **github.com/docker/docker** v24.0.7 -> v25.0.4 * **github.com/docker/go-connections** v0.4.0 -> v0.5.0 * **github.com/foxboron/go-uefi** 18b9ba9cd4c3 -> 48be911532c2 * **github.com/gdamore/tcell/v2** v2.6.0 -> v2.7.4 * **github.com/google/go-containerregistry** v0.16.1 -> v0.19.0 * **github.com/google/go-tpm** v0.9.0 -> ee6cbcd136f8 * **github.com/google/nftables** v0.1.0 -> v0.2.0 * **github.com/google/uuid** v1.4.0 -> v1.6.0 * **github.com/grpc-ecosystem/go-grpc-middleware/v2** v2.1.0 **_new_** * **github.com/hetznercloud/hcloud-go/v2** v2.4.0 -> v2.6.0 * **github.com/insomniacslk/dhcp** b0416c0f187a -> c728f5dd21c8 * **github.com/jeromer/syslogparser** v1.1.0 **_new_** * **github.com/jsimonetti/rtnetlink** v1.4.0 -> v1.4.1 * **github.com/miekg/dns** v1.1.58 **_new_** * **github.com/opencontainers/image-spec** v1.1.0-rc4 -> v1.1.0 * **github.com/opencontainers/runtime-spec** v1.1.0-rc.1 -> v1.2.0 * **github.com/packethost/packngo** v0.30.0 -> v0.31.0 * **github.com/pmorjan/kmod** v1.1.0 -> v1.1.1 * **github.com/prometheus/procfs** v0.12.0 -> v0.13.0 * **github.com/rivo/tview** 33a1d271f2b6 -> e804876934a1 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.21 -> v1.0.0-beta.25 * **github.com/siderolabs/crypto** v0.4.1 -> v0.4.2 * **github.com/siderolabs/discovery-api** v0.1.3 -> v0.1.4 * **github.com/siderolabs/discovery-client** v0.1.5 -> v0.1.8 * **github.com/siderolabs/extras** v1.6.0-1-g113887a -> v1.7.0-alpha.0-3-g47bb718 * **github.com/siderolabs/gen** v0.4.7 -> v0.4.8 * **github.com/siderolabs/go-api-signature** v0.3.2 **_new_** * **github.com/siderolabs/go-copy** v0.1.0 **_new_** * **github.com/siderolabs/go-debug** v0.2.3 -> v0.3.0 * **github.com/siderolabs/go-kmsg** v0.1.3 -> v0.1.4 * **github.com/siderolabs/go-kubernetes** v0.2.8 -> v0.2.9 * **github.com/siderolabs/go-loadbalancer** v0.3.2 -> v0.3.3 * **github.com/siderolabs/pkgs** v1.6.0-5-g3ae2450 -> v1.7.0-alpha.0-35-g8804a60 * **github.com/siderolabs/talos/pkg/machinery** v1.6.0 -> v1.7.0-alpha.0 * **github.com/siderolabs/tools** v1.6.0-1-g336d248 -> v1.7.0-alpha.0-11-gcb5fd56 * **github.com/stretchr/testify** v1.8.4 -> v1.9.0 * **github.com/u-root/u-root** v0.11.0 -> v0.14.0 * **go.etcd.io/etcd/api/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/client/pkg/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/client/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/etcdutl/v3** v3.5.11 -> v3.5.12 * **go.uber.org/zap** v1.26.0 -> v1.27.0 * **go4.org/netipx** 6213f710f925 -> fdeea329fbba * **golang.org/x/net** v0.19.0 -> v0.22.0 * **golang.org/x/oauth2** v0.15.0 -> v0.18.0 * **golang.org/x/sync** v0.5.0 -> v0.6.0 * **golang.org/x/sys** v0.15.0 -> v0.18.0 * **golang.org/x/term** v0.15.0 -> v0.18.0 * **google.golang.org/grpc** v1.59.0 -> v1.62.1 * **google.golang.org/protobuf** v1.31.0 -> v1.33.0 * **k8s.io/api** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/apimachinery** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/apiserver** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/client-go** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/component-base** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/cri-api** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/klog/v2** v2.110.1 -> v2.120.1 * **k8s.io/kube-scheduler** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/kubectl** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/kubelet** v0.29.0 -> v0.30.0-beta.0 * **k8s.io/pod-security-admission** v0.30.0-beta.0 **_new_** Previous release can be found at [v1.6.0](https://github.com/siderolabs/talos/releases/tag/v1.6.0) ## [Talos 1.7.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.7.0-alpha.0) (2024-02-01) Welcome to the v1.7.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Device Selectors Talos Linux now supports `physical: true` qualifier for device selectors, it selects non-virtual network interfaces (i.e. `en0` is selected, while `bond0` is not). ### DNS Caching Talos Linux now provides a caching DNS resolver for host workloads (including host networking pods). It can be disabled with: ```yaml machine: features: localDNS: false ``` ### Known Problems ZFS and DRBD extensions are disabled in this release due to incompatibility with the latest Linux kernel. ### Kubernetes API Server Service Account Key Talos Linux starting from this release uses RSA key for Kubernetes API Server Service Account instead of ECDSA key to provide better compatibility with external OpenID Connect implementations. ### Component Updates Linux: 6.6.14 etcd: 3.5.11 Kubernetes: 1.29.1 containerd: 1.7.13 runc: 1.1.12 Flannel: 0.24.1 Talos is built with Go 1.21.6. ### Contributors * Andrey Smirnov * Dmitriy Matrenichev * Utku Ozdemir * Noel Georgi * Andrey Smirnov * Radosław Piliszek * Artem Chernyshev * Spencer Smith * Steve Francis * Anthony ARNAUD * Cas de Reuver * Christian Mohn * Drew Hess * ExtraClock * Hervé Werner * JJGadgets * Jacob McSwain * Jonomir * Sebastian Gaiser * Serge Logvinov * Tim Jones * edwinavalos * stereobutter ### Changes
81 commits

* [`2ff81c06b`](https://github.com/siderolabs/talos/commit/2ff81c06bc1123af2fa7286fff15d9de0b8a868a) feat: update runc 1.1.12, containerd 1.7.13 * [`9d8cd4d05`](https://github.com/siderolabs/talos/commit/9d8cd4d058e73d30e4864e67377cf55390467725) chore: drop deprecated method EtcdRemoveMember * [`17567f19b`](https://github.com/siderolabs/talos/commit/17567f19be39eeaf0d9a9aa3cd773b73d537814a) fix: take into account the moment seen when cleaning up CRI images * [`aa03204b8`](https://github.com/siderolabs/talos/commit/aa03204b864d8d8ac5a7ee4986a06230863043fb) docs: document the process of building custom kernel packages * [`7af48bd55`](https://github.com/siderolabs/talos/commit/7af48bd5598e61357cdb9b31dd57de6479b1ce7c) feat: use RSA key for kube-apiserver service account key * [`a5e13c696`](https://github.com/siderolabs/talos/commit/a5e13c696d1e1cb8e894a4133791c74470687553) fix: retry blockdevice open in the installer * [`593afeea3`](https://github.com/siderolabs/talos/commit/593afeea38a75de01041e3126cb0ad3443f6e1a1) fix: run the interactive installer loop to report errors * [`87be76b87`](https://github.com/siderolabs/talos/commit/87be76b8788d179058be14c53e1092054b08c5dd) fix: be more tolerant to error handling in Mounts API * [`03add7503`](https://github.com/siderolabs/talos/commit/03add750309dcdeb7c2b87cd72da29a3e228e56e) docs: add section on using imager with extensions from tarball * [`ee0fb5eff`](https://github.com/siderolabs/talos/commit/ee0fb5effce82fec99860b5910e0fb6e5147b49b) docs: consolidate certificate management articles * [`9c14dea20`](https://github.com/siderolabs/talos/commit/9c14dea209bba69b471fd43eb2e8ba05de3ff549) chore: bump coredns * [`ebeef2852`](https://github.com/siderolabs/talos/commit/ebeef28525f71189727200115d62fe8d713d1d07) feat: implement local caching dns server * [`4a3691a27`](https://github.com/siderolabs/talos/commit/4a3691a2739871be5eff4b313c30d454a143fbc4) docs: fix broken links in metal-network-configuration.md * [`c4ed189a6`](https://github.com/siderolabs/talos/commit/c4ed189a6912238350efd5f0181a6ef45728fc63) docs: provide sane defaults for each release series in vmware script * [`8138d54c6`](https://github.com/siderolabs/talos/commit/8138d54c6c9bae4255216007595fa302bc418c1a) docs: clarify node taints/labels for worker nodes * [`b44551ccd`](https://github.com/siderolabs/talos/commit/b44551ccdb0dd0ceaffd2e484c86ce91b25fe841) feat: update Linux to 6.6.13 * [`385707c5f`](https://github.com/siderolabs/talos/commit/385707c5f39e733c8f27532435cd14f5f2ff067d) docs: update vmware.sh * [`d1a79b845`](https://github.com/siderolabs/talos/commit/d1a79b845f025defafb468fb6b5e86957cfad4fc) docs: fix small typo in etcd maintenance guide * [`cf0603330`](https://github.com/siderolabs/talos/commit/cf0603330a5c852163642a6b3844d1dcc3892cf6) docs: copy generated JSON schema to host * [`f11139c22`](https://github.com/siderolabs/talos/commit/f11139c229765cf82cadc84e6fa81d860005100b) docs: document local path provisioner install * [`e0dfbb8fb`](https://github.com/siderolabs/talos/commit/e0dfbb8fba3c50652d0ecbae1db0b0660d0766a6) fix: allow META encoded values to be compressed * [`d677901b6`](https://github.com/siderolabs/talos/commit/d677901b672eec46b8b5edf57c680813b8fcf697) feat: implement device selector for 'physical' * [`7d1117289`](https://github.com/siderolabs/talos/commit/7d1117289658ac04707b09f64a1dc70514a9fba9) docs: add missing talosconfig flag * [`8a1732bcb`](https://github.com/siderolabs/talos/commit/8a1732bcb12deb4444ae87d22cc15d8b968b867d) fix: pull in `mptspi` driver * [`c1e45071f`](https://github.com/siderolabs/talos/commit/c1e45071f0cb0e48ee35d2f87b483fffb05c6123) refactor: use etcd configuration from the EtcdSpec resource * [`4e9b688d3`](https://github.com/siderolabs/talos/commit/4e9b688d3f8bc809e0b2f012d5e58c27de85d1e0) fix: use correct TTL for talosconfig in `talosctl config new` * [`fb5ad0555`](https://github.com/siderolabs/talos/commit/fb5ad05551e08404cb8acde01202c4ae88ddd25a) feat: update Kubernetes default to 1.29.1 * [`fe24139f3`](https://github.com/siderolabs/talos/commit/fe24139f3c0b3f37c8266e5d6c5091950e3a647c) docs: fork docs for v1.7 * [`1c2d10ccc`](https://github.com/siderolabs/talos/commit/1c2d10ccccb84a6d1e008af23866fa13cc14d094) chore: bump dependencies * [`a599e3867`](https://github.com/siderolabs/talos/commit/a599e38674af448fe5cac210f5d80826d3b08a12) chore: allow custom registry to build installer/imager * [`3911ddf7b`](https://github.com/siderolabs/talos/commit/3911ddf7bd630286358f1696adf9bdac207e1b9d) docs: add how-to for cert management * [`b0ee0bfba`](https://github.com/siderolabs/talos/commit/b0ee0bfba3f4c9172c76422a8f8f10a4046c352b) fix: strategic patch merging for audit policy * [`474eccdc4`](https://github.com/siderolabs/talos/commit/474eccdc4cb1d0fab3ba0b370cc388bc8c9d363a) fix: watch bufer overrun for RouteStatus * [`cc06b5d7a`](https://github.com/siderolabs/talos/commit/cc06b5d7a659a7f5a35e86a82ee242344c303302) fix: fix .der output in `talosctl gen secureboot` * [`1dbb4abf4`](https://github.com/siderolabs/talos/commit/1dbb4abf43695d1dd18d51b0386cf644aba67d73) fix: update discovery service client to v0.1.6 * [`9782319c3`](https://github.com/siderolabs/talos/commit/9782319c31e496d998bdf9d505f32a4d8e6e937e) fix: support KubePrism settings in Kubernetes Discovery * [`6c5a0c281`](https://github.com/siderolabs/talos/commit/6c5a0c2811e3c0f3e1ca2a8fb871065df5bf9b46) feat: generate a single JSON schema for multidoc config * [`f70b47ddd`](https://github.com/siderolabs/talos/commit/f70b47dddc2599a618c68d8b403d9b37c61f2b71) fix: force KubePrism to connect using IPv4 * [`d5321e085`](https://github.com/siderolabs/talos/commit/d5321e085eb6c877b1b5b38d69eabb839b505297) fix: update kmsg with utf-8 fix * [`7fa7362dd`](https://github.com/siderolabs/talos/commit/7fa7362ddc0e8a0b85cffcaebc38abd772b355e2) fix: fix nodes on dashboard footer when node names are used in `--nodes` * [`ba88678f1`](https://github.com/siderolabs/talos/commit/ba88678f1a42b4e9f6c9de25bdc827330cfb254c) fix: merge ports and ingress configs correctly in NetworkRuleConfig * [`dea9bda2d`](https://github.com/siderolabs/talos/commit/dea9bda2d00feeb29bf4b2c91c2ca24b6cd362f2) fix: disk UUID & WWID always empty in `talosctl disks` * [`8dc112f36`](https://github.com/siderolabs/talos/commit/8dc112f36bd77ec72e5c501755aa4f056803efd0) chore: pull in NBD modules * [`f6926faab`](https://github.com/siderolabs/talos/commit/f6926faab5a8b878c600d60ef9d693026277f3ee) fix: default priority for ipv6 * [`e8758dcba`](https://github.com/siderolabs/talos/commit/e8758dcbad6d3188dfccd235dbab04c19dd1a6ed) chore: support http downloads for assets in talosctl cluster create * [`265f21be0`](https://github.com/siderolabs/talos/commit/265f21be09d68cc23764d690e9f9479b9d92d749) fix: replace the filemap implementation to not buffer in memory * [`8db3c5b3c`](https://github.com/siderolabs/talos/commit/8db3c5b3c63ad67043b876265ac4687cdcb0f0ff) fix: pick correctly base installer image layers * [`0a30ef784`](https://github.com/siderolabs/talos/commit/0a30ef78456e854419d0c593f9c97f40166102f3) fix: imager should support different Talos versions * [`d6342cda5`](https://github.com/siderolabs/talos/commit/d6342cda53027eb5d46dcb6f57fbb1cc31f920dd) docs: update latest version to v1.6.1 * [`e6e422b92`](https://github.com/siderolabs/talos/commit/e6e422b92ade5f24c898e09affdb6de8ee671cb0) chore: bump dependencies * [`5a19d078a`](https://github.com/siderolabs/talos/commit/5a19d078ad3205d201b11e0d60d5e07b379aba91) fix: properly overwrite files on install * [`9eb6cea78`](https://github.com/siderolabs/talos/commit/9eb6cea7890854173917a096bcffd6202487d38c) docs: secureboot sd-boot menu clarification * [`01f0cbe61`](https://github.com/siderolabs/talos/commit/01f0cbe61c32b3ff6e9d05f2c14c83223ce043fa) feat: support iPXE direct booting in `talosctl cluster create` * [`3ba84701d`](https://github.com/siderolabs/talos/commit/3ba84701d9f87f533b3039395d350b311f4a484f) feat: pull in kernel modules for mlx Infiniband and VFIO * [`ba993e0ed`](https://github.com/siderolabs/talos/commit/ba993e0edd20f927ff8d59f418e47c6cbf8a95b3) docs: announce that SecureBoot is available * [`241bc9312`](https://github.com/siderolabs/talos/commit/241bc9312edcadce83a64e92db807dbca74c80cc) fix: update the way secureboot signer fetches certificate (azure) * [`59b62398f`](https://github.com/siderolabs/talos/commit/59b62398f6265f310108954e9a775e4b8c080679) chore: modernize machined/pkg/controllers/k8s * [`760f793d5`](https://github.com/siderolabs/talos/commit/760f793d55f3965792f58fa3194977aea4f90e03) fix: use correct prefix when installing SBC files * [`0b94550c4`](https://github.com/siderolabs/talos/commit/0b94550c42730121c3d270758286dbefa95ea61c) chore: fix the gvisor test * [`3a787c1d6`](https://github.com/siderolabs/talos/commit/3a787c1d67ddca5102c7d9cbdab4ef1c17a605f4) docs: update 1.6 docs with Noel's feedback * [`d803e40ef`](https://github.com/siderolabs/talos/commit/d803e40ef2cf1030aab522006ba7287bac8b64c4) docs: provide documentation for Talos 1.6 * [`9a185a30f`](https://github.com/siderolabs/talos/commit/9a185a30f79a8d3481606235609c0e5a11c880cc) feat: update Kubernetes to v1.29.0 * [`5934815d2`](https://github.com/siderolabs/talos/commit/5934815d2fe975c4d8ddb2a26ef733d29565cdb2) chore: split more kernel modules on amd64 * [`10c59a6b9`](https://github.com/siderolabs/talos/commit/10c59a6b90310b8c58babf5beb108b59f4d74e4d) fix: leave discovery service later in the reset sequence * [`0c86ca1cc`](https://github.com/siderolabs/talos/commit/0c86ca1cc68e2646d63d19d96b01d3d5486dfc42) chore: enable kubespan+firewall for cilium tests * [`98fd722d5`](https://github.com/siderolabs/talos/commit/98fd722d5110b1422a15ede23873bcd15ab9562e) feat: provide compatibility for future Talos 1.7 * [`131a1b167`](https://github.com/siderolabs/talos/commit/131a1b1671899666d8676b5082cef39efb8f0fa1) fix: add a KubeSpan option to disable extra endpoint harvesting * [`4547ad9af`](https://github.com/siderolabs/talos/commit/4547ad9afa206405032618f9d94470d00ace8684) feat: send `actor id` to the SideroLink events sink * [`04e774547`](https://github.com/siderolabs/talos/commit/04e774547146f0733633b296c4432f4eef847265) docs: cap max heading level * [`6bb1e99aa`](https://github.com/siderolabs/talos/commit/6bb1e99aa3a8132508479b4ca8606522545d8d9a) chore: optimize pcap dump * [`4f9d3b975`](https://github.com/siderolabs/talos/commit/4f9d3b975fa689dc9eea4e44ff453d8b68ae54ef) feat: update Kubernetes to v1.29.0-rc.2 * [`46121c9fe`](https://github.com/siderolabs/talos/commit/46121c9fecb3603c2d2ae2de6152861ee7f19eaf) docs: rework machine config documentation generation * [`e128d3c82`](https://github.com/siderolabs/talos/commit/e128d3c827a406f96457322da87cbde2af233fa0) fix: talosctl cluster create not to enforce kubeprism always * [`320064c5a`](https://github.com/siderolabs/talos/commit/320064c5a869de6d52ba9a23394acaa5549e7aa1) feat: update Go 1.21.5, Linux 6.1.65, etcd 3.5.11 * [`270604bea`](https://github.com/siderolabs/talos/commit/270604bead50423697d6fabffa6bbd7c7b2fbe9e) fix: support user disks via symlinks * [`4f195dd27`](https://github.com/siderolabs/talos/commit/4f195dd271eb38446561f8708a9623324072a0e9) chore: fix the release.toml * [`474fa0480`](https://github.com/siderolabs/talos/commit/474fa0480dd68d112a608548e4d0a0c4efa39e20) fix: store and execute desired action on emergency action * [`515ae2a18`](https://github.com/siderolabs/talos/commit/515ae2a184374e0ac72e3321104265918e45e391) docs: extend hetzner-cloud docs for arm64 * [`eecc4dbd5`](https://github.com/siderolabs/talos/commit/eecc4dbd5198cca5b66e5c3018c407cd38b13c80) fix: trim leading spaces\newlines in inline manifest contents * [`dbf274ddf`](https://github.com/siderolabs/talos/commit/dbf274ddf7b819941c88932e28d2fe362876ec68) fix: skip writing the file if the contents haven't changed * [`6329222bd`](https://github.com/siderolabs/talos/commit/6329222bdcfd5ab29bc46ca03bb0b1d22ada9424) fix: do not panic in `merge.Merge` if map value is nil

### Changes from siderolabs/discovery-client
1 commit

* [`ff8f4be`](https://github.com/siderolabs/discovery-client/commit/ff8f4be618f077f91ce1f9b8240c050719623582) fix: enable gRPC keepalives

### Changes from siderolabs/extras
1 commit

* [`8909d6f`](https://github.com/siderolabs/extras/commit/8909d6f7773542450c756ce4950c9725a05a8f65) chore: update Go to 1.21.5

### Changes from siderolabs/go-api-signature
20 commits

* [`370cebf`](https://github.com/siderolabs/go-api-signature/commit/370cebf63d5b26a3b711ec05b0dedc283d94b136) fix: always print the login URL on key renew flow * [`d28609a`](https://github.com/siderolabs/go-api-signature/commit/d28609aa214f364166cf60533d03a811f9ce2af6) feat: move in the cli grpc interceptor logic, support service account in env * [`4602acc`](https://github.com/siderolabs/go-api-signature/commit/4602acc2f06134aed4940c6c45f3a5fbd9332a72) chore: add a dummy workflow * [`cfd21b6`](https://github.com/siderolabs/go-api-signature/commit/cfd21b6a51d21a344e98b7f434bf3e9198e12b42) fix: support validating signatures generated with the time in the future * [`74dd3dc`](https://github.com/siderolabs/go-api-signature/commit/74dd3dcc1d980837eced68e47d897b03945dd4ee) chore: bump deps * [`d78bedb`](https://github.com/siderolabs/go-api-signature/commit/d78bedb1a7d348832ba9db0438b1fc099aa2dd99) chore: bump deps * [`a034e9f`](https://github.com/siderolabs/go-api-signature/commit/a034e9ff315ba4a56115acc7ad0fb99d0dc77800) feat: replace scopes with roles * [`5b4f3bb`](https://github.com/siderolabs/go-api-signature/commit/5b4f3bb291b7bbec70b690f2969954255ccb8a22) chore: run rekres * [`9dba116`](https://github.com/siderolabs/go-api-signature/commit/9dba116c0838ecc0342a9af1e81e68e04b133623) chore: remove time.Sleep hack * [`e84e686`](https://github.com/siderolabs/go-api-signature/commit/e84e68658095aecead59982255b242ba8bef0fc5) chore: bump dependencies * [`8baaf8a`](https://github.com/siderolabs/go-api-signature/commit/8baaf8a99a28adda6dbdc0d7c38e78b290c84d96) chore: bump deps * [`5f27e1e`](https://github.com/siderolabs/go-api-signature/commit/5f27e1ebc06e26dea6a8102630a5b3529283eb9e) chore: add renovate bot and bump deps * [`69886dc`](https://github.com/siderolabs/go-api-signature/commit/69886dcc1343561add3b4b86ef160e0a1876d97f) feat: allow custom validations on PGP key * [`63d4da3`](https://github.com/siderolabs/go-api-signature/commit/63d4da31ae67052129c5ec795b61fb9c05a52441) fix: limit clock skew for short-lived keys * [`cdb9722`](https://github.com/siderolabs/go-api-signature/commit/cdb9722becf1aaeeaa1e9529dac19f3d5281f0a1) feat: add support for +-5 min clock skew * [`7b80a50`](https://github.com/siderolabs/go-api-signature/commit/7b80a50eea28d9273a49445cc3d39492db2e085b) refactor: use options pattern in RegisterPGPPublicKey * [`c647861`](https://github.com/siderolabs/go-api-signature/commit/c6478610d97a99967e903bdba1a4b7fab20e64b9) feat: add scopes to RegisterPublicKeyRequest * [`5d3647e`](https://github.com/siderolabs/go-api-signature/commit/5d3647e1d988e3162d0e851757fec951f6bb00c9) feat: provide more client PGP functions * [`2b682ec`](https://github.com/siderolabs/go-api-signature/commit/2b682ec61e83260b11cdf65d2f3723a89e4afa88) feat: initial version * [`a4c2943`](https://github.com/siderolabs/go-api-signature/commit/a4c294367c35d1234470d09c6151eed616a0c031) chore: initial commit

### Changes from siderolabs/go-kmsg
2 commits

* [`e358d13`](https://github.com/siderolabs/go-kmsg/commit/e358d13e5bdab79568d6ffea4b071c1530aa8e3d) fix: decode escape sequences while reading from kmsg * [`4297bd5`](https://github.com/siderolabs/go-kmsg/commit/4297bd599c918a5a874fb3b9f3119b394bd70899) feat: add BSD support

### Changes from siderolabs/pkgs
21 commits

* [`96cc841`](https://github.com/siderolabs/pkgs/commit/96cc841fda61f4b91b3a6d57a3baa6650c223957) chore: bump deps * [`064fd58`](https://github.com/siderolabs/pkgs/commit/064fd581bb529db7cef32b7b4adb9b1a2f8fbd98) feat: update Linux to 6.6.14, enable XDP * [`efbbd23`](https://github.com/siderolabs/pkgs/commit/efbbd2382e1e5cfeef8717d2212b43a9b760feaf) feat: update Linux to 6.6.13 * [`dfb5026`](https://github.com/siderolabs/pkgs/commit/dfb5026b73f641ee3000e2460e250ec33a7e9a56) chore: switch to git ref for raspberrypi firmware * [`4af2d0f`](https://github.com/siderolabs/pkgs/commit/4af2d0fdfaf31c31d7f5d24fdc01d5d9d0b27e37) feat: update Linux to 6.1.74 * [`2358efe`](https://github.com/siderolabs/pkgs/commit/2358efe6701139f074213a32d39a314729821cf1) fix: enable FUSION_SPI driver * [`f376a53`](https://github.com/siderolabs/pkgs/commit/f376a539352926b402e7685700defedafa4c58b0) chore: bump dependencies * [`583e519`](https://github.com/siderolabs/pkgs/commit/583e519f2dad762bc63bc5b2bff963c61a7e1c16) feat: add v4l usb video class (webcam) drivers * [`2d3ca68`](https://github.com/siderolabs/pkgs/commit/2d3ca68152d39560b8f34087cdd2e6d386f020d2) feat: enable NBD * [`f647edd`](https://github.com/siderolabs/pkgs/commit/f647edd497852feb4d537d191ef895d33420b5c4) feat: update Linux to 6.1.69 * [`6af1691`](https://github.com/siderolabs/pkgs/commit/6af169101a8a5459706f45a9fe861f90ed766284) feat: enable VFIO also on amd64 * [`d633cd6`](https://github.com/siderolabs/pkgs/commit/d633cd657081623a3681a905c4b875ed84fb4e6f) feat: enable modules for mlx infiniband * [`4c59641`](https://github.com/siderolabs/pkgs/commit/4c596414acd8722c97f45e0839c6d772f3e045a8) fix: zfs module build * [`e325097`](https://github.com/siderolabs/pkgs/commit/e32509763b2953b920c73791ca157c2c1180f0be) feat: enable nct6683 sensors as module * [`d6185ec`](https://github.com/siderolabs/pkgs/commit/d6185ec3ad68e10a9545ca0528f7e517c803f1a8) feat: enable IRQ remapping on amd64 * [`814dc60`](https://github.com/siderolabs/pkgs/commit/814dc601fb79e2b973d41e82cd16872dee513161) feat: update containerd to 1.7.11 * [`dd71790`](https://github.com/siderolabs/pkgs/commit/dd717902792d68576f7ab799a1342138fae475b2) chore: rekres to fix 'failed' build on main * [`a36dec4`](https://github.com/siderolabs/pkgs/commit/a36dec48a1c0de4ca178222ee88251701a881bdb) feat: split more device drivers into modules * [`97270a2`](https://github.com/siderolabs/pkgs/commit/97270a2c26a9c61c2fff5fb104ff0a2bc9fbdd5d) feat: update Linux to 6.1.67 * [`8a73907`](https://github.com/siderolabs/pkgs/commit/8a73907886c003c33cbc03a2a81f0a473fa93c00) feat: update Go to 1.21.5 * [`8f0ffb9`](https://github.com/siderolabs/pkgs/commit/8f0ffb92f88b1a795cfc627abd3ca74cb910cb78) feat: update zfs to v2.2.2

### Changes from siderolabs/tools
6 commits

* [`f4b41d1`](https://github.com/siderolabs/tools/commit/f4b41d189844957a71814ee3e98983553f7ba0ad) fix: rust toolchain * [`8cc79e6`](https://github.com/siderolabs/tools/commit/8cc79e68499624d4dde77d5c7d12aab957aaa22a) feat: update dependencies * [`c7076eb`](https://github.com/siderolabs/tools/commit/c7076eb9f2246f1d6cca431968d2fcce6bd1f951) chore: bump dependencies * [`a80a2aa`](https://github.com/siderolabs/tools/commit/a80a2aa0307d90f07c8a239459191a3f68cdd5d3) feat: update Go to 1.21.6 * [`b677a2b`](https://github.com/siderolabs/tools/commit/b677a2b99fd658710c34f7472df350787346ea35) feat: add rust build stage * [`1659d82`](https://github.com/siderolabs/tools/commit/1659d82e78511522e2820efccb892235d6d7b279) feat: update Go to 1.21.5

### Dependency Changes * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.9.0 -> v1.9.1 * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.4.0 -> v1.5.1 * **github.com/aws/aws-sdk-go-v2/config** v1.25.6 -> v1.26.6 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.14.5 -> v1.14.11 * **github.com/aws/smithy-go** v1.17.0 -> v1.19.0 * **github.com/beevik/ntp** v1.3.0 -> v1.3.1 * **github.com/containerd/cgroups/v3** v3.0.2 -> v3.0.3 * **github.com/containerd/containerd** v1.7.9 -> v1.7.13 * **github.com/containernetworking/plugins** v1.3.0 -> v1.4.0 * **github.com/coredns/coredns** v1.11.1 **_new_** * **github.com/cosi-project/runtime** v0.3.19 -> v0.3.20 * **github.com/docker/docker** v24.0.7 -> v25.0.2 * **github.com/docker/go-connections** v0.4.0 -> v0.5.0 * **github.com/emicklei/dot** v1.6.0 -> v1.6.1 * **github.com/foxboron/go-uefi** 18b9ba9cd4c3 -> 48be911532c2 * **github.com/gdamore/tcell/v2** v2.6.0 -> v2.7.0 * **github.com/google/go-containerregistry** v0.16.1 -> v0.19.0 * **github.com/google/go-tpm** v0.9.0 -> ee6cbcd136f8 * **github.com/google/uuid** v1.4.0 -> v1.6.0 * **github.com/hetznercloud/hcloud-go/v2** v2.4.0 -> v2.6.0 * **github.com/insomniacslk/dhcp** b0416c0f187a -> 15c9b8791914 * **github.com/jsimonetti/rtnetlink** v1.4.0 -> v1.4.1 * **github.com/miekg/dns** v1.1.58 **_new_** * **github.com/opencontainers/image-spec** v1.1.0-rc4 -> v1.1.0-rc6 * **github.com/opencontainers/runtime-spec** v1.1.0-rc.1 -> v1.1.0 * **github.com/packethost/packngo** v0.30.0 -> v0.31.0 * **github.com/pin/tftp** 2f79be2dba4e **_new_** * **github.com/pmorjan/kmod** v1.1.0 -> v1.1.1 * **github.com/rivo/tview** 33a1d271f2b6 -> 8526c9fe1b54 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.21 -> v1.0.0-beta.22 * **github.com/siderolabs/discovery-client** v0.1.5 -> v0.1.6 * **github.com/siderolabs/extras** v1.6.0-1-g113887a -> v1.7.0-alpha.0 * **github.com/siderolabs/go-api-signature** v0.3.1 **_new_** * **github.com/siderolabs/go-kmsg** v0.1.3 -> v0.1.4 * **github.com/siderolabs/pkgs** v1.6.0-5-g3ae2450 -> v1.7.0-alpha.0-19-g96cc841 * **github.com/siderolabs/talos/pkg/machinery** v1.6.0 -> v1.6.0-alpha.2 * **github.com/siderolabs/tools** v1.6.0-1-g336d248 -> v1.7.0-alpha.0-5-gf4b41d1 * **github.com/u-root/u-root** v0.11.0 -> v0.12.0 * **go.etcd.io/etcd/api/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/client/pkg/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/client/v3** v3.5.11 -> v3.5.12 * **go.etcd.io/etcd/etcdutl/v3** v3.5.11 -> v3.5.12 * **go4.org/netipx** 6213f710f925 -> fdeea329fbba * **golang.org/x/net** v0.19.0 -> v0.20.0 * **golang.org/x/oauth2** v0.15.0 -> v0.16.0 * **golang.org/x/sync** v0.5.0 -> v0.6.0 * **golang.org/x/sys** v0.15.0 -> v0.16.0 * **golang.org/x/term** v0.15.0 -> v0.16.0 * **google.golang.org/grpc** v1.59.0 -> v1.61.0 * **google.golang.org/protobuf** v1.31.0 -> v1.32.0 * **k8s.io/api** v0.29.0 -> v0.29.1 * **k8s.io/apimachinery** v0.29.0 -> v0.29.1 * **k8s.io/apiserver** v0.29.0 -> v0.29.1 * **k8s.io/client-go** v0.29.0 -> v0.29.1 * **k8s.io/component-base** v0.29.0 -> v0.29.1 * **k8s.io/cri-api** v0.29.0 -> v0.29.1 * **k8s.io/klog/v2** v2.110.1 -> v2.120.1 * **k8s.io/kube-scheduler** v0.29.0 -> v0.29.1 * **k8s.io/kubectl** v0.29.0 -> v0.29.1 * **k8s.io/kubelet** v0.29.0 -> v0.29.1 Previous release can be found at [v1.6.0](https://github.com/siderolabs/talos/releases/tag/v1.6.0) ## [Talos 1.6.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.6.0-alpha.2) (2023-11-21) Welcome to the v1.6.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Kubelet Credential Provider Configuration Talos now supports specifying the kubelet credential provider configuration in the Talos configuration file. It can be set under `machine.kubelet.credentialProviderConfig` and kubelet will be automatically configured to with the correct flags. The credential binaries are expected to be present under `/usr/local/lib/kubelet/credentialproviders`. Talos System Extensions can be used to install the credential binaries. ### Network Device Selectors Previously, [network device selectors](https://www.talos.dev/v1.6/talos-guides/network/device-selector/) only matched the first link, now the configuration is applied to all matching links. ### Linux Firmware Starting with Talos 1.6, there is no Linux firmware included in the initramfs. Customers who need Linux firmware can pull them as extension during install time using the image factory service. If the initial boot requires firmware, a custom iso can be built with the firmware included using the image factory service. This also ensures that the linux-firmware is not tied to a specific Talos version. ### Kube-Scheduler Configuration Talos now supports specifying the kube-scheduler configuration in the Talos configuration file. It can be set under `cluster.scheduler.config` and kube-scheduler will be automatically configured to with the correct flags. ### KubePrism [KubePrism](https://www.talos.dev/v1.6/kubernetes-guides/configuration/kubeprism/) is enabled by default on port 7445. ### Sysctl Talos now handles sysctl/sysfs key names in line with sysctl.conf(5): * if the first separator is '/', no conversion is done * if the first separator is '.', dots and slashes are remapped Example (both sysctls are equivalent): ```yaml machine: sysctls: net/ipv6/conf/eth0.100/disable_ipv6: "1" net.ipv6.conf.eth0/100.disable_ipv6: "1" ``` ### talosctl CLI The command `images` deprecated in Talos 1.5 was removed, please use `talosctl images default` instead. ### Component Updates Linux: 6.1.63 containerd: 1.7.9 CoreDNS: 1.11.1 Kubernetes: 1.29.0-alpha.3 Flannel: 0.22.3 etcd: 3.5.10 runc: 1.1.10 Talos is built with Go 1.21.4. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Andrey Smirnov * Oscar Utbult * Serge Logvinov * Utku Ozdemir * Artem Chernyshev * Nico Berlee * Radosław Piliszek * Steve Francis * Thomas Way * ndbrew * Andrei Kvapil * Christian Rolland * Drew Hess * Enno Boland * Florian Berchtold * Henry Sachs * Jacob McSwain * Jacob McSwain * Jared Davenport * Mans Matulewicz * Nebula * Sascha Desch * Spencer Smith * Thomas Lemarchand * Tim Jones * Zachary Milonas * budimanjojo * guoguangwu * mikucat0309 ### Changes
177 commits

* [`514e514ba`](https://github.com/siderolabs/talos/commit/514e514ba650419a4caad4ee87c52a367ce1e323) feat: update Linux 6.1.63, containerd 1.7.9 * [`aca8b5e17`](https://github.com/siderolabs/talos/commit/aca8b5e179962c8e1dc27ca8de527e981f763004) fix: ignore kernel command line in container mode * [`020a0eb63`](https://github.com/siderolabs/talos/commit/020a0eb63ea39d25faa8eba8568584243d814457) docs: fix table formatting for bootstraprequest * [`0eb245e04`](https://github.com/siderolabs/talos/commit/0eb245e04374cd21a369d298b73e8bc6db11d153) docs: fix talosctl pcap example indentation * [`de6caf534`](https://github.com/siderolabs/talos/commit/de6caf5348f815dddbd4a595d40d4c4ad71282bc) docs: fix table formatting for machineservice api * [`27d208c26`](https://github.com/siderolabs/talos/commit/27d208c26bd1fe5a37b127cd83cab76b5671758a) feat: implement OAuth2 device flow for machine config * [`5c8fa2a80`](https://github.com/siderolabs/talos/commit/5c8fa2a80382b6ea83d81c434b2e28a9901fdcad) chore: start containerd early in boot * [`95a252cfc`](https://github.com/siderolabs/talos/commit/95a252cfc91eeeeb48ac3b3e3cd6ad7ba14ab1eb) docs: fix link in what is new page * [`0d3c3ed71`](https://github.com/siderolabs/talos/commit/0d3c3ed716670c80d33351d912620e5b91f6c7e3) feat: support kube scheduler config * [`06941b7e5`](https://github.com/siderolabs/talos/commit/06941b7e5ca4f937c1996828e5a543967902656d) fix: allow rootfs propagation configuration for extension services * [`57dc796f3`](https://github.com/siderolabs/talos/commit/57dc796f381e87f398cfed3ac7cd87ff51454b75) docs: update lastRelease to v1.5.5 in _index.md * [`21d944a64`](https://github.com/siderolabs/talos/commit/21d944a643d8eec104d703cc8995e9ac80d2417b) docs: add timezone information * [`4f1ad16c7`](https://github.com/siderolabs/talos/commit/4f1ad16c764e643f7bf71ed8ca46e840875011ec) feat: support kubelet credentialprovider config * [`71a3bf0e3`](https://github.com/siderolabs/talos/commit/71a3bf0e3e42117e7283b41116419d7d2f28d82c) fix: allow extra kernel args for secureboot installer * [`f38eaaab8`](https://github.com/siderolabs/talos/commit/f38eaaab87f77f33b0317d4405c84575023ee0da) feat: rework secureboot and PCR signing key * [`6eade3d5e`](https://github.com/siderolabs/talos/commit/6eade3d5ef5c5356d0bfc0e3d52263a39d2e9f1a) chore: add ability to rewrite uuids and set unique tokens for Talos * [`e9c7ac17a`](https://github.com/siderolabs/talos/commit/e9c7ac17a9b707950b249e08e11ed7ddac64e8ae) fix: set max msg recv size when proxying * [`e22ab440d`](https://github.com/siderolabs/talos/commit/e22ab440d7794a9c46edf1357124571057b6b19d) feat: update Linux 6.1.61, containerd 1.7.8, runc 1.1.10 * [`8245361f9`](https://github.com/siderolabs/talos/commit/8245361f9cfb66d68bc54330a47814eb730eb839) feat: show first 32 bytes of response body on download error * [`75d3987c0`](https://github.com/siderolabs/talos/commit/75d3987c05390d3c0a7cf4de855895f1d10c8a84) chore: drop sha1 from genereated pcr json * [`6f32d2990`](https://github.com/siderolabs/talos/commit/6f32d2990f438a9e8134d7e94558a54b3912854e) feat: add `.der` output `talosctl gen secureboot pcr` * [`87c40da6c`](https://github.com/siderolabs/talos/commit/87c40da6cc5d9ae62d20984ba5d3762da734a49e) fix: proper logging in machined on startup * [`a54da5f64`](https://github.com/siderolabs/talos/commit/a54da5f641886d723465e0a8cfa95b15bc2e96aa) fix: image build for nanopi_4s * [`6f3cd0593`](https://github.com/siderolabs/talos/commit/6f3cd05935a2faaf14d16c2e643f54e6f9134c0f) refactor: update packet capture to use 'afpacket' interface * [`813442dd7`](https://github.com/siderolabs/talos/commit/813442dd7a08b2781829ef190b110aa38c725932) fix: don't validate machine.install if installed * [`dff60069c`](https://github.com/siderolabs/talos/commit/dff60069c0230ecf531c5593724211fd75f26d7c) feat: update Kubernetes to 1.29.0-alpha.3 * [`c97db5dfe`](https://github.com/siderolabs/talos/commit/c97db5dfe174032f012bdd525a3479ebea200c93) chore: bump Go dependencies * [`807a9950a`](https://github.com/siderolabs/talos/commit/807a9950ac5cb542e41d65af0f9f80f1c73550a3) fix: use custom Talos/kernel version when generating UKI * [`eb94468a6`](https://github.com/siderolabs/talos/commit/eb94468a659b4518b317398f92346b62e6adefe4) docs: add documentation for Image Factory * [`2e78513e1`](https://github.com/siderolabs/talos/commit/2e78513e16b2eb0d83a4a7e107c470058d30837d) refactor: drop the dependency link platform -> network ctrl * [`6dc776b8a`](https://github.com/siderolabs/talos/commit/6dc776b8aaa2d9382737d41a90023e8e4ea1a601) fix: when writing to META in the installer/imager, use fixed name * [`3703041e9`](https://github.com/siderolabs/talos/commit/3703041e989c83c1ad7496851c6687f729cb207f) chore: remove uneeded code * [`cbe6e7622`](https://github.com/siderolabs/talos/commit/cbe6e7622d0180ca53ab0ce92d38e4704d466d1a) fix: generate images for SBCs using imager * [`5dff164f1`](https://github.com/siderolabs/talos/commit/5dff164f1c8fc08b66f0ea509db36561eaef464c) fix: fix error output of cli action tracker * [`ef5056122`](https://github.com/siderolabs/talos/commit/ef5056122b38a168dd8ee429a6bc4cad0860177d) feat: update etcd to 3.5.10 * [`45ae80873`](https://github.com/siderolabs/talos/commit/45ae80873f1a7a3cb5643f7d94108a96f36cad32) chore: bump go-api-signature dependency to v0.3.1 * [`ffa5e05cb`](https://github.com/siderolabs/talos/commit/ffa5e05cb9c8028897ce5e08183be52965004726) fix: make Talos work on Rockpi 4c boards again * [`8eba4c599`](https://github.com/siderolabs/talos/commit/8eba4c5999ca4a43220704ff2297706fd9e9d27b) feat: generate secrets bundle from the machine config * [`c7de745f6`](https://github.com/siderolabs/talos/commit/c7de745f61490ee8192bbab34fbb8a4bad21de9f) chore: drop deprecated code * [`cc0c3ab69`](https://github.com/siderolabs/talos/commit/cc0c3ab69c7807236955eb53ccac4cc70fcca32a) docs: update rpi_generic.md * [`a009f5c60`](https://github.com/siderolabs/talos/commit/a009f5c60c9506dd5064106bbef38fe36813db64) fix: accept sysctl paths with dots * [`4919f6ee2`](https://github.com/siderolabs/talos/commit/4919f6ee22b5f6cf53f801e13072f6d64027c215) feat: add GOMEMLIMIT to shipped manifests with memory limits * [`73ee576ea`](https://github.com/siderolabs/talos/commit/73ee576ea711a9f36a8d35ceba4716276a2e5f70) chore: update sonobuouy library, drop the fork * [`c23bc2f4a`](https://github.com/siderolabs/talos/commit/c23bc2f4a77c3e9b2e88f99d05266fcd8fb4a51b) chore: support OCI layout as a source for profile input * [`154bbd70f`](https://github.com/siderolabs/talos/commit/154bbd70f7bdfd464ad6136c7e7e057d2402c0f6) docs: fix talos version in guide for docker * [`11d1f6163`](https://github.com/siderolabs/talos/commit/11d1f616350885bfe5ab3e9d3310ee2b0eee4201) release(v1.6.0-alpha.1): prepare release * [`9dfae8467`](https://github.com/siderolabs/talos/commit/9dfae8467d5a0bf7d3fd753b980bc7801bf3e5f8) chore: update dependencies * [`38ce3c827`](https://github.com/siderolabs/talos/commit/38ce3c827a06c44e0399cd0a3d8a396687001b20) feat: nocloud prefer mac address * [`401e89411`](https://github.com/siderolabs/talos/commit/401e8941124056f9cd9649a555aafebb063bb94d) feat: customize image size * [`865f08f86`](https://github.com/siderolabs/talos/commit/865f08f867fa5784c5a25bfeb929dbe25a6eb763) docs: kubeadm migration guide improvements * [`c3e418200`](https://github.com/siderolabs/talos/commit/c3e418200032be376aa30f6db133f2dcbf8b67c2) refactor: use COSI runtime with new controller runtime DB * [`c1ee24465`](https://github.com/siderolabs/talos/commit/c1ee24465aaac079f84c58ac86f74e89dfeb01ed) feat: update Kubernetes to v1.29.0-alpha.2 * [`0ff7350ab`](https://github.com/siderolabs/talos/commit/0ff7350abe94c046b8c7759ca6a1c64d9b80e497) fix: oracle integration fixes * [`675bada45`](https://github.com/siderolabs/talos/commit/675bada45473a91f5a99134193acf48da2789545) test: add config generation stability tests * [`f9639fb53`](https://github.com/siderolabs/talos/commit/f9639fb531797f4db16696e81371d9043d7041a9) test: fix 'talosctl gen' tests * [`6142d87a0`](https://github.com/siderolabs/talos/commit/6142d87a0f3e0a5e4babb97667a22e2497c67b4c) feat: hostname configuration improvements on the NoCloud platform * [`7bb205ebe`](https://github.com/siderolabs/talos/commit/7bb205ebe2efdbd691dd81b49fc6acbd3a289fa5) fix: don't use runtime-specs Mount struct in machine config * [`d1b27926c`](https://github.com/siderolabs/talos/commit/d1b27926c24109a2044cd07b3bb2d1e2824857c2) feat: update Go to 1.21.3 * [`b87092ab6`](https://github.com/siderolabs/talos/commit/b87092ab69e8a4928727ad71f3ce01502f76c966) fix: handle secure boot state policy pcr digest error * [`498aeb8c3`](https://github.com/siderolabs/talos/commit/498aeb8c32a590b20140541a1a334fdf2da84105) docs: fix incorrect image suffix * [`c14a5d4f7`](https://github.com/siderolabs/talos/commit/c14a5d4f79a3af0d075288cc9fb74f15fa34faf1) feat: support service account auth in cli * [`336aee0fd`](https://github.com/siderolabs/talos/commit/336aee0fdb1302443f627f848bed8081bdb0d9b0) fix: use tpm2 hash algorithm constants and allow non-SHA-256 PCRs * [`69d8054c9`](https://github.com/siderolabs/talos/commit/69d8054c9ec194b801f8d3185519c4b26a6a6b07) chore: drop UpdateEndpointSuite * [`ef7be16c8`](https://github.com/siderolabs/talos/commit/ef7be16c801176fc983299229841a98f935e18ed) fix: clear the encryption config in META when STATE is reset * [`5fc60d2ca`](https://github.com/siderolabs/talos/commit/5fc60d2caa75a6e886e3a70c22b63a708f68ad43) feat: add Solarflare SFC9000 support * [`9b5cfdd0b`](https://github.com/siderolabs/talos/commit/9b5cfdd0bc252a9594f6d7112ebf7401e41d1546) chore: add tests for iscsi * [`b897764f8`](https://github.com/siderolabs/talos/commit/b897764f8e90fa237cedecba50a63f5f2f852543) docs: update proxmox.md * [`159f45bde`](https://github.com/siderolabs/talos/commit/159f45bde65097efe311674b253284cf7d167b26) docs: fix typos in CLI calls to endpoints * [`0bd1bdd74`](https://github.com/siderolabs/talos/commit/0bd1bdd744f68dc42ac64678972fede992a7189e) chore: allow insecure access to installer base image (imager) * [`10ed13067`](https://github.com/siderolabs/talos/commit/10ed13067958f3afa0819a3d8557933b218a391b) fix: the node IP for kubelet shouldn't change if nothing matches * [`e7575ecaa`](https://github.com/siderolabs/talos/commit/e7575ecaaea9625be471c9db1965e256959f0730) feat: support n-5 latest Kubernetes versions * [`e71508ec1`](https://github.com/siderolabs/talos/commit/e71508ec104b42d1882b26d6bab22fc43ca0d8bb) chore: update dependencies * [`6d7fa4668`](https://github.com/siderolabs/talos/commit/6d7fa466807ffcd3b6a5c84ae34a90c728fcb8be) docs: add metal network configuration guide * [`2b548ad0d`](https://github.com/siderolabs/talos/commit/2b548ad0d9fa7b1f1e057c160464494b1828eb77) feat: update containerd to 1.7.x * [`62dcfe81e`](https://github.com/siderolabs/talos/commit/62dcfe81eb17ad2927dff43a855f0169fd84271e) fix: update kubernetes library to support 1.29 upgrades * [`52caf0763`](https://github.com/siderolabs/talos/commit/52caf0763393bc171b95464fefd3af1a3efd5f1c) feat: update Kubernetes to 1.29.0-alpha.1 * [`390137447`](https://github.com/siderolabs/talos/commit/390137447fbf2a8e87cb7bb313a202dbd5a31045) feat: enable KubePrism by default * [`1beb5e86e`](https://github.com/siderolabs/talos/commit/1beb5e86e621595af0d93798c9e158bb48e2b363) docs: add KubePrism video * [`a52d3cda3`](https://github.com/siderolabs/talos/commit/a52d3cda3b2eecc8aabf64b99a3ded0dad7e84c3) chore: update gen and COSI runtime * [`29b201d61`](https://github.com/siderolabs/talos/commit/29b201d61902017be355853a8f11c903fe9fefae) feat: enable common h/w sensors * [`9c2ba7c6f`](https://github.com/siderolabs/talos/commit/9c2ba7c6fa1162cb946e91a7e7d4dfecd62027a5) chore: add tests for chelsio drivers * [`5ca4d58dc`](https://github.com/siderolabs/talos/commit/5ca4d58dc9a2477db44d34c9f30ed21b0c3d2131) fix: generate of modules.dep when on the machine * [`5efcccb6b`](https://github.com/siderolabs/talos/commit/5efcccb6b14f59a9c065273493e0b82af1a85226) chore: bump kernel to 6.1.54 * [`29c767a02`](https://github.com/siderolabs/talos/commit/29c767a028e346c635e99e491cdab150c756f77c) docs: add control plane nodes as users of apid also for control plane nodes * [`4874cfb95`](https://github.com/siderolabs/talos/commit/4874cfb95a8148dc7feec00de8c299d4ac022c53) chore: fix typo * [`96f2a62ea`](https://github.com/siderolabs/talos/commit/96f2a62eafb5c3cee254d6e15f6f8c3e91359b9a) test: update upgrade tests versions * [`f3a370acb`](https://github.com/siderolabs/talos/commit/f3a370acb21c83fd1393da30bad2a37ca6a09b2c) feat: update Flannel to 0.22.3 * [`efdee6965`](https://github.com/siderolabs/talos/commit/efdee69658cfea44681954dac2552cfeee5bb30e) feat: update Kubernetes to 1.28.2 * [`e3b494058`](https://github.com/siderolabs/talos/commit/e3b49405884186dc1db0d9592f95965a0904691d) fix: build CPU ucode correctly for early loader * [`c5bd0ac5c`](https://github.com/siderolabs/talos/commit/c5bd0ac5cf033a9e3084a5fe98f42ee784926636) refactor: reimplement the depmod extension rebuilder * [`0b883f52a`](https://github.com/siderolabs/talos/commit/0b883f52a5a81a36a0e777f6f87e2d1d176e2294) docs: add notes about stable addressing * [`3ef670a9e`](https://github.com/siderolabs/talos/commit/3ef670a9e8e7efff5af9872e1e13d8521ce2dca6) chore: pull in dm modules * [`8f4a36b0d`](https://github.com/siderolabs/talos/commit/8f4a36b0d4c35f5841a270b7b5cd7da7c798165f) docs: update aws to add command to allow KubeSpan wireguard port * [`a7edd0523`](https://github.com/siderolabs/talos/commit/a7edd0523f9e5a7fccc6c382b453000beab4a8ff) fix: set default route priority for hcloud platform * [`87c1b3ddd`](https://github.com/siderolabs/talos/commit/87c1b3ddd83f038c62d34e94ad7e34a98236130b) fix: calculate UKI ISO size dynamically * [`9698e4547`](https://github.com/siderolabs/talos/commit/9698e45479cb293bbefe1651b94344bd7b0a4e52) fix: handle correctly change of listen address for maintenance service * [`a096f05a5`](https://github.com/siderolabs/talos/commit/a096f05a56003c317ffade2c87aa8d327592e3b8) chore: update gRPC library and enable shared write buffers * [`9e78fecca`](https://github.com/siderolabs/talos/commit/9e78feccaecda53778acba43fb9ad177051a009c) chore: improve image signing process * [`f00567e20`](https://github.com/siderolabs/talos/commit/f00567e20f239e781975636b12e31501ee39bbfa) chore: add PKG_KERNEL arg to customize used kernel * [`2960f93ba`](https://github.com/siderolabs/talos/commit/2960f93baa55f6ea2cb3690cbc652df9aee17af8) feat: add readonly information to the disks API response * [`735bf9ed0`](https://github.com/siderolabs/talos/commit/735bf9ed08a5d8dd302ef3e1f61317ff9169549c) feat: bring in Google vNIC driver * [`3f5232075`](https://github.com/siderolabs/talos/commit/3f523207522aa69452516408f914cc792abb78b9) feat: upgrade-k8s without comments * [`e44875106`](https://github.com/siderolabs/talos/commit/e44875106e28e50b15c38fa8b889f51083325800) docs: update deploying-cilium.md * [`7046cae43`](https://github.com/siderolabs/talos/commit/7046cae43dd4e8a4ea7d80934b02cc7c8b84e53a) chore: update gopacket to reduce init memory allocs * [`da73b563d`](https://github.com/siderolabs/talos/commit/da73b563dd0a7d77f4490d10cc506b5570c2bf11) chore: update Go to 1.21.1 * [`5e11f08a6`](https://github.com/siderolabs/talos/commit/5e11f08a639bd791fa7fafe3df35349959b4eb24) fix: trim file path in the container image * [`3d2dad4e6`](https://github.com/siderolabs/talos/commit/3d2dad4e69ba458fb406a7d7441d9e3f2fe8fde2) chore: show securtiystate on dashboard * [`b48510874`](https://github.com/siderolabs/talos/commit/b4851087404e6fcad52da588fd4827046011b271) chore: e2e-aws cleanup * [`1eebbce35`](https://github.com/siderolabs/talos/commit/1eebbce357311aaea739abe55c9e0de947791f39) chore: add output flag for talosctl config info * [`3fbed806c`](https://github.com/siderolabs/talos/commit/3fbed806c4e5a4167f0a357eb20486bb406103a8) chore: add tests for util-linux extensions * [`7c514a1a6`](https://github.com/siderolabs/talos/commit/7c514a1a6c258a5f5f3ed6a4dbb15ed531a7e0b2) docs: update header links * [`6058c3602`](https://github.com/siderolabs/talos/commit/6058c360238ba70c780df7a24a0f6a13fa46a833) fix: shorten VLAN link names to fit into the limit of 15 characters * [`9c2f765c8`](https://github.com/siderolabs/talos/commit/9c2f765c86ca73d6d14957b7ae1bc7bd32fed0fd) fix: allow network device selector to match multiple links * [`a04b98637`](https://github.com/siderolabs/talos/commit/a04b9863762acefe2030a8a64f9c8d8608432fd2) fix: update kubernetes library for 1.28 upgrade pre-checks * [`f7473e477`](https://github.com/siderolabs/talos/commit/f7473e4778fe2d36ce600378cfc8d7630096f2d7) feat: update default Kubernetes to 1.28.1 * [`d693604a1`](https://github.com/siderolabs/talos/commit/d693604a1d76aa72698eed2c1cab19e3cd34dc01) chore: fix default image list in the release notes * [`d91b5b3a3`](https://github.com/siderolabs/talos/commit/d91b5b3a31188d64cbc5ad8385000fae0fcf55e5) feat: set environment variables early in the boot * [`c918c0855`](https://github.com/siderolabs/talos/commit/c918c0855d08e06b832699e8c8b66017e457abc9) fix: set correct (1 year) talosconfig expiration * [`79bbdf454`](https://github.com/siderolabs/talos/commit/79bbdf454eb9bb891e845efff73db1bbdfd6d43e) fix: set proper timeouts for KubePrism loadbalancer * [`b8fb55d5c`](https://github.com/siderolabs/talos/commit/b8fb55d5c2e0433df46ac7bc3eeaea08e12d572d) fix: use a mount prefix when installing a bootloader * [`44f59a804`](https://github.com/siderolabs/talos/commit/44f59a8049beed1db453ef1d5a74f0e771ae39ff) feat: improve imager APIs * [`2d3ac925e`](https://github.com/siderolabs/talos/commit/2d3ac925ea519b8b5160190e1fdb8aba01a9ef74) refactor: update NTP spike detector * [`af0cc70e3`](https://github.com/siderolabs/talos/commit/af0cc70e3775cf7017387c541273a2580c55c78c) test: update e2e-aws to use worker groups * [`d03dc7a8a`](https://github.com/siderolabs/talos/commit/d03dc7a8afdd3fbf084a6d91544de5423f56d68c) chore: validate new system extensions * [`bbeb489aa`](https://github.com/siderolabs/talos/commit/bbeb489aa8282809bf65e89b3a571193814d3b1e) chore: drop firmware from initramfs * [`3c9f7a7de`](https://github.com/siderolabs/talos/commit/3c9f7a7de641bed699533ace6451387ddbfec44e) chore: re-enable nolintlint and typecheck linters * [`c51e2c9b4`](https://github.com/siderolabs/talos/commit/c51e2c9b482a113b154d3e6d7b2b37346a1b1043) feat: update CoreDNS to 1.11.1 * [`8670450d2`](https://github.com/siderolabs/talos/commit/8670450d28040f35e08aa4d771a1415cd5c1920d) release(v1.6.0-alpha.0): prepare release * [`6778ded29`](https://github.com/siderolabs/talos/commit/6778ded29de5369b1869194a0710f627121b5334) feat: add e2e-aws for nvidia extensions * [`74c07ed71`](https://github.com/siderolabs/talos/commit/74c07ed714d5751336e8745977caa3dca5060d7d) chore: update Go to 1.21 * [`a28d72e9c`](https://github.com/siderolabs/talos/commit/a28d72e9c262bd8fb84959ede952542a6e95d0be) fix: ova contents to be named `disk.*` * [`c0ea4d7ba`](https://github.com/siderolabs/talos/commit/c0ea4d7ba504dd8e1558f11e0cddd41dbf8bc720) fix: properly calculate overal of node address with subnet filters * [`d6b2719e2`](https://github.com/siderolabs/talos/commit/d6b2719e2e824cf5df9314523e3a4138b404e615) chore: drone: move extensions step to a function * [`9608ef56d`](https://github.com/siderolabs/talos/commit/9608ef56dc602636da1449ff05d237e0e20e5154) chore: allow bridge traffic with DHCP broadcast traffic * [`c99316457`](https://github.com/siderolabs/talos/commit/c993164576453fd03eb8fc517badd7de8004f4ad) docs: fix the installing system extensions doc * [`833895940`](https://github.com/siderolabs/talos/commit/833895940b173e247816751ca7287ccde7a36d03) chore: add tests for zfs extension * [`cb468c41c`](https://github.com/siderolabs/talos/commit/cb468c41cbbec6cd5f28c3cd3457aa4a30b81d4c) fix: copy proper modules to arm64 squashfs * [`ea0d6e8c6`](https://github.com/siderolabs/talos/commit/ea0d6e8c6a8ce8cd516bc05c99534241dff60b9f) fix: prevent dashboard crashes when process info is not available * [`e9077a6fb`](https://github.com/siderolabs/talos/commit/e9077a6fb9db5bcadea342200f057c1dc6ffb9af) feat: filter the hostname to produce nodename * [`dc8361c1d`](https://github.com/siderolabs/talos/commit/dc8361c1d524e3a52dfa18ee1b539fb81a02ef8d) fix: properly GC images supplied with both tag and digest * [`ccfa8de11`](https://github.com/siderolabs/talos/commit/ccfa8de1174b4e5d59c2f92b44d8dd65235b590a) fix: automatically change `rpi_4` board on upgrade * [`b56e8b7d9`](https://github.com/siderolabs/talos/commit/b56e8b7d9babe9a963b1fc9a2f41882d08fbafe3) fix: support 'List' type manifests * [`574d48e54`](https://github.com/siderolabs/talos/commit/574d48e54020b02f74c2aeadca1c10499bf967b0) fix: use image digest when starting a container * [`175747cea`](https://github.com/siderolabs/talos/commit/175747cea58d73f8532c114b7754668d24ab9c92) fix: ntp query error with bare IPv6 address * [`c8b507fb2`](https://github.com/siderolabs/talos/commit/c8b507fb26ca30cf0aa98c8cf669a2a03583fc1c) docs: fix kubeprism typo * [`0cdcb2e0e`](https://github.com/siderolabs/talos/commit/0cdcb2e0e8131510aab654211d3622fb17f8375e) docs: restructure docs for nvidia drivers for v1.4 * [`676db9768`](https://github.com/siderolabs/talos/commit/676db9768433027ebc6ff22a0414692ccec2ccf4) docs: fork docs for Talos 1.6 * [`92ad18c18`](https://github.com/siderolabs/talos/commit/92ad18c18fae5ac073cdd98d24c5aeb5edb4091a) fix: write correct capacity to the ovf * [`6b0373ebe`](https://github.com/siderolabs/talos/commit/6b0373ebef88600571ec54c189fd6ea3b0c777e8) chore: move bash tests to integration * [`52b3d8d37`](https://github.com/siderolabs/talos/commit/52b3d8d37cd1cf4eb3aa046781f105a1c39e69a0) docs: make Talos 1.5 documentation the default one * [`dc873df9b`](https://github.com/siderolabs/talos/commit/dc873df9b4cf169b4f7789690b80ac1e02b27d57) chore: fix the filenames of openstack images * [`b5c0e7b24`](https://github.com/siderolabs/talos/commit/b5c0e7b24cbd1546304ca33328b89e022e6e0675) docs: update nvidia docs * [`9606e871e`](https://github.com/siderolabs/talos/commit/9606e871e422b72aaef39ae03e334119602b8f31) docs: update Jiva Pod Security Policy * [`a86ed4362`](https://github.com/siderolabs/talos/commit/a86ed4362c009c389766ecd4bfcbc0ade999bb2e) chore: update Kubernetes Go modules to 0.28.0 * [`97b4e3e91`](https://github.com/siderolabs/talos/commit/97b4e3e91cb4a238a8f81c8ce2983c0033a355cb) feat: update Kubernetes to 1.28.0 * [`79ca1a3df`](https://github.com/siderolabs/talos/commit/79ca1a3dfb485fc5180bda38ab58a2d4c595a6aa) feat: e2e-aws using tf code * [`bf3a5e011`](https://github.com/siderolabs/talos/commit/bf3a5e01190e1cf80769343cf94af4c1bfb80318) chore: add version compatibility for Talos 1.6 * [`969e8097c`](https://github.com/siderolabs/talos/commit/969e8097ce062197c9011d206cdbc7de1dc87df5) feat: update Kubernetes to 1.28.0-rc.1 * [`ca41b611e`](https://github.com/siderolabs/talos/commit/ca41b611e97a0ef5020f01011267b82a155d136a) chore: drone jsonnet cleanup * [`bc198e98e`](https://github.com/siderolabs/talos/commit/bc198e98ef6dd03e07d75ab2eb8b944d10ad3739) docs: retain cilium autoMount pending upstream hostPath fix * [`86c94eff8`](https://github.com/siderolabs/talos/commit/86c94eff8d9e1abec11039f79dc6a9b35d46c7f3) refactor: docgen and config examples * [`ee6d639f6`](https://github.com/siderolabs/talos/commit/ee6d639f6c374cf8e1843dd3720047fea7dd3325) fix: match routes on the priority properly * [`bff0d8f32`](https://github.com/siderolabs/talos/commit/bff0d8f32c55d0cec9aed67592a6ccad8e5efee8) chore: fix dependencies in the release pipeline * [`e1b288679`](https://github.com/siderolabs/talos/commit/e1b288679e922fa0e255273adf4b7a1226518424) refactor: compile regex in validation method on the first use * [`daa4c185a`](https://github.com/siderolabs/talos/commit/daa4c185ae9a6318d779f45c730ac695e14ca6c7) docs: add what's new and documentation for Talos 1.5 * [`c4a1ca8d6`](https://github.com/siderolabs/talos/commit/c4a1ca8d61fcb1338da1ca223b9b4349a6af76e2) chore: remove <-errCh where possible in grpc methods * [`e0f383598`](https://github.com/siderolabs/talos/commit/e0f383598e2f285c04264e9a3787fcdcd56add85) chore: clean up the output of the `imager` * [`fb536af4d`](https://github.com/siderolabs/talos/commit/fb536af4d1804b8b802a4211739ac410fd34bb93) chore: optimize memory usage of `tcell` library on init * [`7c86a365e`](https://github.com/siderolabs/talos/commit/7c86a365e2691065e5e06a4789621bc9f43f3c4b) chore: publish systemd-boot and systemd-stub assets * [`7d688ccfe`](https://github.com/siderolabs/talos/commit/7d688ccfeb00ca46999b98512e49ac94f17d2693) fix: make encryption config provider default to `luks2` if not set * [`80238a05a`](https://github.com/siderolabs/talos/commit/80238a05a6f83b2d8bf3b04816d2b0a5c499eca8) chore: unify semver under `github.com/blang/semver/v4` * [`0f1920bdd`](https://github.com/siderolabs/talos/commit/0f1920bdda5b7f2e2291e75d14453cf81a1b6cd6) chore: provide a resource to peek into Linux clock adjustments * [`4eab3017b`](https://github.com/siderolabs/talos/commit/4eab3017b036d3229a6fa7dc9612050d1499e2b6) fix: calculate log2i properly * [`bcf284530`](https://github.com/siderolabs/talos/commit/bcf2845307ad2c4395967cbb8e756d6a0d8caf2c) fix: update providerid prefix for aws * [`ac2aff5cc`](https://github.com/siderolabs/talos/commit/ac2aff5cc5e5234fecf1f49b0f5d583c633aafa4) fix: fix azure portion of cloud uploader * [`793dcedc9`](https://github.com/siderolabs/talos/commit/793dcedc957389c9d91da62517a43968bd99b09d) fix: fast-wipe the system disk on talosctl reset * [`76fa45afb`](https://github.com/siderolabs/talos/commit/76fa45afbac5d212faa534047255c0256e78d08a) docs: update cilium instructions

### Changes since v1.6.0-alpha.1
45 commits

* [`514e514ba`](https://github.com/siderolabs/talos/commit/514e514ba650419a4caad4ee87c52a367ce1e323) feat: update Linux 6.1.63, containerd 1.7.9 * [`aca8b5e17`](https://github.com/siderolabs/talos/commit/aca8b5e179962c8e1dc27ca8de527e981f763004) fix: ignore kernel command line in container mode * [`020a0eb63`](https://github.com/siderolabs/talos/commit/020a0eb63ea39d25faa8eba8568584243d814457) docs: fix table formatting for bootstraprequest * [`0eb245e04`](https://github.com/siderolabs/talos/commit/0eb245e04374cd21a369d298b73e8bc6db11d153) docs: fix talosctl pcap example indentation * [`de6caf534`](https://github.com/siderolabs/talos/commit/de6caf5348f815dddbd4a595d40d4c4ad71282bc) docs: fix table formatting for machineservice api * [`27d208c26`](https://github.com/siderolabs/talos/commit/27d208c26bd1fe5a37b127cd83cab76b5671758a) feat: implement OAuth2 device flow for machine config * [`5c8fa2a80`](https://github.com/siderolabs/talos/commit/5c8fa2a80382b6ea83d81c434b2e28a9901fdcad) chore: start containerd early in boot * [`95a252cfc`](https://github.com/siderolabs/talos/commit/95a252cfc91eeeeb48ac3b3e3cd6ad7ba14ab1eb) docs: fix link in what is new page * [`0d3c3ed71`](https://github.com/siderolabs/talos/commit/0d3c3ed716670c80d33351d912620e5b91f6c7e3) feat: support kube scheduler config * [`06941b7e5`](https://github.com/siderolabs/talos/commit/06941b7e5ca4f937c1996828e5a543967902656d) fix: allow rootfs propagation configuration for extension services * [`57dc796f3`](https://github.com/siderolabs/talos/commit/57dc796f381e87f398cfed3ac7cd87ff51454b75) docs: update lastRelease to v1.5.5 in _index.md * [`21d944a64`](https://github.com/siderolabs/talos/commit/21d944a643d8eec104d703cc8995e9ac80d2417b) docs: add timezone information * [`4f1ad16c7`](https://github.com/siderolabs/talos/commit/4f1ad16c764e643f7bf71ed8ca46e840875011ec) feat: support kubelet credentialprovider config * [`71a3bf0e3`](https://github.com/siderolabs/talos/commit/71a3bf0e3e42117e7283b41116419d7d2f28d82c) fix: allow extra kernel args for secureboot installer * [`f38eaaab8`](https://github.com/siderolabs/talos/commit/f38eaaab87f77f33b0317d4405c84575023ee0da) feat: rework secureboot and PCR signing key * [`6eade3d5e`](https://github.com/siderolabs/talos/commit/6eade3d5ef5c5356d0bfc0e3d52263a39d2e9f1a) chore: add ability to rewrite uuids and set unique tokens for Talos * [`e9c7ac17a`](https://github.com/siderolabs/talos/commit/e9c7ac17a9b707950b249e08e11ed7ddac64e8ae) fix: set max msg recv size when proxying * [`e22ab440d`](https://github.com/siderolabs/talos/commit/e22ab440d7794a9c46edf1357124571057b6b19d) feat: update Linux 6.1.61, containerd 1.7.8, runc 1.1.10 * [`8245361f9`](https://github.com/siderolabs/talos/commit/8245361f9cfb66d68bc54330a47814eb730eb839) feat: show first 32 bytes of response body on download error * [`75d3987c0`](https://github.com/siderolabs/talos/commit/75d3987c05390d3c0a7cf4de855895f1d10c8a84) chore: drop sha1 from genereated pcr json * [`6f32d2990`](https://github.com/siderolabs/talos/commit/6f32d2990f438a9e8134d7e94558a54b3912854e) feat: add `.der` output `talosctl gen secureboot pcr` * [`87c40da6c`](https://github.com/siderolabs/talos/commit/87c40da6cc5d9ae62d20984ba5d3762da734a49e) fix: proper logging in machined on startup * [`a54da5f64`](https://github.com/siderolabs/talos/commit/a54da5f641886d723465e0a8cfa95b15bc2e96aa) fix: image build for nanopi_4s * [`6f3cd0593`](https://github.com/siderolabs/talos/commit/6f3cd05935a2faaf14d16c2e643f54e6f9134c0f) refactor: update packet capture to use 'afpacket' interface * [`813442dd7`](https://github.com/siderolabs/talos/commit/813442dd7a08b2781829ef190b110aa38c725932) fix: don't validate machine.install if installed * [`dff60069c`](https://github.com/siderolabs/talos/commit/dff60069c0230ecf531c5593724211fd75f26d7c) feat: update Kubernetes to 1.29.0-alpha.3 * [`c97db5dfe`](https://github.com/siderolabs/talos/commit/c97db5dfe174032f012bdd525a3479ebea200c93) chore: bump Go dependencies * [`807a9950a`](https://github.com/siderolabs/talos/commit/807a9950ac5cb542e41d65af0f9f80f1c73550a3) fix: use custom Talos/kernel version when generating UKI * [`eb94468a6`](https://github.com/siderolabs/talos/commit/eb94468a659b4518b317398f92346b62e6adefe4) docs: add documentation for Image Factory * [`2e78513e1`](https://github.com/siderolabs/talos/commit/2e78513e16b2eb0d83a4a7e107c470058d30837d) refactor: drop the dependency link platform -> network ctrl * [`6dc776b8a`](https://github.com/siderolabs/talos/commit/6dc776b8aaa2d9382737d41a90023e8e4ea1a601) fix: when writing to META in the installer/imager, use fixed name * [`3703041e9`](https://github.com/siderolabs/talos/commit/3703041e989c83c1ad7496851c6687f729cb207f) chore: remove uneeded code * [`cbe6e7622`](https://github.com/siderolabs/talos/commit/cbe6e7622d0180ca53ab0ce92d38e4704d466d1a) fix: generate images for SBCs using imager * [`5dff164f1`](https://github.com/siderolabs/talos/commit/5dff164f1c8fc08b66f0ea509db36561eaef464c) fix: fix error output of cli action tracker * [`ef5056122`](https://github.com/siderolabs/talos/commit/ef5056122b38a168dd8ee429a6bc4cad0860177d) feat: update etcd to 3.5.10 * [`45ae80873`](https://github.com/siderolabs/talos/commit/45ae80873f1a7a3cb5643f7d94108a96f36cad32) chore: bump go-api-signature dependency to v0.3.1 * [`ffa5e05cb`](https://github.com/siderolabs/talos/commit/ffa5e05cb9c8028897ce5e08183be52965004726) fix: make Talos work on Rockpi 4c boards again * [`8eba4c599`](https://github.com/siderolabs/talos/commit/8eba4c5999ca4a43220704ff2297706fd9e9d27b) feat: generate secrets bundle from the machine config * [`c7de745f6`](https://github.com/siderolabs/talos/commit/c7de745f61490ee8192bbab34fbb8a4bad21de9f) chore: drop deprecated code * [`cc0c3ab69`](https://github.com/siderolabs/talos/commit/cc0c3ab69c7807236955eb53ccac4cc70fcca32a) docs: update rpi_generic.md * [`a009f5c60`](https://github.com/siderolabs/talos/commit/a009f5c60c9506dd5064106bbef38fe36813db64) fix: accept sysctl paths with dots * [`4919f6ee2`](https://github.com/siderolabs/talos/commit/4919f6ee22b5f6cf53f801e13072f6d64027c215) feat: add GOMEMLIMIT to shipped manifests with memory limits * [`73ee576ea`](https://github.com/siderolabs/talos/commit/73ee576ea711a9f36a8d35ceba4716276a2e5f70) chore: update sonobuouy library, drop the fork * [`c23bc2f4a`](https://github.com/siderolabs/talos/commit/c23bc2f4a77c3e9b2e88f99d05266fcd8fb4a51b) chore: support OCI layout as a source for profile input * [`154bbd70f`](https://github.com/siderolabs/talos/commit/154bbd70f7bdfd464ad6136c7e7e057d2402c0f6) docs: fix talos version in guide for docker

### Changes from siderolabs/extras
7 commits

* [`e8e801b`](https://github.com/siderolabs/extras/commit/e8e801b0038ee23385e6a195e0f3d27fdb4f34b1) feat: update Go to 1.21.4 * [`d816a02`](https://github.com/siderolabs/extras/commit/d816a0246b054c94eb7a6ecbf7c0d287a55cfab4) chore: move project to using kres * [`3893789`](https://github.com/siderolabs/extras/commit/389378913436bc1c282c9e38a9066e847b4f1b51) chore: move to github workflows * [`6d48418`](https://github.com/siderolabs/extras/commit/6d484185f40337f1455a99bbe8d8e1bed716bc6a) feat: update Go to 1.21.3 * [`09d7c3e`](https://github.com/siderolabs/extras/commit/09d7c3e93d4fbad01579bb89b8b5ccac06b914fc) chore: update releases * [`a011245`](https://github.com/siderolabs/extras/commit/a011245588d652ef1bfe23ec9a66c0500868b829) feat: update Go to 1.21.1 * [`d3f54c7`](https://github.com/siderolabs/extras/commit/d3f54c7fed6c664f966ebfef76fb338f2fc2bc45) feat: update Go to 1.20.8

### Changes from siderolabs/gen
2 commits

* [`efca710`](https://github.com/siderolabs/gen/commit/efca710d509e6088d7a1a825bd49317df1427639) chore: add `FilterInPlace` method to maps and update module * [`36a3ae3`](https://github.com/siderolabs/gen/commit/36a3ae312ce03876b2c961a1bcb4ef4c221593d7) feat: update module

### Changes from siderolabs/go-kubernetes
7 commits

* [`fa05430`](https://github.com/siderolabs/go-kubernetes/commit/fa054302843bf48d96d839d0b3ffc6621261ff45) chore: support kube-scheduler config version * [`68bf392`](https://github.com/siderolabs/go-kubernetes/commit/68bf392083adbe2b13487ac418930e71e1c318c7) feat: add dropped API resource for 1.29 * [`09fa006`](https://github.com/siderolabs/go-kubernetes/commit/09fa0066c89220f0df6beaddd544ab0100802258) fix: retry Windows connection errors * [`3aa47a4`](https://github.com/siderolabs/go-kubernetes/commit/3aa47a46f28c8a8c62650a00002f88411202e9d8) feat: support Kubernetes 1.29 upgrades * [`ae33a4a`](https://github.com/siderolabs/go-kubernetes/commit/ae33a4a3939cddfceedd2846c0711676775de57e) feat: introduce support for Kubernetes version compatibility checks * [`cf2754e`](https://github.com/siderolabs/go-kubernetes/commit/cf2754eecb4ae54e5333d4f31cc725950963ecf5) chore: update to use GHA * [`44e26b3`](https://github.com/siderolabs/go-kubernetes/commit/44e26b35ffe85bc3e310d0b8dafd4db9bbe99db2) feat: update removed feature gates for 1.28

### Changes from siderolabs/go-retry
1 commit

* [`23b6fc2`](https://github.com/siderolabs/go-retry/commit/23b6fc21e54e702f324dbdd2576b6c7c60fb7bd5) fix: provider modern error unwrapping

### Changes from siderolabs/pkgs
32 commits

* [`3aea711`](https://github.com/siderolabs/pkgs/commit/3aea71139fe19d8161c0e3f90272add239b51b62) feat: bump dependencies * [`d59cb3e`](https://github.com/siderolabs/pkgs/commit/d59cb3e3052f861f02e33b9980dd2a4cb859a2e2) feat(lvm2): configure thin support * [`252a59f`](https://github.com/siderolabs/pkgs/commit/252a59ffe374ce98c71b0c9b959e691addd38919) feat: bump dependencies * [`0bb2a79`](https://github.com/siderolabs/pkgs/commit/0bb2a79ac7dadb6f1cf13ae061b4a6ca63cc2b7e) feat: update Go to 1.21.4 * [`f57b0a9`](https://github.com/siderolabs/pkgs/commit/f57b0a9b3efff0410c758b662f8a717b643526fb) chore: fix kernel target to honor `PLATFORM` * [`5f84302`](https://github.com/siderolabs/pkgs/commit/5f843025416b6e7b5a7b5920f48b610fe94d7611) chore: move to using kres * [`d7509f1`](https://github.com/siderolabs/pkgs/commit/d7509f19c9971155a14d1a3ecda5b23424d02cd6) chore: bump deps * [`3a66437`](https://github.com/siderolabs/pkgs/commit/3a6643741d423de6286457cfb71097d420f038a1) chore: add gh workflows * [`2e892fd`](https://github.com/siderolabs/pkgs/commit/2e892fdca61391fdb060797ed372f86ca71bb5b8) feat: update versions * [`37348d6`](https://github.com/siderolabs/pkgs/commit/37348d6cf39459ff38359a651013136e5c644cb9) feat: update Go to 1.21.3 * [`34f3c41`](https://github.com/siderolabs/pkgs/commit/34f3c41d45980a3282432be79d940d1c87e32708) feat: add Solarflare SFC9000 support * [`0c84090`](https://github.com/siderolabs/pkgs/commit/0c8409060699e20eac8d7123b5213b443d3b7b5e) feat: update releases * [`19cdf71`](https://github.com/siderolabs/pkgs/commit/19cdf71b84363929092b7ad8f2f4a7464abd98fc) feat: enable common sensors * [`acee18e`](https://github.com/siderolabs/pkgs/commit/acee18e8a3cce66a0df47c927cb7fe2b4bc81685) chore: bump kernel to 6.1.54 * [`1d16fd2`](https://github.com/siderolabs/pkgs/commit/1d16fd2e22ce0a444df3df82f8c99a93347698c2) feat: add Chelsio support * [`4504f83`](https://github.com/siderolabs/pkgs/commit/4504f83f668776161af56853c3faec61edc4cdb6) chore: rename kconfig-hardened-check * [`847a9c3`](https://github.com/siderolabs/pkgs/commit/847a9c3bbea9a8c350dc0b2b84d473c2fd23feb2) chore: enable dm thin provisioning * [`1401505`](https://github.com/siderolabs/pkgs/commit/1401505a95eebb1ff9e2baac6239baf822b7576f) chore: drop `-pkgs` for upstream kernel modules * [`a62471d`](https://github.com/siderolabs/pkgs/commit/a62471daea9b4e6f1d7ed03c208a5603096037f3) feat: add binfmt_misc support * [`518c441`](https://github.com/siderolabs/pkgs/commit/518c441851a434e72939a3f27e8dfb64e3360bb6) feat: add gVNIC support * [`7d9e60e`](https://github.com/siderolabs/pkgs/commit/7d9e60e33fb602c81c61112f3557808a7064bb9a) feat: update Go to 1.21.1 * [`d3d7d29`](https://github.com/siderolabs/pkgs/commit/d3d7d295221d951e8f8bb3935f04392f9efe0e1f) chore: bump deps * [`3b70656`](https://github.com/siderolabs/pkgs/commit/3b70656344332f553e0ae16e8bb39e1c3d92287a) chore: fix cacert perms * [`cca80b7`](https://github.com/siderolabs/pkgs/commit/cca80b7b939a2e5eb4769cc9e84d471bc4a6aec1) feat: update Linux to 6.1.46 * [`2e1c0b9`](https://github.com/siderolabs/pkgs/commit/2e1c0b912b2bcde35f04d63fe6840d5e4dc74d60) fix: nonfree kmod pkg name * [`cff5beb`](https://github.com/siderolabs/pkgs/commit/cff5bebf2f23ab02591ca1d72a87208d94328ab4) feat: add btrfs support * [`7717b7e`](https://github.com/siderolabs/pkgs/commit/7717b7e01c4c7170c7a6dcfaf74513585f40b14c) chore: bump deps * [`2f19f18`](https://github.com/siderolabs/pkgs/commit/2f19f18d145096766dea3c592c28e62f08113b38) feat: update containerd to 1.6.23 * [`30d4b74`](https://github.com/siderolabs/pkgs/commit/30d4b743f49396d62dc3ffadcf25511cf891e964) feat: update Go to 1.21 * [`eda123d`](https://github.com/siderolabs/pkgs/commit/eda123ddbd7ea5682ffe62164c41daf8ba531416) feat: update runc to 1.1.9 * [`30cd584`](https://github.com/siderolabs/pkgs/commit/30cd5846bd7a9cbf5e79c23b9e42a65a213276e2) chore: enable pushing of non-free packages * [`fb247b5`](https://github.com/siderolabs/pkgs/commit/fb247b5dcc465b6d77248b544465f582a0dd6e6c) chore: update kernel and microcode

### Changes from siderolabs/siderolink
5 commits

* [`5ab8f9d`](https://github.com/siderolabs/siderolink/commit/5ab8f9d2e62237eb3b9a26c6ee1e8c39a866487d) feat: allow persistent keepalive to be set for the peer * [`71dd308`](https://github.com/siderolabs/siderolink/commit/71dd3084984dfd78880efab9e9a9cc1a2313ad4d) chore: provide unique_token and Talos version in ProvisionRequest * [`0ee5425`](https://github.com/siderolabs/siderolink/commit/0ee54251337fd509e83b714f2fdaf9f48dbf022c) chore: revert sys moduel to 0.13.0 * [`6be9ba7`](https://github.com/siderolabs/siderolink/commit/6be9ba7600782a885f2c11f8dbb81b818d136de1) chore: bump deps * [`448cbe1`](https://github.com/siderolabs/siderolink/commit/448cbe19086c8f3a99869b675054cce6df8cd2c7) chore: bump `golang.org/x/net` to 0.8.0

### Changes from siderolabs/tools
13 commits

* [`ff7fe96`](https://github.com/siderolabs/tools/commit/ff7fe96d1de23ca4c61db36b333e135ec5af4404) feat: update Go to 1.21.4 * [`6216d64`](https://github.com/siderolabs/tools/commit/6216d641c29afefd22e757f73b2dfa7428891971) fix: org name * [`4334b92`](https://github.com/siderolabs/tools/commit/4334b92a02577e6ed7a3bb1645bef45f6465cb1c) chore: move to using kres * [`024ef25`](https://github.com/siderolabs/tools/commit/024ef257f4b13a1b0f31399f00881dd492d9cf20) chore: bump deps * [`5a22409`](https://github.com/siderolabs/tools/commit/5a2240995c086b97d05189ebc965c5ff281e65ad) chore: refactor github actions * [`9a05d12`](https://github.com/siderolabs/tools/commit/9a05d126f5bd2cc14af5d4c55499c07023f058d8) feat: move to gh workflow * [`a4a52e2`](https://github.com/siderolabs/tools/commit/a4a52e235c6debd23c2fbd938ceb6e97326d1b6f) chore: add dummy gh workflow * [`9c09b00`](https://github.com/siderolabs/tools/commit/9c09b00ded2aa843a2142bc6659cdcade607c566) feat: update dependencies * [`35948af`](https://github.com/siderolabs/tools/commit/35948af8c3f955d02900c9dcd76f1c9e33502f52) feat: update Go to 1.21.3 * [`09023c1`](https://github.com/siderolabs/tools/commit/09023c1d6eec46f43ccdba3bd703d0d3ac72220e) feat: update OpenSSL to 3.1.3 * [`7fa8bb5`](https://github.com/siderolabs/tools/commit/7fa8bb542cb2984992e002ccceb0d655d336be96) feat: update releases * [`fa388de`](https://github.com/siderolabs/tools/commit/fa388de914cc3efd54a23ad2a650437e600fbb09) feat: update Go to 1.21.1 * [`33fb4b3`](https://github.com/siderolabs/tools/commit/33fb4b35661b12fcf023ec96746e04281cc8c911) feat: update Go to 1.21

### Dependency Changes * **github.com/Azure/azure-sdk-for-go/sdk/azcore** v1.9.0 **_new_** * **github.com/Azure/azure-sdk-for-go/sdk/azidentity** v1.4.0 **_new_** * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates** v1.0.0 **_new_** * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys** v1.0.1 **_new_** * **github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets** v1.0.1 **_new_** * **github.com/aws/aws-sdk-go-v2/config** v1.18.32 -> v1.25.4 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.13.7 -> v1.14.5 * **github.com/aws/smithy-go** v1.14.0 -> v1.17.0 * **github.com/beevik/ntp** v1.2.0 -> v1.3.0 * **github.com/blang/semver/v4** v4.0.0 **_new_** * **github.com/containerd/cgroups/v3** v3.0.2 **_new_** * **github.com/containerd/containerd** v1.6.23 -> v1.7.9 * **github.com/cosi-project/runtime** v0.3.1 -> v0.3.17 * **github.com/distribution/reference** v0.5.0 **_new_** * **github.com/docker/docker** v24.0.5 -> v24.0.7 * **github.com/fatih/color** v1.15.0 -> v1.16.0 * **github.com/foxboron/go-uefi** 32187aa193d0 -> 18b9ba9cd4c3 * **github.com/fsnotify/fsnotify** v1.6.0 -> v1.7.0 * **github.com/google/go-cmp** v0.5.9 -> v0.6.0 * **github.com/google/go-containerregistry** v0.15.2 -> v0.16.1 * **github.com/google/uuid** v1.3.0 -> v1.4.0 * **github.com/gopacket/gopacket** v1.1.1 -> e79bddbcb4a7 * **github.com/hetznercloud/hcloud-go/v2** v2.0.0 -> v2.4.0 * **github.com/insomniacslk/dhcp** 0f9eb93a696c -> 6a2c8fbdcc1c * **github.com/jsimonetti/rtnetlink** v1.3.4 -> v1.3.5 * **github.com/mattn/go-isatty** v0.0.19 -> v0.0.20 * **github.com/mdp/qrterminal/v3** v3.2.0 **_new_** * **github.com/opencontainers/runtime-spec** 1c3f411f0417 -> v1.1.0-rc.1 * **github.com/prometheus/procfs** v0.11.1 -> v0.12.0 * **github.com/rivo/tview** 6cc0565babaf -> 7c9e464bac02 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.20 -> v1.0.0-beta.21 * **github.com/siderolabs/extras** v1.5.0 -> v1.6.0-alpha.0-5-ge8e801b * **github.com/siderolabs/gen** v0.4.5 -> v0.4.7 * **github.com/siderolabs/go-kubernetes** v0.2.2 -> v0.2.8 * **github.com/siderolabs/go-retry** v0.3.2 -> v0.3.3 * **github.com/siderolabs/pkgs** v1.5.0-6-g2f2c9cd -> v1.6.0-alpha.0-31-g3aea711 * **github.com/siderolabs/siderolink** v0.3.1 -> v0.3.2 * **github.com/siderolabs/talos/pkg/machinery** v1.5.0 -> v1.6.0-alpha.1 * **github.com/siderolabs/tools** v1.5.0 -> v1.6.0-alpha.0-12-gff7fe96 * **github.com/spf13/cobra** v1.7.0 -> v1.8.0 * **github.com/vmware-tanzu/sonobuoy** v0.56.17 -> v0.57.1 * **go.etcd.io/etcd/api/v3** v3.5.9 -> v3.5.10 * **go.etcd.io/etcd/client/pkg/v3** v3.5.9 -> v3.5.10 * **go.etcd.io/etcd/client/v3** v3.5.9 -> v3.5.10 * **go.etcd.io/etcd/etcdutl/v3** v3.5.9 -> v3.5.10 * **go.uber.org/zap** v1.25.0 -> v1.26.0 * **go4.org/netipx** ec4c8b891b28 -> 6213f710f925 * **golang.org/x/net** v0.13.0 -> v0.18.0 * **golang.org/x/oauth2** v0.14.0 **_new_** * **golang.org/x/sync** v0.3.0 -> v0.5.0 * **golang.org/x/sys** v0.10.0 -> v0.14.0 * **golang.org/x/term** v0.10.0 -> v0.14.0 * **golang.org/x/text** v0.11.0 -> v0.14.0 * **golang.org/x/time** v0.3.0 -> v0.4.0 * **google.golang.org/grpc** v1.57.0 -> v1.59.0 * **k8s.io/api** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/apimachinery** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/apiserver** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/client-go** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/component-base** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/cri-api** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/klog/v2** v2.100.1 -> v2.110.1 * **k8s.io/kube-scheduler** v0.29.0-alpha.3 **_new_** * **k8s.io/kubectl** v0.28.0 -> v0.29.0-alpha.3 * **k8s.io/kubelet** v0.28.0 -> v0.29.0-alpha.3 * **sigs.k8s.io/yaml** v1.3.0 -> v1.4.0 Previous release can be found at [v1.5.0](https://github.com/siderolabs/talos/releases/tag/v1.5.0) ## [Talos 1.6.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.6.0-alpha.1) (2023-10-17) Welcome to the v1.6.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Network Device Selectors Previously, [network device selectors](https://www.talos.dev/v1.6/talos-guides/network/device-selector/) only matched the first link, now the configuration is applied to all matching links. ### Linux Firmware Starting with Talos 1.6, there is no Linux firmware included in the initramfs. Customers who need Linux firmware can pull them as extension during install time using the image factory service. If the initial boot requires firmware, a custom iso can be built with the firmware included using the image factory service. This also ensures that the linux-firmware is not tied to a specific Talos version. ### KubePrism [KubePrism](https://www.talos.dev/v1.6/kubernetes-guides/configuration/kubeprism/) is enabled by default on port 7445. ### talosctl CLI The command `images` deprecated in Talos 1.5 was removed, please use `talosctl images default` instead. ### Component Updates Linux: 6.1.58 containerd: 1.7.7 CoreDNS: 1.11.1 Kubernetes: 1.29.0-alpha.2 Flannel: 0.22.3 Talos is built with Go 1.21.3. ### Contributors * Andrey Smirnov * Noel Georgi * Andrey Smirnov * Dmitriy Matrenichev * Serge Logvinov * Radosław Piliszek * Artem Chernyshev * Thomas Way * Utku Ozdemir * Andrei Kvapil * Christian Rolland * Drew Hess * Enno Boland * Henry Sachs * Jacob McSwain * Jacob McSwain * Jared Davenport * Mans Matulewicz * Nebula * Nico Berlee * Sascha Desch * Spencer Smith * Steve Francis * Thomas Lemarchand * Tim Jones * Zachary Milonas * guoguangwu * mikucat0309 * ndbrew ### Changes
131 commits

* [`9dfae8467`](https://github.com/siderolabs/talos/commit/9dfae8467d5a0bf7d3fd753b980bc7801bf3e5f8) chore: update dependencies * [`38ce3c827`](https://github.com/siderolabs/talos/commit/38ce3c827a06c44e0399cd0a3d8a396687001b20) feat: nocloud prefer mac address * [`401e89411`](https://github.com/siderolabs/talos/commit/401e8941124056f9cd9649a555aafebb063bb94d) feat: customize image size * [`865f08f86`](https://github.com/siderolabs/talos/commit/865f08f867fa5784c5a25bfeb929dbe25a6eb763) docs: kubeadm migration guide improvements * [`c3e418200`](https://github.com/siderolabs/talos/commit/c3e418200032be376aa30f6db133f2dcbf8b67c2) refactor: use COSI runtime with new controller runtime DB * [`c1ee24465`](https://github.com/siderolabs/talos/commit/c1ee24465aaac079f84c58ac86f74e89dfeb01ed) feat: update Kubernetes to v1.29.0-alpha.2 * [`0ff7350ab`](https://github.com/siderolabs/talos/commit/0ff7350abe94c046b8c7759ca6a1c64d9b80e497) fix: oracle integration fixes * [`675bada45`](https://github.com/siderolabs/talos/commit/675bada45473a91f5a99134193acf48da2789545) test: add config generation stability tests * [`f9639fb53`](https://github.com/siderolabs/talos/commit/f9639fb531797f4db16696e81371d9043d7041a9) test: fix 'talosctl gen' tests * [`6142d87a0`](https://github.com/siderolabs/talos/commit/6142d87a0f3e0a5e4babb97667a22e2497c67b4c) feat: hostname configuration improvements on the NoCloud platform * [`7bb205ebe`](https://github.com/siderolabs/talos/commit/7bb205ebe2efdbd691dd81b49fc6acbd3a289fa5) fix: don't use runtime-specs Mount struct in machine config * [`d1b27926c`](https://github.com/siderolabs/talos/commit/d1b27926c24109a2044cd07b3bb2d1e2824857c2) feat: update Go to 1.21.3 * [`b87092ab6`](https://github.com/siderolabs/talos/commit/b87092ab69e8a4928727ad71f3ce01502f76c966) fix: handle secure boot state policy pcr digest error * [`498aeb8c3`](https://github.com/siderolabs/talos/commit/498aeb8c32a590b20140541a1a334fdf2da84105) docs: fix incorrect image suffix * [`c14a5d4f7`](https://github.com/siderolabs/talos/commit/c14a5d4f79a3af0d075288cc9fb74f15fa34faf1) feat: support service account auth in cli * [`336aee0fd`](https://github.com/siderolabs/talos/commit/336aee0fdb1302443f627f848bed8081bdb0d9b0) fix: use tpm2 hash algorithm constants and allow non-SHA-256 PCRs * [`69d8054c9`](https://github.com/siderolabs/talos/commit/69d8054c9ec194b801f8d3185519c4b26a6a6b07) chore: drop UpdateEndpointSuite * [`ef7be16c8`](https://github.com/siderolabs/talos/commit/ef7be16c801176fc983299229841a98f935e18ed) fix: clear the encryption config in META when STATE is reset * [`5fc60d2ca`](https://github.com/siderolabs/talos/commit/5fc60d2caa75a6e886e3a70c22b63a708f68ad43) feat: add Solarflare SFC9000 support * [`9b5cfdd0b`](https://github.com/siderolabs/talos/commit/9b5cfdd0bc252a9594f6d7112ebf7401e41d1546) chore: add tests for iscsi * [`b897764f8`](https://github.com/siderolabs/talos/commit/b897764f8e90fa237cedecba50a63f5f2f852543) docs: update proxmox.md * [`159f45bde`](https://github.com/siderolabs/talos/commit/159f45bde65097efe311674b253284cf7d167b26) docs: fix typos in CLI calls to endpoints * [`0bd1bdd74`](https://github.com/siderolabs/talos/commit/0bd1bdd744f68dc42ac64678972fede992a7189e) chore: allow insecure access to installer base image (imager) * [`10ed13067`](https://github.com/siderolabs/talos/commit/10ed13067958f3afa0819a3d8557933b218a391b) fix: the node IP for kubelet shouldn't change if nothing matches * [`e7575ecaa`](https://github.com/siderolabs/talos/commit/e7575ecaaea9625be471c9db1965e256959f0730) feat: support n-5 latest Kubernetes versions * [`e71508ec1`](https://github.com/siderolabs/talos/commit/e71508ec104b42d1882b26d6bab22fc43ca0d8bb) chore: update dependencies * [`6d7fa4668`](https://github.com/siderolabs/talos/commit/6d7fa466807ffcd3b6a5c84ae34a90c728fcb8be) docs: add metal network configuration guide * [`2b548ad0d`](https://github.com/siderolabs/talos/commit/2b548ad0d9fa7b1f1e057c160464494b1828eb77) feat: update containerd to 1.7.x * [`62dcfe81e`](https://github.com/siderolabs/talos/commit/62dcfe81eb17ad2927dff43a855f0169fd84271e) fix: update kubernetes library to support 1.29 upgrades * [`52caf0763`](https://github.com/siderolabs/talos/commit/52caf0763393bc171b95464fefd3af1a3efd5f1c) feat: update Kubernetes to 1.29.0-alpha.1 * [`390137447`](https://github.com/siderolabs/talos/commit/390137447fbf2a8e87cb7bb313a202dbd5a31045) feat: enable KubePrism by default * [`1beb5e86e`](https://github.com/siderolabs/talos/commit/1beb5e86e621595af0d93798c9e158bb48e2b363) docs: add KubePrism video * [`a52d3cda3`](https://github.com/siderolabs/talos/commit/a52d3cda3b2eecc8aabf64b99a3ded0dad7e84c3) chore: update gen and COSI runtime * [`29b201d61`](https://github.com/siderolabs/talos/commit/29b201d61902017be355853a8f11c903fe9fefae) feat: enable common h/w sensors * [`9c2ba7c6f`](https://github.com/siderolabs/talos/commit/9c2ba7c6fa1162cb946e91a7e7d4dfecd62027a5) chore: add tests for chelsio drivers * [`5ca4d58dc`](https://github.com/siderolabs/talos/commit/5ca4d58dc9a2477db44d34c9f30ed21b0c3d2131) fix: generate of modules.dep when on the machine * [`5efcccb6b`](https://github.com/siderolabs/talos/commit/5efcccb6b14f59a9c065273493e0b82af1a85226) chore: bump kernel to 6.1.54 * [`29c767a02`](https://github.com/siderolabs/talos/commit/29c767a028e346c635e99e491cdab150c756f77c) docs: add control plane nodes as users of apid also for control plane nodes * [`4874cfb95`](https://github.com/siderolabs/talos/commit/4874cfb95a8148dc7feec00de8c299d4ac022c53) chore: fix typo * [`96f2a62ea`](https://github.com/siderolabs/talos/commit/96f2a62eafb5c3cee254d6e15f6f8c3e91359b9a) test: update upgrade tests versions * [`f3a370acb`](https://github.com/siderolabs/talos/commit/f3a370acb21c83fd1393da30bad2a37ca6a09b2c) feat: update Flannel to 0.22.3 * [`efdee6965`](https://github.com/siderolabs/talos/commit/efdee69658cfea44681954dac2552cfeee5bb30e) feat: update Kubernetes to 1.28.2 * [`e3b494058`](https://github.com/siderolabs/talos/commit/e3b49405884186dc1db0d9592f95965a0904691d) fix: build CPU ucode correctly for early loader * [`c5bd0ac5c`](https://github.com/siderolabs/talos/commit/c5bd0ac5cf033a9e3084a5fe98f42ee784926636) refactor: reimplement the depmod extension rebuilder * [`0b883f52a`](https://github.com/siderolabs/talos/commit/0b883f52a5a81a36a0e777f6f87e2d1d176e2294) docs: add notes about stable addressing * [`3ef670a9e`](https://github.com/siderolabs/talos/commit/3ef670a9e8e7efff5af9872e1e13d8521ce2dca6) chore: pull in dm modules * [`8f4a36b0d`](https://github.com/siderolabs/talos/commit/8f4a36b0d4c35f5841a270b7b5cd7da7c798165f) docs: update aws to add command to allow KubeSpan wireguard port * [`a7edd0523`](https://github.com/siderolabs/talos/commit/a7edd0523f9e5a7fccc6c382b453000beab4a8ff) fix: set default route priority for hcloud platform * [`87c1b3ddd`](https://github.com/siderolabs/talos/commit/87c1b3ddd83f038c62d34e94ad7e34a98236130b) fix: calculate UKI ISO size dynamically * [`9698e4547`](https://github.com/siderolabs/talos/commit/9698e45479cb293bbefe1651b94344bd7b0a4e52) fix: handle correctly change of listen address for maintenance service * [`a096f05a5`](https://github.com/siderolabs/talos/commit/a096f05a56003c317ffade2c87aa8d327592e3b8) chore: update gRPC library and enable shared write buffers * [`9e78fecca`](https://github.com/siderolabs/talos/commit/9e78feccaecda53778acba43fb9ad177051a009c) chore: improve image signing process * [`f00567e20`](https://github.com/siderolabs/talos/commit/f00567e20f239e781975636b12e31501ee39bbfa) chore: add PKG_KERNEL arg to customize used kernel * [`2960f93ba`](https://github.com/siderolabs/talos/commit/2960f93baa55f6ea2cb3690cbc652df9aee17af8) feat: add readonly information to the disks API response * [`735bf9ed0`](https://github.com/siderolabs/talos/commit/735bf9ed08a5d8dd302ef3e1f61317ff9169549c) feat: bring in Google vNIC driver * [`3f5232075`](https://github.com/siderolabs/talos/commit/3f523207522aa69452516408f914cc792abb78b9) feat: upgrade-k8s without comments * [`e44875106`](https://github.com/siderolabs/talos/commit/e44875106e28e50b15c38fa8b889f51083325800) docs: update deploying-cilium.md * [`7046cae43`](https://github.com/siderolabs/talos/commit/7046cae43dd4e8a4ea7d80934b02cc7c8b84e53a) chore: update gopacket to reduce init memory allocs * [`da73b563d`](https://github.com/siderolabs/talos/commit/da73b563dd0a7d77f4490d10cc506b5570c2bf11) chore: update Go to 1.21.1 * [`5e11f08a6`](https://github.com/siderolabs/talos/commit/5e11f08a639bd791fa7fafe3df35349959b4eb24) fix: trim file path in the container image * [`3d2dad4e6`](https://github.com/siderolabs/talos/commit/3d2dad4e69ba458fb406a7d7441d9e3f2fe8fde2) chore: show securtiystate on dashboard * [`b48510874`](https://github.com/siderolabs/talos/commit/b4851087404e6fcad52da588fd4827046011b271) chore: e2e-aws cleanup * [`1eebbce35`](https://github.com/siderolabs/talos/commit/1eebbce357311aaea739abe55c9e0de947791f39) chore: add output flag for talosctl config info * [`3fbed806c`](https://github.com/siderolabs/talos/commit/3fbed806c4e5a4167f0a357eb20486bb406103a8) chore: add tests for util-linux extensions * [`7c514a1a6`](https://github.com/siderolabs/talos/commit/7c514a1a6c258a5f5f3ed6a4dbb15ed531a7e0b2) docs: update header links * [`6058c3602`](https://github.com/siderolabs/talos/commit/6058c360238ba70c780df7a24a0f6a13fa46a833) fix: shorten VLAN link names to fit into the limit of 15 characters * [`9c2f765c8`](https://github.com/siderolabs/talos/commit/9c2f765c86ca73d6d14957b7ae1bc7bd32fed0fd) fix: allow network device selector to match multiple links * [`a04b98637`](https://github.com/siderolabs/talos/commit/a04b9863762acefe2030a8a64f9c8d8608432fd2) fix: update kubernetes library for 1.28 upgrade pre-checks * [`f7473e477`](https://github.com/siderolabs/talos/commit/f7473e4778fe2d36ce600378cfc8d7630096f2d7) feat: update default Kubernetes to 1.28.1 * [`d693604a1`](https://github.com/siderolabs/talos/commit/d693604a1d76aa72698eed2c1cab19e3cd34dc01) chore: fix default image list in the release notes * [`d91b5b3a3`](https://github.com/siderolabs/talos/commit/d91b5b3a31188d64cbc5ad8385000fae0fcf55e5) feat: set environment variables early in the boot * [`c918c0855`](https://github.com/siderolabs/talos/commit/c918c0855d08e06b832699e8c8b66017e457abc9) fix: set correct (1 year) talosconfig expiration * [`79bbdf454`](https://github.com/siderolabs/talos/commit/79bbdf454eb9bb891e845efff73db1bbdfd6d43e) fix: set proper timeouts for KubePrism loadbalancer * [`b8fb55d5c`](https://github.com/siderolabs/talos/commit/b8fb55d5c2e0433df46ac7bc3eeaea08e12d572d) fix: use a mount prefix when installing a bootloader * [`44f59a804`](https://github.com/siderolabs/talos/commit/44f59a8049beed1db453ef1d5a74f0e771ae39ff) feat: improve imager APIs * [`2d3ac925e`](https://github.com/siderolabs/talos/commit/2d3ac925ea519b8b5160190e1fdb8aba01a9ef74) refactor: update NTP spike detector * [`af0cc70e3`](https://github.com/siderolabs/talos/commit/af0cc70e3775cf7017387c541273a2580c55c78c) test: update e2e-aws to use worker groups * [`d03dc7a8a`](https://github.com/siderolabs/talos/commit/d03dc7a8afdd3fbf084a6d91544de5423f56d68c) chore: validate new system extensions * [`bbeb489aa`](https://github.com/siderolabs/talos/commit/bbeb489aa8282809bf65e89b3a571193814d3b1e) chore: drop firmware from initramfs * [`3c9f7a7de`](https://github.com/siderolabs/talos/commit/3c9f7a7de641bed699533ace6451387ddbfec44e) chore: re-enable nolintlint and typecheck linters * [`c51e2c9b4`](https://github.com/siderolabs/talos/commit/c51e2c9b482a113b154d3e6d7b2b37346a1b1043) feat: update CoreDNS to 1.11.1 * [`8670450d2`](https://github.com/siderolabs/talos/commit/8670450d28040f35e08aa4d771a1415cd5c1920d) release(v1.6.0-alpha.0): prepare release * [`6778ded29`](https://github.com/siderolabs/talos/commit/6778ded29de5369b1869194a0710f627121b5334) feat: add e2e-aws for nvidia extensions * [`74c07ed71`](https://github.com/siderolabs/talos/commit/74c07ed714d5751336e8745977caa3dca5060d7d) chore: update Go to 1.21 * [`a28d72e9c`](https://github.com/siderolabs/talos/commit/a28d72e9c262bd8fb84959ede952542a6e95d0be) fix: ova contents to be named `disk.*` * [`c0ea4d7ba`](https://github.com/siderolabs/talos/commit/c0ea4d7ba504dd8e1558f11e0cddd41dbf8bc720) fix: properly calculate overal of node address with subnet filters * [`d6b2719e2`](https://github.com/siderolabs/talos/commit/d6b2719e2e824cf5df9314523e3a4138b404e615) chore: drone: move extensions step to a function * [`9608ef56d`](https://github.com/siderolabs/talos/commit/9608ef56dc602636da1449ff05d237e0e20e5154) chore: allow bridge traffic with DHCP broadcast traffic * [`c99316457`](https://github.com/siderolabs/talos/commit/c993164576453fd03eb8fc517badd7de8004f4ad) docs: fix the installing system extensions doc * [`833895940`](https://github.com/siderolabs/talos/commit/833895940b173e247816751ca7287ccde7a36d03) chore: add tests for zfs extension * [`cb468c41c`](https://github.com/siderolabs/talos/commit/cb468c41cbbec6cd5f28c3cd3457aa4a30b81d4c) fix: copy proper modules to arm64 squashfs * [`ea0d6e8c6`](https://github.com/siderolabs/talos/commit/ea0d6e8c6a8ce8cd516bc05c99534241dff60b9f) fix: prevent dashboard crashes when process info is not available * [`e9077a6fb`](https://github.com/siderolabs/talos/commit/e9077a6fb9db5bcadea342200f057c1dc6ffb9af) feat: filter the hostname to produce nodename * [`dc8361c1d`](https://github.com/siderolabs/talos/commit/dc8361c1d524e3a52dfa18ee1b539fb81a02ef8d) fix: properly GC images supplied with both tag and digest * [`ccfa8de11`](https://github.com/siderolabs/talos/commit/ccfa8de1174b4e5d59c2f92b44d8dd65235b590a) fix: automatically change `rpi_4` board on upgrade * [`b56e8b7d9`](https://github.com/siderolabs/talos/commit/b56e8b7d9babe9a963b1fc9a2f41882d08fbafe3) fix: support 'List' type manifests * [`574d48e54`](https://github.com/siderolabs/talos/commit/574d48e54020b02f74c2aeadca1c10499bf967b0) fix: use image digest when starting a container * [`175747cea`](https://github.com/siderolabs/talos/commit/175747cea58d73f8532c114b7754668d24ab9c92) fix: ntp query error with bare IPv6 address * [`c8b507fb2`](https://github.com/siderolabs/talos/commit/c8b507fb26ca30cf0aa98c8cf669a2a03583fc1c) docs: fix kubeprism typo * [`0cdcb2e0e`](https://github.com/siderolabs/talos/commit/0cdcb2e0e8131510aab654211d3622fb17f8375e) docs: restructure docs for nvidia drivers for v1.4 * [`676db9768`](https://github.com/siderolabs/talos/commit/676db9768433027ebc6ff22a0414692ccec2ccf4) docs: fork docs for Talos 1.6 * [`92ad18c18`](https://github.com/siderolabs/talos/commit/92ad18c18fae5ac073cdd98d24c5aeb5edb4091a) fix: write correct capacity to the ovf * [`6b0373ebe`](https://github.com/siderolabs/talos/commit/6b0373ebef88600571ec54c189fd6ea3b0c777e8) chore: move bash tests to integration * [`52b3d8d37`](https://github.com/siderolabs/talos/commit/52b3d8d37cd1cf4eb3aa046781f105a1c39e69a0) docs: make Talos 1.5 documentation the default one * [`dc873df9b`](https://github.com/siderolabs/talos/commit/dc873df9b4cf169b4f7789690b80ac1e02b27d57) chore: fix the filenames of openstack images * [`b5c0e7b24`](https://github.com/siderolabs/talos/commit/b5c0e7b24cbd1546304ca33328b89e022e6e0675) docs: update nvidia docs * [`9606e871e`](https://github.com/siderolabs/talos/commit/9606e871e422b72aaef39ae03e334119602b8f31) docs: update Jiva Pod Security Policy * [`a86ed4362`](https://github.com/siderolabs/talos/commit/a86ed4362c009c389766ecd4bfcbc0ade999bb2e) chore: update Kubernetes Go modules to 0.28.0 * [`97b4e3e91`](https://github.com/siderolabs/talos/commit/97b4e3e91cb4a238a8f81c8ce2983c0033a355cb) feat: update Kubernetes to 1.28.0 * [`79ca1a3df`](https://github.com/siderolabs/talos/commit/79ca1a3dfb485fc5180bda38ab58a2d4c595a6aa) feat: e2e-aws using tf code * [`bf3a5e011`](https://github.com/siderolabs/talos/commit/bf3a5e01190e1cf80769343cf94af4c1bfb80318) chore: add version compatibility for Talos 1.6 * [`969e8097c`](https://github.com/siderolabs/talos/commit/969e8097ce062197c9011d206cdbc7de1dc87df5) feat: update Kubernetes to 1.28.0-rc.1 * [`ca41b611e`](https://github.com/siderolabs/talos/commit/ca41b611e97a0ef5020f01011267b82a155d136a) chore: drone jsonnet cleanup * [`bc198e98e`](https://github.com/siderolabs/talos/commit/bc198e98ef6dd03e07d75ab2eb8b944d10ad3739) docs: retain cilium autoMount pending upstream hostPath fix * [`86c94eff8`](https://github.com/siderolabs/talos/commit/86c94eff8d9e1abec11039f79dc6a9b35d46c7f3) refactor: docgen and config examples * [`ee6d639f6`](https://github.com/siderolabs/talos/commit/ee6d639f6c374cf8e1843dd3720047fea7dd3325) fix: match routes on the priority properly * [`bff0d8f32`](https://github.com/siderolabs/talos/commit/bff0d8f32c55d0cec9aed67592a6ccad8e5efee8) chore: fix dependencies in the release pipeline * [`e1b288679`](https://github.com/siderolabs/talos/commit/e1b288679e922fa0e255273adf4b7a1226518424) refactor: compile regex in validation method on the first use * [`daa4c185a`](https://github.com/siderolabs/talos/commit/daa4c185ae9a6318d779f45c730ac695e14ca6c7) docs: add what's new and documentation for Talos 1.5 * [`c4a1ca8d6`](https://github.com/siderolabs/talos/commit/c4a1ca8d61fcb1338da1ca223b9b4349a6af76e2) chore: remove <-errCh where possible in grpc methods * [`e0f383598`](https://github.com/siderolabs/talos/commit/e0f383598e2f285c04264e9a3787fcdcd56add85) chore: clean up the output of the `imager` * [`fb536af4d`](https://github.com/siderolabs/talos/commit/fb536af4d1804b8b802a4211739ac410fd34bb93) chore: optimize memory usage of `tcell` library on init * [`7c86a365e`](https://github.com/siderolabs/talos/commit/7c86a365e2691065e5e06a4789621bc9f43f3c4b) chore: publish systemd-boot and systemd-stub assets * [`7d688ccfe`](https://github.com/siderolabs/talos/commit/7d688ccfeb00ca46999b98512e49ac94f17d2693) fix: make encryption config provider default to `luks2` if not set * [`80238a05a`](https://github.com/siderolabs/talos/commit/80238a05a6f83b2d8bf3b04816d2b0a5c499eca8) chore: unify semver under `github.com/blang/semver/v4` * [`0f1920bdd`](https://github.com/siderolabs/talos/commit/0f1920bdda5b7f2e2291e75d14453cf81a1b6cd6) chore: provide a resource to peek into Linux clock adjustments * [`4eab3017b`](https://github.com/siderolabs/talos/commit/4eab3017b036d3229a6fa7dc9612050d1499e2b6) fix: calculate log2i properly * [`bcf284530`](https://github.com/siderolabs/talos/commit/bcf2845307ad2c4395967cbb8e756d6a0d8caf2c) fix: update providerid prefix for aws * [`ac2aff5cc`](https://github.com/siderolabs/talos/commit/ac2aff5cc5e5234fecf1f49b0f5d583c633aafa4) fix: fix azure portion of cloud uploader * [`793dcedc9`](https://github.com/siderolabs/talos/commit/793dcedc957389c9d91da62517a43968bd99b09d) fix: fast-wipe the system disk on talosctl reset * [`76fa45afb`](https://github.com/siderolabs/talos/commit/76fa45afbac5d212faa534047255c0256e78d08a) docs: update cilium instructions

### Changes since v1.6.0-alpha.0
81 commits

* [`9dfae8467`](https://github.com/siderolabs/talos/commit/9dfae8467d5a0bf7d3fd753b980bc7801bf3e5f8) chore: update dependencies * [`38ce3c827`](https://github.com/siderolabs/talos/commit/38ce3c827a06c44e0399cd0a3d8a396687001b20) feat: nocloud prefer mac address * [`401e89411`](https://github.com/siderolabs/talos/commit/401e8941124056f9cd9649a555aafebb063bb94d) feat: customize image size * [`865f08f86`](https://github.com/siderolabs/talos/commit/865f08f867fa5784c5a25bfeb929dbe25a6eb763) docs: kubeadm migration guide improvements * [`c3e418200`](https://github.com/siderolabs/talos/commit/c3e418200032be376aa30f6db133f2dcbf8b67c2) refactor: use COSI runtime with new controller runtime DB * [`c1ee24465`](https://github.com/siderolabs/talos/commit/c1ee24465aaac079f84c58ac86f74e89dfeb01ed) feat: update Kubernetes to v1.29.0-alpha.2 * [`0ff7350ab`](https://github.com/siderolabs/talos/commit/0ff7350abe94c046b8c7759ca6a1c64d9b80e497) fix: oracle integration fixes * [`675bada45`](https://github.com/siderolabs/talos/commit/675bada45473a91f5a99134193acf48da2789545) test: add config generation stability tests * [`f9639fb53`](https://github.com/siderolabs/talos/commit/f9639fb531797f4db16696e81371d9043d7041a9) test: fix 'talosctl gen' tests * [`6142d87a0`](https://github.com/siderolabs/talos/commit/6142d87a0f3e0a5e4babb97667a22e2497c67b4c) feat: hostname configuration improvements on the NoCloud platform * [`7bb205ebe`](https://github.com/siderolabs/talos/commit/7bb205ebe2efdbd691dd81b49fc6acbd3a289fa5) fix: don't use runtime-specs Mount struct in machine config * [`d1b27926c`](https://github.com/siderolabs/talos/commit/d1b27926c24109a2044cd07b3bb2d1e2824857c2) feat: update Go to 1.21.3 * [`b87092ab6`](https://github.com/siderolabs/talos/commit/b87092ab69e8a4928727ad71f3ce01502f76c966) fix: handle secure boot state policy pcr digest error * [`498aeb8c3`](https://github.com/siderolabs/talos/commit/498aeb8c32a590b20140541a1a334fdf2da84105) docs: fix incorrect image suffix * [`c14a5d4f7`](https://github.com/siderolabs/talos/commit/c14a5d4f79a3af0d075288cc9fb74f15fa34faf1) feat: support service account auth in cli * [`336aee0fd`](https://github.com/siderolabs/talos/commit/336aee0fdb1302443f627f848bed8081bdb0d9b0) fix: use tpm2 hash algorithm constants and allow non-SHA-256 PCRs * [`69d8054c9`](https://github.com/siderolabs/talos/commit/69d8054c9ec194b801f8d3185519c4b26a6a6b07) chore: drop UpdateEndpointSuite * [`ef7be16c8`](https://github.com/siderolabs/talos/commit/ef7be16c801176fc983299229841a98f935e18ed) fix: clear the encryption config in META when STATE is reset * [`5fc60d2ca`](https://github.com/siderolabs/talos/commit/5fc60d2caa75a6e886e3a70c22b63a708f68ad43) feat: add Solarflare SFC9000 support * [`9b5cfdd0b`](https://github.com/siderolabs/talos/commit/9b5cfdd0bc252a9594f6d7112ebf7401e41d1546) chore: add tests for iscsi * [`b897764f8`](https://github.com/siderolabs/talos/commit/b897764f8e90fa237cedecba50a63f5f2f852543) docs: update proxmox.md * [`159f45bde`](https://github.com/siderolabs/talos/commit/159f45bde65097efe311674b253284cf7d167b26) docs: fix typos in CLI calls to endpoints * [`0bd1bdd74`](https://github.com/siderolabs/talos/commit/0bd1bdd744f68dc42ac64678972fede992a7189e) chore: allow insecure access to installer base image (imager) * [`10ed13067`](https://github.com/siderolabs/talos/commit/10ed13067958f3afa0819a3d8557933b218a391b) fix: the node IP for kubelet shouldn't change if nothing matches * [`e7575ecaa`](https://github.com/siderolabs/talos/commit/e7575ecaaea9625be471c9db1965e256959f0730) feat: support n-5 latest Kubernetes versions * [`e71508ec1`](https://github.com/siderolabs/talos/commit/e71508ec104b42d1882b26d6bab22fc43ca0d8bb) chore: update dependencies * [`6d7fa4668`](https://github.com/siderolabs/talos/commit/6d7fa466807ffcd3b6a5c84ae34a90c728fcb8be) docs: add metal network configuration guide * [`2b548ad0d`](https://github.com/siderolabs/talos/commit/2b548ad0d9fa7b1f1e057c160464494b1828eb77) feat: update containerd to 1.7.x * [`62dcfe81e`](https://github.com/siderolabs/talos/commit/62dcfe81eb17ad2927dff43a855f0169fd84271e) fix: update kubernetes library to support 1.29 upgrades * [`52caf0763`](https://github.com/siderolabs/talos/commit/52caf0763393bc171b95464fefd3af1a3efd5f1c) feat: update Kubernetes to 1.29.0-alpha.1 * [`390137447`](https://github.com/siderolabs/talos/commit/390137447fbf2a8e87cb7bb313a202dbd5a31045) feat: enable KubePrism by default * [`1beb5e86e`](https://github.com/siderolabs/talos/commit/1beb5e86e621595af0d93798c9e158bb48e2b363) docs: add KubePrism video * [`a52d3cda3`](https://github.com/siderolabs/talos/commit/a52d3cda3b2eecc8aabf64b99a3ded0dad7e84c3) chore: update gen and COSI runtime * [`29b201d61`](https://github.com/siderolabs/talos/commit/29b201d61902017be355853a8f11c903fe9fefae) feat: enable common h/w sensors * [`9c2ba7c6f`](https://github.com/siderolabs/talos/commit/9c2ba7c6fa1162cb946e91a7e7d4dfecd62027a5) chore: add tests for chelsio drivers * [`5ca4d58dc`](https://github.com/siderolabs/talos/commit/5ca4d58dc9a2477db44d34c9f30ed21b0c3d2131) fix: generate of modules.dep when on the machine * [`5efcccb6b`](https://github.com/siderolabs/talos/commit/5efcccb6b14f59a9c065273493e0b82af1a85226) chore: bump kernel to 6.1.54 * [`29c767a02`](https://github.com/siderolabs/talos/commit/29c767a028e346c635e99e491cdab150c756f77c) docs: add control plane nodes as users of apid also for control plane nodes * [`4874cfb95`](https://github.com/siderolabs/talos/commit/4874cfb95a8148dc7feec00de8c299d4ac022c53) chore: fix typo * [`96f2a62ea`](https://github.com/siderolabs/talos/commit/96f2a62eafb5c3cee254d6e15f6f8c3e91359b9a) test: update upgrade tests versions * [`f3a370acb`](https://github.com/siderolabs/talos/commit/f3a370acb21c83fd1393da30bad2a37ca6a09b2c) feat: update Flannel to 0.22.3 * [`efdee6965`](https://github.com/siderolabs/talos/commit/efdee69658cfea44681954dac2552cfeee5bb30e) feat: update Kubernetes to 1.28.2 * [`e3b494058`](https://github.com/siderolabs/talos/commit/e3b49405884186dc1db0d9592f95965a0904691d) fix: build CPU ucode correctly for early loader * [`c5bd0ac5c`](https://github.com/siderolabs/talos/commit/c5bd0ac5cf033a9e3084a5fe98f42ee784926636) refactor: reimplement the depmod extension rebuilder * [`0b883f52a`](https://github.com/siderolabs/talos/commit/0b883f52a5a81a36a0e777f6f87e2d1d176e2294) docs: add notes about stable addressing * [`3ef670a9e`](https://github.com/siderolabs/talos/commit/3ef670a9e8e7efff5af9872e1e13d8521ce2dca6) chore: pull in dm modules * [`8f4a36b0d`](https://github.com/siderolabs/talos/commit/8f4a36b0d4c35f5841a270b7b5cd7da7c798165f) docs: update aws to add command to allow KubeSpan wireguard port * [`a7edd0523`](https://github.com/siderolabs/talos/commit/a7edd0523f9e5a7fccc6c382b453000beab4a8ff) fix: set default route priority for hcloud platform * [`87c1b3ddd`](https://github.com/siderolabs/talos/commit/87c1b3ddd83f038c62d34e94ad7e34a98236130b) fix: calculate UKI ISO size dynamically * [`9698e4547`](https://github.com/siderolabs/talos/commit/9698e45479cb293bbefe1651b94344bd7b0a4e52) fix: handle correctly change of listen address for maintenance service * [`a096f05a5`](https://github.com/siderolabs/talos/commit/a096f05a56003c317ffade2c87aa8d327592e3b8) chore: update gRPC library and enable shared write buffers * [`9e78fecca`](https://github.com/siderolabs/talos/commit/9e78feccaecda53778acba43fb9ad177051a009c) chore: improve image signing process * [`f00567e20`](https://github.com/siderolabs/talos/commit/f00567e20f239e781975636b12e31501ee39bbfa) chore: add PKG_KERNEL arg to customize used kernel * [`2960f93ba`](https://github.com/siderolabs/talos/commit/2960f93baa55f6ea2cb3690cbc652df9aee17af8) feat: add readonly information to the disks API response * [`735bf9ed0`](https://github.com/siderolabs/talos/commit/735bf9ed08a5d8dd302ef3e1f61317ff9169549c) feat: bring in Google vNIC driver * [`3f5232075`](https://github.com/siderolabs/talos/commit/3f523207522aa69452516408f914cc792abb78b9) feat: upgrade-k8s without comments * [`e44875106`](https://github.com/siderolabs/talos/commit/e44875106e28e50b15c38fa8b889f51083325800) docs: update deploying-cilium.md * [`7046cae43`](https://github.com/siderolabs/talos/commit/7046cae43dd4e8a4ea7d80934b02cc7c8b84e53a) chore: update gopacket to reduce init memory allocs * [`da73b563d`](https://github.com/siderolabs/talos/commit/da73b563dd0a7d77f4490d10cc506b5570c2bf11) chore: update Go to 1.21.1 * [`5e11f08a6`](https://github.com/siderolabs/talos/commit/5e11f08a639bd791fa7fafe3df35349959b4eb24) fix: trim file path in the container image * [`3d2dad4e6`](https://github.com/siderolabs/talos/commit/3d2dad4e69ba458fb406a7d7441d9e3f2fe8fde2) chore: show securtiystate on dashboard * [`b48510874`](https://github.com/siderolabs/talos/commit/b4851087404e6fcad52da588fd4827046011b271) chore: e2e-aws cleanup * [`1eebbce35`](https://github.com/siderolabs/talos/commit/1eebbce357311aaea739abe55c9e0de947791f39) chore: add output flag for talosctl config info * [`3fbed806c`](https://github.com/siderolabs/talos/commit/3fbed806c4e5a4167f0a357eb20486bb406103a8) chore: add tests for util-linux extensions * [`7c514a1a6`](https://github.com/siderolabs/talos/commit/7c514a1a6c258a5f5f3ed6a4dbb15ed531a7e0b2) docs: update header links * [`6058c3602`](https://github.com/siderolabs/talos/commit/6058c360238ba70c780df7a24a0f6a13fa46a833) fix: shorten VLAN link names to fit into the limit of 15 characters * [`9c2f765c8`](https://github.com/siderolabs/talos/commit/9c2f765c86ca73d6d14957b7ae1bc7bd32fed0fd) fix: allow network device selector to match multiple links * [`a04b98637`](https://github.com/siderolabs/talos/commit/a04b9863762acefe2030a8a64f9c8d8608432fd2) fix: update kubernetes library for 1.28 upgrade pre-checks * [`f7473e477`](https://github.com/siderolabs/talos/commit/f7473e4778fe2d36ce600378cfc8d7630096f2d7) feat: update default Kubernetes to 1.28.1 * [`d693604a1`](https://github.com/siderolabs/talos/commit/d693604a1d76aa72698eed2c1cab19e3cd34dc01) chore: fix default image list in the release notes * [`d91b5b3a3`](https://github.com/siderolabs/talos/commit/d91b5b3a31188d64cbc5ad8385000fae0fcf55e5) feat: set environment variables early in the boot * [`c918c0855`](https://github.com/siderolabs/talos/commit/c918c0855d08e06b832699e8c8b66017e457abc9) fix: set correct (1 year) talosconfig expiration * [`79bbdf454`](https://github.com/siderolabs/talos/commit/79bbdf454eb9bb891e845efff73db1bbdfd6d43e) fix: set proper timeouts for KubePrism loadbalancer * [`b8fb55d5c`](https://github.com/siderolabs/talos/commit/b8fb55d5c2e0433df46ac7bc3eeaea08e12d572d) fix: use a mount prefix when installing a bootloader * [`44f59a804`](https://github.com/siderolabs/talos/commit/44f59a8049beed1db453ef1d5a74f0e771ae39ff) feat: improve imager APIs * [`2d3ac925e`](https://github.com/siderolabs/talos/commit/2d3ac925ea519b8b5160190e1fdb8aba01a9ef74) refactor: update NTP spike detector * [`af0cc70e3`](https://github.com/siderolabs/talos/commit/af0cc70e3775cf7017387c541273a2580c55c78c) test: update e2e-aws to use worker groups * [`d03dc7a8a`](https://github.com/siderolabs/talos/commit/d03dc7a8afdd3fbf084a6d91544de5423f56d68c) chore: validate new system extensions * [`bbeb489aa`](https://github.com/siderolabs/talos/commit/bbeb489aa8282809bf65e89b3a571193814d3b1e) chore: drop firmware from initramfs * [`3c9f7a7de`](https://github.com/siderolabs/talos/commit/3c9f7a7de641bed699533ace6451387ddbfec44e) chore: re-enable nolintlint and typecheck linters * [`c51e2c9b4`](https://github.com/siderolabs/talos/commit/c51e2c9b482a113b154d3e6d7b2b37346a1b1043) feat: update CoreDNS to 1.11.1

### Changes from siderolabs/extras
4 commits

* [`6d48418`](https://github.com/siderolabs/extras/commit/6d484185f40337f1455a99bbe8d8e1bed716bc6a) feat: update Go to 1.21.3 * [`09d7c3e`](https://github.com/siderolabs/extras/commit/09d7c3e93d4fbad01579bb89b8b5ccac06b914fc) chore: update releases * [`a011245`](https://github.com/siderolabs/extras/commit/a011245588d652ef1bfe23ec9a66c0500868b829) feat: update Go to 1.21.1 * [`d3f54c7`](https://github.com/siderolabs/extras/commit/d3f54c7fed6c664f966ebfef76fb338f2fc2bc45) feat: update Go to 1.20.8

### Changes from siderolabs/gen
2 commits

* [`efca710`](https://github.com/siderolabs/gen/commit/efca710d509e6088d7a1a825bd49317df1427639) chore: add `FilterInPlace` method to maps and update module * [`36a3ae3`](https://github.com/siderolabs/gen/commit/36a3ae312ce03876b2c961a1bcb4ef4c221593d7) feat: update module

### Changes from siderolabs/go-kubernetes
5 commits

* [`09fa006`](https://github.com/siderolabs/go-kubernetes/commit/09fa0066c89220f0df6beaddd544ab0100802258) fix: retry Windows connection errors * [`3aa47a4`](https://github.com/siderolabs/go-kubernetes/commit/3aa47a46f28c8a8c62650a00002f88411202e9d8) feat: support Kubernetes 1.29 upgrades * [`ae33a4a`](https://github.com/siderolabs/go-kubernetes/commit/ae33a4a3939cddfceedd2846c0711676775de57e) feat: introduce support for Kubernetes version compatibility checks * [`cf2754e`](https://github.com/siderolabs/go-kubernetes/commit/cf2754eecb4ae54e5333d4f31cc725950963ecf5) chore: update to use GHA * [`44e26b3`](https://github.com/siderolabs/go-kubernetes/commit/44e26b35ffe85bc3e310d0b8dafd4db9bbe99db2) feat: update removed feature gates for 1.28

### Changes from siderolabs/pkgs
24 commits

* [`2e892fd`](https://github.com/siderolabs/pkgs/commit/2e892fdca61391fdb060797ed372f86ca71bb5b8) feat: update versions * [`37348d6`](https://github.com/siderolabs/pkgs/commit/37348d6cf39459ff38359a651013136e5c644cb9) feat: update Go to 1.21.3 * [`34f3c41`](https://github.com/siderolabs/pkgs/commit/34f3c41d45980a3282432be79d940d1c87e32708) feat: add Solarflare SFC9000 support * [`0c84090`](https://github.com/siderolabs/pkgs/commit/0c8409060699e20eac8d7123b5213b443d3b7b5e) feat: update releases * [`19cdf71`](https://github.com/siderolabs/pkgs/commit/19cdf71b84363929092b7ad8f2f4a7464abd98fc) feat: enable common sensors * [`acee18e`](https://github.com/siderolabs/pkgs/commit/acee18e8a3cce66a0df47c927cb7fe2b4bc81685) chore: bump kernel to 6.1.54 * [`1d16fd2`](https://github.com/siderolabs/pkgs/commit/1d16fd2e22ce0a444df3df82f8c99a93347698c2) feat: add Chelsio support * [`4504f83`](https://github.com/siderolabs/pkgs/commit/4504f83f668776161af56853c3faec61edc4cdb6) chore: rename kconfig-hardened-check * [`847a9c3`](https://github.com/siderolabs/pkgs/commit/847a9c3bbea9a8c350dc0b2b84d473c2fd23feb2) chore: enable dm thin provisioning * [`1401505`](https://github.com/siderolabs/pkgs/commit/1401505a95eebb1ff9e2baac6239baf822b7576f) chore: drop `-pkgs` for upstream kernel modules * [`a62471d`](https://github.com/siderolabs/pkgs/commit/a62471daea9b4e6f1d7ed03c208a5603096037f3) feat: add binfmt_misc support * [`518c441`](https://github.com/siderolabs/pkgs/commit/518c441851a434e72939a3f27e8dfb64e3360bb6) feat: add gVNIC support * [`7d9e60e`](https://github.com/siderolabs/pkgs/commit/7d9e60e33fb602c81c61112f3557808a7064bb9a) feat: update Go to 1.21.1 * [`d3d7d29`](https://github.com/siderolabs/pkgs/commit/d3d7d295221d951e8f8bb3935f04392f9efe0e1f) chore: bump deps * [`3b70656`](https://github.com/siderolabs/pkgs/commit/3b70656344332f553e0ae16e8bb39e1c3d92287a) chore: fix cacert perms * [`cca80b7`](https://github.com/siderolabs/pkgs/commit/cca80b7b939a2e5eb4769cc9e84d471bc4a6aec1) feat: update Linux to 6.1.46 * [`2e1c0b9`](https://github.com/siderolabs/pkgs/commit/2e1c0b912b2bcde35f04d63fe6840d5e4dc74d60) fix: nonfree kmod pkg name * [`cff5beb`](https://github.com/siderolabs/pkgs/commit/cff5bebf2f23ab02591ca1d72a87208d94328ab4) feat: add btrfs support * [`7717b7e`](https://github.com/siderolabs/pkgs/commit/7717b7e01c4c7170c7a6dcfaf74513585f40b14c) chore: bump deps * [`2f19f18`](https://github.com/siderolabs/pkgs/commit/2f19f18d145096766dea3c592c28e62f08113b38) feat: update containerd to 1.6.23 * [`30d4b74`](https://github.com/siderolabs/pkgs/commit/30d4b743f49396d62dc3ffadcf25511cf891e964) feat: update Go to 1.21 * [`eda123d`](https://github.com/siderolabs/pkgs/commit/eda123ddbd7ea5682ffe62164c41daf8ba531416) feat: update runc to 1.1.9 * [`30cd584`](https://github.com/siderolabs/pkgs/commit/30cd5846bd7a9cbf5e79c23b9e42a65a213276e2) chore: enable pushing of non-free packages * [`fb247b5`](https://github.com/siderolabs/pkgs/commit/fb247b5dcc465b6d77248b544465f582a0dd6e6c) chore: update kernel and microcode

### Changes from siderolabs/tools
6 commits

* [`9c09b00`](https://github.com/siderolabs/tools/commit/9c09b00ded2aa843a2142bc6659cdcade607c566) feat: update dependencies * [`35948af`](https://github.com/siderolabs/tools/commit/35948af8c3f955d02900c9dcd76f1c9e33502f52) feat: update Go to 1.21.3 * [`09023c1`](https://github.com/siderolabs/tools/commit/09023c1d6eec46f43ccdba3bd703d0d3ac72220e) feat: update OpenSSL to 3.1.3 * [`7fa8bb5`](https://github.com/siderolabs/tools/commit/7fa8bb542cb2984992e002ccceb0d655d336be96) feat: update releases * [`fa388de`](https://github.com/siderolabs/tools/commit/fa388de914cc3efd54a23ad2a650437e600fbb09) feat: update Go to 1.21.1 * [`33fb4b3`](https://github.com/siderolabs/tools/commit/33fb4b35661b12fcf023ec96746e04281cc8c911) feat: update Go to 1.21

### Dependency Changes * **github.com/aws/aws-sdk-go-v2/config** v1.18.32 -> v1.19.0 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.13.7 -> v1.13.13 * **github.com/aws/smithy-go** v1.14.0 -> v1.15.0 * **github.com/beevik/ntp** v1.2.0 -> v1.3.0 * **github.com/blang/semver/v4** v4.0.0 **_new_** * **github.com/containerd/cgroups/v3** v3.0.2 **_new_** * **github.com/containerd/containerd** v1.6.23 -> v1.7.7 * **github.com/cosi-project/runtime** v0.3.1 -> v0.3.13 * **github.com/distribution/reference** v0.5.0 **_new_** * **github.com/docker/docker** v24.0.5 -> v24.0.6 * **github.com/foxboron/go-uefi** 32187aa193d0 -> 18b9ba9cd4c3 * **github.com/google/go-cmp** v0.5.9 -> v0.6.0 * **github.com/google/go-containerregistry** v0.15.2 -> v0.16.1 * **github.com/google/uuid** v1.3.0 -> v1.3.1 * **github.com/gopacket/gopacket** v1.1.1 -> 4769cf270e9e * **github.com/hetznercloud/hcloud-go/v2** v2.0.0 -> v2.4.0 * **github.com/insomniacslk/dhcp** 0f9eb93a696c -> 6a2c8fbdcc1c * **github.com/jsimonetti/rtnetlink** v1.3.4 -> v1.3.5 * **github.com/opencontainers/runtime-spec** 1c3f411f0417 -> v1.1.0-rc.1 * **github.com/prometheus/procfs** v0.11.1 -> v0.12.0 * **github.com/rivo/tview** 6cc0565babaf -> 6c844bdc5f7a * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.20 -> v1.0.0-beta.21 * **github.com/siderolabs/extras** v1.5.0 -> v1.6.0-alpha.0-2-g6d48418 * **github.com/siderolabs/gen** v0.4.5 -> v0.4.7 * **github.com/siderolabs/go-kubernetes** v0.2.2 -> v0.2.6 * **github.com/siderolabs/pkgs** v1.5.0-6-g2f2c9cd -> v1.6.0-alpha.0-23-g2e892fd * **github.com/siderolabs/talos/pkg/machinery** v1.5.0 -> v1.6.0-alpha.0 * **github.com/siderolabs/tools** v1.5.0 -> v1.6.0-alpha.0-5-g9c09b00 * **go.uber.org/zap** v1.25.0 -> v1.26.0 * **go4.org/netipx** ec4c8b891b28 -> 6213f710f925 * **golang.org/x/net** v0.13.0 -> v0.17.0 * **golang.org/x/sync** v0.3.0 -> v0.4.0 * **golang.org/x/sys** v0.10.0 -> v0.13.0 * **golang.org/x/term** v0.10.0 -> v0.13.0 * **golang.org/x/text** v0.11.0 -> v0.13.0 * **google.golang.org/grpc** v1.57.0 -> v1.58.3 * **k8s.io/api** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/apimachinery** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/apiserver** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/client-go** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/component-base** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/cri-api** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/kubectl** v0.28.0 -> v0.29.0-alpha.2 * **k8s.io/kubelet** v0.28.0 -> v0.29.0-alpha.2 Previous release can be found at [v1.5.0](https://github.com/siderolabs/talos/releases/tag/v1.5.0) ## [Talos 1.6.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.6.0-alpha.0) (2023-08-24) Welcome to the v1.6.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### talosctl CLI The command `images` deprecated in Talos 1.5 was removed, please use `talosctl images default` instead. ### Component Updates Linux: 6.1.46 Talos is built with Go 1.21. ### Contributors * Andrey Smirnov * Noel Georgi * Andrey Smirnov * Dmitriy Matrenichev * Artem Chernyshev * Christian Rolland * Enno Boland * Henry Sachs * Jared Davenport * Nico Berlee * Sascha Desch * Tim Jones * Utku Ozdemir ### Changes
48 commits

* [`74c07ed71`](https://github.com/siderolabs/talos/commit/74c07ed714d5751336e8745977caa3dca5060d7d) chore: update Go to 1.21 * [`a28d72e9c`](https://github.com/siderolabs/talos/commit/a28d72e9c262bd8fb84959ede952542a6e95d0be) fix: ova contents to be named `disk.*` * [`c0ea4d7ba`](https://github.com/siderolabs/talos/commit/c0ea4d7ba504dd8e1558f11e0cddd41dbf8bc720) fix: properly calculate overal of node address with subnet filters * [`d6b2719e2`](https://github.com/siderolabs/talos/commit/d6b2719e2e824cf5df9314523e3a4138b404e615) chore: drone: move extensions step to a function * [`9608ef56d`](https://github.com/siderolabs/talos/commit/9608ef56dc602636da1449ff05d237e0e20e5154) chore: allow bridge traffic with DHCP broadcast traffic * [`c99316457`](https://github.com/siderolabs/talos/commit/c993164576453fd03eb8fc517badd7de8004f4ad) docs: fix the installing system extensions doc * [`833895940`](https://github.com/siderolabs/talos/commit/833895940b173e247816751ca7287ccde7a36d03) chore: add tests for zfs extension * [`cb468c41c`](https://github.com/siderolabs/talos/commit/cb468c41cbbec6cd5f28c3cd3457aa4a30b81d4c) fix: copy proper modules to arm64 squashfs * [`ea0d6e8c6`](https://github.com/siderolabs/talos/commit/ea0d6e8c6a8ce8cd516bc05c99534241dff60b9f) fix: prevent dashboard crashes when process info is not available * [`e9077a6fb`](https://github.com/siderolabs/talos/commit/e9077a6fb9db5bcadea342200f057c1dc6ffb9af) feat: filter the hostname to produce nodename * [`dc8361c1d`](https://github.com/siderolabs/talos/commit/dc8361c1d524e3a52dfa18ee1b539fb81a02ef8d) fix: properly GC images supplied with both tag and digest * [`ccfa8de11`](https://github.com/siderolabs/talos/commit/ccfa8de1174b4e5d59c2f92b44d8dd65235b590a) fix: automatically change `rpi_4` board on upgrade * [`b56e8b7d9`](https://github.com/siderolabs/talos/commit/b56e8b7d9babe9a963b1fc9a2f41882d08fbafe3) fix: support 'List' type manifests * [`574d48e54`](https://github.com/siderolabs/talos/commit/574d48e54020b02f74c2aeadca1c10499bf967b0) fix: use image digest when starting a container * [`175747cea`](https://github.com/siderolabs/talos/commit/175747cea58d73f8532c114b7754668d24ab9c92) fix: ntp query error with bare IPv6 address * [`c8b507fb2`](https://github.com/siderolabs/talos/commit/c8b507fb26ca30cf0aa98c8cf669a2a03583fc1c) docs: fix kubeprism typo * [`0cdcb2e0e`](https://github.com/siderolabs/talos/commit/0cdcb2e0e8131510aab654211d3622fb17f8375e) docs: restructure docs for nvidia drivers for v1.4 * [`676db9768`](https://github.com/siderolabs/talos/commit/676db9768433027ebc6ff22a0414692ccec2ccf4) docs: fork docs for Talos 1.6 * [`92ad18c18`](https://github.com/siderolabs/talos/commit/92ad18c18fae5ac073cdd98d24c5aeb5edb4091a) fix: write correct capacity to the ovf * [`6b0373ebe`](https://github.com/siderolabs/talos/commit/6b0373ebef88600571ec54c189fd6ea3b0c777e8) chore: move bash tests to integration * [`52b3d8d37`](https://github.com/siderolabs/talos/commit/52b3d8d37cd1cf4eb3aa046781f105a1c39e69a0) docs: make Talos 1.5 documentation the default one * [`dc873df9b`](https://github.com/siderolabs/talos/commit/dc873df9b4cf169b4f7789690b80ac1e02b27d57) chore: fix the filenames of openstack images * [`b5c0e7b24`](https://github.com/siderolabs/talos/commit/b5c0e7b24cbd1546304ca33328b89e022e6e0675) docs: update nvidia docs * [`9606e871e`](https://github.com/siderolabs/talos/commit/9606e871e422b72aaef39ae03e334119602b8f31) docs: update Jiva Pod Security Policy * [`a86ed4362`](https://github.com/siderolabs/talos/commit/a86ed4362c009c389766ecd4bfcbc0ade999bb2e) chore: update Kubernetes Go modules to 0.28.0 * [`97b4e3e91`](https://github.com/siderolabs/talos/commit/97b4e3e91cb4a238a8f81c8ce2983c0033a355cb) feat: update Kubernetes to 1.28.0 * [`79ca1a3df`](https://github.com/siderolabs/talos/commit/79ca1a3dfb485fc5180bda38ab58a2d4c595a6aa) feat: e2e-aws using tf code * [`bf3a5e011`](https://github.com/siderolabs/talos/commit/bf3a5e01190e1cf80769343cf94af4c1bfb80318) chore: add version compatibility for Talos 1.6 * [`969e8097c`](https://github.com/siderolabs/talos/commit/969e8097ce062197c9011d206cdbc7de1dc87df5) feat: update Kubernetes to 1.28.0-rc.1 * [`ca41b611e`](https://github.com/siderolabs/talos/commit/ca41b611e97a0ef5020f01011267b82a155d136a) chore: drone jsonnet cleanup * [`bc198e98e`](https://github.com/siderolabs/talos/commit/bc198e98ef6dd03e07d75ab2eb8b944d10ad3739) docs: retain cilium autoMount pending upstream hostPath fix * [`86c94eff8`](https://github.com/siderolabs/talos/commit/86c94eff8d9e1abec11039f79dc6a9b35d46c7f3) refactor: docgen and config examples * [`ee6d639f6`](https://github.com/siderolabs/talos/commit/ee6d639f6c374cf8e1843dd3720047fea7dd3325) fix: match routes on the priority properly * [`bff0d8f32`](https://github.com/siderolabs/talos/commit/bff0d8f32c55d0cec9aed67592a6ccad8e5efee8) chore: fix dependencies in the release pipeline * [`e1b288679`](https://github.com/siderolabs/talos/commit/e1b288679e922fa0e255273adf4b7a1226518424) refactor: compile regex in validation method on the first use * [`daa4c185a`](https://github.com/siderolabs/talos/commit/daa4c185ae9a6318d779f45c730ac695e14ca6c7) docs: add what's new and documentation for Talos 1.5 * [`c4a1ca8d6`](https://github.com/siderolabs/talos/commit/c4a1ca8d61fcb1338da1ca223b9b4349a6af76e2) chore: remove <-errCh where possible in grpc methods * [`e0f383598`](https://github.com/siderolabs/talos/commit/e0f383598e2f285c04264e9a3787fcdcd56add85) chore: clean up the output of the `imager` * [`fb536af4d`](https://github.com/siderolabs/talos/commit/fb536af4d1804b8b802a4211739ac410fd34bb93) chore: optimize memory usage of `tcell` library on init * [`7c86a365e`](https://github.com/siderolabs/talos/commit/7c86a365e2691065e5e06a4789621bc9f43f3c4b) chore: publish systemd-boot and systemd-stub assets * [`7d688ccfe`](https://github.com/siderolabs/talos/commit/7d688ccfeb00ca46999b98512e49ac94f17d2693) fix: make encryption config provider default to `luks2` if not set * [`80238a05a`](https://github.com/siderolabs/talos/commit/80238a05a6f83b2d8bf3b04816d2b0a5c499eca8) chore: unify semver under `github.com/blang/semver/v4` * [`0f1920bdd`](https://github.com/siderolabs/talos/commit/0f1920bdda5b7f2e2291e75d14453cf81a1b6cd6) chore: provide a resource to peek into Linux clock adjustments * [`4eab3017b`](https://github.com/siderolabs/talos/commit/4eab3017b036d3229a6fa7dc9612050d1499e2b6) fix: calculate log2i properly * [`bcf284530`](https://github.com/siderolabs/talos/commit/bcf2845307ad2c4395967cbb8e756d6a0d8caf2c) fix: update providerid prefix for aws * [`ac2aff5cc`](https://github.com/siderolabs/talos/commit/ac2aff5cc5e5234fecf1f49b0f5d583c633aafa4) fix: fix azure portion of cloud uploader * [`793dcedc9`](https://github.com/siderolabs/talos/commit/793dcedc957389c9d91da62517a43968bd99b09d) fix: fast-wipe the system disk on talosctl reset * [`76fa45afb`](https://github.com/siderolabs/talos/commit/76fa45afbac5d212faa534047255c0256e78d08a) docs: update cilium instructions

### Changes from siderolabs/pkgs
8 commits

* [`2e1c0b9`](https://github.com/siderolabs/pkgs/commit/2e1c0b912b2bcde35f04d63fe6840d5e4dc74d60) fix: nonfree kmod pkg name * [`cff5beb`](https://github.com/siderolabs/pkgs/commit/cff5bebf2f23ab02591ca1d72a87208d94328ab4) feat: add btrfs support * [`7717b7e`](https://github.com/siderolabs/pkgs/commit/7717b7e01c4c7170c7a6dcfaf74513585f40b14c) chore: bump deps * [`2f19f18`](https://github.com/siderolabs/pkgs/commit/2f19f18d145096766dea3c592c28e62f08113b38) feat: update containerd to 1.6.23 * [`30d4b74`](https://github.com/siderolabs/pkgs/commit/30d4b743f49396d62dc3ffadcf25511cf891e964) feat: update Go to 1.21 * [`eda123d`](https://github.com/siderolabs/pkgs/commit/eda123ddbd7ea5682ffe62164c41daf8ba531416) feat: update runc to 1.1.9 * [`30cd584`](https://github.com/siderolabs/pkgs/commit/30cd5846bd7a9cbf5e79c23b9e42a65a213276e2) chore: enable pushing of non-free packages * [`fb247b5`](https://github.com/siderolabs/pkgs/commit/fb247b5dcc465b6d77248b544465f582a0dd6e6c) chore: update kernel and microcode

### Changes from siderolabs/tools
1 commit

* [`33fb4b3`](https://github.com/siderolabs/tools/commit/33fb4b35661b12fcf023ec96746e04281cc8c911) feat: update Go to 1.21

### Dependency Changes * **github.com/aws/aws-sdk-go-v2/config** v1.18.32 -> v1.18.36 * **github.com/aws/aws-sdk-go-v2/feature/ec2/imds** v1.13.7 -> v1.13.11 * **github.com/aws/smithy-go** v1.14.0 -> v1.14.2 * **github.com/beevik/ntp** v1.2.0 -> v1.3.0 * **github.com/blang/semver/v4** v4.0.0 **_new_** * **github.com/containerd/containerd** v1.6.23 -> v1.6.22 * **github.com/foxboron/go-uefi** 32187aa193d0 -> 18b9ba9cd4c3 * **github.com/google/go-containerregistry** v0.15.2 -> v0.16.1 * **github.com/google/uuid** v1.3.0 -> v1.3.1 * **github.com/hetznercloud/hcloud-go/v2** v2.0.0 -> v2.1.1 * **github.com/insomniacslk/dhcp** 0f9eb93a696c -> b3ca2534940d * **github.com/jsimonetti/rtnetlink** v1.3.4 -> v1.3.5 * **github.com/rivo/tview** 6cc0565babaf -> ccc2c8119703 * **github.com/siderolabs/pkgs** v1.5.0-6-g2f2c9cd -> v1.6.0-alpha.0-7-g2e1c0b9 * **github.com/siderolabs/talos/pkg/machinery** v1.5.0 -> v1.5.0-alpha.3 * **github.com/siderolabs/tools** v1.5.0 -> v1.6.0-alpha.0 * **golang.org/x/net** v0.13.0 -> v0.14.0 * **golang.org/x/sys** v0.10.0 -> v0.11.0 * **golang.org/x/term** v0.10.0 -> v0.11.0 * **golang.org/x/text** v0.11.0 -> v0.12.0 Previous release can be found at [v1.5.0](https://github.com/siderolabs/talos/releases/tag/v1.5.0) ## [Talos 1.5.0-alpha.3](https://github.com/siderolabs/talos/releases/tag/v1.5.0-alpha.3) (2023-07-25) Welcome to the v1.5.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Extension Services Talos now supports setting `environmentFile` for an extension service container spec. Refer: https://www.talos.dev/v1.5/advanced/extension-services/#container The extension waits for the file to be present before starting the service. ### Predictable Network Interface Names Starting with version Talos 1.5, network interfaces are renamed to [predictable names](https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/) same way as `systemd` does that in other Linux distributions. The naming schema `enx78e7d1ea46da` (based on MAC addresses) is enabled by default, the order of interface naming decisions is: * firmware/BIOS provided index numbers for on-board devices (example: `eno1`) * firmware/BIOS provided PCI Express hotplug slot index numbers (example: `ens1`) * physical/geographical location of the connector of the hardware (example: `enp2s0`) * interfaces's MAC address (example: `enx78e7d1ea46da`) The predictable network interface names features can be disabled by specifying `net.ifnames=0` in the kernel command line. Talos automatically adds the `net.ifnames=0` kernel argument when upgrading from Talos versions before 1.5. This change doesn't affect "cloud" platforms, like AWS, as Talos automatically adds `net.ifnames=0` to the kernel command line. ### Network KMS Disk Encryption Talos now supports new type of encryption keys which are sealed/unsealed with an external KMS server: ``` systemDiskEncryption: ephemeral: keys: - kms: endpoint: https://1.2.3.4:443 slot: 0 ``` gRPC API definitions and a simple reference implementation of the KMS server can be found in this [repository](https://github.com/siderolabs/kms-client/blob/main/cmd/kms-server/main.go). ### KubePrism - Kubernetes API Server In-Cluster Load Balancer Talos now supports configuring the KubePrism - Kubernetes API Server in-cluster load balancer with machine config `features.kubePrism.port` and `features.kubePrism.enabled` fields. If enabled, KubePrism binds to `localhost` and runs on the same port on every machine in the cluster. The default value for KubePrism endpoint is https://localhost:7445. The KubePrism is used by the `kubelet`, `kube-scheduler`, `kube-controller-manager` and `kube-proxy` by default and can be passed to the CNIs like Cilium and Calico. The KubePrism provides access to the Kubernetes API endpoint even if the external loadbalancer is not healthy, provided that the worker nodes can reach to the controlplane machine addresses directly. ### Machine Config option `.machine.install.bootloader` The `.machine.install.bootloader` option in the machine config is deprecated and will be removed in Talos 1.6. This was a no-op for a long time. The bootloader is always installed. ### XFS Quota Talos 1.5+ enables XFS project quota support by default, also enabling by default kubelet feature gate `LocalStorageCapacityIsolationFSQuotaMonitoring` to use xfs quotas to monitor volume usage instead of `du`. This feature is controlled by the `.machine.features.diskQuotaSupport` field in the machine config, it is set to true for new clusters. When upgrading from a previous version, the feature can be enabled by setting the field to true. On the first mount of a volume, the quota information will be recalculated, which may take some time. ### RDMA/RoCE support Talos no longer loads by default `rdma_rxe` Linux driver, which is required for RoCE support. If the driver is required, it can be enabled by specifying `rdma_rxe` in the `.machine.kernel.modules` field in the machine config. ### SecureBoot Talos now supports generating a custom iso that can be used with SecureBoot. Key generation and enrolling has to be done manually. ### `talosctl image` Command A new set of commands was introduced to manage container images in the CRI: * `talosctl image list` shows list of available images * `talosctl image pull` allows to pre-pull an image into the CRI Both new commands accept `--namespace` flag with two possible values: * `cri` (default): images managed by the CRI (Kubernetes workloads) * `system`: images managed by Talos (`etcd` and `kubelet`) ``` ### `talosctl images` Command The command `talosctl images` was renamed to `talosctl image default`. The backward-compatible alias is kept in Talos 1.5, but it will be dropped in Talos 1.6. ### TPM Disk Encryption Talos now supports encrypting STATE/EPHEMERAL with keys bound to a TPM device. The TPM device must be TPM2.0 compatible. This is ideally supported when booting with new Talos SecureBoot UKI ISOs/Metal images. This feature would still work if SecureBoot is not enabled for UKI images, but not recommended since there is no way to verify the trust of the bootloader. Example machine config: ``` systemDiskEncryption: ephemeral: keys: - slot: 0 tpm: {} state: keys: - slot: 0 tpm: {} ``` ### Component Updates * Linux: 6.1.39 * containerd: 1.6.21 * runc: 1.1.8 * etcd: 3.5.9 * Kubernetes: 1.28.0-beta.0 * Flannel: 0.22.0 Talos is built with Go 1.20.6. ### `talosctl upgrade-k8s` Image Pre-pulling The command `talosctl upgrade-k8s` now by default pre-pulls images for Kubernetes controlplane components and kubelet. This provides an early check for missing images, and minimizes downtime during Kubernetes rolling component update. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Artem Chernyshev * Christian Rolland * Steve Francis * Nanfei Chen * Nico Berlee * Spencer Smith * Alex Corcoles * Alex Corcoles * Alex Lubbock * Andrei Kvapil * Artem Chernyshev * Budiman Jojo * Chris Hoffman * DJAlPee * Dennis Marttinen * Eirik Askheim * Florian Klink * Henk Kraal * Igor Rzegocki * James Callahan * LukasAuerbeck * Markus Reiter * Michael A. Davis * Michael Fornaro * Niklas Wik * Piotr Maksymiuk * Ricky Sadowski * Roee Klinger * Sacha Trémoureux * Scott Cariss * Serge Logvinov * Thomas Lemarchand * Thomas Perronin * Tim Jones * Victor Bajada * Walt Chen * bdronneau ### Changes
194 commits

* [`d2f64af86`](https://github.com/siderolabs/talos/commit/d2f64af863e14b1d111bbeeaa6d9077aadaf6085) chore: disable cloud-images, pull in new kernel and gre module * [`8edce4906`](https://github.com/siderolabs/talos/commit/8edce490639c213cd8c45989a5a87e3388179d37) docs: improve proxmox install guide * [`c783458be`](https://github.com/siderolabs/talos/commit/c783458be0c90b779bcc2fe3c10e37fd3dfe01db) docs: typo dhcp -> dhcp * [`003cbd161`](https://github.com/siderolabs/talos/commit/003cbd161196375edc8ee5033be62014eb88202e) docs: warn about secretboxEncryptionSecret in kubeadm migration guide * [`786e86f5b`](https://github.com/siderolabs/talos/commit/786e86f5b8219ef37c5c6480d97c440cbbd53e30) refactor: rewrite the way Talos acquires the machine configuration * [`5e13cafe5`](https://github.com/siderolabs/talos/commit/5e13cafe5b506039fdd652372b1d1f71a1c1c10b) feat: enforce kernel lockdown for UKI * [`4d96d642f`](https://github.com/siderolabs/talos/commit/4d96d642fd9c462db2c084afee1428009d454a9e) feat: update default Kubernetes version to 1.28.0-beta.0 * [`170a73e16`](https://github.com/siderolabs/talos/commit/170a73e161eacb0e21ce95e7a7e406533552bf1d) chore: support creating qemu guest socket * [`59ac38a6b`](https://github.com/siderolabs/talos/commit/59ac38a6bffe943fa634b124b8ec2a907f95a006) docs: add docs for installing azure ccm and csi * [`6288cd970`](https://github.com/siderolabs/talos/commit/6288cd970e83f18e8b1cadca777deabe3ff9fc91) release(v1.5.0-alpha.2): prepare release * [`60c304126`](https://github.com/siderolabs/talos/commit/60c304126fce95fd4995c416e7757f85505b90fb) chore: bump dependencies * [`9ef4e5efc`](https://github.com/siderolabs/talos/commit/9ef4e5efca4b537a550a5e902fc2479ebb5e53e3) fix: log explicitly when kubelet has no nodeIP match * [`6b39c6a4d`](https://github.com/siderolabs/talos/commit/6b39c6a4d326752f92d98388bbb418f2e50d3ddb) fix: enable compression and bump gRPC max msg size * [`2f2eca861`](https://github.com/siderolabs/talos/commit/2f2eca86175fe98b3bf491f38ff907599333b139) chore: basic support for shutdown/poweroff flags * [`b84277d7d`](https://github.com/siderolabs/talos/commit/b84277d7dc50b196b7cd27e7f2ceff6bf8f58a8d) docs: fix wrong capability name * [`59d7d9344`](https://github.com/siderolabs/talos/commit/59d7d9344b27529af420ec31c7b599027cda044f) chore: use machined for `shutdown`, `poweroff` * [`2439bfb71`](https://github.com/siderolabs/talos/commit/2439bfb719d9f50107cee500d03c90bd50649e05) chore: explicitly add timestamps to machined logs * [`14966e718`](https://github.com/siderolabs/talos/commit/14966e718a07906ff389ecdda063fd16b22baab9) fix: skip over tpm2 1.2 devices * [`6716e7bc0`](https://github.com/siderolabs/talos/commit/6716e7bc0ba6da31b8bc19aa4bd5edb7749b39a1) docs: update cilium documentation about KubePrism usage * [`166d75fe8`](https://github.com/siderolabs/talos/commit/166d75fe888d334349f57dcf405b6867ca5305e2) fix: tpm2 encrypt/decrypt flow * [`130518de7`](https://github.com/siderolabs/talos/commit/130518de71ae96cdf7d733a35e4c306940e1b845) chore: change missing renames of KubePrism * [`5f34f5b41`](https://github.com/siderolabs/talos/commit/5f34f5b41f03d6d455d7b843084d2951c365a7ee) chore: rename api load balancer to KubePrism * [`c8b7095c0`](https://github.com/siderolabs/talos/commit/c8b7095c01f597cd8b41964b42aa7e35c85ae307) refactor: use tpm2 library to calculate policy hash * [`078aac92e`](https://github.com/siderolabs/talos/commit/078aac92ee30c9666235219d4623b82d66362d4d) chore: bump deps * [`53873b844`](https://github.com/siderolabs/talos/commit/53873b8444acaa97d85c50caec625b9dbfdfef93) refactor: move ukify into Talos code * [`d5f6fb9ff`](https://github.com/siderolabs/talos/commit/d5f6fb9ff2980df03365719d9e2690cb5ac788af) chore: add vendor info * [`79365d9ba`](https://github.com/siderolabs/talos/commit/79365d9bacf0e8a6660cdc6b7172c79edf5f3ba3) feat: tpm2 based disk encryption * [`06369e819`](https://github.com/siderolabs/talos/commit/06369e8195e76f96d232d077efb2bfb059b7aa96) fix: retry CRI pod removal, fix upgrade flow in the tests * [`d32dd3a82`](https://github.com/siderolabs/talos/commit/d32dd3a820b07d58ca89c4226c986d87ff0e2b65) chore: update Go to 1.20.6 * [`8017afb10`](https://github.com/siderolabs/talos/commit/8017afb107b901a8785bccaac65d63f34e506568) feat: implement CRI image management and pre-pull on K8s upgrade * [`1c2f19b36`](https://github.com/siderolabs/talos/commit/1c2f19b367af8b04fc49174540e5b141f4b34156) feat: update Kubernetes to 1.28.0-alpha.4 * [`94e9891c1`](https://github.com/siderolabs/talos/commit/94e9891c1bb44a1e7c285b4ccf1fad59ea05aa62) chore: bump sd-boot to v254-rc1 * [`936111ce0`](https://github.com/siderolabs/talos/commit/936111ce062d23ed11b30ea35585c0519260f9c5) fix: properly set up tls for KMS endpoint * [`cb226eec4`](https://github.com/siderolabs/talos/commit/cb226eec46b59372c684c3946e0ba0910066573d) fix: rewrite encryption system information flow * [`3206db528`](https://github.com/siderolabs/talos/commit/3206db52895416d1eb936caa4e953312b34b8549) feat: drop tpm simulator for ukify measure * [`bd4f89f63`](https://github.com/siderolabs/talos/commit/bd4f89f6338423a79b7ce89bda1bd6704caaae59) fix: disable dashboard on Azure, GCP and Scaleway * [`bdb96189f`](https://github.com/siderolabs/talos/commit/bdb96189faadc48e93146f9fd7b03e006bf1dd75) refactor: make maintenance service controller-based * [`d23d04de2`](https://github.com/siderolabs/talos/commit/d23d04de2a5dee30ccf21efe767daf229de78bdb) feat: seed the kernel random pool from the TPM * [`c81ce8cfb`](https://github.com/siderolabs/talos/commit/c81ce8cfb0bc7df66ffd1e1819b64dad6357d890) feat: support controlplane resources configuration * [`74de562b2`](https://github.com/siderolabs/talos/commit/74de562b29c748fda3140871ea3fab99698341ef) fix: mount hugepages with nosuid + nodev * [`ce63abb21`](https://github.com/siderolabs/talos/commit/ce63abb219a2fd4a9d3fdd93a13c343af123efc2) feat: add KMS assisted encryption key handler * [`dafbe9deb`](https://github.com/siderolabs/talos/commit/dafbe9debdee2b015ed574ac4f5f722bce997b31) chore: optimize dockerfile instructions * [`a4289e870`](https://github.com/siderolabs/talos/commit/a4289e8703d9f9e52b739b19b5b38e30a75a1454) chore: fix CLI docs generation stability * [`2fec8388f`](https://github.com/siderolabs/talos/commit/2fec8388fc2fe3058b7b6f141ce9eae2c6a8268f) chore: bump dependencies * [`c1b4262dd`](https://github.com/siderolabs/talos/commit/c1b4262dd60f6cbea6d46a8d0433499bf6365b36) docs: split simple and more complex getting started guides * [`c9a9f9561`](https://github.com/siderolabs/talos/commit/c9a9f95611e38cf5c298f0d9fb0890a9bc0f8b98) refactor: extract secure boot certificate generation * [`6be5a13d5`](https://github.com/siderolabs/talos/commit/6be5a13d5d8341c58d0d2fe75c49ba1de9bf7316) feat: implement machine config documents for event and log streaming * [`e241be85b`](https://github.com/siderolabs/talos/commit/e241be85ba748163268eaeed2a88c8e295f84b28) fix: properly handle YAML comment stripping for multi-doc * [`c02ada7d9`](https://github.com/siderolabs/talos/commit/c02ada7d952255bffe67b3c84f1f832253e1a3b5) fix: capabilities including `ALL` should be uppercase * [`cbdf96d46`](https://github.com/siderolabs/talos/commit/cbdf96d461ec0cf8929c2c76614081ef042dda31) feat: support environment file for extensions * [`35d6adcb9`](https://github.com/siderolabs/talos/commit/35d6adcb9ad7e9420a5bcdfcf3378a05c0b65d46) fix: provide stashed META values before installation * [`258f07449`](https://github.com/siderolabs/talos/commit/258f07449050d69c369fdc71ac613a1a225807bf) fix: ukify cert generation * [`bf3febb7e`](https://github.com/siderolabs/talos/commit/bf3febb7e2bf3ebf1bd66ee088f3885a178c953c) fix: refine OVMF search paths * [`fbebc17f8`](https://github.com/siderolabs/talos/commit/fbebc17f8be7a3ca6c45c3c84d306e52c47d441d) fix: disable LVM backups/archive * [`e5306ef26`](https://github.com/siderolabs/talos/commit/e5306ef2637dd2eb7464691b55159a43933c7419) chore: format and cleanup test scripts * [`bc371ecfd`](https://github.com/siderolabs/talos/commit/bc371ecfdafe51f8cf34461caf9e6f51c0a93108) chore: add `/sbin/shutdown` * [`0d313b973`](https://github.com/siderolabs/talos/commit/0d313b973367906b2fd4bcad4b2def79344dbd67) feat: add `reboot-mode` flag to `talosctl upgrade` * [`7ce87f20c`](https://github.com/siderolabs/talos/commit/7ce87f20c39c615f4d23a3be23780a36008dcb19) fix: compare only basename of `os.Args[0]` in machined * [`53389b1e7`](https://github.com/siderolabs/talos/commit/53389b1e724751e28046167b44f05c6ecf06f184) feat: auto-enroll secure boot keys * [`d77f0bc7b`](https://github.com/siderolabs/talos/commit/d77f0bc7bbe01b7fc8efa21a7c57d73ecb94a01f) docs: fix broken link to powershell module * [`e1b150a11`](https://github.com/siderolabs/talos/commit/e1b150a11014ddd0c60585d320dd7cd556cf2a0c) release(v1.5.0-alpha.1): prepare release * [`8daf432b2`](https://github.com/siderolabs/talos/commit/8daf432b2957a8f9d5c59970cf68e7e8414038f5) chore: bump deps * [`e3f3f5794`](https://github.com/siderolabs/talos/commit/e3f3f5794d276433748d0e677ed8476a54f8a98e) feat: implement revert for sd-boot * [`d8b0903d7`](https://github.com/siderolabs/talos/commit/d8b0903d70181afc901d8ddb71bdfa964d4df2cd) docs: vagrant setup document fix * [`fe0f46980`](https://github.com/siderolabs/talos/commit/fe0f46980f348852907218d6f49581efe4b45d49) feat: implement secure boot from disk * [`445f5ad54`](https://github.com/siderolabs/talos/commit/445f5ad5426b125e29d86ff096695399bd01eb32) feat: support API server load balancer * [`19bc223de`](https://github.com/siderolabs/talos/commit/19bc223de8ad878bffe539bda617d5f861af3cfe) refactor: bootloader interface, labels * [`665702ddd`](https://github.com/siderolabs/talos/commit/665702ddd351e902336e6ab81108ea94d61db5c1) chore: fix cilium e2e tests * [`71a548d18`](https://github.com/siderolabs/talos/commit/71a548d18013ee16394921759e819b0fabb43758) chore: generic boootloader implementation * [`e9dbc9311`](https://github.com/siderolabs/talos/commit/e9dbc9311bcbbbcaab2c7eb7f7128013194c234a) test: bump versions for upgrade tests * [`0a99965ef`](https://github.com/siderolabs/talos/commit/0a99965efbdd5dc0d927eb2cbae209dc143c9541) refactor: replace `uncordonNode` with controllers * [`e858bca3a`](https://github.com/siderolabs/talos/commit/e858bca3a2f75d5035710d52229c8142f3eb6982) test: fix cilium integration tests * [`455328d05`](https://github.com/siderolabs/talos/commit/455328d058fba3a5a8b3358820a02e2b4fabad95) fix: allow time skew for generated kubeconfig * [`3ae05648a`](https://github.com/siderolabs/talos/commit/3ae05648ae0a2f79bebd678f85d63d4e5dafde0a) fix: usage of custom kernels * [`0797b0d16`](https://github.com/siderolabs/talos/commit/0797b0d16808d115649a9e0e37b355bbbc2a30b5) chore: add a pipeline to test cloud-images step without a release * [`e5a36268b`](https://github.com/siderolabs/talos/commit/e5a36268b63e588ea6cd2439bf0de356ee07d752) docs: include `allowSchedulingOnControlPlanes` on `talosctl gen config` output * [`c74d93728`](https://github.com/siderolabs/talos/commit/c74d937280c2ec707936a72d07dc2a5dd252c5d2) chore: bump github.com/cosi-project/runtime * [`dbaf5c699`](https://github.com/siderolabs/talos/commit/dbaf5c69978fd1d22737385ddd096798d408254c) refactor: task `labelControlPlane` into controllers * [`1865a0c29`](https://github.com/siderolabs/talos/commit/1865a0c29663a1a78db7ef6e901d450d67a3cbe1) chore: modify some usages that are not recommended * [`3816318b9`](https://github.com/siderolabs/talos/commit/3816318b9e2e205da0c949c0ec59a087decd0b78) chore: wrap config.Provider in atomic wrapper * [`d04cf1978`](https://github.com/siderolabs/talos/commit/d04cf19788df20c802eadb9678570a4f15d339b2) chore: clean up unnecessary self assignment * [`a34a94898`](https://github.com/siderolabs/talos/commit/a34a948985fed7c3054c4342c48e0e0620569625) fix: copy missing modules.* files * [`f5e3272fc`](https://github.com/siderolabs/talos/commit/f5e3272fce641a878eefa66437d28d3ed9917ab6) refactor: task 'updateBootLoader' as controller * [`e7be6ee7c`](https://github.com/siderolabs/talos/commit/e7be6ee7c3636eebd557d93e440e9749c8093360) refactor: make event log streaming fully reactive * [`aef2192a6`](https://github.com/siderolabs/talos/commit/aef2192a6584e7934086eae0caab6faba52a8ac1) chore: use fixed module list * [`c719aa231`](https://github.com/siderolabs/talos/commit/c719aa2316bffa3b614d27d630ea3d8731684f4e) fix: allow http:// for discovery service URL * [`39134d8d5`](https://github.com/siderolabs/talos/commit/39134d8d5304cec5e1a1c5fe23f62ed957241213) chore: fix cron pipeline * [`a61dcdbbd`](https://github.com/siderolabs/talos/commit/a61dcdbbd5c917b49c810108ff96854ad51269b1) fix: don't load RDMA over Ethernet driver by default * [`aac441f61`](https://github.com/siderolabs/talos/commit/aac441f618ac60f2298d9e17a2044916f7da9d69) chore: update Go to 1.20.5, bump dependencies * [`1c0c7933d`](https://github.com/siderolabs/talos/commit/1c0c7933dfef23544e2fb0fc04c4c5ad7d5b5d9b) chore: cleanup partition code * [`31b988281`](https://github.com/siderolabs/talos/commit/31b988281efb9d0c66975bbfc20b893ad32c161d) docs: add some words about certifcates * [`e912c0dfc`](https://github.com/siderolabs/talos/commit/e912c0dfcf515c5a6c852f4b935c9b48e61b13f1) chore: use go-blockdevice for zeroing partitions * [`e6dde8ffc`](https://github.com/siderolabs/talos/commit/e6dde8ffc50e435a42d11eb96cf6aea2cf3520ca) feat: add network chaos to qemu development environment * [`47986cb79`](https://github.com/siderolabs/talos/commit/47986cb79eb30c6e9c0d091ee37b2b1c2f20885c) chore: unify kexec phase * [`3a865370f`](https://github.com/siderolabs/talos/commit/3a865370f5152243e08a69626de023f924e22689) feat: qemu secureboot * [`5dab45e86`](https://github.com/siderolabs/talos/commit/5dab45e86917837b0991a62ab94a7b96b3ef777e) refactor: allow kmsg log streaming to be reconfigured on the fly * [`8a02ecd4c`](https://github.com/siderolabs/talos/commit/8a02ecd4cb97bcaafe5761d464fec8a4e44b672f) chore: add endpoints balancer controller * [`423a31ac9`](https://github.com/siderolabs/talos/commit/423a31ac9d8f28c2bcf00794bacf5446e43fc0b7) chore: deprectae `bootloader` installer option * [`cdfece7d6`](https://github.com/siderolabs/talos/commit/cdfece7d64a9269afcc213f8d604d0b7e525cb8a) chore: optimize image compression * [`bfc341937`](https://github.com/siderolabs/talos/commit/bfc34193762cb309ef2230f4d79673c4a56f4db5) chore: add default console args * [`2749aeeda`](https://github.com/siderolabs/talos/commit/2749aeeda0451b286369d911696070e2cf4359e9) feat: add support for multi-doc strategic merge patching * [`3f68485e4`](https://github.com/siderolabs/talos/commit/3f68485e44800a0c50b5855531ec10507e7d0df9) feat: add uki iso generation * [`bab484a40`](https://github.com/siderolabs/talos/commit/bab484a405cb598d1c5f35f7602c2ac27e6efa97) feat: use stable network interface names * [`196dfb99b`](https://github.com/siderolabs/talos/commit/196dfb99b0329d5c52fd7089e62fbfa1b09df3c6) fix: do not probe kernel args in dashboard if not needed * [`8c071b579`](https://github.com/siderolabs/talos/commit/8c071b5796db05ecb17e46295eb2140827a58ca8) fix: skip DHCP RENEW if server IP in the lease is all zeroes * [`badbc51e6`](https://github.com/siderolabs/talos/commit/badbc51e63b685e22fffb82ae294a35cd9f65922) refactor: rewrite code to include preliminary support for multi-doc * [`ecce29dee`](https://github.com/siderolabs/talos/commit/ecce29dee9625842e419496e18560291ef90b1b5) fix: upgrade-k8s use internal IP first, external IP fallback * [`3c64a5ffb`](https://github.com/siderolabs/talos/commit/3c64a5ffba2109ccf5102f71652e54def52f8dbf) chore: optimize image generation time * [`2292f36d9`](https://github.com/siderolabs/talos/commit/2292f36d970d3edcf39b5d5f12d0051d7d75f390) chore: registry.k8s.io for coredns image * [`f2b258b37`](https://github.com/siderolabs/talos/commit/f2b258b3733a8fcc34bccde3bf01855a512d519a) docs: document talosctl version for upgrades * [`a0773f783`](https://github.com/siderolabs/talos/commit/a0773f783cfb3cfab8cbbeffb6449159754d785e) chore: add ukify Go script * [`b69e38d1f`](https://github.com/siderolabs/talos/commit/b69e38d1ff069ba8fac7a6524621f8b3c7256238) chore: bump dependencies * [`adce65103`](https://github.com/siderolabs/talos/commit/adce65103424f9f895e6b8c4858b27b3eb6bd74b) docs: add piraeus/drbd to storage documentation * [`a982cabe7`](https://github.com/siderolabs/talos/commit/a982cabe7011c87e863f7bb0829921e927ddf782) docs: link support matrix in k8s update doc * [`1fb29a56a`](https://github.com/siderolabs/talos/commit/1fb29a56a8abe5d72b8a3a336693e798424c63e0) fix: fail quickly if upgrade-k8s is used with multiple nodes * [`51d931c47`](https://github.com/siderolabs/talos/commit/51d931c4705fc7ca0bdadc59d732e56fae318dda) chore: faster dev cycle * [`dc6764871`](https://github.com/siderolabs/talos/commit/dc6764871c9e732b88f7cddc1784e943e9d952bb) refactor: move around config interfaces, make RawV1Alpha1 typed * [`ea9a97dba`](https://github.com/siderolabs/talos/commit/ea9a97dba38c6ab2de830e3b0c3d202d22bdb668) fix: fall back to external IP when discovering nodes in upgrade-k8s * [`0bb7e8a5c`](https://github.com/siderolabs/talos/commit/0bb7e8a5cf8b8f3bf31d9f8c3a85b4153921c126) refactor: split config.Provider into Config & Container * [`85d8a1619`](https://github.com/siderolabs/talos/commit/85d8a1619431989eb05cb15ad01a1bc06b0f63e9) chore: bump deps * [`39b7a56f0`](https://github.com/siderolabs/talos/commit/39b7a56f01d41d33eb96a0feb6e34d43965a99fd) chore: use 8GiB instead of 10GiB for cloud images * [`ff11fd39c`](https://github.com/siderolabs/talos/commit/ff11fd39c723a40c01abe6348f64b1f892856175) fix: race with `udevd` and `mountUserDisks` * [`c3fabb982`](https://github.com/siderolabs/talos/commit/c3fabb9829d12353770d6436a1d726b15820ebce) chore: update default image sizes to 10GB for all "cloud" images * [`10155c390`](https://github.com/siderolabs/talos/commit/10155c390e87898098426600709657fbd51e02e8) feat: enable xfs project quota support, kubelet feature * [`eba818564`](https://github.com/siderolabs/talos/commit/eba81856427dd3f6c0cf317f027e63d65a079029) release(v1.5.0-alpha.0): prepare release * [`383471c3e`](https://github.com/siderolabs/talos/commit/383471c3e956ff6e077a1de75b02a50835fbf352) feat: update default Kubernetes to v1.27.2 * [`8f68d1abe`](https://github.com/siderolabs/talos/commit/8f68d1abeff83c3ff0e6c5d9f61cb14807b44ca5) chore: bump deps * [`e0c1585d3`](https://github.com/siderolabs/talos/commit/e0c1585d3047ef213134331dc57f8e2e8c23a93d) feat: create azure community gallery image version on release * [`dd8336c9e`](https://github.com/siderolabs/talos/commit/dd8336c9ee7f8a3a44d45c9f9e3cbbf741f84c44) fix: refresh kubelet self-issued serving certificates * [`bb02dd263`](https://github.com/siderolabs/talos/commit/bb02dd263cbc5e7e3839148d86a4a0a5f7ea998b) chore: drop deprecated stuff for Talos 1.5 * [`61cad8673`](https://github.com/siderolabs/talos/commit/61cad86731e5c0aa80d7df41ea02d0b7ff579c45) chore: bump deps * [`01dfd3af7`](https://github.com/siderolabs/talos/commit/01dfd3af7d64dacd179d17d9d5eaf4bc44cf72af) feat: update etcd to v3.5.9 * [`aa65fbb8a`](https://github.com/siderolabs/talos/commit/aa65fbb8a1752a70e7bac4e4e9872f35e88d1cc9) chore: update KUBECTL_URL to reflect the community bucket * [`cc3128d94`](https://github.com/siderolabs/talos/commit/cc3128d944abacfb633bc783b7fed6d0a6f80661) chore: bump kernel to 6.1.28 * [`97fffaf78`](https://github.com/siderolabs/talos/commit/97fffaf78a0b9a1dc67709de11d37ea20aefde59) chore: use ctest.UpdateWithConflicts instead of plain UpdateWithConflicts * [`3b36993b9`](https://github.com/siderolabs/talos/commit/3b36993b9926392f4290e6fabc82e635f4c98149) fix: rlimit nofile test * [`45e6e27af`](https://github.com/siderolabs/talos/commit/45e6e27af75746fd0cc8b0f98a2d14579eb0ed40) chore: bump runtime * [`4f720d465`](https://github.com/siderolabs/talos/commit/4f720d46532af39165fc5051052d5c42595d91af) fix: revert: set rlimit explicitly in wrapperd * [`a2565f674`](https://github.com/siderolabs/talos/commit/a2565f67416e9b9bc22f2d5506df9ea7771c0c8c) fix: set rlimit explicitly in wrapperd * [`cdfc242b8`](https://github.com/siderolabs/talos/commit/cdfc242b8354f4cc4e7ce51bbe3a8fb20b35995d) chore: re-enable Go buildid * [`e67f3f5c5`](https://github.com/siderolabs/talos/commit/e67f3f5c5453f947355194ea9656c15ff008c35e) feat: linux 6.1.27, containerd 1.6.21, go 1.20.4 * [`55ae59a0a`](https://github.com/siderolabs/talos/commit/55ae59a0ad71293676b3efed461f5ab98101401a) fix: properly skip/cleanup controlplane configs for workers * [`64eade9bd`](https://github.com/siderolabs/talos/commit/64eade9bde271bce4e629e6ac09407c8c42e01be) chore: clean up unused constant * [`62c6e9655`](https://github.com/siderolabs/talos/commit/62c6e9655cb639d4993aaa4c9b364342688599cb) feat: introduce siderolink config resource & reconnect * [`860002c73`](https://github.com/siderolabs/talos/commit/860002c7352bedd10845e11da37c80685ff0e720) fix: don't reload control plane pods on cert SANs changes * [`d43c61e80`](https://github.com/siderolabs/talos/commit/d43c61e80f5b05b81f2a021cdfe012e500c3d98e) fix: enforce nolock option for all NFS mounts by default * [`339986db9`](https://github.com/siderolabs/talos/commit/339986db9d3675b78ce0d268f799ad654862fb0f) fix: inhibit timer to follow kubelet timer * [`cbf6dc100`](https://github.com/siderolabs/talos/commit/cbf6dc1009ad47a2804774839e4e0301efa8ac78) fix: set timeout for unmount calls * [`b58f913d5`](https://github.com/siderolabs/talos/commit/b58f913d5f4b8ecf39be183d0bafe1109f0f0737) fix: set the static pod priority as values * [`f8a7a5b6b`](https://github.com/siderolabs/talos/commit/f8a7a5b6bf4138a33cbe5c9afe85db99de167aec) docs: add information about KubeSpan ports and topology * [`2bad74d64`](https://github.com/siderolabs/talos/commit/2bad74d6423c083ec34f1b422f23b0024d5f8798) docs: add how to on scaling down * [`7442ff8b0`](https://github.com/siderolabs/talos/commit/7442ff8b095ef1337f54332a71d08053a2832144) chore: fix typos inteface -> interface (docs and tests) * [`d4e94f7a1`](https://github.com/siderolabs/talos/commit/d4e94f7a15acf7f3c9e7532b067cdacd0e805bec) fix: add back required TARGETARCH for installer * [`e6fffda01`](https://github.com/siderolabs/talos/commit/e6fffda01385a2daaa901a5742f30a4edc9186a7) chore: linux 6.1.26, runc 1.1.7 * [`344746ae2`](https://github.com/siderolabs/talos/commit/344746ae2fa038b704d02fec04c3d358762fe938) fix: bump max inhibit delay to 20 min * [`d9bdea2b5`](https://github.com/siderolabs/talos/commit/d9bdea2b54772f067783ee64eb85c834957d386a) chore: fork docs and compatibility modules for Talos 1.5 * [`3d99610fc`](https://github.com/siderolabs/talos/commit/3d99610fc9b0d0084be822be29bb1bf2fbe85833) docs: document building, verifying image and process caps * [`014008ea2`](https://github.com/siderolabs/talos/commit/014008ea25208afbeabb42ef89238802705ad4e0) fix: udevd rules trigger * [`9b36bb613`](https://github.com/siderolabs/talos/commit/9b36bb613b44f182e47ae63bc74e4a8b6342d68d) feat: update Linux to 6.1.25, fix virtio on arm64 * [`08ec66c55`](https://github.com/siderolabs/talos/commit/08ec66c55ccca3f9aa82a9703ebf183913b19a7e) feat: clean up (garbage collect) system images which are not referenced * [`b097efcde`](https://github.com/siderolabs/talos/commit/b097efcde29c20cdc4fed23fe8366bd683db634c) fix: display correct number of machines on dashboard * [`cad43f0ad`](https://github.com/siderolabs/talos/commit/cad43f0ad3bc2ede8a6ae81767c9226b6bc69f19) chore: remove k8s master label * [`e296a566e`](https://github.com/siderolabs/talos/commit/e296a566e6efb0cbdd119e73aff1feaa772d38bd) fix: support kernel userspace module loading * [`103f0ffdd`](https://github.com/siderolabs/talos/commit/103f0ffdd3ebd57a5086852f3502a8a7d4428faa) feat: add startup probes to controller-manager and scheduler * [`5a1ae8aae`](https://github.com/siderolabs/talos/commit/5a1ae8aae89e54d5540586d6f2e99ef3e80a72eb) chore: bump dependences * [`ec8c8dbaf`](https://github.com/siderolabs/talos/commit/ec8c8dbafcdaf63d036bdba92fa153d4d1c90100) chore: fix container image reproducibility * [`f661d8487`](https://github.com/siderolabs/talos/commit/f661d84877e6db5bc8856b982990926dcbfe949c) fix: allow `talosctl cp` to handle special files in `/proc` * [`2d824b563`](https://github.com/siderolabs/talos/commit/2d824b5639a4b8c3b673d13b08b2b97c69aafe0d) fix: do not show control plane status for workers on dashboard * [`e5491ddad`](https://github.com/siderolabs/talos/commit/e5491ddadeb1776bd5c17dd35917e05ec4847d0f) docs: update documentation for nocloud * [`7a004a6f7`](https://github.com/siderolabs/talos/commit/7a004a6f7f47fa5d17e855eb02650754d8411574) fix: parse errors correctly * [`374ef5385`](https://github.com/siderolabs/talos/commit/374ef53853947811dc221d99751cf0e16294508c) test: submit verbose flag to e2e tests * [`e1d38b6fe`](https://github.com/siderolabs/talos/commit/e1d38b6febf26fe31a6b9d6ed8f9b6bdba29aa3b) feat: show template URL in dashboard config URL tab * [`45d7f0ce9`](https://github.com/siderolabs/talos/commit/45d7f0ce95454ce85c403fc493ddb97e4d478238) docs: fix the latest url * [`96efbf147`](https://github.com/siderolabs/talos/commit/96efbf14769579d514ef9c75d01d9f44d276113a) docs: activate 1.4.0 docs by default * [`8c1f515b1`](https://github.com/siderolabs/talos/commit/8c1f515b1b8e40bce42e2fc04755afe5bf8a56aa) feat: update Linux to 6.1.24 * [`8689bef5f`](https://github.com/siderolabs/talos/commit/8689bef5f10839091cf131edb6c8efad4ccba034) docs: update documentation for Talos 1.4 * [`a781dfb8e`](https://github.com/siderolabs/talos/commit/a781dfb8e3ded67edcb2a6a1048bfe76c6bd0d24) feat: update Kubernetes to 1.27.1 * [`a737dd83a`](https://github.com/siderolabs/talos/commit/a737dd83a4cd7549f85f8df0882f1c9a4446060d) chore: typo in `compatibility.ParseKubernetesVersion` * [`f14928b0a`](https://github.com/siderolabs/talos/commit/f14928b0a9dd3d85664605f4f6a206236ea94614) fix: fix dashboard crash when a non-existent node is specified * [`3e406d9b0`](https://github.com/siderolabs/talos/commit/3e406d9b07c0e67a2fb61e612bc3f378f3c35247) feat: update etcd to v3.5.8 * [`bd1cff3e8`](https://github.com/siderolabs/talos/commit/bd1cff3e83530b9b89b27d8083ea8f3f0cf6ede4) chore: remove Go buildid * [`e31f7f50b`](https://github.com/siderolabs/talos/commit/e31f7f50b1b455beb98cd25859a44bbbccc1ff64) feat: update Kubernetes to 1.27.0 * [`aa3640d74`](https://github.com/siderolabs/talos/commit/aa3640d74ce2e3619476453381909fa3520eb87d) docs: update storage.md * [`07bb61e60`](https://github.com/siderolabs/talos/commit/07bb61e60c53b267756dc97874b9c9554f2b1486) chore: module-sig-verify cleanup * [`5e9d836c3`](https://github.com/siderolabs/talos/commit/5e9d836c3d075c3edb2d48b2868c31a1c963e2de) chore: add kernel module signtaure verification * [`3cd1c6bb0`](https://github.com/siderolabs/talos/commit/3cd1c6bb0b83e5747a7356140a44b16deb4727e6) fix: send 'STOP' event on phase end * [`5176d27dc`](https://github.com/siderolabs/talos/commit/5176d27dc566d8689bb305398da7250269ebe9a3) feat: update Kubernetes to 1.27.0-rc.1 * [`2c55550a6`](https://github.com/siderolabs/talos/commit/2c55550a66b49b49d8dc95b83516b7c0f8107300) fix: quote ISO kernel args for GRUB * [`319d76e38`](https://github.com/siderolabs/talos/commit/319d76e38978406d8d37e89ada2c403969d6c972) fix: respect BROWSER=echo in client auth interceptor * [`4e4ace839`](https://github.com/siderolabs/talos/commit/4e4ace839c0f558e7b00979fa4c64c32985aa3ce) chore: update Go to 1.20.3 * [`170f73899`](https://github.com/siderolabs/talos/commit/170f73899a3bf29e9c6f76fdc5e510be08edf4aa) fix: correctly parse static pod phase * [`c3a595d5b`](https://github.com/siderolabs/talos/commit/c3a595d5b7d3c7c3091229caef6b2553416edb56) fix: improve action tracking post checks * [`eb01edbc8`](https://github.com/siderolabs/talos/commit/eb01edbc8a0ef5810693afe450861d5b63877b72) fix: rework DHCP flow * [`e095150a6`](https://github.com/siderolabs/talos/commit/e095150a6e34cbdc805a2cac85ec7f28f98629b4) test: bump CAPI components versions

### Changes since v1.5.0-alpha.2
9 commits

* [`d2f64af86`](https://github.com/siderolabs/talos/commit/d2f64af863e14b1d111bbeeaa6d9077aadaf6085) chore: disable cloud-images, pull in new kernel and gre module * [`8edce4906`](https://github.com/siderolabs/talos/commit/8edce490639c213cd8c45989a5a87e3388179d37) docs: improve proxmox install guide * [`c783458be`](https://github.com/siderolabs/talos/commit/c783458be0c90b779bcc2fe3c10e37fd3dfe01db) docs: typo dhcp -> dhcp * [`003cbd161`](https://github.com/siderolabs/talos/commit/003cbd161196375edc8ee5033be62014eb88202e) docs: warn about secretboxEncryptionSecret in kubeadm migration guide * [`786e86f5b`](https://github.com/siderolabs/talos/commit/786e86f5b8219ef37c5c6480d97c440cbbd53e30) refactor: rewrite the way Talos acquires the machine configuration * [`5e13cafe5`](https://github.com/siderolabs/talos/commit/5e13cafe5b506039fdd652372b1d1f71a1c1c10b) feat: enforce kernel lockdown for UKI * [`4d96d642f`](https://github.com/siderolabs/talos/commit/4d96d642fd9c462db2c084afee1428009d454a9e) feat: update default Kubernetes version to 1.28.0-beta.0 * [`170a73e16`](https://github.com/siderolabs/talos/commit/170a73e161eacb0e21ce95e7a7e406533552bf1d) chore: support creating qemu guest socket * [`59ac38a6b`](https://github.com/siderolabs/talos/commit/59ac38a6bffe943fa634b124b8ec2a907f95a006) docs: add docs for installing azure ccm and csi

### Changes from siderolabs/crypto
2 commits

* [`8f77da3`](https://github.com/siderolabs/crypto/commit/8f77da30a5193d207a6660b562a273a06d73aae0) feat: add a method to load PEM key from file * [`c03ff58`](https://github.com/siderolabs/crypto/commit/c03ff58af5051acb9b56e08377200324a3ea1d5e) feat: add a way to represent redacted x509 private keys

### Changes from siderolabs/discovery-api
1 commit

* [`5e3db3c`](https://github.com/siderolabs/discovery-api/commit/5e3db3c1a656ebdc717494e5384f10c7b11eef0f) chore: app optional ControlPlane data

### Changes from siderolabs/discovery-client
1 commit

* [`9ba5f03`](https://github.com/siderolabs/discovery-client/commit/9ba5f033a47d41448153962c5fe22db2d9a8a00c) chore: app optional ControlPlane data

### Changes from siderolabs/extras
3 commits

* [`f415aac`](https://github.com/siderolabs/extras/commit/f415aac20c245592612a02157d247cb2dd4a5d45) feat: update Go to 1.20.6 * [`a73d524`](https://github.com/siderolabs/extras/commit/a73d5243f443fd32376780bf2a4f97b08f28917c) feat: update Go to 1.20.5 * [`36c8ac4`](https://github.com/siderolabs/extras/commit/36c8ac4ab98300059acaad501c2adc8abd39179f) chore: update to Go 1.20.3

### Changes from siderolabs/gen
3 commits

* [`f9f5805`](https://github.com/siderolabs/gen/commit/f9f5805973d30fe6bbac2f4a79ad4197fe59970e) chore: bump rekres and add functions from exp * [`b968d21`](https://github.com/siderolabs/gen/commit/b968d21c9671d97e54317f80cdf781d6f963e44b) feat: add `TryRecv` and `RecvWithContext` functions * [`476dfea`](https://github.com/siderolabs/gen/commit/476dfeae70882e1ca6e5cfed3d6e12dc36841a26) feat: add foreach and clear to lazymap

### Changes from siderolabs/go-blockdevice
4 commits

* [`fbb01f7`](https://github.com/siderolabs/go-blockdevice/commit/fbb01f714bdc9c32ea3459345b730b1043ce10c0) fix: properly detect token not found error * [`3e08968`](https://github.com/siderolabs/go-blockdevice/commit/3e089682439e885c6386f833e35728ce54daff44) fix: do not attach token to a key slot * [`f2c419e`](https://github.com/siderolabs/go-blockdevice/commit/f2c419e81dcba3c5be007130f677d2075e2aec3c) feat: support LUKS token management * [`076874a`](https://github.com/siderolabs/go-blockdevice/commit/076874a155ad44d764d25081125f950e8194d023) chore: resolve blockdevice symlinks

### Changes from siderolabs/go-debug
1 commit

* [`43d9100`](https://github.com/siderolabs/go-debug/commit/43d9100eba3a30ff0d7f1bed0058e6631243cc47) chore: allow enabling pprof manually

### Changes from siderolabs/go-kubernetes
2 commits

* [`69fea5b`](https://github.com/siderolabs/go-kubernetes/commit/69fea5b840fb51aa08e5fbf380fa924b9d444094) feat: support upgrades to Kubernetes 1.28 * [`5a3df5b`](https://github.com/siderolabs/go-kubernetes/commit/5a3df5b002d74ba9f4d773dc1278047481b1d4ba) fix: remove removed APIs for 1.27 upgrade

### Changes from siderolabs/go-loadbalancer
6 commits

* [`574126c`](https://github.com/siderolabs/go-loadbalancer/commit/574126cbf0e1e45a06cabaf602e5070dd7d441e2) chore: add 0.1ms tier and fix tiers * [`5301800`](https://github.com/siderolabs/go-loadbalancer/commit/5301800a874e853d97f8e12195558f79c97c0beb) chore: fix logging and tests * [`b23a173`](https://github.com/siderolabs/go-loadbalancer/commit/b23a1733aa9b303bda82175b4f5e9f8a4765a27b) chore: replace std log with zap * [`1a2f374`](https://github.com/siderolabs/go-loadbalancer/commit/1a2f374df7804dffe683e8be90e9829f2dfb5e95) feat: add multi-tier scoring based for generic List * [`56a27da`](https://github.com/siderolabs/go-loadbalancer/commit/56a27da7083139b71898f4f9207dc40088e8c815) chore: move to siderolabs/tcpproxy of inet.af/tcpproxy * [`f3a0e24`](https://github.com/siderolabs/go-loadbalancer/commit/f3a0e2411e08eef9c79876f3dc6e09e770710379) fix: use SO_LINGER option when doing TCP healthchecks

### Changes from siderolabs/kms-client
3 commits

* [`50064b6`](https://github.com/siderolabs/kms-client/commit/50064b67ac73c0a3f6f89c6a44ef914711107df0) fix: pass context to the key handler in the server wrapper * [`83e0a2e`](https://github.com/siderolabs/kms-client/commit/83e0a2ec6b06668940ec31d64491d9b8a630524b) feat: define API and add reference implementation for KMS server * [`8c37ee8`](https://github.com/siderolabs/kms-client/commit/8c37ee83099a6563197c89166b0ea596eebf0598) Initial commit

### Changes from siderolabs/pkgs
41 commits

* [`fedfafa`](https://github.com/siderolabs/pkgs/commit/fedfafa77de930ae7945e16ace61c13568024ac5) feat: add thunderbolt/USB4 module * [`17d5b94`](https://github.com/siderolabs/pkgs/commit/17d5b94cc7b3e9f9c86a9f5080dcc70f095659fe) feat: enable NET_IPGRE kernel config * [`84cdfb6`](https://github.com/siderolabs/pkgs/commit/84cdfb6d270201b166dacdcd928669d028e12deb) feat: add 'zfs' package * [`d0eaedc`](https://github.com/siderolabs/pkgs/commit/d0eaedcb5cd2510925e4609369e25c3e3572d5fe) feat: enable DM_RAID kernel config * [`d5e0fad`](https://github.com/siderolabs/pkgs/commit/d5e0fad0d59dfb8d2386ab2ad6c7df749e0b9413) feat: update dependencies * [`c644633`](https://github.com/siderolabs/pkgs/commit/c644633324ed1e56ab19f146c04ed3984736a88a) feat: enable multi-gen lru by default * [`75696ba`](https://github.com/siderolabs/pkgs/commit/75696ba81581ef0f1af668db565a08950145e45d) feat: update Go to 1.20.6 * [`205cab6`](https://github.com/siderolabs/pkgs/commit/205cab6d0e6be2721c5338bef232e3345d3a299f) chore: feat use new sd-boot * [`fb817fe`](https://github.com/siderolabs/pkgs/commit/fb817fe20789ca48895275e1877808a9206630dd) fix: enable USB attached SCSI driver on x86 systems * [`43451e6`](https://github.com/siderolabs/pkgs/commit/43451e68a0ddf634b90c7c12cca9437faa52d183) chore: bump dependencies * [`eca94f8`](https://github.com/siderolabs/pkgs/commit/eca94f8f1b9c3ceb62efb53fd1260d49ce17f1dd) feat: enable sriov * [`5a8e8e5`](https://github.com/siderolabs/pkgs/commit/5a8e8e594248847bb606ca07b3ea29e187e20d26) feat: enable VMWARE/HYPERV vsockets * [`edd725a`](https://github.com/siderolabs/pkgs/commit/edd725a0f9d07d39256d98a67be5dc4c56631078) chore: bump deps * [`c0ac69b`](https://github.com/siderolabs/pkgs/commit/c0ac69b70cfac3cdcf100a35f6d766c5ae47d950) feat: enable CONFIG_NVME_{MULTIPATH|AUTH} * [`f7cd916`](https://github.com/siderolabs/pkgs/commit/f7cd916b47975e61c6732079c1c5c4684dfb8c96) fix: bump drbd to 9.2.4 * [`a56d15a`](https://github.com/siderolabs/pkgs/commit/a56d15ad626b6e76a137636d6088361be9a73a9f) fix: copy missing `modules.*` files * [`1eefa66`](https://github.com/siderolabs/pkgs/commit/1eefa664fc7c65491e956a6f403ada774e73a7d3) feat: build isb modem drivers as module * [`a859f4f`](https://github.com/siderolabs/pkgs/commit/a859f4fb257e17fa19b1c10efcae594d33a86618) fix: build RDMA_RXE as a module * [`5fb5e95`](https://github.com/siderolabs/pkgs/commit/5fb5e9517de9fe35e383b96e92fa873aa045a845) feat: bump dependencies * [`39a64b2`](https://github.com/siderolabs/pkgs/commit/39a64b23e2c8689c44b9891b1e70149b8d003655) feat: update Linux to 6.1.31, add GENEVE for arm64 * [`97177be`](https://github.com/siderolabs/pkgs/commit/97177be803cc91c8fabccfec575b7d920bc78c38) feat: update Linux to 6.1.30 * [`b1f9d4e`](https://github.com/siderolabs/pkgs/commit/b1f9d4e717fbd0132b820d45c226ca643d7f577e) chore: prevent unsigned kexec with secureboot * [`9232a42`](https://github.com/siderolabs/pkgs/commit/9232a425b85b1058cd38eab30304f6cf243ab32c) feat: add reproducibility pipelines * [`702d7a7`](https://github.com/siderolabs/pkgs/commit/702d7a7e90099d8fdc9cc4ba50e86c8ba6e91d77) chore: bump deps * [`7958db1`](https://github.com/siderolabs/pkgs/commit/7958db1549a7c7560eeeb8f9c06d3be9487d8804) chore: copy over sd-boot and sd-stub from tools * [`813b3c3`](https://github.com/siderolabs/pkgs/commit/813b3c3d3276d0d9156919307e9ffe521925d40b) chore: revert xfsprogs * [`0cc78ab`](https://github.com/siderolabs/pkgs/commit/0cc78ab82ce920c8fa5654c73738050107e190bb) chore: bump kernel to 6.1.28 * [`70189e3`](https://github.com/siderolabs/pkgs/commit/70189e3df555fed4afade93798d72cd31aad99c5) chore: bump deps * [`c5d3bf1`](https://github.com/siderolabs/pkgs/commit/c5d3bf1985b49e688d29d06db6730834f65ee480) feat: add sd-stub and sd-boot * [`30a7ac2`](https://github.com/siderolabs/pkgs/commit/30a7ac2974fb7580e83819c76502fde77d777ea0) feat: update Linux 6.1.27, containerd 1.6.21 * [`fbc6ee5`](https://github.com/siderolabs/pkgs/commit/fbc6ee55b6ffae44c117255901ab0fbecae79cc3) chore: bump deps * [`82b9489`](https://github.com/siderolabs/pkgs/commit/82b9489b88b108f144b45fb55432576bfd767f91) chore: bump dependencies * [`f37e520`](https://github.com/siderolabs/pkgs/commit/f37e5205cf10fe10296e86565fa018d149f5d8c4) feat: update Linux to 6.1.25 * [`3920b16`](https://github.com/siderolabs/pkgs/commit/3920b163a5c6a6d7c7969155a909a7b2122e65f6) feat: add multi-gen LRU kernel support * [`988f1ec`](https://github.com/siderolabs/pkgs/commit/988f1ecf95536fb259cbd79e044a556728bc7332) feat: update Linux to 6.1.24 * [`5327d12`](https://github.com/siderolabs/pkgs/commit/5327d1263680f76706ea667906ca08222c8398da) fix: remove FB_NVIDIA drivers, Linux 6.1.23 * [`4eae958`](https://github.com/siderolabs/pkgs/commit/4eae958770573613bc29568d130be7aaa775e530) chore: copy over the kernel signing public key * [`174f8fc`](https://github.com/siderolabs/pkgs/commit/174f8fc9c80d871f1c03ea0a53dc8b6eb7112ccf) chore: update Go to 1.20.3 * [`41629b0`](https://github.com/siderolabs/pkgs/commit/41629b03e82bfb77623a812000ef8e98d15d56fa) chore: reorder pkgs for better kernel caching * [`b483a6b`](https://github.com/siderolabs/pkgs/commit/b483a6b01f539b0da13ca09882015044bff24e41) feat: build 'snp.efi' for iPXE * [`fb853ff`](https://github.com/siderolabs/pkgs/commit/fb853ff6b1194cdc1f2412c776347cf4b55c3336) feat: update containerd to 1.6.20

### Changes from siderolabs/tools
20 commits

* [`dc7dd9e`](https://github.com/siderolabs/tools/commit/dc7dd9e5b949f6f5d7626f11cb3b001526e8d1de) chore: remove libseccomp * [`e27c249`](https://github.com/siderolabs/tools/commit/e27c249c3213af6d12be4fb440a8f896c8e1b3d4) feat: update Go to 1.20.6 * [`9b6d512`](https://github.com/siderolabs/tools/commit/9b6d5123fa1e28160019a4b6e8b0f04482c49dc0) feat: use systemd 254-rc1 * [`cd3b692`](https://github.com/siderolabs/tools/commit/cd3b692b0cf5c663548cbe75db43036e11ee1014) chore: bump deps * [`c1027a6`](https://github.com/siderolabs/tools/commit/c1027a63d058b77f6cce7351fa7b63d4c94883ad) chore: remove sbsign * [`e0c76c0`](https://github.com/siderolabs/tools/commit/e0c76c096d06ef11afdb54287d5f15add108399b) chore: bump dependencies * [`7d0cd58`](https://github.com/siderolabs/tools/commit/7d0cd58b34bba6b9415db5e39bed351e7f00d44d) feat: update Go to 1.20.5 * [`150efc2`](https://github.com/siderolabs/tools/commit/150efc22508043bfadc9d84a8c3c5fee6c2aac5f) chore: remove non needed tools * [`88ebb40`](https://github.com/siderolabs/tools/commit/88ebb40dd348b6c9e4dc5551b616e4a1892b4e42) feat: add swtpm * [`4c5d7fe`](https://github.com/siderolabs/tools/commit/4c5d7feb88dcbae2f7bf45f51f9e5e1ba339abac) chore: use same source epoch everywhere * [`2e46e5b`](https://github.com/siderolabs/tools/commit/2e46e5be764f8180a0762a5ab080ccff04534a8a) feat: add reproducibility pipelines * [`c6a41b6`](https://github.com/siderolabs/tools/commit/c6a41b6c5108d676f8573d3dd47ee29ae46e5cc0) fix: add sd-stub assertion patch * [`d2dde48`](https://github.com/siderolabs/tools/commit/d2dde48f72343aa3c541336f5319b8e649e80c87) chore: bump deps * [`8e45ad7`](https://github.com/siderolabs/tools/commit/8e45ad75ea78e353ca3eae21b18da9a42d1edf49) feat: add sbsign * [`271c4a6`](https://github.com/siderolabs/tools/commit/271c4a66b6987d9de2c0d1d69891b5ff277ebd43) feat: add sd-tools * [`eedc294`](https://github.com/siderolabs/tools/commit/eedc294967d415cca40d4c427d3521cd198661d7) chore: bump deps * [`81b09a5`](https://github.com/siderolabs/tools/commit/81b09a5ab204f16306c980eeff518a0d1a37ddf2) feat: add libcap and gnuefi * [`47b0fd3`](https://github.com/siderolabs/tools/commit/47b0fd3e364d4fbcfffe10965f740db7acd82f70) chore: bump go to 1.20.4 * [`ff4cf2b`](https://github.com/siderolabs/tools/commit/ff4cf2beabab310365ad9887abb6234570f5092a) chore: bump deps * [`1563556`](https://github.com/siderolabs/tools/commit/1563556b8f8fdf20d8aa58ac5340104c7ffe732e) feat: update Go to 1.20.3

### Dependency Changes * **github.com/BurntSushi/toml** v1.2.1 -> v1.3.2 * **github.com/aws/aws-sdk-go** v1.44.232 -> v1.44.304 * **github.com/beevik/ntp** v0.3.0 -> v1.2.0 * **github.com/benbjohnson/clock** v1.1.0 -> v1.3.5 * **github.com/cenkalti/backoff/v4** v4.2.0 -> v4.2.1 * **github.com/containerd/containerd** v1.6.19 -> v1.6.21 * **github.com/containerd/typeurl/v2** v2.1.1 **_new_** * **github.com/containernetworking/plugins** v1.2.0 -> v1.3.0 * **github.com/cosi-project/runtime** v0.3.0 -> v0.3.1-alpha.8 * **github.com/docker/distribution** v2.8.1 -> v2.8.2 * **github.com/docker/docker** v23.0.2 -> v24.0.4 * **github.com/ecks/uefi** caef65d070eb **_new_** * **github.com/emicklei/dot** v1.4.2 -> v1.5.0 * **github.com/foxboron/go-uefi** 32187aa193d0 **_new_** * **github.com/google/go-tpm** v0.9.0 **_new_** * **github.com/hashicorp/go-envparse** v0.1.0 **_new_** * **github.com/hetznercloud/hcloud-go** v1.41.0 -> v1.48.0 * **github.com/insomniacslk/dhcp** 74ae03f2425e -> 5648422c16cd * **github.com/jsimonetti/rtnetlink** v1.3.1 -> v1.3.4 * **github.com/mattn/go-isatty** v0.0.18 -> v0.0.19 * **github.com/mdlayher/ethtool** ba3b4bc2e02c -> v0.1.0 * **github.com/mdlayher/genetlink** v1.3.1 -> v1.3.2 * **github.com/mdlayher/netlink** v1.7.1 -> v1.7.2 * **github.com/mdlayher/netx** c711c2f8512f -> 7e21880baee8 * **github.com/nberlee/go-netstat** v0.1.1 -> v0.1.2 * **github.com/opencontainers/go-digest** v1.0.0 **_new_** * **github.com/opencontainers/image-spec** v1.1.0-rc2 -> v1.1.0-rc4 * **github.com/packethost/packngo** v0.29.0 -> v0.30.0 * **github.com/prometheus/procfs** v0.9.0 -> v0.11.0 * **github.com/rivo/tview** 281d14d896d7 -> 6cc0565babaf * **github.com/rs/xid** v1.4.0 -> v1.5.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.15 -> v1.0.0-beta.19 * **github.com/siderolabs/crypto** v0.4.0 -> v0.4.1 * **github.com/siderolabs/discovery-api** v0.1.2 -> v0.1.3 * **github.com/siderolabs/discovery-client** v0.1.4 -> v0.1.5 * **github.com/siderolabs/extras** v1.4.0-1-g9b07505 -> v1.5.0-alpha.0-2-gf415aac * **github.com/siderolabs/gen** v0.4.3 -> v0.4.5 * **github.com/siderolabs/go-blockdevice** v0.4.4 -> v0.4.6 * **github.com/siderolabs/go-debug** v0.2.2 -> v0.2.3 * **github.com/siderolabs/go-kubernetes** v0.2.0 -> v0.2.2 * **github.com/siderolabs/go-loadbalancer** v0.2.1 -> v0.3.2 * **github.com/siderolabs/kms-client** v0.1.0 **_new_** * **github.com/siderolabs/pkgs** v1.4.1-5-ga333a84 -> v1.5.0-alpha.0-40-gfedfafa * **github.com/siderolabs/talos/pkg/machinery** v1.4.0 -> v1.5.0-alpha.2 * **github.com/siderolabs/tools** v1.4.0-1-g955aabc -> v1.5.0-alpha.0-19-gdc7dd9e * **github.com/spf13/cobra** v1.6.1 -> v1.7.0 * **github.com/stretchr/testify** v1.8.2 -> v1.8.4 * **github.com/vmware-tanzu/sonobuoy** v0.56.16 -> v0.56.17 * **github.com/vmware/govmomi** v0.30.4 -> v0.30.6 * **go.etcd.io/etcd/api/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/pkg/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/etcdutl/v3** v3.5.8 -> v3.5.9 * **golang.org/x/net** v0.8.0 -> v0.12.0 * **golang.org/x/sync** v0.1.0 -> v0.3.0 * **golang.org/x/sys** v0.6.0 -> v0.10.0 * **golang.org/x/term** v0.6.0 -> v0.10.0 * **golang.org/x/text** v0.11.0 **_new_** * **golang.zx2c4.com/wireguard/wgctrl** 9c5414ab4bde -> 925a1e7659e6 * **google.golang.org/grpc** v1.54.0 -> v1.56.2 * **google.golang.org/protobuf** v1.30.0 -> v1.31.0 * **k8s.io/api** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/apimachinery** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/apiserver** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/client-go** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/component-base** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/cri-api** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/klog/v2** v2.90.1 -> v2.100.1 * **k8s.io/kubectl** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/kubelet** v0.27.1 -> v0.28.0-alpha.4 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.68 -> v1.2.69 Previous release can be found at [v1.4.0](https://github.com/siderolabs/talos/releases/tag/v1.4.0) ## [Talos 1.5.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.5.0-alpha.2) (2023-07-20) Welcome to the v1.5.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Extension Services Talos now supports setting `environmentFile` for an extension service container spec. Refer: https://www.talos.dev/v1.5/advanced/extension-services/#container The extension waits for the file to be present before starting the service. ### Predictable Network Interface Names Starting with version Talos 1.5, network interfaces are renamed to [predictable names](https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/) same way as `systemd` does that in other Linux distributions. The naming schema `enx78e7d1ea46da` (based on MAC addresses) is enabled by default, the order of interface naming decisions is: * firmware/BIOS provided index numbers for on-board devices (example: `eno1`) * firmware/BIOS provided PCI Express hotplug slot index numbers (example: `ens1`) * physical/geographical location of the connector of the hardware (example: `enp2s0`) * interfaces's MAC address (example: `enx78e7d1ea46da`) The predictable network interface names features can be disabled by specifying `net.ifnames=0` in the kernel command line. Talos automatically adds the `net.ifnames=0` kernel argument when upgrading from Talos versions before 1.5. This change doesn't affect "cloud" platforms, like AWS, as Talos automatically adds `net.ifnames=0` to the kernel command line. ### Network KMS Disk Encryption Talos now supports new type of encryption keys which are sealed/unsealed with an external KMS server: ``` systemDiskEncryption: ephemeral: keys: - kms: endpoint: https://1.2.3.4:443 slot: 0 ``` gRPC API definitions and a simple reference implementation of the KMS server can be found in this [repository](https://github.com/siderolabs/kms-client/blob/main/cmd/kms-server/main.go). ### KubePrism - Kubernetes API Server In-Cluster Load Balancer Talos now supports configuring the KubePrism - Kubernetes API Server in-cluster load balancer with machine config `features.kubePrism.port` and `features.kubePrism.enabled` fields. If enabled, KubePrism binds to `localhost` and runs on the same port on every machine in the cluster. The default value for KubePrism endpoint is https://localhost:7445. The KubePrism is used by the `kubelet`, `kube-scheduler`, `kube-controller-manager` and `kube-proxy` by default and can be passed to the CNIs like Cilium and Calico. The KubePrism provides access to the Kubernetes API endpoint even if the external loadbalancer is not healthy, provided that the worker nodes can reach to the controlplane machine addresses directly. ### Machine Config option `.machine.install.bootloader` The `.machine.install.bootloader` option in the machine config is deprecated and will be removed in Talos 1.6. This was a no-op for a long time. The bootloader is always installed. ### XFS Quota Talos 1.5+ enables XFS project quota support by default, also enabling by default kubelet feature gate `LocalStorageCapacityIsolationFSQuotaMonitoring` to use xfs quotas to monitor volume usage instead of `du`. This feature is controlled by the `.machine.features.diskQuotaSupport` field in the machine config, it is set to true for new clusters. When upgrading from a previous version, the feature can be enabled by setting the field to true. On the first mount of a volume, the quota information will be recalculated, which may take some time. ### RDMA/RoCE support Talos no longer loads by default `rdma_rxe` Linux driver, which is required for RoCE support. If the driver is required, it can be enabled by specifying `rdma_rxe` in the `.machine.kernel.modules` field in the machine config. ### SecureBoot Talos now supports generating a custom iso that can be used with SecureBoot. Key generation and enrolling has to be done manually. ### `talosctl image` Command A new set of commands was introduced to manage container images in the CRI: * `talosctl image list` shows list of available images * `talosctl image pull` allows to pre-pull an image into the CRI Both new commands accept `--namespace` flag with two possible values: * `cri` (default): images managed by the CRI (Kubernetes workloads) * `system`: images managed by Talos (`etcd` and `kubelet`) ``` ### `talosctl images` Command The command `talosctl images` was renamed to `talosctl image default`. The backward-compatible alias is kept in Talos 1.5, but it will be dropped in Talos 1.6. ### TPM Disk Encryption Talos now supports encrypting STATE/EPHEMERAL with keys bound to a TPM device. The TPM device must be TPM2.0 compatible. This is ideally supported when booting with new Talos SecureBoot UKI ISOs/Metal images. This feature would still work if SecureBoot is not enabled for UKI images, but not recommended since there is no way to verify the trust of the bootloader. Example machine config: ``` systemDiskEncryption: ephemeral: keys: - slot: 0 tpm: {} state: keys: - slot: 0 tpm: {} ``` ### Component Updates * Linux: 6.1.39 * containerd: 1.6.21 * runc: 1.1.8 * etcd: 3.5.9 * Kubernetes: 1.28.0-alpha.4 * Flannel: 0.22.0 Talos is built with Go 1.20.6. ### `talosctl upgrade-k8s` Image Pre-pulling The command `talosctl upgrade-k8s` now by default pre-pulls images for Kubernetes controlplane components and kubelet. This provides an early check for missing images, and minimizes downtime during Kubernetes rolling component update. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Artem Chernyshev * Steve Francis * Christian Rolland * Nanfei Chen * Nico Berlee * Spencer Smith * Alex Corcoles * Alex Corcoles * Alex Lubbock * Artem Chernyshev * Budiman Jojo * Chris Hoffman * DJAlPee * Dennis Marttinen * Eirik Askheim * Florian Klink * Henk Kraal * James Callahan * LukasAuerbeck * Markus Reiter * Michael A. Davis * Michael Fornaro * Niklas Wik * Piotr Maksymiuk * Ricky Sadowski * Roee Klinger * Serge Logvinov * Thomas Perronin * Tim Jones * Victor Bajada * Walt Chen * bdronneau ### Changes
184 commits

* [`60c304126`](https://github.com/siderolabs/talos/commit/60c304126fce95fd4995c416e7757f85505b90fb) chore: bump dependencies * [`9ef4e5efc`](https://github.com/siderolabs/talos/commit/9ef4e5efca4b537a550a5e902fc2479ebb5e53e3) fix: log explicitly when kubelet has no nodeIP match * [`6b39c6a4d`](https://github.com/siderolabs/talos/commit/6b39c6a4d326752f92d98388bbb418f2e50d3ddb) fix: enable compression and bump gRPC max msg size * [`2f2eca861`](https://github.com/siderolabs/talos/commit/2f2eca86175fe98b3bf491f38ff907599333b139) chore: basic support for shutdown/poweroff flags * [`b84277d7d`](https://github.com/siderolabs/talos/commit/b84277d7dc50b196b7cd27e7f2ceff6bf8f58a8d) docs: fix wrong capability name * [`59d7d9344`](https://github.com/siderolabs/talos/commit/59d7d9344b27529af420ec31c7b599027cda044f) chore: use machined for `shutdown`, `poweroff` * [`2439bfb71`](https://github.com/siderolabs/talos/commit/2439bfb719d9f50107cee500d03c90bd50649e05) chore: explicitly add timestamps to machined logs * [`14966e718`](https://github.com/siderolabs/talos/commit/14966e718a07906ff389ecdda063fd16b22baab9) fix: skip over tpm2 1.2 devices * [`6716e7bc0`](https://github.com/siderolabs/talos/commit/6716e7bc0ba6da31b8bc19aa4bd5edb7749b39a1) docs: update cilium documentation about KubePrism usage * [`166d75fe8`](https://github.com/siderolabs/talos/commit/166d75fe888d334349f57dcf405b6867ca5305e2) fix: tpm2 encrypt/decrypt flow * [`130518de7`](https://github.com/siderolabs/talos/commit/130518de71ae96cdf7d733a35e4c306940e1b845) chore: change missing renames of KubePrism * [`5f34f5b41`](https://github.com/siderolabs/talos/commit/5f34f5b41f03d6d455d7b843084d2951c365a7ee) chore: rename api load balancer to KubePrism * [`c8b7095c0`](https://github.com/siderolabs/talos/commit/c8b7095c01f597cd8b41964b42aa7e35c85ae307) refactor: use tpm2 library to calculate policy hash * [`078aac92e`](https://github.com/siderolabs/talos/commit/078aac92ee30c9666235219d4623b82d66362d4d) chore: bump deps * [`53873b844`](https://github.com/siderolabs/talos/commit/53873b8444acaa97d85c50caec625b9dbfdfef93) refactor: move ukify into Talos code * [`d5f6fb9ff`](https://github.com/siderolabs/talos/commit/d5f6fb9ff2980df03365719d9e2690cb5ac788af) chore: add vendor info * [`79365d9ba`](https://github.com/siderolabs/talos/commit/79365d9bacf0e8a6660cdc6b7172c79edf5f3ba3) feat: tpm2 based disk encryption * [`06369e819`](https://github.com/siderolabs/talos/commit/06369e8195e76f96d232d077efb2bfb059b7aa96) fix: retry CRI pod removal, fix upgrade flow in the tests * [`d32dd3a82`](https://github.com/siderolabs/talos/commit/d32dd3a820b07d58ca89c4226c986d87ff0e2b65) chore: update Go to 1.20.6 * [`8017afb10`](https://github.com/siderolabs/talos/commit/8017afb107b901a8785bccaac65d63f34e506568) feat: implement CRI image management and pre-pull on K8s upgrade * [`1c2f19b36`](https://github.com/siderolabs/talos/commit/1c2f19b367af8b04fc49174540e5b141f4b34156) feat: update Kubernetes to 1.28.0-alpha.4 * [`94e9891c1`](https://github.com/siderolabs/talos/commit/94e9891c1bb44a1e7c285b4ccf1fad59ea05aa62) chore: bump sd-boot to v254-rc1 * [`936111ce0`](https://github.com/siderolabs/talos/commit/936111ce062d23ed11b30ea35585c0519260f9c5) fix: properly set up tls for KMS endpoint * [`cb226eec4`](https://github.com/siderolabs/talos/commit/cb226eec46b59372c684c3946e0ba0910066573d) fix: rewrite encryption system information flow * [`3206db528`](https://github.com/siderolabs/talos/commit/3206db52895416d1eb936caa4e953312b34b8549) feat: drop tpm simulator for ukify measure * [`bd4f89f63`](https://github.com/siderolabs/talos/commit/bd4f89f6338423a79b7ce89bda1bd6704caaae59) fix: disable dashboard on Azure, GCP and Scaleway * [`bdb96189f`](https://github.com/siderolabs/talos/commit/bdb96189faadc48e93146f9fd7b03e006bf1dd75) refactor: make maintenance service controller-based * [`d23d04de2`](https://github.com/siderolabs/talos/commit/d23d04de2a5dee30ccf21efe767daf229de78bdb) feat: seed the kernel random pool from the TPM * [`c81ce8cfb`](https://github.com/siderolabs/talos/commit/c81ce8cfb0bc7df66ffd1e1819b64dad6357d890) feat: support controlplane resources configuration * [`74de562b2`](https://github.com/siderolabs/talos/commit/74de562b29c748fda3140871ea3fab99698341ef) fix: mount hugepages with nosuid + nodev * [`ce63abb21`](https://github.com/siderolabs/talos/commit/ce63abb219a2fd4a9d3fdd93a13c343af123efc2) feat: add KMS assisted encryption key handler * [`dafbe9deb`](https://github.com/siderolabs/talos/commit/dafbe9debdee2b015ed574ac4f5f722bce997b31) chore: optimize dockerfile instructions * [`a4289e870`](https://github.com/siderolabs/talos/commit/a4289e8703d9f9e52b739b19b5b38e30a75a1454) chore: fix CLI docs generation stability * [`2fec8388f`](https://github.com/siderolabs/talos/commit/2fec8388fc2fe3058b7b6f141ce9eae2c6a8268f) chore: bump dependencies * [`c1b4262dd`](https://github.com/siderolabs/talos/commit/c1b4262dd60f6cbea6d46a8d0433499bf6365b36) docs: split simple and more complex getting started guides * [`c9a9f9561`](https://github.com/siderolabs/talos/commit/c9a9f95611e38cf5c298f0d9fb0890a9bc0f8b98) refactor: extract secure boot certificate generation * [`6be5a13d5`](https://github.com/siderolabs/talos/commit/6be5a13d5d8341c58d0d2fe75c49ba1de9bf7316) feat: implement machine config documents for event and log streaming * [`e241be85b`](https://github.com/siderolabs/talos/commit/e241be85ba748163268eaeed2a88c8e295f84b28) fix: properly handle YAML comment stripping for multi-doc * [`c02ada7d9`](https://github.com/siderolabs/talos/commit/c02ada7d952255bffe67b3c84f1f832253e1a3b5) fix: capabilities including `ALL` should be uppercase * [`cbdf96d46`](https://github.com/siderolabs/talos/commit/cbdf96d461ec0cf8929c2c76614081ef042dda31) feat: support environment file for extensions * [`35d6adcb9`](https://github.com/siderolabs/talos/commit/35d6adcb9ad7e9420a5bcdfcf3378a05c0b65d46) fix: provide stashed META values before installation * [`258f07449`](https://github.com/siderolabs/talos/commit/258f07449050d69c369fdc71ac613a1a225807bf) fix: ukify cert generation * [`bf3febb7e`](https://github.com/siderolabs/talos/commit/bf3febb7e2bf3ebf1bd66ee088f3885a178c953c) fix: refine OVMF search paths * [`fbebc17f8`](https://github.com/siderolabs/talos/commit/fbebc17f8be7a3ca6c45c3c84d306e52c47d441d) fix: disable LVM backups/archive * [`e5306ef26`](https://github.com/siderolabs/talos/commit/e5306ef2637dd2eb7464691b55159a43933c7419) chore: format and cleanup test scripts * [`bc371ecfd`](https://github.com/siderolabs/talos/commit/bc371ecfdafe51f8cf34461caf9e6f51c0a93108) chore: add `/sbin/shutdown` * [`0d313b973`](https://github.com/siderolabs/talos/commit/0d313b973367906b2fd4bcad4b2def79344dbd67) feat: add `reboot-mode` flag to `talosctl upgrade` * [`7ce87f20c`](https://github.com/siderolabs/talos/commit/7ce87f20c39c615f4d23a3be23780a36008dcb19) fix: compare only basename of `os.Args[0]` in machined * [`53389b1e7`](https://github.com/siderolabs/talos/commit/53389b1e724751e28046167b44f05c6ecf06f184) feat: auto-enroll secure boot keys * [`d77f0bc7b`](https://github.com/siderolabs/talos/commit/d77f0bc7bbe01b7fc8efa21a7c57d73ecb94a01f) docs: fix broken link to powershell module * [`e1b150a11`](https://github.com/siderolabs/talos/commit/e1b150a11014ddd0c60585d320dd7cd556cf2a0c) release(v1.5.0-alpha.1): prepare release * [`8daf432b2`](https://github.com/siderolabs/talos/commit/8daf432b2957a8f9d5c59970cf68e7e8414038f5) chore: bump deps * [`e3f3f5794`](https://github.com/siderolabs/talos/commit/e3f3f5794d276433748d0e677ed8476a54f8a98e) feat: implement revert for sd-boot * [`d8b0903d7`](https://github.com/siderolabs/talos/commit/d8b0903d70181afc901d8ddb71bdfa964d4df2cd) docs: vagrant setup document fix * [`fe0f46980`](https://github.com/siderolabs/talos/commit/fe0f46980f348852907218d6f49581efe4b45d49) feat: implement secure boot from disk * [`445f5ad54`](https://github.com/siderolabs/talos/commit/445f5ad5426b125e29d86ff096695399bd01eb32) feat: support API server load balancer * [`19bc223de`](https://github.com/siderolabs/talos/commit/19bc223de8ad878bffe539bda617d5f861af3cfe) refactor: bootloader interface, labels * [`665702ddd`](https://github.com/siderolabs/talos/commit/665702ddd351e902336e6ab81108ea94d61db5c1) chore: fix cilium e2e tests * [`71a548d18`](https://github.com/siderolabs/talos/commit/71a548d18013ee16394921759e819b0fabb43758) chore: generic boootloader implementation * [`e9dbc9311`](https://github.com/siderolabs/talos/commit/e9dbc9311bcbbbcaab2c7eb7f7128013194c234a) test: bump versions for upgrade tests * [`0a99965ef`](https://github.com/siderolabs/talos/commit/0a99965efbdd5dc0d927eb2cbae209dc143c9541) refactor: replace `uncordonNode` with controllers * [`e858bca3a`](https://github.com/siderolabs/talos/commit/e858bca3a2f75d5035710d52229c8142f3eb6982) test: fix cilium integration tests * [`455328d05`](https://github.com/siderolabs/talos/commit/455328d058fba3a5a8b3358820a02e2b4fabad95) fix: allow time skew for generated kubeconfig * [`3ae05648a`](https://github.com/siderolabs/talos/commit/3ae05648ae0a2f79bebd678f85d63d4e5dafde0a) fix: usage of custom kernels * [`0797b0d16`](https://github.com/siderolabs/talos/commit/0797b0d16808d115649a9e0e37b355bbbc2a30b5) chore: add a pipeline to test cloud-images step without a release * [`e5a36268b`](https://github.com/siderolabs/talos/commit/e5a36268b63e588ea6cd2439bf0de356ee07d752) docs: include `allowSchedulingOnControlPlanes` on `talosctl gen config` output * [`c74d93728`](https://github.com/siderolabs/talos/commit/c74d937280c2ec707936a72d07dc2a5dd252c5d2) chore: bump github.com/cosi-project/runtime * [`dbaf5c699`](https://github.com/siderolabs/talos/commit/dbaf5c69978fd1d22737385ddd096798d408254c) refactor: task `labelControlPlane` into controllers * [`1865a0c29`](https://github.com/siderolabs/talos/commit/1865a0c29663a1a78db7ef6e901d450d67a3cbe1) chore: modify some usages that are not recommended * [`3816318b9`](https://github.com/siderolabs/talos/commit/3816318b9e2e205da0c949c0ec59a087decd0b78) chore: wrap config.Provider in atomic wrapper * [`d04cf1978`](https://github.com/siderolabs/talos/commit/d04cf19788df20c802eadb9678570a4f15d339b2) chore: clean up unnecessary self assignment * [`a34a94898`](https://github.com/siderolabs/talos/commit/a34a948985fed7c3054c4342c48e0e0620569625) fix: copy missing modules.* files * [`f5e3272fc`](https://github.com/siderolabs/talos/commit/f5e3272fce641a878eefa66437d28d3ed9917ab6) refactor: task 'updateBootLoader' as controller * [`e7be6ee7c`](https://github.com/siderolabs/talos/commit/e7be6ee7c3636eebd557d93e440e9749c8093360) refactor: make event log streaming fully reactive * [`aef2192a6`](https://github.com/siderolabs/talos/commit/aef2192a6584e7934086eae0caab6faba52a8ac1) chore: use fixed module list * [`c719aa231`](https://github.com/siderolabs/talos/commit/c719aa2316bffa3b614d27d630ea3d8731684f4e) fix: allow http:// for discovery service URL * [`39134d8d5`](https://github.com/siderolabs/talos/commit/39134d8d5304cec5e1a1c5fe23f62ed957241213) chore: fix cron pipeline * [`a61dcdbbd`](https://github.com/siderolabs/talos/commit/a61dcdbbd5c917b49c810108ff96854ad51269b1) fix: don't load RDMA over Ethernet driver by default * [`aac441f61`](https://github.com/siderolabs/talos/commit/aac441f618ac60f2298d9e17a2044916f7da9d69) chore: update Go to 1.20.5, bump dependencies * [`1c0c7933d`](https://github.com/siderolabs/talos/commit/1c0c7933dfef23544e2fb0fc04c4c5ad7d5b5d9b) chore: cleanup partition code * [`31b988281`](https://github.com/siderolabs/talos/commit/31b988281efb9d0c66975bbfc20b893ad32c161d) docs: add some words about certifcates * [`e912c0dfc`](https://github.com/siderolabs/talos/commit/e912c0dfcf515c5a6c852f4b935c9b48e61b13f1) chore: use go-blockdevice for zeroing partitions * [`e6dde8ffc`](https://github.com/siderolabs/talos/commit/e6dde8ffc50e435a42d11eb96cf6aea2cf3520ca) feat: add network chaos to qemu development environment * [`47986cb79`](https://github.com/siderolabs/talos/commit/47986cb79eb30c6e9c0d091ee37b2b1c2f20885c) chore: unify kexec phase * [`3a865370f`](https://github.com/siderolabs/talos/commit/3a865370f5152243e08a69626de023f924e22689) feat: qemu secureboot * [`5dab45e86`](https://github.com/siderolabs/talos/commit/5dab45e86917837b0991a62ab94a7b96b3ef777e) refactor: allow kmsg log streaming to be reconfigured on the fly * [`8a02ecd4c`](https://github.com/siderolabs/talos/commit/8a02ecd4cb97bcaafe5761d464fec8a4e44b672f) chore: add endpoints balancer controller * [`423a31ac9`](https://github.com/siderolabs/talos/commit/423a31ac9d8f28c2bcf00794bacf5446e43fc0b7) chore: deprectae `bootloader` installer option * [`cdfece7d6`](https://github.com/siderolabs/talos/commit/cdfece7d64a9269afcc213f8d604d0b7e525cb8a) chore: optimize image compression * [`bfc341937`](https://github.com/siderolabs/talos/commit/bfc34193762cb309ef2230f4d79673c4a56f4db5) chore: add default console args * [`2749aeeda`](https://github.com/siderolabs/talos/commit/2749aeeda0451b286369d911696070e2cf4359e9) feat: add support for multi-doc strategic merge patching * [`3f68485e4`](https://github.com/siderolabs/talos/commit/3f68485e44800a0c50b5855531ec10507e7d0df9) feat: add uki iso generation * [`bab484a40`](https://github.com/siderolabs/talos/commit/bab484a405cb598d1c5f35f7602c2ac27e6efa97) feat: use stable network interface names * [`196dfb99b`](https://github.com/siderolabs/talos/commit/196dfb99b0329d5c52fd7089e62fbfa1b09df3c6) fix: do not probe kernel args in dashboard if not needed * [`8c071b579`](https://github.com/siderolabs/talos/commit/8c071b5796db05ecb17e46295eb2140827a58ca8) fix: skip DHCP RENEW if server IP in the lease is all zeroes * [`badbc51e6`](https://github.com/siderolabs/talos/commit/badbc51e63b685e22fffb82ae294a35cd9f65922) refactor: rewrite code to include preliminary support for multi-doc * [`ecce29dee`](https://github.com/siderolabs/talos/commit/ecce29dee9625842e419496e18560291ef90b1b5) fix: upgrade-k8s use internal IP first, external IP fallback * [`3c64a5ffb`](https://github.com/siderolabs/talos/commit/3c64a5ffba2109ccf5102f71652e54def52f8dbf) chore: optimize image generation time * [`2292f36d9`](https://github.com/siderolabs/talos/commit/2292f36d970d3edcf39b5d5f12d0051d7d75f390) chore: registry.k8s.io for coredns image * [`f2b258b37`](https://github.com/siderolabs/talos/commit/f2b258b3733a8fcc34bccde3bf01855a512d519a) docs: document talosctl version for upgrades * [`a0773f783`](https://github.com/siderolabs/talos/commit/a0773f783cfb3cfab8cbbeffb6449159754d785e) chore: add ukify Go script * [`b69e38d1f`](https://github.com/siderolabs/talos/commit/b69e38d1ff069ba8fac7a6524621f8b3c7256238) chore: bump dependencies * [`adce65103`](https://github.com/siderolabs/talos/commit/adce65103424f9f895e6b8c4858b27b3eb6bd74b) docs: add piraeus/drbd to storage documentation * [`a982cabe7`](https://github.com/siderolabs/talos/commit/a982cabe7011c87e863f7bb0829921e927ddf782) docs: link support matrix in k8s update doc * [`1fb29a56a`](https://github.com/siderolabs/talos/commit/1fb29a56a8abe5d72b8a3a336693e798424c63e0) fix: fail quickly if upgrade-k8s is used with multiple nodes * [`51d931c47`](https://github.com/siderolabs/talos/commit/51d931c4705fc7ca0bdadc59d732e56fae318dda) chore: faster dev cycle * [`dc6764871`](https://github.com/siderolabs/talos/commit/dc6764871c9e732b88f7cddc1784e943e9d952bb) refactor: move around config interfaces, make RawV1Alpha1 typed * [`ea9a97dba`](https://github.com/siderolabs/talos/commit/ea9a97dba38c6ab2de830e3b0c3d202d22bdb668) fix: fall back to external IP when discovering nodes in upgrade-k8s * [`0bb7e8a5c`](https://github.com/siderolabs/talos/commit/0bb7e8a5cf8b8f3bf31d9f8c3a85b4153921c126) refactor: split config.Provider into Config & Container * [`85d8a1619`](https://github.com/siderolabs/talos/commit/85d8a1619431989eb05cb15ad01a1bc06b0f63e9) chore: bump deps * [`39b7a56f0`](https://github.com/siderolabs/talos/commit/39b7a56f01d41d33eb96a0feb6e34d43965a99fd) chore: use 8GiB instead of 10GiB for cloud images * [`ff11fd39c`](https://github.com/siderolabs/talos/commit/ff11fd39c723a40c01abe6348f64b1f892856175) fix: race with `udevd` and `mountUserDisks` * [`c3fabb982`](https://github.com/siderolabs/talos/commit/c3fabb9829d12353770d6436a1d726b15820ebce) chore: update default image sizes to 10GB for all "cloud" images * [`10155c390`](https://github.com/siderolabs/talos/commit/10155c390e87898098426600709657fbd51e02e8) feat: enable xfs project quota support, kubelet feature * [`eba818564`](https://github.com/siderolabs/talos/commit/eba81856427dd3f6c0cf317f027e63d65a079029) release(v1.5.0-alpha.0): prepare release * [`383471c3e`](https://github.com/siderolabs/talos/commit/383471c3e956ff6e077a1de75b02a50835fbf352) feat: update default Kubernetes to v1.27.2 * [`8f68d1abe`](https://github.com/siderolabs/talos/commit/8f68d1abeff83c3ff0e6c5d9f61cb14807b44ca5) chore: bump deps * [`e0c1585d3`](https://github.com/siderolabs/talos/commit/e0c1585d3047ef213134331dc57f8e2e8c23a93d) feat: create azure community gallery image version on release * [`dd8336c9e`](https://github.com/siderolabs/talos/commit/dd8336c9ee7f8a3a44d45c9f9e3cbbf741f84c44) fix: refresh kubelet self-issued serving certificates * [`bb02dd263`](https://github.com/siderolabs/talos/commit/bb02dd263cbc5e7e3839148d86a4a0a5f7ea998b) chore: drop deprecated stuff for Talos 1.5 * [`61cad8673`](https://github.com/siderolabs/talos/commit/61cad86731e5c0aa80d7df41ea02d0b7ff579c45) chore: bump deps * [`01dfd3af7`](https://github.com/siderolabs/talos/commit/01dfd3af7d64dacd179d17d9d5eaf4bc44cf72af) feat: update etcd to v3.5.9 * [`aa65fbb8a`](https://github.com/siderolabs/talos/commit/aa65fbb8a1752a70e7bac4e4e9872f35e88d1cc9) chore: update KUBECTL_URL to reflect the community bucket * [`cc3128d94`](https://github.com/siderolabs/talos/commit/cc3128d944abacfb633bc783b7fed6d0a6f80661) chore: bump kernel to 6.1.28 * [`97fffaf78`](https://github.com/siderolabs/talos/commit/97fffaf78a0b9a1dc67709de11d37ea20aefde59) chore: use ctest.UpdateWithConflicts instead of plain UpdateWithConflicts * [`3b36993b9`](https://github.com/siderolabs/talos/commit/3b36993b9926392f4290e6fabc82e635f4c98149) fix: rlimit nofile test * [`45e6e27af`](https://github.com/siderolabs/talos/commit/45e6e27af75746fd0cc8b0f98a2d14579eb0ed40) chore: bump runtime * [`4f720d465`](https://github.com/siderolabs/talos/commit/4f720d46532af39165fc5051052d5c42595d91af) fix: revert: set rlimit explicitly in wrapperd * [`a2565f674`](https://github.com/siderolabs/talos/commit/a2565f67416e9b9bc22f2d5506df9ea7771c0c8c) fix: set rlimit explicitly in wrapperd * [`cdfc242b8`](https://github.com/siderolabs/talos/commit/cdfc242b8354f4cc4e7ce51bbe3a8fb20b35995d) chore: re-enable Go buildid * [`e67f3f5c5`](https://github.com/siderolabs/talos/commit/e67f3f5c5453f947355194ea9656c15ff008c35e) feat: linux 6.1.27, containerd 1.6.21, go 1.20.4 * [`55ae59a0a`](https://github.com/siderolabs/talos/commit/55ae59a0ad71293676b3efed461f5ab98101401a) fix: properly skip/cleanup controlplane configs for workers * [`64eade9bd`](https://github.com/siderolabs/talos/commit/64eade9bde271bce4e629e6ac09407c8c42e01be) chore: clean up unused constant * [`62c6e9655`](https://github.com/siderolabs/talos/commit/62c6e9655cb639d4993aaa4c9b364342688599cb) feat: introduce siderolink config resource & reconnect * [`860002c73`](https://github.com/siderolabs/talos/commit/860002c7352bedd10845e11da37c80685ff0e720) fix: don't reload control plane pods on cert SANs changes * [`d43c61e80`](https://github.com/siderolabs/talos/commit/d43c61e80f5b05b81f2a021cdfe012e500c3d98e) fix: enforce nolock option for all NFS mounts by default * [`339986db9`](https://github.com/siderolabs/talos/commit/339986db9d3675b78ce0d268f799ad654862fb0f) fix: inhibit timer to follow kubelet timer * [`cbf6dc100`](https://github.com/siderolabs/talos/commit/cbf6dc1009ad47a2804774839e4e0301efa8ac78) fix: set timeout for unmount calls * [`b58f913d5`](https://github.com/siderolabs/talos/commit/b58f913d5f4b8ecf39be183d0bafe1109f0f0737) fix: set the static pod priority as values * [`f8a7a5b6b`](https://github.com/siderolabs/talos/commit/f8a7a5b6bf4138a33cbe5c9afe85db99de167aec) docs: add information about KubeSpan ports and topology * [`2bad74d64`](https://github.com/siderolabs/talos/commit/2bad74d6423c083ec34f1b422f23b0024d5f8798) docs: add how to on scaling down * [`7442ff8b0`](https://github.com/siderolabs/talos/commit/7442ff8b095ef1337f54332a71d08053a2832144) chore: fix typos inteface -> interface (docs and tests) * [`d4e94f7a1`](https://github.com/siderolabs/talos/commit/d4e94f7a15acf7f3c9e7532b067cdacd0e805bec) fix: add back required TARGETARCH for installer * [`e6fffda01`](https://github.com/siderolabs/talos/commit/e6fffda01385a2daaa901a5742f30a4edc9186a7) chore: linux 6.1.26, runc 1.1.7 * [`344746ae2`](https://github.com/siderolabs/talos/commit/344746ae2fa038b704d02fec04c3d358762fe938) fix: bump max inhibit delay to 20 min * [`d9bdea2b5`](https://github.com/siderolabs/talos/commit/d9bdea2b54772f067783ee64eb85c834957d386a) chore: fork docs and compatibility modules for Talos 1.5 * [`3d99610fc`](https://github.com/siderolabs/talos/commit/3d99610fc9b0d0084be822be29bb1bf2fbe85833) docs: document building, verifying image and process caps * [`014008ea2`](https://github.com/siderolabs/talos/commit/014008ea25208afbeabb42ef89238802705ad4e0) fix: udevd rules trigger * [`9b36bb613`](https://github.com/siderolabs/talos/commit/9b36bb613b44f182e47ae63bc74e4a8b6342d68d) feat: update Linux to 6.1.25, fix virtio on arm64 * [`08ec66c55`](https://github.com/siderolabs/talos/commit/08ec66c55ccca3f9aa82a9703ebf183913b19a7e) feat: clean up (garbage collect) system images which are not referenced * [`b097efcde`](https://github.com/siderolabs/talos/commit/b097efcde29c20cdc4fed23fe8366bd683db634c) fix: display correct number of machines on dashboard * [`cad43f0ad`](https://github.com/siderolabs/talos/commit/cad43f0ad3bc2ede8a6ae81767c9226b6bc69f19) chore: remove k8s master label * [`e296a566e`](https://github.com/siderolabs/talos/commit/e296a566e6efb0cbdd119e73aff1feaa772d38bd) fix: support kernel userspace module loading * [`103f0ffdd`](https://github.com/siderolabs/talos/commit/103f0ffdd3ebd57a5086852f3502a8a7d4428faa) feat: add startup probes to controller-manager and scheduler * [`5a1ae8aae`](https://github.com/siderolabs/talos/commit/5a1ae8aae89e54d5540586d6f2e99ef3e80a72eb) chore: bump dependences * [`ec8c8dbaf`](https://github.com/siderolabs/talos/commit/ec8c8dbafcdaf63d036bdba92fa153d4d1c90100) chore: fix container image reproducibility * [`f661d8487`](https://github.com/siderolabs/talos/commit/f661d84877e6db5bc8856b982990926dcbfe949c) fix: allow `talosctl cp` to handle special files in `/proc` * [`2d824b563`](https://github.com/siderolabs/talos/commit/2d824b5639a4b8c3b673d13b08b2b97c69aafe0d) fix: do not show control plane status for workers on dashboard * [`e5491ddad`](https://github.com/siderolabs/talos/commit/e5491ddadeb1776bd5c17dd35917e05ec4847d0f) docs: update documentation for nocloud * [`7a004a6f7`](https://github.com/siderolabs/talos/commit/7a004a6f7f47fa5d17e855eb02650754d8411574) fix: parse errors correctly * [`374ef5385`](https://github.com/siderolabs/talos/commit/374ef53853947811dc221d99751cf0e16294508c) test: submit verbose flag to e2e tests * [`e1d38b6fe`](https://github.com/siderolabs/talos/commit/e1d38b6febf26fe31a6b9d6ed8f9b6bdba29aa3b) feat: show template URL in dashboard config URL tab * [`45d7f0ce9`](https://github.com/siderolabs/talos/commit/45d7f0ce95454ce85c403fc493ddb97e4d478238) docs: fix the latest url * [`96efbf147`](https://github.com/siderolabs/talos/commit/96efbf14769579d514ef9c75d01d9f44d276113a) docs: activate 1.4.0 docs by default * [`8c1f515b1`](https://github.com/siderolabs/talos/commit/8c1f515b1b8e40bce42e2fc04755afe5bf8a56aa) feat: update Linux to 6.1.24 * [`8689bef5f`](https://github.com/siderolabs/talos/commit/8689bef5f10839091cf131edb6c8efad4ccba034) docs: update documentation for Talos 1.4 * [`a781dfb8e`](https://github.com/siderolabs/talos/commit/a781dfb8e3ded67edcb2a6a1048bfe76c6bd0d24) feat: update Kubernetes to 1.27.1 * [`a737dd83a`](https://github.com/siderolabs/talos/commit/a737dd83a4cd7549f85f8df0882f1c9a4446060d) chore: typo in `compatibility.ParseKubernetesVersion` * [`f14928b0a`](https://github.com/siderolabs/talos/commit/f14928b0a9dd3d85664605f4f6a206236ea94614) fix: fix dashboard crash when a non-existent node is specified * [`3e406d9b0`](https://github.com/siderolabs/talos/commit/3e406d9b07c0e67a2fb61e612bc3f378f3c35247) feat: update etcd to v3.5.8 * [`bd1cff3e8`](https://github.com/siderolabs/talos/commit/bd1cff3e83530b9b89b27d8083ea8f3f0cf6ede4) chore: remove Go buildid * [`e31f7f50b`](https://github.com/siderolabs/talos/commit/e31f7f50b1b455beb98cd25859a44bbbccc1ff64) feat: update Kubernetes to 1.27.0 * [`aa3640d74`](https://github.com/siderolabs/talos/commit/aa3640d74ce2e3619476453381909fa3520eb87d) docs: update storage.md * [`07bb61e60`](https://github.com/siderolabs/talos/commit/07bb61e60c53b267756dc97874b9c9554f2b1486) chore: module-sig-verify cleanup * [`5e9d836c3`](https://github.com/siderolabs/talos/commit/5e9d836c3d075c3edb2d48b2868c31a1c963e2de) chore: add kernel module signtaure verification * [`3cd1c6bb0`](https://github.com/siderolabs/talos/commit/3cd1c6bb0b83e5747a7356140a44b16deb4727e6) fix: send 'STOP' event on phase end * [`5176d27dc`](https://github.com/siderolabs/talos/commit/5176d27dc566d8689bb305398da7250269ebe9a3) feat: update Kubernetes to 1.27.0-rc.1 * [`2c55550a6`](https://github.com/siderolabs/talos/commit/2c55550a66b49b49d8dc95b83516b7c0f8107300) fix: quote ISO kernel args for GRUB * [`319d76e38`](https://github.com/siderolabs/talos/commit/319d76e38978406d8d37e89ada2c403969d6c972) fix: respect BROWSER=echo in client auth interceptor * [`4e4ace839`](https://github.com/siderolabs/talos/commit/4e4ace839c0f558e7b00979fa4c64c32985aa3ce) chore: update Go to 1.20.3 * [`170f73899`](https://github.com/siderolabs/talos/commit/170f73899a3bf29e9c6f76fdc5e510be08edf4aa) fix: correctly parse static pod phase * [`c3a595d5b`](https://github.com/siderolabs/talos/commit/c3a595d5b7d3c7c3091229caef6b2553416edb56) fix: improve action tracking post checks * [`eb01edbc8`](https://github.com/siderolabs/talos/commit/eb01edbc8a0ef5810693afe450861d5b63877b72) fix: rework DHCP flow * [`e095150a6`](https://github.com/siderolabs/talos/commit/e095150a6e34cbdc805a2cac85ec7f28f98629b4) test: bump CAPI components versions

### Changes since v1.5.0-alpha.1
50 commits

* [`60c304126`](https://github.com/siderolabs/talos/commit/60c304126fce95fd4995c416e7757f85505b90fb) chore: bump dependencies * [`9ef4e5efc`](https://github.com/siderolabs/talos/commit/9ef4e5efca4b537a550a5e902fc2479ebb5e53e3) fix: log explicitly when kubelet has no nodeIP match * [`6b39c6a4d`](https://github.com/siderolabs/talos/commit/6b39c6a4d326752f92d98388bbb418f2e50d3ddb) fix: enable compression and bump gRPC max msg size * [`2f2eca861`](https://github.com/siderolabs/talos/commit/2f2eca86175fe98b3bf491f38ff907599333b139) chore: basic support for shutdown/poweroff flags * [`b84277d7d`](https://github.com/siderolabs/talos/commit/b84277d7dc50b196b7cd27e7f2ceff6bf8f58a8d) docs: fix wrong capability name * [`59d7d9344`](https://github.com/siderolabs/talos/commit/59d7d9344b27529af420ec31c7b599027cda044f) chore: use machined for `shutdown`, `poweroff` * [`2439bfb71`](https://github.com/siderolabs/talos/commit/2439bfb719d9f50107cee500d03c90bd50649e05) chore: explicitly add timestamps to machined logs * [`14966e718`](https://github.com/siderolabs/talos/commit/14966e718a07906ff389ecdda063fd16b22baab9) fix: skip over tpm2 1.2 devices * [`6716e7bc0`](https://github.com/siderolabs/talos/commit/6716e7bc0ba6da31b8bc19aa4bd5edb7749b39a1) docs: update cilium documentation about KubePrism usage * [`166d75fe8`](https://github.com/siderolabs/talos/commit/166d75fe888d334349f57dcf405b6867ca5305e2) fix: tpm2 encrypt/decrypt flow * [`130518de7`](https://github.com/siderolabs/talos/commit/130518de71ae96cdf7d733a35e4c306940e1b845) chore: change missing renames of KubePrism * [`5f34f5b41`](https://github.com/siderolabs/talos/commit/5f34f5b41f03d6d455d7b843084d2951c365a7ee) chore: rename api load balancer to KubePrism * [`c8b7095c0`](https://github.com/siderolabs/talos/commit/c8b7095c01f597cd8b41964b42aa7e35c85ae307) refactor: use tpm2 library to calculate policy hash * [`078aac92e`](https://github.com/siderolabs/talos/commit/078aac92ee30c9666235219d4623b82d66362d4d) chore: bump deps * [`53873b844`](https://github.com/siderolabs/talos/commit/53873b8444acaa97d85c50caec625b9dbfdfef93) refactor: move ukify into Talos code * [`d5f6fb9ff`](https://github.com/siderolabs/talos/commit/d5f6fb9ff2980df03365719d9e2690cb5ac788af) chore: add vendor info * [`79365d9ba`](https://github.com/siderolabs/talos/commit/79365d9bacf0e8a6660cdc6b7172c79edf5f3ba3) feat: tpm2 based disk encryption * [`06369e819`](https://github.com/siderolabs/talos/commit/06369e8195e76f96d232d077efb2bfb059b7aa96) fix: retry CRI pod removal, fix upgrade flow in the tests * [`d32dd3a82`](https://github.com/siderolabs/talos/commit/d32dd3a820b07d58ca89c4226c986d87ff0e2b65) chore: update Go to 1.20.6 * [`8017afb10`](https://github.com/siderolabs/talos/commit/8017afb107b901a8785bccaac65d63f34e506568) feat: implement CRI image management and pre-pull on K8s upgrade * [`1c2f19b36`](https://github.com/siderolabs/talos/commit/1c2f19b367af8b04fc49174540e5b141f4b34156) feat: update Kubernetes to 1.28.0-alpha.4 * [`94e9891c1`](https://github.com/siderolabs/talos/commit/94e9891c1bb44a1e7c285b4ccf1fad59ea05aa62) chore: bump sd-boot to v254-rc1 * [`936111ce0`](https://github.com/siderolabs/talos/commit/936111ce062d23ed11b30ea35585c0519260f9c5) fix: properly set up tls for KMS endpoint * [`cb226eec4`](https://github.com/siderolabs/talos/commit/cb226eec46b59372c684c3946e0ba0910066573d) fix: rewrite encryption system information flow * [`3206db528`](https://github.com/siderolabs/talos/commit/3206db52895416d1eb936caa4e953312b34b8549) feat: drop tpm simulator for ukify measure * [`bd4f89f63`](https://github.com/siderolabs/talos/commit/bd4f89f6338423a79b7ce89bda1bd6704caaae59) fix: disable dashboard on Azure, GCP and Scaleway * [`bdb96189f`](https://github.com/siderolabs/talos/commit/bdb96189faadc48e93146f9fd7b03e006bf1dd75) refactor: make maintenance service controller-based * [`d23d04de2`](https://github.com/siderolabs/talos/commit/d23d04de2a5dee30ccf21efe767daf229de78bdb) feat: seed the kernel random pool from the TPM * [`c81ce8cfb`](https://github.com/siderolabs/talos/commit/c81ce8cfb0bc7df66ffd1e1819b64dad6357d890) feat: support controlplane resources configuration * [`74de562b2`](https://github.com/siderolabs/talos/commit/74de562b29c748fda3140871ea3fab99698341ef) fix: mount hugepages with nosuid + nodev * [`ce63abb21`](https://github.com/siderolabs/talos/commit/ce63abb219a2fd4a9d3fdd93a13c343af123efc2) feat: add KMS assisted encryption key handler * [`dafbe9deb`](https://github.com/siderolabs/talos/commit/dafbe9debdee2b015ed574ac4f5f722bce997b31) chore: optimize dockerfile instructions * [`a4289e870`](https://github.com/siderolabs/talos/commit/a4289e8703d9f9e52b739b19b5b38e30a75a1454) chore: fix CLI docs generation stability * [`2fec8388f`](https://github.com/siderolabs/talos/commit/2fec8388fc2fe3058b7b6f141ce9eae2c6a8268f) chore: bump dependencies * [`c1b4262dd`](https://github.com/siderolabs/talos/commit/c1b4262dd60f6cbea6d46a8d0433499bf6365b36) docs: split simple and more complex getting started guides * [`c9a9f9561`](https://github.com/siderolabs/talos/commit/c9a9f95611e38cf5c298f0d9fb0890a9bc0f8b98) refactor: extract secure boot certificate generation * [`6be5a13d5`](https://github.com/siderolabs/talos/commit/6be5a13d5d8341c58d0d2fe75c49ba1de9bf7316) feat: implement machine config documents for event and log streaming * [`e241be85b`](https://github.com/siderolabs/talos/commit/e241be85ba748163268eaeed2a88c8e295f84b28) fix: properly handle YAML comment stripping for multi-doc * [`c02ada7d9`](https://github.com/siderolabs/talos/commit/c02ada7d952255bffe67b3c84f1f832253e1a3b5) fix: capabilities including `ALL` should be uppercase * [`cbdf96d46`](https://github.com/siderolabs/talos/commit/cbdf96d461ec0cf8929c2c76614081ef042dda31) feat: support environment file for extensions * [`35d6adcb9`](https://github.com/siderolabs/talos/commit/35d6adcb9ad7e9420a5bcdfcf3378a05c0b65d46) fix: provide stashed META values before installation * [`258f07449`](https://github.com/siderolabs/talos/commit/258f07449050d69c369fdc71ac613a1a225807bf) fix: ukify cert generation * [`bf3febb7e`](https://github.com/siderolabs/talos/commit/bf3febb7e2bf3ebf1bd66ee088f3885a178c953c) fix: refine OVMF search paths * [`fbebc17f8`](https://github.com/siderolabs/talos/commit/fbebc17f8be7a3ca6c45c3c84d306e52c47d441d) fix: disable LVM backups/archive * [`e5306ef26`](https://github.com/siderolabs/talos/commit/e5306ef2637dd2eb7464691b55159a43933c7419) chore: format and cleanup test scripts * [`bc371ecfd`](https://github.com/siderolabs/talos/commit/bc371ecfdafe51f8cf34461caf9e6f51c0a93108) chore: add `/sbin/shutdown` * [`0d313b973`](https://github.com/siderolabs/talos/commit/0d313b973367906b2fd4bcad4b2def79344dbd67) feat: add `reboot-mode` flag to `talosctl upgrade` * [`7ce87f20c`](https://github.com/siderolabs/talos/commit/7ce87f20c39c615f4d23a3be23780a36008dcb19) fix: compare only basename of `os.Args[0]` in machined * [`53389b1e7`](https://github.com/siderolabs/talos/commit/53389b1e724751e28046167b44f05c6ecf06f184) feat: auto-enroll secure boot keys * [`d77f0bc7b`](https://github.com/siderolabs/talos/commit/d77f0bc7bbe01b7fc8efa21a7c57d73ecb94a01f) docs: fix broken link to powershell module

### Changes from siderolabs/crypto
2 commits

* [`8f77da3`](https://github.com/siderolabs/crypto/commit/8f77da30a5193d207a6660b562a273a06d73aae0) feat: add a method to load PEM key from file * [`c03ff58`](https://github.com/siderolabs/crypto/commit/c03ff58af5051acb9b56e08377200324a3ea1d5e) feat: add a way to represent redacted x509 private keys

### Changes from siderolabs/discovery-api
1 commit

* [`5e3db3c`](https://github.com/siderolabs/discovery-api/commit/5e3db3c1a656ebdc717494e5384f10c7b11eef0f) chore: app optional ControlPlane data

### Changes from siderolabs/discovery-client
1 commit

* [`9ba5f03`](https://github.com/siderolabs/discovery-client/commit/9ba5f033a47d41448153962c5fe22db2d9a8a00c) chore: app optional ControlPlane data

### Changes from siderolabs/extras
3 commits

* [`f415aac`](https://github.com/siderolabs/extras/commit/f415aac20c245592612a02157d247cb2dd4a5d45) feat: update Go to 1.20.6 * [`a73d524`](https://github.com/siderolabs/extras/commit/a73d5243f443fd32376780bf2a4f97b08f28917c) feat: update Go to 1.20.5 * [`36c8ac4`](https://github.com/siderolabs/extras/commit/36c8ac4ab98300059acaad501c2adc8abd39179f) chore: update to Go 1.20.3

### Changes from siderolabs/gen
3 commits

* [`f9f5805`](https://github.com/siderolabs/gen/commit/f9f5805973d30fe6bbac2f4a79ad4197fe59970e) chore: bump rekres and add functions from exp * [`b968d21`](https://github.com/siderolabs/gen/commit/b968d21c9671d97e54317f80cdf781d6f963e44b) feat: add `TryRecv` and `RecvWithContext` functions * [`476dfea`](https://github.com/siderolabs/gen/commit/476dfeae70882e1ca6e5cfed3d6e12dc36841a26) feat: add foreach and clear to lazymap

### Changes from siderolabs/go-blockdevice
4 commits

* [`fbb01f7`](https://github.com/siderolabs/go-blockdevice/commit/fbb01f714bdc9c32ea3459345b730b1043ce10c0) fix: properly detect token not found error * [`3e08968`](https://github.com/siderolabs/go-blockdevice/commit/3e089682439e885c6386f833e35728ce54daff44) fix: do not attach token to a key slot * [`f2c419e`](https://github.com/siderolabs/go-blockdevice/commit/f2c419e81dcba3c5be007130f677d2075e2aec3c) feat: support LUKS token management * [`076874a`](https://github.com/siderolabs/go-blockdevice/commit/076874a155ad44d764d25081125f950e8194d023) chore: resolve blockdevice symlinks

### Changes from siderolabs/go-debug
1 commit

* [`43d9100`](https://github.com/siderolabs/go-debug/commit/43d9100eba3a30ff0d7f1bed0058e6631243cc47) chore: allow enabling pprof manually

### Changes from siderolabs/go-kubernetes
2 commits

* [`69fea5b`](https://github.com/siderolabs/go-kubernetes/commit/69fea5b840fb51aa08e5fbf380fa924b9d444094) feat: support upgrades to Kubernetes 1.28 * [`5a3df5b`](https://github.com/siderolabs/go-kubernetes/commit/5a3df5b002d74ba9f4d773dc1278047481b1d4ba) fix: remove removed APIs for 1.27 upgrade

### Changes from siderolabs/go-loadbalancer
6 commits

* [`574126c`](https://github.com/siderolabs/go-loadbalancer/commit/574126cbf0e1e45a06cabaf602e5070dd7d441e2) chore: add 0.1ms tier and fix tiers * [`5301800`](https://github.com/siderolabs/go-loadbalancer/commit/5301800a874e853d97f8e12195558f79c97c0beb) chore: fix logging and tests * [`b23a173`](https://github.com/siderolabs/go-loadbalancer/commit/b23a1733aa9b303bda82175b4f5e9f8a4765a27b) chore: replace std log with zap * [`1a2f374`](https://github.com/siderolabs/go-loadbalancer/commit/1a2f374df7804dffe683e8be90e9829f2dfb5e95) feat: add multi-tier scoring based for generic List * [`56a27da`](https://github.com/siderolabs/go-loadbalancer/commit/56a27da7083139b71898f4f9207dc40088e8c815) chore: move to siderolabs/tcpproxy of inet.af/tcpproxy * [`f3a0e24`](https://github.com/siderolabs/go-loadbalancer/commit/f3a0e2411e08eef9c79876f3dc6e09e770710379) fix: use SO_LINGER option when doing TCP healthchecks

### Changes from siderolabs/kms-client
3 commits

* [`50064b6`](https://github.com/siderolabs/kms-client/commit/50064b67ac73c0a3f6f89c6a44ef914711107df0) fix: pass context to the key handler in the server wrapper * [`83e0a2e`](https://github.com/siderolabs/kms-client/commit/83e0a2ec6b06668940ec31d64491d9b8a630524b) feat: define API and add reference implementation for KMS server * [`8c37ee8`](https://github.com/siderolabs/kms-client/commit/8c37ee83099a6563197c89166b0ea596eebf0598) Initial commit

### Changes from siderolabs/pkgs
38 commits

* [`d0eaedc`](https://github.com/siderolabs/pkgs/commit/d0eaedcb5cd2510925e4609369e25c3e3572d5fe) feat: enable DM_RAID kernel config * [`d5e0fad`](https://github.com/siderolabs/pkgs/commit/d5e0fad0d59dfb8d2386ab2ad6c7df749e0b9413) feat: update dependencies * [`c644633`](https://github.com/siderolabs/pkgs/commit/c644633324ed1e56ab19f146c04ed3984736a88a) feat: enable multi-gen lru by default * [`75696ba`](https://github.com/siderolabs/pkgs/commit/75696ba81581ef0f1af668db565a08950145e45d) feat: update Go to 1.20.6 * [`205cab6`](https://github.com/siderolabs/pkgs/commit/205cab6d0e6be2721c5338bef232e3345d3a299f) chore: feat use new sd-boot * [`fb817fe`](https://github.com/siderolabs/pkgs/commit/fb817fe20789ca48895275e1877808a9206630dd) fix: enable USB attached SCSI driver on x86 systems * [`43451e6`](https://github.com/siderolabs/pkgs/commit/43451e68a0ddf634b90c7c12cca9437faa52d183) chore: bump dependencies * [`eca94f8`](https://github.com/siderolabs/pkgs/commit/eca94f8f1b9c3ceb62efb53fd1260d49ce17f1dd) feat: enable sriov * [`5a8e8e5`](https://github.com/siderolabs/pkgs/commit/5a8e8e594248847bb606ca07b3ea29e187e20d26) feat: enable VMWARE/HYPERV vsockets * [`edd725a`](https://github.com/siderolabs/pkgs/commit/edd725a0f9d07d39256d98a67be5dc4c56631078) chore: bump deps * [`c0ac69b`](https://github.com/siderolabs/pkgs/commit/c0ac69b70cfac3cdcf100a35f6d766c5ae47d950) feat: enable CONFIG_NVME_{MULTIPATH|AUTH} * [`f7cd916`](https://github.com/siderolabs/pkgs/commit/f7cd916b47975e61c6732079c1c5c4684dfb8c96) fix: bump drbd to 9.2.4 * [`a56d15a`](https://github.com/siderolabs/pkgs/commit/a56d15ad626b6e76a137636d6088361be9a73a9f) fix: copy missing `modules.*` files * [`1eefa66`](https://github.com/siderolabs/pkgs/commit/1eefa664fc7c65491e956a6f403ada774e73a7d3) feat: build isb modem drivers as module * [`a859f4f`](https://github.com/siderolabs/pkgs/commit/a859f4fb257e17fa19b1c10efcae594d33a86618) fix: build RDMA_RXE as a module * [`5fb5e95`](https://github.com/siderolabs/pkgs/commit/5fb5e9517de9fe35e383b96e92fa873aa045a845) feat: bump dependencies * [`39a64b2`](https://github.com/siderolabs/pkgs/commit/39a64b23e2c8689c44b9891b1e70149b8d003655) feat: update Linux to 6.1.31, add GENEVE for arm64 * [`97177be`](https://github.com/siderolabs/pkgs/commit/97177be803cc91c8fabccfec575b7d920bc78c38) feat: update Linux to 6.1.30 * [`b1f9d4e`](https://github.com/siderolabs/pkgs/commit/b1f9d4e717fbd0132b820d45c226ca643d7f577e) chore: prevent unsigned kexec with secureboot * [`9232a42`](https://github.com/siderolabs/pkgs/commit/9232a425b85b1058cd38eab30304f6cf243ab32c) feat: add reproducibility pipelines * [`702d7a7`](https://github.com/siderolabs/pkgs/commit/702d7a7e90099d8fdc9cc4ba50e86c8ba6e91d77) chore: bump deps * [`7958db1`](https://github.com/siderolabs/pkgs/commit/7958db1549a7c7560eeeb8f9c06d3be9487d8804) chore: copy over sd-boot and sd-stub from tools * [`813b3c3`](https://github.com/siderolabs/pkgs/commit/813b3c3d3276d0d9156919307e9ffe521925d40b) chore: revert xfsprogs * [`0cc78ab`](https://github.com/siderolabs/pkgs/commit/0cc78ab82ce920c8fa5654c73738050107e190bb) chore: bump kernel to 6.1.28 * [`70189e3`](https://github.com/siderolabs/pkgs/commit/70189e3df555fed4afade93798d72cd31aad99c5) chore: bump deps * [`c5d3bf1`](https://github.com/siderolabs/pkgs/commit/c5d3bf1985b49e688d29d06db6730834f65ee480) feat: add sd-stub and sd-boot * [`30a7ac2`](https://github.com/siderolabs/pkgs/commit/30a7ac2974fb7580e83819c76502fde77d777ea0) feat: update Linux 6.1.27, containerd 1.6.21 * [`fbc6ee5`](https://github.com/siderolabs/pkgs/commit/fbc6ee55b6ffae44c117255901ab0fbecae79cc3) chore: bump deps * [`82b9489`](https://github.com/siderolabs/pkgs/commit/82b9489b88b108f144b45fb55432576bfd767f91) chore: bump dependencies * [`f37e520`](https://github.com/siderolabs/pkgs/commit/f37e5205cf10fe10296e86565fa018d149f5d8c4) feat: update Linux to 6.1.25 * [`3920b16`](https://github.com/siderolabs/pkgs/commit/3920b163a5c6a6d7c7969155a909a7b2122e65f6) feat: add multi-gen LRU kernel support * [`988f1ec`](https://github.com/siderolabs/pkgs/commit/988f1ecf95536fb259cbd79e044a556728bc7332) feat: update Linux to 6.1.24 * [`5327d12`](https://github.com/siderolabs/pkgs/commit/5327d1263680f76706ea667906ca08222c8398da) fix: remove FB_NVIDIA drivers, Linux 6.1.23 * [`4eae958`](https://github.com/siderolabs/pkgs/commit/4eae958770573613bc29568d130be7aaa775e530) chore: copy over the kernel signing public key * [`174f8fc`](https://github.com/siderolabs/pkgs/commit/174f8fc9c80d871f1c03ea0a53dc8b6eb7112ccf) chore: update Go to 1.20.3 * [`41629b0`](https://github.com/siderolabs/pkgs/commit/41629b03e82bfb77623a812000ef8e98d15d56fa) chore: reorder pkgs for better kernel caching * [`b483a6b`](https://github.com/siderolabs/pkgs/commit/b483a6b01f539b0da13ca09882015044bff24e41) feat: build 'snp.efi' for iPXE * [`fb853ff`](https://github.com/siderolabs/pkgs/commit/fb853ff6b1194cdc1f2412c776347cf4b55c3336) feat: update containerd to 1.6.20

### Changes from siderolabs/tools
20 commits

* [`dc7dd9e`](https://github.com/siderolabs/tools/commit/dc7dd9e5b949f6f5d7626f11cb3b001526e8d1de) chore: remove libseccomp * [`e27c249`](https://github.com/siderolabs/tools/commit/e27c249c3213af6d12be4fb440a8f896c8e1b3d4) feat: update Go to 1.20.6 * [`9b6d512`](https://github.com/siderolabs/tools/commit/9b6d5123fa1e28160019a4b6e8b0f04482c49dc0) feat: use systemd 254-rc1 * [`cd3b692`](https://github.com/siderolabs/tools/commit/cd3b692b0cf5c663548cbe75db43036e11ee1014) chore: bump deps * [`c1027a6`](https://github.com/siderolabs/tools/commit/c1027a63d058b77f6cce7351fa7b63d4c94883ad) chore: remove sbsign * [`e0c76c0`](https://github.com/siderolabs/tools/commit/e0c76c096d06ef11afdb54287d5f15add108399b) chore: bump dependencies * [`7d0cd58`](https://github.com/siderolabs/tools/commit/7d0cd58b34bba6b9415db5e39bed351e7f00d44d) feat: update Go to 1.20.5 * [`150efc2`](https://github.com/siderolabs/tools/commit/150efc22508043bfadc9d84a8c3c5fee6c2aac5f) chore: remove non needed tools * [`88ebb40`](https://github.com/siderolabs/tools/commit/88ebb40dd348b6c9e4dc5551b616e4a1892b4e42) feat: add swtpm * [`4c5d7fe`](https://github.com/siderolabs/tools/commit/4c5d7feb88dcbae2f7bf45f51f9e5e1ba339abac) chore: use same source epoch everywhere * [`2e46e5b`](https://github.com/siderolabs/tools/commit/2e46e5be764f8180a0762a5ab080ccff04534a8a) feat: add reproducibility pipelines * [`c6a41b6`](https://github.com/siderolabs/tools/commit/c6a41b6c5108d676f8573d3dd47ee29ae46e5cc0) fix: add sd-stub assertion patch * [`d2dde48`](https://github.com/siderolabs/tools/commit/d2dde48f72343aa3c541336f5319b8e649e80c87) chore: bump deps * [`8e45ad7`](https://github.com/siderolabs/tools/commit/8e45ad75ea78e353ca3eae21b18da9a42d1edf49) feat: add sbsign * [`271c4a6`](https://github.com/siderolabs/tools/commit/271c4a66b6987d9de2c0d1d69891b5ff277ebd43) feat: add sd-tools * [`eedc294`](https://github.com/siderolabs/tools/commit/eedc294967d415cca40d4c427d3521cd198661d7) chore: bump deps * [`81b09a5`](https://github.com/siderolabs/tools/commit/81b09a5ab204f16306c980eeff518a0d1a37ddf2) feat: add libcap and gnuefi * [`47b0fd3`](https://github.com/siderolabs/tools/commit/47b0fd3e364d4fbcfffe10965f740db7acd82f70) chore: bump go to 1.20.4 * [`ff4cf2b`](https://github.com/siderolabs/tools/commit/ff4cf2beabab310365ad9887abb6234570f5092a) chore: bump deps * [`1563556`](https://github.com/siderolabs/tools/commit/1563556b8f8fdf20d8aa58ac5340104c7ffe732e) feat: update Go to 1.20.3

### Dependency Changes * **github.com/BurntSushi/toml** v1.2.1 -> v1.3.2 * **github.com/aws/aws-sdk-go** v1.44.232 -> v1.44.304 * **github.com/beevik/ntp** v0.3.0 -> v1.2.0 * **github.com/benbjohnson/clock** v1.1.0 -> v1.3.5 * **github.com/cenkalti/backoff/v4** v4.2.0 -> v4.2.1 * **github.com/containerd/containerd** v1.6.19 -> v1.6.21 * **github.com/containerd/typeurl/v2** v2.1.1 **_new_** * **github.com/containernetworking/plugins** v1.2.0 -> v1.3.0 * **github.com/cosi-project/runtime** v0.3.0 -> v0.3.1-alpha.8 * **github.com/docker/distribution** v2.8.1 -> v2.8.2 * **github.com/docker/docker** v23.0.2 -> v24.0.4 * **github.com/ecks/uefi** caef65d070eb **_new_** * **github.com/emicklei/dot** v1.4.2 -> v1.5.0 * **github.com/foxboron/go-uefi** 32187aa193d0 **_new_** * **github.com/google/go-tpm** v0.9.0 **_new_** * **github.com/hashicorp/go-envparse** v0.1.0 **_new_** * **github.com/hetznercloud/hcloud-go** v1.41.0 -> v1.48.0 * **github.com/insomniacslk/dhcp** 74ae03f2425e -> 5648422c16cd * **github.com/jsimonetti/rtnetlink** v1.3.1 -> v1.3.4 * **github.com/mattn/go-isatty** v0.0.18 -> v0.0.19 * **github.com/mdlayher/ethtool** ba3b4bc2e02c -> v0.1.0 * **github.com/mdlayher/genetlink** v1.3.1 -> v1.3.2 * **github.com/mdlayher/netlink** v1.7.1 -> v1.7.2 * **github.com/mdlayher/netx** c711c2f8512f -> 7e21880baee8 * **github.com/nberlee/go-netstat** v0.1.1 -> v0.1.2 * **github.com/opencontainers/go-digest** v1.0.0 **_new_** * **github.com/opencontainers/image-spec** v1.1.0-rc2 -> v1.1.0-rc4 * **github.com/packethost/packngo** v0.29.0 -> v0.30.0 * **github.com/prometheus/procfs** v0.9.0 -> v0.11.0 * **github.com/rivo/tview** 281d14d896d7 -> 6cc0565babaf * **github.com/rs/xid** v1.4.0 -> v1.5.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.15 -> v1.0.0-beta.19 * **github.com/siderolabs/crypto** v0.4.0 -> v0.4.1 * **github.com/siderolabs/discovery-api** v0.1.2 -> v0.1.3 * **github.com/siderolabs/discovery-client** v0.1.4 -> v0.1.5 * **github.com/siderolabs/extras** v1.4.0-1-g9b07505 -> v1.5.0-alpha.0-2-gf415aac * **github.com/siderolabs/gen** v0.4.3 -> v0.4.5 * **github.com/siderolabs/go-blockdevice** v0.4.4 -> v0.4.6 * **github.com/siderolabs/go-debug** v0.2.2 -> v0.2.3 * **github.com/siderolabs/go-kubernetes** v0.2.0 -> v0.2.2 * **github.com/siderolabs/go-loadbalancer** v0.2.1 -> v0.3.2 * **github.com/siderolabs/kms-client** v0.1.0 **_new_** * **github.com/siderolabs/pkgs** v1.4.1-5-ga333a84 -> v1.5.0-alpha.0-37-gd0eaedc * **github.com/siderolabs/talos/pkg/machinery** v1.4.0 -> v1.5.0-alpha.1 * **github.com/siderolabs/tools** v1.4.0-1-g955aabc -> v1.5.0-alpha.0-19-gdc7dd9e * **github.com/spf13/cobra** v1.6.1 -> v1.7.0 * **github.com/stretchr/testify** v1.8.2 -> v1.8.4 * **github.com/vmware-tanzu/sonobuoy** v0.56.16 -> v0.56.17 * **github.com/vmware/govmomi** v0.30.4 -> v0.30.6 * **go.etcd.io/etcd/api/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/pkg/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/etcdutl/v3** v3.5.8 -> v3.5.9 * **golang.org/x/net** v0.8.0 -> v0.12.0 * **golang.org/x/sync** v0.1.0 -> v0.3.0 * **golang.org/x/sys** v0.6.0 -> v0.10.0 * **golang.org/x/term** v0.6.0 -> v0.10.0 * **golang.org/x/text** v0.11.0 **_new_** * **golang.zx2c4.com/wireguard/wgctrl** 9c5414ab4bde -> 925a1e7659e6 * **google.golang.org/grpc** v1.54.0 -> v1.56.2 * **google.golang.org/protobuf** v1.30.0 -> v1.31.0 * **k8s.io/api** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/apimachinery** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/apiserver** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/client-go** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/component-base** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/cri-api** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/klog/v2** v2.90.1 -> v2.100.1 * **k8s.io/kubectl** v0.27.1 -> v0.28.0-alpha.4 * **k8s.io/kubelet** v0.27.1 -> v0.28.0-alpha.4 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.68 -> v1.2.69 Previous release can be found at [v1.4.0](https://github.com/siderolabs/talos/releases/tag/v1.4.0) ## [Talos 1.5.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.5.0-alpha.1) (2023-06-22) Welcome to the v1.5.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Kubernetes API Server In-Cluster Load Balancer Talos now supports configuring the Kubernetes API Server in-cluster load balancer with machine config `features.apiServerBalancerSupport.port` and `features.apiServerBalancerSupport.enabled` fields. If enabled, the loadbalancer binds to `localhost` and runs on the same port on every machine in the cluster. The default value for loadbalancer endpoint is https://localhost:7445. The in-cluster loadbalancer endpoint is used by the `kubelet`, `kube-scheduler`, `kube-controller-manager` and `kube-proxy` by default and can be passed to the CNIs like Cilium and Calico. The in-cluster loadbalancer provides access to the Kubernetes API endpoint even if the external loadbalancer is not healthy, provided that the worker nodes can reach to the controlplane machine addresses directly. ### Predictable Network Interface Names Starting with version Talos 1.5, network interfaces are renamed to [predictable names](https://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/) same way as `systemd` does that in other Linux distributions. The naming schema `enx78e7d1ea46da` (based on MAC addresses) is enabled by default, the order of interface naming decisions is: * firmware/BIOS provided index numbers for on-board devices (example: `eno1`) * firmware/BIOS provided PCI Express hotplug slot index numbers (example: `ens1`) * physical/geographical location of the connector of the hardware (example: `enp2s0`) * interfaces's MAC address (example: `enx78e7d1ea46da`) The predictable network interface names features can be disabled by specifying `net.ifnames=0` in the kernel command line. Talos automatically adds the `net.ifnames=0` kernel argument when upgrading from Talos versions before 1.5. This change doesn't affect "cloud" platforms, like AWS, as Talos automatically adds `net.ifnames=0` to the kernel command line. ### Machine Config option `.machine.install.bootloader` The `.machine.install.bootloader` option in the machine config is deprecated and will be removed in Talos 1.6. This was a no-op for a long time. The bootloader is always installed. ### XFS Quota Talos 1.5+ enables XFS project quota support by default, also enabling by default kubelet feature gate `LocalStorageCapacityIsolationFSQuotaMonitoring` to use xfs quotas to monitor volume usage instead of `du`. This feature is controlled by the `.machine.features.diskQuotaSupport` field in the machine config, it is set to true for new clusters. When upgrading from a previous version, the feature can be enabled by setting the field to true. On the first mount of a volume, the quota information will be recalculated, which may take some time. ### RDMA/RoCE support Talos no longer loads by default `rdma_rxe` Linux driver, which is required for RoCE support. If the driver is required, it can be enabled by specifying `rdma_rxe` in the `.machine.kernel.modules` field in the machine config. ### SecureBoot Talos now supports generating a custom iso that can be used with SecureBoot. Key generation and enrolling has to be done manually. ### Component Updates * Linux: 6.1.35 * containerd: 1.6.21 * runc: 1.1.7 * etcd: 3.5.9 * Kubernetes: 1.27.3 * Flannel: 0.22.0 Talos is built with Go 1.20.5. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Christian Rolland * Nanfei Chen * Spencer Smith * Steve Francis * Alex Corcoles * Alex Corcoles * Alex Lubbock * Budiman Jojo * DJAlPee * Eirik Askheim * Henk Kraal * Michael A. Davis * Michael Fornaro * Nico Berlee * Niklas Wik * Piotr Maksymiuk * Ricky Sadowski * Roee Klinger * Thomas Perronin * Walt Chen * bdronneau ### Changes
133 commits

* [`8daf432b2`](https://github.com/siderolabs/talos/commit/8daf432b2957a8f9d5c59970cf68e7e8414038f5) chore: bump deps * [`e3f3f5794`](https://github.com/siderolabs/talos/commit/e3f3f5794d276433748d0e677ed8476a54f8a98e) feat: implement revert for sd-boot * [`d8b0903d7`](https://github.com/siderolabs/talos/commit/d8b0903d70181afc901d8ddb71bdfa964d4df2cd) docs: vagrant setup document fix * [`fe0f46980`](https://github.com/siderolabs/talos/commit/fe0f46980f348852907218d6f49581efe4b45d49) feat: implement secure boot from disk * [`445f5ad54`](https://github.com/siderolabs/talos/commit/445f5ad5426b125e29d86ff096695399bd01eb32) feat: support API server load balancer * [`19bc223de`](https://github.com/siderolabs/talos/commit/19bc223de8ad878bffe539bda617d5f861af3cfe) refactor: bootloader interface, labels * [`665702ddd`](https://github.com/siderolabs/talos/commit/665702ddd351e902336e6ab81108ea94d61db5c1) chore: fix cilium e2e tests * [`71a548d18`](https://github.com/siderolabs/talos/commit/71a548d18013ee16394921759e819b0fabb43758) chore: generic boootloader implementation * [`e9dbc9311`](https://github.com/siderolabs/talos/commit/e9dbc9311bcbbbcaab2c7eb7f7128013194c234a) test: bump versions for upgrade tests * [`0a99965ef`](https://github.com/siderolabs/talos/commit/0a99965efbdd5dc0d927eb2cbae209dc143c9541) refactor: replace `uncordonNode` with controllers * [`e858bca3a`](https://github.com/siderolabs/talos/commit/e858bca3a2f75d5035710d52229c8142f3eb6982) test: fix cilium integration tests * [`455328d05`](https://github.com/siderolabs/talos/commit/455328d058fba3a5a8b3358820a02e2b4fabad95) fix: allow time skew for generated kubeconfig * [`3ae05648a`](https://github.com/siderolabs/talos/commit/3ae05648ae0a2f79bebd678f85d63d4e5dafde0a) fix: usage of custom kernels * [`0797b0d16`](https://github.com/siderolabs/talos/commit/0797b0d16808d115649a9e0e37b355bbbc2a30b5) chore: add a pipeline to test cloud-images step without a release * [`e5a36268b`](https://github.com/siderolabs/talos/commit/e5a36268b63e588ea6cd2439bf0de356ee07d752) docs: include `allowSchedulingOnControlPlanes` on `talosctl gen config` output * [`c74d93728`](https://github.com/siderolabs/talos/commit/c74d937280c2ec707936a72d07dc2a5dd252c5d2) chore: bump github.com/cosi-project/runtime * [`dbaf5c699`](https://github.com/siderolabs/talos/commit/dbaf5c69978fd1d22737385ddd096798d408254c) refactor: task `labelControlPlane` into controllers * [`1865a0c29`](https://github.com/siderolabs/talos/commit/1865a0c29663a1a78db7ef6e901d450d67a3cbe1) chore: modify some usages that are not recommended * [`3816318b9`](https://github.com/siderolabs/talos/commit/3816318b9e2e205da0c949c0ec59a087decd0b78) chore: wrap config.Provider in atomic wrapper * [`d04cf1978`](https://github.com/siderolabs/talos/commit/d04cf19788df20c802eadb9678570a4f15d339b2) chore: clean up unnecessary self assignment * [`a34a94898`](https://github.com/siderolabs/talos/commit/a34a948985fed7c3054c4342c48e0e0620569625) fix: copy missing modules.* files * [`f5e3272fc`](https://github.com/siderolabs/talos/commit/f5e3272fce641a878eefa66437d28d3ed9917ab6) refactor: task 'updateBootLoader' as controller * [`e7be6ee7c`](https://github.com/siderolabs/talos/commit/e7be6ee7c3636eebd557d93e440e9749c8093360) refactor: make event log streaming fully reactive * [`aef2192a6`](https://github.com/siderolabs/talos/commit/aef2192a6584e7934086eae0caab6faba52a8ac1) chore: use fixed module list * [`c719aa231`](https://github.com/siderolabs/talos/commit/c719aa2316bffa3b614d27d630ea3d8731684f4e) fix: allow http:// for discovery service URL * [`39134d8d5`](https://github.com/siderolabs/talos/commit/39134d8d5304cec5e1a1c5fe23f62ed957241213) chore: fix cron pipeline * [`a61dcdbbd`](https://github.com/siderolabs/talos/commit/a61dcdbbd5c917b49c810108ff96854ad51269b1) fix: don't load RDMA over Ethernet driver by default * [`aac441f61`](https://github.com/siderolabs/talos/commit/aac441f618ac60f2298d9e17a2044916f7da9d69) chore: update Go to 1.20.5, bump dependencies * [`1c0c7933d`](https://github.com/siderolabs/talos/commit/1c0c7933dfef23544e2fb0fc04c4c5ad7d5b5d9b) chore: cleanup partition code * [`31b988281`](https://github.com/siderolabs/talos/commit/31b988281efb9d0c66975bbfc20b893ad32c161d) docs: add some words about certifcates * [`e912c0dfc`](https://github.com/siderolabs/talos/commit/e912c0dfcf515c5a6c852f4b935c9b48e61b13f1) chore: use go-blockdevice for zeroing partitions * [`e6dde8ffc`](https://github.com/siderolabs/talos/commit/e6dde8ffc50e435a42d11eb96cf6aea2cf3520ca) feat: add network chaos to qemu development environment * [`47986cb79`](https://github.com/siderolabs/talos/commit/47986cb79eb30c6e9c0d091ee37b2b1c2f20885c) chore: unify kexec phase * [`3a865370f`](https://github.com/siderolabs/talos/commit/3a865370f5152243e08a69626de023f924e22689) feat: qemu secureboot * [`5dab45e86`](https://github.com/siderolabs/talos/commit/5dab45e86917837b0991a62ab94a7b96b3ef777e) refactor: allow kmsg log streaming to be reconfigured on the fly * [`8a02ecd4c`](https://github.com/siderolabs/talos/commit/8a02ecd4cb97bcaafe5761d464fec8a4e44b672f) chore: add endpoints balancer controller * [`423a31ac9`](https://github.com/siderolabs/talos/commit/423a31ac9d8f28c2bcf00794bacf5446e43fc0b7) chore: deprectae `bootloader` installer option * [`cdfece7d6`](https://github.com/siderolabs/talos/commit/cdfece7d64a9269afcc213f8d604d0b7e525cb8a) chore: optimize image compression * [`bfc341937`](https://github.com/siderolabs/talos/commit/bfc34193762cb309ef2230f4d79673c4a56f4db5) chore: add default console args * [`2749aeeda`](https://github.com/siderolabs/talos/commit/2749aeeda0451b286369d911696070e2cf4359e9) feat: add support for multi-doc strategic merge patching * [`3f68485e4`](https://github.com/siderolabs/talos/commit/3f68485e44800a0c50b5855531ec10507e7d0df9) feat: add uki iso generation * [`bab484a40`](https://github.com/siderolabs/talos/commit/bab484a405cb598d1c5f35f7602c2ac27e6efa97) feat: use stable network interface names * [`196dfb99b`](https://github.com/siderolabs/talos/commit/196dfb99b0329d5c52fd7089e62fbfa1b09df3c6) fix: do not probe kernel args in dashboard if not needed * [`8c071b579`](https://github.com/siderolabs/talos/commit/8c071b5796db05ecb17e46295eb2140827a58ca8) fix: skip DHCP RENEW if server IP in the lease is all zeroes * [`badbc51e6`](https://github.com/siderolabs/talos/commit/badbc51e63b685e22fffb82ae294a35cd9f65922) refactor: rewrite code to include preliminary support for multi-doc * [`ecce29dee`](https://github.com/siderolabs/talos/commit/ecce29dee9625842e419496e18560291ef90b1b5) fix: upgrade-k8s use internal IP first, external IP fallback * [`3c64a5ffb`](https://github.com/siderolabs/talos/commit/3c64a5ffba2109ccf5102f71652e54def52f8dbf) chore: optimize image generation time * [`2292f36d9`](https://github.com/siderolabs/talos/commit/2292f36d970d3edcf39b5d5f12d0051d7d75f390) chore: registry.k8s.io for coredns image * [`f2b258b37`](https://github.com/siderolabs/talos/commit/f2b258b3733a8fcc34bccde3bf01855a512d519a) docs: document talosctl version for upgrades * [`a0773f783`](https://github.com/siderolabs/talos/commit/a0773f783cfb3cfab8cbbeffb6449159754d785e) chore: add ukify Go script * [`b69e38d1f`](https://github.com/siderolabs/talos/commit/b69e38d1ff069ba8fac7a6524621f8b3c7256238) chore: bump dependencies * [`adce65103`](https://github.com/siderolabs/talos/commit/adce65103424f9f895e6b8c4858b27b3eb6bd74b) docs: add piraeus/drbd to storage documentation * [`a982cabe7`](https://github.com/siderolabs/talos/commit/a982cabe7011c87e863f7bb0829921e927ddf782) docs: link support matrix in k8s update doc * [`1fb29a56a`](https://github.com/siderolabs/talos/commit/1fb29a56a8abe5d72b8a3a336693e798424c63e0) fix: fail quickly if upgrade-k8s is used with multiple nodes * [`51d931c47`](https://github.com/siderolabs/talos/commit/51d931c4705fc7ca0bdadc59d732e56fae318dda) chore: faster dev cycle * [`dc6764871`](https://github.com/siderolabs/talos/commit/dc6764871c9e732b88f7cddc1784e943e9d952bb) refactor: move around config interfaces, make RawV1Alpha1 typed * [`ea9a97dba`](https://github.com/siderolabs/talos/commit/ea9a97dba38c6ab2de830e3b0c3d202d22bdb668) fix: fall back to external IP when discovering nodes in upgrade-k8s * [`0bb7e8a5c`](https://github.com/siderolabs/talos/commit/0bb7e8a5cf8b8f3bf31d9f8c3a85b4153921c126) refactor: split config.Provider into Config & Container * [`85d8a1619`](https://github.com/siderolabs/talos/commit/85d8a1619431989eb05cb15ad01a1bc06b0f63e9) chore: bump deps * [`39b7a56f0`](https://github.com/siderolabs/talos/commit/39b7a56f01d41d33eb96a0feb6e34d43965a99fd) chore: use 8GiB instead of 10GiB for cloud images * [`ff11fd39c`](https://github.com/siderolabs/talos/commit/ff11fd39c723a40c01abe6348f64b1f892856175) fix: race with `udevd` and `mountUserDisks` * [`c3fabb982`](https://github.com/siderolabs/talos/commit/c3fabb9829d12353770d6436a1d726b15820ebce) chore: update default image sizes to 10GB for all "cloud" images * [`10155c390`](https://github.com/siderolabs/talos/commit/10155c390e87898098426600709657fbd51e02e8) feat: enable xfs project quota support, kubelet feature * [`eba818564`](https://github.com/siderolabs/talos/commit/eba81856427dd3f6c0cf317f027e63d65a079029) release(v1.5.0-alpha.0): prepare release * [`383471c3e`](https://github.com/siderolabs/talos/commit/383471c3e956ff6e077a1de75b02a50835fbf352) feat: update default Kubernetes to v1.27.2 * [`8f68d1abe`](https://github.com/siderolabs/talos/commit/8f68d1abeff83c3ff0e6c5d9f61cb14807b44ca5) chore: bump deps * [`e0c1585d3`](https://github.com/siderolabs/talos/commit/e0c1585d3047ef213134331dc57f8e2e8c23a93d) feat: create azure community gallery image version on release * [`dd8336c9e`](https://github.com/siderolabs/talos/commit/dd8336c9ee7f8a3a44d45c9f9e3cbbf741f84c44) fix: refresh kubelet self-issued serving certificates * [`bb02dd263`](https://github.com/siderolabs/talos/commit/bb02dd263cbc5e7e3839148d86a4a0a5f7ea998b) chore: drop deprecated stuff for Talos 1.5 * [`61cad8673`](https://github.com/siderolabs/talos/commit/61cad86731e5c0aa80d7df41ea02d0b7ff579c45) chore: bump deps * [`01dfd3af7`](https://github.com/siderolabs/talos/commit/01dfd3af7d64dacd179d17d9d5eaf4bc44cf72af) feat: update etcd to v3.5.9 * [`aa65fbb8a`](https://github.com/siderolabs/talos/commit/aa65fbb8a1752a70e7bac4e4e9872f35e88d1cc9) chore: update KUBECTL_URL to reflect the community bucket * [`cc3128d94`](https://github.com/siderolabs/talos/commit/cc3128d944abacfb633bc783b7fed6d0a6f80661) chore: bump kernel to 6.1.28 * [`97fffaf78`](https://github.com/siderolabs/talos/commit/97fffaf78a0b9a1dc67709de11d37ea20aefde59) chore: use ctest.UpdateWithConflicts instead of plain UpdateWithConflicts * [`3b36993b9`](https://github.com/siderolabs/talos/commit/3b36993b9926392f4290e6fabc82e635f4c98149) fix: rlimit nofile test * [`45e6e27af`](https://github.com/siderolabs/talos/commit/45e6e27af75746fd0cc8b0f98a2d14579eb0ed40) chore: bump runtime * [`4f720d465`](https://github.com/siderolabs/talos/commit/4f720d46532af39165fc5051052d5c42595d91af) fix: revert: set rlimit explicitly in wrapperd * [`a2565f674`](https://github.com/siderolabs/talos/commit/a2565f67416e9b9bc22f2d5506df9ea7771c0c8c) fix: set rlimit explicitly in wrapperd * [`cdfc242b8`](https://github.com/siderolabs/talos/commit/cdfc242b8354f4cc4e7ce51bbe3a8fb20b35995d) chore: re-enable Go buildid * [`e67f3f5c5`](https://github.com/siderolabs/talos/commit/e67f3f5c5453f947355194ea9656c15ff008c35e) feat: linux 6.1.27, containerd 1.6.21, go 1.20.4 * [`55ae59a0a`](https://github.com/siderolabs/talos/commit/55ae59a0ad71293676b3efed461f5ab98101401a) fix: properly skip/cleanup controlplane configs for workers * [`64eade9bd`](https://github.com/siderolabs/talos/commit/64eade9bde271bce4e629e6ac09407c8c42e01be) chore: clean up unused constant * [`62c6e9655`](https://github.com/siderolabs/talos/commit/62c6e9655cb639d4993aaa4c9b364342688599cb) feat: introduce siderolink config resource & reconnect * [`860002c73`](https://github.com/siderolabs/talos/commit/860002c7352bedd10845e11da37c80685ff0e720) fix: don't reload control plane pods on cert SANs changes * [`d43c61e80`](https://github.com/siderolabs/talos/commit/d43c61e80f5b05b81f2a021cdfe012e500c3d98e) fix: enforce nolock option for all NFS mounts by default * [`339986db9`](https://github.com/siderolabs/talos/commit/339986db9d3675b78ce0d268f799ad654862fb0f) fix: inhibit timer to follow kubelet timer * [`cbf6dc100`](https://github.com/siderolabs/talos/commit/cbf6dc1009ad47a2804774839e4e0301efa8ac78) fix: set timeout for unmount calls * [`b58f913d5`](https://github.com/siderolabs/talos/commit/b58f913d5f4b8ecf39be183d0bafe1109f0f0737) fix: set the static pod priority as values * [`f8a7a5b6b`](https://github.com/siderolabs/talos/commit/f8a7a5b6bf4138a33cbe5c9afe85db99de167aec) docs: add information about KubeSpan ports and topology * [`2bad74d64`](https://github.com/siderolabs/talos/commit/2bad74d6423c083ec34f1b422f23b0024d5f8798) docs: add how to on scaling down * [`7442ff8b0`](https://github.com/siderolabs/talos/commit/7442ff8b095ef1337f54332a71d08053a2832144) chore: fix typos inteface -> interface (docs and tests) * [`d4e94f7a1`](https://github.com/siderolabs/talos/commit/d4e94f7a15acf7f3c9e7532b067cdacd0e805bec) fix: add back required TARGETARCH for installer * [`e6fffda01`](https://github.com/siderolabs/talos/commit/e6fffda01385a2daaa901a5742f30a4edc9186a7) chore: linux 6.1.26, runc 1.1.7 * [`344746ae2`](https://github.com/siderolabs/talos/commit/344746ae2fa038b704d02fec04c3d358762fe938) fix: bump max inhibit delay to 20 min * [`d9bdea2b5`](https://github.com/siderolabs/talos/commit/d9bdea2b54772f067783ee64eb85c834957d386a) chore: fork docs and compatibility modules for Talos 1.5 * [`3d99610fc`](https://github.com/siderolabs/talos/commit/3d99610fc9b0d0084be822be29bb1bf2fbe85833) docs: document building, verifying image and process caps * [`014008ea2`](https://github.com/siderolabs/talos/commit/014008ea25208afbeabb42ef89238802705ad4e0) fix: udevd rules trigger * [`9b36bb613`](https://github.com/siderolabs/talos/commit/9b36bb613b44f182e47ae63bc74e4a8b6342d68d) feat: update Linux to 6.1.25, fix virtio on arm64 * [`08ec66c55`](https://github.com/siderolabs/talos/commit/08ec66c55ccca3f9aa82a9703ebf183913b19a7e) feat: clean up (garbage collect) system images which are not referenced * [`b097efcde`](https://github.com/siderolabs/talos/commit/b097efcde29c20cdc4fed23fe8366bd683db634c) fix: display correct number of machines on dashboard * [`cad43f0ad`](https://github.com/siderolabs/talos/commit/cad43f0ad3bc2ede8a6ae81767c9226b6bc69f19) chore: remove k8s master label * [`e296a566e`](https://github.com/siderolabs/talos/commit/e296a566e6efb0cbdd119e73aff1feaa772d38bd) fix: support kernel userspace module loading * [`103f0ffdd`](https://github.com/siderolabs/talos/commit/103f0ffdd3ebd57a5086852f3502a8a7d4428faa) feat: add startup probes to controller-manager and scheduler * [`5a1ae8aae`](https://github.com/siderolabs/talos/commit/5a1ae8aae89e54d5540586d6f2e99ef3e80a72eb) chore: bump dependences * [`ec8c8dbaf`](https://github.com/siderolabs/talos/commit/ec8c8dbafcdaf63d036bdba92fa153d4d1c90100) chore: fix container image reproducibility * [`f661d8487`](https://github.com/siderolabs/talos/commit/f661d84877e6db5bc8856b982990926dcbfe949c) fix: allow `talosctl cp` to handle special files in `/proc` * [`2d824b563`](https://github.com/siderolabs/talos/commit/2d824b5639a4b8c3b673d13b08b2b97c69aafe0d) fix: do not show control plane status for workers on dashboard * [`e5491ddad`](https://github.com/siderolabs/talos/commit/e5491ddadeb1776bd5c17dd35917e05ec4847d0f) docs: update documentation for nocloud * [`7a004a6f7`](https://github.com/siderolabs/talos/commit/7a004a6f7f47fa5d17e855eb02650754d8411574) fix: parse errors correctly * [`374ef5385`](https://github.com/siderolabs/talos/commit/374ef53853947811dc221d99751cf0e16294508c) test: submit verbose flag to e2e tests * [`e1d38b6fe`](https://github.com/siderolabs/talos/commit/e1d38b6febf26fe31a6b9d6ed8f9b6bdba29aa3b) feat: show template URL in dashboard config URL tab * [`45d7f0ce9`](https://github.com/siderolabs/talos/commit/45d7f0ce95454ce85c403fc493ddb97e4d478238) docs: fix the latest url * [`96efbf147`](https://github.com/siderolabs/talos/commit/96efbf14769579d514ef9c75d01d9f44d276113a) docs: activate 1.4.0 docs by default * [`8c1f515b1`](https://github.com/siderolabs/talos/commit/8c1f515b1b8e40bce42e2fc04755afe5bf8a56aa) feat: update Linux to 6.1.24 * [`8689bef5f`](https://github.com/siderolabs/talos/commit/8689bef5f10839091cf131edb6c8efad4ccba034) docs: update documentation for Talos 1.4 * [`a781dfb8e`](https://github.com/siderolabs/talos/commit/a781dfb8e3ded67edcb2a6a1048bfe76c6bd0d24) feat: update Kubernetes to 1.27.1 * [`a737dd83a`](https://github.com/siderolabs/talos/commit/a737dd83a4cd7549f85f8df0882f1c9a4446060d) chore: typo in `compatibility.ParseKubernetesVersion` * [`f14928b0a`](https://github.com/siderolabs/talos/commit/f14928b0a9dd3d85664605f4f6a206236ea94614) fix: fix dashboard crash when a non-existent node is specified * [`3e406d9b0`](https://github.com/siderolabs/talos/commit/3e406d9b07c0e67a2fb61e612bc3f378f3c35247) feat: update etcd to v3.5.8 * [`bd1cff3e8`](https://github.com/siderolabs/talos/commit/bd1cff3e83530b9b89b27d8083ea8f3f0cf6ede4) chore: remove Go buildid * [`e31f7f50b`](https://github.com/siderolabs/talos/commit/e31f7f50b1b455beb98cd25859a44bbbccc1ff64) feat: update Kubernetes to 1.27.0 * [`aa3640d74`](https://github.com/siderolabs/talos/commit/aa3640d74ce2e3619476453381909fa3520eb87d) docs: update storage.md * [`07bb61e60`](https://github.com/siderolabs/talos/commit/07bb61e60c53b267756dc97874b9c9554f2b1486) chore: module-sig-verify cleanup * [`5e9d836c3`](https://github.com/siderolabs/talos/commit/5e9d836c3d075c3edb2d48b2868c31a1c963e2de) chore: add kernel module signtaure verification * [`3cd1c6bb0`](https://github.com/siderolabs/talos/commit/3cd1c6bb0b83e5747a7356140a44b16deb4727e6) fix: send 'STOP' event on phase end * [`5176d27dc`](https://github.com/siderolabs/talos/commit/5176d27dc566d8689bb305398da7250269ebe9a3) feat: update Kubernetes to 1.27.0-rc.1 * [`2c55550a6`](https://github.com/siderolabs/talos/commit/2c55550a66b49b49d8dc95b83516b7c0f8107300) fix: quote ISO kernel args for GRUB * [`319d76e38`](https://github.com/siderolabs/talos/commit/319d76e38978406d8d37e89ada2c403969d6c972) fix: respect BROWSER=echo in client auth interceptor * [`4e4ace839`](https://github.com/siderolabs/talos/commit/4e4ace839c0f558e7b00979fa4c64c32985aa3ce) chore: update Go to 1.20.3 * [`170f73899`](https://github.com/siderolabs/talos/commit/170f73899a3bf29e9c6f76fdc5e510be08edf4aa) fix: correctly parse static pod phase * [`c3a595d5b`](https://github.com/siderolabs/talos/commit/c3a595d5b7d3c7c3091229caef6b2553416edb56) fix: improve action tracking post checks * [`eb01edbc8`](https://github.com/siderolabs/talos/commit/eb01edbc8a0ef5810693afe450861d5b63877b72) fix: rework DHCP flow * [`e095150a6`](https://github.com/siderolabs/talos/commit/e095150a6e34cbdc805a2cac85ec7f28f98629b4) test: bump CAPI components versions

### Changes since v1.5.0-alpha.0
63 commits

* [`8daf432b2`](https://github.com/siderolabs/talos/commit/8daf432b2957a8f9d5c59970cf68e7e8414038f5) chore: bump deps * [`e3f3f5794`](https://github.com/siderolabs/talos/commit/e3f3f5794d276433748d0e677ed8476a54f8a98e) feat: implement revert for sd-boot * [`d8b0903d7`](https://github.com/siderolabs/talos/commit/d8b0903d70181afc901d8ddb71bdfa964d4df2cd) docs: vagrant setup document fix * [`fe0f46980`](https://github.com/siderolabs/talos/commit/fe0f46980f348852907218d6f49581efe4b45d49) feat: implement secure boot from disk * [`445f5ad54`](https://github.com/siderolabs/talos/commit/445f5ad5426b125e29d86ff096695399bd01eb32) feat: support API server load balancer * [`19bc223de`](https://github.com/siderolabs/talos/commit/19bc223de8ad878bffe539bda617d5f861af3cfe) refactor: bootloader interface, labels * [`665702ddd`](https://github.com/siderolabs/talos/commit/665702ddd351e902336e6ab81108ea94d61db5c1) chore: fix cilium e2e tests * [`71a548d18`](https://github.com/siderolabs/talos/commit/71a548d18013ee16394921759e819b0fabb43758) chore: generic boootloader implementation * [`e9dbc9311`](https://github.com/siderolabs/talos/commit/e9dbc9311bcbbbcaab2c7eb7f7128013194c234a) test: bump versions for upgrade tests * [`0a99965ef`](https://github.com/siderolabs/talos/commit/0a99965efbdd5dc0d927eb2cbae209dc143c9541) refactor: replace `uncordonNode` with controllers * [`e858bca3a`](https://github.com/siderolabs/talos/commit/e858bca3a2f75d5035710d52229c8142f3eb6982) test: fix cilium integration tests * [`455328d05`](https://github.com/siderolabs/talos/commit/455328d058fba3a5a8b3358820a02e2b4fabad95) fix: allow time skew for generated kubeconfig * [`3ae05648a`](https://github.com/siderolabs/talos/commit/3ae05648ae0a2f79bebd678f85d63d4e5dafde0a) fix: usage of custom kernels * [`0797b0d16`](https://github.com/siderolabs/talos/commit/0797b0d16808d115649a9e0e37b355bbbc2a30b5) chore: add a pipeline to test cloud-images step without a release * [`e5a36268b`](https://github.com/siderolabs/talos/commit/e5a36268b63e588ea6cd2439bf0de356ee07d752) docs: include `allowSchedulingOnControlPlanes` on `talosctl gen config` output * [`c74d93728`](https://github.com/siderolabs/talos/commit/c74d937280c2ec707936a72d07dc2a5dd252c5d2) chore: bump github.com/cosi-project/runtime * [`dbaf5c699`](https://github.com/siderolabs/talos/commit/dbaf5c69978fd1d22737385ddd096798d408254c) refactor: task `labelControlPlane` into controllers * [`1865a0c29`](https://github.com/siderolabs/talos/commit/1865a0c29663a1a78db7ef6e901d450d67a3cbe1) chore: modify some usages that are not recommended * [`3816318b9`](https://github.com/siderolabs/talos/commit/3816318b9e2e205da0c949c0ec59a087decd0b78) chore: wrap config.Provider in atomic wrapper * [`d04cf1978`](https://github.com/siderolabs/talos/commit/d04cf19788df20c802eadb9678570a4f15d339b2) chore: clean up unnecessary self assignment * [`a34a94898`](https://github.com/siderolabs/talos/commit/a34a948985fed7c3054c4342c48e0e0620569625) fix: copy missing modules.* files * [`f5e3272fc`](https://github.com/siderolabs/talos/commit/f5e3272fce641a878eefa66437d28d3ed9917ab6) refactor: task 'updateBootLoader' as controller * [`e7be6ee7c`](https://github.com/siderolabs/talos/commit/e7be6ee7c3636eebd557d93e440e9749c8093360) refactor: make event log streaming fully reactive * [`aef2192a6`](https://github.com/siderolabs/talos/commit/aef2192a6584e7934086eae0caab6faba52a8ac1) chore: use fixed module list * [`c719aa231`](https://github.com/siderolabs/talos/commit/c719aa2316bffa3b614d27d630ea3d8731684f4e) fix: allow http:// for discovery service URL * [`39134d8d5`](https://github.com/siderolabs/talos/commit/39134d8d5304cec5e1a1c5fe23f62ed957241213) chore: fix cron pipeline * [`a61dcdbbd`](https://github.com/siderolabs/talos/commit/a61dcdbbd5c917b49c810108ff96854ad51269b1) fix: don't load RDMA over Ethernet driver by default * [`aac441f61`](https://github.com/siderolabs/talos/commit/aac441f618ac60f2298d9e17a2044916f7da9d69) chore: update Go to 1.20.5, bump dependencies * [`1c0c7933d`](https://github.com/siderolabs/talos/commit/1c0c7933dfef23544e2fb0fc04c4c5ad7d5b5d9b) chore: cleanup partition code * [`31b988281`](https://github.com/siderolabs/talos/commit/31b988281efb9d0c66975bbfc20b893ad32c161d) docs: add some words about certifcates * [`e912c0dfc`](https://github.com/siderolabs/talos/commit/e912c0dfcf515c5a6c852f4b935c9b48e61b13f1) chore: use go-blockdevice for zeroing partitions * [`e6dde8ffc`](https://github.com/siderolabs/talos/commit/e6dde8ffc50e435a42d11eb96cf6aea2cf3520ca) feat: add network chaos to qemu development environment * [`47986cb79`](https://github.com/siderolabs/talos/commit/47986cb79eb30c6e9c0d091ee37b2b1c2f20885c) chore: unify kexec phase * [`3a865370f`](https://github.com/siderolabs/talos/commit/3a865370f5152243e08a69626de023f924e22689) feat: qemu secureboot * [`5dab45e86`](https://github.com/siderolabs/talos/commit/5dab45e86917837b0991a62ab94a7b96b3ef777e) refactor: allow kmsg log streaming to be reconfigured on the fly * [`8a02ecd4c`](https://github.com/siderolabs/talos/commit/8a02ecd4cb97bcaafe5761d464fec8a4e44b672f) chore: add endpoints balancer controller * [`423a31ac9`](https://github.com/siderolabs/talos/commit/423a31ac9d8f28c2bcf00794bacf5446e43fc0b7) chore: deprectae `bootloader` installer option * [`cdfece7d6`](https://github.com/siderolabs/talos/commit/cdfece7d64a9269afcc213f8d604d0b7e525cb8a) chore: optimize image compression * [`bfc341937`](https://github.com/siderolabs/talos/commit/bfc34193762cb309ef2230f4d79673c4a56f4db5) chore: add default console args * [`2749aeeda`](https://github.com/siderolabs/talos/commit/2749aeeda0451b286369d911696070e2cf4359e9) feat: add support for multi-doc strategic merge patching * [`3f68485e4`](https://github.com/siderolabs/talos/commit/3f68485e44800a0c50b5855531ec10507e7d0df9) feat: add uki iso generation * [`bab484a40`](https://github.com/siderolabs/talos/commit/bab484a405cb598d1c5f35f7602c2ac27e6efa97) feat: use stable network interface names * [`196dfb99b`](https://github.com/siderolabs/talos/commit/196dfb99b0329d5c52fd7089e62fbfa1b09df3c6) fix: do not probe kernel args in dashboard if not needed * [`8c071b579`](https://github.com/siderolabs/talos/commit/8c071b5796db05ecb17e46295eb2140827a58ca8) fix: skip DHCP RENEW if server IP in the lease is all zeroes * [`badbc51e6`](https://github.com/siderolabs/talos/commit/badbc51e63b685e22fffb82ae294a35cd9f65922) refactor: rewrite code to include preliminary support for multi-doc * [`ecce29dee`](https://github.com/siderolabs/talos/commit/ecce29dee9625842e419496e18560291ef90b1b5) fix: upgrade-k8s use internal IP first, external IP fallback * [`3c64a5ffb`](https://github.com/siderolabs/talos/commit/3c64a5ffba2109ccf5102f71652e54def52f8dbf) chore: optimize image generation time * [`2292f36d9`](https://github.com/siderolabs/talos/commit/2292f36d970d3edcf39b5d5f12d0051d7d75f390) chore: registry.k8s.io for coredns image * [`f2b258b37`](https://github.com/siderolabs/talos/commit/f2b258b3733a8fcc34bccde3bf01855a512d519a) docs: document talosctl version for upgrades * [`a0773f783`](https://github.com/siderolabs/talos/commit/a0773f783cfb3cfab8cbbeffb6449159754d785e) chore: add ukify Go script * [`b69e38d1f`](https://github.com/siderolabs/talos/commit/b69e38d1ff069ba8fac7a6524621f8b3c7256238) chore: bump dependencies * [`adce65103`](https://github.com/siderolabs/talos/commit/adce65103424f9f895e6b8c4858b27b3eb6bd74b) docs: add piraeus/drbd to storage documentation * [`a982cabe7`](https://github.com/siderolabs/talos/commit/a982cabe7011c87e863f7bb0829921e927ddf782) docs: link support matrix in k8s update doc * [`1fb29a56a`](https://github.com/siderolabs/talos/commit/1fb29a56a8abe5d72b8a3a336693e798424c63e0) fix: fail quickly if upgrade-k8s is used with multiple nodes * [`51d931c47`](https://github.com/siderolabs/talos/commit/51d931c4705fc7ca0bdadc59d732e56fae318dda) chore: faster dev cycle * [`dc6764871`](https://github.com/siderolabs/talos/commit/dc6764871c9e732b88f7cddc1784e943e9d952bb) refactor: move around config interfaces, make RawV1Alpha1 typed * [`ea9a97dba`](https://github.com/siderolabs/talos/commit/ea9a97dba38c6ab2de830e3b0c3d202d22bdb668) fix: fall back to external IP when discovering nodes in upgrade-k8s * [`0bb7e8a5c`](https://github.com/siderolabs/talos/commit/0bb7e8a5cf8b8f3bf31d9f8c3a85b4153921c126) refactor: split config.Provider into Config & Container * [`85d8a1619`](https://github.com/siderolabs/talos/commit/85d8a1619431989eb05cb15ad01a1bc06b0f63e9) chore: bump deps * [`39b7a56f0`](https://github.com/siderolabs/talos/commit/39b7a56f01d41d33eb96a0feb6e34d43965a99fd) chore: use 8GiB instead of 10GiB for cloud images * [`ff11fd39c`](https://github.com/siderolabs/talos/commit/ff11fd39c723a40c01abe6348f64b1f892856175) fix: race with `udevd` and `mountUserDisks` * [`c3fabb982`](https://github.com/siderolabs/talos/commit/c3fabb9829d12353770d6436a1d726b15820ebce) chore: update default image sizes to 10GB for all "cloud" images * [`10155c390`](https://github.com/siderolabs/talos/commit/10155c390e87898098426600709657fbd51e02e8) feat: enable xfs project quota support, kubelet feature

### Changes from siderolabs/discovery-api
1 commit

* [`5e3db3c`](https://github.com/siderolabs/discovery-api/commit/5e3db3c1a656ebdc717494e5384f10c7b11eef0f) chore: app optional ControlPlane data

### Changes from siderolabs/discovery-client
1 commit

* [`9ba5f03`](https://github.com/siderolabs/discovery-client/commit/9ba5f033a47d41448153962c5fe22db2d9a8a00c) chore: app optional ControlPlane data

### Changes from siderolabs/extras
2 commits

* [`a73d524`](https://github.com/siderolabs/extras/commit/a73d5243f443fd32376780bf2a4f97b08f28917c) feat: update Go to 1.20.5 * [`36c8ac4`](https://github.com/siderolabs/extras/commit/36c8ac4ab98300059acaad501c2adc8abd39179f) chore: update to Go 1.20.3

### Changes from siderolabs/gen
3 commits

* [`f9f5805`](https://github.com/siderolabs/gen/commit/f9f5805973d30fe6bbac2f4a79ad4197fe59970e) chore: bump rekres and add functions from exp * [`b968d21`](https://github.com/siderolabs/gen/commit/b968d21c9671d97e54317f80cdf781d6f963e44b) feat: add `TryRecv` and `RecvWithContext` functions * [`476dfea`](https://github.com/siderolabs/gen/commit/476dfeae70882e1ca6e5cfed3d6e12dc36841a26) feat: add foreach and clear to lazymap

### Changes from siderolabs/go-blockdevice
1 commit

* [`076874a`](https://github.com/siderolabs/go-blockdevice/commit/076874a155ad44d764d25081125f950e8194d023) chore: resolve blockdevice symlinks

### Changes from siderolabs/go-kubernetes
1 commit

* [`5a3df5b`](https://github.com/siderolabs/go-kubernetes/commit/5a3df5b002d74ba9f4d773dc1278047481b1d4ba) fix: remove removed APIs for 1.27 upgrade

### Changes from siderolabs/go-loadbalancer
5 commits

* [`5301800`](https://github.com/siderolabs/go-loadbalancer/commit/5301800a874e853d97f8e12195558f79c97c0beb) chore: fix logging and tests * [`b23a173`](https://github.com/siderolabs/go-loadbalancer/commit/b23a1733aa9b303bda82175b4f5e9f8a4765a27b) chore: replace std log with zap * [`1a2f374`](https://github.com/siderolabs/go-loadbalancer/commit/1a2f374df7804dffe683e8be90e9829f2dfb5e95) feat: add multi-tier scoring based for generic List * [`56a27da`](https://github.com/siderolabs/go-loadbalancer/commit/56a27da7083139b71898f4f9207dc40088e8c815) chore: move to siderolabs/tcpproxy of inet.af/tcpproxy * [`f3a0e24`](https://github.com/siderolabs/go-loadbalancer/commit/f3a0e2411e08eef9c79876f3dc6e09e770710379) fix: use SO_LINGER option when doing TCP healthchecks

### Changes from siderolabs/pkgs
29 commits

* [`edd725a`](https://github.com/siderolabs/pkgs/commit/edd725a0f9d07d39256d98a67be5dc4c56631078) chore: bump deps * [`c0ac69b`](https://github.com/siderolabs/pkgs/commit/c0ac69b70cfac3cdcf100a35f6d766c5ae47d950) feat: enable CONFIG_NVME_{MULTIPATH|AUTH} * [`f7cd916`](https://github.com/siderolabs/pkgs/commit/f7cd916b47975e61c6732079c1c5c4684dfb8c96) fix: bump drbd to 9.2.4 * [`a56d15a`](https://github.com/siderolabs/pkgs/commit/a56d15ad626b6e76a137636d6088361be9a73a9f) fix: copy missing `modules.*` files * [`1eefa66`](https://github.com/siderolabs/pkgs/commit/1eefa664fc7c65491e956a6f403ada774e73a7d3) feat: build isb modem drivers as module * [`a859f4f`](https://github.com/siderolabs/pkgs/commit/a859f4fb257e17fa19b1c10efcae594d33a86618) fix: build RDMA_RXE as a module * [`5fb5e95`](https://github.com/siderolabs/pkgs/commit/5fb5e9517de9fe35e383b96e92fa873aa045a845) feat: bump dependencies * [`39a64b2`](https://github.com/siderolabs/pkgs/commit/39a64b23e2c8689c44b9891b1e70149b8d003655) feat: update Linux to 6.1.31, add GENEVE for arm64 * [`97177be`](https://github.com/siderolabs/pkgs/commit/97177be803cc91c8fabccfec575b7d920bc78c38) feat: update Linux to 6.1.30 * [`b1f9d4e`](https://github.com/siderolabs/pkgs/commit/b1f9d4e717fbd0132b820d45c226ca643d7f577e) chore: prevent unsigned kexec with secureboot * [`9232a42`](https://github.com/siderolabs/pkgs/commit/9232a425b85b1058cd38eab30304f6cf243ab32c) feat: add reproducibility pipelines * [`702d7a7`](https://github.com/siderolabs/pkgs/commit/702d7a7e90099d8fdc9cc4ba50e86c8ba6e91d77) chore: bump deps * [`7958db1`](https://github.com/siderolabs/pkgs/commit/7958db1549a7c7560eeeb8f9c06d3be9487d8804) chore: copy over sd-boot and sd-stub from tools * [`813b3c3`](https://github.com/siderolabs/pkgs/commit/813b3c3d3276d0d9156919307e9ffe521925d40b) chore: revert xfsprogs * [`0cc78ab`](https://github.com/siderolabs/pkgs/commit/0cc78ab82ce920c8fa5654c73738050107e190bb) chore: bump kernel to 6.1.28 * [`70189e3`](https://github.com/siderolabs/pkgs/commit/70189e3df555fed4afade93798d72cd31aad99c5) chore: bump deps * [`c5d3bf1`](https://github.com/siderolabs/pkgs/commit/c5d3bf1985b49e688d29d06db6730834f65ee480) feat: add sd-stub and sd-boot * [`30a7ac2`](https://github.com/siderolabs/pkgs/commit/30a7ac2974fb7580e83819c76502fde77d777ea0) feat: update Linux 6.1.27, containerd 1.6.21 * [`fbc6ee5`](https://github.com/siderolabs/pkgs/commit/fbc6ee55b6ffae44c117255901ab0fbecae79cc3) chore: bump deps * [`82b9489`](https://github.com/siderolabs/pkgs/commit/82b9489b88b108f144b45fb55432576bfd767f91) chore: bump dependencies * [`f37e520`](https://github.com/siderolabs/pkgs/commit/f37e5205cf10fe10296e86565fa018d149f5d8c4) feat: update Linux to 6.1.25 * [`3920b16`](https://github.com/siderolabs/pkgs/commit/3920b163a5c6a6d7c7969155a909a7b2122e65f6) feat: add multi-gen LRU kernel support * [`988f1ec`](https://github.com/siderolabs/pkgs/commit/988f1ecf95536fb259cbd79e044a556728bc7332) feat: update Linux to 6.1.24 * [`5327d12`](https://github.com/siderolabs/pkgs/commit/5327d1263680f76706ea667906ca08222c8398da) fix: remove FB_NVIDIA drivers, Linux 6.1.23 * [`4eae958`](https://github.com/siderolabs/pkgs/commit/4eae958770573613bc29568d130be7aaa775e530) chore: copy over the kernel signing public key * [`174f8fc`](https://github.com/siderolabs/pkgs/commit/174f8fc9c80d871f1c03ea0a53dc8b6eb7112ccf) chore: update Go to 1.20.3 * [`41629b0`](https://github.com/siderolabs/pkgs/commit/41629b03e82bfb77623a812000ef8e98d15d56fa) chore: reorder pkgs for better kernel caching * [`b483a6b`](https://github.com/siderolabs/pkgs/commit/b483a6b01f539b0da13ca09882015044bff24e41) feat: build 'snp.efi' for iPXE * [`fb853ff`](https://github.com/siderolabs/pkgs/commit/fb853ff6b1194cdc1f2412c776347cf4b55c3336) feat: update containerd to 1.6.20

### Changes from siderolabs/tools
15 commits

* [`e0c76c0`](https://github.com/siderolabs/tools/commit/e0c76c096d06ef11afdb54287d5f15add108399b) chore: bump dependencies * [`7d0cd58`](https://github.com/siderolabs/tools/commit/7d0cd58b34bba6b9415db5e39bed351e7f00d44d) feat: update Go to 1.20.5 * [`150efc2`](https://github.com/siderolabs/tools/commit/150efc22508043bfadc9d84a8c3c5fee6c2aac5f) chore: remove non needed tools * [`88ebb40`](https://github.com/siderolabs/tools/commit/88ebb40dd348b6c9e4dc5551b616e4a1892b4e42) feat: add swtpm * [`4c5d7fe`](https://github.com/siderolabs/tools/commit/4c5d7feb88dcbae2f7bf45f51f9e5e1ba339abac) chore: use same source epoch everywhere * [`2e46e5b`](https://github.com/siderolabs/tools/commit/2e46e5be764f8180a0762a5ab080ccff04534a8a) feat: add reproducibility pipelines * [`c6a41b6`](https://github.com/siderolabs/tools/commit/c6a41b6c5108d676f8573d3dd47ee29ae46e5cc0) fix: add sd-stub assertion patch * [`d2dde48`](https://github.com/siderolabs/tools/commit/d2dde48f72343aa3c541336f5319b8e649e80c87) chore: bump deps * [`8e45ad7`](https://github.com/siderolabs/tools/commit/8e45ad75ea78e353ca3eae21b18da9a42d1edf49) feat: add sbsign * [`271c4a6`](https://github.com/siderolabs/tools/commit/271c4a66b6987d9de2c0d1d69891b5ff277ebd43) feat: add sd-tools * [`eedc294`](https://github.com/siderolabs/tools/commit/eedc294967d415cca40d4c427d3521cd198661d7) chore: bump deps * [`81b09a5`](https://github.com/siderolabs/tools/commit/81b09a5ab204f16306c980eeff518a0d1a37ddf2) feat: add libcap and gnuefi * [`47b0fd3`](https://github.com/siderolabs/tools/commit/47b0fd3e364d4fbcfffe10965f740db7acd82f70) chore: bump go to 1.20.4 * [`ff4cf2b`](https://github.com/siderolabs/tools/commit/ff4cf2beabab310365ad9887abb6234570f5092a) chore: bump deps * [`1563556`](https://github.com/siderolabs/tools/commit/1563556b8f8fdf20d8aa58ac5340104c7ffe732e) feat: update Go to 1.20.3

### Dependency Changes * **github.com/BurntSushi/toml** v1.2.1 -> v1.3.2 * **github.com/aws/aws-sdk-go** v1.44.232 -> v1.44.287 * **github.com/beevik/ntp** v0.3.0 -> v1.1.1 * **github.com/benbjohnson/clock** v1.1.0 -> v1.3.5 * **github.com/cenkalti/backoff/v4** v4.2.0 -> v4.2.1 * **github.com/containerd/containerd** v1.6.19 -> v1.6.21 * **github.com/containerd/typeurl/v2** v2.1.1 **_new_** * **github.com/containernetworking/plugins** v1.2.0 -> v1.3.0 * **github.com/cosi-project/runtime** v0.3.0 -> v0.3.1-alpha.5 * **github.com/docker/distribution** v2.8.1 -> v2.8.2 * **github.com/docker/docker** v23.0.2 -> v24.0.2 * **github.com/ecks/uefi** caef65d070eb **_new_** * **github.com/emicklei/dot** v1.4.2 -> v1.5.0 * **github.com/hetznercloud/hcloud-go** v1.41.0 -> v1.47.0 * **github.com/insomniacslk/dhcp** 74ae03f2425e -> b20c9ba983df * **github.com/jsimonetti/rtnetlink** v1.3.1 -> v1.3.3 * **github.com/mattn/go-isatty** v0.0.18 -> v0.0.19 * **github.com/mdlayher/ethtool** ba3b4bc2e02c -> v0.1.0 * **github.com/mdlayher/genetlink** v1.3.1 -> v1.3.2 * **github.com/mdlayher/netlink** v1.7.1 -> v1.7.2 * **github.com/mdlayher/netx** c711c2f8512f -> 7e21880baee8 * **github.com/nberlee/go-netstat** v0.1.1 -> v0.1.2 * **github.com/opencontainers/image-spec** v1.1.0-rc2 -> v1.1.0-rc3 * **github.com/packethost/packngo** v0.29.0 -> v0.30.0 * **github.com/prometheus/procfs** v0.9.0 -> v0.11.0 * **github.com/rivo/tview** 281d14d896d7 -> 6cc0565babaf * **github.com/rs/xid** v1.4.0 -> v1.5.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.15 -> v1.0.0-beta.17 * **github.com/siderolabs/discovery-api** v0.1.2 -> v0.1.3 * **github.com/siderolabs/discovery-client** v0.1.4 -> v0.1.5 * **github.com/siderolabs/extras** v1.4.0-1-g9b07505 -> v1.5.0-alpha.0-1-ga73d524 * **github.com/siderolabs/gen** v0.4.3 -> v0.4.5 * **github.com/siderolabs/go-blockdevice** v0.4.4 -> v0.4.5 * **github.com/siderolabs/go-kubernetes** v0.2.0 -> v0.2.1 * **github.com/siderolabs/go-loadbalancer** v0.2.1 -> v0.3.1 * **github.com/siderolabs/pkgs** v1.4.1-5-ga333a84 -> v1.5.0-alpha.0-28-gedd725a * **github.com/siderolabs/talos/pkg/machinery** v1.4.0 -> v1.5.0-alpha.0 * **github.com/siderolabs/tools** v1.4.0-1-g955aabc -> v1.5.0-alpha.0-14-ge0c76c0 * **github.com/spf13/cobra** v1.6.1 -> v1.7.0 * **github.com/stretchr/testify** v1.8.2 -> v1.8.4 * **github.com/vmware-tanzu/sonobuoy** v0.56.16 -> v0.56.17 * **go.etcd.io/etcd/api/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/pkg/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/etcdutl/v3** v3.5.8 -> v3.5.9 * **golang.org/x/net** v0.8.0 -> v0.11.0 * **golang.org/x/sync** v0.1.0 -> v0.3.0 * **golang.org/x/sys** v0.6.0 -> v0.9.0 * **golang.org/x/term** v0.6.0 -> v0.9.0 * **golang.org/x/text** v0.10.0 **_new_** * **golang.zx2c4.com/wireguard/wgctrl** 9c5414ab4bde -> 925a1e7659e6 * **google.golang.org/grpc** v1.54.0 -> v1.56.1 * **k8s.io/api** v0.27.1 -> v0.27.3 * **k8s.io/apimachinery** v0.27.1 -> v0.27.3 * **k8s.io/apiserver** v0.27.1 -> v0.27.3 * **k8s.io/client-go** v0.27.1 -> v0.27.3 * **k8s.io/component-base** v0.27.1 -> v0.27.3 * **k8s.io/cri-api** v0.27.1 -> v0.27.3 * **k8s.io/klog/v2** v2.90.1 -> v2.100.1 * **k8s.io/kubectl** v0.27.1 -> v0.27.3 * **k8s.io/kubelet** v0.27.1 -> v0.27.3 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.68 -> v1.2.69 Previous release can be found at [v1.4.0](https://github.com/siderolabs/talos/releases/tag/v1.4.0) ## [Talos 1.5.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.5.0-alpha.0) (2023-05-19) Welcome to the v1.5.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates * Linux: 6.1.28 * containerd: 1.6.21 * runc: 1.1.7 * etcd: 3.5.9 * Kubernetes: 1.27.2 Talos is built with Go 1.20.4. ### Contributors * Andrey Smirnov * Noel Georgi * Utku Ozdemir * Dmitriy Matrenichev * Steve Francis * Budiman Jojo * Christian Rolland * Henk Kraal * Michael A. Davis * Michael Fornaro * Nico Berlee * Niklas Wik * Ricky Sadowski * Thomas Perronin ### Changes
69 commits

* [`383471c3e`](https://github.com/siderolabs/talos/commit/383471c3e956ff6e077a1de75b02a50835fbf352) feat: update default Kubernetes to v1.27.2 * [`8f68d1abe`](https://github.com/siderolabs/talos/commit/8f68d1abeff83c3ff0e6c5d9f61cb14807b44ca5) chore: bump deps * [`e0c1585d3`](https://github.com/siderolabs/talos/commit/e0c1585d3047ef213134331dc57f8e2e8c23a93d) feat: create azure community gallery image version on release * [`dd8336c9e`](https://github.com/siderolabs/talos/commit/dd8336c9ee7f8a3a44d45c9f9e3cbbf741f84c44) fix: refresh kubelet self-issued serving certificates * [`bb02dd263`](https://github.com/siderolabs/talos/commit/bb02dd263cbc5e7e3839148d86a4a0a5f7ea998b) chore: drop deprecated stuff for Talos 1.5 * [`61cad8673`](https://github.com/siderolabs/talos/commit/61cad86731e5c0aa80d7df41ea02d0b7ff579c45) chore: bump deps * [`01dfd3af7`](https://github.com/siderolabs/talos/commit/01dfd3af7d64dacd179d17d9d5eaf4bc44cf72af) feat: update etcd to v3.5.9 * [`aa65fbb8a`](https://github.com/siderolabs/talos/commit/aa65fbb8a1752a70e7bac4e4e9872f35e88d1cc9) chore: update KUBECTL_URL to reflect the community bucket * [`cc3128d94`](https://github.com/siderolabs/talos/commit/cc3128d944abacfb633bc783b7fed6d0a6f80661) chore: bump kernel to 6.1.28 * [`97fffaf78`](https://github.com/siderolabs/talos/commit/97fffaf78a0b9a1dc67709de11d37ea20aefde59) chore: use ctest.UpdateWithConflicts instead of plain UpdateWithConflicts * [`3b36993b9`](https://github.com/siderolabs/talos/commit/3b36993b9926392f4290e6fabc82e635f4c98149) fix: rlimit nofile test * [`45e6e27af`](https://github.com/siderolabs/talos/commit/45e6e27af75746fd0cc8b0f98a2d14579eb0ed40) chore: bump runtime * [`4f720d465`](https://github.com/siderolabs/talos/commit/4f720d46532af39165fc5051052d5c42595d91af) fix: revert: set rlimit explicitly in wrapperd * [`a2565f674`](https://github.com/siderolabs/talos/commit/a2565f67416e9b9bc22f2d5506df9ea7771c0c8c) fix: set rlimit explicitly in wrapperd * [`cdfc242b8`](https://github.com/siderolabs/talos/commit/cdfc242b8354f4cc4e7ce51bbe3a8fb20b35995d) chore: re-enable Go buildid * [`e67f3f5c5`](https://github.com/siderolabs/talos/commit/e67f3f5c5453f947355194ea9656c15ff008c35e) feat: linux 6.1.27, containerd 1.6.21, go 1.20.4 * [`55ae59a0a`](https://github.com/siderolabs/talos/commit/55ae59a0ad71293676b3efed461f5ab98101401a) fix: properly skip/cleanup controlplane configs for workers * [`64eade9bd`](https://github.com/siderolabs/talos/commit/64eade9bde271bce4e629e6ac09407c8c42e01be) chore: clean up unused constant * [`62c6e9655`](https://github.com/siderolabs/talos/commit/62c6e9655cb639d4993aaa4c9b364342688599cb) feat: introduce siderolink config resource & reconnect * [`860002c73`](https://github.com/siderolabs/talos/commit/860002c7352bedd10845e11da37c80685ff0e720) fix: don't reload control plane pods on cert SANs changes * [`d43c61e80`](https://github.com/siderolabs/talos/commit/d43c61e80f5b05b81f2a021cdfe012e500c3d98e) fix: enforce nolock option for all NFS mounts by default * [`339986db9`](https://github.com/siderolabs/talos/commit/339986db9d3675b78ce0d268f799ad654862fb0f) fix: inhibit timer to follow kubelet timer * [`cbf6dc100`](https://github.com/siderolabs/talos/commit/cbf6dc1009ad47a2804774839e4e0301efa8ac78) fix: set timeout for unmount calls * [`b58f913d5`](https://github.com/siderolabs/talos/commit/b58f913d5f4b8ecf39be183d0bafe1109f0f0737) fix: set the static pod priority as values * [`f8a7a5b6b`](https://github.com/siderolabs/talos/commit/f8a7a5b6bf4138a33cbe5c9afe85db99de167aec) docs: add information about KubeSpan ports and topology * [`2bad74d64`](https://github.com/siderolabs/talos/commit/2bad74d6423c083ec34f1b422f23b0024d5f8798) docs: add how to on scaling down * [`7442ff8b0`](https://github.com/siderolabs/talos/commit/7442ff8b095ef1337f54332a71d08053a2832144) chore: fix typos inteface -> interface (docs and tests) * [`d4e94f7a1`](https://github.com/siderolabs/talos/commit/d4e94f7a15acf7f3c9e7532b067cdacd0e805bec) fix: add back required TARGETARCH for installer * [`e6fffda01`](https://github.com/siderolabs/talos/commit/e6fffda01385a2daaa901a5742f30a4edc9186a7) chore: linux 6.1.26, runc 1.1.7 * [`344746ae2`](https://github.com/siderolabs/talos/commit/344746ae2fa038b704d02fec04c3d358762fe938) fix: bump max inhibit delay to 20 min * [`d9bdea2b5`](https://github.com/siderolabs/talos/commit/d9bdea2b54772f067783ee64eb85c834957d386a) chore: fork docs and compatibility modules for Talos 1.5 * [`3d99610fc`](https://github.com/siderolabs/talos/commit/3d99610fc9b0d0084be822be29bb1bf2fbe85833) docs: document building, verifying image and process caps * [`014008ea2`](https://github.com/siderolabs/talos/commit/014008ea25208afbeabb42ef89238802705ad4e0) fix: udevd rules trigger * [`9b36bb613`](https://github.com/siderolabs/talos/commit/9b36bb613b44f182e47ae63bc74e4a8b6342d68d) feat: update Linux to 6.1.25, fix virtio on arm64 * [`08ec66c55`](https://github.com/siderolabs/talos/commit/08ec66c55ccca3f9aa82a9703ebf183913b19a7e) feat: clean up (garbage collect) system images which are not referenced * [`b097efcde`](https://github.com/siderolabs/talos/commit/b097efcde29c20cdc4fed23fe8366bd683db634c) fix: display correct number of machines on dashboard * [`cad43f0ad`](https://github.com/siderolabs/talos/commit/cad43f0ad3bc2ede8a6ae81767c9226b6bc69f19) chore: remove k8s master label * [`e296a566e`](https://github.com/siderolabs/talos/commit/e296a566e6efb0cbdd119e73aff1feaa772d38bd) fix: support kernel userspace module loading * [`103f0ffdd`](https://github.com/siderolabs/talos/commit/103f0ffdd3ebd57a5086852f3502a8a7d4428faa) feat: add startup probes to controller-manager and scheduler * [`5a1ae8aae`](https://github.com/siderolabs/talos/commit/5a1ae8aae89e54d5540586d6f2e99ef3e80a72eb) chore: bump dependences * [`ec8c8dbaf`](https://github.com/siderolabs/talos/commit/ec8c8dbafcdaf63d036bdba92fa153d4d1c90100) chore: fix container image reproducibility * [`f661d8487`](https://github.com/siderolabs/talos/commit/f661d84877e6db5bc8856b982990926dcbfe949c) fix: allow `talosctl cp` to handle special files in `/proc` * [`2d824b563`](https://github.com/siderolabs/talos/commit/2d824b5639a4b8c3b673d13b08b2b97c69aafe0d) fix: do not show control plane status for workers on dashboard * [`e5491ddad`](https://github.com/siderolabs/talos/commit/e5491ddadeb1776bd5c17dd35917e05ec4847d0f) docs: update documentation for nocloud * [`7a004a6f7`](https://github.com/siderolabs/talos/commit/7a004a6f7f47fa5d17e855eb02650754d8411574) fix: parse errors correctly * [`374ef5385`](https://github.com/siderolabs/talos/commit/374ef53853947811dc221d99751cf0e16294508c) test: submit verbose flag to e2e tests * [`e1d38b6fe`](https://github.com/siderolabs/talos/commit/e1d38b6febf26fe31a6b9d6ed8f9b6bdba29aa3b) feat: show template URL in dashboard config URL tab * [`45d7f0ce9`](https://github.com/siderolabs/talos/commit/45d7f0ce95454ce85c403fc493ddb97e4d478238) docs: fix the latest url * [`96efbf147`](https://github.com/siderolabs/talos/commit/96efbf14769579d514ef9c75d01d9f44d276113a) docs: activate 1.4.0 docs by default * [`8c1f515b1`](https://github.com/siderolabs/talos/commit/8c1f515b1b8e40bce42e2fc04755afe5bf8a56aa) feat: update Linux to 6.1.24 * [`8689bef5f`](https://github.com/siderolabs/talos/commit/8689bef5f10839091cf131edb6c8efad4ccba034) docs: update documentation for Talos 1.4 * [`a781dfb8e`](https://github.com/siderolabs/talos/commit/a781dfb8e3ded67edcb2a6a1048bfe76c6bd0d24) feat: update Kubernetes to 1.27.1 * [`a737dd83a`](https://github.com/siderolabs/talos/commit/a737dd83a4cd7549f85f8df0882f1c9a4446060d) chore: typo in `compatibility.ParseKubernetesVersion` * [`f14928b0a`](https://github.com/siderolabs/talos/commit/f14928b0a9dd3d85664605f4f6a206236ea94614) fix: fix dashboard crash when a non-existent node is specified * [`3e406d9b0`](https://github.com/siderolabs/talos/commit/3e406d9b07c0e67a2fb61e612bc3f378f3c35247) feat: update etcd to v3.5.8 * [`bd1cff3e8`](https://github.com/siderolabs/talos/commit/bd1cff3e83530b9b89b27d8083ea8f3f0cf6ede4) chore: remove Go buildid * [`e31f7f50b`](https://github.com/siderolabs/talos/commit/e31f7f50b1b455beb98cd25859a44bbbccc1ff64) feat: update Kubernetes to 1.27.0 * [`aa3640d74`](https://github.com/siderolabs/talos/commit/aa3640d74ce2e3619476453381909fa3520eb87d) docs: update storage.md * [`07bb61e60`](https://github.com/siderolabs/talos/commit/07bb61e60c53b267756dc97874b9c9554f2b1486) chore: module-sig-verify cleanup * [`5e9d836c3`](https://github.com/siderolabs/talos/commit/5e9d836c3d075c3edb2d48b2868c31a1c963e2de) chore: add kernel module signtaure verification * [`3cd1c6bb0`](https://github.com/siderolabs/talos/commit/3cd1c6bb0b83e5747a7356140a44b16deb4727e6) fix: send 'STOP' event on phase end * [`5176d27dc`](https://github.com/siderolabs/talos/commit/5176d27dc566d8689bb305398da7250269ebe9a3) feat: update Kubernetes to 1.27.0-rc.1 * [`2c55550a6`](https://github.com/siderolabs/talos/commit/2c55550a66b49b49d8dc95b83516b7c0f8107300) fix: quote ISO kernel args for GRUB * [`319d76e38`](https://github.com/siderolabs/talos/commit/319d76e38978406d8d37e89ada2c403969d6c972) fix: respect BROWSER=echo in client auth interceptor * [`4e4ace839`](https://github.com/siderolabs/talos/commit/4e4ace839c0f558e7b00979fa4c64c32985aa3ce) chore: update Go to 1.20.3 * [`170f73899`](https://github.com/siderolabs/talos/commit/170f73899a3bf29e9c6f76fdc5e510be08edf4aa) fix: correctly parse static pod phase * [`c3a595d5b`](https://github.com/siderolabs/talos/commit/c3a595d5b7d3c7c3091229caef6b2553416edb56) fix: improve action tracking post checks * [`eb01edbc8`](https://github.com/siderolabs/talos/commit/eb01edbc8a0ef5810693afe450861d5b63877b72) fix: rework DHCP flow * [`e095150a6`](https://github.com/siderolabs/talos/commit/e095150a6e34cbdc805a2cac85ec7f28f98629b4) test: bump CAPI components versions

### Changes from siderolabs/extras
1 commit

* [`36c8ac4`](https://github.com/siderolabs/extras/commit/36c8ac4ab98300059acaad501c2adc8abd39179f) chore: update to Go 1.20.3

### Changes from siderolabs/gen
3 commits

* [`f9f5805`](https://github.com/siderolabs/gen/commit/f9f5805973d30fe6bbac2f4a79ad4197fe59970e) chore: bump rekres and add functions from exp * [`b968d21`](https://github.com/siderolabs/gen/commit/b968d21c9671d97e54317f80cdf781d6f963e44b) feat: add `TryRecv` and `RecvWithContext` functions * [`476dfea`](https://github.com/siderolabs/gen/commit/476dfeae70882e1ca6e5cfed3d6e12dc36841a26) feat: add foreach and clear to lazymap

### Changes from siderolabs/go-blockdevice
1 commit

* [`076874a`](https://github.com/siderolabs/go-blockdevice/commit/076874a155ad44d764d25081125f950e8194d023) chore: resolve blockdevice symlinks

### Changes from siderolabs/go-loadbalancer
1 commit

* [`f3a0e24`](https://github.com/siderolabs/go-loadbalancer/commit/f3a0e2411e08eef9c79876f3dc6e09e770710379) fix: use SO_LINGER option when doing TCP healthchecks

### Changes from siderolabs/pkgs
18 commits

* [`702d7a7`](https://github.com/siderolabs/pkgs/commit/702d7a7e90099d8fdc9cc4ba50e86c8ba6e91d77) chore: bump deps * [`7958db1`](https://github.com/siderolabs/pkgs/commit/7958db1549a7c7560eeeb8f9c06d3be9487d8804) chore: copy over sd-boot and sd-stub from tools * [`813b3c3`](https://github.com/siderolabs/pkgs/commit/813b3c3d3276d0d9156919307e9ffe521925d40b) chore: revert xfsprogs * [`0cc78ab`](https://github.com/siderolabs/pkgs/commit/0cc78ab82ce920c8fa5654c73738050107e190bb) chore: bump kernel to 6.1.28 * [`70189e3`](https://github.com/siderolabs/pkgs/commit/70189e3df555fed4afade93798d72cd31aad99c5) chore: bump deps * [`c5d3bf1`](https://github.com/siderolabs/pkgs/commit/c5d3bf1985b49e688d29d06db6730834f65ee480) feat: add sd-stub and sd-boot * [`30a7ac2`](https://github.com/siderolabs/pkgs/commit/30a7ac2974fb7580e83819c76502fde77d777ea0) feat: update Linux 6.1.27, containerd 1.6.21 * [`fbc6ee5`](https://github.com/siderolabs/pkgs/commit/fbc6ee55b6ffae44c117255901ab0fbecae79cc3) chore: bump deps * [`82b9489`](https://github.com/siderolabs/pkgs/commit/82b9489b88b108f144b45fb55432576bfd767f91) chore: bump dependencies * [`f37e520`](https://github.com/siderolabs/pkgs/commit/f37e5205cf10fe10296e86565fa018d149f5d8c4) feat: update Linux to 6.1.25 * [`3920b16`](https://github.com/siderolabs/pkgs/commit/3920b163a5c6a6d7c7969155a909a7b2122e65f6) feat: add multi-gen LRU kernel support * [`988f1ec`](https://github.com/siderolabs/pkgs/commit/988f1ecf95536fb259cbd79e044a556728bc7332) feat: update Linux to 6.1.24 * [`5327d12`](https://github.com/siderolabs/pkgs/commit/5327d1263680f76706ea667906ca08222c8398da) fix: remove FB_NVIDIA drivers, Linux 6.1.23 * [`4eae958`](https://github.com/siderolabs/pkgs/commit/4eae958770573613bc29568d130be7aaa775e530) chore: copy over the kernel signing public key * [`174f8fc`](https://github.com/siderolabs/pkgs/commit/174f8fc9c80d871f1c03ea0a53dc8b6eb7112ccf) chore: update Go to 1.20.3 * [`41629b0`](https://github.com/siderolabs/pkgs/commit/41629b03e82bfb77623a812000ef8e98d15d56fa) chore: reorder pkgs for better kernel caching * [`b483a6b`](https://github.com/siderolabs/pkgs/commit/b483a6b01f539b0da13ca09882015044bff24e41) feat: build 'snp.efi' for iPXE * [`fb853ff`](https://github.com/siderolabs/pkgs/commit/fb853ff6b1194cdc1f2412c776347cf4b55c3336) feat: update containerd to 1.6.20

### Changes from siderolabs/tools
8 commits

* [`d2dde48`](https://github.com/siderolabs/tools/commit/d2dde48f72343aa3c541336f5319b8e649e80c87) chore: bump deps * [`8e45ad7`](https://github.com/siderolabs/tools/commit/8e45ad75ea78e353ca3eae21b18da9a42d1edf49) feat: add sbsign * [`271c4a6`](https://github.com/siderolabs/tools/commit/271c4a66b6987d9de2c0d1d69891b5ff277ebd43) feat: add sd-tools * [`eedc294`](https://github.com/siderolabs/tools/commit/eedc294967d415cca40d4c427d3521cd198661d7) chore: bump deps * [`81b09a5`](https://github.com/siderolabs/tools/commit/81b09a5ab204f16306c980eeff518a0d1a37ddf2) feat: add libcap and gnuefi * [`47b0fd3`](https://github.com/siderolabs/tools/commit/47b0fd3e364d4fbcfffe10965f740db7acd82f70) chore: bump go to 1.20.4 * [`ff4cf2b`](https://github.com/siderolabs/tools/commit/ff4cf2beabab310365ad9887abb6234570f5092a) chore: bump deps * [`1563556`](https://github.com/siderolabs/tools/commit/1563556b8f8fdf20d8aa58ac5340104c7ffe732e) feat: update Go to 1.20.3

### Dependency Changes * **github.com/aws/aws-sdk-go** v1.44.232 -> v1.44.264 * **github.com/beevik/ntp** v0.3.0 -> v1.0.0 * **github.com/benbjohnson/clock** v1.1.0 -> v1.3.5 * **github.com/cenkalti/backoff/v4** v4.2.0 -> v4.2.1 * **github.com/containerd/containerd** v1.6.19 -> v1.6.20 * **github.com/containerd/typeurl/v2** v2.1.1 **_new_** * **github.com/containernetworking/plugins** v1.2.0 -> v1.3.0 * **github.com/cosi-project/runtime** v0.3.0 -> 82b69d862a7a * **github.com/docker/docker** v23.0.2 -> v23.0.6 * **github.com/hetznercloud/hcloud-go** v1.41.0 -> v1.45.1 * **github.com/insomniacslk/dhcp** 74ae03f2425e -> 49801966e6cb * **github.com/jsimonetti/rtnetlink** v1.3.1 -> v1.3.3 * **github.com/mdlayher/genetlink** v1.3.1 -> v1.3.2 * **github.com/mdlayher/netlink** v1.7.1 -> v1.7.2 * **github.com/mdlayher/netx** c711c2f8512f -> 7e21880baee8 * **github.com/nberlee/go-netstat** v0.1.1 -> v0.1.2 * **github.com/opencontainers/image-spec** v1.1.0-rc2 -> v1.1.0-rc3 * **github.com/rivo/tview** 281d14d896d7 -> 822bd067b165 * **github.com/rs/xid** v1.4.0 -> v1.5.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.15 -> v1.0.0-beta.16 * **github.com/siderolabs/extras** v1.4.0-1-g9b07505 -> v1.5.0-alpha.0 * **github.com/siderolabs/gen** v0.4.3 -> v0.4.5 * **github.com/siderolabs/go-blockdevice** v0.4.4 -> v0.4.5 * **github.com/siderolabs/go-loadbalancer** v0.2.1 -> v0.2.2 * **github.com/siderolabs/pkgs** v1.4.1-5-ga333a84 -> v1.5.0-alpha.0-17-g702d7a7 * **github.com/siderolabs/talos/pkg/machinery** v1.4.0 -> v1.4.4 * **github.com/siderolabs/tools** v1.4.0-1-g955aabc -> v1.5.0-alpha.0-7-gd2dde48 * **github.com/spf13/cobra** v1.6.1 -> v1.7.0 * **go.etcd.io/etcd/api/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/pkg/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/client/v3** v3.5.8 -> v3.5.9 * **go.etcd.io/etcd/etcdutl/v3** v3.5.8 -> v3.5.9 * **golang.org/x/net** v0.8.0 -> v0.10.0 * **golang.org/x/sync** v0.1.0 -> v0.2.0 * **golang.org/x/sys** v0.6.0 -> v0.8.0 * **golang.org/x/term** v0.6.0 -> v0.8.0 * **golang.zx2c4.com/wireguard/wgctrl** 9c5414ab4bde -> 925a1e7659e6 * **google.golang.org/grpc** v1.54.0 -> v1.55.0 * **k8s.io/api** v0.27.1 -> v0.27.2 * **k8s.io/apimachinery** v0.27.1 -> v0.27.2 * **k8s.io/apiserver** v0.27.1 -> v0.27.2 * **k8s.io/client-go** v0.27.1 -> v0.27.2 * **k8s.io/component-base** v0.27.1 -> v0.27.2 * **k8s.io/klog/v2** v2.90.1 -> v2.100.1 * **k8s.io/kubectl** v0.27.1 -> v0.27.2 * **k8s.io/kubelet** v0.27.1 -> v0.27.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.68 -> v1.2.69 Previous release can be found at [v1.4.0](https://github.com/siderolabs/talos/releases/tag/v1.4.0) ## [Talos 1.4.0-alpha.4](https://github.com/siderolabs/talos/releases/tag/v1.4.0-alpha.4) (2023-03-31) Welcome to the v1.4.0-alpha.4 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Bond Device Selectors Bond links can now be described using device selectors instead of explicit device names: ```yaml machine: network: interfaces: - interface: bond0 bond: deviceSelectors: - hardwareAddr: '00:50:56:*' - hardwareAddr: '00:50:57:9c:2c:2d' ``` ### talosctl containers `talosctl logs -k` and `talosctl containers -k` now support and output container display names with their ids. This allows to distinguish between containers with the same name. ### Registry Mirror Catch-All Option Talos now supports a catch-all option for registry mirrors: ```yaml machine: registries: mirrors: docker.io: - https://registry-1.docker.io/ "*": - https://my-registry.example.com/ ``` ### Talos Dashboard on TTY2 Talos now starts a text-based UI dashboard on virtual console `/dev/tty2` and switches to it by default upon boot. Kernel logs remain available on `/dev/tty1`. To switch TTYs, use the `Alt+F1` through `Alt+F2` keys. You can disable this behavior by setting the kernel parameter `talos.dashboard.disabled=1`. This behavior is disabled by default on SBCs. ### Kernel Argument `talos.environment` Talos now supports passing environment variables via `talos.environment` kernel argument. Example: ``` talos.environment=http_proxy=http://proxy.example.com:8080 talos.environment=https_proxy=http://proxy.example.com:8080 ``` ### etcd Maintenance Talos adds new APIs to make it easier to perform etcd maintenance operations. These APIs are available via new `talosctl etcd` sub-commands: * `talosctl etcd alarm list|disarm` * `talosctl etcd defrag` * `talosctl etcd status` See also [etcd maintenance guide](https://talos.dev/v1.4/advanced/etcd-maintenance/). ### Kernel Modules Talos now supports automatically loading kernel drivers built as modules. If any system extensions or the Talos base kernel build provides kernel modules and if they matches the system hardware (via PCI IDs), they will be loaded automatically. Modules can still be loaded explicitly by defining it in [machine configuration](https://www.talos.dev/v1.4/reference/configuration/#kernelconfig). ### Kernel Modules Tree Talos now supports re-building the kernel modules dependency tree information on upgrades. This allows modules of same name to co-exist as in-tree and external modules. System Extensions can provide modules installed into `extras` directory and when loading it'll take precendence over the in-tree module. ### Kernel Reset Argument Talos now supports `talos.experimental.wipe=system:EPHEMERAL,STATE` kernel argument. Talos now also supports the new GRUB boot option - "Reset Talos installation and return to maintenance mode". Both of this options will reset EPHEMERAL and STATE partitions and will return Talos into maintenance mode after the reboot. ### Machine Configuration Strategic merge config patches correctly support merging `.vlans` sections of the network interface. ### talosctl netstat Talos API was extended to support retrieving a list of network connections (sockets) from the node and pods. `talosctl netstat` command was added to retrieve the list of network connections. ### Reset API Enhancements Talos now supports resetting user disks through the Reset API, the list of disks to wipe is set using the `--user-disks-to-wipe` parameter in `talosctl`. Additionally, the Reset API can now function in maintenance mode and has the capability to wipe the node's system disk (partial wipe is not supported). ### New Talos API os:operator role Talos now supports a new `os:operator` role for the Talos API. This role allows everything `os:reader` role allows plus access to maintenance APIs: rebooting, shutting down a node, accessing packet capture, etcd alarm APIs, etcd backup, etc. ### Component Updates * Linux: 6.1.22 * containerd: v1.6.20 * runc: v1.1.5 * Kubernetes: v1.27.0-rc.0 * etcd: v3.5.7 * CoreDNS: v1.10.1 * Flannel: v0.21.4 Talos is built with Go 1.20.2. ### VMware Platform Talos now supports loading network configuration on VMWare platform from the `metadata` key. See [CAPV IPAM Support](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/blob/main/docs/proposal/20220929-ipam-support.md) and [Talos issue 6708](https://github.com/siderolabs/talos/issues/6708) for details. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Spencer Smith * Artem Chernyshev * Serge Logvinov * Steve Francis * Nico Berlee * Tim Jones * Seán C McCord * Steffen Windoffer * Andrey Smirnov * Cees-Jan Kiewiet * Chris van de Sande * Dennis Marttinen * Dzerom Dzenkins * Erik Lund * Jori Huisman * Lance R. Vick * Matthias Riegler * Michael Vorburger * Murtaza Udaipurwala * Niklas Wik * Rowan Smith * Samuel Kees * Sander Maijers * Thomas Way * Tim van Druenen * Victor Seva * budimanjojo * xyhhx ### Changes
200 commits

* [`7ffabe0f1`](https://github.com/siderolabs/talos/commit/7ffabe0f14dd3432857423743701ed5cdb3fe07f) feat: support network bond device selectors * [`cbab12e3a`](https://github.com/siderolabs/talos/commit/cbab12e3a1f2c576164bb721dc70073e6b8d3767) refactor: rename outbound to connectivity on dashboard * [`07c3c5d59`](https://github.com/siderolabs/talos/commit/07c3c5d59e02d82dbc1ff6f5392c2aa428503e0e) feat: return disk subsystem in the `Disks API` * [`b8497b99e`](https://github.com/siderolabs/talos/commit/b8497b99eb46fafd0f908c768d92683771f58cc3) feat: update containerd to 1.6.20 * [`aa1499353`](https://github.com/siderolabs/talos/commit/aa149935390b0ea49e6b6de51a2eeccd6cbcbbcb) feat: introduce network probes * [`9dc1150e3`](https://github.com/siderolabs/talos/commit/9dc1150e3aa08e5cb85c8fb6ecf0cfec5c613029) docs: update nvidia instructions * [`7967ccfc1`](https://github.com/siderolabs/talos/commit/7967ccfc13a534cce32bb49558bb4bbeb5ee4480) feat: add config code entry screen to dashboard * [`ddb014cfd`](https://github.com/siderolabs/talos/commit/ddb014cfdcb3630055d5b7be7f480080612dfc11) fix: udevd rules trigger * [`0af8fe2fb`](https://github.com/siderolabs/talos/commit/0af8fe2fb547e01be1b1231d69ccf2fdc5f2ea0d) feat: netstat pod support * [`52e857f55`](https://github.com/siderolabs/talos/commit/52e857f55ef14e88ed5ac6f7fb73a08a5ec8fe7a) feat: linux 6.1.22, runc 1.1.5 * [`aa662ff63`](https://github.com/siderolabs/talos/commit/aa662ff635f4739343489076bdb9a11722e28798) fix: apply small fixes on dashboard * [`188560a33`](https://github.com/siderolabs/talos/commit/188560a334a39f4e6472d196ce33bcfa88e41102) fix: add a link-scope route if the cmdline gateway is not reachable * [`45c5b47a5`](https://github.com/siderolabs/talos/commit/45c5b47a57c0c7efdc126f24f880238b9aec9781) feat: dhcpv4: send current hostname, fix spec compliance of renewals * [`289b41fe4`](https://github.com/siderolabs/talos/commit/289b41fe4b3af7bd4e1e61a1ca30dc1ed2b0d027) fix: output of `talosctl logs` might be corruped * [`02f0a4526`](https://github.com/siderolabs/talos/commit/02f0a4526d09334757a71d868d9e2ae70aa1aade) feat: allow writing initial META values into the image * [`ea0e9bdbe`](https://github.com/siderolabs/talos/commit/ea0e9bdbe454041f8895e0a869e28eabb5156430) feat: environment variables via the kernel arguments * [`94c24ca64`](https://github.com/siderolabs/talos/commit/94c24ca64e70f227da29cd02bd367d3c2701b96c) chore: add machine config version contract for v1.4 * [`cefa9c3ec`](https://github.com/siderolabs/talos/commit/cefa9c3ecb5675c80b44a2fe3aaa55e402cab7a6) feat: update Kubernetes to 1.27.0-rc.0 * [`9e8603f53`](https://github.com/siderolabs/talos/commit/9e8603f53b83e326a2529b2c595b619e04f2b85b) feat: implement new download URL variable `${code}` * [`d30cf9c86`](https://github.com/siderolabs/talos/commit/d30cf9c86efc30ce2c61b82be1bfb431cc74dd78) test: fix misprint in e2e scripts * [`0d0bb31cf`](https://github.com/siderolabs/talos/commit/0d0bb31cf766ece4c78ffe3f4094f94a9990d88c) fix: use stripped kernel modules * [`3583eea98`](https://github.com/siderolabs/talos/commit/3583eea9830d6701a1e5ee4a128e1819de4c94fd) release(v1.4.0-alpha.3): prepare release * [`a7b79ef1b`](https://github.com/siderolabs/talos/commit/a7b79ef1be79ca7e0ea1530d469c3790f43e6c6b) feat: add network config screen to dashboard * [`cf2ccc521`](https://github.com/siderolabs/talos/commit/cf2ccc521f6a15b8b82bf5fbaab572f481f8edf7) fix: always shutdown maintenance API service * [`a0a5db590`](https://github.com/siderolabs/talos/commit/a0a5db590d9b5f312f9e59bec4ddc7379183c705) feat: update Flannel to 0.21.4 * [`d1a61fd34`](https://github.com/siderolabs/talos/commit/d1a61fd34343e58192864b1464759b78eb57e917) chore: bump golangci-lint * [`36a9a208e`](https://github.com/siderolabs/talos/commit/36a9a208ecf01114f5cc47449bb69099fca99e83) chore: bump deps * [`c63cf90e3`](https://github.com/siderolabs/talos/commit/c63cf90e32ce61e788a00ed79a5ff662d3d25e50) feat: update k8s to v1.27.0-beta.0 * [`b246c90ab`](https://github.com/siderolabs/talos/commit/b246c90abdec14c305dbad8af82147ebe44328ce) fix: add uint32 to Magic1 and Magic2 * [`777c8d6f6`](https://github.com/siderolabs/talos/commit/777c8d6f6ecb438d11ac829a297bf2c6b5660479) chore: update COSI to watch aggregated version * [`bec89bf6e`](https://github.com/siderolabs/talos/commit/bec89bf6e575923f348a4885841de27eead020df) fix: use 'no block' etcd dial with multiple endpoints * [`28713c2c4`](https://github.com/siderolabs/talos/commit/28713c2c4d4d20d5ff455c40cbb8aa004d725801) feat: update Kubernetes to 1.26.3 * [`a3cf41647`](https://github.com/siderolabs/talos/commit/a3cf4164755609e80de5dafa2c49bfaa0fc655fd) docs: add InstallConfig ignored notice to doc * [`df9b851fb`](https://github.com/siderolabs/talos/commit/df9b851fbadaa7c652f343c2facc2bd0a9dd22ca) chore: load all external artifacts earlier * [`2dd0964c5`](https://github.com/siderolabs/talos/commit/2dd0964c5f617a7072af08bd45c35d57bc47e838) refactor: use resource watches on dashboard * [`9933ebb6a`](https://github.com/siderolabs/talos/commit/9933ebb6aa86249d2118ef5fb50bf23cedadb0a5) chore: fix loaded artifacts file permission * [`a14a0aba0`](https://github.com/siderolabs/talos/commit/a14a0aba04a2daf277bf2703575def39b7f2e5e9) fix: nil pointer exception in syncLink * [`cf101e56f`](https://github.com/siderolabs/talos/commit/cf101e56fbf18bb401bebb95e9fe005f65765d3d) fix: add `--force` flag for `talosctl gen` * [`ea2aa0611`](https://github.com/siderolabs/talos/commit/ea2aa06116a1b3c58d40ad42787749783516ef6c) fix: fix data race on network config read * [`64e3d24c6`](https://github.com/siderolabs/talos/commit/64e3d24c6bfe60b5556c41822c8e81f63d0a06d2) feat: provide platform network config for 'metal' in META * [`442cb9c1b`](https://github.com/siderolabs/talos/commit/442cb9c1b0757a9c8204cc92baab11f664cbcb19) feat: implement APIs to write to META * [`9e07832db`](https://github.com/siderolabs/talos/commit/9e07832db9e19e602332821769f479b881fae178) feat: implement summary dashboard * [`1df841bb5`](https://github.com/siderolabs/talos/commit/1df841bb542323adce92013cd55eb24ab238a1dc) refactor: change the interface of META * [`e9962bc3e`](https://github.com/siderolabs/talos/commit/e9962bc3eaa31b9a782c2fcd0c7857a86cba0c28) chore: update CI to tag azure buckets * [`9f5f5cf9b`](https://github.com/siderolabs/talos/commit/9f5f5cf9bf83e9cff0be7720d6bffc13fec97570) feat: update Flannel to v0.21.3 * [`02b0ff35e`](https://github.com/siderolabs/talos/commit/02b0ff35ee2273e59899ac4a999fa101d895aec0) feat: generate Flannel CNI manifest from upstream * [`6656d35ec`](https://github.com/siderolabs/talos/commit/6656d35eca5ec78cd52e7a6478369200ce16b176) docs: fix Talos version to use template * [`72a6d1d70`](https://github.com/siderolabs/talos/commit/72a6d1d70813986f6e9f4b7fc92e594f6ff7da1f) docs: update nocloud * [`9948a646d`](https://github.com/siderolabs/talos/commit/9948a646d20f4ba80916a263ed7bca3e5ca2f0ad) feat: coredns node uninitialized toleration * [`e03902b54`](https://github.com/siderolabs/talos/commit/e03902b546b379c19ea80081bbfaef666d03812d) feat: update Go to 1.20.2 * [`c8f8579f2`](https://github.com/siderolabs/talos/commit/c8f8579f2dcf485e66922679d37e56742b65cc53) fix: upgrade-k8s to flag should not be required since there is a default * [`230cfaf80`](https://github.com/siderolabs/talos/commit/230cfaf80312518222469939e969880040c379f2) feat: use network information from guestinfo.metadata * [`97048f7c3`](https://github.com/siderolabs/talos/commit/97048f7c37ed7b7aceadf6f2e40f007a09c57730) feat: netstat in API and client * [`fda6da692`](https://github.com/siderolabs/talos/commit/fda6da692956d863d320f25cd50833da2f93104c) fix: successful ACPI shutdown in maintenance mode * [`b97e1abaa`](https://github.com/siderolabs/talos/commit/b97e1abaa6a1543bc7b6e8fa7e4fa9e0cb5d8e14) feat: set default image, validate empty image * [`121220a3b`](https://github.com/siderolabs/talos/commit/121220a3b3202de9bd08dce391740c9a66ad9cf2) chore: bump dependencies via renovate bot * [`ebc92f3c1`](https://github.com/siderolabs/talos/commit/ebc92f3c1de97a8b11046268854e957be0b64f81) chore: add container id to `talosctl -k containers` and `talosctl -k logs` * [`22ef81c1e`](https://github.com/siderolabs/talos/commit/22ef81c1e78963a8f46e2f54d00cd111742dd95c) feat: add grub option to drop to maintenance mode * [`642fe0c90`](https://github.com/siderolabs/talos/commit/642fe0c90c4714aeb5f880946c1d337c53bc6fa4) feat: update pkgs with framebuffer console * [`69cb414f0`](https://github.com/siderolabs/talos/commit/69cb414f01d3193931e838f89e21b0c9ac26bf61) docs: update cilium install instructions * [`e71cc6619`](https://github.com/siderolabs/talos/commit/e71cc6619b2cdc34efe8dccca3cc296befef43f9) fix: redo assertHostnames in HostnameMergeSuite.TestMerge * [`8ea4bfad8`](https://github.com/siderolabs/talos/commit/8ea4bfad8feae5f4806be0ea4f6fdd1b79a8197a) refactor: improve the kubernetes upgrade flow * [`81879fc0c`](https://github.com/siderolabs/talos/commit/81879fc0ca98cc3e5df619bd071c279a735697e3) docs: add how tos for workloads on control planes, and scaling up * [`05b0b721c`](https://github.com/siderolabs/talos/commit/05b0b721c9d2acd211519d554d1c23926472a5b4) chore: move blob storage to azure for builds * [`a78281214`](https://github.com/siderolabs/talos/commit/a78281214d349c147498e3b000a9e9aeecb29eb6) feat: add cilium e2e tests * [`061640ccc`](https://github.com/siderolabs/talos/commit/061640cccf69d065806140e670e484c8b1c5a26e) feat: add pod ip to kube-proxy spec * [`dea17d723`](https://github.com/siderolabs/talos/commit/dea17d72340b1deddea2215b556a4f193d1feb7f) feat: update Kubernetes to v1.26.2 * [`337aaba7a`](https://github.com/siderolabs/talos/commit/337aaba7a705536b885d7336343b828dd13e1de4) feat: add 'os:operator' role * [`40e69af22`](https://github.com/siderolabs/talos/commit/40e69af2242fcd91f4a351da02de1b94158d419c) fix: improve etcd leave on reset process * [`638dc9128`](https://github.com/siderolabs/talos/commit/638dc9128fd89f70ddab8d6f342ca5a2e5131be8) fix: fix "defer" leak in ResetUserDisks * [`bfba3677b`](https://github.com/siderolabs/talos/commit/bfba3677b0e85a27a8b92235f5763ac6fc8e0375) chore: handle grub option - "wipe" * [`594f27d87`](https://github.com/siderolabs/talos/commit/594f27d87870ef26fc7166a95a64a40d27cb165a) release(v1.4.0-alpha.2): prepare release * [`b52071081`](https://github.com/siderolabs/talos/commit/b5207108104eda426361c256ec4d78ae9e0b2890) feat: introduce new flag in reset API that makes Talos reset user disks * [`f55f5df73`](https://github.com/siderolabs/talos/commit/f55f5df7396b7073e75267c7e10a35814f1185c9) feat: move dashboard package & run it in tty2 * [`36e077ead`](https://github.com/siderolabs/talos/commit/36e077ead458f15e864f62eeb0d7afa59187c226) chore: bump deps * [`5a01d5fd4`](https://github.com/siderolabs/talos/commit/5a01d5fd473cdc4e0b9fba48047d6434cf31ee42) chore: run extension build as downstream * [`426fe9687`](https://github.com/siderolabs/talos/commit/426fe9687d74690df26ce3cfd6aee47c13e994a8) fix: extension base folder permission * [`609d3a8a6`](https://github.com/siderolabs/talos/commit/609d3a8a694ff90426ce33be86791f2616ec4852) feat: support strategic merge patches on VLAN configuration * [`7e19f32d7`](https://github.com/siderolabs/talos/commit/7e19f32d762dc1363f29e988ddbe334bd00610f2) chore: provide version compatibility data for Talos 1.2.x * [`230e46e56`](https://github.com/siderolabs/talos/commit/230e46e567012d8e12e384c777d6f57db5e7cfee) refactor: extract parts of kubernetes libraries * [`f3d3f0f26`](https://github.com/siderolabs/talos/commit/f3d3f0f2625f1be41a17366ee1c0bd2a3193c08c) fix: update go-smbios library with Hyper-V data fix * [`8711eea96`](https://github.com/siderolabs/talos/commit/8711eea9626a60a996347aaa7e6a89eea87d4b9e) fix: use passed `--context` in `talosctl config` cmd * [`5ac9f43e4`](https://github.com/siderolabs/talos/commit/5ac9f43e45f85f8d37c2855051b9a5cc9ad389ac) feat: start machined earlier & in maintenance mode * [`36ab414a1`](https://github.com/siderolabs/talos/commit/36ab414a1d7c5472522d20a7b698c4eebb3423b9) docs: fix the endpoints in the libvirt guide * [`3d55bd80f`](https://github.com/siderolabs/talos/commit/3d55bd80f42b7d2439541909c9534c386607e578) fix: add `--force` flag to `talosctl gen config` * [`660b8874d`](https://github.com/siderolabs/talos/commit/660b8874da7bd91946aab5f400e7d1dfddefb827) feat: cmdline integer netmask * [`1e3daacc4`](https://github.com/siderolabs/talos/commit/1e3daacc48c0b8ef2eab41b2c2c53f55522e1acf) docs: update nvidia component versions * [`b5c03a7fa`](https://github.com/siderolabs/talos/commit/b5c03a7fab8d213e7048a8f5fc129125b81eb205) fix: docker talosctl cluster create provisioner * [`6e8f13529`](https://github.com/siderolabs/talos/commit/6e8f13529c17ff4c658b340d16d9ee429cfd9a4c) fix: add support for a fallback '*' mirror configuration * [`dcd4eb1a9`](https://github.com/siderolabs/talos/commit/dcd4eb1a93737d60f60693d8c33a20052eee4a4f) fix: improve error message on single node upgrade * [`ed5af3f78`](https://github.com/siderolabs/talos/commit/ed5af3f780732fb0004ddb263feedbf2de9fd09a) chore: bump deps * [`0dc6858e5`](https://github.com/siderolabs/talos/commit/0dc6858e5ba4b110eac9ca74294eb3a29790a323) chore: bump cosi-project/runtime * [`da2edb9de`](https://github.com/siderolabs/talos/commit/da2edb9de067fc21c792e948903bc2c880b2c2d1) chore: bump dependencies * [`e51a110f0`](https://github.com/siderolabs/talos/commit/e51a110f0e876fc091aee0828aca0135499def9c) chore: bump dependencies * [`2d0148018`](https://github.com/siderolabs/talos/commit/2d014801803fa0d5f08a344bdc9ff078b3931633) feat: automatically load modules based on hw info * [`7b75cd8b9`](https://github.com/siderolabs/talos/commit/7b75cd8b94367645adb2dd5be016e6f98d8e6a89) fix: kernel module dependency tree generation * [`65d02e5ad`](https://github.com/siderolabs/talos/commit/65d02e5ade08354aeec794d4131a1f8913fba2b5) fix: dbus shutdown when it's not initialized * [`a7079ce85`](https://github.com/siderolabs/talos/commit/a7079ce85c9839933544b637100f104f02fd3f3a) fix: quote the ampersand character in GRUB config * [`933ba2d82`](https://github.com/siderolabs/talos/commit/933ba2d8203e4418414b3de1c4240c1f88cb033e) fix: display correct blockdevice size * [`c449cb736`](https://github.com/siderolabs/talos/commit/c449cb736b24b268b965da5e2932f18bd4fb7785) fix: talosctl reboot command passing mode in wait mode * [`34ab0007a`](https://github.com/siderolabs/talos/commit/34ab0007a61bbb685d8c194c06568974db2a7375) docs: port is needed for wireguard endpoint * [`1e1aa84f6`](https://github.com/siderolabs/talos/commit/1e1aa84f6cdd0fbe6dd35841b6195cc56f10d333) fix: kubernetes removed resource version check * [`dcbcf5a93`](https://github.com/siderolabs/talos/commit/dcbcf5a93c3d82f8fdd7b8ffef3819010bd1c481) fix: wait for network and retry in platform get config funcs * [`3d7566ec7`](https://github.com/siderolabs/talos/commit/3d7566ec743f573a43a4a49ecb80f6ba59cbb27b) test: update Canal CNI manifest URL * [`e09e10666`](https://github.com/siderolabs/talos/commit/e09e106665aa8716f14ba49d527d8cb182592da7) fix: default dns domain to 'cluster.local' in local case * [`cc6e37a47`](https://github.com/siderolabs/talos/commit/cc6e37a47fd2ca9f1e43ce8ba2c1e8d8bfe44776) feat: use process wrapper for dropping capabilities * [`0c6c88874`](https://github.com/siderolabs/talos/commit/0c6c888745c5482fcf3891c922cc7cc7f72e6af4) fix: trackable action flag usage text. --no-wait does not exist * [`5cb2915d8`](https://github.com/siderolabs/talos/commit/5cb2915d8ea6e4ba913396abe3f45235e6a67213) feat: use wrapper for starting processes * [`56d945326`](https://github.com/siderolabs/talos/commit/56d9453261d47c0739be21cb7a5fe6beb25cb92c) fix: panic in talosctl cluster show * [`38a51191e`](https://github.com/siderolabs/talos/commit/38a51191e49059e93f4adfea479c039819a7f730) fix: correctly expand parameters in the URL * [`af21860a2`](https://github.com/siderolabs/talos/commit/af21860a22598361f68cf49e62a12da54bc95337) fix: return proper error if download attempts time out * [`54f7d4c92`](https://github.com/siderolabs/talos/commit/54f7d4c9231e858216f3b69b2662d7cc188df4f9) fix: correctly quote and unquote strings in GRUB config * [`54cf0672a`](https://github.com/siderolabs/talos/commit/54cf0672a71a8c9427c66bb2601521a9d24f8e13) fix: omit zero MTU in the machine config * [`bdc53ac25`](https://github.com/siderolabs/talos/commit/bdc53ac254a4aaa37ffd917c7c3ad506368205de) docs: add hyperlink to Docker API docs about `config.json` * [`b3bc06dd1`](https://github.com/siderolabs/talos/commit/b3bc06dd14c7faa75269cb6686b2d93ce765595c) chore: bump vtprotobuf to v0.4.0 * [`0ba5e59f6`](https://github.com/siderolabs/talos/commit/0ba5e59f69c08ab566177df9e26a21648bcde54f) fix: drone config for renovate PR's * [`590a393de`](https://github.com/siderolabs/talos/commit/590a393de968556bb5e19594b2f057d4233c378d) fix: udevd healthcheck * [`2b6b6deac`](https://github.com/siderolabs/talos/commit/2b6b6deacda4a3cdf6c5b65ac586cad1363be094) docs: simplify and clarify digital ocean docs * [`92bc15f7f`](https://github.com/siderolabs/talos/commit/92bc15f7f1c561b1e7810371df23f84c7e0d6a1c) release(v1.4.0-alpha.1): prepare release * [`e3da4754e`](https://github.com/siderolabs/talos/commit/e3da4754e7a2e69b998b861034c6f77e2cf6355b) feat: update Linux to 6.1.7 * [`006449e46`](https://github.com/siderolabs/talos/commit/006449e464ac009e15d78bb4d71cee80f2540f31) test: build integration test early in the pipeline * [`09aa71264`](https://github.com/siderolabs/talos/commit/09aa7126422b9b41e74c3d2aacb563daeca33bc5) fix: renovate config * [`2d136f187`](https://github.com/siderolabs/talos/commit/2d136f1879ee66dbd61ab40bb001a45c0bafaad5) feat: set markdown and html descriptions in config json schema * [`f0804027a`](https://github.com/siderolabs/talos/commit/f0804027a499a6e195f049144bff4f939dee3780) fix: renovate config * [`812a2877c`](https://github.com/siderolabs/talos/commit/812a2877cdc1e631ae0244f9696a65e2347594c0) chore: bump deps + renovate cleanup * [`aa9f66c1c`](https://github.com/siderolabs/talos/commit/aa9f66c1c88a1bb35aefe24ea0a5c3a6e7aa966d) fix: mark DigitalOcean anchor IP as scope link * [`bb4937f1b`](https://github.com/siderolabs/talos/commit/bb4937f1b339384fb486cb0cb675df8bf9b9f916) feat: enable renovate * [`3e0057162`](https://github.com/siderolabs/talos/commit/3e00571627568d8c5ab10a72e59207677a89e4cc) fix: unwrap gRPC errors on stop/remove pods check * [`00e52ae07`](https://github.com/siderolabs/talos/commit/00e52ae07867deff9a5877fcb498252bc1b1a740) fix: build correctly etcd initial cluster URL * [`ae83b10ae`](https://github.com/siderolabs/talos/commit/ae83b10ae89dbe600ddfaa338be95ea819546007) feat: create JSON schema for v1alpha1.Config * [`703d96595`](https://github.com/siderolabs/talos/commit/703d9659512d744a606e520faf230e20efddfc4a) feat: update Kubernetes to 1.26.1, etcd to 3.5.7 * [`965e64591`](https://github.com/siderolabs/talos/commit/965e645915d080487a74b35dc8f1d2e4051f0504) docs: update to use talosctl install script * [`c5954f434`](https://github.com/siderolabs/talos/commit/c5954f4345cbf3a92c777a0e7fc5d39e883609bf) chore: bump deps * [`bb50f6a56`](https://github.com/siderolabs/talos/commit/bb50f6a56d971915abb6a895aac9d7e0612a3255) chore: preallocate disk images for QEMU VMs * [`d4b8b35de`](https://github.com/siderolabs/talos/commit/d4b8b35de7849d887c41f9a13dadb59ccd8c08c4) feat: generate kernel module dependency tree * [`18122ae73`](https://github.com/siderolabs/talos/commit/18122ae73e0489a0497956c6d4621c05c6a77387) fix: service restart (including extension services) * [`680fd5e45`](https://github.com/siderolabs/talos/commit/680fd5e452e02b108b7938d0136079c16e6cfd79) fix: bump COSI runtime with the panic controller restart fix * [`0b65bbfc8`](https://github.com/siderolabs/talos/commit/0b65bbfc878fe2a5c01c5d2cd08006b53fda7cf9) fix: handle overwriting tags in syslinux ADV * [`70d9428a1`](https://github.com/siderolabs/talos/commit/70d9428a1d00d9894d68f38b255debb66fe8a440) fix: kubespan MSS clamping * [`683b4ccb4`](https://github.com/siderolabs/talos/commit/683b4ccb4faab6c3da2de00f7314773f42899c25) chore: update Go to 1.19.5 and kernel to 6.1.4 * [`062c7d754`](https://github.com/siderolabs/talos/commit/062c7d754be1714c7763b8f2b399436d64c90ea4) test: fix integration test on cp endpoint update * [`8e9fc13d7`](https://github.com/siderolabs/talos/commit/8e9fc13d7c48da5c5354501e0ad96688670438cf) feat: implement enum generator for proto files * [`771b0dc06`](https://github.com/siderolabs/talos/commit/771b0dc061e0fa33085b28bd0d0a7e4da13081f1) docs: update left over rpi_4 ref to rpi_generic * [`6c04b5f79`](https://github.com/siderolabs/talos/commit/6c04b5f79e6e01e0a3cdabfc99f12c944edd1f0a) chore: bump dependencies * [`0a5a8802e`](https://github.com/siderolabs/talos/commit/0a5a8802e7e337e1f30a40c9f566e57642c39c1a) feat: use 'localhost' endpoint for controlplane nodes * [`b0775ebf2`](https://github.com/siderolabs/talos/commit/b0775ebf2c776c7133cf74c6259de9dc9573786c) feat: add ISO wipe GRUB boot option * [`29020cb9c`](https://github.com/siderolabs/talos/commit/29020cb9c788d87a0457028ce73c8d297959116e) fix: report fatal sequence errors as reboots * [`96629d5ba`](https://github.com/siderolabs/talos/commit/96629d5ba6c1ae9d820824fb38f68112bce27f2c) feat: implement etcd maintenance commands * [`80fed3194`](https://github.com/siderolabs/talos/commit/80fed319408be9e493141fb2c01e5731708835c7) feat: include Kubernetes controlplane endpoint as one of the endpoints * [`c6cb36cc1`](https://github.com/siderolabs/talos/commit/c6cb36cc1f50b5d0e59a5284867e7534dc9f73bb) docs: fix auditpolicy example typo * [`ba8265bc5`](https://github.com/siderolabs/talos/commit/ba8265bc5ce63bcbc6fbd6c1a1076dc3f2ee6bd0) feat: new talosctl config remove to remove context * [`fcb19ff51`](https://github.com/siderolabs/talos/commit/fcb19ff516cc1200ec81f2a954bb6d2ce39ebdc6) fix: implement upgrade version checks for Talos 1.4 * [`80f150ac8`](https://github.com/siderolabs/talos/commit/80f150ac859f5dbf95060c12440afab8c0bc77a8) feat: enable ipv6 on gcp * [`8db622f3d`](https://github.com/siderolabs/talos/commit/8db622f3dc75aed90dd2d0bd92d03aa7e8aefd10) docs: add Vandebron to adopters list * [`f6a86ae90`](https://github.com/siderolabs/talos/commit/f6a86ae90607914c29875df750fe79cbbfcc5897) fix: oralce cloud zone * [`89dbb0ecf`](https://github.com/siderolabs/talos/commit/89dbb0ecf089bb746479238df274ccba4fcb049a) release(v1.4.0-alpha.0): prepare release * [`31fb90535`](https://github.com/siderolabs/talos/commit/31fb9053582190b3b536a309c30e2b78c4611885) feat: update Linux 6.1.1, containerd 1.6.14 * [`a0c0352dd`](https://github.com/siderolabs/talos/commit/a0c0352ddca253e1efb3679224b317692d46b2fd) fix: send diagnostic output to stderr consistently * [`9a5f4c08a`](https://github.com/siderolabs/talos/commit/9a5f4c08a206504a1d30277dcc0597333e5a927a) fix: default the manifest namespace if not set * [`3c6cce5fe`](https://github.com/siderolabs/talos/commit/3c6cce5fe47075f43a73682b57a7b40fa0899795) docs: update last release for Talos 1.2.x * [`703624c43`](https://github.com/siderolabs/talos/commit/703624c43dd8e58c147ccbc3989c6c436c9f3a7f) docs: fix the 1.3 release date * [`386c9293a`](https://github.com/siderolabs/talos/commit/386c9293a33e9d237fbeda0492b01b11fdadc501) docs: update nvidia-container-runtime version * [`ff83d9fd7`](https://github.com/siderolabs/talos/commit/ff83d9fd7bed2e04d5c8107713150c2513f47991) fix: improve talosctl completion * [`31ff431fa`](https://github.com/siderolabs/talos/commit/31ff431faec22c09cad88d565102e6a24785ecb4) chore: add schulz systemtechnik to the list * [`97bef7c47`](https://github.com/siderolabs/talos/commit/97bef7c47bfd133f2b3ad19efe3f30a88dd67460) docs: vsphere.sh > vmware.sh * [`34babe858`](https://github.com/siderolabs/talos/commit/34babe858d15145a1c596febb5e577473e4ffce0) chore: make organization selection an interface * [`a9643b477`](https://github.com/siderolabs/talos/commit/a9643b477417029db73aacbfcf5778cedd97cd95) fix: use proper key usage for apid client certificate * [`171aa9467`](https://github.com/siderolabs/talos/commit/171aa9467966f5869e72374961ea05abc8d9fda9) fix: disable Wireless Lan using dtoverlay * [`2e84d2ab3`](https://github.com/siderolabs/talos/commit/2e84d2ab3417515f539a70d58885dcb69e9f098c) chore: update conformance product.yaml * [`b7763843a`](https://github.com/siderolabs/talos/commit/b7763843af63bbc186f08701a62c19ea96fb7e3c) feat: add install script that improves talosctl installation user experience This install script detects the platform and architecture, and downloads the correct talosctl, and checks the gpg checksums. It also installs and chmods the binary. * [`afc45ad63`](https://github.com/siderolabs/talos/commit/afc45ad632e63cc3afc095b1f3efe6df3ecb9cb1) docs: mark Talos 1.3 docs as default * [`873bd3807`](https://github.com/siderolabs/talos/commit/873bd3807c0fcca2e212deb7fd044662557964c1) fix: redact service account key in config in RedactSecrets method * [`b3aebfadf`](https://github.com/siderolabs/talos/commit/b3aebfadfc15544e5ab448d979129dba5e516c59) feat: validate Talos API access roles in machine config * [`40761e17d`](https://github.com/siderolabs/talos/commit/40761e17db5789f30eef2f15f0b5c6396e09a9e5) docs: fork docs for Talos 1.4 * [`474604cd2`](https://github.com/siderolabs/talos/commit/474604cd279def7a6798e24ede27feef955ba5a3) docs: update documentation for Talos 1.3 * [`faf49218c`](https://github.com/siderolabs/talos/commit/faf49218ce14a48829dae7b3b8d7801188453a89) feat: add more checks for K8s upgrade * [`5b992bd86`](https://github.com/siderolabs/talos/commit/5b992bd8610f41d23d8b7dbd01f9a1be298eda96) fix: allow empty dnsDomain in machine config * [`eb332cfcb`](https://github.com/siderolabs/talos/commit/eb332cfcb785e250c422d6a7ea2b23679189a946) feat: add health check for a minimal memory / disk size * [`d04970dfa`](https://github.com/siderolabs/talos/commit/d04970dfa9d6554e1ee447fd9383bf65b8953671) fix: ignore k8s additional addresses if nil * [`63c17104c`](https://github.com/siderolabs/talos/commit/63c17104c594dfd9ca4066ba41d8a03507464874) feat: update Kubernets to 1.26.0 * [`f7a9a90db`](https://github.com/siderolabs/talos/commit/f7a9a90db2bfd316ea01551daba9becb15361f94) chore: update pkgs/tools (Go 1.19.4, containerd 1.6.11) * [`cf7adc51c`](https://github.com/siderolabs/talos/commit/cf7adc51c9f53234e469dd9f0cca06eed0230e8b) feat: add RedactSecrets method to v1alpha1.Config * [`4c31b9b1a`](https://github.com/siderolabs/talos/commit/4c31b9b1a3a00df0fe817c3edc15260ca3cadd6d) docs: clarify what the deal is with /var * [`a8ebcca4a`](https://github.com/siderolabs/talos/commit/a8ebcca4a9f63643f68d8e85bcb0b9ddb49205ed) chore: remove `watchErr` from `metal.getResource` * [`1253513bd`](https://github.com/siderolabs/talos/commit/1253513bd1deecc4cc42330bad0a713b3630240a) fix: fix nil pointer panic and incorrect error output * [`82e8c9e1f`](https://github.com/siderolabs/talos/commit/82e8c9e1f63371f41b0794b4c1be3209847c5f8b) fix: workaround panic in the kubelet service controller * [`a505b8909`](https://github.com/siderolabs/talos/commit/a505b8909a1c733b30f22a8d46eebc022475431a) fix: update COSI and reset restart backoff on success * [`e92fdcbad`](https://github.com/siderolabs/talos/commit/e92fdcbad1de595d119f78dbed3a97ae46df9bbf) chore: bump kernel to 5.15.81 * [`f0dddca2a`](https://github.com/siderolabs/talos/commit/f0dddca2a3d2e976cee543ab57816a6395fe3d65) docs: expand help for 'talosctl get' * [`fcffc8879`](https://github.com/siderolabs/talos/commit/fcffc88790b5a3006b3b85744771a7eef6e8ac5c) fix: add ext4 filesystem detection * [`5b2960eff`](https://github.com/siderolabs/talos/commit/5b2960efff8b38af85b687a25fa93f01256016de) fix: introduce 'overridePath' setting and fix Talos resolver * [`0219d1124`](https://github.com/siderolabs/talos/commit/0219d1124e5125696364bf92ecf0e8dcad644001) fix: use only kube-apiserver endpoints for Talos API access endpoints * [`dc5e0f4af`](https://github.com/siderolabs/talos/commit/dc5e0f4af087d3b662b0240b4f8fd76379ed0de2) fix: report errors to Equinix Metal event API * [`7ab140a94`](https://github.com/siderolabs/talos/commit/7ab140a94ad1a279be43669d6d70687f3a0c47de) feat: add talosctl machineconfig patch command * [`d3cf06114`](https://github.com/siderolabs/talos/commit/d3cf061149a4a502317d7728c45b6cfb4d38f89f) fix: ignore many more filesystems in IMA * [`44e2799b8`](https://github.com/siderolabs/talos/commit/44e2799b8cb928083f3a777d5cce45ad8dbf6864) feat: add stdout and single config type support to talosctl gen config * [`4452f0e17`](https://github.com/siderolabs/talos/commit/4452f0e179db16c59dc65ccdb5a496ad3306684e) docs: bump talos version * [`38e57bd12`](https://github.com/siderolabs/talos/commit/38e57bd12b8c50d668fcde6ee9aa493682778dcc) feat: update Kubernetes to v1.26.0-rc.1 * [`4cd125d49`](https://github.com/siderolabs/talos/commit/4cd125d499a24798dfde1dddf6fa1c689d16c93f) fix: correctly handle new watch event types * [`881b84152`](https://github.com/siderolabs/talos/commit/881b84152084d157fbd4ff992089a5392aadfd3c) feat: update Flannel to 0.20.2

### Changes since v1.4.0-alpha.3
21 commits

* [`7ffabe0f1`](https://github.com/siderolabs/talos/commit/7ffabe0f14dd3432857423743701ed5cdb3fe07f) feat: support network bond device selectors * [`cbab12e3a`](https://github.com/siderolabs/talos/commit/cbab12e3a1f2c576164bb721dc70073e6b8d3767) refactor: rename outbound to connectivity on dashboard * [`07c3c5d59`](https://github.com/siderolabs/talos/commit/07c3c5d59e02d82dbc1ff6f5392c2aa428503e0e) feat: return disk subsystem in the `Disks API` * [`b8497b99e`](https://github.com/siderolabs/talos/commit/b8497b99eb46fafd0f908c768d92683771f58cc3) feat: update containerd to 1.6.20 * [`aa1499353`](https://github.com/siderolabs/talos/commit/aa149935390b0ea49e6b6de51a2eeccd6cbcbbcb) feat: introduce network probes * [`9dc1150e3`](https://github.com/siderolabs/talos/commit/9dc1150e3aa08e5cb85c8fb6ecf0cfec5c613029) docs: update nvidia instructions * [`7967ccfc1`](https://github.com/siderolabs/talos/commit/7967ccfc13a534cce32bb49558bb4bbeb5ee4480) feat: add config code entry screen to dashboard * [`ddb014cfd`](https://github.com/siderolabs/talos/commit/ddb014cfdcb3630055d5b7be7f480080612dfc11) fix: udevd rules trigger * [`0af8fe2fb`](https://github.com/siderolabs/talos/commit/0af8fe2fb547e01be1b1231d69ccf2fdc5f2ea0d) feat: netstat pod support * [`52e857f55`](https://github.com/siderolabs/talos/commit/52e857f55ef14e88ed5ac6f7fb73a08a5ec8fe7a) feat: linux 6.1.22, runc 1.1.5 * [`aa662ff63`](https://github.com/siderolabs/talos/commit/aa662ff635f4739343489076bdb9a11722e28798) fix: apply small fixes on dashboard * [`188560a33`](https://github.com/siderolabs/talos/commit/188560a334a39f4e6472d196ce33bcfa88e41102) fix: add a link-scope route if the cmdline gateway is not reachable * [`45c5b47a5`](https://github.com/siderolabs/talos/commit/45c5b47a57c0c7efdc126f24f880238b9aec9781) feat: dhcpv4: send current hostname, fix spec compliance of renewals * [`289b41fe4`](https://github.com/siderolabs/talos/commit/289b41fe4b3af7bd4e1e61a1ca30dc1ed2b0d027) fix: output of `talosctl logs` might be corruped * [`02f0a4526`](https://github.com/siderolabs/talos/commit/02f0a4526d09334757a71d868d9e2ae70aa1aade) feat: allow writing initial META values into the image * [`ea0e9bdbe`](https://github.com/siderolabs/talos/commit/ea0e9bdbe454041f8895e0a869e28eabb5156430) feat: environment variables via the kernel arguments * [`94c24ca64`](https://github.com/siderolabs/talos/commit/94c24ca64e70f227da29cd02bd367d3c2701b96c) chore: add machine config version contract for v1.4 * [`cefa9c3ec`](https://github.com/siderolabs/talos/commit/cefa9c3ecb5675c80b44a2fe3aaa55e402cab7a6) feat: update Kubernetes to 1.27.0-rc.0 * [`9e8603f53`](https://github.com/siderolabs/talos/commit/9e8603f53b83e326a2529b2c595b619e04f2b85b) feat: implement new download URL variable `${code}` * [`d30cf9c86`](https://github.com/siderolabs/talos/commit/d30cf9c86efc30ce2c61b82be1bfb431cc74dd78) test: fix misprint in e2e scripts * [`0d0bb31cf`](https://github.com/siderolabs/talos/commit/0d0bb31cf766ece4c78ffe3f4094f94a9990d88c) fix: use stripped kernel modules

### Changes from siderolabs/discovery-api
1 commit

* [`ac75538`](https://github.com/siderolabs/discovery-api/commit/ac75538ee3a9f7b71b6619f509d95ff5057f6754) chore: regen the proto definitions with vtprotobuf v0.4.0

### Changes from siderolabs/discovery-client
1 commit

* [`269a832`](https://github.com/siderolabs/discovery-client/commit/269a832ce9e35d4edeeddba2a23cf5682a2ca425) chore: rekres, update discovery api

### Changes from siderolabs/extras
9 commits

* [`7faf14a`](https://github.com/siderolabs/extras/commit/7faf14a523df4a25073fc463d459d7565b90551d) chore: bump pkgs to v1.4.0 * [`343956e`](https://github.com/siderolabs/extras/commit/343956eb882eed775c68ef5af3bd37407aa914f4) feat: update Go to 1.20.2 * [`6209d87`](https://github.com/siderolabs/extras/commit/6209d8774d2ace990f532ab88cf2fa6464c8bafa) chore: bump tc-redirect-tap * [`8b28b6b`](https://github.com/siderolabs/extras/commit/8b28b6b5a0153c65af596086016faea9d64e95c2) chore: bump deps * [`5ab4f59`](https://github.com/siderolabs/extras/commit/5ab4f5939c830c7043e3939e519305eb810cdfc2) chore: disable renovate builds * [`ddeddbd`](https://github.com/siderolabs/extras/commit/ddeddbd1976813de6b1563f662ca4f2b3f5e0f53) chore: update packages, tc_redirect_tap * [`8cb4792`](https://github.com/siderolabs/extras/commit/8cb4792da9b9e2b2663daca747d24c3b5c973e0f) chore: update Go to 1.19.5 * [`3ca2df3`](https://github.com/siderolabs/extras/commit/3ca2df3ead2a64a5ad30c350b87bfe02bf1f49c7) chore: disable provenance in buildx * [`55d8452`](https://github.com/siderolabs/extras/commit/55d845241c8456909ab36f9b0f4e26cc2b49c256) feat: update releases

### Changes from siderolabs/gen
2 commits

* [`214c1ef`](https://github.com/siderolabs/gen/commit/214c1efe795cf426e5ebcc48cb305bfc7a16fdb8) chore: set `slice.Filter` result slice cap to len * [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods

### Changes from siderolabs/go-blockdevice
2 commits

* [`b4386f3`](https://github.com/siderolabs/go-blockdevice/commit/b4386f37510bc25e39b231fa587288ad0abf0b68) feat: make disk utils read subsystem information from the `/sys/block` * [`8c7ea19`](https://github.com/siderolabs/go-blockdevice/commit/8c7ea1910b27e0660e3e1a6f98b9f7e24bc11ff0) fix: blockdevice size is reported by Linux in 512 blocks always

### Changes from siderolabs/go-kmsg
1 commit

* [`7a51094`](https://github.com/siderolabs/go-kmsg/commit/7a51094e29290697aaeed8f09ccb045634876801) fix: exit properly on context cancel

### Changes from siderolabs/go-kubernetes
4 commits

* [`81887dc`](https://github.com/siderolabs/go-kubernetes/commit/81887dcae8916ccee820af000efe73c151de29a4) feat: add kubelet flag checks * [`fe473c0`](https://github.com/siderolabs/go-kubernetes/commit/fe473c0595e8e2e861fc16d0cddb1ba2cedf1ab3) refactor: make sync easier to consume without CLI * [`570819b`](https://github.com/siderolabs/go-kubernetes/commit/570819b93ecc63218b3db8d90e4810765a069ee0) feat: initial version of the library * [`fb79215`](https://github.com/siderolabs/go-kubernetes/commit/fb7921556e96fc7c0a84ac23834350bcd37cfa38) Initial commit

### Changes from siderolabs/go-smbios
1 commit

* [`c526764`](https://github.com/siderolabs/go-smbios/commit/c5267640be317efd9cbbe936ab78b2a49c757edf) feat: fix reading "broken" Hyper-V DMI data

### Changes from siderolabs/pkgs
39 commits

* [`aadb943`](https://github.com/siderolabs/pkgs/commit/aadb9439f6eabe1996aec742e086dcb21a0912ab) feat: update containerd to 1.6.20 * [`5a7b33e`](https://github.com/siderolabs/pkgs/commit/5a7b33e9d025a945caf02363f6a0ca2d8b552ffc) chore: bump deps * [`5d77814`](https://github.com/siderolabs/pkgs/commit/5d77814d3789807aed0b6e52acc21f68168ba977) fix: strip kernel modules when installing * [`c26b0b5`](https://github.com/siderolabs/pkgs/commit/c26b0b504e5fd0f68432503aabd2653c07888706) chore: bump deps * [`7d8f5bd`](https://github.com/siderolabs/pkgs/commit/7d8f5bd7170464c4f017c8e747dd5eda40c35639) feat: enable Hyper-V dynamic memory driver * [`ea40205`](https://github.com/siderolabs/pkgs/commit/ea4020599aeb1cd0f78abcfd19c546026bfb0634) chore: bump deps * [`21e5a68`](https://github.com/siderolabs/pkgs/commit/21e5a6806288f535773cd8afc20b12ee3082caa8) feat: update Go 1.20.2, Linux 6.1.15 and other * [`1d7e60c`](https://github.com/siderolabs/pkgs/commit/1d7e60cc2da55b0a31bd225479c86f517c7a878f) feat: enable framebuffer drivers and console fonts * [`0e63e95`](https://github.com/siderolabs/pkgs/commit/0e63e955dd118b6d5e8a9dd443c72a5d35d639a6) chore: bump deps * [`5dbce6b`](https://github.com/siderolabs/pkgs/commit/5dbce6b19ff6a1e1b5ae88468e34925c3d30d627) fix: xz url * [`0097233`](https://github.com/siderolabs/pkgs/commit/00972336c3fcc22df8fc1d3774c35b26fdc957b9) chore: re-enable drbd * [`7493721`](https://github.com/siderolabs/pkgs/commit/749372110c6c8e226139cd662832b5a4169db894) fix: sourcefourge url shasums * [`185f482`](https://github.com/siderolabs/pkgs/commit/185f482db6a5c13a3b14feec02a4e361b53bec55) feat: update containerd to 1.6.18 * [`e3cab6c`](https://github.com/siderolabs/pkgs/commit/e3cab6cbd62b96143958ed5e0219d68107a5f583) chore: bump deps * [`18661b0`](https://github.com/siderolabs/pkgs/commit/18661b096559e673152ce0fed45ab74ef3305dff) chore: bump deps * [`885a68b`](https://github.com/siderolabs/pkgs/commit/885a68b6280f3bf4ff75508ccceef73158c53560) chore: bump deps * [`c3a6e18`](https://github.com/siderolabs/pkgs/commit/c3a6e185178d7571e891c7b2614bf6017ab5c913) chore: bump dependencies * [`1fae0b2`](https://github.com/siderolabs/pkgs/commit/1fae0b229a625d692d36e7d6c096f8476e0f56d7) feat: virtio drivers as modules * [`61d8ff4`](https://github.com/siderolabs/pkgs/commit/61d8ff4aaea93b86b82bc2a36a2bbd6d54da3bb8) chore: bump deps and disable un-needed kconfig * [`15fe6d8`](https://github.com/siderolabs/pkgs/commit/15fe6d8555b42e55f920a5576ad55504e356995b) fix: kernel module tree files missing * [`987d24a`](https://github.com/siderolabs/pkgs/commit/987d24aeaa4fb2278954cd96e6bc6a29a4c8dd61) feat: mellanox drivers are modules * [`b82a015`](https://github.com/siderolabs/pkgs/commit/b82a015c78c407d17d23542eba6a4114f3c2c4d7) feat: mellanox oped * [`057d4f9`](https://github.com/siderolabs/pkgs/commit/057d4f96aa3ba63cc456b06a70a6b3a008cf803f) chore: bump deps * [`4ac4138`](https://github.com/siderolabs/pkgs/commit/4ac4138c6b94622646c9f32f0885496c5475d905) feat: enable nvme support for raspberrypi cm4 * [`ccb9d39`](https://github.com/siderolabs/pkgs/commit/ccb9d39dc43cf53431a0d7609839ed9c7141972d) fix: disable magic sysrq * [`d33202d`](https://github.com/siderolabs/pkgs/commit/d33202d99daa6ccf136fca54ebbadda727a43a75) chore: bump u-boot to 2023.01 * [`cb83e16`](https://github.com/siderolabs/pkgs/commit/cb83e169df4a2020994a63e5be61524461ef93e3) chore: bump dependencies * [`e561dcb`](https://github.com/siderolabs/pkgs/commit/e561dcb45beae80161faccedb0303e58d41b1ded) feat: bump Go to 1.19.5 * [`c7797c7`](https://github.com/siderolabs/pkgs/commit/c7797c77bd311449e1f116980166d8d818102f4f) feat: update Linux to 6.1.4, restore RPi support * [`5e8ebb0`](https://github.com/siderolabs/pkgs/commit/5e8ebb073d9b58555a75912cd90490af8a435c7d) feat: add AMD K10 sensor support * [`73ac37d`](https://github.com/siderolabs/pkgs/commit/73ac37d683274e60340d2767f2b8201e7f13474c) chore: disable provenance in buildx * [`8965bee`](https://github.com/siderolabs/pkgs/commit/8965bee65313539e8b6534073d06341f4fb78586) chore: use default symlinks to `/bin` in `base` * [`325c9bf`](https://github.com/siderolabs/pkgs/commit/325c9bf0f3ed2bf7603d1eaea022ea650388cf2b) feat: bump dependencies * [`165dff6`](https://github.com/siderolabs/pkgs/commit/165dff6c3cdb2d05f170c8ae0616d9224416455e) fix: patch ipmitool IANA URL * [`c542f39`](https://github.com/siderolabs/pkgs/commit/c542f398a150567d5cdffc17b4248be5416fe242) feat: add kernel support for usb setrial console * [`f564f45`](https://github.com/siderolabs/pkgs/commit/f564f45645d102b7e3a9563ac7bdb1e816156e65) chore: bump tools, containerd * [`268ea7c`](https://github.com/siderolabs/pkgs/commit/268ea7c593ff04c4e4a9ea5676b3c58d41cbff14) chore: bump deps * [`dcf3ceb`](https://github.com/siderolabs/pkgs/commit/dcf3cebf283698e010aaac5417d91a7385dc2441) feat: add nitro enclave support in kernel * [`17ea5e6`](https://github.com/siderolabs/pkgs/commit/17ea5e680b2438c59fa1773e8b58d6b749cb0d34) chore: bump kernel to 5.15.81

### Changes from siderolabs/tools
31 commits

* [`95f814a`](https://github.com/siderolabs/tools/commit/95f814ab50a28d9418b5c5f1c20ca8eb6e3590de) feat: cmake 3.26.2 * [`a3d5bac`](https://github.com/siderolabs/tools/commit/a3d5bac13858653922ecb0fe57056f20ad9a47b9) chore: bump deps * [`2d710f9`](https://github.com/siderolabs/tools/commit/2d710f9074caefcbd1cd37190dda02372e851500) chore: bump deps * [`9bea7d0`](https://github.com/siderolabs/tools/commit/9bea7d04310bfb1177e55a9e4fe1606b81ad8dbd) chore: skip rc versions for util-linux * [`a94850e`](https://github.com/siderolabs/tools/commit/a94850e6dd52a2b2d08c3e4e1fe95adddcb68f20) chore: bump deps * [`e6b2956`](https://github.com/siderolabs/tools/commit/e6b29564537a54549165ea99fceff160d21634dd) fix: protoc install * [`601e347`](https://github.com/siderolabs/tools/commit/601e3475b6bb9249bcf4e2bee16791ea4f91e8f9) feat: go 1.20.2 + other bumps * [`ca67d0b`](https://github.com/siderolabs/tools/commit/ca67d0ba6ccb45f30da328fd210cbe92782c2151) chore: bump deps * [`662a906`](https://github.com/siderolabs/tools/commit/662a90650841ab6c8ffd74e4abc51654b713dd4e) feat: add libnl * [`a8440a9`](https://github.com/siderolabs/tools/commit/a8440a9c866d9837d358b53a869bcb43774f4e78) fix: partially revert e6c98fdf54425e6382f226e33bccca6f3875aad3a * [`e6c98fd`](https://github.com/siderolabs/tools/commit/e6c98fdf54425e6382f226e33bccca6f3875aad3) chore: remove swig * [`cd9687b`](https://github.com/siderolabs/tools/commit/cd9687b4323b20493b4d582cfaa48c321cd04288) fix: renovate config * [`977e3fc`](https://github.com/siderolabs/tools/commit/977e3fcba92d129eb78cb77300f38428f860b34d) chore: bump go to 1.20.1 * [`15748aa`](https://github.com/siderolabs/tools/commit/15748aa32d7c1d67b190ab7a27ace9922c8d6b56) chore: bump deps * [`d4b719a`](https://github.com/siderolabs/tools/commit/d4b719a1c2055eaa27f80422f93755b0de9ca3f8) chore: bump deps * [`8c36dbd`](https://github.com/siderolabs/tools/commit/8c36dbd05ee27ecc2a7340462a3b49efb7327184) chore: bump toolchain, bump protoc-gen-go-grpc * [`a62e365`](https://github.com/siderolabs/tools/commit/a62e365b223e7ca9d2728865b40b23115764a0ed) feat: update Go to 1.20 * [`28d4a57`](https://github.com/siderolabs/tools/commit/28d4a5721ce1c57fc3f643185386d5c4b5c7e39a) chore: reduce renovate noise * [`e130fd5`](https://github.com/siderolabs/tools/commit/e130fd5b9835d8cc178ec53d5a89dfc6cc2ce7a1) chore: bump deps * [`37612fe`](https://github.com/siderolabs/tools/commit/37612feb7222b943a84f1f98d0901a204d491926) fix: revert enabling provenance * [`e0b01e3`](https://github.com/siderolabs/tools/commit/e0b01e3b7420e8b0b1e0d9077515e007a6b83b56) chore: bump deps * [`d0e6bd0`](https://github.com/siderolabs/tools/commit/d0e6bd06fcfcadc330cf30339488536961f9f70e) feat: add gnutls * [`3d34b5d`](https://github.com/siderolabs/tools/commit/3d34b5d401a67048d365e8faf2f1edf293887a97) chore: bump dependencies * [`763c1d9`](https://github.com/siderolabs/tools/commit/763c1d927822517b3d63c624302e11e8e5a49f5b) feat: update Go to 1.19.5 * [`136958f`](https://github.com/siderolabs/tools/commit/136958f9f8c8cfc439228dec31b840549bca4374) chore: disable provenance in buildx * [`e2a8692`](https://github.com/siderolabs/tools/commit/e2a869294be7e77e295ca651400f85551fb7e665) feat: update releases * [`0e48f37`](https://github.com/siderolabs/tools/commit/0e48f37496a79ce4997d15fefb6300b2324f5668) chore: bump protobuf * [`a21aa1c`](https://github.com/siderolabs/tools/commit/a21aa1c583a10d017ace8da14c6f604f86ce5709) chore: bump toolchain and mpc versions * [`1a75d0f`](https://github.com/siderolabs/tools/commit/1a75d0f6796c4abf1c9a23cfe697d3e38a9ce587) chore: bump deps * [`55bd185`](https://github.com/siderolabs/tools/commit/55bd18532667e325e8938bf0a72cab40a936eadf) feat: update Go to 1.19.4 * [`f291f46`](https://github.com/siderolabs/tools/commit/f291f46e84ec02f5d22718f7ecb476a3f815ae45) chore: bump tools

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 -> v0.2.3 * **github.com/aws/aws-sdk-go** v1.44.147 -> v1.44.232 * **github.com/benbjohnson/clock** v1.1.0 **_new_** * **github.com/containerd/cgroups** v1.0.4 -> v1.1.0 * **github.com/containerd/containerd** v1.6.12 -> v1.6.19 * **github.com/containernetworking/plugins** v1.1.1 -> v1.2.0 * **github.com/coreos/go-semver** v0.3.0 -> v0.3.1 * **github.com/cosi-project/runtime** v0.2.0 -> v0.3.0 * **github.com/docker/docker** v20.10.21 -> v23.0.2 * **github.com/dustin/go-humanize** v1.0.0 -> v1.0.1 * **github.com/emicklei/dot** v1.2.0 -> v1.4.2 * **github.com/fatih/color** v1.13.0 -> v1.15.0 * **github.com/freddierice/go-losetup/v2** v2.0.1 **_new_** * **github.com/gdamore/tcell/v2** v2.5.3 -> v2.6.0 * **github.com/grpc-ecosystem/go-grpc-middleware** v1.3.0 -> v1.4.0 * **github.com/hashicorp/go-getter** v1.6.2 -> v1.7.1 * **github.com/hetznercloud/hcloud-go** v1.37.0 -> v1.41.0 * **github.com/insomniacslk/dhcp** f26e6d78f622 -> 74ae03f2425e * **github.com/jsimonetti/rtnetlink** v1.3.0 -> v1.3.1 * **github.com/mattn/go-isatty** v0.0.16 -> v0.0.18 * **github.com/mdlayher/ethtool** 0e16326d06d1 -> ba3b4bc2e02c * **github.com/mdlayher/genetlink** v1.3.0 -> v1.3.1 * **github.com/mdlayher/netlink** v1.7.0 -> v1.7.1 * **github.com/nberlee/go-netstat** v0.1.1 **_new_** * **github.com/prometheus/procfs** v0.8.0 -> v0.9.0 * **github.com/rivo/tview** db36428c92d9 -> 281d14d896d7 * **github.com/safchain/ethtool** v0.2.0 -> v0.3.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.10 -> v1.0.0-beta.15 * **github.com/siderolabs/discovery-api** v0.1.1 -> v0.1.2 * **github.com/siderolabs/discovery-client** v0.1.3 -> v0.1.4 * **github.com/siderolabs/extras** v1.3.0-1-g3773d71 -> v1.4.0 * **github.com/siderolabs/gen** v0.4.1 -> v0.4.3 * **github.com/siderolabs/go-blockdevice** v0.4.2 -> v0.4.4 * **github.com/siderolabs/go-kmsg** v0.1.2 -> v0.1.3 * **github.com/siderolabs/go-kubernetes** v0.2.0 **_new_** * **github.com/siderolabs/go-smbios** v0.3.1 -> v0.3.2 * **github.com/siderolabs/pkgs** v1.3.0-5-g6509d23 -> v1.4.0-1-gaadb943 * **github.com/siderolabs/talos/pkg/machinery** v1.3.0 -> v1.4.0-alpha.3 * **github.com/siderolabs/tools** v1.3.0-1-g712379c -> v1.4.0 * **github.com/stretchr/testify** v1.8.1 -> v1.8.2 * **github.com/u-root/u-root** v0.10.0 -> v0.11.0 * **github.com/ulikunitz/xz** v0.5.11 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.56.12 -> v0.56.16 * **github.com/vmware/govmomi** v0.29.0 -> v0.30.4 * **go.etcd.io/etcd/api/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/pkg/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/etcdutl/v3** v3.5.6 -> v3.5.7 * **go.uber.org/zap** v1.23.0 -> v1.24.0 * **go4.org/netipx** 797b0c90d8ab -> f1b76eb4bb35 * **golang.org/x/net** v0.4.0 -> v0.8.0 * **golang.org/x/sys** v0.3.0 -> v0.6.0 * **golang.org/x/term** v0.3.0 -> v0.6.0 * **golang.org/x/time** v0.2.0 -> v0.3.0 * **golang.zx2c4.com/wireguard/wgctrl** 97bc4ad4a1cb -> 9c5414ab4bde * **google.golang.org/grpc** v1.51.0 -> v1.54.0 * **google.golang.org/protobuf** v1.28.1 -> v1.30.0 * **k8s.io/api** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/apimachinery** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/apiserver** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/client-go** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/component-base** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/cri-api** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/klog/v2** v2.80.1 -> v2.90.1 * **k8s.io/kubectl** v0.26.0 -> v0.27.0-rc.0 * **k8s.io/kubelet** v0.26.0 -> v0.27.0-rc.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.66 -> v1.2.68 Previous release can be found at [v1.3.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0) ## [Talos 1.4.0-alpha.3](https://github.com/siderolabs/talos/releases/tag/v1.4.0-alpha.3) (2023-03-23) Welcome to the v1.4.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### talosctl containers `talosctl logs -k` and `talosctl containers -k` now support and output container display names with their ids. This allows to distinguish between containers with the same name. ### Registry Mirror Catch-All Option Talos now supports a catch-all option for registry mirrors: ```yaml machine: registries: mirrors: docker.io: - https://registry-1.docker.io/ "*": - https://my-registry.example.com/ ``` ### Talos Dashboard on TTY2 Talos now starts a text-based UI dashboard on virtual console `/dev/tty2` and switches to it by default upon boot. Kernel logs remain available on `/dev/tty1`. To switch TTYs, use the `Alt+F1` through `Alt+F2` keys. You can disable this behavior by setting the kernel parameter `talos.dashboard.disabled=1`. This behavior is disabled by default on SBCs. ### etcd Maintenance Talos adds new APIs to make it easier to perform etcd maintenance operations. These APIs are available via new `talosctl etcd` sub-commands: * `talosctl etcd alarm list|disarm` * `talosctl etcd defrag` * `talosctl etcd status` See also [etcd maintenance guide](https://talos.dev/v1.4/advanced/etcd-maintenance/). ### Kernel Modules Talos now supports automatically loading kernel drivers built as modules. If any system extensions or the Talos base kernel build provides kernel modules and if they matches the system hardware (via PCI IDs), they will be loaded automatically. Modules can still be loaded explicitly by defining it in [machine configuration](https://www.talos.dev/v1.4/reference/configuration/#kernelconfig). ### Kernel Modules Tree Talos now supports re-building the kernel modules dependency tree information on upgrades. This allows modules of same name to co-exist as in-tree and external modules. System Extensions can provide modules installed into `extras` directory and when loading it'll take precendence over the in-tree module. ### Kernel Reset Argument Talos now supports `talos.experimental.wipe=system:EPHEMERAL,STATE` kernel argument. Talos now also supports the new GRUB boot option - "Reset Talos installation and return to maintenance mode". Both of this options will reset EPHEMERAL and STATE partitions and will return Talos into maintenance mode after the reboot. ### Machine Configuration Strategic merge config patches correctly support merging `.vlans` sections of the network interface. ### talosctl netstat Talos API was extended to support retrieving a list of network connections (sockets) from the node. `talosctl netstat` command was added to retrieve the list of network connections. ### Reset API Enhancements Talos now supports resetting user disks through the Reset API, the list of disks to wipe is set using the `--user-disks-to-wipe` parameter in `talosctl`. Additionally, the Reset API can now function in maintenance mode and has the capability to wipe the node's system disk (partial wipe is not supported). ### New Talos API os:operator role Talos now supports a new `os:operator` role for the Talos API. This role allows everything `os:reader` role allows plus access to maintenance APIs: rebooting, shutting down a node, accessing packet capture, etcd alarm APIs, etcd backup, etc. ### Component Updates * Linux: 6.1.20 * containerd: v1.6.19 * Kubernetes: v1.27.0-beta.0 * etcd: v3.5.7 * CoreDNS: v1.10.1 * Flannel: v0.21.4 Talos is built with Go 1.20.2. ### VMware Platform Talos now supports loading network configuration on VMWare platform from the `metadata` key. See [CAPV IPAM Support](https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/blob/main/docs/proposal/20220929-ipam-support.md) and [Talos issue 6708](https://github.com/siderolabs/talos/issues/6708) for details. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Spencer Smith * Serge Logvinov * Artem Chernyshev * Steve Francis * Tim Jones * Nico Berlee * Seán C McCord * Steffen Windoffer * Andrey Smirnov * Cees-Jan Kiewiet * Chris van de Sande * Dzerom Dzenkins * Erik Lund * Jori Huisman * Lance R. Vick * Matthias Riegler * Michael Vorburger * Murtaza Udaipurwala * Niklas Wik * Rowan Smith * Samuel Kees * Sander Maijers * Tim van Druenen * Victor Seva * budimanjojo * xyhhx ### Changes
178 commits

* [`a7b79ef1b`](https://github.com/siderolabs/talos/commit/a7b79ef1be79ca7e0ea1530d469c3790f43e6c6b) feat: add network config screen to dashboard * [`cf2ccc521`](https://github.com/siderolabs/talos/commit/cf2ccc521f6a15b8b82bf5fbaab572f481f8edf7) fix: always shutdown maintenance API service * [`a0a5db590`](https://github.com/siderolabs/talos/commit/a0a5db590d9b5f312f9e59bec4ddc7379183c705) feat: update Flannel to 0.21.4 * [`d1a61fd34`](https://github.com/siderolabs/talos/commit/d1a61fd34343e58192864b1464759b78eb57e917) chore: bump golangci-lint * [`36a9a208e`](https://github.com/siderolabs/talos/commit/36a9a208ecf01114f5cc47449bb69099fca99e83) chore: bump deps * [`c63cf90e3`](https://github.com/siderolabs/talos/commit/c63cf90e32ce61e788a00ed79a5ff662d3d25e50) feat: update k8s to v1.27.0-beta.0 * [`b246c90ab`](https://github.com/siderolabs/talos/commit/b246c90abdec14c305dbad8af82147ebe44328ce) fix: add uint32 to Magic1 and Magic2 * [`777c8d6f6`](https://github.com/siderolabs/talos/commit/777c8d6f6ecb438d11ac829a297bf2c6b5660479) chore: update COSI to watch aggregated version * [`bec89bf6e`](https://github.com/siderolabs/talos/commit/bec89bf6e575923f348a4885841de27eead020df) fix: use 'no block' etcd dial with multiple endpoints * [`28713c2c4`](https://github.com/siderolabs/talos/commit/28713c2c4d4d20d5ff455c40cbb8aa004d725801) feat: update Kubernetes to 1.26.3 * [`a3cf41647`](https://github.com/siderolabs/talos/commit/a3cf4164755609e80de5dafa2c49bfaa0fc655fd) docs: add InstallConfig ignored notice to doc * [`df9b851fb`](https://github.com/siderolabs/talos/commit/df9b851fbadaa7c652f343c2facc2bd0a9dd22ca) chore: load all external artifacts earlier * [`2dd0964c5`](https://github.com/siderolabs/talos/commit/2dd0964c5f617a7072af08bd45c35d57bc47e838) refactor: use resource watches on dashboard * [`9933ebb6a`](https://github.com/siderolabs/talos/commit/9933ebb6aa86249d2118ef5fb50bf23cedadb0a5) chore: fix loaded artifacts file permission * [`a14a0aba0`](https://github.com/siderolabs/talos/commit/a14a0aba04a2daf277bf2703575def39b7f2e5e9) fix: nil pointer exception in syncLink * [`cf101e56f`](https://github.com/siderolabs/talos/commit/cf101e56fbf18bb401bebb95e9fe005f65765d3d) fix: add `--force` flag for `talosctl gen` * [`ea2aa0611`](https://github.com/siderolabs/talos/commit/ea2aa06116a1b3c58d40ad42787749783516ef6c) fix: fix data race on network config read * [`64e3d24c6`](https://github.com/siderolabs/talos/commit/64e3d24c6bfe60b5556c41822c8e81f63d0a06d2) feat: provide platform network config for 'metal' in META * [`442cb9c1b`](https://github.com/siderolabs/talos/commit/442cb9c1b0757a9c8204cc92baab11f664cbcb19) feat: implement APIs to write to META * [`9e07832db`](https://github.com/siderolabs/talos/commit/9e07832db9e19e602332821769f479b881fae178) feat: implement summary dashboard * [`1df841bb5`](https://github.com/siderolabs/talos/commit/1df841bb542323adce92013cd55eb24ab238a1dc) refactor: change the interface of META * [`e9962bc3e`](https://github.com/siderolabs/talos/commit/e9962bc3eaa31b9a782c2fcd0c7857a86cba0c28) chore: update CI to tag azure buckets * [`9f5f5cf9b`](https://github.com/siderolabs/talos/commit/9f5f5cf9bf83e9cff0be7720d6bffc13fec97570) feat: update Flannel to v0.21.3 * [`02b0ff35e`](https://github.com/siderolabs/talos/commit/02b0ff35ee2273e59899ac4a999fa101d895aec0) feat: generate Flannel CNI manifest from upstream * [`6656d35ec`](https://github.com/siderolabs/talos/commit/6656d35eca5ec78cd52e7a6478369200ce16b176) docs: fix Talos version to use template * [`72a6d1d70`](https://github.com/siderolabs/talos/commit/72a6d1d70813986f6e9f4b7fc92e594f6ff7da1f) docs: update nocloud * [`9948a646d`](https://github.com/siderolabs/talos/commit/9948a646d20f4ba80916a263ed7bca3e5ca2f0ad) feat: coredns node uninitialized toleration * [`e03902b54`](https://github.com/siderolabs/talos/commit/e03902b546b379c19ea80081bbfaef666d03812d) feat: update Go to 1.20.2 * [`c8f8579f2`](https://github.com/siderolabs/talos/commit/c8f8579f2dcf485e66922679d37e56742b65cc53) fix: upgrade-k8s to flag should not be required since there is a default * [`230cfaf80`](https://github.com/siderolabs/talos/commit/230cfaf80312518222469939e969880040c379f2) feat: use network information from guestinfo.metadata * [`97048f7c3`](https://github.com/siderolabs/talos/commit/97048f7c37ed7b7aceadf6f2e40f007a09c57730) feat: netstat in API and client * [`fda6da692`](https://github.com/siderolabs/talos/commit/fda6da692956d863d320f25cd50833da2f93104c) fix: successful ACPI shutdown in maintenance mode * [`b97e1abaa`](https://github.com/siderolabs/talos/commit/b97e1abaa6a1543bc7b6e8fa7e4fa9e0cb5d8e14) feat: set default image, validate empty image * [`121220a3b`](https://github.com/siderolabs/talos/commit/121220a3b3202de9bd08dce391740c9a66ad9cf2) chore: bump dependencies via renovate bot * [`ebc92f3c1`](https://github.com/siderolabs/talos/commit/ebc92f3c1de97a8b11046268854e957be0b64f81) chore: add container id to `talosctl -k containers` and `talosctl -k logs` * [`22ef81c1e`](https://github.com/siderolabs/talos/commit/22ef81c1e78963a8f46e2f54d00cd111742dd95c) feat: add grub option to drop to maintenance mode * [`642fe0c90`](https://github.com/siderolabs/talos/commit/642fe0c90c4714aeb5f880946c1d337c53bc6fa4) feat: update pkgs with framebuffer console * [`69cb414f0`](https://github.com/siderolabs/talos/commit/69cb414f01d3193931e838f89e21b0c9ac26bf61) docs: update cilium install instructions * [`e71cc6619`](https://github.com/siderolabs/talos/commit/e71cc6619b2cdc34efe8dccca3cc296befef43f9) fix: redo assertHostnames in HostnameMergeSuite.TestMerge * [`8ea4bfad8`](https://github.com/siderolabs/talos/commit/8ea4bfad8feae5f4806be0ea4f6fdd1b79a8197a) refactor: improve the kubernetes upgrade flow * [`81879fc0c`](https://github.com/siderolabs/talos/commit/81879fc0ca98cc3e5df619bd071c279a735697e3) docs: add how tos for workloads on control planes, and scaling up * [`05b0b721c`](https://github.com/siderolabs/talos/commit/05b0b721c9d2acd211519d554d1c23926472a5b4) chore: move blob storage to azure for builds * [`a78281214`](https://github.com/siderolabs/talos/commit/a78281214d349c147498e3b000a9e9aeecb29eb6) feat: add cilium e2e tests * [`061640ccc`](https://github.com/siderolabs/talos/commit/061640cccf69d065806140e670e484c8b1c5a26e) feat: add pod ip to kube-proxy spec * [`dea17d723`](https://github.com/siderolabs/talos/commit/dea17d72340b1deddea2215b556a4f193d1feb7f) feat: update Kubernetes to v1.26.2 * [`337aaba7a`](https://github.com/siderolabs/talos/commit/337aaba7a705536b885d7336343b828dd13e1de4) feat: add 'os:operator' role * [`40e69af22`](https://github.com/siderolabs/talos/commit/40e69af2242fcd91f4a351da02de1b94158d419c) fix: improve etcd leave on reset process * [`638dc9128`](https://github.com/siderolabs/talos/commit/638dc9128fd89f70ddab8d6f342ca5a2e5131be8) fix: fix "defer" leak in ResetUserDisks * [`bfba3677b`](https://github.com/siderolabs/talos/commit/bfba3677b0e85a27a8b92235f5763ac6fc8e0375) chore: handle grub option - "wipe" * [`594f27d87`](https://github.com/siderolabs/talos/commit/594f27d87870ef26fc7166a95a64a40d27cb165a) release(v1.4.0-alpha.2): prepare release * [`b52071081`](https://github.com/siderolabs/talos/commit/b5207108104eda426361c256ec4d78ae9e0b2890) feat: introduce new flag in reset API that makes Talos reset user disks * [`f55f5df73`](https://github.com/siderolabs/talos/commit/f55f5df7396b7073e75267c7e10a35814f1185c9) feat: move dashboard package & run it in tty2 * [`36e077ead`](https://github.com/siderolabs/talos/commit/36e077ead458f15e864f62eeb0d7afa59187c226) chore: bump deps * [`5a01d5fd4`](https://github.com/siderolabs/talos/commit/5a01d5fd473cdc4e0b9fba48047d6434cf31ee42) chore: run extension build as downstream * [`426fe9687`](https://github.com/siderolabs/talos/commit/426fe9687d74690df26ce3cfd6aee47c13e994a8) fix: extension base folder permission * [`609d3a8a6`](https://github.com/siderolabs/talos/commit/609d3a8a694ff90426ce33be86791f2616ec4852) feat: support strategic merge patches on VLAN configuration * [`7e19f32d7`](https://github.com/siderolabs/talos/commit/7e19f32d762dc1363f29e988ddbe334bd00610f2) chore: provide version compatibility data for Talos 1.2.x * [`230e46e56`](https://github.com/siderolabs/talos/commit/230e46e567012d8e12e384c777d6f57db5e7cfee) refactor: extract parts of kubernetes libraries * [`f3d3f0f26`](https://github.com/siderolabs/talos/commit/f3d3f0f2625f1be41a17366ee1c0bd2a3193c08c) fix: update go-smbios library with Hyper-V data fix * [`8711eea96`](https://github.com/siderolabs/talos/commit/8711eea9626a60a996347aaa7e6a89eea87d4b9e) fix: use passed `--context` in `talosctl config` cmd * [`5ac9f43e4`](https://github.com/siderolabs/talos/commit/5ac9f43e45f85f8d37c2855051b9a5cc9ad389ac) feat: start machined earlier & in maintenance mode * [`36ab414a1`](https://github.com/siderolabs/talos/commit/36ab414a1d7c5472522d20a7b698c4eebb3423b9) docs: fix the endpoints in the libvirt guide * [`3d55bd80f`](https://github.com/siderolabs/talos/commit/3d55bd80f42b7d2439541909c9534c386607e578) fix: add `--force` flag to `talosctl gen config` * [`660b8874d`](https://github.com/siderolabs/talos/commit/660b8874da7bd91946aab5f400e7d1dfddefb827) feat: cmdline integer netmask * [`1e3daacc4`](https://github.com/siderolabs/talos/commit/1e3daacc48c0b8ef2eab41b2c2c53f55522e1acf) docs: update nvidia component versions * [`b5c03a7fa`](https://github.com/siderolabs/talos/commit/b5c03a7fab8d213e7048a8f5fc129125b81eb205) fix: docker talosctl cluster create provisioner * [`6e8f13529`](https://github.com/siderolabs/talos/commit/6e8f13529c17ff4c658b340d16d9ee429cfd9a4c) fix: add support for a fallback '*' mirror configuration * [`dcd4eb1a9`](https://github.com/siderolabs/talos/commit/dcd4eb1a93737d60f60693d8c33a20052eee4a4f) fix: improve error message on single node upgrade * [`ed5af3f78`](https://github.com/siderolabs/talos/commit/ed5af3f780732fb0004ddb263feedbf2de9fd09a) chore: bump deps * [`0dc6858e5`](https://github.com/siderolabs/talos/commit/0dc6858e5ba4b110eac9ca74294eb3a29790a323) chore: bump cosi-project/runtime * [`da2edb9de`](https://github.com/siderolabs/talos/commit/da2edb9de067fc21c792e948903bc2c880b2c2d1) chore: bump dependencies * [`e51a110f0`](https://github.com/siderolabs/talos/commit/e51a110f0e876fc091aee0828aca0135499def9c) chore: bump dependencies * [`2d0148018`](https://github.com/siderolabs/talos/commit/2d014801803fa0d5f08a344bdc9ff078b3931633) feat: automatically load modules based on hw info * [`7b75cd8b9`](https://github.com/siderolabs/talos/commit/7b75cd8b94367645adb2dd5be016e6f98d8e6a89) fix: kernel module dependency tree generation * [`65d02e5ad`](https://github.com/siderolabs/talos/commit/65d02e5ade08354aeec794d4131a1f8913fba2b5) fix: dbus shutdown when it's not initialized * [`a7079ce85`](https://github.com/siderolabs/talos/commit/a7079ce85c9839933544b637100f104f02fd3f3a) fix: quote the ampersand character in GRUB config * [`933ba2d82`](https://github.com/siderolabs/talos/commit/933ba2d8203e4418414b3de1c4240c1f88cb033e) fix: display correct blockdevice size * [`c449cb736`](https://github.com/siderolabs/talos/commit/c449cb736b24b268b965da5e2932f18bd4fb7785) fix: talosctl reboot command passing mode in wait mode * [`34ab0007a`](https://github.com/siderolabs/talos/commit/34ab0007a61bbb685d8c194c06568974db2a7375) docs: port is needed for wireguard endpoint * [`1e1aa84f6`](https://github.com/siderolabs/talos/commit/1e1aa84f6cdd0fbe6dd35841b6195cc56f10d333) fix: kubernetes removed resource version check * [`dcbcf5a93`](https://github.com/siderolabs/talos/commit/dcbcf5a93c3d82f8fdd7b8ffef3819010bd1c481) fix: wait for network and retry in platform get config funcs * [`3d7566ec7`](https://github.com/siderolabs/talos/commit/3d7566ec743f573a43a4a49ecb80f6ba59cbb27b) test: update Canal CNI manifest URL * [`e09e10666`](https://github.com/siderolabs/talos/commit/e09e106665aa8716f14ba49d527d8cb182592da7) fix: default dns domain to 'cluster.local' in local case * [`cc6e37a47`](https://github.com/siderolabs/talos/commit/cc6e37a47fd2ca9f1e43ce8ba2c1e8d8bfe44776) feat: use process wrapper for dropping capabilities * [`0c6c88874`](https://github.com/siderolabs/talos/commit/0c6c888745c5482fcf3891c922cc7cc7f72e6af4) fix: trackable action flag usage text. --no-wait does not exist * [`5cb2915d8`](https://github.com/siderolabs/talos/commit/5cb2915d8ea6e4ba913396abe3f45235e6a67213) feat: use wrapper for starting processes * [`56d945326`](https://github.com/siderolabs/talos/commit/56d9453261d47c0739be21cb7a5fe6beb25cb92c) fix: panic in talosctl cluster show * [`38a51191e`](https://github.com/siderolabs/talos/commit/38a51191e49059e93f4adfea479c039819a7f730) fix: correctly expand parameters in the URL * [`af21860a2`](https://github.com/siderolabs/talos/commit/af21860a22598361f68cf49e62a12da54bc95337) fix: return proper error if download attempts time out * [`54f7d4c92`](https://github.com/siderolabs/talos/commit/54f7d4c9231e858216f3b69b2662d7cc188df4f9) fix: correctly quote and unquote strings in GRUB config * [`54cf0672a`](https://github.com/siderolabs/talos/commit/54cf0672a71a8c9427c66bb2601521a9d24f8e13) fix: omit zero MTU in the machine config * [`bdc53ac25`](https://github.com/siderolabs/talos/commit/bdc53ac254a4aaa37ffd917c7c3ad506368205de) docs: add hyperlink to Docker API docs about `config.json` * [`b3bc06dd1`](https://github.com/siderolabs/talos/commit/b3bc06dd14c7faa75269cb6686b2d93ce765595c) chore: bump vtprotobuf to v0.4.0 * [`0ba5e59f6`](https://github.com/siderolabs/talos/commit/0ba5e59f69c08ab566177df9e26a21648bcde54f) fix: drone config for renovate PR's * [`590a393de`](https://github.com/siderolabs/talos/commit/590a393de968556bb5e19594b2f057d4233c378d) fix: udevd healthcheck * [`2b6b6deac`](https://github.com/siderolabs/talos/commit/2b6b6deacda4a3cdf6c5b65ac586cad1363be094) docs: simplify and clarify digital ocean docs * [`92bc15f7f`](https://github.com/siderolabs/talos/commit/92bc15f7f1c561b1e7810371df23f84c7e0d6a1c) release(v1.4.0-alpha.1): prepare release * [`e3da4754e`](https://github.com/siderolabs/talos/commit/e3da4754e7a2e69b998b861034c6f77e2cf6355b) feat: update Linux to 6.1.7 * [`006449e46`](https://github.com/siderolabs/talos/commit/006449e464ac009e15d78bb4d71cee80f2540f31) test: build integration test early in the pipeline * [`09aa71264`](https://github.com/siderolabs/talos/commit/09aa7126422b9b41e74c3d2aacb563daeca33bc5) fix: renovate config * [`2d136f187`](https://github.com/siderolabs/talos/commit/2d136f1879ee66dbd61ab40bb001a45c0bafaad5) feat: set markdown and html descriptions in config json schema * [`f0804027a`](https://github.com/siderolabs/talos/commit/f0804027a499a6e195f049144bff4f939dee3780) fix: renovate config * [`812a2877c`](https://github.com/siderolabs/talos/commit/812a2877cdc1e631ae0244f9696a65e2347594c0) chore: bump deps + renovate cleanup * [`aa9f66c1c`](https://github.com/siderolabs/talos/commit/aa9f66c1c88a1bb35aefe24ea0a5c3a6e7aa966d) fix: mark DigitalOcean anchor IP as scope link * [`bb4937f1b`](https://github.com/siderolabs/talos/commit/bb4937f1b339384fb486cb0cb675df8bf9b9f916) feat: enable renovate * [`3e0057162`](https://github.com/siderolabs/talos/commit/3e00571627568d8c5ab10a72e59207677a89e4cc) fix: unwrap gRPC errors on stop/remove pods check * [`00e52ae07`](https://github.com/siderolabs/talos/commit/00e52ae07867deff9a5877fcb498252bc1b1a740) fix: build correctly etcd initial cluster URL * [`ae83b10ae`](https://github.com/siderolabs/talos/commit/ae83b10ae89dbe600ddfaa338be95ea819546007) feat: create JSON schema for v1alpha1.Config * [`703d96595`](https://github.com/siderolabs/talos/commit/703d9659512d744a606e520faf230e20efddfc4a) feat: update Kubernetes to 1.26.1, etcd to 3.5.7 * [`965e64591`](https://github.com/siderolabs/talos/commit/965e645915d080487a74b35dc8f1d2e4051f0504) docs: update to use talosctl install script * [`c5954f434`](https://github.com/siderolabs/talos/commit/c5954f4345cbf3a92c777a0e7fc5d39e883609bf) chore: bump deps * [`bb50f6a56`](https://github.com/siderolabs/talos/commit/bb50f6a56d971915abb6a895aac9d7e0612a3255) chore: preallocate disk images for QEMU VMs * [`d4b8b35de`](https://github.com/siderolabs/talos/commit/d4b8b35de7849d887c41f9a13dadb59ccd8c08c4) feat: generate kernel module dependency tree * [`18122ae73`](https://github.com/siderolabs/talos/commit/18122ae73e0489a0497956c6d4621c05c6a77387) fix: service restart (including extension services) * [`680fd5e45`](https://github.com/siderolabs/talos/commit/680fd5e452e02b108b7938d0136079c16e6cfd79) fix: bump COSI runtime with the panic controller restart fix * [`0b65bbfc8`](https://github.com/siderolabs/talos/commit/0b65bbfc878fe2a5c01c5d2cd08006b53fda7cf9) fix: handle overwriting tags in syslinux ADV * [`70d9428a1`](https://github.com/siderolabs/talos/commit/70d9428a1d00d9894d68f38b255debb66fe8a440) fix: kubespan MSS clamping * [`683b4ccb4`](https://github.com/siderolabs/talos/commit/683b4ccb4faab6c3da2de00f7314773f42899c25) chore: update Go to 1.19.5 and kernel to 6.1.4 * [`062c7d754`](https://github.com/siderolabs/talos/commit/062c7d754be1714c7763b8f2b399436d64c90ea4) test: fix integration test on cp endpoint update * [`8e9fc13d7`](https://github.com/siderolabs/talos/commit/8e9fc13d7c48da5c5354501e0ad96688670438cf) feat: implement enum generator for proto files * [`771b0dc06`](https://github.com/siderolabs/talos/commit/771b0dc061e0fa33085b28bd0d0a7e4da13081f1) docs: update left over rpi_4 ref to rpi_generic * [`6c04b5f79`](https://github.com/siderolabs/talos/commit/6c04b5f79e6e01e0a3cdabfc99f12c944edd1f0a) chore: bump dependencies * [`0a5a8802e`](https://github.com/siderolabs/talos/commit/0a5a8802e7e337e1f30a40c9f566e57642c39c1a) feat: use 'localhost' endpoint for controlplane nodes * [`b0775ebf2`](https://github.com/siderolabs/talos/commit/b0775ebf2c776c7133cf74c6259de9dc9573786c) feat: add ISO wipe GRUB boot option * [`29020cb9c`](https://github.com/siderolabs/talos/commit/29020cb9c788d87a0457028ce73c8d297959116e) fix: report fatal sequence errors as reboots * [`96629d5ba`](https://github.com/siderolabs/talos/commit/96629d5ba6c1ae9d820824fb38f68112bce27f2c) feat: implement etcd maintenance commands * [`80fed3194`](https://github.com/siderolabs/talos/commit/80fed319408be9e493141fb2c01e5731708835c7) feat: include Kubernetes controlplane endpoint as one of the endpoints * [`c6cb36cc1`](https://github.com/siderolabs/talos/commit/c6cb36cc1f50b5d0e59a5284867e7534dc9f73bb) docs: fix auditpolicy example typo * [`ba8265bc5`](https://github.com/siderolabs/talos/commit/ba8265bc5ce63bcbc6fbd6c1a1076dc3f2ee6bd0) feat: new talosctl config remove to remove context * [`fcb19ff51`](https://github.com/siderolabs/talos/commit/fcb19ff516cc1200ec81f2a954bb6d2ce39ebdc6) fix: implement upgrade version checks for Talos 1.4 * [`80f150ac8`](https://github.com/siderolabs/talos/commit/80f150ac859f5dbf95060c12440afab8c0bc77a8) feat: enable ipv6 on gcp * [`8db622f3d`](https://github.com/siderolabs/talos/commit/8db622f3dc75aed90dd2d0bd92d03aa7e8aefd10) docs: add Vandebron to adopters list * [`f6a86ae90`](https://github.com/siderolabs/talos/commit/f6a86ae90607914c29875df750fe79cbbfcc5897) fix: oralce cloud zone * [`89dbb0ecf`](https://github.com/siderolabs/talos/commit/89dbb0ecf089bb746479238df274ccba4fcb049a) release(v1.4.0-alpha.0): prepare release * [`31fb90535`](https://github.com/siderolabs/talos/commit/31fb9053582190b3b536a309c30e2b78c4611885) feat: update Linux 6.1.1, containerd 1.6.14 * [`a0c0352dd`](https://github.com/siderolabs/talos/commit/a0c0352ddca253e1efb3679224b317692d46b2fd) fix: send diagnostic output to stderr consistently * [`9a5f4c08a`](https://github.com/siderolabs/talos/commit/9a5f4c08a206504a1d30277dcc0597333e5a927a) fix: default the manifest namespace if not set * [`3c6cce5fe`](https://github.com/siderolabs/talos/commit/3c6cce5fe47075f43a73682b57a7b40fa0899795) docs: update last release for Talos 1.2.x * [`703624c43`](https://github.com/siderolabs/talos/commit/703624c43dd8e58c147ccbc3989c6c436c9f3a7f) docs: fix the 1.3 release date * [`386c9293a`](https://github.com/siderolabs/talos/commit/386c9293a33e9d237fbeda0492b01b11fdadc501) docs: update nvidia-container-runtime version * [`ff83d9fd7`](https://github.com/siderolabs/talos/commit/ff83d9fd7bed2e04d5c8107713150c2513f47991) fix: improve talosctl completion * [`31ff431fa`](https://github.com/siderolabs/talos/commit/31ff431faec22c09cad88d565102e6a24785ecb4) chore: add schulz systemtechnik to the list * [`97bef7c47`](https://github.com/siderolabs/talos/commit/97bef7c47bfd133f2b3ad19efe3f30a88dd67460) docs: vsphere.sh > vmware.sh * [`34babe858`](https://github.com/siderolabs/talos/commit/34babe858d15145a1c596febb5e577473e4ffce0) chore: make organization selection an interface * [`a9643b477`](https://github.com/siderolabs/talos/commit/a9643b477417029db73aacbfcf5778cedd97cd95) fix: use proper key usage for apid client certificate * [`171aa9467`](https://github.com/siderolabs/talos/commit/171aa9467966f5869e72374961ea05abc8d9fda9) fix: disable Wireless Lan using dtoverlay * [`2e84d2ab3`](https://github.com/siderolabs/talos/commit/2e84d2ab3417515f539a70d58885dcb69e9f098c) chore: update conformance product.yaml * [`b7763843a`](https://github.com/siderolabs/talos/commit/b7763843af63bbc186f08701a62c19ea96fb7e3c) feat: add install script that improves talosctl installation user experience This install script detects the platform and architecture, and downloads the correct talosctl, and checks the gpg checksums. It also installs and chmods the binary. * [`afc45ad63`](https://github.com/siderolabs/talos/commit/afc45ad632e63cc3afc095b1f3efe6df3ecb9cb1) docs: mark Talos 1.3 docs as default * [`873bd3807`](https://github.com/siderolabs/talos/commit/873bd3807c0fcca2e212deb7fd044662557964c1) fix: redact service account key in config in RedactSecrets method * [`b3aebfadf`](https://github.com/siderolabs/talos/commit/b3aebfadfc15544e5ab448d979129dba5e516c59) feat: validate Talos API access roles in machine config * [`40761e17d`](https://github.com/siderolabs/talos/commit/40761e17db5789f30eef2f15f0b5c6396e09a9e5) docs: fork docs for Talos 1.4 * [`474604cd2`](https://github.com/siderolabs/talos/commit/474604cd279def7a6798e24ede27feef955ba5a3) docs: update documentation for Talos 1.3 * [`faf49218c`](https://github.com/siderolabs/talos/commit/faf49218ce14a48829dae7b3b8d7801188453a89) feat: add more checks for K8s upgrade * [`5b992bd86`](https://github.com/siderolabs/talos/commit/5b992bd8610f41d23d8b7dbd01f9a1be298eda96) fix: allow empty dnsDomain in machine config * [`eb332cfcb`](https://github.com/siderolabs/talos/commit/eb332cfcb785e250c422d6a7ea2b23679189a946) feat: add health check for a minimal memory / disk size * [`d04970dfa`](https://github.com/siderolabs/talos/commit/d04970dfa9d6554e1ee447fd9383bf65b8953671) fix: ignore k8s additional addresses if nil * [`63c17104c`](https://github.com/siderolabs/talos/commit/63c17104c594dfd9ca4066ba41d8a03507464874) feat: update Kubernets to 1.26.0 * [`f7a9a90db`](https://github.com/siderolabs/talos/commit/f7a9a90db2bfd316ea01551daba9becb15361f94) chore: update pkgs/tools (Go 1.19.4, containerd 1.6.11) * [`cf7adc51c`](https://github.com/siderolabs/talos/commit/cf7adc51c9f53234e469dd9f0cca06eed0230e8b) feat: add RedactSecrets method to v1alpha1.Config * [`4c31b9b1a`](https://github.com/siderolabs/talos/commit/4c31b9b1a3a00df0fe817c3edc15260ca3cadd6d) docs: clarify what the deal is with /var * [`a8ebcca4a`](https://github.com/siderolabs/talos/commit/a8ebcca4a9f63643f68d8e85bcb0b9ddb49205ed) chore: remove `watchErr` from `metal.getResource` * [`1253513bd`](https://github.com/siderolabs/talos/commit/1253513bd1deecc4cc42330bad0a713b3630240a) fix: fix nil pointer panic and incorrect error output * [`82e8c9e1f`](https://github.com/siderolabs/talos/commit/82e8c9e1f63371f41b0794b4c1be3209847c5f8b) fix: workaround panic in the kubelet service controller * [`a505b8909`](https://github.com/siderolabs/talos/commit/a505b8909a1c733b30f22a8d46eebc022475431a) fix: update COSI and reset restart backoff on success * [`e92fdcbad`](https://github.com/siderolabs/talos/commit/e92fdcbad1de595d119f78dbed3a97ae46df9bbf) chore: bump kernel to 5.15.81 * [`f0dddca2a`](https://github.com/siderolabs/talos/commit/f0dddca2a3d2e976cee543ab57816a6395fe3d65) docs: expand help for 'talosctl get' * [`fcffc8879`](https://github.com/siderolabs/talos/commit/fcffc88790b5a3006b3b85744771a7eef6e8ac5c) fix: add ext4 filesystem detection * [`5b2960eff`](https://github.com/siderolabs/talos/commit/5b2960efff8b38af85b687a25fa93f01256016de) fix: introduce 'overridePath' setting and fix Talos resolver * [`0219d1124`](https://github.com/siderolabs/talos/commit/0219d1124e5125696364bf92ecf0e8dcad644001) fix: use only kube-apiserver endpoints for Talos API access endpoints * [`dc5e0f4af`](https://github.com/siderolabs/talos/commit/dc5e0f4af087d3b662b0240b4f8fd76379ed0de2) fix: report errors to Equinix Metal event API * [`7ab140a94`](https://github.com/siderolabs/talos/commit/7ab140a94ad1a279be43669d6d70687f3a0c47de) feat: add talosctl machineconfig patch command * [`d3cf06114`](https://github.com/siderolabs/talos/commit/d3cf061149a4a502317d7728c45b6cfb4d38f89f) fix: ignore many more filesystems in IMA * [`44e2799b8`](https://github.com/siderolabs/talos/commit/44e2799b8cb928083f3a777d5cce45ad8dbf6864) feat: add stdout and single config type support to talosctl gen config * [`4452f0e17`](https://github.com/siderolabs/talos/commit/4452f0e179db16c59dc65ccdb5a496ad3306684e) docs: bump talos version * [`38e57bd12`](https://github.com/siderolabs/talos/commit/38e57bd12b8c50d668fcde6ee9aa493682778dcc) feat: update Kubernetes to v1.26.0-rc.1 * [`4cd125d49`](https://github.com/siderolabs/talos/commit/4cd125d499a24798dfde1dddf6fa1c689d16c93f) fix: correctly handle new watch event types * [`881b84152`](https://github.com/siderolabs/talos/commit/881b84152084d157fbd4ff992089a5392aadfd3c) feat: update Flannel to 0.20.2

### Changes since v1.4.0-alpha.2
50 commits

* [`a7b79ef1b`](https://github.com/siderolabs/talos/commit/a7b79ef1be79ca7e0ea1530d469c3790f43e6c6b) feat: add network config screen to dashboard * [`cf2ccc521`](https://github.com/siderolabs/talos/commit/cf2ccc521f6a15b8b82bf5fbaab572f481f8edf7) fix: always shutdown maintenance API service * [`a0a5db590`](https://github.com/siderolabs/talos/commit/a0a5db590d9b5f312f9e59bec4ddc7379183c705) feat: update Flannel to 0.21.4 * [`d1a61fd34`](https://github.com/siderolabs/talos/commit/d1a61fd34343e58192864b1464759b78eb57e917) chore: bump golangci-lint * [`36a9a208e`](https://github.com/siderolabs/talos/commit/36a9a208ecf01114f5cc47449bb69099fca99e83) chore: bump deps * [`c63cf90e3`](https://github.com/siderolabs/talos/commit/c63cf90e32ce61e788a00ed79a5ff662d3d25e50) feat: update k8s to v1.27.0-beta.0 * [`b246c90ab`](https://github.com/siderolabs/talos/commit/b246c90abdec14c305dbad8af82147ebe44328ce) fix: add uint32 to Magic1 and Magic2 * [`777c8d6f6`](https://github.com/siderolabs/talos/commit/777c8d6f6ecb438d11ac829a297bf2c6b5660479) chore: update COSI to watch aggregated version * [`bec89bf6e`](https://github.com/siderolabs/talos/commit/bec89bf6e575923f348a4885841de27eead020df) fix: use 'no block' etcd dial with multiple endpoints * [`28713c2c4`](https://github.com/siderolabs/talos/commit/28713c2c4d4d20d5ff455c40cbb8aa004d725801) feat: update Kubernetes to 1.26.3 * [`a3cf41647`](https://github.com/siderolabs/talos/commit/a3cf4164755609e80de5dafa2c49bfaa0fc655fd) docs: add InstallConfig ignored notice to doc * [`df9b851fb`](https://github.com/siderolabs/talos/commit/df9b851fbadaa7c652f343c2facc2bd0a9dd22ca) chore: load all external artifacts earlier * [`2dd0964c5`](https://github.com/siderolabs/talos/commit/2dd0964c5f617a7072af08bd45c35d57bc47e838) refactor: use resource watches on dashboard * [`9933ebb6a`](https://github.com/siderolabs/talos/commit/9933ebb6aa86249d2118ef5fb50bf23cedadb0a5) chore: fix loaded artifacts file permission * [`a14a0aba0`](https://github.com/siderolabs/talos/commit/a14a0aba04a2daf277bf2703575def39b7f2e5e9) fix: nil pointer exception in syncLink * [`cf101e56f`](https://github.com/siderolabs/talos/commit/cf101e56fbf18bb401bebb95e9fe005f65765d3d) fix: add `--force` flag for `talosctl gen` * [`ea2aa0611`](https://github.com/siderolabs/talos/commit/ea2aa06116a1b3c58d40ad42787749783516ef6c) fix: fix data race on network config read * [`64e3d24c6`](https://github.com/siderolabs/talos/commit/64e3d24c6bfe60b5556c41822c8e81f63d0a06d2) feat: provide platform network config for 'metal' in META * [`442cb9c1b`](https://github.com/siderolabs/talos/commit/442cb9c1b0757a9c8204cc92baab11f664cbcb19) feat: implement APIs to write to META * [`9e07832db`](https://github.com/siderolabs/talos/commit/9e07832db9e19e602332821769f479b881fae178) feat: implement summary dashboard * [`1df841bb5`](https://github.com/siderolabs/talos/commit/1df841bb542323adce92013cd55eb24ab238a1dc) refactor: change the interface of META * [`e9962bc3e`](https://github.com/siderolabs/talos/commit/e9962bc3eaa31b9a782c2fcd0c7857a86cba0c28) chore: update CI to tag azure buckets * [`9f5f5cf9b`](https://github.com/siderolabs/talos/commit/9f5f5cf9bf83e9cff0be7720d6bffc13fec97570) feat: update Flannel to v0.21.3 * [`02b0ff35e`](https://github.com/siderolabs/talos/commit/02b0ff35ee2273e59899ac4a999fa101d895aec0) feat: generate Flannel CNI manifest from upstream * [`6656d35ec`](https://github.com/siderolabs/talos/commit/6656d35eca5ec78cd52e7a6478369200ce16b176) docs: fix Talos version to use template * [`72a6d1d70`](https://github.com/siderolabs/talos/commit/72a6d1d70813986f6e9f4b7fc92e594f6ff7da1f) docs: update nocloud * [`9948a646d`](https://github.com/siderolabs/talos/commit/9948a646d20f4ba80916a263ed7bca3e5ca2f0ad) feat: coredns node uninitialized toleration * [`e03902b54`](https://github.com/siderolabs/talos/commit/e03902b546b379c19ea80081bbfaef666d03812d) feat: update Go to 1.20.2 * [`c8f8579f2`](https://github.com/siderolabs/talos/commit/c8f8579f2dcf485e66922679d37e56742b65cc53) fix: upgrade-k8s to flag should not be required since there is a default * [`230cfaf80`](https://github.com/siderolabs/talos/commit/230cfaf80312518222469939e969880040c379f2) feat: use network information from guestinfo.metadata * [`97048f7c3`](https://github.com/siderolabs/talos/commit/97048f7c37ed7b7aceadf6f2e40f007a09c57730) feat: netstat in API and client * [`fda6da692`](https://github.com/siderolabs/talos/commit/fda6da692956d863d320f25cd50833da2f93104c) fix: successful ACPI shutdown in maintenance mode * [`b97e1abaa`](https://github.com/siderolabs/talos/commit/b97e1abaa6a1543bc7b6e8fa7e4fa9e0cb5d8e14) feat: set default image, validate empty image * [`121220a3b`](https://github.com/siderolabs/talos/commit/121220a3b3202de9bd08dce391740c9a66ad9cf2) chore: bump dependencies via renovate bot * [`ebc92f3c1`](https://github.com/siderolabs/talos/commit/ebc92f3c1de97a8b11046268854e957be0b64f81) chore: add container id to `talosctl -k containers` and `talosctl -k logs` * [`22ef81c1e`](https://github.com/siderolabs/talos/commit/22ef81c1e78963a8f46e2f54d00cd111742dd95c) feat: add grub option to drop to maintenance mode * [`642fe0c90`](https://github.com/siderolabs/talos/commit/642fe0c90c4714aeb5f880946c1d337c53bc6fa4) feat: update pkgs with framebuffer console * [`69cb414f0`](https://github.com/siderolabs/talos/commit/69cb414f01d3193931e838f89e21b0c9ac26bf61) docs: update cilium install instructions * [`e71cc6619`](https://github.com/siderolabs/talos/commit/e71cc6619b2cdc34efe8dccca3cc296befef43f9) fix: redo assertHostnames in HostnameMergeSuite.TestMerge * [`8ea4bfad8`](https://github.com/siderolabs/talos/commit/8ea4bfad8feae5f4806be0ea4f6fdd1b79a8197a) refactor: improve the kubernetes upgrade flow * [`81879fc0c`](https://github.com/siderolabs/talos/commit/81879fc0ca98cc3e5df619bd071c279a735697e3) docs: add how tos for workloads on control planes, and scaling up * [`05b0b721c`](https://github.com/siderolabs/talos/commit/05b0b721c9d2acd211519d554d1c23926472a5b4) chore: move blob storage to azure for builds * [`a78281214`](https://github.com/siderolabs/talos/commit/a78281214d349c147498e3b000a9e9aeecb29eb6) feat: add cilium e2e tests * [`061640ccc`](https://github.com/siderolabs/talos/commit/061640cccf69d065806140e670e484c8b1c5a26e) feat: add pod ip to kube-proxy spec * [`dea17d723`](https://github.com/siderolabs/talos/commit/dea17d72340b1deddea2215b556a4f193d1feb7f) feat: update Kubernetes to v1.26.2 * [`337aaba7a`](https://github.com/siderolabs/talos/commit/337aaba7a705536b885d7336343b828dd13e1de4) feat: add 'os:operator' role * [`40e69af22`](https://github.com/siderolabs/talos/commit/40e69af2242fcd91f4a351da02de1b94158d419c) fix: improve etcd leave on reset process * [`638dc9128`](https://github.com/siderolabs/talos/commit/638dc9128fd89f70ddab8d6f342ca5a2e5131be8) fix: fix "defer" leak in ResetUserDisks * [`bfba3677b`](https://github.com/siderolabs/talos/commit/bfba3677b0e85a27a8b92235f5763ac6fc8e0375) chore: handle grub option - "wipe" * [`594f27d87`](https://github.com/siderolabs/talos/commit/594f27d87870ef26fc7166a95a64a40d27cb165a) release(v1.4.0-alpha.2): prepare release

### Changes from siderolabs/discovery-api
1 commit

* [`ac75538`](https://github.com/siderolabs/discovery-api/commit/ac75538ee3a9f7b71b6619f509d95ff5057f6754) chore: regen the proto definitions with vtprotobuf v0.4.0

### Changes from siderolabs/discovery-client
1 commit

* [`269a832`](https://github.com/siderolabs/discovery-client/commit/269a832ce9e35d4edeeddba2a23cf5682a2ca425) chore: rekres, update discovery api

### Changes from siderolabs/extras
8 commits

* [`343956e`](https://github.com/siderolabs/extras/commit/343956eb882eed775c68ef5af3bd37407aa914f4) feat: update Go to 1.20.2 * [`6209d87`](https://github.com/siderolabs/extras/commit/6209d8774d2ace990f532ab88cf2fa6464c8bafa) chore: bump tc-redirect-tap * [`8b28b6b`](https://github.com/siderolabs/extras/commit/8b28b6b5a0153c65af596086016faea9d64e95c2) chore: bump deps * [`5ab4f59`](https://github.com/siderolabs/extras/commit/5ab4f5939c830c7043e3939e519305eb810cdfc2) chore: disable renovate builds * [`ddeddbd`](https://github.com/siderolabs/extras/commit/ddeddbd1976813de6b1563f662ca4f2b3f5e0f53) chore: update packages, tc_redirect_tap * [`8cb4792`](https://github.com/siderolabs/extras/commit/8cb4792da9b9e2b2663daca747d24c3b5c973e0f) chore: update Go to 1.19.5 * [`3ca2df3`](https://github.com/siderolabs/extras/commit/3ca2df3ead2a64a5ad30c350b87bfe02bf1f49c7) chore: disable provenance in buildx * [`55d8452`](https://github.com/siderolabs/extras/commit/55d845241c8456909ab36f9b0f4e26cc2b49c256) feat: update releases

### Changes from siderolabs/gen
2 commits

* [`214c1ef`](https://github.com/siderolabs/gen/commit/214c1efe795cf426e5ebcc48cb305bfc7a16fdb8) chore: set `slice.Filter` result slice cap to len * [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods

### Changes from siderolabs/go-blockdevice
1 commit

* [`8c7ea19`](https://github.com/siderolabs/go-blockdevice/commit/8c7ea1910b27e0660e3e1a6f98b9f7e24bc11ff0) fix: blockdevice size is reported by Linux in 512 blocks always

### Changes from siderolabs/go-kmsg
1 commit

* [`7a51094`](https://github.com/siderolabs/go-kmsg/commit/7a51094e29290697aaeed8f09ccb045634876801) fix: exit properly on context cancel

### Changes from siderolabs/go-kubernetes
4 commits

* [`81887dc`](https://github.com/siderolabs/go-kubernetes/commit/81887dcae8916ccee820af000efe73c151de29a4) feat: add kubelet flag checks * [`fe473c0`](https://github.com/siderolabs/go-kubernetes/commit/fe473c0595e8e2e861fc16d0cddb1ba2cedf1ab3) refactor: make sync easier to consume without CLI * [`570819b`](https://github.com/siderolabs/go-kubernetes/commit/570819b93ecc63218b3db8d90e4810765a069ee0) feat: initial version of the library * [`fb79215`](https://github.com/siderolabs/go-kubernetes/commit/fb7921556e96fc7c0a84ac23834350bcd37cfa38) Initial commit

### Changes from siderolabs/go-smbios
1 commit

* [`c526764`](https://github.com/siderolabs/go-smbios/commit/c5267640be317efd9cbbe936ab78b2a49c757edf) feat: fix reading "broken" Hyper-V DMI data

### Changes from siderolabs/pkgs
36 commits

* [`c26b0b5`](https://github.com/siderolabs/pkgs/commit/c26b0b504e5fd0f68432503aabd2653c07888706) chore: bump deps * [`7d8f5bd`](https://github.com/siderolabs/pkgs/commit/7d8f5bd7170464c4f017c8e747dd5eda40c35639) feat: enable Hyper-V dynamic memory driver * [`ea40205`](https://github.com/siderolabs/pkgs/commit/ea4020599aeb1cd0f78abcfd19c546026bfb0634) chore: bump deps * [`21e5a68`](https://github.com/siderolabs/pkgs/commit/21e5a6806288f535773cd8afc20b12ee3082caa8) feat: update Go 1.20.2, Linux 6.1.15 and other * [`1d7e60c`](https://github.com/siderolabs/pkgs/commit/1d7e60cc2da55b0a31bd225479c86f517c7a878f) feat: enable framebuffer drivers and console fonts * [`0e63e95`](https://github.com/siderolabs/pkgs/commit/0e63e955dd118b6d5e8a9dd443c72a5d35d639a6) chore: bump deps * [`5dbce6b`](https://github.com/siderolabs/pkgs/commit/5dbce6b19ff6a1e1b5ae88468e34925c3d30d627) fix: xz url * [`0097233`](https://github.com/siderolabs/pkgs/commit/00972336c3fcc22df8fc1d3774c35b26fdc957b9) chore: re-enable drbd * [`7493721`](https://github.com/siderolabs/pkgs/commit/749372110c6c8e226139cd662832b5a4169db894) fix: sourcefourge url shasums * [`185f482`](https://github.com/siderolabs/pkgs/commit/185f482db6a5c13a3b14feec02a4e361b53bec55) feat: update containerd to 1.6.18 * [`e3cab6c`](https://github.com/siderolabs/pkgs/commit/e3cab6cbd62b96143958ed5e0219d68107a5f583) chore: bump deps * [`18661b0`](https://github.com/siderolabs/pkgs/commit/18661b096559e673152ce0fed45ab74ef3305dff) chore: bump deps * [`885a68b`](https://github.com/siderolabs/pkgs/commit/885a68b6280f3bf4ff75508ccceef73158c53560) chore: bump deps * [`c3a6e18`](https://github.com/siderolabs/pkgs/commit/c3a6e185178d7571e891c7b2614bf6017ab5c913) chore: bump dependencies * [`1fae0b2`](https://github.com/siderolabs/pkgs/commit/1fae0b229a625d692d36e7d6c096f8476e0f56d7) feat: virtio drivers as modules * [`61d8ff4`](https://github.com/siderolabs/pkgs/commit/61d8ff4aaea93b86b82bc2a36a2bbd6d54da3bb8) chore: bump deps and disable un-needed kconfig * [`15fe6d8`](https://github.com/siderolabs/pkgs/commit/15fe6d8555b42e55f920a5576ad55504e356995b) fix: kernel module tree files missing * [`987d24a`](https://github.com/siderolabs/pkgs/commit/987d24aeaa4fb2278954cd96e6bc6a29a4c8dd61) feat: mellanox drivers are modules * [`b82a015`](https://github.com/siderolabs/pkgs/commit/b82a015c78c407d17d23542eba6a4114f3c2c4d7) feat: mellanox oped * [`057d4f9`](https://github.com/siderolabs/pkgs/commit/057d4f96aa3ba63cc456b06a70a6b3a008cf803f) chore: bump deps * [`4ac4138`](https://github.com/siderolabs/pkgs/commit/4ac4138c6b94622646c9f32f0885496c5475d905) feat: enable nvme support for raspberrypi cm4 * [`ccb9d39`](https://github.com/siderolabs/pkgs/commit/ccb9d39dc43cf53431a0d7609839ed9c7141972d) fix: disable magic sysrq * [`d33202d`](https://github.com/siderolabs/pkgs/commit/d33202d99daa6ccf136fca54ebbadda727a43a75) chore: bump u-boot to 2023.01 * [`cb83e16`](https://github.com/siderolabs/pkgs/commit/cb83e169df4a2020994a63e5be61524461ef93e3) chore: bump dependencies * [`e561dcb`](https://github.com/siderolabs/pkgs/commit/e561dcb45beae80161faccedb0303e58d41b1ded) feat: bump Go to 1.19.5 * [`c7797c7`](https://github.com/siderolabs/pkgs/commit/c7797c77bd311449e1f116980166d8d818102f4f) feat: update Linux to 6.1.4, restore RPi support * [`5e8ebb0`](https://github.com/siderolabs/pkgs/commit/5e8ebb073d9b58555a75912cd90490af8a435c7d) feat: add AMD K10 sensor support * [`73ac37d`](https://github.com/siderolabs/pkgs/commit/73ac37d683274e60340d2767f2b8201e7f13474c) chore: disable provenance in buildx * [`8965bee`](https://github.com/siderolabs/pkgs/commit/8965bee65313539e8b6534073d06341f4fb78586) chore: use default symlinks to `/bin` in `base` * [`325c9bf`](https://github.com/siderolabs/pkgs/commit/325c9bf0f3ed2bf7603d1eaea022ea650388cf2b) feat: bump dependencies * [`165dff6`](https://github.com/siderolabs/pkgs/commit/165dff6c3cdb2d05f170c8ae0616d9224416455e) fix: patch ipmitool IANA URL * [`c542f39`](https://github.com/siderolabs/pkgs/commit/c542f398a150567d5cdffc17b4248be5416fe242) feat: add kernel support for usb setrial console * [`f564f45`](https://github.com/siderolabs/pkgs/commit/f564f45645d102b7e3a9563ac7bdb1e816156e65) chore: bump tools, containerd * [`268ea7c`](https://github.com/siderolabs/pkgs/commit/268ea7c593ff04c4e4a9ea5676b3c58d41cbff14) chore: bump deps * [`dcf3ceb`](https://github.com/siderolabs/pkgs/commit/dcf3cebf283698e010aaac5417d91a7385dc2441) feat: add nitro enclave support in kernel * [`17ea5e6`](https://github.com/siderolabs/pkgs/commit/17ea5e680b2438c59fa1773e8b58d6b749cb0d34) chore: bump kernel to 5.15.81

### Changes from siderolabs/tools
29 commits

* [`2d710f9`](https://github.com/siderolabs/tools/commit/2d710f9074caefcbd1cd37190dda02372e851500) chore: bump deps * [`9bea7d0`](https://github.com/siderolabs/tools/commit/9bea7d04310bfb1177e55a9e4fe1606b81ad8dbd) chore: skip rc versions for util-linux * [`a94850e`](https://github.com/siderolabs/tools/commit/a94850e6dd52a2b2d08c3e4e1fe95adddcb68f20) chore: bump deps * [`e6b2956`](https://github.com/siderolabs/tools/commit/e6b29564537a54549165ea99fceff160d21634dd) fix: protoc install * [`601e347`](https://github.com/siderolabs/tools/commit/601e3475b6bb9249bcf4e2bee16791ea4f91e8f9) feat: go 1.20.2 + other bumps * [`ca67d0b`](https://github.com/siderolabs/tools/commit/ca67d0ba6ccb45f30da328fd210cbe92782c2151) chore: bump deps * [`662a906`](https://github.com/siderolabs/tools/commit/662a90650841ab6c8ffd74e4abc51654b713dd4e) feat: add libnl * [`a8440a9`](https://github.com/siderolabs/tools/commit/a8440a9c866d9837d358b53a869bcb43774f4e78) fix: partially revert e6c98fdf54425e6382f226e33bccca6f3875aad3a * [`e6c98fd`](https://github.com/siderolabs/tools/commit/e6c98fdf54425e6382f226e33bccca6f3875aad3) chore: remove swig * [`cd9687b`](https://github.com/siderolabs/tools/commit/cd9687b4323b20493b4d582cfaa48c321cd04288) fix: renovate config * [`977e3fc`](https://github.com/siderolabs/tools/commit/977e3fcba92d129eb78cb77300f38428f860b34d) chore: bump go to 1.20.1 * [`15748aa`](https://github.com/siderolabs/tools/commit/15748aa32d7c1d67b190ab7a27ace9922c8d6b56) chore: bump deps * [`d4b719a`](https://github.com/siderolabs/tools/commit/d4b719a1c2055eaa27f80422f93755b0de9ca3f8) chore: bump deps * [`8c36dbd`](https://github.com/siderolabs/tools/commit/8c36dbd05ee27ecc2a7340462a3b49efb7327184) chore: bump toolchain, bump protoc-gen-go-grpc * [`a62e365`](https://github.com/siderolabs/tools/commit/a62e365b223e7ca9d2728865b40b23115764a0ed) feat: update Go to 1.20 * [`28d4a57`](https://github.com/siderolabs/tools/commit/28d4a5721ce1c57fc3f643185386d5c4b5c7e39a) chore: reduce renovate noise * [`e130fd5`](https://github.com/siderolabs/tools/commit/e130fd5b9835d8cc178ec53d5a89dfc6cc2ce7a1) chore: bump deps * [`37612fe`](https://github.com/siderolabs/tools/commit/37612feb7222b943a84f1f98d0901a204d491926) fix: revert enabling provenance * [`e0b01e3`](https://github.com/siderolabs/tools/commit/e0b01e3b7420e8b0b1e0d9077515e007a6b83b56) chore: bump deps * [`d0e6bd0`](https://github.com/siderolabs/tools/commit/d0e6bd06fcfcadc330cf30339488536961f9f70e) feat: add gnutls * [`3d34b5d`](https://github.com/siderolabs/tools/commit/3d34b5d401a67048d365e8faf2f1edf293887a97) chore: bump dependencies * [`763c1d9`](https://github.com/siderolabs/tools/commit/763c1d927822517b3d63c624302e11e8e5a49f5b) feat: update Go to 1.19.5 * [`136958f`](https://github.com/siderolabs/tools/commit/136958f9f8c8cfc439228dec31b840549bca4374) chore: disable provenance in buildx * [`e2a8692`](https://github.com/siderolabs/tools/commit/e2a869294be7e77e295ca651400f85551fb7e665) feat: update releases * [`0e48f37`](https://github.com/siderolabs/tools/commit/0e48f37496a79ce4997d15fefb6300b2324f5668) chore: bump protobuf * [`a21aa1c`](https://github.com/siderolabs/tools/commit/a21aa1c583a10d017ace8da14c6f604f86ce5709) chore: bump toolchain and mpc versions * [`1a75d0f`](https://github.com/siderolabs/tools/commit/1a75d0f6796c4abf1c9a23cfe697d3e38a9ce587) chore: bump deps * [`55bd185`](https://github.com/siderolabs/tools/commit/55bd18532667e325e8938bf0a72cab40a936eadf) feat: update Go to 1.19.4 * [`f291f46`](https://github.com/siderolabs/tools/commit/f291f46e84ec02f5d22718f7ecb476a3f815ae45) chore: bump tools

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 -> v0.2.3 * **github.com/aws/aws-sdk-go** v1.44.147 -> v1.44.226 * **github.com/containerd/cgroups** v1.0.4 -> v1.1.0 * **github.com/containerd/containerd** v1.6.12 -> v1.6.19 * **github.com/containernetworking/plugins** v1.1.1 -> v1.2.0 * **github.com/coreos/go-semver** v0.3.0 -> v0.3.1 * **github.com/cosi-project/runtime** v0.2.0 -> v0.3.0-alpha.10 * **github.com/docker/docker** v20.10.21 -> v23.0.1 * **github.com/dustin/go-humanize** v1.0.0 -> v1.0.1 * **github.com/emicklei/dot** v1.2.0 -> v1.3.1 * **github.com/fatih/color** v1.13.0 -> v1.15.0 * **github.com/freddierice/go-losetup/v2** v2.0.1 **_new_** * **github.com/gdamore/tcell/v2** v2.5.3 -> v2.6.0 * **github.com/grpc-ecosystem/go-grpc-middleware** v1.3.0 -> v1.4.0 * **github.com/hashicorp/go-getter** v1.6.2 -> v1.7.1 * **github.com/hetznercloud/hcloud-go** v1.37.0 -> v1.41.0 * **github.com/insomniacslk/dhcp** f26e6d78f622 -> e252950ab961 * **github.com/jsimonetti/rtnetlink** v1.3.0 -> v1.3.1 * **github.com/mattn/go-isatty** v0.0.16 -> v0.0.17 * **github.com/mdlayher/ethtool** 0e16326d06d1 -> ba3b4bc2e02c * **github.com/mdlayher/genetlink** v1.3.0 -> v1.3.1 * **github.com/mdlayher/netlink** v1.7.0 -> v1.7.1 * **github.com/nberlee/go-netstat** 19cc338ee40a **_new_** * **github.com/prometheus/procfs** v0.8.0 -> v0.9.0 * **github.com/rivo/tview** db36428c92d9 -> 84f9c0ff9de8 * **github.com/safchain/ethtool** v0.2.0 -> v0.3.0 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.10 -> v1.0.0-beta.15 * **github.com/siderolabs/discovery-api** v0.1.1 -> v0.1.2 * **github.com/siderolabs/discovery-client** v0.1.3 -> v0.1.4 * **github.com/siderolabs/extras** v1.3.0-1-g3773d71 -> v1.4.0-alpha.0-7-g343956e * **github.com/siderolabs/gen** v0.4.1 -> v0.4.3 * **github.com/siderolabs/go-blockdevice** v0.4.2 -> v0.4.3 * **github.com/siderolabs/go-kmsg** v0.1.2 -> v0.1.3 * **github.com/siderolabs/go-kubernetes** v0.2.0 **_new_** * **github.com/siderolabs/go-smbios** v0.3.1 -> v0.3.2 * **github.com/siderolabs/pkgs** v1.3.0-5-g6509d23 -> v1.4.0-alpha.0-35-gc26b0b5 * **github.com/siderolabs/talos/pkg/machinery** v1.3.0 -> v1.4.0-alpha.2 * **github.com/siderolabs/tools** v1.3.0-1-g712379c -> v1.4.0-alpha.0-26-g2d710f9 * **github.com/stretchr/testify** v1.8.1 -> v1.8.2 * **github.com/u-root/u-root** v0.10.0 -> v0.11.0 * **github.com/ulikunitz/xz** v0.5.11 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.56.12 -> v0.56.16 * **github.com/vmware/govmomi** v0.29.0 -> v0.30.4 * **go.etcd.io/etcd/api/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/pkg/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/etcdutl/v3** v3.5.6 -> v3.5.7 * **go.uber.org/zap** v1.23.0 -> v1.24.0 * **go4.org/netipx** 797b0c90d8ab -> f1b76eb4bb35 * **golang.org/x/net** v0.4.0 -> v0.8.0 * **golang.org/x/sys** v0.3.0 -> v0.6.0 * **golang.org/x/term** v0.3.0 -> v0.6.0 * **golang.org/x/time** v0.2.0 -> v0.3.0 * **golang.zx2c4.com/wireguard/wgctrl** 97bc4ad4a1cb -> 9c5414ab4bde * **google.golang.org/grpc** v1.51.0 -> v1.54.0 * **google.golang.org/protobuf** v1.28.1 -> v1.30.0 * **k8s.io/api** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/apimachinery** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/apiserver** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/client-go** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/component-base** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/cri-api** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/klog/v2** v2.80.1 -> v2.90.1 * **k8s.io/kubectl** v0.26.0 -> v0.27.0-beta.0 * **k8s.io/kubelet** v0.26.0 -> v0.27.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.66 -> v1.2.67 Previous release can be found at [v1.3.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0) ## [Talos 1.4.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.4.0-alpha.2) (2023-02-28) Welcome to the v1.4.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Registry Mirror Catch-All Option Talos now supports a catch-all option for registry mirrors: ```yaml machine: registries: mirrors: docker.io: - https://registry-1.docker.io/ "*": - https://my-registry.example.com/ ``` ### Talos Dashboard on TTY2 Talos now starts a text-based UI dashboard on virtual console `/dev/tty2` and switches to it by default upon boot. Kernel logs remain available on `/dev/tty1`. To switch TTYs, use the `Alt+F1` through `Alt+F2` keys. You can disable this behavior by setting the kernel parameter `talos.dashboard.disabled=1`. This behavior is disabled by default on SBCs. ### etcd Maintenance Talos adds new APIs to make it easier to perform etcd maintenance operations. These APIs are available via new `talosctl etcd` sub-commands: * `talosctl etcd alarm list|disarm` * `talosctl etcd defrag` * `talosctl etcd status` See also [etcd maintenance guide](https://talos.dev/v1.4/advanced/etcd-maintenance/). ### Kernel Modules Talos now supports automatically loading kernel drivers built as modules. If any system extensions or the Talos base kernel build provides kernel modules and if they matches the system hardware (via PCI IDs), they will be loaded automatically. Modules can still be loaded explicitly by defining it in [machine configuration](https://www.talos.dev/v1.4/reference/configuration/#kernelconfig). ### Kernel Modules Tree Talos now supports re-building the kernel modules dependency tree information on upgrades. This allows modules of same name to co-exist as in-tree and external modules. System Extensions can provide modules installed into `extras` directory and when loading it'll take precendence over the in-tree module. ### Machine Configuration Strategic merge config patches correctly support merging `.vlans` sections of the network interface. ### Reset API Enhancements Talos now supports resetting user disks through the Reset API, the list of disks to wipe is set using the `--user-disks-to-wipe` parameter in `talosctl`. Additionally, the Reset API can now function in maintenance mode and has the capability to wipe the node's system disk (partial wipe is not supported). ### Component Updates * Linux: 6.1.12 * containerd: v1.6.18 * Kubernetes: v1.26.1 * etcd: v3.5.7 * CoreDNS: v1.10.1 Talos is built with Go 1.20.1. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Spencer Smith * Serge Logvinov * Steve Francis * Artem Chernyshev * Tim Jones * Andrey Smirnov * Cees-Jan Kiewiet * Chris van de Sande * Lance R. Vick * Matthias Riegler * Michael Vorburger * Murtaza Udaipurwala * Nico Berlee * Niklas Wik * Rowan Smith * Samuel Kees * Sander Maijers * Seán C McCord * Steffen Windoffer * Tim van Druenen * Victor Seva * budimanjojo ### Changes
128 commits

* [`b52071081`](https://github.com/siderolabs/talos/commit/b5207108104eda426361c256ec4d78ae9e0b2890) feat: introduce new flag in reset API that makes Talos reset user disks * [`f55f5df73`](https://github.com/siderolabs/talos/commit/f55f5df7396b7073e75267c7e10a35814f1185c9) feat: move dashboard package & run it in tty2 * [`36e077ead`](https://github.com/siderolabs/talos/commit/36e077ead458f15e864f62eeb0d7afa59187c226) chore: bump deps * [`5a01d5fd4`](https://github.com/siderolabs/talos/commit/5a01d5fd473cdc4e0b9fba48047d6434cf31ee42) chore: run extension build as downstream * [`426fe9687`](https://github.com/siderolabs/talos/commit/426fe9687d74690df26ce3cfd6aee47c13e994a8) fix: extension base folder permission * [`609d3a8a6`](https://github.com/siderolabs/talos/commit/609d3a8a694ff90426ce33be86791f2616ec4852) feat: support strategic merge patches on VLAN configuration * [`7e19f32d7`](https://github.com/siderolabs/talos/commit/7e19f32d762dc1363f29e988ddbe334bd00610f2) chore: provide version compatibility data for Talos 1.2.x * [`230e46e56`](https://github.com/siderolabs/talos/commit/230e46e567012d8e12e384c777d6f57db5e7cfee) refactor: extract parts of kubernetes libraries * [`f3d3f0f26`](https://github.com/siderolabs/talos/commit/f3d3f0f2625f1be41a17366ee1c0bd2a3193c08c) fix: update go-smbios library with Hyper-V data fix * [`8711eea96`](https://github.com/siderolabs/talos/commit/8711eea9626a60a996347aaa7e6a89eea87d4b9e) fix: use passed `--context` in `talosctl config` cmd * [`5ac9f43e4`](https://github.com/siderolabs/talos/commit/5ac9f43e45f85f8d37c2855051b9a5cc9ad389ac) feat: start machined earlier & in maintenance mode * [`36ab414a1`](https://github.com/siderolabs/talos/commit/36ab414a1d7c5472522d20a7b698c4eebb3423b9) docs: fix the endpoints in the libvirt guide * [`3d55bd80f`](https://github.com/siderolabs/talos/commit/3d55bd80f42b7d2439541909c9534c386607e578) fix: add `--force` flag to `talosctl gen config` * [`660b8874d`](https://github.com/siderolabs/talos/commit/660b8874da7bd91946aab5f400e7d1dfddefb827) feat: cmdline integer netmask * [`1e3daacc4`](https://github.com/siderolabs/talos/commit/1e3daacc48c0b8ef2eab41b2c2c53f55522e1acf) docs: update nvidia component versions * [`b5c03a7fa`](https://github.com/siderolabs/talos/commit/b5c03a7fab8d213e7048a8f5fc129125b81eb205) fix: docker talosctl cluster create provisioner * [`6e8f13529`](https://github.com/siderolabs/talos/commit/6e8f13529c17ff4c658b340d16d9ee429cfd9a4c) fix: add support for a fallback '*' mirror configuration * [`dcd4eb1a9`](https://github.com/siderolabs/talos/commit/dcd4eb1a93737d60f60693d8c33a20052eee4a4f) fix: improve error message on single node upgrade * [`ed5af3f78`](https://github.com/siderolabs/talos/commit/ed5af3f780732fb0004ddb263feedbf2de9fd09a) chore: bump deps * [`0dc6858e5`](https://github.com/siderolabs/talos/commit/0dc6858e5ba4b110eac9ca74294eb3a29790a323) chore: bump cosi-project/runtime * [`da2edb9de`](https://github.com/siderolabs/talos/commit/da2edb9de067fc21c792e948903bc2c880b2c2d1) chore: bump dependencies * [`e51a110f0`](https://github.com/siderolabs/talos/commit/e51a110f0e876fc091aee0828aca0135499def9c) chore: bump dependencies * [`2d0148018`](https://github.com/siderolabs/talos/commit/2d014801803fa0d5f08a344bdc9ff078b3931633) feat: automatically load modules based on hw info * [`7b75cd8b9`](https://github.com/siderolabs/talos/commit/7b75cd8b94367645adb2dd5be016e6f98d8e6a89) fix: kernel module dependency tree generation * [`65d02e5ad`](https://github.com/siderolabs/talos/commit/65d02e5ade08354aeec794d4131a1f8913fba2b5) fix: dbus shutdown when it's not initialized * [`a7079ce85`](https://github.com/siderolabs/talos/commit/a7079ce85c9839933544b637100f104f02fd3f3a) fix: quote the ampersand character in GRUB config * [`933ba2d82`](https://github.com/siderolabs/talos/commit/933ba2d8203e4418414b3de1c4240c1f88cb033e) fix: display correct blockdevice size * [`c449cb736`](https://github.com/siderolabs/talos/commit/c449cb736b24b268b965da5e2932f18bd4fb7785) fix: talosctl reboot command passing mode in wait mode * [`34ab0007a`](https://github.com/siderolabs/talos/commit/34ab0007a61bbb685d8c194c06568974db2a7375) docs: port is needed for wireguard endpoint * [`1e1aa84f6`](https://github.com/siderolabs/talos/commit/1e1aa84f6cdd0fbe6dd35841b6195cc56f10d333) fix: kubernetes removed resource version check * [`dcbcf5a93`](https://github.com/siderolabs/talos/commit/dcbcf5a93c3d82f8fdd7b8ffef3819010bd1c481) fix: wait for network and retry in platform get config funcs * [`3d7566ec7`](https://github.com/siderolabs/talos/commit/3d7566ec743f573a43a4a49ecb80f6ba59cbb27b) test: update Canal CNI manifest URL * [`e09e10666`](https://github.com/siderolabs/talos/commit/e09e106665aa8716f14ba49d527d8cb182592da7) fix: default dns domain to 'cluster.local' in local case * [`cc6e37a47`](https://github.com/siderolabs/talos/commit/cc6e37a47fd2ca9f1e43ce8ba2c1e8d8bfe44776) feat: use process wrapper for dropping capabilities * [`0c6c88874`](https://github.com/siderolabs/talos/commit/0c6c888745c5482fcf3891c922cc7cc7f72e6af4) fix: trackable action flag usage text. --no-wait does not exist * [`5cb2915d8`](https://github.com/siderolabs/talos/commit/5cb2915d8ea6e4ba913396abe3f45235e6a67213) feat: use wrapper for starting processes * [`56d945326`](https://github.com/siderolabs/talos/commit/56d9453261d47c0739be21cb7a5fe6beb25cb92c) fix: panic in talosctl cluster show * [`38a51191e`](https://github.com/siderolabs/talos/commit/38a51191e49059e93f4adfea479c039819a7f730) fix: correctly expand parameters in the URL * [`af21860a2`](https://github.com/siderolabs/talos/commit/af21860a22598361f68cf49e62a12da54bc95337) fix: return proper error if download attempts time out * [`54f7d4c92`](https://github.com/siderolabs/talos/commit/54f7d4c9231e858216f3b69b2662d7cc188df4f9) fix: correctly quote and unquote strings in GRUB config * [`54cf0672a`](https://github.com/siderolabs/talos/commit/54cf0672a71a8c9427c66bb2601521a9d24f8e13) fix: omit zero MTU in the machine config * [`bdc53ac25`](https://github.com/siderolabs/talos/commit/bdc53ac254a4aaa37ffd917c7c3ad506368205de) docs: add hyperlink to Docker API docs about `config.json` * [`b3bc06dd1`](https://github.com/siderolabs/talos/commit/b3bc06dd14c7faa75269cb6686b2d93ce765595c) chore: bump vtprotobuf to v0.4.0 * [`0ba5e59f6`](https://github.com/siderolabs/talos/commit/0ba5e59f69c08ab566177df9e26a21648bcde54f) fix: drone config for renovate PR's * [`590a393de`](https://github.com/siderolabs/talos/commit/590a393de968556bb5e19594b2f057d4233c378d) fix: udevd healthcheck * [`2b6b6deac`](https://github.com/siderolabs/talos/commit/2b6b6deacda4a3cdf6c5b65ac586cad1363be094) docs: simplify and clarify digital ocean docs * [`92bc15f7f`](https://github.com/siderolabs/talos/commit/92bc15f7f1c561b1e7810371df23f84c7e0d6a1c) release(v1.4.0-alpha.1): prepare release * [`e3da4754e`](https://github.com/siderolabs/talos/commit/e3da4754e7a2e69b998b861034c6f77e2cf6355b) feat: update Linux to 6.1.7 * [`006449e46`](https://github.com/siderolabs/talos/commit/006449e464ac009e15d78bb4d71cee80f2540f31) test: build integration test early in the pipeline * [`09aa71264`](https://github.com/siderolabs/talos/commit/09aa7126422b9b41e74c3d2aacb563daeca33bc5) fix: renovate config * [`2d136f187`](https://github.com/siderolabs/talos/commit/2d136f1879ee66dbd61ab40bb001a45c0bafaad5) feat: set markdown and html descriptions in config json schema * [`f0804027a`](https://github.com/siderolabs/talos/commit/f0804027a499a6e195f049144bff4f939dee3780) fix: renovate config * [`812a2877c`](https://github.com/siderolabs/talos/commit/812a2877cdc1e631ae0244f9696a65e2347594c0) chore: bump deps + renovate cleanup * [`aa9f66c1c`](https://github.com/siderolabs/talos/commit/aa9f66c1c88a1bb35aefe24ea0a5c3a6e7aa966d) fix: mark DigitalOcean anchor IP as scope link * [`bb4937f1b`](https://github.com/siderolabs/talos/commit/bb4937f1b339384fb486cb0cb675df8bf9b9f916) feat: enable renovate * [`3e0057162`](https://github.com/siderolabs/talos/commit/3e00571627568d8c5ab10a72e59207677a89e4cc) fix: unwrap gRPC errors on stop/remove pods check * [`00e52ae07`](https://github.com/siderolabs/talos/commit/00e52ae07867deff9a5877fcb498252bc1b1a740) fix: build correctly etcd initial cluster URL * [`ae83b10ae`](https://github.com/siderolabs/talos/commit/ae83b10ae89dbe600ddfaa338be95ea819546007) feat: create JSON schema for v1alpha1.Config * [`703d96595`](https://github.com/siderolabs/talos/commit/703d9659512d744a606e520faf230e20efddfc4a) feat: update Kubernetes to 1.26.1, etcd to 3.5.7 * [`965e64591`](https://github.com/siderolabs/talos/commit/965e645915d080487a74b35dc8f1d2e4051f0504) docs: update to use talosctl install script * [`c5954f434`](https://github.com/siderolabs/talos/commit/c5954f4345cbf3a92c777a0e7fc5d39e883609bf) chore: bump deps * [`bb50f6a56`](https://github.com/siderolabs/talos/commit/bb50f6a56d971915abb6a895aac9d7e0612a3255) chore: preallocate disk images for QEMU VMs * [`d4b8b35de`](https://github.com/siderolabs/talos/commit/d4b8b35de7849d887c41f9a13dadb59ccd8c08c4) feat: generate kernel module dependency tree * [`18122ae73`](https://github.com/siderolabs/talos/commit/18122ae73e0489a0497956c6d4621c05c6a77387) fix: service restart (including extension services) * [`680fd5e45`](https://github.com/siderolabs/talos/commit/680fd5e452e02b108b7938d0136079c16e6cfd79) fix: bump COSI runtime with the panic controller restart fix * [`0b65bbfc8`](https://github.com/siderolabs/talos/commit/0b65bbfc878fe2a5c01c5d2cd08006b53fda7cf9) fix: handle overwriting tags in syslinux ADV * [`70d9428a1`](https://github.com/siderolabs/talos/commit/70d9428a1d00d9894d68f38b255debb66fe8a440) fix: kubespan MSS clamping * [`683b4ccb4`](https://github.com/siderolabs/talos/commit/683b4ccb4faab6c3da2de00f7314773f42899c25) chore: update Go to 1.19.5 and kernel to 6.1.4 * [`062c7d754`](https://github.com/siderolabs/talos/commit/062c7d754be1714c7763b8f2b399436d64c90ea4) test: fix integration test on cp endpoint update * [`8e9fc13d7`](https://github.com/siderolabs/talos/commit/8e9fc13d7c48da5c5354501e0ad96688670438cf) feat: implement enum generator for proto files * [`771b0dc06`](https://github.com/siderolabs/talos/commit/771b0dc061e0fa33085b28bd0d0a7e4da13081f1) docs: update left over rpi_4 ref to rpi_generic * [`6c04b5f79`](https://github.com/siderolabs/talos/commit/6c04b5f79e6e01e0a3cdabfc99f12c944edd1f0a) chore: bump dependencies * [`0a5a8802e`](https://github.com/siderolabs/talos/commit/0a5a8802e7e337e1f30a40c9f566e57642c39c1a) feat: use 'localhost' endpoint for controlplane nodes * [`b0775ebf2`](https://github.com/siderolabs/talos/commit/b0775ebf2c776c7133cf74c6259de9dc9573786c) feat: add ISO wipe GRUB boot option * [`29020cb9c`](https://github.com/siderolabs/talos/commit/29020cb9c788d87a0457028ce73c8d297959116e) fix: report fatal sequence errors as reboots * [`96629d5ba`](https://github.com/siderolabs/talos/commit/96629d5ba6c1ae9d820824fb38f68112bce27f2c) feat: implement etcd maintenance commands * [`80fed3194`](https://github.com/siderolabs/talos/commit/80fed319408be9e493141fb2c01e5731708835c7) feat: include Kubernetes controlplane endpoint as one of the endpoints * [`c6cb36cc1`](https://github.com/siderolabs/talos/commit/c6cb36cc1f50b5d0e59a5284867e7534dc9f73bb) docs: fix auditpolicy example typo * [`ba8265bc5`](https://github.com/siderolabs/talos/commit/ba8265bc5ce63bcbc6fbd6c1a1076dc3f2ee6bd0) feat: new talosctl config remove to remove context * [`fcb19ff51`](https://github.com/siderolabs/talos/commit/fcb19ff516cc1200ec81f2a954bb6d2ce39ebdc6) fix: implement upgrade version checks for Talos 1.4 * [`80f150ac8`](https://github.com/siderolabs/talos/commit/80f150ac859f5dbf95060c12440afab8c0bc77a8) feat: enable ipv6 on gcp * [`8db622f3d`](https://github.com/siderolabs/talos/commit/8db622f3dc75aed90dd2d0bd92d03aa7e8aefd10) docs: add Vandebron to adopters list * [`f6a86ae90`](https://github.com/siderolabs/talos/commit/f6a86ae90607914c29875df750fe79cbbfcc5897) fix: oralce cloud zone * [`89dbb0ecf`](https://github.com/siderolabs/talos/commit/89dbb0ecf089bb746479238df274ccba4fcb049a) release(v1.4.0-alpha.0): prepare release * [`31fb90535`](https://github.com/siderolabs/talos/commit/31fb9053582190b3b536a309c30e2b78c4611885) feat: update Linux 6.1.1, containerd 1.6.14 * [`a0c0352dd`](https://github.com/siderolabs/talos/commit/a0c0352ddca253e1efb3679224b317692d46b2fd) fix: send diagnostic output to stderr consistently * [`9a5f4c08a`](https://github.com/siderolabs/talos/commit/9a5f4c08a206504a1d30277dcc0597333e5a927a) fix: default the manifest namespace if not set * [`3c6cce5fe`](https://github.com/siderolabs/talos/commit/3c6cce5fe47075f43a73682b57a7b40fa0899795) docs: update last release for Talos 1.2.x * [`703624c43`](https://github.com/siderolabs/talos/commit/703624c43dd8e58c147ccbc3989c6c436c9f3a7f) docs: fix the 1.3 release date * [`386c9293a`](https://github.com/siderolabs/talos/commit/386c9293a33e9d237fbeda0492b01b11fdadc501) docs: update nvidia-container-runtime version * [`ff83d9fd7`](https://github.com/siderolabs/talos/commit/ff83d9fd7bed2e04d5c8107713150c2513f47991) fix: improve talosctl completion * [`31ff431fa`](https://github.com/siderolabs/talos/commit/31ff431faec22c09cad88d565102e6a24785ecb4) chore: add schulz systemtechnik to the list * [`97bef7c47`](https://github.com/siderolabs/talos/commit/97bef7c47bfd133f2b3ad19efe3f30a88dd67460) docs: vsphere.sh > vmware.sh * [`34babe858`](https://github.com/siderolabs/talos/commit/34babe858d15145a1c596febb5e577473e4ffce0) chore: make organization selection an interface * [`a9643b477`](https://github.com/siderolabs/talos/commit/a9643b477417029db73aacbfcf5778cedd97cd95) fix: use proper key usage for apid client certificate * [`171aa9467`](https://github.com/siderolabs/talos/commit/171aa9467966f5869e72374961ea05abc8d9fda9) fix: disable Wireless Lan using dtoverlay * [`2e84d2ab3`](https://github.com/siderolabs/talos/commit/2e84d2ab3417515f539a70d58885dcb69e9f098c) chore: update conformance product.yaml * [`b7763843a`](https://github.com/siderolabs/talos/commit/b7763843af63bbc186f08701a62c19ea96fb7e3c) feat: add install script that improves talosctl installation user experience This install script detects the platform and architecture, and downloads the correct talosctl, and checks the gpg checksums. It also installs and chmods the binary. * [`afc45ad63`](https://github.com/siderolabs/talos/commit/afc45ad632e63cc3afc095b1f3efe6df3ecb9cb1) docs: mark Talos 1.3 docs as default * [`873bd3807`](https://github.com/siderolabs/talos/commit/873bd3807c0fcca2e212deb7fd044662557964c1) fix: redact service account key in config in RedactSecrets method * [`b3aebfadf`](https://github.com/siderolabs/talos/commit/b3aebfadfc15544e5ab448d979129dba5e516c59) feat: validate Talos API access roles in machine config * [`40761e17d`](https://github.com/siderolabs/talos/commit/40761e17db5789f30eef2f15f0b5c6396e09a9e5) docs: fork docs for Talos 1.4 * [`474604cd2`](https://github.com/siderolabs/talos/commit/474604cd279def7a6798e24ede27feef955ba5a3) docs: update documentation for Talos 1.3 * [`faf49218c`](https://github.com/siderolabs/talos/commit/faf49218ce14a48829dae7b3b8d7801188453a89) feat: add more checks for K8s upgrade * [`5b992bd86`](https://github.com/siderolabs/talos/commit/5b992bd8610f41d23d8b7dbd01f9a1be298eda96) fix: allow empty dnsDomain in machine config * [`eb332cfcb`](https://github.com/siderolabs/talos/commit/eb332cfcb785e250c422d6a7ea2b23679189a946) feat: add health check for a minimal memory / disk size * [`d04970dfa`](https://github.com/siderolabs/talos/commit/d04970dfa9d6554e1ee447fd9383bf65b8953671) fix: ignore k8s additional addresses if nil * [`63c17104c`](https://github.com/siderolabs/talos/commit/63c17104c594dfd9ca4066ba41d8a03507464874) feat: update Kubernets to 1.26.0 * [`f7a9a90db`](https://github.com/siderolabs/talos/commit/f7a9a90db2bfd316ea01551daba9becb15361f94) chore: update pkgs/tools (Go 1.19.4, containerd 1.6.11) * [`cf7adc51c`](https://github.com/siderolabs/talos/commit/cf7adc51c9f53234e469dd9f0cca06eed0230e8b) feat: add RedactSecrets method to v1alpha1.Config * [`4c31b9b1a`](https://github.com/siderolabs/talos/commit/4c31b9b1a3a00df0fe817c3edc15260ca3cadd6d) docs: clarify what the deal is with /var * [`a8ebcca4a`](https://github.com/siderolabs/talos/commit/a8ebcca4a9f63643f68d8e85bcb0b9ddb49205ed) chore: remove `watchErr` from `metal.getResource` * [`1253513bd`](https://github.com/siderolabs/talos/commit/1253513bd1deecc4cc42330bad0a713b3630240a) fix: fix nil pointer panic and incorrect error output * [`82e8c9e1f`](https://github.com/siderolabs/talos/commit/82e8c9e1f63371f41b0794b4c1be3209847c5f8b) fix: workaround panic in the kubelet service controller * [`a505b8909`](https://github.com/siderolabs/talos/commit/a505b8909a1c733b30f22a8d46eebc022475431a) fix: update COSI and reset restart backoff on success * [`e92fdcbad`](https://github.com/siderolabs/talos/commit/e92fdcbad1de595d119f78dbed3a97ae46df9bbf) chore: bump kernel to 5.15.81 * [`f0dddca2a`](https://github.com/siderolabs/talos/commit/f0dddca2a3d2e976cee543ab57816a6395fe3d65) docs: expand help for 'talosctl get' * [`fcffc8879`](https://github.com/siderolabs/talos/commit/fcffc88790b5a3006b3b85744771a7eef6e8ac5c) fix: add ext4 filesystem detection * [`5b2960eff`](https://github.com/siderolabs/talos/commit/5b2960efff8b38af85b687a25fa93f01256016de) fix: introduce 'overridePath' setting and fix Talos resolver * [`0219d1124`](https://github.com/siderolabs/talos/commit/0219d1124e5125696364bf92ecf0e8dcad644001) fix: use only kube-apiserver endpoints for Talos API access endpoints * [`dc5e0f4af`](https://github.com/siderolabs/talos/commit/dc5e0f4af087d3b662b0240b4f8fd76379ed0de2) fix: report errors to Equinix Metal event API * [`7ab140a94`](https://github.com/siderolabs/talos/commit/7ab140a94ad1a279be43669d6d70687f3a0c47de) feat: add talosctl machineconfig patch command * [`d3cf06114`](https://github.com/siderolabs/talos/commit/d3cf061149a4a502317d7728c45b6cfb4d38f89f) fix: ignore many more filesystems in IMA * [`44e2799b8`](https://github.com/siderolabs/talos/commit/44e2799b8cb928083f3a777d5cce45ad8dbf6864) feat: add stdout and single config type support to talosctl gen config * [`4452f0e17`](https://github.com/siderolabs/talos/commit/4452f0e179db16c59dc65ccdb5a496ad3306684e) docs: bump talos version * [`38e57bd12`](https://github.com/siderolabs/talos/commit/38e57bd12b8c50d668fcde6ee9aa493682778dcc) feat: update Kubernetes to v1.26.0-rc.1 * [`4cd125d49`](https://github.com/siderolabs/talos/commit/4cd125d499a24798dfde1dddf6fa1c689d16c93f) fix: correctly handle new watch event types * [`881b84152`](https://github.com/siderolabs/talos/commit/881b84152084d157fbd4ff992089a5392aadfd3c) feat: update Flannel to 0.20.2

### Changes since v1.4.0-alpha.1
46 commits

* [`b52071081`](https://github.com/siderolabs/talos/commit/b5207108104eda426361c256ec4d78ae9e0b2890) feat: introduce new flag in reset API that makes Talos reset user disks * [`f55f5df73`](https://github.com/siderolabs/talos/commit/f55f5df7396b7073e75267c7e10a35814f1185c9) feat: move dashboard package & run it in tty2 * [`36e077ead`](https://github.com/siderolabs/talos/commit/36e077ead458f15e864f62eeb0d7afa59187c226) chore: bump deps * [`5a01d5fd4`](https://github.com/siderolabs/talos/commit/5a01d5fd473cdc4e0b9fba48047d6434cf31ee42) chore: run extension build as downstream * [`426fe9687`](https://github.com/siderolabs/talos/commit/426fe9687d74690df26ce3cfd6aee47c13e994a8) fix: extension base folder permission * [`609d3a8a6`](https://github.com/siderolabs/talos/commit/609d3a8a694ff90426ce33be86791f2616ec4852) feat: support strategic merge patches on VLAN configuration * [`7e19f32d7`](https://github.com/siderolabs/talos/commit/7e19f32d762dc1363f29e988ddbe334bd00610f2) chore: provide version compatibility data for Talos 1.2.x * [`230e46e56`](https://github.com/siderolabs/talos/commit/230e46e567012d8e12e384c777d6f57db5e7cfee) refactor: extract parts of kubernetes libraries * [`f3d3f0f26`](https://github.com/siderolabs/talos/commit/f3d3f0f2625f1be41a17366ee1c0bd2a3193c08c) fix: update go-smbios library with Hyper-V data fix * [`8711eea96`](https://github.com/siderolabs/talos/commit/8711eea9626a60a996347aaa7e6a89eea87d4b9e) fix: use passed `--context` in `talosctl config` cmd * [`5ac9f43e4`](https://github.com/siderolabs/talos/commit/5ac9f43e45f85f8d37c2855051b9a5cc9ad389ac) feat: start machined earlier & in maintenance mode * [`36ab414a1`](https://github.com/siderolabs/talos/commit/36ab414a1d7c5472522d20a7b698c4eebb3423b9) docs: fix the endpoints in the libvirt guide * [`3d55bd80f`](https://github.com/siderolabs/talos/commit/3d55bd80f42b7d2439541909c9534c386607e578) fix: add `--force` flag to `talosctl gen config` * [`660b8874d`](https://github.com/siderolabs/talos/commit/660b8874da7bd91946aab5f400e7d1dfddefb827) feat: cmdline integer netmask * [`1e3daacc4`](https://github.com/siderolabs/talos/commit/1e3daacc48c0b8ef2eab41b2c2c53f55522e1acf) docs: update nvidia component versions * [`b5c03a7fa`](https://github.com/siderolabs/talos/commit/b5c03a7fab8d213e7048a8f5fc129125b81eb205) fix: docker talosctl cluster create provisioner * [`6e8f13529`](https://github.com/siderolabs/talos/commit/6e8f13529c17ff4c658b340d16d9ee429cfd9a4c) fix: add support for a fallback '*' mirror configuration * [`dcd4eb1a9`](https://github.com/siderolabs/talos/commit/dcd4eb1a93737d60f60693d8c33a20052eee4a4f) fix: improve error message on single node upgrade * [`ed5af3f78`](https://github.com/siderolabs/talos/commit/ed5af3f780732fb0004ddb263feedbf2de9fd09a) chore: bump deps * [`0dc6858e5`](https://github.com/siderolabs/talos/commit/0dc6858e5ba4b110eac9ca74294eb3a29790a323) chore: bump cosi-project/runtime * [`da2edb9de`](https://github.com/siderolabs/talos/commit/da2edb9de067fc21c792e948903bc2c880b2c2d1) chore: bump dependencies * [`e51a110f0`](https://github.com/siderolabs/talos/commit/e51a110f0e876fc091aee0828aca0135499def9c) chore: bump dependencies * [`2d0148018`](https://github.com/siderolabs/talos/commit/2d014801803fa0d5f08a344bdc9ff078b3931633) feat: automatically load modules based on hw info * [`7b75cd8b9`](https://github.com/siderolabs/talos/commit/7b75cd8b94367645adb2dd5be016e6f98d8e6a89) fix: kernel module dependency tree generation * [`65d02e5ad`](https://github.com/siderolabs/talos/commit/65d02e5ade08354aeec794d4131a1f8913fba2b5) fix: dbus shutdown when it's not initialized * [`a7079ce85`](https://github.com/siderolabs/talos/commit/a7079ce85c9839933544b637100f104f02fd3f3a) fix: quote the ampersand character in GRUB config * [`933ba2d82`](https://github.com/siderolabs/talos/commit/933ba2d8203e4418414b3de1c4240c1f88cb033e) fix: display correct blockdevice size * [`c449cb736`](https://github.com/siderolabs/talos/commit/c449cb736b24b268b965da5e2932f18bd4fb7785) fix: talosctl reboot command passing mode in wait mode * [`34ab0007a`](https://github.com/siderolabs/talos/commit/34ab0007a61bbb685d8c194c06568974db2a7375) docs: port is needed for wireguard endpoint * [`1e1aa84f6`](https://github.com/siderolabs/talos/commit/1e1aa84f6cdd0fbe6dd35841b6195cc56f10d333) fix: kubernetes removed resource version check * [`dcbcf5a93`](https://github.com/siderolabs/talos/commit/dcbcf5a93c3d82f8fdd7b8ffef3819010bd1c481) fix: wait for network and retry in platform get config funcs * [`3d7566ec7`](https://github.com/siderolabs/talos/commit/3d7566ec743f573a43a4a49ecb80f6ba59cbb27b) test: update Canal CNI manifest URL * [`e09e10666`](https://github.com/siderolabs/talos/commit/e09e106665aa8716f14ba49d527d8cb182592da7) fix: default dns domain to 'cluster.local' in local case * [`cc6e37a47`](https://github.com/siderolabs/talos/commit/cc6e37a47fd2ca9f1e43ce8ba2c1e8d8bfe44776) feat: use process wrapper for dropping capabilities * [`0c6c88874`](https://github.com/siderolabs/talos/commit/0c6c888745c5482fcf3891c922cc7cc7f72e6af4) fix: trackable action flag usage text. --no-wait does not exist * [`5cb2915d8`](https://github.com/siderolabs/talos/commit/5cb2915d8ea6e4ba913396abe3f45235e6a67213) feat: use wrapper for starting processes * [`56d945326`](https://github.com/siderolabs/talos/commit/56d9453261d47c0739be21cb7a5fe6beb25cb92c) fix: panic in talosctl cluster show * [`38a51191e`](https://github.com/siderolabs/talos/commit/38a51191e49059e93f4adfea479c039819a7f730) fix: correctly expand parameters in the URL * [`af21860a2`](https://github.com/siderolabs/talos/commit/af21860a22598361f68cf49e62a12da54bc95337) fix: return proper error if download attempts time out * [`54f7d4c92`](https://github.com/siderolabs/talos/commit/54f7d4c9231e858216f3b69b2662d7cc188df4f9) fix: correctly quote and unquote strings in GRUB config * [`54cf0672a`](https://github.com/siderolabs/talos/commit/54cf0672a71a8c9427c66bb2601521a9d24f8e13) fix: omit zero MTU in the machine config * [`bdc53ac25`](https://github.com/siderolabs/talos/commit/bdc53ac254a4aaa37ffd917c7c3ad506368205de) docs: add hyperlink to Docker API docs about `config.json` * [`b3bc06dd1`](https://github.com/siderolabs/talos/commit/b3bc06dd14c7faa75269cb6686b2d93ce765595c) chore: bump vtprotobuf to v0.4.0 * [`0ba5e59f6`](https://github.com/siderolabs/talos/commit/0ba5e59f69c08ab566177df9e26a21648bcde54f) fix: drone config for renovate PR's * [`590a393de`](https://github.com/siderolabs/talos/commit/590a393de968556bb5e19594b2f057d4233c378d) fix: udevd healthcheck * [`2b6b6deac`](https://github.com/siderolabs/talos/commit/2b6b6deacda4a3cdf6c5b65ac586cad1363be094) docs: simplify and clarify digital ocean docs

### Changes from siderolabs/discovery-api
1 commit

* [`ac75538`](https://github.com/siderolabs/discovery-api/commit/ac75538ee3a9f7b71b6619f509d95ff5057f6754) chore: regen the proto definitions with vtprotobuf v0.4.0

### Changes from siderolabs/discovery-client
1 commit

* [`269a832`](https://github.com/siderolabs/discovery-client/commit/269a832ce9e35d4edeeddba2a23cf5682a2ca425) chore: rekres, update discovery api

### Changes from siderolabs/extras
6 commits

* [`8b28b6b`](https://github.com/siderolabs/extras/commit/8b28b6b5a0153c65af596086016faea9d64e95c2) chore: bump deps * [`5ab4f59`](https://github.com/siderolabs/extras/commit/5ab4f5939c830c7043e3939e519305eb810cdfc2) chore: disable renovate builds * [`ddeddbd`](https://github.com/siderolabs/extras/commit/ddeddbd1976813de6b1563f662ca4f2b3f5e0f53) chore: update packages, tc_redirect_tap * [`8cb4792`](https://github.com/siderolabs/extras/commit/8cb4792da9b9e2b2663daca747d24c3b5c973e0f) chore: update Go to 1.19.5 * [`3ca2df3`](https://github.com/siderolabs/extras/commit/3ca2df3ead2a64a5ad30c350b87bfe02bf1f49c7) chore: disable provenance in buildx * [`55d8452`](https://github.com/siderolabs/extras/commit/55d845241c8456909ab36f9b0f4e26cc2b49c256) feat: update releases

### Changes from siderolabs/gen
2 commits

* [`214c1ef`](https://github.com/siderolabs/gen/commit/214c1efe795cf426e5ebcc48cb305bfc7a16fdb8) chore: set `slice.Filter` result slice cap to len * [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods

### Changes from siderolabs/go-blockdevice
1 commit

* [`8c7ea19`](https://github.com/siderolabs/go-blockdevice/commit/8c7ea1910b27e0660e3e1a6f98b9f7e24bc11ff0) fix: blockdevice size is reported by Linux in 512 blocks always

### Changes from siderolabs/go-kubernetes
2 commits

* [`570819b`](https://github.com/siderolabs/go-kubernetes/commit/570819b93ecc63218b3db8d90e4810765a069ee0) feat: initial version of the library * [`fb79215`](https://github.com/siderolabs/go-kubernetes/commit/fb7921556e96fc7c0a84ac23834350bcd37cfa38) Initial commit

### Changes from siderolabs/go-smbios
1 commit

* [`c526764`](https://github.com/siderolabs/go-smbios/commit/c5267640be317efd9cbbe936ab78b2a49c757edf) feat: fix reading "broken" Hyper-V DMI data

### Changes from siderolabs/pkgs
30 commits

* [`5dbce6b`](https://github.com/siderolabs/pkgs/commit/5dbce6b19ff6a1e1b5ae88468e34925c3d30d627) fix: xz url * [`0097233`](https://github.com/siderolabs/pkgs/commit/00972336c3fcc22df8fc1d3774c35b26fdc957b9) chore: re-enable drbd * [`7493721`](https://github.com/siderolabs/pkgs/commit/749372110c6c8e226139cd662832b5a4169db894) fix: sourcefourge url shasums * [`185f482`](https://github.com/siderolabs/pkgs/commit/185f482db6a5c13a3b14feec02a4e361b53bec55) feat: update containerd to 1.6.18 * [`e3cab6c`](https://github.com/siderolabs/pkgs/commit/e3cab6cbd62b96143958ed5e0219d68107a5f583) chore: bump deps * [`18661b0`](https://github.com/siderolabs/pkgs/commit/18661b096559e673152ce0fed45ab74ef3305dff) chore: bump deps * [`885a68b`](https://github.com/siderolabs/pkgs/commit/885a68b6280f3bf4ff75508ccceef73158c53560) chore: bump deps * [`c3a6e18`](https://github.com/siderolabs/pkgs/commit/c3a6e185178d7571e891c7b2614bf6017ab5c913) chore: bump dependencies * [`1fae0b2`](https://github.com/siderolabs/pkgs/commit/1fae0b229a625d692d36e7d6c096f8476e0f56d7) feat: virtio drivers as modules * [`61d8ff4`](https://github.com/siderolabs/pkgs/commit/61d8ff4aaea93b86b82bc2a36a2bbd6d54da3bb8) chore: bump deps and disable un-needed kconfig * [`15fe6d8`](https://github.com/siderolabs/pkgs/commit/15fe6d8555b42e55f920a5576ad55504e356995b) fix: kernel module tree files missing * [`987d24a`](https://github.com/siderolabs/pkgs/commit/987d24aeaa4fb2278954cd96e6bc6a29a4c8dd61) feat: mellanox drivers are modules * [`b82a015`](https://github.com/siderolabs/pkgs/commit/b82a015c78c407d17d23542eba6a4114f3c2c4d7) feat: mellanox oped * [`057d4f9`](https://github.com/siderolabs/pkgs/commit/057d4f96aa3ba63cc456b06a70a6b3a008cf803f) chore: bump deps * [`4ac4138`](https://github.com/siderolabs/pkgs/commit/4ac4138c6b94622646c9f32f0885496c5475d905) feat: enable nvme support for raspberrypi cm4 * [`ccb9d39`](https://github.com/siderolabs/pkgs/commit/ccb9d39dc43cf53431a0d7609839ed9c7141972d) fix: disable magic sysrq * [`d33202d`](https://github.com/siderolabs/pkgs/commit/d33202d99daa6ccf136fca54ebbadda727a43a75) chore: bump u-boot to 2023.01 * [`cb83e16`](https://github.com/siderolabs/pkgs/commit/cb83e169df4a2020994a63e5be61524461ef93e3) chore: bump dependencies * [`e561dcb`](https://github.com/siderolabs/pkgs/commit/e561dcb45beae80161faccedb0303e58d41b1ded) feat: bump Go to 1.19.5 * [`c7797c7`](https://github.com/siderolabs/pkgs/commit/c7797c77bd311449e1f116980166d8d818102f4f) feat: update Linux to 6.1.4, restore RPi support * [`5e8ebb0`](https://github.com/siderolabs/pkgs/commit/5e8ebb073d9b58555a75912cd90490af8a435c7d) feat: add AMD K10 sensor support * [`73ac37d`](https://github.com/siderolabs/pkgs/commit/73ac37d683274e60340d2767f2b8201e7f13474c) chore: disable provenance in buildx * [`8965bee`](https://github.com/siderolabs/pkgs/commit/8965bee65313539e8b6534073d06341f4fb78586) chore: use default symlinks to `/bin` in `base` * [`325c9bf`](https://github.com/siderolabs/pkgs/commit/325c9bf0f3ed2bf7603d1eaea022ea650388cf2b) feat: bump dependencies * [`165dff6`](https://github.com/siderolabs/pkgs/commit/165dff6c3cdb2d05f170c8ae0616d9224416455e) fix: patch ipmitool IANA URL * [`c542f39`](https://github.com/siderolabs/pkgs/commit/c542f398a150567d5cdffc17b4248be5416fe242) feat: add kernel support for usb setrial console * [`f564f45`](https://github.com/siderolabs/pkgs/commit/f564f45645d102b7e3a9563ac7bdb1e816156e65) chore: bump tools, containerd * [`268ea7c`](https://github.com/siderolabs/pkgs/commit/268ea7c593ff04c4e4a9ea5676b3c58d41cbff14) chore: bump deps * [`dcf3ceb`](https://github.com/siderolabs/pkgs/commit/dcf3cebf283698e010aaac5417d91a7385dc2441) feat: add nitro enclave support in kernel * [`17ea5e6`](https://github.com/siderolabs/pkgs/commit/17ea5e680b2438c59fa1773e8b58d6b749cb0d34) chore: bump kernel to 5.15.81

### Changes from siderolabs/tools
20 commits

* [`cd9687b`](https://github.com/siderolabs/tools/commit/cd9687b4323b20493b4d582cfaa48c321cd04288) fix: renovate config * [`977e3fc`](https://github.com/siderolabs/tools/commit/977e3fcba92d129eb78cb77300f38428f860b34d) chore: bump go to 1.20.1 * [`15748aa`](https://github.com/siderolabs/tools/commit/15748aa32d7c1d67b190ab7a27ace9922c8d6b56) chore: bump deps * [`d4b719a`](https://github.com/siderolabs/tools/commit/d4b719a1c2055eaa27f80422f93755b0de9ca3f8) chore: bump deps * [`8c36dbd`](https://github.com/siderolabs/tools/commit/8c36dbd05ee27ecc2a7340462a3b49efb7327184) chore: bump toolchain, bump protoc-gen-go-grpc * [`a62e365`](https://github.com/siderolabs/tools/commit/a62e365b223e7ca9d2728865b40b23115764a0ed) feat: update Go to 1.20 * [`28d4a57`](https://github.com/siderolabs/tools/commit/28d4a5721ce1c57fc3f643185386d5c4b5c7e39a) chore: reduce renovate noise * [`e130fd5`](https://github.com/siderolabs/tools/commit/e130fd5b9835d8cc178ec53d5a89dfc6cc2ce7a1) chore: bump deps * [`37612fe`](https://github.com/siderolabs/tools/commit/37612feb7222b943a84f1f98d0901a204d491926) fix: revert enabling provenance * [`e0b01e3`](https://github.com/siderolabs/tools/commit/e0b01e3b7420e8b0b1e0d9077515e007a6b83b56) chore: bump deps * [`d0e6bd0`](https://github.com/siderolabs/tools/commit/d0e6bd06fcfcadc330cf30339488536961f9f70e) feat: add gnutls * [`3d34b5d`](https://github.com/siderolabs/tools/commit/3d34b5d401a67048d365e8faf2f1edf293887a97) chore: bump dependencies * [`763c1d9`](https://github.com/siderolabs/tools/commit/763c1d927822517b3d63c624302e11e8e5a49f5b) feat: update Go to 1.19.5 * [`136958f`](https://github.com/siderolabs/tools/commit/136958f9f8c8cfc439228dec31b840549bca4374) chore: disable provenance in buildx * [`e2a8692`](https://github.com/siderolabs/tools/commit/e2a869294be7e77e295ca651400f85551fb7e665) feat: update releases * [`0e48f37`](https://github.com/siderolabs/tools/commit/0e48f37496a79ce4997d15fefb6300b2324f5668) chore: bump protobuf * [`a21aa1c`](https://github.com/siderolabs/tools/commit/a21aa1c583a10d017ace8da14c6f604f86ce5709) chore: bump toolchain and mpc versions * [`1a75d0f`](https://github.com/siderolabs/tools/commit/1a75d0f6796c4abf1c9a23cfe697d3e38a9ce587) chore: bump deps * [`55bd185`](https://github.com/siderolabs/tools/commit/55bd18532667e325e8938bf0a72cab40a936eadf) feat: update Go to 1.19.4 * [`f291f46`](https://github.com/siderolabs/tools/commit/f291f46e84ec02f5d22718f7ecb476a3f815ae45) chore: bump tools

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 -> v0.2.3 * **github.com/aws/aws-sdk-go** v1.44.147 -> v1.44.209 * **github.com/containerd/cgroups** v1.0.4 -> v1.1.0 * **github.com/containerd/containerd** v1.6.12 -> v1.6.18 * **github.com/containernetworking/plugins** v1.1.1 -> v1.2.0 * **github.com/coreos/go-semver** v0.3.0 -> v0.3.1 * **github.com/cosi-project/runtime** v0.2.0 -> v0.3.0-alpha.7 * **github.com/docker/docker** v20.10.21 -> v23.0.1 * **github.com/dustin/go-humanize** v1.0.0 -> v1.0.1 * **github.com/emicklei/dot** v1.2.0 -> v1.3.1 * **github.com/fatih/color** v1.13.0 -> v1.14.1 * **github.com/freddierice/go-losetup/v2** v2.0.1 **_new_** * **github.com/gdamore/tcell/v2** v2.5.3 -> v2.6.0 * **github.com/hashicorp/go-getter** v1.6.2 -> v1.7.0 * **github.com/hetznercloud/hcloud-go** v1.37.0 -> v1.40.0 * **github.com/insomniacslk/dhcp** f26e6d78f622 -> 5369909a5de7 * **github.com/jsimonetti/rtnetlink** v1.3.0 -> v1.3.1 * **github.com/mattn/go-isatty** v0.0.16 -> v0.0.17 * **github.com/mdlayher/ethtool** 0e16326d06d1 -> ba3b4bc2e02c * **github.com/mdlayher/genetlink** v1.3.0 -> v1.3.1 * **github.com/mdlayher/netlink** v1.7.0 -> v1.7.1 * **github.com/prometheus/procfs** v0.8.0 -> v0.9.0 * **github.com/rivo/tview** db36428c92d9 -> 47e7db7885b4 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.10 -> v1.0.0-beta.13 * **github.com/siderolabs/discovery-api** v0.1.1 -> v0.1.2 * **github.com/siderolabs/discovery-client** v0.1.3 -> v0.1.4 * **github.com/siderolabs/extras** v1.3.0-1-g3773d71 -> v1.4.0-alpha.0-5-g8b28b6b * **github.com/siderolabs/gen** v0.4.1 -> v0.4.3 * **github.com/siderolabs/go-blockdevice** v0.4.2 -> v0.4.3 * **github.com/siderolabs/go-kubernetes** v0.1.0 **_new_** * **github.com/siderolabs/go-smbios** v0.3.1 -> v0.3.2 * **github.com/siderolabs/pkgs** v1.3.0-5-g6509d23 -> v1.4.0-alpha.0-29-g5dbce6b * **github.com/siderolabs/talos/pkg/machinery** v1.3.0 -> v1.4.0-alpha.1 * **github.com/siderolabs/tools** v1.3.0-1-g712379c -> v1.4.0-alpha.0-17-gcd9687b * **github.com/stretchr/testify** v1.8.1 -> v1.8.2 * **github.com/u-root/u-root** v0.10.0 -> v0.11.0 * **github.com/ulikunitz/xz** v0.5.11 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.56.12 -> v0.56.15 * **github.com/vmware/govmomi** v0.29.0 -> v0.30.2 * **go.etcd.io/etcd/api/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/pkg/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/etcdutl/v3** v3.5.6 -> v3.5.7 * **go.uber.org/zap** v1.23.0 -> v1.24.0 * **go4.org/netipx** 797b0c90d8ab -> 8449b0a6169f * **golang.org/x/net** v0.4.0 -> v0.7.0 * **golang.org/x/sys** v0.3.0 -> v0.5.0 * **golang.org/x/term** v0.3.0 -> v0.5.0 * **golang.org/x/time** v0.2.0 -> v0.3.0 * **golang.zx2c4.com/wireguard/wgctrl** 97bc4ad4a1cb -> 9c5414ab4bde * **google.golang.org/grpc** v1.51.0 -> v1.53.0 * **k8s.io/api** v0.26.0 -> v0.26.1 * **k8s.io/apimachinery** v0.26.0 -> v0.26.1 * **k8s.io/apiserver** v0.26.0 -> v0.26.1 * **k8s.io/client-go** v0.26.0 -> v0.26.1 * **k8s.io/component-base** v0.26.0 -> v0.26.1 * **k8s.io/klog/v2** v2.80.1 -> v2.90.0 * **k8s.io/kubectl** v0.26.0 -> v0.26.1 * **k8s.io/kubelet** v0.26.0 -> v0.26.1 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.66 -> v1.2.67 Previous release can be found at [v1.3.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0) ## [Talos 1.4.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.4.0-alpha.1) (2023-01-25) Welcome to the v1.4.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### etcd Maintenance Talos adds new APIs to make it easier to perform etcd maintenance operations. These APIs are available via new `talosctl etcd` sub-commands: * `talosctl etcd alarm list|disarm` * `talosctl etcd defrag` * `talosctl etcd status` See also [etcd maintenance guide](https://talos.dev/v1.4/advanced/etcd-maintenance/). ### Component Updates * Linux: 6.1.7 * containerd: v1.6.15 * Kubernetes: v1.26.1 * etcd: v3.5.7 Talos is built with Go 1.19.5. ### Contributors * Andrey Smirnov * Noel Georgi * Dmitriy Matrenichev * Utku Ozdemir * Serge Logvinov * Spencer Smith * Steve Francis * Cees-Jan Kiewiet * Chris van de Sande * Lance R. Vick * Matthias Riegler * Michael Vorburger * Murtaza Udaipurwala * Nico Berlee * Niklas Wik * Rowan Smith * Samuel Kees * Seán C McCord * Tim Jones * Tim van Druenen * Victor Seva ### Changes
81 commits

* [`e3da4754e`](https://github.com/siderolabs/talos/commit/e3da4754e7a2e69b998b861034c6f77e2cf6355b) feat: update Linux to 6.1.7 * [`006449e46`](https://github.com/siderolabs/talos/commit/006449e464ac009e15d78bb4d71cee80f2540f31) test: build integration test early in the pipeline * [`09aa71264`](https://github.com/siderolabs/talos/commit/09aa7126422b9b41e74c3d2aacb563daeca33bc5) fix: renovate config * [`2d136f187`](https://github.com/siderolabs/talos/commit/2d136f1879ee66dbd61ab40bb001a45c0bafaad5) feat: set markdown and html descriptions in config json schema * [`f0804027a`](https://github.com/siderolabs/talos/commit/f0804027a499a6e195f049144bff4f939dee3780) fix: renovate config * [`812a2877c`](https://github.com/siderolabs/talos/commit/812a2877cdc1e631ae0244f9696a65e2347594c0) chore: bump deps + renovate cleanup * [`aa9f66c1c`](https://github.com/siderolabs/talos/commit/aa9f66c1c88a1bb35aefe24ea0a5c3a6e7aa966d) fix: mark DigitalOcean anchor IP as scope link * [`bb4937f1b`](https://github.com/siderolabs/talos/commit/bb4937f1b339384fb486cb0cb675df8bf9b9f916) feat: enable renovate * [`3e0057162`](https://github.com/siderolabs/talos/commit/3e00571627568d8c5ab10a72e59207677a89e4cc) fix: unwrap gRPC errors on stop/remove pods check * [`00e52ae07`](https://github.com/siderolabs/talos/commit/00e52ae07867deff9a5877fcb498252bc1b1a740) fix: build correctly etcd initial cluster URL * [`ae83b10ae`](https://github.com/siderolabs/talos/commit/ae83b10ae89dbe600ddfaa338be95ea819546007) feat: create JSON schema for v1alpha1.Config * [`703d96595`](https://github.com/siderolabs/talos/commit/703d9659512d744a606e520faf230e20efddfc4a) feat: update Kubernetes to 1.26.1, etcd to 3.5.7 * [`965e64591`](https://github.com/siderolabs/talos/commit/965e645915d080487a74b35dc8f1d2e4051f0504) docs: update to use talosctl install script * [`c5954f434`](https://github.com/siderolabs/talos/commit/c5954f4345cbf3a92c777a0e7fc5d39e883609bf) chore: bump deps * [`bb50f6a56`](https://github.com/siderolabs/talos/commit/bb50f6a56d971915abb6a895aac9d7e0612a3255) chore: preallocate disk images for QEMU VMs * [`d4b8b35de`](https://github.com/siderolabs/talos/commit/d4b8b35de7849d887c41f9a13dadb59ccd8c08c4) feat: generate kernel module dependency tree * [`18122ae73`](https://github.com/siderolabs/talos/commit/18122ae73e0489a0497956c6d4621c05c6a77387) fix: service restart (including extension services) * [`680fd5e45`](https://github.com/siderolabs/talos/commit/680fd5e452e02b108b7938d0136079c16e6cfd79) fix: bump COSI runtime with the panic controller restart fix * [`0b65bbfc8`](https://github.com/siderolabs/talos/commit/0b65bbfc878fe2a5c01c5d2cd08006b53fda7cf9) fix: handle overwriting tags in syslinux ADV * [`70d9428a1`](https://github.com/siderolabs/talos/commit/70d9428a1d00d9894d68f38b255debb66fe8a440) fix: kubespan MSS clamping * [`683b4ccb4`](https://github.com/siderolabs/talos/commit/683b4ccb4faab6c3da2de00f7314773f42899c25) chore: update Go to 1.19.5 and kernel to 6.1.4 * [`062c7d754`](https://github.com/siderolabs/talos/commit/062c7d754be1714c7763b8f2b399436d64c90ea4) test: fix integration test on cp endpoint update * [`8e9fc13d7`](https://github.com/siderolabs/talos/commit/8e9fc13d7c48da5c5354501e0ad96688670438cf) feat: implement enum generator for proto files * [`771b0dc06`](https://github.com/siderolabs/talos/commit/771b0dc061e0fa33085b28bd0d0a7e4da13081f1) docs: update left over rpi_4 ref to rpi_generic * [`6c04b5f79`](https://github.com/siderolabs/talos/commit/6c04b5f79e6e01e0a3cdabfc99f12c944edd1f0a) chore: bump dependencies * [`0a5a8802e`](https://github.com/siderolabs/talos/commit/0a5a8802e7e337e1f30a40c9f566e57642c39c1a) feat: use 'localhost' endpoint for controlplane nodes * [`b0775ebf2`](https://github.com/siderolabs/talos/commit/b0775ebf2c776c7133cf74c6259de9dc9573786c) feat: add ISO wipe GRUB boot option * [`29020cb9c`](https://github.com/siderolabs/talos/commit/29020cb9c788d87a0457028ce73c8d297959116e) fix: report fatal sequence errors as reboots * [`96629d5ba`](https://github.com/siderolabs/talos/commit/96629d5ba6c1ae9d820824fb38f68112bce27f2c) feat: implement etcd maintenance commands * [`80fed3194`](https://github.com/siderolabs/talos/commit/80fed319408be9e493141fb2c01e5731708835c7) feat: include Kubernetes controlplane endpoint as one of the endpoints * [`c6cb36cc1`](https://github.com/siderolabs/talos/commit/c6cb36cc1f50b5d0e59a5284867e7534dc9f73bb) docs: fix auditpolicy example typo * [`ba8265bc5`](https://github.com/siderolabs/talos/commit/ba8265bc5ce63bcbc6fbd6c1a1076dc3f2ee6bd0) feat: new talosctl config remove to remove context * [`fcb19ff51`](https://github.com/siderolabs/talos/commit/fcb19ff516cc1200ec81f2a954bb6d2ce39ebdc6) fix: implement upgrade version checks for Talos 1.4 * [`80f150ac8`](https://github.com/siderolabs/talos/commit/80f150ac859f5dbf95060c12440afab8c0bc77a8) feat: enable ipv6 on gcp * [`8db622f3d`](https://github.com/siderolabs/talos/commit/8db622f3dc75aed90dd2d0bd92d03aa7e8aefd10) docs: add Vandebron to adopters list * [`f6a86ae90`](https://github.com/siderolabs/talos/commit/f6a86ae90607914c29875df750fe79cbbfcc5897) fix: oralce cloud zone * [`89dbb0ecf`](https://github.com/siderolabs/talos/commit/89dbb0ecf089bb746479238df274ccba4fcb049a) release(v1.4.0-alpha.0): prepare release * [`31fb90535`](https://github.com/siderolabs/talos/commit/31fb9053582190b3b536a309c30e2b78c4611885) feat: update Linux 6.1.1, containerd 1.6.14 * [`a0c0352dd`](https://github.com/siderolabs/talos/commit/a0c0352ddca253e1efb3679224b317692d46b2fd) fix: send diagnostic output to stderr consistently * [`9a5f4c08a`](https://github.com/siderolabs/talos/commit/9a5f4c08a206504a1d30277dcc0597333e5a927a) fix: default the manifest namespace if not set * [`3c6cce5fe`](https://github.com/siderolabs/talos/commit/3c6cce5fe47075f43a73682b57a7b40fa0899795) docs: update last release for Talos 1.2.x * [`703624c43`](https://github.com/siderolabs/talos/commit/703624c43dd8e58c147ccbc3989c6c436c9f3a7f) docs: fix the 1.3 release date * [`386c9293a`](https://github.com/siderolabs/talos/commit/386c9293a33e9d237fbeda0492b01b11fdadc501) docs: update nvidia-container-runtime version * [`ff83d9fd7`](https://github.com/siderolabs/talos/commit/ff83d9fd7bed2e04d5c8107713150c2513f47991) fix: improve talosctl completion * [`31ff431fa`](https://github.com/siderolabs/talos/commit/31ff431faec22c09cad88d565102e6a24785ecb4) chore: add schulz systemtechnik to the list * [`97bef7c47`](https://github.com/siderolabs/talos/commit/97bef7c47bfd133f2b3ad19efe3f30a88dd67460) docs: vsphere.sh > vmware.sh * [`34babe858`](https://github.com/siderolabs/talos/commit/34babe858d15145a1c596febb5e577473e4ffce0) chore: make organization selection an interface * [`a9643b477`](https://github.com/siderolabs/talos/commit/a9643b477417029db73aacbfcf5778cedd97cd95) fix: use proper key usage for apid client certificate * [`171aa9467`](https://github.com/siderolabs/talos/commit/171aa9467966f5869e72374961ea05abc8d9fda9) fix: disable Wireless Lan using dtoverlay * [`2e84d2ab3`](https://github.com/siderolabs/talos/commit/2e84d2ab3417515f539a70d58885dcb69e9f098c) chore: update conformance product.yaml * [`b7763843a`](https://github.com/siderolabs/talos/commit/b7763843af63bbc186f08701a62c19ea96fb7e3c) feat: add install script that improves talosctl installation user experience This install script detects the platform and architecture, and downloads the correct talosctl, and checks the gpg checksums. It also installs and chmods the binary. * [`afc45ad63`](https://github.com/siderolabs/talos/commit/afc45ad632e63cc3afc095b1f3efe6df3ecb9cb1) docs: mark Talos 1.3 docs as default * [`873bd3807`](https://github.com/siderolabs/talos/commit/873bd3807c0fcca2e212deb7fd044662557964c1) fix: redact service account key in config in RedactSecrets method * [`b3aebfadf`](https://github.com/siderolabs/talos/commit/b3aebfadfc15544e5ab448d979129dba5e516c59) feat: validate Talos API access roles in machine config * [`40761e17d`](https://github.com/siderolabs/talos/commit/40761e17db5789f30eef2f15f0b5c6396e09a9e5) docs: fork docs for Talos 1.4 * [`474604cd2`](https://github.com/siderolabs/talos/commit/474604cd279def7a6798e24ede27feef955ba5a3) docs: update documentation for Talos 1.3 * [`faf49218c`](https://github.com/siderolabs/talos/commit/faf49218ce14a48829dae7b3b8d7801188453a89) feat: add more checks for K8s upgrade * [`5b992bd86`](https://github.com/siderolabs/talos/commit/5b992bd8610f41d23d8b7dbd01f9a1be298eda96) fix: allow empty dnsDomain in machine config * [`eb332cfcb`](https://github.com/siderolabs/talos/commit/eb332cfcb785e250c422d6a7ea2b23679189a946) feat: add health check for a minimal memory / disk size * [`d04970dfa`](https://github.com/siderolabs/talos/commit/d04970dfa9d6554e1ee447fd9383bf65b8953671) fix: ignore k8s additional addresses if nil * [`63c17104c`](https://github.com/siderolabs/talos/commit/63c17104c594dfd9ca4066ba41d8a03507464874) feat: update Kubernets to 1.26.0 * [`f7a9a90db`](https://github.com/siderolabs/talos/commit/f7a9a90db2bfd316ea01551daba9becb15361f94) chore: update pkgs/tools (Go 1.19.4, containerd 1.6.11) * [`cf7adc51c`](https://github.com/siderolabs/talos/commit/cf7adc51c9f53234e469dd9f0cca06eed0230e8b) feat: add RedactSecrets method to v1alpha1.Config * [`4c31b9b1a`](https://github.com/siderolabs/talos/commit/4c31b9b1a3a00df0fe817c3edc15260ca3cadd6d) docs: clarify what the deal is with /var * [`a8ebcca4a`](https://github.com/siderolabs/talos/commit/a8ebcca4a9f63643f68d8e85bcb0b9ddb49205ed) chore: remove `watchErr` from `metal.getResource` * [`1253513bd`](https://github.com/siderolabs/talos/commit/1253513bd1deecc4cc42330bad0a713b3630240a) fix: fix nil pointer panic and incorrect error output * [`82e8c9e1f`](https://github.com/siderolabs/talos/commit/82e8c9e1f63371f41b0794b4c1be3209847c5f8b) fix: workaround panic in the kubelet service controller * [`a505b8909`](https://github.com/siderolabs/talos/commit/a505b8909a1c733b30f22a8d46eebc022475431a) fix: update COSI and reset restart backoff on success * [`e92fdcbad`](https://github.com/siderolabs/talos/commit/e92fdcbad1de595d119f78dbed3a97ae46df9bbf) chore: bump kernel to 5.15.81 * [`f0dddca2a`](https://github.com/siderolabs/talos/commit/f0dddca2a3d2e976cee543ab57816a6395fe3d65) docs: expand help for 'talosctl get' * [`fcffc8879`](https://github.com/siderolabs/talos/commit/fcffc88790b5a3006b3b85744771a7eef6e8ac5c) fix: add ext4 filesystem detection * [`5b2960eff`](https://github.com/siderolabs/talos/commit/5b2960efff8b38af85b687a25fa93f01256016de) fix: introduce 'overridePath' setting and fix Talos resolver * [`0219d1124`](https://github.com/siderolabs/talos/commit/0219d1124e5125696364bf92ecf0e8dcad644001) fix: use only kube-apiserver endpoints for Talos API access endpoints * [`dc5e0f4af`](https://github.com/siderolabs/talos/commit/dc5e0f4af087d3b662b0240b4f8fd76379ed0de2) fix: report errors to Equinix Metal event API * [`7ab140a94`](https://github.com/siderolabs/talos/commit/7ab140a94ad1a279be43669d6d70687f3a0c47de) feat: add talosctl machineconfig patch command * [`d3cf06114`](https://github.com/siderolabs/talos/commit/d3cf061149a4a502317d7728c45b6cfb4d38f89f) fix: ignore many more filesystems in IMA * [`44e2799b8`](https://github.com/siderolabs/talos/commit/44e2799b8cb928083f3a777d5cce45ad8dbf6864) feat: add stdout and single config type support to talosctl gen config * [`4452f0e17`](https://github.com/siderolabs/talos/commit/4452f0e179db16c59dc65ccdb5a496ad3306684e) docs: bump talos version * [`38e57bd12`](https://github.com/siderolabs/talos/commit/38e57bd12b8c50d668fcde6ee9aa493682778dcc) feat: update Kubernetes to v1.26.0-rc.1 * [`4cd125d49`](https://github.com/siderolabs/talos/commit/4cd125d499a24798dfde1dddf6fa1c689d16c93f) fix: correctly handle new watch event types * [`881b84152`](https://github.com/siderolabs/talos/commit/881b84152084d157fbd4ff992089a5392aadfd3c) feat: update Flannel to 0.20.2

### Changes since v1.4.0-alpha.0
36 commits

* [`e3da4754e`](https://github.com/siderolabs/talos/commit/e3da4754e7a2e69b998b861034c6f77e2cf6355b) feat: update Linux to 6.1.7 * [`006449e46`](https://github.com/siderolabs/talos/commit/006449e464ac009e15d78bb4d71cee80f2540f31) test: build integration test early in the pipeline * [`09aa71264`](https://github.com/siderolabs/talos/commit/09aa7126422b9b41e74c3d2aacb563daeca33bc5) fix: renovate config * [`2d136f187`](https://github.com/siderolabs/talos/commit/2d136f1879ee66dbd61ab40bb001a45c0bafaad5) feat: set markdown and html descriptions in config json schema * [`f0804027a`](https://github.com/siderolabs/talos/commit/f0804027a499a6e195f049144bff4f939dee3780) fix: renovate config * [`812a2877c`](https://github.com/siderolabs/talos/commit/812a2877cdc1e631ae0244f9696a65e2347594c0) chore: bump deps + renovate cleanup * [`aa9f66c1c`](https://github.com/siderolabs/talos/commit/aa9f66c1c88a1bb35aefe24ea0a5c3a6e7aa966d) fix: mark DigitalOcean anchor IP as scope link * [`bb4937f1b`](https://github.com/siderolabs/talos/commit/bb4937f1b339384fb486cb0cb675df8bf9b9f916) feat: enable renovate * [`3e0057162`](https://github.com/siderolabs/talos/commit/3e00571627568d8c5ab10a72e59207677a89e4cc) fix: unwrap gRPC errors on stop/remove pods check * [`00e52ae07`](https://github.com/siderolabs/talos/commit/00e52ae07867deff9a5877fcb498252bc1b1a740) fix: build correctly etcd initial cluster URL * [`ae83b10ae`](https://github.com/siderolabs/talos/commit/ae83b10ae89dbe600ddfaa338be95ea819546007) feat: create JSON schema for v1alpha1.Config * [`703d96595`](https://github.com/siderolabs/talos/commit/703d9659512d744a606e520faf230e20efddfc4a) feat: update Kubernetes to 1.26.1, etcd to 3.5.7 * [`965e64591`](https://github.com/siderolabs/talos/commit/965e645915d080487a74b35dc8f1d2e4051f0504) docs: update to use talosctl install script * [`c5954f434`](https://github.com/siderolabs/talos/commit/c5954f4345cbf3a92c777a0e7fc5d39e883609bf) chore: bump deps * [`bb50f6a56`](https://github.com/siderolabs/talos/commit/bb50f6a56d971915abb6a895aac9d7e0612a3255) chore: preallocate disk images for QEMU VMs * [`d4b8b35de`](https://github.com/siderolabs/talos/commit/d4b8b35de7849d887c41f9a13dadb59ccd8c08c4) feat: generate kernel module dependency tree * [`18122ae73`](https://github.com/siderolabs/talos/commit/18122ae73e0489a0497956c6d4621c05c6a77387) fix: service restart (including extension services) * [`680fd5e45`](https://github.com/siderolabs/talos/commit/680fd5e452e02b108b7938d0136079c16e6cfd79) fix: bump COSI runtime with the panic controller restart fix * [`0b65bbfc8`](https://github.com/siderolabs/talos/commit/0b65bbfc878fe2a5c01c5d2cd08006b53fda7cf9) fix: handle overwriting tags in syslinux ADV * [`70d9428a1`](https://github.com/siderolabs/talos/commit/70d9428a1d00d9894d68f38b255debb66fe8a440) fix: kubespan MSS clamping * [`683b4ccb4`](https://github.com/siderolabs/talos/commit/683b4ccb4faab6c3da2de00f7314773f42899c25) chore: update Go to 1.19.5 and kernel to 6.1.4 * [`062c7d754`](https://github.com/siderolabs/talos/commit/062c7d754be1714c7763b8f2b399436d64c90ea4) test: fix integration test on cp endpoint update * [`8e9fc13d7`](https://github.com/siderolabs/talos/commit/8e9fc13d7c48da5c5354501e0ad96688670438cf) feat: implement enum generator for proto files * [`771b0dc06`](https://github.com/siderolabs/talos/commit/771b0dc061e0fa33085b28bd0d0a7e4da13081f1) docs: update left over rpi_4 ref to rpi_generic * [`6c04b5f79`](https://github.com/siderolabs/talos/commit/6c04b5f79e6e01e0a3cdabfc99f12c944edd1f0a) chore: bump dependencies * [`0a5a8802e`](https://github.com/siderolabs/talos/commit/0a5a8802e7e337e1f30a40c9f566e57642c39c1a) feat: use 'localhost' endpoint for controlplane nodes * [`b0775ebf2`](https://github.com/siderolabs/talos/commit/b0775ebf2c776c7133cf74c6259de9dc9573786c) feat: add ISO wipe GRUB boot option * [`29020cb9c`](https://github.com/siderolabs/talos/commit/29020cb9c788d87a0457028ce73c8d297959116e) fix: report fatal sequence errors as reboots * [`96629d5ba`](https://github.com/siderolabs/talos/commit/96629d5ba6c1ae9d820824fb38f68112bce27f2c) feat: implement etcd maintenance commands * [`80fed3194`](https://github.com/siderolabs/talos/commit/80fed319408be9e493141fb2c01e5731708835c7) feat: include Kubernetes controlplane endpoint as one of the endpoints * [`c6cb36cc1`](https://github.com/siderolabs/talos/commit/c6cb36cc1f50b5d0e59a5284867e7534dc9f73bb) docs: fix auditpolicy example typo * [`ba8265bc5`](https://github.com/siderolabs/talos/commit/ba8265bc5ce63bcbc6fbd6c1a1076dc3f2ee6bd0) feat: new talosctl config remove to remove context * [`fcb19ff51`](https://github.com/siderolabs/talos/commit/fcb19ff516cc1200ec81f2a954bb6d2ce39ebdc6) fix: implement upgrade version checks for Talos 1.4 * [`80f150ac8`](https://github.com/siderolabs/talos/commit/80f150ac859f5dbf95060c12440afab8c0bc77a8) feat: enable ipv6 on gcp * [`8db622f3d`](https://github.com/siderolabs/talos/commit/8db622f3dc75aed90dd2d0bd92d03aa7e8aefd10) docs: add Vandebron to adopters list * [`f6a86ae90`](https://github.com/siderolabs/talos/commit/f6a86ae90607914c29875df750fe79cbbfcc5897) fix: oralce cloud zone

### Changes from siderolabs/extras
3 commits

* [`8cb4792`](https://github.com/siderolabs/extras/commit/8cb4792da9b9e2b2663daca747d24c3b5c973e0f) chore: update Go to 1.19.5 * [`3ca2df3`](https://github.com/siderolabs/extras/commit/3ca2df3ead2a64a5ad30c350b87bfe02bf1f49c7) chore: disable provenance in buildx * [`55d8452`](https://github.com/siderolabs/extras/commit/55d845241c8456909ab36f9b0f4e26cc2b49c256) feat: update releases

### Changes from siderolabs/gen
2 commits

* [`214c1ef`](https://github.com/siderolabs/gen/commit/214c1efe795cf426e5ebcc48cb305bfc7a16fdb8) chore: set `slice.Filter` result slice cap to len * [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods

### Changes from siderolabs/pkgs
19 commits

* [`987d24a`](https://github.com/siderolabs/pkgs/commit/987d24aeaa4fb2278954cd96e6bc6a29a4c8dd61) feat: mellanox drivers are modules * [`b82a015`](https://github.com/siderolabs/pkgs/commit/b82a015c78c407d17d23542eba6a4114f3c2c4d7) feat: mellanox oped * [`057d4f9`](https://github.com/siderolabs/pkgs/commit/057d4f96aa3ba63cc456b06a70a6b3a008cf803f) chore: bump deps * [`4ac4138`](https://github.com/siderolabs/pkgs/commit/4ac4138c6b94622646c9f32f0885496c5475d905) feat: enable nvme support for raspberrypi cm4 * [`ccb9d39`](https://github.com/siderolabs/pkgs/commit/ccb9d39dc43cf53431a0d7609839ed9c7141972d) fix: disable magic sysrq * [`d33202d`](https://github.com/siderolabs/pkgs/commit/d33202d99daa6ccf136fca54ebbadda727a43a75) chore: bump u-boot to 2023.01 * [`cb83e16`](https://github.com/siderolabs/pkgs/commit/cb83e169df4a2020994a63e5be61524461ef93e3) chore: bump dependencies * [`e561dcb`](https://github.com/siderolabs/pkgs/commit/e561dcb45beae80161faccedb0303e58d41b1ded) feat: bump Go to 1.19.5 * [`c7797c7`](https://github.com/siderolabs/pkgs/commit/c7797c77bd311449e1f116980166d8d818102f4f) feat: update Linux to 6.1.4, restore RPi support * [`5e8ebb0`](https://github.com/siderolabs/pkgs/commit/5e8ebb073d9b58555a75912cd90490af8a435c7d) feat: add AMD K10 sensor support * [`73ac37d`](https://github.com/siderolabs/pkgs/commit/73ac37d683274e60340d2767f2b8201e7f13474c) chore: disable provenance in buildx * [`8965bee`](https://github.com/siderolabs/pkgs/commit/8965bee65313539e8b6534073d06341f4fb78586) chore: use default symlinks to `/bin` in `base` * [`325c9bf`](https://github.com/siderolabs/pkgs/commit/325c9bf0f3ed2bf7603d1eaea022ea650388cf2b) feat: bump dependencies * [`165dff6`](https://github.com/siderolabs/pkgs/commit/165dff6c3cdb2d05f170c8ae0616d9224416455e) fix: patch ipmitool IANA URL * [`c542f39`](https://github.com/siderolabs/pkgs/commit/c542f398a150567d5cdffc17b4248be5416fe242) feat: add kernel support for usb setrial console * [`f564f45`](https://github.com/siderolabs/pkgs/commit/f564f45645d102b7e3a9563ac7bdb1e816156e65) chore: bump tools, containerd * [`268ea7c`](https://github.com/siderolabs/pkgs/commit/268ea7c593ff04c4e4a9ea5676b3c58d41cbff14) chore: bump deps * [`dcf3ceb`](https://github.com/siderolabs/pkgs/commit/dcf3cebf283698e010aaac5417d91a7385dc2441) feat: add nitro enclave support in kernel * [`17ea5e6`](https://github.com/siderolabs/pkgs/commit/17ea5e680b2438c59fa1773e8b58d6b749cb0d34) chore: bump kernel to 5.15.81

### Changes from siderolabs/tools
14 commits

* [`28d4a57`](https://github.com/siderolabs/tools/commit/28d4a5721ce1c57fc3f643185386d5c4b5c7e39a) chore: reduce renovate noise * [`e130fd5`](https://github.com/siderolabs/tools/commit/e130fd5b9835d8cc178ec53d5a89dfc6cc2ce7a1) chore: bump deps * [`37612fe`](https://github.com/siderolabs/tools/commit/37612feb7222b943a84f1f98d0901a204d491926) fix: revert enabling provenance * [`e0b01e3`](https://github.com/siderolabs/tools/commit/e0b01e3b7420e8b0b1e0d9077515e007a6b83b56) chore: bump deps * [`d0e6bd0`](https://github.com/siderolabs/tools/commit/d0e6bd06fcfcadc330cf30339488536961f9f70e) feat: add gnutls * [`3d34b5d`](https://github.com/siderolabs/tools/commit/3d34b5d401a67048d365e8faf2f1edf293887a97) chore: bump dependencies * [`763c1d9`](https://github.com/siderolabs/tools/commit/763c1d927822517b3d63c624302e11e8e5a49f5b) feat: update Go to 1.19.5 * [`136958f`](https://github.com/siderolabs/tools/commit/136958f9f8c8cfc439228dec31b840549bca4374) chore: disable provenance in buildx * [`e2a8692`](https://github.com/siderolabs/tools/commit/e2a869294be7e77e295ca651400f85551fb7e665) feat: update releases * [`0e48f37`](https://github.com/siderolabs/tools/commit/0e48f37496a79ce4997d15fefb6300b2324f5668) chore: bump protobuf * [`a21aa1c`](https://github.com/siderolabs/tools/commit/a21aa1c583a10d017ace8da14c6f604f86ce5709) chore: bump toolchain and mpc versions * [`1a75d0f`](https://github.com/siderolabs/tools/commit/1a75d0f6796c4abf1c9a23cfe697d3e38a9ce587) chore: bump deps * [`55bd185`](https://github.com/siderolabs/tools/commit/55bd18532667e325e8938bf0a72cab40a936eadf) feat: update Go to 1.19.4 * [`f291f46`](https://github.com/siderolabs/tools/commit/f291f46e84ec02f5d22718f7ecb476a3f815ae45) chore: bump tools

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 -> v0.2.3 * **github.com/aws/aws-sdk-go** v1.44.147 -> v1.44.184 * **github.com/containerd/containerd** v1.6.12 -> v1.6.15 * **github.com/containernetworking/plugins** v1.1.1 -> v1.2.0 * **github.com/coreos/go-semver** v0.3.0 -> v0.3.1 * **github.com/cosi-project/runtime** v0.2.0 -> v0.3.0-alpha.4 * **github.com/docker/docker** v20.10.21 -> v20.10.23 * **github.com/dustin/go-humanize** v1.0.0 -> v1.0.1 * **github.com/fatih/color** v1.13.0 -> v1.14.1 * **github.com/freddierice/go-losetup/v2** v2.0.1 **_new_** * **github.com/gdamore/tcell/v2** v2.5.3 -> v2.5.4 * **github.com/hetznercloud/hcloud-go** v1.37.0 -> v1.39.0 * **github.com/insomniacslk/dhcp** f26e6d78f622 -> de60144f33f8 * **github.com/mattn/go-isatty** v0.0.16 -> v0.0.17 * **github.com/mdlayher/ethtool** 0e16326d06d1 -> ba3b4bc2e02c * **github.com/mdlayher/genetlink** v1.3.0 -> v1.3.1 * **github.com/mdlayher/netlink** v1.7.0 -> v1.7.1 * **github.com/prometheus/procfs** v0.8.0 -> v0.9.0 * **github.com/rivo/tview** db36428c92d9 -> 892d1a2eb0da * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.10 -> v1.0.0-beta.12 * **github.com/siderolabs/extras** v1.3.0-1-g3773d71 -> v1.4.0-alpha.0-2-g8cb4792 * **github.com/siderolabs/gen** v0.4.1 -> v0.4.3 * **github.com/siderolabs/pkgs** v1.3.0-5-g6509d23 -> v1.4.0-alpha.0-18-g987d24a * **github.com/siderolabs/talos/pkg/machinery** v1.3.0 -> v1.4.0-alpha.0 * **github.com/siderolabs/tools** v1.3.0-1-g712379c -> v1.4.0-alpha.0-11-g28d4a57 * **github.com/ulikunitz/xz** v0.5.11 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.56.12 -> v0.56.14 * **github.com/vmware/govmomi** v0.29.0 -> v0.30.0 * **go.etcd.io/etcd/api/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/pkg/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/client/v3** v3.5.6 -> v3.5.7 * **go.etcd.io/etcd/etcdutl/v3** v3.5.6 -> v3.5.7 * **go.uber.org/zap** v1.23.0 -> v1.24.0 * **go4.org/netipx** 797b0c90d8ab -> 987e16ee2705 * **golang.org/x/net** v0.4.0 -> v0.5.0 * **golang.org/x/sys** v0.3.0 -> v0.4.0 * **golang.org/x/term** v0.3.0 -> v0.4.0 * **golang.org/x/time** v0.2.0 -> v0.3.0 * **google.golang.org/grpc** v1.51.0 -> v1.52.0 * **k8s.io/api** v0.26.0 -> v0.26.1 * **k8s.io/apimachinery** v0.26.0 -> v0.26.1 * **k8s.io/apiserver** v0.26.0 -> v0.26.1 * **k8s.io/client-go** v0.26.0 -> v0.26.1 * **k8s.io/component-base** v0.26.0 -> v0.26.1 * **k8s.io/klog/v2** v2.80.1 -> v2.90.0 * **k8s.io/kubectl** v0.26.0 -> v0.26.1 * **k8s.io/kubelet** v0.26.0 -> v0.26.1 Previous release can be found at [v1.3.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0) ## [Talos 1.4.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.4.0-alpha.0) (2022-12-23) Welcome to the v1.4.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates * Linux: 6.1.1 * containerd: v1.6.14 Talos is built with Go 1.19.4. ### Contributors * Andrey Smirnov * Noel Georgi * Utku Ozdemir * Dmitriy Matrenichev * Spencer Smith * Lance R. Vick * Michael Vorburger * Nico Berlee * Niklas Wik * Rowan Smith * Samuel Kees * Steve Francis * Victor Seva ### Changes
44 commits

* [`31fb90535`](https://github.com/siderolabs/talos/commit/31fb9053582190b3b536a309c30e2b78c4611885) feat: update Linux 6.1.1, containerd 1.6.14 * [`a0c0352dd`](https://github.com/siderolabs/talos/commit/a0c0352ddca253e1efb3679224b317692d46b2fd) fix: send diagnostic output to stderr consistently * [`9a5f4c08a`](https://github.com/siderolabs/talos/commit/9a5f4c08a206504a1d30277dcc0597333e5a927a) fix: default the manifest namespace if not set * [`3c6cce5fe`](https://github.com/siderolabs/talos/commit/3c6cce5fe47075f43a73682b57a7b40fa0899795) docs: update last release for Talos 1.2.x * [`703624c43`](https://github.com/siderolabs/talos/commit/703624c43dd8e58c147ccbc3989c6c436c9f3a7f) docs: fix the 1.3 release date * [`386c9293a`](https://github.com/siderolabs/talos/commit/386c9293a33e9d237fbeda0492b01b11fdadc501) docs: update nvidia-container-runtime version * [`ff83d9fd7`](https://github.com/siderolabs/talos/commit/ff83d9fd7bed2e04d5c8107713150c2513f47991) fix: improve talosctl completion * [`31ff431fa`](https://github.com/siderolabs/talos/commit/31ff431faec22c09cad88d565102e6a24785ecb4) chore: add schulz systemtechnik to the list * [`97bef7c47`](https://github.com/siderolabs/talos/commit/97bef7c47bfd133f2b3ad19efe3f30a88dd67460) docs: vsphere.sh > vmware.sh * [`34babe858`](https://github.com/siderolabs/talos/commit/34babe858d15145a1c596febb5e577473e4ffce0) chore: make organization selection an interface * [`a9643b477`](https://github.com/siderolabs/talos/commit/a9643b477417029db73aacbfcf5778cedd97cd95) fix: use proper key usage for apid client certificate * [`171aa9467`](https://github.com/siderolabs/talos/commit/171aa9467966f5869e72374961ea05abc8d9fda9) fix: disable Wireless Lan using dtoverlay * [`2e84d2ab3`](https://github.com/siderolabs/talos/commit/2e84d2ab3417515f539a70d58885dcb69e9f098c) chore: update conformance product.yaml * [`b7763843a`](https://github.com/siderolabs/talos/commit/b7763843af63bbc186f08701a62c19ea96fb7e3c) feat: add install script that improves talosctl installation user experience This install script detects the platform and architecture, and downloads the correct talosctl, and checks the gpg checksums. It also installs and chmods the binary. * [`afc45ad63`](https://github.com/siderolabs/talos/commit/afc45ad632e63cc3afc095b1f3efe6df3ecb9cb1) docs: mark Talos 1.3 docs as default * [`873bd3807`](https://github.com/siderolabs/talos/commit/873bd3807c0fcca2e212deb7fd044662557964c1) fix: redact service account key in config in RedactSecrets method * [`b3aebfadf`](https://github.com/siderolabs/talos/commit/b3aebfadfc15544e5ab448d979129dba5e516c59) feat: validate Talos API access roles in machine config * [`40761e17d`](https://github.com/siderolabs/talos/commit/40761e17db5789f30eef2f15f0b5c6396e09a9e5) docs: fork docs for Talos 1.4 * [`474604cd2`](https://github.com/siderolabs/talos/commit/474604cd279def7a6798e24ede27feef955ba5a3) docs: update documentation for Talos 1.3 * [`faf49218c`](https://github.com/siderolabs/talos/commit/faf49218ce14a48829dae7b3b8d7801188453a89) feat: add more checks for K8s upgrade * [`5b992bd86`](https://github.com/siderolabs/talos/commit/5b992bd8610f41d23d8b7dbd01f9a1be298eda96) fix: allow empty dnsDomain in machine config * [`eb332cfcb`](https://github.com/siderolabs/talos/commit/eb332cfcb785e250c422d6a7ea2b23679189a946) feat: add health check for a minimal memory / disk size * [`d04970dfa`](https://github.com/siderolabs/talos/commit/d04970dfa9d6554e1ee447fd9383bf65b8953671) fix: ignore k8s additional addresses if nil * [`63c17104c`](https://github.com/siderolabs/talos/commit/63c17104c594dfd9ca4066ba41d8a03507464874) feat: update Kubernets to 1.26.0 * [`f7a9a90db`](https://github.com/siderolabs/talos/commit/f7a9a90db2bfd316ea01551daba9becb15361f94) chore: update pkgs/tools (Go 1.19.4, containerd 1.6.11) * [`cf7adc51c`](https://github.com/siderolabs/talos/commit/cf7adc51c9f53234e469dd9f0cca06eed0230e8b) feat: add RedactSecrets method to v1alpha1.Config * [`4c31b9b1a`](https://github.com/siderolabs/talos/commit/4c31b9b1a3a00df0fe817c3edc15260ca3cadd6d) docs: clarify what the deal is with /var * [`a8ebcca4a`](https://github.com/siderolabs/talos/commit/a8ebcca4a9f63643f68d8e85bcb0b9ddb49205ed) chore: remove `watchErr` from `metal.getResource` * [`1253513bd`](https://github.com/siderolabs/talos/commit/1253513bd1deecc4cc42330bad0a713b3630240a) fix: fix nil pointer panic and incorrect error output * [`82e8c9e1f`](https://github.com/siderolabs/talos/commit/82e8c9e1f63371f41b0794b4c1be3209847c5f8b) fix: workaround panic in the kubelet service controller * [`a505b8909`](https://github.com/siderolabs/talos/commit/a505b8909a1c733b30f22a8d46eebc022475431a) fix: update COSI and reset restart backoff on success * [`e92fdcbad`](https://github.com/siderolabs/talos/commit/e92fdcbad1de595d119f78dbed3a97ae46df9bbf) chore: bump kernel to 5.15.81 * [`f0dddca2a`](https://github.com/siderolabs/talos/commit/f0dddca2a3d2e976cee543ab57816a6395fe3d65) docs: expand help for 'talosctl get' * [`fcffc8879`](https://github.com/siderolabs/talos/commit/fcffc88790b5a3006b3b85744771a7eef6e8ac5c) fix: add ext4 filesystem detection * [`5b2960eff`](https://github.com/siderolabs/talos/commit/5b2960efff8b38af85b687a25fa93f01256016de) fix: introduce 'overridePath' setting and fix Talos resolver * [`0219d1124`](https://github.com/siderolabs/talos/commit/0219d1124e5125696364bf92ecf0e8dcad644001) fix: use only kube-apiserver endpoints for Talos API access endpoints * [`dc5e0f4af`](https://github.com/siderolabs/talos/commit/dc5e0f4af087d3b662b0240b4f8fd76379ed0de2) fix: report errors to Equinix Metal event API * [`7ab140a94`](https://github.com/siderolabs/talos/commit/7ab140a94ad1a279be43669d6d70687f3a0c47de) feat: add talosctl machineconfig patch command * [`d3cf06114`](https://github.com/siderolabs/talos/commit/d3cf061149a4a502317d7728c45b6cfb4d38f89f) fix: ignore many more filesystems in IMA * [`44e2799b8`](https://github.com/siderolabs/talos/commit/44e2799b8cb928083f3a777d5cce45ad8dbf6864) feat: add stdout and single config type support to talosctl gen config * [`4452f0e17`](https://github.com/siderolabs/talos/commit/4452f0e179db16c59dc65ccdb5a496ad3306684e) docs: bump talos version * [`38e57bd12`](https://github.com/siderolabs/talos/commit/38e57bd12b8c50d668fcde6ee9aa493682778dcc) feat: update Kubernetes to v1.26.0-rc.1 * [`4cd125d49`](https://github.com/siderolabs/talos/commit/4cd125d499a24798dfde1dddf6fa1c689d16c93f) fix: correctly handle new watch event types * [`881b84152`](https://github.com/siderolabs/talos/commit/881b84152084d157fbd4ff992089a5392aadfd3c) feat: update Flannel to 0.20.2

### Changes from siderolabs/extras
1 commit

* [`55d8452`](https://github.com/siderolabs/extras/commit/55d845241c8456909ab36f9b0f4e26cc2b49c256) feat: update releases

### Changes from siderolabs/gen
1 commit

* [`8e89b1e`](https://github.com/siderolabs/gen/commit/8e89b1ede9f35ff4c18a41ee44a69259181c892b) feat: add GetOrCreate and GetOrCall methods

### Changes from siderolabs/pkgs
7 commits

* [`325c9bf`](https://github.com/siderolabs/pkgs/commit/325c9bf0f3ed2bf7603d1eaea022ea650388cf2b) feat: bump dependencies * [`165dff6`](https://github.com/siderolabs/pkgs/commit/165dff6c3cdb2d05f170c8ae0616d9224416455e) fix: patch ipmitool IANA URL * [`c542f39`](https://github.com/siderolabs/pkgs/commit/c542f398a150567d5cdffc17b4248be5416fe242) feat: add kernel support for usb setrial console * [`f564f45`](https://github.com/siderolabs/pkgs/commit/f564f45645d102b7e3a9563ac7bdb1e816156e65) chore: bump tools, containerd * [`268ea7c`](https://github.com/siderolabs/pkgs/commit/268ea7c593ff04c4e4a9ea5676b3c58d41cbff14) chore: bump deps * [`dcf3ceb`](https://github.com/siderolabs/pkgs/commit/dcf3cebf283698e010aaac5417d91a7385dc2441) feat: add nitro enclave support in kernel * [`17ea5e6`](https://github.com/siderolabs/pkgs/commit/17ea5e680b2438c59fa1773e8b58d6b749cb0d34) chore: bump kernel to 5.15.81

### Changes from siderolabs/tools
6 commits

* [`e2a8692`](https://github.com/siderolabs/tools/commit/e2a869294be7e77e295ca651400f85551fb7e665) feat: update releases * [`0e48f37`](https://github.com/siderolabs/tools/commit/0e48f37496a79ce4997d15fefb6300b2324f5668) chore: bump protobuf * [`a21aa1c`](https://github.com/siderolabs/tools/commit/a21aa1c583a10d017ace8da14c6f604f86ce5709) chore: bump toolchain and mpc versions * [`1a75d0f`](https://github.com/siderolabs/tools/commit/1a75d0f6796c4abf1c9a23cfe697d3e38a9ce587) chore: bump deps * [`55bd185`](https://github.com/siderolabs/tools/commit/55bd18532667e325e8938bf0a72cab40a936eadf) feat: update Go to 1.19.4 * [`f291f46`](https://github.com/siderolabs/tools/commit/f291f46e84ec02f5d22718f7ecb476a3f815ae45) chore: bump tools

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 -> v0.2.3 * **github.com/aws/aws-sdk-go** v1.44.147 -> v1.44.166 * **github.com/containerd/containerd** v1.6.12 -> v1.6.14 * **github.com/cosi-project/runtime** v0.2.0 -> v0.3.0-alpha.2 * **github.com/docker/docker** v20.10.21 -> v20.10.22 * **github.com/hetznercloud/hcloud-go** v1.37.0 -> v1.38.0 * **github.com/insomniacslk/dhcp** f26e6d78f622 -> de60144f33f8 * **github.com/mdlayher/ethtool** 0e16326d06d1 -> ba3b4bc2e02c * **github.com/mdlayher/genetlink** v1.3.0 -> v1.3.1 * **github.com/mdlayher/netlink** v1.7.0 -> v1.7.1 * **github.com/prometheus/procfs** v0.8.0 -> v0.9.0 * **github.com/rivo/tview** db36428c92d9 -> 02e38ea9604c * **github.com/siderolabs/extras** v1.3.0-1-g3773d71 -> v1.4.0-alpha.0 * **github.com/siderolabs/gen** v0.4.1 -> v0.4.2 * **github.com/siderolabs/pkgs** v1.3.0-5-g6509d23 -> v1.4.0-alpha.0-6-g325c9bf * **github.com/siderolabs/talos/pkg/machinery** v1.3.0 -> v1.3.0-alpha.2 * **github.com/siderolabs/tools** v1.3.0-1-g712379c -> v1.4.0-alpha.0-3-ge2a8692 * **github.com/vmware-tanzu/sonobuoy** v0.56.12 -> v0.56.14 * **github.com/vmware/govmomi** v0.29.0 -> v0.30.0 * **go.uber.org/zap** v1.23.0 -> v1.24.0 * **golang.org/x/time** v0.2.0 -> v0.3.0 Previous release can be found at [v1.3.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0) ## [Talos 1.3.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.3.0-alpha.2) (2022-11-16) Welcome to the v1.3.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### kube-apiserver Audit Policy Talos now supports setting custom audit policy for `kube-apiserver` in the machine configuration. ### cgroups v1 Talos defaults to using cgroups v2 when Talos doesn't run in a container (when running in a container Talos follows host cgroups mode). Talos can now be forced to use cgroups v1 by setting boot kernel argument `talos.unified_cgroup_hierarchy=0`: ```yaml machine: install: extraKernelArgs: - "talos.unified_cgroup_hierarchy=0" ``` Current cgroups mode can be checked with `talosctl ls /sys/fs/cgroup`: cgroups v1: ``` blkio cpu cpuacct cpuset devices freezer hugetlb memory net_cls net_prio perf_event pids ``` cgroups v2: ``` cgroup.controllers cgroup.max.depth cgroup.max.descendants cgroup.procs cgroup.stat cgroup.subtree_control cgroup.threads cpu.stat cpuset.cpus.effective cpuset.mems.effective init io.stat kubepods memory.numa_stat memory.stat podruntime system ``` > Note: `cgroupsv1` is deprecated and it should be used only for compatibility with workloads which don't support `cgroupsv2` yet. ### Kernel Command Line ip= Argument Talos now supports referencing interface name via `enxMAC` address notation: ``` ip=172.20.0.2::172.20.0.1:255.255.255.0::enx7085c2dfbc59 ``` ### CRI Configuration Overrides Talos no longer supports CRI config overrides placed in `/var/cri/conf.d` directory. [New way](https://www.talos.dev/v1.3/talos-guides/configuration/containerd/) correctly handles merging of containerd/CRI plugin configuration. ### etcd Consistency Check Talos enables [--experimental-compact-hash-check-enabled](https://github.com/etcd-io/etcd/pull/14120) option by default to improve etcd store consistency guarantees. This options is only available with etcd >= v3.5.5, so Talos doesn't support version of etcd before v3.5.5. ### etcd Member ID Talos now internally handles etcd member removal by member ID instead of member name (hostname). This resolves the case when member name is not accurate or empty (eg: when etcd hasn't fully joined yet). Command `talosctl etcd remove-member` now accepts member IDs instead of member names. New resource can be used to get member ID of the Talos node: ```bash talosctl get etcdmember ``` ### Exocale Platform Talos now supports new platform: Exoscale. Exoscale provides a firewall, TCP load balancer and autoscale groups. It works well with CCM and Kubernetes node autoscaler. ### Kernel Modules Talos now supports settings kernel module parameters. Eg: ```yaml machine: kernel: modules: - name: "br_netfilter" parameters: - nf_conntrack_max=131072 ``` ### KubeSpan KubeSpan MTU link size is now configurable via `network.kubespan.mtu` setting in the machine configuration. ### Node Labels Talos now supports specifying node labels in the machine configuration: ```yaml machine: nodeLabels: rack: rack1a zone: us-east-1a ``` Changes to the node labels will be applied immediately without `kubelet` restart. Talos keeps track of the owned node labels in the `talos.dev/owned-labels` annotation. ### Routes Talos now supports setting MTU for a specific route. ### Nano Pi R4S Talos now supports the Nano Pi R4S SBC. ### Raspberry Generic Images The Raspberry Pi 4 specific image has been deprecated and will be removed in the v1.4 release of Talos. Talos now ships a generic Raspberry Pi image that should support more Raspberry Pi variants. Refer to the docs at https://www.talos.dev/v1.3/talos-guides/install/single-board-computers/rpi_generic/ to find which ones are supported. ### Encryption with secretbox By default new clusters will use secretbox for encryption instead of AESCBC. If both are configured secretbox will take precedence. Old clusters may keep using AESCBC. To enable secretbox you may add an encryption secret at `cluster.secretboxEncryptionSecret`. You should keep `aescbcEncryptionSecret` however, even if secretbox is enabled older data will still be encrypted with AESCBC. How to generate the secret: ```bash dd if=/dev/random of=/dev/stdout bs=32 count=1 | base64 ``` ### Static Pod Manifests The directory "/etc/kubernetes/manifests" is now deprecated. Static pods should always be configured in machine.pods. To reenable support you may set `machine.kubelet.disableManifestsDirectory`. Eg: ```yaml machine: kubelet: disableManifestsDirectory: no ``` ### Component Updates * Kubernetes: v1.26.0-rc.0 * Flannel: v0.20.1 * CoreDNS: v1.10.0 * etcd: v3.5.5 * Linux: 5.15.77 * containerd: v1.6.9 Talos is built with Go 1.19.3. ### Contributors * Andrey Smirnov * Noel Georgi * Andrey Smirnov * Michal Witkowski * Artem Chernyshev * Artem Chernyshev * Dmitriy Matrenichev * Alexey Palazhchenko * Serge Logvinov * Andrey Smirnov * Philipp Sauter * Andrew Rynhard * Steve Francis * Utku Ozdemir * Andrew Rynhard * Tim Jones * Seán C McCord * Kris Reeves * Marvin Drees * Spencer Smith * Branden Cash * Brandon Nason * Cameron Brunner * DJAlPee * Daniel Low * Gerard de Leeuw * Jack Wink * Jon Stelly * Martin Stone * Matt Zahorik * Maxim Makarov * Olli Janatuinen * Pau Campana * Rubens Farias * Sander Maijers * Spencer Smith * ankitm123 * emattiza * killcity ### Changes
173 commits

* [`aa56aed79`](https://github.com/siderolabs/talos/commit/aa56aed7981b4bdb29a5f668296597cf4a5c1d3b) feat: publish discovered public IP as one of the KubeSpan endpoint * [`9382443ba`](https://github.com/siderolabs/talos/commit/9382443baa8005f84aa177c6dbeb68b995d09084) feat: update Kubernetes to v1.26.0-rc.0 * [`6ffc381c5`](https://github.com/siderolabs/talos/commit/6ffc381c59b919a3d922a99f896f601cf4f6898a) feat: implement CRI configuration customization * [`e1e340bdd`](https://github.com/siderolabs/talos/commit/e1e340bdd910dc1f9c7fd08f71fb14352e96dbbf) feat: expose Talos node labels as a machine configuration field * [`c78bbbfda`](https://github.com/siderolabs/talos/commit/c78bbbfda3d545c3f50e0ad141369279dd2f4ca0) docs: specify that only XFS partitions are detected * [`b881a9a79`](https://github.com/siderolabs/talos/commit/b881a9a795937ba0c5d94ee4104209652bb992ab) chore: bump dependencies * [`5bfd7dbfa`](https://github.com/siderolabs/talos/commit/5bfd7dbfa75c2d9b4ec4d6a61b2da91e72113a71) test: fix assertion on reboot test * [`1cfb6188b`](https://github.com/siderolabs/talos/commit/1cfb6188bcc2aefaa0b4f707f16053a8a1fd9a28) feat: implement support for cgroupsv1 * [`3866d0e33`](https://github.com/siderolabs/talos/commit/3866d0e334cd4d8146cdc8d17196d7f7aa4877a5) feat: update Kubernetes to v1.26.0-beta.0 * [`e1590ba7b`](https://github.com/siderolabs/talos/commit/e1590ba7b9f0c1b0be9b78a1dd4381f2d2e90e57) fix: lifecycle action tracking * [`804762c59`](https://github.com/siderolabs/talos/commit/804762c597f3aebdc3521cefc93bbbf0ff4a57eb) feat: add timeout to cli action tracking, track by default & refactor * [`4e114ca12`](https://github.com/siderolabs/talos/commit/4e114ca120a1ee7de5a5822dba14c732f1dc7610) feat: use the etcd member id for etcd operations instead of hostname * [`06fea2441`](https://github.com/siderolabs/talos/commit/06fea244140e82fd30a4ac4c5e4433253bd930ab) feat: expand platform metadata resources * [`03a20da9d`](https://github.com/siderolabs/talos/commit/03a20da9da5916c63015d355f4b56823778e994f) fix: filter up duplicate IPs out of NodeAddresses * [`6b771bc73`](https://github.com/siderolabs/talos/commit/6b771bc73984d755dce06bd5452131add5ecf487) chore: bump deps * [`96aa9638f`](https://github.com/siderolabs/talos/commit/96aa9638f724d81ba8ef64db0ed7032694e7da5d) chore: rename talos-systems/talos to siderolabs/talos * [`30bbf6463`](https://github.com/siderolabs/talos/commit/30bbf6463a85c10b4273633e928b6c419731e31d) refactor: use siderolabs/net version with netip.Addr * [`343c55762`](https://github.com/siderolabs/talos/commit/343c55762e4af279ceeb0066540124ef66c77602) chore: replace talos-systems Go modules with siderolabs * [`0301bbe93`](https://github.com/siderolabs/talos/commit/0301bbe9369eb2811aa9d5177fcc024606c71ed4) fix: check if processes is nil to avoid panic * [`08e7e49a2`](https://github.com/siderolabs/talos/commit/08e7e49a29018ed1932709779349a8c056125090) test: update versions for upgrade tests * [`0b41923c3`](https://github.com/siderolabs/talos/commit/0b41923c3608a815d14bccd84a6abcf1bc99db43) fix: restore the StaticPodStatus resource * [`1947092ae`](https://github.com/siderolabs/talos/commit/1947092ae225abe408c1b2c20633af671ae9c2a2) chore: introduce a healthcheck for `machined` service * [`3333cd93c`](https://github.com/siderolabs/talos/commit/3333cd93c821b00bd523584509075d21e2ec16be) fix: generate correct Flannel config for IPv6-only clusters * [`d7070f5e7`](https://github.com/siderolabs/talos/commit/d7070f5e7498f56e644e07402ed30933047b6f8e) release(v1.3.0-alpha.1): prepare release * [`869f3b5a5`](https://github.com/siderolabs/talos/commit/869f3b5a51ac783e8b0a5a31a103c212a068672b) feat: network configuration improvements on the OpenStack platform * [`29f2195e1`](https://github.com/siderolabs/talos/commit/29f2195e130ecf66a911d0c75343486ee7c86046) feat: support exoscale cloud * [`8b4ae08d1`](https://github.com/siderolabs/talos/commit/8b4ae08d1c42a2cab4bbf0daac090e0882b5d4e9) fix: etcd snapshot command on Windows * [`8bfa7ac1d`](https://github.com/siderolabs/talos/commit/8bfa7ac1d6012746bf7264528eac5cacdd752e2b) feat: platform metadata resource * [`7e50e24c0`](https://github.com/siderolabs/talos/commit/7e50e24c0187e514876222857d44eedda79acc5a) fix: properly cleanup legacy static pod manifests directory * [`6ee47bcc6`](https://github.com/siderolabs/talos/commit/6ee47bcc61bd5b8684c43c0d8c020c574631c832) fix: support serving config for qemu launcher on IPv6 * [`6c3d11b49`](https://github.com/siderolabs/talos/commit/6c3d11b49e94b33ccfdf29f93d3233e480b5e7f0) docs: admission control patch note * [`4ea3b99b5`](https://github.com/siderolabs/talos/commit/4ea3b99b527406b0bbf9cbfd22867431b143ed49) fix: serve static pod files on 127.0.0.1 instead of localhost * [`23842114f`](https://github.com/siderolabs/talos/commit/23842114f077d98cf7bdbf8912454623dff41bbb) feat: support encryption with secretbox * [`f6773c472`](https://github.com/siderolabs/talos/commit/f6773c472c0c1094045a26e34be2472a98dad510) docs: talos support on equinix metal * [`b307160f6`](https://github.com/siderolabs/talos/commit/b307160f613f2544c70be115c9a1ae0a7439ec52) chore: bump dependencies * [`d7edd0e2e`](https://github.com/siderolabs/talos/commit/d7edd0e2e6ec5e4cba8bfa119d244c7be09078d9) refactor: use go-circular, go-kubeconfig, and go-tail * [`c6e1702ec`](https://github.com/siderolabs/talos/commit/c6e1702eca2d310f6fad52e0f00bc91d7d6c4996) feat: use URL-based manifests to present static pods to the kubelet * [`136a795e5`](https://github.com/siderolabs/talos/commit/136a795e55b5be5f093aaf6b07039e86df971674) docs: update system requirements to mention dedicated disk usage * [`879e8c0bf`](https://github.com/siderolabs/talos/commit/879e8c0bfe31f6b35f8833cf55624cd934ded50b) chore: update kernel with BTF support * [`ceb0cd99a`](https://github.com/siderolabs/talos/commit/ceb0cd99ae0e29cadf69e121afdc439f3296ff74) feat: implement Talos API auth using SideroV1 signatures * [`e6fba7d3b`](https://github.com/siderolabs/talos/commit/e6fba7d3bc83d008518d7a032b309ddd212e0f81) chore: update dependencies * [`93e55b85f`](https://github.com/siderolabs/talos/commit/93e55b85f207060d053ba9f16267d98c2599a2df) chore: bump golangci-lint to v1.50.0 * [`aa3d9b4ca`](https://github.com/siderolabs/talos/commit/aa3d9b4ca60f0a7e47867e1de134753eb914606b) fix: regenerate cert on node labeling retry * [`021c73c35`](https://github.com/siderolabs/talos/commit/021c73c35233ee5e6cb9cf5e83336eeb70ae05d3) fix: lowercase nodename * [`b902036e1`](https://github.com/siderolabs/talos/commit/b902036e12843d6348d945097d3826a50b040b25) docs: update office hours time link * [`7fcb8c681`](https://github.com/siderolabs/talos/commit/7fcb8c68164d72f14bca284daffc69605002acb5) feat: update Flannel to v0.20.0 * [`dc70d892a`](https://github.com/siderolabs/talos/commit/dc70d892a341f0694be0c0ff5517b63ea6bbadd9) fix: support setting KubeSpan link MTU * [`7d52bad37`](https://github.com/siderolabs/talos/commit/7d52bad370d544d1a2862891e089426dff7c52a3) feat: update Linux to 5.15.73 * [`9c78b3aff`](https://github.com/siderolabs/talos/commit/9c78b3aff48fd95f48ab2c951f7eb61273338e9a) feat: update Kubernetes to v1.26.0-alpha.2 * [`94913a672`](https://github.com/siderolabs/talos/commit/94913a6727e9a802d2e14c141a831a8fddc8d9b2) docs: add lofty to talos adopters * [`0a0bdfe16`](https://github.com/siderolabs/talos/commit/0a0bdfe164625013e807cf5a08f590835894bf92) docs: add Tremor Video to adopters * [`b7b1d4fd6`](https://github.com/siderolabs/talos/commit/b7b1d4fd6a492c8e4c73b9f7f17449241903f868) feat: use readonly containers * [`d210338e3`](https://github.com/siderolabs/talos/commit/d210338e33438919fc8d2d83fc479981077d5164) fix: skip protobuf full unmarshaling for some talosctl commands * [`b3c679d18`](https://github.com/siderolabs/talos/commit/b3c679d18e698092795725e6fcb05d6569d681b1) chore: bump dependencies * [`993743f63`](https://github.com/siderolabs/talos/commit/993743f63495a59020670619abde5a0d5cd322e2) fix: skip hostname via DHCP on OpenStack platform * [`db076e7b5`](https://github.com/siderolabs/talos/commit/db076e7b5afca7c725c4c6876a7e05d643a219a1) feat: pin interface by mac address in cmdline args * [`63de93722`](https://github.com/siderolabs/talos/commit/63de937227362064a05fa3a9ba11f55891458cc7) fix: update go-smbios to v0.3.1 * [`49e9f808e`](https://github.com/siderolabs/talos/commit/49e9f808e7b14af90959c7fca9457128e82f9cb5) chore: bump kernel and go * [`c7372144d`](https://github.com/siderolabs/talos/commit/c7372144de4b953ebe2494676143ea6d0e53e666) docs: add constraints to upgrade docs * [`c71c8ca18`](https://github.com/siderolabs/talos/commit/c71c8ca18fd4bb7dcae2f69ea253c16b9abd7a9d) docs: consolidate, simplify and correct various docs * [`06f76bfeb`](https://github.com/siderolabs/talos/commit/06f76bfebb14e7d826b8c7efe4564a94d841a74a) chore: bump dependencies * [`b1c421b9a`](https://github.com/siderolabs/talos/commit/b1c421b9ad90d36e8a3562aacdcc30c521da585a) chore: publish ami's with imds v2 enabled * [`195c40ab5`](https://github.com/siderolabs/talos/commit/195c40ab5908c3bcd0c8ecf5b6f7275bb9b7a499) docs: add information about applicable use cases of disk encryption * [`54a687fb8`](https://github.com/siderolabs/talos/commit/54a687fb8e68f3669ff140d37ff3fd01595a494d) docs: consolidate and expand on discovery service * [`139c62d76`](https://github.com/siderolabs/talos/commit/139c62d762c2a9001808d4e1bed38145ea86a95d) feat: allow upgrades in maintenance mode (only over SideroLink) * [`48dee4805`](https://github.com/siderolabs/talos/commit/48dee480577c9d1bb4620f78c6b4bbeba0f0d0bc) feat: support mtu for routes * [`1c43c72ae`](https://github.com/siderolabs/talos/commit/1c43c72aebd1a2bcc1991787dcd94c8bab00df42) docs: fix talos required kernel params * [`67cc45ae3`](https://github.com/siderolabs/talos/commit/67cc45ae3f9351cf5ae27c2c1a4c5d762a2d8b77) release(v1.3.0-alpha.0): prepare release * [`18c377a4d`](https://github.com/siderolabs/talos/commit/18c377a4d1ce046b310e3609033e9c1f39f9337b) feat: customize audit policy * [`23c9ea46b`](https://github.com/siderolabs/talos/commit/23c9ea46bba20d8b7cc336bbc64e04af46cccf5d) fix: raspberry pi install * [`f17cdee16`](https://github.com/siderolabs/talos/commit/f17cdee167cfd6d673e2ed71fd5c8d28399a80f3) feat: jsonpath filter for talosctl get outputs * [`6bd3cca1a`](https://github.com/siderolabs/talos/commit/6bd3cca1a8d206fb40199a9f0352aa2670fca754) chore: generic raspberry pi images * [`d914ab8bb`](https://github.com/siderolabs/talos/commit/d914ab8bb4a34cdb5ffc396a20a32a437c5989e1) chore: add vulncheck tool as a linter * [`a0151aa13`](https://github.com/siderolabs/talos/commit/a0151aa13e63b24aba7e39082f6cef3dac923a22) feat: add generic rpi u-boot support * [`30f851d09`](https://github.com/siderolabs/talos/commit/30f851d0931f5d6767e13142876c94dac67ec38b) chore: bump dependences * [`8b2235c3b`](https://github.com/siderolabs/talos/commit/8b2235c3b6de64abb15bf77e9648bf6bebc18e1f) fix: lookup Equinix Metal bond slaves using 'permanent addr' * [`b3257ebb1`](https://github.com/siderolabs/talos/commit/b3257ebb1c529a8f266ba3852d5e4191e0261a79) chore: bump kernel to 5.15.70 * [`0b2767c16`](https://github.com/siderolabs/talos/commit/0b2767c1646e84ce147030692f3904b9feb02b3e) feat: implement 'permanent addr' in link statuses * [`c90e20251`](https://github.com/siderolabs/talos/commit/c90e20251d09a9bedcbd8b1a2055de5e126fc97e) fix: kubeconfig permission * [`fc48849d0`](https://github.com/siderolabs/talos/commit/fc48849d00c185442fb37c72e2c20462cc573a69) chore: move maps/slices/ordered to gen module * [`8b09bd4b0`](https://github.com/siderolabs/talos/commit/8b09bd4b0400f17ef543f0d117ae35e4ba2356cb) feat: update Kubernetes to v1.26.0-alpha.1 * [`276d4175b`](https://github.com/siderolabs/talos/commit/276d4175bbd168d12409a1e96b191abdf09f2ff0) chore: bump extension versions in testing * [`357b770cb`](https://github.com/siderolabs/talos/commit/357b770cb593196fccaf9b6ba3cd740463351a07) fix: cryptsetup delete slot * [`711128839`](https://github.com/siderolabs/talos/commit/7111288393ae4dfdfa7331e39df1803724bc93c0) fix: continue applying bootstrap manifests on some errors * [`ce12c7b38`](https://github.com/siderolabs/talos/commit/ce12c7b3805da65315309a465aeed1764f0ce20a) chore: update COSI runtime to v0.2.0-alpha.1 * [`1b435c0b3`](https://github.com/siderolabs/talos/commit/1b435c0b36a8d0d3e48c5a5e6121117933deeb69) chore: bump kernel + ice drivers * [`18e041f1e`](https://github.com/siderolabs/talos/commit/18e041f1ecb88d0b1e8e874d9b1fb580bc7c2297) docs: fix typo in patching example * [`0ad6452ca`](https://github.com/siderolabs/talos/commit/0ad6452ca152afef2f3c0e97a2255a237b30941a) feat: update CoreDNS to v1.10.0 * [`479f3f52e`](https://github.com/siderolabs/talos/commit/479f3f52ee7149ff2a39bec3d8f78b59978af70a) chore: bump dependencies * [`e07c6ae99`](https://github.com/siderolabs/talos/commit/e07c6ae99ec347735cf0316294ef0c54ebc45234) feat: update Kubernetes to v1.25.1 * [`13fdfaffc`](https://github.com/siderolabs/talos/commit/13fdfaffc4a0eb812cd63c5d188efd4aff6da51c) test: fix up default branch name * [`ef181321a`](https://github.com/siderolabs/talos/commit/ef181321a5be4d03e4f87aab1483b95a8e61f0fe) docs: add component diagram; K8s & Talos Linux * [`aade73643`](https://github.com/siderolabs/talos/commit/aade7364357da6644e8b70ad1dd939130f2fe470) docs: fix missing variable in OpenEBS docs * [`472590aa8`](https://github.com/siderolabs/talos/commit/472590aa82d16e1bd3825ecc8106886e7e1b9053) chore: return InvalidArgument on invalid config in maintenance mode * [`e5cabd42c`](https://github.com/siderolabs/talos/commit/e5cabd42cc7f86bee5486f73fa4068382bf6a7fb) feat: enable etcd consistency hashcheck * [`015535d90`](https://github.com/siderolabs/talos/commit/015535d9051dea243f439b385577d17fd57a122e) fix: update discovery client with the redirect fix * [`d0c8e7699`](https://github.com/siderolabs/talos/commit/d0c8e7699cf3e2415c5712ff9ff620c38857a0dc) chore: bump kernel and go * [`985b0c2e7`](https://github.com/siderolabs/talos/commit/985b0c2e796006f401376ebf30a1ce888d90a1c9) chore: remove go.work.sum * [`69124f102`](https://github.com/siderolabs/talos/commit/69124f10263bdabc556b58b98a3e1f129b85b8ab) feat: update etcd to v3.5.5 * [`1985a796c`](https://github.com/siderolabs/talos/commit/1985a796c0d5a984c397754445b33827f5690806) docs: update docs for pod security * [`94b088f02`](https://github.com/siderolabs/talos/commit/94b088f02f8f8e5b63f0c38e8e091f2ba3329dde) fix: set etcd options consistently * [`92ae7ef4b`](https://github.com/siderolabs/talos/commit/92ae7ef4b1abe0a510fea31e0fde2566281f38b1) fix: fix protoenc encoding for enums and types with custom encoders * [`93809017c`](https://github.com/siderolabs/talos/commit/93809017c594b1faf1405932d884852eb0ce567c) docs: cpu scaling governor knowledgebase * [`7b270ff33`](https://github.com/siderolabs/talos/commit/7b270ff33d6bf74d1fa195c07f98233098b337e9) test: fix api controller test * [`2dadcd669`](https://github.com/siderolabs/talos/commit/2dadcd6695003eb940848583caa6ade53ef94fa0) fix: stop worker nodes from acting as apid routers * [`9eaf33f3f`](https://github.com/siderolabs/talos/commit/9eaf33f3f274e746ca1b442c0a1a0dae0cec088f) fix: never sign client certificate requests in trustd * [`436749124`](https://github.com/siderolabs/talos/commit/43674912479d3fb58c30e350fea9c4daf4ba45d4) feat: environment vars for extension service * [`0c0cb671e`](https://github.com/siderolabs/talos/commit/0c0cb671ead1f514b1f1eb89e8d78f455e1efedb) chore: mark machine configuration validation failure as InvalidArgument * [`f424e5340`](https://github.com/siderolabs/talos/commit/f424e53404db61bbdbcbe8fab7cfec91785aa628) fix: stop containers more thoroughly * [`12827b861`](https://github.com/siderolabs/talos/commit/12827b861c13bb9b83a2f0ea2960582e8be319f0) chore: move "implements" checks to compile time * [`3a67c42cb`](https://github.com/siderolabs/talos/commit/3a67c42cbfdbd565e0af500d97c264ef6095637b) fix: kill the task processes when cleaning up stale task * [`14a79e325`](https://github.com/siderolabs/talos/commit/14a79e325bf0ffa107aaee9c07d3501b7010693c) chore: bump dependencies * [`9beee92e7`](https://github.com/siderolabs/talos/commit/9beee92e71e712a2af24dee612e27c30cac39d0d) docs: fix double vv in Kubernetes version * [`688272515`](https://github.com/siderolabs/talos/commit/6882725157f4c2ea79c248f79160e362be6c2c07) fix: use different username for Talos Kubernetes API access * [`161a52a9e`](https://github.com/siderolabs/talos/commit/161a52a9ef60eb9c1c1a6c31b06d06894456300c) feat: check apid client certificate extended key usage * [`9dadc4a59`](https://github.com/siderolabs/talos/commit/9dadc4a599f52cc564f5411dd35bc981e482d24a) fix: include all node addresses into etcd cert SANs * [`71bfd3e43`](https://github.com/siderolabs/talos/commit/71bfd3e43cdc9790d3cb7a134c3b49256b1942a1) feat: update CoreDNS to 1.9.4 * [`9df8f1ff1`](https://github.com/siderolabs/talos/commit/9df8f1ff1aebb24a6b0649ba491b10b23a0b2198) fix: list COSI APIs for the apid authenticator * [`31462450f`](https://github.com/siderolabs/talos/commit/31462450f19700dd6691ebc4b0c18edca4f6a1b7) fix: pass a pointer to specs.Mount into protoenc.Marshal * [`e626540df`](https://github.com/siderolabs/talos/commit/e626540dfb470386d0750f2f8bbaf4b5cb36b203) chore: avoid double API request logging in trustd * [`f62d17125`](https://github.com/siderolabs/talos/commit/f62d17125b8c1b26b0b62d22c2846f3a2ece37d1) chore: update crypto to use new import path siderolabs/crypto * [`ef27dd855`](https://github.com/siderolabs/talos/commit/ef27dd8553ee0e5467c3baaf4be18d1ccb30dad1) chore: bump dependencies * [`6472ae00b`](https://github.com/siderolabs/talos/commit/6472ae00b21c0f637b1e6610a8f3f71a1b775628) fix: automatically discard VIPs for etcd advertised addresses * [`5e21cca52`](https://github.com/siderolabs/talos/commit/5e21cca52d7462240bb42aafa225ee97d08bdc25) feat: support setting kernel parameters * [`bd56621cd`](https://github.com/siderolabs/talos/commit/bd56621cdf50d25013756a8792dc7b4d5354396f) feat: add structprotogen tool * [`cdb6bb2cc`](https://github.com/siderolabs/talos/commit/cdb6bb2cc78685c218506c61a477c8a8e569e861) feat: add Nano Pi R4S support * [`36c1f1d6e`](https://github.com/siderolabs/talos/commit/36c1f1d6e6aa50379343acba5348d8cc038b137e) fix: flip the client-server version check * [`cd6c53a97`](https://github.com/siderolabs/talos/commit/cd6c53a979236543afc302a67da627ee633883b3) docs: fork docs for v1.3 * [`0847400f7`](https://github.com/siderolabs/talos/commit/0847400f728d67889b9f740a0359eb916108d8ea) fix: prevent panic on health check if a member has no IPs * [`7471d7f01`](https://github.com/siderolabs/talos/commit/7471d7f0174a5240fa3c4cd2f16325ec2a4f1810) feat: update Flannel to v0.19.2 * [`148c75cfb`](https://github.com/siderolabs/talos/commit/148c75cfb99537f64d43a3add3259bf591cb79a9) docs: consolidate the control-plane documentation * [`353154281`](https://github.com/siderolabs/talos/commit/353154281a4cf72076b99160e50e617109f72996) fix: drop kube-system SA default binding * [`4f37b668b`](https://github.com/siderolabs/talos/commit/4f37b668befdbd26bc2d32106e0bcc654f7e6119) chore: remove capi hacks * [`1369afea8`](https://github.com/siderolabs/talos/commit/1369afea853423f22fde20effd431c3f8d906a9d) docs: make 1.2.0 docs default ones * [`7627cb0e3`](https://github.com/siderolabs/talos/commit/7627cb0e30a8b2a5a1cc30906b547511c9d3c98b) docs: add new `talosctl gen secrets` * [`8aa60a37a`](https://github.com/siderolabs/talos/commit/8aa60a37a6ea57bf54d558c7a2f54d806fad3173) chore: bump kernel to 5.15.64 * [`a798dbd5d`](https://github.com/siderolabs/talos/commit/a798dbd5d2d9bc6d1410a56035550d44de934950) docs: update docs for upcoming 1.2.0 release * [`b2fec3c97`](https://github.com/siderolabs/talos/commit/b2fec3c975dba7b0bc2dc7d5447e62350057061b) fix: properly handle `configContext` being `nil` in Talos client * [`1c0977b3a`](https://github.com/siderolabs/talos/commit/1c0977b3af22f9f4b61b80ca6dcedf14a5ef63ae) fix: change the type of returned gRPC connection object from the client * [`41848e421`](https://github.com/siderolabs/talos/commit/41848e421496184008ad2302e3cb03a882c0f5bf) fix: expose Talos client gRPC connection via the function `Conn` * [`2e9be4af8`](https://github.com/siderolabs/talos/commit/2e9be4af8b521eca985c425f62dfc7a59d19e7da) chore: bump dependencies * [`d283aba3a`](https://github.com/siderolabs/talos/commit/d283aba3a3670cfde8ab9137deba3ab3b343906f) test: fix cli reboot test * [`0b339a9dc`](https://github.com/siderolabs/talos/commit/0b339a9dc508327347777619749ff1e2c3e47f37) feat: track progress of action API calls * [`072349812`](https://github.com/siderolabs/talos/commit/072349812506c5cd32159bb14bab5b294ee59811) fix: update COSI to the version with gRPC Wait fix * [`89d57aa81`](https://github.com/siderolabs/talos/commit/89d57aa816a57448d6e350698a8f6a5d128209ac) fix: always abort the maintenance service * [`f6fa74619`](https://github.com/siderolabs/talos/commit/f6fa7461932462160f40f670a5252fbc2981bdc3) fix: limit apid backoff max delay * [`d7ef346db`](https://github.com/siderolabs/talos/commit/d7ef346db8ea7d4f7676ae5e032a3c0d06823d47) fix: get command in the case 'nodes' are not set in the context * [`4e9c32256`](https://github.com/siderolabs/talos/commit/4e9c322564d7f65c82d636a9f80c0c5354455967) fix: correctly render hosts.toml with multiple endpoints * [`cdd0f08bc`](https://github.com/siderolabs/talos/commit/cdd0f08bc5d8d47bc2d21745ee5a13ced3632c8a) feat: check client <> server version in some Talos commands * [`446b0af58`](https://github.com/siderolabs/talos/commit/446b0af58bf273712374472bfa2777de5b7ac46f) chore: bump kernel and runc * [`8c203ce9b`](https://github.com/siderolabs/talos/commit/8c203ce9b1722c5832c506857cb56e14e2a34fe1) feat: remove the machine from the discovery service on reset * [`b59ca5810`](https://github.com/siderolabs/talos/commit/b59ca5810e6cf75f6a3042a47535431110004201) chore: move from inet.af/netaddr to net/netip and go4.org/netipx * [`053af1d59`](https://github.com/siderolabs/talos/commit/053af1d59ea266b84bb049460f92b33b32c1b82e) fix: update etcd certificates when node addresses changes * [`11edb2c6f`](https://github.com/siderolabs/talos/commit/11edb2c6f84fbbfba437361ce4dcd70c50eb08d8) test: re-enable upgrade tests * [`0310e2089`](https://github.com/siderolabs/talos/commit/0310e20890b11e1f4015e923eb9984aea1188d20) chore: bump github.com/siderolabs/protoenc to v0.1.5 * [`29bd63240`](https://github.com/siderolabs/talos/commit/29bd632401ca694df0a2ab921a2a525b4c3440d8) chore: remove old build tags syntax * [`b500d0aa9`](https://github.com/siderolabs/talos/commit/b500d0aa9052ab5066eb6cde06bcdac3e998705a) chore: bump k8s to v1.25.0 * [`29e574be7`](https://github.com/siderolabs/talos/commit/29e574be74c96211fd010ee5bd06675898f04db8) docs: update to v1.2.0-beta.1 * [`26b549f2a`](https://github.com/siderolabs/talos/commit/26b549f2a12c3486b52a8877b8a0a4f985695c7d) chore: bump dependencies * [`8c3ac4c42`](https://github.com/siderolabs/talos/commit/8c3ac4c42bff1f1678ddb62e0f20a9c419460ad4) chore: limit GOMAXPROCS for Talos services * [`361e85b74`](https://github.com/siderolabs/talos/commit/361e85b7443f6f4ff24fbf99a9f9276b73b73ed4) fix: properly read kexec disabled sysctl * [`cfe6c2bc2`](https://github.com/siderolabs/talos/commit/cfe6c2bc2d42ca28f3a5b3217aa4d126777e3db6) docs: nvidia oss drivers * [`2f2d97b6b`](https://github.com/siderolabs/talos/commit/2f2d97b6b5663a0873db9d47b7706f2c0a531d8c) fix: don't wait for the hostname in maintenance mode * [`b15a63924`](https://github.com/siderolabs/talos/commit/b15a6392465aa2aa0df231c622ca1762972ccd20) chore: bump kernel to 5.15.62 * [`a0d94be30`](https://github.com/siderolabs/talos/commit/a0d94be30d3dcf41b2b8b34a1caa6928a029f81a) fix: stable default hostname bias * [`da4cd34ef`](https://github.com/siderolabs/talos/commit/da4cd34ef5c5a01cfc3c3ee56b3f8c2f77997b49) feat: update etcd advertised peer addresses on the fly * [`faf92ce01`](https://github.com/siderolabs/talos/commit/faf92ce01661c5a9a86f9e579da3a2822d93f1f6) chore: bump kubernetes to v1.25.0-rc.1 * [`52de919e3`](https://github.com/siderolabs/talos/commit/52de919e34789c36c4ee71ca133240b50b068064) chore: bump containerd to v1.6.8 * [`7d43fc79b`](https://github.com/siderolabs/talos/commit/7d43fc79b1e913d51f111ecc7c2c8b3bfb36e679) fix: make 'ca', 'crt' and 'key' flags optional for 'talosctl config add' * [`fd467e02c`](https://github.com/siderolabs/talos/commit/fd467e02c1edcfc0eff656392ece5dd8ba1114f2) fix: handle grub config being empty in the `Revert` function * [`9492aca65`](https://github.com/siderolabs/talos/commit/9492aca652eec4d4049fef1c8d141696ed72a197) fix: clean up `cancelCtxMu` leftovers in PriorityLock * [`61e3eb2ea`](https://github.com/siderolabs/talos/commit/61e3eb2eaab1c7974a27440ddd98139a27dfb9dc) fix: talosctl edit mc loop * [`32db7a7f5`](https://github.com/siderolabs/talos/commit/32db7a7f5d6638fc0f731a009dfb0c1870c69083) fix: surround `cancelCtx` with the mutex

### Changes since v1.3.0-alpha.1
23 commits

* [`aa56aed79`](https://github.com/siderolabs/talos/commit/aa56aed7981b4bdb29a5f668296597cf4a5c1d3b) feat: publish discovered public IP as one of the KubeSpan endpoint * [`9382443ba`](https://github.com/siderolabs/talos/commit/9382443baa8005f84aa177c6dbeb68b995d09084) feat: update Kubernetes to v1.26.0-rc.0 * [`6ffc381c5`](https://github.com/siderolabs/talos/commit/6ffc381c59b919a3d922a99f896f601cf4f6898a) feat: implement CRI configuration customization * [`e1e340bdd`](https://github.com/siderolabs/talos/commit/e1e340bdd910dc1f9c7fd08f71fb14352e96dbbf) feat: expose Talos node labels as a machine configuration field * [`c78bbbfda`](https://github.com/siderolabs/talos/commit/c78bbbfda3d545c3f50e0ad141369279dd2f4ca0) docs: specify that only XFS partitions are detected * [`b881a9a79`](https://github.com/siderolabs/talos/commit/b881a9a795937ba0c5d94ee4104209652bb992ab) chore: bump dependencies * [`5bfd7dbfa`](https://github.com/siderolabs/talos/commit/5bfd7dbfa75c2d9b4ec4d6a61b2da91e72113a71) test: fix assertion on reboot test * [`1cfb6188b`](https://github.com/siderolabs/talos/commit/1cfb6188bcc2aefaa0b4f707f16053a8a1fd9a28) feat: implement support for cgroupsv1 * [`3866d0e33`](https://github.com/siderolabs/talos/commit/3866d0e334cd4d8146cdc8d17196d7f7aa4877a5) feat: update Kubernetes to v1.26.0-beta.0 * [`e1590ba7b`](https://github.com/siderolabs/talos/commit/e1590ba7b9f0c1b0be9b78a1dd4381f2d2e90e57) fix: lifecycle action tracking * [`804762c59`](https://github.com/siderolabs/talos/commit/804762c597f3aebdc3521cefc93bbbf0ff4a57eb) feat: add timeout to cli action tracking, track by default & refactor * [`4e114ca12`](https://github.com/siderolabs/talos/commit/4e114ca120a1ee7de5a5822dba14c732f1dc7610) feat: use the etcd member id for etcd operations instead of hostname * [`06fea2441`](https://github.com/siderolabs/talos/commit/06fea244140e82fd30a4ac4c5e4433253bd930ab) feat: expand platform metadata resources * [`03a20da9d`](https://github.com/siderolabs/talos/commit/03a20da9da5916c63015d355f4b56823778e994f) fix: filter up duplicate IPs out of NodeAddresses * [`6b771bc73`](https://github.com/siderolabs/talos/commit/6b771bc73984d755dce06bd5452131add5ecf487) chore: bump deps * [`96aa9638f`](https://github.com/siderolabs/talos/commit/96aa9638f724d81ba8ef64db0ed7032694e7da5d) chore: rename talos-systems/talos to siderolabs/talos * [`30bbf6463`](https://github.com/siderolabs/talos/commit/30bbf6463a85c10b4273633e928b6c419731e31d) refactor: use siderolabs/net version with netip.Addr * [`343c55762`](https://github.com/siderolabs/talos/commit/343c55762e4af279ceeb0066540124ef66c77602) chore: replace talos-systems Go modules with siderolabs * [`0301bbe93`](https://github.com/siderolabs/talos/commit/0301bbe9369eb2811aa9d5177fcc024606c71ed4) fix: check if processes is nil to avoid panic * [`08e7e49a2`](https://github.com/siderolabs/talos/commit/08e7e49a29018ed1932709779349a8c056125090) test: update versions for upgrade tests * [`0b41923c3`](https://github.com/siderolabs/talos/commit/0b41923c3608a815d14bccd84a6abcf1bc99db43) fix: restore the StaticPodStatus resource * [`1947092ae`](https://github.com/siderolabs/talos/commit/1947092ae225abe408c1b2c20633af671ae9c2a2) chore: introduce a healthcheck for `machined` service * [`3333cd93c`](https://github.com/siderolabs/talos/commit/3333cd93c821b00bd523584509075d21e2ec16be) fix: generate correct Flannel config for IPv6-only clusters

### Changes from siderolabs/crypto
27 commits

* [`c3225ee`](https://github.com/siderolabs/crypto/commit/c3225eee603a8d1218c67e1bfe33ddde7953ed74) feat: allow CSR template subject field to be overridden * [`8570669`](https://github.com/siderolabs/crypto/commit/85706698dac8cddd0e9f41006bed059347d2ea26) chore: rename to siderolabs/crypto * [`e9df1b8`](https://github.com/siderolabs/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs * [`510b0d2`](https://github.com/siderolabs/crypto/commit/510b0d2753a89170d0c0f60e052a66484997a5b2) chore: add json tags * [`6fa2d93`](https://github.com/siderolabs/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil` * [`9a63cba`](https://github.com/siderolabs/crypto/commit/9a63cba8dabd278f3080fa8c160613efc48c43f8) fix: add back support for generating ECDSA keys with P-256 and SHA512 * [`893bc66`](https://github.com/siderolabs/crypto/commit/893bc66e4716a4cb7d1d5e66b5660ffc01f22823) fix: use SHA256 for ECDSA-P256 * [`deec8d4`](https://github.com/siderolabs/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types * [`d3cb772`](https://github.com/siderolabs/crypto/commit/d3cb77220384b3a3119a6f3ddb1340bbc811f1d1) feat: make possible to change KeyUsage * [`6bc5bb5`](https://github.com/siderolabs/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument * [`cd18ef6`](https://github.com/siderolabs/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations * [`97c888b`](https://github.com/siderolabs/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR * [`7776057`](https://github.com/siderolabs/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos * [`80df078`](https://github.com/siderolabs/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters * [`15bdd28`](https://github.com/siderolabs/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates * [`4f80b97`](https://github.com/siderolabs/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate * [`39584f1`](https://github.com/siderolabs/crypto/commit/39584f1b6e54e9966db1f16369092b2215707134) feat: support for key/certificate types RSA, Ed25519, ECDSA * [`cf75519`](https://github.com/siderolabs/crypto/commit/cf75519cab82bd1b128ae9b45107c6bb422bd96a) fix: function NewKeyPair should create certificate with proper subject * [`751c95a`](https://github.com/siderolabs/crypto/commit/751c95aa9434832a74deb6884cff7c5fd785db0b) feat: add 'PEMEncodedKey' which allows to transport keys in YAML * [`562c3b6`](https://github.com/siderolabs/crypto/commit/562c3b66f89866746c0ba47927c55f41afed0f7f) feat: add support for public RSA key in RSAKey * [`bda0e9c`](https://github.com/siderolabs/crypto/commit/bda0e9c24e80c658333822e2002e0bc671ac53a3) feat: enable more conversions between encoded and raw versions * [`e0dd56a`](https://github.com/siderolabs/crypto/commit/e0dd56ac47456f85c0b247999afa93fb87ebc78b) feat: add NotBefore option for x509 cert creation * [`12a4897`](https://github.com/siderolabs/crypto/commit/12a489768a6bb2c13e16e54617139c980f99a658) feat: add support for SPKI fingerprint generation and matching * [`d0c3eef`](https://github.com/siderolabs/crypto/commit/d0c3eef149ec9b713e7eca8c35a6214bd0a64bc4) fix: implement NewKeyPair * [`196679e`](https://github.com/siderolabs/crypto/commit/196679e9ec77cb709db54879ddeddd4eaafaea01) feat: move `pkg/grpc/tls` from `github.com/talos-systems/talos` as `./tls` * [`1ff6242`](https://github.com/siderolabs/crypto/commit/1ff6242c91bb298ceeb4acd65685cba952fe4178) chore: initial version as imported from talos-systems/talos * [`835063e`](https://github.com/siderolabs/crypto/commit/835063e055b28a525038b826a6d80cbe76402414) chore: initial commit

### Changes from siderolabs/discovery-api
3 commits

* [`5b0c5e7`](https://github.com/siderolabs/discovery-api/commit/5b0c5e78097c1489457b148a7f13c73890f5ecad) chore: rename to siderolabs, rekres, etc * [`db279ef`](https://github.com/siderolabs/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/siderolabs/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from siderolabs/discovery-client
2 commits

* [`a5c19c6`](https://github.com/siderolabs/discovery-client/commit/a5c19c65f4833a104ac68f35a3c0f8f37be8fe87) feat: provide public IP discovered from the server * [`230f317`](https://github.com/siderolabs/discovery-client/commit/230f317a8e6e9542b82efcbac9f5cd7b9cff34b6) fix: reconnect the client on update failure

### Changes from siderolabs/extras
3 commits

* [`b155fa0`](https://github.com/siderolabs/extras/commit/b155fa067c36717ac84205e73e6ef7f47ba13842) chore: enable renovate * [`8f00d77`](https://github.com/siderolabs/extras/commit/8f00d7719f0a2312eaa3815ae8c7a91d000db661) feat: update tc-redirect-tap to the latest version * [`7c91844`](https://github.com/siderolabs/extras/commit/7c91844de76568335b7ccaec63cecec17401dd83) chore: bump go to 1.19.2

### Changes from siderolabs/gen
6 commits

* [`b3b6db8`](https://github.com/siderolabs/gen/commit/b3b6db858cb6ce46005edeb70776608e3f9bc402) fix: fix Copy documentation and implementation * [`521f737`](https://github.com/siderolabs/gen/commit/521f7371f40556ddce7f730c8de5e1888e40b621) feat: add xerrors package which contains additions to the std errors * [`726e066`](https://github.com/siderolabs/gen/commit/726e066dcb35c86f82866097bed806f22b936292) fix: rename tuples.go to pair.go and set proper package name * [`d8d7d25`](https://github.com/siderolabs/gen/commit/d8d7d25ce9a588609c00cb798206a01a866bf7a6) chore: minor additions * [`338a650`](https://github.com/siderolabs/gen/commit/338a65065f92eb6426a66c4a88a0cc02cc02e529) chore: add initial implementation and documentation * [`4fd8667`](https://github.com/siderolabs/gen/commit/4fd866707052c792a6adccbc28efec5debdd18a8) Initial commit

### Changes from siderolabs/go-blockdevice
56 commits

* [`694ac62`](https://github.com/siderolabs/go-blockdevice/commit/694ac62b3dcf995beea95a77659fdc6064b457b3) chore: update imports to siderolabs, rekres * [`dcf6044`](https://github.com/siderolabs/go-blockdevice/commit/dcf6044c906b36f183e11b6553458c680126d1d9) chore: rekres and rename * [`9c4af49`](https://github.com/siderolabs/go-blockdevice/commit/9c4af492cc17279f0281fcd271e7423be78442bb) fix: cryptsetup remove slot * [`74ea471`](https://github.com/siderolabs/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs * [`9fa801c`](https://github.com/siderolabs/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk * [`fccee8b`](https://github.com/siderolabs/go-blockdevice/commit/fccee8bb082b105cb60db40cb01636efc3241b5f) chore: rekres the source, fix issues * [`d9c3a27`](https://github.com/siderolabs/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems * [`b374eb4`](https://github.com/siderolabs/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default * [`ec428fe`](https://github.com/siderolabs/go-blockdevice/commit/ec428fed2ecd5a389833a88f8dc333762816db99) fix: lookup filesystem labels on the actual device path * [`7b9de26`](https://github.com/siderolabs/go-blockdevice/commit/7b9de26bc6bc3d54b95bd8e8fb3aade4b45adc6c) feat: read symlink fullpath in block device list function * [`6928ee4`](https://github.com/siderolabs/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/siderolabs/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions * [`15b182d`](https://github.com/siderolabs/go-blockdevice/commit/15b182db0cd233b163ed83d1724c7e28cf29d71a) fix: return partition table not exist when trying to read an empty dev * [`b9517d5`](https://github.com/siderolabs/go-blockdevice/commit/b9517d51120d385f97b0026f99ce3c4782940c37) fix: resize partition * [`70d2865`](https://github.com/siderolabs/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks * [`667bf53`](https://github.com/siderolabs/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found * [`d7d4cdd`](https://github.com/siderolabs/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found * [`33afba3`](https://github.com/siderolabs/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method * [`e367f9d`](https://github.com/siderolabs/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode * [`d981156`](https://github.com/siderolabs/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows * [`fe24303`](https://github.com/siderolabs/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations * [`2ec0c3c`](https://github.com/siderolabs/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition * [`87816a8`](https://github.com/siderolabs/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size * [`c34b59f`](https://github.com/siderolabs/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module * [`30c2bc3`](https://github.com/siderolabs/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable * [`1292574`](https://github.com/siderolabs/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/siderolabs/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types * [`1d830a2`](https://github.com/siderolabs/go-blockdevice/commit/1d830a25f64f6fb96a1bedd800c0b40b107dc833) fix: revert mark the EFI partition in PMBR as bootable * [`bec914f`](https://github.com/siderolabs/go-blockdevice/commit/bec914ffdda42abcfe642bc2cdfc9fcda56a74ee) fix: mark the EFI partition in PMBR as bootable * [`776b37d`](https://github.com/siderolabs/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters * [`bb3ad73`](https://github.com/siderolabs/go-blockdevice/commit/bb3ad73f69836acc2785ec659435e24a531359e7) fix: align partition start to physical sector size * [`8f976c2`](https://github.com/siderolabs/go-blockdevice/commit/8f976c2031108651738ebd4db69fb09758754a28) feat: replace exec.Command with go-cmd module * [`1cf7f25`](https://github.com/siderolabs/go-blockdevice/commit/1cf7f252c38cf11ef07723de2debc27d1da6b520) fix: properly handle no child processes error from cmd.Wait * [`04a9851`](https://github.com/siderolabs/go-blockdevice/commit/04a98510c07fe8477f598befbfe6eaec4f4b73a2) feat: implement luks encryption provider * [`b0375e4`](https://github.com/siderolabs/go-blockdevice/commit/b0375e4267fdc6108bd9ff7a5dc97b80cd924b1d) feat: add an option to open block device with exclusive flock * [`5a1c7f7`](https://github.com/siderolabs/go-blockdevice/commit/5a1c7f768e016c93f6c0be130ffeaf34109b5b4d) refactor: add devname into gpt.Partition, refactor probe package * [`f2728a5`](https://github.com/siderolabs/go-blockdevice/commit/f2728a581972be977d863d5d9177a873b8f3fc7b) fix: keep contents of PMBR when writing it * [`2878460`](https://github.com/siderolabs/go-blockdevice/commit/2878460b54e8b8c3846c6a882ca9e1472c8b6b3b) fix: write second copy of partition entries * [`943b08b`](https://github.com/siderolabs/go-blockdevice/commit/943b08bc32a2156cffb23e92b8be9288de4a7421) fix: blockdevice reset should read partition table from disk * [`5b4ee44`](https://github.com/siderolabs/go-blockdevice/commit/5b4ee44cfd434a03ec2d7167bcc56d0f164c3fa2) fix: ignore `/dev/ram` devices * [`98754ec`](https://github.com/siderolabs/go-blockdevice/commit/98754ec2bb200acc9e9e573fa766754d60e25ff2) refactor: rewrite GPT library * [`2a1baad`](https://github.com/siderolabs/go-blockdevice/commit/2a1baadffdf8c9b65355e9af6e744aeab838c9db) fix: correctly build paths for `mmcblk` devices * [`8076344`](https://github.com/siderolabs/go-blockdevice/commit/8076344a95021f25ab5d1fbf5ea4fefc790f6c3c) fix: return proper disk size from GetDisks function * [`8742133`](https://github.com/siderolabs/go-blockdevice/commit/874213371a3fb0925aab45cbba68a957e3319525) chore: add common method to list available disks using /sys/block * [`c4b5833`](https://github.com/siderolabs/go-blockdevice/commit/c4b583363d63503ed7e4adb9a9fa64335f7e198d) feat: implement "fast" wipe * [`b4e67d7`](https://github.com/siderolabs/go-blockdevice/commit/b4e67d73d70d8dc06aa2b4986622dcb854dfc40c) feat: return resize status from Resize() function * [`ceae64e`](https://github.com/siderolabs/go-blockdevice/commit/ceae64edb3a591c6f6bbd75b1149d1cfe426dd8e) fix: sync kernel partition table incrementally * [`2cb9516`](https://github.com/siderolabs/go-blockdevice/commit/2cb95165aa67b0b839863b5ad89920c3ac7e2c82) fix: return correct error value from blkpg functions * [`cebe43d`](https://github.com/siderolabs/go-blockdevice/commit/cebe43d1fdc1e509437198e578faa9d5a804cc37) refactor: expose `InsertAt` method via interface * [`c40dcd8`](https://github.com/siderolabs/go-blockdevice/commit/c40dcd80c50b41c1f2a60ea6aa9d5fb3d3b180a3) fix: properly inform kernel about partition deletion * [`bb8ac5d`](https://github.com/siderolabs/go-blockdevice/commit/bb8ac5d6a25e279e16213f585dc8d02ba6ed645f) feat: implement disk wiping via several methods * [`23fb7dc`](https://github.com/siderolabs/go-blockdevice/commit/23fb7dc755325cfe12e48c8e8e31bebab9ddc2bc) feat: expose partition name (label) * [`ff3a821`](https://github.com/siderolabs/go-blockdevice/commit/ff3a8210be999b8bfb2019f19f8a8b50901c64cc) feat: implement 'InsertAt' method to insert partitions at any position * [`3d1ce4f`](https://github.com/siderolabs/go-blockdevice/commit/3d1ce4fc859fa614a4c5c54a10c0f5f4fce38bb6) fix: calculate last lba of partition correctly * [`b71540f`](https://github.com/siderolabs/go-blockdevice/commit/b71540f6c398e958bdb7c118396a736419f735d4) feat: copy initial version from talos-systems/talos * [`ca3c078`](https://github.com/siderolabs/go-blockdevice/commit/ca3c078da95e6497c9d41667dc242e32682e517d) Initial commit

### Changes from siderolabs/go-circular
2 commits

* [`507e0ec`](https://github.com/siderolabs/go-circular/commit/507e0ec7b70e7c8336c25640929ae7b04869dfa1) refactor: extract circular Go module * [`2234b3a`](https://github.com/siderolabs/go-circular/commit/2234b3ab14ec6a49b5ce48aaec108c6b3f33dc7f) docs: add README

### Changes from siderolabs/go-cmd
5 commits

* [`0aea518`](https://github.com/siderolabs/go-cmd/commit/0aea518205cb71e4126a88d605009b44d4e15f7e) chore: rekres and update * [`68eb006`](https://github.com/siderolabs/go-cmd/commit/68eb0067e0f0fa18db1eb91257764d5a7b69ab30) feat: return typed error for exit error * [`333ccf1`](https://github.com/siderolabs/go-cmd/commit/333ccf125e0e8f36e4d67d05ea0f0e0f09827c73) feat: add stdin support into the Run methods * [`c5c8f1c`](https://github.com/siderolabs/go-cmd/commit/c5c8f1c4f9d549b11fda70358ff21c9956c5f295) feat: extract cmd module from Talos into a separate module * [`77685fc`](https://github.com/siderolabs/go-cmd/commit/77685fc53eb44020f11e2fc5451a86235231903b) Initial commit

### Changes from siderolabs/go-debug
6 commits

* [`c1bc4bf`](https://github.com/siderolabs/go-debug/commit/c1bc4bf306e54879ce9f4b002527876ac0cbf88f) chore: rekres, rename, etc * [`3d0a6e1`](https://github.com/siderolabs/go-debug/commit/3d0a6e1bf5e3c521e83ead2c8b7faad3638b8c5d) feat: race build tag flag detector * [`5b292e5`](https://github.com/siderolabs/go-debug/commit/5b292e50198b8ed91c434f00e2772db394dbf0b9) feat: disable memory profiling by default * [`c6d0ae2`](https://github.com/siderolabs/go-debug/commit/c6d0ae2c0ee099fa0940405401e6a02716a15bd8) fix: linters and CI * [`d969f95`](https://github.com/siderolabs/go-debug/commit/d969f952af9e02feea59963671298fc236ca4399) feat: initial implementation * [`b2044b7`](https://github.com/siderolabs/go-debug/commit/b2044b70379c84f9706de74044bd2fd6a8e891cf) Initial commit

### Changes from siderolabs/go-kmsg
4 commits

* [`e2a0000`](https://github.com/siderolabs/go-kmsg/commit/e2a0000c52ff2735a2e4a535b7ad24bb73499c75) chore: rekres, rename * [`b08e4d3`](https://github.com/siderolabs/go-kmsg/commit/b08e4d36a2f3df0a3d031b1a3028e2d6e4c26710) feat: replace tab character with space in console output * [`2edcd3a`](https://github.com/siderolabs/go-kmsg/commit/2edcd3a913508e2d922776f729bfc4bcab031a8b) feat: add initial version * [`53cdd8d`](https://github.com/siderolabs/go-kmsg/commit/53cdd8d67b9dbab692471a2d5161e7e0b3d04cca) chore: initial commit

### Changes from siderolabs/go-kubeconfig
2 commits

* [`e7fdd94`](https://github.com/siderolabs/go-kubeconfig/commit/e7fdd94573fa175784700cbb24b37a087e6ca35b) refactor: extract kubeconfig library as a Go module * [`50e91b8`](https://github.com/siderolabs/go-kubeconfig/commit/50e91b8ba9df2c14a82d0ba95ee8acad262497b6) docs: add REAMDE

### Changes from siderolabs/go-loadbalancer
12 commits

* [`f54e3c9`](https://github.com/siderolabs/go-loadbalancer/commit/f54e3c9a5450d8c734f915d7038f798afa8c0d0d) chore: update dependencies to siderolabs, rekres * [`438b71d`](https://github.com/siderolabs/go-loadbalancer/commit/438b71da2474525311ee6435606d7a6143696651) chore: update package path and rekres * [`5341eec`](https://github.com/siderolabs/go-loadbalancer/commit/5341eec63c6d3396a37be17506e081ad72ccaeb6) feat: implement public method to check if the route is Healthy * [`b578d47`](https://github.com/siderolabs/go-loadbalancer/commit/b578d477211476bbc34b1ea2c86d54f0d1b0cdc1) feat: add a way to configure loadbalancer options * [`c54d95d`](https://github.com/siderolabs/go-loadbalancer/commit/c54d95d8252780dc374032dc5fe10e7e84a15062) feat: implement control plane loadbalancer * [`4a6e29e`](https://github.com/siderolabs/go-loadbalancer/commit/4a6e29e7c02a2a94193a6014de04c2d2c79bdb02) refactor: clean up names, fix the lingering goroutines * [`af87d1c`](https://github.com/siderolabs/go-loadbalancer/commit/af87d1cbb79da35adabb9587a028db9b3e9fde1c) chore: apply new Kres rules * [`a445702`](https://github.com/siderolabs/go-loadbalancer/commit/a4457024d5189d754b2da4a30b14072a0e3f5f05) feat: allow dial timeout and keep alive period to be configurable * [`3c8f347`](https://github.com/siderolabs/go-loadbalancer/commit/3c8f3471d14e37866c65f73170ef83c038ae5a8c) feat: provide a way to configure logger for the loadbalancer * [`da8e987`](https://github.com/siderolabs/go-loadbalancer/commit/da8e987434c3d407679a40e213b12a8e1c98abb8) feat: implement Reconcile - ability to change upstream list on the fly * [`8b1dfa6`](https://github.com/siderolabs/go-loadbalancer/commit/8b1dfa6e80dea53d699a551221695ca99b2aadb2) feat: copy initial version from talos-systems/talos * [`c2f6a8f`](https://github.com/siderolabs/go-loadbalancer/commit/c2f6a8f88439608ea4b7623e6becdcf079cad217) Initial commit

### Changes from siderolabs/go-procfs
10 commits

* [`a062a4c`](https://github.com/siderolabs/go-procfs/commit/a062a4ca078a6b3b3f119edf86e5f80620e67a55) chore: rekres, rename * [`8cbc42d`](https://github.com/siderolabs/go-procfs/commit/8cbc42d3dc246a693d9b307c5358f6f7f3cb60bc) feat: provide an option to overwrite some args in AppendAll * [`24d06a9`](https://github.com/siderolabs/go-procfs/commit/24d06a955782ed7d468f5117e986ec632f316310) refactor: remove talos kernel default args * [`a82654e`](https://github.com/siderolabs/go-procfs/commit/a82654edcec13531a3f6baf1d9c2933b074326cf) feat: implement SetAll method * [`16ce2ef`](https://github.com/siderolabs/go-procfs/commit/16ce2ef52acd0f351c93365e5c9263af442bec12) fix: update cmdline.Set() to drop the value being overwritten * [`5a9a4a7`](https://github.com/siderolabs/go-procfs/commit/5a9a4a75d559eab694afcdad2496d268473db432) feat: update kernel args for new KSPP requirements * [`57c7311`](https://github.com/siderolabs/go-procfs/commit/57c7311fdd4524bc17f528486bf9b417536153c3) refactor: change directory layout * [`a077c96`](https://github.com/siderolabs/go-procfs/commit/a077c96480d04ad432ce909295cfd969d8c4da7d) fix: fix go module name * [`698666f`](https://github.com/siderolabs/go-procfs/commit/698666fd4540a0460b5141425d47df084f9a6e20) chore: move package to new repo * [`dabb425`](https://github.com/siderolabs/go-procfs/commit/dabb42542312758dd0edc22ece49d8daa5476bbd) Initial commit

### Changes from siderolabs/go-retry
9 commits

* [`6d45449`](https://github.com/siderolabs/go-retry/commit/6d45449c83129d00cc73eb40f9e52294d2e107fc) chore: rekres, rename * [`c78cc95`](https://github.com/siderolabs/go-retry/commit/c78cc953d9e95992575305b4e8648392c6c9b9e6) fix: implement `errors.Is` for all errors in the set * [`7885e16`](https://github.com/siderolabs/go-retry/commit/7885e16b2cb0267bcc8b07cdd0eced14e8005864) feat: add ExpectedErrorf * [`3d83f61`](https://github.com/siderolabs/go-retry/commit/3d83f6126c1a3a238d1d1d59bfb6273e4087bdac) feat: deprecate UnexpectedError * [`b9dc1a9`](https://github.com/siderolabs/go-retry/commit/b9dc1a990133dd3399549b4ea199759bdfe58bb8) feat: add support for `context.Context` in Retry * [`8c63d29`](https://github.com/siderolabs/go-retry/commit/8c63d290a6884095ea2e754c52e575603abe4bc0) fix: correctly implement error interfaces on wrapped errors * [`752f081`](https://github.com/siderolabs/go-retry/commit/752f081252cfef6106151dc285fcbe4849ab0a0c) feat: add an option to log errors being retried * [`073067b`](https://github.com/siderolabs/go-retry/commit/073067bd95a70e9b0a2a8d07d33311be69c24923) feat: copy initial version from talos-systems/talos * [`c7968c5`](https://github.com/siderolabs/go-retry/commit/c7968c54b4b1743d14dedce51431bf6e79a67a4f) Initial commit

### Changes from siderolabs/go-smbios
11 commits

* [`10c1dd8`](https://github.com/siderolabs/go-smbios/commit/10c1dd8f2a7a30cc4a00b90d76afcf3ff22bf8ae) fix: check for end of the slice properly * [`9ca8ce7`](https://github.com/siderolabs/go-smbios/commit/9ca8ce77b796f3f49c0b7fd70f184911da294dc1) chore: treat invalid strings as empty * [`dbc5f79`](https://github.com/siderolabs/go-smbios/commit/dbc5f794726f18f0736c1203a440b8148675bc04) chore: rekres+rename * [`3f1e775`](https://github.com/siderolabs/go-smbios/commit/3f1e775b7e3ef74be41461417d800ac81671a553) feat: rework destructuring of SMBIOS information and added some tests * [`fd5ec8c`](https://github.com/siderolabs/go-smbios/commit/fd5ec8ce4873790b7fbd46dba9d7f49c9de7176a) fix: remove useless (?) goroutines leading to data race error * [`d3a32be`](https://github.com/siderolabs/go-smbios/commit/d3a32bea731a0c2a60ce7f5eae60253300ef27e1) fix: return UUID in middle endian only on SMBIOS >= 2.6 * [`fb425d4`](https://github.com/siderolabs/go-smbios/commit/fb425d4727e620b6a2b6ba49e405a2c6f0e46304) feat: add memory device * [`0bb4f96`](https://github.com/siderolabs/go-smbios/commit/0bb4f96a6679e8fc958903c4f451ca068f8e3c41) feat: add physical memory array * [`8019619`](https://github.com/siderolabs/go-smbios/commit/80196199691e7094946a207463c67fc42da6a0e2) feat: supply wake-up type in SMBIOS info * [`94b8c4e`](https://github.com/siderolabs/go-smbios/commit/94b8c4e489eef8c44cb1a2768678945d73e16e88) feat: initial implementation * [`864ed80`](https://github.com/siderolabs/go-smbios/commit/864ed80937edf072f7e7e63551aef0d1f7776111) Initial commit

### Changes from siderolabs/go-tail
2 commits

* [`962ae43`](https://github.com/siderolabs/go-tail/commit/962ae433288845cfc7f2aab0c0ef74777e2bd992) refactor: extract go-tail module * [`359c3cb`](https://github.com/siderolabs/go-tail/commit/359c3cbde0f6a0a49d6893b2d1f8cb7ee6df9efc) docs: initial commit

### Changes from siderolabs/grpc-proxy
51 commits

* [`4cc7bbe`](https://github.com/siderolabs/grpc-proxy/commit/4cc7bbe397d74ee731398d67d34c214747957122) chore: rename to siderolabs/grpc-proxy, rekres * [`2c586db`](https://github.com/siderolabs/grpc-proxy/commit/2c586dbdda4e9c2bd09754beb13014c52b626db1) feat: pass fullMethodName to GetConnection * [`6dfa2cc`](https://github.com/siderolabs/grpc-proxy/commit/6dfa2cc80b6195844cae2dc2b2bc0b9b62246d8d) fix: ignore errors on duplicate `SetHeader` calls * [`b076302`](https://github.com/siderolabs/grpc-proxy/commit/b076302cc46ec6742e71fe1d49f6ec2d5d3a15dc) fix: use io.EOF error when no backend connections are available * [`82daca0`](https://github.com/siderolabs/grpc-proxy/commit/82daca0322a4293bd27071ae1ba8dd5097509d21) docs: update README * [`fa6843a`](https://github.com/siderolabs/grpc-proxy/commit/fa6843ae5b64500d481a1d031790406ed9df77d7) chore: fix spelling * [`c0a87d9`](https://github.com/siderolabs/grpc-proxy/commit/c0a87d95be9c62b0c4fd1fa694ef768e1f8e2391) chore: major cleanup of the code and build * [`ca3bc61`](https://github.com/siderolabs/grpc-proxy/commit/ca3bc6131f052aa000517339211335aaa4ebb640) fix: ignore some errors so that we don't spam the logs * [`5c579a7`](https://github.com/siderolabs/grpc-proxy/commit/5c579a7a61475bde3ec9c1efe000d2a55e2a3cb2) feat: allow different formats for messages streaming/unary * [`6c9f7b3`](https://github.com/siderolabs/grpc-proxy/commit/6c9f7b399173dd5769dbc4e8e366e78f05cead85) fix: allow mode to be set for each request being proxied * [`cc91c09`](https://github.com/siderolabs/grpc-proxy/commit/cc91c09782824e261bf1c861961a272aedb2b123) refactor: provide better public API, enforce proxying mode * [`d8d3a75`](https://github.com/siderolabs/grpc-proxy/commit/d8d3a751d1e71d006ba90379eed388c487bbb246) chore: update import paths after repo move * [`dbf07a4`](https://github.com/siderolabs/grpc-proxy/commit/dbf07a4d9e16fe3cf7407b9921c1746aa24ffaf6) Merge pull request [#7](https://github.com/siderolabs/grpc-proxy/pull/7) from smira/one2many-4 * [`fc0d27d`](https://github.com/siderolabs/grpc-proxy/commit/fc0d27dc6b5b9db35173f3e78778784a9e7c95bf) More tests, small code fixes, updated README. * [`d9ce0b1`](https://github.com/siderolabs/grpc-proxy/commit/d9ce0b1053a7f15ea65bf46e94cfe4154493bad7) Merge pull request [#6](https://github.com/siderolabs/grpc-proxy/pull/6) from smira/one2many-3 * [`2d37ba4`](https://github.com/siderolabs/grpc-proxy/commit/2d37ba444528a00f988671f3a01666e692739a37) Support for one2many streaming calls, tests. * [`817b035`](https://github.com/siderolabs/grpc-proxy/commit/817b03553ed7d97bd0da09283776d54592d7b5d4) Merge pull request [#5](https://github.com/siderolabs/grpc-proxy/pull/5) from smira/one2many-2 * [`436b338`](https://github.com/siderolabs/grpc-proxy/commit/436b3383a39fd860f3b2379ffab80a44ae1809f7) More unary one-2-many tests, error propagation. * [`1f0cb46`](https://github.com/siderolabs/grpc-proxy/commit/1f0cb466268f046e8e9fb78b1902411ac3a753ba) Merge pull request [#4](https://github.com/siderolabs/grpc-proxy/pull/4) from smira/one2many-1 * [`992a975`](https://github.com/siderolabs/grpc-proxy/commit/992a975ccf0b97e4be329c84bd3018652e8e50ae) Proxying one to many: first iteration * [`a0988ff`](https://github.com/siderolabs/grpc-proxy/commit/a0988ff2b29839892a7913acd76f26f4e7edcc3a) Merge pull request [#3](https://github.com/siderolabs/grpc-proxy/pull/3) from smira/small-fixups * [`e3111ef`](https://github.com/siderolabs/grpc-proxy/commit/e3111ef2c16f0ee4bba597a2ab1ab6a2818c2734) Small fixups in preparation to add one-to-many proxying. * [`6d76ffc`](https://github.com/siderolabs/grpc-proxy/commit/6d76ffcff89f6636d3689ed1c9b0eebe87722114) Merge pull request [#2](https://github.com/siderolabs/grpc-proxy/pull/2) from smira/backend-concept * [`2aad63a`](https://github.com/siderolabs/grpc-proxy/commit/2aad63ac5bae09232ea5ac80b42338e9e3af67c4) Add concept of a 'Backend', but still one to one proxying * [`7cc4610`](https://github.com/siderolabs/grpc-proxy/commit/7cc46101114a2779d6393e0e8f841bf3febb2753) Merge pull request [#1](https://github.com/siderolabs/grpc-proxy/pull/1) from smira/build * [`37f01f3`](https://github.com/siderolabs/grpc-proxy/commit/37f01f3aab3b978a8fecb428fca4d4c722141229) Rework build to use GitHub Actions, linting updates. * [`0f1106e`](https://github.com/siderolabs/grpc-proxy/commit/0f1106ef9c766333b9acb4b81e705da4bade7215) Move error checking further up (#34) * [`d5b35f6`](https://github.com/siderolabs/grpc-proxy/commit/d5b35f634383bf8931f8798797daaf9c1a59235e) Update gRPC and fix tests (#27) * [`67591eb`](https://github.com/siderolabs/grpc-proxy/commit/67591eb23c48346a480470e462289835d96f70da) Break StreamDirector interface, fix metadata propagation for gRPC-Go>1.5. (#20) * [`97396d9`](https://github.com/siderolabs/grpc-proxy/commit/97396d94749c00db659393ba5123f707062f829f) Merge pull request [#11](https://github.com/siderolabs/grpc-proxy/pull/11) from mwitkow/fix-close-bug * [`3fcbd37`](https://github.com/siderolabs/grpc-proxy/commit/3fcbd3737ec6baff505795417e48f162a7a3183c) fixup closing conns * [`a8f5f87`](https://github.com/siderolabs/grpc-proxy/commit/a8f5f87a2f5e6bc3643b78d64594195b2395a238) fixup tests, extend readme * [`428fa1c`](https://github.com/siderolabs/grpc-proxy/commit/428fa1c450320041e0ad8e251d6aed435401174e) Fix a channel closing bug * [`af55d61`](https://github.com/siderolabs/grpc-proxy/commit/af55d612de6c5723a5a59340704db7bc771023ff) Merge pull request [#10](https://github.com/siderolabs/grpc-proxy/pull/10) from mwitkow/bugfix/streaming-fix * [`de4d3db`](https://github.com/siderolabs/grpc-proxy/commit/de4d3db538565636e1e977102f6f0bd1ed0ce9c2) remove spurious printfs * [`84242c4`](https://github.com/siderolabs/grpc-proxy/commit/84242c4e690da18d16d2ab8f2fa47e45986220b6) fix the "i don't know who finished" case * [`9b22f41`](https://github.com/siderolabs/grpc-proxy/commit/9b22f41d8535fa3e40908c78ae66066c7972b6d9) fix full duplex streaming * [`c2f7c98`](https://github.com/siderolabs/grpc-proxy/commit/c2f7c98b0b6cd180659aed31e98cbbc18d616b1c) update readme * [`d654141`](https://github.com/siderolabs/grpc-proxy/commit/d654141edcb92b7fa2bba9d3e690e569c72f8e9d) update README * [`f457856`](https://github.com/siderolabs/grpc-proxy/commit/f4578565f2d34dc89774128db2bfda3a328cba40) move to proxy subdirectory * [`4889d78`](https://github.com/siderolabs/grpc-proxy/commit/4889d78e468681601b8229c81807dcf37b00ff63) Add fixup scripts * [`ef60a37`](https://github.com/siderolabs/grpc-proxy/commit/ef60a37547d137e52873be183f2d7a5626d7c034) version 2 of the grpc-proxy, this time with fewer grpc upstream deps * [`07aeac1`](https://github.com/siderolabs/grpc-proxy/commit/07aeac13e988c0c0b3a886c79972e20408a765e0) Merge pull request [#2](https://github.com/siderolabs/grpc-proxy/pull/2) from daniellowtw/master * [`e5c3df5`](https://github.com/siderolabs/grpc-proxy/commit/e5c3df5b2f0a1ffc4cb755cbe6b30b435e35de37) Fix compatibility with latest grpc library * [`52be0a5`](https://github.com/siderolabs/grpc-proxy/commit/52be0a559a85f0e2480bde6725f3f144396aa6ef) bugfix: fix gRPC Java deadlock, due to different dispatch logic * [`822df7d`](https://github.com/siderolabs/grpc-proxy/commit/822df7d86b556b703fc11798a3bdcbaeb60c18a6) Fix reference to mwitkow. * [`28341d1`](https://github.com/siderolabs/grpc-proxy/commit/28341d171dd4c1a52f46371ddfb5fd2240b79731) move out forward logic to method, allowing for use as `grpc.Server` not found handler. * [`89e28b4`](https://github.com/siderolabs/grpc-proxy/commit/89e28b42ee9dda8e36522b77e3771d9debc645e0) add reference to upstream grpc bug * [`00dd588`](https://github.com/siderolabs/grpc-proxy/commit/00dd588ae68adf4187a7fca87db45a73af4c834d) merge upstream `grpc.Server` changes changing the dispatch logic * [`77edc97`](https://github.com/siderolabs/grpc-proxy/commit/77edc9715de187dcbc9969e2f0e8a04d2087fd13) move to upstream `protobuf` from `gogo` * [`db71c3e`](https://github.com/siderolabs/grpc-proxy/commit/db71c3e7e812db8d75cb282dac38d953fcb436b3) initial commit, tested and working.

### Changes from siderolabs/net
12 commits

* [`19eb1c4`](https://github.com/siderolabs/net/commit/19eb1c4afb54b76fc38523834ec0490f41b50447) feat: switch to use `netip.Addr` instead of `net.IP` * [`5b21171`](https://github.com/siderolabs/net/commit/5b21171f9e5c5eb6b5ffc0110f48c6de451ffe34) chore: rename, rekres * [`409926a`](https://github.com/siderolabs/net/commit/409926aec1c3e659d6c245db4c0b90b0eaa4fdbc) fix: parse correctly some IPv6 CIDRs * [`b4b7181`](https://github.com/siderolabs/net/commit/b4b718179a1aa68e4f54422baf08ca3761723d2d) feat: add a way to filter list of IPs for the machine * [`0abe5bd`](https://github.com/siderolabs/net/commit/0abe5bdae8f85e4e976bc4d90e95dcb4be8fb853) feat: implement FilterIPs function * [`0519054`](https://github.com/siderolabs/net/commit/05190541b0fafc44fc6f3a2f8ba98d9b4a7b527a) feat: add ParseCIDR * [`52c7509`](https://github.com/siderolabs/net/commit/52c75099437634e312f54dd0941a44c626da9b66) feat: add a function to format IPs in CIDR notation * [`005a94f`](https://github.com/siderolabs/net/commit/005a94f8b36b5dfd56873cb168af9efceb072eeb) feat: add methods to manage CIDR list, check for non-local IPv6 * [`8b56890`](https://github.com/siderolabs/net/commit/8b568905bbcede25e492e4d224f85538678c2342) feat: add ValidateEndpointURI * [`402fa79`](https://github.com/siderolabs/net/commit/402fa79a10b453acbbc0079ce093652740c348db) chore: apply kres to get the latest build scripts * [`c7bc477`](https://github.com/siderolabs/net/commit/c7bc477975f89cba217cdff945ea097b00243972) chore: initial version of the package * [`393246a`](https://github.com/siderolabs/net/commit/393246ae638089425bb989427ec7793ae6a65826) chore: initial commit

### Changes from siderolabs/pkgs
39 commits

* [`8b975a7`](https://github.com/siderolabs/pkgs/commit/8b975a7f8456fd33db7c75191941a4bd2631d376) chore: bump deps * [`b153ce6`](https://github.com/siderolabs/pkgs/commit/b153ce616c29c56cd603c757efaab151a8ca4599) chore: bump deps * [`535b8f9`](https://github.com/siderolabs/pkgs/commit/535b8f9f7d4d4293f8b12f91cd7edcd4d6c946b1) chore: update packages version * [`66c77e9`](https://github.com/siderolabs/pkgs/commit/66c77e9669a7e1c2ca6a2477fac809a34e3ce3f6) feat: re-enable build kernel with BTF enabled * [`98ef073`](https://github.com/siderolabs/pkgs/commit/98ef0736130e660b95720db1ac23b7a6e1506f3e) feat: enable INET_DIAG and FANOTFY_PERMISSIONS * [`8fe5cbc`](https://github.com/siderolabs/pkgs/commit/8fe5cbca8243eaa0a60cf1aee80ab3e2ad987e81) chore: update dependencies * [`554c0fe`](https://github.com/siderolabs/pkgs/commit/554c0fe295719e425453e3d763559193b01a8b03) feat: add fanotify and kprobes kernel options * [`54d7e5c`](https://github.com/siderolabs/pkgs/commit/54d7e5c8db1e54c999ba0b4a466fe65c54e0c663) fix: drbd package name * [`b4cb9e2`](https://github.com/siderolabs/pkgs/commit/b4cb9e2125e20aea52a57aaba981326deb8ae0df) feat: add 'drbd' package * [`91e73b3`](https://github.com/siderolabs/pkgs/commit/91e73b325431f997b0e19d6ba083c48a6ae5ff2a) feat: update dependencies * [`b6d0d96`](https://github.com/siderolabs/pkgs/commit/b6d0d969942234defbf08745c57e3141152662e3) chore: bump kernel to 5.15.72 * [`b16dfe9`](https://github.com/siderolabs/pkgs/commit/b16dfe9699e43a03e47109c95ac0707cce038a49) chore: bump go to 1.19.2 * [`861cc32`](https://github.com/siderolabs/pkgs/commit/861cc32274db424975544b67e6f10b3568980a11) chore: bump kernel to 5.15.71 * [`0ac7773`](https://github.com/siderolabs/pkgs/commit/0ac77733506d2f0b0944ff569b6817ae44821bda) chore: use generic raspberry pi u-boot * [`d5633d4`](https://github.com/siderolabs/pkgs/commit/d5633d4838bd6e168b9c80f124540a30c29ae7be) chore: bump kernel to 5.15.70 * [`39c0d43`](https://github.com/siderolabs/pkgs/commit/39c0d4364fd4eedd281e46ce7d305f2562e2cf78) feat: add generic rpi_arm64_defconfig configuration * [`ed269ca`](https://github.com/siderolabs/pkgs/commit/ed269cabad82446095221e45078c8ba85bce5c2e) chore: bump kernel to 5.15.69 * [`f2f8333`](https://github.com/siderolabs/pkgs/commit/f2f83331f93a0a5d2dd1c013e2ff46900684096a) fix: no slack notifications on failure * [`6f0af33`](https://github.com/siderolabs/pkgs/commit/6f0af3390fc170f0cf57450adfada6a87de7ece4) chore: disable drone slack pipeline for renovate * [`32aea3f`](https://github.com/siderolabs/pkgs/commit/32aea3f005b93aaa91d52e4dfd04dd9ce9d564a9) chore: disable drone for renovate/dependabot * [`44579f0`](https://github.com/siderolabs/pkgs/commit/44579f0238993f529e2c141f42c99b32803fd6a5) fix: rollback xfsprogs to 5.18.0 * [`792c0e3`](https://github.com/siderolabs/pkgs/commit/792c0e32ef6b1cf13514dc2693c4c302e1440d3b) feat: add gasket driver package * [`07f1898`](https://github.com/siderolabs/pkgs/commit/07f1898b231390b85519f83638946ed65adacc64) chore: update deps * [`f78f410`](https://github.com/siderolabs/pkgs/commit/f78f410d193953e730aeb14f4e148e47dfa827fd) chore: enable conntrack zones and timestamps * [`049b3c6`](https://github.com/siderolabs/pkgs/commit/049b3c6f080b9af76b1b2e924baade69db27bc0b) chore: enable intel ice drivers * [`606ff32`](https://github.com/siderolabs/pkgs/commit/606ff32cb7e75b6975749b6250b68352b71e943b) chore: bump deps * [`eee5c8a`](https://github.com/siderolabs/pkgs/commit/eee5c8af13ee1fe0b1e660a9581d4f1b14158a39) chore: disable irc in conntrack * [`70e6c46`](https://github.com/siderolabs/pkgs/commit/70e6c460d7b3bd5e154a4e681858832afcf32368) chore: bump kernel to 5.15.64 * [`e510321`](https://github.com/siderolabs/pkgs/commit/e5103217e714bea04e06fd0c4940e84406cb68cf) chore: update renovate config * [`d1fa510`](https://github.com/siderolabs/pkgs/commit/d1fa510cc66ddc63a53482f6ced5573466049d49) feat: enable renovate bot * [`e427a77`](https://github.com/siderolabs/pkgs/commit/e427a778146664b988664008bfe20611f91216b0) chore: bump runc to v1.1.4 * [`40e1215`](https://github.com/siderolabs/pkgs/commit/40e12152a027eb509330c41db21680b9a662fa05) chore: enable nfsv4.2 client support * [`15efada`](https://github.com/siderolabs/pkgs/commit/15efadaa9db4b8dc8003359d6d0ed84016f54746) chore: bump kernel to 5.15.63 * [`e70e3c1`](https://github.com/siderolabs/pkgs/commit/e70e3c1af2b11d4b4646401a617b3d0efa2db4a3) fix: nvidia oss pkg name * [`30b8d79`](https://github.com/siderolabs/pkgs/commit/30b8d79b9ca3e463b5f403f01d39e64e89edc7b1) chore: bump kernel to 5.15.62 * [`862c392`](https://github.com/siderolabs/pkgs/commit/862c392b6defe3c9ce90f9b15eae154e021b0b4d) chore: bump gcc to 12.2.0 * [`2ecd14e`](https://github.com/siderolabs/pkgs/commit/2ecd14ede04637a581fbe7dcbbf612cdd6f9d882) fix: containerd version * [`01df058`](https://github.com/siderolabs/pkgs/commit/01df0583a430f3793f19725c920e942cf37efee4) feat: add NanoPi R4S configuration * [`d4cb33b`](https://github.com/siderolabs/pkgs/commit/d4cb33b9bdfb8c27ea86a42ea60a88e294129ad4) chore: bump containerd to v1.6.8

### Changes from siderolabs/siderolink
19 commits

* [`575c5cc`](https://github.com/siderolabs/siderolink/commit/575c5cc53bf854c8f6261d2b16bf52fe00868669) refactor: drop dependency on Talos machinery package * [`61ab1c4`](https://github.com/siderolabs/siderolink/commit/61ab1c43dd04faeb046c51dca7d891213762a31e) fix: include MachineStatusEvent into the list of supported events * [`16a84eb`](https://github.com/siderolabs/siderolink/commit/16a84ebe6759535c7a5284271418f7f04443e25f) chore: rename to siderolabs/siderolink * [`ca470c7`](https://github.com/siderolabs/siderolink/commit/ca470c735e6922b7d5afea91aef50c043f9563ee) chore: update Talos to the latest master, migrate netaddr -> netip/x * [`93b65f0`](https://github.com/siderolabs/siderolink/commit/93b65f0619c38de7641d75f31a0c88f88b6a46d4) fix: ignore 'exist' error on interface managmeent * [`3c4d9e0`](https://github.com/siderolabs/siderolink/commit/3c4d9e0fac88d30d9b794c254e4e015633156001) chore: move IP to interface binding into NewDevice * [`f0b5e39`](https://github.com/siderolabs/siderolink/commit/f0b5e39d523c633f3345bf06071571385db8aecc) feat: use kernel wireguard implementation when available * [`1d2b7e1`](https://github.com/siderolabs/siderolink/commit/1d2b7e13e7d055b5717dfb7f5111ec242e41ab01) feat: allow setting peer endpoint using peer event * [`5d085d6`](https://github.com/siderolabs/siderolink/commit/5d085d6eac27471a1c0e256c55d8f6ae01b55b8e) feat: expose `wgDevice.Peers` from the `wireguard.Device` wrapper * [`3a5be65`](https://github.com/siderolabs/siderolink/commit/3a5be65da5bbf3f565766993093578094d72e3eb) fix: use correct method to generate Wireguard private key * [`8318a7e`](https://github.com/siderolabs/siderolink/commit/8318a7e1747cb43ec5879d45df2e9a7e2533486e) feat: accept join token in Provision payload * [`b38c192`](https://github.com/siderolabs/siderolink/commit/b38c192875e10a0a9758dde42c7f17cf66694d61) fix: build on Windows * [`9902ad2`](https://github.com/siderolabs/siderolink/commit/9902ad2774f0655e050233854b9d28dad0431f6c) feat: pass request context and node address to the events sink adapter * [`d0612a7`](https://github.com/siderolabs/siderolink/commit/d0612a724a1b1336a2bc6a99ed3178e3e40f6d9b) refactor: pass in listener to the log receiver * [`d86cdd5`](https://github.com/siderolabs/siderolink/commit/d86cdd59ee7a0e0504b739a913991c272c7fb3f5) feat: implement logreceiver for kernel logs * [`f7cadbc`](https://github.com/siderolabs/siderolink/commit/f7cadbcdfbb84d367e27b5af32e89c138d72d9d7) fix: handle duplicate peer updates * [`0755b24`](https://github.com/siderolabs/siderolink/commit/0755b24d4682410b251a2a9d662960da15153106) feat: initial implementation of SideroLink * [`ee73ea9`](https://github.com/siderolabs/siderolink/commit/ee73ea9575a81be7685f24936b2c48a4508a159e) feat: add Talos events sink proto files and the reference implementation * [`1e2cd9d`](https://github.com/siderolabs/siderolink/commit/1e2cd9d38621234a0a6010e33b1bab264f4d9bdf) Initial commit

### Changes from siderolabs/tools
22 commits

* [`e8f92b3`](https://github.com/siderolabs/tools/commit/e8f92b324a97cc2dabdf8dbd27e7024ae184cbdc) chore: bump tools * [`3b5f89a`](https://github.com/siderolabs/tools/commit/3b5f89a4be0f6c754d1c5bf6dd9a295ff2b6eb94) chore: update dependencies * [`6402b99`](https://github.com/siderolabs/tools/commit/6402b9990964789ff257e9e83823f52dd93540d2) feat: update OpenSSL to 1.1.1r * [`00e91b1`](https://github.com/siderolabs/tools/commit/00e91b1a3ca59f2e0a999f8345556527460683a4) feat: update releases * [`a264809`](https://github.com/siderolabs/tools/commit/a26480967908b86d57c787e55c81f788bdf00ce4) chore: bump go to 1.19.2 * [`858cfe7`](https://github.com/siderolabs/tools/commit/858cfe7077b516d963149cd650a5e92f2c3c38ca) fix: no slack notifications on failure * [`ed85950`](https://github.com/siderolabs/tools/commit/ed859505f1ba7d6ace02e128e297b01b3eb62fee) chore: disable drone slack pipeline for renovate * [`5df6589`](https://github.com/siderolabs/tools/commit/5df658937f7bd667ceda8760e2e15ed85c80dc2c) chore: disable drone for renovate/dependabot * [`1f00d2e`](https://github.com/siderolabs/tools/commit/1f00d2e854cdf357c1192428bd44ee846af1b4e4) fix: revert gawk to 5.1.1 * [`feeda1f`](https://github.com/siderolabs/tools/commit/feeda1fc708a0cdb461ac5967ec34bf24ccc2b62) chore: bump grpc-go * [`8542014`](https://github.com/siderolabs/tools/commit/8542014568a101fb6c03a76c91e59dcfb1b893b6) chore: bump deps * [`e5c4968`](https://github.com/siderolabs/tools/commit/e5c496893fb71ff19a33daa4c86792ed03187356) chore: update renovate config * [`f34f94d`](https://github.com/siderolabs/tools/commit/f34f94daa300baab0803f22cecee65b57ee3c1fd) chore: update renovate config * [`cef4cc6`](https://github.com/siderolabs/tools/commit/cef4cc67342c06904258bcf4b7ec681d4c732d53) chore: update renovate config * [`bab8e9e`](https://github.com/siderolabs/tools/commit/bab8e9ee8d0fc2dc1b5676a45175b507d8927e49) chore: add libbpf to tools * [`0a15f7b`](https://github.com/siderolabs/tools/commit/0a15f7bb35f479fbf5551ea4bf02f3716783e33f) chore: build pahole properly * [`a322d06`](https://github.com/siderolabs/tools/commit/a322d066483814db80a15b8c0c7f44224b134429) chore: remove img * [`c7ff47b`](https://github.com/siderolabs/tools/commit/c7ff47b27962cf0f6a95e07c6f45aa2a3c2c5c8b) feat: enable renovate dependency updates (3/3) * [`6e095cf`](https://github.com/siderolabs/tools/commit/6e095cf86a6f734b2f07cc1b854a9a37b055cacc) feat: enable renovate dependency updates (2/n) * [`bad1ad1`](https://github.com/siderolabs/tools/commit/bad1ad17f7fd1208fcbb70b950320f805a765868) feat: add renovatebot * [`7d6f9c3`](https://github.com/siderolabs/tools/commit/7d6f9c35a81392918560ea0c20b3c06b18501ea0) chore: bump gcc to 12.2.0 * [`2719b4b`](https://github.com/siderolabs/tools/commit/2719b4be551134a9d70ab235f56889708377f3c5) chore: bump toolchain

### Dependency Changes * **cloud.google.com/go/compute/metadata** v0.2.1 **_new_** * **github.com/BurntSushi/toml** v1.2.0 -> v1.2.1 * **github.com/aws/aws-sdk-go** v1.44.76 -> v1.44.136 * **github.com/containerd/containerd** v1.6.8 -> v1.6.9 * **github.com/cosi-project/runtime** v0.1.1 -> v0.2.0-alpha.3 * **github.com/docker/docker** v20.10.17 -> v20.10.21 * **github.com/emicklei/dot** v1.0.0 -> v1.1.0 * **github.com/fsnotify/fsnotify** v1.5.4 -> v1.6.0 * **github.com/gdamore/tcell/v2** v2.5.2 -> v2.5.3 * **github.com/google/go-cmp** v0.5.8 -> v0.5.9 * **github.com/google/nftables** 2eca00135732 -> 130caa4c31c9 * **github.com/hetznercloud/hcloud-go** v1.35.2 -> v1.37.0 * **github.com/insomniacslk/dhcp** 509691fd59ec -> 5308ebe5334c * **github.com/jsimonetti/rtnetlink** v1.2.2 -> v1.2.3 * **github.com/mdlayher/ethtool** 856bd6cb8a38 -> 0e16326d06d1 * **github.com/mdlayher/genetlink** v1.2.0 -> v1.3.0 * **github.com/mdlayher/netlink** v1.6.0 -> v1.7.0 * **github.com/opencontainers/image-spec** c5a74bcca799 -> v1.1.0-rc2 * **github.com/packethost/packngo** v0.25.0 -> v0.29.0 * **github.com/pmorjan/kmod** v1.0.0 -> v1.1.0 * **github.com/rivo/tview** 0e6b21a48e96 -> 04a46906d2e9 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.9 -> v1.0.0-beta.10 * **github.com/siderolabs/crypto** v0.4.0 **_new_** * **github.com/siderolabs/discovery-api** v0.1.1 **_new_** * **github.com/siderolabs/discovery-client** v0.1.1 -> v0.1.3 * **github.com/siderolabs/extras** v1.2.0 -> v1.3.0-alpha.0-2-gb155fa0 * **github.com/siderolabs/gen** v0.4.0 **_new_** * **github.com/siderolabs/go-blockdevice** v0.4.1 **_new_** * **github.com/siderolabs/go-circular** v0.1.0 **_new_** * **github.com/siderolabs/go-cmd** v0.1.1 **_new_** * **github.com/siderolabs/go-debug** v0.2.2 **_new_** * **github.com/siderolabs/go-kmsg** v0.1.2 **_new_** * **github.com/siderolabs/go-kubeconfig** v0.1.0 **_new_** * **github.com/siderolabs/go-loadbalancer** v0.2.1 **_new_** * **github.com/siderolabs/go-procfs** v0.1.1 **_new_** * **github.com/siderolabs/go-retry** v0.3.2 **_new_** * **github.com/siderolabs/go-smbios** v0.3.1 **_new_** * **github.com/siderolabs/go-tail** v0.1.0 **_new_** * **github.com/siderolabs/grpc-proxy** v0.4.0 **_new_** * **github.com/siderolabs/net** v0.4.0 **_new_** * **github.com/siderolabs/pkgs** v1.2.0-8-g970860d -> v1.3.0-alpha.0-38-g8b975a7 * **github.com/siderolabs/siderolink** v0.3.0 **_new_** * **github.com/siderolabs/talos/pkg/machinery** v1.3.0-alpha.1 **_new_** * **github.com/siderolabs/tools** v1.2.0 -> v1.3.0-alpha.0-21-ge8f92b3 * **github.com/spf13/cobra** v1.5.0 -> v1.6.1 * **github.com/stretchr/testify** v1.8.0 -> v1.8.1 * **github.com/u-root/u-root** v0.9.0 -> v0.10.0 * **github.com/vmware-tanzu/sonobuoy** v0.56.9 -> v0.56.11 * **go.etcd.io/etcd/api/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/pkg/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/etcdutl/v3** v3.5.4 -> v3.5.5 * **go.uber.org/atomic** v1.9.0 -> v1.10.0 * **go.uber.org/zap** v1.22.0 -> v1.23.0 * **go4.org/netipx** 797b0c90d8ab **_new_** * **golang.org/x/net** 3211cb980234 -> v0.2.0 * **golang.org/x/sync** 886fb9371eb4 -> v0.1.0 * **golang.org/x/sys** fbc7d0a398ab -> v0.2.0 * **golang.org/x/term** a9ba230a4035 -> v0.2.0 * **golang.org/x/time** e5dcc9cfc0b9 -> v0.2.0 * **golang.zx2c4.com/wireguard/wgctrl** 3d4a969bb56b -> 97bc4ad4a1cb * **google.golang.org/grpc** v1.48.0 -> v1.50.1 * **k8s.io/api** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/apimachinery** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/apiserver** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/client-go** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/component-base** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/cri-api** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/klog/v2** v2.70.1 -> v2.80.1 * **k8s.io/kubectl** v0.25.0 -> v0.26.0-beta.0 * **k8s.io/kubelet** v0.25.0 -> v0.26.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.65 -> v1.2.66 Previous release can be found at [v1.2.0](https://github.com/siderolabs/talos/releases/tag/v1.2.0) ## [Talos 1.3.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.3.0-alpha.1) (2022-10-28) Welcome to the v1.3.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### kube-apiserver Audit Policy Talos now supports setting custom audit policy for `kube-apiserver` in the machine configuration. ### Kernel Command Line ip= Argument Talos now supports referencing interface name via `enxMAC` address notation: ``` ip=172.20.0.2::172.20.0.1:255.255.255.0::enx7085c2dfbc59 ``` ### etcd Consistency Check Talos enables [--experimental-compact-hash-check-enabled](https://github.com/etcd-io/etcd/pull/14120) option by default to improve etcd store consistency guarantees. This options is only available with etcd >= v3.5.5, so Talos doesn't support version of etcd before v3.5.5. ### Exocale Platform Talos now supports new platform: Exoscale. Exoscale provides a firewall, TCP load balancer and autoscale groups. It works well with CCM and Kubernetes node autoscaler. ### Kernel Modules Talos now supports settings kernel module parameters. Eg: ```yaml machine: kernel: modules: - name: "br_netfilter" parameters: - nf_conntrack_max=131072 ``` ### KubeSpan KubeSpan MTU link size is now configurable via `network.kubespan.mtu` setting in the machine configuration. ### Routes Talos now supports setting MTU for a specific route. ### Nano Pi R4S Talos now supports the Nano Pi R4S SBC. ### Raspberry Generic Images The Raspberry Pi 4 specific image has been deprecated and will be removed in the v1.4 release of Talos. Talos now ships a generic Raspberry Pi image that should support more Raspberry Pi variants. Refer to the docs at https://www.talos.dev/v1.3/talos-guides/install/single-board-computers/rpi_generic/ to find which ones are supported. ### Encryption with secretbox By default new clusters will use secretbox for encryption instead of AESCBC. If both are configured secretbox will take precedence. Old clusters may keep using AESCBC. To enable secretbox you may add an encryption secret at `cluster.secretboxEncryptionSecret`. You should keep `aescbcEncryptionSecret` however, even if secretbox is enabled older data will still be encrypted with AESCBC. How to generate the secret: ```bash dd if=/dev/random of=/dev/stdout bs=32 count=1 | base64 ``` ### Static Pod Manifests The directory "/etc/kubernetes/manifests" is now deprecated. Static pods should always be configured in machine.pods. To reenable support you may set `machine.kubelet.disableManifestsDirectory`. Eg: ```yaml machine: kubelet: disableManifestsDirectory: no ``` ### Component Updates * Kubernetes: v1.26.0-alpha.2 * Flannel: v0.20.0 * CoreDNS: v1.10.0 * etcd: v3.5.5 * Linux: 5.15.74 Talos is built with Go 1.19.2. ### Contributors * Andrey Smirnov * Noel Georgi * Andrey Smirnov * Michal Witkowski * Artem Chernyshev * Dmitriy Matrenichev * Artem Chernyshev * Serge Logvinov * Andrey Smirnov * Philipp Sauter * Steve Francis * Alexey Palazhchenko * Andrew Rynhard * Tim Jones * Utku Ozdemir * Andrew Rynhard * Kris Reeves * Marvin Drees * Spencer Smith * Branden Cash * Brandon Nason * Cameron Brunner * DJAlPee * Daniel Low * Gerard de Leeuw * Jack Wink * Jon Stelly * Matt Zahorik * Maxim Makarov * Olli Janatuinen * Pau Campana * Rubens Farias * Sander Maijers * Seán C McCord * Spencer Smith * emattiza * killcity ### Changes
149 commits

* [`869f3b5a5`](https://github.com/siderolabs/talos/commit/869f3b5a51ac783e8b0a5a31a103c212a068672b) feat: network configuration improvements on the OpenStack platform * [`29f2195e1`](https://github.com/siderolabs/talos/commit/29f2195e130ecf66a911d0c75343486ee7c86046) feat: support exoscale cloud * [`8b4ae08d1`](https://github.com/siderolabs/talos/commit/8b4ae08d1c42a2cab4bbf0daac090e0882b5d4e9) fix: etcd snapshot command on Windows * [`8bfa7ac1d`](https://github.com/siderolabs/talos/commit/8bfa7ac1d6012746bf7264528eac5cacdd752e2b) feat: platform metadata resource * [`7e50e24c0`](https://github.com/siderolabs/talos/commit/7e50e24c0187e514876222857d44eedda79acc5a) fix: properly cleanup legacy static pod manifests directory * [`6ee47bcc6`](https://github.com/siderolabs/talos/commit/6ee47bcc61bd5b8684c43c0d8c020c574631c832) fix: support serving config for qemu launcher on IPv6 * [`6c3d11b49`](https://github.com/siderolabs/talos/commit/6c3d11b49e94b33ccfdf29f93d3233e480b5e7f0) docs: admission control patch note * [`4ea3b99b5`](https://github.com/siderolabs/talos/commit/4ea3b99b527406b0bbf9cbfd22867431b143ed49) fix: serve static pod files on 127.0.0.1 instead of localhost * [`23842114f`](https://github.com/siderolabs/talos/commit/23842114f077d98cf7bdbf8912454623dff41bbb) feat: support encryption with secretbox * [`f6773c472`](https://github.com/siderolabs/talos/commit/f6773c472c0c1094045a26e34be2472a98dad510) docs: talos support on equinix metal * [`b307160f6`](https://github.com/siderolabs/talos/commit/b307160f613f2544c70be115c9a1ae0a7439ec52) chore: bump dependencies * [`d7edd0e2e`](https://github.com/siderolabs/talos/commit/d7edd0e2e6ec5e4cba8bfa119d244c7be09078d9) refactor: use go-circular, go-kubeconfig, and go-tail * [`c6e1702ec`](https://github.com/siderolabs/talos/commit/c6e1702eca2d310f6fad52e0f00bc91d7d6c4996) feat: use URL-based manifests to present static pods to the kubelet * [`136a795e5`](https://github.com/siderolabs/talos/commit/136a795e55b5be5f093aaf6b07039e86df971674) docs: update system requirements to mention dedicated disk usage * [`879e8c0bf`](https://github.com/siderolabs/talos/commit/879e8c0bfe31f6b35f8833cf55624cd934ded50b) chore: update kernel with BTF support * [`ceb0cd99a`](https://github.com/siderolabs/talos/commit/ceb0cd99ae0e29cadf69e121afdc439f3296ff74) feat: implement Talos API auth using SideroV1 signatures * [`e6fba7d3b`](https://github.com/siderolabs/talos/commit/e6fba7d3bc83d008518d7a032b309ddd212e0f81) chore: update dependencies * [`93e55b85f`](https://github.com/siderolabs/talos/commit/93e55b85f207060d053ba9f16267d98c2599a2df) chore: bump golangci-lint to v1.50.0 * [`aa3d9b4ca`](https://github.com/siderolabs/talos/commit/aa3d9b4ca60f0a7e47867e1de134753eb914606b) fix: regenerate cert on node labeling retry * [`021c73c35`](https://github.com/siderolabs/talos/commit/021c73c35233ee5e6cb9cf5e83336eeb70ae05d3) fix: lowercase nodename * [`b902036e1`](https://github.com/siderolabs/talos/commit/b902036e12843d6348d945097d3826a50b040b25) docs: update office hours time link * [`7fcb8c681`](https://github.com/siderolabs/talos/commit/7fcb8c68164d72f14bca284daffc69605002acb5) feat: update Flannel to v0.20.0 * [`dc70d892a`](https://github.com/siderolabs/talos/commit/dc70d892a341f0694be0c0ff5517b63ea6bbadd9) fix: support setting KubeSpan link MTU * [`7d52bad37`](https://github.com/siderolabs/talos/commit/7d52bad370d544d1a2862891e089426dff7c52a3) feat: update Linux to 5.15.73 * [`9c78b3aff`](https://github.com/siderolabs/talos/commit/9c78b3aff48fd95f48ab2c951f7eb61273338e9a) feat: update Kubernetes to v1.26.0-alpha.2 * [`94913a672`](https://github.com/siderolabs/talos/commit/94913a6727e9a802d2e14c141a831a8fddc8d9b2) docs: add lofty to talos adopters * [`0a0bdfe16`](https://github.com/siderolabs/talos/commit/0a0bdfe164625013e807cf5a08f590835894bf92) docs: add Tremor Video to adopters * [`b7b1d4fd6`](https://github.com/siderolabs/talos/commit/b7b1d4fd6a492c8e4c73b9f7f17449241903f868) feat: use readonly containers * [`d210338e3`](https://github.com/siderolabs/talos/commit/d210338e33438919fc8d2d83fc479981077d5164) fix: skip protobuf full unmarshaling for some talosctl commands * [`b3c679d18`](https://github.com/siderolabs/talos/commit/b3c679d18e698092795725e6fcb05d6569d681b1) chore: bump dependencies * [`993743f63`](https://github.com/siderolabs/talos/commit/993743f63495a59020670619abde5a0d5cd322e2) fix: skip hostname via DHCP on OpenStack platform * [`db076e7b5`](https://github.com/siderolabs/talos/commit/db076e7b5afca7c725c4c6876a7e05d643a219a1) feat: pin interface by mac address in cmdline args * [`63de93722`](https://github.com/siderolabs/talos/commit/63de937227362064a05fa3a9ba11f55891458cc7) fix: update go-smbios to v0.3.1 * [`49e9f808e`](https://github.com/siderolabs/talos/commit/49e9f808e7b14af90959c7fca9457128e82f9cb5) chore: bump kernel and go * [`c7372144d`](https://github.com/siderolabs/talos/commit/c7372144de4b953ebe2494676143ea6d0e53e666) docs: add constraints to upgrade docs * [`c71c8ca18`](https://github.com/siderolabs/talos/commit/c71c8ca18fd4bb7dcae2f69ea253c16b9abd7a9d) docs: consolidate, simplify and correct various docs * [`06f76bfeb`](https://github.com/siderolabs/talos/commit/06f76bfebb14e7d826b8c7efe4564a94d841a74a) chore: bump dependencies * [`b1c421b9a`](https://github.com/siderolabs/talos/commit/b1c421b9ad90d36e8a3562aacdcc30c521da585a) chore: publish ami's with imds v2 enabled * [`195c40ab5`](https://github.com/siderolabs/talos/commit/195c40ab5908c3bcd0c8ecf5b6f7275bb9b7a499) docs: add information about applicable use cases of disk encryption * [`54a687fb8`](https://github.com/siderolabs/talos/commit/54a687fb8e68f3669ff140d37ff3fd01595a494d) docs: consolidate and expand on discovery service * [`139c62d76`](https://github.com/siderolabs/talos/commit/139c62d762c2a9001808d4e1bed38145ea86a95d) feat: allow upgrades in maintenance mode (only over SideroLink) * [`48dee4805`](https://github.com/siderolabs/talos/commit/48dee480577c9d1bb4620f78c6b4bbeba0f0d0bc) feat: support mtu for routes * [`1c43c72ae`](https://github.com/siderolabs/talos/commit/1c43c72aebd1a2bcc1991787dcd94c8bab00df42) docs: fix talos required kernel params * [`67cc45ae3`](https://github.com/siderolabs/talos/commit/67cc45ae3f9351cf5ae27c2c1a4c5d762a2d8b77) release(v1.3.0-alpha.0): prepare release * [`18c377a4d`](https://github.com/siderolabs/talos/commit/18c377a4d1ce046b310e3609033e9c1f39f9337b) feat: customize audit policy * [`23c9ea46b`](https://github.com/siderolabs/talos/commit/23c9ea46bba20d8b7cc336bbc64e04af46cccf5d) fix: raspberry pi install * [`f17cdee16`](https://github.com/siderolabs/talos/commit/f17cdee167cfd6d673e2ed71fd5c8d28399a80f3) feat: jsonpath filter for talosctl get outputs * [`6bd3cca1a`](https://github.com/siderolabs/talos/commit/6bd3cca1a8d206fb40199a9f0352aa2670fca754) chore: generic raspberry pi images * [`d914ab8bb`](https://github.com/siderolabs/talos/commit/d914ab8bb4a34cdb5ffc396a20a32a437c5989e1) chore: add vulncheck tool as a linter * [`a0151aa13`](https://github.com/siderolabs/talos/commit/a0151aa13e63b24aba7e39082f6cef3dac923a22) feat: add generic rpi u-boot support * [`30f851d09`](https://github.com/siderolabs/talos/commit/30f851d0931f5d6767e13142876c94dac67ec38b) chore: bump dependences * [`8b2235c3b`](https://github.com/siderolabs/talos/commit/8b2235c3b6de64abb15bf77e9648bf6bebc18e1f) fix: lookup Equinix Metal bond slaves using 'permanent addr' * [`b3257ebb1`](https://github.com/siderolabs/talos/commit/b3257ebb1c529a8f266ba3852d5e4191e0261a79) chore: bump kernel to 5.15.70 * [`0b2767c16`](https://github.com/siderolabs/talos/commit/0b2767c1646e84ce147030692f3904b9feb02b3e) feat: implement 'permanent addr' in link statuses * [`c90e20251`](https://github.com/siderolabs/talos/commit/c90e20251d09a9bedcbd8b1a2055de5e126fc97e) fix: kubeconfig permission * [`fc48849d0`](https://github.com/siderolabs/talos/commit/fc48849d00c185442fb37c72e2c20462cc573a69) chore: move maps/slices/ordered to gen module * [`8b09bd4b0`](https://github.com/siderolabs/talos/commit/8b09bd4b0400f17ef543f0d117ae35e4ba2356cb) feat: update Kubernetes to v1.26.0-alpha.1 * [`276d4175b`](https://github.com/siderolabs/talos/commit/276d4175bbd168d12409a1e96b191abdf09f2ff0) chore: bump extension versions in testing * [`357b770cb`](https://github.com/siderolabs/talos/commit/357b770cb593196fccaf9b6ba3cd740463351a07) fix: cryptsetup delete slot * [`711128839`](https://github.com/siderolabs/talos/commit/7111288393ae4dfdfa7331e39df1803724bc93c0) fix: continue applying bootstrap manifests on some errors * [`ce12c7b38`](https://github.com/siderolabs/talos/commit/ce12c7b3805da65315309a465aeed1764f0ce20a) chore: update COSI runtime to v0.2.0-alpha.1 * [`1b435c0b3`](https://github.com/siderolabs/talos/commit/1b435c0b36a8d0d3e48c5a5e6121117933deeb69) chore: bump kernel + ice drivers * [`18e041f1e`](https://github.com/siderolabs/talos/commit/18e041f1ecb88d0b1e8e874d9b1fb580bc7c2297) docs: fix typo in patching example * [`0ad6452ca`](https://github.com/siderolabs/talos/commit/0ad6452ca152afef2f3c0e97a2255a237b30941a) feat: update CoreDNS to v1.10.0 * [`479f3f52e`](https://github.com/siderolabs/talos/commit/479f3f52ee7149ff2a39bec3d8f78b59978af70a) chore: bump dependencies * [`e07c6ae99`](https://github.com/siderolabs/talos/commit/e07c6ae99ec347735cf0316294ef0c54ebc45234) feat: update Kubernetes to v1.25.1 * [`13fdfaffc`](https://github.com/siderolabs/talos/commit/13fdfaffc4a0eb812cd63c5d188efd4aff6da51c) test: fix up default branch name * [`ef181321a`](https://github.com/siderolabs/talos/commit/ef181321a5be4d03e4f87aab1483b95a8e61f0fe) docs: add component diagram; K8s & Talos Linux * [`aade73643`](https://github.com/siderolabs/talos/commit/aade7364357da6644e8b70ad1dd939130f2fe470) docs: fix missing variable in OpenEBS docs * [`472590aa8`](https://github.com/siderolabs/talos/commit/472590aa82d16e1bd3825ecc8106886e7e1b9053) chore: return InvalidArgument on invalid config in maintenance mode * [`e5cabd42c`](https://github.com/siderolabs/talos/commit/e5cabd42cc7f86bee5486f73fa4068382bf6a7fb) feat: enable etcd consistency hashcheck * [`015535d90`](https://github.com/siderolabs/talos/commit/015535d9051dea243f439b385577d17fd57a122e) fix: update discovery client with the redirect fix * [`d0c8e7699`](https://github.com/siderolabs/talos/commit/d0c8e7699cf3e2415c5712ff9ff620c38857a0dc) chore: bump kernel and go * [`985b0c2e7`](https://github.com/siderolabs/talos/commit/985b0c2e796006f401376ebf30a1ce888d90a1c9) chore: remove go.work.sum * [`69124f102`](https://github.com/siderolabs/talos/commit/69124f10263bdabc556b58b98a3e1f129b85b8ab) feat: update etcd to v3.5.5 * [`1985a796c`](https://github.com/siderolabs/talos/commit/1985a796c0d5a984c397754445b33827f5690806) docs: update docs for pod security * [`94b088f02`](https://github.com/siderolabs/talos/commit/94b088f02f8f8e5b63f0c38e8e091f2ba3329dde) fix: set etcd options consistently * [`92ae7ef4b`](https://github.com/siderolabs/talos/commit/92ae7ef4b1abe0a510fea31e0fde2566281f38b1) fix: fix protoenc encoding for enums and types with custom encoders * [`93809017c`](https://github.com/siderolabs/talos/commit/93809017c594b1faf1405932d884852eb0ce567c) docs: cpu scaling governor knowledgebase * [`7b270ff33`](https://github.com/siderolabs/talos/commit/7b270ff33d6bf74d1fa195c07f98233098b337e9) test: fix api controller test * [`2dadcd669`](https://github.com/siderolabs/talos/commit/2dadcd6695003eb940848583caa6ade53ef94fa0) fix: stop worker nodes from acting as apid routers * [`9eaf33f3f`](https://github.com/siderolabs/talos/commit/9eaf33f3f274e746ca1b442c0a1a0dae0cec088f) fix: never sign client certificate requests in trustd * [`436749124`](https://github.com/siderolabs/talos/commit/43674912479d3fb58c30e350fea9c4daf4ba45d4) feat: environment vars for extension service * [`0c0cb671e`](https://github.com/siderolabs/talos/commit/0c0cb671ead1f514b1f1eb89e8d78f455e1efedb) chore: mark machine configuration validation failure as InvalidArgument * [`f424e5340`](https://github.com/siderolabs/talos/commit/f424e53404db61bbdbcbe8fab7cfec91785aa628) fix: stop containers more thoroughly * [`12827b861`](https://github.com/siderolabs/talos/commit/12827b861c13bb9b83a2f0ea2960582e8be319f0) chore: move "implements" checks to compile time * [`3a67c42cb`](https://github.com/siderolabs/talos/commit/3a67c42cbfdbd565e0af500d97c264ef6095637b) fix: kill the task processes when cleaning up stale task * [`14a79e325`](https://github.com/siderolabs/talos/commit/14a79e325bf0ffa107aaee9c07d3501b7010693c) chore: bump dependencies * [`9beee92e7`](https://github.com/siderolabs/talos/commit/9beee92e71e712a2af24dee612e27c30cac39d0d) docs: fix double vv in Kubernetes version * [`688272515`](https://github.com/siderolabs/talos/commit/6882725157f4c2ea79c248f79160e362be6c2c07) fix: use different username for Talos Kubernetes API access * [`161a52a9e`](https://github.com/siderolabs/talos/commit/161a52a9ef60eb9c1c1a6c31b06d06894456300c) feat: check apid client certificate extended key usage * [`9dadc4a59`](https://github.com/siderolabs/talos/commit/9dadc4a599f52cc564f5411dd35bc981e482d24a) fix: include all node addresses into etcd cert SANs * [`71bfd3e43`](https://github.com/siderolabs/talos/commit/71bfd3e43cdc9790d3cb7a134c3b49256b1942a1) feat: update CoreDNS to 1.9.4 * [`9df8f1ff1`](https://github.com/siderolabs/talos/commit/9df8f1ff1aebb24a6b0649ba491b10b23a0b2198) fix: list COSI APIs for the apid authenticator * [`31462450f`](https://github.com/siderolabs/talos/commit/31462450f19700dd6691ebc4b0c18edca4f6a1b7) fix: pass a pointer to specs.Mount into protoenc.Marshal * [`e626540df`](https://github.com/siderolabs/talos/commit/e626540dfb470386d0750f2f8bbaf4b5cb36b203) chore: avoid double API request logging in trustd * [`f62d17125`](https://github.com/siderolabs/talos/commit/f62d17125b8c1b26b0b62d22c2846f3a2ece37d1) chore: update crypto to use new import path siderolabs/crypto * [`ef27dd855`](https://github.com/siderolabs/talos/commit/ef27dd8553ee0e5467c3baaf4be18d1ccb30dad1) chore: bump dependencies * [`6472ae00b`](https://github.com/siderolabs/talos/commit/6472ae00b21c0f637b1e6610a8f3f71a1b775628) fix: automatically discard VIPs for etcd advertised addresses * [`5e21cca52`](https://github.com/siderolabs/talos/commit/5e21cca52d7462240bb42aafa225ee97d08bdc25) feat: support setting kernel parameters * [`bd56621cd`](https://github.com/siderolabs/talos/commit/bd56621cdf50d25013756a8792dc7b4d5354396f) feat: add structprotogen tool * [`cdb6bb2cc`](https://github.com/siderolabs/talos/commit/cdb6bb2cc78685c218506c61a477c8a8e569e861) feat: add Nano Pi R4S support * [`36c1f1d6e`](https://github.com/siderolabs/talos/commit/36c1f1d6e6aa50379343acba5348d8cc038b137e) fix: flip the client-server version check * [`cd6c53a97`](https://github.com/siderolabs/talos/commit/cd6c53a979236543afc302a67da627ee633883b3) docs: fork docs for v1.3 * [`0847400f7`](https://github.com/siderolabs/talos/commit/0847400f728d67889b9f740a0359eb916108d8ea) fix: prevent panic on health check if a member has no IPs * [`7471d7f01`](https://github.com/siderolabs/talos/commit/7471d7f0174a5240fa3c4cd2f16325ec2a4f1810) feat: update Flannel to v0.19.2 * [`148c75cfb`](https://github.com/siderolabs/talos/commit/148c75cfb99537f64d43a3add3259bf591cb79a9) docs: consolidate the control-plane documentation * [`353154281`](https://github.com/siderolabs/talos/commit/353154281a4cf72076b99160e50e617109f72996) fix: drop kube-system SA default binding * [`4f37b668b`](https://github.com/siderolabs/talos/commit/4f37b668befdbd26bc2d32106e0bcc654f7e6119) chore: remove capi hacks * [`1369afea8`](https://github.com/siderolabs/talos/commit/1369afea853423f22fde20effd431c3f8d906a9d) docs: make 1.2.0 docs default ones * [`7627cb0e3`](https://github.com/siderolabs/talos/commit/7627cb0e30a8b2a5a1cc30906b547511c9d3c98b) docs: add new `talosctl gen secrets` * [`8aa60a37a`](https://github.com/siderolabs/talos/commit/8aa60a37a6ea57bf54d558c7a2f54d806fad3173) chore: bump kernel to 5.15.64 * [`a798dbd5d`](https://github.com/siderolabs/talos/commit/a798dbd5d2d9bc6d1410a56035550d44de934950) docs: update docs for upcoming 1.2.0 release * [`b2fec3c97`](https://github.com/siderolabs/talos/commit/b2fec3c975dba7b0bc2dc7d5447e62350057061b) fix: properly handle `configContext` being `nil` in Talos client * [`1c0977b3a`](https://github.com/siderolabs/talos/commit/1c0977b3af22f9f4b61b80ca6dcedf14a5ef63ae) fix: change the type of returned gRPC connection object from the client * [`41848e421`](https://github.com/siderolabs/talos/commit/41848e421496184008ad2302e3cb03a882c0f5bf) fix: expose Talos client gRPC connection via the function `Conn` * [`2e9be4af8`](https://github.com/siderolabs/talos/commit/2e9be4af8b521eca985c425f62dfc7a59d19e7da) chore: bump dependencies * [`d283aba3a`](https://github.com/siderolabs/talos/commit/d283aba3a3670cfde8ab9137deba3ab3b343906f) test: fix cli reboot test * [`0b339a9dc`](https://github.com/siderolabs/talos/commit/0b339a9dc508327347777619749ff1e2c3e47f37) feat: track progress of action API calls * [`072349812`](https://github.com/siderolabs/talos/commit/072349812506c5cd32159bb14bab5b294ee59811) fix: update COSI to the version with gRPC Wait fix * [`89d57aa81`](https://github.com/siderolabs/talos/commit/89d57aa816a57448d6e350698a8f6a5d128209ac) fix: always abort the maintenance service * [`f6fa74619`](https://github.com/siderolabs/talos/commit/f6fa7461932462160f40f670a5252fbc2981bdc3) fix: limit apid backoff max delay * [`d7ef346db`](https://github.com/siderolabs/talos/commit/d7ef346db8ea7d4f7676ae5e032a3c0d06823d47) fix: get command in the case 'nodes' are not set in the context * [`4e9c32256`](https://github.com/siderolabs/talos/commit/4e9c322564d7f65c82d636a9f80c0c5354455967) fix: correctly render hosts.toml with multiple endpoints * [`cdd0f08bc`](https://github.com/siderolabs/talos/commit/cdd0f08bc5d8d47bc2d21745ee5a13ced3632c8a) feat: check client <> server version in some Talos commands * [`446b0af58`](https://github.com/siderolabs/talos/commit/446b0af58bf273712374472bfa2777de5b7ac46f) chore: bump kernel and runc * [`8c203ce9b`](https://github.com/siderolabs/talos/commit/8c203ce9b1722c5832c506857cb56e14e2a34fe1) feat: remove the machine from the discovery service on reset * [`b59ca5810`](https://github.com/siderolabs/talos/commit/b59ca5810e6cf75f6a3042a47535431110004201) chore: move from inet.af/netaddr to net/netip and go4.org/netipx * [`053af1d59`](https://github.com/siderolabs/talos/commit/053af1d59ea266b84bb049460f92b33b32c1b82e) fix: update etcd certificates when node addresses changes * [`11edb2c6f`](https://github.com/siderolabs/talos/commit/11edb2c6f84fbbfba437361ce4dcd70c50eb08d8) test: re-enable upgrade tests * [`0310e2089`](https://github.com/siderolabs/talos/commit/0310e20890b11e1f4015e923eb9984aea1188d20) chore: bump github.com/siderolabs/protoenc to v0.1.5 * [`29bd63240`](https://github.com/siderolabs/talos/commit/29bd632401ca694df0a2ab921a2a525b4c3440d8) chore: remove old build tags syntax * [`b500d0aa9`](https://github.com/siderolabs/talos/commit/b500d0aa9052ab5066eb6cde06bcdac3e998705a) chore: bump k8s to v1.25.0 * [`29e574be7`](https://github.com/siderolabs/talos/commit/29e574be74c96211fd010ee5bd06675898f04db8) docs: update to v1.2.0-beta.1 * [`26b549f2a`](https://github.com/siderolabs/talos/commit/26b549f2a12c3486b52a8877b8a0a4f985695c7d) chore: bump dependencies * [`8c3ac4c42`](https://github.com/siderolabs/talos/commit/8c3ac4c42bff1f1678ddb62e0f20a9c419460ad4) chore: limit GOMAXPROCS for Talos services * [`361e85b74`](https://github.com/siderolabs/talos/commit/361e85b7443f6f4ff24fbf99a9f9276b73b73ed4) fix: properly read kexec disabled sysctl * [`cfe6c2bc2`](https://github.com/siderolabs/talos/commit/cfe6c2bc2d42ca28f3a5b3217aa4d126777e3db6) docs: nvidia oss drivers * [`2f2d97b6b`](https://github.com/siderolabs/talos/commit/2f2d97b6b5663a0873db9d47b7706f2c0a531d8c) fix: don't wait for the hostname in maintenance mode * [`b15a63924`](https://github.com/siderolabs/talos/commit/b15a6392465aa2aa0df231c622ca1762972ccd20) chore: bump kernel to 5.15.62 * [`a0d94be30`](https://github.com/siderolabs/talos/commit/a0d94be30d3dcf41b2b8b34a1caa6928a029f81a) fix: stable default hostname bias * [`da4cd34ef`](https://github.com/siderolabs/talos/commit/da4cd34ef5c5a01cfc3c3ee56b3f8c2f77997b49) feat: update etcd advertised peer addresses on the fly * [`faf92ce01`](https://github.com/siderolabs/talos/commit/faf92ce01661c5a9a86f9e579da3a2822d93f1f6) chore: bump kubernetes to v1.25.0-rc.1 * [`52de919e3`](https://github.com/siderolabs/talos/commit/52de919e34789c36c4ee71ca133240b50b068064) chore: bump containerd to v1.6.8 * [`7d43fc79b`](https://github.com/siderolabs/talos/commit/7d43fc79b1e913d51f111ecc7c2c8b3bfb36e679) fix: make 'ca', 'crt' and 'key' flags optional for 'talosctl config add' * [`fd467e02c`](https://github.com/siderolabs/talos/commit/fd467e02c1edcfc0eff656392ece5dd8ba1114f2) fix: handle grub config being empty in the `Revert` function * [`9492aca65`](https://github.com/siderolabs/talos/commit/9492aca652eec4d4049fef1c8d141696ed72a197) fix: clean up `cancelCtxMu` leftovers in PriorityLock * [`61e3eb2ea`](https://github.com/siderolabs/talos/commit/61e3eb2eaab1c7974a27440ddd98139a27dfb9dc) fix: talosctl edit mc loop * [`32db7a7f5`](https://github.com/siderolabs/talos/commit/32db7a7f5d6638fc0f731a009dfb0c1870c69083) fix: surround `cancelCtx` with the mutex

### Changes since v1.3.0-alpha.0
43 commits

* [`869f3b5a5`](https://github.com/siderolabs/talos/commit/869f3b5a51ac783e8b0a5a31a103c212a068672b) feat: network configuration improvements on the OpenStack platform * [`29f2195e1`](https://github.com/siderolabs/talos/commit/29f2195e130ecf66a911d0c75343486ee7c86046) feat: support exoscale cloud * [`8b4ae08d1`](https://github.com/siderolabs/talos/commit/8b4ae08d1c42a2cab4bbf0daac090e0882b5d4e9) fix: etcd snapshot command on Windows * [`8bfa7ac1d`](https://github.com/siderolabs/talos/commit/8bfa7ac1d6012746bf7264528eac5cacdd752e2b) feat: platform metadata resource * [`7e50e24c0`](https://github.com/siderolabs/talos/commit/7e50e24c0187e514876222857d44eedda79acc5a) fix: properly cleanup legacy static pod manifests directory * [`6ee47bcc6`](https://github.com/siderolabs/talos/commit/6ee47bcc61bd5b8684c43c0d8c020c574631c832) fix: support serving config for qemu launcher on IPv6 * [`6c3d11b49`](https://github.com/siderolabs/talos/commit/6c3d11b49e94b33ccfdf29f93d3233e480b5e7f0) docs: admission control patch note * [`4ea3b99b5`](https://github.com/siderolabs/talos/commit/4ea3b99b527406b0bbf9cbfd22867431b143ed49) fix: serve static pod files on 127.0.0.1 instead of localhost * [`23842114f`](https://github.com/siderolabs/talos/commit/23842114f077d98cf7bdbf8912454623dff41bbb) feat: support encryption with secretbox * [`f6773c472`](https://github.com/siderolabs/talos/commit/f6773c472c0c1094045a26e34be2472a98dad510) docs: talos support on equinix metal * [`b307160f6`](https://github.com/siderolabs/talos/commit/b307160f613f2544c70be115c9a1ae0a7439ec52) chore: bump dependencies * [`d7edd0e2e`](https://github.com/siderolabs/talos/commit/d7edd0e2e6ec5e4cba8bfa119d244c7be09078d9) refactor: use go-circular, go-kubeconfig, and go-tail * [`c6e1702ec`](https://github.com/siderolabs/talos/commit/c6e1702eca2d310f6fad52e0f00bc91d7d6c4996) feat: use URL-based manifests to present static pods to the kubelet * [`136a795e5`](https://github.com/siderolabs/talos/commit/136a795e55b5be5f093aaf6b07039e86df971674) docs: update system requirements to mention dedicated disk usage * [`879e8c0bf`](https://github.com/siderolabs/talos/commit/879e8c0bfe31f6b35f8833cf55624cd934ded50b) chore: update kernel with BTF support * [`ceb0cd99a`](https://github.com/siderolabs/talos/commit/ceb0cd99ae0e29cadf69e121afdc439f3296ff74) feat: implement Talos API auth using SideroV1 signatures * [`e6fba7d3b`](https://github.com/siderolabs/talos/commit/e6fba7d3bc83d008518d7a032b309ddd212e0f81) chore: update dependencies * [`93e55b85f`](https://github.com/siderolabs/talos/commit/93e55b85f207060d053ba9f16267d98c2599a2df) chore: bump golangci-lint to v1.50.0 * [`aa3d9b4ca`](https://github.com/siderolabs/talos/commit/aa3d9b4ca60f0a7e47867e1de134753eb914606b) fix: regenerate cert on node labeling retry * [`021c73c35`](https://github.com/siderolabs/talos/commit/021c73c35233ee5e6cb9cf5e83336eeb70ae05d3) fix: lowercase nodename * [`b902036e1`](https://github.com/siderolabs/talos/commit/b902036e12843d6348d945097d3826a50b040b25) docs: update office hours time link * [`7fcb8c681`](https://github.com/siderolabs/talos/commit/7fcb8c68164d72f14bca284daffc69605002acb5) feat: update Flannel to v0.20.0 * [`dc70d892a`](https://github.com/siderolabs/talos/commit/dc70d892a341f0694be0c0ff5517b63ea6bbadd9) fix: support setting KubeSpan link MTU * [`7d52bad37`](https://github.com/siderolabs/talos/commit/7d52bad370d544d1a2862891e089426dff7c52a3) feat: update Linux to 5.15.73 * [`9c78b3aff`](https://github.com/siderolabs/talos/commit/9c78b3aff48fd95f48ab2c951f7eb61273338e9a) feat: update Kubernetes to v1.26.0-alpha.2 * [`94913a672`](https://github.com/siderolabs/talos/commit/94913a6727e9a802d2e14c141a831a8fddc8d9b2) docs: add lofty to talos adopters * [`0a0bdfe16`](https://github.com/siderolabs/talos/commit/0a0bdfe164625013e807cf5a08f590835894bf92) docs: add Tremor Video to adopters * [`b7b1d4fd6`](https://github.com/siderolabs/talos/commit/b7b1d4fd6a492c8e4c73b9f7f17449241903f868) feat: use readonly containers * [`d210338e3`](https://github.com/siderolabs/talos/commit/d210338e33438919fc8d2d83fc479981077d5164) fix: skip protobuf full unmarshaling for some talosctl commands * [`b3c679d18`](https://github.com/siderolabs/talos/commit/b3c679d18e698092795725e6fcb05d6569d681b1) chore: bump dependencies * [`993743f63`](https://github.com/siderolabs/talos/commit/993743f63495a59020670619abde5a0d5cd322e2) fix: skip hostname via DHCP on OpenStack platform * [`db076e7b5`](https://github.com/siderolabs/talos/commit/db076e7b5afca7c725c4c6876a7e05d643a219a1) feat: pin interface by mac address in cmdline args * [`63de93722`](https://github.com/siderolabs/talos/commit/63de937227362064a05fa3a9ba11f55891458cc7) fix: update go-smbios to v0.3.1 * [`49e9f808e`](https://github.com/siderolabs/talos/commit/49e9f808e7b14af90959c7fca9457128e82f9cb5) chore: bump kernel and go * [`c7372144d`](https://github.com/siderolabs/talos/commit/c7372144de4b953ebe2494676143ea6d0e53e666) docs: add constraints to upgrade docs * [`c71c8ca18`](https://github.com/siderolabs/talos/commit/c71c8ca18fd4bb7dcae2f69ea253c16b9abd7a9d) docs: consolidate, simplify and correct various docs * [`06f76bfeb`](https://github.com/siderolabs/talos/commit/06f76bfebb14e7d826b8c7efe4564a94d841a74a) chore: bump dependencies * [`b1c421b9a`](https://github.com/siderolabs/talos/commit/b1c421b9ad90d36e8a3562aacdcc30c521da585a) chore: publish ami's with imds v2 enabled * [`195c40ab5`](https://github.com/siderolabs/talos/commit/195c40ab5908c3bcd0c8ecf5b6f7275bb9b7a499) docs: add information about applicable use cases of disk encryption * [`54a687fb8`](https://github.com/siderolabs/talos/commit/54a687fb8e68f3669ff140d37ff3fd01595a494d) docs: consolidate and expand on discovery service * [`139c62d76`](https://github.com/siderolabs/talos/commit/139c62d762c2a9001808d4e1bed38145ea86a95d) feat: allow upgrades in maintenance mode (only over SideroLink) * [`48dee4805`](https://github.com/siderolabs/talos/commit/48dee480577c9d1bb4620f78c6b4bbeba0f0d0bc) feat: support mtu for routes * [`1c43c72ae`](https://github.com/siderolabs/talos/commit/1c43c72aebd1a2bcc1991787dcd94c8bab00df42) docs: fix talos required kernel params

### Changes from siderolabs/crypto
27 commits

* [`c3225ee`](https://github.com/siderolabs/crypto/commit/c3225eee603a8d1218c67e1bfe33ddde7953ed74) feat: allow CSR template subject field to be overridden * [`8570669`](https://github.com/siderolabs/crypto/commit/85706698dac8cddd0e9f41006bed059347d2ea26) chore: rename to siderolabs/crypto * [`e9df1b8`](https://github.com/siderolabs/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs * [`510b0d2`](https://github.com/siderolabs/crypto/commit/510b0d2753a89170d0c0f60e052a66484997a5b2) chore: add json tags * [`6fa2d93`](https://github.com/siderolabs/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil` * [`9a63cba`](https://github.com/siderolabs/crypto/commit/9a63cba8dabd278f3080fa8c160613efc48c43f8) fix: add back support for generating ECDSA keys with P-256 and SHA512 * [`893bc66`](https://github.com/siderolabs/crypto/commit/893bc66e4716a4cb7d1d5e66b5660ffc01f22823) fix: use SHA256 for ECDSA-P256 * [`deec8d4`](https://github.com/siderolabs/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types * [`d3cb772`](https://github.com/siderolabs/crypto/commit/d3cb77220384b3a3119a6f3ddb1340bbc811f1d1) feat: make possible to change KeyUsage * [`6bc5bb5`](https://github.com/siderolabs/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument * [`cd18ef6`](https://github.com/siderolabs/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations * [`97c888b`](https://github.com/siderolabs/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR * [`7776057`](https://github.com/siderolabs/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos * [`80df078`](https://github.com/siderolabs/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters * [`15bdd28`](https://github.com/siderolabs/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates * [`4f80b97`](https://github.com/siderolabs/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate * [`39584f1`](https://github.com/siderolabs/crypto/commit/39584f1b6e54e9966db1f16369092b2215707134) feat: support for key/certificate types RSA, Ed25519, ECDSA * [`cf75519`](https://github.com/siderolabs/crypto/commit/cf75519cab82bd1b128ae9b45107c6bb422bd96a) fix: function NewKeyPair should create certificate with proper subject * [`751c95a`](https://github.com/siderolabs/crypto/commit/751c95aa9434832a74deb6884cff7c5fd785db0b) feat: add 'PEMEncodedKey' which allows to transport keys in YAML * [`562c3b6`](https://github.com/siderolabs/crypto/commit/562c3b66f89866746c0ba47927c55f41afed0f7f) feat: add support for public RSA key in RSAKey * [`bda0e9c`](https://github.com/siderolabs/crypto/commit/bda0e9c24e80c658333822e2002e0bc671ac53a3) feat: enable more conversions between encoded and raw versions * [`e0dd56a`](https://github.com/siderolabs/crypto/commit/e0dd56ac47456f85c0b247999afa93fb87ebc78b) feat: add NotBefore option for x509 cert creation * [`12a4897`](https://github.com/siderolabs/crypto/commit/12a489768a6bb2c13e16e54617139c980f99a658) feat: add support for SPKI fingerprint generation and matching * [`d0c3eef`](https://github.com/siderolabs/crypto/commit/d0c3eef149ec9b713e7eca8c35a6214bd0a64bc4) fix: implement NewKeyPair * [`196679e`](https://github.com/siderolabs/crypto/commit/196679e9ec77cb709db54879ddeddd4eaafaea01) feat: move `pkg/grpc/tls` from `github.com/talos-systems/talos` as `./tls` * [`1ff6242`](https://github.com/siderolabs/crypto/commit/1ff6242c91bb298ceeb4acd65685cba952fe4178) chore: initial version as imported from talos-systems/talos * [`835063e`](https://github.com/siderolabs/crypto/commit/835063e055b28a525038b826a6d80cbe76402414) chore: initial commit

### Changes from siderolabs/discovery-api
3 commits

* [`5b0c5e7`](https://github.com/siderolabs/discovery-api/commit/5b0c5e78097c1489457b148a7f13c73890f5ecad) chore: rename to siderolabs, rekres, etc * [`db279ef`](https://github.com/siderolabs/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/siderolabs/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from siderolabs/discovery-client
1 commit

* [`230f317`](https://github.com/siderolabs/discovery-client/commit/230f317a8e6e9542b82efcbac9f5cd7b9cff34b6) fix: reconnect the client on update failure

### Changes from siderolabs/extras
2 commits

* [`8f00d77`](https://github.com/siderolabs/extras/commit/8f00d7719f0a2312eaa3815ae8c7a91d000db661) feat: update tc-redirect-tap to the latest version * [`7c91844`](https://github.com/siderolabs/extras/commit/7c91844de76568335b7ccaec63cecec17401dd83) chore: bump go to 1.19.2

### Changes from siderolabs/gen
6 commits

* [`b3b6db8`](https://github.com/siderolabs/gen/commit/b3b6db858cb6ce46005edeb70776608e3f9bc402) fix: fix Copy documentation and implementation * [`521f737`](https://github.com/siderolabs/gen/commit/521f7371f40556ddce7f730c8de5e1888e40b621) feat: add xerrors package which contains additions to the std errors * [`726e066`](https://github.com/siderolabs/gen/commit/726e066dcb35c86f82866097bed806f22b936292) fix: rename tuples.go to pair.go and set proper package name * [`d8d7d25`](https://github.com/siderolabs/gen/commit/d8d7d25ce9a588609c00cb798206a01a866bf7a6) chore: minor additions * [`338a650`](https://github.com/siderolabs/gen/commit/338a65065f92eb6426a66c4a88a0cc02cc02e529) chore: add initial implementation and documentation * [`4fd8667`](https://github.com/siderolabs/gen/commit/4fd866707052c792a6adccbc28efec5debdd18a8) Initial commit

### Changes from siderolabs/go-blockdevice
55 commits

* [`dcf6044`](https://github.com/siderolabs/go-blockdevice/commit/dcf6044c906b36f183e11b6553458c680126d1d9) chore: rekres and rename * [`9c4af49`](https://github.com/siderolabs/go-blockdevice/commit/9c4af492cc17279f0281fcd271e7423be78442bb) fix: cryptsetup remove slot * [`74ea471`](https://github.com/siderolabs/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs * [`9fa801c`](https://github.com/siderolabs/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk * [`fccee8b`](https://github.com/siderolabs/go-blockdevice/commit/fccee8bb082b105cb60db40cb01636efc3241b5f) chore: rekres the source, fix issues * [`d9c3a27`](https://github.com/siderolabs/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems * [`b374eb4`](https://github.com/siderolabs/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default * [`ec428fe`](https://github.com/siderolabs/go-blockdevice/commit/ec428fed2ecd5a389833a88f8dc333762816db99) fix: lookup filesystem labels on the actual device path * [`7b9de26`](https://github.com/siderolabs/go-blockdevice/commit/7b9de26bc6bc3d54b95bd8e8fb3aade4b45adc6c) feat: read symlink fullpath in block device list function * [`6928ee4`](https://github.com/siderolabs/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/siderolabs/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions * [`15b182d`](https://github.com/siderolabs/go-blockdevice/commit/15b182db0cd233b163ed83d1724c7e28cf29d71a) fix: return partition table not exist when trying to read an empty dev * [`b9517d5`](https://github.com/siderolabs/go-blockdevice/commit/b9517d51120d385f97b0026f99ce3c4782940c37) fix: resize partition * [`70d2865`](https://github.com/siderolabs/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks * [`667bf53`](https://github.com/siderolabs/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found * [`d7d4cdd`](https://github.com/siderolabs/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found * [`33afba3`](https://github.com/siderolabs/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method * [`e367f9d`](https://github.com/siderolabs/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode * [`d981156`](https://github.com/siderolabs/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows * [`fe24303`](https://github.com/siderolabs/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations * [`2ec0c3c`](https://github.com/siderolabs/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition * [`87816a8`](https://github.com/siderolabs/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size * [`c34b59f`](https://github.com/siderolabs/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module * [`30c2bc3`](https://github.com/siderolabs/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable * [`1292574`](https://github.com/siderolabs/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/siderolabs/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types * [`1d830a2`](https://github.com/siderolabs/go-blockdevice/commit/1d830a25f64f6fb96a1bedd800c0b40b107dc833) fix: revert mark the EFI partition in PMBR as bootable * [`bec914f`](https://github.com/siderolabs/go-blockdevice/commit/bec914ffdda42abcfe642bc2cdfc9fcda56a74ee) fix: mark the EFI partition in PMBR as bootable * [`776b37d`](https://github.com/siderolabs/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters * [`bb3ad73`](https://github.com/siderolabs/go-blockdevice/commit/bb3ad73f69836acc2785ec659435e24a531359e7) fix: align partition start to physical sector size * [`8f976c2`](https://github.com/siderolabs/go-blockdevice/commit/8f976c2031108651738ebd4db69fb09758754a28) feat: replace exec.Command with go-cmd module * [`1cf7f25`](https://github.com/siderolabs/go-blockdevice/commit/1cf7f252c38cf11ef07723de2debc27d1da6b520) fix: properly handle no child processes error from cmd.Wait * [`04a9851`](https://github.com/siderolabs/go-blockdevice/commit/04a98510c07fe8477f598befbfe6eaec4f4b73a2) feat: implement luks encryption provider * [`b0375e4`](https://github.com/siderolabs/go-blockdevice/commit/b0375e4267fdc6108bd9ff7a5dc97b80cd924b1d) feat: add an option to open block device with exclusive flock * [`5a1c7f7`](https://github.com/siderolabs/go-blockdevice/commit/5a1c7f768e016c93f6c0be130ffeaf34109b5b4d) refactor: add devname into gpt.Partition, refactor probe package * [`f2728a5`](https://github.com/siderolabs/go-blockdevice/commit/f2728a581972be977d863d5d9177a873b8f3fc7b) fix: keep contents of PMBR when writing it * [`2878460`](https://github.com/siderolabs/go-blockdevice/commit/2878460b54e8b8c3846c6a882ca9e1472c8b6b3b) fix: write second copy of partition entries * [`943b08b`](https://github.com/siderolabs/go-blockdevice/commit/943b08bc32a2156cffb23e92b8be9288de4a7421) fix: blockdevice reset should read partition table from disk * [`5b4ee44`](https://github.com/siderolabs/go-blockdevice/commit/5b4ee44cfd434a03ec2d7167bcc56d0f164c3fa2) fix: ignore `/dev/ram` devices * [`98754ec`](https://github.com/siderolabs/go-blockdevice/commit/98754ec2bb200acc9e9e573fa766754d60e25ff2) refactor: rewrite GPT library * [`2a1baad`](https://github.com/siderolabs/go-blockdevice/commit/2a1baadffdf8c9b65355e9af6e744aeab838c9db) fix: correctly build paths for `mmcblk` devices * [`8076344`](https://github.com/siderolabs/go-blockdevice/commit/8076344a95021f25ab5d1fbf5ea4fefc790f6c3c) fix: return proper disk size from GetDisks function * [`8742133`](https://github.com/siderolabs/go-blockdevice/commit/874213371a3fb0925aab45cbba68a957e3319525) chore: add common method to list available disks using /sys/block * [`c4b5833`](https://github.com/siderolabs/go-blockdevice/commit/c4b583363d63503ed7e4adb9a9fa64335f7e198d) feat: implement "fast" wipe * [`b4e67d7`](https://github.com/siderolabs/go-blockdevice/commit/b4e67d73d70d8dc06aa2b4986622dcb854dfc40c) feat: return resize status from Resize() function * [`ceae64e`](https://github.com/siderolabs/go-blockdevice/commit/ceae64edb3a591c6f6bbd75b1149d1cfe426dd8e) fix: sync kernel partition table incrementally * [`2cb9516`](https://github.com/siderolabs/go-blockdevice/commit/2cb95165aa67b0b839863b5ad89920c3ac7e2c82) fix: return correct error value from blkpg functions * [`cebe43d`](https://github.com/siderolabs/go-blockdevice/commit/cebe43d1fdc1e509437198e578faa9d5a804cc37) refactor: expose `InsertAt` method via interface * [`c40dcd8`](https://github.com/siderolabs/go-blockdevice/commit/c40dcd80c50b41c1f2a60ea6aa9d5fb3d3b180a3) fix: properly inform kernel about partition deletion * [`bb8ac5d`](https://github.com/siderolabs/go-blockdevice/commit/bb8ac5d6a25e279e16213f585dc8d02ba6ed645f) feat: implement disk wiping via several methods * [`23fb7dc`](https://github.com/siderolabs/go-blockdevice/commit/23fb7dc755325cfe12e48c8e8e31bebab9ddc2bc) feat: expose partition name (label) * [`ff3a821`](https://github.com/siderolabs/go-blockdevice/commit/ff3a8210be999b8bfb2019f19f8a8b50901c64cc) feat: implement 'InsertAt' method to insert partitions at any position * [`3d1ce4f`](https://github.com/siderolabs/go-blockdevice/commit/3d1ce4fc859fa614a4c5c54a10c0f5f4fce38bb6) fix: calculate last lba of partition correctly * [`b71540f`](https://github.com/siderolabs/go-blockdevice/commit/b71540f6c398e958bdb7c118396a736419f735d4) feat: copy initial version from talos-systems/talos * [`ca3c078`](https://github.com/siderolabs/go-blockdevice/commit/ca3c078da95e6497c9d41667dc242e32682e517d) Initial commit

### Changes from siderolabs/go-circular
2 commits

* [`507e0ec`](https://github.com/siderolabs/go-circular/commit/507e0ec7b70e7c8336c25640929ae7b04869dfa1) refactor: extract circular Go module * [`2234b3a`](https://github.com/siderolabs/go-circular/commit/2234b3ab14ec6a49b5ce48aaec108c6b3f33dc7f) docs: add README

### Changes from siderolabs/go-kubeconfig
2 commits

* [`e7fdd94`](https://github.com/siderolabs/go-kubeconfig/commit/e7fdd94573fa175784700cbb24b37a087e6ca35b) refactor: extract kubeconfig library as a Go module * [`50e91b8`](https://github.com/siderolabs/go-kubeconfig/commit/50e91b8ba9df2c14a82d0ba95ee8acad262497b6) docs: add REAMDE

### Changes from siderolabs/go-loadbalancer
11 commits

* [`438b71d`](https://github.com/siderolabs/go-loadbalancer/commit/438b71da2474525311ee6435606d7a6143696651) chore: update package path and rekres * [`5341eec`](https://github.com/siderolabs/go-loadbalancer/commit/5341eec63c6d3396a37be17506e081ad72ccaeb6) feat: implement public method to check if the route is Healthy * [`b578d47`](https://github.com/siderolabs/go-loadbalancer/commit/b578d477211476bbc34b1ea2c86d54f0d1b0cdc1) feat: add a way to configure loadbalancer options * [`c54d95d`](https://github.com/siderolabs/go-loadbalancer/commit/c54d95d8252780dc374032dc5fe10e7e84a15062) feat: implement control plane loadbalancer * [`4a6e29e`](https://github.com/siderolabs/go-loadbalancer/commit/4a6e29e7c02a2a94193a6014de04c2d2c79bdb02) refactor: clean up names, fix the lingering goroutines * [`af87d1c`](https://github.com/siderolabs/go-loadbalancer/commit/af87d1cbb79da35adabb9587a028db9b3e9fde1c) chore: apply new Kres rules * [`a445702`](https://github.com/siderolabs/go-loadbalancer/commit/a4457024d5189d754b2da4a30b14072a0e3f5f05) feat: allow dial timeout and keep alive period to be configurable * [`3c8f347`](https://github.com/siderolabs/go-loadbalancer/commit/3c8f3471d14e37866c65f73170ef83c038ae5a8c) feat: provide a way to configure logger for the loadbalancer * [`da8e987`](https://github.com/siderolabs/go-loadbalancer/commit/da8e987434c3d407679a40e213b12a8e1c98abb8) feat: implement Reconcile - ability to change upstream list on the fly * [`8b1dfa6`](https://github.com/siderolabs/go-loadbalancer/commit/8b1dfa6e80dea53d699a551221695ca99b2aadb2) feat: copy initial version from talos-systems/talos * [`c2f6a8f`](https://github.com/siderolabs/go-loadbalancer/commit/c2f6a8f88439608ea4b7623e6becdcf079cad217) Initial commit

### Changes from siderolabs/go-smbios
11 commits

* [`10c1dd8`](https://github.com/siderolabs/go-smbios/commit/10c1dd8f2a7a30cc4a00b90d76afcf3ff22bf8ae) fix: check for end of the slice properly * [`9ca8ce7`](https://github.com/siderolabs/go-smbios/commit/9ca8ce77b796f3f49c0b7fd70f184911da294dc1) chore: treat invalid strings as empty * [`dbc5f79`](https://github.com/siderolabs/go-smbios/commit/dbc5f794726f18f0736c1203a440b8148675bc04) chore: rekres+rename * [`3f1e775`](https://github.com/siderolabs/go-smbios/commit/3f1e775b7e3ef74be41461417d800ac81671a553) feat: rework destructuring of SMBIOS information and added some tests * [`fd5ec8c`](https://github.com/siderolabs/go-smbios/commit/fd5ec8ce4873790b7fbd46dba9d7f49c9de7176a) fix: remove useless (?) goroutines leading to data race error * [`d3a32be`](https://github.com/siderolabs/go-smbios/commit/d3a32bea731a0c2a60ce7f5eae60253300ef27e1) fix: return UUID in middle endian only on SMBIOS >= 2.6 * [`fb425d4`](https://github.com/siderolabs/go-smbios/commit/fb425d4727e620b6a2b6ba49e405a2c6f0e46304) feat: add memory device * [`0bb4f96`](https://github.com/siderolabs/go-smbios/commit/0bb4f96a6679e8fc958903c4f451ca068f8e3c41) feat: add physical memory array * [`8019619`](https://github.com/siderolabs/go-smbios/commit/80196199691e7094946a207463c67fc42da6a0e2) feat: supply wake-up type in SMBIOS info * [`94b8c4e`](https://github.com/siderolabs/go-smbios/commit/94b8c4e489eef8c44cb1a2768678945d73e16e88) feat: initial implementation * [`864ed80`](https://github.com/siderolabs/go-smbios/commit/864ed80937edf072f7e7e63551aef0d1f7776111) Initial commit

### Changes from siderolabs/go-tail
2 commits

* [`962ae43`](https://github.com/siderolabs/go-tail/commit/962ae433288845cfc7f2aab0c0ef74777e2bd992) refactor: extract go-tail module * [`359c3cb`](https://github.com/siderolabs/go-tail/commit/359c3cbde0f6a0a49d6893b2d1f8cb7ee6df9efc) docs: initial commit

### Changes from siderolabs/grpc-proxy
51 commits

* [`4cc7bbe`](https://github.com/siderolabs/grpc-proxy/commit/4cc7bbe397d74ee731398d67d34c214747957122) chore: rename to siderolabs/grpc-proxy, rekres * [`2c586db`](https://github.com/siderolabs/grpc-proxy/commit/2c586dbdda4e9c2bd09754beb13014c52b626db1) feat: pass fullMethodName to GetConnection * [`6dfa2cc`](https://github.com/siderolabs/grpc-proxy/commit/6dfa2cc80b6195844cae2dc2b2bc0b9b62246d8d) fix: ignore errors on duplicate `SetHeader` calls * [`b076302`](https://github.com/siderolabs/grpc-proxy/commit/b076302cc46ec6742e71fe1d49f6ec2d5d3a15dc) fix: use io.EOF error when no backend connections are available * [`82daca0`](https://github.com/siderolabs/grpc-proxy/commit/82daca0322a4293bd27071ae1ba8dd5097509d21) docs: update README * [`fa6843a`](https://github.com/siderolabs/grpc-proxy/commit/fa6843ae5b64500d481a1d031790406ed9df77d7) chore: fix spelling * [`c0a87d9`](https://github.com/siderolabs/grpc-proxy/commit/c0a87d95be9c62b0c4fd1fa694ef768e1f8e2391) chore: major cleanup of the code and build * [`ca3bc61`](https://github.com/siderolabs/grpc-proxy/commit/ca3bc6131f052aa000517339211335aaa4ebb640) fix: ignore some errors so that we don't spam the logs * [`5c579a7`](https://github.com/siderolabs/grpc-proxy/commit/5c579a7a61475bde3ec9c1efe000d2a55e2a3cb2) feat: allow different formats for messages streaming/unary * [`6c9f7b3`](https://github.com/siderolabs/grpc-proxy/commit/6c9f7b399173dd5769dbc4e8e366e78f05cead85) fix: allow mode to be set for each request being proxied * [`cc91c09`](https://github.com/siderolabs/grpc-proxy/commit/cc91c09782824e261bf1c861961a272aedb2b123) refactor: provide better public API, enforce proxying mode * [`d8d3a75`](https://github.com/siderolabs/grpc-proxy/commit/d8d3a751d1e71d006ba90379eed388c487bbb246) chore: update import paths after repo move * [`dbf07a4`](https://github.com/siderolabs/grpc-proxy/commit/dbf07a4d9e16fe3cf7407b9921c1746aa24ffaf6) Merge pull request [#7](https://github.com/siderolabs/grpc-proxy/pull/7) from smira/one2many-4 * [`fc0d27d`](https://github.com/siderolabs/grpc-proxy/commit/fc0d27dc6b5b9db35173f3e78778784a9e7c95bf) More tests, small code fixes, updated README. * [`d9ce0b1`](https://github.com/siderolabs/grpc-proxy/commit/d9ce0b1053a7f15ea65bf46e94cfe4154493bad7) Merge pull request [#6](https://github.com/siderolabs/grpc-proxy/pull/6) from smira/one2many-3 * [`2d37ba4`](https://github.com/siderolabs/grpc-proxy/commit/2d37ba444528a00f988671f3a01666e692739a37) Support for one2many streaming calls, tests. * [`817b035`](https://github.com/siderolabs/grpc-proxy/commit/817b03553ed7d97bd0da09283776d54592d7b5d4) Merge pull request [#5](https://github.com/siderolabs/grpc-proxy/pull/5) from smira/one2many-2 * [`436b338`](https://github.com/siderolabs/grpc-proxy/commit/436b3383a39fd860f3b2379ffab80a44ae1809f7) More unary one-2-many tests, error propagation. * [`1f0cb46`](https://github.com/siderolabs/grpc-proxy/commit/1f0cb466268f046e8e9fb78b1902411ac3a753ba) Merge pull request [#4](https://github.com/siderolabs/grpc-proxy/pull/4) from smira/one2many-1 * [`992a975`](https://github.com/siderolabs/grpc-proxy/commit/992a975ccf0b97e4be329c84bd3018652e8e50ae) Proxying one to many: first iteration * [`a0988ff`](https://github.com/siderolabs/grpc-proxy/commit/a0988ff2b29839892a7913acd76f26f4e7edcc3a) Merge pull request [#3](https://github.com/siderolabs/grpc-proxy/pull/3) from smira/small-fixups * [`e3111ef`](https://github.com/siderolabs/grpc-proxy/commit/e3111ef2c16f0ee4bba597a2ab1ab6a2818c2734) Small fixups in preparation to add one-to-many proxying. * [`6d76ffc`](https://github.com/siderolabs/grpc-proxy/commit/6d76ffcff89f6636d3689ed1c9b0eebe87722114) Merge pull request [#2](https://github.com/siderolabs/grpc-proxy/pull/2) from smira/backend-concept * [`2aad63a`](https://github.com/siderolabs/grpc-proxy/commit/2aad63ac5bae09232ea5ac80b42338e9e3af67c4) Add concept of a 'Backend', but still one to one proxying * [`7cc4610`](https://github.com/siderolabs/grpc-proxy/commit/7cc46101114a2779d6393e0e8f841bf3febb2753) Merge pull request [#1](https://github.com/siderolabs/grpc-proxy/pull/1) from smira/build * [`37f01f3`](https://github.com/siderolabs/grpc-proxy/commit/37f01f3aab3b978a8fecb428fca4d4c722141229) Rework build to use GitHub Actions, linting updates. * [`0f1106e`](https://github.com/siderolabs/grpc-proxy/commit/0f1106ef9c766333b9acb4b81e705da4bade7215) Move error checking further up (#34) * [`d5b35f6`](https://github.com/siderolabs/grpc-proxy/commit/d5b35f634383bf8931f8798797daaf9c1a59235e) Update gRPC and fix tests (#27) * [`67591eb`](https://github.com/siderolabs/grpc-proxy/commit/67591eb23c48346a480470e462289835d96f70da) Break StreamDirector interface, fix metadata propagation for gRPC-Go>1.5. (#20) * [`97396d9`](https://github.com/siderolabs/grpc-proxy/commit/97396d94749c00db659393ba5123f707062f829f) Merge pull request [#11](https://github.com/siderolabs/grpc-proxy/pull/11) from mwitkow/fix-close-bug * [`3fcbd37`](https://github.com/siderolabs/grpc-proxy/commit/3fcbd3737ec6baff505795417e48f162a7a3183c) fixup closing conns * [`a8f5f87`](https://github.com/siderolabs/grpc-proxy/commit/a8f5f87a2f5e6bc3643b78d64594195b2395a238) fixup tests, extend readme * [`428fa1c`](https://github.com/siderolabs/grpc-proxy/commit/428fa1c450320041e0ad8e251d6aed435401174e) Fix a channel closing bug * [`af55d61`](https://github.com/siderolabs/grpc-proxy/commit/af55d612de6c5723a5a59340704db7bc771023ff) Merge pull request [#10](https://github.com/siderolabs/grpc-proxy/pull/10) from mwitkow/bugfix/streaming-fix * [`de4d3db`](https://github.com/siderolabs/grpc-proxy/commit/de4d3db538565636e1e977102f6f0bd1ed0ce9c2) remove spurious printfs * [`84242c4`](https://github.com/siderolabs/grpc-proxy/commit/84242c4e690da18d16d2ab8f2fa47e45986220b6) fix the "i don't know who finished" case * [`9b22f41`](https://github.com/siderolabs/grpc-proxy/commit/9b22f41d8535fa3e40908c78ae66066c7972b6d9) fix full duplex streaming * [`c2f7c98`](https://github.com/siderolabs/grpc-proxy/commit/c2f7c98b0b6cd180659aed31e98cbbc18d616b1c) update readme * [`d654141`](https://github.com/siderolabs/grpc-proxy/commit/d654141edcb92b7fa2bba9d3e690e569c72f8e9d) update README * [`f457856`](https://github.com/siderolabs/grpc-proxy/commit/f4578565f2d34dc89774128db2bfda3a328cba40) move to proxy subdirectory * [`4889d78`](https://github.com/siderolabs/grpc-proxy/commit/4889d78e468681601b8229c81807dcf37b00ff63) Add fixup scripts * [`ef60a37`](https://github.com/siderolabs/grpc-proxy/commit/ef60a37547d137e52873be183f2d7a5626d7c034) version 2 of the grpc-proxy, this time with fewer grpc upstream deps * [`07aeac1`](https://github.com/siderolabs/grpc-proxy/commit/07aeac13e988c0c0b3a886c79972e20408a765e0) Merge pull request [#2](https://github.com/siderolabs/grpc-proxy/pull/2) from daniellowtw/master * [`e5c3df5`](https://github.com/siderolabs/grpc-proxy/commit/e5c3df5b2f0a1ffc4cb755cbe6b30b435e35de37) Fix compatibility with latest grpc library * [`52be0a5`](https://github.com/siderolabs/grpc-proxy/commit/52be0a559a85f0e2480bde6725f3f144396aa6ef) bugfix: fix gRPC Java deadlock, due to different dispatch logic * [`822df7d`](https://github.com/siderolabs/grpc-proxy/commit/822df7d86b556b703fc11798a3bdcbaeb60c18a6) Fix reference to mwitkow. * [`28341d1`](https://github.com/siderolabs/grpc-proxy/commit/28341d171dd4c1a52f46371ddfb5fd2240b79731) move out forward logic to method, allowing for use as `grpc.Server` not found handler. * [`89e28b4`](https://github.com/siderolabs/grpc-proxy/commit/89e28b42ee9dda8e36522b77e3771d9debc645e0) add reference to upstream grpc bug * [`00dd588`](https://github.com/siderolabs/grpc-proxy/commit/00dd588ae68adf4187a7fca87db45a73af4c834d) merge upstream `grpc.Server` changes changing the dispatch logic * [`77edc97`](https://github.com/siderolabs/grpc-proxy/commit/77edc9715de187dcbc9969e2f0e8a04d2087fd13) move to upstream `protobuf` from `gogo` * [`db71c3e`](https://github.com/siderolabs/grpc-proxy/commit/db71c3e7e812db8d75cb282dac38d953fcb436b3) initial commit, tested and working.

### Changes from siderolabs/pkgs
36 commits

* [`66c77e9`](https://github.com/siderolabs/pkgs/commit/66c77e9669a7e1c2ca6a2477fac809a34e3ce3f6) feat: re-enable build kernel with BTF enabled * [`98ef073`](https://github.com/siderolabs/pkgs/commit/98ef0736130e660b95720db1ac23b7a6e1506f3e) feat: enable INET_DIAG and FANOTFY_PERMISSIONS * [`8fe5cbc`](https://github.com/siderolabs/pkgs/commit/8fe5cbca8243eaa0a60cf1aee80ab3e2ad987e81) chore: update dependencies * [`554c0fe`](https://github.com/siderolabs/pkgs/commit/554c0fe295719e425453e3d763559193b01a8b03) feat: add fanotify and kprobes kernel options * [`54d7e5c`](https://github.com/siderolabs/pkgs/commit/54d7e5c8db1e54c999ba0b4a466fe65c54e0c663) fix: drbd package name * [`b4cb9e2`](https://github.com/siderolabs/pkgs/commit/b4cb9e2125e20aea52a57aaba981326deb8ae0df) feat: add 'drbd' package * [`91e73b3`](https://github.com/siderolabs/pkgs/commit/91e73b325431f997b0e19d6ba083c48a6ae5ff2a) feat: update dependencies * [`b6d0d96`](https://github.com/siderolabs/pkgs/commit/b6d0d969942234defbf08745c57e3141152662e3) chore: bump kernel to 5.15.72 * [`b16dfe9`](https://github.com/siderolabs/pkgs/commit/b16dfe9699e43a03e47109c95ac0707cce038a49) chore: bump go to 1.19.2 * [`861cc32`](https://github.com/siderolabs/pkgs/commit/861cc32274db424975544b67e6f10b3568980a11) chore: bump kernel to 5.15.71 * [`0ac7773`](https://github.com/siderolabs/pkgs/commit/0ac77733506d2f0b0944ff569b6817ae44821bda) chore: use generic raspberry pi u-boot * [`d5633d4`](https://github.com/siderolabs/pkgs/commit/d5633d4838bd6e168b9c80f124540a30c29ae7be) chore: bump kernel to 5.15.70 * [`39c0d43`](https://github.com/siderolabs/pkgs/commit/39c0d4364fd4eedd281e46ce7d305f2562e2cf78) feat: add generic rpi_arm64_defconfig configuration * [`ed269ca`](https://github.com/siderolabs/pkgs/commit/ed269cabad82446095221e45078c8ba85bce5c2e) chore: bump kernel to 5.15.69 * [`f2f8333`](https://github.com/siderolabs/pkgs/commit/f2f83331f93a0a5d2dd1c013e2ff46900684096a) fix: no slack notifications on failure * [`6f0af33`](https://github.com/siderolabs/pkgs/commit/6f0af3390fc170f0cf57450adfada6a87de7ece4) chore: disable drone slack pipeline for renovate * [`32aea3f`](https://github.com/siderolabs/pkgs/commit/32aea3f005b93aaa91d52e4dfd04dd9ce9d564a9) chore: disable drone for renovate/dependabot * [`44579f0`](https://github.com/siderolabs/pkgs/commit/44579f0238993f529e2c141f42c99b32803fd6a5) fix: rollback xfsprogs to 5.18.0 * [`792c0e3`](https://github.com/siderolabs/pkgs/commit/792c0e32ef6b1cf13514dc2693c4c302e1440d3b) feat: add gasket driver package * [`07f1898`](https://github.com/siderolabs/pkgs/commit/07f1898b231390b85519f83638946ed65adacc64) chore: update deps * [`f78f410`](https://github.com/siderolabs/pkgs/commit/f78f410d193953e730aeb14f4e148e47dfa827fd) chore: enable conntrack zones and timestamps * [`049b3c6`](https://github.com/siderolabs/pkgs/commit/049b3c6f080b9af76b1b2e924baade69db27bc0b) chore: enable intel ice drivers * [`606ff32`](https://github.com/siderolabs/pkgs/commit/606ff32cb7e75b6975749b6250b68352b71e943b) chore: bump deps * [`eee5c8a`](https://github.com/siderolabs/pkgs/commit/eee5c8af13ee1fe0b1e660a9581d4f1b14158a39) chore: disable irc in conntrack * [`70e6c46`](https://github.com/siderolabs/pkgs/commit/70e6c460d7b3bd5e154a4e681858832afcf32368) chore: bump kernel to 5.15.64 * [`e510321`](https://github.com/siderolabs/pkgs/commit/e5103217e714bea04e06fd0c4940e84406cb68cf) chore: update renovate config * [`d1fa510`](https://github.com/siderolabs/pkgs/commit/d1fa510cc66ddc63a53482f6ced5573466049d49) feat: enable renovate bot * [`e427a77`](https://github.com/siderolabs/pkgs/commit/e427a778146664b988664008bfe20611f91216b0) chore: bump runc to v1.1.4 * [`40e1215`](https://github.com/siderolabs/pkgs/commit/40e12152a027eb509330c41db21680b9a662fa05) chore: enable nfsv4.2 client support * [`15efada`](https://github.com/siderolabs/pkgs/commit/15efadaa9db4b8dc8003359d6d0ed84016f54746) chore: bump kernel to 5.15.63 * [`e70e3c1`](https://github.com/siderolabs/pkgs/commit/e70e3c1af2b11d4b4646401a617b3d0efa2db4a3) fix: nvidia oss pkg name * [`30b8d79`](https://github.com/siderolabs/pkgs/commit/30b8d79b9ca3e463b5f403f01d39e64e89edc7b1) chore: bump kernel to 5.15.62 * [`862c392`](https://github.com/siderolabs/pkgs/commit/862c392b6defe3c9ce90f9b15eae154e021b0b4d) chore: bump gcc to 12.2.0 * [`2ecd14e`](https://github.com/siderolabs/pkgs/commit/2ecd14ede04637a581fbe7dcbbf612cdd6f9d882) fix: containerd version * [`01df058`](https://github.com/siderolabs/pkgs/commit/01df0583a430f3793f19725c920e942cf37efee4) feat: add NanoPi R4S configuration * [`d4cb33b`](https://github.com/siderolabs/pkgs/commit/d4cb33b9bdfb8c27ea86a42ea60a88e294129ad4) chore: bump containerd to v1.6.8

### Changes from siderolabs/siderolink
18 commits

* [`61ab1c4`](https://github.com/siderolabs/siderolink/commit/61ab1c43dd04faeb046c51dca7d891213762a31e) fix: include MachineStatusEvent into the list of supported events * [`16a84eb`](https://github.com/siderolabs/siderolink/commit/16a84ebe6759535c7a5284271418f7f04443e25f) chore: rename to siderolabs/siderolink * [`ca470c7`](https://github.com/siderolabs/siderolink/commit/ca470c735e6922b7d5afea91aef50c043f9563ee) chore: update Talos to the latest master, migrate netaddr -> netip/x * [`93b65f0`](https://github.com/siderolabs/siderolink/commit/93b65f0619c38de7641d75f31a0c88f88b6a46d4) fix: ignore 'exist' error on interface managmeent * [`3c4d9e0`](https://github.com/siderolabs/siderolink/commit/3c4d9e0fac88d30d9b794c254e4e015633156001) chore: move IP to interface binding into NewDevice * [`f0b5e39`](https://github.com/siderolabs/siderolink/commit/f0b5e39d523c633f3345bf06071571385db8aecc) feat: use kernel wireguard implementation when available * [`1d2b7e1`](https://github.com/siderolabs/siderolink/commit/1d2b7e13e7d055b5717dfb7f5111ec242e41ab01) feat: allow setting peer endpoint using peer event * [`5d085d6`](https://github.com/siderolabs/siderolink/commit/5d085d6eac27471a1c0e256c55d8f6ae01b55b8e) feat: expose `wgDevice.Peers` from the `wireguard.Device` wrapper * [`3a5be65`](https://github.com/siderolabs/siderolink/commit/3a5be65da5bbf3f565766993093578094d72e3eb) fix: use correct method to generate Wireguard private key * [`8318a7e`](https://github.com/siderolabs/siderolink/commit/8318a7e1747cb43ec5879d45df2e9a7e2533486e) feat: accept join token in Provision payload * [`b38c192`](https://github.com/siderolabs/siderolink/commit/b38c192875e10a0a9758dde42c7f17cf66694d61) fix: build on Windows * [`9902ad2`](https://github.com/siderolabs/siderolink/commit/9902ad2774f0655e050233854b9d28dad0431f6c) feat: pass request context and node address to the events sink adapter * [`d0612a7`](https://github.com/siderolabs/siderolink/commit/d0612a724a1b1336a2bc6a99ed3178e3e40f6d9b) refactor: pass in listener to the log receiver * [`d86cdd5`](https://github.com/siderolabs/siderolink/commit/d86cdd59ee7a0e0504b739a913991c272c7fb3f5) feat: implement logreceiver for kernel logs * [`f7cadbc`](https://github.com/siderolabs/siderolink/commit/f7cadbcdfbb84d367e27b5af32e89c138d72d9d7) fix: handle duplicate peer updates * [`0755b24`](https://github.com/siderolabs/siderolink/commit/0755b24d4682410b251a2a9d662960da15153106) feat: initial implementation of SideroLink * [`ee73ea9`](https://github.com/siderolabs/siderolink/commit/ee73ea9575a81be7685f24936b2c48a4508a159e) feat: add Talos events sink proto files and the reference implementation * [`1e2cd9d`](https://github.com/siderolabs/siderolink/commit/1e2cd9d38621234a0a6010e33b1bab264f4d9bdf) Initial commit

### Changes from siderolabs/tools
21 commits

* [`3b5f89a`](https://github.com/siderolabs/tools/commit/3b5f89a4be0f6c754d1c5bf6dd9a295ff2b6eb94) chore: update dependencies * [`6402b99`](https://github.com/siderolabs/tools/commit/6402b9990964789ff257e9e83823f52dd93540d2) feat: update OpenSSL to 1.1.1r * [`00e91b1`](https://github.com/siderolabs/tools/commit/00e91b1a3ca59f2e0a999f8345556527460683a4) feat: update releases * [`a264809`](https://github.com/siderolabs/tools/commit/a26480967908b86d57c787e55c81f788bdf00ce4) chore: bump go to 1.19.2 * [`858cfe7`](https://github.com/siderolabs/tools/commit/858cfe7077b516d963149cd650a5e92f2c3c38ca) fix: no slack notifications on failure * [`ed85950`](https://github.com/siderolabs/tools/commit/ed859505f1ba7d6ace02e128e297b01b3eb62fee) chore: disable drone slack pipeline for renovate * [`5df6589`](https://github.com/siderolabs/tools/commit/5df658937f7bd667ceda8760e2e15ed85c80dc2c) chore: disable drone for renovate/dependabot * [`1f00d2e`](https://github.com/siderolabs/tools/commit/1f00d2e854cdf357c1192428bd44ee846af1b4e4) fix: revert gawk to 5.1.1 * [`feeda1f`](https://github.com/siderolabs/tools/commit/feeda1fc708a0cdb461ac5967ec34bf24ccc2b62) chore: bump grpc-go * [`8542014`](https://github.com/siderolabs/tools/commit/8542014568a101fb6c03a76c91e59dcfb1b893b6) chore: bump deps * [`e5c4968`](https://github.com/siderolabs/tools/commit/e5c496893fb71ff19a33daa4c86792ed03187356) chore: update renovate config * [`f34f94d`](https://github.com/siderolabs/tools/commit/f34f94daa300baab0803f22cecee65b57ee3c1fd) chore: update renovate config * [`cef4cc6`](https://github.com/siderolabs/tools/commit/cef4cc67342c06904258bcf4b7ec681d4c732d53) chore: update renovate config * [`bab8e9e`](https://github.com/siderolabs/tools/commit/bab8e9ee8d0fc2dc1b5676a45175b507d8927e49) chore: add libbpf to tools * [`0a15f7b`](https://github.com/siderolabs/tools/commit/0a15f7bb35f479fbf5551ea4bf02f3716783e33f) chore: build pahole properly * [`a322d06`](https://github.com/siderolabs/tools/commit/a322d066483814db80a15b8c0c7f44224b134429) chore: remove img * [`c7ff47b`](https://github.com/siderolabs/tools/commit/c7ff47b27962cf0f6a95e07c6f45aa2a3c2c5c8b) feat: enable renovate dependency updates (3/3) * [`6e095cf`](https://github.com/siderolabs/tools/commit/6e095cf86a6f734b2f07cc1b854a9a37b055cacc) feat: enable renovate dependency updates (2/n) * [`bad1ad1`](https://github.com/siderolabs/tools/commit/bad1ad17f7fd1208fcbb70b950320f805a765868) feat: add renovatebot * [`7d6f9c3`](https://github.com/siderolabs/tools/commit/7d6f9c35a81392918560ea0c20b3c06b18501ea0) chore: bump gcc to 12.2.0 * [`2719b4b`](https://github.com/siderolabs/tools/commit/2719b4be551134a9d70ab235f56889708377f3c5) chore: bump toolchain

### Dependency Changes * **cloud.google.com/go/compute** v1.8.0 -> v1.10.0 * **github.com/BurntSushi/toml** v1.2.0 -> v1.2.1 * **github.com/aws/aws-sdk-go** v1.44.76 -> v1.44.122 * **github.com/containerd/containerd** v1.6.8 -> v1.6.9 * **github.com/cosi-project/runtime** v0.1.1 -> e8a8fdcc7548 * **github.com/docker/docker** v20.10.17 -> v20.10.20 * **github.com/fsnotify/fsnotify** v1.5.4 -> v1.6.0 * **github.com/google/go-cmp** v0.5.8 -> v0.5.9 * **github.com/google/nftables** 2eca00135732 -> 4f5cd5826fbd * **github.com/hetznercloud/hcloud-go** v1.35.2 -> v1.35.3 * **github.com/insomniacslk/dhcp** 509691fd59ec -> 5308ebe5334c * **github.com/jsimonetti/rtnetlink** v1.2.2 -> v1.2.3 * **github.com/mdlayher/ethtool** 856bd6cb8a38 -> 0e16326d06d1 * **github.com/mdlayher/netlink** v1.6.0 -> v1.6.2 * **github.com/opencontainers/image-spec** c5a74bcca799 -> v1.1.0-rc2 * **github.com/packethost/packngo** v0.25.0 -> v0.28.1 * **github.com/rivo/tview** 0e6b21a48e96 -> 2e69b7385a37 * **github.com/siderolabs/crypto** v0.4.0 **_new_** * **github.com/siderolabs/discovery-api** v0.1.1 **_new_** * **github.com/siderolabs/discovery-client** v0.1.1 -> v0.1.2 * **github.com/siderolabs/extras** v1.2.0 -> v1.3.0-alpha.0-1-g8f00d77 * **github.com/siderolabs/gen** v0.4.0 **_new_** * **github.com/siderolabs/go-blockdevice** v0.4.0 **_new_** * **github.com/siderolabs/go-circular** v0.1.0 **_new_** * **github.com/siderolabs/go-kubeconfig** v0.1.0 **_new_** * **github.com/siderolabs/go-loadbalancer** v0.2.0 **_new_** * **github.com/siderolabs/go-smbios** v0.3.1 **_new_** * **github.com/siderolabs/go-tail** v0.1.0 **_new_** * **github.com/siderolabs/grpc-proxy** v0.4.0 **_new_** * **github.com/siderolabs/pkgs** v1.2.0-8-g970860d -> v1.3.0-alpha.0-35-g66c77e9 * **github.com/siderolabs/siderolink** v0.2.0 **_new_** * **github.com/siderolabs/tools** v1.2.0 -> v1.3.0-alpha.0-20-g3b5f89a * **github.com/spf13/cobra** v1.5.0 -> v1.6.1 * **github.com/stretchr/testify** v1.8.0 -> v1.8.1 * **github.com/u-root/u-root** v0.9.0 -> v0.10.0 * **github.com/vmware-tanzu/sonobuoy** v0.56.9 -> v0.56.10 * **go.etcd.io/etcd/api/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/pkg/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/etcdutl/v3** v3.5.4 -> v3.5.5 * **go.uber.org/atomic** v1.9.0 -> v1.10.0 * **go.uber.org/zap** v1.22.0 -> v1.23.0 * **go4.org/netipx** 797b0c90d8ab **_new_** * **golang.org/x/net** 3211cb980234 -> v0.1.0 * **golang.org/x/sync** 886fb9371eb4 -> v0.1.0 * **golang.org/x/sys** fbc7d0a398ab -> v0.1.0 * **golang.org/x/term** a9ba230a4035 -> v0.1.0 * **golang.org/x/time** e5dcc9cfc0b9 -> v0.1.0 * **golang.zx2c4.com/wireguard/wgctrl** 3d4a969bb56b -> 473347a5e6e3 * **google.golang.org/grpc** v1.48.0 -> v1.50.1 * **k8s.io/api** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/apimachinery** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/apiserver** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/client-go** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/component-base** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/cri-api** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/kubectl** v0.25.0 -> v0.26.0-alpha.2 * **k8s.io/kubelet** v0.25.0 -> v0.26.0-alpha.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.65 -> v1.2.66 Previous release can be found at [v1.2.0](https://github.com/siderolabs/talos/releases/tag/v1.2.0) ## [Talos 1.3.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.3.0-alpha.0) (2022-09-28) Welcome to the v1.3.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### kube-apiserver Audit Policy Talos now supports setting custom audit policy for `kube-apiserver` in the machine configuration. ### etcd Consistency Check Talos enables [--experimental-compact-hash-check-enabled](https://github.com/etcd-io/etcd/pull/14120) option by default to improve etcd store consistency guarantees. This options is only available with etcd >= v3.5.5, so Talos doesn't support version of etcd before v3.5.5. ### Kernel Modules Talos now supports settings kernel module parameters. Eg: ```yaml machine: kernel: modules: - name: "br_netfilter" parameters: - nf_conntrack_max=131072 ``` ### Nano Pi R4S Talos now supports the Nano Pi R4S SBC. ### Raspberry Generic Images The Raspberry Pi 4 specific image has been deprecated and will be removed in the v1.4 release of Talos. Talos now ships a generic Raspberry Pi image that should support more Raspberry Pi variants. Refer to the docs at https://www.talos.dev/v1.3/talos-guides/install/single-board-computers/rpi_generic/ to find which ones are supported. ### Component Updates * Kubernetes: v1.26.0-alpha.1 * Flannel: v0.19.2 * CoreDNS: v1.10.0 * etcd: v3.5.5 * Linux: 5.15.70 ### Contributors * Andrey Smirnov * Noel Georgi * Andrey Smirnov * Artem Chernyshev * Dmitriy Matrenichev * Artem Chernyshev * Alexey Palazhchenko * Serge Logvinov * Andrew Rynhard * Utku Ozdemir * Kris Reeves * Marvin Drees * Philipp Sauter * Andrew Rynhard * Branden Cash * Matt Zahorik * Olli Janatuinen * Pau Campana * Sander Maijers * Seán C McCord * Spencer Smith * Steve Francis * Tim Jones ### Changes
105 commits

* [`18c377a4d`](https://github.com/siderolabs/talos/commit/18c377a4d1ce046b310e3609033e9c1f39f9337b) feat: customize audit policy * [`23c9ea46b`](https://github.com/siderolabs/talos/commit/23c9ea46bba20d8b7cc336bbc64e04af46cccf5d) fix: raspberry pi install * [`f17cdee16`](https://github.com/siderolabs/talos/commit/f17cdee167cfd6d673e2ed71fd5c8d28399a80f3) feat: jsonpath filter for talosctl get outputs * [`6bd3cca1a`](https://github.com/siderolabs/talos/commit/6bd3cca1a8d206fb40199a9f0352aa2670fca754) chore: generic raspberry pi images * [`d914ab8bb`](https://github.com/siderolabs/talos/commit/d914ab8bb4a34cdb5ffc396a20a32a437c5989e1) chore: add vulncheck tool as a linter * [`a0151aa13`](https://github.com/siderolabs/talos/commit/a0151aa13e63b24aba7e39082f6cef3dac923a22) feat: add generic rpi u-boot support * [`30f851d09`](https://github.com/siderolabs/talos/commit/30f851d0931f5d6767e13142876c94dac67ec38b) chore: bump dependences * [`8b2235c3b`](https://github.com/siderolabs/talos/commit/8b2235c3b6de64abb15bf77e9648bf6bebc18e1f) fix: lookup Equinix Metal bond slaves using 'permanent addr' * [`b3257ebb1`](https://github.com/siderolabs/talos/commit/b3257ebb1c529a8f266ba3852d5e4191e0261a79) chore: bump kernel to 5.15.70 * [`0b2767c16`](https://github.com/siderolabs/talos/commit/0b2767c1646e84ce147030692f3904b9feb02b3e) feat: implement 'permanent addr' in link statuses * [`c90e20251`](https://github.com/siderolabs/talos/commit/c90e20251d09a9bedcbd8b1a2055de5e126fc97e) fix: kubeconfig permission * [`fc48849d0`](https://github.com/siderolabs/talos/commit/fc48849d00c185442fb37c72e2c20462cc573a69) chore: move maps/slices/ordered to gen module * [`8b09bd4b0`](https://github.com/siderolabs/talos/commit/8b09bd4b0400f17ef543f0d117ae35e4ba2356cb) feat: update Kubernetes to v1.26.0-alpha.1 * [`276d4175b`](https://github.com/siderolabs/talos/commit/276d4175bbd168d12409a1e96b191abdf09f2ff0) chore: bump extension versions in testing * [`357b770cb`](https://github.com/siderolabs/talos/commit/357b770cb593196fccaf9b6ba3cd740463351a07) fix: cryptsetup delete slot * [`711128839`](https://github.com/siderolabs/talos/commit/7111288393ae4dfdfa7331e39df1803724bc93c0) fix: continue applying bootstrap manifests on some errors * [`ce12c7b38`](https://github.com/siderolabs/talos/commit/ce12c7b3805da65315309a465aeed1764f0ce20a) chore: update COSI runtime to v0.2.0-alpha.1 * [`1b435c0b3`](https://github.com/siderolabs/talos/commit/1b435c0b36a8d0d3e48c5a5e6121117933deeb69) chore: bump kernel + ice drivers * [`18e041f1e`](https://github.com/siderolabs/talos/commit/18e041f1ecb88d0b1e8e874d9b1fb580bc7c2297) docs: fix typo in patching example * [`0ad6452ca`](https://github.com/siderolabs/talos/commit/0ad6452ca152afef2f3c0e97a2255a237b30941a) feat: update CoreDNS to v1.10.0 * [`479f3f52e`](https://github.com/siderolabs/talos/commit/479f3f52ee7149ff2a39bec3d8f78b59978af70a) chore: bump dependencies * [`e07c6ae99`](https://github.com/siderolabs/talos/commit/e07c6ae99ec347735cf0316294ef0c54ebc45234) feat: update Kubernetes to v1.25.1 * [`13fdfaffc`](https://github.com/siderolabs/talos/commit/13fdfaffc4a0eb812cd63c5d188efd4aff6da51c) test: fix up default branch name * [`ef181321a`](https://github.com/siderolabs/talos/commit/ef181321a5be4d03e4f87aab1483b95a8e61f0fe) docs: add component diagram; K8s & Talos Linux * [`aade73643`](https://github.com/siderolabs/talos/commit/aade7364357da6644e8b70ad1dd939130f2fe470) docs: fix missing variable in OpenEBS docs * [`472590aa8`](https://github.com/siderolabs/talos/commit/472590aa82d16e1bd3825ecc8106886e7e1b9053) chore: return InvalidArgument on invalid config in maintenance mode * [`e5cabd42c`](https://github.com/siderolabs/talos/commit/e5cabd42cc7f86bee5486f73fa4068382bf6a7fb) feat: enable etcd consistency hashcheck * [`015535d90`](https://github.com/siderolabs/talos/commit/015535d9051dea243f439b385577d17fd57a122e) fix: update discovery client with the redirect fix * [`d0c8e7699`](https://github.com/siderolabs/talos/commit/d0c8e7699cf3e2415c5712ff9ff620c38857a0dc) chore: bump kernel and go * [`985b0c2e7`](https://github.com/siderolabs/talos/commit/985b0c2e796006f401376ebf30a1ce888d90a1c9) chore: remove go.work.sum * [`69124f102`](https://github.com/siderolabs/talos/commit/69124f10263bdabc556b58b98a3e1f129b85b8ab) feat: update etcd to v3.5.5 * [`1985a796c`](https://github.com/siderolabs/talos/commit/1985a796c0d5a984c397754445b33827f5690806) docs: update docs for pod security * [`94b088f02`](https://github.com/siderolabs/talos/commit/94b088f02f8f8e5b63f0c38e8e091f2ba3329dde) fix: set etcd options consistently * [`92ae7ef4b`](https://github.com/siderolabs/talos/commit/92ae7ef4b1abe0a510fea31e0fde2566281f38b1) fix: fix protoenc encoding for enums and types with custom encoders * [`93809017c`](https://github.com/siderolabs/talos/commit/93809017c594b1faf1405932d884852eb0ce567c) docs: cpu scaling governor knowledgebase * [`7b270ff33`](https://github.com/siderolabs/talos/commit/7b270ff33d6bf74d1fa195c07f98233098b337e9) test: fix api controller test * [`2dadcd669`](https://github.com/siderolabs/talos/commit/2dadcd6695003eb940848583caa6ade53ef94fa0) fix: stop worker nodes from acting as apid routers * [`9eaf33f3f`](https://github.com/siderolabs/talos/commit/9eaf33f3f274e746ca1b442c0a1a0dae0cec088f) fix: never sign client certificate requests in trustd * [`436749124`](https://github.com/siderolabs/talos/commit/43674912479d3fb58c30e350fea9c4daf4ba45d4) feat: environment vars for extension service * [`0c0cb671e`](https://github.com/siderolabs/talos/commit/0c0cb671ead1f514b1f1eb89e8d78f455e1efedb) chore: mark machine configuration validation failure as InvalidArgument * [`f424e5340`](https://github.com/siderolabs/talos/commit/f424e53404db61bbdbcbe8fab7cfec91785aa628) fix: stop containers more thoroughly * [`12827b861`](https://github.com/siderolabs/talos/commit/12827b861c13bb9b83a2f0ea2960582e8be319f0) chore: move "implements" checks to compile time * [`3a67c42cb`](https://github.com/siderolabs/talos/commit/3a67c42cbfdbd565e0af500d97c264ef6095637b) fix: kill the task processes when cleaning up stale task * [`14a79e325`](https://github.com/siderolabs/talos/commit/14a79e325bf0ffa107aaee9c07d3501b7010693c) chore: bump dependencies * [`9beee92e7`](https://github.com/siderolabs/talos/commit/9beee92e71e712a2af24dee612e27c30cac39d0d) docs: fix double vv in Kubernetes version * [`688272515`](https://github.com/siderolabs/talos/commit/6882725157f4c2ea79c248f79160e362be6c2c07) fix: use different username for Talos Kubernetes API access * [`161a52a9e`](https://github.com/siderolabs/talos/commit/161a52a9ef60eb9c1c1a6c31b06d06894456300c) feat: check apid client certificate extended key usage * [`9dadc4a59`](https://github.com/siderolabs/talos/commit/9dadc4a599f52cc564f5411dd35bc981e482d24a) fix: include all node addresses into etcd cert SANs * [`71bfd3e43`](https://github.com/siderolabs/talos/commit/71bfd3e43cdc9790d3cb7a134c3b49256b1942a1) feat: update CoreDNS to 1.9.4 * [`9df8f1ff1`](https://github.com/siderolabs/talos/commit/9df8f1ff1aebb24a6b0649ba491b10b23a0b2198) fix: list COSI APIs for the apid authenticator * [`31462450f`](https://github.com/siderolabs/talos/commit/31462450f19700dd6691ebc4b0c18edca4f6a1b7) fix: pass a pointer to specs.Mount into protoenc.Marshal * [`e626540df`](https://github.com/siderolabs/talos/commit/e626540dfb470386d0750f2f8bbaf4b5cb36b203) chore: avoid double API request logging in trustd * [`f62d17125`](https://github.com/siderolabs/talos/commit/f62d17125b8c1b26b0b62d22c2846f3a2ece37d1) chore: update crypto to use new import path siderolabs/crypto * [`ef27dd855`](https://github.com/siderolabs/talos/commit/ef27dd8553ee0e5467c3baaf4be18d1ccb30dad1) chore: bump dependencies * [`6472ae00b`](https://github.com/siderolabs/talos/commit/6472ae00b21c0f637b1e6610a8f3f71a1b775628) fix: automatically discard VIPs for etcd advertised addresses * [`5e21cca52`](https://github.com/siderolabs/talos/commit/5e21cca52d7462240bb42aafa225ee97d08bdc25) feat: support setting kernel parameters * [`bd56621cd`](https://github.com/siderolabs/talos/commit/bd56621cdf50d25013756a8792dc7b4d5354396f) feat: add structprotogen tool * [`cdb6bb2cc`](https://github.com/siderolabs/talos/commit/cdb6bb2cc78685c218506c61a477c8a8e569e861) feat: add Nano Pi R4S support * [`36c1f1d6e`](https://github.com/siderolabs/talos/commit/36c1f1d6e6aa50379343acba5348d8cc038b137e) fix: flip the client-server version check * [`cd6c53a97`](https://github.com/siderolabs/talos/commit/cd6c53a979236543afc302a67da627ee633883b3) docs: fork docs for v1.3 * [`0847400f7`](https://github.com/siderolabs/talos/commit/0847400f728d67889b9f740a0359eb916108d8ea) fix: prevent panic on health check if a member has no IPs * [`7471d7f01`](https://github.com/siderolabs/talos/commit/7471d7f0174a5240fa3c4cd2f16325ec2a4f1810) feat: update Flannel to v0.19.2 * [`148c75cfb`](https://github.com/siderolabs/talos/commit/148c75cfb99537f64d43a3add3259bf591cb79a9) docs: consolidate the control-plane documentation * [`353154281`](https://github.com/siderolabs/talos/commit/353154281a4cf72076b99160e50e617109f72996) fix: drop kube-system SA default binding * [`4f37b668b`](https://github.com/siderolabs/talos/commit/4f37b668befdbd26bc2d32106e0bcc654f7e6119) chore: remove capi hacks * [`1369afea8`](https://github.com/siderolabs/talos/commit/1369afea853423f22fde20effd431c3f8d906a9d) docs: make 1.2.0 docs default ones * [`7627cb0e3`](https://github.com/siderolabs/talos/commit/7627cb0e30a8b2a5a1cc30906b547511c9d3c98b) docs: add new `talosctl gen secrets` * [`8aa60a37a`](https://github.com/siderolabs/talos/commit/8aa60a37a6ea57bf54d558c7a2f54d806fad3173) chore: bump kernel to 5.15.64 * [`a798dbd5d`](https://github.com/siderolabs/talos/commit/a798dbd5d2d9bc6d1410a56035550d44de934950) docs: update docs for upcoming 1.2.0 release * [`b2fec3c97`](https://github.com/siderolabs/talos/commit/b2fec3c975dba7b0bc2dc7d5447e62350057061b) fix: properly handle `configContext` being `nil` in Talos client * [`1c0977b3a`](https://github.com/siderolabs/talos/commit/1c0977b3af22f9f4b61b80ca6dcedf14a5ef63ae) fix: change the type of returned gRPC connection object from the client * [`41848e421`](https://github.com/siderolabs/talos/commit/41848e421496184008ad2302e3cb03a882c0f5bf) fix: expose Talos client gRPC connection via the function `Conn` * [`2e9be4af8`](https://github.com/siderolabs/talos/commit/2e9be4af8b521eca985c425f62dfc7a59d19e7da) chore: bump dependencies * [`d283aba3a`](https://github.com/siderolabs/talos/commit/d283aba3a3670cfde8ab9137deba3ab3b343906f) test: fix cli reboot test * [`0b339a9dc`](https://github.com/siderolabs/talos/commit/0b339a9dc508327347777619749ff1e2c3e47f37) feat: track progress of action API calls * [`072349812`](https://github.com/siderolabs/talos/commit/072349812506c5cd32159bb14bab5b294ee59811) fix: update COSI to the version with gRPC Wait fix * [`89d57aa81`](https://github.com/siderolabs/talos/commit/89d57aa816a57448d6e350698a8f6a5d128209ac) fix: always abort the maintenance service * [`f6fa74619`](https://github.com/siderolabs/talos/commit/f6fa7461932462160f40f670a5252fbc2981bdc3) fix: limit apid backoff max delay * [`d7ef346db`](https://github.com/siderolabs/talos/commit/d7ef346db8ea7d4f7676ae5e032a3c0d06823d47) fix: get command in the case 'nodes' are not set in the context * [`4e9c32256`](https://github.com/siderolabs/talos/commit/4e9c322564d7f65c82d636a9f80c0c5354455967) fix: correctly render hosts.toml with multiple endpoints * [`cdd0f08bc`](https://github.com/siderolabs/talos/commit/cdd0f08bc5d8d47bc2d21745ee5a13ced3632c8a) feat: check client <> server version in some Talos commands * [`446b0af58`](https://github.com/siderolabs/talos/commit/446b0af58bf273712374472bfa2777de5b7ac46f) chore: bump kernel and runc * [`8c203ce9b`](https://github.com/siderolabs/talos/commit/8c203ce9b1722c5832c506857cb56e14e2a34fe1) feat: remove the machine from the discovery service on reset * [`b59ca5810`](https://github.com/siderolabs/talos/commit/b59ca5810e6cf75f6a3042a47535431110004201) chore: move from inet.af/netaddr to net/netip and go4.org/netipx * [`053af1d59`](https://github.com/siderolabs/talos/commit/053af1d59ea266b84bb049460f92b33b32c1b82e) fix: update etcd certificates when node addresses changes * [`11edb2c6f`](https://github.com/siderolabs/talos/commit/11edb2c6f84fbbfba437361ce4dcd70c50eb08d8) test: re-enable upgrade tests * [`0310e2089`](https://github.com/siderolabs/talos/commit/0310e20890b11e1f4015e923eb9984aea1188d20) chore: bump github.com/siderolabs/protoenc to v0.1.5 * [`29bd63240`](https://github.com/siderolabs/talos/commit/29bd632401ca694df0a2ab921a2a525b4c3440d8) chore: remove old build tags syntax * [`b500d0aa9`](https://github.com/siderolabs/talos/commit/b500d0aa9052ab5066eb6cde06bcdac3e998705a) chore: bump k8s to v1.25.0 * [`29e574be7`](https://github.com/siderolabs/talos/commit/29e574be74c96211fd010ee5bd06675898f04db8) docs: update to v1.2.0-beta.1 * [`26b549f2a`](https://github.com/siderolabs/talos/commit/26b549f2a12c3486b52a8877b8a0a4f985695c7d) chore: bump dependencies * [`8c3ac4c42`](https://github.com/siderolabs/talos/commit/8c3ac4c42bff1f1678ddb62e0f20a9c419460ad4) chore: limit GOMAXPROCS for Talos services * [`361e85b74`](https://github.com/siderolabs/talos/commit/361e85b7443f6f4ff24fbf99a9f9276b73b73ed4) fix: properly read kexec disabled sysctl * [`cfe6c2bc2`](https://github.com/siderolabs/talos/commit/cfe6c2bc2d42ca28f3a5b3217aa4d126777e3db6) docs: nvidia oss drivers * [`2f2d97b6b`](https://github.com/siderolabs/talos/commit/2f2d97b6b5663a0873db9d47b7706f2c0a531d8c) fix: don't wait for the hostname in maintenance mode * [`b15a63924`](https://github.com/siderolabs/talos/commit/b15a6392465aa2aa0df231c622ca1762972ccd20) chore: bump kernel to 5.15.62 * [`a0d94be30`](https://github.com/siderolabs/talos/commit/a0d94be30d3dcf41b2b8b34a1caa6928a029f81a) fix: stable default hostname bias * [`da4cd34ef`](https://github.com/siderolabs/talos/commit/da4cd34ef5c5a01cfc3c3ee56b3f8c2f77997b49) feat: update etcd advertised peer addresses on the fly * [`faf92ce01`](https://github.com/siderolabs/talos/commit/faf92ce01661c5a9a86f9e579da3a2822d93f1f6) chore: bump kubernetes to v1.25.0-rc.1 * [`52de919e3`](https://github.com/siderolabs/talos/commit/52de919e34789c36c4ee71ca133240b50b068064) chore: bump containerd to v1.6.8 * [`7d43fc79b`](https://github.com/siderolabs/talos/commit/7d43fc79b1e913d51f111ecc7c2c8b3bfb36e679) fix: make 'ca', 'crt' and 'key' flags optional for 'talosctl config add' * [`fd467e02c`](https://github.com/siderolabs/talos/commit/fd467e02c1edcfc0eff656392ece5dd8ba1114f2) fix: handle grub config being empty in the `Revert` function * [`9492aca65`](https://github.com/siderolabs/talos/commit/9492aca652eec4d4049fef1c8d141696ed72a197) fix: clean up `cancelCtxMu` leftovers in PriorityLock * [`61e3eb2ea`](https://github.com/siderolabs/talos/commit/61e3eb2eaab1c7974a27440ddd98139a27dfb9dc) fix: talosctl edit mc loop * [`32db7a7f5`](https://github.com/siderolabs/talos/commit/32db7a7f5d6638fc0f731a009dfb0c1870c69083) fix: surround `cancelCtx` with the mutex

### Changes from siderolabs/crypto
27 commits

* [`c3225ee`](https://github.com/siderolabs/crypto/commit/c3225eee603a8d1218c67e1bfe33ddde7953ed74) feat: allow CSR template subject field to be overridden * [`8570669`](https://github.com/siderolabs/crypto/commit/85706698dac8cddd0e9f41006bed059347d2ea26) chore: rename to siderolabs/crypto * [`e9df1b8`](https://github.com/siderolabs/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs * [`510b0d2`](https://github.com/siderolabs/crypto/commit/510b0d2753a89170d0c0f60e052a66484997a5b2) chore: add json tags * [`6fa2d93`](https://github.com/siderolabs/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil` * [`9a63cba`](https://github.com/siderolabs/crypto/commit/9a63cba8dabd278f3080fa8c160613efc48c43f8) fix: add back support for generating ECDSA keys with P-256 and SHA512 * [`893bc66`](https://github.com/siderolabs/crypto/commit/893bc66e4716a4cb7d1d5e66b5660ffc01f22823) fix: use SHA256 for ECDSA-P256 * [`deec8d4`](https://github.com/siderolabs/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types * [`d3cb772`](https://github.com/siderolabs/crypto/commit/d3cb77220384b3a3119a6f3ddb1340bbc811f1d1) feat: make possible to change KeyUsage * [`6bc5bb5`](https://github.com/siderolabs/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument * [`cd18ef6`](https://github.com/siderolabs/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations * [`97c888b`](https://github.com/siderolabs/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR * [`7776057`](https://github.com/siderolabs/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos * [`80df078`](https://github.com/siderolabs/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters * [`15bdd28`](https://github.com/siderolabs/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates * [`4f80b97`](https://github.com/siderolabs/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate * [`39584f1`](https://github.com/siderolabs/crypto/commit/39584f1b6e54e9966db1f16369092b2215707134) feat: support for key/certificate types RSA, Ed25519, ECDSA * [`cf75519`](https://github.com/siderolabs/crypto/commit/cf75519cab82bd1b128ae9b45107c6bb422bd96a) fix: function NewKeyPair should create certificate with proper subject * [`751c95a`](https://github.com/siderolabs/crypto/commit/751c95aa9434832a74deb6884cff7c5fd785db0b) feat: add 'PEMEncodedKey' which allows to transport keys in YAML * [`562c3b6`](https://github.com/siderolabs/crypto/commit/562c3b66f89866746c0ba47927c55f41afed0f7f) feat: add support for public RSA key in RSAKey * [`bda0e9c`](https://github.com/siderolabs/crypto/commit/bda0e9c24e80c658333822e2002e0bc671ac53a3) feat: enable more conversions between encoded and raw versions * [`e0dd56a`](https://github.com/siderolabs/crypto/commit/e0dd56ac47456f85c0b247999afa93fb87ebc78b) feat: add NotBefore option for x509 cert creation * [`12a4897`](https://github.com/siderolabs/crypto/commit/12a489768a6bb2c13e16e54617139c980f99a658) feat: add support for SPKI fingerprint generation and matching * [`d0c3eef`](https://github.com/siderolabs/crypto/commit/d0c3eef149ec9b713e7eca8c35a6214bd0a64bc4) fix: implement NewKeyPair * [`196679e`](https://github.com/siderolabs/crypto/commit/196679e9ec77cb709db54879ddeddd4eaafaea01) feat: move `pkg/grpc/tls` from `github.com/talos-systems/talos` as `./tls` * [`1ff6242`](https://github.com/siderolabs/crypto/commit/1ff6242c91bb298ceeb4acd65685cba952fe4178) chore: initial version as imported from talos-systems/talos * [`835063e`](https://github.com/siderolabs/crypto/commit/835063e055b28a525038b826a6d80cbe76402414) chore: initial commit

### Changes from siderolabs/discovery-api
3 commits

* [`5b0c5e7`](https://github.com/siderolabs/discovery-api/commit/5b0c5e78097c1489457b148a7f13c73890f5ecad) chore: rename to siderolabs, rekres, etc * [`db279ef`](https://github.com/siderolabs/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/siderolabs/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from siderolabs/discovery-client
1 commit

* [`230f317`](https://github.com/siderolabs/discovery-client/commit/230f317a8e6e9542b82efcbac9f5cd7b9cff34b6) fix: reconnect the client on update failure

### Changes from siderolabs/gen
4 commits

* [`726e066`](https://github.com/siderolabs/gen/commit/726e066dcb35c86f82866097bed806f22b936292) fix: rename tuples.go to pair.go and set proper package name * [`d8d7d25`](https://github.com/siderolabs/gen/commit/d8d7d25ce9a588609c00cb798206a01a866bf7a6) chore: minor additions * [`338a650`](https://github.com/siderolabs/gen/commit/338a65065f92eb6426a66c4a88a0cc02cc02e529) chore: add initial implementation and documentation * [`4fd8667`](https://github.com/siderolabs/gen/commit/4fd866707052c792a6adccbc28efec5debdd18a8) Initial commit

### Changes from siderolabs/go-blockdevice
55 commits

* [`dcf6044`](https://github.com/siderolabs/go-blockdevice/commit/dcf6044c906b36f183e11b6553458c680126d1d9) chore: rekres and rename * [`9c4af49`](https://github.com/siderolabs/go-blockdevice/commit/9c4af492cc17279f0281fcd271e7423be78442bb) fix: cryptsetup remove slot * [`74ea471`](https://github.com/siderolabs/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs * [`9fa801c`](https://github.com/siderolabs/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk * [`fccee8b`](https://github.com/siderolabs/go-blockdevice/commit/fccee8bb082b105cb60db40cb01636efc3241b5f) chore: rekres the source, fix issues * [`d9c3a27`](https://github.com/siderolabs/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems * [`b374eb4`](https://github.com/siderolabs/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default * [`ec428fe`](https://github.com/siderolabs/go-blockdevice/commit/ec428fed2ecd5a389833a88f8dc333762816db99) fix: lookup filesystem labels on the actual device path * [`7b9de26`](https://github.com/siderolabs/go-blockdevice/commit/7b9de26bc6bc3d54b95bd8e8fb3aade4b45adc6c) feat: read symlink fullpath in block device list function * [`6928ee4`](https://github.com/siderolabs/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/siderolabs/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions * [`15b182d`](https://github.com/siderolabs/go-blockdevice/commit/15b182db0cd233b163ed83d1724c7e28cf29d71a) fix: return partition table not exist when trying to read an empty dev * [`b9517d5`](https://github.com/siderolabs/go-blockdevice/commit/b9517d51120d385f97b0026f99ce3c4782940c37) fix: resize partition * [`70d2865`](https://github.com/siderolabs/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks * [`667bf53`](https://github.com/siderolabs/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found * [`d7d4cdd`](https://github.com/siderolabs/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found * [`33afba3`](https://github.com/siderolabs/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method * [`e367f9d`](https://github.com/siderolabs/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode * [`d981156`](https://github.com/siderolabs/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows * [`fe24303`](https://github.com/siderolabs/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations * [`2ec0c3c`](https://github.com/siderolabs/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition * [`87816a8`](https://github.com/siderolabs/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size * [`c34b59f`](https://github.com/siderolabs/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module * [`30c2bc3`](https://github.com/siderolabs/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable * [`1292574`](https://github.com/siderolabs/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/siderolabs/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types * [`1d830a2`](https://github.com/siderolabs/go-blockdevice/commit/1d830a25f64f6fb96a1bedd800c0b40b107dc833) fix: revert mark the EFI partition in PMBR as bootable * [`bec914f`](https://github.com/siderolabs/go-blockdevice/commit/bec914ffdda42abcfe642bc2cdfc9fcda56a74ee) fix: mark the EFI partition in PMBR as bootable * [`776b37d`](https://github.com/siderolabs/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters * [`bb3ad73`](https://github.com/siderolabs/go-blockdevice/commit/bb3ad73f69836acc2785ec659435e24a531359e7) fix: align partition start to physical sector size * [`8f976c2`](https://github.com/siderolabs/go-blockdevice/commit/8f976c2031108651738ebd4db69fb09758754a28) feat: replace exec.Command with go-cmd module * [`1cf7f25`](https://github.com/siderolabs/go-blockdevice/commit/1cf7f252c38cf11ef07723de2debc27d1da6b520) fix: properly handle no child processes error from cmd.Wait * [`04a9851`](https://github.com/siderolabs/go-blockdevice/commit/04a98510c07fe8477f598befbfe6eaec4f4b73a2) feat: implement luks encryption provider * [`b0375e4`](https://github.com/siderolabs/go-blockdevice/commit/b0375e4267fdc6108bd9ff7a5dc97b80cd924b1d) feat: add an option to open block device with exclusive flock * [`5a1c7f7`](https://github.com/siderolabs/go-blockdevice/commit/5a1c7f768e016c93f6c0be130ffeaf34109b5b4d) refactor: add devname into gpt.Partition, refactor probe package * [`f2728a5`](https://github.com/siderolabs/go-blockdevice/commit/f2728a581972be977d863d5d9177a873b8f3fc7b) fix: keep contents of PMBR when writing it * [`2878460`](https://github.com/siderolabs/go-blockdevice/commit/2878460b54e8b8c3846c6a882ca9e1472c8b6b3b) fix: write second copy of partition entries * [`943b08b`](https://github.com/siderolabs/go-blockdevice/commit/943b08bc32a2156cffb23e92b8be9288de4a7421) fix: blockdevice reset should read partition table from disk * [`5b4ee44`](https://github.com/siderolabs/go-blockdevice/commit/5b4ee44cfd434a03ec2d7167bcc56d0f164c3fa2) fix: ignore `/dev/ram` devices * [`98754ec`](https://github.com/siderolabs/go-blockdevice/commit/98754ec2bb200acc9e9e573fa766754d60e25ff2) refactor: rewrite GPT library * [`2a1baad`](https://github.com/siderolabs/go-blockdevice/commit/2a1baadffdf8c9b65355e9af6e744aeab838c9db) fix: correctly build paths for `mmcblk` devices * [`8076344`](https://github.com/siderolabs/go-blockdevice/commit/8076344a95021f25ab5d1fbf5ea4fefc790f6c3c) fix: return proper disk size from GetDisks function * [`8742133`](https://github.com/siderolabs/go-blockdevice/commit/874213371a3fb0925aab45cbba68a957e3319525) chore: add common method to list available disks using /sys/block * [`c4b5833`](https://github.com/siderolabs/go-blockdevice/commit/c4b583363d63503ed7e4adb9a9fa64335f7e198d) feat: implement "fast" wipe * [`b4e67d7`](https://github.com/siderolabs/go-blockdevice/commit/b4e67d73d70d8dc06aa2b4986622dcb854dfc40c) feat: return resize status from Resize() function * [`ceae64e`](https://github.com/siderolabs/go-blockdevice/commit/ceae64edb3a591c6f6bbd75b1149d1cfe426dd8e) fix: sync kernel partition table incrementally * [`2cb9516`](https://github.com/siderolabs/go-blockdevice/commit/2cb95165aa67b0b839863b5ad89920c3ac7e2c82) fix: return correct error value from blkpg functions * [`cebe43d`](https://github.com/siderolabs/go-blockdevice/commit/cebe43d1fdc1e509437198e578faa9d5a804cc37) refactor: expose `InsertAt` method via interface * [`c40dcd8`](https://github.com/siderolabs/go-blockdevice/commit/c40dcd80c50b41c1f2a60ea6aa9d5fb3d3b180a3) fix: properly inform kernel about partition deletion * [`bb8ac5d`](https://github.com/siderolabs/go-blockdevice/commit/bb8ac5d6a25e279e16213f585dc8d02ba6ed645f) feat: implement disk wiping via several methods * [`23fb7dc`](https://github.com/siderolabs/go-blockdevice/commit/23fb7dc755325cfe12e48c8e8e31bebab9ddc2bc) feat: expose partition name (label) * [`ff3a821`](https://github.com/siderolabs/go-blockdevice/commit/ff3a8210be999b8bfb2019f19f8a8b50901c64cc) feat: implement 'InsertAt' method to insert partitions at any position * [`3d1ce4f`](https://github.com/siderolabs/go-blockdevice/commit/3d1ce4fc859fa614a4c5c54a10c0f5f4fce38bb6) fix: calculate last lba of partition correctly * [`b71540f`](https://github.com/siderolabs/go-blockdevice/commit/b71540f6c398e958bdb7c118396a736419f735d4) feat: copy initial version from talos-systems/talos * [`ca3c078`](https://github.com/siderolabs/go-blockdevice/commit/ca3c078da95e6497c9d41667dc242e32682e517d) Initial commit

### Changes from siderolabs/pkgs
26 commits

* [`0ac7773`](https://github.com/siderolabs/pkgs/commit/0ac77733506d2f0b0944ff569b6817ae44821bda) chore: use generic raspberry pi u-boot * [`d5633d4`](https://github.com/siderolabs/pkgs/commit/d5633d4838bd6e168b9c80f124540a30c29ae7be) chore: bump kernel to 5.15.70 * [`39c0d43`](https://github.com/siderolabs/pkgs/commit/39c0d4364fd4eedd281e46ce7d305f2562e2cf78) feat: add generic rpi_arm64_defconfig configuration * [`ed269ca`](https://github.com/siderolabs/pkgs/commit/ed269cabad82446095221e45078c8ba85bce5c2e) chore: bump kernel to 5.15.69 * [`f2f8333`](https://github.com/siderolabs/pkgs/commit/f2f83331f93a0a5d2dd1c013e2ff46900684096a) fix: no slack notifications on failure * [`6f0af33`](https://github.com/siderolabs/pkgs/commit/6f0af3390fc170f0cf57450adfada6a87de7ece4) chore: disable drone slack pipeline for renovate * [`32aea3f`](https://github.com/siderolabs/pkgs/commit/32aea3f005b93aaa91d52e4dfd04dd9ce9d564a9) chore: disable drone for renovate/dependabot * [`44579f0`](https://github.com/siderolabs/pkgs/commit/44579f0238993f529e2c141f42c99b32803fd6a5) fix: rollback xfsprogs to 5.18.0 * [`792c0e3`](https://github.com/siderolabs/pkgs/commit/792c0e32ef6b1cf13514dc2693c4c302e1440d3b) feat: add gasket driver package * [`07f1898`](https://github.com/siderolabs/pkgs/commit/07f1898b231390b85519f83638946ed65adacc64) chore: update deps * [`f78f410`](https://github.com/siderolabs/pkgs/commit/f78f410d193953e730aeb14f4e148e47dfa827fd) chore: enable conntrack zones and timestamps * [`049b3c6`](https://github.com/siderolabs/pkgs/commit/049b3c6f080b9af76b1b2e924baade69db27bc0b) chore: enable intel ice drivers * [`606ff32`](https://github.com/siderolabs/pkgs/commit/606ff32cb7e75b6975749b6250b68352b71e943b) chore: bump deps * [`eee5c8a`](https://github.com/siderolabs/pkgs/commit/eee5c8af13ee1fe0b1e660a9581d4f1b14158a39) chore: disable irc in conntrack * [`70e6c46`](https://github.com/siderolabs/pkgs/commit/70e6c460d7b3bd5e154a4e681858832afcf32368) chore: bump kernel to 5.15.64 * [`e510321`](https://github.com/siderolabs/pkgs/commit/e5103217e714bea04e06fd0c4940e84406cb68cf) chore: update renovate config * [`d1fa510`](https://github.com/siderolabs/pkgs/commit/d1fa510cc66ddc63a53482f6ced5573466049d49) feat: enable renovate bot * [`e427a77`](https://github.com/siderolabs/pkgs/commit/e427a778146664b988664008bfe20611f91216b0) chore: bump runc to v1.1.4 * [`40e1215`](https://github.com/siderolabs/pkgs/commit/40e12152a027eb509330c41db21680b9a662fa05) chore: enable nfsv4.2 client support * [`15efada`](https://github.com/siderolabs/pkgs/commit/15efadaa9db4b8dc8003359d6d0ed84016f54746) chore: bump kernel to 5.15.63 * [`e70e3c1`](https://github.com/siderolabs/pkgs/commit/e70e3c1af2b11d4b4646401a617b3d0efa2db4a3) fix: nvidia oss pkg name * [`30b8d79`](https://github.com/siderolabs/pkgs/commit/30b8d79b9ca3e463b5f403f01d39e64e89edc7b1) chore: bump kernel to 5.15.62 * [`862c392`](https://github.com/siderolabs/pkgs/commit/862c392b6defe3c9ce90f9b15eae154e021b0b4d) chore: bump gcc to 12.2.0 * [`2ecd14e`](https://github.com/siderolabs/pkgs/commit/2ecd14ede04637a581fbe7dcbbf612cdd6f9d882) fix: containerd version * [`01df058`](https://github.com/siderolabs/pkgs/commit/01df0583a430f3793f19725c920e942cf37efee4) feat: add NanoPi R4S configuration * [`d4cb33b`](https://github.com/siderolabs/pkgs/commit/d4cb33b9bdfb8c27ea86a42ea60a88e294129ad4) chore: bump containerd to v1.6.8

### Changes from siderolabs/tools
15 commits

* [`5df6589`](https://github.com/siderolabs/tools/commit/5df658937f7bd667ceda8760e2e15ed85c80dc2c) chore: disable drone for renovate/dependabot * [`1f00d2e`](https://github.com/siderolabs/tools/commit/1f00d2e854cdf357c1192428bd44ee846af1b4e4) fix: revert gawk to 5.1.1 * [`feeda1f`](https://github.com/siderolabs/tools/commit/feeda1fc708a0cdb461ac5967ec34bf24ccc2b62) chore: bump grpc-go * [`8542014`](https://github.com/siderolabs/tools/commit/8542014568a101fb6c03a76c91e59dcfb1b893b6) chore: bump deps * [`e5c4968`](https://github.com/siderolabs/tools/commit/e5c496893fb71ff19a33daa4c86792ed03187356) chore: update renovate config * [`f34f94d`](https://github.com/siderolabs/tools/commit/f34f94daa300baab0803f22cecee65b57ee3c1fd) chore: update renovate config * [`cef4cc6`](https://github.com/siderolabs/tools/commit/cef4cc67342c06904258bcf4b7ec681d4c732d53) chore: update renovate config * [`bab8e9e`](https://github.com/siderolabs/tools/commit/bab8e9ee8d0fc2dc1b5676a45175b507d8927e49) chore: add libbpf to tools * [`0a15f7b`](https://github.com/siderolabs/tools/commit/0a15f7bb35f479fbf5551ea4bf02f3716783e33f) chore: build pahole properly * [`a322d06`](https://github.com/siderolabs/tools/commit/a322d066483814db80a15b8c0c7f44224b134429) chore: remove img * [`c7ff47b`](https://github.com/siderolabs/tools/commit/c7ff47b27962cf0f6a95e07c6f45aa2a3c2c5c8b) feat: enable renovate dependency updates (3/3) * [`6e095cf`](https://github.com/siderolabs/tools/commit/6e095cf86a6f734b2f07cc1b854a9a37b055cacc) feat: enable renovate dependency updates (2/n) * [`bad1ad1`](https://github.com/siderolabs/tools/commit/bad1ad17f7fd1208fcbb70b950320f805a765868) feat: add renovatebot * [`7d6f9c3`](https://github.com/siderolabs/tools/commit/7d6f9c35a81392918560ea0c20b3c06b18501ea0) chore: bump gcc to 12.2.0 * [`2719b4b`](https://github.com/siderolabs/tools/commit/2719b4be551134a9d70ab235f56889708377f3c5) chore: bump toolchain

### Dependency Changes * **cloud.google.com/go/compute** v1.8.0 -> v1.10.0 * **github.com/aws/aws-sdk-go** v1.44.76 -> v1.44.105 * **github.com/cosi-project/runtime** v0.1.1 -> v0.2.0-alpha.1 * **github.com/docker/docker** v20.10.17 -> v20.10.18 * **github.com/google/go-cmp** v0.5.8 -> v0.5.9 * **github.com/google/nftables** 2eca00135732 -> cbeb0fb1eccf * **github.com/hetznercloud/hcloud-go** v1.35.2 -> v1.35.3 * **github.com/insomniacslk/dhcp** 509691fd59ec -> 043f1726f02e * **github.com/mdlayher/ethtool** 856bd6cb8a38 -> 0e16326d06d1 * **github.com/mdlayher/netlink** v1.6.0 -> v1.6.2 * **github.com/opencontainers/image-spec** c5a74bcca799 -> v1.1.0-rc1 * **github.com/packethost/packngo** v0.25.0 -> v0.26.0 * **github.com/rivo/tview** 0e6b21a48e96 -> 2e69b7385a37 * **github.com/siderolabs/crypto** v0.4.0 **_new_** * **github.com/siderolabs/discovery-api** v0.1.1 **_new_** * **github.com/siderolabs/discovery-client** v0.1.1 -> v0.1.2 * **github.com/siderolabs/gen** v0.2.0 **_new_** * **github.com/siderolabs/go-blockdevice** v0.4.0 **_new_** * **github.com/siderolabs/pkgs** v1.2.0-8-g970860d -> v1.3.0-alpha.0-25-g0ac7773 * **github.com/siderolabs/tools** v1.2.0 -> v1.3.0-alpha.0-14-g5df6589 * **github.com/vmware-tanzu/sonobuoy** v0.56.9 -> v0.56.10 * **go.etcd.io/etcd/api/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/pkg/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/client/v3** v3.5.4 -> v3.5.5 * **go.etcd.io/etcd/etcdutl/v3** v3.5.4 -> v3.5.5 * **go.uber.org/atomic** v1.9.0 -> v1.10.0 * **go.uber.org/zap** v1.22.0 -> v1.23.0 * **go4.org/netipx** 797b0c90d8ab **_new_** * **golang.org/x/net** 3211cb980234 -> 8be639271d50 * **golang.org/x/sync** 886fb9371eb4 -> 7f9b1623fab7 * **golang.org/x/sys** fbc7d0a398ab -> fb04ddd9f9c8 * **golang.org/x/term** a9ba230a4035 -> 7a66f970e087 * **golang.org/x/time** e5dcc9cfc0b9 -> f3bd1da661af * **golang.zx2c4.com/wireguard/wgctrl** 3d4a969bb56b -> 473347a5e6e3 * **google.golang.org/grpc** v1.48.0 -> v1.49.0 * **k8s.io/api** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/apimachinery** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/apiserver** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/client-go** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/component-base** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/cri-api** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/kubectl** v0.25.0 -> v0.26.0-alpha.1 * **k8s.io/kubelet** v0.25.0 -> v0.26.0-alpha.1 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.65 -> v1.2.66 Previous release can be found at [v1.2.0](https://github.com/siderolabs/talos/releases/tag/v1.2.0) ## [Talos 1.2.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.2.0-alpha.2) (2022-08-10) Welcome to the v1.2.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Talos API access from Kubernetes Talos now supports access to its API from within Kubernetes. It can be configured in the machine config as below: ```yaml machine: features: kubernetesTalosAPIAccess: enabled: true allowedRoles: - os:reader allowedKubernetesNamespaces: - kube-system ``` This feature introduces a new custom resource definition, `serviceaccounts.talos.dev`. Creating custom resources of this type will provide credentials to access Talos API from within Kubernetes. The new CLI subcommand `talosctl inject serviceaccount` can be used to configure Kubernetes manifests with Talos service accounts as below: ``` talosctl inject serviceaccount -f manifests.yaml > manifests-injected.yaml kubectl apply -f manifests-injected.yaml ``` See [documentation](https://www.talos.dev/v1.2/advanced/configuration/talos-api-access-from-k8s/) for more details. ### Generating Talos secrets from PKI directory It is now possible to generate a secrets bundle from a Kubernetes PKI directory (e.g. `/etc/kubernetes/pki`). You can also specify a bootstrap token to be used in the secrets bundle. This secrets bundle can then be used to generate a machine config. This facilitates migrating clusters (e.g. created using `kubeadm`) to Talos. ``` talosctl gen secrets --kubernetes-bootstrap-token znzio1.1ifu15frz7jd59pv --from-kubernetes-pki /etc/kubernetes/pki talosctl gen config --with-secrets secrets.yaml my-cluster https://172.20.0.1:6443 ``` ### Kubernetes ControlPlane Components Talos now run all Kubernetes Control Plane Components with the CRI default Seccomp Profile and other recommendations as described in [KEP-2568](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/kubeadm/2568-kubeadm-non-root-control-plane). ### Kubelet Default Runtime Seccomp Profile Talos now runs Kubelet with the CRI default Seccomp Profile enabled. This can be disabled by setting `.machine.kubelet.defaultRuntimeSeccompProfileEnabled` to `false`. This is not enabled automatically on upgrades, so upgrading to Talos v1.2 needs this to be explicitly enabled. ### Kubernetes Control Plane labels and taints Talos now defaults to `node-role.kubernetes.io/control-plane` label/taint. On upgrades Talos now removes the `node-role.kubernetes.io/master` label/taint on control-plane nodes and replaces it with the `node-role.kubernetes.io/control-plane` label/taint. Workloads that tolerate the old taints or having node selectors with the old labels will need to be updated. ### Kubernetes Discovery Backend Kubernetes cluster discovery backend is now disabled by default for new clusters. This backend doesn't provide any benefits over the Discovery Service based backend, while it causes issues for KubeSpan enabled clusters when control plane endpoint is KubeSpan-routed. For air-gapped installations when the Discovery Service is not enabled, Kubernetes Discovery Backend can be enabled by applying the following machine configuration patch: ```yaml cluster: discovery: registries: kubernetes: disabled: false ``` ### KubeSpan Kubernetes Network Advertisement KubeSpan no longer by default advertises Kubernetes pod networks of the node over KubeSpan. This means that CNI should handle encapsulation of pod-to-pod traffic into the node-to-node tunnel, and node-to-node traffic will be handled by KubeSpan. This provides better compatibility with popular CNIs like Calico and Cilium. Old behavior can be restored by setting `.machine.kubespan.advertiseKubernetesNetworks = true` in the machine config. ### MachineConfig `.cluster.allowSchedulingOnMasters` deprecated The `.cluster.allowSchedulingOnMasters` is deprecated and replaced by `.cluster.allowSchedulingOnControlPlanes`. The `.cluster.allowSchedulingOnMasters` will be removed in a future release of Talos. If both `.cluster.allowSchedulingOnMasters` and `.cluster.allowSchedulingOnControlPlanes` are set to `true`, the `.cluster.allowSchedulingOnControlPlanes` will be used. ### `k8s.gcr.io` mirror configuration Talos now defaults to adding a registry mirror configuration in the machineconfig for `k8s.gcr.io` pointing to both `registry.k8s.io` and `k8s.gcr.io` unless overridden. This is in line with the Kubernetes 1.25 release having the new `registry.k8s.io` registry endpoint. This is only enabled by default on newly generated configurations and not on upgrades. This can be enabled with a machine configuration as follows: ```yaml machine: registries: mirrors: k8s.gcr.io: endpoints: - https://registry.k8s.io - https://k8s.gcr.io ``` ### Network bridge support Talos now supports configuring Linux bridges. It can be configured in the machine config like the following: ```yaml machine: network: interfaces: - interface: br0 bridge: stp: enabled: true interfaces: - eth0 - eth1 ``` See [documentation](https://www.talos.dev/v1.2/reference/configuration/#bridge) for more details. ### VLAN support in cmdline arguments Talos now supports dracut-style `vlan` kernel argument to allow installing Talos Linux in networks where ports are not tagged with a default VLAN: ``` vlan=eth1.5:eth1 ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1.5::::: ``` ### Packet Capture Talos now supports capturing packets on a network interface with `talosctl pcap` command: talosctl pcap --interface eth0 ### Seccomp Profiles Talos now supports creating custom seccomp profiles on the host machine which in turn can be used by Kubernetes workloads. It can be configured in the machine config as below: ```yaml machine: seccompProfiles: - name: audit.json value: defaultAction: SCMP_ACT_LOG - name: deny.json value: {"defaultAction":"SCMP_ACT_LOG"} ``` This profile data can be either configured as a YAML definition or as a JSON string. The profiles are created on the host under `/var/lib/seccomp/profiles` and bind mounted at `/var/lib/kubelet/seccomp/profiles` so Kubelet can use it. See [documentation](https://www.talos.dev/v1.2/kubernetes-guides/configuration/seccomp-profiles/) for more details. ### Stable Default Hostname Talos now generates the default hostname (when there is no explicitly specified hostname) for the nodes based on the node id (e.g. `talos-2gd-76y`) instead of using the DHCP assigned IP address (e.g. `talos-172-20-0-2`). This ensures that the node hostname is not changed when DHCP assigns a new IP to a node. ### Strategic merge machine configuration patching In addition to JSON (RFC6902) patches Talos now supports [strategic merge patching](https://www.talos.dev/v1.2/talos-guides/configuration/patching/). For example, machine hostname can be set with the following patch: ```yaml machine: network: hostname: worker1 ``` Patch format is detected automatically. ### Variable substitution for URL query parameter in the talos.config kernel parameter The kernel parameter talos.config can now substitute system information into placeholders inside its URL query values. This example shows all supported variables: ```http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}``` ### talosctl `--masters` flag on `talosctl cluster create` is deprecated. Use `--controlplanes` instead. ### Component Updates * Linux: 5.15.59 * Flannel 0.19.1 * containerd 1.16.7 * Kubernetes: v1.25.0-beta.0 Talos is built with Go 1.19. ### Contributors * Andrey Smirnov * Noel Georgi * Utku Ozdemir * Dmitriy Matrenichev * Philipp Sauter * Tim Jones * Artem Chernyshev * Spencer Smith * Davincible * Eirik Askheim * AMet * Alex Wied * Bermi Ferrer * Christoph Schmatzler * Dennis Marttinen * Eng Zer Jun * Flightkick * Florian Klink * Gwyn * Han Cen * Larry Rosenman * Markus Reiter * Matthew Richardson * Nico Berlee * Rio Kierkels * RyanSquared * Serge Logvinov * Seán C McCord * Steve Francis * Tommy Botten Jensen * hobyte * nett_hier * zebernst ### Changes
164 commits

* [`5dd1b4002`](https://github.com/siderolabs/talos/commit/5dd1b400205d794b41de5ffc166d5bfe1605533a) feat: disable Kubernetes discovery backend by default * [`b62b18a97`](https://github.com/siderolabs/talos/commit/b62b18a9722f3b48a600dd5abd25f5f98af76b31) feat: bump k8s to v1.25.0-beta.0 * [`7b80a747b`](https://github.com/siderolabs/talos/commit/7b80a747bcd140918c7be1acc9db74fa9a8952f2) feat: add protobuf encoding/decoding for Go structs * [`00c3ee3ac`](https://github.com/siderolabs/talos/commit/00c3ee3ac3119249fd92d558283404018de2f1bf) docs: remove obsolete references to init nodes * [`6eefa9d9c`](https://github.com/siderolabs/talos/commit/6eefa9d9cb439b073f84ac18f53a889b55a4b4df) fix: properly filter resources in maintenance server * [`fa5aad01a`](https://github.com/siderolabs/talos/commit/fa5aad01a0d9144ab9d20a168a50c747f5a9258c) docs: fix issues in GCP docs * [`98f056603`](https://github.com/siderolabs/talos/commit/98f056603e876ad41fc66af9a586cb921a91583a) chore: bump dependencies * [`84e712a9f`](https://github.com/siderolabs/talos/commit/84e712a9f1626b78638a4fe2524a2b860cb7185f) feat: introduce Talos API access from Kubernetes * [`d7be30892`](https://github.com/siderolabs/talos/commit/d7be3089211691e5c5f62c83915d789eeeca31b8) chore: bump kernel to 5.15.59 * [`c2c2d65bc`](https://github.com/siderolabs/talos/commit/c2c2d65bc98b3ea58df7253df76f3266d7562e48) refactor: use COSI access filter for resource access * [`1dee0579e`](https://github.com/siderolabs/talos/commit/1dee0579e9ef6886b2ca8e42b33fce533dc10008) feat: add support for proxying one-to-one to `apid` * [`86eb01cd6`](https://github.com/siderolabs/talos/commit/86eb01cd6c905f87532a3d0f697b87e8866fd399) docs: add missing dev tools * [`4fd676c04`](https://github.com/siderolabs/talos/commit/4fd676c0468c67799b5d5a1b449551d6243999a5) docs: fix typo in theila name * [`856beb21c`](https://github.com/siderolabs/talos/commit/856beb21cc3505c10d17e030d2ce0487e019c7ab) feat: containerd 1.6.7, Flannel 1.19.1 * [`e97b9f6d3`](https://github.com/siderolabs/talos/commit/e97b9f6d3e14757288d7b2f415b69aef222e8e99) feat: support dhcp options for vlan * [`92314e47b`](https://github.com/siderolabs/talos/commit/92314e47bf66319a67d5c302d4f40a4772cb44b7) refactor: use controllers/resources to feed trustd with data * [`80d298abf`](https://github.com/siderolabs/talos/commit/80d298abfbf35e411f5dfbebf4c4d5334e36fbe3) feat: support skipping node registration * [`7795de313`](https://github.com/siderolabs/talos/commit/7795de313a11918bccda261c184363a8ed750cbb) fix: use controllers/resources for etcd configuration * [`f9b664c94`](https://github.com/siderolabs/talos/commit/f9b664c9470be14f840d33c7d1ebf43fa84d1127) fix: reload trusted CA list when client is recreated * [`8847ccd03`](https://github.com/siderolabs/talos/commit/8847ccd03106d525b40b0f3719d9632b00db9753) fix: shutdown some streaming API calls when machined API is shuting down * [`f95b53726`](https://github.com/siderolabs/talos/commit/f95b5372625a1ad83f61ea86e54ac6a74f2fd09a) fix: allow files in extension spec * [`1a8f6ec8e`](https://github.com/siderolabs/talos/commit/1a8f6ec8e1b47ee63dcc85b9f70b43871a7a5bd4) fix: don't advertise Kubernetes pod networks over KubeSpan by default * [`e3d4a0e4d`](https://github.com/siderolabs/talos/commit/e3d4a0e4d12fc87675178bc34e3a96817c0ad73b) fix: make reset work even if the node is not bootstrapped/not joined * [`a6b010a8b`](https://github.com/siderolabs/talos/commit/a6b010a8b45981a41feb03876828e29e3510a8e9) chore: update Go to 1.19, Linux to 5.15.58 * [`fb058a7c9`](https://github.com/siderolabs/talos/commit/fb058a7c9236a80c333c21bbd2bbda785f33ced6) test: use `T.TempDir` to create temporary test directory * [`6fc38bae6`](https://github.com/siderolabs/talos/commit/6fc38bae690906d628e89f9cb19c17e16500f27f) fix: iterate over etcd members endpoints for member promotion * [`c70b692fb`](https://github.com/siderolabs/talos/commit/c70b692fb34becf651042019c675b1e0750ec703) fix: update default address if removed from the host * [`cf620d473`](https://github.com/siderolabs/talos/commit/cf620d4733d4bd4c243213676caf7c5fe6a20d9d) feat: read talosconfig from secrets directory * [`1ad8e6122`](https://github.com/siderolabs/talos/commit/1ad8e6122c5a9bed71ab74bcc66a60d1c26556c1) fix: keep entire vlan id when parsing cmdline * [`fe2ee3b10`](https://github.com/siderolabs/talos/commit/fe2ee3b100d37297f3d040d92a07b12b152b8f5f) feat: implement MachineStatus resource * [`670d274c4`](https://github.com/siderolabs/talos/commit/670d274c458f8cc3a72bcef2bd3e1b22f053c4e5) chore: bump dependencies * [`08d2612e0`](https://github.com/siderolabs/talos/commit/08d2612e07a348c7cfd003d094812ba4f506d9d4) docs: bond devices are comma separated * [`c3c3e14db`](https://github.com/siderolabs/talos/commit/c3c3e14db5c916c24bf4f64acfa8735ebf6b6540) chore: add gotagsrewrite tool and use it to add tags to resources * [`2e790526f`](https://github.com/siderolabs/talos/commit/2e790526f760c890ad892fffd165ac27ab0dd9b4) refactor: make apid stop gracefully and be stopped late * [`0cdf22243`](https://github.com/siderolabs/talos/commit/0cdf22243169a9e5c6e0ecb351a8b841584d99b8) fix: retry Conflict errors when upgrading k8s manifests * [`1db097f50`](https://github.com/siderolabs/talos/commit/1db097f509031f86b4d2c72b590c9c657cd09b77) release(v1.2.0-alpha.1): prepare release * [`5ac4947b6`](https://github.com/siderolabs/talos/commit/5ac4947b634865104003ac91590d81cae4dd5e53) feat: enable default seccomp profile for kubelet * [`e5994ff7a`](https://github.com/siderolabs/talos/commit/e5994ff7a776e9f43f82340ef9916160d2a24149) fix: skip `ResetDuringBoot` test if the `Cluster` config is unknown * [`8028e1074`](https://github.com/siderolabs/talos/commit/8028e10749bfc53940651a1cb3eb0dcd56c1507c) fix: wait for boot done when rebooting a node in the integration tests * [`ae1bec59e`](https://github.com/siderolabs/talos/commit/ae1bec59e9ce1a9b5f3a2f6a040a16bb26d991a7) feat: allow running only one sequence at a time * [`ec05aee04`](https://github.com/siderolabs/talos/commit/ec05aee040371e7261911bdebb03004140ad8eed) fix: correctly unwrap errors when streaming * [`7c7f2d8c3`](https://github.com/siderolabs/talos/commit/7c7f2d8c3be3a49f9409b67a98984d5a3d6e6404) feat: refactor disk size matcher to be compatible with DeepEqual * [`3addea83b`](https://github.com/siderolabs/talos/commit/3addea83b9ba73418bd1cfd6e083fd4252b41356) feat: introduce support for Talos API access from Kubernetes * [`34d3a4164`](https://github.com/siderolabs/talos/commit/34d3a41643162eaedca9210c0edbc813ca77c3bb) docs: add missing <> to relref * [`c4d2d20c4`](https://github.com/siderolabs/talos/commit/c4d2d20c419fbbac7888b4b033ef939ff476e3a4) fix: enable stable hostnames for worker configs as well * [`0326bac1f`](https://github.com/siderolabs/talos/commit/0326bac1f92c6aa2fc929b768027a71c7285d800) chore: bump kernel to 5.15.57 * [`86820c33f`](https://github.com/siderolabs/talos/commit/86820c33f1055ce0efbfe2934e84c5627919ed07) chore: bump dependencies * [`6e7dfeeb3`](https://github.com/siderolabs/talos/commit/6e7dfeeb38fe5cf0065faa49ca36c3a292e86fae) fix: data race in packet capture (part 2) * [`c11e1dae7`](https://github.com/siderolabs/talos/commit/c11e1dae7033e5a530eb7185eabf5c89deacaace) docs: fix spelling and grammar errors * [`30f7851d2`](https://github.com/siderolabs/talos/commit/30f7851d2a25ed0f9d7cf28548c3a1f09cd664cd) chore: bump golangci-lint from 1.45.2 to 1.47.2 * [`2cce9112d`](https://github.com/siderolabs/talos/commit/2cce9112d17384e491ab91b75494241de664ec18) chore: bump goimports from 0.1.10 to 0.1.11 * [`18756c7ff`](https://github.com/siderolabs/talos/commit/18756c7ff6a9e81615aec1b1ecb3808f500fdaf1) fix: folder permissions of overlay mounted folders * [`47c35dc47`](https://github.com/siderolabs/talos/commit/47c35dc4740cad3f758969b5d93885c9782b439a) feat: set stable default hostname based on machine-id * [`1ed3df295`](https://github.com/siderolabs/talos/commit/1ed3df295c1a26ed8243c58d6bfecb8c7398daec) chore: support glibc apps extension spec * [`a2aea9726`](https://github.com/siderolabs/talos/commit/a2aea97263c787de81f911e085cf81f56dfd0d82) fix: write etcd PKI files in a controller * [`bb4abc096`](https://github.com/siderolabs/talos/commit/bb4abc0961dba4c2e158cfebdd7b3d8c010a30b3) fix: regenerate kubelet certs when hostname changes * [`d650afb6c`](https://github.com/siderolabs/talos/commit/d650afb6cdd405292515be266f3ee05f19f014ec) chore: fix typo in `powercycle` * [`644e803ad`](https://github.com/siderolabs/talos/commit/644e803adf35eaa735af5487ffdcfb7471d17f3e) fix: use masks and different firewall mark for KubeSpan * [`80444a43d`](https://github.com/siderolabs/talos/commit/80444a43d9382f44c515224a02610443c77b0fe9) fix: remove data race in pcap capture * [`04a45dff2`](https://github.com/siderolabs/talos/commit/04a45dff2831b87b2373664e87794dbf5ecabd08) docs: remove katacoda links * [`065b59276`](https://github.com/siderolabs/talos/commit/065b59276c9ac48f2e5fa051c132efc5bfb4b849) feat: implement packet capture API * [`7c006cabc`](https://github.com/siderolabs/talos/commit/7c006cabc7ee15146a8db4358156c049d4525cfe) feat: update Kubernetes to 1.24.3 * [`551290195`](https://github.com/siderolabs/talos/commit/551290195c868c1f23ea0307ef8058537da73064) chore: bump dependencies * [`1677bcc4b`](https://github.com/siderolabs/talos/commit/1677bcc4b243886c75f7acc95fe3225032aeee7e) fix: skip bond itself when matching interface (Equinix Metal) * [`f1c2b5c55`](https://github.com/siderolabs/talos/commit/f1c2b5c558f96ad45261f0f4f50ecbd50475543e) feat: implement strategic merge patching for API server admission config * [`be98cb82b`](https://github.com/siderolabs/talos/commit/be98cb82b5d56e26210e0be0d5d54338df0bb092) feat: follow KEP-2568 non-root enhancements * [`87ea1d961`](https://github.com/siderolabs/talos/commit/87ea1d9611332f4552bcf35a2fc80e43fbef89ed) fix: update kubelet kubeconfig when cluster control plane endpoint changes * [`a75fe7600`](https://github.com/siderolabs/talos/commit/a75fe7600d554c7d8404a32e9a790c27dfdebb44) feat: gen secrets from kubernetes pki dir * [`a1d7b535a`](https://github.com/siderolabs/talos/commit/a1d7b535ad59ec900f8e907bcd8085cece77c1e4) docs: add kubeadm migration guide * [`9e0c56581`](https://github.com/siderolabs/talos/commit/9e0c56581e8ab144324a148dc7489da595b0edcb) docs: guide for setting up synology-csi driver * [`f0b8eea5e`](https://github.com/siderolabs/talos/commit/f0b8eea5e5b30ca6864eda6872d5e23f41ffdf7d) refactor: remove bootstrap sequence * [`89c7da899`](https://github.com/siderolabs/talos/commit/89c7da8991eb1760f220ce7bf7bc7fec7dd4a089) docs: add documentation for vagrant & libvirt * [`014b85fdc`](https://github.com/siderolabs/talos/commit/014b85fdcb6575b3db19d6cc7c848c02957f5913) docs: improve talos kubernetes upgrade note * [`88bb017ed`](https://github.com/siderolabs/talos/commit/88bb017ed0a57139380dcf07db4c9585d96a4b7e) docs: remove old docs from site * [`c92c90655`](https://github.com/siderolabs/talos/commit/c92c90655ace0a0465599483004793c68611af5b) feat: build talosctl for FreeBSD * [`616da3069`](https://github.com/siderolabs/talos/commit/616da30695c0a0f8ffd9eb5fed99e2d4aeaf159f) docs: update last release for 1.1 * [`091e6ef0e`](https://github.com/siderolabs/talos/commit/091e6ef0eb4d5b5fa1245968abb25ebaafcd2a96) feat: resubstitute talos.config url variables on retry * [`ec74ab38a`](https://github.com/siderolabs/talos/commit/ec74ab38aa95c222a26048071cd9911024fe3ae2) feat: update Go to 1.18.4, Linux to 5.15.54 * [`641f6a1e4`](https://github.com/siderolabs/talos/commit/641f6a1e4e106414f9e7667792a84586d49171b9) feat: expose strategic merge config patches * [`6e3d2d647`](https://github.com/siderolabs/talos/commit/6e3d2d647d031ff9f11f595a607ae5227cb1035c) docs: fix disk encryption params * [`c43d6a31d`](https://github.com/siderolabs/talos/commit/c43d6a31d92db98dd44b2f533d1d6dcd6d8a8c48) docs: fix typos * [`551887528`](https://github.com/siderolabs/talos/commit/551887528cf3a29e60c540dc02355a4937cc5b25) chore: bump dependencies * [`626ef05e6`](https://github.com/siderolabs/talos/commit/626ef05e6063df3010aee805f6ac442e3298e568) fix: correct SANs for etcd certs * [`83ce92c5f`](https://github.com/siderolabs/talos/commit/83ce92c5ff8f8c55ab8e2dddcc45a253b7b9191b) docs: fix theila docs * [`8a038d40e`](https://github.com/siderolabs/talos/commit/8a038d40ee9071cf77c559a813ecaa681f730a66) fix: stabilize etcd join and promote sequences * [`136122556`](https://github.com/siderolabs/talos/commit/136122556c596ebf579be3aa00e767d05b0e4bb5) fix: use correct etcd cert path * [`c170ec0b0`](https://github.com/siderolabs/talos/commit/c170ec0b09e20d2277862057a6f5cefbc963d276) chore: bump kernel to 5.15.53 * [`d924901b7`](https://github.com/siderolabs/talos/commit/d924901b79d91c3ce5292b7d95487485a7ce0abc) feat: add cli subcommand to generate secrets * [`34aabedd8`](https://github.com/siderolabs/talos/commit/34aabedd805a6cb804c9db5e54d80b58c00da0de) feat: more circular pkg from internal to pkg * [`4f044e466`](https://github.com/siderolabs/talos/commit/4f044e46643a275a987b61fa4da60f700ccde774) feat: implement strategic merge machine config patching * [`c2a512608`](https://github.com/siderolabs/talos/commit/c2a51260881e95e18567962e437c1081ae59968c) fix: avoid double append of `talos.platform` kernel argument * [`27dfe7c03`](https://github.com/siderolabs/talos/commit/27dfe7c0352b62fee9895f4ae172467499072af5) fix: perform accurate conflict resolution on overal (kubespan) * [`e437445b4`](https://github.com/siderolabs/talos/commit/e437445b4044f58bcad35b171a7f259de6f6ac6d) chore: bump kernel to 5.15.52 * [`d27a6a4ac`](https://github.com/siderolabs/talos/commit/d27a6a4ac0e058e5e526ee51be512c5d01ea7a19) feat: add vlan support to cmdline * [`fdca5d8a9`](https://github.com/siderolabs/talos/commit/fdca5d8a95a04d865a0417624628740480c18b5f) chore: bump dependencies * [`ae3840dbc`](https://github.com/siderolabs/talos/commit/ae3840dbc34f32faf8da426378a8a32f1c009659) refactor: move kubeconfig package under public api * [`184e113f3`](https://github.com/siderolabs/talos/commit/184e113f35f4a3cd2f036502862af325ee6e3d2f) chore: disable systeminfo controller in container * [`86a0a7bdf`](https://github.com/siderolabs/talos/commit/86a0a7bdf70d318bed2143d65784faae6f9125d4) refactor: use pointer types more in machine config structs * [`3a1eb10e6`](https://github.com/siderolabs/talos/commit/3a1eb10e61edeef2af497c6ad9101d6cec539a34) docs: update the Proxmox `kvm64` note * [`30e220fcd`](https://github.com/siderolabs/talos/commit/30e220fcd265337790ccc9a8070fd7b509336fe0) docs: kernel cmdline params updated on upgrades * [`915de9cf9`](https://github.com/siderolabs/talos/commit/915de9cf9bfd33d95b766f8ed5ce0ebb863f60f6) docs: fix bridge documentation * [`52cd12951`](https://github.com/siderolabs/talos/commit/52cd12951c567d76c9dfa3ca11ba53d16cdbc5d3) test: bump Talos versions in upgrade tests * [`022581d80`](https://github.com/siderolabs/talos/commit/022581d8092840f4c1d9aa4b198650db4f3ba78b) release(v1.2.0-alpha.0): prepare release * [`643e81cfe`](https://github.com/siderolabs/talos/commit/643e81cfed675a018ec3af20b74fdcfcdc665d60) feat: add SenseLabs to ADOPTERS.md * [`bdfee2b3b`](https://github.com/siderolabs/talos/commit/bdfee2b3b7bf773326bd839ea6049e0262382071) chore: bump kernel to 5.15.51 * [`36c44a651`](https://github.com/siderolabs/talos/commit/36c44a65110713274fbb4b6638a36f3377c96bb3) fix: provide CA certificates in `/etc/ssl/certs/ca-certificates.crt` * [`7ebd9bcce`](https://github.com/siderolabs/talos/commit/7ebd9bcce6f2b1e6a030e551f41f590996294573) docs: fix pod security talos resource name * [`57b625e0a`](https://github.com/siderolabs/talos/commit/57b625e0a68534fdb3847e1fff5c18906630d1da) refactor: avoid recreating grpc clients in service health checks * [`a68a00f1b`](https://github.com/siderolabs/talos/commit/a68a00f1b9bdbb519966bbf1ac7f463796a85abb) docs: recommend setting "host" Processor Type on proxmox * [`923600a73`](https://github.com/siderolabs/talos/commit/923600a73c7368fd9217680fdfcc0dc2e9dc4b8e) chore: bump kernel to 5.15.50 * [`758a9bf59`](https://github.com/siderolabs/talos/commit/758a9bf59fad2f49ca03937c7d1bcfb3c13a9a0b) docs: add theila ui * [`b81016e62`](https://github.com/siderolabs/talos/commit/b81016e628642d93fb0b123f7146558279e0c648) chore: update blockdevice library to v0.3.3 * [`284a2f959`](https://github.com/siderolabs/talos/commit/284a2f9596ce899236d78d43c6d42a287f60540a) fix: filter static pods correctly and optimize fetching * [`61abf3111`](https://github.com/siderolabs/talos/commit/61abf311109f2d604d227bb6d290ccaeca19b3f0) docs: change command for cluster create to keep $HOME with sudo * [`6ae1e9bf2`](https://github.com/siderolabs/talos/commit/6ae1e9bf2ba7778dbe8a0919cbdf81fbe74b8e8c) chore: bump dependencies * [`2deff6b6e`](https://github.com/siderolabs/talos/commit/2deff6b6e148d99e9c88159f4895594417cdf080) feat: add support for variable substitution in talos.config kernel parameter * [`103c94225`](https://github.com/siderolabs/talos/commit/103c942256e7832b18e973f3fd698d7e94818c6f) fix: update crypto library with support for RSA-SHA* * [`448de7194`](https://github.com/siderolabs/talos/commit/448de7194911b3f8bd79cec3a3e93515ffd2e0a9) docs: add UpCloud installation guide * [`07014e0a8`](https://github.com/siderolabs/talos/commit/07014e0a8ee291ab4f2848787fc7462676c11fec) fix: generate correct bootstrap manifests when only IPv6 CIDR is used * [`465edbb47`](https://github.com/siderolabs/talos/commit/465edbb4791315d8709daeeba19f14b3e53680f3) fix: look for qemu-kvm binary * [`63caa281a`](https://github.com/siderolabs/talos/commit/63caa281ae8b83add1b070014282a6f792843845) fix: create native image format for DigitalOcean * [`f15ce549e`](https://github.com/siderolabs/talos/commit/f15ce549e940e6a0a95b8f78a4d7ad967f0a3900) fix: siderlink api assume port 443 with https schema * [`797596229`](https://github.com/siderolabs/talos/commit/797596229a7c4a883810c4229492cdfd0b441f19) feat: add support for configuring network bridges * [`2b23fabcc`](https://github.com/siderolabs/talos/commit/2b23fabcc1c3f5f495ea4d7fa6597fa639d4ce82) docs: use SVG image for K8s conformance * [`d4606c33e`](https://github.com/siderolabs/talos/commit/d4606c33ec36563d29b5ac95f11d1479c61a1905) chore: bump kernel to 5.15.49 * [`cfb640222`](https://github.com/siderolabs/talos/commit/cfb640222b80e1a2a6c3a8a505c5f6acfb148d24) docs: update docs for release 1.1 * [`b816d0b60`](https://github.com/siderolabs/talos/commit/b816d0b60077e83028b950a544c810d0875be268) docs: fix the vendor information for Kubernetes conformance tests * [`a167a5402`](https://github.com/siderolabs/talos/commit/a167a54021c979a1ca761674d8e368d5fb7dda6a) test: fix CLI nodes discovery without provisioner data * [`916a30682`](https://github.com/siderolabs/talos/commit/916a306829190c8eccbb993cfc166aa3cf08042e) docs: add twitter meta info * [`80090a3ed`](https://github.com/siderolabs/talos/commit/80090a3eda00e9808b0ba15241ea36dc6835f6d1) test: fix health endpoint cli test when discovery is disabled * [`3c263bb44`](https://github.com/siderolabs/talos/commit/3c263bb44639edf456d1c6203f41c71fa4d6d1d0) chore: bump dependencies * [`e8113527f`](https://github.com/siderolabs/talos/commit/e8113527f94f0fbc6cf6fdb9390dfb09d984213d) chore: bump kubernetes to v1.24.2 * [`068f1b6d0`](https://github.com/siderolabs/talos/commit/068f1b6d0517f62d2a76c7b1a761f15104220644) feat: add ctest package and base for test suite * [`2aad3a1e4`](https://github.com/siderolabs/talos/commit/2aad3a1e4911ebcd3eb970f09baa74e10383a959) chore: bump kernel to 5.15.48 * [`a31a858e0`](https://github.com/siderolabs/talos/commit/a31a858e08a7e022dc26c729ef097b6ed56a83ad) docs: snippets for logging api server audit logs * [`89aaaef9f`](https://github.com/siderolabs/talos/commit/89aaaef9f5dd403919535fc3e81ef635d233c0da) chore: bump kernel to 5.15.47 * [`6759fcd4a`](https://github.com/siderolabs/talos/commit/6759fcd4aeeca74e78e346b4265e86580991d800) feat: use discovery service on cluster health checks * [`f54d90787`](https://github.com/siderolabs/talos/commit/f54d9078719a62bcefcab367957f166e7a43decc) fix: enable orderly poweroff in hyper-v on Azure * [`35475ce45`](https://github.com/siderolabs/talos/commit/35475ce45b1ad64bb34149be9960f5acdd2bfe86) docs: openebs jiva example with iscsi-tools extension * [`8d2be5e31`](https://github.com/siderolabs/talos/commit/8d2be5e315fb05002587570d759322c9c00ad525) feat: extend node definition used in health checks * [`7a11b4def`](https://github.com/siderolabs/talos/commit/7a11b4def78e5b4506611fe85d083a12b695bd05) fix: make `talosctl bootstrap` accept only single node * [`217fba288`](https://github.com/siderolabs/talos/commit/217fba288f07ccf7053e804c226a2e0b9301f864) test: fix csi tests * [`90bf34fed`](https://github.com/siderolabs/talos/commit/90bf34fed98cb9ff524097da4043d4ff221a0b20) docs: fork docs for Talos 1.2 * [`a0dd010a8`](https://github.com/siderolabs/talos/commit/a0dd010a87b0ef0350299db3944f3a941fca09b4) docs: add link to discovery service in kubespan * [`c0371410e`](https://github.com/siderolabs/talos/commit/c0371410ee93f9773938b5b73be6eba246fd8f47) fix: support SideroLink "secure" gRPC connection * [`b03709620`](https://github.com/siderolabs/talos/commit/b03709620201b44f6464a7df804e2003c9751a30) feat: build Talos images with system extensions included * [`43def7490`](https://github.com/siderolabs/talos/commit/43def7490ffa598ba973f35903eaea462db374b1) chore: bump kernel and runc * [`4dbbf4ac5`](https://github.com/siderolabs/talos/commit/4dbbf4ac50f6b1ccd62efb1c06c8a92d8f91e65c) chore: add generic methods and use them part #2 * [`7114292b6`](https://github.com/siderolabs/talos/commit/7114292b6cd5f93a51b905db6377ffdadf429f19) docs: fix latest release version in docs * [`da2985fe1`](https://github.com/siderolabs/talos/commit/da2985fe1b29abac46b761a5ec2f4557d12ce985) fix: respect local API server port * [`e03266667`](https://github.com/siderolabs/talos/commit/e03266667f11d751f16a7208e774996ebadf8842) fix: correctly validate reboot mode in CLI * [`70fc42409`](https://github.com/siderolabs/talos/commit/70fc42409980a1a78b98a962284460ea18c42513) chore: add generic methods and use them * [`3ae8bdd92`](https://github.com/siderolabs/talos/commit/3ae8bdd92e43c8a5fedd455d4479678ccb263a6b) chore: run `xfs_repair` on xfs filesystem returing `EUCLEAN` * [`0c91c89f4`](https://github.com/siderolabs/talos/commit/0c91c89f4f0732147f5b6c41fb4f3da8437ae9f1) chore: revert day-two tests for csi tests * [`f71b58312`](https://github.com/siderolabs/talos/commit/f71b58312251ec2924607fb5166afa6c8aaf01bb) feat: disallow anonymous requests by default (kube-apiserver) * [`c19dd1b89`](https://github.com/siderolabs/talos/commit/c19dd1b8925fc8ec25a721d336ad0b363fc27fd4) feat: add 'etcd members should be control plane nodes' health check * [`f2997c0f2`](https://github.com/siderolabs/talos/commit/f2997c0f22b93382bfb61ff556961de56445807f) chore: bump dependencies * [`f3efec4b5`](https://github.com/siderolabs/talos/commit/f3efec4b56bc72dc5c769a76f6254d14d3f20b1b) feat: update containerd 1.6.6, Linux 5.15.45, Flannel 0.18.1 * [`27f8e50ce`](https://github.com/siderolabs/talos/commit/27f8e50ce90c47f5ddc82645e0ebcdb1a8ed778b) fix: add ovmf image path for rhel * [`87e7de30c`](https://github.com/siderolabs/talos/commit/87e7de30cb6ed02991cb46e25d20343555cc6317) docs: fix required ports * [`c126f2ee8`](https://github.com/siderolabs/talos/commit/c126f2ee85572bdfde61f9a3ba878f0595c74cfe) chore: bump golang to 1.18.3 * [`c1aed6240`](https://github.com/siderolabs/talos/commit/c1aed62405dddb2cbd2d47d699aae0c94df70886) fix: wait for `/var` to be mounted in kubelet service controller * [`d7a64f5d2`](https://github.com/siderolabs/talos/commit/d7a64f5d2a6ff9dccdf3bdb948684d9513912be9) fix: improve vip operator shutdown sequence * [`7b9dfcb85`](https://github.com/siderolabs/talos/commit/7b9dfcb852af6a48f00ddfca7337a571aa56a2b3) chore: add 'make go-mod-outdated'

### Changes since v1.2.0-alpha.1
35 commits

* [`5dd1b4002`](https://github.com/siderolabs/talos/commit/5dd1b400205d794b41de5ffc166d5bfe1605533a) feat: disable Kubernetes discovery backend by default * [`b62b18a97`](https://github.com/siderolabs/talos/commit/b62b18a9722f3b48a600dd5abd25f5f98af76b31) feat: bump k8s to v1.25.0-beta.0 * [`7b80a747b`](https://github.com/siderolabs/talos/commit/7b80a747bcd140918c7be1acc9db74fa9a8952f2) feat: add protobuf encoding/decoding for Go structs * [`00c3ee3ac`](https://github.com/siderolabs/talos/commit/00c3ee3ac3119249fd92d558283404018de2f1bf) docs: remove obsolete references to init nodes * [`6eefa9d9c`](https://github.com/siderolabs/talos/commit/6eefa9d9cb439b073f84ac18f53a889b55a4b4df) fix: properly filter resources in maintenance server * [`fa5aad01a`](https://github.com/siderolabs/talos/commit/fa5aad01a0d9144ab9d20a168a50c747f5a9258c) docs: fix issues in GCP docs * [`98f056603`](https://github.com/siderolabs/talos/commit/98f056603e876ad41fc66af9a586cb921a91583a) chore: bump dependencies * [`84e712a9f`](https://github.com/siderolabs/talos/commit/84e712a9f1626b78638a4fe2524a2b860cb7185f) feat: introduce Talos API access from Kubernetes * [`d7be30892`](https://github.com/siderolabs/talos/commit/d7be3089211691e5c5f62c83915d789eeeca31b8) chore: bump kernel to 5.15.59 * [`c2c2d65bc`](https://github.com/siderolabs/talos/commit/c2c2d65bc98b3ea58df7253df76f3266d7562e48) refactor: use COSI access filter for resource access * [`1dee0579e`](https://github.com/siderolabs/talos/commit/1dee0579e9ef6886b2ca8e42b33fce533dc10008) feat: add support for proxying one-to-one to `apid` * [`86eb01cd6`](https://github.com/siderolabs/talos/commit/86eb01cd6c905f87532a3d0f697b87e8866fd399) docs: add missing dev tools * [`4fd676c04`](https://github.com/siderolabs/talos/commit/4fd676c0468c67799b5d5a1b449551d6243999a5) docs: fix typo in theila name * [`856beb21c`](https://github.com/siderolabs/talos/commit/856beb21cc3505c10d17e030d2ce0487e019c7ab) feat: containerd 1.6.7, Flannel 1.19.1 * [`e97b9f6d3`](https://github.com/siderolabs/talos/commit/e97b9f6d3e14757288d7b2f415b69aef222e8e99) feat: support dhcp options for vlan * [`92314e47b`](https://github.com/siderolabs/talos/commit/92314e47bf66319a67d5c302d4f40a4772cb44b7) refactor: use controllers/resources to feed trustd with data * [`80d298abf`](https://github.com/siderolabs/talos/commit/80d298abfbf35e411f5dfbebf4c4d5334e36fbe3) feat: support skipping node registration * [`7795de313`](https://github.com/siderolabs/talos/commit/7795de313a11918bccda261c184363a8ed750cbb) fix: use controllers/resources for etcd configuration * [`f9b664c94`](https://github.com/siderolabs/talos/commit/f9b664c9470be14f840d33c7d1ebf43fa84d1127) fix: reload trusted CA list when client is recreated * [`8847ccd03`](https://github.com/siderolabs/talos/commit/8847ccd03106d525b40b0f3719d9632b00db9753) fix: shutdown some streaming API calls when machined API is shuting down * [`f95b53726`](https://github.com/siderolabs/talos/commit/f95b5372625a1ad83f61ea86e54ac6a74f2fd09a) fix: allow files in extension spec * [`1a8f6ec8e`](https://github.com/siderolabs/talos/commit/1a8f6ec8e1b47ee63dcc85b9f70b43871a7a5bd4) fix: don't advertise Kubernetes pod networks over KubeSpan by default * [`e3d4a0e4d`](https://github.com/siderolabs/talos/commit/e3d4a0e4d12fc87675178bc34e3a96817c0ad73b) fix: make reset work even if the node is not bootstrapped/not joined * [`a6b010a8b`](https://github.com/siderolabs/talos/commit/a6b010a8b45981a41feb03876828e29e3510a8e9) chore: update Go to 1.19, Linux to 5.15.58 * [`fb058a7c9`](https://github.com/siderolabs/talos/commit/fb058a7c9236a80c333c21bbd2bbda785f33ced6) test: use `T.TempDir` to create temporary test directory * [`6fc38bae6`](https://github.com/siderolabs/talos/commit/6fc38bae690906d628e89f9cb19c17e16500f27f) fix: iterate over etcd members endpoints for member promotion * [`c70b692fb`](https://github.com/siderolabs/talos/commit/c70b692fb34becf651042019c675b1e0750ec703) fix: update default address if removed from the host * [`cf620d473`](https://github.com/siderolabs/talos/commit/cf620d4733d4bd4c243213676caf7c5fe6a20d9d) feat: read talosconfig from secrets directory * [`1ad8e6122`](https://github.com/siderolabs/talos/commit/1ad8e6122c5a9bed71ab74bcc66a60d1c26556c1) fix: keep entire vlan id when parsing cmdline * [`fe2ee3b10`](https://github.com/siderolabs/talos/commit/fe2ee3b100d37297f3d040d92a07b12b152b8f5f) feat: implement MachineStatus resource * [`670d274c4`](https://github.com/siderolabs/talos/commit/670d274c458f8cc3a72bcef2bd3e1b22f053c4e5) chore: bump dependencies * [`08d2612e0`](https://github.com/siderolabs/talos/commit/08d2612e07a348c7cfd003d094812ba4f506d9d4) docs: bond devices are comma separated * [`c3c3e14db`](https://github.com/siderolabs/talos/commit/c3c3e14db5c916c24bf4f64acfa8735ebf6b6540) chore: add gotagsrewrite tool and use it to add tags to resources * [`2e790526f`](https://github.com/siderolabs/talos/commit/2e790526f760c890ad892fffd165ac27ab0dd9b4) refactor: make apid stop gracefully and be stopped late * [`0cdf22243`](https://github.com/siderolabs/talos/commit/0cdf22243169a9e5c6e0ecb351a8b841584d99b8) fix: retry Conflict errors when upgrading k8s manifests

### Changes from siderolabs/extras
3 commits

* [`da35a63`](https://github.com/siderolabs/extras/commit/da35a6309dff8cf34316d2c1503a9e44cafa4e1d) feat: update Go to 1.19 * [`17a319f`](https://github.com/siderolabs/extras/commit/17a319ffeecba7f20c2fa9f75ccc677b3964e754) chore: update Go to 1.18.4 * [`892407f`](https://github.com/siderolabs/extras/commit/892407fd7c1a032ec4d7de5d52595ef3bcc7b484) chore: bump golang to 1.18.3

### Changes from siderolabs/pkgs
28 commits

* [`7783ee3`](https://github.com/siderolabs/pkgs/commit/7783ee3df01fb153629e0745dcec7ebe37b5b597) chore: bump kernel to 5.15.59 * [`360d596`](https://github.com/siderolabs/pkgs/commit/360d5968b7841f425f9d3605f419877aa2829c6e) feat: update containerd to 1.6.7 * [`6feece4`](https://github.com/siderolabs/pkgs/commit/6feece4a08ec9bb7b23f0849d8aa3926c3242841) feat: update Go to 1.19 * [`9ad3aeb`](https://github.com/siderolabs/pkgs/commit/9ad3aeb88edb8087db83fdfcdb3c0b604906cf3f) chore: bump kernel to 5.15.58 * [`dcc0311`](https://github.com/siderolabs/pkgs/commit/dcc031138e336747daeee4b77d8813f4a8078abd) chore: bump kernel to 5.15.57 * [`b943a9d`](https://github.com/siderolabs/pkgs/commit/b943a9da08124042a56cf939c2cfc4c2591201d2) chore: update Go to 1.18.4 * [`a44e324`](https://github.com/siderolabs/pkgs/commit/a44e32412d667b26ce682288584d3f413ce888d4) chore: bump kernel to 5.15.54 * [`247f567`](https://github.com/siderolabs/pkgs/commit/247f567b8490bba7b02b54a42c33177521733701) chore: bump kernel to 5.15.53 * [`4fe9867`](https://github.com/siderolabs/pkgs/commit/4fe98672466f361fb7de4e1ddb3449b59e6a4193) chore: bump openssl to 1.1.1q * [`9ee662c`](https://github.com/siderolabs/pkgs/commit/9ee662c5d808c75e3373d0e1fb3dfbfe3cd9663c) chore: bump kernel to 5.15.52 * [`4412db8`](https://github.com/siderolabs/pkgs/commit/4412db88987acf91ffc2a83c09dd0dd5d84819eb) chore: bump kernel to 5.15.51 * [`6fedbdc`](https://github.com/siderolabs/pkgs/commit/6fedbdc826cffe5b0740c43f5641218df58e767c) chore: bump tools * [`f1f44e6`](https://github.com/siderolabs/pkgs/commit/f1f44e6a254e571c34f667e086e50afec099500b) chore: bump kernel to 5.15.50 * [`388af5e`](https://github.com/siderolabs/pkgs/commit/388af5e4eea16e0b19ce58879ea9d79676b9608b) chore: bump openssl to 1.1.1p * [`ed75c50`](https://github.com/siderolabs/pkgs/commit/ed75c5011e29107a59ffca1bececee1d22937ba2) chore: enable `RANDOM_TRUST_BOOTLOADER` by default * [`7c243f6`](https://github.com/siderolabs/pkgs/commit/7c243f6da5b3c0476106ff47d37b5d7a8ad28d98) chore: bump kernel to 5.15.49 * [`6e1269e`](https://github.com/siderolabs/pkgs/commit/6e1269e67f1e8a81cccf6ed45980595f2d6343f5) chore: bump kernel to 5.15.48 * [`5d671a3`](https://github.com/siderolabs/pkgs/commit/5d671a3cd6ebed495022f23e0073c1f971477305) chore: bump nvidia drivers to 515.48.07 * [`b35d835`](https://github.com/siderolabs/pkgs/commit/b35d835a1cfc3215d631f8ace3d3b1b7c83da008) chore: bump kernel to 5.15.47 * [`6604d6b`](https://github.com/siderolabs/pkgs/commit/6604d6b0686ea36983119edd7fb70755d3a812e0) feat: hyperv arm64 * [`c474058`](https://github.com/siderolabs/pkgs/commit/c4740588733138df9503c37304d1460166a3e233) chore: bump nvidia driver to 515.43.04 * [`5bc7e34`](https://github.com/siderolabs/pkgs/commit/5bc7e341fa0c035bbecc999bca3811b853684c5f) feat: update runc to 1.1.3, libseccomp to 2.5.4 * [`c02cd7a`](https://github.com/siderolabs/pkgs/commit/c02cd7a7086098698d1edd1d5ecb024ad9456a48) chore: bump kernel to 5.15.46 * [`b9c72a5`](https://github.com/siderolabs/pkgs/commit/b9c72a59cd6077ceb0ce53f11241d294c137f68b) feat: update containerd to 1.6.6 * [`f7786a3`](https://github.com/siderolabs/pkgs/commit/f7786a3a74bbf79c81cbcb031c357eae0e07726f) chore: bump kernel to 5.15.45 * [`b1c207d`](https://github.com/siderolabs/pkgs/commit/b1c207d63b1cac99b90025d530c57da4f51fc652) feat: update containerd to 1.6.5 * [`4d47830`](https://github.com/siderolabs/pkgs/commit/4d47830f86bfda0ae8cc9c89a6ca8ae3a73772cd) chore: bump golang to 1.18.3 * [`dc21e30`](https://github.com/siderolabs/pkgs/commit/dc21e30a2f31effab56b6e32c785fd0644eb90d2) chore: bump kernel to 5.15.44

### Changes from siderolabs/tools
7 commits

* [`cd35510`](https://github.com/siderolabs/tools/commit/cd355105d5c3acb2f8bf96d79693ed4b48447c73) feat: update Go to 1.19 * [`e83198d`](https://github.com/siderolabs/tools/commit/e83198d7045f376f517274c298a3b0830637d8b7) chore: bump git to v2.37.1 * [`0d669dd`](https://github.com/siderolabs/tools/commit/0d669dd415a044e5279f36c468834848ed6447bf) feat: update Go 1.18.4 * [`26b32d5`](https://github.com/siderolabs/tools/commit/26b32d582f13a9ea3ab55558bb8b8c2500008da0) chore: bump openssl to 1.1.1q * [`d8015e7`](https://github.com/siderolabs/tools/commit/d8015e756d74def09cee0503da08186eeccecb9a) chore: bump curl to 7.84.0 * [`3ec03ed`](https://github.com/siderolabs/tools/commit/3ec03edef31e971f48cb3202667af2045bcc233f) chore: bump openssl to 1.1.1p * [`3df9e13`](https://github.com/siderolabs/tools/commit/3df9e13ab89600655f5371adf254d66dda36ef02) chore: bump golang to 1.18.3

### Changes from talos-systems/crypto
1 commit

* [`e9df1b8`](https://github.com/talos-systems/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs

### Changes from talos-systems/go-blockdevice
2 commits

* [`74ea471`](https://github.com/talos-systems/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs * [`9fa801c`](https://github.com/talos-systems/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk

### Changes from talos-systems/grpc-proxy
1 commit

* [`6dfa2cc`](https://github.com/talos-systems/grpc-proxy/commit/6dfa2cc80b6195844cae2dc2b2bc0b9b62246d8d) fix: ignore errors on duplicate `SetHeader` calls

### Dependency Changes * **cloud.google.com/go/compute** v1.6.1 -> v1.7.0 * **github.com/BurntSushi/toml** v1.1.0 -> v1.2.0 * **github.com/aws/aws-sdk-go** v1.44.24 -> v1.44.71 * **github.com/containerd/containerd** v1.6.4 -> v1.6.8 * **github.com/containernetworking/cni** v1.1.0 -> v1.1.2 * **github.com/cosi-project/runtime** 95d06feaf8b5 -> cd5f564066ad * **github.com/docker/docker** v20.10.16 -> v20.10.17 * **github.com/emicklei/dot** v0.16.0 -> v1.0.0 * **github.com/google/gopacket** v1.1.19 **_new_** * **github.com/google/nftables** a9775fb167d2 -> 2eca00135732 * **github.com/hashicorp/go-getter** v1.6.1 -> v1.6.2 * **github.com/hashicorp/go-version** v1.5.0 -> v1.6.0 * **github.com/hetznercloud/hcloud-go** v1.33.2 -> v1.35.2 * **github.com/jsimonetti/rtnetlink** v1.2.0 -> v1.2.1 * **github.com/martinlindhe/base36** v1.1.1 **_new_** * **github.com/packethost/packngo** v0.24.0 -> v0.25.0 * **github.com/prometheus/procfs** v0.7.3 -> v0.8.0 * **github.com/rivo/tview** 9994674d60a8 -> 37ad0bb93703 * **github.com/siderolabs/extras** v1.1.0-1-g5800284 -> v1.2.0-alpha.0-2-gda35a63 * **github.com/siderolabs/pkgs** v1.1.0-8-gfa9a488 -> v1.2.0-alpha.0-27-g7783ee3 * **github.com/siderolabs/tools** v1.1.0-1-g134974c -> v1.2.0-alpha.0-6-gcd35510 * **github.com/spf13/cobra** v1.4.0 -> v1.5.0 * **github.com/stretchr/testify** v1.7.1 -> v1.8.0 * **github.com/talos-systems/crypto** v0.3.5 -> e9df1b8ca74c * **github.com/talos-systems/go-blockdevice** v0.3.2 -> v0.3.4 * **github.com/talos-systems/grpc-proxy** v0.3.0 -> v0.3.1 * **github.com/u-root/u-root** v0.8.0 -> v0.9.0 * **github.com/vishvananda/netlink** v1.2.0-beta -> v1.2.1-beta.2 * **github.com/vmware-tanzu/sonobuoy** v0.56.6 -> v0.56.9 * **github.com/vmware/govmomi** v0.28.0 -> v0.29.0 * **golang.org/x/net** 5463443f8c37 -> a33c5aa5df48 * **golang.org/x/sync** 0976fa681c29 -> 886fb9371eb4 * **golang.org/x/sys** bc2c85ada10a -> 1c4a2a72c664 * **golang.org/x/term** 065cf7ba2467 -> a9ba230a4035 * **golang.org/x/time** 583f2d630306 -> e5dcc9cfc0b9 * **google.golang.org/grpc** v1.46.2 -> v1.48.0 * **google.golang.org/protobuf** v1.28.0 -> v1.28.1 * **gopkg.in/yaml.v3** 496545a6307b -> v3.0.1 * **inet.af/netaddr** c74959edd3b6 -> 097006376321 * **k8s.io/api** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/apimachinery** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/apiserver** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/client-go** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/component-base** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/cri-api** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/kubectl** v0.24.2 -> v0.25.0-beta.0 * **k8s.io/kubelet** v0.24.2 -> v0.25.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.64 -> v1.2.65 Previous release can be found at [v1.1.0](https://github.com/siderolabs/talos/releases/tag/v1.1.0) ## [Talos 1.2.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.2.0-alpha.1) (2022-07-28) Welcome to the v1.2.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Generating Talos secrets from PKI directory It is now possible to generate a secrets bundle from a Kubernetes PKI directory (e.g. `/etc/kubernetes/pki`). You can also specify a bootstrap token to be used in the secrets bundle. This secrets bundle can then be used to generate a machine config. This facilitates migrating clusters (e.g. created using `kubeadm`) to Talos. ``` talosctl gen secrets --kubernetes-bootstrap-token znzio1.1ifu15frz7jd59pv --from-kubernetes-pki /etc/kubernetes/pki talosctl gen config --with-secrets secrets.yaml my-cluster https://172.20.0.1:6443 ``` ### Kubernetes ControlPlane Components Talos now run all Kubernetes Control Plane Components with the CRI default Seccomp Profile and other recommendations as described in [KEP-2568](https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/kubeadm/2568-kubeadm-non-root-control-plane). ### Kubelet Default Runtime Seccomp Profile Talos now runs Kubelet with the CRI default Seccomp Profile enabled. This can be disabled by setting `.machine.kubelet.defaultRuntimeSeccompProfileEnabled` to `false`. This is not enabled automatically on upgrades, so upgrading to Talos v1.2 needs this to be explicitly enabled. ### Network bridge support Talos now supports configuring Linux bridges. It can be configured in the machine config like the following: ```yaml machine: network: interfaces: - interface: br0 bridge: stp: enabled: true interfaces: - eth0 - eth1 ``` See [documentation](https://www.talos.dev/v1.2/reference/configuration/#bridge) for more details. ### VLAN support in cmdline arguments Talos now supports dracut-style `vlan` kernel argument to allow installing Talos Linux in networks where ports are not tagged with a default VLAN: ``` vlan=eth1.5:eth1 ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1.5::::: ``` ### Packet Capture Talos now supports capturing packets on a network interface with `talosctl pcap` command: talosctl pcap --interface eth0 ### Seccomp Profiles Talos now supports creating custom seccomp profiles on the host machine which in turn can be used by Kubernetes workloads. It can be configured in the machine config as below: ```yaml machine: seccompProfiles: - name: audit.json value: defaultAction: SCMP_ACT_LOG - name: deny.json value: {"defaultAction":"SCMP_ACT_LOG"} ``` This profile data can be either configured as a YAML definition or as a JSON string. The profiles are created on the host under `/var/lib/seccomp/profiles` and bind mounted at `/var/lib/kubelet/seccomp/profiles` so Kubelet can use it. See [documentation](https://www.talos.dev/v1.2/kubernetes-guides/configuration/seccomp-profiles/) for more details. ### Stable Default Hostname Talos now generates the default hostname (when there is no explicitly specified hostname) for the nodes based on the node id (e.g. `talos-2gd-76y`) instead of using the DHCP assigned IP address (e.g. `talos-172-20-0-2`). This ensures that the node hostname is not changed when DHCP assigns a new IP to a node. ### Strategic merge machine configuration patching In addition to JSON (RFC6902) patches Talos now supports [strategic merge patching](https://www.talos.dev/v1.2/talos-guides/configuration/patching/). For example, machine hostname can be set with the following patch: ```yaml machine: network: hostname: worker1 ``` Patch format is detected automatically. ### Variable substitution for URL query parameter in the talos.config kernel parameter The kernel parameter talos.config can now substitute system information into placeholders inside its URL query values. This example shows all supported variables: ```http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}``` ### Component Updates * Linux: 5.15.57 Talos is built with Go 1.18.4. ### Contributors * Andrey Smirnov * Noel Georgi * Utku Ozdemir * Dmitriy Matrenichev * Philipp Sauter * Tim Jones * Spencer Smith * Artem Chernyshev * Davincible * AMet * Alex Wied * Bermi Ferrer * Christoph Schmatzler * Dennis Marttinen * Eirik Askheim * Florian Klink * Han Cen * Larry Rosenman * Markus Reiter * Matthew Richardson * Nico Berlee * Rio Kierkels * RyanSquared * Serge Logvinov * Seán C McCord * hobyte * nett_hier * zebernst ### Changes
128 commits

* [`5ac4947b6`](https://github.com/siderolabs/talos/commit/5ac4947b634865104003ac91590d81cae4dd5e53) feat: enable default seccomp profile for kubelet * [`e5994ff7a`](https://github.com/siderolabs/talos/commit/e5994ff7a776e9f43f82340ef9916160d2a24149) fix: skip `ResetDuringBoot` test if the `Cluster` config is unknown * [`8028e1074`](https://github.com/siderolabs/talos/commit/8028e10749bfc53940651a1cb3eb0dcd56c1507c) fix: wait for boot done when rebooting a node in the integration tests * [`ae1bec59e`](https://github.com/siderolabs/talos/commit/ae1bec59e9ce1a9b5f3a2f6a040a16bb26d991a7) feat: allow running only one sequence at a time * [`ec05aee04`](https://github.com/siderolabs/talos/commit/ec05aee040371e7261911bdebb03004140ad8eed) fix: correctly unwrap errors when streaming * [`7c7f2d8c3`](https://github.com/siderolabs/talos/commit/7c7f2d8c3be3a49f9409b67a98984d5a3d6e6404) feat: refactor disk size matcher to be compatible with DeepEqual * [`3addea83b`](https://github.com/siderolabs/talos/commit/3addea83b9ba73418bd1cfd6e083fd4252b41356) feat: introduce support for Talos API access from Kubernetes * [`34d3a4164`](https://github.com/siderolabs/talos/commit/34d3a41643162eaedca9210c0edbc813ca77c3bb) docs: add missing <> to relref * [`c4d2d20c4`](https://github.com/siderolabs/talos/commit/c4d2d20c419fbbac7888b4b033ef939ff476e3a4) fix: enable stable hostnames for worker configs as well * [`0326bac1f`](https://github.com/siderolabs/talos/commit/0326bac1f92c6aa2fc929b768027a71c7285d800) chore: bump kernel to 5.15.57 * [`86820c33f`](https://github.com/siderolabs/talos/commit/86820c33f1055ce0efbfe2934e84c5627919ed07) chore: bump dependencies * [`6e7dfeeb3`](https://github.com/siderolabs/talos/commit/6e7dfeeb38fe5cf0065faa49ca36c3a292e86fae) fix: data race in packet capture (part 2) * [`c11e1dae7`](https://github.com/siderolabs/talos/commit/c11e1dae7033e5a530eb7185eabf5c89deacaace) docs: fix spelling and grammar errors * [`30f7851d2`](https://github.com/siderolabs/talos/commit/30f7851d2a25ed0f9d7cf28548c3a1f09cd664cd) chore: bump golangci-lint from 1.45.2 to 1.47.2 * [`2cce9112d`](https://github.com/siderolabs/talos/commit/2cce9112d17384e491ab91b75494241de664ec18) chore: bump goimports from 0.1.10 to 0.1.11 * [`18756c7ff`](https://github.com/siderolabs/talos/commit/18756c7ff6a9e81615aec1b1ecb3808f500fdaf1) fix: folder permissions of overlay mounted folders * [`47c35dc47`](https://github.com/siderolabs/talos/commit/47c35dc4740cad3f758969b5d93885c9782b439a) feat: set stable default hostname based on machine-id * [`1ed3df295`](https://github.com/siderolabs/talos/commit/1ed3df295c1a26ed8243c58d6bfecb8c7398daec) chore: support glibc apps extension spec * [`a2aea9726`](https://github.com/siderolabs/talos/commit/a2aea97263c787de81f911e085cf81f56dfd0d82) fix: write etcd PKI files in a controller * [`bb4abc096`](https://github.com/siderolabs/talos/commit/bb4abc0961dba4c2e158cfebdd7b3d8c010a30b3) fix: regenerate kubelet certs when hostname changes * [`d650afb6c`](https://github.com/siderolabs/talos/commit/d650afb6cdd405292515be266f3ee05f19f014ec) chore: fix typo in `powercycle` * [`644e803ad`](https://github.com/siderolabs/talos/commit/644e803adf35eaa735af5487ffdcfb7471d17f3e) fix: use masks and different firewall mark for KubeSpan * [`80444a43d`](https://github.com/siderolabs/talos/commit/80444a43d9382f44c515224a02610443c77b0fe9) fix: remove data race in pcap capture * [`04a45dff2`](https://github.com/siderolabs/talos/commit/04a45dff2831b87b2373664e87794dbf5ecabd08) docs: remove katacoda links * [`065b59276`](https://github.com/siderolabs/talos/commit/065b59276c9ac48f2e5fa051c132efc5bfb4b849) feat: implement packet capture API * [`7c006cabc`](https://github.com/siderolabs/talos/commit/7c006cabc7ee15146a8db4358156c049d4525cfe) feat: update Kubernetes to 1.24.3 * [`551290195`](https://github.com/siderolabs/talos/commit/551290195c868c1f23ea0307ef8058537da73064) chore: bump dependencies * [`1677bcc4b`](https://github.com/siderolabs/talos/commit/1677bcc4b243886c75f7acc95fe3225032aeee7e) fix: skip bond itself when matching interface (Equinix Metal) * [`f1c2b5c55`](https://github.com/siderolabs/talos/commit/f1c2b5c558f96ad45261f0f4f50ecbd50475543e) feat: implement strategic merge patching for API server admission config * [`be98cb82b`](https://github.com/siderolabs/talos/commit/be98cb82b5d56e26210e0be0d5d54338df0bb092) feat: follow KEP-2568 non-root enhancements * [`87ea1d961`](https://github.com/siderolabs/talos/commit/87ea1d9611332f4552bcf35a2fc80e43fbef89ed) fix: update kubelet kubeconfig when cluster control plane endpoint changes * [`a75fe7600`](https://github.com/siderolabs/talos/commit/a75fe7600d554c7d8404a32e9a790c27dfdebb44) feat: gen secrets from kubernetes pki dir * [`a1d7b535a`](https://github.com/siderolabs/talos/commit/a1d7b535ad59ec900f8e907bcd8085cece77c1e4) docs: add kubeadm migration guide * [`9e0c56581`](https://github.com/siderolabs/talos/commit/9e0c56581e8ab144324a148dc7489da595b0edcb) docs: guide for setting up synology-csi driver * [`f0b8eea5e`](https://github.com/siderolabs/talos/commit/f0b8eea5e5b30ca6864eda6872d5e23f41ffdf7d) refactor: remove bootstrap sequence * [`89c7da899`](https://github.com/siderolabs/talos/commit/89c7da8991eb1760f220ce7bf7bc7fec7dd4a089) docs: add documentation for vagrant & libvirt * [`014b85fdc`](https://github.com/siderolabs/talos/commit/014b85fdcb6575b3db19d6cc7c848c02957f5913) docs: improve talos kubernetes upgrade note * [`88bb017ed`](https://github.com/siderolabs/talos/commit/88bb017ed0a57139380dcf07db4c9585d96a4b7e) docs: remove old docs from site * [`c92c90655`](https://github.com/siderolabs/talos/commit/c92c90655ace0a0465599483004793c68611af5b) feat: build talosctl for FreeBSD * [`616da3069`](https://github.com/siderolabs/talos/commit/616da30695c0a0f8ffd9eb5fed99e2d4aeaf159f) docs: update last release for 1.1 * [`091e6ef0e`](https://github.com/siderolabs/talos/commit/091e6ef0eb4d5b5fa1245968abb25ebaafcd2a96) feat: resubstitute talos.config url variables on retry * [`ec74ab38a`](https://github.com/siderolabs/talos/commit/ec74ab38aa95c222a26048071cd9911024fe3ae2) feat: update Go to 1.18.4, Linux to 5.15.54 * [`641f6a1e4`](https://github.com/siderolabs/talos/commit/641f6a1e4e106414f9e7667792a84586d49171b9) feat: expose strategic merge config patches * [`6e3d2d647`](https://github.com/siderolabs/talos/commit/6e3d2d647d031ff9f11f595a607ae5227cb1035c) docs: fix disk encryption params * [`c43d6a31d`](https://github.com/siderolabs/talos/commit/c43d6a31d92db98dd44b2f533d1d6dcd6d8a8c48) docs: fix typos * [`551887528`](https://github.com/siderolabs/talos/commit/551887528cf3a29e60c540dc02355a4937cc5b25) chore: bump dependencies * [`626ef05e6`](https://github.com/siderolabs/talos/commit/626ef05e6063df3010aee805f6ac442e3298e568) fix: correct SANs for etcd certs * [`83ce92c5f`](https://github.com/siderolabs/talos/commit/83ce92c5ff8f8c55ab8e2dddcc45a253b7b9191b) docs: fix theila docs * [`8a038d40e`](https://github.com/siderolabs/talos/commit/8a038d40ee9071cf77c559a813ecaa681f730a66) fix: stabilize etcd join and promote sequences * [`136122556`](https://github.com/siderolabs/talos/commit/136122556c596ebf579be3aa00e767d05b0e4bb5) fix: use correct etcd cert path * [`c170ec0b0`](https://github.com/siderolabs/talos/commit/c170ec0b09e20d2277862057a6f5cefbc963d276) chore: bump kernel to 5.15.53 * [`d924901b7`](https://github.com/siderolabs/talos/commit/d924901b79d91c3ce5292b7d95487485a7ce0abc) feat: add cli subcommand to generate secrets * [`34aabedd8`](https://github.com/siderolabs/talos/commit/34aabedd805a6cb804c9db5e54d80b58c00da0de) feat: more circular pkg from internal to pkg * [`4f044e466`](https://github.com/siderolabs/talos/commit/4f044e46643a275a987b61fa4da60f700ccde774) feat: implement strategic merge machine config patching * [`c2a512608`](https://github.com/siderolabs/talos/commit/c2a51260881e95e18567962e437c1081ae59968c) fix: avoid double append of `talos.platform` kernel argument * [`27dfe7c03`](https://github.com/siderolabs/talos/commit/27dfe7c0352b62fee9895f4ae172467499072af5) fix: perform accurate conflict resolution on overal (kubespan) * [`e437445b4`](https://github.com/siderolabs/talos/commit/e437445b4044f58bcad35b171a7f259de6f6ac6d) chore: bump kernel to 5.15.52 * [`d27a6a4ac`](https://github.com/siderolabs/talos/commit/d27a6a4ac0e058e5e526ee51be512c5d01ea7a19) feat: add vlan support to cmdline * [`fdca5d8a9`](https://github.com/siderolabs/talos/commit/fdca5d8a95a04d865a0417624628740480c18b5f) chore: bump dependencies * [`ae3840dbc`](https://github.com/siderolabs/talos/commit/ae3840dbc34f32faf8da426378a8a32f1c009659) refactor: move kubeconfig package under public api * [`184e113f3`](https://github.com/siderolabs/talos/commit/184e113f35f4a3cd2f036502862af325ee6e3d2f) chore: disable systeminfo controller in container * [`86a0a7bdf`](https://github.com/siderolabs/talos/commit/86a0a7bdf70d318bed2143d65784faae6f9125d4) refactor: use pointer types more in machine config structs * [`3a1eb10e6`](https://github.com/siderolabs/talos/commit/3a1eb10e61edeef2af497c6ad9101d6cec539a34) docs: update the Proxmox `kvm64` note * [`30e220fcd`](https://github.com/siderolabs/talos/commit/30e220fcd265337790ccc9a8070fd7b509336fe0) docs: kernel cmdline params updated on upgrades * [`915de9cf9`](https://github.com/siderolabs/talos/commit/915de9cf9bfd33d95b766f8ed5ce0ebb863f60f6) docs: fix bridge documentation * [`52cd12951`](https://github.com/siderolabs/talos/commit/52cd12951c567d76c9dfa3ca11ba53d16cdbc5d3) test: bump Talos versions in upgrade tests * [`022581d80`](https://github.com/siderolabs/talos/commit/022581d8092840f4c1d9aa4b198650db4f3ba78b) release(v1.2.0-alpha.0): prepare release * [`643e81cfe`](https://github.com/siderolabs/talos/commit/643e81cfed675a018ec3af20b74fdcfcdc665d60) feat: add SenseLabs to ADOPTERS.md * [`bdfee2b3b`](https://github.com/siderolabs/talos/commit/bdfee2b3b7bf773326bd839ea6049e0262382071) chore: bump kernel to 5.15.51 * [`36c44a651`](https://github.com/siderolabs/talos/commit/36c44a65110713274fbb4b6638a36f3377c96bb3) fix: provide CA certificates in `/etc/ssl/certs/ca-certificates.crt` * [`7ebd9bcce`](https://github.com/siderolabs/talos/commit/7ebd9bcce6f2b1e6a030e551f41f590996294573) docs: fix pod security talos resource name * [`57b625e0a`](https://github.com/siderolabs/talos/commit/57b625e0a68534fdb3847e1fff5c18906630d1da) refactor: avoid recreating grpc clients in service health checks * [`a68a00f1b`](https://github.com/siderolabs/talos/commit/a68a00f1b9bdbb519966bbf1ac7f463796a85abb) docs: recommend setting "host" Processor Type on proxmox * [`923600a73`](https://github.com/siderolabs/talos/commit/923600a73c7368fd9217680fdfcc0dc2e9dc4b8e) chore: bump kernel to 5.15.50 * [`758a9bf59`](https://github.com/siderolabs/talos/commit/758a9bf59fad2f49ca03937c7d1bcfb3c13a9a0b) docs: add theila ui * [`b81016e62`](https://github.com/siderolabs/talos/commit/b81016e628642d93fb0b123f7146558279e0c648) chore: update blockdevice library to v0.3.3 * [`284a2f959`](https://github.com/siderolabs/talos/commit/284a2f9596ce899236d78d43c6d42a287f60540a) fix: filter static pods correctly and optimize fetching * [`61abf3111`](https://github.com/siderolabs/talos/commit/61abf311109f2d604d227bb6d290ccaeca19b3f0) docs: change command for cluster create to keep $HOME with sudo * [`6ae1e9bf2`](https://github.com/siderolabs/talos/commit/6ae1e9bf2ba7778dbe8a0919cbdf81fbe74b8e8c) chore: bump dependencies * [`2deff6b6e`](https://github.com/siderolabs/talos/commit/2deff6b6e148d99e9c88159f4895594417cdf080) feat: add support for variable substitution in talos.config kernel parameter * [`103c94225`](https://github.com/siderolabs/talos/commit/103c942256e7832b18e973f3fd698d7e94818c6f) fix: update crypto library with support for RSA-SHA* * [`448de7194`](https://github.com/siderolabs/talos/commit/448de7194911b3f8bd79cec3a3e93515ffd2e0a9) docs: add UpCloud installation guide * [`07014e0a8`](https://github.com/siderolabs/talos/commit/07014e0a8ee291ab4f2848787fc7462676c11fec) fix: generate correct bootstrap manifests when only IPv6 CIDR is used * [`465edbb47`](https://github.com/siderolabs/talos/commit/465edbb4791315d8709daeeba19f14b3e53680f3) fix: look for qemu-kvm binary * [`63caa281a`](https://github.com/siderolabs/talos/commit/63caa281ae8b83add1b070014282a6f792843845) fix: create native image format for DigitalOcean * [`f15ce549e`](https://github.com/siderolabs/talos/commit/f15ce549e940e6a0a95b8f78a4d7ad967f0a3900) fix: siderlink api assume port 443 with https schema * [`797596229`](https://github.com/siderolabs/talos/commit/797596229a7c4a883810c4229492cdfd0b441f19) feat: add support for configuring network bridges * [`2b23fabcc`](https://github.com/siderolabs/talos/commit/2b23fabcc1c3f5f495ea4d7fa6597fa639d4ce82) docs: use SVG image for K8s conformance * [`d4606c33e`](https://github.com/siderolabs/talos/commit/d4606c33ec36563d29b5ac95f11d1479c61a1905) chore: bump kernel to 5.15.49 * [`cfb640222`](https://github.com/siderolabs/talos/commit/cfb640222b80e1a2a6c3a8a505c5f6acfb148d24) docs: update docs for release 1.1 * [`b816d0b60`](https://github.com/siderolabs/talos/commit/b816d0b60077e83028b950a544c810d0875be268) docs: fix the vendor information for Kubernetes conformance tests * [`a167a5402`](https://github.com/siderolabs/talos/commit/a167a54021c979a1ca761674d8e368d5fb7dda6a) test: fix CLI nodes discovery without provisioner data * [`916a30682`](https://github.com/siderolabs/talos/commit/916a306829190c8eccbb993cfc166aa3cf08042e) docs: add twitter meta info * [`80090a3ed`](https://github.com/siderolabs/talos/commit/80090a3eda00e9808b0ba15241ea36dc6835f6d1) test: fix health endpoint cli test when discovery is disabled * [`3c263bb44`](https://github.com/siderolabs/talos/commit/3c263bb44639edf456d1c6203f41c71fa4d6d1d0) chore: bump dependencies * [`e8113527f`](https://github.com/siderolabs/talos/commit/e8113527f94f0fbc6cf6fdb9390dfb09d984213d) chore: bump kubernetes to v1.24.2 * [`068f1b6d0`](https://github.com/siderolabs/talos/commit/068f1b6d0517f62d2a76c7b1a761f15104220644) feat: add ctest package and base for test suite * [`2aad3a1e4`](https://github.com/siderolabs/talos/commit/2aad3a1e4911ebcd3eb970f09baa74e10383a959) chore: bump kernel to 5.15.48 * [`a31a858e0`](https://github.com/siderolabs/talos/commit/a31a858e08a7e022dc26c729ef097b6ed56a83ad) docs: snippets for logging api server audit logs * [`89aaaef9f`](https://github.com/siderolabs/talos/commit/89aaaef9f5dd403919535fc3e81ef635d233c0da) chore: bump kernel to 5.15.47 * [`6759fcd4a`](https://github.com/siderolabs/talos/commit/6759fcd4aeeca74e78e346b4265e86580991d800) feat: use discovery service on cluster health checks * [`f54d90787`](https://github.com/siderolabs/talos/commit/f54d9078719a62bcefcab367957f166e7a43decc) fix: enable orderly poweroff in hyper-v on Azure * [`35475ce45`](https://github.com/siderolabs/talos/commit/35475ce45b1ad64bb34149be9960f5acdd2bfe86) docs: openebs jiva example with iscsi-tools extension * [`8d2be5e31`](https://github.com/siderolabs/talos/commit/8d2be5e315fb05002587570d759322c9c00ad525) feat: extend node definition used in health checks * [`7a11b4def`](https://github.com/siderolabs/talos/commit/7a11b4def78e5b4506611fe85d083a12b695bd05) fix: make `talosctl bootstrap` accept only single node * [`217fba288`](https://github.com/siderolabs/talos/commit/217fba288f07ccf7053e804c226a2e0b9301f864) test: fix csi tests * [`90bf34fed`](https://github.com/siderolabs/talos/commit/90bf34fed98cb9ff524097da4043d4ff221a0b20) docs: fork docs for Talos 1.2 * [`a0dd010a8`](https://github.com/siderolabs/talos/commit/a0dd010a87b0ef0350299db3944f3a941fca09b4) docs: add link to discovery service in kubespan * [`c0371410e`](https://github.com/siderolabs/talos/commit/c0371410ee93f9773938b5b73be6eba246fd8f47) fix: support SideroLink "secure" gRPC connection * [`b03709620`](https://github.com/siderolabs/talos/commit/b03709620201b44f6464a7df804e2003c9751a30) feat: build Talos images with system extensions included * [`43def7490`](https://github.com/siderolabs/talos/commit/43def7490ffa598ba973f35903eaea462db374b1) chore: bump kernel and runc * [`4dbbf4ac5`](https://github.com/siderolabs/talos/commit/4dbbf4ac50f6b1ccd62efb1c06c8a92d8f91e65c) chore: add generic methods and use them part #2 * [`7114292b6`](https://github.com/siderolabs/talos/commit/7114292b6cd5f93a51b905db6377ffdadf429f19) docs: fix latest release version in docs * [`da2985fe1`](https://github.com/siderolabs/talos/commit/da2985fe1b29abac46b761a5ec2f4557d12ce985) fix: respect local API server port * [`e03266667`](https://github.com/siderolabs/talos/commit/e03266667f11d751f16a7208e774996ebadf8842) fix: correctly validate reboot mode in CLI * [`70fc42409`](https://github.com/siderolabs/talos/commit/70fc42409980a1a78b98a962284460ea18c42513) chore: add generic methods and use them * [`3ae8bdd92`](https://github.com/siderolabs/talos/commit/3ae8bdd92e43c8a5fedd455d4479678ccb263a6b) chore: run `xfs_repair` on xfs filesystem returing `EUCLEAN` * [`0c91c89f4`](https://github.com/siderolabs/talos/commit/0c91c89f4f0732147f5b6c41fb4f3da8437ae9f1) chore: revert day-two tests for csi tests * [`f71b58312`](https://github.com/siderolabs/talos/commit/f71b58312251ec2924607fb5166afa6c8aaf01bb) feat: disallow anonymous requests by default (kube-apiserver) * [`c19dd1b89`](https://github.com/siderolabs/talos/commit/c19dd1b8925fc8ec25a721d336ad0b363fc27fd4) feat: add 'etcd members should be control plane nodes' health check * [`f2997c0f2`](https://github.com/siderolabs/talos/commit/f2997c0f22b93382bfb61ff556961de56445807f) chore: bump dependencies * [`f3efec4b5`](https://github.com/siderolabs/talos/commit/f3efec4b56bc72dc5c769a76f6254d14d3f20b1b) feat: update containerd 1.6.6, Linux 5.15.45, Flannel 0.18.1 * [`27f8e50ce`](https://github.com/siderolabs/talos/commit/27f8e50ce90c47f5ddc82645e0ebcdb1a8ed778b) fix: add ovmf image path for rhel * [`87e7de30c`](https://github.com/siderolabs/talos/commit/87e7de30cb6ed02991cb46e25d20343555cc6317) docs: fix required ports * [`c126f2ee8`](https://github.com/siderolabs/talos/commit/c126f2ee85572bdfde61f9a3ba878f0595c74cfe) chore: bump golang to 1.18.3 * [`c1aed6240`](https://github.com/siderolabs/talos/commit/c1aed62405dddb2cbd2d47d699aae0c94df70886) fix: wait for `/var` to be mounted in kubelet service controller * [`d7a64f5d2`](https://github.com/siderolabs/talos/commit/d7a64f5d2a6ff9dccdf3bdb948684d9513912be9) fix: improve vip operator shutdown sequence * [`7b9dfcb85`](https://github.com/siderolabs/talos/commit/7b9dfcb852af6a48f00ddfca7337a571aa56a2b3) chore: add 'make go-mod-outdated'

### Changes since v1.2.0-alpha.0
66 commits

* [`5ac4947b6`](https://github.com/siderolabs/talos/commit/5ac4947b634865104003ac91590d81cae4dd5e53) feat: enable default seccomp profile for kubelet * [`e5994ff7a`](https://github.com/siderolabs/talos/commit/e5994ff7a776e9f43f82340ef9916160d2a24149) fix: skip `ResetDuringBoot` test if the `Cluster` config is unknown * [`8028e1074`](https://github.com/siderolabs/talos/commit/8028e10749bfc53940651a1cb3eb0dcd56c1507c) fix: wait for boot done when rebooting a node in the integration tests * [`ae1bec59e`](https://github.com/siderolabs/talos/commit/ae1bec59e9ce1a9b5f3a2f6a040a16bb26d991a7) feat: allow running only one sequence at a time * [`ec05aee04`](https://github.com/siderolabs/talos/commit/ec05aee040371e7261911bdebb03004140ad8eed) fix: correctly unwrap errors when streaming * [`7c7f2d8c3`](https://github.com/siderolabs/talos/commit/7c7f2d8c3be3a49f9409b67a98984d5a3d6e6404) feat: refactor disk size matcher to be compatible with DeepEqual * [`3addea83b`](https://github.com/siderolabs/talos/commit/3addea83b9ba73418bd1cfd6e083fd4252b41356) feat: introduce support for Talos API access from Kubernetes * [`34d3a4164`](https://github.com/siderolabs/talos/commit/34d3a41643162eaedca9210c0edbc813ca77c3bb) docs: add missing <> to relref * [`c4d2d20c4`](https://github.com/siderolabs/talos/commit/c4d2d20c419fbbac7888b4b033ef939ff476e3a4) fix: enable stable hostnames for worker configs as well * [`0326bac1f`](https://github.com/siderolabs/talos/commit/0326bac1f92c6aa2fc929b768027a71c7285d800) chore: bump kernel to 5.15.57 * [`86820c33f`](https://github.com/siderolabs/talos/commit/86820c33f1055ce0efbfe2934e84c5627919ed07) chore: bump dependencies * [`6e7dfeeb3`](https://github.com/siderolabs/talos/commit/6e7dfeeb38fe5cf0065faa49ca36c3a292e86fae) fix: data race in packet capture (part 2) * [`c11e1dae7`](https://github.com/siderolabs/talos/commit/c11e1dae7033e5a530eb7185eabf5c89deacaace) docs: fix spelling and grammar errors * [`30f7851d2`](https://github.com/siderolabs/talos/commit/30f7851d2a25ed0f9d7cf28548c3a1f09cd664cd) chore: bump golangci-lint from 1.45.2 to 1.47.2 * [`2cce9112d`](https://github.com/siderolabs/talos/commit/2cce9112d17384e491ab91b75494241de664ec18) chore: bump goimports from 0.1.10 to 0.1.11 * [`18756c7ff`](https://github.com/siderolabs/talos/commit/18756c7ff6a9e81615aec1b1ecb3808f500fdaf1) fix: folder permissions of overlay mounted folders * [`47c35dc47`](https://github.com/siderolabs/talos/commit/47c35dc4740cad3f758969b5d93885c9782b439a) feat: set stable default hostname based on machine-id * [`1ed3df295`](https://github.com/siderolabs/talos/commit/1ed3df295c1a26ed8243c58d6bfecb8c7398daec) chore: support glibc apps extension spec * [`a2aea9726`](https://github.com/siderolabs/talos/commit/a2aea97263c787de81f911e085cf81f56dfd0d82) fix: write etcd PKI files in a controller * [`bb4abc096`](https://github.com/siderolabs/talos/commit/bb4abc0961dba4c2e158cfebdd7b3d8c010a30b3) fix: regenerate kubelet certs when hostname changes * [`d650afb6c`](https://github.com/siderolabs/talos/commit/d650afb6cdd405292515be266f3ee05f19f014ec) chore: fix typo in `powercycle` * [`644e803ad`](https://github.com/siderolabs/talos/commit/644e803adf35eaa735af5487ffdcfb7471d17f3e) fix: use masks and different firewall mark for KubeSpan * [`80444a43d`](https://github.com/siderolabs/talos/commit/80444a43d9382f44c515224a02610443c77b0fe9) fix: remove data race in pcap capture * [`04a45dff2`](https://github.com/siderolabs/talos/commit/04a45dff2831b87b2373664e87794dbf5ecabd08) docs: remove katacoda links * [`065b59276`](https://github.com/siderolabs/talos/commit/065b59276c9ac48f2e5fa051c132efc5bfb4b849) feat: implement packet capture API * [`7c006cabc`](https://github.com/siderolabs/talos/commit/7c006cabc7ee15146a8db4358156c049d4525cfe) feat: update Kubernetes to 1.24.3 * [`551290195`](https://github.com/siderolabs/talos/commit/551290195c868c1f23ea0307ef8058537da73064) chore: bump dependencies * [`1677bcc4b`](https://github.com/siderolabs/talos/commit/1677bcc4b243886c75f7acc95fe3225032aeee7e) fix: skip bond itself when matching interface (Equinix Metal) * [`f1c2b5c55`](https://github.com/siderolabs/talos/commit/f1c2b5c558f96ad45261f0f4f50ecbd50475543e) feat: implement strategic merge patching for API server admission config * [`be98cb82b`](https://github.com/siderolabs/talos/commit/be98cb82b5d56e26210e0be0d5d54338df0bb092) feat: follow KEP-2568 non-root enhancements * [`87ea1d961`](https://github.com/siderolabs/talos/commit/87ea1d9611332f4552bcf35a2fc80e43fbef89ed) fix: update kubelet kubeconfig when cluster control plane endpoint changes * [`a75fe7600`](https://github.com/siderolabs/talos/commit/a75fe7600d554c7d8404a32e9a790c27dfdebb44) feat: gen secrets from kubernetes pki dir * [`a1d7b535a`](https://github.com/siderolabs/talos/commit/a1d7b535ad59ec900f8e907bcd8085cece77c1e4) docs: add kubeadm migration guide * [`9e0c56581`](https://github.com/siderolabs/talos/commit/9e0c56581e8ab144324a148dc7489da595b0edcb) docs: guide for setting up synology-csi driver * [`f0b8eea5e`](https://github.com/siderolabs/talos/commit/f0b8eea5e5b30ca6864eda6872d5e23f41ffdf7d) refactor: remove bootstrap sequence * [`89c7da899`](https://github.com/siderolabs/talos/commit/89c7da8991eb1760f220ce7bf7bc7fec7dd4a089) docs: add documentation for vagrant & libvirt * [`014b85fdc`](https://github.com/siderolabs/talos/commit/014b85fdcb6575b3db19d6cc7c848c02957f5913) docs: improve talos kubernetes upgrade note * [`88bb017ed`](https://github.com/siderolabs/talos/commit/88bb017ed0a57139380dcf07db4c9585d96a4b7e) docs: remove old docs from site * [`c92c90655`](https://github.com/siderolabs/talos/commit/c92c90655ace0a0465599483004793c68611af5b) feat: build talosctl for FreeBSD * [`616da3069`](https://github.com/siderolabs/talos/commit/616da30695c0a0f8ffd9eb5fed99e2d4aeaf159f) docs: update last release for 1.1 * [`091e6ef0e`](https://github.com/siderolabs/talos/commit/091e6ef0eb4d5b5fa1245968abb25ebaafcd2a96) feat: resubstitute talos.config url variables on retry * [`ec74ab38a`](https://github.com/siderolabs/talos/commit/ec74ab38aa95c222a26048071cd9911024fe3ae2) feat: update Go to 1.18.4, Linux to 5.15.54 * [`641f6a1e4`](https://github.com/siderolabs/talos/commit/641f6a1e4e106414f9e7667792a84586d49171b9) feat: expose strategic merge config patches * [`6e3d2d647`](https://github.com/siderolabs/talos/commit/6e3d2d647d031ff9f11f595a607ae5227cb1035c) docs: fix disk encryption params * [`c43d6a31d`](https://github.com/siderolabs/talos/commit/c43d6a31d92db98dd44b2f533d1d6dcd6d8a8c48) docs: fix typos * [`551887528`](https://github.com/siderolabs/talos/commit/551887528cf3a29e60c540dc02355a4937cc5b25) chore: bump dependencies * [`626ef05e6`](https://github.com/siderolabs/talos/commit/626ef05e6063df3010aee805f6ac442e3298e568) fix: correct SANs for etcd certs * [`83ce92c5f`](https://github.com/siderolabs/talos/commit/83ce92c5ff8f8c55ab8e2dddcc45a253b7b9191b) docs: fix theila docs * [`8a038d40e`](https://github.com/siderolabs/talos/commit/8a038d40ee9071cf77c559a813ecaa681f730a66) fix: stabilize etcd join and promote sequences * [`136122556`](https://github.com/siderolabs/talos/commit/136122556c596ebf579be3aa00e767d05b0e4bb5) fix: use correct etcd cert path * [`c170ec0b0`](https://github.com/siderolabs/talos/commit/c170ec0b09e20d2277862057a6f5cefbc963d276) chore: bump kernel to 5.15.53 * [`d924901b7`](https://github.com/siderolabs/talos/commit/d924901b79d91c3ce5292b7d95487485a7ce0abc) feat: add cli subcommand to generate secrets * [`34aabedd8`](https://github.com/siderolabs/talos/commit/34aabedd805a6cb804c9db5e54d80b58c00da0de) feat: more circular pkg from internal to pkg * [`4f044e466`](https://github.com/siderolabs/talos/commit/4f044e46643a275a987b61fa4da60f700ccde774) feat: implement strategic merge machine config patching * [`c2a512608`](https://github.com/siderolabs/talos/commit/c2a51260881e95e18567962e437c1081ae59968c) fix: avoid double append of `talos.platform` kernel argument * [`27dfe7c03`](https://github.com/siderolabs/talos/commit/27dfe7c0352b62fee9895f4ae172467499072af5) fix: perform accurate conflict resolution on overal (kubespan) * [`e437445b4`](https://github.com/siderolabs/talos/commit/e437445b4044f58bcad35b171a7f259de6f6ac6d) chore: bump kernel to 5.15.52 * [`d27a6a4ac`](https://github.com/siderolabs/talos/commit/d27a6a4ac0e058e5e526ee51be512c5d01ea7a19) feat: add vlan support to cmdline * [`fdca5d8a9`](https://github.com/siderolabs/talos/commit/fdca5d8a95a04d865a0417624628740480c18b5f) chore: bump dependencies * [`ae3840dbc`](https://github.com/siderolabs/talos/commit/ae3840dbc34f32faf8da426378a8a32f1c009659) refactor: move kubeconfig package under public api * [`184e113f3`](https://github.com/siderolabs/talos/commit/184e113f35f4a3cd2f036502862af325ee6e3d2f) chore: disable systeminfo controller in container * [`86a0a7bdf`](https://github.com/siderolabs/talos/commit/86a0a7bdf70d318bed2143d65784faae6f9125d4) refactor: use pointer types more in machine config structs * [`3a1eb10e6`](https://github.com/siderolabs/talos/commit/3a1eb10e61edeef2af497c6ad9101d6cec539a34) docs: update the Proxmox `kvm64` note * [`30e220fcd`](https://github.com/siderolabs/talos/commit/30e220fcd265337790ccc9a8070fd7b509336fe0) docs: kernel cmdline params updated on upgrades * [`915de9cf9`](https://github.com/siderolabs/talos/commit/915de9cf9bfd33d95b766f8ed5ce0ebb863f60f6) docs: fix bridge documentation * [`52cd12951`](https://github.com/siderolabs/talos/commit/52cd12951c567d76c9dfa3ca11ba53d16cdbc5d3) test: bump Talos versions in upgrade tests

### Changes from siderolabs/extras
2 commits

* [`17a319f`](https://github.com/siderolabs/extras/commit/17a319ffeecba7f20c2fa9f75ccc677b3964e754) chore: update Go to 1.18.4 * [`892407f`](https://github.com/siderolabs/extras/commit/892407fd7c1a032ec4d7de5d52595ef3bcc7b484) chore: bump golang to 1.18.3

### Changes from siderolabs/pkgs
24 commits

* [`dcc0311`](https://github.com/siderolabs/pkgs/commit/dcc031138e336747daeee4b77d8813f4a8078abd) chore: bump kernel to 5.15.57 * [`b943a9d`](https://github.com/siderolabs/pkgs/commit/b943a9da08124042a56cf939c2cfc4c2591201d2) chore: update Go to 1.18.4 * [`a44e324`](https://github.com/siderolabs/pkgs/commit/a44e32412d667b26ce682288584d3f413ce888d4) chore: bump kernel to 5.15.54 * [`247f567`](https://github.com/siderolabs/pkgs/commit/247f567b8490bba7b02b54a42c33177521733701) chore: bump kernel to 5.15.53 * [`4fe9867`](https://github.com/siderolabs/pkgs/commit/4fe98672466f361fb7de4e1ddb3449b59e6a4193) chore: bump openssl to 1.1.1q * [`9ee662c`](https://github.com/siderolabs/pkgs/commit/9ee662c5d808c75e3373d0e1fb3dfbfe3cd9663c) chore: bump kernel to 5.15.52 * [`4412db8`](https://github.com/siderolabs/pkgs/commit/4412db88987acf91ffc2a83c09dd0dd5d84819eb) chore: bump kernel to 5.15.51 * [`6fedbdc`](https://github.com/siderolabs/pkgs/commit/6fedbdc826cffe5b0740c43f5641218df58e767c) chore: bump tools * [`f1f44e6`](https://github.com/siderolabs/pkgs/commit/f1f44e6a254e571c34f667e086e50afec099500b) chore: bump kernel to 5.15.50 * [`388af5e`](https://github.com/siderolabs/pkgs/commit/388af5e4eea16e0b19ce58879ea9d79676b9608b) chore: bump openssl to 1.1.1p * [`ed75c50`](https://github.com/siderolabs/pkgs/commit/ed75c5011e29107a59ffca1bececee1d22937ba2) chore: enable `RANDOM_TRUST_BOOTLOADER` by default * [`7c243f6`](https://github.com/siderolabs/pkgs/commit/7c243f6da5b3c0476106ff47d37b5d7a8ad28d98) chore: bump kernel to 5.15.49 * [`6e1269e`](https://github.com/siderolabs/pkgs/commit/6e1269e67f1e8a81cccf6ed45980595f2d6343f5) chore: bump kernel to 5.15.48 * [`5d671a3`](https://github.com/siderolabs/pkgs/commit/5d671a3cd6ebed495022f23e0073c1f971477305) chore: bump nvidia drivers to 515.48.07 * [`b35d835`](https://github.com/siderolabs/pkgs/commit/b35d835a1cfc3215d631f8ace3d3b1b7c83da008) chore: bump kernel to 5.15.47 * [`6604d6b`](https://github.com/siderolabs/pkgs/commit/6604d6b0686ea36983119edd7fb70755d3a812e0) feat: hyperv arm64 * [`c474058`](https://github.com/siderolabs/pkgs/commit/c4740588733138df9503c37304d1460166a3e233) chore: bump nvidia driver to 515.43.04 * [`5bc7e34`](https://github.com/siderolabs/pkgs/commit/5bc7e341fa0c035bbecc999bca3811b853684c5f) feat: update runc to 1.1.3, libseccomp to 2.5.4 * [`c02cd7a`](https://github.com/siderolabs/pkgs/commit/c02cd7a7086098698d1edd1d5ecb024ad9456a48) chore: bump kernel to 5.15.46 * [`b9c72a5`](https://github.com/siderolabs/pkgs/commit/b9c72a59cd6077ceb0ce53f11241d294c137f68b) feat: update containerd to 1.6.6 * [`f7786a3`](https://github.com/siderolabs/pkgs/commit/f7786a3a74bbf79c81cbcb031c357eae0e07726f) chore: bump kernel to 5.15.45 * [`b1c207d`](https://github.com/siderolabs/pkgs/commit/b1c207d63b1cac99b90025d530c57da4f51fc652) feat: update containerd to 1.6.5 * [`4d47830`](https://github.com/siderolabs/pkgs/commit/4d47830f86bfda0ae8cc9c89a6ca8ae3a73772cd) chore: bump golang to 1.18.3 * [`dc21e30`](https://github.com/siderolabs/pkgs/commit/dc21e30a2f31effab56b6e32c785fd0644eb90d2) chore: bump kernel to 5.15.44

### Changes from siderolabs/tools
5 commits

* [`0d669dd`](https://github.com/siderolabs/tools/commit/0d669dd415a044e5279f36c468834848ed6447bf) feat: update Go 1.18.4 * [`26b32d5`](https://github.com/siderolabs/tools/commit/26b32d582f13a9ea3ab55558bb8b8c2500008da0) chore: bump openssl to 1.1.1q * [`d8015e7`](https://github.com/siderolabs/tools/commit/d8015e756d74def09cee0503da08186eeccecb9a) chore: bump curl to 7.84.0 * [`3ec03ed`](https://github.com/siderolabs/tools/commit/3ec03edef31e971f48cb3202667af2045bcc233f) chore: bump openssl to 1.1.1p * [`3df9e13`](https://github.com/siderolabs/tools/commit/3df9e13ab89600655f5371adf254d66dda36ef02) chore: bump golang to 1.18.3

### Changes from talos-systems/crypto
1 commit

* [`e9df1b8`](https://github.com/talos-systems/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs

### Changes from talos-systems/go-blockdevice
2 commits

* [`74ea471`](https://github.com/talos-systems/go-blockdevice/commit/74ea47109c4525bec139640fed6354ad3097f5fb) feat: add freebsd stubs * [`9fa801c`](https://github.com/talos-systems/go-blockdevice/commit/9fa801cf4da184e3560b9a18ba43d13316f172f9) feat: add ReadOnly attribute to Disk

### Changes from talos-systems/grpc-proxy
1 commit

* [`6dfa2cc`](https://github.com/talos-systems/grpc-proxy/commit/6dfa2cc80b6195844cae2dc2b2bc0b9b62246d8d) fix: ignore errors on duplicate `SetHeader` calls

### Dependency Changes * **cloud.google.com/go/compute** v1.6.1 -> v1.7.0 * **github.com/BurntSushi/toml** v1.1.0 -> v1.2.0 * **github.com/aws/aws-sdk-go** v1.44.24 -> v1.44.61 * **github.com/containerd/containerd** v1.6.4 -> v1.6.6 * **github.com/containernetworking/cni** v1.1.0 -> v1.1.1 * **github.com/cosi-project/runtime** 95d06feaf8b5 -> 22c6aa1ca7ec * **github.com/docker/docker** v20.10.16 -> v20.10.17 * **github.com/emicklei/dot** v0.16.0 -> v1.0.0 * **github.com/google/gopacket** v1.1.19 **_new_** * **github.com/google/nftables** a9775fb167d2 -> a346d51f53b3 * **github.com/hashicorp/go-getter** v1.6.1 -> v1.6.2 * **github.com/hashicorp/go-version** v1.5.0 -> v1.6.0 * **github.com/hetznercloud/hcloud-go** v1.33.2 -> v1.35.1 * **github.com/martinlindhe/base36** v1.1.1 **_new_** * **github.com/packethost/packngo** v0.24.0 -> v0.25.0 * **github.com/rivo/tview** 9994674d60a8 -> 73bf2902b59a * **github.com/siderolabs/extras** v1.1.0-1-g5800284 -> v1.2.0-alpha.0-1-g17a319f * **github.com/siderolabs/pkgs** v1.1.0-8-gfa9a488 -> v1.2.0-alpha.0-23-gdcc0311 * **github.com/siderolabs/tools** v1.1.0-1-g134974c -> v1.2.0-alpha.0-4-g0d669dd * **github.com/spf13/cobra** v1.4.0 -> v1.5.0 * **github.com/stretchr/testify** v1.7.1 -> v1.8.0 * **github.com/talos-systems/crypto** v0.3.5 -> e9df1b8ca74c * **github.com/talos-systems/go-blockdevice** v0.3.2 -> v0.3.4 * **github.com/talos-systems/grpc-proxy** v0.3.0 -> v0.3.1 * **github.com/vishvananda/netlink** v1.2.0-beta -> v1.2.1-beta.2 * **github.com/vmware-tanzu/sonobuoy** v0.56.6 -> v0.56.8 * **github.com/vmware/govmomi** v0.28.0 -> v0.29.0 * **golang.org/x/net** 5463443f8c37 -> a158d28d115b * **golang.org/x/sync** 0976fa681c29 -> 886fb9371eb4 * **golang.org/x/sys** bc2c85ada10a -> 8c9f86f7a55f * **golang.org/x/term** 065cf7ba2467 -> a9ba230a4035 * **golang.org/x/time** 583f2d630306 -> e5dcc9cfc0b9 * **google.golang.org/grpc** v1.46.2 -> v1.48.0 * **gopkg.in/yaml.v3** 496545a6307b -> v3.0.1 * **inet.af/netaddr** c74959edd3b6 -> 097006376321 * **k8s.io/api** v0.24.2 -> v0.24.3 * **k8s.io/apiserver** v0.24.2 -> v0.24.3 * **k8s.io/client-go** v0.24.2 -> v0.24.3 * **k8s.io/component-base** v0.24.2 -> v0.24.3 * **k8s.io/kubectl** v0.24.2 -> v0.24.3 * **k8s.io/kubelet** v0.24.2 -> v0.24.3 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.64 -> v1.2.65 Previous release can be found at [v1.1.0](https://github.com/siderolabs/talos/releases/tag/v1.1.0) ## [Talos 1.2.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.2.0-alpha.0) (2022-06-30) Welcome to the v1.2.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Network bridge support Talos now supports configuring Linux bridges. It can be configured in the machine config like the following: ```yaml spec: machine: network: interfaces: - interface: br0 bridge: stp: enabled: true interfaces: - eth0 - eth1 ``` See [documentation](https://www.talos.dev/v1.1/reference/configuration/#bridge) for more details. ### Variable substitution for URL query parameter in the talos.config kernel parameter The kernel parameter talos.config can now substitute system information into placeholders inside its URL query values. This example shows all supported variables: ```http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}``` ### Component Updates * Linux: 5.15.51 ### Contributors * Noel Georgi * Andrey Smirnov * Utku Ozdemir * Philipp Sauter * Tim Jones * Dmitriy Matrenichev * Davincible * Han Cen * Rio Kierkels * RyanSquared * Serge Logvinov * Spencer Smith * hobyte * nett_hier ### Changes
61 commits

* [`643e81cfe`](https://github.com/siderolabs/talos/commit/643e81cfed675a018ec3af20b74fdcfcdc665d60) feat: add SenseLabs to ADOPTERS.md * [`bdfee2b3b`](https://github.com/siderolabs/talos/commit/bdfee2b3b7bf773326bd839ea6049e0262382071) chore: bump kernel to 5.15.51 * [`36c44a651`](https://github.com/siderolabs/talos/commit/36c44a65110713274fbb4b6638a36f3377c96bb3) fix: provide CA certificates in `/etc/ssl/certs/ca-certificates.crt` * [`7ebd9bcce`](https://github.com/siderolabs/talos/commit/7ebd9bcce6f2b1e6a030e551f41f590996294573) docs: fix pod security talos resource name * [`57b625e0a`](https://github.com/siderolabs/talos/commit/57b625e0a68534fdb3847e1fff5c18906630d1da) refactor: avoid recreating grpc clients in service health checks * [`a68a00f1b`](https://github.com/siderolabs/talos/commit/a68a00f1b9bdbb519966bbf1ac7f463796a85abb) docs: recommend setting "host" Processor Type on proxmox * [`923600a73`](https://github.com/siderolabs/talos/commit/923600a73c7368fd9217680fdfcc0dc2e9dc4b8e) chore: bump kernel to 5.15.50 * [`758a9bf59`](https://github.com/siderolabs/talos/commit/758a9bf59fad2f49ca03937c7d1bcfb3c13a9a0b) docs: add theila ui * [`b81016e62`](https://github.com/siderolabs/talos/commit/b81016e628642d93fb0b123f7146558279e0c648) chore: update blockdevice library to v0.3.3 * [`284a2f959`](https://github.com/siderolabs/talos/commit/284a2f9596ce899236d78d43c6d42a287f60540a) fix: filter static pods correctly and optimize fetching * [`61abf3111`](https://github.com/siderolabs/talos/commit/61abf311109f2d604d227bb6d290ccaeca19b3f0) docs: change command for cluster create to keep $HOME with sudo * [`6ae1e9bf2`](https://github.com/siderolabs/talos/commit/6ae1e9bf2ba7778dbe8a0919cbdf81fbe74b8e8c) chore: bump dependencies * [`2deff6b6e`](https://github.com/siderolabs/talos/commit/2deff6b6e148d99e9c88159f4895594417cdf080) feat: add support for variable substitution in talos.config kernel parameter * [`103c94225`](https://github.com/siderolabs/talos/commit/103c942256e7832b18e973f3fd698d7e94818c6f) fix: update crypto library with support for RSA-SHA* * [`448de7194`](https://github.com/siderolabs/talos/commit/448de7194911b3f8bd79cec3a3e93515ffd2e0a9) docs: add UpCloud installation guide * [`07014e0a8`](https://github.com/siderolabs/talos/commit/07014e0a8ee291ab4f2848787fc7462676c11fec) fix: generate correct bootstrap manifests when only IPv6 CIDR is used * [`465edbb47`](https://github.com/siderolabs/talos/commit/465edbb4791315d8709daeeba19f14b3e53680f3) fix: look for qemu-kvm binary * [`63caa281a`](https://github.com/siderolabs/talos/commit/63caa281ae8b83add1b070014282a6f792843845) fix: create native image format for DigitalOcean * [`f15ce549e`](https://github.com/siderolabs/talos/commit/f15ce549e940e6a0a95b8f78a4d7ad967f0a3900) fix: siderlink api assume port 443 with https schema * [`797596229`](https://github.com/siderolabs/talos/commit/797596229a7c4a883810c4229492cdfd0b441f19) feat: add support for configuring network bridges * [`2b23fabcc`](https://github.com/siderolabs/talos/commit/2b23fabcc1c3f5f495ea4d7fa6597fa639d4ce82) docs: use SVG image for K8s conformance * [`d4606c33e`](https://github.com/siderolabs/talos/commit/d4606c33ec36563d29b5ac95f11d1479c61a1905) chore: bump kernel to 5.15.49 * [`cfb640222`](https://github.com/siderolabs/talos/commit/cfb640222b80e1a2a6c3a8a505c5f6acfb148d24) docs: update docs for release 1.1 * [`b816d0b60`](https://github.com/siderolabs/talos/commit/b816d0b60077e83028b950a544c810d0875be268) docs: fix the vendor information for Kubernetes conformance tests * [`a167a5402`](https://github.com/siderolabs/talos/commit/a167a54021c979a1ca761674d8e368d5fb7dda6a) test: fix CLI nodes discovery without provisioner data * [`916a30682`](https://github.com/siderolabs/talos/commit/916a306829190c8eccbb993cfc166aa3cf08042e) docs: add twitter meta info * [`80090a3ed`](https://github.com/siderolabs/talos/commit/80090a3eda00e9808b0ba15241ea36dc6835f6d1) test: fix health endpoint cli test when discovery is disabled * [`3c263bb44`](https://github.com/siderolabs/talos/commit/3c263bb44639edf456d1c6203f41c71fa4d6d1d0) chore: bump dependencies * [`e8113527f`](https://github.com/siderolabs/talos/commit/e8113527f94f0fbc6cf6fdb9390dfb09d984213d) chore: bump kubernetes to v1.24.2 * [`068f1b6d0`](https://github.com/siderolabs/talos/commit/068f1b6d0517f62d2a76c7b1a761f15104220644) feat: add ctest package and base for test suite * [`2aad3a1e4`](https://github.com/siderolabs/talos/commit/2aad3a1e4911ebcd3eb970f09baa74e10383a959) chore: bump kernel to 5.15.48 * [`a31a858e0`](https://github.com/siderolabs/talos/commit/a31a858e08a7e022dc26c729ef097b6ed56a83ad) docs: snippets for logging api server audit logs * [`89aaaef9f`](https://github.com/siderolabs/talos/commit/89aaaef9f5dd403919535fc3e81ef635d233c0da) chore: bump kernel to 5.15.47 * [`6759fcd4a`](https://github.com/siderolabs/talos/commit/6759fcd4aeeca74e78e346b4265e86580991d800) feat: use discovery service on cluster health checks * [`f54d90787`](https://github.com/siderolabs/talos/commit/f54d9078719a62bcefcab367957f166e7a43decc) fix: enable orderly poweroff in hyper-v on Azure * [`35475ce45`](https://github.com/siderolabs/talos/commit/35475ce45b1ad64bb34149be9960f5acdd2bfe86) docs: openebs jiva example with iscsi-tools extension * [`8d2be5e31`](https://github.com/siderolabs/talos/commit/8d2be5e315fb05002587570d759322c9c00ad525) feat: extend node definition used in health checks * [`7a11b4def`](https://github.com/siderolabs/talos/commit/7a11b4def78e5b4506611fe85d083a12b695bd05) fix: make `talosctl bootstrap` accept only single node * [`217fba288`](https://github.com/siderolabs/talos/commit/217fba288f07ccf7053e804c226a2e0b9301f864) test: fix csi tests * [`90bf34fed`](https://github.com/siderolabs/talos/commit/90bf34fed98cb9ff524097da4043d4ff221a0b20) docs: fork docs for Talos 1.2 * [`a0dd010a8`](https://github.com/siderolabs/talos/commit/a0dd010a87b0ef0350299db3944f3a941fca09b4) docs: add link to discovery service in kubespan * [`c0371410e`](https://github.com/siderolabs/talos/commit/c0371410ee93f9773938b5b73be6eba246fd8f47) fix: support SideroLink "secure" gRPC connection * [`b03709620`](https://github.com/siderolabs/talos/commit/b03709620201b44f6464a7df804e2003c9751a30) feat: build Talos images with system extensions included * [`43def7490`](https://github.com/siderolabs/talos/commit/43def7490ffa598ba973f35903eaea462db374b1) chore: bump kernel and runc * [`4dbbf4ac5`](https://github.com/siderolabs/talos/commit/4dbbf4ac50f6b1ccd62efb1c06c8a92d8f91e65c) chore: add generic methods and use them part #2 * [`7114292b6`](https://github.com/siderolabs/talos/commit/7114292b6cd5f93a51b905db6377ffdadf429f19) docs: fix latest release version in docs * [`da2985fe1`](https://github.com/siderolabs/talos/commit/da2985fe1b29abac46b761a5ec2f4557d12ce985) fix: respect local API server port * [`e03266667`](https://github.com/siderolabs/talos/commit/e03266667f11d751f16a7208e774996ebadf8842) fix: correctly validate reboot mode in CLI * [`70fc42409`](https://github.com/siderolabs/talos/commit/70fc42409980a1a78b98a962284460ea18c42513) chore: add generic methods and use them * [`3ae8bdd92`](https://github.com/siderolabs/talos/commit/3ae8bdd92e43c8a5fedd455d4479678ccb263a6b) chore: run `xfs_repair` on xfs filesystem returing `EUCLEAN` * [`0c91c89f4`](https://github.com/siderolabs/talos/commit/0c91c89f4f0732147f5b6c41fb4f3da8437ae9f1) chore: revert day-two tests for csi tests * [`f71b58312`](https://github.com/siderolabs/talos/commit/f71b58312251ec2924607fb5166afa6c8aaf01bb) feat: disallow anonymous requests by default (kube-apiserver) * [`c19dd1b89`](https://github.com/siderolabs/talos/commit/c19dd1b8925fc8ec25a721d336ad0b363fc27fd4) feat: add 'etcd members should be control plane nodes' health check * [`f2997c0f2`](https://github.com/siderolabs/talos/commit/f2997c0f22b93382bfb61ff556961de56445807f) chore: bump dependencies * [`f3efec4b5`](https://github.com/siderolabs/talos/commit/f3efec4b56bc72dc5c769a76f6254d14d3f20b1b) feat: update containerd 1.6.6, Linux 5.15.45, Flannel 0.18.1 * [`27f8e50ce`](https://github.com/siderolabs/talos/commit/27f8e50ce90c47f5ddc82645e0ebcdb1a8ed778b) fix: add ovmf image path for rhel * [`87e7de30c`](https://github.com/siderolabs/talos/commit/87e7de30cb6ed02991cb46e25d20343555cc6317) docs: fix required ports * [`c126f2ee8`](https://github.com/siderolabs/talos/commit/c126f2ee85572bdfde61f9a3ba878f0595c74cfe) chore: bump golang to 1.18.3 * [`c1aed6240`](https://github.com/siderolabs/talos/commit/c1aed62405dddb2cbd2d47d699aae0c94df70886) fix: wait for `/var` to be mounted in kubelet service controller * [`d7a64f5d2`](https://github.com/siderolabs/talos/commit/d7a64f5d2a6ff9dccdf3bdb948684d9513912be9) fix: improve vip operator shutdown sequence * [`7b9dfcb85`](https://github.com/siderolabs/talos/commit/7b9dfcb852af6a48f00ddfca7337a571aa56a2b3) chore: add 'make go-mod-outdated'

### Changes from siderolabs/extras
1 commit

* [`892407f`](https://github.com/siderolabs/extras/commit/892407fd7c1a032ec4d7de5d52595ef3bcc7b484) chore: bump golang to 1.18.3

### Changes from siderolabs/pkgs
18 commits

* [`4412db8`](https://github.com/siderolabs/pkgs/commit/4412db88987acf91ffc2a83c09dd0dd5d84819eb) chore: bump kernel to 5.15.51 * [`6fedbdc`](https://github.com/siderolabs/pkgs/commit/6fedbdc826cffe5b0740c43f5641218df58e767c) chore: bump tools * [`f1f44e6`](https://github.com/siderolabs/pkgs/commit/f1f44e6a254e571c34f667e086e50afec099500b) chore: bump kernel to 5.15.50 * [`388af5e`](https://github.com/siderolabs/pkgs/commit/388af5e4eea16e0b19ce58879ea9d79676b9608b) chore: bump openssl to 1.1.1p * [`ed75c50`](https://github.com/siderolabs/pkgs/commit/ed75c5011e29107a59ffca1bececee1d22937ba2) chore: enable `RANDOM_TRUST_BOOTLOADER` by default * [`7c243f6`](https://github.com/siderolabs/pkgs/commit/7c243f6da5b3c0476106ff47d37b5d7a8ad28d98) chore: bump kernel to 5.15.49 * [`6e1269e`](https://github.com/siderolabs/pkgs/commit/6e1269e67f1e8a81cccf6ed45980595f2d6343f5) chore: bump kernel to 5.15.48 * [`5d671a3`](https://github.com/siderolabs/pkgs/commit/5d671a3cd6ebed495022f23e0073c1f971477305) chore: bump nvidia drivers to 515.48.07 * [`b35d835`](https://github.com/siderolabs/pkgs/commit/b35d835a1cfc3215d631f8ace3d3b1b7c83da008) chore: bump kernel to 5.15.47 * [`6604d6b`](https://github.com/siderolabs/pkgs/commit/6604d6b0686ea36983119edd7fb70755d3a812e0) feat: hyperv arm64 * [`c474058`](https://github.com/siderolabs/pkgs/commit/c4740588733138df9503c37304d1460166a3e233) chore: bump nvidia driver to 515.43.04 * [`5bc7e34`](https://github.com/siderolabs/pkgs/commit/5bc7e341fa0c035bbecc999bca3811b853684c5f) feat: update runc to 1.1.3, libseccomp to 2.5.4 * [`c02cd7a`](https://github.com/siderolabs/pkgs/commit/c02cd7a7086098698d1edd1d5ecb024ad9456a48) chore: bump kernel to 5.15.46 * [`b9c72a5`](https://github.com/siderolabs/pkgs/commit/b9c72a59cd6077ceb0ce53f11241d294c137f68b) feat: update containerd to 1.6.6 * [`f7786a3`](https://github.com/siderolabs/pkgs/commit/f7786a3a74bbf79c81cbcb031c357eae0e07726f) chore: bump kernel to 5.15.45 * [`b1c207d`](https://github.com/siderolabs/pkgs/commit/b1c207d63b1cac99b90025d530c57da4f51fc652) feat: update containerd to 1.6.5 * [`4d47830`](https://github.com/siderolabs/pkgs/commit/4d47830f86bfda0ae8cc9c89a6ca8ae3a73772cd) chore: bump golang to 1.18.3 * [`dc21e30`](https://github.com/siderolabs/pkgs/commit/dc21e30a2f31effab56b6e32c785fd0644eb90d2) chore: bump kernel to 5.15.44

### Changes from siderolabs/tools
1 commit

* [`3df9e13`](https://github.com/siderolabs/tools/commit/3df9e13ab89600655f5371adf254d66dda36ef02) chore: bump golang to 1.18.3

### Changes from talos-systems/crypto
1 commit

* [`e9df1b8`](https://github.com/talos-systems/crypto/commit/e9df1b8ca74c6efdc7f72191e5d2613830162fd5) feat: add support for generating keys from RSA-SHA256 CAs

### Changes from talos-systems/grpc-proxy
1 commit

* [`6dfa2cc`](https://github.com/talos-systems/grpc-proxy/commit/6dfa2cc80b6195844cae2dc2b2bc0b9b62246d8d) fix: ignore errors on duplicate `SetHeader` calls

### Dependency Changes * **cloud.google.com/go/compute** v1.6.1 -> v1.7.0 * **github.com/aws/aws-sdk-go** v1.44.24 -> v1.44.42 * **github.com/containerd/containerd** v1.6.4 -> v1.6.6 * **github.com/containernetworking/cni** v1.1.0 -> v1.1.1 * **github.com/cosi-project/runtime** 95d06feaf8b5 -> ee09cee2aab7 * **github.com/docker/docker** v20.10.16 -> v20.10.17 * **github.com/emicklei/dot** v0.16.0 -> v1.0.0 * **github.com/google/nftables** a9775fb167d2 -> a346d51f53b3 * **github.com/hashicorp/go-getter** v1.6.1 -> v1.6.2 * **github.com/hetznercloud/hcloud-go** v1.33.2 -> v1.34.0 * **github.com/packethost/packngo** v0.24.0 -> v0.25.0 * **github.com/rivo/tview** 9994674d60a8 -> 691f46d6f500 * **github.com/siderolabs/extras** v1.1.0-1-g5800284 -> v1.2.0-alpha.0 * **github.com/siderolabs/pkgs** v1.1.0-8-gfa9a488 -> v1.2.0-alpha.0-17-g4412db8 * **github.com/siderolabs/tools** v1.1.0-1-g134974c -> v1.2.0-alpha.0 * **github.com/spf13/cobra** v1.4.0 -> v1.5.0 * **github.com/stretchr/testify** v1.7.1 -> v1.7.5 * **github.com/talos-systems/crypto** v0.3.5 -> e9df1b8ca74c * **github.com/talos-systems/grpc-proxy** v0.3.0 -> v0.3.1 * **github.com/vishvananda/netlink** v1.2.0-beta -> v1.2.1-beta.2 * **github.com/vmware-tanzu/sonobuoy** v0.56.6 -> v0.56.7 * **golang.org/x/net** 5463443f8c37 -> 1bab6f366d9e * **golang.org/x/sync** 0976fa681c29 -> 0de741cfad7f * **golang.org/x/sys** bc2c85ada10a -> 87e55d714810 * **golang.org/x/time** 583f2d630306 -> 579cf78fd858 * **google.golang.org/grpc** v1.46.2 -> v1.47.0 * **gopkg.in/yaml.v3** 496545a6307b -> v3.0.1 * **inet.af/netaddr** c74959edd3b6 -> 097006376321 Previous release can be found at [v1.1.0](https://github.com/siderolabs/talos/releases/tag/v1.1.0) ## [Talos 1.1.0-alpha.2](https://github.com/siderolabs/talos/releases/tag/v1.1.0-alpha.2) (2022-05-12) Welcome to the v1.1.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Apply Config `--dry-run` The commands `talosctl apply-config`, `talosctl patch mc` and `talosctl edit mc` now support `--dry-run` flag. If enabled it just prints out the selected config application mode and the configuration diff. ### Apply Config `--mode=try` The commands `talosctl apply-config`, `talosctl patch mc` and `talosctl edit mc` now support the new mode called `try`. In this mode the config change is applied for a period of time and then reverted back to the state it was before the change. `--timeout` parameter can be used to customize the config rollback timeout. This new mode can be used only with the parts of the config that can be changed without a reboot and can help to check that the new configuration doesn't break the node. Can be especially useful to check network interfaces changes that may lead to the loss of connectivity to the node. ### IPv6 in Docker-based Talos Clusters The command `talosctl cluster create` now enables IPv6 by default for the Docker containers created for Talos nodes. This allows to use IPv6 addresses in Kubernetes networking. If `talosctl cluster create` fails to work on Linux due to the lack of IPv6 support, please use the flag `--disable-docker-ipv6` to revert the change. ### drop some default rules shipped by eudev Drops some default eudev rules that doesn't make sense in the context of Talos OS. Especially the ones around sound devices, cd-roms and renaming the network interfaces to be predictable ### Pod Security Admission [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) controller is enabled by default with the following policy: ```yaml apiVersion: apiserver.config.k8s.io/v1 kind: AdmissionConfiguration plugins: - configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration name: PodSecurity path: "" ``` The policy is part of the Talos machine configuration, and it can be modified to suite your needs. ### Support RockPi 4 variants A and B Talos now supports RockPi variants A and B in addition to RockPi 4C ### Raspberry Pi PoE hat fan Talos now enables the Raspberry Pi PoE fan control by pulling in the poe overlay that works with upstream kernel ### Component Updates * Linux: 5.15.39 * Containerd: v1.6.4 * Kubernetes: 1.24.0 * Flannel: 0.17.0 * runc: 1.1.2 * CoreDNS: v1.9.2 Talos is built with Go 1.18.2 ### x86-64 Architecture Talos is built for x86-64 architecture with support for [x86-64-v2 microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels), so Talos no longer runs on processors supporting only baseline `x86-64` microarchitecture (before 2009). ### Contributors * Andrey Smirnov * Noel Georgi * Tim Jones * Dmitriy Matrenichev * Spencer Smith * Artem Chernyshev * Andrew Rynhard * Serge Logvinov * Steve Francis * Philipp Sauter * Steve Francis * Andrei Dobre * Bastiaan Schaap * Caleb Woodbine * Daniel Höxtermann * Jori Huisman * Jorik Jonker * Nico Berlee * Philipp Sauter * Sebastian Hasler * Seán C McCord * Suraj Shirvankar * Tames McTigue * Tim Jones * Tomasz Zurkowski * William Ashton ### Changes
211 commits

* [`91a49c4e7`](https://github.com/siderolabs/talos/commit/91a49c4e7c0495794a991ab7742cd06339ce072d) fix: dhcpv6 leasetime segfault loop * [`afb679586`](https://github.com/siderolabs/talos/commit/afb679586a7319746e59071b6ce0b85df0d77f48) fix: reset certificate SANs on update * [`c87432fe1`](https://github.com/siderolabs/talos/commit/c87432fe1feab8a157c858cf04aefe40c9f84895) fix: detect WSL for `talosctl cluster create` on Docker * [`166d2585c`](https://github.com/siderolabs/talos/commit/166d2585c6d94791d8a3392fc597cd9c489c1958) chore: bump kernel and runc * [`8d9b0cde0`](https://github.com/siderolabs/talos/commit/8d9b0cde0ab51f16421e3db23d301349626b49d8) chore: update deps to go 1.18.2 * [`86741d998`](https://github.com/siderolabs/talos/commit/86741d99845ab36218417bd1e00f446bfe71b922) fix: append hostname to cluster SANs when port is not specified * [`9885bbe17`](https://github.com/siderolabs/talos/commit/9885bbe17762af22d704b6c4e33ef99032e85956) docs: fix typos, edited for clarity * [`7fd1c80c3`](https://github.com/siderolabs/talos/commit/7fd1c80c3ee2ea33fe1aeb2e6cb21a3d0bc98537) fix: ignore failures to dial wireguard client * [`b8e7cdb70`](https://github.com/siderolabs/talos/commit/b8e7cdb7012c89f980dc4c2ac29d6a503c55206a) docs: add vultr guide * [`c2be65b66`](https://github.com/siderolabs/talos/commit/c2be65b6684b208a8ac2e1b598e005b72dd24b8f) fix: openstack unable to parseIP * [`2ae0e3a56`](https://github.com/siderolabs/talos/commit/2ae0e3a56971ece0be47b741ddbf5f652b7244f1) test: add a test for version of Go Talos was built with * [`bef1a13fa`](https://github.com/siderolabs/talos/commit/bef1a13fa2968dd483a45d766ca7fe1de5aedace) docs: update latest Talos in 1.0 docs to 1.0.4 * [`b52962c44`](https://github.com/siderolabs/talos/commit/b52962c44075a34399a4ceb0210b733df812775f) chore: bump dependencies * [`79ae76a6f`](https://github.com/siderolabs/talos/commit/79ae76a6f220683b318e2df6af7326e20c255a79) fix: properly set `allowSchedulingOnMasters` in the interactive install * [`2b7e7d4dc`](https://github.com/siderolabs/talos/commit/2b7e7d4dc49189dd18b1bb5646a1c621d27d82df) feat: print the status of services during boot * [`802d4a23c`](https://github.com/siderolabs/talos/commit/802d4a23c9c6634686136eb80bf86336c5dc6084) fix: load kubelet system service in StartAllServices task * [`67019c434`](https://github.com/siderolabs/talos/commit/67019c434b7e4b20f4986433340bc626b30d16bc) fix: add source path for ovmf flash image * [`da7b24ba5`](https://github.com/siderolabs/talos/commit/da7b24ba57fcfb589bb34f1947b3744c9953bb6b) chore: bump kernel to 5.15.38 * [`8ca8effd6`](https://github.com/siderolabs/talos/commit/8ca8effd6165fb39120c6b30266f0b4ee3b06bfd) chore: add Equinix Managed Services NL to adopters * [`8bc97a30f`](https://github.com/siderolabs/talos/commit/8bc97a30f239ed5b2bbb24e620ffa7bd31a0ebf9) fix: remove D-Bus sockets before listen attempts * [`54cfa039a`](https://github.com/siderolabs/talos/commit/54cfa039ab8196b3d8eec9b69aa723ad90e3054b) fix: use json.Unmarshal instead of yaml.Unmarshal * [`6d30c4822`](https://github.com/siderolabs/talos/commit/6d30c48223022a369556e5a678a78bbab2316e67) docs: update networking resource documentation * [`bc88de729`](https://github.com/siderolabs/talos/commit/bc88de729c69683ac74e3312b154a722b2100a44) chore: bump coredns to v1.9.2 * [`c6722b637`](https://github.com/siderolabs/talos/commit/c6722b637b2f39ecc83f32a1f61a6591d6a2aff6) docs: when mentioning partitions, link to partition docs * [`b189e8426`](https://github.com/siderolabs/talos/commit/b189e84269bdc14b041d7f225545b7c9ee948dbc) chore: fix incorrect ManifestSpec.MarshalYAML signature * [`5d5280200`](https://github.com/siderolabs/talos/commit/5d52802001d4fef74fe2d47e436bfd52dda7617b) feat: add more hardware information to the link status resources * [`2ff6db749`](https://github.com/siderolabs/talos/commit/2ff6db749af4d38959f0adc113e1d406234b05ea) chore: add Nedap Security Atlas as adopter * [`89cab200b`](https://github.com/siderolabs/talos/commit/89cab200b8515a6bbc62659164da61e8913860b1) chore: bump kubernetes to v1.24.0 * [`09d16349f`](https://github.com/siderolabs/talos/commit/09d16349f4e10dfe2f32eaaa824ecf7b9e078074) chore: refactor StaticPod and StaticPodStatus into typed.Resource * [`d2935f98c`](https://github.com/siderolabs/talos/commit/d2935f98c4f9ae99bc8e1c0f63a4b0825a2678f6) chore: refactor LinkRefresh and LinkStatus into typed.Resource * [`b52e0b9b9`](https://github.com/siderolabs/talos/commit/b52e0b9b9ecb84a7b73771a80f3425af4814ada0) fix: talosctl throws error if gen option and --input-dir flags are combined * [`0e15de3a8`](https://github.com/siderolabs/talos/commit/0e15de3a8a4057866e5e51c99936fad1c629098f) docs: add adopters file * [`bb932c297`](https://github.com/siderolabs/talos/commit/bb932c29706c1eb77088d734c65857cd63035031) chore: bump containerd to v1.6.4 * [`4eaaa2d59`](https://github.com/siderolabs/talos/commit/4eaaa2d597d12d8ec4fa265499790d0891beac42) chore: bump kernel to 5.15.37 * [`89dde8f2c`](https://github.com/siderolabs/talos/commit/89dde8f2c426e2eb53de507b69f48bb8b6b5b7fe) chore: refactor remaining resources into typed.Resource * [`bd089e702`](https://github.com/siderolabs/talos/commit/bd089e702d247b2ee5f31bde542cf407e155e10b) chore: bump dependencies * [`3136334b9`](https://github.com/siderolabs/talos/commit/3136334b935e37e4a2f11bd4c02f89aee4806ddb) docs: fix links in VMware documentation * [`403df0e18`](https://github.com/siderolabs/talos/commit/403df0e1802186c409eb8cc255ca1233898a5eda) docs: provide example on using config generation package * [`635192861`](https://github.com/siderolabs/talos/commit/63519286110e75adc03edb89b4dc9cb432b15d46) chore: redo pointer with github.com/siderolabs/go-pointer module * [`a269f740c`](https://github.com/siderolabs/talos/commit/a269f740ce323b21e532b24aa327b1f4b4b1e951) docs: copy knowledge base to v1.0 docs * [`483201026`](https://github.com/siderolabs/talos/commit/48320102630a6a551aaa05202181daad62757180) fix: return an error if there is no byte slice in ReadonlyProvider * [`6e7486f09`](https://github.com/siderolabs/talos/commit/6e7486f099026724c21a73a4d4ff40134ac864b1) fix: allow graceful node shutdown to be overridden * [`867d38f28`](https://github.com/siderolabs/talos/commit/867d38f28f3b65b373206528d18360b714331cec) feat: add bond slaves ordering * [`03ef62ad8`](https://github.com/siderolabs/talos/commit/03ef62ad8bf10eccacbd490ebbcaceb0794f5378) fix: include Go primitive types into unstructured deepcopy * [`f06e6acf2`](https://github.com/siderolabs/talos/commit/f06e6acf2f76c48a59654e53503ae82eb9a6532c) chore: bump kernel to 5.15.36 * [`c0d386abb`](https://github.com/siderolabs/talos/commit/c0d386abb6e427fa0da5ab416a9a3caf9ab1f1d6) fix: don't mount D-Bus socket via mount under recursive bind mount * [`9a8ff76df`](https://github.com/siderolabs/talos/commit/9a8ff76df2fa7366ccdeb597226285a77528442e) refactor: rewrite perf resource to use typed.Resource * [`71d04c4d5`](https://github.com/siderolabs/talos/commit/71d04c4d5cbaeadfc86b6eb5164f7f8026a9b66a) refactor: rewrite runtime resources to use typed.Resource * [`7568d51fc`](https://github.com/siderolabs/talos/commit/7568d51fc8e40c57cef3d86c77a662508ee3f0cd) fix: trigger CRI config merge on correct resource update * [`c456dbcb9`](https://github.com/siderolabs/talos/commit/c456dbcb934533e3026246635911ce44045dfb17) docs: remove references to init nodes * [`1973095d1`](https://github.com/siderolabs/talos/commit/1973095d146a9b10408f972904a06ed555524bd2) feat: update containerd to 1.6.3 * [`b51292d88`](https://github.com/siderolabs/talos/commit/b51292d884b420b30cd5eb8eba29286f72681b72) docs: reformat config reference * [`c0709d970`](https://github.com/siderolabs/talos/commit/c0709d97078f6574be3b6413a1513d91adeea26e) feat: increase aio-max-nr and inotify.max_user_instances * [`85b328e99`](https://github.com/siderolabs/talos/commit/85b328e99719e549346a39ffc4a782e993dc7696) refactor: convert secrets resources to use typed.Resource * [`e91350acd`](https://github.com/siderolabs/talos/commit/e91350acd727581478b852f171b69a67b4322999) refactor: convert time & v1alpha1 resources to use typed.Resource * [`45464412e`](https://github.com/siderolabs/talos/commit/45464412e0526195c3a7f3e447f8f5bee108407d) chore: bump dependencies * [`0af6b35a6`](https://github.com/siderolabs/talos/commit/0af6b35a66ae4145d678796d6b7fea2cf77ea9de) feat: update etcd to 3.5.4 * [`7ad27751c`](https://github.com/siderolabs/talos/commit/7ad27751cbd2a44290b7c5fa708129df5a912375) docs: fix analytics and sitemap * [`55ff876dc`](https://github.com/siderolabs/talos/commit/55ff876dc649f102cb6608efa6fb738d9ea69bba) chore: bump K8s Go modules to 1.24.0-rc.0 * [`f1f43131f`](https://github.com/siderolabs/talos/commit/f1f43131f8f1070240fc32cc96a7b1ccf78e3f76) fix: strip 'v' prefix from versions on Kubernetes upgrade * [`ec621477b`](https://github.com/siderolabs/talos/commit/ec621477bd089d1672bf5ea94378ffa397bee227) chore: tune QEMU disk provisioner options * [`b085343dc`](https://github.com/siderolabs/talos/commit/b085343dcb0bff77eb03a9754843b68c8f2f90af) feat: use discovery information for etcd join (and other etcd calls) * [`2b03057b9`](https://github.com/siderolabs/talos/commit/2b03057b91928f550e22b96885878c2369379e98) feat: implement a new mode `try` in the config manipulation commands * [`51a68c31f`](https://github.com/siderolabs/talos/commit/51a68c31ff6d9bb4dc96500c158ea9968680703a) chore: allow mounting files from the host * [`f3e330a0a`](https://github.com/siderolabs/talos/commit/f3e330a0aaf4611cd1ffc1d2abd90487132e16e9) docs: fix network dependency * [`7ba39bd60`](https://github.com/siderolabs/talos/commit/7ba39bd60052eb41f718d920fa916e5c9b80a036) docs: clarify discovery service * [`8057d076a`](https://github.com/siderolabs/talos/commit/8057d076ada80a19d26a7cd0d678c18ad14dab31) release(v1.1.0-alpha.1): prepare release * [`1d5c08e74`](https://github.com/siderolabs/talos/commit/1d5c08e74f2c9009ff2b3103157eb105e2a32254) chore: bump kernel to 5.15.35 * [`9bf23e516`](https://github.com/siderolabs/talos/commit/9bf23e5162bded75a8c52009a360de1a43060858) feat: update Kubernetes to 1.24.0-rc.0 * [`d78ed320b`](https://github.com/siderolabs/talos/commit/d78ed320b7c9853d5c118223f2289db153ea8145) docs: fix the docs reference to star registry redirects * [`257dfb870`](https://github.com/siderolabs/talos/commit/257dfb870933321175f859348539de6d26161618) fix: run the 'post' stage of the service always * [`992e23023`](https://github.com/siderolabs/talos/commit/992e2302346fb4e34a23d28f4c3a67564ddbb241) fix: correctly handle stopping services with reverse dependencies * [`bb7a50bd5`](https://github.com/siderolabs/talos/commit/bb7a50bd5b31d28cef6a250a056f81c2e1eace80) docs: fix netlify redirects * [`486f79bc7`](https://github.com/siderolabs/talos/commit/486f79bc775564f9fdd2a114b86b70d55324d18a) docs: fix netlify deploy url * [`e8cbedb05`](https://github.com/siderolabs/talos/commit/e8cbedb05bb19bdea339a806576215ae71eee4d8) docs: add canonical link ref * [`0fe4a7832`](https://github.com/siderolabs/talos/commit/0fe4a7832b1327e68d2829ae27078780434f00b3) docs: improve latest-version banner * [`23984efcd`](https://github.com/siderolabs/talos/commit/23984efcdf6ae530301c885c6105aa18d790d9b6) fix: detect lingering mounts in the installer correctly * [`54dba925f`](https://github.com/siderolabs/talos/commit/54dba925f88881f41246a9198955ac6ce95d81d9) chore: refactor network resource to use typed resource * [`4eb9f45cc`](https://github.com/siderolabs/talos/commit/4eb9f45cc82669ac31ffc17bc53a5be05563823e) refactor: split polymorphic K8sControlPlane into typed resources * [`68dfdd331`](https://github.com/siderolabs/talos/commit/68dfdd3311c602faaeb5e5f7970c0e7d13a32600) fix: provide logger to the etcd snapshot restore * [`f190403f0`](https://github.com/siderolabs/talos/commit/f190403f01118c7f60d5e97a4c2349c638ed7e0b) docs: add how to get config after interactive setup * [`fac7b9466`](https://github.com/siderolabs/talos/commit/fac7b94667bb9aae680677b5e3e936f107315062) docs: improve vip caveats documentation * [`250df9e67`](https://github.com/siderolabs/talos/commit/250df9e670c8e4221fa376791b88ee03fa2022ae) docs: improve rook-ceph description * [`b5c1d868d`](https://github.com/siderolabs/talos/commit/b5c1d868deac9fd8d124cda35693b4f12372589f) docs: add talos/kubernetes config faq * [`39721ee93`](https://github.com/siderolabs/talos/commit/39721ee9392ed43da572c71eb056a8a4b1a795fd) chore: bump dependencies * [`610945774`](https://github.com/siderolabs/talos/commit/610945774a8f9cf849ddfefda0e4d456bb8ba2c3) chore: bump tools and pkgs * [`2b68c8b67`](https://github.com/siderolabs/talos/commit/2b68c8b67bf1ea88d471b8baa405a65fcd1aa40f) fix: enable long timestamps for xfs * [`be00d7749`](https://github.com/siderolabs/talos/commit/be00d774921b28ebc9b81727a6e4cf29a06385ee) chore: implement cluster resources using cosi typed resource * [`460d5ab13`](https://github.com/siderolabs/talos/commit/460d5ab13f007a89e72013c443132a845dcc3a09) docs: fix extension services alias * [`bbdfda2dd`](https://github.com/siderolabs/talos/commit/bbdfda2dd2e72f1fd5981dd6fc589d90cd692b72) chore: xfs quota support in kernel * [`8ff8fc77f`](https://github.com/siderolabs/talos/commit/8ff8fc77f3b14679daa31067528f6bcf62e9aca9) chore: enable rpi4 poe hat fan control * [`2b9722d1f`](https://github.com/siderolabs/talos/commit/2b9722d1f5fac39390fde8223d40262af80b1ef2) feat: add `dry-run` flag in `apply-config` and `edit` commands * [`8af50fcd2`](https://github.com/siderolabs/talos/commit/8af50fcd27bed2a437d6d9668233657a47bd9798) fix: correct cri package import path * [`ce09ede83`](https://github.com/siderolabs/talos/commit/ce09ede839e7500df1dd862f8c2726b02798b725) feat: update etcd to 3.5.3 * [`13f41badd`](https://github.com/siderolabs/talos/commit/13f41baddff997dfa15c773d8f078bd0921fb40b) chore: bump kernel to 5.15.34 * [`fa57b5d92`](https://github.com/siderolabs/talos/commit/fa57b5d9225d3075b08a9d07ce29480a4c050143) docs: reorganize documentation * [`a91eb9358`](https://github.com/siderolabs/talos/commit/a91eb9358dfc49e2afc1523f804c0f01660cfb1f) chore: bump deps * [`0aad0df2e`](https://github.com/siderolabs/talos/commit/0aad0df2eb6a8727dfff253619a9b2cb1915d9be) refactor: remove `String()` for resource implementation * [`a4060513c`](https://github.com/siderolabs/talos/commit/a4060513c694f2d45be95a060e4bb719840d8739) feat: build Talos with support for x86-64-v2 microarchitecture * [`8faebd410`](https://github.com/siderolabs/talos/commit/8faebd410be9653808f50df698345ee613be6e68) chore: bump tools and pkgs * [`8499b7e7d`](https://github.com/siderolabs/talos/commit/8499b7e7dcbd5fbcb9aa94a8028a73168a304a06) chore: bump dependencies * [`a7ba7ea67`](https://github.com/siderolabs/talos/commit/a7ba7ea679f10e99b31ee3b4b6c92265d43c12df) feat: migrate to go 1.18 * [`9dace93b5`](https://github.com/siderolabs/talos/commit/9dace93b59e8e1e1d8a7595fda82dc85b9c835cf) feat: enable Pod Security Admission by default * [`c382cb8cd`](https://github.com/siderolabs/talos/commit/c382cb8cd26f2eaece665bcb471f27d188ea1ad5) docs: update vmware docs * [`da0e638f0`](https://github.com/siderolabs/talos/commit/da0e638f04cfab1ed93891231035439ad77666d1) docs: stableize tools versioning * [`f2d2267e7`](https://github.com/siderolabs/talos/commit/f2d2267e749a14b8a060e56f274f603415d69731) docs: use template for netlify redirects * [`88f1d8fcc`](https://github.com/siderolabs/talos/commit/88f1d8fcc0e3bd28a9db4677ad9d782c80ffdbb9) docs: update sitemap to point to direct url * [`a6eebee36`](https://github.com/siderolabs/talos/commit/a6eebee36f9a3f6fbde441ccb5e170dae9727a58) chore: update eudev * [`0cb84e8c1`](https://github.com/siderolabs/talos/commit/0cb84e8c1a09c5b391461aa17c277a0a7803f725) fix: correctly parse tags out of images * [`17d09739f`](https://github.com/siderolabs/talos/commit/17d09739f3fe8cb942008a44f902b65705e39575) docs: enable nested arrow * [`1e4320b64`](https://github.com/siderolabs/talos/commit/1e4320b64e2477a55f808c6b8720b0779088d0f8) chore: add support for rockpi 4A and 4B * [`d1869d948`](https://github.com/siderolabs/talos/commit/d1869d948c84cf7191819eddac9c2aa27b365eb9) docs: update to Sidero Metal, mention clusterctl * [`18d0038ec`](https://github.com/siderolabs/talos/commit/18d0038ecaa2cf43164f72f3acad5445e395b37e) fix: avoid panic in DHCPv6 operator on nil dereference * [`9e3d438db`](https://github.com/siderolabs/talos/commit/9e3d438db461529abf3dfa6ef750b4fa4a9125ec) docs: fix code fence formatting * [`b3f1bb2cf`](https://github.com/siderolabs/talos/commit/b3f1bb2cff544a35f767b32ca8ca1d13b83c535e) fix: add support for FAT12/16 filesystems * [`8619f95c5`](https://github.com/siderolabs/talos/commit/8619f95c5c7779815a87118cbb0a1e493251355d) chore: bump dependencies * [`8c4f72004`](https://github.com/siderolabs/talos/commit/8c4f720048c0187b203ca869befd759249bac79f) docs: override sitemap.xml to only include latest results * [`5192ba4e2`](https://github.com/siderolabs/talos/commit/5192ba4e2314c05e107adcc0a2a71a65ec35bfc3) docs: fix a typo in QEMU VM setup guide * [`663e3e879`](https://github.com/siderolabs/talos/commit/663e3e8796c3f501275fdd7836687b811318b685) refactor: change the stages for embed files generation * [`19bf12af0`](https://github.com/siderolabs/talos/commit/19bf12af07aaf6b54d08027676d8a01b4dd4ed29) fix: enable IPv6 in Docker-based Talos clusters * [`3889a5839`](https://github.com/siderolabs/talos/commit/3889a583970c73ea4c6089b1fe8438b183ec756e) docs: update config.yaml, storage.md, digital-rebar.md * [`25d19131d`](https://github.com/siderolabs/talos/commit/25d19131d378960603a510cb70b35352b07bf7cb) release(v1.1.0-alpha.0): prepare release * [`2ca5279e5`](https://github.com/siderolabs/talos/commit/2ca5279e56d154fdf21fab7ed5c73edb30494560) fix: retry manifest updates in upgrade-k8s * [`eeb756168`](https://github.com/siderolabs/talos/commit/eeb756168f31c8e7a1e0cb2f80e1ae2bc2eed0a9) feat: use kexec when resetting a node * [`1ed1f73e5`](https://github.com/siderolabs/talos/commit/1ed1f73e511f4a5cf4d1db5f97422cf1eb088fda) test: bump CAPI to 1.1.3 * [`2ee1d2c72`](https://github.com/siderolabs/talos/commit/2ee1d2c72085df41ec0355bac0d33bedcb4f2786) feat: update Kuberentes to 1.24.0-beta.0 * [`c26fa4ccc`](https://github.com/siderolabs/talos/commit/c26fa4ccc1e109c889c01384422f88387ad512a2) test: push GITHUB_TOKEN to the e2e-aws/gcp steps * [`95d900de7`](https://github.com/siderolabs/talos/commit/95d900de7799cfa9d0a16049586ba246bddb09d0) feat: use kubeconfig env var * [`0b407dd17`](https://github.com/siderolabs/talos/commit/0b407dd17e9515fecd8083fd5ac1fc84f6085106) feat: add dhcp-v6 NTP/DHCP-DUID * [`a140a6bad`](https://github.com/siderolabs/talos/commit/a140a6bad74bcf34e62e13b6efa63a17741eb5b1) docs: update releases shortcode in upgrade guide * [`12931dced`](https://github.com/siderolabs/talos/commit/12931dcedd38c407a2a03f692d910853130986db) fix: align partitions on 1M boundary * [`37f868e37`](https://github.com/siderolabs/talos/commit/37f868e37454f63a4dfe38d94dbbeef5bb40a2a8) fix: validate empty TLS config for registries * [`ca8b9c0a3`](https://github.com/siderolabs/talos/commit/ca8b9c0a3a15898d9562a6f22aded138d6c3ed7f) feat: update Kubernetes to 1.24.0-alpha.4 * [`d9ec6b215`](https://github.com/siderolabs/talos/commit/d9ec6b2151e94c94eea44771e455555eaf1f257a) chore: drop dirty from abbreviated tag * [`08624fd0b`](https://github.com/siderolabs/talos/commit/08624fd0b12039e5a77ce43f14df65a6c95f7a39) docs: add banner to main page * [`fc23c7a59`](https://github.com/siderolabs/talos/commit/fc23c7a5952d87a51f29d61ead585bf060eeab1c) test: bump versions for upgrade tests * [`4bfe68610`](https://github.com/siderolabs/talos/commit/4bfe686105d5734b282f4817673972b71954e620) feat: update runc to 1.1.1 * [`b315ed953`](https://github.com/siderolabs/talos/commit/b315ed95327a9b7cfb1f83a9da02e96bafecbb1d) chore: use go:embed instead of ldflags * [`a5d64fc81`](https://github.com/siderolabs/talos/commit/a5d64fc814f122fb7e282b97283a46ac0e5d6709) feat: update Flannel to 0.17.0 * [`6d6eb3f6a`](https://github.com/siderolabs/talos/commit/6d6eb3f6a52626c8c94a75439133e7bc22b25e60) docs: fork docs for 1.1 * [`1d55f05d1`](https://github.com/siderolabs/talos/commit/1d55f05d11e5a03a8de0e7ce5ec0167971b03135) docs: update index page * [`ad6b7ec1a`](https://github.com/siderolabs/talos/commit/ad6b7ec1a4347753488de3ab5813947f01967078) fix: enable etcd consistency on check startup * [`65a31f753`](https://github.com/siderolabs/talos/commit/65a31f7531a629b29fbf86ddcbaba20767475924) docs: re-add GA token * [`741c04832`](https://github.com/siderolabs/talos/commit/741c048320b931228336034ad17de10272ff5a77) docs: mark 1.0 docs as latest * [`e97433c8a`](https://github.com/siderolabs/talos/commit/e97433c8a37ca504577355d98c917e083aaedafe) docs: update jetson nano * [`6665e0f00`](https://github.com/siderolabs/talos/commit/6665e0f00c1c5d45123eb28d8755d0815af4822a) docs: code block copying * [`c41f2b216`](https://github.com/siderolabs/talos/commit/c41f2b216717db80e44654f54080a9d462946d45) docs: update whats-new-v1.0 * [`0a36fbbf3`](https://github.com/siderolabs/talos/commit/0a36fbbf3ca579becd0a7f2e5a9715ff4196e8ae) docs: add release notes for 1.0 * [`bd0035f6a`](https://github.com/siderolabs/talos/commit/bd0035f6a285f8b7e4c7c0b5013a271a8d18c5f4) docs: add NVIDIA docs * [`efa3f2898`](https://github.com/siderolabs/talos/commit/efa3f289853a47ae0d4bca5dbf656e527cf312dd) fix: correctly find partitions with config data (`metal-iso`) * [`9ebeec0d0`](https://github.com/siderolabs/talos/commit/9ebeec0d0ea4dd3cc1ba3b7171fe0a9bda943fe8) docs: fix incorrect path for talosconfig * [`9fef4540e`](https://github.com/siderolabs/talos/commit/9fef4540e1c7a7deb5d4745d3de17c6e5cc45369) docs: fix non-latest download links * [`f8ef6a081`](https://github.com/siderolabs/talos/commit/f8ef6a081e055637a5652366a6e344b6df911871) docs: add rook ceph configuration guide * [`e2666f58f`](https://github.com/siderolabs/talos/commit/e2666f58f5835db6ff8802b2370a480d8afcd8fc) chore: bump kernel to 5.15.32 * [`957b2f233`](https://github.com/siderolabs/talos/commit/957b2f233c4b81eacdb5a3190c0070fa36ef0d82) chore: bump dependencies * [`0fd2aa08b`](https://github.com/siderolabs/talos/commit/0fd2aa08bd70d1c869e0dca136ca0c487bfcdefe) fix: correctly escape '.' in volume names * [`108fd03a7`](https://github.com/siderolabs/talos/commit/108fd03a72534cebbab7c09d63051021483566ac) fix: give up virtual IPs before the kubelet workloads are shut down * [`856e1333d`](https://github.com/siderolabs/talos/commit/856e1333dcfb8c0244ca8ead415025b32a4819fc) fix: use 'localhost' endpoint in docker provisioner on Windows * [`c5da38609`](https://github.com/siderolabs/talos/commit/c5da386092185fe4ed4173b08f95eac4e435ff99) docs: use variables and templates in the docs * [`4c83847b9`](https://github.com/siderolabs/talos/commit/4c83847b9091a4e8968544a515632a3391c06cd0) docs: target search results * [`67fb72d96`](https://github.com/siderolabs/talos/commit/67fb72d96db1cb772392dcab9b5a3a08ee50ff03) docs: add algolia versions to all content * [`5344d6e7c`](https://github.com/siderolabs/talos/commit/5344d6e7ce2b7febc6109acc566cf49346eca6d9) docs: fix extension service `path` dependency * [`9b9191c5e`](https://github.com/siderolabs/talos/commit/9b9191c5e7a4a03bb7fa271ab49b52874e63ee31) fix: increase intiial window and connection window sizes * [`7a88a0224`](https://github.com/siderolabs/talos/commit/7a88a0224155755a64c911165bf25bff775e1ec2) docs: show archived/pre-release banner based on version * [`e403470bf`](https://github.com/siderolabs/talos/commit/e403470bfefe7af0217d91cb18d900b7046254f9) docs: filter algolia results by latest * [`0497d5f9f`](https://github.com/siderolabs/talos/commit/0497d5f9fee404f68d09c0c500cb446126cfc6aa) docs: tag latest docs for search * [`a25425483`](https://github.com/siderolabs/talos/commit/a25425483518adc5bdd575c5fb8cc1b3464444ea) feat: update containerd to 1.6.2, Linux to 5.15.31 * [`9b6422fcc`](https://github.com/siderolabs/talos/commit/9b6422fcc39c2f4e0723c0db0b6aefe3e4fc8267) feat: update CoreDNS to 1.9.1 * [`020856f80`](https://github.com/siderolabs/talos/commit/020856f80dd93fb47170351c083602ffd516d113) docs: remove second search bar * [`5f27f4c63`](https://github.com/siderolabs/talos/commit/5f27f4c6384e9bb6df4fc969c3a318ad3052cf3f) docs: update asset links * [`9ff42b432`](https://github.com/siderolabs/talos/commit/9ff42b43202bb59845439a88014011ff002a7770) docs: fix redirects for /docs URLs * [`7283efd56`](https://github.com/siderolabs/talos/commit/7283efd568d35e6d2c68aa2bc101a7af86db8c62) chore: update the talosctl CNI download url * [`e0eee7fcc`](https://github.com/siderolabs/talos/commit/e0eee7fcc68f03243ae3248f84d50eb278998e07) test: use clusterctl.yaml overrides after org rename * [`73966f51e`](https://github.com/siderolabs/talos/commit/73966f51e83b7f166e4f7fe013bfed36e9b9a15a) docs: fix extensions * [`f9766edb5`](https://github.com/siderolabs/talos/commit/f9766edb52d6a029d12ac5d74fdb45b6294be058) docs: remove empty doc file * [`e06e1473b`](https://github.com/siderolabs/talos/commit/e06e1473b02cea088499c25f48a9b5e2b75cf879) feat: update golangci-lint to 1.45.0 and gofumpt to 0.3.0 * [`a92c614b2`](https://github.com/siderolabs/talos/commit/a92c614b2f712fb046fb40e00b37773d1390df71) docs: add enterprise link to docs header * [`0ae7174ba`](https://github.com/siderolabs/talos/commit/0ae7174ba3a6c1674c77cf074087a68915e3e612) docs: update search settings and redirects * [`883d401f9`](https://github.com/siderolabs/talos/commit/883d401f9f62229305c2e24f58a0bb0e2e4bb409) chore: rename github organization to siderolabs * [`d1294d014`](https://github.com/siderolabs/talos/commit/d1294d014f5bee7fc1b5dfd6865f22b22f18f5f1) chore: add day-two tests for e2e-qemu * [`a6240e4b6`](https://github.com/siderolabs/talos/commit/a6240e4b67060357c4250e7e5a3a7960408f7c08) feat: update Linux to 5.15.30 * [`e3fda049f`](https://github.com/siderolabs/talos/commit/e3fda049fee62f3c5cef4ae08eaf848826a6dbed) docs: overhaul all the docs * [`f47750726`](https://github.com/siderolabs/talos/commit/f477507262041a24def6ac9b32fa92d276d4d4e6) fix: the etcd recovery client and tests * [`69e07cddc`](https://github.com/siderolabs/talos/commit/69e07cddc77d6ff2c2477ec64f860ef824132000) fix: trigger properly `udevd` on types and actions * [`47d0e629d`](https://github.com/siderolabs/talos/commit/47d0e629d48930f6cb02dff32469bcb34440c73c) fix: clean up custom udev rules if the config is cleared * [`b6691b350`](https://github.com/siderolabs/talos/commit/b6691b35085e4e614752b60441c17fe39fe15928) chore: bump dependencies * [`27af5d41c`](https://github.com/siderolabs/talos/commit/27af5d41c6c58f4d2fc2f5c222d9de39539de1c0) feat: pause the boot process on some failures instead of rebooting * [`58cb9db1e`](https://github.com/siderolabs/talos/commit/58cb9db1e2b3d8fa86c0db0cf38c9f21a843da9d) feat: allow hardlinks in the system extension images * [`1e982808f`](https://github.com/siderolabs/talos/commit/1e982808fbac0a7f897bafacde348c5d83db38b2) fix: ignore pod CIDRs for kubelet node IPs * [`5e0c80f61`](https://github.com/siderolabs/talos/commit/5e0c80f6168ac8a171e35e0c3ee53d959c2dd80d) fix: ignore connection reset errors on k8s upgrade * [`c156580a3`](https://github.com/siderolabs/talos/commit/c156580a386e19d020b550b8459af339f440bf3e) fix: split regular network operation configuration and virtual IP * [`cd4d4c605`](https://github.com/siderolabs/talos/commit/cd4d4c6054107cd6c9274acb2abb4a045368a9fc) feat: relax extensions file structure validation * [`50594ab1a`](https://github.com/siderolabs/talos/commit/50594ab1a7e4d7d025f41873aaa1bf6954827d3e) fix: ignore terminated pods in pod health checks * [`9d69fb6b4`](https://github.com/siderolabs/talos/commit/9d69fb6b40f47061ff96bd7fb3952aa9c16ed601) feat: update Kubernetes to 1.23.5 * [`327ce5aba`](https://github.com/siderolabs/talos/commit/327ce5aba352054837c9cc03c1ba3993a1d18158) fix: invert the condition to skip kubelet kernel checks * [`cf85b3f07`](https://github.com/siderolabs/talos/commit/cf85b3f07ccc3a6845f82f7853da298f5fce62a3) docs: update cilium inline install * [`84ee1795d`](https://github.com/siderolabs/talos/commit/84ee1795dc914574d299b1b0f1ede42bfaee110a) docs: update logo * [`cc7719c9d`](https://github.com/siderolabs/talos/commit/cc7719c9d014ca8c16828a84ccc95c0344bb34ed) docs: improve comments in security proto * [`caf800fe8`](https://github.com/siderolabs/talos/commit/caf800fe843aca5d3559ae5baf08b59db21cccd7) feat: implement D-Bus systemd-compatible shutdown for kubelet * [`6bec08429`](https://github.com/siderolabs/talos/commit/6bec084299062ec6df6e319d4a83313de97e3c67) feat: add talosctl completions to copy, usage, logs, restart and service * [`355b1a4be`](https://github.com/siderolabs/talos/commit/355b1a4bedd6755dbbaa9e98505f5c8540520bb5) fix: refresh etcd certs on startup/join * [`d256b5c5e`](https://github.com/siderolabs/talos/commit/d256b5c5e46ac87edf5681611eeda95fe091d922) docs: fix spelling mistakes * [`5fdedae20`](https://github.com/siderolabs/talos/commit/5fdedae208bfa561b7ca1a04f140adcee3deb565) chore: bump kernel to 5.15.28 * [`18a21b5f2`](https://github.com/siderolabs/talos/commit/18a21b5f24baeea5b876d99b29f5397cc3617399) chore: add dependency images-essential -> images * [`714e5eca6`](https://github.com/siderolabs/talos/commit/714e5eca63ee0dd4a81ca5937081779829092111) chore: bump dependencies * [`58be4067e`](https://github.com/siderolabs/talos/commit/58be4067e6ddc7ba3a346469c30c435b560df377) docs: update README.md * [`c5fb20930`](https://github.com/siderolabs/talos/commit/c5fb20930555e5e31ea01e75aa3690d2cf628f29) docs: add loki note * [`f448cb4f3`](https://github.com/siderolabs/talos/commit/f448cb4f3c1620669fa34250e39aeec0e4002d37) feat: bump boot partition size to 1000 MiB * [`a095acb09`](https://github.com/siderolabs/talos/commit/a095acb09f225bce0e1c17f86576400549789608) chore: fix equinixMetal platform name * [`2a7f9a445`](https://github.com/siderolabs/talos/commit/2a7f9a4457bcb18e66b9ee6eb0ff49a290c381ce) fix: check for IPv6 before applying accept_ra * [`59681b8c9`](https://github.com/siderolabs/talos/commit/59681b8c9a47701092c7287c2375123134d3f9ba) fix: backport fixes from release-1.0 branch

### Changes since v1.1.0-alpha.1
66 commits

* [`91a49c4e7`](https://github.com/siderolabs/talos/commit/91a49c4e7c0495794a991ab7742cd06339ce072d) fix: dhcpv6 leasetime segfault loop * [`afb679586`](https://github.com/siderolabs/talos/commit/afb679586a7319746e59071b6ce0b85df0d77f48) fix: reset certificate SANs on update * [`c87432fe1`](https://github.com/siderolabs/talos/commit/c87432fe1feab8a157c858cf04aefe40c9f84895) fix: detect WSL for `talosctl cluster create` on Docker * [`166d2585c`](https://github.com/siderolabs/talos/commit/166d2585c6d94791d8a3392fc597cd9c489c1958) chore: bump kernel and runc * [`8d9b0cde0`](https://github.com/siderolabs/talos/commit/8d9b0cde0ab51f16421e3db23d301349626b49d8) chore: update deps to go 1.18.2 * [`86741d998`](https://github.com/siderolabs/talos/commit/86741d99845ab36218417bd1e00f446bfe71b922) fix: append hostname to cluster SANs when port is not specified * [`9885bbe17`](https://github.com/siderolabs/talos/commit/9885bbe17762af22d704b6c4e33ef99032e85956) docs: fix typos, edited for clarity * [`7fd1c80c3`](https://github.com/siderolabs/talos/commit/7fd1c80c3ee2ea33fe1aeb2e6cb21a3d0bc98537) fix: ignore failures to dial wireguard client * [`b8e7cdb70`](https://github.com/siderolabs/talos/commit/b8e7cdb7012c89f980dc4c2ac29d6a503c55206a) docs: add vultr guide * [`c2be65b66`](https://github.com/siderolabs/talos/commit/c2be65b6684b208a8ac2e1b598e005b72dd24b8f) fix: openstack unable to parseIP * [`2ae0e3a56`](https://github.com/siderolabs/talos/commit/2ae0e3a56971ece0be47b741ddbf5f652b7244f1) test: add a test for version of Go Talos was built with * [`bef1a13fa`](https://github.com/siderolabs/talos/commit/bef1a13fa2968dd483a45d766ca7fe1de5aedace) docs: update latest Talos in 1.0 docs to 1.0.4 * [`b52962c44`](https://github.com/siderolabs/talos/commit/b52962c44075a34399a4ceb0210b733df812775f) chore: bump dependencies * [`79ae76a6f`](https://github.com/siderolabs/talos/commit/79ae76a6f220683b318e2df6af7326e20c255a79) fix: properly set `allowSchedulingOnMasters` in the interactive install * [`2b7e7d4dc`](https://github.com/siderolabs/talos/commit/2b7e7d4dc49189dd18b1bb5646a1c621d27d82df) feat: print the status of services during boot * [`802d4a23c`](https://github.com/siderolabs/talos/commit/802d4a23c9c6634686136eb80bf86336c5dc6084) fix: load kubelet system service in StartAllServices task * [`67019c434`](https://github.com/siderolabs/talos/commit/67019c434b7e4b20f4986433340bc626b30d16bc) fix: add source path for ovmf flash image * [`da7b24ba5`](https://github.com/siderolabs/talos/commit/da7b24ba57fcfb589bb34f1947b3744c9953bb6b) chore: bump kernel to 5.15.38 * [`8ca8effd6`](https://github.com/siderolabs/talos/commit/8ca8effd6165fb39120c6b30266f0b4ee3b06bfd) chore: add Equinix Managed Services NL to adopters * [`8bc97a30f`](https://github.com/siderolabs/talos/commit/8bc97a30f239ed5b2bbb24e620ffa7bd31a0ebf9) fix: remove D-Bus sockets before listen attempts * [`54cfa039a`](https://github.com/siderolabs/talos/commit/54cfa039ab8196b3d8eec9b69aa723ad90e3054b) fix: use json.Unmarshal instead of yaml.Unmarshal * [`6d30c4822`](https://github.com/siderolabs/talos/commit/6d30c48223022a369556e5a678a78bbab2316e67) docs: update networking resource documentation * [`bc88de729`](https://github.com/siderolabs/talos/commit/bc88de729c69683ac74e3312b154a722b2100a44) chore: bump coredns to v1.9.2 * [`c6722b637`](https://github.com/siderolabs/talos/commit/c6722b637b2f39ecc83f32a1f61a6591d6a2aff6) docs: when mentioning partitions, link to partition docs * [`b189e8426`](https://github.com/siderolabs/talos/commit/b189e84269bdc14b041d7f225545b7c9ee948dbc) chore: fix incorrect ManifestSpec.MarshalYAML signature * [`5d5280200`](https://github.com/siderolabs/talos/commit/5d52802001d4fef74fe2d47e436bfd52dda7617b) feat: add more hardware information to the link status resources * [`2ff6db749`](https://github.com/siderolabs/talos/commit/2ff6db749af4d38959f0adc113e1d406234b05ea) chore: add Nedap Security Atlas as adopter * [`89cab200b`](https://github.com/siderolabs/talos/commit/89cab200b8515a6bbc62659164da61e8913860b1) chore: bump kubernetes to v1.24.0 * [`09d16349f`](https://github.com/siderolabs/talos/commit/09d16349f4e10dfe2f32eaaa824ecf7b9e078074) chore: refactor StaticPod and StaticPodStatus into typed.Resource * [`d2935f98c`](https://github.com/siderolabs/talos/commit/d2935f98c4f9ae99bc8e1c0f63a4b0825a2678f6) chore: refactor LinkRefresh and LinkStatus into typed.Resource * [`b52e0b9b9`](https://github.com/siderolabs/talos/commit/b52e0b9b9ecb84a7b73771a80f3425af4814ada0) fix: talosctl throws error if gen option and --input-dir flags are combined * [`0e15de3a8`](https://github.com/siderolabs/talos/commit/0e15de3a8a4057866e5e51c99936fad1c629098f) docs: add adopters file * [`bb932c297`](https://github.com/siderolabs/talos/commit/bb932c29706c1eb77088d734c65857cd63035031) chore: bump containerd to v1.6.4 * [`4eaaa2d59`](https://github.com/siderolabs/talos/commit/4eaaa2d597d12d8ec4fa265499790d0891beac42) chore: bump kernel to 5.15.37 * [`89dde8f2c`](https://github.com/siderolabs/talos/commit/89dde8f2c426e2eb53de507b69f48bb8b6b5b7fe) chore: refactor remaining resources into typed.Resource * [`bd089e702`](https://github.com/siderolabs/talos/commit/bd089e702d247b2ee5f31bde542cf407e155e10b) chore: bump dependencies * [`3136334b9`](https://github.com/siderolabs/talos/commit/3136334b935e37e4a2f11bd4c02f89aee4806ddb) docs: fix links in VMware documentation * [`403df0e18`](https://github.com/siderolabs/talos/commit/403df0e1802186c409eb8cc255ca1233898a5eda) docs: provide example on using config generation package * [`635192861`](https://github.com/siderolabs/talos/commit/63519286110e75adc03edb89b4dc9cb432b15d46) chore: redo pointer with github.com/siderolabs/go-pointer module * [`a269f740c`](https://github.com/siderolabs/talos/commit/a269f740ce323b21e532b24aa327b1f4b4b1e951) docs: copy knowledge base to v1.0 docs * [`483201026`](https://github.com/siderolabs/talos/commit/48320102630a6a551aaa05202181daad62757180) fix: return an error if there is no byte slice in ReadonlyProvider * [`6e7486f09`](https://github.com/siderolabs/talos/commit/6e7486f099026724c21a73a4d4ff40134ac864b1) fix: allow graceful node shutdown to be overridden * [`867d38f28`](https://github.com/siderolabs/talos/commit/867d38f28f3b65b373206528d18360b714331cec) feat: add bond slaves ordering * [`03ef62ad8`](https://github.com/siderolabs/talos/commit/03ef62ad8bf10eccacbd490ebbcaceb0794f5378) fix: include Go primitive types into unstructured deepcopy * [`f06e6acf2`](https://github.com/siderolabs/talos/commit/f06e6acf2f76c48a59654e53503ae82eb9a6532c) chore: bump kernel to 5.15.36 * [`c0d386abb`](https://github.com/siderolabs/talos/commit/c0d386abb6e427fa0da5ab416a9a3caf9ab1f1d6) fix: don't mount D-Bus socket via mount under recursive bind mount * [`9a8ff76df`](https://github.com/siderolabs/talos/commit/9a8ff76df2fa7366ccdeb597226285a77528442e) refactor: rewrite perf resource to use typed.Resource * [`71d04c4d5`](https://github.com/siderolabs/talos/commit/71d04c4d5cbaeadfc86b6eb5164f7f8026a9b66a) refactor: rewrite runtime resources to use typed.Resource * [`7568d51fc`](https://github.com/siderolabs/talos/commit/7568d51fc8e40c57cef3d86c77a662508ee3f0cd) fix: trigger CRI config merge on correct resource update * [`c456dbcb9`](https://github.com/siderolabs/talos/commit/c456dbcb934533e3026246635911ce44045dfb17) docs: remove references to init nodes * [`1973095d1`](https://github.com/siderolabs/talos/commit/1973095d146a9b10408f972904a06ed555524bd2) feat: update containerd to 1.6.3 * [`b51292d88`](https://github.com/siderolabs/talos/commit/b51292d884b420b30cd5eb8eba29286f72681b72) docs: reformat config reference * [`c0709d970`](https://github.com/siderolabs/talos/commit/c0709d97078f6574be3b6413a1513d91adeea26e) feat: increase aio-max-nr and inotify.max_user_instances * [`85b328e99`](https://github.com/siderolabs/talos/commit/85b328e99719e549346a39ffc4a782e993dc7696) refactor: convert secrets resources to use typed.Resource * [`e91350acd`](https://github.com/siderolabs/talos/commit/e91350acd727581478b852f171b69a67b4322999) refactor: convert time & v1alpha1 resources to use typed.Resource * [`45464412e`](https://github.com/siderolabs/talos/commit/45464412e0526195c3a7f3e447f8f5bee108407d) chore: bump dependencies * [`0af6b35a6`](https://github.com/siderolabs/talos/commit/0af6b35a66ae4145d678796d6b7fea2cf77ea9de) feat: update etcd to 3.5.4 * [`7ad27751c`](https://github.com/siderolabs/talos/commit/7ad27751cbd2a44290b7c5fa708129df5a912375) docs: fix analytics and sitemap * [`55ff876dc`](https://github.com/siderolabs/talos/commit/55ff876dc649f102cb6608efa6fb738d9ea69bba) chore: bump K8s Go modules to 1.24.0-rc.0 * [`f1f43131f`](https://github.com/siderolabs/talos/commit/f1f43131f8f1070240fc32cc96a7b1ccf78e3f76) fix: strip 'v' prefix from versions on Kubernetes upgrade * [`ec621477b`](https://github.com/siderolabs/talos/commit/ec621477bd089d1672bf5ea94378ffa397bee227) chore: tune QEMU disk provisioner options * [`b085343dc`](https://github.com/siderolabs/talos/commit/b085343dcb0bff77eb03a9754843b68c8f2f90af) feat: use discovery information for etcd join (and other etcd calls) * [`2b03057b9`](https://github.com/siderolabs/talos/commit/2b03057b91928f550e22b96885878c2369379e98) feat: implement a new mode `try` in the config manipulation commands * [`51a68c31f`](https://github.com/siderolabs/talos/commit/51a68c31ff6d9bb4dc96500c158ea9968680703a) chore: allow mounting files from the host * [`f3e330a0a`](https://github.com/siderolabs/talos/commit/f3e330a0aaf4611cd1ffc1d2abd90487132e16e9) docs: fix network dependency * [`7ba39bd60`](https://github.com/siderolabs/talos/commit/7ba39bd60052eb41f718d920fa916e5c9b80a036) docs: clarify discovery service

### Changes from siderolabs/extras
3 commits

* [`a77a6f4`](https://github.com/siderolabs/extras/commit/a77a6f42b96fb23dffd5b909b75792511bcc219c) chore: bump Go to 1.18.2 * [`ac3b9a4`](https://github.com/siderolabs/extras/commit/ac3b9a4be9bc102583f9a8cf37a53f13916d4ce7) chore: bump pkgs * [`d4f8e88`](https://github.com/siderolabs/extras/commit/d4f8e886147749e29026943cff3f5c701aaadf00) chore: update references after org rename

### Changes from siderolabs/go-pointer
2 commits

* [`71ccdf0`](https://github.com/siderolabs/go-pointer/commit/71ccdf0d65330596f4def36da37625e4f362f2a9) chore: implement main functionality * [`c1c3b23`](https://github.com/siderolabs/go-pointer/commit/c1c3b235d30cb0de97ed0645809f2b21af3b021e) Initial commit

### Changes from siderolabs/pkgs
44 commits

* [`7add479`](https://github.com/siderolabs/pkgs/commit/7add479f747b4cb1d77983c7a1d363ff0da588a3) chore: bump kernel to 5.15.39 * [`0886699`](https://github.com/siderolabs/pkgs/commit/08866997587225b89007d3a101b227083ddfb62a) chore: bump runc to v1.1.2 * [`dd06fa9`](https://github.com/siderolabs/pkgs/commit/dd06fa93cdada067b93bf721b5ee4f7e7734e975) chore: bump nvidia drivers to 510.68.02 * [`91bb939`](https://github.com/siderolabs/pkgs/commit/91bb939f5dac2642458250be1c381c0ff0b3e6b8) chore: bump Go to 1.18.2 * [`8bd8397`](https://github.com/siderolabs/pkgs/commit/8bd8397176345e8bfe41ccc5ee96898b29b16cf4) chore: bump kernel to 5.15.38 * [`de96a44`](https://github.com/siderolabs/pkgs/commit/de96a445748f925468e885e0e44c26775e937f8a) chore: bump containerd to v1.6.4 * [`45906c1`](https://github.com/siderolabs/pkgs/commit/45906c10ce195268482336b954f91c22620bb39e) chore: bump ca-certificates to 2022-04-26 * [`d847adc`](https://github.com/siderolabs/pkgs/commit/d847adc118d1cad5886fe22b9fcd732c3103a98a) chore: bump kernel to 5.15.37 * [`c4cfa72`](https://github.com/siderolabs/pkgs/commit/c4cfa721258abf7d7f57786aa7d5d4caeffb8b8c) chore: bump util-linux to 2.38 * [`e22317d`](https://github.com/siderolabs/pkgs/commit/e22317dda8a731e41d483e02c486bee81071ab42) chore: bump tools * [`422ed8e`](https://github.com/siderolabs/pkgs/commit/422ed8e5962f983e30f0c7908ce4564ec0e64fa6) chore: bump kernel to 5.15.36 * [`1e833c6`](https://github.com/siderolabs/pkgs/commit/1e833c6b8f15099d7c6f389ae126945f574b9e3e) chore: enable nvme hardware monitor * [`fe7c46f`](https://github.com/siderolabs/pkgs/commit/fe7c46f913a821e62ded2e64294d623df43eecb3) feat: update containerd to 1.6.3 * [`95f4418`](https://github.com/siderolabs/pkgs/commit/95f4418db567226338ba8ab629ace0de39811cc8) chore: bump kernel to 5.15.35 * [`201af71`](https://github.com/siderolabs/pkgs/commit/201af71e96b176033854f3386b4160c3a38d4d1b) chore: bump tools and bldr * [`3de14d7`](https://github.com/siderolabs/pkgs/commit/3de14d725c18f09e05a7db1cf0b6c424f784e977) chore: enable xfs quota support * [`6955fd0`](https://github.com/siderolabs/pkgs/commit/6955fd003aeff46d6d51d4d5c0e9ba64dccbeb26) chore: bump raspberrypi-firmware to 1.20220331 * [`5b498d8`](https://github.com/siderolabs/pkgs/commit/5b498d846d032f625048a5bd4ad2ce23429c5f6d) chore: bump linux-firmware 20220401 * [`9cda5c0`](https://github.com/siderolabs/pkgs/commit/9cda5c0542555c8dc45b34956f50ba721ac2eb05) chore: bump kernel to 5.15.34 * [`8b48af6`](https://github.com/siderolabs/pkgs/commit/8b48af678020d203c065bac750a633f686eaafce) chore: bump tools * [`ff13660`](https://github.com/siderolabs/pkgs/commit/ff1366042afbe1f7fede53f5fc68d7e51d73e613) chore: bump kernel to 5.15.33 * [`415020f`](https://github.com/siderolabs/pkgs/commit/415020fc27129aff334f3a6cd76a60f1d1064e79) chore: bump eudev, remove non-relevant default rules * [`6691342`](https://github.com/siderolabs/pkgs/commit/6691342a3a270954f87663a3b5efb0cf61b19979) chore: add rockpi4c * [`5bd5fad`](https://github.com/siderolabs/pkgs/commit/5bd5fad8fb2aae865797fa1f7374e82bce169067) chore: build u-boot spi image for rockpi * [`4dace49`](https://github.com/siderolabs/pkgs/commit/4dace49282b610d54b5b39917598a80ac3e1ce6a) fix: ipxe prompt arm64 * [`6041fd7`](https://github.com/siderolabs/pkgs/commit/6041fd7963ca910a743c4b69f4fd8b9416a549af) chore: update to use latest tools (specifically go 1.18) * [`4b3e70e`](https://github.com/siderolabs/pkgs/commit/4b3e70e783906cf8b12b467d1a046ddeab695b94) chore: upstream u-boot for jetson nano * [`cc1c8c7`](https://github.com/siderolabs/pkgs/commit/cc1c8c7062c77d352f743fe4735bae5c39b00356) feat: update runc to 1.1.1 * [`3baf4e4`](https://github.com/siderolabs/pkgs/commit/3baf4e4e1fda9ead732bee3578fc55f4f846d48a) chore: enable random trust CPU * [`df31920`](https://github.com/siderolabs/pkgs/commit/df319204730f890f35740837f2d6878a27f5728c) chore: disable sound * [`c27751b`](https://github.com/siderolabs/pkgs/commit/c27751b9f811d4b52701031c26a741333b45cbe9) chore: bump nvidia drivers to 510.60.02 * [`ba98e20`](https://github.com/siderolabs/pkgs/commit/ba98e20d12daa200343869444a568fec231ed239) chore: bump kernel to 5.15.32 * [`a76edfd`](https://github.com/siderolabs/pkgs/commit/a76edfdf941455237f8f16b7a833233257ae63a4) feat: update containerd to 1.6.2 * [`0c38670`](https://github.com/siderolabs/pkgs/commit/0c38670333f788946090e42897b44871ac179ed1) chore: bump kernel to 5.15.31 * [`bc4fb0c`](https://github.com/siderolabs/pkgs/commit/bc4fb0c2619e960d84984696aeb7e7e9368e38e9) chore: org update * [`41f291d`](https://github.com/siderolabs/pkgs/commit/41f291df5806b832c53ee6e042d3561a1bb52582) feat: update Flannel CNI to 1.0.1 * [`58603ba`](https://github.com/siderolabs/pkgs/commit/58603bae512a70c5206d9fe4394139c5aa0f757c) chore: bump kernel to 5.15.30 * [`d3bb262`](https://github.com/siderolabs/pkgs/commit/d3bb262acb78831dd3bf3ee57dc02fb6f628e78a) chore: bump kernel to 5.15.29 * [`76a24b5`](https://github.com/siderolabs/pkgs/commit/76a24b5c9727b17f900331093c5bab86ba49f61e) chore: update openssl to 1.1.1n * [`490c7b7`](https://github.com/siderolabs/pkgs/commit/490c7b77052d182e09e25abe77ee27b4b54d7c7a) chore: enable aarch64 NVIDIA drivers * [`b794b7a`](https://github.com/siderolabs/pkgs/commit/b794b7a78c62a418edab4759a5f7bb7e0bd83dbe) chore: bump linux-firmware to 20220310 * [`acda207`](https://github.com/siderolabs/pkgs/commit/acda20721dea1fa6af611a260c3a320f52a8ee16) chore: bump kernel to 5.15.28 * [`e0fec11`](https://github.com/siderolabs/pkgs/commit/e0fec11a010e3958a617d7417be3a69fe43ba1b5) chore: bump nvidia driver to 510.54 * [`0407f05`](https://github.com/siderolabs/pkgs/commit/0407f057edb8b96a7e51c5222f5b2ce171eb11c6) chore: bump kernel to 5.15.27

### Changes from siderolabs/tools
18 commits

* [`967ebd9`](https://github.com/siderolabs/tools/commit/967ebd998f099b73493f1403dcf19373f665fbdf) chore: bump curl to 7.83.1 * [`e61f856`](https://github.com/siderolabs/tools/commit/e61f85612303fb1c855a9e1c15a55f6006e773e5) chore: bump go to 1.18.2 * [`315890f`](https://github.com/siderolabs/tools/commit/315890f8fe487a1a0d5e3a84c493eb1006a3b247) chore: bump ca-certificates to 2022-04-26 * [`a1d3530`](https://github.com/siderolabs/tools/commit/a1d35309614e18979c81f0f657e8e22ed8be2b02) chore: bump util-linux to 2.38 * [`d229fe1`](https://github.com/siderolabs/tools/commit/d229fe141577bc1f4d32f9eb921984c2c8afcdfb) chore: update bldr * [`e9f123c`](https://github.com/siderolabs/tools/commit/e9f123caeed4d28dc5dc93c59cc9d82dfc21d42a) chore: bump curl to 7.83.0 * [`8473ef2`](https://github.com/siderolabs/tools/commit/8473ef2f81fbb2e3fbe61740d35f675f20220a22) chore: bump git to 2.36.0 * [`8c1f801`](https://github.com/siderolabs/tools/commit/8c1f8012f2d399bc119a0a35869e6bfd0013a7a8) chore: bump coreutils to 9.1 * [`533d5c9`](https://github.com/siderolabs/tools/commit/533d5c9c05e4d8b4852e4f0d86d94fdeb0fddbde) chore: bump git to 2.35.2 * [`a15cbee`](https://github.com/siderolabs/tools/commit/a15cbee68e65e6c5835a027879349f8fb6a0fa58) chore: bump go to 1.18.1 * [`718ec10`](https://github.com/siderolabs/tools/commit/718ec10e0d80fceb46a93ad602cca0af25813f51) chore: enable conform * [`a60a332`](https://github.com/siderolabs/tools/commit/a60a33251d9bea2606b33f0a616a1da21e5361e9) chore: bump xz and gzip * [`c8a3d4d`](https://github.com/siderolabs/tools/commit/c8a3d4d894fd584ad8ca66c6b9864c447f87eab9) chore: update go to 1.18 * [`1684fdc`](https://github.com/siderolabs/tools/commit/1684fdce5f46cf09401ffb28652f820722bf2d37) chore: bump expat to 2.4.8 * [`7f5e44c`](https://github.com/siderolabs/tools/commit/7f5e44c1ed984732c5ab9bd22fec7d934829f2be) chore: bump zlib to 1.2.12 * [`bfc99ca`](https://github.com/siderolabs/tools/commit/bfc99cae42ef06cf9ca30e5a5fd0771f64115cbd) chore: rename org * [`99be089`](https://github.com/siderolabs/tools/commit/99be089c5f17500146e7345f3228c52b2b61a9be) chore: update openssl to 1.1.1n * [`b63872b`](https://github.com/siderolabs/tools/commit/b63872bb8dba101a519ea2579b0e37f23b92e0e9) chore: update golang to 1.17.8

### Changes from talos-systems/go-blockdevice
2 commits

* [`d9c3a27`](https://github.com/talos-systems/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems * [`b374eb4`](https://github.com/talos-systems/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default

### Dependency Changes * **cloud.google.com/go/compute** v1.5.0 -> v1.6.1 * **github.com/BurntSushi/toml** v1.0.0 -> v1.1.0 * **github.com/aws/aws-sdk-go** v1.43.8 -> v1.44.11 * **github.com/containerd/containerd** v1.6.2 -> v1.6.4 * **github.com/containernetworking/cni** v1.0.1 -> v1.1.0 * **github.com/containernetworking/plugins** v1.1.0 -> v1.1.1 * **github.com/cosi-project/runtime** 264f8fcd1a4f -> e22a85955e81 * **github.com/docker/distribution** v2.8.0 -> v2.8.1 * **github.com/docker/docker** v20.10.12 -> v20.10.15 * **github.com/fsnotify/fsnotify** v1.5.1 -> v1.5.4 * **github.com/gdamore/tcell/v2** f057f0a857a1 -> v2.5.1 * **github.com/google/go-cmp** v0.5.7 -> v0.5.8 * **github.com/google/nftables** 211824995dcb -> eeaebcf55295 * **github.com/hetznercloud/hcloud-go** v1.33.1 -> v1.33.2 * **github.com/insomniacslk/dhcp** 3c283ff8b7dd -> 1ca156eafb9f * **github.com/jsimonetti/rtnetlink** v1.1.0 -> v1.2.0 * **github.com/mdlayher/netx** 669a06fde734 -> c711c2f8512f * **github.com/opencontainers/image-spec** v1.0.2 -> c5a74bcca799 * **github.com/packethost/packngo** v0.22.0 -> v0.24.0 * **github.com/pelletier/go-toml** v1.9.4 -> v1.9.5 * **github.com/rivo/tview** 96063d6082f3 -> 9994674d60a8 * **github.com/rs/xid** v1.3.0 -> v1.4.0 * **github.com/siderolabs/extras** v1.0.0 -> v1.1.0-alpha.0-2-ga77a6f4 * **github.com/siderolabs/go-pointer** v1.0.0 **_new_** * **github.com/siderolabs/pkgs** v1.0.0-6-g7c293d5 -> v1.1.0-alpha.0-41-g7add479 * **github.com/siderolabs/tools** v1.0.0-1-g4c77d96 -> v1.1.0-alpha.0-17-g967ebd9 * **github.com/spf13/cobra** v1.3.0 -> v1.4.0 * **github.com/spf13/pflag** v1.0.5 **_new_** * **github.com/stretchr/testify** v1.7.0 -> v1.7.1 * **github.com/talos-systems/go-blockdevice** v0.3.1 -> d9c3a2738861 * **github.com/vishvananda/netlink** 650dca95af54 -> v1.2.0-beta * **github.com/vmware-tanzu/sonobuoy** v0.56.2 -> v0.56.5 * **github.com/vmware/govmomi** v0.27.4 -> v0.28.0 * **github.com/vmware/vmw-guestinfo** cc1fd90d572c -> 510905f0efa3 * **go.etcd.io/etcd/api/v3** v3.5.2 -> v3.5.4 * **go.etcd.io/etcd/client/pkg/v3** v3.5.2 -> v3.5.4 * **go.etcd.io/etcd/client/v3** v3.5.2 -> v3.5.4 * **go.etcd.io/etcd/etcdutl/v3** v3.5.2 -> v3.5.4 * **golang.org/x/net** 27dd8689420f -> 2871e0cb64e4 * **golang.org/x/sys** 4e6760a101f9 -> 988cb79eb6c6 * **golang.org/x/term** 03fcf44c2211 -> e5f449aeb171 * **golang.org/x/time** 0e9765cccd65 -> 583f2d630306 * **golang.zx2c4.com/wireguard/wgctrl** fde48d68ee68 -> 3d4a969bb56b * **google.golang.org/grpc** v1.44.0 -> v1.46.0 * **google.golang.org/protobuf** v1.27.1 -> v1.28.0 * **k8s.io/api** v0.23.5 -> v0.24.0 * **k8s.io/apimachinery** v0.23.5 -> v0.24.0 * **k8s.io/apiserver** v0.23.5 -> v0.24.0 * **k8s.io/client-go** v0.23.5 -> v0.24.0 * **k8s.io/component-base** v0.23.5 -> v0.24.0 * **k8s.io/cri-api** v0.23.5 -> v0.24.0 * **k8s.io/klog/v2** v2.60.1 **_new_** * **k8s.io/kubectl** v0.23.5 -> v0.24.0 * **k8s.io/kubelet** v0.23.5 -> v0.24.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.63 -> v1.2.64 Previous release can be found at [v1.0.0](https://github.com/siderolabs/talos/releases/tag/v1.0.0) ## [Talos 1.1.0-alpha.1](https://github.com/siderolabs/talos/releases/tag/v1.1.0-alpha.1) (2022-04-20) Welcome to the v1.1.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Apply Config `--dry-run` The commands `talosctl apply-config`, `talosctl patch mc` and `talosctl edit mc` now support `--dry-run` flag. If enabled it just prints out the selected config application mode and the configuration diff. ### IPv6 in Docker-based Talos Clusters The command `talosctl cluster create` now enables IPv6 by default for the Docker containers created for Talos nodes. This allows to use IPv6 addresses in Kubernetes networking. If `talosctl cluster create` fails to work on Linux due to the lack of IPv6 support, please use the flag `--disable-docker-ipv6` to revert the change. ### drop some default rules shipped by eudev Drops some default eudev rules that doesn't make sense in the context of Talos OS. Especially the ones around sound devices, cd-roms and renaming the network interfaces to be predictable ### Pod Security Admission [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) controller is enabled by default with the following policy: ```yaml apiVersion: apiserver.config.k8s.io/v1 kind: AdmissionConfiguration plugins: - configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration name: PodSecurity path: "" ``` The policy is part of the Talos machine configuration, and it can be modified to suite your needs. ### Support RockPi 4 variants A and B Talos now supports RockPi variants A and B in addition to RockPi 4C ### Raspberry Pi PoE hat fan Talos now enables the Raspberry Pi PoE fan control by pulling in the poe overlay that works with upstream kernel ### Component Updates * Linux: 5.15.35 * Kubernetes: 1.24.0-rc.0 * Flannel: 0.17.0 * runc: 1.1.1 Talos is built with Go 1.18.1. ### x86-64 Architecture Talos is built for x86-64 architecture with support for [x86-64-v2 microarchitecture level](https://en.wikipedia.org/wiki/X86-64#Microarchitecture_levels), so Talos no longer runs on processors supporting only baseline `x86-64` microarchitecture (before 2009). ### Contributors * Andrey Smirnov * Noel Georgi * Tim Jones * Spencer Smith * Dmitriy Matrenichev * Andrew Rynhard * Artem Chernyshev * Steve Francis * Andrei Dobre * Caleb Woodbine * Daniel Höxtermann * Jori Huisman * Nico Berlee * Serge Logvinov * Seán C McCord * Steve Francis * Suraj Shirvankar * Tim Jones * Tomasz Zurkowski * William Ashton ### Changes
144 commits

* [`1d5c08e74`](https://github.com/siderolabs/talos/commit/1d5c08e74f2c9009ff2b3103157eb105e2a32254) chore: bump kernel to 5.15.35 * [`9bf23e516`](https://github.com/siderolabs/talos/commit/9bf23e5162bded75a8c52009a360de1a43060858) feat: update Kubernetes to 1.24.0-rc.0 * [`d78ed320b`](https://github.com/siderolabs/talos/commit/d78ed320b7c9853d5c118223f2289db153ea8145) docs: fix the docs reference to star registry redirects * [`257dfb870`](https://github.com/siderolabs/talos/commit/257dfb870933321175f859348539de6d26161618) fix: run the 'post' stage of the service always * [`992e23023`](https://github.com/siderolabs/talos/commit/992e2302346fb4e34a23d28f4c3a67564ddbb241) fix: correctly handle stopping services with reverse dependencies * [`bb7a50bd5`](https://github.com/siderolabs/talos/commit/bb7a50bd5b31d28cef6a250a056f81c2e1eace80) docs: fix netlify redirects * [`486f79bc7`](https://github.com/siderolabs/talos/commit/486f79bc775564f9fdd2a114b86b70d55324d18a) docs: fix netlify deploy url * [`e8cbedb05`](https://github.com/siderolabs/talos/commit/e8cbedb05bb19bdea339a806576215ae71eee4d8) docs: add canonical link ref * [`0fe4a7832`](https://github.com/siderolabs/talos/commit/0fe4a7832b1327e68d2829ae27078780434f00b3) docs: improve latest-version banner * [`23984efcd`](https://github.com/siderolabs/talos/commit/23984efcdf6ae530301c885c6105aa18d790d9b6) fix: detect lingering mounts in the installer correctly * [`54dba925f`](https://github.com/siderolabs/talos/commit/54dba925f88881f41246a9198955ac6ce95d81d9) chore: refactor network resource to use typed resource * [`4eb9f45cc`](https://github.com/siderolabs/talos/commit/4eb9f45cc82669ac31ffc17bc53a5be05563823e) refactor: split polymorphic K8sControlPlane into typed resources * [`68dfdd331`](https://github.com/siderolabs/talos/commit/68dfdd3311c602faaeb5e5f7970c0e7d13a32600) fix: provide logger to the etcd snapshot restore * [`f190403f0`](https://github.com/siderolabs/talos/commit/f190403f01118c7f60d5e97a4c2349c638ed7e0b) docs: add how to get config after interactive setup * [`fac7b9466`](https://github.com/siderolabs/talos/commit/fac7b94667bb9aae680677b5e3e936f107315062) docs: improve vip caveats documentation * [`250df9e67`](https://github.com/siderolabs/talos/commit/250df9e670c8e4221fa376791b88ee03fa2022ae) docs: improve rook-ceph description * [`b5c1d868d`](https://github.com/siderolabs/talos/commit/b5c1d868deac9fd8d124cda35693b4f12372589f) docs: add talos/kubernetes config faq * [`39721ee93`](https://github.com/siderolabs/talos/commit/39721ee9392ed43da572c71eb056a8a4b1a795fd) chore: bump dependencies * [`610945774`](https://github.com/siderolabs/talos/commit/610945774a8f9cf849ddfefda0e4d456bb8ba2c3) chore: bump tools and pkgs * [`2b68c8b67`](https://github.com/siderolabs/talos/commit/2b68c8b67bf1ea88d471b8baa405a65fcd1aa40f) fix: enable long timestamps for xfs * [`be00d7749`](https://github.com/siderolabs/talos/commit/be00d774921b28ebc9b81727a6e4cf29a06385ee) chore: implement cluster resources using cosi typed resource * [`460d5ab13`](https://github.com/siderolabs/talos/commit/460d5ab13f007a89e72013c443132a845dcc3a09) docs: fix extension services alias * [`bbdfda2dd`](https://github.com/siderolabs/talos/commit/bbdfda2dd2e72f1fd5981dd6fc589d90cd692b72) chore: xfs quota support in kernel * [`8ff8fc77f`](https://github.com/siderolabs/talos/commit/8ff8fc77f3b14679daa31067528f6bcf62e9aca9) chore: enable rpi4 poe hat fan control * [`2b9722d1f`](https://github.com/siderolabs/talos/commit/2b9722d1f5fac39390fde8223d40262af80b1ef2) feat: add `dry-run` flag in `apply-config` and `edit` commands * [`8af50fcd2`](https://github.com/siderolabs/talos/commit/8af50fcd27bed2a437d6d9668233657a47bd9798) fix: correct cri package import path * [`ce09ede83`](https://github.com/siderolabs/talos/commit/ce09ede839e7500df1dd862f8c2726b02798b725) feat: update etcd to 3.5.3 * [`13f41badd`](https://github.com/siderolabs/talos/commit/13f41baddff997dfa15c773d8f078bd0921fb40b) chore: bump kernel to 5.15.34 * [`fa57b5d92`](https://github.com/siderolabs/talos/commit/fa57b5d9225d3075b08a9d07ce29480a4c050143) docs: reorganize documentation * [`a91eb9358`](https://github.com/siderolabs/talos/commit/a91eb9358dfc49e2afc1523f804c0f01660cfb1f) chore: bump deps * [`0aad0df2e`](https://github.com/siderolabs/talos/commit/0aad0df2eb6a8727dfff253619a9b2cb1915d9be) refactor: remove `String()` for resource implementation * [`a4060513c`](https://github.com/siderolabs/talos/commit/a4060513c694f2d45be95a060e4bb719840d8739) feat: build Talos with support for x86-64-v2 microarchitecture * [`8faebd410`](https://github.com/siderolabs/talos/commit/8faebd410be9653808f50df698345ee613be6e68) chore: bump tools and pkgs * [`8499b7e7d`](https://github.com/siderolabs/talos/commit/8499b7e7dcbd5fbcb9aa94a8028a73168a304a06) chore: bump dependencies * [`a7ba7ea67`](https://github.com/siderolabs/talos/commit/a7ba7ea679f10e99b31ee3b4b6c92265d43c12df) feat: migrate to go 1.18 * [`9dace93b5`](https://github.com/siderolabs/talos/commit/9dace93b59e8e1e1d8a7595fda82dc85b9c835cf) feat: enable Pod Security Admission by default * [`c382cb8cd`](https://github.com/siderolabs/talos/commit/c382cb8cd26f2eaece665bcb471f27d188ea1ad5) docs: update vmware docs * [`da0e638f0`](https://github.com/siderolabs/talos/commit/da0e638f04cfab1ed93891231035439ad77666d1) docs: stableize tools versioning * [`f2d2267e7`](https://github.com/siderolabs/talos/commit/f2d2267e749a14b8a060e56f274f603415d69731) docs: use template for netlify redirects * [`88f1d8fcc`](https://github.com/siderolabs/talos/commit/88f1d8fcc0e3bd28a9db4677ad9d782c80ffdbb9) docs: update sitemap to point to direct url * [`a6eebee36`](https://github.com/siderolabs/talos/commit/a6eebee36f9a3f6fbde441ccb5e170dae9727a58) chore: update eudev * [`0cb84e8c1`](https://github.com/siderolabs/talos/commit/0cb84e8c1a09c5b391461aa17c277a0a7803f725) fix: correctly parse tags out of images * [`17d09739f`](https://github.com/siderolabs/talos/commit/17d09739f3fe8cb942008a44f902b65705e39575) docs: enable nested arrow * [`1e4320b64`](https://github.com/siderolabs/talos/commit/1e4320b64e2477a55f808c6b8720b0779088d0f8) chore: add support for rockpi 4A and 4B * [`d1869d948`](https://github.com/siderolabs/talos/commit/d1869d948c84cf7191819eddac9c2aa27b365eb9) docs: update to Sidero Metal, mention clusterctl * [`18d0038ec`](https://github.com/siderolabs/talos/commit/18d0038ecaa2cf43164f72f3acad5445e395b37e) fix: avoid panic in DHCPv6 operator on nil dereference * [`9e3d438db`](https://github.com/siderolabs/talos/commit/9e3d438db461529abf3dfa6ef750b4fa4a9125ec) docs: fix code fence formatting * [`b3f1bb2cf`](https://github.com/siderolabs/talos/commit/b3f1bb2cff544a35f767b32ca8ca1d13b83c535e) fix: add support for FAT12/16 filesystems * [`8619f95c5`](https://github.com/siderolabs/talos/commit/8619f95c5c7779815a87118cbb0a1e493251355d) chore: bump dependencies * [`8c4f72004`](https://github.com/siderolabs/talos/commit/8c4f720048c0187b203ca869befd759249bac79f) docs: override sitemap.xml to only include latest results * [`5192ba4e2`](https://github.com/siderolabs/talos/commit/5192ba4e2314c05e107adcc0a2a71a65ec35bfc3) docs: fix a typo in QEMU VM setup guide * [`663e3e879`](https://github.com/siderolabs/talos/commit/663e3e8796c3f501275fdd7836687b811318b685) refactor: change the stages for embed files generation * [`19bf12af0`](https://github.com/siderolabs/talos/commit/19bf12af07aaf6b54d08027676d8a01b4dd4ed29) fix: enable IPv6 in Docker-based Talos clusters * [`3889a5839`](https://github.com/siderolabs/talos/commit/3889a583970c73ea4c6089b1fe8438b183ec756e) docs: update config.yaml, storage.md, digital-rebar.md * [`25d19131d`](https://github.com/siderolabs/talos/commit/25d19131d378960603a510cb70b35352b07bf7cb) release(v1.1.0-alpha.0): prepare release * [`2ca5279e5`](https://github.com/siderolabs/talos/commit/2ca5279e56d154fdf21fab7ed5c73edb30494560) fix: retry manifest updates in upgrade-k8s * [`eeb756168`](https://github.com/siderolabs/talos/commit/eeb756168f31c8e7a1e0cb2f80e1ae2bc2eed0a9) feat: use kexec when resetting a node * [`1ed1f73e5`](https://github.com/siderolabs/talos/commit/1ed1f73e511f4a5cf4d1db5f97422cf1eb088fda) test: bump CAPI to 1.1.3 * [`2ee1d2c72`](https://github.com/siderolabs/talos/commit/2ee1d2c72085df41ec0355bac0d33bedcb4f2786) feat: update Kuberentes to 1.24.0-beta.0 * [`c26fa4ccc`](https://github.com/siderolabs/talos/commit/c26fa4ccc1e109c889c01384422f88387ad512a2) test: push GITHUB_TOKEN to the e2e-aws/gcp steps * [`95d900de7`](https://github.com/siderolabs/talos/commit/95d900de7799cfa9d0a16049586ba246bddb09d0) feat: use kubeconfig env var * [`0b407dd17`](https://github.com/siderolabs/talos/commit/0b407dd17e9515fecd8083fd5ac1fc84f6085106) feat: add dhcp-v6 NTP/DHCP-DUID * [`a140a6bad`](https://github.com/siderolabs/talos/commit/a140a6bad74bcf34e62e13b6efa63a17741eb5b1) docs: update releases shortcode in upgrade guide * [`12931dced`](https://github.com/siderolabs/talos/commit/12931dcedd38c407a2a03f692d910853130986db) fix: align partitions on 1M boundary * [`37f868e37`](https://github.com/siderolabs/talos/commit/37f868e37454f63a4dfe38d94dbbeef5bb40a2a8) fix: validate empty TLS config for registries * [`ca8b9c0a3`](https://github.com/siderolabs/talos/commit/ca8b9c0a3a15898d9562a6f22aded138d6c3ed7f) feat: update Kubernetes to 1.24.0-alpha.4 * [`d9ec6b215`](https://github.com/siderolabs/talos/commit/d9ec6b2151e94c94eea44771e455555eaf1f257a) chore: drop dirty from abbreviated tag * [`08624fd0b`](https://github.com/siderolabs/talos/commit/08624fd0b12039e5a77ce43f14df65a6c95f7a39) docs: add banner to main page * [`fc23c7a59`](https://github.com/siderolabs/talos/commit/fc23c7a5952d87a51f29d61ead585bf060eeab1c) test: bump versions for upgrade tests * [`4bfe68610`](https://github.com/siderolabs/talos/commit/4bfe686105d5734b282f4817673972b71954e620) feat: update runc to 1.1.1 * [`b315ed953`](https://github.com/siderolabs/talos/commit/b315ed95327a9b7cfb1f83a9da02e96bafecbb1d) chore: use go:embed instead of ldflags * [`a5d64fc81`](https://github.com/siderolabs/talos/commit/a5d64fc814f122fb7e282b97283a46ac0e5d6709) feat: update Flannel to 0.17.0 * [`6d6eb3f6a`](https://github.com/siderolabs/talos/commit/6d6eb3f6a52626c8c94a75439133e7bc22b25e60) docs: fork docs for 1.1 * [`1d55f05d1`](https://github.com/siderolabs/talos/commit/1d55f05d11e5a03a8de0e7ce5ec0167971b03135) docs: update index page * [`ad6b7ec1a`](https://github.com/siderolabs/talos/commit/ad6b7ec1a4347753488de3ab5813947f01967078) fix: enable etcd consistency on check startup * [`65a31f753`](https://github.com/siderolabs/talos/commit/65a31f7531a629b29fbf86ddcbaba20767475924) docs: re-add GA token * [`741c04832`](https://github.com/siderolabs/talos/commit/741c048320b931228336034ad17de10272ff5a77) docs: mark 1.0 docs as latest * [`e97433c8a`](https://github.com/siderolabs/talos/commit/e97433c8a37ca504577355d98c917e083aaedafe) docs: update jetson nano * [`6665e0f00`](https://github.com/siderolabs/talos/commit/6665e0f00c1c5d45123eb28d8755d0815af4822a) docs: code block copying * [`c41f2b216`](https://github.com/siderolabs/talos/commit/c41f2b216717db80e44654f54080a9d462946d45) docs: update whats-new-v1.0 * [`0a36fbbf3`](https://github.com/siderolabs/talos/commit/0a36fbbf3ca579becd0a7f2e5a9715ff4196e8ae) docs: add release notes for 1.0 * [`bd0035f6a`](https://github.com/siderolabs/talos/commit/bd0035f6a285f8b7e4c7c0b5013a271a8d18c5f4) docs: add NVIDIA docs * [`efa3f2898`](https://github.com/siderolabs/talos/commit/efa3f289853a47ae0d4bca5dbf656e527cf312dd) fix: correctly find partitions with config data (`metal-iso`) * [`9ebeec0d0`](https://github.com/siderolabs/talos/commit/9ebeec0d0ea4dd3cc1ba3b7171fe0a9bda943fe8) docs: fix incorrect path for talosconfig * [`9fef4540e`](https://github.com/siderolabs/talos/commit/9fef4540e1c7a7deb5d4745d3de17c6e5cc45369) docs: fix non-latest download links * [`f8ef6a081`](https://github.com/siderolabs/talos/commit/f8ef6a081e055637a5652366a6e344b6df911871) docs: add rook ceph configuration guide * [`e2666f58f`](https://github.com/siderolabs/talos/commit/e2666f58f5835db6ff8802b2370a480d8afcd8fc) chore: bump kernel to 5.15.32 * [`957b2f233`](https://github.com/siderolabs/talos/commit/957b2f233c4b81eacdb5a3190c0070fa36ef0d82) chore: bump dependencies * [`0fd2aa08b`](https://github.com/siderolabs/talos/commit/0fd2aa08bd70d1c869e0dca136ca0c487bfcdefe) fix: correctly escape '.' in volume names * [`108fd03a7`](https://github.com/siderolabs/talos/commit/108fd03a72534cebbab7c09d63051021483566ac) fix: give up virtual IPs before the kubelet workloads are shut down * [`856e1333d`](https://github.com/siderolabs/talos/commit/856e1333dcfb8c0244ca8ead415025b32a4819fc) fix: use 'localhost' endpoint in docker provisioner on Windows * [`c5da38609`](https://github.com/siderolabs/talos/commit/c5da386092185fe4ed4173b08f95eac4e435ff99) docs: use variables and templates in the docs * [`4c83847b9`](https://github.com/siderolabs/talos/commit/4c83847b9091a4e8968544a515632a3391c06cd0) docs: target search results * [`67fb72d96`](https://github.com/siderolabs/talos/commit/67fb72d96db1cb772392dcab9b5a3a08ee50ff03) docs: add algolia versions to all content * [`5344d6e7c`](https://github.com/siderolabs/talos/commit/5344d6e7ce2b7febc6109acc566cf49346eca6d9) docs: fix extension service `path` dependency * [`9b9191c5e`](https://github.com/siderolabs/talos/commit/9b9191c5e7a4a03bb7fa271ab49b52874e63ee31) fix: increase intiial window and connection window sizes * [`7a88a0224`](https://github.com/siderolabs/talos/commit/7a88a0224155755a64c911165bf25bff775e1ec2) docs: show archived/pre-release banner based on version * [`e403470bf`](https://github.com/siderolabs/talos/commit/e403470bfefe7af0217d91cb18d900b7046254f9) docs: filter algolia results by latest * [`0497d5f9f`](https://github.com/siderolabs/talos/commit/0497d5f9fee404f68d09c0c500cb446126cfc6aa) docs: tag latest docs for search * [`a25425483`](https://github.com/siderolabs/talos/commit/a25425483518adc5bdd575c5fb8cc1b3464444ea) feat: update containerd to 1.6.2, Linux to 5.15.31 * [`9b6422fcc`](https://github.com/siderolabs/talos/commit/9b6422fcc39c2f4e0723c0db0b6aefe3e4fc8267) feat: update CoreDNS to 1.9.1 * [`020856f80`](https://github.com/siderolabs/talos/commit/020856f80dd93fb47170351c083602ffd516d113) docs: remove second search bar * [`5f27f4c63`](https://github.com/siderolabs/talos/commit/5f27f4c6384e9bb6df4fc969c3a318ad3052cf3f) docs: update asset links * [`9ff42b432`](https://github.com/siderolabs/talos/commit/9ff42b43202bb59845439a88014011ff002a7770) docs: fix redirects for /docs URLs * [`7283efd56`](https://github.com/siderolabs/talos/commit/7283efd568d35e6d2c68aa2bc101a7af86db8c62) chore: update the talosctl CNI download url * [`e0eee7fcc`](https://github.com/siderolabs/talos/commit/e0eee7fcc68f03243ae3248f84d50eb278998e07) test: use clusterctl.yaml overrides after org rename * [`73966f51e`](https://github.com/siderolabs/talos/commit/73966f51e83b7f166e4f7fe013bfed36e9b9a15a) docs: fix extensions * [`f9766edb5`](https://github.com/siderolabs/talos/commit/f9766edb52d6a029d12ac5d74fdb45b6294be058) docs: remove empty doc file * [`e06e1473b`](https://github.com/siderolabs/talos/commit/e06e1473b02cea088499c25f48a9b5e2b75cf879) feat: update golangci-lint to 1.45.0 and gofumpt to 0.3.0 * [`a92c614b2`](https://github.com/siderolabs/talos/commit/a92c614b2f712fb046fb40e00b37773d1390df71) docs: add enterprise link to docs header * [`0ae7174ba`](https://github.com/siderolabs/talos/commit/0ae7174ba3a6c1674c77cf074087a68915e3e612) docs: update search settings and redirects * [`883d401f9`](https://github.com/siderolabs/talos/commit/883d401f9f62229305c2e24f58a0bb0e2e4bb409) chore: rename github organization to siderolabs * [`d1294d014`](https://github.com/siderolabs/talos/commit/d1294d014f5bee7fc1b5dfd6865f22b22f18f5f1) chore: add day-two tests for e2e-qemu * [`a6240e4b6`](https://github.com/siderolabs/talos/commit/a6240e4b67060357c4250e7e5a3a7960408f7c08) feat: update Linux to 5.15.30 * [`e3fda049f`](https://github.com/siderolabs/talos/commit/e3fda049fee62f3c5cef4ae08eaf848826a6dbed) docs: overhaul all the docs * [`f47750726`](https://github.com/siderolabs/talos/commit/f477507262041a24def6ac9b32fa92d276d4d4e6) fix: the etcd recovery client and tests * [`69e07cddc`](https://github.com/siderolabs/talos/commit/69e07cddc77d6ff2c2477ec64f860ef824132000) fix: trigger properly `udevd` on types and actions * [`47d0e629d`](https://github.com/siderolabs/talos/commit/47d0e629d48930f6cb02dff32469bcb34440c73c) fix: clean up custom udev rules if the config is cleared * [`b6691b350`](https://github.com/siderolabs/talos/commit/b6691b35085e4e614752b60441c17fe39fe15928) chore: bump dependencies * [`27af5d41c`](https://github.com/siderolabs/talos/commit/27af5d41c6c58f4d2fc2f5c222d9de39539de1c0) feat: pause the boot process on some failures instead of rebooting * [`58cb9db1e`](https://github.com/siderolabs/talos/commit/58cb9db1e2b3d8fa86c0db0cf38c9f21a843da9d) feat: allow hardlinks in the system extension images * [`1e982808f`](https://github.com/siderolabs/talos/commit/1e982808fbac0a7f897bafacde348c5d83db38b2) fix: ignore pod CIDRs for kubelet node IPs * [`5e0c80f61`](https://github.com/siderolabs/talos/commit/5e0c80f6168ac8a171e35e0c3ee53d959c2dd80d) fix: ignore connection reset errors on k8s upgrade * [`c156580a3`](https://github.com/siderolabs/talos/commit/c156580a386e19d020b550b8459af339f440bf3e) fix: split regular network operation configuration and virtual IP * [`cd4d4c605`](https://github.com/siderolabs/talos/commit/cd4d4c6054107cd6c9274acb2abb4a045368a9fc) feat: relax extensions file structure validation * [`50594ab1a`](https://github.com/siderolabs/talos/commit/50594ab1a7e4d7d025f41873aaa1bf6954827d3e) fix: ignore terminated pods in pod health checks * [`9d69fb6b4`](https://github.com/siderolabs/talos/commit/9d69fb6b40f47061ff96bd7fb3952aa9c16ed601) feat: update Kubernetes to 1.23.5 * [`327ce5aba`](https://github.com/siderolabs/talos/commit/327ce5aba352054837c9cc03c1ba3993a1d18158) fix: invert the condition to skip kubelet kernel checks * [`cf85b3f07`](https://github.com/siderolabs/talos/commit/cf85b3f07ccc3a6845f82f7853da298f5fce62a3) docs: update cilium inline install * [`84ee1795d`](https://github.com/siderolabs/talos/commit/84ee1795dc914574d299b1b0f1ede42bfaee110a) docs: update logo * [`cc7719c9d`](https://github.com/siderolabs/talos/commit/cc7719c9d014ca8c16828a84ccc95c0344bb34ed) docs: improve comments in security proto * [`caf800fe8`](https://github.com/siderolabs/talos/commit/caf800fe843aca5d3559ae5baf08b59db21cccd7) feat: implement D-Bus systemd-compatible shutdown for kubelet * [`6bec08429`](https://github.com/siderolabs/talos/commit/6bec084299062ec6df6e319d4a83313de97e3c67) feat: add talosctl completions to copy, usage, logs, restart and service * [`355b1a4be`](https://github.com/siderolabs/talos/commit/355b1a4bedd6755dbbaa9e98505f5c8540520bb5) fix: refresh etcd certs on startup/join * [`d256b5c5e`](https://github.com/siderolabs/talos/commit/d256b5c5e46ac87edf5681611eeda95fe091d922) docs: fix spelling mistakes * [`5fdedae20`](https://github.com/siderolabs/talos/commit/5fdedae208bfa561b7ca1a04f140adcee3deb565) chore: bump kernel to 5.15.28 * [`18a21b5f2`](https://github.com/siderolabs/talos/commit/18a21b5f24baeea5b876d99b29f5397cc3617399) chore: add dependency images-essential -> images * [`714e5eca6`](https://github.com/siderolabs/talos/commit/714e5eca63ee0dd4a81ca5937081779829092111) chore: bump dependencies * [`58be4067e`](https://github.com/siderolabs/talos/commit/58be4067e6ddc7ba3a346469c30c435b560df377) docs: update README.md * [`c5fb20930`](https://github.com/siderolabs/talos/commit/c5fb20930555e5e31ea01e75aa3690d2cf628f29) docs: add loki note * [`f448cb4f3`](https://github.com/siderolabs/talos/commit/f448cb4f3c1620669fa34250e39aeec0e4002d37) feat: bump boot partition size to 1000 MiB * [`a095acb09`](https://github.com/siderolabs/talos/commit/a095acb09f225bce0e1c17f86576400549789608) chore: fix equinixMetal platform name * [`2a7f9a445`](https://github.com/siderolabs/talos/commit/2a7f9a4457bcb18e66b9ee6eb0ff49a290c381ce) fix: check for IPv6 before applying accept_ra * [`59681b8c9`](https://github.com/siderolabs/talos/commit/59681b8c9a47701092c7287c2375123134d3f9ba) fix: backport fixes from release-1.0 branch

### Changes since v1.1.0-alpha.0
54 commits

* [`1d5c08e74`](https://github.com/siderolabs/talos/commit/1d5c08e74f2c9009ff2b3103157eb105e2a32254) chore: bump kernel to 5.15.35 * [`9bf23e516`](https://github.com/siderolabs/talos/commit/9bf23e5162bded75a8c52009a360de1a43060858) feat: update Kubernetes to 1.24.0-rc.0 * [`d78ed320b`](https://github.com/siderolabs/talos/commit/d78ed320b7c9853d5c118223f2289db153ea8145) docs: fix the docs reference to star registry redirects * [`257dfb870`](https://github.com/siderolabs/talos/commit/257dfb870933321175f859348539de6d26161618) fix: run the 'post' stage of the service always * [`992e23023`](https://github.com/siderolabs/talos/commit/992e2302346fb4e34a23d28f4c3a67564ddbb241) fix: correctly handle stopping services with reverse dependencies * [`bb7a50bd5`](https://github.com/siderolabs/talos/commit/bb7a50bd5b31d28cef6a250a056f81c2e1eace80) docs: fix netlify redirects * [`486f79bc7`](https://github.com/siderolabs/talos/commit/486f79bc775564f9fdd2a114b86b70d55324d18a) docs: fix netlify deploy url * [`e8cbedb05`](https://github.com/siderolabs/talos/commit/e8cbedb05bb19bdea339a806576215ae71eee4d8) docs: add canonical link ref * [`0fe4a7832`](https://github.com/siderolabs/talos/commit/0fe4a7832b1327e68d2829ae27078780434f00b3) docs: improve latest-version banner * [`23984efcd`](https://github.com/siderolabs/talos/commit/23984efcdf6ae530301c885c6105aa18d790d9b6) fix: detect lingering mounts in the installer correctly * [`54dba925f`](https://github.com/siderolabs/talos/commit/54dba925f88881f41246a9198955ac6ce95d81d9) chore: refactor network resource to use typed resource * [`4eb9f45cc`](https://github.com/siderolabs/talos/commit/4eb9f45cc82669ac31ffc17bc53a5be05563823e) refactor: split polymorphic K8sControlPlane into typed resources * [`68dfdd331`](https://github.com/siderolabs/talos/commit/68dfdd3311c602faaeb5e5f7970c0e7d13a32600) fix: provide logger to the etcd snapshot restore * [`f190403f0`](https://github.com/siderolabs/talos/commit/f190403f01118c7f60d5e97a4c2349c638ed7e0b) docs: add how to get config after interactive setup * [`fac7b9466`](https://github.com/siderolabs/talos/commit/fac7b94667bb9aae680677b5e3e936f107315062) docs: improve vip caveats documentation * [`250df9e67`](https://github.com/siderolabs/talos/commit/250df9e670c8e4221fa376791b88ee03fa2022ae) docs: improve rook-ceph description * [`b5c1d868d`](https://github.com/siderolabs/talos/commit/b5c1d868deac9fd8d124cda35693b4f12372589f) docs: add talos/kubernetes config faq * [`39721ee93`](https://github.com/siderolabs/talos/commit/39721ee9392ed43da572c71eb056a8a4b1a795fd) chore: bump dependencies * [`610945774`](https://github.com/siderolabs/talos/commit/610945774a8f9cf849ddfefda0e4d456bb8ba2c3) chore: bump tools and pkgs * [`2b68c8b67`](https://github.com/siderolabs/talos/commit/2b68c8b67bf1ea88d471b8baa405a65fcd1aa40f) fix: enable long timestamps for xfs * [`be00d7749`](https://github.com/siderolabs/talos/commit/be00d774921b28ebc9b81727a6e4cf29a06385ee) chore: implement cluster resources using cosi typed resource * [`460d5ab13`](https://github.com/siderolabs/talos/commit/460d5ab13f007a89e72013c443132a845dcc3a09) docs: fix extension services alias * [`bbdfda2dd`](https://github.com/siderolabs/talos/commit/bbdfda2dd2e72f1fd5981dd6fc589d90cd692b72) chore: xfs quota support in kernel * [`8ff8fc77f`](https://github.com/siderolabs/talos/commit/8ff8fc77f3b14679daa31067528f6bcf62e9aca9) chore: enable rpi4 poe hat fan control * [`2b9722d1f`](https://github.com/siderolabs/talos/commit/2b9722d1f5fac39390fde8223d40262af80b1ef2) feat: add `dry-run` flag in `apply-config` and `edit` commands * [`8af50fcd2`](https://github.com/siderolabs/talos/commit/8af50fcd27bed2a437d6d9668233657a47bd9798) fix: correct cri package import path * [`ce09ede83`](https://github.com/siderolabs/talos/commit/ce09ede839e7500df1dd862f8c2726b02798b725) feat: update etcd to 3.5.3 * [`13f41badd`](https://github.com/siderolabs/talos/commit/13f41baddff997dfa15c773d8f078bd0921fb40b) chore: bump kernel to 5.15.34 * [`fa57b5d92`](https://github.com/siderolabs/talos/commit/fa57b5d9225d3075b08a9d07ce29480a4c050143) docs: reorganize documentation * [`a91eb9358`](https://github.com/siderolabs/talos/commit/a91eb9358dfc49e2afc1523f804c0f01660cfb1f) chore: bump deps * [`0aad0df2e`](https://github.com/siderolabs/talos/commit/0aad0df2eb6a8727dfff253619a9b2cb1915d9be) refactor: remove `String()` for resource implementation * [`a4060513c`](https://github.com/siderolabs/talos/commit/a4060513c694f2d45be95a060e4bb719840d8739) feat: build Talos with support for x86-64-v2 microarchitecture * [`8faebd410`](https://github.com/siderolabs/talos/commit/8faebd410be9653808f50df698345ee613be6e68) chore: bump tools and pkgs * [`8499b7e7d`](https://github.com/siderolabs/talos/commit/8499b7e7dcbd5fbcb9aa94a8028a73168a304a06) chore: bump dependencies * [`a7ba7ea67`](https://github.com/siderolabs/talos/commit/a7ba7ea679f10e99b31ee3b4b6c92265d43c12df) feat: migrate to go 1.18 * [`9dace93b5`](https://github.com/siderolabs/talos/commit/9dace93b59e8e1e1d8a7595fda82dc85b9c835cf) feat: enable Pod Security Admission by default * [`c382cb8cd`](https://github.com/siderolabs/talos/commit/c382cb8cd26f2eaece665bcb471f27d188ea1ad5) docs: update vmware docs * [`da0e638f0`](https://github.com/siderolabs/talos/commit/da0e638f04cfab1ed93891231035439ad77666d1) docs: stableize tools versioning * [`f2d2267e7`](https://github.com/siderolabs/talos/commit/f2d2267e749a14b8a060e56f274f603415d69731) docs: use template for netlify redirects * [`88f1d8fcc`](https://github.com/siderolabs/talos/commit/88f1d8fcc0e3bd28a9db4677ad9d782c80ffdbb9) docs: update sitemap to point to direct url * [`a6eebee36`](https://github.com/siderolabs/talos/commit/a6eebee36f9a3f6fbde441ccb5e170dae9727a58) chore: update eudev * [`0cb84e8c1`](https://github.com/siderolabs/talos/commit/0cb84e8c1a09c5b391461aa17c277a0a7803f725) fix: correctly parse tags out of images * [`17d09739f`](https://github.com/siderolabs/talos/commit/17d09739f3fe8cb942008a44f902b65705e39575) docs: enable nested arrow * [`1e4320b64`](https://github.com/siderolabs/talos/commit/1e4320b64e2477a55f808c6b8720b0779088d0f8) chore: add support for rockpi 4A and 4B * [`d1869d948`](https://github.com/siderolabs/talos/commit/d1869d948c84cf7191819eddac9c2aa27b365eb9) docs: update to Sidero Metal, mention clusterctl * [`18d0038ec`](https://github.com/siderolabs/talos/commit/18d0038ecaa2cf43164f72f3acad5445e395b37e) fix: avoid panic in DHCPv6 operator on nil dereference * [`9e3d438db`](https://github.com/siderolabs/talos/commit/9e3d438db461529abf3dfa6ef750b4fa4a9125ec) docs: fix code fence formatting * [`b3f1bb2cf`](https://github.com/siderolabs/talos/commit/b3f1bb2cff544a35f767b32ca8ca1d13b83c535e) fix: add support for FAT12/16 filesystems * [`8619f95c5`](https://github.com/siderolabs/talos/commit/8619f95c5c7779815a87118cbb0a1e493251355d) chore: bump dependencies * [`8c4f72004`](https://github.com/siderolabs/talos/commit/8c4f720048c0187b203ca869befd759249bac79f) docs: override sitemap.xml to only include latest results * [`5192ba4e2`](https://github.com/siderolabs/talos/commit/5192ba4e2314c05e107adcc0a2a71a65ec35bfc3) docs: fix a typo in QEMU VM setup guide * [`663e3e879`](https://github.com/siderolabs/talos/commit/663e3e8796c3f501275fdd7836687b811318b685) refactor: change the stages for embed files generation * [`19bf12af0`](https://github.com/siderolabs/talos/commit/19bf12af07aaf6b54d08027676d8a01b4dd4ed29) fix: enable IPv6 in Docker-based Talos clusters * [`3889a5839`](https://github.com/siderolabs/talos/commit/3889a583970c73ea4c6089b1fe8438b183ec756e) docs: update config.yaml, storage.md, digital-rebar.md

### Changes from siderolabs/extras
2 commits

* [`ac3b9a4`](https://github.com/siderolabs/extras/commit/ac3b9a4be9bc102583f9a8cf37a53f13916d4ce7) chore: bump pkgs * [`d4f8e88`](https://github.com/siderolabs/extras/commit/d4f8e886147749e29026943cff3f5c701aaadf00) chore: update references after org rename

### Changes from siderolabs/pkgs
31 commits

* [`95f4418`](https://github.com/siderolabs/pkgs/commit/95f4418db567226338ba8ab629ace0de39811cc8) chore: bump kernel to 5.15.35 * [`201af71`](https://github.com/siderolabs/pkgs/commit/201af71e96b176033854f3386b4160c3a38d4d1b) chore: bump tools and bldr * [`3de14d7`](https://github.com/siderolabs/pkgs/commit/3de14d725c18f09e05a7db1cf0b6c424f784e977) chore: enable xfs quota support * [`6955fd0`](https://github.com/siderolabs/pkgs/commit/6955fd003aeff46d6d51d4d5c0e9ba64dccbeb26) chore: bump raspberrypi-firmware to 1.20220331 * [`5b498d8`](https://github.com/siderolabs/pkgs/commit/5b498d846d032f625048a5bd4ad2ce23429c5f6d) chore: bump linux-firmware 20220401 * [`9cda5c0`](https://github.com/siderolabs/pkgs/commit/9cda5c0542555c8dc45b34956f50ba721ac2eb05) chore: bump kernel to 5.15.34 * [`8b48af6`](https://github.com/siderolabs/pkgs/commit/8b48af678020d203c065bac750a633f686eaafce) chore: bump tools * [`ff13660`](https://github.com/siderolabs/pkgs/commit/ff1366042afbe1f7fede53f5fc68d7e51d73e613) chore: bump kernel to 5.15.33 * [`415020f`](https://github.com/siderolabs/pkgs/commit/415020fc27129aff334f3a6cd76a60f1d1064e79) chore: bump eudev, remove non-relevant default rules * [`6691342`](https://github.com/siderolabs/pkgs/commit/6691342a3a270954f87663a3b5efb0cf61b19979) chore: add rockpi4c * [`5bd5fad`](https://github.com/siderolabs/pkgs/commit/5bd5fad8fb2aae865797fa1f7374e82bce169067) chore: build u-boot spi image for rockpi * [`4dace49`](https://github.com/siderolabs/pkgs/commit/4dace49282b610d54b5b39917598a80ac3e1ce6a) fix: ipxe prompt arm64 * [`6041fd7`](https://github.com/siderolabs/pkgs/commit/6041fd7963ca910a743c4b69f4fd8b9416a549af) chore: update to use latest tools (specifically go 1.18) * [`4b3e70e`](https://github.com/siderolabs/pkgs/commit/4b3e70e783906cf8b12b467d1a046ddeab695b94) chore: upstream u-boot for jetson nano * [`cc1c8c7`](https://github.com/siderolabs/pkgs/commit/cc1c8c7062c77d352f743fe4735bae5c39b00356) feat: update runc to 1.1.1 * [`3baf4e4`](https://github.com/siderolabs/pkgs/commit/3baf4e4e1fda9ead732bee3578fc55f4f846d48a) chore: enable random trust CPU * [`df31920`](https://github.com/siderolabs/pkgs/commit/df319204730f890f35740837f2d6878a27f5728c) chore: disable sound * [`c27751b`](https://github.com/siderolabs/pkgs/commit/c27751b9f811d4b52701031c26a741333b45cbe9) chore: bump nvidia drivers to 510.60.02 * [`ba98e20`](https://github.com/siderolabs/pkgs/commit/ba98e20d12daa200343869444a568fec231ed239) chore: bump kernel to 5.15.32 * [`a76edfd`](https://github.com/siderolabs/pkgs/commit/a76edfdf941455237f8f16b7a833233257ae63a4) feat: update containerd to 1.6.2 * [`0c38670`](https://github.com/siderolabs/pkgs/commit/0c38670333f788946090e42897b44871ac179ed1) chore: bump kernel to 5.15.31 * [`bc4fb0c`](https://github.com/siderolabs/pkgs/commit/bc4fb0c2619e960d84984696aeb7e7e9368e38e9) chore: org update * [`41f291d`](https://github.com/siderolabs/pkgs/commit/41f291df5806b832c53ee6e042d3561a1bb52582) feat: update Flannel CNI to 1.0.1 * [`58603ba`](https://github.com/siderolabs/pkgs/commit/58603bae512a70c5206d9fe4394139c5aa0f757c) chore: bump kernel to 5.15.30 * [`d3bb262`](https://github.com/siderolabs/pkgs/commit/d3bb262acb78831dd3bf3ee57dc02fb6f628e78a) chore: bump kernel to 5.15.29 * [`76a24b5`](https://github.com/siderolabs/pkgs/commit/76a24b5c9727b17f900331093c5bab86ba49f61e) chore: update openssl to 1.1.1n * [`490c7b7`](https://github.com/siderolabs/pkgs/commit/490c7b77052d182e09e25abe77ee27b4b54d7c7a) chore: enable aarch64 NVIDIA drivers * [`b794b7a`](https://github.com/siderolabs/pkgs/commit/b794b7a78c62a418edab4759a5f7bb7e0bd83dbe) chore: bump linux-firmware to 20220310 * [`acda207`](https://github.com/siderolabs/pkgs/commit/acda20721dea1fa6af611a260c3a320f52a8ee16) chore: bump kernel to 5.15.28 * [`e0fec11`](https://github.com/siderolabs/pkgs/commit/e0fec11a010e3958a617d7417be3a69fe43ba1b5) chore: bump nvidia driver to 510.54 * [`0407f05`](https://github.com/siderolabs/pkgs/commit/0407f057edb8b96a7e51c5222f5b2ce171eb11c6) chore: bump kernel to 5.15.27

### Changes from siderolabs/tools
11 commits

* [`8c1f801`](https://github.com/siderolabs/tools/commit/8c1f8012f2d399bc119a0a35869e6bfd0013a7a8) chore: bump coreutils to 9.1 * [`533d5c9`](https://github.com/siderolabs/tools/commit/533d5c9c05e4d8b4852e4f0d86d94fdeb0fddbde) chore: bump git to 2.35.2 * [`a15cbee`](https://github.com/siderolabs/tools/commit/a15cbee68e65e6c5835a027879349f8fb6a0fa58) chore: bump go to 1.18.1 * [`718ec10`](https://github.com/siderolabs/tools/commit/718ec10e0d80fceb46a93ad602cca0af25813f51) chore: enable conform * [`a60a332`](https://github.com/siderolabs/tools/commit/a60a33251d9bea2606b33f0a616a1da21e5361e9) chore: bump xz and gzip * [`c8a3d4d`](https://github.com/siderolabs/tools/commit/c8a3d4d894fd584ad8ca66c6b9864c447f87eab9) chore: update go to 1.18 * [`1684fdc`](https://github.com/siderolabs/tools/commit/1684fdce5f46cf09401ffb28652f820722bf2d37) chore: bump expat to 2.4.8 * [`7f5e44c`](https://github.com/siderolabs/tools/commit/7f5e44c1ed984732c5ab9bd22fec7d934829f2be) chore: bump zlib to 1.2.12 * [`bfc99ca`](https://github.com/siderolabs/tools/commit/bfc99cae42ef06cf9ca30e5a5fd0771f64115cbd) chore: rename org * [`99be089`](https://github.com/siderolabs/tools/commit/99be089c5f17500146e7345f3228c52b2b61a9be) chore: update openssl to 1.1.1n * [`b63872b`](https://github.com/siderolabs/tools/commit/b63872bb8dba101a519ea2579b0e37f23b92e0e9) chore: update golang to 1.17.8

### Changes from talos-systems/go-blockdevice
2 commits

* [`d9c3a27`](https://github.com/talos-systems/go-blockdevice/commit/d9c3a273886113e24809ef1e9930fc982318217d) feat: support probing FAT12/FAT16 filesystems * [`b374eb4`](https://github.com/talos-systems/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default

### Dependency Changes * **cloud.google.com/go/compute** v1.5.0 -> v1.6.0 * **github.com/BurntSushi/toml** v1.0.0 -> v1.1.0 * **github.com/aws/aws-sdk-go** v1.43.8 -> v1.43.41 * **github.com/containernetworking/plugins** v1.1.0 -> v1.1.1 * **github.com/cosi-project/runtime** 264f8fcd1a4f -> 639b4a2e6120 * **github.com/docker/distribution** v2.8.0 -> v2.8.1 * **github.com/docker/docker** v20.10.12 -> v20.10.14 * **github.com/gdamore/tcell/v2** f057f0a857a1 -> v2.5.1 * **github.com/google/nftables** 211824995dcb -> 950e408d48c6 * **github.com/insomniacslk/dhcp** 3c283ff8b7dd -> 12fbdcb11b41 * **github.com/jsimonetti/rtnetlink** v1.1.0 -> v1.2.0 * **github.com/rivo/tview** 96063d6082f3 -> 9994674d60a8 * **github.com/rs/xid** v1.3.0 -> v1.4.0 * **github.com/siderolabs/extras** v1.0.0 -> v1.1.0-alpha.0-1-gac3b9a4 * **github.com/siderolabs/pkgs** v1.0.0-6-g7c293d5 -> v1.1.0-alpha.0-28-g95f4418 * **github.com/siderolabs/tools** v1.0.0-1-g4c77d96 -> v1.1.0-alpha.0-10-g8c1f801 * **github.com/spf13/cobra** v1.3.0 -> v1.4.0 * **github.com/stretchr/testify** v1.7.0 -> v1.7.1 * **github.com/talos-systems/go-blockdevice** v0.3.1 -> d9c3a2738861 * **github.com/vishvananda/netlink** 650dca95af54 -> v1.2.0-beta * **github.com/vmware-tanzu/sonobuoy** v0.56.2 -> v0.56.4 * **github.com/vmware/vmw-guestinfo** cc1fd90d572c -> 510905f0efa3 * **go.etcd.io/etcd/api/v3** v3.5.2 -> v3.5.3 * **go.etcd.io/etcd/client/pkg/v3** v3.5.2 -> v3.5.3 * **go.etcd.io/etcd/client/v3** v3.5.2 -> v3.5.3 * **go.etcd.io/etcd/etcdutl/v3** v3.5.2 -> v3.5.3 * **golang.org/x/net** 27dd8689420f -> 290c469a71a5 * **golang.org/x/sys** 4e6760a101f9 -> 33da011f77ad * **golang.org/x/term** 03fcf44c2211 -> e5f449aeb171 * **golang.org/x/time** 0e9765cccd65 -> 583f2d630306 * **golang.zx2c4.com/wireguard/wgctrl** fde48d68ee68 -> fec8f2be4827 * **google.golang.org/grpc** v1.44.0 -> v1.45.0 * **google.golang.org/protobuf** v1.27.1 -> v1.28.0 * **k8s.io/api** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/apimachinery** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/apiserver** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/client-go** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/component-base** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/cri-api** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/kubectl** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/kubelet** v0.23.5 -> v0.24.0-beta.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.63 -> v1.2.64 Previous release can be found at [v1.0.0](https://github.com/siderolabs/talos/releases/tag/v1.0.0) ## [Talos 1.1.0-alpha.0](https://github.com/siderolabs/talos/releases/tag/v1.1.0-alpha.0) (2022-04-01) Welcome to the v1.1.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/siderolabs/talos/issues. ### Component Updates * Kubernetes: 1.24.0-beta.0 * Flannel: 0.17.0 * runc: 1.1.1 ### Contributors * Andrey Smirnov * Noel Georgi * Spencer Smith * Tim Jones * Andrew Rynhard * Dmitriy Matrenichev * Steve Francis * Artem Chernyshev * Caleb Woodbine * Daniel Höxtermann * Jori Huisman * Nico Berlee * Serge Logvinov * Seán C McCord * Suraj Shirvankar * Tomasz Zurkowski ### Changes
90 commits

* [`e860312df`](https://github.com/siderolabs/talos/commit/e860312df099fa51422a3361982790fb457c1558) release(v1.1.0-alpha.0): prepare release * [`2ca5279e5`](https://github.com/siderolabs/talos/commit/2ca5279e56d154fdf21fab7ed5c73edb30494560) fix: retry manifest updates in upgrade-k8s * [`eeb756168`](https://github.com/siderolabs/talos/commit/eeb756168f31c8e7a1e0cb2f80e1ae2bc2eed0a9) feat: use kexec when resetting a node * [`1ed1f73e5`](https://github.com/siderolabs/talos/commit/1ed1f73e511f4a5cf4d1db5f97422cf1eb088fda) test: bump CAPI to 1.1.3 * [`2ee1d2c72`](https://github.com/siderolabs/talos/commit/2ee1d2c72085df41ec0355bac0d33bedcb4f2786) feat: update Kuberentes to 1.24.0-beta.0 * [`c26fa4ccc`](https://github.com/siderolabs/talos/commit/c26fa4ccc1e109c889c01384422f88387ad512a2) test: push GITHUB_TOKEN to the e2e-aws/gcp steps * [`95d900de7`](https://github.com/siderolabs/talos/commit/95d900de7799cfa9d0a16049586ba246bddb09d0) feat: use kubeconfig env var * [`0b407dd17`](https://github.com/siderolabs/talos/commit/0b407dd17e9515fecd8083fd5ac1fc84f6085106) feat: add dhcp-v6 NTP/DHCP-DUID * [`a140a6bad`](https://github.com/siderolabs/talos/commit/a140a6bad74bcf34e62e13b6efa63a17741eb5b1) docs: update releases shortcode in upgrade guide * [`12931dced`](https://github.com/siderolabs/talos/commit/12931dcedd38c407a2a03f692d910853130986db) fix: align partitions on 1M boundary * [`37f868e37`](https://github.com/siderolabs/talos/commit/37f868e37454f63a4dfe38d94dbbeef5bb40a2a8) fix: validate empty TLS config for registries * [`ca8b9c0a3`](https://github.com/siderolabs/talos/commit/ca8b9c0a3a15898d9562a6f22aded138d6c3ed7f) feat: update Kubernetes to 1.24.0-alpha.4 * [`d9ec6b215`](https://github.com/siderolabs/talos/commit/d9ec6b2151e94c94eea44771e455555eaf1f257a) chore: drop dirty from abbreviated tag * [`08624fd0b`](https://github.com/siderolabs/talos/commit/08624fd0b12039e5a77ce43f14df65a6c95f7a39) docs: add banner to main page * [`fc23c7a59`](https://github.com/siderolabs/talos/commit/fc23c7a5952d87a51f29d61ead585bf060eeab1c) test: bump versions for upgrade tests * [`4bfe68610`](https://github.com/siderolabs/talos/commit/4bfe686105d5734b282f4817673972b71954e620) feat: update runc to 1.1.1 * [`b315ed953`](https://github.com/siderolabs/talos/commit/b315ed95327a9b7cfb1f83a9da02e96bafecbb1d) chore: use go:embed instead of ldflags * [`a5d64fc81`](https://github.com/siderolabs/talos/commit/a5d64fc814f122fb7e282b97283a46ac0e5d6709) feat: update Flannel to 0.17.0 * [`6d6eb3f6a`](https://github.com/siderolabs/talos/commit/6d6eb3f6a52626c8c94a75439133e7bc22b25e60) docs: fork docs for 1.1 * [`1d55f05d1`](https://github.com/siderolabs/talos/commit/1d55f05d11e5a03a8de0e7ce5ec0167971b03135) docs: update index page * [`ad6b7ec1a`](https://github.com/siderolabs/talos/commit/ad6b7ec1a4347753488de3ab5813947f01967078) fix: enable etcd consistency on check startup * [`65a31f753`](https://github.com/siderolabs/talos/commit/65a31f7531a629b29fbf86ddcbaba20767475924) docs: re-add GA token * [`741c04832`](https://github.com/siderolabs/talos/commit/741c048320b931228336034ad17de10272ff5a77) docs: mark 1.0 docs as latest * [`e97433c8a`](https://github.com/siderolabs/talos/commit/e97433c8a37ca504577355d98c917e083aaedafe) docs: update jetson nano * [`6665e0f00`](https://github.com/siderolabs/talos/commit/6665e0f00c1c5d45123eb28d8755d0815af4822a) docs: code block copying * [`c41f2b216`](https://github.com/siderolabs/talos/commit/c41f2b216717db80e44654f54080a9d462946d45) docs: update whats-new-v1.0 * [`0a36fbbf3`](https://github.com/siderolabs/talos/commit/0a36fbbf3ca579becd0a7f2e5a9715ff4196e8ae) docs: add release notes for 1.0 * [`bd0035f6a`](https://github.com/siderolabs/talos/commit/bd0035f6a285f8b7e4c7c0b5013a271a8d18c5f4) docs: add NVIDIA docs * [`efa3f2898`](https://github.com/siderolabs/talos/commit/efa3f289853a47ae0d4bca5dbf656e527cf312dd) fix: correctly find partitions with config data (`metal-iso`) * [`9ebeec0d0`](https://github.com/siderolabs/talos/commit/9ebeec0d0ea4dd3cc1ba3b7171fe0a9bda943fe8) docs: fix incorrect path for talosconfig * [`9fef4540e`](https://github.com/siderolabs/talos/commit/9fef4540e1c7a7deb5d4745d3de17c6e5cc45369) docs: fix non-latest download links * [`f8ef6a081`](https://github.com/siderolabs/talos/commit/f8ef6a081e055637a5652366a6e344b6df911871) docs: add rook ceph configuration guide * [`e2666f58f`](https://github.com/siderolabs/talos/commit/e2666f58f5835db6ff8802b2370a480d8afcd8fc) chore: bump kernel to 5.15.32 * [`957b2f233`](https://github.com/siderolabs/talos/commit/957b2f233c4b81eacdb5a3190c0070fa36ef0d82) chore: bump dependencies * [`0fd2aa08b`](https://github.com/siderolabs/talos/commit/0fd2aa08bd70d1c869e0dca136ca0c487bfcdefe) fix: correctly escape '.' in volume names * [`108fd03a7`](https://github.com/siderolabs/talos/commit/108fd03a72534cebbab7c09d63051021483566ac) fix: give up virtual IPs before the kubelet workloads are shut down * [`856e1333d`](https://github.com/siderolabs/talos/commit/856e1333dcfb8c0244ca8ead415025b32a4819fc) fix: use 'localhost' endpoint in docker provisioner on Windows * [`c5da38609`](https://github.com/siderolabs/talos/commit/c5da386092185fe4ed4173b08f95eac4e435ff99) docs: use variables and templates in the docs * [`4c83847b9`](https://github.com/siderolabs/talos/commit/4c83847b9091a4e8968544a515632a3391c06cd0) docs: target search results * [`67fb72d96`](https://github.com/siderolabs/talos/commit/67fb72d96db1cb772392dcab9b5a3a08ee50ff03) docs: add algolia versions to all content * [`5344d6e7c`](https://github.com/siderolabs/talos/commit/5344d6e7ce2b7febc6109acc566cf49346eca6d9) docs: fix extension service `path` dependency * [`9b9191c5e`](https://github.com/siderolabs/talos/commit/9b9191c5e7a4a03bb7fa271ab49b52874e63ee31) fix: increase intiial window and connection window sizes * [`7a88a0224`](https://github.com/siderolabs/talos/commit/7a88a0224155755a64c911165bf25bff775e1ec2) docs: show archived/pre-release banner based on version * [`e403470bf`](https://github.com/siderolabs/talos/commit/e403470bfefe7af0217d91cb18d900b7046254f9) docs: filter algolia results by latest * [`0497d5f9f`](https://github.com/siderolabs/talos/commit/0497d5f9fee404f68d09c0c500cb446126cfc6aa) docs: tag latest docs for search * [`a25425483`](https://github.com/siderolabs/talos/commit/a25425483518adc5bdd575c5fb8cc1b3464444ea) feat: update containerd to 1.6.2, Linux to 5.15.31 * [`9b6422fcc`](https://github.com/siderolabs/talos/commit/9b6422fcc39c2f4e0723c0db0b6aefe3e4fc8267) feat: update CoreDNS to 1.9.1 * [`020856f80`](https://github.com/siderolabs/talos/commit/020856f80dd93fb47170351c083602ffd516d113) docs: remove second search bar * [`5f27f4c63`](https://github.com/siderolabs/talos/commit/5f27f4c6384e9bb6df4fc969c3a318ad3052cf3f) docs: update asset links * [`9ff42b432`](https://github.com/siderolabs/talos/commit/9ff42b43202bb59845439a88014011ff002a7770) docs: fix redirects for /docs URLs * [`7283efd56`](https://github.com/siderolabs/talos/commit/7283efd568d35e6d2c68aa2bc101a7af86db8c62) chore: update the talosctl CNI download url * [`e0eee7fcc`](https://github.com/siderolabs/talos/commit/e0eee7fcc68f03243ae3248f84d50eb278998e07) test: use clusterctl.yaml overrides after org rename * [`73966f51e`](https://github.com/siderolabs/talos/commit/73966f51e83b7f166e4f7fe013bfed36e9b9a15a) docs: fix extensions * [`f9766edb5`](https://github.com/siderolabs/talos/commit/f9766edb52d6a029d12ac5d74fdb45b6294be058) docs: remove empty doc file * [`e06e1473b`](https://github.com/siderolabs/talos/commit/e06e1473b02cea088499c25f48a9b5e2b75cf879) feat: update golangci-lint to 1.45.0 and gofumpt to 0.3.0 * [`a92c614b2`](https://github.com/siderolabs/talos/commit/a92c614b2f712fb046fb40e00b37773d1390df71) docs: add enterprise link to docs header * [`0ae7174ba`](https://github.com/siderolabs/talos/commit/0ae7174ba3a6c1674c77cf074087a68915e3e612) docs: update search settings and redirects * [`883d401f9`](https://github.com/siderolabs/talos/commit/883d401f9f62229305c2e24f58a0bb0e2e4bb409) chore: rename github organization to siderolabs * [`d1294d014`](https://github.com/siderolabs/talos/commit/d1294d014f5bee7fc1b5dfd6865f22b22f18f5f1) chore: add day-two tests for e2e-qemu * [`a6240e4b6`](https://github.com/siderolabs/talos/commit/a6240e4b67060357c4250e7e5a3a7960408f7c08) feat: update Linux to 5.15.30 * [`e3fda049f`](https://github.com/siderolabs/talos/commit/e3fda049fee62f3c5cef4ae08eaf848826a6dbed) docs: overhaul all the docs * [`f47750726`](https://github.com/siderolabs/talos/commit/f477507262041a24def6ac9b32fa92d276d4d4e6) fix: the etcd recovery client and tests * [`69e07cddc`](https://github.com/siderolabs/talos/commit/69e07cddc77d6ff2c2477ec64f860ef824132000) fix: trigger properly `udevd` on types and actions * [`47d0e629d`](https://github.com/siderolabs/talos/commit/47d0e629d48930f6cb02dff32469bcb34440c73c) fix: clean up custom udev rules if the config is cleared * [`b6691b350`](https://github.com/siderolabs/talos/commit/b6691b35085e4e614752b60441c17fe39fe15928) chore: bump dependencies * [`27af5d41c`](https://github.com/siderolabs/talos/commit/27af5d41c6c58f4d2fc2f5c222d9de39539de1c0) feat: pause the boot process on some failures instead of rebooting * [`58cb9db1e`](https://github.com/siderolabs/talos/commit/58cb9db1e2b3d8fa86c0db0cf38c9f21a843da9d) feat: allow hardlinks in the system extension images * [`1e982808f`](https://github.com/siderolabs/talos/commit/1e982808fbac0a7f897bafacde348c5d83db38b2) fix: ignore pod CIDRs for kubelet node IPs * [`5e0c80f61`](https://github.com/siderolabs/talos/commit/5e0c80f6168ac8a171e35e0c3ee53d959c2dd80d) fix: ignore connection reset errors on k8s upgrade * [`c156580a3`](https://github.com/siderolabs/talos/commit/c156580a386e19d020b550b8459af339f440bf3e) fix: split regular network operation configuration and virtual IP * [`cd4d4c605`](https://github.com/siderolabs/talos/commit/cd4d4c6054107cd6c9274acb2abb4a045368a9fc) feat: relax extensions file structure validation * [`50594ab1a`](https://github.com/siderolabs/talos/commit/50594ab1a7e4d7d025f41873aaa1bf6954827d3e) fix: ignore terminated pods in pod health checks * [`9d69fb6b4`](https://github.com/siderolabs/talos/commit/9d69fb6b40f47061ff96bd7fb3952aa9c16ed601) feat: update Kubernetes to 1.23.5 * [`327ce5aba`](https://github.com/siderolabs/talos/commit/327ce5aba352054837c9cc03c1ba3993a1d18158) fix: invert the condition to skip kubelet kernel checks * [`cf85b3f07`](https://github.com/siderolabs/talos/commit/cf85b3f07ccc3a6845f82f7853da298f5fce62a3) docs: update cilium inline install * [`84ee1795d`](https://github.com/siderolabs/talos/commit/84ee1795dc914574d299b1b0f1ede42bfaee110a) docs: update logo * [`cc7719c9d`](https://github.com/siderolabs/talos/commit/cc7719c9d014ca8c16828a84ccc95c0344bb34ed) docs: improve comments in security proto * [`caf800fe8`](https://github.com/siderolabs/talos/commit/caf800fe843aca5d3559ae5baf08b59db21cccd7) feat: implement D-Bus systemd-compatible shutdown for kubelet * [`6bec08429`](https://github.com/siderolabs/talos/commit/6bec084299062ec6df6e319d4a83313de97e3c67) feat: add talosctl completions to copy, usage, logs, restart and service * [`355b1a4be`](https://github.com/siderolabs/talos/commit/355b1a4bedd6755dbbaa9e98505f5c8540520bb5) fix: refresh etcd certs on startup/join * [`d256b5c5e`](https://github.com/siderolabs/talos/commit/d256b5c5e46ac87edf5681611eeda95fe091d922) docs: fix spelling mistakes * [`5fdedae20`](https://github.com/siderolabs/talos/commit/5fdedae208bfa561b7ca1a04f140adcee3deb565) chore: bump kernel to 5.15.28 * [`18a21b5f2`](https://github.com/siderolabs/talos/commit/18a21b5f24baeea5b876d99b29f5397cc3617399) chore: add dependency images-essential -> images * [`714e5eca6`](https://github.com/siderolabs/talos/commit/714e5eca63ee0dd4a81ca5937081779829092111) chore: bump dependencies * [`58be4067e`](https://github.com/siderolabs/talos/commit/58be4067e6ddc7ba3a346469c30c435b560df377) docs: update README.md * [`c5fb20930`](https://github.com/siderolabs/talos/commit/c5fb20930555e5e31ea01e75aa3690d2cf628f29) docs: add loki note * [`f448cb4f3`](https://github.com/siderolabs/talos/commit/f448cb4f3c1620669fa34250e39aeec0e4002d37) feat: bump boot partition size to 1000 MiB * [`a095acb09`](https://github.com/siderolabs/talos/commit/a095acb09f225bce0e1c17f86576400549789608) chore: fix equinixMetal platform name * [`2a7f9a445`](https://github.com/siderolabs/talos/commit/2a7f9a4457bcb18e66b9ee6eb0ff49a290c381ce) fix: check for IPv6 before applying accept_ra * [`59681b8c9`](https://github.com/siderolabs/talos/commit/59681b8c9a47701092c7287c2375123134d3f9ba) fix: backport fixes from release-1.0 branch

### Changes from siderolabs/extras
1 commit

* [`d4f8e88`](https://github.com/siderolabs/extras/commit/d4f8e886147749e29026943cff3f5c701aaadf00) chore: update references after org rename

### Changes from siderolabs/pkgs
18 commits

* [`4b3e70e`](https://github.com/siderolabs/pkgs/commit/4b3e70e783906cf8b12b467d1a046ddeab695b94) chore: upstream u-boot for jetson nano * [`cc1c8c7`](https://github.com/siderolabs/pkgs/commit/cc1c8c7062c77d352f743fe4735bae5c39b00356) feat: update runc to 1.1.1 * [`3baf4e4`](https://github.com/siderolabs/pkgs/commit/3baf4e4e1fda9ead732bee3578fc55f4f846d48a) chore: enable random trust CPU * [`df31920`](https://github.com/siderolabs/pkgs/commit/df319204730f890f35740837f2d6878a27f5728c) chore: disable sound * [`c27751b`](https://github.com/siderolabs/pkgs/commit/c27751b9f811d4b52701031c26a741333b45cbe9) chore: bump nvidia drivers to 510.60.02 * [`ba98e20`](https://github.com/siderolabs/pkgs/commit/ba98e20d12daa200343869444a568fec231ed239) chore: bump kernel to 5.15.32 * [`a76edfd`](https://github.com/siderolabs/pkgs/commit/a76edfdf941455237f8f16b7a833233257ae63a4) feat: update containerd to 1.6.2 * [`0c38670`](https://github.com/siderolabs/pkgs/commit/0c38670333f788946090e42897b44871ac179ed1) chore: bump kernel to 5.15.31 * [`bc4fb0c`](https://github.com/siderolabs/pkgs/commit/bc4fb0c2619e960d84984696aeb7e7e9368e38e9) chore: org update * [`41f291d`](https://github.com/siderolabs/pkgs/commit/41f291df5806b832c53ee6e042d3561a1bb52582) feat: update Flannel CNI to 1.0.1 * [`58603ba`](https://github.com/siderolabs/pkgs/commit/58603bae512a70c5206d9fe4394139c5aa0f757c) chore: bump kernel to 5.15.30 * [`d3bb262`](https://github.com/siderolabs/pkgs/commit/d3bb262acb78831dd3bf3ee57dc02fb6f628e78a) chore: bump kernel to 5.15.29 * [`76a24b5`](https://github.com/siderolabs/pkgs/commit/76a24b5c9727b17f900331093c5bab86ba49f61e) chore: update openssl to 1.1.1n * [`490c7b7`](https://github.com/siderolabs/pkgs/commit/490c7b77052d182e09e25abe77ee27b4b54d7c7a) chore: enable aarch64 NVIDIA drivers * [`b794b7a`](https://github.com/siderolabs/pkgs/commit/b794b7a78c62a418edab4759a5f7bb7e0bd83dbe) chore: bump linux-firmware to 20220310 * [`acda207`](https://github.com/siderolabs/pkgs/commit/acda20721dea1fa6af611a260c3a320f52a8ee16) chore: bump kernel to 5.15.28 * [`e0fec11`](https://github.com/siderolabs/pkgs/commit/e0fec11a010e3958a617d7417be3a69fe43ba1b5) chore: bump nvidia driver to 510.54 * [`0407f05`](https://github.com/siderolabs/pkgs/commit/0407f057edb8b96a7e51c5222f5b2ce171eb11c6) chore: bump kernel to 5.15.27

### Changes from siderolabs/tools
2 commits

* [`99be089`](https://github.com/siderolabs/tools/commit/99be089c5f17500146e7345f3228c52b2b61a9be) chore: update openssl to 1.1.1n * [`b63872b`](https://github.com/siderolabs/tools/commit/b63872bb8dba101a519ea2579b0e37f23b92e0e9) chore: update golang to 1.17.8

### Changes from talos-systems/go-blockdevice
1 commit

* [`b374eb4`](https://github.com/talos-systems/go-blockdevice/commit/b374eb48148dc92a82d8bf9540432bb8531f73f3) fix: align partition to 1M boundary by default

### Dependency Changes * **github.com/aws/aws-sdk-go** v1.43.8 -> v1.43.26 * **github.com/containernetworking/plugins** v1.1.0 -> v1.1.1 * **github.com/docker/distribution** v2.8.0 -> v2.8.1 * **github.com/docker/docker** v20.10.12 -> v20.10.14 * **github.com/jsimonetti/rtnetlink** v1.1.0 -> v1.1.1 * **github.com/rivo/tview** 96063d6082f3 -> 9994674d60a8 * **github.com/rs/xid** v1.3.0 -> v1.4.0 * **github.com/siderolabs/extras** v1.0.0 -> v1.1.0-alpha.0 * **github.com/siderolabs/pkgs** v1.0.0-6-g7c293d5 -> v1.1.0-alpha.0-15-g4b3e70e * **github.com/siderolabs/tools** v1.0.0-1-g4c77d96 -> v1.1.0-alpha.0-1-g99be089 * **github.com/spf13/cobra** v1.3.0 -> v1.4.0 * **github.com/stretchr/testify** v1.7.0 -> v1.7.1 * **github.com/talos-systems/go-blockdevice** v0.3.1 -> b374eb48148d * **github.com/vmware-tanzu/sonobuoy** v0.56.2 -> v0.56.3 * **github.com/vmware/vmw-guestinfo** cc1fd90d572c -> 510905f0efa3 * **golang.org/x/net** 27dd8689420f -> de3da57026de * **golang.org/x/sys** 4e6760a101f9 -> 530d0810a4d0 * **golang.zx2c4.com/wireguard/wgctrl** fde48d68ee68 -> 056925b7df31 * **google.golang.org/grpc** v1.44.0 -> v1.45.0 * **google.golang.org/protobuf** v1.27.1 -> v1.28.0 * **k8s.io/api** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/apimachinery** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/apiserver** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/client-go** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/component-base** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/cri-api** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/kubectl** v0.23.5 -> v0.24.0-beta.0 * **k8s.io/kubelet** v0.23.5 -> v0.24.0-beta.0 Previous release can be found at [v1.0.0](https://github.com/siderolabs/talos/releases/tag/v1.0.0) ## [Talos 0.15.0-alpha.2](https://github.com/talos-systems/talos/releases/tag/v0.15.0-alpha.2) (2022-02-11) Welcome to the v0.15.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Apply Config Enhancements `talosctl apply/patch/edit` cli commands got revamped. Separate flags `--on-reboot`, `--immediate`, `--interactive` were replaced with a single `--mode` flag that can take the following values: - `auto` new mode that automatically applies the configuration in immediate/reboot mode. - `no-reboot` force apply immediately, if not possible, then fail. - `reboot` force reboot with apply config. - `staged` write new machine configuration to STATE, but don't apply it (it will be applied after a reboot). - `interactive` starts interactive installer, only for `apply`. ### Pinned Kubernets Version Command `talosctl gen config` now defaults to Kubernetes version pinning in the generate machine configuration. Previously default was to omit explicit Kubernetes version, so Talos picked up the default version it was built against. Old behavior can be achieved by specifying empty flag value: `--kubernetes-version=`. ### Machine Configuration Talos now preserves machine configuration as it was submitted to the node. ### Machine Configuration Patching `talosctl` commands which accept JSON patches (`gen config`, `cluster create`, `patch machineconfig`) now support multiple patches, loading patches from files with `@file.json` syntax, and support loading from YAML format. ### Platform Support Talos now supports Oracle Cloud. Platform network configuration was rewritten to avoid modifying Talos machine configuration. Network configuration is performed independent of the machine configuration presence, so it works even if Talos is booted in maintenance mode (without machine configuration is platform userdata). ### SBC Support Talos now supports Jetson Nano SBC. ### Static Pods in the Machine Configuration Talos now accepts static pod definitions in the `.machine.pods` key of the machine configuration. Please note that static pod definitions are not validated by Talos. Static pod definitions can be updated without a node reboot. ### System Extensions System extensions allow extending Talos root filesystem, which enables a set of different features, including custom container runtimes, additional firmware, etc. System extensions are only activated during Talos installation (or upgrade), and with system extensions installed, Talos root filesystem is still immutable and read-only. Please see [extensions repository](https://github.com/talos-systems/extensions) and [documentation](https://www.talos.dev/docs/v0.15/guides/system-extensions/) for more information. ### Component Updates * Linux: 5.15.23 * Kubernetes: 1.23.3 * CoreDNS: 1.8.7 * etcd: 3.5.2 * containerd: 1.6.0-rc.0 * runc: 1.1.0 Talos is built with Go 1.17.7 ### Wipe System Kernel Parameter Added new kernel parameter `talos.experimental.wipe=system` which can help resetting system disk for the machine and start over with a fresh installation. See [Resetting a Machine](https://www.talos.dev/docs/v0.15/guides/resetting-a-machine/#kernel-parameter) on how to use it. ### Contributors * Andrey Smirnov * Noel Georgi * Artem Chernyshev * Spencer Smith * Serge Logvinov * Seán C McCord * Florian Klink * Steve Francis * Andrew Rynhard * Anthony Rabbito * Bernard Sébastien * Charlie Haley * Eric Wohltman * Niklas Metje * Philipp Sauter * Shahar Naveh * Tim Jones * nebulait ### Changes
137 commits

* [`1e9f0ad4c`](https://github.com/talos-systems/talos/commit/1e9f0ad4c6abcfb5244f0d4159b7085b387f2cc1) feat: update Go to 1.17.7, Linux to 5.15.23 * [`fef99892d`](https://github.com/talos-systems/talos/commit/fef99892d5ba11c9c87d047e23fb7023de5116a6) chore: pin kubernetes version to `talosctl gen config` * [`bcf928134`](https://github.com/talos-systems/talos/commit/bcf928134c8d1a17d69d425061350040d3ed15a4) feat: udev extensions support * [`47619f832`](https://github.com/talos-systems/talos/commit/47619f8320f8e03246ffa43d19dcd759b1d9511c) docs: update system extensions guide with grammar fixes * [`2bcceb6e4`](https://github.com/talos-systems/talos/commit/2bcceb6e437b5e30f856ea495eccdb0ab4d5e1ca) chore: disable TIPC and B.A.T.M.A.N * [`c6bca1b33`](https://github.com/talos-systems/talos/commit/c6bca1b33b5b0522ee7b997c2bcc5afadc991a94) docs: add guide on system extensions * [`492b156da`](https://github.com/talos-systems/talos/commit/492b156dabca6552002881f9d8ca57b02a04add2) feat: implement static pods via machine configuration * [`6fadfa8db`](https://github.com/talos-systems/talos/commit/6fadfa8dbcc22b80dc83ed477f81f5c55727298c) fix: parse properly IPv6 address in the cmdline `ip=` arg * [`d991f3982`](https://github.com/talos-systems/talos/commit/d991f3982c329e97c78d068eae0abf02020d21a9) chore: update the kernel with IGC driver enabled * [`cbc9610be`](https://github.com/talos-systems/talos/commit/cbc9610be66f4b2552e2c7374118cfa71764a148) feat: sysctl system optimization * [`8b6d6220d`](https://github.com/talos-systems/talos/commit/8b6d6220d3799cb79cd66267017b54d0a09e2c63) fix: parse interface ip correctly (nocloud) * [`54632b1be`](https://github.com/talos-systems/talos/commit/54632b1be7b08440b562dfb0bf44ef9784317dbf) docs: fix developing Talos docs * [`0da370dfe`](https://github.com/talos-systems/talos/commit/0da370dfefecdba9c981ccafa3255c4dc256d4d2) test: unlock CABPT/CACPPT provider versions * [`df0e388a4`](https://github.com/talos-systems/talos/commit/df0e388a4fa1995124d4e722fc1b8a1bfdffed58) feat: extract firmware part of system extensions into initramfs * [`8899dd349`](https://github.com/talos-systems/talos/commit/8899dd34945105e7276fa453341cc0aa4dbe51d4) chore: add json-tags for SecretsBundle * [`4f391cd5c`](https://github.com/talos-systems/talos/commit/4f391cd5c540a0a955f294d628adc7437b7513b5) chore: bump kernel to 5.15.22 * [`6bd07406e`](https://github.com/talos-systems/talos/commit/6bd07406e1895d190b5bbd9838ee84f85d02cd3f) feat: disable reboots via kexec * [`1e3f2f952`](https://github.com/talos-systems/talos/commit/1e3f2f95275aa5f71abe931015799caaca42bf61) fix: validate kubelet node IP subnets correctly * [`d211bff47`](https://github.com/talos-systems/talos/commit/d211bff47d661697926fece893784519dbf4f8f3) feat: enable accept_ra when IPv6 forwarding * [`930205831`](https://github.com/talos-systems/talos/commit/93020583195d066e879ccb19da38b8cfd6b93e96) chore: update kernel to 5.15.21 * [`c7186ed08`](https://github.com/talos-systems/talos/commit/c7186ed08013efaa9957fe064152ccfca8ec1ab8) chore: bump dependencies * [`9ee470f95`](https://github.com/talos-systems/talos/commit/9ee470f9556462dd3fda656d58358e7ae78f4d47) feat: set /etc/localtime to UTC * [`c34768367`](https://github.com/talos-systems/talos/commit/c347683670d489230a2e87e4f04f05009173aca0) fix: disable auto-tls for etcd * [`9bffc7e8d`](https://github.com/talos-systems/talos/commit/9bffc7e8d5eff6d5ce0b83d627557f4110fc5c58) fix: pass proper sequence to shutdown sequence on ACPI shutdown * [`e47387e41`](https://github.com/talos-systems/talos/commit/e47387e4197974366844b2741cae345666d474da) chore: bump CAPI to 1.0.4 * [`5462f5ed1`](https://github.com/talos-systems/talos/commit/5462f5ed18b7ffe023b3a41f1ac7d9b4ca9b726d) feat: update etcd to 3.5.2 * [`f6fa12e53`](https://github.com/talos-systems/talos/commit/f6fa12e53697c763bd0463d91e92a446eb1ac2f7) docs: update upgrading Talos, Kubernetes, and Docker guides * [`5484579c1`](https://github.com/talos-systems/talos/commit/5484579c1a897f2378aacbef94bd4381d6b8299c) feat: allow link scope routes in the machine config * [`56b83b087`](https://github.com/talos-systems/talos/commit/56b83b08730c13910b0e5eb724decaf27e187047) feat: enable persistence for docker provider * [`949464e4b`](https://github.com/talos-systems/talos/commit/949464e4b6e1e807d9299b451758a6d144725fb1) fix: use leaf certificate in the apid RBAC check * [`446972f21`](https://github.com/talos-systems/talos/commit/446972f2113ada8e6c511ce56f630ec170ef0f26) chore: bump kernel to 5.15.19 * [`fe40e7b1b`](https://github.com/talos-systems/talos/commit/fe40e7b1b39281f9bc14393b8c9db55ab6d6f8cd) feat: drain node on shutdown * [`7f0b3aae0`](https://github.com/talos-systems/talos/commit/7f0b3aae0a37b519623422841e3cbcda8bdd21a1) feat: add multiple config patches, patches from files, YAML support * [`202290be7`](https://github.com/talos-systems/talos/commit/202290be7b9b04ec909d369326d463c3b462eafa) docs: update Kubernetes upgrade video * [`036644f7a`](https://github.com/talos-systems/talos/commit/036644f7a03383922fd8407b1d514c7f79d44d0d) chore: bump kernel to 5.15.18 * [`dcde2c4f6`](https://github.com/talos-systems/talos/commit/dcde2c4f68982974d6e55c52ba0fa8665e7f40b8) chore: update k8s upgrade message * [`1c949335c`](https://github.com/talos-systems/talos/commit/1c949335cc41cc9157e4c7dead44826c99b336f3) docs: add documentation for Hyper-V * [`7f9790912`](https://github.com/talos-systems/talos/commit/7f9790912308dfa88457a6db4f94728e5337c399) fix: clean up containerd state on installer run/validate * [`8b98d8eb3`](https://github.com/talos-systems/talos/commit/8b98d8eb3976cb8e64ffa94cfdf0305216f7dbeb) docs: clarify Filebeat example * [`74c03120c`](https://github.com/talos-systems/talos/commit/74c03120cf1da93d79fd786036e8d296c00c221e) docs: replace Talos upgrades video * [`65e64d425`](https://github.com/talos-systems/talos/commit/65e64d425e0253ae6780d52063d227c47df1ae29) chore: update kernel to stable 5.15.17 * [`4245f72d3`](https://github.com/talos-systems/talos/commit/4245f72d3ff3712742d6d7d6ec3310f40f900c79) feat: add --extra-uefi-search-paths option * [`7ffeb6c2e`](https://github.com/talos-systems/talos/commit/7ffeb6c2e2bef1482b641725e4075c44264e899e) docs: update oracle cloud example * [`151c9df09`](https://github.com/talos-systems/talos/commit/151c9df091f32d00748e7e5effbb2c759916e8b9) chore: add CSI tests for e2e-qemu * [`cdb621c82`](https://github.com/talos-systems/talos/commit/cdb621c82e15026a851bbfb567afd834d88165e7) feat: provide a way to list installed system extensions * [`abfb25812`](https://github.com/talos-systems/talos/commit/abfb2581289c72c9e7bda8bc1f7bc2aa2ba758f7) feat: share `/lib/firmware` across initramfs and rootfs * [`ebec5d4a0`](https://github.com/talos-systems/talos/commit/ebec5d4a0c20fe20aa1fd5d1f9b28c0745a08fe7) feat: support full disk path in the diskSelector * [`831f65a07`](https://github.com/talos-systems/talos/commit/831f65a07f3b0a93ee9f38327dc5b84ce97a3237) fix: close client provider instead of Talos client in the upgrade module * [`0bf161dff`](https://github.com/talos-systems/talos/commit/0bf161dffb8c7805c44a4fb2c3db191dfa901b88) test: add integration test for system extensions * [`7b3962745`](https://github.com/talos-systems/talos/commit/7b3962745625decb720c53ca3b454f65079715f6) fix: handle 404 errors from AWS IMDS correctly * [`85782faa2`](https://github.com/talos-systems/talos/commit/85782faa24772dc9fa757aac3803a196f0325544) feat: update Kubernetes to 1.23.3 * [`c5e5922e5`](https://github.com/talos-systems/talos/commit/c5e5922e536533badcaae568171f1b78cac40105) chore: bump dependencies * [`b3c3ef29b`](https://github.com/talos-systems/talos/commit/b3c3ef29bdf0f21805adf3489972cb92c98c00aa) feat: install system extensions * [`a0889600f`](https://github.com/talos-systems/talos/commit/a0889600fb19f62a2503244c32364808777ffdcc) chore: fix golangci-lint install * [`a50c42980`](https://github.com/talos-systems/talos/commit/a50c42980febfe51ba1e4ce750768f01de8c2d47) fix: use #!/usr/bin/env bash as shebang instead of #!/bin/bash * [`4464b725c`](https://github.com/talos-systems/talos/commit/4464b725c4fea4234961959e884426c384822eab) fix: qemu: always use runtime.GOARCH for CNI bundle * [`e7379c81b`](https://github.com/talos-systems/talos/commit/e7379c81b222341633d6f1011bcdbffa1bf429fc) release(v0.15.0-alpha.1): prepare release * [`58eb3600f`](https://github.com/talos-systems/talos/commit/58eb3600fc44dc2fccaa82322207291ffd807205) fix: enforce reasonable TLS min tls-min-version * [`b8d4c5dfa`](https://github.com/talos-systems/talos/commit/b8d4c5dfad4585c0af52287513176411a79fc20c) fix: use correct error in `kernel_param_spec` Modify call handling * [`4961d6867`](https://github.com/talos-systems/talos/commit/4961d6867cadab5e8b48e73355b23b91d36f70b4) docs: drop talos.interface kernel arg * [`b1e61fa5b`](https://github.com/talos-systems/talos/commit/b1e61fa5b1bcd5affd42b498711b9e3378344c33) chore: update Linux to 5.15.16 * [`d4b844593`](https://github.com/talos-systems/talos/commit/d4b844593587ae3f82efcdbdfe0f24cda4262474) feat: support CRI configuration merging and reimplement registry config * [`f94c8c6e1`](https://github.com/talos-systems/talos/commit/f94c8c6e1c3915c962c331943120bdfd2b76259f) feat: update Kubernetes to 1.23.2 * [`21f497b3e`](https://github.com/talos-systems/talos/commit/21f497b3e20f3b1cc9b744f1787ba80cf396d3e0) feat: install readonly overlay mounts during talos chroot sequence * [`9ad5a67d2`](https://github.com/talos-systems/talos/commit/9ad5a67d21b0788d1b43f1bea8e39c003a4a8ecc) feat: inject platform network configuration as network resources * [`907f8cbfb`](https://github.com/talos-systems/talos/commit/907f8cbfb8ed28cf399b9797230790718fc04a58) docs: fix patch flag * [`caa434426`](https://github.com/talos-systems/talos/commit/caa43442640744a0aa7a17aa1a205f1641e6445a) docs: add documentation on developing Talos * [`16eeb6776`](https://github.com/talos-systems/talos/commit/16eeb677625c0859d73b82948c1a073ba6e17e8d) docs: readme updates * [`3c0737027`](https://github.com/talos-systems/talos/commit/3c0737027b5574581a6461211199274ee709b1da) chore: update release notes * [`6d8bea5d5`](https://github.com/talos-systems/talos/commit/6d8bea5d559b1156f7d0b576b7b5784c25cd3595) feat: jetson nano SoC * [`1d8955ebe`](https://github.com/talos-systems/talos/commit/1d8955ebe43259a5e072b8a89f37cb728b6fcf53) feat: update CoreDNS to 1.8.7 * [`6af83afd5`](https://github.com/talos-systems/talos/commit/6af83afd5aba64ffa7887d62f84c434109b7579b) fix: handle multiple-IP cluster nodes * [`43b2d8137`](https://github.com/talos-systems/talos/commit/43b2d8137116863cfc5ca969c429c13483465b01) chore: bump dependencies * [`529e80f4f`](https://github.com/talos-systems/talos/commit/529e80f4f529f066872b5768cd80eeeb7b766a31) docs: update home page and footer * [`37630e70c`](https://github.com/talos-systems/talos/commit/37630e70ccc9950e139bf7fcfcded6a18d0c7a01) Update twitter link * [`af440919b`](https://github.com/talos-systems/talos/commit/af440919bbaf12f414f04a5a621c1e2d5ed84ae2) fix: avoid panic in config loading/validation * [`4b8e9de59`](https://github.com/talos-systems/talos/commit/4b8e9de599812f82275605a93de7f5c05471f7f5) docs: add guide on adding proprietary kernel modules * [`833dc4169`](https://github.com/talos-systems/talos/commit/833dc4169a9702383930816d13be39f6b81c7a31) docs: rework vmware assets * [`2869b5eea`](https://github.com/talos-systems/talos/commit/2869b5eeacf0b6c96aedcb605bfa8a5f9fb87625) feat: add oraclecloud.com platform support * [`f3ec24beb`](https://github.com/talos-systems/talos/commit/f3ec24bebf0aaa7983228a09b21a67b9a2a098c1) fix: vmware documentation typo * [`2f2bdb26a`](https://github.com/talos-systems/talos/commit/2f2bdb26aa5367066c12a6402af554b7a5a148d6) feat: replace flags with --mode in `apply`, `edit` and `patch` commands * [`b09be2a69`](https://github.com/talos-systems/talos/commit/b09be2a69c6b6f8064a676fc014e6e60ea01a08d) docs: update index.md and sync across versions * [`ca65b918a`](https://github.com/talos-systems/talos/commit/ca65b918a7292ae53d40e410cca4e89be91e4261) docs: add nocloud documentation * [`59437d6d8`](https://github.com/talos-systems/talos/commit/59437d6d8360ad7dd8f801797ab91ac0791270f7) fix: filter down nameservers for docker-based cluster create * [`194eaa6f2`](https://github.com/talos-systems/talos/commit/194eaa6f22249fe4f43958bd897744a2cc57279f) chore: clean up /usr/bin from unneeded files * [`74e727240`](https://github.com/talos-systems/talos/commit/74e7272401ccb75464dd42ed0427d73842af74e1) docs: update office office * [`539af338c`](https://github.com/talos-systems/talos/commit/539af338c4b8f6e4291654f66628c81022aeda72) docs: update vmware docs * [`279a3fda7`](https://github.com/talos-systems/talos/commit/279a3fda7ba24037e06377f01cc495207722caa9) feat: update Go to 1.17.6, containerd to 1.5.9 * [`3d3088941`](https://github.com/talos-systems/talos/commit/3d308894120092fe095b41970d6341362ab80a6b) chore: bump Go dependencies * [`d02d944ec`](https://github.com/talos-systems/talos/commit/d02d944ec767441612b84c164af31bc27c0c0659) chore: provide umarshal from YAML methods for network resource specs * [`2e735714d`](https://github.com/talos-systems/talos/commit/2e735714d9218cbc335d9c418730c146821fb8d4) fix: derive machine-id from node identity * [`d8a2721e1`](https://github.com/talos-systems/talos/commit/d8a2721e129be33f4a3c37be1bf5b89a1cd91685) test: update CAPI components to latest * [`7dff8a53e`](https://github.com/talos-systems/talos/commit/7dff8a53ee7bc37afe9dc216ca8a9113718d76af) fix: ignore missing init.yaml for cluster create * [`f4516c7d8`](https://github.com/talos-systems/talos/commit/f4516c7d847d905b49b4e2127eb86a1f38156d53) chore: bump dependencies * [`944f13221`](https://github.com/talos-systems/talos/commit/944f13221d50694d5c59ace1c12f8769d7ade9ae) chore: fix release pipeline * [`cb548a368`](https://github.com/talos-systems/talos/commit/cb548a368a75ca379209213948518c880b242b0c) release(v0.15.0-alpha.0): prepare release * [`da0b36e61`](https://github.com/talos-systems/talos/commit/da0b36e616f7da7eb0c6791b9cf5e4ee2757f08f) feat: introduce `talos.exp.wipe` kernel param to wipe system disk * [`c079eb32b`](https://github.com/talos-systems/talos/commit/c079eb32bd7fc19d506146e2a9edf5b406e25e02) refactor: use AWS SDK to access AWS metadata service * [`2f4b9d8d6`](https://github.com/talos-systems/talos/commit/2f4b9d8d6d10c0aa753f405282aa99696b923bb4) feat: make machine configuration read-only in Talos (almost) * [`524f83d3d`](https://github.com/talos-systems/talos/commit/524f83d3d8af3857f178c179a9552a5f32b70f47) feat: use official Go SDK to fetch GCP instance metadata * [`d2a7e082c`](https://github.com/talos-systems/talos/commit/d2a7e082c24d0b42820b3ea454329a19178ba0a4) test: retry in discovery tests * [`f4219e530`](https://github.com/talos-systems/talos/commit/f4219e530ca7635ada666ae69071746d698939a8) chore: remove unused methods in AWS platform * [`35bc2940e`](https://github.com/talos-systems/talos/commit/35bc2940e375b99e0d6e22a26a05c25d642bf35a) fix: kexec on RPI4 * [`f235cfbae`](https://github.com/talos-systems/talos/commit/f235cfbaed8b5254e19616bfaaa8b48fd7d32e64) fix: multiple usability fixes * [`b3fbb2f31`](https://github.com/talos-systems/talos/commit/b3fbb2f312d5de0c14ffee567956b868a317aba7) test: don't build all images in the default CI pipeline * [`dac550a50`](https://github.com/talos-systems/talos/commit/dac550a50f4793194e4aeee98702a052925a0e88) docs: fix troubleshooting guide * [`83e8bec6b`](https://github.com/talos-systems/talos/commit/83e8bec6b9d4c0ecc689f45b15d7203bbf9bf0cc) feat: update Linux to 5.15.11 * [`d5a82b37e`](https://github.com/talos-systems/talos/commit/d5a82b37eb147a68ffd08fc8ec800edc92da9f9c) feat: remove `ApplyDynamicConfig` * [`3623da136`](https://github.com/talos-systems/talos/commit/3623da136bde51422ba1aec06e22dea2e3dfa756) feat: provide a way to load Linux kernel modules * [`4d1514add`](https://github.com/talos-systems/talos/commit/4d1514add6e0b972aee26a8ad63ef8f972050d46) docs: update Mayastor deployment process * [`cff1ff6d5`](https://github.com/talos-systems/talos/commit/cff1ff6d5c3a68063ed2c0c063daadf2474cc43f) feat: shell completion for `list`, `read` * [`19728437e`](https://github.com/talos-systems/talos/commit/19728437ead7ab6e95afc8bd7f70be3f861c9a6e) feat: output IPs when etcd needs to be bootstrapped * [`c297d66a1`](https://github.com/talos-systems/talos/commit/c297d66a130cba708fcb42f8f2e6b356c36f5109) test: attempt number on two on proper retries in CLI time tests * [`dc299da9e`](https://github.com/talos-systems/talos/commit/dc299da9e8e885b7a44c184ef3d251726aa934a8) docs: add arm64 option to talosctl download * [`f49f40a33`](https://github.com/talos-systems/talos/commit/f49f40a3361381e51d6986547be12ec3b4a3f24a) fix: pass path to conformance retrieve results * [`942c8074f`](https://github.com/talos-systems/talos/commit/942c8074fd14478089769e2b8132ea2796109721) docs: fork docs for 0.15 * [`880a7782c`](https://github.com/talos-systems/talos/commit/880a7782cbc703b38a2ff2b3d76c1eda621524ba) docs: update documentation for 0.14.0 release * [`dc9a0cfe9`](https://github.com/talos-systems/talos/commit/dc9a0cfe94b59c688d65ef74ebc04f273b8a72fb) chore: bump Go dependencies * [`773496935`](https://github.com/talos-systems/talos/commit/7734969356abac8355a31da08d47fafd4000e814) fix: config apply immediate * [`17c147488`](https://github.com/talos-systems/talos/commit/17c14748815e2ab928a9c0c8a079f65a63f0194e) test: retry `talosctl time` call in the tests * [`acf1ac0f1`](https://github.com/talos-systems/talos/commit/acf1ac0f1aff929ae9bf66b1c0322b4f83c0fef1) feat: show human-readable aliases in `talosctl get rd` * [`5532867b0`](https://github.com/talos-systems/talos/commit/5532867b05bb596f42516ff121b0a3a97176b3d1) refactor: rewrite the implementation of Processes API * [`80350861a`](https://github.com/talos-systems/talos/commit/80350861a2c1cee234d2f3a571d3993841c554d9) feat: update Kubernetes to 1.23.1 * [`4c96e936e`](https://github.com/talos-systems/talos/commit/4c96e936ed467ae7838258699bdd83fd6da15ae6) docs: add cilium guide * [`e3f2acb5e`](https://github.com/talos-systems/talos/commit/e3f2acb5e57f9b3e7b11986f180e287f1f693079) refactor: rewrite the check for unknown keys in the machine configuration * [`4175396a8`](https://github.com/talos-systems/talos/commit/4175396a89f836bb1835d201b59224b286eeb62a) refactor: use update go-blockdevice library with allocation fixes * [`b58f567a1`](https://github.com/talos-systems/talos/commit/b58f567a133b661cc045a995dd29ab5090dfe194) refactor: optimize Runtime config interface to avoid config marshaling * [`bb355c9ab`](https://github.com/talos-systems/talos/commit/bb355c9ab38a417ed471bf3ce7b1879609f5e806) chore: remove govalidator library * [`3af56bd2e`](https://github.com/talos-systems/talos/commit/3af56bd2e70e8964cc48b430b1e67e48052af682) test: update capi templates to v1beta1 * [`936b4c4ce`](https://github.com/talos-systems/talos/commit/936b4c4cee87697b3f08d51f22208b44b8a02db5) fix: update DHCP library with the panic fix * [`ab42886bf`](https://github.com/talos-systems/talos/commit/ab42886bf333dcaa9d3a1b765781ab19354de397) fix: allow kubelet to be started via the API * [`ec641f729`](https://github.com/talos-systems/talos/commit/ec641f7296ce62b2f9ba1353ff2eba70c2287c08) fix: use default time servers in time API if none are configured * [`79f213eec`](https://github.com/talos-systems/talos/commit/79f213eec65af46c4a3a4c4494d67ffc1b0a53ec) fix: cleanup affiliates * [`2dd0b5b68`](https://github.com/talos-systems/talos/commit/2dd0b5b68aa5b8efbc9b0bc4f8ebc159e2d991ab) chore: update Go to 1.17.5 * [`97ffa7a64`](https://github.com/talos-systems/talos/commit/97ffa7a645d7db93ee58032795f91131f6950e89) feat: upgrade kubelet version in `talosctl upgrade-k8s` * [`5bc5123eb`](https://github.com/talos-systems/talos/commit/5bc5123eb91386ca12e7e7f9fc0f66637343a642) docs: document `ip=` kernel argument * [`8e1d0bfb5`](https://github.com/talos-systems/talos/commit/8e1d0bfb5fbaf0849bdd07b73a8e3bda4e8c3b75) feat: update Kubernetes to 1.23.0

### Changes since v0.15.0-alpha.1
56 commits

* [`1e9f0ad4c`](https://github.com/talos-systems/talos/commit/1e9f0ad4c6abcfb5244f0d4159b7085b387f2cc1) feat: update Go to 1.17.7, Linux to 5.15.23 * [`fef99892d`](https://github.com/talos-systems/talos/commit/fef99892d5ba11c9c87d047e23fb7023de5116a6) chore: pin kubernetes version to `talosctl gen config` * [`bcf928134`](https://github.com/talos-systems/talos/commit/bcf928134c8d1a17d69d425061350040d3ed15a4) feat: udev extensions support * [`47619f832`](https://github.com/talos-systems/talos/commit/47619f8320f8e03246ffa43d19dcd759b1d9511c) docs: update system extensions guide with grammar fixes * [`2bcceb6e4`](https://github.com/talos-systems/talos/commit/2bcceb6e437b5e30f856ea495eccdb0ab4d5e1ca) chore: disable TIPC and B.A.T.M.A.N * [`c6bca1b33`](https://github.com/talos-systems/talos/commit/c6bca1b33b5b0522ee7b997c2bcc5afadc991a94) docs: add guide on system extensions * [`492b156da`](https://github.com/talos-systems/talos/commit/492b156dabca6552002881f9d8ca57b02a04add2) feat: implement static pods via machine configuration * [`6fadfa8db`](https://github.com/talos-systems/talos/commit/6fadfa8dbcc22b80dc83ed477f81f5c55727298c) fix: parse properly IPv6 address in the cmdline `ip=` arg * [`d991f3982`](https://github.com/talos-systems/talos/commit/d991f3982c329e97c78d068eae0abf02020d21a9) chore: update the kernel with IGC driver enabled * [`cbc9610be`](https://github.com/talos-systems/talos/commit/cbc9610be66f4b2552e2c7374118cfa71764a148) feat: sysctl system optimization * [`8b6d6220d`](https://github.com/talos-systems/talos/commit/8b6d6220d3799cb79cd66267017b54d0a09e2c63) fix: parse interface ip correctly (nocloud) * [`54632b1be`](https://github.com/talos-systems/talos/commit/54632b1be7b08440b562dfb0bf44ef9784317dbf) docs: fix developing Talos docs * [`0da370dfe`](https://github.com/talos-systems/talos/commit/0da370dfefecdba9c981ccafa3255c4dc256d4d2) test: unlock CABPT/CACPPT provider versions * [`df0e388a4`](https://github.com/talos-systems/talos/commit/df0e388a4fa1995124d4e722fc1b8a1bfdffed58) feat: extract firmware part of system extensions into initramfs * [`8899dd349`](https://github.com/talos-systems/talos/commit/8899dd34945105e7276fa453341cc0aa4dbe51d4) chore: add json-tags for SecretsBundle * [`4f391cd5c`](https://github.com/talos-systems/talos/commit/4f391cd5c540a0a955f294d628adc7437b7513b5) chore: bump kernel to 5.15.22 * [`6bd07406e`](https://github.com/talos-systems/talos/commit/6bd07406e1895d190b5bbd9838ee84f85d02cd3f) feat: disable reboots via kexec * [`1e3f2f952`](https://github.com/talos-systems/talos/commit/1e3f2f95275aa5f71abe931015799caaca42bf61) fix: validate kubelet node IP subnets correctly * [`d211bff47`](https://github.com/talos-systems/talos/commit/d211bff47d661697926fece893784519dbf4f8f3) feat: enable accept_ra when IPv6 forwarding * [`930205831`](https://github.com/talos-systems/talos/commit/93020583195d066e879ccb19da38b8cfd6b93e96) chore: update kernel to 5.15.21 * [`c7186ed08`](https://github.com/talos-systems/talos/commit/c7186ed08013efaa9957fe064152ccfca8ec1ab8) chore: bump dependencies * [`9ee470f95`](https://github.com/talos-systems/talos/commit/9ee470f9556462dd3fda656d58358e7ae78f4d47) feat: set /etc/localtime to UTC * [`c34768367`](https://github.com/talos-systems/talos/commit/c347683670d489230a2e87e4f04f05009173aca0) fix: disable auto-tls for etcd * [`9bffc7e8d`](https://github.com/talos-systems/talos/commit/9bffc7e8d5eff6d5ce0b83d627557f4110fc5c58) fix: pass proper sequence to shutdown sequence on ACPI shutdown * [`e47387e41`](https://github.com/talos-systems/talos/commit/e47387e4197974366844b2741cae345666d474da) chore: bump CAPI to 1.0.4 * [`5462f5ed1`](https://github.com/talos-systems/talos/commit/5462f5ed18b7ffe023b3a41f1ac7d9b4ca9b726d) feat: update etcd to 3.5.2 * [`f6fa12e53`](https://github.com/talos-systems/talos/commit/f6fa12e53697c763bd0463d91e92a446eb1ac2f7) docs: update upgrading Talos, Kubernetes, and Docker guides * [`5484579c1`](https://github.com/talos-systems/talos/commit/5484579c1a897f2378aacbef94bd4381d6b8299c) feat: allow link scope routes in the machine config * [`56b83b087`](https://github.com/talos-systems/talos/commit/56b83b08730c13910b0e5eb724decaf27e187047) feat: enable persistence for docker provider * [`949464e4b`](https://github.com/talos-systems/talos/commit/949464e4b6e1e807d9299b451758a6d144725fb1) fix: use leaf certificate in the apid RBAC check * [`446972f21`](https://github.com/talos-systems/talos/commit/446972f2113ada8e6c511ce56f630ec170ef0f26) chore: bump kernel to 5.15.19 * [`fe40e7b1b`](https://github.com/talos-systems/talos/commit/fe40e7b1b39281f9bc14393b8c9db55ab6d6f8cd) feat: drain node on shutdown * [`7f0b3aae0`](https://github.com/talos-systems/talos/commit/7f0b3aae0a37b519623422841e3cbcda8bdd21a1) feat: add multiple config patches, patches from files, YAML support * [`202290be7`](https://github.com/talos-systems/talos/commit/202290be7b9b04ec909d369326d463c3b462eafa) docs: update Kubernetes upgrade video * [`036644f7a`](https://github.com/talos-systems/talos/commit/036644f7a03383922fd8407b1d514c7f79d44d0d) chore: bump kernel to 5.15.18 * [`dcde2c4f6`](https://github.com/talos-systems/talos/commit/dcde2c4f68982974d6e55c52ba0fa8665e7f40b8) chore: update k8s upgrade message * [`1c949335c`](https://github.com/talos-systems/talos/commit/1c949335cc41cc9157e4c7dead44826c99b336f3) docs: add documentation for Hyper-V * [`7f9790912`](https://github.com/talos-systems/talos/commit/7f9790912308dfa88457a6db4f94728e5337c399) fix: clean up containerd state on installer run/validate * [`8b98d8eb3`](https://github.com/talos-systems/talos/commit/8b98d8eb3976cb8e64ffa94cfdf0305216f7dbeb) docs: clarify Filebeat example * [`74c03120c`](https://github.com/talos-systems/talos/commit/74c03120cf1da93d79fd786036e8d296c00c221e) docs: replace Talos upgrades video * [`65e64d425`](https://github.com/talos-systems/talos/commit/65e64d425e0253ae6780d52063d227c47df1ae29) chore: update kernel to stable 5.15.17 * [`4245f72d3`](https://github.com/talos-systems/talos/commit/4245f72d3ff3712742d6d7d6ec3310f40f900c79) feat: add --extra-uefi-search-paths option * [`7ffeb6c2e`](https://github.com/talos-systems/talos/commit/7ffeb6c2e2bef1482b641725e4075c44264e899e) docs: update oracle cloud example * [`151c9df09`](https://github.com/talos-systems/talos/commit/151c9df091f32d00748e7e5effbb2c759916e8b9) chore: add CSI tests for e2e-qemu * [`cdb621c82`](https://github.com/talos-systems/talos/commit/cdb621c82e15026a851bbfb567afd834d88165e7) feat: provide a way to list installed system extensions * [`abfb25812`](https://github.com/talos-systems/talos/commit/abfb2581289c72c9e7bda8bc1f7bc2aa2ba758f7) feat: share `/lib/firmware` across initramfs and rootfs * [`ebec5d4a0`](https://github.com/talos-systems/talos/commit/ebec5d4a0c20fe20aa1fd5d1f9b28c0745a08fe7) feat: support full disk path in the diskSelector * [`831f65a07`](https://github.com/talos-systems/talos/commit/831f65a07f3b0a93ee9f38327dc5b84ce97a3237) fix: close client provider instead of Talos client in the upgrade module * [`0bf161dff`](https://github.com/talos-systems/talos/commit/0bf161dffb8c7805c44a4fb2c3db191dfa901b88) test: add integration test for system extensions * [`7b3962745`](https://github.com/talos-systems/talos/commit/7b3962745625decb720c53ca3b454f65079715f6) fix: handle 404 errors from AWS IMDS correctly * [`85782faa2`](https://github.com/talos-systems/talos/commit/85782faa24772dc9fa757aac3803a196f0325544) feat: update Kubernetes to 1.23.3 * [`c5e5922e5`](https://github.com/talos-systems/talos/commit/c5e5922e536533badcaae568171f1b78cac40105) chore: bump dependencies * [`b3c3ef29b`](https://github.com/talos-systems/talos/commit/b3c3ef29bdf0f21805adf3489972cb92c98c00aa) feat: install system extensions * [`a0889600f`](https://github.com/talos-systems/talos/commit/a0889600fb19f62a2503244c32364808777ffdcc) chore: fix golangci-lint install * [`a50c42980`](https://github.com/talos-systems/talos/commit/a50c42980febfe51ba1e4ce750768f01de8c2d47) fix: use #!/usr/bin/env bash as shebang instead of #!/bin/bash * [`4464b725c`](https://github.com/talos-systems/talos/commit/4464b725c4fea4234961959e884426c384822eab) fix: qemu: always use runtime.GOARCH for CNI bundle

### Changes from talos-systems/crypto
2 commits

* [`510b0d2`](https://github.com/talos-systems/crypto/commit/510b0d2753a89170d0c0f60e052a66484997a5b2) chore: add json tags * [`6fa2d93`](https://github.com/talos-systems/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil`

### Changes from talos-systems/extras
3 commits

* [`8f607fc`](https://github.com/talos-systems/extras/commit/8f607fc575b854c48b91ac01d8a233a68577ef29) chore: bump to Go 1.17.7 * [`7c1f3cc`](https://github.com/talos-systems/extras/commit/7c1f3cc0edbba59f2731cd01c0369af0490cebf9) feat: update Go to 1.17.6 * [`495a5b2`](https://github.com/talos-systems/extras/commit/495a5b2a4964e11a9ae8629788c46a5140d07b10) feat: update Go to 1.17.5

### Changes from talos-systems/go-blockdevice
3 commits

* [`7b9de26`](https://github.com/talos-systems/go-blockdevice/commit/7b9de26bc6bc3d54b95bd8e8fb3aade4b45adc6c) feat: read symlink fullpath in block device list function * [`6928ee4`](https://github.com/talos-systems/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/talos-systems/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions

### Changes from talos-systems/net
1 commit

* [`409926a`](https://github.com/talos-systems/net/commit/409926aec1c3e659d6c245db4c0b90b0eaa4fdbc) fix: parse correctly some IPv6 CIDRs

### Changes from talos-systems/pkgs
25 commits

* [`6019223`](https://github.com/talos-systems/pkgs/commit/601922368a7b82d8eb87f946e314e70df7f2f6eb) chore: bump kernel to 5.15.23 * [`ff4b2d8`](https://github.com/talos-systems/pkgs/commit/ff4b2d8ae80bb0802251d3aba0708a603782c267) chore: bump tools for Go 1.17.7 * [`e34f883`](https://github.com/talos-systems/pkgs/commit/e34f88339ed02c94006e6f6c874d6878d558b26b) chore: disable TIPC and B.A.T.M.A.N * [`2b8cd88`](https://github.com/talos-systems/pkgs/commit/2b8cd882e6dc0e6409e8087934bb3f179153f8f3) feat: add Intel Ethernet Controller I225-V driver * [`407459d`](https://github.com/talos-systems/pkgs/commit/407459da274c80620282a2b690dac1fd3c32394f) feat: enable zstd squashfs compression and firmware (xz) compression * [`81a4b1c`](https://github.com/talos-systems/pkgs/commit/81a4b1c3a9f9a941cec751c6370bd8a92a177052) chore: bump kernel to 5.15.22 * [`c9a6415`](https://github.com/talos-systems/pkgs/commit/c9a64154f11b36e21f25a99c4251bf4ed2bebe48) chore: bump kernel to 5.15.21 * [`90dcd00`](https://github.com/talos-systems/pkgs/commit/90dcd000f68ffe447ad82d15c081923cd2054568) chore: bump kernel to 5.15.19 * [`d457b87`](https://github.com/talos-systems/pkgs/commit/d457b87b18d3a6bff6fb9b22c25658e6f2eb30b2) chore: bump kernel to 5.15.18 * [`dd69678`](https://github.com/talos-systems/pkgs/commit/dd6967866cc38029f8eb30d30b78f6579b14c595) chore: disable ATA-over-Ethernet driver for arm64 * [`388ce13`](https://github.com/talos-systems/pkgs/commit/388ce1365e2508f38efbb925000659663d12987d) chore: bump kernel to 5.15.17 * [`c14eb99`](https://github.com/talos-systems/pkgs/commit/c14eb99ec16603fc1fcbd93fab22ade7b55259ac) feat: update Linux to 5.15.16 * [`5d4d8d6`](https://github.com/talos-systems/pkgs/commit/5d4d8d6e57814fbbdcf1abebe148827f68fd81ec) feat: bump containerd to 1.6.0-rc.0, runc to 1.1.0 * [`5dd08a7`](https://github.com/talos-systems/pkgs/commit/5dd08a771be617720e5fadb9cb4df7b4641d83ba) feat: jetson nano SoC * [`402b960`](https://github.com/talos-systems/pkgs/commit/402b9601d3a0521575006c5f0c0cc1fbdab6af42) chore: bump u-boot to 2022.01 * [`6ce1a40`](https://github.com/talos-systems/pkgs/commit/6ce1a4036d78113e9b66c6c2e074909b05b70caf) feat: update Go to 1.17.6 * [`08f2519`](https://github.com/talos-systems/pkgs/commit/08f25196d91c4c3a1ebcb8e0ec8efc16098214bc) feat: update containerd to 1.5.9 * [`fbb5c5c`](https://github.com/talos-systems/pkgs/commit/fbb5c5ccb83f6779a6a8ec0d4d867fd0fa7c5f56) feat: add qlcnic drivers to kernel * [`0505e01`](https://github.com/talos-systems/pkgs/commit/0505e0147ec27bf2c7f4d2b3c7d16fab796cc9de) chore: fix `=m` kernel build options * [`54aa902`](https://github.com/talos-systems/pkgs/commit/54aa902c3a3e7c3c427450b54271fb92190625e4) feat: enable amdgpu in kernel * [`2779c3f`](https://github.com/talos-systems/pkgs/commit/2779c3fe825a47a7c392d077f4a38d7f7b2f8eb5) fix: kexec on rpi4 * [`950361f`](https://github.com/talos-systems/pkgs/commit/950361f79c03f718d73b60ddfbc6f661f4aa93b6) feat: update Linux to 5.15.11 * [`ad611bc`](https://github.com/talos-systems/pkgs/commit/ad611bc512bd67366e16f0b58c24dfca35f38a12) feat: provide build instructions for NVIDIA kernel module * [`b22723d`](https://github.com/talos-systems/pkgs/commit/b22723d0fb4766488f1bc50244d3cdfb9a9d8bbf) feat: update iPXE to the latest available version * [`a675c67`](https://github.com/talos-systems/pkgs/commit/a675c676e894c33626563f57e9c124e7628bc78f) feat: update Go to 1.17.5

### Changes from talos-systems/tools
4 commits

* [`4c9e7a4`](https://github.com/talos-systems/tools/commit/4c9e7a4a01843363e07687b6d2e5145cf8329368) chore: bump go to 1.17.7 * [`d33b4b6`](https://github.com/talos-systems/tools/commit/d33b4b65642d2989249a16ce207cd5cab156a55f) feat: support zstd compression * [`67314b1`](https://github.com/talos-systems/tools/commit/67314b13104ab1eb1ae9d7137d640499f35caead) feat: update Go to 1.17.6 * [`9c2b9df`](https://github.com/talos-systems/tools/commit/9c2b9dfde84366c486f212cc074405cfb4d52127) feat: update Go to 1.17.5

### Dependency Changes * **cloud.google.com/go/compute** v1.2.0 **_new_** * **github.com/BurntSushi/toml** v0.4.1 -> v1.0.0 * **github.com/aws/aws-sdk-go** v1.42.47 **_new_** * **github.com/containerd/cgroups** v1.0.2 -> v1.0.3 * **github.com/containerd/containerd** v1.5.8 -> v1.6.0-rc.2 * **github.com/docker/docker** v20.10.11 -> v20.10.12 * **github.com/google/go-cmp** v0.5.6 -> v0.5.7 * **github.com/google/nftables** 16a134723a96 -> 91d3b4571db1 * **github.com/hashicorp/go-getter** v1.5.9 -> v1.5.11 * **github.com/hashicorp/go-version** v1.4.0 **_new_** * **github.com/insomniacslk/dhcp** 5297eed8f489 -> 3c283ff8b7dd * **github.com/jsimonetti/rtnetlink** fd9a11f42291 -> v1.1.0 * **github.com/jxskiss/base62** v1.0.0 -> v1.1.0 * **github.com/mdlayher/ethtool** 288d040e9d60 -> 81c2608dd90e * **github.com/mdlayher/genetlink** v1.0.0 -> v1.2.0 * **github.com/mdlayher/netlink** v1.4.2 -> v1.6.0 * **github.com/opencontainers/image-spec** v1.0.2 **_new_** * **github.com/packethost/packngo** v0.20.0 -> v0.21.0 * **github.com/pelletier/go-toml** v1.9.4 **_new_** * **github.com/pmorjan/kmod** v1.0.0 **_new_** * **github.com/rivo/tview** 2a6de950f73b -> 1f7581b67bd1 * **github.com/spf13/cobra** v1.2.1 -> v1.3.0 * **github.com/talos-systems/crypto** v0.3.4 -> 510b0d2753a8 * **github.com/talos-systems/extras** v0.7.0-1-gd6b73a7 -> v0.8.0-alpha.0-2-g8f607fc * **github.com/talos-systems/go-blockdevice** v0.2.5 -> 7b9de26bc6bc * **github.com/talos-systems/net** v0.3.1 -> 409926aec1c3 * **github.com/talos-systems/pkgs** v0.9.0-1-g7a3419a -> v0.10.0-alpha.0-24-g6019223 * **github.com/talos-systems/tools** v0.9.0-1-gb1146f9 -> v0.10.0-alpha.0-3-g4c9e7a4 * **github.com/u-root/u-root** v7.0.0 -> v0.8.0 * **github.com/vishvananda/netlink** f5de75959ad5 -> 650dca95af54 * **github.com/vmware-tanzu/sonobuoy** v0.55.1 -> v0.56.0 * **github.com/vmware/govmomi** v0.27.2 -> v0.27.3 * **go.etcd.io/etcd/api/v3** v3.5.1 -> v3.5.2 * **go.etcd.io/etcd/client/pkg/v3** v3.5.1 -> v3.5.2 * **go.etcd.io/etcd/client/v3** v3.5.1 -> v3.5.2 * **go.etcd.io/etcd/etcdutl/v3** v3.5.1 -> v3.5.2 * **go.uber.org/zap** v1.19.1 -> v1.20.0 * **golang.org/x/net** 491a49abca63 -> cd36cc0744dd * **golang.org/x/sys** 97ca703d548d -> 1c1b9b1eba6a * **golang.zx2c4.com/wireguard/wgctrl** dd7407c86d22 -> daad0b7ba671 * **google.golang.org/grpc** v1.42.0 -> v1.44.0 * **k8s.io/api** v0.23.1 -> v0.23.3 * **k8s.io/apimachinery** v0.23.1 -> v0.23.3 * **k8s.io/client-go** v0.23.1 -> v0.23.3 * **k8s.io/component-base** v0.23.1 -> v0.23.3 * **k8s.io/kubectl** v0.23.1 -> v0.23.3 * **k8s.io/kubelet** v0.23.1 -> v0.23.3 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.61 -> v1.2.63 Previous release can be found at [v0.14.0](https://github.com/talos-systems/talos/releases/tag/v0.14.0) ## [Talos 0.15.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.15.0-alpha.1) (2022-01-24) Welcome to the v0.15.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Apply Config Enhancements `talosctl apply/patch/edit` cli commands got revamped. Separate flags `--on-reboot`, `--immediate`, `--interactive` were replaced with a single `--mode` flag that can take the following values: - `auto` new mode that automatically applies the configuration in immediate/reboot mode. - `no-reboot` force apply immediately, if not possible, then fail. - `reboot` force reboot with apply config. - `staged` write new machine configuration to STATE, but don't apply it (it will be applied after a reboot). - `interactive` starts interactive installer, only for `apply`. ### Machine Configuration Talos now preserves machine configuration as it was submitted to the node. ### Platform Support Talos now supports Oracle Cloud. Platform network configuration was rewritten to avoid modifying Talos machine configuration. Network configuration is performed independent of the machine configuration presence, so it works even if Talos is booted in maintenance mode (without machine configuration is platform userdata). ### SBC Support Talos now supports Jetson Nano SBC. ### Component Updates * Linux: 5.15.16 * containerd: 1.5.9 * CoreDNS: 1.8.7 * containerd: 1.6.0-rc.0 * runc: 1.1.0 Talos is built with Go 1.17.6 ### Wipe System Kernel Parameter Added new kernel parameter `talos.experimental.wipe=system` which can help resetting system disk for the machine and start over with a fresh installation. See [Resetting a Machine](https://www.talos.dev/docs/v0.15/guides/resetting-a-machine/#kernel-parameter) on how to use it. ### Contributors * Andrey Smirnov * Noel Georgi * Spencer Smith * Artem Chernyshev * Seán C McCord * Steve Francis * Serge Logvinov * Andrew Rynhard * Anthony Rabbito * Eric Wohltman * Niklas Metje * Shahar Naveh ### Changes
80 commits

* [`58eb3600f`](https://github.com/talos-systems/talos/commit/58eb3600fc44dc2fccaa82322207291ffd807205) fix: enforce reasonable TLS min tls-min-version * [`b8d4c5dfa`](https://github.com/talos-systems/talos/commit/b8d4c5dfad4585c0af52287513176411a79fc20c) fix: use correct error in `kernel_param_spec` Modify call handling * [`4961d6867`](https://github.com/talos-systems/talos/commit/4961d6867cadab5e8b48e73355b23b91d36f70b4) docs: drop talos.interface kernel arg * [`b1e61fa5b`](https://github.com/talos-systems/talos/commit/b1e61fa5b1bcd5affd42b498711b9e3378344c33) chore: update Linux to 5.15.16 * [`d4b844593`](https://github.com/talos-systems/talos/commit/d4b844593587ae3f82efcdbdfe0f24cda4262474) feat: support CRI configuration merging and reimplement registry config * [`f94c8c6e1`](https://github.com/talos-systems/talos/commit/f94c8c6e1c3915c962c331943120bdfd2b76259f) feat: update Kubernetes to 1.23.2 * [`21f497b3e`](https://github.com/talos-systems/talos/commit/21f497b3e20f3b1cc9b744f1787ba80cf396d3e0) feat: install readonly overlay mounts during talos chroot sequence * [`9ad5a67d2`](https://github.com/talos-systems/talos/commit/9ad5a67d21b0788d1b43f1bea8e39c003a4a8ecc) feat: inject platform network configuration as network resources * [`907f8cbfb`](https://github.com/talos-systems/talos/commit/907f8cbfb8ed28cf399b9797230790718fc04a58) docs: fix patch flag * [`caa434426`](https://github.com/talos-systems/talos/commit/caa43442640744a0aa7a17aa1a205f1641e6445a) docs: add documentation on developing Talos * [`16eeb6776`](https://github.com/talos-systems/talos/commit/16eeb677625c0859d73b82948c1a073ba6e17e8d) docs: readme updates * [`3c0737027`](https://github.com/talos-systems/talos/commit/3c0737027b5574581a6461211199274ee709b1da) chore: update release notes * [`6d8bea5d5`](https://github.com/talos-systems/talos/commit/6d8bea5d559b1156f7d0b576b7b5784c25cd3595) feat: jetson nano SoC * [`1d8955ebe`](https://github.com/talos-systems/talos/commit/1d8955ebe43259a5e072b8a89f37cb728b6fcf53) feat: update CoreDNS to 1.8.7 * [`6af83afd5`](https://github.com/talos-systems/talos/commit/6af83afd5aba64ffa7887d62f84c434109b7579b) fix: handle multiple-IP cluster nodes * [`43b2d8137`](https://github.com/talos-systems/talos/commit/43b2d8137116863cfc5ca969c429c13483465b01) chore: bump dependencies * [`529e80f4f`](https://github.com/talos-systems/talos/commit/529e80f4f529f066872b5768cd80eeeb7b766a31) docs: update home page and footer * [`37630e70c`](https://github.com/talos-systems/talos/commit/37630e70ccc9950e139bf7fcfcded6a18d0c7a01) Update twitter link * [`af440919b`](https://github.com/talos-systems/talos/commit/af440919bbaf12f414f04a5a621c1e2d5ed84ae2) fix: avoid panic in config loading/validation * [`4b8e9de59`](https://github.com/talos-systems/talos/commit/4b8e9de599812f82275605a93de7f5c05471f7f5) docs: add guide on adding proprietary kernel modules * [`833dc4169`](https://github.com/talos-systems/talos/commit/833dc4169a9702383930816d13be39f6b81c7a31) docs: rework vmware assets * [`2869b5eea`](https://github.com/talos-systems/talos/commit/2869b5eeacf0b6c96aedcb605bfa8a5f9fb87625) feat: add oraclecloud.com platform support * [`f3ec24beb`](https://github.com/talos-systems/talos/commit/f3ec24bebf0aaa7983228a09b21a67b9a2a098c1) fix: vmware documentation typo * [`2f2bdb26a`](https://github.com/talos-systems/talos/commit/2f2bdb26aa5367066c12a6402af554b7a5a148d6) feat: replace flags with --mode in `apply`, `edit` and `patch` commands * [`b09be2a69`](https://github.com/talos-systems/talos/commit/b09be2a69c6b6f8064a676fc014e6e60ea01a08d) docs: update index.md and sync across versions * [`ca65b918a`](https://github.com/talos-systems/talos/commit/ca65b918a7292ae53d40e410cca4e89be91e4261) docs: add nocloud documentation * [`59437d6d8`](https://github.com/talos-systems/talos/commit/59437d6d8360ad7dd8f801797ab91ac0791270f7) fix: filter down nameservers for docker-based cluster create * [`194eaa6f2`](https://github.com/talos-systems/talos/commit/194eaa6f22249fe4f43958bd897744a2cc57279f) chore: clean up /usr/bin from unneeded files * [`74e727240`](https://github.com/talos-systems/talos/commit/74e7272401ccb75464dd42ed0427d73842af74e1) docs: update office office * [`539af338c`](https://github.com/talos-systems/talos/commit/539af338c4b8f6e4291654f66628c81022aeda72) docs: update vmware docs * [`279a3fda7`](https://github.com/talos-systems/talos/commit/279a3fda7ba24037e06377f01cc495207722caa9) feat: update Go to 1.17.6, containerd to 1.5.9 * [`3d3088941`](https://github.com/talos-systems/talos/commit/3d308894120092fe095b41970d6341362ab80a6b) chore: bump Go dependencies * [`d02d944ec`](https://github.com/talos-systems/talos/commit/d02d944ec767441612b84c164af31bc27c0c0659) chore: provide umarshal from YAML methods for network resource specs * [`2e735714d`](https://github.com/talos-systems/talos/commit/2e735714d9218cbc335d9c418730c146821fb8d4) fix: derive machine-id from node identity * [`d8a2721e1`](https://github.com/talos-systems/talos/commit/d8a2721e129be33f4a3c37be1bf5b89a1cd91685) test: update CAPI components to latest * [`7dff8a53e`](https://github.com/talos-systems/talos/commit/7dff8a53ee7bc37afe9dc216ca8a9113718d76af) fix: ignore missing init.yaml for cluster create * [`f4516c7d8`](https://github.com/talos-systems/talos/commit/f4516c7d847d905b49b4e2127eb86a1f38156d53) chore: bump dependencies * [`944f13221`](https://github.com/talos-systems/talos/commit/944f13221d50694d5c59ace1c12f8769d7ade9ae) chore: fix release pipeline * [`cb548a368`](https://github.com/talos-systems/talos/commit/cb548a368a75ca379209213948518c880b242b0c) release(v0.15.0-alpha.0): prepare release * [`da0b36e61`](https://github.com/talos-systems/talos/commit/da0b36e616f7da7eb0c6791b9cf5e4ee2757f08f) feat: introduce `talos.exp.wipe` kernel param to wipe system disk * [`c079eb32b`](https://github.com/talos-systems/talos/commit/c079eb32bd7fc19d506146e2a9edf5b406e25e02) refactor: use AWS SDK to access AWS metadata service * [`2f4b9d8d6`](https://github.com/talos-systems/talos/commit/2f4b9d8d6d10c0aa753f405282aa99696b923bb4) feat: make machine configuration read-only in Talos (almost) * [`524f83d3d`](https://github.com/talos-systems/talos/commit/524f83d3d8af3857f178c179a9552a5f32b70f47) feat: use official Go SDK to fetch GCP instance metadata * [`d2a7e082c`](https://github.com/talos-systems/talos/commit/d2a7e082c24d0b42820b3ea454329a19178ba0a4) test: retry in discovery tests * [`f4219e530`](https://github.com/talos-systems/talos/commit/f4219e530ca7635ada666ae69071746d698939a8) chore: remove unused methods in AWS platform * [`35bc2940e`](https://github.com/talos-systems/talos/commit/35bc2940e375b99e0d6e22a26a05c25d642bf35a) fix: kexec on RPI4 * [`f235cfbae`](https://github.com/talos-systems/talos/commit/f235cfbaed8b5254e19616bfaaa8b48fd7d32e64) fix: multiple usability fixes * [`b3fbb2f31`](https://github.com/talos-systems/talos/commit/b3fbb2f312d5de0c14ffee567956b868a317aba7) test: don't build all images in the default CI pipeline * [`dac550a50`](https://github.com/talos-systems/talos/commit/dac550a50f4793194e4aeee98702a052925a0e88) docs: fix troubleshooting guide * [`83e8bec6b`](https://github.com/talos-systems/talos/commit/83e8bec6b9d4c0ecc689f45b15d7203bbf9bf0cc) feat: update Linux to 5.15.11 * [`d5a82b37e`](https://github.com/talos-systems/talos/commit/d5a82b37eb147a68ffd08fc8ec800edc92da9f9c) feat: remove `ApplyDynamicConfig` * [`3623da136`](https://github.com/talos-systems/talos/commit/3623da136bde51422ba1aec06e22dea2e3dfa756) feat: provide a way to load Linux kernel modules * [`4d1514add`](https://github.com/talos-systems/talos/commit/4d1514add6e0b972aee26a8ad63ef8f972050d46) docs: update Mayastor deployment process * [`cff1ff6d5`](https://github.com/talos-systems/talos/commit/cff1ff6d5c3a68063ed2c0c063daadf2474cc43f) feat: shell completion for `list`, `read` * [`19728437e`](https://github.com/talos-systems/talos/commit/19728437ead7ab6e95afc8bd7f70be3f861c9a6e) feat: output IPs when etcd needs to be bootstrapped * [`c297d66a1`](https://github.com/talos-systems/talos/commit/c297d66a130cba708fcb42f8f2e6b356c36f5109) test: attempt number on two on proper retries in CLI time tests * [`dc299da9e`](https://github.com/talos-systems/talos/commit/dc299da9e8e885b7a44c184ef3d251726aa934a8) docs: add arm64 option to talosctl download * [`f49f40a33`](https://github.com/talos-systems/talos/commit/f49f40a3361381e51d6986547be12ec3b4a3f24a) fix: pass path to conformance retrieve results * [`942c8074f`](https://github.com/talos-systems/talos/commit/942c8074fd14478089769e2b8132ea2796109721) docs: fork docs for 0.15 * [`880a7782c`](https://github.com/talos-systems/talos/commit/880a7782cbc703b38a2ff2b3d76c1eda621524ba) docs: update documentation for 0.14.0 release * [`dc9a0cfe9`](https://github.com/talos-systems/talos/commit/dc9a0cfe94b59c688d65ef74ebc04f273b8a72fb) chore: bump Go dependencies * [`773496935`](https://github.com/talos-systems/talos/commit/7734969356abac8355a31da08d47fafd4000e814) fix: config apply immediate * [`17c147488`](https://github.com/talos-systems/talos/commit/17c14748815e2ab928a9c0c8a079f65a63f0194e) test: retry `talosctl time` call in the tests * [`acf1ac0f1`](https://github.com/talos-systems/talos/commit/acf1ac0f1aff929ae9bf66b1c0322b4f83c0fef1) feat: show human-readable aliases in `talosctl get rd` * [`5532867b0`](https://github.com/talos-systems/talos/commit/5532867b05bb596f42516ff121b0a3a97176b3d1) refactor: rewrite the implementation of Processes API * [`80350861a`](https://github.com/talos-systems/talos/commit/80350861a2c1cee234d2f3a571d3993841c554d9) feat: update Kubernetes to 1.23.1 * [`4c96e936e`](https://github.com/talos-systems/talos/commit/4c96e936ed467ae7838258699bdd83fd6da15ae6) docs: add cilium guide * [`e3f2acb5e`](https://github.com/talos-systems/talos/commit/e3f2acb5e57f9b3e7b11986f180e287f1f693079) refactor: rewrite the check for unknown keys in the machine configuration * [`4175396a8`](https://github.com/talos-systems/talos/commit/4175396a89f836bb1835d201b59224b286eeb62a) refactor: use update go-blockdevice library with allocation fixes * [`b58f567a1`](https://github.com/talos-systems/talos/commit/b58f567a133b661cc045a995dd29ab5090dfe194) refactor: optimize Runtime config interface to avoid config marshaling * [`bb355c9ab`](https://github.com/talos-systems/talos/commit/bb355c9ab38a417ed471bf3ce7b1879609f5e806) chore: remove govalidator library * [`3af56bd2e`](https://github.com/talos-systems/talos/commit/3af56bd2e70e8964cc48b430b1e67e48052af682) test: update capi templates to v1beta1 * [`936b4c4ce`](https://github.com/talos-systems/talos/commit/936b4c4cee87697b3f08d51f22208b44b8a02db5) fix: update DHCP library with the panic fix * [`ab42886bf`](https://github.com/talos-systems/talos/commit/ab42886bf333dcaa9d3a1b765781ab19354de397) fix: allow kubelet to be started via the API * [`ec641f729`](https://github.com/talos-systems/talos/commit/ec641f7296ce62b2f9ba1353ff2eba70c2287c08) fix: use default time servers in time API if none are configured * [`79f213eec`](https://github.com/talos-systems/talos/commit/79f213eec65af46c4a3a4c4494d67ffc1b0a53ec) fix: cleanup affiliates * [`2dd0b5b68`](https://github.com/talos-systems/talos/commit/2dd0b5b68aa5b8efbc9b0bc4f8ebc159e2d991ab) chore: update Go to 1.17.5 * [`97ffa7a64`](https://github.com/talos-systems/talos/commit/97ffa7a645d7db93ee58032795f91131f6950e89) feat: upgrade kubelet version in `talosctl upgrade-k8s` * [`5bc5123eb`](https://github.com/talos-systems/talos/commit/5bc5123eb91386ca12e7e7f9fc0f66637343a642) docs: document `ip=` kernel argument * [`8e1d0bfb5`](https://github.com/talos-systems/talos/commit/8e1d0bfb5fbaf0849bdd07b73a8e3bda4e8c3b75) feat: update Kubernetes to 1.23.0

### Changes since v0.15.0-alpha.0
37 commits

* [`58eb3600f`](https://github.com/talos-systems/talos/commit/58eb3600fc44dc2fccaa82322207291ffd807205) fix: enforce reasonable TLS min tls-min-version * [`b8d4c5dfa`](https://github.com/talos-systems/talos/commit/b8d4c5dfad4585c0af52287513176411a79fc20c) fix: use correct error in `kernel_param_spec` Modify call handling * [`4961d6867`](https://github.com/talos-systems/talos/commit/4961d6867cadab5e8b48e73355b23b91d36f70b4) docs: drop talos.interface kernel arg * [`b1e61fa5b`](https://github.com/talos-systems/talos/commit/b1e61fa5b1bcd5affd42b498711b9e3378344c33) chore: update Linux to 5.15.16 * [`d4b844593`](https://github.com/talos-systems/talos/commit/d4b844593587ae3f82efcdbdfe0f24cda4262474) feat: support CRI configuration merging and reimplement registry config * [`f94c8c6e1`](https://github.com/talos-systems/talos/commit/f94c8c6e1c3915c962c331943120bdfd2b76259f) feat: update Kubernetes to 1.23.2 * [`21f497b3e`](https://github.com/talos-systems/talos/commit/21f497b3e20f3b1cc9b744f1787ba80cf396d3e0) feat: install readonly overlay mounts during talos chroot sequence * [`9ad5a67d2`](https://github.com/talos-systems/talos/commit/9ad5a67d21b0788d1b43f1bea8e39c003a4a8ecc) feat: inject platform network configuration as network resources * [`907f8cbfb`](https://github.com/talos-systems/talos/commit/907f8cbfb8ed28cf399b9797230790718fc04a58) docs: fix patch flag * [`caa434426`](https://github.com/talos-systems/talos/commit/caa43442640744a0aa7a17aa1a205f1641e6445a) docs: add documentation on developing Talos * [`16eeb6776`](https://github.com/talos-systems/talos/commit/16eeb677625c0859d73b82948c1a073ba6e17e8d) docs: readme updates * [`3c0737027`](https://github.com/talos-systems/talos/commit/3c0737027b5574581a6461211199274ee709b1da) chore: update release notes * [`6d8bea5d5`](https://github.com/talos-systems/talos/commit/6d8bea5d559b1156f7d0b576b7b5784c25cd3595) feat: jetson nano SoC * [`1d8955ebe`](https://github.com/talos-systems/talos/commit/1d8955ebe43259a5e072b8a89f37cb728b6fcf53) feat: update CoreDNS to 1.8.7 * [`6af83afd5`](https://github.com/talos-systems/talos/commit/6af83afd5aba64ffa7887d62f84c434109b7579b) fix: handle multiple-IP cluster nodes * [`43b2d8137`](https://github.com/talos-systems/talos/commit/43b2d8137116863cfc5ca969c429c13483465b01) chore: bump dependencies * [`529e80f4f`](https://github.com/talos-systems/talos/commit/529e80f4f529f066872b5768cd80eeeb7b766a31) docs: update home page and footer * [`37630e70c`](https://github.com/talos-systems/talos/commit/37630e70ccc9950e139bf7fcfcded6a18d0c7a01) Update twitter link * [`af440919b`](https://github.com/talos-systems/talos/commit/af440919bbaf12f414f04a5a621c1e2d5ed84ae2) fix: avoid panic in config loading/validation * [`4b8e9de59`](https://github.com/talos-systems/talos/commit/4b8e9de599812f82275605a93de7f5c05471f7f5) docs: add guide on adding proprietary kernel modules * [`833dc4169`](https://github.com/talos-systems/talos/commit/833dc4169a9702383930816d13be39f6b81c7a31) docs: rework vmware assets * [`2869b5eea`](https://github.com/talos-systems/talos/commit/2869b5eeacf0b6c96aedcb605bfa8a5f9fb87625) feat: add oraclecloud.com platform support * [`f3ec24beb`](https://github.com/talos-systems/talos/commit/f3ec24bebf0aaa7983228a09b21a67b9a2a098c1) fix: vmware documentation typo * [`2f2bdb26a`](https://github.com/talos-systems/talos/commit/2f2bdb26aa5367066c12a6402af554b7a5a148d6) feat: replace flags with --mode in `apply`, `edit` and `patch` commands * [`b09be2a69`](https://github.com/talos-systems/talos/commit/b09be2a69c6b6f8064a676fc014e6e60ea01a08d) docs: update index.md and sync across versions * [`ca65b918a`](https://github.com/talos-systems/talos/commit/ca65b918a7292ae53d40e410cca4e89be91e4261) docs: add nocloud documentation * [`59437d6d8`](https://github.com/talos-systems/talos/commit/59437d6d8360ad7dd8f801797ab91ac0791270f7) fix: filter down nameservers for docker-based cluster create * [`194eaa6f2`](https://github.com/talos-systems/talos/commit/194eaa6f22249fe4f43958bd897744a2cc57279f) chore: clean up /usr/bin from unneeded files * [`74e727240`](https://github.com/talos-systems/talos/commit/74e7272401ccb75464dd42ed0427d73842af74e1) docs: update office office * [`539af338c`](https://github.com/talos-systems/talos/commit/539af338c4b8f6e4291654f66628c81022aeda72) docs: update vmware docs * [`279a3fda7`](https://github.com/talos-systems/talos/commit/279a3fda7ba24037e06377f01cc495207722caa9) feat: update Go to 1.17.6, containerd to 1.5.9 * [`3d3088941`](https://github.com/talos-systems/talos/commit/3d308894120092fe095b41970d6341362ab80a6b) chore: bump Go dependencies * [`d02d944ec`](https://github.com/talos-systems/talos/commit/d02d944ec767441612b84c164af31bc27c0c0659) chore: provide umarshal from YAML methods for network resource specs * [`2e735714d`](https://github.com/talos-systems/talos/commit/2e735714d9218cbc335d9c418730c146821fb8d4) fix: derive machine-id from node identity * [`d8a2721e1`](https://github.com/talos-systems/talos/commit/d8a2721e129be33f4a3c37be1bf5b89a1cd91685) test: update CAPI components to latest * [`7dff8a53e`](https://github.com/talos-systems/talos/commit/7dff8a53ee7bc37afe9dc216ca8a9113718d76af) fix: ignore missing init.yaml for cluster create * [`f4516c7d8`](https://github.com/talos-systems/talos/commit/f4516c7d847d905b49b4e2127eb86a1f38156d53) chore: bump dependencies

### Changes from talos-systems/crypto
1 commit

* [`6fa2d93`](https://github.com/talos-systems/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil`

### Changes from talos-systems/extras
2 commits

* [`7c1f3cc`](https://github.com/talos-systems/extras/commit/7c1f3cc0edbba59f2731cd01c0369af0490cebf9) feat: update Go to 1.17.6 * [`495a5b2`](https://github.com/talos-systems/extras/commit/495a5b2a4964e11a9ae8629788c46a5140d07b10) feat: update Go to 1.17.5

### Changes from talos-systems/go-blockdevice
2 commits

* [`6928ee4`](https://github.com/talos-systems/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/talos-systems/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions

### Changes from talos-systems/pkgs
14 commits

* [`c14eb99`](https://github.com/talos-systems/pkgs/commit/c14eb99ec16603fc1fcbd93fab22ade7b55259ac) feat: update Linux to 5.15.16 * [`5d4d8d6`](https://github.com/talos-systems/pkgs/commit/5d4d8d6e57814fbbdcf1abebe148827f68fd81ec) feat: bump containerd to 1.6.0-rc.0, runc to 1.1.0 * [`5dd08a7`](https://github.com/talos-systems/pkgs/commit/5dd08a771be617720e5fadb9cb4df7b4641d83ba) feat: jetson nano SoC * [`402b960`](https://github.com/talos-systems/pkgs/commit/402b9601d3a0521575006c5f0c0cc1fbdab6af42) chore: bump u-boot to 2022.01 * [`6ce1a40`](https://github.com/talos-systems/pkgs/commit/6ce1a4036d78113e9b66c6c2e074909b05b70caf) feat: update Go to 1.17.6 * [`08f2519`](https://github.com/talos-systems/pkgs/commit/08f25196d91c4c3a1ebcb8e0ec8efc16098214bc) feat: update containerd to 1.5.9 * [`fbb5c5c`](https://github.com/talos-systems/pkgs/commit/fbb5c5ccb83f6779a6a8ec0d4d867fd0fa7c5f56) feat: add qlcnic drivers to kernel * [`0505e01`](https://github.com/talos-systems/pkgs/commit/0505e0147ec27bf2c7f4d2b3c7d16fab796cc9de) chore: fix `=m` kernel build options * [`54aa902`](https://github.com/talos-systems/pkgs/commit/54aa902c3a3e7c3c427450b54271fb92190625e4) feat: enable amdgpu in kernel * [`2779c3f`](https://github.com/talos-systems/pkgs/commit/2779c3fe825a47a7c392d077f4a38d7f7b2f8eb5) fix: kexec on rpi4 * [`950361f`](https://github.com/talos-systems/pkgs/commit/950361f79c03f718d73b60ddfbc6f661f4aa93b6) feat: update Linux to 5.15.11 * [`ad611bc`](https://github.com/talos-systems/pkgs/commit/ad611bc512bd67366e16f0b58c24dfca35f38a12) feat: provide build instructions for NVIDIA kernel module * [`b22723d`](https://github.com/talos-systems/pkgs/commit/b22723d0fb4766488f1bc50244d3cdfb9a9d8bbf) feat: update iPXE to the latest available version * [`a675c67`](https://github.com/talos-systems/pkgs/commit/a675c676e894c33626563f57e9c124e7628bc78f) feat: update Go to 1.17.5

### Changes from talos-systems/tools
2 commits

* [`67314b1`](https://github.com/talos-systems/tools/commit/67314b13104ab1eb1ae9d7137d640499f35caead) feat: update Go to 1.17.6 * [`9c2b9df`](https://github.com/talos-systems/tools/commit/9c2b9dfde84366c486f212cc074405cfb4d52127) feat: update Go to 1.17.5

### Dependency Changes * **cloud.google.com/go/compute** v1.0.0 **_new_** * **github.com/BurntSushi/toml** v0.4.1 -> v1.0.0 * **github.com/aws/aws-sdk-go** v1.42.35 **_new_** * **github.com/containerd/containerd** v1.5.8 -> v1.6.0-rc.0 * **github.com/containerd/containerd/api** v1.6.0-beta.3 **_new_** * **github.com/docker/docker** v20.10.11 -> v20.10.12 * **github.com/google/nftables** 16a134723a96 -> 6f19c4381e13 * **github.com/hashicorp/go-getter** v1.5.9 -> v1.5.11 * **github.com/jsimonetti/rtnetlink** fd9a11f42291 -> 9dff439f7e79 * **github.com/jxskiss/base62** v1.0.0 -> v1.1.0 * **github.com/mdlayher/ethtool** 288d040e9d60 -> bc8fdcf6e99c * **github.com/mdlayher/genetlink** v1.0.0 -> v1.1.0 * **github.com/mdlayher/netlink** v1.4.2 -> v1.5.0 * **github.com/packethost/packngo** v0.20.0 -> v0.21.0 * **github.com/pelletier/go-toml** v1.9.4 **_new_** * **github.com/pmorjan/kmod** v1.0.0 **_new_** * **github.com/rivo/tview** 2a6de950f73b -> 90d72bc664f5 * **github.com/spf13/cobra** v1.2.1 -> v1.3.0 * **github.com/talos-systems/crypto** v0.3.4 -> 6fa2d93d0382 * **github.com/talos-systems/extras** v0.7.0-1-gd6b73a7 -> v0.8.0-alpha.0-1-g7c1f3cc * **github.com/talos-systems/go-blockdevice** v0.2.5 -> 6928ee43c303 * **github.com/talos-systems/pkgs** v0.9.0-1-g7a3419a -> v0.10.0-alpha.0-13-gc14eb99 * **github.com/talos-systems/tools** v0.9.0-1-gb1146f9 -> v0.10.0-alpha.0-1-g67314b1 * **github.com/u-root/u-root** v7.0.0 -> v0.8.0 * **github.com/vishvananda/netlink** f5de75959ad5 -> 650dca95af54 * **go.uber.org/zap** v1.19.1 -> v1.20.0 * **golang.org/x/net** 491a49abca63 -> 0dd24b26b47d * **golang.org/x/sys** 97ca703d548d -> da31bd327af9 * **golang.zx2c4.com/wireguard/wgctrl** dd7407c86d22 -> daad0b7ba671 * **google.golang.org/grpc** v1.42.0 -> v1.43.0 * **k8s.io/api** v0.23.1 -> v0.23.2 * **k8s.io/apimachinery** v0.23.1 -> v0.23.2 * **k8s.io/client-go** v0.23.1 -> v0.23.2 * **k8s.io/component-base** v0.23.1 -> v0.23.2 * **k8s.io/kubectl** v0.23.1 -> v0.23.2 * **k8s.io/kubelet** v0.23.1 -> v0.23.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.61 -> v1.2.62 Previous release can be found at [v0.14.0](https://github.com/talos-systems/talos/releases/tag/v0.14.0) ## [Talos 0.15.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.15.0-alpha.0) (2021-12-30) Welcome to the v0.15.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Machine Configuration Talos now preserves machine configuration as it was submitted to the node. There is some work still going on various cloud platforms to stop modifying machine configuration on the fly. ### Component Updates * Linux: 5.15.11 ### Wipe System Kernel Parameter Added new kernel parameter `talos.experimental.wipe=system` which can help resetting system disk for the machine and start over with a fresh installation. See [Resetting a Machine](https://www.talos.dev/docs/v0.15/guides/resetting-a-machine/#kernel-parameter) on how to use it. ### Contributors * Andrey Smirnov * Noel Georgi * Spencer Smith * Artem Chernyshev * Niklas Metje ### Changes
41 commits

* [`da0b36e61`](https://github.com/talos-systems/talos/commit/da0b36e616f7da7eb0c6791b9cf5e4ee2757f08f) feat: introduce `talos.exp.wipe` kernel param to wipe system disk * [`c079eb32b`](https://github.com/talos-systems/talos/commit/c079eb32bd7fc19d506146e2a9edf5b406e25e02) refactor: use AWS SDK to access AWS metadata service * [`2f4b9d8d6`](https://github.com/talos-systems/talos/commit/2f4b9d8d6d10c0aa753f405282aa99696b923bb4) feat: make machine configuration read-only in Talos (almost) * [`524f83d3d`](https://github.com/talos-systems/talos/commit/524f83d3d8af3857f178c179a9552a5f32b70f47) feat: use official Go SDK to fetch GCP instance metadata * [`d2a7e082c`](https://github.com/talos-systems/talos/commit/d2a7e082c24d0b42820b3ea454329a19178ba0a4) test: retry in discovery tests * [`f4219e530`](https://github.com/talos-systems/talos/commit/f4219e530ca7635ada666ae69071746d698939a8) chore: remove unused methods in AWS platform * [`35bc2940e`](https://github.com/talos-systems/talos/commit/35bc2940e375b99e0d6e22a26a05c25d642bf35a) fix: kexec on RPI4 * [`f235cfbae`](https://github.com/talos-systems/talos/commit/f235cfbaed8b5254e19616bfaaa8b48fd7d32e64) fix: multiple usability fixes * [`b3fbb2f31`](https://github.com/talos-systems/talos/commit/b3fbb2f312d5de0c14ffee567956b868a317aba7) test: don't build all images in the default CI pipeline * [`dac550a50`](https://github.com/talos-systems/talos/commit/dac550a50f4793194e4aeee98702a052925a0e88) docs: fix troubleshooting guide * [`83e8bec6b`](https://github.com/talos-systems/talos/commit/83e8bec6b9d4c0ecc689f45b15d7203bbf9bf0cc) feat: update Linux to 5.15.11 * [`d5a82b37e`](https://github.com/talos-systems/talos/commit/d5a82b37eb147a68ffd08fc8ec800edc92da9f9c) feat: remove `ApplyDynamicConfig` * [`3623da136`](https://github.com/talos-systems/talos/commit/3623da136bde51422ba1aec06e22dea2e3dfa756) feat: provide a way to load Linux kernel modules * [`4d1514add`](https://github.com/talos-systems/talos/commit/4d1514add6e0b972aee26a8ad63ef8f972050d46) docs: update Mayastor deployment process * [`cff1ff6d5`](https://github.com/talos-systems/talos/commit/cff1ff6d5c3a68063ed2c0c063daadf2474cc43f) feat: shell completion for `list`, `read` * [`19728437e`](https://github.com/talos-systems/talos/commit/19728437ead7ab6e95afc8bd7f70be3f861c9a6e) feat: output IPs when etcd needs to be bootstrapped * [`c297d66a1`](https://github.com/talos-systems/talos/commit/c297d66a130cba708fcb42f8f2e6b356c36f5109) test: attempt number on two on proper retries in CLI time tests * [`dc299da9e`](https://github.com/talos-systems/talos/commit/dc299da9e8e885b7a44c184ef3d251726aa934a8) docs: add arm64 option to talosctl download * [`f49f40a33`](https://github.com/talos-systems/talos/commit/f49f40a3361381e51d6986547be12ec3b4a3f24a) fix: pass path to conformance retrieve results * [`942c8074f`](https://github.com/talos-systems/talos/commit/942c8074fd14478089769e2b8132ea2796109721) docs: fork docs for 0.15 * [`880a7782c`](https://github.com/talos-systems/talos/commit/880a7782cbc703b38a2ff2b3d76c1eda621524ba) docs: update documentation for 0.14.0 release * [`dc9a0cfe9`](https://github.com/talos-systems/talos/commit/dc9a0cfe94b59c688d65ef74ebc04f273b8a72fb) chore: bump Go dependencies * [`773496935`](https://github.com/talos-systems/talos/commit/7734969356abac8355a31da08d47fafd4000e814) fix: config apply immediate * [`17c147488`](https://github.com/talos-systems/talos/commit/17c14748815e2ab928a9c0c8a079f65a63f0194e) test: retry `talosctl time` call in the tests * [`acf1ac0f1`](https://github.com/talos-systems/talos/commit/acf1ac0f1aff929ae9bf66b1c0322b4f83c0fef1) feat: show human-readable aliases in `talosctl get rd` * [`5532867b0`](https://github.com/talos-systems/talos/commit/5532867b05bb596f42516ff121b0a3a97176b3d1) refactor: rewrite the implementation of Processes API * [`80350861a`](https://github.com/talos-systems/talos/commit/80350861a2c1cee234d2f3a571d3993841c554d9) feat: update Kubernetes to 1.23.1 * [`4c96e936e`](https://github.com/talos-systems/talos/commit/4c96e936ed467ae7838258699bdd83fd6da15ae6) docs: add cilium guide * [`e3f2acb5e`](https://github.com/talos-systems/talos/commit/e3f2acb5e57f9b3e7b11986f180e287f1f693079) refactor: rewrite the check for unknown keys in the machine configuration * [`4175396a8`](https://github.com/talos-systems/talos/commit/4175396a89f836bb1835d201b59224b286eeb62a) refactor: use update go-blockdevice library with allocation fixes * [`b58f567a1`](https://github.com/talos-systems/talos/commit/b58f567a133b661cc045a995dd29ab5090dfe194) refactor: optimize Runtime config interface to avoid config marshaling * [`bb355c9ab`](https://github.com/talos-systems/talos/commit/bb355c9ab38a417ed471bf3ce7b1879609f5e806) chore: remove govalidator library * [`3af56bd2e`](https://github.com/talos-systems/talos/commit/3af56bd2e70e8964cc48b430b1e67e48052af682) test: update capi templates to v1beta1 * [`936b4c4ce`](https://github.com/talos-systems/talos/commit/936b4c4cee87697b3f08d51f22208b44b8a02db5) fix: update DHCP library with the panic fix * [`ab42886bf`](https://github.com/talos-systems/talos/commit/ab42886bf333dcaa9d3a1b765781ab19354de397) fix: allow kubelet to be started via the API * [`ec641f729`](https://github.com/talos-systems/talos/commit/ec641f7296ce62b2f9ba1353ff2eba70c2287c08) fix: use default time servers in time API if none are configured * [`79f213eec`](https://github.com/talos-systems/talos/commit/79f213eec65af46c4a3a4c4494d67ffc1b0a53ec) fix: cleanup affiliates * [`2dd0b5b68`](https://github.com/talos-systems/talos/commit/2dd0b5b68aa5b8efbc9b0bc4f8ebc159e2d991ab) chore: update Go to 1.17.5 * [`97ffa7a64`](https://github.com/talos-systems/talos/commit/97ffa7a645d7db93ee58032795f91131f6950e89) feat: upgrade kubelet version in `talosctl upgrade-k8s` * [`5bc5123eb`](https://github.com/talos-systems/talos/commit/5bc5123eb91386ca12e7e7f9fc0f66637343a642) docs: document `ip=` kernel argument * [`8e1d0bfb5`](https://github.com/talos-systems/talos/commit/8e1d0bfb5fbaf0849bdd07b73a8e3bda4e8c3b75) feat: update Kubernetes to 1.23.0

### Changes from talos-systems/crypto
1 commit

* [`6fa2d93`](https://github.com/talos-systems/crypto/commit/6fa2d93d0382299d5471e0de8e831c923398aaa8) fix: deepcopy nil fields as `nil`

### Changes from talos-systems/extras
1 commit

* [`495a5b2`](https://github.com/talos-systems/extras/commit/495a5b2a4964e11a9ae8629788c46a5140d07b10) feat: update Go to 1.17.5

### Changes from talos-systems/go-blockdevice
2 commits

* [`6928ee4`](https://github.com/talos-systems/go-blockdevice/commit/6928ee43c3034549e32f000f8b7bc16a6ebb7ed4) refactor: rewrite GPT serialize/deserialize functions * [`0c7e429`](https://github.com/talos-systems/go-blockdevice/commit/0c7e4296e01b3df815a935db3e30de6b9d4cc1d1) refactor: simplify middle endian functions

### Changes from talos-systems/pkgs
5 commits

* [`2779c3f`](https://github.com/talos-systems/pkgs/commit/2779c3fe825a47a7c392d077f4a38d7f7b2f8eb5) fix: kexec on rpi4 * [`950361f`](https://github.com/talos-systems/pkgs/commit/950361f79c03f718d73b60ddfbc6f661f4aa93b6) feat: update Linux to 5.15.11 * [`ad611bc`](https://github.com/talos-systems/pkgs/commit/ad611bc512bd67366e16f0b58c24dfca35f38a12) feat: provide build instructions for NVIDIA kernel module * [`b22723d`](https://github.com/talos-systems/pkgs/commit/b22723d0fb4766488f1bc50244d3cdfb9a9d8bbf) feat: update iPXE to the latest available version * [`a675c67`](https://github.com/talos-systems/pkgs/commit/a675c676e894c33626563f57e9c124e7628bc78f) feat: update Go to 1.17.5

### Changes from talos-systems/tools
1 commit

* [`9c2b9df`](https://github.com/talos-systems/tools/commit/9c2b9dfde84366c486f212cc074405cfb4d52127) feat: update Go to 1.17.5

### Dependency Changes * **cloud.google.com/go** v0.99.0 **_new_** * **github.com/aws/aws-sdk-go** v1.42.25 **_new_** * **github.com/docker/docker** v20.10.11 -> v20.10.12 * **github.com/google/nftables** 16a134723a96 -> 6f19c4381e13 * **github.com/jsimonetti/rtnetlink** fd9a11f42291 -> 9dff439f7e79 * **github.com/mdlayher/ethtool** 288d040e9d60 -> bc8fdcf6e99c * **github.com/mdlayher/genetlink** v1.0.0 -> v1.1.0 * **github.com/mdlayher/netlink** v1.4.2 -> v1.5.0 * **github.com/pmorjan/kmod** v1.0.0 **_new_** * **github.com/spf13/cobra** v1.2.1 -> v1.3.0 * **github.com/talos-systems/crypto** v0.3.4 -> 6fa2d93d0382 * **github.com/talos-systems/extras** v0.7.0-1-gd6b73a7 -> v0.8.0-alpha.0 * **github.com/talos-systems/go-blockdevice** v0.2.5 -> 6928ee43c303 * **github.com/talos-systems/pkgs** v0.9.0-1-g7a3419a -> v0.10.0-alpha.0-4-g2779c3f * **github.com/talos-systems/tools** v0.9.0-1-gb1146f9 -> v0.10.0-alpha.0 * **golang.org/x/net** 491a49abca63 -> fe4d6282115f * **golang.org/x/sys** 97ca703d548d -> 1d35b9e2eb4e * **golang.zx2c4.com/wireguard/wgctrl** dd7407c86d22 -> 7a385b3431de * **google.golang.org/grpc** v1.42.0 -> v1.43.0 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.61 -> v1.2.62 Previous release can be found at [v0.14.0](https://github.com/talos-systems/talos/releases/tag/v0.14.0) ## [Talos 0.14.0-alpha.2](https://github.com/talos-systems/talos/releases/tag/v0.14.0-alpha.2) (2021-11-30) Welcome to the v0.14.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Kexec and capabilities When kexec support is disabled Talos no longer drops Linux capabilities (`CAP_SYS_BOOT` and `CAP_SYS_MODULES`) for child processes. That is helpful for advanced use-cases like Docker-in-Docker. If you want to permanently disable kexec and capabilities dropping, pass `kexec_load_disabled=1` argument to the kernel. For example: ```yaml install: extraKernelArgs: - sysctl.kernel.kexec_load_disabled=1 ``` Please note that capabilities are dropped before machine configuration is loaded, so disabling kexec via `machine.sysctls` will not be enough. ### Cluster Discovery [Cluster Discovery](https://www.talos.dev/docs/v0.14/guides/discovery/) is enabled by default for Talos 0.14. Cluster Discovery can be disabled with `talosctl gen config --with-cluster-discovery=false`. ### Kubelet Kubelet service can now be restarted with `talosctl service kubelet restart`. Kubelet node IP configuration (`.machine.kubelet.nodeIP.validSubnets`) can now include negative subnet matches (prefixed with `!`). ### Log Shipping Talos can now [ship system logs](https://www.talos.dev/docs/v0.14/guides/logging/) to the configured destination using either JSON-over-UDP or JSON-over-TCP: see `.machine.logging` machine configuration option. ### NTP Sync Talos NTP sync process was improved to align better with kernel time adjustment periods and to filter out spikes. ### SideroLink A set of Talos ehancements is going to unlock a number of exciting features in the upcoming release of [Sidero](https://www.sidero.dev/): * `SideroLink`: a point-to-point Wireguard tunnel connecting Talos node back to the provisioning platform (Sidero). * event sink (kernel arg `talos.event.sink=http://10.0.0.1:4000`) delivers Talos internal events to the specified destination. * kmsg log delivery (kernel arg `talos.logging.kernel=tcp://10.0.0.1:4001`) sends kernel logs as JSON lines over TCP or UDP. ### `talosctl support` `talosctl` CLI tool now has a new subcommand called `support`, that can gather all cluster information that could help with future debugging in a single run. Output of the command is a `zip` archive with all talos service logs, kubernetes pod logs and manifests, talos resources manifests and so on. Generated archive does not contain any secret information so it is safe to send it for analysis to a third party. ### Component Updates * Linux: 5.15.5 * etcd: 3.5.1 * containerd: 1.5.8 * Kubernetes: 1.23.0-rc.0 * CoreDNS: 1.8.6 Talos is built with Go 1.17.3 ### Kubernetes Upgrade Enhancements `talosctl upgrade-k8s` now syncs all Talos manifest resources generated from templates. So there is no need to update CoreDNS, Flannel container manually after running `upgrade-k8s` anymore. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Serge Logvinov * Noel Georgi * Nico Berlee * Spencer Smith * Alex Zero * Andrew Rynhard * Branden Cash * David Haines * Gerard de Leeuw * Michael Fornaro * Rui Lopes ### Changes
136 commits

* [`e9f4b7b2`](https://github.com/talos-systems/talos/commit/e9f4b7b2041223309467227fa8b99cf35b797c72) feat: update Linux to 5.15.5 * [`4d0a75a3`](https://github.com/talos-systems/talos/commit/4d0a75a3f0795d5a0537c3b59007f97423c072ab) docs: add documentation about logging * [`8d1cbeef`](https://github.com/talos-systems/talos/commit/8d1cbeef9f2ae95d04035f5d999aa181fb88e9fc) chore: add API breaking changes detector * [`ed7fb9db`](https://github.com/talos-systems/talos/commit/ed7fb9db14554ccc191cc0c989aba38021a59690) feat: move kubelet proccesses to /podruntime cgroup * [`2cd3f9be`](https://github.com/talos-systems/talos/commit/2cd3f9be1f36dd3389ee528fa8f0b2548032c2f7) feat: filter out SideroLink addresses by default * [`0f169bf9`](https://github.com/talos-systems/talos/commit/0f169bf9b15239bfd35f371832211c42caf4349c) chore: add API deprecations mechanism * [`eaf6d472`](https://github.com/talos-systems/talos/commit/eaf6d4720383881c0dcf967dbc4e960d5ef49dd8) refactor: use random port listener in kernel log delivery tests * [`bf4c81e7`](https://github.com/talos-systems/talos/commit/bf4c81e7da854b7e9491f4ecb6fce89b026f4a9f) feat: kernel log (kmsg) delivery controller * [`f3149780`](https://github.com/talos-systems/talos/commit/f3149780e6663f7dc0fd0091cd6e3df605eac848) feat: update Kubernetes to 1.23.0-rc.0 * [`b824909d`](https://github.com/talos-systems/talos/commit/b824909d686b1f5a8cd20afe9ca5a4f291a6f12d) fix: disable kexec on RPi4 * [`3257751b`](https://github.com/talos-systems/talos/commit/3257751bc0a18e0d3bb7097191989440ae473ee6) fix: initialize Drainer properly * [`e4bc68bf`](https://github.com/talos-systems/talos/commit/e4bc68bf026966a3326872a1d342ef3b9c05cc9d) fix: leave only a single IPv4/IPv6 address as kubelet's node IP * [`e6d00741`](https://github.com/talos-systems/talos/commit/e6d007418efeb5d7f82eb82a35cddacc64ec99ba) feat: update pkgs - Linux 5.15.4, LibreSSL 3.2.7 * [`d5cbc364`](https://github.com/talos-systems/talos/commit/d5cbc3640256090e354b3896ffea72b8e58874bb) feat: add GCP ccm * [`7433150f`](https://github.com/talos-systems/talos/commit/7433150fd84ef0935e1aad91ca654892dc522806) feat: implement events sink controller * [`b4a406ae`](https://github.com/talos-systems/talos/commit/b4a406ae7c72e30ba488493682045495cd31dc4e) test: pin cluster API templates version to tag v1alpha4 * [`9427e78d`](https://github.com/talos-systems/talos/commit/9427e78dc6d581e752bf41a20f1e0379cc99d92d) fix: catch panics in network operator runs * [`d1f55f90`](https://github.com/talos-systems/talos/commit/d1f55f90128859d41ada63159d6b2d12e83fabac) fix: update blockdevice library to properly handle absent GPT * [`5ac64b2d`](https://github.com/talos-systems/talos/commit/5ac64b2d97c6e013c83a6618c6bece2e70dedd98) chore: set version in unit-tests * [`20d39c0b`](https://github.com/talos-systems/talos/commit/20d39c0b48b64f237270e13df7f277abd262d10b) chore: format .proto files * [`852bf4a7`](https://github.com/talos-systems/talos/commit/852bf4a7de815b75e2e632de18fae30bd1bc22be) feat: talosctl fish completion support * [`6bb75150`](https://github.com/talos-systems/talos/commit/6bb75150a394ee1ef4a3677ab4d8e73f27172209) fix: allow add_key and request_key in kubelet seccomp profile * [`6487b21f`](https://github.com/talos-systems/talos/commit/6487b21feb12291419c6fd1f6635a051b0a60afc) feat: update pkgs for u-boot, containerd, etc * [`f7d1e777`](https://github.com/talos-systems/talos/commit/f7d1e7776917475507aa99847f88b9c22c9f7b95) feat: provide SideroLink client implementation * [`58892cd6`](https://github.com/talos-systems/talos/commit/58892cd697676c19f830f55e8ba1d84cd6000621) fix: unblock events watch on context cancel * [`caa76be2`](https://github.com/talos-systems/talos/commit/caa76be2c982d9d6bc8d3103f16b5915796f76b1) fix: containerd failed to load plugin * [`1ffa8e04`](https://github.com/talos-systems/talos/commit/1ffa8e0480084264eee551ad177b2443ddb02ead) feat: add ULA prefix for SideroLink * [`c6a67b86`](https://github.com/talos-systems/talos/commit/c6a67b8662bb3c6efbe912b19699ace19e70dd3f) fix: ignore not existing nodes on cordoning * [`f7302525`](https://github.com/talos-systems/talos/commit/f730252579879df2e95878de292f17f791740804) feat: add new event types * [`7c9b082f`](https://github.com/talos-systems/talos/commit/7c9b082f74f26349a0e309d9818d5bc55e672378) feat: update Kubernetes to 1.23.0-beta.0 * [`750e31c4`](https://github.com/talos-systems/talos/commit/750e31c4a46f2835eca9fc9a085d2bb64e582e40) fix: ignore EBUSY from `kexec_file_load` * [`2d11b595`](https://github.com/talos-systems/talos/commit/2d11b59558c98f4cd07a50b25be29b5c355a4495) fix: ignore virtual IP as kubelet node IPs * [`030fd349`](https://github.com/talos-systems/talos/commit/030fd349b1c0669d7059f8c6883c85096f6f9ef5) fix: don't run kexec prepare on shutdown and reset * [`6dcce20e`](https://github.com/talos-systems/talos/commit/6dcce20e6fa088c3063aab728912731f5e827eb7) test: set proper pod CIDR for Cilium tests * [`695300da`](https://github.com/talos-systems/talos/commit/695300dac46c114b8e7e40abdaeece25f7079c88) release(v0.14.0-alpha.1): prepare release * [`753a8218`](https://github.com/talos-systems/talos/commit/753a82188f227da4f2f40da5f4d46ebe45774455) refactor: move pkg/resources to machinery * [`0102a64a`](https://github.com/talos-systems/talos/commit/0102a64a5f6de2c3fe5d7792c2c5845fc737edff) refactor: remove pkg/resources dependencies on wgtypes, netx * [`7462733b`](https://github.com/talos-systems/talos/commit/7462733bcb075b923b8c7ba4a763308c641c49a2) chore: update golangci-lint * [`032c99a0`](https://github.com/talos-systems/talos/commit/032c99a0300ccb09105a07434884d2b1f57e537d) refactor: remove pkg/resources dependencies on k8s and base62 * [`4a5cff45`](https://github.com/talos-systems/talos/commit/4a5cff45f397ac29b7bfc390f11691c32d8615b2) perf: raspberry PIs clockspeed as fast as firmware allows * [`a76f6d69`](https://github.com/talos-systems/talos/commit/a76f6d69dbfdf34e4383dd5d2ee9f8cca4661e87) feat: allow kubelet to be restarted and provide negative nodeIP subnets * [`189221d5`](https://github.com/talos-systems/talos/commit/189221d589c1c9d4fc012dd9e31fd6d142d88dde) chore: update dependencies * [`41f0aecc`](https://github.com/talos-systems/talos/commit/41f0aecc1d3c4afce96d034f160fa9f120c67e85) docs: update partition info * [`95105071`](https://github.com/talos-systems/talos/commit/95105071de29f70552bd7c0881c2cc2e7c78c0ac) chore: fix simple issues found by golangci-lint * [`d4b0ca21`](https://github.com/talos-systems/talos/commit/d4b0ca21a1ee1183b28738bb3d9ca251e1968fe7) test: retry upgrade mutex lock failures * [`4357e9a8`](https://github.com/talos-systems/talos/commit/4357e9a849fcb7fb66378bdd767a926dde0c4318) docs: add Talos partions info * [`8e8687d7`](https://github.com/talos-systems/talos/commit/8e8687d7592d4bc071981478491d70489e7dd4a9) fix: use temporary sonobuoy version * [`e4e8e873`](https://github.com/talos-systems/talos/commit/e4e8e8737f564be47098e284706a63ef84636890) test: disable e2e-misc test with Canal CNI * [`897da2f6`](https://github.com/talos-systems/talos/commit/897da2f6efc571a66d14722a67bbc401bad31887) docs: common typos * [`a50483dd`](https://github.com/talos-systems/talos/commit/a50483dddfd9a742b998f509ee713af996a2484e) feat: update Linux to 5.15.1 * [`a2233bfe`](https://github.com/talos-systems/talos/commit/a2233bfe46bfb55d71cfc07174f6f22aee6d2651) fix: improve NTP sync process * [`7efc1238`](https://github.com/talos-systems/talos/commit/7efc1238ee285d55c4619b6a40190b54ff953a66) fix: parse partition size correctly * [`d6147eb1`](https://github.com/talos-systems/talos/commit/d6147eb17d2ebf263ca0537068bbbba6d3ced061) chore: update sonobuoy * [`efbae785`](https://github.com/talos-systems/talos/commit/efbae7857d09aa7e5e704d5989efced5aa655259) fix: use etc folder for du cli tests * [`198eea51`](https://github.com/talos-systems/talos/commit/198eea51a81bf041470c3c88cb6cb97af3a4e203) fix: wait for follow reader to start before writing to the file * [`e7f715eb`](https://github.com/talos-systems/talos/commit/e7f715eb0ca0587a05949910cafdeb486654b577) chore: log KubeSpan IPs overlaps * [`82a1ad16`](https://github.com/talos-systems/talos/commit/82a1ad1681bf262dcc68fc9cbac71ff2eb5639af) chore: bump dependencies * [`e8fccbf5`](https://github.com/talos-systems/talos/commit/e8fccbf5351ec2481813553181cb73b8f16c915a) fix: clear time adjustment error when setting time to specific value * [`e6f90bb4`](https://github.com/talos-systems/talos/commit/e6f90bb41a757b5173bbbf7554b6f85c08aaf58e) chore: remove unused parameters * [`785161d1`](https://github.com/talos-systems/talos/commit/785161d19f68fb64451cf3d887b67f85a8bcb952) feat: update k8s to 1.23.0-alpha.4 * [`fe228d7c`](https://github.com/talos-systems/talos/commit/fe228d7c85a1f8437398061b18c090962adc9f29) fix: do not use yaml.v2 in the support cmd * [`9b48ca21`](https://github.com/talos-systems/talos/commit/9b48ca21731cce53f0a61f05f74dcd264417d784) fix: endpoints and nodes in generated talosconfig * [`6e16fd2f`](https://github.com/talos-systems/talos/commit/6e16fd2feeb3f8bf0b99e6cbe21047b7a5c1f05c) chore: update tools, pkgs, and extras * [`261c497c`](https://github.com/talos-systems/talos/commit/261c497c71eb5ab5197bef05d8c209dbeb770d3f) feat: implement `talosctl support` command * [`fc7dc454`](https://github.com/talos-systems/talos/commit/fc7dc454840e100d82bb036a7f065293234593f7) chore: check our API idiosyncrasies * [`b1584429`](https://github.com/talos-systems/talos/commit/b15844298a6bfedca5acc0cc27061666481eb94b) feat: use GCP deployment manager * [`3e7d4df9`](https://github.com/talos-systems/talos/commit/3e7d4df99019e3cc6d9a90920d377c73a76ac577) chore: bump dependencies * [`88f24229`](https://github.com/talos-systems/talos/commit/88f2422955690d1eca1e21cd60a35e1d49141e3d) refactor: get rid of prometheus/procfs dependency in pkg/resources * [`dd196d30`](https://github.com/talos-systems/talos/commit/dd196d3006d29ae5cae5d43b648da1ca2e5af236) refactor: prepare for move of pkg/resources to machinery * [`f6110f80`](https://github.com/talos-systems/talos/commit/f6110f8036bc176188abb583bfa51296c4d3897d) fix: remove listening socket to fix Talos in a container restart * [`53bbb13e`](https://github.com/talos-systems/talos/commit/53bbb13ed8592978dc27578fa79b3a2018941427) docs: update docs with emmc boot guide * [`8329d211`](https://github.com/talos-systems/talos/commit/8329d21114abf841788be64765378343c12eaf69) chore: split polymorphic RootSecret resource into specific types * [`c97becdd`](https://github.com/talos-systems/talos/commit/c97becdd9548d85b2b894a05765f93dcdf9ad803) chore: remove interfaces and routes APIs * [`d798635d`](https://github.com/talos-systems/talos/commit/d798635d993a21392b8a7972a689c4be0728db32) feat: automatically limit kubelet node IP family based on service CIDRs * [`205a8d6d`](https://github.com/talos-systems/talos/commit/205a8d6dc495e25af87bf0b920d0f55b8a27bbfd) chore: make nethelpers build on all OSes * [`5b5dd49f`](https://github.com/talos-systems/talos/commit/5b5dd49f64bef584000655687e5b9c5d25af6a93) feat: extract JSON fields from more log messages * [`eb4f1182`](https://github.com/talos-systems/talos/commit/eb4f11822dc0b35541e0576a75ca263ca96d4981) docs: create cluster in hetzner cloud * [`728164e2`](https://github.com/talos-systems/talos/commit/728164e25a5705ae5194b416941f3607d592b140) docs: fix kexec_load_disabled param name in release notes * [`f6328f09`](https://github.com/talos-systems/talos/commit/f6328f09a2bf8d233a48354dd548fb740e509341) fix: fix filename typo * [`01b0f0ab`](https://github.com/talos-systems/talos/commit/01b0f0abb341b387f16d9b3a142af742f36c8c2b) release(v0.14.0-alpha.0): prepare release * [`8b620653`](https://github.com/talos-systems/talos/commit/8b6206537a30be049f74f8c4c7350028e6e56c74) fix: skip generating empty `.machine.logging` * [`60ad0063`](https://github.com/talos-systems/talos/commit/60ad006367e73f56fd69726e0044f1ce48f18a8b) fix: don't drop ability to use ambient capabilities * [`b6b78e7f`](https://github.com/talos-systems/talos/commit/b6b78e7fef3f6ef0c566e1815d1e28f16f868c93) test: add cluster discovery integration tests * [`97d64d16`](https://github.com/talos-systems/talos/commit/97d64d160ce7e71c3107adbd31404853f543f7cc) fix: hcloud network config changes * [`4c76865d`](https://github.com/talos-systems/talos/commit/4c76865d0ecec726e801a4b8f87e09476481d808) feat: multiple logging improvements * [`1d1e1df6`](https://github.com/talos-systems/talos/commit/1d1e1df643832478aaa715aea5f51ad2e61e2880) fix: handle skipped mounts correctly * [`0a964d92`](https://github.com/talos-systems/talos/commit/0a964d921922a247293e36b5fecaab466b91d924) test: fix openstack unit-test stability * [`72f62ac2`](https://github.com/talos-systems/talos/commit/72f62ac27b5d0a72db409fd003a7cf9c41a03d7c) chore: bump Go and Docker dependencies * [`9c48ebe8`](https://github.com/talos-systems/talos/commit/9c48ebe8f94afa85921ee5f1c1e9315201905a92) fix: gcp fetching externalIP * [`6c297268`](https://github.com/talos-systems/talos/commit/6c297268ce596c2a875b7c419c85317dc24d9f4f) test: fix e2e k8s version * [`ae5af9d3`](https://github.com/talos-systems/talos/commit/ae5af9d3fad399dea95c316d94e3e66b124bfb24) feat: update Kubernetes to 1.23.0-alpha.3 * [`28d3a69e`](https://github.com/talos-systems/talos/commit/28d3a69e9d4ae7ffa231804e26af6d1f39c07afd) feat: openstack config-drive support * [`2258bc49`](https://github.com/talos-systems/talos/commit/2258bc4918e89b3d6fcb841b2ad677f114ddba7e) test: update GCP e2e script to work with new templates * [`36b6ace2`](https://github.com/talos-systems/talos/commit/36b6ace25378e8c4a607de6efb6b89a2d52f5cea) feat: update Linux to 5.10.75 * [`38516a54`](https://github.com/talos-systems/talos/commit/38516a5499d933a8038ce6768946ff096e7c6f98) test: update Talos versions in upgrade tests * [`cff20ec7`](https://github.com/talos-systems/talos/commit/cff20ec78340b3855751e13f2ad0e54bd47e9989) fix: change services OOM score * [`666a2b62`](https://github.com/talos-systems/talos/commit/666a2b6207d257edda20c9e0411b0d4cd4112aa6) feat: azure platform ipv6 support * [`d32814e3`](https://github.com/talos-systems/talos/commit/d32814e302c370ec1e82aa2879186a034cd2a905) feat: extract JSON fields from log lines * [`e77d81ff`](https://github.com/talos-systems/talos/commit/e77d81fff31d68f762da3741846f95a6d2303903) fix: treat literal 'unknown' as a valid machine type * [`c8e404e3`](https://github.com/talos-systems/talos/commit/c8e404e356878f6cd819a33386b351c1c152c3f5) test: update vars for AWS cluster * [`ad23891b`](https://github.com/talos-systems/talos/commit/ad23891b1f6b33409721528c6771304b7ab94b2c) feat: update CoreDNS version 1.8.6 * [`41299cae`](https://github.com/talos-systems/talos/commit/41299cae9961665c2bf2a642290f8309683f040d) feat: udev rules support * [`5237fdc9`](https://github.com/talos-systems/talos/commit/5237fdc957efbb018649b866bfb756f280f589a2) feat: send JSON logs over UDP * [`6d44587a`](https://github.com/talos-systems/talos/commit/6d44587a4d4c16defa6bb06329cdfc6e39c95188) feat: coredns service dualstack * [`12f7888b`](https://github.com/talos-systems/talos/commit/12f7888b75fa2498e0f8305f5d6910cecad5c65c) feat: feed control plane endpoints on workers from cluster discovery * [`431e4fb4`](https://github.com/talos-systems/talos/commit/431e4fb4b690fa4955c407d8dd8156bdecd9a2c5) chore: bump Go and Docker dependencies * [`89f3b9f8`](https://github.com/talos-systems/talos/commit/89f3b9f8d41e33c4cb736917f418ab5cfb9edd83) feat: update etcd to 3.5.1 * [`e60469a3`](https://github.com/talos-systems/talos/commit/e60469a38cb81ace2039bae1927eb6c5f1f0ad1f) feat: initial support for JSON logging * [`68c420e3`](https://github.com/talos-systems/talos/commit/68c420e3c96a0fdc3b3e6cd75be24cc797c48e09) feat: enable cluster discovery by default * [`3e100aa9`](https://github.com/talos-systems/talos/commit/3e100aa97734ea809563e23fc36e19bdd3df1920) test: workaround EventsWatch test flakiness * [`9bd4838a`](https://github.com/talos-systems/talos/commit/9bd4838ac10abbd4760da4fb905d7639a1c26f9f) chore: stop using sonobuoy CLI * [`6ad45951`](https://github.com/talos-systems/talos/commit/6ad45951975aac48fdcc282e5a0e31344058d07e) docs: fix field names for bonding configuration * [`d7a3b7b5`](https://github.com/talos-systems/talos/commit/d7a3b7b5b70293884d2e19c6a59b14ebcfa24397) chore: use discovery-client and discovery-api modules * [`d6309eed`](https://github.com/talos-systems/talos/commit/d6309eed6618abd1b4efd0e3cd18a6c0df39378f) docs: create docs for Talos 0.14 * [`c0fda643`](https://github.com/talos-systems/talos/commit/c0fda6436ae27d8bbc210ee74a1128968108f6a6) fix: attempt to clean up tasks in containerd runner * [`8cf442da`](https://github.com/talos-systems/talos/commit/8cf442daa60d911caff59d1c2c05dd77652c8b51) chore: bump tools, pkgs, extras * [`0dad5f4d`](https://github.com/talos-systems/talos/commit/0dad5f4d7846f3fb41ff4ba27395023d33796a61) chore: small cleanup * [`e3e2113a`](https://github.com/talos-systems/talos/commit/e3e2113adc058940725b1041827d7adb8895c6cf) feat: upgrade CoreDNS during `upgrade-k8s` call * [`d92c98e1`](https://github.com/talos-systems/talos/commit/d92c98e19a054472bff3e0d646756f16c5e65bbf) docs: fix discovery service documentation link * [`e44b11c5`](https://github.com/talos-systems/talos/commit/e44b11c595e4cab796128a932843b90734ff6d1d) feat: update containerd to 1.5.7, bump Go dependencies * [`24129307`](https://github.com/talos-systems/talos/commit/24129307a14d6e59c6bc0d3586c0c95969bde679) docs: make Talos 0.13 docs latest, update documentation * [`31b6e39e`](https://github.com/talos-systems/talos/commit/31b6e39e58a27e1f2c1be500fca8636971bfa5c6) fix: delete expired affiliates from the discovery service * [`877a2b6f`](https://github.com/talos-systems/talos/commit/877a2b6fc00eaa7574349f9086d78c04df163840) test: bump CAPI components to v1alpha4 * [`2ba0e0ac`](https://github.com/talos-systems/talos/commit/2ba0e0ac4ad460409101f5f2374e66698adbba4c) docs: add KubeSpan documentation * [`997873b6`](https://github.com/talos-systems/talos/commit/997873b6d3116b59ebb46df66b8aa1cee06df92f) fix: use ECDSA-SHA512 when generating certs for Talos < 0.13 * [`7137166d`](https://github.com/talos-systems/talos/commit/7137166d1d5817e2d44ead4a01796275f92a9d4a) fix: allow overriding `audit-policy-file` in `kube-apiserver` static pod * [`8fcd4219`](https://github.com/talos-systems/talos/commit/8fcd4219671a9359880ba344a2ec7fd65dfe5e2a) chore: fix integration-qemu-race * [`91a858b5`](https://github.com/talos-systems/talos/commit/91a858b53704ede86392fe3c155ce9ab3c2d406f) fix: sort output of the argument builder * [`657f7a56`](https://github.com/talos-systems/talos/commit/657f7a56b10089e0dc551e178bc85b28d8003243) fix: use ECDSA-SHA256 signature algorithm for Kubernetes certs * [`983d2459`](https://github.com/talos-systems/talos/commit/983d2459e2aa036774828f773bbaba5697665ae7) feat: suppress logging NTP sync to the console * [`022c7335`](https://github.com/talos-systems/talos/commit/022c7335f3063675ab744454a2ad4b2c0c19bfbc) fix: add interface route if DHCP4 router is not directly routeable * [`66a1579e`](https://github.com/talos-systems/talos/commit/66a1579ea7d2a9c4fdf15b762cd024c54b3e8ffb) fix: don't enable 'no new privs' on the system level * [`423861cf`](https://github.com/talos-systems/talos/commit/423861cf9f99eaf034a4f0cb243d73d1275c3f38) feat: don't drop capabilities if kexec is disabled * [`facc8c38`](https://github.com/talos-systems/talos/commit/facc8c38a021610da900a45f397aea8ddfc74f1c) docs: fix documentation for cluster discovery * [`ce65ca4e`](https://github.com/talos-systems/talos/commit/ce65ca4e4a2994f901f01ce5ca269d6df86f0de8) chore: build using only amd64 builders * [`e9b0f010`](https://github.com/talos-systems/talos/commit/e9b0f010d2855b968a5d8b8b5fbcd268e06ba302) chore: update docker image in the pipeline

### Changes since v0.14.0-alpha.1
34 commits

* [`e9f4b7b2`](https://github.com/talos-systems/talos/commit/e9f4b7b2041223309467227fa8b99cf35b797c72) feat: update Linux to 5.15.5 * [`4d0a75a3`](https://github.com/talos-systems/talos/commit/4d0a75a3f0795d5a0537c3b59007f97423c072ab) docs: add documentation about logging * [`8d1cbeef`](https://github.com/talos-systems/talos/commit/8d1cbeef9f2ae95d04035f5d999aa181fb88e9fc) chore: add API breaking changes detector * [`ed7fb9db`](https://github.com/talos-systems/talos/commit/ed7fb9db14554ccc191cc0c989aba38021a59690) feat: move kubelet proccesses to /podruntime cgroup * [`2cd3f9be`](https://github.com/talos-systems/talos/commit/2cd3f9be1f36dd3389ee528fa8f0b2548032c2f7) feat: filter out SideroLink addresses by default * [`0f169bf9`](https://github.com/talos-systems/talos/commit/0f169bf9b15239bfd35f371832211c42caf4349c) chore: add API deprecations mechanism * [`eaf6d472`](https://github.com/talos-systems/talos/commit/eaf6d4720383881c0dcf967dbc4e960d5ef49dd8) refactor: use random port listener in kernel log delivery tests * [`bf4c81e7`](https://github.com/talos-systems/talos/commit/bf4c81e7da854b7e9491f4ecb6fce89b026f4a9f) feat: kernel log (kmsg) delivery controller * [`f3149780`](https://github.com/talos-systems/talos/commit/f3149780e6663f7dc0fd0091cd6e3df605eac848) feat: update Kubernetes to 1.23.0-rc.0 * [`b824909d`](https://github.com/talos-systems/talos/commit/b824909d686b1f5a8cd20afe9ca5a4f291a6f12d) fix: disable kexec on RPi4 * [`3257751b`](https://github.com/talos-systems/talos/commit/3257751bc0a18e0d3bb7097191989440ae473ee6) fix: initialize Drainer properly * [`e4bc68bf`](https://github.com/talos-systems/talos/commit/e4bc68bf026966a3326872a1d342ef3b9c05cc9d) fix: leave only a single IPv4/IPv6 address as kubelet's node IP * [`e6d00741`](https://github.com/talos-systems/talos/commit/e6d007418efeb5d7f82eb82a35cddacc64ec99ba) feat: update pkgs - Linux 5.15.4, LibreSSL 3.2.7 * [`d5cbc364`](https://github.com/talos-systems/talos/commit/d5cbc3640256090e354b3896ffea72b8e58874bb) feat: add GCP ccm * [`7433150f`](https://github.com/talos-systems/talos/commit/7433150fd84ef0935e1aad91ca654892dc522806) feat: implement events sink controller * [`b4a406ae`](https://github.com/talos-systems/talos/commit/b4a406ae7c72e30ba488493682045495cd31dc4e) test: pin cluster API templates version to tag v1alpha4 * [`9427e78d`](https://github.com/talos-systems/talos/commit/9427e78dc6d581e752bf41a20f1e0379cc99d92d) fix: catch panics in network operator runs * [`d1f55f90`](https://github.com/talos-systems/talos/commit/d1f55f90128859d41ada63159d6b2d12e83fabac) fix: update blockdevice library to properly handle absent GPT * [`5ac64b2d`](https://github.com/talos-systems/talos/commit/5ac64b2d97c6e013c83a6618c6bece2e70dedd98) chore: set version in unit-tests * [`20d39c0b`](https://github.com/talos-systems/talos/commit/20d39c0b48b64f237270e13df7f277abd262d10b) chore: format .proto files * [`852bf4a7`](https://github.com/talos-systems/talos/commit/852bf4a7de815b75e2e632de18fae30bd1bc22be) feat: talosctl fish completion support * [`6bb75150`](https://github.com/talos-systems/talos/commit/6bb75150a394ee1ef4a3677ab4d8e73f27172209) fix: allow add_key and request_key in kubelet seccomp profile * [`6487b21f`](https://github.com/talos-systems/talos/commit/6487b21feb12291419c6fd1f6635a051b0a60afc) feat: update pkgs for u-boot, containerd, etc * [`f7d1e777`](https://github.com/talos-systems/talos/commit/f7d1e7776917475507aa99847f88b9c22c9f7b95) feat: provide SideroLink client implementation * [`58892cd6`](https://github.com/talos-systems/talos/commit/58892cd697676c19f830f55e8ba1d84cd6000621) fix: unblock events watch on context cancel * [`caa76be2`](https://github.com/talos-systems/talos/commit/caa76be2c982d9d6bc8d3103f16b5915796f76b1) fix: containerd failed to load plugin * [`1ffa8e04`](https://github.com/talos-systems/talos/commit/1ffa8e0480084264eee551ad177b2443ddb02ead) feat: add ULA prefix for SideroLink * [`c6a67b86`](https://github.com/talos-systems/talos/commit/c6a67b8662bb3c6efbe912b19699ace19e70dd3f) fix: ignore not existing nodes on cordoning * [`f7302525`](https://github.com/talos-systems/talos/commit/f730252579879df2e95878de292f17f791740804) feat: add new event types * [`7c9b082f`](https://github.com/talos-systems/talos/commit/7c9b082f74f26349a0e309d9818d5bc55e672378) feat: update Kubernetes to 1.23.0-beta.0 * [`750e31c4`](https://github.com/talos-systems/talos/commit/750e31c4a46f2835eca9fc9a085d2bb64e582e40) fix: ignore EBUSY from `kexec_file_load` * [`2d11b595`](https://github.com/talos-systems/talos/commit/2d11b59558c98f4cd07a50b25be29b5c355a4495) fix: ignore virtual IP as kubelet node IPs * [`030fd349`](https://github.com/talos-systems/talos/commit/030fd349b1c0669d7059f8c6883c85096f6f9ef5) fix: don't run kexec prepare on shutdown and reset * [`6dcce20e`](https://github.com/talos-systems/talos/commit/6dcce20e6fa088c3063aab728912731f5e827eb7) test: set proper pod CIDR for Cilium tests

### Changes from talos-systems/discovery-api
2 commits

* [`db279ef`](https://github.com/talos-systems/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/talos-systems/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from talos-systems/discovery-client
2 commits

* [`a9a5e9b`](https://github.com/talos-systems/discovery-client/commit/a9a5e9bfddaa670e0fb4f57510167d377cf09b07) feat: initial client code * [`98eb999`](https://github.com/talos-systems/discovery-client/commit/98eb9999c0c76d2f93378108b7e22de6bcae6e81) chore: initial commit

### Changes from talos-systems/extras
2 commits

* [`2bb2efc`](https://github.com/talos-systems/extras/commit/2bb2efcbe68bcce2172b9ac7771dde1d0d2b6d3c) chore: update pkgs and tools * [`d6e8b3a`](https://github.com/talos-systems/extras/commit/d6e8b3a78e9a3371472753286c559627932466c3) chore: update pkgs and tools

### Changes from talos-systems/go-blockdevice
2 commits

* [`15b182d`](https://github.com/talos-systems/go-blockdevice/commit/15b182db0cd233b163ed83d1724c7e28cf29d71a) fix: return partition table not exist when trying to read an empty dev * [`b9517d5`](https://github.com/talos-systems/go-blockdevice/commit/b9517d51120d385f97b0026f99ce3c4782940c37) fix: resize partition

### Changes from talos-systems/go-smbios
1 commit

* [`fd5ec8c`](https://github.com/talos-systems/go-smbios/commit/fd5ec8ce4873790b7fbd46dba9d7f49c9de7176a) fix: remove useless (?) goroutines leading to data race error

### Changes from talos-systems/net
2 commits

* [`b4b7181`](https://github.com/talos-systems/net/commit/b4b718179a1aa68e4f54422baf08ca3761723d2d) feat: add a way to filter list of IPs for the machine * [`0abe5bd`](https://github.com/talos-systems/net/commit/0abe5bdae8f85e4e976bc4d90e95dcb4be8fb853) feat: implement FilterIPs function

### Changes from talos-systems/pkgs
22 commits

* [`422276d`](https://github.com/talos-systems/pkgs/commit/422276d5c06b00e63ec0ba0c70b642eedd13eea6) feat: update Linux to 5.15.5 * [`d385e24`](https://github.com/talos-systems/pkgs/commit/d385e24e868682ca68c74ecdae94af2bd17b4a28) chore: update LibreSSL to 3.2.7 * [`39a3b76`](https://github.com/talos-systems/pkgs/commit/39a3b76c3dd6d50b266b594c8ee2cc2d5537a5e0) feat: update Linux to 5.15.4 * [`ca30b50`](https://github.com/talos-systems/pkgs/commit/ca30b509b4cd3ab591a27eb6f76d5c2fd9da3b7e) feat: update u-boot to 2021.10 * [`cea93f1`](https://github.com/talos-systems/pkgs/commit/cea93f1163d79cea8b44e2f9f0bd645aa2404003) chore: add conformance * [`79d16b8`](https://github.com/talos-systems/pkgs/commit/79d16b87f31759a1fbb3eab0a06728382983fce1) feat: update containerd to 1.5.8 * [`1c76107`](https://github.com/talos-systems/pkgs/commit/1c761077176f46388c4335bb7ceb9f58cc456c44) feat: add mdraid 1/0/10 * [`740da24`](https://github.com/talos-systems/pkgs/commit/740da24d801cc5a8f47f6badf788faea872a6e72) feat: bump raspberrypi-firmware to 1.20211029 * [`832dae4`](https://github.com/talos-systems/pkgs/commit/832dae4175d7cd3336fb3637134833e7b9fe1719) fix: enable CONFIG_DM_SNAPSHOT * [`f307e64`](https://github.com/talos-systems/pkgs/commit/f307e64e6b2e8a29cff5508ae2da4ae92286771e) feat: update Linux to 5.15.1 * [`4f0f238`](https://github.com/talos-systems/pkgs/commit/4f0f238decfb93561c5871207da8dd3b1d350961) chore: update tools * [`932c3cf`](https://github.com/talos-systems/pkgs/commit/932c3cfff9fcb9ffb671d7e5e10b0ca7c290c058) feat: update libseccomp to 2.5.3 * [`7f3311e`](https://github.com/talos-systems/pkgs/commit/7f3311e2a3d2ef759d9f909f9872e5b98f9682a5) feat: update cpu governor to schedutil * [`b4cdb99`](https://github.com/talos-systems/pkgs/commit/b4cdb991a4aa60b1fa859b44efcd57705d89e9ee) fix: update containerd shas * [`80a63d4`](https://github.com/talos-systems/pkgs/commit/80a63d4cf2231383266f244f608a958b94872a99) feat: update Linux to 5.10.75 * [`5c98efd`](https://github.com/talos-systems/pkgs/commit/5c98efd95d2e2e036d845c63b6268583d853d3fd) feat: add QLogic QED 25/40/100Gb Ethernet NIC driver * [`bfb2365`](https://github.com/talos-systems/pkgs/commit/bfb2365b04aa7f92ef87799c47ffde6bc2395785) feat: enable driver for SuperMicro raid controller * [`657e16b`](https://github.com/talos-systems/pkgs/commit/657e16b3976ba376401797277e85dd67c9b7e64e) feat: enable Intel VMD driver * [`f7d9d72`](https://github.com/talos-systems/pkgs/commit/f7d9d728d468b9e3af2552595c9fb145f9008ef3) feat: enable smarpqi driver and related options * [`bca3be0`](https://github.com/talos-systems/pkgs/commit/bca3be04e22367585a60afa421e78707d2c6a1de) feat: enable aqtion device driver * [`b88127a`](https://github.com/talos-systems/pkgs/commit/b88127afec39d3039e93dfd6bc20a62415d396f0) chore: update tools * [`971735f`](https://github.com/talos-systems/pkgs/commit/971735f4b1914cb1c8f2575aeda9b354ecf842f6) feat: update containerd to 1.5.7

### Changes from talos-systems/siderolink
6 commits

* [`d0612a7`](https://github.com/talos-systems/siderolink/commit/d0612a724a1b1336a2bc6a99ed3178e3e40f6d9b) refactor: pass in listener to the log receiver * [`d86cdd5`](https://github.com/talos-systems/siderolink/commit/d86cdd59ee7a0e0504b739a913991c272c7fb3f5) feat: implement logreceiver for kernel logs * [`f7cadbc`](https://github.com/talos-systems/siderolink/commit/f7cadbcdfbb84d367e27b5af32e89c138d72d9d7) fix: handle duplicate peer updates * [`0755b24`](https://github.com/talos-systems/siderolink/commit/0755b24d4682410b251a2a9d662960da15153106) feat: initial implementation of SideroLink * [`ee73ea9`](https://github.com/talos-systems/siderolink/commit/ee73ea9575a81be7685f24936b2c48a4508a159e) feat: add Talos events sink proto files and the reference implementation * [`1e2cd9d`](https://github.com/talos-systems/siderolink/commit/1e2cd9d38621234a0a6010e33b1bab264f4d9bdf) Initial commit

### Changes from talos-systems/tools
6 commits

* [`96e0231`](https://github.com/talos-systems/tools/commit/96e0231c0f01a9fe6120a941b21c40e1a37bab36) feat: update squashfs-tools to 4.5 * [`2c9c826`](https://github.com/talos-systems/tools/commit/2c9c826201dc9f4d869fc00ffac63ee10f5e1101) feat: update libseccomp to 2.5.3 * [`f713a7c`](https://github.com/talos-systems/tools/commit/f713a7cd96fb1176687f5a6c4ec7d1345bb5e568) feat: update protobuf to 3.19.1, grpc-go to 1.42.0 * [`972c5ef`](https://github.com/talos-systems/tools/commit/972c5ef413f721ab5ad835dab526850620d05003) feat: update Go to 1.17.3 * [`f63848c`](https://github.com/talos-systems/tools/commit/f63848c1a756807879f22c752155fe1f36ccec32) feat: update PCRE version and source host * [`fab7532`](https://github.com/talos-systems/tools/commit/fab7532fd59519d62a3985684a250273a14f1893) feat: update Go to 1.17.2

### Dependency Changes * **github.com/AlekSi/pointer** v1.1.0 -> v1.2.0 * **github.com/cenkalti/backoff/v4** v4.1.2 **_new_** * **github.com/containerd/cgroups** v1.0.1 -> v1.0.2 * **github.com/containerd/containerd** v1.5.5 -> v1.5.8 * **github.com/docker/docker** v20.10.8 -> v20.10.11 * **github.com/evanphx/json-patch** v4.11.0 -> v5.6.0 * **github.com/gosuri/uiprogress** v0.0.1 **_new_** * **github.com/hashicorp/go-getter** v1.5.8 -> v1.5.9 * **github.com/hetznercloud/hcloud-go** v1.32.0 -> v1.33.1 * **github.com/insomniacslk/dhcp** b95caade3eac -> ad197bcd36fd * **github.com/jsimonetti/rtnetlink** 435639c8e6a8 -> 93da33804786 * **github.com/jxskiss/base62** 4f11678b909b -> v1.0.0 * **github.com/mdlayher/ethtool** 2b88debcdd43 -> 288d040e9d60 * **github.com/rivo/tview** ee97a7ab3975 -> badfa0f0b301 * **github.com/talos-systems/discovery-api** v0.1.0 **_new_** * **github.com/talos-systems/discovery-client** v0.1.0 **_new_** * **github.com/talos-systems/extras** v0.6.0 -> v0.7.0-alpha.0-1-g2bb2efc * **github.com/talos-systems/go-blockdevice** v0.2.4 -> 15b182db0cd2 * **github.com/talos-systems/go-smbios** v0.1.0 -> fd5ec8ce4873 * **github.com/talos-systems/net** v0.3.0 -> b4b718179a1a * **github.com/talos-systems/pkgs** v0.8.0 -> v0.9.0-alpha.0-21-g422276d * **github.com/talos-systems/siderolink** v0.1.0 **_new_** * **github.com/talos-systems/talos/pkg/machinery** v0.13.0 -> 1ffa8e048008 * **github.com/talos-systems/tools** v0.8.0 -> v0.9.0-alpha.0-5-g96e0231 * **github.com/vmware-tanzu/sonobuoy** v0.53.2 -> v0.55.1 * **github.com/vmware/govmomi** v0.26.1 -> v0.27.2 * **github.com/vmware/vmw-guestinfo** 687661b8bd8e -> cc1fd90d572c * **go.etcd.io/etcd/api/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0 -> v3.5.1 * **go.uber.org/atomic** v1.7.0 **_new_** * **golang.org/x/net** 3ad01bbaa167 -> d83791d6bcd9 * **golang.org/x/sys** 39ccf1dd6fa6 -> fe61309f8881 * **golang.org/x/term** 140adaaadfaf -> 03fcf44c2211 * **golang.org/x/time** 1f47c861a9ac -> f0f3c7e86c11 * **golang.zx2c4.com/wireguard/wgctrl** 0a2f4901cba6 -> dd7407c86d22 * **google.golang.org/grpc** v1.41.0 -> v1.42.0 * **inet.af/netaddr** 85fa6c94624e -> c74959edd3b6 * **k8s.io/api** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/apimachinery** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/client-go** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/cri-api** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/kubectl** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/kubelet** v0.22.2 -> v0.23.0-alpha.4 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.59 -> v1.2.61 * **sigs.k8s.io/yaml** v1.3.0 **_new_** Previous release can be found at [v0.13.0](https://github.com/talos-systems/talos/releases/tag/v0.13.0) ## [Talos 0.14.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.14.0-alpha.1) (2021-11-15) Welcome to the v0.14.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Kexec and capabilities When kexec support is disabled Talos no longer drops Linux capabilities (`CAP_SYS_BOOT` and `CAP_SYS_MODULES`) for child processes. That is helpful for advanced use-cases like Docker-in-Docker. If you want to permanently disable kexec and capabilities dropping, pass `kexec_load_disabled=1` argument to the kernel. For example: ```yaml install: extraKernelArgs: - sysctl.kernel.kexec_load_disabled=1 ``` Please note that capabilities are dropped before machine configuration is loaded, so disabling kexec via `machine.sysctls` will not be enough. ### Cluster Discovery Cluster Discovery is enabled by default for Talos 0.14. Cluster Discovery can be disabled with `talosctl gen config --with-cluster-discovery=false`. ### Kubelet Kubelet service can now be restarted with `talosctl service kubelet restart`. Kubelet node IP configuration (`.machine.kubelet.nodeIP.validSubnets`) can now include negative subnet matches (prefixed with `!`). ### Log Shipping Talos can now ship system logs to the configured destination using either JSON-over-UDP or JSON-over-TCP: see `.machine.logging` machine configuration option. ### `talosctl support` `talosctl` CLI tool now has a new subcommand called `support`, that can gather all cluster information that could help with future debugging in a single run. Output of the command is a `zip` archive with all talos service logs, kubernetes pod logs and manifests, talos resources manifests and so on. Generated archive does not contain any secret information so it is safe to send it for analysis to a third party. ### Component Updates * Linux: 5.15.1 * etcd: 3.5.1 * containerd: 1.5.7 * Kubernetes: 1.23.0-alpha.4 * CoreDNS: 1.8.6 Talos is built with Go 1.17.2 ### Kubernetes Upgrade Enhancements `talosctl upgrade-k8s` now syncs all Talos manifest resources generated from templates. So there is no need to update CoreDNS, Flannel container manually after running `upgrade-k8s` anymore. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Serge Logvinov * Noel Georgi * Spencer Smith * Nico Berlee * Alex Zero * Andrew Rynhard * Branden Cash * David Haines * Gerard de Leeuw * Michael Fornaro * Rui Lopes ### Changes
101 commits

* [`753a8218`](https://github.com/talos-systems/talos/commit/753a82188f227da4f2f40da5f4d46ebe45774455) refactor: move pkg/resources to machinery * [`0102a64a`](https://github.com/talos-systems/talos/commit/0102a64a5f6de2c3fe5d7792c2c5845fc737edff) refactor: remove pkg/resources dependencies on wgtypes, netx * [`7462733b`](https://github.com/talos-systems/talos/commit/7462733bcb075b923b8c7ba4a763308c641c49a2) chore: update golangci-lint * [`032c99a0`](https://github.com/talos-systems/talos/commit/032c99a0300ccb09105a07434884d2b1f57e537d) refactor: remove pkg/resources dependencies on k8s and base62 * [`4a5cff45`](https://github.com/talos-systems/talos/commit/4a5cff45f397ac29b7bfc390f11691c32d8615b2) perf: raspberry PIs clockspeed as fast as firmware allows * [`a76f6d69`](https://github.com/talos-systems/talos/commit/a76f6d69dbfdf34e4383dd5d2ee9f8cca4661e87) feat: allow kubelet to be restarted and provide negative nodeIP subnets * [`189221d5`](https://github.com/talos-systems/talos/commit/189221d589c1c9d4fc012dd9e31fd6d142d88dde) chore: update dependencies * [`41f0aecc`](https://github.com/talos-systems/talos/commit/41f0aecc1d3c4afce96d034f160fa9f120c67e85) docs: update partition info * [`95105071`](https://github.com/talos-systems/talos/commit/95105071de29f70552bd7c0881c2cc2e7c78c0ac) chore: fix simple issues found by golangci-lint * [`d4b0ca21`](https://github.com/talos-systems/talos/commit/d4b0ca21a1ee1183b28738bb3d9ca251e1968fe7) test: retry upgrade mutex lock failures * [`4357e9a8`](https://github.com/talos-systems/talos/commit/4357e9a849fcb7fb66378bdd767a926dde0c4318) docs: add Talos partions info * [`8e8687d7`](https://github.com/talos-systems/talos/commit/8e8687d7592d4bc071981478491d70489e7dd4a9) fix: use temporary sonobuoy version * [`e4e8e873`](https://github.com/talos-systems/talos/commit/e4e8e8737f564be47098e284706a63ef84636890) test: disable e2e-misc test with Canal CNI * [`897da2f6`](https://github.com/talos-systems/talos/commit/897da2f6efc571a66d14722a67bbc401bad31887) docs: common typos * [`a50483dd`](https://github.com/talos-systems/talos/commit/a50483dddfd9a742b998f509ee713af996a2484e) feat: update Linux to 5.15.1 * [`a2233bfe`](https://github.com/talos-systems/talos/commit/a2233bfe46bfb55d71cfc07174f6f22aee6d2651) fix: improve NTP sync process * [`7efc1238`](https://github.com/talos-systems/talos/commit/7efc1238ee285d55c4619b6a40190b54ff953a66) fix: parse partition size correctly * [`d6147eb1`](https://github.com/talos-systems/talos/commit/d6147eb17d2ebf263ca0537068bbbba6d3ced061) chore: update sonobuoy * [`efbae785`](https://github.com/talos-systems/talos/commit/efbae7857d09aa7e5e704d5989efced5aa655259) fix: use etc folder for du cli tests * [`198eea51`](https://github.com/talos-systems/talos/commit/198eea51a81bf041470c3c88cb6cb97af3a4e203) fix: wait for follow reader to start before writing to the file * [`e7f715eb`](https://github.com/talos-systems/talos/commit/e7f715eb0ca0587a05949910cafdeb486654b577) chore: log KubeSpan IPs overlaps * [`82a1ad16`](https://github.com/talos-systems/talos/commit/82a1ad1681bf262dcc68fc9cbac71ff2eb5639af) chore: bump dependencies * [`e8fccbf5`](https://github.com/talos-systems/talos/commit/e8fccbf5351ec2481813553181cb73b8f16c915a) fix: clear time adjustment error when setting time to specific value * [`e6f90bb4`](https://github.com/talos-systems/talos/commit/e6f90bb41a757b5173bbbf7554b6f85c08aaf58e) chore: remove unused parameters * [`785161d1`](https://github.com/talos-systems/talos/commit/785161d19f68fb64451cf3d887b67f85a8bcb952) feat: update k8s to 1.23.0-alpha.4 * [`fe228d7c`](https://github.com/talos-systems/talos/commit/fe228d7c85a1f8437398061b18c090962adc9f29) fix: do not use yaml.v2 in the support cmd * [`9b48ca21`](https://github.com/talos-systems/talos/commit/9b48ca21731cce53f0a61f05f74dcd264417d784) fix: endpoints and nodes in generated talosconfig * [`6e16fd2f`](https://github.com/talos-systems/talos/commit/6e16fd2feeb3f8bf0b99e6cbe21047b7a5c1f05c) chore: update tools, pkgs, and extras * [`261c497c`](https://github.com/talos-systems/talos/commit/261c497c71eb5ab5197bef05d8c209dbeb770d3f) feat: implement `talosctl support` command * [`fc7dc454`](https://github.com/talos-systems/talos/commit/fc7dc454840e100d82bb036a7f065293234593f7) chore: check our API idiosyncrasies * [`b1584429`](https://github.com/talos-systems/talos/commit/b15844298a6bfedca5acc0cc27061666481eb94b) feat: use GCP deployment manager * [`3e7d4df9`](https://github.com/talos-systems/talos/commit/3e7d4df99019e3cc6d9a90920d377c73a76ac577) chore: bump dependencies * [`88f24229`](https://github.com/talos-systems/talos/commit/88f2422955690d1eca1e21cd60a35e1d49141e3d) refactor: get rid of prometheus/procfs dependency in pkg/resources * [`dd196d30`](https://github.com/talos-systems/talos/commit/dd196d3006d29ae5cae5d43b648da1ca2e5af236) refactor: prepare for move of pkg/resources to machinery * [`f6110f80`](https://github.com/talos-systems/talos/commit/f6110f8036bc176188abb583bfa51296c4d3897d) fix: remove listening socket to fix Talos in a container restart * [`53bbb13e`](https://github.com/talos-systems/talos/commit/53bbb13ed8592978dc27578fa79b3a2018941427) docs: update docs with emmc boot guide * [`8329d211`](https://github.com/talos-systems/talos/commit/8329d21114abf841788be64765378343c12eaf69) chore: split polymorphic RootSecret resource into specific types * [`c97becdd`](https://github.com/talos-systems/talos/commit/c97becdd9548d85b2b894a05765f93dcdf9ad803) chore: remove interfaces and routes APIs * [`d798635d`](https://github.com/talos-systems/talos/commit/d798635d993a21392b8a7972a689c4be0728db32) feat: automatically limit kubelet node IP family based on service CIDRs * [`205a8d6d`](https://github.com/talos-systems/talos/commit/205a8d6dc495e25af87bf0b920d0f55b8a27bbfd) chore: make nethelpers build on all OSes * [`5b5dd49f`](https://github.com/talos-systems/talos/commit/5b5dd49f64bef584000655687e5b9c5d25af6a93) feat: extract JSON fields from more log messages * [`eb4f1182`](https://github.com/talos-systems/talos/commit/eb4f11822dc0b35541e0576a75ca263ca96d4981) docs: create cluster in hetzner cloud * [`728164e2`](https://github.com/talos-systems/talos/commit/728164e25a5705ae5194b416941f3607d592b140) docs: fix kexec_load_disabled param name in release notes * [`f6328f09`](https://github.com/talos-systems/talos/commit/f6328f09a2bf8d233a48354dd548fb740e509341) fix: fix filename typo * [`01b0f0ab`](https://github.com/talos-systems/talos/commit/01b0f0abb341b387f16d9b3a142af742f36c8c2b) release(v0.14.0-alpha.0): prepare release * [`8b620653`](https://github.com/talos-systems/talos/commit/8b6206537a30be049f74f8c4c7350028e6e56c74) fix: skip generating empty `.machine.logging` * [`60ad0063`](https://github.com/talos-systems/talos/commit/60ad006367e73f56fd69726e0044f1ce48f18a8b) fix: don't drop ability to use ambient capabilities * [`b6b78e7f`](https://github.com/talos-systems/talos/commit/b6b78e7fef3f6ef0c566e1815d1e28f16f868c93) test: add cluster discovery integration tests * [`97d64d16`](https://github.com/talos-systems/talos/commit/97d64d160ce7e71c3107adbd31404853f543f7cc) fix: hcloud network config changes * [`4c76865d`](https://github.com/talos-systems/talos/commit/4c76865d0ecec726e801a4b8f87e09476481d808) feat: multiple logging improvements * [`1d1e1df6`](https://github.com/talos-systems/talos/commit/1d1e1df643832478aaa715aea5f51ad2e61e2880) fix: handle skipped mounts correctly * [`0a964d92`](https://github.com/talos-systems/talos/commit/0a964d921922a247293e36b5fecaab466b91d924) test: fix openstack unit-test stability * [`72f62ac2`](https://github.com/talos-systems/talos/commit/72f62ac27b5d0a72db409fd003a7cf9c41a03d7c) chore: bump Go and Docker dependencies * [`9c48ebe8`](https://github.com/talos-systems/talos/commit/9c48ebe8f94afa85921ee5f1c1e9315201905a92) fix: gcp fetching externalIP * [`6c297268`](https://github.com/talos-systems/talos/commit/6c297268ce596c2a875b7c419c85317dc24d9f4f) test: fix e2e k8s version * [`ae5af9d3`](https://github.com/talos-systems/talos/commit/ae5af9d3fad399dea95c316d94e3e66b124bfb24) feat: update Kubernetes to 1.23.0-alpha.3 * [`28d3a69e`](https://github.com/talos-systems/talos/commit/28d3a69e9d4ae7ffa231804e26af6d1f39c07afd) feat: openstack config-drive support * [`2258bc49`](https://github.com/talos-systems/talos/commit/2258bc4918e89b3d6fcb841b2ad677f114ddba7e) test: update GCP e2e script to work with new templates * [`36b6ace2`](https://github.com/talos-systems/talos/commit/36b6ace25378e8c4a607de6efb6b89a2d52f5cea) feat: update Linux to 5.10.75 * [`38516a54`](https://github.com/talos-systems/talos/commit/38516a5499d933a8038ce6768946ff096e7c6f98) test: update Talos versions in upgrade tests * [`cff20ec7`](https://github.com/talos-systems/talos/commit/cff20ec78340b3855751e13f2ad0e54bd47e9989) fix: change services OOM score * [`666a2b62`](https://github.com/talos-systems/talos/commit/666a2b6207d257edda20c9e0411b0d4cd4112aa6) feat: azure platform ipv6 support * [`d32814e3`](https://github.com/talos-systems/talos/commit/d32814e302c370ec1e82aa2879186a034cd2a905) feat: extract JSON fields from log lines * [`e77d81ff`](https://github.com/talos-systems/talos/commit/e77d81fff31d68f762da3741846f95a6d2303903) fix: treat literal 'unknown' as a valid machine type * [`c8e404e3`](https://github.com/talos-systems/talos/commit/c8e404e356878f6cd819a33386b351c1c152c3f5) test: update vars for AWS cluster * [`ad23891b`](https://github.com/talos-systems/talos/commit/ad23891b1f6b33409721528c6771304b7ab94b2c) feat: update CoreDNS version 1.8.6 * [`41299cae`](https://github.com/talos-systems/talos/commit/41299cae9961665c2bf2a642290f8309683f040d) feat: udev rules support * [`5237fdc9`](https://github.com/talos-systems/talos/commit/5237fdc957efbb018649b866bfb756f280f589a2) feat: send JSON logs over UDP * [`6d44587a`](https://github.com/talos-systems/talos/commit/6d44587a4d4c16defa6bb06329cdfc6e39c95188) feat: coredns service dualstack * [`12f7888b`](https://github.com/talos-systems/talos/commit/12f7888b75fa2498e0f8305f5d6910cecad5c65c) feat: feed control plane endpoints on workers from cluster discovery * [`431e4fb4`](https://github.com/talos-systems/talos/commit/431e4fb4b690fa4955c407d8dd8156bdecd9a2c5) chore: bump Go and Docker dependencies * [`89f3b9f8`](https://github.com/talos-systems/talos/commit/89f3b9f8d41e33c4cb736917f418ab5cfb9edd83) feat: update etcd to 3.5.1 * [`e60469a3`](https://github.com/talos-systems/talos/commit/e60469a38cb81ace2039bae1927eb6c5f1f0ad1f) feat: initial support for JSON logging * [`68c420e3`](https://github.com/talos-systems/talos/commit/68c420e3c96a0fdc3b3e6cd75be24cc797c48e09) feat: enable cluster discovery by default * [`3e100aa9`](https://github.com/talos-systems/talos/commit/3e100aa97734ea809563e23fc36e19bdd3df1920) test: workaround EventsWatch test flakiness * [`9bd4838a`](https://github.com/talos-systems/talos/commit/9bd4838ac10abbd4760da4fb905d7639a1c26f9f) chore: stop using sonobuoy CLI * [`6ad45951`](https://github.com/talos-systems/talos/commit/6ad45951975aac48fdcc282e5a0e31344058d07e) docs: fix field names for bonding configuration * [`d7a3b7b5`](https://github.com/talos-systems/talos/commit/d7a3b7b5b70293884d2e19c6a59b14ebcfa24397) chore: use discovery-client and discovery-api modules * [`d6309eed`](https://github.com/talos-systems/talos/commit/d6309eed6618abd1b4efd0e3cd18a6c0df39378f) docs: create docs for Talos 0.14 * [`c0fda643`](https://github.com/talos-systems/talos/commit/c0fda6436ae27d8bbc210ee74a1128968108f6a6) fix: attempt to clean up tasks in containerd runner * [`8cf442da`](https://github.com/talos-systems/talos/commit/8cf442daa60d911caff59d1c2c05dd77652c8b51) chore: bump tools, pkgs, extras * [`0dad5f4d`](https://github.com/talos-systems/talos/commit/0dad5f4d7846f3fb41ff4ba27395023d33796a61) chore: small cleanup * [`e3e2113a`](https://github.com/talos-systems/talos/commit/e3e2113adc058940725b1041827d7adb8895c6cf) feat: upgrade CoreDNS during `upgrade-k8s` call * [`d92c98e1`](https://github.com/talos-systems/talos/commit/d92c98e19a054472bff3e0d646756f16c5e65bbf) docs: fix discovery service documentation link * [`e44b11c5`](https://github.com/talos-systems/talos/commit/e44b11c595e4cab796128a932843b90734ff6d1d) feat: update containerd to 1.5.7, bump Go dependencies * [`24129307`](https://github.com/talos-systems/talos/commit/24129307a14d6e59c6bc0d3586c0c95969bde679) docs: make Talos 0.13 docs latest, update documentation * [`31b6e39e`](https://github.com/talos-systems/talos/commit/31b6e39e58a27e1f2c1be500fca8636971bfa5c6) fix: delete expired affiliates from the discovery service * [`877a2b6f`](https://github.com/talos-systems/talos/commit/877a2b6fc00eaa7574349f9086d78c04df163840) test: bump CAPI components to v1alpha4 * [`2ba0e0ac`](https://github.com/talos-systems/talos/commit/2ba0e0ac4ad460409101f5f2374e66698adbba4c) docs: add KubeSpan documentation * [`997873b6`](https://github.com/talos-systems/talos/commit/997873b6d3116b59ebb46df66b8aa1cee06df92f) fix: use ECDSA-SHA512 when generating certs for Talos < 0.13 * [`7137166d`](https://github.com/talos-systems/talos/commit/7137166d1d5817e2d44ead4a01796275f92a9d4a) fix: allow overriding `audit-policy-file` in `kube-apiserver` static pod * [`8fcd4219`](https://github.com/talos-systems/talos/commit/8fcd4219671a9359880ba344a2ec7fd65dfe5e2a) chore: fix integration-qemu-race * [`91a858b5`](https://github.com/talos-systems/talos/commit/91a858b53704ede86392fe3c155ce9ab3c2d406f) fix: sort output of the argument builder * [`657f7a56`](https://github.com/talos-systems/talos/commit/657f7a56b10089e0dc551e178bc85b28d8003243) fix: use ECDSA-SHA256 signature algorithm for Kubernetes certs * [`983d2459`](https://github.com/talos-systems/talos/commit/983d2459e2aa036774828f773bbaba5697665ae7) feat: suppress logging NTP sync to the console * [`022c7335`](https://github.com/talos-systems/talos/commit/022c7335f3063675ab744454a2ad4b2c0c19bfbc) fix: add interface route if DHCP4 router is not directly routeable * [`66a1579e`](https://github.com/talos-systems/talos/commit/66a1579ea7d2a9c4fdf15b762cd024c54b3e8ffb) fix: don't enable 'no new privs' on the system level * [`423861cf`](https://github.com/talos-systems/talos/commit/423861cf9f99eaf034a4f0cb243d73d1275c3f38) feat: don't drop capabilities if kexec is disabled * [`facc8c38`](https://github.com/talos-systems/talos/commit/facc8c38a021610da900a45f397aea8ddfc74f1c) docs: fix documentation for cluster discovery * [`ce65ca4e`](https://github.com/talos-systems/talos/commit/ce65ca4e4a2994f901f01ce5ca269d6df86f0de8) chore: build using only amd64 builders * [`e9b0f010`](https://github.com/talos-systems/talos/commit/e9b0f010d2855b968a5d8b8b5fbcd268e06ba302) chore: update docker image in the pipeline

### Changes since v0.14.0-alpha.0
44 commits

* [`753a8218`](https://github.com/talos-systems/talos/commit/753a82188f227da4f2f40da5f4d46ebe45774455) refactor: move pkg/resources to machinery * [`0102a64a`](https://github.com/talos-systems/talos/commit/0102a64a5f6de2c3fe5d7792c2c5845fc737edff) refactor: remove pkg/resources dependencies on wgtypes, netx * [`7462733b`](https://github.com/talos-systems/talos/commit/7462733bcb075b923b8c7ba4a763308c641c49a2) chore: update golangci-lint * [`032c99a0`](https://github.com/talos-systems/talos/commit/032c99a0300ccb09105a07434884d2b1f57e537d) refactor: remove pkg/resources dependencies on k8s and base62 * [`4a5cff45`](https://github.com/talos-systems/talos/commit/4a5cff45f397ac29b7bfc390f11691c32d8615b2) perf: raspberry PIs clockspeed as fast as firmware allows * [`a76f6d69`](https://github.com/talos-systems/talos/commit/a76f6d69dbfdf34e4383dd5d2ee9f8cca4661e87) feat: allow kubelet to be restarted and provide negative nodeIP subnets * [`189221d5`](https://github.com/talos-systems/talos/commit/189221d589c1c9d4fc012dd9e31fd6d142d88dde) chore: update dependencies * [`41f0aecc`](https://github.com/talos-systems/talos/commit/41f0aecc1d3c4afce96d034f160fa9f120c67e85) docs: update partition info * [`95105071`](https://github.com/talos-systems/talos/commit/95105071de29f70552bd7c0881c2cc2e7c78c0ac) chore: fix simple issues found by golangci-lint * [`d4b0ca21`](https://github.com/talos-systems/talos/commit/d4b0ca21a1ee1183b28738bb3d9ca251e1968fe7) test: retry upgrade mutex lock failures * [`4357e9a8`](https://github.com/talos-systems/talos/commit/4357e9a849fcb7fb66378bdd767a926dde0c4318) docs: add Talos partions info * [`8e8687d7`](https://github.com/talos-systems/talos/commit/8e8687d7592d4bc071981478491d70489e7dd4a9) fix: use temporary sonobuoy version * [`e4e8e873`](https://github.com/talos-systems/talos/commit/e4e8e8737f564be47098e284706a63ef84636890) test: disable e2e-misc test with Canal CNI * [`897da2f6`](https://github.com/talos-systems/talos/commit/897da2f6efc571a66d14722a67bbc401bad31887) docs: common typos * [`a50483dd`](https://github.com/talos-systems/talos/commit/a50483dddfd9a742b998f509ee713af996a2484e) feat: update Linux to 5.15.1 * [`a2233bfe`](https://github.com/talos-systems/talos/commit/a2233bfe46bfb55d71cfc07174f6f22aee6d2651) fix: improve NTP sync process * [`7efc1238`](https://github.com/talos-systems/talos/commit/7efc1238ee285d55c4619b6a40190b54ff953a66) fix: parse partition size correctly * [`d6147eb1`](https://github.com/talos-systems/talos/commit/d6147eb17d2ebf263ca0537068bbbba6d3ced061) chore: update sonobuoy * [`efbae785`](https://github.com/talos-systems/talos/commit/efbae7857d09aa7e5e704d5989efced5aa655259) fix: use etc folder for du cli tests * [`198eea51`](https://github.com/talos-systems/talos/commit/198eea51a81bf041470c3c88cb6cb97af3a4e203) fix: wait for follow reader to start before writing to the file * [`e7f715eb`](https://github.com/talos-systems/talos/commit/e7f715eb0ca0587a05949910cafdeb486654b577) chore: log KubeSpan IPs overlaps * [`82a1ad16`](https://github.com/talos-systems/talos/commit/82a1ad1681bf262dcc68fc9cbac71ff2eb5639af) chore: bump dependencies * [`e8fccbf5`](https://github.com/talos-systems/talos/commit/e8fccbf5351ec2481813553181cb73b8f16c915a) fix: clear time adjustment error when setting time to specific value * [`e6f90bb4`](https://github.com/talos-systems/talos/commit/e6f90bb41a757b5173bbbf7554b6f85c08aaf58e) chore: remove unused parameters * [`785161d1`](https://github.com/talos-systems/talos/commit/785161d19f68fb64451cf3d887b67f85a8bcb952) feat: update k8s to 1.23.0-alpha.4 * [`fe228d7c`](https://github.com/talos-systems/talos/commit/fe228d7c85a1f8437398061b18c090962adc9f29) fix: do not use yaml.v2 in the support cmd * [`9b48ca21`](https://github.com/talos-systems/talos/commit/9b48ca21731cce53f0a61f05f74dcd264417d784) fix: endpoints and nodes in generated talosconfig * [`6e16fd2f`](https://github.com/talos-systems/talos/commit/6e16fd2feeb3f8bf0b99e6cbe21047b7a5c1f05c) chore: update tools, pkgs, and extras * [`261c497c`](https://github.com/talos-systems/talos/commit/261c497c71eb5ab5197bef05d8c209dbeb770d3f) feat: implement `talosctl support` command * [`fc7dc454`](https://github.com/talos-systems/talos/commit/fc7dc454840e100d82bb036a7f065293234593f7) chore: check our API idiosyncrasies * [`b1584429`](https://github.com/talos-systems/talos/commit/b15844298a6bfedca5acc0cc27061666481eb94b) feat: use GCP deployment manager * [`3e7d4df9`](https://github.com/talos-systems/talos/commit/3e7d4df99019e3cc6d9a90920d377c73a76ac577) chore: bump dependencies * [`88f24229`](https://github.com/talos-systems/talos/commit/88f2422955690d1eca1e21cd60a35e1d49141e3d) refactor: get rid of prometheus/procfs dependency in pkg/resources * [`dd196d30`](https://github.com/talos-systems/talos/commit/dd196d3006d29ae5cae5d43b648da1ca2e5af236) refactor: prepare for move of pkg/resources to machinery * [`f6110f80`](https://github.com/talos-systems/talos/commit/f6110f8036bc176188abb583bfa51296c4d3897d) fix: remove listening socket to fix Talos in a container restart * [`53bbb13e`](https://github.com/talos-systems/talos/commit/53bbb13ed8592978dc27578fa79b3a2018941427) docs: update docs with emmc boot guide * [`8329d211`](https://github.com/talos-systems/talos/commit/8329d21114abf841788be64765378343c12eaf69) chore: split polymorphic RootSecret resource into specific types * [`c97becdd`](https://github.com/talos-systems/talos/commit/c97becdd9548d85b2b894a05765f93dcdf9ad803) chore: remove interfaces and routes APIs * [`d798635d`](https://github.com/talos-systems/talos/commit/d798635d993a21392b8a7972a689c4be0728db32) feat: automatically limit kubelet node IP family based on service CIDRs * [`205a8d6d`](https://github.com/talos-systems/talos/commit/205a8d6dc495e25af87bf0b920d0f55b8a27bbfd) chore: make nethelpers build on all OSes * [`5b5dd49f`](https://github.com/talos-systems/talos/commit/5b5dd49f64bef584000655687e5b9c5d25af6a93) feat: extract JSON fields from more log messages * [`eb4f1182`](https://github.com/talos-systems/talos/commit/eb4f11822dc0b35541e0576a75ca263ca96d4981) docs: create cluster in hetzner cloud * [`728164e2`](https://github.com/talos-systems/talos/commit/728164e25a5705ae5194b416941f3607d592b140) docs: fix kexec_load_disabled param name in release notes * [`f6328f09`](https://github.com/talos-systems/talos/commit/f6328f09a2bf8d233a48354dd548fb740e509341) fix: fix filename typo

### Changes from talos-systems/discovery-api
2 commits

* [`db279ef`](https://github.com/talos-systems/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/talos-systems/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from talos-systems/discovery-client
2 commits

* [`a9a5e9b`](https://github.com/talos-systems/discovery-client/commit/a9a5e9bfddaa670e0fb4f57510167d377cf09b07) feat: initial client code * [`98eb999`](https://github.com/talos-systems/discovery-client/commit/98eb9999c0c76d2f93378108b7e22de6bcae6e81) chore: initial commit

### Changes from talos-systems/extras
2 commits

* [`2bb2efc`](https://github.com/talos-systems/extras/commit/2bb2efcbe68bcce2172b9ac7771dde1d0d2b6d3c) chore: update pkgs and tools * [`d6e8b3a`](https://github.com/talos-systems/extras/commit/d6e8b3a78e9a3371472753286c559627932466c3) chore: update pkgs and tools

### Changes from talos-systems/net
1 commit

* [`0abe5bd`](https://github.com/talos-systems/net/commit/0abe5bdae8f85e4e976bc4d90e95dcb4be8fb853) feat: implement FilterIPs function

### Changes from talos-systems/pkgs
15 commits

* [`740da24`](https://github.com/talos-systems/pkgs/commit/740da24d801cc5a8f47f6badf788faea872a6e72) feat: bump raspberrypi-firmware to 1.20211029 * [`832dae4`](https://github.com/talos-systems/pkgs/commit/832dae4175d7cd3336fb3637134833e7b9fe1719) fix: enable CONFIG_DM_SNAPSHOT * [`f307e64`](https://github.com/talos-systems/pkgs/commit/f307e64e6b2e8a29cff5508ae2da4ae92286771e) feat: update Linux to 5.15.1 * [`4f0f238`](https://github.com/talos-systems/pkgs/commit/4f0f238decfb93561c5871207da8dd3b1d350961) chore: update tools * [`932c3cf`](https://github.com/talos-systems/pkgs/commit/932c3cfff9fcb9ffb671d7e5e10b0ca7c290c058) feat: update libseccomp to 2.5.3 * [`7f3311e`](https://github.com/talos-systems/pkgs/commit/7f3311e2a3d2ef759d9f909f9872e5b98f9682a5) feat: update cpu governor to schedutil * [`b4cdb99`](https://github.com/talos-systems/pkgs/commit/b4cdb991a4aa60b1fa859b44efcd57705d89e9ee) fix: update containerd shas * [`80a63d4`](https://github.com/talos-systems/pkgs/commit/80a63d4cf2231383266f244f608a958b94872a99) feat: update Linux to 5.10.75 * [`5c98efd`](https://github.com/talos-systems/pkgs/commit/5c98efd95d2e2e036d845c63b6268583d853d3fd) feat: add QLogic QED 25/40/100Gb Ethernet NIC driver * [`bfb2365`](https://github.com/talos-systems/pkgs/commit/bfb2365b04aa7f92ef87799c47ffde6bc2395785) feat: enable driver for SuperMicro raid controller * [`657e16b`](https://github.com/talos-systems/pkgs/commit/657e16b3976ba376401797277e85dd67c9b7e64e) feat: enable Intel VMD driver * [`f7d9d72`](https://github.com/talos-systems/pkgs/commit/f7d9d728d468b9e3af2552595c9fb145f9008ef3) feat: enable smarpqi driver and related options * [`bca3be0`](https://github.com/talos-systems/pkgs/commit/bca3be04e22367585a60afa421e78707d2c6a1de) feat: enable aqtion device driver * [`b88127a`](https://github.com/talos-systems/pkgs/commit/b88127afec39d3039e93dfd6bc20a62415d396f0) chore: update tools * [`971735f`](https://github.com/talos-systems/pkgs/commit/971735f4b1914cb1c8f2575aeda9b354ecf842f6) feat: update containerd to 1.5.7

### Changes from talos-systems/tools
6 commits

* [`96e0231`](https://github.com/talos-systems/tools/commit/96e0231c0f01a9fe6120a941b21c40e1a37bab36) feat: update squashfs-tools to 4.5 * [`2c9c826`](https://github.com/talos-systems/tools/commit/2c9c826201dc9f4d869fc00ffac63ee10f5e1101) feat: update libseccomp to 2.5.3 * [`f713a7c`](https://github.com/talos-systems/tools/commit/f713a7cd96fb1176687f5a6c4ec7d1345bb5e568) feat: update protobuf to 3.19.1, grpc-go to 1.42.0 * [`972c5ef`](https://github.com/talos-systems/tools/commit/972c5ef413f721ab5ad835dab526850620d05003) feat: update Go to 1.17.3 * [`f63848c`](https://github.com/talos-systems/tools/commit/f63848c1a756807879f22c752155fe1f36ccec32) feat: update PCRE version and source host * [`fab7532`](https://github.com/talos-systems/tools/commit/fab7532fd59519d62a3985684a250273a14f1893) feat: update Go to 1.17.2

### Dependency Changes * **github.com/AlekSi/pointer** v1.1.0 -> v1.2.0 * **github.com/containerd/cgroups** v1.0.1 -> v1.0.2 * **github.com/containerd/containerd** v1.5.5 -> v1.5.7 * **github.com/docker/docker** v20.10.8 -> v20.10.10 * **github.com/evanphx/json-patch** v4.11.0 -> v4.12.0 * **github.com/gosuri/uiprogress** v0.0.1 **_new_** * **github.com/hashicorp/go-getter** v1.5.8 -> v1.5.9 * **github.com/hetznercloud/hcloud-go** v1.32.0 -> v1.33.1 * **github.com/insomniacslk/dhcp** b95caade3eac -> ad197bcd36fd * **github.com/jsimonetti/rtnetlink** 435639c8e6a8 -> 93da33804786 * **github.com/jxskiss/base62** 4f11678b909b -> v1.0.0 * **github.com/mdlayher/ethtool** 2b88debcdd43 -> 288d040e9d60 * **github.com/rivo/tview** ee97a7ab3975 -> badfa0f0b301 * **github.com/talos-systems/discovery-api** v0.1.0 **_new_** * **github.com/talos-systems/discovery-client** v0.1.0 **_new_** * **github.com/talos-systems/extras** v0.6.0 -> v0.7.0-alpha.0-1-g2bb2efc * **github.com/talos-systems/net** v0.3.0 -> 0abe5bdae8f8 * **github.com/talos-systems/pkgs** v0.8.0 -> v0.9.0-alpha.0-14-g740da24 * **github.com/talos-systems/talos/pkg/machinery** v0.13.0 -> 000000000000 * **github.com/talos-systems/tools** v0.8.0 -> v0.9.0-alpha.0-5-g96e0231 * **github.com/vmware-tanzu/sonobuoy** v0.53.2 -> v0.55.0 * **github.com/vmware/govmomi** v0.26.1 -> v0.27.1 * **github.com/vmware/vmw-guestinfo** 687661b8bd8e -> cc1fd90d572c * **go.etcd.io/etcd/api/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0 -> v3.5.1 * **golang.org/x/net** 3ad01bbaa167 -> 69e39bad7dc2 * **golang.org/x/sys** 39ccf1dd6fa6 -> 0c823b97ae02 * **golang.org/x/term** 140adaaadfaf -> 03fcf44c2211 * **golang.zx2c4.com/wireguard/wgctrl** 0a2f4901cba6 -> 0073765f69ba * **google.golang.org/grpc** v1.41.0 -> v1.42.0 * **inet.af/netaddr** 85fa6c94624e -> c74959edd3b6 * **k8s.io/api** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/apimachinery** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/client-go** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/cri-api** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/kubectl** v0.22.2 -> v0.23.0-alpha.4 * **k8s.io/kubelet** v0.22.2 -> v0.23.0-alpha.4 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.59 -> v1.2.60 * **sigs.k8s.io/yaml** v1.3.0 **_new_** Previous release can be found at [v0.13.0](https://github.com/talos-systems/talos/releases/tag/v0.13.0) ## [Talos 0.14.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.14.0-alpha.0) (2021-10-25) Welcome to the v0.14.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Kexec and capabilities When kexec support is disabled Talos no longer drops Linux capabilities (`CAP_SYS_BOOT` and `CAP_SYS_MODULES`) for child processes. That is helpful for advanced use-cases like Docker-in-Docker. If you want to permanently disable kexec and capabilities dropping, pass `kexec_load_disabled=1` argument to the kernel. For example: ```yaml install: extraKernelArgs: - kexec_load_disabled=1 ``` Please note that capabilities are dropped before machine configuration is loaded, so disabling kexec via `machine.sysctls` will not be enough. ### Cluster Discovery Cluster Discovery is enabled by default for Talos 0.14. Cluster Discovery can be disabled with `talosctl gen config --with-cluster-discovery=false`. ### Log Shipping Talos can now ship system logs to the configured destination using either JSON-over-UDP or JSON-over-TCP: see `.machine.logging` machine configuration option. ### Component Updates * Linux: 5.10.75 * etcd: 3.5.1 * containerd: 1.5.7 * Kubernetes: 1.23.0-alpha.0 * CoreDNS: 1.8.6 Talos is built with Go 1.17.2 ### Kubernetes Upgrade Enhancements `talosctl upgrade-k8s` now syncs all Talos manifest resources generated from templates. So there is no need to update CoreDNS, Flannel container manually after running `upgrade-k8s` anymore. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Serge Logvinov * Artem Chernyshev * Spencer Smith * Andrew Rynhard * Branden Cash * Gerard de Leeuw ### Changes
56 commits

* [`8b620653`](https://github.com/talos-systems/talos/commit/8b6206537a30be049f74f8c4c7350028e6e56c74) fix: skip generating empty `.machine.logging` * [`60ad0063`](https://github.com/talos-systems/talos/commit/60ad006367e73f56fd69726e0044f1ce48f18a8b) fix: don't drop ability to use ambient capabilities * [`b6b78e7f`](https://github.com/talos-systems/talos/commit/b6b78e7fef3f6ef0c566e1815d1e28f16f868c93) test: add cluster discovery integration tests * [`97d64d16`](https://github.com/talos-systems/talos/commit/97d64d160ce7e71c3107adbd31404853f543f7cc) fix: hcloud network config changes * [`4c76865d`](https://github.com/talos-systems/talos/commit/4c76865d0ecec726e801a4b8f87e09476481d808) feat: multiple logging improvements * [`1d1e1df6`](https://github.com/talos-systems/talos/commit/1d1e1df643832478aaa715aea5f51ad2e61e2880) fix: handle skipped mounts correctly * [`0a964d92`](https://github.com/talos-systems/talos/commit/0a964d921922a247293e36b5fecaab466b91d924) test: fix openstack unit-test stability * [`72f62ac2`](https://github.com/talos-systems/talos/commit/72f62ac27b5d0a72db409fd003a7cf9c41a03d7c) chore: bump Go and Docker dependencies * [`9c48ebe8`](https://github.com/talos-systems/talos/commit/9c48ebe8f94afa85921ee5f1c1e9315201905a92) fix: gcp fetching externalIP * [`6c297268`](https://github.com/talos-systems/talos/commit/6c297268ce596c2a875b7c419c85317dc24d9f4f) test: fix e2e k8s version * [`ae5af9d3`](https://github.com/talos-systems/talos/commit/ae5af9d3fad399dea95c316d94e3e66b124bfb24) feat: update Kubernetes to 1.23.0-alpha.3 * [`28d3a69e`](https://github.com/talos-systems/talos/commit/28d3a69e9d4ae7ffa231804e26af6d1f39c07afd) feat: openstack config-drive support * [`2258bc49`](https://github.com/talos-systems/talos/commit/2258bc4918e89b3d6fcb841b2ad677f114ddba7e) test: update GCP e2e script to work with new templates * [`36b6ace2`](https://github.com/talos-systems/talos/commit/36b6ace25378e8c4a607de6efb6b89a2d52f5cea) feat: update Linux to 5.10.75 * [`38516a54`](https://github.com/talos-systems/talos/commit/38516a5499d933a8038ce6768946ff096e7c6f98) test: update Talos versions in upgrade tests * [`cff20ec7`](https://github.com/talos-systems/talos/commit/cff20ec78340b3855751e13f2ad0e54bd47e9989) fix: change services OOM score * [`666a2b62`](https://github.com/talos-systems/talos/commit/666a2b6207d257edda20c9e0411b0d4cd4112aa6) feat: azure platform ipv6 support * [`d32814e3`](https://github.com/talos-systems/talos/commit/d32814e302c370ec1e82aa2879186a034cd2a905) feat: extract JSON fields from log lines * [`e77d81ff`](https://github.com/talos-systems/talos/commit/e77d81fff31d68f762da3741846f95a6d2303903) fix: treat literal 'unknown' as a valid machine type * [`c8e404e3`](https://github.com/talos-systems/talos/commit/c8e404e356878f6cd819a33386b351c1c152c3f5) test: update vars for AWS cluster * [`ad23891b`](https://github.com/talos-systems/talos/commit/ad23891b1f6b33409721528c6771304b7ab94b2c) feat: update CoreDNS version 1.8.6 * [`41299cae`](https://github.com/talos-systems/talos/commit/41299cae9961665c2bf2a642290f8309683f040d) feat: udev rules support * [`5237fdc9`](https://github.com/talos-systems/talos/commit/5237fdc957efbb018649b866bfb756f280f589a2) feat: send JSON logs over UDP * [`6d44587a`](https://github.com/talos-systems/talos/commit/6d44587a4d4c16defa6bb06329cdfc6e39c95188) feat: coredns service dualstack * [`12f7888b`](https://github.com/talos-systems/talos/commit/12f7888b75fa2498e0f8305f5d6910cecad5c65c) feat: feed control plane endpoints on workers from cluster discovery * [`431e4fb4`](https://github.com/talos-systems/talos/commit/431e4fb4b690fa4955c407d8dd8156bdecd9a2c5) chore: bump Go and Docker dependencies * [`89f3b9f8`](https://github.com/talos-systems/talos/commit/89f3b9f8d41e33c4cb736917f418ab5cfb9edd83) feat: update etcd to 3.5.1 * [`e60469a3`](https://github.com/talos-systems/talos/commit/e60469a38cb81ace2039bae1927eb6c5f1f0ad1f) feat: initial support for JSON logging * [`68c420e3`](https://github.com/talos-systems/talos/commit/68c420e3c96a0fdc3b3e6cd75be24cc797c48e09) feat: enable cluster discovery by default * [`3e100aa9`](https://github.com/talos-systems/talos/commit/3e100aa97734ea809563e23fc36e19bdd3df1920) test: workaround EventsWatch test flakiness * [`9bd4838a`](https://github.com/talos-systems/talos/commit/9bd4838ac10abbd4760da4fb905d7639a1c26f9f) chore: stop using sonobuoy CLI * [`6ad45951`](https://github.com/talos-systems/talos/commit/6ad45951975aac48fdcc282e5a0e31344058d07e) docs: fix field names for bonding configuration * [`d7a3b7b5`](https://github.com/talos-systems/talos/commit/d7a3b7b5b70293884d2e19c6a59b14ebcfa24397) chore: use discovery-client and discovery-api modules * [`d6309eed`](https://github.com/talos-systems/talos/commit/d6309eed6618abd1b4efd0e3cd18a6c0df39378f) docs: create docs for Talos 0.14 * [`c0fda643`](https://github.com/talos-systems/talos/commit/c0fda6436ae27d8bbc210ee74a1128968108f6a6) fix: attempt to clean up tasks in containerd runner * [`8cf442da`](https://github.com/talos-systems/talos/commit/8cf442daa60d911caff59d1c2c05dd77652c8b51) chore: bump tools, pkgs, extras * [`0dad5f4d`](https://github.com/talos-systems/talos/commit/0dad5f4d7846f3fb41ff4ba27395023d33796a61) chore: small cleanup * [`e3e2113a`](https://github.com/talos-systems/talos/commit/e3e2113adc058940725b1041827d7adb8895c6cf) feat: upgrade CoreDNS during `upgrade-k8s` call * [`d92c98e1`](https://github.com/talos-systems/talos/commit/d92c98e19a054472bff3e0d646756f16c5e65bbf) docs: fix discovery service documentation link * [`e44b11c5`](https://github.com/talos-systems/talos/commit/e44b11c595e4cab796128a932843b90734ff6d1d) feat: update containerd to 1.5.7, bump Go dependencies * [`24129307`](https://github.com/talos-systems/talos/commit/24129307a14d6e59c6bc0d3586c0c95969bde679) docs: make Talos 0.13 docs latest, update documentation * [`31b6e39e`](https://github.com/talos-systems/talos/commit/31b6e39e58a27e1f2c1be500fca8636971bfa5c6) fix: delete expired affiliates from the discovery service * [`877a2b6f`](https://github.com/talos-systems/talos/commit/877a2b6fc00eaa7574349f9086d78c04df163840) test: bump CAPI components to v1alpha4 * [`2ba0e0ac`](https://github.com/talos-systems/talos/commit/2ba0e0ac4ad460409101f5f2374e66698adbba4c) docs: add KubeSpan documentation * [`997873b6`](https://github.com/talos-systems/talos/commit/997873b6d3116b59ebb46df66b8aa1cee06df92f) fix: use ECDSA-SHA512 when generating certs for Talos < 0.13 * [`7137166d`](https://github.com/talos-systems/talos/commit/7137166d1d5817e2d44ead4a01796275f92a9d4a) fix: allow overriding `audit-policy-file` in `kube-apiserver` static pod * [`8fcd4219`](https://github.com/talos-systems/talos/commit/8fcd4219671a9359880ba344a2ec7fd65dfe5e2a) chore: fix integration-qemu-race * [`91a858b5`](https://github.com/talos-systems/talos/commit/91a858b53704ede86392fe3c155ce9ab3c2d406f) fix: sort output of the argument builder * [`657f7a56`](https://github.com/talos-systems/talos/commit/657f7a56b10089e0dc551e178bc85b28d8003243) fix: use ECDSA-SHA256 signature algorithm for Kubernetes certs * [`983d2459`](https://github.com/talos-systems/talos/commit/983d2459e2aa036774828f773bbaba5697665ae7) feat: suppress logging NTP sync to the console * [`022c7335`](https://github.com/talos-systems/talos/commit/022c7335f3063675ab744454a2ad4b2c0c19bfbc) fix: add interface route if DHCP4 router is not directly routeable * [`66a1579e`](https://github.com/talos-systems/talos/commit/66a1579ea7d2a9c4fdf15b762cd024c54b3e8ffb) fix: don't enable 'no new privs' on the system level * [`423861cf`](https://github.com/talos-systems/talos/commit/423861cf9f99eaf034a4f0cb243d73d1275c3f38) feat: don't drop capabilities if kexec is disabled * [`facc8c38`](https://github.com/talos-systems/talos/commit/facc8c38a021610da900a45f397aea8ddfc74f1c) docs: fix documentation for cluster discovery * [`ce65ca4e`](https://github.com/talos-systems/talos/commit/ce65ca4e4a2994f901f01ce5ca269d6df86f0de8) chore: build using only amd64 builders * [`e9b0f010`](https://github.com/talos-systems/talos/commit/e9b0f010d2855b968a5d8b8b5fbcd268e06ba302) chore: update docker image in the pipeline

### Changes from talos-systems/discovery-api
2 commits

* [`db279ef`](https://github.com/talos-systems/discovery-api/commit/db279ef42a1fad2e1feb4902150b4969f7082c81) feat: initial set of APIs and generated files * [`ac52a37`](https://github.com/talos-systems/discovery-api/commit/ac52a378211475ebd281dcbb00954eec42459778) chore: initial commit

### Changes from talos-systems/discovery-client
2 commits

* [`a9a5e9b`](https://github.com/talos-systems/discovery-client/commit/a9a5e9bfddaa670e0fb4f57510167d377cf09b07) feat: initial client code * [`98eb999`](https://github.com/talos-systems/discovery-client/commit/98eb9999c0c76d2f93378108b7e22de6bcae6e81) chore: initial commit

### Changes from talos-systems/extras
1 commit

* [`d6e8b3a`](https://github.com/talos-systems/extras/commit/d6e8b3a78e9a3371472753286c559627932466c3) chore: update pkgs and tools

### Changes from talos-systems/pkgs
8 commits

* [`80a63d4`](https://github.com/talos-systems/pkgs/commit/80a63d4cf2231383266f244f608a958b94872a99) feat: update Linux to 5.10.75 * [`5c98efd`](https://github.com/talos-systems/pkgs/commit/5c98efd95d2e2e036d845c63b6268583d853d3fd) feat: add QLogic QED 25/40/100Gb Ethernet NIC driver * [`bfb2365`](https://github.com/talos-systems/pkgs/commit/bfb2365b04aa7f92ef87799c47ffde6bc2395785) feat: enable driver for SuperMicro raid controller * [`657e16b`](https://github.com/talos-systems/pkgs/commit/657e16b3976ba376401797277e85dd67c9b7e64e) feat: enable Intel VMD driver * [`f7d9d72`](https://github.com/talos-systems/pkgs/commit/f7d9d728d468b9e3af2552595c9fb145f9008ef3) feat: enable smarpqi driver and related options * [`bca3be0`](https://github.com/talos-systems/pkgs/commit/bca3be04e22367585a60afa421e78707d2c6a1de) feat: enable aqtion device driver * [`b88127a`](https://github.com/talos-systems/pkgs/commit/b88127afec39d3039e93dfd6bc20a62415d396f0) chore: update tools * [`971735f`](https://github.com/talos-systems/pkgs/commit/971735f4b1914cb1c8f2575aeda9b354ecf842f6) feat: update containerd to 1.5.7

### Changes from talos-systems/tools
1 commit

* [`fab7532`](https://github.com/talos-systems/tools/commit/fab7532fd59519d62a3985684a250273a14f1893) feat: update Go to 1.17.2

### Dependency Changes * **github.com/AlekSi/pointer** v1.1.0 -> v1.2.0 * **github.com/containerd/cgroups** v1.0.1 -> v1.0.2 * **github.com/containerd/containerd** v1.5.5 -> v1.5.7 * **github.com/docker/docker** v20.10.8 -> v20.10.9 * **github.com/hashicorp/go-getter** v1.5.8 -> v1.5.9 * **github.com/insomniacslk/dhcp** b95caade3eac -> 509557e9f781 * **github.com/jsimonetti/rtnetlink** 435639c8e6a8 -> e34540a94caa * **github.com/jxskiss/base62** 4f11678b909b -> v1.0.0 * **github.com/rivo/tview** ee97a7ab3975 -> 5508f4b00266 * **github.com/talos-systems/discovery-api** v0.1.0 **_new_** * **github.com/talos-systems/discovery-client** v0.1.0 **_new_** * **github.com/talos-systems/extras** v0.6.0 -> v0.7.0-alpha.0 * **github.com/talos-systems/pkgs** v0.8.0 -> v0.9.0-alpha.0-7-g80a63d4 * **github.com/talos-systems/talos/pkg/machinery** v0.13.0 -> 000000000000 * **github.com/talos-systems/tools** v0.8.0 -> v0.9.0-alpha.0 * **github.com/vmware-tanzu/sonobuoy** v0.53.2 -> v0.54.0 * **github.com/vmware/govmomi** v0.26.1 -> v0.27.1 * **github.com/vmware/vmw-guestinfo** 687661b8bd8e -> cc1fd90d572c * **go.etcd.io/etcd/api/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/client/v3** v3.5.0 -> v3.5.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0 -> v3.5.1 * **golang.org/x/net** 3ad01bbaa167 -> d418f374d309 * **golang.org/x/sys** 39ccf1dd6fa6 -> d6a326fbbf70 * **golang.org/x/term** 140adaaadfaf -> 03fcf44c2211 * **golang.zx2c4.com/wireguard/wgctrl** 0a2f4901cba6 -> 5be1d6054c42 * **k8s.io/api** v0.22.2 -> v0.23.0-alpha.3 * **k8s.io/apimachinery** v0.22.2 -> v0.23.0-alpha.3 * **k8s.io/client-go** v0.22.2 -> v0.23.0-alpha.3 * **k8s.io/cri-api** v0.22.2 -> v0.23.0-alpha.3 * **k8s.io/kubectl** v0.22.2 -> v0.23.0-alpha.3 * **k8s.io/kubelet** v0.22.2 -> v0.23.0-alpha.3 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.59 -> v1.2.60 * **sigs.k8s.io/yaml** v1.3.0 **_new_** Previous release can be found at [v0.13.0](https://github.com/talos-systems/talos/releases/tag/v0.13.0) ## [Talos 0.13.0-alpha.3](https://github.com/talos-systems/talos/releases/tag/v0.13.0-alpha.3) (2021-09-29) Welcome to the v0.13.0-alpha.3 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Hetzner, Scaleway, Upcloud and Vultr Talos now natively supports three new cloud platforms: * [Hetzner](https://www.hetzner.com/), including VIP support * [Scaleway](https://www.scaleway.com/en/) * [Upcloud](https://upcloud.com/) * [Vultr](https://www.vultr.com/) Also generic `cloud-init` `nocloud` platform is supported in both networking and storage-based modes. ### etcd Advertised Address The address advertised by etcd can now be controlled with new machine configuration option `machine.etcd.subnet`. ### Reboots via kexec Talos now reboots by default via kexec syscall which means BIOS POST process is skipped. On bare-metal hardware BIOS POST process might take 10-15 minutes, so Talos reboots 10-15 minutes faster on bare-metal. Kexec support can be disabled with the following change to the machine configuration: ``` machine: sysctls: kernel.kexec_load_disabled: "1" ``` ### Cluster Discovery and KubeSpan This release of Talos provides initial support for cluster membership discovery and [KubeSpan](https://www.talos-systems.com/kubespan/). These new features are not enabled by default, to enable them please make following changes to the machine configuration: ```yaml machine: network: kubespan: enabled: true cluster: discovery: enabled: true ``` ### Windows Support CLI tool talosctl is now built for Windows and published as part of the release. ### Contributors * Andrey Smirnov * Artem Chernyshev * Seán C McCord * Serge Logvinov * Alexey Palazhchenko * Andrew Rynhard * Olli Janatuinen * Andrey Smirnov * Lennard Klein * Rui Lopes * Spencer Smith ### Changes
100 commits

* [`4044372e`](https://github.com/talos-systems/talos/commit/4044372e12ff5308ba9cb9178a7e6b3b32955aab) feat: harvest discovered endpoints and push them via discovery svc * [`9a51aa83`](https://github.com/talos-systems/talos/commit/9a51aa83581b25bdb0604904027a4cedf21b8123) feat: add an option to skip downed peers in KubeSpan * [`cbbd7c68`](https://github.com/talos-systems/talos/commit/cbbd7c68219808a4f4b0d805203326019ce14ec9) feat: publish node's ExternalIPs as node addresses * [`0f60ef6d`](https://github.com/talos-systems/talos/commit/0f60ef6d38f9f5978a19e0ca4c6729af03a11f0e) fix: reset inputs back to initial state in secrets.APIController * [`64cb873e`](https://github.com/talos-systems/talos/commit/64cb873ec4421d43b291acb8afe75f65728d5732) feat: override static pods default args by extra Args * [`ecdd7757`](https://github.com/talos-systems/talos/commit/ecdd7757fb5906d6fa904581efff74a16b22ae4b) test: workaround race in the tests with zaptest package * [`9c67fde7`](https://github.com/talos-systems/talos/commit/9c67fde759de1e2a9f2b4406d85485d3d71c3d99) release(v0.13.0-alpha.2): prepare release * [`30ae7142`](https://github.com/talos-systems/talos/commit/30ae714243379aaa3fb1e93023c2249ff3c3b4e3) feat: implement integration with Discovery Service * [`353d632a`](https://github.com/talos-systems/talos/commit/353d632ae5d944a8662f0746ff8e757a67ffca53) feat: add nocloud platform support * [`628fbf9b`](https://github.com/talos-systems/talos/commit/628fbf9b48d98df1063285b14958c94d246ce102) chore: update Linux to 5.10.69 * [`62acd625`](https://github.com/talos-systems/talos/commit/62acd6251637250dbea7d408d8cd4d5eb1f18713) fix: check trustd API CA on worker nodes * [`ba27bc36`](https://github.com/talos-systems/talos/commit/ba27bc366fb3166b22f1bda909b9ede486ad8c7d) feat: implement Hetzner Cloud support for virtual (shared) IP * [`95f440ea`](https://github.com/talos-systems/talos/commit/95f440eaa06d2a558fc828c11b451b6aed8d5855) test: add fuzz test for configloader * [`d2cf021d`](https://github.com/talos-systems/talos/commit/d2cf021d8ffb6d6188b2d50f1f7b9c24df0aac84) chore: remove deprecated "join" term * [`0e18e280`](https://github.com/talos-systems/talos/commit/0e18e2800fc038a86ed2fd9b042278ae29070bb5) chore: bump dependencies * [`b450b7ce`](https://github.com/talos-systems/talos/commit/b450b7cef0d84a9ad975d8b50b93854bb0645173) chore: deprecate Interfaces and Routes APIs * [`cddcb962`](https://github.com/talos-systems/talos/commit/cddcb9622bce7ae3626b8b9dce8c622a0e30ba66) fix: find devices without partition table * [`b1b6d613`](https://github.com/talos-systems/talos/commit/b1b6d61365c900c4ebfc377b86067ddbe4fe8353) fix: check for existence of dhcp6 FQDN first * [`519999b8`](https://github.com/talos-systems/talos/commit/519999b8462ff4931ed12323417b9a9c8c20b369) fix: use readonly mode when probing devices with `All` lookup * [`2b520420`](https://github.com/talos-systems/talos/commit/2b5204200a4bd22aa78245b201c471136016ce3a) feat: enable resource API in the maintenance mode * [`452893c2`](https://github.com/talos-systems/talos/commit/452893c260b920c601b0fc22ff018dc2d4341fca) fix: make probe open blockdevice in readonly mode * [`96bccdd3`](https://github.com/talos-systems/talos/commit/96bccdd3b625f0edefd685cadf5f2cd46e3111f5) test: update CABPT provider to 0.3 release * [`d9eb18bf`](https://github.com/talos-systems/talos/commit/d9eb18bfddf69a61712d930b53aec489a806394a) fix: containerd log symlink * [`efa7f48e`](https://github.com/talos-systems/talos/commit/efa7f48e08382249609e0ecd3241c01a2e46df73) docs: quicklinks on landing page * [`1cb9f282`](https://github.com/talos-systems/talos/commit/1cb9f282b541505f2d61ae0a57655cba9ae62843) fix: don't marshal clock with SecretsBundle * [`b27c75b3`](https://github.com/talos-systems/talos/commit/b27c75b30f689dafa7d4effd0c2eaf8f0f3f8caf) release(v0.13.0-alpha.1): prepare release * [`9d803d75`](https://github.com/talos-systems/talos/commit/9d803d75bfbe788fa5c2ef2ae0639de31e172c7b) chore: bump dependencies and drop firecracker support * [`50a24104`](https://github.com/talos-systems/talos/commit/50a24104820e26bb99e66ab68be2bd9a6c17b0be) feat: add operating system version field to discovery * [`085c61b2`](https://github.com/talos-systems/talos/commit/085c61b2ec432c586daa77464910e967a223ebe0) chore: add a special condition to check for kubeconfig readiness * [`21cdd854`](https://github.com/talos-systems/talos/commit/21cdd854036498fbeb9f6e4d058a0edd55ed4856) fix: add node address to the list of allowed IPs (kubespan) * [`fdd80a12`](https://github.com/talos-systems/talos/commit/fdd80a1234dc993cc01daa7764ba5a9db2fdc275) feat: add an option to continue booting on NTP timeout * [`ef368498`](https://github.com/talos-systems/talos/commit/ef36849899b18bbb35c6116fdf35aa580a50a5e5) feat: add routes, routing rules and nftables rules for KubeSpan * [`ed12379f`](https://github.com/talos-systems/talos/commit/ed12379f2f49fcbca84080f1066cf52dc202bd2d) fix: patch multi nodes support * [`d943bb0e`](https://github.com/talos-systems/talos/commit/d943bb0e280e90f3592d9f7b67813b7a15818c84) feat: update Kubernetes to 1.22.2 * [`d0585fb6`](https://github.com/talos-systems/talos/commit/d0585fb6b303dfdd7fc80a76024915df31c72389) feat: reboot via kexec * [`3de505c8`](https://github.com/talos-systems/talos/commit/3de505c894274bfd5248b6c597f6e3a53f873ba1) fix: skip bad cloud-config in OpenStack platform * [`a394d1e2`](https://github.com/talos-systems/talos/commit/a394d1e20ba82de7d05e4d3f91823a98362ac9ee) fix: tear down control plane static pods when etcd is stopped * [`1c05089b`](https://github.com/talos-systems/talos/commit/1c05089bb22c7c1050e95cf8d7bea8b763a0e86f) feat: implement KubeSpan manager for Wireguard peer state * [`ec7f44ef`](https://github.com/talos-systems/talos/commit/ec7f44efe4f89e7ed207cbd5fe3748953ccfdf28) fix: completely prevent editing resources other than mc * [`19a8ae97`](https://github.com/talos-systems/talos/commit/19a8ae97c69949f7c2421154b2ae4e52a905ff63) feat: add vultr.com cloud support * [`0ff4c7cd`](https://github.com/talos-systems/talos/commit/0ff4c7cdb2b9505823f4c4504ec9bf4d7fddf5c5) fix: write KubernetesCACert chmodded 0400 instead of 0500 * [`a1c9d649`](https://github.com/talos-systems/talos/commit/a1c9d64907cce75bcb566f3ee394734e29b3932d) fix: update the way results are retrieved for certified conformance * [`a0594540`](https://github.com/talos-systems/talos/commit/a0594540451a7636f8cd4bbe835913d31f66d0de) chore: build using Go 1.17 * [`7c5045bd`](https://github.com/talos-systems/talos/commit/7c5045bd929fcf5028cae3840970e692ef3bc7c9) release(v0.13.0-alpha.0): prepare release * [`ee2dce6c`](https://github.com/talos-systems/talos/commit/ee2dce6c1a0e8838e587a9136afd1b7381000432) chore: bump dependencies * [`ef022959`](https://github.com/talos-systems/talos/commit/ef022959280f156d6311836ef9cc2d01e5e3ae7d) fix: print etcd member ID in hex * [`5ca1fb82`](https://github.com/talos-systems/talos/commit/5ca1fb822125483be290e79d8828bba246fda51c) fix: multiple fixes for KubeSpan and Wireguard implementation * [`b1bd6425`](https://github.com/talos-systems/talos/commit/b1bd64250820df3fcb5214368ce9c8cf4634970a) fix: build platform images * [`3b5f4038`](https://github.com/talos-systems/talos/commit/3b5f4038de2f855b3b634e4abb1c564da624e2fc) feat: add scaleway.com cloud support * [`f156ab18`](https://github.com/talos-systems/talos/commit/f156ab1847f2ad1ca2a2548b299a713ee5fe0fcd) feat: add upcloud.com cloud support * [`c3b2429c`](https://github.com/talos-systems/talos/commit/c3b2429ce91edc4f8f9e720a4b144bc941046fc3) fix: suppress spurious Kubernetes API server cert updates * [`ff90b575`](https://github.com/talos-systems/talos/commit/ff90b5751e17a60fc6ca4274f35da7ddcca44fea) feat: implement KubeSpan peer generation controller * [`14c69df5`](https://github.com/talos-systems/talos/commit/14c69df5063e71765b9316ae37657fda2388c60e) fix: correctly parse multiple pod/service CIDRs * [`69897dbb`](https://github.com/talos-systems/talos/commit/69897dbba402812403c0c15d6cb8d2a771ea5a88) feat: drop some capabilities to be never available * [`51e9836b`](https://github.com/talos-systems/talos/commit/51e9836b01926d1619d662e6e08df29210ff94e5) docs: promote 0.12 docs to be the latest * [`812d59c7`](https://github.com/talos-systems/talos/commit/812d59c70085b54136e3b56127b0efea7ddb60af) feat: add hetzner.com cloud support * [`d53e9e89`](https://github.com/talos-systems/talos/commit/d53e9e89633258d85c2232b85855535ebb42c417) chore: use named constants * [`2dfe7f1f`](https://github.com/talos-systems/talos/commit/2dfe7f1fc654c8bec83b632a98dbaa8d1b90a521) chore: bump tools to the latest version * [`82b130e7`](https://github.com/talos-systems/talos/commit/82b130e789aa4376e1f0e2d086233e630b410f74) docs: document required options for extraMounts * [`af662210`](https://github.com/talos-systems/talos/commit/af6622109faecdf03aed43b047035904110c7580) feat: implement Kubernetes cluster discovery registry * [`2c66e1b3`](https://github.com/talos-systems/talos/commit/2c66e1b3c5d4c34c5d4cdc155c32f2808a5f1c69) feat: provide building of local `Affiliate` structure (for the node) * [`d69bd2af`](https://github.com/talos-systems/talos/commit/d69bd2af3e3d3bf12b6d74078e9eedf3dc8752fc) chore: enable GPG identity check for Talos * [`8dbd851f`](https://github.com/talos-systems/talos/commit/8dbd851fde3febb5999df694a079121b43519aa9) chore: update tools/pkgs/extras to the new version * [`0b347570`](https://github.com/talos-systems/talos/commit/0b347570a7aca0a133d6b6e6cc8d3e0355630480) feat: use dynamic NodeAddresses/HostnameStatus in Kubernetes certs * [`bd5b9c96`](https://github.com/talos-systems/talos/commit/bd5b9c96e2563249a5633433703493b292b83ee9) fix: correctly define example for `extraMounts` * [`01cca099`](https://github.com/talos-systems/talos/commit/01cca099f40ec75d1e047a84c89692eb254e8adf) docs: update docs for Talos 0.12 release * [`668627d5`](https://github.com/talos-systems/talos/commit/668627d5b8ec79ec955eb1254732b1cc031d3aec) feat: add subnet filter for etcd address * [`3c3c281b`](https://github.com/talos-systems/talos/commit/3c3c281bff8481f680feca9cf01af413a38e6973) chore: bump dependencies via dependabot * [`f8bebba2`](https://github.com/talos-systems/talos/commit/f8bebba2de3999b7a36fecb2d6b90e583372c98f) fix: ignore error on duplicate for `MountStatus` * [`6956edd0`](https://github.com/talos-systems/talos/commit/6956edd0bfae6c6c5d6eba00a22bc3a4cb7f54ea) feat: add node address filters, filter out k8s addresses for Talos API * [`caee24bf`](https://github.com/talos-systems/talos/commit/caee24bf61136daecb095991a6e439f7fbf40da2) feat: implement KubeSpan identity controller * [`da0f6e7e`](https://github.com/talos-systems/talos/commit/da0f6e7e1d295dce0c44c1854363528a6ffedde1) fix: allow updating diskSelector option * [`761ccaf3`](https://github.com/talos-systems/talos/commit/761ccaf32348d8664eb0d5d1a51f6abb19ca52a6) feat: provide machine configuration for KubeSpan and cluster discovery * [`a81e30cb`](https://github.com/talos-systems/talos/commit/a81e30cb46326fbdd433f37dc37549b588a2bc7a) docs: add bootstrap command to VMware docs * [`97da354c`](https://github.com/talos-systems/talos/commit/97da354cc0e4a965e14b8939c426150d5c12f228) fix: do not panic on invalid machine configs * [`c4048e26`](https://github.com/talos-systems/talos/commit/c4048e263d22682142f12fc4af6ac58c679273f0) fix: don't extract nil IPs in the GCP platform * [`ba169c6f`](https://github.com/talos-systems/talos/commit/ba169c6f91948cf057251236fa7a727a05253639) feat: provide talosctl.exe for Windows * [`6312f473`](https://github.com/talos-systems/talos/commit/6312f473e63df50287e6801c079242e2311a23e6) fix: properly handle omitempty fields in the validator * [`7f22879a`](https://github.com/talos-systems/talos/commit/7f22879af0882af4cdebe9c84afb96ae68eb9f20) feat: provide random node identity * [`032e7c6b`](https://github.com/talos-systems/talos/commit/032e7c6b863b5ca02cfa16df79c88950544dbffb) chore: import yaml.v3 consistently * [`80b5f0e7`](https://github.com/talos-systems/talos/commit/80b5f0e7f78f09a11ed249f9f1dc7b05ea275ab0) fix: validate IP address returned as HTTP response in platform code * [`c9af8f7f`](https://github.com/talos-systems/talos/commit/c9af8f7ff17facc18f10675879ed04982a000f6f) docs: fork docs for 0.13 * [`85cda1b9`](https://github.com/talos-systems/talos/commit/85cda1b956b042ba20696637248999d46f63ccc9) feat: provide MountStatus resource for system partition mounts * [`950f122c`](https://github.com/talos-systems/talos/commit/950f122c95e225858e77083f2490481ed8d21aef) chore: update versions in upgrade tests * [`83fdb772`](https://github.com/talos-systems/talos/commit/83fdb7721f45aa075898caf05a4b6856d3c5f330) feat: provide first NIC hardware addr as a resource * [`5f5ac12f`](https://github.com/talos-systems/talos/commit/5f5ac12f1dc8aeb3a8598e57d965471e93fe3724) fix: properly case the VMware name * [`0a6048f4`](https://github.com/talos-systems/talos/commit/0a6048f469da02efad7e84eb237e6fdeb85b7e33) fix: don't allow bootstrap if etcd data directory is not empty * [`e24b93b4`](https://github.com/talos-systems/talos/commit/e24b93b4e120448f37109599f3e9eb15954b147a) fix: cgroup delegate * [`751f64f9`](https://github.com/talos-systems/talos/commit/751f64f9bc10e9ad8508ade9e3a6a14aaaa54d57) docs: add release notes for 0.12, support matrix * [`57a77696`](https://github.com/talos-systems/talos/commit/57a77696ef2b255a59ee4ed213a1a3971a5e2943) feat: update Kubernetes to 1.22.1 * [`244b08cc`](https://github.com/talos-systems/talos/commit/244b08cc198a8ba676bb9acadcbdd23a161b0876) chore: bump dependencies * [`576ba195`](https://github.com/talos-systems/talos/commit/576ba195784abf275256c861d5f811ab1f7b1102) fix: do not set KSPP kernel params in container mode * [`b8c92ede`](https://github.com/talos-systems/talos/commit/b8c92ede52ed515dba68abf4fb1cc6494d510827) fix: don't support cgroups nesting in process runner * [`9bb0b797`](https://github.com/talos-systems/talos/commit/9bb0b79709a502ab49ea9bacd7e54617554d4cc3) test: adapt tests to the cgroupsv2 * [`1abc12be`](https://github.com/talos-systems/talos/commit/1abc12be13208ad1da03492a1b88d2c1ec0d5d33) fix: extramount should have `yaml:",inline"` tag * [`2b614e43`](https://github.com/talos-systems/talos/commit/2b614e430e478cc111db018996ab2c8f763e4f92) feat: check if cluster has deprecated resources versions * [`0b86edab`](https://github.com/talos-systems/talos/commit/0b86edab80cf4dd01f330d7721b130f5017d84a5) fix: don't panic if the machine config doesn't have network (EM) * [`8bef41e4`](https://github.com/talos-systems/talos/commit/8bef41e4bacc4190976657ae5021afecd2d6e001) fix: make sure file mode is same (reproducibility issue) * [`fcfca55a`](https://github.com/talos-systems/talos/commit/fcfca55a059e92fcda198baa321c4c63bda1f0a4) chore: do not check that go mod tidy gives empty output * [`5ce92ca5`](https://github.com/talos-systems/talos/commit/5ce92ca5163616fcd7abe16c4efc3a100953b246) docs: ensure azure VMs are 0 indexed

### Changes since v0.13.0-alpha.2
6 commits

* [`4044372e`](https://github.com/talos-systems/talos/commit/4044372e12ff5308ba9cb9178a7e6b3b32955aab) feat: harvest discovered endpoints and push them via discovery svc * [`9a51aa83`](https://github.com/talos-systems/talos/commit/9a51aa83581b25bdb0604904027a4cedf21b8123) feat: add an option to skip downed peers in KubeSpan * [`cbbd7c68`](https://github.com/talos-systems/talos/commit/cbbd7c68219808a4f4b0d805203326019ce14ec9) feat: publish node's ExternalIPs as node addresses * [`0f60ef6d`](https://github.com/talos-systems/talos/commit/0f60ef6d38f9f5978a19e0ca4c6729af03a11f0e) fix: reset inputs back to initial state in secrets.APIController * [`64cb873e`](https://github.com/talos-systems/talos/commit/64cb873ec4421d43b291acb8afe75f65728d5732) feat: override static pods default args by extra Args * [`ecdd7757`](https://github.com/talos-systems/talos/commit/ecdd7757fb5906d6fa904581efff74a16b22ae4b) test: workaround race in the tests with zaptest package

### Changes from talos-systems/discovery-service
17 commits

* [`b2e2079`](https://github.com/talos-systems/discovery-service/commit/b2e2079088a5b08d0466b36e0f56927fe4524d78) fix: properly encrypt IPv6 endpoints * [`e9d5dfa`](https://github.com/talos-systems/discovery-service/commit/e9d5dfa15e92fc1bdee620b2687eecd2ff929ac3) fix: enable connections to endpoints with public certs * [`509e9b2`](https://github.com/talos-systems/discovery-service/commit/509e9b2ced989631224afd3bfd5e8bdcbcf13137) feat: implement client wrapper around discovery service API * [`6195466`](https://github.com/talos-systems/discovery-service/commit/619546696afe99c1b58b46ca819bb32e74560f5b) feat: enable vtprotobuf, watch batching, more limits * [`7174ec1`](https://github.com/talos-systems/discovery-service/commit/7174ec10426ff17172aaa195423e2588b23aa868) feat: implement new discovery service * [`1a43970`](https://github.com/talos-systems/discovery-service/commit/1a4397082659a2b6bca6fd53a5080e109d978627) feat: add node and cluster validation * [`6454cfc`](https://github.com/talos-systems/discovery-service/commit/6454cfcb726bba64823f25d962bc04007d057a8b) refactor: kresify, fix linter and rename to Kubespan manager * [`d782452`](https://github.com/talos-systems/discovery-service/commit/d782452e86bf94c1194f806384fc339835a96d2d) add redis database backend * [`924fed4`](https://github.com/talos-systems/discovery-service/commit/924fed4ecc939887c55a4a8a5f5dfe737ca29c78) refactor to flexible addresses * [`cd02b5a`](https://github.com/talos-systems/discovery-service/commit/cd02b5a36783454f7650df5709984f01ccc8bd6e) revert to string IDs * [`576288f`](https://github.com/talos-systems/discovery-service/commit/576288fc7559f9eac1966627b6c1e2c731e0c322) add self-reported IPs * [`6ad15ca`](https://github.com/talos-systems/discovery-service/commit/6ad15ca825bdf4636855f2b77535945d07462a17) strong typing and known endpoint API * [`3437ff2`](https://github.com/talos-systems/discovery-service/commit/3437ff28fb9ab6efb96cc79da1c75bd81bfb50cc) fixes from testing * [`d3fd1f3`](https://github.com/talos-systems/discovery-service/commit/d3fd1f324d7af60f2ddf425c9c2cf9b9743e6d62) add Name to Node * [`eb0e8ba`](https://github.com/talos-systems/discovery-service/commit/eb0e8baadacd702044ca419f933dd409ae58977e) add simple client pkg * [`5e0c1df`](https://github.com/talos-systems/discovery-service/commit/5e0c1df0f1823cce1b92ccffb1e18fb1e540117d) add cluster hash grouping * [`f982696`](https://github.com/talos-systems/discovery-service/commit/f982696ec1b45f8d7e6d2dfa1d780b3b0d515dbb) initial commit

### Changes from talos-systems/extras
1 commit

* [`52b27da`](https://github.com/talos-systems/extras/commit/52b27dad5aeeb5d14225a99e4b5902614c993022) chore: update pkgs and tools to 0.8.0-alpha.0

### Changes from talos-systems/go-blockdevice
6 commits

* [`70d2865`](https://github.com/talos-systems/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks * [`667bf53`](https://github.com/talos-systems/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found * [`d7d4cdd`](https://github.com/talos-systems/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found * [`33afba3`](https://github.com/talos-systems/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method * [`e367f9d`](https://github.com/talos-systems/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode * [`d981156`](https://github.com/talos-systems/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows

### Changes from talos-systems/pkgs
7 commits

* [`28cda67`](https://github.com/talos-systems/pkgs/commit/28cda67f9b518cb4d4a299bac62be1c3ed3cddda) feat: update Linux kernel to 5.10.69 * [`db90f93`](https://github.com/talos-systems/pkgs/commit/db90f93c0b462dcaefa081c18f8adebae5d5744a) chore: update tools * [`ca38c59`](https://github.com/talos-systems/pkgs/commit/ca38c599b71334d5a108021c7e7ccec12701ff18) feat: enable KEXEC_FILE_LOAD in the kernel * [`982bc18`](https://github.com/talos-systems/pkgs/commit/982bc18a4ecf5d4e15a73c350abb97f02adb8871) chore: update tools * [`a243ab8`](https://github.com/talos-systems/pkgs/commit/a243ab8a9345b8bc39cc65254015b6eb07605f61) feat: add /usr/src to FHS * [`428abdb`](https://github.com/talos-systems/pkgs/commit/428abdbfd303fce69cf583f5a8a4f5ed43253807) chore: support builds with HTTP_PROXY * [`13151c5`](https://github.com/talos-systems/pkgs/commit/13151c59b5b29541ed5828aa9c75a061ec920ff1) chore: update bldr version, update tools

### Changes from talos-systems/tools
5 commits

* [`2790b55`](https://github.com/talos-systems/tools/commit/2790b5586e810c7dfc0a197ef9d1e6d77a646e3b) feat: update Go to 1.17.1 * [`5b9d214`](https://github.com/talos-systems/tools/commit/5b9d214c38515a55232ce36591036748fd8c49cc) fix: restore static library for ncurses * [`01104e5`](https://github.com/talos-systems/tools/commit/01104e562efdbff34fb2d597d4cf27d04ba44ea6) chore: reproducible builds * [`53fe146`](https://github.com/talos-systems/tools/commit/53fe146ca8ba55c959fee04302a5ce215a927f1d) chore: update bldr with new version * [`bf4540d`](https://github.com/talos-systems/tools/commit/bf4540d0ed0728cd7751e0c3ab3bb4b8927e334c) chore: add patch dependency

### Dependency Changes * **github.com/containerd/go-cni** v1.0.2 -> v1.1.0 * **github.com/containernetworking/cni** v0.8.1 -> v1.0.1 * **github.com/containernetworking/plugins** v0.9.1 -> v1.0.1 * **github.com/cosi-project/runtime** 25f235cd0682 -> 5cb7f5002d77 * **github.com/fatih/color** v1.12.0 -> v1.13.0 * **github.com/fsnotify/fsnotify** v1.4.9 -> v1.5.1 * **github.com/gdamore/tcell/v2** v2.4.0 -> f057f0a857a1 * **github.com/google/nftables** 16a134723a96 **_new_** * **github.com/hashicorp/go-getter** v1.5.7 -> v1.5.8 * **github.com/hetznercloud/hcloud-go** v1.32.0 **_new_** * **github.com/insomniacslk/dhcp** 1cac67f12b1e -> b95caade3eac * **github.com/jsimonetti/rtnetlink** 9c52e516c709 -> 435639c8e6a8 * **github.com/jxskiss/base62** 4f11678b909b **_new_** * **github.com/mattn/go-isatty** v0.0.13 -> v0.0.14 * **github.com/mdlayher/netx** 669a06fde734 **_new_** * **github.com/packethost/packngo** v0.19.0 -> v0.19.1 * **github.com/prometheus/procfs** v0.7.2 -> v0.7.3 * **github.com/rivo/tview** 29d673af0ce2 -> ee97a7ab3975 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.7 **_new_** * **github.com/talos-systems/discovery-service** b2e2079088a5 **_new_** * **github.com/talos-systems/extras** v0.5.0 -> v0.6.0-alpha.0 * **github.com/talos-systems/go-blockdevice** v0.2.3 -> 70d28650b398 * **github.com/talos-systems/pkgs** v0.7.0 -> v0.8.0-alpha.0-4-g28cda67 * **github.com/talos-systems/tools** v0.7.0-1-ga33ccc1 -> v0.8.0-alpha.0-3-g2790b55 * **github.com/vishvananda/netlink** f5de75959ad5 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.53.1 -> v0.53.2 * **github.com/vmware/govmomi** v0.26.0 -> v0.26.1 * **github.com/vultr/metadata** v1.0.3 **_new_** * **go.uber.org/zap** v1.19.0 -> v1.19.1 * **golang.org/x/net** 853a461950ff -> 3ad01bbaa167 * **golang.org/x/sys** 0f9fa26af87c -> 39ccf1dd6fa6 * **golang.org/x/term** 6886f2dfbf5b -> 140adaaadfaf * **golang.zx2c4.com/wireguard/wgctrl** 92e472f520a5 -> 0a2f4901cba6 * **google.golang.org/grpc** v1.40.0 -> v1.41.0 * **inet.af/netaddr** ce7a8ad02cc1 -> 85fa6c94624e * **k8s.io/api** v0.22.1 -> v0.22.2 * **k8s.io/apimachinery** v0.22.1 -> v0.22.2 * **k8s.io/client-go** v0.22.1 -> v0.22.2 * **k8s.io/kubectl** v0.22.1 -> v0.22.2 * **k8s.io/kubelet** v0.22.1 -> v0.22.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.59 **_new_** Previous release can be found at [v0.12.0](https://github.com/talos-systems/talos/releases/tag/v0.12.0) ## [Talos 0.13.0-alpha.2](https://github.com/talos-systems/talos/releases/tag/v0.13.0-alpha.2) (2021-09-28) Welcome to the v0.13.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Hetzner, Scaleway, Upcloud and Vultr Talos now natively supports three new cloud platforms: * [Hetzner](https://www.hetzner.com/), including VIP support * [Scaleway](https://www.scaleway.com/en/) * [Upcloud](https://upcloud.com/) * [Vultr](https://www.vultr.com/) Also generic `cloud-init` `nocloud` platform is supported in both networking and storage-based modes. ### etcd Advertised Address The address advertised by etcd can now be controlled with new machine configuration option `machine.etcd.subnet`. ### Reboots via kexec Talos now reboots by default via kexec syscall which means BIOS POST process is skipped. On bare-metal hardware BIOS POST process might take 10-15 minutes, so Talos reboots 10-15 minutes faster on bare-metal. Kexec support can be disabled with the following change to the machine configuration: ``` machine: sysctls: kernel.kexec_load_disabled: "1" ``` ### Cluster Discovery and KubeSpan This release of Talos provides initial support for cluster membership discovery and [KubeSpan](https://www.talos-systems.com/kubespan/). These new features are not enabled by default, to enable them please make following changes to the machine configuration: ```yaml machine: network: kubespan: enabled: true cluster: discovery: enabled: true ``` ### Windows Support CLI tool talosctl is now built for Windows and published as part of the release. ### Contributors * Andrey Smirnov * Artem Chernyshev * Seán C McCord * Serge Logvinov * Alexey Palazhchenko * Andrew Rynhard * Olli Janatuinen * Andrey Smirnov * Lennard Klein * Rui Lopes * Spencer Smith ### Changes
93 commits

* [`30ae7142`](https://github.com/talos-systems/talos/commit/30ae714243379aaa3fb1e93023c2249ff3c3b4e3) feat: implement integration with Discovery Service * [`353d632a`](https://github.com/talos-systems/talos/commit/353d632ae5d944a8662f0746ff8e757a67ffca53) feat: add nocloud platform support * [`628fbf9b`](https://github.com/talos-systems/talos/commit/628fbf9b48d98df1063285b14958c94d246ce102) chore: update Linux to 5.10.69 * [`62acd625`](https://github.com/talos-systems/talos/commit/62acd6251637250dbea7d408d8cd4d5eb1f18713) fix: check trustd API CA on worker nodes * [`ba27bc36`](https://github.com/talos-systems/talos/commit/ba27bc366fb3166b22f1bda909b9ede486ad8c7d) feat: implement Hetzner Cloud support for virtual (shared) IP * [`95f440ea`](https://github.com/talos-systems/talos/commit/95f440eaa06d2a558fc828c11b451b6aed8d5855) test: add fuzz test for configloader * [`d2cf021d`](https://github.com/talos-systems/talos/commit/d2cf021d8ffb6d6188b2d50f1f7b9c24df0aac84) chore: remove deprecated "join" term * [`0e18e280`](https://github.com/talos-systems/talos/commit/0e18e2800fc038a86ed2fd9b042278ae29070bb5) chore: bump dependencies * [`b450b7ce`](https://github.com/talos-systems/talos/commit/b450b7cef0d84a9ad975d8b50b93854bb0645173) chore: deprecate Interfaces and Routes APIs * [`cddcb962`](https://github.com/talos-systems/talos/commit/cddcb9622bce7ae3626b8b9dce8c622a0e30ba66) fix: find devices without partition table * [`b1b6d613`](https://github.com/talos-systems/talos/commit/b1b6d61365c900c4ebfc377b86067ddbe4fe8353) fix: check for existence of dhcp6 FQDN first * [`519999b8`](https://github.com/talos-systems/talos/commit/519999b8462ff4931ed12323417b9a9c8c20b369) fix: use readonly mode when probing devices with `All` lookup * [`2b520420`](https://github.com/talos-systems/talos/commit/2b5204200a4bd22aa78245b201c471136016ce3a) feat: enable resource API in the maintenance mode * [`452893c2`](https://github.com/talos-systems/talos/commit/452893c260b920c601b0fc22ff018dc2d4341fca) fix: make probe open blockdevice in readonly mode * [`96bccdd3`](https://github.com/talos-systems/talos/commit/96bccdd3b625f0edefd685cadf5f2cd46e3111f5) test: update CABPT provider to 0.3 release * [`d9eb18bf`](https://github.com/talos-systems/talos/commit/d9eb18bfddf69a61712d930b53aec489a806394a) fix: containerd log symlink * [`efa7f48e`](https://github.com/talos-systems/talos/commit/efa7f48e08382249609e0ecd3241c01a2e46df73) docs: quicklinks on landing page * [`1cb9f282`](https://github.com/talos-systems/talos/commit/1cb9f282b541505f2d61ae0a57655cba9ae62843) fix: don't marshal clock with SecretsBundle * [`b27c75b3`](https://github.com/talos-systems/talos/commit/b27c75b30f689dafa7d4effd0c2eaf8f0f3f8caf) release(v0.13.0-alpha.1): prepare release * [`9d803d75`](https://github.com/talos-systems/talos/commit/9d803d75bfbe788fa5c2ef2ae0639de31e172c7b) chore: bump dependencies and drop firecracker support * [`50a24104`](https://github.com/talos-systems/talos/commit/50a24104820e26bb99e66ab68be2bd9a6c17b0be) feat: add operating system version field to discovery * [`085c61b2`](https://github.com/talos-systems/talos/commit/085c61b2ec432c586daa77464910e967a223ebe0) chore: add a special condition to check for kubeconfig readiness * [`21cdd854`](https://github.com/talos-systems/talos/commit/21cdd854036498fbeb9f6e4d058a0edd55ed4856) fix: add node address to the list of allowed IPs (kubespan) * [`fdd80a12`](https://github.com/talos-systems/talos/commit/fdd80a1234dc993cc01daa7764ba5a9db2fdc275) feat: add an option to continue booting on NTP timeout * [`ef368498`](https://github.com/talos-systems/talos/commit/ef36849899b18bbb35c6116fdf35aa580a50a5e5) feat: add routes, routing rules and nftables rules for KubeSpan * [`ed12379f`](https://github.com/talos-systems/talos/commit/ed12379f2f49fcbca84080f1066cf52dc202bd2d) fix: patch multi nodes support * [`d943bb0e`](https://github.com/talos-systems/talos/commit/d943bb0e280e90f3592d9f7b67813b7a15818c84) feat: update Kubernetes to 1.22.2 * [`d0585fb6`](https://github.com/talos-systems/talos/commit/d0585fb6b303dfdd7fc80a76024915df31c72389) feat: reboot via kexec * [`3de505c8`](https://github.com/talos-systems/talos/commit/3de505c894274bfd5248b6c597f6e3a53f873ba1) fix: skip bad cloud-config in OpenStack platform * [`a394d1e2`](https://github.com/talos-systems/talos/commit/a394d1e20ba82de7d05e4d3f91823a98362ac9ee) fix: tear down control plane static pods when etcd is stopped * [`1c05089b`](https://github.com/talos-systems/talos/commit/1c05089bb22c7c1050e95cf8d7bea8b763a0e86f) feat: implement KubeSpan manager for Wireguard peer state * [`ec7f44ef`](https://github.com/talos-systems/talos/commit/ec7f44efe4f89e7ed207cbd5fe3748953ccfdf28) fix: completely prevent editing resources other than mc * [`19a8ae97`](https://github.com/talos-systems/talos/commit/19a8ae97c69949f7c2421154b2ae4e52a905ff63) feat: add vultr.com cloud support * [`0ff4c7cd`](https://github.com/talos-systems/talos/commit/0ff4c7cdb2b9505823f4c4504ec9bf4d7fddf5c5) fix: write KubernetesCACert chmodded 0400 instead of 0500 * [`a1c9d649`](https://github.com/talos-systems/talos/commit/a1c9d64907cce75bcb566f3ee394734e29b3932d) fix: update the way results are retrieved for certified conformance * [`a0594540`](https://github.com/talos-systems/talos/commit/a0594540451a7636f8cd4bbe835913d31f66d0de) chore: build using Go 1.17 * [`7c5045bd`](https://github.com/talos-systems/talos/commit/7c5045bd929fcf5028cae3840970e692ef3bc7c9) release(v0.13.0-alpha.0): prepare release * [`ee2dce6c`](https://github.com/talos-systems/talos/commit/ee2dce6c1a0e8838e587a9136afd1b7381000432) chore: bump dependencies * [`ef022959`](https://github.com/talos-systems/talos/commit/ef022959280f156d6311836ef9cc2d01e5e3ae7d) fix: print etcd member ID in hex * [`5ca1fb82`](https://github.com/talos-systems/talos/commit/5ca1fb822125483be290e79d8828bba246fda51c) fix: multiple fixes for KubeSpan and Wireguard implementation * [`b1bd6425`](https://github.com/talos-systems/talos/commit/b1bd64250820df3fcb5214368ce9c8cf4634970a) fix: build platform images * [`3b5f4038`](https://github.com/talos-systems/talos/commit/3b5f4038de2f855b3b634e4abb1c564da624e2fc) feat: add scaleway.com cloud support * [`f156ab18`](https://github.com/talos-systems/talos/commit/f156ab1847f2ad1ca2a2548b299a713ee5fe0fcd) feat: add upcloud.com cloud support * [`c3b2429c`](https://github.com/talos-systems/talos/commit/c3b2429ce91edc4f8f9e720a4b144bc941046fc3) fix: suppress spurious Kubernetes API server cert updates * [`ff90b575`](https://github.com/talos-systems/talos/commit/ff90b5751e17a60fc6ca4274f35da7ddcca44fea) feat: implement KubeSpan peer generation controller * [`14c69df5`](https://github.com/talos-systems/talos/commit/14c69df5063e71765b9316ae37657fda2388c60e) fix: correctly parse multiple pod/service CIDRs * [`69897dbb`](https://github.com/talos-systems/talos/commit/69897dbba402812403c0c15d6cb8d2a771ea5a88) feat: drop some capabilities to be never available * [`51e9836b`](https://github.com/talos-systems/talos/commit/51e9836b01926d1619d662e6e08df29210ff94e5) docs: promote 0.12 docs to be the latest * [`812d59c7`](https://github.com/talos-systems/talos/commit/812d59c70085b54136e3b56127b0efea7ddb60af) feat: add hetzner.com cloud support * [`d53e9e89`](https://github.com/talos-systems/talos/commit/d53e9e89633258d85c2232b85855535ebb42c417) chore: use named constants * [`2dfe7f1f`](https://github.com/talos-systems/talos/commit/2dfe7f1fc654c8bec83b632a98dbaa8d1b90a521) chore: bump tools to the latest version * [`82b130e7`](https://github.com/talos-systems/talos/commit/82b130e789aa4376e1f0e2d086233e630b410f74) docs: document required options for extraMounts * [`af662210`](https://github.com/talos-systems/talos/commit/af6622109faecdf03aed43b047035904110c7580) feat: implement Kubernetes cluster discovery registry * [`2c66e1b3`](https://github.com/talos-systems/talos/commit/2c66e1b3c5d4c34c5d4cdc155c32f2808a5f1c69) feat: provide building of local `Affiliate` structure (for the node) * [`d69bd2af`](https://github.com/talos-systems/talos/commit/d69bd2af3e3d3bf12b6d74078e9eedf3dc8752fc) chore: enable GPG identity check for Talos * [`8dbd851f`](https://github.com/talos-systems/talos/commit/8dbd851fde3febb5999df694a079121b43519aa9) chore: update tools/pkgs/extras to the new version * [`0b347570`](https://github.com/talos-systems/talos/commit/0b347570a7aca0a133d6b6e6cc8d3e0355630480) feat: use dynamic NodeAddresses/HostnameStatus in Kubernetes certs * [`bd5b9c96`](https://github.com/talos-systems/talos/commit/bd5b9c96e2563249a5633433703493b292b83ee9) fix: correctly define example for `extraMounts` * [`01cca099`](https://github.com/talos-systems/talos/commit/01cca099f40ec75d1e047a84c89692eb254e8adf) docs: update docs for Talos 0.12 release * [`668627d5`](https://github.com/talos-systems/talos/commit/668627d5b8ec79ec955eb1254732b1cc031d3aec) feat: add subnet filter for etcd address * [`3c3c281b`](https://github.com/talos-systems/talos/commit/3c3c281bff8481f680feca9cf01af413a38e6973) chore: bump dependencies via dependabot * [`f8bebba2`](https://github.com/talos-systems/talos/commit/f8bebba2de3999b7a36fecb2d6b90e583372c98f) fix: ignore error on duplicate for `MountStatus` * [`6956edd0`](https://github.com/talos-systems/talos/commit/6956edd0bfae6c6c5d6eba00a22bc3a4cb7f54ea) feat: add node address filters, filter out k8s addresses for Talos API * [`caee24bf`](https://github.com/talos-systems/talos/commit/caee24bf61136daecb095991a6e439f7fbf40da2) feat: implement KubeSpan identity controller * [`da0f6e7e`](https://github.com/talos-systems/talos/commit/da0f6e7e1d295dce0c44c1854363528a6ffedde1) fix: allow updating diskSelector option * [`761ccaf3`](https://github.com/talos-systems/talos/commit/761ccaf32348d8664eb0d5d1a51f6abb19ca52a6) feat: provide machine configuration for KubeSpan and cluster discovery * [`a81e30cb`](https://github.com/talos-systems/talos/commit/a81e30cb46326fbdd433f37dc37549b588a2bc7a) docs: add bootstrap command to VMware docs * [`97da354c`](https://github.com/talos-systems/talos/commit/97da354cc0e4a965e14b8939c426150d5c12f228) fix: do not panic on invalid machine configs * [`c4048e26`](https://github.com/talos-systems/talos/commit/c4048e263d22682142f12fc4af6ac58c679273f0) fix: don't extract nil IPs in the GCP platform * [`ba169c6f`](https://github.com/talos-systems/talos/commit/ba169c6f91948cf057251236fa7a727a05253639) feat: provide talosctl.exe for Windows * [`6312f473`](https://github.com/talos-systems/talos/commit/6312f473e63df50287e6801c079242e2311a23e6) fix: properly handle omitempty fields in the validator * [`7f22879a`](https://github.com/talos-systems/talos/commit/7f22879af0882af4cdebe9c84afb96ae68eb9f20) feat: provide random node identity * [`032e7c6b`](https://github.com/talos-systems/talos/commit/032e7c6b863b5ca02cfa16df79c88950544dbffb) chore: import yaml.v3 consistently * [`80b5f0e7`](https://github.com/talos-systems/talos/commit/80b5f0e7f78f09a11ed249f9f1dc7b05ea275ab0) fix: validate IP address returned as HTTP response in platform code * [`c9af8f7f`](https://github.com/talos-systems/talos/commit/c9af8f7ff17facc18f10675879ed04982a000f6f) docs: fork docs for 0.13 * [`85cda1b9`](https://github.com/talos-systems/talos/commit/85cda1b956b042ba20696637248999d46f63ccc9) feat: provide MountStatus resource for system partition mounts * [`950f122c`](https://github.com/talos-systems/talos/commit/950f122c95e225858e77083f2490481ed8d21aef) chore: update versions in upgrade tests * [`83fdb772`](https://github.com/talos-systems/talos/commit/83fdb7721f45aa075898caf05a4b6856d3c5f330) feat: provide first NIC hardware addr as a resource * [`5f5ac12f`](https://github.com/talos-systems/talos/commit/5f5ac12f1dc8aeb3a8598e57d965471e93fe3724) fix: properly case the VMware name * [`0a6048f4`](https://github.com/talos-systems/talos/commit/0a6048f469da02efad7e84eb237e6fdeb85b7e33) fix: don't allow bootstrap if etcd data directory is not empty * [`e24b93b4`](https://github.com/talos-systems/talos/commit/e24b93b4e120448f37109599f3e9eb15954b147a) fix: cgroup delegate * [`751f64f9`](https://github.com/talos-systems/talos/commit/751f64f9bc10e9ad8508ade9e3a6a14aaaa54d57) docs: add release notes for 0.12, support matrix * [`57a77696`](https://github.com/talos-systems/talos/commit/57a77696ef2b255a59ee4ed213a1a3971a5e2943) feat: update Kubernetes to 1.22.1 * [`244b08cc`](https://github.com/talos-systems/talos/commit/244b08cc198a8ba676bb9acadcbdd23a161b0876) chore: bump dependencies * [`576ba195`](https://github.com/talos-systems/talos/commit/576ba195784abf275256c861d5f811ab1f7b1102) fix: do not set KSPP kernel params in container mode * [`b8c92ede`](https://github.com/talos-systems/talos/commit/b8c92ede52ed515dba68abf4fb1cc6494d510827) fix: don't support cgroups nesting in process runner * [`9bb0b797`](https://github.com/talos-systems/talos/commit/9bb0b79709a502ab49ea9bacd7e54617554d4cc3) test: adapt tests to the cgroupsv2 * [`1abc12be`](https://github.com/talos-systems/talos/commit/1abc12be13208ad1da03492a1b88d2c1ec0d5d33) fix: extramount should have `yaml:",inline"` tag * [`2b614e43`](https://github.com/talos-systems/talos/commit/2b614e430e478cc111db018996ab2c8f763e4f92) feat: check if cluster has deprecated resources versions * [`0b86edab`](https://github.com/talos-systems/talos/commit/0b86edab80cf4dd01f330d7721b130f5017d84a5) fix: don't panic if the machine config doesn't have network (EM) * [`8bef41e4`](https://github.com/talos-systems/talos/commit/8bef41e4bacc4190976657ae5021afecd2d6e001) fix: make sure file mode is same (reproducibility issue) * [`fcfca55a`](https://github.com/talos-systems/talos/commit/fcfca55a059e92fcda198baa321c4c63bda1f0a4) chore: do not check that go mod tidy gives empty output * [`5ce92ca5`](https://github.com/talos-systems/talos/commit/5ce92ca5163616fcd7abe16c4efc3a100953b246) docs: ensure azure VMs are 0 indexed

### Changes since v0.13.0-alpha.1
18 commits

* [`30ae7142`](https://github.com/talos-systems/talos/commit/30ae714243379aaa3fb1e93023c2249ff3c3b4e3) feat: implement integration with Discovery Service * [`353d632a`](https://github.com/talos-systems/talos/commit/353d632ae5d944a8662f0746ff8e757a67ffca53) feat: add nocloud platform support * [`628fbf9b`](https://github.com/talos-systems/talos/commit/628fbf9b48d98df1063285b14958c94d246ce102) chore: update Linux to 5.10.69 * [`62acd625`](https://github.com/talos-systems/talos/commit/62acd6251637250dbea7d408d8cd4d5eb1f18713) fix: check trustd API CA on worker nodes * [`ba27bc36`](https://github.com/talos-systems/talos/commit/ba27bc366fb3166b22f1bda909b9ede486ad8c7d) feat: implement Hetzner Cloud support for virtual (shared) IP * [`95f440ea`](https://github.com/talos-systems/talos/commit/95f440eaa06d2a558fc828c11b451b6aed8d5855) test: add fuzz test for configloader * [`d2cf021d`](https://github.com/talos-systems/talos/commit/d2cf021d8ffb6d6188b2d50f1f7b9c24df0aac84) chore: remove deprecated "join" term * [`0e18e280`](https://github.com/talos-systems/talos/commit/0e18e2800fc038a86ed2fd9b042278ae29070bb5) chore: bump dependencies * [`b450b7ce`](https://github.com/talos-systems/talos/commit/b450b7cef0d84a9ad975d8b50b93854bb0645173) chore: deprecate Interfaces and Routes APIs * [`cddcb962`](https://github.com/talos-systems/talos/commit/cddcb9622bce7ae3626b8b9dce8c622a0e30ba66) fix: find devices without partition table * [`b1b6d613`](https://github.com/talos-systems/talos/commit/b1b6d61365c900c4ebfc377b86067ddbe4fe8353) fix: check for existence of dhcp6 FQDN first * [`519999b8`](https://github.com/talos-systems/talos/commit/519999b8462ff4931ed12323417b9a9c8c20b369) fix: use readonly mode when probing devices with `All` lookup * [`2b520420`](https://github.com/talos-systems/talos/commit/2b5204200a4bd22aa78245b201c471136016ce3a) feat: enable resource API in the maintenance mode * [`452893c2`](https://github.com/talos-systems/talos/commit/452893c260b920c601b0fc22ff018dc2d4341fca) fix: make probe open blockdevice in readonly mode * [`96bccdd3`](https://github.com/talos-systems/talos/commit/96bccdd3b625f0edefd685cadf5f2cd46e3111f5) test: update CABPT provider to 0.3 release * [`d9eb18bf`](https://github.com/talos-systems/talos/commit/d9eb18bfddf69a61712d930b53aec489a806394a) fix: containerd log symlink * [`efa7f48e`](https://github.com/talos-systems/talos/commit/efa7f48e08382249609e0ecd3241c01a2e46df73) docs: quicklinks on landing page * [`1cb9f282`](https://github.com/talos-systems/talos/commit/1cb9f282b541505f2d61ae0a57655cba9ae62843) fix: don't marshal clock with SecretsBundle

### Changes from talos-systems/discovery-service
16 commits

* [`e9d5dfa`](https://github.com/talos-systems/discovery-service/commit/e9d5dfa15e92fc1bdee620b2687eecd2ff929ac3) fix: enable connections to endpoints with public certs * [`509e9b2`](https://github.com/talos-systems/discovery-service/commit/509e9b2ced989631224afd3bfd5e8bdcbcf13137) feat: implement client wrapper around discovery service API * [`6195466`](https://github.com/talos-systems/discovery-service/commit/619546696afe99c1b58b46ca819bb32e74560f5b) feat: enable vtprotobuf, watch batching, more limits * [`7174ec1`](https://github.com/talos-systems/discovery-service/commit/7174ec10426ff17172aaa195423e2588b23aa868) feat: implement new discovery service * [`1a43970`](https://github.com/talos-systems/discovery-service/commit/1a4397082659a2b6bca6fd53a5080e109d978627) feat: add node and cluster validation * [`6454cfc`](https://github.com/talos-systems/discovery-service/commit/6454cfcb726bba64823f25d962bc04007d057a8b) refactor: kresify, fix linter and rename to Kubespan manager * [`d782452`](https://github.com/talos-systems/discovery-service/commit/d782452e86bf94c1194f806384fc339835a96d2d) add redis database backend * [`924fed4`](https://github.com/talos-systems/discovery-service/commit/924fed4ecc939887c55a4a8a5f5dfe737ca29c78) refactor to flexible addresses * [`cd02b5a`](https://github.com/talos-systems/discovery-service/commit/cd02b5a36783454f7650df5709984f01ccc8bd6e) revert to string IDs * [`576288f`](https://github.com/talos-systems/discovery-service/commit/576288fc7559f9eac1966627b6c1e2c731e0c322) add self-reported IPs * [`6ad15ca`](https://github.com/talos-systems/discovery-service/commit/6ad15ca825bdf4636855f2b77535945d07462a17) strong typing and known endpoint API * [`3437ff2`](https://github.com/talos-systems/discovery-service/commit/3437ff28fb9ab6efb96cc79da1c75bd81bfb50cc) fixes from testing * [`d3fd1f3`](https://github.com/talos-systems/discovery-service/commit/d3fd1f324d7af60f2ddf425c9c2cf9b9743e6d62) add Name to Node * [`eb0e8ba`](https://github.com/talos-systems/discovery-service/commit/eb0e8baadacd702044ca419f933dd409ae58977e) add simple client pkg * [`5e0c1df`](https://github.com/talos-systems/discovery-service/commit/5e0c1df0f1823cce1b92ccffb1e18fb1e540117d) add cluster hash grouping * [`f982696`](https://github.com/talos-systems/discovery-service/commit/f982696ec1b45f8d7e6d2dfa1d780b3b0d515dbb) initial commit

### Changes from talos-systems/extras
1 commit

* [`52b27da`](https://github.com/talos-systems/extras/commit/52b27dad5aeeb5d14225a99e4b5902614c993022) chore: update pkgs and tools to 0.8.0-alpha.0

### Changes from talos-systems/go-blockdevice
6 commits

* [`70d2865`](https://github.com/talos-systems/go-blockdevice/commit/70d28650b398a14469cbb5356417355b0ba62956) fix: try to find cdrom disks * [`667bf53`](https://github.com/talos-systems/go-blockdevice/commit/667bf539b99ac34b629a0103ef7a7278a5a5f35d) fix: revert gpt partition not found * [`d7d4cdd`](https://github.com/talos-systems/go-blockdevice/commit/d7d4cdd7ac56c82caab19246b5decd59f12195eb) fix: gpt partition not found * [`33afba3`](https://github.com/talos-systems/go-blockdevice/commit/33afba347c0dce38a436c46a0aac26d2f99427c1) fix: also open in readonly mode when running `All` lookup method * [`e367f9d`](https://github.com/talos-systems/go-blockdevice/commit/e367f9dc7fa935f11672de0fdc8a89429285a07a) feat: make probe always open blockdevices in readonly mode * [`d981156`](https://github.com/talos-systems/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows

### Changes from talos-systems/pkgs
7 commits

* [`28cda67`](https://github.com/talos-systems/pkgs/commit/28cda67f9b518cb4d4a299bac62be1c3ed3cddda) feat: update Linux kernel to 5.10.69 * [`db90f93`](https://github.com/talos-systems/pkgs/commit/db90f93c0b462dcaefa081c18f8adebae5d5744a) chore: update tools * [`ca38c59`](https://github.com/talos-systems/pkgs/commit/ca38c599b71334d5a108021c7e7ccec12701ff18) feat: enable KEXEC_FILE_LOAD in the kernel * [`982bc18`](https://github.com/talos-systems/pkgs/commit/982bc18a4ecf5d4e15a73c350abb97f02adb8871) chore: update tools * [`a243ab8`](https://github.com/talos-systems/pkgs/commit/a243ab8a9345b8bc39cc65254015b6eb07605f61) feat: add /usr/src to FHS * [`428abdb`](https://github.com/talos-systems/pkgs/commit/428abdbfd303fce69cf583f5a8a4f5ed43253807) chore: support builds with HTTP_PROXY * [`13151c5`](https://github.com/talos-systems/pkgs/commit/13151c59b5b29541ed5828aa9c75a061ec920ff1) chore: update bldr version, update tools

### Changes from talos-systems/tools
5 commits

* [`2790b55`](https://github.com/talos-systems/tools/commit/2790b5586e810c7dfc0a197ef9d1e6d77a646e3b) feat: update Go to 1.17.1 * [`5b9d214`](https://github.com/talos-systems/tools/commit/5b9d214c38515a55232ce36591036748fd8c49cc) fix: restore static library for ncurses * [`01104e5`](https://github.com/talos-systems/tools/commit/01104e562efdbff34fb2d597d4cf27d04ba44ea6) chore: reproducible builds * [`53fe146`](https://github.com/talos-systems/tools/commit/53fe146ca8ba55c959fee04302a5ce215a927f1d) chore: update bldr with new version * [`bf4540d`](https://github.com/talos-systems/tools/commit/bf4540d0ed0728cd7751e0c3ab3bb4b8927e334c) chore: add patch dependency

### Dependency Changes * **github.com/containerd/go-cni** v1.0.2 -> v1.1.0 * **github.com/containernetworking/cni** v0.8.1 -> v1.0.1 * **github.com/containernetworking/plugins** v0.9.1 -> v1.0.1 * **github.com/cosi-project/runtime** 25f235cd0682 -> 5cb7f5002d77 * **github.com/fatih/color** v1.12.0 -> v1.13.0 * **github.com/fsnotify/fsnotify** v1.4.9 -> v1.5.1 * **github.com/gdamore/tcell/v2** v2.4.0 -> f057f0a857a1 * **github.com/google/nftables** 16a134723a96 **_new_** * **github.com/hashicorp/go-getter** v1.5.7 -> v1.5.8 * **github.com/hetznercloud/hcloud-go** v1.32.0 **_new_** * **github.com/insomniacslk/dhcp** 1cac67f12b1e -> b95caade3eac * **github.com/jsimonetti/rtnetlink** 9c52e516c709 -> 435639c8e6a8 * **github.com/jxskiss/base62** 4f11678b909b **_new_** * **github.com/mattn/go-isatty** v0.0.13 -> v0.0.14 * **github.com/mdlayher/netx** 669a06fde734 **_new_** * **github.com/packethost/packngo** v0.19.0 -> v0.19.1 * **github.com/prometheus/procfs** v0.7.2 -> v0.7.3 * **github.com/rivo/tview** 29d673af0ce2 -> ee97a7ab3975 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.7 **_new_** * **github.com/talos-systems/discovery-service** e9d5dfa15e92 **_new_** * **github.com/talos-systems/extras** v0.5.0 -> v0.6.0-alpha.0 * **github.com/talos-systems/go-blockdevice** v0.2.3 -> 70d28650b398 * **github.com/talos-systems/pkgs** v0.7.0 -> v0.8.0-alpha.0-4-g28cda67 * **github.com/talos-systems/tools** v0.7.0-1-ga33ccc1 -> v0.8.0-alpha.0-3-g2790b55 * **github.com/vishvananda/netlink** f5de75959ad5 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.53.1 -> v0.53.2 * **github.com/vmware/govmomi** v0.26.0 -> v0.26.1 * **github.com/vultr/metadata** v1.0.3 **_new_** * **go.uber.org/zap** v1.19.0 -> v1.19.1 * **golang.org/x/net** 853a461950ff -> 3ad01bbaa167 * **golang.org/x/sys** 0f9fa26af87c -> 39ccf1dd6fa6 * **golang.org/x/term** 6886f2dfbf5b -> 140adaaadfaf * **golang.zx2c4.com/wireguard/wgctrl** 92e472f520a5 -> 0a2f4901cba6 * **google.golang.org/grpc** v1.40.0 -> v1.41.0 * **inet.af/netaddr** ce7a8ad02cc1 -> 85fa6c94624e * **k8s.io/api** v0.22.1 -> v0.22.2 * **k8s.io/apimachinery** v0.22.1 -> v0.22.2 * **k8s.io/client-go** v0.22.1 -> v0.22.2 * **k8s.io/kubectl** v0.22.1 -> v0.22.2 * **k8s.io/kubelet** v0.22.1 -> v0.22.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.59 **_new_** Previous release can be found at [v0.12.0](https://github.com/talos-systems/talos/releases/tag/v0.12.0) ## [Talos 0.13.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.13.0-alpha.1) (2021-09-20) Welcome to the v0.13.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Hetzner, Scaleway, Upcloud and Vultr Talos now natively supports three new cloud platforms: * [Hetzner](https://www.hetzner.com/) * [Scaleway](https://www.scaleway.com/en/) * [Upcloud](https://upcloud.com/) * [Vultr](https://www.vultr.com/) ### etcd Advertised Address The address advertised by etcd can now be controlled with new machine configuration option `machine.etcd.subnet`. ### Reboots via kexec Talos now reboots by default via kexec syscall which means BIOS POST process is skipped. On bare-metal hardware BIOS POST process might take 10-15 minutes, so Talos reboots 10-15 minutes faster on bare-metal. Kexec support can be disabled with the following change to the machine configuration: ``` machine: sysctls: kernel.kexec_load_disabled: "1" ``` ### Cluster Discovery and KubeSpan This release of Talos provides initial support for cluster membership discovery and [KubeSpan](https://www.talos-systems.com/kubespan/). These new features are not enabled by default, to enable them please make following changes to the machine configuration: ```yaml machine: network: kubespan: enabled: true cluster: discovery: enabled: true ``` ### Windows Support CLI tool talosctl is now built for Windows and published as part of the release. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Serge Logvinov * Andrew Rynhard * Olli Janatuinen * Andrey Smirnov * Lennard Klein * Rui Lopes * Spencer Smith ### Changes
74 commits

* [`9d803d75`](https://github.com/talos-systems/talos/commit/9d803d75bfbe788fa5c2ef2ae0639de31e172c7b) chore: bump dependencies and drop firecracker support * [`50a24104`](https://github.com/talos-systems/talos/commit/50a24104820e26bb99e66ab68be2bd9a6c17b0be) feat: add operating system version field to discovery * [`085c61b2`](https://github.com/talos-systems/talos/commit/085c61b2ec432c586daa77464910e967a223ebe0) chore: add a special condition to check for kubeconfig readiness * [`21cdd854`](https://github.com/talos-systems/talos/commit/21cdd854036498fbeb9f6e4d058a0edd55ed4856) fix: add node address to the list of allowed IPs (kubespan) * [`fdd80a12`](https://github.com/talos-systems/talos/commit/fdd80a1234dc993cc01daa7764ba5a9db2fdc275) feat: add an option to continue booting on NTP timeout * [`ef368498`](https://github.com/talos-systems/talos/commit/ef36849899b18bbb35c6116fdf35aa580a50a5e5) feat: add routes, routing rules and nftables rules for KubeSpan * [`ed12379f`](https://github.com/talos-systems/talos/commit/ed12379f2f49fcbca84080f1066cf52dc202bd2d) fix: patch multi nodes support * [`d943bb0e`](https://github.com/talos-systems/talos/commit/d943bb0e280e90f3592d9f7b67813b7a15818c84) feat: update Kubernetes to 1.22.2 * [`d0585fb6`](https://github.com/talos-systems/talos/commit/d0585fb6b303dfdd7fc80a76024915df31c72389) feat: reboot via kexec * [`3de505c8`](https://github.com/talos-systems/talos/commit/3de505c894274bfd5248b6c597f6e3a53f873ba1) fix: skip bad cloud-config in OpenStack platform * [`a394d1e2`](https://github.com/talos-systems/talos/commit/a394d1e20ba82de7d05e4d3f91823a98362ac9ee) fix: tear down control plane static pods when etcd is stopped * [`1c05089b`](https://github.com/talos-systems/talos/commit/1c05089bb22c7c1050e95cf8d7bea8b763a0e86f) feat: implement KubeSpan manager for Wireguard peer state * [`ec7f44ef`](https://github.com/talos-systems/talos/commit/ec7f44efe4f89e7ed207cbd5fe3748953ccfdf28) fix: completely prevent editing resources other than mc * [`19a8ae97`](https://github.com/talos-systems/talos/commit/19a8ae97c69949f7c2421154b2ae4e52a905ff63) feat: add vultr.com cloud support * [`0ff4c7cd`](https://github.com/talos-systems/talos/commit/0ff4c7cdb2b9505823f4c4504ec9bf4d7fddf5c5) fix: write KubernetesCACert chmodded 0400 instead of 0500 * [`a1c9d649`](https://github.com/talos-systems/talos/commit/a1c9d64907cce75bcb566f3ee394734e29b3932d) fix: update the way results are retrieved for certified conformance * [`a0594540`](https://github.com/talos-systems/talos/commit/a0594540451a7636f8cd4bbe835913d31f66d0de) chore: build using Go 1.17 * [`7c5045bd`](https://github.com/talos-systems/talos/commit/7c5045bd929fcf5028cae3840970e692ef3bc7c9) release(v0.13.0-alpha.0): prepare release * [`ee2dce6c`](https://github.com/talos-systems/talos/commit/ee2dce6c1a0e8838e587a9136afd1b7381000432) chore: bump dependencies * [`ef022959`](https://github.com/talos-systems/talos/commit/ef022959280f156d6311836ef9cc2d01e5e3ae7d) fix: print etcd member ID in hex * [`5ca1fb82`](https://github.com/talos-systems/talos/commit/5ca1fb822125483be290e79d8828bba246fda51c) fix: multiple fixes for KubeSpan and Wireguard implementation * [`b1bd6425`](https://github.com/talos-systems/talos/commit/b1bd64250820df3fcb5214368ce9c8cf4634970a) fix: build platform images * [`3b5f4038`](https://github.com/talos-systems/talos/commit/3b5f4038de2f855b3b634e4abb1c564da624e2fc) feat: add scaleway.com cloud support * [`f156ab18`](https://github.com/talos-systems/talos/commit/f156ab1847f2ad1ca2a2548b299a713ee5fe0fcd) feat: add upcloud.com cloud support * [`c3b2429c`](https://github.com/talos-systems/talos/commit/c3b2429ce91edc4f8f9e720a4b144bc941046fc3) fix: suppress spurious Kubernetes API server cert updates * [`ff90b575`](https://github.com/talos-systems/talos/commit/ff90b5751e17a60fc6ca4274f35da7ddcca44fea) feat: implement KubeSpan peer generation controller * [`14c69df5`](https://github.com/talos-systems/talos/commit/14c69df5063e71765b9316ae37657fda2388c60e) fix: correctly parse multiple pod/service CIDRs * [`69897dbb`](https://github.com/talos-systems/talos/commit/69897dbba402812403c0c15d6cb8d2a771ea5a88) feat: drop some capabilities to be never available * [`51e9836b`](https://github.com/talos-systems/talos/commit/51e9836b01926d1619d662e6e08df29210ff94e5) docs: promote 0.12 docs to be the latest * [`812d59c7`](https://github.com/talos-systems/talos/commit/812d59c70085b54136e3b56127b0efea7ddb60af) feat: add hetzner.com cloud support * [`d53e9e89`](https://github.com/talos-systems/talos/commit/d53e9e89633258d85c2232b85855535ebb42c417) chore: use named constants * [`2dfe7f1f`](https://github.com/talos-systems/talos/commit/2dfe7f1fc654c8bec83b632a98dbaa8d1b90a521) chore: bump tools to the latest version * [`82b130e7`](https://github.com/talos-systems/talos/commit/82b130e789aa4376e1f0e2d086233e630b410f74) docs: document required options for extraMounts * [`af662210`](https://github.com/talos-systems/talos/commit/af6622109faecdf03aed43b047035904110c7580) feat: implement Kubernetes cluster discovery registry * [`2c66e1b3`](https://github.com/talos-systems/talos/commit/2c66e1b3c5d4c34c5d4cdc155c32f2808a5f1c69) feat: provide building of local `Affiliate` structure (for the node) * [`d69bd2af`](https://github.com/talos-systems/talos/commit/d69bd2af3e3d3bf12b6d74078e9eedf3dc8752fc) chore: enable GPG identity check for Talos * [`8dbd851f`](https://github.com/talos-systems/talos/commit/8dbd851fde3febb5999df694a079121b43519aa9) chore: update tools/pkgs/extras to the new version * [`0b347570`](https://github.com/talos-systems/talos/commit/0b347570a7aca0a133d6b6e6cc8d3e0355630480) feat: use dynamic NodeAddresses/HostnameStatus in Kubernetes certs * [`bd5b9c96`](https://github.com/talos-systems/talos/commit/bd5b9c96e2563249a5633433703493b292b83ee9) fix: correctly define example for `extraMounts` * [`01cca099`](https://github.com/talos-systems/talos/commit/01cca099f40ec75d1e047a84c89692eb254e8adf) docs: update docs for Talos 0.12 release * [`668627d5`](https://github.com/talos-systems/talos/commit/668627d5b8ec79ec955eb1254732b1cc031d3aec) feat: add subnet filter for etcd address * [`3c3c281b`](https://github.com/talos-systems/talos/commit/3c3c281bff8481f680feca9cf01af413a38e6973) chore: bump dependencies via dependabot * [`f8bebba2`](https://github.com/talos-systems/talos/commit/f8bebba2de3999b7a36fecb2d6b90e583372c98f) fix: ignore error on duplicate for `MountStatus` * [`6956edd0`](https://github.com/talos-systems/talos/commit/6956edd0bfae6c6c5d6eba00a22bc3a4cb7f54ea) feat: add node address filters, filter out k8s addresses for Talos API * [`caee24bf`](https://github.com/talos-systems/talos/commit/caee24bf61136daecb095991a6e439f7fbf40da2) feat: implement KubeSpan identity controller * [`da0f6e7e`](https://github.com/talos-systems/talos/commit/da0f6e7e1d295dce0c44c1854363528a6ffedde1) fix: allow updating diskSelector option * [`761ccaf3`](https://github.com/talos-systems/talos/commit/761ccaf32348d8664eb0d5d1a51f6abb19ca52a6) feat: provide machine configuration for KubeSpan and cluster discovery * [`a81e30cb`](https://github.com/talos-systems/talos/commit/a81e30cb46326fbdd433f37dc37549b588a2bc7a) docs: add bootstrap command to VMware docs * [`97da354c`](https://github.com/talos-systems/talos/commit/97da354cc0e4a965e14b8939c426150d5c12f228) fix: do not panic on invalid machine configs * [`c4048e26`](https://github.com/talos-systems/talos/commit/c4048e263d22682142f12fc4af6ac58c679273f0) fix: don't extract nil IPs in the GCP platform * [`ba169c6f`](https://github.com/talos-systems/talos/commit/ba169c6f91948cf057251236fa7a727a05253639) feat: provide talosctl.exe for Windows * [`6312f473`](https://github.com/talos-systems/talos/commit/6312f473e63df50287e6801c079242e2311a23e6) fix: properly handle omitempty fields in the validator * [`7f22879a`](https://github.com/talos-systems/talos/commit/7f22879af0882af4cdebe9c84afb96ae68eb9f20) feat: provide random node identity * [`032e7c6b`](https://github.com/talos-systems/talos/commit/032e7c6b863b5ca02cfa16df79c88950544dbffb) chore: import yaml.v3 consistently * [`80b5f0e7`](https://github.com/talos-systems/talos/commit/80b5f0e7f78f09a11ed249f9f1dc7b05ea275ab0) fix: validate IP address returned as HTTP response in platform code * [`c9af8f7f`](https://github.com/talos-systems/talos/commit/c9af8f7ff17facc18f10675879ed04982a000f6f) docs: fork docs for 0.13 * [`85cda1b9`](https://github.com/talos-systems/talos/commit/85cda1b956b042ba20696637248999d46f63ccc9) feat: provide MountStatus resource for system partition mounts * [`950f122c`](https://github.com/talos-systems/talos/commit/950f122c95e225858e77083f2490481ed8d21aef) chore: update versions in upgrade tests * [`83fdb772`](https://github.com/talos-systems/talos/commit/83fdb7721f45aa075898caf05a4b6856d3c5f330) feat: provide first NIC hardware addr as a resource * [`5f5ac12f`](https://github.com/talos-systems/talos/commit/5f5ac12f1dc8aeb3a8598e57d965471e93fe3724) fix: properly case the VMware name * [`0a6048f4`](https://github.com/talos-systems/talos/commit/0a6048f469da02efad7e84eb237e6fdeb85b7e33) fix: don't allow bootstrap if etcd data directory is not empty * [`e24b93b4`](https://github.com/talos-systems/talos/commit/e24b93b4e120448f37109599f3e9eb15954b147a) fix: cgroup delegate * [`751f64f9`](https://github.com/talos-systems/talos/commit/751f64f9bc10e9ad8508ade9e3a6a14aaaa54d57) docs: add release notes for 0.12, support matrix * [`57a77696`](https://github.com/talos-systems/talos/commit/57a77696ef2b255a59ee4ed213a1a3971a5e2943) feat: update Kubernetes to 1.22.1 * [`244b08cc`](https://github.com/talos-systems/talos/commit/244b08cc198a8ba676bb9acadcbdd23a161b0876) chore: bump dependencies * [`576ba195`](https://github.com/talos-systems/talos/commit/576ba195784abf275256c861d5f811ab1f7b1102) fix: do not set KSPP kernel params in container mode * [`b8c92ede`](https://github.com/talos-systems/talos/commit/b8c92ede52ed515dba68abf4fb1cc6494d510827) fix: don't support cgroups nesting in process runner * [`9bb0b797`](https://github.com/talos-systems/talos/commit/9bb0b79709a502ab49ea9bacd7e54617554d4cc3) test: adapt tests to the cgroupsv2 * [`1abc12be`](https://github.com/talos-systems/talos/commit/1abc12be13208ad1da03492a1b88d2c1ec0d5d33) fix: extramount should have `yaml:",inline"` tag * [`2b614e43`](https://github.com/talos-systems/talos/commit/2b614e430e478cc111db018996ab2c8f763e4f92) feat: check if cluster has deprecated resources versions * [`0b86edab`](https://github.com/talos-systems/talos/commit/0b86edab80cf4dd01f330d7721b130f5017d84a5) fix: don't panic if the machine config doesn't have network (EM) * [`8bef41e4`](https://github.com/talos-systems/talos/commit/8bef41e4bacc4190976657ae5021afecd2d6e001) fix: make sure file mode is same (reproducibility issue) * [`fcfca55a`](https://github.com/talos-systems/talos/commit/fcfca55a059e92fcda198baa321c4c63bda1f0a4) chore: do not check that go mod tidy gives empty output * [`5ce92ca5`](https://github.com/talos-systems/talos/commit/5ce92ca5163616fcd7abe16c4efc3a100953b246) docs: ensure azure VMs are 0 indexed

### Changes since v0.13.0-alpha.0
17 commits

* [`9d803d75`](https://github.com/talos-systems/talos/commit/9d803d75bfbe788fa5c2ef2ae0639de31e172c7b) chore: bump dependencies and drop firecracker support * [`50a24104`](https://github.com/talos-systems/talos/commit/50a24104820e26bb99e66ab68be2bd9a6c17b0be) feat: add operating system version field to discovery * [`085c61b2`](https://github.com/talos-systems/talos/commit/085c61b2ec432c586daa77464910e967a223ebe0) chore: add a special condition to check for kubeconfig readiness * [`21cdd854`](https://github.com/talos-systems/talos/commit/21cdd854036498fbeb9f6e4d058a0edd55ed4856) fix: add node address to the list of allowed IPs (kubespan) * [`fdd80a12`](https://github.com/talos-systems/talos/commit/fdd80a1234dc993cc01daa7764ba5a9db2fdc275) feat: add an option to continue booting on NTP timeout * [`ef368498`](https://github.com/talos-systems/talos/commit/ef36849899b18bbb35c6116fdf35aa580a50a5e5) feat: add routes, routing rules and nftables rules for KubeSpan * [`ed12379f`](https://github.com/talos-systems/talos/commit/ed12379f2f49fcbca84080f1066cf52dc202bd2d) fix: patch multi nodes support * [`d943bb0e`](https://github.com/talos-systems/talos/commit/d943bb0e280e90f3592d9f7b67813b7a15818c84) feat: update Kubernetes to 1.22.2 * [`d0585fb6`](https://github.com/talos-systems/talos/commit/d0585fb6b303dfdd7fc80a76024915df31c72389) feat: reboot via kexec * [`3de505c8`](https://github.com/talos-systems/talos/commit/3de505c894274bfd5248b6c597f6e3a53f873ba1) fix: skip bad cloud-config in OpenStack platform * [`a394d1e2`](https://github.com/talos-systems/talos/commit/a394d1e20ba82de7d05e4d3f91823a98362ac9ee) fix: tear down control plane static pods when etcd is stopped * [`1c05089b`](https://github.com/talos-systems/talos/commit/1c05089bb22c7c1050e95cf8d7bea8b763a0e86f) feat: implement KubeSpan manager for Wireguard peer state * [`ec7f44ef`](https://github.com/talos-systems/talos/commit/ec7f44efe4f89e7ed207cbd5fe3748953ccfdf28) fix: completely prevent editing resources other than mc * [`19a8ae97`](https://github.com/talos-systems/talos/commit/19a8ae97c69949f7c2421154b2ae4e52a905ff63) feat: add vultr.com cloud support * [`0ff4c7cd`](https://github.com/talos-systems/talos/commit/0ff4c7cdb2b9505823f4c4504ec9bf4d7fddf5c5) fix: write KubernetesCACert chmodded 0400 instead of 0500 * [`a1c9d649`](https://github.com/talos-systems/talos/commit/a1c9d64907cce75bcb566f3ee394734e29b3932d) fix: update the way results are retrieved for certified conformance * [`a0594540`](https://github.com/talos-systems/talos/commit/a0594540451a7636f8cd4bbe835913d31f66d0de) chore: build using Go 1.17

### Changes from talos-systems/extras
1 commit

* [`52b27da`](https://github.com/talos-systems/extras/commit/52b27dad5aeeb5d14225a99e4b5902614c993022) chore: update pkgs and tools to 0.8.0-alpha.0

### Changes from talos-systems/go-blockdevice
1 commit

* [`d981156`](https://github.com/talos-systems/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows

### Changes from talos-systems/pkgs
6 commits

* [`db90f93`](https://github.com/talos-systems/pkgs/commit/db90f93c0b462dcaefa081c18f8adebae5d5744a) chore: update tools * [`ca38c59`](https://github.com/talos-systems/pkgs/commit/ca38c599b71334d5a108021c7e7ccec12701ff18) feat: enable KEXEC_FILE_LOAD in the kernel * [`982bc18`](https://github.com/talos-systems/pkgs/commit/982bc18a4ecf5d4e15a73c350abb97f02adb8871) chore: update tools * [`a243ab8`](https://github.com/talos-systems/pkgs/commit/a243ab8a9345b8bc39cc65254015b6eb07605f61) feat: add /usr/src to FHS * [`428abdb`](https://github.com/talos-systems/pkgs/commit/428abdbfd303fce69cf583f5a8a4f5ed43253807) chore: support builds with HTTP_PROXY * [`13151c5`](https://github.com/talos-systems/pkgs/commit/13151c59b5b29541ed5828aa9c75a061ec920ff1) chore: update bldr version, update tools

### Changes from talos-systems/tools
5 commits

* [`2790b55`](https://github.com/talos-systems/tools/commit/2790b5586e810c7dfc0a197ef9d1e6d77a646e3b) feat: update Go to 1.17.1 * [`5b9d214`](https://github.com/talos-systems/tools/commit/5b9d214c38515a55232ce36591036748fd8c49cc) fix: restore static library for ncurses * [`01104e5`](https://github.com/talos-systems/tools/commit/01104e562efdbff34fb2d597d4cf27d04ba44ea6) chore: reproducible builds * [`53fe146`](https://github.com/talos-systems/tools/commit/53fe146ca8ba55c959fee04302a5ce215a927f1d) chore: update bldr with new version * [`bf4540d`](https://github.com/talos-systems/tools/commit/bf4540d0ed0728cd7751e0c3ab3bb4b8927e334c) chore: add patch dependency

### Dependency Changes * **github.com/containerd/go-cni** v1.0.2 -> v1.1.0 * **github.com/containernetworking/cni** v0.8.1 -> v1.0.1 * **github.com/containernetworking/plugins** v0.9.1 -> v1.0.1 * **github.com/cosi-project/runtime** 25f235cd0682 -> 5cb7f5002d77 * **github.com/fsnotify/fsnotify** v1.4.9 -> v1.5.1 * **github.com/gdamore/tcell/v2** v2.4.0 -> f057f0a857a1 * **github.com/google/nftables** 16a134723a96 **_new_** * **github.com/hashicorp/go-getter** v1.5.7 -> v1.5.8 * **github.com/insomniacslk/dhcp** 1cac67f12b1e -> b95caade3eac * **github.com/jsimonetti/rtnetlink** 9c52e516c709 -> 4cc3c1489576 * **github.com/jxskiss/base62** 4f11678b909b **_new_** * **github.com/mattn/go-isatty** v0.0.13 -> v0.0.14 * **github.com/mdlayher/netx** 669a06fde734 **_new_** * **github.com/packethost/packngo** v0.19.0 -> v0.19.1 * **github.com/prometheus/procfs** v0.7.2 -> v0.7.3 * **github.com/rivo/tview** 29d673af0ce2 -> f7430b878d17 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.7 **_new_** * **github.com/talos-systems/extras** v0.5.0 -> v0.6.0-alpha.0 * **github.com/talos-systems/go-blockdevice** v0.2.3 -> d9811569588b * **github.com/talos-systems/pkgs** v0.7.0 -> v0.8.0-alpha.0-3-gdb90f93 * **github.com/talos-systems/tools** v0.7.0-1-ga33ccc1 -> v0.8.0-alpha.0-3-g2790b55 * **github.com/vishvananda/netlink** f5de75959ad5 **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.53.1 -> v0.53.2 * **github.com/vmware/govmomi** v0.26.0 -> v0.26.1 * **github.com/vultr/metadata** v1.0.3 **_new_** * **go.uber.org/zap** v1.19.0 -> v1.19.1 * **golang.org/x/net** 853a461950ff -> 978cfadd31cf * **golang.org/x/sys** 0f9fa26af87c -> d61c044b1678 * **golang.org/x/term** 6886f2dfbf5b -> 140adaaadfaf * **golang.zx2c4.com/wireguard/wgctrl** 92e472f520a5 -> 91d1988e44de * **inet.af/netaddr** ce7a8ad02cc1 -> 85fa6c94624e * **k8s.io/api** v0.22.1 -> v0.22.2 * **k8s.io/apimachinery** v0.22.1 -> v0.22.2 * **k8s.io/client-go** v0.22.1 -> v0.22.2 * **k8s.io/kubectl** v0.22.1 -> v0.22.2 * **k8s.io/kubelet** v0.22.1 -> v0.22.2 * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.58 **_new_** Previous release can be found at [v0.12.0](https://github.com/talos-systems/talos/releases/tag/v0.12.0) ## [Talos 0.13.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.13.0-alpha.0) (2021-09-13) Welcome to the v0.13.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Hetzner, Scaleway and Upcloud Talos now natively supports three new cloud platforms: * [Hetzner](https://www.hetzner.com/) * [Scaleway](https://www.scaleway.com/en/) * [Upcloud](https://upcloud.com/) ### etcd Advertised Address The address advertised by etcd can now be controlled with new machine configuration option `machine.etcd.subnet`. ### Cluster Discovery and KubeSpan This release of Talos provides some initial support for cluster membership discovery and [KubeSpan](https://www.talos-systems.com/kubespan/). These new features are not enabled by default. ### Windows Support CLI tool talosctl is now built for Windows and published as part of the release. ### Contributors * Andrey Smirnov * Artem Chernyshev * Alexey Palazhchenko * Serge Logvinov * Andrew Rynhard * Olli Janatuinen * Andrey Smirnov * Rui Lopes * Spencer Smith ### Changes
55 commits

* [`ef022959`](https://github.com/talos-systems/talos/commit/ef022959280f156d6311836ef9cc2d01e5e3ae7d) fix: print etcd member ID in hex * [`5ca1fb82`](https://github.com/talos-systems/talos/commit/5ca1fb822125483be290e79d8828bba246fda51c) fix: multiple fixes for KubeSpan and Wireguard implementation * [`b1bd6425`](https://github.com/talos-systems/talos/commit/b1bd64250820df3fcb5214368ce9c8cf4634970a) fix: build platform images * [`3b5f4038`](https://github.com/talos-systems/talos/commit/3b5f4038de2f855b3b634e4abb1c564da624e2fc) feat: add scaleway.com cloud support * [`f156ab18`](https://github.com/talos-systems/talos/commit/f156ab1847f2ad1ca2a2548b299a713ee5fe0fcd) feat: add upcloud.com cloud support * [`c3b2429c`](https://github.com/talos-systems/talos/commit/c3b2429ce91edc4f8f9e720a4b144bc941046fc3) fix: suppress spurious Kubernetes API server cert updates * [`ff90b575`](https://github.com/talos-systems/talos/commit/ff90b5751e17a60fc6ca4274f35da7ddcca44fea) feat: implement KubeSpan peer generation controller * [`14c69df5`](https://github.com/talos-systems/talos/commit/14c69df5063e71765b9316ae37657fda2388c60e) fix: correctly parse multiple pod/service CIDRs * [`69897dbb`](https://github.com/talos-systems/talos/commit/69897dbba402812403c0c15d6cb8d2a771ea5a88) feat: drop some capabilities to be never available * [`51e9836b`](https://github.com/talos-systems/talos/commit/51e9836b01926d1619d662e6e08df29210ff94e5) docs: promote 0.12 docs to be the latest * [`812d59c7`](https://github.com/talos-systems/talos/commit/812d59c70085b54136e3b56127b0efea7ddb60af) feat: add hetzner.com cloud support * [`d53e9e89`](https://github.com/talos-systems/talos/commit/d53e9e89633258d85c2232b85855535ebb42c417) chore: use named constants * [`2dfe7f1f`](https://github.com/talos-systems/talos/commit/2dfe7f1fc654c8bec83b632a98dbaa8d1b90a521) chore: bump tools to the latest version * [`82b130e7`](https://github.com/talos-systems/talos/commit/82b130e789aa4376e1f0e2d086233e630b410f74) docs: document required options for extraMounts * [`af662210`](https://github.com/talos-systems/talos/commit/af6622109faecdf03aed43b047035904110c7580) feat: implement Kubernetes cluster discovery registry * [`2c66e1b3`](https://github.com/talos-systems/talos/commit/2c66e1b3c5d4c34c5d4cdc155c32f2808a5f1c69) feat: provide building of local `Affiliate` structure (for the node) * [`d69bd2af`](https://github.com/talos-systems/talos/commit/d69bd2af3e3d3bf12b6d74078e9eedf3dc8752fc) chore: enable GPG identity check for Talos * [`8dbd851f`](https://github.com/talos-systems/talos/commit/8dbd851fde3febb5999df694a079121b43519aa9) chore: update tools/pkgs/extras to the new version * [`0b347570`](https://github.com/talos-systems/talos/commit/0b347570a7aca0a133d6b6e6cc8d3e0355630480) feat: use dynamic NodeAddresses/HostnameStatus in Kubernetes certs * [`bd5b9c96`](https://github.com/talos-systems/talos/commit/bd5b9c96e2563249a5633433703493b292b83ee9) fix: correctly define example for `extraMounts` * [`01cca099`](https://github.com/talos-systems/talos/commit/01cca099f40ec75d1e047a84c89692eb254e8adf) docs: update docs for Talos 0.12 release * [`668627d5`](https://github.com/talos-systems/talos/commit/668627d5b8ec79ec955eb1254732b1cc031d3aec) feat: add subnet filter for etcd address * [`3c3c281b`](https://github.com/talos-systems/talos/commit/3c3c281bff8481f680feca9cf01af413a38e6973) chore: bump dependencies via dependabot * [`f8bebba2`](https://github.com/talos-systems/talos/commit/f8bebba2de3999b7a36fecb2d6b90e583372c98f) fix: ignore error on duplicate for `MountStatus` * [`6956edd0`](https://github.com/talos-systems/talos/commit/6956edd0bfae6c6c5d6eba00a22bc3a4cb7f54ea) feat: add node address filters, filter out k8s addresses for Talos API * [`caee24bf`](https://github.com/talos-systems/talos/commit/caee24bf61136daecb095991a6e439f7fbf40da2) feat: implement KubeSpan identity controller * [`da0f6e7e`](https://github.com/talos-systems/talos/commit/da0f6e7e1d295dce0c44c1854363528a6ffedde1) fix: allow updating diskSelector option * [`761ccaf3`](https://github.com/talos-systems/talos/commit/761ccaf32348d8664eb0d5d1a51f6abb19ca52a6) feat: provide machine configuration for KubeSpan and cluster discovery * [`a81e30cb`](https://github.com/talos-systems/talos/commit/a81e30cb46326fbdd433f37dc37549b588a2bc7a) docs: add bootstrap command to VMware docs * [`97da354c`](https://github.com/talos-systems/talos/commit/97da354cc0e4a965e14b8939c426150d5c12f228) fix: do not panic on invalid machine configs * [`c4048e26`](https://github.com/talos-systems/talos/commit/c4048e263d22682142f12fc4af6ac58c679273f0) fix: don't extract nil IPs in the GCP platform * [`ba169c6f`](https://github.com/talos-systems/talos/commit/ba169c6f91948cf057251236fa7a727a05253639) feat: provide talosctl.exe for Windows * [`6312f473`](https://github.com/talos-systems/talos/commit/6312f473e63df50287e6801c079242e2311a23e6) fix: properly handle omitempty fields in the validator * [`7f22879a`](https://github.com/talos-systems/talos/commit/7f22879af0882af4cdebe9c84afb96ae68eb9f20) feat: provide random node identity * [`032e7c6b`](https://github.com/talos-systems/talos/commit/032e7c6b863b5ca02cfa16df79c88950544dbffb) chore: import yaml.v3 consistently * [`80b5f0e7`](https://github.com/talos-systems/talos/commit/80b5f0e7f78f09a11ed249f9f1dc7b05ea275ab0) fix: validate IP address returned as HTTP response in platform code * [`c9af8f7f`](https://github.com/talos-systems/talos/commit/c9af8f7ff17facc18f10675879ed04982a000f6f) docs: fork docs for 0.13 * [`85cda1b9`](https://github.com/talos-systems/talos/commit/85cda1b956b042ba20696637248999d46f63ccc9) feat: provide MountStatus resource for system partition mounts * [`950f122c`](https://github.com/talos-systems/talos/commit/950f122c95e225858e77083f2490481ed8d21aef) chore: update versions in upgrade tests * [`83fdb772`](https://github.com/talos-systems/talos/commit/83fdb7721f45aa075898caf05a4b6856d3c5f330) feat: provide first NIC hardware addr as a resource * [`5f5ac12f`](https://github.com/talos-systems/talos/commit/5f5ac12f1dc8aeb3a8598e57d965471e93fe3724) fix: properly case the VMware name * [`0a6048f4`](https://github.com/talos-systems/talos/commit/0a6048f469da02efad7e84eb237e6fdeb85b7e33) fix: don't allow bootstrap if etcd data directory is not empty * [`e24b93b4`](https://github.com/talos-systems/talos/commit/e24b93b4e120448f37109599f3e9eb15954b147a) fix: cgroup delegate * [`751f64f9`](https://github.com/talos-systems/talos/commit/751f64f9bc10e9ad8508ade9e3a6a14aaaa54d57) docs: add release notes for 0.12, support matrix * [`57a77696`](https://github.com/talos-systems/talos/commit/57a77696ef2b255a59ee4ed213a1a3971a5e2943) feat: update Kubernetes to 1.22.1 * [`244b08cc`](https://github.com/talos-systems/talos/commit/244b08cc198a8ba676bb9acadcbdd23a161b0876) chore: bump dependencies * [`576ba195`](https://github.com/talos-systems/talos/commit/576ba195784abf275256c861d5f811ab1f7b1102) fix: do not set KSPP kernel params in container mode * [`b8c92ede`](https://github.com/talos-systems/talos/commit/b8c92ede52ed515dba68abf4fb1cc6494d510827) fix: don't support cgroups nesting in process runner * [`9bb0b797`](https://github.com/talos-systems/talos/commit/9bb0b79709a502ab49ea9bacd7e54617554d4cc3) test: adapt tests to the cgroupsv2 * [`1abc12be`](https://github.com/talos-systems/talos/commit/1abc12be13208ad1da03492a1b88d2c1ec0d5d33) fix: extramount should have `yaml:",inline"` tag * [`2b614e43`](https://github.com/talos-systems/talos/commit/2b614e430e478cc111db018996ab2c8f763e4f92) feat: check if cluster has deprecated resources versions * [`0b86edab`](https://github.com/talos-systems/talos/commit/0b86edab80cf4dd01f330d7721b130f5017d84a5) fix: don't panic if the machine config doesn't have network (EM) * [`8bef41e4`](https://github.com/talos-systems/talos/commit/8bef41e4bacc4190976657ae5021afecd2d6e001) fix: make sure file mode is same (reproducibility issue) * [`fcfca55a`](https://github.com/talos-systems/talos/commit/fcfca55a059e92fcda198baa321c4c63bda1f0a4) chore: do not check that go mod tidy gives empty output * [`5ce92ca5`](https://github.com/talos-systems/talos/commit/5ce92ca5163616fcd7abe16c4efc3a100953b246) docs: ensure azure VMs are 0 indexed

### Changes from talos-systems/extras
1 commit

* [`52b27da`](https://github.com/talos-systems/extras/commit/52b27dad5aeeb5d14225a99e4b5902614c993022) chore: update pkgs and tools to 0.8.0-alpha.0

### Changes from talos-systems/go-blockdevice
1 commit

* [`d981156`](https://github.com/talos-systems/go-blockdevice/commit/d9811569588ba44be878a00ce316f59a37abed8b) fix: allow Build for Windows

### Changes from talos-systems/pkgs
3 commits

* [`a243ab8`](https://github.com/talos-systems/pkgs/commit/a243ab8a9345b8bc39cc65254015b6eb07605f61) feat: add /usr/src to FHS * [`428abdb`](https://github.com/talos-systems/pkgs/commit/428abdbfd303fce69cf583f5a8a4f5ed43253807) chore: support builds with HTTP_PROXY * [`13151c5`](https://github.com/talos-systems/pkgs/commit/13151c59b5b29541ed5828aa9c75a061ec920ff1) chore: update bldr version, update tools

### Changes from talos-systems/tools
4 commits

* [`5b9d214`](https://github.com/talos-systems/tools/commit/5b9d214c38515a55232ce36591036748fd8c49cc) fix: restore static library for ncurses * [`01104e5`](https://github.com/talos-systems/tools/commit/01104e562efdbff34fb2d597d4cf27d04ba44ea6) chore: reproducible builds * [`53fe146`](https://github.com/talos-systems/tools/commit/53fe146ca8ba55c959fee04302a5ce215a927f1d) chore: update bldr with new version * [`bf4540d`](https://github.com/talos-systems/tools/commit/bf4540d0ed0728cd7751e0c3ab3bb4b8927e334c) chore: add patch dependency

### Dependency Changes * **github.com/cosi-project/runtime** 25f235cd0682 -> 57b048cd66b0 * **github.com/fsnotify/fsnotify** v1.4.9 -> v1.5.1 * **github.com/insomniacslk/dhcp** 1cac67f12b1e -> d82598001386 * **github.com/jxskiss/base62** 4f11678b909b **_new_** * **github.com/mdlayher/netx** 669a06fde734 **_new_** * **github.com/prometheus/procfs** v0.7.2 -> v0.7.3 * **github.com/scaleway/scaleway-sdk-go** v1.0.0-beta.7 **_new_** * **github.com/talos-systems/extras** v0.5.0 -> v0.6.0-alpha.0 * **github.com/talos-systems/go-blockdevice** v0.2.3 -> d9811569588b * **github.com/talos-systems/pkgs** v0.7.0 -> v0.8.0-alpha.0 * **github.com/talos-systems/tools** v0.7.0-1-ga33ccc1 -> v0.8.0-alpha.0-2-g5b9d214 * **github.com/vmware-tanzu/sonobuoy** v0.53.1 -> v0.53.2 * **github.com/vmware/govmomi** v0.26.0 -> v0.26.1 * **golang.org/x/net** 853a461950ff -> 60bc85c4be6d * **golang.org/x/sys** 0f9fa26af87c -> 63515b42dcdf * **kernel.org/pub/linux/libs/security/libcap/cap** v1.2.56 **_new_** Previous release can be found at [v0.12.0](https://github.com/talos-systems/talos/releases/tag/v0.12.0) ## [Talos 0.12.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.12.0-alpha.1) (2021-08-13) Welcome to the v0.12.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Support for Self-hosted Control Plane Dropped > **Note**: This item only applies to clusters bootstrapped with Talos <= 0.8. Talos 0.12 completely removes support for self-hosted Kubernetes control plane (bootkube-based). Talos 0.9 introduced support for Talos-managed control plane and provided migration path to convert self-hosted control plane to Talos-managed static pods. Automated and manual conversion process is available in Talos from 0.9.x to 0.11.x. For clusters bootstrapped with bootkube (Talos <= 0.8), please make sure control plane is converted to Talos-managed before before upgrading to Talos 0.12. Current control plane status can be checked with `talosctl get bootstrapstatus` before performing upgrade to Talos 0.12. ### Cluster API v0.3.x Cluster API v0.3.x (v1alpha3) is not compatible with Kubernetes 1.22 used by default in Talos 0.12. Talos can be configued to use Kubernetes 1.21 or CAPI v0.4.x components can be used instead. ### Machine Config Validation Unknown keys in the machine config now make the config invalid, so any attempt to apply/edit the configuration with the unknown keys will lead into an error. ### Sysctl Configuration Sysctl Kernel Params configuration was completely rewritten to be based on controllers and resources, which makes it possible to apply `.machine.sysctls` in immediate mode (without a reboot). `talosctl get kernelparams` returns merged list of KSPP, Kubernetes and user defined params along with the default values overwritten by Talos. ### Equinix Metal Added support for Equinix Metal IPs for the Talos virtual (shared) IP (option `equnixMetal` under `vip` in the machine configuration). Talos automatically re-assigns IP using the Equinix Metal API when leadership changes. ### etcd New etcd cluster members are now joined in [learner mode](https://etcd.io/docs/v3.4/learning/design-learner/), which improves cluster resiliency to member join issues. ### Join Node Type Node type `join` was renamed to `worker` for clarity. The old value is still accepted in the machine configuration but deprecated. `talosctl gen config` now generates `worker.yaml` instead of `join.yaml`. ### Networking * multiple static addresses can be specified for the interface with new `.addresses` field (old `.cidr` field is deprecated now) * static addresses can be set on interfaces configured with DHCP ### Performance * machined uses less memory and CPU time * more disk encryption options are exposed via the machine configuration * disk partitions are now aligned properly with minimum I/O size * Talos system processes are moved under proper cgroups, resource metrics are now available via the kubelet * OOM score is set on the system processes making sure they are killed last under memory pressure ### Security * etcd PKI moved to `/system/secrets` * kubelet bootstrap CSR auto-signing scoped to kubelet bootstrap tokens only * enforce default seccomp profile on all system containers * run system services apid, trustd, and etcd as non-root users ### Component Updates * Linux: 5.10.57 * Kubernetes: 1.22.0 * containerd: 1.5.5 * runc: 1.0.1 * GRUB: 2.06 * Talos is built with Go 1.16.7 ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Andrey Smirnov * Serge Logvinov * Artem Chernyshev * Spencer Smith * Alexey Palazhchenko * dependabot[bot] * Andrew Rynhard * Artem Chernyshev * Rui Lopes * Caleb Woodbine * Seán C McCord ### Changes
109 commits

* [`1ed5e545`](https://github.com/talos-systems/talos/commit/1ed5e545385e160fe3b61e6dbbcaa8a701437b62) feat: add ClusterID and ClusterSecret * [`228b3761`](https://github.com/talos-systems/talos/commit/228b376163597cd825e4a142e6b4bdea0f870365) chore: run etcd as non-root user * [`3518219b`](https://github.com/talos-systems/talos/commit/3518219bff44f71a60ad8e448e518844d1b933fd) chore: drop deprecated `--no-reboot` param and KernelCurrentRoot const * [`33d1c3e4`](https://github.com/talos-systems/talos/commit/33d1c3e42582649f25a44fc3c86007bcebbc80b3) chore: run apid and trustd services as non-root user * [`dadaa65d`](https://github.com/talos-systems/talos/commit/dadaa65d542171d25317840fcf35fa3979cf0632) feat: print uid/gid for the files in `ls -l` * [`e6fa401b`](https://github.com/talos-systems/talos/commit/e6fa401b663d0ebd4374c9e47a7ca6150a4756cd) fix: enable seccomp default profile by default * [`8ddbcc96`](https://github.com/talos-systems/talos/commit/8ddbcc9643113c15de538fc070b7053d1c6efdfc) feat: validate if extra fields present in the decoder * [`5b57a980`](https://github.com/talos-systems/talos/commit/5b57a98008c64d7cb07729fd9b31a0e3493c289c) chore: update Go to 1.16.7, Linux to 5.10.57 * [`eefe1c21`](https://github.com/talos-systems/talos/commit/eefe1c21c30fa2cd281fc5524b2e88553f6fdfcc) feat: add new etcd members in learner mode * [`b1c66fba`](https://github.com/talos-systems/talos/commit/b1c66fbad113400729cf4db806e30192bf7e0462) feat: implement Equinix Metal support for virtual (shared) IP * [`62242f97`](https://github.com/talos-systems/talos/commit/62242f979e1921ed8abfa06a26564ea0bf8a5fb3) chore: require GPG signatures * [`faecae44`](https://github.com/talos-systems/talos/commit/faecae44fde60fc626ccb01da3b221519a9d41d7) feat: make ISO builds reproducible * [`887c2326`](https://github.com/talos-systems/talos/commit/887c2326a4f81c846e3aa3bd1787bc840877e494) release(v0.12.0-alpha.0): prepare release * [`a15f0184`](https://github.com/talos-systems/talos/commit/a15f01844fdaf0d3e2dad2750d9353d03e18dea2) fix: move etcd PKI under /system/secrets * [`eb02afe1`](https://github.com/talos-systems/talos/commit/eb02afe18be63bf483a0467f655611561aef10f6) fix: match correctly routes on the address family * [`cb948acc`](https://github.com/talos-systems/talos/commit/cb948accfeca13c57b3b512dc8a06425989294f9) feat: allow multiple addresses per interface * [`e030b2e8`](https://github.com/talos-systems/talos/commit/e030b2e8bb0a65abf4e1f7b5f27348631210ebc4) chore: use k8s 1.21.3 in CAPI tests for now * [`e08b4f8f`](https://github.com/talos-systems/talos/commit/e08b4f8f9e72f8db1116b4bbe395d49b4bccb460) feat: implement sysctl controllers * [`fdf6b243`](https://github.com/talos-systems/talos/commit/fdf6b2433c40613bcb039852a96196dbe9b7b5e2) chore: revert "improve artifacts generation reproducibility" * [`b68ed1eb`](https://github.com/talos-systems/talos/commit/b68ed1eb896039ec1319db2e3d6d364034c86863) fix: make route resources ID match closer routing table primary key * [`585f6337`](https://github.com/talos-systems/talos/commit/585f633710abb7a6d863b54c37aa65c50a3c7312) fix: correctly handle nodoc for struct fields * [`f2d394dc`](https://github.com/talos-systems/talos/commit/f2d394dc42f9ec704050db0a8a928a889483ce3e) docs: add AMIs for v0.11.5 * [`d0970cbf`](https://github.com/talos-systems/talos/commit/d0970cbfd696b28b201b232a03da2119f664afbd) feat: bootstrap token limit * [`5285a46d`](https://github.com/talos-systems/talos/commit/5285a46d78ef2fc76594aad4ad4acb75312bc0a7) fix: maintenance mode reason message * [`009d15e8`](https://github.com/talos-systems/talos/commit/009d15e8dc6e75eca6b5963dddf8063941099f14) chore: use etcd client TryLock function on upgrade * [`4dae9ea5`](https://github.com/talos-systems/talos/commit/4dae9ea55c087c28a9d7a8d241e0ec3a7a1b8ca3) chore: use vtprotobuf compiled marshaling in Talos API * [`7ca5749a`](https://github.com/talos-systems/talos/commit/7ca5749ad4267701ce639d0f0d91c10a7f9c1d3d) chore: bump dependencies via dependabot * [`b2507b41`](https://github.com/talos-systems/talos/commit/b2507b41d250b989b9c13ad23e16202cd53a18d2) chore: improve artifacts generation reproducibility * [`1f7dad23`](https://github.com/talos-systems/talos/commit/1f7dad234b480c7a5e3484ccf10180747c979036) chore: update PKGS version (512 cpus, new ca-certficates) * [`1a2e78a2`](https://github.com/talos-systems/talos/commit/1a2e78a24e997241c4cd18dfac3c2d971ba78116) fix: update go-blockdevice * [`6d6ed117`](https://github.com/talos-systems/talos/commit/6d6ed1170f3f28e7f559ccdf64e7c34dfee022a0) chore: use parallel xz with higher compression level * [`571f7db1`](https://github.com/talos-systems/talos/commit/571f7db1bb44a0dcb5e373f9c37396d50eb0e8f4) chore: workaround GitHub new release notes limit * [`09d70b7e`](https://github.com/talos-systems/talos/commit/09d70b7eafb18343eb4ca57d7f8b84e4ccd2fcfb) feat: update Kubernetes to v1.22.0 * [`f25f10e7`](https://github.com/talos-systems/talos/commit/f25f10e73ec534acd7cc483f254d612d8a7c1858) feat: add an option to disable PSP * [`7c6e4cf2`](https://github.com/talos-systems/talos/commit/7c6e4cf230ba1f30da664374c41c934d1e6620bc) feat: allow both DHCP and static addressing for the interface * [`3c566dbc`](https://github.com/talos-systems/talos/commit/3c566dbc30595467a3789707c6e993aa92f36df6) fix: remove admission plugins enabled by default from the list * [`69ead373`](https://github.com/talos-systems/talos/commit/69ead37353b7e3aa7f089c70073037a6eba37767) fix: preserve PMBR bootable flag correctly * [`dee63051`](https://github.com/talos-systems/talos/commit/dee63051702d49f495bfb28b4be74ed8b39143ad) fix: align partitions with minimal I/O size * [`62890229`](https://github.com/talos-systems/talos/commit/628902297d2efe93e6388377b2ea6d4beda83095) feat: update GRUB to 2.06 * [`b9d04928`](https://github.com/talos-systems/talos/commit/b9d04928d960f9d576671c6f3511cf242ff31cb7) feat: move system processes to cgroups * [`0b8681b4`](https://github.com/talos-systems/talos/commit/0b8681b4b49ab109b8863792d48c2f551d1ceeb5) fix: resolve several issues with Wireguard link specs * [`f8f4bf3b`](https://github.com/talos-systems/talos/commit/f8f4bf3baef31d4ac957ec68cd869adea1e931cd) docs: add disk encryptions examples * [`79b8fa64`](https://github.com/talos-systems/talos/commit/79b8fa64b9453917860faae3df5d14647186b9ba) feat: update containerd to 1.5.5 * [`539f4209`](https://github.com/talos-systems/talos/commit/539f42090e436921a23087296cde6eaf7e495b5e) chore: bump dependencies via dependabot * [`0c7ce1cd`](https://github.com/talos-systems/talos/commit/0c7ce1cd814354213a1a6c7a9251b166ee58c493) feat: remove remnants of bootkube support * [`d4f9804f`](https://github.com/talos-systems/talos/commit/d4f9804f8659562f6152ae73cb1788f6f6d6ad89) chore: fix typos * [`5f027615`](https://github.com/talos-systems/talos/commit/5f027615ffac68e0a484a5da4827a6589bae3880) feat: expose more encryption options to the machine config * [`585152a0`](https://github.com/talos-systems/talos/commit/585152a0be051accd4cb8b7c2f130c5a92dfd32d) chore: bump dependencies * [`fc66ec59`](https://github.com/talos-systems/talos/commit/fc66ec59691fb1b9d00b27e1f7b34c870a09d717) feat: set oom score for main processes * [`df54584a`](https://github.com/talos-systems/talos/commit/df54584a33d88de13deadcb87a5cfa9c1f9b3961) fix: drop linux capabilities * [`f65d0b73`](https://github.com/talos-systems/talos/commit/f65d0b739bd36a57979f9bf26c3092ac544e607c) docs: add 0.11.3 AMIs * [`7332d636`](https://github.com/talos-systems/talos/commit/7332d63695074dd5eef35ad545d48aff857fbde8) fix: bump pkgs for new kernel 5.10.52 * [`70d2505b`](https://github.com/talos-systems/talos/commit/70d2505b7c8807cb5d4f8a017f9f6200757e13e0) fix: do not require ToVersion to be set when detecting version * [`0953b199`](https://github.com/talos-systems/talos/commit/0953b1998579f855adffff4b83db917f26687a7b) chore: update extras to bring a new CNI bundle * [`b6c47f86`](https://github.com/talos-systems/talos/commit/b6c47f866a57bafb60f85fb1ce10428ed3f52c4a) fix: set the /etc/os-release HOME_URL parameter * [`c780821d`](https://github.com/talos-systems/talos/commit/c780821d0b8fda0b3ef6d33b63b595e40970a897) feat: update containerd to 1.5.3, runc to 1.0.1 * [`f8f1c83a`](https://github.com/talos-systems/talos/commit/f8f1c83a757f5a729896174f95f83c6d804d4858) feat: detect the lowest Kubernetes version in upgrade-k8s CLI command * [`55e17ccd`](https://github.com/talos-systems/talos/commit/55e17ccdd1df789466ccfb0c9cfe55a62b437f77) chore: bump dependencies * [`da6f786c`](https://github.com/talos-systems/talos/commit/da6f786cab80cbacb886d34b7c5e0ed957cc24c9) fix: kuberentes => kubernetes typo * [`2e463348`](https://github.com/talos-systems/talos/commit/2e463348b26fb8b36657b8cb6871e4bce8030b0b) fix: pass all logs through the options.Log method * [`4e9c5afb`](https://github.com/talos-systems/talos/commit/4e9c5afb6dd6bdedb4032b7cf4a24b6f1bf88144) fix: make ethtool optional in link status controller * [`bf61c2cc`](https://github.com/talos-systems/talos/commit/bf61c2cc4a51d290fe98aaeb80224bdd52bb7ac5) fix: write upgrade logs only to the LogOutput if it's defined * [`9c73257c`](https://github.com/talos-systems/talos/commit/9c73257cb128a76459b7d4442b56a50feed089d6) feat: update Go to 1.16.6 * [`23ef1d40`](https://github.com/talos-systems/talos/commit/23ef1d40af44b873d60337d691f878e2cfe0fe8d) chore: add ability to redirect talos upgrade module logs to io.Writer * [`33e9d6c9`](https://github.com/talos-systems/talos/commit/33e9d6c984f82af24ad79e002758841935e60a6a) chore: bump github.com/aws/aws-sdk-go in /hack/cloud-image-uploader * [`604434c4`](https://github.com/talos-systems/talos/commit/604434c43eb63aa760cd2176aa1041b653c9bd75) chore: bump github.com/prometheus/procfs from 0.6.0 to 0.7.0 * [`2ea28f62`](https://github.com/talos-systems/talos/commit/2ea28f62d8dcac3280d7a133ae6532f3ca5709cc) chore: bump node from 16.3.0-alpine to 16.4.2-alpine * [`b358a189`](https://github.com/talos-systems/talos/commit/b358a189bcbaa480d1bb3fbcc58eecd1b61f447d) fix: correctly pick route scope for link-local destination * [`6848d431`](https://github.com/talos-systems/talos/commit/6848d431427636e415436cdda95543a9a0da5676) feat: can change clusterdns ip lists * [`72b76abf`](https://github.com/talos-systems/talos/commit/72b76abfd43d04aa7a9283669925bd49498dc05f) fix: workaround issues when IPv6 is fully or partially disabled * [`679b08f4`](https://github.com/talos-systems/talos/commit/679b08f4fabd098311786551e75e38c2a027bd31) docs: update docs for 0.12 * [`6fbec9e0`](https://github.com/talos-systems/talos/commit/6fbec9e0cb656f411cceb986560473b1a40b6a45) fix: cache etcd client used for healthchecks * [`eea750de`](https://github.com/talos-systems/talos/commit/eea750de2c11a9883f343c65a36e30712b987f89) chore: rename "join" type to "worker" * [`951493ac`](https://github.com/talos-systems/talos/commit/951493ac8356a414ff85fce25e30e4bd808b412c) docs: update what's new for Talos 0.11 * [`b47d1098`](https://github.com/talos-systems/talos/commit/b47d1098b1f1cbd21c501266ffc4a38711ed213f) docs: promote 0.11 docs to be the latest * [`d930a265`](https://github.com/talos-systems/talos/commit/d930a26502759cebccb05d9b78741e1fc147b30b) chore: implement DeepCopy for machine configuration * [`fe4ed3c7`](https://github.com/talos-systems/talos/commit/fe4ed3c734e5713b2fa1d639bd80bffc7888d7e7) chore: ignore tags which don't look like semantic version * [`b969e772`](https://github.com/talos-systems/talos/commit/b969e7720ebcb0103e94494533d819a91dba59f5) chore: update references to old protobuf package * [`2ba8ac9a`](https://github.com/talos-systems/talos/commit/2ba8ac9ab4b24572512c2a877acd26b912b5423a) docs: add documentation directory for 0.12 * [`011e2885`](https://github.com/talos-systems/talos/commit/011e2885e7f88a3a92f3f495fdc1d3be6ed0c877) fix: validate bond slaves addressing * [`10c28758`](https://github.com/talos-systems/talos/commit/10c28758a4fc50a5e5a29097769b4a3a92ed249a) fix: ignore DeadlineExceeded error correctly on bootstrap * [`77fabace`](https://github.com/talos-systems/talos/commit/77fabaceca242f89949d4bf231e9754b4d04eb5e) chore: ignore future pkg/machinery/vX.Y.Z tags * [`6b661114`](https://github.com/talos-systems/talos/commit/6b661114d03a7cd1ddd8939ea323d4fe2ce9976c) fix: make COSI runtime history depth smaller * [`9bf899bd`](https://github.com/talos-systems/talos/commit/9bf899bdd852befbb4aa5ac4f3ceecb3c33502c8) fix: make forfeit leadership connect to the right node * [`4708beae`](https://github.com/talos-systems/talos/commit/4708beaee53e3aacbeec07c38cdd2c7316d16a4c) feat: implement `talosctl config info` command * [`6d13d2cf`](https://github.com/talos-systems/talos/commit/6d13d2cf9243adce739673f1982cbc1f12252ef1) fix: close Kubernetes API client * [`aaa36f3b`](https://github.com/talos-systems/talos/commit/aaa36f3b4fb250d2921f35c09bcb01b6c31ad423) fix: ignore 'not a leader' error on forfeit leadership * [`22a41936`](https://github.com/talos-systems/talos/commit/22a4193678d2245b4c24b7e173d4cfd5fa876e95) fix: workaround 'Unauthorized' errors when accessing Kubernetes API * [`71c6f700`](https://github.com/talos-systems/talos/commit/71c6f7004e28c8a72410652d7d38f770bcf8a5f8) chore: bump go.mod dependencies * [`915cd8fe`](https://github.com/talos-systems/talos/commit/915cd8fe20c55112cc1fa7776c115ac85c7f3da9) docs: add guide for RBAC * [`f5721050`](https://github.com/talos-systems/talos/commit/f5721050deffe61f892a9fca2d20b3fccb5021a6) fix: controlplane keyusage * [`3d772661`](https://github.com/talos-systems/talos/commit/3d7726613ca5c5e6b14b4854564d71ee3644d32e) fix: fill uuid argument correctly in the config download URL * [`d8602025`](https://github.com/talos-systems/talos/commit/d8602025c828189fa15350a15bf3ccefe39bd0ce) chore: update containerd config version 2 * [`5949ec4e`](https://github.com/talos-systems/talos/commit/5949ec4e6e05ada904d69a24c9d21e20cc7dea85) docs: describe the new network configuration subsystem * [`444d72b4`](https://github.com/talos-systems/talos/commit/444d72b4d7cff7b38c8e3a483bbe10c74251448a) feat: update pkgs version * [`e883c12b`](https://github.com/talos-systems/talos/commit/e883c12b31e2ddc3860abc04e7c0867701f46026) fix: make output of `upgrade-k8s` command less scary * [`7f8e50de`](https://github.com/talos-systems/talos/commit/7f8e50de4d9a36dae9de7783d71a981fb6a72854) fix: restart the merge controllers on conflict * [`60d73609`](https://github.com/talos-systems/talos/commit/60d7360944ff6fc1e75f98e37a754f3bb2962144) fix: ignore deadline exceeded errors on bootstrap * [`ee06dd69`](https://github.com/talos-systems/talos/commit/ee06dd69fc39d5df720a88991caaf3646c6fa349) fix: don't print git sha of the release twice in the dashboard * [`07fb61e5`](https://github.com/talos-systems/talos/commit/07fb61e5d22da86b434d30f12b84b845ac1a4df7) fix: issue worker apid certs properly on renewal * [`84817f73`](https://github.com/talos-systems/talos/commit/84817f733458cbd35549eebc72df6a5df202b299) chore: bump Talos version in upgrade tests * [`2fa54107`](https://github.com/talos-systems/talos/commit/2fa54107b2c84cabe948ace5d70836dd4be95799) chore: fix tests for disabled RBAC * [`78583ba9`](https://github.com/talos-systems/talos/commit/78583ba985fa2b90ec610d148b2cbeb0b92d646b) fix: don't set bond delay options if miimon is not enabled * [`bbf1c091`](https://github.com/talos-systems/talos/commit/bbf1c091d4cea0b4610bce7165a98c7572423b01) feat: add RBAC to `talosctl version` output * [`5f6ec3ef`](https://github.com/talos-systems/talos/commit/5f6ec3ef66c8bf2cb334e02b5aa9869330c985d8) fix: handle cases when merged resource re-appears before being destroyed * [`1e9a0e74`](https://github.com/talos-systems/talos/commit/1e9a0e745db73bd45ec0881aa19e43d7badb5914) fix: documentation typos * [`f228af40`](https://github.com/talos-systems/talos/commit/f228af4061e2025531c953fdb7f8bf83de4bf8b0) chore: bump go.mod dependencies * [`2060ceaa`](https://github.com/talos-systems/talos/commit/2060ceaa0b16be04a61a00e0085e25889ffe613a) chore: add CAPI version to CI setup * [`ad047a7d`](https://github.com/talos-systems/talos/commit/ad047a7dee4c0ac26c01862bdaa923fab93cc2e1) chore: small RBAC improvements

### Changes since v0.12.0-alpha.0
12 commits

* [`1ed5e545`](https://github.com/talos-systems/talos/commit/1ed5e545385e160fe3b61e6dbbcaa8a701437b62) feat: add ClusterID and ClusterSecret * [`228b3761`](https://github.com/talos-systems/talos/commit/228b376163597cd825e4a142e6b4bdea0f870365) chore: run etcd as non-root user * [`3518219b`](https://github.com/talos-systems/talos/commit/3518219bff44f71a60ad8e448e518844d1b933fd) chore: drop deprecated `--no-reboot` param and KernelCurrentRoot const * [`33d1c3e4`](https://github.com/talos-systems/talos/commit/33d1c3e42582649f25a44fc3c86007bcebbc80b3) chore: run apid and trustd services as non-root user * [`dadaa65d`](https://github.com/talos-systems/talos/commit/dadaa65d542171d25317840fcf35fa3979cf0632) feat: print uid/gid for the files in `ls -l` * [`e6fa401b`](https://github.com/talos-systems/talos/commit/e6fa401b663d0ebd4374c9e47a7ca6150a4756cd) fix: enable seccomp default profile by default * [`8ddbcc96`](https://github.com/talos-systems/talos/commit/8ddbcc9643113c15de538fc070b7053d1c6efdfc) feat: validate if extra fields present in the decoder * [`5b57a980`](https://github.com/talos-systems/talos/commit/5b57a98008c64d7cb07729fd9b31a0e3493c289c) chore: update Go to 1.16.7, Linux to 5.10.57 * [`eefe1c21`](https://github.com/talos-systems/talos/commit/eefe1c21c30fa2cd281fc5524b2e88553f6fdfcc) feat: add new etcd members in learner mode * [`b1c66fba`](https://github.com/talos-systems/talos/commit/b1c66fbad113400729cf4db806e30192bf7e0462) feat: implement Equinix Metal support for virtual (shared) IP * [`62242f97`](https://github.com/talos-systems/talos/commit/62242f979e1921ed8abfa06a26564ea0bf8a5fb3) chore: require GPG signatures * [`faecae44`](https://github.com/talos-systems/talos/commit/faecae44fde60fc626ccb01da3b221519a9d41d7) feat: make ISO builds reproducible

### Changes from talos-systems/crypto
1 commit

* [`deec8d4`](https://github.com/talos-systems/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types

### Changes from talos-systems/extras
3 commits

* [`8ce17e5`](https://github.com/talos-systems/extras/commit/8ce17e5e5d60dce7b46cf87555400f7951fe9fda) chore: bump tools and packages for Go 1.16.7 * [`4957f3c`](https://github.com/talos-systems/extras/commit/4957f3c64bc5fd1574fe3d3f251f52e914e78e41) chore: update pkgs to use CNI plugins v0.9.1 * [`233716a`](https://github.com/talos-systems/extras/commit/233716a04f1e4e1762101b279308630caa46d17d) feat: update Go to 1.16.6

### Changes from talos-systems/go-blockdevice
4 commits

* [`fe24303`](https://github.com/talos-systems/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations * [`2ec0c3c`](https://github.com/talos-systems/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition * [`87816a8`](https://github.com/talos-systems/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size * [`c34b59f`](https://github.com/talos-systems/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module

### Changes from talos-systems/pkgs
17 commits

* [`da4ac04`](https://github.com/talos-systems/pkgs/commit/da4ac04969924256df4ebc66d3bf435a52e30cb7) chore: bump tools for Go 1.16.7 * [`10275fb`](https://github.com/talos-systems/pkgs/commit/10275fbf737aaa0ac41cc7220d824f5d68d3b0fa) feat: update Linux to 5.10.57 * [`875c7ec`](https://github.com/talos-systems/pkgs/commit/875c7ecaacc9e999416a2ba17bea3130261120eb) chore: patch grub with support for reproducible ISO builds * [`12856ce`](https://github.com/talos-systems/pkgs/commit/12856ce15d6d72814a2f40bbaf3f8ab6efb849f9) feat: increase number of CPUs supported by the kernel to 512 * [`cbfabac`](https://github.com/talos-systems/pkgs/commit/cbfabaca6a3faf20914aae5c535e44a393a4f422) chore: update ca-certificates to 2021-07-05 * [`0c011c0`](https://github.com/talos-systems/pkgs/commit/0c011c088068e5fdb55066008b526ca3ef69f218) feat: update GRUB to 2.06 * [`5090d14`](https://github.com/talos-systems/pkgs/commit/5090d149a669f7eb3cc922196b7e82869c152dae) chore: update containerd to v1.5.5 * [`6653902`](https://github.com/talos-systems/pkgs/commit/66539021daf1037782b1c4009dd96544057628d3) feat: add kernel drivers for fusion and scsi-isci * [`9b4041f`](https://github.com/talos-systems/pkgs/commit/9b4041fb79d9c5d8e18391f1e2f4843a88d26c19) chore: update containerd to v1.5.4 * [`7b6cc05`](https://github.com/talos-systems/pkgs/commit/7b6cc05ceee8c24e746afa7ed105f9f55fef589b) feat: update kernel to latest 5.10.52 * [`65159fb`](https://github.com/talos-systems/pkgs/commit/65159fb19c3138ec612cdca507e5cc795b657a7d) chore: update runc and CNI plugins * [`514ba34`](https://github.com/talos-systems/pkgs/commit/514ba3420a0773ac7305d00e8b582858f9685953) feat: disable aufs, devmapper, zfs * [`6bc118f`](https://github.com/talos-systems/pkgs/commit/6bc118f37cfd018183952b9feb009c54f1a3c215) chore: update runc and containerd * [`b6fca88`](https://github.com/talos-systems/pkgs/commit/b6fca88d22436a0fb78b8a4e06792b7af1a22ef5) feat: update Go to 1.16.6 * [`fd56852`](https://github.com/talos-systems/pkgs/commit/fd568520e8c77bd8d96f96efb47dd2bdd2f36c1a) chore: update `open-isns` and `open-iscsi` * [`d779204`](https://github.com/talos-systems/pkgs/commit/d779204c0d9e9c8e90f32b1f68eb9ff4b030b83c) chore: update dosfstools to v4.2 * [`bc7c0d7`](https://github.com/talos-systems/pkgs/commit/bc7c0d7c6afaec8226c2a52299981ac519b5e595) feat: add support for hotplug of PCIE devices

### Changes from talos-systems/tools
5 commits

* [`2368154`](https://github.com/talos-systems/tools/commit/23681542fc7e29ede59b3775e04089c5b1a0f666) feat: update Go and protoc-gen-go tools * [`7172a5d`](https://github.com/talos-systems/tools/commit/7172a5db9d361527aa7bd9c7af407b9d578e2e02) feat: update Go to 1.16.6 * [`1de34d7`](https://github.com/talos-systems/tools/commit/1de34d7961c7ac86f369217dea4ce69cdde04122) chore: update musl * [`76979a1`](https://github.com/talos-systems/tools/commit/76979a1c194c74c25db22c9ec90ec36f97179e3f) chore: update protobuf deps * [`0846c64`](https://github.com/talos-systems/tools/commit/0846c6493316b5d00ecc241b7051ced1bac1cf7e) chore: update expat

### Dependency Changes * **github.com/BurntSushi/toml** v0.3.1 -> v0.4.1 * **github.com/aws/aws-sdk-go** v1.38.66 -> v1.40.2 * **github.com/containerd/containerd** v1.5.2 -> v1.5.5 * **github.com/cosi-project/runtime** 93ead370bf57 -> 25f235cd0682 * **github.com/docker/docker** v20.10.7 -> v20.10.8 * **github.com/google/uuid** v1.2.0 -> v1.3.0 * **github.com/hashicorp/go-getter** v1.5.4 -> v1.5.6 * **github.com/opencontainers/runtime-spec** e6143ca7d51d -> 1c3f411f0417 * **github.com/packethost/packngo** v0.19.0 **_new_** * **github.com/prometheus/procfs** v0.6.0 -> v0.7.2 * **github.com/rivo/tview** d4fb0348227b -> 29d673af0ce2 * **github.com/spf13/cobra** v1.1.3 -> v1.2.1 * **github.com/talos-systems/crypto** v0.3.1 -> deec8d47700e * **github.com/talos-systems/extras** v0.4.0 -> v0.5.0-alpha.0-2-g8ce17e5 * **github.com/talos-systems/go-blockdevice** v0.2.1 -> v0.2.3 * **github.com/talos-systems/pkgs** v0.6.0-1-g7b2e126 -> v0.7.0-alpha.0-16-gda4ac04 * **github.com/talos-systems/tools** v0.6.0 -> v0.7.0-alpha.0-3-g2368154 * **github.com/vmware-tanzu/sonobuoy** v0.52.0 -> v0.53.0 * **go.uber.org/zap** v1.17.0 -> v1.18.1 * **golang.org/x/net** 04defd469f4e -> 853a461950ff * **golang.org/x/sys** 59db8d763f22 -> 0f9fa26af87c * **golang.org/x/time** 38a9dc6acbc6 -> 1f47c861a9ac * **google.golang.org/grpc** v1.38.0 -> v1.39.1 * **google.golang.org/protobuf** v1.26.0 -> v1.27.1 * **inet.af/netaddr** bf05d8b52dda -> ce7a8ad02cc1 * **k8s.io/api** v0.21.2 -> v0.22.0 * **k8s.io/apimachinery** v0.21.2 -> v0.22.0 * **k8s.io/apiserver** v0.21.2 -> v0.22.0 * **k8s.io/client-go** v0.21.2 -> v0.22.0 * **k8s.io/cri-api** v0.21.2 -> v0.22.0 * **k8s.io/kubectl** v0.21.2 -> v0.22.0 * **k8s.io/kubelet** v0.21.2 -> v0.22.0 Previous release can be found at [v0.11.0](https://github.com/talos-systems/talos/releases/tag/v0.11.0) ## [Talos 0.12.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.12.0-alpha.0) (2021-08-11) Welcome to the v0.12.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Support for Self-hosted Control Plane Dropped > **Note**: This item only applies to clusters bootstrapped with Talos <= 0.8. Talos 0.12 completely removes support for self-hosted Kubernetes control plane (bootkube-based). Talos 0.9 introduced support for Talos-managed control plane and provided migration path to convert self-hosted control plane to Talos-managed static pods. Automated and manual conversion process is available in Talos from 0.9.x to 0.11.x. For clusters bootstrapped with bootkube (Talos <= 0.8), please make sure control plane is converted to Talos-managed before before upgrading to Talos 0.12. Current control plane status can be checked with `talosctl get bootstrapstatus` before performing upgrade to Talos 0.12. ### Cluster API v0.3.x Cluster API v0.3.x (v1alpha3) is not compatible with Kubernetes 1.22 used by default in Talos 0.12. Talos can be configued to use Kubernetes 1.21 or CAPI v0.4.x components can be used instead. ### Sysctl Configuration Sysctl Kernel Params configuration was completely rewritten to be based on controllers and resources, which makes it possible to apply `.machine.sysctls` in immediate mode (without a reboot). `talosctl get kernelparams` returns merged list of KSPP, Kubernetes and user defined params along with the default values overwritten by Talos. ### Join Node Type Node type `join` was renamed to `worker` for clarity. The old value is still accepted in the machine configuration but deprecated. `talosctl gen config` now generates `worker.yaml` instead of `join.yaml`. ### Networking * multiple static addresses can be specified for the interface with new `.addresses` field (old `.cidr` field is deprecated now) * static addresses can be set on interfaces configured with DHCP ### Performance * machined uses less memory and CPU time * more disk encryption options are exposed via the machine configuration * disk partitions are now aligned properly with minimum I/O size * Talos system processes are moved under proper cgroups, resource metrics are now available via the kubelet * OOM score is set on the system processes making sure they are killed last under memory pressure ### Security * etcd PKI moved to `/system/secrets` * kubelet bootstrap CSR auto-signing scoped to kubelet bootstrap tokens only ### Component Updates * Linux: 5.10.52 * Kubernetes: 1.22.0 * containerd: 1.5.5 * runc: 1.0.1 * GRUB: 2.06 * Talos is built with Go 1.16.6 ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Serge Logvinov * Andrey Smirnov * Artem Chernyshev * Spencer Smith * Alexey Palazhchenko * dependabot[bot] * Rui Lopes * Andrew Rynhard * Caleb Woodbine ### Changes
96 commits

* [`a15f0184`](https://github.com/talos-systems/talos/commit/a15f01844fdaf0d3e2dad2750d9353d03e18dea2) fix: move etcd PKI under /system/secrets * [`eb02afe1`](https://github.com/talos-systems/talos/commit/eb02afe18be63bf483a0467f655611561aef10f6) fix: match correctly routes on the address family * [`cb948acc`](https://github.com/talos-systems/talos/commit/cb948accfeca13c57b3b512dc8a06425989294f9) feat: allow multiple addresses per interface * [`e030b2e8`](https://github.com/talos-systems/talos/commit/e030b2e8bb0a65abf4e1f7b5f27348631210ebc4) chore: use k8s 1.21.3 in CAPI tests for now * [`e08b4f8f`](https://github.com/talos-systems/talos/commit/e08b4f8f9e72f8db1116b4bbe395d49b4bccb460) feat: implement sysctl controllers * [`fdf6b243`](https://github.com/talos-systems/talos/commit/fdf6b2433c40613bcb039852a96196dbe9b7b5e2) chore: revert "improve artifacts generation reproducibility" * [`b68ed1eb`](https://github.com/talos-systems/talos/commit/b68ed1eb896039ec1319db2e3d6d364034c86863) fix: make route resources ID match closer routing table primary key * [`585f6337`](https://github.com/talos-systems/talos/commit/585f633710abb7a6d863b54c37aa65c50a3c7312) fix: correctly handle nodoc for struct fields * [`f2d394dc`](https://github.com/talos-systems/talos/commit/f2d394dc42f9ec704050db0a8a928a889483ce3e) docs: add AMIs for v0.11.5 * [`d0970cbf`](https://github.com/talos-systems/talos/commit/d0970cbfd696b28b201b232a03da2119f664afbd) feat: bootstrap token limit * [`5285a46d`](https://github.com/talos-systems/talos/commit/5285a46d78ef2fc76594aad4ad4acb75312bc0a7) fix: maintenance mode reason message * [`009d15e8`](https://github.com/talos-systems/talos/commit/009d15e8dc6e75eca6b5963dddf8063941099f14) chore: use etcd client TryLock function on upgrade * [`4dae9ea5`](https://github.com/talos-systems/talos/commit/4dae9ea55c087c28a9d7a8d241e0ec3a7a1b8ca3) chore: use vtprotobuf compiled marshaling in Talos API * [`7ca5749a`](https://github.com/talos-systems/talos/commit/7ca5749ad4267701ce639d0f0d91c10a7f9c1d3d) chore: bump dependencies via dependabot * [`b2507b41`](https://github.com/talos-systems/talos/commit/b2507b41d250b989b9c13ad23e16202cd53a18d2) chore: improve artifacts generation reproducibility * [`1f7dad23`](https://github.com/talos-systems/talos/commit/1f7dad234b480c7a5e3484ccf10180747c979036) chore: update PKGS version (512 cpus, new ca-certficates) * [`1a2e78a2`](https://github.com/talos-systems/talos/commit/1a2e78a24e997241c4cd18dfac3c2d971ba78116) fix: update go-blockdevice * [`6d6ed117`](https://github.com/talos-systems/talos/commit/6d6ed1170f3f28e7f559ccdf64e7c34dfee022a0) chore: use parallel xz with higher compression level * [`571f7db1`](https://github.com/talos-systems/talos/commit/571f7db1bb44a0dcb5e373f9c37396d50eb0e8f4) chore: workaround GitHub new release notes limit * [`09d70b7e`](https://github.com/talos-systems/talos/commit/09d70b7eafb18343eb4ca57d7f8b84e4ccd2fcfb) feat: update Kubernetes to v1.22.0 * [`f25f10e7`](https://github.com/talos-systems/talos/commit/f25f10e73ec534acd7cc483f254d612d8a7c1858) feat: add an option to disable PSP * [`7c6e4cf2`](https://github.com/talos-systems/talos/commit/7c6e4cf230ba1f30da664374c41c934d1e6620bc) feat: allow both DHCP and static addressing for the interface * [`3c566dbc`](https://github.com/talos-systems/talos/commit/3c566dbc30595467a3789707c6e993aa92f36df6) fix: remove admission plugins enabled by default from the list * [`69ead373`](https://github.com/talos-systems/talos/commit/69ead37353b7e3aa7f089c70073037a6eba37767) fix: preserve PMBR bootable flag correctly * [`dee63051`](https://github.com/talos-systems/talos/commit/dee63051702d49f495bfb28b4be74ed8b39143ad) fix: align partitions with minimal I/O size * [`62890229`](https://github.com/talos-systems/talos/commit/628902297d2efe93e6388377b2ea6d4beda83095) feat: update GRUB to 2.06 * [`b9d04928`](https://github.com/talos-systems/talos/commit/b9d04928d960f9d576671c6f3511cf242ff31cb7) feat: move system processes to cgroups * [`0b8681b4`](https://github.com/talos-systems/talos/commit/0b8681b4b49ab109b8863792d48c2f551d1ceeb5) fix: resolve several issues with Wireguard link specs * [`f8f4bf3b`](https://github.com/talos-systems/talos/commit/f8f4bf3baef31d4ac957ec68cd869adea1e931cd) docs: add disk encryptions examples * [`79b8fa64`](https://github.com/talos-systems/talos/commit/79b8fa64b9453917860faae3df5d14647186b9ba) feat: update containerd to 1.5.5 * [`539f4209`](https://github.com/talos-systems/talos/commit/539f42090e436921a23087296cde6eaf7e495b5e) chore: bump dependencies via dependabot * [`0c7ce1cd`](https://github.com/talos-systems/talos/commit/0c7ce1cd814354213a1a6c7a9251b166ee58c493) feat: remove remnants of bootkube support * [`d4f9804f`](https://github.com/talos-systems/talos/commit/d4f9804f8659562f6152ae73cb1788f6f6d6ad89) chore: fix typos * [`5f027615`](https://github.com/talos-systems/talos/commit/5f027615ffac68e0a484a5da4827a6589bae3880) feat: expose more encryption options to the machine config * [`585152a0`](https://github.com/talos-systems/talos/commit/585152a0be051accd4cb8b7c2f130c5a92dfd32d) chore: bump dependencies * [`fc66ec59`](https://github.com/talos-systems/talos/commit/fc66ec59691fb1b9d00b27e1f7b34c870a09d717) feat: set oom score for main processes * [`df54584a`](https://github.com/talos-systems/talos/commit/df54584a33d88de13deadcb87a5cfa9c1f9b3961) fix: drop linux capabilities * [`f65d0b73`](https://github.com/talos-systems/talos/commit/f65d0b739bd36a57979f9bf26c3092ac544e607c) docs: add 0.11.3 AMIs * [`7332d636`](https://github.com/talos-systems/talos/commit/7332d63695074dd5eef35ad545d48aff857fbde8) fix: bump pkgs for new kernel 5.10.52 * [`70d2505b`](https://github.com/talos-systems/talos/commit/70d2505b7c8807cb5d4f8a017f9f6200757e13e0) fix: do not require ToVersion to be set when detecting version * [`0953b199`](https://github.com/talos-systems/talos/commit/0953b1998579f855adffff4b83db917f26687a7b) chore: update extras to bring a new CNI bundle * [`b6c47f86`](https://github.com/talos-systems/talos/commit/b6c47f866a57bafb60f85fb1ce10428ed3f52c4a) fix: set the /etc/os-release HOME_URL parameter * [`c780821d`](https://github.com/talos-systems/talos/commit/c780821d0b8fda0b3ef6d33b63b595e40970a897) feat: update containerd to 1.5.3, runc to 1.0.1 * [`f8f1c83a`](https://github.com/talos-systems/talos/commit/f8f1c83a757f5a729896174f95f83c6d804d4858) feat: detect the lowest Kubernetes version in upgrade-k8s CLI command * [`55e17ccd`](https://github.com/talos-systems/talos/commit/55e17ccdd1df789466ccfb0c9cfe55a62b437f77) chore: bump dependencies * [`da6f786c`](https://github.com/talos-systems/talos/commit/da6f786cab80cbacb886d34b7c5e0ed957cc24c9) fix: kuberentes => kubernetes typo * [`2e463348`](https://github.com/talos-systems/talos/commit/2e463348b26fb8b36657b8cb6871e4bce8030b0b) fix: pass all logs through the options.Log method * [`4e9c5afb`](https://github.com/talos-systems/talos/commit/4e9c5afb6dd6bdedb4032b7cf4a24b6f1bf88144) fix: make ethtool optional in link status controller * [`bf61c2cc`](https://github.com/talos-systems/talos/commit/bf61c2cc4a51d290fe98aaeb80224bdd52bb7ac5) fix: write upgrade logs only to the LogOutput if it's defined * [`9c73257c`](https://github.com/talos-systems/talos/commit/9c73257cb128a76459b7d4442b56a50feed089d6) feat: update Go to 1.16.6 * [`23ef1d40`](https://github.com/talos-systems/talos/commit/23ef1d40af44b873d60337d691f878e2cfe0fe8d) chore: add ability to redirect talos upgrade module logs to io.Writer * [`33e9d6c9`](https://github.com/talos-systems/talos/commit/33e9d6c984f82af24ad79e002758841935e60a6a) chore: bump github.com/aws/aws-sdk-go in /hack/cloud-image-uploader * [`604434c4`](https://github.com/talos-systems/talos/commit/604434c43eb63aa760cd2176aa1041b653c9bd75) chore: bump github.com/prometheus/procfs from 0.6.0 to 0.7.0 * [`2ea28f62`](https://github.com/talos-systems/talos/commit/2ea28f62d8dcac3280d7a133ae6532f3ca5709cc) chore: bump node from 16.3.0-alpine to 16.4.2-alpine * [`b358a189`](https://github.com/talos-systems/talos/commit/b358a189bcbaa480d1bb3fbcc58eecd1b61f447d) fix: correctly pick route scope for link-local destination * [`6848d431`](https://github.com/talos-systems/talos/commit/6848d431427636e415436cdda95543a9a0da5676) feat: can change clusterdns ip lists * [`72b76abf`](https://github.com/talos-systems/talos/commit/72b76abfd43d04aa7a9283669925bd49498dc05f) fix: workaround issues when IPv6 is fully or partially disabled * [`679b08f4`](https://github.com/talos-systems/talos/commit/679b08f4fabd098311786551e75e38c2a027bd31) docs: update docs for 0.12 * [`6fbec9e0`](https://github.com/talos-systems/talos/commit/6fbec9e0cb656f411cceb986560473b1a40b6a45) fix: cache etcd client used for healthchecks * [`eea750de`](https://github.com/talos-systems/talos/commit/eea750de2c11a9883f343c65a36e30712b987f89) chore: rename "join" type to "worker" * [`951493ac`](https://github.com/talos-systems/talos/commit/951493ac8356a414ff85fce25e30e4bd808b412c) docs: update what's new for Talos 0.11 * [`b47d1098`](https://github.com/talos-systems/talos/commit/b47d1098b1f1cbd21c501266ffc4a38711ed213f) docs: promote 0.11 docs to be the latest * [`d930a265`](https://github.com/talos-systems/talos/commit/d930a26502759cebccb05d9b78741e1fc147b30b) chore: implement DeepCopy for machine configuration * [`fe4ed3c7`](https://github.com/talos-systems/talos/commit/fe4ed3c734e5713b2fa1d639bd80bffc7888d7e7) chore: ignore tags which don't look like semantic version * [`b969e772`](https://github.com/talos-systems/talos/commit/b969e7720ebcb0103e94494533d819a91dba59f5) chore: update references to old protobuf package * [`2ba8ac9a`](https://github.com/talos-systems/talos/commit/2ba8ac9ab4b24572512c2a877acd26b912b5423a) docs: add documentation directory for 0.12 * [`011e2885`](https://github.com/talos-systems/talos/commit/011e2885e7f88a3a92f3f495fdc1d3be6ed0c877) fix: validate bond slaves addressing * [`10c28758`](https://github.com/talos-systems/talos/commit/10c28758a4fc50a5e5a29097769b4a3a92ed249a) fix: ignore DeadlineExceeded error correctly on bootstrap * [`77fabace`](https://github.com/talos-systems/talos/commit/77fabaceca242f89949d4bf231e9754b4d04eb5e) chore: ignore future pkg/machinery/vX.Y.Z tags * [`6b661114`](https://github.com/talos-systems/talos/commit/6b661114d03a7cd1ddd8939ea323d4fe2ce9976c) fix: make COSI runtime history depth smaller * [`9bf899bd`](https://github.com/talos-systems/talos/commit/9bf899bdd852befbb4aa5ac4f3ceecb3c33502c8) fix: make forfeit leadership connect to the right node * [`4708beae`](https://github.com/talos-systems/talos/commit/4708beaee53e3aacbeec07c38cdd2c7316d16a4c) feat: implement `talosctl config info` command * [`6d13d2cf`](https://github.com/talos-systems/talos/commit/6d13d2cf9243adce739673f1982cbc1f12252ef1) fix: close Kubernetes API client * [`aaa36f3b`](https://github.com/talos-systems/talos/commit/aaa36f3b4fb250d2921f35c09bcb01b6c31ad423) fix: ignore 'not a leader' error on forfeit leadership * [`22a41936`](https://github.com/talos-systems/talos/commit/22a4193678d2245b4c24b7e173d4cfd5fa876e95) fix: workaround 'Unauthorized' errors when accessing Kubernetes API * [`71c6f700`](https://github.com/talos-systems/talos/commit/71c6f7004e28c8a72410652d7d38f770bcf8a5f8) chore: bump go.mod dependencies * [`915cd8fe`](https://github.com/talos-systems/talos/commit/915cd8fe20c55112cc1fa7776c115ac85c7f3da9) docs: add guide for RBAC * [`f5721050`](https://github.com/talos-systems/talos/commit/f5721050deffe61f892a9fca2d20b3fccb5021a6) fix: controlplane keyusage * [`3d772661`](https://github.com/talos-systems/talos/commit/3d7726613ca5c5e6b14b4854564d71ee3644d32e) fix: fill uuid argument correctly in the config download URL * [`d8602025`](https://github.com/talos-systems/talos/commit/d8602025c828189fa15350a15bf3ccefe39bd0ce) chore: update containerd config version 2 * [`5949ec4e`](https://github.com/talos-systems/talos/commit/5949ec4e6e05ada904d69a24c9d21e20cc7dea85) docs: describe the new network configuration subsystem * [`444d72b4`](https://github.com/talos-systems/talos/commit/444d72b4d7cff7b38c8e3a483bbe10c74251448a) feat: update pkgs version * [`e883c12b`](https://github.com/talos-systems/talos/commit/e883c12b31e2ddc3860abc04e7c0867701f46026) fix: make output of `upgrade-k8s` command less scary * [`7f8e50de`](https://github.com/talos-systems/talos/commit/7f8e50de4d9a36dae9de7783d71a981fb6a72854) fix: restart the merge controllers on conflict * [`60d73609`](https://github.com/talos-systems/talos/commit/60d7360944ff6fc1e75f98e37a754f3bb2962144) fix: ignore deadline exceeded errors on bootstrap * [`ee06dd69`](https://github.com/talos-systems/talos/commit/ee06dd69fc39d5df720a88991caaf3646c6fa349) fix: don't print git sha of the release twice in the dashboard * [`07fb61e5`](https://github.com/talos-systems/talos/commit/07fb61e5d22da86b434d30f12b84b845ac1a4df7) fix: issue worker apid certs properly on renewal * [`84817f73`](https://github.com/talos-systems/talos/commit/84817f733458cbd35549eebc72df6a5df202b299) chore: bump Talos version in upgrade tests * [`2fa54107`](https://github.com/talos-systems/talos/commit/2fa54107b2c84cabe948ace5d70836dd4be95799) chore: fix tests for disabled RBAC * [`78583ba9`](https://github.com/talos-systems/talos/commit/78583ba985fa2b90ec610d148b2cbeb0b92d646b) fix: don't set bond delay options if miimon is not enabled * [`bbf1c091`](https://github.com/talos-systems/talos/commit/bbf1c091d4cea0b4610bce7165a98c7572423b01) feat: add RBAC to `talosctl version` output * [`5f6ec3ef`](https://github.com/talos-systems/talos/commit/5f6ec3ef66c8bf2cb334e02b5aa9869330c985d8) fix: handle cases when merged resource re-appears before being destroyed * [`1e9a0e74`](https://github.com/talos-systems/talos/commit/1e9a0e745db73bd45ec0881aa19e43d7badb5914) fix: documentation typos * [`f228af40`](https://github.com/talos-systems/talos/commit/f228af4061e2025531c953fdb7f8bf83de4bf8b0) chore: bump go.mod dependencies * [`2060ceaa`](https://github.com/talos-systems/talos/commit/2060ceaa0b16be04a61a00e0085e25889ffe613a) chore: add CAPI version to CI setup * [`ad047a7d`](https://github.com/talos-systems/talos/commit/ad047a7dee4c0ac26c01862bdaa923fab93cc2e1) chore: small RBAC improvements

### Changes from talos-systems/crypto
1 commit

* [`deec8d4`](https://github.com/talos-systems/crypto/commit/deec8d47700e10e3ea813bdce01377bd93c83367) chore: implement DeepCopy methods for PEMEncoded* types

### Changes from talos-systems/extras
2 commits

* [`4957f3c`](https://github.com/talos-systems/extras/commit/4957f3c64bc5fd1574fe3d3f251f52e914e78e41) chore: update pkgs to use CNI plugins v0.9.1 * [`233716a`](https://github.com/talos-systems/extras/commit/233716a04f1e4e1762101b279308630caa46d17d) feat: update Go to 1.16.6

### Changes from talos-systems/go-blockdevice
4 commits

* [`fe24303`](https://github.com/talos-systems/go-blockdevice/commit/fe2430349e9d734ce6dbf4e7b2e0f8a37bb22679) fix: perform correct PMBR partition calculations * [`2ec0c3c`](https://github.com/talos-systems/go-blockdevice/commit/2ec0c3cc0ff5ff705ed5c910ca1bcd5d93c7b102) fix: preserve the PMBR bootable flag when opening GPT partition * [`87816a8`](https://github.com/talos-systems/go-blockdevice/commit/87816a81cefc728cfe3cb221b476d8ed4b609fd8) feat: align partition to minimum I/O size * [`c34b59f`](https://github.com/talos-systems/go-blockdevice/commit/c34b59fb33a7ad8be18bb19bc8c8d8294b4b3a78) feat: expose more encryption options in the LUKS module

### Changes from talos-systems/pkgs
14 commits

* [`12856ce`](https://github.com/talos-systems/pkgs/commit/12856ce15d6d72814a2f40bbaf3f8ab6efb849f9) feat: increase number of CPUs supported by the kernel to 512 * [`cbfabac`](https://github.com/talos-systems/pkgs/commit/cbfabaca6a3faf20914aae5c535e44a393a4f422) chore: update ca-certificates to 2021-07-05 * [`0c011c0`](https://github.com/talos-systems/pkgs/commit/0c011c088068e5fdb55066008b526ca3ef69f218) feat: update GRUB to 2.06 * [`5090d14`](https://github.com/talos-systems/pkgs/commit/5090d149a669f7eb3cc922196b7e82869c152dae) chore: update containerd to v1.5.5 * [`6653902`](https://github.com/talos-systems/pkgs/commit/66539021daf1037782b1c4009dd96544057628d3) feat: add kernel drivers for fusion and scsi-isci * [`9b4041f`](https://github.com/talos-systems/pkgs/commit/9b4041fb79d9c5d8e18391f1e2f4843a88d26c19) chore: update containerd to v1.5.4 * [`7b6cc05`](https://github.com/talos-systems/pkgs/commit/7b6cc05ceee8c24e746afa7ed105f9f55fef589b) feat: update kernel to latest 5.10.52 * [`65159fb`](https://github.com/talos-systems/pkgs/commit/65159fb19c3138ec612cdca507e5cc795b657a7d) chore: update runc and CNI plugins * [`514ba34`](https://github.com/talos-systems/pkgs/commit/514ba3420a0773ac7305d00e8b582858f9685953) feat: disable aufs, devmapper, zfs * [`6bc118f`](https://github.com/talos-systems/pkgs/commit/6bc118f37cfd018183952b9feb009c54f1a3c215) chore: update runc and containerd * [`b6fca88`](https://github.com/talos-systems/pkgs/commit/b6fca88d22436a0fb78b8a4e06792b7af1a22ef5) feat: update Go to 1.16.6 * [`fd56852`](https://github.com/talos-systems/pkgs/commit/fd568520e8c77bd8d96f96efb47dd2bdd2f36c1a) chore: update `open-isns` and `open-iscsi` * [`d779204`](https://github.com/talos-systems/pkgs/commit/d779204c0d9e9c8e90f32b1f68eb9ff4b030b83c) chore: update dosfstools to v4.2 * [`bc7c0d7`](https://github.com/talos-systems/pkgs/commit/bc7c0d7c6afaec8226c2a52299981ac519b5e595) feat: add support for hotplug of PCIE devices

### Changes from talos-systems/tools
4 commits

* [`7172a5d`](https://github.com/talos-systems/tools/commit/7172a5db9d361527aa7bd9c7af407b9d578e2e02) feat: update Go to 1.16.6 * [`1de34d7`](https://github.com/talos-systems/tools/commit/1de34d7961c7ac86f369217dea4ce69cdde04122) chore: update musl * [`76979a1`](https://github.com/talos-systems/tools/commit/76979a1c194c74c25db22c9ec90ec36f97179e3f) chore: update protobuf deps * [`0846c64`](https://github.com/talos-systems/tools/commit/0846c6493316b5d00ecc241b7051ced1bac1cf7e) chore: update expat

### Dependency Changes * **github.com/BurntSushi/toml** v0.3.1 -> v0.4.1 * **github.com/aws/aws-sdk-go** v1.38.66 -> v1.40.2 * **github.com/containerd/containerd** v1.5.2 -> v1.5.5 * **github.com/cosi-project/runtime** 93ead370bf57 -> 25f235cd0682 * **github.com/docker/docker** v20.10.7 -> v20.10.8 * **github.com/google/uuid** v1.2.0 -> v1.3.0 * **github.com/hashicorp/go-getter** v1.5.4 -> v1.5.6 * **github.com/opencontainers/runtime-spec** e6143ca7d51d -> 1c3f411f0417 * **github.com/prometheus/procfs** v0.6.0 -> v0.7.2 * **github.com/rivo/tview** d4fb0348227b -> 29d673af0ce2 * **github.com/spf13/cobra** v1.1.3 -> v1.2.1 * **github.com/talos-systems/crypto** v0.3.1 -> deec8d47700e * **github.com/talos-systems/extras** v0.4.0 -> v0.5.0-alpha.0-1-g4957f3c * **github.com/talos-systems/go-blockdevice** v0.2.1 -> v0.2.3 * **github.com/talos-systems/pkgs** v0.6.0-1-g7b2e126 -> v0.7.0-alpha.0-13-g12856ce * **github.com/talos-systems/tools** v0.6.0 -> v0.7.0-alpha.0-2-g7172a5d * **github.com/vmware-tanzu/sonobuoy** v0.52.0 -> v0.53.0 * **go.uber.org/zap** v1.17.0 -> v1.18.1 * **golang.org/x/net** 04defd469f4e -> 853a461950ff * **golang.org/x/sys** 59db8d763f22 -> 0f9fa26af87c * **golang.org/x/time** 38a9dc6acbc6 -> 1f47c861a9ac * **google.golang.org/grpc** v1.38.0 -> v1.39.1 * **google.golang.org/protobuf** v1.26.0 -> v1.27.1 * **inet.af/netaddr** bf05d8b52dda -> ce7a8ad02cc1 * **k8s.io/api** v0.21.2 -> v0.22.0 * **k8s.io/apimachinery** v0.21.2 -> v0.22.0 * **k8s.io/apiserver** v0.21.2 -> v0.22.0 * **k8s.io/client-go** v0.21.2 -> v0.22.0 * **k8s.io/cri-api** v0.21.2 -> v0.22.0 * **k8s.io/kubectl** v0.21.2 -> v0.22.0 * **k8s.io/kubelet** v0.21.2 -> v0.22.0 Previous release can be found at [v0.11.0](https://github.com/talos-systems/talos/releases/tag/v0.11.0) ## [Talos 0.11.0-alpha.2](https://github.com/talos-systems/talos/releases/tag/v0.11.0-alpha.2) (2021-06-23) Welcome to the v0.11.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Default to Bootstrap workflow The `init.yaml` is no longer an output of `talosctl gen config`. We now encourage using the bootstrap API, instead of `init` node types, as we intend on deprecating this machine type in the future. The `init.yaml` and `controlplane.yaml` machine configs are identical with the exception of the machine type. Users can use a modified `controlplane.yaml` with the machine type set to `init` if they would like to avoid using the bootstrap API. ### Component Updates * containerd was updated to 1.5.2 * Linux kernel was updated to 5.10.45 * Kubernetes was updated to 1.21.2 * etcd was updated to 3.4.16 ### CoreDNS Added the flag `cluster.coreDNS.disabled` to coreDNS deployment during the cluster bootstrap. ### Legacy BIOS Support Added an option to the `machine.install` section of the machine config that can enable marking MBR partition bootable for the machines that have legacy BIOS which does not support GPT partitioning scheme. ### Multi-arch Installer Talos installer image (for any arch) now contains artifacts for both `amd64` and `arm64` architecture. This means that e.g. images for arm64 SBCs can be generated on amd64 host. ### Networking Configuration Talos networking configuration was completely rewritten to be based on controllers and resources. There are no changes to the machine configuration, but any update to `.machine.network` can now be applied in immediate mode (without a reboot). Talos should be setting up network configuration much faster on boot now, not blocking on DHCP for unconfigured interfaces and skipping the reset network step. ### Talos API RBAC Limited RBAC support in Talos API is now enabled by default for Talos 0.11. Default `talosconfig` has `os:admin` role embedded in the certificate so that all the APIs are available. Certificates with reduced set of roles can be created with `talosctl config new` command. When upgrading from Talos 0.10, RBAC is not enabled by default. Before enabling RBAC, generate `talosconfig` with `os:admin` role first to make sure that administrator still has access to the cluster when RBAC is enabled. List of available roles: * `os:admin` role enables every Talos API * `os:reader` role limits access to read-only APIs which do not return sensitive data * `os:etcd:backup` role only allows `talosctl etcd snapshot` API call (for etcd backup automation) ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Serge Logvinov * Jorik Jonker * Spencer Smith * Andrew Rynhard * Andrew LeCody * Kevin Hellemun * Seán C McCord * Boran Car * Brandon Nason * Gabor Nyiri * Gabor Nyiri * Joost Coelingh * Lance R. Vick * Lennard Klein * Sébastien Bernard * Sébastien Bernard ### Changes
162 commits

* [`0731be90`](https://github.com/talos-systems/talos/commit/0731be908bfe130b37db3d5f54b96f3981b1c860) feat: add cloud images to releases * [`b52b2066`](https://github.com/talos-systems/talos/commit/b52b206665ba963ceec0b7a4ff41bcee77aa8a67) feat: split etcd certificates to peer/client * [`33119d2b`](https://github.com/talos-systems/talos/commit/33119d2b8e4b48367421ed8e66aa4b11e639b2ac) chore: add an option to launch cluster with bad RTC state * [`d8c2bca1`](https://github.com/talos-systems/talos/commit/d8c2bca1b53dc9d0e7bb627fe43c629a52489dec) feat: reimplement apid certificate generation on top of COSI * [`3c1b3219`](https://github.com/talos-systems/talos/commit/3c1b32199d294bd52983c4dd57738cad29aa8738) chore: refactor CLI tests * [`0fd9ea2d`](https://github.com/talos-systems/talos/commit/0fd9ea2d63af00f7d2423c2daba2988c38cdae78) feat: enable MACVTAP support * [`898673e8`](https://github.com/talos-systems/talos/commit/898673e8d3e53a0022f2564ee26a29991c145aa8) chore: update e2e tests to use latest capi releases * [`e26c5583`](https://github.com/talos-systems/talos/commit/e26c5583c2dbe771bd50a7f9efe958cdc9c60d54) docs: add AMI IDs for Talos 0.10.4 * [`72ef48f0`](https://github.com/talos-systems/talos/commit/72ef48f0ea1898e80977f56724e931c73d7aff94) fix: assign source address to the DHCP default gateway routes * [`004885a3`](https://github.com/talos-systems/talos/commit/004885a379a8617a874bd97062eb7af00fe7dc3b) feat: update Linux kernel to 5.10.45, etcd to 3.4.16 * [`821f469a`](https://github.com/talos-systems/talos/commit/821f469a1d82e180528dc07afffde05f08a57dd1) feat: skip overlay mount checks with docker * [`b6e02311`](https://github.com/talos-systems/talos/commit/b6e02311a36a7eeb5bfb22037989f49483b9e3f0) feat: use COSI RD's sensitivity for RBAC * [`46751c1a`](https://github.com/talos-systems/talos/commit/46751c1ad2b2102ea6b8e151bdbe854d041250cb) feat: improve security of Kubernetes control plane components * [`0f659622`](https://github.com/talos-systems/talos/commit/0f659622d02260731a30d4862da99697adc7ab5c) fix: build with custom kernel/rootfs * [`5b5089ab`](https://github.com/talos-systems/talos/commit/5b5089ab95e2a7a345e18232520d9071180d9f10) fix: mark kube-proxy as system critical priority * [`42c16f67`](https://github.com/talos-systems/talos/commit/42c16f67f4476b8b57c39fea2bd3ec8168cb8193) chore: bump dependencies * [`60f78419`](https://github.com/talos-systems/talos/commit/60f78419e490f47dc1424008f33cc1baa05097b4) chore: bump etcd client libraries to final 3.5.0 release * [`2b0de9ed`](https://github.com/talos-systems/talos/commit/2b0de9edb2b0f158f12cd320ac672c3c3a5a339b) feat: improve security of Kubernetes control plane components * [`48a5c460`](https://github.com/talos-systems/talos/commit/48a5c460a140b50026210576a46a691393511461) docs: provide more storage details * [`e13d905c`](https://github.com/talos-systems/talos/commit/e13d905c2e682b8470e62fd1ee9cd4f07a6c6c65) release(v0.11.0-alpha.1): prepare release * [`70ac771e`](https://github.com/talos-systems/talos/commit/70ac771e0846247dbebf484aca20ef950d8b99c7) fix: use localhost API server endpoint for internal communication * [`a941eb7d`](https://github.com/talos-systems/talos/commit/a941eb7da06246d59cec1b63883f2d7e3f91ce73) feat: improve security of Kubernetes control plane components * [`3aae94e5`](https://github.com/talos-systems/talos/commit/3aae94e5306c0d6e31df4aee127ee3562709edd3) feat: provide Kubernetes nodename as a COSI resource * [`06209bba`](https://github.com/talos-systems/talos/commit/06209bba2867829561a60f0e7cd9847fa9a8edd3) chore: update RBAC rules, remove old APIs * [`9f24b519`](https://github.com/talos-systems/talos/commit/9f24b519dce07ce05099b242ba95e8a1e319630e) chore: remove bootkube check from cluster health check * [`4ac9bea2`](https://github.com/talos-systems/talos/commit/4ac9bea27dc098ebdfdc0958f3000d960fad50de) fix: stop etcd client logs from going to the server console * [`f63ab9dd`](https://github.com/talos-systems/talos/commit/f63ab9dd9bb6c734873dc8073892f5f10a2ed2e1) feat: implement `talosctl config new` command * [`fa15a668`](https://github.com/talos-systems/talos/commit/fa15a6687fc56820fbc5566d494bedbc1a5f600f) fix: don't enable RBAC feature in the config for Talos < 0.11 * [`2dc27d99`](https://github.com/talos-systems/talos/commit/2dc27d9964fa3df08a6ec11c0b045d7325ea0d2b) fix: do not format state partition in the initialize sequence * [`b609f33c`](https://github.com/talos-systems/talos/commit/b609f33cdebb0659738d4fa3802035b2b344b9b9) fix: update networking stack after Equnix Metal testing * [`243a3b53`](https://github.com/talos-systems/talos/commit/243a3b53e0e7591d5958a3b8373ab963990c40d6) fix: separate healthy and unknown flags in the service resource * [`1a1378be`](https://github.com/talos-systems/talos/commit/1a1378be16fdce45273bdc81fb72715c4766ee4b) fix: update retry package with a fix for errors.Is * [`cb83edd7`](https://github.com/talos-systems/talos/commit/cb83edd7fcf14bd199950a04e366fc573bcf4270) fix: wait for the network to be ready in mainteancne mode * [`96f89071`](https://github.com/talos-systems/talos/commit/96f89071c3ecd809d912762e40cb9d98ce052018) feat: update controller-runtime logs to console level on config.debug * [`973069b6`](https://github.com/talos-systems/talos/commit/973069b611456f758037c9ca4dc50a4a84e7a59c) feat: support NFS 4.1 * [`654dcad4`](https://github.com/talos-systems/talos/commit/654dcad4753211599d12655ec0f0466f27f49589) chore: bump dependencies via dependabot * [`d7394457`](https://github.com/talos-systems/talos/commit/d7394457d978d073690bec589ea78d957539e333) fix: don't treat ethtool errors as fatal * [`f2ae9cd0`](https://github.com/talos-systems/talos/commit/f2ae9cd0c1b7d27b5b9971f4820e5feae7934124) feat: replace networkd with new network implementation * [`caec3063`](https://github.com/talos-systems/talos/commit/caec3063c82777f82599632ca4914a58515cb9a9) fix: do not complain about empty roles * [`11918a11`](https://github.com/talos-systems/talos/commit/11918a110a628d7e0b8749fce92ef572aca47874) docs: update community meeting time * [`aeddb9c0`](https://github.com/talos-systems/talos/commit/aeddb9c0977a51e7aca72f69edda8b69d917db13) feat: implement platform config controller (hostnames) * [`1ece334d`](https://github.com/talos-systems/talos/commit/1ece334da9d7bb247c385dba08202345b83c1a0f) feat: implement controller which runs network operators * [`744ea8a5`](https://github.com/talos-systems/talos/commit/744ea8a5d4b4cb4ff69c2c2fc636e499af892fee) fix: do not add bootstrap contents option if tail events is not 0 * [`5029edfb`](https://github.com/talos-systems/talos/commit/5029edfb71990581515cabe9634d0519a9988316) fix: overwrite nodes in the gRPC metadata * [`6a35c8f1`](https://github.com/talos-systems/talos/commit/6a35c8f110abaf0017530650c55a34f1caae6288) feat: implement virtual IP (shared IP) network operator * [`0f3b8380`](https://github.com/talos-systems/talos/commit/0f3b83803d812a30e1418666fa5758734c20e5c2) chore: expose WatchRequest in the resources client * [`11e258b1`](https://github.com/talos-systems/talos/commit/11e258b15097493d2b4efd596b2fde2d52579455) feat: implement operator configuration controller * [`ce3815e7`](https://github.com/talos-systems/talos/commit/ce3815e75e889de32d9473a23e75863f56b893da) feat: implement DHCP6 operator * [`f010d99a`](https://github.com/talos-systems/talos/commit/f010d99afbc6095ad8fe218187fda306c59d3e1e) feat: implement operator framework with DHCP4 as the first example * [`f93c9c8f`](https://github.com/talos-systems/talos/commit/f93c9c8fa607a5116274d7e090f49568d01814e7) feat: bring unconfigured links with link carrier up by default * [`02bd657b`](https://github.com/talos-systems/talos/commit/02bd657b252ae64ea054b2dc338e55ce9352b420) feat: implement network.Status resource and controller * [`da329f00`](https://github.com/talos-systems/talos/commit/da329f00ab0af9f670207da1e13541aef36c4ca6) feat: enable RBAC by default * [`0f168a88`](https://github.com/talos-systems/talos/commit/0f168a880143141d8637d21aa9da403383dcf025) feat: add configuration for enabling RBAC * [`e74f789b`](https://github.com/talos-systems/talos/commit/e74f789b01b9910f8193415dcefb4b32abcb5f5c) feat: implement EtcFileController to render files in `/etc` * [`5aede1a8`](https://github.com/talos-systems/talos/commit/5aede1a83313152bd83891d0cae4b388a54bd9c2) fix: prefer extraConfig over OVF env, skip empty config * [`5ad314fe`](https://github.com/talos-systems/talos/commit/5ad314fe7e7cfca8196770071d52b93aa4f767f6) feat: implement basic RBAC interceptors * [`c031be81`](https://github.com/talos-systems/talos/commit/c031be8139dbe1f803b70fc9941cfe438b9ddeb9) chore: use Go 1.16.5 * [`8b0763f6`](https://github.com/talos-systems/talos/commit/8b0763f6a20691d36d2c82f2a756171c55450a8a) chore: bump dependencies via dependabot * [`8b8de11d`](https://github.com/talos-systems/talos/commit/8b8de11d9f4d1b1fde43b7fdd56b96d5e3eb5413) feat: implement new controllers for hostname, resolvers and time servers * [`24859b14`](https://github.com/talos-systems/talos/commit/24859b14108df7c5895022043d02d4d5ca7660a4) docs: update Rpi4 firmware guide * [`62c702c4`](https://github.com/talos-systems/talos/commit/62c702c4fd6e7a11654f542bbe31d1adfc896731) fix: remove conflicting etcd member on rejoin with empty data directory * [`ff62a599`](https://github.com/talos-systems/talos/commit/ff62a59984ef0c61dcf549ab38d39584e3630724) fix: drop into maintenance mode if config URL is `none` (metal) * [`14e696d0`](https://github.com/talos-systems/talos/commit/14e696d068b5d895b4fefc06bc6d26b4ac2bc450) feat: update COSI runtime and add support for tail in the Talos gRPC * [`a71053fc`](https://github.com/talos-systems/talos/commit/a71053fcd88d7651e536ce29b574e18f84678f3e) feat: default to bootstrap workflow * [`76aac4bb`](https://github.com/talos-systems/talos/commit/76aac4bb25d8bc6a86458b8ac5be10ca67f236be) feat: implement CPU and Memory stats controller * [`8f90c6a8`](https://github.com/talos-systems/talos/commit/8f90c6a8e1d76a3ddecc99be4e4b9f0ce0235daa) feat: parse Talos-specific cmdline params * [`ed10e139`](https://github.com/talos-systems/talos/commit/ed10e139c161b0a6e0f3460e21e4e1752b26cb46) feat: implement NodeAddress controller * [`33db8857`](https://github.com/talos-systems/talos/commit/33db8857aaf6e411464d08c51560473455e8e156) fix: use COSI runtime DestroyReady input type * [`6e775363`](https://github.com/talos-systems/talos/commit/6e775363920b7869b83775d1b674807163039eb1) refactor: rename *.Status() to *.TypedSpec() in the resources * [`97627061`](https://github.com/talos-systems/talos/commit/97627061d7e8de90e2f2745efa7497137447d116) docs: set static IP on ISO install mode * [`5811f4dd`](https://github.com/talos-systems/talos/commit/5811f4dda1b62848eefae9be56e8b91d443f4d34) feat: implement link (interface) controllers * [`046b229b`](https://github.com/talos-systems/talos/commit/046b229b13708c3ffe1d77b8884242fc100097d0) chore: skip building multi-arch installer for race-enabled build * [`73fbb4b5`](https://github.com/talos-systems/talos/commit/73fbb4b523b41d266840eced306242d57a332b4d) fix: only fetch machine uuid if it's not set * [`f112a540`](https://github.com/talos-systems/talos/commit/f112a540b0e776f06820ee900d6ce9f4f2de02ec) fix: clean up stale snapshots on container start * [`c036b949`](https://github.com/talos-systems/talos/commit/c036b949486d94cbbce54c7511633d398f75797c) chore: bump dependencies * [`a4d67a01`](https://github.com/talos-systems/talos/commit/a4d67a01820894d3ebf8c65a06345232fae4f93b) feat: add the ability to disable CoreDNS * [`76dbfb36`](https://github.com/talos-systems/talos/commit/76dbfb3699df0725a8acf29bff39c43e4aa34f9d) feat: add ability to mark MBR partition bootable * [`e0f5b1e2`](https://github.com/talos-systems/talos/commit/e0f5b1e20aa0d22898274ddc0f9026c0d813cee2) chore: split mgmt/gen.go into several files * [`fad1b4f1`](https://github.com/talos-systems/talos/commit/fad1b4f1fdce962b779ceb960f81d572ee5033af) chore: fix go generate for the machinery * [`1117294a`](https://github.com/talos-systems/talos/commit/1117294ad21945d24b0954d223cc4996df01dd81) release(v0.11.0-alpha.0): prepare release * [`c0962946`](https://github.com/talos-systems/talos/commit/c09629466321f4d220454164784edf41fd3d5813) chore: prepare for 0.11 release series * [`72359765`](https://github.com/talos-systems/talos/commit/723597657ad78e9766190ea2e110208c62d0093b) feat: enable GORACE=halt_on_panic=1 in machined binary * [`0acb04ad`](https://github.com/talos-systems/talos/commit/0acb04ad7a2a0a7b75471f0251b0e04eccd927cd) feat: implement route network controllers * [`f5bf88a4`](https://github.com/talos-systems/talos/commit/f5bf88a4c2ab8f48fd93bc7ac13543c613bf9bd1) feat: create certificates with os:admin role * [`1db301ed`](https://github.com/talos-systems/talos/commit/1db301edf6a4057814a6d5b8f87fbfe1e020caeb) feat: switch controller-runtime to zap.Logger * [`f7cf64d4`](https://github.com/talos-systems/talos/commit/f7cf64d42ec77ca68408ecb0f437ab5f86bc787a) fix: add talos.config to the vApp Properties in VMware OVA * [`209527ec`](https://github.com/talos-systems/talos/commit/209527eccc6c93edad33a01a3f3d24fb978f2f07) docs: add AMIs for Talos 0.10.3 * [`59cfd312`](https://github.com/talos-systems/talos/commit/59cfd312c1ac531528c4ceb2adeb3f85829cc4e1) chore: bump dependencies via dependabot * [`1edb20cf`](https://github.com/talos-systems/talos/commit/1edb20cf98fe2e641cefc658d17206e09acabc26) feat: extract config generation * [`af77c295`](https://github.com/talos-systems/talos/commit/af77c29565b65766d135884ec7740f67b56626e3) docs: update wirguard guide * [`4fe69121`](https://github.com/talos-systems/talos/commit/4fe691214366c08ea846bdc6233dd592da0d4769) test: better `talosctl ls` tests * [`04ddda96`](https://github.com/talos-systems/talos/commit/04ddda962fbcfdeaae59d232e7bb7f9c5bb63bc7) feat: update containerd to 1.5.2, runc to 1.0.0-rc95 * [`49c7276b`](https://github.com/talos-systems/talos/commit/49c7276b16a82b7da8c83f8bd930361768f0e249) chore: fix markdown linting * [`7270495a`](https://github.com/talos-systems/talos/commit/7270495ace9faf48a73829bbed0e4eb2c939eecb) docs: add mayastor quickstart * [`d3d9112f`](https://github.com/talos-systems/talos/commit/d3d9112f288d3b0f3ebe1c8b28b1c4e2fc8512b2) docs: fix spelling/grammar in What's New for Talos 0.9 * [`82804414`](https://github.com/talos-systems/talos/commit/82804414fc2fcb21da77edc2fbbefe92a14fc30d) test: provide a way to force different boot order in provision library * [`a1c0e99a`](https://github.com/talos-systems/talos/commit/a1c0e99a1729c704a633dcc557dc46466b828e11) docs: add guide for deploying metrics-server * [`6bc6658b`](https://github.com/talos-systems/talos/commit/6bc6658b518379d418baafcf9b1045a3b84f48ec) feat: update containerd to 1.5.1 * [`c6567fae`](https://github.com/talos-systems/talos/commit/c6567fae9c59da5148c9876289a4bf248240b99d) chore: dependabot updates * [`61ccbb3f`](https://github.com/talos-systems/talos/commit/61ccbb3f5a2564376af13ea9bbfe51e364fcb3a1) chore: keep debug symbols in debug builds * [`1ce362e0`](https://github.com/talos-systems/talos/commit/1ce362e05e41cd76cdda17a6fc971767e036df37) docs: update customizing kernel build steps * [`a26174b5`](https://github.com/talos-systems/talos/commit/a26174b54846bdfa0b66d2f9147bfe1dc8f2eb52) fix: properly compose pattern and header in etcd members output * [`0825cf11`](https://github.com/talos-systems/talos/commit/0825cf11f412eef930db269b6cae02d059058101) fix: stop networkd and pods before leaving etcd on upgrade * [`bed6b15d`](https://github.com/talos-systems/talos/commit/bed6b15d6fcf0634a887b79797d639e221fe9387) fix: properly populate AllowSchedulingOnMasters option in gen config RPC * [`071f0445`](https://github.com/talos-systems/talos/commit/071f044562dd247dd54584d7b9fa0bb24d6f7599) feat: implement AddressSpec handling * [`76e38b7b`](https://github.com/talos-systems/talos/commit/76e38b7b8251548292ae15ecda2bfa1c8ddc5cf3) feat: update Kubernetes to 1.21.1 * [`9b1338d9`](https://github.com/talos-systems/talos/commit/9b1338d989e6cdf7e0b6d5fe1ba3c32d27fc2251) chore: parse "boolean" variables * [`c81cfb21`](https://github.com/talos-systems/talos/commit/c81cfb21670b82e518cf4c32230e8fbbce6be8ff) chore: allow building with debug handlers * [`c9651673`](https://github.com/talos-systems/talos/commit/c9651673b9eaf811ae4acfed313debbf78bd80e8) feat: update go-smbios library * [`95c656fb`](https://github.com/talos-systems/talos/commit/95c656fb72b6b858b55dae37020cb59ba26115f8) feat: update containerd to 1.5.0, runc to 1.0.0-rc94 * [`db9c35b5`](https://github.com/talos-systems/talos/commit/db9c35b570b39f4423f4636f9e9f1d14cac5d7c1) feat: implement AddressStatusController * [`1cf011a8`](https://github.com/talos-systems/talos/commit/1cf011a809b924fc8f2083566d169704c6e07cd5) chore: bump dependencies via dependabot * [`e3f407a1`](https://github.com/talos-systems/talos/commit/e3f407a1dff3f4ee7e024bbfb64f17b5cb5d625d) fix: properly pass disk type selector from config to matcher * [`66b2b450`](https://github.com/talos-systems/talos/commit/66b2b450582593e93598fac80c8b3c29e8c8a944) feat: add resources and use HTTPS checks in control plane pods * [`4ffd7c0a`](https://github.com/talos-systems/talos/commit/4ffd7c0adf281033ac02d37ca434e7f9ad71e692) fix: stop networkd before leaving etcd on 'reset' path * [`610d38d3`](https://github.com/talos-systems/talos/commit/610d38d309dabaa623494ade12234f1ccf018a9e) docs: add AMIs for 0.10.1, collapse list of AMIs by default * [`807497ec`](https://github.com/talos-systems/talos/commit/807497ec20dee15953186bda0fe7a45ffec0307c) chore: make conformance pipeline depend on cron-default * [`3c121359`](https://github.com/talos-systems/talos/commit/3c1213596cdf03daf09050103f57b29e756439b1) feat: implement LinkStatusController * [`0e8de046`](https://github.com/talos-systems/talos/commit/0e8de04698aac95062f3037da0a9af8b6ee916b0) fix: update go-blockdevice to fix disk type detection * [`4d50a4ed`](https://github.com/talos-systems/talos/commit/4d50a4edd0eb413c16e899536ccdc2642e37aeaa) fix: update the way NTP sync uses `adjtimex` syscall * [`1a85c14a`](https://github.com/talos-systems/talos/commit/1a85c14a51fdab43ae84274563bf89b30e4e6d92) fix: avoid data race on CRI pod stop * [`5de8dbc0`](https://github.com/talos-systems/talos/commit/5de8dbc06c7ed36c8f3af9adea8b1abedeb372b6) fix: repair pine64 support * [`38239097`](https://github.com/talos-systems/talos/commit/3823909735859f2ac5d95bc39c051fc9c2c07685) fix: properly parse matcher expressions * [`e54b6b7a`](https://github.com/talos-systems/talos/commit/e54b6b7a3d7412ddce1467dfbd35efe3cfd76f3f) chore: update dependencies via dependabot * [`f2caed0d`](https://github.com/talos-systems/talos/commit/f2caed0df5b76c4a719f968191081a6e5e2e95c7) chore: use extracted talos-systems/go-kmsg library * [`79d804c5`](https://github.com/talos-systems/talos/commit/79d804c5b4af50a0fd73db17d2522d6a6b45c9ca) docs: fix typos * [`a2bb390e`](https://github.com/talos-systems/talos/commit/a2bb390e1d56106d6d3c1526f3f76b34846b0274) feat: deterministic builds * [`e480fedf`](https://github.com/talos-systems/talos/commit/e480fedff047233e78ad2c22e7b84cbbb22798d5) feat: add USB serial drivers * [`79299d76`](https://github.com/talos-systems/talos/commit/79299d761c50aff386ab7b3c12f39c1797585632) docs: add Matrix room links * [`1b3e8b09`](https://github.com/talos-systems/talos/commit/1b3e8b09edcd51cf3df2d43d14c8fbf1e912a465) docs: add survey to README * [`8d51c9bb`](https://github.com/talos-systems/talos/commit/8d51c9bb190c2c60fa9be6a00572d2eaf4221e94) docs: update redirects to Talos 0.10 * [`1092c3a5`](https://github.com/talos-systems/talos/commit/1092c3a5069a3add439860d90c3615111fa03c98) feat: add Pine64 SBC support * [`63e01754`](https://github.com/talos-systems/talos/commit/63e0175437e45c8f7e5296841337a640c600982c) feat: pull kernel with VMware balloon module enabled * [`aeec99d8`](https://github.com/talos-systems/talos/commit/aeec99d8247f4eb534e0db1ed639f95cd726fe08) chore: remove temporary fork * [`0f49722d`](https://github.com/talos-systems/talos/commit/0f49722d0ff4e731f17a55d1ca50472714334748) feat: add `--config-patch` flag by node type * [`a01b1d22`](https://github.com/talos-systems/talos/commit/a01b1d22d9f3fa94355817217fefd80fe34628f3) chore: dump dependencies via dependabot * [`d540a4a4`](https://github.com/talos-systems/talos/commit/d540a4a4711367a0ada203f668382e39876ba081) fix: bump crypto library for the CSR verification fix * [`c3a4173e`](https://github.com/talos-systems/talos/commit/c3a4173e11a92c2bc51ea4f284ad38c9750105d2) chore: remove security API ReadFile/WriteFile * [`38037131`](https://github.com/talos-systems/talos/commit/38037131cddc2aefbae0f48fb7e355ec76247b67) chore: update wgctrl dependecy * [`d9ba0fd0`](https://github.com/talos-systems/talos/commit/d9ba0fd0164b2bfb2bc4ffe7a2d9d6c665a38e4d) docs: create v0.11 docs, promote v0.10 docs, add v0.10 AMIs * [`2261d7ed`](https://github.com/talos-systems/talos/commit/2261d7ed0212c287273eac647647e4390c530a6e) fix: use both self-signed and Kubernetes CA to verify Kubelet cert * [`a3537a69`](https://github.com/talos-systems/talos/commit/a3537a691320430eeb7149abe73419ee242312fc) docs: update cloud images for Talos v0.9.3 * [`5b9ee861`](https://github.com/talos-systems/talos/commit/5b9ee86179fb92989b02533d6d6745a5b0f37566) docs: add what's new for Talos 0.10 * [`f1107fa3`](https://github.com/talos-systems/talos/commit/f1107fa3a33955f3aa57a49991c87f9ee47b6e67) docs: add survey * [`93623d47`](https://github.com/talos-systems/talos/commit/93623d47f24fef0d149fa006678b61e3182ef771) docs: update AWS instructions * [`a739d1b8`](https://github.com/talos-systems/talos/commit/a739d1b8adbc026796d1c55f7319677f9010f727) feat: add support of custom registry CA certificate usage * [`7f468d35`](https://github.com/talos-systems/talos/commit/7f468d350a6f80d2815149376fa24f7d7629402c) fix: update osType in OVA other3xLinux64Guest" * [`4a184b67`](https://github.com/talos-systems/talos/commit/4a184b67d6ae25b21b35373e7dd6eab41b042c96) docs: add etcd backup and restore guide * [`5fb38d3e`](https://github.com/talos-systems/talos/commit/5fb38d3e5f201934d64bae186c5300e7de7af3d4) chore: refactor Dockerfile for cross-compilation * [`a8f1e526`](https://github.com/talos-systems/talos/commit/a8f1e526bfc00107c915572df2be08b3f154f4e6) chore: build talosctl for Darwin / Apple Silicon * [`eb0b64d3`](https://github.com/talos-systems/talos/commit/eb0b64d3138228a6c751387c720ca81c338b834d) chore: list specifically for enabled regions * [`669a0cbd`](https://github.com/talos-systems/talos/commit/669a0cbdc4756f0ad8f0dacc56a20f71e96fe4cd) fix: check if OVF env is empty * [`da92049c`](https://github.com/talos-systems/talos/commit/da92049c0b4beae32af80205f50849443cd6dad3) chore: use codecov from the build container * [`9996d4b0`](https://github.com/talos-systems/talos/commit/9996d4b028f3845071850def75f2b534e4d2b190) chore: use REGISTRY_MIRROR_FLAGS if defined * [`05cbe250`](https://github.com/talos-systems/talos/commit/05cbe250c87339e097d435d6b10b9d8a5f2eb49e) chore: bump dependencies via dependabot * [`9a91142a`](https://github.com/talos-systems/talos/commit/9a91142a38b3b1f210773acf8df01ed6a45599c2) feat: print complete member info in etcd members * [`bb40d6dd`](https://github.com/talos-systems/talos/commit/bb40d6dd06a967464c24ab33744bbf460aa84038) feat: update pkgs version * [`e7a9164b`](https://github.com/talos-systems/talos/commit/e7a9164b1e1630f953a420d99c865aef6e652d15) test: implement `talosctl conformance` command to run e2e tests * [`6cb266e7`](https://github.com/talos-systems/talos/commit/6cb266e74e60d9d5423feaad550a7861dc73f11d) fix: update etcd client errors, print etcd join failures * [`0bd8b0e8`](https://github.com/talos-systems/talos/commit/0bd8b0e8008c12e4914c6e9b5faf06dda6c744f7) feat: provide an option to recover etcd from data directory copy * [`f9818540`](https://github.com/talos-systems/talos/commit/f98185408d618ebcc780247ea2c42239df27a74e) chore: fix conform with scopes * [`21018f28`](https://github.com/talos-systems/talos/commit/21018f28c732719535c30c8e1abdbb346f1dc4bf) chore: bump website node.js dependencies

### Changes since v0.11.0-alpha.1
19 commits

* [`0731be90`](https://github.com/talos-systems/talos/commit/0731be908bfe130b37db3d5f54b96f3981b1c860) feat: add cloud images to releases * [`b52b2066`](https://github.com/talos-systems/talos/commit/b52b206665ba963ceec0b7a4ff41bcee77aa8a67) feat: split etcd certificates to peer/client * [`33119d2b`](https://github.com/talos-systems/talos/commit/33119d2b8e4b48367421ed8e66aa4b11e639b2ac) chore: add an option to launch cluster with bad RTC state * [`d8c2bca1`](https://github.com/talos-systems/talos/commit/d8c2bca1b53dc9d0e7bb627fe43c629a52489dec) feat: reimplement apid certificate generation on top of COSI * [`3c1b3219`](https://github.com/talos-systems/talos/commit/3c1b32199d294bd52983c4dd57738cad29aa8738) chore: refactor CLI tests * [`0fd9ea2d`](https://github.com/talos-systems/talos/commit/0fd9ea2d63af00f7d2423c2daba2988c38cdae78) feat: enable MACVTAP support * [`898673e8`](https://github.com/talos-systems/talos/commit/898673e8d3e53a0022f2564ee26a29991c145aa8) chore: update e2e tests to use latest capi releases * [`e26c5583`](https://github.com/talos-systems/talos/commit/e26c5583c2dbe771bd50a7f9efe958cdc9c60d54) docs: add AMI IDs for Talos 0.10.4 * [`72ef48f0`](https://github.com/talos-systems/talos/commit/72ef48f0ea1898e80977f56724e931c73d7aff94) fix: assign source address to the DHCP default gateway routes * [`004885a3`](https://github.com/talos-systems/talos/commit/004885a379a8617a874bd97062eb7af00fe7dc3b) feat: update Linux kernel to 5.10.45, etcd to 3.4.16 * [`821f469a`](https://github.com/talos-systems/talos/commit/821f469a1d82e180528dc07afffde05f08a57dd1) feat: skip overlay mount checks with docker * [`b6e02311`](https://github.com/talos-systems/talos/commit/b6e02311a36a7eeb5bfb22037989f49483b9e3f0) feat: use COSI RD's sensitivity for RBAC * [`46751c1a`](https://github.com/talos-systems/talos/commit/46751c1ad2b2102ea6b8e151bdbe854d041250cb) feat: improve security of Kubernetes control plane components * [`0f659622`](https://github.com/talos-systems/talos/commit/0f659622d02260731a30d4862da99697adc7ab5c) fix: build with custom kernel/rootfs * [`5b5089ab`](https://github.com/talos-systems/talos/commit/5b5089ab95e2a7a345e18232520d9071180d9f10) fix: mark kube-proxy as system critical priority * [`42c16f67`](https://github.com/talos-systems/talos/commit/42c16f67f4476b8b57c39fea2bd3ec8168cb8193) chore: bump dependencies * [`60f78419`](https://github.com/talos-systems/talos/commit/60f78419e490f47dc1424008f33cc1baa05097b4) chore: bump etcd client libraries to final 3.5.0 release * [`2b0de9ed`](https://github.com/talos-systems/talos/commit/2b0de9edb2b0f158f12cd320ac672c3c3a5a339b) feat: improve security of Kubernetes control plane components * [`48a5c460`](https://github.com/talos-systems/talos/commit/48a5c460a140b50026210576a46a691393511461) docs: provide more storage details

### Changes from talos-systems/crypto
8 commits

* [`d3cb772`](https://github.com/talos-systems/crypto/commit/d3cb77220384b3a3119a6f3ddb1340bbc811f1d1) feat: make possible to change KeyUsage * [`6bc5bb5`](https://github.com/talos-systems/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument * [`cd18ef6`](https://github.com/talos-systems/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations * [`97c888b`](https://github.com/talos-systems/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR * [`7776057`](https://github.com/talos-systems/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos * [`80df078`](https://github.com/talos-systems/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters * [`15bdd28`](https://github.com/talos-systems/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates * [`4f80b97`](https://github.com/talos-systems/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate

### Changes from talos-systems/extras
1 commit

* [`4fe2706`](https://github.com/talos-systems/extras/commit/4fe27060347c861b716392eec3dfee698becb5f3) feat: build with Go 1.16.5

### Changes from talos-systems/go-blockdevice
3 commits

* [`30c2bc3`](https://github.com/talos-systems/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable * [`1292574`](https://github.com/talos-systems/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/talos-systems/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types

### Changes from talos-systems/go-debug
5 commits

* [`3d0a6e1`](https://github.com/talos-systems/go-debug/commit/3d0a6e1bf5e3c521e83ead2c8b7faad3638b8c5d) feat: race build tag flag detector * [`5b292e5`](https://github.com/talos-systems/go-debug/commit/5b292e50198b8ed91c434f00e2772db394dbf0b9) feat: disable memory profiling by default * [`c6d0ae2`](https://github.com/talos-systems/go-debug/commit/c6d0ae2c0ee099fa0940405401e6a02716a15bd8) fix: linters and CI * [`d969f95`](https://github.com/talos-systems/go-debug/commit/d969f952af9e02feea59963671298fc236ca4399) feat: initial implementation * [`b2044b7`](https://github.com/talos-systems/go-debug/commit/b2044b70379c84f9706de74044bd2fd6a8e891cf) Initial commit

### Changes from talos-systems/go-kmsg
2 commits

* [`2edcd3a`](https://github.com/talos-systems/go-kmsg/commit/2edcd3a913508e2d922776f729bfc4bcab031a8b) feat: add initial version * [`53cdd8d`](https://github.com/talos-systems/go-kmsg/commit/53cdd8d67b9dbab692471a2d5161e7e0b3d04cca) chore: initial commit

### Changes from talos-systems/go-loadbalancer
3 commits

* [`a445702`](https://github.com/talos-systems/go-loadbalancer/commit/a4457024d5189d754b2da4a30b14072a0e3f5f05) feat: allow dial timeout and keep alive period to be configurable * [`3c8f347`](https://github.com/talos-systems/go-loadbalancer/commit/3c8f3471d14e37866c65f73170ef83c038ae5a8c) feat: provide a way to configure logger for the loadbalancer * [`da8e987`](https://github.com/talos-systems/go-loadbalancer/commit/da8e987434c3d407679a40e213b12a8e1c98abb8) feat: implement Reconcile - ability to change upstream list on the fly

### Changes from talos-systems/go-retry
3 commits

* [`c78cc95`](https://github.com/talos-systems/go-retry/commit/c78cc953d9e95992575305b4e8648392c6c9b9e6) fix: implement `errors.Is` for all errors in the set * [`7885e16`](https://github.com/talos-systems/go-retry/commit/7885e16b2cb0267bcc8b07cdd0eced14e8005864) feat: add ExpectedErrorf * [`3d83f61`](https://github.com/talos-systems/go-retry/commit/3d83f6126c1a3a238d1d1d59bfb6273e4087bdac) feat: deprecate UnexpectedError

### Changes from talos-systems/go-smbios
1 commit

* [`d3a32be`](https://github.com/talos-systems/go-smbios/commit/d3a32bea731a0c2a60ce7f5eae60253300ef27e1) fix: return UUID in middle endian only on SMBIOS >= 2.6

### Changes from talos-systems/pkgs
22 commits

* [`41d6ccc`](https://github.com/talos-systems/pkgs/commit/41d6ccc8d40259e77da6cc46b047f265e6ebc58b) feat: enable MACVTAP support * [`96072f8`](https://github.com/talos-systems/pkgs/commit/96072f89ac6b6b7dccd25e54ebbb33eef312c8e4) feat: enable adiantum block encryption (both amd64 arm64) * [`f5eac03`](https://github.com/talos-systems/pkgs/commit/f5eac033223b1116de70c51204af3a096d9130a5) feat: update Linux to 5.10.45 * [`d756119`](https://github.com/talos-systems/pkgs/commit/d756119b2b0dfabda50a945ee16ee4fd62109cb0) feat: enable HP ILO kernel module (both amd64 arm64) * [`2d51360`](https://github.com/talos-systems/pkgs/commit/2d51360a254b237943e92cd445e42912d39fce7a) feat: support NFS 4.1 * [`e63e4e9`](https://github.com/talos-systems/pkgs/commit/e63e4e97b4c398e090028eaf7b967cc9eafadf96) feat: bump tools for Go 1.16.5 * [`1f8af29`](https://github.com/talos-systems/pkgs/commit/1f8af290e5d242f7dfc784fd2fc7fcfd714500bd) feat: update Linux to 5.10.38 * [`a3a6650`](https://github.com/talos-systems/pkgs/commit/a3a66505f36b9e9f92f4980df3708a872d56caec) feat: update containerd to 1.5.2 * [`c70ea44`](https://github.com/talos-systems/pkgs/commit/c70ea44ba4bc1ffabdb1422deda107a94e1fe94c) feat: update runc to 1.0.0-rc95 * [`db60235`](https://github.com/talos-systems/pkgs/commit/db602359cc594b35291911b4220dc5b331b323bb) feat: add support for netxen card * [`f934187`](https://github.com/talos-systems/pkgs/commit/f934187ebdc455f18cc6d2da847be3d48a6e3d8f) feat: update containerd to 1.5.1 * [`e8ed5bc`](https://github.com/talos-systems/pkgs/commit/e8ed5bcb848954ca30967de8d7c81afecdea4825) feat: add geneve encapsulation support for openvswitch * [`9f7903c`](https://github.com/talos-systems/pkgs/commit/9f7903cb5c110f77db8093347b69ec141325d47c) feat: update containerd to 1.5.0, runc to -rc94 * [`d7c0f70`](https://github.com/talos-systems/pkgs/commit/d7c0f70e41bb7bf542092f2882b062ff52f5ae44) feat: add AES-NI support for amd64 * [`b0d9cd2`](https://github.com/talos-systems/pkgs/commit/b0d9cd2c36e37190c5ce7b85acea6a51a853faaf) fix: build `zbin` utility for both amd64 and arm64 * [`bb39b97`](https://github.com/talos-systems/pkgs/commit/bb39b9744c0c4a29ccfa190a0d2cce0f8547676b) feat: add IPMI support in kernel * [`1148f9a`](https://github.com/talos-systems/pkgs/commit/1148f9a897d9a52b6013396151e1eab264709037) feat: add DS1307 RTC support for arm64 * [`350aa6f`](https://github.com/talos-systems/pkgs/commit/350aa6f200d441d7dbbf60ec8ebb39a6761d6a8b) feat: add USB serial support * [`de9c582`](https://github.com/talos-systems/pkgs/commit/de9c58238483219a574fb697ddb1126f36a02da3) feat: add Pine64 SBC support * [`b56f36b`](https://github.com/talos-systems/pkgs/commit/b56f36bedbe9270ae5cf969f8078a10345457e83) feat: enable VMware baloon kernel module * [`f87c194`](https://github.com/talos-systems/pkgs/commit/f87c19425352eb9b68d20dec987d0c484987dea9) feat: add iPXE build with embedded placeholder script * [`a8b9e71`](https://github.com/talos-systems/pkgs/commit/a8b9e71e6538d7554b7a48d1361709d5495bb4de) feat: add cpu scaling for rpi

### Changes from talos-systems/tools
1 commit

* [`c8c2a18`](https://github.com/talos-systems/tools/commit/c8c2a18b7e587e0b8464574e375a680c5a09a028) feat: update Go to 1.16.5

### Dependency Changes * **github.com/aws/aws-sdk-go** v1.27.0 **_new_** * **github.com/containerd/cgroups** 4cbc285b3327 -> v1.0.1 * **github.com/containerd/containerd** v1.4.4 -> v1.5.2 * **github.com/containerd/go-cni** v1.0.1 -> v1.0.2 * **github.com/containerd/typeurl** v1.0.1 -> v1.0.2 * **github.com/coreos/go-iptables** v0.5.0 -> v0.6.0 * **github.com/cosi-project/runtime** 10d6103c19ab -> f1649aff7641 * **github.com/docker/docker** v20.10.4 -> v20.10.7 * **github.com/emicklei/dot** v0.15.0 -> v0.16.0 * **github.com/evanphx/json-patch** v4.9.0 -> v4.11.0 * **github.com/fatih/color** v1.10.0 -> v1.12.0 * **github.com/google/go-cmp** v0.5.5 -> v0.5.6 * **github.com/google/gofuzz** v1.2.0 **_new_** * **github.com/googleapis/gnostic** v0.5.5 **_new_** * **github.com/grpc-ecosystem/go-grpc-middleware** v1.2.2 -> v1.3.0 * **github.com/hashicorp/go-getter** v1.5.2 -> v1.5.4 * **github.com/imdario/mergo** v0.3.12 **_new_** * **github.com/insomniacslk/dhcp** cc9239ac6294 -> 465dd6c35f6c * **github.com/jsimonetti/rtnetlink** 1b79e63a70a0 -> 9c52e516c709 * **github.com/magiconair/properties** v1.8.5 **_new_** * **github.com/mattn/go-isatty** v0.0.12 -> v0.0.13 * **github.com/mdlayher/arp** f72070a231fc **_new_** * **github.com/mdlayher/ethtool** 2b88debcdd43 **_new_** * **github.com/mdlayher/netlink** v1.4.0 -> v1.4.1 * **github.com/mdlayher/raw** 51b895745faf **_new_** * **github.com/mitchellh/mapstructure** v1.4.1 **_new_** * **github.com/opencontainers/runtime-spec** 4d89ac9fbff6 -> e6143ca7d51d * **github.com/pelletier/go-toml** v1.9.0 **_new_** * **github.com/rivo/tview** 8a8f78a6dd01 -> d4fb0348227b * **github.com/rs/xid** v1.2.1 -> v1.3.0 * **github.com/sirupsen/logrus** v1.8.1 **_new_** * **github.com/spf13/afero** v1.6.0 **_new_** * **github.com/spf13/cast** v1.3.1 **_new_** * **github.com/spf13/viper** v1.7.1 **_new_** * **github.com/talos-systems/crypto** 39584f1b6e54 -> d3cb77220384 * **github.com/talos-systems/extras** v0.3.0 -> v0.3.0-1-g4fe2706 * **github.com/talos-systems/go-blockdevice** 1d830a25f64f -> v0.2.1 * **github.com/talos-systems/go-debug** 3d0a6e1bf5e3 **_new_** * **github.com/talos-systems/go-kmsg** v0.1.0 **_new_** * **github.com/talos-systems/go-loadbalancer** v0.1.0 -> v0.1.1 * **github.com/talos-systems/go-retry** b9dc1a990133 -> c78cc953d9e9 * **github.com/talos-systems/go-smbios** fb425d4727e6 -> d3a32bea731a * **github.com/talos-systems/pkgs** v0.5.0-1-g5dd650b -> v0.6.0-alpha.0-12-g41d6ccc * **github.com/talos-systems/talos/pkg/machinery** 8ffb55943c71 -> 000000000000 * **github.com/talos-systems/tools** v0.5.0 -> v0.5.0-1-gc8c2a18 * **github.com/vishvananda/netns** 2eb08e3e575f **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.20.0 -> v0.51.0 * **github.com/vmware/govmomi** v0.24.0 -> v0.26.0 * **go.etcd.io/etcd/api/v3** v3.5.0-alpha.0 -> v3.5.0 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0 **_new_** * **go.etcd.io/etcd/client/v3** v3.5.0-alpha.0 -> v3.5.0 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0 **_new_** * **go.uber.org/zap** v1.17.0 **_new_** * **golang.org/x/net** e18ecbb05110 -> 04defd469f4e * **golang.org/x/oauth2** 81ed05c6b58c **_new_** * **golang.org/x/sys** 77cc2087c03b -> 59db8d763f22 * **golang.org/x/term** 6a3ed077a48d -> 6886f2dfbf5b * **golang.org/x/time** f8bda1e9f3ba -> 38a9dc6acbc6 * **golang.zx2c4.com/wireguard/wgctrl** bd2cb7843e1b -> 92e472f520a5 * **google.golang.org/appengine** v1.6.7 **_new_** * **google.golang.org/grpc** v1.37.0 -> v1.38.0 * **gopkg.in/ini.v1** v1.62.0 **_new_** * **inet.af/netaddr** 1d252cf8125e **_new_** * **k8s.io/api** v0.21.0 -> v0.21.2 * **k8s.io/apimachinery** v0.21.0 -> v0.21.2 * **k8s.io/apiserver** v0.21.0 -> v0.21.2 * **k8s.io/client-go** v0.21.0 -> v0.21.2 * **k8s.io/cri-api** v0.21.0 -> v0.21.2 * **k8s.io/kubectl** v0.21.0 -> v0.21.2 * **k8s.io/kubelet** v0.21.0 -> v0.21.2 * **k8s.io/utils** 2afb4311ab10 **_new_** * **sigs.k8s.io/structured-merge-diff/v4** v4.1.1 **_new_** Previous release can be found at [v0.10.0](https://github.com/talos-systems/talos/releases/tag/v0.10.0) ## [Talos 0.11.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.11.0-alpha.1) (2021-06-18) Welcome to the v0.11.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Default to Bootstrap workflow The `init.yaml` is no longer an output of `talosctl gen config`. We now encourage using the bootstrap API, instead of `init` node types, as we intend on deprecating this machine type in the future. The `init.yaml` and `controlplane.yaml` machine configs are identical with the exception of the machine type. Users can use a modified `controlplane.yaml` with the machine type set to `init` if they would like to avoid using the bootstrap API. ### Component Updates * containerd was updated to 1.5.2 * Linux kernel was updated to 5.10.38 ### CoreDNS Added the flag `cluster.coreDNS.disabled` to coreDNS deployment during the cluster bootstrap. ### Legacy BIOS Support Added an option to the `machine.install` section of the machine config that can enable marking MBR partition bootable for the machines that have legacy BIOS which does not support GPT partitioning scheme. ### Multi-arch Installer Talos installer image (for any arch) now contains artifacts for both `amd64` and `arm64` architecture. This means that e.g. images for arm64 SBCs can be generated on amd64 host. ### Networking Configuration Talos networking configuration was completely rewritten to be based on controllers and resources. There are no changes to the machine configuration, but any update to `.machine.network` can now be applied in immediate mode (without a reboot). Talos should be setting up network configuration much faster on boot now, not blocking on DHCP for unconfigured interfaces and skipping the reset network step. ### Talos API RBAC Limited RBAC support in Talos API is now enabled by default for Talos 0.11. Default `talosconfig` has `os:admin` role embedded in the certificate so that all the APIs are available. Certificates with reduced set of roles can be created with `talosctl config new` command. When upgrading from Talos 0.10, RBAC is not enabled by default. Before enabling RBAC, generate `talosconfig` with `os:admin` role first to make sure that administrator still have access to the cluster when RBAC is enabled. List of available roles: * `os:admin` role enables every Talos API * `os:reader` role limits access to read-only APIs which do not return sensitive informtation * `os:etcd:backup` role only allows `talosctl etcd snapshot` API call (for etcd backup automation) ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Jorik Jonker * Spencer Smith * Andrew Rynhard * Serge Logvinov * Andrew LeCody * Kevin Hellemun * Boran Car * Brandon Nason * Gabor Nyiri * Joost Coelingh * Lance R. Vick * Lennard Klein * Seán C McCord * Sébastien Bernard * Sébastien Bernard ### Changes
143 commits

* [`f8e1cf09`](https://github.com/talos-systems/talos/commit/f8e1cf09d09c5a3d8c8ed0bdcae3d564a97e6446) release(v0.11.0-alpha.1): prepare release * [`70ac771e`](https://github.com/talos-systems/talos/commit/70ac771e0846247dbebf484aca20ef950d8b99c7) fix: use localhost API server endpoint for internal communication * [`a941eb7d`](https://github.com/talos-systems/talos/commit/a941eb7da06246d59cec1b63883f2d7e3f91ce73) feat: improve security of Kubernetes control plane components * [`3aae94e5`](https://github.com/talos-systems/talos/commit/3aae94e5306c0d6e31df4aee127ee3562709edd3) feat: provide Kubernetes nodename as a COSI resource * [`06209bba`](https://github.com/talos-systems/talos/commit/06209bba2867829561a60f0e7cd9847fa9a8edd3) chore: update RBAC rules, remove old APIs * [`9f24b519`](https://github.com/talos-systems/talos/commit/9f24b519dce07ce05099b242ba95e8a1e319630e) chore: remove bootkube check from cluster health check * [`4ac9bea2`](https://github.com/talos-systems/talos/commit/4ac9bea27dc098ebdfdc0958f3000d960fad50de) fix: stop etcd client logs from going to the server console * [`f63ab9dd`](https://github.com/talos-systems/talos/commit/f63ab9dd9bb6c734873dc8073892f5f10a2ed2e1) feat: implement `talosctl config new` command * [`fa15a668`](https://github.com/talos-systems/talos/commit/fa15a6687fc56820fbc5566d494bedbc1a5f600f) fix: don't enable RBAC feature in the config for Talos < 0.11 * [`2dc27d99`](https://github.com/talos-systems/talos/commit/2dc27d9964fa3df08a6ec11c0b045d7325ea0d2b) fix: do not format state partition in the initialize sequence * [`b609f33c`](https://github.com/talos-systems/talos/commit/b609f33cdebb0659738d4fa3802035b2b344b9b9) fix: update networking stack after Equnix Metal testing * [`243a3b53`](https://github.com/talos-systems/talos/commit/243a3b53e0e7591d5958a3b8373ab963990c40d6) fix: separate healthy and unknown flags in the service resource * [`1a1378be`](https://github.com/talos-systems/talos/commit/1a1378be16fdce45273bdc81fb72715c4766ee4b) fix: update retry package with a fix for errors.Is * [`cb83edd7`](https://github.com/talos-systems/talos/commit/cb83edd7fcf14bd199950a04e366fc573bcf4270) fix: wait for the network to be ready in mainteancne mode * [`96f89071`](https://github.com/talos-systems/talos/commit/96f89071c3ecd809d912762e40cb9d98ce052018) feat: update controller-runtime logs to console level on config.debug * [`973069b6`](https://github.com/talos-systems/talos/commit/973069b611456f758037c9ca4dc50a4a84e7a59c) feat: support NFS 4.1 * [`654dcad4`](https://github.com/talos-systems/talos/commit/654dcad4753211599d12655ec0f0466f27f49589) chore: bump dependencies via dependabot * [`d7394457`](https://github.com/talos-systems/talos/commit/d7394457d978d073690bec589ea78d957539e333) fix: don't treat ethtool errors as fatal * [`f2ae9cd0`](https://github.com/talos-systems/talos/commit/f2ae9cd0c1b7d27b5b9971f4820e5feae7934124) feat: replace networkd with new network implementation * [`caec3063`](https://github.com/talos-systems/talos/commit/caec3063c82777f82599632ca4914a58515cb9a9) fix: do not complain about empty roles * [`11918a11`](https://github.com/talos-systems/talos/commit/11918a110a628d7e0b8749fce92ef572aca47874) docs: update community meeting time * [`aeddb9c0`](https://github.com/talos-systems/talos/commit/aeddb9c0977a51e7aca72f69edda8b69d917db13) feat: implement platform config controller (hostnames) * [`1ece334d`](https://github.com/talos-systems/talos/commit/1ece334da9d7bb247c385dba08202345b83c1a0f) feat: implement controller which runs network operators * [`744ea8a5`](https://github.com/talos-systems/talos/commit/744ea8a5d4b4cb4ff69c2c2fc636e499af892fee) fix: do not add bootstrap contents option if tail events is not 0 * [`5029edfb`](https://github.com/talos-systems/talos/commit/5029edfb71990581515cabe9634d0519a9988316) fix: overwrite nodes in the gRPC metadata * [`6a35c8f1`](https://github.com/talos-systems/talos/commit/6a35c8f110abaf0017530650c55a34f1caae6288) feat: implement virtual IP (shared IP) network operator * [`0f3b8380`](https://github.com/talos-systems/talos/commit/0f3b83803d812a30e1418666fa5758734c20e5c2) chore: expose WatchRequest in the resources client * [`11e258b1`](https://github.com/talos-systems/talos/commit/11e258b15097493d2b4efd596b2fde2d52579455) feat: implement operator configuration controller * [`ce3815e7`](https://github.com/talos-systems/talos/commit/ce3815e75e889de32d9473a23e75863f56b893da) feat: implement DHCP6 operator * [`f010d99a`](https://github.com/talos-systems/talos/commit/f010d99afbc6095ad8fe218187fda306c59d3e1e) feat: implement operator framework with DHCP4 as the first example * [`f93c9c8f`](https://github.com/talos-systems/talos/commit/f93c9c8fa607a5116274d7e090f49568d01814e7) feat: bring unconfigured links with link carrier up by default * [`02bd657b`](https://github.com/talos-systems/talos/commit/02bd657b252ae64ea054b2dc338e55ce9352b420) feat: implement network.Status resource and controller * [`da329f00`](https://github.com/talos-systems/talos/commit/da329f00ab0af9f670207da1e13541aef36c4ca6) feat: enable RBAC by default * [`0f168a88`](https://github.com/talos-systems/talos/commit/0f168a880143141d8637d21aa9da403383dcf025) feat: add configuration for enabling RBAC * [`e74f789b`](https://github.com/talos-systems/talos/commit/e74f789b01b9910f8193415dcefb4b32abcb5f5c) feat: implement EtcFileController to render files in `/etc` * [`5aede1a8`](https://github.com/talos-systems/talos/commit/5aede1a83313152bd83891d0cae4b388a54bd9c2) fix: prefer extraConfig over OVF env, skip empty config * [`5ad314fe`](https://github.com/talos-systems/talos/commit/5ad314fe7e7cfca8196770071d52b93aa4f767f6) feat: implement basic RBAC interceptors * [`c031be81`](https://github.com/talos-systems/talos/commit/c031be8139dbe1f803b70fc9941cfe438b9ddeb9) chore: use Go 1.16.5 * [`8b0763f6`](https://github.com/talos-systems/talos/commit/8b0763f6a20691d36d2c82f2a756171c55450a8a) chore: bump dependencies via dependabot * [`8b8de11d`](https://github.com/talos-systems/talos/commit/8b8de11d9f4d1b1fde43b7fdd56b96d5e3eb5413) feat: implement new controllers for hostname, resolvers and time servers * [`24859b14`](https://github.com/talos-systems/talos/commit/24859b14108df7c5895022043d02d4d5ca7660a4) docs: update Rpi4 firmware guide * [`62c702c4`](https://github.com/talos-systems/talos/commit/62c702c4fd6e7a11654f542bbe31d1adfc896731) fix: remove conflicting etcd member on rejoin with empty data directory * [`ff62a599`](https://github.com/talos-systems/talos/commit/ff62a59984ef0c61dcf549ab38d39584e3630724) fix: drop into maintenance mode if config URL is `none` (metal) * [`14e696d0`](https://github.com/talos-systems/talos/commit/14e696d068b5d895b4fefc06bc6d26b4ac2bc450) feat: update COSI runtime and add support for tail in the Talos gRPC * [`a71053fc`](https://github.com/talos-systems/talos/commit/a71053fcd88d7651e536ce29b574e18f84678f3e) feat: default to bootstrap workflow * [`76aac4bb`](https://github.com/talos-systems/talos/commit/76aac4bb25d8bc6a86458b8ac5be10ca67f236be) feat: implement CPU and Memory stats controller * [`8f90c6a8`](https://github.com/talos-systems/talos/commit/8f90c6a8e1d76a3ddecc99be4e4b9f0ce0235daa) feat: parse Talos-specific cmdline params * [`ed10e139`](https://github.com/talos-systems/talos/commit/ed10e139c161b0a6e0f3460e21e4e1752b26cb46) feat: implement NodeAddress controller * [`33db8857`](https://github.com/talos-systems/talos/commit/33db8857aaf6e411464d08c51560473455e8e156) fix: use COSI runtime DestroyReady input type * [`6e775363`](https://github.com/talos-systems/talos/commit/6e775363920b7869b83775d1b674807163039eb1) refactor: rename *.Status() to *.TypedSpec() in the resources * [`97627061`](https://github.com/talos-systems/talos/commit/97627061d7e8de90e2f2745efa7497137447d116) docs: set static IP on ISO install mode * [`5811f4dd`](https://github.com/talos-systems/talos/commit/5811f4dda1b62848eefae9be56e8b91d443f4d34) feat: implement link (interface) controllers * [`046b229b`](https://github.com/talos-systems/talos/commit/046b229b13708c3ffe1d77b8884242fc100097d0) chore: skip building multi-arch installer for race-enabled build * [`73fbb4b5`](https://github.com/talos-systems/talos/commit/73fbb4b523b41d266840eced306242d57a332b4d) fix: only fetch machine uuid if it's not set * [`f112a540`](https://github.com/talos-systems/talos/commit/f112a540b0e776f06820ee900d6ce9f4f2de02ec) fix: clean up stale snapshots on container start * [`c036b949`](https://github.com/talos-systems/talos/commit/c036b949486d94cbbce54c7511633d398f75797c) chore: bump dependencies * [`a4d67a01`](https://github.com/talos-systems/talos/commit/a4d67a01820894d3ebf8c65a06345232fae4f93b) feat: add the ability to disable CoreDNS * [`76dbfb36`](https://github.com/talos-systems/talos/commit/76dbfb3699df0725a8acf29bff39c43e4aa34f9d) feat: add ability to mark MBR partition bootable * [`e0f5b1e2`](https://github.com/talos-systems/talos/commit/e0f5b1e20aa0d22898274ddc0f9026c0d813cee2) chore: split mgmt/gen.go into several files * [`fad1b4f1`](https://github.com/talos-systems/talos/commit/fad1b4f1fdce962b779ceb960f81d572ee5033af) chore: fix go generate for the machinery * [`1117294a`](https://github.com/talos-systems/talos/commit/1117294ad21945d24b0954d223cc4996df01dd81) release(v0.11.0-alpha.0): prepare release * [`c0962946`](https://github.com/talos-systems/talos/commit/c09629466321f4d220454164784edf41fd3d5813) chore: prepare for 0.11 release series * [`72359765`](https://github.com/talos-systems/talos/commit/723597657ad78e9766190ea2e110208c62d0093b) feat: enable GORACE=halt_on_panic=1 in machined binary * [`0acb04ad`](https://github.com/talos-systems/talos/commit/0acb04ad7a2a0a7b75471f0251b0e04eccd927cd) feat: implement route network controllers * [`f5bf88a4`](https://github.com/talos-systems/talos/commit/f5bf88a4c2ab8f48fd93bc7ac13543c613bf9bd1) feat: create certificates with os:admin role * [`1db301ed`](https://github.com/talos-systems/talos/commit/1db301edf6a4057814a6d5b8f87fbfe1e020caeb) feat: switch controller-runtime to zap.Logger * [`f7cf64d4`](https://github.com/talos-systems/talos/commit/f7cf64d42ec77ca68408ecb0f437ab5f86bc787a) fix: add talos.config to the vApp Properties in VMware OVA * [`209527ec`](https://github.com/talos-systems/talos/commit/209527eccc6c93edad33a01a3f3d24fb978f2f07) docs: add AMIs for Talos 0.10.3 * [`59cfd312`](https://github.com/talos-systems/talos/commit/59cfd312c1ac531528c4ceb2adeb3f85829cc4e1) chore: bump dependencies via dependabot * [`1edb20cf`](https://github.com/talos-systems/talos/commit/1edb20cf98fe2e641cefc658d17206e09acabc26) feat: extract config generation * [`af77c295`](https://github.com/talos-systems/talos/commit/af77c29565b65766d135884ec7740f67b56626e3) docs: update wirguard guide * [`4fe69121`](https://github.com/talos-systems/talos/commit/4fe691214366c08ea846bdc6233dd592da0d4769) test: better `talosctl ls` tests * [`04ddda96`](https://github.com/talos-systems/talos/commit/04ddda962fbcfdeaae59d232e7bb7f9c5bb63bc7) feat: update containerd to 1.5.2, runc to 1.0.0-rc95 * [`49c7276b`](https://github.com/talos-systems/talos/commit/49c7276b16a82b7da8c83f8bd930361768f0e249) chore: fix markdown linting * [`7270495a`](https://github.com/talos-systems/talos/commit/7270495ace9faf48a73829bbed0e4eb2c939eecb) docs: add mayastor quickstart * [`d3d9112f`](https://github.com/talos-systems/talos/commit/d3d9112f288d3b0f3ebe1c8b28b1c4e2fc8512b2) docs: fix spelling/grammar in What's New for Talos 0.9 * [`82804414`](https://github.com/talos-systems/talos/commit/82804414fc2fcb21da77edc2fbbefe92a14fc30d) test: provide a way to force different boot order in provision library * [`a1c0e99a`](https://github.com/talos-systems/talos/commit/a1c0e99a1729c704a633dcc557dc46466b828e11) docs: add guide for deploying metrics-server * [`6bc6658b`](https://github.com/talos-systems/talos/commit/6bc6658b518379d418baafcf9b1045a3b84f48ec) feat: update containerd to 1.5.1 * [`c6567fae`](https://github.com/talos-systems/talos/commit/c6567fae9c59da5148c9876289a4bf248240b99d) chore: dependabot updates * [`61ccbb3f`](https://github.com/talos-systems/talos/commit/61ccbb3f5a2564376af13ea9bbfe51e364fcb3a1) chore: keep debug symbols in debug builds * [`1ce362e0`](https://github.com/talos-systems/talos/commit/1ce362e05e41cd76cdda17a6fc971767e036df37) docs: update customizing kernel build steps * [`a26174b5`](https://github.com/talos-systems/talos/commit/a26174b54846bdfa0b66d2f9147bfe1dc8f2eb52) fix: properly compose pattern and header in etcd members output * [`0825cf11`](https://github.com/talos-systems/talos/commit/0825cf11f412eef930db269b6cae02d059058101) fix: stop networkd and pods before leaving etcd on upgrade * [`bed6b15d`](https://github.com/talos-systems/talos/commit/bed6b15d6fcf0634a887b79797d639e221fe9387) fix: properly populate AllowSchedulingOnMasters option in gen config RPC * [`071f0445`](https://github.com/talos-systems/talos/commit/071f044562dd247dd54584d7b9fa0bb24d6f7599) feat: implement AddressSpec handling * [`76e38b7b`](https://github.com/talos-systems/talos/commit/76e38b7b8251548292ae15ecda2bfa1c8ddc5cf3) feat: update Kubernetes to 1.21.1 * [`9b1338d9`](https://github.com/talos-systems/talos/commit/9b1338d989e6cdf7e0b6d5fe1ba3c32d27fc2251) chore: parse "boolean" variables * [`c81cfb21`](https://github.com/talos-systems/talos/commit/c81cfb21670b82e518cf4c32230e8fbbce6be8ff) chore: allow building with debug handlers * [`c9651673`](https://github.com/talos-systems/talos/commit/c9651673b9eaf811ae4acfed313debbf78bd80e8) feat: update go-smbios library * [`95c656fb`](https://github.com/talos-systems/talos/commit/95c656fb72b6b858b55dae37020cb59ba26115f8) feat: update containerd to 1.5.0, runc to 1.0.0-rc94 * [`db9c35b5`](https://github.com/talos-systems/talos/commit/db9c35b570b39f4423f4636f9e9f1d14cac5d7c1) feat: implement AddressStatusController * [`1cf011a8`](https://github.com/talos-systems/talos/commit/1cf011a809b924fc8f2083566d169704c6e07cd5) chore: bump dependencies via dependabot * [`e3f407a1`](https://github.com/talos-systems/talos/commit/e3f407a1dff3f4ee7e024bbfb64f17b5cb5d625d) fix: properly pass disk type selector from config to matcher * [`66b2b450`](https://github.com/talos-systems/talos/commit/66b2b450582593e93598fac80c8b3c29e8c8a944) feat: add resources and use HTTPS checks in control plane pods * [`4ffd7c0a`](https://github.com/talos-systems/talos/commit/4ffd7c0adf281033ac02d37ca434e7f9ad71e692) fix: stop networkd before leaving etcd on 'reset' path * [`610d38d3`](https://github.com/talos-systems/talos/commit/610d38d309dabaa623494ade12234f1ccf018a9e) docs: add AMIs for 0.10.1, collapse list of AMIs by default * [`807497ec`](https://github.com/talos-systems/talos/commit/807497ec20dee15953186bda0fe7a45ffec0307c) chore: make conformance pipeline depend on cron-default * [`3c121359`](https://github.com/talos-systems/talos/commit/3c1213596cdf03daf09050103f57b29e756439b1) feat: implement LinkStatusController * [`0e8de046`](https://github.com/talos-systems/talos/commit/0e8de04698aac95062f3037da0a9af8b6ee916b0) fix: update go-blockdevice to fix disk type detection * [`4d50a4ed`](https://github.com/talos-systems/talos/commit/4d50a4edd0eb413c16e899536ccdc2642e37aeaa) fix: update the way NTP sync uses `adjtimex` syscall * [`1a85c14a`](https://github.com/talos-systems/talos/commit/1a85c14a51fdab43ae84274563bf89b30e4e6d92) fix: avoid data race on CRI pod stop * [`5de8dbc0`](https://github.com/talos-systems/talos/commit/5de8dbc06c7ed36c8f3af9adea8b1abedeb372b6) fix: repair pine64 support * [`38239097`](https://github.com/talos-systems/talos/commit/3823909735859f2ac5d95bc39c051fc9c2c07685) fix: properly parse matcher expressions * [`e54b6b7a`](https://github.com/talos-systems/talos/commit/e54b6b7a3d7412ddce1467dfbd35efe3cfd76f3f) chore: update dependencies via dependabot * [`f2caed0d`](https://github.com/talos-systems/talos/commit/f2caed0df5b76c4a719f968191081a6e5e2e95c7) chore: use extracted talos-systems/go-kmsg library * [`79d804c5`](https://github.com/talos-systems/talos/commit/79d804c5b4af50a0fd73db17d2522d6a6b45c9ca) docs: fix typos * [`a2bb390e`](https://github.com/talos-systems/talos/commit/a2bb390e1d56106d6d3c1526f3f76b34846b0274) feat: deterministic builds * [`e480fedf`](https://github.com/talos-systems/talos/commit/e480fedff047233e78ad2c22e7b84cbbb22798d5) feat: add USB serial drivers * [`79299d76`](https://github.com/talos-systems/talos/commit/79299d761c50aff386ab7b3c12f39c1797585632) docs: add Matrix room links * [`1b3e8b09`](https://github.com/talos-systems/talos/commit/1b3e8b09edcd51cf3df2d43d14c8fbf1e912a465) docs: add survey to README * [`8d51c9bb`](https://github.com/talos-systems/talos/commit/8d51c9bb190c2c60fa9be6a00572d2eaf4221e94) docs: update redirects to Talos 0.10 * [`1092c3a5`](https://github.com/talos-systems/talos/commit/1092c3a5069a3add439860d90c3615111fa03c98) feat: add Pine64 SBC support * [`63e01754`](https://github.com/talos-systems/talos/commit/63e0175437e45c8f7e5296841337a640c600982c) feat: pull kernel with VMware balloon module enabled * [`aeec99d8`](https://github.com/talos-systems/talos/commit/aeec99d8247f4eb534e0db1ed639f95cd726fe08) chore: remove temporary fork * [`0f49722d`](https://github.com/talos-systems/talos/commit/0f49722d0ff4e731f17a55d1ca50472714334748) feat: add `--config-patch` flag by node type * [`a01b1d22`](https://github.com/talos-systems/talos/commit/a01b1d22d9f3fa94355817217fefd80fe34628f3) chore: dump dependencies via dependabot * [`d540a4a4`](https://github.com/talos-systems/talos/commit/d540a4a4711367a0ada203f668382e39876ba081) fix: bump crypto library for the CSR verification fix * [`c3a4173e`](https://github.com/talos-systems/talos/commit/c3a4173e11a92c2bc51ea4f284ad38c9750105d2) chore: remove security API ReadFile/WriteFile * [`38037131`](https://github.com/talos-systems/talos/commit/38037131cddc2aefbae0f48fb7e355ec76247b67) chore: update wgctrl dependecy * [`d9ba0fd0`](https://github.com/talos-systems/talos/commit/d9ba0fd0164b2bfb2bc4ffe7a2d9d6c665a38e4d) docs: create v0.11 docs, promote v0.10 docs, add v0.10 AMIs * [`2261d7ed`](https://github.com/talos-systems/talos/commit/2261d7ed0212c287273eac647647e4390c530a6e) fix: use both self-signed and Kubernetes CA to verify Kubelet cert * [`a3537a69`](https://github.com/talos-systems/talos/commit/a3537a691320430eeb7149abe73419ee242312fc) docs: update cloud images for Talos v0.9.3 * [`5b9ee861`](https://github.com/talos-systems/talos/commit/5b9ee86179fb92989b02533d6d6745a5b0f37566) docs: add what's new for Talos 0.10 * [`f1107fa3`](https://github.com/talos-systems/talos/commit/f1107fa3a33955f3aa57a49991c87f9ee47b6e67) docs: add survey * [`93623d47`](https://github.com/talos-systems/talos/commit/93623d47f24fef0d149fa006678b61e3182ef771) docs: update AWS instructions * [`a739d1b8`](https://github.com/talos-systems/talos/commit/a739d1b8adbc026796d1c55f7319677f9010f727) feat: add support of custom registry CA certificate usage * [`7f468d35`](https://github.com/talos-systems/talos/commit/7f468d350a6f80d2815149376fa24f7d7629402c) fix: update osType in OVA other3xLinux64Guest" * [`4a184b67`](https://github.com/talos-systems/talos/commit/4a184b67d6ae25b21b35373e7dd6eab41b042c96) docs: add etcd backup and restore guide * [`5fb38d3e`](https://github.com/talos-systems/talos/commit/5fb38d3e5f201934d64bae186c5300e7de7af3d4) chore: refactor Dockerfile for cross-compilation * [`a8f1e526`](https://github.com/talos-systems/talos/commit/a8f1e526bfc00107c915572df2be08b3f154f4e6) chore: build talosctl for Darwin / Apple Silicon * [`eb0b64d3`](https://github.com/talos-systems/talos/commit/eb0b64d3138228a6c751387c720ca81c338b834d) chore: list specifically for enabled regions * [`669a0cbd`](https://github.com/talos-systems/talos/commit/669a0cbdc4756f0ad8f0dacc56a20f71e96fe4cd) fix: check if OVF env is empty * [`da92049c`](https://github.com/talos-systems/talos/commit/da92049c0b4beae32af80205f50849443cd6dad3) chore: use codecov from the build container * [`9996d4b0`](https://github.com/talos-systems/talos/commit/9996d4b028f3845071850def75f2b534e4d2b190) chore: use REGISTRY_MIRROR_FLAGS if defined * [`05cbe250`](https://github.com/talos-systems/talos/commit/05cbe250c87339e097d435d6b10b9d8a5f2eb49e) chore: bump dependencies via dependabot * [`9a91142a`](https://github.com/talos-systems/talos/commit/9a91142a38b3b1f210773acf8df01ed6a45599c2) feat: print complete member info in etcd members * [`bb40d6dd`](https://github.com/talos-systems/talos/commit/bb40d6dd06a967464c24ab33744bbf460aa84038) feat: update pkgs version * [`e7a9164b`](https://github.com/talos-systems/talos/commit/e7a9164b1e1630f953a420d99c865aef6e652d15) test: implement `talosctl conformance` command to run e2e tests * [`6cb266e7`](https://github.com/talos-systems/talos/commit/6cb266e74e60d9d5423feaad550a7861dc73f11d) fix: update etcd client errors, print etcd join failures * [`0bd8b0e8`](https://github.com/talos-systems/talos/commit/0bd8b0e8008c12e4914c6e9b5faf06dda6c744f7) feat: provide an option to recover etcd from data directory copy * [`f9818540`](https://github.com/talos-systems/talos/commit/f98185408d618ebcc780247ea2c42239df27a74e) chore: fix conform with scopes * [`21018f28`](https://github.com/talos-systems/talos/commit/21018f28c732719535c30c8e1abdbb346f1dc4bf) chore: bump website node.js dependencies

### Changes since v0.11.0-alpha.0
60 commits

* [`f8e1cf09`](https://github.com/talos-systems/talos/commit/f8e1cf09d09c5a3d8c8ed0bdcae3d564a97e6446) release(v0.11.0-alpha.1): prepare release * [`70ac771e`](https://github.com/talos-systems/talos/commit/70ac771e0846247dbebf484aca20ef950d8b99c7) fix: use localhost API server endpoint for internal communication * [`a941eb7d`](https://github.com/talos-systems/talos/commit/a941eb7da06246d59cec1b63883f2d7e3f91ce73) feat: improve security of Kubernetes control plane components * [`3aae94e5`](https://github.com/talos-systems/talos/commit/3aae94e5306c0d6e31df4aee127ee3562709edd3) feat: provide Kubernetes nodename as a COSI resource * [`06209bba`](https://github.com/talos-systems/talos/commit/06209bba2867829561a60f0e7cd9847fa9a8edd3) chore: update RBAC rules, remove old APIs * [`9f24b519`](https://github.com/talos-systems/talos/commit/9f24b519dce07ce05099b242ba95e8a1e319630e) chore: remove bootkube check from cluster health check * [`4ac9bea2`](https://github.com/talos-systems/talos/commit/4ac9bea27dc098ebdfdc0958f3000d960fad50de) fix: stop etcd client logs from going to the server console * [`f63ab9dd`](https://github.com/talos-systems/talos/commit/f63ab9dd9bb6c734873dc8073892f5f10a2ed2e1) feat: implement `talosctl config new` command * [`fa15a668`](https://github.com/talos-systems/talos/commit/fa15a6687fc56820fbc5566d494bedbc1a5f600f) fix: don't enable RBAC feature in the config for Talos < 0.11 * [`2dc27d99`](https://github.com/talos-systems/talos/commit/2dc27d9964fa3df08a6ec11c0b045d7325ea0d2b) fix: do not format state partition in the initialize sequence * [`b609f33c`](https://github.com/talos-systems/talos/commit/b609f33cdebb0659738d4fa3802035b2b344b9b9) fix: update networking stack after Equnix Metal testing * [`243a3b53`](https://github.com/talos-systems/talos/commit/243a3b53e0e7591d5958a3b8373ab963990c40d6) fix: separate healthy and unknown flags in the service resource * [`1a1378be`](https://github.com/talos-systems/talos/commit/1a1378be16fdce45273bdc81fb72715c4766ee4b) fix: update retry package with a fix for errors.Is * [`cb83edd7`](https://github.com/talos-systems/talos/commit/cb83edd7fcf14bd199950a04e366fc573bcf4270) fix: wait for the network to be ready in mainteancne mode * [`96f89071`](https://github.com/talos-systems/talos/commit/96f89071c3ecd809d912762e40cb9d98ce052018) feat: update controller-runtime logs to console level on config.debug * [`973069b6`](https://github.com/talos-systems/talos/commit/973069b611456f758037c9ca4dc50a4a84e7a59c) feat: support NFS 4.1 * [`654dcad4`](https://github.com/talos-systems/talos/commit/654dcad4753211599d12655ec0f0466f27f49589) chore: bump dependencies via dependabot * [`d7394457`](https://github.com/talos-systems/talos/commit/d7394457d978d073690bec589ea78d957539e333) fix: don't treat ethtool errors as fatal * [`f2ae9cd0`](https://github.com/talos-systems/talos/commit/f2ae9cd0c1b7d27b5b9971f4820e5feae7934124) feat: replace networkd with new network implementation * [`caec3063`](https://github.com/talos-systems/talos/commit/caec3063c82777f82599632ca4914a58515cb9a9) fix: do not complain about empty roles * [`11918a11`](https://github.com/talos-systems/talos/commit/11918a110a628d7e0b8749fce92ef572aca47874) docs: update community meeting time * [`aeddb9c0`](https://github.com/talos-systems/talos/commit/aeddb9c0977a51e7aca72f69edda8b69d917db13) feat: implement platform config controller (hostnames) * [`1ece334d`](https://github.com/talos-systems/talos/commit/1ece334da9d7bb247c385dba08202345b83c1a0f) feat: implement controller which runs network operators * [`744ea8a5`](https://github.com/talos-systems/talos/commit/744ea8a5d4b4cb4ff69c2c2fc636e499af892fee) fix: do not add bootstrap contents option if tail events is not 0 * [`5029edfb`](https://github.com/talos-systems/talos/commit/5029edfb71990581515cabe9634d0519a9988316) fix: overwrite nodes in the gRPC metadata * [`6a35c8f1`](https://github.com/talos-systems/talos/commit/6a35c8f110abaf0017530650c55a34f1caae6288) feat: implement virtual IP (shared IP) network operator * [`0f3b8380`](https://github.com/talos-systems/talos/commit/0f3b83803d812a30e1418666fa5758734c20e5c2) chore: expose WatchRequest in the resources client * [`11e258b1`](https://github.com/talos-systems/talos/commit/11e258b15097493d2b4efd596b2fde2d52579455) feat: implement operator configuration controller * [`ce3815e7`](https://github.com/talos-systems/talos/commit/ce3815e75e889de32d9473a23e75863f56b893da) feat: implement DHCP6 operator * [`f010d99a`](https://github.com/talos-systems/talos/commit/f010d99afbc6095ad8fe218187fda306c59d3e1e) feat: implement operator framework with DHCP4 as the first example * [`f93c9c8f`](https://github.com/talos-systems/talos/commit/f93c9c8fa607a5116274d7e090f49568d01814e7) feat: bring unconfigured links with link carrier up by default * [`02bd657b`](https://github.com/talos-systems/talos/commit/02bd657b252ae64ea054b2dc338e55ce9352b420) feat: implement network.Status resource and controller * [`da329f00`](https://github.com/talos-systems/talos/commit/da329f00ab0af9f670207da1e13541aef36c4ca6) feat: enable RBAC by default * [`0f168a88`](https://github.com/talos-systems/talos/commit/0f168a880143141d8637d21aa9da403383dcf025) feat: add configuration for enabling RBAC * [`e74f789b`](https://github.com/talos-systems/talos/commit/e74f789b01b9910f8193415dcefb4b32abcb5f5c) feat: implement EtcFileController to render files in `/etc` * [`5aede1a8`](https://github.com/talos-systems/talos/commit/5aede1a83313152bd83891d0cae4b388a54bd9c2) fix: prefer extraConfig over OVF env, skip empty config * [`5ad314fe`](https://github.com/talos-systems/talos/commit/5ad314fe7e7cfca8196770071d52b93aa4f767f6) feat: implement basic RBAC interceptors * [`c031be81`](https://github.com/talos-systems/talos/commit/c031be8139dbe1f803b70fc9941cfe438b9ddeb9) chore: use Go 1.16.5 * [`8b0763f6`](https://github.com/talos-systems/talos/commit/8b0763f6a20691d36d2c82f2a756171c55450a8a) chore: bump dependencies via dependabot * [`8b8de11d`](https://github.com/talos-systems/talos/commit/8b8de11d9f4d1b1fde43b7fdd56b96d5e3eb5413) feat: implement new controllers for hostname, resolvers and time servers * [`24859b14`](https://github.com/talos-systems/talos/commit/24859b14108df7c5895022043d02d4d5ca7660a4) docs: update Rpi4 firmware guide * [`62c702c4`](https://github.com/talos-systems/talos/commit/62c702c4fd6e7a11654f542bbe31d1adfc896731) fix: remove conflicting etcd member on rejoin with empty data directory * [`ff62a599`](https://github.com/talos-systems/talos/commit/ff62a59984ef0c61dcf549ab38d39584e3630724) fix: drop into maintenance mode if config URL is `none` (metal) * [`14e696d0`](https://github.com/talos-systems/talos/commit/14e696d068b5d895b4fefc06bc6d26b4ac2bc450) feat: update COSI runtime and add support for tail in the Talos gRPC * [`a71053fc`](https://github.com/talos-systems/talos/commit/a71053fcd88d7651e536ce29b574e18f84678f3e) feat: default to bootstrap workflow * [`76aac4bb`](https://github.com/talos-systems/talos/commit/76aac4bb25d8bc6a86458b8ac5be10ca67f236be) feat: implement CPU and Memory stats controller * [`8f90c6a8`](https://github.com/talos-systems/talos/commit/8f90c6a8e1d76a3ddecc99be4e4b9f0ce0235daa) feat: parse Talos-specific cmdline params * [`ed10e139`](https://github.com/talos-systems/talos/commit/ed10e139c161b0a6e0f3460e21e4e1752b26cb46) feat: implement NodeAddress controller * [`33db8857`](https://github.com/talos-systems/talos/commit/33db8857aaf6e411464d08c51560473455e8e156) fix: use COSI runtime DestroyReady input type * [`6e775363`](https://github.com/talos-systems/talos/commit/6e775363920b7869b83775d1b674807163039eb1) refactor: rename *.Status() to *.TypedSpec() in the resources * [`97627061`](https://github.com/talos-systems/talos/commit/97627061d7e8de90e2f2745efa7497137447d116) docs: set static IP on ISO install mode * [`5811f4dd`](https://github.com/talos-systems/talos/commit/5811f4dda1b62848eefae9be56e8b91d443f4d34) feat: implement link (interface) controllers * [`046b229b`](https://github.com/talos-systems/talos/commit/046b229b13708c3ffe1d77b8884242fc100097d0) chore: skip building multi-arch installer for race-enabled build * [`73fbb4b5`](https://github.com/talos-systems/talos/commit/73fbb4b523b41d266840eced306242d57a332b4d) fix: only fetch machine uuid if it's not set * [`f112a540`](https://github.com/talos-systems/talos/commit/f112a540b0e776f06820ee900d6ce9f4f2de02ec) fix: clean up stale snapshots on container start * [`c036b949`](https://github.com/talos-systems/talos/commit/c036b949486d94cbbce54c7511633d398f75797c) chore: bump dependencies * [`a4d67a01`](https://github.com/talos-systems/talos/commit/a4d67a01820894d3ebf8c65a06345232fae4f93b) feat: add the ability to disable CoreDNS * [`76dbfb36`](https://github.com/talos-systems/talos/commit/76dbfb3699df0725a8acf29bff39c43e4aa34f9d) feat: add ability to mark MBR partition bootable * [`e0f5b1e2`](https://github.com/talos-systems/talos/commit/e0f5b1e20aa0d22898274ddc0f9026c0d813cee2) chore: split mgmt/gen.go into several files * [`fad1b4f1`](https://github.com/talos-systems/talos/commit/fad1b4f1fdce962b779ceb960f81d572ee5033af) chore: fix go generate for the machinery

### Changes from talos-systems/crypto
7 commits

* [`6bc5bb5`](https://github.com/talos-systems/crypto/commit/6bc5bb50c52767296a1b1cab6580e3fcf1358f34) chore: remove unused argument * [`cd18ef6`](https://github.com/talos-systems/crypto/commit/cd18ef62eb9f65d8b6730a2eb73e47e629949e1b) feat: add support for several organizations * [`97c888b`](https://github.com/talos-systems/crypto/commit/97c888b3924dd5ac70b8d30dd66b4370b5ab1edc) chore: add options to CSR * [`7776057`](https://github.com/talos-systems/crypto/commit/7776057f5086157873f62f6a21ec23fa9fd86e05) chore: fix typos * [`80df078`](https://github.com/talos-systems/crypto/commit/80df078327030af7e822668405bb4853c512bd7c) chore: remove named result parameters * [`15bdd28`](https://github.com/talos-systems/crypto/commit/15bdd282b74ac406ab243853c1b50338a1bc29d0) chore: minor updates * [`4f80b97`](https://github.com/talos-systems/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate

### Changes from talos-systems/extras
1 commit

* [`4fe2706`](https://github.com/talos-systems/extras/commit/4fe27060347c861b716392eec3dfee698becb5f3) feat: build with Go 1.16.5

### Changes from talos-systems/go-blockdevice
3 commits

* [`30c2bc3`](https://github.com/talos-systems/go-blockdevice/commit/30c2bc3cb62af52f0aea9ce347923b0649fb7928) feat: mark MBR bootable * [`1292574`](https://github.com/talos-systems/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/talos-systems/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types

### Changes from talos-systems/go-debug
5 commits

* [`3d0a6e1`](https://github.com/talos-systems/go-debug/commit/3d0a6e1bf5e3c521e83ead2c8b7faad3638b8c5d) feat: race build tag flag detector * [`5b292e5`](https://github.com/talos-systems/go-debug/commit/5b292e50198b8ed91c434f00e2772db394dbf0b9) feat: disable memory profiling by default * [`c6d0ae2`](https://github.com/talos-systems/go-debug/commit/c6d0ae2c0ee099fa0940405401e6a02716a15bd8) fix: linters and CI * [`d969f95`](https://github.com/talos-systems/go-debug/commit/d969f952af9e02feea59963671298fc236ca4399) feat: initial implementation * [`b2044b7`](https://github.com/talos-systems/go-debug/commit/b2044b70379c84f9706de74044bd2fd6a8e891cf) Initial commit

### Changes from talos-systems/go-kmsg
2 commits

* [`2edcd3a`](https://github.com/talos-systems/go-kmsg/commit/2edcd3a913508e2d922776f729bfc4bcab031a8b) feat: add initial version * [`53cdd8d`](https://github.com/talos-systems/go-kmsg/commit/53cdd8d67b9dbab692471a2d5161e7e0b3d04cca) chore: initial commit

### Changes from talos-systems/go-loadbalancer
3 commits

* [`a445702`](https://github.com/talos-systems/go-loadbalancer/commit/a4457024d5189d754b2da4a30b14072a0e3f5f05) feat: allow dial timeout and keep alive period to be configurable * [`3c8f347`](https://github.com/talos-systems/go-loadbalancer/commit/3c8f3471d14e37866c65f73170ef83c038ae5a8c) feat: provide a way to configure logger for the loadbalancer * [`da8e987`](https://github.com/talos-systems/go-loadbalancer/commit/da8e987434c3d407679a40e213b12a8e1c98abb8) feat: implement Reconcile - ability to change upstream list on the fly

### Changes from talos-systems/go-retry
3 commits

* [`c78cc95`](https://github.com/talos-systems/go-retry/commit/c78cc953d9e95992575305b4e8648392c6c9b9e6) fix: implement `errors.Is` for all errors in the set * [`7885e16`](https://github.com/talos-systems/go-retry/commit/7885e16b2cb0267bcc8b07cdd0eced14e8005864) feat: add ExpectedErrorf * [`3d83f61`](https://github.com/talos-systems/go-retry/commit/3d83f6126c1a3a238d1d1d59bfb6273e4087bdac) feat: deprecate UnexpectedError

### Changes from talos-systems/go-smbios
1 commit

* [`d3a32be`](https://github.com/talos-systems/go-smbios/commit/d3a32bea731a0c2a60ce7f5eae60253300ef27e1) fix: return UUID in middle endian only on SMBIOS >= 2.6

### Changes from talos-systems/pkgs
18 commits

* [`2d51360`](https://github.com/talos-systems/pkgs/commit/2d51360a254b237943e92cd445e42912d39fce7a) feat: support NFS 4.1 * [`e63e4e9`](https://github.com/talos-systems/pkgs/commit/e63e4e97b4c398e090028eaf7b967cc9eafadf96) feat: bump tools for Go 1.16.5 * [`1f8af29`](https://github.com/talos-systems/pkgs/commit/1f8af290e5d242f7dfc784fd2fc7fcfd714500bd) feat: update Linux to 5.10.38 * [`a3a6650`](https://github.com/talos-systems/pkgs/commit/a3a66505f36b9e9f92f4980df3708a872d56caec) feat: update containerd to 1.5.2 * [`c70ea44`](https://github.com/talos-systems/pkgs/commit/c70ea44ba4bc1ffabdb1422deda107a94e1fe94c) feat: update runc to 1.0.0-rc95 * [`db60235`](https://github.com/talos-systems/pkgs/commit/db602359cc594b35291911b4220dc5b331b323bb) feat: add support for netxen card * [`f934187`](https://github.com/talos-systems/pkgs/commit/f934187ebdc455f18cc6d2da847be3d48a6e3d8f) feat: update containerd to 1.5.1 * [`e8ed5bc`](https://github.com/talos-systems/pkgs/commit/e8ed5bcb848954ca30967de8d7c81afecdea4825) feat: add geneve encapsulation support for openvswitch * [`9f7903c`](https://github.com/talos-systems/pkgs/commit/9f7903cb5c110f77db8093347b69ec141325d47c) feat: update containerd to 1.5.0, runc to -rc94 * [`d7c0f70`](https://github.com/talos-systems/pkgs/commit/d7c0f70e41bb7bf542092f2882b062ff52f5ae44) feat: add AES-NI support for amd64 * [`b0d9cd2`](https://github.com/talos-systems/pkgs/commit/b0d9cd2c36e37190c5ce7b85acea6a51a853faaf) fix: build `zbin` utility for both amd64 and arm64 * [`bb39b97`](https://github.com/talos-systems/pkgs/commit/bb39b9744c0c4a29ccfa190a0d2cce0f8547676b) feat: add IPMI support in kernel * [`1148f9a`](https://github.com/talos-systems/pkgs/commit/1148f9a897d9a52b6013396151e1eab264709037) feat: add DS1307 RTC support for arm64 * [`350aa6f`](https://github.com/talos-systems/pkgs/commit/350aa6f200d441d7dbbf60ec8ebb39a6761d6a8b) feat: add USB serial support * [`de9c582`](https://github.com/talos-systems/pkgs/commit/de9c58238483219a574fb697ddb1126f36a02da3) feat: add Pine64 SBC support * [`b56f36b`](https://github.com/talos-systems/pkgs/commit/b56f36bedbe9270ae5cf969f8078a10345457e83) feat: enable VMware baloon kernel module * [`f87c194`](https://github.com/talos-systems/pkgs/commit/f87c19425352eb9b68d20dec987d0c484987dea9) feat: add iPXE build with embedded placeholder script * [`a8b9e71`](https://github.com/talos-systems/pkgs/commit/a8b9e71e6538d7554b7a48d1361709d5495bb4de) feat: add cpu scaling for rpi

### Changes from talos-systems/tools
1 commit

* [`c8c2a18`](https://github.com/talos-systems/tools/commit/c8c2a18b7e587e0b8464574e375a680c5a09a028) feat: update Go to 1.16.5

### Dependency Changes * **github.com/aws/aws-sdk-go** v1.27.0 **_new_** * **github.com/containerd/cgroups** 4cbc285b3327 -> v1.0.1 * **github.com/containerd/containerd** v1.4.4 -> v1.5.2 * **github.com/containerd/go-cni** v1.0.1 -> v1.0.2 * **github.com/containerd/typeurl** v1.0.1 -> v1.0.2 * **github.com/coreos/go-iptables** v0.5.0 -> v0.6.0 * **github.com/cosi-project/runtime** 10d6103c19ab -> ca95c7538d17 * **github.com/docker/docker** v20.10.4 -> v20.10.7 * **github.com/emicklei/dot** v0.15.0 -> v0.16.0 * **github.com/fatih/color** v1.10.0 -> v1.12.0 * **github.com/google/go-cmp** v0.5.5 -> v0.5.6 * **github.com/google/gofuzz** v1.2.0 **_new_** * **github.com/googleapis/gnostic** v0.5.5 **_new_** * **github.com/grpc-ecosystem/go-grpc-middleware** v1.2.2 -> v1.3.0 * **github.com/hashicorp/go-getter** v1.5.2 -> v1.5.3 * **github.com/imdario/mergo** v0.3.12 **_new_** * **github.com/insomniacslk/dhcp** cc9239ac6294 -> fb4eaaa00ad2 * **github.com/jsimonetti/rtnetlink** 1b79e63a70a0 -> b34cb89a106b * **github.com/magiconair/properties** v1.8.5 **_new_** * **github.com/mattn/go-isatty** v0.0.12 -> v0.0.13 * **github.com/mdlayher/arp** f72070a231fc **_new_** * **github.com/mdlayher/ethtool** 2b88debcdd43 **_new_** * **github.com/mdlayher/netlink** v1.4.0 -> v1.4.1 * **github.com/mdlayher/raw** 51b895745faf **_new_** * **github.com/mitchellh/mapstructure** v1.4.1 **_new_** * **github.com/opencontainers/runtime-spec** 4d89ac9fbff6 -> e6143ca7d51d * **github.com/pelletier/go-toml** v1.9.0 **_new_** * **github.com/rivo/tview** 8a8f78a6dd01 -> 807e706f86d1 * **github.com/rs/xid** v1.2.1 -> v1.3.0 * **github.com/sirupsen/logrus** v1.8.1 **_new_** * **github.com/spf13/afero** v1.6.0 **_new_** * **github.com/spf13/cast** v1.3.1 **_new_** * **github.com/spf13/viper** v1.7.1 **_new_** * **github.com/talos-systems/crypto** 39584f1b6e54 -> 6bc5bb50c527 * **github.com/talos-systems/extras** v0.3.0 -> v0.3.0-1-g4fe2706 * **github.com/talos-systems/go-blockdevice** 1d830a25f64f -> 30c2bc3cb62a * **github.com/talos-systems/go-debug** 3d0a6e1bf5e3 **_new_** * **github.com/talos-systems/go-kmsg** v0.1.0 **_new_** * **github.com/talos-systems/go-loadbalancer** v0.1.0 -> v0.1.1 * **github.com/talos-systems/go-retry** b9dc1a990133 -> c78cc953d9e9 * **github.com/talos-systems/go-smbios** fb425d4727e6 -> d3a32bea731a * **github.com/talos-systems/pkgs** v0.5.0-1-g5dd650b -> v0.6.0-alpha.0-8-g2d51360 * **github.com/talos-systems/talos/pkg/machinery** 8ffb55943c71 -> 000000000000 * **github.com/talos-systems/tools** v0.5.0 -> v0.5.0-1-gc8c2a18 * **github.com/vishvananda/netns** 2eb08e3e575f **_new_** * **github.com/vmware-tanzu/sonobuoy** v0.20.0 -> v0.51.0 * **github.com/vmware/govmomi** v0.24.0 -> v0.26.0 * **go.etcd.io/etcd/api/v3** v3.5.0-alpha.0 -> v3.5.0-rc.1 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0-rc.1 **_new_** * **go.etcd.io/etcd/client/v3** v3.5.0-alpha.0 -> v3.5.0-rc.1 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0-rc.1 **_new_** * **go.uber.org/zap** v1.17.0 **_new_** * **golang.org/x/net** e18ecbb05110 -> abc453219eb5 * **golang.org/x/oauth2** 81ed05c6b58c **_new_** * **golang.org/x/sys** 77cc2087c03b -> ebe580a85c40 * **golang.org/x/term** 6a3ed077a48d -> a79de5458b56 * **golang.zx2c4.com/wireguard/wgctrl** bd2cb7843e1b -> 92e472f520a5 * **google.golang.org/appengine** v1.6.7 **_new_** * **google.golang.org/grpc** v1.37.0 -> v1.38.0 * **gopkg.in/ini.v1** v1.62.0 **_new_** * **inet.af/netaddr** 1d252cf8125e **_new_** * **k8s.io/api** v0.21.0 -> v0.21.1 * **k8s.io/apimachinery** v0.21.0 -> v0.21.1 * **k8s.io/apiserver** v0.21.0 -> v0.21.1 * **k8s.io/client-go** v0.21.0 -> v0.21.1 * **k8s.io/kubectl** v0.21.0 -> v0.21.1 * **k8s.io/kubelet** v0.21.0 -> v0.21.1 * **k8s.io/utils** 2afb4311ab10 **_new_** * **sigs.k8s.io/structured-merge-diff/v4** v4.1.1 **_new_** Previous release can be found at [v0.10.0](https://github.com/talos-systems/talos/releases/tag/v0.10.0) ## [Talos 0.11.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.11.0-alpha.0) (2021-05-26) Welcome to the v0.11.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Component Updates * containerd was updated to 1.5.2 * Linux kernel was updated to 5.10.29 ### Multi-arch Installer Talos installer image (for any arch) now contains artifacts for both `amd64` and `arm64` architecture. This means that e.g. images for arm64 SBCs can be generated on amd64 host. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Jorik Jonker * Spencer Smith * Serge Logvinov * Andrew LeCody * Andrew Rynhard * Boran Car * Brandon Nason * Gabor Nyiri * Joost Coelingh * Kevin Hellemun * Lance R. Vick * Lennard Klein * Seán C McCord * Sébastien Bernard * Sébastien Bernard ### Changes
82 commits

* [`c0962946`](https://github.com/talos-systems/talos/commit/c09629466321f4d220454164784edf41fd3d5813) chore: prepare for 0.11 release series * [`72359765`](https://github.com/talos-systems/talos/commit/723597657ad78e9766190ea2e110208c62d0093b) feat: enable GORACE=halt_on_panic=1 in machined binary * [`0acb04ad`](https://github.com/talos-systems/talos/commit/0acb04ad7a2a0a7b75471f0251b0e04eccd927cd) feat: implement route network controllers * [`f5bf88a4`](https://github.com/talos-systems/talos/commit/f5bf88a4c2ab8f48fd93bc7ac13543c613bf9bd1) feat: create certificates with os:admin role * [`1db301ed`](https://github.com/talos-systems/talos/commit/1db301edf6a4057814a6d5b8f87fbfe1e020caeb) feat: switch controller-runtime to zap.Logger * [`f7cf64d4`](https://github.com/talos-systems/talos/commit/f7cf64d42ec77ca68408ecb0f437ab5f86bc787a) fix: add talos.config to the vApp Properties in VMware OVA * [`209527ec`](https://github.com/talos-systems/talos/commit/209527eccc6c93edad33a01a3f3d24fb978f2f07) docs: add AMIs for Talos 0.10.3 * [`59cfd312`](https://github.com/talos-systems/talos/commit/59cfd312c1ac531528c4ceb2adeb3f85829cc4e1) chore: bump dependencies via dependabot * [`1edb20cf`](https://github.com/talos-systems/talos/commit/1edb20cf98fe2e641cefc658d17206e09acabc26) feat: extract config generation * [`af77c295`](https://github.com/talos-systems/talos/commit/af77c29565b65766d135884ec7740f67b56626e3) docs: update wirguard guide * [`4fe69121`](https://github.com/talos-systems/talos/commit/4fe691214366c08ea846bdc6233dd592da0d4769) test: better `talosctl ls` tests * [`04ddda96`](https://github.com/talos-systems/talos/commit/04ddda962fbcfdeaae59d232e7bb7f9c5bb63bc7) feat: update containerd to 1.5.2, runc to 1.0.0-rc95 * [`49c7276b`](https://github.com/talos-systems/talos/commit/49c7276b16a82b7da8c83f8bd930361768f0e249) chore: fix markdown linting * [`7270495a`](https://github.com/talos-systems/talos/commit/7270495ace9faf48a73829bbed0e4eb2c939eecb) docs: add mayastor quickstart * [`d3d9112f`](https://github.com/talos-systems/talos/commit/d3d9112f288d3b0f3ebe1c8b28b1c4e2fc8512b2) docs: fix spelling/grammar in What's New for Talos 0.9 * [`82804414`](https://github.com/talos-systems/talos/commit/82804414fc2fcb21da77edc2fbbefe92a14fc30d) test: provide a way to force different boot order in provision library * [`a1c0e99a`](https://github.com/talos-systems/talos/commit/a1c0e99a1729c704a633dcc557dc46466b828e11) docs: add guide for deploying metrics-server * [`6bc6658b`](https://github.com/talos-systems/talos/commit/6bc6658b518379d418baafcf9b1045a3b84f48ec) feat: update containerd to 1.5.1 * [`c6567fae`](https://github.com/talos-systems/talos/commit/c6567fae9c59da5148c9876289a4bf248240b99d) chore: dependabot updates * [`61ccbb3f`](https://github.com/talos-systems/talos/commit/61ccbb3f5a2564376af13ea9bbfe51e364fcb3a1) chore: keep debug symbols in debug builds * [`1ce362e0`](https://github.com/talos-systems/talos/commit/1ce362e05e41cd76cdda17a6fc971767e036df37) docs: update customizing kernel build steps * [`a26174b5`](https://github.com/talos-systems/talos/commit/a26174b54846bdfa0b66d2f9147bfe1dc8f2eb52) fix: properly compose pattern and header in etcd members output * [`0825cf11`](https://github.com/talos-systems/talos/commit/0825cf11f412eef930db269b6cae02d059058101) fix: stop networkd and pods before leaving etcd on upgrade * [`bed6b15d`](https://github.com/talos-systems/talos/commit/bed6b15d6fcf0634a887b79797d639e221fe9387) fix: properly populate AllowSchedulingOnMasters option in gen config RPC * [`071f0445`](https://github.com/talos-systems/talos/commit/071f044562dd247dd54584d7b9fa0bb24d6f7599) feat: implement AddressSpec handling * [`76e38b7b`](https://github.com/talos-systems/talos/commit/76e38b7b8251548292ae15ecda2bfa1c8ddc5cf3) feat: update Kubernetes to 1.21.1 * [`9b1338d9`](https://github.com/talos-systems/talos/commit/9b1338d989e6cdf7e0b6d5fe1ba3c32d27fc2251) chore: parse "boolean" variables * [`c81cfb21`](https://github.com/talos-systems/talos/commit/c81cfb21670b82e518cf4c32230e8fbbce6be8ff) chore: allow building with debug handlers * [`c9651673`](https://github.com/talos-systems/talos/commit/c9651673b9eaf811ae4acfed313debbf78bd80e8) feat: update go-smbios library * [`95c656fb`](https://github.com/talos-systems/talos/commit/95c656fb72b6b858b55dae37020cb59ba26115f8) feat: update containerd to 1.5.0, runc to 1.0.0-rc94 * [`db9c35b5`](https://github.com/talos-systems/talos/commit/db9c35b570b39f4423f4636f9e9f1d14cac5d7c1) feat: implement AddressStatusController * [`1cf011a8`](https://github.com/talos-systems/talos/commit/1cf011a809b924fc8f2083566d169704c6e07cd5) chore: bump dependencies via dependabot * [`e3f407a1`](https://github.com/talos-systems/talos/commit/e3f407a1dff3f4ee7e024bbfb64f17b5cb5d625d) fix: properly pass disk type selector from config to matcher * [`66b2b450`](https://github.com/talos-systems/talos/commit/66b2b450582593e93598fac80c8b3c29e8c8a944) feat: add resources and use HTTPS checks in control plane pods * [`4ffd7c0a`](https://github.com/talos-systems/talos/commit/4ffd7c0adf281033ac02d37ca434e7f9ad71e692) fix: stop networkd before leaving etcd on 'reset' path * [`610d38d3`](https://github.com/talos-systems/talos/commit/610d38d309dabaa623494ade12234f1ccf018a9e) docs: add AMIs for 0.10.1, collapse list of AMIs by default * [`807497ec`](https://github.com/talos-systems/talos/commit/807497ec20dee15953186bda0fe7a45ffec0307c) chore: make conformance pipeline depend on cron-default * [`3c121359`](https://github.com/talos-systems/talos/commit/3c1213596cdf03daf09050103f57b29e756439b1) feat: implement LinkStatusController * [`0e8de046`](https://github.com/talos-systems/talos/commit/0e8de04698aac95062f3037da0a9af8b6ee916b0) fix: update go-blockdevice to fix disk type detection * [`4d50a4ed`](https://github.com/talos-systems/talos/commit/4d50a4edd0eb413c16e899536ccdc2642e37aeaa) fix: update the way NTP sync uses `adjtimex` syscall * [`1a85c14a`](https://github.com/talos-systems/talos/commit/1a85c14a51fdab43ae84274563bf89b30e4e6d92) fix: avoid data race on CRI pod stop * [`5de8dbc0`](https://github.com/talos-systems/talos/commit/5de8dbc06c7ed36c8f3af9adea8b1abedeb372b6) fix: repair pine64 support * [`38239097`](https://github.com/talos-systems/talos/commit/3823909735859f2ac5d95bc39c051fc9c2c07685) fix: properly parse matcher expressions * [`e54b6b7a`](https://github.com/talos-systems/talos/commit/e54b6b7a3d7412ddce1467dfbd35efe3cfd76f3f) chore: update dependencies via dependabot * [`f2caed0d`](https://github.com/talos-systems/talos/commit/f2caed0df5b76c4a719f968191081a6e5e2e95c7) chore: use extracted talos-systems/go-kmsg library * [`79d804c5`](https://github.com/talos-systems/talos/commit/79d804c5b4af50a0fd73db17d2522d6a6b45c9ca) docs: fix typos * [`a2bb390e`](https://github.com/talos-systems/talos/commit/a2bb390e1d56106d6d3c1526f3f76b34846b0274) feat: deterministic builds * [`e480fedf`](https://github.com/talos-systems/talos/commit/e480fedff047233e78ad2c22e7b84cbbb22798d5) feat: add USB serial drivers * [`79299d76`](https://github.com/talos-systems/talos/commit/79299d761c50aff386ab7b3c12f39c1797585632) docs: add Matrix room links * [`1b3e8b09`](https://github.com/talos-systems/talos/commit/1b3e8b09edcd51cf3df2d43d14c8fbf1e912a465) docs: add survey to README * [`8d51c9bb`](https://github.com/talos-systems/talos/commit/8d51c9bb190c2c60fa9be6a00572d2eaf4221e94) docs: update redirects to Talos 0.10 * [`1092c3a5`](https://github.com/talos-systems/talos/commit/1092c3a5069a3add439860d90c3615111fa03c98) feat: add Pine64 SBC support * [`63e01754`](https://github.com/talos-systems/talos/commit/63e0175437e45c8f7e5296841337a640c600982c) feat: pull kernel with VMware balloon module enabled * [`aeec99d8`](https://github.com/talos-systems/talos/commit/aeec99d8247f4eb534e0db1ed639f95cd726fe08) chore: remove temporary fork * [`0f49722d`](https://github.com/talos-systems/talos/commit/0f49722d0ff4e731f17a55d1ca50472714334748) feat: add `--config-patch` flag by node type * [`a01b1d22`](https://github.com/talos-systems/talos/commit/a01b1d22d9f3fa94355817217fefd80fe34628f3) chore: dump dependencies via dependabot * [`d540a4a4`](https://github.com/talos-systems/talos/commit/d540a4a4711367a0ada203f668382e39876ba081) fix: bump crypto library for the CSR verification fix * [`c3a4173e`](https://github.com/talos-systems/talos/commit/c3a4173e11a92c2bc51ea4f284ad38c9750105d2) chore: remove security API ReadFile/WriteFile * [`38037131`](https://github.com/talos-systems/talos/commit/38037131cddc2aefbae0f48fb7e355ec76247b67) chore: update wgctrl dependecy * [`d9ba0fd0`](https://github.com/talos-systems/talos/commit/d9ba0fd0164b2bfb2bc4ffe7a2d9d6c665a38e4d) docs: create v0.11 docs, promote v0.10 docs, add v0.10 AMIs * [`2261d7ed`](https://github.com/talos-systems/talos/commit/2261d7ed0212c287273eac647647e4390c530a6e) fix: use both self-signed and Kubernetes CA to verify Kubelet cert * [`a3537a69`](https://github.com/talos-systems/talos/commit/a3537a691320430eeb7149abe73419ee242312fc) docs: update cloud images for Talos v0.9.3 * [`5b9ee861`](https://github.com/talos-systems/talos/commit/5b9ee86179fb92989b02533d6d6745a5b0f37566) docs: add what's new for Talos 0.10 * [`f1107fa3`](https://github.com/talos-systems/talos/commit/f1107fa3a33955f3aa57a49991c87f9ee47b6e67) docs: add survey * [`93623d47`](https://github.com/talos-systems/talos/commit/93623d47f24fef0d149fa006678b61e3182ef771) docs: update AWS instructions * [`a739d1b8`](https://github.com/talos-systems/talos/commit/a739d1b8adbc026796d1c55f7319677f9010f727) feat: add support of custom registry CA certificate usage * [`7f468d35`](https://github.com/talos-systems/talos/commit/7f468d350a6f80d2815149376fa24f7d7629402c) fix: update osType in OVA other3xLinux64Guest" * [`4a184b67`](https://github.com/talos-systems/talos/commit/4a184b67d6ae25b21b35373e7dd6eab41b042c96) docs: add etcd backup and restore guide * [`5fb38d3e`](https://github.com/talos-systems/talos/commit/5fb38d3e5f201934d64bae186c5300e7de7af3d4) chore: refactor Dockerfile for cross-compilation * [`a8f1e526`](https://github.com/talos-systems/talos/commit/a8f1e526bfc00107c915572df2be08b3f154f4e6) chore: build talosctl for Darwin / Apple Silicon * [`eb0b64d3`](https://github.com/talos-systems/talos/commit/eb0b64d3138228a6c751387c720ca81c338b834d) chore: list specifically for enabled regions * [`669a0cbd`](https://github.com/talos-systems/talos/commit/669a0cbdc4756f0ad8f0dacc56a20f71e96fe4cd) fix: check if OVF env is empty * [`da92049c`](https://github.com/talos-systems/talos/commit/da92049c0b4beae32af80205f50849443cd6dad3) chore: use codecov from the build container * [`9996d4b0`](https://github.com/talos-systems/talos/commit/9996d4b028f3845071850def75f2b534e4d2b190) chore: use REGISTRY_MIRROR_FLAGS if defined * [`05cbe250`](https://github.com/talos-systems/talos/commit/05cbe250c87339e097d435d6b10b9d8a5f2eb49e) chore: bump dependencies via dependabot * [`9a91142a`](https://github.com/talos-systems/talos/commit/9a91142a38b3b1f210773acf8df01ed6a45599c2) feat: print complete member info in etcd members * [`bb40d6dd`](https://github.com/talos-systems/talos/commit/bb40d6dd06a967464c24ab33744bbf460aa84038) feat: update pkgs version * [`e7a9164b`](https://github.com/talos-systems/talos/commit/e7a9164b1e1630f953a420d99c865aef6e652d15) test: implement `talosctl conformance` command to run e2e tests * [`6cb266e7`](https://github.com/talos-systems/talos/commit/6cb266e74e60d9d5423feaad550a7861dc73f11d) fix: update etcd client errors, print etcd join failures * [`0bd8b0e8`](https://github.com/talos-systems/talos/commit/0bd8b0e8008c12e4914c6e9b5faf06dda6c744f7) feat: provide an option to recover etcd from data directory copy * [`f9818540`](https://github.com/talos-systems/talos/commit/f98185408d618ebcc780247ea2c42239df27a74e) chore: fix conform with scopes * [`21018f28`](https://github.com/talos-systems/talos/commit/21018f28c732719535c30c8e1abdbb346f1dc4bf) chore: bump website node.js dependencies

### Changes from talos-systems/crypto
1 commit

* [`4f80b97`](https://github.com/talos-systems/crypto/commit/4f80b976b640d773fb025d981bf85bcc8190815b) fix: verify CSR signature before issuing a certificate

### Changes from talos-systems/go-blockdevice
2 commits

* [`1292574`](https://github.com/talos-systems/go-blockdevice/commit/1292574643e06512255fb0f45107e0c296eb5a3b) fix: make disk type matcher parser case insensitive * [`b77400e`](https://github.com/talos-systems/go-blockdevice/commit/b77400e0a7261bf25da77c1f28c2f393f367bfa9) fix: properly detect nvme and sd card disk types

### Changes from talos-systems/go-debug
5 commits

* [`3d0a6e1`](https://github.com/talos-systems/go-debug/commit/3d0a6e1bf5e3c521e83ead2c8b7faad3638b8c5d) feat: race build tag flag detector * [`5b292e5`](https://github.com/talos-systems/go-debug/commit/5b292e50198b8ed91c434f00e2772db394dbf0b9) feat: disable memory profiling by default * [`c6d0ae2`](https://github.com/talos-systems/go-debug/commit/c6d0ae2c0ee099fa0940405401e6a02716a15bd8) fix: linters and CI * [`d969f95`](https://github.com/talos-systems/go-debug/commit/d969f952af9e02feea59963671298fc236ca4399) feat: initial implementation * [`b2044b7`](https://github.com/talos-systems/go-debug/commit/b2044b70379c84f9706de74044bd2fd6a8e891cf) Initial commit

### Changes from talos-systems/go-kmsg
2 commits

* [`2edcd3a`](https://github.com/talos-systems/go-kmsg/commit/2edcd3a913508e2d922776f729bfc4bcab031a8b) feat: add initial version * [`53cdd8d`](https://github.com/talos-systems/go-kmsg/commit/53cdd8d67b9dbab692471a2d5161e7e0b3d04cca) chore: initial commit

### Changes from talos-systems/go-loadbalancer
3 commits

* [`a445702`](https://github.com/talos-systems/go-loadbalancer/commit/a4457024d5189d754b2da4a30b14072a0e3f5f05) feat: allow dial timeout and keep alive period to be configurable * [`3c8f347`](https://github.com/talos-systems/go-loadbalancer/commit/3c8f3471d14e37866c65f73170ef83c038ae5a8c) feat: provide a way to configure logger for the loadbalancer * [`da8e987`](https://github.com/talos-systems/go-loadbalancer/commit/da8e987434c3d407679a40e213b12a8e1c98abb8) feat: implement Reconcile - ability to change upstream list on the fly

### Changes from talos-systems/go-smbios
1 commit

* [`d3a32be`](https://github.com/talos-systems/go-smbios/commit/d3a32bea731a0c2a60ce7f5eae60253300ef27e1) fix: return UUID in middle endian only on SMBIOS >= 2.6

### Changes from talos-systems/pkgs
15 commits

* [`a3a6650`](https://github.com/talos-systems/pkgs/commit/a3a66505f36b9e9f92f4980df3708a872d56caec) feat: update containerd to 1.5.2 * [`c70ea44`](https://github.com/talos-systems/pkgs/commit/c70ea44ba4bc1ffabdb1422deda107a94e1fe94c) feat: update runc to 1.0.0-rc95 * [`db60235`](https://github.com/talos-systems/pkgs/commit/db602359cc594b35291911b4220dc5b331b323bb) feat: add support for netxen card * [`f934187`](https://github.com/talos-systems/pkgs/commit/f934187ebdc455f18cc6d2da847be3d48a6e3d8f) feat: update containerd to 1.5.1 * [`e8ed5bc`](https://github.com/talos-systems/pkgs/commit/e8ed5bcb848954ca30967de8d7c81afecdea4825) feat: add geneve encapsulation support for openvswitch * [`9f7903c`](https://github.com/talos-systems/pkgs/commit/9f7903cb5c110f77db8093347b69ec141325d47c) feat: update containerd to 1.5.0, runc to -rc94 * [`d7c0f70`](https://github.com/talos-systems/pkgs/commit/d7c0f70e41bb7bf542092f2882b062ff52f5ae44) feat: add AES-NI support for amd64 * [`b0d9cd2`](https://github.com/talos-systems/pkgs/commit/b0d9cd2c36e37190c5ce7b85acea6a51a853faaf) fix: build `zbin` utility for both amd64 and arm64 * [`bb39b97`](https://github.com/talos-systems/pkgs/commit/bb39b9744c0c4a29ccfa190a0d2cce0f8547676b) feat: add IPMI support in kernel * [`1148f9a`](https://github.com/talos-systems/pkgs/commit/1148f9a897d9a52b6013396151e1eab264709037) feat: add DS1307 RTC support for arm64 * [`350aa6f`](https://github.com/talos-systems/pkgs/commit/350aa6f200d441d7dbbf60ec8ebb39a6761d6a8b) feat: add USB serial support * [`de9c582`](https://github.com/talos-systems/pkgs/commit/de9c58238483219a574fb697ddb1126f36a02da3) feat: add Pine64 SBC support * [`b56f36b`](https://github.com/talos-systems/pkgs/commit/b56f36bedbe9270ae5cf969f8078a10345457e83) feat: enable VMware baloon kernel module * [`f87c194`](https://github.com/talos-systems/pkgs/commit/f87c19425352eb9b68d20dec987d0c484987dea9) feat: add iPXE build with embedded placeholder script * [`a8b9e71`](https://github.com/talos-systems/pkgs/commit/a8b9e71e6538d7554b7a48d1361709d5495bb4de) feat: add cpu scaling for rpi

### Dependency Changes * **github.com/containerd/cgroups** 4cbc285b3327 -> v1.0.1 * **github.com/containerd/containerd** v1.4.4 -> v1.5.2 * **github.com/containerd/go-cni** v1.0.1 -> v1.0.2 * **github.com/containerd/typeurl** v1.0.1 -> v1.0.2 * **github.com/coreos/go-iptables** v0.5.0 -> v0.6.0 * **github.com/cosi-project/runtime** 10d6103c19ab -> 8a4533ce68e2 * **github.com/docker/docker** v20.10.4 -> v20.10.6 * **github.com/emicklei/dot** v0.15.0 -> v0.16.0 * **github.com/fatih/color** v1.10.0 -> v1.11.0 * **github.com/grpc-ecosystem/go-grpc-middleware** v1.2.2 -> v1.3.0 * **github.com/hashicorp/go-getter** v1.5.2 -> v1.5.3 * **github.com/mdlayher/ethtool** 2b88debcdd43 **_new_** * **github.com/opencontainers/runtime-spec** 4d89ac9fbff6 -> e6143ca7d51d * **github.com/plunder-app/kube-vip** v0.3.2 -> v0.3.4 * **github.com/rs/xid** v1.2.1 -> v1.3.0 * **github.com/talos-systems/crypto** 39584f1b6e54 -> 4f80b976b640 * **github.com/talos-systems/go-blockdevice** 1d830a25f64f -> 1292574643e0 * **github.com/talos-systems/go-debug** 3d0a6e1bf5e3 **_new_** * **github.com/talos-systems/go-kmsg** v0.1.0 **_new_** * **github.com/talos-systems/go-loadbalancer** v0.1.0 -> v0.1.1 * **github.com/talos-systems/go-smbios** fb425d4727e6 -> d3a32bea731a * **github.com/talos-systems/pkgs** v0.5.0-1-g5dd650b -> v0.6.0-alpha.0-5-ga3a6650 * **github.com/vmware-tanzu/sonobuoy** v0.20.0 -> v0.50.0 * **github.com/vmware/govmomi** v0.24.0 -> v0.25.0 * **go.etcd.io/etcd/api/v3** v3.5.0-alpha.0 -> v3.5.0-beta.3 * **go.etcd.io/etcd/client/pkg/v3** v3.5.0-beta.3 **_new_** * **go.etcd.io/etcd/client/v3** v3.5.0-alpha.0 -> v3.5.0-beta.3 * **go.etcd.io/etcd/etcdutl/v3** v3.5.0-beta.3 **_new_** * **go.uber.org/zap** c23abee72d19 **_new_** * **golang.org/x/net** e18ecbb05110 -> 0714010a04ed * **golang.org/x/sys** 77cc2087c03b -> 0981d6026fa6 * **golang.org/x/term** 6a3ed077a48d -> a79de5458b56 * **golang.zx2c4.com/wireguard/wgctrl** bd2cb7843e1b -> f9ad6d392236 * **google.golang.org/grpc** v1.37.0 -> v1.38.0 * **inet.af/netaddr** 1d252cf8125e **_new_** * **k8s.io/api** v0.21.0 -> v0.21.1 * **k8s.io/apimachinery** v0.21.0 -> v0.21.1 * **k8s.io/apiserver** v0.21.0 -> v0.21.1 * **k8s.io/client-go** v0.21.0 -> v0.21.1 * **k8s.io/kubectl** v0.21.0 -> v0.21.1 * **k8s.io/kubelet** v0.21.0 -> v0.21.1 Previous release can be found at [v0.10.0](https://github.com/talos-systems/talos/releases/tag/v0.10.0) ## [Talos 0.10.0-alpha.2](https://github.com/talos-systems/talos/releases/tag/v0.10.0-alpha.2) (2021-04-08) Welcome to the v0.10.0-alpha.2 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Disaster Recovery * support for creating etcd snapshots (backups) with `talosctl etcd snapshot` command. * etcd cluster can be recovered from a snapshot using `talosctl boostrap --recover-from=` command. ### Install Disk Selector Install section of the machine config now has `diskSelector` field that allows querying install disk using the list of qualifiers: ```yaml ... install: diskSelector: size: >= 500GB model: WDC* ... ``` `talosctl disks -n -i` can be used to check allowed disk qualifiers when the node is running in the maintenance mode. ### Optimizations * Talos `system` services now run without container images on initramfs from the single executable; this change reduces RAM usage, initramfs size and boot time.. ### SBCs * u-boot version was updated to fix the boot and USB issues on Raspberry Pi 4 8GiB version. * added support for Rock Pi 4. ### Time Syncrhonization * `timed` service was replaced with a time sync controller, no machine configuration changes. * Talos now prefers last successful time server (by IP address) on each sync attempt (improves sync accuracy). ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Spencer Smith * Seán C McCord * Andrew Rynhard * Branden Cash * Jorik Jonker * Matt Zahorik * bzub ### Changes
104 commits

* [`e0650218`](https://github.com/talos-systems/talos/commit/e0650218a6b0a05a8e109262a0d7ed3d7359ea37) feat: support etcd recovery from snapshot on bootstrap * [`247bd50e`](https://github.com/talos-systems/talos/commit/247bd50e0510f57c969e3bb8fee5b53bfcdbb074) docs: describe steps to install and boot Talos from the SSD on rockpi4 * [`e6b4e524`](https://github.com/talos-systems/talos/commit/e6b4e524ffa33a5c76368f0fe8e9c372e3297cfc) test: update CAPA to 0.6.4 * [`28753f6d`](https://github.com/talos-systems/talos/commit/28753f6dcb85450965e4d4a0fb68f448e1deee23) fix: trim endpoints/nodes from arguments in talosctl config * [`aca63b88`](https://github.com/talos-systems/talos/commit/aca63b8829ad0eebd449573120bff2d9b90ba828) docs: fix "DigitalOcean" spelling * [`33035901`](https://github.com/talos-systems/talos/commit/33035901ff7875bdf9eb99fb86b377318f60d74b) fix: revert mark PMBR EFI partition as bootable * [`fbfd1eb2`](https://github.com/talos-systems/talos/commit/fbfd1eb2b1684fe38caa12b8d46d608c42b5daf6) refactor: pull new version of os-runtime, update code * [`8737ea71`](https://github.com/talos-systems/talos/commit/8737ea716a5d9adf24959a56a73dd61e1139b808) feat: allow external cloud provides configration * [`3909e2d0`](https://github.com/talos-systems/talos/commit/3909e2d011b9d11653903687e5a4210daa440ef2) chore: update Go to 1.16.3 * [`690eb20e`](https://github.com/talos-systems/talos/commit/690eb20e9763d8f3036f0a1b4b9447f19c5ec05b) chore: update blockdevice library for PMBR bootable fix * [`a8761b8e`](https://github.com/talos-systems/talos/commit/a8761b8e1efd07a3bda3d8f706d3d7bf658955bb) fix: require leader on etcd member operations * [`3dc84625`](https://github.com/talos-systems/talos/commit/3dc84625cb1b323bad1dd93d89a13d3d59ea22d8) fix: make both HDMI ports work on RPi 4 * [`bd5ae1e0`](https://github.com/talos-systems/talos/commit/bd5ae1e0b5dd303a017156ba7af733f79d3c13ef) fix: add a check for overlay mounts in installer pre-flight checks * [`df8649cb`](https://github.com/talos-systems/talos/commit/df8649cbe6f4fcf04c4b84a444ec2519e37ac171) refactor: download modules before `go generate` * [`39ae0415`](https://github.com/talos-systems/talos/commit/39ae0415e9d932c01ff33163d97daef375c21a7f) chore: bump dependencies via dependabot * [`e16d6d34`](https://github.com/talos-systems/talos/commit/e16d6d3468a7a072b41e94fdc352df15b8321376) fix: publish rockpi4 image to release artifacts * [`39c6dbcc`](https://github.com/talos-systems/talos/commit/39c6dbcc7ae8f07e1ab4c2a82508ebee07f66207) feat: add --config-patch parameter to talosctl gen config * [`e664362c`](https://github.com/talos-systems/talos/commit/e664362cecb476a41360143a05c0cfad718b2e0f) feat: add API and command to save etcd snapshot (backup) * [`61b694b9`](https://github.com/talos-systems/talos/commit/61b694b94896da47e2ddf677cbf12b18007268a5) fix: create rootfs for system services via /system tmpfs * [`abc2e17e`](https://github.com/talos-systems/talos/commit/abc2e17ebb6d440438e407e5a5d1c5c1f7d1eeff) test: update 0.9.x version in upgrade tests to 0.9.1 * [`a1e64154`](https://github.com/talos-systems/talos/commit/a1e6415403df9827fb486492a4b292b9aab3076b) fix: retry Kubernetes API errors on cordon/uncordon/etc * [`063d1abe`](https://github.com/talos-systems/talos/commit/063d1abe9cf1634f3517893977fc907dd9004c55) fix: print task failure error immediately * [`e039172e`](https://github.com/talos-systems/talos/commit/e039172edac115afbd5bf36a1f266e5967ca5398) fix: ignore EOF errors from Kubernetes API when converting control plane * [`7bcb91a4`](https://github.com/talos-systems/talos/commit/7bcb91a433f14a29a0d2bbe9d70eb5a997eb9ab0) docs: fix typo for stage flag * [`a43acb21`](https://github.com/talos-systems/talos/commit/a43acb2150cadd78da51c41569b7f219b704f089) feat: bring in Linux 5.10.27, support for 32-bit time syscalls * [`e2bb5973`](https://github.com/talos-systems/talos/commit/e2bb5973da5b2dc15aba2a809e0e31426b6f22b3) release(v0.10.0-alpha.1): prepare release * [`8309312a`](https://github.com/talos-systems/talos/commit/8309312a3db89cea17b673d0d1c73175db5258ac) chore: build components with race detector enabled in dev mode * [`7d912584`](https://github.com/talos-systems/talos/commit/7d9125847506dfadc7e137a30bf0c93ab9ca0b50) test: fix data race in apply config tests * [`204caf8e`](https://github.com/talos-systems/talos/commit/204caf8eb9c6c43a90c20ebaea8387584201e7f5) test: fix apply-config integration test, bump clusterctl version * [`d812099d`](https://github.com/talos-systems/talos/commit/d812099df3d060ae74cd3d28405ddacbdd72ab15) fix: address several issues in TUI installer * [`269c9ad0`](https://github.com/talos-systems/talos/commit/269c9ad0988f0f966a4e31a5ab744fed7d585385) fix: don't write to config object on access * [`a9451f57`](https://github.com/talos-systems/talos/commit/a9451f57129b0b452825850bba9477ac3c536547) feat: update Kubernetes to 1.21.0-beta.1 * [`4b42ced4`](https://github.com/talos-systems/talos/commit/4b42ced4c2a300aa22f253435a4d6330770ec5c2) feat: add ability to disable comments in talosctl gen config * [`a0dcfc3d`](https://github.com/talos-systems/talos/commit/a0dcfc3d5288e633db80bf3e32d31e41756cc90f) fix: workaround race in containerd runner with stdin pipe * [`2ea20f59`](https://github.com/talos-systems/talos/commit/2ea20f598a01f3de95f633bdfaf5711738524ba2) feat: replace timed with time sync controller * [`c38a161a`](https://github.com/talos-systems/talos/commit/c38a161ade34f00f7af52d9ae047d7936246e7f0) test: add unit-test for machine config validation * [`a6106815`](https://github.com/talos-systems/talos/commit/a6106815b72efcb7f4df0caab6b93be49a7590ea) chore: bump dependencies via dependabot * [`35598f39`](https://github.com/talos-systems/talos/commit/35598f391d5d0659e3390d4db67c7ed88c17b6eb) chore: refactor: extract ClusterConfig * [`03285184`](https://github.com/talos-systems/talos/commit/032851844fdea4b1bde7507720025c981ee3b12c) fix: get rid of data race in encoder and fix concurrent map access * [`4b3580aa`](https://github.com/talos-systems/talos/commit/4b3580aa57d83358434238ad953793070cfc67a7) fix: prevent panic in validate config if `machine.install` is missing * [`d7e9f6d6`](https://github.com/talos-systems/talos/commit/d7e9f6d6a89143f0def74a270a21ed5e53556e07) chore: build integration tests with -race * [`9f7d67ac`](https://github.com/talos-systems/talos/commit/9f7d67ac717834ed428b8f13d4061db5f33c81f9) chore: fix typo * [`672c9707`](https://github.com/talos-systems/talos/commit/672c970739971dd0c558ad0319fe9fdbd66a741b) fix: allow `convert-k8s --remove-initialized-keys` with K8s cp is down * [`fb605a0f`](https://github.com/talos-systems/talos/commit/fb605a0fc56e6df1ceae8c391524ac987bbba09d) chore: tweak nolintlint settings * [`1f5a0c40`](https://github.com/talos-systems/talos/commit/1f5a0c4065e1fbd63ebe6d48c13e669bfb1dbeac) fix: resolve the issue with Kubernetes upgrade * [`74b2b557`](https://github.com/talos-systems/talos/commit/74b2b5578cbe639a6f2663df6ab7a5e80b139fe0) docs: update AWS docs to ensure instances are tagged * [`dc21d9b4`](https://github.com/talos-systems/talos/commit/dc21d9b4b0f5858fbe0d4072e8a47a934780c3dd) chore: remove old file * [`966caf7a`](https://github.com/talos-systems/talos/commit/966caf7a674c20047c1184e64f3727abc0c54296) chore: remove unused module replace directives * [`98b22f1e`](https://github.com/talos-systems/talos/commit/98b22f1e0b0f5e85b71d344041265efa95e1bb91) feat: show short options in talosctl kubeconfig * [`51139d54`](https://github.com/talos-systems/talos/commit/51139d54d4ce4acf2e78f11ab0f384f91f86ff33) chore: cache go modules in the build * [`65701aa7`](https://github.com/talos-systems/talos/commit/65701aa724130645fcabe521557225ff41b359b0) fix: resolve the issue with DHCP lease not being renewed * [`711f5b23`](https://github.com/talos-systems/talos/commit/711f5b23be69665d6204dbb80064e0ab0d1468c0) fix: config validation: CNI should apply to cp nodes, encryption config * [`5ff491d9`](https://github.com/talos-systems/talos/commit/5ff491d9686434a6208583dca97171bfbecf3f70) fix: allow empty list for CNI URLs * [`946e74f0`](https://github.com/talos-systems/talos/commit/946e74f047f30180bf5f0554fd8ae1043e0d1f52) docs: update path for kernel downloads in qemu docs * [`ed272e60`](https://github.com/talos-systems/talos/commit/ed272e604e67dc38557812e5f4dbcb8666c4b546) feat: update Kubernetes to 1.21.0-beta.0 * [`b0209fd2`](https://github.com/talos-systems/talos/commit/b0209fd29d3895d7a0b8806e505bbefcf2bba520) refactor: move networkd, timed APIs to machined, remove routerd * [`6ffabe51`](https://github.com/talos-systems/talos/commit/6ffabe51691907b43f9f970f22d7aec4df19a6c3) feat: add ability to find disk by disk properties * [`ac876470`](https://github.com/talos-systems/talos/commit/ac8764702f980a8dea5b6a67f0bc33b5203efecb) refactor: move apid, routerd, timed and trustd to single executable * [`89a4b09f`](https://github.com/talos-systems/talos/commit/89a4b09fe8015e70f7074d9af72d47023ece2f1d) refactor: run networkd as a goroutine in machined * [`f4a6a19c`](https://github.com/talos-systems/talos/commit/f4a6a19cd1bf1da7f2610276c00e8144a78f8694) chore: update sonobuoy * [`dc294db1`](https://github.com/talos-systems/talos/commit/dc294db16c8bdb10e3f63987c87c0bbdf629b158) chore: bump dependencies via dependabot * [`2b1641a3`](https://github.com/talos-systems/talos/commit/2b1641a3b543d736eb0d2e359d2a25dbc906e631) docs: add AMIs for Talos 0.9.0 * [`79ceb428`](https://github.com/talos-systems/talos/commit/79ceb428d4216a06418933058485ec2273474e3c) docs: make v0.9 the default docs * [`a5b62f4d`](https://github.com/talos-systems/talos/commit/a5b62f4dc20da721b0f74c5fbb5082038e05e4f4) docs: add documentation for Talos 0.10 * [`ce795f1c`](https://github.com/talos-systems/talos/commit/ce795f1cea9d78c26edbcd4a40bb5d3637fde629) fix: command `etcd remove-member` shouldn't remove etcd data directory * [`aab49a16`](https://github.com/talos-systems/talos/commit/aab49a167b1f1cd3974e3aa1244d636ba712f678) fix: repair zsh completion * [`fc9c416a`](https://github.com/talos-systems/talos/commit/fc9c416a3c8425bb42892f740c910894610acd00) fix: build rockpi4 metal image as part of CI build * [`125b86f4`](https://github.com/talos-systems/talos/commit/125b86f4efbc2ed3e0a4bdfc945e97b05f1cb82c) fix: upgrade-k8s bug with empty config values and provision script * [`8b2d228d`](https://github.com/talos-systems/talos/commit/8b2d228dc42c196090aae1e6958683e265ebc05c) chore: add script for starting registry proxies * [`f7d276b8`](https://github.com/talos-systems/talos/commit/f7d276b854c4c06f85155c517cc1de7109a53359) chore: remove old `osctl` reference * [`5b14d6f2`](https://github.com/talos-systems/talos/commit/5b14d6f2b89c5b86f9ec2cb0271c6605272269d4) chore: fix `make help` output * [`f0512dfc`](https://github.com/talos-systems/talos/commit/f0512dfce9443cf20790ef8b4fd8e87906cc5bda) feat: update Kubernetes to 1.20.5 * [`24cd0a20`](https://github.com/talos-systems/talos/commit/24cd0a20678f2728a0b36c1c401dd8af3d4932ed) feat: publish talosctl container image * [`6e17102c`](https://github.com/talos-systems/talos/commit/6e17102c210dccd4bf78d347de07cfe2ba7737c4) chore: remove unused code * [`88104407`](https://github.com/talos-systems/talos/commit/8810440744453550697ad39530633b81889d38b7) docs: add control plane in-depth guide * [`ecf03449`](https://github.com/talos-systems/talos/commit/ecf034496e7450f89369140ad1791188580dee0d) chore: bump Go to 1.16.2 * [`cbc38418`](https://github.com/talos-systems/talos/commit/cbc38418d856a00ffb35d31676e1efb14fb6da36) release(v0.10.0-alpha.0): prepare release * [`3455a8e8`](https://github.com/talos-systems/talos/commit/3455a8e8185ba25777784d392d6150a4a7e2d4a9) chore: use new release tool for changelogs and release notes * [`08271ba9`](https://github.com/talos-systems/talos/commit/08271ba93178c17a7c495788fea00c5c380f8301) chore: use Go 1.16 language version * [`7662d033`](https://github.com/talos-systems/talos/commit/7662d033bfc3d6e3878e2c2a2a1ec4d71dc2502e) fix: talosctl health should not check kube-proxy when it is disabled * [`0dbaeb9e`](https://github.com/talos-systems/talos/commit/0dbaeb9e655acdc44f8b4db6d1bc6da2ddf6cc9d) chore: update tools, use new generators * [`e31790f6`](https://github.com/talos-systems/talos/commit/e31790f6f548095fe3f1b9a5c88b47e70c197d2c) fix: properly format spec comments in the resources * [`78d384eb`](https://github.com/talos-systems/talos/commit/78d384ebb6246cf41a73014312dfb0d86a8008d6) test: update aws cloud provider version * [`3c5bfbb4`](https://github.com/talos-systems/talos/commit/3c5bfbb4736c86f493a665dbfe63a6e2d20acb3d) fix: don't touch any partitions on upgrade with --preserve * [`891f90fe`](https://github.com/talos-systems/talos/commit/891f90fee9818f0f013878c0c77c1920e6427a91) chore: update Linux to 5.10.23 * [`d4d77882`](https://github.com/talos-systems/talos/commit/d4d77882e3f53f2449f50f54116a407726f41ede) chore: update dependencies via dependabot * [`2e22f20b`](https://github.com/talos-systems/talos/commit/2e22f20bd876e4972bfdebd44fee13356b70b83f) docs: minor fixes to getting started * [`ca8a5596`](https://github.com/talos-systems/talos/commit/ca8a5596c79f638e52601e850236b715f906e3d2) chore: fix provision tests after changes to build-container * [`4aae924c`](https://github.com/talos-systems/talos/commit/4aae924c685ff578af06a1adceeec4f1938576a6) refactor: provide explicit logger for networkd * [`22f37530`](https://github.com/talos-systems/talos/commit/22f375300c1cc1d95db540afd510a21b66d7c8a3) chore: update golanci-lint to 1.38.0 * [`83b4e7f7`](https://github.com/talos-systems/talos/commit/83b4e7f744e3a8ed21443642a9afcf5b1342c62b) feat: add Rock pi 4 support * [`1362966f`](https://github.com/talos-systems/talos/commit/1362966ff546ee620c14e9312255616685743eed) docs: rewrite getting-started for ISO * [`8e57fc4f`](https://github.com/talos-systems/talos/commit/8e57fc4f526096878213048658bae50cfac4cda8) fix: move containerd CRI config files under `/var/` * [`6f7df3da`](https://github.com/talos-systems/talos/commit/6f7df3da1e147212e6d4b40a5de65e5ca8be84db) fix: update output of `convert-k8s` command * [`dce6118c`](https://github.com/talos-systems/talos/commit/dce6118c290afe957e375586b6bbc5b10ef6ba09) docs: add guide for VIP * [`ee5d9ffa`](https://github.com/talos-systems/talos/commit/ee5d9ffac60c93561874995d8926fc329e2b67dc) chore: bump Go to 1.16.1 * [`7c529e1c`](https://github.com/talos-systems/talos/commit/7c529e1cbd2be66d71e8496304781dd406495bdd) docs: fix links in the documentation * [`f596c7f6`](https://github.com/talos-systems/talos/commit/f596c7f6be3880be994faf7c5361628024c6be7d) docs: add video for raspberry pi install * [`47324dca`](https://github.com/talos-systems/talos/commit/47324dcaeaee94e4963eb3764fc01cd2d2d43041) docs: add guide on editing machine configuration * [`99d5f894`](https://github.com/talos-systems/talos/commit/99d5f894e17f39004e61ee9d5b64d5a8139f33d0) chore: update website npm dependencies * [`11056a80`](https://github.com/talos-systems/talos/commit/11056a80349e4c8df10a9ea98b6e3d53f96b971c) docs: add highlights for 0.9 release * [`ae8bedb9`](https://github.com/talos-systems/talos/commit/ae8bedb9a0d999bfbe97b6e18dc2eff62f0fcb80) docs: add control plane conversion guide and 0.9 upgrade notes * [`ed9673e5`](https://github.com/talos-systems/talos/commit/ed9673e50a7cb973fc49be9c2d659447a4c5bd62) docs: add troubleshooting control plane documentation * [`485cb126`](https://github.com/talos-systems/talos/commit/485cb1262f97e982ea81597b49d173836c75558d) docs: update Kubernetes upgrade guide

### Changes since v0.10.0-alpha.1
25 commits

* [`e0650218`](https://github.com/talos-systems/talos/commit/e0650218a6b0a05a8e109262a0d7ed3d7359ea37) feat: support etcd recovery from snapshot on bootstrap * [`247bd50e`](https://github.com/talos-systems/talos/commit/247bd50e0510f57c969e3bb8fee5b53bfcdbb074) docs: describe steps to install and boot Talos from the SSD on rockpi4 * [`e6b4e524`](https://github.com/talos-systems/talos/commit/e6b4e524ffa33a5c76368f0fe8e9c372e3297cfc) test: update CAPA to 0.6.4 * [`28753f6d`](https://github.com/talos-systems/talos/commit/28753f6dcb85450965e4d4a0fb68f448e1deee23) fix: trim endpoints/nodes from arguments in talosctl config * [`aca63b88`](https://github.com/talos-systems/talos/commit/aca63b8829ad0eebd449573120bff2d9b90ba828) docs: fix "DigitalOcean" spelling * [`33035901`](https://github.com/talos-systems/talos/commit/33035901ff7875bdf9eb99fb86b377318f60d74b) fix: revert mark PMBR EFI partition as bootable * [`fbfd1eb2`](https://github.com/talos-systems/talos/commit/fbfd1eb2b1684fe38caa12b8d46d608c42b5daf6) refactor: pull new version of os-runtime, update code * [`8737ea71`](https://github.com/talos-systems/talos/commit/8737ea716a5d9adf24959a56a73dd61e1139b808) feat: allow external cloud provides configration * [`3909e2d0`](https://github.com/talos-systems/talos/commit/3909e2d011b9d11653903687e5a4210daa440ef2) chore: update Go to 1.16.3 * [`690eb20e`](https://github.com/talos-systems/talos/commit/690eb20e9763d8f3036f0a1b4b9447f19c5ec05b) chore: update blockdevice library for PMBR bootable fix * [`a8761b8e`](https://github.com/talos-systems/talos/commit/a8761b8e1efd07a3bda3d8f706d3d7bf658955bb) fix: require leader on etcd member operations * [`3dc84625`](https://github.com/talos-systems/talos/commit/3dc84625cb1b323bad1dd93d89a13d3d59ea22d8) fix: make both HDMI ports work on RPi 4 * [`bd5ae1e0`](https://github.com/talos-systems/talos/commit/bd5ae1e0b5dd303a017156ba7af733f79d3c13ef) fix: add a check for overlay mounts in installer pre-flight checks * [`df8649cb`](https://github.com/talos-systems/talos/commit/df8649cbe6f4fcf04c4b84a444ec2519e37ac171) refactor: download modules before `go generate` * [`39ae0415`](https://github.com/talos-systems/talos/commit/39ae0415e9d932c01ff33163d97daef375c21a7f) chore: bump dependencies via dependabot * [`e16d6d34`](https://github.com/talos-systems/talos/commit/e16d6d3468a7a072b41e94fdc352df15b8321376) fix: publish rockpi4 image to release artifacts * [`39c6dbcc`](https://github.com/talos-systems/talos/commit/39c6dbcc7ae8f07e1ab4c2a82508ebee07f66207) feat: add --config-patch parameter to talosctl gen config * [`e664362c`](https://github.com/talos-systems/talos/commit/e664362cecb476a41360143a05c0cfad718b2e0f) feat: add API and command to save etcd snapshot (backup) * [`61b694b9`](https://github.com/talos-systems/talos/commit/61b694b94896da47e2ddf677cbf12b18007268a5) fix: create rootfs for system services via /system tmpfs * [`abc2e17e`](https://github.com/talos-systems/talos/commit/abc2e17ebb6d440438e407e5a5d1c5c1f7d1eeff) test: update 0.9.x version in upgrade tests to 0.9.1 * [`a1e64154`](https://github.com/talos-systems/talos/commit/a1e6415403df9827fb486492a4b292b9aab3076b) fix: retry Kubernetes API errors on cordon/uncordon/etc * [`063d1abe`](https://github.com/talos-systems/talos/commit/063d1abe9cf1634f3517893977fc907dd9004c55) fix: print task failure error immediately * [`e039172e`](https://github.com/talos-systems/talos/commit/e039172edac115afbd5bf36a1f266e5967ca5398) fix: ignore EOF errors from Kubernetes API when converting control plane * [`7bcb91a4`](https://github.com/talos-systems/talos/commit/7bcb91a433f14a29a0d2bbe9d70eb5a997eb9ab0) docs: fix typo for stage flag * [`a43acb21`](https://github.com/talos-systems/talos/commit/a43acb2150cadd78da51c41569b7f219b704f089) feat: bring in Linux 5.10.27, support for 32-bit time syscalls

### Changes from talos-systems/extras
3 commits

* [`cf3934a`](https://github.com/talos-systems/extras/commit/cf3934ae09b22c396226bed6618b3d03ab298e33) feat: build with Go 1.16.3 * [`c0fa0c0`](https://github.com/talos-systems/extras/commit/c0fa0c04641d8dfc418888c210788a6894e8d40c) feat: bump Go to 1.16.2 * [`5f89d77`](https://github.com/talos-systems/extras/commit/5f89d77a91f44d52146dae9c23b4654d219042b9) feat: bump Go to 1.16.1

### Changes from talos-systems/go-blockdevice
3 commits

* [`1d830a2`](https://github.com/talos-systems/go-blockdevice/commit/1d830a25f64f6fb96a1bedd800c0b40b107dc833) fix: revert mark the EFI partition in PMBR as bootable * [`bec914f`](https://github.com/talos-systems/go-blockdevice/commit/bec914ffdda42abcfe642bc2cdfc9fcda56a74ee) fix: mark the EFI partition in PMBR as bootable * [`776b37d`](https://github.com/talos-systems/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters

### Changes from talos-systems/os-runtime
5 commits

* [`86d9e09`](https://github.com/talos-systems/os-runtime/commit/86d9e090bdc4ebfdc8bba0333a067ce189e839da) chore: bump go.mod dependencies * [`2de411a`](https://github.com/talos-systems/os-runtime/commit/2de411a4765de15de1d5b1524131d262801eb395) feat: major rewrite of the os-runtime with new features * [`ded40a7`](https://github.com/talos-systems/os-runtime/commit/ded40a78343f77dfc02ba5e5857a6baea99da682) feat: implement controller runtime gRPC bridge * [`0d5b5a9`](https://github.com/talos-systems/os-runtime/commit/0d5b5a942c26c8de35e741c078a38ab6529a54b7) feat: implement resource state service and client * [`d04ec51`](https://github.com/talos-systems/os-runtime/commit/d04ec51da46abf20110d6a4d5acc250fa7810c17) feat: add common COSI resource protobuf, implement bridge with state

### Changes from talos-systems/pkgs
8 commits

* [`9a6cf6b`](https://github.com/talos-systems/pkgs/commit/9a6cf6b99e1b8c0ef49e5dba2ce7e0260212c30d) feat: build with Go 1.16.3 * [`60ce626`](https://github.com/talos-systems/pkgs/commit/60ce6260e3956566d40ef77e2194c31c18c92d10) feat: update Linux to 5.10.27, enable 32-bit time syscalls * [`fdf4866`](https://github.com/talos-systems/pkgs/commit/fdf48667851b4c80b0ca220c574d2fb57a943f64) feat: bump tools for Go 1.16.2 * [`35f9b6f`](https://github.com/talos-systems/pkgs/commit/35f9b6f22bbe094e93723559132b2a23f0853c2b) feat: update kernel to 5.10.23 * [`dbae83e`](https://github.com/talos-systems/pkgs/commit/dbae83e704da264066ceeca20e0fe66883b542ba) fix: do not use git-lfs for rockpi4 binaries * [`1c6b9a3`](https://github.com/talos-systems/pkgs/commit/1c6b9a3a6ef91bce4f0cba18c466a9ece7b14750) feat: bump tools for Go 1.16.1 * [`c18073f`](https://github.com/talos-systems/pkgs/commit/c18073fe79b9d7ec36411c6f329fa60c580d4cea) feat: add u-boot for Rock Pi 4 * [`6b85a2b`](https://github.com/talos-systems/pkgs/commit/6b85a2bffbb144f25356eed6ed9dc8bb9a3fd392) feat: upgrade u-boot to 2021.04-rc3

### Changes from talos-systems/tools
5 commits

* [`1f26def`](https://github.com/talos-systems/tools/commit/1f26def38066c41fdb5c4bfe85559a87aa832c51) feat: update Go to 1.16.3 * [`41b8073`](https://github.com/talos-systems/tools/commit/41b807369779606f54d76e56038bfaf88d4f0f25) feat: bump protobuf-related tools * [`f7bce92`](https://github.com/talos-systems/tools/commit/f7bce92febdf9f58f2940952d5138494b9232ea8) chore: bump Go to 1.16.2 * [`bcf3380`](https://github.com/talos-systems/tools/commit/bcf3380dd55810e556851acbe20e20cb4ddd5ef0) feat: bump protobuf deps, add protoc-gen-go-grpc * [`b49c40e`](https://github.com/talos-systems/tools/commit/b49c40e0ad701f13192c1ad85ec616224343dc3f) feat: bump Go to 1.16.1

### Dependency Changes * **github.com/coreos/go-semver** v0.3.0 **_new_** * **github.com/golang/protobuf** v1.4.3 -> v1.5.2 * **github.com/google/go-cmp** v0.5.4 -> v0.5.5 * **github.com/hashicorp/go-multierror** v1.1.0 -> v1.1.1 * **github.com/talos-systems/extras** v0.2.0-1-g0db3328 -> v0.3.0-alpha.0-2-gcf3934a * **github.com/talos-systems/go-blockdevice** bb3ad73f6983 -> 1d830a25f64f * **github.com/talos-systems/os-runtime** 7b3d14457439 -> 86d9e090bdc4 * **github.com/talos-systems/pkgs** v0.4.1-2-gd471b60 -> v0.5.0-alpha.0-5-g9a6cf6b * **github.com/talos-systems/tools** v0.4.0-1-g3b25a7e -> v0.5.0-alpha.0-4-g1f26def * **go.etcd.io/etcd/etcdctl/v3** v3.5.0-alpha.0 **_new_** * **google.golang.org/grpc** v1.36.0 -> v1.36.1 * **google.golang.org/protobuf** v1.25.0 -> v1.26.0 * **k8s.io/api** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/apimachinery** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/apiserver** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/client-go** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/cri-api** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/kubectl** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/kubelet** v0.20.5 -> v0.21.0-rc.0 Previous release can be found at [v0.9.0](https://github.com/talos-systems/talos/releases/tag/v0.9.0) ## [Talos 0.10.0-alpha.1](https://github.com/talos-systems/talos/releases/tag/v0.10.0-alpha.1) (2021-03-31) Welcome to the v0.10.0-alpha.1 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### Install Disk Selector Install section of the machine config now has `diskSelector` field that allows querying install disk using the list of qualifiers: ```yaml ... install: diskSelector: size: >= 500GB model: WDC* ... ``` `talosctl disks -n -i` can be used to check allowed disk qualifiers when the node is running in the maintenance mode. ### Optimizations * Talos `system` services now run without container images on initramfs from the single executable; this change reduces RAM usage, initramfs size and boot time.. ### SBCs * u-boot version was updated to fix the boot and USB issues on Raspberry Pi 4 8GiB version. * added support for Rock Pi 4. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Spencer Smith * Seán C McCord * Andrew Rynhard * Jorik Jonker * bzub ### Changes
78 commits

* [`8309312a`](https://github.com/talos-systems/talos/commit/8309312a3db89cea17b673d0d1c73175db5258ac) chore: build components with race detector enabled in dev mode * [`7d912584`](https://github.com/talos-systems/talos/commit/7d9125847506dfadc7e137a30bf0c93ab9ca0b50) test: fix data race in apply config tests * [`204caf8e`](https://github.com/talos-systems/talos/commit/204caf8eb9c6c43a90c20ebaea8387584201e7f5) test: fix apply-config integration test, bump clusterctl version * [`d812099d`](https://github.com/talos-systems/talos/commit/d812099df3d060ae74cd3d28405ddacbdd72ab15) fix: address several issues in TUI installer * [`269c9ad0`](https://github.com/talos-systems/talos/commit/269c9ad0988f0f966a4e31a5ab744fed7d585385) fix: don't write to config object on access * [`a9451f57`](https://github.com/talos-systems/talos/commit/a9451f57129b0b452825850bba9477ac3c536547) feat: update Kubernetes to 1.21.0-beta.1 * [`4b42ced4`](https://github.com/talos-systems/talos/commit/4b42ced4c2a300aa22f253435a4d6330770ec5c2) feat: add ability to disable comments in talosctl gen config * [`a0dcfc3d`](https://github.com/talos-systems/talos/commit/a0dcfc3d5288e633db80bf3e32d31e41756cc90f) fix: workaround race in containerd runner with stdin pipe * [`2ea20f59`](https://github.com/talos-systems/talos/commit/2ea20f598a01f3de95f633bdfaf5711738524ba2) feat: replace timed with time sync controller * [`c38a161a`](https://github.com/talos-systems/talos/commit/c38a161ade34f00f7af52d9ae047d7936246e7f0) test: add unit-test for machine config validation * [`a6106815`](https://github.com/talos-systems/talos/commit/a6106815b72efcb7f4df0caab6b93be49a7590ea) chore: bump dependencies via dependabot * [`35598f39`](https://github.com/talos-systems/talos/commit/35598f391d5d0659e3390d4db67c7ed88c17b6eb) chore: refactor: extract ClusterConfig * [`03285184`](https://github.com/talos-systems/talos/commit/032851844fdea4b1bde7507720025c981ee3b12c) fix: get rid of data race in encoder and fix concurrent map access * [`4b3580aa`](https://github.com/talos-systems/talos/commit/4b3580aa57d83358434238ad953793070cfc67a7) fix: prevent panic in validate config if `machine.install` is missing * [`d7e9f6d6`](https://github.com/talos-systems/talos/commit/d7e9f6d6a89143f0def74a270a21ed5e53556e07) chore: build integration tests with -race * [`9f7d67ac`](https://github.com/talos-systems/talos/commit/9f7d67ac717834ed428b8f13d4061db5f33c81f9) chore: fix typo * [`672c9707`](https://github.com/talos-systems/talos/commit/672c970739971dd0c558ad0319fe9fdbd66a741b) fix: allow `convert-k8s --remove-initialized-keys` with K8s cp is down * [`fb605a0f`](https://github.com/talos-systems/talos/commit/fb605a0fc56e6df1ceae8c391524ac987bbba09d) chore: tweak nolintlint settings * [`1f5a0c40`](https://github.com/talos-systems/talos/commit/1f5a0c4065e1fbd63ebe6d48c13e669bfb1dbeac) fix: resolve the issue with Kubernetes upgrade * [`74b2b557`](https://github.com/talos-systems/talos/commit/74b2b5578cbe639a6f2663df6ab7a5e80b139fe0) docs: update AWS docs to ensure instances are tagged * [`dc21d9b4`](https://github.com/talos-systems/talos/commit/dc21d9b4b0f5858fbe0d4072e8a47a934780c3dd) chore: remove old file * [`966caf7a`](https://github.com/talos-systems/talos/commit/966caf7a674c20047c1184e64f3727abc0c54296) chore: remove unused module replace directives * [`98b22f1e`](https://github.com/talos-systems/talos/commit/98b22f1e0b0f5e85b71d344041265efa95e1bb91) feat: show short options in talosctl kubeconfig * [`51139d54`](https://github.com/talos-systems/talos/commit/51139d54d4ce4acf2e78f11ab0f384f91f86ff33) chore: cache go modules in the build * [`65701aa7`](https://github.com/talos-systems/talos/commit/65701aa724130645fcabe521557225ff41b359b0) fix: resolve the issue with DHCP lease not being renewed * [`711f5b23`](https://github.com/talos-systems/talos/commit/711f5b23be69665d6204dbb80064e0ab0d1468c0) fix: config validation: CNI should apply to cp nodes, encryption config * [`5ff491d9`](https://github.com/talos-systems/talos/commit/5ff491d9686434a6208583dca97171bfbecf3f70) fix: allow empty list for CNI URLs * [`946e74f0`](https://github.com/talos-systems/talos/commit/946e74f047f30180bf5f0554fd8ae1043e0d1f52) docs: update path for kernel downloads in qemu docs * [`ed272e60`](https://github.com/talos-systems/talos/commit/ed272e604e67dc38557812e5f4dbcb8666c4b546) feat: update Kubernetes to 1.21.0-beta.0 * [`b0209fd2`](https://github.com/talos-systems/talos/commit/b0209fd29d3895d7a0b8806e505bbefcf2bba520) refactor: move networkd, timed APIs to machined, remove routerd * [`6ffabe51`](https://github.com/talos-systems/talos/commit/6ffabe51691907b43f9f970f22d7aec4df19a6c3) feat: add ability to find disk by disk properties * [`ac876470`](https://github.com/talos-systems/talos/commit/ac8764702f980a8dea5b6a67f0bc33b5203efecb) refactor: move apid, routerd, timed and trustd to single executable * [`89a4b09f`](https://github.com/talos-systems/talos/commit/89a4b09fe8015e70f7074d9af72d47023ece2f1d) refactor: run networkd as a goroutine in machined * [`f4a6a19c`](https://github.com/talos-systems/talos/commit/f4a6a19cd1bf1da7f2610276c00e8144a78f8694) chore: update sonobuoy * [`dc294db1`](https://github.com/talos-systems/talos/commit/dc294db16c8bdb10e3f63987c87c0bbdf629b158) chore: bump dependencies via dependabot * [`2b1641a3`](https://github.com/talos-systems/talos/commit/2b1641a3b543d736eb0d2e359d2a25dbc906e631) docs: add AMIs for Talos 0.9.0 * [`79ceb428`](https://github.com/talos-systems/talos/commit/79ceb428d4216a06418933058485ec2273474e3c) docs: make v0.9 the default docs * [`a5b62f4d`](https://github.com/talos-systems/talos/commit/a5b62f4dc20da721b0f74c5fbb5082038e05e4f4) docs: add documentation for Talos 0.10 * [`ce795f1c`](https://github.com/talos-systems/talos/commit/ce795f1cea9d78c26edbcd4a40bb5d3637fde629) fix: command `etcd remove-member` shouldn't remove etcd data directory * [`aab49a16`](https://github.com/talos-systems/talos/commit/aab49a167b1f1cd3974e3aa1244d636ba712f678) fix: repair zsh completion * [`fc9c416a`](https://github.com/talos-systems/talos/commit/fc9c416a3c8425bb42892f740c910894610acd00) fix: build rockpi4 metal image as part of CI build * [`125b86f4`](https://github.com/talos-systems/talos/commit/125b86f4efbc2ed3e0a4bdfc945e97b05f1cb82c) fix: upgrade-k8s bug with empty config values and provision script * [`8b2d228d`](https://github.com/talos-systems/talos/commit/8b2d228dc42c196090aae1e6958683e265ebc05c) chore: add script for starting registry proxies * [`f7d276b8`](https://github.com/talos-systems/talos/commit/f7d276b854c4c06f85155c517cc1de7109a53359) chore: remove old `osctl` reference * [`5b14d6f2`](https://github.com/talos-systems/talos/commit/5b14d6f2b89c5b86f9ec2cb0271c6605272269d4) chore: fix `make help` output * [`f0512dfc`](https://github.com/talos-systems/talos/commit/f0512dfce9443cf20790ef8b4fd8e87906cc5bda) feat: update Kubernetes to 1.20.5 * [`24cd0a20`](https://github.com/talos-systems/talos/commit/24cd0a20678f2728a0b36c1c401dd8af3d4932ed) feat: publish talosctl container image * [`6e17102c`](https://github.com/talos-systems/talos/commit/6e17102c210dccd4bf78d347de07cfe2ba7737c4) chore: remove unused code * [`88104407`](https://github.com/talos-systems/talos/commit/8810440744453550697ad39530633b81889d38b7) docs: add control plane in-depth guide * [`ecf03449`](https://github.com/talos-systems/talos/commit/ecf034496e7450f89369140ad1791188580dee0d) chore: bump Go to 1.16.2 * [`cbc38418`](https://github.com/talos-systems/talos/commit/cbc38418d856a00ffb35d31676e1efb14fb6da36) release(v0.10.0-alpha.0): prepare release * [`3455a8e8`](https://github.com/talos-systems/talos/commit/3455a8e8185ba25777784d392d6150a4a7e2d4a9) chore: use new release tool for changelogs and release notes * [`08271ba9`](https://github.com/talos-systems/talos/commit/08271ba93178c17a7c495788fea00c5c380f8301) chore: use Go 1.16 language version * [`7662d033`](https://github.com/talos-systems/talos/commit/7662d033bfc3d6e3878e2c2a2a1ec4d71dc2502e) fix: talosctl health should not check kube-proxy when it is disabled * [`0dbaeb9e`](https://github.com/talos-systems/talos/commit/0dbaeb9e655acdc44f8b4db6d1bc6da2ddf6cc9d) chore: update tools, use new generators * [`e31790f6`](https://github.com/talos-systems/talos/commit/e31790f6f548095fe3f1b9a5c88b47e70c197d2c) fix: properly format spec comments in the resources * [`78d384eb`](https://github.com/talos-systems/talos/commit/78d384ebb6246cf41a73014312dfb0d86a8008d6) test: update aws cloud provider version * [`3c5bfbb4`](https://github.com/talos-systems/talos/commit/3c5bfbb4736c86f493a665dbfe63a6e2d20acb3d) fix: don't touch any partitions on upgrade with --preserve * [`891f90fe`](https://github.com/talos-systems/talos/commit/891f90fee9818f0f013878c0c77c1920e6427a91) chore: update Linux to 5.10.23 * [`d4d77882`](https://github.com/talos-systems/talos/commit/d4d77882e3f53f2449f50f54116a407726f41ede) chore: update dependencies via dependabot * [`2e22f20b`](https://github.com/talos-systems/talos/commit/2e22f20bd876e4972bfdebd44fee13356b70b83f) docs: minor fixes to getting started * [`ca8a5596`](https://github.com/talos-systems/talos/commit/ca8a5596c79f638e52601e850236b715f906e3d2) chore: fix provision tests after changes to build-container * [`4aae924c`](https://github.com/talos-systems/talos/commit/4aae924c685ff578af06a1adceeec4f1938576a6) refactor: provide explicit logger for networkd * [`22f37530`](https://github.com/talos-systems/talos/commit/22f375300c1cc1d95db540afd510a21b66d7c8a3) chore: update golanci-lint to 1.38.0 * [`83b4e7f7`](https://github.com/talos-systems/talos/commit/83b4e7f744e3a8ed21443642a9afcf5b1342c62b) feat: add Rock pi 4 support * [`1362966f`](https://github.com/talos-systems/talos/commit/1362966ff546ee620c14e9312255616685743eed) docs: rewrite getting-started for ISO * [`8e57fc4f`](https://github.com/talos-systems/talos/commit/8e57fc4f526096878213048658bae50cfac4cda8) fix: move containerd CRI config files under `/var/` * [`6f7df3da`](https://github.com/talos-systems/talos/commit/6f7df3da1e147212e6d4b40a5de65e5ca8be84db) fix: update output of `convert-k8s` command * [`dce6118c`](https://github.com/talos-systems/talos/commit/dce6118c290afe957e375586b6bbc5b10ef6ba09) docs: add guide for VIP * [`ee5d9ffa`](https://github.com/talos-systems/talos/commit/ee5d9ffac60c93561874995d8926fc329e2b67dc) chore: bump Go to 1.16.1 * [`7c529e1c`](https://github.com/talos-systems/talos/commit/7c529e1cbd2be66d71e8496304781dd406495bdd) docs: fix links in the documentation * [`f596c7f6`](https://github.com/talos-systems/talos/commit/f596c7f6be3880be994faf7c5361628024c6be7d) docs: add video for raspberry pi install * [`47324dca`](https://github.com/talos-systems/talos/commit/47324dcaeaee94e4963eb3764fc01cd2d2d43041) docs: add guide on editing machine configuration * [`99d5f894`](https://github.com/talos-systems/talos/commit/99d5f894e17f39004e61ee9d5b64d5a8139f33d0) chore: update website npm dependencies * [`11056a80`](https://github.com/talos-systems/talos/commit/11056a80349e4c8df10a9ea98b6e3d53f96b971c) docs: add highlights for 0.9 release * [`ae8bedb9`](https://github.com/talos-systems/talos/commit/ae8bedb9a0d999bfbe97b6e18dc2eff62f0fcb80) docs: add control plane conversion guide and 0.9 upgrade notes * [`ed9673e5`](https://github.com/talos-systems/talos/commit/ed9673e50a7cb973fc49be9c2d659447a4c5bd62) docs: add troubleshooting control plane documentation * [`485cb126`](https://github.com/talos-systems/talos/commit/485cb1262f97e982ea81597b49d173836c75558d) docs: update Kubernetes upgrade guide

### Changes since v0.10.0-alpha.0
50 commits

* [`8309312a`](https://github.com/talos-systems/talos/commit/8309312a3db89cea17b673d0d1c73175db5258ac) chore: build components with race detector enabled in dev mode * [`7d912584`](https://github.com/talos-systems/talos/commit/7d9125847506dfadc7e137a30bf0c93ab9ca0b50) test: fix data race in apply config tests * [`204caf8e`](https://github.com/talos-systems/talos/commit/204caf8eb9c6c43a90c20ebaea8387584201e7f5) test: fix apply-config integration test, bump clusterctl version * [`d812099d`](https://github.com/talos-systems/talos/commit/d812099df3d060ae74cd3d28405ddacbdd72ab15) fix: address several issues in TUI installer * [`269c9ad0`](https://github.com/talos-systems/talos/commit/269c9ad0988f0f966a4e31a5ab744fed7d585385) fix: don't write to config object on access * [`a9451f57`](https://github.com/talos-systems/talos/commit/a9451f57129b0b452825850bba9477ac3c536547) feat: update Kubernetes to 1.21.0-beta.1 * [`4b42ced4`](https://github.com/talos-systems/talos/commit/4b42ced4c2a300aa22f253435a4d6330770ec5c2) feat: add ability to disable comments in talosctl gen config * [`a0dcfc3d`](https://github.com/talos-systems/talos/commit/a0dcfc3d5288e633db80bf3e32d31e41756cc90f) fix: workaround race in containerd runner with stdin pipe * [`2ea20f59`](https://github.com/talos-systems/talos/commit/2ea20f598a01f3de95f633bdfaf5711738524ba2) feat: replace timed with time sync controller * [`c38a161a`](https://github.com/talos-systems/talos/commit/c38a161ade34f00f7af52d9ae047d7936246e7f0) test: add unit-test for machine config validation * [`a6106815`](https://github.com/talos-systems/talos/commit/a6106815b72efcb7f4df0caab6b93be49a7590ea) chore: bump dependencies via dependabot * [`35598f39`](https://github.com/talos-systems/talos/commit/35598f391d5d0659e3390d4db67c7ed88c17b6eb) chore: refactor: extract ClusterConfig * [`03285184`](https://github.com/talos-systems/talos/commit/032851844fdea4b1bde7507720025c981ee3b12c) fix: get rid of data race in encoder and fix concurrent map access * [`4b3580aa`](https://github.com/talos-systems/talos/commit/4b3580aa57d83358434238ad953793070cfc67a7) fix: prevent panic in validate config if `machine.install` is missing * [`d7e9f6d6`](https://github.com/talos-systems/talos/commit/d7e9f6d6a89143f0def74a270a21ed5e53556e07) chore: build integration tests with -race * [`9f7d67ac`](https://github.com/talos-systems/talos/commit/9f7d67ac717834ed428b8f13d4061db5f33c81f9) chore: fix typo * [`672c9707`](https://github.com/talos-systems/talos/commit/672c970739971dd0c558ad0319fe9fdbd66a741b) fix: allow `convert-k8s --remove-initialized-keys` with K8s cp is down * [`fb605a0f`](https://github.com/talos-systems/talos/commit/fb605a0fc56e6df1ceae8c391524ac987bbba09d) chore: tweak nolintlint settings * [`1f5a0c40`](https://github.com/talos-systems/talos/commit/1f5a0c4065e1fbd63ebe6d48c13e669bfb1dbeac) fix: resolve the issue with Kubernetes upgrade * [`74b2b557`](https://github.com/talos-systems/talos/commit/74b2b5578cbe639a6f2663df6ab7a5e80b139fe0) docs: update AWS docs to ensure instances are tagged * [`dc21d9b4`](https://github.com/talos-systems/talos/commit/dc21d9b4b0f5858fbe0d4072e8a47a934780c3dd) chore: remove old file * [`966caf7a`](https://github.com/talos-systems/talos/commit/966caf7a674c20047c1184e64f3727abc0c54296) chore: remove unused module replace directives * [`98b22f1e`](https://github.com/talos-systems/talos/commit/98b22f1e0b0f5e85b71d344041265efa95e1bb91) feat: show short options in talosctl kubeconfig * [`51139d54`](https://github.com/talos-systems/talos/commit/51139d54d4ce4acf2e78f11ab0f384f91f86ff33) chore: cache go modules in the build * [`65701aa7`](https://github.com/talos-systems/talos/commit/65701aa724130645fcabe521557225ff41b359b0) fix: resolve the issue with DHCP lease not being renewed * [`711f5b23`](https://github.com/talos-systems/talos/commit/711f5b23be69665d6204dbb80064e0ab0d1468c0) fix: config validation: CNI should apply to cp nodes, encryption config * [`5ff491d9`](https://github.com/talos-systems/talos/commit/5ff491d9686434a6208583dca97171bfbecf3f70) fix: allow empty list for CNI URLs * [`946e74f0`](https://github.com/talos-systems/talos/commit/946e74f047f30180bf5f0554fd8ae1043e0d1f52) docs: update path for kernel downloads in qemu docs * [`ed272e60`](https://github.com/talos-systems/talos/commit/ed272e604e67dc38557812e5f4dbcb8666c4b546) feat: update Kubernetes to 1.21.0-beta.0 * [`b0209fd2`](https://github.com/talos-systems/talos/commit/b0209fd29d3895d7a0b8806e505bbefcf2bba520) refactor: move networkd, timed APIs to machined, remove routerd * [`6ffabe51`](https://github.com/talos-systems/talos/commit/6ffabe51691907b43f9f970f22d7aec4df19a6c3) feat: add ability to find disk by disk properties * [`ac876470`](https://github.com/talos-systems/talos/commit/ac8764702f980a8dea5b6a67f0bc33b5203efecb) refactor: move apid, routerd, timed and trustd to single executable * [`89a4b09f`](https://github.com/talos-systems/talos/commit/89a4b09fe8015e70f7074d9af72d47023ece2f1d) refactor: run networkd as a goroutine in machined * [`f4a6a19c`](https://github.com/talos-systems/talos/commit/f4a6a19cd1bf1da7f2610276c00e8144a78f8694) chore: update sonobuoy * [`dc294db1`](https://github.com/talos-systems/talos/commit/dc294db16c8bdb10e3f63987c87c0bbdf629b158) chore: bump dependencies via dependabot * [`2b1641a3`](https://github.com/talos-systems/talos/commit/2b1641a3b543d736eb0d2e359d2a25dbc906e631) docs: add AMIs for Talos 0.9.0 * [`79ceb428`](https://github.com/talos-systems/talos/commit/79ceb428d4216a06418933058485ec2273474e3c) docs: make v0.9 the default docs * [`a5b62f4d`](https://github.com/talos-systems/talos/commit/a5b62f4dc20da721b0f74c5fbb5082038e05e4f4) docs: add documentation for Talos 0.10 * [`ce795f1c`](https://github.com/talos-systems/talos/commit/ce795f1cea9d78c26edbcd4a40bb5d3637fde629) fix: command `etcd remove-member` shouldn't remove etcd data directory * [`aab49a16`](https://github.com/talos-systems/talos/commit/aab49a167b1f1cd3974e3aa1244d636ba712f678) fix: repair zsh completion * [`fc9c416a`](https://github.com/talos-systems/talos/commit/fc9c416a3c8425bb42892f740c910894610acd00) fix: build rockpi4 metal image as part of CI build * [`125b86f4`](https://github.com/talos-systems/talos/commit/125b86f4efbc2ed3e0a4bdfc945e97b05f1cb82c) fix: upgrade-k8s bug with empty config values and provision script * [`8b2d228d`](https://github.com/talos-systems/talos/commit/8b2d228dc42c196090aae1e6958683e265ebc05c) chore: add script for starting registry proxies * [`f7d276b8`](https://github.com/talos-systems/talos/commit/f7d276b854c4c06f85155c517cc1de7109a53359) chore: remove old `osctl` reference * [`5b14d6f2`](https://github.com/talos-systems/talos/commit/5b14d6f2b89c5b86f9ec2cb0271c6605272269d4) chore: fix `make help` output * [`f0512dfc`](https://github.com/talos-systems/talos/commit/f0512dfce9443cf20790ef8b4fd8e87906cc5bda) feat: update Kubernetes to 1.20.5 * [`24cd0a20`](https://github.com/talos-systems/talos/commit/24cd0a20678f2728a0b36c1c401dd8af3d4932ed) feat: publish talosctl container image * [`6e17102c`](https://github.com/talos-systems/talos/commit/6e17102c210dccd4bf78d347de07cfe2ba7737c4) chore: remove unused code * [`88104407`](https://github.com/talos-systems/talos/commit/8810440744453550697ad39530633b81889d38b7) docs: add control plane in-depth guide * [`ecf03449`](https://github.com/talos-systems/talos/commit/ecf034496e7450f89369140ad1791188580dee0d) chore: bump Go to 1.16.2

### Changes from talos-systems/extras
2 commits

* [`c0fa0c0`](https://github.com/talos-systems/extras/commit/c0fa0c04641d8dfc418888c210788a6894e8d40c) feat: bump Go to 1.16.2 * [`5f89d77`](https://github.com/talos-systems/extras/commit/5f89d77a91f44d52146dae9c23b4654d219042b9) feat: bump Go to 1.16.1

### Changes from talos-systems/go-blockdevice
1 commit

* [`776b37d`](https://github.com/talos-systems/go-blockdevice/commit/776b37d31de0781f098f5d9d1894fbea3f2dfa1d) feat: add options to probe disk by various sysblock parameters

### Changes from talos-systems/pkgs
6 commits

* [`fdf4866`](https://github.com/talos-systems/pkgs/commit/fdf48667851b4c80b0ca220c574d2fb57a943f64) feat: bump tools for Go 1.16.2 * [`35f9b6f`](https://github.com/talos-systems/pkgs/commit/35f9b6f22bbe094e93723559132b2a23f0853c2b) feat: update kernel to 5.10.23 * [`dbae83e`](https://github.com/talos-systems/pkgs/commit/dbae83e704da264066ceeca20e0fe66883b542ba) fix: do not use git-lfs for rockpi4 binaries * [`1c6b9a3`](https://github.com/talos-systems/pkgs/commit/1c6b9a3a6ef91bce4f0cba18c466a9ece7b14750) feat: bump tools for Go 1.16.1 * [`c18073f`](https://github.com/talos-systems/pkgs/commit/c18073fe79b9d7ec36411c6f329fa60c580d4cea) feat: add u-boot for Rock Pi 4 * [`6b85a2b`](https://github.com/talos-systems/pkgs/commit/6b85a2bffbb144f25356eed6ed9dc8bb9a3fd392) feat: upgrade u-boot to 2021.04-rc3

### Changes from talos-systems/tools
4 commits

* [`41b8073`](https://github.com/talos-systems/tools/commit/41b807369779606f54d76e56038bfaf88d4f0f25) feat: bump protobuf-related tools * [`f7bce92`](https://github.com/talos-systems/tools/commit/f7bce92febdf9f58f2940952d5138494b9232ea8) chore: bump Go to 1.16.2 * [`bcf3380`](https://github.com/talos-systems/tools/commit/bcf3380dd55810e556851acbe20e20cb4ddd5ef0) feat: bump protobuf deps, add protoc-gen-go-grpc * [`b49c40e`](https://github.com/talos-systems/tools/commit/b49c40e0ad701f13192c1ad85ec616224343dc3f) feat: bump Go to 1.16.1

### Dependency Changes * **github.com/coreos/go-semver** v0.3.0 **_new_** * **github.com/golang/protobuf** v1.4.3 -> v1.5.1 * **github.com/google/go-cmp** v0.5.4 -> v0.5.5 * **github.com/hashicorp/go-multierror** v1.1.0 -> v1.1.1 * **github.com/talos-systems/extras** v0.2.0-1-g0db3328 -> v0.3.0-alpha.0-1-gc0fa0c0 * **github.com/talos-systems/go-blockdevice** bb3ad73f6983 -> 776b37d31de0 * **github.com/talos-systems/pkgs** v0.4.1-2-gd471b60 -> v0.5.0-alpha.0-3-gfdf4866 * **github.com/talos-systems/tools** v0.4.0-1-g3b25a7e -> v0.5.0-alpha.0-3-g41b8073 * **google.golang.org/grpc** v1.36.0 -> v1.36.1 * **google.golang.org/protobuf** v1.25.0 -> v1.26.0 * **k8s.io/api** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/apimachinery** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/apiserver** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/client-go** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/cri-api** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/kubectl** v0.20.5 -> v0.21.0-rc.0 * **k8s.io/kubelet** v0.20.5 -> v0.21.0-rc.0 Previous release can be found at [v0.9.0](https://github.com/talos-systems/talos/releases/tag/v0.9.0) ## [Talos 0.10.0-alpha.0](https://github.com/talos-systems/talos/releases/tag/v0.10.0-alpha.0) (2021-03-17) Welcome to the v0.10.0-alpha.0 release of Talos! *This is a pre-release of Talos* Please try out the release binaries and report any issues at https://github.com/talos-systems/talos/issues. ### SBCs * u-boot version was updated to fix the boot and USB issues on Raspberry Pi 4 8GiB version. * added support for Rock Pi 4. ### Contributors * Andrey Smirnov * Alexey Palazhchenko * Artem Chernyshev * Seán C McCord * Spencer Smith * Andrew Rynhard ### Changes
27 commits

* [`3455a8e8`](https://github.com/talos-systems/talos/commit/3455a8e8185ba25777784d392d6150a4a7e2d4a9) chore: use new release tool for changelogs and release notes * [`08271ba9`](https://github.com/talos-systems/talos/commit/08271ba93178c17a7c495788fea00c5c380f8301) chore: use Go 1.16 language version * [`7662d033`](https://github.com/talos-systems/talos/commit/7662d033bfc3d6e3878e2c2a2a1ec4d71dc2502e) fix: talosctl health should not check kube-proxy when it is disabled * [`0dbaeb9e`](https://github.com/talos-systems/talos/commit/0dbaeb9e655acdc44f8b4db6d1bc6da2ddf6cc9d) chore: update tools, use new generators * [`e31790f6`](https://github.com/talos-systems/talos/commit/e31790f6f548095fe3f1b9a5c88b47e70c197d2c) fix: properly format spec comments in the resources * [`78d384eb`](https://github.com/talos-systems/talos/commit/78d384ebb6246cf41a73014312dfb0d86a8008d6) test: update aws cloud provider version * [`3c5bfbb4`](https://github.com/talos-systems/talos/commit/3c5bfbb4736c86f493a665dbfe63a6e2d20acb3d) fix: don't touch any partitions on upgrade with --preserve * [`891f90fe`](https://github.com/talos-systems/talos/commit/891f90fee9818f0f013878c0c77c1920e6427a91) chore: update Linux to 5.10.23 * [`d4d77882`](https://github.com/talos-systems/talos/commit/d4d77882e3f53f2449f50f54116a407726f41ede) chore: update dependencies via dependabot * [`2e22f20b`](https://github.com/talos-systems/talos/commit/2e22f20bd876e4972bfdebd44fee13356b70b83f) docs: minor fixes to getting started * [`ca8a5596`](https://github.com/talos-systems/talos/commit/ca8a5596c79f638e52601e850236b715f906e3d2) chore: fix provision tests after changes to build-container * [`4aae924c`](https://github.com/talos-systems/talos/commit/4aae924c685ff578af06a1adceeec4f1938576a6) refactor: provide explicit logger for networkd * [`22f37530`](https://github.com/talos-systems/talos/commit/22f375300c1cc1d95db540afd510a21b66d7c8a3) chore: update golanci-lint to 1.38.0 * [`83b4e7f7`](https://github.com/talos-systems/talos/commit/83b4e7f744e3a8ed21443642a9afcf5b1342c62b) feat: add Rock pi 4 support * [`1362966f`](https://github.com/talos-systems/talos/commit/1362966ff546ee620c14e9312255616685743eed) docs: rewrite getting-started for ISO * [`8e57fc4f`](https://github.com/talos-systems/talos/commit/8e57fc4f526096878213048658bae50cfac4cda8) fix: move containerd CRI config files under `/var/` * [`6f7df3da`](https://github.com/talos-systems/talos/commit/6f7df3da1e147212e6d4b40a5de65e5ca8be84db) fix: update output of `convert-k8s` command * [`dce6118c`](https://github.com/talos-systems/talos/commit/dce6118c290afe957e375586b6bbc5b10ef6ba09) docs: add guide for VIP * [`ee5d9ffa`](https://github.com/talos-systems/talos/commit/ee5d9ffac60c93561874995d8926fc329e2b67dc) chore: bump Go to 1.16.1 * [`7c529e1c`](https://github.com/talos-systems/talos/commit/7c529e1cbd2be66d71e8496304781dd406495bdd) docs: fix links in the documentation * [`f596c7f6`](https://github.com/talos-systems/talos/commit/f596c7f6be3880be994faf7c5361628024c6be7d) docs: add video for raspberry pi install * [`47324dca`](https://github.com/talos-systems/talos/commit/47324dcaeaee94e4963eb3764fc01cd2d2d43041) docs: add guide on editing machine configuration * [`99d5f894`](https://github.com/talos-systems/talos/commit/99d5f894e17f39004e61ee9d5b64d5a8139f33d0) chore: update website npm dependencies * [`11056a80`](https://github.com/talos-systems/talos/commit/11056a80349e4c8df10a9ea98b6e3d53f96b971c) docs: add highlights for 0.9 release * [`ae8bedb9`](https://github.com/talos-systems/talos/commit/ae8bedb9a0d999bfbe97b6e18dc2eff62f0fcb80) docs: add control plane conversion guide and 0.9 upgrade notes * [`ed9673e5`](https://github.com/talos-systems/talos/commit/ed9673e50a7cb973fc49be9c2d659447a4c5bd62) docs: add troubleshooting control plane documentation * [`485cb126`](https://github.com/talos-systems/talos/commit/485cb1262f97e982ea81597b49d173836c75558d) docs: update Kubernetes upgrade guide

### Changes since v0.10.0-alpha.0
0 commit

### Changes from talos-systems/extras
1 commit

* [`5f89d77`](https://github.com/talos-systems/extras/commit/5f89d77a91f44d52146dae9c23b4654d219042b9) feat: bump Go to 1.16.1

### Changes from talos-systems/os-runtime
1 commit

* [`7b3d144`](https://github.com/talos-systems/os-runtime/commit/7b3d14457439d4fc10928cd6332c867b4acbae45) feat: use go-yaml fork and serialize spec as RawYAML objects

### Changes from talos-systems/pkgs
5 commits

* [`35f9b6f`](https://github.com/talos-systems/pkgs/commit/35f9b6f22bbe094e93723559132b2a23f0853c2b) feat: update kernel to 5.10.23 * [`dbae83e`](https://github.com/talos-systems/pkgs/commit/dbae83e704da264066ceeca20e0fe66883b542ba) fix: do not use git-lfs for rockpi4 binaries * [`1c6b9a3`](https://github.com/talos-systems/pkgs/commit/1c6b9a3a6ef91bce4f0cba18c466a9ece7b14750) feat: bump tools for Go 1.16.1 * [`c18073f`](https://github.com/talos-systems/pkgs/commit/c18073fe79b9d7ec36411c6f329fa60c580d4cea) feat: add u-boot for Rock Pi 4 * [`6b85a2b`](https://github.com/talos-systems/pkgs/commit/6b85a2bffbb144f25356eed6ed9dc8bb9a3fd392) feat: upgrade u-boot to 2021.04-rc3

### Changes from talos-systems/tools
2 commits

* [`bcf3380`](https://github.com/talos-systems/tools/commit/bcf3380dd55810e556851acbe20e20cb4ddd5ef0) feat: bump protobuf deps, add protoc-gen-go-grpc * [`b49c40e`](https://github.com/talos-systems/tools/commit/b49c40e0ad701f13192c1ad85ec616224343dc3f) feat: bump Go to 1.16.1

### Dependency Changes * **github.com/hashicorp/go-multierror** v1.1.0 -> v1.1.1 * **github.com/talos-systems/extras** v0.2.0 -> v0.3.0-alpha.0 * **github.com/talos-systems/os-runtime** 84c3c875eb2b -> 7b3d14457439 * **github.com/talos-systems/pkgs** v0.4.1 -> v0.5.0-alpha.0-2-g35f9b6f * **github.com/talos-systems/tools** v0.4.0 -> v0.5.0-alpha.0-1-gbcf3380 Previous release can be found at [v0.9.0-beta.0](https://github.com/talos-systems/talos/releases/tag/v0.9.0-beta.0) ## [v0.9.0-alpha.5](https://github.com/talos-systems/talos/compare/v0.9.0-alpha.4...v0.9.0-alpha.5) (2021-03-03) ### Chore * bump Go module dependencies * properly propagate context object in the controller ### Feat * bypass lock if ACPI reboot/shutdown issued * add `--on-reboot` flag to talosctl edit/patch machineConfig * support JSON output in `talosctl get`, event types * rename namespaces, resources, types etc ## [v0.9.0-alpha.4](https://github.com/talos-systems/talos/compare/v0.9.0-alpha.3...v0.9.0-alpha.4) (2021-03-02) ### Chore * update provision/upgrade tests to 0.9.0-alpha.3 ### Docs * bump v0.8 release version in the SBCs guides * add disk encryption guide ### Feat * update linux kernel to 5.10.19 ### Fix * ignore 'ENOENT' (no such file directory) on mount * move etcd to `cri` containerd runner ## [v0.9.0-alpha.3](https://github.com/talos-systems/talos/compare/v0.9.0-alpha.2...v0.9.0-alpha.3) (2021-03-01) ### Chore * bump dependencies via dependabot * build both Darwin and Linux versions of talosctl * bump dependencies via dependabot * switch CI to stop embedding local registry into the builds ### Docs * update AMI images for 0.8.4 ### Feat * implement etcd remove-member cli command * update etcd to 3.4.15 * talosctl: allow v-prefixed k8s versions * implement simple layer 2 shared IP for CP * implement talosctl edit and patch config commands * bump etcd client library to 3.5.0-alpha.0 ### Fix * update in-cluster kubeconfig validity to match other certs * add ApplyDynamicConfig call in the apply-config --immediate mode * set hdmi_safe=1 on Raspberry Pi for maximum HDMI compatibility * show stopped/exited containers via CRI inspector * make ApplyDynamicConfig idempotent * improve the drain function * correctly set service state in the resource * update the layout of the Disks API to match proxying requirements * stop and clean up installer container correctly * sanitize volume name better in static pod extra volumes ### Refactor * add context to the networkd * split WithNetworkConfig into sub-options ### Test * add integration test with Canal CNI and reset API * upgrade master to master tests ## [v0.9.0-alpha.2](https://github.com/talos-systems/talos/compare/v0.9.0-alpha.1...v0.9.0-alpha.2) (2021-02-20) ### Chore * add default cron pipeline to the list of pipelines * run default pipeline as part of the `cron` pipeline ### Docs * add link to GitHub Discussions as a support forum ### Feat * u-boot 2021.01, ca-certificates update, Linux file ACLs * support control plane upgrades with Talos managed control plane * add support for extra volume mounts for control plane pods * add a warning to boot log if running self-hosted control plane * add an option to disable kube-proxy manifest * update Kubernetes to 1.20.4 * add state encryption support ### Fix * redirect warnings in manifest apply k8s client * handle case when kubelet serving certificates are issued * correctly escape extra args in kube-proxy manifest * skip empty manifest YAML sub-documents ### Refactor * split kubernetes/etcd resource generation into subresources ### Test * enable disk encryption key rotation test * update integration tests to use wrapped client for etcd APIs ## [v0.9.0-alpha.1](https://github.com/talos-systems/talos/compare/v0.9.0-alpha.0...v0.9.0-alpha.1) (2021-02-09) ### Chore * update artifacts bucket name in Drone * rework Drone pipelines * update dependencies via dependabot * **ci:** fix schedules in Drone pipelines * **ci:** update gcp templates ### Docs * update AMI list for 0.8.2 * fix typos ### Feat * add a tool and package to convert self-hosted CP to static pods * implement ephemeral partition encryption * add resource watch API + CLI * rename apply-config --no-reboot to --on-reboot * skip filesystem for state and ephemeral partitions in the installer * stop all pods before unmounting ephemeral partition * bump Go to 1.15.8 * support version contract for Talos config generation * update Linux to 5.10.14 * add an option to force upgrade without checks * upgrade CoreDNS to 1.8.0 * implement IPv6 DHCP client in networkd ### Fix * correctly unwrap responses for etcd commands * drop cri dependency on etcd * move versions to annotations in control plane static pods * find master node IPs correctly in health checks * add 3 seconds grub boot timeout * don't use filename from URL when downloading manifest * pass attributes when adding routes * correct response structure for GenerateConfig API * correctly extract wrapped error messages * prevent crash in machined on apid service stop * wait for time sync before generating Kubernetes certificates * set proper hostname on docker nodes * mount kubelet secrets from system instead of ephemeral * allow loading of empty config files * prefer configured nameservers, fix DHCP6 in container * refresh control plane endpoints on worker apids on schedule * update DHCP client to use Request-Ack sequence after an Offer ### Refactor * extract go-cmd into a separate library ### Test * trigger e2e on thrice daily * update aws templates * add support for IPv6 in talosctl cluster create ## [v0.9.0-alpha.0](https://github.com/talos-systems/talos/compare/v0.8.1...v0.9.0-alpha.0) (2021-02-01) ### Chore * bump dependencies (via dependabot) * fix import path for fsnotify * add dependabot config * enable virtio-balloon and monitor in QEMU provisioner * update protobuf, grpc-go, prototool * update upgrade test version used ### Docs * update components.md * add v0.9 docs * add modes to validate command * document omitting DiskPartition size * update references to 0.8.0, add 0.8.0 AWS AMIs * fix latest docs * set latest docs to v0.8 * provide AMIs for 0.8.0-beta.0 * fix SBC docs to point to beta.0 instead of beta.1 * update Talos release for SBCs ### Feat * move to ECDSA keys for all Kubernetes/etcd certs and keys * update kernel * mount hugetlbfs * allow fqdn to be used when registering k8s node * copy cryptsetup executable from pkgs * use multi-arch images for k8s and Flannel CNI * replace bootkube with Talos-managed control plane * implement resource API in Talos * update Linux to 5.10.7, musl-libc to 1.2.2 * update Kubernetes to 1.20.2 * support Wireguard networking * bump pkgs for kernel with CONFIG_IPV6_MULTIPLE_TABLES * support type filter in list API and CLI * add commands to manage/query etcd cluster * support disk image in talosctl cluster create * update Kubernetes to 1.20.1 ### Fix * use hugetlbfs instead of none * use grpc load-balancing when connecting to trustd * lower memory usage a bit by disabling memory profiling * don't probe disks in container mode * prefix rendered Talos-owned static pod manifests * bump timeout for worker apid waiting for kubelet client config * kill all processes and umount all disk on reboot/shutdown * open blockdevices with exclusive flock for partitioning * list command unlimited recursion default behavior * pick first interface valid hostname (vs. last one) * allow 'console' argument in kernel args to be always overridden * bring up bonded interfaces correctly on packet * checkpoint controller-manager and scheduler * correctly transport gRPC errors from apid * use SetAll instead of AppendAll when building kernel args * add more dependencies for bootstrap services * pass disk image flags to e2e-qemu cluster create command * ignore pods spun up from checkpoints in health checks * leave etcd for staged upgrades * ignore errors on stopping/removing pod sandboxes * use the correct console on Banana Pi M64 * don't run LabelNodeAsMaster in two sequences ### Refactor * update go-blockdevice and restructure disk interaction code * define default kernel flags in machinery instead of procfs ### Test * clear connection refused errors after reset * skip etcd tests on non-HA clusters ## [v0.8.0-alpha.3](https://github.com/talos-systems/talos/compare/v0.8.0-alpha.2...v0.8.0-alpha.3) (2020-12-10) ### Chore * update CONTRIBUTING.md * limit unit-test run concurrency * bump Go to 1.15.6 * bump dockerfile frontend version * fix conform for releases ### Docs * update Equinix Metal guide * add architectural doc on the root file system layout * add a note on caveats in container mode * add storage doc * add guide for custom CAs * add docs for network connectivity * improve SBC documentation ### Feat * update kernel to 5.9.13, new KSPP requirements * reset with system disk wipe spec * add talosctl merge config command * add talosctl config contexts * update Kubernetes to 1.20.0 * implement "staged" (failsafe/backup) upgrades * allow disabling NoSchedule taint on masters using TUI installer ### Fix * remove kmsg ratelimiting on startup * zero out partitions without filesystems on install * make interactive installer work without endpoints provided ### Test * add ISO test * add support for mounting ISO in talosctl cluster create * bump Talos release version for upgrade test to 0.7.1 * bump defaults for provision tests resources ## [v0.8.0-alpha.2](https://github.com/talos-systems/talos/compare/v0.8.0-alpha.1...v0.8.0-alpha.2) (2020-12-04) ### Chore * publish Rock64 image * enable thrice daily pipeline * run integration test thrice daily * output SBC images as compressed raw images * build SBC images * update module dependencies * drop support for `docker load` * fix metal image name * use IMAGE_TAG instead of TAG for :latest pushes ### Docs * fix typos * add openstack docs * ensure port for vbox and proxmox docs * add console kernel arg to rpi_4 image generation * add console kernel arg to libretech_all_h3_cc_h5 image generation ### Feat * add support for the Pine64 Rock64 * add TUI for configuring network interfaces settings * make GenerateConfiguration accept current time as a parameter * introduce configpatcher package in machinery * suggest fixed control plane endpoints in talosctl gen config * update kubernetes to 1.20.0-rc.0 * allow boards to set kernel args * add support for the Banana Pi M64 * stop including K8s version by default in `talosctl gen config` * add support for the Raspberry Pi 4 Model B * implement network interfaces list API * bump package for kernel with CIFS support * upgrade etcd to 3.4.14 * update Containerd and Linux * add support for installing to SBCs * add ability to choose CNI config ### Fix * make default generate image arch dynamic based on arch * stabilize serial console on RPi4, add video console * make reset work again * node taint doesn't contain value anymore * defer resolving config context in client code * remove value (change to empty) for `NoSchedule` taint * prevent endless loop with DHCP requests in networkd * skip `board` argument to the installer if it's not set * use the dtb from kernel pkg for libretech_all_h3_cc_h5 * prevent crash in `talosctl config` commands * update generated .ova manifest for raw disk size * **security:** update Containerd to v1.4.3 ### Release * **v0.8.0-alpha.2:** prepare release ## [v0.8.0-alpha.1](https://github.com/talos-systems/talos/compare/v0.8.0-alpha.0...v0.8.0-alpha.1) (2020-11-26) ### Chore * add cloud image uploader (AWS AMIs for now) * bump K8s to 1.19.4 in e2e scripts with CABPT version * build arm64 images in CI * remove maintenance service interface and use machine service ### Docs * provide list of AMIs on AWS documentation page * add 0.8 docs for the upcoming release * ensure we configure nodes in guides * ensure gcp docs have firewall and node info * add qemu diagram and video walkthrough * graduate v0.7 docs * improve configuration reference documentation * fix small typo in talosctl processes cast * update asciinemas with talosctl * add proxmox doc * add live walkthroughs where applicable ### Feat * support openstack platform * update Kubernetes to v1.20.0-beta.2 * change UI component for disks selector * support cluster expansion in the interactive installer * implement apply configuration without reboot * make GenerateConfiguration API reuse current node auth * sync time before installer runs * set interface MTU in DHCP mode even if DHCP is not successful * print hint about using interative installer in mainenance mode * add TUI based talos interactive installer * support ipv6 routes * return client config as the second value in GenerateConfiguration * correctly merge talosconfig (don't ever overwrite) * drop to maintenance mode in cloud platforms if userdata is missing * read config from extra guestinfo key (vmware) * update Go to 1.15.5 * add generate config gRPC API * upgrade Kubernetes default version to 1.19.4 * add example command in maintenance, enforce cert fingerprint * add storage API ### Fix * bump blockdevice library for `mmcblk` part name fix * ignore 'not found' errors when stopping/removing CRI pods * return hostname from packet platform * make fingerprint clearly optional in a boot hint * ensure packet nics get all IPs * use ghcr.io/talos-systems/kubelet * bump timeout for config downloading on bare metal ### Refactor * drop osd compatibility layer ### Release * **v0.8.0-alpha.1:** prepare release ### Test * update integration test versions, clean up names ================================================ FILE: CODE_OF_CONDUCT.md ================================================ ## Code of Conduct ### Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ### Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ### Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ### Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ### Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [INSERT EMAIL ADDRESS]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ### Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing More information about contributing to Talos can be found in the [Documentation](https://docs.siderolabs.com/talos/latest/build-and-extend-talos/custom-images-and-development/developing-talos). ## Developer Certificate of Origin All commits require a [DCO](https://developercertificate.org/) sign-off. This is done by committing with the `--signoff` flag. ## Development The build process for this project is designed to run entirely in containers. To get started, run `make help` and follow the instructions. ## Conformance To verify conformance status, run `make conformance`. This runs a series of tests on the working tree and is required to pass before a contribution is accepted. ================================================ FILE: Dockerfile ================================================ # syntax = docker/dockerfile-upstream:1.22.0-labs # Meta args applied to stage base names. ARG TOOLS=scratch ARG PKGS=scratch ARG INSTALLER_ARCH=scratch ARG DEBUG_TOOLS_SOURCE=scratch ARG PKGS_PREFIX=scratch ARG TOOLS_PREFIX=scratch ARG GENERATE_VEX_PREFIX=scratch ARG GENERATE_VEX=scratch ARG PKG_APPARMOR=scratch ARG PKG_CA_CERTIFICATES=scratch ARG PKG_CNI=scratch ARG PKG_CONTAINERD=scratch ARG PKG_CPIO=scratch ARG PKG_CRYPTSETUP=scratch ARG PKG_DOSFSTOOLS=scratch ARG PKG_E2FSPROGS=scratch ARG PKG_FHS=scratch ARG PKG_FLANNEL_CNI=scratch ARG PKG_GLIB=scratch ARG PKG_GRUB=scratch ARG PKG_IGZIP=scratch ARG PKG_IPTABLES=scratch ARG PKG_IPXE=scratch ARG PKG_KERNEL=scratch ARG PKG_KMOD=scratch ARG PKG_LIBAIO=scratch ARG PKG_LIBARCHIVE=scratch ARG PKG_LIBATTR=scratch ARG PKG_LIBBURN=scratch ARG PKG_LIBCAP=scratch ARG PKG_LIBINIH=scratch ARG PKG_LIBISOBURN=scratch ARG PKG_LIBISOFS=scratch ARG PKG_LIBJANSSON=scratch ARG PKG_LIBJSON_C=scratch ARG PKG_LIBLZMA=scratch ARG PKG_LIBMNL=scratch ARG PKG_LIBNFTNL=scratch ARG PKG_LIBPOPT=scratch ARG PKG_LIBSELINUX=scratch ARG PKG_LIBSEPOL=scratch ARG PKG_LIBURCU=scratch ARG PKG_LINUX_FIRMWARE=scratch ARG PKG_LVM2=scratch ARG PKG_MTOOLS=scratch ARG PKG_MUSL=scratch ARG PKG_NFTABLES=scratch ARG PKG_OPENSSL=scratch ARG PKG_OPEN_VMDK=scratch ARG PKG_PCRE2=scratch ARG PKG_PIGZ=scratch ARG PKG_QEMU_TOOLS=scratch ARG PKG_RUNC=scratch ARG PKG_SD_BOOT=scratch ARG PKG_SQUASHFS_TOOLS=scratch ARG PKG_SYSTEMD_UDEVD=scratch ARG PKG_TALOSCTL_CNI_BUNDLE=scratch ARG PKG_TAR=scratch ARG PKG_UTIL_LINUX=scratch ARG PKG_XFSPROGS=scratch ARG PKG_XZ=scratch ARG PKG_ZLIB=scratch ARG PKG_ZSTD=scratch ARG DEBUG_TOOLS_SOURCE=scratch ARG EMBED_TARGET=embed # Resolve package images using ${PKGS} to be used later in COPY --from=. FROM ${PKG_FHS} AS pkg-fhs FROM ${PKG_CA_CERTIFICATES} AS pkg-ca-certificates FROM --platform=amd64 ${PKG_APPARMOR} AS pkg-apparmor-amd64 FROM --platform=arm64 ${PKG_APPARMOR} AS pkg-apparmor-arm64 FROM --platform=amd64 ${PKG_CRYPTSETUP} AS pkg-cryptsetup-amd64 FROM --platform=arm64 ${PKG_CRYPTSETUP} AS pkg-cryptsetup-arm64 FROM --platform=amd64 ${PKG_CONTAINERD} AS pkg-containerd-amd64 FROM --platform=arm64 ${PKG_CONTAINERD} AS pkg-containerd-arm64 FROM --platform=amd64 ${PKG_DOSFSTOOLS} AS pkg-dosfstools-amd64 FROM --platform=arm64 ${PKG_DOSFSTOOLS} AS pkg-dosfstools-arm64 FROM --platform=amd64 ${PKG_E2FSPROGS} AS pkg-e2fsprogs-amd64 FROM --platform=arm64 ${PKG_E2FSPROGS} AS pkg-e2fsprogs-arm64 FROM --platform=amd64 ${PKG_SYSTEMD_UDEVD} AS pkg-systemd-udevd-amd64 FROM --platform=arm64 ${PKG_SYSTEMD_UDEVD} AS pkg-systemd-udevd-arm64 FROM --platform=amd64 ${PKG_LIBCAP} AS pkg-libcap-amd64 FROM --platform=arm64 ${PKG_LIBCAP} AS pkg-libcap-arm64 FROM ${PKG_GRUB} AS pkg-grub FROM --platform=amd64 ${PKG_GRUB} AS pkg-grub-amd64 FROM --platform=arm64 ${PKG_GRUB} AS pkg-grub-arm64 FROM ${PKG_SD_BOOT} AS pkg-sd-boot FROM --platform=amd64 ${PKG_SD_BOOT} AS pkg-sd-boot-amd64 FROM --platform=arm64 ${PKG_SD_BOOT} AS pkg-sd-boot-arm64 FROM --platform=amd64 ${PKG_IPTABLES} AS pkg-iptables-amd64 FROM --platform=arm64 ${PKG_IPTABLES} AS pkg-iptables-arm64 FROM --platform=amd64 ${PKG_IPXE} AS pkg-ipxe-amd64 FROM --platform=arm64 ${PKG_IPXE} AS pkg-ipxe-arm64 FROM --platform=amd64 ${PKG_LIBARCHIVE} AS pkg-libarchive-amd64 FROM --platform=arm64 ${PKG_LIBARCHIVE} AS pkg-libarchive-arm64 FROM --platform=amd64 ${PKG_LIBATTR} AS pkg-libattr-amd64 FROM --platform=arm64 ${PKG_LIBATTR} AS pkg-libattr-arm64 FROM --platform=amd64 ${PKG_LIBINIH} AS pkg-libinih-amd64 FROM --platform=arm64 ${PKG_LIBINIH} AS pkg-libinih-arm64 FROM --platform=amd64 ${PKG_LIBJANSSON} AS pkg-libjansson-amd64 FROM --platform=arm64 ${PKG_LIBJANSSON} AS pkg-libjansson-arm64 FROM --platform=amd64 ${PKG_LIBJSON_C} AS pkg-libjson-c-amd64 FROM --platform=arm64 ${PKG_LIBJSON_C} AS pkg-libjson-c-arm64 FROM --platform=amd64 ${PKG_LIBMNL} AS pkg-libmnl-amd64 FROM --platform=arm64 ${PKG_LIBMNL} AS pkg-libmnl-arm64 FROM --platform=amd64 ${PKG_LIBNFTNL} AS pkg-libnftnl-amd64 FROM --platform=arm64 ${PKG_LIBNFTNL} AS pkg-libnftnl-arm64 FROM --platform=amd64 ${PKG_LIBPOPT} AS pkg-libpopt-amd64 FROM --platform=arm64 ${PKG_LIBPOPT} AS pkg-libpopt-arm64 FROM --platform=amd64 ${PKG_LIBURCU} AS pkg-liburcu-amd64 FROM --platform=arm64 ${PKG_LIBURCU} AS pkg-liburcu-arm64 FROM --platform=amd64 ${PKG_LIBSEPOL} AS pkg-libsepol-amd64 FROM --platform=arm64 ${PKG_LIBSEPOL} AS pkg-libsepol-arm64 FROM --platform=amd64 ${PKG_LIBSELINUX} AS pkg-libselinux-amd64 FROM --platform=arm64 ${PKG_LIBSELINUX} AS pkg-libselinux-arm64 FROM --platform=amd64 ${PKG_PCRE2} AS pkg-pcre2-amd64 FROM --platform=arm64 ${PKG_PCRE2} AS pkg-pcre2-arm64 FROM --platform=amd64 ${PKG_OPENSSL} AS pkg-openssl-amd64 FROM --platform=arm64 ${PKG_OPENSSL} AS pkg-openssl-arm64 # linux-firmware is not arch-specific FROM --platform=amd64 ${PKG_LINUX_FIRMWARE} AS pkg-linux-firmware FROM --platform=amd64 ${PKG_LVM2} AS pkg-lvm2-amd64 FROM --platform=arm64 ${PKG_LVM2} AS pkg-lvm2-arm64 FROM --platform=amd64 ${PKG_LIBAIO} AS pkg-libaio-amd64 FROM --platform=arm64 ${PKG_LIBAIO} AS pkg-libaio-arm64 FROM --platform=amd64 ${PKG_NFTABLES} AS pkg-nftables-amd64 FROM --platform=arm64 ${PKG_NFTABLES} AS pkg-nftables-arm64 FROM --platform=amd64 ${PKG_MUSL} AS pkg-musl-amd64 FROM --platform=arm64 ${PKG_MUSL} AS pkg-musl-arm64 FROM --platform=amd64 ${PKG_RUNC} AS pkg-runc-amd64 FROM --platform=arm64 ${PKG_RUNC} AS pkg-runc-arm64 FROM --platform=amd64 ${PKG_XFSPROGS} AS pkg-xfsprogs-amd64 FROM --platform=arm64 ${PKG_XFSPROGS} AS pkg-xfsprogs-arm64 FROM ${PKG_UTIL_LINUX} AS pkg-util-linux FROM --platform=amd64 ${PKG_UTIL_LINUX} AS pkg-util-linux-amd64 FROM --platform=arm64 ${PKG_UTIL_LINUX} AS pkg-util-linux-arm64 FROM --platform=amd64 ${PKG_KMOD} AS pkg-kmod-amd64 FROM --platform=arm64 ${PKG_KMOD} AS pkg-kmod-arm64 FROM --platform=amd64 ${PKG_CNI} AS pkg-cni-amd64 FROM --platform=arm64 ${PKG_CNI} AS pkg-cni-arm64 FROM --platform=amd64 ${PKG_FLANNEL_CNI} AS pkg-flannel-cni-amd64 FROM --platform=arm64 ${PKG_FLANNEL_CNI} AS pkg-flannel-cni-arm64 FROM ${PKG_KERNEL} AS pkg-kernel FROM --platform=amd64 ${PKG_KERNEL} AS pkg-kernel-amd64 FROM --platform=arm64 ${PKG_KERNEL} AS pkg-kernel-arm64 FROM ${PKG_PIGZ} AS pkg-pigz FROM --platform=arm64 ${PKG_PIGZ} AS pkg-pigz-arm64 FROM ${PKG_ZLIB} AS pkg-zlib FROM --platform=arm64 ${PKG_ZLIB} AS pkg-zlib-arm64 FROM --platform=amd64 ${PKG_IGZIP} AS pkg-igzip-amd64 FROM ${PKG_CPIO} AS pkg-cpio FROM ${PKG_DOSFSTOOLS} AS pkg-dosfstools FROM ${PKG_E2FSPROGS} AS pkg-e2fsprogs FROM ${PKG_GLIB} AS pkg-glib FROM ${PKG_KMOD} AS pkg-kmod FROM ${PKG_LIBARCHIVE} AS pkg-libarchive FROM ${PKG_LIBATTR} AS pkg-libattr FROM ${PKG_LIBBURN} AS pkg-libburn FROM ${PKG_LIBINIH} AS pkg-libinih FROM ${PKG_LIBISOBURN} AS pkg-libisoburn FROM ${PKG_LIBISOFS} AS pkg-libisofs FROM ${PKG_LIBLZMA} AS pkg-liblzma FROM ${PKG_LIBURCU} AS pkg-liburcu FROM ${PKG_MTOOLS} AS pkg-mtools FROM ${PKG_MUSL} AS pkg-musl FROM ${PKG_OPENSSL} AS pkg-openssl FROM ${PKG_OPEN_VMDK} AS pkg-open-vmdk FROM ${PKG_PCRE2} AS pkg-pcre2 FROM ${PKG_QEMU_TOOLS} AS pkg-qemu-tools FROM ${PKG_SQUASHFS_TOOLS} AS pkg-squashfs-tools FROM ${PKG_TAR} AS pkg-tar FROM ${PKG_XFSPROGS} AS pkg-xfsprogs FROM ${PKG_XZ} AS pkg-xz FROM ${PKG_ZSTD} AS pkg-zstd FROM --platform=amd64 ${TOOLS_PREFIX}:${TOOLS} AS tools-amd64 FROM --platform=arm64 ${TOOLS_PREFIX}:${TOOLS} AS tools-arm64 FROM scratch AS pkg-debug-tools-scratch-amd64 FROM scratch AS pkg-debug-tools-scratch-arm64 FROM scratch AS pkg-debug-tools-bash-minimal-amd64 COPY --from=tools-amd64 /usr/bin/bash /bin/bash COPY --from=tools-amd64 /usr/lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1 COPY --from=tools-amd64 /usr/bin/cat /bin/cat COPY --from=tools-amd64 /usr/bin/ls /bin/ls COPY --from=tools-amd64 /usr/bin/tee /bin/tee FROM scratch AS pkg-debug-tools-bash-minimal-arm64 COPY --from=tools-arm64 /usr/bin/bash /bin/bash COPY --from=tools-arm64 /usr/lib/ld-musl-aarch64.so.1 /lib/ld-musl-aarch64.so.1 COPY --from=tools-arm64 /usr/bin/cat /bin/cat COPY --from=tools-arm64 /usr/bin/ls /bin/ls COPY --from=tools-arm64 /usr/bin/tee /bin/tee FROM pkg-debug-tools-${DEBUG_TOOLS_SOURCE}-amd64 AS pkg-debug-tools-amd64 FROM pkg-debug-tools-${DEBUG_TOOLS_SOURCE}-arm64 AS pkg-debug-tools-arm64 # Strip CNI package. FROM scratch AS pkg-cni-stripped-amd64 COPY --from=pkg-cni-amd64 /opt/cni/bin/bridge /opt/cni/bin/bridge COPY --from=pkg-cni-amd64 /opt/cni/bin/firewall /opt/cni/bin/firewall COPY --from=pkg-cni-amd64 /opt/cni/bin/host-local /opt/cni/bin/host-local COPY --from=pkg-cni-amd64 /opt/cni/bin/loopback /opt/cni/bin/loopback COPY --from=pkg-cni-amd64 /opt/cni/bin/portmap /opt/cni/bin/portmap COPY --from=pkg-cni-amd64 /usr/share/spdx/cni.spdx.json /usr/share/spdx/cni.spdx.json FROM scratch AS pkg-cni-stripped-arm64 COPY --from=pkg-cni-arm64 /opt/cni/bin/bridge /opt/cni/bin/bridge COPY --from=pkg-cni-arm64 /opt/cni/bin/firewall /opt/cni/bin/firewall COPY --from=pkg-cni-arm64 /opt/cni/bin/host-local /opt/cni/bin/host-local COPY --from=pkg-cni-arm64 /opt/cni/bin/loopback /opt/cni/bin/loopback COPY --from=pkg-cni-arm64 /opt/cni/bin/portmap /opt/cni/bin/portmap COPY --from=pkg-cni-arm64 /usr/share/spdx/cni.spdx.json /usr/share/spdx/cni.spdx.json FROM ${PKG_TALOSCTL_CNI_BUNDLE} AS pkgs-talosctl-cni-bundle # The tools target provides base toolchain for the build. FROM --platform=${BUILDPLATFORM} ${TOOLS_PREFIX}:${TOOLS} AS tools ENV GOTOOLCHAIN=local ENV CGO_ENABLED=0 SHELL ["/bin/bash", "-c"] # The build target creates a container that will be used to build Talos source # code. FROM --platform=${BUILDPLATFORM} tools AS build SHELL ["/bin/bash", "-c"] ENV GO111MODULE=on ENV GOPROXY=https://proxy.golang.org ARG CGO_ENABLED ENV CGO_ENABLED=${CGO_ENABLED} ARG GOFIPS140 ENV GOFIPS140=${GOFIPS140} ENV GOCACHE=/.cache/go-build ENV GOMODCACHE=/.cache/mod ARG SOURCE_DATE_EPOCH ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} # Go standard library is shipped with Talos, thus it must be tracked in SBOM COPY --link --from=tools /usr/share/spdx/golang.spdx.json /rootfs/usr/share/spdx/golang.spdx.json WORKDIR /src # The build-go target creates a container to build Go code with Go modules downloaded and verified. FROM build AS build-go COPY ./go.mod ./go.sum ./go.work ./ COPY ./pkg/machinery/go.mod ./pkg/machinery/go.sum ./pkg/machinery/ COPY ./hack/cloud-image-uploader/go.mod ./hack/cloud-image-uploader/go.sum ./hack/cloud-image-uploader/ COPY ./tools ./tools WORKDIR /src RUN --mount=type=cache,target=/.cache,id=talos/.cache go mod download RUN --mount=type=cache,target=/.cache,id=talos/.cache go mod verify # The generate target generates code from protobuf service definitions and machinery config. FROM build AS embed-generate ARG NAME ARG SHA ARG USERNAME ARG REGISTRY ARG TAG ARG ARTIFACTS ARG PKGS ARG TOOLS RUN mkdir -p pkg/machinery/gendata/data && \ echo -n ${NAME} > pkg/machinery/gendata/data/name && \ echo -n ${SHA} > pkg/machinery/gendata/data/sha && \ echo -n ${USERNAME} > pkg/machinery/gendata/data/username && \ echo -n ${REGISTRY} > pkg/machinery/gendata/data/registry && \ echo -n ${PKGS} > pkg/machinery/gendata/data/pkgs && \ echo -n ${TOOLS} > pkg/machinery/gendata/data/tools && \ echo -n ${TAG} > pkg/machinery/gendata/data/tag && \ echo -n ${ARTIFACTS} > pkg/machinery/gendata/data/artifacts FROM scratch AS embed COPY --from=embed-generate /src/pkg/machinery/gendata/data /pkg/machinery/gendata/data FROM embed-generate AS embed-abbrev-generate ARG ABBREV_TAG RUN echo -n "undefined" > pkg/machinery/gendata/data/sha && \ echo -n ${ABBREV_TAG} > pkg/machinery/gendata/data/tag RUN mkdir -p _out && \ echo PKGS=${PKGS} >> _out/talos-metadata && \ echo TOOLS=${TOOLS} >> _out/talos-metadata && \ echo TAG=${TAG} >> _out/talos-metadata FROM scratch AS embed-abbrev COPY --from=embed-abbrev-generate /src/pkg/machinery/gendata/data /pkg/machinery/gendata/data COPY --from=embed-abbrev-generate /src/_out/talos-metadata /_out/talos-metadata FROM ${EMBED_TARGET} AS embed-target # generate API descriptors FROM build-go AS api-descriptors-build WORKDIR /src/api COPY api . RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf format RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf build --exclude-source-info -o lock.binpb FROM --platform=${BUILDPLATFORM} scratch AS api-descriptors COPY --from=api-descriptors-build /src/api/lock.binpb /api/lock.binpb # format protobuf service definitions FROM build-go AS proto-format-build WORKDIR /src/api COPY api . RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf format FROM --platform=${BUILDPLATFORM} scratch AS fmt-protobuf COPY --from=proto-format-build /src/api/ /api/ # run docgen for machinery config FROM build-go AS go-generate COPY ./pkg ./pkg COPY ./hack/boilerplate.txt ./hack/boilerplate.txt COPY --from=embed-target / ./ RUN --mount=type=cache,target=/.cache,id=talos/.cache go generate ./pkg/... RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool golang.org/x/tools/cmd/goimports -w -local github.com/siderolabs/talos ./pkg/ RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool mvdan.cc/gofumpt -w ./pkg/ WORKDIR /src/pkg/machinery RUN --mount=type=cache,target=/.cache,id=talos/.cache go generate ./... RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/siderolabs/talos/tools/gotagsrewrite . RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool golang.org/x/tools/cmd/goimports -w -local github.com/siderolabs/talos ./ RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool mvdan.cc/gofumpt -w ./ FROM go-generate AS gen-proto-go WORKDIR /src/ RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/siderolabs/talos/tools/structprotogen github.com/siderolabs/talos/pkg/machinery/... /api/resource/definitions/ # compile protobuf service definitions FROM build-go AS generate-build COPY --from=proto-format-build /src/api /src/api/ COPY --from=gen-proto-go /api/resource/definitions/ /src/api/resource/definitions/ WORKDIR /src/api RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf build RUN --mount=type=cache,target=/.cache,id=talos/.cache,sharing=locked go tool github.com/bufbuild/buf/cmd/buf generate # Goimports and gofumpt generated files to adjust import order RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool golang.org/x/tools/cmd/goimports -w -local github.com/siderolabs/talos /src/api/machinery/ RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool mvdan.cc/gofumpt -w /src/api/machinery/ FROM scratch AS generate-build-clean COPY --from=generate-build /src/api /api/ FROM tools AS selinux COPY ./internal/pkg/selinux/policy/* /selinux/ RUN mkdir /policy; secilc -o /policy/policy.33 -f /policy/file_contexts -c 33 /selinux/**/*.cil -vvvvv -O FROM scratch AS selinux-generate COPY --from=selinux /policy /policy FROM scratch AS ipxe-generate COPY --from=pkg-ipxe-amd64 /usr/libexec/snp.efi /amd64/snp.efi COPY --from=pkg-ipxe-arm64 /usr/libexec/snp.efi /arm64/snp.efi FROM scratch AS microsoft-secureboot-database ARG MICROSOFT_SECUREBOOT_RELEASE ADD https://github.com/microsoft/secureboot_objects.git#${MICROSOFT_SECUREBOOT_RELEASE}:PreSignedObjects / FROM scratch AS microsoft-key-keys COPY --from=microsoft-secureboot-database /KEK/Certificates/*.der /kek/ FROM scratch AS microsoft-db-keys COPY --from=microsoft-secureboot-database /DB/Certificates/MicCor*.der /db/ COPY --from=microsoft-secureboot-database /DB/Certificates/microsoft*.der /db/ FROM --platform=${BUILDPLATFORM} scratch AS generate COPY --from=proto-format-build /src/api /api/ COPY --from=generate-build-clean /api/resource/definitions/ /api/resource/definitions/ COPY --from=generate-build-clean /api/machinery /pkg/machinery/ COPY --from=go-generate /src/pkg/imager/profile/ /pkg/imager/profile/ COPY --from=go-generate /src/pkg/machinery/resources/ /pkg/machinery/resources/ COPY --from=go-generate /src/pkg/machinery/config/schemas/ /pkg/machinery/config/schemas/ COPY --from=go-generate /src/pkg/machinery/config/types/ /pkg/machinery/config/types/ COPY --from=go-generate /src/pkg/machinery/imager/imageropts/ /pkg/machinery/imager/imageropts/ COPY --from=go-generate /src/pkg/machinery/nethelpers/ /pkg/machinery/nethelpers/ COPY --from=go-generate /src/pkg/machinery/extensions/ /pkg/machinery/extensions/ COPY --from=go-generate /src/pkg/machinery/version/os-release /pkg/machinery/version/os-release COPY --from=ipxe-generate / /pkg/provision/providers/vm/internal/ipxe/data/ipxe/ COPY --from=selinux-generate / /internal/pkg/selinux/ COPY --from=embed-abbrev / / COPY --from=pkg-ca-certificates /etc/ssl/certs/ca-certificates /internal/app/machined/pkg/controllers/secrets/data/ COPY --from=microsoft-key-keys / /internal/pkg/secureboot/database/certs/ COPY --from=microsoft-db-keys / /internal/pkg/secureboot/database/certs/ # The base target provides a container that can be used to build all Talos # assets. FROM build-go AS base COPY ./cmd ./cmd COPY ./pkg ./pkg COPY ./internal ./internal COPY --from=embed / ./ RUN --mount=type=cache,target=/.cache,id=talos/.cache go list all >/dev/null WORKDIR /src/pkg/machinery RUN --mount=type=cache,target=/.cache,id=talos/.cache go list all >/dev/null RUN --mount=type=cache,target=/.cache,id=talos/.cache go generate -v ./version WORKDIR /src # The vulncheck target runs the vulnerability check tool. FROM base AS lint-vulncheck RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool golang.org/x/vuln/cmd/govulncheck ./... # The lint-deadcode target runs the deadcode elimination check. FROM base AS lint-deadcode ARG GO_BUILDFLAGS ARG GO_LDFLAGS ARG GO_MACHINED_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS} ${GO_MACHINED_LDFLAGS} -dumpdep" ./internal/app/machined \ |& go tool github.com/aarzilli/whydeadcode > deadcode.txt RUN if [[ -s deadcode.txt ]]; then \ echo "Dead code elimination problem found:"; \ cat deadcode.txt; \ exit 1; \ else \ echo "No dead code elimination issues found"; \ fi # The init target builds the init binary. FROM base AS init-build-amd64 WORKDIR /src/internal/app/init ARG GO_BUILDFLAGS ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=v1 go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS}" -o /init RUN chmod +x /init FROM base AS init-build-arm64 WORKDIR /src/internal/app/init ARG GO_BUILDFLAGS ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=arm64 go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS}" -o /init RUN chmod +x /init FROM init-build-${TARGETARCH} AS init-build FROM scratch AS init COPY --from=init-build /init /init # The machined target builds the machined binary. FROM base AS machined-build-amd64 WORKDIR /src/internal/app/machined ARG GO_BUILDFLAGS ARG GO_LDFLAGS ARG GO_MACHINED_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS} ${GO_MACHINED_LDFLAGS}" -o /machined RUN chmod +x /machined FROM base AS machined-build-arm64 WORKDIR /src/internal/app/machined ARG GO_BUILDFLAGS ARG GO_LDFLAGS ARG GO_MACHINED_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=arm64 go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS} ${GO_MACHINED_LDFLAGS}" -o /machined RUN chmod +x /machined FROM machined-build-${TARGETARCH} AS machined-build FROM scratch AS machined COPY --from=machined-build /machined /machined # The talosctl targets build the talosctl binaries. FROM base AS talosctl-linux-amd64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-linux-amd64 RUN chmod +x /talosctl-linux-amd64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-linux-amd64 FROM base AS talosctl-linux-arm64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=arm64 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-linux-arm64 RUN chmod +x /talosctl-linux-arm64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-linux-arm64 FROM base AS talosctl-linux-armv7-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=arm GOARM=7 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-linux-armv7 RUN chmod +x /talosctl-linux-armv7 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-linux-armv7 FROM base AS talosctl-linux-riscv64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=riscv64 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-linux-riscv64 RUN chmod +x /talosctl-linux-riscv64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-linux-riscv64 FROM base AS talosctl-darwin-amd64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=darwin GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-darwin-amd64 RUN chmod +x /talosctl-darwin-amd64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-darwin-amd64 FROM base AS talosctl-darwin-arm64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=darwin GOARCH=arm64 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-darwin-arm64 RUN chmod +x /talosctl-darwin-arm64 RUN touch --date="@${SOURCE_DATE_EPOCH}" talosctl-darwin-arm64 FROM base AS talosctl-windows-amd64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=windows GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-windows-amd64.exe RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-windows-amd64.exe FROM base AS talosctl-windows-arm64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=windows GOARCH=arm64 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-windows-arm64.exe RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-windows-arm64.exe FROM base AS talosctl-freebsd-amd64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=freebsd GOARCH=amd64 GOAMD64=${GOAMD64} go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-freebsd-amd64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-freebsd-amd64 FROM base AS talosctl-freebsd-arm64-build WORKDIR /src/cmd/talosctl ARG GO_BUILDFLAGS_TALOSCTL ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=freebsd GOARCH=arm64 go build ${GO_BUILDFLAGS_TALOSCTL} -ldflags "${GO_LDFLAGS}" -o /talosctl-freebsd-arm64 RUN touch --date="@${SOURCE_DATE_EPOCH}" /talosctl-freebsd-arm64 FROM scratch AS talosctl-linux-amd64 COPY --from=talosctl-linux-amd64-build /talosctl-linux-amd64 /talosctl-linux-amd64 FROM scratch AS talosctl-linux-arm64 COPY --from=talosctl-linux-arm64-build /talosctl-linux-arm64 /talosctl-linux-arm64 FROM scratch AS talosctl-linux-armv7 COPY --from=talosctl-linux-armv7-build /talosctl-linux-armv7 /talosctl-linux-armv7 FROM scratch AS talosctl-linux-riscv64 COPY --from=talosctl-linux-riscv64-build /talosctl-linux-riscv64 /talosctl-linux-riscv64 FROM scratch AS talosctl-darwin-amd64 COPY --from=talosctl-darwin-amd64-build /talosctl-darwin-amd64 /talosctl-darwin-amd64 FROM scratch AS talosctl-darwin-arm64 COPY --from=talosctl-darwin-arm64-build /talosctl-darwin-arm64 /talosctl-darwin-arm64 FROM scratch AS talosctl-freebsd-amd64 COPY --from=talosctl-freebsd-amd64-build /talosctl-freebsd-amd64 /talosctl-freebsd-amd64 FROM scratch AS talosctl-freebsd-arm64 COPY --from=talosctl-freebsd-arm64-build /talosctl-freebsd-arm64 /talosctl-freebsd-arm64 FROM scratch AS talosctl-windows-amd64 COPY --from=talosctl-windows-amd64-build /talosctl-windows-amd64.exe /talosctl-windows-amd64.exe FROM scratch AS talosctl-windows-arm64 COPY --from=talosctl-windows-arm64-build /talosctl-windows-arm64.exe /talosctl-windows-arm64.exe FROM --platform=${BUILDPLATFORM} talosctl-${TARGETOS}-${TARGETARCH} AS talosctl-targetarch FROM scratch AS talosctl-all COPY --from=talosctl-linux-amd64 / / COPY --from=talosctl-linux-arm64 / / COPY --from=talosctl-linux-armv7 / / COPY --from=talosctl-linux-riscv64 / / COPY --from=talosctl-darwin-amd64 / / COPY --from=talosctl-darwin-arm64 / / COPY --from=talosctl-freebsd-amd64 / / COPY --from=talosctl-freebsd-arm64 / / COPY --from=talosctl-windows-amd64 / / COPY --from=talosctl-windows-arm64 / / FROM scratch AS talosctl ARG TARGETARCH COPY --from=talosctl-all /talosctl-linux-${TARGETARCH} /talosctl ARG TAG ENV VERSION=${TAG} LABEL "alpha.talos.dev/version"="${VERSION}" LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos ENTRYPOINT ["/talosctl"] # The kernel target is the linux kernel. FROM scratch AS kernel ARG TARGETARCH COPY --from=pkg-kernel /boot/vmlinuz /vmlinuz-${TARGETARCH} # The sd-boot target is the systemd-boot asset. FROM scratch AS sd-boot ARG TARGETARCH COPY --from=pkg-sd-boot /*.efi /sd-boot-${TARGETARCH}.efi # The sd-stub target is the systemd-stub asset. FROM scratch AS sd-stub ARG TARGETARCH COPY --from=pkg-sd-boot /*.efi.stub /sd-stub-${TARGETARCH}.efi FROM tools AS depmod-amd64 WORKDIR /staging COPY hack/modules-amd64.txt . COPY --from=pkg-kernel-amd64 /usr/lib/modules usr/lib/modules RUN < /talos.vex.json # This config contains IDs of the tracked, but affected vulnerabilities. # Once an advisory is made, the CI should go back to passing status. RUN /generate-vex grype-config --target-version $TAG > /talos.grype.yaml FROM scratch AS vex COPY --from=vex-generate /talos.vex.json /talos.vex.json COPY --from=vex-generate /talos.grype.yaml /talos.grype.yaml FROM build-go AS grype-scan COPY --from=sbom-arm64 /talos-arm64.spdx.json /talos-arm64.spdx.json COPY --from=vex /talos.vex.json /talos.vex.json RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool \ github.com/anchore/grype/cmd/grype sbom:/talos-arm64.spdx.json \ --vex /talos.vex.json -vv 2>&1 | tee /grype-scan.log FROM scratch AS grype-scan-result COPY --from=grype-scan /grype-scan.log /grype-scan.log FROM build-go AS grype-validate COPY --from=sbom-arm64 /talos-arm64.spdx.json /talos-arm64.spdx.json COPY --from=vex /talos.vex.json /talos.vex.json COPY --from=vex /talos.grype.yaml /talos.grype.yaml RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool \ github.com/anchore/grype/cmd/grype sbom:/talos-arm64.spdx.json \ --vex /talos.vex.json -vv --fail-on negligible --config /talos.grype.yaml FROM rootfs-base-${TARGETARCH} AS rootfs-base RUN rm -rf /rootfs/usr/share/spdx/* COPY --from=sbom-container-target / /rootfs/usr/share/spdx/ RUN echo "true" > /rootfs/usr/etc/in-container RUN rm -rf /rootfs/usr/lib/modules/* RUN find /rootfs -print0 \ | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" FROM rootfs-base-arm64 AS rootfs-squashfs-arm64 RUN rm -rf /rootfs/usr/share/spdx/* COPY --from=sbom-arm64 / /rootfs/usr/share/spdx/ ARG ZSTD_COMPRESSION_LEVEL COPY --from=selinux-generate /policy/file_contexts /file_contexts COPY ./hack/labeled-squashfs.sh / RUN fakeroot /labeled-squashfs.sh /rootfs /rootfs.sqsh /file_contexts ${ZSTD_COMPRESSION_LEVEL} FROM rootfs-base-amd64 AS rootfs-squashfs-amd64 RUN rm -rf /rootfs/usr/share/spdx/* COPY --from=sbom-amd64 / /rootfs/usr/share/spdx/ ARG ZSTD_COMPRESSION_LEVEL COPY --from=selinux-generate /policy/file_contexts /file_contexts COPY ./hack/labeled-squashfs.sh / RUN fakeroot /labeled-squashfs.sh /rootfs /rootfs.sqsh /file_contexts ${ZSTD_COMPRESSION_LEVEL} FROM scratch AS squashfs-arm64 COPY --from=rootfs-squashfs-arm64 /rootfs.sqsh / FROM scratch AS squashfs-amd64 COPY --from=rootfs-squashfs-amd64 /rootfs.sqsh / FROM scratch AS rootfs COPY --from=rootfs-base /rootfs / # The initramfs target provides the Talos initramfs image. FROM build AS initramfs-archive-arm64 WORKDIR /initramfs ARG ZSTD_COMPRESSION_LEVEL COPY --from=squashfs-arm64 /rootfs.sqsh . COPY --from=init-build-arm64 /init . RUN find . -print0 \ | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" RUN set -o pipefail \ && find . 2>/dev/null \ | LC_ALL=c sort \ | cpio --reproducible -H newc -o \ | zstd -c -T0 -${ZSTD_COMPRESSION_LEVEL} \ > /initramfs.xz FROM build AS initramfs-archive-amd64 WORKDIR /initramfs ARG ZSTD_COMPRESSION_LEVEL COPY --from=squashfs-amd64 /rootfs.sqsh . COPY --from=init-build-amd64 /init . RUN find . -print0 \ | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" RUN set -o pipefail \ && find . 2>/dev/null \ | LC_ALL=c sort \ | cpio --reproducible -H newc -o \ | zstd -c -T0 -${ZSTD_COMPRESSION_LEVEL} \ > /initramfs.xz FROM initramfs-archive-${TARGETARCH} AS initramfs-archive FROM scratch AS initramfs ARG TARGETARCH COPY --from=initramfs-archive /initramfs.xz /initramfs-${TARGETARCH}.xz # The talos target generates a docker image that can be used to run Talos # in containers. FROM scratch AS talos COPY --from=rootfs / / LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos ENTRYPOINT ["/sbin/init"] # The installer target generates an image that can be used to install Talos to # various environments. # Make the installer binary. FROM base AS installer-build ARG GO_BUILDFLAGS ARG GO_LDFLAGS WORKDIR /src/cmd/installer ARG TARGETARCH RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=${TARGETARCH} go build ${GO_BUILDFLAGS} -ldflags "${GO_LDFLAGS}" -o /installer RUN chmod +x /installer # Make the images containing the boot artifacts. FROM scratch AS install-artifacts-amd64 COPY --from=pkg-kernel-amd64 /boot/vmlinuz /usr/install/amd64/vmlinuz COPY --from=initramfs-archive-amd64 /initramfs.xz /usr/install/amd64/initramfs.xz COPY --from=pkg-sd-boot-amd64 /linuxx64.efi.stub /usr/install/amd64/systemd-stub.efi COPY --from=pkg-sd-boot-amd64 /systemd-bootx64.efi /usr/install/amd64/systemd-boot.efi COPY --from=sbom-amd64 /talos-amd64.spdx.json /usr/install/amd64/talos.spdx.json FROM scratch AS install-artifacts-arm64 COPY --from=pkg-kernel-arm64 /boot/vmlinuz /usr/install/arm64/vmlinuz COPY --from=initramfs-archive-arm64 /initramfs.xz /usr/install/arm64/initramfs.xz COPY --from=pkg-sd-boot-arm64 /linuxaa64.efi.stub /usr/install/arm64/systemd-stub.efi COPY --from=pkg-sd-boot-arm64 /systemd-bootaa64.efi /usr/install/arm64/systemd-boot.efi COPY --from=sbom-arm64 /talos-arm64.spdx.json /usr/install/arm64/talos.spdx.json FROM scratch AS install-artifacts-all COPY --from=install-artifacts-amd64 / / COPY --from=install-artifacts-arm64 / / FROM install-artifacts-${TARGETARCH} AS install-artifacts-targetarch FROM install-artifacts-${INSTALLER_ARCH} AS install-artifacts # Add the installer with a symlink as 'imager' and a /rootfs dir containing only the installer. FROM tools AS installer-image-gen COPY --from=installer-build /installer /rootfs/usr/bin/installer RUN ln -s installer /rootfs/usr/bin/imager # Add the installer binary and the tools needed to run the installer. FROM scratch AS installer-base-image ARG TARGETARCH ENV TARGETARCH=${TARGETARCH} COPY --link --from=pkg-fhs / / COPY --link --from=pkg-ca-certificates / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --from=pkg-musl / / COPY --link --from=pkg-dosfstools / / COPY --link --exclude=etc/bash_completion.d --from=pkg-grub / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libattr / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libinih / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-liblzma / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-liburcu / / COPY --link --from=pkg-mtools / / COPY --link --from=pkg-xfsprogs / / # Only copy the installer binary and none of the tools used for building it. COPY --link --from=installer-image-gen /rootfs / # Squash the installer-base-image layers to reduce size. FROM scratch AS installer-base-image-squashed COPY --from=installer-base-image / / # Add metadata. # 'installer-base' only contains the installer binary and the tools it uses. # 'installer-base' does not contain boot assets or talos itself. FROM installer-base-image-squashed AS installer-base ARG TAG ENV VERSION=${TAG} LABEL "alpha.talos.dev/version"="${VERSION}" LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos ENTRYPOINT ["/bin/installer"] # Imager can be thought of as an extended installer. # It has the boot artifacts and tools to build any requested talos image with desired modifications and system extensions. # Imager is meant to be run outside of talos and the talos installation flow. FROM installer-base-image-squashed AS imager-image COPY --link --from=pkg-cpio / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/lib/pkgconfig --from=pkg-e2fsprogs / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-glib / / COPY --link --from=pkg-grub-amd64 /usr/lib/grub /usr/lib/grub COPY --link --from=pkg-grub-arm64 /usr/lib/grub /usr/lib/grub COPY --link --exclude=usr/include --exclude=usr/lib/pkgconfig --exclude=usr/share/pkgconfig --exclude=usr/share/bash-completion --from=pkg-kmod / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libarchive / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libburn / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libisoburn / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-libisofs / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --exclude=usr/lib/cmake --from=pkg-openssl / / COPY --link --from=pkg-open-vmdk / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-pcre2 / / COPY --link --from=pkg-pigz / / COPY --link --from=pkg-qemu-tools / / COPY --link --from=pkg-squashfs-tools / / COPY --link --from=pkg-tar / / COPY --link --exclude=**/*.a --exclude=*.a --from=pkg-util-linux /usr/lib/libblkid.* /usr/lib/ COPY --link --exclude=**/*.a --exclude=*.a --from=pkg-util-linux /usr/lib/libuuid.* /usr/lib/ COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-xz / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-zlib / / COPY --link --exclude=**/*.a --exclude=**/*.la --exclude=usr/include --exclude=usr/lib/pkgconfig --from=pkg-zstd / / COPY --chmod=0644 hack/extra-modules.conf /etc/modules.d/10-extra-modules.conf COPY --from=install-artifacts / / FROM scratch AS imager-image-squashed COPY --from=imager-image / / FROM imager-image-squashed AS imager ARG TAG ENV VERSION=${TAG} LABEL "alpha.talos.dev/version"="${VERSION}" LABEL org.opencontainers.image.source=https://github.com/siderolabs/talos ENTRYPOINT ["/bin/imager"] FROM imager AS iso-amd64-build ARG SOURCE_DATE_EPOCH ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} RUN /bin/installer \ iso \ --arch amd64 \ --output /out FROM imager AS iso-arm64-build ARG SOURCE_DATE_EPOCH ENV SOURCE_DATE_EPOCH=${SOURCE_DATE_EPOCH} RUN /bin/installer \ iso \ --arch arm64 \ --output /out FROM scratch AS iso-amd64 COPY --from=iso-amd64-build /out / FROM scratch AS iso-arm64 COPY --from=iso-arm64-build /out / FROM --platform=${BUILDPLATFORM} iso-${TARGETARCH} AS iso # The test target performs tests on the source code. FROM base AS unit-tests-runner COPY --from=rootfs / / COPY --from=pkg-ca-certificates / / ARG TESTPKGS ENV PLATFORM=container ARG GO_LDFLAGS RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache,id=talos/.cache go test \ -ldflags "${GO_LDFLAGS}" \ -covermode=atomic -coverprofile=coverage.txt -coverpkg=${TESTPKGS} -p 4 ${TESTPKGS} FROM scratch AS unit-tests COPY --from=unit-tests-runner /src/coverage.txt /coverage.txt # The unit-tests-race target performs tests with race detector. FROM base AS unit-tests-race COPY --from=rootfs / / COPY --from=pkg-ca-certificates / / ARG TESTPKGS ENV PLATFORM=container ENV CGO_ENABLED=1 ARG GO_LDFLAGS RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache,id=talos/.cache go test \ -ldflags "${GO_LDFLAGS}" \ -race -p 4 ${TESTPKGS} # The unit-tests-fips target performs tests with FIPS strict mode. FROM base AS unit-tests-fips COPY --from=rootfs / / COPY --from=pkg-ca-certificates / / ARG TESTPKGS ENV PLATFORM=container ENV GOFIPS140=latest ENV GODEBUG=fips140=only,tlsmlkem=0 ARG GO_LDFLAGS RUN --security=insecure --mount=type=cache,id=testspace,target=/tmp --mount=type=cache,target=/.cache,id=talos/.cache go test \ -ldflags "${GO_LDFLAGS}" \ -p 4 ${TESTPKGS} # The integration-test targets builds integration test binary. FROM base AS integration-test-linux-amd64-build ARG GO_BUILDFLAGS ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=${GOAMD64} go test -v -c ${GO_BUILDFLAGS} \ -ldflags "${GO_LDFLAGS}" \ -tags integration,integration_api,integration_cli,integration_k8s \ ./internal/integration FROM scratch AS integration-test-linux-amd64 COPY --from=integration-test-linux-amd64-build /src/integration.test /integration-test-linux-amd64 FROM base AS integration-test-linux-arm64-build ARG GO_BUILDFLAGS ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=arm64 go test -v -c ${GO_BUILDFLAGS} \ -ldflags "${GO_LDFLAGS}" \ -tags integration,integration_api,integration_cli,integration_k8s \ ./internal/integration FROM scratch AS integration-test-linux-arm64 COPY --from=integration-test-linux-arm64-build /src/integration.test /integration-test-linux-arm64 FROM base AS integration-test-darwin-arm64-build ARG GO_BUILDFLAGS ARG GO_LDFLAGS RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=darwin GOARCH=arm64 go test -v -c ${GO_BUILDFLAGS} \ -ldflags "${GO_LDFLAGS}" \ -tags integration,integration_api,integration_cli,integration_k8s \ ./internal/integration FROM scratch AS integration-test-darwin-arm64 COPY --from=integration-test-darwin-arm64-build /src/integration.test /integration-test-darwin-arm64 FROM --platform=${BUILDPLATFORM} integration-test-${TARGETOS}-${TARGETARCH} AS integration-test-targetarch # The integration-test-provision target builds integration test binary with provisioning tests. FROM base AS integration-test-provision-linux-build ARG GO_BUILDFLAGS ARG GO_LDFLAGS ARG GOAMD64 RUN --mount=type=cache,target=/.cache,id=talos/.cache GOOS=linux GOARCH=amd64 GOAMD64=${GOAMD64} go test -v -c ${GO_BUILDFLAGS} \ -ldflags "${GO_LDFLAGS}" \ -tags integration,integration_provision \ ./internal/integration FROM scratch AS integration-test-provision-linux COPY --from=integration-test-provision-linux-build /src/integration.test /integration-test-provision-linux-amd64 # The lint target performs linting on the source code. FROM base AS lint-go COPY .golangci.yml . ENV GOGC=50 ENV GOLANGCI_LINT_CACHE=/.cache/lint RUN --mount=type=cache,target=/.cache,id=talos/.cache,sharing=locked go tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint config verify --config .golangci.yml RUN --mount=type=cache,target=/.cache,id=talos/.cache,sharing=locked go tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint run --config .golangci.yml WORKDIR /src/pkg/machinery RUN --mount=type=cache,target=/.cache,id=talos/.cache,sharing=locked go tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint run --config ../../.golangci.yml COPY ./hack/cloud-image-uploader /src/hack/cloud-image-uploader WORKDIR /src/hack/cloud-image-uploader RUN --mount=type=cache,target=/.cache,id=talos/.cache,sharing=locked go tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint run --config ../../.golangci.yml WORKDIR /src RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/siderolabs/importvet/cmd/importvet github.com/siderolabs/talos/... # The protolint target performs linting on protobuf files. FROM base AS lint-protobuf WORKDIR /src/api COPY api . COPY --from=api-descriptors /api/lock.binpb ./current.lock.binpb RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf lint RUN --mount=type=cache,target=/.cache,id=talos/.cache go tool github.com/bufbuild/buf/cmd/buf breaking current.lock.binpb --against lock.binpb # The markdownlint target performs linting on Markdown files. FROM oven/bun:1-alpine AS lint-markdown ARG MARKDOWNLINTCLI_VERSION RUN apk add --no-cache findutils RUN bun i -g markdownlint-cli@${MARKDOWNLINTCLI_VERSION} WORKDIR /src COPY . . RUN bun run --bun markdownlint \ --ignore '**/LICENCE.md' \ --ignore '**/CHANGELOG.md' \ --ignore '**/CODE_OF_CONDUCT.md' \ --ignore '**/node_modules/**' \ --ignore '**/hack/chglog/**' \ --ignore 'website/content/*/reference/*' \ --ignore 'website/themes/**' \ --disable MD045 MD056 -- \ . # The docs target generates documentation. FROM base AS docs-build ARG TARGETOS ARG TARGETARCH WORKDIR /src COPY --from=talosctl-targetarch /talosctl-${TARGETOS}-${TARGETARCH} /bin/talosctl RUN env HOME=/home/user TAG=latest /bin/talosctl docs --config /tmp/configuration \ && env HOME=/home/user TAG=latest /bin/talosctl docs --cli /tmp COPY ./pkg/machinery/config/schemas/*.schema.json /tmp/schemas/ FROM scratch AS proto-docs-build COPY --from=generate-build-clean /api/docs/api.md /api.md FROM scratch AS docs COPY --from=docs-build /tmp/configuration/ /website/content/v1.13/reference/configuration/ COPY --from=docs-build /tmp/cli.md /website/content/v1.13/reference/ COPY --from=docs-build /tmp/schemas /website/content/v1.13/schemas/ COPY --from=proto-docs-build /api.md /website/content/v1.13/reference/ # The talosctl-cni-bundle builds the CNI bundle for talosctl. FROM scratch AS talosctl-cni-bundle ARG TARGETARCH COPY --from=pkgs-talosctl-cni-bundle /opt/cni/bin/ /talosctl-cni-bundle-${TARGETARCH}/ # The go-mod-outdated target lists all outdated modules. FROM base AS go-mod-outdated RUN --mount=type=cache,target=/.cache,id=talos/.cache go install github.com/psampaz/go-mod-outdated@latest \ && mv /root/go/bin/go-mod-outdated /usr/bin/go-mod-outdated COPY ./hack/cloud-image-uploader ./hack/cloud-image-uploader # fail always to get the output back RUN --mount=type=cache,target=/.cache,id=talos/.cache <>>> ${project}:" && \ (cd "${project}" && go list -u -m -json all | go-mod-outdated -update -direct) done exit 1 EOF ================================================ FILE: LICENSE ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: Makefile ================================================ REGISTRY ?= ghcr.io USERNAME ?= siderolabs SHA ?= $(shell git describe --match=none --always --abbrev=8 --dirty) TAG ?= $(shell git describe --tag --always --dirty --match v[0-9]\*) ABBREV_TAG ?= $(shell git describe --tag --always --match v[0-9]\* --abbrev=0 ) TAG_SUFFIX ?= TAG_SUFFIX_IN ?= $(TAG_SUFFIX) TAG_SUFFIX_OUT ?= $(TAG_SUFFIX) SOURCE_DATE_EPOCH ?= $(shell git log -1 --pretty=%ct) IMAGE_REGISTRY ?= $(REGISTRY) IMAGE_TAG_IN ?= $(TAG)$(TAG_SUFFIX_IN) IMAGE_TAG_OUT ?= $(TAG)$(TAG_SUFFIX_OUT) BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) REGISTRY_AND_USERNAME := $(IMAGE_REGISTRY)/$(USERNAME) NAME = Talos CLOUD_IMAGES_EXTRA_ARGS ?= "" ZSTD_COMPRESSION_LEVEL ?= 18 CI_RELEASE_TAG := $(shell git log --oneline --format=%B -n 1 HEAD^2 -- 2>/dev/null | head -n 1 | sed -r "/^release\(.*\)/ s/^release\((.*)\):.*$$/\\1/; t; Q") ARTIFACTS := _out DEBUG_TOOLS_SOURCE := scratch EMBED_TARGET ?= embed TOOLS_PREFIX ?= ghcr.io/siderolabs/tools TOOLS ?= v1.13.0-beta.0 PKGS_PREFIX ?= ghcr.io/siderolabs PKGS ?= v1.13.0-beta.0 GENERATE_VEX_PREFIX ?= ghcr.io/siderolabs/generate-vex GENERATE_VEX ?= latest KRES_IMAGE ?= ghcr.io/siderolabs/kres:latest CONFORMANCE_IMAGE ?= ghcr.io/siderolabs/conform:latest IMAGE_SIGNER_RELEASE ?= v0.2.0 PKG_APPARMOR ?= $(PKGS_PREFIX)/apparmor:$(PKGS) PKG_CA_CERTIFICATES ?= $(PKGS_PREFIX)/ca-certificates:$(PKGS) PKG_CNI ?= $(PKGS_PREFIX)/cni:$(PKGS) PKG_CONTAINERD ?= $(PKGS_PREFIX)/containerd:$(PKGS) PKG_CPIO ?= $(PKGS_PREFIX)/cpio:$(PKGS) PKG_CRYPTSETUP ?= $(PKGS_PREFIX)/cryptsetup:$(PKGS) PKG_DOSFSTOOLS ?= $(PKGS_PREFIX)/dosfstools:$(PKGS) PKG_E2FSPROGS ?= $(PKGS_PREFIX)/e2fsprogs:$(PKGS) PKG_FHS ?= $(PKGS_PREFIX)/fhs:$(PKGS) PKG_FLANNEL_CNI ?= $(PKGS_PREFIX)/flannel-cni:$(PKGS) PKG_GLIB ?= $(PKGS_PREFIX)/glib:$(PKGS) PKG_GRUB ?= $(PKGS_PREFIX)/grub:$(PKGS) PKG_IGZIP ?= $(PKGS_PREFIX)/igzip:$(PKGS) PKG_IPTABLES ?= $(PKGS_PREFIX)/iptables:$(PKGS) PKG_IPXE ?= $(PKGS_PREFIX)/ipxe:$(PKGS) PKG_KERNEL ?= $(PKGS_PREFIX)/kernel:$(PKGS) PKG_KMOD ?= $(PKGS_PREFIX)/kmod:$(PKGS) PKG_LIBAIO ?= $(PKGS_PREFIX)/libaio:$(PKGS) PKG_LIBARCHIVE ?= $(PKGS_PREFIX)/libarchive:$(PKGS) PKG_LIBATTR ?= $(PKGS_PREFIX)/libattr:$(PKGS) PKG_LIBBURN ?= $(PKGS_PREFIX)/libburn:$(PKGS) PKG_LIBCAP ?= $(PKGS_PREFIX)/libcap:$(PKGS) PKG_LIBINIH ?= $(PKGS_PREFIX)/libinih:$(PKGS) PKG_LIBISOBURN ?= $(PKGS_PREFIX)/libisoburn:$(PKGS) PKG_LIBISOFS ?= $(PKGS_PREFIX)/libisofs:$(PKGS) PKG_LIBJANSSON ?= $(PKGS_PREFIX)/libjansson:$(PKGS) PKG_LIBJSON_C ?= $(PKGS_PREFIX)/libjson-c:$(PKGS) PKG_LIBLZMA ?= $(PKGS_PREFIX)/liblzma:$(PKGS) PKG_LIBMNL ?= $(PKGS_PREFIX)/libmnl:$(PKGS) PKG_LIBNFTNL ?= $(PKGS_PREFIX)/libnftnl:$(PKGS) PKG_LIBPOPT ?= $(PKGS_PREFIX)/libpopt:$(PKGS) PKG_LIBSELINUX ?= $(PKGS_PREFIX)/libselinux:$(PKGS) PKG_LIBSEPOL ?= $(PKGS_PREFIX)/libsepol:$(PKGS) PKG_LIBURCU ?= $(PKGS_PREFIX)/liburcu:$(PKGS) PKG_LINUX_FIRMWARE ?= $(PKGS_PREFIX)/linux-firmware:$(PKGS) PKG_LVM2 ?= $(PKGS_PREFIX)/lvm2:$(PKGS) PKG_MTOOLS ?= $(PKGS_PREFIX)/mtools:$(PKGS) PKG_MUSL ?= $(PKGS_PREFIX)/musl:$(PKGS) PKG_NFTABLES ?= $(PKGS_PREFIX)/nftables:$(PKGS) PKG_OPENSSL ?= $(PKGS_PREFIX)/openssl:$(PKGS) PKG_OPEN_VMDK ?= $(PKGS_PREFIX)/open-vmdk:$(PKGS) PKG_PCRE2 ?= $(PKGS_PREFIX)/pcre2:$(PKGS) PKG_PIGZ ?= $(PKGS_PREFIX)/pigz:$(PKGS) PKG_QEMU_TOOLS ?= $(PKGS_PREFIX)/qemu-tools:$(PKGS) PKG_RUNC ?= $(PKGS_PREFIX)/runc:$(PKGS) PKG_SD_BOOT ?= $(PKGS_PREFIX)/sd-boot:$(PKGS) PKG_SQUASHFS_TOOLS ?= $(PKGS_PREFIX)/squashfs-tools:$(PKGS) PKG_SYSTEMD_UDEVD ?= $(PKGS_PREFIX)/systemd-udevd:$(PKGS) PKG_TALOSCTL_CNI_BUNDLE ?= $(PKGS_PREFIX)/talosctl-cni-bundle:$(PKGS) PKG_TAR ?= $(PKGS_PREFIX)/tar:$(PKGS) PKG_UTIL_LINUX ?= $(PKGS_PREFIX)/util-linux:$(PKGS) PKG_XFSPROGS ?= $(PKGS_PREFIX)/xfsprogs:$(PKGS) PKG_XZ ?= $(PKGS_PREFIX)/xz:$(PKGS) PKG_ZLIB ?= $(PKGS_PREFIX)/zlib:$(PKGS) PKG_ZSTD ?= $(PKGS_PREFIX)/zstd:$(PKGS) # renovate: datasource=github-tags depName=golang/go GO_VERSION ?= 1.26 # renovate: datasource=npm depName=markdownlint-cli MARKDOWNLINTCLI_VERSION ?= 0.48.0 OPERATING_SYSTEM := $(shell uname -s | tr "[:upper:]" "[:lower:]") ARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') TALOSCTL_DEFAULT_TARGET := talosctl-$(OPERATING_SYSTEM) TALOSCTL_EXECUTABLE := $(PWD)/$(ARTIFACTS)/$(TALOSCTL_DEFAULT_TARGET)-$(ARCH) INTEGRATION_TEST := integration-test INTEGRATION_TEST_DEFAULT_TARGET := $(INTEGRATION_TEST)-$(OPERATING_SYSTEM) INTEGRATION_TEST_PROVISION_DEFAULT_TARGET := integration-test-provision-$(OPERATING_SYSTEM) # renovate: datasource=github-releases depName=kubernetes/kubernetes KUBECTL_VERSION ?= v1.36.0-alpha.2 # renovate: datasource=github-releases depName=kastenhq/kubestr KUBESTR_VERSION ?= v0.4.49 # renovate: datasource=github-releases depName=helm/helm HELM_VERSION ?= v4.1.3 # renovate: datasource=github-releases depName=cilium/cilium-cli CILIUM_CLI_VERSION ?= v0.19.2 # renovate: datasource=github-releases depName=microsoft/secureboot_objects MICROSOFT_SECUREBOOT_RELEASE ?= v1.1.3 KUBECTL_URL ?= https://dl.k8s.io/release/$(KUBECTL_VERSION)/bin/$(OPERATING_SYSTEM)/amd64/kubectl KUBESTR_URL ?= https://github.com/kastenhq/kubestr/releases/download/$(KUBESTR_VERSION)/kubestr_$(subst v,,$(KUBESTR_VERSION))_Linux_amd64.tar.gz HELM_URL ?= https://get.helm.sh/helm-$(HELM_VERSION)-linux-amd64.tar.gz CILIUM_CLI_URL ?= https://github.com/cilium/cilium-cli/releases/download/$(CILIUM_CLI_VERSION)/cilium-$(OPERATING_SYSTEM)-amd64.tar.gz TESTPKGS ?= github.com/siderolabs/talos/... RELEASES ?= v1.11.6 v1.12.0 SHORT_INTEGRATION_TEST ?= CUSTOM_CNI_URL ?= INSTALLER_ARCH ?= all IMAGER_ARGS ?= CGO_ENABLED ?= 0 GO_BUILDFLAGS ?= GO_BUILDTAGS ?= tcell_minimal,grpcnotrace GO_BUILDTAGS_TALOSCTL ?= grpcnotrace GO_LDFLAGS ?= GO_MACHINED_LDFLAGS ?= -X golang.zx2c4.com/wireguard/ipc.socketDirectory=/system/wireguard-sock # see https://github.com/siderolabs/talos/issues/8514 GOAMD64 ?= v2 GOFIPS140 ?= off WITH_RACE ?= false WITH_DEBUG ?= false ifneq (, $(filter $(WITH_RACE), t true TRUE y yes 1)) CGO_ENABLED = 1 GO_BUILDFLAGS += -race GO_LDFLAGS += -linkmode=external -extldflags '-static' INSTALLER_ARCH = targetarch endif ifneq (, $(filter $(WITH_DEBUG), t true TRUE y yes 1)) GO_BUILDTAGS := $(GO_BUILDTAGS),sidero.debug GO_BUILDTAGS_TALOSCTL := $(GO_BUILDTAGS_TALOSCTL),sidero.debug else GO_LDFLAGS += -s -w endif ifneq (, $(filter $(WITH_DEBUG_SHELL), t true TRUE y yes 1)) # bash-minimal is a Dockerfile target that copies over the bash from siderolabs tools DEBUG_TOOLS_SOURCE := bash-minimal endif GO_BUILDFLAGS_TALOSCTL := $(GO_BUILDFLAGS) -tags "$(GO_BUILDTAGS_TALOSCTL)" GO_BUILDFLAGS += -tags "$(GO_BUILDTAGS)" , := , space := $(subst ,, ) BUILD := docker buildx build PLATFORM ?= linux/$(ARCH) PROGRESS ?= auto PUSH ?= false COMMON_ARGS := --file=Dockerfile COMMON_ARGS += --progress=$(PROGRESS) COMMON_ARGS += --platform=$(PLATFORM) COMMON_ARGS += --push=$(PUSH) COMMON_ARGS += --build-arg=ABBREV_TAG=$(ABBREV_TAG) COMMON_ARGS += --build-arg=ARTIFACTS=$(ARTIFACTS) COMMON_ARGS += --build-arg=CGO_ENABLED=$(CGO_ENABLED) COMMON_ARGS += --build-arg=DEBUG_TOOLS_SOURCE=$(DEBUG_TOOLS_SOURCE) COMMON_ARGS += --build-arg=EMBED_TARGET=$(EMBED_TARGET) COMMON_ARGS += --build-arg=GO_BUILDFLAGS_TALOSCTL="$(GO_BUILDFLAGS_TALOSCTL)" COMMON_ARGS += --build-arg=GO_BUILDFLAGS="$(GO_BUILDFLAGS)" COMMON_ARGS += --build-arg=GO_LDFLAGS="$(GO_LDFLAGS)" COMMON_ARGS += --build-arg=GO_MACHINED_LDFLAGS="$(GO_MACHINED_LDFLAGS)" COMMON_ARGS += --build-arg=GOAMD64="$(GOAMD64)" COMMON_ARGS += --build-arg=GOFIPS140="$(GOFIPS140)" COMMON_ARGS += --build-arg=http_proxy=$(http_proxy) COMMON_ARGS += --build-arg=https_proxy=$(https_proxy) COMMON_ARGS += --build-arg=INSTALLER_ARCH=$(INSTALLER_ARCH) COMMON_ARGS += --build-arg=MARKDOWNLINTCLI_VERSION=$(MARKDOWNLINTCLI_VERSION) COMMON_ARGS += --build-arg=MICROSOFT_SECUREBOOT_RELEASE=$(MICROSOFT_SECUREBOOT_RELEASE) COMMON_ARGS += --build-arg=NAME=$(NAME) COMMON_ARGS += --build-arg=PKG_APPARMOR=$(PKG_APPARMOR) COMMON_ARGS += --build-arg=PKG_CA_CERTIFICATES=$(PKG_CA_CERTIFICATES) COMMON_ARGS += --build-arg=PKG_CNI=$(PKG_CNI) COMMON_ARGS += --build-arg=PKG_CONTAINERD=$(PKG_CONTAINERD) COMMON_ARGS += --build-arg=PKG_CPIO=$(PKG_CPIO) COMMON_ARGS += --build-arg=PKG_CRYPTSETUP=$(PKG_CRYPTSETUP) COMMON_ARGS += --build-arg=PKG_DOSFSTOOLS=$(PKG_DOSFSTOOLS) COMMON_ARGS += --build-arg=PKG_E2FSPROGS=$(PKG_E2FSPROGS) COMMON_ARGS += --build-arg=PKG_FHS=$(PKG_FHS) COMMON_ARGS += --build-arg=PKG_FLANNEL_CNI=$(PKG_FLANNEL_CNI) COMMON_ARGS += --build-arg=PKG_GLIB=$(PKG_GLIB) COMMON_ARGS += --build-arg=PKG_GRUB=$(PKG_GRUB) COMMON_ARGS += --build-arg=PKG_IGZIP=$(PKG_IGZIP) COMMON_ARGS += --build-arg=PKG_IPTABLES=$(PKG_IPTABLES) COMMON_ARGS += --build-arg=PKG_IPXE=$(PKG_IPXE) COMMON_ARGS += --build-arg=PKG_KERNEL=$(PKG_KERNEL) COMMON_ARGS += --build-arg=PKG_KMOD=$(PKG_KMOD) COMMON_ARGS += --build-arg=PKG_LIBAIO=$(PKG_LIBAIO) COMMON_ARGS += --build-arg=PKG_LIBARCHIVE=$(PKG_LIBARCHIVE) COMMON_ARGS += --build-arg=PKG_LIBATTR=$(PKG_LIBATTR) COMMON_ARGS += --build-arg=PKG_LIBBURN=$(PKG_LIBBURN) COMMON_ARGS += --build-arg=PKG_LIBCAP=$(PKG_LIBCAP) COMMON_ARGS += --build-arg=PKG_LIBINIH=$(PKG_LIBINIH) COMMON_ARGS += --build-arg=PKG_LIBISOBURN=$(PKG_LIBISOBURN) COMMON_ARGS += --build-arg=PKG_LIBISOFS=$(PKG_LIBISOFS) COMMON_ARGS += --build-arg=PKG_LIBJANSSON=$(PKG_LIBJANSSON) COMMON_ARGS += --build-arg=PKG_LIBJSON_C=$(PKG_LIBJSON_C) COMMON_ARGS += --build-arg=PKG_LIBLZMA=$(PKG_LIBLZMA) COMMON_ARGS += --build-arg=PKG_LIBMNL=$(PKG_LIBMNL) COMMON_ARGS += --build-arg=PKG_LIBNFTNL=$(PKG_LIBNFTNL) COMMON_ARGS += --build-arg=PKG_LIBPOPT=$(PKG_LIBPOPT) COMMON_ARGS += --build-arg=PKG_LIBSELINUX=$(PKG_LIBSELINUX) COMMON_ARGS += --build-arg=PKG_LIBSEPOL=$(PKG_LIBSEPOL) COMMON_ARGS += --build-arg=PKG_LIBURCU=$(PKG_LIBURCU) COMMON_ARGS += --build-arg=PKG_LINUX_FIRMWARE=$(PKG_LINUX_FIRMWARE) COMMON_ARGS += --build-arg=PKG_LVM2=$(PKG_LVM2) COMMON_ARGS += --build-arg=PKG_MTOOLS=$(PKG_MTOOLS) COMMON_ARGS += --build-arg=PKG_NFTABLES=$(PKG_NFTABLES) COMMON_ARGS += --build-arg=PKG_MUSL=$(PKG_MUSL) COMMON_ARGS += --build-arg=PKG_OPENSSL=$(PKG_OPENSSL) COMMON_ARGS += --build-arg=PKG_OPEN_VMDK=$(PKG_OPEN_VMDK) COMMON_ARGS += --build-arg=PKG_PCRE2=$(PKG_PCRE2) COMMON_ARGS += --build-arg=PKG_PIGZ=$(PKG_PIGZ) COMMON_ARGS += --build-arg=PKG_QEMU_TOOLS=$(PKG_QEMU_TOOLS) COMMON_ARGS += --build-arg=PKG_RASPBERYPI_FIRMWARE=$(PKG_RASPBERYPI_FIRMWARE) COMMON_ARGS += --build-arg=PKG_RUNC=$(PKG_RUNC) COMMON_ARGS += --build-arg=PKG_SD_BOOT=$(PKG_SD_BOOT) COMMON_ARGS += --build-arg=PKG_SQUASHFS_TOOLS=$(PKG_SQUASHFS_TOOLS) COMMON_ARGS += --build-arg=PKG_SYSTEMD_UDEVD=$(PKG_SYSTEMD_UDEVD) COMMON_ARGS += --build-arg=PKG_TALOSCTL_CNI_BUNDLE=$(PKG_TALOSCTL_CNI_BUNDLE) COMMON_ARGS += --build-arg=PKG_TAR=$(PKG_TAR) COMMON_ARGS += --build-arg=PKG_U_BOOT=$(PKG_U_BOOT) COMMON_ARGS += --build-arg=PKG_UTIL_LINUX=$(PKG_UTIL_LINUX) COMMON_ARGS += --build-arg=PKG_XFSPROGS=$(PKG_XFSPROGS) COMMON_ARGS += --build-arg=PKG_XZ=$(PKG_XZ) COMMON_ARGS += --build-arg=PKG_ZLIB=$(PKG_ZLIB) COMMON_ARGS += --build-arg=PKG_ZSTD=$(PKG_ZSTD) COMMON_ARGS += --build-arg=PKGS_PREFIX=$(PKGS_PREFIX) COMMON_ARGS += --build-arg=PKGS=$(PKGS) COMMON_ARGS += --build-arg=REGISTRY=$(REGISTRY) COMMON_ARGS += --build-arg=SHA=$(SHA) COMMON_ARGS += --build-arg=SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) COMMON_ARGS += --build-arg=TAG=$(TAG) COMMON_ARGS += --build-arg=TESTPKGS=$(TESTPKGS) COMMON_ARGS += --build-arg=TOOLS_PREFIX=$(TOOLS_PREFIX) COMMON_ARGS += --build-arg=TOOLS=$(TOOLS) COMMON_ARGS += --build-arg=GENERATE_VEX_PREFIX=$(GENERATE_VEX_PREFIX) COMMON_ARGS += --build-arg=GENERATE_VEX=$(GENERATE_VEX) COMMON_ARGS += --build-arg=USERNAME=$(USERNAME) COMMON_ARGS += --build-arg=ZSTD_COMPRESSION_LEVEL=$(ZSTD_COMPRESSION_LEVEL) CI_ARGS ?= EXTENSIONS_FILTER_COMMAND ?= grep -vE 'tailscale|xen-guest-agent|nvidia|vmtoolsd-guest-agent|metal-agent|cloudflared|zerotier|nebula|newt|netbird|multipath-tools|trident-iscsi-tools' all: initramfs kernel installer imager talosctl talosctl-image talos # Help Menu define HELP_MENU_HEADER # Getting Started To build this project, you must have the following installed: - git - make - docker (19.03 or higher) - buildx (https://github.com/docker/buildx) - crane (https://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md) ## Creating a Builder Instance The build process makes use of features not currently supported by the default builder instance (docker driver). To create a compatible builder instance, run: ``` docker buildx create --driver docker-container --name local --buildkitd-flags '--allow-insecure-entitlement security.insecure' --use ``` If you already have a compatible builder instance, you may use that instead. > Note: The security.insecure entitlement is only required, and used by the unit-tests target. ## Artifacts All artifacts will be output to ./$(ARTIFACTS). Images will be tagged with the registry "$(IMAGE_REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(REGISTRY_AND_USERNAME)/image:$(IMAGE_TAG)). The registry and username can be overridden by exporting REGISTRY, and USERNAME respectively. ## Race Detector Building with `WITH_RACE=1` enables race detector in the Talos executables. Integration tests are always built with the race detector enabled. endef export HELP_MENU_HEADER help: ## This help menu. @echo "$$HELP_MENU_HEADER" @grep -E '^[a-zA-Z0-9%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' # Build Abstractions $(ARTIFACTS): @mkdir -p $(ARTIFACTS) .PHONY: base target-%: ## Builds the specified target defined in the Dockerfile. The build result will only remain in the build cache. @$(BUILD) \ --target=$* \ $(COMMON_ARGS) \ $(TARGET_ARGS) \ $(CI_ARGS) . local-%: ## Builds the specified target defined in the Dockerfile using the local output type. The build result will be output to the specified local destination. @$(MAKE) target-$* TARGET_ARGS="--output=type=local,dest=$(DEST) $(TARGET_ARGS)" @PLATFORM=$(PLATFORM) \ ARTIFACTS=$(ARTIFACTS) \ ./hack/fix-artifacts.sh docker-%: ## Builds the specified target defined in the Dockerfile using the docker output type. The build result will be output to the specified local destination. @mkdir -p $(DEST) @$(MAKE) target-$* TARGET_ARGS="--output type=docker,dest=$(DEST)/$*.tar,name=$(REGISTRY_AND_USERNAME)/$*:$(IMAGE_TAG_OUT) $(TARGET_ARGS)" registry-%: ## Builds the specified target defined in the Dockerfile using the image/registry output type. The build result will be pushed to the registry if PUSH=true. @$(MAKE) target-$* TARGET_ARGS="--output type=image,name=$(REGISTRY_AND_USERNAME)/$*:$(IMAGE_TAG_OUT),rewrite-timestamp=true $(TARGET_ARGS)" hack-test-%: ## Runs the specified script in ./hack/test with well known environment variables. @./hack/test/$*.sh # Generators .PHONY: generate generate: ## Generates code from protobuf service definitions and machinery config. @$(MAKE) local-$@ DEST=./ PLATFORM=linux/$(ARCH) EMBED_TARGET=embed-abbrev .PHONY: docs docs: ## Generates the documentation for machine config, and talosctl. @$(MAKE) local-$@ DEST=./ PLATFORM=linux/amd64 # Local Artifacts .PHONY: kernel kernel: ## Outputs the kernel package contents (vmlinuz) to the artifact directory. @$(MAKE) local-$@ DEST=$(ARTIFACTS) PUSH=false @-rm -rf $(ARTIFACTS)/modules .PHONY: initramfs initramfs: ## Builds the compressed initramfs and outputs it to the artifact directory. @$(MAKE) local-$@ DEST=$(ARTIFACTS) PUSH=false .PHONY: sd-boot sd-boot: ## Outputs the systemd-boot to the artifact directory. @$(MAKE) local-$@ DEST=$(ARTIFACTS) PUSH=false .PHONY: sd-stub sd-stub: ## Outputs the systemd-stub to the artifact directory. @$(MAKE) local-$@ DEST=$(ARTIFACTS) PUSH=false .PHONY: installer-base installer-base: ## Builds the container image for the installer-base and outputs it to the registry. @$(MAKE) registry-$@ .PHONY: imager imager: ## Builds the container image for the imager and outputs it to the registry. @$(MAKE) registry-$@ .PHONY: talos talos: ## Builds the Talos container image and outputs it to the registry. @$(MAKE) registry-$@ .PHONY: talosctl-image talosctl-image: ## Builds the talosctl container image and outputs it to the registry. @$(MAKE) registry-talosctl talosctl-all-image: @$(MAKE) registry-talosctl-all talosctl-all: @$(MAKE) local-talosctl-all DEST=$(ARTIFACTS) PUSH=false talosctl-linux-amd64: @$(MAKE) local-talosctl-linux-amd64 DEST=$(ARTIFACTS) PUSH=false talosctl-linux-arm64: @$(MAKE) local-talosctl-linux-arm64 DEST=$(ARTIFACTS) PUSH=false talosctl-linux-armv7: @$(MAKE) local-talosctl-linux-armv7 DEST=$(ARTIFACTS) PUSH=false talosctl-linux-riscv64: @$(MAKE) local-talosctl-linux-riscv64 DEST=$(ARTIFACTS) PUSH=false talosctl-darwin-amd64: @$(MAKE) local-talosctl-darwin-amd64 DEST=$(ARTIFACTS) PUSH=false talosctl-darwin-arm64: @$(MAKE) local-talosctl-darwin-arm64 DEST=$(ARTIFACTS) PUSH=false talosctl-freebsd-amd64: @$(MAKE) local-talosctl-freebsd-amd64 DEST=$(ARTIFACTS) PUSH=false talosctl-freebsd-arm64: @$(MAKE) local-talosctl-freebsd-arm64 DEST=$(ARTIFACTS) PUSH=false talosctl-windows-amd64: @$(MAKE) local-talosctl-windows-amd64 DEST=$(ARTIFACTS) PUSH=false talosctl-windows-arm64: @$(MAKE) local-talosctl-windows-arm64 DEST=$(ARTIFACTS) PUSH=false talosctl: talosctl-$(OPERATING_SYSTEM)-$(ARCH) sbom: @$(MAKE) local-sbom DEST=$(ARTIFACTS) image-%: ## Builds the specified image. Valid options are aws, azure, digital-ocean, gcp, and vmware etc (e.g. image-aws) @docker pull $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG_IN) @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=$$(basename "$${platform}") && \ docker run --rm -t \ --network=host \ --user $(shell id -u):$(shell id -g) \ -v $(PWD)/$(ARTIFACTS):/secureboot:ro \ -v $(PWD)/$(ARTIFACTS):/out \ -e GITHUB_TOKEN \ -e SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) \ -e DETERMINISTIC_SEED=1 \ $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG_IN) $* \ --arch $$arch \ --base-installer-image $(REGISTRY_AND_USERNAME)/installer-base:$(IMAGE_TAG_IN) \ $(IMAGER_ARGS) || exit 1 ; \ done .PHONY: images-essential images-essential: image-metal image-metal-uki installer secureboot-installer ## Builds only essential images used in the CI. # Defines all known images (AWS, Azure, DigitalOcean, Exoscale, Cloudstack, GCP, HCloud, Metal, NoCloud, OpenNebula, OpenStack, Oracle, Scaleway, UpCloud, Vultr and VMware). IMAGES := image-akamai image-aws image-azure image-digital-ocean image-exoscale image-cloudstack image-gcp image-hcloud image-iso image-metal image-metal-uki image-nocloud image-opennebula image-openstack image-oracle image-scaleway image-upcloud image-vmware image-vultr .PHONY: images images: $(IMAGES) .PHONY: iso iso: image-iso ## Builds the ISO and outputs it to the artifact directory. .PHONY: secureboot-iso secureboot-iso: image-secureboot-iso ## Builds UEFI only ISO which uses UKI and outputs it to the artifact directory. IMAGES_LIST := .PHONY: installer installer: ## Builds the installer and outputs it to the artifact directory. @$(MAKE) image-installer IMAGER_ARGS="--base-installer-image $(REGISTRY_AND_USERNAME)/installer-base:$(IMAGE_TAG_IN) $(IMAGER_ARGS)" @crane_args="" @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=$$(basename "$${platform}") && \ image=$$(crane push $(ARTIFACTS)/installer-$${arch}.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG_OUT)-$${arch}) && \ crane_args="$${crane_args} -m $${image}" && \ rm -f $(ARTIFACTS)/installer-$${arch}.tar ; \ done; \ crane index append -t "${REGISTRY_AND_USERNAME}/installer:${IMAGE_TAG_OUT}" $${crane_args} @echo "${REGISTRY_AND_USERNAME}/installer:${IMAGE_TAG_OUT}" > $(ARTIFACTS)/installer_image .PHONY: secureboot-installer secureboot-installer: ## Builds UEFI only installer which uses UKI and push it to the registry. @$(MAKE) image-secureboot-installer IMAGER_ARGS="--base-installer-image $(REGISTRY_AND_USERNAME)/installer-base:$(IMAGE_TAG_IN) $(IMAGER_ARGS)" @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=$$(basename "$${platform}") && \ crane push $(ARTIFACTS)/installer-$${arch}-secureboot.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG_OUT)-$${arch}-secureboot && \ rm -f $(ARTIFACTS)/installer-$${arch}-secureboot.tar ; \ done .PHONY: talosctl-cni-bundle talosctl-cni-bundle: ## Creates a compressed tarball that includes CNI bundle for talosctl. @$(MAKE) local-$@ DEST=$(ARTIFACTS) @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=`basename "$${platform}"` ; \ tar -C $(ARTIFACTS)/talosctl-cni-bundle-$${arch} -czf $(ARTIFACTS)/talosctl-cni-bundle-$${arch}.tar.gz . ; \ done @rm -rf $(ARTIFACTS)/talosctl-cni-bundle-*/ .PHONY: cloud-images cloud-images: ## Uploads cloud images (AMIs, etc.) to the cloud registry. @docker run --rm -v $(PWD):/src -w /src \ -e TAG=$(TAG) -e ARTIFACTS=$(ARTIFACTS) -e ABBREV_TAG=$(ABBREV_TAG) \ -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY \ -e GOOGLE_PROJECT_ID -e GOOGLE_CREDENTIALS \ golang:$(GO_VERSION) \ ./hack/cloud-image-uploader.sh $(CLOUD_IMAGES_EXTRA_ARGS) .PHONY: uki-certs uki-certs: talosctl ## Generate test certificates for SecureBoot/PCR Signing @$(TALOSCTL_EXECUTABLE) gen secureboot uki @$(TALOSCTL_EXECUTABLE) gen secureboot pcr @$(TALOSCTL_EXECUTABLE) gen secureboot database .PHONY: integration-images-list integration-images-list: ## Generate list of integration images. @docker run --entrypoint /usr/local/bin/e2e.test registry.k8s.io/conformance:$(KUBECTL_VERSION) --list-images | \ $(TALOSCTL_EXECUTABLE) images integration --installer-tag=$(IMAGE_TAG_IN) --registry-and-user=$(REGISTRY_AND_USERNAME) \ > $(ARTIFACTS)/integration-images.txt IGNORE_CACHE_IMAGES ?= registry.k8s.io/e2e-test-images/node-perf/pytorch-wide-deep # Convert space-separated list into grep-compatible regex (img1|img2|img3) IGNORE_CACHE_IMAGES_RE := $(subst $(space),|,$(strip $(IGNORE_CACHE_IMAGES))) # space helper space := $(empty) $(empty) empty := .PHONY: cache-create cache-create: installer imager integration-images-list ## Generate image cache. @grep -vE "$(IGNORE_CACHE_IMAGES_RE)" $(ARTIFACTS)/integration-images.txt | \ $(TALOSCTL_EXECUTABLE) images cache-create --image-cache-path=/tmp/cache.tar --images=- --force @crane push /tmp/cache.tar $(REGISTRY_AND_USERNAME)/image-cache:$(IMAGE_TAG_OUT) @$(MAKE) image-iso IMAGER_ARGS="--image-cache=$(REGISTRY_AND_USERNAME)/image-cache:$(IMAGE_TAG_OUT) --extra-kernel-arg='console=ttyS0'" # Code Quality api-descriptors: ## Generates API descriptors used to detect breaking API changes. @$(MAKE) local-api-descriptors DEST=./ PLATFORM=linux/$(ARCH) fmt-protobuf: ## Formats protobuf files. @$(MAKE) local-fmt-protobuf DEST=./ PLATFORM=linux/$(ARCH) fmt: ## Formats the source code and protobuf files. @$(MAKE) fmt-protobuf lint-%: ## Runs the specified linter. Valid options are go, protobuf, and markdown (e.g. lint-go). @$(MAKE) target-lint-$* PLATFORM=linux/$(ARCH) lint: ## Runs linters on go, vulncheck, deadcode, protobuf, and markdown file types. @$(MAKE) lint-go lint-vulncheck lint-deadcode lint-protobuf lint-markdown check-dirty: ## Verifies that source tree is not dirty @if test -n "`git status --porcelain`"; then echo "Source tree is dirty"; git status; git diff; exit 1 ; fi go-mod-outdated: ## Runs the go-mod-oudated to show outdated dependencies. @$(MAKE) target-go-mod-outdated PLATFORM=linux/$(ARCH) # Tests .PHONY: unit-tests unit-tests: ## Performs unit tests. @$(MAKE) local-$@ DEST=$(ARTIFACTS) TARGET_ARGS="--allow security.insecure" PLATFORM=linux/$(ARCH) .PHONY: unit-tests-race unit-tests-race: ## Performs unit tests with race detection enabled. @$(MAKE) target-$@ TARGET_ARGS="--allow security.insecure" PLATFORM=linux/$(ARCH) .PHONY: unit-tests-fips unit-tests-fips: ## Performs unit tests with FIPS strict mode. @$(MAKE) target-$@ TARGET_ARGS="--allow security.insecure" PLATFORM=linux/$(ARCH) $(ARTIFACTS)/$(INTEGRATION_TEST_DEFAULT_TARGET)-amd64: @$(MAKE) local-$(INTEGRATION_TEST_DEFAULT_TARGET)-amd64 DEST=$(ARTIFACTS) PLATFORM=linux/amd64 WITH_RACE=true PUSH=false $(ARTIFACTS)/$(INTEGRATION_TEST_DEFAULT_TARGET)-arm64: @$(MAKE) local-$(INTEGRATION_TEST_DEFAULT_TARGET)-arm64 DEST=$(ARTIFACTS) PLATFORM=linux/arm64 WITH_RACE=true PUSH=false $(ARTIFACTS)/$(INTEGRATION_TEST): @$(MAKE) local-$(INTEGRATION_TEST)-targetarch DEST=$(ARTIFACTS) $(ARTIFACTS)/$(INTEGRATION_TEST_PROVISION_DEFAULT_TARGET)-amd64: @$(MAKE) local-$(INTEGRATION_TEST_PROVISION_DEFAULT_TARGET) DEST=$(ARTIFACTS) PLATFORM=linux/amd64 WITH_RACE=true $(ARTIFACTS)/kubectl: $(ARTIFACTS) @curl -L -o $(ARTIFACTS)/kubectl "$(KUBECTL_URL)" @chmod +x $(ARTIFACTS)/kubectl $(ARTIFACTS)/kubestr: $(ARTIFACTS) @curl -L "$(KUBESTR_URL)" | tar xzf - -C $(ARTIFACTS) kubestr @chmod +x $(ARTIFACTS)/kubestr $(ARTIFACTS)/helm: $(ARTIFACTS) @curl -L "$(HELM_URL)" | tar xzf - -C $(ARTIFACTS) --strip-components=1 linux-amd64/helm @chmod +x $(ARTIFACTS)/helm $(ARTIFACTS)/cilium: $(ARTIFACTS) @curl -L "$(CILIUM_CLI_URL)" | tar xzf - -C $(ARTIFACTS) cilium @chmod +x $(ARTIFACTS)/cilium external-artifacts: $(ARTIFACTS)/kubectl $(ARTIFACTS)/kubestr $(ARTIFACTS)/helm $(ARTIFACTS)/cilium e2e-%: $(ARTIFACTS)/$(INTEGRATION_TEST_DEFAULT_TARGET)-amd64 external-artifacts ## Runs the E2E test for the specified platform (e.g. e2e-docker). @$(MAKE) hack-test-$@ \ PLATFORM=$* \ TAG=$(TAG) \ SHA=$(SHA) \ REGISTRY=$(IMAGE_REGISTRY) \ IMAGE=$(REGISTRY_AND_USERNAME)/talos:$(IMAGE_TAG_IN) \ INSTALLER_IMAGE=$(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG_IN) \ ARTIFACTS=$(ARTIFACTS) \ TALOSCTL=$(PWD)/$(ARTIFACTS)/$(TALOSCTL_DEFAULT_TARGET)-amd64 \ INTEGRATION_TEST=$(PWD)/$(ARTIFACTS)/$(INTEGRATION_TEST_DEFAULT_TARGET)-amd64 \ SHORT_INTEGRATION_TEST=$(SHORT_INTEGRATION_TEST) \ CUSTOM_CNI_URL=$(CUSTOM_CNI_URL) \ KUBECTL=$(PWD)/$(ARTIFACTS)/kubectl \ KUBESTR=$(PWD)/$(ARTIFACTS)/kubestr \ HELM=$(PWD)/$(ARTIFACTS)/helm \ CILIUM_CLI=$(PWD)/$(ARTIFACTS)/cilium provision-tests-prepare: release-artifacts $(ARTIFACTS)/$(INTEGRATION_TEST_PROVISION_DEFAULT_TARGET)-amd64 provision-tests: provision-tests-prepare @$(MAKE) hack-test-$@ \ TAG=$(TAG) \ TALOSCTL=$(PWD)/$(ARTIFACTS)/$(TALOSCTL_DEFAULT_TARGET)-amd64 \ INTEGRATION_TEST=$(PWD)/$(ARTIFACTS)/$(INTEGRATION_TEST_PROVISION_DEFAULT_TARGET)-amd64 provision-tests-track-%: @$(MAKE) hack-test-provision-tests \ TAG=$(TAG) \ TALOSCTL=$(PWD)/$(ARTIFACTS)/$(TALOSCTL_DEFAULT_TARGET)-amd64 \ INTEGRATION_TEST=$(PWD)/$(ARTIFACTS)/$(INTEGRATION_TEST_PROVISION_DEFAULT_TARGET)-amd64 \ INTEGRATION_TEST_RUN="TestIntegration/.+-TR$*" \ INTEGRATION_TEST_TRACK="$*" \ CUSTOM_CNI_URL=$(CUSTOM_CNI_URL) \ REGISTRY=$(IMAGE_REGISTRY) \ ARTIFACTS=$(ARTIFACTS) installer-with-extensions: $(ARTIFACTS)/extensions/_out/extensions-metadata $(MAKE) image-installer \ IMAGER_ARGS="--base-installer-image=$(REGISTRY_AND_USERNAME)/installer-base:$(IMAGE_TAG_IN) $(shell cat $(ARTIFACTS)/extensions/_out/extensions-metadata | $(EXTENSIONS_FILTER_COMMAND) | xargs -n 1 echo --system-extension-image)" crane push $(ARTIFACTS)/installer-amd64.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG_OUT)-amd64-extensions INSTALLER_IMAGE_EXTENSIONS="$(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG_OUT)-amd64-extensions" yq eval -n '.machine.install.image = strenv(INSTALLER_IMAGE_EXTENSIONS)' > $(ARTIFACTS)/installer-extensions-patch.yaml kubelet-fat-patch: K8S_VERSION=$(KUBECTL_VERSION) yq eval -n '.machine.kubelet.image = "ghcr.io/siderolabs/kubelet:" + strenv(K8S_VERSION) + "-fat"' > $(ARTIFACTS)/kubelet-fat-patch.yaml # Assets for releases .PHONY: $(ARTIFACTS)/$(TALOS_RELEASE) $(ARTIFACTS)/$(TALOS_RELEASE): $(ARTIFACTS)/$(TALOS_RELEASE)/vmlinuz $(ARTIFACTS)/$(TALOS_RELEASE)/initramfs.xz # download release artifacts for specific version $(ARTIFACTS)/$(TALOS_RELEASE)/%: @mkdir -p $(ARTIFACTS)/$(TALOS_RELEASE)/ @case "$*" in \ vmlinuz) \ curl -L -o "$(ARTIFACTS)/$(TALOS_RELEASE)/$*" "https://github.com/siderolabs/talos/releases/download/$(TALOS_RELEASE)/vmlinuz-amd64" \ ;; \ initramfs.xz) \ curl -L -o "$(ARTIFACTS)/$(TALOS_RELEASE)/$*" "https://github.com/siderolabs/talos/releases/download/$(TALOS_RELEASE)/initramfs-amd64.xz" \ ;; \ esac .PHONY: release-artifacts release-artifacts: @for release in $(RELEASES); do \ $(MAKE) $(ARTIFACTS)/$$release TALOS_RELEASE=$$release; \ done # Utilities .PHONY: rekres rekres: @docker pull $(KRES_IMAGE) @docker run --rm --net=host --user $(shell id -u):$(shell id -g) -v $(PWD):/src -w /src -e GITHUB_TOKEN $(KRES_IMAGE) .PHONY: conformance conformance: @docker pull $(CONFORMANCE_IMAGE) @docker run --rm -it -v $(PWD):/src -w /src $(CONFORMANCE_IMAGE) enforce .PHONY: release-notes release-notes: ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG) push: ## Pushes the installer, imager, talos and talosctl images to the configured container registry with the generated tag. @$(MAKE) installer-base PUSH=true @$(MAKE) imager PUSH=true @$(MAKE) installer PUSH=true @$(MAKE) talos PUSH=true @$(MAKE) talosctl-image PUSH=true @$(MAKE) talosctl-all-image PUSH=true push-%: ## Pushes the installer, imager, talos and talosctl images to the configured container registry with the specified tag (e.g. push-latest). @$(MAKE) push IMAGE_TAG_OUT=$* .PHONY: clean clean: ## Cleans up all artifacts. @-rm -rf $(ARTIFACTS) .PHONY: image-list image-list: ## Prints a list of all images built by this Makefile with digests. @echo -n installer installer-base talos imager talosctl talosctl-all | xargs -d ' ' -I{} sh -c 'echo $(REGISTRY_AND_USERNAME)/{}:$(IMAGE_TAG_IN)' | xargs -I{} sh -c 'echo {}@$$(crane digest {})' $(ARTIFACTS)/image-signer: $(ARTIFACTS) ## Downloads image-signer binary @curl -sSL https://github.com/siderolabs/go-tools/releases/download/$(IMAGE_SIGNER_RELEASE)/image-signer-$(OPERATING_SYSTEM)-$(ARCH) -o $(ARTIFACTS)/image-signer @chmod +x $(ARTIFACTS)/image-signer .PHONY: sign-images sign-images: $(ARTIFACTS)/image-signer ## Run cosign to sign all images built by this Makefile. @$(ARTIFACTS)/image-signer sign $(shell $(MAKE) --quiet image-list REGISTRY_AND_USERNAME=$(REGISTRY_AND_USERNAME) IMAGE_TAG_IN=$(IMAGE_TAG_IN)) .PHONY: reproducibility-test reproducibility-test: $(ARTIFACTS) @$(MAKE) reproducibility-test-local-initramfs @$(MAKE) reproducibility-test-docker-installer-base INSTALLER_ARCH=targetarch PLATFORM=linux/$(ARCH) @$(MAKE) reproducibility-test-docker-talos reproducibility-test-docker-installer-base reproducibility-test-docker-imager reproducibility-test-docker-talosctl PLATFORM=linux/$(ARCH) @$(MAKE) reproducibility-test-iso reproducibility-test-docker-%: $(ARTIFACTS) @rm -rf _out1/ _out2/ @mkdir -p _out1/ _out2/ @$(MAKE) docker-$* DEST=_out1/ @$(MAKE) docker-$* DEST=_out2/ TARGET_ARGS="--no-cache" @find _out1/ -type f | xargs -IFILE diffoscope FILE `echo FILE | sed 's/_out1/_out2/'` @rm -rf _out1/ _out2/ reproducibility-test-local-%: $(ARTIFACTS) @rm -rf _out1/ _out2/ @mkdir -p _out1/ _out2/ @$(MAKE) local-$* DEST=_out1/ @$(MAKE) local-$* DEST=_out2/ TARGET_ARGS="--no-cache" @find _out1/ -type f | xargs -IFILE diffoscope FILE `echo FILE | sed 's/_out1/_out2/'` @rm -rf _out1/ _out2/ reproducibility-test-iso: $(ARTIFACTS) @$(MAKE) iso mv $(ARTIFACTS)/metal-amd64.iso $(ARTIFACTS)/metal-amd64.iso.orig @$(MAKE) iso @diffoscope $(ARTIFACTS)/metal-amd64.iso.orig $(ARTIFACTS)/metal-amd64.iso @rm -rf $(ARTIFACTS)/metal-amd64.iso.orig reproducibility-test-images: $(ARTIFACTS) @rm -rf $(ARTIFACTS)/images-orig $(ARTIFACTS)/images-new @$(MAKE) $(filter-out image-azure image-vmware,$(IMAGES)) ARTIFACTS=$(ARTIFACTS)/images-orig @$(MAKE) $(filter-out image-azure image-vmware,$(IMAGES)) ARTIFACTS=$(ARTIFACTS)/images-new @touch -ch -t $$(date -d @$(SOURCE_DATE_EPOCH) +%Y%m%d0000) $(ARTIFACTS)/images-orig $(ARTIFACTS)/images-new @find $(ARTIFACTS)/images-orig $(ARTIFACTS)/images-new -type f -exec touch -ch -t $$(date -d @$(SOURCE_DATE_EPOCH) +%Y%m%d0000) {} + @diffoscope $(ARTIFACTS)/images-orig $(ARTIFACTS)/images-new @rm -rf $(ARTIFACTS)/images-orig $(ARTIFACTS)/images-new .PHONY: ci-temp-release-tag ci-temp-release-tag: ## Generates a temporary release tag for CI run. @if [ -n "$(CI_RELEASE_TAG)" -a -n "$${GITHUB_ENV}" ]; then \ echo Setting temporary release tag "$(CI_RELEASE_TAG)"; \ echo "TAG=$(CI_RELEASE_TAG)" >> "$${GITHUB_ENV}"; \ echo "ABBREV_TAG=$(CI_RELEASE_TAG)" >> "$${GITHUB_ENV}"; \ fi ================================================ FILE: README.md ================================================

Talos Linux

A modern OS for Kubernetes.

Release Pre-release OpenSSF badge

--- **Talos** is a modern OS for running Kubernetes: secure, immutable, and minimal. Talos is fully open source, production-ready, and supported by the people at [Sidero Labs](https://www.SideroLabs.com/). All system management is done via an API - there is no shell or interactive console. Benefits include: - **Security**: Talos reduces your attack surface: It's minimal, hardened, and immutable. All API access is secured with mutual TLS (mTLS) authentication. - **Predictability**: Talos eliminates configuration drift, reduces unknown factors by employing immutable infrastructure ideology, and delivers atomic updates. - **Evolvability**: Talos simplifies your architecture, increases your agility, and always delivers current stable Kubernetes and Linux versions. ## Documentation For instructions on deploying and managing Talos, see the [Documentation](https://docs.siderolabs.com/talos). ## Community - Support: Questions, bugs, feature requests [GitHub Discussions](https://github.com/siderolabs/talos/discussions) - Slack: Join our [slack channel](https://taloscommunity.slack.com/). Request access via [inviter.co](https://inviter.co/sidero-labs-community). - Forum: [community](https://groups.google.com/a/SideroLabs.com/forum/#!forum/community) - Twitter: [@SideroLabs](https://twitter.com/SideroLabs) - Email: [info@SideroLabs.com](mailto:info@SideroLabs.com) If you're interested in this project and would like to help in engineering efforts or have general usage questions, we are happy to have you! We hold a monthly meeting that all audiences are welcome to attend. We would appreciate your feedback so that we can make Talos even better! To do so, you can take our [survey](https://docs.google.com/forms/d/1TUna5YTYGCKot68Y9YN_CLobY6z9JzLVCq1G7DoyNjA/edit). ### Office Hours - When: Second Monday of every month at 16:30 UTC. - Where: [Google Meet](https://meet.google.com/ivb-kjfm-jfc). You can subscribe to this meeting by joining the community forum above. > Note: You can convert the meeting hours to your [local time](https://everytimezone.com/s/599e61d6). ## Contributing Contributions are welcomed and appreciated! See [Contributing](CONTRIBUTING.md) for our guidelines. ## License GitHub Some software we distribute is under the General Public License family of licenses or other licenses that require we provide you with the source code. If you would like a copy of the source code for this software, please contact us via email: info at SideroLabs.com. ================================================ FILE: SECURITY.md ================================================ # Reporting Security Issues The Sidero Labs team and community take security bugs in Talos Linux seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/siderolabs/talos/security/advisories/new) tab. The Sidero Labs team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance. The expected response time is within 3 business days, and the fix is expected to be delivered within 30 days. ## Supported Releases The Sidero Labs team will only provide security updates for the two latest minor releases of Talos Linux. If you are using an older version of Talos Linux, we recommend upgrading to the latest release. For example, if the latest release is `v1.8.3`, the supported releases are `v1.7.x` and `v1.8.x`. ================================================ FILE: api/buf.gen.yaml ================================================ version: v2 plugins: - local: ["go", "tool", "google.golang.org/protobuf/cmd/protoc-gen-go"] out: ./machinery/api opt: - paths=source_relative - local: ["go", "tool", "google.golang.org/grpc/cmd/protoc-gen-go-grpc"] out: ./machinery/api opt: - paths=source_relative - local: ["go", "tool", "github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto"] out: ./machinery/api opt: - paths=source_relative - features=marshal+unmarshal+size - local: ["go", "tool", "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc"] strategy: all out: ./docs/ opt: - docs.markdown.tmpl - api.md inputs: - directory: . paths: - cluster/ - common/ - inspect/ - machine/ - resource/ - security/ - storage/ - time/ ================================================ FILE: api/buf.yaml ================================================ version: v2 modules: - path: ./ excludes: - vendor/ breaking: use: - WIRE lint: use: - BASIC ignore_only: PACKAGE_DIRECTORY_MATCH: - resource/definitions/ - security/ ENUM_NO_ALLOW_ALIAS: - resource/definitions/enums/ - path: vendor/ lint: ignore: - vendor/ lint: use: - BASIC breaking: use: - FILE ================================================ FILE: api/cluster/cluster.proto ================================================ syntax = "proto3"; package cluster; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/cluster"; option java_package = "dev.talos.api.cluster"; import "common/common.proto"; import "google/protobuf/duration.proto"; // The cluster service definition. service ClusterService { rpc HealthCheck(HealthCheckRequest) returns (stream HealthCheckProgress); } message HealthCheckRequest { google.protobuf.Duration wait_timeout = 1; ClusterInfo cluster_info = 2; } message ClusterInfo { repeated string control_plane_nodes = 1; repeated string worker_nodes = 2; string force_endpoint = 3; } message HealthCheckProgress { common.Metadata metadata = 1; string message = 2; } ================================================ FILE: api/common/common.proto ================================================ syntax = "proto3"; package common; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/common"; option java_package = "dev.talos.api.common"; import "google/protobuf/any.proto"; import "google/protobuf/descriptor.proto"; import "google/rpc/status.proto"; // An alternative to using options could be extracting versions from comments. // Unfortunately, they are not available: https://github.com/golang/protobuf/issues/1134 // Also, while option numbers can be the same, // names should be different: https://github.com/protocolbuffers/protobuf/issues/4861 extend google.protobuf.MessageOptions { // Indicates the Talos version when this deprecated message will be removed from API. string remove_deprecated_message = 93117; } extend google.protobuf.FieldOptions { // Indicates the Talos version when this deprecated filed will be removed from API. string remove_deprecated_field = 93117; } extend google.protobuf.EnumOptions { // Indicates the Talos version when this deprecated enum will be removed from API. string remove_deprecated_enum = 93117; } extend google.protobuf.EnumValueOptions { // Indicates the Talos version when this deprecated enum value will be removed from API. string remove_deprecated_enum_value = 93117; } extend google.protobuf.MethodOptions { // Indicates the Talos version when this deprecated method will be removed from API. string remove_deprecated_method = 93117; } extend google.protobuf.ServiceOptions { // Indicates the Talos version when this deprecated service will be removed from API. string remove_deprecated_service = 93117; } enum Code { FATAL = 0; LOCKED = 1; CANCELED = 2; } message Error { Code code = 1; string message = 2; repeated google.protobuf.Any details = 3; } // Common metadata message nested in all reply message types message Metadata { // hostname of the server response comes from (injected by proxy) string hostname = 1; // error is set if request failed to the upstream (rest of response is // undefined) string error = 2; // error as gRPC Status google.rpc.Status status = 3; } message Data { Metadata metadata = 1; bytes bytes = 2; } message DataResponse { repeated Data messages = 1; } message Empty { Metadata metadata = 1; } message EmptyResponse { repeated Empty messages = 1; } enum ContainerDriver { CONTAINERD = 0; CRI = 1; } enum ContainerdNamespace { NS_UNKNOWN = 0; NS_SYSTEM = 1; NS_CRI = 2; } message ContainerdInstance { // Containerd instance to use. ContainerDriver driver = 1; // Containerd namespace to use. ContainerdNamespace namespace = 2; } message URL { string full_path = 1; } message PEMEncodedCertificateAndKey { bytes crt = 1; bytes key = 2; } message PEMEncodedKey { bytes key = 1; } message PEMEncodedCertificate { bytes crt = 1; } message NetIP { bytes ip = 1; } message NetIPPort { bytes ip = 1; int32 port = 2; } message NetIPPrefix { bytes ip = 1; int32 prefix_length = 2; } ================================================ FILE: api/docs.markdown.tmpl ================================================ --- title: API description: Talos gRPC API reference. --- ## Table of Contents {{range .Files}} {{$file_name := .Name}}- [{{.Name}}](#{{.Name}}) {{- if .Messages }} {{range .Messages}} - [{{.LongName}}](#{{.FullName}}) {{end}} {{- end -}} {{- if .Enums }} {{range .Enums}} - [{{.LongName}}](#{{.FullName}}) {{end}} {{- end -}} {{- if .Extensions }} {{range (list .Extensions | uniq)}} - [File-level Extensions](#{{$file_name}}-extensions) {{end}} {{- end -}} {{- if .Services }} {{range .Services}} - [{{.Name}}](#{{.FullName}}) {{end}} {{- end -}} {{end}} - [Scalar Value Types](#scalar-value-types) {{range .Files}} {{$file_name := .Name}}

Top

## {{.Name}} {{.Description}} {{range .Messages}} ### {{.LongName}} {{.Description}} {{if .HasFields}} | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | {{range .Fields -}} | {{.Name}} | [{{.LongType}}](#{{.FullType}}) | {{.Label}} | {{if (index .Options "deprecated"|default false)}}**Deprecated.** {{end}}{{nobr .Description | replace "\n\n" "

"}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | {{end}} {{end}} {{if .HasExtensions}} | Extension | Type | Base | Number | Description | | --------- | ---- | ---- | ------ | ----------- | {{range .Extensions -}} | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description | replace "\n\n" "

"}}{{if .DefaultValue}} Default: {{.DefaultValue}}{{end}} | {{end}} {{end}} {{end}} {{range .Enums}} ### {{.LongName}} {{.Description}} | Name | Number | Description | | ---- | ------ | ----------- | {{range .Values -}} | {{.Name}} | {{.Number}} | {{nobr .Description | replace "\n\n" "

"}} | {{end}} {{end}} {{if .HasExtensions}} ### File-level Extensions | Extension | Type | Base | Number | Description | | --------- | ---- | ---- | ------ | ----------- | {{range .Extensions -}} | {{.Name}} | {{.LongType}} | {{.ContainingLongType}} | {{.Number}} | {{nobr .Description | replace "\n\n" "

"}}{{if .DefaultValue}} Default: `{{.DefaultValue}}`{{end}} | {{end}} {{end}} {{range .Services}} ### {{.Name}} {{.Description}} | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| {{range .Methods -}} | {{.Name}} | [{{.RequestLongType}}](#{{.RequestFullType}}){{if .RequestStreaming}} stream{{end}} | [{{.ResponseLongType}}](#{{.ResponseFullType}}){{if .ResponseStreaming}} stream{{end}} | {{nobr .Description | replace "\n\n" "

"}} | {{end}} {{end}} {{end}} ## Scalar Value Types | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | | ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | {{range .Scalars -}} | {{.ProtoType}} | {{.Notes}} | {{.CppType}} | {{.JavaType}} | {{.PythonType}} | {{.GoType}} | {{.CSharp}} | {{.PhpType}} | {{.RubyType}} | {{end}} ================================================ FILE: api/inspect/inspect.proto ================================================ syntax = "proto3"; package inspect; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/inspect"; option java_package = "dev.talos.api.inspect"; import "common/common.proto"; import "google/protobuf/empty.proto"; // The inspect service definition. // // InspectService provides auxiliary API to inspect OS internals. service InspectService { rpc ControllerRuntimeDependencies(google.protobuf.Empty) returns (ControllerRuntimeDependenciesResponse); } // The ControllerRuntimeDependency message contains the graph of controller-resource dependencies. message ControllerRuntimeDependency { common.Metadata metadata = 1; repeated ControllerDependencyEdge edges = 2; } message ControllerRuntimeDependenciesResponse { repeated ControllerRuntimeDependency messages = 1; } enum DependencyEdgeType { OUTPUT_EXCLUSIVE = 0; OUTPUT_SHARED = 3; INPUT_STRONG = 1; INPUT_WEAK = 2; INPUT_DESTROY_READY = 4; } message ControllerDependencyEdge { string controller_name = 1; DependencyEdgeType edge_type = 2; string resource_namespace = 3; string resource_type = 4; string resource_id = 5; } ================================================ FILE: api/machine/debug.proto ================================================ syntax = "proto3"; package machine; import "common/common.proto"; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/machine"; option java_package = "dev.talos.api.machine"; // DebugService provides debugging and inspection capabilities for a Talos node. service DebugService { // ContainerRun runs a debug container, attaches to it, and streams I/O. rpc ContainerRun(stream DebugContainerRunRequest) returns (stream DebugContainerRunResponse); } message DebugContainerRunRequest { oneof request { // 1. send the container spec DebugContainerRunRequestSpec spec = 1; // 2. send either of the three below to interact with the running container bytes stdin_data = 2; int32 signal = 3; DebugContainerTerminalResize term_resize = 4; } } message DebugContainerRunRequestSpec { common.ContainerdInstance containerd = 1; string image_name = 2; repeated string args = 3; map env = 4; enum Profile { PROFILE_UNSPECIFIED = 0; PROFILE_PRIVILEGED = 1; } Profile profile = 5; bool tty = 6; } message DebugContainerTerminalResize { int32 width = 1; int32 height = 2; } message DebugContainerRunResponse { oneof resp { bytes stdout_data = 2; int32 exit_code = 3; } } ================================================ FILE: api/machine/image.proto ================================================ syntax = "proto3"; package machine; import "common/common.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/machine"; option java_package = "dev.talos.api.machine"; // The machine service definition. service ImageService { // List images in the containerd. rpc List(ImageServiceListRequest) returns (stream ImageServiceListResponse); // Pull an image into the containerd. rpc Pull(ImageServicePullRequest) returns (stream ImageServicePullResponse); // Import an image from a stream (tarball). rpc Import(stream ImageServiceImportRequest) returns (ImageServiceImportResponse); // Remove an image from the containerd. rpc Remove(ImageServiceRemoveRequest) returns (google.protobuf.Empty); // Verify an image signature. rpc Verify(ImageServiceVerifyRequest) returns (ImageServiceVerifyResponse); } message ImageServiceListRequest { common.ContainerdInstance containerd = 1; } message ImageServiceListResponse { string name = 1; string digest = 2; int64 size = 3; google.protobuf.Timestamp created_at = 4; map labels = 5; } message ImageServicePullRequest { common.ContainerdInstance containerd = 1; // Image reference to pull. string image_ref = 3; } message ImageServicePullResponse { oneof response { // Name of the pulled image (when done). string name = 1; // Progress of the image pull (intermediate updates). ImageServicePullProgress pull_progress = 2; } } message ImageServiceImportRequest { oneof request { // Containerd instance to use. common.ContainerdInstance containerd = 1; // Chunk of the image tarball. common.Data image_chunk = 2; } } message ImageServiceImportResponse { // Name of the imported image. string name = 1; } message ImageServicePullLayerProgress { enum Status { // Keep this in sync with ImagePullLayerProgress.Status. DOWNLOADING = 0; DOWNLOAD_COMPLETE = 1; EXTRACTING = 2; EXTRACT_COMPLETE = 3; ALREADY_EXISTS = 4; } Status status = 1; google.protobuf.Duration elapsed = 2; int64 offset = 3; int64 total = 4; } message ImageServicePullProgress { string layer_id = 1; ImageServicePullLayerProgress progress = 2; } message ImageServiceRemoveRequest { common.ContainerdInstance containerd = 1; // Image reference to remove. string image_ref = 2; } message ImageServiceVerifyRequest { // Image reference to verify. // // The image reference could be either in: // * the digest form (e.g. "docker.io/library/nginx@sha256:abc123...") to ensure that the exact image is verified. // * the tag form (e.g. "docker.io/library/nginx:latest") to verify the image currently pointed by the tag, and the resolved // digested will be returned in the response. // // Any other format will cause the error. string image_ref = 1; // Authentication credentials for the registry (if needed). // // By default Talos will use configured auth, but additional // image pull secret can be submitted here. ImageServiceCredentials credentials = 2; } message ImageServiceCredentials { // Host of the registry (e.g. "docker.io"). string host = 1; // Username for the registry. string username = 2; // Password (token) for the registry. string password = 3; } message ImageServiceVerifyResponse { // Was the image verified: if it didn't match any verify rule, false will be returned. // If the image matched the rule, but the verification failed, an error will be returned. bool verified = 1; // Free-form verification result message, e.g. with details about the matched rule and how the image was verified. string message = 2; // The pinned image reference with resolved digest that was verified (e.g. "docker.io/library/nginx@sha256:abc123..."). // // This is only set if verified=true. string digested_image_ref = 3; } ================================================ FILE: api/machine/lifecycle.proto ================================================ syntax = "proto3"; package machine; import "common/common.proto"; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/machine"; option java_package = "dev.talos.api.machine"; // The LifecycleService handles installation and upgrade operations. service LifecycleService { // Install Talos to disk. // The RPC should fail if the Talos is already installed on the target disk. rpc Install(LifecycleServiceInstallRequest) returns (stream LifecycleServiceInstallResponse); // Upgrade Talos to a new version. // The RPC should fail if Talos is not already installed on the target disk. rpc Upgrade(LifecycleServiceUpgradeRequest) returns (stream LifecycleServiceUpgradeResponse); } // InstallArtifactsSource specifies the source of the installation artifacts. message InstallArtifactsSource { // The reference name of the image, as returned by `talosctl image pull`. string image_name = 1; } // InstallDestination specifies the target for installation. message InstallDestination { // The disk to which Talos should be installed, e.g. "/dev/sda". string disk = 1; } // LifecycleServiceInstallRequest contains the necessary information to perform an installation. message LifecycleServiceInstallRequest { // The containerd instance to use for pulling the installation artifacts. common.ContainerdInstance containerd = 1; // The source of the installation artifacts. InstallArtifactsSource source = 2; // The destination for the installation. InstallDestination destination = 3; } // LifecycleServiceInstallProgress represents the progress of the installation or upgrade process. message LifecycleServiceInstallProgress { oneof response { // A message indicating the current progress of the installation or upgrade. string message = 1; // An exit code indicating the result of the installation or upgrade process. // A non-zero value indicates an error. // Server SHOULD NOT respond with error, even if the value is non-zero. // It's responsibility of the client to handle the exit code appropriately. int32 exit_code = 2; } } // LifecycleServiceInstallResponse is the response message for the Install RPC, containing progress updates. message LifecycleServiceInstallResponse { // The progress of the installation process. LifecycleServiceInstallProgress progress = 1; } // LifecycleServiceUpgradeRequest contains the necessary information to perform an upgrade. message LifecycleServiceUpgradeRequest { // The containerd instance to use for pulling the installation artifacts. common.ContainerdInstance containerd = 1; // The source of the installation artifacts for the upgrade. InstallArtifactsSource source = 2; } // LifecycleServiceUpgradeResponse is the response message for the Upgrade RPC, containing progress updates. message LifecycleServiceUpgradeResponse { // The progress of the upgrade process. LifecycleServiceInstallProgress progress = 1; } ================================================ FILE: api/machine/machine.proto ================================================ syntax = "proto3"; package machine; import "common/common.proto"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/machine"; option java_package = "dev.talos.api.machine"; // The machine service definition. service MachineService { rpc ApplyConfiguration(ApplyConfigurationRequest) returns (ApplyConfigurationResponse); // Bootstrap method makes control plane node enter etcd bootstrap mode. // Node aborts etcd join sequence and creates single-node etcd cluster. // If recover_etcd argument is specified, etcd is recovered from a snapshot // uploaded with EtcdRecover. rpc Bootstrap(BootstrapRequest) returns (BootstrapResponse); rpc Containers(ContainersRequest) returns (ContainersResponse); rpc Copy(CopyRequest) returns (stream common.Data); rpc CPUFreqStats(google.protobuf.Empty) returns (CPUFreqStatsResponse); rpc CPUInfo(google.protobuf.Empty) returns (CPUInfoResponse); rpc DiskStats(google.protobuf.Empty) returns (DiskStatsResponse); rpc Dmesg(DmesgRequest) returns (stream common.Data); rpc Events(EventsRequest) returns (stream Event); rpc EtcdMemberList(EtcdMemberListRequest) returns (EtcdMemberListResponse); // EtcdRemoveMemberByID removes a member from the etcd cluster identified by member ID. // This API should be used to remove members which don't have an associated Talos node anymore. // To remove a member with a running Talos node, use EtcdLeaveCluster API on the node to be removed. rpc EtcdRemoveMemberByID(EtcdRemoveMemberByIDRequest) returns (EtcdRemoveMemberByIDResponse); rpc EtcdLeaveCluster(EtcdLeaveClusterRequest) returns (EtcdLeaveClusterResponse); rpc EtcdForfeitLeadership(EtcdForfeitLeadershipRequest) returns (EtcdForfeitLeadershipResponse); // EtcdRecover method uploads etcd data snapshot created with EtcdSnapshot // to the node. // Snapshot can be later used to recover the cluster via Bootstrap method. rpc EtcdRecover(stream common.Data) returns (EtcdRecoverResponse); // EtcdSnapshot method creates etcd data snapshot (backup) from the local etcd instance // and streams it back to the client. // This method is available only on control plane nodes (which run etcd). rpc EtcdSnapshot(EtcdSnapshotRequest) returns (stream common.Data); // EtcdAlarmList lists etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). rpc EtcdAlarmList(google.protobuf.Empty) returns (EtcdAlarmListResponse); // EtcdAlarmDisarm disarms etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). rpc EtcdAlarmDisarm(google.protobuf.Empty) returns (EtcdAlarmDisarmResponse); // EtcdDefragment defragments etcd data directory for the current node. // Defragmentation is a resource-heavy operation, so it should only run on a specific // node. // This method is available only on control plane nodes (which run etcd). rpc EtcdDefragment(google.protobuf.Empty) returns (EtcdDefragmentResponse); // EtcdStatus returns etcd status for the current member. // This method is available only on control plane nodes (which run etcd). rpc EtcdStatus(google.protobuf.Empty) returns (EtcdStatusResponse); // EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). rpc EtcdDowngradeValidate(EtcdDowngradeValidateRequest) returns (EtcdDowngradeValidateResponse); // EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). rpc EtcdDowngradeEnable(EtcdDowngradeEnableRequest) returns (EtcdDowngradeEnableResponse); // EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. // This method is available only on control plane nodes (which run etcd). rpc EtcdDowngradeCancel(google.protobuf.Empty) returns (EtcdDowngradeCancelResponse); rpc Hostname(google.protobuf.Empty) returns (HostnameResponse); rpc Kubeconfig(google.protobuf.Empty) returns (stream common.Data); rpc List(ListRequest) returns (stream FileInfo); rpc DiskUsage(DiskUsageRequest) returns (stream DiskUsageInfo); rpc LoadAvg(google.protobuf.Empty) returns (LoadAvgResponse); rpc Logs(LogsRequest) returns (stream common.Data); rpc LogsContainers(google.protobuf.Empty) returns (LogsContainersResponse); rpc Memory(google.protobuf.Empty) returns (MemoryResponse); rpc Mounts(google.protobuf.Empty) returns (MountsResponse); rpc NetworkDeviceStats(google.protobuf.Empty) returns (NetworkDeviceStatsResponse); rpc Processes(google.protobuf.Empty) returns (ProcessesResponse); rpc Read(ReadRequest) returns (stream common.Data); rpc Reboot(RebootRequest) returns (RebootResponse); rpc Restart(RestartRequest) returns (RestartResponse); rpc Rollback(RollbackRequest) returns (RollbackResponse); rpc Reset(ResetRequest) returns (ResetResponse); rpc ServiceList(google.protobuf.Empty) returns (ServiceListResponse); rpc ServiceRestart(ServiceRestartRequest) returns (ServiceRestartResponse); rpc ServiceStart(ServiceStartRequest) returns (ServiceStartResponse); rpc ServiceStop(ServiceStopRequest) returns (ServiceStopResponse); rpc Shutdown(ShutdownRequest) returns (ShutdownResponse); rpc Stats(StatsRequest) returns (StatsResponse); rpc SystemStat(google.protobuf.Empty) returns (SystemStatResponse); // Upgrade initiates the upgrade of the node to a new version of Talos. // // Use LifecycleService Upgrade RPC instead. rpc Upgrade(UpgradeRequest) returns (UpgradeResponse) { option (common.remove_deprecated_method) = "v1.18"; option deprecated = true; } rpc Version(google.protobuf.Empty) returns (VersionResponse); // GenerateClientConfiguration generates talosctl client configuration (talosconfig). rpc GenerateClientConfiguration(GenerateClientConfigurationRequest) returns (GenerateClientConfigurationResponse); // PacketCapture performs packet capture and streams back pcap file. rpc PacketCapture(PacketCaptureRequest) returns (stream common.Data); // Netstat provides information about network connections. rpc Netstat(NetstatRequest) returns (NetstatResponse); // MetaWrite writes a META key-value pair. rpc MetaWrite(MetaWriteRequest) returns (MetaWriteResponse); // MetaDelete deletes a META key. rpc MetaDelete(MetaDeleteRequest) returns (MetaDeleteResponse); // ImageList lists images in the CRI. // // Use ImageService List RPC instead. rpc ImageList(ImageListRequest) returns (stream ImageListResponse) { option (common.remove_deprecated_method) = "v1.18"; option deprecated = true; } // ImagePull pulls an image into the CRI. // // Use ImageService Pull RPC instead. rpc ImagePull(ImagePullRequest) returns (ImagePullResponse) { option (common.remove_deprecated_method) = "v1.18"; option deprecated = true; } } // rpc applyConfiguration // ApplyConfiguration describes a request to assert a new configuration upon a // node. message ApplyConfigurationRequest { enum Mode { REBOOT = 0; AUTO = 1; NO_REBOOT = 2; STAGED = 3; TRY = 4; } bytes data = 1; Mode mode = 4; bool dry_run = 5; google.protobuf.Duration try_mode_timeout = 6; } // ApplyConfigurationResponse describes the response to a configuration request. message ApplyConfiguration { common.Metadata metadata = 1; // Configuration validation warnings. repeated string warnings = 2; // States which mode was actually chosen. ApplyConfigurationRequest.Mode mode = 3; // Human-readable message explaining the result of the apply configuration call. string mode_details = 4; } message ApplyConfigurationResponse { repeated ApplyConfiguration messages = 1; } // rpc reboot message RebootRequest { enum Mode { DEFAULT = 0; POWERCYCLE = 1; FORCE = 2; } Mode mode = 1; } // The reboot message containing the reboot status. message Reboot { common.Metadata metadata = 1; string actor_id = 2; } message RebootResponse { repeated Reboot messages = 1; } // rpc Bootstrap message BootstrapRequest { // Enable etcd recovery from the snapshot. // Snapshot should be uploaded before this call via EtcdRecover RPC. bool recover_etcd = 1; // Skip hash check on the snapshot (etcd). // Enable this when recovering from data directory copy to skip integrity check. bool recover_skip_hash_check = 2; } // The bootstrap message containing the bootstrap status. message Bootstrap { common.Metadata metadata = 1; } message BootstrapResponse { repeated Bootstrap messages = 1; } // rpc events message SequenceEvent { string sequence = 1; enum Action { NOOP = 0; START = 1; STOP = 2; } Action action = 2; common.Error error = 3; } message PhaseEvent { string phase = 1; enum Action { START = 0; STOP = 1; } Action action = 2; } message TaskEvent { string task = 1; enum Action { START = 0; STOP = 1; } Action action = 2; } message ServiceStateEvent { string service = 1; enum Action { INITIALIZED = 0; PREPARING = 1; WAITING = 2; RUNNING = 3; STOPPING = 4; FINISHED = 5; FAILED = 6; SKIPPED = 7; STARTING = 8; } Action action = 2; string message = 3; ServiceHealth health = 4; } message RestartEvent { int64 cmd = 1; } // ConfigLoadErrorEvent is reported when the config loading has failed. message ConfigLoadErrorEvent { string error = 1; } // ConfigValidationErrorEvent is reported when config validation has failed. message ConfigValidationErrorEvent { string error = 1; } // AddressEvent reports node endpoints aggregated from k8s.Endpoints and network.Hostname. message AddressEvent { string hostname = 1; repeated string addresses = 2; } // MachineStatusEvent reports changes to the MachineStatus resource. message MachineStatusEvent { message MachineStatus { message UnmetCondition { string name = 1; string reason = 2; } bool ready = 1; repeated UnmetCondition unmet_conditions = 2; } enum MachineStage { UNKNOWN = 0; BOOTING = 1; INSTALLING = 2; MAINTENANCE = 3; RUNNING = 4; REBOOTING = 5; SHUTTING_DOWN = 6; RESETTING = 7; UPGRADING = 8; } MachineStage stage = 1; MachineStatus status = 2; } message EventsRequest { int32 tail_events = 1; string tail_id = 2; int32 tail_seconds = 3; string with_actor_id = 4; } message Event { common.Metadata metadata = 1; google.protobuf.Any data = 2; string id = 3; string actor_id = 4; } // rpc reset message ResetPartitionSpec { string label = 1; bool wipe = 2; } message ResetRequest { enum WipeMode { ALL = 0; SYSTEM_DISK = 1; USER_DISKS = 2; } // Graceful indicates whether node should leave etcd before the upgrade, it also // enforces etcd checks before leaving. bool graceful = 1; // Reboot indicates whether node should reboot or halt after resetting. bool reboot = 2; // System_partitions_to_wipe lists specific system disk partitions to be reset (wiped). // If system_partitions_to_wipe is empty, all the partitions are erased. repeated ResetPartitionSpec system_partitions_to_wipe = 3; // UserDisksToWipe lists specific connected block devices to be reset (wiped). repeated string user_disks_to_wipe = 4; // WipeMode defines which devices should be wiped. WipeMode mode = 5; } // The reset message containing the restart status. message Reset { common.Metadata metadata = 1; string actor_id = 2; } message ResetResponse { repeated Reset messages = 1; } // rpc shutdown // The messages message containing the shutdown status. message Shutdown { common.Metadata metadata = 1; string actor_id = 2; } message ShutdownRequest { // Force indicates whether node should shutdown without first cordening and draining bool force = 1; } message ShutdownResponse { repeated Shutdown messages = 1; } // rpc upgrade message UpgradeRequest { enum RebootMode { DEFAULT = 0; POWERCYCLE = 1; } string image = 1; bool preserve = 2; bool stage = 3; bool force = 4; RebootMode reboot_mode = 5; } message Upgrade { common.Metadata metadata = 1; string ack = 2; string actor_id = 3; } message UpgradeResponse { repeated Upgrade messages = 1; } // rpc servicelist message ServiceList { common.Metadata metadata = 1; repeated ServiceInfo services = 2; } message ServiceListResponse { repeated ServiceList messages = 1; } message ServiceInfo { string id = 1; string state = 2; ServiceEvents events = 3; ServiceHealth health = 4; } message ServiceEvents { repeated ServiceEvent events = 1; } message ServiceEvent { string msg = 1; string state = 2; google.protobuf.Timestamp ts = 3; } message ServiceHealth { bool unknown = 1; bool healthy = 2; string last_message = 3; google.protobuf.Timestamp last_change = 4; } // rpc servicestart message ServiceStartRequest { string id = 1; } message ServiceStart { common.Metadata metadata = 1; string resp = 2; } message ServiceStartResponse { repeated ServiceStart messages = 1; } message ServiceStopRequest { string id = 1; } message ServiceStop { common.Metadata metadata = 1; string resp = 2; } message ServiceStopResponse { repeated ServiceStop messages = 1; } message ServiceRestartRequest { string id = 1; } message ServiceRestart { common.Metadata metadata = 1; string resp = 2; } message ServiceRestartResponse { repeated ServiceRestart messages = 1; } // CopyRequest describes a request to copy data out of Talos node // // Copy produces .tar.gz archive which is streamed back to the caller message CopyRequest { // Root path to start copying data out, it might be either a file or directory string root_path = 1; } // ListRequest describes a request to list the contents of a directory. message ListRequest { // Root indicates the root directory for the list. If not indicated, '/' is // presumed. string root = 1; // Recurse indicates that subdirectories should be recursed. bool recurse = 2; // RecursionDepth indicates how many levels of subdirectories should be // recursed. The default (0) indicates that no limit should be enforced. int32 recursion_depth = 3; // File type. enum Type { // Regular file (not directory, symlink, etc). REGULAR = 0; // Directory. DIRECTORY = 1; // Symbolic link. SYMLINK = 2; } // Types indicates what file type should be returned. If not indicated, // all files will be returned. repeated Type types = 4; // Report xattrs bool report_xattrs = 5; } // DiskUsageRequest describes a request to list disk usage of directories and regular files message DiskUsageRequest { // RecursionDepth indicates how many levels of subdirectories should be // recursed. The default (0) indicates that no limit should be enforced. int32 recursion_depth = 1; // All write sizes for all files, not just directories. bool all = 2; // Threshold exclude entries smaller than SIZE if positive, // or entries greater than SIZE if negative. int64 threshold = 3; // DiskUsagePaths is the list of directories to calculate disk usage for. repeated string paths = 4; } // FileInfo describes a file or directory's information message FileInfo { common.Metadata metadata = 1; // Name is the name (including prefixed path) of the file or directory string name = 2; // Size indicates the number of bytes contained within the file int64 size = 3; // Mode is the bitmap of UNIX mode/permission flags of the file uint32 mode = 4; // Modified indicates the UNIX timestamp at which the file was last modified int64 modified = 5; // IsDir indicates that the file is a directory bool is_dir = 6; // Error describes any error encountered while trying to read the file // information. string error = 7; // Link is filled with symlink target string link = 8; // RelativeName is the name of the file or directory relative to the RootPath string relative_name = 9; // Owner uid uint32 uid = 10; // Owner gid uint32 gid = 11; // Extended attributes (if present and requested) repeated Xattr xattrs = 12; } message Xattr { string name = 1; bytes data = 2; } // DiskUsageInfo describes a file or directory's information for du command message DiskUsageInfo { common.Metadata metadata = 1; // Name is the name (including prefixed path) of the file or directory string name = 2; // Size indicates the number of bytes contained within the file int64 size = 3; // Error describes any error encountered while trying to read the file // information. string error = 4; // RelativeName is the name of the file or directory relative to the RootPath string relative_name = 5; } // The messages message containing the requested df stats. message Mounts { common.Metadata metadata = 1; repeated MountStat stats = 2; } message MountsResponse { repeated Mounts messages = 1; } // The messages message containing the requested processes. message MountStat { string filesystem = 1; uint64 size = 2; uint64 available = 3; string mounted_on = 4; } message Version { common.Metadata metadata = 1; VersionInfo version = 2; PlatformInfo platform = 3; // Features describe individual Talos features that can be switched on or off. FeaturesInfo features = 4; } message VersionResponse { repeated Version messages = 1; } message VersionInfo { string tag = 1; string sha = 2; string built = 3; string go_version = 4; string os = 5; string arch = 6; } message PlatformInfo { string name = 1; string mode = 2; } // FeaturesInfo describes individual Talos features that can be switched on or off. message FeaturesInfo { // RBAC is true if role-based access control is enabled. bool rbac = 1; } // rpc logs // The request message containing the process name. message LogsRequest { string namespace = 1; string id = 2; // driver might be default "containerd" or "cri" common.ContainerDriver driver = 3; bool follow = 4; int32 tail_lines = 5; } message ReadRequest { string path = 1; } // LogsContainer describes all available registered log containers. message LogsContainer { common.Metadata metadata = 1; repeated string ids = 2; } message LogsContainersResponse { repeated LogsContainer messages = 1; } // rpc rollback message RollbackRequest {} message Rollback { common.Metadata metadata = 1; } message RollbackResponse { repeated Rollback messages = 1; } // rpc Containers message ContainersRequest { string namespace = 1; // driver might be default "containerd" or "cri" common.ContainerDriver driver = 2; } // The messages message containing the requested containers. message ContainerInfo { string namespace = 1; string id = 2; string uid = 10; string internal_id = 9; string image = 3; uint32 pid = 4; string status = 5; string pod_id = 6; string name = 7; string network_namespace = 8; } // The messages message containing the requested containers. message Container { common.Metadata metadata = 1; repeated ContainerInfo containers = 2; } message ContainersResponse { repeated Container messages = 1; } // dmesg message DmesgRequest { bool follow = 1; bool tail = 2; } // rpc processes message ProcessesResponse { repeated Process messages = 1; } message Process { common.Metadata metadata = 1; repeated ProcessInfo processes = 2; } message ProcessInfo { int32 pid = 1; int32 ppid = 2; string state = 3; int32 threads = 4; double cpu_time = 5; uint64 virtual_memory = 6; uint64 resident_memory = 7; string command = 8; string executable = 9; string args = 10; string label = 11; } // rpc restart // The request message containing the process to restart. message RestartRequest { string namespace = 1; string id = 2; // driver might be default "containerd" or "cri" common.ContainerDriver driver = 3; } message Restart { common.Metadata metadata = 1; } // The messages message containing the restart status. message RestartResponse { repeated Restart messages = 1; } // rpc stats // The request message containing the containerd namespace. message StatsRequest { string namespace = 1; // driver might be default "containerd" or "cri" common.ContainerDriver driver = 2; } // The messages message containing the requested stats. message Stats { common.Metadata metadata = 1; repeated Stat stats = 2; } message StatsResponse { repeated Stats messages = 1; } // The messages message containing the requested stat. message Stat { string namespace = 1; string id = 2; uint64 memory_usage = 4; uint64 cpu_usage = 5; string pod_id = 6; string name = 7; } message Memory { common.Metadata metadata = 1; MemInfo meminfo = 2; } message MemoryResponse { repeated Memory messages = 1; } message MemInfo { uint64 memtotal = 1; uint64 memfree = 2; uint64 memavailable = 3; uint64 buffers = 4; uint64 cached = 5; uint64 swapcached = 6; uint64 active = 7; uint64 inactive = 8; uint64 activeanon = 9; uint64 inactiveanon = 10; uint64 activefile = 11; uint64 inactivefile = 12; uint64 unevictable = 13; uint64 mlocked = 14; uint64 swaptotal = 15; uint64 swapfree = 16; uint64 dirty = 17; uint64 writeback = 18; uint64 anonpages = 19; uint64 mapped = 20; uint64 shmem = 21; uint64 slab = 22; uint64 sreclaimable = 23; uint64 sunreclaim = 24; uint64 kernelstack = 25; uint64 pagetables = 26; uint64 nfsunstable = 27; uint64 bounce = 28; uint64 writebacktmp = 29; uint64 commitlimit = 30; uint64 committedas = 31; uint64 vmalloctotal = 32; uint64 vmallocused = 33; uint64 vmallocchunk = 34; uint64 hardwarecorrupted = 35; uint64 anonhugepages = 36; uint64 shmemhugepages = 37; uint64 shmempmdmapped = 38; uint64 cmatotal = 39; uint64 cmafree = 40; uint64 hugepagestotal = 41; uint64 hugepagesfree = 42; uint64 hugepagesrsvd = 43; uint64 hugepagessurp = 44; uint64 hugepagesize = 45; uint64 directmap4k = 46; uint64 directmap2m = 47; uint64 directmap1g = 48; } // rpc Hostname message HostnameResponse { repeated Hostname messages = 1; } message Hostname { common.Metadata metadata = 1; string hostname = 2; } // rpc LoadAvg message LoadAvgResponse { repeated LoadAvg messages = 1; } message LoadAvg { common.Metadata metadata = 1; double load1 = 2; double load5 = 3; double load15 = 4; } // rpc SystemStat message SystemStatResponse { repeated SystemStat messages = 1; } message SystemStat { common.Metadata metadata = 1; uint64 boot_time = 2; CPUStat cpu_total = 3; repeated CPUStat cpu = 4; uint64 irq_total = 5; repeated uint64 irq = 6; uint64 context_switches = 7; uint64 process_created = 8; uint64 process_running = 9; uint64 process_blocked = 10; uint64 soft_irq_total = 11; SoftIRQStat soft_irq = 12; } message CPUStat { double user = 1; double nice = 2; double system = 3; double idle = 4; double iowait = 5; double irq = 6; double soft_irq = 7; double steal = 8; double guest = 9; double guest_nice = 10; } message SoftIRQStat { uint64 hi = 1; uint64 timer = 2; uint64 net_tx = 3; uint64 net_rx = 4; uint64 block = 5; uint64 block_io_poll = 6; uint64 tasklet = 7; uint64 sched = 8; uint64 hrtimer = 9; uint64 rcu = 10; } // rpc CPUFreqStats message CPUFreqStatsResponse { repeated CPUsFreqStats messages = 1; } message CPUsFreqStats { common.Metadata metadata = 1; repeated CPUFreqStats cpu_freq_stats = 2; } message CPUFreqStats { uint64 current_frequency = 1; uint64 minimum_frequency = 2; uint64 maximum_frequency = 3; string governor = 4; } // rpc CPUInfo message CPUInfoResponse { repeated CPUsInfo messages = 1; } message CPUsInfo { common.Metadata metadata = 1; repeated CPUInfo cpu_info = 2; } message CPUInfo { uint32 processor = 1; string vendor_id = 2; string cpu_family = 3; string model = 4; string model_name = 5; string stepping = 6; string microcode = 7; double cpu_mhz = 8; string cache_size = 9; string physical_id = 10; uint32 siblings = 11; string core_id = 12; uint32 cpu_cores = 13; string apic_id = 14; string initial_apic_id = 15; string fpu = 16; string fpu_exception = 17; uint32 cpu_id_level = 18; string wp = 19; repeated string flags = 20; repeated string bugs = 21; double bogo_mips = 22; uint32 cl_flush_size = 23; uint32 cache_alignment = 24; string address_sizes = 25; string power_management = 26; } // rpc NetworkDeviceStats message NetworkDeviceStatsResponse { repeated NetworkDeviceStats messages = 1; } message NetworkDeviceStats { common.Metadata metadata = 1; NetDev total = 2; repeated NetDev devices = 3; } message NetDev { string name = 1; uint64 rx_bytes = 2; uint64 rx_packets = 3; uint64 rx_errors = 4; uint64 rx_dropped = 5; uint64 rx_fifo = 6; uint64 rx_frame = 7; uint64 rx_compressed = 8; uint64 rx_multicast = 9; uint64 tx_bytes = 10; uint64 tx_packets = 11; uint64 tx_errors = 12; uint64 tx_dropped = 13; uint64 tx_fifo = 14; uint64 tx_collisions = 15; uint64 tx_carrier = 16; uint64 tx_compressed = 17; } // rpc DiskStats message DiskStatsResponse { repeated DiskStats messages = 1; } message DiskStats { common.Metadata metadata = 1; DiskStat total = 2; repeated DiskStat devices = 3; } message DiskStat { string name = 1; uint64 read_completed = 2; uint64 read_merged = 3; uint64 read_sectors = 4; uint64 read_time_ms = 5; uint64 write_completed = 6; uint64 write_merged = 7; uint64 write_sectors = 8; uint64 write_time_ms = 9; uint64 io_in_progress = 10; uint64 io_time_ms = 11; uint64 io_time_weighted_ms = 12; uint64 discard_completed = 13; uint64 discard_merged = 14; uint64 discard_sectors = 15; uint64 discard_time_ms = 16; } message EtcdLeaveClusterRequest {} message EtcdLeaveCluster { common.Metadata metadata = 1; } message EtcdLeaveClusterResponse { repeated EtcdLeaveCluster messages = 1; } message EtcdRemoveMemberRequest { string member = 1; } message EtcdRemoveMember { common.Metadata metadata = 1; } message EtcdRemoveMemberResponse { repeated EtcdRemoveMember messages = 1; } message EtcdRemoveMemberByIDRequest { uint64 member_id = 1; } message EtcdRemoveMemberByID { common.Metadata metadata = 1; } message EtcdRemoveMemberByIDResponse { repeated EtcdRemoveMemberByID messages = 1; } message EtcdForfeitLeadershipRequest {} message EtcdForfeitLeadership { common.Metadata metadata = 1; string member = 2; } message EtcdForfeitLeadershipResponse { repeated EtcdForfeitLeadership messages = 1; } message EtcdMemberListRequest { bool query_local = 1; } // EtcdMember describes a single etcd member. message EtcdMember { // member ID. uint64 id = 2; // human-readable name of the member. string hostname = 3; // the list of URLs the member exposes to clients for communication. repeated string peer_urls = 4; // the list of URLs the member exposes to the cluster for communication. repeated string client_urls = 5; // learner flag bool is_learner = 6; } // EtcdMembers contains the list of members registered on the host. message EtcdMembers { common.Metadata metadata = 1; // list of member hostnames. repeated string legacy_members = 2; // the list of etcd members registered on the node. repeated EtcdMember members = 3; } message EtcdMemberListResponse { repeated EtcdMembers messages = 1; } message EtcdSnapshotRequest {} message EtcdRecover { common.Metadata metadata = 1; } message EtcdRecoverResponse { repeated EtcdRecover messages = 1; } message EtcdAlarmListResponse { repeated EtcdAlarm messages = 1; } message EtcdAlarm { common.Metadata metadata = 1; repeated EtcdMemberAlarm member_alarms = 2; } message EtcdMemberAlarm { enum AlarmType { NONE = 0; NOSPACE = 1; CORRUPT = 2; } uint64 member_id = 1; AlarmType alarm = 2; } message EtcdAlarmDisarmResponse { repeated EtcdAlarmDisarm messages = 1; } message EtcdAlarmDisarm { common.Metadata metadata = 1; repeated EtcdMemberAlarm member_alarms = 2; } message EtcdDefragmentResponse { repeated EtcdDefragment messages = 1; } message EtcdDefragment { common.Metadata metadata = 1; } message EtcdStatusResponse { repeated EtcdStatus messages = 1; } message EtcdStatus { common.Metadata metadata = 1; EtcdMemberStatus member_status = 2; } message EtcdMemberStatus { string storage_version = 11; uint64 member_id = 10; string protocol_version = 1; int64 db_size = 2; int64 db_size_in_use = 3; uint64 leader = 4; uint64 raft_index = 5; uint64 raft_term = 6; uint64 raft_applied_index = 7; repeated string errors = 8; bool is_learner = 9; } message EtcdDowngradeValidateRequest { string version = 1; } message EtcdDowngradeValidateResponse { repeated EtcdDowngradeValidate messages = 1; } message EtcdDowngradeValidate { common.Metadata metadata = 1; EtcdClusterDowngrade cluster_downgrade = 2; } message EtcdDowngradeEnableRequest { string version = 1; } message EtcdDowngradeEnableResponse { repeated EtcdDowngradeEnable messages = 1; } message EtcdDowngradeEnable { common.Metadata metadata = 1; EtcdClusterDowngrade cluster_downgrade = 2; } message EtcdDowngradeCancelResponse { repeated EtcdDowngradeCancel messages = 1; } message EtcdDowngradeCancel { common.Metadata metadata = 1; EtcdClusterDowngrade cluster_downgrade = 2; } message EtcdClusterDowngrade { string cluster_version = 1; } // rpc generateConfiguration message RouteConfig { string network = 1; string gateway = 2; uint32 metric = 3; } message DHCPOptionsConfig { uint32 route_metric = 1; } message NetworkDeviceConfig { string interface = 1; string cidr = 2; int32 mtu = 3; bool dhcp = 4; bool ignore = 5; DHCPOptionsConfig dhcp_options = 6; repeated RouteConfig routes = 7; } message NetworkConfig { string hostname = 1; repeated NetworkDeviceConfig interfaces = 2; } message InstallConfig { string install_disk = 1; string install_image = 2; } message MachineConfig { enum MachineType { TYPE_UNKNOWN = 0; TYPE_INIT = 1; TYPE_CONTROL_PLANE = 2; TYPE_WORKER = 3; } MachineType type = 1; InstallConfig install_config = 2; NetworkConfig network_config = 3; string kubernetes_version = 4; } message ControlPlaneConfig { string endpoint = 1; } message CNIConfig { string name = 1; repeated string urls = 2; } message ClusterNetworkConfig { string dns_domain = 1; CNIConfig cni_config = 2; } message ClusterConfig { string name = 1; ControlPlaneConfig control_plane = 2; ClusterNetworkConfig cluster_network = 3; bool allow_scheduling_on_control_planes = 4; } message GenerateClientConfigurationRequest { // Roles in the generated client certificate. repeated string roles = 1; // Client certificate TTL. google.protobuf.Duration crt_ttl = 2; } message GenerateClientConfiguration { common.Metadata metadata = 1; // PEM-encoded CA certificate. bytes ca = 2; // PEM-encoded generated client certificate. bytes crt = 3; // PEM-encoded generated client key. bytes key = 4; // Client configuration (talosconfig) file content. bytes talosconfig = 5; } message GenerateClientConfigurationResponse { repeated GenerateClientConfiguration messages = 1; } message PacketCaptureRequest { // Interface name to perform packet capture on. string interface = 1; // Enable promiscuous mode. bool promiscuous = 2; // Snap length in bytes. uint32 snap_len = 3; // BPF filter. repeated BPFInstruction bpf_filter = 4; } message BPFInstruction { uint32 op = 1; uint32 jt = 2; uint32 jf = 3; uint32 k = 4; } message NetstatRequest { enum Filter { ALL = 0; CONNECTED = 1; LISTENING = 2; } Filter filter = 1; message Feature { bool pid = 1; } Feature feature = 2; message L4proto { bool tcp = 1; bool tcp6 = 2; bool udp = 3; bool udp6 = 4; bool udplite = 5; bool udplite6 = 6; bool raw = 7; bool raw6 = 8; } L4proto l4proto = 3; message NetNS { bool hostnetwork = 1; repeated string netns = 2; bool allnetns = 3; } NetNS netns = 4; } message ConnectRecord { string l4proto = 1; string localip = 2; uint32 localport = 3; string remoteip = 4; uint32 remoteport = 5; enum State { RESERVED = 0; ESTABLISHED = 1; SYN_SENT = 2; SYN_RECV = 3; FIN_WAIT1 = 4; FIN_WAIT2 = 5; TIME_WAIT = 6; CLOSE = 7; CLOSEWAIT = 8; LASTACK = 9; LISTEN = 10; CLOSING = 11; } State state = 6; uint64 txqueue = 7; uint64 rxqueue = 8; enum TimerActive { OFF = 0; ON = 1; KEEPALIVE = 2; TIMEWAIT = 3; PROBE = 4; } TimerActive tr = 9; uint64 timerwhen = 10; uint64 retrnsmt = 11; uint32 uid = 12; uint64 timeout = 13; uint64 inode = 14; uint64 ref = 15; uint64 pointer = 16; message Process { uint32 pid = 1; string name = 2; } Process process = 17; string netns = 18; } message Netstat { common.Metadata metadata = 1; repeated ConnectRecord connectrecord = 2; } message NetstatResponse { repeated Netstat messages = 1; } message MetaWriteRequest { uint32 key = 1; bytes value = 2; } message MetaWrite { common.Metadata metadata = 1; } message MetaWriteResponse { repeated MetaWrite messages = 1; } message MetaDeleteRequest { uint32 key = 1; } message MetaDelete { common.Metadata metadata = 1; } message MetaDeleteResponse { repeated MetaDelete messages = 1; } message ImageListRequest { // Containerd namespace to use. common.ContainerdNamespace namespace = 1; } message ImageListResponse { common.Metadata metadata = 1; string name = 2; string digest = 3; int64 size = 4; google.protobuf.Timestamp created_at = 5; } message ImagePullRequest { // Containerd namespace to use. common.ContainerdNamespace namespace = 1; // Image reference to pull. string reference = 2; } message ImagePull { common.Metadata metadata = 1; } message ImagePullResponse { repeated ImagePull messages = 1; } ================================================ FILE: api/resource/config/config.proto ================================================ syntax = "proto3"; package resource.config; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/config"; option java_package = "dev.talos.api.resource.config"; // MessageConfigSpec is the spec for the config.MachineConfig resource. message MachineConfigSpec { // Contains YAML marshalled machine configuration. // // Byte representation is preserved as the machine configuration was submitted to the node. bytes yaml_marshalled = 1; } // MachineType matches machine.Type constants. enum MachineType { UNKNOWN = 0; INIT = 1; CONTROL_PLANE = 2; WORKER = 3; } // MachineTypeSpec is the spec for the config.MachineType resource. message MachineTypeSpec { MachineType machine_type = 1; } ================================================ FILE: api/resource/definitions/block/block.proto ================================================ syntax = "proto3"; package talos.resource.definitions.block; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block"; option java_package = "dev.talos.api.resource.definitions.block"; import "google/api/expr/v1alpha1/checked.proto"; import "resource/definitions/enums/enums.proto"; // DeviceSpec is the spec for devices status. message DeviceSpec { string type = 1; int64 major = 2; int64 minor = 3; string partition_name = 4; int64 partition_number = 5; int64 generation = 6; string device_path = 7; string parent = 8; repeated string secondaries = 9; } // DiscoveredVolumeSpec is the spec for DiscoveredVolumes resource. message DiscoveredVolumeSpec { uint64 size = 1; uint64 sector_size = 2; uint64 io_size = 3; string name = 4; string uuid = 5; string label = 6; uint32 block_size = 7; uint32 filesystem_block_size = 8; uint64 probed_size = 9; string partition_uuid = 10; string partition_type = 11; string partition_label = 12; uint64 partition_index = 13; string type = 14; string device_path = 15; string parent = 16; string dev_path = 17; string parent_dev_path = 18; string pretty_size = 19; uint64 offset = 20; } // DiscoveryRefreshRequestSpec is the spec for DiscoveryRefreshRequest. message DiscoveryRefreshRequestSpec { int64 request = 1; } // DiscoveryRefreshStatusSpec is the spec for DiscoveryRefreshStatus status. message DiscoveryRefreshStatusSpec { int64 request = 1; } // DiskSelector selects a disk for the volume. message DiskSelector { google.api.expr.v1alpha1.CheckedExpr match = 1; string external = 2; } // DiskSpec is the spec for Disks status. message DiskSpec { uint64 size = 1; uint64 io_size = 2; uint64 sector_size = 3; bool readonly = 4; string model = 5; string serial = 6; string modalias = 7; string wwid = 8; string bus_path = 9; string sub_system = 10; string transport = 11; bool rotational = 12; bool cdrom = 13; string dev_path = 14; string pretty_size = 15; repeated string secondary_disks = 16; string uuid = 17; repeated string symlinks = 18; } // EncryptionKey is the spec for volume encryption key. message EncryptionKey { int64 slot = 1; talos.resource.definitions.enums.BlockEncryptionKeyType type = 2; bytes static_passphrase = 3; string kms_endpoint = 4; bool tpm_check_secureboot_status_on_enroll = 5; bool lock_to_state = 6; repeated int64 tpmpc_rs = 7; repeated int64 tpm_pub_key_pc_rs = 8; } // EncryptionSpec is the spec for volume encryption. message EncryptionSpec { talos.resource.definitions.enums.BlockEncryptionProviderType provider = 1; repeated EncryptionKey keys = 2; string cipher = 3; uint64 key_size = 4; uint64 block_size = 5; repeated string perf_options = 6; } // FilesystemSpec is the spec for volume filesystem. message FilesystemSpec { talos.resource.definitions.enums.BlockFilesystemType type = 1; string label = 2; } // LocatorSpec is the spec for volume locator. message LocatorSpec { google.api.expr.v1alpha1.CheckedExpr match = 1; google.api.expr.v1alpha1.CheckedExpr disk_match = 2; } // MountRequestSpec is the spec for MountRequest. message MountRequestSpec { string volume_id = 1; string parent_mount_id = 2; repeated string requesters = 3; repeated string requester_i_ds = 4; bool read_only = 5; bool detached = 6; bool disable_access_time = 7; bool secure = 8; } // MountSpec is the spec for volume mount. message MountSpec { string target_path = 1; string selinux_label = 2; bool project_quota_support = 3; string parent_id = 4; uint32 file_mode = 5; int64 uid = 6; int64 gid = 7; bool recursive_relabel = 8; string bind_target = 9; repeated ParameterSpec parameters = 10; } // MountStatusSpec is the spec for MountStatus. message MountStatusSpec { MountRequestSpec spec = 1; string target = 2; string source = 3; talos.resource.definitions.enums.BlockFilesystemType filesystem = 4; bool read_only = 5; bool project_quota_support = 6; talos.resource.definitions.enums.BlockEncryptionProviderType encryption_provider = 7; bool detached = 8; } // ParameterSpec is a mount parameter. message ParameterSpec { talos.resource.definitions.enums.BlockFSParameterType type = 1; string name = 2; string string = 3; bytes binary = 5; } // PartitionSpec is the spec for volume partitioning. message PartitionSpec { uint64 min_size = 1; uint64 max_size = 2; bool grow = 3; string label = 4; string type_uuid = 5; uint64 relative_max_size = 6; bool negative_max_size = 7; } // ProvisioningSpec is the spec for volume provisioning. message ProvisioningSpec { DiskSelector disk_selector = 1; PartitionSpec partition_spec = 2; int64 wave = 3; FilesystemSpec filesystem_spec = 4; } // SwapStatusSpec is the spec for SwapStatuss resource. message SwapStatusSpec { string device = 1; uint64 size_bytes = 2; string size_human = 3; uint64 used_bytes = 4; string used_human = 5; int32 priority = 6; string type = 7; } // SymlinkProvisioningSpec is the spec for volume symlink. message SymlinkProvisioningSpec { string symlink_target_path = 1; bool force = 2; } // SymlinkSpec is the spec for Symlinks resource. message SymlinkSpec { repeated string paths = 1; } // SystemDiskSpec is the spec for SystemDisks resource. message SystemDiskSpec { string disk_id = 1; string dev_path = 2; } // TPMEncryptionOptionsInfo is the options for TPM-based encryption. message TPMEncryptionOptionsInfo { repeated int64 pc_rs = 1; repeated int64 pub_key_pc_rs = 2; } // UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus resource. message UserDiskConfigStatusSpec { bool ready = 1; bool torn_down = 2; } // VolumeConfigSpec is the spec for VolumeConfig resource. message VolumeConfigSpec { string parent_id = 1; talos.resource.definitions.enums.BlockVolumeType type = 2; ProvisioningSpec provisioning = 3; LocatorSpec locator = 4; MountSpec mount = 5; EncryptionSpec encryption = 6; SymlinkProvisioningSpec symlink = 7; } // VolumeMountRequestSpec is the spec for VolumeMountRequest. message VolumeMountRequestSpec { string volume_id = 1; string requester = 2; bool read_only = 3; bool detached = 4; bool disable_access_time = 5; bool secure = 6; } // VolumeMountStatusSpec is the spec for VolumeMountStatus. message VolumeMountStatusSpec { string volume_id = 1; string requester = 2; string target = 3; bool read_only = 4; bool detached = 5; bool disable_access_time = 6; bool secure = 7; } // VolumeStatusSpec is the spec for VolumeStatus resource. message VolumeStatusSpec { talos.resource.definitions.enums.BlockVolumePhase phase = 1; string location = 2; string error_message = 3; string uuid = 4; string partition_uuid = 5; talos.resource.definitions.enums.BlockVolumePhase pre_fail_phase = 6; string parent_location = 7; int64 partition_index = 8; uint64 size = 9; talos.resource.definitions.enums.BlockFilesystemType filesystem = 10; string mount_location = 11; talos.resource.definitions.enums.BlockEncryptionProviderType encryption_provider = 12; string pretty_size = 13; repeated string encryption_failed_syncs = 14; MountSpec mount_spec = 15; talos.resource.definitions.enums.BlockVolumeType type = 16; repeated string configured_encryption_keys = 17; SymlinkProvisioningSpec symlink_spec = 18; string parent_id = 19; bool encryption_locked_to_state = 20; int64 encryption_slot = 21; TPMEncryptionOptionsInfo tpm_encryption_options = 22; } // ZswapStatusSpec is the spec for ZswapStatus resource. message ZswapStatusSpec { uint64 total_size_bytes = 1; string total_size_human = 2; uint64 stored_pages = 3; uint64 pool_limit_hit = 4; uint64 reject_reclaim_fail = 5; uint64 reject_alloc_fail = 6; uint64 reject_kmemcache_fail = 7; uint64 reject_compress_fail = 8; uint64 reject_compress_poor = 9; uint64 written_back_pages = 10; } ================================================ FILE: api/resource/definitions/cluster/cluster.proto ================================================ syntax = "proto3"; package talos.resource.definitions.cluster; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cluster"; option java_package = "dev.talos.api.resource.definitions.cluster"; import "common/common.proto"; import "resource/definitions/enums/enums.proto"; // AffiliateSpec describes Affiliate state. message AffiliateSpec { string node_id = 1; repeated common.NetIP addresses = 2; string hostname = 3; string nodename = 4; string operating_system = 5; talos.resource.definitions.enums.MachineType machine_type = 6; KubeSpanAffiliateSpec kube_span = 7; ControlPlane control_plane = 8; } // ConfigSpec describes KubeSpan configuration. message ConfigSpec { bool discovery_enabled = 1; bool registry_kubernetes_enabled = 2; bool registry_service_enabled = 3; string service_endpoint = 4; bool service_endpoint_insecure = 5; bytes service_encryption_key = 6; string service_cluster_id = 7; } // ControlPlane describes ControlPlane data if any. message ControlPlane { int64 api_server_port = 1; } // IdentitySpec describes status of rendered secrets. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. message IdentitySpec { string node_id = 1; } // InfoSpec describes cluster information. message InfoSpec { string cluster_id = 1; string cluster_name = 2; } // KubeSpanAffiliateSpec describes additional information specific for the KubeSpan. message KubeSpanAffiliateSpec { string public_key = 1; common.NetIP address = 2; repeated common.NetIPPrefix additional_addresses = 3; repeated common.NetIPPort endpoints = 4; repeated common.NetIPPrefix exclude_advertised_networks = 5; } // MemberSpec describes Member state. message MemberSpec { string node_id = 1; repeated common.NetIP addresses = 2; string hostname = 3; talos.resource.definitions.enums.MachineType machine_type = 4; string operating_system = 5; ControlPlane control_plane = 6; } ================================================ FILE: api/resource/definitions/cri/cri.proto ================================================ syntax = "proto3"; package talos.resource.definitions.cri; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cri"; option java_package = "dev.talos.api.resource.definitions.cri"; import "common/common.proto"; import "google/protobuf/struct.proto"; import "resource/definitions/enums/enums.proto"; // ImageCacheConfigSpec represents the ImageCacheConfig. message ImageCacheConfigSpec { talos.resource.definitions.enums.CriImageCacheStatus status = 1; repeated string roots = 2; talos.resource.definitions.enums.CriImageCacheCopyStatus copy_status = 3; } // RegistriesConfigSpec describes status of rendered secrets. message RegistriesConfigSpec { map registry_mirrors = 1; map registry_auths = 2; map registry_tl_ss = 3; } // RegistryAuthConfig specifies authentication configuration for a registry. message RegistryAuthConfig { string registry_username = 1; string registry_password = 2; string registry_auth = 3; string registry_identity_token = 4; } // RegistryEndpointConfig represents a single registry endpoint. message RegistryEndpointConfig { string endpoint_endpoint = 1; bool endpoint_override_path = 2; } // RegistryMirrorConfig represents mirror configuration for a registry. message RegistryMirrorConfig { repeated RegistryEndpointConfig mirror_endpoints = 1; bool mirror_skip_fallback = 3; } // RegistryTLSConfig specifies TLS config for HTTPS registries. message RegistryTLSConfig { common.PEMEncodedCertificateAndKey tls_client_identity = 1; bytes tlsca = 2; bool tls_insecure_skip_verify = 3; } // SeccompProfileSpec represents the SeccompProfile. message SeccompProfileSpec { string name = 1; google.protobuf.Struct value = 2; } ================================================ FILE: api/resource/definitions/enums/enums.proto ================================================ syntax = "proto3"; package talos.resource.definitions.enums; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums"; option java_package = "dev.talos.api.resource.definitions.enums"; // RuntimeMachineStage describes the stage of the machine boot/run process. enum RuntimeMachineStage { MACHINE_STAGE_UNKNOWN = 0; MACHINE_STAGE_BOOTING = 1; MACHINE_STAGE_INSTALLING = 2; MACHINE_STAGE_MAINTENANCE = 3; MACHINE_STAGE_RUNNING = 4; MACHINE_STAGE_REBOOTING = 5; MACHINE_STAGE_SHUTTING_DOWN = 6; MACHINE_STAGE_RESETTING = 7; MACHINE_STAGE_UPGRADING = 8; } // RuntimeSELinuxState describes the current SELinux status. enum RuntimeSELinuxState { SE_LINUX_STATE_DISABLED = 0; SE_LINUX_STATE_PERMISSIVE = 1; SE_LINUX_STATE_ENFORCING = 2; } // RuntimeFIPSState describes the current FIPS status. enum RuntimeFIPSState { FIPS_STATE_DISABLED = 0; FIPS_STATE_ENABLED = 1; FIPS_STATE_STRICT = 2; } // MachineType represents a machine type. enum MachineType { // TypeUnknown represents undefined node type, when there is no machine configuration yet. TYPE_UNKNOWN = 0; // TypeInit type designates the first control plane node to come up. You can think of it like a bootstrap node. // This node will perform the initial steps to bootstrap the cluster -- generation of TLS assets, starting of the control plane, etc. TYPE_INIT = 1; // TypeControlPlane designates the node as a control plane member. // This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler. TYPE_CONTROL_PLANE = 2; // TypeWorker designates the node as a worker node. // This means it will be an available compute node for scheduling workloads. TYPE_WORKER = 3; } // NethelpersAddressFlag wraps IFF_* constants. enum NethelpersAddressFlag { NETHELPERS_ADDRESSFLAG_UNSPECIFIED = 0; ADDRESS_TEMPORARY = 1; ADDRESS_NO_DAD = 2; ADDRESS_OPTIMISTIC = 4; ADDRESS_DAD_FAILED = 8; ADDRESS_HOME = 16; ADDRESS_DEPRECATED = 32; ADDRESS_TENTATIVE = 64; ADDRESS_PERMANENT = 128; ADDRESS_MANAGEMENT_TEMP = 256; ADDRESS_NO_PREFIX_ROUTE = 512; ADDRESS_MC_AUTO_JOIN = 1024; ADDRESS_STABLE_PRIVACY = 2048; } // NethelpersAddressSortAlgorithm is an internal address sorting algorithm. enum NethelpersAddressSortAlgorithm { ADDRESS_SORT_ALGORITHM_V1 = 0; ADDRESS_SORT_ALGORITHM_V2 = 1; } // NethelpersADLACPActive is ADLACPActive. enum NethelpersADLACPActive { ADLACP_ACTIVE_OFF = 0; ADLACP_ACTIVE_ON = 1; } // NethelpersADSelect is ADSelect. enum NethelpersADSelect { AD_SELECT_STABLE = 0; AD_SELECT_BANDWIDTH = 1; AD_SELECT_COUNT = 2; } // NethelpersARPAllTargets is an ARP targets mode. enum NethelpersARPAllTargets { ARP_ALL_TARGETS_ANY = 0; ARP_ALL_TARGETS_ALL = 1; } // NethelpersARPValidate is an ARP Validation mode. enum NethelpersARPValidate { ARP_VALIDATE_NONE = 0; ARP_VALIDATE_ACTIVE = 1; ARP_VALIDATE_BACKUP = 2; ARP_VALIDATE_ALL = 3; ARP_VALIDATE_FILTER = 4; ARP_VALIDATE_FILTER_ACTIVE = 5; ARP_VALIDATE_FILTER_BACKUP = 6; } // NethelpersAutoHostnameKind is a kind of automatically generated hostname. enum NethelpersAutoHostnameKind { AUTO_HOSTNAME_KIND_OFF = 0; AUTO_HOSTNAME_KIND_ADDR = 1; AUTO_HOSTNAME_KIND_STABLE = 2; } // NethelpersBondMode is a bond mode. enum NethelpersBondMode { BOND_MODE_ROUNDROBIN = 0; BOND_MODE_ACTIVE_BACKUP = 1; BOND_MODE_XOR = 2; BOND_MODE_BROADCAST = 3; BOND_MODE8023_AD = 4; BOND_MODE_TLB = 5; BOND_MODE_ALB = 6; } // NethelpersBondXmitHashPolicy is a bond hash policy. enum NethelpersBondXmitHashPolicy { BOND_XMIT_POLICY_LAYER2 = 0; BOND_XMIT_POLICY_LAYER34 = 1; BOND_XMIT_POLICY_LAYER23 = 2; BOND_XMIT_POLICY_ENCAP23 = 3; BOND_XMIT_POLICY_ENCAP34 = 4; } // NethelpersClientIdentifier is a DHCP client identifier. enum NethelpersClientIdentifier { CLIENT_IDENTIFIER_NONE = 0; CLIENT_IDENTIFIER_MAC = 1; CLIENT_IDENTIFIER_DUID = 2; } // NethelpersConntrackState is a conntrack state. enum NethelpersConntrackState { NETHELPERS_CONNTRACKSTATE_UNSPECIFIED = 0; CONNTRACK_STATE_NEW = 8; CONNTRACK_STATE_RELATED = 4; CONNTRACK_STATE_ESTABLISHED = 2; CONNTRACK_STATE_INVALID = 1; } // NethelpersDuplex wraps ethtool.Duplex for YAML marshaling. enum NethelpersDuplex { HALF = 0; FULL = 1; UNKNOWN = 255; } // NethelpersFailOverMAC is a MAC failover mode. enum NethelpersFailOverMAC { FAIL_OVER_MAC_NONE = 0; FAIL_OVER_MAC_ACTIVE = 1; FAIL_OVER_MAC_FOLLOW = 2; } // NethelpersFamily is a network family. enum NethelpersFamily { NETHELPERS_FAMILY_UNSPECIFIED = 0; FAMILY_INET4 = 2; FAMILY_INET6 = 10; } // NethelpersICMPType is a ICMP packet type. enum NethelpersICMPType { NETHELPERS_ICMPTYPE_UNSPECIFIED = 0; ICMP_TYPE_TIMESTAMP_REQUEST = 13; ICMP_TYPE_TIMESTAMP_REPLY = 14; ICMP_TYPE_ADDRESS_MASK_REQUEST = 17; ICMP_TYPE_ADDRESS_MASK_REPLY = 18; } // NethelpersLACPRate is a LACP rate. enum NethelpersLACPRate { LACP_RATE_SLOW = 0; LACP_RATE_FAST = 1; } // NethelpersLinkType is a link type. enum NethelpersLinkType { option allow_alias = true; LINK_NETROM = 0; LINK_ETHER = 1; LINK_EETHER = 2; LINK_AX25 = 3; LINK_PRONET = 4; LINK_CHAOS = 5; LINK_IEE802 = 6; LINK_ARCNET = 7; LINK_ATALK = 8; LINK_DLCI = 15; LINK_ATM = 19; LINK_METRICOM = 23; LINK_IEEE1394 = 24; LINK_EUI64 = 27; LINK_INFINIBAND = 32; LINK_SLIP = 256; LINK_CSLIP = 257; LINK_SLIP6 = 258; LINK_CSLIP6 = 259; LINK_RSRVD = 260; LINK_ADAPT = 264; LINK_ROSE = 270; LINK_X25 = 271; LINK_HWX25 = 272; LINK_CAN = 280; LINK_PPP = 512; LINK_CISCO = 513; LINK_HDLC = 513; LINK_LAPB = 516; LINK_DDCMP = 517; LINK_RAWHDLC = 518; LINK_TUNNEL = 768; LINK_TUNNEL6 = 769; LINK_FRAD = 770; LINK_SKIP = 771; LINK_LOOPBCK = 772; LINK_LOCALTLK = 773; LINK_FDDI = 774; LINK_BIF = 775; LINK_SIT = 776; LINK_IPDDP = 777; LINK_IPGRE = 778; LINK_PIMREG = 779; LINK_HIPPI = 780; LINK_ASH = 781; LINK_ECONET = 782; LINK_IRDA = 783; LINK_FCPP = 784; LINK_FCAL = 785; LINK_FCPL = 786; LINK_FCFABRIC = 787; LINK_FCFABRIC1 = 788; LINK_FCFABRIC2 = 789; LINK_FCFABRIC3 = 790; LINK_FCFABRIC4 = 791; LINK_FCFABRIC5 = 792; LINK_FCFABRIC6 = 793; LINK_FCFABRIC7 = 794; LINK_FCFABRIC8 = 795; LINK_FCFABRIC9 = 796; LINK_FCFABRIC10 = 797; LINK_FCFABRIC11 = 798; LINK_FCFABRIC12 = 799; LINK_IEE802TR = 800; LINK_IEE80211 = 801; LINK_IEE80211PRISM = 802; LINK_IEE80211_RADIOTAP = 803; LINK_IEE8021154 = 804; LINK_IEE8021154MONITOR = 805; LINK_PHONET = 820; LINK_PHONETPIPE = 821; LINK_CAIF = 822; LINK_IP6GRE = 823; LINK_NETLINK = 824; LINK6_LOWPAN = 825; LINK_VOID = 65535; LINK_NONE = 65534; } // NethelpersMatchOperator is a netfilter match operator. enum NethelpersMatchOperator { OPERATOR_EQUAL = 0; OPERATOR_NOT_EQUAL = 1; } // NethelpersNfTablesChainHook wraps nftables.ChainHook for YAML marshaling. enum NethelpersNfTablesChainHook { CHAIN_HOOK_PREROUTING = 0; CHAIN_HOOK_INPUT = 1; CHAIN_HOOK_FORWARD = 2; CHAIN_HOOK_OUTPUT = 3; CHAIN_HOOK_POSTROUTING = 4; } // NethelpersNfTablesChainPriority wraps nftables.ChainPriority for YAML marshaling. enum NethelpersNfTablesChainPriority { option allow_alias = true; NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED = 0; CHAIN_PRIORITY_FIRST = -2147483648; CHAIN_PRIORITY_CONNTRACK_DEFRAG = -400; CHAIN_PRIORITY_RAW = -300; CHAIN_PRIORITY_SE_LINUX_FIRST = -225; CHAIN_PRIORITY_CONNTRACK = -200; CHAIN_PRIORITY_MANGLE = -150; CHAIN_PRIORITY_NAT_DEST = -100; CHAIN_PRIORITY_FILTER = 0; CHAIN_PRIORITY_SECURITY = 50; CHAIN_PRIORITY_NAT_SOURCE = 100; CHAIN_PRIORITY_SE_LINUX_LAST = 225; CHAIN_PRIORITY_CONNTRACK_HELPER = 300; CHAIN_PRIORITY_LAST = 2147483647; } // NethelpersNfTablesVerdict wraps nftables.Verdict for YAML marshaling. enum NethelpersNfTablesVerdict { VERDICT_DROP = 0; VERDICT_ACCEPT = 1; } // NethelpersOperationalState wraps rtnetlink.OperationalState for YAML marshaling. enum NethelpersOperationalState { OPER_STATE_UNKNOWN = 0; OPER_STATE_NOT_PRESENT = 1; OPER_STATE_DOWN = 2; OPER_STATE_LOWER_LAYER_DOWN = 3; OPER_STATE_TESTING = 4; OPER_STATE_DORMANT = 5; OPER_STATE_UP = 6; } // NethelpersPort wraps ethtool.Port for YAML marshaling. enum NethelpersPort { TWISTED_PAIR = 0; AUI = 1; MII = 2; FIBRE = 3; BNC = 4; DIRECT_ATTACH = 5; NONE = 239; OTHER = 255; } // NethelpersPrimaryReselect is an ARP targets mode. enum NethelpersPrimaryReselect { PRIMARY_RESELECT_ALWAYS = 0; PRIMARY_RESELECT_BETTER = 1; PRIMARY_RESELECT_FAILURE = 2; } // NethelpersProtocol is a inet protocol. enum NethelpersProtocol { NETHELPERS_PROTOCOL_UNSPECIFIED = 0; PROTOCOL_ICMP = 1; PROTOCOL_TCP = 6; PROTOCOL_UDP = 17; PROTOCOL_ICM_PV6 = 58; } // NethelpersRouteFlag wraps RTM_F_* constants. enum NethelpersRouteFlag { NETHELPERS_ROUTEFLAG_UNSPECIFIED = 0; ROUTE_NOTIFY = 256; ROUTE_CLONED = 512; ROUTE_EQUALIZE = 1024; ROUTE_PREFIX = 2048; ROUTE_LOOKUP_TABLE = 4096; ROUTE_FIB_MATCH = 8192; ROUTE_OFFLOAD = 16384; ROUTE_TRAP = 32768; } // NethelpersRouteProtocol is a routing protocol. enum NethelpersRouteProtocol { PROTOCOL_UNSPEC = 0; PROTOCOL_REDIRECT = 1; PROTOCOL_KERNEL = 2; PROTOCOL_BOOT = 3; PROTOCOL_STATIC = 4; PROTOCOL_RA = 9; PROTOCOL_MRT = 10; PROTOCOL_ZEBRA = 11; PROTOCOL_BIRD = 12; PROTOCOL_DNROUTED = 13; PROTOCOL_XORP = 14; PROTOCOL_NTK = 15; PROTOCOL_DHCP = 16; PROTOCOL_MRTD = 17; PROTOCOL_KEEPALIVED = 18; PROTOCOL_BABEL = 42; PROTOCOL_OPENR = 99; PROTOCOL_BGP = 186; PROTOCOL_ISIS = 187; PROTOCOL_OSPF = 188; PROTOCOL_RIP = 189; PROTOCOL_EIGRP = 192; } // NethelpersRouteType is a route type. enum NethelpersRouteType { TYPE_UNSPEC = 0; TYPE_UNICAST = 1; TYPE_LOCAL = 2; TYPE_BROADCAST = 3; TYPE_ANYCAST = 4; TYPE_MULTICAST = 5; TYPE_BLACKHOLE = 6; TYPE_UNREACHABLE = 7; TYPE_PROHIBIT = 8; TYPE_THROW = 9; TYPE_NAT = 10; TYPE_X_RESOLVE = 11; } // NethelpersRoutingRuleAction is a routing rule action. enum NethelpersRoutingRuleAction { ROUTING_RULE_ACTION_UNSPEC = 0; ROUTING_RULE_ACTION_UNICAST = 1; ROUTING_RULE_ACTION_BLACKHOLE = 6; ROUTING_RULE_ACTION_UNREACHABLE = 7; ROUTING_RULE_ACTION_PROHIBIT = 8; } // NethelpersRoutingTable is a routing table ID. enum NethelpersRoutingTable { TABLE_UNSPEC = 0; TABLE1 = 1; TABLE2 = 2; TABLE3 = 3; TABLE4 = 4; TABLE5 = 5; TABLE6 = 6; TABLE7 = 7; TABLE8 = 8; TABLE9 = 9; TABLE10 = 10; TABLE11 = 11; TABLE12 = 12; TABLE13 = 13; TABLE14 = 14; TABLE15 = 15; TABLE16 = 16; TABLE17 = 17; TABLE18 = 18; TABLE19 = 19; TABLE20 = 20; TABLE21 = 21; TABLE22 = 22; TABLE23 = 23; TABLE24 = 24; TABLE25 = 25; TABLE26 = 26; TABLE27 = 27; TABLE28 = 28; TABLE29 = 29; TABLE30 = 30; TABLE31 = 31; TABLE32 = 32; TABLE33 = 33; TABLE34 = 34; TABLE35 = 35; TABLE36 = 36; TABLE37 = 37; TABLE38 = 38; TABLE39 = 39; TABLE40 = 40; TABLE41 = 41; TABLE42 = 42; TABLE43 = 43; TABLE44 = 44; TABLE45 = 45; TABLE46 = 46; TABLE47 = 47; TABLE48 = 48; TABLE49 = 49; TABLE50 = 50; TABLE51 = 51; TABLE52 = 52; TABLE53 = 53; TABLE54 = 54; TABLE55 = 55; TABLE56 = 56; TABLE57 = 57; TABLE58 = 58; TABLE59 = 59; TABLE60 = 60; TABLE61 = 61; TABLE62 = 62; TABLE63 = 63; TABLE64 = 64; TABLE65 = 65; TABLE66 = 66; TABLE67 = 67; TABLE68 = 68; TABLE69 = 69; TABLE70 = 70; TABLE71 = 71; TABLE72 = 72; TABLE73 = 73; TABLE74 = 74; TABLE75 = 75; TABLE76 = 76; TABLE77 = 77; TABLE78 = 78; TABLE79 = 79; TABLE80 = 80; TABLE81 = 81; TABLE82 = 82; TABLE83 = 83; TABLE84 = 84; TABLE85 = 85; TABLE86 = 86; TABLE87 = 87; TABLE88 = 88; TABLE89 = 89; TABLE90 = 90; TABLE91 = 91; TABLE92 = 92; TABLE93 = 93; TABLE94 = 94; TABLE95 = 95; TABLE96 = 96; TABLE97 = 97; TABLE98 = 98; TABLE99 = 99; TABLE100 = 100; TABLE101 = 101; TABLE102 = 102; TABLE103 = 103; TABLE104 = 104; TABLE105 = 105; TABLE106 = 106; TABLE107 = 107; TABLE108 = 108; TABLE109 = 109; TABLE110 = 110; TABLE111 = 111; TABLE112 = 112; TABLE113 = 113; TABLE114 = 114; TABLE115 = 115; TABLE116 = 116; TABLE117 = 117; TABLE118 = 118; TABLE119 = 119; TABLE120 = 120; TABLE121 = 121; TABLE122 = 122; TABLE123 = 123; TABLE124 = 124; TABLE125 = 125; TABLE126 = 126; TABLE127 = 127; TABLE128 = 128; TABLE129 = 129; TABLE130 = 130; TABLE131 = 131; TABLE132 = 132; TABLE133 = 133; TABLE134 = 134; TABLE135 = 135; TABLE136 = 136; TABLE137 = 137; TABLE138 = 138; TABLE139 = 139; TABLE140 = 140; TABLE141 = 141; TABLE142 = 142; TABLE143 = 143; TABLE144 = 144; TABLE145 = 145; TABLE146 = 146; TABLE147 = 147; TABLE148 = 148; TABLE149 = 149; TABLE150 = 150; TABLE151 = 151; TABLE152 = 152; TABLE153 = 153; TABLE154 = 154; TABLE155 = 155; TABLE156 = 156; TABLE157 = 157; TABLE158 = 158; TABLE159 = 159; TABLE160 = 160; TABLE161 = 161; TABLE162 = 162; TABLE163 = 163; TABLE164 = 164; TABLE165 = 165; TABLE166 = 166; TABLE167 = 167; TABLE168 = 168; TABLE169 = 169; TABLE170 = 170; TABLE171 = 171; TABLE172 = 172; TABLE173 = 173; TABLE174 = 174; TABLE175 = 175; TABLE176 = 176; TABLE177 = 177; TABLE178 = 178; TABLE179 = 179; TABLE180 = 180; TABLE181 = 181; TABLE182 = 182; TABLE183 = 183; TABLE184 = 184; TABLE185 = 185; TABLE186 = 186; TABLE187 = 187; TABLE188 = 188; TABLE189 = 189; TABLE190 = 190; TABLE191 = 191; TABLE192 = 192; TABLE193 = 193; TABLE194 = 194; TABLE195 = 195; TABLE196 = 196; TABLE197 = 197; TABLE198 = 198; TABLE199 = 199; TABLE200 = 200; TABLE201 = 201; TABLE202 = 202; TABLE203 = 203; TABLE204 = 204; TABLE205 = 205; TABLE206 = 206; TABLE207 = 207; TABLE208 = 208; TABLE209 = 209; TABLE210 = 210; TABLE211 = 211; TABLE212 = 212; TABLE213 = 213; TABLE214 = 214; TABLE215 = 215; TABLE216 = 216; TABLE217 = 217; TABLE218 = 218; TABLE219 = 219; TABLE220 = 220; TABLE221 = 221; TABLE222 = 222; TABLE223 = 223; TABLE224 = 224; TABLE225 = 225; TABLE226 = 226; TABLE227 = 227; TABLE228 = 228; TABLE229 = 229; TABLE230 = 230; TABLE231 = 231; TABLE232 = 232; TABLE233 = 233; TABLE234 = 234; TABLE235 = 235; TABLE236 = 236; TABLE237 = 237; TABLE238 = 238; TABLE239 = 239; TABLE240 = 240; TABLE241 = 241; TABLE242 = 242; TABLE243 = 243; TABLE244 = 244; TABLE245 = 245; TABLE246 = 246; TABLE247 = 247; TABLE248 = 248; TABLE249 = 249; TABLE250 = 250; TABLE251 = 251; TABLE252 = 252; TABLE_DEFAULT = 253; TABLE_MAIN = 254; TABLE_LOCAL = 255; } // NethelpersScope is an address scope. enum NethelpersScope { SCOPE_GLOBAL = 0; SCOPE_SITE = 200; SCOPE_LINK = 253; SCOPE_HOST = 254; SCOPE_NOWHERE = 255; } // NethelpersVLANProtocol is a VLAN protocol. enum NethelpersVLANProtocol { NETHELPERS_VLANPROTOCOL_UNSPECIFIED = 0; VLAN_PROTOCOL8021_Q = 33024; VLAN_PROTOCOL8021_AD = 34984; } // NethelpersWOLMode wraps ethtool.WOLMode for YAML marshaling. enum NethelpersWOLMode { NETHELPERS_WOLMODE_UNSPECIFIED = 0; WOL_MODE_PHY = 1; WOL_MODE_UNICAST = 2; WOL_MODE_MULTICAST = 4; WOL_MODE_BROADCAST = 8; WOL_MODE_MAGIC = 32; WOL_MODE_MAGIC_SECURE = 64; WOL_MODE_FILTER = 128; } // BlockEncryptionKeyType describes encryption key type. enum BlockEncryptionKeyType { ENCRYPTION_KEY_STATIC = 0; ENCRYPTION_KEY_NODE_ID = 1; ENCRYPTION_KEY_KMS = 2; ENCRYPTION_KEY_TPM = 3; } // BlockEncryptionProviderType describes encryption provider type. enum BlockEncryptionProviderType { ENCRYPTION_PROVIDER_NONE = 0; ENCRYPTION_PROVIDER_LUKS2 = 1; } // BlockFilesystemType describes filesystem type. enum BlockFilesystemType { FILESYSTEM_TYPE_NONE = 0; FILESYSTEM_TYPE_XFS = 1; FILESYSTEM_TYPE_VFAT = 2; FILESYSTEM_TYPE_EXT4 = 3; FILESYSTEM_TYPE_ISO9660 = 4; FILESYSTEM_TYPE_SWAP = 5; FILESYSTEM_TYPE_VIRTIOFS = 6; } // BlockFSParameterType describes Filesystem Parameter type. enum BlockFSParameterType { FS_PARAMETER_TYPE_STRING_VALUE = 0; FS_PARAMETER_TYPE_BOOLEAN_VALUE = 1; FS_PARAMETER_TYPE_BINARY_VALUE = 2; } // BlockVolumePhase describes volume phase. enum BlockVolumePhase { VOLUME_PHASE_WAITING = 0; VOLUME_PHASE_FAILED = 1; VOLUME_PHASE_MISSING = 2; VOLUME_PHASE_LOCATED = 3; VOLUME_PHASE_PROVISIONED = 4; VOLUME_PHASE_PREPARED = 5; VOLUME_PHASE_READY = 6; VOLUME_PHASE_CLOSED = 7; } // BlockVolumeType describes volume type. enum BlockVolumeType { VOLUME_TYPE_PARTITION = 0; VOLUME_TYPE_DISK = 1; VOLUME_TYPE_TMPFS = 2; VOLUME_TYPE_DIRECTORY = 3; VOLUME_TYPE_SYMLINK = 4; VOLUME_TYPE_OVERLAY = 5; VOLUME_TYPE_EXTERNAL = 6; } // CriImageCacheStatus describes image cache status type. enum CriImageCacheStatus { IMAGE_CACHE_STATUS_UNKNOWN = 0; IMAGE_CACHE_STATUS_DISABLED = 1; IMAGE_CACHE_STATUS_PREPARING = 2; IMAGE_CACHE_STATUS_READY = 3; } // CriImageCacheCopyStatus describes image cache copy status type. enum CriImageCacheCopyStatus { IMAGE_CACHE_COPY_STATUS_UNKNOWN = 0; IMAGE_CACHE_COPY_STATUS_SKIPPED = 1; IMAGE_CACHE_COPY_STATUS_PENDING = 2; IMAGE_CACHE_COPY_STATUS_READY = 3; } // KubespanPeerState is KubeSpan peer current state. enum KubespanPeerState { PEER_STATE_UNKNOWN = 0; PEER_STATE_UP = 1; PEER_STATE_DOWN = 2; } // NetworkConfigLayer describes network configuration layers, with lowest priority first. enum NetworkConfigLayer { CONFIG_DEFAULT = 0; CONFIG_CMDLINE = 1; CONFIG_PLATFORM = 2; CONFIG_OPERATOR = 3; CONFIG_MACHINE_CONFIGURATION = 4; } // NetworkOperator enumerates Talos network operators. enum NetworkOperator { OPERATOR_DHCP4 = 0; OPERATOR_DHCP6 = 1; OPERATOR_VIP = 2; } ================================================ FILE: api/resource/definitions/etcd/etcd.proto ================================================ syntax = "proto3"; package talos.resource.definitions.etcd; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/etcd"; option java_package = "dev.talos.api.resource.definitions.etcd"; import "common/common.proto"; // ArgValues represents values for a command line argument which can be specified multiple times. message ArgValues { repeated string values = 1; } // ConfigSpec describes (some) configuration settings of etcd. message ConfigSpec { repeated string advertise_valid_subnets = 1; repeated string advertise_exclude_subnets = 2; string image = 3; map extra_args = 4; repeated string listen_valid_subnets = 5; repeated string listen_exclude_subnets = 6; } // MemberSpec holds information about an etcd member. message MemberSpec { string member_id = 1; } // PKIStatusSpec describes status of rendered secrets. message PKIStatusSpec { bool ready = 1; string version = 2; } // SpecSpec describes (some) Specuration settings of etcd. message SpecSpec { string name = 1; repeated common.NetIP advertised_addresses = 2; string image = 3; map extra_args = 4; repeated common.NetIP listen_peer_addresses = 5; repeated common.NetIP listen_client_addresses = 6; } ================================================ FILE: api/resource/definitions/extensions/extensions.proto ================================================ syntax = "proto3"; package talos.resource.definitions.extensions; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/extensions"; option java_package = "dev.talos.api.resource.definitions.extensions"; // Compatibility describes extension compatibility. message Compatibility { Constraint talos = 1; } // Constraint describes compatibility constraint. message Constraint { string version = 1; } // Layer defines overlay mount layer. message Layer { string image = 1; Metadata metadata = 2; } // Metadata describes base extension metadata. message Metadata { string name = 1; string version = 2; string author = 3; string description = 4; Compatibility compatibility = 5; string extra_info = 6; } ================================================ FILE: api/resource/definitions/files/files.proto ================================================ syntax = "proto3"; package talos.resource.definitions.files; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/files"; option java_package = "dev.talos.api.resource.definitions.files"; // EtcFileSpecSpec describes status of rendered secrets. message EtcFileSpecSpec { bytes contents = 1; uint32 mode = 2; string selinux_label = 3; } // EtcFileStatusSpec describes status of rendered secrets. message EtcFileStatusSpec { string spec_version = 1; } ================================================ FILE: api/resource/definitions/hardware/hardware.proto ================================================ syntax = "proto3"; package talos.resource.definitions.hardware; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/hardware"; option java_package = "dev.talos.api.resource.definitions.hardware"; // MemoryModuleSpec represents a single Memory. message MemoryModuleSpec { uint32 size = 1; string device_locator = 2; string bank_locator = 3; uint32 speed = 4; string manufacturer = 5; string serial_number = 6; string asset_tag = 7; string product_name = 8; } // PCIDeviceSpec represents a single processor. message PCIDeviceSpec { string class = 1; string subclass = 2; string vendor = 3; string product = 4; string class_id = 5; string subclass_id = 6; string vendor_id = 7; string product_id = 8; string driver = 9; } // PCIDriverRebindConfigSpec describes PCI rebind configuration. message PCIDriverRebindConfigSpec { string pciid = 1; string target_driver = 2; } // PCIDriverRebindStatusSpec describes status of rebinded drivers. message PCIDriverRebindStatusSpec { string pciid = 1; string target_driver = 2; } // ProcessorSpec represents a single processor. message ProcessorSpec { string socket = 1; string manufacturer = 2; string product_name = 3; uint32 max_speed = 4; uint32 boot_speed = 5; uint32 status = 6; string serial_number = 7; string asset_tag = 8; string part_number = 9; uint32 core_count = 10; uint32 core_enabled = 11; uint32 thread_count = 12; } // SystemInformationSpec represents the system information obtained from smbios. message SystemInformationSpec { string manufacturer = 1; string product_name = 2; string version = 3; string serial_number = 4; string uuid = 5; string wake_up_type = 6; string sku_number = 7; } ================================================ FILE: api/resource/definitions/k8s/k8s.proto ================================================ syntax = "proto3"; package talos.resource.definitions.k8s; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/k8s"; option java_package = "dev.talos.api.resource.definitions.k8s"; import "common/common.proto"; import "google/protobuf/struct.proto"; import "resource/definitions/proto/proto.proto"; // APIServerConfigSpec is configuration for kube-apiserver. message APIServerConfigSpec { string image = 1; string cloud_provider = 2; string control_plane_endpoint = 3; repeated string etcd_servers = 4; int64 local_port = 5; repeated string service_cid_rs = 6; map extra_args = 7; repeated ExtraVolume extra_volumes = 8; map environment_variables = 9; string advertised_address = 11; Resources resources = 12; } // AdmissionControlConfigSpec is configuration for kube-apiserver. message AdmissionControlConfigSpec { repeated AdmissionPluginSpec config = 1; } // AdmissionPluginSpec is a single admission plugin configuration Admission Control plugins. message AdmissionPluginSpec { string name = 1; google.protobuf.Struct configuration = 2; } // ArgValues represents values for a command line argument which can be specified multiple times. message ArgValues { repeated string values = 1; } // AuditPolicyConfigSpec is audit policy configuration for kube-apiserver. message AuditPolicyConfigSpec { google.protobuf.Struct config = 1; } // AuthorizationAuthorizersSpec is a configuration of authorization authorizers. message AuthorizationAuthorizersSpec { string type = 1; string name = 2; google.protobuf.Struct webhook = 3; } // AuthorizationConfigSpec is authorization configuration for kube-apiserver. message AuthorizationConfigSpec { string image = 1; repeated AuthorizationAuthorizersSpec config = 2; } // BootstrapManifestsConfigSpec is configuration for bootstrap manifests. message BootstrapManifestsConfigSpec { string server = 1; string cluster_domain = 2; repeated string pod_cid_rs = 3; bool proxy_enabled = 4; string proxy_image = 5; repeated string proxy_args = 6; bool core_dns_enabled = 7; string core_dns_image = 8; string dns_service_ip = 9; string dns_service_i_pv6 = 10; bool flannel_enabled = 11; string flannel_image = 12; bool pod_security_policy_enabled = 14; bool talos_api_service_enabled = 15; repeated string flannel_extra_args = 16; string flannel_kube_service_host = 17; string flannel_kube_service_port = 18; bool flannel_kube_network_policies_enabled = 19; string flannel_kube_network_policies_image = 20; string cni_name = 21; } // ConfigStatusSpec describes status of rendered secrets. message ConfigStatusSpec { bool ready = 1; string version = 2; } // ControllerManagerConfigSpec is configuration for kube-controller-manager. message ControllerManagerConfigSpec { bool enabled = 1; string image = 2; string cloud_provider = 3; repeated string pod_cid_rs = 4; repeated string service_cid_rs = 5; map extra_args = 6; repeated ExtraVolume extra_volumes = 7; map environment_variables = 8; Resources resources = 9; } // EndpointSpec describes a list of endpoints to connect to. message EndpointSpec { repeated common.NetIP addresses = 1; repeated string hosts = 2; } // ExtraManifest defines a single extra manifest to download. message ExtraManifest { string name = 1; string url = 2; string priority = 3; map extra_headers = 4; string inline_manifest = 5; } // ExtraManifestsConfigSpec is configuration for extra bootstrap manifests. message ExtraManifestsConfigSpec { repeated ExtraManifest extra_manifests = 1; } // ExtraVolume is a configuration of extra volume. message ExtraVolume { string name = 1; string host_path = 2; string mount_path = 3; bool read_only = 4; } // KubePrismConfigSpec describes KubePrismConfig data. message KubePrismConfigSpec { string host = 1; int64 port = 2; repeated KubePrismEndpoint endpoints = 3; } // KubePrismEndpoint holds data for control plane endpoint. message KubePrismEndpoint { string host = 1; uint32 port = 2; } // KubePrismEndpointsSpec describes KubePrismEndpoints configuration. message KubePrismEndpointsSpec { repeated KubePrismEndpoint endpoints = 1; } // KubePrismStatusesSpec describes KubePrismStatuses data. message KubePrismStatusesSpec { string host = 1; bool healthy = 2; } // KubeletConfigSpec holds the source of kubelet configuration. message KubeletConfigSpec { string image = 1; repeated string cluster_dns = 2; string cluster_domain = 3; map extra_args = 4; repeated talos.resource.definitions.proto.Mount extra_mounts = 5; google.protobuf.Struct extra_config = 6; bool cloud_provider_external = 7; bool default_runtime_seccomp_enabled = 8; bool skip_node_registration = 9; string static_pod_list_url = 10; bool disable_manifests_directory = 11; bool enable_fs_quota_monitoring = 12; google.protobuf.Struct credential_provider_config = 13; bool allow_scheduling_on_control_plane = 14; } // KubeletSpecSpec holds the source of kubelet configuration. message KubeletSpecSpec { string image = 1; repeated string args = 2; repeated talos.resource.definitions.proto.Mount extra_mounts = 3; string expected_nodename = 4; google.protobuf.Struct config = 5; google.protobuf.Struct credential_provider_config = 6; } // ManifestSpec holds the Kubernetes resources spec. message ManifestSpec { repeated SingleManifest items = 1; } // ManifestStatusSpec describes manifest application status. message ManifestStatusSpec { repeated string manifests_applied = 1; } // NodeAnnotationSpecSpec represents an annoation that's attached to a Talos node. message NodeAnnotationSpecSpec { string key = 1; string value = 2; } // NodeIPConfigSpec holds the Node IP specification. message NodeIPConfigSpec { repeated string valid_subnets = 1; repeated string exclude_subnets = 2; } // NodeIPSpec holds the Node IP specification. message NodeIPSpec { repeated common.NetIP addresses = 1; } // NodeLabelSpecSpec represents a label that's attached to a Talos node. message NodeLabelSpecSpec { string key = 1; string value = 2; } // NodeStatusSpec describes Kubernetes NodeStatus. message NodeStatusSpec { string nodename = 1; bool node_ready = 2; bool unschedulable = 3; map labels = 4; map annotations = 5; repeated common.NetIPPrefix pod_cid_rs = 6; } // NodeTaintSpecSpec represents a label that's attached to a Talos node. message NodeTaintSpecSpec { string key = 1; string effect = 2; string value = 3; } // NodenameSpec describes Kubernetes nodename. message NodenameSpec { string nodename = 1; string hostname_version = 2; bool skip_node_registration = 3; } // Resources is a configuration of cpu and memory resources. message Resources { map requests = 1; map limits = 2; } // SchedulerConfigSpec is configuration for kube-scheduler. message SchedulerConfigSpec { bool enabled = 1; string image = 2; map extra_args = 3; repeated ExtraVolume extra_volumes = 4; map environment_variables = 5; Resources resources = 6; google.protobuf.Struct config = 7; } // SecretsStatusSpec describes status of rendered secrets. message SecretsStatusSpec { bool ready = 1; string version = 2; } // SingleManifest is a single manifest. message SingleManifest { google.protobuf.Struct object = 1; } // StaticPodServerStatusSpec describes static pod spec, it contains marshaled *v1.Pod spec. message StaticPodServerStatusSpec { string url = 1; } // StaticPodSpec describes static pod spec, it contains marshaled *v1.Pod spec. message StaticPodSpec { google.protobuf.Struct pod = 1; } // StaticPodStatusSpec describes kubelet static pod status. message StaticPodStatusSpec { google.protobuf.Struct pod_status = 1; } ================================================ FILE: api/resource/definitions/kubeaccess/kubeaccess.proto ================================================ syntax = "proto3"; package talos.resource.definitions.kubeaccess; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/kubeaccess"; option java_package = "dev.talos.api.resource.definitions.kubeaccess"; // ConfigSpec describes KubeSpan configuration.. message ConfigSpec { bool enabled = 1; repeated string allowed_api_roles = 2; repeated string allowed_kubernetes_namespaces = 3; } ================================================ FILE: api/resource/definitions/kubespan/kubespan.proto ================================================ syntax = "proto3"; package talos.resource.definitions.kubespan; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/kubespan"; option java_package = "dev.talos.api.resource.definitions.kubespan"; import "common/common.proto"; import "google/protobuf/timestamp.proto"; import "resource/definitions/enums/enums.proto"; // ConfigSpec describes KubeSpan configuration.. message ConfigSpec { bool enabled = 1; string cluster_id = 2; string shared_secret = 3; bool force_routing = 4; bool advertise_kubernetes_networks = 5; uint32 mtu = 6; repeated string endpoint_filters = 7; bool harvest_extra_endpoints = 8; repeated common.NetIPPort extra_endpoints = 9; repeated common.NetIPPrefix exclude_advertised_networks = 10; } // EndpointSpec describes Endpoint state. message EndpointSpec { string affiliate_id = 1; common.NetIPPort endpoint = 2; } // IdentitySpec describes KubeSpan keys and address. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. message IdentitySpec { common.NetIPPrefix address = 1; common.NetIPPrefix subnet = 2; string private_key = 3; string public_key = 4; } // PeerSpecSpec describes PeerSpec state. message PeerSpecSpec { common.NetIP address = 1; repeated common.NetIPPrefix allowed_ips = 2; repeated common.NetIPPort endpoints = 3; string label = 4; } // PeerStatusSpec describes PeerStatus state. message PeerStatusSpec { common.NetIPPort endpoint = 1; string label = 2; talos.resource.definitions.enums.KubespanPeerState state = 3; int64 receive_bytes = 4; int64 transmit_bytes = 5; google.protobuf.Timestamp last_handshake_time = 6; common.NetIPPort last_used_endpoint = 7; google.protobuf.Timestamp last_endpoint_change = 8; } ================================================ FILE: api/resource/definitions/network/network.proto ================================================ syntax = "proto3"; package talos.resource.definitions.network; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/network"; option java_package = "dev.talos.api.resource.definitions.network"; import "common/common.proto"; import "google/protobuf/duration.proto"; import "resource/definitions/enums/enums.proto"; import "resource/definitions/runtime/runtime.proto"; // AddressSpecSpec describes status of rendered secrets. message AddressSpecSpec { common.NetIPPrefix address = 1; string link_name = 2; talos.resource.definitions.enums.NethelpersFamily family = 3; talos.resource.definitions.enums.NethelpersScope scope = 4; uint32 flags = 5; bool announce_with_arp = 6; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 7; uint32 priority = 8; } // AddressStatusSpec describes status of rendered secrets. message AddressStatusSpec { common.NetIPPrefix address = 1; common.NetIP local = 2; common.NetIP broadcast = 3; common.NetIP anycast = 4; common.NetIP multicast = 5; uint32 link_index = 6; string link_name = 7; talos.resource.definitions.enums.NethelpersFamily family = 8; talos.resource.definitions.enums.NethelpersScope scope = 9; uint32 flags = 10; uint32 priority = 11; } // BondMasterSpec describes bond settings if Kind == "bond". message BondMasterSpec { talos.resource.definitions.enums.NethelpersBondMode mode = 1; talos.resource.definitions.enums.NethelpersBondXmitHashPolicy hash_policy = 2; talos.resource.definitions.enums.NethelpersLACPRate lacp_rate = 3; talos.resource.definitions.enums.NethelpersARPValidate arp_validate = 4; talos.resource.definitions.enums.NethelpersARPAllTargets arp_all_targets = 5; uint32 primary_index = 6; talos.resource.definitions.enums.NethelpersPrimaryReselect primary_reselect = 7; talos.resource.definitions.enums.NethelpersFailOverMAC fail_over_mac = 8; talos.resource.definitions.enums.NethelpersADSelect ad_select = 9; uint32 mii_mon = 10; uint32 up_delay = 11; uint32 down_delay = 12; uint32 arp_interval = 13; uint32 resend_igmp = 14; uint32 min_links = 15; uint32 lp_interval = 16; uint32 packets_per_slave = 17; uint32 num_peer_notif = 18; uint32 tlb_dynamic_lb = 19; uint32 all_slaves_active = 20; bool use_carrier = 21; uint32 ad_actor_sys_prio = 22; uint32 ad_user_port_key = 23; uint32 peer_notify_delay = 24; repeated common.NetIP arpip_targets = 25; repeated common.NetIP nsip6_targets = 26; talos.resource.definitions.enums.NethelpersADLACPActive adlacp_active = 27; uint32 missed_max = 28; } // BondSlave contains a bond's master name and slave index. message BondSlave { string master_name = 1; int64 slave_index = 2; } // BridgeMasterSpec describes bridge settings if Kind == "bridge". message BridgeMasterSpec { STPSpec stp = 1; BridgeVLANSpec vlan = 2; } // BridgeSlave contains the name of the master bridge of a bridged interface message BridgeSlave { string master_name = 1; } // BridgeVLANSpec describes VLAN settings of a bridge. message BridgeVLANSpec { bool filtering_enabled = 1; } // ClientIdentifierSpec is a shared DHCP4/DHCP6 client identifier spec. message ClientIdentifierSpec { talos.resource.definitions.enums.NethelpersClientIdentifier client_identifier = 1; string duid_raw_hex = 2; } // DHCP4OperatorSpec describes DHCP4 operator options. message DHCP4OperatorSpec { uint32 route_metric = 1; bool skip_hostname_request = 2; ClientIdentifierSpec client_identifier = 3; } // DHCP6OperatorSpec describes DHCP6 operator options. message DHCP6OperatorSpec { uint32 route_metric = 2; bool skip_hostname_request = 3; ClientIdentifierSpec client_identifier = 4; } // DNSResolveCacheSpec describes DNS servers status. message DNSResolveCacheSpec { string status = 1; } // EthernetChannelsSpec describes config of Ethernet channels. message EthernetChannelsSpec { uint32 rx = 1; uint32 tx = 2; uint32 other = 3; uint32 combined = 4; } // EthernetChannelsStatus describes status of Ethernet channels. message EthernetChannelsStatus { uint32 rx_max = 1; uint32 tx_max = 2; uint32 other_max = 3; uint32 combined_max = 4; uint32 rx = 5; uint32 tx = 6; uint32 other = 7; uint32 combined = 8; } // EthernetFeatureStatus describes status of Ethernet features. message EthernetFeatureStatus { string name = 1; string status = 2; } // EthernetRingsSpec describes config of Ethernet rings. message EthernetRingsSpec { uint32 rx = 1; uint32 rx_mini = 2; uint32 rx_jumbo = 3; uint32 tx = 4; uint32 rx_buf_len = 5; uint32 cqe_size = 6; bool tx_push = 7; bool rx_push = 8; uint32 tx_push_buf_len = 9; bool tcp_data_split = 10; } // EthernetRingsStatus describes status of Ethernet rings. message EthernetRingsStatus { uint32 rx_max = 1; uint32 rx_mini_max = 2; uint32 rx_jumbo_max = 3; uint32 tx_max = 4; uint32 tx_push_buf_len_max = 5; uint32 rx = 6; uint32 rx_mini = 7; uint32 rx_jumbo = 8; uint32 tx = 9; uint32 rx_buf_len = 10; uint32 cqe_size = 11; bool tx_push = 12; bool rx_push = 13; uint32 tx_push_buf_len = 14; bool tcp_data_split = 15; } // EthernetSpecSpec describes config of Ethernet link. message EthernetSpecSpec { EthernetRingsSpec rings = 1; map features = 2; EthernetChannelsSpec channels = 3; repeated talos.resource.definitions.enums.NethelpersWOLMode wake_on_lan = 4; } // EthernetStatusSpec describes status of rendered secrets. message EthernetStatusSpec { bool link_state = 1; int64 speed_megabits = 2; talos.resource.definitions.enums.NethelpersPort port = 3; talos.resource.definitions.enums.NethelpersDuplex duplex = 4; repeated string our_modes = 5; repeated string peer_modes = 6; EthernetRingsStatus rings = 7; repeated EthernetFeatureStatus features = 8; EthernetChannelsStatus channels = 9; repeated talos.resource.definitions.enums.NethelpersWOLMode wake_on_lan = 10; } // HardwareAddrSpec describes spec for the link. message HardwareAddrSpec { string name = 1; bytes hardware_addr = 2; } // HostDNSConfigSpec describes host DNS config. message HostDNSConfigSpec { bool enabled = 1; repeated common.NetIPPort listen_addresses = 2; common.NetIP service_host_dns_address = 3; bool resolve_member_names = 4; } // HostnameSpecSpec describes node hostname. message HostnameSpecSpec { string hostname = 1; string domainname = 2; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 3; } // HostnameStatusSpec describes node hostname. message HostnameStatusSpec { string hostname = 1; string domainname = 2; } // LinkAliasSpecSpec describes status of rendered secrets. message LinkAliasSpecSpec { string alias = 1; } // LinkRefreshSpec describes status of rendered secrets. message LinkRefreshSpec { int64 generation = 1; } // LinkSpecSpec describes spec for the link. message LinkSpecSpec { string name = 1; bool logical = 2; bool up = 3; uint32 mtu = 4; string kind = 5; talos.resource.definitions.enums.NethelpersLinkType type = 6; string parent_name = 7; BondSlave bond_slave = 8; BridgeSlave bridge_slave = 9; VLANSpec vlan = 10; BondMasterSpec bond_master = 11; BridgeMasterSpec bridge_master = 12; WireguardSpec wireguard = 13; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 14; bytes hardware_address = 15; bool multicast = 16; VRFMasterSpec vrf_master = 17; VRFSlave vrf_slave = 18; } // LinkStatusSpec describes status of rendered secrets. message LinkStatusSpec { uint32 index = 1; talos.resource.definitions.enums.NethelpersLinkType type = 2; uint32 link_index = 3; uint32 flags = 4; bytes hardware_addr = 5; bytes broadcast_addr = 6; uint32 mtu = 7; string queue_disc = 8; uint32 master_index = 9; talos.resource.definitions.enums.NethelpersOperationalState operational_state = 10; string kind = 11; string slave_kind = 12; string bus_path = 13; string pciid = 14; string driver = 15; string driver_version = 16; string firmware_version = 17; string product_id = 18; string vendor_id = 19; string product = 20; string vendor = 21; bool link_state = 22; int64 speed_megabits = 23; talos.resource.definitions.enums.NethelpersPort port = 24; talos.resource.definitions.enums.NethelpersDuplex duplex = 25; VLANSpec vlan = 26; BridgeMasterSpec bridge_master = 27; BondMasterSpec bond_master = 28; WireguardSpec wireguard = 29; bytes permanent_addr = 30; string alias = 31; repeated string alt_names = 32; VRFMasterSpec vrf_master = 33; } // NfTablesAddressMatch describes the match on the IP address. message NfTablesAddressMatch { repeated common.NetIPPrefix include_subnets = 1; repeated common.NetIPPrefix exclude_subnets = 2; bool invert = 3; } // NfTablesChainSpec describes status of rendered secrets. message NfTablesChainSpec { string type = 1; talos.resource.definitions.enums.NethelpersNfTablesChainHook hook = 2; talos.resource.definitions.enums.NethelpersNfTablesChainPriority priority = 3; repeated NfTablesRule rules = 4; talos.resource.definitions.enums.NethelpersNfTablesVerdict policy = 5; } // NfTablesClampMSS describes the TCP MSS clamping operation. // // MSS is limited by the `MaxMTU` so that: // - IPv4: MSS = MaxMTU - 40 // - IPv6: MSS = MaxMTU - 60. message NfTablesClampMSS { uint32 mtu = 1; } // NfTablesConntrackStateMatch describes the match on the connection tracking state. message NfTablesConntrackStateMatch { repeated talos.resource.definitions.enums.NethelpersConntrackState states = 1; } // NfTablesICMPTypeMatch describes the match on the ICMP type. message NfTablesICMPTypeMatch { repeated talos.resource.definitions.enums.NethelpersICMPType types = 1; } // NfTablesIfNameMatch describes the match on the interface name. message NfTablesIfNameMatch { talos.resource.definitions.enums.NethelpersMatchOperator operator = 2; repeated string interface_names = 3; } // NfTablesLayer4Match describes the match on the transport layer protocol. message NfTablesLayer4Match { talos.resource.definitions.enums.NethelpersProtocol protocol = 1; NfTablesPortMatch match_source_port = 2; NfTablesPortMatch match_destination_port = 3; NfTablesICMPTypeMatch match_icmp_type = 4; } // NfTablesLimitMatch describes the match on the packet rate. message NfTablesLimitMatch { uint64 packet_rate_per_second = 1; } // NfTablesMark encodes packet mark match/update operation. // // When used as a match computes the following condition: // (mark & mask) ^ xor == value // // When used as an update computes the following operation: // mark = (mark & mask) ^ xor. message NfTablesMark { uint32 mask = 1; uint32 xor = 2; uint32 value = 3; } // NfTablesPortMatch describes the match on the transport layer port. message NfTablesPortMatch { repeated PortRange ranges = 1; } // NfTablesRule describes a single rule in the nftables chain. message NfTablesRule { NfTablesIfNameMatch match_o_if_name = 1; talos.resource.definitions.enums.NethelpersNfTablesVerdict verdict = 2; NfTablesMark match_mark = 3; NfTablesMark set_mark = 4; NfTablesAddressMatch match_source_address = 5; NfTablesAddressMatch match_destination_address = 6; NfTablesLayer4Match match_layer4 = 7; NfTablesIfNameMatch match_i_if_name = 8; NfTablesClampMSS clamp_mss = 9; NfTablesLimitMatch match_limit = 10; NfTablesConntrackStateMatch match_conntrack_state = 11; bool anon_counter = 12; } // NodeAddressFilterSpec describes a filter for NodeAddresses. message NodeAddressFilterSpec { repeated common.NetIPPrefix include_subnets = 1; repeated common.NetIPPrefix exclude_subnets = 2; } // NodeAddressSortAlgorithmSpec describes a filter for NodeAddresses. message NodeAddressSortAlgorithmSpec { talos.resource.definitions.enums.NethelpersAddressSortAlgorithm algorithm = 1; } // NodeAddressSpec describes a set of node addresses. message NodeAddressSpec { repeated common.NetIPPrefix addresses = 1; talos.resource.definitions.enums.NethelpersAddressSortAlgorithm sort_algorithm = 2; } // OperatorSpecSpec describes operator specification. message OperatorSpecSpec { talos.resource.definitions.enums.NetworkOperator operator = 1; string link_name = 2; bool require_up = 3; DHCP4OperatorSpec dhcp4 = 4; DHCP6OperatorSpec dhcp6 = 5; VIPOperatorSpec vip = 6; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 7; } // PlatformConfigSpec describes platform network configuration. // // This structure is marshaled to STATE partition to persist cached network configuration across // reboots. message PlatformConfigSpec { repeated AddressSpecSpec addresses = 1; repeated LinkSpecSpec links = 2; repeated RouteSpecSpec routes = 3; repeated HostnameSpecSpec hostnames = 4; repeated ResolverSpecSpec resolvers = 5; repeated TimeServerSpecSpec time_servers = 6; repeated OperatorSpecSpec operators = 7; repeated common.NetIP external_ips = 8; repeated ProbeSpecSpec probes = 9; talos.resource.definitions.runtime.PlatformMetadataSpec metadata = 10; } // PortRange describes a range of ports. // // Range is [lo, hi]. message PortRange { uint32 lo = 1; uint32 hi = 2; } // ProbeSpecSpec describes the Probe. message ProbeSpecSpec { google.protobuf.Duration interval = 1; int64 failure_threshold = 2; TCPProbeSpec tcp = 3; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 4; } // ProbeStatusSpec describes the Probe. message ProbeStatusSpec { bool success = 1; string last_error = 2; } // ResolverSpecSpec describes DNS resolvers. message ResolverSpecSpec { repeated common.NetIP dns_servers = 1; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 2; repeated string search_domains = 3; } // ResolverStatusSpec describes DNS resolvers. message ResolverStatusSpec { repeated common.NetIP dns_servers = 1; repeated string search_domains = 2; } // RouteSpecSpec describes the route. message RouteSpecSpec { talos.resource.definitions.enums.NethelpersFamily family = 1; common.NetIPPrefix destination = 2; common.NetIP source = 3; common.NetIP gateway = 4; string out_link_name = 5; talos.resource.definitions.enums.NethelpersRoutingTable table = 6; uint32 priority = 7; talos.resource.definitions.enums.NethelpersScope scope = 8; talos.resource.definitions.enums.NethelpersRouteType type = 9; uint32 flags = 10; talos.resource.definitions.enums.NethelpersRouteProtocol protocol = 11; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 12; uint32 mtu = 13; } // RouteStatusSpec describes status of rendered secrets. message RouteStatusSpec { talos.resource.definitions.enums.NethelpersFamily family = 1; common.NetIPPrefix destination = 2; common.NetIP source = 3; common.NetIP gateway = 4; uint32 out_link_index = 5; string out_link_name = 6; talos.resource.definitions.enums.NethelpersRoutingTable table = 7; uint32 priority = 8; talos.resource.definitions.enums.NethelpersScope scope = 9; talos.resource.definitions.enums.NethelpersRouteType type = 10; uint32 flags = 11; talos.resource.definitions.enums.NethelpersRouteProtocol protocol = 12; uint32 mtu = 13; } // RoutingRuleSpecSpec describes the routing rule. message RoutingRuleSpecSpec { talos.resource.definitions.enums.NethelpersFamily family = 1; common.NetIPPrefix src = 2; common.NetIPPrefix dst = 3; talos.resource.definitions.enums.NethelpersRoutingTable table = 4; uint32 priority = 5; talos.resource.definitions.enums.NethelpersRoutingRuleAction action = 6; string iif_name = 7; string oif_name = 8; uint32 fw_mark = 9; uint32 fw_mask = 10; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 11; } // RoutingRuleStatusSpec describes the observed routing rule state. message RoutingRuleStatusSpec { talos.resource.definitions.enums.NethelpersFamily family = 1; common.NetIPPrefix src = 2; common.NetIPPrefix dst = 3; talos.resource.definitions.enums.NethelpersRoutingTable table = 4; uint32 priority = 5; talos.resource.definitions.enums.NethelpersRoutingRuleAction action = 6; string iif_name = 7; string oif_name = 8; uint32 fw_mark = 9; uint32 fw_mask = 10; } // STPSpec describes Spanning Tree Protocol (STP) settings of a bridge. message STPSpec { bool enabled = 1; } // StatusSpec describes network state. message StatusSpec { bool address_ready = 1; bool connectivity_ready = 2; bool hostname_ready = 3; bool etc_files_ready = 4; } // TCPProbeSpec describes the TCP Probe. message TCPProbeSpec { string endpoint = 1; google.protobuf.Duration timeout = 2; } // TimeServerSpecSpec describes NTP servers. message TimeServerSpecSpec { repeated string ntp_servers = 1; talos.resource.definitions.enums.NetworkConfigLayer config_layer = 2; } // TimeServerStatusSpec describes NTP servers. message TimeServerStatusSpec { repeated string ntp_servers = 1; } // VIPEquinixMetalSpec describes virtual (elastic) IP settings for Equinix Metal. message VIPEquinixMetalSpec { string project_id = 1; string device_id = 2; string api_token = 3; } // VIPHCloudSpec describes virtual (elastic) IP settings for Hetzner Cloud. message VIPHCloudSpec { int64 device_id = 1; int64 network_id = 2; string api_token = 3; } // VIPOperatorSpec describes virtual IP operator options. message VIPOperatorSpec { common.NetIP ip = 1; bool gratuitous_arp = 2; VIPEquinixMetalSpec equinix_metal = 3; VIPHCloudSpec h_cloud = 4; } // VLANSpec describes VLAN settings if Kind == "vlan". message VLANSpec { uint32 vid = 1; talos.resource.definitions.enums.NethelpersVLANProtocol protocol = 2; } // VRFMasterSpec describes vrf settings if Kind == "vrf". message VRFMasterSpec { talos.resource.definitions.enums.NethelpersRoutingTable table = 1; } // VRFSlave contains the name of the master vrf for an interface message VRFSlave { string master_name = 1; } // WireguardPeer describes a single peer. message WireguardPeer { string public_key = 1; string preshared_key = 2; string endpoint = 3; google.protobuf.Duration persistent_keepalive_interval = 4; repeated common.NetIPPrefix allowed_ips = 5; } // WireguardSpec describes Wireguard settings if Kind == "wireguard". message WireguardSpec { string private_key = 1; string public_key = 2; int64 listen_port = 3; int64 firewall_mark = 4; repeated WireguardPeer peers = 5; } ================================================ FILE: api/resource/definitions/perf/perf.proto ================================================ syntax = "proto3"; package talos.resource.definitions.perf; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/perf"; option java_package = "dev.talos.api.resource.definitions.perf"; // CPUSpec represents the last CPU stats snapshot. message CPUSpec { repeated CPUStat cpu = 1; CPUStat cpu_total = 2; uint64 irq_total = 3; uint64 context_switches = 4; uint64 process_created = 5; uint64 process_running = 6; uint64 process_blocked = 7; uint64 soft_irq_total = 8; } // CPUStat represents a single cpu stat. message CPUStat { double user = 1; double nice = 2; double system = 3; double idle = 4; double iowait = 5; double irq = 6; double soft_irq = 7; double steal = 8; double guest = 9; double guest_nice = 10; } // MemorySpec represents the last Memory stats snapshot. message MemorySpec { uint64 mem_total = 1; uint64 mem_used = 2; uint64 mem_available = 3; uint64 buffers = 4; uint64 cached = 5; uint64 swap_cached = 6; uint64 active = 7; uint64 inactive = 8; uint64 active_anon = 9; uint64 inactive_anon = 10; uint64 active_file = 11; uint64 inactive_file = 12; uint64 unevictable = 13; uint64 mlocked = 14; uint64 swap_total = 15; uint64 swap_free = 16; uint64 dirty = 17; uint64 writeback = 18; uint64 anon_pages = 19; uint64 mapped = 20; uint64 shmem = 21; uint64 slab = 22; uint64 s_reclaimable = 23; uint64 s_unreclaim = 24; uint64 kernel_stack = 25; uint64 page_tables = 26; uint64 nf_sunstable = 27; uint64 bounce = 28; uint64 writeback_tmp = 29; uint64 commit_limit = 30; uint64 committed_as = 31; uint64 vmalloc_total = 32; uint64 vmalloc_used = 33; uint64 vmalloc_chunk = 34; uint64 hardware_corrupted = 35; uint64 anon_huge_pages = 36; uint64 shmem_huge_pages = 37; uint64 shmem_pmd_mapped = 38; uint64 cma_total = 39; uint64 cma_free = 40; uint64 huge_pages_total = 41; uint64 huge_pages_free = 42; uint64 huge_pages_rsvd = 43; uint64 huge_pages_surp = 44; uint64 hugepagesize = 45; uint64 direct_map4k = 46; uint64 direct_map2m = 47; uint64 direct_map1g = 48; } ================================================ FILE: api/resource/definitions/proto/proto.proto ================================================ syntax = "proto3"; package talos.resource.definitions.proto; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/proto"; option java_package = "dev.talos.api.resource.definitions.proto"; // LinuxIDMapping specifies UID/GID mappings. message LinuxIDMapping { uint32 container_id = 1; uint32 host_id = 2; uint32 size = 3; } // Mount specifies a mount for a container. message Mount { string destination = 1; string type = 2; string source = 3; repeated string options = 4; repeated LinuxIDMapping uid_mappings = 5; repeated LinuxIDMapping gid_mappings = 6; } ================================================ FILE: api/resource/definitions/runtime/runtime.proto ================================================ syntax = "proto3"; package talos.resource.definitions.runtime; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/runtime"; option java_package = "dev.talos.api.resource.definitions.runtime"; import "common/common.proto"; import "google/protobuf/duration.proto"; import "resource/definitions/enums/enums.proto"; // APIServiceConfigSpec describes configuration for Talos API service (apid). message APIServiceConfigSpec { string listen_address = 1; bool node_routing_disabled = 2; bool readonly_role_mode = 3; bool skip_verifying_client_cert = 4; } // BootedEntrySpec describes the booted entry resource properties. message BootedEntrySpec { string booted_entry = 1; } // DevicesStatusSpec is the spec for devices status. message DevicesStatusSpec { bool ready = 1; } // DiagnosticSpec is the spec for devices status. message DiagnosticSpec { string message = 1; repeated string details = 2; } // EnvironmentSpec describes the specification of Environment resource. message EnvironmentSpec { repeated string variables = 1; } // EventSinkConfigSpec describes configuration of Talos event log streaming. message EventSinkConfigSpec { string endpoint = 1; } // ExtensionServiceConfigFile describes extensions service config files. message ExtensionServiceConfigFile { string content = 1; string mount_path = 2; } // ExtensionServiceConfigSpec describes status of rendered extensions service config files. message ExtensionServiceConfigSpec { repeated ExtensionServiceConfigFile files = 1; repeated string environment = 2; } // ExtensionServiceConfigStatusSpec describes status of rendered extensions service config files. message ExtensionServiceConfigStatusSpec { string spec_version = 1; } // KernelCmdlineSpec presents kernel command line (contents of /proc/cmdline). message KernelCmdlineSpec { string cmdline = 1; } // KernelModuleSpecSpec describes Linux kernel module to load. message KernelModuleSpecSpec { string name = 1; repeated string parameters = 2; } // KernelParamSpecSpec describes status of the defined sysctls. message KernelParamSpecSpec { string value = 1; bool ignore_errors = 2; } // KernelParamStatusSpec describes status of the defined sysctls. message KernelParamStatusSpec { string current = 1; string default = 2; bool unsupported = 3; } // KmsgLogConfigSpec describes configuration for kmsg log streaming. message KmsgLogConfigSpec { repeated common.URL destinations = 1; } // LoadedKernelModuleSpec describes Linux kernel module to load. message LoadedKernelModuleSpec { int64 size = 1; int64 reference_count = 2; repeated string dependencies = 3; string state = 4; string address = 5; } // MachineStatusSpec describes status of the defined sysctls. message MachineStatusSpec { talos.resource.definitions.enums.RuntimeMachineStage stage = 1; MachineStatusStatus status = 2; } // MachineStatusStatus describes machine current status at the stage. message MachineStatusStatus { bool ready = 1; repeated UnmetCondition unmet_conditions = 2; } // MaintenanceServiceConfigSpec describes configuration for maintenance service API. message MaintenanceServiceConfigSpec { string listen_address = 1; repeated common.NetIP reachable_addresses = 2; } // MetaKeySpec describes status of the defined sysctls. message MetaKeySpec { string value = 1; } // MetaLoadedSpec is the spec for meta loaded. The Done field is always true when resource exists. message MetaLoadedSpec { bool done = 1; } // MountStatusSpec describes status of the defined sysctls. message MountStatusSpec { string source = 1; string target = 2; string filesystem_type = 3; repeated string options = 4; bool encrypted = 5; repeated string encryption_providers = 6; } // OOMActionSpec describes the OOM action record resource properties. message OOMActionSpec { string trigger_context = 1; double score = 2; repeated string processes = 3; } // PlatformMetadataSpec describes platform metadata properties. message PlatformMetadataSpec { string platform = 1; string hostname = 2; string region = 3; string zone = 4; string instance_type = 5; string instance_id = 6; string provider_id = 7; bool spot = 8; string internal_dns = 9; string external_dns = 10; map tags = 11; } // SBOMItemSpec describes the SBOM item resource properties. message SBOMItemSpec { string name = 1; string version = 2; string license = 3; repeated string cp_es = 4; repeated string pur_ls = 5; bool extension = 6; } // SecurityStateSpec describes the security state resource properties. message SecurityStateSpec { bool secure_boot = 1; string uki_signing_key_fingerprint = 2; string pcr_signing_key_fingerprint = 3; talos.resource.definitions.enums.RuntimeSELinuxState se_linux_state = 4; bool booted_with_uki = 5; talos.resource.definitions.enums.RuntimeFIPSState fips_state = 6; bool module_signature_enforced = 7; } // UniqueMachineTokenSpec is the spec for the machine unique token. Token can be empty if machine wasn't assigned any. message UniqueMachineTokenSpec { string token = 1; } // UnmetCondition is a failure which prevents machine from being ready at the stage. message UnmetCondition { string name = 1; string reason = 2; } // WatchdogTimerConfigSpec describes configuration of watchdog timer. message WatchdogTimerConfigSpec { string device = 1; google.protobuf.Duration timeout = 2; } // WatchdogTimerStatusSpec describes configuration of watchdog timer. message WatchdogTimerStatusSpec { string device = 1; google.protobuf.Duration timeout = 2; google.protobuf.Duration feed_interval = 3; } ================================================ FILE: api/resource/definitions/secrets/secrets.proto ================================================ syntax = "proto3"; package talos.resource.definitions.secrets; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/secrets"; option java_package = "dev.talos.api.resource.definitions.secrets"; import "common/common.proto"; // APICertsSpec describes etcd certs secrets. message APICertsSpec { common.PEMEncodedCertificateAndKey client = 2; common.PEMEncodedCertificateAndKey server = 3; repeated common.PEMEncodedCertificate accepted_c_as = 4; bool skip_verifying_client_cert = 5; } // CertSANSpec describes fields of the cert SANs. message CertSANSpec { repeated common.NetIP i_ps = 1; repeated string dns_names = 2; string fqdn = 3; } // EncryptionSaltSpec describes the salt. message EncryptionSaltSpec { bytes disk_salt = 1; } // EtcdCertsSpec describes etcd certs secrets. message EtcdCertsSpec { common.PEMEncodedCertificateAndKey etcd = 1; common.PEMEncodedCertificateAndKey etcd_peer = 2; common.PEMEncodedCertificateAndKey etcd_admin = 3; common.PEMEncodedCertificateAndKey etcd_api_server = 4; } // EtcdRootSpec describes etcd CA secrets. message EtcdRootSpec { common.PEMEncodedCertificateAndKey etcd_ca = 1; } // KubeletSpec describes root Kubernetes secrets. message KubeletSpec { common.URL endpoint = 1; string bootstrap_token_id = 3; string bootstrap_token_secret = 4; repeated common.PEMEncodedCertificate accepted_c_as = 5; } // KubernetesCertsSpec describes generated Kubernetes certificates. message KubernetesCertsSpec { string scheduler_kubeconfig = 4; string controller_manager_kubeconfig = 5; string localhost_admin_kubeconfig = 6; string admin_kubeconfig = 7; } // KubernetesDynamicCertsSpec describes generated KubernetesCerts certificates. message KubernetesDynamicCertsSpec { common.PEMEncodedCertificateAndKey api_server = 1; common.PEMEncodedCertificateAndKey api_server_kubelet_client = 2; common.PEMEncodedCertificateAndKey front_proxy = 3; } // KubernetesRootSpec describes root Kubernetes secrets. message KubernetesRootSpec { string name = 1; common.URL endpoint = 2; common.URL local_endpoint = 3; repeated string cert_sa_ns = 4; string dns_domain = 6; common.PEMEncodedCertificateAndKey issuing_ca = 7; common.PEMEncodedKey service_account = 8; common.PEMEncodedCertificateAndKey aggregator_ca = 9; string aescbc_encryption_secret = 10; string bootstrap_token_id = 11; string bootstrap_token_secret = 12; string secretbox_encryption_secret = 13; repeated common.NetIP api_server_ips = 14; repeated common.PEMEncodedCertificate accepted_c_as = 15; } // MaintenanceRootSpec describes maintenance service CA. message MaintenanceRootSpec { common.PEMEncodedCertificateAndKey ca = 1; } // OSRootSpec describes operating system CA. message OSRootSpec { common.PEMEncodedCertificateAndKey issuing_ca = 1; repeated common.NetIP cert_sani_ps = 2; repeated string cert_sandns_names = 3; string token = 4; repeated common.PEMEncodedCertificate accepted_c_as = 5; } // TrustdCertsSpec describes etcd certs secrets. message TrustdCertsSpec { common.PEMEncodedCertificateAndKey server = 2; repeated common.PEMEncodedCertificate accepted_c_as = 3; } ================================================ FILE: api/resource/definitions/security/security.proto ================================================ syntax = "proto3"; package talos.resource.definitions.security; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/security"; option java_package = "dev.talos.api.resource.definitions.security"; import "google/protobuf/timestamp.proto"; // ImageKeylessVerifierSpec represents a signature verification provider. message ImageKeylessVerifierSpec { string issuer = 1; string subject = 2; string subject_regex = 3; } // ImagePublicKeyVerifierSpec represents a signature verification provider with static public key. message ImagePublicKeyVerifierSpec { string certificate = 1; } // ImageVerificationRuleSpec represents a verification rule. message ImageVerificationRuleSpec { string image_pattern = 2; bool skip = 3; bool deny = 4; ImageKeylessVerifierSpec keyless_verifier = 5; ImagePublicKeyVerifierSpec public_key_verifier = 6; } // TUFTrustedRootSpec represents a sigstore's TUF trusted root information. message TUFTrustedRootSpec { google.protobuf.Timestamp last_refresh_time = 1; string json_data = 2; } ================================================ FILE: api/resource/definitions/siderolink/siderolink.proto ================================================ syntax = "proto3"; package talos.resource.definitions.siderolink; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/siderolink"; option java_package = "dev.talos.api.resource.definitions.siderolink"; import "common/common.proto"; // ConfigSpec describes Siderolink configuration. message ConfigSpec { string api_endpoint = 1; string host = 2; string join_token = 3; bool insecure = 4; bool tunnel = 5; } // StatusSpec describes Siderolink status. message StatusSpec { string host = 1; bool connected = 2; string link_name = 3; bool grpc_tunnel = 4; } // TunnelSpec describes Siderolink GRPC Tunnel configuration. message TunnelSpec { string api_endpoint = 1; string link_name = 2; int64 mtu = 3; common.NetIPPort node_address = 4; } ================================================ FILE: api/resource/definitions/time/time.proto ================================================ syntax = "proto3"; package talos.resource.definitions.time; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/time"; option java_package = "dev.talos.api.resource.definitions.time"; import "google/protobuf/duration.proto"; // AdjtimeStatusSpec describes Linux internal adjtime state. message AdjtimeStatusSpec { google.protobuf.Duration offset = 1; double frequency_adjustment_ratio = 2; google.protobuf.Duration max_error = 3; google.protobuf.Duration est_error = 4; string status = 5; int64 constant = 6; bool sync_status = 7; string state = 8; } // StatusSpec describes time sync state. message StatusSpec { bool synced = 1; int64 epoch = 2; bool sync_disabled = 3; } ================================================ FILE: api/resource/definitions/v1alpha1/v1alpha1.proto ================================================ syntax = "proto3"; package talos.resource.definitions.v1alpha1; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/v1alpha1"; option java_package = "dev.talos.api.resource.definitions.v1alpha1"; // ServiceSpec describe service state. message ServiceSpec { bool running = 1; bool healthy = 2; bool unknown = 3; } ================================================ FILE: api/resource/network/device_config.proto ================================================ syntax = "proto3"; package resource.network; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/network"; option java_package = "dev.talos.api.resource.network"; // DeviceConfigSpecSpec is the spec for the network.DeviceConfigSpec resource. message DeviceConfigSpecSpec { // Contains YAML marshalled device config (as part of the machine config). bytes yaml_marshalled = 1; } ================================================ FILE: api/security/security.proto ================================================ syntax = "proto3"; package securityapi; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/security"; option java_package = "dev.talos.api.security"; // The security service definition. service SecurityService { rpc Certificate(CertificateRequest) returns (CertificateResponse); } // The request message containing the certificate signing request. message CertificateRequest { // Certificate Signing Request in PEM format. bytes csr = 1; } // The response message containing signed certificate. message CertificateResponse { // Certificate of the CA that signed the requested certificate in PEM format. bytes ca = 1; // Signed X.509 requested certificate in PEM format. bytes crt = 2; } ================================================ FILE: api/storage/storage.proto ================================================ syntax = "proto3"; package storage; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/storage"; option java_package = "dev.talos.api.storage"; import "common/common.proto"; import "google/protobuf/empty.proto"; // StorageService represents the storage service. service StorageService { rpc Disks(google.protobuf.Empty) returns (DisksResponse); // BlockDeviceWipe performs a wipe of the blockdevice (partition or disk). // // The method doesn't require a reboot, and it can only wipe blockdevices which are not // being used as volumes at the moment. // Wiping of volumes requires a different API. rpc BlockDeviceWipe(BlockDeviceWipeRequest) returns (BlockDeviceWipeResponse); } // Disk represents a disk. message Disk { // Size indicates the disk size in bytes. uint64 size = 1; // Model idicates the disk model. string model = 2; // DeviceName indicates the disk name (e.g. `sda`). string device_name = 3; // Name as in `/sys/block//device/name`. string name = 4; // Serial as in `/sys/block//device/serial`. string serial = 5; // Modalias as in `/sys/block//device/modalias`. string modalias = 6; // Uuid as in `/sys/block//device/uuid`. string uuid = 7; // Wwid as in `/sys/block//device/wwid`. string wwid = 8; enum DiskType { UNKNOWN = 0; SSD = 1; HDD = 2; NVME = 3; SD = 4; CD = 5; } // Type is a type of the disk: nvme, ssd, hdd, sd card. DiskType type = 9; // BusPath is the bus path of the disk. string bus_path = 10; // SystemDisk indicates that the disk is used as Talos system disk. bool system_disk = 11; // Subsystem is the symlink path in the `/sys/block//subsystem`. string subsystem = 12; // Readonly specifies if the disk is read only. bool readonly = 13; } // DisksResponse represents the response of the `Disks` RPC. message Disks { common.Metadata metadata = 1; repeated Disk disks = 2; } message DisksResponse { repeated Disks messages = 1; } // rpc BlockDeviceWipe message BlockDeviceWipeRequest { repeated BlockDeviceWipeDescriptor devices = 1; } // BlockDeviceWipeDescriptor represents a single block device to be wiped. // // The device can be either a full disk (e.g. vda) or a partition (vda5). // The device should not be used in any of active volumes. // The device should not be used as a secondary (e.g. part of LVM). message BlockDeviceWipeDescriptor { enum Method { // Fast wipe - wipe only filesystem signatures. FAST = 0; // Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features). ZEROES = 1; } // Device name to wipe (e.g. sda or sda5). // // The name should be submitted without `/dev/` prefix. string device = 1; // Wipe method to use. Method method = 2; // Skip the volume in use check. bool skip_volume_check = 3; // Skip the secondary disk check (e.g. underlying disk for RAID or LVM). bool skip_secondary_check = 5; // Drop the partition (only applies if the device is a partition). bool drop_partition = 4; } message BlockDeviceWipeResponse { repeated BlockDeviceWipe messages = 1; } message BlockDeviceWipe { common.Metadata metadata = 1; } ================================================ FILE: api/time/time.proto ================================================ syntax = "proto3"; package time; option go_package = "github.com/siderolabs/talos/pkg/machinery/api/time"; option java_package = "dev.talos.api.time"; import "common/common.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/timestamp.proto"; // The time service definition. service TimeService { rpc Time(google.protobuf.Empty) returns (TimeResponse); rpc TimeCheck(TimeRequest) returns (TimeResponse); } // The response message containing the ntp server message TimeRequest { string server = 1; } message Time { common.Metadata metadata = 1; string server = 2; google.protobuf.Timestamp localtime = 3; google.protobuf.Timestamp remotetime = 4; } // The response message containing the ntp server, time, and offset message TimeResponse { repeated Time messages = 1; } ================================================ FILE: api/vendor/google/api/expr/v1alpha1/checked.proto ================================================ // Copyright 2024 Google LLC // // 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. syntax = "proto3"; package google.api.expr.v1alpha1; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; option java_multiple_files = true; option java_outer_classname = "DeclProto"; option java_package = "com.google.api.expr.v1alpha1"; import "google/api/expr/v1alpha1/syntax.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; // Protos for representing CEL declarations and typed checked expressions. // A CEL expression which has been successfully type checked. message CheckedExpr { // A map from expression ids to resolved references. // // The following entries are in this table: // // - An Ident or Select expression is represented here if it resolves to a // declaration. For instance, if `a.b.c` is represented by // `select(select(id(a), b), c)`, and `a.b` resolves to a declaration, // while `c` is a field selection, then the reference is attached to the // nested select expression (but not to the id or or the outer select). // In turn, if `a` resolves to a declaration and `b.c` are field selections, // the reference is attached to the ident expression. // - Every Call expression has an entry here, identifying the function being // called. // - Every CreateStruct expression for a message has an entry, identifying // the message. map reference_map = 2; // A map from expression ids to types. // // Every expression node which has a type different than DYN has a mapping // here. If an expression has type DYN, it is omitted from this map to save // space. map type_map = 3; // The source info derived from input that generated the parsed `expr` and // any optimizations made during the type-checking pass. SourceInfo source_info = 5; // The expr version indicates the major / minor version number of the `expr` // representation. // // The most common reason for a version change will be to indicate to the CEL // runtimes that transformations have been performed on the expr during static // analysis. In some cases, this will save the runtime the work of applying // the same or similar transformations prior to evaluation. string expr_version = 6; // The checked expression. Semantically equivalent to the parsed `expr`, but // may have structural differences. Expr expr = 4; } // Represents a CEL type. message Type { // List type with typed elements, e.g. `list`. message ListType { // The element type. Type elem_type = 1; } // Map type with parameterized key and value types, e.g. `map`. message MapType { // The type of the key. Type key_type = 1; // The type of the value. Type value_type = 2; } // Function type with result and arg types. message FunctionType { // Result type of the function. Type result_type = 1; // Argument types of the function. repeated Type arg_types = 2; } // Application defined abstract type. message AbstractType { // The fully qualified name of this abstract type. string name = 1; // Parameter types for this abstract type. repeated Type parameter_types = 2; } // CEL primitive types. enum PrimitiveType { // Unspecified type. PRIMITIVE_TYPE_UNSPECIFIED = 0; // Boolean type. BOOL = 1; // Int64 type. // // Proto-based integer values are widened to int64. INT64 = 2; // Uint64 type. // // Proto-based unsigned integer values are widened to uint64. UINT64 = 3; // Double type. // // Proto-based float values are widened to double values. DOUBLE = 4; // String type. STRING = 5; // Bytes type. BYTES = 6; } // Well-known protobuf types treated with first-class support in CEL. enum WellKnownType { // Unspecified type. WELL_KNOWN_TYPE_UNSPECIFIED = 0; // Well-known protobuf.Any type. // // Any types are a polymorphic message type. During type-checking they are // treated like `DYN` types, but at runtime they are resolved to a specific // message type specified at evaluation time. ANY = 1; // Well-known protobuf.Timestamp type, internally referenced as `timestamp`. TIMESTAMP = 2; // Well-known protobuf.Duration type, internally referenced as `duration`. DURATION = 3; } // The kind of type. oneof type_kind { // Dynamic type. google.protobuf.Empty dyn = 1; // Null value. google.protobuf.NullValue null = 2; // Primitive types: `true`, `1u`, `-2.0`, `'string'`, `b'bytes'`. PrimitiveType primitive = 3; // Wrapper of a primitive type, e.g. `google.protobuf.Int64Value`. PrimitiveType wrapper = 4; // Well-known protobuf type such as `google.protobuf.Timestamp`. WellKnownType well_known = 5; // Parameterized list with elements of `list_type`, e.g. `list`. ListType list_type = 6; // Parameterized map with typed keys and values. MapType map_type = 7; // Function type. FunctionType function = 8; // Protocol buffer message type. // // The `message_type` string specifies the qualified message type name. For // example, `google.plus.Profile`. string message_type = 9; // Type param type. // // The `type_param` string specifies the type parameter name, e.g. `list` // would be a `list_type` whose element type was a `type_param` type // named `E`. string type_param = 10; // Type type. // // The `type` value specifies the target type. e.g. int is type with a // target type of `Primitive.INT`. Type type = 11; // Error type. // // During type-checking if an expression is an error, its type is propagated // as the `ERROR` type. This permits the type-checker to discover other // errors present in the expression. google.protobuf.Empty error = 12; // Abstract, application defined type. AbstractType abstract_type = 14; } } // Represents a declaration of a named value or function. // // A declaration is part of the contract between the expression, the agent // evaluating that expression, and the caller requesting evaluation. message Decl { // Identifier declaration which specifies its type and optional `Expr` value. // // An identifier without a value is a declaration that must be provided at // evaluation time. An identifier with a value should resolve to a constant, // but may be used in conjunction with other identifiers bound at evaluation // time. message IdentDecl { // Required. The type of the identifier. Type type = 1; // The constant value of the identifier. If not specified, the identifier // must be supplied at evaluation time. Constant value = 2; // Documentation string for the identifier. string doc = 3; } // Function declaration specifies one or more overloads which indicate the // function's parameter types and return type. // // Functions have no observable side-effects (there may be side-effects like // logging which are not observable from CEL). message FunctionDecl { // An overload indicates a function's parameter types and return type, and // may optionally include a function body described in terms of // [Expr][google.api.expr.v1alpha1.Expr] values. // // Functions overloads are declared in either a function or method // call-style. For methods, the `params[0]` is the expected type of the // target receiver. // // Overloads must have non-overlapping argument types after erasure of all // parameterized type variables (similar as type erasure in Java). message Overload { // Required. Globally unique overload name of the function which reflects // the function name and argument types. // // This will be used by a [Reference][google.api.expr.v1alpha1.Reference] // to indicate the `overload_id` that was resolved for the function // `name`. string overload_id = 1; // List of function parameter [Type][google.api.expr.v1alpha1.Type] // values. // // Param types are disjoint after generic type parameters have been // replaced with the type `DYN`. Since the `DYN` type is compatible with // any other type, this means that if `A` is a type parameter, the // function types `int` and `int` are not disjoint. Likewise, // `map` is not disjoint from `map`. // // When the `result_type` of a function is a generic type param, the // type param name also appears as the `type` of on at least one params. repeated Type params = 2; // The type param names associated with the function declaration. // // For example, `function ex(K key, map map) : V` would yield // the type params of `K, V`. repeated string type_params = 3; // Required. The result type of the function. For example, the operator // `string.isEmpty()` would have `result_type` of `kind: BOOL`. Type result_type = 4; // Whether the function is to be used in a method call-style `x.f(...)` // or a function call-style `f(x, ...)`. // // For methods, the first parameter declaration, `params[0]` is the // expected type of the target receiver. bool is_instance_function = 5; // Documentation string for the overload. string doc = 6; } // Required. List of function overloads, must contain at least one overload. repeated Overload overloads = 1; } // The fully qualified name of the declaration. // // Declarations are organized in containers and this represents the full path // to the declaration in its container, as in `google.api.expr.Decl`. // // Declarations used as // [FunctionDecl.Overload][google.api.expr.v1alpha1.Decl.FunctionDecl.Overload] // parameters may or may not have a name depending on whether the overload is // function declaration or a function definition containing a result // [Expr][google.api.expr.v1alpha1.Expr]. string name = 1; // Required. The declaration kind. oneof decl_kind { // Identifier declaration. IdentDecl ident = 2; // Function declaration. FunctionDecl function = 3; } } // Describes a resolved reference to a declaration. message Reference { // The fully qualified name of the declaration. string name = 1; // For references to functions, this is a list of `Overload.overload_id` // values which match according to typing rules. // // If the list has more than one element, overload resolution among the // presented candidates must happen at runtime because of dynamic types. The // type checker attempts to narrow down this list as much as possible. // // Empty if this is not a reference to a // [Decl.FunctionDecl][google.api.expr.v1alpha1.Decl.FunctionDecl]. repeated string overload_id = 3; // For references to constants, this may contain the value of the // constant if known at compile time. Constant value = 4; } ================================================ FILE: api/vendor/google/api/expr/v1alpha1/eval.proto ================================================ // Copyright 2024 Google LLC // // 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. syntax = "proto3"; package google.api.expr.v1alpha1; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; option java_multiple_files = true; option java_outer_classname = "EvalProto"; option java_package = "com.google.api.expr.v1alpha1"; import "google/api/expr/v1alpha1/value.proto"; import "google/rpc/status.proto"; // The state of an evaluation. // // Can represent an initial, partial, or completed state of evaluation. message EvalState { // A single evalution result. message Result { // The id of the expression this result if for. int64 expr = 1; // The index in `values` of the resulting value. int64 value = 2; } // The unique values referenced in this message. repeated ExprValue values = 1; // An ordered list of results. // // Tracks the flow of evaluation through the expression. // May be sparse. repeated Result results = 3; } // The value of an evaluated expression. message ExprValue { // An expression can resolve to a value, error or unknown. oneof kind { // A concrete value. Value value = 1; // The set of errors in the critical path of evalution. // // Only errors in the critical path are included. For example, // `( || true) && ` will only result in ``, // while ` || ` will result in both `` and // ``. // // Errors cause by the presence of other errors are not included in the // set. For example `.foo`, `foo()`, and ` + 1` will // only result in ``. // // Multiple errors *might* be included when evaluation could result // in different errors. For example ` + ` and // `foo(, )` may result in ``, `` or both. // The exact subset of errors included for this case is unspecified and // depends on the implementation details of the evaluator. ErrorSet error = 2; // The set of unknowns in the critical path of evaluation. // // Unknown behaves identically to Error with regards to propagation. // Specifically, only unknowns in the critical path are included, unknowns // caused by the presence of other unknowns are not included, and multiple // unknowns *might* be included included when evaluation could result in // different unknowns. For example: // // ( || true) && -> // || -> // .foo -> // foo() -> // + -> or // // Unknown takes precidence over Error in cases where a `Value` can short // circuit the result: // // || -> // && -> // // Errors take precidence in all other cases: // // + -> // foo(, ) -> UnknownSet unknown = 3; } } // A set of errors. // // The errors included depend on the context. See `ExprValue.error`. message ErrorSet { // The errors in the set. repeated google.rpc.Status errors = 1; } // A set of expressions for which the value is unknown. // // The unknowns included depend on the context. See `ExprValue.unknown`. message UnknownSet { // The ids of the expressions with unknown values. repeated int64 exprs = 1; } ================================================ FILE: api/vendor/google/api/expr/v1alpha1/explain.proto ================================================ // Copyright 2024 Google LLC // // 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. syntax = "proto3"; package google.api.expr.v1alpha1; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; option java_multiple_files = true; option java_outer_classname = "ExplainProto"; option java_package = "com.google.api.expr.v1alpha1"; import "google/api/expr/v1alpha1/value.proto"; // Values of intermediate expressions produced when evaluating expression. // Deprecated, use `EvalState` instead. message Explain { option deprecated = true; // ID and value index of one step. message ExprStep { // ID of corresponding Expr node. int64 id = 1; // Index of the value in the values list. int32 value_index = 2; } // All of the observed values. // // The field value_index is an index in the values list. // Separating values from steps is needed to remove redundant values. repeated Value values = 1; // List of steps. // // Repeated evaluations of the same expression generate new ExprStep // instances. The order of such ExprStep instances matches the order of // elements returned by Comprehension.iter_range. repeated ExprStep expr_steps = 2; } ================================================ FILE: api/vendor/google/api/expr/v1alpha1/syntax.proto ================================================ // Copyright 2024 Google LLC // // 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. syntax = "proto3"; package google.api.expr.v1alpha1; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; option java_multiple_files = true; option java_outer_classname = "SyntaxProto"; option java_package = "com.google.api.expr.v1alpha1"; import "google/protobuf/duration.proto"; import "google/protobuf/struct.proto"; import "google/protobuf/timestamp.proto"; // A representation of the abstract syntax of the Common Expression Language. // An expression together with source information as returned by the parser. message ParsedExpr { // The parsed expression. Expr expr = 2; // The source info derived from input that generated the parsed `expr`. SourceInfo source_info = 3; } // An abstract representation of a common expression. // // Expressions are abstractly represented as a collection of identifiers, // select statements, function calls, literals, and comprehensions. All // operators with the exception of the '.' operator are modelled as function // calls. This makes it easy to represent new operators into the existing AST. // // All references within expressions must resolve to a // [Decl][google.api.expr.v1alpha1.Decl] provided at type-check for an // expression to be valid. A reference may either be a bare identifier `name` or // a qualified identifier `google.api.name`. References may either refer to a // value or a function declaration. // // For example, the expression `google.api.name.startsWith('expr')` references // the declaration `google.api.name` within a // [Expr.Select][google.api.expr.v1alpha1.Expr.Select] expression, and the // function declaration `startsWith`. message Expr { // An identifier expression. e.g. `request`. message Ident { // Required. Holds a single, unqualified identifier, possibly preceded by a // '.'. // // Qualified names are represented by the // [Expr.Select][google.api.expr.v1alpha1.Expr.Select] expression. string name = 1; } // A field selection expression. e.g. `request.auth`. message Select { // Required. The target of the selection expression. // // For example, in the select expression `request.auth`, the `request` // portion of the expression is the `operand`. Expr operand = 1; // Required. The name of the field to select. // // For example, in the select expression `request.auth`, the `auth` portion // of the expression would be the `field`. string field = 2; // Whether the select is to be interpreted as a field presence test. // // This results from the macro `has(request.auth)`. bool test_only = 3; } // A call expression, including calls to predefined functions and operators. // // For example, `value == 10`, `size(map_value)`. message Call { // The target of an method call-style expression. For example, `x` in // `x.f()`. Expr target = 1; // Required. The name of the function or method being called. string function = 2; // The arguments. repeated Expr args = 3; } // A list creation expression. // // Lists may either be homogenous, e.g. `[1, 2, 3]`, or heterogeneous, e.g. // `dyn([1, 'hello', 2.0])` message CreateList { // The elements part of the list. repeated Expr elements = 1; // The indices within the elements list which are marked as optional // elements. // // When an optional-typed value is present, the value it contains // is included in the list. If the optional-typed value is absent, the list // element is omitted from the CreateList result. repeated int32 optional_indices = 2; } // A map or message creation expression. // // Maps are constructed as `{'key_name': 'value'}`. Message construction is // similar, but prefixed with a type name and composed of field ids: // `types.MyType{field_id: 'value'}`. message CreateStruct { // Represents an entry. message Entry { // Required. An id assigned to this node by the parser which is unique // in a given expression tree. This is used to associate type // information and other attributes to the node. int64 id = 1; // The `Entry` key kinds. oneof key_kind { // The field key for a message creator statement. string field_key = 2; // The key expression for a map creation statement. Expr map_key = 3; } // Required. The value assigned to the key. // // If the optional_entry field is true, the expression must resolve to an // optional-typed value. If the optional value is present, the key will be // set; however, if the optional value is absent, the key will be unset. Expr value = 4; // Whether the key-value pair is optional. bool optional_entry = 5; } // The type name of the message to be created, empty when creating map // literals. string message_name = 1; // The entries in the creation expression. repeated Entry entries = 2; } // A comprehension expression applied to a list or map. // // Comprehensions are not part of the core syntax, but enabled with macros. // A macro matches a specific call signature within a parsed AST and replaces // the call with an alternate AST block. Macro expansion happens at parse // time. // // The following macros are supported within CEL: // // Aggregate type macros may be applied to all elements in a list or all keys // in a map: // // * `all`, `exists`, `exists_one` - test a predicate expression against // the inputs and return `true` if the predicate is satisfied for all, // any, or only one value `list.all(x, x < 10)`. // * `filter` - test a predicate expression against the inputs and return // the subset of elements which satisfy the predicate: // `payments.filter(p, p > 1000)`. // * `map` - apply an expression to all elements in the input and return the // output aggregate type: `[1, 2, 3].map(i, i * i)`. // // The `has(m.x)` macro tests whether the property `x` is present in struct // `m`. The semantics of this macro depend on the type of `m`. For proto2 // messages `has(m.x)` is defined as 'defined, but not set`. For proto3, the // macro tests whether the property is set to its default. For map and struct // types, the macro tests whether the property `x` is defined on `m`. message Comprehension { // The name of the iteration variable. string iter_var = 1; // The range over which var iterates. Expr iter_range = 2; // The name of the variable used for accumulation of the result. string accu_var = 3; // The initial value of the accumulator. Expr accu_init = 4; // An expression which can contain iter_var and accu_var. // // Returns false when the result has been computed and may be used as // a hint to short-circuit the remainder of the comprehension. Expr loop_condition = 5; // An expression which can contain iter_var and accu_var. // // Computes the next value of accu_var. Expr loop_step = 6; // An expression which can contain accu_var. // // Computes the result. Expr result = 7; } // Required. An id assigned to this node by the parser which is unique in a // given expression tree. This is used to associate type information and other // attributes to a node in the parse tree. int64 id = 2; // Required. Variants of expressions. oneof expr_kind { // A literal expression. Constant const_expr = 3; // An identifier expression. Ident ident_expr = 4; // A field selection expression, e.g. `request.auth`. Select select_expr = 5; // A call expression, including calls to predefined functions and operators. Call call_expr = 6; // A list creation expression. CreateList list_expr = 7; // A map or message creation expression. CreateStruct struct_expr = 8; // A comprehension expression. Comprehension comprehension_expr = 9; } } // Represents a primitive literal. // // Named 'Constant' here for backwards compatibility. // // This is similar as the primitives supported in the well-known type // `google.protobuf.Value`, but richer so it can represent CEL's full range of // primitives. // // Lists and structs are not included as constants as these aggregate types may // contain [Expr][google.api.expr.v1alpha1.Expr] elements which require // evaluation and are thus not constant. // // Examples of literals include: `"hello"`, `b'bytes'`, `1u`, `4.2`, `-2`, // `true`, `null`. message Constant { // Required. The valid constant kinds. oneof constant_kind { // null value. google.protobuf.NullValue null_value = 1; // boolean value. bool bool_value = 2; // int64 value. int64 int64_value = 3; // uint64 value. uint64 uint64_value = 4; // double value. double double_value = 5; // string value. string string_value = 6; // bytes value. bytes bytes_value = 7; // protobuf.Duration value. // // Deprecated: duration is no longer considered a builtin cel type. google.protobuf.Duration duration_value = 8 [deprecated = true]; // protobuf.Timestamp value. // // Deprecated: timestamp is no longer considered a builtin cel type. google.protobuf.Timestamp timestamp_value = 9 [deprecated = true]; } } // Source information collected at parse time. message SourceInfo { // An extension that was requested for the source expression. message Extension { // Version message Version { // Major version changes indicate different required support level from // the required components. int64 major = 1; // Minor version changes must not change the observed behavior from // existing implementations, but may be provided informationally. int64 minor = 2; } // CEL component specifier. enum Component { // Unspecified, default. COMPONENT_UNSPECIFIED = 0; // Parser. Converts a CEL string to an AST. COMPONENT_PARSER = 1; // Type checker. Checks that references in an AST are defined and types // agree. COMPONENT_TYPE_CHECKER = 2; // Runtime. Evaluates a parsed and optionally checked CEL AST against a // context. COMPONENT_RUNTIME = 3; } // Identifier for the extension. Example: constant_folding string id = 1; // If set, the listed components must understand the extension for the // expression to evaluate correctly. // // This field has set semantics, repeated values should be deduplicated. repeated Component affected_components = 2; // Version info. May be skipped if it isn't meaningful for the extension. // (for example constant_folding might always be v0.0). Version version = 3; } // The syntax version of the source, e.g. `cel1`. string syntax_version = 1; // The location name. All position information attached to an expression is // relative to this location. // // The location could be a file, UI element, or similar. For example, // `acme/app/AnvilPolicy.cel`. string location = 2; // Monotonically increasing list of code point offsets where newlines // `\n` appear. // // The line number of a given position is the index `i` where for a given // `id` the `line_offsets[i] < id_positions[id] < line_offsets[i+1]`. The // column may be derivd from `id_positions[id] - line_offsets[i]`. repeated int32 line_offsets = 3; // A map from the parse node id (e.g. `Expr.id`) to the code point offset // within the source. map positions = 4; // A map from the parse node id where a macro replacement was made to the // call `Expr` that resulted in a macro expansion. // // For example, `has(value.field)` is a function call that is replaced by a // `test_only` field selection in the AST. Likewise, the call // `list.exists(e, e > 10)` translates to a comprehension expression. The key // in the map corresponds to the expression id of the expanded macro, and the // value is the call `Expr` that was replaced. map macro_calls = 5; // A list of tags for extensions that were used while parsing or type checking // the source expression. For example, optimizations that require special // runtime support may be specified. // // These are used to check feature support between components in separate // implementations. This can be used to either skip redundant work or // report an error if the extension is unsupported. repeated Extension extensions = 6; } // A specific position in source. message SourcePosition { // The soucre location name (e.g. file name). string location = 1; // The UTF-8 code unit offset. int32 offset = 2; // The 1-based index of the starting line in the source text // where the issue occurs, or 0 if unknown. int32 line = 3; // The 0-based index of the starting position within the line of source text // where the issue occurs. Only meaningful if line is nonzero. int32 column = 4; } ================================================ FILE: api/vendor/google/api/expr/v1alpha1/value.proto ================================================ // Copyright 2024 Google LLC // // 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. syntax = "proto3"; package google.api.expr.v1alpha1; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/api/expr/v1alpha1;expr"; option java_multiple_files = true; option java_outer_classname = "ValueProto"; option java_package = "com.google.api.expr.v1alpha1"; import "google/protobuf/any.proto"; import "google/protobuf/struct.proto"; // Contains representations for CEL runtime values. // Represents a CEL value. // // This is similar to `google.protobuf.Value`, but can represent CEL's full // range of values. message Value { // Required. The valid kinds of values. oneof kind { // Null value. google.protobuf.NullValue null_value = 1; // Boolean value. bool bool_value = 2; // Signed integer value. int64 int64_value = 3; // Unsigned integer value. uint64 uint64_value = 4; // Floating point value. double double_value = 5; // UTF-8 string value. string string_value = 6; // Byte string value. bytes bytes_value = 7; // An enum value. EnumValue enum_value = 9; // The proto message backing an object value. google.protobuf.Any object_value = 10; // Map value. MapValue map_value = 11; // List value. ListValue list_value = 12; // Type value. string type_value = 15; } } // An enum value. message EnumValue { // The fully qualified name of the enum type. string type = 1; // The value of the enum. int32 value = 2; } // A list. // // Wrapped in a message so 'not set' and empty can be differentiated, which is // required for use in a 'oneof'. message ListValue { // The ordered values in the list. repeated Value values = 1; } // A map. // // Wrapped in a message so 'not set' and empty can be differentiated, which is // required for use in a 'oneof'. message MapValue { // An entry in the map. message Entry { // The key. // // Must be unique with in the map. // Currently only boolean, int, uint, and string values can be keys. Value key = 1; // The value. Value value = 2; } // The set of map entries. // // CEL has fewer restrictions on keys, so a protobuf map representation // cannot be used. repeated Entry entries = 1; } ================================================ FILE: api/vendor/google/rpc/status.proto ================================================ // Copyright 2020 Google LLC // // 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. syntax = "proto3"; package google.rpc; option cc_enable_arenas = true; option go_package = "google.golang.org/genproto/googleapis/rpc/status;status"; option java_multiple_files = true; option java_outer_classname = "StatusProto"; option java_package = "com.google.rpc"; option objc_class_prefix = "RPC"; import "google/protobuf/any.proto"; // The `Status` type defines a logical error model that is suitable for // different programming environments, including REST APIs and RPC APIs. It is // used by [gRPC](https://github.com/grpc). Each `Status` message contains // three pieces of data: error code, error message, and error details. // // You can find out more about this error model and how to work with it in the // [API Design Guide](https://cloud.google.com/apis/design/errors). message Status { // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. int32 code = 1; // A developer-facing error message, which should be in English. Any // user-facing error message should be localized and sent in the // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. string message = 2; // A list of messages that carry the error details. There is a common set of // message types for APIs to use. repeated google.protobuf.Any details = 3; } ================================================ FILE: cmd/installer/cmd/imager/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package imager implements the imager command. package imager import ( "bytes" "context" "fmt" "os" "runtime" "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/installer/pkg/install" "github.com/siderolabs/talos/pkg/archiver" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/imager" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/overlay" "github.com/siderolabs/talos/pkg/reporter" ) var cmdFlags struct { Platform string Arch string // Insecure can be set to true to force pull from insecure registry. Insecure bool ExtraKernelArgs []string MetaValues install.MetaValues SystemExtensionImages []string BaseInstallerImage string ImageCache string EmbeddedConfigPath string OutputPath string OutputKind string TarToStdout bool OverlayName string OverlayImage string OverlayOptions []string // Only used when generating a secure boot iso without also providing a secure boot database. SecurebootIncludeWellKnownCerts bool } // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "imager |-", Short: "Generate various boot assets and images.", Long: ``, Args: cobra.ExactArgs(1), SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), func(ctx context.Context) error { report := reporter.New() report.Report(reporter.Update{ Message: "assembling the finalized profile...", Status: reporter.StatusRunning, }) baseProfile := args[0] var prof profile.Profile if baseProfile == "-" { if err := yaml.NewDecoder(os.Stdin).Decode(&prof); err != nil { return err } } else { prof = profile.Profile{ BaseProfileName: baseProfile, Arch: cmdFlags.Arch, Platform: cmdFlags.Platform, Customization: profile.CustomizationProfile{ ExtraKernelArgs: cmdFlags.ExtraKernelArgs, MetaContents: cmdFlags.MetaValues.GetMetaValues(), }, } extraOverlayOptions := overlay.ExtraOptions{} for _, option := range cmdFlags.OverlayOptions { if strings.HasPrefix(option, "@") { data, err := os.ReadFile(option[1:]) if err != nil { return err } decoder := yaml.NewDecoder(bytes.NewReader(data)) decoder.KnownFields(true) if err := decoder.Decode(&extraOverlayOptions); err != nil { return err } continue } k, v, _ := strings.Cut(option, "=") if strings.HasPrefix(v, "@") { data, err := os.ReadFile(v[1:]) if err != nil { return err } v = string(data) } extraOverlayOptions[k] = v } if cmdFlags.OverlayName != "" || cmdFlags.OverlayImage != "" { prof.Overlay = &profile.OverlayOptions{ Name: cmdFlags.OverlayName, Image: profile.ContainerAsset{ ImageRef: cmdFlags.OverlayImage, }, ExtraOptions: extraOverlayOptions, } prof.Input.OverlayInstaller.ImageRef = cmdFlags.OverlayImage } prof.Input.SystemExtensions = xslices.Map( cmdFlags.SystemExtensionImages, func(imageRef string) profile.ContainerAsset { return profile.ContainerAsset{ ImageRef: imageRef, ForceInsecure: cmdFlags.Insecure, } }, ) if cmdFlags.OutputKind != "" { outKind, err := profile.OutputKindString(cmdFlags.OutputKind) if err != nil { return err } prof.Output.Kind = outKind } if cmdFlags.BaseInstallerImage != "" { prof.Input.BaseInstaller = profile.ContainerAsset{ ImageRef: cmdFlags.BaseInstallerImage, } } if cmdFlags.ImageCache != "" { parseOpts := []name.Option{name.StrictValidation} if cmdFlags.Insecure { parseOpts = append(parseOpts, name.Insecure) } if _, err := name.ParseReference(cmdFlags.ImageCache, parseOpts...); err == nil { prof.Input.ImageCache = profile.ContainerAsset{ ImageRef: cmdFlags.ImageCache, } } else { prof.Input.ImageCache = profile.ContainerAsset{ OCIPath: cmdFlags.ImageCache, } } } if cmdFlags.Insecure { prof.Input.BaseInstaller.ForceInsecure = cmdFlags.Insecure prof.Input.ImageCache.ForceInsecure = cmdFlags.Insecure } if cmdFlags.SecurebootIncludeWellKnownCerts { if prof.Input.SecureBoot == nil { prof.Input.SecureBoot = &profile.SecureBootAssets{} } prof.Input.SecureBoot.IncludeWellKnownCerts = true } if cmdFlags.EmbeddedConfigPath != "" { data, err := os.ReadFile(cmdFlags.EmbeddedConfigPath) if err != nil { return fmt.Errorf("error reading embedded config file: %w", err) } prof.Customization.EmbeddedMachineConfiguration = string(data) } } if err := os.MkdirAll(cmdFlags.OutputPath, 0o755); err != nil { return err } imager, err := imager.New(prof) if err != nil { return err } if _, err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil { report.Report(reporter.Update{ Message: err.Error(), Status: reporter.StatusError, }) return err } if cmdFlags.TarToStdout { return archiver.TarGz(ctx, cmdFlags.OutputPath, os.Stdout) } return nil }) }, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } } func init() { rootCmd.PersistentFlags().StringVar(&cmdFlags.Platform, "platform", "", "The value of "+constants.KernelParamPlatform) rootCmd.PersistentFlags().StringVar(&cmdFlags.Arch, "arch", runtime.GOARCH, "The target architecture") rootCmd.PersistentFlags().StringVar(&cmdFlags.BaseInstallerImage, "base-installer-image", "", "Base installer image to use") rootCmd.PersistentFlags().StringVar(&cmdFlags.ImageCache, "image-cache", "", "Image cache container image or oci path") rootCmd.PersistentFlags().BoolVar(&cmdFlags.Insecure, "insecure", false, "Pull assets from insecure registry") rootCmd.PersistentFlags().StringArrayVar(&cmdFlags.ExtraKernelArgs, "extra-kernel-arg", []string{}, "Extra argument to pass to the kernel") rootCmd.PersistentFlags().Var(&cmdFlags.MetaValues, "meta", "A key/value pair for META") rootCmd.PersistentFlags().StringArrayVar(&cmdFlags.SystemExtensionImages, "system-extension-image", []string{}, "The image reference to the system extension to install") rootCmd.PersistentFlags().StringVar(&cmdFlags.OutputPath, "output", "/out", "The output directory path") rootCmd.PersistentFlags().StringVar(&cmdFlags.OutputKind, "output-kind", "", "Override output kind") rootCmd.PersistentFlags().BoolVar(&cmdFlags.TarToStdout, "tar-to-stdout", false, "Tar output and send to stdout") rootCmd.PersistentFlags().StringVar(&cmdFlags.OverlayName, "overlay-name", "", "The name of the overlay to use") rootCmd.PersistentFlags().StringVar(&cmdFlags.OverlayImage, "overlay-image", "", "The image reference to the overlay") rootCmd.PersistentFlags().StringArrayVar(&cmdFlags.OverlayOptions, "overlay-option", []string{}, "Extra options to pass to the overlay") rootCmd.PersistentFlags().StringVar(&cmdFlags.EmbeddedConfigPath, "embedded-config-path", "", "Path to a file containing the machine configuration to embed into the image") rootCmd.PersistentFlags().BoolVar( &cmdFlags.SecurebootIncludeWellKnownCerts, "secureboot-include-well-known-certs", false, "Include well-known (Microsoft) UEFI certificates when generating a secure boot database") } ================================================ FILE: cmd/installer/cmd/installer/install.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package installer import ( "context" "errors" "fmt" "log" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/installer/pkg/install" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/version" ) // installCmd represents the installation command. var installCmd = &cobra.Command{ Use: "install", Short: "", Long: ``, RunE: func(cmd *cobra.Command, args []string) (err error) { return runInstallCmd(cmd.Context()) }, } func init() { rootCmd.AddCommand(installCmd) } //nolint:gocyclo func runInstallCmd(ctx context.Context) (err error) { log.Printf("running Talos installer %s", version.NewVersion().Tag) mode := install.ModeInstall if options.Upgrade { mode = install.ModeUpgrade } p, err := platform.NewPlatform(options.Platform) if err != nil { return err } config, err := configloader.NewFromStdin() if err != nil { if errors.Is(err, configloader.ErrNoConfig) { log.Printf("machine configuration missing, skipping validation") // machine configuration can be only missing while running an upgrade in maintenance mode, assume that we should follow GrubUseUKICmdline options.GrubUseUKICmdline = true } else { return fmt.Errorf("error loading machine configuration: %w", err) } } else { var warnings []string warnings, err = config.Validate(p.Mode()) if err != nil { return fmt.Errorf("machine configuration is invalid: %w", err) } if len(warnings) > 0 { log.Printf("WARNING: config validation:") for _, warning := range warnings { log.Printf(" %s", warning) } } if config.Machine() != nil && config.Machine().Install().LegacyBIOSSupport() { options.LegacyBIOSSupport = true } // if we don't have v1alpha1 config (we are in maintenance mode), // or if we have v1alpha1 config, and GrubUseUKICmdline is set to true, // then we should set the option to true if config.Machine() == nil || config.Machine().Install().GrubUseUKICmdline() { options.GrubUseUKICmdline = true } } return install.Install(ctx, p, mode, options) } ================================================ FILE: cmd/installer/cmd/installer/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package installer implements the installer command. package installer import ( "fmt" "os" "runtime" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/installer/pkg/install" "github.com/siderolabs/talos/pkg/machinery/constants" ) // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "installer", Short: "", Long: ``, } func setFlagsFromEnvironment() error { if metaEnvBase64 := os.Getenv(constants.MetaValuesEnvVar); metaEnvBase64 != "" { if err := options.MetaValues.Decode(metaEnvBase64); err != nil { return err } } return nil } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { // Set defaults for flags from the environment variables. if err := setFlagsFromEnvironment(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } if err := rootCmd.Execute(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } var options = &install.Options{} func init() { rootCmd.PersistentFlags().StringVar(&options.ConfigSource, "config", "", "The value of "+constants.KernelParamConfig) rootCmd.PersistentFlags().StringVar(&options.DiskPath, "disk", "", "The path to the disk to install to") rootCmd.PersistentFlags().StringVar(&options.Platform, "platform", "", "The value of "+constants.KernelParamPlatform) rootCmd.PersistentFlags().StringVar(&options.Arch, "arch", runtime.GOARCH, "The target architecture") rootCmd.PersistentFlags().StringArrayVar(&options.ExtraKernelArgs, "extra-kernel-arg", []string{}, "Extra argument to pass to the kernel") rootCmd.PersistentFlags().BoolVar(&options.Upgrade, "upgrade", false, "Indicates that the install is being performed by an upgrade") rootCmd.PersistentFlags().BoolVar(&options.Force, "force", false, "Indicates that the install should forcefully format the partition") rootCmd.PersistentFlags().BoolVar(&options.Zero, "zero", false, "Indicates that the install should write zeros to the disk before installing") rootCmd.PersistentFlags().Var(&options.MetaValues, "meta", "A key/value pair for META") } ================================================ FILE: cmd/installer/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package installer provides the installer implementation. package main import ( "os" "path/filepath" "github.com/siderolabs/talos/cmd/installer/cmd/imager" "github.com/siderolabs/talos/cmd/installer/cmd/installer" ) func main() { switch filepath.Base(os.Args[0]) { case "imager": imager.Execute() default: installer.Execute() } } ================================================ FILE: cmd/installer/pkg/install/errata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install import ( "context" "fmt" "log" "os" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) // errataNetIfnames appends the `net.ifnames=0` kernel parameter to the kernel command line if upgrading // from an old enough version of Talos. func (i *Installer) errataNetIfnames(talosVersion *compatibility.TalosVersion) { if i.cmdline.Get(constants.KernelParamNetIfnames).First() != nil { // net.ifnames is already set, nothing to do return } oldTalos := upgradeFromPreIfnamesTalos(talosVersion) if oldTalos { log.Printf("appending net.ifnames=0 to the kernel command line") i.cmdline.Append(constants.KernelParamNetIfnames, "0") } } func readHostTalosVersion() (*compatibility.TalosVersion, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() if _, err := os.Stat(constants.MachineSocketPath); err != nil { // can't read Talos version return nil, nil } c, err := client.New(ctx, client.WithUnixSocket(constants.MachineSocketPath), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) if err != nil { return nil, fmt.Errorf("error connecting to the machine service: %w", err) } defer c.Close() //nolint:errcheck // inject "fake" authorization ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(constants.APIAuthzRoleMetadataKey, string(role.Admin))) resp, err := c.Version(ctx) if err != nil { return nil, fmt.Errorf("error getting Talos version: %w", err) } hostVersion := unpack(resp.Messages) talosVersion, err := compatibility.ParseTalosVersion(hostVersion.Version) if err != nil { return nil, fmt.Errorf("error parsing Talos version: %w", err) } return talosVersion, nil } func upgradeFromPreIfnamesTalos(talosVersion *compatibility.TalosVersion) bool { if talosVersion == nil { // old Talos version, include fallback return true } return talosVersion.DisablePredictableNetworkInterfaces() } ================================================ FILE: cmd/installer/pkg/install/install.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package install provides the installation routine. package install import ( "bytes" "context" "errors" "fmt" "io" "log" "os" "path/filepath" "slices" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" bootloaderpkg "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" bootloaderoptions "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/meta" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/imager/overlay/executor" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/kernel" metaconsts "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/overlay" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/makefs" ) // Options represents the set of options available for an install. type Options struct { ConfigSource string // Can be an actual disk path or a file representing a disk image. DiskPath string Platform string Arch string ExtraKernelArgs []string Upgrade bool Force bool Zero bool LegacyBIOSSupport bool GrubUseUKICmdline bool MetaValues MetaValues OverlayInstaller overlay.Installer[overlay.ExtraOptions] OverlayName string OverlayExtractedDir string ExtraOptions overlay.ExtraOptions ImageCachePath string ImageCacheSize int64 // Options specific for the image creation mode. ImageSecureboot bool DiskImageBootloader string Version string BootAssets bootloaderoptions.BootAssets Printf func(string, ...any) MountPrefix string } // Mode is the install mode. type Mode int const ( // ModeInstall is the install mode. ModeInstall Mode = iota // ModeUpgrade is the upgrade mode. ModeUpgrade // ModeImage is the image creation mode. ModeImage ) // IsImage returns true if the mode is image creation. func (m Mode) IsImage() bool { return m == ModeImage } const typeGPT = "gpt" // diskImageLabel is used as a label to generate a deterministic GPT UUID for disk images. const diskImageLabel = "talos-image-disk" // Install installs Talos. // //nolint:gocyclo func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options) error { if overlayPresent() { extraOptionsBytes, err := os.ReadFile(constants.ImagerOverlayExtraOptionsPath) if err != nil { return err } var extraOptions overlay.ExtraOptions decoder := yaml.NewDecoder(bytes.NewReader(extraOptionsBytes)) decoder.KnownFields(true) if err := decoder.Decode(&extraOptions); err != nil { return fmt.Errorf("failed to decode extra options: %w", err) } opts.OverlayInstaller = executor.New(constants.ImagerOverlayInstallerDefaultPath) opts.ExtraOptions = extraOptions } // NOTE: this is legacy code which is only used when running in GRUB mode with GrubUseUKICmdline set to false. cmdline := procfs.NewCmdline("") cmdline.Append(constants.KernelParamPlatform, p.Name()) if opts.ConfigSource != "" { cmdline.Append(constants.KernelParamConfig, opts.ConfigSource) } cmdline.SetAll(p.KernelArgs(opts.Arch, quirks.Quirks{}).Strings()) // first defaults, then extra kernel args to allow extra kernel args to override defaults if err := cmdline.AppendAll(kernel.DefaultArgs(quirks.Quirks{})); err != nil { return err } if opts.OverlayInstaller != nil { overlayOpts, getOptsErr := opts.OverlayInstaller.GetOptions(ctx, opts.ExtraOptions) if getOptsErr != nil { return fmt.Errorf("failed to get overlay installer options: %w", getOptsErr) } opts.OverlayName = overlayOpts.Name cmdline.SetAll(overlayOpts.KernelArgs) } // preserve console=ttyS0 if it was already present in cmdline for metal platform existingCmdline := procfs.ProcCmdline() if *existingCmdline.Get(constants.KernelParamPlatform).First() == constants.PlatformMetal && existingCmdline.Get("console").Contains("ttyS0") { if !slices.Contains(opts.ExtraKernelArgs, "console=ttyS0") { cmdline.Append("console", "ttyS0") } } if err := cmdline.AppendAll( opts.ExtraKernelArgs, procfs.WithOverwriteArgs("console"), procfs.WithOverwriteArgs(constants.KernelParamPlatform), procfs.WithDeleteNegatedArgs(), ); err != nil { return err } i, err := NewInstaller(ctx, cmdline, mode, opts) if err != nil { return err } if err = i.Install(ctx, mode); err != nil { return err } i.options.Printf("installation of %s complete", version.Tag) return nil } // Installer represents the installer logic. It serves as the entrypoint to all // installation methods. type Installer struct { cmdline *procfs.Cmdline options *Options } // NewInstaller initializes and returns an Installer. func NewInstaller(ctx context.Context, cmdline *procfs.Cmdline, mode Mode, opts *Options) (i *Installer, err error) { i = &Installer{ cmdline: cmdline, options: opts, } if i.options.Version == "" { i.options.Version = version.Tag } if i.options.Printf == nil { i.options.Printf = log.Printf } if mode == ModeUpgrade && i.options.Force { i.options.Printf("system disk wipe on upgrade is not supported anymore, option ignored") } if i.options.Zero && mode != ModeInstall { i.options.Printf("zeroing of the disk is only supported for the initial installation, option ignored") } i.options.BootAssets.FillDefaults(opts.Arch) return i, nil } // detectBootloader detects the bootloader to use based on the mode. func (i *Installer) detectBootloader(mode Mode) (bootloaderpkg.Bootloader, error) { switch mode { case ModeInstall: return bootloaderpkg.NewAuto(), nil case ModeUpgrade: return bootloaderpkg.Probe(i.options.DiskPath, bootloaderoptions.ProbeOptions{ // the disk is already locked BlockProbeOptions: []blkid.ProbeOption{ blkid.WithSkipLocking(true), }, Logger: log.Printf, }) case ModeImage: return bootloaderpkg.New(i.options.DiskImageBootloader, i.options.Version, i.options.Arch) default: return nil, fmt.Errorf("unknown image mode: %d", mode) } } // diskOperations performs any disk operations required before installation. // //nolint:gocyclo func (i *Installer) diskOperations(mode Mode, bd *block.Device, info *blkid.Info) error { switch mode { case ModeInstall: if !i.options.Zero && !i.options.Force { // verify that the disk is either empty or has an empty GPT partition table, otherwise fail the install switch { case info.Name == "": // empty, ok case info.Name == typeGPT && len(info.Parts) == 0: // GPT, no partitions, ok default: return fmt.Errorf("disk %s is not empty, skipping install, detected %q", i.options.DiskPath, info.Name) } } else { // zero the disk if err := bd.FastWipe(); err != nil { return fmt.Errorf("failed to zero blockdevice %s: %w", i.options.DiskPath, err) } } return nil case ModeUpgrade: // on upgrade, we don't touch the disk partitions, but we need to verify that the disk has the expected GPT partition table if info.Name != typeGPT { return fmt.Errorf("disk %s has an unexpected format %q", i.options.DiskPath, info.Name) } return nil case ModeImage: // no disk operations required for image creation return nil default: return fmt.Errorf("unknown image mode: %d", mode) } } // blockDeviceData opens and locks the block device and probes it. // the caller is responsible for unlocking and closing the block device. func (i *Installer) blockDeviceData(mode Mode) (*block.Device, *blkid.Info, error) { // open and lock the blockdevice for the installation disk for the whole duration of the installer bd, err := block.NewFromPath(i.options.DiskPath, block.OpenForWrite()) if err != nil { return nil, nil, fmt.Errorf("failed to open blockdevice %s: %w", i.options.DiskPath, err) } if err = bd.Lock(true); err != nil { bd.Close() //nolint:errcheck return nil, nil, fmt.Errorf("failed to lock blockdevice %s: %w", i.options.DiskPath, err) } switch mode { case ModeInstall, ModeUpgrade: info, err := blkid.Probe(bd.File(), blkid.WithSkipLocking(true)) if err != nil { return nil, nil, fmt.Errorf("failed to probe blockdevice %s: %w", i.options.DiskPath, err) } return bd, info, nil case ModeImage: info, err := blkid.ProbePath(i.options.DiskPath, blkid.WithSkipLocking(true)) if err != nil { return nil, nil, fmt.Errorf("failed to probe blockdevice %s: %w", i.options.DiskPath, err) } return bd, info, nil default: return nil, nil, fmt.Errorf("unknown image mode: %d", mode) } } // Install fetches the necessary data locations and copies or extracts // to the target locations. // //nolint:gocyclo,cyclop func (i *Installer) Install(ctx context.Context, mode Mode) (err error) { // pre-flight checks, erratas hostTalosVersion, err := readHostTalosVersion() if err != nil { return err } if mode == ModeUpgrade { i.errataNetIfnames(hostTalosVersion) } if err = i.runPreflightChecks(mode); err != nil { return err } bootlder, err := i.detectBootloader(mode) if err != nil { return fmt.Errorf("failed to detect bootloader: %w", err) } bootPartitions, err := i.getBootPartitions(ctx, mode, bootlder) if err != nil { return fmt.Errorf("failed to get bootloader partitions: %w", err) } // create an exclusive lock on the disk and perform necessary disk operations // this lock will be held for the whole duration of the installation // this is necessary to prevent other processes from interfering with the installation like udevd // otherwise udevd might remove and re-add partition device nodes while we are using them bd, info, err := i.blockDeviceData(mode) if err != nil { return fmt.Errorf("failed to get blockdevice data: %w", err) } defer bd.Unlock() //nolint:errcheck defer bd.Close() //nolint:errcheck if err = i.diskOperations(mode, bd, info); err != nil { return fmt.Errorf("failed to perform disk operations: %w", err) } // create partitions and re-probe the device partitionOptions, err := i.createPartitions(ctx, mode, bd, hostTalosVersion, bootPartitions) if err != nil { return fmt.Errorf("failed to create partitions: %w", err) } if err := i.formatPartitions(ctx, mode, partitionOptions); err != nil { return fmt.Errorf("failed to format partitions: %w", err) } // re-probe the device to get updated partition information info, err = blkid.ProbePath(i.options.DiskPath, blkid.WithSkipLocking(true)) if err != nil { return fmt.Errorf("failed to probe blockdevice %s: %w", i.options.DiskPath, err) } bootInstallResult, err := i.installBootloader(ctx, mode, bootlder, info) if err != nil { return fmt.Errorf("failed to install bootloader: %w", err) } if err = i.handleMeta(ctx, mode, bootInstallResult.PreviousLabel, info); err != nil { return fmt.Errorf("failed to handle META partition: %w", err) } return nil } //nolint:gocyclo,cyclop func (i *Installer) handleMeta(ctx context.Context, mode Mode, previousLabel string, info *blkid.Info) error { switch mode { case ModeInstall, ModeUpgrade: var metaPartitionName string for _, partition := range info.Parts { if pointer.SafeDeref(partition.PartitionLabel) == constants.MetaPartitionLabel { metaPartitionName = partitioning.DevName(i.options.DiskPath, partition.PartitionIndex) break } } if metaPartitionName == "" { return errors.New("failed to detect META partition") } metaState, err := meta.New(ctx, nil, meta.WithPrinter(i.options.Printf), meta.WithFixedPath(metaPartitionName)) if err != nil { return fmt.Errorf("failed to open META: %w", err) } if mode == ModeUpgrade { if ok, err := metaState.SetTag(ctx, metaconsts.Upgrade, previousLabel); !ok || err != nil { return fmt.Errorf("failed to set upgrade tag: %q", previousLabel) } } for _, v := range i.options.MetaValues.values { if ok, err := metaState.SetTag(ctx, v.Key, v.Value); !ok || err != nil { return fmt.Errorf("failed to set meta tag: %q -> %q", v.Key, v.Value) } } if err := metaState.Flush(); err != nil { return fmt.Errorf("failed to flush META: %w", err) } return nil case ModeImage: if i.options.MetaValues.values == nil { return nil } f, err := os.OpenFile(i.options.DiskPath, os.O_RDWR, 0) if err != nil { return fmt.Errorf("failed to open image file %s: %w", i.options.DiskPath, err) } defer f.Close() //nolint:errcheck gptdev, err := gpt.DeviceFromFile(f) if err != nil { return fmt.Errorf("failed to initialize GPT device from image file %s: %w", i.options.DiskPath, err) } pt, err := gpt.Read(gptdev) if err != nil { return fmt.Errorf("failed to read GPT from image file %s: %w", i.options.DiskPath, err) } metaPartitionInfo := xslices.Filter(info.Parts, func(pr blkid.NestedProbeResult) bool { return pointer.SafeDeref(pr.PartitionLabel) == constants.MetaPartitionLabel }) if len(metaPartitionInfo) == 0 { return errors.New("failed to detect META partition") } metaPartitionIndex := int(metaPartitionInfo[0].PartitionIndex) - 1 metaFilePath := filepath.Join(i.options.MountPrefix, "meta.img") if err := utils.CreateRawDisk(i.options.Printf, metaFilePath, int64(metaPartitionInfo[0].PartitionSize), true); err != nil { return fmt.Errorf("failed to create meta image file: %w", err) } metaState, err := meta.New(ctx, nil, meta.WithPrinter(i.options.Printf), meta.WithFixedPath(metaFilePath)) if err != nil { return fmt.Errorf("failed to open META: %w", err) } for _, v := range i.options.MetaValues.values { if ok, err := metaState.SetTag(ctx, v.Key, v.Value); !ok || err != nil { return fmt.Errorf("failed to set meta tag: %q -> %q", v.Key, v.Value) } } if err := metaState.Flush(); err != nil { return fmt.Errorf("failed to flush META: %w", err) } metaFile, err := os.Open(metaFilePath) if err != nil { return fmt.Errorf("failed to open meta image file: %w", err) } defer metaFile.Close() //nolint:errcheck pw, pSize, err := pt.PartitionWriter(metaPartitionIndex) if err != nil { return fmt.Errorf("failed to get partition writer for META partition: %w", err) } metaFileInfo, err := metaFile.Stat() if err != nil { return fmt.Errorf("failed to stat meta image file: %w", err) } if metaFileInfo.Size() != int64(pSize) { return fmt.Errorf("META partition size mismatch: image size %d, partition size %d", metaFileInfo.Size(), pSize) } if _, err := io.Copy(pw, metaFile); err != nil { return fmt.Errorf("failed to write back META partition data: %w", err) } return nil default: return fmt.Errorf("unknown image mode: %d", mode) } } func (i *Installer) generateBootloaderOptions(ctx context.Context, mode Mode, info *blkid.Info) bootloaderoptions.InstallOptions { return bootloaderoptions.InstallOptions{ BootDisk: i.options.DiskPath, Arch: i.options.Arch, Cmdline: i.cmdline.String(), GrubUseUKICmdline: i.options.GrubUseUKICmdline, Version: i.options.Version, ImageMode: mode.IsImage(), BootAssets: i.options.BootAssets, Printf: i.options.Printf, MountPrefix: i.options.MountPrefix, BlkidInfo: info, ExtraInstallStep: func() error { if i.options.OverlayInstaller != nil { i.options.Printf("running overlay installer %q", i.options.OverlayName) if err := i.options.OverlayInstaller.Install(ctx, overlay.InstallOptions[overlay.ExtraOptions]{ InstallDisk: i.options.DiskPath, MountPrefix: i.options.MountPrefix, ArtifactsPath: filepath.Join(i.options.OverlayExtractedDir, constants.ImagerOverlayArtifactsPath), ExtraOptions: i.options.ExtraOptions, }); err != nil { return fmt.Errorf("failed to run overlay installer: %w", err) } } return nil }, } } func (i *Installer) getBootPartitions(ctx context.Context, mode Mode, bootloader bootloaderpkg.Bootloader) ([]partition.Options, error) { if mode == ModeUpgrade { return nil, nil // no need to generate boot partitions on upgrade } bootloaderOptions := i.generateBootloaderOptions(ctx, mode, nil) partitionOptions, err := bootloader.GenerateAssets(bootloaderOptions) if err != nil { return nil, fmt.Errorf("failed to generate bootloader assets: %w", err) } efiPartitionPresent := slices.ContainsFunc(partitionOptions, func(p partition.Options) bool { return p.Label == constants.EFIPartitionLabel && p.SourceDirectory != "" }) // We need to move out bootloaderOptions.MountPrefix+/boot/EFI to bootloaderOptions.MountPrefix+/EFI otherwise // BOOT partition will be populated with EFI directory inside boot directory. if efiPartitionPresent { if err := os.Rename(filepath.Join(bootloaderOptions.MountPrefix, constants.EFIMountPoint), filepath.Join(bootloaderOptions.MountPrefix, "EFI")); err != nil { return nil, fmt.Errorf("failed to move EFI directory: %w", err) } } return partitionOptions, nil } func (i *Installer) installBootloader(ctx context.Context, mode Mode, bootloader bootloaderpkg.Bootloader, info *blkid.Info) (*bootloaderoptions.InstallResult, error) { installOptions := i.generateBootloaderOptions(ctx, mode, info) switch mode { case ModeInstall: return bootloader.Install(installOptions) case ModeUpgrade: return bootloader.Upgrade(installOptions) case ModeImage: return &bootloaderoptions.InstallResult{}, nil // bootloader already installed in image mode during partition creation default: return nil, fmt.Errorf("unknown image mode: %d", mode) } } //nolint:gocyclo,cyclop func (i *Installer) createPartitions(ctx context.Context, mode Mode, bd *block.Device, hostTalosVersion *compatibility.TalosVersion, bootPartitions []partition.Options) ([]partition.Options, error) { var ( gptdev gpt.Device err error ) switch mode { case ModeInstall: gptdev, err = gpt.DeviceFromBlockDevice(bd) if err != nil { return nil, fmt.Errorf("failed to initialize GPT device from blockdevice %s: %w", i.options.DiskPath, err) } case ModeUpgrade: return nil, nil // no partitioning on upgrade case ModeImage: f, err := os.OpenFile(i.options.DiskPath, os.O_RDWR, 0) if err != nil { return nil, fmt.Errorf("failed to open image file %s: %w", i.options.DiskPath, err) } defer f.Close() //nolint:errcheck gptdev, err = gpt.DeviceFromFile(f) if err != nil { return nil, fmt.Errorf("failed to initialize GPT device from image file %s: %w", i.options.DiskPath, err) } default: return nil, fmt.Errorf("unknown image mode: %d", mode) } partitions, gptOptions, err := i.getPartitionOptions(ctx, mode, hostTalosVersion, bootPartitions) if err != nil { return nil, fmt.Errorf("failed to get partition options: %w", err) } pt, err := gpt.New(gptdev, gptOptions...) if err != nil { return nil, fmt.Errorf("failed to initialize GPT: %w", err) } for _, p := range partitions { size := p.Size if size == 0 { size = pt.LargestContiguousAllocatable() } partitionTyp := uuid.MustParse(p.PartitionType) _, _, err = pt.AllocatePartition(size, p.PartitionLabel, partitionTyp, p.PartitionOpts...) if err != nil { return nil, fmt.Errorf("failed to allocate partition %s: %w", p.PartitionLabel, err) } i.options.Printf("created %s (%s) size %d bytes", p.PartitionLabel, p.PartitionType, size) } if err = pt.Write(); err != nil { return nil, fmt.Errorf("failed to write GPT: %w", err) } if err := gptdev.Sync(); err != nil { return nil, fmt.Errorf("failed to sync GPT device: %w", err) } return partitions, nil } // formatPartitions formats the created partitions populating them with filesystems and data as required. // //nolint:gocyclo func (i *Installer) formatPartitions(ctx context.Context, mode Mode, parts []partition.Options) error { switch mode { case ModeInstall: // format also populates partitions, so we need to make sure source directories are set for idx, p := range parts { devName := partitioning.DevName(i.options.DiskPath, uint(idx+1)) if err := partition.Format(ctx, devName, &p.FormatOptions, i.options.Version, i.options.Printf); err != nil { return fmt.Errorf("failed to format partition %s: %w", devName, err) } } return nil case ModeUpgrade: // no formatting on upgrade return nil case ModeImage: // format also populates partitions, so we need to make sure source directories are set f, err := os.OpenFile(i.options.DiskPath, os.O_RDWR, 0) if err != nil { return fmt.Errorf("failed to open image file %s: %w", i.options.DiskPath, err) } defer f.Close() //nolint:errcheck gptdev, err := gpt.DeviceFromFile(f) if err != nil { return fmt.Errorf("failed to initialize GPT device from image file %s: %w", i.options.DiskPath, err) } pt, err := gpt.Read(gptdev) if err != nil { return fmt.Errorf("failed to initialize GPT: %w", err) } for idx, p := range parts { if err := i.handlePartitionDataPopulation(ctx, idx, p, pt); err != nil { return fmt.Errorf("failed to handle partition data population for partition %s: %w", p.Label, err) } } if err := i.handleGrubBlocklist(gptdev, pt); err != nil { return fmt.Errorf("failed to handle GRUB blocklist: %w", err) } return nil default: return fmt.Errorf("unknown image mode: %d", mode) } } //nolint:gocyclo func (i *Installer) handlePartitionDataPopulation(ctx context.Context, idx int, p partition.Options, pt *gpt.Table) error { // skip data population for partitions without filesystem ie. partition.FilesystemTypeNone // or zeroed partitions ie. partition.FilesystemTypeZeroes if p.FileSystemType == partition.FilesystemTypeNone || p.FileSystemType == partition.FilesystemTypeZeroes { // no data population required return nil } partitionImageFile := filepath.Join(i.options.MountPrefix, p.Label+".img") if err := utils.CreateRawDisk(i.options.Printf, partitionImageFile, int64(p.Size), true); err != nil { return fmt.Errorf("failed to create raw disk for partition %s: %w", p.Label, err) } if p.SourceDirectory == "" { return fmt.Errorf("missing source directory for partition %s", p.Label) } // this ensures that the images are reproducible if err := utils.TouchFiles(i.options.Printf, p.SourceDirectory); err != nil { return fmt.Errorf("failed to touch files in source directory %s for partition %s: %w", p.SourceDirectory, p.Label, err) } if err := partition.Format(ctx, partitionImageFile, &p.FormatOptions, i.options.Version, i.options.Printf); err != nil { return fmt.Errorf("failed to format partition %s: %w", partitionImageFile, err) } partitionData, err := os.Open(partitionImageFile) if err != nil { return fmt.Errorf("failed to open partition image file %s: %w", partitionImageFile, err) } defer partitionData.Close() //nolint:errcheck w, size, err := pt.PartitionWriter(idx) if err != nil { return fmt.Errorf("failed to get partition writer for partition %s: %w", p.Label, err) } if size != int(p.Size) { return fmt.Errorf("partition size mismatch for partition %s: expected %d, got %d", p.Label, p.Size, size) } written, err := io.Copy(w, partitionData) if err != nil { return fmt.Errorf("failed to copy partition data for partition %s: %w", p.Label, err) } if written != int64(size) { return fmt.Errorf("partition data size mismatch for partition %s: expected %d, got %d", p.Label, size, written) } i.options.Printf("updated partition %s with %d bytes of data", p.Label, written) return nil } //nolint:gocyclo,cyclop func (i *Installer) handleGrubBlocklist(gptdev gpt.Device, pt *gpt.Table) error { if i.options.Arch != "amd64" { return nil } if i.options.DiskImageBootloader != profile.BootLoaderKindGrub.String() && i.options.DiskImageBootloader != profile.BootLoaderKindDualBoot.String() { return nil } sectorSize := gptdev.GetSectorSize() // Find the BIOS GRUB partition where the `core.img` is going to be written to. var ( biosPartitionIndex int biosPartition *gpt.Partition ) for idx, p := range pt.Partitions() { if p.Name == constants.BIOSGrubPartitionLabel { biosPartition = p biosPartitionIndex = idx break } } if biosPartition == nil { return fmt.Errorf("failed to find BOOT partition for GRUB blocklist handling") } // Patch boot.img and core.img with blocklist information. if err := grub.PatchBlocklistsForDiskImage(sectorSize, biosPartition.FirstLBA, i.options.MountPrefix); err != nil { return fmt.Errorf("failed to patch GRUB blocklists: %w", err) } coreImgData, err := os.ReadFile(filepath.Join(i.options.MountPrefix, "core.img")) if err != nil { return fmt.Errorf("failed to read core.img: %w", err) } w, size, err := pt.PartitionWriter(biosPartitionIndex) if err != nil { return fmt.Errorf("failed to get partition writer for partition %s: %w", biosPartition.Name, err) } if len(coreImgData) > size { return fmt.Errorf("core.img size (%d bytes) exceeds BIOS partition size (%d bytes)", len(coreImgData), size) } writtenSize, err := w.Write(coreImgData) if err != nil { return fmt.Errorf("failed to copy partition data for partition %s: %w", biosPartition.Name, err) } if writtenSize != len(coreImgData) { return fmt.Errorf("partition data size mismatch for partition %s: expected %d, got %d", biosPartition.Name, len(coreImgData), writtenSize) } i.options.Printf("embedded GRUB core.img into BIOS partition image (%d bytes)", len(coreImgData)) bootImg, err := os.Open(filepath.Join(i.options.MountPrefix, "boot.img")) if err != nil { return fmt.Errorf("failed to open boot.img for MBR write: %w", err) } defer bootImg.Close() //nolint:errcheck mbr := make([]byte, 446) if _, err := io.ReadFull(bootImg, mbr); err != nil { return fmt.Errorf("failed to read MBR from boot.img: %w", err) } written, err := gptdev.WriteAt(mbr, 0) if err != nil { return fmt.Errorf("failed to write MBR to image file %s: %w", i.options.DiskPath, err) } if written != len(mbr) { return fmt.Errorf("failed to write full MBR to image file %s: wrote %d bytes, expected %d bytes", i.options.DiskPath, written, len(mbr)) } i.options.Printf("wrote GRUB MBR to image file %s", i.options.DiskPath) return nil } // getPartitionOptions builds the complete list of partition options for an installation. // It combines the bootloader-specific partitions passed in via bootPartitions with the // system partitions defined by Talos and handling META and IMAGE cache. // It also generates GPT options required to create // the final partition layout. Mode-specific behavior is handled, // including configuration required for reproducible GUIDs when running in image mode. // //nolint:gocyclo func (i *Installer) getPartitionOptions(ctx context.Context, mode Mode, hostTalosVersion *compatibility.TalosVersion, bootPartitions []partition.Options) ([]partition.Options, []gpt.Option, error) { var partitionOffset uint64 if i.options.OverlayInstaller != nil { overlayOpts, getOptsErr := i.options.OverlayInstaller.GetOptions(ctx, i.options.ExtraOptions) if getOptsErr != nil { return nil, nil, fmt.Errorf("failed to get overlay installer options: %w", getOptsErr) } if overlayOpts.PartitionOptions.Offset != 0 { partitionOffset = overlayOpts.PartitionOptions.Offset } } var gptOptions []gpt.Option if partitionOffset != 0 { gptOptions = append(gptOptions, gpt.WithSkipLBAs(uint(partitionOffset))) } if i.options.LegacyBIOSSupport { gptOptions = append(gptOptions, gpt.WithMarkPMBRBootable()) } if mode == ModeImage { gptOptions = append(gptOptions, gpt.WithDiskGUID(makefs.GUIDFromLabel(diskImageLabel))) } quirk := quirks.New(i.options.Version) // boot partitions partitions := slices.Clone(bootPartitions) // META partition partitions = append(partitions, partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.MetaPartitionLabel)), ) legacyImage := mode == ModeImage && !quirks.New(i.options.Version).SkipDataPartitions() // compatibility when installing on Talos < 1.8 if legacyImage || (hostTalosVersion != nil && hostTalosVersion.PrecreateStatePartition()) { partitions = append(partitions, partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.StatePartitionLabel)), ) } if legacyImage { partitions = append(partitions, partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.EphemeralPartitionLabel)), ) } if i.options.ImageCachePath != "" { imageCachePartitionFormatOptions := []partition.FormatOption{ partition.WithLabel(constants.ImageCachePartitionLabel), partition.WithSourceDirectory(i.options.ImageCachePath), } if mode == ModeImage { imageCachePartitionFormatOptions = append(imageCachePartitionFormatOptions, partition.WithReproducible(), ) } imageCachePartition := partition.NewPartitionOptions( false, quirk, imageCachePartitionFormatOptions..., ) imageCachePartition.Size = uint64(i.options.ImageCacheSize) partitions = append(partitions, imageCachePartition) } if mode == ModeImage { partitions = xslices.Map(partitions, func(p partition.Options) partition.Options { partitionGUID := makefs.GUIDFromLabel(p.PartitionLabel) // Generate deterministic partition GUID from label for reproducible images p.PartitionOpts = append(p.PartitionOpts, gpt.WithUniqueGUID(partitionGUID)) return p }) } return partitions, gptOptions, nil } func (i *Installer) runPreflightChecks(mode Mode) error { if mode != ModeUpgrade { // pre-flight checks only apply to upgrades return nil } ctx, cancel := context.WithCancel(context.Background()) defer cancel() checks, err := NewPreflightChecks(ctx) if err != nil { return fmt.Errorf("error initializing pre-flight checks: %w", err) } defer checks.Close() //nolint:errcheck return checks.Run(ctx) } func overlayPresent() bool { _, err := os.Stat(constants.ImagerOverlayInstallerDefaultPath) return err == nil } ================================================ FILE: cmd/installer/pkg/install/meta_value.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install import ( "strings" "github.com/spf13/pflag" "github.com/siderolabs/talos/pkg/machinery/meta" ) // MetaValues is a list of MetaValue. type MetaValues struct { values meta.Values changed bool } // Interface check. var ( _ pflag.Value = &MetaValues{} _ pflag.SliceValue = &MetaValues{} ) // FromMeta returns a new MetaValues from a meta.Values. func FromMeta(values meta.Values) MetaValues { return MetaValues{values: values} } // Set implements pflag.Value. func (s *MetaValues) Set(val string) error { var v meta.Value if err := v.Parse(val); err != nil { return err } if !s.changed { s.values = meta.Values{v} } else { s.values = append(s.values, v) } s.changed = true return nil } // Type implements pflag.Value. func (s *MetaValues) Type() string { return "metaValueSlice" } // String implements pflag.Value. func (s *MetaValues) String() string { return "[" + strings.Join(s.GetSlice(), ",") + "]" } // Append implements pflag.SliceValue. func (s *MetaValues) Append(val string) error { var v meta.Value if err := v.Parse(val); err != nil { return err } s.values = append(s.values, v) return nil } // Replace implements pflag.SliceValue. func (s *MetaValues) Replace(val []string) error { out := make(meta.Values, len(val)) for i, pair := range val { var v meta.Value if err := v.Parse(pair); err != nil { return err } out[i] = v } s.values = out return nil } // GetSlice implements pflag.SliceValue. func (s *MetaValues) GetSlice() []string { out := make([]string, len(s.values)) for i, v := range s.values { out[i] = v.String() } return out } // Encode returns the encoded values. func (s *MetaValues) Encode() string { return s.values.Encode(false) } // Decode the values from the given string. func (s *MetaValues) Decode(val string) error { values, err := meta.DecodeValues(val) if err != nil { return err } s.values = values return nil } // GetMetaValues returns the wrapped meta.Values. func (s *MetaValues) GetMetaValues() meta.Values { return s.values } ================================================ FILE: cmd/installer/pkg/install/meta_value_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/installer/pkg/install" ) func TestMetaValues(t *testing.T) { t.Parallel() var s install.MetaValues require.NoError(t, s.Set("10=foo")) require.NoError(t, s.Append("20=bar")) assert.Equal(t, "[0xa=foo,0x14=bar]", s.String()) encoded := s.Encode() var s2 install.MetaValues require.NoError(t, s2.Decode(encoded)) assert.Equal(t, s.String(), s2.String()) } ================================================ FILE: cmd/installer/pkg/install/preflight.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install import ( "context" "fmt" "log" "os" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/machinery/version" ) // PreflightChecks runs the preflight checks. type PreflightChecks struct { disabled bool client *client.Client installerTalosVersion *compatibility.TalosVersion hostTalosVersion *compatibility.TalosVersion } // NewPreflightChecks initializes and returns the installation PreflightChecks. func NewPreflightChecks(ctx context.Context) (*PreflightChecks, error) { if _, err := os.Stat(constants.MachineSocketPath); err != nil { log.Printf("pre-flight checks disabled, as host Talos version is too old") return &PreflightChecks{disabled: true}, nil //nolint:nilerr } c, err := client.New(ctx, client.WithUnixSocket(constants.MachineSocketPath), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) if err != nil { return nil, fmt.Errorf("error connecting to the machine service: %w", err) } return &PreflightChecks{ client: c, }, nil } // Close closes the client. func (checks *PreflightChecks) Close() error { if checks.disabled { return nil } return checks.client.Close() } // Run the checks, return the error if the check fails. func (checks *PreflightChecks) Run(ctx context.Context) error { if checks.disabled { return nil } log.Printf("running pre-flight checks") // inject "fake" authorization ctx = metadata.NewOutgoingContext(ctx, metadata.Pairs(constants.APIAuthzRoleMetadataKey, string(role.Admin))) for _, check := range []func(context.Context) error{ checks.talosVersion, } { if err := check(ctx); err != nil { return fmt.Errorf("pre-flight checks failed: %w", err) } } log.Printf("all pre-flight checks successful") return nil } func (checks *PreflightChecks) talosVersion(ctx context.Context) error { resp, err := checks.client.Version(ctx) if err != nil { return fmt.Errorf("error getting Talos version: %w", err) } hostVersion := unpack(resp.Messages) log.Printf("host Talos version: %s", hostVersion.Version.Tag) checks.hostTalosVersion, err = compatibility.ParseTalosVersion(hostVersion.Version) if err != nil { return fmt.Errorf("error parsing host Talos version: %w", err) } checks.installerTalosVersion, err = compatibility.ParseTalosVersion(version.NewVersion()) if err != nil { return fmt.Errorf("error parsing installer Talos version: %w", err) } return checks.installerTalosVersion.UpgradeableFrom(checks.hostTalosVersion) } func unpack[T any](s []T) T { if len(s) != 1 { panic("unpack: slice length is not 1") } return s[0] } ================================================ FILE: cmd/talosctl/acompat/acompat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package acompat provides compatibility with gRPC 1.67.0 and later. package acompat import "os" func init() { if err := os.Setenv("GRPC_ENFORCE_ALPN_ENABLED", "false"); err != nil { panic(err) } } ================================================ FILE: cmd/talosctl/cmd/common/common.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package common provides common functionality for talosctl commands. // //nolint:revive package common // SuppressErrors is a flag to suppress printing errors after the command was run. var SuppressErrors = false ================================================ FILE: cmd/talosctl/cmd/completion.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cmd import ( "fmt" "os" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" ) // completionCmd represents the completion command. var completionCmd = &cobra.Command{ Use: "completion SHELL", Short: "Output shell completion code for the specified shell (bash, fish or zsh)", Long: `Output shell completion code for the specified shell (bash, fish or zsh). The shell code must be evaluated to provide interactive completion of talosctl commands. This can be done by sourcing it from the .bash_profile. Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2`, Example: `# Installing bash completion on macOS using homebrew ## If running Bash 3.2 included with macOS brew install bash-completion ## or, if running Bash 4.1+ brew install bash-completion@2 ## If talosctl is installed via homebrew, this should start working immediately. ## If you've installed via other means, you may need add the completion to your completion directory talosctl completion bash > $(brew --prefix)/etc/bash_completion.d/talosctl # Installing bash completion on Linux ## If bash-completion is not installed on Linux, please install the 'bash-completion' package ## via your distribution's package manager. ## Load the talosctl completion code for bash into the current shell source <(talosctl completion bash) ## Write bash completion code to a file and source if from .bash_profile talosctl completion bash > "${TALOS_HOME:-$HOME/.talos}/completion.bash.inc" printf ' # talosctl shell completion source "${TALOS_HOME:-$HOME/.talos}/completion.bash.inc" ' >> $HOME/.bash_profile source $HOME/.bash_profile # Load the talosctl completion code for fish[1] into the current shell talosctl completion fish | source # Set the talosctl completion code for fish[1] to autoload on startup talosctl completion fish > ~/.config/fish/completions/talosctl.fish # Load the talosctl completion code for zsh[1] into the current shell source <(talosctl completion zsh) # Set the talosctl completion code for zsh[1] to autoload on startup talosctl completion zsh > "${fpath[1]}/_talosctl"`, ValidArgs: []string{"bash", "fish", "zsh"}, Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { cli.Should(cmd.Usage()) os.Exit(1) } switch args[0] { case "bash": return rootCmd.GenBashCompletion(os.Stdout) case "fish": return rootCmd.GenFishCompletion(os.Stdout, true) case "zsh": err := rootCmd.GenZshCompletion(os.Stdout) // cobra does not hook the completion, so let's do it manually fmt.Printf("compdef _talosctl talosctl") return err default: return fmt.Errorf("unsupported shell %q", args[0]) } }, } func init() { rootCmd.AddCommand(completionCmd) } ================================================ FILE: cmd/talosctl/cmd/constants/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package constants const ( // ImageFactoryEmptySchematicID is the ID of an empty image factory schematic. ImageFactoryEmptySchematicID = "376567988ad370138ad8b2698212367b8edcb69b5fd68c80be1f2ec7d603b4ba" // ImageFactoryURL is the url of the Sidero hosted image factory. ImageFactoryURL = "https://factory.talos.dev/" ) ================================================ FILE: cmd/talosctl/cmd/docs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cmd provides the talosctl command implementation. package cmd import ( "bytes" "fmt" "io" "os" "path" "path/filepath" "strings" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func frontmatter(title, description string) string { var buf bytes.Buffer buf.WriteString("---\n") if err := yaml.NewEncoder(&buf).Encode(map[string]string{ "title": title, "description": description, }); err != nil { panic(err) } buf.WriteString("---\n") buf.WriteString("\n") buf.WriteString("\n\n") return buf.String() } func linkHandler(name string) string { base := strings.TrimSuffix(name, path.Ext(name)) base = strings.ReplaceAll(base, "_", "-") return "#" + strings.ToLower(base) } const cliDescription = "Talosctl CLI tool reference." var ( cliDocs bool configDocs bool ) // docsCmd represents the docs command. var docsCmd = &cobra.Command{ Use: "docs [flags]", Short: "Generate documentation for the CLI or config", Long: ``, Args: cobra.ExactArgs(1), Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { dir := args[0] if err := os.MkdirAll(dir, 0o777); err != nil { return fmt.Errorf("failed to create output directory %q", dir) } all := !cliDocs && !configDocs if cliDocs || all { w := &bytes.Buffer{} if err := GenMarkdownReference(rootCmd, w, linkHandler); err != nil { return fmt.Errorf("failed to generate docs: %w", err) } filename := filepath.Join(dir, "cli.md") f, err := os.Create(filename) if err != nil { return err } //nolint:errcheck defer f.Close() if _, err := io.WriteString(f, frontmatter("CLI", cliDescription)); err != nil { return err } if _, err := io.WriteString(f, w.String()); err != nil { return err } } if configDocs || all { for _, pkg := range []struct { name string fileDoc *encoder.FileDoc }{ { name: "network", fileDoc: network.GetFileDoc(), }, { name: "runtime", fileDoc: runtime.GetFileDoc(), }, { name: "siderolink", fileDoc: siderolink.GetFileDoc(), }, { name: "v1alpha1", fileDoc: v1alpha1.GetFileDoc(), }, { name: "extensions", fileDoc: extensions.GetFileDoc(), }, { name: "security", fileDoc: security.GetFileDoc(), }, { name: "block", fileDoc: block.GetFileDoc(), }, { name: "hardware", fileDoc: hardware.GetFileDoc(), }, { name: "cri", fileDoc: cri.GetFileDoc(), }, } { path := filepath.Join(dir, pkg.name) if err := os.MkdirAll(path, 0o777); err != nil { return fmt.Errorf("failed to create output directory %q", path) } if err := pkg.fileDoc.Write(path, frontmatter); err != nil { return fmt.Errorf("failed to generate docs: %w", err) } } } return nil }, } // GenMarkdownReference is the same as GenMarkdownTree, but // with custom filePrepender and linkHandler. // //nolint:gocyclo func GenMarkdownReference(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error { for _, c := range cmd.Commands() { // Generate docs for children of the cluster create command although the command itself is hidden. if cmd.Name() == "cluster" && c.Name() == "create" { if err := GenMarkdownReference(c, w, linkHandler); err != nil { return err } } if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { continue } if err := GenMarkdownReference(c, w, linkHandler); err != nil { return err } } // Skip generating docs for the cluster create command itself and only generate docs for children. // TODO: remove once "cluster create" is completely migrated to "cluster create dev". if cmd.Name() == "create" && cmd.Parent() != nil && cmd.Parent().Name() == "cluster" { return nil } return doc.GenMarkdownCustom(cmd, w, linkHandler) } func init() { docsCmd.Flags().BoolVar(&configDocs, "config", false, "generate documentation for the default configuration schema") docsCmd.Flags().BoolVar(&cliDocs, "cli", false, "generate documentation for the CLI") rootCmd.AddCommand(docsCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cluster implements "cluster" subcommands. package cluster import ( "path/filepath" "github.com/spf13/cobra" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/provision/providers" ) const ( // ProvisionerFlagName is the flag with which the provisioner is configured. ProvisionerFlagName = "provisioner" ) // Cmd represents the cluster command. var Cmd = &cobra.Command{ Use: "cluster", Short: "A collection of commands for managing local docker-based or QEMU-based clusters", } // CmdOps are the options for the cluster command. type CmdOps struct { StateDir string ClusterName string } // PersistentFlags are the persistent flags of the cluster command. var PersistentFlags CmdOps var provisionerName string var ( // DefaultStateDir is the default location of the cluster related file state. DefaultStateDir string // DefaultCNIDir is the default location of the CNI binaries. DefaultCNIDir string ) func init() { talosDir, err := clientconfig.GetTalosDirectory() if err == nil { DefaultStateDir = filepath.Join(talosDir, "clusters") DefaultCNIDir = filepath.Join(talosDir, "cni") } Cmd.PersistentFlags().StringVar(&PersistentFlags.StateDir, "state", DefaultStateDir, "directory path to store cluster state") Cmd.PersistentFlags().StringVar(&PersistentFlags.ClusterName, "name", "talos-default", "the name of the cluster") } // AddProvisionerFlag adds the provisioner flag to a command. func AddProvisionerFlag(cmd *cobra.Command) { cmd.Flags().StringVar(&provisionerName, ProvisionerFlagName, providers.DockerProviderName, "Talos cluster provisioner to use") } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/config_maker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configmaker import ( "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers" ) // DockerOptions are the options for provisioning a docker based Talos cluster. type DockerOptions makers.MakerOptions[clusterops.Docker] // GetDockerConfigs returns the cluster configs for docker. func GetDockerConfigs(options DockerOptions) (clusterops.ClusterConfigs, error) { maker, err := makers.NewDocker(options) if err != nil { return clusterops.ClusterConfigs{}, err } return maker.GetClusterConfigs() } // QemuOptions are the options for provisioning a qemu based Talos cluster. type QemuOptions makers.MakerOptions[clusterops.Qemu] // GetQemuConfigs returns the cluster configs for qemu. func GetQemuConfigs(options QemuOptions) (clusterops.ClusterConfigs, error) { maker, err := makers.NewQemu(options) if err != nil { return clusterops.ClusterConfigs{}, err } return maker.GetClusterConfigs() } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/common.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers import ( "errors" "fmt" "math/big" "net/netip" "net/url" "os" "slices" "strconv" "strings" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) const ( // gatewayOffset is the offset from the network address of the IP address of the network gateway. gatewayOffset = 1 + iota // nodesOffset is the offset from the network address of the beginning of the IP addresses to be used for nodes. nodesOffset ) // MakerOptions are the options needed to initialize a maker. type MakerOptions[ExtraOps any] = struct { ExtraOps ExtraOps CommonOps clusterops.Common Provisioner provision.Provisioner } // New creates a new maker. func New[ExtraOps any](options MakerOptions[ExtraOps]) (Maker[ExtraOps], error) { m := Maker[ExtraOps]{} m.Ops = options.CommonOps m.EOps = options.ExtraOps m.Provisioner = options.Provisioner return m, nil } // Maker contains config making logic shared between provisioners. type Maker[ExtraOps any] struct { Ops clusterops.Common ClusterRequest provision.ClusterRequest Provisioner provision.Provisioner IPs [][]netip.Addr VersionContract *config.VersionContract GatewayIPs []netip.Addr Cidrs []netip.Prefix InClusterEndpoint string Endpoints []string WithOmni bool ProvisionOps []provision.Option GenOps []generate.Option ConfigBundleOps []bundle.Option EOps ExtraOps extraOptionsProvider ExtraOptionsProvider } // SetExtraOptionsProvider sets extra options provider containing the provider specific logic. func (m *Maker[T]) SetExtraOptionsProvider(hooks ExtraOptionsProvider) { m.extraOptionsProvider = hooks } // Init initializes the common struct fields. func (m *Maker[T]) Init() error { if err := m.InitCommon(); err != nil { return err } if err := m.InitExtra(); err != nil { return err } return nil } // InitExtra calls the init functions set by the individual implementation of the maker. func (m *Maker[T]) InitExtra() error { if err := m.extraOptionsProvider.InitExtra(); err != nil { return err } // skip generating machine config if nodes are to be used with omni if !m.WithOmni { if err := m.extraOptionsProvider.AddExtraGenOps(); err != nil { return err } if err := m.extraOptionsProvider.AddExtraConfigBundleOpts(); err != nil { return err } } if err := m.extraOptionsProvider.AddExtraProvisionOpts(); err != nil { return err } if err := m.extraOptionsProvider.ModifyClusterRequest(); err != nil { return err } if err := m.extraOptionsProvider.ModifyNodes(); err != nil { return err } return nil } // InitCommon initializes the common fields. // //nolint:gocyclo func (m *Maker[T]) InitCommon() error { if m.Ops.OmniAPIEndpoint != "" { m.WithOmni = true } if err := m.initVersionContract(); err != nil { return err } if err := m.initCIDRs(); err != nil { return err } if err := m.initIPs(); err != nil { return err } if err := m.initGatewayIPs(); err != nil { return err } m.initClusterRequest() if err := m.initEndpoints(); err != nil { return err } if err := m.initNodeRequests(); err != nil { return err } // skip generating machine config if nodes are to be used with omni if !m.WithOmni { if err := m.initGenOps(); err != nil { return err } if err := m.initConfigBundleOps(); err != nil { return err } } if err := m.initProvisionOps(); err != nil { return err } return nil } func (m *Maker[T]) initProvisionOps() error { m.ProvisionOps = []provision.Option{ provision.WithKubernetesEndpoint(m.Provisioner.GetExternalKubernetesControlPlaneEndpoint(m.ClusterRequest.Network, m.Ops.ControlPlanePort)), } return nil } func (m *Maker[T]) initConfigBundleOps() error { configPatchBundleOps, err := getConfigPatchBundleOps(m.Ops) if err != nil { return err } m.ConfigBundleOps = slices.Clone(configPatchBundleOps) return nil } func (m *Maker[T]) initVersionContract() error { if m.Ops.TalosVersion == "latest" { m.VersionContract = nil return nil } versionContract, err := config.ParseContractFromVersion(m.Ops.TalosVersion) if err != nil { return fmt.Errorf("error parsing Talos version %q: %w", m.Ops.TalosVersion, err) } m.VersionContract = versionContract return nil } // GetClusterConfigs prepares and returns the cluster create request data. This method is ment to be called after the implemeting maker // logic has been run. func (m *Maker[T]) GetClusterConfigs() (clusterops.ClusterConfigs, error) { var configBundle *bundle.Bundle if !m.WithOmni { cfgBundle, err := m.finalizeMachineConfigs() if err != nil { return clusterops.ClusterConfigs{}, err } configBundle = cfgBundle } else { err := m.applyOmniConfigs() if err != nil { return clusterops.ClusterConfigs{}, err } } return clusterops.ClusterConfigs{ ClusterRequest: m.ClusterRequest, ProvisionOptions: m.ProvisionOps, ConfigBundle: configBundle, }, nil } func (m *Maker[T]) applyOmniConfigs() error { cfg := siderolink.NewConfigV1Alpha1() parsedURL, err := url.Parse(m.Ops.OmniAPIEndpoint) if err != nil { return fmt.Errorf("error parsing omni api url: %w", err) } cfg.APIUrlConfig.URL = parsedURL _, err = cfg.Validate(runtime.ModeMetal) if err != nil { return err } ctr, err := container.New(cfg) if err != nil { return err } m.ForEachNode(func(i int, node *provision.NodeRequest) { node.Config = ctr node.Name = m.Ops.RootOps.ClusterName + "-machine-" + strconv.Itoa(i+1) }) return nil } func (m *Maker[T]) finalizeMachineConfigs() (*bundle.Bundle, error) { // These options needs to be generated after the implementing maker has made changes to the cluster request. provisionGenOps, provisionBundleOps := m.Provisioner.GenOptions(m.ClusterRequest.Network, m.VersionContract) m.GenOps = slices.Concat(m.GenOps, provisionGenOps) m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, provisionBundleOps) m.GenOps = slices.Concat(m.GenOps, []generate.Option{generate.WithEndpointList(m.Endpoints)}) m.ConfigBundleOps = append(m.ConfigBundleOps, bundle.WithInputOptions( &bundle.InputOptions{ ClusterName: m.Ops.RootOps.ClusterName, Endpoint: m.InClusterEndpoint, KubeVersion: strings.TrimPrefix(m.Ops.KubernetesVersion, "v"), GenOptions: m.GenOps, }), ) configBundle, err := bundle.NewBundle(m.ConfigBundleOps...) if err != nil { return nil, err } if m.ClusterRequest.Nodes[0].Type == machine.TypeInit { m.ClusterRequest.Nodes[0].Config = configBundle.Init() } m.ForEachControlplaneNode(func(i, controlplaneIndex int, node *provision.NodeRequest) { node.Config = configBundle.ControlPlane() }) m.ForEachWorkerNode(func(i, workerI int, node *provision.NodeRequest) { node.Config = configBundle.Worker() }) if m.Ops.WireguardCIDR != "" { wireguardConfigBundle, err := helpers.NewWireguardConfigBundle(m.IPs[0], m.Ops.WireguardCIDR, 51111, m.Ops.Controlplanes) if err != nil { return nil, err } for i := range m.ClusterRequest.Nodes { node := &m.ClusterRequest.Nodes[i] patch, err := wireguardConfigBundle.PatchNode(node.IPs[0]) if err != nil { return nil, err } out, err := configpatcher.Apply(configpatcher.WithConfig(node.Config), []configpatcher.Patch{patch}) if err != nil { return nil, err } node.Config, err = out.Config() if err != nil { return nil, err } } } m.ProvisionOps = append(m.ProvisionOps, provision.WithTalosConfig(configBundle.TalosConfig())) return configBundle, nil } // ForEachNode iterates over all nodes allowing modification of each node. func (m *Maker[T]) ForEachNode(fn func(i int, node *provision.NodeRequest)) { for i := range m.ClusterRequest.Nodes { fn(i, &m.ClusterRequest.Nodes[i]) } } // ForEachWorkerNode iterates over all worker nodes allowing modification of each worker node. func (m *Maker[T]) ForEachWorkerNode(fn func(i, workerI int, node *provision.NodeRequest)) { workerIndex := 0 for i := range m.ClusterRequest.Nodes { if m.ClusterRequest.Nodes[i].Type != machine.TypeWorker { continue } fn(i, workerIndex, &m.ClusterRequest.Nodes[i]) workerIndex++ } } // ForEachControlplaneNode iterates over all controlplane nodes allowing modification of each controlplane node. func (m *Maker[T]) ForEachControlplaneNode(fn func(i, controlplaneIndex int, node *provision.NodeRequest)) { controlplaneIndex := 0 for i := range m.ClusterRequest.Nodes { if m.ClusterRequest.Nodes[i].Type != machine.TypeControlPlane { continue } fn(i, controlplaneIndex, &m.ClusterRequest.Nodes[i]) controlplaneIndex++ } } func (m *Maker[T]) initCIDRs() error { cidr4, err := netip.ParsePrefix(m.Ops.NetworkCIDR) if err != nil { return fmt.Errorf("error validating cidr block: %w", err) } if !cidr4.Addr().Is4() { return errors.New("IPV4 CIDR expected, got IPV6 CIDR") } // use ULA IPv6 network fd00::/8, add 'TAL' in hex to build /32 network, add IPv4 CIDR to build /64 unique network cidr6, err := netip.ParsePrefix( fmt.Sprintf( "fd74:616c:%02x%02x:%02x%02x::/64", cidr4.Addr().As4()[0], cidr4.Addr().As4()[1], cidr4.Addr().As4()[2], cidr4.Addr().As4()[3], ), ) if err != nil { return fmt.Errorf("error validating cidr IPv6 block: %w", err) } var cidrs []netip.Prefix if m.Ops.NetworkIPv4 { cidrs = append(cidrs, cidr4) } if m.Ops.NetworkIPv6 { cidrs = append(cidrs, cidr6) } if len(cidrs) == 0 { return errors.New("neither IPv4 nor IPv6 network was enabled") } m.Cidrs = cidrs return nil } func (m *Maker[T]) initGenOps() error { genOptions := []generate.Option{ generate.WithDNSDomain(m.Ops.DNSDomain), generate.WithClusterDiscovery(m.Ops.EnableClusterDiscovery), generate.WithDebug(m.Ops.ConfigDebug), } registryMirrorOps, err := getRegistryMirrorGenOps(m.Ops) if err != nil { return err } for _, registryHost := range m.Ops.RegistryInsecure { genOptions = append(genOptions, generate.WithRegistryInsecureSkipVerify(registryHost)) } genOptions = append(genOptions, registryMirrorOps...) genOptions = append(genOptions, generate.WithVersionContract(m.VersionContract)) if m.Ops.ControlPlanePort != constants.DefaultControlPlanePort { genOptions = slices.Concat(genOptions, []generate.Option{ generate.WithLocalAPIServerPort(m.Ops.ControlPlanePort), }) } if m.Ops.KubePrismPort != constants.DefaultKubePrismPort { genOptions = slices.Concat(genOptions, []generate.Option{ generate.WithKubePrismPort(m.Ops.KubePrismPort), }) } if m.Ops.EnableKubeSpan { genOptions = slices.Concat(genOptions, []generate.Option{ generate.WithKubeSpanEnabled(m.Ops.EnableKubeSpan), }, ) } m.GenOps = genOptions return nil } func getConfigPatchBundleOps(cOps clusterops.Common) ([]bundle.Option, error) { configBundleOpts := []bundle.Option{} addConfigPatch := func(configPatches []string, configOpt func([]configpatcher.Patch) bundle.Option) error { var patches []configpatcher.Patch patches, err := configpatcher.LoadPatches(configPatches) if err != nil { return fmt.Errorf("error parsing config patch: %w", err) } configBundleOpts = append(configBundleOpts, configOpt(patches)) return nil } if err := addConfigPatch(cOps.ConfigPatch, bundle.WithPatch); err != nil { return nil, err } if err := addConfigPatch(cOps.ConfigPatchControlPlane, bundle.WithPatchControlPlane); err != nil { return nil, err } if err := addConfigPatch(cOps.ConfigPatchWorker, bundle.WithPatchWorker); err != nil { return nil, err } return configBundleOpts, nil } func (m *Maker[T]) initIPs() error { nodes := m.Ops.Controlplanes + m.Ops.Workers ips := make([][]netip.Addr, len(m.Cidrs)) for cidrIndex, cidr := range m.Cidrs { for nodeIndex := range nodes { ip, err := sideronet.NthIPInNetwork(cidr, nodesOffset+nodeIndex) if err != nil { return err } ips[cidrIndex] = append(ips[cidrIndex], ip) } } m.IPs = ips return nil } func parseCPUShare(cpus string) (int64, error) { cpu, ok := new(big.Rat).SetString(cpus) if !ok { return 0, fmt.Errorf("failed to parsing as a rational number: %s", cpus) } nano := cpu.Mul(cpu, big.NewRat(1e9, 1)) if !nano.IsInt() { return 0, errors.New("value is too precise") } return nano.Num().Int64(), nil } func getRegistryMirrorGenOps(cOps clusterops.Common) ([]generate.Option, error) { ops := make([]generate.Option, 0, len(cOps.RegistryMirrors)) for _, registryMirror := range cOps.RegistryMirrors { left, right, ok := strings.Cut(registryMirror, "=") if !ok { return nil, fmt.Errorf("invalid registry mirror spec: %q", registryMirror) } ops = append(ops, generate.WithRegistryMirror(left, right)) } return ops, nil } func (m *Maker[T]) initClusterRequest() { m.ClusterRequest = provision.ClusterRequest{ Name: m.Ops.RootOps.ClusterName, SelfExecutable: os.Args[0], StateDirectory: m.Ops.RootOps.StateDir, Network: provision.NetworkRequest{ Name: m.Ops.RootOps.ClusterName, CIDRs: m.Cidrs, GatewayAddrs: m.GatewayIPs, MTU: m.Ops.NetworkMTU, LoadBalancerPorts: []int{m.Ops.ControlPlanePort}, }, } } func (m *Maker[T]) initGatewayIPs() error { gatewayIPs := make([]netip.Addr, len(m.Cidrs)) for j := range gatewayIPs { ip, err := sideronet.NthIPInNetwork(m.Cidrs[j], gatewayOffset) if err != nil { return err } gatewayIPs[j] = ip } m.GatewayIPs = gatewayIPs return nil } func (m *Maker[T]) initEndpoints() error { m.InClusterEndpoint = m.Provisioner.GetInClusterKubernetesControlPlaneEndpoint(m.ClusterRequest.Network, m.Ops.ControlPlanePort) m.Endpoints = m.Provisioner.GetTalosAPIEndpoints(m.ClusterRequest.Network) return nil } func parseResources(res clusterops.NodeResources) (clusterops.ParsedNodeResources, error) { nanoCPUs, err := parseCPUShare(res.CPU) if err != nil { return clusterops.ParsedNodeResources{}, err } return clusterops.ParsedNodeResources{ NanoCPUs: nanoCPUs, Memory: res.Memory, }, nil } func (m *Maker[T]) initNodeRequests() error { controlplaneResources, err := parseResources(m.Ops.ControlplaneResources) if err != nil { return fmt.Errorf("error parsing controlplane resources: %s", err) } workerResources, err := parseResources(m.Ops.WorkerResources) if err != nil { return fmt.Errorf("error parsing worker resources: %s", err) } if m.Ops.Controlplanes < 1 { return errors.New("number of controlplanes can't be less than 1") } nodes := []provision.NodeRequest{} for i := range m.Ops.Controlplanes { nodeUUID := uuid.New() machineName := fmt.Sprintf("%s-%s-%d", m.Ops.RootOps.ClusterName, "controlplane", i+1) if m.Ops.WithUUIDHostnames { machineName = fmt.Sprintf("%s-%s", "machine", nodeUUID) } machineType := machine.TypeControlPlane if m.Ops.WithInitNode && i == 0 { machineType = machine.TypeInit } ips := getNodeIPs(m.IPs, i) nodes = append(nodes, provision.NodeRequest{ Name: machineName, IPs: ips, Type: machineType, Memory: int64(controlplaneResources.Memory.Bytes()), NanoCPUs: controlplaneResources.NanoCPUs, UUID: new(nodeUUID), SkipInjectingConfig: m.Ops.SkipInjectingConfig, }) } for workerIndex := range m.Ops.Workers { nodeUUID := uuid.New() machineName := fmt.Sprintf("%s-%s-%d", m.Ops.RootOps.ClusterName, "worker", workerIndex+1) if m.Ops.WithUUIDHostnames { machineName = fmt.Sprintf("%s-%s", "machine", nodeUUID) } nodeIndex := m.Ops.Controlplanes + workerIndex ips := getNodeIPs(m.IPs, nodeIndex) nodes = append(nodes, provision.NodeRequest{ Name: machineName, IPs: ips, Type: machine.TypeWorker, Memory: int64(workerResources.Memory.Bytes()), NanoCPUs: workerResources.NanoCPUs, UUID: new(nodeUUID), SkipInjectingConfig: m.Ops.SkipInjectingConfig, }) } m.ClusterRequest.Nodes = nodes return nil } func getNodeIPs(ips [][]netip.Addr, nodeIndex int) []netip.Addr { return xslices.Map(ips, func(ips []netip.Addr) netip.Addr { return ips[nodeIndex] }) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/common_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers_test import ( "regexp" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/provision" ) type testProvisioner struct { provision.Provisioner } func (p testProvisioner) GenOptions(r provision.NetworkRequest, _ *config.VersionContract) ([]generate.Option, []bundle.Option) { return []generate.Option{func(o *generate.Options) error { return nil }}, nil } func (p testProvisioner) GetTalosAPIEndpoints(provision.NetworkRequest) []string { return []string{"talos-api-endpoint.test"} } func (p testProvisioner) GetInClusterKubernetesControlPlaneEndpoint(networkReq provision.NetworkRequest, controlPlanePort int) string { return "controlplane-endpoint.test" } func (p testProvisioner) GetExternalKubernetesControlPlaneEndpoint(networkReq provision.NetworkRequest, controlPlanePort int) string { return "external-kubernetes-controlplane-endpoint.test" } type nothingProvider struct{} func (*nothingProvider) InitExtra() error { return nil } func (*nothingProvider) AddExtraGenOps() error { return nil } func (*nothingProvider) AddExtraProvisionOpts() error { return nil } func (*nothingProvider) AddExtraConfigBundleOpts() error { return nil } func (*nothingProvider) ModifyClusterRequest() error { return nil } func (*nothingProvider) ModifyNodes() error { return nil } func getInitializedTestMaker(t *testing.T, cOps clusterops.Common) makers.Maker[any] { m, err := makers.New(makers.MakerOptions[any]{CommonOps: cOps, Provisioner: testProvisioner{}}) require.NoError(t, err) m.SetExtraOptionsProvider(¬hingProvider{}) err = m.Init() require.NoError(t, err) return m } var nodeUUIDHostnameRegex = regexp.MustCompile("^machine-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") func TestCommonMaker(t *testing.T) { cOps := clusterops.GetCommon() cOps.Controlplanes = 2 cOps.Workers = 2 cOps.NetworkIPv6 = true cOps.RootOps.ClusterName = "test-cluster" m := getInitializedTestMaker(t, cOps) controlplanes := m.ClusterRequest.Nodes.ControlPlaneNodes() workers := m.ClusterRequest.Nodes.WorkerNodes() assert.Equal(t, 2, len(controlplanes)) assert.Equal(t, 2, len(workers)) assert.Equal(t, "test-cluster", m.ClusterRequest.Name) assert.Equal(t, "test-cluster", m.ClusterRequest.Network.Name) assert.Equal(t, 2, len(m.Cidrs)) assert.Equal(t, "10.5.0.0/24", m.Cidrs[0].String()) assert.Equal(t, "fd74:616c:a05::/64", m.Cidrs[1].String()) assert.Equal(t, []string{"talos-api-endpoint.test"}, m.Endpoints) assert.Equal(t, "test-cluster-controlplane-1", controlplanes[0].Name) assert.Equal(t, "test-cluster-controlplane-2", controlplanes[1].Name) assert.Equal(t, "test-cluster-worker-1", workers[0].Name) assert.Equal(t, "test-cluster-worker-2", workers[1].Name) for _, node := range append(controlplanes, workers...) { assert.Equal(t, 2, len(node.IPs)) } assert.Equal(t, "10.5.0.2", controlplanes[0].IPs[0].String()) assert.Equal(t, "fd74:616c:a05::2", controlplanes[0].IPs[1].String()) assert.Equal(t, "10.5.0.3", controlplanes[1].IPs[0].String()) assert.Equal(t, "fd74:616c:a05::3", controlplanes[1].IPs[1].String()) assert.Equal(t, "10.5.0.4", workers[0].IPs[0].String()) assert.Equal(t, "fd74:616c:a05::4", workers[0].IPs[1].String()) assert.Equal(t, "10.5.0.5", workers[1].IPs[0].String()) assert.Equal(t, "fd74:616c:a05::5", workers[1].IPs[1].String()) assert.Equal(t, "controlplane-endpoint.test", m.InClusterEndpoint) m.Ops.WithUUIDHostnames = true err := m.Init() assert.NoError(t, err) controlplanes = m.ClusterRequest.Nodes.ControlPlaneNodes() workers = m.ClusterRequest.Nodes.WorkerNodes() assert.Regexp(t, nodeUUIDHostnameRegex, controlplanes[0].Name) assert.Regexp(t, nodeUUIDHostnameRegex, controlplanes[1].Name) assert.Regexp(t, nodeUUIDHostnameRegex, workers[0].Name) assert.Regexp(t, nodeUUIDHostnameRegex, workers[1].Name) _, err = m.GetClusterConfigs() assert.NoError(t, err) m.Ops.OmniAPIEndpoint = "grpc://10.5.0.1:8090?jointoken=my-token" err = m.Init() assert.NoError(t, err) clusterCfgs, err := m.GetClusterConfigs() assert.NoError(t, err) req := clusterCfgs.ClusterRequest assert.Equal(t, "test-cluster-machine-1", req.Nodes[0].Name) assert.Equal(t, "test-cluster-machine-2", req.Nodes[1].Name) cfgBytes, err := req.Nodes[0].Config.Bytes() assert.NoError(t, err) assert.Contains(t, string(cfgBytes), "apiVersion: v1alpha1") assert.Contains(t, string(cfgBytes), "kind: SideroLinkConfig") assert.Contains(t, string(cfgBytes), "apiUrl: grpc://10.5.0.1:8090?jointoken=my-token") } func TestCommonMaker_MachineConfig(t *testing.T) { cOps := clusterops.GetCommon() m := getInitializedTestMaker(t, cOps) assertConfigDefaultness(t, cOps, m) } // assertConfigDefaultness makes sure the maker-generated machine configs are not different from default talos machine configs. func assertConfigDefaultness[ExtraOps any](t *testing.T, cOps clusterops.Common, m makers.Maker[ExtraOps], desiredExtraGenOps ...generate.Option) { var versionContract *config.VersionContract secretsBundle, err := secrets.NewBundle(secrets.NewClock(), versionContract) require.NoError(t, err) // The only allowed differences from the default machine config. desiredExtraGenOps = append(desiredExtraGenOps, generate.WithSecretsBundle(secretsBundle), generate.WithVersionContract(versionContract), ) in, err := generate.NewInput(cOps.RootOps.ClusterName, "controlplane-endpoint.test", cOps.KubernetesVersion, desiredExtraGenOps..., ) require.NoError(t, err) m.GenOps = append(m.GenOps, generate.WithSecretsBundle(secretsBundle)) clusterCfgs, err := m.GetClusterConfigs() require.NoError(t, err) for _, node := range clusterCfgs.ClusterRequest.Nodes { assertMachineConfig(t, in, node) } } func assertMachineConfig(t *testing.T, in *generate.Input, node provision.NodeRequest) { cfgExpected, err := in.Config(node.Type) require.NoError(t, err) cfgGot := node.Config cfgGot = cfgGot.RedactSecrets("secret") cfgExpected = cfgExpected.RedactSecrets("secret") cfgExpectedBytes, err := cfgExpected.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, err) cfgGotBytes, err := cfgGot.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, err) assert.Equal(t, string(cfgExpectedBytes), string(cfgGotBytes)) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/docker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers import ( "net" "slices" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/provision" ) var _ ConfigMaker = &(Docker{}) // Docker is the maker for docker. type Docker struct { *Maker[clusterops.Docker] } // NewDocker returns a new initialized Docker Maker. func NewDocker(ops MakerOptions[clusterops.Docker]) (Docker, error) { maker, err := New(ops) if err != nil { return Docker{}, err } m := Docker{Maker: &maker} m.SetExtraOptionsProvider(&m) if err := m.Init(); err != nil { return Docker{}, err } return m, nil } // InitExtra implements ExtraOptionsProvider. func (m *Docker) InitExtra() error { return nil } // AddExtraConfigBundleOpts implements ExtraOptionsProvider. func (m *Docker) AddExtraConfigBundleOpts() error { return nil } // AddExtraGenOps implements ExtraOptionsProvider. func (m *Docker) AddExtraGenOps() error { m.GenOps = slices.Concat(m.GenOps, getWithAdditionalSubjectAltNamesGenOps(m.Endpoints)) return nil } // AddExtraProvisionOpts implements ExtraOptionsProvider. func (m *Docker) AddExtraProvisionOpts() error { if m.EOps.Ports != "" { portList := strings.Split(m.EOps.Ports, ",") m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{provision.WithDockerPorts(portList)}) } m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{provision.WithDockerPortsHostIP(m.EOps.HostIP)}) return nil } // ModifyClusterRequest implements ExtraOptionsProvider. func (m *Docker) ModifyClusterRequest() error { m.ClusterRequest.Network.DockerDisableIPv6 = m.EOps.DisableIPv6 m.ClusterRequest.Image = m.EOps.TalosImage return nil } // ModifyNodes implements ExtraOptionsProvider. func (m *Docker) ModifyNodes() error { m.ForEachNode(func(i int, node *provision.NodeRequest) { node.Mounts = m.EOps.MountOpts.Value() }) return nil } func getWithAdditionalSubjectAltNamesGenOps(endpointList []string) []generate.Option { return xslices.Map(endpointList, func(endpointHostPort string) generate.Option { endpointHost, _, err := net.SplitHostPort(endpointHostPort) if err != nil { endpointHost = endpointHostPort } return generate.WithAdditionalSubjectAltNames([]string{endpointHost}) }) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/docker_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers" "github.com/siderolabs/talos/pkg/machinery/config/generate" ) func TestDockerMaker_MachineConfig(t *testing.T) { cOps := clusterops.GetCommon() dOps := clusterops.GetDocker() m, err := makers.NewDocker(makers.MakerOptions[clusterops.Docker]{ ExtraOps: dOps, CommonOps: cOps, Provisioner: testProvisioner{}, // use test provisioner to simplify the test case. }) require.NoError(t, err) desiredExtraGenOps := []generate.Option{ generate.WithAdditionalSubjectAltNames([]string{"talos-api-endpoint.test"}), } assertConfigDefaultness(t, cOps, *m.Maker, desiredExtraGenOps...) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/maker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers import "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" // ConfigMaker helps creating cluster and provision configuration. type ConfigMaker interface { GetClusterConfigs() (clusterops.ClusterConfigs, error) } // ExtraOptionsProvider are used to implement provider specific logic. type ExtraOptionsProvider interface { InitExtra() error AddExtraGenOps() error AddExtraProvisionOpts() error AddExtraConfigBundleOpts() error ModifyClusterRequest() error ModifyNodes() error } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers import ( "context" "errors" "fmt" "net/netip" "net/url" "slices" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-procfs/procfs" sideronet "github.com/siderolabs/net" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/siderolinkbuilder" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/internal/firewallpatch" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/bundle" configbase "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/block" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/provision" ) const ( // vipOffset is the offset from the network address of the CIDR to use for allocating the Virtual (shared) IP address, if enabled. vipOffset = 50 ) var _ ConfigMaker = &(Qemu{}) // Qemu is the maker for qemu. type Qemu struct { *Maker[clusterops.Qemu] VIP netip.Addr SideroLinkBuilder *siderolinkbuilder.SiderolinkBuilder } // NewQemu returns a new qemu Maker. func NewQemu(ops MakerOptions[clusterops.Qemu]) (Qemu, error) { maker, err := New(ops) if err != nil { return Qemu{}, err } m := Qemu{Maker: &maker} m.SetExtraOptionsProvider(&m) if err := m.Init(); err != nil { return Qemu{}, err } return m, nil } // InitExtra implements ExtraOptionsProvider. func (m *Qemu) InitExtra() error { if m.EOps.UseVIP { vip, err := sideronet.NthIPInNetwork(m.Cidrs[0], vipOffset) if err != nil { return err } m.VIP = vip m.InClusterEndpoint = "https://" + nethelpers.JoinHostPort(vip.String(), m.Ops.ControlPlanePort) } if err := m.initDisks(); err != nil { return err } if m.EOps.WithSiderolinkAgent.IsEnabled() { slb, err := siderolinkbuilder.New(context.Background(), m.GatewayIPs[0].String(), m.EOps.WithSiderolinkAgent.IsTLS()) if err != nil { return err } m.SideroLinkBuilder = slb } m.initEndpoints() if m.Ops.WithJSONLogs { m.initJSONLogs() } return nil } func (m *Qemu) initEndpoints() { switch { case m.Ops.ForceEndpoint != "": // using non-default endpoints, provision additional cert SANs and fix endpoint list m.Endpoints = []string{m.Ops.ForceEndpoint} case m.Ops.ForceInitNodeAsEndpoint: m.Endpoints = []string{m.IPs[0][0].String()} case m.Endpoints == nil: // use control plane nodes as endpoints, client-side load-balancing for i := range m.Ops.Controlplanes { m.Endpoints = slices.Concat(m.Endpoints, []string{m.IPs[0][i].String()}) } } } // AddExtraGenOps implements ExtraOptionsProvider. func (m *Qemu) AddExtraGenOps() error { m.GenOps = slices.Concat(m.GenOps, []generate.Option{generate.WithInstallImage(m.EOps.NodeInstallImage)}) if m.Ops.CustomCNIUrl != "" { m.GenOps = slices.Concat(m.GenOps, []generate.Option{generate.WithClusterCNIConfig(&v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{m.Ops.CustomCNIUrl}, })}) } if m.EOps.UseVIP { if m.VersionContract.MultidocNetworkConfigSupported() { vipCfg := networkcfg.NewLayer2VIPConfigV1Alpha1(m.VIP.String()) vipCfg.LinkName = m.Provisioner.GetFirstInterfaceName() ctr, err := container.New(vipCfg) if err != nil { return err } m.ConfigBundleOps = append(m.ConfigBundleOps, bundle.WithPatchControlPlane([]configpatcher.Patch{configpatcher.NewStrategicMergePatch(ctr)}), ) } else { m.GenOps = slices.Concat(m.GenOps, []generate.Option{generate.WithNetworkOptions( v1alpha1.WithNetworkInterfaceVirtualIP(m.Provisioner.GetFirstInterface(), m.VIP.String()), )}, ) } } // disable kexec, if bootloader is disabled, and // also disable kexec on arm64 due to https://github.com/siderolabs/talos/issues/12393 if !m.EOps.BootloaderEnabled || m.EOps.TargetArch == "arm64" { m.GenOps = slices.Concat(m.GenOps, []generate.Option{ generate.WithSysctls(map[string]string{ "kernel.kexec_load_disabled": "1", }), }) } if m.Ops.ForceEndpoint != "" { m.GenOps = slices.Concat(m.GenOps, []generate.Option{generate.WithAdditionalSubjectAltNames(m.Endpoints)}) } return nil } // AddExtraProvisionOpts implements ExtraOptionsProvider. func (m *Qemu) AddExtraProvisionOpts() error { m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{ provision.WithBootlader(m.EOps.BootloaderEnabled), provision.WithSkipInjectingExtraCmdline(m.EOps.SkipInjectingExtraCmdline), provision.WithUEFI(m.EOps.UefiEnabled), provision.WithTPM1_2(m.EOps.Tpm1_2Enabled), provision.WithTPM2(m.EOps.Tpm2Enabled), provision.WithDebugShell(m.EOps.DebugShellEnabled), provision.WithIOMMU(m.EOps.WithIOMMU), provision.WithExtraUEFISearchPaths(m.EOps.ExtraUEFISearchPaths), provision.WithTargetArch(m.EOps.TargetArch), provision.WithSiderolinkAgent(m.EOps.WithSiderolinkAgent.IsEnabled()), }) externalKubernetesEndpoint := m.Provisioner.GetExternalKubernetesControlPlaneEndpoint(m.ClusterRequest.Network, m.Ops.ControlPlanePort) if m.EOps.UseVIP { externalKubernetesEndpoint = "https://" + nethelpers.JoinHostPort(m.VIP.String(), m.Ops.ControlPlanePort) } m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{provision.WithKubernetesEndpoint(externalKubernetesEndpoint)}) return nil } // AddExtraConfigBundleOpts implements ExtraOptionsProvider. func (m *Qemu) AddExtraConfigBundleOpts() error { if m.EOps.WithFirewall != "" { var defaultAction nethelpers.DefaultAction defaultAction, err := nethelpers.DefaultActionString(m.EOps.WithFirewall) if err != nil { return err } var controlplaneIPs []netip.Addr for i := range m.IPs { controlplaneIPs = slices.Concat(controlplaneIPs, m.IPs[i][:m.Ops.Controlplanes]) } m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, []bundle.Option{ bundle.WithPatchControlPlane([]configpatcher.Patch{firewallpatch.ControlPlane(defaultAction, m.Cidrs, m.GatewayIPs, controlplaneIPs)}), bundle.WithPatchWorker([]configpatcher.Patch{firewallpatch.Worker(defaultAction, m.Cidrs, m.GatewayIPs)}), }) } if err := m.addDiskEncryptionPatches(); err != nil { return err } m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, []bundle.Option{ bundle.WithPatch(m.SideroLinkBuilder.ConfigPatches(m.EOps.WithSiderolinkAgent.IsTunnel())), }) return nil } // ModifyClusterRequest implements ExtraOptionsProvider. func (m *Qemu) ModifyClusterRequest() error { nameserverIPs, err := getNameserverIPs(m.EOps.Nameservers, m.GatewayIPs) if err != nil { return err } noMasqueradeCIDRs := make([]netip.Prefix, 0, len(m.EOps.NetworkNoMasqueradeCIDRs)) for _, cidr := range m.EOps.NetworkNoMasqueradeCIDRs { var parsedCIDR netip.Prefix parsedCIDR, err = netip.ParsePrefix(cidr) if err != nil { return fmt.Errorf("error parsing non-masquerade CIDR %q: %w", cidr, err) } noMasqueradeCIDRs = append(noMasqueradeCIDRs, parsedCIDR) } err = m.validateNetworkChaosParams() if err != nil { return err } m.ClusterRequest.Network.CNI = provision.CNIConfig{ BinPath: m.EOps.CniBinPath, ConfDir: m.EOps.CniConfDir, CacheDir: m.EOps.CniCacheDir, BundleURL: m.EOps.CniBundleURL, } m.ClusterRequest.Network.Nameservers = nameserverIPs m.ClusterRequest.Network.NoMasqueradeCIDRs = noMasqueradeCIDRs m.ClusterRequest.Network.DHCPSkipHostname = m.EOps.DHCPSkipHostname m.ClusterRequest.Network.NetworkChaos = m.EOps.NetworkChaos m.ClusterRequest.Network.Jitter = m.EOps.Jjitter m.ClusterRequest.Network.Latency = m.EOps.Latency m.ClusterRequest.Network.PacketLoss = m.EOps.PacketLoss m.ClusterRequest.Network.PacketReorder = m.EOps.PacketReorder m.ClusterRequest.Network.PacketCorrupt = m.EOps.PacketCorrupt m.ClusterRequest.Network.Bandwidth = m.EOps.Bandwidth m.ClusterRequest.Network.Airgapped = m.EOps.Airgapped m.ClusterRequest.Network.ImageCachePath = m.EOps.ImageCachePath m.ClusterRequest.Network.ImageCacheTLSCertFile = m.EOps.ImageCacheTLSCertFile m.ClusterRequest.Network.ImageCacheTLSKeyFile = m.EOps.ImageCacheTLSKeyFile m.ClusterRequest.Network.ImageCachePort = m.EOps.ImageCachePort m.ClusterRequest.KernelPath = m.EOps.NodeVmlinuzPath m.ClusterRequest.InitramfsPath = m.EOps.NodeInitramfsPath m.ClusterRequest.ISOPath = m.EOps.NodeISOPath m.ClusterRequest.USBPath = m.EOps.NodeUSBPath m.ClusterRequest.UKIPath = m.EOps.NodeUKIPath m.ClusterRequest.IPXEBootScript = m.EOps.NodeIPXEBootScript m.ClusterRequest.DiskImagePath = m.EOps.NodeDiskImagePath return nil } func (m *Qemu) validateNetworkChaosParams() error { if !m.EOps.NetworkChaos { if m.EOps.Jjitter != 0 || m.EOps.Latency != 0 || m.EOps.PacketLoss != 0 || m.EOps.PacketReorder != 0 || m.EOps.PacketCorrupt != 0 || m.EOps.Bandwidth != 0 { return errors.New("network chaos flags can only be used with network-chaos option enabled") } } return nil } // ModifyNodes implements ExtraOptionsProvider. func (m *Qemu) ModifyNodes() error { var configInjectionMethod provision.ConfigInjectionMethod switch m.EOps.ConfigInjectionMethod { case "", "default", "http": configInjectionMethod = provision.ConfigInjectionMethodHTTP case "metal-iso": configInjectionMethod = provision.ConfigInjectionMethodMetalISO default: return fmt.Errorf("unknown config injection method %d", configInjectionMethod) } var extraKernelArgs *procfs.Cmdline if m.EOps.ExtraBootKernelArgs != "" || m.EOps.WithSiderolinkAgent.IsEnabled() { extraKernelArgs = procfs.NewCmdline(m.EOps.ExtraBootKernelArgs) } err := m.SideroLinkBuilder.SetKernelArgs(extraKernelArgs, m.EOps.WithSiderolinkAgent.IsTunnel()) if err != nil { return err } for i := range m.ClusterRequest.Nodes { node := &m.ClusterRequest.Nodes[i] err := m.SideroLinkBuilder.DefineIPv6ForUUID(*node.UUID) if err != nil { return err } node.ConfigInjectionMethod = configInjectionMethod node.Quirks = quirks.New(m.Ops.TalosVersion) node.SkipInjectingConfig = m.Ops.SkipInjectingConfig node.BadRTC = m.EOps.BadRTC node.ExtraKernelArgs = extraKernelArgs } m.ClusterRequest.SiderolinkRequest = m.SideroLinkBuilder.SiderolinkRequest() return nil } func (m *Qemu) addDiskEncryptionPatches() error { var diskEncryptionPatches []configpatcher.Patch if m.EOps.EncryptStatePartition || m.EOps.EncryptEphemeralPartition { keys, err := m.getEncryptionKeys(m.EOps.DiskEncryptionKeyTypes) if err != nil { return err } if !m.VersionContract.VolumeConfigEncryptionSupported() { // legacy v1alpha1 flow to support booting old Talos versions patch, err := m.getLegacyDiskEncryptionPatch(keys) if err != nil { return err } diskEncryptionPatches = append(diskEncryptionPatches, patch) } else { for _, spec := range []struct { label string enabled bool }{ {label: constants.StatePartitionLabel, enabled: m.EOps.EncryptStatePartition}, {label: constants.EphemeralPartitionLabel, enabled: m.EOps.EncryptEphemeralPartition}, } { if !spec.enabled { continue } patch, err := m.getDiskEncryptionPatch(spec, keys) if err != nil { return err } diskEncryptionPatches = append(diskEncryptionPatches, patch) } } } m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, []bundle.Option{bundle.WithPatch(diskEncryptionPatches)}, ) return nil } func (*Qemu) getDiskEncryptionPatch(spec struct { label string enabled bool }, keys []*v1alpha1.EncryptionKey, ) (configpatcher.StrategicMergePatch, error) { blockCfg := block.NewVolumeConfigV1Alpha1() blockCfg.MetaName = spec.label blockCfg.EncryptionSpec = block.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionKeys: convertEncryptionKeys(keys), } if spec.label != constants.StatePartitionLabel { for idx := range blockCfg.EncryptionSpec.EncryptionKeys { blockCfg.EncryptionSpec.EncryptionKeys[idx].KeyLockToSTATE = new(true) } } ctr, err := container.New(blockCfg) if err != nil { return nil, fmt.Errorf("error creating container for %q volume: %w", spec.label, err) } patch := configpatcher.NewStrategicMergePatch(ctr) return patch, nil } func (m *Qemu) getLegacyDiskEncryptionPatch(keys []*v1alpha1.EncryptionKey) (configpatcher.Patch, error) { diskEncryptionConfig := &v1alpha1.SystemDiskEncryptionConfig{} if m.EOps.EncryptStatePartition { diskEncryptionConfig.StatePartition = &v1alpha1.EncryptionConfig{ EncryptionProvider: encryption.LUKS2, EncryptionKeys: keys, } } if m.EOps.EncryptEphemeralPartition { diskEncryptionConfig.EphemeralPartition = &v1alpha1.EncryptionConfig{ EncryptionProvider: encryption.LUKS2, EncryptionKeys: keys, } } patchRaw := map[string]any{ "machine": map[string]any{ "systemDiskEncryption": diskEncryptionConfig, }, } patchData, err := yaml.Marshal(patchRaw) if err != nil { return nil, fmt.Errorf("error marshaling patch: %w", err) } patch, err := configpatcher.LoadPatch(patchData) if err != nil { return nil, fmt.Errorf("error loading patch: %w", err) } return patch, nil } func (m *Qemu) initDisks() error { workerExtraDisks := make([]*provision.Disk, 0, len(m.EOps.Disks.Requests())-1) primaryDisks := []*provision.Disk{ { Size: m.EOps.Disks.Requests()[0].Size.Bytes(), SkipPreallocate: !m.EOps.PreallocateDisks, Driver: m.EOps.Disks.Requests()[0].Driver, BlockSize: m.EOps.DiskBlockSize, Serial: m.EOps.Disks.Requests()[0].Serial, }, } // get worker extra disks for _, d := range m.EOps.Disks.Requests()[1:] { workerExtraDisks = append(workerExtraDisks, &provision.Disk{ Size: d.Size.Bytes(), SkipPreallocate: !m.EOps.PreallocateDisks, Driver: d.Driver, BlockSize: m.EOps.DiskBlockSize, Tag: d.Tag, Serial: d.Serial, }) } m.ForEachNode(func(i int, node *provision.NodeRequest) { node.Disks = slices.Concat(node.Disks, primaryDisks) }) if err := m.initExtraDisks(); err != nil { return err } m.ForEachNode(func(i int, node *provision.NodeRequest) { if node.Type == machine.TypeWorker { node.Disks = slices.Concat(node.Disks, workerExtraDisks) } }) return nil } //nolint:gocyclo func (m *Qemu) initExtraDisks() error { const GPTAlignment = 2 * 1024 * 1024 // 2 MB var ( userVolumes []*block.UserVolumeConfigV1Alpha1 encryptionSpec block.EncryptionSpec ) if m.EOps.EncryptUserVolumes { encryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 keys, err := m.getEncryptionKeys(m.EOps.DiskEncryptionKeyTypes) if err != nil { return err } encryptionSpec.EncryptionKeys = convertEncryptionKeys(keys) } disks := make([]*provision.Disk, 0, len(m.EOps.ClusterUserVolumes)) for diskID, disk := range m.EOps.ClusterUserVolumes { var ( volumes = strings.Split(disk, ":") diskSize uint64 ) if len(volumes)%2 != 0 { return errors.New("failed to parse malformed volume definitions") } for j := 0; j < len(volumes); j += 2 { volumeName := volumes[j] volumeSize := volumes[j+1] userVolume := block.NewUserVolumeConfigV1Alpha1() userVolume.MetaName = volumeName userVolume.ProvisioningSpec = block.ProvisioningSpec{ DiskSelectorSpec: block.DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", m.Provisioner.UserDiskName(diskID+1)), celenv.DiskLocator())), }, ProvisioningMinSize: block.MustByteSize(volumeSize), ProvisioningMaxSize: block.MustSize(volumeSize), } userVolume.EncryptionSpec = encryptionSpec userVolumes = append(userVolumes, userVolume) diskSize += userVolume.ProvisioningSpec.ProvisioningMaxSize.Value() } disks = append(disks, &provision.Disk{ // add 2 MB per partition to make extra room for GPT and alignment Size: diskSize + GPTAlignment*uint64(len(volumes)/2+1), SkipPreallocate: !m.EOps.PreallocateDisks, Driver: "ide", BlockSize: m.EOps.DiskBlockSize, }) } if len(userVolumes) > 0 { ctr, err := container.New(xslices.Map(userVolumes, func(u *block.UserVolumeConfigV1Alpha1) configbase.Document { return u })...) if err != nil { return fmt.Errorf("failed to create user volumes container: %w", err) } userVolumePatches := []configpatcher.Patch{configpatcher.NewStrategicMergePatch(ctr)} m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, []bundle.Option{bundle.WithPatch(userVolumePatches)}) } m.ForEachNode(func(i int, node *provision.NodeRequest) { node.Disks = slices.Concat(node.Disks, disks) }) return nil } func (m *Qemu) getEncryptionKeys(diskEncryptionKeyTypes []string) ([]*v1alpha1.EncryptionKey, error) { var keys []*v1alpha1.EncryptionKey for i, key := range diskEncryptionKeyTypes { switch key { case "uuid": keys = append(keys, &v1alpha1.EncryptionKey{ KeyNodeID: &v1alpha1.EncryptionKeyNodeID{}, KeySlot: i, }) case "kms": var ip netip.Addr // get bridge IP ip, err := sideronet.NthIPInNetwork(m.Cidrs[0], 1) if err != nil { return nil, err } const port = 4050 keys = append(keys, &v1alpha1.EncryptionKey{ KeyKMS: &v1alpha1.EncryptionKeyKMS{ KMSEndpoint: "grpc://" + nethelpers.JoinHostPort(ip.String(), port), }, KeySlot: i, }) m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{provision.WithKMS(nethelpers.JoinHostPort("0.0.0.0", port))}) case "tpm": keyTPM := &v1alpha1.EncryptionKeyTPM{} if m.VersionContract.SecureBootEnrollEnforcementSupported() { keyTPM.TPMCheckSecurebootStatusOnEnroll = new(true) } keys = append(keys, &v1alpha1.EncryptionKey{ KeyTPM: keyTPM, KeySlot: i, }) default: return nil, fmt.Errorf("unknown key type %q", key) } } if len(keys) == 0 { return nil, errors.New("no disk encryption key types enabled") } return keys, nil } func convertEncryptionKeys(keys []*v1alpha1.EncryptionKey) []block.EncryptionKey { return xslices.Map(keys, func(k *v1alpha1.EncryptionKey) block.EncryptionKey { r := block.EncryptionKey{ KeySlot: k.KeySlot, } if k.KeyKMS != nil { r.KeyKMS = new(block.EncryptionKeyKMS(*k.KeyKMS)) } if k.KeyTPM != nil { encryptionKeyTPM := block.EncryptionKeyTPM{ TPMCheckSecurebootStatusOnEnroll: k.KeyTPM.TPMCheckSecurebootStatusOnEnroll, } r.KeyTPM = new(encryptionKeyTPM) } if k.KeyNodeID != nil { r.KeyNodeID = new(block.EncryptionKeyNodeID(*k.KeyNodeID)) } if k.KeyStatic != nil { r.KeyStatic = new(block.EncryptionKeyStatic(*k.KeyStatic)) } return r }) } func (m *Qemu) initJSONLogs() { const port = 4003 m.ProvisionOps = slices.Concat(m.ProvisionOps, []provision.Option{provision.WithJSONLogs(nethelpers.JoinHostPort(m.GatewayIPs[0].String(), port))}) cfg := container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineLogging: &v1alpha1.LoggingConfig{ LoggingDestinations: []v1alpha1.LoggingDestination{ { LoggingEndpoint: &v1alpha1.Endpoint{ URL: &url.URL{ Scheme: "tcp", Host: nethelpers.JoinHostPort(m.GatewayIPs[0].String(), port), }, }, LoggingFormat: "json_lines", }, }, }, }, }) m.ConfigBundleOps = slices.Concat(m.ConfigBundleOps, []bundle.Option{bundle.WithPatch([]configpatcher.Patch{configpatcher.NewStrategicMergePatch(cfg)})}) } func getNameserverIPs(nameservers []string, gatewayIPs []netip.Addr) ([]netip.Addr, error) { nameserverIPs := make([]netip.Addr, len(nameservers)) if len(nameservers) == 0 { return gatewayIPs, nil } for i := range nameserverIPs { ip, err := netip.ParseAddr(nameservers[i]) if err != nil { return nil, fmt.Errorf("failed parsing nameserver IP %q: %w", nameservers[i], err) } nameserverIPs[i] = ip } return nameserverIPs, nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers/qemu_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makers_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/makers" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/provision" ) func TestQemuMaker_MachineConfig(t *testing.T) { cOps := clusterops.GetCommon() qOps := clusterops.GetQemu() m, err := makers.NewQemu(makers.MakerOptions[clusterops.Qemu]{ ExtraOps: qOps, CommonOps: cOps, Provisioner: testProvisioner{}, // use test provisioner to simplify the test case. }) require.NoError(t, err) desiredExtraGenOps := []generate.Option{} assertConfigDefaultness(t, cOps, *m.Maker, desiredExtraGenOps...) } func TestQemuMaker_Disks(t *testing.T) { cOps := clusterops.GetCommon() qOps := clusterops.GetQemu() disks := flags.Disks{} err := disks.Set("virtio:10GiB,nvme:20GiB,virtio:30GiB") require.NoError(t, err) qOps.Disks = disks cOps.Controlplanes = 1 cOps.Workers = 1 m, err := makers.NewQemu(makers.MakerOptions[clusterops.Qemu]{ ExtraOps: qOps, CommonOps: cOps, Provisioner: testProvisioner{}, // use test provisioner to simplify the test case. }) require.NoError(t, err) req, err := m.GetClusterConfigs() require.NoError(t, err) controlplaneDisks := req.ClusterRequest.Nodes[0].Disks workerDisks := req.ClusterRequest.Nodes[1].Disks assert.Equal(t, 1, len(controlplaneDisks)) assert.Equal(t, 3, len(workerDisks)) assert.Equal(t, []*provision.Disk{ { Size: disks.Requests()[0].Size.Bytes(), SkipPreallocate: !qOps.PreallocateDisks, Driver: "virtio", BlockSize: qOps.DiskBlockSize, Serial: "", }, }, controlplaneDisks) assert.Equal(t, []*provision.Disk{ { Size: disks.Requests()[0].Size.Bytes(), SkipPreallocate: !qOps.PreallocateDisks, Driver: "virtio", BlockSize: qOps.DiskBlockSize, Serial: "", }, { Size: disks.Requests()[1].Size.Bytes(), SkipPreallocate: !qOps.PreallocateDisks, Driver: "nvme", BlockSize: qOps.DiskBlockSize, Serial: "", }, { Size: disks.Requests()[2].Size.Bytes(), SkipPreallocate: !qOps.PreallocateDisks, Driver: "virtio", BlockSize: qOps.DiskBlockSize, Serial: "", }, }, workerDisks) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/siderolinkbuilder/builder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolinkbuilder import ( "bytes" "context" "encoding/base64" "errors" "fmt" "net" "net/netip" "net/url" "slices" "strconv" "github.com/google/uuid" "github.com/klauspost/compress/zstd" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/config" configbase "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) // New creates a new SiderolinkBuilder. func New(ctx context.Context, wgHost string, useTLS bool) (*SiderolinkBuilder, error) { prefix, err := networkPrefix("") if err != nil { return nil, err } result := &SiderolinkBuilder{ wgHost: wgHost, binds: map[uuid.UUID]netip.Addr{}, prefix: prefix, nodeIPv6Addr: prefix.Addr().Next().String(), } if useTLS { ca, err := x509.NewSelfSignedCertificateAuthority(x509.ECDSA(true), x509.IPAddresses([]net.IP{net.ParseIP(wgHost)})) if err != nil { return nil, err } result.apiCert = ca.CrtPEM result.apiKey = ca.KeyPEM } var resultErr error for range 10 { for _, d := range []struct { field *int net string what string }{ {&result.wgPort, "udp", "WireGuard"}, {&result.apiPort, "tcp", "gRPC API"}, {&result.sinkPort, "tcp", "Event Sink"}, {&result.logPort, "tcp", "Log Receiver"}, } { var err error *d.field, err = getDynamicPort(ctx, d.net) if err != nil { return nil, fmt.Errorf("failed to get dynamic port for %s: %w", d.what, err) } } resultErr = checkPortsDontOverlap(result.wgPort, result.apiPort, result.sinkPort, result.logPort) if resultErr == nil { break } } if resultErr != nil { return nil, fmt.Errorf("failed to get non-overlapping dynamic ports in 10 attempts: %w", resultErr) } return result, nil } // SiderolinkBuilder is responsible for building Siderolink configurations. type SiderolinkBuilder struct { wgHost string binds map[uuid.UUID]netip.Addr prefix netip.Prefix nodeIPv6Addr string wgPort int apiPort int sinkPort int logPort int apiCert []byte apiKey []byte } // DefineIPv6ForUUID defines an IPv6 address for a given UUID. It is safe to call this method on a nil pointer. func (slb *SiderolinkBuilder) DefineIPv6ForUUID(id uuid.UUID) error { if slb == nil { return nil } result, err := generateRandomNodeAddr(slb.prefix) if err != nil { return err } slb.binds[id] = result.Addr() return nil } // SiderolinkRequest returns a SiderolinkRequest based on the current state of the builder. // It is safe to call this method on a nil pointer. func (slb *SiderolinkBuilder) SiderolinkRequest() provision.SiderolinkRequest { if slb == nil { return provision.SiderolinkRequest{} } return provision.SiderolinkRequest{ WireguardEndpoint: net.JoinHostPort(slb.wgHost, strconv.Itoa(slb.wgPort)), APIEndpoint: ":" + strconv.Itoa(slb.apiPort), APICertificate: slb.apiCert, APIKey: slb.apiKey, SinkEndpoint: ":" + strconv.Itoa(slb.sinkPort), LogEndpoint: ":" + strconv.Itoa(slb.logPort), SiderolinkBind: maps.ToSlice(slb.binds, func(k uuid.UUID, v netip.Addr) provision.SiderolinkBind { return provision.SiderolinkBind{ UUID: k, Addr: v, } }), } } // ConfigPatches returns the config patches for the current builder. func (slb *SiderolinkBuilder) ConfigPatches(tunnel bool) []configpatcher.Patch { cfg := slb.ConfigDocument(tunnel) if cfg == nil { return nil } return []configpatcher.Patch{configpatcher.NewStrategicMergePatch(cfg)} } // ConfigDocument returns the config document for the current builder. func (slb *SiderolinkBuilder) ConfigDocument(tunnel bool) config.Provider { if slb == nil { return nil } scheme := "grpc://" if slb.apiCert != nil { scheme = "https://" } apiLink := scheme + net.JoinHostPort(slb.wgHost, strconv.Itoa(slb.apiPort)) + "?jointoken=foo" if tunnel { apiLink += "&grpc_tunnel=true" } apiURL, err := url.Parse(apiLink) if err != nil { panic(fmt.Sprintf("failed to parse API URL: %s", err)) } sdlConfig := siderolink.NewConfigV1Alpha1() sdlConfig.APIUrlConfig.URL = apiURL eventsConfig := runtime.NewEventSinkV1Alpha1() eventsConfig.Endpoint = net.JoinHostPort(slb.nodeIPv6Addr, strconv.Itoa(slb.sinkPort)) logURL, err := url.Parse("tcp://" + net.JoinHostPort(slb.nodeIPv6Addr, strconv.Itoa(slb.logPort))) if err != nil { panic(fmt.Sprintf("failed to parse log URL: %s", err)) } logConfig := runtime.NewKmsgLogV1Alpha1() logConfig.MetaName = "siderolink" logConfig.KmsgLogURL.URL = logURL documents := []configbase.Document{ sdlConfig, eventsConfig, logConfig, } if slb.apiCert != nil { trustedRootsConfig := security.NewTrustedRootsConfigV1Alpha1() trustedRootsConfig.MetaName = "siderolink-ca" trustedRootsConfig.Certificates = string(slb.apiCert) documents = append(documents, trustedRootsConfig) } ctr, err := container.New(documents...) if err != nil { panic(fmt.Sprintf("failed to create container for Siderolink config: %s", err)) } return ctr } // SetKernelArgs sets the kernel arguments for the current builder. It is safe to call this method on a nil pointer. func (slb *SiderolinkBuilder) SetKernelArgs(extraKernelArgs *procfs.Cmdline, tunnel bool) error { switch { case slb == nil: return nil case extraKernelArgs.Get("siderolink.api") != nil, extraKernelArgs.Get("talos.events.sink") != nil, extraKernelArgs.Get("talos.logging.kernel") != nil: return errors.New("siderolink kernel arguments are already set, cannot run with --with-siderolink") default: marshaled, err := slb.ConfigDocument(tunnel).EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { panic(fmt.Sprintf("failed to marshal trusted roots config: %s", err)) } var buf bytes.Buffer zencoder, err := zstd.NewWriter(&buf) if err != nil { return fmt.Errorf("failed to create zstd encoder: %w", err) } _, err = zencoder.Write(marshaled) if err != nil { return fmt.Errorf("failed to write zstd data: %w", err) } if err = zencoder.Close(); err != nil { return fmt.Errorf("failed to close zstd encoder: %w", err) } extraKernelArgs.Append(constants.KernelParamConfigEarly, base64.StdEncoding.EncodeToString(buf.Bytes())) return nil } } func getDynamicPort(ctx context.Context, network string) (int, error) { var ( closeFn func() error addrFn func() net.Addr ) switch network { case "tcp", "tcp4", "tcp6": l, err := (&net.ListenConfig{}).Listen(ctx, network, "127.0.0.1:0") if err != nil { return 0, err } addrFn, closeFn = l.Addr, l.Close case "udp", "udp4", "udp6": l, err := (&net.ListenConfig{}).ListenPacket(ctx, network, "127.0.0.1:0") if err != nil { return 0, err } addrFn, closeFn = l.LocalAddr, l.Close default: return 0, fmt.Errorf("unsupported network: %s", network) } _, portStr, err := net.SplitHostPort(addrFn().String()) if err != nil { return 0, handleCloseErr(err, closeFn()) } port, err := strconv.Atoi(portStr) if err != nil { return 0, err } return port, handleCloseErr(nil, closeFn()) } func handleCloseErr(err error, closeErr error) error { switch { case err != nil && closeErr != nil: return fmt.Errorf("error: %w, close error: %w", err, closeErr) case err == nil && closeErr != nil: return closeErr case err != nil && closeErr == nil: return err default: return nil } } func checkPortsDontOverlap(ports ...int) error { slices.Sort(ports) if len(ports) != len(slices.Compact(ports)) { return errors.New("generated ports overlap") } return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/siderolinkbuilder/helpers_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux && !darwin package siderolinkbuilder import ( "errors" "net/netip" ) func generateRandomNodeAddr(prefix netip.Prefix) (netip.Prefix, error) { return netip.Prefix{}, nil } func networkPrefix(prefix string) (netip.Prefix, error) { return netip.Prefix{}, errors.New("unsupported platform") } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/internal/siderolinkbuilder/helpers_supported.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package siderolinkbuilder import ( "net/netip" "github.com/siderolabs/siderolink/pkg/wireguard" ) func generateRandomNodeAddr(prefix netip.Prefix) (netip.Prefix, error) { return wireguard.GenerateRandomNodeAddr(prefix) } func networkPrefix(prefix string) (netip.Prefix, error) { return wireguard.NetworkPrefix(prefix), nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/disk_image_preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import ( "fmt" "net/url" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/machinery/platforms" ) // DiskImage configures Talos to boot from a disk image from the Image Factory. type DiskImage struct{} // Name implements the Preset interface. func (DiskImage) Name() string { return "disk-image" } // Description implements the Preset interface. func (DiskImage) Description() string { return "Configure Talos to boot from a disk image from the Image Factory." } // ModifyOptions implements the Preset interface. func (DiskImage) ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { diskImageURL, err := url.JoinPath(presetOps.ImageFactoryURL.String(), "image", presetOps.SchematicID, cOps.TalosVersion, platforms.MetalPlatform().DiskImageDefaultPath(qOps.TargetArch)) if err != nil { return fmt.Errorf("failed to build an Image Factory disk-image url: %w", err) } qOps.NodeDiskImagePath = diskImageURL return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/iso_preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import ( "net/url" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/machinery/platforms" ) // ISO configures Talos to boot from an iso from the Image Factory. type ISO struct{} // Name implements the Preset interface. func (ISO) Name() string { return "iso" } // Description implements the Preset interface. func (ISO) Description() string { return "Configure Talos to boot from an ISO from the Image Factory." } // ModifyOptions implements the Preset interface. func (ISO) ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { isoURL, err := getISOURL(presetOps, cOps, qOps) if err != nil { return err } qOps.NodeISOPath = isoURL return nil } func getISOURL(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) (string, error) { isoPath := platforms.MetalPlatform().ISOPath(qOps.TargetArch) if presetOps.secureBoot { isoPath = platforms.MetalPlatform().SecureBootISOPath(qOps.TargetArch) } return url.JoinPath(presetOps.ImageFactoryURL.String(), "image", presetOps.SchematicID, cOps.TalosVersion, isoPath) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/iso_secureboot_preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" // ISOSecureBoot configures Talos to boot from a disk image from the Image Factory. type ISOSecureBoot struct{} // Name implements the Preset interface. func (ISOSecureBoot) Name() string { return "iso-secureboot" } // Description implements the Preset interface. func (ISOSecureBoot) Description() string { return "Configure Talos for Secureboot via ISO. Only available on Linux hosts." } // ModifyOptions implements the Preset interface. func (ISOSecureBoot) ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { isoURL, err := getISOURL(presetOps, cOps, qOps) if err != nil { return err } qOps.NodeISOPath = isoURL qOps.Tpm2Enabled = true qOps.DiskEncryptionKeyTypes = []string{"tpm"} qOps.EncryptEphemeralPartition = true qOps.EncryptStatePartition = true return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/maintenance_preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" // Maintenance configures Talos to boot from a disk image from the Image Factory. type Maintenance struct{} // Name implements the Preset interface. func (Maintenance) Name() string { return "maintenance" } // Description implements the Preset interface. func (Maintenance) Description() string { return "Skip applying machine configuration and leave the machines in maintenance mode. The machine configuration files are written to the working directory." } // ModifyOptions implements the Preset interface. func (Maintenance) ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { cOps.SkipInjectingConfig = true cOps.ApplyConfigEnabled = false return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import ( "errors" "flag" "fmt" "net/url" "runtime" "gopkg.in/typ.v4/slices" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" ) const secureBootSuffix = "-secureboot" // Preset modifies cluster create options to achieve certain behavior. type Preset interface { Name() string Description() string // ModifyOptions modifies configs to achieve the desired behavior ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error } // Options are the options required for presets to function. type Options struct { SchematicID string ImageFactoryURL *url.URL // secureBoot preset also affects other presets so this option needs to be shared. secureBoot bool } // Presets is a list of all available presets. var Presets = [...]Preset{ ISO{}, ISOSecureBoot{}, PXE{}, DiskImage{}, Maintenance{}, } // Apply validates and applies a set of multiple presets. func Apply(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu, presetNames []string) error { presets, err := slices.MapErr(presetNames, func(name string) (Preset, error) { if name == (ISOSecureBoot{}).Name() { presetOps.secureBoot = true } for _, p := range Presets { if p.Name() == name { return p, nil } } return nil, fmt.Errorf("error: unknown preset: %q", name) }) if err != nil { return err } err = Validate(presetNames, presetOps) if err != nil { return err } if err := applyDefaultSettings(presetOps, cOps, qOps); err != nil { return err } for _, p := range presets { err = p.ModifyOptions(presetOps, cOps, qOps) if err != nil { return fmt.Errorf("failed to apply %q preset: %w", p.Name(), err) } } return nil } // Validate checks if the provided presets are valid and compatible. // //nolint:gocyclo func Validate(presetNames []string, presetOps Options) error { bootMethodPresets := []string{ISO{}.Name(), PXE{}.Name(), DiskImage{}.Name(), ISOSecureBoot{}.Name()} // check if at least one boot method preset is selected, but no more than one bootMethodPresetCount := 0 for _, name := range presetNames { for _, bm := range bootMethodPresets { if name == bm { bootMethodPresetCount++ } } } if bootMethodPresetCount == 0 { return fmt.Errorf("error: at least one boot method preset must be specified (one of %v)", bootMethodPresets) } if bootMethodPresetCount > 1 { return fmt.Errorf("error: multiple boot method presets specified, please select only one (one of %v)", bootMethodPresets) } if presetOps.secureBoot && runtime.GOOS == "darwin" { // skip the check if it's a unit test environment if flag.Lookup("test.v") == nil { return errors.New("error: 'secureboot' preset is currently not supported on darwin") } } return nil } func applyDefaultSettings(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { installerName := "metal-installer" if presetOps.secureBoot { installerName += secureBootSuffix } installerURL, err := url.JoinPath(presetOps.ImageFactoryURL.Host, installerName, presetOps.SchematicID+":"+cOps.TalosVersion) if err != nil { return fmt.Errorf("failed to build installer image URL: %w", err) } qOps.NodeInstallImage = installerURL return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/preset_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset_test import ( "net/url" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/talosctl/cmd/constants" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset" ) func TestValidatePresets(t *testing.T) { imageFactoryURL, err := url.Parse(constants.ImageFactoryURL) require.NoError(t, err) tests := []struct { name string presets []string shouldFail bool }{ { name: "no presets", presets: []string{}, shouldFail: true, }, { name: "multiple boot method presets", presets: []string{preset.ISO{}.Name(), preset.PXE{}.Name()}, shouldFail: true, }, { name: "valid single boot method preset - iso", presets: []string{preset.ISO{}.Name()}, shouldFail: false, }, { name: "valid single boot method preset - pxe", presets: []string{preset.PXE{}.Name()}, shouldFail: false, }, { name: "valid single boot method preset - disk-image", presets: []string{preset.DiskImage{}.Name()}, shouldFail: false, }, { name: "valid boot method preset with maintenance", presets: []string{preset.ISO{}.Name(), preset.Maintenance{}.Name()}, shouldFail: false, }, { name: "iso-secureboot", presets: []string{preset.ISOSecureBoot{}.Name()}, shouldFail: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := preset.Validate(tt.presets, preset.Options{ SchematicID: constants.ImageFactoryEmptySchematicID, ImageFactoryURL: imageFactoryURL, }) if tt.shouldFail { require.Error(t, err) } else { require.NoError(t, err) } }) } } func applyPreset(t *testing.T, presets ...string) (clusterops.Common, clusterops.Qemu) { imageFactoryURL, err := url.Parse(constants.ImageFactoryURL) require.NoError(t, err) cOps := clusterops.GetCommon() qOps := clusterops.GetQemu() qOps.TargetArch = "arm64" cOps.TalosVersion = "v9.9.9" err = preset.Apply(preset.Options{ SchematicID: "123schematic123", ImageFactoryURL: imageFactoryURL, }, &cOps, &qOps, presets) require.NoError(t, err) return cOps, qOps } func TestPXE(t *testing.T) { _, qOps := applyPreset(t, preset.PXE{}.Name()) require.Equal(t, "factory.talos.dev/metal-installer/123schematic123:v9.9.9", qOps.NodeInstallImage) require.Equal(t, "https://factory.talos.dev/pxe/123schematic123/v9.9.9/metal-arm64", qOps.NodeIPXEBootScript) require.False(t, qOps.Tpm2Enabled) require.Empty(t, qOps.NodeISOPath) } func TestSecureboot(t *testing.T) { _, qOps := applyPreset(t, preset.ISOSecureBoot{}.Name()) require.Equal(t, "https://factory.talos.dev/image/123schematic123/v9.9.9/metal-arm64-secureboot.iso", qOps.NodeISOPath) require.True(t, qOps.Tpm2Enabled) require.Contains(t, qOps.DiskEncryptionKeyTypes, "tpm") require.True(t, qOps.EncryptEphemeralPartition) require.True(t, qOps.EncryptStatePartition) require.Equal(t, "factory.talos.dev/metal-installer-secureboot/123schematic123:v9.9.9", qOps.NodeInstallImage) } func TestDiskImage(t *testing.T) { _, qOps := applyPreset(t, preset.DiskImage{}.Name()) require.Equal(t, "https://factory.talos.dev/image/123schematic123/v9.9.9/metal-arm64.raw.zst", qOps.NodeDiskImagePath) } func TestMaintenance(t *testing.T) { cOps, _ := applyPreset(t, preset.Maintenance{}.Name(), preset.ISO{}.Name()) require.True(t, cOps.SkipInjectingConfig) require.False(t, cOps.ApplyConfigEnabled) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset/pxe_preset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package preset import ( "fmt" "net/url" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/machinery/platforms" ) // PXE configures Talos to boot from via pxe from the Image Factory. type PXE struct{} // Name implements the Preset interface. func (PXE) Name() string { return "pxe" } // Description implements the Preset interface. func (PXE) Description() string { return "Configure Talos to boot via PXE from the Image Factory." } // ModifyOptions implements the Preset interface. func (PXE) ModifyOptions(presetOps Options, cOps *clusterops.Common, qOps *clusterops.Qemu) error { pxeURL, err := url.JoinPath(presetOps.ImageFactoryURL.String(), "pxe", presetOps.SchematicID, cOps.TalosVersion, platforms.MetalPlatform().PXEScriptPath(qOps.TargetArch)) if err != nil { return fmt.Errorf("failed to build an Image Factory pxe url: %w", err) } qOps.NodeIPXEBootScript = pxeURL return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package clusterops import ( "fmt" "path/filepath" "runtime" "time" "github.com/docker/cli/opts" clustercmd "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/bytesize" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/provision" ) // ClusterConfigs is the configuration needed to create a talos cluster via the provisioner interface. type ClusterConfigs struct { ClusterRequest provision.ClusterRequest ProvisionOptions []provision.Option ConfigBundle *bundle.Bundle } // NodeResources represents CPU and Memory resources for a node. type NodeResources struct { CPU string Memory bytesize.ByteSize } // ParsedNodeResources represents parsed CPU and Memory resources for a node. type ParsedNodeResources struct { NanoCPUs int64 Memory bytesize.ByteSize } // Common are the options that are not specific to a single provider. type Common struct { // rootOps are the options from the root cluster command RootOps *clustercmd.CmdOps TalosconfigDestination string RegistryMirrors []string RegistryInsecure []string KubernetesVersion string ApplyConfigEnabled bool ConfigDebug bool NetworkCIDR string NetworkMTU int NetworkIPv4 bool DNSDomain string Workers int Controlplanes int ControlplaneResources NodeResources WorkerResources NodeResources ClusterWait bool ClusterWaitTimeout time.Duration ForceInitNodeAsEndpoint bool ForceEndpoint string ControlPlanePort int WithInitNode bool CustomCNIUrl string SkipKubeconfig bool SkipInjectingConfig bool TalosVersion string EnableKubeSpan bool EnableClusterDiscovery bool ConfigPatch []string ConfigPatchControlPlane []string ConfigPatchWorker []string KubePrismPort int SkipK8sNodeReadinessCheck bool WithJSONLogs bool WireguardCIDR string WithUUIDHostnames bool NetworkIPv6 bool OmniAPIEndpoint string } // Docker are options specific to docker provisioner. type Docker struct { HostIP string DisableIPv6 bool MountOpts opts.MountOpt Ports string TalosImage string } // Qemu are options specific to qemu provisioner. type Qemu struct { NodeInstallImage string NodeVmlinuzPath string NodeInitramfsPath string NodeISOPath string NodeUSBPath string NodeUKIPath string NodeDiskImagePath string NodeIPXEBootScript string BootloaderEnabled bool SkipInjectingExtraCmdline bool UefiEnabled bool Tpm1_2Enabled bool Tpm2Enabled bool ExtraUEFISearchPaths []string NetworkNoMasqueradeCIDRs []string Nameservers []string Disks flags.Disks DiskBlockSize uint PreallocateDisks bool ClusterUserVolumes []string TargetArch string CniBinPath []string CniConfDir string CniCacheDir string CniBundleURL string EncryptStatePartition bool EncryptEphemeralPartition bool EncryptUserVolumes bool UseVIP bool BadRTC bool ExtraBootKernelArgs string DHCPSkipHostname bool NetworkChaos bool Jjitter time.Duration Latency time.Duration PacketLoss float64 PacketReorder float64 PacketCorrupt float64 Bandwidth int DiskEncryptionKeyTypes []string WithFirewall string WithSiderolinkAgent flags.Agent DebugShellEnabled bool WithIOMMU bool ConfigInjectionMethod string Airgapped bool ImageCachePath string ImageCacheTLSCertFile string ImageCacheTLSKeyFile string ImageCachePort uint16 } // GetCommon returns the default common options. func GetCommon() Common { memory2GB := bytesize.WithDefaultUnit("MiB") cli.Should(memory2GB.Set("2.0GiB")) defaultResources := NodeResources{ CPU: "2.0", Memory: *memory2GB, } return Common{ Controlplanes: 1, ControlplaneResources: defaultResources, Workers: 1, WorkerResources: defaultResources, NetworkCIDR: "10.5.0.0/24", KubernetesVersion: constants.DefaultKubernetesVersion, NetworkMTU: 1500, ClusterWaitTimeout: 20 * time.Minute, ClusterWait: true, DNSDomain: "cluster.local", ControlPlanePort: constants.DefaultControlPlanePort, RootOps: &clustercmd.PersistentFlags, // TODO: move this elsewhere NetworkIPv4: true, KubePrismPort: constants.DefaultKubePrismPort, EnableClusterDiscovery: true, TalosVersion: helpers.GetTag(), } } // GetQemu returns default QEMU options. func GetQemu() Qemu { disks := flags.Disks{} cli.Should(disks.Set("virtio:10GiB,virtio:6GiB")) return Qemu{ PreallocateDisks: false, BootloaderEnabled: true, UefiEnabled: true, Nameservers: defaultNameservers, DiskBlockSize: 512, TargetArch: runtime.GOARCH, CniBinPath: []string{filepath.Join(clustercmd.DefaultCNIDir, "bin")}, CniConfDir: filepath.Join(clustercmd.DefaultCNIDir, "conf.d"), CniCacheDir: filepath.Join(clustercmd.DefaultCNIDir, "cache"), CniBundleURL: fmt.Sprintf("https://github.com/%s/talos/releases/download/%s/talosctl-cni-bundle-%s.tar.gz", images.Username, version.Trim(version.Tag), constants.ArchVariable), Disks: disks, ImageCachePort: 5000, } } // GetDocker returns default Docker options. func GetDocker() Docker { return Docker{ HostIP: "0.0.0.0", TalosImage: helpers.DefaultImage(images.DefaultTalosImageRepository), } } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/options_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package clusterops var defaultNameservers = []string{} ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/clusterops/options_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux package clusterops var defaultNameservers = []string{"8.8.8.8", "1.1.1.1", "2001:4860:4860::8888", "2606:4700:4700::1111"} ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/cmd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package create provides way to create talos clusters package create import ( "fmt" "path/filepath" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/pkg/bytesize" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/constants" ) var ( workersFlagName = "workers" controlplanesFlagName = "controlplanes" kubernetesVersionFlagName = "kubernetes-version" registryMirrorFlagName = "registry-mirror" networkMTUFlagName = "mtu" networkCIDRFlagName = "cidr" talosVersionFlagName = "talos-version" // Flags that have been renamed in the user-facing commands. controlPlaneCpusFlagName = "cpus-controlplanes" controlPlaneMemoryFlagName = "memory-controlplanes" workersCpusFlagName = "cpus-workers" workersMemoryFlagName = "memory-workers" configPatchFlagName = "config-patch" configPatchControlPlaneFlagName = "config-patch-controlplanes" configPatchWorkerFlagName = "config-patch-workers" talosconfigDestinationFlagName = "talosconfig-destination" // Qemu flags. disksFlagName = "disks" omniAPIEndpointFlagName = "omni-api-endpoint" ) func getCommonUserFacingFlags(pointer *clusterops.Common) *pflag.FlagSet { common := pflag.NewFlagSet("common", pflag.PanicOnError) addWorkersFlag(common, &pointer.Workers) addKubernetesVersionFlag(common, &pointer.KubernetesVersion) addTalosconfigDestinationFlag(common, &pointer.TalosconfigDestination, talosconfigDestinationFlagName) addConfigPatchFlag(common, &pointer.ConfigPatch, configPatchFlagName) addConfigPatchControlPlaneFlag(common, &pointer.ConfigPatchControlPlane, configPatchControlPlaneFlagName) addConfigPatchWorkerFlag(common, &pointer.ConfigPatchWorker, configPatchWorkerFlagName) addControlplaneCpusFlag(common, &pointer.ControlplaneResources.CPU, controlPlaneCpusFlagName) addWorkersCpusFlag(common, &pointer.WorkerResources.CPU, workersCpusFlagName) addControlPlaneMemoryFlag(common, &pointer.ControlplaneResources.Memory, controlPlaneMemoryFlagName) addWorkersMemoryFlag(common, &pointer.WorkerResources.Memory, workersMemoryFlagName) // The following flags are used in tests and development addNetworkMTUFlag(common, &pointer.NetworkMTU) cli.Should(common.MarkHidden(networkMTUFlagName)) addRegistryMirrorFlag(common, &pointer.RegistryMirrors) cli.Should(common.MarkHidden(registryMirrorFlagName)) return common } // Common flags func addTalosconfigDestinationFlag(flagset *pflag.FlagSet, bind *string, flagName string) { flagset.StringVar(bind, flagName, "", fmt.Sprintf("The location to save the generated Talos configuration file to. Defaults to '%s' env variable if set, otherwise '%s' and '%s' in order.", constants.TalosConfigEnvVar, filepath.Join("$HOME", constants.TalosDir, constants.TalosconfigFilename), filepath.Join(constants.ServiceAccountMountPath, constants.TalosconfigFilename), ), ) } func addControlplaneCpusFlag(flagset *pflag.FlagSet, bind *string, flagName string) { flagset.StringVar(bind, flagName, *bind, "the share of CPUs as fraction for each control plane/VM") } func addWorkersCpusFlag(flagset *pflag.FlagSet, bind *string, flagName string) { flagset.StringVar(bind, flagName, *bind, "the share of CPUs as fraction for each worker/VM") } func addControlPlaneMemoryFlag(flagset *pflag.FlagSet, bind *bytesize.ByteSize, flagName string) { flagset.Var(bind, flagName, "the limit on memory usage for each control plane/VM") } func addWorkersMemoryFlag(flagset *pflag.FlagSet, bind *bytesize.ByteSize, flagName string) { flagset.Var(bind, flagName, "the limit on memory usage for each worker/VM") } func addConfigPatchFlag(flagset *pflag.FlagSet, bind *[]string, flagName string) { flagset.StringArrayVar(bind, flagName, nil, "patch generated machineconfigs (applied to all node types), use @file to read a patch from file") } func addConfigPatchControlPlaneFlag(flagset *pflag.FlagSet, bind *[]string, flagName string) { flagset.StringArrayVar(bind, flagName, nil, "patch generated machineconfigs (applied to 'controlplane' type)") } func addConfigPatchWorkerFlag(flagset *pflag.FlagSet, bind *[]string, flagName string) { flagset.StringArrayVar(bind, flagName, nil, "patch generated machineconfigs (applied to 'worker' type)") } func addWorkersFlag(flagset *pflag.FlagSet, bind *int) { flagset.IntVar(bind, workersFlagName, *bind, "the number of workers to create") } func addControlplanesFlag(flagset *pflag.FlagSet, bind *int) { flagset.IntVar(bind, controlplanesFlagName, *bind, "the number of controlplanes to create") } func addKubernetesVersionFlag(flagset *pflag.FlagSet, bind *string) { flagset.StringVar(bind, kubernetesVersionFlagName, *bind, "desired kubernetes version to run") } func addRegistryMirrorFlag(flagset *pflag.FlagSet, bind *[]string) { flagset.StringSliceVar(bind, registryMirrorFlagName, []string{}, "list of registry mirrors to use in format: =") } func addNetworkMTUFlag(flagset *pflag.FlagSet, bind *int) { flagset.IntVar(bind, networkMTUFlagName, *bind, "MTU of the cluster network") } func addTalosVersionFlag(flagset *pflag.FlagSet, bind *string, description string) { flagset.StringVar(bind, talosVersionFlagName, *bind, description) } // qemu flags func addDisksFlag(flagset *pflag.FlagSet, bind *flags.Disks) { flagset.Var(bind, disksFlagName, `list of disks to create in format ":" (disks after the first one are added only to worker machines)`) } func addOmniJoinTokenFlag(cmd *cobra.Command, bindAPIEndpoint *string, cfgPatchAllFlagName, cfgPatchWorkersFlagName, cfgPatchCPsFlagName string) { cmd.Flags().StringVar(bindAPIEndpoint, omniAPIEndpointFlagName, *bindAPIEndpoint, "the Omni API endpoint (must include a scheme, a hostname and a join token, e.g. 'https://siderolink.omni.example?jointoken=foobar')") cmd.MarkFlagsMutuallyExclusive(omniAPIEndpointFlagName, cfgPatchAllFlagName) cmd.MarkFlagsMutuallyExclusive(omniAPIEndpointFlagName, cfgPatchWorkersFlagName) cmd.MarkFlagsMutuallyExclusive(omniAPIEndpointFlagName, cfgPatchCPsFlagName) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/cmd_dev.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "fmt" "runtime" "slices" "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" clustercmd "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/constants" ) type legacyOps struct { clusterDiskSize int extraDisks int extraDiskSize int extraDisksDrivers []string extraDisksTags []string extraDisksSerials []string } var ( createCmd = getCreateCmd("create", true) createDevCmd = getCreateCmd("dev", false) ) //nolint:gocyclo func getCreateCmd(cmdName string, hidden bool) *cobra.Command { const ( networkIPv4Flag = "ipv4" networkIPv6Flag = "ipv6" networkNoMasqueradeCIDRsFlag = "no-masquerade-cidrs" nameserversFlag = "nameservers" preallocateDisksFlag = "disk-preallocate" clusterUserVolumesFlag = "user-volumes" clusterDiskSizeFlag = "disk" diskBlockSizeFlag = "disk-block-size" useVIPFlag = "use-vip" bootloaderEnabledFlag = "with-bootloader" skipInjectingExtraCmdlineFlag = "skip-injecting-extra-cmdline" controlPlanePortFlag = "control-plane-port" firewallFlag = "with-firewall" tpmEnabledFlag = "with-tpm1_2" tpm2EnabledFlag = "with-tpm2" withDebugShellFlag = "with-debug-shell" withIOMMUFlag = "with-iommu" talosconfigFlag = "talosconfig" applyConfigEnabledFlag = "with-apply-config" wireguardCIDRFlag = "wireguard-cidr" controlPlaneCpusFlag = "cpus" workersCpusFlag = "cpus-workers" controlPlaneMemoryFlag = "memory" workersMemoryFlag = "memory-workers" clusterWaitFlag = "wait" clusterWaitTimeoutFlag = "wait-timeout" forceInitNodeAsEndpointFlag = "init-node-as-endpoint" withInitNodeFlag = "with-init-node" skipKubeconfigFlag = "skip-kubeconfig" skipInjectingConfigFlag = "skip-injecting-config" configPatchFlag = "config-patch" configPatchControlPlaneFlag = "config-patch-control-plane" configPatchWorkerFlag = "config-patch-worker" skipK8sNodeReadinessCheckFlag = "skip-k8s-node-readiness-check" withJSONLogsFlag = "with-json-logs" nodeVmlinuzPathFlag = "vmlinuz-path" nodeISOPathFlag = "iso-path" nodeUSBPathFlag = "usb-path" nodeUKIPathFlag = "uki-path" nodeInitramfsPathFlag = "initrd-path" nodeDiskImagePathFlag = "disk-image-path" nodeIPXEBootScriptFlag = "ipxe-boot-script" uefiEnabledFlag = "with-uefi" extraUEFISearchPathsFlag = "extra-uefi-search-paths" extraDisksFlag = "extra-disks" extraDisksDriversFlag = "extra-disks-drivers" extraDisksTagsFlag = "extra-disks-tags" extraDisksSerialsFlag = "extra-disks-serials" extraDiskSizeFlag = "extra-disks-size" targetArchFlag = "arch" cniBinPathFlag = "cni-bin-path" cniConfDirFlag = "cni-conf-dir" cniCacheDirFlag = "cni-cache-dir" cniBundleURLFlag = "cni-bundle-url" badRTCFlag = "bad-rtc" extraBootKernelArgsFlag = "extra-boot-kernel-args" dhcpSkipHostnameFlag = "disable-dhcp-hostname" networkChaosFlag = "with-network-chaos" jitterFlag = "with-network-jitter" latencyFlag = "with-network-latency" packetLossFlag = "with-network-packet-loss" packetReorderFlag = "with-network-packet-reorder" packetCorruptFlag = "with-network-packet-corrupt" bandwidthFlag = "with-network-bandwidth" withUUIDHostnamesFlag = "with-uuid-hostnames" withSiderolinkAgentFlag = "with-siderolink" configInjectionMethodFlag = "config-injection-method" airgappedFlag = "airgapped" imageCachePathFlag = "image-cache-path" imageCacheTLSCertFileFlag = "image-cache-tls-cert-file" imageCacheTLSKeyFileFlag = "image-cache-tls-key-file" imageCachePortFlag = "image-cache-port" // The following flags are the gen options - the options that are only used in machine configuration (i.e., not during the qemu/docker provisioning). // They are not applicable when no machine configuration is generated, hence mutually exclusive with the --input-dir flag. nodeInstallImageFlag = "install-image" configDebugFlag = "with-debug" dnsDomainFlag = "dns-domain" withClusterDiscoveryFlag = "with-cluster-discovery" registryInsecureFlag = "registry-insecure-skip-verify" customCNIUrlFlag = "custom-cni-url" encryptStatePartitionFlag = "encrypt-state" encryptEphemeralPartitionFlag = "encrypt-ephemeral" encryptUserVolumeFlag = "encrypt-user-volumes" enableKubeSpanFlag = "with-kubespan" forceEndpointFlag = "endpoint" kubePrismFlag = "kubeprism-port" diskEncryptionKeyTypesFlag = "disk-encryption-key-types" ) unImplementedFlagsDarwin := []string{ networkNoMasqueradeCIDRsFlag, cniBinPathFlag, cniConfDirFlag, cniCacheDirFlag, cniBundleURLFlag, badRTCFlag, networkChaosFlag, jitterFlag, latencyFlag, packetLossFlag, packetReorderFlag, packetCorruptFlag, bandwidthFlag, airgappedFlag, // The following might work but need testing first. configInjectionMethodFlag, } qOps := clusterops.GetQemu() cOps := clusterops.GetCommon() legacyOps := legacyOps{} getCommonFlags := func() *pflag.FlagSet { common := pflag.NewFlagSet("common", pflag.PanicOnError) addControlplaneCpusFlag(common, &cOps.ControlplaneResources.CPU, controlPlaneCpusFlag) addWorkersCpusFlag(common, &cOps.WorkerResources.CPU, workersCpusFlag) addControlPlaneMemoryFlag(common, &cOps.ControlplaneResources.Memory, controlPlaneMemoryFlag) addWorkersMemoryFlag(common, &cOps.WorkerResources.Memory, workersMemoryFlag) addWorkersFlag(common, &cOps.Workers) addControlplanesFlag(common, &cOps.Controlplanes) addKubernetesVersionFlag(common, &cOps.KubernetesVersion) addTalosconfigDestinationFlag(common, &cOps.TalosconfigDestination, talosconfigFlag) addConfigPatchFlag(common, &cOps.ConfigPatch, configPatchFlag) addConfigPatchControlPlaneFlag(common, &cOps.ConfigPatchControlPlane, configPatchControlPlaneFlag) addConfigPatchWorkerFlag(common, &cOps.ConfigPatchWorker, configPatchWorkerFlag) addRegistryMirrorFlag(common, &cOps.RegistryMirrors) addNetworkMTUFlag(common, &cOps.NetworkMTU) addTalosVersionFlag(common, &cOps.TalosVersion, "the desired Talos version to generate config for") common.StringVar(&cOps.NetworkCIDR, networkCIDRFlagName, cOps.NetworkCIDR, "CIDR of the cluster network (IPv4, ULA network for IPv6 is derived in automated way)") common.StringVar(&cOps.WireguardCIDR, wireguardCIDRFlag, cOps.WireguardCIDR, "CIDR of the wireguard network") common.BoolVar(&cOps.ApplyConfigEnabled, applyConfigEnabledFlag, cOps.ApplyConfigEnabled, "enable apply config when the VM is starting in maintenance mode") common.StringSliceVar(&cOps.RegistryInsecure, registryInsecureFlag, cOps.RegistryInsecure, "list of registry hostnames to skip TLS verification for") common.IntVar(&cOps.ControlPlanePort, controlPlanePortFlag, cOps.ControlPlanePort, "control plane port (load balancer and local API port)") common.BoolVar(&cOps.ConfigDebug, configDebugFlag, cOps.ConfigDebug, "enable debug in Talos config to send service logs to the console") common.BoolVar(&cOps.NetworkIPv4, networkIPv4Flag, cOps.NetworkIPv4, "enable IPv4 network in the cluster") common.BoolVar(&cOps.ClusterWait, clusterWaitFlag, cOps.ClusterWait, "wait for the cluster to be ready before returning") common.DurationVar(&cOps.ClusterWaitTimeout, clusterWaitTimeoutFlag, cOps.ClusterWaitTimeout, "timeout to wait for the cluster to be ready") common.BoolVar(&cOps.ForceInitNodeAsEndpoint, forceInitNodeAsEndpointFlag, cOps.ForceInitNodeAsEndpoint, "use init node as endpoint instead of any load balancer endpoint") common.StringVar(&cOps.ForceEndpoint, forceEndpointFlag, cOps.ForceEndpoint, "use endpoint instead of provider defaults") common.BoolVar(&cOps.WithInitNode, withInitNodeFlag, cOps.WithInitNode, "create the cluster with an init node") common.StringVar(&cOps.CustomCNIUrl, customCNIUrlFlag, cOps.CustomCNIUrl, "install custom CNI from the URL (Talos cluster)") common.StringVar(&cOps.DNSDomain, dnsDomainFlag, cOps.DNSDomain, "the dns domain to use for cluster") common.BoolVar(&cOps.SkipKubeconfig, skipKubeconfigFlag, cOps.SkipKubeconfig, "skip merging kubeconfig from the created cluster") common.BoolVar(&cOps.SkipInjectingConfig, skipInjectingConfigFlag, cOps.SkipInjectingConfig, "skip injecting config from embedded metadata server, write config files to current directory") common.BoolVar(&cOps.EnableClusterDiscovery, withClusterDiscoveryFlag, cOps.EnableClusterDiscovery, "enable cluster discovery") common.BoolVar(&cOps.EnableKubeSpan, enableKubeSpanFlag, cOps.EnableKubeSpan, "enable KubeSpan system") common.IntVar(&cOps.KubePrismPort, kubePrismFlag, cOps.KubePrismPort, "KubePrism port (set to 0 to disable)") common.BoolVar(&cOps.SkipK8sNodeReadinessCheck, skipK8sNodeReadinessCheckFlag, cOps.SkipK8sNodeReadinessCheck, "skip k8s node readiness checks") common.BoolVar(&cOps.WithJSONLogs, withJSONLogsFlag, cOps.WithJSONLogs, "enable JSON logs receiver and configure Talos to send logs there") common.BoolVar(&cOps.WithUUIDHostnames, withUUIDHostnamesFlag, cOps.WithUUIDHostnames, "use machine UUIDs as default hostnames") common.BoolVar(&cOps.NetworkIPv6, networkIPv6Flag, cOps.NetworkIPv6, "enable IPv6 network in the cluster") return common } getQemuFlags := func() *pflag.FlagSet { qemu := pflag.NewFlagSet("qemu", pflag.PanicOnError) qemu.BoolVar(&qOps.PreallocateDisks, preallocateDisksFlag, true, "whether disk space should be preallocated") qemu.StringSliceVar(&qOps.ClusterUserVolumes, clusterUserVolumesFlag, qOps.ClusterUserVolumes, "list of user volumes to create for each VM in format: :::") qemu.StringVar(&qOps.NodeInstallImage, nodeInstallImageFlag, helpers.DefaultImage(images.DefaultInstallerImageRepository), "the installer image to use") qemu.StringVar(&qOps.NodeVmlinuzPath, nodeVmlinuzPathFlag, helpers.ArtifactPath(constants.KernelAssetWithArch), "the compressed kernel image to use") qemu.StringVar(&qOps.NodeISOPath, nodeISOPathFlag, qOps.NodeISOPath, "the ISO path to use for the initial boot") qemu.StringVar(&qOps.NodeUSBPath, nodeUSBPathFlag, qOps.NodeUSBPath, "the USB stick image path to use for the initial boot") qemu.StringVar(&qOps.NodeUKIPath, nodeUKIPathFlag, qOps.NodeUKIPath, "the UKI image path to use for the initial boot") qemu.StringVar(&qOps.NodeInitramfsPath, nodeInitramfsPathFlag, helpers.ArtifactPath(constants.InitramfsAssetWithArch), "initramfs image to use") qemu.StringVar(&qOps.NodeDiskImagePath, nodeDiskImagePathFlag, qOps.NodeDiskImagePath, "disk image to use") qemu.StringVar(&qOps.NodeIPXEBootScript, nodeIPXEBootScriptFlag, qOps.NodeIPXEBootScript, "iPXE boot script (URL) to use") qemu.BoolVar(&qOps.BootloaderEnabled, bootloaderEnabledFlag, qOps.BootloaderEnabled, "enable bootloader to load kernel and initramfs from disk image after install") qemu.BoolVar(&qOps.SkipInjectingExtraCmdline, skipInjectingExtraCmdlineFlag, qOps.SkipInjectingExtraCmdline, "skip injecting extra kernel cmdline parameters via EFI vars through bootloader") qemu.BoolVar(&qOps.UefiEnabled, uefiEnabledFlag, qOps.UefiEnabled, "enable UEFI on x86_64 architecture") qemu.BoolVar(&qOps.Tpm1_2Enabled, tpmEnabledFlag, qOps.Tpm1_2Enabled, "enable TPM 1.2 emulation support using swtpm") qemu.BoolVar(&qOps.Tpm2Enabled, tpm2EnabledFlag, qOps.Tpm2Enabled, "enable TPM 2.0 emulation support using swtpm") qemu.BoolVar(&qOps.DebugShellEnabled, withDebugShellFlag, qOps.DebugShellEnabled, "drop talos into a maintenance shell on boot, this is for advanced debugging for developers only") qemu.BoolVar(&qOps.WithIOMMU, withIOMMUFlag, qOps.WithIOMMU, "enable IOMMU support, this also add a new PCI root port and an interface attached to it") qemu.MarkHidden("with-debug-shell") //nolint:errcheck qemu.StringSliceVar(&qOps.ExtraUEFISearchPaths, extraUEFISearchPathsFlag, qOps.ExtraUEFISearchPaths, "additional search paths for UEFI firmware (only applies when UEFI is enabled)") qemu.StringSliceVar(&qOps.NetworkNoMasqueradeCIDRs, networkNoMasqueradeCIDRsFlag, qOps.NetworkNoMasqueradeCIDRs, "list of CIDRs to exclude from NAT") qemu.StringSliceVar(&qOps.Nameservers, nameserversFlag, qOps.Nameservers, "list of nameservers to use") qemu.UintVar(&qOps.DiskBlockSize, diskBlockSizeFlag, qOps.DiskBlockSize, "disk block size") qemu.StringVar(&qOps.TargetArch, targetArchFlag, qOps.TargetArch, "cluster architecture") qemu.StringSliceVar(&qOps.CniBinPath, cniBinPathFlag, qOps.CniBinPath, "search path for CNI binaries") qemu.StringVar(&qOps.CniConfDir, cniConfDirFlag, qOps.CniConfDir, "CNI config directory path") qemu.StringVar(&qOps.CniCacheDir, cniCacheDirFlag, qOps.CniCacheDir, "CNI cache directory path") qemu.StringVar(&qOps.CniBundleURL, cniBundleURLFlag, qOps.CniBundleURL, "URL to download CNI bundle from") qemu.BoolVar(&qOps.EncryptStatePartition, encryptStatePartitionFlag, qOps.EncryptStatePartition, "enable state partition encryption") qemu.BoolVar(&qOps.EncryptEphemeralPartition, encryptEphemeralPartitionFlag, qOps.EncryptEphemeralPartition, "enable ephemeral partition encryption") qemu.BoolVar(&qOps.EncryptUserVolumes, encryptUserVolumeFlag, qOps.EncryptUserVolumes, "enable ephemeral partition encryption") qemu.StringArrayVar(&qOps.DiskEncryptionKeyTypes, diskEncryptionKeyTypesFlag, []string{"uuid"}, "encryption key types to use for disk encryption (uuid, kms)") qemu.BoolVar(&qOps.UseVIP, useVIPFlag, qOps.UseVIP, "use a virtual IP for the controlplane endpoint instead of the loadbalancer") qemu.BoolVar(&qOps.BadRTC, badRTCFlag, qOps.BadRTC, "launch VM with bad RTC state") qemu.StringVar(&qOps.ExtraBootKernelArgs, extraBootKernelArgsFlag, qOps.ExtraBootKernelArgs, "add extra kernel args to the initial boot from vmlinuz and initramfs") qemu.BoolVar(&qOps.DHCPSkipHostname, dhcpSkipHostnameFlag, qOps.DHCPSkipHostname, "skip announcing hostname via DHCP") qemu.BoolVar(&qOps.NetworkChaos, networkChaosFlag, qOps.NetworkChaos, "enable to use network chaos parameters") qemu.DurationVar(&qOps.Jjitter, jitterFlag, qOps.Jjitter, "specify jitter on the bridge interface") qemu.DurationVar(&qOps.Latency, latencyFlag, qOps.Latency, "specify latency on the bridge interface") qemu.Float64Var(&qOps.PacketLoss, packetLossFlag, qOps.PacketLoss, "specify percent of packet loss on the bridge interface. e.g. 50% = 0.50 (default: 0.0)") qemu.Float64Var(&qOps.PacketReorder, packetReorderFlag, qOps.PacketReorder, "specify percent of reordered packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0)") qemu.Float64Var(&qOps.PacketCorrupt, packetCorruptFlag, qOps.PacketCorrupt, "specify percent of corrupt packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0)") qemu.IntVar(&qOps.Bandwidth, bandwidthFlag, qOps.Bandwidth, "specify bandwidth restriction (in kbps) on the bridge interface") qemu.StringVar(&qOps.WithFirewall, firewallFlag, qOps.WithFirewall, "inject firewall rules into the cluster, value is default policy - accept/block") qemu.Var(&qOps.WithSiderolinkAgent, withSiderolinkAgentFlag, "enables the use of siderolink agent as configuration apply mechanism. `true` or `wireguard` enables the agent, `tunnel` enables the agent with grpc tunneling") qemu.StringVar(&qOps.ConfigInjectionMethod, configInjectionMethodFlag, qOps.ConfigInjectionMethod, "a method to inject machine config: default is HTTP server, 'metal-iso' to mount an ISO") qemu.BoolVar(&qOps.Airgapped, airgappedFlag, qOps.Airgapped, "limit VM network access to the provisioning network only") qemu.StringVar(&qOps.ImageCachePath, imageCachePathFlag, qOps.ImageCachePath, "path to image cache") qemu.StringVar(&qOps.ImageCacheTLSCertFile, imageCacheTLSCertFileFlag, qOps.ImageCacheTLSCertFile, "path to image cache TLS cert") qemu.StringVar(&qOps.ImageCacheTLSKeyFile, imageCacheTLSKeyFileFlag, qOps.ImageCacheTLSKeyFile, "path to image cache TLS key") qemu.Uint16Var(&qOps.ImageCachePort, imageCachePortFlag, qOps.ImageCachePort, "port on which to serve image cache") return qemu } // createCmd is the developer oriented create command. createCmd := &cobra.Command{ Use: cmdName, Short: "Creates a local QEMU-based cluster for Talos development.", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), func(ctx context.Context) error { if cmdName == "create" { cli.Warning("the developer oriented 'cluster create' command has been moved to 'cluster create dev'") } if err := validateQemuFlags(cmd.Flags(), unImplementedFlagsDarwin); err != nil { return err } var disks strings.Builder fmt.Fprintf(&disks, "virtio:%d", legacyOps.clusterDiskSize) for i := range legacyOps.extraDisks { driver := "ide" tag := "" serial := "" // ide driver is not supported on arm64 if qOps.TargetArch == "arm64" { driver = "virtio" } if i < len(legacyOps.extraDisksDrivers) { driver = legacyOps.extraDisksDrivers[i] } if i < len(legacyOps.extraDisksTags) { if legacyOps.extraDisksTags[i] != "" { tag = fmt.Sprintf(":tag=%s", legacyOps.extraDisksTags[i]) } } if i < len(legacyOps.extraDisksSerials) { if legacyOps.extraDisksSerials[i] != "" { serial = fmt.Sprintf(":serial=%s", legacyOps.extraDisksSerials[i]) } } fmt.Fprintf(&disks, ",%s:%d%s%s", driver, legacyOps.extraDiskSize, tag, serial) } qOps.Disks = flags.Disks{} if err := qOps.Disks.Set(disks.String()); err != nil { return err } return createDevCluster(ctx, cOps, qOps) }) }, } createCmd.Flags().IntVar(&legacyOps.clusterDiskSize, clusterDiskSizeFlag, 6*1024, "default limit on disk size in MB (each VM)") createCmd.Flags().IntVar(&legacyOps.extraDisks, extraDisksFlag, 0, "number of extra disks to create for each worker VM") createCmd.Flags().StringSliceVar(&legacyOps.extraDisksDrivers, extraDisksDriversFlag, nil, "driver for each extra disk (virtio, ide, ahci, scsi, nvme, megaraid)") createCmd.Flags().StringSliceVar(&legacyOps.extraDisksTags, extraDisksTagsFlag, nil, "tags for each extra disk (only used by virtiofs)") createCmd.Flags().StringSliceVar(&legacyOps.extraDisksSerials, extraDisksSerialsFlag, nil, "serials for each extra disk") createCmd.Flags().IntVar(&legacyOps.extraDiskSize, extraDiskSizeFlag, 5*1024, "default limit on disk size in MB (each VM)") clustercmd.AddProvisionerFlag(createCmd) cli.Should(createCmd.Flags().MarkHidden(clustercmd.ProvisionerFlagName)) createCmd.Flags().AddFlagSet(getCommonFlags()) createCmd.Flags().AddFlagSet(getQemuFlags()) addOmniJoinTokenFlag(createCmd, &cOps.OmniAPIEndpoint, configPatchFlag, configPatchWorkerFlag, configPatchControlPlaneFlag) createCmd.MarkFlagsMutuallyExclusive(tpmEnabledFlag, tpm2EnabledFlag) hideUnimplementedQemuFlags(createCmd, unImplementedFlagsDarwin) if hidden { createCmd.Flags().VisitAll(func(f *pflag.Flag) { f.Hidden = true }) createCmd.Short = "Create a local Talos cluster." createCmd.DisableFlagsInUseLine = true } return createCmd } func init() { createCmd.AddCommand(createDevCmd) clustercmd.Cmd.AddCommand(createCmd) } func validateQemuFlags(allCmdFlags *pflag.FlagSet, unImplementedQemuFlagsDarwin []string) error { errMsg := "" allCmdFlags.VisitAll(func(f *pflag.Flag) { if f.Changed { if runtime.GOOS == "darwin" && slices.Contains(unImplementedQemuFlagsDarwin, f.Name) { errMsg += fmt.Sprintf("the \"%s\" flag is not supported on macos\n", f.Name) } } }) if errMsg != "" { return fmt.Errorf("%sinvalid flags found", errMsg) } return nil } func hideUnimplementedQemuFlags(cmd *cobra.Command, unImplementedQemuFlagsDarwin []string) { cmd.Flags().VisitAll(func(f *pflag.Flag) { if runtime.GOOS != "darwin" { return } for _, unimplemented := range unImplementedQemuFlagsDarwin { if f.Name == unimplemented { f.Hidden = true } } }) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/cmd_docker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "github.com/spf13/cobra" "github.com/spf13/pflag" clustercmd "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/provision/providers" ) func init() { dOps := clusterops.GetDocker() cOps := clusterops.GetCommon() const ( portsFlag = "exposed-ports" dockerDisableIPv6Flag = "disable-ipv6" dockerHostIPFlag = "host-ip" mountOptsFlag = "mount" subnetFlag = "subnet" ) getDockerFlags := func() *pflag.FlagSet { docker := pflag.NewFlagSet("docker", pflag.PanicOnError) docker.StringVarP(&dOps.Ports, portsFlag, "p", dOps.Ports, "comma-separated list of ports/protocols to expose on init node. Ex -p :/") docker.StringVar(&dOps.HostIP, dockerHostIPFlag, dOps.HostIP, "Host IP to forward exposed ports to") docker.BoolVar(&dOps.DisableIPv6, dockerDisableIPv6Flag, dOps.DisableIPv6, "skip enabling IPv6 in containers") cli.Should(docker.MarkHidden(dockerDisableIPv6Flag)) docker.Var(&dOps.MountOpts, mountOptsFlag, "attach a mount to the container (docker --mount syntax)") docker.StringVar(&dOps.TalosImage, "image", dOps.TalosImage, "the talos image to run") return docker } commonFlags := getCommonUserFacingFlags(&cOps) commonFlags.StringVar(&cOps.NetworkCIDR, subnetFlag, cOps.NetworkCIDR, "Docker network subnet CIDR") createDockerCmd := &cobra.Command{ Use: "docker", Short: "Create a local Docker based kubernetes cluster", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), func(ctx context.Context) error { provisioner, err := providers.Factory(ctx, providers.DockerProviderName) if err != nil { return err } clusterConfigs, err := getDockerClusterRequest(cOps, dOps, provisioner) if err != nil { return err } cluster, err := provisioner.Create(ctx, clusterConfigs.ClusterRequest, clusterConfigs.ProvisionOptions...) if err != nil { return err } err = postCreate(ctx, cOps, cluster, clusterConfigs) if err != nil { return err } return clustercmd.ShowCluster(cluster) }) }, } createDockerCmd.Flags().AddFlagSet(getDockerFlags()) createDockerCmd.Flags().AddFlagSet(commonFlags) createCmd.AddCommand(createDockerCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/cmd_qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/siderolabs/talos/cmd/talosctl/cmd/constants" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/provision/providers" ) type presetOptions struct { schematicID string imageFactoryURL string presets []string } func init() { presetOptions := presetOptions{} qOps := clusterops.GetQemu() cOps := clusterops.GetCommon() cOps.SkipInjectingConfig = true cOps.ApplyConfigEnabled = true commonFlags := getCommonUserFacingFlags(&cOps) addControlplanesFlag(commonFlags, &cOps.Controlplanes) addTalosVersionFlag(commonFlags, &cOps.TalosVersion, "the desired talos version") commonFlags.StringVar(&cOps.NetworkCIDR, networkCIDRFlagName, "10.5.0.0/24", "CIDR of the cluster network") getQemuFlags := func() *pflag.FlagSet { qemu := pflag.NewFlagSet("qemu", pflag.PanicOnError) addDisksFlag(qemu, &qOps.Disks) qemu.StringVar(&presetOptions.schematicID, "schematic-id", "", "image factory schematic id (defaults to an empty schematic)") qemu.StringVar(&presetOptions.imageFactoryURL, "image-factory-url", constants.ImageFactoryURL, "image factory url") qemu.StringSliceVar(&presetOptions.presets, "presets", []string{preset.ISO{}.Name()}, "list of presets to apply") return qemu } var cmdDescription strings.Builder cmdDescription.WriteString("Create a local QEMU based Talos cluster.\n\n") cmdDescription.WriteString("Available presets:\n") for _, p := range preset.Presets { cmdDescription.WriteString(" - " + p.Name() + ": " + p.Description() + "\n") } cmdDescription.WriteString("\n") cmdDescription.WriteString("Note: exactly one of 'iso', 'iso-secureboot', 'pxe' or 'disk-image' presets must be specified.\n") createQemuCmd := &cobra.Command{ Use: providers.QemuProviderName, Short: "Create a local QEMU based Talos cluster.", Long: cmdDescription.String(), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), func(ctx context.Context) error { provisioner, err := providers.Factory(ctx, providers.QemuProviderName) if err != nil { return err } return createQemuCluster(ctx, qOps, cOps, presetOptions, provisioner) }) }, } createQemuCmd.Flags().AddFlagSet(commonFlags) createQemuCmd.Flags().AddFlagSet(getQemuFlags()) addOmniJoinTokenFlag(createQemuCmd, &cOps.OmniAPIEndpoint, configPatchFlagName, configPatchWorkerFlagName, configPatchControlPlaneFlagName) createCmd.AddCommand(createQemuCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/create.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "fmt" "net/url" "os" "path/filepath" "slices" "strings" "time" "github.com/hashicorp/go-getter/v2" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/pkg/cluster/check" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/access" ) // downloadBootAssets downloads the boot assets in the given qemuOps if they are URLs, and replaces their URL paths with the downloaded paths on the filesystem. // // As it modifies the qemuOps struct, it needs to be passed by reference. // //nolint:gocyclo func downloadBootAssets(ctx context.Context, qOps *clusterops.Qemu) error { // download & cache images if provides as URLs for _, downloadableImage := range []struct { path *string disableArchive bool }{ { path: &qOps.NodeVmlinuzPath, }, { path: &qOps.NodeInitramfsPath, disableArchive: true, }, { path: &qOps.NodeISOPath, }, { path: &qOps.NodeUSBPath, }, { path: &qOps.NodeUKIPath, }, { path: &qOps.NodeDiskImagePath, // we disable extracting the compressed image since we handle zstd for disk images disableArchive: true, }, } { if *downloadableImage.path == "" { continue } u, err := url.Parse(*downloadableImage.path) if err != nil || !(u.Scheme == "http" || u.Scheme == "https") { // not a URL continue } defaultStateDir, err := clientconfig.GetTalosDirectory() if err != nil { return err } cacheDir := filepath.Join(defaultStateDir, "cache") if err = os.MkdirAll(cacheDir, 0o755); err != nil { return err } destPath := strings.ReplaceAll( strings.ReplaceAll(u.String(), "/", "-"), ":", "-") _, err = os.Stat(filepath.Join(cacheDir, destPath)) if err == nil { *downloadableImage.path = filepath.Join(cacheDir, destPath) // already cached continue } fmt.Fprintf(os.Stderr, "downloading asset from %q to %q\n", u.String(), filepath.Join(cacheDir, destPath)) client := getter.Client{ Getters: []getter.Getter{ &getter.HttpGetter{ HeadFirstTimeout: 30 * time.Minute, ReadTimeout: 30 * time.Minute, }, }, } if downloadableImage.disableArchive { q := u.Query() q.Set("archive", "false") u.RawQuery = q.Encode() } _, err = client.Get(ctx, &getter.Request{ Src: u.String(), Dst: filepath.Join(cacheDir, destPath), GetMode: getter.ModeFile, }) if err != nil { // clean up the destination on failure os.Remove(filepath.Join(cacheDir, destPath)) //nolint:errcheck return err } *downloadableImage.path = filepath.Join(cacheDir, destPath) } return nil } func postCreate( ctx context.Context, cOps clusterops.Common, cluster provision.Cluster, clusterConfigs clusterops.ClusterConfigs, ) error { if clusterConfigs.ConfigBundle != nil { bundleTalosconfig := clusterConfigs.ConfigBundle.TalosConfig() if err := saveConfig(bundleTalosconfig, cOps.TalosconfigDestination); err != nil { return err } } clusterAccess := access.NewAdapter(cluster, clusterConfigs.ProvisionOptions...) defer clusterAccess.Close() //nolint:errcheck if cOps.ApplyConfigEnabled { fmt.Println("applying configuration to the cluster nodes") err := clusterAccess.ApplyConfig(ctx, clusterConfigs.ClusterRequest.Nodes, clusterConfigs.ClusterRequest.SiderolinkRequest, os.Stdout) if err != nil { return err } } if cOps.OmniAPIEndpoint != "" || (cOps.SkipInjectingConfig && !cOps.ApplyConfigEnabled) { return nil } return bootstrapCluster(ctx, clusterAccess, cOps) } func bootstrapCluster(ctx context.Context, clusterAccess *access.Adapter, cOps clusterops.Common) error { if !cOps.WithInitNode { if err := clusterAccess.Bootstrap(ctx, os.Stdout); err != nil { return fmt.Errorf("bootstrap error: %w", err) } } if !cOps.ClusterWait { return nil } // Run cluster readiness checks checkCtx, checkCtxCancel := context.WithTimeout(ctx, cOps.ClusterWaitTimeout) defer checkCtxCancel() checks := check.DefaultClusterChecks() if cOps.SkipK8sNodeReadinessCheck { checks = slices.Concat(check.PreBootSequenceChecks(), check.K8sComponentsReadinessChecks()) } checks = slices.Concat(checks, check.ExtraClusterChecks()) if err := check.Wait(checkCtx, clusterAccess, checks, check.StderrReporter()); err != nil { return err } if cOps.SkipKubeconfig { return nil } return mergeKubeconfig(ctx, clusterAccess) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/create_dev.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "strings" "github.com/siderolabs/go-kubeconfig" "k8s.io/client-go/tools/clientcmd" clustercmd "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/provision/access" "github.com/siderolabs/talos/pkg/provision/providers" ) //nolint:gocyclo,cyclop func createDevCluster(ctx context.Context, cOps clusterops.Common, qOps clusterops.Qemu) error { if err := downloadBootAssets(ctx, &qOps); err != nil { return err } if cOps.TalosVersion == "" { parts := strings.Split(qOps.NodeInstallImage, ":") cOps.TalosVersion = parts[len(parts)-1] } provisioner, err := providers.Factory(ctx, providers.QemuProviderName) if err != nil { return err } clusterConfigs, err := configmaker.GetQemuConfigs(configmaker.QemuOptions{ ExtraOps: qOps, CommonOps: cOps, Provisioner: provisioner, }) if err != nil { return err } err = preCreate(cOps, clusterConfigs) if err != nil { return err } cluster, err := provisioner.Create(ctx, clusterConfigs.ClusterRequest, clusterConfigs.ProvisionOptions...) if err != nil { return err } if qOps.DebugShellEnabled { fmt.Println("You can now connect to debug shell on any node using these commands:") for _, node := range clusterConfigs.ClusterRequest.Nodes { talosDir, err := clientconfig.GetTalosDirectory() if err != nil { return err } fmt.Printf("socat - UNIX-CONNECT:%s\n", filepath.Join(talosDir, "clusters", cOps.RootOps.ClusterName, node.Name+".serial")) } return nil } // Create and save the talosctl configuration file. err = postCreate(ctx, cOps, cluster, clusterConfigs) if err != nil { return err } return clustercmd.ShowCluster(cluster) } func saveConfig(talosConfigObj *clientconfig.Config, talosconfigPath string) (err error) { c, err := clientconfig.Open(talosconfigPath) if err != nil { return fmt.Errorf("error opening talos config: %w", err) } renames := c.Merge(talosConfigObj) for _, rename := range renames { fmt.Fprintf(os.Stderr, "renamed talosconfig context %s\n", rename.String()) } return c.Save(talosconfigPath) } func mergeKubeconfig(ctx context.Context, clusterAccess *access.Adapter) error { kubeconfigPath, err := kubeconfig.SinglePath() if err != nil { return err } fmt.Fprintf(os.Stderr, "\nmerging kubeconfig into %q\n", kubeconfigPath) k8sconfig, err := clusterAccess.Kubeconfig(ctx) if err != nil { return fmt.Errorf("error fetching kubeconfig: %w", err) } kubeConfig, err := clientcmd.Load(k8sconfig) if err != nil { return fmt.Errorf("error parsing kubeconfig: %w", err) } if clusterAccess.ForceEndpoint != "" { for name := range kubeConfig.Clusters { kubeConfig.Clusters[name].Server = clusterAccess.ForceEndpoint } } _, err = os.Stat(kubeconfigPath) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return err } return clientcmd.WriteToFile(*kubeConfig, kubeconfigPath) } merger, err := kubeconfig.Load(kubeconfigPath) if err != nil { return fmt.Errorf("error loading existing kubeconfig: %w", err) } err = merger.Merge(kubeConfig, kubeconfig.MergeOptions{ ActivateContext: true, OutputWriter: os.Stdout, ConflictHandler: func(component kubeconfig.ConfigComponent, name string) (kubeconfig.ConflictDecision, error) { return kubeconfig.RenameDecision, nil }, }) if err != nil { return fmt.Errorf("error merging kubeconfig: %w", err) } return merger.Write(kubeconfigPath) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/create_docker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "fmt" "strings" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/provision" ) //nolint:gocyclo,cyclop func getDockerClusterRequest(cOps clusterops.Common, dOps clusterops.Docker, provisioner provision.Provisioner) (clusterops.ClusterConfigs, error) { parts := strings.Split(dOps.TalosImage, ":") cOps.TalosVersion = parts[len(parts)-1] _, err := config.ParseContractFromVersion(cOps.TalosVersion) if err != nil { currentVersion := helpers.GetTag() fmt.Printf("failed to derrive Talos version from the docker image, defaulting to %s\n", currentVersion) cOps.TalosVersion = currentVersion } return configmaker.GetDockerConfigs(configmaker.DockerOptions{ ExtraOps: dOps, CommonOps: cOps, Provisioner: provisioner, }) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/create_qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package create import ( "context" "fmt" "net/url" "os" "path/filepath" "slices" "github.com/siderolabs/talos/cmd/talosctl/cmd/constants" clustercmd "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/clusterops/configmaker/preset" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/provision" ) //nolint:gocyclo,cyclop func createQemuCluster( ctx context.Context, qOps clusterops.Qemu, cOps clusterops.Common, presetOptions presetOptions, provisioner provision.Provisioner, ) error { if cOps.TalosVersion == "" || cOps.TalosVersion[0] != 'v' { return fmt.Errorf("failed to parse talos version: version string must start with a 'v'") } _, err := config.ParseContractFromVersion(cOps.TalosVersion) if err != nil { return fmt.Errorf("failed to parse talos version: %s", err) } if presetOptions.schematicID == "" { presetOptions.schematicID = constants.ImageFactoryEmptySchematicID } factoryURL, err := url.Parse(presetOptions.imageFactoryURL) if err != nil { return fmt.Errorf("malformed Image Factory URL: %q: %w", presetOptions.imageFactoryURL, err) } if factoryURL.Scheme == "" || factoryURL.Host == "" { return fmt.Errorf("image Factory URL must include scheme and host: %q", presetOptions.imageFactoryURL) } if slices.Contains(presetOptions.presets, preset.Maintenance{}.Name()) && cOps.OmniAPIEndpoint != "" { fmt.Println("omni-api-endpoint specified along with the 'maintenance' preset") fmt.Println("machine configuration containing 'SideroLinkConfig' will be written to the working path but will not be applied to the nodes") } err = preset.Apply( preset.Options{ SchematicID: presetOptions.schematicID, ImageFactoryURL: factoryURL, }, &cOps, &qOps, presetOptions.presets) if err != nil { return err } if err := downloadBootAssets(ctx, &qOps); err != nil { return err } clusterConfigs, err := configmaker.GetQemuConfigs(configmaker.QemuOptions{ ExtraOps: qOps, CommonOps: cOps, Provisioner: provisioner, }) if err != nil { return err } err = preCreate(cOps, clusterConfigs) if err != nil { return err } cluster, err := provisioner.Create(ctx, clusterConfigs.ClusterRequest, clusterConfigs.ProvisionOptions...) if err != nil { return err } err = postCreate(ctx, cOps, cluster, clusterConfigs) if err != nil { return err } return clustercmd.ShowCluster(cluster) } func preCreate(cOps clusterops.Common, clusterConfigs clusterops.ClusterConfigs) error { // write machine config if cOps.SkipInjectingConfig { if err := writeMachineconfig(clusterConfigs, cOps); err != nil { return err } } return nil } func writeMachineconfig(clusterConfigs clusterops.ClusterConfigs, cOps clusterops.Common) error { if clusterConfigs.ConfigBundle != nil { types := []machine.Type{machine.TypeControlPlane, machine.TypeWorker} if cOps.WithInitNode { types = slices.Insert(types, 0, machine.TypeInit) } return clusterConfigs.ConfigBundle.Write(".", encoder.CommentsAll, types...) } // no configbundle, just write the machine config as-is cfgBytes, err := clusterConfigs.ClusterRequest.Nodes[0].Config.Bytes() if err != nil { return err } fullFilePath := filepath.Join(".", "machineconfig.yaml") if err = os.WriteFile(fullFilePath, cfgBytes, 0o644); err != nil { return err } fmt.Fprintf(os.Stderr, "created %s\n", fullFilePath) return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/flags/agent.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package flags import "fmt" // Agent represents the type of the agent to use for cluster management. type Agent uint8 func (a *Agent) String() string { switch *a { case 1: return "wireguard" case 2: return "grpc-tunnel" case 3: return "wireguard+tls" case 4: return "grpc-tunnel+tls" default: return "none" } } // Set implements pflag.Value interface. func (a *Agent) Set(s string) error { switch s { case "true", "wireguard": *a = 1 case "tunnel": *a = 2 case "wireguard+tls": *a = 3 case "grpc-tunnel+tls": *a = 4 default: return fmt.Errorf("unknown type: %s, possible values: 'true', 'wireguard' for the usual WG; 'tunnel' for WG over GRPC, add '+tls' to enable TLS for API", s) } return nil } // Type implements pflag.Value interface. func (a *Agent) Type() string { return "agent" } // IsEnabled returns true if the agent is enabled. func (a *Agent) IsEnabled() bool { return *a != 0 } // IsTunnel returns true if the agent is a tunnel. func (a *Agent) IsTunnel() bool { return *a == 2 || *a == 4 } // IsTLS returns true if the agent is using TLS. func (a *Agent) IsTLS() bool { return *a == 3 || *a == 4 } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/flags/disks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package flags import ( "errors" "fmt" "slices" "strings" "github.com/siderolabs/talos/pkg/bytesize" ) // DiskRequest is the configuration required for disk creation. type DiskRequest struct { Driver string Size bytesize.ByteSize Tag string Serial string } // Drivers that support tag option. var tagDrivers = []string{"virtiofs"} // Drivers that support serial option. var serialDrivers = []string{"virtio"} // ParseDisksFlag parses the disks flag into a slice of DiskRequests. func ParseDisksFlag(disks []string) ([]DiskRequest, error) { result := []DiskRequest{} if len(disks) == 0 { return nil, errors.New("at least one disk has to be specified") } for _, d := range disks { parts := strings.Split(d, ":") if len(parts) < 2 { return nil, fmt.Errorf("invalid disk format: %q", d) } size := bytesize.WithDefaultUnit("MiB") err := size.Set(parts[1]) if err != nil { return nil, fmt.Errorf("invalid size in disk spec: %q", d) } req := DiskRequest{ Driver: parts[0], Size: *size, } if len(parts) > 2 { for _, part := range parts[2:] { if err := parseKVParams(&req, part); err != nil { return nil, fmt.Errorf("%w: %q", err, d) } } } result = append(result, req) } return result, nil } func parseKVParams(req *DiskRequest, part string) error { kv := strings.SplitN(part, "=", 2) if len(kv) != 2 { return fmt.Errorf("invalid disk option in spec: %q", part) } switch kv[0] { case "tag": if !slices.Contains(tagDrivers, req.Driver) { return fmt.Errorf("tag option is only supported for %v drivers in spec", tagDrivers) } req.Tag = kv[1] case "serial": if !slices.Contains(serialDrivers, req.Driver) { return fmt.Errorf("serial option is only supported for %v drivers in spec", serialDrivers) } req.Serial = kv[1] default: return fmt.Errorf("unknown disk option %q in spec", kv[0]) } return nil } // Disks implements pflag.Value for accumulating multiple DiskRequest entries. // It accepts repeated uses of the flag (e.g., --disks a:1GiB --disks b:10GiB) // and comma-separated lists (e.g., --disks a:1GiB,b:10GiB). type Disks struct { requests []DiskRequest } // String returns a string representation suitable for flag printing. func (f *Disks) String() string { if f == nil || len(f.requests) == 0 { return "" } parts := make([]string, 0, len(f.requests)) for _, r := range f.requests { sb := strings.Builder{} sb.WriteString(r.Driver) sb.WriteString(":") sb.WriteString(r.Size.String()) if r.Tag != "" { sb.WriteString(":tag=") sb.WriteString(r.Tag) } if r.Serial != "" { sb.WriteString(":serial=") sb.WriteString(r.Serial) } parts = append(parts, sb.String()) } return strings.Join(parts, ",") } // Set parses and appends one or more disk specifications to the flag value. // The input may contain a single spec ("driver:size") or a comma-separated list. func (f *Disks) Set(value string) error { if strings.TrimSpace(value) == "" { return errors.New("disk value must not be empty") } // Support comma-separated values in a single Set call. raw := strings.Split(value, ",") reqs, err := ParseDisksFlag(raw) if err != nil { return err } f.requests = reqs return nil } // Type returns the flag's value type name. func (f *Disks) Type() string { return "disks" } // Requests returns a defensive copy of the accumulated disk requests. func (f *Disks) Requests() []DiskRequest { out := make([]DiskRequest, len(f.requests)) copy(out, f.requests) return out } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/flags/disks_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package flags_test import ( "testing" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" flags "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/pkg/bytesize" ) func TestDisksFlag_ExtraOpts(t *testing.T) { t.Parallel() var d flags.Disks fs := pflag.NewFlagSet("test", pflag.ContinueOnError) fs.Var(&d, "disks", "") args := []string{ "--disks", "virtio:1GiB:serial=test-1,virtiofs:1GiB:tag=foo,virtiofs:1GiB:tag=bar", } err := fs.Parse(args) assert.NoError(t, err) reqs := d.Requests() assert.Len(t, reqs, 3) toBytes := func(s string) uint64 { bs := bytesize.WithDefaultUnit("MiB") assert.NoError(t, bs.Set(s)) return bs.Bytes() } assert.Equal(t, "virtio", reqs[0].Driver) assert.Equal(t, toBytes("1GiB"), reqs[0].Size.Bytes()) assert.Equal(t, "test-1", reqs[0].Serial) assert.Equal(t, "", reqs[0].Tag) assert.Equal(t, "virtiofs", reqs[1].Driver) assert.Equal(t, toBytes("1GiB"), reqs[1].Size.Bytes()) assert.Equal(t, "", reqs[1].Serial) assert.Equal(t, "foo", reqs[1].Tag) assert.Equal(t, "virtiofs", reqs[2].Driver) assert.Equal(t, toBytes("1GiB"), reqs[2].Size.Bytes()) assert.Equal(t, "", reqs[2].Serial) assert.Equal(t, "bar", reqs[2].Tag) // Type should be stable assert.Equal(t, "disks", d.Type()) assert.Equal(t, "virtio:1GiB:serial=test-1,virtiofs:1GiB:tag=foo,virtiofs:1GiB:tag=bar", d.String()) } func TestDisksFlag_AccumulatesAndRequests(t *testing.T) { t.Parallel() var d flags.Disks fs := pflag.NewFlagSet("test", pflag.ContinueOnError) fs.Var(&d, "disks", "") args := []string{ "--disks", "virtio:1GiB,nvme:10GiB,sata:512MiB", } err := fs.Parse(args) assert.NoError(t, err) reqs := d.Requests() assert.Len(t, reqs, 3) toBytes := func(s string) uint64 { bs := bytesize.WithDefaultUnit("MiB") assert.NoError(t, bs.Set(s)) return bs.Bytes() } assert.Equal(t, "virtio", reqs[0].Driver) assert.Equal(t, toBytes("1GiB"), reqs[0].Size.Bytes()) assert.Equal(t, "nvme", reqs[1].Driver) assert.Equal(t, toBytes("10GiB"), reqs[1].Size.Bytes()) assert.Equal(t, "sata", reqs[2].Driver) assert.Equal(t, toBytes("512MiB"), reqs[2].Size.Bytes()) // Type should be stable assert.Equal(t, "disks", d.Type()) assert.Equal(t, "virtio:1GiB,nvme:10GiB,sata:512MiB", d.String()) } func TestDisksFlag_Set(t *testing.T) { t.Parallel() var d flags.Disks err := d.Set("virtio:6gb") assert.NoError(t, err) assert.Equal(t, "virtio:6gb", d.String()) err = d.Set("nvme:12mb,sata:2gb") assert.NoError(t, err) assert.Equal(t, "nvme:12mb,sata:2gb", d.String()) err = d.Set("invalid-no-colon") assert.Error(t, err) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/flags/virtiofs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package flags import ( "errors" "fmt" "strings" ) // VirtiofsRequest is the configuration required for virtiofs share creation. type VirtiofsRequest struct { SharedDir string SocketPath string } // ParseVirtiofsFlag parses the virtiofs flag into a slice of VirtiofsRequest. func ParseVirtiofsFlag(disks []string) ([]VirtiofsRequest, error) { result := []VirtiofsRequest{} if len(disks) == 0 { return nil, errors.New("at least one disk has to be specified") } for _, d := range disks { parts := strings.SplitN(d, ":", 2) if len(parts) != 2 { return nil, fmt.Errorf("invalid disk format: %q", d) } result = append(result, VirtiofsRequest{ SharedDir: parts[0], SocketPath: parts[1], }) } return result, nil } // Virtiofs implements pflag.Value for accumulating multiple VirtiofsRequest entries. type Virtiofs struct { requests []VirtiofsRequest } // String returns a string representation suitable for flag printing. func (f *Virtiofs) String() string { if f == nil || len(f.requests) == 0 { return "" } parts := make([]string, 0, len(f.requests)) for _, r := range f.requests { parts = append(parts, fmt.Sprintf("%s:%s", r.SharedDir, r.SocketPath)) } return strings.Join(parts, ",") } // Set parses and appends one or more disk specifications to the flag value. // The input may contain a single spec ("sharedDir:socketPath") or a comma-separated list. func (f *Virtiofs) Set(value string) error { if strings.TrimSpace(value) == "" { return errors.New("virtiofs value must not be empty") } // Support comma-separated values in a single Set call. raw := strings.Split(value, ",") reqs, err := ParseVirtiofsFlag(raw) if err != nil { return err } f.requests = reqs return nil } // Type returns the flag's value type name. func (f *Virtiofs) Type() string { return "virtiofs" } // Requests returns a defensive copy of the accumulated virtiofs share requests. func (f *Virtiofs) Requests() []VirtiofsRequest { out := make([]VirtiofsRequest, len(f.requests)) copy(out, f.requests) return out } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/create/flags/virtiofs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package flags_test import ( "testing" "github.com/spf13/pflag" "github.com/stretchr/testify/assert" flags "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" ) func TestVirtiofsFlag_AccumulatesAndRequests(t *testing.T) { t.Parallel() var d flags.Virtiofs fs := pflag.NewFlagSet("test", pflag.ContinueOnError) fs.Var(&d, "virtiofs", "") args := []string{ "--virtiofs", "/mnt/shared/1:/tmp/mnt-shared-1.sock,/mnt/shared/2:/tmp/mnt-shared-2.sock,/mnt/shared/3:/tmp/mnt-shared-3.sock", } err := fs.Parse(args) assert.NoError(t, err) reqs := d.Requests() assert.Len(t, reqs, 3) assert.Equal(t, "/mnt/shared/1", reqs[0].SharedDir) assert.Equal(t, "/tmp/mnt-shared-1.sock", reqs[0].SocketPath) assert.Equal(t, "/mnt/shared/2", reqs[1].SharedDir) assert.Equal(t, "/tmp/mnt-shared-2.sock", reqs[1].SocketPath) assert.Equal(t, "/mnt/shared/3", reqs[2].SharedDir) assert.Equal(t, "/tmp/mnt-shared-3.sock", reqs[2].SocketPath) // Type should be stable assert.Equal(t, "virtiofs", d.Type()) assert.Equal(t, "/mnt/shared/1:/tmp/mnt-shared-1.sock,/mnt/shared/2:/tmp/mnt-shared-2.sock,/mnt/shared/3:/tmp/mnt-shared-3.sock", d.String()) } func TestVirtiofsFlag_SetInvalid(t *testing.T) { t.Parallel() var f flags.Virtiofs err := f.Set("/mnt/shared/1:/tmp/mnt-shared-1.sock") assert.NoError(t, err) assert.Equal(t, "/mnt/shared/1:/tmp/mnt-shared-1.sock", f.String()) err = f.Set("/mnt/shared/1:/tmp/mnt-shared-1.sock,/mnt/shared/2:/tmp/mnt-shared-2.sock") assert.NoError(t, err) assert.Equal(t, "/mnt/shared/1:/tmp/mnt-shared-1.sock,/mnt/shared/2:/tmp/mnt-shared-2.sock", f.String()) err = f.Set("invalid-no-colon") assert.Error(t, err) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/destroy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers" ) var destroyCmdFlags struct { forceDelete bool saveSupportArchivePath string saveClusterLogsArchivePath string } // destroyCmd represents the cluster destroy command. var destroyCmd = &cobra.Command{ Use: "destroy", Short: "Destroys a local Talos kubernetes cluster", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), destroy) }, } func destroy(ctx context.Context) error { state, err := provision.ReadState(ctx, PersistentFlags.ClusterName, PersistentFlags.StateDir) if err != nil { return fmt.Errorf("failed to read cluster state: %w", err) } provisioner, err := providers.Factory(ctx, state.ProvisionerName) if err != nil { return err } defer provisioner.Close() //nolint:errcheck cluster, err := provisioner.Reflect(ctx, PersistentFlags.ClusterName, PersistentFlags.StateDir) if err != nil { return err } return provisioner.Destroy( ctx, cluster, provision.WithDeleteOnErr(destroyCmdFlags.forceDelete), provision.WithSaveSupportArchivePath(destroyCmdFlags.saveSupportArchivePath), provision.WithSaveClusterLogsArchivePath(destroyCmdFlags.saveClusterLogsArchivePath), ) } func init() { destroyCmd.PersistentFlags().BoolVarP(&destroyCmdFlags.forceDelete, "force", "f", false, "force deletion of cluster directory if there were errors") destroyCmd.PersistentFlags().StringVarP(&destroyCmdFlags.saveSupportArchivePath, "save-support-archive-path", "", "", "save support archive to the specified file on destroy") destroyCmd.PersistentFlags().StringVarP(&destroyCmdFlags.saveClusterLogsArchivePath, "save-cluster-logs-archive-path", "", "", "save cluster logs archive to the specified file on destroy") AddProvisionerFlag(destroyCmd) cli.Should(destroyCmd.Flags().MarkDeprecated(ProvisionerFlagName, "the provisioner is inferred automatically")) Cmd.AddCommand(destroyCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/internal/firewallpatch/firewallpatch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package firewallpatch provides a set of default config patches to enable firewall. package firewallpatch import ( "net/netip" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) func ingressRuleWithinCluster(cidrs []netip.Prefix, gateways []netip.Addr) []network.IngressRule { rules := make([]network.IngressRule, 0, len(cidrs)) for i := range cidrs { rules = append(rules, network.IngressRule{ Subnet: cidrs[i], Except: network.Prefix{Prefix: netip.PrefixFrom(gateways[i], gateways[i].BitLen())}, }, ) } return rules } func ingressRuleWideOpen() []network.IngressRule { return []network.IngressRule{ { Subnet: netip.MustParsePrefix("0.0.0.0/0"), }, { Subnet: netip.MustParsePrefix("::/0"), }, } } func ingressOnly(ips []netip.Addr) []network.IngressRule { return xslices.Map(ips, func(ip netip.Addr) network.IngressRule { return network.IngressRule{ Subnet: netip.PrefixFrom(ip, ip.BitLen()), } }) } // ControlPlane generates a default firewall for a controlplane node. // // Kubelet and Trustd are only available within the cluster. // Apid & Kubernetes API is wide open. // Etcd is only available within the controlplanes. func ControlPlane(defaultAction nethelpers.DefaultAction, cidrs []netip.Prefix, gateways []netip.Addr, controlplanes []netip.Addr) configpatcher.Patch { def := network.NewDefaultActionConfigV1Alpha1() def.Ingress = defaultAction kubeletRule := network.NewRuleConfigV1Alpha1() kubeletRule.MetaName = "kubelet-ingress" kubeletRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.KubeletPort, Hi: constants.KubeletPort, }, } kubeletRule.PortSelector.Protocol = nethelpers.ProtocolTCP kubeletRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) apidRule := network.NewRuleConfigV1Alpha1() apidRule.MetaName = "apid-ingress" apidRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.ApidPort, Hi: constants.ApidPort, }, } apidRule.PortSelector.Protocol = nethelpers.ProtocolTCP apidRule.Ingress = ingressRuleWideOpen() trustdRule := network.NewRuleConfigV1Alpha1() trustdRule.MetaName = "trustd-ingress" trustdRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.TrustdPort, Hi: constants.TrustdPort, }, } trustdRule.PortSelector.Protocol = nethelpers.ProtocolTCP trustdRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) kubeAPIRule := network.NewRuleConfigV1Alpha1() kubeAPIRule.MetaName = "kubernetes-api-ingress" kubeAPIRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.DefaultControlPlanePort, Hi: constants.DefaultControlPlanePort, }, } kubeAPIRule.PortSelector.Protocol = nethelpers.ProtocolTCP kubeAPIRule.Ingress = ingressRuleWideOpen() etcdRule := network.NewRuleConfigV1Alpha1() etcdRule.MetaName = "etcd-ingress" etcdRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.EtcdClientPort, Hi: constants.EtcdPeerPort, }, } etcdRule.PortSelector.Protocol = nethelpers.ProtocolTCP etcdRule.Ingress = ingressOnly(controlplanes) vxlanRule := network.NewRuleConfigV1Alpha1() vxlanRule.MetaName = "cni-vxlan" vxlanRule.PortSelector.Ports = []network.PortRange{ { Lo: 4789, // Flannel, Calico VXLAN Hi: 4789, }, { Lo: 8472, // Cilium VXLAN Hi: 8472, }, } vxlanRule.PortSelector.Protocol = nethelpers.ProtocolUDP vxlanRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) provider, err := container.New(def, kubeletRule, apidRule, trustdRule, kubeAPIRule, etcdRule, vxlanRule) if err != nil { // should not fail panic(err) } return configpatcher.NewStrategicMergePatch(provider) } // Worker generates a default firewall for a worker node. // // Kubelet & apid are only available within the cluster. func Worker(defaultAction nethelpers.DefaultAction, cidrs []netip.Prefix, gateways []netip.Addr) configpatcher.Patch { def := network.NewDefaultActionConfigV1Alpha1() def.Ingress = defaultAction kubeletRule := network.NewRuleConfigV1Alpha1() kubeletRule.MetaName = "kubelet-ingress" kubeletRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.KubeletPort, Hi: constants.KubeletPort, }, } kubeletRule.PortSelector.Protocol = nethelpers.ProtocolTCP kubeletRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) apidRule := network.NewRuleConfigV1Alpha1() apidRule.MetaName = "apid-ingress" apidRule.PortSelector.Ports = []network.PortRange{ { Lo: constants.ApidPort, Hi: constants.ApidPort, }, } apidRule.PortSelector.Protocol = nethelpers.ProtocolTCP apidRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) vxlanRule := network.NewRuleConfigV1Alpha1() vxlanRule.MetaName = "cni-vxlan" vxlanRule.PortSelector.Ports = []network.PortRange{ { Lo: 4789, // Flannel, Calico VXLAN Hi: 4789, }, { Lo: 8472, // Cilium VXLAN Hi: 8472, }, } vxlanRule.PortSelector.Protocol = nethelpers.ProtocolUDP vxlanRule.Ingress = ingressRuleWithinCluster(cidrs, gateways) provider, err := container.New(def, kubeletRule, apidRule, vxlanRule) if err != nil { // should not fail panic(err) } return configpatcher.NewStrategicMergePatch(provider) } ================================================ FILE: cmd/talosctl/cmd/mgmt/cluster/show.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "cmp" "context" "fmt" "net/netip" "os" "slices" "strings" "text/tabwriter" "github.com/dustin/go-humanize" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers" ) // showCmd represents the cluster show command. var showCmd = &cobra.Command{ Use: "show", Short: "Shows info about a local provisioned kubernetes cluster", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext(context.Background(), show) }, } func show(ctx context.Context) error { provisioner, err := providers.Factory(ctx, provisionerName) if err != nil { return err } defer provisioner.Close() //nolint:errcheck cluster, err := provisioner.Reflect(ctx, PersistentFlags.ClusterName, PersistentFlags.StateDir) if err != nil { return err } return ShowCluster(cluster) } // ShowCluster prints the details about the cluster to the terminal. func ShowCluster(cluster provision.Cluster) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintf(w, "PROVISIONER\t%s\n", cluster.Provisioner()) fmt.Fprintf(w, "NAME\t%s\n", cluster.Info().ClusterName) fmt.Fprintf(w, "NETWORK NAME\t%s\n", cluster.Info().Network.Name) cidrs := xslices.Map(cluster.Info().Network.CIDRs, netip.Prefix.String) fmt.Fprintf(w, "NETWORK CIDR\t%s\n", strings.Join(cidrs, ",")) gateways := xslices.Map(cluster.Info().Network.GatewayAddrs, netip.Addr.String) fmt.Fprintf(w, "NETWORK GATEWAY\t%s\n", strings.Join(gateways, ",")) fmt.Fprintf(w, "NETWORK MTU\t%d\n", cluster.Info().Network.MTU) fmt.Fprintf(w, "KUBERNETES ENDPOINT\t%s\n", cluster.Info().KubernetesEndpoint) if err := w.Flush(); err != nil { return err } fmt.Fprint(os.Stdout, "\nNODES:\n\n") w = tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintf(w, "NAME\tTYPE\tIP\tCPU\tRAM\tDISK\n") nodes := cluster.Info().Nodes slices.SortFunc(nodes, func(a, b provision.NodeInfo) int { return cmp.Compare(a.Name, b.Name) }) for _, node := range nodes { cpus := "-" if node.NanoCPUs > 0 { cpus = fmt.Sprintf("%.2f", float64(node.NanoCPUs)/1000.0/1000.0/1000.0) } mem := "-" if node.Memory > 0 { mem = humanize.Bytes(uint64(node.Memory)) } disk := "-" if node.DiskSize > 0 { disk = humanize.Bytes(node.DiskSize) } ips := xslices.Map(node.IPs, netip.Addr.String) fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", node.Name, node.Type, strings.Join(ips, ","), cpus, mem, disk, ) } return w.Flush() } func init() { AddProvisionerFlag(showCmd) Cmd.AddCommand(showCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/debug/air-gapped.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package debug import ( "context" "crypto/tls" "embed" "fmt" "io" "io/fs" "log" "net" "net/http" "net/http/httputil" "net/url" "os" "strconv" "time" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) //go:embed httproot/* var httpFs embed.FS var airgappedFlags struct { advertisedAddress net.IP proxyPort int httpsProxyPort int httpsPort int useSecureProxy bool injectHTTPProxy bool httpsReverseProxyPort int httpsReverseProxyTarget string } // airgappedCmd represents the `gen ca` command. var airgappedCmd = &cobra.Command{ Use: "air-gapped", Short: "Starts a local HTTP proxy and HTTPS server serving a test manifest.", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return cli.WithContext( context.Background(), func(ctx context.Context) error { caPEM, certPEM, keyPEM, err := helpers.GenerateSelfSignedCert([]net.IP{airgappedFlags.advertisedAddress}, nil) if err != nil { return nil } if err = generateConfigPatch(caPEM); err != nil { return err } eg, ctx := errgroup.WithContext(ctx) eg.Go(func() error { return runHTTPServer(ctx, certPEM, keyPEM) }) eg.Go(func() error { return runHTTPProxy(ctx) }) eg.Go(func() error { return runHTTPSProxy(ctx, certPEM, keyPEM) }) eg.Go(func() error { return runHTTPSReverseProxy(ctx, certPEM, keyPEM) }) return eg.Wait() }, ) }, } func generateConfigPatch(caPEM []byte) error { patch1 := &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ExtraManifests: []string{ fmt.Sprintf("https://%s/debug.yaml", net.JoinHostPort(airgappedFlags.advertisedAddress.String(), strconv.Itoa(airgappedFlags.httpsPort))), }, }, } patch2 := runtime.NewEnvironmentV1Alpha1() if airgappedFlags.injectHTTPProxy { proxyURL := fmt.Sprintf("http://%s", net.JoinHostPort(airgappedFlags.advertisedAddress.String(), strconv.Itoa(airgappedFlags.proxyPort))) if airgappedFlags.useSecureProxy { proxyURL = fmt.Sprintf("https://%s", net.JoinHostPort(airgappedFlags.advertisedAddress.String(), strconv.Itoa(airgappedFlags.httpsProxyPort))) } patch2.EnvironmentVariables = map[string]string{ "http_proxy": proxyURL, "https_proxy": proxyURL, "no_proxy": fmt.Sprintf("%s/24", airgappedFlags.advertisedAddress.String()), } } patch3 := security.NewTrustedRootsConfigV1Alpha1() patch3.MetaName = "air-gapped-ca" patch3.Certificates = string(caPEM) ctr, err := container.New(patch1, patch2, patch3) if err != nil { return err } patchBytes, err := ctr.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return err } const patchFile = "air-gapped-patch.yaml" log.Printf("writing config patch to %s", patchFile) return os.WriteFile(patchFile, patchBytes, 0o644) } func runHTTPServer(ctx context.Context, certPEM, keyPEM []byte) error { certificate, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{certificate}, } subFs, err := fs.Sub(httpFs, "httproot") if err != nil { return err } srv := &http.Server{ Addr: net.JoinHostPort("", strconv.Itoa(airgappedFlags.httpsPort)), Handler: loggingMiddleware(http.FileServer(http.FS(subFs))), TLSConfig: tlsConfig, } log.Printf("starting HTTPS server with self-signed cert on %s", srv.Addr) go srv.ListenAndServeTLS("", "") //nolint:errcheck <-ctx.Done() return srv.Close() } func handleTunneling(ctx context.Context, w http.ResponseWriter, r *http.Request) { addr := r.URL.Host dstConn, err := (&net.Dialer{Timeout: 10 * time.Second}).DialContext(ctx, "tcp", addr) if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } dst := dstConn.(*net.TCPConn) hijacker, ok := w.(http.Hijacker) if !ok { http.Error(w, "Hijacking not supported", http.StatusInternalServerError) return } clientConn, _, err := hijacker.Hijack() if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } var src conn if src, ok = clientConn.(conn); !ok { if tlsConn, ok := clientConn.(*tls.Conn); ok { src = &tlsConnWrapper{ Conn: tlsConn, closeReadWrite: tlsConn.NetConn().(*net.TCPConn), } } else { log.Printf("HTTP CONNECT: tunneling to %s: failed: connection is not a net.Conn: %T", addr, clientConn) http.Error(w, "Connection is not a net.Conn", http.StatusInternalServerError) return } } src.Write([]byte("HTTP/1.0 200 Connection established\r\n\r\n")) //nolint:errcheck log.Printf("HTTP CONNECT: tunneling to %s", addr) defer dst.Close() //nolint:errcheck defer src.Close() //nolint:errcheck var eg errgroup.Group eg.Go(func() error { return transfer(dst, src, "src -> dst: "+addr) }) eg.Go(func() error { return transfer(src, dst, "dst -> src: "+addr) }) if err = eg.Wait(); err != nil { log.Printf("HTTP CONNECT: tunneling to %s: failed %v", addr, err) } } type conn interface { io.Reader io.Writer io.Closer CloseRead() error CloseWrite() error } type tlsConnWrapper struct { net.Conn closeReadWrite *net.TCPConn } func (t *tlsConnWrapper) CloseRead() error { return t.closeReadWrite.CloseRead() } func (t *tlsConnWrapper) CloseWrite() error { return t.closeReadWrite.CloseWrite() } func transfer(destination conn, source conn, label string) error { defer destination.CloseWrite() //nolint:errcheck defer source.CloseRead() //nolint:errcheck n, err := io.Copy(destination, source) if err != nil { return fmt.Errorf("transfer failed %s (%d bytes copied): %w", label, n, err) } return nil } func handleHTTP(w http.ResponseWriter, req *http.Request) { resp, err := http.DefaultTransport.RoundTrip(req) if err != nil { http.Error(w, err.Error(), http.StatusServiceUnavailable) return } defer resp.Body.Close() //nolint:errcheck copyHeaders(w.Header(), resp.Header) w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) //nolint:errcheck } func copyHeaders(dst, src http.Header) { for k, vv := range src { for _, v := range vv { dst.Add(k, v) } } } func loggingMiddleware(h http.Handler) http.Handler { logFn := func(rw http.ResponseWriter, r *http.Request) { h.ServeHTTP(rw, r) // serve the original request log.Printf("%s %s", r.Method, r.RequestURI) } return http.HandlerFunc(logFn) } func runHTTPProxy(ctx context.Context) error { srv := &http.Server{ Addr: net.JoinHostPort("", strconv.Itoa(airgappedFlags.proxyPort)), Handler: loggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodConnect { handleTunneling(ctx, w, r) } else { handleHTTP(w, r) } })), // Disable HTTP/2. TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), } log.Printf("starting HTTP proxy on %s", srv.Addr) go srv.ListenAndServe() //nolint:errcheck <-ctx.Done() return srv.Close() } func runHTTPSProxy(ctx context.Context, certPEM, keyPEM []byte) error { certificate, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{certificate}, } srv := &http.Server{ Addr: net.JoinHostPort("", strconv.Itoa(airgappedFlags.httpsProxyPort)), Handler: loggingMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodConnect { handleTunneling(ctx, w, r) } else { handleHTTP(w, r) } })), // Disable HTTP/2. TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)), // Secure TLSConfig: tlsConfig, } log.Printf("starting HTTPS proxy on %s", srv.Addr) go srv.ListenAndServeTLS("", "") //nolint:errcheck <-ctx.Done() return srv.Close() } func runHTTPSReverseProxy(ctx context.Context, certPEM, keyPEM []byte) error { certificate, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { return err } tlsConfig := &tls.Config{ Certificates: []tls.Certificate{certificate}, } target, err := url.Parse(airgappedFlags.httpsReverseProxyTarget) if err != nil { return fmt.Errorf("error parsing reverse proxy target %q: %w", airgappedFlags.httpsReverseProxyTarget, err) } reverseProxy := httputil.NewSingleHostReverseProxy(target) srv := &http.Server{ Addr: net.JoinHostPort("", strconv.Itoa(airgappedFlags.httpsReverseProxyPort)), Handler: loggingMiddleware(reverseProxy), // Secure TLSConfig: tlsConfig, } log.Printf("starting HTTPS reverse proxy on %s to %s", srv.Addr, target.String()) go srv.ListenAndServeTLS("", "") //nolint:errcheck <-ctx.Done() return srv.Close() } func init() { airgappedCmd.Flags().IPVar(&airgappedFlags.advertisedAddress, "advertised-address", net.IPv4(10, 5, 0, 2), "The address to advertise to the cluster.") airgappedCmd.Flags().IntVar(&airgappedFlags.httpsPort, "https-port", 8001, "The HTTPS server port.") airgappedCmd.Flags().IntVar(&airgappedFlags.proxyPort, "proxy-port", 8002, "The HTTP proxy port.") airgappedCmd.Flags().IntVar(&airgappedFlags.httpsProxyPort, "https-proxy-port", 8003, "The HTTPS proxy port.") airgappedCmd.Flags().BoolVar(&airgappedFlags.useSecureProxy, "use-secure-proxy", false, "Whether to use HTTPS proxy.") airgappedCmd.Flags().BoolVar(&airgappedFlags.injectHTTPProxy, "inject-http-proxy", true, "Whether to inject HTTP proxy configuration.") airgappedCmd.Flags().IntVar(&airgappedFlags.httpsReverseProxyPort, "https-reverse-proxy-port", 8004, "The HTTPS reverse proxy port.") airgappedCmd.Flags().StringVar(&airgappedFlags.httpsReverseProxyTarget, "https-reverse-proxy-target", "http://localhost/", "The HTTPS reverse proxy target (URL).") Cmd.AddCommand(airgappedCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/debug/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package debug implements "debug" subcommands. package debug import ( "github.com/spf13/cobra" ) // Cmd represents the debug command. var Cmd = &cobra.Command{ Use: "debug-tool", Short: "A collection of commands to facilitate debugging of Talos.", Hidden: true, Long: ``, } ================================================ FILE: cmd/talosctl/cmd/mgmt/debug/httproot/debug.yaml ================================================ apiVersion: apps/v1 kind: DaemonSet metadata: labels: app: debug-container name: debug-container namespace: default spec: selector: matchLabels: app: debug-container template: metadata: labels: app: debug-container spec: containers: - args: - "inf" command: - /bin/sleep image: alpine:latest imagePullPolicy: IfNotPresent name: debug-container resources: {} terminationGracePeriodSeconds: 30 updateStrategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate ================================================ FILE: cmd/talosctl/cmd/mgmt/dhcpd_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package mgmt import ( "net" "slices" "strings" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) var dhcpdLaunchCmdFlags struct { addr string ifName string statePath string ipxeNextHandler string } // dhcpdLaunchCmd represents the dhcpd-launch command. var dhcpdLaunchCmd = &cobra.Command{ Use: "dhcpd-launch", Short: "Internal command used by VM provisioners", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { ips := xslices.Map(slices.Collect(strings.SplitSeq(dhcpdLaunchCmdFlags.addr, ",")), net.ParseIP) var eg errgroup.Group eg.Go(func() error { return vm.DHCPd(dhcpdLaunchCmdFlags.ifName, ips, dhcpdLaunchCmdFlags.statePath) }) if dhcpdLaunchCmdFlags.ipxeNextHandler != "" { eg.Go(func() error { return vm.TFTPd(ips, dhcpdLaunchCmdFlags.ipxeNextHandler) }) } return eg.Wait() }, } func init() { dhcpdLaunchCmd.Flags().StringVar(&dhcpdLaunchCmdFlags.addr, "addr", "localhost", "IP addresses to listen on") dhcpdLaunchCmd.Flags().StringVar(&dhcpdLaunchCmdFlags.ifName, "interface", "", "interface to listen on") dhcpdLaunchCmd.Flags().StringVar(&dhcpdLaunchCmdFlags.statePath, "state-path", "", "path to state directory") dhcpdLaunchCmd.Flags().StringVar(&dhcpdLaunchCmdFlags.ipxeNextHandler, "ipxe-next-handler", "", "iPXE script to chain load") addCommand(dhcpdLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/dnsd_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package mgmt import ( "net" "slices" "strings" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) var dnsdLaunchCmdFlags struct { addr string resolvConf string } // dnsdLaunchCmd represents the dnsd-launch command. var dnsdLaunchCmd = &cobra.Command{ Use: "dnsd-launch", Short: "Internal command used by VM provisioners", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { ips := xslices.Map(slices.Collect(strings.SplitSeq(dnsdLaunchCmdFlags.addr, ",")), net.ParseIP) var eg errgroup.Group eg.Go(func() error { return vm.DNSd(ips, dnsdLaunchCmdFlags.resolvConf) }) return eg.Wait() }, } func init() { dnsdLaunchCmd.Flags().StringVar(&dnsdLaunchCmdFlags.addr, "addr", "localhost:53", "IP addresses to listen on") dnsdLaunchCmd.Flags().StringVar(&dnsdLaunchCmdFlags.resolvConf, "resolv-conf", "/etc/resolv.conf", "path to resolv file") addCommand(dnsdLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/ca.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package gen implements the genration of various artifacts. package gen import ( "fmt" "os" "time" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" ) var genCACmdFlags struct { organization string hours int rsa bool } // genCACmd represents the `gen ca` command. var genCACmd = &cobra.Command{ Use: "ca", Short: "Generates a self-signed X.509 certificate authority", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { opts := []x509.Option{x509.RSA(genCACmdFlags.rsa)} if genCACmdFlags.organization != "" { opts = append(opts, x509.Organization(genCACmdFlags.organization)) } opts = append(opts, x509.NotAfter(time.Now().Add(time.Duration(genCACmdFlags.hours)*time.Hour))) ca, err := x509.NewSelfSignedCertificateAuthority(opts...) if err != nil { return fmt.Errorf("error generating CA: %w", err) } caCertFile := genCACmdFlags.organization + crtExt caHashFile := genCACmdFlags.organization + ".sha256" caKeyFile := genCACmdFlags.organization + keyExt if err := validateFilesExists([]string{caCertFile, caHashFile, caKeyFile}); err != nil { return err } if err := os.WriteFile(caCertFile, ca.CrtPEM, 0o600); err != nil { return fmt.Errorf("error writing CA certificate: %w", err) } if err := os.WriteFile(caHashFile, []byte(x509.Hash(ca.Crt)), 0o600); err != nil { return fmt.Errorf("error writing certificate hash: %w", err) } if err := os.WriteFile(caKeyFile, ca.KeyPEM, 0o600); err != nil { return fmt.Errorf("error writing key: %w", err) } return nil }, } func init() { genCACmd.Flags().StringVar(&genCACmdFlags.organization, "organization", "", "X.509 distinguished name for the Organization") cli.Should(cobra.MarkFlagRequired(genCACmd.Flags(), "organization")) genCACmd.Flags().IntVar(&genCACmdFlags.hours, "hours", 87600, "the hours from now on which the certificate validity period ends") genCACmd.Flags().BoolVar(&genCACmdFlags.rsa, "rsa", false, "generate in RSA format") Cmd.AddCommand(genCACmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "errors" "fmt" "net/url" "os" "path/filepath" "slices" "strings" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/xslices" sideronet "github.com/siderolabs/net" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( controlPlaneOutputType = "controlplane" workerOutputType = "worker" talosconfigOutputType = "talosconfig" stdoutOutput = "-" yamlExt = ".yaml" ) var allOutputTypes = []string{ controlPlaneOutputType, workerOutputType, talosconfigOutputType, } type configOutputPaths struct { controlPlane, worker, talosconfig string } var genConfigCmdFlags struct { additionalSANs []string configVersion string dnsDomain string kubernetesVersion string talosVersion string installDisk string installImage string // outputDir is a hidden flag kept for backwards compatibility outputDir string output string outputTypes []string configPatch []string configPatchControlPlane []string configPatchWorker []string registryMirrors []string withExamples bool withDocs bool withClusterDiscovery bool withKubeSpan bool withSecrets string } // NewConfigCmd builds the config generation subcommand with the given name. func NewConfigCmd(name string) *cobra.Command { return &cobra.Command{ Use: fmt.Sprintf("%s ", name), Short: "Generates a set of configuration files for Talos cluster", Long: `The cluster endpoint is the URL for the Kubernetes API. If you decide to use a control plane node, common in a single node control plane setup, use port 6443 as this is the port that the API server binds to on every control plane node. For an HA setup, usually involving a load balancer, use the IP and port of the load balancer.`, Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { err := validateClusterEndpoint(args[1]) if err != nil { return err } switch genConfigCmdFlags.configVersion { case "v1alpha1": return writeConfig(args) default: return fmt.Errorf("unknown config version: %q", genConfigCmdFlags.configVersion) } }, } } func fixControlPlaneEndpoint(u *url.URL) *url.URL { // handle the case when the hostname/IP is given without the port, it parses as URL Path if u.Scheme == "" && u.Host == "" && u.Path != "" { u.Host = u.Path u.Path = "" } u.Scheme = "https" if u.Port() == "" { u.Host = fmt.Sprintf("%s:%d", u.Host, constants.DefaultControlPlanePort) } return u } // GenerateConfigBundle generates the Talos config bundle // // GenerateConfigBundle is useful for integration with external tooling options. func GenerateConfigBundle(genOptions []generate.Option, clusterName string, endpoint string, kubernetesVersion string, configPatch []string, configPatchControlPlane []string, configPatchWorker []string, ) (*bundle.Bundle, error) { configBundleOpts := []bundle.Option{ bundle.WithInputOptions( &bundle.InputOptions{ ClusterName: clusterName, Endpoint: endpoint, KubeVersion: strings.TrimPrefix(kubernetesVersion, "v"), GenOptions: genOptions, }, ), } addConfigPatch := func(configPatches []string, configOpt func([]configpatcher.Patch) bundle.Option) error { patches, err := configpatcher.LoadPatches(configPatches) if err != nil { return fmt.Errorf("error parsing config JSON patch: %w", err) } configBundleOpts = append(configBundleOpts, configOpt(patches)) return nil } if err := addConfigPatch(configPatch, bundle.WithPatch); err != nil { return nil, err } if err := addConfigPatch(configPatchControlPlane, bundle.WithPatchControlPlane); err != nil { return nil, err } if err := addConfigPatch(configPatchWorker, bundle.WithPatchWorker); err != nil { return nil, err } configBundle, err := bundle.NewBundle(configBundleOpts...) if err != nil { return nil, fmt.Errorf("failed to generate config bundle: %w", err) } return configBundle, nil } //nolint:gocyclo func writeConfig(args []string) error { if err := validateFlags(); err != nil { return err } paths, err := outputPaths() if err != nil { return err } var genOptions []generate.Option //nolint:prealloc for _, registryMirror := range genConfigCmdFlags.registryMirrors { left, right, ok := strings.Cut(registryMirror, "=") if !ok { return fmt.Errorf("invalid registry mirror spec: %q", registryMirror) } genOptions = append(genOptions, generate.WithRegistryMirror(left, right)) } if genConfigCmdFlags.talosVersion != "" { var versionContract *config.VersionContract versionContract, err = config.ParseContractFromVersion(genConfigCmdFlags.talosVersion) if err != nil { return fmt.Errorf("invalid talos-version: %w", err) } genOptions = append(genOptions, generate.WithVersionContract(versionContract)) } // Add KubeSpan configuration based on version if genConfigCmdFlags.withKubeSpan { genOptions = append(genOptions, generate.WithKubeSpanEnabled(genConfigCmdFlags.withKubeSpan), ) } if genConfigCmdFlags.withSecrets != "" { var secretsBundle *secrets.Bundle secretsBundle, err = secrets.LoadBundle(genConfigCmdFlags.withSecrets) if err != nil { return fmt.Errorf("failed to load secrets bundle: %w", err) } if err = secretsBundle.Validate(); err != nil { return fmt.Errorf("failed to validate secrets bundle: %w", err) } genOptions = append(genOptions, generate.WithSecretsBundle(secretsBundle)) } genOptions = append(genOptions, generate.WithInstallDisk(genConfigCmdFlags.installDisk), generate.WithInstallImage(genConfigCmdFlags.installImage), generate.WithAdditionalSubjectAltNames(genConfigCmdFlags.additionalSANs), generate.WithDNSDomain(genConfigCmdFlags.dnsDomain), generate.WithClusterDiscovery(genConfigCmdFlags.withClusterDiscovery), ) commentsFlags := encoder.CommentsDisabled if genConfigCmdFlags.withDocs { commentsFlags |= encoder.CommentsDocs } if genConfigCmdFlags.withExamples { commentsFlags |= encoder.CommentsExamples } configBundle, err := GenerateConfigBundle( genOptions, args[0], args[1], genConfigCmdFlags.kubernetesVersion, genConfigCmdFlags.configPatch, genConfigCmdFlags.configPatchControlPlane, genConfigCmdFlags.configPatchWorker) if err != nil { return err } return writeConfigBundle(configBundle, paths, commentsFlags) } func validateFlags() error { if len(genConfigCmdFlags.outputTypes) == 0 { return errors.New("at least one output type must be specified") } if len(genConfigCmdFlags.outputTypes) > 1 && genConfigCmdFlags.output == stdoutOutput { return errors.New("can't use multiple output types with stdout") } if genConfigCmdFlags.outputDir != "" && genConfigCmdFlags.output != "" { return errors.New("can't use both output-dir and output") } if genConfigCmdFlags.outputDir != "" { genConfigCmdFlags.output = genConfigCmdFlags.outputDir } var err error for _, outputType := range genConfigCmdFlags.outputTypes { if !slices.ContainsFunc(allOutputTypes, func(t string) bool { return t == outputType }) { err = multierror.Append(err, fmt.Errorf("invalid output type: %q", outputType)) } } return err } func writeConfigBundle(configBundle *bundle.Bundle, outputPaths configOutputPaths, commentsFlags encoder.CommentsFlags) error { outputTypesSet := xslices.ToSet(genConfigCmdFlags.outputTypes) if _, ok := outputTypesSet[controlPlaneOutputType]; ok { data, err := configBundle.Serialize(commentsFlags, machine.TypeControlPlane) if err != nil { return err } if err = writeToDestination(data, outputPaths.controlPlane, 0o644); err != nil { return err } } if _, ok := outputTypesSet[workerOutputType]; ok { data, err := configBundle.Serialize(commentsFlags, machine.TypeWorker) if err != nil { return err } if err = writeToDestination(data, outputPaths.worker, 0o644); err != nil { return err } } if _, ok := outputTypesSet[talosconfigOutputType]; ok { data, err := yaml.Marshal(configBundle.TalosConfig()) if err != nil { return fmt.Errorf("failed to marshal config: %+v", err) } if err = writeToDestination(data, outputPaths.talosconfig, 0o644); err != nil { return err } } return nil } func writeToDestination(data []byte, destination string, permissions os.FileMode) error { if destination == stdoutOutput { _, err := os.Stdout.Write(data) return err } if err := validateFileExists(destination); err != nil { return err } parentDir := filepath.Dir(destination) // Create dir path, ignoring "already exists" messages if err := os.MkdirAll(parentDir, os.ModePerm); err != nil { return fmt.Errorf("failed to create output dir: %w", err) } err := os.WriteFile(destination, data, permissions) fmt.Fprintf(os.Stderr, "Created %s\n", destination) return err } func outputPaths() (configOutputPaths, error) { // output to stdout if genConfigCmdFlags.output == stdoutOutput { return configOutputPaths{controlPlane: stdoutOutput, worker: stdoutOutput, talosconfig: stdoutOutput}, nil } // output is not specified - use current working directory as the default if genConfigCmdFlags.output == "" { cwd, err := os.Getwd() if err != nil { return configOutputPaths{}, err } controlPlane := filepath.Join(cwd, machine.TypeControlPlane.String()+yamlExt) worker := filepath.Join(cwd, machine.TypeWorker.String()+yamlExt) talosconfig := filepath.Join(cwd, "talosconfig") return configOutputPaths{controlPlane: controlPlane, worker: worker, talosconfig: talosconfig}, nil } // output is specified // if a single output type is specified, treat --output as a file path and not a directory // except when the deprecated flag of --output-dir is specified - it is always treated as a directory if len(genConfigCmdFlags.outputTypes) == 1 && genConfigCmdFlags.outputDir == "" { // specified output is a file return configOutputPaths{ controlPlane: genConfigCmdFlags.output, worker: genConfigCmdFlags.output, talosconfig: genConfigCmdFlags.output, }, nil } // treat --output as a directory controlPlane := filepath.Join(genConfigCmdFlags.output, machine.TypeControlPlane.String()+yamlExt) worker := filepath.Join(genConfigCmdFlags.output, machine.TypeWorker.String()+yamlExt) talosconfig := filepath.Join(genConfigCmdFlags.output, "talosconfig") return configOutputPaths{controlPlane: controlPlane, worker: worker, talosconfig: talosconfig}, nil } func validateClusterEndpoint(endpoint string) error { // Validate url input to ensure it has https:// scheme before we attempt to gen u, err := url.Parse(endpoint) if err != nil { if !strings.Contains(endpoint, "/") { // not a URL, could be just host:port u = &url.URL{ Host: endpoint, } } else { return fmt.Errorf("failed to parse the cluster endpoint URL: %w", err) } } if u.Scheme == "" { if u.Port() == "" { return fmt.Errorf("no scheme and port specified for the cluster endpoint URL\ntry: %q", fixControlPlaneEndpoint(u)) } return fmt.Errorf("no scheme specified for the cluster endpoint URL\ntry: %q", fixControlPlaneEndpoint(u)) } if u.Scheme != "https" { return fmt.Errorf("the control plane endpoint URL should have scheme https://\ntry: %q", fixControlPlaneEndpoint(u)) } if err = sideronet.ValidateEndpointURI(endpoint); err != nil { return fmt.Errorf("error validating the cluster endpoint URL: %w", err) } return nil } func init() { genConfigCmd := NewConfigCmd("config") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.installDisk, "install-disk", "/dev/sda", "the disk to install to") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.installImage, "install-image", helpers.DefaultImage(images.DefaultInstallerImageRepository), "the image used to perform an installation") genConfigCmd.Flags().StringSliceVar(&genConfigCmdFlags.additionalSANs, "additional-sans", []string{}, "additional Subject-Alt-Names for the APIServer certificate") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.dnsDomain, "dns-domain", "cluster.local", "the dns domain to use for cluster") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.configVersion, "version", "v1alpha1", "the desired machine config version to generate") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.talosVersion, "talos-version", "", "the desired Talos version to generate config for (backwards compatibility, e.g. v0.8)") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.kubernetesVersion, "kubernetes-version", constants.DefaultKubernetesVersion, "desired kubernetes version to run") genConfigCmd.Flags().StringArrayVar(&genConfigCmdFlags.configPatch, "config-patch", nil, "patch generated machineconfigs (applied to all node types), use @file to read a patch from file") genConfigCmd.Flags().StringArrayVar(&genConfigCmdFlags.configPatchControlPlane, "config-patch-control-plane", nil, "patch generated machineconfigs (applied to 'init' and 'controlplane' types)") genConfigCmd.Flags().StringArrayVar(&genConfigCmdFlags.configPatchWorker, "config-patch-worker", nil, "patch generated machineconfigs (applied to 'worker' type)") genConfigCmd.Flags().StringSliceVar(&genConfigCmdFlags.registryMirrors, "registry-mirror", []string{}, "list of registry mirrors to use in format: =") genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withExamples, "with-examples", "", true, "renders all machine configs with the commented examples") genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withDocs, "with-docs", "", true, "renders all machine configs adding the documentation for each field") genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withClusterDiscovery, "with-cluster-discovery", "", true, "enable cluster discovery feature") genConfigCmd.Flags().BoolVarP(&genConfigCmdFlags.withKubeSpan, "with-kubespan", "", false, "enable KubeSpan feature") genConfigCmd.Flags().StringVar(&genConfigCmdFlags.withSecrets, "with-secrets", "", "use a secrets file generated using 'gen secrets'") genConfigCmd.Flags().StringSliceVarP(&genConfigCmdFlags.outputTypes, "output-types", "t", allOutputTypes, fmt.Sprintf("types of outputs to be generated. valid types are: %q", allOutputTypes)) genConfigCmd.Flags().StringVarP(&genConfigCmdFlags.output, "output", "o", "", `destination to output generated files. when multiple output types are specified, it must be a directory. for a single output type, it must either be a file path, or "-" for stdout`) genConfigCmd.Flags().StringVar(&genConfigCmdFlags.outputDir, "output-dir", "", "destination to output generated files") // kept for backwards compatibility genConfigCmd.Flags().MarkHidden("output-dir") //nolint:errcheck Cmd.AddCommand(genConfigCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/crt.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( stdlibx509 "crypto/x509" "encoding/pem" "errors" "fmt" "os" "time" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" ) var genCrtCmdFlags struct { name string ca string csr string hours int } // genCrtCmd represents the `gen crt` command. var genCrtCmd = &cobra.Command{ Use: "crt", Short: "Generates an X.509 Ed25519 certificate", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { caBytes, err := os.ReadFile(genCrtCmdFlags.ca + crtExt) if err != nil { return fmt.Errorf("error reading CA cert: %s", err) } caPemBlock, _ := pem.Decode(caBytes) if caPemBlock == nil { return errors.New("error decoding cert PEM") } caCrt, err := stdlibx509.ParseCertificate(caPemBlock.Bytes) if err != nil { return fmt.Errorf("error parsing cert: %s", err) } keyBytes, err := os.ReadFile(genCrtCmdFlags.ca + keyExt) if err != nil { return fmt.Errorf("error reading key file: %s", err) } keyPemBlock, _ := pem.Decode(keyBytes) if keyPemBlock == nil { return errors.New("error decoding key PEM") } caKey, err := stdlibx509.ParsePKCS8PrivateKey(keyPemBlock.Bytes) if err != nil { return fmt.Errorf("error parsing EC key: %s", err) } csrBytes, err := os.ReadFile(genCrtCmdFlags.csr) if err != nil { return fmt.Errorf("error reading CSR: %s", err) } csrPemBlock, _ := pem.Decode(csrBytes) if csrPemBlock == nil { return errors.New("error parsing CSR PEM") } ccsr, err := stdlibx509.ParseCertificateRequest(csrPemBlock.Bytes) if err != nil { return fmt.Errorf("error parsing CSR: %s", err) } signedCrt, err := x509.NewCertificateFromCSR(caCrt, caKey, ccsr, x509.NotAfter(time.Now().Add(time.Duration(genCrtCmdFlags.hours)*time.Hour)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageClientAuth}), ) if err != nil { return fmt.Errorf("error signing certificate: %s", err) } certFile := genCrtCmdFlags.name + crtExt if err = validateFileExists(certFile); err != nil { return err } if err = os.WriteFile(certFile, signedCrt.X509CertificatePEM, 0o600); err != nil { return fmt.Errorf("error writing certificate: %s", err) } return err }, } func init() { genCrtCmd.Flags().StringVar(&genCrtCmdFlags.name, "name", "", "the basename of the generated file") cli.Should(cobra.MarkFlagRequired(genCrtCmd.Flags(), "name")) genCrtCmd.Flags().StringVar(&genCrtCmdFlags.ca, "ca", "", "path to the PEM encoded CERTIFICATE") cli.Should(cobra.MarkFlagRequired(genCrtCmd.Flags(), "ca")) genCrtCmd.Flags().StringVar(&genCrtCmdFlags.csr, "csr", "", "path to the PEM encoded CERTIFICATE REQUEST") cli.Should(cobra.MarkFlagRequired(genCrtCmd.Flags(), "csr")) genCrtCmd.Flags().IntVar(&genCrtCmdFlags.hours, "hours", 24, "the hours from now on which the certificate validity period ends") Cmd.AddCommand(genCrtCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/csr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( stdlibx509 "crypto/x509" "encoding/pem" "errors" "fmt" "net" "os" "path" "strings" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/role" ) var genCSRCmdFlags struct { key string ip string roles []string } // genCSRCmd represents the `gen csr` command. var genCSRCmd = &cobra.Command{ Use: "csr", Short: "Generates a CSR using an Ed25519 private key", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { keyBytes, err := os.ReadFile(genCSRCmdFlags.key) if err != nil { return fmt.Errorf("error reading key: %s", err) } pemBlock, _ := pem.Decode(keyBytes) if pemBlock == nil { return errors.New("error decoding PEM") } keyEC, err := stdlibx509.ParsePKCS8PrivateKey(pemBlock.Bytes) if err != nil { return fmt.Errorf("error parsing ECDSA key: %s", err) } var opts []x509.Option //nolint:prealloc // dynamic parsed := net.ParseIP(genCSRCmdFlags.ip) if parsed == nil { return fmt.Errorf("invalid IP: %s", genCSRCmdFlags.ip) } roles, unknownRoles := role.Parse(genCSRCmdFlags.roles) if len(unknownRoles) != 0 { return fmt.Errorf("unknown roles: %s", strings.Join(unknownRoles, ", ")) } ips := []net.IP{parsed} opts = append(opts, x509.Organization(roles.Strings()...)) opts = append(opts, x509.IPAddresses(ips)) csr, err := x509.NewCertificateSigningRequest(keyEC, opts...) if err != nil { return fmt.Errorf("error generating CSR: %s", err) } csrFile := strings.TrimSuffix(genCSRCmdFlags.key, path.Ext(genCSRCmdFlags.key)) + ".csr" if err := validateFileExists(csrFile); err != nil { return err } if err := os.WriteFile(csrFile, csr.X509CertificateRequestPEM, 0o600); err != nil { return fmt.Errorf("error writing CSR: %s", err) } return nil }, } func init() { genCSRCmd.Flags().StringVar(&genCSRCmdFlags.key, "key", "", "path to the PEM encoded EC or RSA PRIVATE KEY") cli.Should(cobra.MarkFlagRequired(genCSRCmd.Flags(), "key")) genCSRCmd.Flags().StringVar(&genCSRCmdFlags.ip, "ip", "", "generate the certificate for this IP address") cli.Should(cobra.MarkFlagRequired(genCSRCmd.Flags(), "ip")) genCSRCmd.Flags().StringSliceVar(&genCSRCmdFlags.roles, "roles", role.MakeSet(role.Admin).Strings(), "roles") Cmd.AddCommand(genCSRCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/gen.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "fmt" "os" "github.com/hashicorp/go-multierror" "github.com/spf13/cobra" ) const ( crtExt = ".crt" keyExt = ".key" ) var genCmdFlags struct { force bool } // Cmd represents the `gen` command. var Cmd = &cobra.Command{ Use: "gen", Short: "Generate CAs, certificates, and private keys", Long: ``, } func init() { Cmd.PersistentFlags().BoolVarP(&genCmdFlags.force, "force", "f", false, "will overwrite existing files") } func validateFileExists(file string) error { if !genCmdFlags.force { if _, err := os.Stat(file); err == nil { return fmt.Errorf("file %q already exists, use --force to overwrite", file) } } return nil } func validateFilesExists(files []string) error { var combinedErr multierror.Error for _, file := range files { if err := validateFileExists(file); err != nil { combinedErr.Errors = append(combinedErr.Errors, err) } } return combinedErr.ErrorOrNil() } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/key.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "fmt" "os" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" ) var genKeyCmdFlags struct { name string } // genKeyCmd represents the `gen key` command. var genKeyCmd = &cobra.Command{ Use: "key", Short: "Generates an Ed25519 private key", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { key, err := x509.NewEd25519Key() if err != nil { return fmt.Errorf("error generating key: %w", err) } keyFile := genKeyCmdFlags.name + keyExt if err = validateFileExists(keyFile); err != nil { return err } if err := os.WriteFile(keyFile, key.PrivateKeyPEM, 0o600); err != nil { return fmt.Errorf("error writing key: %w", err) } return nil }, } func init() { genKeyCmd.Flags().StringVar(&genKeyCmdFlags.name, "name", "", "the basename of the generated file") cli.Should(cobra.MarkFlagRequired(genKeyCmd.Flags(), "name")) Cmd.AddCommand(genKeyCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/keypair.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "fmt" "net" "os" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" ) var genKeypairCmdFlags struct { ip string organization string } var genKeypairCmd = &cobra.Command{ Use: "keypair", Short: "Generates an X.509 Ed25519 key pair", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { var opts []x509.Option if genKeypairCmdFlags.ip != "" { parsed := net.ParseIP(genKeypairCmdFlags.ip) if parsed == nil { return fmt.Errorf("invalid IP: %s", genKeypairCmdFlags.ip) } ips := []net.IP{parsed} opts = append(opts, x509.IPAddresses(ips)) } if genKeypairCmdFlags.organization != "" { opts = append(opts, x509.Organization(genKeypairCmdFlags.organization)) } ca, err := x509.NewSelfSignedCertificateAuthority(opts...) if err != nil { return fmt.Errorf("error generating CA: %s", err) } certFile := genKeypairCmdFlags.organization + crtExt keyFile := genKeypairCmdFlags.organization + keyExt if err = validateFilesExists([]string{certFile, keyFile}); err != nil { return err } if err := os.WriteFile(certFile, ca.CrtPEM, 0o600); err != nil { return fmt.Errorf("error writing certificate: %s", err) } if err := os.WriteFile(keyFile, ca.KeyPEM, 0o600); err != nil { return fmt.Errorf("error writing key: %s", err) } return nil }, } func init() { genKeypairCmd.Flags().StringVar(&genKeypairCmdFlags.ip, "ip", "", "generate the certificate for this IP address") genKeypairCmd.Flags().StringVar(&genKeypairCmdFlags.organization, "organization", "", "X.509 distinguished name for the Organization") cli.Should(cobra.MarkFlagRequired(genKeypairCmd.Flags(), "organization")) Cmd.AddCommand(genKeypairCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/secrets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "fmt" "os" "time" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" ) var genSecretsCmdFlags struct { outputFile string talosVersion string fromKubernetesPki string fromControlplaneConfig string kubernetesBootstrapToken string } // genSecretsCmd represents the `gen secrets` command. var genSecretsCmd = &cobra.Command{ Use: "secrets", Short: "Generates a secrets bundle file which can later be used to generate a config", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { var ( secretsBundle *secrets.Bundle versionContract *config.VersionContract err error ) if genSecretsCmdFlags.talosVersion != "" { versionContract, err = config.ParseContractFromVersion(genSecretsCmdFlags.talosVersion) if err != nil { return fmt.Errorf("invalid talos-version: %w", err) } } switch { case genSecretsCmdFlags.fromKubernetesPki != "": secretsBundle, err = secrets.NewBundleFromKubernetesPKI(genSecretsCmdFlags.fromKubernetesPki, genSecretsCmdFlags.kubernetesBootstrapToken, versionContract) case genSecretsCmdFlags.fromControlplaneConfig != "": var cfg config.Provider cfg, err = configloader.NewFromFile(genSecretsCmdFlags.fromControlplaneConfig) if err != nil { return fmt.Errorf("failed to load controlplane config: %w", err) } secretsBundle = secrets.NewBundleFromConfig(secrets.NewFixedClock(time.Now()), cfg) default: secretsBundle, err = secrets.NewBundle(secrets.NewFixedClock(time.Now()), versionContract, ) } if err != nil { return fmt.Errorf("failed to create secrets bundle: %w", err) } return writeSecretsBundleToFile(secretsBundle) }, } func writeSecretsBundleToFile(bundle *secrets.Bundle) error { bundleBytes, err := yaml.Marshal(bundle) if err != nil { return err } if genSecretsCmdFlags.outputFile == stdoutOutput { _, err = os.Stdout.Write(bundleBytes) return err } if err = validateFileExists(genSecretsCmdFlags.outputFile); err != nil { return err } return os.WriteFile(genSecretsCmdFlags.outputFile, bundleBytes, 0o600) } func init() { genSecretsCmd.Flags().StringVarP(&genSecretsCmdFlags.outputFile, "output-file", "o", "secrets.yaml", `path of the output file, or "-" for stdout`) genSecretsCmd.Flags().StringVar(&genSecretsCmdFlags.talosVersion, "talos-version", "", "the desired Talos version to generate secrets bundle for (backwards compatibility, e.g. v0.8)") genSecretsCmd.Flags().StringVar(&genSecretsCmdFlags.fromControlplaneConfig, "from-controlplane-config", "", "use the provided controlplane Talos machine configuration as input") genSecretsCmd.Flags().StringVarP(&genSecretsCmdFlags.fromKubernetesPki, "from-kubernetes-pki", "p", "", "use a Kubernetes PKI directory (e.g. /etc/kubernetes/pki) as input") genSecretsCmd.Flags().StringVarP(&genSecretsCmdFlags.kubernetesBootstrapToken, "kubernetes-bootstrap-token", "t", "", "use the provided bootstrap token as input") Cmd.AddCommand(genSecretsCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/gen/secureboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "context" "encoding/pem" "errors" "fmt" "io/fs" "os" "path/filepath" "time" "github.com/siderolabs/crypto/x509" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/internal/pkg/secureboot/database" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" ) var genSecurebootCmdFlags struct { outputDirectory string } // genSecurebootCmd represents the `gen secureboot` command. var genSecurebootCmd = &cobra.Command{ Use: "secureboot", Short: "Generates secrets for the SecureBoot process", Long: ``, } var genSecurebootUKICmdFlags struct { commonName string } // genSecurebootUKICmd represents the `gen secureboot uki` command. var genSecurebootUKICmd = &cobra.Command{ Use: "uki", Short: "Generates a certificate which is used to sign boot assets (UKI)", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return generateSigningCerts(genSecurebootCmdFlags.outputDirectory, "uki", genSecurebootUKICmdFlags.commonName, 4096, true) }, } // genSecurebootPCRCmd represents the `gen secureboot pcr` command. var genSecurebootPCRCmd = &cobra.Command{ Use: "pcr", Short: "Generates a key which is used to sign TPM PCR values", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return generateSigningCerts(genSecurebootCmdFlags.outputDirectory, "pcr", "dummy", 2048, false) }, } var genSecurebootDatabaseCmdFlags struct { enrolledCertificatePath string signingCertificatePath, signingKeyPath string includeWellKnownCerts bool } // genSecurebootDatabaseCmd represents the `gen secureboot database` command. var genSecurebootDatabaseCmd = &cobra.Command{ Use: "database", Short: "Generates a UEFI database to enroll the signing certificate", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return generateSecureBootDatabase( genSecurebootCmdFlags.outputDirectory, genSecurebootDatabaseCmdFlags.enrolledCertificatePath, genSecurebootDatabaseCmdFlags.signingKeyPath, genSecurebootDatabaseCmdFlags.signingCertificatePath, genSecurebootDatabaseCmdFlags.includeWellKnownCerts, ) }, } func checkedWrite(path string, data []byte, perm fs.FileMode) error { //nolint:unparam if err := validateFileExists(path); err != nil { return err } if dirname := filepath.Dir(path); dirname != "." { if err := os.MkdirAll(dirname, 0o700); err != nil { return err } } fmt.Fprintf(os.Stderr, "writing %s\n", path) return os.WriteFile(path, data, perm) } func generateSigningCerts(path, prefix, commonName string, rsaBits int, outputCert bool) error { currentTime := time.Now() opts := []x509.Option{ x509.RSA(true), x509.Bits(rsaBits), x509.CommonName(commonName), x509.NotAfter(currentTime.Add(secrets.CAValidityTime)), x509.NotBefore(currentTime), x509.Organization(commonName), } signingKey, err := x509.NewSelfSignedCertificateAuthority(opts...) if err != nil { return err } if outputCert { if err = checkedWrite(filepath.Join(path, prefix+"-signing-cert.pem"), signingKey.CrtPEM, 0o600); err != nil { return err } if err = saveAsDER(filepath.Join(path, prefix+"-signing-cert.der"), signingKey.CrtPEM); err != nil { return err } } return checkedWrite(filepath.Join(path, prefix+"-signing-key.pem"), signingKey.KeyPEM, 0o600) } func saveAsDER(file string, pem []byte) error { publicKeyDER, err := convertPEMToDER(pem) if err != nil { return err } return checkedWrite(file, publicKeyDER, 0o600) } // generateSecureBootDatabase generates a UEFI database to enroll the signing certificate. // // ref: https://blog.hansenpartnership.com/the-meaning-of-all-the-uefi-keys/ func generateSecureBootDatabase(path, enrolledCertificatePath, signingKeyPath, signingCertificatePath string, includeWellKnownCerts bool) error { in := profile.SigningKeyAndCertificate{ KeyPath: signingKeyPath, CertPath: signingCertificatePath, } signer, err := in.GetSigner(context.Background()) // context not used if err != nil { return fmt.Errorf("failed to create signer: %w", err) } enrolledPEM, err := os.ReadFile(enrolledCertificatePath) if err != nil { return err } db, err := database.Generate(enrolledPEM, signer, database.IncludeWellKnownCertificates(includeWellKnownCerts)) if err != nil { return fmt.Errorf("failed to generate database: %w", err) } // output all files with sd-boot conventional names for auto-enrolment for _, entry := range db { if err = checkedWrite(filepath.Join(path, entry.Name), entry.Contents, 0o600); err != nil { return err } } return nil } func init() { genSecurebootCmd.PersistentFlags().StringVarP(&genSecurebootCmdFlags.outputDirectory, "output", "o", helpers.ArtifactsPath, "path to the directory storing the generated files") Cmd.AddCommand(genSecurebootCmd) genSecurebootUKICmd.Flags().StringVar(&genSecurebootUKICmdFlags.commonName, "common-name", "Test UKI Signing Key", "common name for the certificate") genSecurebootCmd.AddCommand(genSecurebootUKICmd) genSecurebootCmd.AddCommand(genSecurebootPCRCmd) genSecurebootDatabaseCmd.Flags().StringVar( &genSecurebootDatabaseCmdFlags.enrolledCertificatePath, "enrolled-certificate", helpers.ArtifactPath(constants.SecureBootSigningCertAsset), "path to the certificate to enroll") genSecurebootDatabaseCmd.Flags().StringVar( &genSecurebootDatabaseCmdFlags.signingCertificatePath, "signing-certificate", helpers.ArtifactPath(constants.SecureBootSigningCertAsset), "path to the certificate used to sign the database") genSecurebootDatabaseCmd.Flags().StringVar( &genSecurebootDatabaseCmdFlags.signingKeyPath, "signing-key", helpers.ArtifactPath(constants.SecureBootSigningKeyAsset), "path to the key used to sign the database") genSecurebootDatabaseCmd.Flags().BoolVar( &genSecurebootDatabaseCmdFlags.includeWellKnownCerts, "include-well-known-uefi-certs", false, "include well-known UEFI (Microsoft) certificates in the database") genSecurebootCmd.AddCommand(genSecurebootDatabaseCmd) } func convertPEMToDER(data []byte) ([]byte, error) { block, rest := pem.Decode(data) if block == nil { return nil, errors.New("failed to decode PEM data") } if len(rest) > 0 { return nil, errors.New("more than one PEM block found in PEM data") } return block.Bytes, nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/inject/inject.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package inject import "github.com/spf13/cobra" // Cmd represents the debug command. var Cmd = &cobra.Command{ Use: "inject", Short: "Inject Talos API resources into Kubernetes manifests", Long: ``, } ================================================ FILE: cmd/talosctl/cmd/mgmt/inject/serviceaccount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package inject import ( "fmt" "os" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/kubernetes/inject" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) var serviceAccountCmdFlags struct { file string roles []string } var serviceAccountCmd = &cobra.Command{ Use: fmt.Sprintf("%s [--roles=','] -f ", constants.ServiceAccountResourceSingular), Aliases: []string{constants.ServiceAccountResourceShortName}, Short: "Inject Talos API ServiceAccount into Kubernetes manifests", Example: fmt.Sprintf( `talosctl inject %[1]s --roles="os:admin" -f deployment.yaml > deployment-injected.yaml Alternatively, stdin can be piped to the command: cat deployment.yaml | talosctl inject %[1]s --roles="os:admin" -f - > deployment-injected.yaml `, constants.ServiceAccountResourceSingular, ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { var err error if serviceAccountCmdFlags.file == "" { return cmd.Help() } reader := os.Stdin if serviceAccountCmdFlags.file != "-" { reader, err = os.Open(serviceAccountCmdFlags.file) if err != nil { return err } } injectedYaml, err := inject.ServiceAccount(reader, serviceAccountCmdFlags.roles) if err != nil { return err } fmt.Println(string(injectedYaml)) return nil }, } func init() { serviceAccountCmd.Flags().StringVarP(&serviceAccountCmdFlags.file, "file", "f", "", fmt.Sprintf("file with Kubernetes manifests to be injected with %s", constants.ServiceAccountResourceKind)) serviceAccountCmd.Flags().StringSliceVarP(&serviceAccountCmdFlags.roles, "roles", "r", []string{string(role.Reader)}, fmt.Sprintf("roles to add to the generated %s manifests", constants.ServiceAccountResourceKind)) Cmd.AddCommand(serviceAccountCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/json_logs_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mgmt import ( "bufio" "log" "net" "net/netip" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) var jsonLogsLaunchCmdFlags struct { addr string } // jsonLogsLaunchCmd represents the kms-launch command. var jsonLogsLaunchCmd = &cobra.Command{ Use: "json-logs-launch", Short: "Internal command used by QEMU provisioner", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { lis, err := net.Listen("tcp", jsonLogsLaunchCmdFlags.addr) if err != nil { return err } log.Printf("starting JSON logs server on %s", jsonLogsLaunchCmdFlags.addr) eg, ctx := errgroup.WithContext(cmd.Context()) eg.Go(func() error { for { conn, err := lis.Accept() if err != nil { return err } go func() { defer conn.Close() //nolint:errcheck remoteAddr := conn.RemoteAddr().String() if addr, err := netip.ParseAddrPort(remoteAddr); err == nil { remoteAddr = addr.Addr().String() } scanner := bufio.NewScanner(conn) for scanner.Scan() { log.Printf("%s: %s", remoteAddr, scanner.Text()) } }() } }) eg.Go(func() error { <-ctx.Done() return lis.Close() }) return eg.Wait() }, } func init() { jsonLogsLaunchCmd.Flags().StringVar(&jsonLogsLaunchCmdFlags.addr, "addr", "localhost:3000", "JSON logs listen address") addCommand(jsonLogsLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/kms_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mgmt import ( "context" "errors" "log" "net" "github.com/siderolabs/kms-client/api/kms" "github.com/siderolabs/kms-client/pkg/server" "github.com/spf13/cobra" "go.uber.org/zap" "golang.org/x/sync/errgroup" "google.golang.org/grpc" grpclog "github.com/siderolabs/talos/pkg/grpc/middleware/log" ) var kmsLaunchCmdFlags struct { addr string key []byte } // kmsLaunchCmd represents the kms-launch command. var kmsLaunchCmd = &cobra.Command{ Use: "kms-launch", Short: "Internal command used by QEMU provisioner", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { if kmsLaunchCmdFlags.key == nil { return errors.New("no key provided to the KMS server") } logger, err := zap.NewDevelopment() if err != nil { return err } defer logger.Sync() //nolint:errcheck undo := zap.RedirectStdLog(logger) defer undo() srv := server.NewServer(logger, func(_ context.Context, nodeUUID string) ([]byte, error) { return kmsLaunchCmdFlags.key, nil }) lis, err := net.Listen("tcp", kmsLaunchCmdFlags.addr) if err != nil { return err } logger.Info("starting KMS server", zap.String("address", kmsLaunchCmdFlags.addr)) logMiddleware := grpclog.NewMiddleware(log.New(log.Writer(), "", log.Flags())) s := grpc.NewServer( grpc.UnaryInterceptor(logMiddleware.UnaryInterceptor()), grpc.StreamInterceptor(logMiddleware.StreamInterceptor()), ) kms.RegisterKMSServiceServer(s, srv) eg, ctx := errgroup.WithContext(cmd.Context()) eg.Go(func() error { err := s.Serve(lis) if errors.Is(err, context.Canceled) { return nil } return err }) eg.Go(func() error { <-ctx.Done() s.Stop() return nil }) return eg.Wait() }, } func init() { kmsLaunchCmd.Flags().StringVar(&kmsLaunchCmdFlags.addr, "kms-addr", "localhost", "KMS listen address (IP or host)") kmsLaunchCmd.Flags().BytesBase64Var(&kmsLaunchCmdFlags.key, "kms-key", nil, "KMS key to use") addCommand(kmsLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/loadbalancer_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mgmt import ( "fmt" "slices" "github.com/siderolabs/gen/xiter" "github.com/siderolabs/go-loadbalancer/loadbalancer" "github.com/spf13/cobra" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" ) var loadbalancerLaunchCmdFlags struct { addr string ports []int upstreams []string apidOnlyInitNode bool } var loadbalancerLaunchCmd = &cobra.Command{ Use: "loadbalancer-launch", Short: "Internal command used by QEMU provisioner", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { lb := loadbalancer.TCP{Logger: makeLogger()} for _, port := range loadbalancerLaunchCmdFlags.ports { if err := lb.AddRoute( fmt.Sprintf("%s:%d", loadbalancerLaunchCmdFlags.addr, port), xiter.Map( func(upstream string) string { return fmt.Sprintf("%s:%d", upstream, port) }, slices.Values(loadbalancerLaunchCmdFlags.upstreams), ), ); err != nil { return err } } return lb.Run() }, } func makeLogger() *zap.Logger { config := zap.NewProductionConfig() config.Encoding = "console" config.DisableStacktrace = true return zap.Must(config.Build()) } func init() { loadbalancerLaunchCmd.Flags().StringVar(&loadbalancerLaunchCmdFlags.addr, "loadbalancer-addr", "localhost", "load balancer listen address (IP or host)") loadbalancerLaunchCmd.Flags().IntSliceVar(&loadbalancerLaunchCmdFlags.ports, "loadbalancer-ports", []int{constants.DefaultControlPlanePort}, "load balancer ports") loadbalancerLaunchCmd.Flags().StringSliceVar(&loadbalancerLaunchCmdFlags.upstreams, "loadbalancer-upstreams", []string{}, "load balancer upstreams (nodes to proxy to)") addCommand(loadbalancerLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/machineconfig/gen.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package machineconfig import "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/gen" func init() { // alias for `talosctl gen config` Cmd.AddCommand(gen.NewConfigCmd("gen")) } ================================================ FILE: cmd/talosctl/cmd/mgmt/machineconfig/machineconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package machineconfig import "github.com/spf13/cobra" // Cmd represents the `machineconfig` command. var Cmd = &cobra.Command{ Use: "machineconfig", Short: "Machine config related commands", Aliases: []string{"mc"}, } ================================================ FILE: cmd/talosctl/cmd/mgmt/machineconfig/patch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package machineconfig import ( "fmt" "os" "path/filepath" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" ) var patchCmdFlags struct { patches []string output string } // patchCmd represents the `machineconfig patch` command. var patchCmd = &cobra.Command{ Use: "patch ", Short: "Patch a machine config", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { data, err := os.ReadFile(args[0]) if err != nil { return err } patches, err := configpatcher.LoadPatches(patchCmdFlags.patches) if err != nil { return err } patched, err := configpatcher.Apply(configpatcher.WithBytes(data), patches) if err != nil { return err } patchedData, err := patched.Bytes() if err != nil { return err } if patchCmdFlags.output == "" { // write to stdout fmt.Printf("%s\n", patchedData) return nil } // write to file parentDir := filepath.Dir(patchCmdFlags.output) // Create dir path, ignoring "already exists" messages if err := os.MkdirAll(parentDir, os.ModePerm); err != nil && !os.IsExist(err) { return fmt.Errorf("failed to create output dir: %w", err) } return os.WriteFile(patchCmdFlags.output, patchedData, 0o644) }, } func init() { // use StringArrayVarP instead of StringSliceVarP to prevent cobra from splitting the patch string on commas patchCmd.Flags().StringArrayVarP(&patchCmdFlags.patches, "patch", "p", nil, "patch generated machineconfigs (applied to all node types), use @file to read a patch from file") patchCmd.Flags().StringVarP(&patchCmdFlags.output, "output", "o", "", "output destination. if not specified, output will be printed to stdout") Cmd.AddCommand(patchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/qemu_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package mgmt import ( "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/provision/providers/qemu" ) // qemuLaunchCmd represents the qemu-launch command. var qemuLaunchCmd = &cobra.Command{ Use: "qemu-launch", Short: "Internal command used by QEMU provisioner", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return qemu.Launch() }, } func init() { addCommand(qemuLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mgmt import ( "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/debug" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/gen" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/inject" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/machineconfig" ) // Commands is a list of commands published by the package. var Commands []*cobra.Command // GenV1Alpha1Config generates the Talos config bundle // // Kept with this name in this package for backwards-compatibility. var GenV1Alpha1Config = gen.GenerateConfigBundle func addCommand(cmd *cobra.Command) { Commands = append(Commands, cmd) } func init() { addCommand(cluster.Cmd) addCommand(gen.Cmd) addCommand(debug.Cmd) addCommand(inject.Cmd) addCommand(machineconfig.Cmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/siderolink_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package mgmt import ( "context" "crypto/tls" "fmt" "os" "os/signal" "github.com/siderolabs/siderolink/pkg/agent" "github.com/siderolabs/siderolink/pkg/wireguard" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.uber.org/zap" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) var siderolinkFlags struct { joinToken string wireguardEndpoint string sinkEndpoint string apiEndpoint string apiCertPath string apiKeyPath string logEndpoint string predefinedPairs []string } var siderolinkCmd = &cobra.Command{ Use: "siderolink-launch", Short: "Internal command used by cluster create to launch siderolink agent", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt) defer cancel() return run(ctx) }, } func init() { siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.joinToken, "sidero-link-join-token", "", "join token for the cluster") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.wireguardEndpoint, "sidero-link-wireguard-endpoint", "", "advertised Wireguard endpoint") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.sinkEndpoint, "event-sink-endpoint", "", "gRPC API endpoint for the Event Sink") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.apiEndpoint, "sidero-link-api-endpoint", "", "gRPC API endpoint for the SideroLink") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.apiCertPath, "sidero-link-api-cert", "", "path to the API server certificate (optional)") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.apiKeyPath, "sidero-link-api-key", "", "path to the API server key (optional)") siderolinkCmd.PersistentFlags().StringVar(&siderolinkFlags.logEndpoint, "log-receiver-endpoint", "", "TCP log receiver endpoint") siderolinkCmd.PersistentFlags().StringArrayVar(&siderolinkFlags.predefinedPairs, "predefined-pair", nil, "predefined pairs of UUID=IPv6 addrs for the nodes") siderolinkCmd.PersistentFlags().VisitAll(func(flag *pflag.Flag) { err := siderolinkCmd.PersistentFlags().MarkHidden(flag.Name) if err != nil { panic(err) } }) addCommand(siderolinkCmd) } func run(ctx context.Context) error { logger, err := zap.NewDevelopment() if err != nil { return err } logger.Info("starting embedded siderolink agent") defer logger.Info("stopping embedded siderolink agent") var apiTLSConfig *tls.Config if siderolinkFlags.apiCertPath != "" && siderolinkFlags.apiKeyPath != "" { apiCert, err := tls.LoadX509KeyPair(siderolinkFlags.apiCertPath, siderolinkFlags.apiKeyPath) if err != nil { return fmt.Errorf("failed to load API server certificate: %w", err) } apiTLSConfig = &tls.Config{ Certificates: []tls.Certificate{apiCert}, } } err = agent.Run( ctx, agent.Config{ WireguardEndpoint: siderolinkFlags.wireguardEndpoint, APIEndpoint: siderolinkFlags.apiEndpoint, APITLSConfig: apiTLSConfig, JoinToken: siderolinkFlags.joinToken, SinkEndpoint: siderolinkFlags.sinkEndpoint, LogEndpoint: siderolinkFlags.logEndpoint, UUIDIPv6Pairs: siderolinkFlags.predefinedPairs, ForceUserspace: true, }, &handler{l: logger}, logger, ) if err != nil { return fmt.Errorf("failed to run siderolink agent: %w", err) } return nil } type handler struct { l *zap.Logger } func (h *handler) HandlePeerAdded(event wireguard.PeerEvent) error { h.l.Info("talos agent sees peer added", zap.String("address", event.Address.String())) return nil } func (h *handler) HandlePeerRemoved(wgtypes.Key) error { return nil } ================================================ FILE: cmd/talosctl/cmd/mgmt/validate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mgmt import ( "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) var ( validateConfigArg string validateModeArg string validateStrictArg bool ) // validateCmd reads in a userData file and attempts to parse it. var validateCmd = &cobra.Command{ Use: "validate", Short: "Validate config", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := configloader.NewFromFile(validateConfigArg) if err != nil { return err } mode, err := runtime.ParseMode(validateModeArg) if err != nil { return err } opts := []validation.Option{validation.WithLocal()} if validateStrictArg { opts = append(opts, validation.WithStrict()) } warnings, err := cfg.Validate(mode, opts...) for _, w := range warnings { cli.Warning("%s", w) } if err != nil { return err } fmt.Printf("%s is valid for %s mode\n", validateConfigArg, validateModeArg) return nil }, } func init() { validateCmd.Flags().StringVarP(&validateConfigArg, "config", "c", "", "the path of the config file") validateCmd.Flags().StringVarP( &validateModeArg, "mode", "m", "", fmt.Sprintf("the mode to validate the config for (valid values are %s, %s, and %s)", runtime.ModeMetal.String(), runtime.ModeCloud.String(), runtime.ModeContainer.String()), ) cli.Should(validateCmd.MarkFlagRequired("mode")) validateCmd.Flags().BoolVarP(&validateStrictArg, "strict", "", false, "treat validation warnings as errors") addCommand(validateCmd) } ================================================ FILE: cmd/talosctl/cmd/mgmt/virtiofsd_launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package mgmt import ( "github.com/spf13/cobra" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create/flags" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) var virtiofsdLaunchCmdFlags struct { virtiofsdBin string virtiofs flags.Virtiofs } // virtiofsdLaunchCmd represents the virtiofsd-launch command. var virtiofsdLaunchCmd = &cobra.Command{ Use: "virtiofsd-launch", Short: "Internal command used by VM provisioners", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { eg, ctx := errgroup.WithContext(cmd.Context()) for _, vfs := range virtiofsdLaunchCmdFlags.virtiofs.Requests() { eg.Go(func() error { return vm.Virtiofsd(ctx, virtiofsdLaunchCmdFlags.virtiofsdBin, vfs.SharedDir, vfs.SocketPath) }) } return eg.Wait() }, } func init() { virtiofsdLaunchCmd.Flags().StringVar(&virtiofsdLaunchCmdFlags.virtiofsdBin, "bin", "/usr/libexec/virtiofsd", `path to the virtiofsd binary`) virtiofsdLaunchCmd.Flags().Var(&virtiofsdLaunchCmdFlags.virtiofs, "virtiofs", `list of virtiofs shares to create in format ":"`) addCommand(virtiofsdLaunchCmd) } ================================================ FILE: cmd/talosctl/cmd/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cmd import ( "context" "fmt" "os" "strings" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/cmd/common" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt" "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster" _ "github.com/siderolabs/talos/cmd/talosctl/cmd/mgmt/cluster/create" // import to get the command registered via the init() function. "github.com/siderolabs/talos/cmd/talosctl/cmd/talos" ) // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "talosctl", Short: "A CLI for out-of-band management of Kubernetes nodes created by Talos", Long: ``, SilenceErrors: true, SilenceUsage: true, DisableAutoGenTag: true, } // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() error { cmd, err := rootCmd.ExecuteContextC(context.Background()) if err != nil && !common.SuppressErrors { fmt.Fprintln(os.Stderr, err.Error()) errorString := err.Error() // TODO: this is a nightmare, but arg-flag related validation returns simple `fmt.Errorf`, no way to distinguish // these errors if strings.Contains(errorString, "arg(s)") || strings.Contains(errorString, "flag") || strings.Contains(errorString, "command") { fmt.Fprintln(os.Stderr) fmt.Fprintln(os.Stderr, cmd.UsageString()) } } return err } func init() { const ( talosGroup = "talos" mgmtGroup = "mgmt" clusterGroup = "cluster" ) rootCmd.AddGroup(&cobra.Group{ID: talosGroup, Title: "Manage running Talos clusters:"}) rootCmd.AddGroup(&cobra.Group{ID: mgmtGroup, Title: "Commands to generate and manage machine configuration offline:"}) rootCmd.AddGroup(&cobra.Group{ID: clusterGroup, Title: "Local Talos cluster commands:"}) for _, cmd := range mgmt.Commands { cmd.GroupID = mgmtGroup if cmd == cluster.Cmd { cmd.GroupID = clusterGroup } rootCmd.AddCommand(cmd) } for _, cmd := range talos.Commands { cmd.GroupID = talosGroup rootCmd.AddCommand(cmd) } } ================================================ FILE: cmd/talosctl/cmd/talos/apply-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "strings" "time" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/durationpb" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/constants" ) var applyConfigCmdFlags struct { helpers.Mode certFingerprints []string patches []string filename string insecure bool dryRun bool configTryTimeout time.Duration } // applyConfigCmd represents the applyConfiguration command. var applyConfigCmd = &cobra.Command{ Use: "apply-config", Aliases: []string{"apply"}, Short: "Apply a new configuration to a node", Long: ``, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { var ( cfgBytes []byte err error ) if len(args) > 0 { if args[0] != "config" && !strings.EqualFold(args[0], "machineconfig") { cmd.Help() //nolint:errcheck return fmt.Errorf("unknown positional argument %s", args[0]) } else if cmd.CalledAs() == "apply-config" { cmd.Help() //nolint:errcheck return errors.New("expected no positional arguments") } } if applyConfigCmdFlags.filename == "" { return errors.New("no filename supplied for configuration") } cfgBytes, err = os.ReadFile(applyConfigCmdFlags.filename) if err != nil { return fmt.Errorf("failed to read configuration from %q: %w", applyConfigCmdFlags.filename, err) } if len(cfgBytes) < 1 { return errors.New("no configuration data read") } if len(applyConfigCmdFlags.patches) != 0 { var ( cfg configpatcher.Input patches []configpatcher.Patch ) patches, err = configpatcher.LoadPatches(applyConfigCmdFlags.patches) if err != nil { return err } cfg, err = configpatcher.Apply(configpatcher.WithBytes(cfgBytes), patches) if err != nil { return err } cfgBytes, err = cfg.Bytes() if err != nil { return err } } withClient := func(f func(context.Context, *client.Client) error) error { if applyConfigCmdFlags.insecure { return WithClientMaintenance(applyConfigCmdFlags.certFingerprints, f) } return WithClient(f) } return withClient(func(ctx context.Context, c *client.Client) error { resp, err := c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{ Data: cfgBytes, Mode: applyConfigCmdFlags.Mode.Mode, DryRun: applyConfigCmdFlags.dryRun, TryModeTimeout: durationpb.New(applyConfigCmdFlags.configTryTimeout), }) if err != nil { return fmt.Errorf("error applying new configuration: %s", err) } helpers.PrintApplyResults(resp) return nil }) }, } func init() { applyConfigCmd.Flags().StringVarP(&applyConfigCmdFlags.filename, "file", "f", "", "the filename of the updated configuration") applyConfigCmd.Flags().BoolVarP(&applyConfigCmdFlags.insecure, "insecure", "i", false, "apply the config using the insecure (encrypted with no auth) maintenance service") applyConfigCmd.Flags().BoolVar(&applyConfigCmdFlags.dryRun, "dry-run", false, "check how the config change will be applied in dry-run mode") applyConfigCmd.Flags().StringSliceVar(&applyConfigCmdFlags.certFingerprints, "cert-fingerprint", nil, "list of server certificate fingeprints to accept (defaults to no check)") applyConfigCmd.Flags().StringArrayVarP(&applyConfigCmdFlags.patches, "config-patch", "p", nil, "the list of config patches to apply to the local config file before sending it to the node") applyConfigCmd.Flags().DurationVar(&applyConfigCmdFlags.configTryTimeout, "timeout", constants.ConfigTryTimeout, "the config will be rolled back after specified timeout (if try mode is selected)") helpers.AddModeFlags(&applyConfigCmdFlags.Mode, applyConfigCmd) addCommand(applyConfigCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/bootstrap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "github.com/spf13/cobra" snapshot "go.etcd.io/etcd/etcdutl/v3/snapshot" "github.com/siderolabs/talos/pkg/logging" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var bootstrapCmdFlags struct { recoverFrom string recoverSkipHashCheck bool } // bootstrapCmd represents the bootstrap command. var bootstrapCmd = &cobra.Command{ Use: "bootstrap", Short: "Bootstrap the etcd cluster on the specified node.", Long: `When Talos cluster is created etcd service on control plane nodes enter the join loop waiting to join etcd peers from other control plane nodes. One node should be picked as the bootstrap node. When bootstrap command is issued, the node aborts join process and bootstraps etcd cluster as a single node cluster. Other control plane nodes will join etcd cluster once Kubernetes is bootstrapped on the bootstrap node. This command should not be used when "init" type node are used. Talos etcd cluster can be recovered from a known snapshot with '--recover-from=' flag.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if len(GlobalArgs.Nodes) > 1 { return errors.New("command \"bootstrap\" is not supported with multiple nodes") } if bootstrapCmdFlags.recoverFrom != "" { manager := snapshot.NewV3(logging.Wrap(os.Stderr)) status, err := manager.Status(bootstrapCmdFlags.recoverFrom) if err != nil { return err } fmt.Printf("recovering from snapshot %q: hash %08x, revision %d, total keys %d, total size %d\n", bootstrapCmdFlags.recoverFrom, status.Hash, status.Revision, status.TotalKey, status.TotalSize) snapshot, err := os.Open(bootstrapCmdFlags.recoverFrom) if err != nil { return fmt.Errorf("error opening snapshot file: %w", err) } defer snapshot.Close() //nolint:errcheck _, err = c.EtcdRecover(ctx, snapshot) if err != nil { return fmt.Errorf("error uploading snapshot: %w", err) } } if err := c.Bootstrap(ctx, &machineapi.BootstrapRequest{ RecoverEtcd: bootstrapCmdFlags.recoverFrom != "", RecoverSkipHashCheck: bootstrapCmdFlags.recoverSkipHashCheck, }); err != nil { return fmt.Errorf("error executing bootstrap: %w", err) } return nil }) }, } func init() { bootstrapCmd.Flags().StringVar(&bootstrapCmdFlags.recoverFrom, "recover-from", "", "recover etcd cluster from the snapshot") bootstrapCmd.Flags().BoolVar(&bootstrapCmdFlags.recoverSkipHashCheck, "recover-skip-hash-check", false, "skip integrity check when recovering etcd (use when recovering from data directory copy)") addCommand(bootstrapCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/cgroups.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "archive/tar" "compress/gzip" "context" "fmt" "os" "path/filepath" "slices" "strconv" "strings" "text/tabwriter" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/cgroupsprinter" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/pkg/cgroups" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) var cgroupsCmdFlags struct { schemaFile string presetName string skipCRIResolve bool } // cgroupsCmd represents the cgroups command. var cgroupsCmd = &cobra.Command{ Use: "cgroups", Aliases: []string{"cg"}, Short: "Retrieve cgroups usage information", Long: `The cgroups command fetches control group v2 (cgroupv2) usage details from the machine. Several presets are available to focus on specific cgroup subsystems: * cpu * cpuset * io * memory * process * swap You can specify the preset using the --preset flag. Alternatively, a custom schema can be provided using the --schema-file flag. To see schema examples, refer to https://github.com/siderolabs/talos/tree/main/cmd/talosctl/cmd/talos/cgroupsprinter/schemas. `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "cgroups"); err != nil { return err } var schema cgroupsprinter.Schema switch { case cgroupsCmdFlags.schemaFile != "": in, err := os.Open(cgroupsCmdFlags.schemaFile) if err != nil { return fmt.Errorf("error opening schema file: %w", err) } defer in.Close() //nolint:errcheck if err = yaml.NewDecoder(in).Decode(&schema); err != nil { return fmt.Errorf("error decoding schema file: %w", err) } case cgroupsCmdFlags.presetName != "": presetNames := cgroupsprinter.GetPresetNames() if slices.Index(presetNames, cgroupsCmdFlags.presetName) == -1 { return fmt.Errorf("invalid preset name: %s (valid %v)", cgroupsCmdFlags.presetName, presetNames) } schema = cgroupsprinter.GetPreset(cgroupsCmdFlags.presetName) default: return fmt.Errorf("either schema file or preset must be specified") } if err := schema.Compile(); err != nil { return fmt.Errorf("error compiling schema: %w", err) } processResolveMap := buildProcessResolveMap(ctx, c) devicesResolveMap := buildDevicesResolveMap(ctx, c) r, err := c.Copy(ctx, constants.CgroupMountPath) if err != nil { return fmt.Errorf("error copying: %w", err) } defer r.Close() //nolint:errcheck tree, err := cgroups.TreeFromTarGz(r) if err != nil { return fmt.Errorf("error reading cgroups: %w", err) } if !cgroupsCmdFlags.skipCRIResolve { cgroupNameResolveMap := buildCgroupResolveMap(ctx, c) tree.ResolveNames(cgroupNameResolveMap) } tree.Walk(func(node *cgroups.Node) { node.CgroupProcsResolved = xslices.Map(node.CgroupProcs, func(pid cgroups.Value) cgroups.RawValue { if name, ok := processResolveMap[pid.String()]; ok { return cgroups.RawValue(name) } return cgroups.RawValue(pid.String()) }) for dev := range node.IOStat { if name, ok := devicesResolveMap[dev]; ok { node.IOStat[name] = node.IOStat[dev] delete(node.IOStat, dev) } } }) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) defer w.Flush() //nolint:errcheck headerLine := "NAME\t" + schema.HeaderLine() + "\n" _, err = w.Write([]byte(headerLine)) if err != nil { return fmt.Errorf("error writing header line: %w", err) } return cgroupsprinter.PrintNode(".", w, &schema, tree.Root, nil, 0, nil, false, true) }) }, } func completeCgroupPresetArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return cgroupsprinter.GetPresetNames(), cobra.ShellCompDirectiveNoFileComp } func buildCgroupResolveMap(ctx context.Context, c *client.Client) map[string]string { cgroupNameResolveMap := map[string]string{} containersResp, err := c.Containers(ctx, constants.K8sContainerdNamespace, common.ContainerDriver_CRI) if err != nil { cli.Warning("error getting containers: %s", err) } else { for _, ctr := range containersResp.Messages[0].Containers { if ctr.Uid != "" && ctr.PodId != "" { cgroupNameResolveMap["pod"+ctr.Uid] = ctr.PodId } if ctr.InternalId != "" { if ctr.PodId == ctr.Name { cgroupNameResolveMap[ctr.InternalId] = "sandbox" } else { cgroupNameResolveMap[ctr.InternalId] = ctr.Name } } } } return cgroupNameResolveMap } func buildProcessResolveMap(ctx context.Context, c *client.Client) map[string]string { processResolveMap := map[string]string{} processesResp, err := c.Processes(ctx) if err != nil { cli.Warning("error getting processes: %s", err) return processResolveMap } for _, proc := range processesResp.Messages[0].Processes { name := proc.Executable if name == "" { name = proc.Command } if name == "" { args := strings.Fields(proc.Args) if len(args) > 0 { name = args[0] } } name = filepath.Base(name) processResolveMap[strconv.FormatInt(int64(proc.Pid), 10)] = name } return processResolveMap } func buildDevicesResolveMap(ctx context.Context, c *client.Client) map[string]string { devicesResolveMap := map[string]string{} r, err := c.Copy(ctx, "/sys/dev/block") if err != nil { cli.Warning("error copying devices: %s", err) return devicesResolveMap } defer r.Close() //nolint:errcheck gzR, err := gzip.NewReader(r) if err != nil { cli.Warning("error reading devices: %s", err) return devicesResolveMap } defer gzR.Close() //nolint:errcheck tarR := tar.NewReader(gzR) for { header, err := tarR.Next() if err != nil { break } if header.Typeflag != tar.TypeSymlink { continue } devicesResolveMap[header.Name] = filepath.Base(header.Linkname) } return devicesResolveMap } func init() { presetNames := cgroupsprinter.GetPresetNames() cgroupsCmd.Flags().StringVar(&cgroupsCmdFlags.schemaFile, "schema-file", "", "path to the columns schema file") cgroupsCmd.Flags().StringVar(&cgroupsCmdFlags.presetName, "preset", "", fmt.Sprintf("preset name (one of: %v)", presetNames)) cgroupsCmd.Flags().BoolVar(&cgroupsCmdFlags.skipCRIResolve, "skip-cri-resolve", false, "do not resolve cgroup names via a request to CRI") cgroupsCmd.MarkFlagsMutuallyExclusive("schema-file", "preset") cgroupsCmd.RegisterFlagCompletionFunc("preset", completeCgroupPresetArg) //nolint:errcheck addCommand(cgroupsCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/cpu.yaml ================================================ # Basic CPU metrics columns: - name: CpuWeight template: '{{ .CPUWeight | printf "%6s" }}' - name: CpuNice template: '{{ .CPUWeightNice | printf "%6s" }}' - name: CpuMax template: '{{ .CPUMax | printf "%6s" }}' - name: CpuUser template: '{{ .CPUStat.user_usec.UsecToDuration | printf "%12s" }}' - name: User/% template: '{{ if .Parent }}{{ .CPUStat.user_usec.DivideBy .Parent.CPUStat.user_usec | printf "%6s" }}%{{ else }}-{{ end }}' - name: CpuSystem template: '{{ .CPUStat.system_usec.UsecToDuration | printf "%12s" }}' - name: System/% template: '{{ if .Parent }}{{ .CPUStat.system_usec.DivideBy .Parent.CPUStat.system_usec | printf "%6s" }}%{{ else }}-{{ end }}' - name: Throttled template: '{{ .CPUStat.throttled_usec.UsecToDuration | printf "%12s" }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/cpuset.yaml ================================================ columns: - name: CpuSet template: '{{ .CPUSetCPUs | printf "%12s" }}' - name: CpuSet(Eff) template: '{{ .CPUSetCPUsEffective | printf "%12s" }}' - name: Mems template: '{{ .CPUSetMems | printf "%12s" }}' - name: Mems(Eff) template: '{{ .CPUSetMemsEffective | printf "%12s" }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/io.yaml ================================================ columns: - name: Bytes Read/Written template: '{{ range $disk, $v := .IOStat }}{{ if $v }}{{ $disk }}: {{ $v.rbytes.HumanizeIBytes }}/{{ $v.wbytes.HumanizeIBytes }} {{ end }}{{ end }}' - name: ios Read/Write template: '{{ if .Parent }}{{ range $disk, $v := .IOStat }}{{ $disk }}: {{ $v.rios }}/{{ $v.wios }} {{ end }}{{ end }}' - name: PressAvg10 template: '{{ .IOPressure.some.avg10 | printf "%6s" }}' - name: PressAvg60 template: '{{ .IOPressure.some.avg60 | printf "%6s" }}' - name: PressTotal template: '{{ .IOPressure.some.total.UsecToDuration | printf "%12s" }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/memory.yaml ================================================ # Memory-related cgroup metrics columns: - name: MemCurrent template: '{{ .MemoryCurrent.HumanizeIBytes | printf "%8s" }}' - name: MemPeak template: '{{ .MemoryPeak.HumanizeIBytes | printf "%8s" }}' - name: MemLow template: '{{ .MemoryLow.HumanizeIBytes | printf "%8s" }}' - name: Peak/Low template: '{{ .MemoryPeak.DivideBy .MemoryLow | printf "%6s%%" }}' - name: MemHigh template: '{{ .MemoryHigh.HumanizeIBytes | printf "%8s" }}' - name: MemMin template: '{{ .MemoryMin.HumanizeIBytes | printf "%8s" }}' - name: Current/Min template: '{{ .MemoryCurrent.DivideBy .MemoryMin | printf "%6s%%" }}' - name: MemMax template: '{{ .MemoryMax.HumanizeIBytes | printf "%8s" }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/process.yaml ================================================ columns: - name: PidsCurrent template: '{{ .PidsCurrent | printf "%8s" }}' - name: PidsPeak template: '{{ .PidsPeak | printf "%8s" }}' - name: PidsMax template: '{{ .PidsMax | printf "%8s" }}' - name: Procs template: '{{ .CgroupProcs | len | printf "%7d" }}' - name: Threads template: '{{ .CgroupThreads | len | printf "%7d" }}' - name: Processes template: '{{ if .Parent }}{{ .CgroupProcsResolved | printf "%s" | printf "%.50s" }}{{ end }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/psi.yaml ================================================ # PSI-related cgroup metrics columns: - name: MemCurrent template: '{{ .MemoryCurrent.HumanizeIBytes | printf "%8s" }}' - name: MemPsiSome10 template: '{{ .MemoryPressure.some.avg10 | printf "%6s" }}' - name: MemPsi10 template: '{{ .MemoryPressure.full.avg10 | printf "%6s" }}' - name: CpuPsi10 template: '{{ .CPUPressure.full.avg10 | printf "%6s" }}' - name: IoPsi10 template: '{{ .IOPressure.full.avg10 | printf "%6s" }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets/swap.yaml ================================================ columns: - name: SwapCurrent template: '{{ .MemorySwapCurrent.HumanizeIBytes | printf "%8s" }}' - name: SwapPeak template: '{{ .MemorySwapPeak.HumanizeIBytes | printf "%8s" }}' - name: SwapHigh template: '{{ .MemorySwapHigh.HumanizeIBytes | printf "%8s" }}' - name: SwapMax template: '{{ .MemorySwapMax.HumanizeIBytes | printf "%8s" }}' - name: ZswapCurrent template: '{{ .MemoryZswapCurrent.HumanizeIBytes | printf "%8s" }}' - name: ZswapMax template: '{{ .MemoryZswapMax.HumanizeIBytes | printf "%8s" }}' - name: ZswapWriteback template: '{{ .MemoryZswapWriteback }}' ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroupsprinter import ( "embed" "io/fs" "path" "path/filepath" "slices" "strings" "github.com/siderolabs/gen/xslices" "go.yaml.in/yaml/v4" ) //go:embed presets/*.yaml var presetsFS embed.FS // GetPresetNames returns the list of preset names. func GetPresetNames() []string { list, err := presetsFS.ReadDir("presets") if err != nil { panic(err) // should not fail } presets := xslices.Map(list, func(dirEntry fs.DirEntry) string { // cut extension return strings.TrimSuffix(dirEntry.Name(), filepath.Ext(dirEntry.Name())) }) slices.Sort(presets) return presets } // GetPreset returns the preset by name. func GetPreset(name string) Schema { // embed.FS always uses / as separator, even on Windows, we need OS-agnostic path joining here f, err := presetsFS.Open(path.Join("presets", name+".yaml")) if err != nil { panic(err) // should not fail } defer f.Close() //nolint:errcheck var schema Schema if err := yaml.NewDecoder(f).Decode(&schema); err != nil { panic(err) // should not fail } if err := schema.Compile(); err != nil { panic(err) // should not fail } return schema } ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/presets_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroupsprinter_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/cgroupsprinter" ) func TestGetPresetNames(t *testing.T) { assert.Equal(t, []string{"cpu", "cpuset", "io", "memory", "process", "psi", "swap"}, cgroupsprinter.GetPresetNames()) } func TestGetPreset(t *testing.T) { for _, name := range cgroupsprinter.GetPresetNames() { t.Run(name, func(t *testing.T) { assert.NotEmpty(t, cgroupsprinter.GetPreset(name)) }) } } ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/schema.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroupsprinter import ( "io" "strings" "text/template" ) // Schema defines columns for cgroups printer. type Schema struct { Columns []*Column `yaml:"columns"` } // Compile compiles the templates. func (s *Schema) Compile() error { for _, c := range s.Columns { tmpl, err := template.New(c.Name).Parse(c.Template) if err != nil { return err } c.tmpl = tmpl } return nil } // HeaderLine returns the header line. func (s *Schema) HeaderLine() string { var headerLine strings.Builder for i, c := range s.Columns { if i > 0 { headerLine.WriteString("\t") } headerLine.WriteString(c.Name) } return headerLine.String() } // Render returns the row line. func (s *Schema) Render(data any) (string, error) { var rowLine strings.Builder for i, c := range s.Columns { if i > 0 { rowLine.WriteString("\t") } if err := c.Render(&rowLine, data); err != nil { return "", err } } return rowLine.String(), nil } // Column defines a column for cgroups printer. type Column struct { Name string `yaml:"name"` Template string `yaml:"template"` // Template is a Go template string. tmpl *template.Template } // Render the template with the data. func (c *Column) Render(out io.Writer, data any) error { if c.tmpl == nil { panic("template is not compiled") } return c.tmpl.Execute(out, data) } ================================================ FILE: cmd/talosctl/cmd/talos/cgroupsprinter/tree.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cgroupsprinter provides functions to print cgroup information. package cgroupsprinter import ( "fmt" "io" "slices" "strings" "github.com/siderolabs/talos/internal/pkg/cgroups" ) type edgeType string const ( edgeTypeNone edgeType = "" edgeTypeLink edgeType = "│" edgeTypeMid edgeType = "├──" edgeTypeEnd edgeType = "└──" indentSize = 3 ) // PrintNode prints the cgroup node recursively. // //nolint:gocyclo func PrintNode(name string, w io.Writer, schema *Schema, node, parent *cgroups.Node, level int, levelsEnded []int, lastNode, treeRoot bool) error { var prefix strings.Builder for i := range level { if slices.Index(levelsEnded, i) != -1 { prefix.WriteString(strings.Repeat(" ", indentSize+1)) } else { prefix.WriteString(string(edgeTypeLink) + strings.Repeat(" ", indentSize)) } } var edge edgeType switch { case treeRoot: edge = edgeTypeNone case lastNode: edge = edgeTypeEnd default: edge = edgeTypeMid } rowData, err := schema.Render(struct { *cgroups.Node Parent *cgroups.Node }{ Node: node, Parent: parent, }) if err != nil { return err } _, err = fmt.Fprintf(w, "%s%s%s\t%s\n", prefix.String(), edge, name, rowData) if err != nil { return err } children := node.SortedChildren() if lastNode { levelsEnded = append(levelsEnded, level) } if !treeRoot { level++ } for i, child := range children { last := i == len(children)-1 if err = PrintNode(child, w, schema, node.Children[child], node, level, levelsEnded, last, false); err != nil { return err } } return nil } ================================================ FILE: cmd/talosctl/cmd/talos/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bytes" "context" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "errors" "fmt" "os" "slices" "strings" "text/tabwriter" "text/template" "time" "github.com/dustin/go-humanize" "github.com/ryanuber/go-glob" "github.com/siderolabs/gen/maps" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "google.golang.org/protobuf/types/known/durationpb" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) // configCmd represents the config command. var configCmd = &cobra.Command{ Use: "config", Short: "Manage the client configuration file (talosconfig)", Long: ``, } func openConfigAndContext(context string) (*clientconfig.Config, error) { c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return nil, fmt.Errorf("error reading config: %w", err) } if context == "" { context = c.Context } if context == "" { return nil, errors.New("no context is set") } if _, ok := c.Contexts[context]; !ok { return nil, fmt.Errorf("context %q is not defined", context) } return c, nil } // configEndpointCmd represents the `config endpoint` command. var configEndpointCmd = &cobra.Command{ Use: "endpoint ...", Aliases: []string{"endpoints"}, Short: "Set the endpoint(s) for the current context", Long: ``, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { c, err := openConfigAndContext("") if err != nil { return err } for i := range args { args[i] = strings.TrimSpace(args[i]) } ctxData, err := getContextData(c) if err != nil { return err } ctxData.Endpoints = args if err := c.Save(GlobalArgs.Talosconfig); err != nil { return fmt.Errorf("error writing config: %w", err) } return nil }, } // configNodeCmd represents the `config node` command. var configNodeCmd = &cobra.Command{ Use: "node ...", Aliases: []string{"nodes"}, Short: "Set the node(s) for the current context", Long: ``, Args: cobra.ArbitraryArgs, RunE: func(cmd *cobra.Command, args []string) error { c, err := openConfigAndContext("") if err != nil { return err } for i := range args { args[i] = strings.TrimSpace(args[i]) } ctxData, err := getContextData(c) if err != nil { return err } ctxData.Nodes = args if err := c.Save(GlobalArgs.Talosconfig); err != nil { return fmt.Errorf("error writing config: %w", err) } return nil }, } // configContextCmd represents the `config context` command. var configContextCmd = &cobra.Command{ Use: "context ", Short: "Set the current context", Aliases: []string{"use-context"}, Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { context := args[0] c, err := openConfigAndContext(context) if err != nil { return err } c.Context = context if err := c.Save(GlobalArgs.Talosconfig); err != nil { return fmt.Errorf("error writing config: %s", err) } return nil }, ValidArgsFunction: CompleteConfigContext, } // configAddCmdFlags represents the `config add` command flags. var configAddCmdFlags struct { ca string crt string key string } // configAddCmd represents the `config add` command. var configAddCmd = &cobra.Command{ Use: "add ", Short: "Add a new context", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { context := args[0] c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return fmt.Errorf("error reading config: %w", err) } newContext := &clientconfig.Context{} if configAddCmdFlags.ca != "" { var caBytes []byte caBytes, err = os.ReadFile(configAddCmdFlags.ca) if err != nil { return fmt.Errorf("error reading CA: %w", err) } newContext.CA = base64.StdEncoding.EncodeToString(caBytes) } err = checkAndSetCrtAndKey(newContext) if err != nil { return err } if c.Contexts == nil { c.Contexts = map[string]*clientconfig.Context{} } c.Contexts[context] = newContext if err := c.Save(GlobalArgs.Talosconfig); err != nil { return fmt.Errorf("error writing config: %w", err) } return nil }, } // configRemoveCmdFlags represents the `config remove` command flags. var configRemoveCmdFlags struct { noconfirm bool dry bool } // configRemoveCmd represents the `config remove` command. var configRemoveCmd = &cobra.Command{ Use: "remove ", Short: "Remove contexts", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { pattern := args[0] if pattern == "" { return errors.New("no context specified") } c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return fmt.Errorf("error reading config: %w", err) } if len(c.Contexts) == 0 { return errors.New("no contexts defined") } matches := sortInPlace(maps.Keys( maps.Filter(c.Contexts, func(context string, _ *clientconfig.Context) bool { return glob.Glob(pattern, context) }), )) if len(matches) == 0 { return fmt.Errorf("no contexts matched %q", pattern) } // we want to prevent file updates in case there were no changes noChanges := true for _, match := range matches { if match == c.Context { fmt.Fprintf( os.Stderr, "skipping removal of current context %q, please change it to another before removing\n", match, ) continue } if !configRemoveCmdFlags.noconfirm { prompt := fmt.Sprintf("remove context %q", match) if !helpers.Confirm(prompt + "?") { continue } } else { fmt.Fprintf(os.Stderr, "removing context %q\n", match) } noChanges = false delete(c.Contexts, match) } if configRemoveCmdFlags.dry || noChanges { return nil } err = c.Save(GlobalArgs.Talosconfig) if err != nil { return fmt.Errorf("error writing config: %w", err) } return nil }, ValidArgsFunction: CompleteConfigContext, } func sortInPlace(slc []string) []string { slices.Sort(slc) return slc } func checkAndSetCrtAndKey(configContext *clientconfig.Context) error { crt := configAddCmdFlags.crt key := configAddCmdFlags.key if crt == "" && key == "" { return nil } if crt == "" || key == "" { return errors.New("if either the 'crt' or 'key' flag is specified, both are required") } crtBytes, err := os.ReadFile(crt) if err != nil { return fmt.Errorf("error reading certificate: %w", err) } configContext.Crt = base64.StdEncoding.EncodeToString(crtBytes) keyBytes, err := os.ReadFile(key) if err != nil { return fmt.Errorf("error reading key: %w", err) } configContext.Key = base64.StdEncoding.EncodeToString(keyBytes) return nil } // configGetContextsCmd represents the `config contexts` command. var configGetContextsCmd = &cobra.Command{ Use: "contexts", Short: "List defined contexts", Aliases: []string{"get-contexts"}, Long: ``, RunE: func(cmd *cobra.Command, args []string) error { c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return fmt.Errorf("error reading config: %w", err) } keys := maps.Keys(c.Contexts) slices.Sort(keys) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "CURRENT\tNAME\tENDPOINTS\tNODES") for _, name := range keys { context := c.Contexts[name] var ( current string endpoints string nodes string ) if name == c.Context { current = "*" } endpoints = strings.Join(context.Endpoints, ",") if len(context.Nodes) > 3 { nodes = strings.Join(context.Nodes[:3], ",") nodes += "..." } else { nodes = strings.Join(context.Nodes, ",") } fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", current, name, endpoints, nodes) } return w.Flush() }, } // configMergeCmd represents the `config merge` command. var configMergeCmd = &cobra.Command{ Use: "merge ", Short: "Merge additional contexts from another client configuration file", Long: "Contexts with the same name are renamed while merging configs.", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { from := args[0] c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return fmt.Errorf("error reading config: %w", err) } secondConfig, err := clientconfig.Open(from) if err != nil { return fmt.Errorf("error reading config: %w", err) } renames := c.Merge(secondConfig) for _, rename := range renames { fmt.Fprintf(os.Stderr, "renamed talosconfig context %s\n", rename.String()) } if err := c.Save(GlobalArgs.Talosconfig); err != nil { return fmt.Errorf("error writing config: %s", err) } return nil }, } // configNewCmdFlags represents the `config new` command flags. var configNewCmdFlags struct { roles []string crtTTL time.Duration } // configNewCmd represents the `config new` command. var configNewCmd = &cobra.Command{ Use: "new []", Short: "Generate a new client configuration file", Args: cobra.RangeArgs(0, 1), RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { args = []string{"talosconfig"} } path := args[0] return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "talosconfig"); err != nil { return err } roles, unknownRoles := role.Parse(configNewCmdFlags.roles) if len(unknownRoles) != 0 { return fmt.Errorf("unknown roles: %s", strings.Join(unknownRoles, ", ")) } if _, err := os.Stat(path); err == nil { return fmt.Errorf("talosconfig file already exists: %q", path) } resp, err := c.GenerateClientConfiguration(ctx, &machineapi.GenerateClientConfigurationRequest{ Roles: roles.Strings(), CrtTtl: durationpb.New(configNewCmdFlags.crtTTL), }) if err != nil { return err } if l := len(resp.Messages); l != 1 { panic(fmt.Sprintf("expected 1 message, got %d", l)) } config, err := clientconfig.FromBytes(resp.Messages[0].Talosconfig) if err != nil { return err } // make the new config immediately useful config.Contexts[config.Context].Endpoints = c.GetEndpoints() return config.Save(path) }) }, } // configNewCmd represents the `config info` command output template. var configInfoCmdTemplate = template.Must(template.New("configInfoCmdTemplate"). Funcs(template.FuncMap{"join": strings.Join}). Option("missingkey=error"). Parse(strings.TrimSpace(` Current context: {{ .Context }} Nodes: {{ if .Nodes }}{{ join .Nodes ", " }}{{ else }}not defined{{ end }} Endpoints: {{ if .Endpoints }}{{ join .Endpoints ", " }}{{ else }}not defined{{ end }} {{- if .Roles }} Roles: {{ join .Roles ", " }}{{ end }} {{- if .CertTTL }} Certificate expires: {{ .CertTTL }} ({{ .CertNotAfter }}){{ end }} `))) type talosconfigInfo struct { Context string `json:"context" yaml:"context"` Nodes []string `json:"nodes" yaml:"nodes"` Endpoints []string `json:"endpoints" yaml:"endpoints"` Roles []string `json:"roles" yaml:"roles"` CertTTL string `json:"certTTL" yaml:"certTTL"` CertNotAfter string `json:"certNotAfter" yaml:"certNotAfter"` } // configInfo returns talosct config info. func configInfo(config *clientconfig.Config, now time.Time) (talosconfigInfo, error) { cfgContext, err := getContextData(config) if err != nil { return talosconfigInfo{}, err } var ( certTTL, certNotAfter string roles role.Set ) if cfgContext.Crt != "" { var b []byte b, err = base64.StdEncoding.DecodeString(cfgContext.Crt) if err != nil { return talosconfigInfo{}, err } block, _ := pem.Decode(b) if block == nil { return talosconfigInfo{}, errors.New("error decoding PEM") } var crt *x509.Certificate crt, err = x509.ParseCertificate(block.Bytes) if err != nil { return talosconfigInfo{}, err } roles, _ = role.Parse(crt.Subject.Organization) certTTL = humanize.RelTime(crt.NotAfter, now, "ago", "from now") certNotAfter = crt.NotAfter.UTC().Format("2006-01-02") } return talosconfigInfo{ Context: config.Context, Nodes: cfgContext.Nodes, Endpoints: cfgContext.Endpoints, Roles: roles.Strings(), CertTTL: certTTL, CertNotAfter: certNotAfter, }, nil } // configInfoCommand implements `config info` command logic. func configInfoCommand(config *clientconfig.Config, now time.Time) (string, error) { info, err := configInfo(config, now) if err != nil { return "", err } var res bytes.Buffer err = configInfoCmdTemplate.Execute(&res, info) return res.String() + "\n", err } var configInfoCmdFlags struct { output string } // configInfoCmd represents the `config info` command. var configInfoCmd = &cobra.Command{ Use: "info", Short: "Show information about the current context", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { c, err := openConfigAndContext("") if err != nil { return err } switch configInfoCmdFlags.output { case "text": res, err := configInfoCommand(c, time.Now()) if err != nil { return err } fmt.Print(res) return nil case "json": info, err := configInfo(c, time.Now()) if err != nil { return err } enc := json.NewEncoder(os.Stdout) enc.SetIndent("", " ") return enc.Encode(&info) case "yaml": info, err := configInfo(c, time.Now()) if err != nil { return err } return yaml.NewEncoder(os.Stdout).Encode(&info) default: return fmt.Errorf("unknown output format: %q", configInfoCmdFlags.output) } }, } // CompleteConfigContext represents tab completion for `--context` // argument and `config [context|remove]` command. func CompleteConfigContext(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { c, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return nil, cobra.ShellCompDirectiveError } contextnames := maps.Keys(c.Contexts) slices.Sort(contextnames) return contextnames, cobra.ShellCompDirectiveNoFileComp } func init() { configCmd.AddCommand( configEndpointCmd, configNodeCmd, configContextCmd, configAddCmd, configRemoveCmd, configGetContextsCmd, configMergeCmd, configNewCmd, configInfoCmd, ) configAddCmd.Flags().StringVar(&configAddCmdFlags.ca, "ca", "", "the path to the CA certificate") configAddCmd.Flags().StringVar(&configAddCmdFlags.crt, "crt", "", "the path to the certificate") configAddCmd.Flags().StringVar(&configAddCmdFlags.key, "key", "", "the path to the key") configRemoveCmd.Flags().BoolVarP( &configRemoveCmdFlags.noconfirm, "noconfirm", "y", false, "do not ask for confirmation", ) configRemoveCmd.Flags().BoolVar( &configRemoveCmdFlags.dry, "dry-run", false, "dry run", ) configNewCmd.Flags().StringSliceVar(&configNewCmdFlags.roles, "roles", role.MakeSet(role.Admin).Strings(), "roles") configNewCmd.Flags().DurationVar(&configNewCmdFlags.crtTTL, "crt-ttl", constants.TalosAPIDefaultCertificateValidityDuration, "certificate TTL") configInfoCmd.Flags().StringVarP(&configInfoCmdFlags.output, "output", "o", "text", "output format (json|yaml|text). Default text.") addCommand(configCmd) } func getContextData(c *clientconfig.Config) (*clientconfig.Context, error) { contextName := c.Context if GlobalArgs.CmdContext != "" { contextName = GlobalArgs.CmdContext } ctxData, ok := c.Contexts[contextName] if !ok { return nil, fmt.Errorf("context %q is not defined", contextName) } return ctxData, nil } ================================================ FILE: cmd/talosctl/cmd/talos/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos //nolint:testpackage // to test unexported function import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) func TestConfigInfoCommand(t *testing.T) { t.Parallel() now := time.Date(2021, 7, 5, 22, 6, 42, 0, time.UTC) //nolint:lll testCases := []struct { name string config string expected string }{ { name: "Default", config: ` context: no-roles contexts: no-roles: endpoints: - 172.20.1.2 - 172.20.1.3 - 172.20.1.4 nodes: - 172.20.1.2 ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJNakNCNWFBREFnRUNBaEFyalRDdnc3TElYZVRPTjFSTnhqeFNNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRFNE16TmFGdzB6TVRBM01ETXhNVEU0TXpOYU1CTXhFVEFQQmdOVgpCQW9UQ0c5ek9tRmtiV2x1TUNvd0JRWURLMlZ3QXlFQTI2bDQ0eU1ZRTAvZUVUVEtsQXBTZGhlMEgzOEhGRDBnClh1Q2VFdWZ0YnZpalVqQlFNQTRHQTFVZER3RUIvd1FFQXdJSGdEQWRCZ05WSFNVRUZqQVVCZ2dyQmdFRkJRY0QKQVFZSUt3WUJCUVVIQXdJd0h3WURWUjBqQkJnd0ZvQVVjcnZXdVBrMDJWbkJHNVZhbGJ5Y0ptZjY5cVV3QlFZRApLMlZ3QTBFQXhlN3Qrb2tZUURoNlZOQ0ZLenlqTmVuWkhmQ1MrRTdFdUYyVC9kcjRkU3JXcko2eXYvaE5ZalJqCnhNb0grU3dLak5kU2trNnJhWHdWb0ZwY3JraWRBdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJQWhmdzFISzBKVFgzVjh2K09wZ2J5dXQ2VzN0OWhVaGczQW5pK043WTFTNAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K `, expected: strings.TrimSpace(` Current context: no-roles Nodes: 172.20.1.2 Endpoints: 172.20.1.2, 172.20.1.3, 172.20.1.4 Roles: os:admin Certificate expires: 10 years from now (2031-07-03) `) + "\n", }, { name: "NoRoles", config: ` context: no-roles contexts: no-roles: endpoints: - 172.20.1.2 ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJIekNCMHFBREFnRUNBaEFpcVA1MjN0NkJWNWZmNTVhUlBOWW1NQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRNeE5UWmFGdzB6TVRBM01ETXhNVE14TlRaYU1BQXdLakFGQmdNcgpaWEFESVFCNTlmL2h0MG1tOUJqL3I4b0VsdjFUU3VVcG5kazlwang3Mm10MUpxZGEyNk5TTUZBd0RnWURWUjBQCkFRSC9CQVFEQWdlQU1CMEdBMVVkSlFRV01CUUdDQ3NHQVFVRkJ3TUJCZ2dyQmdFRkJRY0RBakFmQmdOVkhTTUUKR0RBV2dCUnl1OWE0K1RUWldjRWJsVnFWdkp3bVovcjJwVEFGQmdNclpYQURRUUNXUnAzcHB6YkM5ZzlmWC9RRgp0ZGg1eFY2YVYvdTVVdTFkU05TNmQ5VFFlUE00c1NXL1U5UGViNEpvK3Uzd1lPblBlb3huSWoxNzJOdTBQTm81CldPa0MKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJREJybmN1UEV2RHFPRjhqWWJMNUxvNWhSUkZ2cXBPVWZud2RMOHRPdzdFRgotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K `, expected: strings.TrimSpace(` Current context: no-roles Nodes: not defined Endpoints: 172.20.1.2 Certificate expires: 10 years from now (2031-07-03) `) + "\n", }, { name: "FutureRole", config: ` context: future-role contexts: future-role: ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBblpENTFNNW0zUmNMNUZwWXVYUkRWVEFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qRXdOekExTVRFeE9ETXpXaGNOTXpFd056QXpNVEV4T0RNeldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUFPYU93TVBaaDNHaE9kd2ZtSjYxcUhheUxDakFzdGcrWlNJCmpLbVV0WXR2bzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkhLNzFyajVOTmxad1J1VgpXcFc4bkNabit2YWxNQVVHQXl0bGNBTkJBTjlMQ2d2RzltSDdka3RSRTVQTFJJT25VcTNORnZaMTl1SHF5bWttCjJwQVR2SU02cXpMS0x0NXUyWEtBVFUzcDZDQWFGVVpOMkJhVjV0amVTeXZSWkFzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJNekNCNXFBREFnRUNBaEFQL1MrVGx0WTBHdGk5Q1g0UDNVZStNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTNNRFV4TVRRek1qRmFGdzB6TVRBM01ETXhNVFF6TWpGYU1CUXhFakFRQmdOVgpCQW9UQ1c5ek9tWjFkSFZ5WlRBcU1BVUdBeXRsY0FNaEFLck04NmtPYm1MdGw5OVdpdzFFL29pdnl2YXVqVmNkCmlQTk82TVhQNGxEMm8xSXdVREFPQmdOVkhROEJBZjhFQkFNQ0I0QXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUgKQXdFR0NDc0dBUVVGQndNQ01COEdBMVVkSXdRWU1CYUFGSEs3MXJqNU5ObFp3UnVWV3BXOG5DWm4rdmFsTUFVRwpBeXRsY0FOQkFDSno3NTlGeVFJMXlIWTJNRG9vZDNrZjdZeG9HRG1Hem1nNllqRHJueXAxWGpaQ3o1Q1RQbm9jCjhxWlAyTXE5MDJnOXZSSUh1dm84N0NIZjJacTNGZ1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJS1RCRDIyZDBLVnNTek5iSkNBdjNObUVnL1VWOTk4SHZvY2NGb1lDOEJ1bAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K `, expected: strings.TrimSpace(` Current context: future-role Nodes: not defined Endpoints: not defined Roles: os:future Certificate expires: 10 years from now (2031-07-03) `) + "\n", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { t.Parallel() config, err := clientconfig.FromString(tc.config) require.NoError(t, err) actual, err := configInfoCommand(config, now) assert.NoError(t, err) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: cmd/talosctl/cmd/talos/conformance.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/cluster/hydrophone" "github.com/siderolabs/talos/pkg/machinery/client" ) // conformanceCmd represents the conformance command. var conformanceCmd = &cobra.Command{ Use: "conformance", Short: "Run conformance tests", Long: ``, } var conformanceKubernetesCmdFlags struct { mode string } var conformanceKubernetesCmd = &cobra.Command{ Use: "kubernetes", Aliases: []string{"k8s"}, Short: "Run Kubernetes conformance tests", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { clientProvider := &cluster.ConfigClientProvider{ DefaultClient: c, } defer clientProvider.Close() //nolint:errcheck state := struct { cluster.K8sProvider }{ K8sProvider: &cluster.KubernetesClient{ ClientProvider: clientProvider, ForceEndpoint: healthCmdFlags.forceEndpoint, }, } switch conformanceKubernetesCmdFlags.mode { case "fast": return hydrophone.FastConformance(ctx, &state) case "certified": return hydrophone.CertifiedConformance(ctx, &state) case "network-policy": return hydrophone.NetworkPolicies(ctx, &state) default: return fmt.Errorf("unsupported conformance mode %v", conformanceKubernetesCmdFlags.mode) } }) }, } func init() { conformanceKubernetesCmd.Flags().StringVar(&conformanceKubernetesCmdFlags.mode, "mode", "fast", "conformance test mode: [fast, certified, network-policy]") conformanceCmd.AddCommand(conformanceKubernetesCmd) addCommand(conformanceCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/containers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "slices" "strings" "text/tabwriter" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // containersCmd represents the processes command. var containersCmd = &cobra.Command{ Use: "containers", Aliases: []string{"c"}, Short: "List containers", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var ( namespace string driver common.ContainerDriver ) if kubernetesFlag { namespace = constants.K8sContainerdNamespace driver = common.ContainerDriver_CRI } else { namespace = constants.SystemContainerdNamespace driver = common.ContainerDriver_CONTAINERD } var remotePeer peer.Peer resp, err := c.Containers(ctx, namespace, driver, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error getting container list: %s", err) } cli.Warning("%s", err) } return containerRender(&remotePeer, resp) }) }, } func containerRender(remotePeer *peer.Peer, resp *machineapi.ContainersResponse) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tIMAGE\tPID\tSTATUS") defaultNode := client.AddrFromPeer(remotePeer) for _, msg := range resp.Messages { slices.SortFunc(msg.Containers, func(a, b *machineapi.ContainerInfo) int { return strings.Compare(a.Id, b.Id) }) for _, p := range msg.Containers { display := p.Id if p.Id != p.PodId { // container in a sandbox display = "└─ " + display } node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\t%s\n", node, p.Namespace, display, p.Image, p.Pid, p.Status) } } return w.Flush() } func init() { containersCmd.Flags().BoolVarP(&kubernetesFlag, "kubernetes", "k", false, "use the k8s.io containerd namespace") containersCmd.Flags().Bool("use-cri", false, "use the CRI driver") containersCmd.Flags().MarkHidden("use-cri") //nolint:errcheck addCommand(containersCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/copy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "io" "io/fs" "os" "path/filepath" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/client" ) // cpCmd represents the cp command. var cpCmd = &cobra.Command{ Use: "copy -|", Aliases: []string{"cp"}, Short: "Copy data out from the node", Long: `Creates an .tar.gz archive at the node starting at and streams it back to the client. If '-' is given for , archive is written to stdout. Otherwise archive is extracted to which should be an empty directory or talosctl creates a directory if doesn't exist. Command doesn't preserve ownership and access mode for the files in extract mode, while streamed .tar archive captures ownership and permission bits.`, Args: cobra.ExactArgs(2), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { switch len(args) { case 0: return completePathFromNode(toComplete), cobra.ShellCompDirectiveNoFileComp case 1: return nil, cobra.ShellCompDirectiveDefault } return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "copy"); err != nil { return err } r, err := c.Copy(ctx, args[0]) if err != nil { return fmt.Errorf("error copying: %w", err) } localPath := args[1] if localPath == "-" { _, err = io.Copy(os.Stdout, r) return err } localPath = filepath.Clean(localPath) fi, err := os.Stat(localPath) if err == nil && !fi.IsDir() { return fmt.Errorf("local path %q should be a directory", args[1]) } if err != nil { if !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("failed to stat local path: %w", err) } if err = os.MkdirAll(localPath, 0o777); err != nil { return fmt.Errorf("error creating local path %q: %w", localPath, err) } } return helpers.ExtractTarGz(localPath, r) }) }, } func init() { addCommand(cpCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/crashdump.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/client" ) var crashdumpCmdFlags struct { clusterState clusterNodes } // crashdumpCmd represents the crashdump command. var crashdumpCmd = &cobra.Command{ Use: "crashdump", Short: "Dump debug information about the cluster", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { return errors.New("`talosctl crashdump` is deprecated, please use `talosctl support` instead") }) }, } func init() { addCommand(crashdumpCmd) crashdumpCmd.Flags().StringVar(&crashdumpCmdFlags.clusterState.InitNode, "init-node", "", "specify IPs of init node") crashdumpCmd.Flags().StringSliceVar(&crashdumpCmdFlags.clusterState.ControlPlaneNodes, "control-plane-nodes", nil, "specify IPs of control plane nodes") crashdumpCmd.Flags().StringSliceVar(&crashdumpCmdFlags.clusterState.WorkerNodes, "worker-nodes", nil, "specify IPs of worker nodes") } ================================================ FILE: cmd/talosctl/cmd/talos/dashboard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "time" "github.com/spf13/cobra" "github.com/siderolabs/talos/internal/pkg/dashboard" "github.com/siderolabs/talos/pkg/machinery/client" ) var dashboardCmdFlags struct { interval time.Duration } // dashboardCmd represents the monitor command. var dashboardCmd = &cobra.Command{ Use: "dashboard", Short: "Cluster dashboard with node overview, logs and real-time metrics", Long: `Provide a text-based UI to navigate node overview, logs and real-time metrics. Keyboard shortcuts: - h, - switch one node to the left - l, - switch one node to the right - j, - scroll logs/process list down - k, - scroll logs/process list up - - scroll logs/process list half page down - - scroll logs/process list half page up - - scroll logs/process list one page down - - scroll logs/process list one page up `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { return dashboard.Run(ctx, c, dashboard.WithInterval(dashboardCmdFlags.interval), dashboard.WithScreens(dashboard.ScreenSummary, dashboard.ScreenMonitor), dashboard.WithAllowExitKeys(true), ) }) }, } func init() { dashboardCmd.Flags().DurationVarP(&dashboardCmdFlags.interval, "update-interval", "d", 3*time.Second, "interval between updates") addCommand(dashboardCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !windows package talos import ( "context" "fmt" "io" "os" "os/signal" "syscall" "github.com/siderolabs/gen/channel" "github.com/spf13/cobra" "golang.org/x/term" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/reporter" ) var debugCmdFlags struct { imageCmdFlagsType args []string } func init() { debugCmd.Flags().StringSliceVar(&debugCmdFlags.args, "args", nil, "arguments to pass to the container") debugCmd.Flags().StringVar(&debugCmdFlags.namespace, "namespace", "inmem", "namespace to use: `system` (CRI containerd) or `inmem` for in-memory containerd instance") addCommand(debugCmd) } // debugCmd represents the debug command. var debugCmd = &cobra.Command{ Use: "debug [args]", Short: "Run a debug container from an image archive or reference", Example: ` # Run a debug container from a local tar archive (image will be loaded into Talos from the archive) talosctl debug ./debug-tools.tar --args /bin/sh # Run a debug container from an image reference (Talos will pull the image if not present) talosctl debug docker.io/library/alpine:latest --args /bin/sh`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClientAndNodes(func(ctx context.Context, c *client.Client, nodes []string) error { if len(nodes) != 1 { return fmt.Errorf("expected exactly one node, got %v", nodes) } ctx = client.WithNode(ctx, nodes[0]) rep := reporter.New() ctrdInstance, err := debugCmdFlags.containerdInstance() if err != nil { return err } // verify if we are sending a tarball or pulling an image _, err = os.Stat(args[0]) if err != nil && !os.IsNotExist(err) { return fmt.Errorf("failed to stat image argument: %w", err) } var imgName string if err == nil { imgName, err = imageImportInternal(ctx, c, ctrdInstance, nodes[0], args[0], rep) if err != nil { return fmt.Errorf("failed to import image: %w", err) } } else { pullResult, err := imagePullInternal(ctx, c, ctrdInstance, nodes, args[0], rep) if err != nil { return fmt.Errorf("failed to pull image: %w", err) } imgName = pullResult[nodes[0]] } // no easy way to disable hooking up signal handling to the command context, // so instead save this context and use a new one from here on out. // // new context so that SIGINT/similar won't immediately cancel streaming // and instead allow us to forward the signal to the container ctx = context.WithoutCancel(ctx) ctx, cancel := context.WithCancel(ctx) defer cancel() runStream, err := c.DebugClient.ContainerRun(ctx, grpc.MaxCallRecvMsgSize(4*1024*1024), // 4 MiB grpc.MaxCallSendMsgSize(4*1024*1024), ) if err != nil { return fmt.Errorf("failed to create debug container stream: %w", err) } return runContainer(ctx, rep, runStream, imgName, debugCmdFlags.args, ctrdInstance) }) }, } //nolint:gocyclo,cyclop func runContainer( ctx context.Context, rep *reporter.Reporter, stream grpc.BidiStreamingClient[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse], imageName string, args []string, ctrdInstance *common.ContainerdInstance, ) error { //nolint:gocyclo,cyclop ctx, cancel := context.WithCancel(ctx) defer cancel() isTTY := term.IsTerminal(int(os.Stdin.Fd())) err := stream.Send(&machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_Spec{ Spec: &machine.DebugContainerRunRequestSpec{ Containerd: ctrdInstance, ImageName: imageName, Args: args, Profile: machine.DebugContainerRunRequestSpec_PROFILE_PRIVILEGED, Tty: isTTY, }, }, }) if err != nil { return fmt.Errorf("failed to send container spec: %w", err) } if isTTY { oldState, err := term.MakeRaw(int(os.Stdin.Fd())) if err != nil { return fmt.Errorf("failed to set terminal to raw mode: %w", err) } defer func() { if oldState != nil { term.Restore(int(os.Stdin.Fd()), oldState) //nolint:errcheck } }() } var ( sendC = make(chan *machine.DebugContainerRunRequest, 100) sendDone chan error recvDone = make(chan error, 1) stdinDone chan error exitCode int32 = -1 ) sigHandler(ctx, sendC) stdinDone = stdinReader(ctx, sendC) sendDone = sendLoop(stream, sendC) go func() { for { msg, err := stream.Recv() if err != nil { if status.Code(err) == codes.Canceled { recvDone <- context.Canceled return } recvDone <- err return } switch msg.Resp.(type) { case *machine.DebugContainerRunResponse_StdoutData: if stdoutData := msg.GetStdoutData(); stdoutData != nil { os.Stdout.Write(stdoutData) //nolint:errcheck } case *machine.DebugContainerRunResponse_ExitCode: exitCode = msg.GetExitCode() recvDone <- io.EOF return default: fmt.Fprintf(os.Stderr, "unknown message type %T\n", msg.Resp) } } }() // either stdin closes first, and we cancel goroutines and CloseSend this end of the stream // or recvLoop exits first (container exit or error), and we cancel goroutines and wait // for stdinReader to finish select { case err := <-stdinDone: if err != nil && err != io.EOF { fmt.Fprintf(os.Stderr, "%s\n", err.Error()) } cancel() // cancels sigHandler, stdinReader goroutines close(sendC) if sendDone != nil { <-sendDone } // close send stream and wait for the server to exit // which will cause recvLoop to return if err := stream.CloseSend(); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to close send stream: %v\n", err) } if recvErr := <-recvDone; recvErr != nil && recvErr != io.EOF { return recvErr } case err := <-recvDone: if err != nil { if err == context.Canceled { rep.Report(reporter.Update{ Message: "context canceled", Status: reporter.StatusError, }) return nil } else if err != io.EOF { rep.Report(reporter.Update{ Message: fmt.Sprintf("error: %s", err.Error()), Status: reporter.StatusError, }) return nil } } cancel() // sigHandler, stdinReader goroutines if stdinDone != nil { <-stdinDone } close(sendC) if sendDone != nil { <-sendDone } } if exitCode != -1 && exitCode != 0 { return fmt.Errorf("container exited with code %d", exitCode) } return nil } var forwardedSignals = []os.Signal{ syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP, syscall.SIGWINCH, } // sigHandler registers signal handlers for the signals in `forwardedSignals`, // and sends them to `msgC`. // // In case the signal received is `SIGWINCH`, the size of the user's terminal // is queried and sent as a `DebugContainerTerminalResize` message, in order // to resize the container's terminal. func sigHandler(ctx context.Context, msgC chan<- *machine.DebugContainerRunRequest) { sigC := make(chan os.Signal, 1) signal.Notify(sigC, forwardedSignals...) go func() { defer func() { defer signal.Stop(sigC) }() for { select { case <-ctx.Done(): return case sig := <-sigC: if sig.(syscall.Signal) == syscall.SIGWINCH { if term.IsTerminal(int(os.Stdin.Fd())) { width, height, err := term.GetSize(int(os.Stdin.Fd())) if err != nil { continue } if !channel.SendWithContext(ctx, msgC, &machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_TermResize{ TermResize: &machine.DebugContainerTerminalResize{ Width: int32(width), Height: int32(height), }, }, }) { return } } continue } if !channel.SendWithContext(ctx, msgC, &machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_Signal{ Signal: int32(sig.(syscall.Signal)), }, }) { return } } } }() sigC <- syscall.SIGWINCH // trigger an initial resize } // stdinReader reads from stdin and sends data to msgC. // // This implementation is unfortunately a bit complex. This is due to the fact // that reading from stdin is a blocking syscall, which means if we just do // `os.Stdin.Read` and send it's output to `msgC`, canceling `ctx` won't cause // this goroutine to end (that will only happen when there's something to read // from stdin, and the goroutine will loop around and check `ctx.Done()`. // To address this, wrap `os.Stdin` in a `io.Pipe()` we can cancel, and launch a // separate goroutine to close the pipe when `ctx` is canceled. // This way, as soon as `ctx` is canceled, the pipe is closed and the main // goroutine will get an `io.EOF` and exit. func stdinReader(ctx context.Context, msgC chan<- *machine.DebugContainerRunRequest) chan error { r, w := io.Pipe() done := make(chan error) go func() { io.Copy(w, os.Stdin) //nolint:errcheck w.Close() //nolint:errcheck }() go func() { <-ctx.Done() w.Close() //nolint:errcheck }() go func() { buf := make([]byte, 1024) for { n, err := r.Read(buf) if err != nil { done <- err return } if n == 0 { continue } if !channel.SendWithContext(ctx, msgC, &machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_StdinData{ StdinData: buf[:n], }, }) { done <- ctx.Err() return } } }() return done } // sendLoop launches a goroutine that reads messages from msgC and sends them to // the gRPC stream. // The launched goroutine exits if ctx is canceled, or an error is returned while // sending a message to the gRPC stream. // // sendLoop returns a channel that will receive the error (or nil) when the // goroutine exits. func sendLoop( stream grpc.BidiStreamingClient[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse], msgC chan *machine.DebugContainerRunRequest, ) chan error { done := make(chan error) go func() { for msg := range msgC { err := stream.Send(msg) if err != nil { done <- err return } } done <- nil }() return done } ================================================ FILE: cmd/talosctl/cmd/talos/disks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "errors" "github.com/spf13/cobra" ) var disksCmd = &cobra.Command{ Use: "disks", Short: "Get the list of disks from /sys/block on the machine", Long: ``, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return errors.New("`talosctl disks` is deprecated, please use `talosctl get disks`, `talosctl get systemdisk`, `talosctl get discoveredvolumes` instead") }, } func init() { addCommand(disksCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/diskusage.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "slices" "strconv" "text/tabwriter" "github.com/dustin/go-humanize" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var ( all bool threshold int64 ) // duCmd represents the du command. var duCmd = &cobra.Command{ Use: "usage [path1] [path2] ... [pathN]", Aliases: []string{"du"}, Short: "Retrieve a disk usage", Long: ``, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } var completeOnlyPaths []string for _, path := range completePathFromNode(toComplete) { if path[len(path)-1:] == "/" { completeOnlyPaths = append(completeOnlyPaths, path) } } return completeOnlyPaths, cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var paths []string if len(args) == 0 { paths = []string{"/"} } else { paths = args } stream, err := c.DiskUsage(ctx, &machineapi.DiskUsageRequest{ RecursionDepth: recursionDepth + 1, All: all, Threshold: threshold, Paths: paths, }) if err != nil { return fmt.Errorf("error fetching disk usage: %s", err) } addedHeader := false w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) stringifySize := func(s int64) string { if humanizeFlag { return humanize.Bytes(uint64(s)) } return strconv.FormatInt(s, 10) } defer w.Flush() //nolint:errcheck return helpers.ReadGRPCStream(stream, func(info *machineapi.DiskUsageInfo, node string, multipleNodes bool) error { if info.Error != "" { return helpers.NonFatalError(errors.New(info.Error)) } pattern := "%s\t%s\n" size := stringifySize(info.Size) args := []any{ size, info.RelativeName, } if info.Metadata != nil && info.Metadata.Hostname != "" { multipleNodes = true node = info.Metadata.Hostname } if !addedHeader { if multipleNodes { fmt.Fprintln(w, "NODE\tSIZE\tNAME") } else { fmt.Fprintln(w, "SIZE\tNAME") } addedHeader = true } if multipleNodes { pattern = "%s\t%s\t%s\n" args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) return nil }) }) }, } func init() { duCmd.Flags().BoolVarP(&humanizeFlag, "humanize", "H", false, "humanize size and time in the output") duCmd.Flags().BoolVarP(&all, "all", "a", false, "write counts for all files, not just directories") duCmd.Flags().Int64VarP(&threshold, "threshold", "t", 0, "threshold exclude entries smaller than SIZE if positive, or entries greater than SIZE if negative") duCmd.Flags().Int32VarP(&recursionDepth, "depth", "d", 0, "maximum recursion depth") addCommand(duCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/dmesg.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" ) var dmesgTail bool // dmesgCmd represents the dmesg command. var dmesgCmd = &cobra.Command{ Use: "dmesg", Short: "Retrieve kernel logs", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { stream, err := c.Dmesg(ctx, follow, dmesgTail) if err != nil { return fmt.Errorf("error getting dmesg: %w", err) } return helpers.ReadGRPCStream(stream, func(data *common.Data, node string, multipleNodes bool) error { if data.Bytes != nil { fmt.Printf("%s: %s", node, data.Bytes) } return nil }) }) }, } func init() { addCommand(dmesgCmd) dmesgCmd.Flags().BoolVarP(&follow, "follow", "f", false, "specify if the kernel log should be streamed") dmesgCmd.Flags().BoolVarP(&dmesgTail, "tail", "", false, "specify if only new messages should be sent (makes sense only when combined with --follow)") } ================================================ FILE: cmd/talosctl/cmd/talos/edit.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bytes" "context" "errors" "fmt" "io" "io/fs" "os" "runtime" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/durationpb" "k8s.io/kubectl/pkg/cmd/util/editor" "k8s.io/kubectl/pkg/cmd/util/editor/crlf" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/yamlstrip" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) var editCmdFlags struct { helpers.Mode namespace string dryRun bool configTryTimeout time.Duration } //nolint:gocyclo func editFn(c *client.Client) func(context.Context, string, resource.Resource, error) error { var ( path string lastError string ) edit := editor.NewDefaultEditor([]string{ "TALOS_EDITOR", "EDITOR", }) return func(ctx context.Context, node string, mc resource.Resource, callError error) error { if callError != nil { return fmt.Errorf("%s: %w", node, callError) } if mc.Metadata().Type() != config.MachineConfigType { return errors.New("only the machineconfig resource can be edited") } id := mc.Metadata().ID() if id != config.ActiveID { return nil } body, err := extractMachineConfigBody(mc) if err != nil { return err } edited := body for { var ( buf bytes.Buffer w io.Writer = &buf ) if runtime.GOOS == "windows" { w = crlf.NewCRLFWriter(w) } _, err := fmt.Fprintf(w, "# Editing %s/%s at node %s\n", mc.Metadata().Type(), id, node, ) if err != nil { return err } if lastError != "" { _, err = w.Write([]byte(addEditingComment(lastError))) if err != nil { return err } } _, err = w.Write(edited) if err != nil { return err } editedDiff := edited edited, path, err = edit.LaunchTempFile(fmt.Sprintf("%s-%s-edit-", mc.Metadata().Type(), id), ".yaml", &buf) if err != nil { return err } defer os.Remove(path) //nolint:errcheck edited = stripEditingComment(edited) // If we're retrying the loop because of an error, and no change was made in the file, short-circuit if lastError != "" && bytes.Equal(yamlstrip.Comments(editedDiff), yamlstrip.Comments(edited)) { if _, err = os.Stat(path); !errors.Is(err, fs.ErrNotExist) { message := addEditingComment(lastError) message += fmt.Sprintf("A copy of your changes has been stored to %q\nEdit canceled, no valid changes were saved.\n", path) return errors.New(message) } } if len(bytes.TrimSpace(bytes.TrimSpace(yamlstrip.Comments(edited)))) == 0 { fmt.Fprintln(os.Stderr, "Apply was skipped: empty file.") break } if bytes.Equal(edited, body) { fmt.Fprintln(os.Stderr, "Apply was skipped: no changes detected.") break } resp, err := c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{ Data: edited, Mode: editCmdFlags.Mode.Mode, DryRun: editCmdFlags.dryRun, TryModeTimeout: durationpb.New(editCmdFlags.configTryTimeout), }) if err != nil { lastError = err.Error() continue } helpers.PrintApplyResults(resp) break } return nil } } func stripEditingComment(in []byte) []byte { // Note: on Windows, we use crlf.NewCRLFWriter which converts LF to CRLF before opening the editor. // So this code block below undoes that conversion back to LF for consistent processing. if runtime.GOOS == "windows" { // revert back CRLF to LF for processing in = bytes.ReplaceAll(in, []byte("\r\n"), []byte("\n")) } for { idx := bytes.Index(in, []byte{'\n'}) if idx == -1 { return in } if !bytes.HasPrefix(in, []byte("# ")) { return in } in = in[idx+1:] } } func addEditingComment(in string) string { lines := strings.Split(in, "\n") return fmt.Sprintf("# \n# %s\n", strings.Join(lines, "\n# ")) } // editCmd represents the edit command. var editCmd = &cobra.Command{ Use: "edit machineconfig", Short: "Edit Talos node machine configuration with the default editor.", Args: cobra.RangeArgs(1, 2), Long: `The edit command allows you to directly edit the machine configuration of a Talos node using your preferred text editor. It will open the editor defined by your TALOS_EDITOR, or EDITOR environment variables, or fall back to 'vi' for Linux or 'notepad' for Windows.`, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } for _, node := range GlobalArgs.Nodes { nodeCtx := client.WithNodes(ctx, node) if err := helpers.ForEachResource(nodeCtx, c, nil, editFn(c), editCmdFlags.namespace, args...); err != nil { return err } } return nil }) }, } func init() { editCmd.Flags().StringVar(&editCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)") helpers.AddModeFlags(&editCmdFlags.Mode, editCmd) editCmd.Flags().BoolVar(&editCmdFlags.dryRun, "dry-run", false, "do not apply the change after editing and print the change summary instead") editCmd.Flags().DurationVar(&editCmdFlags.configTryTimeout, "timeout", constants.ConfigTryTimeout, "the config will be rolled back after specified timeout (if try mode is selected)") addCommand(editCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "crypto/sha256" "fmt" "io" "os" "slices" "strings" "text/tabwriter" "github.com/dustin/go-humanize" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" snapshot "go.etcd.io/etcd/etcdutl/v3/snapshot" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/logging" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) // etcdCmd represents the etcd command. var etcdCmd = &cobra.Command{ Use: "etcd", Short: "Manage etcd", Long: ``, } // etcdAlarmCmd represents the etcd alarm command. var etcdAlarmCmd = &cobra.Command{ Use: "alarm", Short: "Manage etcd alarms", Long: ``, } type alarmMessage interface { GetMetadata() *common.Metadata GetMemberAlarms() []*machine.EtcdMemberAlarm } func displayAlarms(messages []alarmMessage) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := "%s\t%s\n" header := "MEMBER\tALARM" for i, message := range messages { if message.GetMetadata() != nil && message.GetMetadata().GetHostname() != "" { node = message.GetMetadata().GetHostname() } for j, alarm := range message.GetMemberAlarms() { if i == 0 && j == 0 { if node != "" { header = "NODE\t" + header pattern = "%s\t" + pattern } fmt.Fprintln(w, header) } args := []any{ etcdresource.FormatMemberID(alarm.GetMemberId()), alarm.GetAlarm().String(), } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } } return w.Flush() } // etcdAlarmListCmd represents the etcd alarm list command. var etcdAlarmListCmd = &cobra.Command{ Use: "list", Short: "List the etcd alarms for the node.", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { response, err := c.EtcdAlarmList(ctx) if err != nil { if response == nil { return fmt.Errorf("error getting alarms: %w", err) } cli.Warning("%s", err) } return displayAlarms(xslices.Map(response.Messages, func(v *machine.EtcdAlarm) alarmMessage { return v })) }) }, } // etcdAlarmDisarmCmd represents the etcd alarm disarm command. var etcdAlarmDisarmCmd = &cobra.Command{ Use: "disarm", Short: "Disarm the etcd alarms for the node.", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { response, err := c.EtcdAlarmDisarm(ctx) if err != nil { if response == nil { return fmt.Errorf("error disarming alarms: %w", err) } cli.Warning("%s", err) } return displayAlarms(xslices.Map(response.Messages, func(v *machine.EtcdAlarmDisarm) alarmMessage { return v })) }) }, } // etcdDefragCmd represents the etcd defrag command. var etcdDefragCmd = &cobra.Command{ Use: "defrag", Short: "Defragment etcd database on the node", Long: `Defragmentation is a maintenance operation that releases unused space from the etcd database file. Defragmentation is a resource heavy operation and should be performed only when necessary on a single node at a time.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd defrag"); err != nil { return err } _, err := c.EtcdDefragment(ctx) return err }) }, } var etcdLeaveCmd = &cobra.Command{ Use: "leave", Short: "Tell nodes to leave etcd cluster", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd leave"); err != nil { return err } return c.EtcdLeaveCluster(ctx, &machine.EtcdLeaveClusterRequest{}) }) }, } var etcdMemberRemoveCmd = &cobra.Command{ Use: "remove-member ", Short: "Remove the node from etcd cluster", Long: `Use this command only if you want to remove a member which is in broken state. If there is no access to the node, or the node can't access etcd to call etcd leave. Always prefer etcd leave over this command.`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { memberID, err := etcdresource.ParseMemberID(args[0]) if err != nil { return fmt.Errorf("error parsing member ID: %w", err) } return c.EtcdRemoveMemberByID(ctx, &machine.EtcdRemoveMemberByIDRequest{ MemberId: memberID, }) }) }, } var etcdForfeitLeadershipCmd = &cobra.Command{ Use: "forfeit-leadership", Short: "Tell node to forfeit etcd cluster leadership", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { _, err := c.EtcdForfeitLeadership(ctx, &machine.EtcdForfeitLeadershipRequest{}) return err }) }, } var etcdMemberListCmd = &cobra.Command{ Use: "members", Short: "Get the list of etcd cluster members", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { response, err := c.EtcdMemberList(ctx, &machine.EtcdMemberListRequest{ QueryLocal: true, }) if err != nil { if response == nil { return fmt.Errorf("error getting members: %w", err) } cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := "%s\t%s\t%s\t%s\t%v\n" for i, message := range response.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if len(message.Members) == 0 { continue } for j, member := range message.Members { if i == 0 && j == 0 { if node != "" { fmt.Fprintln(w, "NODE\tID\tHOSTNAME\tPEER URLS\tCLIENT URLS\tLEARNER") pattern = "%s\t" + pattern } else { fmt.Fprintln(w, "ID\tHOSTNAME\tPEER URLS\tCLIENT URLS\tLEARNER") } } args := []any{ etcdresource.FormatMemberID(member.Id), member.Hostname, strings.Join(member.PeerUrls, ","), strings.Join(member.ClientUrls, ","), member.IsLearner, } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } } return w.Flush() }) }, } var etcdStatusCmd = &cobra.Command{ Use: "status", Short: "Get the status of etcd cluster member", Long: `Returns the status of etcd member on the node, use multiple nodes to get status of all members.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { response, err := c.EtcdStatus(ctx) if err != nil { if response == nil { return fmt.Errorf("error getting status: %w", err) } cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := "%s\t%s\t%s (%.2f%%)\t%s\t%d\t%d\t%d\t%v\t%s\t%s\t%s\n" header := "MEMBER\tDB SIZE\tIN USE\tLEADER\tRAFT INDEX\tRAFT TERM\tRAFT APPLIED INDEX\tLEARNER\tPROTOCOL\tSTORAGE\tERRORS" for i, message := range response.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if i == 0 { if node != "" { header = "NODE\t" + header pattern = "%s\t" + pattern } fmt.Fprintln(w, header) } var ratio float64 if message.GetMemberStatus().GetDbSize() > 0 { ratio = float64(message.GetMemberStatus().GetDbSizeInUse()) / float64(message.GetMemberStatus().GetDbSize()) * 100.0 } args := []any{ etcdresource.FormatMemberID(message.GetMemberStatus().GetMemberId()), humanize.Bytes(uint64(message.GetMemberStatus().GetDbSize())), humanize.Bytes(uint64(message.GetMemberStatus().GetDbSizeInUse())), ratio, etcdresource.FormatMemberID(message.GetMemberStatus().GetLeader()), message.GetMemberStatus().GetRaftIndex(), message.GetMemberStatus().GetRaftTerm(), message.GetMemberStatus().GetRaftAppliedIndex(), message.GetMemberStatus().GetIsLearner(), message.GetMemberStatus().GetProtocolVersion(), message.GetMemberStatus().GetStorageVersion(), strings.Join(message.GetMemberStatus().GetErrors(), ", "), } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } return w.Flush() }) }, } var etcdSnapshotCmd = &cobra.Command{ Use: "snapshot ", Short: "Stream snapshot of the etcd node to the path.", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd snapshot"); err != nil { return err } dbPath := args[0] partPath := dbPath + ".part" defer os.RemoveAll(partPath) //nolint:errcheck dest, err := os.OpenFile(partPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { return fmt.Errorf("error creating temporary file: %w", err) } defer dest.Close() //nolint:errcheck r, err := c.EtcdSnapshot(ctx, &machine.EtcdSnapshotRequest{}) if err != nil { return fmt.Errorf("error reading file: %w", err) } defer r.Close() //nolint:errcheck size, err := io.Copy(dest, r) if err != nil { return fmt.Errorf("error reading: %w", err) } if err = dest.Sync(); err != nil { return fmt.Errorf("failed to fsync: %w", err) } // this check is from https://github.com/etcd-io/etcd/blob/client/v3.5.0-alpha.0/client/v3/snapshot/v3_snapshot.go#L46 if (size % 512) != sha256.Size { return fmt.Errorf("sha256 checksum not found (size %d)", size) } if err = dest.Close(); err != nil { return fmt.Errorf("failed to close: %w", err) } if err = os.Rename(partPath, dbPath); err != nil { return fmt.Errorf("error renaming to final location: %w", err) } fmt.Printf("etcd snapshot saved to %q (%d bytes)\n", dbPath, size) manager := snapshot.NewV3(logging.Wrap(os.Stderr)) status, err := manager.Status(dbPath) if err != nil { return err } fmt.Printf("snapshot info: hash %08x, revision %d, total keys %d, total size %d\n", status.Hash, status.Revision, status.TotalKey, status.TotalSize) return nil }) }, } var etcdDowngradeCmd = &cobra.Command{ Use: "downgrade", Short: "Manage etcd storage system downgrades", Long: ``, } const ( etcdDowngradePattern = "%s\n" etcdDowngradeHeader = "MESSAGE" ) var etcdDowngradeValidateCmd = &cobra.Command{ Use: "validate ", Short: "Validate if the etcd storage system can be downgraded to the specified version.", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd downgrade validate"); err != nil { return err } version := args[0] r, err := c.EtcdDowngradeValidate(ctx, &machine.EtcdDowngradeValidateRequest{Version: version}) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := etcdDowngradePattern header := etcdDowngradeHeader for i, message := range r.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if i == 0 { if node != "" { header = "NODE\t" + header pattern = "%s\t" + pattern } fmt.Fprintln(w, header) } args := []any{ fmt.Sprintf("downgrade validate success, cluster version %s", message.GetClusterDowngrade().GetClusterVersion(), ), } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } return w.Flush() }) }, } var etcdDowngradeEnableCmd = &cobra.Command{ Use: "enable ", Short: "Enable etcd storage system downgrade to the specified version.", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd downgrade enable"); err != nil { return err } version := args[0] r, err := c.EtcdDowngradeEnable(ctx, &machine.EtcdDowngradeEnableRequest{Version: version}) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := etcdDowngradePattern header := etcdDowngradeHeader for i, message := range r.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if i == 0 { if node != "" { header = "NODE\t" + header pattern = "%s\t" + pattern } fmt.Fprintln(w, header) } args := []any{ fmt.Sprintf("downgrade enable success, cluster version %s", message.GetClusterDowngrade().GetClusterVersion(), ), } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } return w.Flush() }) }, } var etcdDowngradeCancelCmd = &cobra.Command{ Use: "cancel", Short: "Cancel etcd storage system downgrade.", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "etcd downgrade cancel"); err != nil { return err } r, err := c.EtcdDowngradeCancel(ctx) if err != nil { return err } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" pattern := etcdDowngradePattern header := etcdDowngradeHeader for i, message := range r.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if i == 0 { if node != "" { header = "NODE\t" + header pattern = "%s\t" + pattern } fmt.Fprintln(w, header) } args := []any{ fmt.Sprintf("downgrade cancel success, cluster version %s", message.GetClusterDowngrade().GetClusterVersion(), ), } if node != "" { args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, pattern, args...) } return w.Flush() }) }, } func init() { etcdAlarmCmd.AddCommand( etcdAlarmListCmd, etcdAlarmDisarmCmd, ) etcdDowngradeCmd.AddCommand( etcdDowngradeValidateCmd, etcdDowngradeEnableCmd, etcdDowngradeCancelCmd, ) etcdCmd.AddCommand( etcdAlarmCmd, etcdDefragCmd, etcdForfeitLeadershipCmd, etcdLeaveCmd, etcdMemberListCmd, etcdMemberRemoveCmd, etcdSnapshotCmd, etcdStatusCmd, etcdDowngradeCmd, ) addCommand(etcdCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "strings" "text/tabwriter" "time" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var eventsCmdFlags struct { tailEvents int32 tailDuration time.Duration tailID string actorID string } // eventsCmd represents the events command. var eventsCmd = &cobra.Command{ Use: "events", Short: "Stream runtime events", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tID\tEVENT\tACTOR\tSOURCE\tMESSAGE") var opts []client.EventsOptionFunc if eventsCmdFlags.tailEvents != 0 { opts = append(opts, client.WithTailEvents(eventsCmdFlags.tailEvents)) } if eventsCmdFlags.tailDuration != 0 { opts = append(opts, client.WithTailDuration(eventsCmdFlags.tailDuration)) } if eventsCmdFlags.tailID != "" { opts = append(opts, client.WithTailID(eventsCmdFlags.tailID)) } if eventsCmdFlags.actorID != "" { opts = append(opts, client.WithActorID(eventsCmdFlags.actorID)) } events, err := c.Events(ctx, opts...) if err != nil { return err } return helpers.ReadGRPCStream(events, func(ev *machine.Event, node string, multipleNodes bool) error { format := "%s\t%s\t%s\n%s\t%s\t%s\n" event, err := client.UnmarshalEvent(ev) if err != nil { var errBadEvent client.EventNotSupportedError if errors.As(err, &errBadEvent) { return nil } return err } var args []any switch msg := event.Payload.(type) { case *machine.SequenceEvent: args = []any{msg.GetSequence()} if msg.Error != nil { args = append(args, "error:"+" "+msg.GetError().GetMessage()) } else { args = append(args, msg.GetAction().String()) } case *machine.PhaseEvent: args = []any{msg.GetPhase(), msg.GetAction().String()} case *machine.TaskEvent: args = []any{msg.GetTask(), msg.GetAction().String()} case *machine.ServiceStateEvent: args = []any{msg.GetService(), fmt.Sprintf("%s: %s", msg.GetAction(), msg.GetMessage())} case *machine.ConfigLoadErrorEvent: args = []any{"error", msg.GetError()} case *machine.ConfigValidationErrorEvent: args = []any{"error", msg.GetError()} case *machine.AddressEvent: args = []any{msg.GetHostname(), fmt.Sprintf("ADDRESSES: %s", strings.Join(msg.GetAddresses(), ","))} case *machine.MachineStatusEvent: args = []any{ msg.GetStage().String(), fmt.Sprintf("ready: %v, unmet conditions: %v", msg.GetStatus().Ready, xslices.Map(msg.GetStatus().GetUnmetConditions(), func(c *machine.MachineStatusEvent_MachineStatus_UnmetCondition) string { return c.Name }, ), ), } } args = append([]any{event.Node, event.ID, event.TypeURL, event.ActorID}, args...) fmt.Fprintf(w, format, args...) return w.Flush() }) }) }, } func init() { addCommand(eventsCmd) eventsCmd.Flags().Int32Var(&eventsCmdFlags.tailEvents, "tail", 0, "show specified number of past events (use -1 to show full history, default is to show no history)") eventsCmd.Flags().DurationVar(&eventsCmdFlags.tailDuration, "duration", 0, "show events for the past duration interval (one second resolution, default is to show no history)") eventsCmd.Flags().StringVar(&eventsCmdFlags.tailID, "since", "", "show events after the specified event ID (default is to show no history)") eventsCmd.Flags().StringVar(&eventsCmdFlags.actorID, "actor-id", "", "filter events by the specified actor ID (default is no filter)") } ================================================ FILE: cmd/talosctl/cmd/talos/get.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/spf13/cobra" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/output" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) var getCmdFlags struct { insecure bool namespace string output string watch bool } // getCmd represents the get (resources) command. var getCmd = &cobra.Command{ Use: "get []", Aliases: []string{"g"}, SuggestFor: []string{}, Short: "Get a specific resource or list of resources (use 'talosctl get rd' to see all available resource types).", Long: `Similar to 'kubectl get', 'talosctl get' returns a set of resources from the OS. To get a list of all available resource definitions, issue 'talosctl get rd'`, Example: "", ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { switch len(args) { case 0: return completeResourceDefinition(toComplete != "") case 1: return completeResourceID(args[0], getCmdFlags.namespace) } return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp }, Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { if getCmdFlags.insecure { return WithClientMaintenance(nil, getResources(args)) } return WithClient(getResources(args)) }, } //nolint:gocyclo,cyclop func getResources(args []string) func(ctx context.Context, c *client.Client) error { return func(ctx context.Context, c *client.Client) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } out, err := output.NewWriter(getCmdFlags.output) if err != nil { return err } resourceType := args[0] var resourceID string if len(args) == 2 { resourceID = args[1] } defer out.Flush() //nolint:errcheck if getCmdFlags.watch { // get -w OR get -w md, _ := metadata.FromOutgoingContext(ctx) nodes := md.Get("nodes") if len(nodes) == 0 { // use "current" node nodes = []string{""} } // fetch the RD from the first node (it doesn't matter which one to use, so we'll use the first one) rd, err := c.ResolveResourceKind(client.WithNode(ctx, nodes[0]), &getCmdFlags.namespace, resourceType) if err != nil { return err } resourceType = rd.TypedSpec().Type if err = out.WriteHeader(rd, true); err != nil { return err } aggregatedCh := make(chan nodeAndEvent) for _, node := range nodes { var nodeCtx context.Context if node == "" { nodeCtx = ctx } else { nodeCtx = client.WithNode(ctx, node) } watchCh := make(chan state.Event) if resourceID == "" { err = c.COSI.WatchKind( nodeCtx, resource.NewMetadata(getCmdFlags.namespace, resourceType, "", resource.VersionUndefined), watchCh, state.WithBootstrapContents(true), state.WithWatchKindUnmarshalOptions(state.WithSkipProtobufUnmarshal()), ) } else { err = c.COSI.Watch( nodeCtx, resource.NewMetadata(getCmdFlags.namespace, resourceType, resourceID, resource.VersionUndefined), watchCh, state.WithWatchUnmarshalOptions(state.WithSkipProtobufUnmarshal()), ) } if err != nil { return fmt.Errorf("error setting up watch on node %s: %w", node, err) } go aggregateEvents(ctx, aggregatedCh, watchCh, node) } var bootstrapped bool for { var nev nodeAndEvent select { case nev = <-aggregatedCh: case <-ctx.Done(): return nil } if nev.ev.Type == state.Errored { return fmt.Errorf("error watching resource: %w", nev.ev.Error) } if nev.ev.Type == state.Bootstrapped { bootstrapped = true if err = out.Flush(); err != nil { return err } continue } if nev.ev.Resource == nil { // new event type without resource, skip it continue } if err = out.WriteResource(nev.node, nev.ev.Resource, nev.ev.Type); err != nil { return err } if bootstrapped { if err = out.Flush(); err != nil { return err } } } } var multiErr *multierror.Error // get // get callbackResource := func(parentCtx context.Context, hostname string, r resource.Resource, callError error) error { if callError != nil { multiErr = multierror.Append(multiErr, callError) return nil } return out.WriteResource(hostname, r, 0) } callbackRD := func(definition *meta.ResourceDefinition) error { return out.WriteHeader(definition, false) } helperErr := helpers.ForEachResource(ctx, c, callbackRD, callbackResource, getCmdFlags.namespace, args...) if helperErr != nil { return helperErr } return multiErr.ErrorOrNil() } } type nodeAndEvent struct { node string ev state.Event } func aggregateEvents(ctx context.Context, outCh chan<- nodeAndEvent, watchCh <-chan state.Event, node string) { for { select { case ev := <-watchCh: select { case outCh <- nodeAndEvent{node, ev}: case <-ctx.Done(): return } case <-ctx.Done(): return } } } // completeResourceDefinition represents tab complete options for `get` and `get *` commands. func completeResourceDefinition(withAliases bool) ([]string, cobra.ShellCompDirective) { var result []string if WithClientNoNodes(func(ctx context.Context, c *client.Client) error { items, err := safe.StateListAll[*meta.ResourceDefinition](ctx, c.COSI) if err != nil { return err } for res := range items.All() { if withAliases { result = append(result, res.TypedSpec().Aliases...) } result = append(result, res.Metadata().ID()) } return nil }) != nil { return nil, cobra.ShellCompDirectiveError } return result, cobra.ShellCompDirectiveNoFileComp } // completeResourceID represents tab complete options for `get` and `get *` commands. func completeResourceID(resourceType, namespace string) ([]string, cobra.ShellCompDirective) { var result []string if WithClientNoNodes(func(ctx context.Context, c *client.Client) error { if len(GlobalArgs.Nodes) > 0 { ctx = client.WithNode(ctx, GlobalArgs.Nodes[0]) } rd, err := c.ResolveResourceKind(ctx, &namespace, resourceType) if err != nil { return err } items, err := c.COSI.List(ctx, resource.NewMetadata(namespace, rd.TypedSpec().Type, "", resource.VersionUndefined)) if err != nil { return err } for _, item := range items.Items { result = append(result, item.Metadata().ID()) } return nil }) != nil { return nil, cobra.ShellCompDirectiveError } return result, cobra.ShellCompDirectiveNoFileComp } // CompleteNodes represents tab completion for `--nodes` argument. func CompleteNodes(*cobra.Command, []string, string) ([]string, cobra.ShellCompDirective) { var nodes []string if WithClientNoNodes(func(ctx context.Context, c *client.Client) error { items, err := safe.StateListAll[*cluster.Member](ctx, c.COSI) if err != nil { return err } for res := range items.All() { if hostname := res.TypedSpec().Hostname; hostname != "" { nodes = append(nodes, hostname) } for _, address := range res.TypedSpec().Addresses { nodes = append(nodes, address.String()) } } return nil }) != nil { return nil, cobra.ShellCompDirectiveError } return nodes, cobra.ShellCompDirectiveNoFileComp } func init() { getCmd.Flags().StringVar(&getCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)") getCmd.Flags().StringVarP(&getCmdFlags.output, "output", "o", "table", "output mode (json, table, yaml, jsonpath)") getCmd.Flags().BoolVarP(&getCmdFlags.watch, "watch", "w", false, "watch resource changes") getCmd.Flags().BoolVarP(&getCmdFlags.insecure, "insecure", "i", false, "get resources using the insecure (encrypted with no auth) maintenance service") cli.Should(getCmd.RegisterFlagCompletionFunc("output", output.CompleteOutputArg)) addCommand(getCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/health.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "io" "os" "slices" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/spf13/cobra" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/cluster/check" "github.com/siderolabs/talos/pkg/cluster/hydrophone" clusterapi "github.com/siderolabs/talos/pkg/machinery/api/cluster" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" clusterres "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) type clusterNodes struct { InitNode string ControlPlaneNodes []string WorkerNodes []string nodes []cluster.NodeInfo nodesByType map[machine.Type][]cluster.NodeInfo } func (cl *clusterNodes) InitNodeInfos() error { var initNodes []string if cl.InitNode != "" { initNodes = []string{cl.InitNode} } initNodeInfos, err := cluster.IPsToNodeInfos(initNodes) if err != nil { return err } controlPlaneNodeInfos, err := cluster.IPsToNodeInfos(cl.ControlPlaneNodes) if err != nil { return err } workerNodeInfos, err := cluster.IPsToNodeInfos(cl.WorkerNodes) if err != nil { return err } nodesByType := make(map[machine.Type][]cluster.NodeInfo) nodesByType[machine.TypeInit] = initNodeInfos nodesByType[machine.TypeControlPlane] = controlPlaneNodeInfos nodesByType[machine.TypeWorker] = workerNodeInfos cl.nodesByType = nodesByType cl.nodes = slices.Concat(initNodeInfos, controlPlaneNodeInfos, workerNodeInfos) return nil } func (cl *clusterNodes) Nodes() []cluster.NodeInfo { return cl.nodes } func (cl *clusterNodes) NodesByType(t machine.Type) []cluster.NodeInfo { return cl.nodesByType[t] } var healthCmdFlags struct { clusterState clusterNodes clusterWaitTimeout time.Duration forceEndpoint string runOnServer bool runE2E bool } // healthCmd represents the health command. var healthCmd = &cobra.Command{ Use: "health", Short: "Check cluster health", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { err := healthCmdFlags.clusterState.InitNodeInfos() if err != nil { return err } if err := runHealth(); err != nil { return err } if healthCmdFlags.runE2E { return runE2E() } return nil }, } func runHealth() error { if healthCmdFlags.runOnServer { return WithClient(healthOnServer) } return WithClientNoNodes(healthOnClient) } func healthOnClient(ctx context.Context, c *client.Client) error { clientProvider := &cluster.ConfigClientProvider{ DefaultClient: c, } defer clientProvider.Close() //nolint:errcheck clusterInfo, err := buildClusterInfo(healthCmdFlags.clusterState) if err != nil { return err } state := struct { cluster.ClientProvider cluster.K8sProvider cluster.Info }{ ClientProvider: clientProvider, K8sProvider: &cluster.KubernetesClient{ ClientProvider: clientProvider, ForceEndpoint: healthCmdFlags.forceEndpoint, }, Info: clusterInfo, } // Run cluster readiness checks checkCtx, checkCtxCancel := context.WithTimeout(ctx, healthCmdFlags.clusterWaitTimeout) defer checkCtxCancel() return check.Wait(checkCtx, &state, append(check.DefaultClusterChecks(), check.ExtraClusterChecks()...), check.StderrReporter()) } func healthOnServer(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "health"); err != nil { return err } controlPlaneNodes := healthCmdFlags.clusterState.ControlPlaneNodes if healthCmdFlags.clusterState.InitNode != "" { controlPlaneNodes = append(controlPlaneNodes, healthCmdFlags.clusterState.InitNode) } healthCheckClient, err := c.ClusterHealthCheck(ctx, healthCmdFlags.clusterWaitTimeout, &clusterapi.ClusterInfo{ ControlPlaneNodes: controlPlaneNodes, WorkerNodes: healthCmdFlags.clusterState.WorkerNodes, ForceEndpoint: healthCmdFlags.forceEndpoint, }) if err != nil { return err } if err := healthCheckClient.CloseSend(); err != nil { return err } for { msg, err := healthCheckClient.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { return nil } return err } if msg.GetMetadata().GetError() != "" { return fmt.Errorf("healthcheck error: %s", msg.GetMetadata().GetError()) } fmt.Fprintln(os.Stderr, msg.GetMessage()) } } func runE2E() error { return WithClient(func(ctx context.Context, c *client.Client) error { clientProvider := &cluster.ConfigClientProvider{ DefaultClient: c, } defer clientProvider.Close() //nolint:errcheck state := &cluster.KubernetesClient{ ClientProvider: clientProvider, ForceEndpoint: healthCmdFlags.forceEndpoint, } // Run cluster readiness checks checkCtx, checkCtxCancel := context.WithTimeout(ctx, healthCmdFlags.clusterWaitTimeout) defer checkCtxCancel() options := hydrophone.DefaultOptions() options.UseSpinner = true return hydrophone.Run(checkCtx, state, options) }) } func init() { addCommand(healthCmd) healthCmd.Flags().StringVar(&healthCmdFlags.clusterState.InitNode, "init-node", "", "specify IPs of init node") healthCmd.Flags().StringSliceVar(&healthCmdFlags.clusterState.ControlPlaneNodes, "control-plane-nodes", nil, "specify IPs of control plane nodes") healthCmd.Flags().StringSliceVar(&healthCmdFlags.clusterState.WorkerNodes, "worker-nodes", nil, "specify IPs of worker nodes") healthCmd.Flags().DurationVar(&healthCmdFlags.clusterWaitTimeout, "wait-timeout", 20*time.Minute, "timeout to wait for the cluster to be ready") healthCmd.Flags().StringVar(&healthCmdFlags.forceEndpoint, "k8s-endpoint", "", "use endpoint instead of kubeconfig default") healthCmd.Flags().BoolVar(&healthCmdFlags.runOnServer, "server", true, "run server-side check") healthCmd.Flags().BoolVar(&healthCmdFlags.runE2E, "run-e2e", false, "run Kubernetes e2e test") } func buildClusterInfo(clusterState clusterNodes) (cluster.Info, error) { // if nodes are set explicitly via command line args, use them if len(clusterState.ControlPlaneNodes) > 0 || len(clusterState.WorkerNodes) > 0 { return &clusterState, nil } // read members from the Talos API var members []*clusterres.Member err := WithClientNoNodes(func(ctx context.Context, c *client.Client) error { items, err := safe.StateListAll[*clusterres.Member](ctx, c.COSI) if err != nil { return err } items.ForEach(func(item *clusterres.Member) { members = append(members, item) }) return nil }) if err != nil { return nil, err } return check.NewDiscoveredClusterInfo(members) } ================================================ FILE: cmd/talosctl/cmd/talos/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bufio" "context" "errors" "fmt" "io" "log" "net" "net/url" "os" "os/signal" "slices" "strings" "text/tabwriter" "time" "github.com/blang/semver/v4" "github.com/dustin/go-humanize" "github.com/siderolabs/gen/ensure" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/multiplex" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/pull" mgmthelpers "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/artifacts" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services/registry" "github.com/siderolabs/talos/pkg/flags" "github.com/siderolabs/talos/pkg/imager/cache" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/reporter" ) type imageCmdFlagsType struct { namespace string } var imageCmdFlags imageCmdFlagsType func (flags imageCmdFlagsType) apiNamespace() (common.ContainerdNamespace, error) { switch flags.namespace { case "cri": return common.ContainerdNamespace_NS_CRI, nil case "system": return common.ContainerdNamespace_NS_SYSTEM, nil default: return 0, fmt.Errorf("unsupported namespace %q", flags.namespace) } } func (flags imageCmdFlagsType) containerdInstance() (*common.ContainerdInstance, error) { switch flags.namespace { case "cri": return &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, nil case "system": return &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, nil case "inmem": return &common.ContainerdInstance{ Driver: common.ContainerDriver_CONTAINERD, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, nil default: return nil, fmt.Errorf("unsupported namespace %q", flags.namespace) } } // imagesCmd represents the image command. var imageCmd = &cobra.Command{ Use: "image", Aliases: []string{"images"}, Short: "Manage container images", Long: ``, Args: cobra.NoArgs, } // imageListCmd represents the image list command. var imageListCmd = &cobra.Command{ Use: "list", Aliases: []string{"l", "ls"}, Short: "List images in the machine's container runtime", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return imageList() }, } func imageList() error { return WithClientAndNodes(func(ctx context.Context, c *client.Client, nodes []string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() containerdInstance, err := imageCmdFlags.containerdInstance() if err != nil { return err } responseChan := multiplex.Streaming(ctx, nodes, func(ctx context.Context) (grpc.ServerStreamingClient[machine.ImageServiceListResponse], error) { return c.ImageClient.List(ctx, &machine.ImageServiceListRequest{ Containerd: containerdInstance, }) }, ) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) headerWritten := false var errs error for resp := range responseChan { if resp.Err != nil { if status.Code(resp.Err) == codes.Unimplemented { // fallback to legacy API for older Talos return imageListLegacy() } errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) continue } if !headerWritten { headerWritten = true fmt.Fprintln(w, "NODE\tIMAGE\tDIGEST\tSIZE\tLABELS\tCREATED") } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", resp.Node, resp.Payload.GetName(), resp.Payload.GetDigest(), humanize.Bytes(uint64(resp.Payload.GetSize())), helpers.FormatLabels(resp.Payload.GetLabels()), resp.Payload.GetCreatedAt().AsTime().Format(time.RFC3339), ) } return errors.Join(errs, w.Flush()) }) } // imageListLegacy lists images using the legacy ImageList API. // // Note: remove me in Talos 1.15. func imageListLegacy() error { return WithClient(func(ctx context.Context, c *client.Client) error { ns, err := imageCmdFlags.apiNamespace() if err != nil { return err } rcv, err := c.ImageList(ctx, ns) //nolint:staticcheck // legacy talosctl methods, to be removed in Talos 1.15 if err != nil { return fmt.Errorf("error listing images: %w", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tIMAGE\tDIGEST\tSIZE\tCREATED") if err = helpers.ReadGRPCStream(rcv, func(msg *machine.ImageListResponse, node string, multipleNodes bool) error { fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", node, msg.Name, msg.Digest, humanize.Bytes(uint64(msg.Size)), msg.CreatedAt.AsTime().Format(time.RFC3339), ) return nil }); err != nil { return err } return w.Flush() }) } // imagePullCmd represents the image pull command. var imagePullCmd = &cobra.Command{ Use: "pull ", Aliases: []string{"p"}, Short: "Pull an image into the machine's container runtime", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return imagePull(args[0]) }, } // imagePull pulls an image using modern API and showing progress. func imagePull(imageRef string) error { return WithClientAndNodes(func(ctx context.Context, c *client.Client, nodes []string) error { rep := reporter.New() containerdInstance, err := imageCmdFlags.containerdInstance() if err != nil { return err } _, err = imagePullInternal(ctx, c, containerdInstance, nodes, imageRef, rep) return err }) } func imagePullInternal( ctx context.Context, c *client.Client, containerdInstance *common.ContainerdInstance, nodes []string, imageRef string, rep *reporter.Reporter, ) (map[string]string, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() responseChan := multiplex.Streaming(ctx, nodes, func(ctx context.Context) (grpc.ServerStreamingClient[machine.ImageServicePullResponse], error) { return c.ImageClient.Pull(ctx, &machine.ImageServicePullRequest{ Containerd: containerdInstance, ImageRef: imageRef, }) }, ) finishedPulls := map[string]string{} var ( w pull.ProgressWriter errs error ) for resp := range responseChan { if resp.Err != nil { if status.Code(resp.Err) == codes.Unimplemented { cancel() // fallback to legacy API for older Talos return nil, imagePullLegacy(imageRef) } errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) continue } switch payload := resp.Payload.Response.(type) { case *machine.ImageServicePullResponse_PullProgress: if !rep.IsColorized() { // don't show progress if not colorized/terminal continue } w.UpdateJob(resp.Node, payload.PullProgress.GetLayerId(), payload.PullProgress.GetProgress()) w.PrintLayerProgress(rep) case *machine.ImageServicePullResponse_Name: finishedPulls[resp.Node] = payload.Name } } if len(finishedPulls) > 0 { var sb strings.Builder for node, imageName := range finishedPulls { fmt.Fprintf(&sb, "%s: pulled image %s\n", node, imageName) } rep.Report(reporter.Update{ Message: sb.String(), Status: reporter.StatusSucceeded, }) } return finishedPulls, errs } // imagePullLegacy pulls an image using the legacy ImagePull API. // // Note: remove me in Talos 1.15. func imagePullLegacy(imageRef string) error { return WithClient(func(ctx context.Context, c *client.Client) error { ns, err := imageCmdFlags.apiNamespace() if err != nil { return err } err = c.ImagePull(ctx, ns, imageRef) //nolint:staticcheck // legacy talosctl methods, to be removed in Talos 1.15 if err != nil { return fmt.Errorf("error pulling image: %w", err) } return nil }) } // imageImportInternal imports an image from a tarball. // // Note: this is not exposed as a command, but used in talosctl debug flow. // //nolint:gocyclo func imageImportInternal( ctx context.Context, c *client.Client, containerdInstance *common.ContainerdInstance, node string, imageTarballPath string, rep *reporter.Reporter, ) (string, error) { in, err := os.Open(imageTarballPath) if err != nil { return "", fmt.Errorf("failed to open image tarball: %w", err) } defer in.Close() //nolint:errcheck ctx = client.WithNode(ctx, node) ctx, cancel := context.WithCancel(ctx) defer cancel() rcv, err := c.ImageClient.Import(ctx) if err != nil { return "", err } if err = rcv.Send(&machine.ImageServiceImportRequest{ Request: &machine.ImageServiceImportRequest_Containerd{ Containerd: containerdInstance, }, }); err != nil { return "", err } const chunkSize = 32 * 1024 buf := make([]byte, chunkSize) var bytesImported uint64 for { n, err := in.Read(buf) if err != nil { if err == io.EOF { break } return "", fmt.Errorf("error reading image tarball: %w", err) } if n > 0 { if err = rcv.Send(&machine.ImageServiceImportRequest{ Request: &machine.ImageServiceImportRequest_ImageChunk{ ImageChunk: &common.Data{ Bytes: buf[:n], }, }, }); err != nil { return "", fmt.Errorf("error sending image chunk: %w", err) } } bytesImported += uint64(n) if rep.IsColorized() { rep.Report(reporter.Update{ Message: fmt.Sprintf("%s: %s imported", node, humanize.IBytes(bytesImported)), Status: reporter.StatusRunning, }) } } resp, err := rcv.CloseAndRecv() if err != nil { return "", fmt.Errorf("error closing send stream: %w", err) } rep.Report(reporter.Update{ Message: fmt.Sprintf("%s: image imported %s from %s", node, resp.GetName(), imageTarballPath), Status: reporter.StatusSucceeded, }) return resp.GetName(), nil } // imageRemoveCmd represents the image remove command. var imageRemoveCmd = &cobra.Command{ Use: "remove ", Aliases: []string{"rm"}, Short: "Remove an image from the machine's container runtime", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return imageRemove(args[0]) }, } // imageRemove removes an image using modern API. func imageRemove(imageRef string) error { return WithClientAndNodes(func(ctx context.Context, c *client.Client, nodes []string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() containerdInstance, err := imageCmdFlags.containerdInstance() if err != nil { return err } responseChan := multiplex.Unary(ctx, nodes, func(ctx context.Context) (*emptypb.Empty, error) { return c.ImageClient.Remove(ctx, &machine.ImageServiceRemoveRequest{ Containerd: containerdInstance, ImageRef: imageRef, }) }, ) var errs error for resp := range responseChan { if resp.Err != nil { errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) } } return errs }) } var imageK8sBundleCmdFlags = struct { k8sVersion pflag.Value flannelVersion pflag.Value corednsVersion pflag.Value etcdVersion pflag.Value kubeNetworkPoliciesVersion pflag.Value }{ k8sVersion: flags.Semver(constants.DefaultKubernetesVersion), flannelVersion: flags.Semver(constants.FlannelVersion), corednsVersion: flags.Semver(constants.DefaultCoreDNSVersion), etcdVersion: flags.Semver(constants.DefaultEtcdVersion), kubeNetworkPoliciesVersion: flags.Semver(constants.KubeNetworkPoliciesVersion), } // imageK8sBundleCmd represents the image k8s-bundle command. var imageK8sBundleCmd = &cobra.Command{ Use: "k8s-bundle", Aliases: []string{"default"}, Short: "List the default Kubernetes images used by Talos", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { images := images.ListWithOptions(container.NewV1Alpha1( &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{}, }, ClusterConfig: &v1alpha1.ClusterConfig{ EtcdConfig: &v1alpha1.EtcdConfig{}, APIServerConfig: &v1alpha1.APIServerConfig{}, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{}, SchedulerConfig: &v1alpha1.SchedulerConfig{}, CoreDNSConfig: &v1alpha1.CoreDNS{}, ProxyConfig: &v1alpha1.ProxyConfig{}, }, }), images.VersionsListOptions{ KubernetesVersion: imageK8sBundleCmdFlags.k8sVersion.String(), EtcdVersion: imageK8sBundleCmdFlags.etcdVersion.String(), FlannelVersion: imageK8sBundleCmdFlags.flannelVersion.String(), CoreDNSVersion: imageK8sBundleCmdFlags.corednsVersion.String(), KubeNetworkPoliciesVersion: imageK8sBundleCmdFlags.kubeNetworkPoliciesVersion.String(), }, ) fmt.Printf("%s\n", images.Flannel) fmt.Printf("%s\n", images.CoreDNS) fmt.Printf("%s\n", images.Etcd) fmt.Printf("%s\n", images.Pause) fmt.Printf("%s\n", images.KubeAPIServer) fmt.Printf("%s\n", images.KubeControllerManager) fmt.Printf("%s\n", images.KubeScheduler) fmt.Printf("%s\n", images.KubeProxy) fmt.Printf("%s\n", images.Kubelet) fmt.Printf("%s\n", images.KubeNetworkPolicies) return nil }, } var imageTalosBundleCmdFlags = struct { extensions bool overlays bool }{} // imageTalosBundleCmd represents the image talos-bundle command. var imageTalosBundleCmd = &cobra.Command{ Use: "talos-bundle [talos-version]", Short: "List the default system images and extensions used for Talos", Long: ``, Args: cobra.MatchAll( cobra.RangeArgs(0, 1), func(cmd *cobra.Command, args []string) error { maximumVersion, err := semver.ParseTolerant(version.Tag) if err != nil { panic(err) // panic, this should never happen } maximumVersion.Patch = 0 maximumVersion.Pre = nil if err := maximumVersion.IncrementMinor(); err != nil { panic(err) // panic, this should never happen } // If no version specified, use current version if len(args) == 0 { return nil } tag := args[0] if !strings.HasPrefix(tag, "v") { return fmt.Errorf("invalid tag %q: must have \"v\" prefix", tag) } ver, err := semver.ParseTolerant(tag) if err != nil { return fmt.Errorf("invalid argument %q: tag must be a valid semver", tag) } if !ver.GTE(talosBundleMinimumVersion) || !ver.LT(maximumVersion) { return fmt.Errorf( "invalid tag %q: must be between v%s (inclusive) and v%s (exclusive)", tag, talosBundleMinimumVersion, maximumVersion, ) } return nil }, ), RunE: func(cmd *cobra.Command, args []string) error { var ( tag string err error extensions []artifacts.ExtensionRef overlays []artifacts.OverlayRef ) // Default to current version if not specified if len(args) == 0 { tag = version.Tag } else { tag = args[0] } sources := images.ListSourcesFor(tag) fmt.Printf("%s\n", sources.Installer) fmt.Printf("%s\n", sources.InstallerBase) fmt.Printf("%s\n", sources.Imager) fmt.Printf("%s\n", sources.Talos) fmt.Printf("%s\n", sources.TalosctlAll) fmt.Printf("%s\n", sources.Overlays) fmt.Printf("%s\n", sources.Extensions) digestedReferences := []string{} if imageTalosBundleCmdFlags.extensions { extensions, err = artifacts.FetchOfficialExtensions(tag) if err != nil { return fmt.Errorf("error fetching official extensions for %s: %w", tag, err) } for _, extension := range extensions { digestedReferences = append(digestedReferences, fmt.Sprintf("%s@%s", extension.TaggedReference.String(), extension.Digest)) } } if imageTalosBundleCmdFlags.overlays { overlays, err = artifacts.FetchOfficialOverlays(tag) if err != nil { return fmt.Errorf("error fetching official overlays for %s: %w", tag, err) } for _, overlay := range overlays { digestedReferences = append(digestedReferences, fmt.Sprintf("%s@%s", overlay.TaggedReference.String(), overlay.Digest)) } } slices.Sort(digestedReferences) for _, ref := range slices.Compact(digestedReferences) { fmt.Printf("%s\n", ref) } return nil }, } var talosBundleMinimumVersion = semver.MustParse("1.11.0-alpha.0") // imageIntegrationCmd represents the integration image command. var imageIntegrationCmd = &cobra.Command{ Use: "integration", Short: "List the integration images used by k8s in Talos", Args: cobra.NoArgs, Hidden: true, RunE: func(*cobra.Command, []string) error { if stat, _ := os.Stdin.Stat(); (stat.Mode() & os.ModeCharDevice) != 0 { //nolint:errcheck return errors.New("input must be piped") } if imageIntegrationCmdFlags.installerTag == "" { return errors.New("installer tag is required") } if imageIntegrationCmdFlags.registryAndUser == "" { return errors.New("registry and user string is required") } imgs := images.List(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{}, }, ClusterConfig: &v1alpha1.ClusterConfig{ EtcdConfig: &v1alpha1.EtcdConfig{}, APIServerConfig: &v1alpha1.APIServerConfig{}, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{}, SchedulerConfig: &v1alpha1.SchedulerConfig{}, CoreDNSConfig: &v1alpha1.CoreDNS{}, ProxyConfig: &v1alpha1.ProxyConfig{}, }, })) imageNames := []string{ imgs.Flannel.String(), imgs.CoreDNS.String(), imgs.Etcd.String(), imgs.KubeAPIServer.String(), imgs.KubeControllerManager.String(), imgs.KubeScheduler.String(), imgs.KubeProxy.String(), imgs.Kubelet.String(), imgs.Pause.String(), imgs.KubeNetworkPolicies.String(), "registry.k8s.io/conformance:v" + constants.DefaultKubernetesVersion, "docker.io/library/alpine:latest", "ghcr.io/siderolabs/talosctl:v1.12.4", "registry.k8s.io/kube-apiserver:v1.27.0", "registry.k8s.io/kube-apiserver:v1.27.1", "docker.io/library/alpine:3.23", imageIntegrationCmdFlags.registryAndUser + "/installer:" + imageIntegrationCmdFlags.installerTag, imageIntegrationCmdFlags.registryAndUser + "/talos:" + imageIntegrationCmdFlags.talosTag, } sc := bufio.NewScanner(os.Stdin) for sc.Scan() { switch sc := sc.Text(); { case strings.Contains(sc, "authenticated-"): // skip authenticated images case strings.HasPrefix(sc, "invalid.registry.k8s.io"): // skip invalid images default: imageNames = append(imageNames, sc) } } if err := sc.Err(); err != nil { return fmt.Errorf("error reading from stdin: %w", err) } slices.Sort(imageNames) imageNames = slices.Compact(imageNames) for _, img := range imageNames { fmt.Println(img) } return nil }, } var imageIntegrationCmdFlags struct { installerTag string talosTag string registryAndUser string } // imageCacheCreate represents the image cache create command. var imageCacheCreateCmd = &cobra.Command{ Use: "cache-create", Short: "Create a cache of images in OCI format into a directory", Long: `Create a cache of images in OCI format into a directory`, Example: fmt.Sprintf( `talosctl images cache-create --images=ghcr.io/siderolabs/kubelet:v%s --image-cache-path=/tmp/talos-image-cache Alternatively, stdin can be piped to the command: talosctl images default | talosctl images cache-create --image-cache-path=/tmp/talos-image-cache --images=- `, constants.DefaultKubernetesVersion, ), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if len(imageCacheCreateCmdFlags.images) == 0 { return fmt.Errorf("no images specified") } if imageCacheCreateCmdFlags.force { if err := os.RemoveAll(imageCacheCreateCmdFlags.imageCachePath); err != nil { return fmt.Errorf("error removing existing image cache path %s: %w", imageCacheCreateCmdFlags.imageCachePath, err) } } if _, err := os.Stat(imageCacheCreateCmdFlags.imageCachePath); err == nil { return fmt.Errorf("image cache path %s already exists, use --force to remove and use the path", imageCacheCreateCmdFlags.imageCachePath) } if imageCacheCreateCmdFlags.images[0] == "-" { var imagesListData strings.Builder if _, err := io.Copy(&imagesListData, os.Stdin); err != nil { return fmt.Errorf("error reading from stdin: %w", err) } imageCacheCreateCmdFlags.images = strings.Split(strings.Trim(imagesListData.String(), "\n"), "\n") } err := cache.Generate( imageCacheCreateCmdFlags.images, imageCacheCreateCmdFlags.platform, imageCacheCreateCmdFlags.insecure, imageCacheCreateCmdFlags.imageLayerCachePath, imageCacheCreateCmdFlags.imageCachePath, imageCacheCreateCmdFlags.layout.String() == layoutFlat, imageCacheCreateCmdFlags.cosignSignatures, ) if err != nil { return fmt.Errorf("error generating cache: %w", err) } return nil }, } const ( layoutOCI = "oci" layoutFlat = "flat" ) var imageCacheCreateCmdFlags = struct { imageCachePath string imageLayerCachePath string layout pflag.Value platform []string images []string insecure bool force bool cosignSignatures bool }{ layout: flags.StringChoice(layoutOCI, layoutFlat), } // imageCacheServeCmd represents the image cache serve command. var imageCacheServeCmd = &cobra.Command{ Use: "cache-serve", Short: "Serve an OCI image cache directory over HTTP(S) as a container registry", Long: `Serve an OCI image cache directory over HTTP(S) as a container registry`, Example: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { ctx, cancel := signal.NotifyContext(cmd.Context(), os.Interrupt) defer cancel() development, err := zap.NewDevelopment() if err != nil { return fmt.Errorf("failed to create development logger: %w", err) } if err = generateMirrorsConfigPatch( imageCacheServeCmdFlags.address, imageCacheServeCmdFlags.mirrors, imageCacheServeCmdFlags.tlsCertFile != "" && imageCacheServeCmdFlags.tlsKeyFile != "", ); err != nil { development.Error("failed to generate Talos config patch for registry mirrors", zap.Error(err)) } it := func(yield func(string) bool) { for _, root := range []string{imageCacheServeCmdFlags.imageCachePath} { if !yield(root) { return } } } return registry.NewService(registry.NewMultiPathFS(it), development).Run( ctx, registry.WithTLS( imageCacheServeCmdFlags.tlsCertFile, imageCacheServeCmdFlags.tlsKeyFile, ), registry.WithAddress(imageCacheServeCmdFlags.address), ) }, } //nolint:gocyclo func generateMirrorsConfigPatch(addr string, mirrors []string, secure bool) error { addresses := []string{} host, port, err := net.SplitHostPort(addr) if err != nil { return err } if host == "" || host == "0.0.0.0" || host == "[::]" { // list all IPs for the host ips, err := net.InterfaceAddrs() if err != nil { return err } for _, addr := range ips { if ipnet, ok := addr.(*net.IPNet); ok { if ipnet.IP.IsLoopback() { continue } addresses = append(addresses, net.JoinHostPort(ipnet.IP.String(), port)) } if ipa, ok := addr.(*net.IPAddr); ok { if ipa.IP.IsLoopback() { continue } addresses = append(addresses, net.JoinHostPort(ipa.IP.String(), port)) } } } else { addresses = []string{net.JoinHostPort(host, port)} } if port == "0" { return nil // we do not generate patch for dynamic ports } patches := make([]config.Document, 0, len(mirrors)) prefix := "http://" if secure { prefix = "https://" } for _, mirror := range mirrors { patch := cri.NewRegistryMirrorConfigV1Alpha1(mirror) patch.RegistryEndpoints = []cri.RegistryEndpoint{} for _, endpoint := range addresses { patch.RegistryEndpoints = append(patch.RegistryEndpoints, cri.RegistryEndpoint{ EndpointURL: meta.URL{URL: ensure.Value(url.Parse(prefix + endpoint))}, }) } patches = append(patches, patch) } ctr, err := container.New(patches...) if err != nil { return err } patchBytes, err := ctr.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return err } const patchFile = "image-cache-mirrors-patch.yaml" log.Printf("writing config patch to %s", patchFile) return os.WriteFile(patchFile, patchBytes, 0o644) } var imageCacheServeCmdFlags struct { imageCachePath string address string mirrors []string tlsCertFile string tlsKeyFile string } // imageCacheCertGenCmd represents the image cache tls certificate generation command. var imageCacheCertGenCmd = &cobra.Command{ Use: "cache-cert-gen", Short: "Generate TLS certificates and CA patch required for securing image cache to Talos communication", Long: `Generate TLS certificates and CA patch required for securing image cache to Talos communication`, Example: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { caPEM, certPEM, keyPEM, err := mgmthelpers.GenerateSelfSignedCert( imageCacheCertGenCmdFlags.advertisedAddresses, imageCacheCertGenCmdFlags.advertisedNames, ) if err != nil { return nil } if err = generateCAConfigPatch(caPEM); err != nil { return err } if err := os.WriteFile(imageCacheCertGenCmdFlags.tlsCaFile, caPEM, 0o644); err != nil { return err } if err := os.WriteFile(imageCacheCertGenCmdFlags.tlsCertFile, certPEM, 0o644); err != nil { return err } if err := os.WriteFile(imageCacheCertGenCmdFlags.tlsKeyFile, keyPEM, 0o600); err != nil { return err } return nil }, } func generateCAConfigPatch(caPEM []byte) error { patch := security.NewTrustedRootsConfigV1Alpha1() patch.MetaName = "image-cache-ca" patch.Certificates = string(caPEM) ctr, err := container.New(patch) if err != nil { return err } patchBytes, err := ctr.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return err } const patchFile = "image-cache-patch.yaml" log.Printf("writing config patch to %s", patchFile) return os.WriteFile(patchFile, patchBytes, 0o644) } var imageCacheCertGenCmdFlags struct { advertisedAddresses []net.IP advertisedNames []string tlsCaFile string tlsCertFile string tlsKeyFile string } func init() { imageCmd.PersistentFlags().StringVar(&imageCmdFlags.namespace, "namespace", "cri", "namespace to use: \"system\" (etcd and kubelet images), \"cri\" for all Kubernetes workloads, \"inmem\" for in-memory containerd instance", ) addCommand(imageCmd) imageCmd.AddCommand(imageListCmd) imageCmd.AddCommand(imagePullCmd) imageCmd.AddCommand(imageRemoveCmd) imageCmd.AddCommand(imageTalosBundleCmd) imageTalosBundleCmd.PersistentFlags().BoolVar(&imageTalosBundleCmdFlags.overlays, "overlays", true, "Include images that belong to Talos overlays") imageTalosBundleCmd.PersistentFlags().BoolVar(&imageTalosBundleCmdFlags.extensions, "extensions", true, "Include images that belong to Talos extensions") imageCmd.AddCommand(imageK8sBundleCmd) imageK8sBundleCmd.PersistentFlags().Var(imageK8sBundleCmdFlags.k8sVersion, "k8s-version", "Kubernetes semantic version") imageK8sBundleCmd.PersistentFlags().Var(imageK8sBundleCmdFlags.etcdVersion, "etcd-version", "ETCD semantic version") imageK8sBundleCmd.PersistentFlags().Var(imageK8sBundleCmdFlags.flannelVersion, "flannel-version", "Flannel CNI semantic version") imageK8sBundleCmd.PersistentFlags().Var(imageK8sBundleCmdFlags.corednsVersion, "coredns-version", "CoreDNS semantic version") imageK8sBundleCmd.PersistentFlags().Var(imageK8sBundleCmdFlags.kubeNetworkPoliciesVersion, "kube-network-policies-version", "kube-network-policies semantic version") imageCmd.AddCommand(imageCacheCreateCmd) imageCacheCreateCmd.PersistentFlags().StringVar(&imageCacheCreateCmdFlags.imageCachePath, "image-cache-path", "", "directory to save the image cache in OCI format") imageCacheCreateCmd.MarkPersistentFlagRequired("image-cache-path") //nolint:errcheck imageCacheCreateCmd.PersistentFlags().StringVar(&imageCacheCreateCmdFlags.imageLayerCachePath, "image-layer-cache-path", "", "directory to save the image layer cache") imageCacheCreateCmd.PersistentFlags().Var(imageCacheCreateCmdFlags.layout, "layout", "Specifies the cache layout format: \"oci\" for an OCI image layout directory, or \"flat\" for a registry-like flat file structure") imageCacheCreateCmd.PersistentFlags().StringSliceVar(&imageCacheCreateCmdFlags.platform, "platform", []string{"linux/amd64"}, "platform to use for the cache") imageCacheCreateCmd.PersistentFlags().StringSliceVar(&imageCacheCreateCmdFlags.images, "images", nil, "images to cache") imageCacheCreateCmd.MarkPersistentFlagRequired("images") //nolint:errcheck imageCacheCreateCmd.PersistentFlags().BoolVar(&imageCacheCreateCmdFlags.insecure, "insecure", false, "allow insecure registries") imageCacheCreateCmd.PersistentFlags().BoolVar(&imageCacheCreateCmdFlags.force, "force", false, "force overwrite of existing image cache") imageCacheCreateCmd.PersistentFlags().BoolVar(&imageCacheCreateCmdFlags.cosignSignatures, "cosign-signatures", true, "pull and cache cosign signatures for images") imageCmd.AddCommand(imageCacheServeCmd) imageCacheServeCmd.PersistentFlags().StringVar(&imageCacheServeCmdFlags.imageCachePath, "image-cache-path", "", "directory to save the image cache in flat format") imageCacheServeCmd.MarkPersistentFlagRequired("image-cache-path") //nolint:errcheck imageCacheServeCmd.PersistentFlags().StringVar(&imageCacheServeCmdFlags.address, "address", constants.RegistrydListenAddress, "address to serve the registry on") imageCacheServeCmd.PersistentFlags().StringSliceVar(&imageCacheServeCmdFlags.mirrors, "mirror", []string{"docker.io", "ghcr.io", "registry.k8s.io"}, "list of registry mirrors to add to the Talos config patch") imageCacheServeCmd.PersistentFlags().StringVar(&imageCacheServeCmdFlags.tlsCertFile, "tls-cert-file", "", "TLS certificate file to use for serving") imageCacheServeCmd.PersistentFlags().StringVar(&imageCacheServeCmdFlags.tlsKeyFile, "tls-key-file", "", "TLS key file to use for serving") imageCmd.AddCommand(imageCacheCertGenCmd) imageCacheCertGenCmd.PersistentFlags().StringVar(&imageCacheCertGenCmdFlags.tlsCaFile, "tls-ca-file", "ca.crt", "TLS certificate authority file") imageCacheCertGenCmd.PersistentFlags().StringVar(&imageCacheCertGenCmdFlags.tlsCertFile, "tls-cert-file", "tls.crt", "TLS certificate file to use for serving") imageCacheCertGenCmd.PersistentFlags().StringVar(&imageCacheCertGenCmdFlags.tlsKeyFile, "tls-key-file", "tls.key", "TLS key file to use for serving") imageCacheCertGenCmd.PersistentFlags().IPSliceVar(&imageCacheCertGenCmdFlags.advertisedAddresses, "advertised-address", []net.IP{}, "The addresses to advertise.") imageCacheCertGenCmd.PersistentFlags().StringSliceVar(&imageCacheCertGenCmdFlags.advertisedNames, "advertised-name", []string{}, "The DNS names to advertise.") imageIntegrationCmd.MarkPersistentFlagRequired("advertised-address") //nolint:errcheck imageCmd.AddCommand(imageIntegrationCmd) imageIntegrationCmd.PersistentFlags().StringVar(&imageIntegrationCmdFlags.installerTag, "installer-tag", "", "tag of the installer image to use") imageIntegrationCmd.MarkPersistentFlagRequired("installer-tag") //nolint:errcheck imageIntegrationCmd.PersistentFlags().StringVar(&imageIntegrationCmdFlags.talosTag, "talos-tag", version.Tag, "tag of the installer image to use") imageIntegrationCmd.PersistentFlags().StringVar(&imageIntegrationCmdFlags.registryAndUser, "registry-and-user", "", "registry and user to use for the images") imageIntegrationCmd.MarkPersistentFlagRequired("registry-and-user") //nolint:errcheck } ================================================ FILE: cmd/talosctl/cmd/talos/inspect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/formatters" ) // inspectCmd represents the inspect command. var inspectCmd = &cobra.Command{ Use: "inspect", Short: "Inspect internals of Talos", Long: ``, } var inspectDependenciesCmdFlags struct { withResources bool } // inspectDependenciesCmd represents the inspect dependencies command. var inspectDependenciesCmd = &cobra.Command{ Use: "dependencies", Short: "Inspect controller-resource dependencies as graphviz graph.", Long: `Inspect controller-resource dependencies as graphviz graph. Pipe the output of the command through the "dot" program (part of graphviz package) to render the graph: talosctl inspect dependencies | dot -Tpng > graph.png `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "inspect dependencies"); err != nil { return err } resp, err := c.Inspect.ControllerRuntimeDependencies(ctx) if err != nil { if resp == nil { return fmt.Errorf("error getting controller runtime dependencies: %s", err) } cli.Warning("%s", err) } return formatters.RenderGraph(ctx, c, resp, os.Stdout, inspectDependenciesCmdFlags.withResources) }) }, } func init() { addCommand(inspectCmd) inspectCmd.AddCommand(inspectDependenciesCmd) inspectDependenciesCmd.Flags().BoolVar(&inspectDependenciesCmdFlags.withResources, "with-resources", false, "display live resource information with dependencies") } ================================================ FILE: cmd/talosctl/cmd/talos/install.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "io" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/reporter" ) var installCmdFlags struct { imageCmdFlagsType installerImage string } var installCmd = &cobra.Command{ Use: "install ", Short: "Install Talos to disk on the target node", Long: ``, Args: cobra.ExactArgs(1), // TODO: This API is not available in maintenance mode, and once the system is fully running, installation is not relevant. // Requires https://github.com/siderolabs/talos/issues/12702 Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return WithClientMaintenance(nil, func(ctx context.Context, c *client.Client) error { return installInternal(ctx, c, args[0]) }) }, } //nolint:gocyclo func installInternal(ctx context.Context, c *client.Client, disk string) error { containerdInstance, err := installCmdFlags.containerdInstance() if err != nil { return err } stream, err := c.LifecycleClient.Install(ctx, &machine.LifecycleServiceInstallRequest{ Containerd: containerdInstance, Source: &machine.InstallArtifactsSource{ ImageName: installCmdFlags.installerImage, }, Destination: &machine.InstallDestination{ Disk: disk, }, }) if err != nil { return fmt.Errorf("error starting install: %w", err) } rep := reporter.New() exited := false for { resp, err := stream.Recv() if err != nil { if errors.Is(err, io.EOF) { break } return fmt.Errorf("error during install: %w", err) } switch payload := resp.GetProgress().GetResponse().(type) { case *machine.LifecycleServiceInstallProgress_Message: if rep.IsColorized() { rep.Report(reporter.Update{ Message: payload.Message, Status: reporter.StatusRunning, }) } case *machine.LifecycleServiceInstallProgress_ExitCode: exited = true if payload.ExitCode != 0 { rep.Report(reporter.Update{ Message: fmt.Sprintf("install failed with exit code %d", payload.ExitCode), Status: reporter.StatusError, }) return fmt.Errorf("install failed with exit code %d", payload.ExitCode) } rep.Report(reporter.Update{ Message: "install completed successfully", Status: reporter.StatusSucceeded, }) } } if !exited { rep.Report(reporter.Update{ Message: "install stream closed without exit code", Status: reporter.StatusError, }) } return nil } func init() { installCmd.Flags().StringVar(&installCmdFlags.namespace, "namespace", "system", "namespace to use: \"system\" (etcd and kubelet images), \"cri\" for all Kubernetes workloads, \"inmem\" for in-memory containerd instance", ) installCmd.Flags().StringVarP(&installCmdFlags.installerImage, "image", "i", fmt.Sprintf("%s/%s/installer:%s", images.Registry, images.Username, version.Trim(version.Tag)), "the container image to use for performing the install") addCommand(installCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/interfaces.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/client" ) // interfacesCmd represents the net interfaces command. var interfacesCmd = &cobra.Command{ Use: "interfaces", Short: "List network interfaces", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { return errors.New("`talosctl interfaces` is deprecated, please use `talosctl get addresses` and `talosctl get links` instead") }) }, } func init() { addCommand(interfacesCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/kubeconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bufio" "context" "errors" "fmt" "io/fs" "os" "path/filepath" "strings" "github.com/mattn/go-isatty" "github.com/siderolabs/go-kubeconfig" "github.com/spf13/cobra" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/client" ) const stdoutOutput = "-" var kubeconfigFlags struct { force bool forceContextName string merge bool } // kubeconfigCmd represents the kubeconfig command. var kubeconfigCmd = &cobra.Command{ Use: "kubeconfig [local-path]", Short: "Download the admin kubeconfig from the node", Long: `Download the admin kubeconfig from the node. If merge flag is true, config will be merged with ~/.kube/config or [local-path] if specified. Otherwise, kubeconfig will be written to PWD or [local-path] if specified. If merge flag is false and [local-path] is "-", config will be written to stdout.`, Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "kubeconfig"); err != nil { return err } var localPath string if len(args) == 0 { // no path given, use defaults var err error if kubeconfigFlags.merge { localPath, err = kubeconfig.SinglePath() if err != nil { return err } } else { localPath, err = os.Getwd() if err != nil { return fmt.Errorf("error getting current working directory: %s", err) } } } else { localPath = args[0] } localPath = filepath.Clean(localPath) st, err := os.Stat(localPath) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error checking path %q: %w", localPath, err) } err = os.MkdirAll(filepath.Dir(localPath), 0o755) if err != nil { return err } } else if st.IsDir() { // only dir name was given, append `kubeconfig` by default localPath = filepath.Join(localPath, "kubeconfig") } _, err = os.Stat(localPath) if err == nil && !(kubeconfigFlags.force || kubeconfigFlags.merge) { return fmt.Errorf("kubeconfig file already exists, use --force to overwrite: %q", localPath) } else if err != nil { if errors.Is(err, fs.ErrNotExist) { // merge doesn't make sense if target path doesn't exist kubeconfigFlags.merge = false } else { return fmt.Errorf("error checking path %q: %w", localPath, err) } } r, err := c.KubeconfigRaw(ctx) if err != nil { return fmt.Errorf("error copying: %w", err) } defer r.Close() //nolint:errcheck data, err := helpers.ExtractFileFromTarGz("kubeconfig", r) if err != nil { return err } if kubeconfigFlags.merge { return extractAndMerge(data, localPath) } if localPath == stdoutOutput { _, err = os.Stdout.Write(data) return err } return os.WriteFile(localPath, data, 0o600) }) }, } func extractAndMerge(data []byte, localPath string) error { config, err := clientcmd.Load(data) if err != nil { return err } merger, err := kubeconfig.Load(localPath) if err != nil { return err } interactive := isatty.IsTerminal(os.Stdout.Fd()) err = merger.Merge(config, kubeconfig.MergeOptions{ ActivateContext: true, ForceContextName: kubeconfigFlags.forceContextName, OutputWriter: os.Stdout, ConflictHandler: func(component kubeconfig.ConfigComponent, name string) (kubeconfig.ConflictDecision, error) { if kubeconfigFlags.force { return kubeconfig.OverwriteDecision, nil } if !interactive { return kubeconfig.RenameDecision, nil } return askOverwriteOrRename(fmt.Sprintf("%s %q already exists", component, name)) }, }) if err != nil { return err } return merger.Write(localPath) } func askOverwriteOrRename(prompt string) (kubeconfig.ConflictDecision, error) { reader := bufio.NewReader(os.Stdin) for { fmt.Printf("%s [(r)ename/(o)verwrite]: ", prompt) response, err := reader.ReadString('\n') if err != nil { return "", err } switch strings.ToLower(strings.TrimSpace(response)) { case "overwrite", "o": return kubeconfig.OverwriteDecision, nil case "rename", "r": return kubeconfig.RenameDecision, nil } } } func init() { kubeconfigCmd.Flags().BoolVarP(&kubeconfigFlags.force, "force", "f", false, "Force overwrite of kubeconfig if already present, force overwrite on kubeconfig merge") kubeconfigCmd.Flags().StringVar(&kubeconfigFlags.forceContextName, "force-context-name", "", "Force context name for kubeconfig merge") kubeconfigCmd.Flags().BoolVarP(&kubeconfigFlags.merge, "merge", "m", true, "Merge with existing kubeconfig") addCommand(kubeconfigCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/lifecycle/lifecycle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package lifecycle implements image install progress reporting. package lifecycle import ( "fmt" "maps" "slices" "sort" "strings" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/reporter" ) // ProgressWriter writes install progress updates to a reporter. type ProgressWriter struct { // ongoingInstalls keeps track of ongoing install jobs per node. ongoingInstalls map[string]installJob } // UpdateJob updates the progress of a pull job for a given node and layer ID. // // It is supposed to be called whenever there is a progress update for a layer pull. func (w *ProgressWriter) UpdateJob(node string, status *machine.LifecycleServiceInstallProgress) { if w.ongoingInstalls == nil { w.ongoingInstalls = make(map[string]installJob) } w.ongoingInstalls[node] = installJob{Status: status} } // PrintLayerProgress prints the current layer pull progress to the reporter. func (w *ProgressWriter) PrintLayerProgress(rep *reporter.Reporter) { nodes := slices.Collect(maps.Keys(w.ongoingInstalls)) sort.Strings(nodes) sb := strings.Builder{} for _, node := range nodes { sb.WriteString(node + ": ") fmt.Fprintf(&sb, "%s\n", w.ongoingInstalls[node].Status.Fmt()) } rep.Report(reporter.Update{ Message: sb.String(), Status: reporter.StatusRunning, }) } type installJob struct { Status *machine.LifecycleServiceInstallProgress } ================================================ FILE: cmd/talosctl/cmd/talos/list.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bytes" "context" "errors" "fmt" "os" "strconv" "strings" "text/tabwriter" "time" humanize "github.com/dustin/go-humanize" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) const sixMonths = 6 * time.Hour * 24 * 30 var ( long bool recurse bool recursionDepth int32 humanizeFlag bool types []string ) // lsCmd represents the ls command. var lsCmd = &cobra.Command{ Use: "list [path]", Aliases: []string{"ls"}, Short: "Retrieve a directory listing", Long: ``, Args: cobra.MaximumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } return completePathFromNode(toComplete), cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { if recurse && recursionDepth != 1 { return errors.New("only one of flags --recurse and --depth can be specified at the same time") } return WithClient(func(ctx context.Context, c *client.Client) error { rootDir := "/" if len(args) > 0 { rootDir = args[0] } // handle all variants: --type=f,l; -tfl; etc var reqTypes []machineapi.ListRequest_Type for _, typ := range types { for _, t := range typ { // handle both `find -type X` and os.FileMode.String() designations switch t { case 'f': reqTypes = append(reqTypes, machineapi.ListRequest_REGULAR) case 'd': reqTypes = append(reqTypes, machineapi.ListRequest_DIRECTORY) case 'l', 'L': reqTypes = append(reqTypes, machineapi.ListRequest_SYMLINK) default: return fmt.Errorf("invalid file type: %s", string(t)) } } } if recurse { recursionDepth = -1 } stream, err := c.LS(ctx, &machineapi.ListRequest{ Root: rootDir, Recurse: recursionDepth > 1 || recurse, RecursionDepth: recursionDepth, Types: reqTypes, ReportXattrs: long, }) if err != nil { return fmt.Errorf("error fetching logs: %s", err) } if !long { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNAME") defer w.Flush() //nolint:errcheck return helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { if info.Error != "" { return helpers.NonFatalError(fmt.Errorf("%s: error reading file %s: %s", node, info.Name, info.Error)) } if !multipleNodes { fmt.Println(info.RelativeName) } else { fmt.Fprintf(w, "%s\t%s\n", node, info.RelativeName, ) } return nil }) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) defer w.Flush() //nolint:errcheck fmt.Fprintln(w, "NODE\tMODE\tUID\tGID\tSIZE(B)\tLASTMOD\tLABEL\tNAME") return helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { if info.Error != "" { return helpers.NonFatalError(fmt.Errorf("%s: error reading file %s: %s", node, info.Name, info.Error)) } display := info.RelativeName if info.Link != "" { display += " -> " + info.Link } size := strconv.FormatInt(info.Size, 10) if humanizeFlag { size = humanize.Bytes(uint64(info.Size)) } timestamp := time.Unix(info.Modified, 0) timestampFormatted := "" if humanizeFlag { timestampFormatted = humanize.Time(timestamp) } else { if time.Since(timestamp) < sixMonths { timestampFormatted = timestamp.Format("Jan _2 15:04:05") } else { timestampFormatted = timestamp.Format("Jan _2 2006 15:04") } } label := "" if info.Xattrs != nil { for _, l := range info.Xattrs { if l.Name == "security.selinux" { label = string(bytes.Trim(l.Data, "\x00\n")) break } } } fmt.Fprintf(w, "%s\t%s\t%d\t%d\t%s\t%s\t%s\t%s\n", node, os.FileMode(info.Mode).String(), info.Uid, info.Gid, size, timestampFormatted, label, display, ) return nil }) }) }, } func init() { typesHelp := strings.Join([]string{ "filter by specified types:", "f" + "\t" + "regular file", "d" + "\t" + "directory", "l, L" + "\t" + "symbolic link", }, "\n") lsCmd.Flags().BoolVarP(&long, "long", "l", false, "display additional file details") lsCmd.Flags().BoolVarP(&recurse, "recurse", "r", false, "recurse into subdirectories") lsCmd.Flags().BoolVarP(&humanizeFlag, "humanize", "H", false, "humanize size and time in the output") lsCmd.Flags().Int32VarP(&recursionDepth, "depth", "d", 1, "maximum recursion depth") lsCmd.Flags().StringSliceVarP(&types, "type", "t", nil, typesHelp) addCommand(lsCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/logs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bufio" "context" "fmt" "io" "os" "sync" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) var ( follow bool tailLines int32 ) var logsCmd = &cobra.Command{ Use: "logs ", Short: "Retrieve logs for a service", Long: ``, Args: cobra.ExactArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } if kubernetesFlag { return getContainersFromNode(kubernetesFlag), cobra.ShellCompDirectiveNoFileComp } return mergeSuggestions(getServiceFromNode(), getContainersFromNode(kubernetesFlag), getLogsContainers()), cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var ( namespace string driver common.ContainerDriver ) if kubernetesFlag { namespace = constants.K8sContainerdNamespace driver = common.ContainerDriver_CRI } else { namespace = constants.SystemContainerdNamespace driver = common.ContainerDriver_CONTAINERD } stream, err := c.Logs(ctx, namespace, driver, args[0], follow, tailLines) if err != nil { return fmt.Errorf("error fetching logs: %s", err) } defaultNode := client.RemotePeer(stream.Context()) respCh, errCh := newLineSlicer(stream) var gotErrors bool for data := range respCh { if data.Metadata != nil && data.Metadata.Error != "" { _, err = fmt.Fprintf(os.Stderr, "ERROR: %s\n", data.Metadata.Error) if err != nil { return err } gotErrors = true continue } node := defaultNode if data.Metadata != nil && data.Metadata.Hostname != "" { node = data.Metadata.Hostname } _, err = fmt.Printf("%s: %s\n", node, data.Bytes) if err != nil { return err } } if err = <-errCh; err != nil { return fmt.Errorf("error getting logs: %v", err) } if gotErrors { os.Exit(1) } return nil }) }, } // lineSlicer splits random chunks of bytes coming from nodes into a stream // of lines aggregated per node. type lineSlicer struct { respCh chan *common.Data errCh chan error pipes map[string]*io.PipeWriter wg sync.WaitGroup } func newLineSlicer(stream machine.MachineService_LogsClient) (chan *common.Data, chan error) { slicer := &lineSlicer{ respCh: make(chan *common.Data), errCh: make(chan error, 1), pipes: map[string]*io.PipeWriter{}, } go slicer.run(stream) return slicer.respCh, slicer.errCh } func (slicer *lineSlicer) chopper(r io.Reader, hostname string) { defer slicer.wg.Done() scanner := bufio.NewScanner(r) for scanner.Scan() { line := scanner.Bytes() line = xslices.CopyN(line, len(line)) slicer.respCh <- &common.Data{ Metadata: &common.Metadata{ Hostname: hostname, }, Bytes: line, } } } func (slicer *lineSlicer) getPipe(node string) *io.PipeWriter { pipe, ok := slicer.pipes[node] if !ok { var piper *io.PipeReader piper, pipe = io.Pipe() slicer.wg.Add(1) go slicer.chopper(piper, node) slicer.pipes[node] = pipe } return pipe } func (slicer *lineSlicer) cleanupChoppers() { for _, p := range slicer.pipes { _ = p.Close() //nolint:errcheck } slicer.wg.Wait() } func (slicer *lineSlicer) run(stream machine.MachineService_LogsClient) { defer close(slicer.errCh) defer close(slicer.respCh) defer slicer.cleanupChoppers() for { data, err := stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { return } slicer.errCh <- err return } if data.Metadata != nil && data.Metadata.Error != "" { // errors are delivered OOB slicer.respCh <- data continue } node := "" if data.Metadata != nil { node = data.Metadata.Hostname } _, err = slicer.getPipe(node).Write(data.Bytes) cli.Should(err) } } func getLogsContainers() []string { var result []string //nolint:errcheck WithClient( func(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.LogsContainers(ctx, grpc.Peer(&remotePeer)) if err != nil { return err } result = xslices.FlatMap(resp.Messages, func(lc *machine.LogsContainer) []string { return lc.Ids }) return nil }, ) return result } func init() { logsCmd.Flags().BoolVarP(&kubernetesFlag, "kubernetes", "k", false, "use the k8s.io containerd namespace") logsCmd.Flags().BoolVarP(&follow, "follow", "f", false, "specify if the logs should be streamed") logsCmd.Flags().Int32VarP(&tailLines, "tail", "", -1, "lines of log file to display (default is to show from the beginning)") logsCmd.Flags().Bool("use-cri", false, "use the CRI driver") logsCmd.Flags().MarkHidden("use-cri") //nolint:errcheck addCommand(logsCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/memory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "text/tabwriter" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cli" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var verbose bool // memoryCmd represents the processes command. var memoryCmd = &cobra.Command{ Use: "memory", Aliases: []string{"m", "free"}, Short: "Show memory usage", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.Memory(ctx, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error getting memory stats: %s", err) } cli.Warning("%s", err) } if verbose { verboseRender(&remotePeer, resp) } else { err = briefRender(&remotePeer, resp) if err != nil { return err } } return helpers.CheckErrors(resp.Messages...) }) }, } func briefRender(remotePeer *peer.Peer, resp *machineapi.MemoryResponse) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tTOTAL\tUSED\tFREE\tSHARED\tBUFFERS\tCACHE\tAVAILABLE") defaultNode := client.AddrFromPeer(remotePeer) for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } // Default to displaying output as MB fmt.Fprintf(w, "%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", node, msg.Meminfo.Memtotal/1024, (msg.Meminfo.Memtotal-msg.Meminfo.Memfree-msg.Meminfo.Cached-msg.Meminfo.Buffers)/1024, msg.Meminfo.Memfree/1024, msg.Meminfo.Shmem/1024, msg.Meminfo.Buffers/1024, msg.Meminfo.Cached/1024, msg.Meminfo.Memavailable/1024, ) } return w.Flush() } func verboseRender(remotePeer *peer.Peer, resp *machineapi.MemoryResponse) { defaultNode := client.AddrFromPeer(remotePeer) // Dump as /proc/meminfo for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Printf("%s: %s\n", "NODE", node) fmt.Printf("%s: %d %s\n", "MemTotal", msg.Meminfo.Memtotal, "kB") fmt.Printf("%s: %d %s\n", "MemFree", msg.Meminfo.Memfree, "kB") fmt.Printf("%s: %d %s\n", "MemAvailable", msg.Meminfo.Memavailable, "kB") fmt.Printf("%s: %d %s\n", "Buffers", msg.Meminfo.Buffers, "kB") fmt.Printf("%s: %d %s\n", "Cached", msg.Meminfo.Cached, "kB") fmt.Printf("%s: %d %s\n", "SwapCached", msg.Meminfo.Swapcached, "kB") fmt.Printf("%s: %d %s\n", "Active", msg.Meminfo.Active, "kB") fmt.Printf("%s: %d %s\n", "Inactive", msg.Meminfo.Inactive, "kB") fmt.Printf("%s: %d %s\n", "ActiveAnon", msg.Meminfo.Activeanon, "kB") fmt.Printf("%s: %d %s\n", "InactiveAnon", msg.Meminfo.Inactiveanon, "kB") fmt.Printf("%s: %d %s\n", "ActiveFile", msg.Meminfo.Activefile, "kB") fmt.Printf("%s: %d %s\n", "InactiveFile", msg.Meminfo.Inactivefile, "kB") fmt.Printf("%s: %d %s\n", "Unevictable", msg.Meminfo.Unevictable, "kB") fmt.Printf("%s: %d %s\n", "Mlocked", msg.Meminfo.Mlocked, "kB") fmt.Printf("%s: %d %s\n", "SwapTotal", msg.Meminfo.Swaptotal, "kB") fmt.Printf("%s: %d %s\n", "SwapFree", msg.Meminfo.Swapfree, "kB") fmt.Printf("%s: %d %s\n", "Dirty", msg.Meminfo.Dirty, "kB") fmt.Printf("%s: %d %s\n", "Writeback", msg.Meminfo.Writeback, "kB") fmt.Printf("%s: %d %s\n", "AnonPages", msg.Meminfo.Anonpages, "kB") fmt.Printf("%s: %d %s\n", "Mapped", msg.Meminfo.Mapped, "kB") fmt.Printf("%s: %d %s\n", "Shmem", msg.Meminfo.Shmem, "kB") fmt.Printf("%s: %d %s\n", "Slab", msg.Meminfo.Slab, "kB") fmt.Printf("%s: %d %s\n", "SReclaimable", msg.Meminfo.Sreclaimable, "kB") fmt.Printf("%s: %d %s\n", "SUnreclaim", msg.Meminfo.Sunreclaim, "kB") fmt.Printf("%s: %d %s\n", "KernelStack", msg.Meminfo.Kernelstack, "kB") fmt.Printf("%s: %d %s\n", "PageTables", msg.Meminfo.Pagetables, "kB") fmt.Printf("%s: %d %s\n", "NFSUnstable", msg.Meminfo.Nfsunstable, "kB") fmt.Printf("%s: %d %s\n", "Bounce", msg.Meminfo.Bounce, "kB") fmt.Printf("%s: %d %s\n", "WritebackTmp", msg.Meminfo.Writebacktmp, "kB") fmt.Printf("%s: %d %s\n", "CommitLimit", msg.Meminfo.Commitlimit, "kB") fmt.Printf("%s: %d %s\n", "CommittedAS", msg.Meminfo.Committedas, "kB") fmt.Printf("%s: %d %s\n", "VmallocTotal", msg.Meminfo.Vmalloctotal, "kB") fmt.Printf("%s: %d %s\n", "VmallocUsed", msg.Meminfo.Vmallocused, "kB") fmt.Printf("%s: %d %s\n", "VmallocChunk", msg.Meminfo.Vmallocchunk, "kB") fmt.Printf("%s: %d %s\n", "HardwareCorrupted", msg.Meminfo.Hardwarecorrupted, "kB") fmt.Printf("%s: %d %s\n", "AnonHugePages", msg.Meminfo.Anonhugepages, "kB") fmt.Printf("%s: %d %s\n", "ShmemHugePages", msg.Meminfo.Shmemhugepages, "kB") fmt.Printf("%s: %d %s\n", "ShmemPmdMapped", msg.Meminfo.Shmempmdmapped, "kB") fmt.Printf("%s: %d %s\n", "CmaTotal", msg.Meminfo.Cmatotal, "kB") fmt.Printf("%s: %d %s\n", "CmaFree", msg.Meminfo.Cmafree, "kB") fmt.Printf("%s: %d\n", "HugePagesTotal", msg.Meminfo.Hugepagestotal) fmt.Printf("%s: %d\n", "HugePagesFree", msg.Meminfo.Hugepagesfree) fmt.Printf("%s: %d\n", "HugePagesRsvd", msg.Meminfo.Hugepagesrsvd) fmt.Printf("%s: %d\n", "HugePagesSurp", msg.Meminfo.Hugepagessurp) fmt.Printf("%s: %d %s\n", "Hugepagesize", msg.Meminfo.Hugepagesize, "kB") fmt.Printf("%s: %d %s\n", "DirectMap4k", msg.Meminfo.Directmap4K, "kB") fmt.Printf("%s: %d %s\n", "DirectMap2M", msg.Meminfo.Directmap2M, "kB") fmt.Printf("%s: %d %s\n", "DirectMap1G", msg.Meminfo.Directmap1G, "kB") } } func init() { memoryCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "display extended memory statistics") addCommand(memoryCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "strconv" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/client" ) var metaCmdFlags struct { insecure bool } var metaCmd = &cobra.Command{ Use: "meta", Short: "Write and delete keys in the META partition", Long: ``, Args: cobra.NoArgs, } var metaWriteCmd = &cobra.Command{ Use: "write key value", Short: "Write a key-value pair to the META partition.", Long: ``, Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { fn := func(ctx context.Context, c *client.Client) error { key, err := strconv.ParseUint(args[0], 0, 8) if err != nil { return err } return c.MetaWrite(ctx, uint8(key), []byte(args[1])) } if metaCmdFlags.insecure { return WithClientMaintenance(nil, fn) } return WithClient(fn) }, } var metaDeleteCmd = &cobra.Command{ Use: "delete key", Short: "Delete a key from the META partition.", Long: ``, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { fn := func(ctx context.Context, c *client.Client) error { key, err := strconv.ParseUint(args[0], 0, 8) if err != nil { return err } return c.MetaDelete(ctx, uint8(key)) } if metaCmdFlags.insecure { return WithClientMaintenance(nil, fn) } return WithClient(fn) }, } func init() { metaCmd.PersistentFlags().BoolVarP(&metaCmdFlags.insecure, "insecure", "i", false, "write|delete meta using the insecure (encrypted with no auth) maintenance service") metaCmd.AddCommand(metaWriteCmd) metaCmd.AddCommand(metaDeleteCmd) addCommand(metaCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/mounts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/formatters" ) // mountsCmd represents the mounts command. var mountsCmd = &cobra.Command{ Use: "mounts", Aliases: []string{"mount"}, Short: "List mounts", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.Mounts(ctx, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error getting mount information: %s", err) } cli.Warning("%s", err) } return formatters.RenderMounts(resp, os.Stdout, &remotePeer) }) }, } func init() { addCommand(mountsCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/multiplex/multiplex.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package multiplex implements client-side multiplexing helpers. package multiplex // Response represents a multiplexed response from a specific node. type Response[ResponseT any] struct { Node string Payload ResponseT Err error } ================================================ FILE: cmd/talosctl/cmd/talos/multiplex/stream.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package multiplex import ( "context" "errors" "io" "sync" "github.com/siderolabs/gen/channel" "google.golang.org/grpc" "github.com/siderolabs/talos/pkg/machinery/client" ) // Streaming initiates a multiplexed streaming gRPC client call to multiple nodes. func Streaming[ResponseT any](ctx context.Context, nodes []string, initiate func(context.Context) (grpc.ServerStreamingClient[ResponseT], error)) <-chan Response[*ResponseT] { responseCh := make(chan Response[*ResponseT]) var wg sync.WaitGroup for _, node := range nodes { wg.Go(func() { stream, err := initiate(client.WithNode(ctx, node)) if err != nil { channel.SendWithContext(ctx, responseCh, Response[*ResponseT]{ Node: node, Err: err, }, ) return } for { payload, err := stream.Recv() if err != nil { if errors.Is(err, io.EOF) { return } channel.SendWithContext(ctx, responseCh, Response[*ResponseT]{ Node: node, Err: err, }, ) return } if !channel.SendWithContext(ctx, responseCh, Response[*ResponseT]{ Node: node, Payload: payload, }, ) { return } } }) } go func() { wg.Wait() close(responseCh) }() return responseCh } ================================================ FILE: cmd/talosctl/cmd/talos/multiplex/unary.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package multiplex import ( "context" "sync" "github.com/siderolabs/gen/channel" "github.com/siderolabs/talos/pkg/machinery/client" ) // Unary initiates a multiplexed unary gRPC client call to multiple nodes. func Unary[ResponseT any](ctx context.Context, nodes []string, initiate func(context.Context) (*ResponseT, error)) <-chan Response[*ResponseT] { responseCh := make(chan Response[*ResponseT]) var wg sync.WaitGroup for _, node := range nodes { wg.Go(func() { response, err := initiate(client.WithNode(ctx, node)) if err != nil { channel.SendWithContext(ctx, responseCh, Response[*ResponseT]{ Node: node, Err: err, }, ) return } channel.SendWithContext(ctx, responseCh, Response[*ResponseT]{ Node: node, Payload: response, }, ) }) } go func() { wg.Wait() close(responseCh) }() return responseCh } ================================================ FILE: cmd/talosctl/cmd/talos/netstat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "strconv" "strings" "text/tabwriter" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) var netstatCmdFlags struct { verbose bool extend bool pid bool timers bool listening bool all bool pods bool tcp bool udp bool udplite bool raw bool ipv4 bool ipv6 bool } type netstat struct { client *client.Client NodeNetNSPods map[string]map[string]string } // netstatCmd represents the netstat command. var netstatCmd = &cobra.Command{ Use: "netstat", Aliases: []string{"ss"}, Short: "Show network connections and sockets", Long: `Show network connections and sockets. You can pass an optional argument to view a specific pod's connections. To do this, format the argument as "namespace/pod". Note that only pods with a pod network namespace are allowed. If you don't pass an argument, the command will show host connections.`, Args: cobra.MaximumNArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) > 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } var podList []string if WithClient(func(ctx context.Context, c *client.Client) error { n := netstat{ NodeNetNSPods: make(map[string]map[string]string), client: c, } err := n.getPodNetNsFromNode(ctx) if err != nil { return err } for _, netNsPods := range n.NodeNetNSPods { for _, podName := range netNsPods { podList = append(podList, podName) } } return nil }) != nil { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } return podList, cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { req := netstatFlagsToRequest() return WithClient(func(ctx context.Context, c *client.Client) (err error) { if netstatCmdFlags.pods && len(args) > 0 { return errors.New("cannot use --pods and specify a pod") } findThePod := len(args) > 0 n := netstat{ client: c, } n.NodeNetNSPods = make(map[string]map[string]string) if findThePod || netstatCmdFlags.pods { err = n.getPodNetNsFromNode(ctx) if err != nil { return err } } if findThePod { var foundNode, foundNetNs string foundNode, foundNetNs = n.findPodNetNs(args[0]) if foundNetNs == "" { cli.Fatalf("pod %s not found", args[0]) } ctx = client.WithNode(ctx, foundNode) req.Netns.Netns = []string{foundNetNs} req.Netns.Hostnetwork = false } response, err := c.Netstat(ctx, req) if err != nil { if response == nil { return err } cli.Warning("%s", err) } err = n.printNetstat(response) return err }) }, } //nolint:gocyclo func netstatFlagsToRequest() *machine.NetstatRequest { req := machine.NetstatRequest{ Feature: &machine.NetstatRequest_Feature{ Pid: netstatCmdFlags.pid, }, L4Proto: &machine.NetstatRequest_L4Proto{ Tcp: netstatCmdFlags.tcp, Tcp6: netstatCmdFlags.tcp, Udp: netstatCmdFlags.udp, Udp6: netstatCmdFlags.udp, Udplite: netstatCmdFlags.udplite, Udplite6: netstatCmdFlags.udplite, Raw: netstatCmdFlags.raw, Raw6: netstatCmdFlags.raw, }, Netns: &machine.NetstatRequest_NetNS{ Allnetns: netstatCmdFlags.pods, Hostnetwork: true, }, } switch { case netstatCmdFlags.all: req.Filter = machine.NetstatRequest_ALL case netstatCmdFlags.listening: req.Filter = machine.NetstatRequest_LISTENING default: req.Filter = machine.NetstatRequest_CONNECTED } if netstatCmdFlags.verbose { req.L4Proto.Tcp = true req.L4Proto.Tcp6 = true req.L4Proto.Udp = true req.L4Proto.Udp6 = true req.L4Proto.Udplite = true req.L4Proto.Udplite6 = true req.L4Proto.Raw = true req.L4Proto.Raw6 = true } if !req.L4Proto.Tcp && !req.L4Proto.Tcp6 && !req.L4Proto.Udp && !req.L4Proto.Udp6 && !req.L4Proto.Udplite && !req.L4Proto.Udplite6 && !req.L4Proto.Raw && !req.L4Proto.Raw6 { req.L4Proto.Tcp = true req.L4Proto.Tcp6 = true req.L4Proto.Udp = true req.L4Proto.Udp6 = true } if netstatCmdFlags.ipv4 && !netstatCmdFlags.ipv6 { req.L4Proto.Tcp6 = false req.L4Proto.Udp6 = false req.L4Proto.Udplite6 = false req.L4Proto.Raw6 = false } if netstatCmdFlags.ipv6 && !netstatCmdFlags.ipv4 { req.L4Proto.Tcp = false req.L4Proto.Udp = false req.L4Proto.Udplite = false req.L4Proto.Raw = false } return &req } func (n *netstat) getPodNetNsFromNode(ctx context.Context) (err error) { resp, err := n.client.Containers(ctx, constants.K8sContainerdNamespace, common.ContainerDriver_CRI) if err != nil { cli.Warning("error getting containers: %v", err) return err } for _, msg := range resp.Messages { for _, p := range msg.Containers { if p.NetworkNamespace == "" { continue } if p.Pid == 0 { continue } if p.Id != p.PodId { continue } if n.NodeNetNSPods[msg.Metadata.Hostname] == nil { n.NodeNetNSPods[msg.Metadata.Hostname] = make(map[string]string) } n.NodeNetNSPods[msg.Metadata.Hostname][p.NetworkNamespace] = p.Id } } return nil } func (n *netstat) findPodNetNs(findNamespaceAndPod string) (string, string) { var foundNetNs, foundNode string for node, netNSPods := range n.NodeNetNSPods { for NetNs, podName := range netNSPods { if podName == strings.ToLower(findNamespaceAndPod) { foundNetNs = NetNs foundNode = node break } } } return foundNode, foundNetNs } //nolint:gocyclo func (n *netstat) printNetstat(response *machine.NetstatResponse) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) node := "" for i, message := range response.Messages { if message.Metadata != nil && message.Metadata.Hostname != "" { node = message.Metadata.Hostname } if len(message.Connectrecord) == 0 { continue } for j, record := range message.Connectrecord { if i == 0 && j == 0 { labels := netstatSummaryLabels() if node != "" { fmt.Fprintln(w, "NODE\t"+labels) } else { fmt.Fprintln(w, labels) } } var args []any if node != "" { args = append(args, node) } state := "" if record.State != 7 { state = record.State.String() } args = append(args, []any{ record.L4Proto, strconv.FormatUint(record.Rxqueue, 10), strconv.FormatUint(record.Txqueue, 10), fmt.Sprintf("%s:%d", record.Localip, record.Localport), fmt.Sprintf("%s:%s", record.Remoteip, wildcardIfZero(record.Remoteport)), state, }...) if netstatCmdFlags.extend { args = append(args, []any{ strconv.FormatUint(uint64(record.Uid), 10), strconv.FormatUint(record.Inode, 10), }...) } if netstatCmdFlags.pid { if record.Process.Pid != 0 { args = append(args, []any{ fmt.Sprintf("%d/%s", record.Process.Pid, record.Process.Name), }...) } else { args = append(args, []any{ "-", }...) } } if netstatCmdFlags.pods { if record.Netns == "" || node == "" || n.NodeNetNSPods[node] == nil { args = append(args, []any{ "-", }...) } else { args = append(args, []any{ n.NodeNetNSPods[node][record.Netns], }...) } } if netstatCmdFlags.timers { timerwhen := strconv.FormatFloat(float64(record.Timerwhen)/100, 'f', 2, 64) args = append(args, []any{ fmt.Sprintf("%s (%s/%d/%d)", strings.ToLower(record.Tr.String()), timerwhen, record.Retrnsmt, record.Timeout), }...) } pattern := strings.Repeat("%s\t", len(args)) pattern = strings.TrimSpace(pattern) + "\n" fmt.Fprintf(w, pattern, args...) } } return w.Flush() } func netstatSummaryLabels() (labels string) { labels = strings.Join( []string{ "Proto", "Recv-Q", "Send-Q", "Local Address", "Foreign Address", "State", }, "\t") if netstatCmdFlags.extend { labels += "\t" + strings.Join( []string{ "Uid", "Inode", }, "\t") } if netstatCmdFlags.pid { labels += "\t" + "PID/Program name" } if netstatCmdFlags.pods { labels += "\t" + "Pod" } if netstatCmdFlags.timers { labels += "\t" + "Timer" } return labels } func wildcardIfZero(num uint32) string { if num == 0 { return "*" } return strconv.FormatUint(uint64(num), 10) } func init() { netstatCmd.Flags().BoolVarP(&netstatCmdFlags.verbose, "verbose", "v", false, "display sockets of all supported transport protocols") // extend is normally -e but cannot be used as this is endpoint in talosctl netstatCmd.Flags().BoolVarP(&netstatCmdFlags.extend, "extend", "x", false, "show detailed socket information") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.pid, "programs", "p", false, "show process using socket") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.timers, "timers", "o", false, "display timers") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.listening, "listening", "l", false, "display listening server sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.all, "all", "a", false, "display all sockets states (default: connected)") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.pods, "pods", "k", false, "show sockets used by Kubernetes pods") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.tcp, "tcp", "t", false, "display only TCP sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.udp, "udp", "u", false, "display only UDP sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.udplite, "udplite", "U", false, "display only UDPLite sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.raw, "raw", "w", false, "display only RAW sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.ipv4, "ipv4", "4", false, "display only ipv4 sockets") netstatCmd.Flags().BoolVarP(&netstatCmdFlags.ipv6, "ipv6", "6", false, "display only ipv6 sockets") addCommand(netstatCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/output/json.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package output import ( "encoding/json" "io" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // JSON outputs resources in JSON format. type JSON struct { withEvents bool writer io.Writer } // NewJSON initializes JSON resource output. func NewJSON(writer io.Writer) *JSON { return &JSON{ writer: writer, } } // WriteHeader implements output.Writer interface. func (j *JSON) WriteHeader(definition *meta.ResourceDefinition, withEvents bool) error { j.withEvents = withEvents return nil } // prepareEncodableData prepares the data of a resource to be encoded as JSON and populates it with some extra information. func (j *JSON) prepareEncodableData(node string, r resource.Resource, event state.EventType) (map[string]any, error) { if r.Metadata().Type() == config.MachineConfigType && r.Metadata().Annotations().Empty() { // use a temporary wrapper to adjust YAML marshaling // for backwards compatibility with versions of Talos // which incorrectly marshal MachineConfig spec as YAML document // directly r = &mcYamlRepr{r} } out, err := resource.MarshalYAML(r) if err != nil { return nil, err } yamlBytes, err := yaml.Marshal(out) if err != nil { return nil, err } var data map[string]any err = yaml.Unmarshal(yamlBytes, &data) if err != nil { return nil, err } data["node"] = node if j.withEvents { data["event"] = strings.ToLower(event.String()) } return data, nil } func writeAsIndentedJSON(wr io.Writer, data any) error { enc := json.NewEncoder(wr) enc.SetIndent("", " ") return enc.Encode(data) } // WriteResource implements output.Writer interface. func (j *JSON) WriteResource(node string, r resource.Resource, event state.EventType) error { data, err := j.prepareEncodableData(node, r, event) if err != nil { return err } return writeAsIndentedJSON(j.writer, data) } // Flush implements output.Writer interface. func (j *JSON) Flush() error { return nil } ================================================ FILE: cmd/talosctl/cmd/talos/output/jsonpath.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package output import ( "bytes" "encoding/json" "fmt" "io" "reflect" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "k8s.io/client-go/third_party/forked/golang/template" "k8s.io/client-go/util/jsonpath" ) // JSONPath outputs resources in JSONPath format. type JSONPath struct { jsonPath *jsonpath.JSONPath json *JSON writer io.Writer } // NewJSONPath initializes JSONPath resource output. func NewJSONPath(writer io.Writer, jsonPath *jsonpath.JSONPath) *JSONPath { return &JSONPath{ jsonPath: jsonPath, json: NewJSON(writer), writer: writer, } } // WriteHeader implements output.Writer interface. func (j *JSONPath) WriteHeader(definition *meta.ResourceDefinition, withEvents bool) error { return j.json.WriteHeader(definition, withEvents) } // printResult prints a reflect.Value as JSON if it's a map, array, slice or struct. // But if it's just a 'scalar' type it prints it as a mere string. func printResult(wr io.Writer, result reflect.Value) error { kind := result.Kind() if kind == reflect.Interface { kind = result.Elem().Kind() } outputJSON := kind == reflect.Map || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Struct var text []byte //nolint:prealloc // dynamic var err error if outputJSON { text, err = json.MarshalIndent(result.Interface(), "", " ") if err != nil { return err } } else { text, err = valueToText(result) } if err != nil { return err } text = append(text, '\n') if _, err = wr.Write(text); err != nil { return err } return nil } // valueToText translates reflect value to corresponding text. func valueToText(v reflect.Value) ([]byte, error) { iface, ok := template.PrintableValue(v) if !ok { return nil, fmt.Errorf("can't translate type %s to text", v.Type()) } var buffer bytes.Buffer fmt.Fprint(&buffer, iface) return buffer.Bytes(), nil } // WriteResource implements output.Writer interface. func (j *JSONPath) WriteResource(node string, r resource.Resource, event state.EventType) error { data, err := j.json.prepareEncodableData(node, r, event) if err != nil { return err } results, err := j.jsonPath.FindResults(data) if err != nil { return fmt.Errorf("error finding result for jsonpath: %w", err) } j.jsonPath.EnableJSONOutput(true) for _, resultGroup := range results { for _, result := range resultGroup { err = printResult(j.writer, result) if err != nil { return fmt.Errorf("error generating jsonpath results: %w", err) } } } return nil } // Flush implements output.Writer interface. func (j *JSONPath) Flush() error { return nil } ================================================ FILE: cmd/talosctl/cmd/talos/output/jsonpath_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package output_test import ( "bytes" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "k8s.io/client-go/util/jsonpath" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/output" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) func TestWriteResource(t *testing.T) { node := "123.123.123.123" event := state.Created t.Run("prints scalar values on one line", func(tt *testing.T) { var buf bytes.Buffer // given expectedID := "myCPU" processorResource := hardware.NewProcessorInfo(expectedID) jsonPath := jsonpath.New("talos") assert.Nil(t, jsonPath.Parse("{.metadata.id}")) // when testObj := output.NewJSONPath(&buf, jsonPath) err := testObj.WriteResource(node, processorResource, event) // then assert.Nil(t, err) assert.Equal(t, expectedID+"\n", buf.String()) }) t.Run("prints complex values as JSON", func(tt *testing.T) { var buf bytes.Buffer // given expectedMetadata := `{ "coreCount": 2 } ` processorResource := hardware.NewProcessorInfo("myCPU") processorResource.TypedSpec().CoreCount = 2 jsonPath := jsonpath.New("talos") assert.Nil(t, jsonPath.Parse("{.spec}")) // when testObj := output.NewJSONPath(&buf, jsonPath) err := testObj.WriteResource(node, processorResource, event) // then assert.Nil(t, err) assert.Equal(t, expectedMetadata, buf.String()) }) } ================================================ FILE: cmd/talosctl/cmd/talos/output/output.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package output provides writers in different formats. package output import ( "fmt" "os" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/spf13/cobra" "k8s.io/client-go/util/jsonpath" ) // Writer interface. type Writer interface { WriteHeader(definition *meta.ResourceDefinition, withEvents bool) error WriteResource(node string, r resource.Resource, event state.EventType) error Flush() error } // NewWriter builds writer from type. func NewWriter(format string) (Writer, error) { writer := os.Stdout switch { case format == "table": return NewTable(writer), nil case format == "yaml": return NewYAML(writer), nil case format == "json": return NewJSON(writer), nil case strings.HasPrefix(format, "jsonpath="): path := format[len("jsonpath="):] jp := jsonpath.New("talos") if err := jp.Parse(path); err != nil { return nil, fmt.Errorf("error parsing jsonpath: %w", err) } return NewJSONPath(writer, jp), nil default: return nil, fmt.Errorf("output format %q is not supported", format) } } // CompleteOutputArg represents tab completion for `--output` argument. func CompleteOutputArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { return []string{"json", "table", "yaml", "jsonpath"}, cobra.ShellCompDirectiveNoFileComp } ================================================ FILE: cmd/talosctl/cmd/talos/output/table.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package output import ( "bytes" "fmt" "io" "slices" "strings" "text/tabwriter" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "go.yaml.in/yaml/v4" "k8s.io/client-go/util/jsonpath" ) // Table outputs resources in Table view. type Table struct { w tabwriter.Writer withEvents bool displayType string dynamicColumns []dynamicColumn } type dynamicColumn func(value any) (string, error) // NewTable initializes table resource output. func NewTable(writer io.Writer) *Table { output := &Table{} output.w.Init(writer, 0, 0, 3, ' ', 0) return output } // WriteHeader implements output.Writer interface. func (table *Table) WriteHeader(definition *meta.ResourceDefinition, withEvents bool) error { table.withEvents = withEvents fields := []string{"NAMESPACE", "TYPE", "ID", "VERSION"} if withEvents { fields = slices.Insert(fields, 0, "*") } table.displayType = definition.TypedSpec().DisplayType for _, column := range definition.TypedSpec().PrintColumns { name := column.Name fields = append(fields, strings.ToUpper(name)) expr := jsonpath.New(name) if err := expr.Parse(column.JSONPath); err != nil { return fmt.Errorf("error parsing column %q jsonpath: %w", name, err) } expr = expr.AllowMissingKeys(true) table.dynamicColumns = append(table.dynamicColumns, func(val any) (string, error) { var buf bytes.Buffer if e := expr.Execute(&buf, val); e != nil { return "", e } return buf.String(), nil }) } fields = slices.Insert(fields, 0, "NODE") _, err := fmt.Fprintln(&table.w, strings.Join(fields, "\t")) return err } // WriteResource implements output.Writer interface. func (table *Table) WriteResource(node string, r resource.Resource, event state.EventType) error { values := []string{r.Metadata().Namespace(), table.displayType, r.Metadata().ID(), r.Metadata().Version().String()} if table.withEvents { var label string switch event { case state.Created: label = "+" case state.Destroyed: label = "-" case state.Updated: label = " " case state.Bootstrapped, state.Errored, state.Noop: return nil } values = slices.Insert(values, 0, label) } yml, err := yaml.Marshal(r.Spec()) if err != nil { return err } var unstructured any if err = yaml.Unmarshal(yml, &unstructured); err != nil { return err } for _, dynamicColumn := range table.dynamicColumns { var value string value, err = dynamicColumn(unstructured) if err != nil { return err } values = append(values, value) } values = slices.Insert(values, 0, node) _, err = fmt.Fprintln(&table.w, strings.Join(values, "\t")) return err } // Flush implements output.Writer interface. func (table *Table) Flush() error { return table.w.Flush() } ================================================ FILE: cmd/talosctl/cmd/talos/output/yaml.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package output import ( "fmt" "io" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/state" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // YAML outputs resources in YAML format. type YAML struct { needDashes bool withEvents bool writer io.Writer } // NewYAML initializes YAML resource output. func NewYAML(writer io.Writer) *YAML { return &YAML{ writer: writer, } } // WriteHeader implements output.Writer interface. func (y *YAML) WriteHeader(definition *meta.ResourceDefinition, withEvents bool) error { y.withEvents = withEvents return nil } // WriteResource implements output.Writer interface. func (y *YAML) WriteResource(node string, r resource.Resource, event state.EventType) error { if r.Metadata().Type() == config.MachineConfigType && r.Metadata().Annotations().Empty() { // use a temporary wrapper to adjust YAML marshaling // for backwards compatibility with versions of Talos // which incorrectly marshal MachineConfig spec as YAML document // directly r = &mcYamlRepr{r} } out, err := resource.MarshalYAML(r) if err != nil { return err } if y.needDashes { fmt.Fprintln(y.writer, "---") } y.needDashes = true fmt.Fprintf(y.writer, "node: %s\n", node) if y.withEvents { fmt.Fprintf(y.writer, "event: %s\n", strings.ToLower(event.String())) } return yaml.NewEncoder(y.writer).Encode(out) } // Flush implements output.Writer interface. func (y *YAML) Flush() error { return nil } type mcYamlRepr struct{ resource.Resource } func (m *mcYamlRepr) Spec() any { return &mcYamlSpec{res: m.Resource} } type mcYamlSpec struct{ res resource.Resource } func (m *mcYamlSpec) MarshalYAML() (any, error) { // this is backwards compatibility for versions of Talos which marshaled the MachineConfig spec as a YAML document // instead of putting it as string // // if try to go via yaml.Marshal path, it will cut off all documents after the first one (as there is no way to return // multiple documents from MarshalYAML), so we need to extract the original body from the resource if pb, ok := m.res.(*protobuf.Resource); ok { p, err := pb.Marshal() if err != nil { return nil, fmt.Errorf("marshal protobuf resource: %w", err) } return p.GetSpec().GetYamlSpec(), nil } out, err := yaml.Marshal(m.res.Spec()) if err != nil { return nil, err } return string(out), err } ================================================ FILE: cmd/talosctl/cmd/talos/patch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bytes" "context" "errors" "fmt" "os" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/spf13/cobra" "go.yaml.in/yaml/v4" "google.golang.org/protobuf/types/known/durationpb" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/yamlstrip" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) var patchCmdFlags struct { helpers.Mode namespace string patch []string patchFile string dryRun bool configTryTimeout time.Duration } func extractMachineConfigBody(mc resource.Resource) ([]byte, error) { if mc.Metadata().Annotations().Empty() { // this is backwards compatibility for versions of Talos which marshaled the MachineConfig spec as a YAML document // instead of putting it as string // // if try to go via yaml.Marshal path, it will cut off all documents after the first one (as there is no way to return // multiple documents from MarshalYAML), so we need to extract the original body from the resource if pb, ok := mc.(*protobuf.Resource); ok { p, err := pb.Marshal() if err != nil { return nil, fmt.Errorf("marshal protobuf resource: %w", err) } return []byte(p.GetSpec().GetYamlSpec()), nil } return yaml.Marshal(mc.Spec()) } spec, err := yaml.Marshal(mc.Spec()) if err != nil { return nil, err } var bodyStr string if err = yaml.Unmarshal(spec, &bodyStr); err != nil { return nil, err } return []byte(bodyStr), nil } func patchFn(c *client.Client, patches []configpatcher.Patch) func(context.Context, string, resource.Resource, error) error { return func(ctx context.Context, node string, mc resource.Resource, callError error) error { if callError != nil { return fmt.Errorf("%s: %w", node, callError) } if mc.Metadata().Type() != config.MachineConfigType { return fmt.Errorf("%s: unsupported resource type: %s", node, mc.Metadata().Type()) } if mc.Metadata().ID() != config.ActiveID { return nil } body, err := extractMachineConfigBody(mc) if err != nil { return err } cfg, err := configpatcher.Apply(configpatcher.WithBytes(body), patches) if err != nil { return err } patched, err := cfg.Bytes() if err != nil { return err } resp, err := c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{ Data: patched, Mode: patchCmdFlags.Mode.Mode, DryRun: patchCmdFlags.dryRun, TryModeTimeout: durationpb.New(patchCmdFlags.configTryTimeout), }) if bytes.Equal( bytes.TrimSpace(yamlstrip.Comments(patched)), bytes.TrimSpace(yamlstrip.Comments(body)), ) { fmt.Fprintln(os.Stderr, "Apply was skipped: no changes detected.") return nil } fmt.Fprintf(os.Stderr, "patched %s/%s at the node %s\n", mc.Metadata().Type(), mc.Metadata().ID(), node, ) helpers.PrintApplyResults(resp) return err } } // patchCmd represents the edit command. var patchCmd = &cobra.Command{ Use: "patch machineconfig", Short: "Patch machine configuration of a Talos node with a local patch.", Args: cobra.RangeArgs(1, 2), RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if patchCmdFlags.patchFile != "" { patchCmdFlags.patch = append(patchCmdFlags.patch, "@"+patchCmdFlags.patchFile) } if len(patchCmdFlags.patch) == 0 { return errors.New("either --patch or --patch-file should be defined") } patches, err := configpatcher.LoadPatches(patchCmdFlags.patch) if err != nil { return err } if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } for _, node := range GlobalArgs.Nodes { nodeCtx := client.WithNodes(ctx, node) if err := helpers.ForEachResource(nodeCtx, c, nil, patchFn(c, patches), patchCmdFlags.namespace, args...); err != nil { return err } } return nil }) }, } func init() { patchCmd.Flags().StringVar(&patchCmdFlags.namespace, "namespace", "", "resource namespace (default is to use default namespace per resource)") patchCmd.Flags().StringVar(&patchCmdFlags.patchFile, "patch-file", "", "a file containing a patch to be applied to the resource.") patchCmd.Flags().StringArrayVarP(&patchCmdFlags.patch, "patch", "p", nil, "the patch to be applied to the resource file, use @file to read a patch from file.") patchCmd.Flags().BoolVar(&patchCmdFlags.dryRun, "dry-run", false, "print the change summary and patch preview without applying the changes") patchCmd.Flags().DurationVar(&patchCmdFlags.configTryTimeout, "timeout", constants.ConfigTryTimeout, "the config will be rolled back after specified timeout (if try mode is selected)") helpers.AddModeFlags(&patchCmdFlags.Mode, patchCmd) addCommand(patchCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/pcap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "io" "net" "os" "strings" "syscall" "time" "github.com/gopacket/gopacket" "github.com/gopacket/gopacket/pcapgo" "github.com/spf13/cobra" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var pcapCmdFlags struct { iface string promisc bool snaplen int output string bpfFilter string duration time.Duration } // pcapCmd represents the pcap command. var pcapCmd = &cobra.Command{ Use: "pcap", Aliases: []string{"tcpdump"}, Short: "Capture the network packets from the node.", Long: `The command launches packet capture on the node and streams back the packets as raw pcap file.`, Example: `Default behavior is to decode the packets with internal decoder to stdout: talosctl pcap -i eth0 Raw pcap file can be saved with ` + "`--output`" + ` flag: talosctl pcap -i eth0 --output eth0.pcap Output can be piped to tcpdump: talosctl pcap -i eth0 -o - | tcpdump -vvv -r - BPF filter can be applied, but it has to compiled to BPF instructions first using tcpdump. Correct link type should be specified for the tcpdump: EN10MB for Ethernet links and RAW for e.g. Wireguard tunnels: talosctl pcap -i eth0 --bpf-filter "$(tcpdump -dd -y EN10MB 'tcp and dst port 80')" talosctl pcap -i kubespan --bpf-filter "$(tcpdump -dd -y RAW 'port 50000')" As packet capture is transmitted over the network, it is recommended to filter out the Talos API traffic, e.g. by excluding packets with the port 50000. `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "pcap"); err != nil { return err } if pcapCmdFlags.duration > 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, pcapCmdFlags.duration) defer cancel() } req := machine.PacketCaptureRequest{ Interface: pcapCmdFlags.iface, Promiscuous: pcapCmdFlags.promisc, } var err error req.BpfFilter, err = parseBPFInstructions(pcapCmdFlags.bpfFilter) if err != nil { return err } r, err := c.PacketCapture(ctx, &req) if err != nil { return fmt.Errorf("error copying: %w", err) } if pcapCmdFlags.output == "" { return dumpPackets(ctx, r) } var out io.Writer if pcapCmdFlags.output == "-" { out = os.Stdout } else { out, err = os.Create(pcapCmdFlags.output) if err != nil { return err } } _, err = io.Copy(out, r) if errors.Is(err, io.EOF) || client.StatusCode(err) == codes.DeadlineExceeded { err = nil } return err }) }, } // snapLength defines a snap length for the packet reading. For some reason // TF_PACKET captures more than the snap length. Tools like tcpdump ignore snaplen entirely and set their own // (https://github.com/the-tcpdump-group/tcpdump/blob/9fad826b0e487e8939325d62b7a461619b2722eb/netdissect.h#L342) // so it makes sense to do the same. const snapLength = 262144 func dumpPackets(ctx context.Context, r io.Reader) error { src, err := pcapgo.NewReader(r) if err != nil { if errors.Is(err, io.EOF) { // nothing in the capture at all return nil } return fmt.Errorf("error opening pcap reader: %w", err) } src.SetSnaplen(snapLength) forEachPacket( ctx, gopacket.NewZeroCopyPacketSource(src, src.LinkType(), gopacket.WithPool(true)), func(packet gopacket.Packet, err error) { switch err { case nil: fmt.Println(packet) default: fmt.Println("packet capture error:", err) } }, ) return nil } // parseBPFInstructions parses the BPF raw instructions in 'tcpdump -dd' format. // // Example: // // { 0x30, 0, 0, 0x00000000 }, // { 0x54, 0, 0, 0x000000f0 }, // { 0x15, 0, 8, 0x00000060 }, // //nolint:dupword func parseBPFInstructions(in string) ([]*machine.BPFInstruction, error) { in = strings.TrimSpace(in) if in == "" { return nil, nil } var result []*machine.BPFInstruction //nolint:prealloc for line := range strings.SplitSeq(in, "\n") { if line == "" { continue } ins := &machine.BPFInstruction{} n, err := fmt.Sscanf(line, "{ 0x%x, %d, %d, 0x%x },", &ins.Op, &ins.Jt, &ins.Jf, &ins.K) if err != nil { return nil, fmt.Errorf("error parsing bpf instruction %q: %w", line, err) } if n != 4 { return nil, fmt.Errorf("error parsing bpf instruction %q: expected 4 fields, got %d", line, n) } result = append(result, ins) } return result, nil } func init() { pcapCmd.Flags().StringVarP(&pcapCmdFlags.iface, "interface", "i", "eth0", "interface name to capture packets on") pcapCmd.Flags().BoolVar(&pcapCmdFlags.promisc, "promiscuous", false, "put interface into promiscuous mode") pcapCmd.Flags().IntVarP(&pcapCmdFlags.snaplen, "snaplen", "s", 4096, "maximum packet size to capture") pcapCmd.Flags().StringVarP(&pcapCmdFlags.output, "output", "o", "", "if not set, decode packets to stdout; if set write raw pcap data to a file, use '-' for stdout") pcapCmd.Flags().StringVar(&pcapCmdFlags.bpfFilter, "bpf-filter", "", "bpf filter to apply, tcpdump -dd format") pcapCmd.Flags().DurationVar(&pcapCmdFlags.duration, "duration", 0, "duration of the capture") pcapCmd.Flags().MarkDeprecated("snaplen", "support of snap length is removed") //nolint:errcheck addCommand(pcapCmd) } // forEachPacket reads packets from the packet source and calls the provided function for each packet. fn should not // store the packet as it will be reused for the next packet. It will also call fn with nil packet and non nill // error if the error is not known. If the context is canceled, the function will return as soon as // [gopacket.PacketSource.NextPacket] returns. // // This function is more or less direct copy of [gopacket.PacketSource.PacketsCtx] minus the sleeps. // //nolint:gocyclo func forEachPacket(ctx context.Context, p *gopacket.PacketSource, fn func(gopacket.Packet, error)) { for ctx.Err() == nil { packet, err := p.NextPacket() if err == nil { fn(packet, nil) if ctx.Err() != nil { break } // If we use pooled packets, we need to send them back to the pool if pooled, ok := packet.(gopacket.PooledPacket); ok { pooled.Dispose() } continue } // if timeout error -> retry var netErr net.Error if ok := errors.As(err, &netErr); ok && netErr.Timeout() { continue } // Immediately break for known unrecoverable errors if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.ErrNoProgress) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, io.ErrShortBuffer) || errors.Is(err, syscall.EBADF) || strings.Contains(err.Error(), "use of closed file") { break } // Otherwise, send error to the caller fn(nil, err) // and try again if context is not canceled if ctx.Err() != nil { break } } } ================================================ FILE: cmd/talosctl/cmd/talos/processes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "path/filepath" "sort" "strings" "github.com/dustin/go-humanize" "github.com/ryanuber/columnize" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var sortMethod string // processesCmd represents the processes command. var processesCmd = &cobra.Command{ Use: "processes", Aliases: []string{"p", "ps"}, Short: "List running processes", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { output, err := processesOutput(ctx, c) if err != nil { return err } fmt.Println(output) return nil }) }, } func init() { processesCmd.Flags().StringVarP(&sortMethod, "sort", "s", "rss", "Column to sort output by. [rss|cpu]") addCommand(processesCmd) } type by func(p1, p2 *machineapi.ProcessInfo) bool func (b by) sort(procs []*machineapi.ProcessInfo) { ps := &procSorter{ procs: procs, by: b, // The Sort method's receiver is the function (closure) that defines the sort order. } sort.Sort(ps) } type procSorter struct { procs []*machineapi.ProcessInfo by func(p1, p2 *machineapi.ProcessInfo) bool // Closure used in the Less method. } // Len is part of sort.Interface. func (s *procSorter) Len() int { return len(s.procs) } // Swap is part of sort.Interface. func (s *procSorter) Swap(i, j int) { s.procs[i], s.procs[j] = s.procs[j], s.procs[i] } // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. func (s *procSorter) Less(i, j int) bool { return s.by(s.procs[i], s.procs[j]) } // Sort Methods. var rss = func(p1, p2 *machineapi.ProcessInfo) bool { // Reverse sort ( Descending ) return p1.ResidentMemory > p2.ResidentMemory } var cpu = func(p1, p2 *machineapi.ProcessInfo) bool { // Reverse sort ( Descending ) return p1.CpuTime > p2.CpuTime } //nolint:gocyclo func processesOutput(ctx context.Context, c *client.Client) (output string, err error) { var remotePeer peer.Peer resp, err := c.Processes(ctx, grpc.Peer(&remotePeer)) if err != nil { return output, err } defaultNode := client.AddrFromPeer(&remotePeer) var s []string s = append(s, "NODE | PID | STATE | THREADS | CPU-TIME | VIRTMEM | RESMEM | LABEL | COMMAND") for _, msg := range resp.Messages { procs := msg.Processes switch sortMethod { case "cpu": by(cpu).sort(procs) default: by(rss).sort(procs) } var args string for _, p := range procs { switch { case p.Executable == "": args = p.Command case p.Args != "" && strings.Fields(p.Args)[0] == filepath.Base(strings.Fields(p.Executable)[0]): args = strings.Replace(p.Args, strings.Fields(p.Args)[0], p.Executable, 1) default: args = p.Args } // filter out non-printable characters args = strings.Map(func(r rune) rune { if r < 32 || r > 126 { return ' ' } return r }, args) node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } s = append(s, fmt.Sprintf("%12s | %6d | %1s | %4d | %8.2f | %7s | %7s | %64s | %s", node, p.Pid, p.State, p.Threads, p.CpuTime, humanize.Bytes(p.VirtualMemory), humanize.Bytes(p.ResidentMemory), p.Label, args)) } } res := columnize.SimpleFormat(s) return res, helpers.CheckErrors(resp.Messages...) } ================================================ FILE: cmd/talosctl/cmd/talos/pull/pull.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pull implements image pull progress reporting. package pull import ( "fmt" "maps" "slices" "sort" "strings" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/reporter" ) // ProgressWriter writes pull progress updates to a reporter. type ProgressWriter struct { // ongoingPulls keeps track of ongoing pull jobs per node. ongoingPulls map[string]pullJobs } // UpdateJob updates the progress of a pull job for a given node and layer ID. // // It is supposed to be called whenever there is a progress update for a layer pull. func (w *ProgressWriter) UpdateJob(node, layerID string, progress *machine.ImageServicePullLayerProgress) { if w.ongoingPulls == nil { w.ongoingPulls = make(map[string]pullJobs) } ongoingPulls, ok := w.ongoingPulls[node] if !ok { ongoingPulls = pullJobs{} } for _, job := range ongoingPulls { if job.LayerID == layerID { job.Status = progress return } } ongoingPulls = append(ongoingPulls, &pullJob{ LayerID: layerID, Status: progress, }) w.ongoingPulls[node] = ongoingPulls } // PrintLayerProgress prints the current layer pull progress to the reporter. func (w *ProgressWriter) PrintLayerProgress(rep *reporter.Reporter) { nodes := slices.Collect(maps.Keys(w.ongoingPulls)) sort.Strings(nodes) sb := strings.Builder{} for _, node := range nodes { sb.WriteString(node + ":\n") ongoingPulls := w.ongoingPulls[node] slices.SortFunc(ongoingPulls, func(a, b *pullJob) int { return strings.Compare(a.LayerID, b.LayerID) }) for _, job := range ongoingPulls { fmt.Fprintf(&sb, " %s\n", job.Status.Fmt()) } } rep.Report(reporter.Update{ Message: sb.String(), Status: reporter.StatusRunning, }) } type pullJob struct { LayerID string Status *machine.ImageServicePullLayerProgress } type pullJobs []*pullJob func (p pullJobs) Len() int { return len(p) } func (p pullJobs) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p pullJobs) Less(i, j int) bool { return p[i].LayerID < p[j].LayerID } ================================================ FILE: cmd/talosctl/cmd/talos/read.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "io" "os" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/client" ) // readCmd represents the read command. var readCmd = &cobra.Command{ Use: "read ", Short: "Read a file on the machine", Long: ``, Args: cobra.ExactArgs(1), Aliases: []string{"cat"}, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } return completePathFromNode(toComplete), cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "read"); err != nil { return err } r, err := c.Read(ctx, args[0]) if err != nil { return fmt.Errorf("error reading file: %w", err) } defer r.Close() //nolint:errcheck _, err = io.Copy(os.Stdout, r) if err != nil { return fmt.Errorf("error reading: %w", err) } return r.Close() }) }, } func init() { addCommand(readCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/reboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "time" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/action" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/reporter" ) var rebootCmdFlags struct { trackableActionCmdFlags mode string } // rebootCmd represents the reboot command. var rebootCmd = &cobra.Command{ Use: "reboot", Short: "Reboot a node", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if rebootCmdFlags.debug { rebootCmdFlags.wait = true } var opts []client.RebootMode switch rebootCmdFlags.mode { // skips kexec and reboots with power cycle case "powercycle": opts = append(opts, client.WithPowerCycle) case "force": opts = append(opts, client.WithForce) case "default": default: return fmt.Errorf("invalid reboot mode: %q", rebootCmdFlags.mode) } return rebootInternal(rebootCmdFlags.debug, rebootCmdFlags.debug, rebootCmdFlags.timeout, nil, opts...) }, } func rebootInternal(wait, debug bool, timeout time.Duration, rep *reporter.Reporter, opts ...client.RebootMode) error { if !wait { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } if err := c.Reboot(ctx, opts...); err != nil { return fmt.Errorf("error executing reboot: %s", err) } return nil }) } return action.NewTracker( &GlobalArgs, action.MachineReadyEventFn, rebootGetActorID(opts...), action.WithPostCheck(action.BootIDChangedPostCheckFn), action.WithDebug(debug), action.WithTimeout(timeout), action.WithReporter(rep), ).Run() } func rebootGetActorID(opts ...client.RebootMode) func(ctx context.Context, c *client.Client) (string, error) { return func(ctx context.Context, c *client.Client) (string, error) { resp, err := c.RebootWithResponse(ctx, opts...) if err != nil { return "", err } if len(resp.GetMessages()) == 0 { return "", errors.New("no messages returned from action run") } return resp.GetMessages()[0].GetActorId(), nil } } func init() { rebootCmd.Flags().StringVarP(&rebootCmdFlags.mode, "mode", "m", "default", "select the reboot mode: \"default\", \"powercycle\" (skips kexec), \"force\" (skips graceful teardown)") rebootCmdFlags.addTrackActionFlags(rebootCmd) addCommand(rebootCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/reset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "slices" "strings" "github.com/siderolabs/gen/maps" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/action" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) var wipeOptions = map[string]machineapi.ResetRequest_WipeMode{ wipeModeAll: machineapi.ResetRequest_ALL, wipeModeSystemDisk: machineapi.ResetRequest_SYSTEM_DISK, wipeModeUserDisks: machineapi.ResetRequest_USER_DISKS, } // WipeMode apply, patch, edit config update mode. type WipeMode machineapi.ResetRequest_WipeMode const ( wipeModeAll = "all" wipeModeSystemDisk = "system-disk" wipeModeUserDisks = "user-disks" ) func (m WipeMode) String() string { switch machineapi.ResetRequest_WipeMode(m) { case machineapi.ResetRequest_ALL: return wipeModeAll case machineapi.ResetRequest_SYSTEM_DISK: return wipeModeSystemDisk case machineapi.ResetRequest_USER_DISKS: return wipeModeUserDisks } return wipeModeAll } // Set implements Flag interface. func (m *WipeMode) Set(value string) error { mode, ok := wipeOptions[value] if !ok { return fmt.Errorf("possible options are: %s", m.Type()) } *m = WipeMode(mode) return nil } // Type implements Flag interface. func (m *WipeMode) Type() string { options := maps.Keys(wipeOptions) slices.Sort(options) return strings.Join(options, ", ") } var resetCmdFlags struct { trackableActionCmdFlags graceful bool reboot bool insecure bool wipeMode WipeMode userDisksToWipe []string systemLabelsToWipe []string } // resetCmd represents the reset command. var resetCmd = &cobra.Command{ Use: "reset", Short: "Reset a node", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if resetCmdFlags.debug { resetCmdFlags.wait = true } resetRequest := buildResetRequest() if resetCmdFlags.wait && resetCmdFlags.insecure { return errors.New("cannot use --wait and --insecure together") } if !resetCmdFlags.wait { resetNoWait := func(ctx context.Context, c *client.Client) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } if err := c.ResetGeneric(ctx, resetRequest); err != nil { return fmt.Errorf("error executing reset: %s", err) } return nil } if resetCmdFlags.insecure { return WithClientMaintenance(nil, resetNoWait) } return WithClient(resetNoWait) } actionFn := func(ctx context.Context, c *client.Client) (string, error) { return resetGetActorID(ctx, c, resetRequest) } var postCheckFn func(context.Context, *client.Client, string) error if resetCmdFlags.reboot { postCheckFn = func(ctx context.Context, c *client.Client, preActionBootID string) error { err := WithClientMaintenance(nil, func(ctx context.Context, cli *client.Client) error { _, err := cli.Disks(ctx) return err }) // if we can get into maintenance mode, reset has succeeded if err == nil { return nil } // try to get the boot ID in the normal mode to see if the node has rebooted return action.BootIDChangedPostCheckFn(ctx, c, preActionBootID) } } return action.NewTracker( &GlobalArgs, action.StopAllServicesEventFn, actionFn, action.WithPostCheck(postCheckFn), action.WithDebug(resetCmdFlags.debug), action.WithTimeout(resetCmdFlags.timeout), ).Run() }, } func buildResetRequest() *machineapi.ResetRequest { systemPartitionsToWipe := make([]*machineapi.ResetPartitionSpec, 0, len(resetCmdFlags.systemLabelsToWipe)) for _, label := range resetCmdFlags.systemLabelsToWipe { systemPartitionsToWipe = append(systemPartitionsToWipe, &machineapi.ResetPartitionSpec{ Label: label, Wipe: true, }) } return &machineapi.ResetRequest{ Graceful: resetCmdFlags.graceful, Reboot: resetCmdFlags.reboot, UserDisksToWipe: resetCmdFlags.userDisksToWipe, Mode: machineapi.ResetRequest_WipeMode(resetCmdFlags.wipeMode), SystemPartitionsToWipe: systemPartitionsToWipe, } } func resetGetActorID(ctx context.Context, c *client.Client, req *machineapi.ResetRequest) (string, error) { resp, err := c.ResetGenericWithResponse(ctx, req) if err != nil { return "", err } if len(resp.GetMessages()) == 0 { return "", errors.New("no messages returned from action run") } return resp.GetMessages()[0].GetActorId(), nil } func init() { resetCmd.Flags().BoolVar(&resetCmdFlags.graceful, "graceful", true, "if true, attempt to cordon/drain node and leave etcd (if applicable)") resetCmd.Flags().BoolVar(&resetCmdFlags.reboot, "reboot", false, "if true, reboot the node after resetting instead of shutting down") resetCmd.Flags().BoolVar(&resetCmdFlags.insecure, "insecure", false, "reset using the insecure (encrypted with no auth) maintenance service") resetCmd.Flags().Var(&resetCmdFlags.wipeMode, "wipe-mode", "disk reset mode") resetCmd.Flags().StringSliceVar(&resetCmdFlags.userDisksToWipe, "user-disks-to-wipe", nil, "if set, wipes defined devices in the list") resetCmd.Flags().StringSliceVar(&resetCmdFlags.systemLabelsToWipe, "system-labels-to-wipe", nil, "if set, just wipe selected system disk partitions by label but keep other partitions intact") resetCmdFlags.addTrackActionFlags(resetCmd) addCommand(resetCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/restart.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // restartCmd represents the restart command. var restartCmd = &cobra.Command{ Use: "restart ", Short: "Restart a process", Long: ``, Args: cobra.ExactArgs(1), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp } return getContainersFromNode(kubernetesFlag), cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var ( namespace string driver common.ContainerDriver ) if kubernetesFlag { namespace = constants.K8sContainerdNamespace driver = common.ContainerDriver_CRI } else { namespace = constants.SystemContainerdNamespace driver = common.ContainerDriver_CONTAINERD } if err := c.Restart(ctx, namespace, driver, args[0]); err != nil { return fmt.Errorf("error restarting process: %s", err) } return nil }) }, } func init() { restartCmd.Flags().BoolVarP(&kubernetesFlag, "kubernetes", "k", false, "use the k8s.io containerd namespace") restartCmd.Flags().Bool("use-cri", false, "use the CRI driver") restartCmd.Flags().MarkHidden("use-cri") //nolint:errcheck addCommand(restartCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/rollback.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/client" ) // rollbackCmd represents the rollback command. var rollbackCmd = &cobra.Command{ Use: "rollback", Short: "Rollback a node to the previous installation", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { if err := c.Rollback(ctx); err != nil { return fmt.Errorf("error executing rollback: %s", err) } return nil }) }, } func init() { addCommand(rollbackCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "io" "path/filepath" "slices" "strings" "github.com/siderolabs/gen/maps" _ "github.com/siderolabs/proto-codec/codec" // register codec v2 "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/global" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/formatters" ) var kubernetesFlag bool // GlobalArgs is the common arguments for the root command. var GlobalArgs global.Args const pathAutoCompleteLimit = 500 // WithClientNoNodes wraps common code to initialize Talos client and provide cancellable context. // // WithClientNoNodes doesn't set any node information on the request context. func WithClientNoNodes(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error { return GlobalArgs.WithClientNoNodes(action, dialOptions...) } // WithClient builds upon WithClientNoNodes to provide set of nodes on request context based on config & flags. func WithClient(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error { return GlobalArgs.WithClient(action, dialOptions...) } // WithClientAndNodes builds upon WithClientNoNodes to pass a list of nodes via command function. func WithClientAndNodes(action func(context.Context, *client.Client, []string) error, dialOptions ...grpc.DialOption) error { return GlobalArgs.WithClientAndNodes(action, dialOptions...) } // WithClientMaintenance wraps common code to initialize Talos client in maintenance (insecure mode). func WithClientMaintenance(enforceFingerprints []string, action func(context.Context, *client.Client) error) error { return GlobalArgs.WithClientMaintenance(enforceFingerprints, action) } // Commands is a list of commands published by the package. var Commands []*cobra.Command func addCommand(cmd *cobra.Command) { cmd.PersistentFlags().StringVar( &GlobalArgs.Talosconfig, "talosconfig", "", fmt.Sprintf("The path to the Talos configuration file. Defaults to '%s' env variable if set, otherwise '%s' and '%s' in order.", constants.TalosConfigEnvVar, filepath.Join("$HOME", constants.TalosDir, constants.TalosconfigFilename), filepath.Join(constants.ServiceAccountMountPath, constants.TalosconfigFilename), ), ) cmd.PersistentFlags().StringSliceVarP(&GlobalArgs.Nodes, "nodes", "n", []string{}, "target the specified nodes") cmd.PersistentFlags().StringSliceVarP(&GlobalArgs.Endpoints, "endpoints", "e", []string{}, "override default endpoints in Talos configuration") cli.Should(cmd.RegisterFlagCompletionFunc("nodes", CompleteNodes)) cmd.PersistentFlags().StringVarP(&GlobalArgs.Cluster, "cluster", "c", "", "Cluster to connect to if a proxy endpoint is used.") cmd.PersistentFlags().StringVar(&GlobalArgs.CmdContext, "context", "", "Context to be used in command") cmd.PersistentFlags().StringVar( &GlobalArgs.SideroV1KeysDir, "siderov1-keys-dir", "", fmt.Sprintf("The path to the SideroV1 auth PGP keys directory. Defaults to '%s' env variable if set, otherwise '%s'. Only valid for Contexts that use SideroV1 auth.", constants.SideroV1KeysDirEnvVar, filepath.Join("$HOME", constants.TalosDir, constants.SideroV1KeysDir), ), ) cli.Should(cmd.RegisterFlagCompletionFunc("context", CompleteConfigContext)) Commands = append(Commands, cmd) } // completePathFromNode represents tab complete options for `ls` and `ls *` commands. func completePathFromNode(inputPath string) []string { pathToSearch := inputPath // If the pathToSearch is empty, use root '/' if pathToSearch == "" { pathToSearch = "/" } var paths map[string]struct{} // search up one level to find possible completions if pathToSearch != "/" && !strings.HasSuffix(pathToSearch, "/") { index := strings.LastIndex(pathToSearch, "/") // we need a trailing slash to search for items in a directory pathToSearch = pathToSearch[:index] + "/" } paths = getPathFromNode(pathToSearch, inputPath) return maps.Keys(paths) } //nolint:gocyclo func getPathFromNode(path, filter string) map[string]struct{} { paths := make(map[string]struct{}) //nolint:errcheck GlobalArgs.WithClient( func(ctx context.Context, c *client.Client) error { ctx, cancel := context.WithCancel(ctx) defer cancel() stream, err := c.LS( ctx, &machineapi.ListRequest{ Root: path, }, ) if err != nil { return err } for { resp, err := stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { return nil } return fmt.Errorf("error streaming results: %s", err) } if resp.Metadata != nil && resp.Metadata.Error != "" { continue } if resp.Error != "" { continue } // skip reference to the same directory if resp.RelativeName == "." { continue } // limit the results to a reasonable amount if len(paths) > pathAutoCompleteLimit { return nil } // directories have a trailing slash if resp.IsDir { fullPath := path + resp.RelativeName + "/" if relativeTo(fullPath, filter) { paths[fullPath] = struct{}{} } } else { fullPath := path + resp.RelativeName if relativeTo(fullPath, filter) { paths[fullPath] = struct{}{} } } } }, ) return paths } func getServiceFromNode() []string { var svcIDs []string //nolint:errcheck GlobalArgs.WithClient( func(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.ServiceList(ctx, grpc.Peer(&remotePeer)) if err != nil { return err } for _, msg := range resp.Messages { for _, s := range msg.Services { svc := formatters.ServiceInfoWrapper{ServiceInfo: s} svcIDs = append(svcIDs, svc.Id) } } return nil }, ) return svcIDs } func getContainersFromNode(kubernetes bool) []string { var containerIDs []string //nolint:errcheck GlobalArgs.WithClient( func(ctx context.Context, c *client.Client) error { var ( namespace string driver common.ContainerDriver ) if kubernetes { namespace = constants.K8sContainerdNamespace driver = common.ContainerDriver_CRI } else { namespace = constants.SystemContainerdNamespace driver = common.ContainerDriver_CONTAINERD } resp, err := c.Containers(ctx, namespace, driver) if err != nil { return err } for _, msg := range resp.Messages { for _, p := range msg.Containers { if p.Pid == 0 { continue } if kubernetes && p.Id == p.PodId { continue } containerIDs = append(containerIDs, p.Id) } } return nil }, ) return containerIDs } func mergeSuggestions(s ...[]string) []string { merged := slices.Concat(s...) slices.Sort(merged) return slices.Compact(merged) } func relativeTo(fullPath string, filter string) bool { return strings.HasPrefix(fullPath, filter) } ================================================ FILE: cmd/talosctl/cmd/talos/rotate-ca.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "time" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/rotate/pki/kubernetes" "github.com/siderolabs/talos/pkg/rotate/pki/talos" ) var rotateCACmdFlags struct { clusterState clusterNodes forceEndpoint string output string withExamples bool withDocs bool dryRun bool rotateTalos bool rotateKubernetes bool } // rotateCACmd represents the rotate-ca command. var rotateCACmd = &cobra.Command{ Use: "rotate-ca", Short: "Rotate cluster CAs (Talos and Kubernetes APIs).", Long: `The command can rotate both Talos and Kubernetes root CAs (for the API). By default both CAs are rotated, but you can choose to rotate just one or another. The command starts by generating new CAs, and gracefully applying it to the cluster. For Kubernetes, the command only rotates the API server issuing CA, and other Kubernetes PKI can be rotated by applying machine config changes to the controlplane nodes.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { err := rotateCACmdFlags.clusterState.InitNodeInfos() if err != nil { return err } return WithClient(rotateCA) }, } func rotateCA(ctx context.Context, c *client.Client) error { commentsFlags := encoder.CommentsDisabled if rotateCACmdFlags.withDocs { commentsFlags |= encoder.CommentsDocs } if rotateCACmdFlags.withExamples { commentsFlags |= encoder.CommentsExamples } encoderOpt := encoder.WithComments(commentsFlags) clusterInfo, err := buildClusterInfo(rotateCACmdFlags.clusterState) if err != nil { return err } newBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) if err != nil { return fmt.Errorf("error generating new Talos CA: %w", err) } if rotateCACmdFlags.rotateTalos { var newTalosconfig *clientconfig.Config newTalosconfig, err = rotateTalosCA(ctx, c, encoderOpt, clusterInfo, newBundle) if err != nil { return fmt.Errorf("error rotating Talos CA: %w", err) } // re-create client with new Talos PKI c, err = client.New(ctx, client.WithConfig(newTalosconfig)) if err != nil { return fmt.Errorf("failed to create new client with rotated Talos CA: %w", err) } } if rotateCACmdFlags.rotateKubernetes { if err = rotateKubernetesCA(ctx, c, encoderOpt, clusterInfo, newBundle); err != nil { return fmt.Errorf("error rotating Kubernetes CA: %w", err) } } return nil } func rotateTalosCA(ctx context.Context, oldClient *client.Client, encoderOpt encoder.Option, clusterInfo cluster.Info, newBundle *secrets.Bundle) (*clientconfig.Config, error) { oldTalosconfig, err := clientconfig.Open(GlobalArgs.Talosconfig) if err != nil { return nil, fmt.Errorf("failed to open config file %q: %w", GlobalArgs.Talosconfig, err) } configContext := oldTalosconfig.Context if GlobalArgs.CmdContext != "" { configContext = GlobalArgs.CmdContext } options := talos.Options{ DryRun: rotateCACmdFlags.dryRun, CurrentClient: oldClient, ClusterInfo: clusterInfo, ContextName: configContext, Endpoints: oldClient.GetEndpoints(), NewTalosCA: newBundle.Certs.OS, EncoderOption: encoderOpt, Printf: func(format string, args ...any) { fmt.Printf(format, args...) }, } newTalosconfig, err := talos.Rotate(ctx, options) if err != nil { return nil, err } if rotateCACmdFlags.dryRun { fmt.Println("> Dry-run mode enabled, no changes were made to the cluster, re-run with `--dry-run=false` to apply the changes.") return nil, nil } fmt.Printf("> Writing new talosconfig to %q\n", rotateCACmdFlags.output) return newTalosconfig, newTalosconfig.Save(rotateCACmdFlags.output) } func rotateKubernetesCA(ctx context.Context, c *client.Client, encoderOpt encoder.Option, clusterInfo cluster.Info, newBundle *secrets.Bundle) error { options := kubernetes.Options{ DryRun: rotateCACmdFlags.dryRun, TalosClient: c, ClusterInfo: clusterInfo, KubernetesEndpoint: rotateCACmdFlags.forceEndpoint, NewKubernetesCA: newBundle.Certs.K8s, EncoderOption: encoderOpt, Printf: func(format string, args ...any) { fmt.Printf(format, args...) }, } if err := kubernetes.Rotate(ctx, options); err != nil { return err } if rotateCACmdFlags.dryRun { fmt.Println("> Dry-run mode enabled, no changes were made to the cluster, re-run with `--dry-run=false` to apply the changes.") return nil } fmt.Printf("> Kubernetes CA rotation done, new 'kubeconfig' can be fetched with `talosctl kubeconfig`.\n") return nil } func init() { addCommand(rotateCACmd) rotateCACmd.Flags().StringVar(&rotateCACmdFlags.clusterState.InitNode, "init-node", "", "specify IPs of init node") rotateCACmd.Flags().StringSliceVar(&rotateCACmdFlags.clusterState.ControlPlaneNodes, "control-plane-nodes", nil, "specify IPs of control plane nodes") rotateCACmd.Flags().StringSliceVar(&rotateCACmdFlags.clusterState.WorkerNodes, "worker-nodes", nil, "specify IPs of worker nodes") rotateCACmd.Flags().StringVar(&rotateCACmdFlags.forceEndpoint, "k8s-endpoint", "", "use endpoint instead of kubeconfig default") rotateCACmd.Flags().BoolVarP(&rotateCACmdFlags.withExamples, "with-examples", "", true, "patch all machine configs with the commented examples") rotateCACmd.Flags().BoolVarP(&rotateCACmdFlags.withDocs, "with-docs", "", true, "patch all machine configs adding the documentation for each field") rotateCACmd.Flags().StringVarP(&rotateCACmdFlags.output, "output", "o", "talosconfig", "path to the output new `talosconfig`") rotateCACmd.Flags().BoolVarP(&rotateCACmdFlags.dryRun, "dry-run", "", true, "dry-run mode (no changes to the cluster)") rotateCACmd.Flags().BoolVarP(&rotateCACmdFlags.rotateTalos, "talos", "", true, "rotate Talos API CA") rotateCACmd.Flags().BoolVarP(&rotateCACmdFlags.rotateKubernetes, "kubernetes", "", true, "rotate Kubernetes API CA") } ================================================ FILE: cmd/talosctl/cmd/talos/routes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/client" ) // routesCmd represents the net routes command. var routesCmd = &cobra.Command{ Use: "routes", Aliases: []string{"route"}, Short: "List network routes", Long: ``, Args: cobra.NoArgs, Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { return errors.New("`talosctl routes` is deprecated, please use `talosctl get routes` instead") }) }, } func init() { addCommand(routesCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "text/tabwriter" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/formatters" ) // serviceCmd represents the service command. var serviceCmd = &cobra.Command{ Use: "service [ [start|stop|restart|status]]", Aliases: []string{"services"}, Short: "Retrieve the state of a service (or all services), control service state", Long: `Service control command. If run without arguments, lists all the services and their state. If service ID is specified, default action 'status' is executed which shows status of a single list service. With actions 'start', 'stop', 'restart', service state is updated respectively.`, Args: cobra.MaximumNArgs(2), ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { switch len(args) { case 0: return getServiceFromNode(), cobra.ShellCompDirectiveNoFileComp case 1: return []string{"start", "stop", "restart", "status"}, cobra.ShellCompDirectiveNoFileComp } return nil, cobra.ShellCompDirectiveError | cobra.ShellCompDirectiveNoFileComp }, RunE: func(cmd *cobra.Command, args []string) error { action := "status" serviceID := "" if len(args) >= 1 { serviceID = args[0] } if len(args) == 2 { action = args[1] } return WithClient(func(ctx context.Context, c *client.Client) error { switch action { case "status": if serviceID == "" { return serviceList(ctx, c) } return serviceInfo(ctx, c, serviceID) case "start": return serviceStart(ctx, c, serviceID) case "stop": return serviceStop(ctx, c, serviceID) case "restart": return serviceRestart(ctx, c, serviceID) default: return fmt.Errorf("unsupported service action: %q", action) } }) }, } func serviceList(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.ServiceList(ctx, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error listing services: %w", err) } cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tSERVICE\tSTATE\tHEALTH\tLAST CHANGE\tLAST EVENT") defaultNode := client.AddrFromPeer(&remotePeer) for _, msg := range resp.Messages { for _, s := range msg.Services { svc := formatters.ServiceInfoWrapper{ServiceInfo: s} node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s ago\t%s\n", node, svc.Id, svc.State, svc.HealthStatus(), svc.LastUpdated(), svc.LastEvent()) } } return w.Flush() } func serviceInfo(ctx context.Context, c *client.Client, id string) error { var remotePeer peer.Peer services, err := c.ServiceInfo(ctx, id, grpc.Peer(&remotePeer)) if err != nil { if services == nil { return fmt.Errorf("error listing services: %w", err) } cli.Warning("%s", err) } defaultNode := client.AddrFromPeer(&remotePeer) if len(services) == 0 { return fmt.Errorf("service %q is not registered on any nodes", id) } return formatters.RenderServicesInfo(services, os.Stdout, defaultNode, true) } func serviceStart(ctx context.Context, c *client.Client, id string) error { var remotePeer peer.Peer resp, err := c.ServiceStart(ctx, id, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error starting service: %w", err) } cli.Warning("%s", err) } defaultNode := client.AddrFromPeer(&remotePeer) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\n", node, msg.Resp) } return w.Flush() } func serviceStop(ctx context.Context, c *client.Client, id string) error { var remotePeer peer.Peer resp, err := c.ServiceStop(ctx, id, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error starting service: %w", err) } cli.Warning("%s", err) } defaultNode := client.AddrFromPeer(&remotePeer) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\n", node, msg.Resp) } return w.Flush() } func serviceRestart(ctx context.Context, c *client.Client, id string) error { var remotePeer peer.Peer resp, err := c.ServiceRestart(ctx, id, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error starting service: %w", err) } cli.Warning("%s", err) } defaultNode := client.AddrFromPeer(&remotePeer) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\n", node, msg.Resp) } return w.Flush() } func init() { addCommand(serviceCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/shutdown.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/action" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/client" ) var shutdownCmdFlags struct { trackableActionCmdFlags force bool } // shutdownCmd represents the shutdown command. var shutdownCmd = &cobra.Command{ Use: "shutdown", Short: "Shutdown a node", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if shutdownCmdFlags.debug { shutdownCmdFlags.wait = true } opts := []client.ShutdownOption{ client.WithShutdownForce(shutdownCmdFlags.force), } if !shutdownCmdFlags.wait { return WithClient(func(ctx context.Context, c *client.Client) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } if err := c.Shutdown(ctx, opts...); err != nil { return fmt.Errorf("error executing shutdown: %s", err) } return nil }) } return action.NewTracker( &GlobalArgs, action.StopAllServicesEventFn, shutdownGetActorID, action.WithDebug(shutdownCmdFlags.debug), action.WithTimeout(shutdownCmdFlags.timeout), ).Run() }, } func shutdownGetActorID(ctx context.Context, c *client.Client) (string, error) { resp, err := c.ShutdownWithResponse(ctx, client.WithShutdownForce(shutdownCmdFlags.force)) if err != nil { return "", err } if len(resp.GetMessages()) == 0 { return "", errors.New("no messages returned from action run") } return resp.GetMessages()[0].GetActorId(), nil } func init() { shutdownCmd.Flags().BoolVar(&shutdownCmdFlags.force, "force", false, "if true, force a node to shutdown without a cordon/drain") shutdownCmdFlags.addTrackActionFlags(shutdownCmd) addCommand(shutdownCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/stats.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "os" "slices" "strings" "text/tabwriter" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // statsCmd represents the stats command. var statsCmd = &cobra.Command{ Use: "stats", Short: "Get container stats", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var ( namespace string driver common.ContainerDriver ) if kubernetesFlag { namespace = constants.K8sContainerdNamespace driver = common.ContainerDriver_CRI } else { namespace = constants.SystemContainerdNamespace driver = common.ContainerDriver_CONTAINERD } var remotePeer peer.Peer resp, err := c.Stats(ctx, namespace, driver, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error getting stats: %s", err) } cli.Warning("%s", err) } return statsRender(&remotePeer, resp) }) }, } func statsRender(remotePeer *peer.Peer, resp *machineapi.StatsResponse) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tMEMORY(MB)\tCPU") defaultNode := client.AddrFromPeer(remotePeer) for _, msg := range resp.Messages { slices.SortFunc(msg.Stats, func(a, b *machineapi.Stat) int { return strings.Compare(a.Id, b.Id) }) for _, s := range msg.Stats { display := s.Id if s.Id != s.PodId { // container in a sandbox display = "└─ " + display } node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\t%s\t%.2f\t%d\n", node, s.Namespace, display, float64(s.MemoryUsage)*1e-6, s.CpuUsage) } } return w.Flush() } func init() { statsCmd.Flags().BoolVarP(&kubernetesFlag, "kubernetes", "k", false, "use the k8s.io containerd namespace") statsCmd.Flags().Bool("use-cri", false, "use the CRI driver") statsCmd.Flags().MarkHidden("use-cri") //nolint:errcheck addCommand(statsCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/support.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "bufio" "context" "errors" "fmt" "io" "os" "strings" "sync" "text/tabwriter" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/fatih/color" "github.com/gosuri/uiprogress" "github.com/siderolabs/go-talos-support/support" "github.com/siderolabs/go-talos-support/support/bundle" "github.com/siderolabs/go-talos-support/support/collectors" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8s "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/pkg/machinery/client" clusterresource "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) var supportCmdFlags struct { output string numWorkers int verbose bool } // supportCmd represents the support command. var supportCmd = &cobra.Command{ Use: "support", Short: "Dump debug information about the cluster", Long: `Generated bundle contains the following debug information: - For each node: - Kernel logs. - All Talos internal services logs. - All kube-system pods logs. - Talos COSI resources without secrets. - COSI runtime state graph. - Processes snapshot. - IO pressure snapshot. - Mounts list. - PCI devices info. - Talos version. - For the cluster: - Kubernetes nodes and kube-system pods manifests. `, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if len(GlobalArgs.Nodes) == 0 { return errors.New("please provide at least a single node to gather the debug information from") } f, err := openArchive() if err != nil { return err } defer f.Close() //nolint:errcheck progress := make(chan bundle.Progress) var ( eg errgroup.Group errors supportBundleErrors ) eg.Go(func() error { if supportCmdFlags.verbose { for p := range progress { errors.handleProgress(p) } } else { showProgress(progress, &errors) } return nil }) collectErr := collectData(f, progress) close(progress) if e := eg.Wait(); e != nil { return e } if err = errors.print(); err != nil { return err } fmt.Fprintf(os.Stderr, "Support bundle is written to %s\n", supportCmdFlags.output) return collectErr }, } func collectData(dest *os.File, progress chan bundle.Progress) error { return WithClientNoNodes(func(ctx context.Context, c *client.Client) error { clientset, err := getKubernetesClient(ctx, c) if err != nil { fmt.Fprintf(os.Stderr, "Failed to create kubernetes client %s\n", err) } opts := []bundle.Option{ bundle.WithArchiveOutput(dest), bundle.WithKubernetesClient(clientset), bundle.WithTalosClient(c), bundle.WithNodes(GlobalArgs.Nodes...), bundle.WithNumWorkers(supportCmdFlags.numWorkers), bundle.WithProgressChan(progress), } if !supportCmdFlags.verbose { opts = append(opts, bundle.WithLogOutput(io.Discard)) } options := bundle.NewOptions(opts...) collectors, err := collectors.GetForOptions(ctx, options) if err != nil { return err } return support.CreateSupportBundle(ctx, options, collectors...) }) } func getKubernetesClient(ctx context.Context, c *client.Client) (*k8s.Clientset, error) { kubeconfig, err := c.Kubeconfig(ctx) if err != nil { return nil, err } config, err := clientcmd.NewClientConfigFromBytes(kubeconfig) if err != nil { return nil, err } restconfig, err := config.ClientConfig() if err != nil { return nil, err } clientset, err := k8s.NewForConfig(restconfig) if err != nil { return nil, err } // just checking that k8s responds _, err = clientset.CoreV1().Namespaces().Get(ctx, "kube-system", v1.GetOptions{}) if err != nil { return nil, err } return clientset, nil } func getDiscoveryConfig() (*clusterresource.Config, error) { var config *clusterresource.Config if e := WithClient(func(ctx context.Context, c *client.Client) error { var err error config, err = safe.StateGet[*clusterresource.Config]( ctx, c.COSI, resource.NewMetadata(clusterresource.NamespaceName, clusterresource.IdentityType, clusterresource.LocalIdentity, resource.VersionUndefined), ) return err }); e != nil { return nil, e } return config, nil } func openArchive() (*os.File, error) { if supportCmdFlags.output == "" { supportCmdFlags.output = "support" if config, err := getDiscoveryConfig(); err == nil && config.TypedSpec().DiscoveryEnabled { supportCmdFlags.output += "-" + config.TypedSpec().ServiceClusterID } supportCmdFlags.output += ".zip" } if _, err := os.Stat(supportCmdFlags.output); err != nil { if !errors.Is(err, os.ErrNotExist) { return nil, err } } else { buf := bufio.NewReader(os.Stdin) fmt.Printf("%s already exists, overwrite? [y/N]: ", supportCmdFlags.output) choice, err := buf.ReadString('\n') if err != nil { return nil, err } if strings.TrimSpace(strings.ToLower(choice)) != "y" { return nil, fmt.Errorf("operation aborted") } } return os.OpenFile(supportCmdFlags.output, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644) } type supportBundleError struct { source string value string } type supportBundleErrors struct { errors []supportBundleError } func (sbe *supportBundleErrors) handleProgress(p bundle.Progress) { if p.Error != nil { sbe.errors = append(sbe.errors, supportBundleError{ source: p.Source, value: p.Error.Error(), }) } } func (sbe *supportBundleErrors) print() error { if sbe.errors == nil { return nil } var wroteHeader bool w := tabwriter.NewWriter(os.Stderr, 0, 0, 3, ' ', 0) for _, err := range sbe.errors { if !wroteHeader { wroteHeader = true fmt.Fprintln(os.Stderr, "Processed with errors:") fmt.Fprintln(w, "\tSOURCE\tERROR") } details := strings.Split(err.value, "\n") for i, d := range details { details[i] = strings.TrimSpace(d) } fmt.Fprintf(w, "\t%s\t%s\n", err.source, color.RedString(details[0])) if len(details) > 1 { for _, line := range details[1:] { fmt.Fprintf(w, "\t\t%s\n", color.RedString(line)) } } } return w.Flush() } func showProgress(progress <-chan bundle.Progress, errors *supportBundleErrors) { uiprogress.Start() type nodeProgress struct { mu sync.Mutex state string bar *uiprogress.Bar } nodes := map[string]*nodeProgress{} for p := range progress { errors.handleProgress(p) var ( np *nodeProgress ok bool ) src := p.Source if _, ok = nodes[p.Source]; !ok { bar := uiprogress.AddBar(p.Total) bar = bar.AppendCompleted().PrependElapsed() np = &nodeProgress{ state: "initializing...", bar: bar, } bar.AppendFunc( func(src string, np *nodeProgress) func(b *uiprogress.Bar) string { return func(b *uiprogress.Bar) string { np.mu.Lock() defer np.mu.Unlock() return fmt.Sprintf("%s: %s", src, np.state) } }(src, np), ) bar.Width = 20 nodes[src] = np } else { np = nodes[src] } np.mu.Lock() np.state = p.State np.mu.Unlock() np.bar.Incr() } uiprogress.Stop() } func init() { addCommand(supportCmd) supportCmd.Flags().StringVarP(&supportCmdFlags.output, "output", "O", "", "output file to write support archive to") supportCmd.Flags().IntVarP(&supportCmdFlags.numWorkers, "num-workers", "w", 1, "number of workers per node") supportCmd.Flags().BoolVarP(&supportCmdFlags.verbose, "verbose", "v", false, "verbose output") } ================================================ FILE: cmd/talosctl/cmd/talos/time.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "text/tabwriter" "time" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/cli" timeapi "github.com/siderolabs/talos/pkg/machinery/api/time" "github.com/siderolabs/talos/pkg/machinery/client" ) var timeCmdFlags struct { ntpServer string } // timeCmd represents the time command. var timeCmd = &cobra.Command{ Use: "time [--check server]", Short: "Gets current server time", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(func(ctx context.Context, c *client.Client) error { var ( resp *timeapi.TimeResponse remotePeer peer.Peer err error ) if timeCmdFlags.ntpServer == "" { resp, err = c.Time(ctx, grpc.Peer(&remotePeer)) } else { resp, err = c.TimeCheck(ctx, timeCmdFlags.ntpServer, grpc.Peer(&remotePeer)) } if err != nil { if resp == nil { return fmt.Errorf("error fetching time: %w", err) } cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNTP-SERVER\tNODE-TIME\tNTP-SERVER-TIME") defaultNode := client.AddrFromPeer(&remotePeer) var localtime, remotetime time.Time for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } if !msg.Localtime.IsValid() { return errors.New("error parsing local time") } if !msg.Remotetime.IsValid() { return errors.New("error parsing remote time") } localtime = msg.Localtime.AsTime() remotetime = msg.Remotetime.AsTime() fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", node, msg.Server, localtime.String(), remotetime.String()) } return w.Flush() }) }, } func init() { timeCmd.Flags().StringVar(&timeCmdFlags.ntpServer, "check", "", "checks server time against specified ntp server") addCommand(timeCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/track.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "time" "github.com/spf13/cobra" ) type trackableActionCmdFlags struct { wait bool debug bool timeout time.Duration } func (f *trackableActionCmdFlags) addTrackActionFlags(cmd *cobra.Command) { cmd.Flags().BoolVar(&f.wait, "wait", true, "wait for the operation to complete, tracking its progress. always set to true when --debug is set") cmd.Flags().BoolVar(&f.debug, "debug", false, "debug operation from kernel logs. --wait is set to true when this flag is set") cmd.Flags().DurationVar(&f.timeout, "timeout", 30*time.Minute, "time to wait for the operation is complete if --debug or --wait is set") } ================================================ FILE: cmd/talosctl/cmd/talos/upgrade-k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "time" "github.com/siderolabs/go-kubernetes/kubernetes/ssa" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "github.com/spf13/cobra" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cluster" k8s "github.com/siderolabs/talos/pkg/cluster/kubernetes" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/constants" ) // upgradeK8sCmd represents the upgrade-k8s command. var upgradeK8sCmd = &cobra.Command{ Use: "upgrade-k8s", Short: "Upgrade Kubernetes control plane in the Talos cluster.", Long: `Command runs upgrade of Kubernetes control plane components between specified versions.`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { return WithClient(upgradeKubernetes) }, } var upgradeOptions k8s.UpgradeOptions var upgradeK8sCmdFlags struct { FromVersion string ToVersion string withExamples bool withDocs bool inventoryPolicy string } func init() { upgradeK8sCmd.Flags().StringVar(&upgradeK8sCmdFlags.FromVersion, "from", "", "the Kubernetes control plane version to upgrade from") upgradeK8sCmd.Flags().StringVar(&upgradeK8sCmdFlags.ToVersion, "to", constants.DefaultKubernetesVersion, "the Kubernetes control plane version to upgrade to") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.ControlPlaneEndpoint, "endpoint", "", "the cluster control plane endpoint") upgradeK8sCmd.Flags().BoolVarP(&upgradeK8sCmdFlags.withExamples, "with-examples", "", true, "patch all machine configs with the commented examples") upgradeK8sCmd.Flags().BoolVarP(&upgradeK8sCmdFlags.withDocs, "with-docs", "", true, "patch all machine configs adding the documentation for each field") upgradeK8sCmd.Flags().BoolVar(&upgradeOptions.PrePullImages, "pre-pull-images", true, "pre-pull images before upgrade") upgradeK8sCmd.Flags().BoolVar(&upgradeOptions.UpgradeKubelet, "upgrade-kubelet", true, "upgrade kubelet service") upgradeK8sCmd.Flags().BoolVar(&upgradeOptions.DryRun, "dry-run", false, "skip the actual upgrade and show the upgrade plan instead") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.KubeletImage, "kubelet-image", constants.KubeletImage, "kubelet image to use") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.APIServerImage, "apiserver-image", constants.KubernetesAPIServerImage, "kube-apiserver image to use") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.ControllerManagerImage, "controller-manager-image", constants.KubernetesControllerManagerImage, "kube-controller-manager image to use") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.SchedulerImage, "scheduler-image", constants.KubernetesSchedulerImage, "kube-scheduler image to use") upgradeK8sCmd.Flags().StringVar(&upgradeOptions.ProxyImage, "proxy-image", constants.KubeProxyImage, "kube-proxy image to use") // manifest sync related options upgradeK8sCmd.Flags().BoolVar(&upgradeOptions.ForceManifests, "manifests-force", false, "whether to recreate objects that contain immutable field changes") upgradeK8sCmd.Flags().BoolVar(&upgradeOptions.NoPrune, "manifests-no-prune", false, "whether pruning of previously applied objects should happen after apply") upgradeK8sCmd.Flags().StringVar(&upgradeK8sCmdFlags.inventoryPolicy, "manifests-inventory-policy", "AdoptIfNoInventory", "kubernetes SSA inventory policy (one of 'MustMatch', 'AdoptIfNoInventory' or 'AdoptAll')") upgradeK8sCmd.Flags().DurationVar(&upgradeOptions.ReconcileTimeout, "manifests-reconcile-timeout", 5*time.Minute, "how long to wait for resources to be fully reconciled (set to zero to disable waiting)") addCommand(upgradeK8sCmd) } func upgradeKubernetes(ctx context.Context, c *client.Client) error { if err := helpers.FailIfMultiNodes(ctx, "upgrade-k8s"); err != nil { return err } if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } clientProvider := &cluster.ConfigClientProvider{ DefaultClient: c, } defer clientProvider.Close() //nolint:errcheck state := struct { cluster.ClientProvider cluster.K8sProvider }{ ClientProvider: clientProvider, K8sProvider: &cluster.KubernetesClient{ ClientProvider: clientProvider, ForceEndpoint: upgradeOptions.ControlPlaneEndpoint, }, } var err error if upgradeK8sCmdFlags.FromVersion == "" { upgradeK8sCmdFlags.FromVersion, err = k8s.DetectLowestVersion(ctx, &state, upgradeOptions) if err != nil { return fmt.Errorf("error detecting the lowest Kubernetes version %w", err) } upgradeOptions.Log("automatically detected the lowest Kubernetes version %s", upgradeK8sCmdFlags.FromVersion) } upgradeOptions.Path, err = upgrade.NewPath(upgradeK8sCmdFlags.FromVersion, upgradeK8sCmdFlags.ToVersion) if err != nil { return fmt.Errorf("error creating upgrade path %w", err) } commentsFlags := encoder.CommentsDisabled if upgradeK8sCmdFlags.withDocs { commentsFlags |= encoder.CommentsDocs } if upgradeK8sCmdFlags.withExamples { commentsFlags |= encoder.CommentsExamples } policy, err := ssa.ParseInventoryPolicy(upgradeK8sCmdFlags.inventoryPolicy) if err != nil { return err } upgradeOptions.InventoryPolicy = policy upgradeOptions.EncoderOpt = encoder.WithComments(commentsFlags) if upgradeOptions.ReconcileTimeout == 0 { upgradeOptions.SkipManifestWait = true } return k8s.Upgrade(ctx, &state, upgradeOptions) } ================================================ FILE: cmd/talosctl/cmd/talos/upgrade.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "errors" "fmt" "os" "strings" "text/tabwriter" "time" "github.com/blang/semver/v4" "github.com/siderolabs/gen/xerrors" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/lifecycle" "github.com/siderolabs/talos/cmd/talosctl/cmd/talos/multiplex" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/action" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/flags" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/reporter" ) var upgradeCmdFlags = struct { trackableActionCmdFlags imageCmdFlagsType upgradeImage string rebootMode flags.PflagExtended[machine.RebootRequest_Mode] progress flags.PflagExtended[reporter.OutputMode] force bool // Deprecated: only used for legacy upgrade path, to be removed in Talos 1.18. insecure bool // Deprecated: only used for legacy upgrade path, to be removed in Talos 1.18. preserve bool // Deprecated: only used for legacy upgrade path, to be removed in Talos 1.18. stage bool // Deprecated: only used for legacy upgrade path, to be removed in Talos 1.18. }{ rebootMode: flags.ProtoEnum(machine.RebootRequest_DEFAULT, machine.RebootRequest_Mode_value, machine.RebootRequest_Mode_name), progress: reporter.NewOutputModeFlag(), } // upgradeCmd represents the processes command. var upgradeCmd = &cobra.Command{ Use: "upgrade", Short: "Upgrade Talos on the target node", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if upgradeCmdFlags.debug { upgradeCmdFlags.wait = true } if upgradeCmdFlags.wait && upgradeCmdFlags.insecure { return errors.New("cannot use --wait and --insecure together") } return upgradeRun() }, } func upgradeRun() error { return WithClientAndNodes(upgradeViaLifecycleService) } var talosUpgradeAPIVersionRange = semver.MustParseRange(">1.13.0-alpha.2 <2.0.0") // upgradeViaLifecycleService tries the new LifecycleService.Upgrade streaming API. // If the server returns codes.Unimplemented, it falls back to the legacy MachineService.Upgrade. func upgradeViaLifecycleService(ctx context.Context, c *client.Client, nodes []string) error { if upgradeCmdFlags.debug { upgradeCmdFlags.wait = true } opts := []client.RebootMode{ client.WithRebootMode(upgradeCmdFlags.rebootMode.Value()), } containerdInstance, err := upgradeCmdFlags.containerdInstance() if err != nil { return err } rep := reporter.New( reporter.WithOutputMode(upgradeCmdFlags.progress.Value()), ) err = WithClient(func(ctx context.Context, c *client.Client) error { return helpers.TalosVersionCheck(ctx, c, talosUpgradeAPIVersionRange) }) if err != nil { if xerrors.TagIs[helpers.VersionOutsideRangeError](err) { rep.Report(reporter.Update{ Status: reporter.StatusError, Message: "New upgrade API is not available, falling back to legacy", }) return upgradeLegacy() } return fmt.Errorf("error checking Talos version compatibility: %w", err) } _, err = imagePullInternal(ctx, c, containerdInstance, nodes, upgradeCmdFlags.upgradeImage, rep) if err != nil { return fmt.Errorf("error pulling upgrade image: %w", err) } _, err = upgradeInternal(ctx, c, containerdInstance, nodes, upgradeCmdFlags.upgradeImage, rep) if err != nil { return fmt.Errorf("error during upgrade: %w", err) } err = rebootInternal(upgradeCmdFlags.wait, upgradeCmdFlags.debug, upgradeCmdFlags.timeout, rep, opts...) if err != nil { return fmt.Errorf("error during upgrade: %w", err) } return nil } func upgradeInternal(ctx context.Context, c *client.Client, containerdInstance *common.ContainerdInstance, nodes []string, imageRef string, rep *reporter.Reporter) (map[string]int32, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() var ( errs error w lifecycle.ProgressWriter ) finishedUpgrades := map[string]int32{} responseChan := multiplex.Streaming(ctx, nodes, func(ctx context.Context) (grpc.ServerStreamingClient[machine.LifecycleServiceUpgradeResponse], error) { return c.LifecycleClient.Upgrade(ctx, &machine.LifecycleServiceUpgradeRequest{ Containerd: containerdInstance, Source: &machine.InstallArtifactsSource{ ImageName: imageRef, }, }) }, ) for resp := range responseChan { if resp.Err != nil { errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) continue } switch resp.Payload.GetProgress().GetResponse().(type) { case *machine.LifecycleServiceInstallProgress_Message: w.UpdateJob(resp.Node, resp.Payload.GetProgress()) w.PrintLayerProgress(rep) case *machine.LifecycleServiceInstallProgress_ExitCode: finishedUpgrades[resp.Node] = resp.Payload.GetProgress().GetExitCode() w.UpdateJob(resp.Node, resp.Payload.GetProgress()) w.PrintLayerProgress(rep) } } if len(finishedUpgrades) > 0 { var sb strings.Builder status := reporter.StatusSucceeded for node, exitCode := range finishedUpgrades { if exitCode != 0 { errs = errors.Join(errs, fmt.Errorf("node %s: upgrade failed with exit code %d", node, exitCode)) status = reporter.StatusError fmt.Fprintf(&sb, "%s: upgrade failed with exit code %d\n", node, exitCode) } else { fmt.Fprintf(&sb, "%s: upgrade completed\n", node) } } rep.Report(reporter.Update{ Message: sb.String(), Status: status, }) } return finishedUpgrades, errs } // upgradeLegacy dispatches to the legacy upgrade path, respecting --wait. // // Note: remove me in Talos 1.18. func upgradeLegacy() error { rebootModeStr := strings.ToUpper(upgradeCmdFlags.rebootMode.String()) rebootMode, ok := machine.UpgradeRequest_RebootMode_value[rebootModeStr] if !ok { return fmt.Errorf("invalid reboot mode: %s", upgradeCmdFlags.rebootMode) } opts := []client.UpgradeOption{ client.WithUpgradeImage(upgradeCmdFlags.upgradeImage), client.WithUpgradeRebootMode(machine.UpgradeRequest_RebootMode(rebootMode)), client.WithUpgradePreserve(upgradeCmdFlags.preserve), client.WithUpgradeStage(upgradeCmdFlags.stage), client.WithUpgradeForce(upgradeCmdFlags.force), } if !upgradeCmdFlags.wait { return runUpgradeLegacyNoWaitWithOpts(opts) } return action.NewTracker( &GlobalArgs, action.MachineReadyEventFn, func(ctx context.Context, c *client.Client) (string, error) { return upgradeGetActorID(ctx, c, opts) }, action.WithPostCheck(action.BootIDChangedPostCheckFn), action.WithDebug(upgradeCmdFlags.debug), action.WithTimeout(upgradeCmdFlags.timeout), ).Run() } // runUpgradeLegacyNoWaitWithOpts runs the legacy upgrade without waiting. // // Note: remove me in Talos 1.18. func runUpgradeLegacyNoWaitWithOpts(opts []client.UpgradeOption) error { if upgradeCmdFlags.insecure { return WithClientMaintenance(nil, func(ctx context.Context, c *client.Client) error { return doUpgradeLegacy(ctx, c, opts) }) } return WithClient(func(ctx context.Context, c *client.Client) error { return doUpgradeLegacy(ctx, c, opts) }) } // doUpgradeLegacy performs the legacy MachineService.Upgrade call on an existing client. // // Note: remove me in Talos 1.18. func doUpgradeLegacy(ctx context.Context, c *client.Client, opts []client.UpgradeOption) error { if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } var remotePeer peer.Peer opts = append(opts, client.WithUpgradeGRPCCallOptions(grpc.Peer(&remotePeer))) // TODO: See if we can validate version and prevent starting upgrades to an unknown version resp, err := c.UpgradeWithOptions(ctx, opts...) //nolint:staticcheck // legacy talosctl methods, to be removed in Talos 1.18 if err != nil { if resp == nil { return fmt.Errorf("error performing upgrade: %s", err) } cli.Warning("%s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tACK\tSTARTED") defaultNode := client.AddrFromPeer(&remotePeer) for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } fmt.Fprintf(w, "%s\t%s\t%s\t\n", node, msg.Ack, time.Now()) } return w.Flush() } // upgradeGetActorID is used by the legacy action tracker path. // // Note: remove me in Talos 1.18. func upgradeGetActorID(ctx context.Context, c *client.Client, opts []client.UpgradeOption) (string, error) { resp, err := c.UpgradeWithOptions(ctx, opts...) //nolint:staticcheck // legacy talosctl methods, to be removed in Talos 1.18 if err != nil { return "", err } if len(resp.GetMessages()) == 0 { return "", errors.New("no messages returned from action run") } return resp.GetMessages()[0].GetActorId(), nil } func init() { upgradeCmd.Flags().StringVarP(&upgradeCmdFlags.upgradeImage, "image", "i", fmt.Sprintf("%s/%s/installer:%s", images.Registry, images.Username, version.Trim(version.Tag)), "the container image to use for performing the install") upgradeCmd.Flags().StringVar(&upgradeCmdFlags.namespace, "namespace", "system", "namespace to use: \"system\" (etcd and kubelet images), \"cri\" for all Kubernetes workloads, \"inmem\" for in-memory containerd instance", ) upgradeCmd.Flags().VarP(upgradeCmdFlags.rebootMode, "reboot-mode", "m", fmt.Sprintf( "select the reboot mode during upgrade. Mode %q bypasses kexec. Values: %v", strings.ToLower(machine.UpgradeRequest_POWERCYCLE.String()), upgradeCmdFlags.rebootMode.Options(), ), ) upgradeCmd.Flags().Var(upgradeCmdFlags.progress, "progress", fmt.Sprintf("output mode for upgrade progress. Values: %v", upgradeCmdFlags.progress.Options())) // Mark legacy-only flags as deprecated. These are only used when falling back // to the legacy MachineService.Upgrade unary API for older Talos versions. // // Note: remove me in Talos 1.18. upgradeCmdFlags.addTrackActionFlags(upgradeCmd) upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.force, "force", "f", false, "force the upgrade (skip checks on etcd health and members, might lead to data loss)") upgradeCmd.Flags().BoolVar(&upgradeCmdFlags.insecure, "insecure", false, "upgrade using the insecure (encrypted with no auth) maintenance service") upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.preserve, "preserve", "p", false, "preserve data") upgradeCmd.Flags().BoolVarP(&upgradeCmdFlags.stage, "stage", "s", false, "stage the upgrade to perform it after a reboot") for _, flag := range []string{"force", "insecure", "preserve", "stage"} { upgradeCmd.Flags().MarkDeprecated(flag, "legacy flag for MachineService.Upgrade fallback, to be removed in Talos 1.18") //nolint:errcheck } addCommand(upgradeCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "strings" "github.com/spf13/cobra" "google.golang.org/grpc" "google.golang.org/grpc/peer" "google.golang.org/protobuf/encoding/protojson" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/version" ) // versionCmdFlags represents the `talosctl version` command's flags. var versionCmdFlags struct { clientOnly bool shortVersion bool json bool insecure bool } // versionCmd represents the `talosctl version` command. var versionCmd = &cobra.Command{ Use: "version", Short: "Prints the version", Long: ``, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if !versionCmdFlags.json { fmt.Println("Client:") if versionCmdFlags.shortVersion { version.PrintShortVersion() } else { version.PrintLongVersion() } // Exit early if we're only looking for client version if versionCmdFlags.clientOnly { return nil } fmt.Println("Server:") } if versionCmdFlags.insecure { return WithClientMaintenance(nil, cmdVersion) } return WithClient(cmdVersion) }, } func cmdVersion(ctx context.Context, c *client.Client) error { var remotePeer peer.Peer resp, err := c.Version(ctx, grpc.Peer(&remotePeer)) if err != nil { if resp == nil { return fmt.Errorf("error getting version: %s", err) } cli.Warning("%s", err) } defaultNode := client.AddrFromPeer(&remotePeer) for _, msg := range resp.Messages { node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } if !versionCmdFlags.json { fmt.Printf("\t%s: %s\n", "NODE", node) version.PrintLongVersionFromExisting(msg.Version) var enabledFeatures []string if msg.Features.GetRbac() { enabledFeatures = append(enabledFeatures, "RBAC") } fmt.Printf("\tEnabled: %s\n", strings.Join(enabledFeatures, ", ")) continue } b, err := protojson.Marshal(msg) if err != nil { return err } fmt.Printf("%s\n", b) } return nil } func init() { versionCmd.Flags().BoolVar(&versionCmdFlags.shortVersion, "short", false, "Print the short version") versionCmd.Flags().BoolVar(&versionCmdFlags.clientOnly, "client", false, "Print client version only") versionCmd.Flags().BoolVarP(&versionCmdFlags.insecure, "insecure", "i", false, "use Talos maintenance mode API") // TODO remove when https://github.com/siderolabs/talos/issues/907 is implemented versionCmd.Flags().BoolVar(&versionCmdFlags.json, "json", false, "") cli.Should(versionCmd.Flags().MarkHidden("json")) addCommand(versionCmd) } ================================================ FILE: cmd/talosctl/cmd/talos/wipe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos import ( "context" "fmt" "github.com/siderolabs/gen/xslices" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/client" ) // wipeCmd represents the wipe command. var wipeCmd = &cobra.Command{ Use: "wipe", Short: "Wipe block device or volumes", Args: cobra.NoArgs, } var wipeDiskCmdFlags struct { wipeMethod string skipVolumeCheck bool skipSecondaryCheck bool dropPartition bool insecure bool } // wipeDiskCmd represents the wipe disk command. var wipeDiskCmd = &cobra.Command{ Use: "disk ...", Short: "Wipe a block device (disk or partition) which is not used as a volume", Long: `Wipe a block device (disk or partition) which is not used as a volume. Use device names as arguments, for example: vda or sda5.`, Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { if wipeDiskCmdFlags.insecure { return WithClientMaintenance(nil, cmdWipe(args)) } return WithClient(cmdWipe(args)) }, } func cmdWipe(args []string) func(ctx context.Context, c *client.Client) error { return func(ctx context.Context, c *client.Client) error { method, ok := storage.BlockDeviceWipeDescriptor_Method_value[wipeDiskCmdFlags.wipeMethod] if !ok { return fmt.Errorf("invalid wipe method %q", wipeDiskCmdFlags.wipeMethod) } return c.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: xslices.Map(args, func(devName string) *storage.BlockDeviceWipeDescriptor { return &storage.BlockDeviceWipeDescriptor{ Device: devName, Method: storage.BlockDeviceWipeDescriptor_Method(method), SkipVolumeCheck: wipeDiskCmdFlags.skipVolumeCheck, SkipSecondaryCheck: wipeDiskCmdFlags.skipSecondaryCheck, DropPartition: wipeDiskCmdFlags.dropPartition, } }), }) } } func wipeMethodValues() []string { var method storage.BlockDeviceWipeDescriptor_Method values := make([]string, method.Descriptor().Values().Len()) for idx := range method.Descriptor().Values().Len() { values[idx] = storage.BlockDeviceWipeDescriptor_Method_name[int32(idx)] } return values } func init() { addCommand(wipeCmd) wipeDiskCmd.Flags().StringVar(&wipeDiskCmdFlags.wipeMethod, "method", wipeMethodValues()[0], fmt.Sprintf("wipe method to use %s", wipeMethodValues())) wipeDiskCmd.Flags().BoolVar(&wipeDiskCmdFlags.skipVolumeCheck, "skip-volume-check", false, "skip volume check") wipeDiskCmd.Flags().BoolVar(&wipeDiskCmdFlags.skipSecondaryCheck, "skip-secondary-check", false, "skip secondary disk check (e.g. underlying disk for RAID or LVM), use with caution") wipeDiskCmd.Flags().BoolVar(&wipeDiskCmdFlags.dropPartition, "drop-partition", false, "drop partition after wipe (if applicable)") wipeDiskCmd.Flags().MarkHidden("skip-volume-check") //nolint:errcheck wipeDiskCmd.Flags().MarkHidden("skip-secondary-check") //nolint:errcheck wipeDiskCmd.Flags().BoolVarP(&wipeDiskCmdFlags.insecure, "insecure", "i", false, "use Talos maintenance mode API") wipeCmd.AddCommand(wipeDiskCmd) } ================================================ FILE: cmd/talosctl/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talosctl provides the talosctl utility implementation. package main import ( "os" _ "github.com/siderolabs/talos/cmd/talosctl/acompat" "github.com/siderolabs/talos/cmd/talosctl/cmd" ) func main() { if err := cmd.Execute(); err != nil { os.Exit(1) } } ================================================ FILE: cmd/talosctl/pkg/mgmt/helpers/airgapped.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( stdx509 "crypto/x509" "net" "github.com/siderolabs/crypto/x509" ) // GenerateSelfSignedCert generates self-signed certificate. func GenerateSelfSignedCert(sanIPs []net.IP, sanNames []string) ([]byte, []byte, []byte, error) { ca, err := x509.NewSelfSignedCertificateAuthority( x509.ECDSA(true), x509.Organization("talos.dev"), x509.CommonName("talos.dev Root CA"), ) if err != nil { return nil, nil, nil, err } serverIdentity, err := x509.NewKeyPair(ca, x509.Organization("talos.dev"), x509.CommonName("server"), x509.IPAddresses(sanIPs), x509.DNSNames(sanNames), x509.ExtKeyUsage([]stdx509.ExtKeyUsage{stdx509.ExtKeyUsageServerAuth}), ) if err != nil { return nil, nil, nil, err } return ca.CrtPEM, serverIdentity.CrtPEM, serverIdentity.KeyPEM, nil } ================================================ FILE: cmd/talosctl/pkg/mgmt/helpers/artifacts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package helpers provides helpers for talosctl. package helpers import ( "path/filepath" "github.com/siderolabs/talos/pkg/machinery/gendata" ) // ArtifactsPath is a path to artifacts output directory (set during the build). var ArtifactsPath = gendata.ArtifactsPath // ArtifactPath returns path to the artifact by name. func ArtifactPath(name string) string { return filepath.Join(ArtifactsPath, name) } ================================================ FILE: cmd/talosctl/pkg/mgmt/helpers/helpers_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: cmd/talosctl/pkg/mgmt/helpers/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "fmt" "os" "github.com/siderolabs/talos/pkg/machinery/version" ) // DefaultImage appends default image version. func DefaultImage(image string) string { return fmt.Sprintf("%s:%s", image, GetTag()) } // GetTag retrieves the current tag. func GetTag() string { return getEnv("TAG", version.Tag) } func getEnv(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value } return fallback } ================================================ FILE: cmd/talosctl/pkg/mgmt/helpers/wireguard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "fmt" "net/netip" "time" "github.com/siderolabs/gen/xslices" sideronet "github.com/siderolabs/net" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) // NewWireguardConfigBundle creates a new Wireguard config bundle. func NewWireguardConfigBundle(ips []netip.Addr, wireguardCidr string, listenPort, controlplanesCount int) (*WireguardConfigBundle, error) { configs := map[netip.Addr]*network.WireguardConfigV1Alpha1{} keys := make([]wgtypes.Key, len(ips)) peers := make([]network.WireguardPeer, len(ips)) wgCidr, err := netip.ParsePrefix(wireguardCidr) if err != nil { return nil, fmt.Errorf("failed to parse wireguard cidr %s: %w", wireguardCidr, err) } for i, ip := range ips { key, err := wgtypes.GeneratePrivateKey() if err != nil { return nil, err } wgAddr, err := sideronet.NthIPInNetwork(wgCidr, i+2) if err != nil { return nil, err } keys[i] = key peers[i] = network.WireguardPeer{ WireguardAllowedIPs: []network.Prefix{ { Prefix: netip.PrefixFrom(wgAddr, wgAddr.BitLen()), }, }, WireguardPublicKey: key.PublicKey().String(), WireguardPersistentKeepaliveInterval: time.Second * 5, } if i < controlplanesCount { peers[i].WireguardEndpoint = network.AddrPort{AddrPort: netip.AddrPortFrom(ip, uint16(listenPort))} } } for i, nodeIP := range ips { wgAddr, err := sideronet.NthIPInNetwork(wgCidr, i+2) if err != nil { return nil, err } config := network.NewWireguardConfigV1Alpha1("wg0") config.WireguardPeers = xslices.Filter(peers, func(p network.WireguardPeer) bool { return p.WireguardPublicKey != keys[i].PublicKey().String() }) config.WireguardPrivateKey = keys[i].String() config.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.PrefixFrom(wgAddr, wgCidr.Bits()), }, } config.LinkUp = new(true) config.LinkMTU = 1500 if i < controlplanesCount { config.WireguardListenPort = listenPort } configs[nodeIP] = config } return &WireguardConfigBundle{ configs: configs, }, nil } // WireguardConfigBundle allows assembling wireguard network configuration with first controlplane being listen node. type WireguardConfigBundle struct { configs map[netip.Addr]*network.WireguardConfigV1Alpha1 } // PatchNode generates config patch for a node. func (w *WireguardConfigBundle) PatchNode(ip netip.Addr) (configpatcher.Patch, error) { cfg, ok := w.configs[ip] if !ok { return nil, fmt.Errorf("no wireguard config for ip %s", ip.String()) } ctr, err := container.New(cfg) if err != nil { return nil, fmt.Errorf("failed to create wireguard config container: %w", err) } return configpatcher.NewStrategicMergePatch(ctr), nil } ================================================ FILE: cmd/talosctl/pkg/talos/action/node.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package action import ( "context" "errors" "fmt" "io" "strings" "github.com/siderolabs/go-circular" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/reporter" ) // nodeTracker tracks the actions of a single node. type nodeTracker struct { ctx context.Context //nolint:containedctx node string tracker *Tracker dmesg *circular.Buffer cli *client.Client } // tailDebugLogs starts tailing the dmesg of the node. func (a *nodeTracker) tailDebugLogs() error { return retry.Constant(a.tracker.timeout).RetryWithContext(a.ctx, func(ctx context.Context) error { err := func() error { stream, err := a.cli.Dmesg(ctx, true, true) if err != nil { return err } return helpers.ReadGRPCStream(stream, func(data *common.Data, _ string, _ bool) error { _, err := fmt.Fprintf(a.dmesg, "%s: %s", a.node, data.GetBytes()) return err }) }() if err == nil { return nil } if strings.Contains(err.Error(), "file already closed") { return retry.ExpectedError(err) } statusCode := client.StatusCode(err) if errors.Is(err, io.EOF) || statusCode == codes.Unavailable { return retry.ExpectedError(err) } return err }) } func (a *nodeTracker) run() error { var ( actorIDCh chan string nodeEg errgroup.Group actorID, preActionBootID string err error ) actorIDCh = make(chan string) nodeEg.Go(func() error { return a.trackEventsWithRetry(actorIDCh) }) if a.tracker.postCheckFn != nil { preActionBootID, err = getBootID(a.ctx, a.cli) if err != nil { return err } } actorID, err = a.tracker.actionFn(a.ctx, a.cli) if err != nil { return err } select { case actorIDCh <- actorID: case <-a.ctx.Done(): return a.ctx.Err() } err = nodeEg.Wait() if err != nil { return err } if a.tracker.postCheckFn == nil { return nil } return a.runPostCheckWithRetry(preActionBootID) } func (a *nodeTracker) update(update reporter.Update) { select { case a.tracker.reportCh <- nodeUpdate{ node: a.node, update: update, }: case <-a.ctx.Done(): } } func (a *nodeTracker) trackEventsWithRetry(actorIDCh chan string) error { var ( tailEvents int32 actorID string waitForActorID = true ) return retry.Constant(a.tracker.timeout).RetryWithContext(a.ctx, func(ctx context.Context) error { // retryable function err := func() error { eventCh := make(chan client.EventResult) err := a.cli.EventsWatchV2(ctx, eventCh, client.WithTailEvents(tailEvents)) if err != nil { return err } if waitForActorID { a.update(reporter.Update{ Message: "waiting for actor ID", Status: reporter.StatusRunning, }) select { case actorID = <-actorIDCh: case <-ctx.Done(): return ctx.Err() } a.update(reporter.Update{ Message: fmt.Sprintf("actor ID: %v", actorID), Status: reporter.StatusRunning, }) waitForActorID = false } return a.handleEvents(eventCh, actorID) }() // handle retryable errors statusCode := client.StatusCode(err) if errors.Is(err, io.EOF) || statusCode == codes.Unavailable { a.update(reporter.Update{ Message: "unavailable, retrying...", Status: reporter.StatusError, }) tailEvents = -1 actorID = "" return retry.ExpectedError(err) } if err != nil { a.update(reporter.Update{ Message: fmt.Sprintf("error: %v", err), Status: reporter.StatusError, }) } return err }) } func (a *nodeTracker) runPostCheckWithRetry(preActionBootID string) error { return retry.Constant(a.tracker.timeout).RetryWithContext(a.ctx, func(ctx context.Context) error { // retryable function err := func() error { err := a.tracker.postCheckFn(ctx, a.cli, preActionBootID) if err != nil { return err } a.update(reporter.Update{ Message: "post check passed", Status: reporter.StatusSucceeded, }) return nil }() // handle retryable errors statusCode := client.StatusCode(err) if errors.Is(err, io.EOF) || statusCode == codes.Unavailable || statusCode == codes.Canceled { a.update(reporter.Update{ Message: "unavailable, retrying...", Status: reporter.StatusError, }) return retry.ExpectedError(err) } return err }) } func (a *nodeTracker) handleEvents(eventCh chan client.EventResult, actorID string) error { for { var eventResult client.EventResult select { case eventResult = <-eventCh: case <-a.ctx.Done(): return a.ctx.Err() } if a.tracker.expectedEventFn(eventResult) { status := reporter.StatusSucceeded if a.tracker.postCheckFn != nil { status = reporter.StatusRunning } a.update(reporter.Update{ Message: "events check condition met", Status: status, }) return nil } if eventResult.Error != nil { return eventResult.Error } if eventResult.Event.ActorID == actorID { err := a.handleEvent(eventResult.Event) if err != nil { return err } } } } func (a *nodeTracker) handleEvent(event client.Event) error { switch msg := event.Payload.(type) { case *machineapi.PhaseEvent: a.update(reporter.Update{ Message: fmt.Sprintf("phase: %s action: %v", msg.GetPhase(), msg.GetAction()), Status: reporter.StatusRunning, }) case *machineapi.TaskEvent: a.update(reporter.Update{ Message: fmt.Sprintf("task: %s action: %v", msg.GetTask(), msg.GetAction()), Status: reporter.StatusRunning, }) if msg.GetTask() == "stopAllServices" { return retry.ExpectedErrorf("stopAllServices task completed") } case *machineapi.SequenceEvent: errStr := "" if msg.GetError().GetMessage() != "" { errStr = fmt.Sprintf( " error: [code: %v message: %v]", msg.GetError().GetMessage(), msg.GetError().GetCode(), ) } a.update(reporter.Update{ Message: fmt.Sprintf("sequence: %s action: %v%v", msg.GetSequence(), msg.GetAction(), errStr), Status: reporter.StatusRunning, }) if msg.GetSequence() == "reboot" { return retry.ExpectedErrorf("reboot sequence completed") } if errStr != "" { return fmt.Errorf("sequence error: %s", msg.GetError().GetMessage()) } case *machineapi.MachineStatusEvent: a.update(reporter.Update{ Message: fmt.Sprintf("stage: %v ready: %v unmetCond: %v", msg.GetStage(), msg.GetStatus().GetReady(), msg.GetStatus().GetUnmetConditions()), Status: reporter.StatusRunning, }) case *machineapi.ServiceStateEvent: a.update(reporter.Update{ Message: fmt.Sprintf("service: %v message: %v healthy: %v", msg.GetService(), msg.GetMessage(), msg.GetHealth().GetHealthy()), Status: reporter.StatusRunning, }) } return nil } ================================================ FILE: cmd/talosctl/pkg/talos/action/tracker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package action import ( "context" "errors" "fmt" "io" "os" "slices" "strings" "time" "github.com/mattn/go-isatty" "github.com/siderolabs/gen/containers" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-circular" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/cmd/talosctl/cmd/common" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/reporter" ) const unauthorizedBootIDFallback = "(unauthorized)" var ( // MachineReadyEventFn is the predicate function that returns true if the event indicates the machine is ready. MachineReadyEventFn = func(event client.EventResult) bool { machineStatusEvent, ok := event.Event.Payload.(*machineapi.MachineStatusEvent) if !ok { return false } return machineStatusEvent.GetStage() == machineapi.MachineStatusEvent_RUNNING && machineStatusEvent.GetStatus().GetReady() } // StopAllServicesEventFn is the predicate function that returns true if the event indicates that all services are being stopped. StopAllServicesEventFn = func(event client.EventResult) bool { taskEvent, ok := event.Event.Payload.(*machineapi.TaskEvent) if !ok { return false } return taskEvent.GetTask() == "stopAllServices" } // BootIDChangedPostCheckFn is a post check function that returns nil if the boot ID has changed. BootIDChangedPostCheckFn = func(ctx context.Context, c *client.Client, preActionBootID string) error { if preActionBootID == unauthorizedBootIDFallback { return nil } currentBootID, err := getBootID(ctx, c) if err != nil { return err } if preActionBootID == currentBootID { return retry.ExpectedErrorf("didn't reboot yet") } return nil } ) type nodeUpdate struct { node string update reporter.Update } // Tracker runs the action in the actionFn on the nodes and tracks its progress using the provided expectedEventFn and postCheckFn. type Tracker struct { expectedEventFn func(event client.EventResult) bool actionFn func(ctx context.Context, c *client.Client) (string, error) postCheckFn func(ctx context.Context, c *client.Client, preActionBootID string) error reporter *reporter.Reporter nodeToLatestStatusUpdate map[string]reporter.Update reportCh chan nodeUpdate timeout time.Duration isTerminal bool debug bool clientExecutor ClientExecutor } // TrackerOption is the functional option for the Tracker. type TrackerOption func(*Tracker) // WithReporter sets the reporter for the tracker. func WithReporter(r *reporter.Reporter) TrackerOption { return func(t *Tracker) { if r != nil { t.reporter = r } } } // WithTimeout sets the timeout for the tracker. func WithTimeout(timeout time.Duration) TrackerOption { return func(t *Tracker) { t.timeout = timeout } } // WithPostCheck sets the post check function. func WithPostCheck(postCheckFn func(ctx context.Context, c *client.Client, preActionBootID string) error) TrackerOption { return func(t *Tracker) { t.postCheckFn = postCheckFn } } // WithDebug enables debug mode. func WithDebug(debug bool) TrackerOption { return func(t *Tracker) { t.debug = debug } } // WithTerminalOverride sets the terminal override. func WithTerminalOverride(isTerminal bool) TrackerOption { return func(t *Tracker) { t.isTerminal = isTerminal } } // NewTracker creates a new Tracker. func NewTracker( clientExecutor ClientExecutor, expectedEventFn func(event client.EventResult) bool, actionFn func(ctx context.Context, c *client.Client) (string, error), opts ...TrackerOption, ) *Tracker { tracker := Tracker{ expectedEventFn: expectedEventFn, actionFn: actionFn, nodeToLatestStatusUpdate: make(map[string]reporter.Update, len(clientExecutor.NodeList())), reporter: reporter.New(), reportCh: make(chan nodeUpdate), isTerminal: isatty.IsTerminal(os.Stderr.Fd()), clientExecutor: clientExecutor, } for _, option := range opts { option(&tracker) } return &tracker } // ClientExecutor is the interface for the client executor. type ClientExecutor interface { WithClient(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error NodeList() []string } // Run executes the action on nodes and tracks its progress by watching events with retries. // After receiving the expected event, if provided, it tracks the progress by running the post check with retries. // //nolint:gocyclo func (a *Tracker) Run() error { var failedNodesToDmesgs containers.ConcurrentMap[string, io.Reader] var eg errgroup.Group err := a.clientExecutor.WithClient(func(ctx context.Context, c *client.Client) error { ctx, cancel := context.WithTimeout(ctx, a.timeout) defer cancel() if err := helpers.ClientVersionCheck(ctx, c); err != nil { return err } eg.Go(func() error { return a.runReporter(ctx) }) // Reporter is started, it will print the errors if there is any. // So from here on we can suppress the command error to be printed to avoid it being printed twice. common.SuppressErrors = true var trackEg errgroup.Group for _, node := range a.clientExecutor.NodeList() { var ( dmesg *circular.Buffer err error ) if a.debug { dmesg, err = circular.NewBuffer() if err != nil { return err } } tracker := nodeTracker{ ctx: client.WithNode(ctx, node), node: node, tracker: a, dmesg: dmesg, cli: c, } if a.debug { eg.Go(tracker.tailDebugLogs) } trackEg.Go(func() error { trackErr := tracker.run() if trackErr != nil { if a.debug { failedNodesToDmesgs.Set(node, dmesg.GetReader()) } tracker.update(reporter.Update{ Message: trackErr.Error(), Status: reporter.StatusError, }) } return trackErr }) } return trackEg.Wait() }, grpc.WithConnectParams(grpc.ConnectParams{ // disable grpc backoff Backoff: backoff.Config{}, MinConnectTimeout: 20 * time.Second, })) if errors.Is(err, context.Canceled) { err = nil } eg.Wait() //nolint:errcheck if !a.debug { return err } var failedNodes []string failedNodesToDmesgs.ForEach(func(key string, _ io.Reader) { failedNodes = append(failedNodes, key) }) if len(failedNodes) > 0 { slices.Sort(failedNodes) fmt.Fprintf(os.Stderr, "console logs for nodes %q:\n", failedNodes) for _, node := range failedNodes { dmesgReader, _ := failedNodesToDmesgs.Get(node) _, copyErr := io.Copy(os.Stderr, dmesgReader) if copyErr != nil { fmt.Fprintf(os.Stderr, "%q: failed to print debug logs: %v\n", node, copyErr) } } } return err } // runReporter starts the (colored) stderr reporter. func (a *Tracker) runReporter(ctx context.Context) error { var ( update nodeUpdate reportUpdate reporter.Update ) ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-ctx.Done(): if a.isTerminal { a.reporter.Report(reportUpdate) } return ctx.Err() case <-ticker.C: if a.isTerminal { a.reporter.Report(reportUpdate) } case update = <-a.reportCh: if !a.isTerminal { fmt.Fprintf(os.Stderr, "%q: %v\n", update.node, update.update.Message) continue } reportUpdate = a.processNodeUpdate(update) } } } func (a *Tracker) processNodeUpdate(update nodeUpdate) reporter.Update { if update.node != "" { a.nodeToLatestStatusUpdate[update.node] = update.update } nodes := maps.Keys(a.nodeToLatestStatusUpdate) slices.Sort(nodes) messages := make([]string, 0, len(nodes)+1) messages = append(messages, fmt.Sprintf("watching nodes: %v", nodes)) for _, node := range nodes { nUpdate := a.nodeToLatestStatusUpdate[node] messages = append(messages, fmt.Sprintf(" * %s: %s", node, nUpdate.Message)) } combinedMessage := strings.Join(messages, "\n") combinedStatus := func() reporter.Status { combined := reporter.StatusSucceeded for _, status := range a.nodeToLatestStatusUpdate { if status.Status == reporter.StatusError { return reporter.StatusError } if status.Status == reporter.StatusRunning { combined = reporter.StatusRunning } } return combined }() return reporter.Update{ Message: combinedMessage, Status: combinedStatus, } } // getBootID reads the boot ID from the node. // It returns the node as the first return value and the boot ID as the second. func getBootID(ctx context.Context, c *client.Client) (string, error) { reader, err := c.Read(ctx, "/proc/sys/kernel/random/boot_id") if err != nil { return "", err } defer reader.Close() //nolint:errcheck body, err := io.ReadAll(reader) if err != nil { if status.Code(err) == codes.PermissionDenied { // we are not authorized to read the boot ID, skip the check return unauthorizedBootIDFallback, nil } return "", err } bootID := strings.TrimSpace(string(body)) return bootID, reader.Close() } ================================================ FILE: cmd/talosctl/pkg/talos/artifacts/arch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package artifacts // Arch is the artifacts architecture. type Arch string // Supported architectures. const ( ArchAmd64 Arch = "amd64" ArchArm64 Arch = "arm64" ) ================================================ FILE: cmd/talosctl/pkg/talos/artifacts/fetch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package artifacts handles manifest traversal for Overalys and Extensions. package artifacts import ( "context" "fmt" "io" "time" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/images" ) type imageHandler func(ctx context.Context, img v1.Image) error // FetchTimeout controls overall timeout for fetching artifacts for a release. const FetchTimeout = 20 * time.Minute type fetchManager struct { imageRegistry name.Registry puller *remote.Puller } func newManager() (*fetchManager, error) { imageRegistry, err := name.NewRegistry(images.Registry) if err != nil { return nil, fmt.Errorf("failed to create image registry: %w", err) } puller, err := remote.NewPuller( remote.WithPlatform(v1.Platform{ Architecture: string(ArchAmd64), OS: "linux", }), ) if err != nil { return nil, fmt.Errorf("failed to create puller: %w", err) } return &fetchManager{ imageRegistry: imageRegistry, puller: puller, }, nil } func (m *fetchManager) fetchImageByTag(imageName, tag string, imageHandler imageHandler) error { // set a timeout for fetching, but don't bind it to any context, as we want fetch operation to finish ctx, cancel := context.WithTimeout(context.Background(), FetchTimeout) defer cancel() // light check first - if the image exists, and resolve the digest // it's important to do further checks by digest exactly repoRef := m.imageRegistry.Repo(imageName).Tag(tag) descriptor, err := m.puller.Head(ctx, repoRef) if err != nil { return err } digestRef := repoRef.Digest(descriptor.Digest.String()) return m.fetchImageByDigest(digestRef, imageHandler) } // fetchImageByDigest fetches an image by digest, verifies signatures, and exports it to the storage. func (m *fetchManager) fetchImageByDigest(digestRef name.Digest, imageHandler imageHandler) error { var err error // set a timeout for fetching, but don't bind it to any context, as we want fetch operation to finish ctx, cancel := context.WithTimeout(context.Background(), FetchTimeout) defer cancel() desc, err := m.puller.Get(ctx, digestRef) if err != nil { return fmt.Errorf("error pulling image %s: %w", digestRef, err) } img, err := desc.Image() if err != nil { return fmt.Errorf("error creating image from descriptor: %w", err) } return imageHandler(ctx, img) } // imageExportHandler exports the image for further processing. func imageExportHandler(exportHandler func(r io.Reader) error) imageHandler { return func(_ context.Context, img v1.Image) error { r, w := io.Pipe() var eg errgroup.Group eg.Go(func() error { defer w.Close() //nolint:errcheck return crane.Export(img, w) }) eg.Go(func() error { err := exportHandler(r) if err != nil { r.CloseWithError(err) // signal the exporter to stop } return err }) if err := eg.Wait(); err != nil { return fmt.Errorf("error extracting the image: %w", err) } return nil } } ================================================ FILE: cmd/talosctl/pkg/talos/artifacts/images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package artifacts import ( "archive/tar" "bufio" "errors" "fmt" "io" "strings" "github.com/google/go-containerregistry/pkg/name" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/images" ) // ExtensionRef is a ref to the extension for some Talos version. type ExtensionRef struct { TaggedReference name.Tag Digest string Description string Author string imageDigest string } // OverlayRef is a ref to the overlay for some Talos version. type OverlayRef struct { Name string TaggedReference name.Tag Digest string } // FetchOfficialExtensions fetches list of extensions for specific Talos version. func FetchOfficialExtensions(tag string) ([]ExtensionRef, error) { var extensions []ExtensionRef m, err := newManager() if err != nil { return nil, err } if err := m.fetchImageByTag(images.DefaultExtensionsManifestRepository, tag, imageExportHandler(func(r io.Reader) error { var extractErr error extensions, extractErr = extractExtensionList(r) return extractErr })); err != nil { return nil, err } return extensions, nil } // FetchOfficialOverlays fetches list of overlays for specific Talos version. func FetchOfficialOverlays(tag string) ([]OverlayRef, error) { var overlays []OverlayRef m, err := newManager() if err != nil { return nil, err } if err := m.fetchImageByTag(images.DefaultOverlaysManifestRepository, tag, imageExportHandler(func(r io.Reader) error { var extractErr error overlays, extractErr = extractOverlayList(r) return extractErr })); err != nil { return nil, err } return overlays, nil } type extensionsDescriptions map[string]struct { Author string `yaml:"author"` Description string `yaml:"description"` } type overlaysDescriptions struct { Overlays []overlaysDescription `yaml:"overlays"` } type overlaysDescription struct { Name string `yaml:"name"` Image string `yaml:"image"` Digest string `yaml:"digest"` } //nolint:gocyclo func extractExtensionList(r io.Reader) ([]ExtensionRef, error) { var extensions []ExtensionRef tr := tar.NewReader(r) var descriptions extensionsDescriptions for { hdr, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { break } return nil, fmt.Errorf("error reading tar header: %w", err) } if hdr.Name == "descriptions.yaml" { decoder := yaml.NewDecoder(tr) if err = decoder.Decode(&descriptions); err != nil { return nil, fmt.Errorf("error reading descriptions.yaml file: %w", err) } } if hdr.Name == "image-digests" { scanner := bufio.NewScanner(tr) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) tagged, digest, ok := strings.Cut(line, "@") if !ok { continue } taggedRef, err := name.NewTag(tagged) if err != nil { return nil, fmt.Errorf("failed to parse tagged reference %s: %w", tagged, err) } extensions = append(extensions, ExtensionRef{ TaggedReference: taggedRef, Digest: digest, imageDigest: line, }) } if scanner.Err() != nil { return nil, fmt.Errorf("error reading image-digests: %w", scanner.Err()) } } } if extensions != nil { if descriptions != nil { for i, extension := range extensions { desc, ok := descriptions[extension.imageDigest] if !ok { continue } extensions[i].Author = desc.Author extensions[i].Description = desc.Description } } return extensions, nil } return nil, errors.New("failed to find image-digests file") } func extractOverlayList(r io.Reader) ([]OverlayRef, error) { var overlays []OverlayRef tr := tar.NewReader(r) var overlayInfo overlaysDescriptions for { hdr, err := tr.Next() if err != nil { if errors.Is(err, io.EOF) { break } return nil, fmt.Errorf("error reading tar header: %w", err) } if hdr.Name == "overlays.yaml" { decoder := yaml.NewDecoder(tr) if err = decoder.Decode(&overlayInfo); err != nil { return nil, fmt.Errorf("error reading overlays.yaml file: %w", err) } for _, overlay := range overlayInfo.Overlays { taggedRef, err := name.NewTag(overlay.Image) if err != nil { return nil, fmt.Errorf("failed to parse tagged reference %s: %w", overlay.Image, err) } overlays = append(overlays, OverlayRef{ Name: overlay.Name, TaggedReference: taggedRef, Digest: overlay.Digest, }) } } } if overlays != nil { return overlays, nil } return nil, errors.New("failed to find overlays.yaml file") } ================================================ FILE: cmd/talosctl/pkg/talos/global/client.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package global provides global flags for talosctl. package global import ( "context" "crypto/tls" "errors" "fmt" "github.com/siderolabs/crypto/x509" "google.golang.org/grpc" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // Args is a context for the Talos command line client. type Args struct { Talosconfig string CmdContext string Cluster string Nodes []string Endpoints []string SideroV1KeysDir string } // NodeList returns the list of nodes to run the command against. func (c *Args) NodeList() []string { return c.Nodes } // WithClientNoNodes wraps common code to initialize Talos client and provide cancellable context. // // WithClientNoNodes doesn't set any node information on the request context. func (c *Args) WithClientNoNodes(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error { return cli.WithContext( context.Background(), func(ctx context.Context) error { cfg, err := clientconfig.Open(c.Talosconfig) if err != nil { return fmt.Errorf("failed to open config file %q: %w", c.Talosconfig, err) } opts := []client.OptionFunc{ client.WithConfig(cfg), client.WithDefaultGRPCDialOptions(), client.WithGRPCDialOptions(dialOptions...), client.WithSideroV1KeysDir(clientconfig.CustomSideroV1KeysDirPath(c.SideroV1KeysDir)), } if c.CmdContext != "" { opts = append(opts, client.WithContextName(c.CmdContext)) } if len(c.Endpoints) > 0 { // override endpoints from command-line flags opts = append(opts, client.WithEndpoints(c.Endpoints...)) } if c.Cluster != "" { opts = append(opts, client.WithCluster(c.Cluster)) } c, err := client.New(ctx, opts...) if err != nil { return fmt.Errorf("error constructing client: %w", err) } //nolint:errcheck defer c.Close() return action(ctx, c) }, ) } // ErrConfigContext is returned when config context cannot be resolved. var ErrConfigContext = errors.New("failed to resolve config context") func (c *Args) getNodes(cli *client.Client) ([]string, error) { if len(c.Nodes) < 1 { configContext := cli.GetConfigContext() if configContext == nil { return nil, ErrConfigContext } c.Nodes = configContext.Nodes } if len(c.Nodes) < 1 { return nil, errors.New("nodes are not set for the command: please use `--nodes` flag or configuration file to set the nodes to run the command against") } return c.Nodes, nil } // WithClient builds upon WithClientNoNodes to provide set of nodes on request context based on config & flags. func (c *Args) WithClient(action func(context.Context, *client.Client) error, dialOptions ...grpc.DialOption) error { return c.WithClientNoNodes( func(ctx context.Context, cli *client.Client) error { nodes, err := c.getNodes(cli) if err != nil { return err } ctx = client.WithNodes(ctx, nodes...) return action(ctx, cli) }, dialOptions..., ) } // WithClientAndNodes builds upon WithClientNoNodes to provide a list of nodes to the function. func (c *Args) WithClientAndNodes(action func(context.Context, *client.Client, []string) error, dialOptions ...grpc.DialOption) error { return c.WithClientNoNodes( func(ctx context.Context, cli *client.Client) error { nodes, err := c.getNodes(cli) if err != nil { return err } return action(ctx, cli, nodes) }, dialOptions..., ) } // WithClientMaintenance wraps common code to initialize Talos client in maintenance (insecure mode). func (c *Args) WithClientMaintenance(enforceFingerprints []string, action func(context.Context, *client.Client) error) error { return cli.WithContext( context.Background(), func(ctx context.Context) error { tlsConfig := &tls.Config{ InsecureSkipVerify: true, } if len(enforceFingerprints) > 0 { fingerprints := make([]x509.Fingerprint, len(enforceFingerprints)) for i, stringFingerprint := range enforceFingerprints { var err error fingerprints[i], err = x509.ParseFingerprint(stringFingerprint) if err != nil { return fmt.Errorf("error parsing certificate fingerprint %q: %v", stringFingerprint, err) } } tlsConfig.VerifyConnection = x509.MatchSPKIFingerprints(fingerprints...) } c, err := client.New(ctx, client.WithTLSConfig(tlsConfig), client.WithEndpoints(c.Nodes...)) if err != nil { return err } //nolint:errcheck defer c.Close() return action(ctx, c) }, ) } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/archive.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "archive/tar" "compress/gzip" "errors" "fmt" "io" "os" "path/filepath" "github.com/siderolabs/talos/pkg/safepath" ) // ExtractFileFromTarGz reads a single file data from an archive. func ExtractFileFromTarGz(filename string, r io.ReadCloser) ([]byte, error) { defer r.Close() //nolint:errcheck zr, err := gzip.NewReader(r) if err != nil { return nil, fmt.Errorf("error initializing gzip: %w", err) } tr := tar.NewReader(zr) for { hdr, err := tr.Next() if err != nil { if err == io.EOF { break } return nil, err } hdrPath := safepath.CleanPath(hdr.Name) if hdrPath == "" { return nil, errors.New("empty tar header path") } if hdrPath == filename { if hdr.Typeflag == tar.TypeDir || hdr.Typeflag == tar.TypeSymlink { return nil, fmt.Errorf("%s is not a file", filename) } return io.ReadAll(tr) } } return nil, fmt.Errorf("couldn't find file %s in the archive", filename) } // ExtractTarGz extracts .tar.gz archive from r into filesystem under localPath. // //nolint:gocyclo func ExtractTarGz(localPath string, r io.ReadCloser) error { defer r.Close() //nolint:errcheck zr, err := gzip.NewReader(r) if err != nil { return fmt.Errorf("error initializing gzip: %w", err) } tr := tar.NewReader(zr) for { hdr, err := tr.Next() if err != nil { if err == io.EOF { break } return fmt.Errorf("error reading tar header: %s", err) } hdrPath := safepath.CleanPath(hdr.Name) if hdrPath == "" { return errors.New("empty tar header path") } path := filepath.Join(localPath, hdrPath) // TODO: do we need to clean up any '..' references? switch hdr.Typeflag { case tar.TypeDir: mode := hdr.FileInfo().Mode() mode |= 0o700 // make rwx for the owner if err = os.Mkdir(path, mode); err != nil { return fmt.Errorf("error creating directory %q mode %s: %w", path, mode, err) } if err = os.Chmod(path, mode); err != nil { return fmt.Errorf("error updating mode %s for %q: %w", mode, path, err) } case tar.TypeSymlink: if err = os.Symlink(hdr.Linkname, path); err != nil { return fmt.Errorf("error creating symlink %q -> %q: %w", path, hdr.Linkname, err) } default: mode := hdr.FileInfo().Mode() fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, mode) if err != nil { return fmt.Errorf("error creating file %q mode %s: %w", path, mode, err) } _, err = io.Copy(fp, tr) if err != nil { return fmt.Errorf("error copying data to %q: %w", path, err) } if err = fp.Close(); err != nil { return fmt.Errorf("error closing %q: %w", path, err) } if err = os.Chmod(path, mode); err != nil { return fmt.Errorf("error updating mode %s for %q: %w", mode, path, err) } } } return nil } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/checks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "context" "errors" "fmt" "os" "strings" "github.com/blang/semver/v4" "github.com/siderolabs/gen/xerrors" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/version" ) // FailIfMultiNodes checks if ctx contains multi-node request metadata. func FailIfMultiNodes(ctx context.Context, command string) error { md, ok := metadata.FromOutgoingContext(ctx) if !ok { return nil } if len(md.Get("nodes")) <= 1 { return nil } return fmt.Errorf("command %q is not supported with multiple nodes", command) } // CheckErrors goes through the returned message list and checks if any messages have errors set. func CheckErrors[T interface{ GetMetadata() *common.Metadata }](messages ...T) error { var err error for _, msg := range messages { md := msg.GetMetadata() if md != nil && md.Error != "" { err = AppendErrors(err, errors.New(md.Error)) } } return err } // VersionOutsideRangeError is returned when a node is running a Talos version that is outside the desired range. type VersionOutsideRangeError struct{} // TalosVersionCheck verifies that all nodes are running the desired Talos version. func TalosVersionCheck(ctx context.Context, c *client.Client, desired semver.Range) error { serverVersions, err := c.Version(ctx) if err != nil { return fmt.Errorf("error getting server versions: %w", err) } var errs error for _, msg := range serverVersions.GetMessages() { node := msg.GetMetadata().GetHostname() serverVersion, err := semver.ParseTolerant(msg.GetVersion().Tag) if err != nil { return fmt.Errorf("%s: error parsing server version: %w", node, err) } if !desired(serverVersion) { errs = errors.Join(errs, xerrors.NewTaggedf[VersionOutsideRangeError]("%s: server version %s is outside the desired range", node, serverVersion)) } } return errs } // ClientVersionCheck verifies that client is not outdated vs. Talos version. func ClientVersionCheck(ctx context.Context, c *client.Client) error { // ignore the error, as we are only interested in the nodes which respond serverVersions, _ := c.Version(ctx) //nolint:errcheck clientVersion, err := semver.ParseTolerant(version.NewVersion().Tag) if err != nil { return fmt.Errorf("error parsing client version: %w", err) } var warnings []string for _, msg := range serverVersions.GetMessages() { node := msg.GetMetadata().GetHostname() serverVersion, err := semver.ParseTolerant(msg.GetVersion().Tag) if err != nil { return fmt.Errorf("%s: error parsing server version: %w", node, err) } if serverVersion.Compare(clientVersion) < 0 { warnings = append(warnings, fmt.Sprintf("%s: server version %s is older than client version %s", node, serverVersion, clientVersion)) } } if warnings != nil { fmt.Fprintf(os.Stderr, "WARNING: %s\n", strings.Join(warnings, ", ")) } return nil } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/confirm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "fmt" "strings" ) var okays = []string{"y", "yes"} // Confirm asks the user to confirm their action. Anything other than // `y` and `yes` returns false. func Confirm(prompt string) bool { var inp string fmt.Printf("%s (y/N): ", prompt) fmt.Scanf("%s", &inp) //nolint:errcheck inp = strings.TrimSpace(inp) for _, ok := range okays { if strings.EqualFold(inp, ok) { return true } } return false } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/error.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "fmt" "strings" "github.com/fatih/color" "github.com/gertd/go-pluralize" "github.com/hashicorp/go-multierror" ) // AppendErrors adds errors to the multierr wrapper. func AppendErrors(err error, errs ...error) error { res := multierror.Append(err, errs...) res.ErrorFormat = func(errs []error) string { lines := make([]string, 0, len(errs)) for _, err := range errs { lines = append(lines, fmt.Sprintf(" %s", err.Error())) } count := pluralize.NewClient().Pluralize("error", len(lines), true) return color.RedString(fmt.Sprintf("%s occurred:\n%s", count, strings.Join(lines, "\n"))) } return res } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/helpers_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers_test import ( "os" "testing" "github.com/stretchr/testify/assert" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" ) type cfg struct { APIVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` } func TestExtractFileFromTarGz(t *testing.T) { file, err := os.Open("./testdata/archive.tar.gz") assert.NoError(t, err) data, err := helpers.ExtractFileFromTarGz("kubeconfig", file) assert.NoError(t, err) // just some primitive sanity check that yaml file inside was not corrupted somehow var c cfg err = yaml.Unmarshal(data, &c) assert.NoError(t, err) assert.Equal(t, c.APIVersion, "v1") assert.Equal(t, c.Kind, "Config") _, err = helpers.ExtractFileFromTarGz("void", file) assert.Error(t, err) } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/labels.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "slices" "strings" "github.com/siderolabs/gen/maps" ) // FormatLabels formats labels as a comma-separated key=value pairs. func FormatLabels(labels map[string]string) string { if len(labels) == 0 { return "" } keys := maps.Keys(labels) slices.Sort(keys) var sb strings.Builder for i, k := range keys { if i > 0 { sb.WriteString(",") } sb.WriteString(k) sb.WriteString("=") sb.WriteString(labels[k]) } return sb.String() } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/mode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "fmt" "os" "slices" "strings" "github.com/siderolabs/gen/maps" "github.com/spf13/cobra" "github.com/siderolabs/talos/pkg/cli" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // Mode apply, patch, edit config update mode. type Mode struct { options map[string]machine.ApplyConfigurationRequest_Mode Mode machine.ApplyConfigurationRequest_Mode } func (m Mode) String() string { switch m.Mode { case machine.ApplyConfigurationRequest_TRY: return modeTry case machine.ApplyConfigurationRequest_AUTO: return modeAuto case machine.ApplyConfigurationRequest_NO_REBOOT: return modeNoReboot case machine.ApplyConfigurationRequest_REBOOT: return modeReboot case machine.ApplyConfigurationRequest_STAGED: return modeStaged default: return modeAuto } } // Set implements Flag interface. func (m *Mode) Set(value string) error { mode, ok := m.options[value] if !ok { return fmt.Errorf("possible options are: %s", m.Type()) } m.Mode = mode return nil } // Type implements Flag interface. func (m *Mode) Type() string { options := maps.Keys(m.options) slices.Sort(options) return strings.Join(options, ", ") } const ( modeAuto = "auto" modeNoReboot = "no-reboot" modeReboot = "reboot" modeStaged = "staged" modeTry = "try" ) // AddModeFlags adds deprecated flags to the command and registers mode flag with it's parser. func AddModeFlags(mode *Mode, command *cobra.Command) { modes := map[string]machine.ApplyConfigurationRequest_Mode{ modeAuto: machine.ApplyConfigurationRequest_AUTO, modeNoReboot: machine.ApplyConfigurationRequest_NO_REBOOT, modeReboot: machine.ApplyConfigurationRequest_REBOOT, modeStaged: machine.ApplyConfigurationRequest_STAGED, modeTry: machine.ApplyConfigurationRequest_TRY, } mode.Mode = machine.ApplyConfigurationRequest_AUTO mode.options = modes command.Flags().VarP(mode, "mode", "m", "apply config mode") } // PrintApplyResults prints out all warnings and auto apply results. func PrintApplyResults(resp *machine.ApplyConfigurationResponse) { for _, m := range resp.GetMessages() { for _, w := range m.GetWarnings() { cli.Warning("%s", w) } if m.ModeDetails != "" { fmt.Fprintln(os.Stderr, m.ModeDetails) } } } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/resources.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "context" "errors" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/client" ) // ForEachResource gets resources from the controller runtime and runs a callback for each resource. // //nolint:gocyclo func ForEachResource(ctx context.Context, c *client.Client, callbackRD func(rd *meta.ResourceDefinition) error, callback func(ctx context.Context, hostname string, r resource.Resource, callError error) error, namespace string, args ...string, ) error { if len(args) == 0 { return errors.New("not enough arguments: at least 1 is expected") } resourceType := args[0] var resourceID string if len(args) > 1 { resourceID = args[1] } md, _ := metadata.FromOutgoingContext(ctx) nodes := md.Get("nodes") if len(nodes) == 0 { nodes = []string{""} } // fetch the RD from the first node (it doesn't matter which one to use, so we'll use the first one) rd, err := c.ResolveResourceKind(client.WithNode(ctx, nodes[0]), &namespace, resourceType) if err != nil { return err } if callbackRD != nil { if err = callbackRD(rd); err != nil { return err } } resourceType = rd.TypedSpec().Type for _, node := range nodes { var nodeCtx context.Context if node == "" { nodeCtx = ctx } else { nodeCtx = client.WithNode(ctx, node) } if resourceID != "" { r, callErr := c.COSI.Get( nodeCtx, resource.NewMetadata(namespace, rd.TypedSpec().Type, resourceID, resource.VersionUndefined), state.WithGetUnmarshalOptions(state.WithSkipProtobufUnmarshal()), ) if err = callback(ctx, node, r, callErr); err != nil { return err } } else { items, callErr := c.COSI.List( nodeCtx, resource.NewMetadata(namespace, resourceType, "", resource.VersionUndefined), state.WithListUnmarshalOptions(state.WithSkipProtobufUnmarshal()), ) if callErr != nil { if err = callback(ctx, node, nil, callErr); err != nil { return err } continue } for _, r := range items.Items { if err = callback(ctx, node, r, nil); err != nil { return err } } } } return nil } ================================================ FILE: cmd/talosctl/pkg/talos/helpers/stream.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package helpers import ( "errors" "fmt" "io" "google.golang.org/grpc" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/proto" ) // Stream implements the contract for the grpc stream of a specific type. type Stream[T proto.Message] interface { Recv() (T, error) grpc.ClientStream } // Message defines the contract for the grpc message. type Message interface { GetMetadata() *common.Metadata proto.Message } // ReadGRPCStream consumes all messages from the gRPC stream, handles errors, calls the passed handler for each message. func ReadGRPCStream[S Stream[T], T Message](stream S, handler func(T, string, bool) error) error { var streamErrs error defaultNode := client.RemotePeer(stream.Context()) multipleNodes := false for { info, err := stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { return streamErrs } return fmt.Errorf("error streaming results: %s", err) } node := defaultNode if info.GetMetadata() != nil { if info.GetMetadata().Hostname != "" { multipleNodes = true node = info.GetMetadata().Hostname } if info.GetMetadata().Error != "" { streamErrs = AppendErrors(streamErrs, errors.New(info.GetMetadata().Error)) continue } } if err = handler(info, node, multipleNodes); err != nil { var errNonFatal *ErrNonFatalError if errors.As(err, &errNonFatal) { streamErrs = AppendErrors(streamErrs, err) continue } return err } } } // ErrNonFatalError represents the error that can be returned from the handler in the gRPC stream reader // which doesn't mean that we should stop iterating over the messages in the stream, but log this error // and continue the process. type ErrNonFatalError struct { err error } // Error implements error interface. func (e *ErrNonFatalError) Error() string { return e.err.Error() } // NonFatalError wraps another error into a ErrNonFatal. func NonFatalError(err error) error { return &ErrNonFatalError{ err: err, } } ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/testdata/malformed.in.yaml ================================================ data: # This is a comment some: other: a: b: c ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/testdata/malformed.out.yaml ================================================ data: some: other: a: b: c ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/testdata/multidoc.in.yaml ================================================ # siderolink config apiVersion: v1alpha1 kind: SideroLinkConfig # kind of the document # apiUrl is the URL of the SideroLink API endpoint apiUrl: grpc://172.20.0.1:4000/?jointoken=foo --- apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink # named document url: tcp://[fdae:41e4:649b:9303::1]:4001/ options: # options are optional # more options foo: bar # this option --- apiVersion: v1alpha1 kind: EventSinkConfig endpoint: "[fdae:41e4:649b:9303::1]:8080" # end of document ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/testdata/multidoc.out.yaml ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: grpc://172.20.0.1:4000/?jointoken=foo --- apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ options: foo: bar --- apiVersion: v1alpha1 kind: EventSinkConfig endpoint: "[fdae:41e4:649b:9303::1]:8080" ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/yamlstrip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package yamlstrip provides YAML file manipulation. package yamlstrip import ( "bytes" "errors" "io" "go.yaml.in/yaml/v4" ) // Comments strips comments from a YAML file. // // If the YAML file is parseable, it will be accurately stripped. Otherwise, it // will be stripped in a best-effort manner. func Comments(b []byte) []byte { stripped, err := stripViaDecoding(b) if err != nil { stripped = stripManual(b) } return stripped } func stripViaDecoding(b []byte) ([]byte, error) { var out bytes.Buffer decoder := yaml.NewDecoder(bytes.NewReader(b)) encoder := yaml.NewEncoder(&out) for { var node yaml.Node err := decoder.Decode(&node) if err != nil { if errors.Is(err, io.EOF) { break } return nil, err } removeComments(&node) if err = encoder.Encode(&node); err != nil { return nil, err } } return out.Bytes(), nil } func removeComments(node *yaml.Node) { node.FootComment = "" node.HeadComment = "" node.LineComment = "" for _, child := range node.Content { removeComments(child) } } func stripManual(b []byte) []byte { var stripped []byte lines := bytes.Split(b, []byte("\n")) for i, line := range lines { trimline := bytes.TrimSpace(line) // this is not accurate, but best effort if bytes.HasPrefix(trimline, []byte("#")) && !bytes.HasPrefix(trimline, []byte("#!")) { continue } stripped = append(stripped, line...) if i < len(lines)-1 { stripped = append(stripped, '\n') } } return stripped } ================================================ FILE: cmd/talosctl/pkg/talos/yamlstrip/yamlstrip_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package yamlstrip_test import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/yamlstrip" ) func TestComments(t *testing.T) { testCases, err := filepath.Glob(filepath.Join("testdata", "*.in.yaml")) require.NoError(t, err) for _, path := range testCases { t.Run(filepath.Base(path), func(t *testing.T) { in, err := os.ReadFile(path) require.NoError(t, err) expected, err := os.ReadFile(strings.ReplaceAll(path, ".in.yaml", ".out.yaml")) require.NoError(t, err) out := yamlstrip.Comments(in) require.Equal(t, string(expected), string(out)) }) } } ================================================ FILE: go.mod ================================================ module github.com/siderolabs/talos go 1.26.1 replace ( // forked coredns so we don't carry caddy and other stuff into the Talos github.com/coredns/coredns => github.com/siderolabs/coredns v1.14.53 // see https://github.com/jsimonetti/rtnetlink/pull/306 github.com/jsimonetti/rtnetlink/v2 => github.com/shanduur/rtnetlink/v2 v2.0.0-20260313131132-118a2ded4751 // forked ethtool introduces missing APIs github.com/mdlayher/ethtool => github.com/siderolabs/ethtool v0.4.0-sidero // see https://github.com/mdlayher/kobject/pull/5 github.com/mdlayher/kobject => github.com/smira/kobject v0.0.0-20240304111826-49c8d4613389 // Use nested module. github.com/siderolabs/talos/pkg/machinery => ./pkg/machinery // fork to add Talos-specific userspace socket location: https://github.com/siderolabs/talos/issues/8514 golang.zx2c4.com/wireguard/wgctrl => github.com/siderolabs/wgctrl-go v0.0.0-20251029173431-c4fd5f6a4e72 ) // deadcode elimination fix replacement: https://github.com/siderolabs/talos/issues/11296 // upstream PR: https://github.com/containerd/containerd/pull/12175 // this a fork with containerd 2.2 branch + the commit from the PR above replace github.com/containerd/containerd/v2 => github.com/smira/containerd/v2 v2.2.3-0.20260311174942-e5fa687ba763 // Kubernetes dependencies sharing the same version. require ( k8s.io/api v0.35.2 k8s.io/apiextensions-apiserver v0.35.2 k8s.io/apimachinery v0.35.2 k8s.io/apiserver v0.35.2 k8s.io/client-go v0.35.2 k8s.io/component-base v0.35.2 k8s.io/cri-api v0.35.2 k8s.io/kube-scheduler v0.35.2 k8s.io/kubectl v0.35.2 k8s.io/kubelet v0.35.2 k8s.io/pod-security-admission v0.35.2 ) require ( cloud.google.com/go/compute/metadata v0.9.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.4.0 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 github.com/alexflint/go-filemutex v1.3.0 github.com/aws/aws-sdk-go-v2/config v1.32.12 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 github.com/aws/aws-sdk-go-v2/service/acm v1.37.22 github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 github.com/aws/smithy-go v1.24.2 github.com/beevik/ntp v1.5.0 github.com/blang/semver/v4 v4.0.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/containerd/cgroups/v3 v3.1.3 github.com/containerd/containerd/api v1.10.0 github.com/containerd/containerd/v2 v2.2.2 github.com/containerd/errdefs v1.0.0 github.com/containerd/log v0.1.0 github.com/containerd/platforms v1.0.0-rc.2 github.com/containerd/typeurl/v2 v2.2.3 github.com/containernetworking/cni v1.3.0 github.com/containernetworking/plugins v1.9.0 github.com/coredns/coredns v1.14.2 github.com/coreos/go-iptables v0.8.0 github.com/cosi-project/runtime v1.14.0 github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e github.com/distribution/reference v0.6.0 github.com/docker/cli v29.3.0+incompatible github.com/dustin/go-humanize v1.0.1 github.com/elastic/go-libaudit/v2 v2.6.2 github.com/equinix-ms/go-vmw-guestrpc v1.0.0 github.com/fatih/color v1.18.0 github.com/florianl/go-tc v0.4.7 github.com/foxboron/go-uefi v0.0.0-20251010190908-d29549a44f29 github.com/freddierice/go-losetup/v2 v2.0.1 github.com/fsnotify/fsnotify v1.9.0 github.com/g0rbe/go-chattr v1.0.1 github.com/gdamore/tcell/v2 v2.13.8 github.com/gertd/go-pluralize v0.2.1 github.com/godbus/dbus/v5 v5.2.2 github.com/golang/mock v1.7.0-rc.1 github.com/google/cadvisor v0.56.2 github.com/google/cel-go v0.27.0 github.com/google/go-containerregistry v0.21.2 github.com/google/go-tpm v0.9.8 github.com/google/nftables v0.3.0 github.com/google/uuid v1.6.0 github.com/gopacket/gopacket v1.5.0 github.com/gosuri/uiprogress v0.0.1 github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-envparse v0.1.0 github.com/hashicorp/go-getter/v2 v2.2.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hetznercloud/hcloud-go/v2 v2.36.0 github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91 github.com/jeromer/syslogparser v1.1.0 github.com/jsimonetti/rtnetlink/v2 v2.2.0 github.com/jxskiss/base62 v1.1.0 github.com/klauspost/compress v1.18.4 github.com/klauspost/cpuid/v2 v2.3.0 github.com/linode/go-metadata v0.2.4 github.com/martinlindhe/base36 v1.1.1 github.com/mattn/go-isatty v0.0.20 github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 github.com/mdlayher/ethtool v0.5.1 github.com/mdlayher/genetlink v1.3.2 github.com/mdlayher/kobject v0.0.0-20200520190114-19ca17470d7d github.com/mdlayher/netlink v1.9.0 github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8 github.com/mdp/qrterminal/v3 v3.2.1 github.com/miekg/dns v1.1.72 github.com/moby/moby/api v1.54.0 github.com/moby/moby/client v0.3.0 github.com/navidys/tvxwidgets v0.13.0 github.com/nberlee/go-netstat v0.1.2 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/opencontainers/runtime-spec v1.3.0 github.com/packethost/packngo v0.31.0 github.com/pelletier/go-toml/v2 v2.2.4 github.com/pin/tftp/v3 v3.2.0 github.com/pkg/xattr v0.4.12 github.com/pmorjan/kmod v1.1.1 github.com/prometheus/procfs v0.20.1 github.com/rivo/tview v0.42.0 github.com/rs/xid v1.6.0 github.com/ryanuber/columnize v2.1.2+incompatible github.com/ryanuber/go-glob v1.0.0 github.com/safchain/ethtool v0.7.0 github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 github.com/siderolabs/crypto v0.6.4 github.com/siderolabs/discovery-api v0.1.8 github.com/siderolabs/discovery-client v0.1.15 github.com/siderolabs/gen v0.8.6 github.com/siderolabs/go-api-signature v0.3.12 github.com/siderolabs/go-blockdevice/v2 v2.0.26 github.com/siderolabs/go-circular v0.2.3 github.com/siderolabs/go-cmd v0.2.0 github.com/siderolabs/go-copy v0.1.0 github.com/siderolabs/go-debug v0.6.2 github.com/siderolabs/go-kmsg v0.1.5 github.com/siderolabs/go-kubeconfig v0.1.1 github.com/siderolabs/go-kubernetes v0.2.33 github.com/siderolabs/go-loadbalancer v0.5.0 github.com/siderolabs/go-pcidb v0.3.3 github.com/siderolabs/go-pointer v1.0.1 github.com/siderolabs/go-procfs v0.1.2 github.com/siderolabs/go-retry v0.3.3 github.com/siderolabs/go-smbios v0.3.3 github.com/siderolabs/go-tail v0.1.1 github.com/siderolabs/go-talos-support v0.1.4 github.com/siderolabs/grpc-proxy v0.5.1 github.com/siderolabs/kms-client v0.2.0 github.com/siderolabs/net v0.4.0 github.com/siderolabs/proto-codec v0.1.3 github.com/siderolabs/siderolink v0.3.15 github.com/siderolabs/talos/pkg/machinery v1.13.0-alpha.2 github.com/sigstore/cosign/v3 v3.0.5 github.com/sigstore/sigstore v1.10.5-0.20260304232115-b56c8664d026 github.com/sigstore/sigstore-go v1.1.4 github.com/sirupsen/logrus v1.9.4 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 github.com/thejerf/suture/v4 v4.0.6 github.com/theupdateframework/go-tuf/v2 v2.4.1 github.com/u-root/u-root v0.16.0 github.com/ulikunitz/xz v0.5.15 github.com/vultr/metadata v1.1.0 go.etcd.io/etcd/api/v3 v3.6.8 go.etcd.io/etcd/client/pkg/v3 v3.6.8 go.etcd.io/etcd/client/v3 v3.6.8 go.etcd.io/etcd/etcdutl/v3 v3.6.8 go.uber.org/goleak v1.3.0 go.uber.org/zap v1.27.1 go.yaml.in/yaml/v4 v4.0.0-rc.4 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/net v0.52.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 golang.org/x/sys v0.42.0 golang.org/x/term v0.41.0 golang.org/x/text v0.35.0 golang.org/x/time v0.15.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 google.golang.org/grpc v1.79.3 google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af gopkg.in/typ.v4 v4.4.0 k8s.io/klog/v2 v2.140.0 kernel.org/pub/linux/libs/security/libcap/cap v1.2.77 sigs.k8s.io/hydrophone v0.7.0 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.1 // indirect cyphar.com/go-pathrs v0.2.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.14.0-rc.1 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/ProtonMail/gopenpgp/v2 v2.9.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apparentlymart/go-cidr v1.1.0 // indirect github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2 v1.41.4 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cilium/ebpf v0.21.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/go-cni v1.1.13 // indirect github.com/containerd/plugin v1.0.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/containerd/ttrpc v1.2.7 // indirect github.com/coreos/go-oidc/v3 v3.17.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.6.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.4 // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/dot v1.11.0 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch v5.9.11+incompatible // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fluxcd/cli-utils v0.36.0-flux.15 // indirect github.com/fluxcd/pkg/ssa v0.60.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gdamore/encoding v1.0.1 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-chi/chi/v5 v5.2.4 // indirect github.com/go-errors/errors v1.5.1 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.24.1 // indirect github.com/go-openapi/errors v0.22.7 // indirect github.com/go-openapi/jsonpointer v0.22.4 // indirect github.com/go-openapi/jsonreference v0.21.4 // indirect github.com/go-openapi/loads v0.23.2 // indirect github.com/go-openapi/runtime v0.29.2 // indirect github.com/go-openapi/spec v0.22.3 // indirect github.com/go-openapi/strfmt v0.26.0 // indirect github.com/go-openapi/swag v0.25.5 // indirect github.com/go-openapi/swag/cmdutils v0.25.5 // indirect github.com/go-openapi/swag/conv v0.25.5 // indirect github.com/go-openapi/swag/fileutils v0.25.5 // indirect github.com/go-openapi/swag/jsonname v0.25.5 // indirect github.com/go-openapi/swag/jsonutils v0.25.5 // indirect github.com/go-openapi/swag/loading v0.25.5 // indirect github.com/go-openapi/swag/mangling v0.25.5 // indirect github.com/go-openapi/swag/netutils v0.25.5 // indirect github.com/go-openapi/swag/stringutils v0.25.5 // indirect github.com/go-openapi/swag/typeutils v0.25.5 // indirect github.com/go-openapi/swag/yamlutils v0.25.5 // indirect github.com/go-openapi/validate v0.25.1 // indirect github.com/go-resty/resty/v2 v2.17.1 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/certificate-transparency-go v1.3.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gosuri/uilive v0.0.4 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/in-toto/attestation v1.1.2 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/letsencrypt/boulder v0.20260223.0 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lmittmann/tint v1.0.4 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 // indirect github.com/mdlayher/packet v1.1.2 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/neticdk/go-stdlib v1.0.1 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect github.com/oklog/ulid/v2 v2.1.1 // indirect github.com/opencontainers/selinux v1.13.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.6 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/siderolabs/protoenc v0.2.4 // indirect github.com/siderolabs/tcpproxy v0.1.0 // indirect github.com/sigstore/protobuf-specs v0.5.0 // indirect github.com/sigstore/rekor v1.5.0 // indirect github.com/sigstore/rekor-tiles/v2 v2.2.0 // indirect github.com/sigstore/timestamp-authority/v2 v2.0.4 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c // indirect github.com/transparency-dev/merkle v0.0.2 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/vbatts/tar-split v0.12.2 // indirect github.com/vishvananda/netlink v1.3.1 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 // indirect github.com/xlab/treeprint v1.2.0 // indirect github.com/zalando/go-keyring v0.2.6 // indirect go.etcd.io/bbolt v1.4.3 // indirect go.etcd.io/etcd/pkg/v3 v3.6.8 // indirect go.etcd.io/etcd/server/v3 v3.6.8 // indirect go.etcd.io/raft/v3 v3.6.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect go.opentelemetry.io/otel v1.40.0 // indirect 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.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/mod v0.33.0 // indirect golang.org/x/tools v0.42.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/cli-runtime v0.35.2 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect k8s.io/utils v0.0.0-20260108192941-914a6e750570 // indirect kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 // indirect rsc.io/qr v0.2.0 // indirect sigs.k8s.io/controller-runtime v0.22.2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/knftables v0.0.18 // indirect sigs.k8s.io/kustomize/api v0.20.1 // indirect sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) exclude github.com/containerd/containerd v1.7.0 ================================================ FILE: go.sum ================================================ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs= cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/kms v1.25.0 h1:gVqvGGUmz0nYCmtoxWmdc1wli2L1apgP8U4fghPGSbQ= cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8= cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw= filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.4.0 h1:mtvR5ZXH5Ew6PSONd5lO5OXovWP1E3oAlgC8fpxor2Q= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates v1.4.0/go.mod h1:u560+RFVfG0CBPzkXlDW43slESbBAQjgDGi3r6z+wk8= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/gopenpgp/v2 v2.9.0 h1:ruLzBmwe4dR1hdnrsEJ/S7psSBmV15gFttFUPP/+/kE= github.com/ProtonMail/gopenpgp/v2 v2.9.0/go.mod h1:IldDyh9Hv1ZCCYatTuuEt1XZJ0OPjxLpTarDfglih7s= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/alexflint/go-filemutex v1.3.0 h1:LgE+nTUWnQCyRKbpoceKZsPQbs84LivvgwUymZXdOcM= github.com/alexflint/go-filemutex v1.3.0/go.mod h1:U0+VA/i30mGBlLCrFPGtTe9y6wGQfNAWPBTekHQ+c8A= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2 h1:7Ip0wMmLHLRJdrloDxZfhMm0xrLXZS8+COSu2bXmEQs= github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/acm v1.37.22 h1:gc1fzEkQZXff6e6rF6BpsHqYEhBtpL5ckBdiSXzWySk= github.com/aws/aws-sdk-go-v2/service/acm v1.37.22/go.mod h1:YUf/0QA0wySPQ3TJC5cHWHLwWw9nV3EXgTPkzcjnoq0= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= github.com/aws/aws-sdk-go-v2/service/kms v1.50.3 h1:s/zDSG/a/Su9aX+v0Ld9cimUCdkr5FWPmBV8owaEbZY= github.com/aws/aws-sdk-go-v2/service/kms v1.50.3/go.mod h1:/iSgiUor15ZuxFGQSTf3lA2FmKxFsQoc2tADOarQBSw= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beevik/ntp v1.5.0 h1:y+uj/JjNwlY2JahivxYvtmv4ehfi3h74fAuABB9ZSM4= github.com/beevik/ntp v1.5.0/go.mod h1:mJEhBrwT76w9D+IfOEGvuzyuudiW9E52U2BaTrMOYow= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/brianvoe/gofakeit/v7 v7.7.3 h1:RWOATEGpJ5EVg2nN8nlaEyaV/aB4d6c3GqYrbqQekss= github.com/brianvoe/gofakeit/v7 v7.7.3/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk= github.com/cilium/ebpf v0.21.0 h1:4dpx1J/B/1apeTmWBH5BkVLayHTkFrMovVPnHEk+l3k= github.com/cilium/ebpf v0.21.0/go.mod h1:1kHKv6Kvh5a6TePP5vvvoMa1bclRyzUXELSs272fmIQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ= github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/go-cni v1.1.13 h1:eFSGOKlhoYNxpJ51KRIMHZNlg5UgocXEIEBGkY7Hnis= github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/containernetworking/plugins v1.9.0 h1:Mg3SXBdRGkdXyFC4lcwr6u2ZB2SDeL6LC3U+QrEANuQ= github.com/containernetworking/plugins v1.9.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c= github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495 h1:JFeOmbjLnVRhvmLHyuO3M1pfXWlPWpwkdM8UqXZRtBg= github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc= github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc= github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8= 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/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/cosi-project/runtime v1.14.0 h1:puGI7sssk1h2KScC4ETjC+M7nyN+0ur44bAuSLdY91A= github.com/cosi-project/runtime v1.14.0/go.mod h1:sd2+E6DjC/QjrnlEEglINDZ4FUW7cVDMB5aG98Dl3LA= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q= github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e h1:lj77EKYUpYXTd8CD/+QMIf8b6OIOTsfEBSXiAzuEHTU= github.com/detailyang/go-fallocate v0.0.0-20180908115635-432fa640bd2e/go.mod h1:3ZQK6DMPSz/QZ73jlWxBtUhNA8xZx7LzUFSq/OfP8vk= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 h1:lxmTCgmHE1GUYL7P0MlNa00M67axePTq+9nBSGddR8I= github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk= github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elastic/go-libaudit/v2 v2.6.2 h1:1PM6wVBTJHJQYsKl8jfA9/Aw9pFty5uUezPiUfKtOI4= github.com/elastic/go-libaudit/v2 v2.6.2/go.mod h1:8205nkf2oSrXFlO4H5j8/cyVMoSF3Y7jt+FjgS4ubQU= github.com/elastic/go-licenser v0.4.1 h1:1xDURsc8pL5zYT9R29425J3vkHdt4RT5TNEMeRN48x4= github.com/elastic/go-licenser v0.4.1/go.mod h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU= github.com/emicklei/dot v1.11.0 h1:zsrhCuFHAJge/aZIC4N4LdHy5tqYu4tWEaUzIwdYj4Y= github.com/emicklei/dot v1.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/equinix-ms/go-vmw-guestrpc v1.0.0 h1:O1+zDPdtli9NDWFyYe9ChaaROr3MJusEP0b89UTZDTI= github.com/equinix-ms/go-vmw-guestrpc v1.0.0/go.mod h1:YB8EWh3S7z7T2BmHYc95pGynNqjMLJCFnt0X57B0+p4= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/florianl/go-tc v0.4.7 h1:Ysai5TIx4PgOzqI/1cse/pquOFCEkWofKtc/EPumfrg= github.com/florianl/go-tc v0.4.7/go.mod h1:Fdz6eHitQZwylSvpAW3y9R9cUrnS/zinuAdjJpD7XqY= github.com/fluxcd/cli-utils v0.36.0-flux.15 h1:Et5QLnIpRjj+oZtM9gEybkAaoNsjysHq0y1253Ai94Y= github.com/fluxcd/cli-utils v0.36.0-flux.15/go.mod h1:AqRUmWIfNE7cdL6NWSGF0bAlypGs+9x5UQ2qOtlEzv4= github.com/fluxcd/pkg/ssa v0.60.0 h1:ikA78TWSLDmIc8I/goGAU/buYF6jto/gswE5hnOfWGk= github.com/fluxcd/pkg/ssa v0.60.0/go.mod h1:3k9t4B4UjOF0536RQssQ4r9BXLSCq6FSTnUNKseFVHQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/foxboron/go-uefi v0.0.0-20251010190908-d29549a44f29 h1:2XQY2y+CZCLpFjK5p2EEMwDdP99c7AWP29WhCTkiQm8= github.com/foxboron/go-uefi v0.0.0-20251010190908-d29549a44f29/go.mod h1:sqQZKX1X86EAN4C07n6DcbGC/DCN36BNaX/uNvjzmfk= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/freddierice/go-losetup/v2 v2.0.1 h1:wPDx/Elu9nDV8y/CvIbEDz5Xi5Zo80y4h7MKbi3XaAI= github.com/freddierice/go-losetup/v2 v2.0.1/go.mod h1:TEyBrvlOelsPEhfWD5rutNXDmUszBXuFnwT1kIQF4J8= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/g0rbe/go-chattr v1.0.1 h1:CHwYB+WKB46hkzt6Jxyvkyrz7u9njghUOFvmx2gir84= github.com/g0rbe/go-chattr v1.0.1/go.mod h1:yQc6VPJfpDDC1g+W2t47+yYmzBNioax/GLiyJ25/IOs= github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= github.com/gdamore/tcell/v2 v2.13.8 h1:Mys/Kl5wfC/GcC5Cx4C2BIQH9dbnhnkPgS9/wF3RlfU= github.com/gdamore/tcell/v2 v2.13.8/go.mod h1:+Wfe208WDdB7INEtCsNrAN6O2m+wsTPk1RAovjaILlo= github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA= github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.24.1 h1:Xp+7Yn/KOnVWYG8d+hPksOYnCYImE3TieBa7rBOesYM= github.com/go-openapi/analysis v0.24.1/go.mod h1:dU+qxX7QGU1rl7IYhBC8bIfmWQdX4Buoea4TGtxXY84= github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= github.com/go-openapi/loads v0.23.2 h1:rJXAcP7g1+lWyBHC7iTY+WAF0rprtM+pm8Jxv1uQJp4= github.com/go-openapi/loads v0.23.2/go.mod h1:IEVw1GfRt/P2Pplkelxzj9BYFajiWOtY2nHZNj4UnWY= github.com/go-openapi/runtime v0.29.2 h1:UmwSGWNmWQqKm1c2MGgXVpC2FTGwPDQeUsBMufc5Yj0= github.com/go-openapi/runtime v0.29.2/go.mod h1:biq5kJXRJKBJxTDJXAa00DOTa/anflQPhT0/wmjuy+0= github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc= github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs= github.com/go-openapi/strfmt v0.26.0 h1:SDdQLyOEqu8W96rO1FRG1fuCtVyzmukky0zcD6gMGLU= github.com/go-openapi/strfmt v0.26.0/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y= github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU= github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA= github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c= github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU= github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14= github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= github.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw= github.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-openapi/validate v0.25.1 h1:sSACUI6Jcnbo5IWqbYHgjibrhhmt3vR6lCzKZnmAgBw= github.com/go-openapi/validate v0.25.1/go.mod h1:RMVyVFYte0gbSTaZ0N4KmTn6u/kClvAFp+mAVfS/DQc= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s= github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI= github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4= github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cadvisor v0.56.2 h1:ra6p4Nxc4zT8VLbZscWUxhvjsqy+1AzMvuSdEM90o1w= github.com/google/cadvisor v0.56.2/go.mod h1:CWidr4DqGbkN4aKuOEjLB7Bab3gl01Xxm3co38C3xRU= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A= github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.21.2 h1:vYaMU4nU55JJGFC9JR/s8NZcTjbE9DBBbvusTW9NeS0= github.com/google/go-containerregistry v0.21.2/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo= github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY= github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc= github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= 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/nftables v0.3.0 h1:bkyZ0cbpVeMHXOrtlFc8ISmfVqq5gPJukoYieyVmITg= github.com/google/nftables v0.3.0/go.mod h1:BCp9FsrbF1Fn/Yu6CLUc9GGZFw/+hsxfluNXXmxBfRM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 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/trillian v1.7.2 h1:EPBxc4YWY4Ak8tcuhyFleY+zYlbCDCa4Sn24e1Ka8Js= github.com/google/trillian v1.7.2/go.mod h1:mfQJW4qRH6/ilABtPYNBerVJAJ/upxHLX81zxNQw05s= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao= github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8= github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= github.com/gopacket/gopacket v1.5.0 h1:9s9fcSUVKFlRV97B77Bq9XNV3ly2gvvsneFMQUGjc+M= github.com/gopacket/gopacket v1.5.0/go.mod h1:i3NaGaqfoWKAr1+g7qxEdWsmfT+MXuWkAe9+THv8LME= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gosuri/uiprogress v0.0.1 h1:0kpv/XY/qTmFWl/SkaJykZXrBBzwwadmW8fRb7RJSxw= github.com/gosuri/uiprogress v0.0.1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs/O40yoNK9vmy4rhUGBVyMf1lISBGtXRpsu/Qu/o= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-envparse v0.1.0 h1:bE++6bhIsNCPLvgDZkYqo3nA+/PFI51pkrHdmPSDFPY= github.com/hashicorp/go-envparse v0.1.0/go.mod h1:OHheN1GoygLlAkTlXLXvAdnXdZxy8JUweQ1rAXx1xnc= github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk= github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= github.com/hetznercloud/hcloud-go/v2 v2.36.0 h1:HlLL/aaVXUulqe+rsjoJmrxKhPi1MflL5O9iq5QEtvo= github.com/hetznercloud/hcloud-go/v2 v2.36.0/go.mod h1:MnN/QJEa/RYNQiiVoJjNHPntM7Z1wlYPgJ2HA40/cDE= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/hugelgupf/vmtest v0.0.0-20240307030256-5d9f3d34a58d h1:nP8SfQJqruIVSWYJTuYc37jLHEY1Z0fF+zKSrs3K/C8= github.com/hugelgupf/vmtest v0.0.0-20240307030256-5d9f3d34a58d/go.mod h1:B63hDJMhTupLWCHwopAyEo7wRFowx9kOc8m8j1sfOqE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E= github.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91 h1:u9i04mGE3iliBh0EFuWaKsmcwrLacqGmq1G3XoaM7gY= github.com/insomniacslk/dhcp v0.0.0-20260220084031-5adc3eb26f91/go.mod h1:qfvBmyDNp+/liLEYWRvqny/PEz9hGe2Dz833eXILSmo= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs= github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A= github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= github.com/jeromer/syslogparser v1.1.0 h1:HES0EviO9iPvCu56LjVFVhbM3o0BckDlIbQfkkaRJAw= github.com/jeromer/syslogparser v1.1.0/go.mod h1:zfowyus/j2SEgW31bIntTvEBE2zCSndtFsCC6NcW4S4= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY= github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw= github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs= github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA= github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U= github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmKSdU4VGSiv1bMsdqNALI4RSvvjtz65tTMCnD05qLo= github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs= github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA= github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/letsencrypt/boulder v0.20260223.0 h1:xdS2OnJNUasR6TgVIOpqqcvdkOu47+PQQMBk9ThuWBw= github.com/letsencrypt/boulder v0.20260223.0/go.mod h1:r3aTSA7UZ7dbDfiGK+HLHJz0bWNbHk6YSPiXgzl23sA= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/linode/go-metadata v0.2.4 h1:iif+14PN5T0u/wRnu6i/UlLQrlaFlWvDJzm0Wl9Rn0I= github.com/linode/go-metadata v0.2.4/go.mod h1:h6yHiRupdKvyPkydU9g8kUA3QKdAgj3WX2wrF+mUv1c= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc= github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/martinlindhe/base36 v1.1.1 h1:1F1MZ5MGghBXDZ2KJ3QfxmiydlWOGB8HCEtkap5NkVg= github.com/martinlindhe/base36 v1.1.1/go.mod h1:vMS8PaZ5e/jV9LwFKlm0YLnXl/hpOihiBxKkIoc3g08= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875 h1:ql8x//rJsHMjS+qqEag8n3i4azw1QneKh5PieH9UEbY= github.com/mdlayher/arp v0.0.0-20220512170110-6706a2966875/go.mod h1:kfOoFJuHWp76v1RgZCb9/gVUc7XdY877S2uVYbNliGc= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8= github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU= github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys= github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco= github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg= github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8 h1:HMgSn3c16SXca3M+n6fLK2hXJLd4mhKAsZZh7lQfYmQ= github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8/go.mod h1:qhZhwMDNWwZglKfwuWm0U9pCr/YKX1QAEwwJk9qfiTQ= github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU= github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdp/qrterminal/v3 v3.2.1 h1:6+yQjiiOsSuXT5n9/m60E54vdgFsw0zhADHhHLrFet4= github.com/mdp/qrterminal/v3 v3.2.1/go.mod h1:jOTmXvnBsMy5xqLniO0R++Jmjs2sTm9dFSuQ5kpz/SU= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0= github.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs= github.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM= github.com/navidys/tvxwidgets v0.13.0 h1:ccGODowWhHQH7zh43lwdeML+qpZS0cMHByjS3CPcRJY= github.com/navidys/tvxwidgets v0.13.0/go.mod h1:C+hTUXTFCOaYQkKlwqqn9K54RT0zrNqfqhI/RWwt+g4= github.com/nberlee/go-netstat v0.1.2 h1:wgPV1YOUo+kDFypqiKgfxMtnSs1Wb42c7ahI4qyEUJc= github.com/nberlee/go-netstat v0.1.2/go.mod h1:GvDCRLsUKMRN1wULkt7tpnDmjSIE6YGf5zeVq+mBO64= github.com/neticdk/go-stdlib v1.0.1 h1:3P6tJIICo8kvMMEFWSZCk+iRh+HoN8P/51WwMO+Ka2k= github.com/neticdk/go-stdlib v1.0.1/go.mod h1:KP9nLuDoanLbM8Wturn+hage2FtcrJaF1+1Znu+MKEw= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= 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= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.27.5 h1:ZeVgZMx2PDMdJm/+w5fE/OyG6ILo1Y3e+QX4zSR0zTE= github.com/onsi/ginkgo/v2 v2.27.5/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q= github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE= github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/packethost/packngo v0.31.0 h1:LLH90ardhULWbagBIc3I3nl2uU75io0a7AwY6hyi0S4= github.com/packethost/packngo v0.31.0/go.mod h1:Io6VJqzkiqmIEQbpOjeIw9v8q9PfcTEq8TEY/tMQsfw= github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= 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/petermattis/goid v0.0.0-20250813065127-a731cc31b4fe/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741 h1:KPpdlQLZcHfTMQRi6bFQ7ogNO0ltFT4PmtwTLW4W+14= github.com/petermattis/goid v0.0.0-20260113132338-7c7de50cc741/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pin/tftp/v3 v3.2.0 h1:q6K5G6T0TA7e3wDJsB/7VpD3iaWwVEJD/nEuh3q9Sk0= github.com/pin/tftp/v3 v3.2.0/go.mod h1:qc5ySXB5aOS1H6ULneqB4g5nshqV1CgeV/l/M6rEDms= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM= github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5JiKP7883xBzZAr1ydcxrKNSVNm7+3+JwjxZEsg= github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmorjan/kmod v1.1.1 h1:Vfw6bMaOg/sYSBCqJPT9TbqHHf5zK00GbaL5JQLO4r0= github.com/pmorjan/kmod v1.1.1/go.mod h1:jR4fVosEpQ6b5U0rpxaqoShTDPvCjLIP8vEESZyvnqQ= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rivo/tview v0.42.0 h1:b/ftp+RxtDsHSaynXTbJb+/n/BxDEi+W3UfF5jILK6c= github.com/rivo/tview v0.42.0/go.mod h1:cSfIYfhpSGCjp3r/ECJb+GKS7cGJnqV8vfjQPwoXyfY= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk= github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/safchain/ethtool v0.7.0 h1:rlJzfDetsVvT61uz8x1YIcFn12akMfuPulHtZjtb7Is= github.com/safchain/ethtool v0.7.0/go.mod h1:MenQKEjXdfkjD3mp2QdCk8B/hwvkrlOTm/FD4gTpFxQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sasha-s/go-deadlock v0.3.6 h1:TR7sfOnZ7x00tWPfD397Peodt57KzMDo+9Ae9rMiUmw= github.com/sasha-s/go-deadlock v0.3.6/go.mod h1:CUqNyyvMxTyjFqDT7MRg9mb4Dv/btmGTqSR+rky/UXo= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36/go.mod h1:LEsDu4BubxK7/cWhtlQWfuxwL4rf/2UEpxXz1o1EMtM= github.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14= github.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shanduur/rtnetlink/v2 v2.0.0-20260313131132-118a2ded4751 h1:rUMfGoS7VczaiZIGnG33ERqRnwgrcXiUoq+WgAFMSJo= github.com/shanduur/rtnetlink/v2 v2.0.0-20260313131132-118a2ded4751/go.mod h1:A/gqt1BEMJcvzGQJXQ3SnsDOQL7QRNhxTiC3eb++608= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/siderolabs/coredns v1.14.53 h1:rwWyicwWFcu+i8OV0WT1u2W46J/iIReqzm+pSvEPPWs= github.com/siderolabs/coredns v1.14.53/go.mod h1:KAGRTpTbhrScGMhiLsgnzo30OgiBBut1Y5XTuG1V+BQ= github.com/siderolabs/crypto v0.6.4 h1:uMoe/X/mABOv6yOgvKcjmjIMdv6U8JegBXlPKtyjn3g= github.com/siderolabs/crypto v0.6.4/go.mod h1:39B7Mdrd8qTfEYOjsWPQOk7gLTWrEI30isAW+YYj9nk= github.com/siderolabs/discovery-api v0.1.8 h1:Hq/Si0fFQICvdT+P/I81fRf9t5I+J6vaJNBvgehv8GE= github.com/siderolabs/discovery-api v0.1.8/go.mod h1:JN8aBpnsArIeLNLbqt3HIYHyFR14Qfwr4etAB2ZfygA= github.com/siderolabs/discovery-client v0.1.15 h1:XN2nm2U/RUtevZtKZnwYAOE+Z68VpW32Me1K19zXtHU= github.com/siderolabs/discovery-client v0.1.15/go.mod h1:iUpFYp40CNTnqshG7d2r9zjMruLEaT0sb49rZzVSXVA= github.com/siderolabs/ethtool v0.4.0-sidero h1:Ls/M4bFUjfcB1RDVviPZlL3kWcXaEVVSbKke+EZ2A9U= github.com/siderolabs/ethtool v0.4.0-sidero/go.mod h1:nOIR88fiFTdBfakYLEUAhxdy75Ih/fgnSlsSKAHRpfc= github.com/siderolabs/gen v0.8.0/go.mod h1:an3a2Y53O7kUjnnK8Bfu3gewtvnIOu5RTU6HalFtXQQ= github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4= github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ= github.com/siderolabs/go-api-signature v0.3.12 h1:i1X+kPh9fzo+lEjtEplZSbtq1p21vKv4FCWJcB/ozvk= github.com/siderolabs/go-api-signature v0.3.12/go.mod h1:dPLiXohup4qHX7KUgF/wwOE3lRU5uAr3ssEomNxiyxY= github.com/siderolabs/go-blockdevice/v2 v2.0.26 h1:t7faVJft7OrC/INPpODKg79O4qVpeKlkbs3amk/DIdQ= github.com/siderolabs/go-blockdevice/v2 v2.0.26/go.mod h1:a6KUjzyU8Joo7y9cW9BdmORCFJwVNweHYRpKiuDfMU8= github.com/siderolabs/go-circular v0.2.3 h1:GKkA1Tw79kEFGtWdl7WTxEUTbwtklITeiRT0V1McHrA= github.com/siderolabs/go-circular v0.2.3/go.mod h1:YBN/q9YpQphUYnBtBgPsngauSHj1TEZfgQZWZVjk1WE= github.com/siderolabs/go-cmd v0.2.0 h1:fZ0jbQzZg4bFLmJzNEDZM/RlebZsfHOo2k+PCJ6g7y4= github.com/siderolabs/go-cmd v0.2.0/go.mod h1:YA/UEDh8Av84RlI4LfQw7HN9+vgxUrQw/Byef1dgptY= github.com/siderolabs/go-copy v0.1.0 h1:OIWCtSg+rhOtnIZTpT31Gfpn17rv5kwJqQHG+QUEgC8= github.com/siderolabs/go-copy v0.1.0/go.mod h1:4bF2rZOZAR/ags/U4AVSpjFE5RPGdEeSkOq6yR9YOkU= github.com/siderolabs/go-debug v0.6.2 h1:zWWMTcrYDVyiNTotSxEVg++hj9mb2ctuTNVnOeCWtO8= github.com/siderolabs/go-debug v0.6.2/go.mod h1:tcHnBjzOfEC/Stfc+cpP8J9Y6y5Pp89XNBN0n3dsWD4= github.com/siderolabs/go-kmsg v0.1.5 h1:bdaultamVoM6f2ZmhwFL+LAh2A1c2sdCno5cEubv3rs= github.com/siderolabs/go-kmsg v0.1.5/go.mod h1:fryspKc1f6nMIOK5YbUPutz2v2rdBTBeLqW/ci9BDfk= github.com/siderolabs/go-kubeconfig v0.1.1 h1:tZlgpelj/OqrcHVUwISPN0NRgObcflpH9WtE41mtQZ0= github.com/siderolabs/go-kubeconfig v0.1.1/go.mod h1:QaGp4i9L95oDbcU7jDn30aw4gnREkb3O5otgxw8imOk= github.com/siderolabs/go-kubernetes v0.2.33 h1:I5J1EW6McjQeTzQCaZ7Vc0ZTbwypsCburRshTkb6HS0= github.com/siderolabs/go-kubernetes v0.2.33/go.mod h1:Ow/1iR+kJmMvKno9whL8WTRBoCPKPrHqxy1a/7SYacs= github.com/siderolabs/go-loadbalancer v0.5.0 h1:0v7E6GrxoONyqwcmHiA+J0vIDPWbkTmevHGCFb4tjdc= github.com/siderolabs/go-loadbalancer v0.5.0/go.mod h1:tRVouZ9i2R/TRbNUF9MqyBlV2wsjX0cxkYTjPXcI9P0= github.com/siderolabs/go-pcidb v0.3.3 h1:Cxng3j6gUrWCh1tHlt524A4cC+dyRU+3j1AWQM6ULPc= github.com/siderolabs/go-pcidb v0.3.3/go.mod h1:FHHkNSBdcdxba/UKFH8936pPFKAvjqqTsq182X0ulcY= github.com/siderolabs/go-pointer v1.0.1 h1:f7Yi4IK1jptS8yrT9GEbwhmGcVxvPQgBUG/weH3V3DM= github.com/siderolabs/go-pointer v1.0.1/go.mod h1:C8Q/3pNHT4RE9e4rYR9PHeS6KPMlStRBgYrJQJNy/vA= github.com/siderolabs/go-procfs v0.1.2 h1:bDs9hHyYGE2HO1frpmUsD60yg80VIEDrx31fkbi4C8M= github.com/siderolabs/go-procfs v0.1.2/go.mod h1:dBzQXobsM7+TWRRI3DS9X7vAuj8Nkfgu3Z/U9iY3ZTY= github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg= github.com/siderolabs/go-retry v0.3.3/go.mod h1:Ff/VGc7v7un4uQg3DybgrmOWHEmJ8BzZds/XNn/BqMI= github.com/siderolabs/go-smbios v0.3.3 h1:rM3UKHQ8in1mqNRkpV75Ls3Wnk6rAhQJVYKUsKkQS20= github.com/siderolabs/go-smbios v0.3.3/go.mod h1:kScnr0XSyzLfkRo/ChjITgI0rPRQnIi6PdgbxVCwA9U= github.com/siderolabs/go-tail v0.1.1 h1:3XeJgd97OHyFAIE7nQEMcRhOfnv7DvXbu0BRKbtT6u8= github.com/siderolabs/go-tail v0.1.1/go.mod h1:IihAL39acadXHfb5fEAOKK2DaDFIrG2+VD3b2H/ziZ0= github.com/siderolabs/go-talos-support v0.1.4 h1:vFYI7tcuyH4PoSkSOZtFMX8OKjvrkktzXWztbsCDh6Q= github.com/siderolabs/go-talos-support v0.1.4/go.mod h1:1DX20fTINVjMaW2zBmmqDBmQ4Be/6a6HwyqI1DR1ijM= github.com/siderolabs/grpc-proxy v0.5.1 h1:WTZYLMPTZPt43BzEJ02LT9kYA9qAfquWwCezc6NPPYE= github.com/siderolabs/grpc-proxy v0.5.1/go.mod h1:EQwE87LiWxhiIUPBeWmpjJb9DIWxWID8R6ARtdTC+8A= github.com/siderolabs/kms-client v0.2.0 h1:8RniCStUI75RTZO8qkhHOSVOnEU1AvvsKqJ7FqW/8NA= github.com/siderolabs/kms-client v0.2.0/go.mod h1:qq6dwcLPO0gaUyfkrhWi/37g/ZyZJzOHzvHrilLz48E= github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I= github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM= github.com/siderolabs/proto-codec v0.1.3 h1:tRzt2Rlc84Uv+Lx6vV2VmFpgHSV1fUNq/7nTXU892dM= github.com/siderolabs/proto-codec v0.1.3/go.mod h1:k6Sn8r+0473+xHL4t1rxD51lWm05rDqFpXecSUg3UE8= github.com/siderolabs/protoenc v0.2.4 h1:D3Fpn2nQSQOhl8ZlAxijZAf7K6F8CM1uZq0afIGsr8Q= github.com/siderolabs/protoenc v0.2.4/go.mod h1:i5XLHjfv5vyi7LhQrSEo19HCA+lYtDd7CWxsoWp9XE8= github.com/siderolabs/siderolink v0.3.15 h1:WSsgKQGJY/ObIKjTcYYGEaGfRMyox+r/Ft+9lIgJqOI= github.com/siderolabs/siderolink v0.3.15/go.mod h1:iWdlsHji90zotgDg4+a2zJL2ZMNJckQ8/VwqR39ThBM= github.com/siderolabs/tcpproxy v0.1.0 h1:IbkS9vRhjMOscc1US3M5P1RnsGKFgB6U5IzUk+4WkKA= github.com/siderolabs/tcpproxy v0.1.0/go.mod h1:onn6CPPj/w1UNqQ0U97oRPF0CqbrgEApYCw4P9IiCW8= github.com/siderolabs/wgctrl-go v0.0.0-20251029173431-c4fd5f6a4e72 h1:Boabco/vhoFVTUlPcLr4B27NnYUq1QMZVgMtPvyaDzk= github.com/siderolabs/wgctrl-go v0.0.0-20251029173431-c4fd5f6a4e72/go.mod h1:T97yPqesLiNrOYxkwmhMI0ZIlJDm+p0PMR8eRVeR5tQ= github.com/sigstore/cosign/v3 v3.0.5 h1:c1zPqjU+H4wmirgysC+AkWMg7a7fykyOYF/m+F1150I= github.com/sigstore/cosign/v3 v3.0.5/go.mod h1:ble1vMvJagCFyTIDkibCq6MIHiWDw00JNYl0f9rB4T4= github.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY= github.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= github.com/sigstore/rekor v1.5.0 h1:rL7SghHd5HLCtsCrxw0yQg+NczGvM75EjSPPWuGjaiQ= github.com/sigstore/rekor v1.5.0/go.mod h1:D7JoVCUkxwQOpPDNYeu+CE8zeBC18Y5uDo6tF8s2rcQ= github.com/sigstore/rekor-tiles/v2 v2.2.0 h1:QwJNwxT+k5A3id+Hrg+8vYcNsTaB0Sj51xjfW2rKyAs= github.com/sigstore/rekor-tiles/v2 v2.2.0/go.mod h1:/WNRYctHKdxcjgXydYwO5OclW72Zqh6fNHSyGE8zQOE= github.com/sigstore/sigstore v1.10.5-0.20260304232115-b56c8664d026 h1:s+iQW///NgIyhJenrynY/joaLTbo91hFL+vytjDaKck= github.com/sigstore/sigstore v1.10.5-0.20260304232115-b56c8664d026/go.mod h1:k/mcVVXw3I87dYG/iCVTSW2xTrW7vPzxxGic4KqsqXs= github.com/sigstore/sigstore-go v1.1.4 h1:wTTsgCHOfqiEzVyBYA6mDczGtBkN7cM8mPpjJj5QvMg= github.com/sigstore/sigstore-go v1.1.4/go.mod h1:2U/mQOT9cjjxrtIUeKDVhL+sHBKsnWddn8URlswdBsg= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.4 h1:VZ+L6SKVWbLPHznIF0tBuO7qKMFdJiJMVwFKu9DlY5o= github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.4/go.mod h1:Rstj47WpJym25il8j4jTL0BfikzP/9AhVD+DsBcYzZc= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.4 h1:G7yOv8bxk3zIEEZyVCixPxtePIAm+t3ZWSaKRPzVw+o= github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.4/go.mod h1:hxJelB/bRItMYOzi6qD9xEKjse2QZcikh4TbysfdDHc= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.4 h1:Qxt6dE4IwhJ6gIXmg2q4S/SeqEDSZ29nmfsv7Zb6LL4= github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.4/go.mod h1:hJVeNOwarqfyALjOwsf0OR8YA/A96NABucEaQumPr30= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.4 h1:KVavYMPfSf5NryOl6VrZ9nRG3fXOOJOPp7Czk/YCPkM= github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.4/go.mod h1:J7CA1AaBkyK8dYq6EdQANhj+8oEcsA7PrIp088qgPiY= github.com/sigstore/timestamp-authority/v2 v2.0.4 h1:65IBa4LUeFWDQu9hiTt5lBpi/F5jonJWZtH6VLn4InU= github.com/sigstore/timestamp-authority/v2 v2.0.4/go.mod h1:EXJLiMDBqRPlzC02hPiFSiYTCqSuUpU68a4vr0DFePM= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smira/containerd/v2 v2.2.3-0.20260311174942-e5fa687ba763 h1:l0WOZNJ5VG2EYm4Tnj2Pm0m1v5Tlw9W7TwhWAn/k/Sk= github.com/smira/containerd/v2 v2.2.3-0.20260311174942-e5fa687ba763/go.mod h1:5Jhevmv6/2J+Iu/A2xXAdUIdI5Ah/hfyO7okJ4AFIdY= github.com/smira/kobject v0.0.0-20240304111826-49c8d4613389 h1:f/5NRv5IGZxbjBhc5MnlbNmyuXBPxvekhBAUzyKWyLY= github.com/smira/kobject v0.0.0-20240304111826-49c8d4613389/go.mod h1:+SexPO1ZvdbbWUdUnyXEWv3+4NwHZjKhxOmQqHY4Pqc= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8= github.com/thejerf/suture/v4 v4.0.6/go.mod h1:gu9Y4dXNUWFrByqRt30Rm9/UZ0wzRSt9AJS6xu/ZGxU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= github.com/theupdateframework/go-tuf/v2 v2.4.1 h1:K6ewW064rKZCPkRo1W/CTbTtm/+IB4+coG1iNURAGCw= github.com/theupdateframework/go-tuf/v2 v2.4.1/go.mod h1:Nex2enPVYDFCklrnbTzl3OVwD7fgIAj0J5++z/rvCj8= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= github.com/tink-crypto/tink-go-hcvault/v2 v2.4.0 h1:j+S+WKBQ5ya26A5EM/uXoVe+a2IaPQN8KgBJZ22cJ+4= github.com/tink-crypto/tink-go-hcvault/v2 v2.4.0/go.mod h1:OCKJIujnTzDq7f+73NhVs99oA2c1TR6nsOpuasYM6Yo= github.com/tink-crypto/tink-go/v2 v2.6.0 h1:+KHNBHhWH33Vn+igZWcsgdEPUxKwBMEe0QC60t388v4= github.com/tink-crypto/tink-go/v2 v2.6.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c h1:5a2XDQ2LiAUV+/RjckMyq9sXudfrPSuCY4FuPC1NyAw= github.com/transparency-dev/formats v0.0.0-20251017110053-404c0d5b696c/go.mod h1:g85IafeFJZLxlzZCDRu4JLpfS7HKzR+Hw9qRh3bVzDI= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= github.com/u-root/u-root v0.16.0 h1:wY40O83MBVks97+Is0WlFlOPSwKQMIrWP9R1IsrExg8= github.com/u-root/u-root v0.16.0/go.mod h1:yL/XdSSW27PdGLgUh4MNRBy54mKM+TBLzpwiB4nwj90= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM= github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vultr/metadata v1.1.0 h1:RUjCnH5Mdlz7uuyfb1jOZNkU72zl/HwK76jLzVFdiOo= github.com/vultr/metadata v1.1.0/go.mod h1:4yocaI6h2EFJzwN0m1KnnC/vDCx2axIqnyxmtF/LWoQ= 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/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.6.8 h1:gqb1VN92TAI6G2FiBvWcqKtHiIjr4SU2GdXxTwyexbM= go.etcd.io/etcd/api/v3 v3.6.8/go.mod h1:qyQj1HZPUV3B5cbAL8scG62+fyz5dSxxu0w8pn28N6Q= go.etcd.io/etcd/client/pkg/v3 v3.6.8 h1:Qs/5C0LNFiqXxYf2GU8MVjYUEXJ6sZaYOz0zEqQgy50= go.etcd.io/etcd/client/pkg/v3 v3.6.8/go.mod h1:GsiTRUZE2318PggZkAo6sWb6l8JLVrnckTNfbG8PWtw= go.etcd.io/etcd/client/v3 v3.6.8 h1:B3G76t1UykqAOrbio7s/EPatixQDkQBevN8/mwiplrY= go.etcd.io/etcd/client/v3 v3.6.8/go.mod h1:MVG4BpSIuumPi+ELF7wYtySETmoTWBHVcDoHdVupwt8= go.etcd.io/etcd/etcdutl/v3 v3.6.8 h1:5YolVcLplhVwSR7IXemN7kBpx/L4qHAmyNc+iW+PL/k= go.etcd.io/etcd/etcdutl/v3 v3.6.8/go.mod h1:HGfpMG6Sjo9S6KWeXctiYcN8LjLbbUBdAjCYb8V977w= go.etcd.io/etcd/pkg/v3 v3.6.8 h1:Xe+LIL974spy8b4nEx3H0KMr1ofq3r0kh6FbU3aw4es= go.etcd.io/etcd/pkg/v3 v3.6.8/go.mod h1:TRibVNe+FqJIe1abOAA1PsuQ4wqO87ZaOoprg09Tn8c= go.etcd.io/etcd/server/v3 v3.6.8 h1:U2strdSEy1U8qcSzRIdkYpvOPtBy/9i/IfaaCI9flZ4= go.etcd.io/etcd/server/v3 v3.6.8/go.mod h1:88dCtwUnSirkUoJbflQxxWXqtBSZa6lSG0Kuej+dois= go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= 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.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= 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.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.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= 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.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.step.sm/crypto v0.76.0 h1:K23BSaeoiY7Y5dvvijTeYC9EduDBetNwQYMBwMhi1aA= go.step.sm/crypto v0.76.0/go.mod h1:PXYJdKkK8s+GHLwLguFaLxHNAFsFL3tL1vSBrYfey5k= 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.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= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= 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-20200420201142-3c4aac89819a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4= golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA= 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/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE= google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c h1:OyQPd6I3pN/9gDxz6L13kYGJgqkpdrAohJRBeXyxlgI= google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg= google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 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 v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/dnaeon/go-vcr.v4 v4.0.6 h1:PiJkrakkmzc5s7EfBnZOnyiLwi7o7A9fwPzN0X2uwe0= gopkg.in/dnaeon/go-vcr.v4 v4.0.6/go.mod h1:sbq5oMEcM4PXngbcNbHhzfCP9OdZodLhrbRYoyg09HY= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/typ.v4 v4.4.0 h1:O9vTueEmZd0iA9DF+g2wXeNCeloN2TOpxu6FXKl3AqM= gopkg.in/typ.v4 v4.4.0/go.mod h1:wolXe8DlewxRCjA7SOiT3zjrZ0eQJZcr8cmV6bQWJUM= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 h1:TbRPT0HtzFP3Cno1zZo7yPzEEnfu8EjLfl6IU9VfqkQ= gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259/go.mod h1:AVgIgHMwK63XvmAzWG9vLQ41YnVHN0du0tEC46fI7yY= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw= k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60= k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0= k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU= k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8= k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= k8s.io/apiserver v0.35.2 h1:rb52v0CZGEL0FkhjS+I6jHflAp7fZ4MIaKcEHX7wmDk= k8s.io/apiserver v0.35.2/go.mod h1:CROJUAu0tfjZLyYgSeBsBan2T7LUJGh0ucWwTCSSk7g= k8s.io/cli-runtime v0.35.2 h1:3DNctzpPNXavqyrm/FFiT60TLk4UjUxuUMYbKOE970E= k8s.io/cli-runtime v0.35.2/go.mod h1:G2Ieu0JidLm5m1z9b0OkFhnykvJ1w+vjbz1tR5OFKL0= k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o= k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g= k8s.io/component-base v0.35.2 h1:btgR+qNrpWuRSuvWSnQYsZy88yf5gVwemvz0yw79pGc= k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0= k8s.io/cri-api v0.35.2 h1:Lfg8KG0XFPph2KM+yWA+/mfv71v7UOkGt+uuqKMSWCU= k8s.io/cri-api v0.35.2/go.mod h1:Cnt29u/tYl1Se1cBRL30uSZ/oJ5TaIp4sZm1xDLvcMc= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/kube-scheduler v0.35.2 h1:VMH5AxwoFp4k30uaYSTchDr6MPmURmGpuwldS2R3Opw= k8s.io/kube-scheduler v0.35.2/go.mod h1:D0Fqh5wr/ECi22MVfn4n8aywz/Vh0pfD59VGbYkQfA4= k8s.io/kubectl v0.35.2 h1:aSmqhSOfsoG9NR5oR8OD5eMKpLN9x8oncxfqLHbJJII= k8s.io/kubectl v0.35.2/go.mod h1:+OJC779UsDJGxNPbHxCwvb4e4w9Eh62v/DNYU2TlsyM= k8s.io/kubelet v0.35.2 h1:qF9jOe1j6vT4bVQZ6nnTTA5uu5NCnyR10o9IkW8Z0JQ= k8s.io/kubelet v0.35.2/go.mod h1:2pyCVLDfm7ErNwWZw2mutCloAXX76gfOToIMCHCq/8s= k8s.io/pod-security-admission v0.35.2 h1:vzEfL/TpdwwIE25xQiamiRfmWD+FIcNXJYzoMI50AUY= k8s.io/pod-security-admission v0.35.2/go.mod h1:zrNF0GSYasCR8SHiAD67q2iUTHitVoFQRvTOy/UijyU= k8s.io/utils v0.0.0-20260108192941-914a6e750570 h1:JT4W8lsdrGENg9W+YwwdLJxklIuKWdRm+BC+xt33FOY= k8s.io/utils v0.0.0-20260108192941-914a6e750570/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= kernel.org/pub/linux/libs/security/libcap/cap v1.2.77 h1:iQtQTjFUOcTT19fI8sTCzYXsjeVs56et3D8AbKS2Uks= kernel.org/pub/linux/libs/security/libcap/cap v1.2.77/go.mod h1:oV+IO8kGh0B7TxErbydDe2+BRmi9g/W0CkpVV+QBTJU= kernel.org/pub/linux/libs/security/libcap/psx v1.2.77 h1:Z06sMOzc0GNCwp6efaVrIrz4ywGJ1v+DP0pjVkOfDuA= kernel.org/pub/linux/libs/security/libcap/psx v1.2.77/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY= rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs= sigs.k8s.io/controller-runtime v0.22.2 h1:cK2l8BGWsSWkXz09tcS4rJh95iOLney5eawcK5A33r4= sigs.k8s.io/controller-runtime v0.22.2/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/hydrophone v0.7.0 h1:BKEb8m6mcVL6kFEZ4jUCk5VD81bqm2XPtNxFT52ifxc= sigs.k8s.io/hydrophone v0.7.0/go.mod h1:w22N+PiIfL6XrIAxEqnZ7HnoER3F447WvdDlQhURiro= 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/knftables v0.0.18 h1:6Duvmu0s/HwGifKrtl6G3AyAPYlWiZqTgS8bkVMiyaE= sigs.k8s.io/knftables v0.0.18/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I= sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78= sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= 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/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= ================================================ FILE: go.work ================================================ go 1.26.1 use ( . ./hack/cloud-image-uploader ./pkg/machinery ./tools ./tools/docgen ./tools/gotagsrewrite ./tools/structprotogen ) ================================================ FILE: hack/boilerplate.txt ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. ================================================ FILE: hack/cleanup.sh ================================================ #!/bin/bash PREFIX="${1}" # Remove any archives as we do not need them since everything is dynamically linked. find ${PREFIX} -type f -name \*.a -delete find ${PREFIX} -type f -name \*.la -delete # Remove static binaries. find ${PREFIX} -type f \( -name \*.static -o -name \*.o \) -delete # Strip debug symbols from all libraries and binaries. find ${PREFIX}/{lib,usr/lib} -type f \( -name \*.so* -a ! -name \*dbg \) -exec strip --strip-unneeded {} ';' || true find ${PREFIX}/usr/bin -type f -exec strip --strip-all {} ';' || true # Remove header files, man files, and any other non-runtime dependencies. rm -rf ${PREFIX}/usr/lib/pkgconfig/ \ ${PREFIX}/{include,usr/include}/* \ ${PREFIX}/share/* \ ${PREFIX}/usr/lib/cmake \ ${PREFIX}/usr/lib/gconv/ \ ${PREFIX}/usr/libexec/getconf \ ${PREFIX}/var/db # Drop broken symlinks. find ${PREFIX} -xtype l -print -delete ================================================ FILE: hack/cloud-image-uploader/README.md ================================================ # cloud-image-uploader ## vmimport role Role should be pre-created before running this command. aws iam create-role --role-name vmimport --assume-role-policy-document file://trust-policy.json aws iam put-role-policy --role-name vmimport --policy-name vmimport --policy-document file://role-policy.json ## Google Cloud Pre-requisites - `GOOGLE_PROJECT_ID` - Google Cloud Project ID - `GOOGLE_CREDENTIALS_JSON` - Google Cloud Service Account JSON ================================================ FILE: hack/cloud-image-uploader/aws.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "context" "fmt" "log" "os" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/s3" s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" "github.com/google/uuid" "github.com/klauspost/compress/zstd" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" ) var denyInsecurePolicyTemplate = `{ "Id": "ExamplePolicy", "Version": "2012-10-17", "Statement": [ { "Sid": "AllowSSLRequestsOnly", "Action": "s3:*", "Effect": "Deny", "Resource": [ "arn:aws:s3:::%s", "arn:aws:s3:::%s/*" ], "Condition": { "Bool": { "aws:SecureTransport": "false" } }, "Principal": "*" } ] }` // GetAWSDefaultRegions returns a list of regions which are enabled for this account. func GetAWSDefaultRegions(ctx context.Context) ([]string, error) { cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion("us-east-1")) if err != nil { return nil, fmt.Errorf("failed loading AWS config: %w", err) } svc := ec2.NewFromConfig(cfg) resp, err := svc.DescribeRegions(ctx, &ec2.DescribeRegionsInput{ Filters: []types.Filter{ { Name: new("opt-in-status"), Values: []string{"opt-in-not-required", "opted-in"}, }, }, }) if err != nil { return nil, fmt.Errorf("failed describing regions: %w", err) } return xslices.Map(resp.Regions, func(r types.Region) string { return pointer.SafeDeref(r.RegionName) }), nil } // AWSUploader registers AMI in the AWS. type AWSUploader struct { Options Options cfg aws.Config ec2svcs map[string]*ec2.Client } var awsArchitectures = map[string]types.ArchitectureValues{ "amd64": types.ArchitectureValuesX8664, "arm64": types.ArchitectureValuesArm64, } // Upload image and register with AWS. func (au *AWSUploader) Upload(ctx context.Context) error { var err error au.cfg, err = config.LoadDefaultConfig(ctx) if err != nil { return fmt.Errorf("failed loading AWS config: %w", err) } au.ec2svcs = make(map[string]*ec2.Client) for _, region := range au.Options.AWSRegions { au.ec2svcs[region] = ec2.NewFromConfig(au.cfg, func(o *ec2.Options) { o.Region = region }) } return au.RegisterAMIs(ctx) } // RegisterAMIs in every region. func (au *AWSUploader) RegisterAMIs(ctx context.Context) error { var g *errgroup.Group g, ctx = errgroup.WithContext(ctx) for region, svc := range au.ec2svcs { g.Go(func() error { err := au.registerAMI(ctx, region, svc) if err != nil { return fmt.Errorf("error registering AMI in %s: %w", region, err) } return nil }) } return g.Wait() } func cleanupBucket(s3Svc *s3.Client, bucketName string) { // create a detached context, as context might be canceled ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() mpuPaginator := s3.NewListMultipartUploadsPaginator(s3Svc, &s3.ListMultipartUploadsInput{ Bucket: new(bucketName), }) for mpuPaginator.HasMorePages() { page, err := mpuPaginator.NextPage(ctx) if err != nil { log.Printf("failed listing multipart uploads: %s", err) break } for _, upload := range page.Uploads { _, err = s3Svc.AbortMultipartUpload(ctx, &s3.AbortMultipartUploadInput{ Bucket: new(bucketName), Key: upload.Key, UploadId: upload.UploadId, }) if err != nil { log.Printf("failed aborting multipart upload: %s", err) } } } objectsPaginator := s3.NewListObjectsV2Paginator(s3Svc, &s3.ListObjectsV2Input{ Bucket: new(bucketName), }) for objectsPaginator.HasMorePages() { page, err := objectsPaginator.NextPage(ctx) if err != nil { log.Printf("failed listing objects: %s", err) break } if len(page.Contents) == 0 { break } _, err = s3Svc.DeleteObjects(ctx, &s3.DeleteObjectsInput{ Bucket: new(bucketName), Delete: &s3types.Delete{ Objects: xslices.Map(page.Contents, func(obj s3types.Object) s3types.ObjectIdentifier { return s3types.ObjectIdentifier{ Key: obj.Key, } }), }, }) if err != nil { log.Printf("failed deleting objects: %s", err) } } _, err := s3Svc.DeleteBucket(ctx, &s3.DeleteBucketInput{ Bucket: aws.String(bucketName), }) if err != nil { log.Printf("failed deleting bucket: %s", err) } log.Printf("aws: deleted bucket %q", bucketName) } func (au *AWSUploader) registerAMI(ctx context.Context, region string, svc *ec2.Client) error { s3Svc := s3.NewFromConfig(au.cfg, func(o *s3.Options) { o.Region = region }) bucketName := fmt.Sprintf("talos-image-upload-%s", uuid.New()) var createBucketConfiguration *s3types.CreateBucketConfiguration if region != "us-east-1" { createBucketConfiguration = &s3types.CreateBucketConfiguration{ LocationConstraint: s3types.BucketLocationConstraint(region), } } _, err := s3Svc.CreateBucket(ctx, &s3.CreateBucketInput{ Bucket: new(bucketName), CreateBucketConfiguration: createBucketConfiguration, }) if err != nil { return fmt.Errorf("failed creating S3 bucket: %w", err) } if err = s3.NewBucketExistsWaiter(s3Svc).Wait(ctx, &s3.HeadBucketInput{ Bucket: new(bucketName), }, time.Minute); err != nil { return fmt.Errorf("failed waiting for S3 bucket: %w", err) } log.Printf("aws: created bucket %q for %s", bucketName, region) defer func() { cleanupBucket(s3Svc, bucketName) }() _, err = s3Svc.PutBucketPolicy(ctx, &s3.PutBucketPolicyInput{ Bucket: new(bucketName), Policy: new(fmt.Sprintf(denyInsecurePolicyTemplate, bucketName, bucketName)), }) if err != nil { return fmt.Errorf("failed applying S3 bucket policy: %w", err) } log.Printf("aws: applied policy to bucket %q", bucketName) uploader := manager.NewUploader(s3Svc) //nolint:staticcheck var g errgroup.Group for _, arch := range au.Options.Architectures { g.Go(func() error { err = au.registerAMIArch(ctx, region, svc, arch, bucketName, uploader) if err != nil { log.Printf("WARNING: aws: ignoring failure to upload AMI into %s/%s: %s", region, arch, err) } return nil }) } return g.Wait() } //nolint:gocyclo func (au *AWSUploader) tagSnapshot(ctx context.Context, svc *ec2.Client, snapshotID, imageName string) { if snapshotID == "" { return } _, tagErr := svc.CreateTags(ctx, &ec2.CreateTagsInput{ Resources: []string{snapshotID}, Tags: []types.Tag{{ Key: new("Name"), Value: new(imageName), }}, }) if tagErr != nil { log.Printf("WARNING: failed to tag snapshot %s: %v", snapshotID, tagErr) } } //nolint:gocyclo func (au *AWSUploader) registerAMIArch(ctx context.Context, region string, svc *ec2.Client, arch, bucketName string, uploader *manager.Uploader) error { //nolint:staticcheck err := retry.Constant(30*time.Minute, retry.WithUnits(time.Second), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error { source, err := os.Open(au.Options.AWSImage(arch)) if err != nil { return err } defer source.Close() //nolint:errcheck image, err := zstd.NewReader(source) if err != nil { return err } defer image.Close() _, err = uploader.Upload(ctx, &s3.PutObjectInput{ //nolint:staticcheck Bucket: new(bucketName), Key: new(fmt.Sprintf("disk-%s.raw", arch)), Body: image, }) return retry.ExpectedError(err) }) if err != nil { return fmt.Errorf("failed to upload image to the bucket: %w", err) } log.Printf("aws: import into %s/%s, image uploaded to S3", region, arch) resp, err := svc.ImportSnapshot(ctx, &ec2.ImportSnapshotInput{ Description: new(fmt.Sprintf("Talos Image %s %s %s", au.Options.Tag, arch, region)), DiskContainer: &types.SnapshotDiskContainer{ Description: new(fmt.Sprintf("Talos Image %s %s %s", au.Options.Tag, arch, region)), Format: new("raw"), UserBucket: &types.UserBucket{ S3Bucket: new(bucketName), S3Key: new(fmt.Sprintf("disk-%s.raw", arch)), }, }, }) if err != nil { return fmt.Errorf("failed to import snapshot: %w", err) } taskID := *resp.ImportTaskId var snapshotID string log.Printf("aws: import into %s/%s, task ID %q", region, arch, taskID) progress := "0" err = retry.Constant(30*time.Minute, retry.WithUnits(30*time.Second)).Retry(func() error { var status *ec2.DescribeImportSnapshotTasksOutput status, err = svc.DescribeImportSnapshotTasks(ctx, &ec2.DescribeImportSnapshotTasksInput{ ImportTaskIds: []string{taskID}, }) if err != nil { return err } for _, task := range status.ImportSnapshotTasks { if pointer.SafeDeref(task.ImportTaskId) == taskID { if task.SnapshotTaskDetail == nil { continue } if pointer.SafeDeref(task.SnapshotTaskDetail.Status) == "completed" { snapshotID = pointer.SafeDeref(task.SnapshotTaskDetail.SnapshotId) return nil } if pointer.SafeDeref(task.SnapshotTaskDetail.Progress) != progress { progress = pointer.SafeDeref(task.SnapshotTaskDetail.Progress) log.Printf("aws: import into %s/%s, import snapshot %s%%", region, arch, progress) } return retry.ExpectedErrorf("task status is %s", *task.SnapshotTaskDetail.Status) } } return retry.ExpectedErrorf("task status not found") }) if err != nil { return fmt.Errorf("failed to wait for import task: %w", err) } log.Printf("aws: import into %s/%s, snapshot ID %q", region, arch, snapshotID) imageName := fmt.Sprintf("talos-%s-%s-%s", au.Options.Tag, region, arch) if au.Options.NamePrefix != "" { imageName = fmt.Sprintf("%s-%s-%s-%s", au.Options.NamePrefix, au.Options.Tag, region, arch) } au.tagSnapshot(ctx, svc, snapshotID, imageName) imageResp, err := svc.DescribeImages(ctx, &ec2.DescribeImagesInput{ Filters: []types.Filter{ { Name: aws.String("name"), Values: []string{imageName}, }, }, }) if err != nil { return fmt.Errorf("failed to describe images: %w", err) } for _, image := range imageResp.Images { _, err = svc.DeregisterImage(ctx, &ec2.DeregisterImageInput{ ImageId: image.ImageId, }) if err != nil { return fmt.Errorf("failed to deregister image: %w", err) } log.Printf("aws: import into %s/%s, deregistered image ID %q", region, arch, *image.ImageId) } registerReq := &ec2.RegisterImageInput{ Name: aws.String(imageName), BlockDeviceMappings: []types.BlockDeviceMapping{ { DeviceName: new("/dev/xvda"), VirtualName: new("talos"), Ebs: &types.EbsBlockDevice{ DeleteOnTermination: new(true), SnapshotId: new(snapshotID), VolumeSize: new(int32(20)), VolumeType: types.VolumeTypeGp2, }, }, }, RootDeviceName: new("/dev/xvda"), VirtualizationType: new("hvm"), EnaSupport: new(true), Description: new(fmt.Sprintf("Talos AMI %s %s %s", au.Options.Tag, arch, region)), Architecture: awsArchitectures[arch], ImdsSupport: types.ImdsSupportValuesV20, } if !au.Options.AWSForceBIOS { registerReq.BootMode = types.BootModeValuesUefiPreferred } registerResp, err := svc.RegisterImage(ctx, registerReq) if err != nil { return fmt.Errorf("failed to register image: %w", err) } imageID := *registerResp.ImageId log.Printf("aws: import into %s/%s, registered image ID %q", region, arch, imageID) _, err = svc.ModifyImageAttribute(ctx, &ec2.ModifyImageAttributeInput{ ImageId: aws.String(imageID), LaunchPermission: &types.LaunchPermissionModifications{ Add: []types.LaunchPermission{ { Group: types.PermissionGroupAll, }, }, }, Attribute: aws.String("launchPermission"), }) if err != nil { return fmt.Errorf("failed to modify image attribute: %w", err) } pushResult(CloudImage{ Cloud: "aws", Tag: au.Options.Tag, Region: region, Arch: arch, Type: "hvm", ID: imageID, }) return nil } ================================================ FILE: hack/cloud-image-uploader/factory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "context" "fmt" "io" "log" "net/http" "net/url" "os" "path/filepath" "golang.org/x/sync/errgroup" ) var extensions = map[string]string{ "aws": ".raw.zst", "gcp": ".raw.tar.gz", } // FactoryDownloader is helper for downloading images from Image Factory. type FactoryDownloader struct { Target string Options Options } func (f *FactoryDownloader) Download(ctx context.Context) error { g, ctx := errgroup.WithContext(ctx) for _, arch := range f.Options.Architectures { g.Go(func() error { artifact := fmt.Sprintf("%s-%s%s", f.Target, arch, extensions[f.Target]) r, err := f.getArtifact(ctx, artifact) if err != nil { return err } defer r.Close() //nolint:errcheck return f.saveArtifact(artifact, r) }) } return g.Wait() } func (f *FactoryDownloader) getArtifact(ctx context.Context, name string) (io.ReadCloser, error) { url, err := url.JoinPath( f.Options.FactoryHost, "image", f.Options.SchematicFor(f.Target), f.Options.Tag, name, ) if err != nil { return nil, fmt.Errorf("failed to construct URL: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } log.Printf("requesting artifact: %s", url) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("failed to download image from %q: %w", url, err) } if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code %d when fetching %q", resp.StatusCode, url) } return resp.Body, nil } func (f *FactoryDownloader) saveArtifact(name string, r io.Reader) error { artifact := filepath.Join(f.Options.ArtifactsPath, name) err := os.MkdirAll(f.Options.ArtifactsPath, 0o755) if err != nil { return fmt.Errorf("failed to create artifacts directory %q: %w", f.Options.ArtifactsPath, err) } of, err := os.OpenFile(artifact, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) if err != nil { return fmt.Errorf("failed to create file %q: %w", artifact, err) } defer of.Close() //nolint:errcheck log.Printf("saving artifact: %s", artifact) _, err = io.Copy(of, r) if err != nil { return fmt.Errorf("failed to write image to file %q: %w", artifact, err) } return nil } ================================================ FILE: hack/cloud-image-uploader/gcp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "context" "errors" "fmt" "io" "log" "net/http" "os" "path/filepath" "strings" "time" "cloud.google.com/go/storage" "github.com/google/uuid" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" "google.golang.org/api/compute/v1" "google.golang.org/api/googleapi" "google.golang.org/api/iterator" "google.golang.org/api/option" ) // GCPUploder registers the image in GCP. type GCPUploder struct { Options Options storageClient *storage.Client computeService *compute.Service projectID string imagePath string } // NewGCPUploder creates a new GCPUploder. func NewGCPUploder(options Options) (*GCPUploder, error) { projectID := os.Getenv("GOOGLE_PROJECT_ID") credentials := os.Getenv("GOOGLE_CREDENTIALS") if projectID == "" { return nil, fmt.Errorf("gcp: GOOGLE_PROJECT_ID is not set") } if credentials == "" { return nil, fmt.Errorf("gcp: GOOGLE_CREDENTIALS is not set") } gcpUploader := &GCPUploder{ Options: options, } gcpUploader.projectID = projectID var err error gcpUploader.storageClient, err = storage.NewClient(context.Background(), option.WithCredentialsJSON([]byte(credentials))) //nolint:staticcheck if err != nil { return nil, fmt.Errorf("gcp: failed to create google storage client: %w", err) } gcpUploader.computeService, err = compute.NewService(context.Background(), option.WithCredentialsJSON([]byte(credentials))) //nolint:staticcheck if err != nil { return nil, fmt.Errorf("gcp: failed to create google compute service: %w", err) } return gcpUploader, nil } // Upload uploads the image to GCP. func (u *GCPUploder) Upload(ctx context.Context) error { bucketName := fmt.Sprintf("talos-image-upload-%s", uuid.New()) bucketHandle := u.storageClient.Bucket(bucketName) if err := bucketHandle.Create(ctx, u.projectID, &storage.BucketAttrs{ PublicAccessPrevention: storage.PublicAccessPreventionEnforced, }); err != nil { return fmt.Errorf("gcp: failed to create bucket %s: %w", bucketName, err) } log.Println("gcp: created bucket", bucketName) defer func() { objects := bucketHandle.Objects(ctx, nil) for { objAttr, err := objects.Next() if errors.Is(err, iterator.Done) { break } if err != nil { log.Printf("gcp: failed to list objects: %v", err) } if err := bucketHandle.Object(objAttr.Name).Delete(ctx); err != nil { log.Printf("gcp: failed to delete object %s: %v", objAttr.Name, err) } } if err := bucketHandle.Delete(ctx); err != nil { log.Printf("gcp: failed to delete bucket %s: %v", bucketName, err) } }() var g errgroup.Group for _, arch := range u.Options.Architectures { g.Go(func() error { return u.uploadImage(ctx, arch, bucketName) }) } if err := g.Wait(); err != nil { return fmt.Errorf("gcp: failed to upload images: %w", err) } return nil } func (u *GCPUploder) uploadImage(ctx context.Context, arch, bucketName string) error { objectPath := u.Options.GCPImage(arch) objectName := filepath.Base(objectPath) objectReader, err := os.Open(objectPath) if err != nil { return fmt.Errorf("gcp: failed to open object data file %s: %w", objectPath, err) } objectHandle := u.storageClient.Bucket(bucketName).Object(objectName) objectWriter := objectHandle.NewWriter(ctx) defer objectWriter.Close() //nolint:errcheck if _, err := io.Copy(objectWriter, objectReader); err != nil { return fmt.Errorf("gcp: failed to write object data: %w", err) } if err := objectWriter.Close(); err != nil { return fmt.Errorf("gcp: failed to close object writer: %w", err) } u.imagePath = fmt.Sprintf("https://storage.googleapis.com/%s/%s", bucketName, objectName) log.Println("gcp: uploaded image", u.imagePath) return u.registerImage(arch) } //nolint:gocyclo func (u *GCPUploder) registerImage(arch string) error { imageName := fmt.Sprintf("talos-%s-%s", strings.ReplaceAll(u.Options.Tag, ".", "-"), arch) if u.Options.NamePrefix != "" { imageName = fmt.Sprintf("%s-talos-%s-%s", u.Options.NamePrefix, strings.ReplaceAll(u.Options.Tag, ".", "-"), arch) } exists, err := u.checkImageExists(imageName) if err != nil { return err } if exists { log.Printf("gcp: image %s already exists, deleting", imageName) if deleteErr := u.deleteImage(imageName); deleteErr != nil { return deleteErr } } operationID, link, err := u.insertImage(imageName, arch) if err != nil { return err } if err := retry.Constant(15*time.Minute, retry.WithUnits(30*time.Second)).Retry(func() error { op, err := u.computeService.GlobalOperations.Get(u.projectID, operationID).Do() if err != nil { return fmt.Errorf("gcp: failed to get operation: %w", err) } if op.HTTPStatusCode != http.StatusOK { return fmt.Errorf("gcp: operation failed with http error message: %s", op.HttpErrorMessage) } if op.Error != nil { return fmt.Errorf("gcp: operation faild with error message: %s", op.Error.Errors[0].Message) } if op.Status == "DONE" { return nil } log.Printf("gcp: image creation progress: %d", op.Progress) return retry.ExpectedError(fmt.Errorf("gcp: image status is %s", op.Status)) }); err != nil { return fmt.Errorf("gcp: image creation is taking longer than expected: %w", err) } pushResult(CloudImage{ Cloud: "gcp", Tag: u.Options.Tag, Region: "us", Arch: arch, Type: "compute#image", ID: link, }) return nil } func (u *GCPUploder) checkImageExists(imageName string) (bool, error) { _, err := u.computeService.Images.Get(u.projectID, imageName).Do() if err != nil { var googleErr *googleapi.Error if errors.As(err, &googleErr) { if googleErr.Code == http.StatusNotFound { return false, nil } } return false, fmt.Errorf("gcp: failed to get image %s: %w", imageName, err) } return true, nil } func (u *GCPUploder) insertImage(imageName, arch string) (operationID, imageLink string, err error) { var archImage string switch arch { case "amd64": archImage = "x86_64" case "arm64": archImage = "ARM64" default: return "", "", fmt.Errorf("gcp: unknown architecture %s", arch) } op, err := u.computeService.Images.Insert(u.projectID, &compute.Image{ Architecture: archImage, Description: fmt.Sprintf("Talos %s %s", u.Options.Tag, arch), GuestOsFeatures: []*compute.GuestOsFeature{ { Type: "VIRTIO_SCSI_MULTIQUEUE", }, { Type: "UEFI_COMPATIBLE", }, }, Name: imageName, RawDisk: &compute.ImageRawDisk{ Source: u.imagePath, }, ShieldedInstanceInitialState: &compute.InitialStateConfig{}, }).Do() if err != nil { return "", "", fmt.Errorf("gcp: failed to insert image: %w", err) } if op.HTTPStatusCode != http.StatusOK { return "", "", fmt.Errorf("gcp: insert image failed with http error message: %s", op.HttpErrorMessage) } if op.Error != nil { return "", "", fmt.Errorf("gcp: insert image failed with error message: %s", op.Error.Errors[0].Message) } log.Printf("gcp: image %s is being created with operation %s", imageName, op.Name) return op.Name, op.TargetLink, nil } func (u *GCPUploder) deleteImage(imageName string) error { if _, err := u.computeService.Images.Delete(u.projectID, imageName).Do(); err != nil { return fmt.Errorf("gcp: failed to delete image %s: %w", imageName, err) } if err := retry.Constant(5*time.Minute, retry.WithUnits(30*time.Second)).Retry(func() error { _, err := u.computeService.Images.Get(u.projectID, imageName).Do() if err != nil { var googleErr *googleapi.Error if errors.As(err, &googleErr) { if googleErr.Code == http.StatusNotFound { return nil } } return err } return retry.ExpectedError(fmt.Errorf("gcp: image %s still exists", imageName)) }); err != nil { return fmt.Errorf("gcp: failed to delete image %s: %w", imageName, err) } return nil } ================================================ FILE: hack/cloud-image-uploader/go.mod ================================================ module github.com/siderolabs/cloud-image-uploader go 1.26.0 require ( cloud.google.com/go/storage v1.61.3 github.com/aws/aws-sdk-go-v2 v1.41.4 github.com/aws/aws-sdk-go-v2/config v1.32.12 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.8 github.com/aws/aws-sdk-go-v2/service/ec2 v1.294.1 github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1 github.com/google/uuid v1.6.0 github.com/klauspost/compress v1.18.4 github.com/siderolabs/gen v0.8.6 github.com/siderolabs/go-pointer v1.0.1 github.com/siderolabs/go-retry v0.3.3 github.com/spf13/pflag v1.0.10 golang.org/x/sync v0.20.0 google.golang.org/api v0.271.0 ) require ( cel.dev/expr v0.25.1 // indirect cloud.google.com/go v0.123.0 // indirect cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect cloud.google.com/go/monitoring v1.24.3 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect github.com/aws/smithy-go v1.24.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect github.com/googleapis/gax-go/v2 v2.17.0 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/otel 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/sdk/metric v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.15.0 // indirect google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect ) ================================================ FILE: hack/cloud-image-uploader/go.sum ================================================ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY= cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw= cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KMOg= cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk= cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 h1:3kGOqnh1pPeddVa/E37XNTaWJ8W6vrbYV9lJEkCnhuY= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.8 h1:nuc44j+otOY0d1e+CWwB6zul57d2YEGlgCyiq3SL0lI= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.22.8/go.mod h1:qSFgGCN8fjdhvlLhTPZdWRWXbwfeZZWF2FEaIplYPhE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21 h1:SwGMTMLIlvDNyhMteQ6r8IJSBPlRdXX5d4idhIGbkXA= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.21/go.mod h1:UUxgWxofmOdAMuqEsSppbDtGKLfR04HGsD0HXzvhI1k= github.com/aws/aws-sdk-go-v2/service/ec2 v1.294.1 h1:c2BbWVkQ0hVqls6SruYCRxfN5W46qvL+hIg7VLhXpg8= github.com/aws/aws-sdk-go-v2/service/ec2 v1.294.1/go.mod h1:T6ndRfdhnXLIY5oKBHjYZDVj706los2zGdpThppquvA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12 h1:qtJZ70afD3ISKWnoX3xB0J2otEqu3LqicRcDBqsj0hQ= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.12/go.mod h1:v2pNpJbRNl4vEUWEh5ytQok0zACAKfdmKS51Hotc3pQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20 h1:siU1A6xjUZ2N8zjTHSXFhB9L/2OY8Dqs0xXiLjF30jA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.20/go.mod h1:4TLZCmVJDM3FOu5P5TJP0zOlu9zWgDWU7aUxWbr+rcw= github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1 h1:csi9NLpFZXb9fxY7rS1xVzgPRGMt7MSNWeQ6eo247kE= github.com/aws/aws-sdk-go-v2/service/s3 v1.97.1/go.mod h1:qXVal5H0ChqXP63t6jze5LmFalc7+ZE7wOdLtZ0LCP0= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow= github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= 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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4= github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ= github.com/siderolabs/go-pointer v1.0.1 h1:f7Yi4IK1jptS8yrT9GEbwhmGcVxvPQgBUG/weH3V3DM= github.com/siderolabs/go-pointer v1.0.1/go.mod h1:C8Q/3pNHT4RE9e4rYR9PHeS6KPMlStRBgYrJQJNy/vA= github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg= github.com/siderolabs/go-retry v0.3.3/go.mod h1:Ff/VGc7v7un4uQg3DybgrmOWHEmJ8BzZds/XNn/BqMI= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= 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/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk= 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= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= 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/api v0.271.0 h1:cIPN4qcUc61jlh7oXu6pwOQqbJW2GqYh5PS6rB2C/JY= google.golang.org/api v0.271.0/go.mod h1:CGT29bhwkbF+i11qkRUJb2KMKqcJ1hdFceEIRd9u64Q= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM= google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM= google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0= google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= google.golang.org/grpc v1.79.2/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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: hack/cloud-image-uploader/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package main implements Talos cloud image uploader. package main import ( "context" cryptorand "crypto/rand" "encoding/json" "fmt" "io" "log" "os" "os/signal" "path/filepath" "sync" "syscall" "github.com/spf13/pflag" "golang.org/x/sync/errgroup" ) // Result of the upload process. type Result []CloudImage // CloudImage is the record official cloud image. type CloudImage struct { Cloud string `json:"cloud"` Tag string `json:"version"` Region string `json:"region"` Arch string `json:"arch"` Type string `json:"type"` ID string `json:"id"` } var ( result Result resultMu sync.Mutex ) func pushResult(image CloudImage) { resultMu.Lock() defer resultMu.Unlock() result = append(result, image) } func main() { if err := run(); err != nil { log.Fatalf("%s", err) } } //nolint:gocyclo func run() error { var err error pflag.StringSliceVar(&DefaultOptions.TargetClouds, "target-clouds", DefaultOptions.TargetClouds, "cloud targets to upload to") pflag.StringSliceVar(&DefaultOptions.Architectures, "architectures", DefaultOptions.Architectures, "list of architectures to process") pflag.StringVar(&DefaultOptions.ArtifactsPath, "artifacts-path", DefaultOptions.ArtifactsPath, "artifacts path") pflag.StringVar(&DefaultOptions.Tag, "tag", DefaultOptions.Tag, "tag (version) of the uploaded image") pflag.StringVar(&DefaultOptions.NamePrefix, "name-prefix", DefaultOptions.NamePrefix, "prefix for the name of the uploaded image") pflag.BoolVar(&DefaultOptions.UseFactory, "use-factory", DefaultOptions.UseFactory, "whether to use factory to fetch images") pflag.StringVar(&DefaultOptions.FactoryHost, "factory-host", DefaultOptions.FactoryHost, "factory host to fetch images from") pflag.StringArrayVar(&DefaultOptions.FactorySchematics, "factory-schematics", DefaultOptions.FactorySchematics, "list of schematics to fetch from factory") pflag.StringSliceVar(&DefaultOptions.AWSRegions, "aws-regions", DefaultOptions.AWSRegions, "list of AWS regions to upload to") pflag.BoolVar(&DefaultOptions.AWSForceBIOS, "aws-force-bios", DefaultOptions.AWSForceBIOS, "force BIOS boot mode for AWS images") pflag.Parse() seed := make([]byte, 8) if _, err = cryptorand.Read(seed); err != nil { log.Fatalf("error seeding rand: %s", err) } ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer cancel() var g *errgroup.Group g, ctx = errgroup.WithContext(ctx) for _, target := range DefaultOptions.TargetClouds { switch target { case "aws": g.Go(func() error { if DefaultOptions.UseFactory { downloader := FactoryDownloader{ Target: target, Options: DefaultOptions, } if err := downloader.Download(ctx); err != nil { return fmt.Errorf("failed to download image: %w", err) } } if len(DefaultOptions.AWSRegions) == 0 { DefaultOptions.AWSRegions, err = GetAWSDefaultRegions(ctx) if err != nil { log.Printf("failed to get a list of enabled AWS regions: %s, ignored", err) } } aws := AWSUploader{ Options: DefaultOptions, } return aws.Upload(ctx) }) case "gcp": g.Go(func() error { if DefaultOptions.UseFactory { downloader := FactoryDownloader{ Target: target, Options: DefaultOptions, } if err := downloader.Download(ctx); err != nil { return fmt.Errorf("failed to download image: %w", err) } } gcp, err := NewGCPUploder(DefaultOptions) if err != nil { return fmt.Errorf("failed to create GCP uploader: %w", err) } return gcp.Upload(ctx) }) default: return fmt.Errorf("unknown target: %s", target) } } if err = g.Wait(); err != nil { return fmt.Errorf("failed: %w", err) } f, err := os.Create(filepath.Join(DefaultOptions.ArtifactsPath, "cloud-images.json")) if err != nil { return fmt.Errorf("failed: %w", err) } defer f.Close() //nolint:errcheck e := json.NewEncoder(io.MultiWriter(os.Stdout, f)) e.SetIndent("", " ") return e.Encode(&result) } ================================================ FILE: hack/cloud-image-uploader/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "fmt" "path/filepath" "strings" ) // Options for the cli. type Options struct { Tag string ArtifactsPath string NamePrefix string Architectures []string TargetClouds []string // ImageFactory options. UseFactory bool FactoryHost string FactorySchematics []string // AWS options. AWSRegions []string AWSForceBIOS bool } // DefaultOptions used throughout the cli. var DefaultOptions = Options{ ArtifactsPath: "_out/", Architectures: []string{"amd64", "arm64"}, TargetClouds: []string{"aws"}, FactoryHost: "https://factory.talos.dev", FactorySchematics: []string{ "aws:10e276a06c1f86b182757a962258ac00655d3425e5957f617bdc82f06894e39b", }, } // AWSImage returns path to AWS pre-built image. func (o *Options) AWSImage(architecture string) string { return filepath.Join(o.ArtifactsPath, fmt.Sprintf("aws-%s.raw.zst", architecture)) } // GCPImage returns path to GCP pre-built image. func (o *Options) GCPImage(architecture string) string { return filepath.Join(o.ArtifactsPath, fmt.Sprintf("gcp-%s.raw.tar.gz", architecture)) } // SchematicFor returns the schematic for the given cloud. func (o *Options) SchematicFor(cloud string) string { for _, schematic := range o.FactorySchematics { parts := strings.Split(schematic, ":") if len(parts) != 2 { continue } cloudPart := parts[0] schematicPart := parts[1] if cloudPart == cloud { return schematicPart } } return "" } ================================================ FILE: hack/cloud-image-uploader/role-policy.json ================================================ { "Version":"2012-10-17", "Statement":[ { "Effect":"Allow", "Action":[ "s3:ListBucket", "s3:GetBucketLocation" ], "Resource":[ "arn:aws:s3:::*" ] }, { "Effect":"Allow", "Action":[ "s3:GetObject" ], "Resource":[ "arn:aws:s3:::*/*" ] }, { "Effect":"Allow", "Action":[ "ec2:ModifySnapshotAttribute", "ec2:CopySnapshot", "ec2:RegisterImage", "ec2:Describe*" ], "Resource":"*" } ] } ================================================ FILE: hack/cloud-image-uploader/trust-policy.json ================================================ { "Version":"2012-10-17", "Statement":[ { "Sid":"", "Effect":"Allow", "Principal":{ "Service":"vmie.amazonaws.com" }, "Action":"sts:AssumeRole", "Condition":{ "StringEquals":{ "sts:ExternalId":"vmimport" } } } ] } ================================================ FILE: hack/cloud-image-uploader.sh ================================================ #!/usr/bin/env bash set -e cd hack/cloud-image-uploader go run . --artifacts-path="../../${ARTIFACTS}" --tag="${TAG}" "$@" ================================================ FILE: hack/containerd.toml ================================================ version = 3 disabled_plugins = [ "io.containerd.cri.v1.images", "io.containerd.cri.v1.runtime", "io.containerd.differ.v1.erofs", "io.containerd.grpc.v1.cri", "io.containerd.grpc.v1.sandbox-controllers", "io.containerd.grpc.v1.sandboxes", "io.containerd.internal.v1.opt", "io.containerd.internal.v1.tracing", "io.containerd.monitor.container.v1.restart", "io.containerd.nri.v1.nri", "io.containerd.podsandbox.controller.v1.podsandbox", "io.containerd.sandbox.controller.v1.podsandbox", "io.containerd.sandbox.controller.v1.shim", "io.containerd.sandbox.controller.v1", "io.containerd.sandbox.store.v1.local", "io.containerd.sandbox.store.v1", "io.containerd.snapshotter.v1.blockfile", "io.containerd.snapshotter.v1.erofs", "io.containerd.ttrpc.v1.otelttrpc", "io.containerd.tracing.processor.v1.otlp", ] [debug] level = "info" format = "json" ================================================ FILE: hack/cri-containerd.toml ================================================ version = 3 disabled_plugins = [ "io.containerd.differ.v1.erofs", "io.containerd.internal.v1.tracing", "io.containerd.snapshotter.v1.blockfile", "io.containerd.snapshotter.v1.erofs", "io.containerd.ttrpc.v1.otelttrpc", "io.containerd.tracing.processor.v1.otlp", ] imports = [ "/etc/cri/conf.d/cri.toml", ] [debug] level = "info" format = "json" ================================================ FILE: hack/cri-plugin.part ================================================ version = 3 [plugins."io.containerd.cri.v1.images"] discard_unpacked_layers = true use_local_image_pull = true [plugins."io.containerd.cri.v1.runtime"] enable_cdi = true cdi_spec_dirs = ["/run/cdi"] [plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc] base_runtime_spec = "/etc/cri/conf.d/base-spec.json" [plugins."io.containerd.nri.v1.nri"] disable = true ================================================ FILE: hack/extra-modules.conf ================================================ search extras built-in ================================================ FILE: hack/fix-artifacts.sh ================================================ #!/usr/bin/env bash for platform in $(tr "," "\n" <<< "${PLATFORM}"); do echo ${platform} directory="${platform//\//_}" if [[ -d "${ARTIFACTS}/${directory}" ]]; then mv "${ARTIFACTS}/${directory}/"* ${ARTIFACTS} rmdir "${ARTIFACTS}/${directory}/" fi done ================================================ FILE: hack/labeled-squashfs.sh ================================================ #!/bin/bash set -eufx if [ $# -ne 4 ]; then printf 'Usage: %s \n' "${0##*/}" exit 2 fi root_dir=$1;shift output_image=$1;shift file_contexts=$1;shift compression_level=$1;shift if [ -n "${file_contexts:-}" ]; then # set SELinux labels for files according to file_contexts supplied setfiles -r "${root_dir}" -F -vv "${file_contexts}" "${root_dir}" fi mksquashfs "${root_dir}" "${output_image}" \ -all-root -noappend \ -comp zstd -Xcompression-level "${compression_level}" \ -no-progress ================================================ FILE: hack/lvm.conf ================================================ # Disable LVM backups as Talos rootfs is read-only, and ephemeral partition is not a safe place to store # metadata backups. # # See https://github.com/siderolabs/talos/issues/3129 backup { backup = 0 archive = 0 } ================================================ FILE: hack/modules-amd64.txt ================================================ kernel/crypto/async_tx/async_memcpy.ko kernel/crypto/async_tx/async_pq.ko kernel/crypto/async_tx/async_raid6_recov.ko kernel/crypto/async_tx/async_tx.ko kernel/crypto/async_tx/async_xor.ko kernel/crypto/hkdf.ko kernel/crypto/xor.ko kernel/drivers/ata/ahci.ko kernel/drivers/ata/libahci.ko kernel/drivers/ata/pata_amd.ko kernel/drivers/ata/pata_marvell.ko kernel/drivers/ata/pata_oldpiix.ko kernel/drivers/ata/pata_sch.ko kernel/drivers/block/nbd.ko kernel/drivers/block/ublk_drv.ko kernel/drivers/char/hw_random/amd-rng.ko kernel/drivers/char/hw_random/ba431-rng.ko kernel/drivers/char/hw_random/intel-rng.ko kernel/drivers/char/hw_random/via-rng.ko kernel/drivers/char/hw_random/virtio-rng.ko kernel/drivers/char/hw_random/xiphera-trng.ko kernel/drivers/char/ipmi/ipmi_watchdog.ko kernel/drivers/edac/amd64_edac.ko kernel/drivers/edac/e752x_edac.ko kernel/drivers/edac/i3000_edac.ko kernel/drivers/edac/i3200_edac.ko kernel/drivers/edac/i5100_edac.ko kernel/drivers/edac/i5400_edac.ko kernel/drivers/edac/i7300_edac.ko kernel/drivers/edac/i7core_edac.ko kernel/drivers/edac/i82975x_edac.ko kernel/drivers/edac/ie31200_edac.ko kernel/drivers/edac/igen6_edac.ko kernel/drivers/edac/sb_edac.ko kernel/drivers/edac/skx_edac.ko kernel/drivers/edac/x38_edac.ko kernel/drivers/gpu/drm/display/drm_display_helper.ko kernel/drivers/gpu/drm/drm_buddy.ko kernel/drivers/gpu/drm/drm_exec.ko kernel/drivers/gpu/drm/drm_gpusvm_helper.ko kernel/drivers/gpu/drm/drm_gpuvm.ko kernel/drivers/gpu/drm/drm_panel_backlight_quirks.ko kernel/drivers/gpu/drm/drm_suballoc_helper.ko kernel/drivers/gpu/drm/drm_ttm_helper.ko kernel/drivers/gpu/drm/scheduler/gpu-sched.ko kernel/drivers/gpu/drm/ttm/ttm.ko kernel/drivers/hid/hid-a4tech.ko kernel/drivers/hid/hid-apple.ko kernel/drivers/hid/hid-belkin.ko kernel/drivers/hid/hid-cherry.ko kernel/drivers/hid/hid-chicony.ko kernel/drivers/hid/hid-cypress.ko kernel/drivers/hid/hid-ezkey.ko kernel/drivers/hid/hid-gyration.ko kernel/drivers/hid/hid-ite.ko kernel/drivers/hid/hid-kensington.ko kernel/drivers/hid/hid-lg-g15.ko kernel/drivers/hid/hid-logitech.ko kernel/drivers/hid/hid-microsoft.ko kernel/drivers/hid/hid-monterey.ko kernel/drivers/hid/hid-multitouch.ko kernel/drivers/hid/hid-petalynx.ko kernel/drivers/hid/hid-pl.ko kernel/drivers/hid/hid-samsung.ko kernel/drivers/hid/hid-sunplus.ko kernel/drivers/hid/hid-topseed.ko kernel/drivers/hwmon/fam15h_power.ko kernel/drivers/hwmon/i5500_temp.ko kernel/drivers/hwmon/i5k_amb.ko kernel/drivers/hwmon/it87.ko kernel/drivers/hwmon/k10temp.ko kernel/drivers/hwmon/k8temp.ko kernel/drivers/i2c/algos/i2c-algo-bit.ko kernel/drivers/i2c/busses/i2c-i801.ko kernel/drivers/i2c/i2c-smbus.ko kernel/drivers/infiniband/core/ib_umad.ko kernel/drivers/infiniband/core/ib_uverbs.ko kernel/drivers/infiniband/core/rdma_ucm.ko kernel/drivers/infiniband/hw/irdma/irdma.ko kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko kernel/drivers/infiniband/sw/rxe/rdma_rxe.ko kernel/drivers/leds/led-class-multicolor.ko kernel/drivers/md/bcache/bcache.ko kernel/drivers/md/dm-bio-prison.ko kernel/drivers/md/dm-cache-smq.ko kernel/drivers/md/dm-cache.ko kernel/drivers/md/dm-integrity.ko kernel/drivers/md/dm-multipath.ko kernel/drivers/md/dm-raid.ko kernel/drivers/md/dm-round-robin.ko kernel/drivers/md/dm-thin-pool.ko kernel/drivers/md/persistent-data/dm-persistent-data.ko kernel/drivers/md/raid456.ko kernel/drivers/media/cec/core/cec.ko kernel/drivers/message/fusion/mptbase.ko kernel/drivers/message/fusion/mptsas.ko kernel/drivers/message/fusion/mptscsih.ko kernel/drivers/message/fusion/mptspi.ko kernel/drivers/mfd/lpc_ich.ko kernel/drivers/mfd/mfd-core.ko kernel/drivers/misc/hpilo.ko kernel/drivers/mmc/host/sdhci_f_sdh30.ko kernel/drivers/mmc/host/sdhci-acpi.ko kernel/drivers/mmc/host/sdhci-pci.ko kernel/drivers/mmc/host/sdhci-pltfm.ko kernel/drivers/mmc/host/sdhci-uhs2.ko kernel/drivers/mmc/host/sdhci-xenon-driver.ko kernel/drivers/net/ethernet/amazon/ena/ena.ko kernel/drivers/net/ethernet/aquantia/atlantic/atlantic.ko kernel/drivers/net/ethernet/atheros/alx/alx.ko kernel/drivers/net/ethernet/broadcom/bnx2.ko kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko kernel/drivers/net/ethernet/broadcom/tg3.ko kernel/drivers/net/ethernet/cavium/common/cavium_ptp.ko kernel/drivers/net/ethernet/cisco/enic/enic.ko kernel/drivers/net/ethernet/emulex/benet/be2net.ko kernel/drivers/net/ethernet/google/gve/gve.ko kernel/drivers/net/ethernet/intel/e100.ko kernel/drivers/net/ethernet/intel/e1000/e1000.ko kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko kernel/drivers/net/ethernet/intel/i40e/i40e.ko kernel/drivers/net/ethernet/intel/iavf/iavf.ko kernel/drivers/net/ethernet/intel/ice/ice.ko kernel/drivers/net/ethernet/intel/idpf/idpf.ko kernel/drivers/net/ethernet/intel/igb/igb.ko kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko kernel/drivers/net/ethernet/intel/igc/igc.ko kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko kernel/drivers/net/ethernet/intel/libeth/libeth_xdp.ko kernel/drivers/net/ethernet/intel/libeth/libeth.ko kernel/drivers/net/ethernet/intel/libie/libie_adminq.ko kernel/drivers/net/ethernet/intel/libie/libie_fwlog.ko kernel/drivers/net/ethernet/intel/libie/libie.ko kernel/drivers/net/ethernet/marvell/sky2.ko kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko kernel/drivers/net/ethernet/mellanox/mlxfw/mlxfw.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko kernel/drivers/net/ethernet/qlogic/netxen/netxen_nic.ko kernel/drivers/net/ethernet/qlogic/qed/qed.ko kernel/drivers/net/ethernet/qlogic/qede/qede.ko kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko kernel/drivers/net/ethernet/realtek/8139too.ko kernel/drivers/net/ethernet/realtek/r8169.ko kernel/drivers/net/ethernet/sfc/sfc.ko kernel/drivers/net/ethernet/sfc/siena/sfc-siena.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-intel.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko kernel/drivers/net/mdio.ko kernel/drivers/net/mii.ko kernel/drivers/net/pcs/pcs_xpcs.ko kernel/drivers/net/usb/r8152.ko kernel/drivers/net/vmxnet3/vmxnet3.ko kernel/drivers/net/vrf.ko kernel/drivers/nvme/common/nvme-auth.ko kernel/drivers/nvme/host/nvme-rdma.ko kernel/drivers/nvme/host/nvme.ko kernel/drivers/nvme/target/nvme-loop.ko kernel/drivers/nvme/target/nvmet-fc.ko kernel/drivers/nvme/target/nvmet-rdma.ko kernel/drivers/nvme/target/nvmet-tcp.ko kernel/drivers/nvme/target/nvmet.ko kernel/drivers/platform/x86/intel/intel-vsec.ko kernel/drivers/platform/x86/intel/pmc/intel_pmc_core_pltdrv.ko kernel/drivers/platform/x86/intel/pmc/intel_pmc_core.ko kernel/drivers/platform/x86/intel/pmc/intel_pmc_ssram_telemetry.ko kernel/drivers/platform/x86/intel/pmt/pmt_class.ko kernel/drivers/platform/x86/intel/pmt/pmt_discovery.ko kernel/drivers/platform/x86/intel/pmt/pmt_telemetry.ko kernel/drivers/powercap/intel_rapl_common.ko kernel/drivers/powercap/intel_rapl_msr.ko kernel/drivers/scsi/aacraid/aacraid.ko kernel/drivers/scsi/fcoe/libfcoe.ko kernel/drivers/scsi/fnic/fnic.ko kernel/drivers/scsi/hpsa.ko kernel/drivers/scsi/isci/isci.ko kernel/drivers/scsi/libfc/libfc.ko kernel/drivers/scsi/lpfc/lpfc.ko kernel/drivers/scsi/megaraid/megaraid_sas.ko kernel/drivers/scsi/mpi3mr/mpi3mr.ko kernel/drivers/scsi/mpt3sas/mpt3sas.ko kernel/drivers/scsi/qedf/qedf.ko kernel/drivers/scsi/qla2xxx/qla2xxx.ko kernel/drivers/scsi/smartpqi/smartpqi.ko kernel/drivers/scsi/vmw_pvscsi.ko kernel/drivers/uio/uio_pci_generic.ko kernel/drivers/uio/uio.ko kernel/drivers/usb/serial/ch341.ko kernel/drivers/usb/serial/cp210x.ko kernel/drivers/usb/serial/ftdi_sio.ko kernel/drivers/usb/serial/pl2303.ko kernel/drivers/vdpa/alibaba/eni_vdpa.ko kernel/drivers/vdpa/mlx5/mlx5_vdpa.ko kernel/drivers/vdpa/octeon_ep/octep_vdpa.ko kernel/drivers/vdpa/solidrun/snet_vdpa.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim_blk.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim_net.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim.ko kernel/drivers/vdpa/vdpa_user/vduse.ko kernel/drivers/vdpa/vdpa.ko kernel/drivers/vdpa/virtio_pci/vp_vdpa.ko kernel/drivers/vfio/pci/vfio-pci-core.ko kernel/drivers/vfio/pci/vfio-pci.ko kernel/drivers/vfio/vfio_iommu_type1.ko kernel/drivers/vfio/vfio.ko kernel/drivers/vhost/vhost_vdpa.ko kernel/drivers/vhost/vringh.ko kernel/drivers/virt/coco/guest/tsm_report.ko kernel/drivers/virt/coco/sev-guest/sev-guest.ko kernel/drivers/virtio/virtio_balloon.ko kernel/drivers/virtio/virtio_mem.ko kernel/drivers/virtio/virtio_pci_legacy_dev.ko kernel/drivers/virtio/virtio_pci_modern_dev.ko kernel/drivers/virtio/virtio_pci.ko kernel/drivers/virtio/virtio_vdpa.ko kernel/drivers/watchdog/f71808e_wdt.ko kernel/drivers/watchdog/i6300esb.ko kernel/drivers/watchdog/iTCO_vendor_support.ko kernel/drivers/watchdog/iTCO_wdt.ko kernel/drivers/watchdog/sp5100_tco.ko kernel/drivers/watchdog/watchdog.ko kernel/drivers/watchdog/wdat_wdt.ko kernel/drivers/watchdog/xen_wdt.ko kernel/lib/crc/crc8.ko kernel/lib/objagg.ko kernel/lib/parman.ko kernel/lib/raid6/raid6_pq.ko kernel/net/ipv4/ip_gre.ko kernel/net/ipv6/ip6_gre.ko kernel/net/openvswitch/vport-gre.ko kernel/net/tls/tls.ko modules.builtin modules.builtin.modinfo modules.order ================================================ FILE: hack/modules-arm64.txt ================================================ kernel/arch/arm64/lib/xor-neon.ko kernel/crypto/async_tx/async_memcpy.ko kernel/crypto/async_tx/async_pq.ko kernel/crypto/async_tx/async_raid6_recov.ko kernel/crypto/async_tx/async_tx.ko kernel/crypto/async_tx/async_xor.ko kernel/crypto/hkdf.ko kernel/crypto/xor.ko kernel/drivers/acpi/video.ko kernel/drivers/ata/ahci.ko kernel/drivers/ata/pata_amd.ko kernel/drivers/ata/pata_marvell.ko kernel/drivers/ata/pata_oldpiix.ko kernel/drivers/ata/pata_sch.ko kernel/drivers/block/nbd.ko kernel/drivers/block/ublk_drv.ko kernel/drivers/crypto/tegra/tegra-se.ko kernel/drivers/gpu/drm/drm_buddy.ko kernel/drivers/gpu/drm/drm_exec.ko kernel/drivers/gpu/drm/drm_gpuvm.ko kernel/drivers/gpu/drm/drm_panel_backlight_quirks.ko kernel/drivers/gpu/drm/drm_suballoc_helper.ko kernel/drivers/gpu/drm/drm_ttm_helper.ko kernel/drivers/gpu/drm/drm_vram_helper.ko kernel/drivers/gpu/drm/hisilicon/hibmc/hibmc-drm.ko kernel/drivers/gpu/drm/scheduler/gpu-sched.ko kernel/drivers/gpu/drm/tegra/tegra-drm.ko kernel/drivers/gpu/drm/ttm/ttm.ko kernel/drivers/gpu/host1x/host1x.ko kernel/drivers/hid/hid-a4tech.ko kernel/drivers/hid/hid-apple.ko kernel/drivers/hid/hid-belkin.ko kernel/drivers/hid/hid-cherry.ko kernel/drivers/hid/hid-chicony.ko kernel/drivers/hid/hid-cypress.ko kernel/drivers/hid/hid-ezkey.ko kernel/drivers/hid/hid-gyration.ko kernel/drivers/hid/hid-ite.ko kernel/drivers/hid/hid-kensington.ko kernel/drivers/hid/hid-lg-g15.ko kernel/drivers/hid/hid-logitech.ko kernel/drivers/hid/hid-microsoft.ko kernel/drivers/hid/hid-monterey.ko kernel/drivers/hid/hid-multitouch.ko kernel/drivers/hid/hid-petalynx.ko kernel/drivers/hid/hid-pl.ko kernel/drivers/hid/hid-samsung.ko kernel/drivers/hid/hid-sunplus.ko kernel/drivers/hid/hid-topseed.ko kernel/drivers/hwmon/i5k_amb.ko kernel/drivers/i2c/algos/i2c-algo-bit.ko kernel/drivers/i2c/busses/i2c-i801.ko kernel/drivers/i2c/i2c-mux.ko kernel/drivers/infiniband/core/ib_umad.ko kernel/drivers/infiniband/core/ib_uverbs.ko kernel/drivers/infiniband/core/rdma_ucm.ko kernel/drivers/infiniband/hw/hns/hns-roce-hw-v2.ko kernel/drivers/infiniband/hw/irdma/irdma.ko kernel/drivers/infiniband/hw/mlx4/mlx4_ib.ko kernel/drivers/infiniband/hw/mlx5/mlx5_ib.ko kernel/drivers/infiniband/sw/rxe/rdma_rxe.ko kernel/drivers/irqchip/irq-bcm2712-mip.ko kernel/drivers/irqchip/irq-imx-mu-msi.ko kernel/drivers/leds/led-class-multicolor.ko kernel/drivers/mailbox/bcm-flexrm-mailbox.ko kernel/drivers/md/bcache/bcache.ko kernel/drivers/md/dm-bio-prison.ko kernel/drivers/md/dm-cache-smq.ko kernel/drivers/md/dm-cache.ko kernel/drivers/md/dm-integrity.ko kernel/drivers/md/dm-multipath.ko kernel/drivers/md/dm-raid.ko kernel/drivers/md/dm-round-robin.ko kernel/drivers/md/dm-thin-pool.ko kernel/drivers/md/persistent-data/dm-persistent-data.ko kernel/drivers/md/raid456.ko kernel/drivers/misc/hpilo.ko kernel/drivers/mmc/host/sdhci_f_sdh30.ko kernel/drivers/mmc/host/sdhci-acpi.ko kernel/drivers/mmc/host/sdhci-brcmstb.ko kernel/drivers/mmc/host/sdhci-cadence.ko kernel/drivers/mmc/host/sdhci-iproc.ko kernel/drivers/mmc/host/sdhci-msm.ko kernel/drivers/mmc/host/sdhci-of-arasan.ko kernel/drivers/mmc/host/sdhci-of-dwcmshc.ko kernel/drivers/mmc/host/sdhci-of-esdhc.ko kernel/drivers/mmc/host/sdhci-pci.ko kernel/drivers/mmc/host/sdhci-pltfm.ko kernel/drivers/mmc/host/sdhci-tegra.ko kernel/drivers/mmc/host/sdhci-uhs2.ko kernel/drivers/mmc/host/sdhci-xenon-driver.ko kernel/drivers/net/ethernet/amazon/ena/ena.ko kernel/drivers/net/ethernet/aquantia/atlantic/atlantic.ko kernel/drivers/net/ethernet/atheros/alx/alx.ko kernel/drivers/net/ethernet/broadcom/bnx2.ko kernel/drivers/net/ethernet/broadcom/bnx2x/bnx2x.ko kernel/drivers/net/ethernet/broadcom/bnxt/bnxt_en.ko kernel/drivers/net/ethernet/broadcom/tg3.ko kernel/drivers/net/ethernet/cavium/common/cavium_ptp.ko kernel/drivers/net/ethernet/cisco/enic/enic.ko kernel/drivers/net/ethernet/google/gve/gve.ko kernel/drivers/net/ethernet/hisilicon/hip04_eth.ko kernel/drivers/net/ethernet/hisilicon/hisi_femac.ko kernel/drivers/net/ethernet/hisilicon/hix5hd2_gmac.ko kernel/drivers/net/ethernet/hisilicon/hns_mdio.ko kernel/drivers/net/ethernet/hisilicon/hns/hnae.ko kernel/drivers/net/ethernet/hisilicon/hns/hns_dsaf.ko kernel/drivers/net/ethernet/hisilicon/hns/hns_enet_drv.ko kernel/drivers/net/ethernet/hisilicon/hns3/hclge-common.ko kernel/drivers/net/ethernet/hisilicon/hns3/hclge.ko kernel/drivers/net/ethernet/hisilicon/hns3/hclgevf.ko kernel/drivers/net/ethernet/hisilicon/hns3/hnae3.ko kernel/drivers/net/ethernet/hisilicon/hns3/hns3.ko kernel/drivers/net/ethernet/intel/e100.ko kernel/drivers/net/ethernet/intel/e1000/e1000.ko kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko kernel/drivers/net/ethernet/intel/i40e/i40e.ko kernel/drivers/net/ethernet/intel/iavf/iavf.ko kernel/drivers/net/ethernet/intel/ice/ice.ko kernel/drivers/net/ethernet/intel/idpf/idpf.ko kernel/drivers/net/ethernet/intel/igb/igb.ko kernel/drivers/net/ethernet/intel/igbvf/igbvf.ko kernel/drivers/net/ethernet/intel/igc/igc.ko kernel/drivers/net/ethernet/intel/ixgbe/ixgbe.ko kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko kernel/drivers/net/ethernet/intel/libeth/libeth_xdp.ko kernel/drivers/net/ethernet/intel/libeth/libeth.ko kernel/drivers/net/ethernet/intel/libie/libie_adminq.ko kernel/drivers/net/ethernet/intel/libie/libie_fwlog.ko kernel/drivers/net/ethernet/intel/libie/libie.ko kernel/drivers/net/ethernet/marvell/sky2.ko kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_core.ko kernel/drivers/net/ethernet/mellanox/mlx4/mlx4_en.ko kernel/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.ko kernel/drivers/net/ethernet/mellanox/mlxfw/mlxfw.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_core.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_i2c.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_minimal.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_pci.ko kernel/drivers/net/ethernet/mellanox/mlxsw/mlxsw_spectrum.ko kernel/drivers/net/ethernet/qlogic/qed/qed.ko kernel/drivers/net/ethernet/qlogic/qede/qede.ko kernel/drivers/net/ethernet/qlogic/qlcnic/qlcnic.ko kernel/drivers/net/ethernet/realtek/8139too.ko kernel/drivers/net/ethernet/realtek/r8169.ko kernel/drivers/net/ethernet/sfc/sfc.ko kernel/drivers/net/ethernet/sfc/siena/sfc-siena.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-dwc-qos-eth.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.ko kernel/drivers/net/ethernet/stmicro/stmmac/dwmac-tegra.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-pci.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac-platform.ko kernel/drivers/net/ethernet/stmicro/stmmac/stmmac.ko kernel/drivers/net/mdio.ko kernel/drivers/net/pcs/pcs_xpcs.ko kernel/drivers/net/phy/dp83867.ko kernel/drivers/net/usb/r8152.ko kernel/drivers/net/vmxnet3/vmxnet3.ko kernel/drivers/net/vrf.ko kernel/drivers/nvme/common/nvme-auth.ko kernel/drivers/nvme/host/nvme-rdma.ko kernel/drivers/nvme/host/nvme.ko kernel/drivers/nvme/target/nvme-loop.ko kernel/drivers/nvme/target/nvmet-fc.ko kernel/drivers/nvme/target/nvmet-rdma.ko kernel/drivers/nvme/target/nvmet-tcp.ko kernel/drivers/nvme/target/nvmet.ko kernel/drivers/perf/hisilicon/hisi_pcie_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_hha_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_pa_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.ko kernel/drivers/perf/hisilicon/hisi_uncore_uc_pmu.ko kernel/drivers/perf/hisilicon/hns3_pmu.ko kernel/drivers/scsi/fcoe/libfcoe.ko kernel/drivers/scsi/hisi_sas/hisi_sas_main.ko kernel/drivers/scsi/hisi_sas/hisi_sas_v1_hw.ko kernel/drivers/scsi/hisi_sas/hisi_sas_v2_hw.ko kernel/drivers/scsi/hisi_sas/hisi_sas_v3_hw.ko kernel/drivers/scsi/hpsa.ko kernel/drivers/scsi/libfc/libfc.ko kernel/drivers/scsi/lpfc/lpfc.ko kernel/drivers/scsi/megaraid/megaraid_sas.ko kernel/drivers/scsi/mpi3mr/mpi3mr.ko kernel/drivers/scsi/mpt3sas/mpt3sas.ko kernel/drivers/scsi/qedf/qedf.ko kernel/drivers/scsi/qla2xxx/qla2xxx.ko kernel/drivers/scsi/smartpqi/smartpqi.ko kernel/drivers/uio/uio_pci_generic.ko kernel/drivers/uio/uio.ko kernel/drivers/usb/serial/ch341.ko kernel/drivers/usb/serial/cp210x.ko kernel/drivers/usb/serial/ftdi_sio.ko kernel/drivers/usb/serial/pl2303.ko kernel/drivers/vdpa/mlx5/mlx5_vdpa.ko kernel/drivers/vdpa/octeon_ep/octep_vdpa.ko kernel/drivers/vdpa/solidrun/snet_vdpa.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim_blk.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim_net.ko kernel/drivers/vdpa/vdpa_sim/vdpa_sim.ko kernel/drivers/vdpa/vdpa_user/vduse.ko kernel/drivers/vdpa/vdpa.ko kernel/drivers/vdpa/virtio_pci/vp_vdpa.ko kernel/drivers/vfio/pci/vfio-pci-core.ko kernel/drivers/vfio/pci/vfio-pci.ko kernel/drivers/vfio/vfio_iommu_type1.ko kernel/drivers/vfio/vfio.ko kernel/drivers/vhost/vhost_vdpa.ko kernel/drivers/vhost/vringh.ko kernel/drivers/virtio/virtio_balloon.ko kernel/drivers/virtio/virtio_input.ko kernel/drivers/virtio/virtio_mmio.ko kernel/drivers/virtio/virtio_pci_legacy_dev.ko kernel/drivers/virtio/virtio_pci_modern_dev.ko kernel/drivers/virtio/virtio_pci.ko kernel/drivers/virtio/virtio_vdpa.ko kernel/drivers/watchdog/sbsa_gwdt.ko kernel/lib/objagg.ko kernel/lib/parman.ko kernel/lib/raid6/raid6_pq.ko kernel/net/ipv4/ip_gre.ko kernel/net/ipv6/ip6_gre.ko kernel/net/openvswitch/vport-gre.ko kernel/net/tls/tls.ko modules.builtin modules.builtin.modinfo modules.order ================================================ FILE: hack/nfsmount.conf ================================================ [ NFSMount_Global_Options ] nolock=true ================================================ FILE: hack/release.sh ================================================ #!/usr/bin/env bash set -e RELEASE_TOOL_IMAGE="ghcr.io/siderolabs/release-tool:latest" function release-tool { docker pull "${RELEASE_TOOL_IMAGE}" >/dev/null docker run --net=host --rm -w /src -v "${PWD}":/src:ro "${RELEASE_TOOL_IMAGE}" -l -d -n ${2} -t "${1}" ./hack/release.toml } function changelog { if [ "$#" -eq 1 ]; then (release-tool ${1}; echo; cat CHANGELOG.md) > CHANGELOG.md- && mv CHANGELOG.md- CHANGELOG.md else echo 1>&2 "Usage: $0 changelog [tag]" exit 1 fi } function release-notes { release-tool "${2}" --gfm > "${1}" echo -e '\n## Images\n\n```' >> ${1} ${ARTIFACTS}/talosctl-linux-amd64 image k8s-bundle >> ${1} ${ARTIFACTS}/talosctl-linux-amd64 image talos-bundle --overlays=false --extensions=false >> ${1} echo -e '```\n' >> ${1} } function cherry-pick { if [ $# -ne 2 ]; then echo 1>&2 "Usage: $0 cherry-pick " exit 1 fi git checkout $2 git fetch git rebase upstream/$2 git cherry-pick -x $1 } function commit { if [ $# -ne 1 ]; then echo 1>&2 "Usage: $0 commit " exit 1 fi git commit -s -m "release($1): prepare release" -m "This is the official $1 release." } if declare -f "$1" > /dev/null then cmd="$1" shift $cmd "$@" else cat <` to `map`. """ [notes.serviceAccountIssuer] title = "Service Account Issuer configuration" description = """\ In API Server, passing extra args with `service-account-issuer` will append them after default value. This allows easy migration, e.g. by changing `.cluster.controlPlane.endpoint` to new value, and keeping the old value in `.cluster.apiServer.extraArgs["service-account-issuer"]`. """ [notes.negativeMaxVolumeSize] title = "Negative Max Volume Size" description = """\ Negative max size represents the amount of space to be left free on the device, rather than the size the volume should consume. For example: * a max size of "-10GiB" means the volume can grow to the available space minus 10GiB. * a max size of "-25%" means the volume can grow to the available space minus 25%. """ [notes.resolver_config] title = "ResolverConfig" description = """\ The nameservers configuration in machine configuration now overwrites any previous layers (defaults, platform, etc.) when specified. Previously a smart merge was performed to keep IPv4/IPv6 nameservers from lower layers if the machine configuration specified only one type. """ [notes.kernel_preempt] title = "Dynamic Linux Kernel Preemption Model" description = """\ Talos Linux now defaults to dynamic Linux kernel preemption model, the default value `none` matches previous version, but now with kernel argument `preempt=` the preemption model can be changed. See [Linux kernel documentation](https://docs.kernel.org/admin-guide/kernel-parameters.html) for more information on supported values. This change only applies to amd64 (x86_64) architecture. """ [notes.probe_config] title = "ProbeConfig" description = """\ The TCPProbeConfig configuration document allows to configure TCP probes for network reachability checks. This allows to define a custom connectivity condition. """ [notes.images] title = "Image APIs Updated" description = """\ Talos Linux provides new APIs to manage container images on the node: listing, pulling, importing and removing images. The new pull APIs provides pull progress notifications. The CLI commands `talosctl image pull`, `talosctl image list` and `talosctl image remove` have been updated to interact with the new APIs. """ [notes.debug] title = "talosctl debug" description = """\ Talos Linux now provides a way to run and attach to the privileged debug container with a user-provided container image. The debug container might be used for troubleshooting and debugging purposes. """ [notes.network-policy] title = "Flannel CNI with Network Policy Support" description = """\ Talos Linux now supports optionally deploying Flannel CNI with [network policy support](https://kubernetes.io/docs/concepts/services-networking/network-policies/) enabled. The network policy implementation is [kube-network-policies](https://github.com/kubernetes-sigs/kube-network-policies/). To enable Flannel CNI with network policy support, use the following machine configuration patch: ```yaml cluster: network: cni: name: flannel flannel: kubeNetworkPoliciesEnabled: true ``` (If the cluster is already running, sync the bootstrap manifests after applying the patch to deploy the new CNI configuration.) """ [notes.kubespan-filters] title = "KubeSpan Advertised Network Filters" description = """\ KubeSpan now supports filtering of advertised networks using the `excludeAdvertisedNetworks` field in the `KubeSpanConfig` document. This allows users to specify a list of CIDRs to exclude from the advertised networks. Please note that routing must be symmetric for any pair of peers, so if one peer excludes a certain network, the other peer must also exclude it. In other words, for any given pair of peers, and any pair of their addresses, the traffic should either go through KubeSpan or not, but not one way or the other. """ [notes.clang-thinlto] title = "Clang built kernel and ThinLTO" description = """\ Talos now uses a kernel built using Clang compiler, and optimized using ThinLTO. This should bring a small performance improvement, alongside some hardening features, such as BTI on supported ARM systems. """ [notes.vrf] title = "VRF Support" description = """\ Talos now supports VRF (Virtual Routing and Forwarding) via the new `VRFConfig` machine config document. """ [notes.image_signatures] title = "Container Image Signature Verification" description = """\ Talos now supports machine-wide container image signature verification via the new `ImageVerificationConfig` machine config document. Any image which gets pulled on the node will be verified against the configured rules, and if no rule matches, it will be pulled without verification. """ [notest.blackhole_routes] title = "Blackhole Route Support" description = """\ Talos now supports blackhole routes via the new `BlackholeRouteConfig` machine config document. """ [notes.install_upgrade_api] title = "Install and Upgrade API" description = """\ Talos now exposes install and upgrade operations via the `LifecycleService` API, enabling programmatic installs and upgrades through a single, consistent interface. The legacy upgrade API is deprecated; new integrations should migrate to `LifecycleService` for future compatibility. """ [notes.talosctl_upgrade_lifecycle] title = "Lifecycle Upgrade in talosctl" description = """\ `talosctl` upgrades now route through `LifecycleService`, aligning CLI behavior with the new install/upgrade API and unifying the upgrade path. This change is transparent to users but standardizes the backend used for upgrades. """ [notes.container_device_interface] title = "Container Device Interface" description = """\ Talos now enables [CDI](https://github.com/cncf-tags/container-device-interface) by default and extension/extension services can bring in dynamic CDI spec files under `/run/cdi`. """ [notes.nvidia] title = "NVIDIA GPU Support" description = """\ Talos switched to using CDI and now supports configuring NVIDIA GPU via the gpu-operator helm chart. See the documentation on [upgrade notes](https://docs.siderolabs.com/talos/v1.13/configure-your-talos-cluster/lifecycle-management/upgrading-talos#after-upgrade-to) for more details on how to configure NVIDIA GPU support in Talos. """ [notes.routing_rules] title = "Routing Rules Support" description = """\ Talos now supports routing rules via the new `RoutingRuleConfig` machine config document. """ [make_deps] [make_deps.tools] variable = "TOOLS" repository = "github.com/siderolabs/tools" [make_deps.pkgs] variable = "PKGS" repository = "github.com/siderolabs/pkgs" ================================================ FILE: hack/sbom.sh ================================================ #!/bin/bash set -euo pipefail SYFT_FORMAT_PRETTY=1 SYFT_FORMAT_SPDX_JSON_DETERMINISTIC_UUID=1 \ go tool \ github.com/anchore/syft/cmd/syft \ scan --from dir "$1" \ --select-catalogers "+sbom-cataloger,go" \ --source-name "$NAME" --source-version "$TAG" \ -o spdx-json > "/rootfs/usr/share/spdx/$2" ================================================ FILE: hack/start-registry-proxies.sh ================================================ #!/usr/bin/env bash # Sync changes with configuring-pull-through-cache.md. set -e docker run -d -p 5000:5000 \ -e REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io \ --restart always \ --name registry-docker.io registry:2 docker run -d -p 5001:5000 \ -e REGISTRY_PROXY_REMOTEURL=https://registry.k8s.io \ --restart always \ --name registry-registry.k8s.io registry:2 docker run -d -p 5003:5000 \ -e REGISTRY_PROXY_REMOTEURL=https://gcr.io \ --restart always \ --name registry-gcr.io registry:2 docker run -d -p 5004:5000 \ -e REGISTRY_PROXY_REMOTEURL=https://ghcr.io \ --restart always \ --name registry-ghcr.io registry:2 docker run -d -p 5006:5000 \ -e REGISTRY_PROXY_REMOTEURL=https://factory.talos.dev \ --restart always \ --name registry-factory.talos.dev registry:2 ================================================ FILE: hack/test/cis/kube-bench-master.yaml ================================================ apiVersion: batch/v1 kind: Job metadata: name: kube-bench-master spec: template: spec: hostPID: true nodeSelector: node-role.kubernetes.io/master: '' tolerations: - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: kube-bench image: aquasec/kube-bench:latest args: - master - --version=1.13 volumeMounts: - name: etc-kubernetes mountPath: /etc/kubernetes volumes: - name: etc-kubernetes hostPath: path: /etc/kubernetes type: Directory restartPolicy: Never backoffLimit: 0 ================================================ FILE: hack/test/cis/kube-bench-node.yaml ================================================ apiVersion: batch/v1 kind: Job metadata: name: kube-bench-node spec: template: spec: hostPID: true containers: - name: kube-bench image: aquasec/kube-bench:latest args: - node - --version=1.13 volumeMounts: - name: etc-kubernetes mountPath: /etc/kubernetes volumes: - name: etc-kubernetes hostPath: path: /etc/kubernetes type: Directory restartPolicy: Never backoffLimit: 0 ================================================ FILE: hack/test/e2e-aws-prepare.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh REGION="us-east-1" function cloud_image_upload() { RANDOM_SUFFIX=$(openssl rand -hex 4) CLOUD_IMAGES_EXTRA_ARGS=("--name-prefix=${1}-${RANDOM_SUFFIX}" "--target-clouds=aws" "--architectures=amd64" "--aws-regions=${REGION}") case "${1}" in talos-e2e-nvidia-oss-*) CLOUD_IMAGES_EXTRA_ARGS+=("--aws-force-bios") ;; esac make cloud-images CLOUD_IMAGES_EXTRA_ARGS="${CLOUD_IMAGES_EXTRA_ARGS[*]}" } function get_ami_id() { jq -r ".[] | select(.cloud == \"aws\") | select(.region == \"${REGION}\") | select (.arch == \"amd64\") | .id" "${ARTIFACTS}/cloud-images.json" } function cloud_image_upload_with_extensions() { case "${1}" in nvidia-oss-lts) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nvidia-open-gpu-kernel-modules-lts") or contains("nvidia-container-toolkit-lts")) and (contains("nvidia-fabricmanager") or contains("nonfree-kmod-nvidia") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; nvidia-oss-production) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nvidia-open-gpu-kernel-modules-production") or contains("nvidia-container-toolkit-production")) and (contains("nvidia-fabricmanager") or contains("nonfree-kmod-nvidia") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; nvidia-oss-fabricmanager) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nvidia-open-gpu-kernel-modules-production") or contains("nvidia-container-toolkit-production")) and (contains("nonfree-kmod-nvidia") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; nvidia-nonfree-lts) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nonfree-kmod-nvidia-lts") or contains("nvidia-container-toolkit-lts")) and (contains("nvidia-fabricmanager") or contains("nvidia-open-gpu-kernel-modules") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; nvidia-nonfree-production) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nonfree-kmod-nvidia-production") or contains("nvidia-container-toolkit-production")) and (contains("nvidia-fabricmanager") or contains("nvidia-open-gpu-kernel-modules") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; nvidia-nonfree-fabricmanager) EXTENSIONS=$(jq -R < "${EXTENSIONS_METADATA_FILE}" | jq -rs 'map(select(. | (contains("nonfree-kmod-nvidia-lts") or contains("nvidia-container-toolkit-lts")) and (contains("nvidia-open-gpu-kernel-modules") | not))) | .[] |= "--system-extension-image=" + . | join(" ")') ;; *) ;; esac make image-aws IMAGER_ARGS="${EXTENSIONS}" PLATFORM=linux/amd64 cloud_image_upload "talos-e2e-${1}" } cloud_image_upload "talos-e2e" AMI_ID=$(get_ami_id) WORKER_GROUP= NVIDIA_AMI_ID= case "${E2E_AWS_TARGET:-default}" in default) ;; *) WORKER_GROUP="nvidia" cloud_image_upload_with_extensions "${E2E_AWS_TARGET}" NVIDIA_AMI_ID=$(get_ami_id) # cloud_image_upload_with_extensions "${E2E_AWS_TARGET}-fabricmanager" # NVIDIA_FM_AMI_ID=$(get_ami_id) ;; esac mkdir -p "${ARTIFACTS}/e2e-aws-generated" NAME_PREFIX="${SHA}-${E2E_AWS_TARGET}" jq --null-input \ --arg WORKER_GROUP "${WORKER_GROUP}" \ --arg AMI_ID "${AMI_ID}" \ --arg NVIDIA_AMI_ID "${NVIDIA_AMI_ID}" \ --arg CLUSTER_NAME "${NAME_PREFIX}" \ --arg TALOS_VERSION_CONTRACT "${TALOS_VERSION}" \ --arg KUBERNETES_VERSION "${KUBERNETES_VERSION}" \ '{ worker_group: $WORKER_GROUP, ami_id: $AMI_ID, nvidia_ami_id: $NVIDIA_AMI_ID, cluster_name: $CLUSTER_NAME, talos_version_contract: $TALOS_VERSION_CONTRACT, kubernetes_version: $KUBERNETES_VERSION }' \ | jq -f hack/test/tfvars/aws.jq > "${ARTIFACTS}/e2e-aws-generated/vars.json" cp hack/test/tfvars/*.yaml "${ARTIFACTS}/e2e-aws-generated" ================================================ FILE: hack/test/e2e-aws.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh cp "${ARTIFACTS}/e2e-aws-talosconfig" "${TALOSCONFIG}" cp "${ARTIFACTS}/e2e-aws-kubeconfig" "${KUBECONFIG}" # set the talosconfig to use the first controlplane ip CONTROLPLANE0_NODE=$(${TALOSCTL} config info -o json | jq -r '.endpoints[0]') ${TALOSCTL} config node "${CONTROLPLANE0_NODE}" run_talos_integration_test run_kubernetes_integration_test ================================================ FILE: hack/test/e2e-azure.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh cp "${ARTIFACTS}/e2e-azure-talosconfig" "${TALOSCONFIG}" cp "${ARTIFACTS}/e2e-azure-kubeconfig" "${KUBECONFIG}" # set the talosconfig to use the first controlplane ip CONTROLPLANE0_NODE=$(${TALOSCTL} config info -o json | jq -r '.endpoints[0]') ${TALOSCTL} config node "${CONTROLPLANE0_NODE}" run_talos_integration_test run_kubernetes_integration_test ================================================ FILE: hack/test/e2e-cloud-tf.sh ================================================ #!/usr/bin/env bash set -eou pipefail # This script is used to run the end-to-end tests on a cloud provider using Terraform. BUCKET_NAME="talos-ci-e2e" TF_DIR="${TF_SCRIPT_DIR}/examples/terraform/${TF_E2E_TEST_TYPE}" cp "${TF_SCRIPT_DIR}/hack/backend-aws.tf" "${TF_DIR}/backend.tf" cp "${ARTIFACTS}/e2e-${TF_E2E_TEST_TYPE}-generated"/* "${TF_DIR}" CLUSTER_NAME=$(jq -e -r '.cluster_name' "${TF_DIR}/vars.json") BACKEND_CONFIG_KEY="cloud-tf/${CLUSTER_NAME}-terraform.tfstate" terraform -chdir="${TF_DIR}" \ init \ -backend-config="bucket=${BUCKET_NAME}" \ -backend-config="key=${BACKEND_CONFIG_KEY}" case "${TF_E2E_ACTION}" in "apply") terraform -chdir="${TF_DIR}" \ apply \ -auto-approve \ -var-file="vars.json" terraform -chdir="${TF_DIR}" \ output \ -raw \ talosconfig > "${ARTIFACTS}/e2e-${TF_E2E_TEST_TYPE}-talosconfig" terraform -chdir="${TF_DIR}" \ output \ -raw \ kubeconfig > "${ARTIFACTS}/e2e-${TF_E2E_TEST_TYPE}-kubeconfig" ;; "destroy") terraform -chdir="${TF_DIR}" \ apply \ -destroy \ -auto-approve \ -var-file="vars.json" \ -refresh="${TF_E2E_REFRESH_ON_DESTROY:-true}" aws s3api delete-object --bucket "${BUCKET_NAME}" --key "${BACKEND_CONFIG_KEY}" ;; *) echo "Unsupported action: ${TF_E2E_ACTION}" exit 1 ;; esac ================================================ FILE: hack/test/e2e-docker.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh PROVISIONER=docker CLUSTER_NAME=e2e-${PROVISIONER} function create_cluster { build_registry_mirrors "${TALOSCTL}" cluster create docker \ --name="${CLUSTER_NAME}" \ --kubernetes-version=${KUBERNETES_VERSION} \ --image="${IMAGE}" \ --workers=1 \ --mtu=1430 \ --config-patch=hack/test/patches/image-verification.yaml \ "${REGISTRY_MIRROR_FLAGS[@]}" "${TALOSCTL}" config node 10.5.0.2 } function destroy_cluster() { "${TALOSCTL}" cluster destroy --name "${CLUSTER_NAME}" --provisioner "${PROVISIONER}" --save-support-archive-path=/tmp/support-${CLUSTER_NAME}.zip } trap destroy_cluster SIGINT EXIT create_cluster get_kubeconfig ${KUBECTL} config set-cluster e2e-docker --server https://10.5.0.2:6443 run_talos_integration_test_docker run_kubernetes_integration_test ================================================ FILE: hack/test/e2e-embedded.sh ================================================ #!/usr/bin/env bash set -eoux pipefail source ./hack/test/e2e.sh PROVISIONER=qemu CLUSTER_NAME=e2e-embedded NODE="172.20.3.2" function build_iso_embedded { # build the ISO with embedded config cp hack/test/patches/watchdog.yaml ${ARTIFACTS}/embedded.yaml make image-iso IMAGER_ARGS="--embedded-config-path=/out/embedded.yaml" PLATFORM=linux/amd64 } function create_cluster { "${TALOSCTL}" cluster create \ --provisioner="${PROVISIONER}" \ --name="${CLUSTER_NAME}" \ --iso-path=${ARTIFACTS}/metal-amd64.iso \ --controlplanes=1 \ --workers=0 \ --mtu=1430 \ --memory=2048 \ --cpus=2.0 \ --cidr=172.20.3.0/24 \ --skip-injecting-config \ --wait=false \ --cni-bundle-url=${ARTIFACTS}/talosctl-cni-bundle-'${ARCH}'.tar.gz } function destroy_cluster() { "${TALOSCTL}" cluster destroy \ --name "${CLUSTER_NAME}" \ --provisioner "${PROVISIONER}" \ --save-cluster-logs-archive-path="/tmp/logs-${CLUSTER_NAME}.tar.gz" \ --save-support-archive-path="/tmp/support-${CLUSTER_NAME}.zip" } trap destroy_cluster SIGINT EXIT build_iso_embedded create_cluster # wait for the Talos API to be up for i in $(seq 1 30); do if "${TALOSCTL}" -n "${NODE}" get disks --insecure &>/dev/null; then break fi sleep 1 done # verify that the config is applied "${TALOSCTL}" -n "${NODE}" get watchdogtimerstatus timer --insecure ================================================ FILE: hack/test/e2e-gcp-prepare.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh REGION="us-central1" ZONE="us-central1-a" PROJECT_ID="${GOOGLE_PROJECT_ID}" function cloud_image_upload() { CLOUD_IMAGES_EXTRA_ARGS=("--target-clouds=gcp" "--architectures=amd64") make cloud-images CLOUD_IMAGES_EXTRA_ARGS="${CLOUD_IMAGES_EXTRA_ARGS[*]}" } function get_image() { jq -r ".[] | select(.cloud == \"gcp\") | select (.arch == \"amd64\") | .id" "${ARTIFACTS}/cloud-images.json" } cloud_image_upload GCP_IMAGE=$(get_image) mkdir -p "${ARTIFACTS}/e2e-gcp-generated" NAME_PREFIX="talos-e2e-${SHA}-gcp" jq --null-input \ --arg REGION "${REGION}" \ --arg ZONE "${ZONE}" \ --arg PROJECT_ID "${PROJECT_ID}" \ --arg GCP_IMAGE "${GCP_IMAGE}" \ --arg CLUSTER_NAME "${NAME_PREFIX}" \ --arg TALOS_VERSION_CONTRACT "${TALOS_VERSION}" \ --arg KUBERNETES_VERSION "${KUBERNETES_VERSION}" \ '{ region: $REGION, zone: $ZONE, project_id: $PROJECT_ID, gcp_image: $GCP_IMAGE, cluster_name: $CLUSTER_NAME, talos_version_contract: $TALOS_VERSION_CONTRACT, kubernetes_version: $KUBERNETES_VERSION }' \ | jq -f hack/test/tfvars/gcp.jq > "${ARTIFACTS}/e2e-gcp-generated/vars.json" cp hack/test/tfvars/*.yaml "${ARTIFACTS}/e2e-gcp-generated" ================================================ FILE: hack/test/e2e-gcp.sh ================================================ #!/usr/bin/env bash set -eou pipefail source ./hack/test/e2e.sh cp "${ARTIFACTS}/e2e-gcp-talosconfig" "${TALOSCONFIG}" cp "${ARTIFACTS}/e2e-gcp-kubeconfig" "${KUBECONFIG}" # set the talosconfig to use the first controlplane ip CONTROLPLANE0_NODE=$(${TALOSCTL} config info -o json | jq -r '.endpoints[0]') ${TALOSCTL} config node "${CONTROLPLANE0_NODE}" run_talos_integration_test run_kubernetes_integration_test ================================================ FILE: hack/test/e2e-image-factory.sh ================================================ #!/usr/bin/env bash set -eou pipefail # shellcheck source=/dev/null source ./hack/test/e2e.sh PROVISIONER=qemu CLUSTER_NAME="e2e-${PROVISIONER}" LOG_ARCHIVE_SUFFIX="${GITHUB_STEP_NAME:-e2e-${PROVISIONER}}" FACTORY_HOSTNAME=${FACTORY_HOSTNAME:-factory.talos.dev} if [ "${FACTORY_BOOT_METHOD}" = "ipxe" ]; then FACTORY_HOSTNAME=${PXE_FACTORY_HOSTNAME:-pxe.${FACTORY_HOSTNAME}} fi FACTORY_SCHEME=${FACTORY_SCHEME:-https} INSTALLER_IMAGE_NAME=${INSTALLER_IMAGE_NAME:-installer} case "${FACTORY_BOOT_METHOD:-iso}" in iso) QEMU_FLAGS+=("--presets=iso") ;; disk-image) QEMU_FLAGS+=("--presets=disk-image") ;; ipxe) QEMU_FLAGS+=("--presets=pxe") ;; secureboot-iso) QEMU_FLAGS+=("--presets=iso-secureboot") INSTALLER_IMAGE_NAME=installer-secureboot ;; *) echo "unknown factory boot method: ${FACTORY_BOOT_METHOD}" exit 1 ;; esac function assert_secureboot { if [[ "${FACTORY_BOOT_METHOD:-iso}" != "secureboot-iso" ]]; then return fi ${TALOSCTL} get securitystate -o json ${TALOSCTL} get securitystate -o json | jq -e '.spec.secureBoot == true' } function create_cluster { build_registry_mirrors "${TALOSCTL}" cluster create qemu \ --name="${CLUSTER_NAME}" \ --kubernetes-version="${KUBERNETES_VERSION}" \ --controlplanes=3 \ --workers="${QEMU_WORKERS:-1}" \ --mtu=1430 \ --memory-controlplanes=2048 \ --memory-workers="${QEMU_MEMORY_WORKERS:-2048}" \ --cpus-controlplanes="${QEMU_CPUS:-2}" \ --cpus-workers="${QEMU_CPUS_WORKERS:-2}" \ --cidr=172.20.1.0/24 \ --image-factory-url="${FACTORY_SCHEME}://${FACTORY_HOSTNAME}/" \ --talos-version="${FACTORY_VERSION}" \ --schematic-id="${FACTORY_SCHEMATIC}" \ "${REGISTRY_MIRROR_FLAGS[@]}" \ "${QEMU_FLAGS[@]}" ${TALOSCTL} config node 172.20.1.2 } function destroy_cluster() { "${TALOSCTL}" cluster destroy \ --name "${CLUSTER_NAME}" \ --provisioner "${PROVISIONER}" \ --save-cluster-logs-archive-path="/tmp/logs-${LOG_ARCHIVE_SUFFIX}.tar.gz" \ --save-support-archive-path="/tmp/support-${LOG_ARCHIVE_SUFFIX}.zip" } trap destroy_cluster SIGINT EXIT create_cluster ${TALOSCTL} health --run-e2e ${TALOSCTL} version | grep "${FACTORY_VERSION}" ${TALOSCTL} get extensions | grep "${FACTORY_SCHEMATIC}" assert_secureboot if [[ "${FACTORY_UPGRADE:-false}" == "true" ]]; then ${TALOSCTL} upgrade -i "${FACTORY_HOSTNAME}/${INSTALLER_IMAGE_NAME}/${FACTORY_UPGRADE_SCHEMATIC:-$FACTORY_SCHEMATIC}:${FACTORY_UPGRADE_VERSION:-$FACTORY_VERSION}" ${TALOSCTL} version | grep "${FACTORY_UPGRADE_VERSION:-$FACTORY_VERSION}" ${TALOSCTL} get extensions | grep "${FACTORY_UPGRADE_SCHEMATIC:-$FACTORY_SCHEMATIC}" assert_secureboot fi ================================================ FILE: hack/test/e2e-iso.sh ================================================ #!/usr/bin/env bash set -eoux pipefail source ./hack/test/e2e.sh PROVISIONER=qemu CLUSTER_NAME=e2e-iso NODE="172.20.2.2" function create_cluster { build_registry_mirrors "${TALOSCTL}" cluster create \ --provisioner="${PROVISIONER}" \ --name="${CLUSTER_NAME}" \ --kubernetes-version=${KUBERNETES_VERSION} \ --iso-path=${ARTIFACTS}/metal-amd64.iso \ --controlplanes=1 \ --workers=0 \ --mtu=1430 \ --memory=2048 \ --cpus=2.0 \ --cidr=172.20.2.0/24 \ --with-apply-config \ --install-image="${INSTALLER_IMAGE}" \ --cni-bundle-url=${ARTIFACTS}/talosctl-cni-bundle-'${ARCH}'.tar.gz \ "${REGISTRY_MIRROR_FLAGS[@]}" "${TALOSCTL}" config node "${NODE}" } function destroy_cluster() { "${TALOSCTL}" cluster destroy \ --name "${CLUSTER_NAME}" \ --provisioner "${PROVISIONER}" \ --save-cluster-logs-archive-path="/tmp/logs-${CLUSTER_NAME}.tar.gz" \ --save-support-archive-path="/tmp/support-${CLUSTER_NAME}.zip" } trap destroy_cluster SIGINT EXIT create_cluster sleep 5 ================================================ FILE: hack/test/e2e-qemu.sh ================================================ #!/usr/bin/env bash set -eou pipefail # shellcheck source=/dev/null source ./hack/test/e2e.sh PROVISIONER=qemu CLUSTER_NAME="e2e-${PROVISIONER}" LOG_ARCHIVE_SUFFIX="${GITHUB_STEP_NAME:-e2e-${PROVISIONER}}" QEMU_FLAGS=() case "${CI:-false}" in false) QEMU_FLAGS+=("--with-bootloader=false") ;; *) ;; esac case "${CUSTOM_CNI_URL:-false}" in false) ;; *) QEMU_FLAGS+=("--custom-cni-url=${CUSTOM_CNI_URL}") ;; esac case "${WITH_UEFI:-none}" in none) ;; *) QEMU_FLAGS+=("--with-uefi=${WITH_UEFI}") ;; esac case "${WITH_VIRTUAL_IP:-false}" in true) QEMU_FLAGS+=("--use-vip") ;; esac case "${WITH_JSON_LOGS:-true}" in false) ;; *) QEMU_FLAGS+=("--with-json-logs") ;; esac case "${WITH_CLUSTER_DISCOVERY:-true}" in false) QEMU_FLAGS+=("--with-cluster-discovery=false" "--kubeprism-port=0") # disable both KubePrism and cluster discovery ;; esac case "${WITH_KUBESPAN:-false}" in true) QEMU_FLAGS+=("--with-kubespan") ;; esac case "${WITH_CONTROL_PLANE_PORT:-false}" in false) ;; *) QEMU_FLAGS+=("--control-plane-port=${WITH_CONTROL_PLANE_PORT}") ;; esac case "${VIA_MAINTENANCE_MODE:-false}" in false) ;; *) # apply config via maintenance mode QEMU_FLAGS+=("--skip-injecting-config" "--with-apply-config") ;; esac case "${DISABLE_DHCP_HOSTNAME:-false}" in false) ;; *) QEMU_FLAGS+=("--disable-dhcp-hostname") ;; esac case "${WITH_NETWORK_CHAOS:-false}" in false) ;; *) QEMU_FLAGS+=("--with-network-chaos" "--with-network-packet-loss=0.01" "--with-network-latency=15ms" "--with-network-jitter=5ms") ;; esac case "${WITH_FIREWALL:-false}" in false) ;; *) QEMU_FLAGS+=("--with-firewall=${WITH_FIREWALL}") ;; esac case "${USE_DISK_IMAGE:-false}" in false) ;; *) QEMU_FLAGS+=("--disk-image-path=_out/metal-amd64.raw.zst") ;; esac case "${WITH_DISK_ENCRYPTION:-false}" in false) ;; *) QEMU_FLAGS+=("--encrypt-ephemeral" "--encrypt-state" "--encrypt-user-volumes" "--disk-encryption-key-types=kms") ;; esac case "${WITH_CONFIG_PATCH:-false}" in false) ;; *) [[ ! ${WITH_CONFIG_PATCH} =~ ^@ ]] && echo "WITH_CONFIG_PATCH variable should start with @" && exit 1 for i in ${WITH_CONFIG_PATCH//:/ }; do QEMU_FLAGS+=("--config-patch=${i}") done ;; esac case "${WITH_ISO:-false}" in false) ;; *) QEMU_FLAGS+=("--iso-path=${ARTIFACTS}/metal-amd64.iso") ;; esac case "${WITH_CONFIG_PATCH_CONTROLPLANE:-false}" in false) ;; *) [[ ! ${WITH_CONFIG_PATCH_CONTROLPLANE} =~ ^@ ]] && echo "WITH_CONFIG_PATCH_CONTROLPLANE variable should start with @" && exit 1 for i in ${WITH_CONFIG_PATCH_CONTROLPLANE//:/ }; do QEMU_FLAGS+=("--config-patch-control-plane=${i}") done ;; esac case "${WITH_CONFIG_PATCH_WORKER:-false}" in false) ;; *) [[ ! ${WITH_CONFIG_PATCH_WORKER} =~ ^@ ]] && echo "WITH_CONFIG_PATCH_WORKER variable should start with @" && exit 1 for i in ${WITH_CONFIG_PATCH_WORKER//:/ }; do QEMU_FLAGS+=("--config-patch-worker=${i}") done ;; esac case "${WITH_CUSTOM_CNI:-none}" in false) ;; cilium) QEMU_FLAGS+=("--kubeprism-port=13336") ;; esac case "${WITH_TRUSTED_BOOT_ISO:-false}" in false) ;; *) INSTALLER_IMAGE=${INSTALLER_IMAGE}-amd64-secureboot QEMU_FLAGS+=("--iso-path=_out/metal-amd64-secureboot.iso" "--with-tpm2" "--encrypt-ephemeral" "--encrypt-state" "--encrypt-user-volumes" "--disk-encryption-key-types=tpm") ;; esac case "${WITH_TPM1_2:-false}" in false) ;; *) QEMU_FLAGS+=("--with-tpm1_2") ;; esac case "${WITH_SIDEROLINK_AGENT:-false}" in false) ;; *) QEMU_FLAGS+=("--with-siderolink=${WITH_SIDEROLINK_AGENT}") ;; esac case "${WITH_APPARMOR_LSM_ENABLED:-false}" in false) ;; *) # build disk image with specific kernel args to enable AppArmor LSM make image-metal PLATFORM=linux/amd64 IMAGER_ARGS="--extra-kernel-arg -selinux --extra-kernel-arg lsm=lockdown,capability,yama,apparmor,bpf --extra-kernel-arg apparmor=1" QEMU_FLAGS+=("--disk-image-path=_out/metal-amd64.raw.zst" "--skip-injecting-config" "--with-apply-config") ;; esac case "${WITH_CONFIG_INJECTION_METHOD:-default}" in default) ;; *) QEMU_FLAGS+=("--config-injection-method=${WITH_CONFIG_INJECTION_METHOD}") ;; esac case "${WITH_IOMMU:-false}" in false) ;; *) QEMU_FLAGS+=("--with-iommu") ;; esac case "${WITH_4K_DISK:-false}" in false) ;; *) QEMU_FLAGS+=("--disk-block-size=4096") ;; esac case "${WITH_UKI_BOOT:-false}" in false) ;; *) QEMU_FLAGS+=("--uki-path=_out/metal-amd64-uki.efi") ;; esac case "${WITH_USER_DISK:-false}" in false) ;; *) QEMU_FLAGS+=("--user-volumes=extra:350MB") QEMU_FLAGS+=("--user-volumes=p1:350MB:p2:350MB") ;; esac case "${WITH_ENFORCING:-false}" in false) ;; *) QEMU_FLAGS+=("--extra-boot-kernel-args=enforcing=1") ;; esac case "${WITH_AIRGAPPED:-false}" in no-proxy) INSTALLER_IMAGE="${INSTALLER_IMAGE/registry.dev.siderolabs.io/172.20.1.1:5000}" QEMU_FLAGS+=("--config-patch=@hack/test/patches/airgapped-timesync.yaml") QEMU_FLAGS+=("--config-patch=@${TMP}/image-cache-patch.yaml") QEMU_FLAGS+=("--airgapped") QEMU_FLAGS+=("--image-cache-path=${TMP}/image-cache") QEMU_FLAGS+=("--image-cache-tls-cert-file=${TMP}/image-cache-tls.crt") QEMU_FLAGS+=("--image-cache-tls-key-file=${TMP}/image-cache-tls.key") ;; http-proxy) "${TALOSCTL}" debug-tool air-gapped --advertised-address 172.20.1.1 > /tmp/airgapped.log 2>&1 & sleep 5 # wait for the air-gapped server to start cat air-gapped-patch.yaml mv air-gapped-patch.yaml "${TMP}/air-gapped-patch.yaml" QEMU_FLAGS+=("--config-patch=@${TMP}/air-gapped-patch.yaml") ;; secure-http-proxy) "${TALOSCTL}" debug-tool air-gapped --advertised-address 172.20.1.1 --use-secure-proxy > /tmp/airgapped-secure.log 2>&1 & sleep 5 # wait for the air-gapped server to start cat air-gapped-patch.yaml mv air-gapped-patch.yaml "${TMP}/air-gapped-patch.yaml" QEMU_FLAGS+=("--config-patch=@${TMP}/air-gapped-patch.yaml") ;; https-reverse-proxy) "${TALOSCTL}" debug-tool air-gapped --advertised-address 172.20.1.1 --inject-http-proxy=false --https-reverse-proxy-target=https://registry.dev.siderolabs.io > /tmp/airgapped-reverse-proxy.log 2>&1 & sleep 5 # wait for the air-gapped server to start cat air-gapped-patch.yaml mv air-gapped-patch.yaml "${TMP}/air-gapped-patch.yaml" QEMU_FLAGS+=("--config-patch=@${TMP}/air-gapped-patch.yaml") ;; false) ;; esac function create_cluster { build_registry_mirrors "${TALOSCTL}" cluster create \ --provisioner="${PROVISIONER}" \ --name="${CLUSTER_NAME}" \ --kubernetes-version="${KUBERNETES_VERSION}" \ --controlplanes=3 \ --workers="${QEMU_WORKERS:-2}" \ --disk="${QEMU_SYSTEM_DISK_SIZE:-15360}" \ --extra-disks="${QEMU_EXTRA_DISKS:-0}" \ --extra-disks-size="${QEMU_EXTRA_DISKS_SIZE:-6144}" \ --extra-disks-drivers="${QEMU_EXTRA_DISKS_DRIVERS:-}" \ --extra-disks-serials="${QEMU_EXTRA_DISKS_SERIALS:-}" \ --extra-disks-tags="${QEMU_EXTRA_DISKS_TAGS:-}" \ --mtu=1430 \ --memory="${QEMU_MEMORY_CONTROLPLANES:-2048}" \ --memory-workers="${QEMU_MEMORY_WORKERS:-2048}" \ --cpus="${QEMU_CPUS:-2}" \ --cpus-workers="${QEMU_CPUS_WORKERS:-2}" \ --cidr=172.20.1.0/24 \ --install-image="${INSTALLER_IMAGE}" \ --with-init-node=false \ --cni-bundle-url="${ARTIFACTS}/talosctl-cni-bundle-\${ARCH}.tar.gz" \ "${REGISTRY_MIRROR_FLAGS[@]}" \ "${QEMU_FLAGS[@]}" "${TALOSCTL}" config node 172.20.1.2 } function destroy_cluster() { jobs -p | xargs -r kill "${TALOSCTL}" cluster destroy \ --name "${CLUSTER_NAME}" \ --provisioner "${PROVISIONER}" \ --save-cluster-logs-archive-path="/tmp/logs-${LOG_ARCHIVE_SUFFIX}.tar.gz" \ --save-support-archive-path="/tmp/support-${LOG_ARCHIVE_SUFFIX}.zip" } trap destroy_cluster SIGINT EXIT create_cluster case "${WITH_CUSTOM_CNI:-none}" in cilium) install_and_run_cilium_cni_tests ;; *) ;; esac case "${TEST_MODE:-default}" in fast-conformance) run_kubernetes_conformance_test fast ;; *) get_kubeconfig run_talos_integration_test if [[ ${QEMU_MEMORY_WORKERS:-2048} -gt 1024 ]]; then run_kubernetes_integration_test fi if [ "${TEST_MODE:-default}" = "network-policy" ]; then run_kubernetes_conformance_test network-policy fi if [ "${WITH_TEST:-none}" != "none" ]; then "${WITH_TEST}" fi ;; esac ================================================ FILE: hack/test/e2e.sh ================================================ #!/usr/bin/env bash # This file contains common environment variables and setup logic for all test # scripts. It assumes that the following environment variables are set by the # Makefile: # - PLATFORM # - TAG # - SHA # - REGISTRY # - IMAGE # - INSTALLER_IMAGE # - ARTIFACTS # - TALOSCTL # - INTEGRATION_TEST # - SHORT_INTEGRATION_TEST # - CUSTOM_CNI_URL # - KUBECTL # - KUBESTR # - HELM # - CILIUM_CLI set -eoux pipefail TMP="/tmp/e2e/${PLATFORM}" mkdir -p "${TMP}" # Talos export TALOSCONFIG="${TMP}/talosconfig" TALOS_VERSION=$(cut -d "." -f 1,2 <<< "${TAG}") export TALOS_VERSION # Kubernetes export KUBECONFIG="${TMP}/kubeconfig" export KUBERNETES_VERSION=${KUBERNETES_VERSION:-1.36.0-alpha.2} export NAME_PREFIX="talos-e2e-${SHA}-${PLATFORM}" export TIMEOUT=1200 # default values, overridden by talosctl cluster create tests PROVISIONER= CLUSTER_NAME= TEST_SHORT=() TEST_RUN=("-test.run" ".") function run_talos_integration_test { case "${SHORT_INTEGRATION_TEST:-no}" in no) ;; *) TEST_SHORT=("-test.short") ;; esac case "${WITH_AIRGAPPED:-false}" in no-proxy) TEST_AIRGAPPED=("-talos.airgapped") ;; *) ;; esac case "${INTEGRATION_TEST_RUN:-no}" in no) ;; *) TEST_RUN=("-test.run" "${INTEGRATION_TEST_RUN}") ;; esac if [ -n "${QEMU_EXTRA_DISKS_TAGS:-}" ]; then TEST_VIRTIOFSD=("-talos.virtiofsd") fi "${INTEGRATION_TEST}" \ -test.v \ -talos.failfast \ -talos.talosctlpath "${TALOSCTL}" \ -talos.kubectlpath "${KUBECTL}" \ -talos.helmpath "${HELM}" \ -talos.kubestrpath "${KUBESTR}" \ -talos.provisioner "${PROVISIONER}" \ -talos.name "${CLUSTER_NAME}" \ -talos.image "${REGISTRY}/siderolabs/talos" \ ${EXTRA_TEST_ARGS:-} \ "${TEST_RUN[@]}" \ "${TEST_SHORT[@]}" \ "${TEST_AIRGAPPED[@]}" \ "${TEST_VIRTIOFSD[@]}" } function run_talos_integration_test_docker { case "${SHORT_INTEGRATION_TEST:-no}" in no) ;; *) TEST_SHORT=("-test.short") ;; esac case "${INTEGRATION_TEST_RUN:-no}" in no) ;; *) TEST_RUN=("-test.run" "${INTEGRATION_TEST_RUN}") ;; esac "${INTEGRATION_TEST}" \ -test.v \ -talos.failfast \ -talos.talosctlpath "${TALOSCTL}" \ -talos.kubectlpath "${KUBECTL}" \ -talos.helmpath "${HELM}" \ -talos.kubestrpath "${KUBESTR}" \ -talos.provisioner "${PROVISIONER}" \ -talos.name "${CLUSTER_NAME}" \ -talos.image "${REGISTRY}/siderolabs/talos" \ ${EXTRA_TEST_ARGS:-} \ "${TEST_RUN[@]}" \ "${TEST_SHORT[@]}" } function run_kubernetes_conformance_test { "${TALOSCTL}" conformance kubernetes --mode="${1}" } function run_kubernetes_integration_test { "${TALOSCTL}" health --run-e2e } function run_control_plane_cis_benchmark { ${KUBECTL} apply -f "${PWD}/hack/test/cis/kube-bench-master.yaml" ${KUBECTL} wait --timeout=300s --for=condition=complete job/kube-bench-master > /dev/null ${KUBECTL} logs job/kube-bench-master } function run_worker_cis_benchmark { ${KUBECTL} apply -f "${PWD}/hack/test/cis/kube-bench-node.yaml" ${KUBECTL} wait --timeout=300s --for=condition=complete job/kube-bench-node > /dev/null ${KUBECTL} logs job/kube-bench-node } function get_kubeconfig { rm -f "${TMP}/kubeconfig" "${TALOSCTL}" kubeconfig "${TMP}" } function dump_cluster_state { nodes=$(${KUBECTL} get nodes -o jsonpath="{.items[*].status.addresses[?(@.type == 'InternalIP')].address}" | tr '[:space:]' ',') "${TALOSCTL}" -n "${nodes}" services ${KUBECTL} get nodes -o wide ${KUBECTL} get pods --all-namespaces -o wide } function build_image_cache { cat _out/integration-images.txt | "${TALOSCTL}" image cache-create --images=- --image-cache-path="${TMP}/image-cache" --layout=flat "${TALOSCTL}" image cache-cert-gen \ --tls-ca-file="${TMP}/image-cache-ca.crt" \ --tls-cert-file="${TMP}/image-cache-tls.crt" \ --tls-key-file="${TMP}/image-cache-tls.key" \ --advertised-address="172.20.1.1" cat image-cache-patch.yaml mv image-cache-patch.yaml "${TMP}/image-cache-patch.yaml" } function build_registry_mirrors { if [[ "${WITH_AIRGAPPED:-false}" == "no-proxy" ]]; then build_image_cache REGISTRY_MIRROR_FLAGS=() for registry in docker.io registry.k8s.io quay.io gcr.io ghcr.io; do addr="172.20.1.1" REGISTRY_MIRROR_FLAGS+=("--registry-mirror=${registry}=https://${addr}:5000") done return fi if [[ "${REGISTRY_MIRROR_FLAGS:-yes}" == "no" ]]; then REGISTRY_MIRROR_FLAGS=() return fi if [[ "${CI:-false}" == "true" ]]; then REGISTRY_MIRROR_FLAGS=() for registry in docker.io registry.k8s.io quay.io gcr.io ghcr.io; do local service="registry-${registry//./-}.ci.svc" addr=$(python3 -c "import socket; print(socket.gethostbyname('${service}'))") REGISTRY_MIRROR_FLAGS+=("--registry-mirror=${registry}=http://${addr}:5000") done fi } function install_and_run_cilium_cni_tests { get_kubeconfig case "${WITH_KUBESPAN:-false}" in true) CILIUM_NODE_ENCRYPTION=false CILIUM_TEST_EXTRA_ARGS=("--test=!node-to-node-encryption,!check-log-errors,!pod-to-pod-encryption-v2") ;; *) CILIUM_NODE_ENCRYPTION=true CILIUM_TEST_EXTRA_ARGS=("--test=!check-log-errors") ;; esac case "${CILIUM_INSTALL_TYPE:-none}" in strict) ${CILIUM_CLI} install \ --set=ipam.mode=kubernetes \ --set=kubeProxyReplacement=true \ --set=encryption.nodeEncryption=${CILIUM_NODE_ENCRYPTION} \ --set=securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \ --set=securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \ --set=cgroup.autoMount.enabled=false \ --set=cgroup.hostRoot=/sys/fs/cgroup \ --set=k8sServiceHost=localhost \ --set=k8sServicePort=13336 ;; *) # explicitly setting kubeProxyReplacement=disabled since by the time cilium cli runs talos # has not yet applied the kube-proxy manifests ${CILIUM_CLI} install \ --set=ipam.mode=kubernetes \ --set=kubeProxyReplacement=false \ --set=encryption.nodeEncryption=${CILIUM_NODE_ENCRYPTION} \ --set=securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \ --set=securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \ --set=cgroup.autoMount.enabled=false \ --set=cgroup.hostRoot=/sys/fs/cgroup ;; esac ${CILIUM_CLI} status --wait --wait-duration=10m # ref: https://github.com/cilium/cilium-cli/releases/tag/v0.16.14 ${KUBECTL} delete ns --ignore-not-found cilium-test-1 cilium-test-ccnp1 cilium-test-ccnp2 ${KUBECTL} create ns cilium-test-1 ${KUBECTL} create ns cilium-test-ccnp1 ${KUBECTL} create ns cilium-test-ccnp2 ${KUBECTL} label ns cilium-test-1 cilium-test-ccnp1 cilium-test-ccnp2 pod-security.kubernetes.io/enforce=privileged # --external-target added, as default 'one.one.one.one' is buggy, and CloudFlare status is of course "all healthy" ${CILIUM_CLI} connectivity test --test-namespace cilium-test --external-target google.com --timeout=20m "${CILIUM_TEST_EXTRA_ARGS[@]}" ${KUBECTL} delete ns cilium-test-1 cilium-test-ccnp1 cilium-test-ccnp2 } ================================================ FILE: hack/test/patches/airgapped-timesync.yaml ================================================ machine: time: servers: - /dev/ptp0 ================================================ FILE: hack/test/patches/cilium-kubeproxy.yaml ================================================ cluster: network: cni: name: none proxy: disabled: true ================================================ FILE: hack/test/patches/cilium-no-kubeproxy.yaml ================================================ cluster: network: cni: name: none ================================================ FILE: hack/test/patches/dm-raid-module.yaml ================================================ machine: kernel: modules: - name: dm_raid ================================================ FILE: hack/test/patches/ephemeral-min-max.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL provisioning: diskSelector: match: system_disk minSize: 4GB maxSize: 100GB ================================================ FILE: hack/test/patches/ephemeral-nvme.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL provisioning: diskSelector: match: disk.transport == 'nvme' minSize: 3GB maxSize: 5GB --- apiVersion: v1alpha1 kind: SwapVolumeConfig name: swap1 provisioning: diskSelector: match: disk.transport == 'nvme' minSize: 500MB maxSize: 500MB --- machine: kubelet: extraConfig: memorySwap: swapBehavior: LimitedSwap --- apiVersion: v1alpha1 kind: ZswapConfig maxPoolPercent: 25 shrinkerEnabled: true ================================================ FILE: hack/test/patches/extensions.yaml ================================================ machine: sysctls: user.max_user_namespaces: "11255" kernel: modules: - name: asix - name: ax88179_178a - name: ax88796b - name: binfmt_misc - name: btrfs - name: cdc_ether - name: cdc_mbim - name: cdc_ncm - name: cdc_subset - name: cdc_wdm - name: cxgb - name: cxgb3 - name: cxgb4 - name: cxgb4vf - name: drbd - name: ena - name: gasket - name: net1080 - name: option - name: qmi_wwan - name: r8153_ecm - name: thunderbolt - name: thunderbolt_net - name: usb_wwan - name: usbnet - name: usbserial - name: xdma - name: zaurus - name: zfs --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: tailscale environment: - TS_AUTHKEY=tskey-0000000000000000 --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: nut-client configFiles: - content: |- MONITOR ${upsmonHost} 1 remote ${upsmonPasswd} slave SHUTDOWNCMD "/sbin/poweroff" mountPath: /usr/local/etc/nut/upsmon.conf --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: lldpd configFiles: - content: | configure lldpd portidsubtype ifname unconfigure lldp management-addresses-advertisements unconfigure lldp capabilities-advertisements configure system description "Talos Node" mountPath: /usr/local/etc/lldpd/lldpd.conf --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: bird2 configFiles: - mountPath: /usr/local/etc/bird.conf content: | # This is just a minimal NOOP config! log stderr all; router id 6.6.6.6; protocol device {} ================================================ FILE: hack/test/patches/flannel-netpol.yaml ================================================ # enable network policies in flannel CNI plugin # see PR: https://github.com/siderolabs/talos/pull/12590 cluster: network: cni: name: flannel flannel: kubeNetworkPoliciesEnabled: true ================================================ FILE: hack/test/patches/image-cache-encrypted.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: IMAGECACHE encryption: provider: luks2 keys: - slot: 0 static: passphrase: imagesecret ================================================ FILE: hack/test/patches/image-cache.yaml ================================================ machine: features: imageCache: localEnabled: true --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: '*' skipFallback: true endpoints: - url: http://172.20.0.251:65000 --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: k8s.gcr.io skipFallback: true endpoints: - url: http://172.20.0.251:65000 --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: registry.k8s.io skipFallback: true endpoints: - url: http://172.20.0.251:65000 --- apiVersion: v1alpha1 kind: VolumeConfig name: IMAGECACHE provisioning: diskSelector: match: 'system_disk' maxSize: 3GiB grow: true ================================================ FILE: hack/test/patches/image-verification.yaml ================================================ apiVersion: v1alpha1 kind: ImageVerificationConfig rules: - image: registry.k8s.io/e2e-test-images/* skip: true # skip verification for e2e test images since they are not signed - image: registry.k8s.io/* keyless: issuer: https://accounts.google.com subject: krel-trust@k8s-releng-prod.iam.gserviceaccount.com - image: ghcr.io/siderolabs/* keyless: issuer: https://accounts.google.com subjectRegex: '(@siderolabs\.com$|^releasemgr-svc@talos-production\.iam\.gserviceaccount\.com$)' ================================================ FILE: hack/test/patches/longhorn-cp.yaml ================================================ --- cluster: apiServer: admissionControl: - name: PodSecurity configuration: exemptions: namespaces: - longhorn-system ================================================ FILE: hack/test/patches/longhorn.yaml ================================================ --- machine: sysctls: vm.nr_hugepages: "1024" kernel: modules: - name: nvme_tcp - name: vfio_pci - name: uio_pci_generic kubelet: extraMounts: - destination: /var/lib/longhorn type: bind source: /var/lib/longhorn options: - bind - rshared - rw ================================================ FILE: hack/test/patches/node-address-v2.yaml ================================================ machine: features: nodeAddressSortAlgorithm: v2 ================================================ FILE: hack/test/patches/openebs-cp.yaml ================================================ --- cluster: apiServer: admissionControl: - name: PodSecurity configuration: exemptions: namespaces: - openebs ================================================ FILE: hack/test/patches/openebs.yaml ================================================ --- machine: sysctls: vm.nr_hugepages: "1024" nodeLabels: openebs.io/engine: "mayastor" kubelet: extraMounts: - destination: /var/local type: bind source: /var/local options: - bind - rshared - rw ================================================ FILE: hack/test/patches/proxied-registry.yaml ================================================ apiVersion: v1alpha1 kind: RegistryMirrorConfig name: registry.dev.siderolabs.io skipFallback: true endpoints: - url: https://172.20.1.1:8004 ================================================ FILE: hack/test/patches/rook-ceph.yaml ================================================ cluster: apiServer: admissionControl: - name: PodSecurity configuration: exemptions: namespaces: - rook-ceph ================================================ FILE: hack/test/patches/usernamespace.yaml ================================================ --- cluster: apiServer: extraArgs: feature-gates: UserNamespacesSupport=true machine: sysctls: user.max_user_namespaces: "11255" kubelet: extraConfig: featureGates: UserNamespacesSupport: true ================================================ FILE: hack/test/patches/watchdog.yaml ================================================ apiVersion: v1alpha1 kind: WatchdogTimerConfig device: /dev/watchdog0 timeout: 2m0s ================================================ FILE: hack/test/provision-tests.sh ================================================ #!/usr/bin/env bash set -eoux pipefail INTEGRATION_TEST_FLAGS=() case "${CI:-false}" in true) MIRROR_FLAG=() for registry in docker.io k8s.gcr.io registry.k8s.io quay.io gcr.io ghcr.io; do service="registry-${registry//./-}.ci.svc" addr=$(python3 -c "import socket; print(socket.gethostbyname('${service}'))") MIRROR_FLAG+=("${registry}=http://${addr}:5000") done MIRROR_FLAGS="${MIRROR_FLAG[*]}" INTEGRATION_TEST_FLAGS+=("-talos.provision.target-installer-registry=${REGISTRY}" "-talos.provision.registry-mirror=${MIRROR_FLAGS// /,}") ;; *) ;; esac if [ "${INTEGRATION_TEST_RUN:-undefined}" != "undefined" ]; then INTEGRATION_TEST_FLAGS+=("-test.run=${INTEGRATION_TEST_RUN}") fi if [ "${INTEGRATION_TEST_TRACK:-undefined}" != "undefined" ]; then INTEGRATION_TEST_FLAGS+=("-talos.provision.cidr=172.$(( INTEGRATION_TEST_TRACK + 21 )).0.0/24") fi case "${CUSTOM_CNI_URL:-false}" in false) ;; *) INTEGRATION_TEST_FLAGS+=("-talos.provision.custom-cni-url=${CUSTOM_CNI_URL}") ;; esac "${INTEGRATION_TEST}" -test.v \ -talos.talosctlpath "${TALOSCTL}" \ -talos.provision.mtu 1430 \ -talos.provision.cni-bundle-url "${ARTIFACTS}/talosctl-cni-bundle-\${ARCH}.tar.gz" \ "${INTEGRATION_TEST_FLAGS[@]}" ================================================ FILE: hack/test/tfvars/aws.jq ================================================ { "cluster_name": .cluster_name, "ccm": true, "talos_version_contract": .talos_version_contract, "kubernetes_version": .kubernetes_version, "control_plane": { "ami_id": .ami_id, "instance_type": "t3.large" }, "worker_groups": (if .worker_group == "nvidia" then [ { "name": "nvidia-t4", "ami_id": .nvidia_ami_id, "instance_type": "g4dn.xlarge", "config_patch_files": [ "nvidia.yaml" ], "tags": { "Type": "nvidia-t4" } } ] else [ { "name": "default", "num_instances": 3, "ami_id": .ami_id, "instance_type": "t3.large" } ] end), "extra_tags": { "ClusterName": .cluster_name, "Project": "talos-e2e-ci", "Environment": "ci" } } ================================================ FILE: hack/test/tfvars/azure.jq ================================================ { "cluster_name": .cluster_name, "talos_version_contract": .talos_version_contract, "kubernetes_version": .kubernetes_version, "azure_location": "eastus", "control_plane": { "vm_os_id": .vm_os_id, "vm_size": "Standard_B2s" }, "worker_groups": [ { "name": "default", "vm_os_id": .vm_os_id, "vm_size": "Standard_B2s" } ], "extra_tags": { "Cluster Name": .cluster_name, "Project": "talos-e2e-ci", "Environment": "ci" } } ================================================ FILE: hack/test/tfvars/gcp.jq ================================================ { "project": .project_id, "region": .region, "zone": .zone, "cluster_name": .cluster_name, "talos_version_contract": .talos_version_contract, "kubernetes_version": .kubernetes_version, "control_plane": { "image": .gcp_image, } } ================================================ FILE: hack/test/tfvars/nvidia.yaml ================================================ machine: kernel: modules: - name: nvidia - name: nvidia_uvm - name: nvidia_drm - name: nvidia_modeset ================================================ FILE: hack/udevd/40-vm-hotadd.rules ================================================ SUBSYSTEM=="cpu", ACTION=="add", TEST=="online", ATTR{online}!="1", ATTR{online}="1" ================================================ FILE: hack/udevd/90-selinux.rules ================================================ SUBSYSTEM=="rtc",SECLABEL{selinux}="system_u:object_r:rtc_device_t:s0" SUBSYSTEM=="mtd",SECLABEL{selinux}="system_u:object_r:mtd_device_t:s0" SUBSYSTEM=="tpm",SECLABEL{selinux}="system_u:object_r:tpm_device_t:s0" SUBSYSTEM=="tpmrm",SECLABEL{selinux}="system_u:object_r:tpm_device_t:s0" KERNEL=="watchdog",SECLABEL{selinux}="system_u:object_r:wdt_device_t:s0" KERNEL=="watchdog*",SECLABEL{selinux}="system_u:object_r:wdt_device_t:s0" KERNEL=="null",SECLABEL{selinux}="system_u:object_r:null_device_t:s0" KERNEL=="zero",SECLABEL{selinux}="system_u:object_r:null_device_t:s0" ================================================ FILE: hack/udevd/99-default.link ================================================ # SPDX-License-Identifier: MIT-0 # # This config file is installed as part of systemd. # It may be freely copied and edited (following the MIT No Attribution license). # # To make local modifications, use "networkctl edit". See networkctl(1) for details. # This file should not be edited in place, because it'll be overwritten on upgrades. [Match] OriginalName=* [Link] NamePolicy=keep kernel database onboard slot path mac AlternativeNamesPolicy=database onboard slot path mac MACAddressPolicy=none ================================================ FILE: internal/app/apid/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package apid import ( "context" "log" "github.com/siderolabs/go-debug" ) func runDebugServer(ctx context.Context) { const debugAddr = ":9981" debugLogFunc := func(msg string) { log.Print(msg) } if err := debug.ListenAndServe(ctx, debugAddr, debugLogFunc); err != nil { log.Fatalf("failed to start debug server: %s", err) } } ================================================ FILE: internal/app/apid/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package apid implements apid functionality. package apid import ( "context" "flag" "fmt" "log" "os/signal" "syscall" "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/client" "github.com/siderolabs/gen/panicsafe" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/startup" ) // Main is the entrypoint of apid. func Main() { if err := apidMain(); err != nil { log.Fatal(err) } } // apidMain is the entrypoint of apid. // // It fetches service config as a resource and keeps watching it // for changes. // // If the service config changes, it shuts down the listener and starts a new one with the new configuration. // //nolint:gocyclo func apidMain() error { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer cancel() log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime) flag.Parse() go runDebugServer(ctx) startup.LimitMaxProcs(constants.ApidMaxProcs) runtimeConn, err := grpc.NewClient("unix://"+constants.APIRuntimeSocketPath, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithNoProxy(), ) if err != nil { return fmt.Errorf("failed to dial runtime connection: %w", err) } stateClient := v1alpha1.NewStateClient(runtimeConn) resources := state.WrapCore(client.NewAdapter(stateClient)) configWatchCh := make(chan safe.WrappedStateEvent[*runtime.APIServiceConfig]) if err = safe.StateWatch(ctx, resources, runtime.NewAPIServiceConfig().Metadata(), configWatchCh); err != nil { return fmt.Errorf("failed to set up watch for API service config: %w", err) } var ( serviceConfig *runtime.APIServiceConfig cancelService context.CancelFunc ) serviceErrCh := make(chan error, 1) outerLoop: for { select { case <-ctx.Done(): break outerLoop case err = <-serviceErrCh: cancelService = nil if err != nil { return fmt.Errorf("service error: %w", err) } case event := <-configWatchCh: switch event.Type() { case state.Created, state.Updated: serviceConfig, err = event.Resource() if err != nil { return fmt.Errorf("failed to get API service config from watch event: %w", err) //nolint:govet } case state.Destroyed: serviceConfig = nil case state.Errored: return fmt.Errorf("service config watch error: %w", event.Error()) case state.Bootstrapped, state.Noop: // ignore continue outerLoop } } // we got a change in the service config, restart the server with the new config if cancelService != nil { cancelService() cancelService = nil // wait for the service to shut down <-serviceErrCh } if serviceConfig != nil { var serviceCtx context.Context serviceCtx, cancelService = context.WithCancel(ctx) //nolint:govet go func() { serviceErrCh <- panicsafe.RunErr(func() error { return runService(serviceCtx, resources, serviceConfig) }) }() } } if cancelService != nil { cancelService() <-serviceErrCh } return nil } ================================================ FILE: internal/app/apid/pkg/backend/apid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend import ( "context" "crypto/tls" "fmt" "slices" "sync" "time" "github.com/siderolabs/grpc-proxy/proxy" "github.com/siderolabs/net" "google.golang.org/grpc" "google.golang.org/grpc/backoff" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/credentials" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "google.golang.org/protobuf/encoding/protowire" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/proto" ) // GracefulShutdownTimeout is the timeout for graceful shutdown of the backend connection. // // Talos has a few long-running API calls, so we need to give the backend some time to finish them. // // The connection will enter IDLE time after GracefulShutdownTimeout/2, if no RPC is running. const GracefulShutdownTimeout = 30 * time.Minute var _ proxy.Backend = (*APID)(nil) // APID backend performs proxying to another apid instance. // // Backend authenticates itself using given grpc credentials. type APID struct { target string tlsConfigProvider func() (*tls.Config, error) mu sync.Mutex conn *grpc.ClientConn } // NewAPID creates new instance of APID backend. func NewAPID(target string, tlsConfigProvider func() (*tls.Config, error)) (*APID, error) { // perform very basic validation on target, trying to weed out empty addresses or addresses with the port appended if target == "" || net.AddressContainsPort(target) { return nil, fmt.Errorf("invalid target %q", target) } return &APID{ target: target, tlsConfigProvider: tlsConfigProvider, }, nil } func (a *APID) String() string { return a.target } // GetConnection returns a grpc connection to the backend. func (a *APID) GetConnection(ctx context.Context, _ string) (context.Context, *grpc.ClientConn, error) { md, _ := metadata.FromIncomingContext(ctx) md = md.Copy() authz.SetMetadata(md, authz.GetRoles(ctx)) if authority := md[":authority"]; len(authority) > 0 { md.Set("proxyfrom", authority...) } else { md.Set("proxyfrom", "unknown") } delete(md, ":authority") delete(md, "nodes") delete(md, "node") outCtx := metadata.NewOutgoingContext(ctx, md) a.mu.Lock() defer a.mu.Unlock() if a.conn != nil { return outCtx, a.conn, nil } tlsConfig, err := a.tlsConfigProvider() if err != nil { return outCtx, nil, err } // override max delay to avoid excessive backoff when the another node is unavailable (e.g. rebooted), // and apid used as an endpoint considers another node to be down for longer than expected. // // default max delay is 2 minutes, which is too long for our use case. backoffConfig := backoff.DefaultConfig backoffConfig.MaxDelay = 15 * time.Second a.conn, err = grpc.NewClient( fmt.Sprintf("%s:%d", net.FormatAddress(a.target), constants.ApidPort), grpc.WithInitialWindowSize(65535*32), grpc.WithInitialConnWindowSize(65535*16), grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), grpc.WithIdleTimeout(GracefulShutdownTimeout/2), // use half of the shutdown timeout as idle timeout grpc.WithConnectParams(grpc.ConnectParams{ Backoff: backoffConfig, // not published as a constant in gRPC library // see: https://github.com/grpc/grpc-go/blob/d5dee5fdbdeb52f6ea10b37b2cc7ce37814642d7/clientconn.go#L55-L56 MinConnectTimeout: 20 * time.Second, }), grpc.WithDefaultCallOptions( grpc.MaxCallRecvMsgSize(constants.GRPCMaxMessageSize), grpc.ForceCodecV2(proxy.Codec()), ), grpc.WithSharedWriteBuffer(true), ) return outCtx, a.conn, err } // AppendInfo is called to enhance response from the backend with additional data. // // AppendInfo enhances upstream response with node metadata (target). // // This method depends on grpc protobuf response structure, each response should // look like: // // message SomeResponse { // repeated SomeReply messages = 1; // please note field ID == 1 // } // // message SomeReply { // common.Metadata metadata = 1; // // } // // As 'SomeReply' is repeated in 'SomeResponse', if we concatenate protobuf representation // of several 'SomeResponse' messages, we still get valid 'SomeResponse' representation but with more // entries (feature of protobuf binary representation). // // If we look at binary representation of any unary 'SomeResponse' message, it will always contain one // protobuf field with field ID 1 (see above) and type 2 (embedded message SomeReply is encoded // as string with length). So if we want to add fields to 'SomeReply', we can simply read field // header, adjust length for new 'SomeReply' representation, and prepend new field header. // // At the same time, we can add 'common.Metadata' structure to 'SomeReply' by simply // appending or prepending 'common.Metadata' as a single field. This requires 'metadata' // field to be not defined in original response. (This is due to the fact that protobuf message // representation is concatenation of each field representation). // // To build only single field (Metadata) we use helper message which contains exactly this // field with same field ID as in every other 'SomeReply': // // message Empty { // common.Metadata metadata = 1; // } // // As streaming replies are not wrapped into 'SomeResponse' with 'repeated', handling is simpler: we just // need to append Empty with details. // // So AppendInfo does the following: validates that response contains field ID 1 encoded as string, // cuts field header, rest is representation of some reply. Marshal 'Empty' as protobuf, // which builds 'common.Metadata' field, append it to original response message, build new header // for new length of some response, and add back new field header. func (a *APID) AppendInfo(streaming bool, resp []byte) ([]byte, error) { payload, err := proto.Marshal(&common.Empty{ Metadata: &common.Metadata{ Hostname: a.target, }, }) if streaming { return append(resp, payload...), err } const ( metadataField = 1 // field number in proto definition for repeated response metadataType = 2 // "string" for embedded messages ) // decode protobuf embedded header typ, n1 := protowire.ConsumeVarint(resp) if n1 < 0 { return nil, protowire.ParseError(n1) } _, n2 := protowire.ConsumeVarint(resp[n1:]) // length if n2 < 0 { return nil, protowire.ParseError(n2) } if typ != (metadataField<<3)|metadataType { return nil, fmt.Errorf("unexpected message format: %d", typ) } if n1+n2 > len(resp) { return nil, fmt.Errorf("unexpected message size: %d", len(resp)) } // cut off embedded message header resp = resp[n1+n2:] // build new embedded message header prefix := protowire.AppendVarint( protowire.AppendVarint(nil, (metadataField<<3)|metadataType), uint64(len(resp)+len(payload)), ) return slices.Concat(prefix, resp, payload), err } // BuildError is called to convert error from upstream into response field. // // BuildError converts upstream error into message from upstream, so that multiple // successful and failure responses might be returned. // // This simply relies on the fact that any response contains 'Empty' message. // So if 'Empty' is unmarshalled into any other reply message, all the fields // are undefined but 'Metadata': // // message Empty { // common.Metadata metadata = 1; // } // // message EmptyResponse { // repeated Empty messages = 1; // } // // Streaming responses are not wrapped into Empty, so we simply marshall EmptyResponse // message. func (a *APID) BuildError(streaming bool, err error) ([]byte, error) { var resp proto.Message = &common.Empty{ Metadata: &common.Metadata{ Hostname: a.target, Error: err.Error(), Status: status.Convert(err).Proto(), }, } if !streaming { resp = &common.EmptyResponse{ Messages: []*common.Empty{ resp.(*common.Empty), }, } } return proto.Marshal(resp) } // Close connection. func (a *APID) Close() { a.mu.Lock() defer a.mu.Unlock() if a.conn != nil { gracefulGRPCClose(a.conn, GracefulShutdownTimeout) a.conn = nil } } func gracefulGRPCClose(conn *grpc.ClientConn, timeout time.Duration) { // close the client connection in the background, tries to avoid closing the connection // if the connection is in the middle of a call (e.g. streaming API) // // see https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md for details on connection states go func() { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() for ctx.Err() == nil { switch state := conn.GetState(); state { //nolint:exhaustive case connectivity.Idle, connectivity.Shutdown, connectivity.TransientFailure: // close immediately, connection is not used conn.Close() //nolint:errcheck return default: // wait for state change of the connection conn.WaitForStateChange(ctx, state) } } // close anyways on timeout conn.Close() //nolint:errcheck }() } ================================================ FILE: internal/app/apid/pkg/backend/apid_factory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend import ( "crypto/tls" "github.com/siderolabs/gen/concurrent" "github.com/siderolabs/grpc-proxy/proxy" ) // APIDFactory caches connection to apid instances by target. // // TODO: need to clean up idle connections from time to time. type APIDFactory struct { cache *concurrent.HashTrieMap[string, *APID] provider TLSConfigProvider } // TLSConfigProvider provides tls.Config for client connections. type TLSConfigProvider interface { ClientConfig() (*tls.Config, error) } // NewAPIDFactory creates new APIDFactory with given tls.Config. // // Client TLS config is used to connect to other apid instances. func NewAPIDFactory(provider TLSConfigProvider) *APIDFactory { return &APIDFactory{ cache: concurrent.NewHashTrieMap[string, *APID](), provider: provider, } } // Get backend by target. // // Get performs caching of backends. func (factory *APIDFactory) Get(target string) (proxy.Backend, error) { b, ok := factory.cache.Load(target) if ok { return b, nil } backend, err := NewAPID(target, factory.provider.ClientConfig) if err != nil { return nil, err } existing, loaded := factory.cache.LoadOrStore(target, backend) if loaded { // race: another Get() call built different backend backend.Close() return existing, nil } return backend, nil } // Flush all cached backends. // // This ensures that all connections are closed. func (factory *APIDFactory) Flush() { factory.cache.Range(func(key string, backend *APID) bool { backend.Close() return true }) } ================================================ FILE: internal/app/apid/pkg/backend/apid_factory_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend_test import ( "crypto/tls" "sync" "testing" "github.com/siderolabs/grpc-proxy/proxy" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/apid/pkg/backend" ) type APIDFactorySuite struct { suite.Suite f *backend.APIDFactory } type fakeTLSConfigProvider struct{} func (fakeTLSConfigProvider) ClientConfig() (*tls.Config, error) { return &tls.Config{}, nil } func (suite *APIDFactorySuite) SetupSuite() { suite.f = backend.NewAPIDFactory(fakeTLSConfigProvider{}) } func (suite *APIDFactorySuite) TestGet() { b1, err := suite.f.Get("127.0.0.1") suite.Require().NoError(err) suite.Require().NotNil(b1) b2, err := suite.f.Get("127.0.0.1") suite.Require().NoError(err) suite.Require().Equal(b1, b2) b3, err := suite.f.Get("127.0.0.2") suite.Require().NoError(err) suite.Require().NotEqual(b1, b3) _, err = suite.f.Get("127.0.0.2:50000") suite.Require().Error(err) } func (suite *APIDFactorySuite) TestGetConcurrent() { // for race detector var wg sync.WaitGroup backendCh := make(chan proxy.Backend, 10) for range 10 { wg.Go(func() { b, _ := suite.f.Get("10.0.0.1") //nolint:errcheck backendCh <- b }) } wg.Wait() close(backendCh) b := <-backendCh for anotherB := range backendCh { suite.Assert().Equal(b, anotherB) } } func TestAPIDFactorySuite(t *testing.T) { suite.Run(t, new(APIDFactorySuite)) } ================================================ FILE: internal/app/apid/pkg/backend/apid_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend_test import ( "context" "crypto/tls" "errors" "testing" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "google.golang.org/grpc/metadata" protobuf "google.golang.org/protobuf/proto" //nolint:depguard "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" "github.com/siderolabs/talos/internal/app/apid/pkg/backend" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/api" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/machinery/version" ) type APIDSuite struct { suite.Suite b *backend.APID } func (suite *APIDSuite) SetupSuite() { tlsConfigProvider := func() (*tls.Config, error) { return &tls.Config{}, nil } var err error suite.b, err = backend.NewAPID("127.0.0.1", tlsConfigProvider) suite.Require().NoError(err) } func (suite *APIDSuite) TestGetConnection() { md1 := metadata.New(nil) md1.Set(":authority", "127.0.0.2") md1.Set("nodes", "127.0.0.1") md1.Set("key", "value1", "value2") ctx1 := metadata.NewIncomingContext(authz.ContextWithRoles(context.Background(), role.MakeSet(role.Admin)), md1) outCtx1, conn1, err1 := suite.b.GetConnection(ctx1, "") suite.Require().NoError(err1) suite.Assert().NotNil(conn1) suite.Assert().Equal(role.MakeSet(role.Admin), authz.GetRoles(outCtx1)) mdOut1, ok1 := metadata.FromOutgoingContext(outCtx1) suite.Require().True(ok1) suite.Assert().Equal([]string{"value1", "value2"}, mdOut1.Get("key")) suite.Assert().Equal([]string{"127.0.0.2"}, mdOut1.Get("proxyfrom")) suite.Assert().Equal([]string{"os:admin"}, mdOut1.Get("talos-role")) suite.Run( "Same context", func() { ctx2 := ctx1 outCtx2, conn2, err2 := suite.b.GetConnection(ctx2, "") suite.Require().NoError(err2) suite.Assert().Equal(conn1, conn2) // connection is cached suite.Assert().Equal(role.MakeSet(role.Admin), authz.GetRoles(outCtx2)) mdOut2, ok2 := metadata.FromOutgoingContext(outCtx2) suite.Require().True(ok2) suite.Assert().Equal([]string{"value1", "value2"}, mdOut2.Get("key")) suite.Assert().Equal([]string{"127.0.0.2"}, mdOut2.Get("proxyfrom")) suite.Assert().Equal([]string{"os:admin"}, mdOut2.Get("talos-role")) }, ) suite.Run( "Other context", func() { md3 := metadata.New(nil) md3.Set(":authority", "127.0.0.2") md3.Set("nodes", "127.0.0.1") md3.Set("key", "value3", "value4") ctx3 := metadata.NewIncomingContext( authz.ContextWithRoles(context.Background(), role.MakeSet(role.Reader)), md3, ) outCtx3, conn3, err3 := suite.b.GetConnection(ctx3, "") suite.Require().NoError(err3) suite.Assert().Equal(conn1, conn3) // connection is cached suite.Assert().Equal(role.MakeSet(role.Reader), authz.GetRoles(outCtx3)) mdOut3, ok3 := metadata.FromOutgoingContext(outCtx3) suite.Require().True(ok3) suite.Assert().Equal([]string{"value3", "value4"}, mdOut3.Get("key")) suite.Assert().Equal([]string{"127.0.0.2"}, mdOut3.Get("proxyfrom")) suite.Assert().Equal([]string{"os:reader"}, mdOut3.Get("talos-role")) }, ) } func (suite *APIDSuite) TestAppendInfoUnary() { reply := &common.DataResponse{ Messages: []*common.Data{ { Bytes: []byte("foobar"), }, }, } resp, err := proto.Marshal(reply) suite.Require().NoError(err) newResp, err := suite.b.AppendInfo(false, resp) suite.Require().NoError(err) var newReply common.DataResponse err = proto.Unmarshal(newResp, &newReply) suite.Require().NoError(err) suite.Assert().EqualValues([]byte("foobar"), newReply.Messages[0].Bytes) suite.Assert().Equal(suite.b.String(), newReply.Messages[0].Metadata.Hostname) suite.Assert().Empty(newReply.Messages[0].Metadata.Error) } func (suite *APIDSuite) TestAppendInfoStreaming() { response := &common.Data{ Bytes: []byte("foobar"), } resp, err := proto.Marshal(response) suite.Require().NoError(err) newResp, err := suite.b.AppendInfo(true, resp) suite.Require().NoError(err) var newResponse common.Data err = proto.Unmarshal(newResp, &newResponse) suite.Require().NoError(err) suite.Assert().EqualValues([]byte("foobar"), newResponse.Bytes) suite.Assert().Equal(suite.b.String(), newResponse.Metadata.Hostname) suite.Assert().Empty(newResponse.Metadata.Error) } func (suite *APIDSuite) TestAppendInfoStreamingMetadata() { // this tests the case when metadata field is appended twice // to the message, but protobuf merges definitions response := &common.Data{ Metadata: &common.Metadata{ Error: "something went wrong", }, } resp, err := proto.Marshal(response) suite.Require().NoError(err) newResp, err := suite.b.AppendInfo(true, resp) suite.Require().NoError(err) var newResponse common.Data err = proto.Unmarshal(newResp, &newResponse) suite.Require().NoError(err) suite.Assert().Nil(newResponse.Bytes) suite.Assert().Equal(suite.b.String(), newResponse.Metadata.Hostname) suite.Assert().Equal("something went wrong", newResponse.Metadata.Error) } func (suite *APIDSuite) TestBuildErrorUnary() { resp, err := suite.b.BuildError(false, errors.New("some error")) suite.Require().NoError(err) var reply common.DataResponse err = proto.Unmarshal(resp, &reply) suite.Require().NoError(err) suite.Assert().Nil(reply.Messages[0].Bytes) suite.Assert().Equal(suite.b.String(), reply.Messages[0].Metadata.Hostname) suite.Assert().Equal("some error", reply.Messages[0].Metadata.Error) } func (suite *APIDSuite) TestBuildErrorStreaming() { resp, err := suite.b.BuildError(true, errors.New("some error")) suite.Require().NoError(err) var response common.Data err = proto.Unmarshal(resp, &response) suite.Require().NoError(err) suite.Assert().Nil(response.Bytes) suite.Assert().Equal(suite.b.String(), response.Metadata.Hostname) suite.Assert().Equal("some error", response.Metadata.Error) } func TestAPIDSuite(t *testing.T) { suite.Run(t, new(APIDSuite)) } func TestAPIIdiosyncrasies(t *testing.T) { for _, services := range xslices.Map(api.TalosAPIdOne2ManyAPIs(), func(fd protoreflect.FileDescriptor) protoreflect.ServiceDescriptors { return fd.Services() }, ) { for i := range services.Len() { service := services.Get(i) methods := service.Methods() for j := range methods.Len() { method := methods.Get(j) t.Run( string(method.FullName()), func(t *testing.T) { response := method.Output() responseFields := response.Fields() if method.IsStreamingServer() { metadata := responseFields.Get(0) assert.Equal(t, "metadata", metadata.TextName()) assert.Equal(t, 1, int(metadata.Number())) } else { require.Equal(t, 1, responseFields.Len(), "unary responses should have exactly one field") messages := responseFields.Get(0) assert.Equal(t, "messages", messages.TextName()) assert.Equal(t, 1, int(messages.Number())) reply := messages.Message() replyFields := reply.Fields() require.GreaterOrEqual( t, replyFields.Len(), 1, "unary replies should have at least one field", ) metadata := replyFields.Get(0) assert.Equal(t, "metadata", metadata.TextName()) assert.Equal(t, 1, int(metadata.Number())) } }, ) } } } } //nolint:nakedret,gocyclo,forcetypeassert func getOptions(t *testing.T, descriptor protoreflect.Descriptor) (deprecated bool, version string) { switch opts := descriptor.Options().(type) { case *descriptorpb.EnumOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedEnum).(string) } case *descriptorpb.EnumValueOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedEnumValue).(string) } case *descriptorpb.MessageOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedMessage).(string) } case *descriptorpb.FieldOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedField).(string) } case *descriptorpb.ServiceOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedService).(string) } case *descriptorpb.MethodOptions: if opts != nil { deprecated = pointer.SafeDeref(opts.Deprecated) version = protobuf.GetExtension(opts, common.E_RemoveDeprecatedMethod).(string) } case *descriptorpb.OneofOptions: // OneofOptions do not have deprecated option default: t.Fatalf("unhandled %T", opts) } return deprecated, version } func testDeprecated(t *testing.T, descriptor protoreflect.Descriptor, currentVersion *config.VersionContract) { deprecated, version := getOptions(t, descriptor) assert.Equal( t, deprecated, version != "", "%s: `deprecated` and `remove_deprecated_XXX_in` options should be used together", descriptor.FullName(), ) if !deprecated || version == "" { return } v, err := config.ParseContractFromVersion(version) require.NoError(t, err, "%s", descriptor.FullName()) assert.True(t, v.Greater(currentVersion), "%s should be removed in this version", descriptor.FullName()) } func testEnum(t *testing.T, enum protoreflect.EnumDescriptor, currentVersion *config.VersionContract) { testDeprecated(t, enum, currentVersion) values := enum.Values() for i := range values.Len() { testDeprecated(t, values.Get(i), currentVersion) } } func testMessage(t *testing.T, message protoreflect.MessageDescriptor, currentVersion *config.VersionContract) { testDeprecated(t, message, currentVersion) fields := message.Fields() for i := range fields.Len() { testDeprecated(t, fields.Get(i), currentVersion) } oneofs := message.Oneofs() for i := range oneofs.Len() { testDeprecated(t, oneofs.Get(i), currentVersion) } enums := message.Enums() for i := range enums.Len() { testEnum(t, enums.Get(i), currentVersion) } // test nested messages messages := message.Messages() for i := range messages.Len() { testMessage(t, messages.Get(i), currentVersion) } } func TestDeprecatedAPIs(t *testing.T) { currentVersion, err := config.ParseContractFromVersion(version.Tag) require.NoError(t, err) for _, file := range api.AllAPIs() { enums := file.Enums() for i := range enums.Len() { testEnum(t, enums.Get(i), currentVersion) } messages := file.Messages() for i := range messages.Len() { testMessage(t, messages.Get(i), currentVersion) } services := file.Services() for i := range services.Len() { service := services.Get(i) testDeprecated(t, service, currentVersion) methods := service.Methods() for j := range methods.Len() { method := methods.Get(j) testDeprecated(t, method, currentVersion) message := method.Input() testMessage(t, message, currentVersion) message = method.Output() testMessage(t, message, currentVersion) } } } } ================================================ FILE: internal/app/apid/pkg/backend/backend.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package backend implements backends satisfying proxy.Backend interface package backend ================================================ FILE: internal/app/apid/pkg/director/director.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package director provides proxy call routing facility package director import ( "context" "regexp" "slices" "strings" "github.com/siderolabs/grpc-proxy/proxy" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // Router wraps grpc-proxy StreamDirector. type Router struct { localBackend proxy.Backend remoteBackendFactory RemoteBackendFactory localAddressProvider LocalAddressProvider streamedMatchers []*regexp.Regexp skipRouting bool } // RemoteBackendFactory provides backend generation by address (target). type RemoteBackendFactory func(target string) (proxy.Backend, error) // NewRouter builds new Router. func NewRouter(backendFactory RemoteBackendFactory, localBackend proxy.Backend, localAddressProvider LocalAddressProvider, skipRouting bool) *Router { return &Router{ localBackend: localBackend, remoteBackendFactory: backendFactory, localAddressProvider: localAddressProvider, skipRouting: skipRouting, } } // Register is no-op to implement factory.Registrator interface. // // Actual proxy handler is installed via grpc.UnknownServiceHandler option. func (r *Router) Register(srv *grpc.Server) { } // Director implements proxy.StreamDirector function. // //nolint:gocyclo,cyclop func (r *Router) Director(ctx context.Context, fullMethodName string) (proxy.Mode, []proxy.Backend, error) { if r.skipRouting { return proxy.One2One, []proxy.Backend{r.localBackend}, nil } md, ok := metadata.FromIncomingContext(ctx) if !ok { return proxy.One2One, []proxy.Backend{r.localBackend}, nil } if _, exists := md["proxyfrom"]; exists { return proxy.One2One, []proxy.Backend{r.localBackend}, nil } nodes, okNodes := md["nodes"] node, okNode := md["node"] if okNode && len(node) != 1 { return proxy.One2One, nil, status.Error(codes.InvalidArgument, "node metadata must be single-valued") } // special handling for cases when a single node is requested, but forwarding is disabled // // if there's a single destination, and that destination is local node, skip forwarding and send a request to the same node if r.remoteBackendFactory == nil { if okNode && r.localAddressProvider.IsLocalTarget(node[0]) { okNode = false } if okNodes && len(nodes) == 1 && r.localAddressProvider.IsLocalTarget(nodes[0]) { okNodes = false } } switch { case okNodes: // Explicit list of gRPC methods that support one-2-many proxying. switch { case strings.HasPrefix(fullMethodName, "/machine.MachineService/"): case strings.HasPrefix(fullMethodName, "/cluster.ClusterService/"): case strings.HasPrefix(fullMethodName, "/inspect.InspectService/"): case strings.HasPrefix(fullMethodName, "/storage.StorageService/"): case strings.HasPrefix(fullMethodName, "/time.TimeService/"): default: return proxy.One2One, nil, status.Errorf(codes.InvalidArgument, "one-2-many proxying is not supported for method %s", fullMethodName) } return r.aggregateDirector(nodes) case okNode: return r.singleDirector(node[0]) default: // send directly to local node, skips another layer of proxying return proxy.One2One, []proxy.Backend{r.localBackend}, nil } } // singleDirector sends request to a single instance in one-2-one mode. func (r *Router) singleDirector(target string) (proxy.Mode, []proxy.Backend, error) { if r.remoteBackendFactory == nil { return proxy.One2One, nil, status.Error(codes.PermissionDenied, "no request forwarding") } backend, err := r.remoteBackendFactory(target) if err != nil { return proxy.One2One, nil, status.Error(codes.Internal, err.Error()) } return proxy.One2One, []proxy.Backend{backend}, nil } // aggregateDirector sends request across set of remote instances and aggregates results. func (r *Router) aggregateDirector(targets []string) (proxy.Mode, []proxy.Backend, error) { if r.remoteBackendFactory == nil { return proxy.One2One, nil, status.Error(codes.PermissionDenied, "no request forwarding") } var err error backends := make([]proxy.Backend, len(targets)) for i, target := range targets { backends[i], err = r.remoteBackendFactory(target) if err != nil { return proxy.One2Many, nil, status.Error(codes.Internal, err.Error()) } } return proxy.One2Many, backends, nil } // StreamedDetector implements proxy.StreamedDetector. func (r *Router) StreamedDetector(fullMethodName string) bool { return slices.ContainsFunc(r.streamedMatchers, func(regex *regexp.Regexp) bool { return regex.MatchString(fullMethodName) }) } // RegisterStreamedRegex register regex for streamed method. // // This could be exact literal match: /^\/serviceName\/methodName$/ or any // suffix/prefix match. func (r *Router) RegisterStreamedRegex(regex string) { r.streamedMatchers = append(r.streamedMatchers, regexp.MustCompile(regex)) } ================================================ FILE: internal/app/apid/pkg/director/director_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package director_test import ( "context" "regexp" "testing" "github.com/siderolabs/grpc-proxy/proxy" "github.com/stretchr/testify/suite" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/app/apid/pkg/director" ) type DirectorSuite struct { suite.Suite localBackend *mockBackend router *director.Router } func (suite *DirectorSuite) SetupSuite() { suite.localBackend = &mockBackend{} suite.router = director.NewRouter( mockBackendFactory, suite.localBackend, &mockLocalAddressProvider{ local: map[string]struct{}{ "localhost": {}, }, }, false, ) } func (suite *DirectorSuite) TestStreamedDetector() { suite.Assert().False(suite.router.StreamedDetector("/service.Service/someMethod")) suite.router.RegisterStreamedRegex("^" + regexp.QuoteMeta("/service.Service/someMethod") + "$") suite.Assert().True(suite.router.StreamedDetector("/service.Service/someMethod")) suite.Assert().False(suite.router.StreamedDetector("/service.Service/someMethod2")) suite.Assert().False(suite.router.StreamedDetector("/servicexService/someMethod")) suite.router.RegisterStreamedRegex("Stream$") suite.Assert().True(suite.router.StreamedDetector("/service.Service/getStream")) suite.Assert().False(suite.router.StreamedDetector("/service.Service/getStreamItem")) } func (suite *DirectorSuite) TestDirectorAggregate() { ctx := context.Background() md := metadata.New(nil) md.Set("nodes", "127.0.0.1", "127.0.0.2") mode, backends, err := suite.router.Director(metadata.NewIncomingContext(ctx, md), "/machine.MachineService/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2Many, mode) suite.Assert().Len(backends, 2) suite.Assert().Equal("127.0.0.1", backends[0].(*mockBackend).target) suite.Assert().Equal("127.0.0.2", backends[1].(*mockBackend).target) md = metadata.New(nil) md.Set("nodes", "127.0.0.1") mode, backends, err = suite.router.Director(metadata.NewIncomingContext(ctx, md), "/machine.MachineService/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2Many, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal("127.0.0.1", backends[0].(*mockBackend).target) } func (suite *DirectorSuite) TestDirectorSingleNode() { ctx := context.Background() md := metadata.New(nil) md.Set("node", "127.0.0.1") mode, backends, err := suite.router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal("127.0.0.1", backends[0].(*mockBackend).target) suite.Assert().NoError(err) md = metadata.New(nil) md.Set("node", "127.0.0.1", "127.0.0.2") _, _, err = suite.router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Assert().Equal(codes.InvalidArgument, status.Code(err)) } func (suite *DirectorSuite) TestDirectorLocal() { ctx := context.Background() md := metadata.New(nil) mode, backends, err := suite.router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) suite.Assert().NoError(err) } func (suite *DirectorSuite) TestDirectorNoRemoteBackend() { // override the router to have no remote backends router := director.NewRouter( nil, suite.localBackend, &mockLocalAddressProvider{ local: map[string]struct{}{ "localhost": {}, }, }, false, ) ctx := context.Background() // request forwarding via node/nodes is disabled md := metadata.New(nil) md.Set("node", "127.0.0.1") _, _, err := router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Require().Error(err) suite.Assert().Equal(codes.PermissionDenied, status.Code(err)) md = metadata.New(nil) md.Set("nodes", "127.0.0.1", "127.0.0.2") _, _, err = router.Director(metadata.NewIncomingContext(ctx, md), "/machine.MachineService/method") suite.Require().Error(err) suite.Assert().Equal(codes.PermissionDenied, status.Code(err)) // no request forwarding, allowed md = metadata.New(nil) mode, backends, err := router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) // request forwarding to local address, allowed md = metadata.New(nil) md.Set("node", "localhost") mode, backends, err = router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) md = metadata.New(nil) md.Set("nodes", "localhost") mode, backends, err = router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) } func (suite *DirectorSuite) TestDirectorNoRouting() { router := director.NewRouter( nil, suite.localBackend, &mockLocalAddressProvider{ local: map[string]struct{}{ "localhost": {}, }, }, true, ) ctx := context.Background() // request forwarding via node/nodes is ignored md := metadata.New(nil) md.Set("node", "127.0.0.1") mode, backends, err := router.Director(metadata.NewIncomingContext(ctx, md), "/machine.MachineService/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) md = metadata.New(nil) md.Set("nodes", "127.0.0.1", "127.0.0.2") mode, backends, err = router.Director(metadata.NewIncomingContext(ctx, md), "/machine.MachineService/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) // no request forwarding -- same md = metadata.New(nil) mode, backends, err = router.Director(metadata.NewIncomingContext(ctx, md), "/service.Service/method") suite.Require().NoError(err) suite.Assert().Equal(proxy.One2One, mode) suite.Assert().Len(backends, 1) suite.Assert().Equal(suite.localBackend, backends[0]) } func TestDirectorSuite(t *testing.T) { t.Parallel() suite.Run(t, new(DirectorSuite)) } ================================================ FILE: internal/app/apid/pkg/director/local_address.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package director import ( "context" "sync" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LocalAddressProvider provides local address information. type LocalAddressProvider interface { IsLocalTarget(string) bool } // localAddressProvider watches and keeps track of the local node addresses. type localAddressProvider struct { mu sync.Mutex localAddresses map[string]struct{} localHostnames map[string]struct{} } // NewLocalAddressProvider initializes and returns a new LocalAddressProvider. func NewLocalAddressProvider(st state.State) (LocalAddressProvider, error) { p := &localAddressProvider{} evCh := make(chan state.Event) if err := st.Watch(context.Background(), resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.NodeAddressCurrentID, resource.VersionUndefined), evCh); err != nil { return nil, err } if err := st.Watch(context.Background(), resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined), evCh); err != nil { return nil, err } go p.watch(evCh) return p, nil } func (p *localAddressProvider) watch(evCh <-chan state.Event) { for ev := range evCh { switch ev.Type { case state.Created, state.Updated: // expected case state.Destroyed, state.Bootstrapped, state.Errored, state.Noop: // shouldn't happen, ignore continue } switch r := ev.Resource.(type) { case *network.NodeAddress: p.mu.Lock() p.localAddresses = make(map[string]struct{}, len(r.TypedSpec().Addresses)) for _, addr := range r.TypedSpec().Addresses { p.localAddresses[addr.Addr().String()] = struct{}{} } p.mu.Unlock() case *network.HostnameStatus: p.mu.Lock() p.localHostnames = make(map[string]struct{}, 2) p.localHostnames[r.TypedSpec().Hostname] = struct{}{} p.localHostnames[r.TypedSpec().FQDN()] = struct{}{} p.mu.Unlock() } } } // IsLocalTarget returns true if the address (hostname) is local. func (p *localAddressProvider) IsLocalTarget(target string) bool { p.mu.Lock() defer p.mu.Unlock() _, ok1 := p.localAddresses[target] _, ok2 := p.localHostnames[target] return ok1 || ok2 } ================================================ FILE: internal/app/apid/pkg/director/mocks_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package director_test import ( "context" "github.com/siderolabs/grpc-proxy/proxy" "google.golang.org/grpc" ) type mockBackend struct { target string } func (m *mockBackend) String() string { return m.target } func (m *mockBackend) GetConnection(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) { return ctx, nil, nil } func (m *mockBackend) AppendInfo(streaming bool, resp []byte) ([]byte, error) { return resp, nil } func (m *mockBackend) BuildError(streaming bool, err error) ([]byte, error) { return nil, nil } func mockBackendFactory(target string) (proxy.Backend, error) { return &mockBackend{target: target}, nil } type mockLocalAddressProvider struct { local map[string]struct{} } func (m *mockLocalAddressProvider) IsLocalTarget(t string) bool { _, ok := m.local[t] return ok } ================================================ FILE: internal/app/apid/pkg/provider/provider.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package provider provides TLS config for client & server. package provider import ( "bytes" "context" stdlibtls "crypto/tls" stdx509 "crypto/x509" "fmt" "sync" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/tls" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // TLSConfig provides client & server TLS configs for apid. type TLSConfig struct { certificateProvider *certificateProvider watchCh <-chan state.Event skipClientCertVerify bool } // NewTLSConfig builds provider from configuration and endpoints. func NewTLSConfig(ctx context.Context, resources state.State, skipClientCertVerify bool) (*TLSConfig, error) { watchCh := make(chan state.Event) if err := resources.Watch(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.APIType, secrets.APIID, resource.VersionUndefined), watchCh); err != nil { return nil, fmt.Errorf("error setting up watch: %w", err) } // wait for the first event to set up certificate provider provider := &certificateProvider{} for { var event state.Event select { case <-ctx.Done(): return nil, ctx.Err() case event = <-watchCh: } switch event.Type { case state.Created, state.Updated: // expected case state.Destroyed, state.Bootstrapped, state.Noop: // ignore, we'll get another event continue case state.Errored: return nil, fmt.Errorf("error watching for API certificates: %w", event.Error) } apiCerts := event.Resource.(*secrets.API) //nolint:forcetypeassert if err := provider.Update(apiCerts); err != nil { return nil, err } return &TLSConfig{ certificateProvider: provider, watchCh: watchCh, skipClientCertVerify: skipClientCertVerify, }, nil } } // Watch for changes in API certificates and updates the TLSConfig. func (tlsConfig *TLSConfig) Watch(ctx context.Context, onUpdate func()) error { for { var event state.Event select { case <-ctx.Done(): return nil case event = <-tlsConfig.watchCh: } switch event.Type { case state.Created, state.Updated: // expected case state.Destroyed, state.Bootstrapped, state.Noop: // ignore, we'll get another event continue case state.Errored: return fmt.Errorf("error watching API certificates: %w", event.Error) } apiCerts := event.Resource.(*secrets.API) //nolint:forcetypeassert if err := tlsConfig.certificateProvider.Update(apiCerts); err != nil { return fmt.Errorf("failed updating cert: %v", err) } if onUpdate != nil { onUpdate() } } } // ServerConfig generates server-side tls.Config. func (tlsConfig *TLSConfig) ServerConfig() (*stdlibtls.Config, error) { authType := tls.Mutual if tlsConfig.skipClientCertVerify { authType = tls.ServerOnly } return tls.New( tls.WithClientAuthType(authType), tls.WithDynamicClientCA(tlsConfig.certificateProvider), tls.WithServerCertificateProvider(tlsConfig.certificateProvider), ) } // ClientConfig generates client-side tls.Config. func (tlsConfig *TLSConfig) ClientConfig() (*stdlibtls.Config, error) { if !tlsConfig.certificateProvider.HasClientCertificate() { return nil, nil } ca, err := tlsConfig.certificateProvider.GetCA() if err != nil { return nil, fmt.Errorf("failed to get root CA: %w", err) } return tls.New( tls.WithClientAuthType(tls.Mutual), tls.WithCACertPEM(ca), tls.WithClientCertificateProvider(tlsConfig.certificateProvider), ) } type certificateProvider struct { mu sync.Mutex ca []byte caCertPool *stdx509.CertPool clientCert, serverCert *stdlibtls.Certificate } func (p *certificateProvider) Update(apiCerts *secrets.API) error { p.mu.Lock() defer p.mu.Unlock() serverCert, err := stdlibtls.X509KeyPair(apiCerts.TypedSpec().Server.Crt, apiCerts.TypedSpec().Server.Key) if err != nil { return fmt.Errorf("failed to parse server cert and key into a TLS Certificate: %w", err) } p.serverCert = &serverCert p.ca = bytes.Join( xslices.Map( apiCerts.TypedSpec().AcceptedCAs, func(cert *x509.PEMEncodedCertificate) []byte { return cert.Crt }, ), nil, ) p.caCertPool = stdx509.NewCertPool() if len(p.ca) > 0 { if !p.caCertPool.AppendCertsFromPEM(p.ca) { return fmt.Errorf("failed to parse CA certs into a CertPool") } } if apiCerts.TypedSpec().Client != nil { clientCert, err := stdlibtls.X509KeyPair(apiCerts.TypedSpec().Client.Crt, apiCerts.TypedSpec().Client.Key) if err != nil { return fmt.Errorf("failed to parse client cert and key into a TLS Certificate: %w", err) } p.clientCert = &clientCert } else { p.clientCert = nil } return nil } func (p *certificateProvider) GetCA() ([]byte, error) { p.mu.Lock() defer p.mu.Unlock() return p.ca, nil } func (p *certificateProvider) GetCACertPool() (*stdx509.CertPool, error) { p.mu.Lock() defer p.mu.Unlock() return p.caCertPool, nil } func (p *certificateProvider) GetCertificate(*stdlibtls.ClientHelloInfo) (*stdlibtls.Certificate, error) { p.mu.Lock() defer p.mu.Unlock() return p.serverCert, nil } func (p *certificateProvider) HasClientCertificate() bool { p.mu.Lock() defer p.mu.Unlock() return p.clientCert != nil } func (p *certificateProvider) GetClientCertificate(*stdlibtls.CertificateRequestInfo) (*stdlibtls.Certificate, error) { p.mu.Lock() defer p.mu.Unlock() return p.clientCert, nil } ================================================ FILE: internal/app/apid/pkg/provider/provider_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provider_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: internal/app/apid/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package apid import ( "context" "crypto/x509" "errors" "fmt" "log" "net" "regexp" "slices" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-debug" "github.com/siderolabs/grpc-proxy/proxy" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials" apidbackend "github.com/siderolabs/talos/internal/app/apid/pkg/backend" "github.com/siderolabs/talos/internal/app/apid/pkg/director" "github.com/siderolabs/talos/internal/app/apid/pkg/provider" "github.com/siderolabs/talos/pkg/grpc/factory" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/grpc/proxy/backend" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func runService(ctx context.Context, resources state.State, config *runtime.APIServiceConfig) error { log.Printf( "starting apid with config: listen address %s, skip client cert verify %v, node routing disabled %v, readonly role mode %v", config.TypedSpec().ListenAddress, config.TypedSpec().SkipVerifyingClientCert, config.TypedSpec().NodeRoutingDisabled, config.TypedSpec().ReadonlyRoleMode, ) tlsConfig, err := provider.NewTLSConfig(ctx, resources, config.TypedSpec().SkipVerifyingClientCert) if err != nil { return fmt.Errorf("failed to create remote certificate provider: %w", err) } serverTLSConfig, err := tlsConfig.ServerConfig() if err != nil { return fmt.Errorf("failed to create OS-level TLS configuration: %w", err) } serverTLSConfig.VerifyPeerCertificate = verifyExtKeyUsage clientTLSConfig, err := tlsConfig.ClientConfig() if err != nil { return fmt.Errorf("failed to create client TLS config: %w", err) } var ( remoteFactory director.RemoteBackendFactory onPKIUpdate func() ) if clientTLSConfig != nil { backendFactory := apidbackend.NewAPIDFactory(tlsConfig) remoteFactory = backendFactory.Get onPKIUpdate = backendFactory.Flush defer backendFactory.Flush() } localAddressProvider, err := director.NewLocalAddressProvider(resources) if err != nil { return fmt.Errorf("failed to create local address provider: %w", err) } localBackend := backend.NewLocal("machined", constants.MachineSocketPath) defer localBackend.Close() //nolint:errcheck router := director.NewRouter(remoteFactory, localBackend, localAddressProvider, config.TypedSpec().NodeRoutingDisabled) // all existing streaming methods for _, methodName := range []string{ "/machine.MachineService/Copy", "/machine.MachineService/DiskUsage", "/machine.MachineService/Dmesg", "/machine.MachineService/EtcdSnapshot", "/machine.MachineService/Events", "/machine.MachineService/ImageList", "/machine.MachineService/Kubeconfig", "/machine.MachineService/List", "/machine.MachineService/DebugContainer", "/machine.MachineService/Logs", "/machine.MachineService/PacketCapture", "/machine.MachineService/Read", "/machine.LifecycleService/Install", "/machine.LifecycleService/Upgrade", "/os.OSService/Dmesg", "/cluster.ClusterService/HealthCheck", } { router.RegisterStreamedRegex("^" + regexp.QuoteMeta(methodName) + "$") } // register future pattern: method should have suffix "Stream" router.RegisterStreamedRegex("Stream$") networkListener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", config.TypedSpec().ListenAddress) if err != nil { return fmt.Errorf("error creating listner: %w", err) } networkServer := func() *grpc.Server { injector := &authz.Injector{ Mode: authz.Enabled, } if config.TypedSpec().ReadonlyRoleMode { injector.Mode = authz.ReadOnlyWithAdminOnSiderolink } if debug.Enabled { injector.Logger = log.New(log.Writer(), "apid/authz/injector/http ", log.Flags()).Printf } return factory.NewServer( router, factory.WithDefaultLog(), factory.ServerOptions( grpc.Creds( credentials.NewTLS(serverTLSConfig), ), grpc.ForceServerCodecV2(proxy.Codec()), grpc.UnknownServiceHandler( proxy.TransparentHandler( router.Director, proxy.WithStreamedDetector(router.StreamedDetector), ), ), grpc.MaxRecvMsgSize(constants.GRPCMaxMessageSize), ), factory.WithUnaryInterceptor(injector.UnaryInterceptor()), factory.WithStreamInterceptor(injector.StreamInterceptor()), ) }() errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { return networkServer.Serve(networkListener) }) errGroup.Go(func() error { return tlsConfig.Watch(ctx, onPKIUpdate) }) errGroup.Go(func() error { <-ctx.Done() shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() factory.ServerGracefulStop(networkServer, shutdownCtx) return nil }) return errGroup.Wait() } func verifyExtKeyUsage(_ [][]byte, verifiedChains [][]*x509.Certificate) error { if len(verifiedChains) == 0 { return errors.New("no verified chains") } certs := verifiedChains[0] for _, cert := range certs { if cert.IsCA { continue } if !slices.Equal(cert.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}) { return fmt.Errorf("certificate %q is missing the client auth extended key usage", cert.Subject) } } return nil } ================================================ FILE: internal/app/auditd/auditd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package auditd registers auditd service and logs audit events. package auditd import ( "context" "errors" "fmt" "io" "sync" "sync/atomic" "syscall" "github.com/elastic/go-libaudit/v2" "github.com/elastic/go-libaudit/v2/auparse" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // Main is an entrypoint to the auditd service. func Main(ctx context.Context, _ runtime.Runtime, logWriter io.Writer) error { return Run(ctx, logWriter) } // Run starts the auditd service. // // based on https://github.com/elastic/go-libaudit/blob/main/cmd/audit/audit.go func Run(ctx context.Context, logWriter io.Writer) error { var wg sync.WaitGroup defer wg.Wait() ctx, cancel := context.WithCancel(ctx) defer cancel() client, err := libaudit.NewAuditClient(nil) if err != nil { return fmt.Errorf("failed to create audit client: %w", err) } var auditDefaultEnabled atomic.Bool wg.Add(1) go func(c *libaudit.AuditClient) { defer wg.Done() <-ctx.Done() if !auditDefaultEnabled.Load() { c.SetEnabled(false, libaudit.NoWait) //nolint:errcheck } c.Close() //nolint:errcheck }(client) status, err := client.GetStatus() if err != nil { return fmt.Errorf("failed to get audit status: %w", err) } auditDefaultEnabled.Store(status.Enabled >= 1) if status.Enabled == 0 { if err := client.SetEnabled(true, libaudit.WaitForReply); err != nil { return fmt.Errorf("failed to enable audit: %w", err) } } if err = client.SetRateLimit(uint32(4096), libaudit.NoWait); err != nil { return fmt.Errorf("failed to set rate limit: %w", err) } if err := client.SetBacklogLimit(8192, libaudit.NoWait); err != nil { return fmt.Errorf("failed to set backlog limit: %w", err) } if err := client.SetPID(libaudit.NoWait); err != nil { return fmt.Errorf("failed to set audit PID: %w", err) } return receiveEvents(ctx, client, logWriter) } func receiveEvents(ctx context.Context, client *libaudit.AuditClient, logWriter io.Writer) error { for { rawEvent, err := client.Receive(false) if err != nil { if errors.Is(err, syscall.EBADF) { return nil } if errors.Is(err, syscall.EINTR) && errors.Is(err, syscall.EAGAIN) { continue } return fmt.Errorf("failed to receive audit event: %w", err) } select { case <-ctx.Done(): return nil default: } // Messages from 1100-2999 are valid audit messages. if rawEvent.Type < auparse.AUDIT_USER_AUTH || rawEvent.Type > auparse.AUDIT_LAST_USER_MSG2 { continue } fmt.Fprintf(logWriter, "type=%s msg=%s\n", rawEvent.Type, rawEvent.Data) } } ================================================ FILE: internal/app/dashboard/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package dashboard implements dashboard functionality. package dashboard import ( "context" "fmt" "log" "net/url" "os" "os/signal" "syscall" "github.com/siderolabs/go-procfs/procfs" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" metalurl "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" "github.com/siderolabs/talos/internal/pkg/dashboard" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/startup" ) // Main is the entrypoint into dashboard. func Main() { if err := dashboardMain(); err != nil { log.Fatal(err) } } func dashboardMain() error { startup.LimitMaxProcs(constants.DashboardMaxProcs) md := metadata.Pairs() authz.SetMetadata(md, role.MakeSet(role.Admin)) ctx, cancel := sigtermAwareContext(context.Background()) defer cancel() ctx = metadata.NewOutgoingContext(ctx, md) c, err := client.New(ctx, client.WithUnixSocket(constants.MachineSocketPath), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) if err != nil { return fmt.Errorf("error connecting to the machine service: %w", err) } screens := []dashboard.Screen{dashboard.ScreenSummary, dashboard.ScreenMonitor} // activate the network config screen only on metal platform currentPlatform, _ := platform.CurrentPlatform() //nolint:errcheck if currentPlatform != nil && currentPlatform.Name() == constants.PlatformMetal { screens = append(screens, dashboard.ScreenNetworkConfig) if showConfigURLTab() { screens = append(screens, dashboard.ScreenConfigURL) } } return dashboard.Run(ctx, c, dashboard.WithAllowExitKeys(false), dashboard.WithScreens(screens...)) } func showConfigURLTab() bool { option := procfs.ProcCmdline().Get(constants.KernelParamConfig).First() if option == nil { return false } parsedURL, err := url.Parse(*option) if err != nil { return false } codeVar := metalurl.AllVariables()[constants.CodeKey] if codeVar == nil { return false } return codeVar.Matches(parsedURL.Query()) } func sigtermAwareContext(ctx context.Context) (context.Context, context.CancelFunc) { ctx, cancel := context.WithCancel(ctx) signalCh := make(chan os.Signal, 1) signal.Notify(signalCh, syscall.SIGTERM) go func() { select { case <-signalCh: cancel() case <-ctx.Done(): } }() return ctx, cancel } ================================================ FILE: internal/app/debug/container_streams.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package debug import ( "context" "fmt" "io" "log" "syscall" containerdapi "github.com/containerd/containerd/v2/client" "github.com/siderolabs/gen/panicsafe" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) func newGrpcStreamWriter(srv grpc.BidiStreamingServer[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse]) ( *grpcStdioStreamer, io.Reader, io.Writer, ) { stdinR, stdinW := io.Pipe() stdoutR, stdoutW := io.Pipe() return &grpcStdioStreamer{ srv: srv, stdinW: stdinW, stdoutR: stdoutR, stdoutW: stdoutW, }, stdinR, stdoutW } type grpcStdioStreamer struct { srv grpc.BidiStreamingServer[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse] stdinW *io.PipeWriter stdoutR *io.PipeReader stdoutW *io.PipeWriter } //nolint:gocyclo func (g *grpcStdioStreamer) stream(ctx context.Context, statusC <-chan containerdapi.ExitStatus, task containerdapi.Task) error { ctx, cancel := context.WithCancel(ctx) defer cancel() sendLoopCh, recvLoopCh := make(chan error), make(chan error) go func(errCh chan<- error) { errCh <- panicsafe.RunErr(g.sendLoop) }(sendLoopCh) go func(errCh chan<- error) { errCh <- panicsafe.RunErr(func() error { return g.recvLoop(ctx, task) }) }(recvLoopCh) for { select { // task terminated case ec := <-statusC: // closing r.stdoutW causes the sendLoop, which s // blocking on r.stdoutR.Read(), to get an EOF and exit g.stdoutW.Close() //nolint:errcheck // close r.stdinW to ensure the container exits if it's still running g.stdinW.Close() //nolint:errcheck // cancel the context to stop loops cancel() if ec.Error() != nil { return ec.Error() } // wait for send loop to exit // // calling srv.Send from multiple goroutines is not safe, // so we need to wait for sendLoop to exit if sendLoopCh != nil { <-sendLoopCh } // then, sending the exit code back to the client makes // the client disconnect if err := g.srv.Send(&machine.DebugContainerRunResponse{ Resp: &machine.DebugContainerRunResponse_ExitCode{ ExitCode: int32(ec.ExitCode()), }, }); err != nil { return fmt.Errorf("debug container: failed to send exit code: %w", err) } // wait for recv loop to exit after client disconnects if recvLoopCh != nil { <-recvLoopCh } return nil // our send loop terminated case sendErr := <-sendLoopCh: if sendErr == nil { // keep waiting for task to exit sendLoopCh = nil continue } // close r.stdinW to ensure the container exits if it's still running g.stdinW.Close() //nolint:errcheck return fmt.Errorf("debug container: send loop error: %w", sendErr) case recvErr := <-recvLoopCh: if recvErr == nil { // keep waiting for task to exit recvLoopCh = nil continue } // close r.stdoutW to stop the send loop g.stdoutW.Close() //nolint:errcheck return fmt.Errorf("debug container: receive loop error: %w", recvErr) // client walked away case <-ctx.Done(): // closing r.stdoutW causes the sendLoop, which s // blocking on r.stdoutR.Read(), to get an EOF and exit g.stdoutW.Close() //nolint:errcheck // close r.stdinW to ensure the container exits if it's still running g.stdinW.Close() //nolint:errcheck // wait for loops to exit if recvLoopCh != nil { <-recvLoopCh } if sendLoopCh != nil { <-sendLoopCh } return ctx.Err() } } } func (g *grpcStdioStreamer) recvLoop(ctx context.Context, task containerdapi.Task) error { defer g.stdinW.Close() //nolint:errcheck for { msg, err := g.srv.Recv() if err != nil { if status.Code(err) != codes.Canceled && err != io.EOF { return fmt.Errorf("error receiving input message: %w", err) } return nil } if err = g.processMessage(ctx, task, msg); err != nil { return fmt.Errorf("error processing input message: %w", err) } } } func (g *grpcStdioStreamer) processMessage(ctx context.Context, task containerdapi.Task, msg *machine.DebugContainerRunRequest) error { switch msg.Request.(type) { case *machine.DebugContainerRunRequest_StdinData: if stdinData := msg.GetStdinData(); stdinData != nil { _, err := g.stdinW.Write(stdinData) if err != nil { return fmt.Errorf("failed to write to stdin: %w", err) } } case *machine.DebugContainerRunRequest_TermResize: if err := task.Resize( ctx, uint32(msg.GetTermResize().Width), uint32(msg.GetTermResize().Height), ); err != nil { return fmt.Errorf("failed to resize terminal: %w", err) } case *machine.DebugContainerRunRequest_Signal: signalNum := msg.GetSignal() log.Printf("debug container: received signal %d, forwarding to task", signalNum) if err := task.Kill(ctx, syscall.Signal(signalNum)); err != nil { return fmt.Errorf("debug container: failed to forward signal to task: %w", err) } default: return fmt.Errorf("unknown request type: %T", msg.Request) } return nil } func (g *grpcStdioStreamer) sendLoop() error { defer g.stdoutW.Close() //nolint:errcheck b := make([]byte, 512) for { n, err := g.stdoutR.Read(b) if err != nil { if err == io.EOF { return nil } return fmt.Errorf("failed to read from stdout: %w", err) } err = g.srv.Send(&machine.DebugContainerRunResponse{ Resp: &machine.DebugContainerRunResponse_StdoutData{ StdoutData: b[:n], }, }) if err != nil { if status.Code(err) != codes.Canceled { return fmt.Errorf("debug container: failed to send stdout data: %w", err) } return nil } } } ================================================ FILE: internal/app/debug/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package debug implements machine.DebugService. package debug import ( "context" "crypto/rand" "encoding/hex" "fmt" "log" "path/filepath" "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/leases" "github.com/containerd/containerd/v2/pkg/cio" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/errdefs" "github.com/opencontainers/runtime-spec/specs-go" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/app/internal/ctrhelper" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/cgroup" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Service implements machine.DebugService. type Service struct { machine.UnimplementedDebugServiceServer } // ContainerRun implements machine.DebugService.ContainerRun. func (s *Service) ContainerRun(srv grpc.BidiStreamingServer[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse]) error { //nolint:gocyclo ctx := srv.Context() // 1. get the debug container spec specReq, err := srv.Recv() if err != nil { return status.Errorf(codes.InvalidArgument, "failed to receive spec: %v", err) } spec := specReq.GetSpec() if spec == nil { return status.Errorf(codes.InvalidArgument, "expected debug container spec") } if spec.GetProfile() != machine.DebugContainerRunRequestSpec_PROFILE_PRIVILEGED { return status.Errorf(codes.InvalidArgument, "unsupported debug container profile: %s", spec.GetProfile()) } log.Printf("debug container request received: image=%s args=%v env=%v profile=%s", spec.GetImageName(), spec.GetArgs(), spec.GetEnv(), spec.GetProfile()) // 2. connect to containerd with a lease ctx, detachedContext, c8dClient, err := ctrhelper.ContainerdInstanceHelper(ctx, spec.GetContainerd()) if err != nil { return err } defer c8dClient.Close() //nolint:errcheck l, err := c8dClient.LeasesService().Create(ctx, leases.WithRandomID(), ) if err != nil { return fmt.Errorf("failed to create lease: %v", err) } defer func() { if err := c8dClient.LeasesService().Delete(detachedContext, l, leases.SynchronousDelete); err != nil { log.Printf("failed to delete lease %s: %v", l.ID, err) } }() ctx = leases.WithLease(ctx, l.ID) img, err := c8dClient.GetImage(ctx, spec.ImageName) if err != nil { if errdefs.IsNotFound(err) { return status.Errorf(codes.NotFound, "image %s not found: %v", spec.ImageName, err) } return err } // 3. create the debug container containerID, err := generateContainerID() if err != nil { return fmt.Errorf("failed to generate container ID: %v", err) } // create the root cgroup to populate resources as needed _, err = cgroup.CreateCgroup(constants.CgroupSystemDebug) if err != nil { return fmt.Errorf("failed to create debug cgroup: %v", err) } cgroupPath := filepath.Join(constants.CgroupSystemDebug, containerID) // create a cgroup for this container instance, and clean it up afterwards cg, err := cgroup.CreateCgroup(cgroupPath) if err != nil { return fmt.Errorf("failed to create cgroup for debug container: %v", err) } defer func() { cg.Delete() // nolint: errcheck }() ctr, err := createDebugContainer(ctx, c8dClient, containerID, img, spec, cgroupPath) if err != nil { return err } log.Printf("debug container: container %s created", ctr.ID()) defer func() { cleanupErr := ctr.Delete(detachedContext, client.WithSnapshotCleanup) if cleanupErr != nil { log.Printf("debug container: failed to delete container %s: %s", ctr.ID(), cleanupErr.Error()) } log.Printf("debug container: container %s deleted", ctr.ID()) }() // 4. run and attach to the debug container return runAndAttachContainer(ctx, detachedContext, spec, srv, ctr) } func createDebugContainer( ctx context.Context, c8dClient *client.Client, containerID string, image client.Image, spec *machine.DebugContainerRunRequestSpec, cgroupPath string, ) (client.Container, error) { ociOpts := []oci.SpecOpts{ oci.WithDefaultSpec(), oci.WithDefaultUnixDevices, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostNamespace(specs.PIDNamespace), oci.WithHostNamespace(specs.IPCNamespace), oci.WithHostDevices, oci.WithAllDevicesAllowed, oci.WithHostHostsFile, oci.WithWriteableSysfs, oci.WithCapabilities(capability.AllGrantableCapabilities()), oci.WithHostResolvconf, oci.WithMounts([]specs.Mount{ // mount host / under /host { Destination: "/host", Type: "bind", Source: "/", Options: []string{"rbind", "rw"}, }, { Destination: "/sys", Type: "bind", Source: "/sys", Options: []string{"rbind", "rw"}, }, }), oci.WithSelinuxLabel(""), // SELinux will automatically transition the debug container into the proper context oci.WithApparmorProfile(""), oci.WithSeccompUnconfined, oci.WithImageConfig(image), oci.WithCgroup(cgroupPath), } if spec.GetTty() { ociOpts = append(ociOpts, oci.WithTTY) } if len(spec.Args) > 0 { ociOpts = append(ociOpts, oci.WithProcessArgs(spec.Args...)) } if len(spec.Env) > 0 { envVars := make([]string, 0, len(spec.Env)) for k, v := range spec.Env { envVars = append(envVars, fmt.Sprintf("%s=%s", k, v)) } ociOpts = append(ociOpts, oci.WithEnv(envVars)) } container, err := c8dClient.NewContainer(ctx, containerID, client.WithImage(image), client.WithNewSnapshot(containerID+"-snapshot", image), client.WithNewSpec(ociOpts...), ) if err != nil { return nil, fmt.Errorf("failed to create container: %v", err) } return container, nil } func runAndAttachContainer( ctx context.Context, detachedContext context.Context, spec *machine.DebugContainerRunRequestSpec, srv grpc.BidiStreamingServer[machine.DebugContainerRunRequest, machine.DebugContainerRunResponse], ctr client.Container, ) error { grpcStreamer, stdinR, stdoutW := newGrpcStreamWriter(srv) stdin := &containerd.StdinCloser{ Stdin: stdinR, Closer: make(chan struct{}), } cIoOpts := []cio.Opt{ cio.WithStreams(stdin, stdoutW, stdoutW), } if spec.GetTty() { cIoOpts = append(cIoOpts, cio.WithTerminal) } cIo := cio.NewCreator(cIoOpts...) task, err := ctr.NewTask(ctx, cIo) if err != nil { return fmt.Errorf("failed to create task: %v", err) } defer func() { _, err := task.Delete(detachedContext, client.WithProcessKill) if err != nil && !errdefs.IsNotFound(err) { log.Printf("debug container: failed to delete task: %s", err.Error()) } }() go stdin.WaitAndClose(ctx, task) if err := task.Start(ctx); err != nil { return fmt.Errorf("failed to start task: %v", err) } statusC, err := task.Wait(detachedContext) if err != nil { return fmt.Errorf("failed to wait for task: %v", err) } return grpcStreamer.stream(ctx, statusC, task) } func generateContainerID() (string, error) { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("failed to generate random ID: %w", err) } return fmt.Sprintf("debug-%s", hex.EncodeToString(b)), nil } ================================================ FILE: internal/app/images/images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package images implements machine.ImageService. package images import ( "context" "errors" "io" "maps" containerdapi "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/errdefs" "github.com/containerd/platforms" "github.com/distribution/reference" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" "github.com/siderolabs/talos/internal/app/internal/ctrhelper" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/progress" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // Service implements machine.ImageService. type Service struct { machine.UnimplementedImageServiceServer controller runtime.Controller logger *zap.Logger } // NewService creates a new ImageService. func NewService(controller runtime.Controller, logger *zap.Logger) *Service { return &Service{ controller: controller, logger: logger, } } // List images in the containerd. func (svc *Service) List(req *machine.ImageServiceListRequest, srv grpc.ServerStreamingServer[machine.ImageServiceListResponse]) error { ctx, _, client, err := ctrhelper.ContainerdInstanceHelper(srv.Context(), req.GetContainerd()) if err != nil { return err } //nolint:errcheck defer client.Close() images, err := client.ImageService().List(ctx) if err != nil { return err } for _, image := range images { item := &machine.ImageServiceListResponse{ Name: image.Name, Digest: image.Target.Digest.String(), CreatedAt: timestamppb.New(image.CreatedAt), Labels: maps.Clone(image.Labels), } size, err := image.Size(ctx, client.ContentStore(), platforms.Default()) if err == nil { item.Size = size } if err = srv.Send(item); err != nil { return err } } return nil } // Pull an image into the containerd. func (svc *Service) Pull(req *machine.ImageServicePullRequest, srv grpc.ServerStreamingServer[machine.ImageServicePullResponse]) error { ctx, _, client, err := ctrhelper.ContainerdInstanceHelper(srv.Context(), req.GetContainerd()) if err != nil { return err } //nolint:errcheck defer client.Close() img, err := image.Pull(ctx, cri.RegistryBuilder(svc.controller.Runtime().State().V1Alpha2().Resources()), svc.controller.Runtime().State().V1Alpha2().Resources(), client, req.GetImageRef(), image.WithSkipIfAlreadyPulled(), image.WithMaxNotFoundRetries(0), // return an error immediately if the image is not found image.WithProgressReporter(image.NewSimpleProgressReporter(func(lpp progress.LayerPullProgress) { srv.Send(&machine.ImageServicePullResponse{ //nolint:errcheck Response: &machine.ImageServicePullResponse_PullProgress{ PullProgress: &machine.ImageServicePullProgress{ LayerId: lpp.LayerID, Progress: &machine.ImageServicePullLayerProgress{ Status: machine.ImageServicePullLayerProgress_Status(lpp.Status), Elapsed: durationpb.New(lpp.Elapsed), Offset: lpp.Offset, Total: lpp.Total, }, }, }, }) })), ) if err != nil { if errdefs.IsNotFound(err) { return status.Errorf(codes.NotFound, "error pulling image: %s", err) } return err } pulledRef := img.Name() parsedRef, err := reference.ParseDockerRef(pulledRef) if err != nil { return status.Errorf(codes.Internal, "error parsing image reference: %s", err) } if _, ok := parsedRef.(reference.Canonical); !ok { // if the reference is not canonical, pin it pulledRef = parsedRef.Name() + "@" + img.Target().Digest.String() } return srv.Send(&machine.ImageServicePullResponse{ Response: &machine.ImageServicePullResponse_Name{ Name: pulledRef, }, }) } // Import an image from a stream (tarball). // //nolint:gocyclo func (svc *Service) Import(srv grpc.ClientStreamingServer[machine.ImageServiceImportRequest, machine.ImageServiceImportResponse]) error { msg, err := srv.Recv() if err != nil { return err } req := msg.GetContainerd() if req == nil { return status.Errorf(codes.InvalidArgument, "containerd instance is required") } ctx, _, client, err := ctrhelper.ContainerdInstanceHelper(srv.Context(), req) if err != nil { return err } defer client.Close() //nolint:errcheck r, w := io.Pipe() go func() { defer w.Close() //nolint:errcheck for { msg, err := srv.Recv() if err != nil { if errors.Is(err, io.EOF) { return } w.CloseWithError(err) return } chunk := msg.GetImageChunk() if chunk == nil { w.CloseWithError(errors.New("no image chunk")) return } if _, err := w.Write(chunk.GetBytes()); err != nil { w.CloseWithError(err) return } } }() images, err := client.Import(ctx, r) if err != nil { return status.Errorf(codes.Internal, "failed to import image: %v", err) } r.Close() //nolint:errcheck if len(images) == 0 { return status.Errorf(codes.InvalidArgument, "no images imported from archive") } imageName := images[0].Name for _, img := range images { image := containerdapi.NewImage(client, img) err = image.Unpack(ctx, "") if err != nil { return status.Errorf(codes.Internal, "failed to unpack image %s: %v", img.Name, err) } } img, err := client.GetImage(ctx, imageName) if err != nil { if errdefs.IsNotFound(err) { created, err := client.ImageService().Create(ctx, images[0]) if err != nil { return status.Errorf(codes.Internal, "failed to create image: %v", err) } imageName = created.Name img, err = client.GetImage(ctx, imageName) if err != nil { return status.Errorf(codes.Internal, "failed to get image: %v", err) } } } return srv.SendAndClose(&machine.ImageServiceImportResponse{ Name: img.Name(), }) } // Remove an image from the containerd. func (svc *Service) Remove(ctx context.Context, req *machine.ImageServiceRemoveRequest) (*emptypb.Empty, error) { ctx, _, client, err := ctrhelper.ContainerdInstanceHelper(ctx, req.GetContainerd()) if err != nil { return nil, err } //nolint:errcheck defer client.Close() err = client.ImageService().Delete(ctx, req.ImageRef, images.SynchronousDelete()) if err != nil { if errdefs.IsNotFound(err) { return nil, status.Errorf(codes.NotFound, "image %s not found", req.ImageRef) } return nil, status.Errorf(codes.Internal, "failed to remove image %s: %v", req.ImageRef, err) } return &emptypb.Empty{}, nil } ================================================ FILE: internal/app/images/verify.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package images import ( "context" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/verify" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // Verify an image signature against the configured verification policy. // // This endpoint is called by containerd before unpacking an image to ensure // the image meets the verification requirements configured in the machine config. // // If no verification policy is configured, all images are allowed by default. func (svc *Service) Verify(ctx context.Context, req *machine.ImageServiceVerifyRequest) (*machine.ImageServiceVerifyResponse, error) { // build resolver with custom auth if credentials are provided in the request var opts []func(*cri.RegistriesConfigSpec) if req.Credentials != nil { opts = append(opts, func(spec *cri.RegistriesConfigSpec) { if spec.RegistryAuths == nil { spec.RegistryAuths = make(map[string]*cri.RegistryAuthConfig) } spec.RegistryAuths[req.GetCredentials().GetHost()] = &cri.RegistryAuthConfig{ RegistryUsername: req.GetCredentials().Username, RegistryPassword: req.GetCredentials().Password, } }) } registries, err := cri.RegistryBuilder(svc.controller.Runtime().State().V1Alpha2().Resources(), opts...)(ctx) if err != nil { return nil, status.Errorf(codes.Internal, "failed to build registry configuration: %s", err) } resolver := image.NewResolver(registries) return verify.ImageSignature(ctx, svc.logger, svc.controller.Runtime().State().V1Alpha2().Resources(), resolver, req.GetImageRef()) } ================================================ FILE: internal/app/init/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package init implements booting process. package main import ( "errors" "fmt" "io/fs" "log" "os" "os/signal" "path/filepath" "runtime" "syscall" "time" "github.com/klauspost/cpuid/v2" "github.com/siderolabs/go-kmsg" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/mount/switchroot" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/rng" "github.com/siderolabs/talos/internal/pkg/secureboot" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/version" ) func init() { // Explicitly disable memory profiling to save around 1.4MiB of memory. runtime.MemProfileRate = 0 } //nolint:gocyclo func run() error { // Mount the pseudo devices. pseudo := mount.Pseudo(nil) if _, err := pseudo.Mount(); err != nil { return err } if _, err := mount.PseudoSub(nil).Mount(); err != nil { return err } // Setup logging to /dev/kmsg. if err := kmsg.SetupLogger(nil, "[talos] [initramfs]", nil); err != nil { return err } // Seed RNG. if err := rng.TPMSeed(); err != nil { // not making this fatal error log.Printf("failed to seed from the TPM: %s", err) } // extend PCR 11 with enter-initrd if err := tpm2.PCRExtend(constants.UKIPCR, []byte(secureboot.EnterInitrd)); err != nil { return fmt.Errorf("failed to extend PCR %d with enter-initrd: %v", constants.UKIPCR, err) } log.Printf("booting Talos %s", version.Tag) cpuInfo() // Mount the rootfs. if err := mountRootFS(); err != nil { return err } // Bind mount the usr/lib/firmware if needed. if err := bindMountFirmware(); err != nil { return err } // Bind mount /.extra if needed. if err := bindMountExtra(); err != nil { return err } // Switch into the new rootfs. log.Println("entering the rootfs") return switchroot.Switch(constants.NewRoot, pseudo) } func recovery() { // If panic is set in the kernel flags, we'll hang instead of rebooting. // But we still allow users to hit CTRL+ALT+DEL to try and restart when they're ready. // Listening for these signals also keep us from deadlocking the goroutine. if r := recover(); r != nil { log.Printf("recovered from: %+v\n", r) p := procfs.ProcCmdline().Get(constants.KernelParamPanic).First() if p != nil && *p == "0" { log.Printf("panic=0 kernel flag found. sleeping forever") exitSignal := make(chan os.Signal, 1) signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) <-exitSignal } for i := 10; i >= 0; i-- { log.Printf("rebooting in %d seconds\n", i) time.Sleep(1 * time.Second) } } //nolint:errcheck unix.Reboot(unix.LINUX_REBOOT_CMD_RESTART) } //nolint:gocyclo func mountRootFS() error { log.Println("mounting the rootfs") var extensionsConfig extensions.Config if err := extensionsConfig.Read(constants.ExtensionsConfigFile); err != nil { if !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("failed to read extensions config: %w", err) } } // if no extensions found use plain squashfs mount if len(extensionsConfig.Layers) == 0 { squashfs, err := mount.Squashfs(constants.NewRoot, "/"+constants.RootfsAsset, log.Printf) if err != nil { return fmt.Errorf("failed to initialize squashfs: %w", err) } if _, err := squashfs.Mount(); err != nil { return fmt.Errorf("failed to mount squashfs: %w", err) } return nil } // otherwise compose overlay mounts type layer struct { name string image string } var ( layers []layer squashfs mount.Managers ) // going in the inverse order as earlier layers are overlayed on top of the latter ones for i := len(extensionsConfig.Layers) - 1; i >= 0; i-- { layers = append(layers, layer{ name: fmt.Sprintf("layer%d", i), image: extensionsConfig.Layers[i].Image, }) log.Printf("enabling system extension %s %s", extensionsConfig.Layers[i].Metadata.Name, extensionsConfig.Layers[i].Metadata.Version) } layers = append(layers, layer{ name: "root", image: "/" + constants.RootfsAsset, }) overlays := make([]string, 0, len(layers)) for _, layer := range layers { target := filepath.Join(constants.ExtensionLayers, layer.name) manager, err := mount.Squashfs(target, layer.image, nil) if err != nil { return err } squashfs = append(squashfs, manager) overlays = append(overlays, target) } squashfsUnmounter, err := squashfs.Mount() if err != nil { return err } overlayPoint := mount.NewReadOnlyOverlay( overlays, constants.NewRoot, nil, mount.WithShared(), ) _, err = overlayPoint.Mount() if err != nil { return err } if err = squashfsUnmounter(); err != nil { return err } return mount.BindReadonly(constants.ExtensionsConfigFile, filepath.Join(constants.NewRoot, constants.ExtensionsRuntimeConfigFile)) } func bindMountFirmware() error { firmwarePath := quirks.New("").FirmwarePath() if _, err := os.Stat(firmwarePath); err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return err } log.Printf("bind mounting %s", firmwarePath) return mount.BindReadonly(firmwarePath, filepath.Join(constants.NewRoot, firmwarePath)) } func bindMountExtra() error { if _, err := os.Stat(constants.SDStubDynamicInitrdPath); err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return err } log.Printf("bind mounting %s", constants.SDStubDynamicInitrdPath) return mount.BindReadonly(constants.SDStubDynamicInitrdPath, filepath.Join(constants.NewRoot, constants.SDStubDynamicInitrdPath)) } func cpuInfo() { log.Printf("CPU: %s, %d core(s), %d thread(s) per core", cpuid.CPU.BrandName, cpuid.CPU.PhysicalCores, cpuid.CPU.ThreadsPerCore) if runtime.GOARCH == "amd64" { log.Printf("x86_64 microarchitecture level: %d", cpuid.CPU.X64Level()) if cpuid.CPU.X64Level() < constants.MinimumGOAMD64Level { if cpuid.CPU.VM() { log.Printf("it might be that the VM is configured with an older CPU model, please check the VM configuration") } log.Printf("x86_64 microarchitecture level %d or higher is required, halting", constants.MinimumGOAMD64Level) time.Sleep(365 * 24 * time.Hour) } } } func main() { defer recovery() if err := run(); err != nil { panic(fmt.Errorf("early boot failed: %w", err)) } // We should never reach this point if things are working as intended. panic(errors.New("unknown error")) } ================================================ FILE: internal/app/internal/ctrhelper/ctrhelper.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ctrhelper provides helpers for container-related APIs. package ctrhelper import ( "context" containerdapi "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ContainerdInstanceHelper helps to create containerd client and context from the given ContainerdInstance. // // This function returns: // - inbound context annotated with the appropriate containerd namespace // - detached (context.Background()) context with the appropriate containerd namespace // - containerd client func ContainerdInstanceHelper(ctx context.Context, req *common.ContainerdInstance) (context.Context, context.Context, *containerdapi.Client, error) { var ( containerdAddress string containerdNamespace string ) switch req.GetDriver() { case common.ContainerDriver_CONTAINERD: containerdAddress = constants.SystemContainerdAddress case common.ContainerDriver_CRI: containerdAddress = constants.CRIContainerdAddress default: return nil, nil, nil, status.Errorf(codes.InvalidArgument, "invalid containerd driver %s", req.GetDriver()) } switch req.GetNamespace() { case common.ContainerdNamespace_NS_CRI: containerdNamespace = constants.K8sContainerdNamespace case common.ContainerdNamespace_NS_SYSTEM: containerdNamespace = constants.SystemContainerdNamespace case common.ContainerdNamespace_NS_UNKNOWN: fallthrough default: return nil, nil, nil, status.Errorf(codes.InvalidArgument, "invalid containerd namespace %s", req.GetNamespace()) } if req.GetDriver() == common.ContainerDriver_CONTAINERD && req.GetNamespace() == common.ContainerdNamespace_NS_CRI { return nil, nil, nil, status.Errorf(codes.InvalidArgument, "cannot use CRI namespace with containerd driver") } client, err := containerdapi.New(containerdAddress) if err != nil { return ctx, nil, nil, status.Errorf(codes.Unavailable, "error connecting to containerd: %s", err) } return namespaces.WithNamespace(ctx, containerdNamespace), namespaces.WithNamespace(context.Background(), containerdNamespace), client, nil } ================================================ FILE: internal/app/internal/machinehelper/machinehelper.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package machinehelper provides helper functions for machine-related information. package machinehelper import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // CheckControlplane implements the controlplane machine type check. // // This works for API handlers. func CheckControlplane(ctx context.Context, resources state.State, apiName string) error { machineType, err := safe.StateGetByID[*config.MachineType](ctx, resources, config.MachineTypeID) if err != nil { if state.IsNotFoundError(err) { return status.Errorf(codes.Unimplemented, "machine type is not set, cannot use %s API", apiName) } return fmt.Errorf("failed to get machine type: %w", err) } if !machineType.MachineType().IsControlPlane() { return status.Errorf(codes.Unimplemented, "%s is only available on control plane nodes", apiName) } return nil } ================================================ FILE: internal/app/lifecycle/container.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package lifecycle import ( "bytes" "context" "crypto/rand" "encoding/hex" "fmt" "io" "log" "os" "strconv" "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/leases" "github.com/containerd/containerd/v2/pkg/cio" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/errdefs" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/internal/ctrhelper" containerdrunner "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/install" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/api/common" configcore "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) func generateContainerID() (string, error) { b := make([]byte, 8) if _, err := rand.Read(b); err != nil { return "", fmt.Errorf("failed to generate random ID: %w", err) } return fmt.Sprintf("installer-%s", hex.EncodeToString(b)), nil } // sendFunc is a callback to stream a message line back to the client. type sendFunc func(msg string) error // sendExitCodeFunc is a callback to stream the exit code back to the client. type sendExitCodeFunc func(exitCode int32) error // containerRunConfig holds all parameters needed to create and run the installer container. type containerRunConfig struct { containerdInst *common.ContainerdInstance imageRef string disk string platform string cfgContainer configcore.Container opts []install.Option send sendFunc sendExitCode sendExitCodeFunc } // runInstallerContainer creates and runs the installer container synchronously, // streaming output lines back to the client via the send callback. // //nolint:gocyclo,cyclop func runInstallerContainer(ctx context.Context, rc *containerRunConfig) error { options := install.Options{Pull: true} //nolint:staticcheck if err := options.Apply(rc.opts...); err != nil { return fmt.Errorf("failed to apply install options: %w", err) } // connect to containerd ctx, detachedCtx, c8dClient, err := ctrhelper.ContainerdInstanceHelper(ctx, rc.containerdInst) if err != nil { return err } defer c8dClient.Close() //nolint:errcheck l, err := c8dClient.LeasesService().Create(ctx, leases.WithRandomID()) if err != nil { return fmt.Errorf("failed to create lease: %w", err) } defer func() { if err := c8dClient.LeasesService().Delete(detachedCtx, l, leases.SynchronousDelete); err != nil { log.Printf("failed to delete lease %s: %v", l.ID, err) } }() ctx = leases.WithLease(ctx, l.ID) img, err := c8dClient.GetImage(ctx, rc.imageRef) if err != nil { return fmt.Errorf("installer image %q not found in containerd store: %w", rc.imageRef, err) } // build container spec mounts := buildMounts() args := buildInstallerArgs(rc.disk, rc.platform, &options) specOpts := buildSpecOpts(img, args, mounts) containerID, err := generateContainerID() if err != nil { return fmt.Errorf("failed to generate container ID: %v", err) } // create container ctr, err := c8dClient.NewContainer(ctx, containerID, client.WithImage(img), client.WithNewSnapshot(containerID, img), client.WithNewSpec(specOpts...), ) if err != nil { return fmt.Errorf("failed to create container: %w", err) } defer func() { if cleanupErr := ctr.Delete(detachedCtx, client.WithSnapshotCleanup); cleanupErr != nil { log.Printf("failed to delete container %s: %v", ctr.ID(), cleanupErr) } }() // set up I/O: stdout/stderr -> pipe -> stream to client; stdin <- config bytes stdoutR, stdoutW := io.Pipe() var stdinReader interface { io.Reader WaitAndClose(context.Context, client.Task) } if rc.cfgContainer != nil { configBytes, cfgErr := rc.cfgContainer.Bytes() if cfgErr != nil { return fmt.Errorf("failed to serialize config: %w", cfgErr) } stdinReader = &containerdrunner.StdinCloser{ Stdin: bytes.NewReader(configBytes), Closer: make(chan struct{}), } } creator := cio.NewCreator(cio.WithStreams(stdinReader, stdoutW, stdoutW)) task, err := ctr.NewTask(ctx, creator) if err != nil { stdoutW.Close() //nolint:errcheck return fmt.Errorf("failed to create task: %w", err) } defer func() { if _, delErr := task.Delete(detachedCtx, client.WithProcessKill); delErr != nil && !errdefs.IsNotFound(delErr) { log.Printf("failed to delete task: %v", delErr) } }() if stdinReader != nil { go stdinReader.WaitAndClose(ctx, task) } if err := task.Start(ctx); err != nil { stdoutW.Close() //nolint:errcheck return fmt.Errorf("failed to start task: %w", err) } statusC, err := task.Wait(detachedCtx) if err != nil { stdoutW.Close() //nolint:errcheck return fmt.Errorf("failed to wait for task: %w", err) } // stream output in a goroutine sendDone := make(chan error, 1) go func() { sendDone <- streamOutput(stdoutR, rc.send) }() // wait for task to exit exitStatus := <-statusC // close the write end so the reader gets EOF stdoutW.Close() //nolint:errcheck // wait for send loop to finish if sendErr := <-sendDone; sendErr != nil { log.Printf("error streaming output: %v", sendErr) } if exitStatus.Error() != nil { return fmt.Errorf("task exited with error: %w", exitStatus.Error()) } exitCode := int32(exitStatus.ExitCode()) // send exit code to client if err := rc.sendExitCode(exitCode); err != nil { return fmt.Errorf("failed to send exit code: %w", err) } if exitCode != 0 { log.Printf("installer container exited with code %d", exitCode) } return nil } // streamOutput reads from r line by line and sends each line via the send callback. func streamOutput(r io.Reader, send sendFunc) error { buf := make([]byte, 512) for { n, err := r.Read(buf) if n > 0 { if sendErr := send(string(buf[:n])); sendErr != nil { return fmt.Errorf("failed to send message: %w", sendErr) } } if err != nil { if err == io.EOF { return nil } return fmt.Errorf("failed to read output: %w", err) } } } // buildMounts constructs the OCI mounts for the installer container. func buildMounts() []specs.Mount { mounts := []specs.Mount{ {Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"rbind", "rshared", "rw"}}, } if _, err := os.Stat(constants.MachineSocketPath); err == nil { mounts = append(mounts, specs.Mount{ Type: "bind", Destination: constants.MachineSocketPath, Source: constants.MachineSocketPath, Options: []string{"rbind", "rshared", "ro"}, }) } if _, err := os.Stat(constants.EFIVarsMountPoint); err == nil { mounts = append(mounts, specs.Mount{ Type: "efivarfs", Source: "efivarfs", Destination: constants.EFIVarsMountPoint, Options: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}, }) } if _, err := os.Stat(constants.SDStubDynamicInitrdPath); err == nil { mounts = append(mounts, specs.Mount{ Type: "bind", Destination: constants.SDStubDynamicInitrdPath, Source: constants.SDStubDynamicInitrdPath, Options: []string{"rbind", "rshared", "ro"}, }) } return mounts } // buildInstallerArgs constructs the command-line arguments for the installer binary. func buildInstallerArgs(disk, platform string, options *install.Options) []string { config := constants.ConfigNone if c := procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); c != nil { config = *c } args := []string{ "/bin/installer", "install", "--disk=" + disk, "--platform=" + platform, "--config=" + config, "--upgrade=" + strconv.FormatBool(options.Upgrade), "--force=" + strconv.FormatBool(options.Force), "--zero=" + strconv.FormatBool(options.Zero), } for _, arg := range options.ExtraKernelArgs { args = append(args, "--extra-kernel-arg", arg) } for _, preservedArg := range []string{ constants.KernelParamSideroLink, constants.KernelParamEventsSink, constants.KernelParamLoggingKernel, constants.KernelParamEquinixMetalEvents, constants.KernelParamAuditdDisabled, constants.KernelParamDashboardDisabled, constants.KernelParamNetIfnames, constants.KernelParamEnforceModuleSigVerify, } { if c := procfs.ProcCmdline().Get(preservedArg).First(); c != nil { args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", preservedArg, *c)) } } return args } // buildSpecOpts constructs the OCI spec options for the installer container. func buildSpecOpts(img client.Image, args []string, mounts []specs.Mount) []oci.SpecOpts { specOpts := []oci.SpecOpts{ oci.WithImageConfig(img), oci.WithProcessArgs(args...), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostNamespace(specs.PIDNamespace), oci.WithMounts(mounts), oci.WithHostHostsFile, oci.WithHostResolvconf, oci.WithParentCgroupDevices, oci.WithCapabilities(capability.AllGrantableCapabilities()), oci.WithMaskedPaths(nil), oci.WithReadonlyPaths(nil), oci.WithWriteableSysfs, oci.WithWriteableCgroupfs, oci.WithApparmorProfile(""), oci.WithSeccompUnconfined, oci.WithAllDevicesAllowed, } if selinux.IsEnabled() { specOpts = append(specOpts, oci.WithSelinuxLabel(constants.SelinuxLabelInstaller)) } return specOpts } ================================================ FILE: internal/app/lifecycle/lifecycle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package lifecycle implements machine.LifecycleService. package lifecycle import ( "fmt" "path/filepath" "sync" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/install" "github.com/siderolabs/talos/pkg/machinery/api/machine" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" crires "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // Service implements machine.LifecycleService. type Service struct { machine.UnimplementedLifecycleServiceServer lock sync.Mutex runtime runtime.Runtime logger *zap.Logger } // NewService creates a new instance of the lifecycle service. func NewService(runtime runtime.Runtime, logger *zap.Logger) *Service { return &Service{ lock: sync.Mutex{}, runtime: runtime, logger: logger.With(zap.String("service", "lifecycle")), } } // Install handles the installation of the machine. // It ensures that only one installation or upgrade can occur at a time by using a mutex lock. // //nolint:gocyclo func (s *Service) Install(req *machine.LifecycleServiceInstallRequest, ss grpc.ServerStreamingServer[machine.LifecycleServiceInstallResponse]) error { if s.runtime.State().Platform().Mode().IsAgent() { return status.Error(codes.Unimplemented, "API is not implemented in agent mode") } if err := s.checkSupported(runtime.Upgrade); err != nil { return err } ctx := ss.Context() if !s.lock.TryLock() { return status.Error(codes.FailedPrecondition, "another installation/upgrade is already in progress") } defer s.lock.Unlock() if s.runtime.State().Platform().Mode().InContainer() { return status.Error(codes.FailedPrecondition, "installation is not supported in container mode") } if s.runtime.State().Machine().Installed() { return status.Error(codes.AlreadyExists, "machine is already installed") } if err := crires.WaitForImageCache(ctx, s.runtime.State().V1Alpha2().Resources()); err != nil { return status.Error(codes.Internal, fmt.Sprintf("failed to wait for the image cache: %v", err)) } installerImage := req.GetSource().GetImageName() if installerImage == "" { return status.Error(codes.InvalidArgument, "installer image name is required") } disk := req.GetDestination().GetDisk() if disk == "" { return status.Error(codes.InvalidArgument, "destination disk is required") } targetDisk, err := filepath.EvalSymlinks(disk) if err != nil { return status.Error(codes.InvalidArgument, fmt.Sprintf("invalid disk path: %v", err)) } s.logger.Info("starting installation", zap.String("installer_image", installerImage), zap.String("disk", targetDisk)) //nolint:dupl err = runInstallerContainer(ctx, &containerRunConfig{ containerdInst: req.GetContainerd(), imageRef: installerImage, disk: targetDisk, platform: s.runtime.State().Platform().Name(), cfgContainer: s.runtime.ConfigContainer(), opts: []install.Option{ install.WithForce(true), install.WithZero(false), }, send: func(msg string) error { s.logger.Info("installation progress", zap.String("message", msg)) return ss.Send(&machine.LifecycleServiceInstallResponse{ Progress: &machine.LifecycleServiceInstallProgress{ Response: &machine.LifecycleServiceInstallProgress_Message{ Message: msg, }, }, }) }, sendExitCode: func(exitCode int32) error { if exitCode == 0 { s.logger.Info("installation completed successfully", zap.Int32("exit_code", exitCode)) } else { s.logger.Error("installation failed", zap.Int32("exit_code", exitCode)) } return ss.Send(&machine.LifecycleServiceInstallResponse{ Progress: &machine.LifecycleServiceInstallProgress{ Response: &machine.LifecycleServiceInstallProgress_ExitCode{ ExitCode: exitCode, }, }, }) }, }) if err != nil { return status.Error(codes.Internal, fmt.Sprintf("installation failed: %v", err)) } return nil } // Upgrade handles the upgrade of the machine. // It ensures that only one installation or upgrade can occur at a time by using a mutex lock. // //nolint:gocyclo func (s *Service) Upgrade(req *machine.LifecycleServiceUpgradeRequest, ss grpc.ServerStreamingServer[machine.LifecycleServiceUpgradeResponse]) error { if s.runtime.State().Platform().Mode().IsAgent() { return status.Error(codes.Unimplemented, "API is not implemented in agent mode") } if err := s.checkSupported(runtime.Upgrade); err != nil { return err } ctx := ss.Context() if !s.lock.TryLock() { return status.Error(codes.FailedPrecondition, "another installation/upgrade is already in progress") } defer s.lock.Unlock() if s.runtime.State().Platform().Mode().InContainer() { return status.Error(codes.FailedPrecondition, "upgrade is not supported in container mode") } if !s.runtime.State().Machine().Installed() { return status.Error(codes.FailedPrecondition, "machine is not installed") } installerImage := req.GetSource().GetImageName() if installerImage == "" { return status.Error(codes.InvalidArgument, "installer image name is required") } systemDisk, err := blockres.GetSystemDisk(ctx, s.runtime.State().V1Alpha2().Resources()) if err != nil { return status.Error(codes.Internal, fmt.Sprintf("failed to get system disk: %v", err)) } if systemDisk == nil { return status.Error(codes.Internal, "system disk not found") } devname := systemDisk.DevPath s.logger.Info("starting upgrade", zap.String("installer_image", installerImage), zap.String("disk", devname)) //nolint:dupl err = runInstallerContainer(ctx, &containerRunConfig{ containerdInst: req.GetContainerd(), imageRef: installerImage, disk: devname, platform: s.runtime.State().Platform().Name(), cfgContainer: s.runtime.ConfigContainer(), opts: []install.Option{ install.WithUpgrade(true), install.WithForce(false), }, send: func(msg string) error { s.logger.Info("upgrade progress", zap.String("message", msg)) return ss.Send(&machine.LifecycleServiceUpgradeResponse{ Progress: &machine.LifecycleServiceInstallProgress{ Response: &machine.LifecycleServiceInstallProgress_Message{ Message: msg, }, }, }) }, sendExitCode: func(exitCode int32) error { if exitCode == 0 { s.logger.Info("upgrade completed successfully", zap.Int32("exit_code", exitCode)) } else { s.logger.Error("upgrade failed", zap.Int32("exit_code", exitCode)) } return ss.Send(&machine.LifecycleServiceUpgradeResponse{ Progress: &machine.LifecycleServiceInstallProgress{ Response: &machine.LifecycleServiceInstallProgress_ExitCode{ ExitCode: exitCode, }, }, }) }, }) if err != nil { return status.Error(codes.Internal, fmt.Sprintf("upgrade failed: %v", err)) } return nil } func (s *Service) checkSupported(feature runtime.ModeCapability) error { mode := s.runtime.State().Platform().Mode() if !mode.Supports(feature) { return status.Errorf(codes.FailedPrecondition, "method is not supported in %s mode", mode.String()) } return nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package runtime provides the runtime implementation. package runtime import ( "context" "fmt" "log" "net/netip" "slices" "strings" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/app/internal/machinehelper" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/cluster/check" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" clusterapi "github.com/siderolabs/talos/pkg/machinery/api/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" clusterres "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // HealthCheck implements the cluster.ClusterServer interface. func (s *Server) HealthCheck(in *clusterapi.HealthCheckRequest, srv clusterapi.ClusterService_HealthCheckServer) error { if err := machinehelper.CheckControlplane(srv.Context(), s.Controller.Runtime().State().V1Alpha2().Resources(), "cluster health check"); err != nil { return err } clientProvider := cluster.NewLocalClientProvider( s.Controller.Runtime().State().V1Alpha2().Resources(), // use talosconfig with same roles as incoming request for local communication authz.GetRoles(srv.Context()), ) defer clientProvider.Close() //nolint:errcheck k8sProvider := &cluster.KubernetesClient{ ClientProvider: clientProvider, ForceEndpoint: in.GetClusterInfo().GetForceEndpoint(), } defer k8sProvider.K8sClose() //nolint:errcheck checkCtx, checkCtxCancel := context.WithTimeout(srv.Context(), in.WaitTimeout.AsDuration()) defer checkCtxCancel() r := s.Controller.Runtime() clusterInfo, err := buildClusterInfo(checkCtx, in, r, *k8sProvider) if err != nil { return err } state := struct { cluster.ClientProvider cluster.K8sProvider cluster.Info }{ ClientProvider: clientProvider, K8sProvider: k8sProvider, Info: clusterInfo, } nodeInternalIPs := xslices.Map(clusterInfo.Nodes(), func(info cluster.NodeInfo) string { return info.InternalIP.String() }) if err := srv.Send(&clusterapi.HealthCheckProgress{ Message: fmt.Sprintf("discovered nodes: %q", nodeInternalIPs), }); err != nil { return err } return check.Wait(checkCtx, &state, append(check.DefaultClusterChecks(), check.ExtraClusterChecks()...), &healthReporter{srv: srv}) } type healthReporter struct { srv clusterapi.ClusterService_HealthCheckServer lastLine string } func (hr *healthReporter) Update(condition conditions.Condition) { line := fmt.Sprintf("waiting for %s", condition) if line != hr.lastLine { hr.srv.Send(&clusterapi.HealthCheckProgress{ //nolint:errcheck Message: strings.TrimSpace(line), }) hr.lastLine = line } } type clusterState struct { nodeInfos []cluster.NodeInfo nodeInfosByType map[machine.Type][]cluster.NodeInfo } func (cl *clusterState) Nodes() []cluster.NodeInfo { return cl.nodeInfos } func (cl *clusterState) NodesByType(t machine.Type) []cluster.NodeInfo { return cl.nodeInfosByType[t] } func (cl *clusterState) String() string { return fmt.Sprintf("control plane: %q, worker: %q", xslices.Map(cl.nodeInfosByType[machine.TypeControlPlane], func(info cluster.NodeInfo) string { return info.InternalIP.String() }), xslices.Map(cl.nodeInfosByType[machine.TypeWorker], func(info cluster.NodeInfo) string { return info.InternalIP.String() })) } //nolint:gocyclo func buildClusterInfo(ctx context.Context, req *clusterapi.HealthCheckRequest, r runtime.Runtime, cli cluster.KubernetesClient, ) (cluster.Info, error) { controlPlaneNodes := req.GetClusterInfo().GetControlPlaneNodes() workerNodes := req.GetClusterInfo().GetWorkerNodes() // if the node list is explicitly provided, use it if len(controlPlaneNodes) != 0 || len(workerNodes) != 0 { controlPlaneNodeInfos, err := cluster.IPsToNodeInfos(controlPlaneNodes) if err != nil { return nil, err } workerNodeInfos, err := cluster.IPsToNodeInfos(workerNodes) if err != nil { return nil, err } return &clusterState{ nodeInfos: slices.Concat(controlPlaneNodeInfos, workerNodeInfos), nodeInfosByType: map[machine.Type][]cluster.NodeInfo{ machine.TypeControlPlane: controlPlaneNodeInfos, machine.TypeWorker: workerNodeInfos, }, }, nil } // try to discover nodes using discovery service discoveryMemberList, err := getDiscoveryMemberList(ctx, r) if err != nil { log.Printf("discovery service returned error: %v\n", err) } // discovery service returned some nodes, use them if len(discoveryMemberList) > 0 { return check.NewDiscoveredClusterInfo(discoveryMemberList) } // as the last resort, get the nodes from the cluster itself k8sCli, err := cli.K8sClient(ctx) if err != nil { return nil, err } nodeList, err := k8sCli.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } nodeInfos := make([]cluster.NodeInfo, len(nodeList.Items)) nodeInfosByType := map[machine.Type][]cluster.NodeInfo{} for i, node := range nodeList.Items { nodeInfo, err2 := k8sNodeToNodeInfo(&node) if err2 != nil { return nil, err2 } if isControlPlaneNode(&node) { nodeInfosByType[machine.TypeControlPlane] = append(nodeInfosByType[machine.TypeControlPlane], *nodeInfo) } else { nodeInfosByType[machine.TypeWorker] = append(nodeInfosByType[machine.TypeWorker], *nodeInfo) } nodeInfos[i] = *nodeInfo } return &clusterState{ nodeInfos: nodeInfos, nodeInfosByType: nodeInfosByType, }, nil } func k8sNodeToNodeInfo(node *corev1.Node) (*cluster.NodeInfo, error) { if node == nil { return nil, nil } var internalIP netip.Addr ips := make([]netip.Addr, 0, len(node.Status.Addresses)) for _, address := range node.Status.Addresses { switch address.Type { //nolint:exhaustive case corev1.NodeInternalIP: ip, err := netip.ParseAddr(address.Address) if err != nil { return nil, err } internalIP = ip ips = append(ips, ip) case corev1.NodeExternalIP: ip, err := netip.ParseAddr(address.Address) if err != nil { return nil, err } ips = append(ips, ip) } } return &cluster.NodeInfo{ InternalIP: internalIP, IPs: ips, }, nil } func getDiscoveryMemberList(ctx context.Context, runtime runtime.Runtime) ([]*clusterres.Member, error) { res := runtime.State().V1Alpha2().Resources() list, err := safe.StateListAll[*clusterres.Member](ctx, res) if err != nil { return nil, err } return safe.ToSlice(list, func(m *clusterres.Member) *clusterres.Member { return m }), nil } func isControlPlaneNode(node *corev1.Node) bool { for key := range node.Labels { if key == constants.LabelNodeRoleControlPlane { return true } } return false } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" containerdapi "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/errdefs" "github.com/containerd/platforms" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) func containerdNamespaceHelper(ctx context.Context, ns common.ContainerdNamespace) (context.Context, error) { var namespaceName string switch ns { case common.ContainerdNamespace_NS_CRI: namespaceName = constants.K8sContainerdNamespace case common.ContainerdNamespace_NS_SYSTEM: namespaceName = constants.SystemContainerdNamespace case common.ContainerdNamespace_NS_UNKNOWN: fallthrough default: return nil, status.Errorf(codes.InvalidArgument, "invalid namespace %s", ns) } return namespaces.WithNamespace(ctx, namespaceName), nil } // ImageList lists the images in the CRI. // // Deprecated: use ImageService.List instead. func (s *Server) ImageList(req *machine.ImageListRequest, srv machine.MachineService_ImageListServer) error { client, err := containerdapi.New(constants.CRIContainerdAddress) if err != nil { return status.Errorf(codes.Unavailable, "error connecting to containerd: %s", err) } //nolint:errcheck defer client.Close() ctx, err := containerdNamespaceHelper(srv.Context(), req.Namespace) if err != nil { return err } images, err := client.ImageService().List(ctx) if err != nil { return err } for _, image := range images { item := &machine.ImageListResponse{ Name: image.Name, Digest: image.Target.Digest.String(), CreatedAt: timestamppb.New(image.CreatedAt), } size, err := image.Size(ctx, client.ContentStore(), platforms.Default()) if err == nil { item.Size = size } if err = srv.Send(item); err != nil { return err } } return nil } // ImagePull pulls an image to the CRI. // // Deprecated: use ImageService.Pull instead. func (s *Server) ImagePull(ctx context.Context, req *machine.ImagePullRequest) (*machine.ImagePullResponse, error) { client, err := containerdapi.New(constants.CRIContainerdAddress) if err != nil { return nil, status.Errorf(codes.Unavailable, "error connecting to containerd: %s", err) } //nolint:errcheck defer client.Close() ctx, err = containerdNamespaceHelper(ctx, req.Namespace) if err != nil { return nil, err } _, err = image.Pull(ctx, cri.RegistryBuilder(s.Controller.Runtime().State().V1Alpha2().Resources()), s.Controller.Runtime().State().V1Alpha2().Resources(), client, req.Reference, image.WithSkipIfAlreadyPulled(), image.WithMaxNotFoundRetries(0), // return an error immediately if the image is not found ) if err != nil { if errdefs.IsNotFound(err) { return nil, status.Errorf(codes.NotFound, "error pulling image: %s", err) } return nil, err } return &machine.ImagePullResponse{ Messages: []*machine.ImagePull{ {}, }, }, nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_inspect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "google.golang.org/protobuf/types/known/emptypb" inspectapi "github.com/siderolabs/talos/pkg/machinery/api/inspect" ) // InspectServer implements InspectService API. type InspectServer struct { inspectapi.UnimplementedInspectServiceServer server *Server } // ControllerRuntimeDependencies implements inspect.InspectService interface. func (s *InspectServer) ControllerRuntimeDependencies(ctx context.Context, in *emptypb.Empty) (*inspectapi.ControllerRuntimeDependenciesResponse, error) { graph, err := s.server.Controller.V1Alpha2().DependencyGraph() if err != nil { return nil, fmt.Errorf("error fetching dependency graph: %w", err) } edges := make([]*inspectapi.ControllerDependencyEdge, 0, len(graph.Edges)) for i := range graph.Edges { var edgeType inspectapi.DependencyEdgeType switch graph.Edges[i].EdgeType { case controller.EdgeOutputExclusive: edgeType = inspectapi.DependencyEdgeType_OUTPUT_EXCLUSIVE case controller.EdgeOutputShared: edgeType = inspectapi.DependencyEdgeType_OUTPUT_SHARED case controller.EdgeInputStrong: edgeType = inspectapi.DependencyEdgeType_INPUT_STRONG case controller.EdgeInputWeak: edgeType = inspectapi.DependencyEdgeType_INPUT_WEAK case controller.EdgeInputDestroyReady: edgeType = inspectapi.DependencyEdgeType_INPUT_DESTROY_READY case controller.EdgeInputQPrimary, controller.EdgeInputQMapped, controller.EdgeInputQMappedDestroyReady: return nil, fmt.Errorf("unexpected edge type: %v", graph.Edges[i].EdgeType) } edges = append(edges, &inspectapi.ControllerDependencyEdge{ ControllerName: graph.Edges[i].ControllerName, EdgeType: edgeType, ResourceNamespace: graph.Edges[i].ResourceNamespace, ResourceType: graph.Edges[i].ResourceType, ResourceId: graph.Edges[i].ResourceID, }) } return &inspectapi.ControllerRuntimeDependenciesResponse{ Messages: []*inspectapi.ControllerRuntimeDependency{ { Edges: edges, }, }, }, nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "io/fs" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // MetaWrite implements the machine.MachineServer interface. func (s *Server) MetaWrite(ctx context.Context, req *machine.MetaWriteRequest) (*machine.MetaWriteResponse, error) { if err := s.checkSupported(runtime.MetaKV); err != nil { return nil, err } if uint32(uint8(req.Key)) != req.Key { return nil, status.Errorf(codes.InvalidArgument, "key must be a uint8") } ok, err := s.Controller.Runtime().State().Machine().Meta().SetTagBytes(ctx, uint8(req.Key), req.Value) if err != nil { return nil, err } if !ok { // META overflowed return nil, status.Errorf(codes.ResourceExhausted, "meta write failed") } err = s.Controller.Runtime().State().Machine().Meta().Flush() if err != nil && !errors.Is(err, fs.ErrNotExist) { // ignore not exist error, as it's possible that the meta partition is not created yet return nil, err } return &machine.MetaWriteResponse{ Messages: []*machine.MetaWrite{ {}, }, }, nil } // MetaDelete implements the machine.MachineServer interface. func (s *Server) MetaDelete(ctx context.Context, req *machine.MetaDeleteRequest) (*machine.MetaDeleteResponse, error) { if err := s.checkSupported(runtime.MetaKV); err != nil { return nil, err } if uint32(uint8(req.Key)) != req.Key { return nil, status.Errorf(codes.InvalidArgument, "key must be a uint8") } ok, err := s.Controller.Runtime().State().Machine().Meta().DeleteTag(ctx, uint8(req.Key)) if err != nil { return nil, err } if !ok { // META key not found return nil, status.Errorf(codes.NotFound, "meta key not found") } err = s.Controller.Runtime().State().Machine().Meta().Flush() if err != nil && !errors.Is(err, fs.ErrNotExist) { // ignore not exist error, as it's possible that the meta partition is not created yet return nil, err } return &machine.MetaDeleteResponse{ Messages: []*machine.MetaDelete{ {}, }, }, nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_monitoring.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "bufio" "context" "os" "strconv" "strings" "github.com/prometheus/procfs" "github.com/prometheus/procfs/sysfs" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // Hostname implements the machine.MachineServer interface. func (s *Server) Hostname(ctx context.Context, in *emptypb.Empty) (*machine.HostnameResponse, error) { hostname, err := os.Hostname() if err != nil { return nil, err } reply := &machine.HostnameResponse{ Messages: []*machine.Hostname{ { Hostname: hostname, }, }, } return reply, nil } // LoadAvg implements the machine.MachineServer interface. func (s *Server) LoadAvg(ctx context.Context, in *emptypb.Empty) (*machine.LoadAvgResponse, error) { fs, err := procfs.NewDefaultFS() if err != nil { return nil, err } loadAvg, err := fs.LoadAvg() if err != nil { return nil, err } reply := &machine.LoadAvgResponse{ Messages: []*machine.LoadAvg{ { Load1: loadAvg.Load1, Load5: loadAvg.Load5, Load15: loadAvg.Load15, }, }, } return reply, nil } // SystemStat implements the machine.MachineServer interface. func (s *Server) SystemStat(ctx context.Context, in *emptypb.Empty) (*machine.SystemStatResponse, error) { fs, err := procfs.NewDefaultFS() if err != nil { return nil, err } stat, err := fs.Stat() if err != nil { return nil, err } translateCPUStat := func(in procfs.CPUStat) *machine.CPUStat { return &machine.CPUStat{ User: in.User, Nice: in.Nice, System: in.System, Idle: in.Idle, Iowait: in.Iowait, Irq: in.IRQ, SoftIrq: in.SoftIRQ, Steal: in.Steal, Guest: in.Guest, GuestNice: in.GuestNice, } } translateListOfCPUStat := func(in map[int64]procfs.CPUStat) []*machine.CPUStat { maxCore := int64(-1) for core := range in { maxCore = max(maxCore, core) } slc := make([]*machine.CPUStat, maxCore+1) for core, stat := range in { slc[core] = translateCPUStat(stat) } return slc } translateSoftIRQ := func(in procfs.SoftIRQStat) *machine.SoftIRQStat { return &machine.SoftIRQStat{ Hi: in.Hi, Timer: in.Timer, NetTx: in.NetTx, NetRx: in.NetRx, Block: in.Block, BlockIoPoll: in.BlockIoPoll, Tasklet: in.Tasklet, Sched: in.Sched, Hrtimer: in.Hrtimer, Rcu: in.Rcu, } } reply := &machine.SystemStatResponse{ Messages: []*machine.SystemStat{ { BootTime: stat.BootTime, CpuTotal: translateCPUStat(stat.CPUTotal), Cpu: translateListOfCPUStat(stat.CPU), IrqTotal: stat.IRQTotal, Irq: stat.IRQ, ContextSwitches: stat.ContextSwitches, ProcessCreated: stat.ProcessCreated, ProcessRunning: stat.ProcessesRunning, ProcessBlocked: stat.ProcessesBlocked, SoftIrqTotal: stat.SoftIRQTotal, SoftIrq: translateSoftIRQ(stat.SoftIRQ), }, }, } return reply, nil } // CPUFreqStats implements the machine.MachineServer interface. func (s *Server) CPUFreqStats(ctx context.Context, in *emptypb.Empty) (*machine.CPUFreqStatsResponse, error) { fs, err := sysfs.NewDefaultFS() if err != nil { return nil, err } systemCpufreqStats, err := fs.SystemCpufreq() if err != nil { return nil, err } translateCPUFreqStats := func(in sysfs.SystemCPUCpufreqStats) *machine.CPUFreqStats { if in.CpuinfoCurrentFrequency == nil || in.CpuinfoMinimumFrequency == nil || in.CpuinfoMaximumFrequency == nil { return &machine.CPUFreqStats{} } return &machine.CPUFreqStats{ CurrentFrequency: *in.CpuinfoCurrentFrequency, MinimumFrequency: *in.CpuinfoMinimumFrequency, MaximumFrequency: *in.CpuinfoMaximumFrequency, Governor: in.Governor, } } reply := &machine.CPUFreqStatsResponse{ Messages: []*machine.CPUsFreqStats{ { CpuFreqStats: xslices.Map(systemCpufreqStats, translateCPUFreqStats), }, }, } return reply, nil } // CPUInfo implements the machine.MachineServer interface. func (s *Server) CPUInfo(ctx context.Context, in *emptypb.Empty) (*machine.CPUInfoResponse, error) { fs, err := procfs.NewDefaultFS() if err != nil { return nil, err } info, err := fs.CPUInfo() if err != nil { return nil, err } translateCPUInfo := func(in procfs.CPUInfo) *machine.CPUInfo { return &machine.CPUInfo{ Processor: uint32(in.Processor), VendorId: in.VendorID, CpuFamily: in.CPUFamily, Model: in.Model, ModelName: in.ModelName, Stepping: in.Stepping, Microcode: in.Microcode, CpuMhz: in.CPUMHz, CacheSize: in.CacheSize, PhysicalId: in.PhysicalID, Siblings: uint32(in.Siblings), CoreId: in.CoreID, ApicId: in.APICID, InitialApicId: in.InitialAPICID, Fpu: in.FPU, FpuException: in.FPUException, CpuIdLevel: uint32(in.CPUIDLevel), Wp: in.WP, Flags: in.Flags, Bugs: in.Bugs, BogoMips: in.BogoMips, ClFlushSize: uint32(in.CLFlushSize), CacheAlignment: uint32(in.CacheAlignment), AddressSizes: in.AddressSizes, PowerManagement: in.PowerManagement, } } reply := &machine.CPUInfoResponse{ Messages: []*machine.CPUsInfo{ { CpuInfo: xslices.Map(info, translateCPUInfo), }, }, } return reply, nil } // NetworkDeviceStats implements the machine.MachineServer interface. func (s *Server) NetworkDeviceStats(ctx context.Context, in *emptypb.Empty) (*machine.NetworkDeviceStatsResponse, error) { fs, err := procfs.NewDefaultFS() if err != nil { return nil, err } info, err := fs.NetDev() if err != nil { return nil, err } translateNetDevLine := func(in procfs.NetDevLine) *machine.NetDev { return &machine.NetDev{ Name: in.Name, RxBytes: in.RxBytes, RxPackets: in.RxPackets, RxErrors: in.RxErrors, RxDropped: in.RxDropped, RxFifo: in.RxFIFO, RxFrame: in.RxFrame, RxCompressed: in.RxCompressed, RxMulticast: in.RxMulticast, TxBytes: in.TxBytes, TxPackets: in.TxPackets, TxErrors: in.TxErrors, TxDropped: in.TxDropped, TxFifo: in.TxFIFO, TxCollisions: in.TxCollisions, TxCarrier: in.TxCarrier, TxCompressed: in.TxCompressed, } } reply := &machine.NetworkDeviceStatsResponse{ Messages: []*machine.NetworkDeviceStats{ { Devices: maps.ValuesFunc(info, translateNetDevLine), Total: translateNetDevLine(info.Total()), }, }, } return reply, nil } // DiskStats implements the machine.MachineServer interface. func (s *Server) DiskStats(ctx context.Context, in *emptypb.Empty) (*machine.DiskStatsResponse, error) { f, err := os.Open("/proc/diskstats") if err != nil { return nil, err } defer f.Close() //nolint:errcheck resp := machine.DiskStats{ Devices: []*machine.DiskStat{}, Total: &machine.DiskStat{}, } scanner := bufio.NewScanner(f) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 18 { continue } values := make([]uint64, 15) for i := range values { values[i], err = strconv.ParseUint(fields[3+i], 10, 64) if err != nil { return nil, err } } stat := &machine.DiskStat{ Name: fields[2], ReadCompleted: values[0], ReadMerged: values[1], ReadSectors: values[2], ReadTimeMs: values[3], WriteCompleted: values[4], WriteMerged: values[5], WriteSectors: values[6], WriteTimeMs: values[7], IoInProgress: values[8], IoTimeMs: values[9], IoTimeWeightedMs: values[10], DiscardCompleted: values[11], DiscardMerged: values[12], DiscardSectors: values[13], DiscardTimeMs: values[14], } resp.Devices = append(resp.Devices, stat) resp.Total.ReadCompleted += stat.ReadCompleted resp.Total.ReadMerged += stat.ReadMerged resp.Total.ReadSectors += stat.ReadSectors resp.Total.ReadTimeMs += stat.ReadTimeMs resp.Total.WriteCompleted += stat.WriteCompleted resp.Total.WriteMerged += stat.WriteMerged resp.Total.WriteSectors += stat.WriteSectors resp.Total.WriteTimeMs += stat.WriteTimeMs resp.Total.IoInProgress += stat.IoInProgress resp.Total.IoTimeMs += stat.IoTimeMs resp.Total.IoTimeWeightedMs += stat.IoTimeWeightedMs resp.Total.DiscardCompleted += stat.DiscardCompleted resp.Total.DiscardMerged += stat.DiscardMerged resp.Total.DiscardSectors += stat.DiscardSectors resp.Total.DiscardTimeMs += stat.DiscardTimeMs } if err = scanner.Err(); err != nil { return nil, err } reply := &machine.DiskStatsResponse{ Messages: []*machine.DiskStats{ &resp, }, } return reply, nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_server.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "archive/tar" "bufio" "bytes" "compress/gzip" "context" "encoding/json" "errors" "fmt" "io" "io/fs" "log" "net" "os" "path/filepath" "slices" "strconv" "strings" "syscall" "time" "github.com/blang/semver/v4" cosiv1alpha1 "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/server" "github.com/google/uuid" "github.com/gopacket/gopacket/afpacket" multierror "github.com/hashicorp/go-multierror" "github.com/nberlee/go-netstat/netstat" "github.com/pkg/xattr" "github.com/prometheus/procfs" "github.com/rs/xid" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kmsg" "github.com/siderolabs/go-pointer" "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/zap" "golang.org/x/net/bpf" "golang.org/x/sys/unix" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/internal/app/debug" "github.com/siderolabs/talos/internal/app/images" "github.com/siderolabs/talos/internal/app/internal/machinehelper" "github.com/siderolabs/talos/internal/app/lifecycle" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/resources" storaged "github.com/siderolabs/talos/internal/app/storaged" "github.com/siderolabs/talos/internal/pkg/containers" taloscontainerd "github.com/siderolabs/talos/internal/pkg/containers/containerd" "github.com/siderolabs/talos/internal/pkg/containers/cri" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/internal/pkg/install" "github.com/siderolabs/talos/internal/pkg/miniprocfs" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/internal/pkg/pcap" "github.com/siderolabs/talos/pkg/archiver" "github.com/siderolabs/talos/pkg/chunker" "github.com/siderolabs/talos/pkg/chunker/stream" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/kubeconfig" "github.com/siderolabs/talos/pkg/machinery/api/cluster" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/inspect" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/api/storage" timeapi "github.com/siderolabs/talos/pkg/machinery/api/time" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configdiff" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" machinetype "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/block" crires "github.com/siderolabs/talos/pkg/machinery/resources/cri" etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/network" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/machinery/version" ) // MinimumEtcdUpgradeLeaseLockSeconds indicates the minimum number of seconds for which we open a lease lock for upgrading Etcd nodes. // This is not intended to lock for the duration of an upgrade. // Rather, it is intended to make sure only one node processes the various pre-upgrade checks at a time. // Thus, this timeout should be reflective of the expected time for the pre-upgrade checks, NOT the time to perform the upgrade itself. const MinimumEtcdUpgradeLeaseLockSeconds = 60 // OSPathSeparator is the string version of the os.PathSeparator. const OSPathSeparator = string(os.PathSeparator) // Server implements ClusterService and MachineService APIs // and is also responsible for registering ResourceServer and InspectServer. type Server struct { cluster.UnimplementedClusterServiceServer machine.UnimplementedMachineServiceServer Controller runtime.Controller // breaking the import loop cycle between services/ package and v1alpha1_server.go EtcdBootstrapper func(context.Context, runtime.Runtime, *machine.BootstrapRequest) error // ShutdownCtx signals that the server is shutting down. ShutdownCtx context.Context //nolint:containedctx // Zap logger. Logger *zap.Logger server *grpc.Server } func (s *Server) checkSupported(feature runtime.ModeCapability) error { mode := s.Controller.Runtime().State().Platform().Mode() if !mode.Supports(feature) { return status.Errorf(codes.FailedPrecondition, "method is not supported in %s mode", mode.String()) } return nil } // Register implements the factory.Registrator interface. func (s *Server) Register(obj *grpc.Server) { s.server = obj // wrap resources with access filter resourceState := s.Controller.Runtime().State().V1Alpha2().Resources() resourceState = state.WrapCore(state.Filter(resourceState, resources.AccessPolicy(resourceState))) machine.RegisterMachineServiceServer(obj, s) machine.RegisterImageServiceServer(obj, images.NewService(s.Controller, s.Logger)) machine.RegisterDebugServiceServer(obj, &debug.Service{}) machine.RegisterLifecycleServiceServer(obj, lifecycle.NewService(s.Controller.Runtime(), s.Logger)) cluster.RegisterClusterServiceServer(obj, s) cosiv1alpha1.RegisterStateServer(obj, server.NewState(resourceState)) inspect.RegisterInspectServiceServer(obj, &InspectServer{server: s}) storage.RegisterStorageServiceServer(obj, &storaged.Server{Controller: s.Controller}) timeapi.RegisterTimeServiceServer(obj, &TimeServer{ConfigProvider: s.Controller.Runtime()}) } // modeWrapper overrides RequiresInstall() based on actual installed status. type modeWrapper struct { runtime.Mode installed bool } func (m modeWrapper) RequiresInstall() bool { return m.Mode.RequiresInstall() && !m.installed } // ApplyConfiguration implements machine.MachineService. // //nolint:gocyclo,cyclop func (s *Server) ApplyConfiguration(ctx context.Context, in *machine.ApplyConfigurationRequest) (*machine.ApplyConfigurationResponse, error) { if s.Controller.Runtime().State().Platform().Mode().IsAgent() { return nil, status.Error(codes.Unimplemented, "API is not implemented in agent mode") } roles := authz.GetRoles(ctx) inMaintenance := !s.Controller.Runtime().ConfigCompleteForBoot() if !inMaintenance && !roles.Includes(role.Admin) { return nil, authz.ErrNotAuthorized } mode := in.Mode.String() modeDetails := "Applied configuration with a reboot" modeErr := "" if in.Mode != machine.ApplyConfigurationRequest_TRY { s.Controller.Runtime().CancelConfigRollbackTimeout() } cfgProvider, err := configloader.NewFromBytes(in.GetData()) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } // as we are not in maintenance mode, the v1alpha1 config should be always present // in the future we should allow to remove v1alpha1, but for now for better UX we deny // such requests to avoid confusion if !inMaintenance && cfgProvider.RawV1Alpha1() == nil { return nil, status.Error(codes.InvalidArgument, "the applied machine configuration doesn't contain v1alpha1 config, did you mean to patch the machine config instead?") } validationMode := modeWrapper{ Mode: s.Controller.Runtime().State().Platform().Mode(), installed: s.Controller.Runtime().State().Machine().Installed(), } warnings, err := cfgProvider.Validate(validationMode) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } warningsRuntime, err := cfgProvider.RuntimeValidate(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), validationMode) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } warnings = slices.Concat(warnings, warningsRuntime) if inMaintenance && in.Mode == machine.ApplyConfigurationRequest_REBOOT { in.Mode = machine.ApplyConfigurationRequest_NO_REBOOT } //nolint:exhaustive switch in.Mode { // --mode=try case machine.ApplyConfigurationRequest_TRY: fallthrough // --mode=no-reboot case machine.ApplyConfigurationRequest_NO_REBOOT: if err = s.Controller.Runtime().CanApplyImmediate(cfgProvider); err != nil && !inMaintenance { return nil, status.Error(codes.InvalidArgument, err.Error()) } modeDetails = "Applied configuration without a reboot" // --mode=staged case machine.ApplyConfigurationRequest_STAGED: modeDetails = "Staged configuration to be applied after the next reboot" // --mode=auto detect actual update mode case machine.ApplyConfigurationRequest_AUTO: if err = s.Controller.Runtime().CanApplyImmediate(cfgProvider); err != nil && !inMaintenance { in.Mode = machine.ApplyConfigurationRequest_REBOOT modeDetails = "Applied configuration with a reboot" modeErr = ": " + err.Error() } else { in.Mode = machine.ApplyConfigurationRequest_NO_REBOOT modeDetails = "Applied configuration without a reboot" } mode = fmt.Sprintf("%s(%s)", mode, in.Mode) } if in.DryRun { details, err := generateDiff(s.Controller.Runtime(), cfgProvider) if err != nil { return nil, fmt.Errorf("failed to generate diff: %w", err) } return &machine.ApplyConfigurationResponse{ Messages: []*machine.ApplyConfiguration{ { Mode: in.Mode, ModeDetails: fmt.Sprintf(`Dry run summary: %s (skipped in dry-run). %s`, modeDetails, details), }, }, }, nil } log.Printf("apply config request: mode %s", strings.ToLower(mode)) //nolint:exhaustive switch in.Mode { // --mode=try case machine.ApplyConfigurationRequest_TRY: timeout := constants.ConfigTryTimeout if in.TryModeTimeout != nil { timeout = in.TryModeTimeout.AsDuration() } modeDetails += fmt.Sprintf("\nThe config is applied in 'try' mode and will be automatically reverted back in %s", timeout.String()) if err := s.Controller.Runtime().RollbackToConfigAfter(timeout); err != nil { return nil, err } if err := s.Controller.Runtime().SetConfig(cfgProvider); err != nil { return nil, err } // --mode=no-reboot case machine.ApplyConfigurationRequest_NO_REBOOT: if err := s.Controller.Runtime().SetPersistedConfig(cfgProvider); err != nil { return nil, err } if err := s.Controller.Runtime().SetConfig(cfgProvider); err != nil { return nil, err } // --mode=staged case machine.ApplyConfigurationRequest_STAGED: if err := s.Controller.Runtime().SetPersistedConfig(cfgProvider); err != nil { return nil, err } // --mode=reboot case machine.ApplyConfigurationRequest_REBOOT: if err := s.Controller.Runtime().SetPersistedConfig(cfgProvider); err != nil { return nil, err } go func() { if err := s.Controller.Run(context.Background(), runtime.SequenceReboot, nil, runtime.WithTakeover()); err != nil { if !runtime.IsRebootError(err) { log.Println("apply configuration failed:", err) } } }() default: return nil, fmt.Errorf("incorrect mode '%s' specified for the apply config call", in.Mode.String()) } return &machine.ApplyConfigurationResponse{ Messages: []*machine.ApplyConfiguration{ { Mode: in.Mode, Warnings: warnings, ModeDetails: modeDetails + modeErr, }, }, }, nil } func generateDiff(r runtime.Runtime, provider config.Provider) (string, error) { documentsDiff, err := configdiff.DiffConfigs(r.ConfigContainer(), provider) if err != nil { return "", err } if documentsDiff == "" { documentsDiff = "No changes." } return "Config diff:\n\n" + documentsDiff, nil } // Reboot implements the machine.MachineServer interface. func (s *Server) Reboot(ctx context.Context, in *machine.RebootRequest) (reply *machine.RebootResponse, err error) { actorID := uuid.New().String() log.Printf("reboot via API received. actor id: %s", actorID) if err := s.checkSupported(runtime.Reboot); err != nil { return nil, err } rebootCtx := context.WithValue(context.Background(), runtime.ActorIDCtxKey{}, actorID) go func() { if err := s.Controller.Run(rebootCtx, runtime.SequenceReboot, in); err != nil { if !runtime.IsRebootError(err) { log.Println("reboot failed:", err) } } }() reply = &machine.RebootResponse{ Messages: []*machine.Reboot{ { ActorId: actorID, }, }, } return reply, nil } // Rollback implements the machine.MachineServer interface. func (s *Server) Rollback(ctx context.Context, in *machine.RollbackRequest) (*machine.RollbackResponse, error) { log.Printf("rollback via API received") if err := s.checkSupported(runtime.Rollback); err != nil { return nil, err } systemDisk, err := block.GetSystemDisk(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("system disk lookup failed: %w", err) } if systemDisk == nil { return nil, status.Errorf(codes.FailedPrecondition, "system disk not found") } if err := func() error { config, err := bootloader.Probe(systemDisk.DevPath, options.ProbeOptions{}) if err != nil { return err } return config.Revert(systemDisk.DevPath) }(); err != nil { return nil, err } go func() { if err := s.Controller.Run(context.Background(), runtime.SequenceReboot, in, runtime.WithTakeover()); err != nil { if !runtime.IsRebootError(err) { log.Println("reboot failed:", err) } } }() return &machine.RollbackResponse{ Messages: []*machine.Rollback{ {}, }, }, nil } // Bootstrap implements the machine.MachineServer interface. func (s *Server) Bootstrap(ctx context.Context, in *machine.BootstrapRequest) (reply *machine.BootstrapResponse, err error) { log.Printf("bootstrap request received") if !s.Controller.Runtime().IsBootstrapAllowed() { return nil, status.Error(codes.FailedPrecondition, "bootstrap is not available yet") } if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "bootstrap"); err != nil { return nil, err } timeCtx, timeCtxCancel := context.WithTimeout(ctx, 5*time.Second) defer timeCtxCancel() if err := timeresource.NewSyncCondition(s.Controller.Runtime().State().V1Alpha2().Resources()).Wait(timeCtx); err != nil { return nil, status.Error(codes.FailedPrecondition, "time is not in sync yet") } if entries, _ := os.ReadDir(constants.EtcdDataPath); len(entries) > 0 { //nolint:errcheck return nil, status.Error(codes.AlreadyExists, "etcd data directory is not empty") } if err := s.EtcdBootstrapper(ctx, s.Controller.Runtime(), in); err != nil { return nil, err } reply = &machine.BootstrapResponse{ Messages: []*machine.Bootstrap{ {}, }, } return reply, nil } // Shutdown implements the machine.MachineServer interface. func (s *Server) Shutdown(ctx context.Context, in *machine.ShutdownRequest) (reply *machine.ShutdownResponse, err error) { actorID := uuid.New().String() log.Printf("shutdown via API received. actor id: %s", actorID) if err = s.checkSupported(runtime.Shutdown); err != nil { return nil, err } shutdownCtx := context.WithValue(context.Background(), runtime.ActorIDCtxKey{}, actorID) go func() { if err := s.Controller.Run(shutdownCtx, runtime.SequenceShutdown, in, runtime.WithTakeover()); err != nil { if !runtime.IsRebootError(err) { log.Println("shutdown failed:", err) } } }() reply = &machine.ShutdownResponse{ Messages: []*machine.Shutdown{ { ActorId: actorID, }, }, } return reply, nil } // Upgrade initiates an upgrade. // //nolint:gocyclo,cyclop func (s *Server) Upgrade(ctx context.Context, in *machine.UpgradeRequest) (*machine.UpgradeResponse, error) { if s.Controller.Runtime().State().Platform().Mode().IsAgent() { return nil, status.Error(codes.Unimplemented, "API is not implemented in agent mode") } actorID := uuid.New().String() ctx = context.WithValue(ctx, runtime.ActorIDCtxKey{}, actorID) if err := s.checkSupported(runtime.Upgrade); err != nil { return nil, err } log.Printf("upgrade request received: staged %v, force %v, reboot mode %v", in.GetStage(), in.GetForce(), in.GetRebootMode().String()) log.Printf("validating %q", in.GetImage()) if err := install.PullAndValidateInstallerImage( ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), crires.RegistryBuilder(s.Controller.Runtime().State().V1Alpha2().Resources()), in.GetImage(), ); err != nil { return nil, fmt.Errorf("error validating installer image %q: %w", in.GetImage(), err) } if s.Controller.Runtime().Config().Machine().Type() != machinetype.TypeWorker && !in.GetForce() { etcdClient, err := etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } // acquire the upgrade mutex unlocker, err := tryLockUpgradeMutex(ctx, etcdClient) if err != nil { return nil, fmt.Errorf("failed to acquire upgrade mutex: %w", err) } // unlock the mutex once the API call is done, as it protects only pre-upgrade checks defer unlocker() if err = etcdClient.ValidateForUpgrade(ctx, s.Controller.Runtime().Config()); err != nil { return nil, fmt.Errorf("error validating etcd for upgrade: %w", err) } } runCtx := context.WithValue(context.Background(), runtime.ActorIDCtxKey{}, actorID) if in.GetStage() { if ok, err := s.Controller.Runtime().State().Machine().Meta().SetTag(ctx, meta.StagedUpgradeImageRef, in.GetImage()); !ok || err != nil { return nil, fmt.Errorf("error adding staged upgrade image ref tag: %w", err) } opts := install.Options{Pull: true} //nolint:staticcheck if err := opts.Apply(install.OptionsFromUpgradeRequest(s.Controller.Runtime(), in)...); err != nil { return nil, fmt.Errorf("error applying install options: %w", err) } serialized, err := json.Marshal(opts) if err != nil { return nil, fmt.Errorf("error serializing install options: %s", err) } var ok bool if ok, err = s.Controller.Runtime().State().Machine().Meta().SetTag(ctx, meta.StagedUpgradeInstallOptions, string(serialized)); !ok || err != nil { return nil, fmt.Errorf("error adding staged upgrade install options tag: %w", err) } if err = s.Controller.Runtime().State().Machine().Meta().Flush(); err != nil { return nil, fmt.Errorf("error writing meta: %w", err) } go func() { if err := s.Controller.Run(runCtx, runtime.SequenceStageUpgrade, in); err != nil { if !runtime.IsRebootError(err) { log.Println("reboot for staged upgrade failed:", err) } } }() } else { go func() { if err := s.Controller.Run(runCtx, runtime.SequenceUpgrade, in); err != nil { if !runtime.IsRebootError(err) { log.Println("upgrade failed:", err) } } }() } return &machine.UpgradeResponse{ Messages: []*machine.Upgrade{ { Ack: "Upgrade request received", ActorId: actorID, }, }, }, nil } // ResetOptions implements runtime.ResetOptions interface. type ResetOptions struct { *machine.ResetRequest systemDiskTargets []*partition.VolumeWipeTarget systemDiskPaths []string } // GetSystemDiskTargets implements runtime.ResetOptions interface. func (opt *ResetOptions) GetSystemDiskTargets() []runtime.PartitionTarget { if opt.systemDiskTargets == nil { return nil } return xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) runtime.PartitionTarget { return t }) } // GetSystemDiskPaths implements runtime.ResetOptions interface. func (opt *ResetOptions) GetSystemDiskPaths() []string { return opt.systemDiskPaths } // String implements runtime.ResetOptions interface. func (opt *ResetOptions) String() string { return strings.Join(xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) string { return t.String() }), ", ") } // Reset resets the node. // //nolint:gocyclo,cyclop func (s *Server) Reset(ctx context.Context, in *machine.ResetRequest) (reply *machine.ResetResponse, err error) { if s.Controller.Runtime().State().Platform().Mode().IsAgent() { return nil, status.Error(codes.Unimplemented, "API is not implemented in agent mode") } actorID := uuid.New().String() log.Printf("reset request received. actorID: %s", actorID) opts := ResetOptions{ ResetRequest: in, } if len(in.GetUserDisksToWipe()) > 0 { if in.Mode == machine.ResetRequest_SYSTEM_DISK { return nil, errors.New("reset failed: invalid input, wipe mode SYSTEM_DISK doesn't support UserDisksToWipe parameter") } diskList, err := safe.StateListAll[*block.Disk](ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("listing disks failed: %w", err) } disks := xslices.ToMap( safe.ToSlice(diskList, func(d *block.Disk) *block.Disk { return d }), func(disk *block.Disk) (string, *block.Disk) { return disk.TypedSpec().DevPath, disk }, ) systemDisk, err := block.GetSystemDisk(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("system disk lookup failed: %w", err) } // validate input for _, deviceName := range in.GetUserDisksToWipe() { disk, ok := disks[deviceName] if !ok { return nil, fmt.Errorf("reset user disk failed: device %s wasn't found", deviceName) } if disk.TypedSpec().Readonly { return nil, fmt.Errorf("reset user disk failed: device %s is readonly", deviceName) } if systemDisk != nil && deviceName == systemDisk.DevPath { return nil, fmt.Errorf("reset user disk failed: device %s is the system disk", deviceName) } } } if len(in.GetSystemPartitionsToWipe()) > 0 { if in.Mode == machine.ResetRequest_USER_DISKS { return nil, errors.New("reset failed: invalid input, wipe mode USER_DISKS doesn't support SystemPartitionsToWipe parameter") } for _, spec := range in.GetSystemPartitionsToWipe() { volumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), spec.Label) if err != nil { return nil, fmt.Errorf("failed to get volume status with label %q: %w", spec.Label, err) } if volumeStatus.TypedSpec().Location == "" { return nil, fmt.Errorf("failed to reset: volume %q is not located", spec.Label) } target := partition.VolumeWipeTargetFromVolumeStatus(volumeStatus) if spec.Wipe { opts.systemDiskTargets = append(opts.systemDiskTargets, target) } } } if in.Mode != machine.ResetRequest_USER_DISKS && len(in.GetSystemPartitionsToWipe()) == 0 { opts.systemDiskPaths, err = block.GetSystemDiskPaths(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("system disk paths lookup failed: %w", err) } } resetCtx := context.WithValue(context.Background(), runtime.ActorIDCtxKey{}, actorID) go func() { if err := s.Controller.Run(resetCtx, runtime.SequenceReset, &opts); err != nil { if !runtime.IsRebootError(err) { log.Println("reset failed:", err) } } }() reply = &machine.ResetResponse{ Messages: []*machine.Reset{ { ActorId: actorID, }, }, } return reply, nil } // ServiceList returns list of the registered services and their status. func (s *Server) ServiceList(ctx context.Context, in *emptypb.Empty) (result *machine.ServiceListResponse, err error) { services := system.Services(s.Controller.Runtime()).List() result = &machine.ServiceListResponse{ Messages: []*machine.ServiceList{ { Services: xslices.Map(services, (*system.ServiceRunner).AsProto), }, }, } return result, nil } // ServiceStart implements the machine.MachineServer interface and starts a // service running on Talos. func (s *Server) ServiceStart(ctx context.Context, in *machine.ServiceStartRequest) (reply *machine.ServiceStartResponse, err error) { if err = system.Services(s.Controller.Runtime()).APIStart(ctx, in.Id); err != nil { return &machine.ServiceStartResponse{}, err } reply = &machine.ServiceStartResponse{ Messages: []*machine.ServiceStart{ { Resp: fmt.Sprintf("Service %q started", in.Id), }, }, } return reply, err } // ServiceStop implements the machine.MachineServer interface and stops a // service running on Talos. func (s *Server) ServiceStop(ctx context.Context, in *machine.ServiceStopRequest) (reply *machine.ServiceStopResponse, err error) { if err = system.Services(s.Controller.Runtime()).APIStop(ctx, in.Id); err != nil { return &machine.ServiceStopResponse{}, err } reply = &machine.ServiceStopResponse{ Messages: []*machine.ServiceStop{ { Resp: fmt.Sprintf("Service %q stopped", in.Id), }, }, } return reply, err } // ServiceRestart implements the machine.MachineServer interface and stops a // service running on Talos. func (s *Server) ServiceRestart(ctx context.Context, in *machine.ServiceRestartRequest) (reply *machine.ServiceRestartResponse, err error) { if err = system.Services(s.Controller.Runtime()).APIRestart(ctx, in.Id); err != nil { return &machine.ServiceRestartResponse{}, err } reply = &machine.ServiceRestartResponse{ Messages: []*machine.ServiceRestart{ { Resp: fmt.Sprintf("Service %q restarted", in.Id), }, }, } return reply, err } // Copy implements the machine.MachineServer interface and copies data out of Talos node. func (s *Server) Copy(req *machine.CopyRequest, obj machine.MachineService_CopyServer) error { path := req.RootPath path = filepath.Clean(path) if !filepath.IsAbs(path) { return fmt.Errorf("path is not absolute %v", path) } pr, pw := io.Pipe() errCh := make(chan error, 1) ctx, ctxCancel := context.WithCancel(obj.Context()) defer ctxCancel() go func() { //nolint:errcheck defer pw.Close() errCh <- archiver.TarGz(ctx, path, pw) }() chunker := stream.NewChunker(ctx, pr) chunkCh := chunker.Read() for data := range chunkCh { err := obj.SendMsg(&common.Data{Bytes: data}) if err != nil { ctxCancel() } } archiveErr := <-errCh if archiveErr != nil { return obj.SendMsg(&common.Data{ Metadata: &common.Metadata{ Error: archiveErr.Error(), }, }) } return nil } // List implements the machine.MachineServer interface. // //nolint:gocyclo,cyclop func (s *Server) List(req *machine.ListRequest, obj machine.MachineService_ListServer) error { if req == nil { req = new(machine.ListRequest) } if !strings.HasPrefix(req.Root, OSPathSeparator) { // Make sure we use complete paths req.Root = OSPathSeparator + req.Root } req.Root = strings.TrimSuffix(req.Root, OSPathSeparator) if req.Root == "" { req.Root = "/" } var recursionDepth int if req.Recurse { if req.RecursionDepth == 0 { recursionDepth = -1 } else { recursionDepth = int(req.RecursionDepth) } } opts := []archiver.WalkerOption{ archiver.WithMaxRecurseDepth(recursionDepth), } if len(req.Types) > 0 { types := make([]archiver.FileType, 0, len(req.Types)) for _, t := range req.Types { switch t { case machine.ListRequest_REGULAR: types = append(types, archiver.RegularFileType) case machine.ListRequest_DIRECTORY: types = append(types, archiver.DirectoryFileType) case machine.ListRequest_SYMLINK: types = append(types, archiver.SymlinkFileType) } } opts = append(opts, archiver.WithFileTypes(types...)) } files, err := archiver.Walker(obj.Context(), req.Root, opts...) if err != nil { return err } for fi := range files { xattrs := []*machine.Xattr{} if req.ReportXattrs { // On filesystems such as devtmpfs and sysfs, xattrs are not supported. // However, we can still get the label from the security.selinux xattr for automatic labels. foundSelinux := false if list, err := xattr.List(fi.FullPath); err == nil { for _, attr := range list { if data, err := xattr.Get(fi.FullPath, attr); err == nil { if attr == "security.selinux" { foundSelinux = true } xattrs = append(xattrs, &machine.Xattr{Name: attr, Data: data}) } } } if !foundSelinux { if data, err := xattr.Get(fi.FullPath, "security.selinux"); err == nil { xattrs = append(xattrs, &machine.Xattr{Name: "security.selinux", Data: data}) } } } if fi.Error != nil { err = obj.Send(&machine.FileInfo{ Name: fi.FullPath, RelativeName: fi.RelPath, Error: fi.Error.Error(), Xattrs: xattrs, }) } else { err = obj.Send(&machine.FileInfo{ Name: fi.FullPath, RelativeName: fi.RelPath, Size: fi.FileInfo.Size(), Mode: uint32(fi.FileInfo.Mode()), Modified: fi.FileInfo.ModTime().Unix(), IsDir: fi.FileInfo.IsDir(), Link: fi.Link, Uid: fi.FileInfo.Sys().(*syscall.Stat_t).Uid, Gid: fi.FileInfo.Sys().(*syscall.Stat_t).Gid, Xattrs: xattrs, }) } if err != nil { return err } } return nil } // DiskUsage implements the machine.MachineServer interface. // //nolint:cyclop func (s *Server) DiskUsage(req *machine.DiskUsageRequest, obj machine.MachineService_DiskUsageServer) error { //nolint:gocyclo if req == nil { req = new(machine.DiskUsageRequest) } for _, path := range req.Paths { if !strings.HasPrefix(path, OSPathSeparator) { // Make sure we use complete paths path = OSPathSeparator + path } path = strings.TrimSuffix(path, OSPathSeparator) if path == "" { path = "/" } _, err := os.Stat(path) if err == os.ErrNotExist { err = obj.Send( &machine.DiskUsageInfo{ Name: path, RelativeName: path, Error: err.Error(), }, ) if err != nil { return err } continue } files, err := archiver.Walker(obj.Context(), path, archiver.WithMaxRecurseDepth(-1)) if err != nil { err = obj.Send( &machine.DiskUsageInfo{ Name: path, RelativeName: path, Error: err.Error(), }, ) if err != nil { return err } continue } folders := map[string]*machine.DiskUsageInfo{} // send a record back to client if the message shouldn't be skipped // at the same time use record information for folder size estimation sendSize := func(info *machine.DiskUsageInfo, depth int32, isDir bool) error { prefix := strings.TrimRight(filepath.Dir(info.Name), "/") if folder, ok := folders[prefix]; ok { folder.Size += info.Size } // recursion depth check skip := depth >= req.RecursionDepth && req.RecursionDepth > 0 // skip files check skip = skip || !isDir && !req.All // threshold check skip = skip || req.Threshold > 0 && info.Size < req.Threshold skip = skip || req.Threshold < 0 && info.Size > -req.Threshold if skip { return nil } return obj.Send(info) } var ( depth int32 prefix = path rootDepth = int32(strings.Count(path, archiver.OSPathSeparator)) ) // flush all folder sizes until we get to the common prefix flushFolders := func(prefix, nextPrefix string) error { for !strings.HasPrefix(nextPrefix, prefix) { currentDepth := int32(strings.Count(prefix, archiver.OSPathSeparator)) - rootDepth if folder, ok := folders[prefix]; ok { err = sendSize(folder, currentDepth, true) if err != nil { return err } delete(folders, prefix) } prefix = strings.TrimRight(filepath.Dir(prefix), "/") } return nil } for fi := range files { if fi.Error != nil { err = obj.Send( &machine.DiskUsageInfo{ Name: fi.FullPath, RelativeName: fi.RelPath, Error: fi.Error.Error(), }, ) } else { currentDepth := int32(strings.Count(fi.FullPath, archiver.OSPathSeparator)) - rootDepth size := max(fi.FileInfo.Size(), 0) // kcore file size gives wrong value, this code should be smarter when it reads it // TODO: figure out better way to skip such file if fi.FullPath == "/proc/kcore" { size = 0 } if fi.FileInfo.IsDir() { folders[strings.TrimRight(fi.FullPath, "/")] = &machine.DiskUsageInfo{ Name: fi.FullPath, RelativeName: fi.RelPath, Size: size, } } else { err = sendSize(&machine.DiskUsageInfo{ Name: fi.FullPath, RelativeName: fi.RelPath, Size: size, }, currentDepth, false) if err != nil { return err } } // depth goes down when walker gets to the next sibling folder if currentDepth < depth { nextPrefix := fi.FullPath if err = flushFolders(prefix, nextPrefix); err != nil { return err } prefix = nextPrefix } if fi.FileInfo.IsDir() { prefix = fi.FullPath } depth = currentDepth } } if path != "" { p := strings.TrimRight(path, "/") if folder, ok := folders[p]; ok { err = flushFolders(prefix, p) if err != nil { return err } err = sendSize(folder, 0, true) if err != nil { return err } } } return nil } return nil } // Mounts implements the machine.MachineServer interface. func (s *Server) Mounts(ctx context.Context, in *emptypb.Empty) (reply *machine.MountsResponse, err error) { file, err := os.Open("/proc/mounts") if err != nil { return nil, err } //nolint:errcheck defer file.Close() var ( stat unix.Statfs_t multiErr *multierror.Error ) var stats []*machine.MountStat scanner := bufio.NewScanner(file) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 2 { continue } filesystem := fields[0] mountpoint := fields[1] var ( totalSize uint64 totalAvail uint64 ) if statInfo, err := os.Stat(mountpoint); err == nil && statInfo.Mode().IsDir() { if err := unix.Statfs(mountpoint, &stat); err != nil { multiErr = multierror.Append(multiErr, err) } else { totalSize = uint64(stat.Bsize) * stat.Blocks totalAvail = uint64(stat.Bsize) * stat.Bavail } } stat := &machine.MountStat{ Filesystem: filesystem, Size: totalSize, Available: totalAvail, MountedOn: mountpoint, } stats = append(stats, stat) } if err := scanner.Err(); err != nil { multiErr = multierror.Append(multiErr, err) } reply = &machine.MountsResponse{ Messages: []*machine.Mounts{ { Stats: stats, }, }, } return reply, multiErr.ErrorOrNil() } // Version implements the machine.MachineServer interface. func (s *Server) Version(ctx context.Context, in *emptypb.Empty) (reply *machine.VersionResponse, err error) { var platform *machine.PlatformInfo if s.Controller.Runtime().State().Platform() != nil { platform = &machine.PlatformInfo{ Name: s.Controller.Runtime().State().Platform().Name(), Mode: s.Controller.Runtime().State().Platform().Mode().String(), } } var features *machine.FeaturesInfo config := s.Controller.Runtime().Config() if config != nil && config.Machine() != nil { features = &machine.FeaturesInfo{ Rbac: true, } } return &machine.VersionResponse{ Messages: []*machine.Version{ { Version: version.NewVersion(), Platform: platform, Features: features, }, }, }, nil } // Kubeconfig implements the machine.MachineServer interface. func (s *Server) Kubeconfig(empty *emptypb.Empty, obj machine.MachineService_KubeconfigServer) error { if err := machinehelper.CheckControlplane(obj.Context(), s.Controller.Runtime().State().V1Alpha2().Resources(), "kubeconfig"); err != nil { return err } var b bytes.Buffer if err := kubeconfig.GenerateAdmin(s.Controller.Runtime().Config().Cluster(), &b); err != nil { return err } // wrap in .tar.gz to match Copy protocol var buf bytes.Buffer zw := gzip.NewWriter(&buf) tarW := tar.NewWriter(zw) err := tarW.WriteHeader(&tar.Header{ Typeflag: tar.TypeReg, Name: "kubeconfig", Size: int64(b.Len()), ModTime: time.Now(), Mode: 0o600, }) if err != nil { return err } _, err = io.Copy(tarW, &b) if err != nil { return err } if err = zw.Close(); err != nil { return err } return obj.Send(&common.Data{ Bytes: buf.Bytes(), }) } // Logs provides a service or container logs can be requested and the contents of the // log file are streamed in chunks. func (s *Server) Logs(req *machine.LogsRequest, l machine.MachineService_LogsServer) (err error) { var chunk chunker.Chunker switch { case req.Namespace == constants.SystemContainerdNamespace || req.Id == "kubelet": var options []runtime.LogOption if req.Follow { options = append(options, runtime.WithFollow()) } if req.TailLines >= 0 { options = append(options, runtime.WithTailLines(int(req.TailLines))) } var logR io.ReadCloser logR, err = s.Controller.Runtime().Logging().ServiceLog(req.Id).Reader(options...) if err != nil { return err } //nolint:errcheck defer logR.Close() chunk = stream.NewChunker(l.Context(), logR) default: var file io.Closer if chunk, file, err = k8slogs(l.Context(), req); err != nil { return err } //nolint:errcheck defer file.Close() } for data := range chunk.Read() { if err = l.Send(&common.Data{Bytes: data}); err != nil { return err } } return nil } // LogsContainers provide a list of registered log containers. func (s *Server) LogsContainers(context.Context, *emptypb.Empty) (*machine.LogsContainersResponse, error) { return &machine.LogsContainersResponse{ Messages: []*machine.LogsContainer{ { Ids: s.Controller.Runtime().Logging().RegisteredLogs(), }, }, }, nil } func k8slogs(ctx context.Context, req *machine.LogsRequest) (chunker.Chunker, io.Closer, error) { inspector, err := getContainerInspector(ctx, req.Namespace, req.Driver) if err != nil { return nil, nil, err } //nolint:errcheck defer inspector.Close() container, err := inspector.Container(req.Id) if err != nil { return nil, nil, err } if container == nil { return nil, nil, fmt.Errorf("container %q not found", req.Id) } return container.GetLogChunker(ctx, req.Follow, int(req.TailLines)) } func getContainerInspector(ctx context.Context, namespace string, driver common.ContainerDriver) (containers.Inspector, error) { switch driver { case common.ContainerDriver_CRI: if namespace != constants.K8sContainerdNamespace { return nil, errors.New("CRI inspector is supported only for K8s namespace") } return cri.NewInspector(ctx) case common.ContainerDriver_CONTAINERD: addr := constants.CRIContainerdAddress if namespace == constants.SystemContainerdNamespace { addr = constants.SystemContainerdAddress } return taloscontainerd.NewInspector(ctx, namespace, taloscontainerd.WithContainerdAddress(addr)) default: return nil, fmt.Errorf("unsupported driver %q", driver) } } // Read implements the read API. func (s *Server) Read(in *machine.ReadRequest, srv machine.MachineService_ReadServer) (err error) { stat, err := os.Stat(in.Path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return status.Error(codes.NotFound, err.Error()) } return err } switch mode := stat.Mode(); { case mode.IsRegular(): f, err := os.OpenFile(in.Path, os.O_RDONLY, 0) if err != nil { return err } defer f.Close() //nolint:errcheck ctx, cancel := context.WithCancel(srv.Context()) defer cancel() chunker := stream.NewChunker(ctx, f) chunkCh := chunker.Read() for data := range chunkCh { err := srv.SendMsg(&common.Data{Bytes: data}) if err != nil { cancel() } } return nil default: return errors.New("path must be a regular file") } } // Events streams runtime events. // //nolint:gocyclo func (s *Server) Events(req *machine.EventsRequest, l machine.MachineService_EventsServer) error { // send an empty (hello) event to indicate to client that streaming has started err := sendEmptyEvent(req, l) if err != nil { return err } errCh := make(chan error) var opts []runtime.WatchOptionFunc if req.TailEvents != 0 { opts = append(opts, runtime.WithTailEvents(int(req.TailEvents))) } if req.TailId != "" { tailID, err := xid.FromString(req.TailId) if err != nil { return fmt.Errorf("error parsing tail_id: %w", err) } opts = append(opts, runtime.WithTailID(tailID)) } if req.TailSeconds != 0 { opts = append(opts, runtime.WithTailDuration(time.Duration(req.TailSeconds)*time.Second)) } if req.WithActorId != "" { opts = append(opts, runtime.WithActorID(req.WithActorId)) } if err := s.Controller.Runtime().Events().Watch(func(events <-chan runtime.EventInfo) { errCh <- func() error { for { select { case <-s.ShutdownCtx.Done(): return nil case <-l.Context().Done(): return l.Context().Err() case event, ok := <-events: if !ok { return nil } msg, err := event.ToMachineEvent() if err != nil { return err } if err = l.Send(msg); err != nil { return err } } } }() }, opts...); err != nil { return err } return <-errCh } func sendEmptyEvent(req *machine.EventsRequest, l machine.MachineService_EventsServer) error { emptyEvent, err := new(runtime.NewEvent(nil, req.WithActorId)).ToMachineEvent() if err != nil { return err } return l.Send(emptyEvent) } // Containers implements the machine.MachineServer interface. func (s *Server) Containers(ctx context.Context, in *machine.ContainersRequest) (reply *machine.ContainersResponse, err error) { inspector, err := getContainerInspector(ctx, in.Namespace, in.Driver) if err != nil { return nil, err } //nolint:errcheck defer inspector.Close() pods, err := inspector.Pods() if err != nil { // fatal error if pods == nil { return nil, err } // TODO: only some failed, need to handle it better via client log.Println(err.Error()) } var containers []*machine.ContainerInfo for _, pod := range pods { for _, container := range pod.Containers { container := &machine.ContainerInfo{ Namespace: in.Namespace, Id: container.Display, InternalId: container.ID, Uid: container.UID, PodId: pod.Name, Name: container.Name, Image: container.Image, Pid: container.Pid, Status: container.Status, NetworkNamespace: container.NetworkNamespace, } containers = append(containers, container) } } reply = &machine.ContainersResponse{ Messages: []*machine.Container{ { Containers: containers, }, }, } return reply, nil } // Stats implements the machine.MachineServer interface. func (s *Server) Stats(ctx context.Context, in *machine.StatsRequest) (reply *machine.StatsResponse, err error) { inspector, err := getContainerInspector(ctx, in.Namespace, in.Driver) if err != nil { return nil, err } //nolint:errcheck defer inspector.Close() pods, err := inspector.Pods() if err != nil { // fatal error if pods == nil { return nil, err } // TODO: only some failed, need to handle it better via client log.Println(err.Error()) } var stats []*machine.Stat for _, pod := range pods { for _, container := range pod.Containers { if container.Metrics == nil { continue } stat := &machine.Stat{ Namespace: in.Namespace, Id: container.Display, PodId: pod.Name, Name: container.Name, MemoryUsage: container.Metrics.MemoryUsage, CpuUsage: container.Metrics.CPUUsage, } stats = append(stats, stat) } } reply = &machine.StatsResponse{ Messages: []*machine.Stats{ { Stats: stats, }, }, } return reply, nil } // Restart implements the machine.MachineServer interface. func (s *Server) Restart(ctx context.Context, in *machine.RestartRequest) (*machine.RestartResponse, error) { inspector, err := getContainerInspector(ctx, in.Namespace, in.Driver) if err != nil { return nil, err } //nolint:errcheck defer inspector.Close() container, err := inspector.Container(in.Id) if err != nil { return nil, err } if container == nil { return nil, fmt.Errorf("container %q not found", in.Id) } err = container.Kill(syscall.SIGTERM) if err != nil { return nil, err } return &machine.RestartResponse{ Messages: []*machine.Restart{ {}, }, }, nil } // Dmesg implements the machine.MachineServer interface. // //nolint:gocyclo func (s *Server) Dmesg(req *machine.DmesgRequest, srv machine.MachineService_DmesgServer) error { ctx := srv.Context() var options []kmsg.Option if req.Follow { options = append(options, kmsg.Follow()) } if req.Tail { options = append(options, kmsg.FromTail()) } reader, err := kmsg.NewReader(options...) if err != nil { return fmt.Errorf("error opening /dev/kmsg reader: %w", err) } defer reader.Close() //nolint:errcheck ch := reader.Scan(ctx) for { select { case <-s.ShutdownCtx.Done(): if err = reader.Close(); err != nil { return err } case <-ctx.Done(): if err = reader.Close(); err != nil { return err } case packet, ok := <-ch: if !ok { return nil } if packet.Err != nil { err = srv.Send(&common.Data{ Metadata: &common.Metadata{ Error: packet.Err.Error(), }, }) } else { msg := packet.Message err = srv.Send(&common.Data{ Bytes: fmt.Appendf(nil, "%s: %7s: [%s]: %s", msg.Facility, msg.Priority, msg.Timestamp.Format(time.RFC3339Nano), msg.Message), }) } if err != nil { return err } } } } // Processes implements the machine.MachineServer interface. func (s *Server) Processes(ctx context.Context, in *emptypb.Empty) (reply *machine.ProcessesResponse, err error) { var processes []*machine.ProcessInfo procs, err := miniprocfs.NewProcesses() if err != nil { return nil, err } for { info, err := procs.Next() if err != nil { return nil, err } if info == nil { break } processes = append(processes, info) } reply = &machine.ProcessesResponse{ Messages: []*machine.Process{ { Processes: processes, }, }, } return reply, nil } // Memory implements the machine.MachineServer interface. func (s *Server) Memory(ctx context.Context, in *emptypb.Empty) (reply *machine.MemoryResponse, err error) { proc, err := procfs.NewDefaultFS() if err != nil { return nil, err } info, err := proc.Meminfo() if err != nil { return nil, err } meminfo := &machine.MemInfo{ Memtotal: pointer.SafeDeref(info.MemTotal), Memfree: pointer.SafeDeref(info.MemFree), Memavailable: pointer.SafeDeref(info.MemAvailable), Buffers: pointer.SafeDeref(info.Buffers), Cached: pointer.SafeDeref(info.Cached), Swapcached: pointer.SafeDeref(info.SwapCached), Active: pointer.SafeDeref(info.Active), Inactive: pointer.SafeDeref(info.Inactive), Activeanon: pointer.SafeDeref(info.ActiveAnon), Inactiveanon: pointer.SafeDeref(info.InactiveAnon), Activefile: pointer.SafeDeref(info.ActiveFile), Inactivefile: pointer.SafeDeref(info.InactiveFile), Unevictable: pointer.SafeDeref(info.Unevictable), Mlocked: pointer.SafeDeref(info.Mlocked), Swaptotal: pointer.SafeDeref(info.SwapTotal), Swapfree: pointer.SafeDeref(info.SwapFree), Dirty: pointer.SafeDeref(info.Dirty), Writeback: pointer.SafeDeref(info.Writeback), Anonpages: pointer.SafeDeref(info.AnonPages), Mapped: pointer.SafeDeref(info.Mapped), Shmem: pointer.SafeDeref(info.Shmem), Slab: pointer.SafeDeref(info.Slab), Sreclaimable: pointer.SafeDeref(info.SReclaimable), Sunreclaim: pointer.SafeDeref(info.SUnreclaim), Kernelstack: pointer.SafeDeref(info.KernelStack), Pagetables: pointer.SafeDeref(info.PageTables), Nfsunstable: pointer.SafeDeref(info.NFSUnstable), Bounce: pointer.SafeDeref(info.Bounce), Writebacktmp: pointer.SafeDeref(info.WritebackTmp), Commitlimit: pointer.SafeDeref(info.CommitLimit), Committedas: pointer.SafeDeref(info.CommittedAS), Vmalloctotal: pointer.SafeDeref(info.VmallocTotal), Vmallocused: pointer.SafeDeref(info.VmallocUsed), Vmallocchunk: pointer.SafeDeref(info.VmallocChunk), Hardwarecorrupted: pointer.SafeDeref(info.HardwareCorrupted), Anonhugepages: pointer.SafeDeref(info.AnonHugePages), Shmemhugepages: pointer.SafeDeref(info.ShmemHugePages), Shmempmdmapped: pointer.SafeDeref(info.ShmemPmdMapped), Cmatotal: pointer.SafeDeref(info.CmaTotal), Cmafree: pointer.SafeDeref(info.CmaFree), Hugepagestotal: pointer.SafeDeref(info.HugePagesTotal), Hugepagesfree: pointer.SafeDeref(info.HugePagesFree), Hugepagesrsvd: pointer.SafeDeref(info.HugePagesRsvd), Hugepagessurp: pointer.SafeDeref(info.HugePagesSurp), Hugepagesize: pointer.SafeDeref(info.Hugepagesize), Directmap4K: pointer.SafeDeref(info.DirectMap4k), Directmap2M: pointer.SafeDeref(info.DirectMap2M), Directmap1G: pointer.SafeDeref(info.DirectMap1G), } reply = &machine.MemoryResponse{ Messages: []*machine.Memory{ { Meminfo: meminfo, }, }, } return reply, err } // EtcdMemberList implements the machine.MachineServer interface. func (s *Server) EtcdMemberList(ctx context.Context, in *machine.EtcdMemberListRequest) (*machine.EtcdMemberListResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "member list"); err != nil { return nil, err } var ( client *etcd.Client err error ) if in.QueryLocal { client, err = etcd.NewLocalClient(ctx) } else { client, err = etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) } if err != nil { return nil, err } //nolint:errcheck defer client.Close() ctx = clientv3.WithRequireLeader(ctx) resp, err := client.MemberList(ctx) if err != nil { return nil, err } return &machine.EtcdMemberListResponse{ Messages: []*machine.EtcdMembers{ { LegacyMembers: xslices.Map(resp.Members, (*etcdserverpb.Member).GetName), Members: xslices.Map(resp.Members, func(member *etcdserverpb.Member) *machine.EtcdMember { return &machine.EtcdMember{ Id: member.GetID(), Hostname: member.GetName(), PeerUrls: member.GetPeerURLs(), ClientUrls: member.GetClientURLs(), IsLearner: member.GetIsLearner(), } }), }, }, }, nil } // EtcdRemoveMemberByID implements the machine.MachineServer interface. func (s *Server) EtcdRemoveMemberByID(ctx context.Context, in *machine.EtcdRemoveMemberByIDRequest) (*machine.EtcdRemoveMemberByIDResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd remove member"); err != nil { return nil, err } client, err := etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } defer client.Close() //nolint:errcheck ctx = clientv3.WithRequireLeader(ctx) if err = client.RemoveMemberByMemberID(ctx, in.MemberId); err != nil { if errors.Is(err, rpctypes.ErrMemberNotFound) { return nil, status.Error(codes.NotFound, err.Error()) } return nil, fmt.Errorf("failed to remove member: %w", err) } return &machine.EtcdRemoveMemberByIDResponse{ Messages: []*machine.EtcdRemoveMemberByID{ {}, }, }, nil } // EtcdLeaveCluster implements the machine.MachineServer interface. func (s *Server) EtcdLeaveCluster(ctx context.Context, in *machine.EtcdLeaveClusterRequest) (*machine.EtcdLeaveClusterResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd leave"); err != nil { return nil, err } client, err := etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } defer client.Close() //nolint:errcheck ctx = clientv3.WithRequireLeader(ctx) if err = client.LeaveCluster(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()); err != nil { return nil, fmt.Errorf("failed to leave cluster: %w", err) } return &machine.EtcdLeaveClusterResponse{ Messages: []*machine.EtcdLeaveCluster{ {}, }, }, nil } // EtcdForfeitLeadership implements the machine.MachineServer interface. func (s *Server) EtcdForfeitLeadership(ctx context.Context, in *machine.EtcdForfeitLeadershipRequest) (*machine.EtcdForfeitLeadershipResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd forfeit leadership"); err != nil { return nil, err } client, err := etcd.NewClientFromControlPlaneIPs(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } defer client.Close() //nolint:errcheck ctx = clientv3.WithRequireLeader(ctx) memberID, err := etcd.GetLocalMemberID(ctx, s.Controller.Runtime().State().V1Alpha2().Resources()) if err != nil { return nil, err } leader, err := client.ForfeitLeadership(ctx, etcdresource.FormatMemberID(memberID)) if err != nil { return nil, fmt.Errorf("failed to forfeit leadership: %w", err) } return &machine.EtcdForfeitLeadershipResponse{ Messages: []*machine.EtcdForfeitLeadership{ { Member: leader, }, }, }, nil } // EtcdSnapshot implements the machine.MachineServer interface. func (s *Server) EtcdSnapshot(in *machine.EtcdSnapshotRequest, srv machine.MachineService_EtcdSnapshotServer) error { if err := machinehelper.CheckControlplane(srv.Context(), s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd snapshot"); err != nil { return err } ctx, cancel := context.WithCancel(srv.Context()) defer cancel() client, err := etcd.NewLocalClient(ctx) if err != nil { return fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() rd, err := client.Snapshot(srv.Context()) if err != nil { return fmt.Errorf("failed reading etcd snapshot: %w", err) } chunker := stream.NewChunker(ctx, rd) chunkCh := chunker.Read() for data := range chunkCh { err := srv.SendMsg(&common.Data{Bytes: data}) if err != nil { cancel() return err } } return nil } // EtcdRecover implements the machine.MachineServer interface. // //nolint:gocyclo func (s *Server) EtcdRecover(srv machine.MachineService_EtcdRecoverServer) error { if _, err := os.Stat(filepath.Dir(constants.EtcdRecoverySnapshotPath)); err != nil { if errors.Is(err, fs.ErrNotExist) { return status.Error(codes.FailedPrecondition, "etcd service is not ready for recovery yet") } return err } if err := machinehelper.CheckControlplane(srv.Context(), s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd recover"); err != nil { return err } snapshot, err := os.OpenFile(constants.EtcdRecoverySnapshotPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o700) if err != nil { return fmt.Errorf("error creating etcd recovery snapshot: %w", err) } defer snapshot.Close() //nolint:errcheck successfulUpload := false defer func() { if !successfulUpload { os.Remove(snapshot.Name()) //nolint:errcheck } }() for { var msg *common.Data msg, err = srv.Recv() if err != nil { if err == io.EOF { break } return err } _, err = snapshot.Write(msg.Bytes) if err != nil { return fmt.Errorf("error writing snapshot: %w", err) } } if err = snapshot.Sync(); err != nil { return fmt.Errorf("error fsyncing snapshot: %w", err) } if err = snapshot.Close(); err != nil { return fmt.Errorf("error closing snapshot: %w", err) } successfulUpload = true return srv.SendAndClose(&machine.EtcdRecoverResponse{ Messages: []*machine.EtcdRecover{ {}, }, }) } func mapAlarms(alarms []*etcdserverpb.AlarmMember) []*machine.EtcdMemberAlarm { mapAlarmType := func(alarmType etcdserverpb.AlarmType) machine.EtcdMemberAlarm_AlarmType { switch alarmType { case etcdserverpb.AlarmType_NOSPACE: return machine.EtcdMemberAlarm_NOSPACE case etcdserverpb.AlarmType_CORRUPT: return machine.EtcdMemberAlarm_CORRUPT case etcdserverpb.AlarmType_NONE: return machine.EtcdMemberAlarm_NONE default: return machine.EtcdMemberAlarm_NONE } } return xslices.Map(alarms, func(alarm *etcdserverpb.AlarmMember) *machine.EtcdMemberAlarm { return &machine.EtcdMemberAlarm{ MemberId: alarm.MemberID, Alarm: mapAlarmType(alarm.Alarm), } }) } // EtcdAlarmList lists etcd alarms for the current node. // // This method is available only on control plane nodes (which run etcd). func (s *Server) EtcdAlarmList(ctx context.Context, in *emptypb.Empty) (*machine.EtcdAlarmListResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd alarm list"); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.AlarmList(ctx) if err != nil { return nil, fmt.Errorf("failed to list etcd alarms: %w", err) } return &machine.EtcdAlarmListResponse{ Messages: []*machine.EtcdAlarm{ { MemberAlarms: mapAlarms(resp.Alarms), }, }, }, nil } // EtcdAlarmDisarm disarms etcd alarms for the current node. // // This method is available only on control plane nodes (which run etcd). func (s *Server) EtcdAlarmDisarm(ctx context.Context, in *emptypb.Empty) (*machine.EtcdAlarmDisarmResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd alarm disarm"); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.AlarmDisarm(ctx, &clientv3.AlarmMember{}) if err != nil { return nil, fmt.Errorf("failed to disarm etcd alarm: %w", err) } return &machine.EtcdAlarmDisarmResponse{ Messages: []*machine.EtcdAlarmDisarm{ { MemberAlarms: mapAlarms(resp.Alarms), }, }, }, nil } // EtcdDefragment defragments etcd data directory for the current node. // // Defragmentation is a resource-heavy operation, so it should only run on a specific // node. // // This method is available only on control plane nodes (which run etcd). func (s *Server) EtcdDefragment(ctx context.Context, in *emptypb.Empty) (*machine.EtcdDefragmentResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd defragment"); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() _, err = client.Defragment(ctx, nethelpers.JoinHostPort("localhost", constants.EtcdClientPort)) if err != nil { return nil, fmt.Errorf("failed to defragment etcd: %w", err) } return &machine.EtcdDefragmentResponse{ Messages: []*machine.EtcdDefragment{ {}, }, }, nil } // EtcdStatus returns etcd status for the member of the cluster. // // This method is available only on control plane nodes (which run etcd). func (s *Server) EtcdStatus(ctx context.Context, in *emptypb.Empty) (*machine.EtcdStatusResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd status"); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.Status(ctx, nethelpers.JoinHostPort("localhost", constants.EtcdClientPort)) if err != nil { return nil, fmt.Errorf("failed to query etcd status: %w", err) } storageVersion := resp.StorageVersion // NOTE: this field is only filled on >3.6.0, thus we need a workaround for previous ETCD versions if storageVersion == "" { if v, err := semver.Parse(resp.Version); err == nil { storageVersion = fmt.Sprintf("%d.%d.0", v.Major, v.Minor) } else { // we swallow the error here, as we don't want to fail the request // over something that is not critical storageVersion = "unknown" } } return &machine.EtcdStatusResponse{ Messages: []*machine.EtcdStatus{ { MemberStatus: &machine.EtcdMemberStatus{ MemberId: resp.Header.MemberId, ProtocolVersion: resp.Version, DbSize: resp.DbSize, DbSizeInUse: resp.DbSizeInUse, Leader: resp.Leader, RaftIndex: resp.RaftIndex, RaftTerm: resp.RaftTerm, RaftAppliedIndex: resp.RaftAppliedIndex, StorageVersion: storageVersion, Errors: resp.Errors, IsLearner: resp.IsLearner, }, }, }, }, nil } // EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. // // This method is available only on control plane nodes (which run etcd). func (s *Server) EtcdDowngradeCancel(ctx context.Context, _ *emptypb.Empty) (*machine.EtcdDowngradeCancelResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd downgrade cancel"); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.Downgrade(ctx, clientv3.DowngradeCancel, "") if err != nil { return nil, fmt.Errorf("failed to query etcd status: %w", err) } return &machine.EtcdDowngradeCancelResponse{ Messages: []*machine.EtcdDowngradeCancel{ { ClusterDowngrade: &machine.EtcdClusterDowngrade{ ClusterVersion: resp.Version, }, }, }, }, nil } // EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. // // This method is available only on control plane nodes (which run etcd). // //nolint:dupl func (s *Server) EtcdDowngradeEnable(ctx context.Context, in *machine.EtcdDowngradeEnableRequest) (*machine.EtcdDowngradeEnableResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd downgrade enable"); err != nil { return nil, err } if err := validateDowngrade(in.Version); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.Downgrade(ctx, clientv3.DowngradeEnable, in.Version) if err != nil { return nil, fmt.Errorf("failed to query etcd status: %w", err) } return &machine.EtcdDowngradeEnableResponse{ Messages: []*machine.EtcdDowngradeEnable{ { ClusterDowngrade: &machine.EtcdClusterDowngrade{ ClusterVersion: resp.Version, }, }, }, }, nil } // EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. // // This method is available only on control plane nodes (which run etcd). // //nolint:dupl func (s *Server) EtcdDowngradeValidate(ctx context.Context, in *machine.EtcdDowngradeValidateRequest) (*machine.EtcdDowngradeValidateResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "etcd downgrade validate"); err != nil { return nil, err } if err := validateDowngrade(in.Version); err != nil { return nil, err } client, err := etcd.NewLocalClient(ctx) if err != nil { return nil, fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() resp, err := client.Downgrade(ctx, clientv3.DowngradeValidate, in.Version) if err != nil { return nil, fmt.Errorf("failed to query etcd status: %w", err) } return &machine.EtcdDowngradeValidateResponse{ Messages: []*machine.EtcdDowngradeValidate{ { ClusterDowngrade: &machine.EtcdClusterDowngrade{ ClusterVersion: resp.Version, }, }, }, }, nil } var minEtcdDowngradeVersion = semver.Version{Major: 3, Minor: 5} func validateDowngrade(version string) error { if version == "" { return status.Error(codes.InvalidArgument, "version is required for etcd downgrade") } parts := strings.Split(version, ".") if len(parts) != 2 { return status.Error(codes.InvalidArgument, "version should be in MAJOR.MINOR format") } major, err := strconv.ParseInt(parts[0], 10, 64) if err != nil { return status.Error(codes.InvalidArgument, "major version should be a number") } minor, err := strconv.ParseInt(parts[1], 10, 64) if err != nil { return status.Error(codes.InvalidArgument, "minor version should be a number") } semverVersion := semver.Version{Major: uint64(major), Minor: uint64(minor)} if semverVersion.LT(minEtcdDowngradeVersion) { return status.Error(codes.InvalidArgument, "etcd downgrade is only supported to 3.5 and later versions") } return nil } // GenerateClientConfiguration implements the machine.MachineServer interface. func (s *Server) GenerateClientConfiguration(ctx context.Context, in *machine.GenerateClientConfigurationRequest) (*machine.GenerateClientConfigurationResponse, error) { if err := machinehelper.CheckControlplane(ctx, s.Controller.Runtime().State().V1Alpha2().Resources(), "generate client configuration"); err != nil { return nil, err } crtTTL := in.CrtTtl.AsDuration() if crtTTL <= 0 { return nil, status.Error(codes.InvalidArgument, "crt_ttl should be positive") } roles, _ := role.Parse(in.Roles) secretsBundle := secrets.NewBundleFromConfig(secrets.NewFixedClock(time.Now()), s.Controller.Runtime().Config()) cert, err := secretsBundle.GenerateTalosAPIClientCertificateWithTTL(roles, crtTTL) if err != nil { return nil, err } // make a nice context name contextName := s.Controller.Runtime().Config().Cluster().Name() if r := roles.Strings(); len(r) == 1 { contextName = strings.TrimPrefix(r[0], role.Prefix) + "@" + contextName } talosconfig := clientconfig.NewConfig(contextName, nil, secretsBundle.Certs.OS.Crt, cert) b, err := talosconfig.Bytes() if err != nil { return nil, err } reply := &machine.GenerateClientConfigurationResponse{ Messages: []*machine.GenerateClientConfiguration{ { Ca: secretsBundle.Certs.OS.Crt, Crt: cert.Crt, Key: cert.Key, Talosconfig: b, }, }, } return reply, nil } type packetStreamWriter struct { stream machine.MachineService_PacketCaptureServer } func (w *packetStreamWriter) Write(data []byte) (int, error) { // copy the data as the stream may not send it immediately data = slices.Clone(data) err := w.stream.Send(&common.Data{Bytes: data}) if err != nil { return 0, err } return len(data), nil } // PacketCapture performs packet capture and streams the pcap file. // //nolint:gocyclo func (s *Server) PacketCapture(in *machine.PacketCaptureRequest, srv machine.MachineService_PacketCaptureServer) error { linkInfo, err := safe.StateGetResource(srv.Context(), s.Controller.Runtime().State().V1Alpha2().Resources(), network.NewLinkStatus(network.NamespaceName, in.Interface)) if err != nil { if state.IsNotFoundError(err) { return status.Errorf(codes.NotFound, "interface %q not found", in.Interface) } return err } var linkType pcap.LinkType switch linkInfo.TypedSpec().Type { //nolint:exhaustive case nethelpers.LinkEther, nethelpers.LinkLoopbck: linkType = pcap.LinkTypeEthernet case nethelpers.LinkNone: linkType = pcap.LinkTypeRaw default: return status.Errorf(codes.InvalidArgument, "unsupported link type %s", linkInfo.TypedSpec().Type) } if in.SnapLen == 0 { in.SnapLen = afpacket.DefaultFrameSize } filter := make([]bpf.RawInstruction, 0, len(in.BpfFilter)) for _, f := range in.BpfFilter { filter = append(filter, bpf.RawInstruction{ Op: uint16(f.Op), Jt: uint8(f.Jt), Jf: uint8(f.Jf), K: f.K, }) } handle, err := afpacket.NewTPacket( afpacket.OptInterface(in.Interface), afpacket.OptPollTimeout(100*time.Millisecond), afpacket.OptSocketType(unix.SOCK_RAW|unix.SOCK_CLOEXEC), ) if err != nil { return fmt.Errorf("error creating afpacket handle: %w", err) } if len(filter) > 0 { if err = handle.SetBPF(filter); err != nil { handle.Close() return fmt.Errorf("error setting BPF filter: %w", err) } } if err = handle.SetPromiscuous(in.Promiscuous); err != nil { handle.Close() return fmt.Errorf("error setting promiscuous mode %v: %w", in.Promiscuous, err) } return capturePackets(srv.Context(), &packetStreamWriter{srv}, handle, in.SnapLen, linkType) } //nolint:gocyclo,cyclop func capturePackets(ctx context.Context, w io.Writer, handle *afpacket.TPacket, snapLen uint32, linkType pcap.LinkType) error { defer handle.Close() pcapw := pcap.NewWriter(w) if err := pcapw.WriteFileHeader(snapLen, linkType); err != nil { return err } defer func() { infoMessage := "pcap: " stats, errStats := handle.Stats() if errStats == nil { infoMessage += fmt.Sprintf("packets captured %d, polls %d", stats.Packets, stats.Polls) } _, socketStatsV3, socketStatsErr := handle.SocketStats() if socketStatsErr == nil { infoMessage += fmt.Sprintf(", socket stats: drops %d, packets %d, queue freezes %d", socketStatsV3.Drops(), socketStatsV3.Packets(), socketStatsV3.QueueFreezes()) } log.Print(infoMessage) }() for { select { case <-ctx.Done(): return ctx.Err() default: } data, captureData, err := handle.ZeroCopyReadPacketData() if err == nil { if err = pcapw.WritePacket(captureData, data); err != nil { return err } continue } // Immediately retry for temporary network errors if nerr, ok := err.(net.Error); ok && nerr.Temporary() { //nolint:staticcheck continue } // Immediately retry for EAGAIN and poll timeout if errors.Is(err, syscall.EAGAIN) || errors.Is(err, afpacket.ErrTimeout) { continue } // Immediately break for known unrecoverable errors if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.ErrNoProgress) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, io.ErrShortBuffer) || errors.Is(err, syscall.EBADF) || errors.Is(err, afpacket.ErrPoll) || strings.Contains(err.Error(), "use of closed file") { return err } time.Sleep(5 * time.Millisecond) // short sleep before retrying some errors } } func tryLockUpgradeMutex(ctx context.Context, etcdClient *etcd.Client) (unlock func(), err error) { sess, err := concurrency.NewSession(etcdClient.Client, concurrency.WithContext(ctx), concurrency.WithTTL(MinimumEtcdUpgradeLeaseLockSeconds), ) if err != nil { return nil, fmt.Errorf("error establishing etcd concurrency session: %w", err) } mu := concurrency.NewMutex(sess, constants.EtcdTalosEtcdUpgradeMutex) if err = mu.TryLock(ctx); err != nil { return nil, fmt.Errorf("error trying to lock etcd upgrade mutex: %w", err) } log.Printf("etcd upgrade mutex locked with session ID %08x", sess.Lease()) return func() { unlockCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := mu.Unlock(unlockCtx); err != nil { log.Printf("error unlocking etcd upgrade mutex: %v", err) } if err := sess.Close(); err != nil { log.Printf("error closing etcd upgrade mutex session: %v", err) } log.Printf("etcd upgrade mutex unlocked and session closed") }, nil } // Netstat implements the machine.MachineServer interface. func (s *Server) Netstat(ctx context.Context, req *machine.NetstatRequest) (*machine.NetstatResponse, error) { if req == nil { req = new(machine.NetstatRequest) } features := netstat.EnableFeatures{ TCP: req.L4Proto.Tcp, TCP6: req.L4Proto.Tcp6, UDP: req.L4Proto.Udp, UDP6: req.L4Proto.Udp6, UDPLite: req.L4Proto.Udplite, UDPLite6: req.L4Proto.Udplite6, Raw: req.L4Proto.Raw, Raw6: req.L4Proto.Raw6, PID: req.Feature.Pid, NoHostNetwork: !req.Netns.Hostnetwork, AllNetNs: req.Netns.Allnetns, NetNsName: req.Netns.Netns, } var fn netstat.AcceptFn switch req.Filter { case machine.NetstatRequest_ALL: fn = func(*netstat.SockTabEntry) bool { return true } case machine.NetstatRequest_LISTENING: fn = func(s *netstat.SockTabEntry) bool { return s.RemoteEndpoint.IP.IsUnspecified() && s.RemoteEndpoint.Port == 0 } case machine.NetstatRequest_CONNECTED: fn = func(s *netstat.SockTabEntry) bool { return !s.RemoteEndpoint.IP.IsUnspecified() && s.RemoteEndpoint.Port != 0 } } netstatResp, err := netstat.Netstat(ctx, features, fn) if err != nil { return nil, err } records := make([]*machine.ConnectRecord, len(netstatResp)) for i, entry := range netstatResp { records[i] = &machine.ConnectRecord{ L4Proto: entry.Transport, Localip: entry.LocalEndpoint.IP.String(), Localport: uint32(entry.LocalEndpoint.Port), Remoteip: entry.RemoteEndpoint.IP.String(), Remoteport: uint32(entry.RemoteEndpoint.Port), State: machine.ConnectRecord_State(entry.State), Txqueue: entry.TxQueue, Rxqueue: entry.RxQueue, Tr: machine.ConnectRecord_TimerActive(entry.Tr), Timerwhen: entry.TimerWhen, Retrnsmt: entry.Retrnsmt, Uid: entry.UID, Timeout: entry.Timeout, Inode: entry.Inode, Ref: entry.Ref, Pointer: entry.Pointer, Process: &machine.ConnectRecord_Process{}, Netns: entry.NetNS, } if entry.Process != nil { records[i].Process = &machine.ConnectRecord_Process{ Pid: uint32(entry.Process.Pid), Name: entry.Process.Name, } } } reply := &machine.NetstatResponse{ Messages: []*machine.Netstat{ { Connectrecord: records, }, }, } return reply, err } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_time.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "time" ntpclient "github.com/beevik/ntp" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" "github.com/siderolabs/talos/internal/pkg/ntp" timeapi "github.com/siderolabs/talos/pkg/machinery/api/time" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ConfigProvider defines an interface sufficient for the TimeServer. type ConfigProvider interface { Config() config.Config } // TimeServer implements TimeService API. type TimeServer struct { timeapi.UnimplementedTimeServiceServer ConfigProvider ConfigProvider } // Register implements the factory.Registrator interface. func (r *TimeServer) Register(s *grpc.Server) { timeapi.RegisterTimeServiceServer(s, r) } // Time issues a query to the configured ntp server and displays the results. func (r *TimeServer) Time(ctx context.Context, in *emptypb.Empty) (reply *timeapi.TimeResponse, err error) { var timeServers []string if r.ConfigProvider.Config() != nil { if cfg := r.ConfigProvider.Config().NetworkTimeSyncConfig(); cfg != nil { timeServers = cfg.Servers() } } if len(timeServers) == 0 { timeServers = []string{constants.DefaultNTPServer} } return r.TimeCheck(ctx, &timeapi.TimeRequest{ Server: timeServers[0], }) } // TimeCheck issues a query to the specified ntp server and displays the results. func (r *TimeServer) TimeCheck(ctx context.Context, in *timeapi.TimeRequest) (reply *timeapi.TimeResponse, err error) { var t time.Time if ntp.IsPTPDevice(in.Server) { ts, err := ntp.QueryPTPDevice(in.Server) if err != nil { return nil, fmt.Errorf("error querying PTP device %q: %w", in.Server, err) } t = time.Unix(ts.Sec, ts.Nsec) } else { rt, err := ntpclient.Query(in.Server) if err != nil { return nil, fmt.Errorf("error querying NTP server %q: %w", in.Server, err) } if err = rt.Validate(); err != nil { return nil, fmt.Errorf("error validating NTP response: %w", err) } t = rt.Time } return &timeapi.TimeResponse{ Messages: []*timeapi.Time{ { Server: in.Server, Localtime: timestamppb.New(time.Now()), Remotetime: timestamppb.New(t), }, }, }, nil } ================================================ FILE: internal/app/machined/internal/server/v1alpha1/v1alpha1_time_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "fmt" "net" "os" "testing" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/emptypb" runtime "github.com/siderolabs/talos/internal/app/machined/internal/server/v1alpha1" "github.com/siderolabs/talos/pkg/grpc/factory" timeapi "github.com/siderolabs/talos/pkg/machinery/api/time" "github.com/siderolabs/talos/pkg/machinery/client/dialer" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) type TimedSuite struct { suite.Suite } func TestTimedSuite(t *testing.T) { // Hide all our state transition messages // log.SetOutput(ioutil.Discard) suite.Run(t, new(TimedSuite)) } type mockConfigProvider struct { timeServer string } func (provider *mockConfigProvider) Config() config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeServers: []string{provider.timeServer}, }, }, }) } func (suite *TimedSuite) TestTime() { testServer := "time.cloudflare.com" // Create gRPC server api := &runtime.TimeServer{ ConfigProvider: &mockConfigProvider{timeServer: testServer}, } server := factory.NewServer(api) listener, err := fakeTimedRPC(suite.T()) suite.Assert().NoError(err) defer server.Stop() //nolint:errcheck defer os.Remove(listener.Addr().String()) //nolint:errcheck go server.Serve(listener) conn, err := grpc.NewClient( fmt.Sprintf("%s://%s", "unix", listener.Addr().String()), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer.DialUnix()), ) suite.Require().NoError(err) suite.T().Cleanup(func() { conn.Close() }) //nolint:errcheck nClient := timeapi.NewTimeServiceClient(conn) reply, err := nClient.Time(context.Background(), &emptypb.Empty{}) suite.Require().NoError(err) suite.Assert().Equal(reply.Messages[0].Server, testServer) } func (suite *TimedSuite) TestTimeCheck() { testServer := "time.cloudflare.com" // Create ntp client with bogus server // so we can check that we explicitly check the time of the // specified server ( testserver ) // Create gRPC server api := &runtime.TimeServer{} server := factory.NewServer(api) listener, err := fakeTimedRPC(suite.T()) suite.Assert().NoError(err) defer server.Stop() //nolint:errcheck defer os.Remove(listener.Addr().String()) //nolint:errcheck go server.Serve(listener) conn, err := grpc.NewClient( fmt.Sprintf("%s://%s", "unix", listener.Addr().String()), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer.DialUnix()), ) suite.Require().NoError(err) suite.T().Cleanup(func() { conn.Close() }) //nolint:errcheck nClient := timeapi.NewTimeServiceClient(conn) reply, err := nClient.TimeCheck(context.Background(), &timeapi.TimeRequest{Server: testServer}) suite.Require().NoError(err) suite.Assert().Equal(reply.Messages[0].Server, testServer) } func fakeTimedRPC(t *testing.T) (net.Listener, error) { t.Helper() tmpfile, err := os.CreateTemp(t.TempDir(), "timed") require.NoError(t, err) return factory.NewListener( t.Context(), factory.Network("unix"), factory.SocketPath(tmpfile.Name()), ) } ================================================ FILE: internal/app/machined/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package machined provides machined implementation. package main import ( "context" "errors" "fmt" "log" "net/http" "os" "os/signal" "path/filepath" "sync" "syscall" "time" "github.com/hashicorp/go-cleanhttp" "github.com/siderolabs/go-cmd/pkg/cmd/proc" "github.com/siderolabs/go-cmd/pkg/cmd/proc/reaper" debug "github.com/siderolabs/go-debug" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/apid" "github.com/siderolabs/talos/internal/app/dashboard" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/emergency" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1" startuptasks "github.com/siderolabs/talos/internal/app/machined/pkg/startup" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/internal/app/poweroff" "github.com/siderolabs/talos/internal/app/trustd" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/startup" ) func init() { // Patch a default HTTP client with updated transport to handle cases when default client is being used. http.DefaultClient.Transport = httpdefaults.PatchTransport(cleanhttp.DefaultPooledTransport()) } func recovery(ctx context.Context) { if r := recover(); r != nil { var ( err error ok bool ) err, ok = r.(error) if ok { handle(ctx, err) } } } // syncNonVolatileStorageBuffers invokes unix.Sync and waits up to 30 seconds // for it to finish. // // See http://man7.org/linux/man-pages/man2/reboot.2.html. func syncNonVolatileStorageBuffers() { syncdone := make(chan struct{}) go func() { defer close(syncdone) unix.Sync() }() log.Printf("waiting for sync...") for i := 29; i >= 0; i-- { select { case <-syncdone: log.Printf("sync done") return case <-time.After(time.Second): } if i != 0 { log.Printf("waiting %d more seconds for sync to finish", i) } } log.Printf("sync hasn't completed in time, aborting...") } //nolint:gocyclo func handle(ctx context.Context, err error) { rebootCmd := int(emergency.RebootCmd.Load()) var rebootErr runtime.RebootError if errors.As(err, &rebootErr) { // not a failure, but wrapped reboot command rebootCmd = rebootErr.Cmd err = nil } if err != nil { log.Print(err) revertBootloader(ctx) if p := procfs.ProcCmdline().Get(constants.KernelParamPanic).First(); p != nil { if *p == "0" { log.Printf("panic=0 kernel flag found, sleeping forever") rebootCmd = 0 } } if rebootCmd == unix.LINUX_REBOOT_CMD_RESTART { for i := 10; i >= 0; i-- { log.Printf("rebooting in %d seconds\n", i) time.Sleep(1 * time.Second) } } } if err = proc.KillAll(); err != nil { log.Printf("error killing all procs: %s", err) } if err = mount.UnmountAll(); err != nil { log.Printf("error unmounting: %s", err) } syncNonVolatileStorageBuffers() if rebootCmd == 0 { exitSignal := make(chan os.Signal, 1) signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM) <-exitSignal } else if unix.Reboot(rebootCmd) == nil { // Wait forever. select {} } } func runDebugServer(ctx context.Context) { const debugAddr = ":9982" debugLogFunc := func(msg string) { log.Print(msg) } if err := debug.ListenAndServe(ctx, debugAddr, debugLogFunc); err != nil { log.Fatalf("failed to start debug server: %s", err) } } func run() error { // Limit GOMAXPROCS. startup.LimitMaxProcs(constants.MachinedMaxProcs) // Initialize the controller without a config. c, err := v1alpha1runtime.NewController() if err != nil { return err } revertSetState(c.Runtime().State().V1Alpha2().Resources()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() logger, err := c.V1Alpha2().MakeLogger("early-startup") if err != nil { return err } start := time.Now() // Run startup tasks, and then run the entrypoint. return startuptasks.RunTasks(ctx, logger, c.Runtime(), append( startuptasks.DefaultTasks(), func(ctx context.Context, log *zap.Logger, _ runtime.Runtime, _ startuptasks.NextTaskFunc) error { logger.Info("early startup done", zap.Duration("duration", time.Since(start))) return runEntrypoint(ctx, c) }, )...) } //nolint:gocyclo func runEntrypoint(ctx context.Context, c *v1alpha1runtime.Controller) error { errCh := make(chan error) var controllerWaitGroup sync.WaitGroup defer controllerWaitGroup.Wait() // wait for controller-runtime to finish before rebooting ctx, cancel := context.WithCancel(ctx) defer cancel() drainer := runtime.NewDrainer() defer func() { drainCtx, drainCtxCancel := context.WithTimeout(context.Background(), time.Second*10) defer drainCtxCancel() if e := drainer.Drain(drainCtx); e != nil { log.Printf("WARNING: failed to drain controllers: %s", e) } }() go runDebugServer(ctx) // Schedule service shutdown on any return. defer system.Services(c.Runtime()).Shutdown(ctx) // Start signal and ACPI listeners. go func() { if e := c.ListenForEvents(ctx); e != nil { log.Printf("WARNING: signals and ACPI events will be ignored: %s", e) } }() controllerWaitGroup.Go(func() { if e := c.V1Alpha2().Run(ctx, drainer); e != nil { ctrlErr := fmt.Errorf("fatal controller runtime error: %s", e) log.Printf("controller runtime goroutine error: %s", ctrlErr) errCh <- ctrlErr } log.Printf("controller runtime finished") }) // Load machined service. system.Services(c.Runtime()).Load( &services.Machined{Controller: c}, ) initializeCanceled := false // Initialize the machine. if err := c.Run(ctx, runtime.SequenceInitialize, nil); err != nil { if errors.Is(err, context.Canceled) { initializeCanceled = true } else { return err } } // If Initialize sequence was canceled, don't run any other sequence. if !initializeCanceled { // Perform an installation if required. if err := c.Run(ctx, runtime.SequenceInstall, nil); err != nil { return err } // Boot the machine. if err := c.Run(ctx, runtime.SequenceBoot, nil); err != nil && !errors.Is(err, context.Canceled) { return err } } // Watch and handle runtime events. //nolint:errcheck _ = c.Runtime().Events().Watch( func(events <-chan runtime.EventInfo) { for { for event := range events { switch msg := event.Payload.(type) { case *machine.SequenceEvent: if msg.Error != nil { if msg.Error.GetCode() == common.Code_LOCKED || msg.Error.GetCode() == common.Code_CANCELED { // ignore sequence lock and canceled errors, they're not fatal continue } errCh <- fmt.Errorf( "fatal sequencer error in %q sequence: %v", msg.GetSequence(), msg.GetError().String(), ) } case *machine.RestartEvent: errCh <- runtime.RebootError{Cmd: int(msg.Cmd)} } } } }, ) return <-errCh } func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() switch filepath.Base(os.Args[0]) { case "apid": apid.Main() return case "trustd": trustd.Main() return // Azure uses the hv_utils kernel module to shutdown the node in hyper-v by calling perform_shutdown which will call orderly_poweroff which will call /sbin/poweroff. case "poweroff", "shutdown": poweroff.Main(os.Args) return case "dashboard": dashboard.Main() return default: } // Setup panic handler. defer recovery(ctx) // Initialize the process reaper. reaper.Run() defer reaper.Shutdown() handle(ctx, run()) } ================================================ FILE: internal/app/machined/pkg/adapters/block/mount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package block implements adapters wrapping resources/block to provide additional functionality. package block ================================================ FILE: internal/app/machined/pkg/adapters/block/volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "fmt" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumeConfigSpec adapter provides conversion from MountStatus. // //nolint:revive,golint func VolumeConfigSpec(r *block.VolumeConfigSpec) volumeConfigSpec { return volumeConfigSpec{ volumeConfigSpec: r, } } type volumeConfigSpec struct { volumeConfigSpec *block.VolumeConfigSpec } // WithRoot adapts VolumeConfigSpec to xfs.Root and calls the provided callback with it. func (a volumeConfigSpec) ApplyEncryptionConfig(in config.EncryptionConfig) error { out := a.volumeConfigSpec if in == nil { out.Encryption = block.EncryptionSpec{} return nil } out.Encryption.Provider = in.Provider() out.Encryption.Cipher = in.Cipher() out.Encryption.KeySize = in.KeySize() out.Encryption.BlockSize = in.BlockSize() out.Encryption.PerfOptions = in.Options() out.Encryption.Keys = make([]block.EncryptionKey, len(in.Keys())) for i, key := range in.Keys() { out.Encryption.Keys[i].Slot = key.Slot() out.Encryption.Keys[i].LockToSTATE = key.LockToSTATE() switch { case key.Static() != nil: out.Encryption.Keys[i].Type = block.EncryptionKeyStatic out.Encryption.Keys[i].StaticPassphrase = key.Static().Key() case key.NodeID() != nil: out.Encryption.Keys[i].Type = block.EncryptionKeyNodeID case key.KMS() != nil: out.Encryption.Keys[i].Type = block.EncryptionKeyKMS out.Encryption.Keys[i].KMSEndpoint = key.KMS().Endpoint() case key.TPM() != nil: out.Encryption.Keys[i].Type = block.EncryptionKeyTPM out.Encryption.Keys[i].TPMCheckSecurebootStatusOnEnroll = key.TPM().CheckSecurebootOnEnroll() out.Encryption.Keys[i].TPMPCRs = key.TPM().PCRs() out.Encryption.Keys[i].TPMPubKeyPCRs = key.TPM().PubKeyPCRs() default: return fmt.Errorf("unsupported encryption key type: slot %d", key.Slot()) } } return nil } ================================================ FILE: internal/app/machined/pkg/adapters/block/volume_mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "fmt" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/opentree" ) // VolumeMountStatus adapter provides conversion from MountStatus. // //nolint:revive,golint func VolumeMountStatus(r *block.VolumeMountStatus) volumeMountStatus { return volumeMountStatus{ VolumeMountStatus: r, } } type volumeMountStatus struct { VolumeMountStatus *block.VolumeMountStatus } // WithRoot adapts VolumeMountStatus to xfs.Root and calls the provided callback with it. func (a volumeMountStatus) WithRoot(logger *zap.Logger, callback func(root xfs.Root) error) error { var root xfs.Root root, ok := a.VolumeMountStatus.TypedSpec().Root().(xfs.Root) if !ok || root == nil || !a.VolumeMountStatus.TypedSpec().Detached { root = &xfs.UnixRoot{ FS: opentree.NewFromPath(a.VolumeMountStatus.TypedSpec().Target), } if err := root.OpenFS(); err != nil { return fmt.Errorf("error opening filesystem: %w", err) } defer func() { if err := root.Close(); err != nil { logger.Error("error closing filesystem", zap.Error(err)) } }() } return callback(root) } ================================================ FILE: internal/app/machined/pkg/adapters/cluster/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cluster implements adapters wrapping resources/cluster to provide additional functionality. package cluster ================================================ FILE: internal/app/machined/pkg/adapters/cluster/identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "crypto/rand" "encoding/hex" "io" "github.com/jxskiss/base62" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // IdentitySpec adapter provides identity generation. // //nolint:revive,golint func IdentitySpec(r *cluster.IdentitySpec) identity { return identity{ IdentitySpec: r, } } type identity struct { *cluster.IdentitySpec } // Generate new identity. func (a identity) Generate() error { buf := make([]byte, constants.DefaultNodeIdentitySize) if _, err := io.ReadFull(rand.Reader, buf); err != nil { return err } a.IdentitySpec.NodeID = base62.EncodeToString(buf) return nil } // ConvertMachineID returns /etc/machine-id compatible representation. func (a identity) ConvertMachineID() ([]byte, error) { raw, err := base62.DecodeString(a.IdentitySpec.NodeID) if err != nil { return nil, err } buf := make([]byte, 32) hex.Encode(buf, raw[:16]) return buf, nil } ================================================ FILE: internal/app/machined/pkg/adapters/cluster/identity_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func TestIdentityGenerate(t *testing.T) { t.Parallel() var spec1, spec2 cluster.IdentitySpec require.NoError(t, clusteradapter.IdentitySpec(&spec1).Generate()) require.NoError(t, clusteradapter.IdentitySpec(&spec2).Generate()) assert.NotEqual(t, spec1, spec2) length := len(spec1.NodeID) assert.GreaterOrEqual(t, length, 43) assert.LessOrEqual(t, length, 45) } func TestIdentityConvertMachineID(t *testing.T) { t.Parallel() spec := cluster.IdentitySpec{ NodeID: "sou7yy34ykX3n373Zw1DXKb8zD7UnyKT6HT3QDsGH6L", } machineID, err := clusteradapter.IdentitySpec(&spec).ConvertMachineID() require.NoError(t, err) assert.Equal(t, "be871ac0d0dd31fa4caca753b0f3f1b2", string(machineID)) } ================================================ FILE: internal/app/machined/pkg/adapters/hardware/hardware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package hardware implements adapters wrapping resources/hardware to provide additional functionality. package hardware ================================================ FILE: internal/app/machined/pkg/adapters/hardware/memorymodule.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/siderolabs/go-smbios/smbios" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // MemoryModule adapter provider conversion from smbios.SMBIOS. // //nolint:revive,golint func MemoryModule(m *hardware.MemoryModule) memoryModule { return memoryModule{ MemoryModule: m, } } type memoryModule struct { *hardware.MemoryModule } // Update current processor info. func (m memoryModule) Update(memory *smbios.MemoryDevice) { translateMemoryModuleInfo := func(in *smbios.MemoryDevice) hardware.MemoryModuleSpec { var memoryModuleSpec hardware.MemoryModuleSpec if in.Size != 0 && in.Size != 0xFFFF { var size uint32 if in.Size == 0x7FFF { size = uint32(in.ExtendedSize) } else { size = uint32(in.Size) } memoryModuleSpec.AssetTag = in.AssetTag memoryModuleSpec.BankLocator = in.BankLocator memoryModuleSpec.DeviceLocator = in.DeviceLocator memoryModuleSpec.Manufacturer = in.Manufacturer memoryModuleSpec.ProductName = in.PartNumber memoryModuleSpec.SerialNumber = in.SerialNumber memoryModuleSpec.Size = size memoryModuleSpec.Speed = uint32(in.Speed) } return memoryModuleSpec } *m.MemoryModule.TypedSpec() = translateMemoryModuleInfo(memory) } ================================================ FILE: internal/app/machined/pkg/adapters/hardware/processor.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/siderolabs/go-smbios/smbios" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // Processor adapter provider conversion from smbios.SMBIOS. // //nolint:revive,golint func Processor(p *hardware.Processor) processor { return processor{ Processor: p, } } type processor struct { *hardware.Processor } // Update current processor info. func (p processor) Update(processor *smbios.ProcessorInformation) { translateProcessorInfo := func(in *smbios.ProcessorInformation) hardware.ProcessorSpec { var processorSpec hardware.ProcessorSpec if in.Status.SocketPopulated() { processorSpec.Socket = in.SocketDesignation processorSpec.Manufacturer = in.ProcessorManufacturer processorSpec.ProductName = in.ProcessorVersion processorSpec.MaxSpeed = uint32(in.MaxSpeed) processorSpec.BootSpeed = uint32(in.CurrentSpeed) processorSpec.Status = uint32(in.Status) processorSpec.SerialNumber = in.SerialNumber processorSpec.AssetTag = in.AssetTag processorSpec.PartNumber = in.PartNumber processorSpec.CoreCount = uint32(in.CoreCount) processorSpec.CoreEnabled = uint32(in.CoreEnabled) processorSpec.ThreadCount = uint32(in.ThreadCount) } return processorSpec } *p.Processor.TypedSpec() = translateProcessorInfo(processor) } ================================================ FILE: internal/app/machined/pkg/adapters/hardware/system_information.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/siderolabs/go-smbios/smbios" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // SystemInformation adapter provider conversion from smbios.SMBIOS. // //nolint:revive,golint func SystemInformation(p *hardware.SystemInformation) systemInformation { return systemInformation{ SystemInformation: p, } } type systemInformation struct { *hardware.SystemInformation } // Update current systemInformation info. func (p systemInformation) Update(systemInformation *smbios.SystemInformation, uuidRewrite string) { if uuidRewrite == "" { uuidRewrite = systemInformation.UUID } *p.SystemInformation.TypedSpec() = hardware.SystemInformationSpec{ Manufacturer: systemInformation.Manufacturer, ProductName: systemInformation.ProductName, Version: systemInformation.Version, SerialNumber: systemInformation.SerialNumber, UUID: uuidRewrite, WakeUpType: systemInformation.WakeUpType.String(), SKUNumber: systemInformation.SKUNumber, } } ================================================ FILE: internal/app/machined/pkg/adapters/k8s/k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s implements adapters wrapping resources/k8s to provide additional functionality. package k8s ================================================ FILE: internal/app/machined/pkg/adapters/k8s/manifest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "bufio" "bytes" "encoding/json" "fmt" "io" "github.com/siderolabs/gen/xslices" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/yaml" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // Manifest adapter provides conversion from procfs. // //nolint:revive,golint func Manifest(r *k8s.Manifest) manifest { return manifest{ Manifest: r, } } type manifest struct { *k8s.Manifest } // SetObjects parses manifest from a list of runtime objects. func (a manifest) SetObjects(objects []runtime.Object) error { a.Manifest.TypedSpec().Items = make([]k8s.SingleManifest, 0, len(objects)) for _, obj := range objects { unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) if err != nil { return fmt.Errorf("error converting object to unstructured: %w", err) } u := unstructured.Unstructured{Object: unstructuredObj} u.SetGroupVersionKind(obj.GetObjectKind().GroupVersionKind()) delete(u.Object, "status") // remove status field if present a.Manifest.TypedSpec().Items = append(a.Manifest.TypedSpec().Items, k8s.SingleManifest{ Object: u.Object, }) } return nil } // SetYAML parses manifest from YAML. // //nolint:gocyclo func (a manifest) SetYAML(yamlBytes []byte) error { a.Manifest.TypedSpec().Items = nil reader := yaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(yamlBytes))) for { yamlManifest, err := reader.Read() if err != nil { if err == io.EOF { break } return err } yamlManifest = bytes.TrimSpace(yamlManifest) if len(yamlManifest) == 0 { continue } jsonManifest, err := yaml.ToJSON(yamlManifest) if err != nil { return fmt.Errorf("error converting manifest to JSON: %w", err) } if bytes.Equal(jsonManifest, []byte("null")) || bytes.Equal(jsonManifest, []byte("{}")) { // skip YAML docs which contain only comments continue } var obj unstructured.Unstructured if err = json.Unmarshal(jsonManifest, &obj); err != nil { return fmt.Errorf("error loading JSON manifest into unstructured: %w", err) } // if the manifest is a list, we will unwrap it if obj.IsList() { if err = obj.EachListItem(func(item runtime.Object) error { obj, ok := item.(*unstructured.Unstructured) if !ok { return fmt.Errorf("list item is not Unstructured: %T", item) } a.Manifest.TypedSpec().Items = append(a.Manifest.TypedSpec().Items, k8s.SingleManifest{Object: obj.Object}) return nil }); err != nil { return fmt.Errorf("error unwrapping a List: %w", err) } } else { a.Manifest.TypedSpec().Items = append(a.Manifest.TypedSpec().Items, k8s.SingleManifest{Object: obj.Object}) } } return nil } // Objects returns list of unstructured object. func (a manifest) Objects() []*unstructured.Unstructured { return xslices.Map(a.Manifest.TypedSpec().Items, func(item k8s.SingleManifest) *unstructured.Unstructured { return &unstructured.Unstructured{Object: item.Object} }) } ================================================ FILE: internal/app/machined/pkg/adapters/k8s/manifest_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( _ "embed" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) func TestManifestSetYAML(t *testing.T) { manifest := k8s.NewManifest(k8s.ControlPlaneNamespaceName, "test") adapter := k8sadapter.Manifest(manifest) require.NoError(t, adapter.SetYAML([]byte(strings.TrimSpace(` --- apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata --- `)))) assert.Len(t, adapter.Objects(), 1) assert.Equal(t, adapter.Objects()[0].GetKind(), "Policy") } func TestManifestSetYAMLEmptyComments(t *testing.T) { manifest := k8s.NewManifest(k8s.ControlPlaneNamespaceName, "test") adapter := k8sadapter.Manifest(manifest) require.NoError(t, adapter.SetYAML([]byte(strings.TrimSpace(` --- apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata --- # Left empty --- `)))) assert.Len(t, adapter.Objects(), 1) assert.Equal(t, adapter.Objects()[0].GetKind(), "Policy") } //go:embed testdata/list.yaml var listManifest []byte func TestManifestSetYAMLList(t *testing.T) { manifest := k8s.NewManifest(k8s.ControlPlaneNamespaceName, "test") adapter := k8sadapter.Manifest(manifest) require.NoError(t, adapter.SetYAML(listManifest)) assert.Len(t, adapter.Objects(), 2) assert.Equal(t, "ClusterRoleBinding", adapter.Objects()[0].GetKind()) assert.Equal(t, "system:cloud-node-controller", adapter.Objects()[0].GetName()) assert.Equal(t, "ClusterRoleBinding", adapter.Objects()[1].GetKind()) assert.Equal(t, "system:cloud-controller-manager", adapter.Objects()[1].GetName()) } ================================================ FILE: internal/app/machined/pkg/adapters/k8s/static_pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "encoding/json" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // StaticPod adapter provides conversion from *v1.Pod. // //nolint:revive,golint func StaticPod(r *k8s.StaticPod) staticPod { return staticPod{ StaticPod: r, } } type staticPod struct { *k8s.StaticPod } // Pod returns native Kubernetes resource. func (a staticPod) Pod() (*v1.Pod, error) { var spec v1.Pod jsonSerialized, err := json.Marshal(a.StaticPod.TypedSpec().Pod) if err != nil { return nil, err } err = json.Unmarshal(jsonSerialized, &spec) return &spec, err } // SetPod sets spec from native Kubernetes resource. func (a staticPod) SetPod(podSpec *v1.Pod) error { jsonSerialized, err := json.Marshal(podSpec) if err != nil { return err } a.StaticPod.TypedSpec().Pod = map[string]any{} return json.Unmarshal(jsonSerialized, &a.StaticPod.TypedSpec().Pod) } ================================================ FILE: internal/app/machined/pkg/adapters/k8s/static_pod_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "encoding/json" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // StaticPodStatus adapter provides conversion from *v1.PodStatus. // //nolint:revive,golint func StaticPodStatus(r *k8s.StaticPodStatus) staticPodStatus { return staticPodStatus{ StaticPodStatus: r, } } type staticPodStatus struct { *k8s.StaticPodStatus } // SetStatus sets status from native Kubernetes resource. func (a staticPodStatus) SetStatus(status *v1.PodStatus) error { jsonSerialized, err := json.Marshal(status) if err != nil { return err } a.StaticPodStatus.TypedSpec().PodStatus = map[string]any{} return json.Unmarshal(jsonSerialized, &a.StaticPodStatus.TypedSpec().PodStatus) } // Status gets status from native Kubernetes resource. func (a staticPodStatus) Status() (*v1.PodStatus, error) { var spec v1.PodStatus jsonSerialized, err := json.Marshal(a.StaticPodStatus.TypedSpec().PodStatus) if err != nil { return nil, err } err = json.Unmarshal(jsonSerialized, &spec) return &spec, err } ================================================ FILE: internal/app/machined/pkg/adapters/k8s/testdata/list.yaml ================================================ apiVersion: v1 items: - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:cloud-node-controller roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:cloud-node-controller subjects: - kind: ServiceAccount name: cloud-node-controller namespace: kube-system - apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system:cloud-controller-manager roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:cloud-controller-manager subjects: - kind: ServiceAccount name: cloud-controller-manager namespace: kube-system kind: List metadata: {} ================================================ FILE: internal/app/machined/pkg/adapters/kubespan/identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "errors" "fmt" "net" "net/netip" "github.com/mdlayher/netx/eui64" "github.com/siderolabs/gen/value" "go4.org/netipx" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // IdentitySpec adapter provides identity generation. // //nolint:revive,golint func IdentitySpec(r *kubespan.IdentitySpec) identity { return identity{ IdentitySpec: r, } } type identity struct { *kubespan.IdentitySpec } // GenerateKey generates new Wireguard key. func (a identity) GenerateKey() error { key, err := wgtypes.GeneratePrivateKey() if err != nil { return err } a.IdentitySpec.PrivateKey = key.String() a.IdentitySpec.PublicKey = key.PublicKey().String() return nil } // UpdateAddress re-calculates node address based on input data. func (a identity) UpdateAddress(clusterID string, mac net.HardwareAddr) error { a.IdentitySpec.Subnet = network.ULAPrefix(clusterID, network.ULAKubeSpan) var err error a.IdentitySpec.Address, err = wgEUI64(a.IdentitySpec.Subnet, mac) return err } func wgEUI64(prefix netip.Prefix, mac net.HardwareAddr) (out netip.Prefix, err error) { if value.IsZero(prefix) { return out, errors.New("cannot calculate IP from zero prefix") } stdIP, err := eui64.ParseMAC(netipx.PrefixIPNet(prefix).IP, mac) if err != nil { return out, fmt.Errorf("failed to parse MAC into EUI-64 address: %w", err) } ip, ok := netipx.FromStdIP(stdIP) if !ok { return out, fmt.Errorf("failed to parse intermediate standard IP %q: %w", stdIP.String(), err) } return netip.PrefixFrom(ip, ip.BitLen()), nil } ================================================ FILE: internal/app/machined/pkg/adapters/kubespan/identity_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "net" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) func TestIdentityGenerateKey(t *testing.T) { if fipsmode.Strict() { t.Skip("skipping test in strict FIPS mode") } var spec kubespan.IdentitySpec assert.NoError(t, kubespanadapter.IdentitySpec(&spec).GenerateKey()) } func TestIdentityUpdateAddress(t *testing.T) { var spec kubespan.IdentitySpec mac, err := net.ParseMAC("2e:1a:b6:53:81:69") require.NoError(t, err) assert.NoError(t, kubespanadapter.IdentitySpec(&spec).UpdateAddress("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", mac)) assert.Equal(t, "fd7f:175a:b97c:5602:2c1a:b6ff:fe53:8169/128", spec.Address.String()) assert.Equal(t, "fd7f:175a:b97c:5602::/64", spec.Subnet.String()) } ================================================ FILE: internal/app/machined/pkg/adapters/kubespan/kubespan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubespan implements adapters wrapping resources/kubespan to provide additional functionality. package kubespan ================================================ FILE: internal/app/machined/pkg/adapters/kubespan/peer_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "time" "github.com/siderolabs/gen/value" "go4.org/netipx" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/wireguard" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) // PeerStatusSpec adapter provides Wireguard integration and state management. // //nolint:revive,golint func PeerStatusSpec(r *kubespan.PeerStatusSpec) peerStatus { return peerStatus{ PeerStatusSpec: r, } } type peerStatus struct { *kubespan.PeerStatusSpec } // EndpointConnectionTimeout is time to wait for initial handshake when the endpoint is just set. const EndpointConnectionTimeout = 15 * time.Second // CalculateState updates connection state based on other fields values. // // Goal: endpoint is ultimately down if we haven't seen handshake for more than peerDownInterval, // but as the endpoints get updated we want faster feedback, so we start checking more aggressively // that the handshake happened within endpointConnectionTimeout since last endpoint change. // // Timeline: // // ----------------------------------------------------------------------> // ^ ^ ^ // | | | // T0 T0+endpointConnectionTimeout T0+peerDownInterval // // Where T0 = LastEndpointChange // // The question is where is LastHandshakeTimeout vs. those points above: // // - if we're past (T0+peerDownInterval), simply check that time since last handshake < peerDownInterval // - if we're between (T0+endpointConnectionTimeout) and (T0+peerDownInterval), and there's no handshake // after the endpoint change, assume that the endpoint is down // - if we're between (T0) and (T0+endpointConnectionTimeout), and there's no handshake since the endpoint change, // consider the state to be unknown func (a peerStatus) CalculateState() { sinceLastHandshake := time.Since(a.PeerStatusSpec.LastHandshakeTime) sinceEndpointChange := time.Since(a.PeerStatusSpec.LastEndpointChange) a.CalculateStateWithDurations(sinceLastHandshake, sinceEndpointChange) } // CalculateStateWithDurations calculates the state based on the time since events. func (a peerStatus) CalculateStateWithDurations(sinceLastHandshake, sinceEndpointChange time.Duration) { switch { case sinceEndpointChange > wireguard.PeerDownInterval: // past T0+peerDownInterval // if we got handshake in the last peerDownInterval, endpoint is up if sinceLastHandshake < wireguard.PeerDownInterval { a.PeerStatusSpec.State = kubespan.PeerStateUp } else { a.PeerStatusSpec.State = kubespan.PeerStateDown } case sinceEndpointChange < EndpointConnectionTimeout: // between (T0) and (T0+endpointConnectionTimeout) // endpoint got recently updated, consider no handshake as 'unknown' if a.PeerStatusSpec.LastHandshakeTime.After(a.PeerStatusSpec.LastEndpointChange) { a.PeerStatusSpec.State = kubespan.PeerStateUp } else { a.PeerStatusSpec.State = kubespan.PeerStateUnknown } default: // otherwise, we're between (T0+endpointConnectionTimeout) and (T0+peerDownInterval) // if we haven't had the handshake yet, consider the endpoint to be down if a.PeerStatusSpec.LastHandshakeTime.After(a.PeerStatusSpec.LastEndpointChange) { a.PeerStatusSpec.State = kubespan.PeerStateUp } else { a.PeerStatusSpec.State = kubespan.PeerStateDown } } if a.PeerStatusSpec.State == kubespan.PeerStateDown && value.IsZero(a.PeerStatusSpec.LastUsedEndpoint) { // no endpoint, so unknown a.PeerStatusSpec.State = kubespan.PeerStateUnknown } } // UpdateFromWireguard updates fields from wgtypes information. func (a peerStatus) UpdateFromWireguard(peer wgtypes.Peer) { if peer.Endpoint != nil { a.PeerStatusSpec.Endpoint, _ = netipx.FromStdAddr(peer.Endpoint.IP, peer.Endpoint.Port, "") } else { a.PeerStatusSpec.Endpoint = netip.AddrPort{} } a.PeerStatusSpec.LastHandshakeTime = peer.LastHandshakeTime a.PeerStatusSpec.TransmitBytes = peer.TransmitBytes a.PeerStatusSpec.ReceiveBytes = peer.ReceiveBytes } // UpdateEndpoint updates the endpoint information and last update timestamp. func (a peerStatus) UpdateEndpoint(endpoint netip.AddrPort) { a.PeerStatusSpec.Endpoint = endpoint a.PeerStatusSpec.LastUsedEndpoint = endpoint a.PeerStatusSpec.LastEndpointChange = time.Now() a.PeerStatusSpec.State = kubespan.PeerStateUnknown } // ShouldChangeEndpoint tells whether endpoint should be updated. func (a peerStatus) ShouldChangeEndpoint() bool { return a.PeerStatusSpec.State == kubespan.PeerStateDown || value.IsZero(a.PeerStatusSpec.LastUsedEndpoint) } // PickNewEndpoint picks new endpoint given the state and list of available endpoints. // // If returned newEndpoint is zero value, no new endpoint is available. func (a peerStatus) PickNewEndpoint(endpoints []netip.AddrPort) (newEndpoint netip.AddrPort) { if len(endpoints) == 0 { return newEndpoint } if value.IsZero(a.PeerStatusSpec.LastUsedEndpoint) { // first time setting the endpoint newEndpoint = endpoints[0] } else { // find the next endpoint after LastUsedEndpoint and use it idx := -1 for i := range endpoints { if endpoints[i] == a.PeerStatusSpec.LastUsedEndpoint { idx = i break } } // special case: if the peer has just a single endpoint, we can't rotate if !(len(endpoints) == 1 && idx == 0 && a.PeerStatusSpec.Endpoint == a.PeerStatusSpec.LastUsedEndpoint) { newEndpoint = endpoints[(idx+1)%len(endpoints)] } } return newEndpoint } ================================================ FILE: internal/app/machined/pkg/adapters/kubespan/peer_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "net/netip" "testing" "time" "github.com/siderolabs/gen/value" "github.com/stretchr/testify/assert" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/wireguard" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) func TestPeerStatus_PickNewEndpoint(t *testing.T) { // zero status peerStatus := kubespan.PeerStatusSpec{} // no endpoint => no way to pick new one assert.True(t, value.IsZero(kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(nil))) endpoints := []netip.AddrPort{ netip.MustParseAddrPort("10.3.4.5:10500"), netip.MustParseAddrPort("192.168.3.8:457"), } // initial choice should be the first endpoint newEndpoint := kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints) assert.Equal(t, endpoints[0], newEndpoint) kubespanadapter.PeerStatusSpec(&peerStatus).UpdateEndpoint(newEndpoint) // next choice should be 2nd endpoint newEndpoint = kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints) assert.Equal(t, endpoints[1], newEndpoint) kubespanadapter.PeerStatusSpec(&peerStatus).UpdateEndpoint(newEndpoint) // back to the first endpoint newEndpoint = kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints) assert.Equal(t, endpoints[0], newEndpoint) kubespanadapter.PeerStatusSpec(&peerStatus).UpdateEndpoint(newEndpoint) // can't rotate a single endpoint assert.True(t, value.IsZero(kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints[:1]))) // can rotate if the endpoint is different newEndpoint = kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints[1:]) assert.Equal(t, endpoints[1], newEndpoint) kubespanadapter.PeerStatusSpec(&peerStatus).UpdateEndpoint(newEndpoint) // if totally new list of endpoints is given, pick the first one endpoints = []netip.AddrPort{ netip.MustParseAddrPort("10.3.4.5:10501"), netip.MustParseAddrPort("192.168.3.8:458"), } newEndpoint = kubespanadapter.PeerStatusSpec(&peerStatus).PickNewEndpoint(endpoints) assert.Equal(t, endpoints[0], newEndpoint) kubespanadapter.PeerStatusSpec(&peerStatus).UpdateEndpoint(newEndpoint) } func TestPeerStatus_CalculateState(t *testing.T) { for _, tt := range []struct { name string sinceLastHandshake, sinceEndpointChange time.Duration lastUsedEndpointZero bool expectedState kubespan.PeerState }{ { name: "no endpoint set", sinceLastHandshake: time.Hour, sinceEndpointChange: time.Hour, lastUsedEndpointZero: true, expectedState: kubespan.PeerStateUnknown, }, { name: "peer is down", sinceLastHandshake: 2 * wireguard.PeerDownInterval, sinceEndpointChange: 2 * wireguard.PeerDownInterval, expectedState: kubespan.PeerStateDown, }, { name: "fresh peer, no handshake", sinceLastHandshake: 2 * wireguard.PeerDownInterval, sinceEndpointChange: kubespanadapter.EndpointConnectionTimeout / 2, expectedState: kubespan.PeerStateUnknown, }, { name: "fresh peer, with handshake", sinceLastHandshake: 0, sinceEndpointChange: kubespanadapter.EndpointConnectionTimeout / 2, expectedState: kubespan.PeerStateUp, }, { name: "peer after initial timeout, with handshake", sinceLastHandshake: 0, sinceEndpointChange: kubespanadapter.EndpointConnectionTimeout + 1, expectedState: kubespan.PeerStateUp, }, { name: "peer after initial timeout, no handshake", sinceLastHandshake: 2 * kubespanadapter.EndpointConnectionTimeout, sinceEndpointChange: kubespanadapter.EndpointConnectionTimeout + 1, expectedState: kubespan.PeerStateDown, }, { name: "established peer, up", sinceLastHandshake: wireguard.PeerDownInterval / 2, sinceEndpointChange: wireguard.PeerDownInterval + 1, expectedState: kubespan.PeerStateUp, }, } { t.Run(tt.name, func(t *testing.T) { peerStatus := kubespan.PeerStatusSpec{ LastHandshakeTime: time.Now().Add(-tt.sinceLastHandshake), LastEndpointChange: time.Now().Add(-tt.sinceEndpointChange), } if !tt.lastUsedEndpointZero { peerStatus.LastUsedEndpoint = netip.MustParseAddrPort("192.168.1.1:10000") } kubespanadapter.PeerStatusSpec(&peerStatus).CalculateStateWithDurations(tt.sinceLastHandshake, tt.sinceEndpointChange) assert.Equal(t, tt.expectedState, peerStatus.State) }) } } ================================================ FILE: internal/app/machined/pkg/adapters/network/bond_master_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "fmt" "net/netip" "github.com/mdlayher/netlink" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // BondMasterSpec adapter provides encoding/decoding to netlink structures. // //nolint:revive,golint func BondMasterSpec(r *network.BondMasterSpec) bondMaster { return bondMaster{ BondMasterSpec: r, } } type bondMaster struct { *network.BondMasterSpec } // FillDefaults fills zero values with proper default values. // //nolint:gocyclo func (a bondMaster) FillDefaults() { bond := a.BondMasterSpec bond.UseCarrier = true // Linux 6.18 locks this value to true if bond.ResendIGMP == 0 { bond.ResendIGMP = 1 } if bond.LPInterval == 0 { bond.LPInterval = 1 } if bond.PacketsPerSlave == 0 { bond.PacketsPerSlave = 1 } if bond.NumPeerNotif == 0 { bond.NumPeerNotif = 1 } if bond.Mode != nethelpers.BondModeALB && bond.Mode != nethelpers.BondModeTLB { bond.TLBDynamicLB = 1 } if bond.Mode == nethelpers.BondMode8023AD && bond.ADActorSysPrio == 0 { bond.ADActorSysPrio = 65535 } if bond.Mode != nethelpers.BondMode8023AD && bond.Mode != nethelpers.BondModeALB && bond.Mode != nethelpers.BondModeTLB { if bond.MissedMax == 0 { bond.MissedMax = 2 } } if bond.Mode != nethelpers.BondMode8023AD { bond.ADLACPActive = nethelpers.ADLACPActiveOn } } // Encode the BondMasterSpec into netlink attributes. // //nolint:gocyclo,cyclop func (a bondMaster) Encode() ([]byte, error) { bond := a.BondMasterSpec encoder := netlink.NewAttributeEncoder() encoder.Uint8(unix.IFLA_BOND_MODE, uint8(bond.Mode)) encoder.Uint8(unix.IFLA_BOND_XMIT_HASH_POLICY, uint8(bond.HashPolicy)) if bond.Mode == nethelpers.BondMode8023AD { encoder.Uint8(unix.IFLA_BOND_AD_LACP_RATE, uint8(bond.LACPRate)) encoder.Uint8(unix.IFLA_BOND_AD_LACP_ACTIVE, uint8(bond.ADLACPActive)) } if bond.Mode != nethelpers.BondMode8023AD && bond.Mode != nethelpers.BondModeALB && bond.Mode != nethelpers.BondModeTLB { encoder.Uint32(unix.IFLA_BOND_ARP_VALIDATE, uint32(bond.ARPValidate)) } encoder.Uint32(unix.IFLA_BOND_ARP_ALL_TARGETS, uint32(bond.ARPAllTargets)) if bond.Mode == nethelpers.BondModeActiveBackup || bond.Mode == nethelpers.BondModeALB || bond.Mode == nethelpers.BondModeTLB { if bond.PrimaryIndex != nil { encoder.Uint32(unix.IFLA_BOND_PRIMARY, *bond.PrimaryIndex) } } encoder.Uint8(unix.IFLA_BOND_PRIMARY_RESELECT, uint8(bond.PrimaryReselect)) encoder.Uint8(unix.IFLA_BOND_FAIL_OVER_MAC, uint8(bond.FailOverMac)) encoder.Uint8(unix.IFLA_BOND_AD_SELECT, uint8(bond.ADSelect)) encoder.Uint32(unix.IFLA_BOND_MIIMON, bond.MIIMon) if bond.MIIMon != 0 { encoder.Uint32(unix.IFLA_BOND_UPDELAY, bond.UpDelay) encoder.Uint32(unix.IFLA_BOND_DOWNDELAY, bond.DownDelay) } if bond.Mode != nethelpers.BondMode8023AD && bond.Mode != nethelpers.BondModeALB && bond.Mode != nethelpers.BondModeTLB { encoder.Uint32(unix.IFLA_BOND_ARP_INTERVAL, bond.ARPInterval) encoder.Nested(unix.IFLA_BOND_ARP_IP_TARGET, func(nae *netlink.AttributeEncoder) error { for i, addr := range bond.ARPIPTargets { if !addr.Is4() { return fmt.Errorf("%s is not IPV4 address", addr) } ip := addr.As4() nae.Bytes(uint16(i), ip[:]) } return nil }) encoder.Nested(unix.IFLA_BOND_NS_IP6_TARGET, func(nae *netlink.AttributeEncoder) error { for i, addr := range bond.NSIP6Targets { if !addr.Is6() { return fmt.Errorf("%s is not IPV6 address", addr) } ip := addr.As16() nae.Bytes(uint16(i), ip[:]) } return nil }) } encoder.Uint32(unix.IFLA_BOND_RESEND_IGMP, bond.ResendIGMP) encoder.Uint32(unix.IFLA_BOND_MIN_LINKS, bond.MinLinks) encoder.Uint32(unix.IFLA_BOND_LP_INTERVAL, bond.LPInterval) if bond.Mode == nethelpers.BondModeRoundrobin { encoder.Uint32(unix.IFLA_BOND_PACKETS_PER_SLAVE, bond.PacketsPerSlave) } encoder.Uint8(unix.IFLA_BOND_NUM_PEER_NOTIF, bond.NumPeerNotif) if bond.Mode == nethelpers.BondModeALB || bond.Mode == nethelpers.BondModeTLB { encoder.Uint8(unix.IFLA_BOND_TLB_DYNAMIC_LB, bond.TLBDynamicLB) } encoder.Uint8(unix.IFLA_BOND_ALL_SLAVES_ACTIVE, bond.AllSlavesActive) var useCarrier uint8 if bond.UseCarrier { useCarrier = 1 } encoder.Uint8(unix.IFLA_BOND_USE_CARRIER, useCarrier) if bond.Mode == nethelpers.BondMode8023AD { encoder.Uint16(unix.IFLA_BOND_AD_ACTOR_SYS_PRIO, bond.ADActorSysPrio) encoder.Uint16(unix.IFLA_BOND_AD_USER_PORT_KEY, bond.ADUserPortKey) } if bond.MIIMon != 0 { encoder.Uint32(unix.IFLA_BOND_PEER_NOTIF_DELAY, bond.PeerNotifyDelay) } if bond.MissedMax != 0 { encoder.Uint8(unix.IFLA_BOND_MISSED_MAX, bond.MissedMax) } return encoder.Encode() } // Decode the BondMasterSpec from netlink attributes. // //nolint:gocyclo,cyclop func (a bondMaster) Decode(data []byte) error { *a.BondMasterSpec = network.BondMasterSpec{} bond := a.BondMasterSpec decoder, err := netlink.NewAttributeDecoder(data) if err != nil { return err } for decoder.Next() { switch decoder.Type() { case unix.IFLA_BOND_MODE: bond.Mode = nethelpers.BondMode(decoder.Uint8()) case unix.IFLA_BOND_XMIT_HASH_POLICY: bond.HashPolicy = nethelpers.BondXmitHashPolicy(decoder.Uint8()) case unix.IFLA_BOND_AD_LACP_RATE: bond.LACPRate = nethelpers.LACPRate(decoder.Uint8()) case unix.IFLA_BOND_ARP_VALIDATE: bond.ARPValidate = nethelpers.ARPValidate(decoder.Uint32()) case unix.IFLA_BOND_ARP_ALL_TARGETS: bond.ARPAllTargets = nethelpers.ARPAllTargets(decoder.Uint32()) case unix.IFLA_BOND_PRIMARY: bond.PrimaryIndex = new(decoder.Uint32()) case unix.IFLA_BOND_PRIMARY_RESELECT: bond.PrimaryReselect = nethelpers.PrimaryReselect(decoder.Uint8()) case unix.IFLA_BOND_FAIL_OVER_MAC: bond.FailOverMac = nethelpers.FailOverMAC(decoder.Uint8()) case unix.IFLA_BOND_AD_SELECT: bond.ADSelect = nethelpers.ADSelect(decoder.Uint8()) case unix.IFLA_BOND_MIIMON: bond.MIIMon = decoder.Uint32() case unix.IFLA_BOND_UPDELAY: bond.UpDelay = decoder.Uint32() case unix.IFLA_BOND_DOWNDELAY: bond.DownDelay = decoder.Uint32() case unix.IFLA_BOND_ARP_INTERVAL: bond.ARPInterval = decoder.Uint32() case unix.IFLA_BOND_ARP_IP_TARGET: decoder.Nested(func(nad *netlink.AttributeDecoder) error { for nad.Next() { addr, ok := netip.AddrFromSlice(nad.Bytes()) if ok { bond.ARPIPTargets = append(bond.ARPIPTargets, addr) } else { return fmt.Errorf("invalid ARP IP target") } } return nil }) case unix.IFLA_BOND_NS_IP6_TARGET: decoder.Nested(func(nad *netlink.AttributeDecoder) error { for nad.Next() { addr, ok := netip.AddrFromSlice(nad.Bytes()) if ok { bond.NSIP6Targets = append(bond.NSIP6Targets, addr) } else { return fmt.Errorf("invalid NS IP6 target") } } return nil }) case unix.IFLA_BOND_RESEND_IGMP: bond.ResendIGMP = decoder.Uint32() case unix.IFLA_BOND_MIN_LINKS: bond.MinLinks = decoder.Uint32() case unix.IFLA_BOND_LP_INTERVAL: bond.LPInterval = decoder.Uint32() case unix.IFLA_BOND_PACKETS_PER_SLAVE: bond.PacketsPerSlave = decoder.Uint32() case unix.IFLA_BOND_NUM_PEER_NOTIF: bond.NumPeerNotif = decoder.Uint8() case unix.IFLA_BOND_TLB_DYNAMIC_LB: bond.TLBDynamicLB = decoder.Uint8() case unix.IFLA_BOND_ALL_SLAVES_ACTIVE: bond.AllSlavesActive = decoder.Uint8() case unix.IFLA_BOND_USE_CARRIER: bond.UseCarrier = decoder.Uint8() == 1 case unix.IFLA_BOND_AD_ACTOR_SYS_PRIO: bond.ADActorSysPrio = decoder.Uint16() case unix.IFLA_BOND_AD_USER_PORT_KEY: bond.ADUserPortKey = decoder.Uint16() case unix.IFLA_BOND_PEER_NOTIF_DELAY: bond.PeerNotifyDelay = decoder.Uint32() case unix.IFLA_BOND_AD_LACP_ACTIVE: bond.ADLACPActive = nethelpers.ADLACPActive(decoder.Uint8()) case unix.IFLA_BOND_MISSED_MAX: bond.MissedMax = decoder.Uint8() } } return decoder.Err() } ================================================ FILE: internal/app/machined/pkg/adapters/network/bond_master_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/require" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestBondMasterSpec(t *testing.T) { spec := network.BondMasterSpec{ Mode: nethelpers.BondModeActiveBackup, MIIMon: 100, UpDelay: 200, DownDelay: 300, } b, err := networkadapter.BondMasterSpec(&spec).Encode() require.NoError(t, err) var decodedSpec network.BondMasterSpec require.NoError(t, networkadapter.BondMasterSpec(&decodedSpec).Decode(b)) require.Equal(t, spec, decodedSpec) } func TestBondMasterSpecDecodeClearsTargets(t *testing.T) { initial := network.BondMasterSpec{ Mode: nethelpers.BondModeActiveBackup, ARPIPTargets: []netip.Addr{netip.MustParseAddr("198.51.100.254")}, NSIP6Targets: []netip.Addr{netip.MustParseAddr("fd00::1")}, } initialEncoded, err := networkadapter.BondMasterSpec(&initial).Encode() require.NoError(t, err) cleared := network.BondMasterSpec{ Mode: nethelpers.BondModeActiveBackup, } clearedEncoded, err := networkadapter.BondMasterSpec(&cleared).Encode() require.NoError(t, err) var decodedSpec network.BondMasterSpec require.NoError(t, networkadapter.BondMasterSpec(&decodedSpec).Decode(initialEncoded)) require.Equal(t, initial.ARPIPTargets, decodedSpec.ARPIPTargets) require.Equal(t, initial.NSIP6Targets, decodedSpec.NSIP6Targets) require.NoError(t, networkadapter.BondMasterSpec(&decodedSpec).Decode(clearedEncoded)) require.Empty(t, decodedSpec.ARPIPTargets) require.Empty(t, decodedSpec.NSIP6Targets) } ================================================ FILE: internal/app/machined/pkg/adapters/network/bridge_master_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/mdlayher/netlink" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // BridgeMasterSpec adapter provides encoding/decoding to netlink structures. // //nolint:revive func BridgeMasterSpec(r *network.BridgeMasterSpec) bridgeMaster { return bridgeMaster{ BridgeMasterSpec: r, } } // bridgeMaster contains the bridge master spec and provides methods for encoding/decoding it to netlink structures. type bridgeMaster struct { *network.BridgeMasterSpec } // Encode the BridgeMasterSpec into netlink attributes. func (a bridgeMaster) Encode() ([]byte, error) { bridge := a.BridgeMasterSpec encoder := netlink.NewAttributeEncoder() stpEnabled := 0 if bridge.STP.Enabled { stpEnabled = 1 } vlanFiltering := 0 if bridge.VLAN.FilteringEnabled { vlanFiltering = 1 } encoder.Uint32(unix.IFLA_BR_STP_STATE, uint32(stpEnabled)) encoder.Uint8(unix.IFLA_BR_VLAN_FILTERING, uint8(vlanFiltering)) return encoder.Encode() } // Decode the BridgeMasterSpec from netlink attributes. func (a bridgeMaster) Decode(data []byte) error { bridge := a.BridgeMasterSpec decoder, err := netlink.NewAttributeDecoder(data) if err != nil { return err } for decoder.Next() { switch decoder.Type() { case unix.IFLA_BR_STP_STATE: bridge.STP.Enabled = decoder.Uint32() == 1 case unix.IFLA_BR_VLAN_FILTERING: bridge.VLAN.FilteringEnabled = decoder.Uint8() == 1 } } return decoder.Err() } ================================================ FILE: internal/app/machined/pkg/adapters/network/bridge_master_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/require" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestBridgeMasterSpec(t *testing.T) { spec := network.BridgeMasterSpec{ STP: network.STPSpec{ Enabled: true, }, VLAN: network.BridgeVLANSpec{ FilteringEnabled: true, }, } b, err := networkadapter.BridgeMasterSpec(&spec).Encode() require.NoError(t, err) var decodedSpec network.BridgeMasterSpec require.NoError(t, networkadapter.BridgeMasterSpec(&decodedSpec).Decode(b)) require.Equal(t, spec, decodedSpec) } ================================================ FILE: internal/app/machined/pkg/adapters/network/ipset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "go4.org/netipx" ) // BuildIPSet builds an IPSet from the given include and exclude prefixes. func BuildIPSet(include, exclude []netip.Prefix) (*netipx.IPSet, error) { var builder netipx.IPSetBuilder for _, pfx := range include { builder.AddPrefix(pfx) } for _, pfx := range exclude { builder.RemovePrefix(pfx) } return builder.IPSet() } // SplitIPSet splits the given IPSet into IPv4 and IPv6 ranges. func SplitIPSet(set *netipx.IPSet) (ipv4, ipv6 []netipx.IPRange) { for _, rng := range set.Ranges() { if rng.From().Is4() { ipv4 = append(ipv4, rng) } else { ipv6 = append(ipv6, rng) } } return ipv4, ipv6 } ================================================ FILE: internal/app/machined/pkg/adapters/network/ipset_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go4.org/netipx" "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" ) func TestBuildIPSet(t *testing.T) { ipset, err := network.BuildIPSet( []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("2001:db8::/32"), }, []netip.Prefix{ netip.MustParsePrefix("10.4.0.0/16"), }) require.NoError(t, err) assert.Equal(t, []string{"10.0.0.0-10.3.255.255", "10.5.0.0-10.255.255.255", "2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"}, xslices.Map(ipset.Ranges(), netipx.IPRange.String), ) } func TestSplitIPSet(t *testing.T) { ipset, err := network.BuildIPSet( []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("2001:db8::/32"), }, []netip.Prefix{ netip.MustParsePrefix("10.4.0.0/16"), }) require.NoError(t, err) v4, v6 := network.SplitIPSet(ipset) assert.Equal(t, []string{"10.0.0.0-10.3.255.255", "10.5.0.0-10.255.255.255"}, xslices.Map(v4, netipx.IPRange.String), ) assert.Equal(t, []string{"2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff"}, xslices.Map(v6, netipx.IPRange.String), ) } ================================================ FILE: internal/app/machined/pkg/adapters/network/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network implements adapters wrapping resources/network to provide additional functionality. package network // MSS calculation constants. const ( IPv4HeaderLen = 20 // IPv4 fixed header length IPv6HeaderLen = 40 // IPv6 fixed header length TCPHeaderLen = 20 // fixed TCP header length, without options TCPOptionsLen = 12 // assuming typical options like SACK, timestamps, etc. used by default in Linux ) ================================================ FILE: internal/app/machined/pkg/adapters/network/nftables_rule.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "errors" "fmt" "io/fs" "net/netip" "os" "slices" "github.com/google/nftables" "github.com/google/nftables/binaryutil" "github.com/google/nftables/expr" "github.com/siderolabs/gen/xslices" "go4.org/netipx" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NfTablesRule adapter provides encoding to nftables instructions. // //nolint:revive,golint func NfTablesRule(r *network.NfTablesRule) nftablesRule { return nftablesRule{ NfTablesRule: r, } } type nftablesRule struct { *network.NfTablesRule } // SetKind is the type of the nftables Set. type SetKind uint8 // SetKind constants. const ( SetKindIPv4 SetKind = iota SetKindIPv6 SetKindPort SetKindIfName SetKindConntrackState SetKindICMPType ) // NfTablesSet is a compiled representation of the set. type NfTablesSet struct { Kind SetKind Addresses []netipx.IPRange Ports [][2]uint16 Strings [][]byte ConntrackStates []nethelpers.ConntrackState ICMPTypes []nethelpers.ICMPType } // IsInterval returns true if the set is an interval set. func (set NfTablesSet) IsInterval() bool { switch set.Kind { case SetKindIPv4, SetKindIPv6, SetKindPort: return true case SetKindIfName, SetKindConntrackState, SetKindICMPType: return false default: panic(fmt.Sprintf("unknown set kind: %d", set.Kind)) } } // KeyType returns the type of the set. func (set NfTablesSet) KeyType() nftables.SetDatatype { switch set.Kind { case SetKindIPv4: return nftables.TypeIPAddr case SetKindIPv6: return nftables.TypeIP6Addr case SetKindPort: return nftables.TypeInetService case SetKindIfName: return nftables.TypeIFName case SetKindConntrackState: return nftables.TypeCTState case SetKindICMPType: return nftables.TypeICMPType default: panic(fmt.Sprintf("unknown set kind: %d", set.Kind)) } } // SetElements returns the set elements. // //nolint:gocyclo func (set NfTablesSet) SetElements() []nftables.SetElement { switch set.Kind { case SetKindIPv4, SetKindIPv6: elements := make([]nftables.SetElement, 0, len(set.Addresses)*2) for _, r := range set.Addresses { fromBin, _ := r.From().MarshalBinary() //nolint:errcheck // doesn't fail elements = append(elements, nftables.SetElement{ Key: fromBin, IntervalEnd: false, }, ) // r.To().Next() overflows for the max address (255.255.255.255 or ffff:...:ffff), // returning the zero Addr whose MarshalBinary() yields nil. // In that case, omit the interval-end element — nftables treats the absence // of an end boundary as extending to the maximum value. next := r.To().Next() if next.IsValid() { toBin, _ := next.MarshalBinary() //nolint:errcheck // doesn't fail elements = append(elements, nftables.SetElement{ Key: toBin, IntervalEnd: true, }) } } return elements case SetKindPort: ports := mergeAdjacentPorts(set.Ports) elements := make([]nftables.SetElement, 0, len(ports)) for _, p := range ports { from := binaryutil.BigEndian.PutUint16(p[0]) elements = append(elements, nftables.SetElement{ Key: from, IntervalEnd: false, }, ) // handle overflow of p[1]+1 for the max port 65535, as binaryutil.BigEndian.PutUint16(65536) returns 0 if p[1] != 65535 { to := binaryutil.BigEndian.PutUint16(p[1] + 1) elements = append(elements, nftables.SetElement{ Key: to, IntervalEnd: true, }, ) } } return elements case SetKindIfName: elements := make([]nftables.SetElement, 0, len(set.Strings)) for _, s := range set.Strings { elements = append(elements, nftables.SetElement{ Key: s, }, ) } return elements case SetKindConntrackState: elements := make([]nftables.SetElement, 0, len(set.ConntrackStates)) for _, s := range set.ConntrackStates { elements = append(elements, nftables.SetElement{ Key: binaryutil.NativeEndian.PutUint32(uint32(s)), }, ) } return elements case SetKindICMPType: return xslices.Map(set.ICMPTypes, func(t nethelpers.ICMPType) nftables.SetElement { return nftables.SetElement{ Key: []byte{byte(t)}, } }) default: panic(fmt.Sprintf("unknown set kind: %d", set.Kind)) } } func mergeAdjacentPorts(in [][2]uint16) [][2]uint16 { ports := slices.Clone(in) slices.SortFunc(ports, func(a, b [2]uint16) int { // sort by the lower bound of the range, assume no overlap return cmp.Compare(a[0], b[0]) }) for i := 0; i < len(ports)-1; { if ports[i][1]+1 >= ports[i+1][0] { ports[i][1] = ports[i+1][1] ports = append(ports[:i+1], ports[i+2:]...) } else { i++ } } return ports } // NfTablesCompiled is a compiled representation of the rule. type NfTablesCompiled struct { Rules [][]expr.Any Sets []NfTablesSet } var ( matchV4 = []expr.Any{ // Store protocol type to register 1 &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, // Match IP Family &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv4)}, }, } matchV6 = []expr.Any{ // Store protocol type to register 1 &expr.Meta{ Key: expr.MetaKeyNFPROTO, Register: 1, }, // Match IP Family &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv6)}, }, } firstIPv4 = netip.MustParseAddr("0.0.0.0") lastIPv4 = netip.MustParseAddr("255.255.255.255") firstIPv6 = netip.MustParseAddr("::") lastIPv6 = netip.MustParseAddr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") ) // Compile translates the rule into the set of nftables instructions. // //nolint:gocyclo,cyclop func (a nftablesRule) Compile() (*NfTablesCompiled, error) { var ( // common for ipv4 & ipv6 expression, pre & post rulePre []expr.Any rulePost []expr.Any // speficic for ipv4 & ipv6 expression rule4, rule6 []expr.Any result NfTablesCompiled ) matchIfNames := func(operator nethelpers.MatchOperator, ifnames []string) { if len(ifnames) == 1 { rulePre = append(rulePre, // [ cmp eq/neq reg 1 ] &expr.Cmp{ Op: expr.CmpOp(operator), Register: 1, Data: ifname(ifnames[0]), }, ) } else { result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindIfName, Strings: xslices.Map(ifnames, ifname), }) rulePre = append(rulePre, // Match from target set &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller Invert: operator == nethelpers.OperatorNotEqual, }, ) } } if a.NfTablesRule.MatchIIfName != nil { match := a.NfTablesRule.MatchIIfName rulePre = append(rulePre, // [ meta load iifname => reg 1 ] &expr.Meta{ Key: expr.MetaKeyIIFNAME, Register: 1, }, ) matchIfNames(match.Operator, match.InterfaceNames) } if a.NfTablesRule.MatchOIfName != nil { match := a.NfTablesRule.MatchOIfName rulePre = append(rulePre, // [ meta load oifname => reg 1 ] &expr.Meta{ Key: expr.MetaKeyOIFNAME, Register: 1, }, ) matchIfNames(match.Operator, match.InterfaceNames) } if a.NfTablesRule.MatchMark != nil { match := a.NfTablesRule.MatchMark rulePre = append(rulePre, // [ meta load mark => reg 1 ] &expr.Meta{ Key: expr.MetaKeyMARK, Register: 1, }, // Mask the mark with the configured mask: // R1 = R1 & mask ^ xor &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Xor: binaryutil.NativeEndian.PutUint32(match.Xor), Mask: binaryutil.NativeEndian.PutUint32(match.Mask), }, // Compare the masked firewall mark with expected value &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: binaryutil.NativeEndian.PutUint32(match.Value), }, ) } if a.NfTablesRule.MatchConntrackState != nil { match := a.NfTablesRule.MatchConntrackState if len(match.States) == 1 { rulePre = append(rulePre, // [ ct load state => reg 1 ] &expr.Ct{ Key: expr.CtKeySTATE, Register: 1, }, // [ bitwise reg 1 = ( reg 1 & state ) ^ 0x00000000 ] &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Mask: binaryutil.NativeEndian.PutUint32(uint32(match.States[0])), Xor: []byte{0x0, 0x0, 0x0, 0x0}, }, // [ cmp neq reg 1 0x00000000 ] &expr.Cmp{ Op: expr.CmpOpNeq, Register: 1, Data: []byte{0x0, 0x0, 0x0, 0x0}, }, ) } else { result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindConntrackState, ConntrackStates: match.States, }) rulePre = append(rulePre, // [ ct load state => reg 1 ] &expr.Ct{ Key: expr.CtKeySTATE, Register: 1, }, // [ lookup reg 1 set ] &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller }, ) } } addressMatchExpression := func(match *network.NfTablesAddressMatch, label string, offV4, offV6 uint32) error { ipSet, err := BuildIPSet(match.IncludeSubnets, match.ExcludeSubnets) if err != nil { return fmt.Errorf("failed to build IPSet for %s address match: %w", label, err) } v4Set, v6Set := SplitIPSet(ipSet) if v4Set == nil && v6Set == nil && !match.Invert { // this rule doesn't match anything return os.ErrNotExist } v4SetCoversAll := len(v4Set) == 1 && v4Set[0].From() == firstIPv4 && v4Set[0].To() == lastIPv4 v6SetCoversAll := len(v6Set) == 1 && v6Set[0].From() == firstIPv6 && v6Set[0].To() == lastIPv6 if v4SetCoversAll && v6SetCoversAll && match.Invert { // this rule doesn't match anything return os.ErrNotExist } switch { //nolint:dupl case v4SetCoversAll && !match.Invert, match.Invert && v4Set == nil: // match any v4 IP if rule4 == nil { rule4 = []expr.Any{} } case !v4SetCoversAll && match.Invert, !match.Invert && v4Set != nil: // match specific v4 IPs result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindIPv4, Addresses: v4Set, }, ) rule4 = append(rule4, // Store the destination IP address to register 1 &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: offV4, Len: 4, }, // Match from target set &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller Invert: match.Invert, }, ) default: // otherwise skip generating v4 rule, as it doesn't match anything } switch { //nolint:dupl case v6SetCoversAll && !match.Invert, match.Invert && v6Set == nil: // match any v6 IP if rule6 == nil { rule6 = []expr.Any{} } case !v6SetCoversAll && match.Invert, !match.Invert && v6Set != nil: // match specific v6 IPs result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindIPv6, Addresses: v6Set, }) rule6 = append(rule6, // Store the destination IP address to register 1 &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: offV6, Len: 16, }, // Match from target set &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller Invert: match.Invert, }, ) default: // otherwise skip generating v6 rule, as it doesn't match anything } return nil } if a.NfTablesRule.MatchSourceAddress != nil { match := a.NfTablesRule.MatchSourceAddress if err := addressMatchExpression(match, "source", 12, 8); err != nil { if errors.Is(err, fs.ErrNotExist) { return &NfTablesCompiled{}, nil } return nil, err } } if a.NfTablesRule.MatchDestinationAddress != nil { match := a.NfTablesRule.MatchDestinationAddress if err := addressMatchExpression(match, "destination", 16, 24); err != nil { if errors.Is(err, fs.ErrNotExist) { return &NfTablesCompiled{}, nil } return nil, err } } if a.NfTablesRule.MatchLayer4 != nil { match := a.NfTablesRule.MatchLayer4 rulePre = append(rulePre, // [ meta load l4proto => reg 1 ] &expr.Meta{ Key: expr.MetaKeyL4PROTO, Register: 1, }, // [ cmp eq reg 1 ] &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(match.Protocol)}, }, ) portMatch := func(off uint32, ports []network.PortRange) { result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindPort, Ports: xslices.Map(ports, func(r network.PortRange) [2]uint16 { return [2]uint16{r.Lo, r.Hi} }), }, ) rulePost = append(rulePost, // [ payload load 2b @ transport header + => reg 1 ] &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: off, Len: 2, }, // [ lookup reg 1 set ] &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller }, ) } if match.MatchSourcePort != nil { portMatch(0, match.MatchSourcePort.Ranges) } if match.MatchDestinationPort != nil { portMatch(2, match.MatchDestinationPort.Ranges) } if match.MatchICMPType != nil { result.Sets = append(result.Sets, NfTablesSet{ Kind: SetKindICMPType, ICMPTypes: match.MatchICMPType.Types, }, ) rulePost = append(rulePost, // [ payload load 1b @ transport header + 0 => reg 1 ] &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 1, }, // [ lookup reg 1 set ] &expr.Lookup{ SourceRegister: 1, SetID: uint32(len(result.Sets) - 1), // reference will be fixed up by the controller }, ) } } if a.NfTablesRule.MatchLimit != nil { match := a.NfTablesRule.MatchLimit rulePost = append(rulePost, // [ limit rate ] &expr.Limit{ Type: expr.LimitTypePkts, Rate: match.PacketRatePerSecond, Burst: uint32(match.PacketRatePerSecond), Unit: expr.LimitTimeSecond, }, ) } clampMSS := func(family nftables.TableFamily, mtu uint16) []expr.Any { var mss uint16 switch family { //nolint:exhaustive case nftables.TableFamilyIPv4: mss = mtu - (IPv4HeaderLen + TCPHeaderLen + TCPOptionsLen) // TCP + IPv4 overhead case nftables.TableFamilyIPv6: mss = mtu - (IPv6HeaderLen + TCPHeaderLen + TCPOptionsLen) // TCP + IPv6 overhead default: panic("unexpected IP family") } return []expr.Any{ // Load the L4 protocol into register 1 &expr.Meta{ Key: expr.MetaKeyL4PROTO, Register: 1, }, // Match TCP Family &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{unix.IPPROTO_TCP}, }, // [ payload load 1b @ transport header + 13 => reg 1 ] &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 13, Len: 1, }, // [ bitwise reg 1 = ( reg 1 & 0x00000006 ) ^ 0x00000000 ] &expr.Bitwise{ DestRegister: 1, SourceRegister: 1, Len: 1, Mask: []byte{0x02 | 0x04}, Xor: []byte{0x00}, }, // [ cmp eq reg 1 0x00000002 ] &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}, }, // [ exthdr load tcpopt 2b @ 2 + 2 => reg 1 ] &expr.Exthdr{ DestRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, // [ cmp gte reg 1 MTU ] &expr.Cmp{ Op: expr.CmpOpGt, Register: 1, Data: binaryutil.BigEndian.PutUint16(mss), }, // [ immediate reg 1 MTU ] &expr.Immediate{ Register: 1, Data: binaryutil.BigEndian.PutUint16(mss), }, // [ exthdr write tcpopt reg 1 => 2b @ 2 + 2 ] &expr.Exthdr{ SourceRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, } } if a.NfTablesRule.ClampMSS != nil { if rule4 != nil { rule4 = append(rule4, clampMSS(nftables.TableFamilyIPv4, a.NfTablesRule.ClampMSS.MTU)...) } if rule6 != nil { rule6 = append(rule6, clampMSS(nftables.TableFamilyIPv6, a.NfTablesRule.ClampMSS.MTU)...) } } if a.NfTablesRule.SetMark != nil { set := a.NfTablesRule.SetMark rulePost = append(rulePost, // Load the current packet mark into register 1 &expr.Meta{ Key: expr.MetaKeyMARK, Register: 1, }, // Calculate the new mark value in register 1 &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Xor: binaryutil.NativeEndian.PutUint32(set.Xor), Mask: binaryutil.NativeEndian.PutUint32(set.Mask), }, // Set firewall mark to the value computed in register 1 &expr.Meta{ Key: expr.MetaKeyMARK, SourceRegister: true, Register: 1, }, ) } if a.NfTablesRule.AnonCounter { rulePost = append(rulePost, // [ counter ] &expr.Counter{}, ) } if a.NfTablesRule.Verdict != nil { rulePost = append(rulePost, // [ verdict accept|drop ] &expr.Verdict{ Kind: expr.VerdictKind(*a.NfTablesRule.Verdict), }, ) } // Build v4/v6 rules as requested. // // If there's no IPv4/IPv6 part, generate a single rule. // If there's a specific IPv4/IPv6 part, generate a rule per IP version. switch { case rule4 == nil && rule6 == nil && rulePre == nil && rulePost == nil: // nothing case rule4 == nil && rule6 == nil: result.Rules = [][]expr.Any{append(rulePre, rulePost...)} case rule4 != nil && rule6 == nil: result.Rules = [][]expr.Any{ slices.Concat(rulePre, matchV4, rule4, rulePost), } case rule4 == nil && rule6 != nil: result.Rules = [][]expr.Any{ slices.Concat(rulePre, matchV6, rule6, rulePost), } case rule4 != nil && rule6 != nil: result.Rules = [][]expr.Any{ slices.Concat(rulePre, matchV4, rule4, rulePost), slices.Concat(rulePre, matchV6, rule6, rulePost), } } return &result, nil } func ifname(name string) []byte { b := make([]byte, 16) copy(b, []byte(name)) return b } ================================================ FILE: internal/app/machined/pkg/adapters/network/nftables_rule_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/google/nftables" "github.com/google/nftables/expr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go4.org/netipx" "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" networkres "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestNfTablesRuleCompile(t *testing.T) { //nolint:tparallel t.Parallel() for _, test := range []struct { name string spec networkres.NfTablesRule expectedRules [][]expr.Any expectedSets []network.NfTablesSet }{ { name: "empty", }, { name: "match oifname", spec: networkres.NfTablesRule{ MatchOIfName: &networkres.NfTablesIfNameMatch{ InterfaceNames: []string{"eth0"}, Operator: nethelpers.OperatorEqual, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte("eth0\000\000\000\000\000\000\000\000\000\000\000\000"), }, }, }, }, { name: "match iifname", spec: networkres.NfTablesRule{ MatchIIfName: &networkres.NfTablesIfNameMatch{ InterfaceNames: []string{"lo"}, Operator: nethelpers.OperatorNotEqual, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1}, &expr.Cmp{ Op: expr.CmpOpNeq, Register: 1, Data: []byte("lo\000\000\000\000\000\000\000\000\000\000\000\000\000\000"), }, }, }, }, { name: "match multiple iifname", spec: networkres.NfTablesRule{ MatchIIfName: &networkres.NfTablesIfNameMatch{ InterfaceNames: []string{"siderolink", "kubespan"}, Operator: nethelpers.OperatorEqual, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1}, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindIfName, Strings: [][]byte{ []byte("siderolink\000\000\000\000\000\000"), []byte("kubespan\000\000\000\000\000\000\000\000"), }, }, }, }, { name: "verdict accept", spec: networkres.NfTablesRule{ MatchOIfName: &networkres.NfTablesIfNameMatch{ InterfaceNames: []string{"eth0"}, Operator: nethelpers.OperatorNotEqual, }, Verdict: new(nethelpers.VerdictAccept), }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1}, &expr.Cmp{ Op: expr.CmpOpNeq, Register: 1, Data: []byte("eth0\000\000\000\000\000\000\000\000\000\000\000\000"), }, &expr.Verdict{Kind: expr.VerdictAccept}, }, }, }, { name: "match and set mark", spec: networkres.NfTablesRule{ MatchMark: &networkres.NfTablesMark{ Mask: 0xff00ffff, Xor: 0x00ff0000, Value: 0x00ee0000, }, SetMark: &networkres.NfTablesMark{ Mask: 0x0000ffff, Xor: 0xffff0000, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyMARK, Register: 1}, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Xor: []byte{0x00, 0x00, 0xff, 0x00}, Mask: []byte{0xff, 0xff, 0x00, 0xff}, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x00, 0x00, 0xee, 0x00}, }, &expr.Meta{Key: expr.MetaKeyMARK, Register: 1}, &expr.Bitwise{ SourceRegister: 1, DestRegister: 1, Len: 4, Xor: []byte{0x00, 0x00, 0xff, 0xff}, Mask: []byte{0xff, 0xff, 0x00, 0x00}, }, &expr.Meta{Key: expr.MetaKeyMARK, SourceRegister: true, Register: 1}, }, }, }, { name: "match on empty source address", spec: networkres.NfTablesRule{ MatchSourceAddress: &networkres.NfTablesAddressMatch{}, Verdict: new(nethelpers.VerdictDrop), }, }, { name: "match on v4 source address", spec: networkres.NfTablesRule{ MatchSourceAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/16"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.4.0/24"), }, }, Verdict: new(nethelpers.VerdictDrop), }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv4)}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 12, Len: 4, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindIPv4, Addresses: []netipx.IPRange{netipx.MustParseIPRange("192.168.0.0-192.168.3.255"), netipx.MustParseIPRange("192.168.5.0-192.168.255.255")}, }, }, }, { name: "match on v6 source and destination addresses", spec: networkres.NfTablesRule{ MatchSourceAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("2001::/16"), }, }, MatchDestinationAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("20fe::/16"), }, Invert: true, }, Verdict: new(nethelpers.VerdictDrop), }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv4)}, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv6)}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 8, Len: 16, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 24, Len: 16, }, &expr.Lookup{ SourceRegister: 1, SetID: 1, Invert: true, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindIPv6, Addresses: []netipx.IPRange{netipx.MustParseIPRange("2001::-2001:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, }, { Kind: network.SetKindIPv6, Addresses: []netipx.IPRange{netipx.MustParseIPRange("20fe::-20fe:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, }, }, }, { name: "match on v6 destination addresses", spec: networkres.NfTablesRule{ MatchDestinationAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("20fe::/16"), }, }, Verdict: new(nethelpers.VerdictDrop), }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv6)}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 24, Len: 16, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindIPv6, Addresses: []netipx.IPRange{netipx.MustParseIPRange("20fe::-20fe:ffff:ffff:ffff:ffff:ffff:ffff:ffff")}, }, }, }, { name: "match on any v6 address", spec: networkres.NfTablesRule{ MatchSourceAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.37.45/32"), }, Invert: true, }, Verdict: new(nethelpers.VerdictDrop), }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv4)}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseNetworkHeader, Offset: 12, Len: 4, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, Invert: true, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv6)}, }, &expr.Verdict{ Kind: expr.VerdictDrop, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindIPv4, Addresses: []netipx.IPRange{netipx.MustParseIPRange("192.168.37.45-192.168.37.45")}, }, }, }, { //nolint:dupl name: "clamp MSS v4", spec: networkres.NfTablesRule{ MatchDestinationAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, }, ClampMSS: &networkres.NfTablesClampMSS{ MTU: 1280, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv4)}, }, &expr.Meta{ Key: expr.MetaKeyL4PROTO, Register: 1, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{6}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 13, Len: 1, }, &expr.Bitwise{ DestRegister: 1, SourceRegister: 1, Len: 1, Mask: []byte{0x02 | 0x04}, Xor: []byte{0x00}, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}, }, &expr.Exthdr{ DestRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, &expr.Cmp{ Op: expr.CmpOpGt, Register: 1, Data: []byte{0x04, 0xcc}, }, &expr.Immediate{ Register: 1, Data: []byte{0x04, 0xcc}, }, &expr.Exthdr{ SourceRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, }, }, }, { //nolint:dupl name: "clamp MSS v6", spec: networkres.NfTablesRule{ MatchDestinationAddress: &networkres.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("::/0"), }, }, ClampMSS: &networkres.NfTablesClampMSS{ MTU: 1280, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyNFPROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{byte(nftables.TableFamilyIPv6)}, }, &expr.Meta{ Key: expr.MetaKeyL4PROTO, Register: 1, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{6}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 13, Len: 1, }, &expr.Bitwise{ DestRegister: 1, SourceRegister: 1, Len: 1, Mask: []byte{0x02 | 0x04}, Xor: []byte{0x00}, }, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x02}, }, &expr.Exthdr{ DestRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, &expr.Cmp{ Op: expr.CmpOpGt, Register: 1, Data: []byte{0x04, 0xb8}, }, &expr.Immediate{ Register: 1, Data: []byte{0x04, 0xb8}, }, &expr.Exthdr{ SourceRegister: 1, Type: 2, Offset: 2, Len: 2, Op: expr.ExthdrOpTcpopt, }, }, }, }, { name: "match L4 proto", spec: networkres.NfTablesRule{ MatchLayer4: &networkres.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolUDP, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x11}, }, }, }, }, { name: "match L4 proto and src port", spec: networkres.NfTablesRule{ MatchLayer4: &networkres.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchSourcePort: &networkres.NfTablesPortMatch{ Ranges: []networkres.PortRange{ { Lo: 2000, Hi: 2000, }, { Lo: 1000, Hi: 1025, }, }, }, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x6}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 2, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindPort, Ports: [][2]uint16{ {2000, 2000}, {1000, 1025}, }, }, }, }, { name: "match L4 proto and dst port", spec: networkres.NfTablesRule{ MatchLayer4: &networkres.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &networkres.NfTablesPortMatch{ Ranges: []networkres.PortRange{ { Lo: 2000, Hi: 2000, }, }, }, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x6}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 2, Len: 2, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindPort, Ports: [][2]uint16{ {2000, 2000}, }, }, }, }, { name: "limit", spec: networkres.NfTablesRule{ MatchLimit: &networkres.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, }, expectedRules: [][]expr.Any{ { &expr.Limit{ Type: expr.LimitTypePkts, Rate: 5, Burst: 5, Unit: expr.LimitTimeSecond, }, }, }, }, { name: "counter", spec: networkres.NfTablesRule{ AnonCounter: true, }, expectedRules: [][]expr.Any{ { &expr.Counter{}, }, }, }, { name: "ct state", spec: networkres.NfTablesRule{ MatchConntrackState: &networkres.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateInvalid, }, }, }, expectedRules: [][]expr.Any{ { &expr.Ct{ Key: expr.CtKeySTATE, Register: 1, }, &expr.Bitwise{ DestRegister: 1, SourceRegister: 1, Len: 4, Mask: []byte{0x01, 0x00, 0x00, 0x00}, Xor: []byte{0x00, 0x00, 0x00, 0x00}, }, &expr.Cmp{ Op: expr.CmpOpNeq, Register: 1, Data: []byte{0x00, 0x00, 0x00, 0x00}, }, }, }, }, { name: "ct states", spec: networkres.NfTablesRule{ MatchConntrackState: &networkres.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateRelated, nethelpers.ConntrackStateEstablished, }, }, }, expectedRules: [][]expr.Any{ { &expr.Ct{ Key: expr.CtKeySTATE, Register: 1, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindConntrackState, ConntrackStates: []nethelpers.ConntrackState{ nethelpers.ConntrackStateRelated, nethelpers.ConntrackStateEstablished, }, }, }, }, { name: "icmp packet type", spec: networkres.NfTablesRule{ MatchLayer4: &networkres.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, MatchICMPType: &networkres.NfTablesICMPTypeMatch{ Types: []nethelpers.ICMPType{ nethelpers.ICMPTypeTimestampRequest, nethelpers.ICMPTypeTimestampReply, }, }, }, }, expectedRules: [][]expr.Any{ { &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1}, &expr.Cmp{ Op: expr.CmpOpEq, Register: 1, Data: []byte{0x1}, }, &expr.Payload{ DestRegister: 1, Base: expr.PayloadBaseTransportHeader, Offset: 0, Len: 1, }, &expr.Lookup{ SourceRegister: 1, SetID: 0, }, }, }, expectedSets: []network.NfTablesSet{ { Kind: network.SetKindICMPType, ICMPTypes: []nethelpers.ICMPType{ nethelpers.ICMPTypeTimestampRequest, nethelpers.ICMPTypeTimestampReply, }, }, }, }, } { t.Run(test.name, func(t *testing.T) { result, err := network.NfTablesRule(&test.spec).Compile() require.NoError(t, err) assert.Equal(t, test.expectedRules, result.Rules) assert.Equal(t, test.expectedSets, result.Sets) }) } } func TestNftablesSet(t *testing.T) { //nolint:tparallel t.Parallel() for _, test := range []struct { name string set network.NfTablesSet expectedKeyType nftables.SetDatatype expectedInterval bool expectedData []nftables.SetElement }{ { name: "ports", set: network.NfTablesSet{ Kind: network.SetKindPort, Ports: [][2]uint16{ {443, 443}, {80, 81}, {5000, 5000}, {5001, 5001}, }, }, expectedKeyType: nftables.TypeInetService, expectedInterval: true, expectedData: []nftables.SetElement{ // network byte order {Key: []uint8{0x0, 80}, IntervalEnd: false}, // 80 - 81 {Key: []uint8{0x0, 82}, IntervalEnd: true}, {Key: []uint8{0x1, 0xbb}, IntervalEnd: false}, // 443-443 {Key: []uint8{0x1, 0xbc}, IntervalEnd: true}, {Key: []uint8{0x13, 0x88}, IntervalEnd: false}, // 5000-5001 {Key: []uint8{0x13, 0x8a}, IntervalEnd: true}, }, }, { name: "ports with overflow", set: network.NfTablesSet{ Kind: network.SetKindPort, Ports: [][2]uint16{ {65530, 65535}, }, }, expectedKeyType: nftables.TypeInetService, expectedInterval: true, expectedData: []nftables.SetElement{ // network byte order {Key: []uint8{0xff, 0xfa}, IntervalEnd: false}, // 65530-inf }, }, { name: "regular ip range", set: network.NfTablesSet{ Kind: network.SetKindIPv4, Addresses: []netipx.IPRange{ netipx.MustParseIPRange("10.0.0.0-10.0.0.255"), }, }, expectedKeyType: nftables.TypeIPAddr, expectedInterval: true, expectedData: []nftables.SetElement{ // network byte order {Key: []uint8{10, 0, 0, 0}, IntervalEnd: false}, {Key: []uint8{10, 0, 1, 0}, IntervalEnd: true}, }, }, { name: "ip range with overflow", set: network.NfTablesSet{ Kind: network.SetKindIPv4, Addresses: []netipx.IPRange{ netipx.MustParseIPRange("10.0.0.0-255.255.255.255"), }, }, expectedKeyType: nftables.TypeIPAddr, expectedInterval: true, expectedData: []nftables.SetElement{ // network byte order {Key: []uint8{10, 0, 0, 0}, IntervalEnd: false}, // 10.0.0.0-inf }, }, } { t.Run(test.name, func(t *testing.T) { assert.Equal(t, test.expectedKeyType, test.set.KeyType()) assert.Equal(t, test.expectedInterval, test.set.IsInterval()) assert.Equal(t, test.expectedData, test.set.SetElements()) }) } } ================================================ FILE: internal/app/machined/pkg/adapters/network/vlan_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "encoding/binary" "github.com/mdlayher/netlink" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // VLANSpec adapter provides encoding/decoding to netlink structures. // //nolint:revive,golint func VLANSpec(r *network.VLANSpec) vlanSpec { return vlanSpec{ VLANSpec: r, } } type vlanSpec struct { *network.VLANSpec } // Encode the VLANSpec into netlink attributes. func (a vlanSpec) Encode() ([]byte, error) { vlan := a.VLANSpec encoder := netlink.NewAttributeEncoder() encoder.Uint16(unix.IFLA_VLAN_ID, vlan.VID) buf := make([]byte, 2) binary.BigEndian.PutUint16(buf, uint16(vlan.Protocol)) encoder.Bytes(unix.IFLA_VLAN_PROTOCOL, buf) return encoder.Encode() } // Decode the VLANSpec from netlink attributes. func (a vlanSpec) Decode(data []byte) error { vlan := a.VLANSpec decoder, err := netlink.NewAttributeDecoder(data) if err != nil { return err } for decoder.Next() { switch decoder.Type() { case unix.IFLA_VLAN_ID: vlan.VID = decoder.Uint16() case unix.IFLA_VLAN_PROTOCOL: vlan.Protocol = nethelpers.VLANProtocol(binary.BigEndian.Uint16(decoder.Bytes())) } } return decoder.Err() } ================================================ FILE: internal/app/machined/pkg/adapters/network/vlan_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/require" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestVLANSpec(t *testing.T) { spec := network.VLANSpec{ VID: 25, Protocol: nethelpers.VLANProtocol8021AD, } b, err := networkadapter.VLANSpec(&spec).Encode() require.NoError(t, err) var decodedSpec network.VLANSpec require.NoError(t, networkadapter.VLANSpec(&decodedSpec).Decode(b)) require.Equal(t, spec, decodedSpec) } ================================================ FILE: internal/app/machined/pkg/adapters/network/vrf_master_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/mdlayher/netlink" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // VRFMasterSpec adapter provides encoding/decoding to netlink structures. // //nolint:revive func VRFMasterSpec(r *network.VRFMasterSpec) vrfMaster { return vrfMaster{ VRFMasterSpec: r, } } // vrfMaster contains the vrf master spec and provides methods for encoding/decoding it to netlink structures. type vrfMaster struct { *network.VRFMasterSpec } // Encode the VRFMasterSpec into netlink attributes. func (a vrfMaster) Encode() ([]byte, error) { vrf := a.VRFMasterSpec encoder := netlink.NewAttributeEncoder() encoder.Uint32(unix.IFLA_VRF_TABLE, uint32(vrf.Table)) return encoder.Encode() } // Decode the VRFMasterSpec from netlink attributes. func (a vrfMaster) Decode(data []byte) error { vrf := a.VRFMasterSpec decoder, err := netlink.NewAttributeDecoder(data) if err != nil { return err } for decoder.Next() { if decoder.Type() == unix.IFLA_VRF_TABLE { vrf.Table = nethelpers.RoutingTable(decoder.Uint32()) } } return decoder.Err() } ================================================ FILE: internal/app/machined/pkg/adapters/network/vrf_master_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/require" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestVRFMasterSpec(t *testing.T) { spec := network.VRFMasterSpec{ Table: 4294967295, } b, err := networkadapter.VRFMasterSpec(&spec).Encode() require.NoError(t, err) var decodedSpec network.VRFMasterSpec require.NoError(t, networkadapter.VRFMasterSpec(&decodedSpec).Decode(b)) require.Equal(t, spec, decodedSpec) } ================================================ FILE: internal/app/machined/pkg/adapters/network/wireguard_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net" "net/netip" "github.com/siderolabs/gen/xslices" "go4.org/netipx" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // WireguardSpec adapter provides encoding/decoding to netlink structures. // //nolint:revive,golint func WireguardSpec(r *network.WireguardSpec) wireguardSpec { return wireguardSpec{ WireguardSpec: r, } } type wireguardSpec struct { *network.WireguardSpec } // Encode converts WireguardSpec to wgctrl.Config "patch" to adjust the config to match the spec. // // Both specs should be sorted. // // Encode produces a "diff" as *wgtypes.Config which when applied transitions `existing` configuration into // configuration `spec`. // //nolint:gocyclo,cyclop func (a wireguardSpec) Encode(existing *network.WireguardSpec) (*wgtypes.Config, error) { spec := a.WireguardSpec cfg := &wgtypes.Config{} if existing.PrivateKey != spec.PrivateKey { key, err := wgtypes.ParseKey(spec.PrivateKey) if err != nil { return nil, err } cfg.PrivateKey = &key } if existing.ListenPort != spec.ListenPort { cfg.ListenPort = &spec.ListenPort } if existing.FirewallMark != spec.FirewallMark { cfg.FirewallMark = &spec.FirewallMark } // perform a merge of two sorted list of peers producing diff l, r := 0, 0 for l < len(existing.Peers) || r < len(spec.Peers) { addPeer := func(peer *network.WireguardPeer) error { pubKey, err := wgtypes.ParseKey(peer.PublicKey) if err != nil { return err } var presharedKey *wgtypes.Key if peer.PresharedKey != "" { var parsedKey wgtypes.Key parsedKey, err = wgtypes.ParseKey(peer.PresharedKey) if err != nil { return err } presharedKey = &parsedKey } var endpoint *net.UDPAddr if peer.Endpoint != "" { endpoint, err = net.ResolveUDPAddr("", peer.Endpoint) if err != nil { return err } } cfg.Peers = append(cfg.Peers, wgtypes.PeerConfig{ PublicKey: pubKey, Endpoint: endpoint, PresharedKey: presharedKey, PersistentKeepaliveInterval: &peer.PersistentKeepaliveInterval, ReplaceAllowedIPs: true, AllowedIPs: xslices.Map(peer.AllowedIPs, func(peerIP netip.Prefix) net.IPNet { return *netipx.PrefixIPNet(peerIP) }), }) return nil } deletePeer := func(peer *network.WireguardPeer) error { pubKey, err := wgtypes.ParseKey(peer.PublicKey) if err != nil { return err } cfg.Peers = append(cfg.Peers, wgtypes.PeerConfig{ PublicKey: pubKey, Remove: true, }) return nil } var left, right *network.WireguardPeer if l < len(existing.Peers) { left = &existing.Peers[l] } if r < len(spec.Peers) { right = &spec.Peers[r] } switch { // peer from the "right" (new spec) is missing in "existing" (left), add it case left == nil || (right != nil && left.PublicKey > right.PublicKey): if err := addPeer(right); err != nil { return nil, err } r++ // peer from the "left" (existing) is missing in new spec (right), so it should be removed case right == nil || (left != nil && left.PublicKey < right.PublicKey): // deleting peers from the existing if err := deletePeer(left); err != nil { return nil, err } l++ // peer public keys are equal, so either they are identical or peer should be replaced case left.PublicKey == right.PublicKey: if !left.Equal(right) { // replace peer if err := addPeer(right); err != nil { return nil, err } } l++ r++ } } return cfg, nil } // Decode spec from the device state. func (a wireguardSpec) Decode(dev *wgtypes.Device, isStatus bool) { spec := a.WireguardSpec if isStatus { spec.PublicKey = dev.PublicKey.String() } else { spec.PrivateKey = dev.PrivateKey.String() } spec.ListenPort = dev.ListenPort spec.FirewallMark = dev.FirewallMark spec.Peers = make([]network.WireguardPeer, len(dev.Peers)) for i := range spec.Peers { spec.Peers[i].PublicKey = dev.Peers[i].PublicKey.String() if dev.Peers[i].Endpoint != nil { spec.Peers[i].Endpoint = dev.Peers[i].Endpoint.String() } var zeroKey wgtypes.Key if dev.Peers[i].PresharedKey != zeroKey { spec.Peers[i].PresharedKey = dev.Peers[i].PresharedKey.String() } spec.Peers[i].PersistentKeepaliveInterval = dev.Peers[i].PersistentKeepaliveInterval spec.Peers[i].AllowedIPs = xslices.Map(dev.Peers[i].AllowedIPs, func(peerIP net.IPNet) netip.Prefix { res, _ := netipx.FromStdIPNet(&peerIP) return res }) } } ================================================ FILE: internal/app/machined/pkg/adapters/network/wireguard_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net" "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestWireguardSpecDecode(t *testing.T) { if fipsmode.Strict() { t.Skip("skipping test in strict FIPS mode") } priv, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) pub1, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) pub2, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) var spec network.WireguardSpec // decode in spec mode networkadapter.WireguardSpec(&spec).Decode(&wgtypes.Device{ PrivateKey: priv, ListenPort: 30000, FirewallMark: 1, Peers: []wgtypes.Peer{ { PublicKey: pub1.PublicKey(), PresharedKey: priv, Endpoint: &net.UDPAddr{ IP: net.ParseIP("10.2.0.3"), Port: 20000, }, AllowedIPs: []net.IPNet{ { IP: net.ParseIP("172.24.0.0"), Mask: net.IPv4Mask(255, 255, 0, 0), }, }, }, { PublicKey: pub2.PublicKey(), AllowedIPs: []net.IPNet{ { IP: net.ParseIP("172.25.0.0"), Mask: net.IPv4Mask(255, 255, 255, 0), }, }, }, }, }, false) expected := network.WireguardSpec{ PrivateKey: priv.String(), ListenPort: 30000, FirewallMark: 1, Peers: []network.WireguardPeer{ { PublicKey: pub1.PublicKey().String(), PresharedKey: priv.String(), Endpoint: "10.2.0.3:20000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.24.0.0/16"), }, }, { PublicKey: pub2.PublicKey().String(), AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.25.0.0/24"), }, }, }, } assert.Equal(t, expected, spec) assert.True(t, expected.Equal(&spec)) // zeroed out listen port is still acceptable on the right side spec.ListenPort = 0 assert.True(t, expected.Equal(&spec)) // ... but not on the left side expected.ListenPort = 0 spec.ListenPort = 30000 assert.False(t, expected.Equal(&spec)) var zeroSpec network.WireguardSpec assert.False(t, zeroSpec.Equal(&spec)) } func TestWireguardSpecDecodeStatus(t *testing.T) { if fipsmode.Strict() { t.Skip("skipping test in strict FIPS mode") } priv, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) var spec network.WireguardSpec // decode in status mode networkadapter.WireguardSpec(&spec).Decode(&wgtypes.Device{ PrivateKey: priv, PublicKey: priv.PublicKey(), ListenPort: 30000, FirewallMark: 1, }, true) expected := network.WireguardSpec{ PublicKey: priv.PublicKey().String(), ListenPort: 30000, FirewallMark: 1, Peers: []network.WireguardPeer{}, } assert.Equal(t, expected, spec) } func TestWireguardSpecEncode(t *testing.T) { if fipsmode.Strict() { t.Skip("skipping test in strict FIPS mode") } priv, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) pub1, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) pub2, err := wgtypes.GeneratePrivateKey() require.NoError(t, err) // make sure pub1 < pub2 if pub1.PublicKey().String() > pub2.PublicKey().String() { pub1, pub2 = pub2, pub1 } specV1 := network.WireguardSpec{ PrivateKey: priv.String(), ListenPort: 30000, FirewallMark: 1, Peers: []network.WireguardPeer{ { PublicKey: pub1.PublicKey().String(), Endpoint: "10.2.0.3:20000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.24.0.0/16"), }, }, { PublicKey: pub2.PublicKey().String(), AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.25.0.0/24"), }, }, }, } specV1.Sort() var zero network.WireguardSpec networkadapter.WireguardSpec(&zero).Decode(&wgtypes.Device{}, false) zero.Sort() // from zero (empty) config to config with two peers delta, err := networkadapter.WireguardSpec(&specV1).Encode(&zero) require.NoError(t, err) assert.Equal(t, &wgtypes.Config{ PrivateKey: &priv, ListenPort: new(30000), FirewallMark: new(1), Peers: []wgtypes.PeerConfig{ { PublicKey: pub1.PublicKey(), Endpoint: &net.UDPAddr{ IP: net.ParseIP("10.2.0.3"), Port: 20000, }, PersistentKeepaliveInterval: new(time.Duration(0)), ReplaceAllowedIPs: true, AllowedIPs: []net.IPNet{ { IP: net.ParseIP("172.24.0.0").To4(), Mask: net.IPv4Mask(255, 255, 0, 0), }, }, }, { PublicKey: pub2.PublicKey(), PersistentKeepaliveInterval: new(time.Duration(0)), ReplaceAllowedIPs: true, AllowedIPs: []net.IPNet{ { IP: net.ParseIP("172.25.0.0").To4(), Mask: net.IPv4Mask(255, 255, 255, 0), }, }, }, }, }, delta) // noop delta, err = networkadapter.WireguardSpec(&specV1).Encode(&specV1) require.NoError(t, err) assert.Equal(t, &wgtypes.Config{}, delta) // delete peer2 specV2 := network.WireguardSpec{ PrivateKey: priv.String(), ListenPort: 30000, FirewallMark: 1, Peers: []network.WireguardPeer{ { PublicKey: pub1.PublicKey().String(), Endpoint: "10.2.0.3:20000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.24.0.0/16"), }, }, }, } delta, err = networkadapter.WireguardSpec(&specV2).Encode(&specV1) require.NoError(t, err) assert.Equal(t, &wgtypes.Config{ Peers: []wgtypes.PeerConfig{ { PublicKey: pub2.PublicKey(), Remove: true, }, }, }, delta) // update peer1, firewallMark specV3 := network.WireguardSpec{ PrivateKey: priv.String(), ListenPort: 30000, FirewallMark: 2, Peers: []network.WireguardPeer{ { PublicKey: pub1.PublicKey().String(), PresharedKey: priv.String(), AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.24.0.0/16"), }, }, }, } delta, err = networkadapter.WireguardSpec(&specV3).Encode(&specV2) require.NoError(t, err) assert.Equal(t, &wgtypes.Config{ FirewallMark: new(2), Peers: []wgtypes.PeerConfig{ { PublicKey: pub1.PublicKey(), PresharedKey: &priv, PersistentKeepaliveInterval: new(time.Duration(0)), ReplaceAllowedIPs: true, AllowedIPs: []net.IPNet{ { IP: net.ParseIP("172.24.0.0").To4(), Mask: net.IPv4Mask(255, 255, 0, 0), }, }, }, }, }, delta) } ================================================ FILE: internal/app/machined/pkg/adapters/perf/cpu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import ( "github.com/prometheus/procfs" "github.com/siderolabs/talos/pkg/machinery/resources/perf" ) // CPU adapter provides conversion from procfs. // //nolint:revive,golint func CPU(r *perf.CPU) cpu { return cpu{ CPU: r, } } type cpu struct { *perf.CPU } // Update current CPU snapshot. func (a cpu) Update(stat *procfs.Stat) { translateCPUStat := func(in procfs.CPUStat) perf.CPUStat { return perf.CPUStat{ User: in.User, Nice: in.Nice, System: in.System, Idle: in.Idle, Iowait: in.Iowait, Irq: in.IRQ, SoftIrq: in.SoftIRQ, Steal: in.Steal, Guest: in.Guest, GuestNice: in.GuestNice, } } translateListOfCPUStat := func(in map[int64]procfs.CPUStat) []perf.CPUStat { maxCore := int64(-1) for core := range in { maxCore = max(maxCore, core) } slc := make([]perf.CPUStat, maxCore+1) for core, stat := range in { slc[core] = translateCPUStat(stat) } return slc } *a.CPU.TypedSpec() = perf.CPUSpec{ CPUTotal: translateCPUStat(stat.CPUTotal), CPU: translateListOfCPUStat(stat.CPU), IRQTotal: stat.IRQTotal, ContextSwitches: stat.ContextSwitches, ProcessCreated: stat.ProcessCreated, ProcessRunning: stat.ProcessesRunning, ProcessBlocked: stat.ProcessesBlocked, SoftIrqTotal: stat.SoftIRQTotal, } } ================================================ FILE: internal/app/machined/pkg/adapters/perf/mem.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import ( "github.com/prometheus/procfs" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/resources/perf" ) // Memory adapter provides conversion from procfs. // //nolint:revive,golint func Memory(r *perf.Memory) memory { return memory{ Memory: r, } } type memory struct { *perf.Memory } // Update current Mem snapshot. func (a memory) Update(info *procfs.Meminfo) { *a.Memory.TypedSpec() = perf.MemorySpec{ MemTotal: pointer.SafeDeref(info.MemTotal), MemUsed: pointer.SafeDeref(info.MemTotal) - pointer.SafeDeref(info.MemFree), MemAvailable: pointer.SafeDeref(info.MemAvailable), Buffers: pointer.SafeDeref(info.Buffers), Cached: pointer.SafeDeref(info.Cached), SwapCached: pointer.SafeDeref(info.SwapCached), Active: pointer.SafeDeref(info.Active), Inactive: pointer.SafeDeref(info.Inactive), ActiveAnon: pointer.SafeDeref(info.ActiveAnon), InactiveAnon: pointer.SafeDeref(info.InactiveAnon), ActiveFile: pointer.SafeDeref(info.ActiveFile), InactiveFile: pointer.SafeDeref(info.InactiveFile), Unevictable: pointer.SafeDeref(info.Unevictable), Mlocked: pointer.SafeDeref(info.Mlocked), SwapTotal: pointer.SafeDeref(info.SwapTotal), SwapFree: pointer.SafeDeref(info.SwapFree), Dirty: pointer.SafeDeref(info.Dirty), Writeback: pointer.SafeDeref(info.Writeback), AnonPages: pointer.SafeDeref(info.AnonPages), Mapped: pointer.SafeDeref(info.Mapped), Shmem: pointer.SafeDeref(info.Shmem), Slab: pointer.SafeDeref(info.Slab), SReclaimable: pointer.SafeDeref(info.SReclaimable), SUnreclaim: pointer.SafeDeref(info.SUnreclaim), KernelStack: pointer.SafeDeref(info.KernelStack), PageTables: pointer.SafeDeref(info.PageTables), NFSunstable: pointer.SafeDeref(info.NFSUnstable), Bounce: pointer.SafeDeref(info.Bounce), WritebackTmp: pointer.SafeDeref(info.WritebackTmp), CommitLimit: pointer.SafeDeref(info.CommitLimit), CommittedAS: pointer.SafeDeref(info.CommittedAS), VmallocTotal: pointer.SafeDeref(info.VmallocTotal), VmallocUsed: pointer.SafeDeref(info.VmallocUsed), VmallocChunk: pointer.SafeDeref(info.VmallocChunk), HardwareCorrupted: pointer.SafeDeref(info.HardwareCorrupted), AnonHugePages: pointer.SafeDeref(info.AnonHugePages), ShmemHugePages: pointer.SafeDeref(info.ShmemHugePages), ShmemPmdMapped: pointer.SafeDeref(info.ShmemPmdMapped), CmaTotal: pointer.SafeDeref(info.CmaTotal), CmaFree: pointer.SafeDeref(info.CmaFree), HugePagesTotal: pointer.SafeDeref(info.HugePagesTotal), HugePagesFree: pointer.SafeDeref(info.HugePagesFree), HugePagesRsvd: pointer.SafeDeref(info.HugePagesRsvd), HugePagesSurp: pointer.SafeDeref(info.HugePagesSurp), Hugepagesize: pointer.SafeDeref(info.Hugepagesize), DirectMap4k: pointer.SafeDeref(info.DirectMap4k), DirectMap2m: pointer.SafeDeref(info.DirectMap2M), DirectMap1g: pointer.SafeDeref(info.DirectMap1G), } } ================================================ FILE: internal/app/machined/pkg/adapters/perf/perf.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package perf implements adapters wrapping resources/perf to provide additional functionality. package perf ================================================ FILE: internal/app/machined/pkg/adapters/secrets/encryption_salt.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "crypto/rand" "io" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // EncryptionSalt adapter provides encryption salt generation. // //nolint:revive,golint func EncryptionSalt(r *secrets.EncryptionSaltSpec) encryptionSalt { return encryptionSalt{ EncryptionSaltSpec: r, } } type encryptionSalt struct { *secrets.EncryptionSaltSpec } // Generate new encryption salt. func (a encryptionSalt) Generate() error { buf := make([]byte, constants.DiskEncryptionSaltSize) if _, err := io.ReadFull(rand.Reader, buf); err != nil { return err } a.EncryptionSaltSpec.DiskSalt = buf return nil } ================================================ FILE: internal/app/machined/pkg/adapters/secrets/encryption_salt_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" secretsadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestEncryptionSaltGenerate(t *testing.T) { t.Parallel() var spec1, spec2 secrets.EncryptionSaltSpec require.NoError(t, secretsadapter.EncryptionSalt(&spec1).Generate()) require.NoError(t, secretsadapter.EncryptionSalt(&spec2).Generate()) assert.NotEqual(t, spec1, spec2) assert.Len(t, spec1.DiskSalt, constants.DiskEncryptionSaltSize) } ================================================ FILE: internal/app/machined/pkg/adapters/secrets/secrets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package secrets implements adapters wrapping resources to provide additional functionality. package secrets ================================================ FILE: internal/app/machined/pkg/adapters/wireguard/wireguard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package wireguard implements common wireguard functionality. package wireguard import "time" // PeerDownInterval is the time since last handshake when established peer is considered to be down. // // WG whitepaper defines a downed peer as being: // Handshake Timeout (180s) + Rekey Timeout (5s) + Rekey Attempt Timeout (90s) // // This interval is applied when the link is already established. const PeerDownInterval = (180 + 5 + 90) * time.Second ================================================ FILE: internal/app/machined/pkg/automaton/blockautomaton/blockmachine.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package blockautomaton implements the controller-specific state automaton for block resources. package blockautomaton ================================================ FILE: internal/app/machined/pkg/automaton/blockautomaton/volume_mount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package blockautomaton import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xerrors" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumeMountCallbackFunc is a callback function that is called when a volume is mounted. type VolumeMountCallbackFunc func(context.Context, controller.ReaderWriter, *zap.Logger, *block.VolumeMountStatus) error // volumeMountContext is the internal context for the volume mounter controller state machine. type volumeMountContext struct { mountID string volumeID string requester string callback VolumeMountCallbackFunc options VolumeMounterOptions } // VolumeMounterAutomaton is the type of the volume mounter controller state machine. type VolumeMounterAutomaton = *automaton.ControllerAutomaton[volumeMountContext] // VolumeMounterOptions is the options for the volume mounter controller state machine. type VolumeMounterOptions struct { ReadOnly bool Detached bool } // VolumeMounterOption is a function that configures the volume mounter controller state machine. type VolumeMounterOption func(*VolumeMounterOptions) // WithReadOnly sets the volume mounter controller state machine to read-only mode. func WithReadOnly(readOnly bool) VolumeMounterOption { return func(options *VolumeMounterOptions) { options.ReadOnly = readOnly } } // WithDetached sets the volume mounter controller state machine to detached mode. func WithDetached(detached bool) VolumeMounterOption { return func(options *VolumeMounterOptions) { options.Detached = detached } } // NewVolumeMounter creates a new volume mounter controller state machine. // // It ensures that the volume is mounted, and calls the callback function when the volume is mounted, // unmounting the volume before terminating the state machine. func NewVolumeMounter(requester, volumeID string, callback VolumeMountCallbackFunc, options ...VolumeMounterOption) VolumeMounterAutomaton { opts := VolumeMounterOptions{} for _, option := range options { option(&opts) } return automaton.NewControllerAutomaton(createVolumeMountRequest, volumeMountContext{ mountID: requester + "-" + volumeID, volumeID: volumeID, requester: requester, callback: callback, options: opts, }, ) } // createVolumeMountRequest is the initial state of the volume mounter controller state machine. // // Transitions to: waitForMountStatus. func createVolumeMountRequest(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { if err := safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, mountContext.mountID), func(req *block.VolumeMountRequest) error { req.TypedSpec().VolumeID = mountContext.volumeID req.TypedSpec().Requester = mountContext.requester req.TypedSpec().ReadOnly = mountContext.options.ReadOnly req.TypedSpec().Detached = mountContext.options.Detached return nil }); err != nil { return nil, fmt.Errorf("error creating volume mount request: %w", err) } return waitForMountStatus, nil } // waitForMountStatus is the state of the volume mounter controller state machine that waits for the mount status to be established. // // Transitions to: callbackWithMountStatus. func waitForMountStatus(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { mountStatus, err := safe.ReaderGetByID[*block.VolumeMountStatus](ctx, r, mountContext.mountID) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error reading volume mount status: %w", err) } if mountStatus == nil { // wait for the mount status to be established return nil, xerrors.NewTaggedf[automaton.Continue]("waiting for mount status to be established") } if mountStatus.TypedSpec().ReadOnly && !mountContext.options.ReadOnly { return nil, xerrors.NewTaggedf[automaton.Continue]("volume is mounted read-only, but read-write was requested") } if !mountStatus.Metadata().Finalizers().Has(mountContext.requester) { if err = r.AddFinalizer(ctx, mountStatus.Metadata(), mountContext.requester); err != nil { return nil, fmt.Errorf("error adding finalizer: %w", err) } } return callbackWithMountStatus(mountStatus), nil } // callbackWithMountStatus is the state of the volume mounter controller state machine that calls the callback with the mount status. // // Transitions to: removeMountStatusFinalizer. func callbackWithMountStatus(mountStatus *block.VolumeMountStatus) func( ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext, ) (automaton.ControllerStateFunc[volumeMountContext], error) { return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { if err := mountContext.callback(ctx, r, logger, mountStatus); err != nil { return nil, err } return removeMountStatusFinalizer, nil } } // removeMountStatusFinalizer is the state of the volume mounter controller state machine that removes the mount status finalizer. // // Transitions to: removeMountRequest. func removeMountStatusFinalizer(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { if err := r.RemoveFinalizer(ctx, block.NewVolumeMountStatus(block.NamespaceName, mountContext.mountID).Metadata(), mountContext.requester); err != nil { return nil, fmt.Errorf("error removing finalizer: %w", err) } return removeMountRequest, nil } // removeMountRequest is the state of the volume mounter controller state machine that removes the mount request. // // Transitions to: nil. func removeMountRequest(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { mountRequest := block.NewVolumeMountRequest(block.NamespaceName, mountContext.mountID) okToDestroy, err := r.Teardown(ctx, mountRequest.Metadata()) if err != nil { return nil, fmt.Errorf("error tearing down mount request: %w", err) } if !okToDestroy { return nil, xerrors.NewTaggedf[automaton.Continue]("mount request is not ready to be destroyed") } if err = r.Destroy(ctx, mountRequest.Metadata()); err != nil { return nil, fmt.Errorf("error destroying mount request: %w", err) } return waitForVolumeMountStatusRemoved, nil } // waitForVolumeMountStatusRemoved is the state of the volume mounter controller state machine that waits for the volume mount status to be removed. func waitForVolumeMountStatusRemoved(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountContext volumeMountContext) (automaton.ControllerStateFunc[volumeMountContext], error) { mountStatus, err := safe.ReaderGetByID[*block.VolumeMountStatus](ctx, r, mountContext.mountID) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error reading volume mount status: %w", err) } if mountStatus == nil { // removed return nil, nil } return nil, xerrors.NewTaggedf[automaton.Continue]("waiting for mount status to be removed") } ================================================ FILE: internal/app/machined/pkg/automaton/blockautomaton/volume_mount_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package blockautomaton_test import ( "context" "errors" "testing" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/owned" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestVolumeMounter(t *testing.T) { t.Parallel() logger := zaptest.NewLogger(t) st := state.WrapCore(namespaced.NewState(inmem.Build)) ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) t.Cleanup(cancel) mountedCh := make(chan struct{}) volumeMounter := blockautomaton.NewVolumeMounter("requester", "volumeID", func(ctx context.Context, rw controller.ReaderWriter, l *zap.Logger, vms *block.VolumeMountStatus) error { select { case <-mountedCh: // already closed return nil default: close(mountedCh) return errors.New("mount status callback") } }) const mountID = "requester-volumeID" adapter := owned.New(st, "automaton") // 1st run, should create the volume mount request require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) rtestutils.AssertResource(ctx, t, st, mountID, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal("requester", vmr.TypedSpec().Requester) asrt.Equal("volumeID", vmr.TypedSpec().VolumeID) }) require.NoError(t, st.AddFinalizer(ctx, block.NewVolumeMountRequest(block.NamespaceName, mountID).Metadata(), "test")) // no-op run, as the volume mount status doesn't exist require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) vms := block.NewVolumeMountStatus(block.NamespaceName, mountID) require.NoError(t, st.Create(ctx, vms)) // 2nd run, should put a finalizer on the volume mount status and call the callback 1st time err := volumeMounter.Run(ctx, adapter, logger) select { case <-mountedCh: case <-ctx.Done(): t.Fatal("timed out waiting for mount status callback") } require.ErrorContains(t, err, "mount status callback") // should put a finalizer on the volume mount status rtestutils.AssertResource(ctx, t, st, mountID, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Has("requester")) }) // 3rd run, now the mount callback should be called again, return nil, // and volume mount status finalizer should be removed require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) // should remove a finalizer on the volume mount status rtestutils.AssertResource(ctx, t, st, mountID, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.False(vms.Metadata().Finalizers().Has("requester")) }) // the mount request now should be torn down by the automaton rtestutils.AssertResource(ctx, t, st, mountID, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(resource.PhaseTearingDown, vmr.Metadata().Phase()) }) // remove our finalizer from the mount request require.NoError(t, st.RemoveFinalizer(ctx, block.NewVolumeMountRequest(block.NamespaceName, mountID).Metadata(), "test")) // 4th run, now the mount request should be destroyed require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) rtestutils.AssertNoResource[*block.VolumeMountRequest](ctx, t, st, mountID) // destroy the volume mount status require.NoError(t, st.Destroy(ctx, vms.Metadata())) var finished bool // 5th run, now the automaton should have finished require.NoError(t, volumeMounter.Run(ctx, adapter, logger, automaton.WithAfterFunc(func() error { finished = true return nil }))) assert.True(t, finished) } func TestVolumeMounterReadWrite(t *testing.T) { t.Parallel() logger := zaptest.NewLogger(t) st := state.WrapCore(namespaced.NewState(inmem.Build)) ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) t.Cleanup(cancel) mountedCh := make(chan struct{}) volumeMounter := blockautomaton.NewVolumeMounter("rw", "volumeID", func(ctx context.Context, rw controller.ReaderWriter, l *zap.Logger, vms *block.VolumeMountStatus) error { close(mountedCh) return nil }) const mountID = "rw-volumeID" adapter := owned.New(st, "automaton") // 1st run, should create the volume mount request require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) rtestutils.AssertResource(ctx, t, st, mountID, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal("rw", vmr.TypedSpec().Requester) asrt.Equal("volumeID", vmr.TypedSpec().VolumeID) asrt.False(vmr.TypedSpec().ReadOnly) }) require.NoError(t, st.AddFinalizer(ctx, block.NewVolumeMountRequest(block.NamespaceName, mountID).Metadata(), "test")) vms := block.NewVolumeMountStatus(block.NamespaceName, mountID) vms.TypedSpec().ReadOnly = true // volume is mounted read-only (from some other request) require.NoError(t, st.Create(ctx, vms)) // no-op run, as the volume mount status is read-only require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) vms.TypedSpec().ReadOnly = false // volume is now mounted read-write require.NoError(t, st.Update(ctx, vms)) // 2nd run, should put a finalizer on the volume mount status and call the callback 1st time, finishing the whole sequence require.NoError(t, volumeMounter.Run(ctx, adapter, logger)) select { case <-mountedCh: case <-ctx.Done(): t.Fatal("timed out waiting for mount status callback") } } ================================================ FILE: internal/app/machined/pkg/automaton/machine.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package automaton implements the controller-specific state automaton (state machine). package automaton import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/siderolabs/gen/xerrors" "go.uber.org/zap" ) // ControllerStateFunc is a function that implements a state in the controller state automaton. // // Each state in the automaton is implemented by a function which returns the next state and an error. // If the error returned is tagged with Continue, the state automaton returns nil error keeping the state, // this should be used to keep progressing on next controller reconcile loop. // If the state returns an error, the state automaton returns the error and pauses the automaton.ControllerStateFunc // If the returned next state is nil, the state automaton terminates and always returns nil. type ControllerStateFunc[T any] func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, v T) (ControllerStateFunc[T], error) // ControllerAutomaton is a state automaton that is used in a controller context. // // Type T holds a context value that is passed to each state function. type ControllerAutomaton[T any] struct { state ControllerStateFunc[T] value T } // NewControllerAutomaton creates a new controller automaton with the specified initialState and value. func NewControllerAutomaton[T any](initialState ControllerStateFunc[T], v T) *ControllerAutomaton[T] { return &ControllerAutomaton[T]{ state: initialState, value: v, } } // Continue is an error tag that indicates that the state automaton should return to the controller with nil error and keep the state. type Continue struct{} // RunOptions is a struct that holds options for the Run function. type RunOptions struct { AfterFunc func() error } // RunOption is a function that configures the RunOptions. type RunOption func(*RunOptions) // WithAfterFunc sets the AfterFunc option. func WithAfterFunc(afterFunc func() error) RunOption { return func(options *RunOptions) { options.AfterFunc = afterFunc } } // Run is the entrypoint to the state automaton. // // Run is supposed to be called from the controller's reconcile loop. // // If Run returns an error, the controller should propagate the error back, and on nil error, // the controller should wait for the next event. func (automaton *ControllerAutomaton[T]) Run(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, options ...RunOption) error { opts := &RunOptions{} for _, opt := range options { opt(opts) } for { if automaton.state == nil { if opts.AfterFunc != nil { return opts.AfterFunc() } return nil } nextState, err := automaton.state(ctx, r, logger, automaton.value) if err != nil { if xerrors.TagIs[Continue](err) { return nil } return err } automaton.state = nextState } } ================================================ FILE: internal/app/machined/pkg/controllers/block/block.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package block provides the controllers related to blockdevices, mounts, etc. package block ================================================ FILE: internal/app/machined/pkg/controllers/block/devices.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "os" "path/filepath" "strconv" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/inotify" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/kobject" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/sysblock" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // DevicesController provides a view of available block devices with information about pending updates. type DevicesController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *DevicesController) Name() string { return "block.DevicesController" } // Inputs implements controller.Controller interface. func (ctrl *DevicesController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *DevicesController) Outputs() []controller.Output { return []controller.Output{ { Type: block.DeviceType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DevicesController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // in container mode, no devices if ctrl.V1Alpha1Mode == machineruntime.ModeContainer { return nil } // start the watcher first watcher, err := kobject.NewWatcher() if err != nil { return fmt.Errorf("failed to create kobject watcher: %w", err) } defer watcher.Close() //nolint:errcheck watchCh := watcher.Run(logger) // start the inotify watcher inotifyWatcher, err := inotify.NewWatcher() if err != nil { return fmt.Errorf("failed to create inotify watcher: %w", err) } defer inotifyWatcher.Close() //nolint:errcheck inotifyCh, inotifyErrCh := inotifyWatcher.Run() // reconcile the initial list of devices while the watcher is running select { case <-ctx.Done(): return nil case <-r.EventCh(): } if err = ctrl.resync(ctx, r, logger, inotifyWatcher); err != nil { return fmt.Errorf("failed to resync: %w", err) } for { select { case ev := <-watchCh: if ev.Subsystem != "block" { continue } ev.DevicePath = filepath.Join("/sys", ev.DevicePath) if err = ctrl.processEvent(ctx, r, logger, inotifyWatcher, ev); err != nil { return err } case err = <-inotifyErrCh: return fmt.Errorf("inotify watcher failed: %w", err) case updatedPath := <-inotifyCh: id := filepath.Base(updatedPath) if err = ctrl.bumpGeneration(ctx, r, logger, id); err != nil { return err } case <-ctx.Done(): return nil } } } func (ctrl *DevicesController) bumpGeneration(ctx context.Context, r controller.Runtime, logger *zap.Logger, id string) error { _, err := safe.ReaderGetByID[*block.Device](ctx, r, id) if err != nil { if state.IsNotFoundError(err) { // skip it return nil } return err } logger.Debug("bumping generation for device, inotify update", zap.String("id", id)) return safe.WriterModify(ctx, r, block.NewDevice(block.NamespaceName, id), func(dev *block.Device) error { dev.TypedSpec().Generation++ return nil }) } func (ctrl *DevicesController) resync(ctx context.Context, r controller.Runtime, logger *zap.Logger, inotifyWatcher *inotify.Watcher) error { events, err := sysblock.Walk("/sys/block") if err != nil { return fmt.Errorf("failed to walk /sys/block: %w", err) } touchedIDs := make(map[string]struct{}, len(events)) for _, ev := range events { if err = ctrl.processEvent(ctx, r, logger, inotifyWatcher, ev); err != nil { return err } touchedIDs[ev.Values["DEVNAME"]] = struct{}{} } // remove devices that were not touched devices, err := safe.ReaderListAll[*block.Device](ctx, r) if err != nil { return fmt.Errorf("failed to list devices: %w", err) } for dev := range devices.All() { if _, ok := touchedIDs[dev.Metadata().ID()]; ok { continue } if err = r.Destroy(ctx, dev.Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to remove device: %w", err) } } return nil } //nolint:gocyclo func (ctrl *DevicesController) processEvent(ctx context.Context, r controller.Runtime, logger *zap.Logger, inotifyWatcher *inotify.Watcher, ev *kobject.Event) error { logger = logger.With( zap.String("action", string(ev.Action)), zap.String("path", ev.DevicePath), zap.String("id", ev.Values["DEVNAME"]), ) logger.Debug("processing event") id := ev.Values["DEVNAME"] devPath := filepath.Join("/dev", id) // re-stat the sysfs entry to make sure we are not out of sync with events _, reStatErr := os.Stat(ev.DevicePath) switch ev.Action { case kobject.ActionAdd, kobject.ActionBind, kobject.ActionOnline, kobject.ActionChange, kobject.ActionMove, kobject.ActionUnbind, kobject.ActionOffline: if reStatErr != nil { logger.Debug("skipped, as device path doesn't exist") return nil //nolint:nilerr // entry doesn't exist now, so skip the event } if err := safe.WriterModify(ctx, r, block.NewDevice(block.NamespaceName, id), func(dev *block.Device) error { dev.TypedSpec().Type = ev.Values["DEVTYPE"] dev.TypedSpec().Major = atoiOrZero(ev.Values["MAJOR"]) dev.TypedSpec().Minor = atoiOrZero(ev.Values["MINOR"]) dev.TypedSpec().PartitionName = ev.Values["PARTNAME"] dev.TypedSpec().PartitionNumber = atoiOrZero(ev.Values["PARTN"]) dev.TypedSpec().DevicePath = ev.DevicePath if dev.TypedSpec().Type == "partition" { dev.TypedSpec().Parent = filepath.Base(filepath.Dir(dev.TypedSpec().DevicePath)) dev.TypedSpec().Secondaries = nil } else { dev.TypedSpec().Parent = "" dev.TypedSpec().Secondaries = sysblock.ReadSecondaries(ev.DevicePath) } dev.TypedSpec().Generation++ return nil }); err != nil { return fmt.Errorf("failed to modify device %q: %w", id, err) } if err := inotifyWatcher.Add(devPath, unix.IN_CLOSE_WRITE); err != nil { return fmt.Errorf("failed to add inotify watch for %q: %w", devPath, err) } case kobject.ActionRemove: if reStatErr == nil { // entry still exists, skip removing logger.Debug("skipped, as device path still exists") return nil } if err := r.Destroy(ctx, block.NewDevice(block.NamespaceName, id).Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to remove device %q: %w", id, err) } if err := inotifyWatcher.Remove(devPath); err != nil { logger.Debug("failed to remove inotify watch", zap.String("device", devPath), zap.Error(err)) } default: logger.Debug("skipped, as action is not supported") } return nil } func atoiOrZero(s string) int { i, _ := strconv.Atoi(s) //nolint:errcheck return i } ================================================ FILE: internal/app/machined/pkg/controllers/block/devices_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "os" "testing" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type DevicesSuite struct { ctest.DefaultSuite } func TestDevicesSuite(t *testing.T) { suite.Run(t, new(DevicesSuite)) } func (suite *DevicesSuite) TestDiscover() { if os.Geteuid() != 0 { suite.T().Skip("skipping test; must be root to use inotify") } suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.DevicesController{})) // these devices should always exist on Linux rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"loop0", "loop1"}, func(r *block.Device, assertions *assert.Assertions) { assertions.Equal("disk", r.TypedSpec().Type) }) } ================================================ FILE: internal/app/machined/pkg/controllers/block/discovery.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "errors" "fmt" "path/filepath" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-blockdevice/v2/partitioning" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // DiscoveryController provides a filesystem/partition discovery for blockdevices. type DiscoveryController struct{} // Name implements controller.Controller interface. func (ctrl *DiscoveryController) Name() string { return "block.DiscoveryController" } // Inputs implements controller.Controller interface. func (ctrl *DiscoveryController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.DeviceType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.DiscoveryRefreshRequestType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DiscoveryController) Outputs() []controller.Output { return []controller.Output{ { Type: block.DiscoveredVolumeType, Kind: controller.OutputExclusive, }, { Type: block.DiscoveryRefreshStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *DiscoveryController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // lastObservedGenerations holds the last observed generation of each device. // // when the generation of a device changes, the device might have changed and might need to be re-probed. lastObservedGenerations := map[string]int{} // whenever new DiscoveryRefresh requests are received, the devices are re-probed. var lastObservedDiscoveryRefreshRequest int // nextRescan holds the pool of devices to be rescanned in the next batch. nextRescan := map[string]int{} rescanTicker := time.NewTicker(100 * time.Millisecond) defer rescanTicker.Stop() for { select { case <-ctx.Done(): return nil case <-rescanTicker.C: if len(nextRescan) == 0 { continue } logger.Debug("rescanning devices", zap.Strings("devices", maps.Keys(nextRescan))) if nextRescanBatch, err := ctrl.rescan(ctx, r, logger, maps.Keys(nextRescan)); err != nil { return fmt.Errorf("failed to rescan devices: %w", err) } else { nextRescan = map[string]int{} for id := range nextRescanBatch { nextRescan[id] = lastObservedGenerations[id] } } if err := safe.WriterModify(ctx, r, block.NewDiscoveryRefreshStatus(block.NamespaceName, block.RefreshID), func(status *block.DiscoveryRefreshStatus) error { status.TypedSpec().Request = lastObservedDiscoveryRefreshRequest return nil }); err != nil { return fmt.Errorf("failed to write discovery refresh status: %w", err) } case <-r.EventCh(): refreshRequest, err := safe.ReaderGetByID[*block.DiscoveryRefreshRequest](ctx, r, block.RefreshID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get refresh request: %w", err) } if refreshRequest != nil && refreshRequest.TypedSpec().Request != lastObservedDiscoveryRefreshRequest { lastObservedDiscoveryRefreshRequest = refreshRequest.TypedSpec().Request // force re-probe all devices clear(lastObservedGenerations) } devices, err := safe.ReaderListAll[*block.Device](ctx, r) if err != nil { return fmt.Errorf("failed to list devices: %w", err) } parents := map[string]string{} allDevices := map[string]struct{}{} for device := range devices.All() { if device.TypedSpec().Major == 1 { // ignore ram disks (/dev/ramX), major number is 1 // ref: https://www.kernel.org/doc/Documentation/admin-guide/devices.txt // ref: https://github.com/util-linux/util-linux/blob/c0207d354ee47fb56acfa64b03b5b559bb301280/misc-utils/lsblk.c#L2697-L2699 continue } allDevices[device.Metadata().ID()] = struct{}{} if device.TypedSpec().Parent != "" { parents[device.Metadata().ID()] = device.TypedSpec().Parent } if device.TypedSpec().Generation == lastObservedGenerations[device.Metadata().ID()] { continue } nextRescan[device.Metadata().ID()] = device.TypedSpec().Generation lastObservedGenerations[device.Metadata().ID()] = device.TypedSpec().Generation } // remove child devices if the parent is marked for rescan for id := range nextRescan { if parent, ok := parents[id]; ok { if _, ok := nextRescan[parent]; ok { delete(nextRescan, id) } } } // if the device is removed, add it to the nextRescan, and remove from lastObservedGenerations for id := range lastObservedGenerations { if _, ok := allDevices[id]; !ok { nextRescan[id] = lastObservedGenerations[id] delete(lastObservedGenerations, id) } } } } } //nolint:gocyclo,cyclop func (ctrl *DiscoveryController) rescan(ctx context.Context, r controller.Runtime, logger *zap.Logger, ids []string) (map[string]struct{}, error) { failedIDs := map[string]struct{}{} touchedIDs := map[string]struct{}{} nextRescan := map[string]struct{}{} for _, id := range ids { device, err := safe.ReaderGetByID[*block.Device](ctx, r, id) if err != nil { if state.IsNotFoundError(err) { failedIDs[id] = struct{}{} continue } return nil, fmt.Errorf("failed to get device: %w", err) } info, err := blkid.ProbePath(filepath.Join("/dev", id), blkid.WithProbeLogger(logger.With(zap.String("device", id)))) if err != nil { if errors.Is(err, blkid.ErrFailedLock) { // failed to lock the blockdevice, retry later logger.Debug("failed to lock device, retrying later", zap.String("id", id)) nextRescan[id] = struct{}{} } else { logger.Debug("failed to probe device", zap.String("id", id), zap.Error(err)) failedIDs[id] = struct{}{} } continue } logger.Debug("probed device", zap.String("id", id), zap.Any("info", info)) if err = safe.WriterModify(ctx, r, block.NewDiscoveredVolume(block.NamespaceName, id), func(dv *block.DiscoveredVolume) error { dv.TypedSpec().DevPath = filepath.Join("/dev", id) dv.TypedSpec().Type = device.TypedSpec().Type dv.TypedSpec().DevicePath = device.TypedSpec().DevicePath dv.TypedSpec().Parent = device.TypedSpec().Parent if device.TypedSpec().Parent != "" { dv.TypedSpec().ParentDevPath = filepath.Join("/dev", device.TypedSpec().Parent) } dv.TypedSpec().Offset = 0 dv.TypedSpec().SetSize(info.Size) dv.TypedSpec().SectorSize = info.SectorSize dv.TypedSpec().IOSize = info.IOSize ctrl.fillDiscoveredVolumeFromInfo(dv, info.ProbeResult) return nil }); err != nil { return nil, fmt.Errorf("failed to write discovered volume: %w", err) } touchedIDs[id] = struct{}{} for _, nested := range info.Parts { partID := partitioning.DevName(id, nested.PartitionIndex) if err = safe.WriterModify(ctx, r, block.NewDiscoveredVolume(block.NamespaceName, partID), func(dv *block.DiscoveredVolume) error { dv.TypedSpec().Type = "partition" dv.TypedSpec().DevPath = filepath.Join("/dev", partID) dv.TypedSpec().DevicePath = filepath.Join(device.TypedSpec().DevicePath, partID) dv.TypedSpec().Parent = id dv.TypedSpec().ParentDevPath = filepath.Join("/dev", id) dv.TypedSpec().Offset = nested.PartitionOffset dv.TypedSpec().SetSize(nested.PartitionSize) dv.TypedSpec().SectorSize = info.SectorSize dv.TypedSpec().IOSize = info.IOSize ctrl.fillDiscoveredVolumeFromInfo(dv, nested.ProbeResult) if nested.PartitionUUID != nil { dv.TypedSpec().PartitionUUID = nested.PartitionUUID.String() } else { dv.TypedSpec().PartitionUUID = "" } if nested.PartitionType != nil { dv.TypedSpec().PartitionType = nested.PartitionType.String() } else { dv.TypedSpec().PartitionType = "" } if nested.PartitionLabel != nil { dv.TypedSpec().PartitionLabel = *nested.PartitionLabel } else { dv.TypedSpec().PartitionLabel = "" } dv.TypedSpec().PartitionIndex = nested.PartitionIndex return nil }); err != nil { return nil, fmt.Errorf("failed to write discovered volume: %w", err) } touchedIDs[partID] = struct{}{} } } // clean up discovered volumes discoveredVolumes, err := safe.ReaderListAll[*block.DiscoveredVolume](ctx, r) if err != nil { return nil, fmt.Errorf("failed to list discovered volumes: %w", err) } for dv := range discoveredVolumes.All() { if _, ok := touchedIDs[dv.Metadata().ID()]; ok { continue } _, isFailed := failedIDs[dv.Metadata().ID()] parentTouched := false if dv.TypedSpec().Parent != "" { if _, ok := touchedIDs[dv.TypedSpec().Parent]; ok { parentTouched = true } } if isFailed || parentTouched { // if the probe failed, or if the parent was touched, while this device was not, remove it if err = r.Destroy(ctx, dv.Metadata()); err != nil { return nil, fmt.Errorf("failed to destroy discovered volume: %w", err) } } } return nextRescan, nil } func (ctrl *DiscoveryController) fillDiscoveredVolumeFromInfo(dv *block.DiscoveredVolume, info blkid.ProbeResult) { dv.TypedSpec().Name = info.Name if info.UUID != nil { dv.TypedSpec().UUID = info.UUID.String() } else { dv.TypedSpec().UUID = "" } if info.Label != nil { dv.TypedSpec().Label = *info.Label } else { dv.TypedSpec().Label = "" } dv.TypedSpec().BlockSize = info.BlockSize dv.TypedSpec().FilesystemBlockSize = info.FilesystemBlockSize dv.TypedSpec().ProbedSize = info.ProbedSize } ================================================ FILE: internal/app/machined/pkg/controllers/block/disks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bufio" "cmp" "context" "fmt" "io" "path/filepath" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" blkdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-cmd/pkg/cmd" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // DisksController provides a detailed view of blockdevices of type 'disk'. type DisksController struct{} // Name implements controller.Controller interface. func (ctrl *DisksController) Name() string { return "block.DisksController" } // Inputs implements controller.Controller interface. func (ctrl *DisksController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.DeviceType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.SymlinkType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DisksController) Outputs() []controller.Output { return []controller.Output{ { Type: block.DiskType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DisksController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // lastObservedGenerations holds the last observed generation of each device. // // when the generation of a device changes, the device might have changed and might need to be re-probed. lastObservedGenerations := map[string]int{} for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } blockdevices, err := safe.ReaderListAll[*block.Device](ctx, r) if err != nil { return fmt.Errorf("failed to list block devices: %w", err) } touchedDisks := map[string]struct{}{} for device := range blockdevices.All() { if device.TypedSpec().Type != block.DeviceTypeDisk { continue } if device.TypedSpec().Major == 1 { // ignore ram disks (/dev/ramX), major number is 1 // ref: https://www.kernel.org/doc/Documentation/admin-guide/devices.txt // ref: https://github.com/util-linux/util-linux/blob/c0207d354ee47fb56acfa64b03b5b559bb301280/misc-utils/lsblk.c#L2697-L2699 continue } // always update symlinks, but skip if the disk hasn't been created yet if err = ctrl.updateSymlinks(ctx, r, device); err != nil { return err } if lastObserved, ok := lastObservedGenerations[device.Metadata().ID()]; ok && device.TypedSpec().Generation == lastObserved { // ignore disks which have same generation as before (don't query them once again) touchedDisks[device.Metadata().ID()] = struct{}{} continue } lastObservedGenerations[device.Metadata().ID()] = device.TypedSpec().Generation if err = ctrl.analyzeBlockDevice(ctx, r, logger.With(zap.String("device", device.Metadata().ID())), device, touchedDisks, blockdevices); err != nil { return fmt.Errorf("failed to analyze block device: %w", err) } } disks, err := safe.ReaderListAll[*block.Disk](ctx, r) if err != nil { return fmt.Errorf("failed to list disks: %w", err) } for disk := range disks.All() { if _, ok := touchedDisks[disk.Metadata().ID()]; ok { continue } if err = r.Destroy(ctx, disk.Metadata()); err != nil { return fmt.Errorf("failed to remove disk: %w", err) } delete(lastObservedGenerations, disk.Metadata().ID()) } } } func (ctrl *DisksController) updateSymlinks(ctx context.Context, r controller.Runtime, device *block.Device) error { symlinks, err := safe.ReaderGetByID[*block.Symlink](ctx, r, device.Metadata().ID()) if err != nil { if state.IsNotFoundError(err) { return nil } return err } _, err = safe.ReaderGetByID[*block.Disk](ctx, r, device.Metadata().ID()) if err != nil { if state.IsNotFoundError(err) { // don't create disk entries even if we have symlinks, let analyze handle it return nil } return err } return safe.WriterModify(ctx, r, block.NewDisk(block.NamespaceName, device.Metadata().ID()), func(d *block.Disk) error { d.TypedSpec().Symlinks = symlinks.TypedSpec().Paths return nil }) } //nolint:gocyclo func (ctrl *DisksController) analyzeBlockDevice( ctx context.Context, r controller.Runtime, logger *zap.Logger, device *block.Device, touchedDisks map[string]struct{}, allBlockdevices safe.List[*block.Device], ) error { bd, err := blkdev.NewFromPath(filepath.Join("/dev", device.Metadata().ID())) if err != nil { logger.Debug("failed to open blockdevice", zap.Error(err)) return nil } defer bd.Close() //nolint:errcheck size, err := bd.GetSize() if err != nil || size == 0 { return nil } if privateDM, _ := bd.IsPrivateDeviceMapper(); privateDM { //nolint:errcheck return nil } isCD := bd.IsCD() if isCD && bd.IsCDNoMedia() { // Linux reports non-zero size for CD-ROMs even when there is no media. size = 0 } ioSize, err := bd.GetIOSize() if err != nil { logger.Debug("failed to get io size", zap.Error(err)) } sectorSize := bd.GetSectorSize() readOnly, err := bd.IsReadOnly() if err != nil { logger.Debug("failed to get read only", zap.Error(err)) } props, err := bd.GetProperties() if err != nil { logger.Debug("failed to get properties", zap.Error(err)) } secondaryDisks := xslices.Map(device.TypedSpec().Secondaries, func(devID string) string { if secondary, ok := allBlockdevices.Find(func(dev *block.Device) bool { return dev.Metadata().ID() == devID }); ok { if secondary.TypedSpec().Parent != "" { return secondary.TypedSpec().Parent } } return devID }) symlinks, err := safe.ReaderGetByID[*block.Symlink](ctx, r, device.Metadata().ID()) if err != nil && !state.IsNotFoundError(err) { return err } touchedDisks[device.Metadata().ID()] = struct{}{} serial := props.Serial if serial == "" { // try to get serial from udevd helpers serial = serialFromUdevdHelpers(ctx, device.Metadata().ID(), props.Transport) if serial == "" { logger.Debug("failed to get serial from udevd helpers, using empty value", zap.String("device", device.Metadata().ID()), zap.String("transport", props.Transport)) } } return safe.WriterModify(ctx, r, block.NewDisk(block.NamespaceName, device.Metadata().ID()), func(d *block.Disk) error { d.TypedSpec().SetSize(size) d.TypedSpec().DevPath = filepath.Join("/dev", device.Metadata().ID()) d.TypedSpec().IOSize = ioSize d.TypedSpec().SectorSize = sectorSize d.TypedSpec().Readonly = readOnly d.TypedSpec().CDROM = isCD d.TypedSpec().Model = props.Model d.TypedSpec().Serial = serial d.TypedSpec().Modalias = props.Modalias d.TypedSpec().WWID = props.WWID d.TypedSpec().UUID = props.UUID d.TypedSpec().BusPath = props.BusPath d.TypedSpec().SubSystem = props.SubSystem d.TypedSpec().Transport = props.Transport d.TypedSpec().Rotational = props.Rotational d.TypedSpec().SecondaryDisks = secondaryDisks if symlinks != nil { d.TypedSpec().Symlinks = symlinks.TypedSpec().Paths } else { d.TypedSpec().Symlinks = nil } return nil }) } func serialFromUdevdHelpers(ctx context.Context, id, transport string) string { switch strings.ToLower(transport) { case "ata": return runUdevdHelper( ctx, "/usr/lib/udev/ata_id", "--export", filepath.Join("/dev", id), ) case "scsi": return runUdevdHelper( ctx, "/usr/lib/udev/scsi_id", "--export", "--allowlisted", "--device", filepath.Join("/dev", id), ) default: return "" } } func runUdevdHelper(ctx context.Context, helper string, args ...string) string { out, err := cmd.RunWithOptions(ctx, helper, args) if err != nil { return "" } env := parseEnv(strings.NewReader(out)) return cmp.Or( env["ID_SERIAL"], env["ID_SERIAL_SHORT"], ) } func parseEnv(r io.Reader) map[string]string { env := map[string]string{} scanner := bufio.NewScanner(r) for scanner.Scan() { line := scanner.Text() if line == "" || strings.HasPrefix(line, "#") { continue } key, value, ok := strings.Cut(line, "=") if !ok { continue } env[strings.TrimSpace(key)] = strings.TrimSpace(value) } return env } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/inotify/inotify.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package inotify implements a specialized inotify watcher for block devices. package inotify import ( "errors" "os" "strings" "sync" "unsafe" "golang.org/x/sys/unix" ) type ( watches struct { mu sync.RWMutex wd map[uint32]*watch // wd → watch path map[string]uint32 // pathname → wd } watch struct { wd uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) path string // Watch path. } ) func newWatches() *watches { return &watches{ wd: make(map[uint32]*watch), path: make(map[string]uint32), } } func (w *watches) remove(wd uint32) { w.mu.Lock() defer w.mu.Unlock() if _, ok := w.wd[wd]; ok { delete(w.path, w.wd[wd].path) } delete(w.wd, wd) } func (w *watches) removePath(path string) (uint32, bool) { w.mu.Lock() defer w.mu.Unlock() wd, ok := w.path[path] if !ok { return 0, false } delete(w.path, path) delete(w.wd, wd) return wd, true } func (w *watches) byWd(wd uint32) *watch { w.mu.RLock() defer w.mu.RUnlock() return w.wd[wd] } func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error { w.mu.Lock() defer w.mu.Unlock() var existing *watch wd, ok := w.path[path] if ok { existing = w.wd[wd] } upd, err := f(existing) if err != nil { return err } if upd != nil { w.wd[upd.wd] = upd w.path[upd.path] = upd.wd if upd.wd != wd { delete(w.wd, wd) } } return nil } // Watcher implements inotify-based file watching. type Watcher struct { wg sync.WaitGroup fd int inotifyFile *os.File watches *watches } // NewWatcher creates a new inotify Watcher. func NewWatcher() (*Watcher, error) { // Need to set nonblocking mode for SetDeadline to work, otherwise blocking // I/O operations won't terminate on close. fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) if fd == -1 { return nil, errno } return &Watcher{ fd: fd, inotifyFile: os.NewFile(uintptr(fd), ""), watches: newWatches(), }, nil } // Close the inotify watcher. func (w *Watcher) Close() error { // Causes any blocking reads to return with an error, provided the file // still supports deadline operations. err := w.inotifyFile.Close() if err != nil { return err } // Wait for goroutine to close w.wg.Wait() return nil } // Run the watcher, returns two channels for errors and events (paths changed). // //nolint:gocyclo func (w *Watcher) Run() (<-chan string, <-chan error) { errCh := make(chan error, 1) eventCh := make(chan string, 128) w.wg.Add(1) var buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events go func() { defer w.wg.Done() for { n, err := w.inotifyFile.Read(buf[:]) switch { case errors.Is(err, os.ErrClosed): return case err != nil: errCh <- err return } if n < unix.SizeofInotifyEvent { errCh <- errors.New("short read from inotify") return } var offset uint32 // We don't know how many events we just read into the buffer // While the offset points to at least one whole event... for offset <= uint32(n-unix.SizeofInotifyEvent) { var ( // Point "raw" to the event in the buffer raw = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) mask = raw.Mask nameLen = raw.Len ) if mask&unix.IN_Q_OVERFLOW != 0 { errCh <- errors.New("inotify queue overflow") return } // If the event happened to the watched directory or the watched file, the kernel // doesn't append the filename to the event, but we would like to always fill the // the "Name" field with a valid filename. We retrieve the path of the watch from // the "paths" map. watch := w.watches.byWd(uint32(raw.Wd)) // inotify will automatically remove the watch on deletes; just need // to clean our state here. if watch != nil && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { w.watches.remove(watch.wd) } var name string if watch != nil { name = watch.path } if nameLen > 0 { // Point "bytes" at the first byte of the filename bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] // The filename is padded with NULL bytes. TrimRight() gets rid of those. name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") } // Send the events that are not ignored on the events channel if mask&unix.IN_IGNORED == 0 && mask&^uint32(unix.IN_DELETE_SELF) != 0 { eventCh <- name } // Move to the next event in the buffer offset += unix.SizeofInotifyEvent + nameLen } } }() return eventCh, errCh } // Add a watch to the inotify watcher. func (w *Watcher) Add(name string, flags uint32) error { flags |= unix.IN_DELETE_SELF return w.watches.updatePath(name, func(existing *watch) (*watch, error) { if existing != nil { flags |= existing.flags | unix.IN_MASK_ADD } wd, err := unix.InotifyAddWatch(w.fd, name, flags) if wd == -1 { return nil, err } if existing == nil { return &watch{ wd: uint32(wd), path: name, flags: flags, }, nil } existing.wd = uint32(wd) existing.flags = flags return existing, nil }) } // Remove a watch from the inotify watcher. func (w *Watcher) Remove(name string) error { wd, ok := w.watches.removePath(name) if !ok { return nil } success, errno := unix.InotifyRmWatch(w.fd, wd) if success == -1 { // TODO: Perhaps it's not helpful to return an error here in every case; // The only two possible errors are: // // - EBADF, which happens when w.fd is not a valid file descriptor // of any kind. // - EINVAL, which is when fd is not an inotify descriptor or wd // is not a valid watch descriptor. Watch descriptors are // invalidated when they are removed explicitly or implicitly; // explicitly by inotify_rm_watch, implicitly when the file they // are watching is deleted. return errno } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/inotify/inotify_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package inotify_test import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/inotify" ) func assertEvent(t *testing.T, watchCh <-chan string, errCh <-chan error, expected string) { t.Helper() select { case path := <-watchCh: require.Equal(t, expected, path) case err := <-errCh: require.FailNow(t, "unexpected error", "%s", err) case <-time.After(time.Second): require.FailNow(t, "timeout") } } func assertNoEvent(t *testing.T, watchCh <-chan string, errCh <-chan error) { t.Helper() select { case path := <-watchCh: require.FailNow(t, "unexpected path", "%s", path) case err := <-errCh: require.FailNow(t, "unexpected error", "%s", err) case <-time.After(100 * time.Millisecond): } } func TestWatcherCloseWrite(t *testing.T) { watcher, err := inotify.NewWatcher() require.NoError(t, err) d := t.TempDir() require.NoError(t, os.WriteFile(filepath.Join(d, "file1"), []byte("test1"), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(d, "file2"), []byte("test2"), 0o644)) require.NoError(t, watcher.Add(filepath.Join(d, "file1"), unix.IN_CLOSE_WRITE)) watchCh, errCh := watcher.Run() require.NoError(t, watcher.Add(filepath.Join(d, "file2"), unix.IN_CLOSE_WRITE)) assertNoEvent(t, watchCh, errCh) // open file1 for writing, should get inotify event f1, err := os.OpenFile(filepath.Join(d, "file1"), os.O_WRONLY, 0) require.NoError(t, err) require.NoError(t, f1.Close()) assertEvent(t, watchCh, errCh, filepath.Join(d, "file1")) // open file2 for reading, should not get inotify event f2, err := os.OpenFile(filepath.Join(d, "file2"), os.O_RDONLY, 0) require.NoError(t, err) require.NoError(t, f2.Close()) assertNoEvent(t, watchCh, errCh) // remove file2 require.NoError(t, os.Remove(filepath.Join(d, "file2"))) assertNoEvent(t, watchCh, errCh) require.NoError(t, watcher.Remove(filepath.Join(d, "file2"))) require.NoError(t, watcher.Close()) } func TestWatcherDirectory(t *testing.T) { watcher, err := inotify.NewWatcher() require.NoError(t, err) d := t.TempDir() require.NoError(t, os.Mkdir(filepath.Join(d, "dir1"), 0o755)) require.NoError(t, os.Symlink("a1", filepath.Join(d, "dir1", "link1"))) require.NoError(t, os.Symlink("a2", filepath.Join(d, "dir1", "link2"))) require.NoError(t, watcher.Add(d, unix.IN_CREATE|unix.IN_DELETE|unix.IN_MOVE)) require.NoError(t, watcher.Add(filepath.Join(d, "dir1"), unix.IN_CREATE|unix.IN_DELETE|unix.IN_MOVE)) watchCh, errCh := watcher.Run() assertNoEvent(t, watchCh, errCh) require.NoError(t, os.Remove(filepath.Join(d, "dir1", "link1"))) assertEvent(t, watchCh, errCh, filepath.Join(d, "dir1", "link1")) require.NoError(t, os.Mkdir(filepath.Join(d, "dir2"), 0o755)) assertEvent(t, watchCh, errCh, filepath.Join(d, "dir2")) require.NoError(t, os.Symlink("a3", filepath.Join(d, "dir1", "#.link3"))) assertEvent(t, watchCh, errCh, filepath.Join(d, "dir1", "#.link3")) require.NoError(t, os.Rename(filepath.Join(d, "dir1", "#.link3"), filepath.Join(d, "dir1", "link3"))) assertEvent(t, watchCh, errCh, filepath.Join(d, "dir1", "#.link3")) assertEvent(t, watchCh, errCh, filepath.Join(d, "dir1", "link3")) // no more events assertNoEvent(t, watchCh, errCh) require.NoError(t, watcher.Remove(d)) require.NoError(t, watcher.Close()) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/kobject/kobject.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kobject implements Linux kernel kobject uvent watcher. package kobject import ( "fmt" "sync" "github.com/mdlayher/kobject" "go.uber.org/zap" ) const readBufferSize = 64 * 1024 * 1024 // Event is exported. type Event = kobject.Event // Re-export action constants. const ( ActionAdd = kobject.Add ActionRemove = kobject.Remove ActionChange = kobject.Change ActionMove = kobject.Move ActionOnline = kobject.Online ActionOffline = kobject.Offline ActionBind = kobject.Bind ActionUnbind = kobject.Unbind ) // Watcher is a kobject uvent watcher. type Watcher struct { wg sync.WaitGroup cli *kobject.Client } // NewWatcher creates a new kobject watcher. func NewWatcher() (*Watcher, error) { cli, err := kobject.New() if err != nil { return nil, fmt.Errorf("failed to create kobject client: %w", err) } if err = cli.SetReadBuffer(readBufferSize); err != nil { return nil, err } return &Watcher{ cli: cli, }, nil } // Close the watcher. func (w *Watcher) Close() error { if err := w.cli.Close(); err != nil { return err } w.wg.Wait() return nil } // Run the watcher, returns the channel of events. func (w *Watcher) Run(logger *zap.Logger) <-chan *Event { ch := make(chan *kobject.Event, 128) w.wg.Go(func() { defer close(ch) for { ev, err := w.cli.Receive() if err != nil { if err.Error() != "use of closed file" { // unfortunately not an exported error, just errors.New() logger.Error("failed to receive kobject event", zap.Error(err)) } return } ch <- ev } }) return ch } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/kobject/kobject_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kobject_test import ( "testing" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/kobject" ) func TestWatcher(t *testing.T) { watcher, err := kobject.NewWatcher() require.NoError(t, err) evCh := watcher.Run(zaptest.NewLogger(t)) require.NoError(t, watcher.Close()) // the evCh should be closed for range evCh { //nolint:revive } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/sysblock/sysblock.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package sysblock implements gathering block device information from /sys/block filesystem. package sysblock import ( "bytes" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/mdlayher/kobject" "github.com/siderolabs/gen/xslices" ) // Walk the /sys/block filesystem and gather block device information. // //nolint:gocyclo func Walk(root string) ([]*kobject.Event, error) { entries, err := os.ReadDir(root) if err != nil { return nil, fmt.Errorf("failed to read %q: %w", root, err) } result := make([]*kobject.Event, 0, len(entries)) for _, entry := range entries { fi, err := entry.Info() if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return nil, fmt.Errorf("failed to stat %s: %w", entry.Name(), err) } if fi.Mode()&os.ModeSymlink == 0 { continue } path, err := filepath.EvalSymlinks(filepath.Join(root, entry.Name())) if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return nil, fmt.Errorf("failed to resolve symlink %s: %w", entry.Name(), err) } uevent, err := readUevent(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return nil, err } result = append(result, &kobject.Event{ Action: kobject.Add, DevicePath: path, Subsystem: "block", Values: uevent, }) partitions, err := readPartitions(path) if err != nil { return nil, err } result = append(result, partitions...) } return result, nil } // readUevent reads the /sys/block//uevent file and returns the content. func readUevent(path string) (map[string]string, error) { path = filepath.Join(path, "uevent") content, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read %q: %w", path, err) } result := map[string]string{} for kv := range bytes.SplitSeq(content, []byte("\n")) { key, value, ok := bytes.Cut(kv, []byte("=")) if !ok { continue } result[string(key)] = string(value) } return result, nil } // readPartitions reads partitions for a given device and returns the list of events. func readPartitions(path string) ([]*kobject.Event, error) { entries, err := os.ReadDir(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, fmt.Errorf("failed to read %s: %w", path, err) } var result []*kobject.Event //nolint:prealloc for _, entry := range entries { if !entry.IsDir() { continue } partitionPath := filepath.Join(path, entry.Name()) _, err = os.Stat(filepath.Join(partitionPath, "partition")) if err != nil { continue } uevent, err := readUevent(partitionPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return nil, err } result = append(result, &kobject.Event{ Action: kobject.Add, DevicePath: partitionPath, Subsystem: "block", Values: uevent, }) } return result, nil } // ReadSecondaries reads secondary devices for a given device and returns the list. func ReadSecondaries(devPath string) []string { entries, err := os.ReadDir(filepath.Join(devPath, "slaves")) if err != nil { return nil } return xslices.Map(entries, func(entry os.DirEntry) string { return entry.Name() }) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/sysblock/sysblock_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sysblock_test import ( "testing" "github.com/mdlayher/kobject" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/sysblock" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestWalk(t *testing.T) { events, err := sysblock.Walk("/sys/block") require.NoError(t, err) require.NotEmpty(t, events) // there should be at least a single blockdevice and a partition partitions, disks := 0, 0 for _, event := range events { require.Equal(t, "block", event.Subsystem) require.EqualValues(t, kobject.Add, event.Action) require.NotEmpty(t, event.DevicePath) require.NotEmpty(t, event.Action) switch event.Values["DEVTYPE"] { case block.DeviceTypePartition: partitions++ case block.DeviceTypeDisk: disks++ } } require.Greater(t, partitions, 0) require.Greater(t, disks, 0) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/close.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "github.com/siderolabs/gen/xerrors" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/encryption" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Close the encrypted volumes. func Close(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { switch volumeContext.Cfg.TypedSpec().Type { case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal: // volume types can be always closed volumeContext.Status.Phase = block.VolumePhaseClosed return nil case block.VolumeTypeDisk, block.VolumeTypePartition: } switch volumeContext.Cfg.TypedSpec().Encryption.Provider { case block.EncryptionProviderNone: // nothing to do volumeContext.Status.Phase = block.VolumePhaseClosed return nil case block.EncryptionProviderLUKS2: encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.EncryptionHelpers) if err != nil { return fmt.Errorf("failed to create encryption handler: %w", err) } return CloseWithHandler(ctx, logger, volumeContext, handler) default: return fmt.Errorf("provider %s not implemented yet", volumeContext.Cfg.TypedSpec().Encryption.Provider) } } // CloseWithHandler closes the encrypted volumes. func CloseWithHandler(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext, handler *encryption.Handler) error { ctx, cancel := context.WithTimeout(ctx, encryptionTimeout) defer cancel() mappedName := handler.Name() + "-" + volumeContext.Cfg.Metadata().ID() if err := handler.Close(ctx, mappedName); err != nil { return xerrors.NewTaggedf[Retryable]("error closing encrypted volume mapped to %q: %w", mappedName, err) } volumeContext.Status.Phase = block.VolumePhaseClosed logger.Info("encrypted volume closed", zap.String("name", mappedName)) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "github.com/siderolabs/go-blockdevice/v2/blkid" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // DiskRejectedReason is the reason why a disk cannot be used for provisioning. type DiskRejectedReason int // Possible reasons why a disk cannot be used for provisioning. const ( GeneralError DiskRejectedReason = iota NotEnoughSpace WrongFormat ) // CheckDiskResult is the result of checking a disk for provisioning. type CheckDiskResult struct { // CanProvision indicates if the disk can be used for provisioning. CanProvision bool // HasGPT indicates if the disk has a GPT partition table. HasGPT bool // DiskSize is the size of the disk. DiskSize uint64 // RejectedReason is the reason why the disk cannot be used for provisioning (if CanProvision is false). RejectedReason DiskRejectedReason } // CheckDiskForProvisioning checks if the disk can be used for provisioning for the given volume configuration. func CheckDiskForProvisioning(logger *zap.Logger, diskPath string, volumeCfg *block.VolumeConfig) CheckDiskResult { info, err := blkid.ProbePath(diskPath) if err != nil { logger.Error("error probing disk", zap.String("disk", diskPath), zap.Error(err)) return CheckDiskResult{} } switch volumeCfg.TypedSpec().Type { //nolint:exhaustive case block.VolumeTypeDisk: return CheckDiskResult{ CanProvision: info.Name == "", DiskSize: info.Size, RejectedReason: WrongFormat, } case block.VolumeTypePartition: if info.Name == "" { // if the disk is not partitioned, it can be used for partitioning, but we need to check the size overhead := uint64(info.SectorSize) * 67 // GPT + MBR return CheckDiskResult{ CanProvision: info.Size >= volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize+overhead, DiskSize: info.Size, RejectedReason: NotEnoughSpace, } } if info.Name != "gpt" { // not empty, and not gpt => can't be used for partitioning return CheckDiskResult{ RejectedReason: WrongFormat, } } default: panic("unexpected volume type") } // the rest for partition type volumes with existing GPT partition table // find the amount of space available dev, err := blockdev.NewFromPath(diskPath) if err != nil { logger.Error("error opening disk", zap.String("disk", diskPath), zap.Error(err)) return CheckDiskResult{} } defer dev.Close() //nolint:errcheck if err = dev.TryLock(false); err != nil { logger.Error("error locking disk", zap.String("disk", diskPath), zap.Error(err)) return CheckDiskResult{} } defer dev.Unlock() //nolint:errcheck gptdev, err := gpt.DeviceFromBlockDevice(dev) if err != nil { logger.Error("error getting GPT device", zap.String("disk", diskPath), zap.Error(err)) return CheckDiskResult{} } pt, err := gpt.Read(gptdev) if err != nil { logger.Error("error reading GPT", zap.String("disk", diskPath), zap.Error(err)) return CheckDiskResult{} } available := pt.LargestContiguousAllocatable() logger.Debug("checking disk for provisioning", zap.String("disk", diskPath), zap.Uint64("available", available)) return CheckDiskResult{ CanProvision: available >= volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize, HasGPT: true, DiskSize: info.Size, RejectedReason: NotEnoughSpace, } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/disk_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestCheckDiskForProvisioning(t *testing.T) { checkRequirements(t) for _, test := range []struct { name string diskSetup func(t *testing.T) string volumeConfig block.VolumeConfigSpec expected volumes.CheckDiskResult }{ { name: "small empty disk", diskSetup: func(t *testing.T) string { return prepareRawImage(t, 1<<20) }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, }, }, }, expected: volumes.CheckDiskResult{ DiskSize: 1 << 20, }, }, { name: "big enough empty disk", diskSetup: func(t *testing.T) string { return prepareRawImage(t, 1<<20) }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 18, }, }, }, expected: volumes.CheckDiskResult{ CanProvision: true, DiskSize: 1 << 20, }, }, { name: "big enough formatted disk", diskSetup: func(t *testing.T) string { disk := prepareRawImage(t, 1<<21) formatExt4(t, disk) return disk }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 18, }, }, }, expected: volumes.CheckDiskResult{ CanProvision: false, }, }, { name: "big enough empty GPT disk", diskSetup: func(t *testing.T) string { disk := prepareRawImage(t, 1<<24) prepareGPT(t, disk) return disk }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, }, }, }, expected: volumes.CheckDiskResult{ CanProvision: true, HasGPT: true, DiskSize: 1 << 24, }, }, } { t.Run(test.name, func(t *testing.T) { diskPath := test.diskSetup(t) logger := zaptest.NewLogger(t) volumeCfg := block.NewVolumeConfig(block.NamespaceName, "TEST") *volumeCfg.TypedSpec() = test.volumeConfig assert.Equal(t, test.expected, volumes.CheckDiskForProvisioning(logger, diskPath, volumeCfg)) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/encrypt.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "path/filepath" "slices" "time" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/blkid" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/encryption" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // HandleEncryption makes sure the encryption for the volumes is handled appropriately. func HandleEncryption(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { switch volumeContext.Cfg.TypedSpec().Encryption.Provider { case block.EncryptionProviderNone: // nothing to do volumeContext.Status.Phase = block.VolumePhasePrepared volumeContext.Status.MountLocation = volumeContext.Status.Location volumeContext.Status.EncryptionProvider = block.EncryptionProviderNone volumeContext.Status.EncryptionFailedSyncs = nil volumeContext.Status.ConfiguredEncryptionKeys = nil return nil case block.EncryptionProviderLUKS2: encryptionConfig := volumeContext.Cfg.TypedSpec().Encryption handler, err := encryption.NewHandler(encryptionConfig, volumeContext.Cfg.Metadata().ID(), volumeContext.EncryptionHelpers) if err != nil { return fmt.Errorf("failed to create encryption handler: %w", err) } return HandleEncryptionWithHandler(ctx, logger, volumeContext, handler) default: return fmt.Errorf("provider %s not implemented yet", volumeContext.Cfg.TypedSpec().Encryption.Provider) } } const encryptionTimeout = time.Minute // HandleEncryptionWithHandler makes sure the encryption for the volumes is handled appropriately. // //nolint:gocyclo func HandleEncryptionWithHandler(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext, handler *encryption.Handler) error { ctx, cancel := context.WithTimeout(ctx, encryptionTimeout) defer cancel() // lock either the parent device or the device itself devPath := volumeContext.Status.ParentLocation if devPath == "" { devPath = volumeContext.Status.Location } dev, err := blockdev.NewFromPath(devPath, blockdev.OpenForWrite()) if err != nil { return xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) } defer dev.Close() //nolint:errcheck if err = dev.RetryLockWithTimeout(ctx, true, 10*time.Second); err != nil { return xerrors.NewTaggedf[Retryable]("error locking disk: %w", err) } defer dev.Unlock() //nolint:errcheck info, err := blkid.ProbePath(volumeContext.Status.Location, blkid.WithSkipLocking(true)) if err != nil { return xerrors.NewTaggedf[Retryable]("error probing disk: %w", err) } switch info.Name { case "": // no filesystem, encrypt logger.Info("formatting and encrypting volume") if err = handler.FormatAndEncrypt(ctx, logger, volumeContext.Status.Location); err != nil { return xerrors.NewTaggedf[Retryable]("error formatting and encrypting volume: %w", err) } case "luks": // already encrypted default: // mismatch return fmt.Errorf("block dev type mismatch: %s != %s", info.Name, "luks") } logger.Info("opening encrypted volume") mappedName := handler.Name() + "-" + volumeContext.Cfg.Metadata().ID() mappedPath, usedSlot, failedSyncs, err := handler.Open(ctx, logger, volumeContext.Status.Location, mappedName) if err != nil { return xerrors.NewTaggedf[Retryable]("error opening encrypted volume: %w", err) } resolvedPath, err := filepath.EvalSymlinks(mappedPath) if err != nil { return fmt.Errorf("error resolving symlink: %w", err) } volumeContext.Status.Phase = block.VolumePhasePrepared volumeContext.Status.MountLocation = resolvedPath volumeContext.Status.EncryptionProvider = volumeContext.Cfg.TypedSpec().Encryption.Provider volumeContext.Status.EncryptionFailedSyncs = failedSyncs volumeContext.Status.ConfiguredEncryptionKeys = nil volumeContext.Status.EncryptionSlot = &usedSlot for _, key := range volumeContext.Cfg.TypedSpec().Encryption.Keys { provider := key.Type.String() if key.Slot == usedSlot { volumeContext.Status.EncryptionLockedToState = key.LockToSTATE if provider == "tpm" { volumeContext.Status.TPMEncryptionOptions = block.TPMEncryptionOptionsInfo{ PCRs: key.TPMPCRs, PubKeyPCRs: key.TPMPubKeyPCRs, } } } if slices.Index(volumeContext.Status.ConfiguredEncryptionKeys, provider) == -1 { volumeContext.Status.ConfiguredEncryptionKeys = append(volumeContext.Status.ConfiguredEncryptionKeys, provider) } } slices.Sort(volumeContext.Status.ConfiguredEncryptionKeys) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/encryption_meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "encoding/json" "fmt" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/pkg/machinery/config/config" blocktype "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // MarshalEncryptionMeta is a function to persist encryption config to the META value. func MarshalEncryptionMeta(cfg config.EncryptionConfig) ([]byte, error) { return json.Marshal(cfg) } // UnmarshalEncryptionMeta is a function to load encryption config from the META value. func UnmarshalEncryptionMeta(data []byte) (config.EncryptionConfig, error) { var encryptionFromMeta blocktype.EncryptionSpec if err := json.Unmarshal(data, &encryptionFromMeta); err != nil { var legacyEncryption v1alpha1.EncryptionConfig if legacyErr := json.Unmarshal(data, &legacyEncryption); legacyErr != nil { return nil, fmt.Errorf("error unmarshalling state encryption meta key: %w", err) } return &legacyEncryption, nil } return &encryptionFromMeta, nil } // ConvertEncryptionConfiguration converts a `config.EncryptionConfig` into a // `block.EncryptionSpec`, and writes it into `out`. func ConvertEncryptionConfiguration(in config.EncryptionConfig, out *block.VolumeConfigSpec) error { return blockadapter.VolumeConfigSpec(out).ApplyEncryptionConfig(in) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/encryption_meta_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/config/config" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func legacyEncryptionConfig() config.EncryptionConfig { return &v1alpha1.EncryptionConfig{ EncryptionCipher: "aes-xts-plain64", EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeyStatic: &v1alpha1.EncryptionKeyStatic{ KeyData: "secret", }, }, { KeyNodeID: &v1alpha1.EncryptionKeyNodeID{}, }, }, } } func modernEncryptionConfig() config.EncryptionConfig { return blockcfg.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionCipher: "aes-xts-plain64", EncryptionKeys: []blockcfg.EncryptionKey{ { KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "secret", }, }, { KeyNodeID: &blockcfg.EncryptionKeyNodeID{}, }, }, } } func assertEqualEncryptionConfigs(t *testing.T, a, b config.EncryptionConfig) { t.Helper() require.NotNil(t, a) require.NotNil(t, b) assert.Equal(t, a.Provider(), b.Provider()) assert.Equal(t, a.Cipher(), b.Cipher()) assert.Equal(t, a.KeySize(), b.KeySize()) assert.Equal(t, a.BlockSize(), b.BlockSize()) assert.ElementsMatch(t, a.Options(), b.Options()) require.Equal(t, len(a.Keys()), len(b.Keys())) for i := range a.Keys() { assert.Equal(t, a.Keys()[i].Slot(), b.Keys()[i].Slot()) assert.Equal(t, a.Keys()[i].Static(), b.Keys()[i].Static()) assert.Equal(t, a.Keys()[i].NodeID(), b.Keys()[i].NodeID()) assert.Equal(t, a.Keys()[i].KMS(), b.Keys()[i].KMS()) assert.Equal(t, a.Keys()[i].TPM(), b.Keys()[i].TPM()) assert.Equal(t, a.Keys()[i].LockToSTATE(), b.Keys()[i].LockToSTATE()) } } //nolint:lll const ( legacyMarshalled = `{"EncryptionProvider":"","EncryptionKeys":[{"KeyStatic":{"KeyData":"secret"},"KeyNodeID":null,"KeyKMS":null,"KeySlot":0,"KeyTPM":null},{"KeyStatic":null,"KeyNodeID":{},"KeyKMS":null,"KeySlot":0,"KeyTPM":null}],"EncryptionCipher":"aes-xts-plain64","EncryptionKeySize":0,"EncryptionBlockSize":0,"EncryptionPerfOptions":null}` modernMarshalled = `{"EncryptionProvider":"luks2","EncryptionKeys":[{"KeySlot":0,"KeyStatic":{"KeyData":"secret"},"KeyNodeID":null,"KeyKMS":null,"KeyTPM":null,"KeyLockToSTATE":null},{"KeySlot":0,"KeyStatic":null,"KeyNodeID":{},"KeyKMS":null,"KeyTPM":null,"KeyLockToSTATE":null}],"EncryptionCipher":"aes-xts-plain64","EncryptionKeySize":0,"EncryptionBlockSize":0,"EncryptionPerfOptions":null}` ) func TestMarshalEncryptionMeta(t *testing.T) { t.Parallel() data, err := volumes.MarshalEncryptionMeta(legacyEncryptionConfig()) require.NoError(t, err) assert.Equal(t, legacyMarshalled, string(data)) data, err = volumes.MarshalEncryptionMeta(modernEncryptionConfig()) require.NoError(t, err) assert.Equal(t, modernMarshalled, string(data)) } func TestUnmarshalEncryptionMeta(t *testing.T) { t.Parallel() cfg, err := volumes.UnmarshalEncryptionMeta([]byte(legacyMarshalled)) require.NoError(t, err) assertEqualEncryptionConfigs(t, cfg, legacyEncryptionConfig()) cfg, err = volumes.UnmarshalEncryptionMeta([]byte(modernMarshalled)) require.NoError(t, err) assertEqualEncryptionConfigs(t, cfg, modernEncryptionConfig()) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/format.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "os" "time" "github.com/google/uuid" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/blkid" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/swap" "go.uber.org/zap" mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/makefs" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) // Format establishes a filesystem on a block device. // //nolint:gocyclo,cyclop func Format(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { // lock either the parent device or the device itself devPath := volumeContext.Status.ParentLocation if devPath == "" { devPath = volumeContext.Status.Location } dev, err := blockdev.NewFromPath(devPath) if err != nil { return xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) } defer dev.Close() //nolint:errcheck if err = dev.RetryLockWithTimeout(ctx, true, 10*time.Second); err != nil { return xerrors.NewTaggedf[Retryable]("error locking disk: %w", err) } defer dev.Unlock() //nolint:errcheck info, err := blkid.ProbePath(volumeContext.Status.MountLocation, blkid.WithSkipLocking(true)) if err != nil { return xerrors.NewTaggedf[Retryable]("error probing disk: %w", err) } switch { case volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type == block.FilesystemTypeNone: volumeContext.Status.Filesystem, _ = block.FilesystemTypeString(info.Name) //nolint:errcheck // this is mountable if volumeContext.Cfg.TypedSpec().Mount.TargetPath != "" { switch info.Name { case "": return fmt.Errorf("filesystem not found on %s", volumeContext.Status.MountLocation) case "luks": // this volume is actually encrypted, but we got here without encryption config, move phase back volumeContext.Status.Phase = block.VolumePhaseProvisioned return fmt.Errorf("volume is encrypted, but no encryption config provided") } } volumeContext.Status.Phase = block.VolumePhaseReady return nil case info.Name == "": // no filesystem, format case info.Name == "swap": // swap volume, format always case info.Name == volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type.String(): // filesystem already exists and matches the requested type if volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.Grow { // if the partition is set to grow, we need to grow the filesystem if err = GrowFilesystem(ctx, logger, volumeContext); err != nil { return fmt.Errorf("error growing filesystem: %w", err) } } volumeContext.Status.Phase = block.VolumePhaseReady volumeContext.Status.Filesystem = volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type return nil default: // mismatch return fmt.Errorf("filesystem type mismatch: %s != %s", info.Name, volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type) } logger.Info("formatting filesystem", zap.String("device", volumeContext.Status.MountLocation), zap.Stringer("filesystem", volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type), ) switch volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type { //nolint:exhaustive case block.FilesystemTypeXFS: var makefsOptions []makefs.Option // xfs doesn't support by default filesystems < 300 MiB if volumeContext.Status.Size <= 300*1024*1024 { makefsOptions = append(makefsOptions, makefs.WithUnsupportedFSOption(true)) } if volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Label != "" { makefsOptions = append(makefsOptions, makefs.WithLabel(volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Label)) } makefsOptions = append(makefsOptions, makefs.WithConfigFile(quirks.New("").XFSMkfsConfig())) if err = makefs.XFS(ctx, volumeContext.Status.MountLocation, makefsOptions...); err != nil { return xerrors.NewTaggedf[Retryable]("error formatting XFS: %w", err) } case block.FilesystemTypeEXT4: var makefsOptions []makefs.Option if volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Label != "" { makefsOptions = append(makefsOptions, makefs.WithLabel(volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Label)) } if err = makefs.Ext4(ctx, volumeContext.Status.MountLocation, makefsOptions...); err != nil { return xerrors.NewTaggedf[Retryable]("error formatting ext4: %w", err) } case block.FilesystemTypeSwap: if err = swap.Format(volumeContext.Status.MountLocation, swap.FormatOptions{ Label: volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Label, UUID: uuid.New(), }); err != nil { return xerrors.NewTaggedf[Retryable]("error formatting swap: %w", err) } default: return fmt.Errorf("unsupported filesystem type: %s", volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type) } volumeContext.Status.Phase = block.VolumePhaseReady volumeContext.Status.Filesystem = volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type return nil } // GrowFilesystem grows the filesystem on the block device. func GrowFilesystem(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { switch volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type { //nolint:exhaustive case block.FilesystemTypeXFS: // XFS requires partition to be mounted to grow tmpDir, err := os.MkdirTemp("", "talos-growfs-") if err != nil { return fmt.Errorf("error creating temporary directory: %w", err) } defer os.Remove(tmpDir) //nolint:errcheck manager := mountv3.NewManager( mountv3.WithPrinter(logger.Sugar().Infof), mountv3.WithTarget(tmpDir), mountv3.WithFsopen( volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type.String(), fsopen.WithSource(volumeContext.Status.MountLocation), ), ) if _, err := manager.Mount(); err != nil { return fmt.Errorf("error mounting partition: %w", err) } defer manager.Unmount() //nolint:errcheck logger.Info("growing XFS filesystem", zap.String("device", volumeContext.Status.MountLocation)) if err = makefs.XFSGrow(ctx, tmpDir); err != nil { return fmt.Errorf("error growing XFS: %w", err) } return nil case block.FilesystemTypeEXT4: logger.Info("growing ext4 filesystem", zap.String("device", volumeContext.Status.MountLocation)) if err := makefs.Ext4Resize(ctx, volumeContext.Status.MountLocation); err != nil { return fmt.Errorf("error growing ext4: %w", err) } return nil case block.FilesystemTypeSwap: // swap is always reformatted, so we don't need to grow it return nil default: return fmt.Errorf("unsupported filesystem type to grow: %s", volumeContext.Cfg.TypedSpec().Provisioning.FilesystemSpec.Type) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/grow.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "time" "github.com/siderolabs/gen/xerrors" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Grow grows a volume. // //nolint:gocyclo func Grow(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error { if !(volumeContext.Cfg.TypedSpec().Type == block.VolumeTypePartition && volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.Grow) { // nothing to do volumeContext.Status.Phase = block.VolumePhaseProvisioned return nil } if volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.MaxSize > 0 && volumeContext.Status.Size >= volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.MaxSize { // nowhere to grow volumeContext.Status.Phase = block.VolumePhaseProvisioned return nil } dev, err := blockdev.NewFromPath(volumeContext.Status.ParentLocation, blockdev.OpenForWrite()) if err != nil { return xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) } defer dev.Close() //nolint:errcheck if err = dev.RetryLockWithTimeout(ctx, true, 10*time.Second); err != nil { return xerrors.NewTaggedf[Retryable]("error locking disk: %w", err) } defer dev.Unlock() //nolint:errcheck gptdev, err := gpt.DeviceFromBlockDevice(dev) if err != nil { return fmt.Errorf("error getting GPT device: %w", err) } pt, err := gpt.Read(gptdev) if err != nil { return fmt.Errorf("error initializing GPT: %w", err) } availableGrowth, err := pt.AvailablePartitionGrowth(volumeContext.Status.PartitionIndex - 1) if err != nil { return fmt.Errorf("error getting available partition growth: %w", err) } if availableGrowth <= 1024*1024 { // don't grow by less than 1MiB volumeContext.Status.Phase = block.VolumePhaseProvisioned return nil } if volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.MaxSize > 0 && availableGrowth > volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.MaxSize-volumeContext.Status.Size { availableGrowth = volumeContext.Cfg.TypedSpec().Provisioning.PartitionSpec.MaxSize - volumeContext.Status.Size } logger.Debug("growing partition", zap.String("disk", volumeContext.Status.ParentLocation), zap.Int("partition", volumeContext.Status.PartitionIndex), zap.Uint64("size", availableGrowth)) if err = pt.GrowPartition(volumeContext.Status.PartitionIndex-1, availableGrowth); err != nil { return fmt.Errorf("error growing partition: %w", err) } if err = pt.Write(); err != nil { return fmt.Errorf("error writing GPT: %w", err) } volumeContext.Status.Phase = block.VolumePhaseProvisioned volumeContext.Status.SetSize(volumeContext.Status.Size + availableGrowth) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/grow_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "context" "testing" "time" "github.com/google/uuid" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) //nolint:dupl func TestGrow(t *testing.T) { checkRequirements(t) for _, test := range []struct { name string diskSetup func(t *testing.T) string volumeConfig block.VolumeConfigSpec volumeStatus block.VolumeStatusSpec expectedSize uint64 }{ { name: "grow at the end of the disk", diskSetup: func(t *testing.T) string { diskPath := prepareRawImage(t, 1<<23) prepareGPT(t, diskPath, func(pt *gpt.Table) { _, _, err := pt.AllocatePartition(1<<20, "GROWS", uuid.MustParse("c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) require.NoError(t, err) }, ) return diskPath }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, Grow: true, }, }, }, volumeStatus: block.VolumeStatusSpec{ Size: 1 << 20, PartitionIndex: 1, }, expectedSize: 1<<23 - 2*(1<<20), }, { name: "grow to max size", diskSetup: func(t *testing.T) string { diskPath := prepareRawImage(t, 1<<23) prepareGPT(t, diskPath, func(pt *gpt.Table) { _, _, err := pt.AllocatePartition(1<<20, "GROWS", uuid.MustParse("c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) require.NoError(t, err) }, ) return diskPath }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 22, Grow: true, }, }, }, volumeStatus: block.VolumeStatusSpec{ Size: 1 << 20, PartitionIndex: 1, }, expectedSize: 1 << 22, }, { name: "doesn't grow at max size", diskSetup: func(t *testing.T) string { diskPath := prepareRawImage(t, 1<<23) prepareGPT(t, diskPath, func(pt *gpt.Table) { _, _, err := pt.AllocatePartition(1<<21, "BIG", uuid.MustParse("c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) require.NoError(t, err) }, ) return diskPath }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 21, Grow: true, }, }, }, volumeStatus: block.VolumeStatusSpec{ Size: 1 << 21, PartitionIndex: 1, }, expectedSize: 1 << 21, }, } { t.Run(test.name, func(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) t.Cleanup(cancel) logger := zaptest.NewLogger(t) volumeCfg := block.NewVolumeConfig(block.NamespaceName, "TEST") *volumeCfg.TypedSpec() = test.volumeConfig volumeStatus := test.volumeStatus volumeStatus.ParentLocation = test.diskSetup(t) managerContext := volumes.ManagerContext{ Cfg: volumeCfg, Status: &volumeStatus, } var err error for range 10 { err = volumes.Grow(ctx, logger, managerContext) if err != nil && xerrors.TagIs[volumes.Retryable](err) { // retry various disk locked and other retryable errors time.Sleep(10 * time.Millisecond) continue } break } require.NoError(t, err) assert.Equal(t, block.VolumePhaseProvisioned, volumeStatus.Phase) assert.Equal(t, test.expectedSize, volumeStatus.Size) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/helpers_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "errors" randv2 "math/rand/v2" "os" "os/exec" "path/filepath" "testing" "time" "github.com/freddierice/go-losetup/v2" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" ) func checkRequirements(t *testing.T) { t.Helper() if os.Geteuid() != 0 { t.Skip("test requires root privileges") } if hostname, _ := os.Hostname(); hostname == "buildkitsandbox" { //nolint: errcheck t.Skip("test not supported under buildkit as partition devices are not propagated from /dev") } } func losetupAttachHelper(t *testing.T, rawImage string, readonly bool) losetup.Device { t.Helper() for range 10 { loDev, err := losetup.Attach(rawImage, 0, readonly) if err != nil { if errors.Is(err, unix.EBUSY) { spraySleep := max(randv2.ExpFloat64(), 2.0) t.Logf("retrying after %v seconds", spraySleep) time.Sleep(time.Duration(spraySleep * float64(time.Second))) continue } } require.NoError(t, err) return loDev } t.Fatal("failed to attach loop device") //nolint:revive panic("unreachable") } func prepareRawImage(t *testing.T, size int64) string { t.Helper() tmpDir := t.TempDir() rawImage := filepath.Join(tmpDir, "image.raw") f, err := os.Create(rawImage) require.NoError(t, err) require.NoError(t, f.Truncate(size)) require.NoError(t, f.Close()) loDev := losetupAttachHelper(t, rawImage, false) t.Cleanup(func() { assert.NoError(t, loDev.Detach()) }) return loDev.Path() } func formatExt4(t *testing.T, path string) { t.Helper() cmd := exec.CommandContext(t.Context(), "mkfs.ext4", "-L", "extlabel", path) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr require.NoError(t, cmd.Run()) } func prepareGPT(t *testing.T, path string, funcs ...func(*gpt.Table)) { t.Helper() blkdev, err := block.NewFromPath(path, block.OpenForWrite()) require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, blkdev.Close()) }) gptdev, err := gpt.DeviceFromBlockDevice(blkdev) require.NoError(t, err) pt, err := gpt.New(gptdev) require.NoError(t, err) for _, f := range funcs { f(pt) } require.NoError(t, pt.Write()) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/locate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/partitioning" "go.uber.org/zap" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // LocateAndProvision locates and provisions a volume. func LocateAndProvision(ctx context.Context, logger *zap.Logger, vc ManagerContext) error { // 1. Setup common status fields vc.Status.MountSpec = vc.Cfg.TypedSpec().Mount vc.Status.SymlinkSpec = vc.Cfg.TypedSpec().Symlink // 2. Handle simple types (Tmpfs, Overlay, External, etc.) // If handled, we return early. if done := handleSimpleVolumeTypes(vc); done { return nil } // 3. Validation for Disk/Partition types if value.IsZero(vc.Cfg.TypedSpec().Locator) { return fmt.Errorf("volume locator is not set") } // 4. Attempt to locate an existing volume located, err := locateExistingVolume(vc) if err != nil { return err } if located { return nil } // 5. Handle Waiting State // If not found and devices aren't ready, we must wait. if !vc.DevicesReady { vc.Status.Phase = block.VolumePhaseWaiting return nil } // 6. Provision new volume return provisionNewVolume(ctx, logger, vc) } // handleSimpleVolumeTypes handles non-provisionable types. // Returns true if the volume type was handled. func handleSimpleVolumeTypes(vc ManagerContext) bool { spec := vc.Cfg.TypedSpec() switch spec.Type { case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay: vc.Status.Phase = block.VolumePhaseReady return true case block.VolumeTypeExternal: vc.Status.Phase = block.VolumePhaseReady vc.Status.Filesystem = spec.Provisioning.FilesystemSpec.Type vc.Status.Location = spec.Provisioning.DiskSelector.External vc.Status.MountLocation = spec.Provisioning.DiskSelector.External return true case block.VolumeTypeDisk, block.VolumeTypePartition: fallthrough default: return false } } // locateExistingVolume iterates through discovered volumes or disks to find a match. // // For disk volumes with a DiskMatch locator the function iterates over disks // (each disk is evaluated exactly once) and then picks the best discovered // volume for the matched disk. For all other volume types it iterates over // discovered volumes and returns on the first match. func locateExistingVolume(vc ManagerContext) (bool, error) { spec := vc.Cfg.TypedSpec() switch { case !spec.Locator.DiskMatch.IsZero(): if spec.Type != block.VolumeTypeDisk { return false, fmt.Errorf("DiskMatch locator is only valid for disk volumes") } return locateDiskByDiskMatch(vc) case !spec.Locator.Match.IsZero(): return locateVolumeByMatch(vc) default: return false, fmt.Errorf("no locator expression set for volume") } } // locateDiskByDiskMatch handles VolumeTypeDisk with Locator.DiskMatch. // // It iterates over disks (not discovered volumes), so each physical disk is // evaluated exactly once. If more than one disk matches, an error is returned. // The best discovered volume for the matched disk is then selected, preferring // whole-disk entries over partition entries. // //nolint:gocyclo func locateDiskByDiskMatch(vc ManagerContext) (bool, error) { spec := vc.Cfg.TypedSpec() env := celenv.DiskLocator() var matchedDisks []string for _, diskCtx := range vc.Disks { matches, err := spec.Locator.DiskMatch.EvalBool(env, map[string]any{"disk": diskCtx.Disk}) if err != nil { return false, fmt.Errorf("error evaluating disk locator: %w", err) } if matches { matchedDisks = append(matchedDisks, diskCtx.Disk.DevPath) } } if len(matchedDisks) > 1 { return false, fmt.Errorf("multiple disks matched locator for disk volume; matched disks: %v", matchedDisks) } if len(matchedDisks) == 0 { return false, nil } diskDev := matchedDisks[0] // Find the best discovered volume for this disk, preferring whole-disk // entries (ParentDevPath == "") over partition entries. var matchedVol *blockpb.DiscoveredVolumeSpec for _, dv := range vc.DiscoveredVolumes { if dv.DevPath != diskDev && dv.ParentDevPath != diskDev { continue } if matchedVol == nil || (matchedVol.ParentDevPath != "" && dv.ParentDevPath == "") { matchedVol = dv } } if matchedVol != nil { applyLocatedStatus(vc, matchedVol) return true, nil } return false, nil } // locateVolumeByMatch handles volumes with Locator.Match (a CEL expression // evaluated against each discovered volume). func locateVolumeByMatch(vc ManagerContext) (bool, error) { spec := vc.Cfg.TypedSpec() env := celenv.VolumeLocator() for _, dv := range vc.DiscoveredVolumes { matchContext := map[string]any{"volume": dv} // Resolve the parent disk for CEL context for _, diskCtx := range vc.Disks { if (dv.ParentDevPath != "" && diskCtx.Disk.DevPath == dv.ParentDevPath) || (dv.ParentDevPath == "" && diskCtx.Disk.DevPath == dv.DevPath) { matchContext["disk"] = diskCtx.Disk break } } matches, err := spec.Locator.Match.EvalBool(env, matchContext) if err != nil { return false, fmt.Errorf("error evaluating volume locator: %w", err) } if matches { applyLocatedStatus(vc, dv) return true, nil } } return false, nil } // applyLocatedStatus updates the status when a volume is found. func applyLocatedStatus(vc ManagerContext, vol *blockpb.DiscoveredVolumeSpec) { vc.Status.Phase = block.VolumePhaseLocated vc.Status.Location = vol.DevPath vc.Status.PartitionIndex = int(vol.PartitionIndex) vc.Status.ParentLocation = vol.ParentDevPath vc.Status.UUID = vol.Uuid vc.Status.PartitionUUID = vol.PartitionUuid vc.Status.SetSize(vol.Size) } // provisionNewVolume handles the creation/provisioning of missing volumes. func provisionNewVolume(ctx context.Context, logger *zap.Logger, vc ManagerContext) error { spec := vc.Cfg.TypedSpec() // Pre-checks if value.IsZero(spec.Provisioning) { vc.Status.Phase = block.VolumePhaseMissing return nil } if !vc.PreviousWaveProvisioned { vc.Status.Phase = block.VolumePhaseWaiting return nil } // 1. Find candidate disks candidates, err := findCandidateDisks(vc) if err != nil { return err } logger.Debug("matched disks", zap.Strings("disks", candidates)) // 2. Select the best fit pickedDisk, diskRes, err := selectBestDisk(logger, candidates, vc.Cfg) if err != nil { return err } logger.Debug("picked disk", zap.String("disk", pickedDisk)) // 3. Apply Provisioning (Update status or Create Partition) return applyProvisioning(ctx, logger, vc, pickedDisk, diskRes) } // findCandidateDisks filters available disks based on the selector. func findCandidateDisks(vc ManagerContext) ([]string, error) { var matchedDisks []string spec := vc.Cfg.TypedSpec() for _, diskCtx := range vc.Disks { if diskCtx.Disk.Readonly { continue } matches, err := spec.Provisioning.DiskSelector.Match.EvalBool(celenv.DiskLocator(), diskCtx.ToCELContext()) if err != nil { return nil, fmt.Errorf("error evaluating disk locator: %w", err) } if matches { matchedDisks = append(matchedDisks, diskCtx.Disk.DevPath) } } if len(matchedDisks) == 0 { return nil, fmt.Errorf("no disks matched selector for volume") } if spec.Type == block.VolumeTypeDisk && len(matchedDisks) > 1 { return nil, fmt.Errorf("multiple disks matched locator for disk volume; matched disks: %v", matchedDisks) } return matchedDisks, nil } // selectBestDisk analyzes candidates and picks the one that satisfies constraints. func selectBestDisk(logger *zap.Logger, candidates []string, cfg *block.VolumeConfig) (string, CheckDiskResult, error) { var ( pickedDisk string finalResult CheckDiskResult rejectedReasons = map[DiskRejectedReason]int{} ) for _, disk := range candidates { res := CheckDiskForProvisioning(logger, disk, cfg) if res.CanProvision { pickedDisk = disk finalResult = res break } rejectedReasons[res.RejectedReason]++ } if pickedDisk == "" { err := xerrors.NewTaggedf[Retryable]( "no disks matched for volume (%d matched selector): %d have not enough space, %d have wrong format, %d have other issues", len(candidates), rejectedReasons[NotEnoughSpace], rejectedReasons[WrongFormat], rejectedReasons[GeneralError], ) return "", CheckDiskResult{}, err } return pickedDisk, finalResult, nil } // applyProvisioning performs the final provisioning step. func applyProvisioning(ctx context.Context, logger *zap.Logger, vc ManagerContext, disk string, res CheckDiskResult) error { switch vc.Cfg.TypedSpec().Type { case block.VolumeTypeDisk: vc.Status.Phase = block.VolumePhaseProvisioned vc.Status.Location = disk vc.Status.ParentLocation = "" vc.Status.SetSize(res.DiskSize) case block.VolumeTypePartition: partRes, err := CreatePartition(ctx, logger, disk, vc.Cfg, res.HasGPT) if err != nil { return fmt.Errorf("error creating partition: %w", err) } vc.Status.Phase = block.VolumePhaseProvisioned vc.Status.Location = partitioning.DevName(disk, uint(partRes.PartitionIdx)) vc.Status.PartitionIndex = partRes.PartitionIdx vc.Status.ParentLocation = disk vc.Status.PartitionUUID = partRes.Partition.PartGUID.String() vc.Status.SetSize(partRes.Size) case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal: fallthrough default: panic(fmt.Sprintf("unexpected volume type: %s", vc.Cfg.TypedSpec().Type)) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/locate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "testing" "github.com/google/cel-go/cel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" taloscel "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestLocateAndProvision(t *testing.T) { // Helpers to reduce boilerplate in test table mkCEL := func(expr string, env *cel.Env) taloscel.Expression { return taloscel.MustExpression(taloscel.ParseBooleanExpression(expr, env)) } mkDisk := func(dev string, size uint64, opts ...func(*blockpb.DiskSpec)) volumes.DiskContext { d := &blockpb.DiskSpec{DevPath: dev, Size: size} for _, opt := range opts { opt(d) } return volumes.DiskContext{Disk: d} } mkVol := func(dev string, parent string, size uint64, opts ...func(*blockpb.DiscoveredVolumeSpec)) *blockpb.DiscoveredVolumeSpec { v := &blockpb.DiscoveredVolumeSpec{DevPath: dev, ParentDevPath: parent, Size: size} for _, opt := range opts { opt(v) } return v } // Option helpers withLabel := func(l string) func(*blockpb.DiscoveredVolumeSpec) { return func(v *blockpb.DiscoveredVolumeSpec) { v.PartitionLabel = l } } withName := func(n string) func(*blockpb.DiscoveredVolumeSpec) { return func(v *blockpb.DiscoveredVolumeSpec) { v.Name = n } } withUUID := func(u string) func(*blockpb.DiscoveredVolumeSpec) { return func(v *blockpb.DiscoveredVolumeSpec) { v.Uuid = u } } withSerial := func(s string) func(*blockpb.DiskSpec) { return func(d *blockpb.DiskSpec) { d.Serial = s } } readOnly := func(d *blockpb.DiskSpec) { d.Readonly = true } // Constants const gb = 1 << 30 //nolint:dupl tests := []struct { name string volumeConfig block.VolumeConfigSpec discoveredVolumes []*blockpb.DiscoveredVolumeSpec disks []volumes.DiskContext devicesReady bool prevWaveProvisioned bool expectedPhase block.VolumePhase expectedError string assertStatus func(*testing.T, block.VolumeStatusSpec) }{ // --- Simple Volume Types --- { name: "tmpfs volume is always ready", volumeConfig: block.VolumeConfigSpec{Type: block.VolumeTypeTmpfs}, expectedPhase: block.VolumePhaseReady, }, { name: "external volume is ready with location", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypeExternal, Provisioning: block.ProvisioningSpec{ DiskSelector: block.DiskSelector{External: "/dev/ext0"}, FilesystemSpec: block.FilesystemSpec{Type: block.FilesystemTypeXFS}, }, }, expectedPhase: block.VolumePhaseReady, assertStatus: func(t *testing.T, s block.VolumeStatusSpec) { assert.Equal(t, block.FilesystemTypeXFS, s.Filesystem) assert.Equal(t, "/dev/ext0", s.Location) }, }, // --- Validation --- { name: "partition with zero locator fails", volumeConfig: block.VolumeConfigSpec{Type: block.VolumeTypePartition}, expectedError: "volume locator is not set", }, // --- Locator Logic --- { name: "located via Match expression (Partition)", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{ Match: mkCEL(`volume.partition_label == "STATE"`, celenv.VolumeLocator()), }, }, discoveredVolumes: []*blockpb.DiscoveredVolumeSpec{ mkVol("/dev/sda1", "/dev/sda", 1*gb, withLabel("STATE"), withUUID("uuid-123")), }, disks: []volumes.DiskContext{mkDisk("/dev/sda", 10*gb)}, expectedPhase: block.VolumePhaseLocated, assertStatus: func(t *testing.T, s block.VolumeStatusSpec) { assert.Equal(t, "/dev/sda1", s.Location) assert.Equal(t, "uuid-123", s.UUID) }, }, { name: "located via Match expression (Disk without parent)", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypeDisk, Locator: block.LocatorSpec{ Match: mkCEL(`volume.name == "xfs"`, celenv.VolumeLocator()), }, }, discoveredVolumes: []*blockpb.DiscoveredVolumeSpec{ mkVol("/dev/sdb", "", 5*gb, withName("xfs"), withUUID("uuid-disk")), }, disks: []volumes.DiskContext{mkDisk("/dev/sdb", 5*gb)}, expectedPhase: block.VolumePhaseLocated, assertStatus: func(t *testing.T, s block.VolumeStatusSpec) { assert.Equal(t, "/dev/sdb", s.Location) assert.Equal(t, "", s.ParentLocation) }, }, { name: "partition with DiskMatch locator fails", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{ DiskMatch: mkCEL(`disk.serial == "SERIAL001"`, celenv.DiskLocator()), }, }, discoveredVolumes: []*blockpb.DiscoveredVolumeSpec{ mkVol("/dev/sda1", "/dev/sda", 2*gb, withLabel("DATA")), }, disks: []volumes.DiskContext{ mkDisk("/dev/sda", 10*gb, withSerial("SERIAL001")), }, expectedError: "DiskMatch locator is only valid for disk volumes", }, // --- Waiting / Missing States --- { name: "volume not located, devices NOT ready -> Waiting", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{ Match: mkCEL(`volume.partition_label == "MISSING"`, celenv.VolumeLocator()), }, }, devicesReady: false, expectedPhase: block.VolumePhaseWaiting, }, { name: "volume not located, devices ready, NO provisioning spec -> Missing", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{ Match: mkCEL(`volume.partition_label == "MISSING"`, celenv.VolumeLocator()), }, }, devicesReady: true, expectedPhase: block.VolumePhaseMissing, }, { name: "volume not located, previous wave NOT provisioned -> Waiting", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{ Match: mkCEL(`volume.partition_label == "MISSING"`, celenv.VolumeLocator()), }, Provisioning: block.ProvisioningSpec{ DiskSelector: block.DiskSelector{ Match: mkCEL(`!disk.readonly`, celenv.DiskLocator()), }, }, }, devicesReady: true, prevWaveProvisioned: false, expectedPhase: block.VolumePhaseWaiting, }, // --- Provisioning Logic Errors --- { name: "provisioning: no disks matched selector", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{Match: mkCEL(`false`, celenv.VolumeLocator())}, // Force miss Provisioning: block.ProvisioningSpec{ DiskSelector: block.DiskSelector{ Match: mkCEL(`disk.serial == "NONEXISTENT"`, celenv.DiskLocator()), }, }, }, disks: []volumes.DiskContext{mkDisk("/dev/sda", 10*gb, withSerial("ACTUAL"))}, devicesReady: true, prevWaveProvisioned: true, expectedError: "no disks matched selector for volume", }, { name: "provisioning: match fail due to readonly", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Locator: block.LocatorSpec{Match: mkCEL(`false`, celenv.VolumeLocator())}, Provisioning: block.ProvisioningSpec{ DiskSelector: block.DiskSelector{ Match: mkCEL(`!disk.readonly`, celenv.DiskLocator()), }, }, }, disks: []volumes.DiskContext{mkDisk("/dev/sda", 10*gb, readOnly)}, devicesReady: true, prevWaveProvisioned: true, expectedError: "no disks matched selector for volume", }, { name: "provisioning: multiple disks matched for Disk Volume", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypeDisk, Locator: block.LocatorSpec{Match: mkCEL(`false`, celenv.VolumeLocator())}, Provisioning: block.ProvisioningSpec{ DiskSelector: block.DiskSelector{ Match: mkCEL(`!disk.readonly`, celenv.DiskLocator()), }, }, }, disks: []volumes.DiskContext{ mkDisk("/dev/sda", 10*gb), mkDisk("/dev/sdb", 20*gb), }, devicesReady: true, prevWaveProvisioned: true, expectedError: "multiple disks matched locator for disk volume", }, { name: "disk volume with DiskMatch locates disk device, not partition", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypeDisk, Locator: block.LocatorSpec{ DiskMatch: mkCEL(`disk.serial == "SN100"`, celenv.DiskLocator()), }, }, discoveredVolumes: []*blockpb.DiscoveredVolumeSpec{ // partition entry appears first mkVol("/dev/sda1", "/dev/sda", 1*gb, withLabel("EFI")), // whole-disk entry appears second mkVol("/dev/sda", "", 10*gb), }, disks: []volumes.DiskContext{ mkDisk("/dev/sda", 10*gb, withSerial("SN100")), }, expectedPhase: block.VolumePhaseLocated, assertStatus: func(t *testing.T, s block.VolumeStatusSpec) { assert.Equal(t, "/dev/sda", s.Location, "disk volume should locate the disk device, not a partition") assert.Equal(t, "", s.ParentLocation, "disk volume should have no parent location") }, }, { name: "disk volume with DiskMatch errors on multiple disks during locate", volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypeDisk, Locator: block.LocatorSpec{ DiskMatch: mkCEL(`!disk.readonly`, celenv.DiskLocator()), }, }, discoveredVolumes: []*blockpb.DiscoveredVolumeSpec{ mkVol("/dev/sda", "", 10*gb), mkVol("/dev/sdb1", "/dev/sdb", 4*gb, withLabel("DATA")), }, disks: []volumes.DiskContext{ mkDisk("/dev/sda", 10*gb, withSerial("SN-A")), mkDisk("/dev/sdb", 20*gb, withSerial("SN-B")), }, expectedError: "multiple disks matched locator for disk volume", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { logger := zaptest.NewLogger(t) // Setup Config volumeCfg := block.NewVolumeConfig(block.NamespaceName, "TEST") *volumeCfg.TypedSpec() = test.volumeConfig status := block.VolumeStatusSpec{} // Build Context ctx := volumes.ManagerContext{ Cfg: volumeCfg, Status: &status, DiscoveredVolumes: test.discoveredVolumes, Disks: test.disks, DevicesReady: test.devicesReady, PreviousWaveProvisioned: test.prevWaveProvisioned, } // Execute err := volumes.LocateAndProvision(t.Context(), logger, ctx) // Assertions if test.expectedError != "" { require.Error(t, err) assert.Contains(t, err.Error(), test.expectedError) } else { require.NoError(t, err) assert.Equal(t, test.expectedPhase, status.Phase) } if test.assertStatus != nil { test.assertStatus(t, status) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/partition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes import ( "context" "fmt" "time" "github.com/dustin/go-humanize" "github.com/google/uuid" "github.com/siderolabs/gen/xerrors" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // CreatePartitionResult is the result of creating a partition. type CreatePartitionResult struct { PartitionIdx int Partition gpt.Partition Size uint64 } // CreatePartition creates a partition on a disk. // //nolint:gocyclo func CreatePartition(ctx context.Context, logger *zap.Logger, diskPath string, volumeCfg *block.VolumeConfig, hasPT bool) (CreatePartitionResult, error) { dev, err := blockdev.NewFromPath(diskPath, blockdev.OpenForWrite()) if err != nil { return CreatePartitionResult{}, xerrors.NewTaggedf[Retryable]("error opening disk: %w", err) } defer dev.Close() //nolint:errcheck if err = dev.RetryLockWithTimeout(ctx, true, 10*time.Second); err != nil { return CreatePartitionResult{}, xerrors.NewTaggedf[Retryable]("error locking disk: %w", err) } defer dev.Unlock() //nolint:errcheck gptdev, err := gpt.DeviceFromBlockDevice(dev) if err != nil { return CreatePartitionResult{}, fmt.Errorf("error getting GPT device: %w", err) } var pt *gpt.Table if hasPT { pt, err = gpt.Read(gptdev) } else { pt, err = gpt.New(gptdev) } if err != nil { return CreatePartitionResult{}, fmt.Errorf("error initializing GPT: %w", err) } available := pt.LargestContiguousAllocatable() size := volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize maxSize, err := volumeCfg.TypedSpec().Provisioning.PartitionSpec.ResolveMaxSize(available) if err != nil { return CreatePartitionResult{}, fmt.Errorf("error resolving max size: %w", err) } if available < size { // should never happen return CreatePartitionResult{}, fmt.Errorf("not enough space on disk") } if maxSize == 0 || maxSize >= available { size = available } else { size = maxSize } typeUUID, err := uuid.Parse(volumeCfg.TypedSpec().Provisioning.PartitionSpec.TypeUUID) if err != nil { return CreatePartitionResult{}, fmt.Errorf("error parsing type UUID: %w", err) } partitionIdx, partitionEntry, err := pt.AllocatePartition(size, volumeCfg.TypedSpec().Provisioning.PartitionSpec.Label, typeUUID) if err != nil { return CreatePartitionResult{}, fmt.Errorf("error allocating partition: %w", err) } if err = pt.Write(); err != nil { return CreatePartitionResult{}, fmt.Errorf("error writing GPT: %w", err) } // wipe the newly created partition, as it might contain old data partitionDevName := partitioning.DevName(diskPath, uint(partitionIdx)) partitionDev, err := blockdev.NewFromPath(partitionDevName, blockdev.OpenForWrite()) if err != nil { return CreatePartitionResult{}, xerrors.NewTaggedf[Retryable]("error opening partition: %w", err) } defer partitionDev.Close() //nolint:errcheck if err = partition.WipeWithSignatures(partitionDev, partitionDevName, logger.Sugar().Debugf); err != nil { return CreatePartitionResult{}, xerrors.NewTaggedf[Retryable]("error wiping partition: %w", err) } logger.Info("partition created", zap.String("disk", diskPath), zap.Int("partition", partitionIdx), zap.String("label", volumeCfg.TypedSpec().Provisioning.PartitionSpec.Label), zap.String("size", humanize.IBytes(size)), ) return CreatePartitionResult{ PartitionIdx: partitionIdx, Partition: partitionEntry, Size: size, }, nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/partition_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "context" "testing" "time" "github.com/google/uuid" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestCreatePartition(t *testing.T) { checkRequirements(t) for _, test := range []struct { name string diskSetup func(t *testing.T) string volumeConfig block.VolumeConfigSpec hasPT bool expectedPartitionIdx int expectedSize uint64 }{ { name: "empty disk, fixed partition size", diskSetup: func(t *testing.T) string { return prepareRawImage(t, 1<<22) }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 20, Label: "TEST1", TypeUUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", }, }, }, hasPT: false, expectedPartitionIdx: 1, expectedSize: 1 << 20, }, { name: "empty disk, with max size", diskSetup: func(t *testing.T) string { return prepareRawImage(t, 1<<22) }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 21, Label: "TEST2", TypeUUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", }, }, }, hasPT: false, expectedPartitionIdx: 1, expectedSize: 1 << 21, }, { name: "empty disk, no max size", diskSetup: func(t *testing.T) string { return prepareRawImage(t, 1<<23) }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, Label: "TEST3", TypeUUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93c", }, }, }, hasPT: false, expectedPartitionIdx: 1, expectedSize: 1<<23 - 1<<21, // partition grows to max available size minus GPT overhead/alignment }, { name: "empty GPT, fixed partition size", diskSetup: func(t *testing.T) string { diskPath := prepareRawImage(t, 1<<22) prepareGPT(t, diskPath) return diskPath }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 20, Label: "TEST4", TypeUUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", }, }, }, hasPT: true, expectedPartitionIdx: 1, expectedSize: 1 << 20, }, { name: "non-empty GPT, with max size", diskSetup: func(t *testing.T) string { diskPath := prepareRawImage(t, 1<<23) prepareGPT(t, diskPath, func(pt *gpt.Table) { _, _, err := pt.AllocatePartition(1<<20, "FIXED", uuid.MustParse("c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) require.NoError(t, err) }, ) return diskPath }, volumeConfig: block.VolumeConfigSpec{ Type: block.VolumeTypePartition, Provisioning: block.ProvisioningSpec{ PartitionSpec: block.PartitionSpec{ MinSize: 1 << 20, MaxSize: 1 << 24, Label: "TEST4", TypeUUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b", }, }, }, hasPT: true, expectedPartitionIdx: 2, expectedSize: 1<<23 - 3*(1<<20), // partition grows to max available size minus GPT overhead/alignment }, } { t.Run(test.name, func(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 30*time.Second) defer cancel() diskPath := test.diskSetup(t) logger := zaptest.NewLogger(t) volumeCfg := block.NewVolumeConfig(block.NamespaceName, "TEST") *volumeCfg.TypedSpec() = test.volumeConfig var ( result volumes.CreatePartitionResult err error ) for range 10 { result, err = volumes.CreatePartition(ctx, logger, diskPath, volumeCfg, test.hasPT) if err != nil && xerrors.TagIs[volumes.Retryable](err) { // retry various disk locked and other retryable errors time.Sleep(10 * time.Millisecond) continue } break } require.NoError(t, err) assert.Equal(t, test.expectedPartitionIdx, result.PartitionIdx) assert.Equal(t, test.expectedSize, result.Size) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/system_volumes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package volumeconfig import ( "context" "fmt" "os" "path/filepath" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/internal/pkg/partition" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // GetSystemVolumeTransformers returns the transformers for system volumes. func GetSystemVolumeTransformers(ctx context.Context, encryptionMeta *runtime.MetaKey, inContainer, isAgent bool, ) []volumeConfigTransformer { //nolint:gocyclo metaVolumeTransformer := func(_ configconfig.Config) ([]VolumeResource, error) { return []VolumeResource{ { VolumeID: constants.MetaPartitionLabel, Label: block.SystemVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypePartition). WithLocator(metaMatch()). WriterFunc(), }, }, nil } return []volumeConfigTransformer{ metaVolumeTransformer, GetStateVolumeTransformer(encryptionMeta, inContainer, isAgent), GetEphemeralVolumeTransformer(inContainer), StandardDirectoryVolumesTransformer, GetOverlayVolumesTransformer(inContainer), } } // GetStateVolumeTransformer returns the transformer for the STATE volume. func GetStateVolumeTransformer(encryptionMeta *runtime.MetaKey, inContainer, isAgent bool) volumeConfigTransformer { return func(cfg configconfig.Config) ([]VolumeResource, error) { var volumeConfigurator func(*block.VolumeConfig) error if inContainer { volumeConfigurator = NewBuilder(). WithType(block.VolumeTypeDirectory). WithMount(block.MountSpec{ TargetPath: constants.StateMountPoint, SelinuxLabel: constants.StateSelinuxLabel, FileMode: 0o700, UID: 0, GID: 0, }).WriterFunc() } else { // STATE configuration should be always created, but it depends on the configuration presence if cfg != nil && cfg.Machine() != nil { volumeConfigurator = manageStateConfigPresent(cfg) } else { volumeConfigurator = manageStateNoConfig(encryptionMeta, isAgent) } } return []VolumeResource{ { VolumeID: constants.StatePartitionLabel, Label: block.SystemVolumeLabel, TransformFunc: volumeConfigurator, }, }, nil } } // GetEphemeralVolumeTransformer returns the transformer for the EPHEMERAL volume. func GetEphemeralVolumeTransformer(inContainer bool) volumeConfigTransformer { return func(cfg configconfig.Config) ([]VolumeResource, error) { // skip if no config if cfg == nil || cfg.Machine() == nil { return nil, nil } var volumeConfigurator func(*block.VolumeConfig) error if inContainer { volumeConfigurator = NewBuilder(). WithType(block.VolumeTypeDirectory). WithMount(block.MountSpec{ TargetPath: constants.EphemeralMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, }).WriterFunc() } else { volumeConfigurator = func(vc *block.VolumeConfig) error { extraVolumeConfig, _ := cfg.Volumes().ByName(constants.EphemeralPartitionLabel) return NewBuilder(). WithType(block.VolumeTypePartition). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveSystemDisk, DiskSelector: block.DiskSelector{ Match: extraVolumeConfig.Provisioning().DiskSelector().ValueOr(systemDiskMatch()), }, PartitionSpec: block.PartitionSpec{ MinSize: extraVolumeConfig.Provisioning().MinSize().ValueOr(quirks.New("").PartitionSizes().EphemeralMinSize()), MaxSize: extraVolumeConfig.Provisioning().MaxSize().ValueOrZero(), RelativeMaxSize: extraVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(), NegativeMaxSize: extraVolumeConfig.Provisioning().MaxSizeNegative(), Grow: extraVolumeConfig.Provisioning().Grow().ValueOr(true), Label: constants.EphemeralPartitionLabel, TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeXFS, Label: constants.EphemeralPartitionLabel, }, }). WithMount(block.MountSpec{ TargetPath: constants.EphemeralMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, ProjectQuotaSupport: cfg.Machine().Features().DiskQuotaSupportEnabled(), }). WithLocator(labelVolumeMatch(constants.EphemeralPartitionLabel)). WithFunc(func(vcs *block.VolumeConfigSpec) error { encryptionConfig := extraVolumeConfig.Encryption() if encryptionConfig == nil { // fall back to v1alpha1 encryption config encryptionConfig = cfg.Machine().SystemDiskEncryption().Get(constants.EphemeralPartitionLabel) } if err := volumes.ConvertEncryptionConfiguration( encryptionConfig, vc.TypedSpec(), ); err != nil { return fmt.Errorf("error converting encryption for %s: %w", constants.EphemeralPartitionLabel, err) } return nil }). Apply(vc.TypedSpec()) } } return []VolumeResource{{ VolumeID: constants.EphemeralPartitionLabel, Label: block.SystemVolumeLabel, TransformFunc: volumeConfigurator, }}, nil } } // GetOverlayVolumesTransformer returns the transformer for overlay volumes. func GetOverlayVolumesTransformer(inContainer bool) func(configconfig.Config) ([]VolumeResource, error) { return func(cfg configconfig.Config) ([]VolumeResource, error) { // skip if no config or in container if cfg == nil || cfg.Machine() == nil || inContainer { return nil, nil } var resources []VolumeResource for _, overlay := range constants.Overlays { resources = append(resources, VolumeResource{ VolumeID: overlay.Path, Label: block.SystemVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypeOverlay). WithParentID(constants.EphemeralPartitionLabel). WithMount(block.MountSpec{ TargetPath: overlay.Path, SelinuxLabel: overlay.Label, FileMode: 0o755, UID: 0, GID: 0, }).WriterFunc(), }) } return resources, nil } } func manageStateNoConfig(encryptionMeta *runtime.MetaKey, isAgent bool) func(vc *block.VolumeConfig) error { match := labelVolumeMatchAndNonEmpty(constants.StatePartitionLabel) if isAgent { // mark as missing match = noMatch } return NewBuilder(). WithType(block.VolumeTypePartition). WithMount(block.MountSpec{ TargetPath: constants.StateMountPoint, SelinuxLabel: constants.StateSelinuxLabel, FileMode: 0o700, UID: 0, GID: 0, }).WithLocator(match). WithFunc(func(spec *block.VolumeConfigSpec) error { if encryptionMeta != nil { encryptionFromMeta, err := volumes.UnmarshalEncryptionMeta([]byte(encryptionMeta.TypedSpec().Value)) if err != nil { return err } if err := volumes.ConvertEncryptionConfiguration( encryptionFromMeta, spec, ); err != nil { return fmt.Errorf("error converting encryption for %s: %w", constants.StatePartitionLabel, err) } } else { spec.Encryption = block.EncryptionSpec{} } return nil }). WriterFunc() } func manageStateConfigPresent(cfg configconfig.Config) func(vc *block.VolumeConfig) error { return func(vc *block.VolumeConfig) error { extraVolumeConfig, _ := cfg.Volumes().ByName(constants.StatePartitionLabel) encryptionConfig := extraVolumeConfig.Encryption() if encryptionConfig == nil { // fall back to v1alpha1 encryption config encryptionConfig = cfg.Machine().SystemDiskEncryption().Get(constants.StatePartitionLabel) } return NewBuilder(). WithType(block.VolumeTypePartition). WithMount(block.MountSpec{ TargetPath: constants.StateMountPoint, SelinuxLabel: constants.StateSelinuxLabel, FileMode: 0o700, UID: 0, GID: 0, }). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveSystemDisk, DiskSelector: block.DiskSelector{ Match: systemDiskMatch(), }, PartitionSpec: block.PartitionSpec{ MinSize: quirks.New("").PartitionSizes().StateSize(), MaxSize: quirks.New("").PartitionSizes().StateSize(), Label: constants.StatePartitionLabel, TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeXFS, Label: constants.StatePartitionLabel, }, }). WithLocator(labelVolumeMatch(constants.StatePartitionLabel)). WithConvertEncryptionConfiguration(encryptionConfig). Apply(vc.TypedSpec()) } } var standardVolumeDefinitions = []struct { ID string Path string Mode os.FileMode UID int GID int Recursive bool SELinuxLabel string }{ // /var/log { Path: "/var/log", Mode: 0o755, SELinuxLabel: "system_u:object_r:var_log_t:s0", }, { Path: "/var/log/audit", Mode: 0o700, SELinuxLabel: "system_u:object_r:audit_log_t:s0", }, { Path: constants.KubernetesAuditLogDir, Mode: 0o700, UID: constants.KubernetesAPIServerRunUser, GID: constants.KubernetesAPIServerRunGroup, Recursive: true, SELinuxLabel: "system_u:object_r:kube_log_t:s0", }, { Path: "/var/log/containers", Mode: 0o755, SELinuxLabel: "system_u:object_r:containers_log_t:s0", }, { Path: "/var/log/pods", Mode: 0o755, SELinuxLabel: "system_u:object_r:pods_log_t:s0", }, // /var/lib { Path: "/var/lib", Mode: 0o700, SELinuxLabel: constants.EphemeralSelinuxLabel, }, { ID: constants.EtcdDataVolumeID, Path: constants.EtcdDataPath, SELinuxLabel: constants.EtcdDataSELinuxLabel, Mode: 0o700, UID: constants.EtcdUserID, GID: constants.EtcdUserID, Recursive: true, }, { Path: "/var/lib/containerd", Mode: 0o000, SELinuxLabel: "system_u:object_r:containerd_state_t:s0", }, { Path: "/var/lib/kubelet", Mode: 0o700, SELinuxLabel: "system_u:object_r:kubelet_state_t:s0", }, { Path: "/var/lib/cni", Mode: 0o700, Recursive: true, SELinuxLabel: "system_u:object_r:cni_state_t:s0", }, { Path: "/var/lib/kubelet/seccomp", Mode: 0o700, SELinuxLabel: "system_u:object_r:seccomp_profile_t:s0", }, { Path: constants.SeccompProfilesDirectory, Mode: 0o700, Recursive: true, SELinuxLabel: "system_u:object_r:seccomp_profile_t:s0", }, // /var/mnt { Path: constants.UserVolumeMountPoint, Mode: 0o755, SELinuxLabel: constants.EphemeralSelinuxLabel, }, // /var/run { Path: "/var/run/lock", Mode: 0o755, SELinuxLabel: "system_u:object_r:var_lock_t:s0", }, } // StandardDirectoryVolumesTransformer is the transformer for standard directory volumes, // including the /var/run symlink. func StandardDirectoryVolumesTransformer(cfg configconfig.Config) ([]VolumeResource, error) { // skip if no config if cfg == nil || cfg.Machine() == nil { return nil, nil } resources := []VolumeResource{ // /var/run symlink { VolumeID: "/var/run", Label: block.SystemVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypeSymlink). WithSymlink(block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/run", Force: true, }). WithMount(block.MountSpec{ TargetPath: "/var/run", }). WriterFunc(), }, } parentIDs := map[string]string{ "/var": constants.EphemeralPartitionLabel, "/var/run": "/var/run", } for _, volume := range standardVolumeDefinitions { parentDir := filepath.Dir(volume.Path) targetDir := filepath.Base(volume.Path) parentID, ok := parentIDs[parentDir] if !ok { return nil, fmt.Errorf("unknown parent directory volume %q for %q", parentDir, volume.Path) } volumeID := volume.Path if volume.ID != "" { volumeID = volume.ID } resources = append(resources, VolumeResource{ VolumeID: volumeID, Label: block.SystemVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypeDirectory). WithMount(block.MountSpec{ TargetPath: targetDir, ParentID: parentID, SelinuxLabel: volume.SELinuxLabel, FileMode: volume.Mode, UID: volume.UID, GID: volume.GID, RecursiveRelabel: volume.Recursive, }).WriterFunc(), }) parentIDs[volume.Path] = volumeID } return resources, nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/system_volumes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig_test import ( "context" "encoding/json" "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig" "github.com/siderolabs/talos/pkg/machinery/config/container" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) var baseCfg v1alpha1.Config func init() { u, _ := url.Parse("https://foo:6443") //nolint:errcheck baseCfg = v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{URL: u}, }, }, } } func TestGetSystemVolumeTransformers(t *testing.T) { t.Parallel() ctx := context.Background() encryptionMeta := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.StateEncryptionConfig)) transformers := volumeconfig.GetSystemVolumeTransformers(ctx, encryptionMeta, false, false) require.Len(t, transformers, 5, "should return 5 transformers") var allResources []volumeconfig.VolumeResource //nolint:prealloc // this is a test for _, transformer := range transformers { resources, err := transformer(container.NewV1Alpha1(&baseCfg)) require.NoError(t, err) allResources = append(allResources, resources...) } for _, volumeID := range []string{ constants.MetaPartitionLabel, constants.StatePartitionLabel, constants.EphemeralPartitionLabel, "/var/run", "/var/log", "/etc/cni", } { assert.Condition(t, func() bool { for _, r := range allResources { if r.VolumeID == volumeID { return true } } return false }, "should have volume config resource for %s", volumeID) } } func TestGetStateVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string encryptionMeta *runtime.MetaKey inContainer bool isAgent bool cfg *v1alpha1.Config checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "in container", inContainer: true, isAgent: false, encryptionMeta: nil, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeDirectory, vc.TypedSpec().Type) assert.Equal(t, constants.StateMountPoint, vc.TypedSpec().Mount.TargetPath) }) }, }, { name: "W/ config", inContainer: false, isAgent: false, encryptionMeta: nil, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { //nolint:dupl require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, constants.StateMountPoint, vc.TypedSpec().Mount.TargetPath) assert.NotEmpty(t, vc.TypedSpec().Provisioning) }) }, }, { name: "NO config", inContainer: false, isAgent: false, encryptionMeta: nil, cfg: nil, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, constants.StateMountPoint, vc.TypedSpec().Mount.TargetPath) locator, err := vc.TypedSpec().Locator.Match.MarshalText() require.NoError(t, err) assert.Equal(t, `volume.partition_label == "STATE" && volume.name != ""`, string(locator)) }) }, }, { name: "agent w/ NO config = no match locator", inContainer: false, isAgent: true, encryptionMeta: nil, cfg: nil, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) locator, err := vc.TypedSpec().Locator.Match.MarshalText() require.NoError(t, err) assert.Equal(t, "false", string(locator)) }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() transformer := volumeconfig.GetStateVolumeTransformer(tc.encryptionMeta, tc.inContainer, tc.isAgent) resources, err := transformer(container.NewV1Alpha1(tc.cfg)) require.NoError(t, err) assert.Equal(t, constants.StatePartitionLabel, resources[0].VolumeID) assert.Equal(t, block.SystemVolumeLabel, resources[0].Label) tc.checkFunc(t, resources) }) } } func TestGetEphemeralVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string inContainer bool cfg *v1alpha1.Config checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "NO config", inContainer: false, cfg: nil, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "container W/ config", inContainer: true, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { //nolint:dupl require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeDirectory, vc.TypedSpec().Type) assert.Equal(t, constants.EphemeralMountPoint, vc.TypedSpec().Mount.TargetPath) assert.Empty(t, vc.TypedSpec().Provisioning) }) }, }, { name: "W/ config", inContainer: false, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, constants.EphemeralPartitionLabel, resources[0].VolumeID) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, constants.EphemeralMountPoint, vc.TypedSpec().Mount.TargetPath) assert.NotEmpty(t, vc.TypedSpec().Provisioning) }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() transformer := volumeconfig.GetEphemeralVolumeTransformer(tc.inContainer) resources, err := transformer(container.NewV1Alpha1(tc.cfg)) require.NoError(t, err) tc.checkFunc(t, resources) }) } } func TestStandardDirectoryVolumesTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string cfg *v1alpha1.Config checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "NO config", cfg: nil, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "W/ config", cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1+14) // +1 for /var/run symlink, +14 for standard directories var varRunSymlinkResource *volumeconfig.VolumeResource for i := range resources { if resources[i].VolumeID == "/var/run" { varRunSymlinkResource = &resources[i] break } } require.NotNil(t, varRunSymlinkResource, "should have /var/run symlink resource") assert.Equal(t, block.SystemVolumeLabel, varRunSymlinkResource.Label) testTransformFunc(t, varRunSymlinkResource.TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeSymlink, vc.TypedSpec().Type) assert.Equal(t, "/run", vc.TypedSpec().Symlink.SymlinkTargetPath) }) // Check some standard directories expectedPaths := []string{"/var/log", "/var/lib", constants.EtcdDataVolumeID} for _, expectedPath := range expectedPaths { var found bool for i := range resources { if resources[i].VolumeID == expectedPath { found = true assert.Equal(t, block.SystemVolumeLabel, resources[i].Label) testTransformFunc(t, resources[i].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeDirectory, vc.TypedSpec().Type) }) break } } assert.True(t, found, "should have resource for %s", expectedPath) } }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() resources, err := volumeconfig.StandardDirectoryVolumesTransformer(container.NewV1Alpha1(tc.cfg)) require.NoError(t, err) tc.checkFunc(t, resources) }) } } func TestGetOverlayVolumesTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string inContainer bool cfg *v1alpha1.Config checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "NO config", inContainer: false, cfg: nil, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "in container", inContainer: true, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "W/ config", inContainer: false, cfg: &baseCfg, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, len(constants.Overlays)) for _, resource := range resources { assert.Equal(t, block.SystemVolumeLabel, resource.Label) testTransformFunc(t, resource.TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeOverlay, vc.TypedSpec().Type) assert.Equal(t, constants.EphemeralPartitionLabel, vc.TypedSpec().ParentID) }) } }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() resources, err := volumeconfig.GetOverlayVolumesTransformer(tc.inContainer)(container.NewV1Alpha1(tc.cfg)) require.NoError(t, err) tc.checkFunc(t, resources) }) } } func TestStateVolumeTransformerWithEncryptionMeta(t *testing.T) { t.Parallel() // Create encryption meta encryptionConfig := &v1alpha1.EncryptionConfig{ EncryptionProvider: "luks2", EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeySlot: 1, KeyStatic: &v1alpha1.EncryptionKeyStatic{ KeyData: "test-secret", }, }, }, } encryptionMetaMarshalled, err := json.Marshal(encryptionConfig) require.NoError(t, err) encryptionMeta := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.StateEncryptionConfig)) encryptionMeta.TypedSpec().Value = string(encryptionMetaMarshalled) transformer := volumeconfig.GetStateVolumeTransformer(encryptionMeta, false, false) resources, err := transformer(nil) require.NoError(t, err) require.Len(t, resources, 1) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) // Encryption should be set from meta assert.NotEmpty(t, vc.TypedSpec().Encryption) }) } func TestEphemeralVolumeTransformerWithExtraConfig(t *testing.T) { t.Parallel() ephemeralConfig := blockcfg.NewVolumeConfigV1Alpha1() ephemeralConfig.MetaName = constants.EphemeralPartitionLabel ephemeralConfig.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("10GiB") ephemeralConfig.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("100GiB") cfg, err := container.New( baseCfg.DeepCopy(), ephemeralConfig, ) require.NoError(t, err) transformer := volumeconfig.GetEphemeralVolumeTransformer(false) resources, err := transformer(cfg) require.NoError(t, err) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) require.Len(t, resources, 1) assert.EqualValues(t, 10*1024*1024*1024, vc.TypedSpec().Provisioning.PartitionSpec.MinSize) assert.EqualValues(t, 100*1024*1024*1024, vc.TypedSpec().Provisioning.PartitionSpec.MaxSize) }) } func testTransformFunc(t *testing.T, transformer func(vc *block.VolumeConfig) error, checkFunc func(t *testing.T, vc *block.VolumeConfig, err error), ) { t.Helper() vc := block.NewVolumeConfig(block.NamespaceName, "test") err := transformer(vc) checkFunc(t, vc, err) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/types.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig import ( "fmt" machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumeResource is an internal type containing the information required by // VolumeConfigController to create a VolumeConfig (and optionally // VolumeMountRequest) resource. // // Transformers (System and User) transform the config into VolumeResources, which are // later created by VolumeConfigController. type VolumeResource struct { VolumeID string // ID of the volume to create. Label string // label of the volume to create. TransformFunc func(vc *block.VolumeConfig) error // func that applies the changes to the provided VolumeConfig. MountTransformFunc func(m *block.VolumeMountRequest) error // func that applies the changes to the provided VolumeMountRequest. } type volumeConfigTransformer func(c configconfig.Config) ([]VolumeResource, error) // SkipUserVolumeMountRequest is used to skip creating a VolumeMountRequest for a user volume. type SkipUserVolumeMountRequest struct{} var noMatch = cel.MustExpression(cel.ParseBooleanExpression("false", celenv.Empty())) func labelVolumeMatch(label string) cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("volume.partition_label == '%s'", label), celenv.VolumeLocator())) } func labelVolumeMatchAndNonEmpty(label string) cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("volume.partition_label == '%s' && volume.name != ''", label), celenv.VolumeLocator())) } func metaMatch() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("volume.partition_label == '%s' && volume.name in ['', 'talosmeta'] && volume.size == 1048576u", constants.MetaPartitionLabel), celenv.VolumeLocator())) //nolint:lll } func systemDiskMatch() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression("system_disk", celenv.DiskLocator())) } // MetaProvider wraps acquiring meta. type MetaProvider interface { Meta() machinedruntime.Meta } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/user_volumes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig import ( "cmp" "errors" "fmt" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/talos/internal/pkg/partition" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Size constants. const ( MiB = 1024 * 1024 MinUserVolumeSize = 100 * MiB ) // UserVolumeTransformers contains all the user volume config transformers. var UserVolumeTransformers = []volumeConfigTransformer{ UserVolumeTransformer, RawVolumeTransformer, ExistingVolumeTransformer, ExternalVolumeTransformer, SwapVolumeTransformer, } // UserVolumeTransformer is the transformer for user volume configs. func UserVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) { if c == nil { return nil, nil } resources := make([]VolumeResource, 0, len(c.UserVolumeConfigs())) for _, userVolumeConfig := range c.UserVolumeConfigs() { volumeID := constants.UserVolumePrefix + userVolumeConfig.Name() userVolumeResource := VolumeResource{ VolumeID: volumeID, Label: block.UserVolumeLabel, MountTransformFunc: HandleUserVolumeMountRequest(userVolumeConfig), // This is overridden for Directory type below. } switch userVolumeConfig.Type().ValueOr(block.VolumeTypePartition) { case block.VolumeTypeDirectory: userVolumeResource.MountTransformFunc = DefaultMountTransform userVolumeResource.TransformFunc = NewBuilder(). WithType(block.VolumeTypeDirectory). WithMount(block.MountSpec{ TargetPath: userVolumeConfig.Name(), ParentID: constants.UserVolumeMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, BindTarget: new(userVolumeConfig.Name()), }). WriterFunc() case block.VolumeTypeDisk: userVolumeResource.TransformFunc = NewBuilder(). WithType(block.VolumeTypeDisk). WithDiskLocator(userVolumeConfig.Provisioning().DiskSelector().ValueOr(noMatch)). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ Match: userVolumeConfig.Provisioning().DiskSelector().ValueOr(noMatch), }, PartitionSpec: block.PartitionSpec{ TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: userVolumeConfig.Filesystem().Type(), }, }). WithMount(block.MountSpec{ TargetPath: userVolumeConfig.Name(), ParentID: constants.UserVolumeMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, ProjectQuotaSupport: userVolumeConfig.Filesystem().ProjectQuotaSupport(), }). WithConvertEncryptionConfiguration(userVolumeConfig.Encryption()). WriterFunc() case block.VolumeTypePartition: userVolumeResource.TransformFunc = NewBuilder(). WithType(block.VolumeTypePartition). WithLocator(labelVolumeMatch(volumeID)). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ Match: userVolumeConfig.Provisioning().DiskSelector().ValueOr(noMatch), }, PartitionSpec: block.PartitionSpec{ MinSize: cmp.Or(userVolumeConfig.Provisioning().MinSize().ValueOrZero(), MinUserVolumeSize), MaxSize: userVolumeConfig.Provisioning().MaxSize().ValueOrZero(), RelativeMaxSize: userVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(), NegativeMaxSize: userVolumeConfig.Provisioning().MaxSizeNegative(), Grow: userVolumeConfig.Provisioning().Grow().ValueOrZero(), Label: volumeID, TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: userVolumeConfig.Filesystem().Type(), }, }). WithMount(block.MountSpec{ TargetPath: userVolumeConfig.Name(), ParentID: constants.UserVolumeMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, ProjectQuotaSupport: userVolumeConfig.Filesystem().ProjectQuotaSupport(), }). WithConvertEncryptionConfiguration(userVolumeConfig.Encryption()). WriterFunc() case block.VolumeTypeTmpfs, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal: fallthrough default: return nil, fmt.Errorf("unsupported volume type %q", userVolumeConfig.Type().ValueOr(block.VolumeTypePartition).String()) } resources = append(resources, userVolumeResource) } return resources, nil } // RawVolumeTransformer is the transformer for raw volume configs. func RawVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) { if c == nil { return nil, nil } resources := make([]VolumeResource, 0, len(c.RawVolumeConfigs())) for _, rawVolumeConfig := range c.RawVolumeConfigs() { volumeID := constants.RawVolumePrefix + rawVolumeConfig.Name() resources = append(resources, VolumeResource{ VolumeID: volumeID, Label: block.RawVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypePartition). WithLocator(labelVolumeMatch(volumeID)). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ Match: rawVolumeConfig.Provisioning().DiskSelector().ValueOr(noMatch), }, PartitionSpec: block.PartitionSpec{ MinSize: cmp.Or(rawVolumeConfig.Provisioning().MinSize().ValueOrZero(), MinUserVolumeSize), MaxSize: rawVolumeConfig.Provisioning().MaxSize().ValueOrZero(), RelativeMaxSize: rawVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(), Grow: rawVolumeConfig.Provisioning().Grow().ValueOrZero(), Label: volumeID, TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeNone, }, }). WithConvertEncryptionConfiguration(rawVolumeConfig.Encryption()). WriterFunc(), MountTransformFunc: SkipMountTransform, }) } return resources, nil } // ExistingVolumeTransformer is the transformer for existing user volume configs. func ExistingVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) { if c == nil { return nil, nil } resources := make([]VolumeResource, 0, len(c.ExistingVolumeConfigs())) for _, existingVolumeConfig := range c.ExistingVolumeConfigs() { volumeID := constants.ExistingVolumePrefix + existingVolumeConfig.Name() resources = append(resources, VolumeResource{ VolumeID: volumeID, Label: block.ExistingVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypePartition). WithLocator(existingVolumeConfig.VolumeDiscovery().VolumeSelector()). WithMount(block.MountSpec{ TargetPath: existingVolumeConfig.Name(), ParentID: constants.UserVolumeMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, }). WriterFunc(), MountTransformFunc: HandleExistingVolumeMountRequest(existingVolumeConfig), }) } return resources, nil } // ExternalVolumeTransformer is the transformer for external user volume configs. func ExternalVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) { if c == nil { return nil, nil } resources := make([]VolumeResource, 0, len(c.ExternalVolumeConfigs())) for _, externalVolumeConfig := range c.ExternalVolumeConfigs() { volumeID := constants.ExternalVolumePrefix + externalVolumeConfig.Name() params, err := externalVolumeParameters(externalVolumeConfig) if err != nil { return nil, fmt.Errorf("failed to get external volume parameters for volume %q: %w", externalVolumeConfig.Name(), err) } resources = append(resources, VolumeResource{ VolumeID: volumeID, Label: block.ExternalVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypeExternal). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ External: externalVolumeSource(externalVolumeConfig), }, FilesystemSpec: block.FilesystemSpec{ Type: externalVolumeConfig.Type(), }, }). WithMount(block.MountSpec{ TargetPath: externalVolumeConfig.Name(), ParentID: constants.UserVolumeMountPoint, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, Parameters: params, }). WriterFunc(), MountTransformFunc: HandleExternalVolumeMountRequest(externalVolumeConfig), }) } return resources, nil } func externalVolumeSource(ext configconfig.ExternalVolumeConfig) string { switch ext.Type() { case block.FilesystemTypeVirtiofs: if ext.Mount().Virtiofs().IsPresent() { return ext.Mount().Virtiofs().ValueOrZero().Source() } case block.FilesystemTypeNone, block.FilesystemTypeXFS, block.FilesystemTypeVFAT, block.FilesystemTypeEXT4, block.FilesystemTypeISO9660, block.FilesystemTypeSwap: fallthrough default: return "" } return "" } func externalVolumeParameters(ext configconfig.ExternalVolumeConfig) ([]block.ParameterSpec, error) { switch ext.Type() { case block.FilesystemTypeVirtiofs: if ext.Mount().Virtiofs().IsPresent() { return ext.Mount().Virtiofs().ValueOrZero().Parameters() } return nil, errors.New("virtiofs mount specification is required for Virtiofs external volume") case block.FilesystemTypeNone, block.FilesystemTypeXFS, block.FilesystemTypeVFAT, block.FilesystemTypeEXT4, block.FilesystemTypeISO9660, block.FilesystemTypeSwap: fallthrough default: return nil, fmt.Errorf("unsupported external volume type %q", ext.Type().String()) } } // SwapVolumeTransformer is the transformer for swap volume configs. func SwapVolumeTransformer(c configconfig.Config) ([]VolumeResource, error) { if c == nil { return nil, nil } resources := make([]VolumeResource, 0, len(c.SwapVolumeConfigs())) for _, swapVolumeConfig := range c.SwapVolumeConfigs() { volumeID := constants.SwapVolumePrefix + swapVolumeConfig.Name() resources = append(resources, VolumeResource{ VolumeID: volumeID, Label: block.SwapVolumeLabel, TransformFunc: NewBuilder(). WithType(block.VolumeTypePartition). WithLocator(labelVolumeMatch(volumeID)). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ Match: swapVolumeConfig.Provisioning().DiskSelector().ValueOr(noMatch), }, PartitionSpec: block.PartitionSpec{ MinSize: cmp.Or(swapVolumeConfig.Provisioning().MinSize().ValueOrZero(), MinUserVolumeSize), MaxSize: swapVolumeConfig.Provisioning().MaxSize().ValueOrZero(), RelativeMaxSize: swapVolumeConfig.Provisioning().RelativeMaxSize().ValueOrZero(), NegativeMaxSize: swapVolumeConfig.Provisioning().MaxSizeNegative(), Grow: swapVolumeConfig.Provisioning().Grow().ValueOrZero(), Label: volumeID, TypeUUID: partition.LinkSwap, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeSwap, }, }). WithConvertEncryptionConfiguration(swapVolumeConfig.Encryption()). WriterFunc(), MountTransformFunc: DefaultMountTransform, }) } return resources, nil } // HandleUserVolumeMountRequest returns a MountTransformFunc for user volumes. func HandleUserVolumeMountRequest(userVolumeConfig configconfig.UserVolumeConfig) func(m *block.VolumeMountRequest) error { return func(m *block.VolumeMountRequest) error { m.TypedSpec().DisableAccessTime = userVolumeConfig.Mount().DisableAccessTime() m.TypedSpec().Secure = userVolumeConfig.Mount().Secure() return nil } } // HandleExistingVolumeMountRequest returns a MountTransformFunc for existing volumes. // It sets `VolumeMountRequestSpec.ReadOnly` based on the existing configuration. func HandleExistingVolumeMountRequest(existingVolumeConfig configconfig.ExistingVolumeConfig) func(m *block.VolumeMountRequest) error { return func(m *block.VolumeMountRequest) error { m.TypedSpec().ReadOnly = existingVolumeConfig.Mount().ReadOnly() m.TypedSpec().DisableAccessTime = existingVolumeConfig.Mount().DisableAccessTime() m.TypedSpec().Secure = existingVolumeConfig.Mount().Secure() return nil } } // HandleExternalVolumeMountRequest returns a MountTransformFunc for external volumes. func HandleExternalVolumeMountRequest(externalVolumeConfig configconfig.ExternalVolumeConfig) func(m *block.VolumeMountRequest) error { return func(m *block.VolumeMountRequest) error { m.TypedSpec().ReadOnly = externalVolumeConfig.Mount().ReadOnly() m.TypedSpec().DisableAccessTime = externalVolumeConfig.Mount().DisableAccessTime() m.TypedSpec().Secure = externalVolumeConfig.Mount().Secure() return nil } } // DefaultMountTransform is a no-op. func DefaultMountTransform(_ *block.VolumeMountRequest) error { return nil } // SkipMountTransform is a MountTransformFunc which skips creating a MountRequest. // It returns a tagged error, which is handled by the VolumeConfigController. func SkipMountTransform(_ *block.VolumeMountRequest) error { return xerrors.NewTaggedf[SkipUserVolumeMountRequest]("skip") } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/user_volumes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig_test import ( "io/fs" "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) //nolint:dupl func TestUserVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string cfg []*blockcfg.UserVolumeConfigV1Alpha1 checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource, err error) }{ { name: "no config", checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource, err error) { require.Len(t, resources, 0) require.NoError(t, err) }, }, { name: "partition volume", cfg: []*blockcfg.UserVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "foo", VolumeType: new(block.VolumeTypePartition), FilesystemSpec: blockcfg.FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource, err error) { require.NoError(t, err) require.Len(t, resources, 1) assert.Equal(t, constants.UserVolumePrefix+"foo", resources[0].VolumeID) assert.Equal(t, block.UserVolumeLabel, resources[0].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, block.FilesystemTypeXFS, vc.TypedSpec().Provisioning.FilesystemSpec.Type) assert.Equal(t, "foo", vc.TypedSpec().Mount.TargetPath) assert.Equal(t, constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) assert.Equal(t, fs.FileMode(0o755), vc.TypedSpec().Mount.FileMode) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { // default mount transform is noop require.NoError(t, err) }) }, }, { name: "directory volume", cfg: []*blockcfg.UserVolumeConfigV1Alpha1{{ Meta: meta.Meta{ MetaKind: blockcfg.UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "bar", VolumeType: new(block.VolumeTypeDirectory), FilesystemSpec: blockcfg.FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, }, }}, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource, err error) { require.NoError(t, err) require.Len(t, resources, 1) assert.Equal(t, constants.UserVolumePrefix+"bar", resources[0].VolumeID) assert.Equal(t, block.UserVolumeLabel, resources[0].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeDirectory, vc.TypedSpec().Type) require.Empty(t, vc.TypedSpec().Provisioning) assert.Equal(t, "bar", vc.TypedSpec().Mount.TargetPath) assert.Equal(t, constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) assert.Equal(t, new("bar"), vc.TypedSpec().Mount.BindTarget) assert.Equal(t, fs.FileMode(0o755), vc.TypedSpec().Mount.FileMode) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { // default mount transform is noop require.NoError(t, err) }) }, }, { name: "unsupported volume type", cfg: []*blockcfg.UserVolumeConfigV1Alpha1{{ Meta: meta.Meta{ MetaKind: blockcfg.UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, VolumeType: new(block.VolumeTypeTmpfs), }}, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource, err error) { require.Error(t, err) assert.Equal(t, "unsupported volume type \"tmpfs\"", err.Error()) require.Empty(t, resources) }, }, { name: "multiple configs", cfg: []*blockcfg.UserVolumeConfigV1Alpha1{{ Meta: meta.Meta{ MetaKind: blockcfg.UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "foo", VolumeType: new(block.VolumeTypePartition), FilesystemSpec: blockcfg.FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, }, }, { Meta: meta.Meta{ MetaKind: blockcfg.UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "bar", VolumeType: new(block.VolumeTypeDirectory), FilesystemSpec: blockcfg.FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, }, }}, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource, err error) { require.NoError(t, err) require.Len(t, resources, 2) assert.Equal(t, constants.UserVolumePrefix+"foo", resources[0].VolumeID) assert.Equal(t, block.UserVolumeLabel, resources[0].Label) assert.Equal(t, constants.UserVolumePrefix+"bar", resources[1].VolumeID) assert.Equal(t, block.UserVolumeLabel, resources[1].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, block.FilesystemTypeXFS, vc.TypedSpec().Provisioning.FilesystemSpec.Type) }) testTransformFunc(t, resources[1].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeDirectory, vc.TypedSpec().Type) }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() mergedCfg, err := container.New(xslices.Map(tc.cfg, func(cfg *blockcfg.UserVolumeConfigV1Alpha1) configconfig.Document { return cfg })...) require.NoError(t, err) resources, err := volumeconfig.UserVolumeTransformer(mergedCfg) tc.checkFunc(t, resources, err) }) } } //nolint:dupl func TestRawVolumeTransformer(t *testing.T) { t.Parallel() volumeCfg := &blockcfg.RawVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: blockcfg.RawVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "raw-data", } cfg, err := container.New(volumeCfg) require.NoError(t, err) resources, err := volumeconfig.RawVolumeTransformer(cfg) require.NoError(t, err) assert.Equal(t, block.RawVolumeLabel, resources[0].Label) require.Len(t, resources, 1) assert.Equal(t, constants.RawVolumePrefix+"raw-data", resources[0].VolumeID) assert.Equal(t, block.RawVolumeLabel, resources[0].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, block.WaveUserVolumes, vc.TypedSpec().Provisioning.Wave) assert.Equal(t, block.FilesystemTypeNone, vc.TypedSpec().Provisioning.FilesystemSpec.Type) assert.Equal(t, constants.RawVolumePrefix+"raw-data", vc.TypedSpec().Provisioning.PartitionSpec.Label) assert.Equal(t, partition.LinuxFilesystemData, vc.TypedSpec().Provisioning.PartitionSpec.TypeUUID) assert.Equal(t, block.FilesystemTypeNone, vc.TypedSpec().Provisioning.FilesystemSpec.Type) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { // SkipMountTransform should return an error tagged with SkipUserVolumeMountRequest require.Error(t, err) assert.Equal(t, "skip", err.Error()) }) } //nolint:dupl func TestExistingVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string cfg []*blockcfg.ExistingVolumeConfigV1Alpha1 checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "no config", checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "existing volume RW", cfg: []*blockcfg.ExistingVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.ExistingVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "existing-data", VolumeDiscoverySpec: blockcfg.VolumeDiscoverySpec{ VolumeSelectorConfig: blockcfg.VolumeSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`volume.partition_label == "MY-DATA"`, celenv.VolumeLocator())), }, }, MountSpec: blockcfg.ExistingMountSpec{ MountReadOnly: new(false), }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, block.ExistingVolumeLabel, resources[0].Label) assert.Equal(t, constants.ExistingVolumePrefix+"existing-data", resources[0].VolumeID) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, "existing-data", vc.TypedSpec().Mount.TargetPath) assert.Equal(t, constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) assert.Equal(t, fs.FileMode(0o755), vc.TypedSpec().Mount.FileMode) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { require.NoError(t, err) assert.False(t, m.TypedSpec().ReadOnly, "expected read-write mount") }) }, }, { name: "existing volume RO", cfg: []*blockcfg.ExistingVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.ExistingVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "readonly-data", VolumeDiscoverySpec: blockcfg.VolumeDiscoverySpec{ VolumeSelectorConfig: blockcfg.VolumeSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`volume.partition_label == "READONLY-DATA"`, celenv.VolumeLocator())), }, }, MountSpec: blockcfg.ExistingMountSpec{ MountReadOnly: new(true), }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { require.NoError(t, err) assert.True(t, m.TypedSpec().ReadOnly, "expected read-only mount") }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() mergedCfg, err := container.New(xslices.Map(tc.cfg, func(cfg *blockcfg.ExistingVolumeConfigV1Alpha1) configconfig.Document { return cfg })...) require.NoError(t, err) resources, err := volumeconfig.ExistingVolumeTransformer(mergedCfg) require.NoError(t, err) tc.checkFunc(t, resources) }) } } //nolint:dupl func TestExternalVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string cfg []*blockcfg.ExternalVolumeConfigV1Alpha1 checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "no config", checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "external volume RW", cfg: []*blockcfg.ExternalVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.ExternalVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "external-data", FilesystemType: block.FilesystemTypeVirtiofs, MountSpec: blockcfg.ExternalMountSpec{ MountReadOnly: new(false), MountVirtiofs: &blockcfg.VirtiofsMountSpec{ VirtiofsTag: "data", }, }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, block.ExternalVolumeLabel, resources[0].Label) assert.Equal(t, constants.ExternalVolumePrefix+"external-data", resources[0].VolumeID) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeExternal, vc.TypedSpec().Type) assert.Equal(t, "external-data", vc.TypedSpec().Mount.TargetPath) assert.Equal(t, constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) assert.Equal(t, fs.FileMode(0o755), vc.TypedSpec().Mount.FileMode) assert.Equal(t, "data", vc.TypedSpec().Provisioning.DiskSelector.External) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { require.NoError(t, err) assert.False(t, m.TypedSpec().ReadOnly, "expected read-write mount") }) }, }, { name: "external volume RW", cfg: []*blockcfg.ExternalVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.ExternalVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "external-data", FilesystemType: block.FilesystemTypeVirtiofs, MountSpec: blockcfg.ExternalMountSpec{ MountReadOnly: new(true), MountVirtiofs: &blockcfg.VirtiofsMountSpec{ VirtiofsTag: "data", }, }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, block.ExternalVolumeLabel, resources[0].Label) assert.Equal(t, constants.ExternalVolumePrefix+"external-data", resources[0].VolumeID) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypeExternal, vc.TypedSpec().Type) assert.Equal(t, "external-data", vc.TypedSpec().Mount.TargetPath) assert.Equal(t, constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) assert.Equal(t, fs.FileMode(0o755), vc.TypedSpec().Mount.FileMode) assert.Equal(t, "data", vc.TypedSpec().Provisioning.DiskSelector.External) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { require.NoError(t, err) assert.True(t, m.TypedSpec().ReadOnly, "expected read-write mount") }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() mergedCfg, err := container.New(xslices.Map(tc.cfg, func(cfg *blockcfg.ExternalVolumeConfigV1Alpha1) configconfig.Document { return cfg })...) require.NoError(t, err) resources, err := volumeconfig.ExternalVolumeTransformer(mergedCfg) require.NoError(t, err) tc.checkFunc(t, resources) }) } } //nolint:dupl func TestSwapVolumeTransformer(t *testing.T) { t.Parallel() for _, tc := range []struct { name string cfg []*blockcfg.SwapVolumeConfigV1Alpha1 checkFunc func(t *testing.T, resources []volumeconfig.VolumeResource) }{ { name: "no config", checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 0) }, }, { name: "swap volume", cfg: []*blockcfg.SwapVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.SwapVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "swap1", }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, constants.SwapVolumePrefix+"swap1", resources[0].VolumeID) assert.Equal(t, block.SwapVolumeLabel, resources[0].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, block.FilesystemTypeSwap, vc.TypedSpec().Provisioning.FilesystemSpec.Type) assert.Equal(t, constants.SwapVolumePrefix+"swap1", vc.TypedSpec().Provisioning.PartitionSpec.Label) assert.Equal(t, block.WaveUserVolumes, vc.TypedSpec().Provisioning.Wave) assert.EqualValues(t, volumeconfig.MinUserVolumeSize, vc.TypedSpec().Provisioning.PartitionSpec.MinSize) assert.EqualValues(t, 0, vc.TypedSpec().Provisioning.PartitionSpec.MaxSize) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { // default mount transform is noop require.NoError(t, err) }) }, }, { name: "swap volume with sizes", cfg: []*blockcfg.SwapVolumeConfigV1Alpha1{ { Meta: meta.Meta{ MetaKind: blockcfg.SwapVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, MetaName: "swap1", ProvisioningSpec: blockcfg.ProvisioningSpec{ ProvisioningMinSize: blockcfg.MustByteSize("1GB"), ProvisioningMaxSize: blockcfg.MustSize("2GB"), }, }, }, checkFunc: func(t *testing.T, resources []volumeconfig.VolumeResource) { require.Len(t, resources, 1) assert.Equal(t, constants.SwapVolumePrefix+"swap1", resources[0].VolumeID) assert.Equal(t, block.SwapVolumeLabel, resources[0].Label) testTransformFunc(t, resources[0].TransformFunc, func(t *testing.T, vc *block.VolumeConfig, err error) { require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, block.FilesystemTypeSwap, vc.TypedSpec().Provisioning.FilesystemSpec.Type) assert.Equal(t, constants.SwapVolumePrefix+"swap1", vc.TypedSpec().Provisioning.PartitionSpec.Label) assert.Equal(t, block.WaveUserVolumes, vc.TypedSpec().Provisioning.Wave) assert.EqualValues(t, 1*1000*1000*1000, vc.TypedSpec().Provisioning.PartitionSpec.MinSize) assert.EqualValues(t, 2*1000*1000*1000, vc.TypedSpec().Provisioning.PartitionSpec.MaxSize) }) testMountTransformFunc(t, resources[0].MountTransformFunc, func(t *testing.T, m *block.VolumeMountRequest, err error) { // default mount transform is noop require.NoError(t, err) }) }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() mergedCfg, err := container.New(xslices.Map(tc.cfg, func(cfg *blockcfg.SwapVolumeConfigV1Alpha1) configconfig.Document { return cfg })...) require.NoError(t, err) resources, err := volumeconfig.SwapVolumeTransformer(mergedCfg) require.NoError(t, err) tc.checkFunc(t, resources) }) } } func testMountTransformFunc(t *testing.T, transformer func(*block.VolumeMountRequest) error, checkFunc func(t *testing.T, m *block.VolumeMountRequest, err error), ) { t.Helper() m := block.NewVolumeMountRequest(block.NamespaceName, "test") err := transformer(m) checkFunc(t, m, err) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/volume_config_builder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig import ( "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/cel" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Builder is a small utility to build spec-modifying functions // that can be applied (inside `safe.WriterModify`) to a VolumeConfigSpec. // // The builder is just a wrapper around a slice `func(*VolumeConfigSpec) error`, and the // `.WithXXX` methods only append to the slice. No modifications are made to the spec until // `Apply()` is called, or the function returned by `WriterFunc()` is called. // // Errors that occur during application are collected into a multierror and returned // by `Apply`/`WriterFunc`. type Builder struct { opts []func(*block.VolumeConfigSpec) error } // NewBuilder creates a new VolumeConfigBuilder. func NewBuilder() *Builder { return &Builder{ opts: nil, } } // WithType sets VolumeConfigSpec.Type. func (b *Builder) WithType(volumeType block.VolumeType) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Type = volumeType return nil }) return b } // WithLocator sets VolumeConfigSpec.Locator.Match. func (b *Builder) WithLocator(match cel.Expression) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Locator = block.LocatorSpec{Match: match} return nil }) return b } // WithDiskLocator sets VolumeConfigSpec.Locator.DiskMatch. func (b *Builder) WithDiskLocator(diskMatch cel.Expression) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Locator = block.LocatorSpec{DiskMatch: diskMatch} return nil }) return b } // WithProvisioning sets VolumeConfigSpec.Provisioning. func (b *Builder) WithProvisioning(provisioning block.ProvisioningSpec) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Provisioning = provisioning return nil }) return b } // WithMount sets VolumeConfigSpec.Mount. func (b *Builder) WithMount(mount block.MountSpec) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Mount = mount return nil }) return b } // WithSymlink sets VolumeConfigSpec.Symlink. func (b *Builder) WithSymlink(symlink block.SymlinkProvisioningSpec) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Symlink = symlink return nil }) return b } // WithParentID sets VolumeConfigSpec.ParentID. func (b *Builder) WithParentID(parentID string) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.ParentID = parentID return nil }) return b } // WithEncryption sets VolumeConfigSpec.Encryption. func (b *Builder) WithEncryption(encryption block.EncryptionSpec) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { spec.Encryption = encryption return nil }) return b } // WithConvertEncryptionConfiguration sets VolumeConfigSpec.Encryption, converting the provided // encryption config. func (b *Builder) WithConvertEncryptionConfiguration(encryption configconfig.EncryptionConfig) *Builder { b.opts = append(b.opts, func(spec *block.VolumeConfigSpec) error { return volumes.ConvertEncryptionConfiguration(encryption, spec) }) return b } // WithFunc adds an arbitraty spec-modifying `func(*block.VolumeConfigSpec) error` to the builder. // Errors returned by the function are collected and returned by Apply/WriterFunc. func (b *Builder) WithFunc(fn func(*block.VolumeConfigSpec) error) *Builder { b.opts = append(b.opts, fn) return b } // Apply applies all the changes to the provided VolumeConfigSpec. // Returns a multierror containing all errors that occurred during application. func (b *Builder) Apply(spec *block.VolumeConfigSpec) error { var errors *multierror.Error for _, opt := range b.opts { if err := opt(spec); err != nil { errors = multierror.Append(errors, err) } } return errors.ErrorOrNil() } // WriterFunc returns a function that applies all the changes to the provided VolumeConfig. // The function returned is suitable for use in `safe.WriterModify`, and returns a multierror // containing all errors that occurred during application. func (b *Builder) WriterFunc() func(*block.VolumeConfig) error { return func(vc *block.VolumeConfig) error { return b.Apply(vc.TypedSpec()) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig/volume_config_builder_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumeconfig_test import ( "errors" "io/fs" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestNewVolumeConfigBuilder(t *testing.T) { t.Parallel() builder := volumeconfig.NewBuilder() require.NotNil(t, builder) spec := &block.VolumeConfigSpec{} err := builder.Apply(spec) require.NoError(t, err) } func TestVolumeConfigBuilder_EmptyBuilder(t *testing.T) { t.Parallel() spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder() err := builder.Apply(spec) require.NoError(t, err) // spec should remain empty/default assert.Equal(t, block.VolumeType(0), spec.Type) assert.Empty(t, spec.ParentID) assert.Empty(t, spec.Mount) assert.Empty(t, spec.Provisioning) } func TestVolumeConfigBuilder_WithType(t *testing.T) { t.Parallel() for _, tc := range []struct { name string volumeType block.VolumeType }{ { name: "partition", volumeType: block.VolumeTypePartition, }, { name: "directory", volumeType: block.VolumeTypeDirectory, }, { name: "overlay", volumeType: block.VolumeTypeOverlay, }, { name: "symlink", volumeType: block.VolumeTypeSymlink, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithType(tc.volumeType) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, tc.volumeType, spec.Type) }) } } func TestVolumeConfigBuilder_WithLocator(t *testing.T) { t.Parallel() match := cel.MustExpression(cel.ParseBooleanExpression(`volume.partition_label == "TEST"`, celenv.VolumeLocator())) spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithLocator(match) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, match, spec.Locator.Match) } func TestVolumeConfigBuilder_WithProvisioning(t *testing.T) { t.Parallel() provisioning := block.ProvisioningSpec{ Wave: block.WaveUserVolumes, DiskSelector: block.DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`system_disk`, celenv.DiskLocator())), }, PartitionSpec: block.PartitionSpec{ MinSize: 100 * 1024 * 1024, MaxSize: 200 * 1024 * 1024, Grow: true, Label: "test-label", TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeXFS, }, } spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithProvisioning(provisioning) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, provisioning, spec.Provisioning) } func TestVolumeConfigBuilder_WithMount(t *testing.T) { t.Parallel() mount := block.MountSpec{ TargetPath: "/test/path", ParentID: "parent-id", SelinuxLabel: "test_label", FileMode: 0o755, UID: 1000, GID: 2000, } spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithMount(mount) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, mount, spec.Mount) } func TestVolumeConfigBuilder_WithEncryption(t *testing.T) { t.Parallel() encryption := block.EncryptionSpec{ Provider: block.EncryptionProviderLUKS2, Keys: []block.EncryptionKey{ { Slot: 0, Type: block.EncryptionKeyTPM, }, }, } spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithEncryption(encryption) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, encryption, spec.Encryption) } func TestVolumeConfigBuilder_WithSymlink(t *testing.T) { t.Parallel() symlink := block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/target/path", Force: true, } spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithSymlink(symlink) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, symlink, spec.Symlink) } func TestVolumeConfigBuilder_WithParentID(t *testing.T) { t.Parallel() parentID := "parent-volume-id" spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder().WithParentID(parentID) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, parentID, spec.ParentID) } func TestVolumeConfigBuilder_WithFunc(t *testing.T) { t.Parallel() customValue := "custom-value" spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder(). WithFunc(func(s *block.VolumeConfigSpec) error { s.ParentID = customValue return nil }) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, customValue, spec.ParentID) } func TestVolumeConfigBuilder_WithFunc_Error(t *testing.T) { t.Parallel() testErr := errors.New("test error") spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder(). WithFunc(func(s *block.VolumeConfigSpec) error { return testErr }) err := builder.Apply(spec) require.Error(t, err) assert.Contains(t, err.Error(), "test error") } func TestVolumeConfigBuilder_WithFunc_MultipleErrors(t *testing.T) { t.Parallel() err1 := errors.New("error 1") err2 := errors.New("error 2") spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder(). WithFunc(func(s *block.VolumeConfigSpec) error { return err1 }). WithFunc(func(s *block.VolumeConfigSpec) error { return err2 }) err := builder.Apply(spec) require.Error(t, err) assert.Contains(t, err.Error(), "error 1") assert.Contains(t, err.Error(), "error 2") } func TestVolumeConfigBuilder_Chaining(t *testing.T) { t.Parallel() spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder(). WithType(block.VolumeTypePartition). WithParentID("parent-id"). WithMount(block.MountSpec{ TargetPath: "/test/path", FileMode: 0o755, }). WithProvisioning(block.ProvisioningSpec{ Wave: block.WaveUserVolumes, PartitionSpec: block.PartitionSpec{ Label: "test-label", }, }) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, spec.Type) assert.Equal(t, "parent-id", spec.ParentID) assert.Equal(t, "/test/path", spec.Mount.TargetPath) assert.Equal(t, fs.FileMode(0o755), spec.Mount.FileMode) assert.Equal(t, block.WaveUserVolumes, spec.Provisioning.Wave) assert.Equal(t, "test-label", spec.Provisioning.PartitionSpec.Label) } func TestVolumeConfigBuilder_Chaining_Overwrite(t *testing.T) { t.Parallel() spec := &block.VolumeConfigSpec{} builder := volumeconfig.NewBuilder(). WithType(block.VolumeTypePartition). WithType(block.VolumeTypeDirectory). WithParentID("parent-1"). WithParentID("parent-2") err := builder.Apply(spec) require.NoError(t, err) // Last call should win assert.Equal(t, block.VolumeTypeDirectory, spec.Type) assert.Equal(t, "parent-2", spec.ParentID) } func TestVolumeConfigBuilder_WriterFunc(t *testing.T) { t.Parallel() vc := block.NewVolumeConfig(block.NamespaceName, "test") builder := volumeconfig.NewBuilder(). WithType(block.VolumeTypePartition). WithParentID("parent-id") writerFunc := builder.WriterFunc() require.NotNil(t, writerFunc) err := writerFunc(vc) require.NoError(t, err) assert.Equal(t, block.VolumeTypePartition, vc.TypedSpec().Type) assert.Equal(t, "parent-id", vc.TypedSpec().ParentID) } func TestVolumeConfigBuilder_MultipleWithFunc(t *testing.T) { t.Parallel() spec := &block.VolumeConfigSpec{} counter := 0 builder := volumeconfig.NewBuilder(). WithFunc(func(s *block.VolumeConfigSpec) error { counter++ s.ParentID = "func1" return nil }). WithFunc(func(s *block.VolumeConfigSpec) error { counter++ s.ParentID = "func2" return nil }). WithFunc(func(s *block.VolumeConfigSpec) error { counter++ return nil }) err := builder.Apply(spec) require.NoError(t, err) assert.Equal(t, 3, counter) assert.Equal(t, "func2", spec.ParentID) } func TestVolumeConfigBuilder_WithConvertEncryptionConfiguration_Nil(t *testing.T) { t.Parallel() builder := volumeconfig.NewBuilder(). WithConvertEncryptionConfiguration(nil) vc := block.NewVolumeConfig(block.NamespaceName, "test") err := builder.Apply(vc.TypedSpec()) require.NoError(t, err) assert.Empty(t, vc.TypedSpec().Encryption) } func TestVolumeConfigBuilder_WithConvertEncryptionConfiguration_WithConfig(t *testing.T) { t.Parallel() encryptionConfig := blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyTPM: &blockcfg.EncryptionKeyTPM{}, }, }, } builder := volumeconfig.NewBuilder(). WithConvertEncryptionConfiguration(encryptionConfig) vc := block.NewVolumeConfig(block.NamespaceName, "test") err := builder.Apply(vc.TypedSpec()) require.NoError(t, err) assert.Equal(t, block.EncryptionProviderLUKS2, vc.TypedSpec().Encryption.Provider) assert.Len(t, vc.TypedSpec().Encryption.Keys, 1) assert.Equal(t, 0, vc.TypedSpec().Encryption.Keys[0].Slot) assert.Equal(t, block.EncryptionKeyTPM, vc.TypedSpec().Encryption.Keys[0].Type) } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package volumes provides utilities and extra functions for the volume manager. package volumes import ( "cmp" "math" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "github.com/siderolabs/talos/internal/pkg/encryption" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // CompareVolumeConfigs compares two volume configs in the proposed order of provisioning. func CompareVolumeConfigs(a, b *block.VolumeConfig) int { // first, sort volumes without provisioning instructions first, as they don't block provisioning of other volumes if c := cmpBool(!value.IsZero(a.TypedSpec().Provisioning), !value.IsZero(b.TypedSpec().Provisioning)); c != 0 { return c } // second, sort by wave, smaller wave first if c := cmp.Compare(a.TypedSpec().Provisioning.Wave, b.TypedSpec().Provisioning.Wave); c != 0 { return c } // prefer partitions which do not grow, as growing partitions may consume space needed by other partitions if c := cmpBool(a.TypedSpec().Provisioning.PartitionSpec.Grow, b.TypedSpec().Provisioning.PartitionSpec.Grow); c != 0 { return c } // prefer partitions with smaller sizes first // e.g.: for a disk of size 1GiB, and following config with min-max requested sizes: // 1. 100MiB - 200MiB // 2. 300MiB - 2GiB // // if the order is 2-1, the second partition will grow to full disk size and will leave no space for the first partition, // but if the order is 1-2, partition sizes will 200MiB and 800MiB respectively. // // we compare only max size, as it affects the resulting size of the partition desiredSizeA := cmp.Or(a.TypedSpec().Provisioning.PartitionSpec.MaxSize, math.MaxUint64) desiredSizeB := cmp.Or(b.TypedSpec().Provisioning.PartitionSpec.MaxSize, math.MaxUint64) return cmp.Compare(desiredSizeA, desiredSizeB) } func cmpBool(a, b bool) int { if a == b { return 0 } if a { return 1 } return -1 } // Retryable is an error tag. type Retryable struct{} // DiskContext captures the context of a disk. type DiskContext struct { Disk *blockpb.DiskSpec SystemDisk optional.Optional[bool] } // ToCELContext converts the disk context to CEL contexts. func (d *DiskContext) ToCELContext() map[string]any { result := map[string]any{ "disk": d.Disk, } if val, ok := d.SystemDisk.Get(); ok { result["system_disk"] = val } return result } // ManagerContext captures the context of the volume manager. type ManagerContext struct { Cfg *block.VolumeConfig Status *block.VolumeStatusSpec ParentStatus *block.VolumeStatus ParentFinalizer string DiscoveredVolumes []*blockpb.DiscoveredVolumeSpec Disks []DiskContext DevicesReady bool PreviousWaveProvisioned bool EncryptionHelpers encryption.Helpers ShouldCloseVolume bool } ================================================ FILE: internal/app/machined/pkg/controllers/block/internal/volumes/volumes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package volumes_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestCompareVolumeConfigs(t *testing.T) { t.Parallel() for _, test := range []struct { name string a *block.VolumeConfigSpec b *block.VolumeConfigSpec expected int }{ { name: "no provisioning instructions", a: &block.VolumeConfigSpec{}, b: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, }, }, expected: -1, }, { name: "different wave", a: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, }, }, b: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveUserVolumes, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeEXT4, }, }, }, expected: -1, }, { name: "prefer grow", a: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: true, }, }, }, b: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: false, }, }, }, expected: 1, }, { name: "prefer smaller size", a: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: false, MinSize: 100, MaxSize: 200, }, }, }, b: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: false, MinSize: 150, MaxSize: 1000, }, }, }, expected: -1, }, { name: "prefer max size", a: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: false, MinSize: 100, MaxSize: 200, }, }, }, b: &block.VolumeConfigSpec{ Provisioning: block.ProvisioningSpec{ Wave: block.WaveSystemDisk, PartitionSpec: block.PartitionSpec{ Grow: false, MinSize: 50, MaxSize: 0, // no limit }, }, }, expected: -1, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() resA := block.NewVolumeConfig(block.NamespaceName, "A") *resA.TypedSpec() = *test.a resB := block.NewVolumeConfig(block.NamespaceName, "B") *resB.TypedSpec() = *test.b actual := volumes.CompareVolumeConfigs(resA, resB) assert.Equal(t, test.expected, actual) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/lvm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-cmd/pkg/cmd" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // LVMActivationController activates LVM volumes when they are discovered by the block.DiscoveryController. type LVMActivationController struct { V1Alpha1Mode machineruntime.Mode seenVolumes map[string]struct{} activatedVGs map[string]struct{} } // Name implements controller.Controller interface. func (ctrl *LVMActivationController) Name() string { return "block.LVMActivationController" } // Inputs implements controller.Controller interface. func (ctrl *LVMActivationController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.DiscoveredVolumeType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, ID: optional.Some(constants.MetaPartitionLabel), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("udevd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *LVMActivationController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *LVMActivationController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.seenVolumes == nil { ctrl.seenVolumes = map[string]struct{}{} } if ctrl.activatedVGs == nil { ctrl.activatedVGs = map[string]struct{}{} } if ctrl.V1Alpha1Mode.IsAgent() { // in agent mode, we don't want to activate LVMs return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } udevdService, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "udevd") if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get udevd service: %w", err) } if udevdService == nil { logger.Debug("udevd service not registered yet") continue } if !udevdService.TypedSpec().Running || !udevdService.TypedSpec().Healthy { logger.Debug("waiting for udevd service to be running and healthy") continue } meta, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, r, constants.MetaPartitionLabel) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get meta partition info: %w", err) } if meta == nil { logger.Debug("meta partition not registered yet") continue } if meta.TypedSpec().Phase != block.VolumePhaseReady { logger.Debug("meta partition not ready yet") continue } discoveredVolumes, err := safe.ReaderListAll[*block.DiscoveredVolume](ctx, r) if err != nil { return fmt.Errorf("failed to list discovered volumes: %w", err) } var multiErr error for dv := range discoveredVolumes.All() { if dv.TypedSpec().Name != "lvm2-pv" { // if the volume is not an LVM volume the moment we saw it, we can skip it // we need to activate the volumes only on reboot, not when they are first formatted ctrl.seenVolumes[dv.Metadata().ID()] = struct{}{} continue } if _, ok := ctrl.seenVolumes[dv.Metadata().ID()]; ok { continue } logger.Debug("checking device for LVM volume activation", zap.String("device", dv.TypedSpec().DevPath)) vgName, err := ctrl.checkVGNeedsActivation(ctx, dv.TypedSpec().DevPath) if err != nil { multiErr = multierror.Append(multiErr, err) continue } if vgName == "" { continue } if _, ok := ctrl.activatedVGs[vgName]; ok { continue } logger.Info("activating LVM volume", zap.String("name", vgName)) // activate the volume group if _, err = cmd.RunWithOptions(ctx, "/sbin/lvm", []string{ "vgchange", "-aay", "--autoactivation", "event", vgName, }, ); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("failed to activate LVM volume %s: %w", vgName, err)) } else { ctrl.activatedVGs[vgName] = struct{}{} } } if multiErr != nil { return multiErr } } } // checkVGNeedsActivation checks if the device is part of a volume group and returns the volume group name // if it needs to be activated, otherwise it returns an empty string. func (ctrl *LVMActivationController) checkVGNeedsActivation(ctx context.Context, devicePath string) (string, error) { // first we check if all associated volumes are available // https://man7.org/linux/man-pages/man7/lvmautoactivation.7.html stdOut, err := cmd.RunWithOptions(ctx, "/sbin/lvm", []string{ "pvscan", "--cache", "--listvg", "--checkcomplete", "--vgonline", "--autoactivation", "event", "--udevoutput", devicePath, }, ) if err != nil { return "", fmt.Errorf("failed to check if LVM volume backed by device %s needs activation: %w", devicePath, err) } // parse the key-value pairs from the udev output for line := range strings.SplitSeq(stdOut, "\n") { key, value, ok := strings.Cut(line, "=") if !ok { continue } value = strings.Trim(value, "'\"") if key == "LVM_VG_NAME_COMPLETE" { return value, nil } } return "", nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "slices" "syscall" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/swap" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/filetree" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) type mountContext struct { point *mount.Point readOnly bool disableAccessTime bool secure bool unmounter func() error } // MountController performs actual mount/unmount operations based on the MountRequests. type MountController struct { activeMounts map[string]*mountContext } // Name implements controller.Controller interface. func (ctrl *MountController) Name() string { return "block.MountController" } // Inputs implements controller.Controller interface. func (ctrl *MountController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.MountRequestType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.MountStatusType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *MountController) Outputs() []controller.Output { return []controller.Output{ { Type: block.MountStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *MountController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.activeMounts == nil { ctrl.activeMounts = map[string]*mountContext{} } for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } volumeStatuses, err := safe.ReaderListAll[*block.VolumeStatus](ctx, r) if err != nil { return fmt.Errorf("failed to read volume statuses: %w", err) } volumeStatusMap := xslices.ToMap( safe.ToSlice( volumeStatuses, identity, ), func(v *block.VolumeStatus) (string, *block.VolumeStatus) { return v.Metadata().ID(), v }, ) mountStatuses, err := safe.ReaderListAll[*block.MountStatus](ctx, r) if err != nil { return fmt.Errorf("failed to read mount statuses: %w", err) } mountStatusMap := xslices.ToMap( safe.ToSlice( mountStatuses, identity, ), func(v *block.MountStatus) (string, *block.MountStatus) { return v.Metadata().ID(), v }, ) mountRequests, err := safe.ReaderListAll[*block.MountRequest](ctx, r) if err != nil { return fmt.Errorf("failed to read mount requests: %w", err) } for mountRequest := range mountRequests.All() { volumeStatus := volumeStatusMap[mountRequest.TypedSpec().VolumeID] volumeNotReady := volumeStatus == nil || volumeStatus.TypedSpec().Phase != block.VolumePhaseReady || volumeStatus.Metadata().Phase() != resource.PhaseRunning mountRequestTearingDown := mountRequest.Metadata().Phase() == resource.PhaseTearingDown mountStatus := mountStatusMap[mountRequest.Metadata().ID()] mountStatusTearingDown := mountStatus != nil && mountStatus.Metadata().Phase() == resource.PhaseTearingDown mountHasParent := mountRequest.TypedSpec().ParentMountID != "" mountParentStatus := mountStatusMap[mountRequest.TypedSpec().ParentMountID] // this might be nil mountParentReady := !mountHasParent || (mountParentStatus != nil && mountParentStatus.Metadata().Phase() == resource.PhaseRunning) mountParentTearingDown := mountHasParent && mountParentStatus != nil && mountParentStatus.Metadata().Phase() == resource.PhaseTearingDown parentFinalizerName := ctrl.Name() + "-" + mountRequest.Metadata().ID() if volumeNotReady || mountRequestTearingDown || mountStatusTearingDown || mountParentTearingDown { // we should tear down the mount in the following sequence: // 1. tear down & destroy MountStatus // 2. perform actual unmount // 3. remove finalizer from VolumeStatus // 4. remove finalizer from parent MountStatus (if any) // 5. remove finalizer from MountRequest mountStatusTornDown, err := ctrl.tearDownMountStatus(ctx, r, logger, mountRequest) if err != nil { return fmt.Errorf("error tearing down mount status %q: %w", mountRequest.Metadata().ID(), err) } if !mountStatusTornDown { continue } if volumeStatus != nil { if err = ctrl.handleUnmountOperation(logger, mountRequest, volumeStatus); err != nil { return err } } if volumeStatus != nil && volumeStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.RemoveFinalizer(ctx, volumeStatus.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to remove finalizer from volume status %q: %w", volumeStatus.Metadata().ID(), err) } } if mountParentStatus != nil && mountParentStatus.Metadata().Finalizers().Has(parentFinalizerName) { if err = r.RemoveFinalizer(ctx, mountParentStatus.Metadata(), parentFinalizerName); err != nil { return fmt.Errorf("failed to remove finalizer from parent mount status %q: %w", mountParentStatus.Metadata().ID(), err) } } if mountRequest.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.RemoveFinalizer(ctx, mountRequest.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to remove finalizer from mount request %q: %w", mountRequest.Metadata().ID(), err) } } } if !(volumeNotReady || mountRequestTearingDown) && mountParentReady { // we should perform mount operation in the following sequence: // 1. add finalizer on MountRequest // 2. add finalizer on parent MountStatus (if any) // 3. add finalizer on VolumeStatus // 4. perform actual mount // 5. create MountStatus if !mountRequest.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, mountRequest.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to add finalizer to mount request %q: %w", mountRequest.Metadata().ID(), err) } } if mountHasParent && !mountParentStatus.Metadata().Finalizers().Has(parentFinalizerName) && mountParentStatus.Metadata().Phase() == resource.PhaseRunning { if err = r.AddFinalizer(ctx, mountParentStatus.Metadata(), parentFinalizerName); err != nil { return fmt.Errorf("failed to add finalizer to parent mount status %q: %w", mountParentStatus.Metadata().ID(), err) } } if !volumeStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, volumeStatus.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to add finalizer to volume status %q: %w", volumeStatus.Metadata().ID(), err) } } mountSource := volumeStatus.TypedSpec().MountLocation mountTarget := volumeStatus.TypedSpec().MountSpec.TargetPath mountFilesystem := volumeStatus.TypedSpec().Filesystem rootPath := "/" if mountHasParent { rootPath = mountParentStatus.TypedSpec().Target } if err = ctrl.handleMountOperation(logger, rootPath, mountSource, mountTarget, mountFilesystem, mountRequest, volumeStatus); err != nil { return err } if err = safe.WriterModify( ctx, r, block.NewMountStatus(block.NamespaceName, mountRequest.Metadata().ID()), func(mountStatus *block.MountStatus) error { mountStatus.TypedSpec().Spec = *mountRequest.TypedSpec() mountStatus.TypedSpec().Source = mountSource mountStatus.TypedSpec().Target = filepath.Join(rootPath, mountTarget) mountStatus.TypedSpec().Filesystem = mountFilesystem mountStatus.TypedSpec().EncryptionProvider = volumeStatus.TypedSpec().EncryptionProvider mountStatus.TypedSpec().ReadOnly = mountRequest.TypedSpec().ReadOnly mountStatus.TypedSpec().ProjectQuotaSupport = volumeStatus.TypedSpec().MountSpec.ProjectQuotaSupport mountStatus.TypedSpec().Detached = mountRequest.TypedSpec().Detached // This needs to be set through accessor, and is not guaranteed to resolve to a valid root. mount, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if ok && mount.point != nil { mountStatus.TypedSpec().SetRoot(mount.point.Root()) } else { mountStatus.TypedSpec().SetRoot(&xfs.OSRoot{Shadow: filepath.Join(rootPath, mountTarget)}) } return nil }, ); err != nil { return fmt.Errorf("failed to create mount status %q: %w", mountRequest.Metadata().ID(), err) } } } r.ResetRestartBackoff() } } func (ctrl *MountController) tearDownMountStatus(ctx context.Context, r controller.Runtime, logger *zap.Logger, mountRequest *block.MountRequest) (bool, error) { logger = logger.With(zap.String("mount_request", mountRequest.Metadata().ID())) okToDestroy, err := r.Teardown(ctx, block.NewMountStatus(block.NamespaceName, mountRequest.Metadata().ID()).Metadata()) if err != nil { if state.IsNotFoundError(err) { // no mount status, we are done return true, nil } return false, fmt.Errorf("failed to teardown mount status %q: %w", mountRequest.Metadata().ID(), err) } if !okToDestroy { logger.Debug("waiting for mount status to be torn down") return false, nil } err = r.Destroy(ctx, block.NewMountStatus(block.NamespaceName, mountRequest.Metadata().ID()).Metadata()) if err != nil { return false, fmt.Errorf("failed to destroy mount status %q: %w", mountRequest.Metadata().ID(), err) } return true, nil } func (ctrl *MountController) handleMountOperation( logger *zap.Logger, rootPath string, mountSource, mountTarget string, mountFilesystem block.FilesystemType, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { switch volumeStatus.TypedSpec().Type { case block.VolumeTypeDirectory: return ctrl.handleDirectoryMountOperation(logger, rootPath, mountTarget, mountRequest, volumeStatus) case block.VolumeTypeOverlay: return ctrl.handleOverlayMountOperation(logger, filepath.Join(rootPath, mountTarget), mountRequest, volumeStatus) case block.VolumeTypeSymlink: return ctrl.handleSymlinkMountOperation(logger, rootPath, mountTarget, mountRequest, volumeStatus) case block.VolumeTypeTmpfs: return fmt.Errorf("not implemented yet") case block.VolumeTypeExternal: return ctrl.handleDiskMountOperation(logger, mountSource, filepath.Join(rootPath, mountTarget), mountFilesystem, mountRequest, volumeStatus) case block.VolumeTypeDisk, block.VolumeTypePartition: if mountFilesystem == block.FilesystemTypeSwap { return ctrl.handleSwapMountOperation(logger, mountSource, mountRequest, volumeStatus) } return ctrl.handleDiskMountOperation(logger, mountSource, filepath.Join(rootPath, mountTarget), mountFilesystem, mountRequest, volumeStatus) default: return fmt.Errorf("unsupported volume type %q", volumeStatus.TypedSpec().Type) } } func (ctrl *MountController) handleDirectoryMountOperation( logger *zap.Logger, rootPath string, target string, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { targetPath := filepath.Join(rootPath, target) if err := os.Mkdir(targetPath, volumeStatus.TypedSpec().MountSpec.FileMode); err != nil { if !os.IsExist(err) { return fmt.Errorf("failed to create target path: %w", err) } st, err := os.Stat(targetPath) if err != nil { return fmt.Errorf("failed to stat target path: %w", err) } if !st.IsDir() { return fmt.Errorf("target path %q is not a directory", targetPath) } } if volumeStatus.TypedSpec().MountSpec.BindTarget != nil { if err := ctrl.handleBindMountOperation( logger, rootPath, target, *volumeStatus.TypedSpec().MountSpec.BindTarget, mountRequest, volumeStatus, ); err != nil { return fmt.Errorf("target path %q is not a directory", targetPath) } } return ctrl.updateTargetSettings(targetPath, volumeStatus.TypedSpec().Filesystem, volumeStatus.TypedSpec().MountSpec) } func (ctrl *MountController) handleBindMountOperation( logger *zap.Logger, rootPath string, source string, bindTarget string, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { _, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] // mount hasn't been done yet if !ok { mountSource := filepath.Join(rootPath, source) mountTarget := filepath.Join(rootPath, bindTarget) if err := os.Mkdir(mountTarget, volumeStatus.TypedSpec().MountSpec.FileMode); err != nil { if !os.IsExist(err) { return fmt.Errorf("failed to create target path: %w", err) } st, err := os.Stat(mountTarget) if err != nil { return fmt.Errorf("failed to stat target path: %w", err) } if !st.IsDir() { return fmt.Errorf("target path %q is not a directory", mountTarget) } } opts := []mount.ManagerOption{ mount.WithSelinuxLabel(volumeStatus.TypedSpec().MountSpec.SelinuxLabel), } manager := mount.NewManager(slices.Concat( []mount.ManagerOption{ mount.WithTarget(mountTarget), mount.WithOpentreeFromPath(mountSource), mount.WithPrinter(logger.Sugar().Infof), }, opts, )...) mountpoint, err := manager.Mount() if err != nil { return fmt.Errorf("failed to mount %q: %w", mountRequest.Metadata().ID(), err) } if !mountRequest.TypedSpec().ReadOnly && !mountRequest.TypedSpec().Detached { if err = ctrl.updateTargetSettings(mountTarget, volumeStatus.TypedSpec().Filesystem, volumeStatus.TypedSpec().MountSpec); err != nil { manager.Unmount() //nolint:errcheck return fmt.Errorf("failed to update target settings %q: %w", mountRequest.Metadata().ID(), err) } } logger.Info("bind mount", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("source", mountSource), zap.String("target", mountTarget), ) ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{ point: mountpoint, readOnly: mountRequest.TypedSpec().ReadOnly, unmounter: manager.Unmount, } } return nil } //nolint:gocyclo func (ctrl *MountController) handleSymlinkMountOperation( logger *zap.Logger, rootPath string, target string, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { _, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if ok { return nil } targetPath := filepath.Join(rootPath, target) st, err := os.Lstat(targetPath) if err != nil && !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("failed to stat target path: %w", err) } if st == nil { // create the symlink if err := os.Symlink(volumeStatus.TypedSpec().SymlinkSpec.SymlinkTargetPath, targetPath); err != nil { return fmt.Errorf("failed to create symlink %q: %w", targetPath, err) } ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{} return nil } if st.Mode()&os.ModeSymlink != 0 { // if it's already a symlink, check if it points to the right target symlinkTarget, err := os.Readlink(targetPath) if err != nil { return fmt.Errorf("failed to read symlink target: %w", err) } if symlinkTarget == volumeStatus.TypedSpec().SymlinkSpec.SymlinkTargetPath { return nil } } if !volumeStatus.TypedSpec().SymlinkSpec.Force { return fmt.Errorf("target path %q is not a symlink to %q", targetPath, volumeStatus.TypedSpec().SymlinkSpec.SymlinkTargetPath) } // try to remove forcefully if err := os.RemoveAll(targetPath); err != nil { if !st.Mode().IsDir() { return fmt.Errorf("failed to remove target path, and target is not a directory %s: %w", st.Mode(), err) } // try to remove all entries if it's a directory entries, err := os.ReadDir(targetPath) if err != nil { return fmt.Errorf("failed to read target path: %w", err) } for _, entry := range entries { if err := os.RemoveAll(filepath.Join(targetPath, entry.Name())); err != nil { logger.Warn("failed to remove target path entry", zap.String("entry", entry.Name()), zap.Error(err)) } } ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{} // return early, i.e. keep this as a directory return nil } if err := os.Symlink(volumeStatus.TypedSpec().SymlinkSpec.SymlinkTargetPath, targetPath); err != nil { return fmt.Errorf("failed to create symlink %q: %w", targetPath, err) } ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{} return nil } //nolint:gocyclo func (ctrl *MountController) updateTargetSettings( targetPath string, fstype block.FilesystemType, mountSpec block.MountSpec, ) error { if err := os.Chmod(targetPath, mountSpec.FileMode); err != nil { return fmt.Errorf("failed to chmod %q: %w", targetPath, err) } st, err := os.Stat(targetPath) if err != nil { return fmt.Errorf("failed to stat %q: %w", targetPath, err) } sysStat := st.Sys().(*syscall.Stat_t) if sysStat.Uid != uint32(mountSpec.UID) || sysStat.Gid != uint32(mountSpec.GID) { if mountSpec.RecursiveRelabel { err = filetree.ChownRecursive(targetPath, uint32(mountSpec.UID), uint32(mountSpec.GID)) } else { err = os.Chown(targetPath, mountSpec.UID, mountSpec.GID) } if err != nil { return fmt.Errorf("failed to chown %q: %w", targetPath, err) } } currentLabel, err := selinux.GetLabel(targetPath) if err != nil { return fmt.Errorf("failed to get current label %q: %w", targetPath, err) } if currentLabel == mountSpec.SelinuxLabel { // nothing to do return nil } if mountSpec.RecursiveRelabel { err = selinux.SetLabelRecursive(targetPath, mountSpec.SelinuxLabel) } else { err = selinux.SetLabel(targetPath, mountSpec.SelinuxLabel) } return mount.FilterSelinuxLabelErrors(targetPath, fstype.String(), err) } //nolint:gocyclo,cyclop func (ctrl *MountController) handleDiskMountOperation( logger *zap.Logger, mountSource, mountTarget string, mountFilesystem block.FilesystemType, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { mountCtx, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] logger = logger.With(zap.String("mount_request.id", mountRequest.Metadata().ID())) // mount hasn't been done yet if !ok { var ( opts []mount.ManagerOption fsOpts []fsopen.Option ) fsOpts = append(fsOpts, fsopen.WithSource(mountSource), fsopen.WithProjectQuota(volumeStatus.TypedSpec().MountSpec.ProjectQuotaSupport), ) for _, param := range volumeStatus.TypedSpec().MountSpec.Parameters { logger.Info("adding new parameter", zap.String("parameter", param.Name), zap.String("parameter.type", param.Type.String()), zap.String("parameter.string", pointer.SafeDeref(param.String)), zap.Binary("parameter.bytes", param.Binary), ) switch param.Type { case block.FSParameterTypeBinaryValue: if param.Binary == nil { logger.Warn("skipping nil binary parameter", zap.String("parameter", param.Name)) continue } fsOpts = append(fsOpts, fsopen.WithBinaryParameters(param.Name, param.Binary)) case block.FSParameterTypeStringValue: if param.String == nil { logger.Warn("skipping nil string parameter", zap.String("parameter", param.Name)) continue } fsOpts = append(fsOpts, fsopen.WithStringParameter(param.Name, *param.String)) case block.FSParameterTypeBooleanValue: fsOpts = append(fsOpts, fsopen.WithBoolParameter(param.Name)) } } opts = append(opts, mount.WithSelinuxLabel(volumeStatus.TypedSpec().MountSpec.SelinuxLabel), ) if mountRequest.TypedSpec().DisableAccessTime { opts = append(opts, mount.WithDisableAccessTime()) } if mountRequest.TypedSpec().Secure { opts = append(opts, mount.WithSecure()) } if mountRequest.TypedSpec().ReadOnly { opts = append(opts, mount.WithReadOnly()) } if mountRequest.TypedSpec().Detached { opts = append(opts, mount.WithDetached()) } manager := mount.NewManager(slices.Concat( []mount.ManagerOption{ mount.WithTarget(mountTarget), mount.WithFsopen( mountFilesystem.String(), fsOpts..., ), mount.WithPrinter(logger.Sugar().Infof), }, opts, )...) mountpoint, err := manager.Mount() if err != nil { return fmt.Errorf("failed to mount %q: %w", mountRequest.Metadata().ID(), err) } if !mountRequest.TypedSpec().ReadOnly && !mountRequest.TypedSpec().Detached { if err = ctrl.updateTargetSettings(mountTarget, volumeStatus.TypedSpec().Filesystem, volumeStatus.TypedSpec().MountSpec); err != nil { manager.Unmount() //nolint:errcheck return fmt.Errorf("failed to update target settings %q: %w", mountRequest.Metadata().ID(), err) } } logger.Info("volume mount", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("source", mountSource), zap.String("target", mountTarget), zap.Stringer("filesystem", mountFilesystem), zap.Bool("read_only", mountRequest.TypedSpec().ReadOnly), zap.Bool("secure", mountRequest.TypedSpec().Secure), zap.Bool("disable_access_time", mountRequest.TypedSpec().DisableAccessTime), zap.Bool("detached", mountRequest.TypedSpec().Detached), ) mountCtx = &mountContext{ point: mountpoint, readOnly: mountRequest.TypedSpec().ReadOnly, disableAccessTime: mountRequest.TypedSpec().DisableAccessTime, secure: mountRequest.TypedSpec().Secure, unmounter: manager.Unmount, } ctrl.activeMounts[mountRequest.Metadata().ID()] = mountCtx } if mountCtx.readOnly != mountRequest.TypedSpec().ReadOnly { // remount if needed var err error switch mountRequest.TypedSpec().ReadOnly { case true: err = mountCtx.point.RemountReadOnly() case false: err = mountCtx.point.RemountReadWrite() } if err != nil { return fmt.Errorf("failed to remount %q: %w", mountRequest.Metadata().ID(), err) } logger.Info("volume remounted", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("read_only", fmt.Sprintf("%v -> %v", mountCtx.readOnly, mountRequest.TypedSpec().ReadOnly)), ) mountCtx.readOnly = mountRequest.TypedSpec().ReadOnly } //nolint:dupl if mountCtx.disableAccessTime != mountRequest.TypedSpec().DisableAccessTime { err := mountCtx.point.SetDisableAccessTime(mountRequest.TypedSpec().DisableAccessTime) if err != nil { return fmt.Errorf("failed to update disableAccessTime for %q: %w", mountRequest.Metadata().ID(), err) } logger.Info("volume mount attributes updated", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("disable_access_time", fmt.Sprintf("%v -> %v", mountCtx.disableAccessTime, mountRequest.TypedSpec().DisableAccessTime)), ) mountCtx.disableAccessTime = mountRequest.TypedSpec().DisableAccessTime } //nolint:dupl if mountCtx.secure != mountRequest.TypedSpec().Secure { err := mountCtx.point.SetSecure(mountRequest.TypedSpec().Secure) if err != nil { return fmt.Errorf("failed to update secure for %q: %w", mountRequest.Metadata().ID(), err) } logger.Info("volume mount attributes updated", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("secure", fmt.Sprintf("%v -> %v", mountCtx.secure, mountRequest.TypedSpec().Secure)), ) mountCtx.secure = mountRequest.TypedSpec().Secure } return nil } func (ctrl *MountController) handleOverlayMountOperation( logger *zap.Logger, mountTarget string, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { if _, ok := ctrl.activeMounts[mountRequest.Metadata().ID()]; ok { return nil } if volumeStatus.TypedSpec().ParentID != constants.EphemeralPartitionLabel { return fmt.Errorf("overlay mount is not supported for %q", volumeStatus.TypedSpec().ParentID) } manager := mount.NewVarOverlay( []string{mountTarget}, mountTarget, logger.Sugar().Infof, mount.WithSelinuxLabel(volumeStatus.TypedSpec().MountSpec.SelinuxLabel), ) mountpoint, err := manager.Mount() if err != nil { return fmt.Errorf("failed to mount %q: %w", mountRequest.Metadata().ID(), err) } if err = ctrl.updateTargetSettings(mountTarget, volumeStatus.TypedSpec().Filesystem, volumeStatus.TypedSpec().MountSpec); err != nil { manager.Unmount() //nolint:errcheck return fmt.Errorf("failed to update target settings %q: %w", mountRequest.Metadata().ID(), err) } logger.Info("overlay mount", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("target", mountTarget), zap.String("parent", volumeStatus.TypedSpec().ParentID), ) ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{ point: mountpoint, unmounter: manager.Unmount, } return nil } func (ctrl *MountController) handleSwapMountOperation( logger *zap.Logger, mountSource string, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { _, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if ok { return nil } if err := swap.On(mountSource, swap.FLAG_DISCARD_ONCE); err != nil { return fmt.Errorf("failed to enable swap on %q: %w", mountSource, err) } ctrl.activeMounts[mountRequest.Metadata().ID()] = &mountContext{ point: mount.NewPoint(mountSource, 0, "", 0, "swap"), } logger.Info("swap enabled", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("source", mountSource), ) return nil } func (ctrl *MountController) handleUnmountOperation( logger *zap.Logger, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { switch volumeStatus.TypedSpec().Type { case block.VolumeTypeDirectory: return ctrl.handleDirectoryUnmountOperation(logger, mountRequest, volumeStatus) case block.VolumeTypeTmpfs: return fmt.Errorf("not implemented yet") case block.VolumeTypeExternal: return ctrl.handleDiskUnmountOperation(logger, mountRequest, volumeStatus) case block.VolumeTypeDisk, block.VolumeTypePartition, block.VolumeTypeOverlay: if volumeStatus.TypedSpec().Filesystem == block.FilesystemTypeSwap { return ctrl.handleSwapUmountOperation(logger, mountRequest, volumeStatus) } return ctrl.handleDiskUnmountOperation(logger, mountRequest, volumeStatus) case block.VolumeTypeSymlink: return ctrl.handleSymlinkUmountOperation(mountRequest) default: return fmt.Errorf("unsupported volume type %q", volumeStatus.TypedSpec().Type) } } func (ctrl *MountController) handleDiskUnmountOperation( logger *zap.Logger, mountRequest *block.MountRequest, _ *block.VolumeStatus, ) error { mountCtx, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if !ok { return nil } if err := mountCtx.unmounter(); err != nil { return fmt.Errorf("failed to unmount %q: %w", mountRequest.Metadata().ID(), err) } delete(ctrl.activeMounts, mountRequest.Metadata().ID()) logger.Info("volume unmount", zap.String("volume", mountRequest.Metadata().ID()), zap.String("source", mountCtx.point.Source()), zap.String("target", mountCtx.point.Target()), zap.String("filesystem", mountCtx.point.FSType()), ) return nil } func (ctrl *MountController) handleDirectoryUnmountOperation( logger *zap.Logger, mountRequest *block.MountRequest, _ *block.VolumeStatus, ) error { mountCtx, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if !ok { return nil } if err := mountCtx.unmounter(); err != nil { return fmt.Errorf("failed to unmount %q: %w", mountRequest.Metadata().ID(), err) } delete(ctrl.activeMounts, mountRequest.Metadata().ID()) logger.Info("volume unmount", zap.String("volume", mountRequest.Metadata().ID()), zap.String("source", mountCtx.point.Source()), zap.String("target", mountCtx.point.Target()), ) return nil } func (ctrl *MountController) handleSymlinkUmountOperation( mountRequest *block.MountRequest, ) error { delete(ctrl.activeMounts, mountRequest.Metadata().ID()) return nil } func (ctrl *MountController) handleSwapUmountOperation( logger *zap.Logger, mountRequest *block.MountRequest, volumeStatus *block.VolumeStatus, ) error { mountCtx, ok := ctrl.activeMounts[mountRequest.Metadata().ID()] if !ok { return nil } if err := swap.Off(mountCtx.point.Source()); err != nil { return fmt.Errorf("failed to disable swap on %q: %w", mountCtx.point.Source(), err) } delete(ctrl.activeMounts, mountRequest.Metadata().ID()) logger.Info("swap disabled", zap.String("volume", volumeStatus.Metadata().ID()), zap.String("source", mountCtx.point.Source()), ) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount_request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // MountRequestController provides mount requests based on VolumeMountRequests and VolumeStatuses. type MountRequestController struct{} // Name implements controller.Controller interface. func (ctrl *MountRequestController) Name() string { return "block.MountRequestController" } // Inputs implements controller.Controller interface. func (ctrl *MountRequestController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.MountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *MountRequestController) Outputs() []controller.Output { return []controller.Output{ { Type: block.MountRequestType, Kind: controller.OutputExclusive, }, } } func identity[T any](v T) T { return v } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *MountRequestController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } volumeStatuses, err := safe.ReaderListAll[*block.VolumeStatus](ctx, r) if err != nil { return fmt.Errorf("failed to read volume statuses: %w", err) } volumeStatusMap := xslices.ToMap( safe.ToSlice( volumeStatuses, identity, ), func(v *block.VolumeStatus) (string, *block.VolumeStatus) { return v.Metadata().ID(), v }, ) volumeMountRequests, err := safe.ReaderListAll[*block.VolumeMountRequest](ctx, r) if err != nil { return fmt.Errorf("failed to read volume mount requests: %w", err) } desiredMountRequests := map[string]*block.MountRequestSpec{} for volumeMountRequest := range volumeMountRequests.All() { volumeID := volumeMountRequest.TypedSpec().VolumeID volumeStatus, ok := volumeStatusMap[volumeID] if !ok || volumeStatus.TypedSpec().Phase != block.VolumePhaseReady || volumeStatus.Metadata().Phase() != resource.PhaseRunning { continue } if _, exists := desiredMountRequests[volumeID]; !exists { desiredMountRequests[volumeID] = &block.MountRequestSpec{ VolumeID: volumeID, ReadOnly: volumeMountRequest.TypedSpec().ReadOnly, Detached: volumeMountRequest.TypedSpec().Detached, DisableAccessTime: volumeMountRequest.TypedSpec().DisableAccessTime, Secure: volumeMountRequest.TypedSpec().Secure, } } desiredMountRequest := desiredMountRequests[volumeID] desiredMountRequest.Requesters = append(desiredMountRequest.Requesters, volumeMountRequest.TypedSpec().Requester) desiredMountRequest.RequesterIDs = append(desiredMountRequest.RequesterIDs, volumeMountRequest.Metadata().ID()) // read-only if all requesters are read-only desiredMountRequest.ReadOnly = desiredMountRequest.ReadOnly && volumeMountRequest.TypedSpec().ReadOnly // detached if all requesters are detached desiredMountRequest.Detached = desiredMountRequest.Detached && volumeMountRequest.TypedSpec().Detached // disable access time if any requester wants it disabled desiredMountRequest.DisableAccessTime = desiredMountRequest.DisableAccessTime || volumeMountRequest.TypedSpec().DisableAccessTime // secure if any requester wants it secure desiredMountRequest.Secure = desiredMountRequest.Secure || volumeMountRequest.TypedSpec().Secure desiredMountRequest.ParentMountID = volumeStatus.TypedSpec().MountSpec.ParentID } // list and figure out what to do with existing mount requests mountRequests, err := safe.ReaderListAll[*block.MountRequest](ctx, r) if err != nil { return fmt.Errorf("failed to read mount requests: %w", err) } // perform cleanup of mount requests which should be cleaned up for mountRequest := range mountRequests.All() { tearingDown := mountRequest.Metadata().Phase() == resource.PhaseTearingDown shouldBeDestroyed := desiredMountRequests[mountRequest.Metadata().ID()] == nil if tearingDown || shouldBeDestroyed { okToDestroy, err := r.Teardown(ctx, mountRequest.Metadata()) if err != nil { return fmt.Errorf("failed to teardown mount request %q: %w", mountRequest.Metadata().ID(), err) } if okToDestroy { if err = r.Destroy(ctx, mountRequest.Metadata()); err != nil { return fmt.Errorf("failed to destroy mount request %q: %w", mountRequest.Metadata().ID(), err) } } else if !shouldBeDestroyed { // previous mount request version is still being torn down delete(desiredMountRequests, mountRequest.Metadata().ID()) } } } // create/update mount requests for id, desiredMountRequest := range desiredMountRequests { if err = safe.WriterModify( ctx, r, block.NewMountRequest(block.NamespaceName, id), func(mr *block.MountRequest) error { *mr.TypedSpec() = *desiredMountRequest return nil }, ); err != nil { return fmt.Errorf("failed to create/update mount request %q: %w", id, err) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount_request_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type MountRequestSuite struct { ctest.DefaultSuite } func TestMountRequestSuite(t *testing.T) { t.Parallel() suite.Run(t, &MountRequestSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.MountRequestController{})) }, }, }) } func (suite *MountRequestSuite) TestReconcile() { mountRequest1 := block.NewVolumeMountRequest(block.NamespaceName, "mountRequest1") mountRequest1.TypedSpec().Requester = "requester1" mountRequest1.TypedSpec().VolumeID = "volume1" mountRequest1.TypedSpec().ReadOnly = true suite.Create(mountRequest1) // mount request is not created as the volume is not ready ctest.AssertNoResource[*block.MountRequest](suite, "volume1") volumeStatus1 := block.NewVolumeStatus(block.NamespaceName, "volume1") volumeStatus1.TypedSpec().Phase = block.VolumePhaseWaiting suite.Create(volumeStatus1) // mount request is not created as the volume status is not ready ctest.AssertNoResource[*block.MountRequest](suite, "volume1") volumeStatus1.TypedSpec().Phase = block.VolumePhaseReady suite.Update(volumeStatus1) ctest.AssertResource(suite, "volume1", func(mr *block.MountRequest, asrt *assert.Assertions) { asrt.Equal("volume1", mr.TypedSpec().VolumeID) asrt.True(mr.TypedSpec().ReadOnly) asrt.ElementsMatch([]string{"requester1"}, mr.TypedSpec().Requesters) asrt.ElementsMatch([]string{"mountRequest1"}, mr.TypedSpec().RequesterIDs) }) // add another mount request for the same volume mountRequest2 := block.NewVolumeMountRequest(block.NamespaceName, "mountRequest2") mountRequest2.TypedSpec().Requester = "requester2" mountRequest2.TypedSpec().VolumeID = "volume1" mountRequest2.TypedSpec().ReadOnly = false suite.Create(mountRequest2) ctest.AssertResource(suite, "volume1", func(mr *block.MountRequest, asrt *assert.Assertions) { asrt.Equal("volume1", mr.TypedSpec().VolumeID) asrt.False(mr.TypedSpec().ReadOnly) asrt.ElementsMatch([]string{"requester1", "requester2"}, mr.TypedSpec().Requesters) asrt.ElementsMatch([]string{"mountRequest1", "mountRequest2"}, mr.TypedSpec().RequesterIDs) }) // if the mount request is fulfilled, a finalizer should be added suite.AddFinalizer(block.NewMountRequest(block.NamespaceName, "volume1").Metadata(), "mounted") // try to remove one mount requests now suite.Destroy(mountRequest2) ctest.AssertResource(suite, "volume1", func(mr *block.MountRequest, asrt *assert.Assertions) { asrt.Equal("volume1", mr.TypedSpec().VolumeID) asrt.True(mr.TypedSpec().ReadOnly) asrt.ElementsMatch([]string{"requester1"}, mr.TypedSpec().Requesters) asrt.ElementsMatch([]string{"mountRequest1"}, mr.TypedSpec().RequesterIDs) }) // try to remove another mount request now suite.Destroy(mountRequest1) ctest.AssertResource(suite, "volume1", func(mr *block.MountRequest, asrt *assert.Assertions) { asrt.Equal("volume1", mr.TypedSpec().VolumeID) asrt.True(mr.TypedSpec().ReadOnly) asrt.Equal([]string{"requester1"}, mr.TypedSpec().Requesters) asrt.ElementsMatch([]string{"mountRequest1"}, mr.TypedSpec().RequesterIDs) asrt.Equal(resource.PhaseTearingDown, mr.Metadata().Phase()) }) // remove the finalizer, allowing the mount request to be destroyed suite.RemoveFinalizer(block.NewMountRequest(block.NamespaceName, "volume1").Metadata(), "mounted") ctest.AssertNoResource[*block.MountRequest](suite, "volume1") } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // MountStatusController provides mount requests based on VolumeMountRequests and VolumeStatuses. type MountStatusController struct{} // Name implements controller.Controller interface. func (ctrl *MountStatusController) Name() string { return "block.MountStatusController" } // Inputs implements controller.Controller interface. func (ctrl *MountStatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.MountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *MountStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeMountStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *MountStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } mountStatuses, err := safe.ReaderListAll[*block.MountStatus](ctx, r) if err != nil { return fmt.Errorf("failed to read volume mount requests: %w", err) } for mountStatus := range mountStatuses.All() { switch mountStatus.Metadata().Phase() { case resource.PhaseRunning: // always put our own finalizer if !mountStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, mountStatus.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to add finalizer to mount status %q: %w", mountStatus.Metadata().ID(), err) } } // now "explode" the mount status into volume mount statuses per requester for i, requester := range mountStatus.TypedSpec().Spec.Requesters { requestID := mountStatus.TypedSpec().Spec.RequesterIDs[i] if err = safe.WriterModify( ctx, r, block.NewVolumeMountStatus(block.NamespaceName, requestID), func(vms *block.VolumeMountStatus) error { vms.Metadata().Labels().Set("mount-status-id", mountStatus.Metadata().ID()) vms.TypedSpec().Requester = requester vms.TypedSpec().Target = mountStatus.TypedSpec().Target vms.TypedSpec().VolumeID = mountStatus.TypedSpec().Spec.VolumeID vms.TypedSpec().ReadOnly = mountStatus.TypedSpec().Spec.ReadOnly vms.TypedSpec().Detached = mountStatus.TypedSpec().Detached vms.TypedSpec().DisableAccessTime = mountStatus.TypedSpec().Spec.DisableAccessTime vms.TypedSpec().Secure = mountStatus.TypedSpec().Spec.Secure // This needs to be set through accessor, and is not guaranteed to resolve to a valid root. vms.TypedSpec().SetRoot(mountStatus.TypedSpec().Root()) return nil }, ); err != nil { return fmt.Errorf("failed to create volume mount status %q: %w", requestID, err) } } // now clean up volume mount statuses that do match any existing requesters volumeMountStatuses, err := safe.ReaderListAll[*block.VolumeMountStatus](ctx, r, state.WithLabelQuery(resource.LabelEqual("mount-status-id", mountStatus.Metadata().ID()))) if err != nil { return fmt.Errorf("failed to read volume mount statuses for mount status %q: %w", mountStatus.Metadata().ID(), err) } for volumeMountStatus := range volumeMountStatuses.All() { if slices.Contains(mountStatus.TypedSpec().Spec.RequesterIDs, volumeMountStatus.Metadata().ID()) { // still active continue } okToDestroy, err := r.Teardown(ctx, volumeMountStatus.Metadata()) if err != nil { return fmt.Errorf("failed to teardown volume mount status %q: %w", volumeMountStatus.Metadata().ID(), err) } if okToDestroy { if err = r.Destroy(ctx, volumeMountStatus.Metadata()); err != nil { return fmt.Errorf("failed to destroy volume mount status %q: %w", volumeMountStatus.Metadata().ID(), err) } } } case resource.PhaseTearingDown: // we need to ensure that all volume mount statuses are torn down and destroyed volumeMountStatus, err := safe.ReaderListAll[*block.VolumeMountStatus](ctx, r, state.WithLabelQuery(resource.LabelEqual("mount-status-id", mountStatus.Metadata().ID()))) if err != nil { return fmt.Errorf("failed to read volume mount statuses for mount status %q: %w", mountStatus.Metadata().ID(), err) } allDestroyed := true for volumeMountStatus := range volumeMountStatus.All() { okToDestroy, err := r.Teardown(ctx, volumeMountStatus.Metadata()) if err != nil { return fmt.Errorf("failed to teardown volume mount status %q: %w", volumeMountStatus.Metadata().ID(), err) } if okToDestroy { if err = r.Destroy(ctx, volumeMountStatus.Metadata()); err != nil { return fmt.Errorf("failed to destroy volume mount status %q: %w", volumeMountStatus.Metadata().ID(), err) } } else { allDestroyed = false } } if allDestroyed { // remove our finalizer now if mountStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.RemoveFinalizer(ctx, mountStatus.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to remove finalizer from mount status %q: %w", mountStatus.Metadata().ID(), err) } } } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type MountStatusSuite struct { ctest.DefaultSuite } func TestMountStatusSuite(t *testing.T) { t.Parallel() suite.Run(t, &MountStatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.MountStatusController{})) }, }, }) } func (suite *MountStatusSuite) TestReconcile() { mountStatus1 := block.NewMountStatus(block.NamespaceName, "volume1") mountStatus1.TypedSpec().Spec = block.MountRequestSpec{ VolumeID: "volume1", Requesters: []string{"requester1", "requester2"}, RequesterIDs: []string{"requester1/volume1", "requester2/volume1"}, } mountStatus1.TypedSpec().Target = "/target" suite.Create(mountStatus1) // mount status is exploded into volume mount statuses ctest.AssertResources(suite, []resource.ID{"requester1/volume1", "requester2/volume1"}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.Equal("volume1", vms.Metadata().Labels().Raw()["mount-status-id"]) asrt.Equal("volume1", vms.TypedSpec().VolumeID) asrt.Equal("/target", vms.TypedSpec().Target) }, ) // mount status should now have a finalizer ctest.AssertResource(suite, "volume1", func(ms *block.MountStatus, asrt *assert.Assertions) { asrt.True(ms.Metadata().Finalizers().Has((&blockctrls.MountStatusController{}).Name())) }) // add a finalizer for volume mount status suite.AddFinalizer(block.NewVolumeMountStatus(block.NamespaceName, "requester1/volume1").Metadata(), "test-finalizer") // now, teardown the mount status ready, err := suite.State().Teardown(suite.Ctx(), mountStatus1.Metadata()) suite.Require().NoError(err) suite.Assert().False(ready) // volume mount status without finalizer should be removed ctest.AssertNoResource[*block.VolumeMountStatus](suite, "requester2/volume1") // volume mount status with finalizer should be tearing down ctest.AssertResource(suite, "requester1/volume1", func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.Equal(resource.PhaseTearingDown, vms.Metadata().Phase()) }) // remove finalizer from volume mount status suite.RemoveFinalizer(block.NewVolumeMountStatus(block.NamespaceName, "requester1/volume1").Metadata(), "test-finalizer") // volume mount status should be destroyed ctest.AssertNoResource[*block.VolumeMountStatus](suite, "requester1/volume1") // now the mount status finalizers should be empty as well ctest.AssertResource(suite, "volume1", func(ms *block.MountStatus, asrt *assert.Assertions) { asrt.True(ms.Metadata().Finalizers().Empty()) }) suite.Destroy(mountStatus1) } func (suite *MountStatusSuite) TestReconcileRequesterGoingOut() { mountStatus1 := block.NewMountStatus(block.NamespaceName, "volume1") mountStatus1.TypedSpec().Spec = block.MountRequestSpec{ VolumeID: "volume1", Requesters: []string{"requester1", "requester2"}, RequesterIDs: []string{"requester1/volume1", "requester2/volume1"}, } mountStatus1.TypedSpec().Target = "/target" suite.Create(mountStatus1) // mount status is exploded into volume mount statuses ctest.AssertResources(suite, []resource.ID{"requester1/volume1", "requester2/volume1"}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.Equal("volume1", vms.Metadata().Labels().Raw()["mount-status-id"]) asrt.Equal("volume1", vms.TypedSpec().VolumeID) asrt.Equal("/target", vms.TypedSpec().Target) }, ) // put a finalizer on volume mount status suite.AddFinalizer(block.NewVolumeMountStatus(block.NamespaceName, "requester1/volume1").Metadata(), "test-finalizer") // update the mount status, as if requester1 is no longer mounting it mountStatus1, err := safe.StateGetByID[*block.MountStatus](suite.Ctx(), suite.State(), mountStatus1.Metadata().ID()) suite.Require().NoError(err) mountStatus1.TypedSpec().Spec.Requesters = []string{"requester2"} mountStatus1.TypedSpec().Spec.RequesterIDs = []string{"requester2/volume1"} suite.Update(mountStatus1) // volume mount status with finalizer should be tearing down ctest.AssertResource(suite, "requester1/volume1", func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.Equal(resource.PhaseTearingDown, vms.Metadata().Phase()) }) // remove finalizer from volume mount status suite.RemoveFinalizer(block.NewVolumeMountStatus(block.NamespaceName, "requester1/volume1").Metadata(), "test-finalizer") // volume mount status should be destroyed ctest.AssertNoResource[*block.VolumeMountStatus](suite, "requester1/volume1") } ================================================ FILE: internal/app/machined/pkg/controllers/block/mount_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type MountSuite struct { ctest.DefaultSuite } func TestMountSuite(t *testing.T) { t.Parallel() suite.Run(t, &MountSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.MountController{})) }, }, }) } func (suite *MountSuite) mountVolume(volumeID string) { //nolint:unparam mountRequest := block.NewMountRequest(block.NamespaceName, volumeID) mountRequest.TypedSpec().RequesterIDs = []string{"requester1/" + volumeID} mountRequest.TypedSpec().Requesters = []string{"requester1"} mountRequest.TypedSpec().VolumeID = volumeID suite.Create(mountRequest) // wait for the mount status to be created ctest.AssertResource(suite, volumeID, func(*block.MountStatus, *assert.Assertions) {}) } func (suite *MountSuite) TestSymlinkNew() { dir := suite.T().TempDir() targetPath := filepath.Join(dir, "target") volumeStatus := block.NewVolumeStatus(block.NamespaceName, "volume1") volumeStatus.TypedSpec().Type = block.VolumeTypeSymlink volumeStatus.TypedSpec().SymlinkSpec = block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/run", Force: true, } volumeStatus.TypedSpec().MountSpec = block.MountSpec{ TargetPath: targetPath, } volumeStatus.TypedSpec().Phase = block.VolumePhaseReady suite.Create(volumeStatus) suite.mountVolume("volume1") // verify symlink path, err := os.Readlink(targetPath) suite.Require().NoError(err) suite.Assert().Equal("/run", path) } func (suite *MountSuite) TestSymlinkExists() { dir := suite.T().TempDir() targetPath := filepath.Join(dir, "target") // symlink already exists suite.Require().NoError(os.Symlink("/run", targetPath)) volumeStatus := block.NewVolumeStatus(block.NamespaceName, "volume1") volumeStatus.TypedSpec().Type = block.VolumeTypeSymlink volumeStatus.TypedSpec().SymlinkSpec = block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/run", } volumeStatus.TypedSpec().MountSpec = block.MountSpec{ TargetPath: targetPath, } volumeStatus.TypedSpec().Phase = block.VolumePhaseReady suite.Create(volumeStatus) suite.mountVolume("volume1") // verify symlink path, err := os.Readlink(targetPath) suite.Require().NoError(err) suite.Assert().Equal("/run", path) } func (suite *MountSuite) TestSymlinkWrong() { dir := suite.T().TempDir() targetPath := filepath.Join(dir, "target") // wrong symlink target suite.Require().NoError(os.Symlink("/foo", targetPath)) volumeStatus := block.NewVolumeStatus(block.NamespaceName, "volume1") volumeStatus.TypedSpec().Type = block.VolumeTypeSymlink volumeStatus.TypedSpec().SymlinkSpec = block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/run", Force: true, } volumeStatus.TypedSpec().MountSpec = block.MountSpec{ TargetPath: targetPath, } volumeStatus.TypedSpec().Phase = block.VolumePhaseReady suite.Create(volumeStatus) suite.mountVolume("volume1") // verify symlink path, err := os.Readlink(targetPath) suite.Require().NoError(err) suite.Assert().Equal("/run", path) } func (suite *MountSuite) TestSymlinkDirectory() { dir := suite.T().TempDir() targetPath := filepath.Join(dir, "target") // non-empty directory structure suite.Require().NoError(os.Mkdir(targetPath, 0o755)) suite.Require().NoError(os.Mkdir(filepath.Join(targetPath, "foo"), 0o755)) volumeStatus := block.NewVolumeStatus(block.NamespaceName, "volume1") volumeStatus.TypedSpec().Type = block.VolumeTypeSymlink volumeStatus.TypedSpec().SymlinkSpec = block.SymlinkProvisioningSpec{ SymlinkTargetPath: "/run", Force: true, } volumeStatus.TypedSpec().MountSpec = block.MountSpec{ TargetPath: targetPath, } volumeStatus.TypedSpec().Phase = block.VolumePhaseReady suite.Create(volumeStatus) suite.mountVolume("volume1") // verify symlink path, err := os.Readlink(targetPath) suite.Require().NoError(err) suite.Assert().Equal("/run", path) } ================================================ FILE: internal/app/machined/pkg/controllers/block/swap_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bufio" "bytes" "context" "fmt" "os" "strconv" "strings" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/dustin/go-humanize" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // SwapStatusController provides a view of active swap devices. type SwapStatusController struct { V1Alpha1Mode machineruntime.Mode ProcSwapsPath string } // Name implements controller.Controller interface. func (ctrl *SwapStatusController) Name() string { return "block.SwapStatusController" } // Inputs implements controller.Controller interface. func (ctrl *SwapStatusController) Inputs() []controller.Input { return []controller.Input{ { // not really a dependency, but we refresh swap status on mount status change Namespace: block.NamespaceName, Type: block.MountStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SwapStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: block.SwapStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *SwapStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // in container mode, no swap applies if ctrl.V1Alpha1Mode == machineruntime.ModeContainer { return nil } if ctrl.ProcSwapsPath == "" { ctrl.ProcSwapsPath = "/proc/swaps" } // there is no way to watch for swap devices, so we are going to poll every minute ticker := time.NewTicker(time.Minute) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ticker.C: } r.StartTrackingOutputs() if err := ctrl.parseSwaps(ctx, r); err != nil { return fmt.Errorf("failed to parse swaps: %w", err) } if err := safe.CleanupOutputs[*block.SwapStatus](ctx, r); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } func (ctrl *SwapStatusController) parseSwaps(ctx context.Context, r controller.ReaderWriter) error { swapsContent, err := os.ReadFile(ctrl.ProcSwapsPath) if err != nil { return fmt.Errorf("failed to read %q: %w", ctrl.ProcSwapsPath, err) } scanner := bufio.NewScanner(bytes.NewReader(swapsContent)) // skip the first line, it contains headers if !scanner.Scan() { return fmt.Errorf("failed to read header line from %q: %w", ctrl.ProcSwapsPath, scanner.Err()) } for scanner.Scan() { line := scanner.Text() fields := strings.Fields(line) if len(fields) < 5 { return fmt.Errorf("invalid swap line in %q: %q", ctrl.ProcSwapsPath, line) } swapDevice := fields[0] if err = safe.WriterModify(ctx, r, block.NewSwapStatus(block.NamespaceName, swapDevice), func(swapStatus *block.SwapStatus) error { swapStatus.TypedSpec().Device = swapDevice swapStatus.TypedSpec().Type = fields[1] size, err := strconv.ParseUint(fields[2], 10, 64) if err != nil { return fmt.Errorf("failed to parse size from %q: %w", fields[2], err) } swapStatus.TypedSpec().SizeBytes = size * 1024 // convert from KiB to bytes used, err := strconv.ParseUint(fields[3], 10, 64) if err != nil { return fmt.Errorf("failed to parse used from %q: %w", fields[3], err) } swapStatus.TypedSpec().UsedBytes = used * 1024 // convert from KiB to bytes swapStatus.TypedSpec().SizeHuman = humanize.IBytes(swapStatus.TypedSpec().SizeBytes) swapStatus.TypedSpec().UsedHuman = humanize.IBytes(swapStatus.TypedSpec().UsedBytes) priority, err := strconv.ParseInt(fields[4], 10, 32) if err != nil { return fmt.Errorf("failed to parse priority from %q: %w", fields[4], err) } swapStatus.TypedSpec().Priority = int32(priority) return nil }, ); err != nil { return fmt.Errorf("failed to modify swap status for %q: %w", swapDevice, err) } } if err := scanner.Err(); err != nil { return fmt.Errorf("failed to read swaps from %q: %w", ctrl.ProcSwapsPath, err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/swap_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( _ "embed" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type SwapStatusSuite struct { ctest.DefaultSuite } func TestSwapStatusSuite(t *testing.T) { t.Parallel() suite.Run(t, &SwapStatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) {}, }, }) } //go:embed "testdata/procswaps.txt" var procSwapsData []byte func (suite *SwapStatusSuite) TestReconcile() { tmpDir := suite.T().TempDir() path := filepath.Join(tmpDir, "procswaps.txt") suite.Require().NoError(os.WriteFile(path, procSwapsData, 0o644)) suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.SwapStatusController{ ProcSwapsPath: path, })) ctest.AssertResources(suite, []string{"/dev/vda1", "/dev/vda2"}, func(s *block.SwapStatus, asrt *assert.Assertions) { asrt.Equal("partition", s.TypedSpec().Type) switch s.Metadata().ID() { case "/dev/vda1": asrt.Equal("/dev/vda1", s.TypedSpec().Device) asrt.EqualValues(524280*1024, s.TypedSpec().SizeBytes) asrt.Equal("512 MiB", s.TypedSpec().SizeHuman) asrt.EqualValues(1024*1024, s.TypedSpec().UsedBytes) asrt.Equal("1.0 MiB", s.TypedSpec().UsedHuman) asrt.EqualValues(-1, s.TypedSpec().Priority) case "/dev/vda2": asrt.Equal("/dev/vda2", s.TypedSpec().Device) asrt.EqualValues(2*1024*1024, s.TypedSpec().SizeBytes) asrt.Equal("2.0 MiB", s.TypedSpec().SizeHuman) asrt.EqualValues(0, s.TypedSpec().UsedBytes) asrt.Equal("0 B", s.TypedSpec().UsedHuman) asrt.EqualValues(-2, s.TypedSpec().Priority) } }) } ================================================ FILE: internal/app/machined/pkg/controllers/block/symlinks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "slices" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/inotify" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // SymlinksController provides a view of symlinks created by udevd to the blockdevices. type SymlinksController struct{} // Name implements controller.Controller interface. func (ctrl *SymlinksController) Name() string { return "block.SymlinksController" } // Inputs implements controller.Controller interface. func (ctrl *SymlinksController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("udevd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SymlinksController) Outputs() []controller.Output { return []controller.Output{ { Type: block.SymlinkType, Kind: controller.OutputExclusive, }, } } const ( baseDevDiskPath = "/dev/disk" tempSymlinkPrefix = ".#" ) // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *SymlinksController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy & running for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } udevdService, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "udevd") if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get udevd service: %w", err) } if udevdService.TypedSpec().Healthy && udevdService.TypedSpec().Running { break } } // start the inotify watcher inotifyWatcher, err := inotify.NewWatcher() if err != nil { return fmt.Errorf("failed to create inotify watcher: %w", err) } defer inotifyWatcher.Close() //nolint:errcheck inotifyCh, inotifyErrCh := inotifyWatcher.Run() // build initial list of symlinks // // map of path -> destination detectedSymlinks := map[string]string{} // get list of subpaths under /dev/disk if err = ctrl.handleDir(logger, inotifyWatcher, detectedSymlinks, baseDevDiskPath); err != nil { return err } if err = ctrl.updateOutputs(ctx, r, detectedSymlinks); err != nil { return err } // now wait for inotify events for { select { case <-ctx.Done(): return nil case updatedPath := <-inotifyCh: logger.Debug("inotify event", zap.String("path", updatedPath)) st, err := os.Stat(updatedPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { delete(detectedSymlinks, updatedPath) } else { return fmt.Errorf("failed to stat %q: %w", updatedPath, err) } } else { if st.IsDir() { if err = ctrl.handleDir(logger, inotifyWatcher, detectedSymlinks, updatedPath); err != nil { return err } } else { dest, err := os.Readlink(updatedPath) if err != nil { if errors.Is(err, fs.ErrNotExist) || errors.Is(err, unix.EINVAL) { delete(detectedSymlinks, updatedPath) } else { return fmt.Errorf("failed to readlink %q: %w", updatedPath, err) } } else if !strings.HasPrefix(filepath.Base(updatedPath), tempSymlinkPrefix) { detectedSymlinks[updatedPath] = dest } } } case watchErr := <-inotifyErrCh: return fmt.Errorf("inotify watcher failed: %w", watchErr) } if err = ctrl.updateOutputs(ctx, r, detectedSymlinks); err != nil { return err } } } func (ctrl *SymlinksController) updateOutputs(ctx context.Context, r controller.Runtime, detectedSymlinks map[string]string) error { r.StartTrackingOutputs() deviceToSymlinks := map[string][]string{} for path, dest := range detectedSymlinks { devicePath := filepath.Base(dest) deviceToSymlinks[devicePath] = append(deviceToSymlinks[devicePath], path) } for devicePath := range deviceToSymlinks { slices.Sort(deviceToSymlinks[devicePath]) } for devicePath, symlinks := range deviceToSymlinks { if err := safe.WriterModify(ctx, r, block.NewSymlink(block.NamespaceName, devicePath), func(symlink *block.Symlink) error { symlink.TypedSpec().Paths = symlinks return nil }); err != nil { return fmt.Errorf("failed to update symlink %q: %w", devicePath, err) } } return safe.CleanupOutputs[*block.Symlink](ctx, r) } //nolint:gocyclo func (ctrl *SymlinksController) handleDir(logger *zap.Logger, inotifyWatcher *inotify.Watcher, detectedSymlinks map[string]string, path string) error { if err := inotifyWatcher.Add(path, unix.IN_CREATE|unix.IN_DELETE|unix.IN_MOVE); err != nil { logger.Debug("failed to add inotify watch", zap.String("path", path), zap.Error(err)) if !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("failed to add inotify watch for %q: %w", path, err) } } logger.Debug("processing directory", zap.String("path", path)) entries, err := os.ReadDir(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return fmt.Errorf("failed to read directory %q: %w", path, err) } for _, entry := range entries { fullPath := filepath.Join(path, entry.Name()) st, err := os.Stat(fullPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return fmt.Errorf("failed to stat %q: %w", fullPath, err) } if st.IsDir() { if err = ctrl.handleDir(logger, inotifyWatcher, detectedSymlinks, fullPath); err != nil { return err } } else { dest, err := os.Readlink(fullPath) if err != nil { if errors.Is(err, fs.ErrNotExist) || errors.Is(err, unix.EINVAL) { continue } return fmt.Errorf("failed to readlink %q: %w", fullPath, err) } if !strings.HasPrefix(entry.Name(), tempSymlinkPrefix) { detectedSymlinks[fullPath] = dest } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/system_disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // SystemDiskController provides a detailed view of blockdevices of type 'disk'. type SystemDiskController struct{} // Name implements controller.Controller interface. func (ctrl *SystemDiskController) Name() string { return "block.SystemDiskController" } // Inputs implements controller.Controller interface. func (ctrl *SystemDiskController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.DiscoveredVolumeType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SystemDiskController) Outputs() []controller.Output { return []controller.Output{ { Type: block.SystemDiskType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *SystemDiskController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } discoveredVolumes, err := safe.ReaderListAll[*block.DiscoveredVolume](ctx, r) if err != nil { return fmt.Errorf("failed to list discovered volumes: %w", err) } var ( systemDiskID string systemDiskPath string ) for volume := range discoveredVolumes.All() { if volume.TypedSpec().PartitionLabel == constants.MetaPartitionLabel { systemDiskID = volume.TypedSpec().Parent systemDiskPath = volume.TypedSpec().ParentDevPath break } } if systemDiskID != "" { if err = safe.WriterModify(ctx, r, block.NewSystemDisk(block.NamespaceName, block.SystemDiskID), func(d *block.SystemDisk) error { d.TypedSpec().DiskID = systemDiskID d.TypedSpec().DevPath = systemDiskPath return nil }); err != nil { return fmt.Errorf("failed to write system disk: %w", err) } } else { if err = r.Destroy(ctx, block.NewSystemDisk(block.NamespaceName, block.SystemDiskID).Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to destroy system disk: %w", err) } } } } ================================================ FILE: internal/app/machined/pkg/controllers/block/system_disk_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) type SystemDiskSuite struct { ctest.DefaultSuite } func TestSystemDiskSuite(t *testing.T) { t.Parallel() suite.Run(t, &SystemDiskSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.SystemDiskController{})) }, }, }) } func (suite *SystemDiskSuite) TestReconcile() { ctest.AssertNoResource[*block.SystemDisk](suite, block.SystemDiskID) discoveredVolume := block.NewDiscoveredVolume(block.NamespaceName, "vda4") discoveredVolume.TypedSpec().PartitionLabel = constants.MetaPartitionLabel discoveredVolume.TypedSpec().Parent = "vda" discoveredVolume.TypedSpec().ParentDevPath = "/dev/vda" suite.Require().NoError(suite.State().Create(suite.Ctx(), discoveredVolume)) ctest.AssertResource(suite, block.SystemDiskID, func(r *block.SystemDisk, asrt *assert.Assertions) { asrt.Equal("vda", r.TypedSpec().DiskID) asrt.Equal("/dev/vda", r.TypedSpec().DevPath) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), discoveredVolume.Metadata())) ctest.AssertNoResource[*block.SystemDisk](suite, block.SystemDiskID) } ================================================ FILE: internal/app/machined/pkg/controllers/block/testdata/procswaps.txt ================================================ Filename Type Size Used Priority /dev/vda1 partition 524280 1024 -1 /dev/vda2 partition 2048 0 -2 ================================================ FILE: internal/app/machined/pkg/controllers/block/user_disk_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "path/filepath" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" machineconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // UserDiskConfigController provides volume configuration based on Talos v1alpha1 user disks. type UserDiskConfigController struct{} // Name implements controller.Controller interface. func (ctrl *UserDiskConfigController) Name() string { return "block.UserDiskConfigController" } // Inputs implements controller.Controller interface. func (ctrl *UserDiskConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *UserDiskConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeConfigType, Kind: controller.OutputShared, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, { Type: block.UserDiskConfigStatusType, Kind: controller.OutputExclusive, }, } } func diskPathMatch(devicePath string) cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("disk.dev_path == '%s'", devicePath), celenv.DiskLocator())) } func partitionIdxMatch(devicePath string, partitionIdx int) cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(fmt.Sprintf("volume.parent_dev_path == '%s' && volume.partition_index == %du", devicePath, partitionIdx), celenv.VolumeLocator())) } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *UserDiskConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching machine configuration") } r.StartTrackingOutputs() configurationPresent := cfg != nil && cfg.Config().Machine() != nil status := block.NewUserDiskConfigStatus(block.NamespaceName, block.UserDiskConfigStatusID) status.TypedSpec().Ready = configurationPresent status.TypedSpec().TornDown = true if configurationPresent { // user disks for _, disk := range cfg.Config().Machine().Disks() { result, err := ctrl.processUserDisk(ctx, r, disk) if err != nil { return fmt.Errorf("error processing user disk %s: %w", disk.Device(), err) } status.TypedSpec().Ready = status.TypedSpec().Ready && result.ready status.TypedSpec().TornDown = status.TypedSpec().TornDown && result.tornDown } } if err = safe.CleanupOutputs[*block.VolumeConfig](ctx, r); err != nil { return fmt.Errorf("error cleaning up volume configuration: %w", err) } if configurationPresent { if err = safe.WriterModify(ctx, r, block.NewUserDiskConfigStatus(block.NamespaceName, block.UserDiskConfigStatusID), func(udcs *block.UserDiskConfigStatus) error { *udcs.TypedSpec() = *status.TypedSpec() return nil }, ); err != nil { return fmt.Errorf("error creating user disk configuration status: %w", err) } } } } type userDiskResult struct { ready bool tornDown bool } func (ctrl *UserDiskConfigController) processUserDisk(ctx context.Context, r controller.ReaderWriter, disk machineconfig.Disk) (*userDiskResult, error) { device := disk.Device() resolvedDevicePath, err := filepath.EvalSymlinks(device) if err != nil { return nil, fmt.Errorf("error resolving device path: %w", err) } overallResult := &userDiskResult{ ready: true, tornDown: true, } for idx, part := range disk.Partitions() { result, err := ctrl.processUserDiskPartition(ctx, r, device, idx, part, resolvedDevicePath) if err != nil { return nil, fmt.Errorf("error processing user disk partition %s: %w", part.MountPoint(), err) } overallResult.ready = overallResult.ready && result.ready overallResult.tornDown = overallResult.tornDown && result.tornDown } return overallResult, nil } //nolint:gocyclo,cyclop func (ctrl *UserDiskConfigController) processUserDiskPartition( ctx context.Context, r controller.ReaderWriter, device string, idx int, part machineconfig.Partition, resolvedDevicePath string, ) (*userDiskResult, error) { id := fmt.Sprintf("%s-%d", device, idx+1) // volume configuration if err := safe.WriterModify(ctx, r, block.NewVolumeConfig(block.NamespaceName, id), func(vc *block.VolumeConfig) error { vc.Metadata().Labels().Set(block.UserDiskLabel, "") vc.TypedSpec().Type = block.VolumeTypePartition vc.TypedSpec().Provisioning = block.ProvisioningSpec{ // it's crucial to keep the order of provisioning locked within each disk, otherwise // provisioning might order them different way, and create partitions in wrong order // the matcher on partition idx would then discover partitions in wrong order, and mount them // in wrong order Wave: block.WaveLegacyUserDisks + idx, DiskSelector: block.DiskSelector{ Match: diskPathMatch(resolvedDevicePath), }, PartitionSpec: block.PartitionSpec{ MinSize: part.Size(), MaxSize: part.Size(), TypeUUID: partition.LinuxFilesystemData, }, FilesystemSpec: block.FilesystemSpec{ Type: block.FilesystemTypeXFS, }, } vc.TypedSpec().Locator = block.LocatorSpec{ Match: partitionIdxMatch(resolvedDevicePath, idx+1), } targetPath := part.MountPoint() parentID := "" // machine configuration doesn't enforce any specific mount point for user disks, // so we don't do any more thorough validation here if strings.HasPrefix(targetPath, "/var/") { parentID = constants.EphemeralPartitionLabel targetPath = strings.TrimPrefix(targetPath, "/var/") } vc.TypedSpec().Mount = block.MountSpec{ TargetPath: targetPath, ParentID: parentID, SelinuxLabel: constants.EphemeralSelinuxLabel, FileMode: 0o755, UID: 0, GID: 0, } return nil }, ); err != nil { return nil, fmt.Errorf("error creating user disk volume configuration: %w", err) } // figure out if we want to create the mount of to tear it down volumeMountStatus, err := safe.ReaderGetByID[*block.VolumeMountStatus](ctx, r, id) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error fetching volume mount status: %w", err) } volumeMountRequest, err := safe.ReaderGetByID[*block.VolumeMountRequest](ctx, r, id) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error fetching volume mount request: %w", err) } shouldTearDown := (volumeMountStatus != nil && volumeMountStatus.Metadata().Phase() == resource.PhaseTearingDown) || (volumeMountRequest != nil && volumeMountRequest.Metadata().Phase() == resource.PhaseTearingDown) if !shouldTearDown { // create volume mount request if err = safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, id), func(vmr *block.VolumeMountRequest) error { vmr.TypedSpec().Requester = ctrl.Name() vmr.TypedSpec().VolumeID = id return nil }, ); err != nil { return nil, fmt.Errorf("error creating volume mount request: %w", err) } if volumeMountStatus == nil { // not mounted yet return &userDiskResult{}, nil } if !volumeMountStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, volumeMountStatus.Metadata(), ctrl.Name()); err != nil { return nil, fmt.Errorf("error adding finalizer to volume mount status: %w", err) } } // ready return &userDiskResult{ ready: true, }, nil } // tear down volume mount request if volumeMountStatus != nil && volumeMountStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.RemoveFinalizer(ctx, volumeMountStatus.Metadata(), ctrl.Name()); err != nil { return nil, fmt.Errorf("error removing finalizer from volume mount status: %w", err) } } if volumeMountRequest == nil { // already torn down return &userDiskResult{ tornDown: true, }, nil } okToDestroy, err := r.Teardown(ctx, volumeMountRequest.Metadata()) if err != nil { if state.IsNotFoundError(err) { return &userDiskResult{ tornDown: true, }, nil } return nil, fmt.Errorf("error tearing down volume mount request: %w", err) } if !okToDestroy { return &userDiskResult{}, nil } if err = r.Destroy(ctx, volumeMountRequest.Metadata()); err != nil { return nil, fmt.Errorf("error destroying volume mount request: %w", err) } return &userDiskResult{ tornDown: true, }, nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/user_disk_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "net/url" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) type UserDiskConfigSuite struct { ctest.DefaultSuite } func TestUserDiskConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &UserDiskConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&blockctrls.UserDiskConfigController{})) }, }, }) } func (suite *UserDiskConfigSuite) TestReconcileDefaults() { ctest.AssertNoResource[*block.UserDiskConfigStatus](suite, block.UserDiskConfigStatusID) // create a dummy machine config u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // now the volume config should be created ctest.AssertResource(suite, block.UserDiskConfigStatusID, func(r *block.UserDiskConfigStatus, asrt *assert.Assertions) { asrt.True(r.TypedSpec().Ready) }) } func (suite *UserDiskConfigSuite) TestReconcileUserDisk() { ctest.AssertNoResource[*block.UserDiskConfigStatus](suite, block.UserDiskConfigStatusID) dir := suite.T().TempDir() disk1, disk2 := filepath.Join(dir, "disk1"), filepath.Join(dir, "disk2") suite.Require().NoError(os.WriteFile(disk1, nil, 0o644)) suite.Require().NoError(os.WriteFile(disk2, nil, 0o644)) // create a machine config with user disks u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineDisks: []*v1alpha1.MachineDisk{ { DeviceName: disk1, DiskPartitions: []*v1alpha1.DiskPartition{ { DiskSize: 1024 * 1024, DiskMountPoint: "/var/1-1", }, { DiskSize: 1024 * 1024, DiskMountPoint: "/var/1-2", }, }, }, { DeviceName: disk2, DiskPartitions: []*v1alpha1.DiskPartition{ { DiskSize: 1024 * 1024, DiskMountPoint: "/var/2-1", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // now the volume config should be created for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { ctest.AssertResource(suite, id, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) asrt.Contains(r.Metadata().Labels().Raw(), block.UserDiskLabel) asrt.GreaterOrEqual(r.TypedSpec().Provisioning.Wave, block.WaveLegacyUserDisks) asrt.Equal(constants.EphemeralPartitionLabel, r.TypedSpec().Mount.ParentID) asrt.NotContains(r.TypedSpec().Mount.TargetPath, "/") // path should become relative }) } // .. and a volume mount request for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { ctest.AssertResource(suite, id, func(r *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(id, r.TypedSpec().VolumeID) }) } // the status should not be ready (yet) ctest.AssertResource(suite, block.UserDiskConfigStatusID, func(r *block.UserDiskConfigStatus, asrt *assert.Assertions) { asrt.False(r.TypedSpec().Ready) }) // now emulate that the mount requests are fulfilled for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, id) suite.Create(volumeMountStatus) suite.AddFinalizer(block.NewVolumeMountRequest(block.NamespaceName, id).Metadata(), "test") } // the controller should put a finalizer on the mount status for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { ctest.AssertResource(suite, id, func(r *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(r.Metadata().Finalizers().Has((&blockctrls.UserDiskConfigController{}).Name())) }) } // now everything should be ready ctest.AssertResource(suite, block.UserDiskConfigStatusID, func(r *block.UserDiskConfigStatus, asrt *assert.Assertions) { asrt.True(r.TypedSpec().Ready) asrt.False(r.TypedSpec().TornDown) }) // start tearing down volume mount status for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { _, err := suite.State().Teardown(suite.Ctx(), block.NewVolumeMountStatus(block.NamespaceName, id).Metadata()) suite.Require().NoError(err) } // back to not ready ctest.AssertResource(suite, block.UserDiskConfigStatusID, func(r *block.UserDiskConfigStatus, asrt *assert.Assertions) { asrt.False(r.TypedSpec().Ready) asrt.False(r.TypedSpec().TornDown) }) // the finalizers on mount statuses should be removed for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { ctest.AssertResource(suite, id, func(r *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(r.Metadata().Finalizers().Empty()) }) } // remove the finalizer from the mount request for _, id := range []string{disk1 + "-1", disk1 + "-2", disk2 + "-1"} { suite.RemoveFinalizer(block.NewVolumeMountRequest(block.NamespaceName, id).Metadata(), "test") } } ================================================ FILE: internal/app/machined/pkg/controllers/block/volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bytes" "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes/volumeconfig" machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // VolumeConfigController provides volume configuration based on Talos defaults and machine configuration. type VolumeConfigController struct { V1Alpha1Mode machinedruntime.Mode MetaProvider volumeconfig.MetaProvider } // Name implements controller.Controller interface. func (ctrl *VolumeConfigController) Name() string { return "block.VolumeConfigController" } // Inputs implements controller.Controller interface. func (ctrl *VolumeConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MetaKeyType, ID: optional.Some(runtime.MetaKeyTagToID(meta.StateEncryptionConfig)), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, { Namespace: block.NamespaceName, Type: block.VolumeConfigType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *VolumeConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeConfigType, Kind: controller.OutputShared, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *VolumeConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { //nolint:gocyclo for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } // create a volume mount request for the root user volume mount point // to keep it alive and prevent it from being torn down if err := safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, constants.UserVolumeMountPoint), func(v *block.VolumeMountRequest) error { v.TypedSpec().Requester = ctrl.Name() v.TypedSpec().VolumeID = constants.UserVolumeMountPoint return nil }, ); err != nil { return fmt.Errorf("error creating volume mount request for user volume mount point: %w", err) } machineCfg, encryptionMeta, err := ctrl.loadConfiguration(ctx, r) if err != nil { return err } var cfg configconfig.Config if machineCfg != nil { cfg = machineCfg.Config() } if err := ctrl.setupStateEncryption(ctx, logger, cfg); err != nil { return err } transformers := append(volumeconfig.GetSystemVolumeTransformers(ctx, encryptionMeta, ctrl.V1Alpha1Mode.InContainer(), ctrl.V1Alpha1Mode.IsAgent()), volumeconfig.UserVolumeTransformers...) var resources []volumeconfig.VolumeResource for _, transformer := range transformers { r, err := transformer(cfg) if err != nil { return err } resources = append(resources, r...) } volumeConfigsByID, volumeMountRequestsByID, err := ctrl.getExistingVolumes(ctx, r) if err != nil { return fmt.Errorf("error getting existing user volumes: %w", err) } for _, resource := range resources { if err := ctrl.createVolume(ctx, r, resource, volumeConfigsByID, volumeMountRequestsByID); err != nil { return fmt.Errorf("error creating volumes: %w", err) } } if err := ctrl.cleanupUnusedVolumes(ctx, r, volumeConfigsByID, volumeMountRequestsByID); err != nil { return fmt.Errorf("error cleaning up unused volumes: %w", err) } } } func (ctrl *VolumeConfigController) setupStateEncryption(ctx context.Context, l *zap.Logger, cfg configconfig.Config) error { //nolint:gocyclo if cfg == nil || cfg.Machine() == nil || ctrl.V1Alpha1Mode.InContainer() { return nil } extraVolumeConfig, _ := cfg.Volumes().ByName(constants.StatePartitionLabel) encryptionConfig := extraVolumeConfig.Encryption() if encryptionConfig == nil { // fall back to v1alpha1 encryption config encryptionConfig = cfg.Machine().SystemDiskEncryption().Get(constants.StatePartitionLabel) } metaEncryptionConfig, err := volumes.MarshalEncryptionMeta(encryptionConfig) if err != nil { return fmt.Errorf("error marshaling encryption config for %s: %w", constants.StatePartitionLabel, err) } previous, ok := ctrl.MetaProvider.Meta().ReadTagBytes(meta.StateEncryptionConfig) if ok && bytes.Equal(previous, metaEncryptionConfig) { return nil } ok, err = ctrl.MetaProvider.Meta().SetTagBytes(ctx, meta.StateEncryptionConfig, metaEncryptionConfig) if err != nil { return fmt.Errorf("error setting meta tag %d: %w", meta.StateEncryptionConfig, err) } if !ok { return errors.New("failed to save state encryption config to meta") } if err = ctrl.MetaProvider.Meta().Flush(); err != nil { return fmt.Errorf("error flushing meta: %w", err) } l.Info("saved state encryption config to META") return nil } func (ctrl *VolumeConfigController) loadConfiguration(ctx context.Context, r controller.Runtime) (*config.MachineConfig, *runtime.MetaKey, error) { // load config if present machineCfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return nil, nil, fmt.Errorf("error fetching machine configuration: %w", err) } // load STATE encryption meta key encryptionMeta, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.StateEncryptionConfig)) if err != nil && !state.IsNotFoundError(err) { return nil, nil, fmt.Errorf("error fetching state encryption meta key: %w", err) } return machineCfg, encryptionMeta, nil } func (ctrl *VolumeConfigController) createVolume( ctx context.Context, r controller.ReaderWriter, rsrc volumeconfig.VolumeResource, volumeConfigsByID map[string]*block.VolumeConfig, volumeMountRequestsByID map[string]*block.VolumeMountRequest, ) error { volumeConfig := volumeConfigsByID[rsrc.VolumeID] volumeMountRequest := volumeMountRequestsByID[rsrc.VolumeID] tearingDown := (volumeConfig != nil && volumeConfig.Metadata().Phase() == resource.PhaseTearingDown) || (volumeMountRequest != nil && volumeMountRequest.Metadata().Phase() == resource.PhaseTearingDown) // if the volume is being torn down, do the tear down (in the next loop) if tearingDown { return nil } delete(volumeConfigsByID, rsrc.VolumeID) delete(volumeMountRequestsByID, rsrc.VolumeID) if err := safe.WriterModify(ctx, r, block.NewVolumeConfig(block.NamespaceName, rsrc.VolumeID), func(vc *block.VolumeConfig) error { if rsrc.Label != "" { vc.Metadata().Labels().Set(rsrc.Label, "") } return rsrc.TransformFunc(vc) }); err != nil { return fmt.Errorf("error creating volume %s: %w", rsrc.VolumeID, err) } if rsrc.MountTransformFunc != nil { if err := safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, rsrc.VolumeID), func(v *block.VolumeMountRequest) error { v.Metadata().Labels().Set(block.UserVolumeLabel, "") v.TypedSpec().Requester = ctrl.Name() v.TypedSpec().VolumeID = rsrc.VolumeID return rsrc.MountTransformFunc(v) }); err != nil && !xerrors.TagIs[volumeconfig.SkipUserVolumeMountRequest](err) { return fmt.Errorf("error creating volume mount request: %w", err) } } return nil } // getExistingVolumes retrieves existing volume configurations and mount requests. func (ctrl *VolumeConfigController) getExistingVolumes(ctx context.Context, r controller.Runtime) (map[string]*block.VolumeConfig, map[string]*block.VolumeMountRequest, error) { labelQuery := []state.ListOption{ state.WithLabelQuery(resource.LabelExists(block.SystemVolumeLabel)), state.WithLabelQuery(resource.LabelExists(block.UserVolumeLabel)), state.WithLabelQuery(resource.LabelExists(block.RawVolumeLabel)), state.WithLabelQuery(resource.LabelExists(block.ExistingVolumeLabel)), state.WithLabelQuery(resource.LabelExists(block.ExternalVolumeLabel)), state.WithLabelQuery(resource.LabelExists(block.SwapVolumeLabel)), } volumeConfigs, err := safe.ReaderListAll[*block.VolumeConfig](ctx, r, labelQuery...) if err != nil { return nil, nil, fmt.Errorf("error fetching volume configs: %w", err) } volumeMountRequests, err := safe.ReaderListAll[*block.VolumeMountRequest](ctx, r, labelQuery...) if err != nil { return nil, nil, fmt.Errorf("error fetching volume mount requests: %w", err) } volumeConfigsByID := xslices.ToMap( safe.ToSlice(volumeConfigs, identity), func(v *block.VolumeConfig) (resource.ID, *block.VolumeConfig) { return v.Metadata().ID(), v }, ) volumeMountRequestsByID := xslices.ToMap( safe.ToSlice(volumeMountRequests, identity), func(v *block.VolumeMountRequest) (resource.ID, *block.VolumeMountRequest) { return v.Metadata().ID(), v }, ) return volumeConfigsByID, volumeMountRequestsByID, nil } // cleanupUnusedVolumes removes volumes that are no longer needed. func (ctrl *VolumeConfigController) cleanupUnusedVolumes( ctx context.Context, r controller.Runtime, volumeConfigsByID map[string]*block.VolumeConfig, volumeMountRequestsByID map[string]*block.VolumeMountRequest, ) error { for _, volumeConfig := range volumeConfigsByID { okToDestroy, err := r.Teardown(ctx, volumeConfig.Metadata()) if err != nil { return fmt.Errorf("error tearing down volume config %q: %w", volumeConfig.Metadata().ID(), err) } if okToDestroy { if err = r.Destroy(ctx, volumeConfig.Metadata()); err != nil { return fmt.Errorf("error destroying volume config %q: %w", volumeConfig.Metadata().ID(), err) } } } // Clean up unused volume mount requests for _, volumeMountRequest := range volumeMountRequestsByID { okToDestroy, err := r.Teardown(ctx, volumeMountRequest.Metadata()) if err != nil { return fmt.Errorf("error tearing down volume mount request %q: %w", volumeMountRequest.Metadata().ID(), err) } if okToDestroy { if err = r.Destroy(ctx, volumeMountRequest.Metadata()); err != nil { return fmt.Errorf("error destroying volume mount request %q: %w", volumeMountRequest.Metadata().ID(), err) } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/block/volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "encoding/json" "net/url" "os" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" blockctrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" intmeta "github.com/siderolabs/talos/internal/pkg/meta" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/container" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/yamlutils" ) type VolumeConfigSuite struct { ctest.DefaultSuite } type metaProvider struct { meta *intmeta.Meta } func (m metaProvider) Meta() machineruntime.Meta { return m.meta } func TestVolumeConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &VolumeConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { tmpDir := suite.T().TempDir() path := filepath.Join(tmpDir, "meta") f, err := os.Create(path) suite.Require().NoError(err) suite.Require().NoError(f.Truncate(1024 * 1024)) suite.Require().NoError(f.Close()) st := state.WrapCore(namespaced.NewState(inmem.Build)) m, err := intmeta.New(t.Context(), st, intmeta.WithFixedPath(path)) suite.Require().NoError(err) suite.Require().NoError(suite.Runtime().RegisterController( &blockctrls.VolumeConfigController{ MetaProvider: metaProvider{meta: m}, }, )) }, }, }) } func (suite *VolumeConfigSuite) TestReconcileDefaults() { // user volume mount point should be hold mounted all the time to prevent cascading unmounts on kubelet restart ctest.AssertResource(suite, constants.UserVolumeMountPoint, func(*block.VolumeMountRequest, *assert.Assertions) {}) // no machine config, default config which only searches for ctest.AssertResource(suite, constants.MetaPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) }) ctest.AssertResource(suite, constants.StatePartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) locator, err := r.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Equal(`volume.partition_label == "STATE" && volume.name != ""`, string(locator)) asrt.Equal(constants.StateMountPoint, r.TypedSpec().Mount.TargetPath) }) ctest.AssertNoResource[*block.VolumeConfig](suite, constants.EphemeralPartitionLabel) // create a dummy machine config u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // now the volume config should be created ctest.AssertResource(suite, constants.MetaPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) asrt.Empty(r.TypedSpec().Mount) locator, err := r.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Equal(`volume.partition_label == "META" && volume.name in ["", "talosmeta"] && volume.size == 1048576u`, string(locator)) }) ctest.AssertResource(suite, constants.StatePartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) locator, err := r.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Equal(`volume.partition_label == "STATE"`, string(locator)) asrt.Equal(constants.StateMountPoint, r.TypedSpec().Mount.TargetPath) }) ctest.AssertResource(suite, constants.EphemeralPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) locator, err := r.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Equal(`volume.partition_label == "EPHEMERAL"`, string(locator)) locator, err = r.TypedSpec().Provisioning.DiskSelector.Match.MarshalText() asrt.NoError(err) asrt.Equal(`system_disk`, string(locator)) asrt.True(r.TypedSpec().Provisioning.PartitionSpec.Grow) asrt.EqualValues(0, r.TypedSpec().Provisioning.PartitionSpec.MaxSize) asrt.EqualValues(quirks.New("").PartitionSizes().EphemeralMinSize(), r.TypedSpec().Provisioning.PartitionSpec.MinSize) asrt.Equal(constants.EphemeralMountPoint, r.TypedSpec().Mount.TargetPath) }) ctest.AssertResource(suite, "/var/run", func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(r.TypedSpec().Type, block.VolumeTypeSymlink) asrt.Equal(r.TypedSpec().Symlink.SymlinkTargetPath, "/run") asrt.Equal(r.TypedSpec().Mount.TargetPath, "/var/run") }) ctest.AssertResources(suite, []resource.ID{ constants.LogMountPoint, "/var/log/audit", "/var/log/containers", "/var/log/pods", constants.EtcdDataVolumeID, "/var/lib/containerd", "/var/lib/kubelet", "/var/lib/cni", constants.SeccompProfilesDirectory, constants.KubernetesAuditLogDir, "/var/run/lock", }, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(block.VolumeTypeDirectory, r.TypedSpec().Type) }) ctest.AssertResources(suite, xslices.Map(constants.Overlays, func(target constants.SELinuxLabeledPath) resource.ID { return target.Path }), func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(block.VolumeTypeOverlay, r.TypedSpec().Type) }) } func (suite *VolumeConfigSuite) TestReconcileEncryptedSTATE() { stateEncryption := &v1alpha1.EncryptionConfig{ EncryptionProvider: "luks2", EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeySlot: 1, KeyStatic: &v1alpha1.EncryptionKeyStatic{ KeyData: "supersecret", }, }, { KeySlot: 2, KeyTPM: &v1alpha1.EncryptionKeyTPM{}, }, }, } stateEncryptionMarshalled, err := json.Marshal(stateEncryption) suite.Require().NoError(err) stateMetaKey := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.StateEncryptionConfig)) stateMetaKey.TypedSpec().Value = string(stateEncryptionMarshalled) suite.Require().NoError(suite.State().Create(suite.Ctx(), stateMetaKey)) // no machine config, default config which only searches for ctest.AssertResource(suite, constants.MetaPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) }) ctest.AssertResource(suite, constants.StatePartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) asrt.NotEmpty(r.TypedSpec().Encryption) asrt.Equal(block.EncryptionProviderLUKS2, r.TypedSpec().Encryption.Provider) asrt.Len(r.TypedSpec().Encryption.Keys, 2) if len(r.TypedSpec().Encryption.Keys) != 2 { return } asrt.Equal(1, r.TypedSpec().Encryption.Keys[0].Slot) asrt.Equal(block.EncryptionKeyStatic, r.TypedSpec().Encryption.Keys[0].Type) asrt.Equal(yamlutils.StringBytes([]byte("supersecret")), r.TypedSpec().Encryption.Keys[0].StaticPassphrase) asrt.Equal(2, r.TypedSpec().Encryption.Keys[1].Slot) asrt.Equal(block.EncryptionKeyTPM, r.TypedSpec().Encryption.Keys[1].Type) }) ctest.AssertNoResource[*block.VolumeConfig](suite, constants.EphemeralPartitionLabel) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineSystemDiskEncryption: &v1alpha1.SystemDiskEncryptionConfig{ StatePartition: stateEncryption, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // now the volume config should be created ctest.AssertResource(suite, constants.MetaPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Provisioning) }) ctest.AssertResource(suite, constants.StatePartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) asrt.NotEmpty(r.TypedSpec().Encryption) }) ctest.AssertResource(suite, constants.EphemeralPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) asrt.Empty(r.TypedSpec().Encryption) }) } func (suite *VolumeConfigSuite) TestReconcileExtraEPHEMERALConfig() { ctest.AssertNoResource[*block.VolumeConfig](suite, constants.EphemeralPartitionLabel) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) ctr, err := container.New( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, &blockcfg.VolumeConfigV1Alpha1{ MetaName: constants.EphemeralPartitionLabel, ProvisioningSpec: blockcfg.ProvisioningSpec{ DiskSelectorSpec: blockcfg.DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, ProvisioningGrow: new(false), ProvisioningMaxSize: blockcfg.MustSize("2.5TiB"), }, EncryptionSpec: blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyTPM: &blockcfg.EncryptionKeyTPM{}, }, }, }, }, ) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // now the volume config should be created ctest.AssertResource(suite, constants.EphemeralPartitionLabel, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Provisioning) asrt.NotEmpty(r.TypedSpec().Encryption) locator, err := r.TypedSpec().Provisioning.DiskSelector.Match.MarshalText() asrt.NoError(err) asrt.Equal(`disk.transport == "nvme"`, string(locator)) asrt.False(r.TypedSpec().Provisioning.PartitionSpec.Grow) asrt.EqualValues(2.5*1024*1024*1024*1024, r.TypedSpec().Provisioning.PartitionSpec.MaxSize) asrt.EqualValues(quirks.New("").PartitionSizes().EphemeralMinSize(), r.TypedSpec().Provisioning.PartitionSpec.MinSize) asrt.Equal(block.EncryptionProviderLUKS2, r.TypedSpec().Encryption.Provider) }) } func (suite *VolumeConfigSuite) TestReconcileUserRawVolumes() { rv1 := blockcfg.NewRawVolumeConfigV1Alpha1() rv1.MetaName = "data1" suite.Require().NoError(rv1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) rv1.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("10GiB") rv1.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("100GiB") rv2 := blockcfg.NewRawVolumeConfigV1Alpha1() rv2.MetaName = "data2" suite.Require().NoError(rv2.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) rv2.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("1TiB") rv2.EncryptionSpec = blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyTPM: &blockcfg.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &blockcfg.EncryptionKeyStatic{KeyData: "secret"}, }, }, } ctr, err := container.New(rv1, rv2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) rawVolumes := []string{ constants.RawVolumePrefix + "data1", constants.RawVolumePrefix + "data2", } ctest.AssertResources(suite, rawVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) { asrt.Contains(vc.Metadata().Labels().Raw(), block.RawVolumeLabel) asrt.Equal(block.VolumeTypePartition, vc.TypedSpec().Type) asrt.Contains(rawVolumes, vc.TypedSpec().Provisioning.PartitionSpec.Label) locator, err := vc.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Contains(string(locator), vc.TypedSpec().Provisioning.PartitionSpec.Label) asrt.Equal(block.FilesystemTypeNone, vc.TypedSpec().Provisioning.FilesystemSpec.Type) asrt.Empty(vc.TypedSpec().Mount) }) for _, volumeID := range rawVolumes { ctest.AssertNoResource[*block.VolumeMountRequest](suite, volumeID) } // drop the first volume ctr, err = container.New(rv2) suite.Require().NoError(err) newCfg := config.NewMachineConfig(ctr) newCfg.Metadata().SetVersion(cfg.Metadata().Version()) suite.Update(newCfg) // now the resources should be removed ctest.AssertNoResource[*block.VolumeConfig](suite, rawVolumes[0]) } func (suite *VolumeConfigSuite) TestReconcileUserSwapVolumes() { userVolumeNames := []string{ "data-part1", "data-part2", "data-dir1", "data-disk1", } uvPart1 := blockcfg.NewUserVolumeConfigV1Alpha1() uvPart1.MetaName = userVolumeNames[0] suite.Require().NoError(uvPart1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) uvPart1.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("10GiB") uvPart1.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("100GiB") uvPart1.FilesystemSpec.FilesystemType = block.FilesystemTypeXFS uvPart2 := blockcfg.NewUserVolumeConfigV1Alpha1() uvPart2.MetaName = userVolumeNames[1] uvPart2.VolumeType = new(block.VolumeTypePartition) suite.Require().NoError(uvPart2.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) uvPart2.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("1TiB") uvPart2.EncryptionSpec = blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyTPM: &blockcfg.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &blockcfg.EncryptionKeyStatic{KeyData: "secret"}, }, }, } uvDir1 := blockcfg.NewUserVolumeConfigV1Alpha1() uvDir1.MetaName = userVolumeNames[2] uvDir1.VolumeType = new(block.VolumeTypeDirectory) uvDisk1 := blockcfg.NewUserVolumeConfigV1Alpha1() uvDisk1.MetaName = userVolumeNames[3] suite.Require().NoError(uvDisk1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) uvDisk1.EncryptionSpec = blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyTPM: &blockcfg.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &blockcfg.EncryptionKeyStatic{KeyData: "secret"}, }, }, } sv1 := blockcfg.NewSwapVolumeConfigV1Alpha1() sv1.MetaName = "swap" suite.Require().NoError(sv1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.transport == "nvme"`))) sv1.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("2GiB") ctr, err := container.New(uvPart1, uvPart2, uvDir1, uvDisk1, sv1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) userVolumes := xslices.Map(userVolumeNames, func(in string) string { return constants.UserVolumePrefix + in }) ctest.AssertResources(suite, userVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) { asrt.Contains(vc.Metadata().Labels().Raw(), block.UserVolumeLabel) switch vc.Metadata().ID() { case userVolumes[0], userVolumes[1], userVolumes[3]: asrt.Equal(block.VolumeTypePartition, vc.TypedSpec().Type) asrt.Contains(userVolumes, vc.TypedSpec().Provisioning.PartitionSpec.Label) locator, err := vc.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Contains(string(locator), vc.TypedSpec().Provisioning.PartitionSpec.Label) case userVolumes[2]: asrt.Equal(block.VolumeTypeDirectory, vc.TypedSpec().Type) } asrt.Contains(userVolumeNames, vc.TypedSpec().Mount.TargetPath) asrt.Equal(constants.UserVolumeMountPoint, vc.TypedSpec().Mount.ParentID) switch vc.Metadata().ID() { case userVolumes[0]: asrt.EqualValues(10*1024*1024*1024, vc.TypedSpec().Provisioning.PartitionSpec.MinSize) case userVolumes[1]: asrt.EqualValues(100*1024*1024, vc.TypedSpec().Provisioning.PartitionSpec.MinSize) } }) ctest.AssertResources(suite, userVolumes, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Contains(vmr.Metadata().Labels().Raw(), block.UserVolumeLabel) }) swapVolumes := []string{ constants.SwapVolumePrefix + "swap", } ctest.AssertResources(suite, swapVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) { asrt.Contains(vc.Metadata().Labels().Raw(), block.SwapVolumeLabel) asrt.Equal(block.VolumeTypePartition, vc.TypedSpec().Type) asrt.Contains(swapVolumes, vc.TypedSpec().Provisioning.PartitionSpec.Label) locator, err := vc.TypedSpec().Locator.Match.MarshalText() asrt.NoError(err) asrt.Contains(string(locator), vc.TypedSpec().Provisioning.PartitionSpec.Label) asrt.Equal(block.FilesystemTypeSwap, vc.TypedSpec().Provisioning.FilesystemSpec.Type) }) // simulate other controllers working - add finalizers for volume config & mount request for _, volumeID := range userVolumes { suite.AddFinalizer(block.NewVolumeConfig(block.NamespaceName, volumeID).Metadata(), "test") suite.AddFinalizer(block.NewVolumeMountRequest(block.NamespaceName, volumeID).Metadata(), "test") } // keep only the first volume ctr, err = container.New(uvPart1) suite.Require().NoError(err) newCfg := config.NewMachineConfig(ctr) newCfg.Metadata().SetVersion(cfg.Metadata().Version()) suite.Update(newCfg) // controller should tear down removed resources ctest.AssertResources(suite, userVolumes, func(vc *block.VolumeConfig, asrt *assert.Assertions) { if vc.Metadata().ID() == userVolumes[0] { asrt.Equal(resource.PhaseRunning, vc.Metadata().Phase()) } else { asrt.Equal(resource.PhaseTearingDown, vc.Metadata().Phase()) } }) ctest.AssertResources(suite, userVolumes, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { if vmr.Metadata().ID() == userVolumes[0] { asrt.Equal(resource.PhaseRunning, vmr.Metadata().Phase()) } else { asrt.Equal(resource.PhaseTearingDown, vmr.Metadata().Phase()) } }) // remove finalizers for _, userVolume := range userVolumes[1:] { suite.RemoveFinalizer(block.NewVolumeConfig(block.NamespaceName, userVolume).Metadata(), "test") suite.RemoveFinalizer(block.NewVolumeMountRequest(block.NamespaceName, userVolume).Metadata(), "test") } // now the resources should be removed for _, userVolume := range userVolumes[1:] { ctest.AssertNoResource[*block.VolumeConfig](suite, userVolume) ctest.AssertNoResource[*block.VolumeMountRequest](suite, userVolume) } } ================================================ FILE: internal/app/machined/pkg/controllers/block/volume_manager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "errors" "fmt" "math" "slices" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/kvutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block/internal/volumes" "github.com/siderolabs/talos/internal/pkg/encryption" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // VolumeManagerController manages volumes in the system, converting VolumeConfig resources to VolumeStatuses. type VolumeManagerController struct{} // Name implements controller.Controller interface. func (ctrl *VolumeManagerController) Name() string { return "block.VolumeManagerController" } // Inputs implements controller.Controller interface. func (ctrl *VolumeManagerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeConfigType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.DiscoveredVolumeType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.DiskType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.SystemDiskType, ID: optional.Some(block.SystemDiskID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.DevicesStatusType, ID: optional.Some(runtime.DevicesID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.DiscoveryRefreshStatusType, Kind: controller.InputWeak, }, { Namespace: hardware.NamespaceName, Type: hardware.SystemInformationType, ID: optional.Some(hardware.SystemInformationID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeLifecycleType, ID: optional.Some(block.VolumeLifecycleID), Kind: controller.InputStrong, }, { Namespace: hardware.NamespaceName, Type: hardware.PCRStatusType, Kind: controller.InputStrong, }, { Namespace: secrets.NamespaceName, Type: secrets.EncryptionSaltType, ID: optional.Some(secrets.EncryptionSaltID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *VolumeManagerController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeStatusType, Kind: controller.OutputExclusive, }, { Type: block.DiscoveryRefreshRequestType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *VolumeManagerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( deviceReadyObserved bool deviceReadyRequest int ) retryTicker := time.NewTicker(30 * time.Second) defer retryTicker.Stop() shouldRetry := false for { select { case <-r.EventCh(): case <-ctx.Done(): return nil case <-retryTicker.C: if !shouldRetry { continue } shouldRetry = false } // if devices are not ready, we can't provision and locate most volumes devicesStatus, err := safe.ReaderGetByID[*runtime.DevicesStatus](ctx, r, runtime.DevicesID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching devices status: %w", err) } devicesReady := devicesStatus != nil && devicesStatus.TypedSpec().Ready if devicesReady && !deviceReadyObserved { deviceReadyObserved = true // udevd reports that devices are ready, now it's time to refresh the discovery volumes if err = safe.WriterModify(ctx, r, block.NewDiscoveryRefreshRequest(block.NamespaceName, block.RefreshID), func(drr *block.DiscoveryRefreshRequest) error { drr.TypedSpec().Request++ deviceReadyRequest = drr.TypedSpec().Request return nil }); err != nil { return fmt.Errorf("error updating discovery refresh request: %w", err) } } refreshStatus, err := safe.ReaderGetByID[*block.DiscoveryRefreshStatus](ctx, r, block.RefreshID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching discovery refresh status: %w", err) } // now devicesReady is only true if the refresh status is up to date devicesReady = devicesReady && refreshStatus != nil && refreshStatus.TypedSpec().Request == deviceReadyRequest discoveredVolumes, err := safe.ReaderListAll[*block.DiscoveredVolume](ctx, r) if err != nil { return fmt.Errorf("error fetching discovered volumes: %w", err) } discoveredVolumesSpecs, err := safe.Map(discoveredVolumes, func(dv *block.DiscoveredVolume) (*blockpb.DiscoveredVolumeSpec, error) { spec := &blockpb.DiscoveredVolumeSpec{} return spec, proto.ResourceSpecToProto(dv, spec) }) if err != nil { return fmt.Errorf("error mapping discovered volumes: %w", err) } disks, err := safe.ReaderListAll[*block.Disk](ctx, r) if err != nil { return fmt.Errorf("error fetching disks: %w", err) } systemDisk, err := safe.ReaderGetByID[*block.SystemDisk](ctx, r, block.SystemDiskID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching system disk: %w", err) } volumeLifecycle, err := safe.ReaderGetByID[*block.VolumeLifecycle](ctx, r, block.VolumeLifecycleID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching volume lifecycle: %w", err) } if volumeLifecycle == nil { // no volume lifecycle, cease all operations continue } if volumeLifecycle.Metadata().Phase() == resource.PhaseRunning { if err = r.AddFinalizer(ctx, volumeLifecycle.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer to volume lifecycle: %w", err) } } diskSpecs, err := safe.Map(disks, func(d *block.Disk) (volumes.DiskContext, error) { spec := &blockpb.DiskSpec{} if err := proto.ResourceSpecToProto(d, spec); err != nil { return volumes.DiskContext{}, err } var optionalSystemDisk optional.Optional[bool] if systemDisk != nil { optionalSystemDisk = optional.Some(d.Metadata().ID() == systemDisk.TypedSpec().DiskID) } return volumes.DiskContext{ Disk: spec, SystemDisk: optionalSystemDisk, }, nil }) if err != nil { return fmt.Errorf("error mapping disks: %w", err) } volumeConfigList, err := safe.ReaderListAll[*block.VolumeConfig](ctx, r) if err != nil { return fmt.Errorf("error fetching volume configurations: %w", err) } volumeStatusList, err := safe.ReaderListAll[*block.VolumeStatus](ctx, r) if err != nil { return fmt.Errorf("error fetching volume statuses: %w", err) } volumeStatuses := xslices.ToMap( safe.ToSlice(volumeStatusList, func(vs *block.VolumeStatus) *block.VolumeStatus { return vs }), func(vs *block.VolumeStatus) (resource.ID, *block.VolumeStatus) { return vs.Metadata().ID(), vs }, ) if volumeStatuses == nil { volumeStatuses = map[resource.ID]*block.VolumeStatus{} } // ensure all volume configs have our finalizers for vc := range volumeConfigList.All() { if vc.Metadata().Phase() != resource.PhaseRunning { continue } if !vc.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, vc.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer to volume configuration: %w", err) } } } volumeLifecycleTearingDown := volumeLifecycle.Metadata().Phase() == resource.PhaseTearingDown if volumeLifecycleTearingDown { for _, fin := range *volumeLifecycle.Metadata().Finalizers() { if fin == ctrl.Name() { continue } // if there are finalizers other than us, we don't start global teardown volumeLifecycleTearingDown = false break } } volumeConfigs := safe.ToSlice(volumeConfigList, identity) // re-sort volume configs by provisioning wave slices.SortStableFunc(volumeConfigs, volumes.CompareVolumeConfigs) fullyProvisionedWave := math.MaxInt allClosed := true for _, vc := range volumeConfigs { // abort on context cancel, as each volume processing might take a while select { case <-ctx.Done(): return nil default: } volumeStatus := volumeStatuses[vc.Metadata().ID()] volumeLogger := logger.With(zap.String("volume", vc.Metadata().ID())) var volumeParentStatus *block.VolumeStatus if vc.TypedSpec().ParentID != "" { volumeParentStatus = volumeStatuses[vc.TypedSpec().ParentID] } parentFinalizer := ctrl.Name() + "-" + vc.Metadata().ID() // figure out if we are tearing down this volume or building it tearingDown := (volumeStatus != nil && volumeStatus.Metadata().Phase() == resource.PhaseTearingDown) || // we started tearing down the volume, so finish doing so vc.Metadata().Phase() == resource.PhaseTearingDown || // volume config is being torn down volumeParentStatus != nil && volumeParentStatus.Metadata().Phase() == resource.PhaseTearingDown || // parent volume is being torn down volumeLifecycleTearingDown // global volume lifecycle requires all volumes to be torn down // volume status doesn't exist yet, figure out what to do if volumeStatus == nil { if tearingDown { if volumeParentStatus != nil { if volumeParentStatus.Metadata().Finalizers().Has(parentFinalizer) { if err = r.RemoveFinalizer(ctx, volumeParentStatus.Metadata(), parentFinalizer); err != nil { return fmt.Errorf("error removing finalizer from parent volume configuration: %w", err) } } } // happy case, we don't need to progress this volume if vc.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.RemoveFinalizer(ctx, vc.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer from volume configuration: %w", err) } } continue } // create a stub volume status volumeStatus = block.NewVolumeStatus(block.NamespaceName, vc.Metadata().ID()) for k, v := range vc.Metadata().Labels().Raw() { volumeStatus.Metadata().Labels().Set(k, v) } volumeStatus.TypedSpec().Phase = block.VolumePhaseWaiting volumeStatus.TypedSpec().Type = vc.TypedSpec().Type volumeStatus.TypedSpec().ParentID = vc.TypedSpec().ParentID volumeStatuses[vc.Metadata().ID()] = volumeStatus } if tearingDown && volumeStatus.Metadata().Phase() != resource.PhaseTearingDown { // volume status is not yet in the tearing down phase, so move it there _, err = r.Teardown(ctx, volumeStatus.Metadata()) if err != nil { return fmt.Errorf("error tearing down volume status: %w", err) } } shouldCloseVolume := tearingDown && volumeStatus.Metadata().Finalizers().Empty() // we can start closing volume as soon as all finalizers are gone, so the volume is not e.g. mounted prevPhase := volumeStatus.TypedSpec().Phase if err = ctrl.progressVolumeConfig( ctx, volumeLogger, r, volumes.ManagerContext{ Cfg: vc, Status: volumeStatus.TypedSpec(), ParentStatus: volumeParentStatus, ParentFinalizer: parentFinalizer, DiscoveredVolumes: discoveredVolumesSpecs, Disks: diskSpecs, DevicesReady: devicesReady, PreviousWaveProvisioned: vc.TypedSpec().Provisioning.Wave <= fullyProvisionedWave, EncryptionHelpers: encryption.Helpers{ GetSystemInformation: ctrl.getSystemInformation(r), TPMLocker: hardware.LockPCRStatus(r, constants.UKIPCR, vc.Metadata().ID()), SaltGetter: ctrl.getSaltGetter(r), }, ShouldCloseVolume: shouldCloseVolume, }, ); err != nil { volumeStatus.TypedSpec().PreFailPhase = volumeStatus.TypedSpec().Phase volumeStatus.TypedSpec().Phase = block.VolumePhaseFailed volumeStatus.TypedSpec().ErrorMessage = err.Error() if xerrors.TagIs[volumes.Retryable](err) { shouldRetry = true } } else { volumeStatus.TypedSpec().ErrorMessage = "" volumeStatus.TypedSpec().PreFailPhase = block.VolumePhase(0) } // if the volume is not ready yet, we can consider the wave not fully provisioned, so the next wave can't start provisioning either // but if the volume doesn't have provisioning instructions, we don't block on it being ready if volumeStatus.TypedSpec().Phase != block.VolumePhaseReady && !value.IsZero(vc.TypedSpec().Provisioning) { fullyProvisionedWave = vc.TypedSpec().Provisioning.Wave - 1 } if prevPhase != volumeStatus.TypedSpec().Phase || err != nil { suppressVolumeLogs := slices.Contains( []block.VolumeType{ block.VolumeTypeDirectory, block.VolumeTypeOverlay, block.VolumeTypeSymlink, }, volumeStatus.TypedSpec().Type, ) if !suppressVolumeLogs { fields := []zap.Field{ zap.String("phase", fmt.Sprintf("%s -> %s", prevPhase, volumeStatus.TypedSpec().Phase)), zap.Error(err), } if volumeStatus.TypedSpec().Location != "" { fields = append(fields, zap.String("location", volumeStatus.TypedSpec().Location)) } if volumeStatus.TypedSpec().MountLocation != "" && volumeStatus.TypedSpec().MountLocation != volumeStatus.TypedSpec().Location { fields = append(fields, zap.String("mountLocation", volumeStatus.TypedSpec().MountLocation)) } if volumeStatus.TypedSpec().ParentLocation != "" { fields = append(fields, zap.String("parentLocation", volumeStatus.TypedSpec().ParentLocation)) } if len(volumeStatus.TypedSpec().EncryptionFailedSyncs) > 0 { fields = append(fields, zap.Strings("encryptionFailedSyncs", volumeStatus.TypedSpec().EncryptionFailedSyncs)) } volumeLogger.Info("volume status", fields...) } } // when closing, ignore META volume, we want it to stay longer, so no problem if is not closed yet allClosed = allClosed && (volumeStatus.TypedSpec().Phase == block.VolumePhaseClosed || vc.Metadata().ID() == constants.MetaPartitionLabel) if shouldCloseVolume && volumeStatus.TypedSpec().Phase == block.VolumePhaseClosed { if volumeParentStatus != nil { if volumeParentStatus.Metadata().Finalizers().Has(parentFinalizer) { if err = r.RemoveFinalizer(ctx, volumeParentStatus.Metadata(), parentFinalizer); err != nil { return fmt.Errorf("error removing finalizer from parent volume configuration: %w", err) } } } // we can destroy the volume status now if err = r.Destroy(ctx, volumeStatus.Metadata()); err != nil { return fmt.Errorf("error destroying volume status: %w", err) } delete(volumeStatuses, volumeStatus.Metadata().ID()) } } // update statuses for id, newVs := range volumeStatuses { if err = safe.WriterModify(ctx, r, block.NewVolumeStatus(block.NamespaceName, id), func(vs *block.VolumeStatus) error { vs.Metadata().Labels().Do(func(temp kvutils.TempKV) { for k, v := range newVs.Metadata().Labels().Raw() { temp.Set(k, v) } }) *vs.TypedSpec() = *newVs.TypedSpec() return nil }, controller.WithExpectedPhaseAny()); err != nil { return fmt.Errorf("error updating volume status: %w", err) } } // remove our finalizer if all volumes are closed if volumeLifecycle.Metadata().Phase() == resource.PhaseTearingDown && allClosed { if err = r.RemoveFinalizer(ctx, volumeLifecycle.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer from volume lifecycle: %w", err) } } } } func (ctrl *VolumeManagerController) progressVolumeConfig(ctx context.Context, logger *zap.Logger, r controller.Runtime, volumeContext volumes.ManagerContext) error { if !volumeContext.ShouldCloseVolume { if volumeContext.Cfg.TypedSpec().ParentID != "" { if volumeContext.ParentStatus == nil { // not ready yet return nil } if !volumeContext.ParentStatus.Metadata().Finalizers().Has(volumeContext.ParentFinalizer) { if err := r.AddFinalizer(ctx, volumeContext.ParentStatus.Metadata(), volumeContext.ParentFinalizer); err != nil { return fmt.Errorf("error adding finalizer to parent volume configuration: %w", err) } } } } return ctrl.processVolumeConfig(ctx, logger, volumeContext) } // processVolumeConfig implements the volume configuration automata. // // Initial -> { Waiting } ----> { Missing } // volume is not found (by locator) // | | // v v // { Located } ----> { Provisioned } // partition is ready, grown as needed // | // v // { Prepared } // decrypted (if needed) // | // v // { Ready } // can be mounted // //nolint:gocyclo,cyclop func (ctrl *VolumeManagerController) processVolumeConfig(ctx context.Context, logger *zap.Logger, volumeContext volumes.ManagerContext) error { prevPhase := volumeContext.Status.Phase for { if !volumeContext.ShouldCloseVolume { // normal state machine switch volumeContext.Status.Phase { case block.VolumePhaseReady: // nothing to do, ready return nil case block.VolumePhaseWaiting, block.VolumePhaseMissing: if err := volumes.LocateAndProvision(ctx, logger, volumeContext); err != nil { return err } case block.VolumePhaseLocated: // grow the partition if needed if err := volumes.Grow(ctx, logger, volumeContext); err != nil { return err } case block.VolumePhaseProvisioned: // decrypt/encrypt the volume if err := volumes.HandleEncryption(ctx, logger, volumeContext); err != nil { return err } case block.VolumePhasePrepared: // format the volume if err := volumes.Format(ctx, logger, volumeContext); err != nil { return err } case block.VolumePhaseFailed: // recover from the failure by restoring the pre-failure phase volumeContext.Status.Phase = volumeContext.Status.PreFailPhase case block.VolumePhaseClosed: // no progress, stop the loop return nil } } else { // closing state machine switch volumeContext.Status.Phase { case block.VolumePhaseReady, block.VolumePhasePrepared: if err := volumes.Close(ctx, logger, volumeContext); err != nil { return err } case block.VolumePhaseWaiting, block.VolumePhaseMissing, block.VolumePhaseLocated, block.VolumePhaseProvisioned: volumeContext.Status.Phase = block.VolumePhaseClosed case block.VolumePhaseClosed: // done return nil case block.VolumePhaseFailed: // recover from the failure by restoring the pre-failure phase volumeContext.Status.Phase = volumeContext.Status.PreFailPhase } } if volumeContext.Status.Phase == prevPhase { // doesn't progress, stop the loop return nil } select { // abort case <-ctx.Done(): return nil default: } prevPhase = volumeContext.Status.Phase } } func (ctrl *VolumeManagerController) getSystemInformation(r controller.Reader) helpers.SystemInformationGetter { return func(ctx context.Context) (*hardware.SystemInformation, error) { systemInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error fetching system information: %w", err) } if systemInfo == nil { return nil, errors.New("system information not available") } return systemInfo, nil } } func (ctrl *VolumeManagerController) getSaltGetter(r controller.Reader) helpers.SaltGetter { return func(ctx context.Context) ([]byte, error) { salt, err := safe.ReaderGetByID[*secrets.EncryptionSalt](ctx, r, secrets.EncryptionSaltID) if err != nil && !state.IsNotFoundError(err) { return nil, fmt.Errorf("error fetching encryption salt: %w", err) } if salt == nil { return nil, errors.New("encryption salt not available") } return salt.TypedSpec().DiskSalt, nil } } ================================================ FILE: internal/app/machined/pkg/controllers/block/zswap_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "context" "fmt" "strconv" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ZswapConfigController provides zswap configuration based machine configuration. type ZswapConfigController struct{} // Name implements controller.Controller interface. func (ctrl *ZswapConfigController) Name() string { return "block.ZswapConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ZswapConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ZswapConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelParamSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ZswapConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } // load config if present cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching machine configuration") } r.StartTrackingOutputs() var zswapCfg configconfig.ZswapConfig if cfg != nil { zswapCfg = cfg.Config().ZswapConfig() } if zswapCfg != nil { // enabled if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.enabled"), func(p *runtime.KernelParamSpec) error { p.TypedSpec().Value = "Y" return nil }); err != nil { return fmt.Errorf("error setting zswap config: %w", err) } if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.max_pool_percent"), func(p *runtime.KernelParamSpec) error { p.TypedSpec().Value = strconv.Itoa(zswapCfg.MaxPoolPercent()) return nil }); err != nil { return fmt.Errorf("error setting zswap config: %w", err) } if err := safe.WriterModify(ctx, r, runtime.NewKernelParamSpec(runtime.NamespaceName, "sys.module.zswap.parameters.shrinker_enabled"), func(p *runtime.KernelParamSpec) error { if zswapCfg.ShrinkerEnabled() { p.TypedSpec().Value = "Y" } else { p.TypedSpec().Value = "N" } return nil }); err != nil { return fmt.Errorf("error setting zswap config: %w", err) } } if err = safe.CleanupOutputs[*runtime.KernelParamSpec](ctx, r); err != nil { return fmt.Errorf("error cleaning up volume configuration: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/block/zswap_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bytes" "context" "fmt" "os" "path/filepath" "strconv" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/dustin/go-humanize" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ZswapStatusController provides a view of active swap devices. type ZswapStatusController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *ZswapStatusController) Name() string { return "block.ZswapStatusController" } // Inputs implements controller.Controller interface. func (ctrl *ZswapStatusController) Inputs() []controller.Input { return []controller.Input{ { // not really a dependency, but we refresh zswap status kernel param change Namespace: runtime.NamespaceName, Type: runtime.KernelParamStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ZswapStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: block.ZswapStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ZswapStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // in container mode, no zswap applies if ctrl.V1Alpha1Mode == machineruntime.ModeContainer { return nil } // there is no way to watch for zswap status, so we are going to poll every minute ticker := time.NewTicker(time.Minute) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ticker.C: } r.StartTrackingOutputs() // try to read a single status file to see if zswap is enabled if _, err := os.ReadFile("/sys/kernel/debug/zswap/pool_total_size"); err == nil { if err = safe.WriterModify(ctx, r, block.NewZswapStatus(block.NamespaceName, block.ZswapStatusID), func(zs *block.ZswapStatus) error { for _, p := range []struct { name string out *uint64 }{ {"pool_total_size", &zs.TypedSpec().TotalSizeBytes}, {"stored_pages", &zs.TypedSpec().StoredPages}, {"pool_limit_hit", &zs.TypedSpec().PoolLimitHit}, {"reject_reclaim_fail", &zs.TypedSpec().RejectReclaimFail}, {"reject_alloc_fail", &zs.TypedSpec().RejectAllocFail}, {"reject_kmemcache_fail", &zs.TypedSpec().RejectKmemcacheFail}, {"reject_compress_fail", &zs.TypedSpec().RejectCompressFail}, {"reject_compress_poor", &zs.TypedSpec().RejectCompressPoor}, {"written_back_pages", &zs.TypedSpec().WrittenBackPages}, } { if err := ctrl.readZswapParam(p.name, p.out); err != nil { return err } } zs.TypedSpec().TotalSizeHuman = humanize.IBytes(zs.TypedSpec().TotalSizeBytes) return nil }, ); err != nil { return fmt.Errorf("failed to create zswap status: %w", err) } } if err := safe.CleanupOutputs[*block.ZswapStatus](ctx, r); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } func (ctrl *ZswapStatusController) readZswapParam(name string, out *uint64) error { content, err := os.ReadFile(filepath.Join("/sys/kernel/debug/zswap", name)) if err != nil { return fmt.Errorf("failed to read zswap parameter %q: %w", name, err) } *out, err = strconv.ParseUint(string(bytes.TrimSpace(content)), 10, 64) if err != nil { return fmt.Errorf("failed to parse zswap parameter %q: %w", name, err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/affiliate_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // AffiliateMergeController merges raw Affiliates from the RawNamespaceName into final representation in the NamespaceName. type AffiliateMergeController struct{} // Name implements controller.Controller interface. func (ctrl *AffiliateMergeController) Name() string { return "cluster.AffiliateMergeController" } // Inputs implements controller.Controller interface. func (ctrl *AffiliateMergeController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cluster.RawNamespaceName, Type: cluster.AffiliateType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *AffiliateMergeController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.AffiliateType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *AffiliateMergeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } rawAffiliates, err := safe.ReaderList[*cluster.Affiliate](ctx, r, resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined)) if err != nil { return errors.New("error listing affiliates") } mergedAffiliates := make(map[resource.ID]*cluster.AffiliateSpec, rawAffiliates.Len()) for rawAffiliate := range rawAffiliates.All() { affiliateSpec := rawAffiliate.TypedSpec() id := affiliateSpec.NodeID if affiliate, ok := mergedAffiliates[id]; ok { affiliate.Merge(affiliateSpec) } else { mergedAffiliates[id] = affiliateSpec } } touchedIDs := make(map[resource.ID]struct{}, len(mergedAffiliates)) for id, affiliateSpec := range mergedAffiliates { if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.NamespaceName, id), func(res *cluster.Affiliate) error { *res.TypedSpec() = *affiliateSpec return nil }); err != nil { return err } touchedIDs[id] = struct{}{} } // list keys for cleanup list, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range list.All() { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/affiliate_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) type AffiliateMergeSuite struct { ClusterSuite } func (suite *AffiliateMergeSuite) TestReconcileDefault() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.AffiliateMergeController{})) affiliate1 := cluster.NewAffiliate(cluster.RawNamespaceName, "k8s/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate1.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, } affiliate2 := cluster.NewAffiliate(cluster.RawNamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate2.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4"), netip.MustParseAddr("10.5.0.2")}, } affiliate3 := cluster.NewAffiliate(cluster.RawNamespaceName, "service/9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F") *affiliate3.TypedSpec() = cluster.AffiliateSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Hostname: "worker-1", Nodename: "worker-1", MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")}, } for _, r := range []resource.Resource{affiliate1, affiliate2, affiliate3} { suite.Require().NoError(suite.state.Create(suite.ctx, r)) } // there should be two merged affiliates: one from affiliate1+affiliate2, and another from affiliate3 ctest.AssertResource( suite, affiliate1.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID) asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4"), netip.MustParseAddr("10.5.0.2")}, spec.Addresses) asrt.Equal("foo.com", spec.Hostname) asrt.Equal("bar", spec.Nodename) asrt.Equal(machine.TypeControlPlane, spec.MachineType) asrt.Equal(netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), spec.KubeSpan.Address) asrt.Equal("PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", spec.KubeSpan.PublicKey) asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, spec.KubeSpan.AdditionalAddresses) asrt.Equal([]netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, spec.KubeSpan.Endpoints) asrt.Equal(&cluster.ControlPlane{APIServerPort: 6443}, spec.ControlPlane) }, ) ctest.AssertResource( suite, affiliate3.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(affiliate3.TypedSpec().NodeID, spec.NodeID) asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses) asrt.Equal("worker-1", spec.Hostname) asrt.Equal("worker-1", spec.Nodename) asrt.Equal(machine.TypeWorker, spec.MachineType) asrt.Zero(spec.KubeSpan.PublicKey) asrt.Nil(spec.ControlPlane) }, ) // remove affiliate2, KubeSpan information should eventually go away suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate1.Metadata())) ctest.AssertResource( suite, affiliate1.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID) asrt.Zero(spec.KubeSpan.Address) asrt.Zero(spec.KubeSpan.PublicKey) asrt.Zero(spec.KubeSpan.AdditionalAddresses) asrt.Zero(spec.KubeSpan.Endpoints) asrt.Nil(spec.ControlPlane) }, ) // remove affiliate3, merged affiliate should be removed suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate3.Metadata())) ctest.AssertNoResource[*cluster.Affiliate](suite, affiliate3.TypedSpec().NodeID) } func TestAffiliateMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, new(AffiliateMergeSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cluster provides controllers which manage Talos cluster resources. package cluster import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func cleanupAffiliates(ctx context.Context, ctrl controller.Controller, r controller.Runtime, touchedIDs map[resource.ID]struct{}) error { // list keys for cleanup list, err := safe.ReaderList[*cluster.Affiliate]( ctx, r, resource.NewMetadata(cluster.RawNamespaceName, cluster.AffiliateType, "", resource.VersionUndefined), ) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range list.All() { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/cluster_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "context" "sync" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" ) type ClusterSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *ClusterSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) } func (suite *ClusterSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *ClusterSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func (suite *ClusterSuite) State() state.State { return suite.state } func (suite *ClusterSuite) Ctx() context.Context { return suite.ctx } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "encoding/base64" "net" "net/url" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // ConfigController watches v1alpha1.Config, updates discovery config. type ConfigController = transform.Controller[*config.MachineConfig, *cluster.Config] // NewConfigController instanciates the config controller. func NewConfigController() *ConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *cluster.Config]{ Name: "cluster.ConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*cluster.Config] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*cluster.Config]() } if cfg.Config().Cluster() == nil { return optional.None[*cluster.Config]() } return optional.Some(cluster.NewConfig(config.NamespaceName, cluster.ConfigID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *cluster.Config) error { c := cfg.Config() res.TypedSpec().DiscoveryEnabled = c.Cluster().Discovery().Enabled() if c.Cluster().Discovery().Enabled() { res.TypedSpec().RegistryKubernetesEnabled = c.Cluster().Discovery().Registries().Kubernetes().Enabled() res.TypedSpec().RegistryServiceEnabled = c.Cluster().Discovery().Registries().Service().Enabled() if c.Cluster().Discovery().Registries().Service().Enabled() { var u *url.URL u, err := url.ParseRequestURI(c.Cluster().Discovery().Registries().Service().Endpoint()) if err != nil { return err } host := u.Hostname() port := u.Port() if port == "" { if u.Scheme == "http" { port = "80" } else { port = "443" // use default https port for everything else } } res.TypedSpec().ServiceEndpoint = net.JoinHostPort(host, port) res.TypedSpec().ServiceEndpointInsecure = u.Scheme == "http" res.TypedSpec().ServiceEncryptionKey, err = base64.StdEncoding.DecodeString(c.Cluster().Secret()) if err != nil { return err } res.TypedSpec().ServiceClusterID = c.Cluster().ID() } else { res.TypedSpec().ServiceEndpoint = "" res.TypedSpec().ServiceEndpointInsecure = false res.TypedSpec().ServiceEncryptionKey = nil res.TypedSpec().ServiceClusterID = "" } } else { res.TypedSpec().RegistryKubernetesEnabled = false res.TypedSpec().RegistryServiceEnabled = false } return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package cluster_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) type ConfigSuite struct { ctest.DefaultSuite } func (suite *ConfigSuite) TestReconcileConfig() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "cluster1", ClusterSecret: "kCQsKr4B28VUl7qw1sVkTDNF9fFH++ViIuKsss+C6kc=", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.ConfigID}, func(res *cluster.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.True(spec.DiscoveryEnabled) asrt.True(spec.RegistryKubernetesEnabled) asrt.True(spec.RegistryServiceEnabled) asrt.Equal("discovery.talos.dev:443", spec.ServiceEndpoint) asrt.False(spec.ServiceEndpointInsecure) asrt.Equal("cluster1", spec.ServiceClusterID) asrt.Equal( []byte("\x90\x24\x2c\x2a\xbe\x01\xdb\xc5\x54\x97\xba\xb0\xd6\xc5\x64\x4c\x33\x45\xf5\xf1\x47\xfb\xe5\x62\x22\xe2\xac\xb2\xcf\x82\xea\x47"), spec.ServiceEncryptionKey, ) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), cfg.Metadata())) rtestutils.AssertNoResource[*cluster.Config](suite.Ctx(), suite.T(), suite.State(), cluster.ConfigID) } func (suite *ConfigSuite) TestReconcileConfigCustom() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "cluster1", ClusterSecret: "kCQsKr4B28VUl7qw1sVkTDNF9fFH++ViIuKsss+C6kc=", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), DiscoveryRegistries: v1alpha1.DiscoveryRegistriesConfig{ RegistryKubernetes: v1alpha1.RegistryKubernetesConfig{ RegistryDisabled: new(true), }, RegistryService: v1alpha1.RegistryServiceConfig{ RegistryEndpoint: "https://[2001:470:6d:30e:565d:e162:e2a0:cf5a]:3456/", }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.ConfigID}, func(res *cluster.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.True(spec.DiscoveryEnabled) asrt.False(spec.RegistryKubernetesEnabled) asrt.True(spec.RegistryServiceEnabled) asrt.Equal("[2001:470:6d:30e:565d:e162:e2a0:cf5a]:3456", spec.ServiceEndpoint) asrt.False(spec.ServiceEndpointInsecure) }, ) } func (suite *ConfigSuite) TestReconcileConfigCustomInsecure() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "cluster1", ClusterSecret: "kCQsKr4B28VUl7qw1sVkTDNF9fFH++ViIuKsss+C6kc=", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), DiscoveryRegistries: v1alpha1.DiscoveryRegistriesConfig{ RegistryKubernetes: v1alpha1.RegistryKubernetesConfig{ RegistryDisabled: new(true), }, RegistryService: v1alpha1.RegistryServiceConfig{ RegistryEndpoint: "http://localhost:3000", }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.ConfigID}, func(res *cluster.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.True(spec.DiscoveryEnabled) asrt.False(spec.RegistryKubernetesEnabled) asrt.True(spec.RegistryServiceEnabled) asrt.Equal("localhost:3000", spec.ServiceEndpoint) asrt.True(spec.ServiceEndpointInsecure) }, ) } func (suite *ConfigSuite) TestReconcileDisabled() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{}, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.ConfigID}, func(res *cluster.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.False(spec.DiscoveryEnabled) asrt.False(spec.RegistryKubernetesEnabled) }, ) } func (suite *ConfigSuite) TestReconcilePartial() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{}, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.ConfigID}, func(res *cluster.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.False(spec.DiscoveryEnabled) asrt.False(spec.RegistryKubernetesEnabled) }, ) newCfg := config.NewMachineConfig(must(container.New())) newCfg.Metadata().SetVersion(cfg.Metadata().Version()) suite.Require().NoError(suite.State().Update(suite.Ctx(), newCfg)) rtestutils.AssertNoResource[*cluster.Config](suite.Ctx(), suite.T(), suite.State(), cluster.ConfigID) } func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(clusterctrl.NewConfigController())) }, }, }) } func must[T any](t T, err error) T { if err != nil { panic(err) } return t } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/discovery_service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "crypto/aes" "crypto/cipher" "crypto/tls" "errors" "fmt" "net/netip" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/discovery-api/api/v1alpha1/client/pb" discoveryclient "github.com/siderolabs/discovery-client/pkg/client" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "google.golang.org/grpc" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/client/dialer" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/version" ) const defaultDiscoveryTTL = 30 * time.Minute // DiscoveryServiceController pushes Affiliate resource to the Kubernetes registry. type DiscoveryServiceController struct { localAffiliateID resource.ID discoveryConfigVersion resource.Version } // Name implements controller.Controller interface. func (ctrl *DiscoveryServiceController) Name() string { return "cluster.DiscoveryServiceController" } // Inputs implements controller.Controller interface. func (ctrl *DiscoveryServiceController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: cluster.ConfigType, ID: optional.Some(cluster.ConfigID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, { Namespace: kubespan.NamespaceName, Type: kubespan.EndpointType, Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MachineResetSignalType, ID: optional.Some(runtime.MachineResetSignalID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DiscoveryServiceController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.AffiliateType, Kind: controller.OutputShared, }, { Type: network.AddressStatusType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *DiscoveryServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( client *discoveryclient.Client clientCtxCancel context.CancelFunc ) clientErrCh := make(chan error, 1) defer func() { if clientCtxCancel != nil { clientCtxCancel() <-clientErrCh } }() notifyCh := make(chan struct{}, 1) var ( prevLocalData *pb.Affiliate prevLocalEndpoints []*pb.Endpoint prevOtherEndpoints []discoveryclient.Endpoint ) for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-notifyCh: case err := <-clientErrCh: if clientCtxCancel != nil { clientCtxCancel() } clientCtxCancel = nil if err != nil && !errors.Is(err, context.Canceled) { return fmt.Errorf("error from discovery client: %w", err) } } cleanupClient := func() { if clientCtxCancel != nil { clientCtxCancel() <-clientErrCh clientCtxCancel = nil client = nil prevLocalData = nil prevLocalEndpoints = nil prevOtherEndpoints = nil } } discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting discovery config: %w", err) } continue } if !discoveryConfig.TypedSpec().RegistryServiceEnabled { // if discovery is disabled cleanup existing resources if err = cleanupAffiliates(ctx, ctrl, r, nil); err != nil { return err } cleanupClient() continue } if !discoveryConfig.Metadata().Version().Equal(ctrl.discoveryConfigVersion) { // force reconnect on config change cleanupClient() } identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local identity: %w", err) } continue } localAffiliateID := identity.TypedSpec().NodeID if ctrl.localAffiliateID != localAffiliateID { ctrl.localAffiliateID = localAffiliateID if err = r.UpdateInputs(append(ctrl.Inputs(), controller.Input{ Namespace: cluster.NamespaceName, Type: cluster.AffiliateType, ID: optional.Some(ctrl.localAffiliateID), Kind: controller.InputWeak, }, )); err != nil { return err } cleanupClient() } affiliate, err := safe.ReaderGetByID[*cluster.Affiliate](ctx, r, ctrl.localAffiliateID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local affiliate: %w", err) } continue } affiliateSpec := affiliate.TypedSpec() otherEndpointsList, err := safe.ReaderListAll[*kubespan.Endpoint](ctx, r) if err != nil { return fmt.Errorf("error listing endpoints: %w", err) } machineResetSginal, err := safe.ReaderGetByID[*runtime.MachineResetSignal](ctx, r, runtime.MachineResetSignalID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine reset signal: %w", err) } if client == nil { var cipherBlock cipher.Block cipherBlock, err = aes.NewCipher(discoveryConfig.TypedSpec().ServiceEncryptionKey) if err != nil { return fmt.Errorf("error initializing AES cipher: %w", err) } tlsConfigFunc := func() *tls.Config { return &tls.Config{ RootCAs: httpdefaults.RootCAs(), } } client, err = discoveryclient.NewClient(discoveryclient.Options{ Cipher: cipherBlock, Endpoint: discoveryConfig.TypedSpec().ServiceEndpoint, ClusterID: discoveryConfig.TypedSpec().ServiceClusterID, AffiliateID: localAffiliateID, TTL: defaultDiscoveryTTL, Insecure: discoveryConfig.TypedSpec().ServiceEndpointInsecure, ClientVersion: version.Tag, TLSConfig: tlsConfigFunc, DialOptions: []grpc.DialOption{ grpc.WithContextDialer(dialer.DynamicProxyDialerWithTLSConfig(tlsConfigFunc)), }, }) if err != nil { return fmt.Errorf("error initializing discovery client: %w", err) } var clientCtx context.Context clientCtx, clientCtxCancel = context.WithCancel(ctx) //nolint:govet ctrl.discoveryConfigVersion = discoveryConfig.Metadata().Version() go func() { clientErrCh <- client.Run(clientCtx, logger, notifyCh) }() } // delete/update local affiliate // // if the node enters final resetting stage, cleanup the local affiliate // otherwise, update local affiliate data if machineResetSginal != nil { client.DeleteLocalAffiliate() } else { localData := pbAffiliate(affiliateSpec) localEndpoints := pbEndpoints(affiliateSpec) otherEndpoints := pbOtherEndpoints(otherEndpointsList) // don't send updates on localData if it hasn't changed: this introduces positive feedback loop, // as the watch loop will notify on self update if !proto.Equal(localData, prevLocalData) || !equalEndpoints(localEndpoints, prevLocalEndpoints) || !equalOtherEndpoints(otherEndpoints, prevOtherEndpoints) { if err = client.SetLocalData(&discoveryclient.Affiliate{ Affiliate: localData, Endpoints: localEndpoints, }, otherEndpoints); err != nil { return fmt.Errorf("error setting local affiliate data: %w", err) } prevLocalData = localData prevLocalEndpoints = localEndpoints prevOtherEndpoints = otherEndpoints } } // discover public IP if publicIP := client.GetPublicIP(); len(publicIP) > 0 { if err = safe.WriterModify(ctx, r, network.NewAddressStatus(cluster.NamespaceName, "service"), func(address *network.AddressStatus) error { var addr netip.Addr if err = addr.UnmarshalBinary(publicIP); err != nil { return fmt.Errorf("error unmarshaling public IP: %w", err) } address.TypedSpec().Address = netip.PrefixFrom(addr, addr.BitLen()) return nil }); err != nil { return err //nolint:govet } } // discover other nodes (affiliates) touchedIDs := make(map[resource.ID]struct{}) for _, discoveredAffiliate := range client.GetAffiliates() { id := fmt.Sprintf("service/%s", discoveredAffiliate.Affiliate.NodeId) if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res *cluster.Affiliate) error { *res.TypedSpec() = specAffiliate(discoveredAffiliate.Affiliate, discoveredAffiliate.Endpoints) return nil }); err != nil { return err } touchedIDs[id] = struct{}{} } if err := cleanupAffiliates(ctx, ctrl, r, touchedIDs); err != nil { return err } r.ResetRestartBackoff() } } func pbAffiliate(affiliate *cluster.AffiliateSpec) *pb.Affiliate { addresses := xslices.Map(affiliate.Addresses, func(address netip.Addr) []byte { return takeResult(address.MarshalBinary()) }) var kubeSpan *pb.KubeSpan if affiliate.KubeSpan.PublicKey != "" { kubeSpan = &pb.KubeSpan{ PublicKey: affiliate.KubeSpan.PublicKey, Address: takeResult(affiliate.KubeSpan.Address.MarshalBinary()), AdditionalAddresses: xslices.Map(affiliate.KubeSpan.AdditionalAddresses, func(address netip.Prefix) *pb.IPPrefix { return &pb.IPPrefix{ Bits: uint32(address.Bits()), Ip: takeResult(address.Addr().MarshalBinary()), } }), ExcludeAdvertisedAddresses: xslices.Map(affiliate.KubeSpan.ExcludeAdvertisedNetworks, func(address netip.Prefix) *pb.IPPrefix { return &pb.IPPrefix{ Bits: uint32(address.Bits()), Ip: takeResult(address.Addr().MarshalBinary()), } }), } } return &pb.Affiliate{ NodeId: affiliate.NodeID, Addresses: addresses, Hostname: affiliate.Hostname, Nodename: affiliate.Nodename, MachineType: affiliate.MachineType.String(), OperatingSystem: affiliate.OperatingSystem, Kubespan: kubeSpan, ControlPlane: toPlane(affiliate.ControlPlane), } } func toPlane(data *cluster.ControlPlane) *pb.ControlPlane { if data == nil { return nil } return &pb.ControlPlane{ApiServerPort: uint32(data.APIServerPort)} } func pbEndpoints(affiliate *cluster.AffiliateSpec) []*pb.Endpoint { if affiliate.KubeSpan.PublicKey == "" || len(affiliate.KubeSpan.Endpoints) == 0 { return nil } return xslices.Map(affiliate.KubeSpan.Endpoints, func(endpoint netip.AddrPort) *pb.Endpoint { return &pb.Endpoint{ Port: uint32(endpoint.Port()), Ip: takeResult(endpoint.Addr().MarshalBinary()), } }) } func pbOtherEndpoints(otherEndpointsList safe.List[*kubespan.Endpoint]) []discoveryclient.Endpoint { if otherEndpointsList.Len() == 0 { return nil } result := make([]discoveryclient.Endpoint, 0, otherEndpointsList.Len()) for endpoint := range otherEndpointsList.All() { endpointSpec := endpoint.TypedSpec() result = append(result, discoveryclient.Endpoint{ AffiliateID: endpointSpec.AffiliateID, Endpoints: []*pb.Endpoint{ { Port: uint32(endpointSpec.Endpoint.Port()), Ip: takeResult(endpointSpec.Endpoint.Addr().MarshalBinary()), }, }, }) } return result } func equalEndpoints(a, b []*pb.Endpoint) bool { if a == nil || b == nil { return a == nil && b == nil } if len(a) != len(b) { return false } for i := range a { if !proto.Equal(a[i], b[i]) { return false } } return true } func equalOtherEndpoints(a, b []discoveryclient.Endpoint) bool { if a == nil || b == nil { return a == nil && b == nil } if len(a) != len(b) { return false } for i := range a { if a[i].AffiliateID != b[i].AffiliateID { return false } if !equalEndpoints(a[i].Endpoints, b[i].Endpoints) { return false } } return true } func specAffiliate(affiliate *pb.Affiliate, endpoints []*pb.Endpoint) cluster.AffiliateSpec { result := cluster.AffiliateSpec{ NodeID: affiliate.NodeId, Hostname: affiliate.Hostname, Nodename: affiliate.Nodename, OperatingSystem: affiliate.OperatingSystem, MachineType: takeResult(machine.ParseType(affiliate.MachineType)), // ignore parse error (machine.TypeUnknown) ControlPlane: fromControlPlane(affiliate.ControlPlane), } result.Addresses = make([]netip.Addr, 0, len(affiliate.Addresses)) for i := range affiliate.Addresses { var ip netip.Addr if err := ip.UnmarshalBinary(affiliate.Addresses[i]); err == nil { result.Addresses = append(result.Addresses, ip) } } if affiliate.Kubespan != nil { result.KubeSpan.PublicKey = affiliate.Kubespan.PublicKey result.KubeSpan.Address.UnmarshalBinary(affiliate.Kubespan.Address) //nolint:errcheck // ignore error, address will be zero result.KubeSpan.AdditionalAddresses = make([]netip.Prefix, 0, len(affiliate.Kubespan.AdditionalAddresses)) for i := range affiliate.Kubespan.AdditionalAddresses { var ip netip.Addr if err := ip.UnmarshalBinary(affiliate.Kubespan.AdditionalAddresses[i].Ip); err == nil { result.KubeSpan.AdditionalAddresses = append(result.KubeSpan.AdditionalAddresses, netip.PrefixFrom(ip, int(affiliate.Kubespan.AdditionalAddresses[i].Bits))) } } result.KubeSpan.Endpoints = make([]netip.AddrPort, 0, len(endpoints)) for i := range endpoints { var ip netip.Addr if err := ip.UnmarshalBinary(endpoints[i].Ip); err == nil { result.KubeSpan.Endpoints = append(result.KubeSpan.Endpoints, netip.AddrPortFrom(ip, uint16(endpoints[i].Port))) } } result.KubeSpan.ExcludeAdvertisedNetworks = make([]netip.Prefix, 0, len(affiliate.Kubespan.ExcludeAdvertisedAddresses)) for i := range affiliate.Kubespan.ExcludeAdvertisedAddresses { var ip netip.Addr if err := ip.UnmarshalBinary(affiliate.Kubespan.ExcludeAdvertisedAddresses[i].Ip); err == nil { result.KubeSpan.ExcludeAdvertisedNetworks = append(result.KubeSpan.ExcludeAdvertisedNetworks, netip.PrefixFrom(ip, int(affiliate.Kubespan.ExcludeAdvertisedAddresses[i].Bits))) } } } return result } func fromControlPlane(plane *pb.ControlPlane) *cluster.ControlPlane { if plane == nil { return nil } return &cluster.ControlPlane{APIServerPort: int(plane.ApiServerPort)} } func takeResult[T any](arg1 T, _ error) T { return arg1 } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/discovery_service_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "context" "crypto/aes" "crypto/rand" "encoding/base64" "io" "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/discovery-api/api/v1alpha1/client/pb" "github.com/siderolabs/discovery-client/pkg/client" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type DiscoveryServiceSuite struct { ClusterSuite } func (suite *DiscoveryServiceSuite) TestReconcile() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.DiscoveryServiceController{})) serviceEndpoint, err := url.Parse(constants.DefaultDiscoveryServiceEndpoint) suite.Require().NoError(err) if serviceEndpoint.Port() == "" { serviceEndpoint.Host += ":443" } clusterIDRaw := make([]byte, constants.DefaultClusterIDSize) _, err = io.ReadFull(rand.Reader, clusterIDRaw) suite.Require().NoError(err) clusterID := base64.StdEncoding.EncodeToString(clusterIDRaw) encryptionKey := make([]byte, constants.DefaultClusterSecretSize) _, err = io.ReadFull(rand.Reader, encryptionKey) suite.Require().NoError(err) // regular discovery affiliate discoveryConfig := cluster.NewConfig(config.NamespaceName, cluster.ConfigID) discoveryConfig.TypedSpec().DiscoveryEnabled = true discoveryConfig.TypedSpec().RegistryServiceEnabled = true discoveryConfig.TypedSpec().ServiceEndpoint = serviceEndpoint.Host discoveryConfig.TypedSpec().ServiceClusterID = clusterID discoveryConfig.TypedSpec().ServiceEncryptionKey = encryptionKey suite.Require().NoError(suite.state.Create(suite.ctx, discoveryConfig)) nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Require().NoError(suite.state.Create(suite.ctx, nodeIdentity)) localAffiliate := cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID) *localAffiliate.TypedSpec() = cluster.AffiliateSpec{ NodeID: nodeIdentity.TypedSpec().NodeID, Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, } suite.Require().NoError(suite.state.Create(suite.ctx, localAffiliate)) // create a test client connected to the same cluster but under different affiliate ID cipher, err := aes.NewCipher(discoveryConfig.TypedSpec().ServiceEncryptionKey) suite.Require().NoError(err) cli, err := client.NewClient(client.Options{ Cipher: cipher, Endpoint: serviceEndpoint.Host, ClusterID: discoveryConfig.TypedSpec().ServiceClusterID, AffiliateID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", TTL: 5 * time.Minute, }) suite.Require().NoError(err) errCh := make(chan error, 1) notifyCh := make(chan struct{}, 1) cliCtx, cliCtxCancel := context.WithCancel(suite.ctx) defer cliCtxCancel() go func() { errCh <- cli.Run(cliCtx, zaptest.NewLogger(suite.T()), notifyCh) }() suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { // controller should register its local affiliate, and we should see it being discovered affiliates := cli.GetAffiliates() if len(affiliates) != 1 { return retry.ExpectedErrorf("affiliates len %d != 1", len(affiliates)) } suite.Require().Len(affiliates[0].Endpoints, 2) suite.Assert().True(proto.Equal(&pb.Affiliate{ NodeId: nodeIdentity.TypedSpec().NodeID, Addresses: [][]byte{[]byte("\xc0\xa8\x03\x04")}, Hostname: "foo.com", Nodename: "bar", MachineType: "controlplane", OperatingSystem: "", Kubespan: &pb.KubeSpan{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: []byte("\xfd\x50\x8d\x60\x42\x38\x63\x02\xf8\x57\x23\xff\xfe\x21\xd1\xe0"), AdditionalAddresses: []*pb.IPPrefix{ { Ip: []byte("\x0a\xf4\x03\x01"), Bits: 24, }, }, ExcludeAdvertisedAddresses: []*pb.IPPrefix{ { Ip: []byte("\x00\x00\x00\x00"), Bits: 0, }, }, }, ControlPlane: &pb.ControlPlane{ApiServerPort: 6443}, }, affiliates[0].Affiliate)) suite.Assert().True(proto.Equal( &pb.Endpoint{ Ip: []byte("\n\x00\x00\x02"), Port: 51820, }, affiliates[0].Endpoints[0]), "expected %v", affiliates[0].Endpoints[0]) suite.Assert().True(proto.Equal( &pb.Endpoint{ Ip: []byte("\xc0\xa8\x03\x04"), Port: 51820, }, affiliates[0].Endpoints[1]), "expected %v", affiliates[0].Endpoints[1]) return nil }, )) // inject some affiliate via our client, controller should publish it as an affiliate suite.Require().NoError(cli.SetLocalData(&client.Affiliate{ Affiliate: &pb.Affiliate{ NodeId: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: [][]byte{[]byte("\xc0\xa8\x03\x05")}, Hostname: "some.com", Nodename: "some", MachineType: "worker", OperatingSystem: "test OS", Kubespan: &pb.KubeSpan{ PublicKey: "1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=", Address: []byte("\xfd\x50\x8d\x60\x42\x38\x63\x02\xf8\x57\x23\xff\xfe\x21\xd1\xe1"), AdditionalAddresses: []*pb.IPPrefix{ { Ip: []byte("\x0a\xf4\x04\x01"), Bits: 24, }, }, ExcludeAdvertisedAddresses: []*pb.IPPrefix{ { Ip: []byte("\x01\x01\x01\x01"), Bits: 32, }, }, }, }, Endpoints: []*pb.Endpoint{ { Ip: []byte("\xc0\xa8\x03\x05"), Port: 51820, }, }, }, nil)) ctest.AssertResource( suite, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() suite.Assert().Equal("7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", spec.NodeID) suite.Assert().Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses) suite.Assert().Equal("some.com", spec.Hostname) suite.Assert().Equal("some", spec.Nodename) suite.Assert().Equal(machine.TypeWorker, spec.MachineType) suite.Assert().Equal("test OS", spec.OperatingSystem) suite.Assert().Equal(netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e1"), spec.KubeSpan.Address) suite.Assert().Equal("1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=", spec.KubeSpan.PublicKey) suite.Assert().Equal([]netip.Prefix{netip.MustParsePrefix("10.244.4.1/24")}, spec.KubeSpan.AdditionalAddresses) suite.Assert().Equal([]netip.Prefix{netip.MustParsePrefix("1.1.1.1/32")}, spec.KubeSpan.ExcludeAdvertisedNetworks) suite.Assert().Equal([]netip.AddrPort{netip.MustParseAddrPort("192.168.3.5:51820")}, spec.KubeSpan.Endpoints) suite.Assert().Zero(spec.ControlPlane) }, rtestutils.WithNamespace(cluster.RawNamespaceName), ) // controller should publish public IP ctest.AssertResource(suite, "service", func(r *network.AddressStatus, assertions *assert.Assertions) { spec := r.TypedSpec() assertions.True(spec.Address.IsValid()) assertions.True(spec.Address.IsSingleIP()) }, rtestutils.WithNamespace(cluster.NamespaceName)) // make controller inject additional endpoint via kubespan.Endpoint endpoint := kubespan.NewEndpoint(kubespan.NamespaceName, "1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=") *endpoint.TypedSpec() = kubespan.EndpointSpec{ AffiliateID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Endpoint: netip.MustParseAddrPort("1.1.1.1:343"), } suite.Require().NoError(suite.state.Create(suite.ctx, endpoint)) ctest.AssertResource(suite, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", func(r *cluster.Affiliate, assertions *assert.Assertions) { spec := r.TypedSpec() assertions.Len(spec.KubeSpan.Endpoints, 2) assertions.Equal([]netip.AddrPort{ netip.MustParseAddrPort("192.168.3.5:51820"), netip.MustParseAddrPort("1.1.1.1:343"), }, spec.KubeSpan.Endpoints) }, rtestutils.WithNamespace(cluster.RawNamespaceName), ) // pretend that machine is being reset machineResetSignal := runtime.NewMachineResetSignal() suite.Require().NoError(suite.state.Create(suite.ctx, machineResetSignal)) // client should see the affiliate being deleted suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { // controller should delete its local affiliate affiliates := cli.GetAffiliates() if len(affiliates) != 0 { return retry.ExpectedErrorf("affiliates len %d != 0", len(affiliates)) } return nil }, )) cliCtxCancel() suite.Assert().NoError(<-errCh) } func (suite *DiscoveryServiceSuite) TestDisable() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.DiscoveryServiceController{})) serviceEndpoint, err := url.Parse(constants.DefaultDiscoveryServiceEndpoint) suite.Require().NoError(err) if serviceEndpoint.Port() == "" { serviceEndpoint.Host += ":443" } clusterIDRaw := make([]byte, constants.DefaultClusterIDSize) _, err = io.ReadFull(rand.Reader, clusterIDRaw) suite.Require().NoError(err) clusterID := base64.StdEncoding.EncodeToString(clusterIDRaw) encryptionKey := make([]byte, constants.DefaultClusterSecretSize) _, err = io.ReadFull(rand.Reader, encryptionKey) suite.Require().NoError(err) // regular discovery affiliate discoveryConfig := cluster.NewConfig(config.NamespaceName, cluster.ConfigID) discoveryConfig.TypedSpec().DiscoveryEnabled = true discoveryConfig.TypedSpec().RegistryServiceEnabled = true discoveryConfig.TypedSpec().ServiceEndpoint = serviceEndpoint.Host discoveryConfig.TypedSpec().ServiceClusterID = clusterID discoveryConfig.TypedSpec().ServiceEncryptionKey = encryptionKey suite.Require().NoError(suite.state.Create(suite.ctx, discoveryConfig)) nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Require().NoError(suite.state.Create(suite.ctx, nodeIdentity)) localAffiliate := cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID) *localAffiliate.TypedSpec() = cluster.AffiliateSpec{ NodeID: nodeIdentity.TypedSpec().NodeID, Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, } suite.Require().NoError(suite.state.Create(suite.ctx, localAffiliate)) // create a test client connected to the same cluster but under different affiliate ID cipher, err := aes.NewCipher(discoveryConfig.TypedSpec().ServiceEncryptionKey) suite.Require().NoError(err) cli, err := client.NewClient(client.Options{ Cipher: cipher, Endpoint: serviceEndpoint.Host, ClusterID: discoveryConfig.TypedSpec().ServiceClusterID, AffiliateID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", TTL: 5 * time.Minute, }) suite.Require().NoError(err) errCh := make(chan error, 1) notifyCh := make(chan struct{}, 1) cliCtx, cliCtxCancel := context.WithCancel(suite.ctx) defer cliCtxCancel() go func() { errCh <- cli.Run(cliCtx, zaptest.NewLogger(suite.T()), notifyCh) }() // inject some affiliate via our client, controller should publish it as an affiliate suite.Require().NoError(cli.SetLocalData(&client.Affiliate{ Affiliate: &pb.Affiliate{ NodeId: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", }, }, nil)) ctest.AssertResource( suite, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", func(r *cluster.Affiliate, asrt *assert.Assertions) { suite.Assert().Equal("7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", r.TypedSpec().NodeID) }, rtestutils.WithNamespace(cluster.RawNamespaceName), ) // now disable the service registry ctest.UpdateWithConflicts(suite, discoveryConfig, func(r *cluster.Config) error { r.TypedSpec().RegistryServiceEnabled = false return nil }) ctest.AssertNoResource[*cluster.Affiliate]( suite, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", rtestutils.WithNamespace(cluster.RawNamespaceName), ) cliCtxCancel() suite.Assert().NoError(<-errCh) } func TestDiscoveryServiceSuite(t *testing.T) { t.Parallel() suite.Run(t, new(DiscoveryServiceSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // EndpointController looks up control plane endpoints. type EndpointController struct{} // Name implements controller.Controller interface. func (ctrl *EndpointController) Name() string { return "cluster.EndpointController" } // Inputs implements controller.Controller interface. func (ctrl *EndpointController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cluster.NamespaceName, Type: cluster.MemberType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EndpointController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.EndpointType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } memberList, err := safe.ReaderListAll[*cluster.Member](ctx, r) if err != nil { return fmt.Errorf("error listing members: %w", err) } var endpoints []netip.Addr for member := range memberList.All() { memberSpec := member.TypedSpec() if !(memberSpec.MachineType == machine.TypeControlPlane || memberSpec.MachineType == machine.TypeInit) { continue } endpoints = append(endpoints, memberSpec.Addresses...) } slices.SortFunc(endpoints, func(a, b netip.Addr) int { return a.Compare(b) }) if err := safe.WriterModify( ctx, r, k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, k8s.ControlPlaneDiscoveredEndpointsID), func(r *k8s.Endpoint) error { if !slices.Equal(r.TypedSpec().Addresses, endpoints) { logger.Debug("updated controlplane endpoints", zap.Any("endpoints", endpoints)) } r.TypedSpec().Addresses = endpoints return nil }, ); err != nil { return fmt.Errorf("error updating endpoints: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type EndpointSuite struct { ClusterSuite } func (suite *EndpointSuite) TestReconcileDefault() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.EndpointController{})) member1 := cluster.NewMember(cluster.NamespaceName, "talos-default-controlplane-1") *member1.TypedSpec() = cluster.MemberSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: []netip.Addr{netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0")}, Hostname: "talos-default-controlplane-1", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", } member2 := cluster.NewMember(cluster.NamespaceName, "talos-default-controlplane-2") *member2.TypedSpec() = cluster.MemberSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Addresses: []netip.Addr{netip.MustParseAddr("172.20.0.3"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e1")}, Hostname: "talos-default-controlplane-2", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", } member3 := cluster.NewMember(cluster.NamespaceName, "talos-default-worker-1") *member3.TypedSpec() = cluster.MemberSpec{ NodeID: "xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F", Addresses: []netip.Addr{netip.MustParseAddr("172.20.0.4")}, Hostname: "talos-default-worker-1", MachineType: machine.TypeWorker, OperatingSystem: "Talos (v1.0.0)", } for _, r := range []resource.Resource{member1, member2, member3} { suite.Require().NoError(suite.state.Create(suite.ctx, r)) } // control plane members should be translated to Endpoints ctest.AssertResource(suite, k8s.ControlPlaneDiscoveredEndpointsID, func(r *k8s.Endpoint, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal( []string{ "172.20.0.2", "172.20.0.3", "fd50:8d60:4238:6302:f857:23ff:fe21:d1e0", "fd50:8d60:4238:6302:f857:23ff:fe21:d1e1", }, xslices.Map(spec.Addresses, netip.Addr.String), ) }) } func TestEndpointSuite(t *testing.T) { t.Parallel() suite.Run(t, new(EndpointSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/info.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // InfoController looks up control plane infos. type InfoController = transform.Controller[*config.MachineConfig, *cluster.Info] // NewInfoController instanciates the cluster info controller. func NewInfoController() *InfoController { return transform.NewController( transform.Settings[*config.MachineConfig, *cluster.Info]{ Name: "cluster.InfoController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*cluster.Info] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*cluster.Info]() } if cfg.Config().Cluster() == nil { return optional.None[*cluster.Info]() } return optional.Some(cluster.NewInfo()) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, info *cluster.Info) error { info.TypedSpec().ClusterID = cfg.Config().Cluster().ID() info.TypedSpec().ClusterName = cfg.Config().Cluster().Name() return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/info_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) type InfoSuite struct { ctest.DefaultSuite } func (suite *InfoSuite) TestReconcile() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "cluster1", ClusterName: "foo", }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{cluster.InfoID}, func(res *cluster.Info, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal("cluster1", spec.ClusterID) asrt.Equal("foo", spec.ClusterName) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), cfg.Metadata())) rtestutils.AssertNoResource[*cluster.Config](suite.Ctx(), suite.T(), suite.State(), cluster.ConfigID) } func TestInfoSuite(t *testing.T) { t.Parallel() suite.Run(t, &InfoSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(clusterctrl.NewInfoController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/kubernetes_pull.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/discovery/registry" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubernetesPullController pulls list of Affiliate resource from the Kubernetes registry. type KubernetesPullController struct{} // Name implements controller.Controller interface. func (ctrl *KubernetesPullController) Name() string { return "cluster.KubernetesPullController" } // Inputs implements controller.Controller interface. func (ctrl *KubernetesPullController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: cluster.ConfigType, ID: optional.Some(cluster.ConfigID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubernetesPullController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.AffiliateType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KubernetesPullController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( kubernetesClient *kubernetes.Client kubernetesRegistry *registry.Kubernetes watchCtxCancel context.CancelFunc notifyCh <-chan struct{} notifyCloser func() ) defer func() { if watchCtxCancel != nil { watchCtxCancel() } if notifyCloser != nil { notifyCloser() } if kubernetesClient != nil { kubernetesClient.Close() //nolint:errcheck } }() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-notifyCh: } discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting discovery config: %w", err) } continue } if !discoveryConfig.TypedSpec().RegistryKubernetesEnabled { // if discovery is disabled cleanup existing resources if err = cleanupAffiliates(ctx, ctrl, r, nil); err != nil { return err } continue } if err = conditions.WaitForKubeconfigReady(constants.KubeletKubeconfig).Wait(ctx); err != nil { return err } nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting nodename: %w", err) } continue } if kubernetesClient == nil { kubernetesClient, err = kubernetes.NewClientFromKubeletKubeconfig() if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } } if kubernetesRegistry == nil { kubernetesRegistry = registry.NewKubernetes(kubernetesClient) } if notifyCh == nil { var watchCtx context.Context watchCtx, watchCtxCancel = context.WithCancel(ctx) //nolint:govet notifyCh, notifyCloser, err = kubernetesRegistry.Watch(watchCtx, logger) if err != nil { return fmt.Errorf("error setting up registry watcher: %w", err) //nolint:govet } } affiliateSpecs, err := kubernetesRegistry.List(nodename.TypedSpec().Nodename) if err != nil { return fmt.Errorf("error listing affiliates: %w", err) } touchedIDs := make(map[resource.ID]struct{}) for _, affilateSpec := range affiliateSpecs { id := fmt.Sprintf("k8s/%s", affilateSpec.NodeID) if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.RawNamespaceName, id), func(res *cluster.Affiliate) error { *res.TypedSpec() = *affilateSpec return nil }); err != nil { return err } touchedIDs[id] = struct{}{} } if err := cleanupAffiliates(ctx, ctrl, r, touchedIDs); err != nil { return err } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/kubernetes_push.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/discovery/registry" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // KubernetesPushController pushes Affiliate resource to the Kubernetes registry. type KubernetesPushController struct { localAffiliateID resource.ID kubernetesClient *kubernetes.Client } // Name implements controller.Controller interface. func (ctrl *KubernetesPushController) Name() string { return "cluster.KubernetesPushController" } // Inputs implements controller.Controller interface. func (ctrl *KubernetesPushController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: cluster.ConfigType, ID: optional.Some(cluster.ConfigID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubernetesPushController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KubernetesPushController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { defer func() { if ctrl.kubernetesClient != nil { ctrl.kubernetesClient.Close() //nolint:errcheck } ctrl.kubernetesClient = nil }() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting discovery config: %w", err) } continue } if !discoveryConfig.TypedSpec().RegistryKubernetesEnabled { continue } if err = conditions.WaitForKubeconfigReady(constants.KubeletKubeconfig).Wait(ctx); err != nil { return err } identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local identity: %w", err) } continue } localAffiliateID := identity.TypedSpec().NodeID if ctrl.localAffiliateID != localAffiliateID { ctrl.localAffiliateID = localAffiliateID if err = r.UpdateInputs(append(ctrl.Inputs(), controller.Input{ Namespace: cluster.NamespaceName, Type: cluster.AffiliateType, ID: optional.Some(ctrl.localAffiliateID), Kind: controller.InputWeak, }, )); err != nil { return err } } affiliate, err := safe.ReaderGetByID[*cluster.Affiliate](ctx, r, ctrl.localAffiliateID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local affiliate: %w", err) } continue } if ctrl.kubernetesClient == nil { ctrl.kubernetesClient, err = kubernetes.NewClientFromKubeletKubeconfig() if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } } if err = registry.NewKubernetes(ctrl.kubernetesClient).Push(ctx, affiliate); err != nil { // reset client connection ctrl.kubernetesClient.Close() //nolint:errcheck ctrl.kubernetesClient = nil return fmt.Errorf("error pushing to Kubernetes registry: %w", err) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/local_affiliate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/net" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/version" ) // LocalAffiliateController builds Affiliate resource for the local node. type LocalAffiliateController struct{} // Name implements controller.Controller interface. func (ctrl *LocalAffiliateController) Name() string { return "cluster.LocalAffiliateController" } // Inputs implements controller.Controller interface. func (ctrl *LocalAffiliateController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: cluster.ConfigType, ID: optional.Some(cluster.ConfigID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeStatusType, Kind: controller.InputWeak, }, { Namespace: kubespan.NamespaceName, Type: kubespan.IdentityType, ID: optional.Some(kubespan.LocalIdentity), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: kubespan.ConfigType, ID: optional.Some(kubespan.ConfigID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: network.AddressStatusType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.APIServerConfigType, ID: optional.Some(k8s.APIServerConfigID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *LocalAffiliateController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.AffiliateType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *LocalAffiliateController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // mandatory resources to be fetched discoveryConfig, err := safe.ReaderGetByID[*cluster.Config](ctx, r, cluster.ConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting discovery config: %w", err) } continue } identity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local identity: %w", err) } continue } hostname, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting hostname: %w", err) } continue } nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting nodename: %w", err) } continue } routedAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting addresses: %w", err) } continue } currentAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting addresses: %w", err) } continue } machineType, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine type: %w", err) } continue } // optional resources (kubespan) kubespanIdentity, err := safe.ReaderGetByID[*kubespan.Identity](ctx, r, kubespan.LocalIdentity) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan identity: %w", err) } kubespanConfig, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan config: %w", err) } nodeStatus, err := safe.ReaderGetByID[*k8s.NodeStatus](ctx, r, nodename.TypedSpec().Nodename) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting node status: %w", err) } discoveredPublicIPs, err := safe.ReaderList[*network.AddressStatus](ctx, r, resource.NewMetadata(cluster.NamespaceName, network.AddressStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error getting discovered public IP: %w", err) } // optional resources (kubernetes) apiServerConfig, err := safe.ReaderGetByID[*k8s.APIServerConfig](ctx, r, k8s.APIServerConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting API server config: %w", err) } localID := identity.TypedSpec().NodeID touchedIDs := map[resource.ID]struct{}{} if discoveryConfig.TypedSpec().DiscoveryEnabled { if err = safe.WriterModify(ctx, r, cluster.NewAffiliate(cluster.NamespaceName, localID), func(res *cluster.Affiliate) error { spec := res.TypedSpec() spec.NodeID = localID spec.Hostname = hostname.TypedSpec().FQDN() spec.Nodename = nodename.TypedSpec().Nodename spec.MachineType = machineType.MachineType() spec.OperatingSystem = fmt.Sprintf("%s (%s)", version.Name, version.Tag) if machineType.MachineType().IsControlPlane() && apiServerConfig != nil { spec.ControlPlane = &cluster.ControlPlane{ APIServerPort: apiServerConfig.TypedSpec().LocalPort, } } else { spec.ControlPlane = nil } routedNodeIPs := routedAddresses.TypedSpec().IPs() currentNodeIPs := currentAddresses.TypedSpec().IPs() spec.Addresses = routedNodeIPs spec.KubeSpan = cluster.KubeSpanAffiliateSpec{} if kubespanIdentity != nil && kubespanConfig != nil { spec.KubeSpan.Address = kubespanIdentity.TypedSpec().Address.Addr() spec.KubeSpan.PublicKey = kubespanIdentity.TypedSpec().PublicKey if kubespanConfig.TypedSpec().AdvertiseKubernetesNetworks && nodeStatus != nil { spec.KubeSpan.AdditionalAddresses = slices.Clone(nodeStatus.TypedSpec().PodCIDRs) } else { spec.KubeSpan.AdditionalAddresses = nil } spec.KubeSpan.ExcludeAdvertisedNetworks = kubespanConfig.TypedSpec().ExcludeAdvertisedNetworks endpointIPs := xslices.Filter(currentNodeIPs, func(ip netip.Addr) bool { if ip == spec.KubeSpan.Address { // skip kubespan local address return false } if network.IsULA(ip, network.ULASideroLink) { // ignore SideroLink addresses, as they are point-to-point addresses return false } return true }) // mix in discovered public IPs for res := range discoveredPublicIPs.All() { addr := res.TypedSpec().Address.Addr() if slices.ContainsFunc(endpointIPs, func(a netip.Addr) bool { return addr == a }) { // this address is already published continue } endpointIPs = append(endpointIPs, addr) } // filter endpoints if configured if kubespanConfig.TypedSpec().EndpointFilters != nil { endpointIPs, err = net.FilterIPs(endpointIPs, kubespanConfig.TypedSpec().EndpointFilters) if err != nil { return fmt.Errorf("error filtering KubeSpan endpoints: %w", err) } } spec.KubeSpan.Endpoints = xslices.Map(endpointIPs, func(addr netip.Addr) netip.AddrPort { return netip.AddrPortFrom(addr, constants.KubeSpanDefaultPort) }) // add extra announced endpoints, deduplicating on the way for _, addr := range kubespanConfig.TypedSpec().ExtraEndpoints { if !slices.Contains(spec.KubeSpan.Endpoints, addr) { spec.KubeSpan.Endpoints = append(spec.KubeSpan.Endpoints, addr) } } } return nil }); err != nil { return err } touchedIDs[localID] = struct{}{} } // list keys for cleanup list, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range list.All() { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/local_affiliate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net" "net/netip" "testing" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/version" ) type LocalAffiliateSuite struct { ClusterSuite } func (suite *LocalAffiliateSuite) TestGeneration() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.LocalAffiliateController{})) nodeIdentity, nonK8sRoutedAddresses, nodeName, discoveryConfig := suite.createResources() machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.state.Create(suite.ctx, machineType)) ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal([]string{ "172.20.0.2", "10.5.0.1", "192.168.192.168", "2001:123:4567::1", }, xslices.Map(spec.Addresses, netip.Addr.String)) asrt.Equal("example1", spec.Hostname) asrt.Equal("example1.com", spec.Nodename) asrt.Equal(machine.TypeWorker, spec.MachineType) asrt.Equal("Talos ("+version.Tag+")", spec.OperatingSystem) asrt.Equal(cluster.KubeSpanAffiliateSpec{}, spec.KubeSpan) }) if fipsmode.Strict() { suite.T().Skip("skipping test in strict FIPS mode (Wireguard/KubeSpan)") } // enable kubespan mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee") suite.Require().NoError(err) ksIdentity := kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity) suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).GenerateKey()) suite.Require().NoError(kubespanadapter.IdentitySpec(ksIdentity.TypedSpec()).UpdateAddress("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", mac)) suite.Require().NoError(suite.state.Create(suite.ctx, ksIdentity)) ksConfig := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) ksConfig.TypedSpec().EndpointFilters = []string{"0.0.0.0/0", "!192.168.0.0/16", "2001::/16"} ksConfig.TypedSpec().AdvertiseKubernetesNetworks = true ksConfig.TypedSpec().ExtraEndpoints = []netip.AddrPort{netip.MustParseAddrPort("10.5.0.1:51820"), netip.MustParseAddrPort("1.2.3.4:5678")} ksConfig.TypedSpec().ExcludeAdvertisedNetworks = []netip.Prefix{netip.MustParsePrefix("2001:123:4567::/64")} suite.Require().NoError(suite.state.Create(suite.ctx, ksConfig)) // add KS address to the list of node addresses, it should be ignored in the endpoints nonK8sRoutedAddresses.TypedSpec().Addresses = append(nonK8sRoutedAddresses.TypedSpec().Addresses, ksIdentity.TypedSpec().Address) suite.Require().NoError(suite.state.Update(suite.ctx, nonK8sRoutedAddresses)) nodeStatus := k8s.NewNodeStatus(k8s.NamespaceName, nodeName.TypedSpec().Nodename) nodeStatus.TypedSpec().PodCIDRs = []netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")} suite.Require().NoError(suite.state.Create(suite.ctx, nodeStatus)) // add discovered public IPs for _, addr := range []netip.Addr{ netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("2001:123:4567::1"), // duplicate, will be ignored } { discoveredAddr := network.NewAddressStatus(cluster.NamespaceName, addr.String()) discoveredAddr.TypedSpec().Address = netip.PrefixFrom(addr, addr.BitLen()) suite.Require().NoError(suite.state.Create(suite.ctx, discoveredAddr)) } ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.False(len(spec.Addresses) < 5) asrt.Equal([]netip.Addr{ netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("10.5.0.1"), netip.MustParseAddr("192.168.192.168"), netip.MustParseAddr("2001:123:4567::1"), ksIdentity.TypedSpec().Address.Addr(), }, spec.Addresses) asrt.Equal("example1", spec.Hostname) asrt.Equal("example1.com", spec.Nodename) asrt.Equal(machine.TypeWorker, spec.MachineType) asrt.NotZero(spec.KubeSpan.PublicKey) asrt.NotZero(spec.KubeSpan.AdditionalAddresses) asrt.Len(spec.KubeSpan.ExcludeAdvertisedNetworks, 1) asrt.Equal(ksIdentity.TypedSpec().Address.Addr(), spec.KubeSpan.Address) asrt.Equal(ksIdentity.TypedSpec().PublicKey, spec.KubeSpan.PublicKey) asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.244.1.0/24")}, spec.KubeSpan.AdditionalAddresses) asrt.Equal( []string{ "172.20.0.2:51820", "10.5.0.1:51820", "1.1.1.1:51820", "[2001:123:4567::1]:51820", "1.2.3.4:5678", }, xslices.Map(spec.KubeSpan.Endpoints, netip.AddrPort.String), ) }) // disable advertising K8s addresses ksConfig.TypedSpec().AdvertiseKubernetesNetworks = false suite.Require().NoError(suite.state.Update(suite.ctx, ksConfig)) ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().KubeSpan.AdditionalAddresses) }) // disable discovery, local affiliate should be removed discoveryConfig.TypedSpec().DiscoveryEnabled = false suite.Require().NoError(suite.state.Update(suite.ctx, discoveryConfig)) ctest.AssertNoResource[*cluster.Affiliate](suite, nodeIdentity.TypedSpec().NodeID) } func (suite *LocalAffiliateSuite) TestCPGeneration() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.LocalAffiliateController{})) nodeIdentity, _, _, discoveryConfig := suite.createResources() machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Require().NoError(suite.state.Create(suite.ctx, machineType)) apiServerConfig := k8s.NewAPIServerConfig() apiServerConfig.TypedSpec().LocalPort = 6445 suite.Require().NoError(suite.state.Create(suite.ctx, apiServerConfig)) ctest.AssertResource(suite, nodeIdentity.TypedSpec().NodeID, func(r *cluster.Affiliate, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal([]string{ "172.20.0.2", "10.5.0.1", "192.168.192.168", "2001:123:4567::1", }, xslices.Map(spec.Addresses, netip.Addr.String)) asrt.Equal("example1", spec.Hostname) asrt.Equal("example1.com", spec.Nodename) asrt.Equal(machine.TypeControlPlane, spec.MachineType) asrt.Equal("Talos ("+version.Tag+")", spec.OperatingSystem) asrt.Equal(cluster.KubeSpanAffiliateSpec{}, spec.KubeSpan) asrt.NotNil(spec.ControlPlane) asrt.Equal(6445, pointer.SafeDeref(spec.ControlPlane).APIServerPort) }) discoveryConfig.TypedSpec().DiscoveryEnabled = false suite.Require().NoError(suite.state.Update(suite.ctx, discoveryConfig)) ctest.AssertNoResource[*cluster.Affiliate](suite, nodeIdentity.TypedSpec().NodeID) } func (suite *LocalAffiliateSuite) createResources() (*cluster.Identity, *network.NodeAddress, *k8s.Nodename, *cluster.Config) { // regular discovery affiliate discoveryConfig := cluster.NewConfig(config.NamespaceName, cluster.ConfigID) discoveryConfig.TypedSpec().DiscoveryEnabled = true suite.Require().NoError(suite.state.Create(suite.ctx, discoveryConfig)) nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Require().NoError(suite.state.Create(suite.ctx, nodeIdentity)) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "example1" suite.Require().NoError(suite.state.Create(suite.ctx, hostnameStatus)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "example1.com" suite.Require().NoError(suite.state.Create(suite.ctx, nodename)) nonK8sCurrentAddresses := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s)) nonK8sCurrentAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("172.20.0.2/24"), netip.MustParsePrefix("10.5.0.1/32"), netip.MustParsePrefix("192.168.192.168/24"), netip.MustParsePrefix("2001:123:4567::1/64"), netip.MustParsePrefix("2001:123:4567::1/128"), netip.MustParsePrefix("fdae:41e4:649b:9303:60be:7e36:c270:3238/128"), // SideroLink, should be ignored } suite.Require().NoError(suite.state.Create(suite.ctx, nonK8sCurrentAddresses)) nonK8sRoutedAddresses := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) nonK8sRoutedAddresses.TypedSpec().Addresses = []netip.Prefix{ // routed node addresses don't contain SideroLink addresses netip.MustParsePrefix("172.20.0.2/24"), netip.MustParsePrefix("10.5.0.1/32"), netip.MustParsePrefix("192.168.192.168/24"), netip.MustParsePrefix("2001:123:4567::1/64"), netip.MustParsePrefix("2001:123:4567::1/128"), } suite.Require().NoError(suite.state.Create(suite.ctx, nonK8sRoutedAddresses)) return nodeIdentity, nonK8sRoutedAddresses, nodename, discoveryConfig } func TestLocalAffiliateSuite(t *testing.T) { t.Parallel() suite.Run(t, new(LocalAffiliateSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/member.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "errors" "fmt" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // MemberController converts Affiliates which have Nodename set into Members. type MemberController struct{} // Name implements controller.Controller interface. func (ctrl *MemberController) Name() string { return "cluster.MemberController" } // Inputs implements controller.Controller interface. func (ctrl *MemberController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cluster.NamespaceName, Type: cluster.AffiliateType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MemberController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.MemberType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *MemberController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r) if err != nil { return errors.New("error listing affiliates") } touchedIDs := make(map[resource.ID]struct{}) for affiliate := range affiliates.All() { affiliateSpec := affiliate.TypedSpec() if affiliateSpec.Nodename == "" { // not a cluster member continue } if err = safe.WriterModify(ctx, r, cluster.NewMember(cluster.NamespaceName, affiliateSpec.Nodename), func(res *cluster.Member) error { spec := res.TypedSpec() spec.Addresses = slices.Clone(affiliateSpec.Addresses) spec.Hostname = affiliateSpec.Hostname spec.MachineType = affiliateSpec.MachineType spec.OperatingSystem = affiliateSpec.OperatingSystem spec.NodeID = affiliateSpec.NodeID spec.ControlPlane = affiliateSpec.ControlPlane return nil }); err != nil { return err } touchedIDs[affiliateSpec.Nodename] = struct{}{} } // list keys for cleanup list, err := safe.ReaderListAll[*cluster.Member](ctx, r) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range list.All() { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/member_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) type MemberSuite struct { ClusterSuite } func (suite *MemberSuite) TestReconcileDefault() { suite.startRuntime() suite.Require().NoError(suite.runtime.RegisterController(&clusterctrl.MemberController{})) affiliate1 := cluster.NewAffiliate(cluster.NamespaceName, "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate1.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, } affiliate2 := cluster.NewAffiliate(cluster.NamespaceName, "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F") *affiliate2.TypedSpec() = cluster.AffiliateSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Hostname: "worker-1", Nodename: "worker-1", MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")}, } affiliate3 := cluster.NewAffiliate(cluster.NamespaceName, "xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F") *affiliate3.TypedSpec() = cluster.AffiliateSpec{ NodeID: "xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F", MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.6")}, } for _, r := range []resource.Resource{affiliate1, affiliate2, affiliate3} { suite.Require().NoError(suite.state.Create(suite.ctx, r)) } // affiliates with non-empty Nodename should be translated to Members ctest.AssertResource( suite, affiliate1.TypedSpec().Nodename, func(r *cluster.Member, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(affiliate1.TypedSpec().NodeID, spec.NodeID) asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.4")}, spec.Addresses) asrt.Equal("foo.com", spec.Hostname) asrt.Equal(machine.TypeControlPlane, spec.MachineType) asrt.Equal("Talos (v1.0.0)", spec.OperatingSystem) asrt.Equal(6443, spec.ControlPlane.APIServerPort) }, ) ctest.AssertResource( suite, affiliate2.TypedSpec().Nodename, func(r *cluster.Member, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(affiliate2.TypedSpec().NodeID, spec.NodeID) asrt.Equal([]netip.Addr{netip.MustParseAddr("192.168.3.5")}, spec.Addresses) asrt.Equal("worker-1", spec.Hostname) asrt.Equal(machine.TypeWorker, spec.MachineType) }, ) // remove affiliate2, member information should eventually go away suite.Require().NoError(suite.state.Destroy(suite.ctx, affiliate2.Metadata())) ctest.AssertNoResource[*cluster.Member](suite, affiliate2.TypedSpec().Nodename) } func TestMemberSuite(t *testing.T) { suite.Run(t, new(MemberSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/node_identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/xfs" ) // NodeIdentityController manages runtime.Identity caching identity in the STATE. type NodeIdentityController struct { stateMachine blockautomaton.VolumeMounterAutomaton } // Name implements controller.Controller interface. func (ctrl *NodeIdentityController) Name() string { return "cluster.NodeIdentityController" } // Inputs implements controller.Controller interface. func (ctrl *NodeIdentityController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeIdentityController) Outputs() []controller.Output { return []controller.Output{ { Type: cluster.IdentityType, Kind: controller.OutputShared, }, { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeIdentityController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if ctrl.stateMachine == nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.establishNodeIdentity, blockautomaton.WithDetached(true), ) } if err := ctrl.stateMachine.Run(ctx, r, logger); err != nil { return fmt.Errorf("error running volume mounter machine: %w", err) } r.ResetRestartBackoff() } } func (ctrl *NodeIdentityController) establishNodeIdentity(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { var localIdentity cluster.IdentitySpec if err := controllers.LoadOrNewFromFile(root, constants.NodeIdentityFilename, &localIdentity, func(v *cluster.IdentitySpec) error { return clusteradapter.IdentitySpec(v).Generate() }); err != nil { return fmt.Errorf("error caching node identity: %w", err) } if err := safe.WriterModify(ctx, r, cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity), func(r *cluster.Identity) error { *r.TypedSpec() = localIdentity return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } // generate `/etc/machine-id` from node identity if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "machine-id"), func(r *files.EtcFileSpec) error { var err error r.TypedSpec().Contents, err = clusteradapter.IdentitySpec(&localIdentity).ConvertMachineID() r.TypedSpec().Mode = 0o444 r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return err }); err != nil { return fmt.Errorf("error modifying machine-id: %w", err) } logger.Info("node identity established", zap.String("node_id", localIdentity.NodeID)) return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/cluster/node_identity_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "os" "path/filepath" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) type NodeIdentitySuite struct { ctest.DefaultSuite } func (suite *NodeIdentitySuite) TestDefault() { statePath := suite.T().TempDir() mountID := (&clusterctrl.NodeIdentityController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) ctest.AssertNoResource[*cluster.Identity](suite, cluster.LocalIdentity) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, cluster.LocalIdentity, func(*cluster.Identity, *assert.Assertions) {}) ctest.AssertResource(suite, "machine-id", func(*files.EtcFileSpec, *assert.Assertions) {}) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) } func (suite *NodeIdentitySuite) TestLoad() { statePath := suite.T().TempDir() mountID := (&clusterctrl.NodeIdentityController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) // using verbatim data here to make sure nodeId representation is supported in future version fo Talos suite.Require().NoError(os.WriteFile(filepath.Join(statePath, constants.NodeIdentityFilename), []byte("nodeId: gvqfS27LxD58lPlASmpaueeRVzuof16iXoieRgEvBWaE\n"), 0o600)) ctest.AssertNoResource[*cluster.Identity](suite, cluster.LocalIdentity) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, cluster.LocalIdentity, func(identity *cluster.Identity, asrt *assert.Assertions) { asrt.Equal("gvqfS27LxD58lPlASmpaueeRVzuof16iXoieRgEvBWaE", identity.TypedSpec().NodeID) }) ctest.AssertResource(suite, "machine-id", func(f *files.EtcFileSpec, asrt *assert.Assertions) { asrt.Equal("8d2c0de2408fa2a178bad7f45d9aa8fb", string(f.TypedSpec().Contents)) }) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) } func TestNodeIdentitySuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } suite.Run(t, &NodeIdentitySuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&clusterctrl.NodeIdentityController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/config/acquire.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "bytes" "compress/gzip" "context" "encoding/base64" "errors" "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "slices" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/klauspost/compress/zstd" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "golang.org/x/sys/unix" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" talosruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" platformerrors "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/pkg/mount/v3" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" configresource "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" "github.com/siderolabs/talos/pkg/xfs" ) // PlatformConfigurator is a reduced interface of runtime.Platform. type PlatformConfigurator interface { Name() string Configuration(context.Context) ([]byte, error) } // PlatformEventer sends events based on the config process via platform-specific interface. type PlatformEventer interface { FireEvent(context.Context, platform.Event) } // Setter sets the current machine config. type Setter interface { SetConfig(config.Provider) error SetPersistedConfig(config.Provider) error } // ModeGetter gets the current runtime mode. type ModeGetter interface { InContainer() bool } // AcquireController loads the machine configuration from multiple sources. type AcquireController struct { PlatformConfiguration PlatformConfigurator PlatformEvent PlatformEventer Mode ModeGetter CmdlineGetter func() *procfs.Cmdline ConfigSetter Setter EventPublisher talosruntime.Publisher ValidationMode validation.RuntimeMode ResourceState state.State EmbeddedDirectory string configSourcesUsed []string stateMachine blockautomaton.VolumeMounterAutomaton diskConfig config.Provider storedEmbeddedConfig []byte skipMaskingEmbeddedConfig bool } // Name implements controller.Controller interface. func (ctrl *AcquireController) Name() string { return "config.AcquireController" } // Inputs implements controller.Controller interface. func (ctrl *AcquireController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.AcquireConfigSpecType, Kind: controller.InputWeak, }, { Namespace: configresource.NamespaceName, Type: configresource.MachineConfigType, ID: optional.Some(configresource.ActiveID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MaintenanceServiceRequestType, Kind: controller.InputDestroyReady, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, ID: optional.Some(constants.StatePartitionLabel), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *AcquireController) Outputs() []controller.Output { return []controller.Output{ { Type: v1alpha1.AcquireConfigStatusType, Kind: controller.OutputExclusive, }, { Type: runtime.MaintenanceServiceRequestType, Kind: controller.OutputExclusive, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // stateMachineFunc represents the state machine of config.AcquireController. type stateMachineFunc func(context.Context, controller.Runtime, *zap.Logger) (stateMachineFunc, config.Provider, error) // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *AcquireController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.EmbeddedDirectory == "" { ctrl.EmbeddedDirectory = constants.EmbeddedConfigDirectory } else { // if the embedded directory is overridden, we skip masking the embedded config // as we are in the test mode ctrl.skipMaskingEmbeddedConfig = true } // early on, load the embedded config, and store it in the controller struct // this way we can "mask" the config directory with an empty mount to prevent it from being read from the tree // by malicious workloads if err := ctrl.processEmbeddedConfig(logger); err != nil { return fmt.Errorf("failed to process embedded config: %w", err) } // start always with loading config from disk var currentState stateMachineFunc = ctrl.stateDisk // initialize with empty sources ctrl.configSourcesUsed = []string{} for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // check the spec first _, err := safe.ReaderGet[*v1alpha1.AcquireConfigSpec](ctx, r, v1alpha1.NewAcquireConfigSpec().Metadata()) if err != nil { if state.IsNotFoundError(err) { // spec is not found, wait for it continue } return fmt.Errorf("failed to get spec: %w", err) } // run the state machine for { newState, cfg, err := currentState(ctx, r, logger) if err != nil { ctrl.EventPublisher.Publish(ctx, &machineapi.ConfigLoadErrorEvent{ Error: err.Error(), }) ctrl.PlatformEvent.FireEvent( ctx, platform.Event{ Type: platform.EventTypeFailure, Message: "Error loading and validating Talos machine config.", Error: err, }, ) return err } if cfg != nil { // apply config if err = ctrl.ConfigSetter.SetConfig(cfg); err != nil { return fmt.Errorf("failed to set config: %w", err) } if !(len(ctrl.configSourcesUsed) == 1 && ctrl.configSourcesUsed[0] == "state") { // if the only source is state, we do not need to persist it if err = ctrl.ConfigSetter.SetPersistedConfig(cfg); err != nil { return fmt.Errorf("failed to set persisted config: %w", err) } } } if newState == nil { // wait for reconcile event, keep running in the same state break } currentState = newState } r.ResetRestartBackoff() } } // stateDisk acquires machine configuration from disk (STATE partition). // // Transitions: // // --> embedded: no config found on disk, proceed to embedded // --> maintenanceEnter: config found on disk, but it's incomplete, proceed to maintenance // --> done: config found on disk, and it's complete // //nolint:gocyclo func (ctrl *AcquireController) stateDisk(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { // check if the STATE is missing/available first, if it's missing, we skip the step stateVolumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, r, constants.StatePartitionLabel) if err != nil && !state.IsNotFoundError(err) { return nil, nil, fmt.Errorf("failed observing STATE volume status: %w", err) } switch { case stateVolumeStatus == nil: // wait for the status to be available return nil, nil, nil case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseMissing: // STATE is missing, proceed to stateEmbedded return ctrl.stateEmbedded, nil, nil case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseReady: // STATE is ready, proceed to to the action default: // wait for the definitive status return nil, nil, nil } cfg, done, err := ctrl.loadFromDisk(ctx, r, logger) if err != nil { return nil, nil, err } if !done { // wait for the state machine to finish return nil, nil, nil } if cfg != nil { ctrl.configSourcesUsed = append(ctrl.configSourcesUsed, "state") } switch { case cfg == nil: // no config loaded, proceed to cmdlineEarly return ctrl.stateEmbedded, nil, nil case cfg.CompleteForBoot(): // complete config, we are done return ctrl.stateDone, cfg, nil default: // incomplete config, proceed to maintenance return ctrl.stateMaintenanceEnter, cfg, nil } } // validationModeDiskConfig is a "fake" validation mode for config loaded from disk. type validationModeDiskConfig struct{} // RequiresInstall implements validation.RuntimeMode interface. func (validationModeDiskConfig) RequiresInstall() bool { return false } // InContainer implements validation.RuntimeMode interface. func (validationModeDiskConfig) InContainer() bool { // containers don't persist config to disk return false } // String implements validation.RuntimeMode interface. func (validationModeDiskConfig) String() string { return "diskConfig" } // loadFromDisk is a helper function for stateDisk. func (ctrl *AcquireController) loadFromDisk(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger) (config.Provider, bool, error) { if ctrl.stateMachine == nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.loadConfigFromDisk, blockautomaton.WithReadOnly(true), blockautomaton.WithDetached(true), ) } if err := ctrl.stateMachine.Run(ctx, r, logger, automaton.WithAfterFunc(func() error { ctrl.stateMachine = nil return nil }), ); err != nil { return nil, false, err } if ctrl.stateMachine == nil { // state machine finished return ctrl.diskConfig, true, nil } return nil, false, nil } func (ctrl *AcquireController) loadConfigFromDisk(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { configPath := constants.ConfigFilename logger.Debug("loading config from STATE", zap.String("path", configPath)) _, err := xfs.Stat(root, configPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { // no saved machine config return nil } return fmt.Errorf("failed to stat %s: %w", configPath, err) } cfg, err := loadConfig(root, configPath) if err != nil { return err } // if the STATE partition is present & contains machine config, Talos is already installed warnings, err := cfg.Validate(validationModeDiskConfig{}) if err != nil { return fmt.Errorf("failed to validate on-disk config: %w", err) } for _, warning := range warnings { logger.Warn("config validation warning", zap.String("warning", warning)) } // we can't return the value directly ctrl.diskConfig = cfg return nil }) } // stateEmbedded acquires machine configuration from the embedded file path. // // It is called before the cmdlineEarly source. // // Transitions: // // --> cmdlineEarly: config loaded from embedded, but it's incomplete, or no config: proceed to cmdlineEarly // --> done: config loaded from cmdline, and it's complete func (ctrl *AcquireController) stateEmbedded(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { cfg, err := ctrl.loadConfigFromEmbedded(logger) if err != nil { return nil, nil, err } if cfg != nil { ctrl.configSourcesUsed = append(ctrl.configSourcesUsed, "embedded") } switch { case cfg == nil: fallthrough case !cfg.CompleteForBoot(): // incomplete or missing config, proceed to cmdlineEarly return ctrl.stateCmdlineEarly, cfg, nil default: // complete config, we are done return ctrl.stateDone, cfg, nil } } func (ctrl *AcquireController) processEmbeddedConfig(logger *zap.Logger) error { configPath := filepath.Join(ctrl.EmbeddedDirectory, constants.ConfigFilename) _, err := os.Stat(configPath) if err != nil { if os.IsNotExist(err) { // no saved machine config return nil } return fmt.Errorf("failed to stat %s: %w", configPath, err) } cfgBytes, err := os.ReadFile(configPath) if err != nil { return fmt.Errorf("failed to read embedded config: %w", err) } logger.Info("initialized embedded config processing", zap.String("path", configPath)) ctrl.storedEmbeddedConfig = cfgBytes if ctrl.skipMaskingEmbeddedConfig { return nil } // we are not going to unmount this, so we don't store the point _, err = mount.NewManager( mount.WithTarget(ctrl.EmbeddedDirectory), mount.WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_RELATIME|unix.MOUNT_ATTR_RDONLY), mount.WithFsopen( "tmpfs", ), ).Mount() if err != nil { return fmt.Errorf("failed to mask embedded config directory %q: %w", ctrl.EmbeddedDirectory, err) } return nil } func (ctrl *AcquireController) loadConfigFromEmbedded(logger *zap.Logger) (config.Provider, error) { if ctrl.storedEmbeddedConfig == nil { // no embedded config return nil, nil } logger.Info("loading embedded config") cfg, err := configloader.NewFromBytes(ctrl.storedEmbeddedConfig) if err != nil { return nil, fmt.Errorf("failed to load config from embedded: %w", err) } // if the STATE partition is present & contains machine config, Talos is already installed warnings, err := cfg.Validate(validationModeDiskConfig{}) if err != nil { return nil, fmt.Errorf("failed to validate embedded config: %w", err) } for _, warning := range warnings { logger.Warn("config validation warning", zap.String("warning", warning)) } return cfg, nil } // stateCmdlineEarly acquires machine configuration from the kernel cmdline source (talos.config.early). // // It is called before the platform source. // // Transitions: // // --> platform: config loaded from cmdline, but it's incomplete, or no config: proceed to platform // --> done: config loaded from cmdline, and it's complete func (ctrl *AcquireController) stateCmdlineEarly(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { return ctrl.stateCmdlineGeneric(constants.KernelParamConfigEarly, "cmdline-early", ctrl.statePlatform)(ctx, r, logger) } // statePlatform acquires machine configuration from the platform source. // // Transitions: // // --> cmdlineLate: config loaded from platform, but it's incomplete, or no config from platform: proceed to cmdline // --> done: config loaded from platform, and it's complete func (ctrl *AcquireController) statePlatform(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { cfg, err := ctrl.loadFromPlatform(ctx, logger) if err != nil { return nil, nil, err } if cfg != nil { ctrl.configSourcesUsed = append(ctrl.configSourcesUsed, ctrl.PlatformConfiguration.Name()) } switch { case cfg == nil: fallthrough case !cfg.CompleteForBoot(): // incomplete or missing config, proceed to maintenance return ctrl.stateCmdlineLate, cfg, nil default: // complete config, we are done return ctrl.stateDone, cfg, nil } } // loadFromPlatform is a helper function for statePlatform. func (ctrl *AcquireController) loadFromPlatform(ctx context.Context, logger *zap.Logger) (config.Provider, error) { platformName := ctrl.PlatformConfiguration.Name() logger.Info("downloading config", zap.String("platform", platformName)) cfgBytes, err := ctrl.PlatformConfiguration.Configuration(ctx) if err != nil { if errors.Is(err, platformerrors.ErrNoConfigSource) { // no config in the platform return nil, nil } return nil, fmt.Errorf("error acquiring via platform %s: %w", platformName, err) } // Detect if config is a gzip archive and unzip it if so contentType := http.DetectContentType(cfgBytes) if contentType == "application/x-gzip" { var gzipReader *gzip.Reader gzipReader, err = gzip.NewReader(bytes.NewReader(cfgBytes)) if err != nil { return nil, fmt.Errorf("error creating gzip reader: %w", err) } //nolint:errcheck defer gzipReader.Close() var unzippedData []byte unzippedData, err = io.ReadAll(gzipReader) if err != nil { return nil, fmt.Errorf("error unzipping machine config: %w", err) } cfgBytes = unzippedData } cfg, err := configloader.NewFromBytes(cfgBytes) if err != nil { return nil, fmt.Errorf("failed to load config via platform %s: %w", platformName, err) } warnings, err := cfg.Validate(ctrl.ValidationMode) if err != nil { return nil, fmt.Errorf("failed to validate config acquired via platform %s: %w", platformName, err) } warningsRuntime, err := cfg.RuntimeValidate(ctx, ctrl.ResourceState, ctrl.ValidationMode) if err != nil { return nil, fmt.Errorf("failed to runtime validate config acquired via platform %s: %w", platformName, err) } for _, warning := range slices.Concat(warnings, warningsRuntime) { logger.Warn("config validation warning", zap.String("platform", platformName), zap.String("warning", warning)) } return cfg, nil } // stateCmdlineLate acquires machine configuration from the kernel cmdline source (talos.config.inline). // // It is called after the platform source. // // Transitions: // // --> maintenanceEnter: config loaded from cmdline, but it's incomplete, or no config from cmdline: proceed to maintenance // --> done: config loaded from cmdline, and it's complete func (ctrl *AcquireController) stateCmdlineLate(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { return ctrl.stateCmdlineGeneric(constants.KernelParamConfigInline, "cmdline-late", ctrl.stateMaintenanceEnter)(ctx, r, logger) } // stateCmdlineGeneric is a generic function to load config from cmdline given a parameter name and source name, and the next state in the state machine. func (ctrl *AcquireController) stateCmdlineGeneric( paramName, sourceName string, next stateMachineFunc, ) func(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { return func(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { if ctrl.Mode.InContainer() { // no cmdline in containers return next, nil, nil } cfg, err := ctrl.loadFromCmdline(ctx, logger, paramName) if err != nil { return nil, nil, err } if cfg != nil { ctrl.configSourcesUsed = append(ctrl.configSourcesUsed, sourceName) } switch { case cfg == nil: fallthrough case !cfg.CompleteForBoot(): // incomplete or missing config, proceed to maintenance return next, cfg, nil default: // complete config, we are done return ctrl.stateDone, cfg, nil } } } // loadFromCmdline is a helper function for stateCmdline. // //nolint:gocyclo func (ctrl *AcquireController) loadFromCmdline(ctx context.Context, logger *zap.Logger, paramName string) (config.Provider, error) { cmdline := ctrl.CmdlineGetter() param := cmdline.Get(paramName) if param == nil { return nil, nil } logger.Info("getting config from cmdline", zap.String("param", paramName)) var cfgEncoded strings.Builder for i := 0; ; i++ { v := param.Get(i) if v == nil { break } cfgEncoded.WriteString(*v) } cfgDecoded, err := base64.StdEncoding.DecodeString(cfgEncoded.String()) if err != nil { return nil, fmt.Errorf("failed to decode base64 config from cmdline %s: %w", paramName, err) } zr, err := zstd.NewReader(bytes.NewReader(cfgDecoded)) if err != nil { return nil, fmt.Errorf("failed to create zstd reader: %w", err) } defer zr.Close() cfgBytes, err := io.ReadAll(zr) if err != nil { return nil, fmt.Errorf("failed to read zstd compressed config from cmdline %s: %w", paramName, err) } cfg, err := configloader.NewFromBytes(cfgBytes) if err != nil { return nil, fmt.Errorf("failed to load config via cmdline %s: %w", paramName, err) } warnings, err := cfg.Validate(ctrl.ValidationMode) if err != nil { return nil, fmt.Errorf("failed to validate config acquired via cmdline %s: %w", paramName, err) } warningsRuntime, err := cfg.RuntimeValidate(ctx, ctrl.ResourceState, ctrl.ValidationMode) if err != nil { return nil, fmt.Errorf("failed to validate config acquired via cmdline %s: %w", paramName, err) } for _, warning := range slices.Concat(warnings, warningsRuntime) { logger.Warn("config validation warning", zap.String("cmdline", paramName), zap.String("warning", warning)) } return cfg, nil } // stateMaintenanceEnter initializes maintenance service. // // Transitions: // // --> stateMaintenance: run the maintenance service func (ctrl *AcquireController) stateMaintenanceEnter(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { logger.Info("entering maintenance service") // nb: we treat maintenance mode as an "activate" // event b/c the user is expected to be able to // interact with the system at this point. ctrl.PlatformEvent.FireEvent( ctx, platform.Event{ Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, ) // add "fake" events to signal when Talos enters and leaves maintenance mode ctrl.EventPublisher.Publish(ctx, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }) return ctrl.stateMaintenance, nil, nil } // stateMaintenance acquires machine configuration from the maintenance service. // // Transitions: // // --> maintenanceLeave: config loaded from maintenance service, and it's complete func (ctrl *AcquireController) stateMaintenance(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { // init maintenance if err := safe.WriterModify(ctx, r, runtime.NewMaintenanceServiceRequest(), func(*runtime.MaintenanceServiceRequest) error { return nil }); err != nil { return nil, nil, fmt.Errorf("failed creating maintenance service request: %w", err) } // check current config cfgResource, err := safe.ReaderGetByID[*configresource.MachineConfig](ctx, r, configresource.ActiveID) if err != nil { if state.IsNotFoundError(err) { // no config loaded, wait for it return nil, nil, nil } return nil, nil, fmt.Errorf("failed to get maintenance config: %w", err) } cfg := cfgResource.Provider() if cfg.CompleteForBoot() { // complete config, we are done ctrl.configSourcesUsed = append(ctrl.configSourcesUsed, "maintenance") return ctrl.stateMaintenanceLeave, nil, nil } // incomplete config, keep waiting return nil, nil, nil } // stateMaintenanceLeave leaves the maintenance service. // // Transitions: // // --> done: proceed to done state func (ctrl *AcquireController) stateMaintenanceLeave(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { // stop the maintenance service ready, err := r.Teardown(ctx, runtime.NewMaintenanceServiceRequest().Metadata()) switch { case err != nil && !state.IsNotFoundError(err): return nil, nil, fmt.Errorf("failed to tear down maintenance service: %w", err) case err == nil && !ready: // wait for the maintenance service to be torn down return nil, nil, nil case err == nil && ready: if err = r.Destroy(ctx, runtime.NewMaintenanceServiceRequest().Metadata()); err != nil { return nil, nil, fmt.Errorf("failed cleaning up maintenance service request: %w", err) } } ctrl.EventPublisher.Publish(ctx, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }) logger.Info("leaving maintenance service") return ctrl.stateDone, nil, nil } // stateDone is the final state of the controller. func (ctrl *AcquireController) stateDone(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { if err := safe.WriterModify(ctx, r, v1alpha1.NewAcquireConfigStatus(), func(_ *v1alpha1.AcquireConfigStatus) error { return nil }); err != nil { return nil, nil, fmt.Errorf("failed to write status: %w", err) } ctrl.PlatformEvent.FireEvent( ctx, platform.Event{ Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, ) logger.Info("machine config loaded successfully", zap.Strings("sources", ctrl.configSourcesUsed)) // fall through to the controller loop return ctrl.stateFinal, nil, nil } // stateFinal just makes the controller do nothing. func (ctrl *AcquireController) stateFinal(ctx context.Context, r controller.Runtime, logger *zap.Logger) (stateMachineFunc, config.Provider, error) { return nil, nil, nil } func loadConfig(root xfs.Root, configPath string) (config.Provider, error) { f, err := xfs.Open(root, configPath) if err != nil { return nil, fmt.Errorf("failed to open %q from STATE: %w", configPath, err) } defer f.Close() //nolint:errcheck cfg, err := configloader.NewFromReader(f) if err != nil { return nil, fmt.Errorf("failed to load %q from STATE: %w", configPath, err) } return cfg, nil } ================================================ FILE: internal/app/machined/pkg/controllers/config/acquire_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( "bytes" "compress/gzip" "context" "encoding/base64" stderrors "errors" "fmt" "math/rand/v2" "net/url" "os" "path/filepath" "slices" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/klauspost/compress/zstd" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" configctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/config" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/block" configresource "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) type AcquireSuite struct { ctest.DefaultSuite platformConfig *platformConfigMock platformEvent *platformEventMock configSetter *configSetterMock eventPublisher *eventPublisherMock cmdline *cmdlineGetterMock embeddedPath string clusterName string completeMachineConfig []byte partialMachineConfig []byte } type platformConfigMock struct { configuration []byte err error } func (p *platformConfigMock) Configuration(context.Context) ([]byte, error) { return p.configuration, p.err } func (p *platformConfigMock) Name() string { return "mock" } type platformEventMock struct { mu sync.Mutex events []platform.Event } func (p *platformEventMock) FireEvent(_ context.Context, ev platform.Event) { p.mu.Lock() defer p.mu.Unlock() p.events = append(p.events, ev) } func (p *platformEventMock) getEvents() []platform.Event { p.mu.Lock() defer p.mu.Unlock() return slices.Clone(p.events) } type configSetterMock struct { cfgCh chan config.Provider persistedCfgCh chan config.Provider } func (c *configSetterMock) SetConfig(cfg config.Provider) error { c.cfgCh <- cfg return nil } func (c *configSetterMock) SetPersistedConfig(cfg config.Provider) error { c.persistedCfgCh <- cfg return nil } type eventPublisherMock struct { mu sync.Mutex events []proto.Message } func (e *eventPublisherMock) Publish(_ context.Context, ev proto.Message) { e.mu.Lock() defer e.mu.Unlock() e.events = append(e.events, ev) } func (e *eventPublisherMock) getEvents() []proto.Message { e.mu.Lock() defer e.mu.Unlock() return slices.Clone(e.events) } type cmdlineGetterMock struct { cmdline *procfs.Cmdline } func (c *cmdlineGetterMock) Getter() func() *procfs.Cmdline { return func() *procfs.Cmdline { return c.cmdline } } type validationModeMock struct{} func (v validationModeMock) String() string { return "mock" } func (v validationModeMock) RequiresInstall() bool { return false } func (v validationModeMock) InContainer() bool { return false } func TestAcquireSuite(t *testing.T) { t.Parallel() s := &AcquireSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 15 * time.Second, }, } s.DefaultSuite.AfterSetup = func(*ctest.DefaultSuite) { s.platformConfig = &platformConfigMock{ err: errors.ErrNoConfigSource, } s.platformEvent = &platformEventMock{} s.configSetter = &configSetterMock{ cfgCh: make(chan config.Provider, 1), persistedCfgCh: make(chan config.Provider, 1), } s.eventPublisher = &eventPublisherMock{} s.cmdline = &cmdlineGetterMock{ procfs.NewCmdline(""), } s.clusterName = fmt.Sprintf("cluster-%d", rand.Int32()) input, err := generate.NewInput(s.clusterName, "https://localhost:6443", "") s.Require().NoError(err) cfg, err := input.Config(machine.TypeControlPlane) s.Require().NoError(err) s.completeMachineConfig, err = cfg.Bytes() s.Require().NoError(err) sideroLinkCfg := siderolink.NewConfigV1Alpha1() sideroLinkCfg.APIUrlConfig.URL = must(url.Parse("https://siderolink.api/?jointoken=secret&user=alice")) pCfg, err := container.New(sideroLinkCfg) s.Require().NoError(err) s.partialMachineConfig, err = pCfg.Bytes() s.Require().NoError(err) s.embeddedPath = t.TempDir() s.Require().NoError(s.Runtime().RegisterController(&configctrl.AcquireController{ PlatformConfiguration: s.platformConfig, PlatformEvent: s.platformEvent, ConfigSetter: s.configSetter, Mode: validationModeMock{}, CmdlineGetter: s.cmdline.Getter(), EventPublisher: s.eventPublisher, ValidationMode: validationModeMock{}, ResourceState: s.State(), EmbeddedDirectory: s.embeddedPath, })) } suite.Run(t, s) } func (suite *AcquireSuite) triggerAcquire() { suite.Require().NoError(suite.State().Create(suite.Ctx(), v1alpha1.NewAcquireConfigSpec())) } func (suite *AcquireSuite) waitForConfig(shouldPersist bool) config.Provider { var ( appliedConfig config.Provider persistedConfig config.Provider ) for { select { case cfg := <-suite.configSetter.cfgCh: suite.Require().Nil(appliedConfig) appliedConfig = cfg case cfg := <-suite.configSetter.persistedCfgCh: suite.Require().Nil(persistedConfig) suite.Require().True(shouldPersist) persistedConfig = cfg case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config: applied %v persisted %v", appliedConfig, persistedConfig) } if appliedConfig != nil && (persistedConfig != nil || !shouldPersist) { break } } if persistedConfig != nil { suite.Assert().Same(persistedConfig, appliedConfig) } status := v1alpha1.NewAcquireConfigStatus() rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{status.Metadata().ID()}, func(*v1alpha1.AcquireConfigStatus, *assert.Assertions) {}) return appliedConfig } func (suite *AcquireSuite) injectViaMaintenance(cfg []byte) { _, err := suite.State().WatchFor(suite.Ctx(), runtime.NewMaintenanceServiceRequest().Metadata(), state.WithEventTypes(state.Created)) suite.Require().NoError(err) mCfg, err := configloader.NewFromBytes(cfg) suite.Require().NoError(err) existingCfg, err := safe.StateGetByID[*configresource.MachineConfig](suite.Ctx(), suite.State(), configresource.ActiveID) if err != nil && !state.IsNotFoundError(err) { suite.Require().NoError(err) } newCfg := configresource.NewMachineConfigWithID(mCfg, configresource.ActiveID) if existingCfg == nil { suite.Create(newCfg) } else { newCfg.Metadata().SetVersion(existingCfg.Metadata().Version()) suite.Update(newCfg) } _, err = suite.State().WatchFor(suite.Ctx(), runtime.NewMaintenanceServiceRequest().Metadata(), state.WithEventTypes(state.Destroyed)) suite.Require().NoError(err) } func (suite *AcquireSuite) noStateVolume() { volumeStatus := block.NewVolumeStatus(block.NamespaceName, constants.StatePartitionLabel) volumeStatus.TypedSpec().Phase = block.VolumePhaseMissing suite.Create(volumeStatus) } func (suite *AcquireSuite) presentStateVolume() { volumeStatus := block.NewVolumeStatus(block.NamespaceName, constants.StatePartitionLabel) volumeStatus.TypedSpec().Phase = block.VolumePhaseReady suite.Create(volumeStatus) } func (suite *AcquireSuite) injectViaDisk(cfg []byte, wait bool) { statePath := suite.T().TempDir() mountID := (&configctrl.AcquireController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) suite.Require().NoError(os.WriteFile(filepath.Join(statePath, constants.ConfigFilename), cfg, 0o644)) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) if wait { ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) suite.Destroy(volumeMountStatus) } } func (suite *AcquireSuite) TestFromDisk() { if os.Geteuid() != 0 { suite.T().Skip("skipping test that requires root privileges") } suite.presentStateVolume() suite.triggerAcquire() suite.injectViaDisk(suite.completeMachineConfig, true) cfg := suite.waitForConfig(false) suite.Require().Equal(cfg.Cluster().Name(), suite.clusterName) suite.Assert().Empty(suite.eventPublisher.getEvents()) suite.Assert().Equal( []platform.Event{ { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) } func (suite *AcquireSuite) TestFromDiskFailure() { if os.Geteuid() != 0 { suite.T().Skip("skipping test that requires root privileges") } suite.presentStateVolume() suite.triggerAcquire() suite.injectViaDisk(slices.Concat([]byte("aaa"), suite.completeMachineConfig), false) suite.AssertWithin(time.Second, 10*time.Millisecond, func() error { if len(suite.platformEvent.getEvents()) == 0 || len(suite.eventPublisher.getEvents()) == 0 { return retry.ExpectedErrorf("no events received") } return nil }) ev := suite.platformEvent.getEvents()[0] suite.Assert().Equal(platform.EventTypeFailure, ev.Type) suite.Assert().Equal("Error loading and validating Talos machine config.", ev.Message) suite.Assert().Equal( "failed to load \"config.yaml\" from STATE: error decoding document /v1alpha1/ (line 1): unknown keys found during decoding:\n"+ "aaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n", ev.Error.Error(), ) suite.Assert().Equal(&machineapi.ConfigLoadErrorEvent{ Error: "failed to load \"config.yaml\" from STATE: error decoding document /v1alpha1/ (line 1): unknown keys found during decoding:\n" + "aaaversion: v1alpha1 # Indicates the schema used to decode the contents.\n", }, suite.eventPublisher.getEvents()[0]) } func (suite *AcquireSuite) TestFromDiskToMaintenance() { if os.Geteuid() != 0 { suite.T().Skip("skipping test that requires root privileges") } suite.presentStateVolume() suite.triggerAcquire() suite.injectViaDisk(suite.partialMachineConfig, true) var cfg config.Provider select { case cfg = <-suite.configSetter.cfgCh: case <-suite.configSetter.persistedCfgCh: suite.Require().Fail("should not persist") case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config") } suite.Require().Equal(cfg.SideroLink().APIUrl().Host, "siderolink.api") // no asserts here, as maintenance injects the config bypassing the controller suite.injectViaMaintenance(suite.completeMachineConfig) suite.Assert().Equal( []proto.Message{ &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }, }, suite.eventPublisher.getEvents(), ) suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { assert.New(collect).Equal( []platform.Event{ { Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) }, 2*time.Second, 10*time.Millisecond, ) } func (suite *AcquireSuite) TestFromPlatform() { suite.noStateVolume() suite.platformConfig.configuration = suite.completeMachineConfig suite.platformConfig.err = nil suite.triggerAcquire() cfg := suite.waitForConfig(true) suite.Require().Equal(cfg.Cluster().Name(), suite.clusterName) suite.Assert().Empty(suite.eventPublisher.getEvents()) suite.Assert().Equal( []platform.Event{ { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) } func (suite *AcquireSuite) TestFromPlatformFailure() { suite.noStateVolume() suite.platformConfig.err = stderrors.New("mock error") suite.triggerAcquire() suite.AssertWithin(time.Second, 10*time.Millisecond, func() error { if len(suite.platformEvent.getEvents()) == 0 || len(suite.eventPublisher.getEvents()) == 0 { return retry.ExpectedErrorf("no events received") } return nil }) ev := suite.platformEvent.getEvents()[0] suite.Assert().Equal(platform.EventTypeFailure, ev.Type) suite.Assert().Equal("Error loading and validating Talos machine config.", ev.Message) suite.Assert().Equal("error acquiring via platform mock: mock error", ev.Error.Error()) suite.Assert().Equal(&machineapi.ConfigLoadErrorEvent{ Error: "error acquiring via platform mock: mock error", }, suite.eventPublisher.getEvents()[0]) } func (suite *AcquireSuite) TestFromPlatformNotValid() { suite.noStateVolume() patchCfg, err := configloader.NewFromBytes([]byte(`{"machine": {"nodeLabels": {"/1": "2"}}}`)) suite.Require().NoError(err) out, err := configpatcher.Apply(configpatcher.WithBytes(suite.completeMachineConfig), []configpatcher.Patch{ configpatcher.NewStrategicMergePatch(patchCfg), }) suite.Require().NoError(err) outCfg, err := out.Bytes() suite.Require().NoError(err) suite.platformConfig.configuration = outCfg suite.platformConfig.err = nil suite.triggerAcquire() suite.AssertWithin(time.Second, 10*time.Millisecond, func() error { if len(suite.platformEvent.getEvents()) == 0 || len(suite.eventPublisher.getEvents()) == 0 { return retry.ExpectedErrorf("no events received") } return nil }) ev := suite.platformEvent.getEvents()[0] suite.Assert().Equal(platform.EventTypeFailure, ev.Type) suite.Assert().Equal("Error loading and validating Talos machine config.", ev.Message) suite.Assert().Equal( "failed to validate config acquired via platform mock: 1 error occurred:\n"+ "\t* v1alpha1.Config: 1 error occurred:\n\t* invalid machine node labels: 1 error occurred:\n\t* prefix cannot be empty: \"/1\"\n\n\n\n\n\n", ev.Error.Error(), ) suite.Assert().Equal(&machineapi.ConfigLoadErrorEvent{ Error: "failed to validate config acquired via platform mock: 1 error occurred:" + "\n\t* v1alpha1.Config: 1 error occurred:\n\t* invalid machine node labels: 1 error occurred:\n\t* prefix cannot be empty: \"/1\"\n\n\n\n\n\n", }, suite.eventPublisher.getEvents()[0]) } func (suite *AcquireSuite) TestFromPlatformGzip() { var buf bytes.Buffer gz := gzip.NewWriter(&buf) _, err := gz.Write(suite.completeMachineConfig) suite.Require().NoError(err) suite.Require().NoError(gz.Close()) suite.platformConfig.configuration = buf.Bytes() suite.platformConfig.err = nil suite.noStateVolume() suite.triggerAcquire() cfg := suite.waitForConfig(true) suite.Require().Equal(cfg.Cluster().Name(), suite.clusterName) suite.Assert().Empty(suite.eventPublisher.getEvents()) suite.Assert().Equal( []platform.Event{ { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) } func (suite *AcquireSuite) TestFromPlatformToMaintenance() { suite.platformConfig.configuration = suite.partialMachineConfig suite.platformConfig.err = nil suite.noStateVolume() suite.triggerAcquire() var cfg config.Provider select { case cfg = <-suite.configSetter.cfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config") } select { case <-suite.configSetter.persistedCfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for persisted config") } suite.Require().Equal(cfg.SideroLink().APIUrl().Host, "siderolink.api") // no asserts here, as maintenance injects the config bypassing the controller suite.injectViaMaintenance(suite.completeMachineConfig) suite.Assert().Equal( []proto.Message{ &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }, }, suite.eventPublisher.getEvents(), ) suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { assert.New(collect).Equal( []platform.Event{ { Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) }, 2*time.Second, 10*time.Millisecond, ) } func (suite *AcquireSuite) TestFromCmdlineLateToMaintenance() { var cfgCompressed bytes.Buffer zw, err := zstd.NewWriter(&cfgCompressed) suite.Require().NoError(err) _, err = zw.Write(suite.partialMachineConfig) suite.Require().NoError(err) suite.Require().NoError(zw.Close()) cfgEncoded := base64.StdEncoding.EncodeToString(cfgCompressed.Bytes()) suite.cmdline.cmdline = procfs.NewCmdline(fmt.Sprintf("%s=%s", constants.KernelParamConfigInline, cfgEncoded)) suite.noStateVolume() suite.triggerAcquire() var cfg config.Provider select { case cfg = <-suite.configSetter.cfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config") } select { case <-suite.configSetter.persistedCfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for persisted config") } suite.Require().Equal(cfg.SideroLink().APIUrl().Host, "siderolink.api") // no asserts here, as maintenance injects the config bypassing the controller suite.injectViaMaintenance(suite.completeMachineConfig) suite.Assert().Equal( []proto.Message{ &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }, }, suite.eventPublisher.getEvents(), ) suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { assert.New(collect).Equal( []platform.Event{ { Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) }, 2*time.Second, 10*time.Millisecond, ) } func (suite *AcquireSuite) TestFromCmdlineEarlyToPlatform() { var cfgCompressed bytes.Buffer zw, err := zstd.NewWriter(&cfgCompressed) suite.Require().NoError(err) _, err = zw.Write(suite.partialMachineConfig) suite.Require().NoError(err) suite.Require().NoError(zw.Close()) cfgEncoded := base64.StdEncoding.EncodeToString(cfgCompressed.Bytes()) suite.cmdline.cmdline = procfs.NewCmdline(fmt.Sprintf("%s=%s", constants.KernelParamConfigEarly, cfgEncoded)) suite.noStateVolume() suite.platformConfig.configuration = suite.completeMachineConfig suite.platformConfig.err = nil suite.triggerAcquire() var cfg config.Provider select { case cfg = <-suite.configSetter.cfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config") } select { case <-suite.configSetter.persistedCfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for persisted config") } suite.Require().Equal(cfg.SideroLink().APIUrl().Host, "siderolink.api") cfg = suite.waitForConfig(true) suite.Require().Equal(cfg.Cluster().Name(), suite.clusterName) suite.Assert().Empty(suite.eventPublisher.getEvents()) suite.Assert().Equal( []platform.Event{ { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) } func (suite *AcquireSuite) TestFromMaintenance() { suite.noStateVolume() suite.triggerAcquire() // no asserts here, as maintenance injects the config bypassing the controller suite.injectViaMaintenance(suite.completeMachineConfig) suite.Assert().Equal( []proto.Message{ &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }, }, suite.eventPublisher.getEvents(), ) suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { assert.New(collect).Equal( []platform.Event{ { Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) }, 2*time.Second, 10*time.Millisecond, ) } func (suite *AcquireSuite) TestFromEmbeddedToMaintenance() { suite.Require().NoError(os.WriteFile(filepath.Join(suite.embeddedPath, constants.ConfigFilename), suite.partialMachineConfig, 0o644)) suite.noStateVolume() suite.triggerAcquire() var cfg config.Provider select { case cfg = <-suite.configSetter.cfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for config") } select { case <-suite.configSetter.persistedCfgCh: case <-suite.Ctx().Done(): suite.Require().Fail("timed out waiting for persisted config") } suite.Require().Equal(cfg.SideroLink().APIUrl().Host, "siderolink.api") // no asserts here, as maintenance injects the config bypassing the controller suite.injectViaMaintenance(suite.completeMachineConfig) suite.Assert().Equal( []proto.Message{ &machineapi.TaskEvent{ Action: machineapi.TaskEvent_START, Task: "runningMaintenance", }, &machineapi.TaskEvent{ Action: machineapi.TaskEvent_STOP, Task: "runningMaintenance", }, }, suite.eventPublisher.getEvents(), ) suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { assert.New(collect).Equal( []platform.Event{ { Type: platform.EventTypeActivate, Message: "Talos booted into maintenance mode. Ready for user interaction.", }, { Type: platform.EventTypeConfigLoaded, Message: "Talos machine config loaded successfully.", }, }, suite.platformEvent.getEvents(), ) }, 2*time.Second, 10*time.Millisecond, ) } func must[T any](t T, err error) T { if err != nil { panic(err) } return t } ================================================ FILE: internal/app/machined/pkg/controllers/config/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package config provides controllers which manage config resources. package config ================================================ FILE: internal/app/machined/pkg/controllers/config/machine_type.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // MachineTypeController manages config.MachineType based on configuration. type MachineTypeController struct{} // Name implements controller.Controller interface. func (ctrl *MachineTypeController) Name() string { return "config.MachineTypeController" } // Inputs implements controller.Controller interface. func (ctrl *MachineTypeController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MachineTypeController) Outputs() []controller.Output { return []controller.Output{ { Type: config.MachineTypeType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *MachineTypeController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var machineType machine.Type cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else if cfg.Config().Machine() != nil { machineType = cfg.Config().Machine().Type() } if err = safe.WriterModify(ctx, r, config.NewMachineType(), func(r *config.MachineType) error { r.SetMachineType(machineType) return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/config/persistence.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/xfs" ) // PersistenceController ensures that the machine configuration is persisted in STATE partition. type PersistenceController struct { lastPersistedVersion resource.Version configToPersist *config.MachineConfig stateMachine blockautomaton.VolumeMounterAutomaton } // Name implements controller.Controller interface. func (ctrl *PersistenceController) Name() string { return "config.PersistenceController" } // Inputs implements controller.Controller interface. func (ctrl *PersistenceController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.PersistentID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, ID: optional.Some(constants.StatePartitionLabel), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeLifecycleType, ID: optional.Some(block.VolumeLifecycleID), Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *PersistenceController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *PersistenceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } volumeLifecycle, err := safe.ReaderGetByID[*block.VolumeLifecycle](ctx, r, block.VolumeLifecycleID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching volume lifecycle: %w", err) } if volumeLifecycle == nil { // no volume lifecycle, cease all operations continue } if volumeLifecycle.Metadata().Phase() == resource.PhaseRunning { if err = r.AddFinalizer(ctx, volumeLifecycle.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer to volume lifecycle: %w", err) } } stateStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, r, constants.StatePartitionLabel) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error fetching STATE volume status: %w", err) } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.PersistentID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } if cfg != nil && !ctrl.lastPersistedVersion.Equal(cfg.Metadata().Version()) { // if the version is newer than the last persisted version ctrl.configToPersist = cfg } if ctrl.stateMachine == nil && ctrl.configToPersist != nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.persistMachineConfig, blockautomaton.WithDetached(true), ) } if ctrl.stateMachine != nil { err := ctrl.stateMachine.Run(ctx, r, logger, automaton.WithAfterFunc(func() error { ctrl.stateMachine = nil r.QueueReconcile() return nil }), ) if err != nil { return fmt.Errorf("error running state machine: %w", err) } } if volumeLifecycle.Metadata().Phase() == resource.PhaseTearingDown { if ctrl.configToPersist == nil || (stateStatus != nil && stateStatus.TypedSpec().Phase == block.VolumePhaseMissing) { if err = r.RemoveFinalizer(ctx, volumeLifecycle.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } } } r.ResetRestartBackoff() } } func (ctrl *PersistenceController) persistMachineConfig(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { tempName := constants.ConfigFilename + "-tmp" configContents, err := ctrl.configToPersist.Provider().Bytes() if err != nil { return fmt.Errorf("error getting config bytes: %w", err) } if err = xfs.WriteFile(root, tempName, configContents, 0o600); err != nil { return fmt.Errorf("error writing config to file: %w", err) } if err = xfs.Rename(root, tempName, constants.ConfigFilename); err != nil { return fmt.Errorf("error renaming config file: %w", err) } logger.Info("machine configuration persisted to STATE") ctrl.lastPersistedVersion = ctrl.configToPersist.Metadata().Version() ctrl.configToPersist = nil return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/config/persistence_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( "net/url" "os" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" configctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/config" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) type PersistenceSuite struct { ctest.DefaultSuite cfg1, cfg2 talosconfig.Provider } func (suite *PersistenceSuite) TestPersist() { volumeLifecycle := block.NewVolumeLifecycle(block.NamespaceName, block.VolumeLifecycleID) suite.Create(volumeLifecycle) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.False(vl.Metadata().Finalizers().Empty()) }) statePath := suite.T().TempDir() mountID := (&configctrl.PersistenceController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) c1 := config.NewMachineConfigWithID(suite.cfg1, config.PersistentID) suite.Create(c1) ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) asrt.FileExists(filepath.Join(statePath, constants.ConfigFilename)) }, time.Second, 10*time.Millisecond) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) c2 := config.NewMachineConfigWithID(suite.cfg2, config.PersistentID) c2.Metadata().SetVersion(c1.Metadata().Version()) suite.Update(c2) ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) // teardown the volume lifecycle, but finalizer should not be removed yet _, err := suite.State().Teardown(suite.Ctx(), volumeLifecycle.Metadata()) suite.Require().NoError(err) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.False(vl.Metadata().Finalizers().Empty()) }) volumeMountStatus = block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) contents, err := os.ReadFile(filepath.Join(statePath, constants.ConfigFilename)) asrt.NoError(err) asrt.Contains(string(contents), "jointoken=none") }, time.Second, 10*time.Millisecond) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.True(vl.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeLifecycle) } func (suite *PersistenceSuite) TestConfig() { volumeLifecycle := block.NewVolumeLifecycle(block.NamespaceName, block.VolumeLifecycleID) suite.Create(volumeLifecycle) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.False(vl.Metadata().Finalizers().Empty()) }) _, err := suite.State().Teardown(suite.Ctx(), volumeLifecycle.Metadata()) suite.Require().NoError(err) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.True(vl.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeLifecycle) } func (suite *PersistenceSuite) TestNoPersistenceWithMissingState() { volumeLifecycle := block.NewVolumeLifecycle(block.NamespaceName, block.VolumeLifecycleID) suite.Create(volumeLifecycle) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.False(vl.Metadata().Finalizers().Empty()) }) c1 := config.NewMachineConfigWithID(suite.cfg1, config.PersistentID) suite.Create(c1) _, err := suite.State().Teardown(suite.Ctx(), volumeLifecycle.Metadata()) suite.Require().NoError(err) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.False(vl.Metadata().Finalizers().Empty()) }) // simulate STATE missing volumeStatus := block.NewVolumeStatus(block.NamespaceName, constants.StatePartitionLabel) volumeStatus.TypedSpec().Phase = block.VolumePhaseMissing suite.Create(volumeStatus) ctest.AssertResource(suite, block.VolumeLifecycleID, func(vl *block.VolumeLifecycle, asrt *assert.Assertions) { asrt.True(vl.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeLifecycle) } func TestPersistenceSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } sideroLinkCfg1 := siderolink.NewConfigV1Alpha1() sideroLinkCfg1.APIUrlConfig.URL = must(url.Parse("https://siderolink.api/?jointoken=secret&user=alice")) cfg1, err := container.New(sideroLinkCfg1) require.NoError(t, err) sideroLinkCfg2 := siderolink.NewConfigV1Alpha1() sideroLinkCfg2.APIUrlConfig.URL = must(url.Parse("https://siderolink.api/?jointoken=none&user=bob")) cfg2, err := container.New(sideroLinkCfg2) require.NoError(t, err) suite.Run(t, &PersistenceSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&configctrl.PersistenceController{})) }, }, cfg1: cfg1, cfg2: cfg2, }) } ================================================ FILE: internal/app/machined/pkg/controllers/cri/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cri provides CRI related controllers. package cri ================================================ FILE: internal/app/machined/pkg/controllers/cri/cri_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test ================================================ FILE: internal/app/machined/pkg/controllers/cri/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri // BuildExpectedDigests is exported for testing. var BuildExpectedDigests = buildExpectedDigests ================================================ FILE: internal/app/machined/pkg/controllers/cri/image_cache_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" "io" "io/fs" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/dustin/go-humanize" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" "github.com/siderolabs/gen/optional" "go.uber.org/zap" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" cfg "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // ServiceManager is the interface to the v1alpha1 services subsystems. type ServiceManager interface { IsRunning(id string) (system.Service, bool, error) Load(services ...system.Service) []string Start(serviceIDs ...string) error } // ImageCacheConfigController manages configures Image Cache. type ImageCacheConfigController struct { V1Alpha1ServiceManager ServiceManager DisableCacheCopy bool // used for testing cacheCopyDone bool } // Name implements controller.StatsController interface. func (ctrl *ImageCacheConfigController) Name() string { return "cri.ImageCacheConfigController" } // Inputs implements controller.StatsController interface. func (ctrl *ImageCacheConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some(RegistrydServiceID), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.StatsController interface. func (ctrl *ImageCacheConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: cri.ImageCacheConfigType, Kind: controller.OutputExclusive, }, { Type: block.VolumeConfigType, Kind: controller.OutputShared, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Volume configuration constants. const ( VolumeImageCacheISO = "IMAGECACHE-ISO" VolumeImageCacheDISK = constants.ImageCachePartitionLabel MinImageCacheSize = 500 * 1024 * 1024 // 500MB MaxImageCacheSize = 1 * 1024 * 1024 * 1024 // 1GB RegistrydServiceID = services.RegistryID ) // Run implements controller.StatsController interface. // //nolint:gocyclo,cyclop func (ctrl *ImageCacheConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } registryDService, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, RegistrydServiceID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting service: %w", err) } // image cache is disabled imageCacheDisabled := cfg == nil || cfg.Config().Machine() == nil || !cfg.Config().Machine().Features().ImageCache().LocalEnabled() var ( status cri.ImageCacheStatus copyStatus cri.ImageCacheCopyStatus roots []string allReady bool ) if imageCacheDisabled { status = cri.ImageCacheStatusDisabled copyStatus = cri.ImageCacheCopyStatusSkipped } else { status = cri.ImageCacheStatusPreparing // image cache is enabled, so create the volume config resources to find the image cache roots if err = ctrl.createVolumeConfigISO(ctx, r); err != nil { return fmt.Errorf("error creating volume config: %w", err) } if err = ctrl.createVolumeConfigDisk(ctx, r, cfg.Config()); err != nil { return fmt.Errorf("error creating volume config: %w", err) } cacheVolumeStatus, err := ctrl.analyzeImageCacheVolumes(ctx, logger, r) if err != nil { return fmt.Errorf("error analyzing image cache volumes: %w", err) } allReady = cacheVolumeStatus.allReady roots = cacheVolumeStatus.roots copyStatus = cacheVolumeStatus.copyStatus if allReady && len(roots) == 0 { // all volumes identified, but no roots found status = cri.ImageCacheStatusDisabled } } if status == cri.ImageCacheStatusPreparing && len(roots) > 0 { _, running, err := ctrl.V1Alpha1ServiceManager.IsRunning(RegistrydServiceID) if err != nil { ctrl.V1Alpha1ServiceManager.Load(services.NewRegistryD()) } if !running { if err = ctrl.V1Alpha1ServiceManager.Start(RegistrydServiceID); err != nil { return fmt.Errorf("error starting service: %w", err) } } if registryDService != nil && registryDService.TypedSpec().Running && registryDService.TypedSpec().Healthy { status = cri.ImageCacheStatusReady } } logger.Debug("image cache status", zap.String("status", status.String()), zap.String("copy_status", copyStatus.String())) if err = safe.WriterModify(ctx, r, cri.NewImageCacheConfig(), func(cfg *cri.ImageCacheConfig) error { cfg.TypedSpec().Status = status cfg.TypedSpec().CopyStatus = copyStatus cfg.TypedSpec().Roots = roots return nil }); err != nil { return fmt.Errorf("error writing ImageCacheConfig: %w", err) } } } func (ctrl *ImageCacheConfigController) createVolumeConfigISO(ctx context.Context, r controller.ReaderWriter) error { builder := cel.NewBuilder(celenv.VolumeLocator()) // volume.name in ["iso9660", "vfat"] && volume.label.startsWith("TALOS_") expr := builder.NewCall( builder.NextID(), operators.LogicalAnd, builder.NewCall( builder.NextID(), operators.In, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "name", ), builder.NewList( builder.NextID(), []ast.Expr{ builder.NewLiteral(builder.NextID(), types.String("iso9660")), builder.NewLiteral(builder.NextID(), types.String("vfat")), }, nil, ), ), builder.NewMemberCall( builder.NextID(), "startsWith", builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "label", ), builder.NewLiteral(builder.NextID(), types.String("TALOS_")), ), ) boolExpr, err := builder.ToBooleanExpression(expr) if err != nil { return fmt.Errorf("error creating boolean expression: %w", err) } return safe.WriterModify(ctx, r, block.NewVolumeConfig(block.NamespaceName, VolumeImageCacheISO), func(volumeCfg *block.VolumeConfig) error { volumeCfg.TypedSpec().Type = block.VolumeTypeDisk volumeCfg.TypedSpec().Locator = block.LocatorSpec{ Match: *boolExpr, } volumeCfg.TypedSpec().Mount = block.MountSpec{ TargetPath: constants.ImageCacheISOMountPoint, FileMode: 0o700, UID: 0, GID: 0, } return nil }) } func (ctrl *ImageCacheConfigController) createVolumeConfigDisk(ctx context.Context, r controller.ReaderWriter, cfg cfg.Config) error { builder := cel.NewBuilder(celenv.VolumeLocator()) // volume.partition_label == "IMAGECACHE" expr := builder.NewCall( builder.NextID(), operators.Equals, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "partition_label", ), builder.NewLiteral(builder.NextID(), types.String(constants.ImageCachePartitionLabel)), ) locatorExpr, err := builder.ToBooleanExpression(expr) if err != nil { return fmt.Errorf("error creating boolean expression: %w", err) } // system_disk builder = cel.NewBuilder(celenv.DiskLocator()) expr = builder.NewIdent(builder.NextID(), "system_disk") diskExpr, err := builder.ToBooleanExpression(expr) if err != nil { return fmt.Errorf("error creating boolean expression: %w", err) } return safe.WriterModify(ctx, r, block.NewVolumeConfig(block.NamespaceName, VolumeImageCacheDISK), func(volumeCfg *block.VolumeConfig) error { volumeCfg.TypedSpec().Type = block.VolumeTypePartition volumeCfg.TypedSpec().Locator = block.LocatorSpec{ Match: *locatorExpr, } if extraCfg, ok := cfg.Volumes().ByName(constants.ImageCachePartitionLabel); ok { volumeCfg.TypedSpec().Provisioning.Wave = block.WaveSystemDisk volumeCfg.TypedSpec().Provisioning.DiskSelector.Match = extraCfg.Provisioning().DiskSelector().ValueOr(*diskExpr) volumeCfg.TypedSpec().Provisioning.PartitionSpec.Grow = extraCfg.Provisioning().Grow().ValueOr(false) volumeCfg.TypedSpec().Provisioning.PartitionSpec.MinSize = extraCfg.Provisioning().MinSize().ValueOr(MinImageCacheSize) volumeCfg.TypedSpec().Provisioning.PartitionSpec.MaxSize = extraCfg.Provisioning().MaxSize().ValueOr(MaxImageCacheSize) volumeCfg.TypedSpec().Provisioning.PartitionSpec.Label = constants.ImageCachePartitionLabel volumeCfg.TypedSpec().Provisioning.PartitionSpec.TypeUUID = partition.LinuxFilesystemData volumeCfg.TypedSpec().Provisioning.FilesystemSpec.Type = block.FilesystemTypeEXT4 if err := blockadapter.VolumeConfigSpec(volumeCfg.TypedSpec()).ApplyEncryptionConfig(extraCfg.Encryption()); err != nil { return fmt.Errorf("error applying encryption config: %w", err) } } volumeCfg.TypedSpec().Mount = block.MountSpec{ TargetPath: constants.ImageCacheDiskMountPoint, FileMode: 0o700, UID: 0, GID: 0, } return nil }) } type imageCacheVolumeStatus struct { roots []string allReady bool copyStatus cri.ImageCacheCopyStatus } //nolint:gocyclo,cyclop func (ctrl *ImageCacheConfigController) analyzeImageCacheVolumes(ctx context.Context, logger *zap.Logger, r controller.ReaderWriter) (*imageCacheVolumeStatus, error) { volumeIDs := []string{VolumeImageCacheDISK, VolumeImageCacheISO} // prefer disk cache over ISO cache volumeStatuses := make([]*block.VolumeStatus, 0, len(volumeIDs)) for _, volumeID := range volumeIDs { volumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, r, volumeID) if err != nil { if state.IsNotFoundError(err) { // wait for volume statuses to be present continue } return nil, fmt.Errorf("error getting volume status: %w", err) } volumeStatuses = append(volumeStatuses, volumeStatus) } // we need to ensure that we first wait for the ISO to be either missing or ready, // so that we can make a decision on copying the image cache from an ISO to the disk volume var isoStatus, diskStatus block.VolumePhase for _, volumeStatus := range volumeStatuses { switch volumeStatus.Metadata().ID() { case VolumeImageCacheISO: isoStatus = volumeStatus.TypedSpec().Phase case VolumeImageCacheDISK: diskStatus = volumeStatus.TypedSpec().Phase } } isoPresent := isoStatus == block.VolumePhaseReady diskMissing := diskStatus == block.VolumePhaseMissing for _, volumeStatus := range volumeStatuses { volumeID := volumeStatus.Metadata().ID() // create a mount request for the volume, it doesn't matter if the volume is ready or not, // but we want them to be mounted whenever they are ready mountID := ctrl.Name() + "-" + volumeID if err := safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, mountID), func(mountRequest *block.VolumeMountRequest) error { mountRequest.TypedSpec().Requester = ctrl.Name() mountRequest.TypedSpec().VolumeID = volumeID mountRequest.TypedSpec().ReadOnly = !(volumeStatus.Metadata().ID() == VolumeImageCacheDISK && isoPresent) return nil }, ); err != nil { return nil, fmt.Errorf("error creating volume mount request: %w", err) } } roots := make([]string, 0, len(volumeIDs)) var ( isoReady, diskReady bool copySource, copyTarget string ) allReady := true // analyze volume statuses, and build the roots for _, volumeStatus := range volumeStatuses { // mount as rw only disk cache if the ISO cache is present root, ready, err := ctrl.getImageCacheRoot(ctx, r, volumeStatus) if err != nil { return nil, fmt.Errorf("error getting image cache root: %w", err) } if ready { switch volumeStatus.Metadata().ID() { case VolumeImageCacheISO: isoReady = true copySource = root.ValueOr("") logger = logger.With(zap.String("iso_size", volumeStatus.TypedSpec().PrettySize)) case VolumeImageCacheDISK: diskReady = true copyTarget = root.ValueOr("") logger = logger.With(zap.String("disk_size", volumeStatus.TypedSpec().PrettySize)) } } allReady = allReady && ready if rootPath, ok := root.Get(); ok { roots = append(roots, rootPath) } } logger = logger.With(zap.Bool("all_ready", allReady)) var copyStatus cri.ImageCacheCopyStatus switch { case !isoPresent: // if there's no ISO, we don't need to copy anything copyStatus = cri.ImageCacheCopyStatusSkipped case diskMissing: // if the disk volume is not configured, we can't copy the image cache copyStatus = cri.ImageCacheCopyStatusSkipped case ctrl.cacheCopyDone: // if the copy has already been done, we don't need to do it again copyStatus = cri.ImageCacheCopyStatusReady case isoReady && diskReady && copySource != "" && copyTarget != "": // ready to copy if err := ctrl.copyImageCache(ctx, logger, copySource, copyTarget); err != nil { return nil, fmt.Errorf("error copying image cache: %w", err) } copyStatus = cri.ImageCacheCopyStatusReady default: // waiting for copy preconditions copyStatus = cri.ImageCacheCopyStatusPending } return &imageCacheVolumeStatus{ roots: roots, allReady: allReady, copyStatus: copyStatus, }, nil } func (ctrl *ImageCacheConfigController) getImageCacheRoot( ctx context.Context, r controller.ReaderWriter, volumeStatus *block.VolumeStatus, ) (optional.Optional[string], bool, error) { switch volumeStatus.TypedSpec().Phase { //nolint:exhaustive case block.VolumePhaseMissing, block.VolumePhaseFailed, block.VolumePhaseWaiting: // image cache is missing return optional.None[string](), true, nil case block.VolumePhaseReady: // fall through to below default: // undetermined status return optional.None[string](), false, nil } volumeID := volumeStatus.Metadata().ID() mountID := ctrl.Name() + "-" + volumeID mountStatus, err := safe.ReaderGetByID[*block.VolumeMountStatus](ctx, r, mountID) if err != nil { if state.IsNotFoundError(err) { return optional.None[string](), false, nil } return optional.None[string](), false, fmt.Errorf("error fetching volume mount status: %w", err) } if mountStatus.Metadata().Phase() == resource.PhaseTearingDown { // the mount status is being torn down, so we should stop using it if err = r.RemoveFinalizer(ctx, mountStatus.Metadata(), ctrl.Name()); err != nil { return optional.None[string](), false, fmt.Errorf("error removing finalizer: %w", err) } return optional.None[string](), true, nil } // put a finalizer on the mount status, meaning that we are using it if !mountStatus.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, mountStatus.Metadata(), ctrl.Name()); err != nil { return optional.None[string](), false, fmt.Errorf("error adding finalizer: %w", err) } } targetPath := mountStatus.TypedSpec().Target if volumeID == VolumeImageCacheISO { // the ISO volume has a subdirectory with the actual image cache targetPath = filepath.Join(targetPath, "imagecache") } return optional.Some(targetPath), true, nil } func (ctrl *ImageCacheConfigController) copyImageCache(ctx context.Context, logger *zap.Logger, source, target string) error { logger.Info("copying image cache", zap.String("source", source), zap.String("target", target)) if ctrl.DisableCacheCopy { // used for testing return nil } var bytesCopied int64 if err := filepath.WalkDir(source, func(path string, d fs.DirEntry, err error) error { if err != nil { return fmt.Errorf("error walking source directory: %w", err) } else if err = ctx.Err(); err != nil { return err } relPath, err := filepath.Rel(source, path) if err != nil { return fmt.Errorf("error getting relative path: %w", err) } targetPath := filepath.Join(target, relPath) info, err := d.Info() if err != nil { return fmt.Errorf("error getting file info: %w", err) } // we only support directories and files switch { case info.Mode().IsDir(): if err := os.MkdirAll(targetPath, 0o755); err != nil { return fmt.Errorf("error creating directory: %w", err) } return nil case info.Mode().IsRegular(): bytesCopied += info.Size() return copyFileSafe(path, targetPath) default: return fmt.Errorf("unsupported file type %s: %s", info.Mode(), path) } }); err != nil { return fmt.Errorf("error copying image cache: %w", err) } logger.Info("image cache copied", zap.String("size", humanize.IBytes(uint64(bytesCopied)))) ctrl.cacheCopyDone = true return nil } func copyFileSafe(src, dst string) error { srcStat, err := os.Stat(src) if err != nil { return fmt.Errorf("error getting source file info: %w", err) } dstStat, err := os.Stat(dst) if err == nil && srcStat.Size() == dstStat.Size() { // skipping copy return nil } srcFile, err := os.Open(src) if err != nil { return fmt.Errorf("error opening source file: %w", err) } defer srcFile.Close() //nolint:errcheck tempPath := dst + ".tmp" dstFile, err := os.Create(tempPath) if err != nil { return fmt.Errorf("error creating destination file: %w", err) } defer dstFile.Close() //nolint:errcheck if _, err = io.Copy(dstFile, srcFile); err != nil { return fmt.Errorf("error copying file: %w, source size is %d", err, srcStat.Size()) } if err = dstFile.Close(); err != nil { return fmt.Errorf("error closing destination file: %w", err) } if err = os.Rename(tempPath, dst); err != nil { return fmt.Errorf("error renaming file: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cri/image_cache_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" crictrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/pkg/machinery/config/container" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" v1alpha1res "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) func (suite *ImageCacheConfigSuite) TestReconcileNoConfig() { ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) }) } func (suite *ImageCacheConfigSuite) TestReconcileFeatureNotEnabled() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{}, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) }) } func (suite *ImageCacheConfigSuite) TestReconcileFeatureEnabled() { ctrlName := (&crictrl.ImageCacheConfigController{}).Name() cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ ImageCacheSupport: &v1alpha1.ImageCacheConfig{ CacheLocalEnabled: new(true), }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crictrl.VolumeImageCacheISO, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(`volume.name in ["iso9660", "vfat"] && volume.label.startsWith("TALOS_")`, r.TypedSpec().Locator.Match.String()) }) ctest.AssertResource(suite, crictrl.VolumeImageCacheDISK, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(`volume.partition_label == "IMAGECACHE"`, r.TypedSpec().Locator.Match.String()) }) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) }) // create volume statuses to simulate the volume being ready vs1 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheISO) vs1.TypedSpec().Phase = block.VolumePhaseReady suite.Require().NoError(suite.State().Create(suite.Ctx(), vs1)) vs2 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheDISK) vs2.TypedSpec().Phase = block.VolumePhaseWaiting suite.Require().NoError(suite.State().Create(suite.Ctx(), vs2)) // controller should create mount requests ctest.AssertResources(suite, []string{ ctrlName + "-" + crictrl.VolumeImageCacheISO, ctrlName + "-" + crictrl.VolumeImageCacheDISK, }, func(vmr *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(vmr.TypedSpec().VolumeID == crictrl.VolumeImageCacheISO, vmr.TypedSpec().ReadOnly) }, ) // simulate ISO being mounted vms1 := block.NewVolumeMountStatus(block.NamespaceName, ctrlName+"-"+crictrl.VolumeImageCacheISO) vms1.TypedSpec().ReadOnly = true vms1.TypedSpec().Target = constants.ImageCacheISOMountPoint suite.Require().NoError(suite.State().Create(suite.Ctx(), vms1)) // one volume is ready, but second one is not (yet) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusPreparing, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusPending, r.TypedSpec().CopyStatus) asrt.Equal([]string{filepath.Join(constants.ImageCacheISOMountPoint, "imagecache")}, r.TypedSpec().Roots) }) // mark second as ready vs2.TypedSpec().Phase = block.VolumePhaseReady suite.Require().NoError(suite.State().Update(suite.Ctx(), vs2)) // simulate disk being mounted vms2 := block.NewVolumeMountStatus(block.NamespaceName, ctrlName+"-"+crictrl.VolumeImageCacheDISK) vms2.TypedSpec().ReadOnly = false vms2.TypedSpec().Target = constants.ImageCacheDiskMountPoint suite.Require().NoError(suite.State().Create(suite.Ctx(), vms2)) // now both volumes are ready, but service hasn't started yet ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusPreparing, r.TypedSpec().Status) asrt.Equal([]string{constants.ImageCacheDiskMountPoint, filepath.Join(constants.ImageCacheISOMountPoint, "imagecache")}, r.TypedSpec().Roots) }) // simulate registryd being ready service := v1alpha1res.NewService(crictrl.RegistrydServiceID) service.TypedSpec().Healthy = true service.TypedSpec().Running = true suite.Require().NoError(suite.State().Create(suite.Ctx(), service)) // now both volumes are ready, and service is ready, should be ready ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusReady, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusReady, r.TypedSpec().CopyStatus) asrt.Equal([]string{constants.ImageCacheDiskMountPoint, filepath.Join(constants.ImageCacheISOMountPoint, "imagecache")}, r.TypedSpec().Roots) }) } func (suite *ImageCacheConfigSuite) TestReconcileJustDiskVolume() { ctrlName := (&crictrl.ImageCacheConfigController{}).Name() cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ ImageCacheSupport: &v1alpha1.ImageCacheConfig{ CacheLocalEnabled: new(true), }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) }) // create volume statuses to simulate the volume being ready/not vs1 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheISO) vs1.TypedSpec().Phase = block.VolumePhaseMissing suite.Require().NoError(suite.State().Create(suite.Ctx(), vs1)) vs2 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheDISK) vs2.TypedSpec().Phase = block.VolumePhaseWaiting suite.Require().NoError(suite.State().Create(suite.Ctx(), vs2)) // ISO is missing, but disk volume is not ready yet ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) asrt.Empty(r.TypedSpec().Roots) }) // make disk image cache ready vs2.TypedSpec().Phase = block.VolumePhaseReady suite.Update(vs2) // simulate disk being mounted vms2 := block.NewVolumeMountStatus(block.NamespaceName, ctrlName+"-"+crictrl.VolumeImageCacheDISK) vms2.TypedSpec().ReadOnly = false vms2.TypedSpec().Target = constants.ImageCacheDiskMountPoint suite.Require().NoError(suite.State().Create(suite.Ctx(), vms2)) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusPreparing, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) asrt.Equal([]string{constants.ImageCacheDiskMountPoint}, r.TypedSpec().Roots) }) // simulate registryd being ready service := v1alpha1res.NewService(crictrl.RegistrydServiceID) service.TypedSpec().Healthy = true service.TypedSpec().Running = true suite.Require().NoError(suite.State().Create(suite.Ctx(), service)) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusReady, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) asrt.Equal([]string{constants.ImageCacheDiskMountPoint}, r.TypedSpec().Roots) }) // volume mount status should have a finalizer ctest.AssertResource(suite, ctrlName+"-"+crictrl.VolumeImageCacheDISK, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Has(ctrlName)) }, ) // now, simulate reboot sequence: // * missing ISO volume is destroyed // * volume mount status is being torn down suite.Destroy(vs1) _, err := suite.State().Teardown(suite.Ctx(), block.NewVolumeMountStatus(block.NamespaceName, ctrlName+"-"+crictrl.VolumeImageCacheDISK).Metadata()) suite.Require().NoError(err) // controller should remove its finalizer ctest.AssertResource(suite, ctrlName+"-"+crictrl.VolumeImageCacheDISK, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }, ) } func (suite *ImageCacheConfigSuite) TestReconcileWithImageCacheVolume() { ctrlName := (&crictrl.ImageCacheConfigController{}).Name() v1alpha1Cfg := &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ ImageCacheSupport: &v1alpha1.ImageCacheConfig{ CacheLocalEnabled: new(true), }, }, }, } volumeConfig := blockcfg.NewVolumeConfigV1Alpha1() volumeConfig.MetaName = constants.ImageCachePartitionLabel volumeConfig.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("10GiB") container, err := container.New(v1alpha1Cfg, volumeConfig) suite.Require().NoError(err) cfg := config.NewMachineConfig(container) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crictrl.VolumeImageCacheDISK, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(`volume.partition_label == "IMAGECACHE"`, r.TypedSpec().Locator.Match.String()) asrt.Equal(`system_disk`, r.TypedSpec().Provisioning.DiskSelector.Match.String()) asrt.False(r.TypedSpec().Provisioning.PartitionSpec.Grow) asrt.EqualValues(crictrl.MinImageCacheSize, r.TypedSpec().Provisioning.PartitionSpec.MinSize) asrt.EqualValues(10*1024*1024*1024, r.TypedSpec().Provisioning.PartitionSpec.MaxSize) }) ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusDisabled, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) }) // create volume statuses to simulate the volume being ready & missing vs1 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheISO) vs1.TypedSpec().Phase = block.VolumePhaseMissing suite.Require().NoError(suite.State().Create(suite.Ctx(), vs1)) vs2 := block.NewVolumeStatus(block.NamespaceName, crictrl.VolumeImageCacheDISK) vs2.TypedSpec().Phase = block.VolumePhaseReady suite.Require().NoError(suite.State().Create(suite.Ctx(), vs2)) // simulate disk being mounted vms := block.NewVolumeMountStatus(block.NamespaceName, ctrlName+"-"+crictrl.VolumeImageCacheDISK) vms.TypedSpec().ReadOnly = false vms.TypedSpec().Target = constants.ImageCacheDiskMountPoint suite.Require().NoError(suite.State().Create(suite.Ctx(), vms)) // simulate registryd being ready service := v1alpha1res.NewService(crictrl.RegistrydServiceID) service.TypedSpec().Healthy = true service.TypedSpec().Running = true suite.Require().NoError(suite.State().Create(suite.Ctx(), service)) // now both volumes are ready, and service is ready, should be ready ctest.AssertResource(suite, cri.ImageCacheConfigID, func(r *cri.ImageCacheConfig, asrt *assert.Assertions) { asrt.Equal(cri.ImageCacheStatusReady, r.TypedSpec().Status) asrt.Equal(cri.ImageCacheCopyStatusSkipped, r.TypedSpec().CopyStatus) asrt.Equal([]string{constants.ImageCacheDiskMountPoint}, r.TypedSpec().Roots) }) } func (suite *ImageCacheConfigSuite) TestReconcileWithEncryptionConfig() { v1alpha1Cfg := &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ ImageCacheSupport: &v1alpha1.ImageCacheConfig{ CacheLocalEnabled: new(true), }, }, }, } volumeConfig := blockcfg.NewVolumeConfigV1Alpha1() volumeConfig.MetaName = constants.ImageCachePartitionLabel volumeConfig.EncryptionSpec = blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "allsecret", }, }, }, } container, err := container.New(v1alpha1Cfg, volumeConfig) suite.Require().NoError(err) cfg := config.NewMachineConfig(container) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crictrl.VolumeImageCacheDISK, func(r *block.VolumeConfig, asrt *assert.Assertions) { asrt.Equal(`volume.partition_label == "IMAGECACHE"`, r.TypedSpec().Locator.Match.String()) asrt.Equal(`system_disk`, r.TypedSpec().Provisioning.DiskSelector.Match.String()) asrt.False(r.TypedSpec().Provisioning.PartitionSpec.Grow) asrt.EqualValues(crictrl.MinImageCacheSize, r.TypedSpec().Provisioning.PartitionSpec.MinSize) asrt.EqualValues(crictrl.MaxImageCacheSize, r.TypedSpec().Provisioning.PartitionSpec.MaxSize) asrt.Equal(block.EncryptionProviderLUKS2, r.TypedSpec().Encryption.Provider) asrt.Len(r.TypedSpec().Encryption.Keys, 1) }) } func TestImageCacheConfigSuite(t *testing.T) { s := &ImageCacheConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, } s.AfterSetup = func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&crictrl.ImageCacheConfigController{ V1Alpha1ServiceManager: &mockServiceRunner{}, DisableCacheCopy: true, })) } suite.Run(t, s) } type ImageCacheConfigSuite struct { ctest.DefaultSuite } type mockServiceRunner struct{} func (mock *mockServiceRunner) IsRunning(id string) (system.Service, bool, error) { return nil, true, nil } func (mock *mockServiceRunner) Load(services ...system.Service) []string { return nil } func (mock *mockServiceRunner) Start(serviceIDs ...string) error { return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cri/image_gc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "errors" "fmt" "time" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/distribution/reference" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // ImageCleanupInterval is the interval at which the image GC controller runs. const ImageCleanupInterval = 15 * time.Minute // ImageGCGracePeriod is the minimum age of an image before it can be deleted. const ImageGCGracePeriod = 4 * ImageCleanupInterval // NewImageGCController creates a new ImageGCController. func NewImageGCController(containerdName string, buildExpectedImages bool) *ImageGCController { controllerName := "cri." + containerdName + "ImageGCController" return &ImageGCController{ containerdName: containerdName, controllerName: controllerName, buildExpectedImages: buildExpectedImages, } } // ImageGCController performs garbage collection of unused container images. type ImageGCController struct { ImageServiceProvider func() (ImageServiceProvider, error) containerdName string controllerName string buildExpectedImages bool imageFirstSeenUnreferenced map[string]time.Time } // ImageServiceProvider wraps the containerd image service. type ImageServiceProvider interface { ImageService() images.Store Close() error } // Name implements controller.Controller interface. func (ctrl *ImageGCController) Name() string { return ctrl.controllerName } // Inputs implements controller.Controller interface. func (ctrl *ImageGCController) Inputs() []controller.Input { inputs := []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some(ctrl.containerdName), Kind: controller.InputWeak, }, } if ctrl.buildExpectedImages { inputs = append(inputs, controller.Input{ Namespace: k8s.NamespaceName, Type: k8s.KubeletSpecType, ID: optional.Some(k8s.KubeletID), Kind: controller.InputWeak, }, controller.Input{ Namespace: etcd.NamespaceName, Type: etcd.SpecType, ID: optional.Some(etcd.SpecID), Kind: controller.InputWeak, }, ) } return inputs } // Outputs implements controller.Controller interface. func (ctrl *ImageGCController) Outputs() []controller.Output { return nil } func defaultImageServiceProvider(containerdName string) func() (ImageServiceProvider, error) { return func() (ImageServiceProvider, error) { var addr string switch containerdName { case "cri": addr = constants.CRIContainerdAddress case "containerd": addr = constants.SystemContainerdAddress default: return nil, fmt.Errorf("unknown containerd name: %s", containerdName) } criClient, err := containerd.New(addr) if err != nil { return nil, fmt.Errorf("error creating containerd client: %w", err) } return &containerdImageServiceProvider{ criClient: criClient, }, nil } } type containerdImageServiceProvider struct { criClient *containerd.Client } func (s *containerdImageServiceProvider) ImageService() images.Store { return s.criClient.ImageService() } func (s *containerdImageServiceProvider) Close() error { return s.criClient.Close() } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *ImageGCController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.ImageServiceProvider == nil { ctrl.ImageServiceProvider = defaultImageServiceProvider(ctrl.containerdName) } if ctrl.imageFirstSeenUnreferenced == nil { ctrl.imageFirstSeenUnreferenced = map[string]time.Time{} } var ( containerdIsUp bool expectedImages []string imageServiceProvider ImageServiceProvider ) ticker := time.NewTicker(ImageCleanupInterval) defer ticker.Stop() defer func() { if imageServiceProvider != nil { imageServiceProvider.Close() //nolint:errcheck } }() for { select { case <-ctx.Done(): return nil case <-ticker.C: if !containerdIsUp || (ctrl.buildExpectedImages && len(expectedImages) == 0) { continue } if imageServiceProvider == nil { var err error imageServiceProvider, err = ctrl.ImageServiceProvider() if err != nil { return fmt.Errorf("error creating image service provider: %w", err) } } if err := ctrl.cleanup(ctx, logger, imageServiceProvider.ImageService(), expectedImages); err != nil { return fmt.Errorf("error running image cleanup: %w", err) } case <-r.EventCh(): containerdService, err := safe.ReaderGet[*v1alpha1.Service](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, ctrl.containerdName, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting container service: %w", err) } containerdIsUp = containerdService != nil && containerdService.TypedSpec().Running && containerdService.TypedSpec().Healthy expectedImages = nil if ctrl.buildExpectedImages { etcdSpec, err := safe.ReaderGet[*etcd.Spec](ctx, r, resource.NewMetadata(etcd.NamespaceName, etcd.SpecType, etcd.SpecID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting etcd spec: %w", err) } if etcdSpec != nil { expectedImages = append(expectedImages, etcdSpec.TypedSpec().Image) } kubeletSpec, err := safe.ReaderGet[*k8s.KubeletSpec](ctx, r, resource.NewMetadata(k8s.NamespaceName, k8s.KubeletSpecType, k8s.KubeletID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubelet spec: %w", err) } if kubeletSpec != nil { expectedImages = append(expectedImages, kubeletSpec.TypedSpec().Image) } } } r.ResetRestartBackoff() } } //nolint:gocyclo func buildExpectedDigests(logger *zap.Logger, actualImages []images.Image, expectedImages []string) (map[string]struct{}, error) { var parseErrors error expectedReferences := xslices.Map(expectedImages, func(ref string) reference.Named { res, parseErr := reference.ParseNamed(ref) parseErrors = errors.Join(parseErrors, parseErr) return res }) if parseErrors != nil { return nil, fmt.Errorf("error parsing expected images: %w", parseErrors) } expectedDigests := map[string]struct{}{} for _, expectedRef := range expectedReferences { // easy case: image ref has digest, record it if expectedDigested, ok := expectedRef.(reference.Digested); ok { expectedDigests[expectedDigested.Digest().String()] = struct{}{} continue } // hard case: iterate over actual images to find the digest for the tag for _, image := range actualImages { imageRef, err := reference.ParseAnyReference(image.Name) if err != nil { logger.Debug("failed to parse image reference", zap.Error(err), zap.String("image", image.Name)) continue } digest := image.Target.Digest.String() if ref, ok := imageRef.(reference.NamedTagged); ok { if expectedRef.Name() != ref.Name() { continue } if expectedTagged, ok := expectedRef.(reference.Tagged); ok && ref.Tag() == expectedTagged.Tag() { // this is expected image by tag, inject digest expectedDigests[digest] = struct{}{} break } } } } return expectedDigests, nil } func (ctrl *ImageGCController) cleanup(ctx context.Context, logger *zap.Logger, imageService images.Store, expectedImages []string) error { logger.Debug("running image cleanup") ctx = namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace) actualImages, err := imageService.List(ctx) if err != nil { return fmt.Errorf("error listing images: %w", err) } // first pass: scan actualImages and expand expectedImages from tags to digests expectedDigests, err := buildExpectedDigests(logger, actualImages, expectedImages) if err != nil { return err } // second pass, drop whatever is not expected for _, image := range actualImages { _, shouldKeep := expectedDigests[image.Target.Digest.String()] if shouldKeep { logger.Debug("image is referenced, skipping garbage collection", zap.String("image", image.Name)) delete(ctrl.imageFirstSeenUnreferenced, image.Name) continue } if _, ok := ctrl.imageFirstSeenUnreferenced[image.Name]; !ok { ctrl.imageFirstSeenUnreferenced[image.Name] = time.Now() } // calculate image age two ways, and pick the minimum: // * as CRI reports it, which is the time image got pulled // * as we see it, this means the image won't be deleted until it reaches the age of ImageGCGracePeriod from the moment it became unreferenced imageAgeCRI := time.Since(image.CreatedAt) imageAgeInternal := time.Since(ctrl.imageFirstSeenUnreferenced[image.Name]) imageAge := min(imageAgeCRI, imageAgeInternal) if imageAge < ImageGCGracePeriod { logger.Debug("skipping image cleanup, as it's below minimum age", zap.String("image", image.Name), zap.Duration("age", imageAge)) continue } if err = imageService.Delete(ctx, image.Name); err != nil { return fmt.Errorf("failed to delete an image %s: %w", image.Name, err) } delete(ctrl.imageFirstSeenUnreferenced, image.Name) logger.Info("deleted an image", zap.String("image", image.Name)) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cri/image_gc_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "context" "slices" "sync" "testing" "testing/synctest" "time" "github.com/containerd/containerd/v2/core/images" "github.com/opencontainers/go-digest" v1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" crictrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) func TestImageGC(t *testing.T) { synctest.Test(t, func(t *testing.T) { mockImageService := &mockImageService{} // Create the controller inside synctest time function so it uses the controlled time controller := crictrl.NewImageGCController("cri", true) controller.ImageServiceProvider = func() (crictrl.ImageServiceProvider, error) { return mockImageService, nil } // Set up the test environment manually suite := &ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { // Register the controller suite.Require().NoError(suite.Runtime().RegisterController(controller)) }, // We need a long timeout here because we advance time manually in the test and we want the controller // to have enough time to run its cleanup cycles. Timeout: 2 * time.Hour, } suite.SetT(t) // we need to explicitly set to the t from the synctest.Test suite.SetupTest() defer suite.TearDownTest() // Use synctest controlled time as the base time now := time.Now() storedImages := []images.Image{ { Name: "registry.io/org/image1:v1.3.5@sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a", CreatedAt: now.Add(-2 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a")), }, }, // ok to be gc'd { Name: "sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a", // the image age is more than the grace period, but the controller won't remove due to the check on the last seen unreferenced timestamp CreatedAt: now.Add(-4 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a")), }, }, // ok to be gc'd, same as above, another ref { Name: "registry.io/org/image1:v1.3.7", CreatedAt: now.Add(-2 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, // current image { Name: "registry.io/org/image1@sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", CreatedAt: now.Add(-2 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, // current image, canonical ref { Name: "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", CreatedAt: now.Add(-2 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, // current image, digest ref { Name: "registry.io/org/image1:v1.3.8", CreatedAt: now.Add(crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:fd03335dd2e7163e5e36e933a0c735d7fec6f42b33ddafad0bc54f333e4a23c0")), }, }, // not ok to clean up, too new { Name: "registry.io/org/image2@sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8", CreatedAt: now.Add(-2 * crictrl.ImageGCGracePeriod), Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8")), }, }, // current image } mockImageService.images = storedImages criService := v1alpha1.NewService("cri") criService.TypedSpec().Healthy = true criService.TypedSpec().Running = true require.NoError(t, suite.State().Create(suite.Ctx(), criService)) kubelet := k8s.NewKubeletSpec(k8s.NamespaceName, k8s.KubeletID) kubelet.TypedSpec().Image = "registry.io/org/image1:v1.3.7" require.NoError(t, suite.State().Create(suite.Ctx(), kubelet)) etcd := etcd.NewSpec(etcd.NamespaceName, etcd.SpecID) etcd.TypedSpec().Image = "registry.io/org/image2:v3.5.9@sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8" require.NoError(t, suite.State().Create(suite.Ctx(), etcd)) // // Wait for the controller to process all events and set up state // synctest.Wait() // Advance time past the grace period to make old images eligible for cleanup // Grace period is 60 minutes, so advance by 65 minutes to ensure cleanup time.Sleep(crictrl.ImageGCGracePeriod + 5*time.Minute) synctest.Wait() // Advance time to trigger the cleanup cycle (15 minutes) time.Sleep(crictrl.ImageCleanupInterval) synctest.Wait() // Wait for cleanup to complete // Images that should remain after cleanup: // - All referenced images (from kubelet and etcd specs) // - The "new" image that hasn't aged enough yet expectedImages := []string{ "registry.io/org/image1:v1.3.7", // kubelet image "registry.io/org/image1@sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", // kubelet image canonical ref "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", // kubelet image digest ref "registry.io/org/image1:v1.3.8", // new image, not old enough to clean "registry.io/org/image2@sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8", // etcd image } imageList, err := mockImageService.List(suite.Ctx()) require.NoError(t, err) actualImages := xslices.Map(imageList, func(i images.Image) string { return i.Name }) suite.Assert().Equal(expectedImages, actualImages, "images after first GC run do not match expected") }) } type mockImageService struct { mu sync.Mutex images []images.Image } func (m *mockImageService) ImageService() images.Store { return m } func (m *mockImageService) Close() error { return nil } func (m *mockImageService) Get(ctx context.Context, name string) (images.Image, error) { panic("not implemented") } func (m *mockImageService) List(ctx context.Context, filters ...string) ([]images.Image, error) { m.mu.Lock() defer m.mu.Unlock() return slices.Clone(m.images), nil } func (m *mockImageService) Create(ctx context.Context, image images.Image) (images.Image, error) { panic("not implemented") } func (m *mockImageService) Update(ctx context.Context, image images.Image, fieldpaths ...string) (images.Image, error) { panic("not implemented") } func (m *mockImageService) Delete(ctx context.Context, name string, opts ...images.DeleteOpt) error { m.mu.Lock() defer m.mu.Unlock() m.images = xslices.FilterInPlace(m.images, func(i images.Image) bool { return i.Name != name }) return nil } func TestBuildExpectedImageDigests(t *testing.T) { actualImages := []images.Image{ { Name: "registry.io/org/image1:v1.3.5@sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a")), }, }, { Name: "sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:6b094bd0b063a1172eec7da249eccbb48cc48333800569363d67c747960cfa0a")), }, }, { Name: "registry.io/org/image1:v1.3.7", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, { Name: "registry.io/org/image1@sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, { Name: "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135")), }, }, { Name: "registry.io/org/image1:v1.3.8", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:fd03335dd2e7163e5e36e933a0c735d7fec6f42b33ddafad0bc54f333e4a23c0")), }, }, { Name: "registry.io/org/image2@sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8", Target: v1.Descriptor{ Digest: must(digest.Parse("sha256:2f794176e9bd8a28501fa185693dc1073013a048c51585022ebce4f84b469db8")), }, }, } logger := zaptest.NewLogger(t) for _, test := range []struct { name string expectedImages []string expectedDigests []string }{ { name: "empty", }, { name: "by tag", expectedImages: []string{ "registry.io/org/image1:v1.3.7", }, expectedDigests: []string{ "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", }, }, { name: "by digest", expectedImages: []string{ "registry.io/org/image1@sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", }, expectedDigests: []string{ "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", }, }, { name: "by digest and tag", expectedImages: []string{ "registry.io/org/image1:v1.3.7@sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", }, expectedDigests: []string{ "sha256:7051a34bcd2522e58a2291d1aa065667f225fd07e4445590b091e86c6799b135", }, }, { name: "not found", expectedImages: []string{ "registry.io/org/image1:v1.3.9", }, }, } { t.Run(test.name, func(t *testing.T) { expectedDigests, err := crictrl.BuildExpectedDigests(logger, actualImages, test.expectedImages) require.NoError(t, err) expectedDigestKeys := maps.Keys(expectedDigests) slices.Sort(test.expectedDigests) slices.Sort(expectedDigestKeys) assert.Equal(t, test.expectedDigests, expectedDigestKeys) }) } } func must[T any](t T, err error) T { if err != nil { panic(err) } return t } ================================================ FILE: internal/app/machined/pkg/controllers/cri/registries_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" config2 "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // RegistriesConfigController watches v1alpha1.Config, updates registry.RegistriesConfig. type RegistriesConfigController struct{} // Name implements controller.Controller interface. func (ctrl *RegistriesConfigController) Name() string { return "cri.RegistriesConfigController" } // Inputs implements controller.Controller interface. func (ctrl *RegistriesConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: cri.NamespaceName, Type: cri.ImageCacheConfigType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *RegistriesConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: cri.RegistriesConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *RegistriesConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get machine config: %w", err) } imageCacheConfig, err := safe.ReaderGetByID[*cri.ImageCacheConfig](ctx, r, cri.ImageCacheConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get image cache config: %w", err) } if err := safe.WriterModify(ctx, r, cri.NewRegistriesConfig(), func(res *cri.RegistriesConfig) error { spec := res.TypedSpec() spec.RegistryAuths = clearInit(spec.RegistryAuths) spec.RegistryMirrors = clearInit(spec.RegistryMirrors) spec.RegistryTLSs = clearInit(spec.RegistryTLSs) if cfg != nil { for k, v := range cfg.Config().RegistryMirrorConfigs() { spec.RegistryMirrors[k] = &cri.RegistryMirrorConfig{ MirrorEndpoints: xslices.Map( v.Endpoints(), func(endpoint config2.RegistryEndpointConfig) cri.RegistryEndpointConfig { return cri.RegistryEndpointConfig{ EndpointEndpoint: endpoint.Endpoint(), EndpointOverridePath: endpoint.OverridePath(), } }, ), MirrorSkipFallback: v.SkipFallback(), } } for k, v := range cfg.Config().RegistryAuthConfigs() { spec.RegistryAuths[k] = &cri.RegistryAuthConfig{ RegistryUsername: v.Username(), RegistryPassword: v.Password(), RegistryAuth: v.Auth(), RegistryIdentityToken: v.IdentityToken(), } } for k, v := range cfg.Config().RegistryTLSConfigs() { spec.RegistryTLSs[k] = &cri.RegistryTLSConfig{ TLSCA: v.CA(), TLSInsecureSkipVerify: v.InsecureSkipVerify(), TLSClientIdentity: v.ClientIdentity(), } } } if imageCacheConfig != nil && imageCacheConfig.TypedSpec().Status == cri.ImageCacheStatusReady { // if the '*' was configured, we just use it, otherwise create it so that we can inject the registryd if _, hasStar := spec.RegistryMirrors["*"]; !hasStar { spec.RegistryMirrors["*"] = &cri.RegistryMirrorConfig{} } // inject the registryd mirror endpoint as the first one for all registries for registry := range spec.RegistryMirrors { spec.RegistryMirrors[registry].MirrorEndpoints = append( []cri.RegistryEndpointConfig{{EndpointEndpoint: "http://" + constants.RegistrydListenAddress}}, spec.RegistryMirrors[registry].MirrorEndpoints..., ) } } return nil }); err != nil { return fmt.Errorf("failed to write registries config: %w", err) } if err := safe.CleanupOutputs[*cri.RegistriesConfig](ctx, r); err != nil { return fmt.Errorf("failed to clean up outputs: %w", err) } } } func clearInit[M ~map[K]V, K comparable, V any](m M) M { if m == nil { return make(M) } clear(m) return m } ================================================ FILE: internal/app/machined/pkg/controllers/cri/registries_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "net/url" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/container" criconfig "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" crires "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) type ConfigSuite struct { ctest.DefaultSuite } func (suite *ConfigSuite) TestRegistry() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineRegistries: v1alpha1.RegistriesConfig{ RegistryMirrors: map[string]*v1alpha1.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []string{"https://mirror.io"}, MirrorOverridePath: new(true), }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []crires.RegistryEndpointConfig{ { EndpointEndpoint: "https://mirror.io", EndpointOverridePath: true, }, }, }, }, spec.RegistryMirrors, ) }) ic := crires.NewImageCacheConfig() ic.TypedSpec().Roots = []string{"/imagecache"} ic.TypedSpec().Status = crires.ImageCacheStatusReady suite.Require().NoError(suite.State().Create(suite.Ctx(), ic)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "*": { MirrorEndpoints: []crires.RegistryEndpointConfig{ { EndpointEndpoint: "http://" + constants.RegistrydListenAddress, }, }, }, "docker.io": { MirrorEndpoints: []crires.RegistryEndpointConfig{ { EndpointEndpoint: "http://" + constants.RegistrydListenAddress, }, { EndpointEndpoint: "https://mirror.io", EndpointOverridePath: true, }, }, }, }, spec.RegistryMirrors, ) }) } func (suite *ConfigSuite) TestRegistryAuth() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineRegistries: v1alpha1.RegistriesConfig{ RegistryMirrors: map[string]*v1alpha1.RegistryMirrorConfig{ "docker.io": {MirrorEndpoints: []string{"https://mirror.io"}}, }, RegistryConfig: map[string]*v1alpha1.RegistryConfig{ "docker.io": { RegistryAuth: &v1alpha1.RegistryAuthConfig{ RegistryUsername: "example", RegistryPassword: "pass", RegistryAuth: "someauth", RegistryIdentityToken: "token", }, }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "docker.io": {MirrorEndpoints: []crires.RegistryEndpointConfig{{EndpointEndpoint: "https://mirror.io"}}}, }, spec.RegistryMirrors, ) a.Equal( map[string]*crires.RegistryAuthConfig{ "docker.io": { RegistryUsername: "example", RegistryPassword: "pass", RegistryAuth: "someauth", RegistryIdentityToken: "token", }, }, spec.RegistryAuths, ) a.Empty(spec.RegistryTLSs) }) ic := crires.NewImageCacheConfig() ic.TypedSpec().Roots = []string{"/imagecache"} ic.TypedSpec().Status = crires.ImageCacheStatusReady suite.Require().NoError(suite.State().Create(suite.Ctx(), ic)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "*": {MirrorEndpoints: []crires.RegistryEndpointConfig{ {EndpointEndpoint: "http://" + constants.RegistrydListenAddress}, }}, "docker.io": {MirrorEndpoints: []crires.RegistryEndpointConfig{ {EndpointEndpoint: "http://" + constants.RegistrydListenAddress}, {EndpointEndpoint: "https://mirror.io"}, }}, }, spec.RegistryMirrors, ) a.Equal( map[string]*crires.RegistryAuthConfig{ "docker.io": { RegistryUsername: "example", RegistryPassword: "pass", RegistryAuth: "someauth", RegistryIdentityToken: "token", }, }, spec.RegistryAuths, ) a.Empty(spec.RegistryTLSs) }) } func (suite *ConfigSuite) TestRegistryTLS() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineRegistries: v1alpha1.RegistriesConfig{ RegistryMirrors: map[string]*v1alpha1.RegistryMirrorConfig{ "docker.io": {MirrorEndpoints: []string{"https://mirror.io"}}, }, RegistryConfig: map[string]*v1alpha1.RegistryConfig{ "docker.io": { RegistryTLS: &v1alpha1.RegistryTLSConfig{ TLSInsecureSkipVerify: new(true), }, }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "docker.io": {MirrorEndpoints: []crires.RegistryEndpointConfig{{EndpointEndpoint: "https://mirror.io"}}}, }, spec.RegistryMirrors, ) a.Empty(spec.RegistryAuths) a.Equal( map[string]*crires.RegistryTLSConfig{ "docker.io": { TLSInsecureSkipVerify: true, }, }, spec.RegistryTLSs, ) }) ic := crires.NewImageCacheConfig() ic.TypedSpec().Roots = []string{"/imagecache"} ic.TypedSpec().Status = crires.ImageCacheStatusReady suite.Require().NoError(suite.State().Create(suite.Ctx(), ic)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "*": {MirrorEndpoints: []crires.RegistryEndpointConfig{ {EndpointEndpoint: "http://" + constants.RegistrydListenAddress}, }}, "docker.io": {MirrorEndpoints: []crires.RegistryEndpointConfig{ {EndpointEndpoint: "http://" + constants.RegistrydListenAddress}, {EndpointEndpoint: "https://mirror.io"}, }}, }, spec.RegistryMirrors, ) a.Empty(spec.RegistryAuths) a.Equal( map[string]*crires.RegistryTLSConfig{ "docker.io": { TLSInsecureSkipVerify: true, }, }, spec.RegistryTLSs, ) }) } func (suite *ConfigSuite) TestRegistryImageCacheNoConfig() { ic := crires.NewImageCacheConfig() ic.TypedSpec().Roots = []string{"/imagecache"} ic.TypedSpec().Status = crires.ImageCacheStatusReady suite.Require().NoError(suite.State().Create(suite.Ctx(), ic)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "*": {MirrorEndpoints: []crires.RegistryEndpointConfig{ {EndpointEndpoint: "http://" + constants.RegistrydListenAddress}, }}, }, spec.RegistryMirrors, ) }) } func (suite *ConfigSuite) TestRegistryNoConfig() { ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Empty( spec.RegistryMirrors, ) }) } func (suite *ConfigSuite) TestRegistryNewStyle() { mr1 := criconfig.NewRegistryMirrorConfigV1Alpha1("docker.io") mr1.RegistryEndpoints = []criconfig.RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://mirror1.io"))}, EndpointOverridePath: new(true), }, { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://mirror2.io"))}, }, } ar1 := criconfig.NewRegistryAuthConfigV1Alpha1("registry-1.docker.io") ar1.RegistryUsername = "docker-example" ar1.RegistryPassword = "docker-pass" tr1 := criconfig.NewRegistryTLSConfigV1Alpha1("private-registry:3000") tr1.TLSInsecureSkipVerify = new(true) tr1.TLSClientIdentity = &meta.CertificateAndKey{ Cert: "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----", Key: "-----BEGIN PRIVATE KEY-----\nMIIE...AB\n-----END PRIVATE KEY-----", } tr1.TLSCA = "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----" tr2 := criconfig.NewRegistryTLSConfigV1Alpha1("another-registry") tr2.TLSInsecureSkipVerify = new(true) ctr, err := container.New(mr1, ar1, tr1, tr2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, crires.RegistriesConfigID, func(r *crires.RegistriesConfig, a *assert.Assertions) { spec := r.TypedSpec() a.Equal( map[string]*crires.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []crires.RegistryEndpointConfig{ { EndpointEndpoint: "https://mirror1.io", EndpointOverridePath: true, }, { EndpointEndpoint: "https://mirror2.io", }, }, }, }, spec.RegistryMirrors, ) a.Equal( map[string]*crires.RegistryAuthConfig{ "registry-1.docker.io": { RegistryUsername: "docker-example", RegistryPassword: "docker-pass", }, }, spec.RegistryAuths, ) a.Equal( map[string]*crires.RegistryTLSConfig{ "private-registry:3000": { TLSInsecureSkipVerify: true, TLSClientIdentity: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----"), Key: []byte("-----BEGIN PRIVATE KEY-----\nMIIE...AB\n-----END PRIVATE KEY-----"), }, TLSCA: []byte("-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----"), }, "another-registry": { TLSInsecureSkipVerify: true, }, }, spec.RegistryTLSs, ) }) } func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&cri.RegistriesConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/cri/seccomp_profile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // SeccompProfileController manages SeccompProfiles. type SeccompProfileController struct{} // Name implements controller.StatsController interface. func (ctrl *SeccompProfileController) Name() string { return "cri.SeccompProfileController" } // Inputs implements controller.StatsController interface. func (ctrl *SeccompProfileController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.StatsController interface. func (ctrl *SeccompProfileController) Outputs() []controller.Output { return []controller.Output{ { Type: cri.SeccompProfileType, Kind: controller.OutputExclusive, }, } } // Run implements controller.StatsController interface. func (ctrl *SeccompProfileController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } r.StartTrackingOutputs() if cfg.Config().Machine() != nil { for _, profile := range cfg.Config().Machine().SeccompProfiles() { if err = safe.WriterModify(ctx, r, cri.NewSeccompProfile(profile.Name()), func(cri *cri.SeccompProfile) error { cri.TypedSpec().Name = profile.Name() cri.TypedSpec().Value = profile.Value() return nil }); err != nil { return err } } } if err = safe.CleanupOutputs[*cri.SeccompProfile](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/cri/seccomp_profile_file.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "bytes" "context" "encoding/json" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // SeccompProfileFileController manages the Seccomp Profiles on the host. type SeccompProfileFileController struct { V1Alpha1Mode runtimetalos.Mode SeccompProfilesDirectory string } // Name implements controller.StatsController interface. func (ctrl *SeccompProfileFileController) Name() string { return "cri.SeccompProfileFileController" } // Inputs implements controller.StatsController interface. func (ctrl *SeccompProfileFileController) Inputs() []controller.Input { return nil } // Outputs implements controller.StatsController interface. func (ctrl *SeccompProfileFileController) Outputs() []controller.Output { return nil } // Run implements controller.StatsController interface. // //nolint:gocyclo,cyclop func (ctrl *SeccompProfileFileController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // initially, wait for /var to be mounted if err := r.UpdateInputs([]controller.Input{ { Namespace: runtimeres.NamespaceName, Type: runtimeres.MountStatusType, ID: optional.Some(constants.EphemeralPartitionLabel), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } _, err := safe.ReaderGet[*runtimeres.MountStatus](ctx, r, resource.NewMetadata(runtimeres.NamespaceName, runtimeres.MountStatusType, constants.EphemeralPartitionLabel, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { // in container mode EPHEMERAL is always mounted if ctrl.V1Alpha1Mode != runtimetalos.ModeContainer { // wait for the EPHEMERAL to be mounted continue } } else { return fmt.Errorf("error getting ephemeral mount status: %w", err) } } break } // normal reconcile loop if err := r.UpdateInputs([]controller.Input{ { Namespace: cri.NamespaceName, Type: cri.SeccompProfileType, Kind: controller.InputWeak, }, }); err != nil { return err } r.QueueReconcile() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } list, err := safe.ReaderListAll[*cri.SeccompProfile](ctx, r) if err != nil { return fmt.Errorf("error listing seccomp profiles: %w", err) } touchedIDs := make(map[string]struct{}, list.Len()) for profile := range list.All() { profileName := profile.TypedSpec().Name profilePath := filepath.Join(ctrl.SeccompProfilesDirectory, profileName) profileContent, err := json.Marshal(profile.TypedSpec().Value) if err != nil { return fmt.Errorf("error marshaling seccomp profile: %w", err) } existingProfileContent, err := os.ReadFile(profilePath) if err != nil { if !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("error reading existing seccomp profile at %s: %w", profilePath, err) } if err := writeSeccompFile(profilePath, profileContent); err != nil { return err } } else { if val := bytes.Compare(existingProfileContent, profileContent); val != 0 { if err := writeSeccompFile(profilePath, profileContent); err != nil { return err } } } touchedIDs[profileName] = struct{}{} } // cleanup if err := filepath.WalkDir(ctrl.SeccompProfilesDirectory, func(path string, d fs.DirEntry, err error) error { fileName, errRel := filepath.Rel(ctrl.SeccompProfilesDirectory, path) if errRel != nil { return errRel } // ignore current folder if fileName != "." { if _, ok := touchedIDs[fileName]; !ok { if err := os.RemoveAll(path); err != nil { return err } } } return nil }); err != nil { return err } r.ResetRestartBackoff() } } func writeSeccompFile(path string, content []byte) error { if err := os.WriteFile(path, content, 0o644); err != nil { return fmt.Errorf("error writing seccomp profile at %s: %w", path, err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/cri/seccomp_profile_file_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "encoding/json" "os" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" criseccompresource "github.com/siderolabs/talos/pkg/machinery/resources/cri" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func (suite *CRISeccompProfileFileSuite) TestReconcileSeccompProfileFile() { // need to mock mountStatus so that the controller moves ahead with the actual code mountStatus := runtimeres.NewMountStatus(runtimeres.NamespaceName, "EPHEMERAL") suite.Create(mountStatus) for _, tt := range []struct { seccompProfileName string seccompProfileValue map[string]any }{ { seccompProfileName: "audit.json", seccompProfileValue: map[string]any{ "defaultAction": "SCMP_ACT_LOG", }, }, { seccompProfileName: "deny.json", seccompProfileValue: map[string]any{ "defaultAction": "SCMP_ACT_ERRNO", }, }, } { seccompProfiles := criseccompresource.NewSeccompProfile(tt.seccompProfileName) seccompProfiles.TypedSpec().Name = tt.seccompProfileName seccompProfiles.TypedSpec().Value = tt.seccompProfileValue suite.Create(seccompProfiles) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) if !asrt.FileExists(suite.seccompProfilesDirectory + "/" + tt.seccompProfileName) { return } seccompProfileContent, err := os.ReadFile(suite.seccompProfilesDirectory + "/" + tt.seccompProfileName) asrt.NoError(err) expectedSeccompProfileContent, err := json.Marshal(tt.seccompProfileValue) asrt.NoError(err) asrt.Equal(seccompProfileContent, expectedSeccompProfileContent) }, time.Second, 100*time.Millisecond) } // create a directory and file manually in the seccomp profile directory // ensure that the controller deletes the manually created directory/file // also ensure that an update doesn't update existing files timestamp suite.Require().NoError(os.Mkdir(suite.seccompProfilesDirectory+"/test", 0o755)) suite.Require().NoError(os.WriteFile(suite.seccompProfilesDirectory+"/test.json", []byte("{}"), 0o644)) auditJSONSeccompProfile, err := os.Stat(suite.seccompProfilesDirectory + "/audit.json") suite.Require().NoError(err) // delete deny.json resource suite.Require().NoError(suite.State().Destroy(suite.Ctx(), resource.NewMetadata(criseccompresource.NamespaceName, criseccompresource.SeccompProfileType, "deny.json", resource.VersionUndefined))) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) auditJSONSeccompProfileAfterUpdate, err := os.Stat(suite.seccompProfilesDirectory + "/audit.json") if !asrt.NoError(err) { return } asrt.Equal(auditJSONSeccompProfile.ModTime(), auditJSONSeccompProfileAfterUpdate.ModTime()) }, 1*time.Second, 100*time.Millisecond) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) asrt.NoFileExists(suite.seccompProfilesDirectory + "/deny.json") asrt.NoFileExists(suite.seccompProfilesDirectory + "/test.json") asrt.NoDirExists(suite.seccompProfilesDirectory + "/test") }, 1*time.Second, 100*time.Millisecond) } func TestSeccompProfileFileSuite(t *testing.T) { seccompProfiesDirectory := t.TempDir() suite.Run(t, &CRISeccompProfileFileSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&cri.SeccompProfileFileController{ SeccompProfilesDirectory: seccompProfiesDirectory, })) }, }, seccompProfilesDirectory: seccompProfiesDirectory, }) } type CRISeccompProfileFileSuite struct { ctest.DefaultSuite seccompProfilesDirectory string } ================================================ FILE: internal/app/machined/pkg/controllers/cri/seccomp_profile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" criseccompresource "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) func (suite *CRISeccompProfileSuite) TestReconcileSeccompProfile() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineSeccompProfiles: []*v1alpha1.MachineSeccompProfile{ { MachineSeccompProfileName: "audit.json", MachineSeccompProfileValue: v1alpha1.Unstructured{ Object: map[string]any{ "defaultAction": "SCMP_ACT_LOG", }, }, }, { MachineSeccompProfileName: "deny.json", MachineSeccompProfileValue: v1alpha1.Unstructured{ Object: map[string]any{ "defaultAction": "SCMP_ACT_ERRNO", }, }, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) for _, tt := range []struct { name string value map[string]any }{ { name: "audit.json", value: map[string]any{ "defaultAction": "SCMP_ACT_LOG", }, }, { name: "deny.json", value: map[string]any{ "defaultAction": "SCMP_ACT_ERRNO", }, }, } { suite.AssertWithin(1*time.Second, 100*time.Millisecond, func() error { seccompProfile, err := ctest.Get[*criseccompresource.SeccompProfile]( suite, criseccompresource.NewSeccompProfile(tt.name).Metadata(), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := seccompProfile.TypedSpec() suite.Assert().Equal(tt.name, spec.Name) suite.Assert().Equal(tt.value, spec.Value) return nil }) } // test deletion cfg = config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineSeccompProfiles: []*v1alpha1.MachineSeccompProfile{ { MachineSeccompProfileName: "audit.json", MachineSeccompProfileValue: v1alpha1.Unstructured{ Object: map[string]any{ "defaultAction": "SCMP_ACT_LOG", }, }, }, }, }, })) ctest.UpdateWithConflicts(suite, cfg, func(mc *config.MachineConfig) error { return nil }) suite.AssertWithin(1*time.Second, 100*time.Millisecond, func() error { _, err := ctest.Get[*criseccompresource.SeccompProfile]( suite, criseccompresource.NewSeccompProfile("deny.json").Metadata(), ) if err != nil { if !state.IsNotFoundError(err) { return err } return err } return nil }) } func TestSeccompProfileSuite(t *testing.T) { suite.Run(t, &CRISeccompProfileSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&cri.SeccompProfileController{})) }, }, }) } type CRISeccompProfileSuite struct { ctest.DefaultSuite } ================================================ FILE: internal/app/machined/pkg/controllers/ctest/assert.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package ctest import ( "fmt" "slices" "strings" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type assertionAggregator struct { errors map[string]struct{} failNow bool hadErrors bool } func (agg *assertionAggregator) Errorf(format string, args ...any) { errorString := fmt.Sprintf(format, args...) if agg.errors == nil { agg.errors = make(map[string]struct{}) } agg.errors[errorString] = struct{}{} agg.hadErrors = true } func (agg *assertionAggregator) FailNow() { agg.failNow = true } func (agg *assertionAggregator) Error() error { if !agg.hadErrors { return nil } lines := make([]string, 0, len(agg.errors)) for errorString := range agg.errors { lines = append(lines, " * "+errorString) } slices.Sort(lines) return fmt.Errorf("%s", strings.Join(lines, "\n")) } // WrapRetry wraps the function with assertions and requires to return retry-compatible errors. func WrapRetry(f func(*assert.Assertions, *require.Assertions)) func() error { return func() error { var errs assertionAggregator f(assert.New(&errs), require.New(&errs)) if errs.failNow { return errs.Error() } return retry.ExpectedError(errs.Error()) } } ================================================ FILE: internal/app/machined/pkg/controllers/ctest/ctest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ctest provides basic types and functions for controller testing. package ctest import ( "context" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" ) // DefaultSuite is a base suite for controller testing. type DefaultSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc AfterSetup func(suite *DefaultSuite) AfterTearDown func(suite *DefaultSuite) Timeout time.Duration } // SetupTest is a function for setting up a test. func (suite *DefaultSuite) SetupTest() { if suite.Timeout == 0 { suite.Timeout = 3 * time.Minute } suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), suite.Timeout) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.startRuntime() if suite.AfterSetup != nil { suite.AfterSetup(suite) } } func (suite *DefaultSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } // Runtime returns the runtime of the suite. func (suite *DefaultSuite) Runtime() *runtime.Runtime { return suite.runtime } // State returns the state of the suite. func (suite *DefaultSuite) State() state.State { return suite.state } // Ctx returns the context of the suite. func (suite *DefaultSuite) Ctx() context.Context { return suite.ctx } // AssertWithin asserts that fn returns within the given duration without an error. func (suite *DefaultSuite) AssertWithin(d time.Duration, rate time.Duration, fn func() error) { retryer := retry.Constant(d, retry.WithUnits(rate)) suite.Assert().NoError(retryer.Retry(fn)) } // TearDownTest is a function for tearing down a test. func (suite *DefaultSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() if suite.AfterTearDown != nil { suite.AfterTearDown(suite) } } // Create creates a new resource in the state of the suite. func (suite *DefaultSuite) Create(res resource.Resource, opts ...state.CreateOption) { suite.Require().NoError(suite.State().Create(suite.Ctx(), res, opts...)) } // Update updates a resource in the state of the suite. func (suite *DefaultSuite) Update(res resource.Resource, opts ...state.UpdateOption) { suite.Require().NoError(suite.State().Update(suite.Ctx(), res, opts...)) } // AddFinalizer adds a finalizer to a resource in the state of the suite. func (suite *DefaultSuite) AddFinalizer(resourcePointer resource.Pointer, finalizer string) { suite.Require().NoError(suite.State().AddFinalizer(suite.Ctx(), resourcePointer, finalizer)) } // RemoveFinalizer removes a finalizer from a resource in the state of the suite. func (suite *DefaultSuite) RemoveFinalizer(resourcePointer resource.Pointer, finalizer string) { suite.Require().NoError(suite.State().RemoveFinalizer(suite.Ctx(), resourcePointer, finalizer)) } // Destroy destroys a resource in the state of the suite. func (suite *DefaultSuite) Destroy(res resource.Resource, opts ...state.DestroyOption) { suite.Require().NoError(suite.State().Destroy(suite.Ctx(), res.Metadata(), opts...)) } // Suite is a type which describes the suite type. type Suite interface { T() *testing.T Require() *require.Assertions State() state.State Ctx() context.Context } // UpdateWithConflicts is a type safe wrapper around state.UpdateWithConflicts which uses the provided suite. func UpdateWithConflicts[T resource.Resource](suite Suite, res T, updateFn func(T) error, options ...state.UpdateOption) T { //nolint:ireturn suite.T().Helper() result, err := safe.StateUpdateWithConflicts(suite.Ctx(), suite.State(), res.Metadata(), updateFn, options...) suite.Require().NoError(err) return result } // GetUsingResource is a type safe wrapper around state.StateGetResource which uses the provided suite. func GetUsingResource[T resource.Resource](suite Suite, res T, options ...state.GetOption) (T, error) { //nolint:ireturn return safe.StateGetResource(suite.Ctx(), suite.State(), res, options...) } // Get is a type safe wrapper around state.Get which uses the provided suite. func Get[T resource.Resource](suite Suite, ptr resource.Pointer, options ...state.GetOption) (T, error) { //nolint:ireturn return safe.StateGet[T](suite.Ctx(), suite.State(), ptr, options...) } // Suiter is like Suite but do not require Require() method. type Suiter interface { T() *testing.T State() state.State Ctx() context.Context } // AssertResources asserts on a resource list. func AssertResources[R rtestutils.ResourceWithRD]( suiter Suiter, requiredIDs []resource.ID, check func(R, *assert.Assertions), opts ...rtestutils.Option, ) { ctx, cancel := context.WithTimeout(suiter.Ctx(), 10*time.Second) defer cancel() rtestutils.AssertResources(ctx, suiter.T(), suiter.State(), requiredIDs, check, opts...) } // AssertResource asserts on a single resource. func AssertResource[R rtestutils.ResourceWithRD]( suiter Suiter, requiredIDs resource.ID, check func(R, *assert.Assertions), opts ...rtestutils.Option, ) { AssertResources(suiter, []resource.ID{requiredIDs}, check, opts...) } // AssertNoResource asserts that a resource no longer exists. func AssertNoResource[R rtestutils.ResourceWithRD]( suiter Suiter, id string, opts ...rtestutils.Option, ) { ctx, cancel := context.WithTimeout(suiter.Ctx(), 10*time.Second) defer cancel() rtestutils.AssertNoResource[R]( ctx, suiter.T(), suiter.State(), id, opts..., ) } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/advertised_peer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "errors" "fmt" "net/netip" "slices" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.etcd.io/etcd/api/v3/etcdserverpb" "go.uber.org/zap" etcdcli "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // AdvertisedPeerController updates advertised peer list for this instance of etcd. type AdvertisedPeerController struct{} // Name implements controller.Controller interface. func (ctrl *AdvertisedPeerController) Name() string { return "etcd.AdvertisedPeerController" } // Inputs implements controller.Controller interface. func (ctrl *AdvertisedPeerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: etcd.NamespaceName, Type: etcd.SpecType, ID: optional.Some(etcd.SpecID), Kind: controller.InputWeak, }, { Namespace: etcd.NamespaceName, Type: etcd.PKIStatusType, ID: optional.Some(etcd.PKIID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("etcd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *AdvertisedPeerController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *AdvertisedPeerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } etcdService, err := safe.ReaderGet[*v1alpha1.Service](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "etcd", resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting etcd service: %w", err) } if !(etcdService.TypedSpec().Healthy && etcdService.TypedSpec().Running) { continue } etcdSpec, err := safe.ReaderGet[*etcd.Spec](ctx, r, resource.NewMetadata(etcd.NamespaceName, etcd.SpecType, etcd.SpecID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting etcd spec: %w", err) } _, err = safe.ReaderGet[*etcd.PKIStatus](ctx, r, resource.NewMetadata(etcd.NamespaceName, etcd.PKIStatusType, etcd.PKIID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting etcd PKI status: %w", err) } if err = ctrl.updateAdvertisedPeers(ctx, logger, etcdSpec.TypedSpec().AdvertisedAddresses); err != nil { return fmt.Errorf("error updating advertised peers: %w", err) } r.ResetRestartBackoff() } } func (ctrl *AdvertisedPeerController) updateAdvertisedPeers(ctx context.Context, logger *zap.Logger, advertisedAddresses []netip.Addr) error { ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() client, err := etcdcli.NewLocalClient(ctx) if err != nil { return fmt.Errorf("error creating etcd client: %w", err) } defer client.Close() //nolint:errcheck // figure out local member ID resp, err := client.MemberList(ctx) if err != nil { return fmt.Errorf("error getting member list: %w", err) } localMemberID := resp.Header.MemberId var localMember *etcdserverpb.Member for _, member := range resp.Members { if member.ID == localMemberID { localMember = member break } } if localMember == nil { return errors.New("local member not found in member list") } newPeerURLs := xslices.Map(advertisedAddresses, func(addr netip.Addr) string { return fmt.Sprintf("https://%s", nethelpers.JoinHostPort(addr.String(), constants.EtcdPeerPort)) }) currentPeerURLs := localMember.PeerURLs if slices.Equal(newPeerURLs, currentPeerURLs) { return nil } logger.Debug("updating etcd peer URLs", zap.Strings("current_peer_urls", currentPeerURLs), zap.Strings("new_peer_urls", newPeerURLs), zap.Uint64("member_id", localMemberID), ) _, err = client.MemberUpdate(ctx, localMemberID, newPeerURLs) if err != nil { return fmt.Errorf("error updating member: %w", err) } logger.Info("updated etcd peer URLs", zap.Strings("new_peer_urls", newPeerURLs), zap.Uint64("member_id", localMemberID), ) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) // ConfigController watches v1alpha1.Config, updates etcd config. type ConfigController = transform.Controller[*config.MachineConfig, *etcd.Config] // NewConfigController instanciates the config controller. // //nolint:gocyclo func NewConfigController() *ConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *etcd.Config]{ Name: "etcd.ConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*etcd.Config] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*etcd.Config]() } if cfg.Config().Machine() == nil || cfg.Config().Cluster() == nil { return optional.None[*etcd.Config]() } if !cfg.Config().Machine().Type().IsControlPlane() { // etcd only runs on controlplane nodes return optional.None[*etcd.Config]() } return optional.Some(etcd.NewConfig(etcd.NamespaceName, etcd.ConfigID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, cfg *etcd.Config) error { cfg.TypedSpec().AdvertiseValidSubnets = machineConfig.Config().Cluster().Etcd().AdvertisedSubnets() cfg.TypedSpec().AdvertiseExcludeSubnets = nil cfg.TypedSpec().ListenValidSubnets = machineConfig.Config().Cluster().Etcd().ListenSubnets() cfg.TypedSpec().ListenExcludeSubnets = nil // filter out any virtual IPs, they can't be node IPs either for _, device := range machineConfig.Config().Machine().Network().Devices() { if device.VIPConfig() != nil { cfg.TypedSpec().AdvertiseExcludeSubnets = append(cfg.TypedSpec().AdvertiseExcludeSubnets, device.VIPConfig().IP()) } for _, vlan := range device.Vlans() { if vlan.VIPConfig() != nil { cfg.TypedSpec().AdvertiseExcludeSubnets = append(cfg.TypedSpec().AdvertiseExcludeSubnets, vlan.VIPConfig().IP()) } } } for _, doc := range machineConfig.Config().NetworkVirtualIPConfigs() { cfg.TypedSpec().AdvertiseExcludeSubnets = append(cfg.TypedSpec().AdvertiseExcludeSubnets, doc.VIP().String()) } cfg.TypedSpec().Image = machineConfig.Config().Cluster().Etcd().Image() extraArgs := make(map[string]etcd.ArgValues, len(machineConfig.Config().Cluster().Etcd().ExtraArgs())) for k, v := range machineConfig.Config().Cluster().Etcd().ExtraArgs() { extraArgs[k] = etcd.ArgValues{Values: v} } cfg.TypedSpec().ExtraArgs = extraArgs return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd_test import ( "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" etcdctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/etcd" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(etcdctrl.NewConfigController())) }, }, }) } type ConfigSuite struct { ctest.DefaultSuite } func (suite *ConfigSuite) TestReconcile() { for _, tt := range []struct { name string etcdConfig *v1alpha1.EtcdConfig networkConfig v1alpha1.NetworkDeviceList expectedConfig etcd.ConfigSpec extraConfig func(*testing.T) []configconfig.Document }{ { name: "default config", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: nil, ListenValidSubnets: nil, }, }, { name: "extra args config", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdExtraArgs: v1alpha1.Args{ "foo": v1alpha1.NewArgValue("", []string{"bar", "baz"}), }, }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{ "foo": {Values: []string{"bar", "baz"}}, }, AdvertiseValidSubnets: nil, ListenValidSubnets: nil, }, }, { name: "legacy subnet", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdExtraArgs: v1alpha1.Args{ "arg": v1alpha1.NewArgValue("value", nil), }, EtcdSubnet: "10.0.0.0/8", }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{ "arg": {Values: []string{"value"}}, }, AdvertiseValidSubnets: []string{"10.0.0.0/8"}, ListenValidSubnets: nil, }, }, { name: "advertised subnets", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdAdvertisedSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, ListenValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, }, { name: "advertised and listen subnets", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdAdvertisedSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, EtcdListenSubnets: []string{"10.0.0.0/8"}, }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, ListenValidSubnets: []string{"10.0.0.0/8"}, }, }, { name: "default with legacy vip", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", }, networkConfig: v1alpha1.NetworkDeviceList{ { DeviceInterface: "eth0", DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "10.0.0.4", }, }, }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: nil, AdvertiseExcludeSubnets: []string{"10.0.0.4"}, ListenValidSubnets: nil, }, }, { name: "default with new vip", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", }, extraConfig: func(*testing.T) []configconfig.Document { vipCfg := network.NewLayer2VIPConfigV1Alpha1("10.0.0.4") vipCfg.LinkName = "eth0" return []configconfig.Document{vipCfg} }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: nil, AdvertiseExcludeSubnets: []string{"10.0.0.4"}, ListenValidSubnets: nil, }, }, { name: "advertised with legacy vip", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdAdvertisedSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, networkConfig: v1alpha1.NetworkDeviceList{ { DeviceInterface: "eth0", DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "10.0.0.4", }, }, }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, AdvertiseExcludeSubnets: []string{"10.0.0.4"}, ListenValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, }, { name: "advertised with new vip", etcdConfig: &v1alpha1.EtcdConfig{ ContainerImage: "foo/bar:v1.0.0", EtcdAdvertisedSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, extraConfig: func(*testing.T) []configconfig.Document { vipCfg := network.NewLayer2VIPConfigV1Alpha1("10.0.0.4") vipCfg.LinkName = "eth0" return []configconfig.Document{vipCfg} }, expectedConfig: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{}, AdvertiseValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, AdvertiseExcludeSubnets: []string{"10.0.0.4"}, ListenValidSubnets: []string{"10.0.0.0/8", "192.168.0.0/24"}, }, }, } { suite.Run(tt.name, func() { cfgV1Alpha1 := &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ EtcdConfig: tt.etcdConfig, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: tt.networkConfig, }, }, } documents := []configconfig.Document{cfgV1Alpha1} if tt.extraConfig != nil { documents = slices.Concat(documents, tt.extraConfig(suite.T())) } ctr, err := container.New(documents...) suite.Require().NoError(err) machineConfig := config.NewMachineConfig(ctr) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineConfig)) suite.AssertWithin(3*time.Second, 100*time.Millisecond, ctest.WrapRetry(func(assert *assert.Assertions, require *require.Assertions) { etcdConfig, err := safe.StateGet[*etcd.Config](suite.Ctx(), suite.State(), etcd.NewConfig(etcd.NamespaceName, etcd.ConfigID).Metadata()) if err != nil { assert.NoError(err) return } assert.Equal(tt.expectedConfig, *etcdConfig.TypedSpec()) })) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), machineConfig.Metadata())) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package etcd provides controllers which manage etcd resources. package etcd ================================================ FILE: internal/app/machined/pkg/controllers/etcd/member.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" pkgetcd "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // MemberController updates information about the local etcd member. type MemberController struct { GetLocalMemberIDFunc func(ctx context.Context) (uint64, error) } // Name implements controller.Controller interface. func (ctrl *MemberController) Name() string { return "etcd.MemberController" } const etcdServiceID = "etcd" // Inputs implements controller.Controller interface. func (ctrl *MemberController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some(etcdServiceID), Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *MemberController) Outputs() []controller.Output { return []controller.Output{ { Type: etcd.MemberType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *MemberController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } m := etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID) etcdService, err := safe.ReaderGet[*v1alpha1.Service](ctx, r, v1alpha1.NewService(etcdServiceID).Metadata()) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting etcd service resource: %w", err) } updateMemberID := etcdService != nil && etcdService.Metadata().Phase() == resource.PhaseRunning && etcdService.TypedSpec().Healthy if updateMemberID { var memberID uint64 memberID, err = ctrl.getLocalMemberID(ctx) if err != nil { return fmt.Errorf("error getting etcd local member ID: %w", err) } if err = safe.WriterModify(ctx, r, m, func(status *etcd.Member) error { status.TypedSpec().MemberID = etcd.FormatMemberID(memberID) return nil }); err != nil { return fmt.Errorf("error updating etcd member resource: %w", err) } } else { if err = r.Destroy(ctx, m.Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying etcd member resource: %w", err) } } r.ResetRestartBackoff() } } // getLocalMemberID gets the etcd member ID of the local node. func (ctrl *MemberController) getLocalMemberID(ctx context.Context) (uint64, error) { if ctrl.GetLocalMemberIDFunc != nil { return ctrl.GetLocalMemberIDFunc(ctx) } client, err := pkgetcd.NewLocalClient(ctx) if err != nil { return 0, err } defer client.Close() //nolint:errcheck return client.GetMemberID(ctx) } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/member_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd_test import ( "context" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" etcdctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) func TestMemberSuite(t *testing.T) { t.Parallel() ctrl := &etcdctrl.MemberController{} suite.Run(t, &MemberSuite{ ctrl: ctrl, DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(ctrl)) }, }, }) } type MemberSuite struct { ctest.DefaultSuite ctrl *etcdctrl.MemberController } func (suite *MemberSuite) assertEtcdMember(member *etcd.Member) func() error { return func() error { r, err := ctest.Get[*etcd.Member](suite, member.Metadata()) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := r.TypedSpec() expectedSpec := member.TypedSpec() suite.Require().Equal(expectedSpec.MemberID, spec.MemberID) return nil } } func (suite *MemberSuite) assertInexistentEtcdMember(member *etcd.Member) func() error { return func() error { _, err := suite.State().Get(suite.Ctx(), member.Metadata()) if err != nil { if state.IsNotFoundError(err) { return nil } return retry.ExpectedError(err) } return retry.ExpectedErrorf("should not exist") } } func (suite *MemberSuite) TestEtcdRunning() { // given suite.ctrl.GetLocalMemberIDFunc = func(ctx context.Context) (uint64, error) { return 123, nil } etcdService := v1alpha1.NewService("etcd") etcdService.TypedSpec().Running = true etcdService.TypedSpec().Healthy = true // when suite.Require().NoError(suite.State().Create(suite.Ctx(), etcdService)) // then expectedMember := etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID) expectedMember.TypedSpec().MemberID = "000000000000007b" suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertEtcdMember(expectedMember), ), ) } func (suite *MemberSuite) TestEtcdNotRunning() { // given suite.ctrl.GetLocalMemberIDFunc = func(ctx context.Context) (uint64, error) { return 123, nil } etcdService := v1alpha1.NewService("etcd") etcdService.TypedSpec().Running = false // when suite.Require().NoError(suite.State().Create(suite.Ctx(), etcdService)) // then expectedMember := etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID) expectedMember.TypedSpec().MemberID = "" suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertInexistentEtcdMember(expectedMember), ), ) } func (suite *MemberSuite) TestCleanup() { // given suite.ctrl.GetLocalMemberIDFunc = func(ctx context.Context) (uint64, error) { return 123, nil } etcdService := v1alpha1.NewService("etcd") etcdService.TypedSpec().Running = true etcdService.TypedSpec().Healthy = true expectedMember := etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID) expectedMember.TypedSpec().MemberID = "000000000000007b" suite.Require().NoError(suite.State().Create(suite.Ctx(), etcdService)) suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertEtcdMember(expectedMember), ), ) // when okToDestroy, err := suite.State().Teardown(suite.Ctx(), etcdService.Metadata()) suite.Require().NoError(err) suite.Require().True(okToDestroy) // then suite.Assert().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertInexistentEtcdMember(expectedMember), )) } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/pki.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "fmt" "os" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/filetree" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // PKIController renders manifests based on templates and config/secrets. type PKIController struct{} // Name implements controller.Controller interface. func (ctrl *PKIController) Name() string { return "etcd.PKIController" } // Inputs implements controller.Controller interface. func (ctrl *PKIController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.EtcdRootType, ID: optional.Some(secrets.EtcdRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.EtcdType, ID: optional.Some(secrets.EtcdID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PKIController) Outputs() []controller.Output { return []controller.Output{ { Type: etcd.PKIStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *PKIController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } rootScrts, err := safe.ReaderGet[*secrets.EtcdRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdRootType, secrets.EtcdRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting root secrets: %w", err) } scrts, err := safe.ReaderGet[*secrets.Etcd](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdType, secrets.EtcdID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets: %w", err) } if err = os.MkdirAll(constants.EtcdPKIPath, 0o700); err != nil { return err } if err = selinux.SetLabel(constants.EtcdPKIPath, constants.EtcdPKISELinuxLabel); err != nil { return err } if err = os.WriteFile(constants.EtcdCACert, rootScrts.TypedSpec().EtcdCA.Crt, 0o400); err != nil { return fmt.Errorf("failed to write CA certificate: %w", err) } if err = os.WriteFile(constants.EtcdCAKey, rootScrts.TypedSpec().EtcdCA.Key, 0o400); err != nil { return fmt.Errorf("failed to write CA key: %w", err) } etcdCerts := scrts.TypedSpec() for _, keypair := range []struct { getter func() *x509.PEMEncodedCertificateAndKey keyPath string certPath string }{ { getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.Etcd }, keyPath: constants.EtcdKey, certPath: constants.EtcdCert, }, { getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdPeer }, keyPath: constants.EtcdPeerKey, certPath: constants.EtcdPeerCert, }, { getter: func() *x509.PEMEncodedCertificateAndKey { return etcdCerts.EtcdAdmin }, keyPath: constants.EtcdAdminKey, certPath: constants.EtcdAdminCert, }, } { if err = os.WriteFile(keypair.keyPath, keypair.getter().Key, 0o400); err != nil { return err } if err = os.WriteFile(keypair.certPath, keypair.getter().Crt, 0o400); err != nil { return err } } if err = filetree.ChownRecursive(constants.EtcdPKIPath, constants.EtcdUserID, constants.EtcdUserID); err != nil { return err } if err = safe.WriterModify(ctx, r, etcd.NewPKIStatus(etcd.NamespaceName, etcd.PKIID), func(status *etcd.PKIStatus) error { status.TypedSpec().Ready = true status.TypedSpec().Version = scrts.Metadata().Version().String() return nil }); err != nil { return fmt.Errorf("error updating PKI status: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/net" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // SpecController renders manifests based on templates and Spec/secrets. type SpecController struct{} // Name implements controller.Controller interface. func (ctrl *SpecController) Name() string { return "etcd.SpecController" } // Inputs implements controller.Controller interface. func (ctrl *SpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: etcd.NamespaceName, Type: etcd.ConfigType, ID: optional.Some(etcd.ConfigID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SpecController) Outputs() []controller.Output { return []controller.Output{ { Type: etcd.SpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *SpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } etcdConfig, err := safe.ReaderGet[*etcd.Config](ctx, r, resource.NewMetadata(etcd.NamespaceName, etcd.ConfigType, etcd.ConfigID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting etcd config: %w", err) } hostnameStatus, err := safe.ReaderGet[*network.HostnameStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting hostname status: %w", err) } nodeRoutedAddrs, err := safe.ReaderGet[*network.NodeAddress]( ctx, r, resource.NewMetadata( network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting addresses: %w", err) } nodeCurrentAddrs, err := safe.ReaderGet[*network.NodeAddress]( ctx, r, resource.NewMetadata( network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting addresses: %w", err) } routedAddrs := nodeRoutedAddrs.TypedSpec().IPs() currentAddrs := nodeCurrentAddrs.TypedSpec().IPs() // need at least a single address if len(routedAddrs) == 0 { continue } advertiseValidSubnets := etcdConfig.TypedSpec().AdvertiseValidSubnets if len(advertiseValidSubnets) == 0 { // not specified, advertise all addresses advertiseValidSubnets = []string{"0.0.0.0/0", "::/0"} } advertisedCIDRs := slices.Concat( advertiseValidSubnets, xslices.Map(etcdConfig.TypedSpec().AdvertiseExcludeSubnets, func(cidr string) string { return "!" + cidr }), ) listenCIDRs := slices.Concat( etcdConfig.TypedSpec().ListenValidSubnets, xslices.Map(etcdConfig.TypedSpec().ListenExcludeSubnets, func(cidr string) string { return "!" + cidr }), ) defaultListenAddress := netip.IPv4Unspecified() loopbackAddress := netip.AddrFrom4([4]byte{127, 0, 0, 1}) var ( advertisedIPs []netip.Addr listenPeerIPs []netip.Addr listenClientIPs []netip.Addr ) if len(etcdConfig.TypedSpec().AdvertiseValidSubnets) == 0 { advertisedIPs, err = net.FilterIPs(routedAddrs, advertisedCIDRs) if err != nil { return fmt.Errorf("error filtering IPs: %w", err) } // if advertise subnet is not set, advertise the first address if len(advertisedIPs) > 0 { advertisedIPs = advertisedIPs[:1] } } else { advertisedIPs, err = net.FilterIPs(currentAddrs, advertisedCIDRs) if err != nil { return fmt.Errorf("error filtering IPs: %w", err) } } if len(listenCIDRs) > 0 { listenPeerIPs, err = net.FilterIPs(routedAddrs, listenCIDRs) if err != nil { return fmt.Errorf("error filtering IPs: %w", err) } listenClientIPs = append([]netip.Addr{loopbackAddress}, listenPeerIPs...) } else { listenPeerIPs = []netip.Addr{defaultListenAddress} listenClientIPs = []netip.Addr{defaultListenAddress} } if len(advertisedIPs) == 0 || len(listenPeerIPs) == 0 { continue } if err = safe.WriterModify(ctx, r, etcd.NewSpec(etcd.NamespaceName, etcd.SpecID), func(status *etcd.Spec) error { status.TypedSpec().AdvertisedAddresses = advertisedIPs status.TypedSpec().ListenClientAddresses = listenClientIPs status.TypedSpec().ListenPeerAddresses = listenPeerIPs status.TypedSpec().Name = hostnameStatus.TypedSpec().Hostname status.TypedSpec().Image = etcdConfig.TypedSpec().Image status.TypedSpec().ExtraArgs = etcdConfig.TypedSpec().ExtraArgs return nil }); err != nil { return fmt.Errorf("error updating Spec status: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/etcd/spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" etcdctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestSpecSuite(t *testing.T) { t.Parallel() suite.Run(t, &SpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&etcdctrl.SpecController{})) }, }, }) } type SpecSuite struct { ctest.DefaultSuite } func (suite *SpecSuite) TestReconcile() { hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "worker1" hostnameStatus.TypedSpec().Domainname = "some.domain" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) routedAddresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), ) routedAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.0.0.5/24"), netip.MustParsePrefix("192.168.1.1/24"), netip.MustParsePrefix("192.168.1.50/32"), netip.MustParsePrefix("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"), netip.MustParsePrefix("2002:0db8:85a3:0000:0000:8a2e:0370:7335/64"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), routedAddresses)) currentAddrs := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s), ) currentAddrs.TypedSpec().Addresses = append( []netip.Prefix{netip.MustParsePrefix("1.3.5.7/32")}, routedAddresses.TypedSpec().Addresses..., ) suite.Require().NoError(suite.State().Create(suite.Ctx(), currentAddrs)) for _, tt := range []struct { name string cfg etcd.ConfigSpec expected etcd.SpecSpec }{ { name: "defaults", cfg: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{ "arg": {Values: []string{"value"}}, }, }, expected: etcd.SpecSpec{ Name: "worker1", Image: "foo/bar:v1.0.0", ExtraArgs: map[string]etcd.ArgValues{ "arg": {Values: []string{"value"}}, }, AdvertisedAddresses: []netip.Addr{ netip.MustParseAddr("10.0.0.5"), }, ListenPeerAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, ListenClientAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, }, }, { name: "defaults with exclude", cfg: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", AdvertiseExcludeSubnets: []string{ "10.0.0.5", }, }, expected: etcd.SpecSpec{ Name: "worker1", Image: "foo/bar:v1.0.0", AdvertisedAddresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), }, ListenPeerAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, ListenClientAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, }, }, { name: "only advertised", cfg: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", AdvertiseValidSubnets: []string{ "192.168.0.0/16", "1.3.5.7/32", }, }, expected: etcd.SpecSpec{ Name: "worker1", Image: "foo/bar:v1.0.0", AdvertisedAddresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.50"), netip.MustParseAddr("1.3.5.7"), }, ListenPeerAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, ListenClientAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, }, }, { name: "only advertised with exclude", cfg: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", AdvertiseValidSubnets: []string{ "192.168.0.0/16", }, AdvertiseExcludeSubnets: []string{ "10.0.0.5", "192.168.1.50", }, }, expected: etcd.SpecSpec{ Name: "worker1", Image: "foo/bar:v1.0.0", AdvertisedAddresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), }, ListenPeerAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, ListenClientAddresses: []netip.Addr{ netip.IPv4Unspecified(), }, }, }, { name: "advertised and listen", cfg: etcd.ConfigSpec{ Image: "foo/bar:v1.0.0", AdvertiseValidSubnets: []string{ "192.168.0.0/16", "2001::/16", }, ListenValidSubnets: []string{ "192.168.0.0/16", }, }, expected: etcd.SpecSpec{ Name: "worker1", Image: "foo/bar:v1.0.0", AdvertisedAddresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.50"), netip.MustParseAddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"), }, ListenPeerAddresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.50"), }, ListenClientAddresses: []netip.Addr{ netip.MustParseAddr("127.0.0.1"), netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.50"), }, }, }, } { suite.Run(tt.name, func() { etcdConfig := etcd.NewConfig(etcd.NamespaceName, etcd.ConfigID) *etcdConfig.TypedSpec() = tt.cfg suite.Require().NoError(suite.State().Create(suite.Ctx(), etcdConfig)) ctest.AssertResource(suite, etcd.SpecID, func(etcdSpec *etcd.Spec, asrt *assert.Assertions) { asrt.Equal(tt.expected, *etcdSpec.TypedSpec(), "spec %v", *etcdSpec.TypedSpec()) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), etcdConfig.Metadata())) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/files/cri_base_runtime_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "context" "encoding/json" "fmt" "github.com/containerd/containerd/v2/core/containers" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/platforms" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) // CRIBaseRuntimeSpecController generates parts of the CRI config for base OCI runtime configuration. type CRIBaseRuntimeSpecController struct{} // Name implements controller.Controller interface. func (ctrl *CRIBaseRuntimeSpecController) Name() string { return "files.CRIBaseRuntimeSpecController" } // Inputs implements controller.Controller interface. func (ctrl *CRIBaseRuntimeSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *CRIBaseRuntimeSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *CRIBaseRuntimeSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if state.IsNotFoundError(err) { // wait for machine config to be available continue } return fmt.Errorf("error getting machine config: %w", err) } if cfg.Config().Machine() == nil { // wait for machine config to be available continue } platform := platforms.DefaultString() defaultSpec, err := oci.GenerateSpecWithPlatform( namespaces.WithNamespace(ctx, constants.K8sContainerdNamespace), nil, platform, &containers.Container{}, ) if err != nil { return fmt.Errorf("error generating default spec: %w", err) } // compatibility with CRI defaults: // * remove default rlimits (See https://github.com/containerd/cri/issues/515) defaultSpec.Process.Rlimits = nil if len(cfg.Config().Machine().BaseRuntimeSpecOverrides()) > 0 { var overrides oci.Spec jsonOverrides, err := json.Marshal(cfg.Config().Machine().BaseRuntimeSpecOverrides()) if err != nil { return fmt.Errorf("error marshaling runtime spec overrides: %w", err) } if err := json.Unmarshal(jsonOverrides, &overrides); err != nil { return fmt.Errorf("error unmarshaling runtime spec overrides: %w", err) } if err := merge.Merge(defaultSpec, &overrides); err != nil { return fmt.Errorf("error merging runtime spec overrides: %w", err) } } contents, err := json.Marshal(defaultSpec) if err != nil { return fmt.Errorf("error marshaling runtime spec: %w", err) } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, constants.CRIBaseRuntimeSpec), func(r *files.EtcFileSpec) error { spec := r.TypedSpec() spec.Contents = contents spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/files/cri_base_runtime_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files_test import ( "encoding/json" "testing" "time" "github.com/containerd/containerd/v2/pkg/oci" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" filesctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) type CRIBaseRuntimeSpecSuite struct { ctest.DefaultSuite } func (suite *CRIBaseRuntimeSpecSuite) TestDefaults() { cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, constants.CRIBaseRuntimeSpec, func(etcFile *files.EtcFileSpec, asrt *assert.Assertions) { contents := etcFile.TypedSpec().Contents var ociSpec oci.Spec asrt.NoError(json.Unmarshal(contents, &ociSpec)) asrt.Empty(ociSpec.Process.Rlimits) }) } func (suite *CRIBaseRuntimeSpecSuite) TestOverrides() { cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineBaseRuntimeSpecOverrides: v1alpha1.Unstructured{ Object: map[string]any{ "process": map[string]any{ "rlimits": []map[string]any{ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024, }, }, }, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertResource(suite, constants.CRIBaseRuntimeSpec, func(etcFile *files.EtcFileSpec, asrt *assert.Assertions) { contents := etcFile.TypedSpec().Contents var ociSpec oci.Spec asrt.NoError(json.Unmarshal(contents, &ociSpec)) asrt.NotEmpty(ociSpec.Process.Rlimits) asrt.Equal("RLIMIT_NOFILE", ociSpec.Process.Rlimits[0].Type) asrt.Equal(uint64(1024), ociSpec.Process.Rlimits[0].Hard) asrt.Equal(uint64(1024), ociSpec.Process.Rlimits[0].Soft) }) } func TestCRIBaseRuntimeSpecSuite(t *testing.T) { t.Parallel() suite.Run(t, &CRIBaseRuntimeSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&filesctrl.CRIBaseRuntimeSpecController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/files/cri_config_parts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "context" "encoding/hex" "fmt" "path/filepath" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/toml" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) // CRIConfigPartsController merges parts of the CRI config from /etc/cri/conf.d/*.part into final /etc/cri/conf.d/cri.toml. type CRIConfigPartsController struct { // Path to /etc/cri/conf.d directory. CRIConfdPath string } // Name implements controller.Controller interface. func (ctrl *CRIConfigPartsController) Name() string { return "files.CRIConfigPartsController" } // Inputs implements controller.Controller interface. func (ctrl *CRIConfigPartsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: files.NamespaceName, Type: files.EtcFileStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *CRIConfigPartsController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *CRIConfigPartsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.CRIConfdPath == "" { ctrl.CRIConfdPath = constants.EtcCRIConfdPath } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // scan conf.d directory for config parts and merge them together into final configuration parts, err := filepath.Glob(filepath.Join(ctrl.CRIConfdPath, "*.part")) if err != nil { return err } slices.Sort(parts) out, checksums, err := toml.Merge(parts) if err != nil { return err } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, constants.CRIConfig), func(r *files.EtcFileSpec) error { for _, key := range r.Metadata().Annotations().Raw() { r.Metadata().Annotations().Delete(key) } for path, checksum := range checksums { r.Metadata().Annotations().Set(files.SourceFileAnnotation+":"+path, hex.EncodeToString(checksum)) } spec := r.TypedSpec() spec.Contents = out spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/files/cri_registry_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "bytes" "context" "fmt" "io/fs" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/containers/cri/containerd" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/xfs" ) // CRIRegistryConfigController generates parts of the CRI config for registry configuration. type CRIRegistryConfigController struct { // Path to /etc directory, read-only filesystem. EtcPath string // EtcRoot is the root for /etc filesystem operations. EtcRoot xfs.Root bindMountCreated bool } // Name implements controller.Controller interface. func (ctrl *CRIRegistryConfigController) Name() string { return "files.CRIRegistryConfigController" } // Inputs implements controller.Controller interface. func (ctrl *CRIRegistryConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cri.NamespaceName, Type: cri.RegistriesConfigType, ID: optional.Some(cri.RegistriesConfigID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *CRIRegistryConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *CRIRegistryConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { src := filepath.Join(constants.CRIConfdPath, "hosts") dest := filepath.Join(ctrl.EtcPath, src) if !ctrl.bindMountCreated { if ctrl.EtcRoot.FSType() == "os" { shadowPath := filepath.Join(ctrl.EtcRoot.Source(), src) if err := createBindMountDir(shadowPath, dest); err != nil { return fmt.Errorf("bind mount failed for %q -> %q: %w", shadowPath, dest, err) } } else { if err := createBindMountDirFd(ctrl.EtcRoot, src, dest); err != nil { return fmt.Errorf("bind mount failed for %q: %w", dest, err) } } ctrl.bindMountCreated = true } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*cri.RegistriesConfig](ctx, r, cri.RegistriesConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting registries config: %w", err) } var ( criRegistryContents []byte criHosts *containerd.HostsConfig ) if cfg != nil { criRegistryContents, err = containerd.GenerateCRIConfig(cfg.TypedSpec()) if err != nil { return err } criHosts, err = containerd.GenerateHosts(cfg.TypedSpec(), dest) if err != nil { return err } } else { criHosts = &containerd.HostsConfig{} } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, constants.CRIRegistryConfigPart), func(r *files.EtcFileSpec) error { spec := r.TypedSpec() spec.Contents = criRegistryContents spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } if err := ctrl.syncHosts(src, criHosts, logger); err != nil { return fmt.Errorf("error syncing hosts: %w", err) } r.ResetRestartBackoff() } } //nolint:gocyclo func (ctrl *CRIRegistryConfigController) syncHosts(basePath string, criHosts *containerd.HostsConfig, _ *zap.Logger) error { // 1. create/update all files and directories for dirName, directory := range criHosts.Directories { path := filepath.Join(basePath, dirName) if err := xfs.MkdirAll(ctrl.EtcRoot, path, 0o700); err != nil { return err } for _, file := range directory.Files { // match contents to see if the update can be skipped contents, err := xfs.ReadFile(ctrl.EtcRoot, filepath.Join(path, file.Name)) if err == nil && bytes.Equal(contents, file.Contents) { continue } // write file if err = xfs.WriteFile( ctrl.EtcRoot, filepath.Join(path, file.Name), file.Contents, file.Mode, ); err != nil { return err } } // remove any files which shouldn't be present fileList, err := xfs.ReadDir(ctrl.EtcRoot, path) if err != nil { return err } fileListMap := xslices.ToSetFunc(fileList, fs.DirEntry.Name) for _, file := range directory.Files { delete(fileListMap, file.Name) } for file := range fileListMap { if err = xfs.Remove(ctrl.EtcRoot, filepath.Join(path, file)); err != nil { return err } } } // 2. remove any directories which shouldn't be present directoryList, err := xfs.ReadDir(ctrl.EtcRoot, basePath) if err != nil { return err } directoryListMap := make(map[string]struct{}, len(directoryList)) for _, dir := range directoryList { directoryListMap[dir.Name()] = struct{}{} } for dirName := range criHosts.Directories { delete(directoryListMap, dirName) } for dirName := range directoryListMap { if err = xfs.RemoveAll(ctrl.EtcRoot, filepath.Join(basePath, dirName)); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/files/etcfile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "bytes" "context" "errors" "fmt" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/xfs" ) // EtcFileController watches EtcFileSpecs, creates/updates files. type EtcFileController struct { // Path to /etc directory, read-only filesystem. EtcPath string // EtcRoot is the root for /etc filesystem operations. EtcRoot xfs.Root // Cache of bind mounts created. bindMounts map[string]any } // Name implements controller.Controller interface. func (ctrl *EtcFileController) Name() string { return "files.EtcFileController" } // Inputs implements controller.Controller interface. func (ctrl *EtcFileController) Inputs() []controller.Input { return []controller.Input{ { Namespace: files.NamespaceName, Type: files.EtcFileSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *EtcFileController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.bindMounts == nil { ctrl.bindMounts = make(map[string]any) } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } list, err := safe.ReaderList[*files.EtcFileSpec](ctx, r, resource.NewMetadata(files.NamespaceName, files.EtcFileSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing specs: %w", err) } // add finalizers for all live resources for res := range list.All() { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } touchedIDs := make(map[resource.ID]struct{}) for spec := range list.All() { filename := spec.Metadata().ID() _, mountExists := ctrl.bindMounts[filename] dst := filepath.Join(ctrl.EtcPath, filename) src := filename switch spec.Metadata().Phase() { case resource.PhaseTearingDown: if mountExists { logger.Debug("removing bind mount", zap.String("src", src), zap.String("dst", dst)) if err = unix.Unmount(dst, 0); err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("failed to unmount bind mount %q: %w", dst, err) } delete(ctrl.bindMounts, filename) } logger.Debug("removing file", zap.String("src", src)) if err = xfs.Remove(ctrl.EtcRoot, src); err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("failed to remove %q: %w", src, err) } // now remove finalizer as the link was deleted if err = r.RemoveFinalizer(ctx, spec.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: if !mountExists { logger.Debug("creating bind mount", zap.String("src", src), zap.String("dst", dst)) if ctrl.EtcRoot.FSType() == "os" { shadow := filepath.Join(ctrl.EtcRoot.Source(), src) if err = createBindMountFile(shadow, dst, spec.TypedSpec().Mode); err != nil { return fmt.Errorf("failed to create shadow bind mount %q -> %q (mode: os): %w", shadow, dst, err) } } else { if err = createBindMountFileFd(ctrl.EtcRoot, src, dst, spec.TypedSpec().Mode); err != nil { return fmt.Errorf("failed to create shadow bind mount %q -> %q (mode: fd): %w", src, dst, err) } } ctrl.bindMounts[filename] = struct{}{} } logger.Debug("writing file contents", zap.String("src", src), zap.Stringer("version", spec.Metadata().Version())) if err = UpdateFile(ctrl.EtcRoot, src, spec.TypedSpec().Contents, spec.TypedSpec().Mode, spec.TypedSpec().SelinuxLabel); err != nil { return fmt.Errorf("error updating %q: %w", src, err) } if err = safe.WriterModify(ctx, r, files.NewEtcFileStatus(files.NamespaceName, filename), func(r *files.EtcFileStatus) error { r.TypedSpec().SpecVersion = spec.Metadata().Version().String() return nil }); err != nil { return fmt.Errorf("error updating status: %w", err) } touchedIDs[filename] = struct{}{} } } // list statuses for cleanup statuses, err := safe.ReaderList[*files.EtcFileStatus](ctx, r, resource.NewMetadata(files.NamespaceName, files.EtcFileStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range statuses.All() { if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } // createBindMountFile creates a common way to create a writable source file with a // bind mounted destination. This is most commonly used for well known files // under /etc that need to be adjusted during startup. func createBindMountFile(src, dst string, mode os.FileMode) (err error) { if err = os.MkdirAll(filepath.Dir(src), 0o755); err != nil { return fmt.Errorf("mkdir all failed: %w", err) } var f *os.File if f, err = os.OpenFile(src, os.O_WRONLY|os.O_CREATE, mode); err != nil { return fmt.Errorf("open file failed: %w", err) } if err = f.Close(); err != nil { return fmt.Errorf("close file failed: %w", err) } return mount.BindReadonly(src, dst) } // createBindMountDir creates a common way to create a writable source dir with a // bind mounted destination. This is most commonly used for well known directories // under /etc that need to be adjusted during startup. func createBindMountDir(src, dst string) (err error) { if err = os.MkdirAll(src, 0o755); err != nil { return err } return mount.BindReadonly(src, dst) } // createBindMountFileFd creates a common way to create a writable source file with a // bind mounted destination. This is most commonly used for well known files // under /etc that need to be adjusted during startup. func createBindMountFileFd(root xfs.Root, src, dst string, mode os.FileMode) (err error) { if err = xfs.MkdirAll(root, filepath.Dir(src), 0o755); err != nil { return err } fsrc, err := xfs.OpenFile(root, src, os.O_WRONLY|os.O_CREATE, mode) if err != nil { return err } defer fsrc.Close() //nolint:errcheck return mount.BindReadonlyFd(int(fsrc.Fd()), dst) } // createBindMountDirFd creates a common way to create a writable source dir with a // bind mounted destination. This is most commonly used for well known directories // under /etc that need to be adjusted during startup. func createBindMountDirFd(root xfs.Root, src, dst string) (err error) { if err = xfs.MkdirAll(root, src, 0o755); err != nil { return fmt.Errorf("mkdir all failed: %w", err) } f, err := xfs.OpenFile(root, src, os.O_RDONLY, 0) if err != nil { return fmt.Errorf("open file failed: %w", err) } defer f.Close() //nolint:errcheck return mount.BindReadonlyFd(int(f.Fd()), dst) } // UpdateFile is like `os.WriteFile`, but it will only update the file if the // contents have changed. func UpdateFile(root xfs.Root, filename string, contents []byte, mode os.FileMode, selinuxLabel string) error { oldContents, err := xfs.ReadFile(root, filename) if err == nil && bytes.Equal(oldContents, contents) { return selinux.FSetLabel(root, filename, selinuxLabel) } if err = xfs.MkdirAll(root, filepath.Dir(filename), 0o755); err != nil { return fmt.Errorf("mkdir all failed: %w", err) } if err := xfs.WriteFile(root, filename, contents, mode); err != nil { return fmt.Errorf("write file failed: %w", err) } return selinux.FSetLabel(root, filename, selinuxLabel) } ================================================ FILE: internal/app/machined/pkg/controllers/files/etcfile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files_test import ( "context" "os" "path/filepath" "strconv" "sync" "testing" "time" osruntime "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" filesctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/opentree" ) type EtcFileSuite struct { suite.Suite state state.State runtime *osruntime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc etcPath string etcRoot xfs.Root } func (suite *EtcFileSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = osruntime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.startRuntime() ok, err := runtime.KernelCapabilities().OpentreeOnAnonymousFS() suite.Require().NoError(err) suite.etcPath = suite.T().TempDir() if ok { suite.etcRoot = &xfs.UnixRoot{FS: opentree.NewFromPath(suite.T().TempDir())} } else { suite.etcRoot = &xfs.OSRoot{Shadow: suite.T().TempDir()} } suite.Require().NoError(suite.etcRoot.OpenFS()) suite.Require().NoError( suite.runtime.RegisterController( &filesctrl.EtcFileController{ EtcPath: suite.etcPath, EtcRoot: suite.etcRoot, }, ), ) } func (suite *EtcFileSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *EtcFileSuite) assertFileContents(root xfs.Root, filename, contents string) error { rwb, err := xfs.ReadFile(root, filename) if err != nil { return retry.ExpectedError(err) } if string(rwb) != contents { return retry.ExpectedErrorf("contents for RW file %q don't match %q != %q", filename, string(rwb), contents) } rob, err := os.ReadFile(filepath.Join(suite.etcPath, filename)) if err != nil { return retry.ExpectedError(err) } if string(rob) != contents { return retry.ExpectedErrorf("contents for RO file %q don't match %q != %q", filepath.Join(suite.etcPath, filename), string(rob), contents) } return nil } func (suite *EtcFileSuite) assertEtcFile(filename, contents string, expectedVersion resource.Version) error { if err := suite.assertFileContents(suite.etcRoot, filename, contents); err != nil { return err // Already wrapped in the retry.ExpectedError } r, err := safe.ReaderGet[*files.EtcFileStatus](suite.ctx, suite.state, resource.NewMetadata(files.NamespaceName, files.EtcFileStatusType, filename, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } version := r.TypedSpec().SpecVersion expected, err := strconv.Atoi(expectedVersion.String()) suite.Require().NoError(err) ver, err := strconv.Atoi(version) suite.Require().NoError(err) if ver < expected { return retry.ExpectedErrorf("version mismatch %s > %s", expectedVersion, version) } return nil } func (suite *EtcFileSuite) TestFiles() { etcFileSpec := files.NewEtcFileSpec(files.NamespaceName, "test1") etcFileSpec.TypedSpec().Contents = []byte("foo") etcFileSpec.TypedSpec().Mode = 0o644 // create "read-only" mock (in Talos it's part of rootfs) suite.T().Logf("mock created %q", filepath.Join(suite.etcPath, etcFileSpec.Metadata().ID())) suite.Require().NoError(os.WriteFile(filepath.Join(suite.etcPath, etcFileSpec.Metadata().ID()), nil, 0o644)) suite.Require().NoError(suite.state.Create(suite.ctx, etcFileSpec)) suite.Assert().NoError( retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertEtcFile("test1", "foo", etcFileSpec.Metadata().Version()) }, ), ) for _, r := range []resource.Resource{etcFileSpec} { for { ready, err := suite.state.Teardown(suite.ctx, r.Metadata()) suite.Require().NoError(err) if ready { break } time.Sleep(100 * time.Millisecond) } } } func (suite *EtcFileSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() suite.etcRoot.Close() //nolint:errcheck } func TestEtcFileSuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("requires root") } suite.Run(t, new(EtcFileSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/files/files.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package files provides controllers which manage file resources. package files ================================================ FILE: internal/app/machined/pkg/controllers/files/iqn.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) // IQNController creates an EtcFileSpec for the iSCSI Qualified Name (IQN) file. type IQNController struct { V1Alpha1Mode runtimetalos.Mode } // Name implements controller.Controller interface. func (ctrl *IQNController) Name() string { return "files.IQNController" } // Inputs implements controller.Controller interface. func (ctrl *IQNController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *IQNController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *IQNController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // Skip the controller if we're running in a container. if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // get the local node identity localIdentity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get machine-id etcfile status: %w", err) } machineID, err := clusteradapter.IdentitySpec(localIdentity.TypedSpec()).ConvertMachineID() if err != nil { return fmt.Errorf("failed to convert identity to machine ID: %w", err) } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "iscsi/initiatorname.iscsi"), func(r *files.EtcFileSpec) error { spec := r.TypedSpec() // Fri Nov 3 16:19:12 2017 -0700 is the date of the first commit in the talos repository. spec.Contents = fmt.Appendf([]byte{}, "InitiatorName=iqn.2017-11.dev.talos:%s\n", machineID) spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/files/nqn.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/google/uuid" "github.com/siderolabs/gen/optional" "go.uber.org/zap" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) // NQNController creates an EtcFileSpec for the NVMe Qualified Name (NQN) and HostID file. type NQNController struct { V1Alpha1Mode runtimetalos.Mode } // Name implements controller.Controller interface. func (ctrl *NQNController) Name() string { return "files.NQNController" } // Inputs implements controller.Controller interface. func (ctrl *NQNController) Inputs() []controller.Input { return []controller.Input{ { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NQNController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NQNController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // Skip the controller if we're running in a container. if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // get the local node identity localIdentity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get machine-id etcfile status: %w", err) } machineID, err := clusteradapter.IdentitySpec(localIdentity.TypedSpec()).ConvertMachineID() if err != nil { return fmt.Errorf("failed to convert identity to machine ID: %w", err) } hostID, err := uuid.FromBytes(machineID[:16]) if err != nil { return fmt.Errorf("failed to convert machine-id to UUID: %w", err) } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "nvme/hostid"), func(r *files.EtcFileSpec) error { spec := r.TypedSpec() spec.Contents = []byte(hostID.String()) spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } if err := safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "nvme/hostnqn"), func(r *files.EtcFileSpec) error { spec := r.TypedSpec() // Fri Nov 3 16:19:12 2017 -0700 is the date of the first commit in the talos repository. spec.Contents = fmt.Appendf([]byte{}, "nqn.2017-11.dev.talos:uuid:%s", hostID.String()) spec.Mode = 0o600 spec.SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/hardware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package hardware provides the hardware controller implementation. package hardware ================================================ FILE: internal/app/machined/pkg/controllers/hardware/hardware_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware_test import ( "context" "sync" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" ) type HardwareSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *HardwareSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) } func (suite *HardwareSuite) assertNoResource(md resource.Metadata) func() error { return func() error { _, err := suite.state.Get(suite.ctx, md) if err == nil { return retry.ExpectedErrorf("resource %s still exists", md) } if state.IsNotFoundError(err) { return nil } return err } } func (suite *HardwareSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func (suite *HardwareSuite) State() state.State { return suite.state } func (suite *HardwareSuite) Ctx() context.Context { return suite.ctx } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/pci_driver_rebind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) const ( targetDeviceSYSFSPath = "/sys/bus/pci/devices/%s" driverOverridePath = targetDeviceSYSFSPath + "/driver_override" driverUnbindPath = targetDeviceSYSFSPath + "/driver/unbind" driverPath = targetDeviceSYSFSPath + "/driver" driverProbePath = "/sys/bus/pci/drivers_probe" ) // PCIDriverRebindController binds PCI devices to a specific driver and unbinds them from the host driver. type PCIDriverRebindController struct { V1Alpha1Mode v1alpha1runtime.Mode boundDevices map[string]struct{} } // Name implements controller.Controller interface. func (c *PCIDriverRebindController) Name() string { return "hardware.PCIDriverRebindController" } // Inputs implements controller.Controller interface. func (c *PCIDriverRebindController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (c *PCIDriverRebindController) Outputs() []controller.Output { return []controller.Output{ { Type: hardware.PCIDriverRebindStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (c *PCIDriverRebindController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) (err error) { // Skip PCI rebind handling if running in a container or agent mode. if c.V1Alpha1Mode.InContainer() || c.V1Alpha1Mode.IsAgent() { return nil } if c.boundDevices == nil { c.boundDevices = map[string]struct{}{} } // wait for udevd to be healthy, this is to ensure that host drivers if any are loaded. if err := runtimectrl.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: hardware.NamespaceName, Type: hardware.PCIDriverRebindConfigType, Kind: controller.InputWeak, }, }); err != nil { return fmt.Errorf("error waiting for devices to be ready: %w", err) } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } pciDriverRebindConfigs, err := safe.ReaderListAll[*hardware.PCIDriverRebindConfig](ctx, r) if err != nil { return fmt.Errorf("error listing all PCI rebind configs: %w", err) } r.StartTrackingOutputs() touchedIDs := map[string]struct{}{} for cfg := range pciDriverRebindConfigs.All() { if err := c.handlePCIDriverReBind(cfg.TypedSpec().PCIID, cfg.TypedSpec().TargetDriver); err != nil { return err } boundDriver, err := checkDeviceBoundDriver(cfg.TypedSpec().PCIID) if err != nil { return fmt.Errorf("error checking bound driver for device with id: %s, %w", cfg.TypedSpec().PCIID, err) } if boundDriver != cfg.TypedSpec().TargetDriver { logger.Info( "cannot validate if device is bound to target driver, ensure target driver module is loaded", zap.String("id", cfg.TypedSpec().PCIID), zap.String("targetDriver", cfg.TypedSpec().TargetDriver), ) } logger.Info("PCI device bound to target driver", zap.String("id", cfg.TypedSpec().PCIID), zap.String("targetDriver", cfg.TypedSpec().TargetDriver)) if err := safe.WriterModify[*hardware.PCIDriverRebindStatus](ctx, r, hardware.NewPCIDriverRebindStatus(cfg.TypedSpec().PCIID), func(res *hardware.PCIDriverRebindStatus) error { res.TypedSpec().PCIID = cfg.TypedSpec().PCIID res.TypedSpec().TargetDriver = cfg.TypedSpec().TargetDriver return nil }); err != nil { return fmt.Errorf("error updating PCI rebind status: %w", err) } touchedIDs[cfg.TypedSpec().PCIID] = struct{}{} c.boundDevices[cfg.TypedSpec().PCIID] = struct{}{} } // cleanup any PCI devices that were not touched in the current run. for pciID := range c.boundDevices { if _, ok := touchedIDs[pciID]; !ok { // writing a newline to driver_override file will set the device to default driver based on pci device id. if err := c.handlePCIDriverReBind(pciID, "\n"); err != nil { return err } logger.Info("PCI device set to default", zap.String("id", pciID)) } } if err := safe.CleanupOutputs[*hardware.PCIDriverRebindStatus](ctx, r); err != nil { return err } } } // handlePCIBindToTarget binds PCI device to a target driver and unbinds it from the host driver. func (c *PCIDriverRebindController) handlePCIDriverReBind(pciID, targetDriver string) error { if err := os.WriteFile(fmt.Sprintf(driverOverridePath, pciID), []byte(targetDriver), 0o200); err != nil { return fmt.Errorf("error writing driver override for device with id: %s, target driver: %s, %w", pciID, targetDriver, err) } // Unbind device from the host driver. // in some cases, the device may not be bound to any driver, so we ignore the error. if err := os.WriteFile(fmt.Sprintf(driverUnbindPath, pciID), []byte(pciID), 0o200); err != nil && !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error unbinding device with id: %s, %w", pciID, err) } if err := os.WriteFile(driverProbePath, []byte(pciID), 0o200); err != nil { return fmt.Errorf("error probing driver for device with id: %s, %w", pciID, err) } return nil } // checkDeviceBoundDriver checks if the device is bound to a driver or not bound at all. func checkDeviceBoundDriver(pciID string) (string, error) { driverPath := fmt.Sprintf(driverPath, pciID) driver, err := os.Readlink(driverPath) if err == nil { return filepath.Base(driver), nil } return "", fmt.Errorf("error reading path: %s, %w", driverPath, err) } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/pci_driver_rebind_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // PCIDriverRebindConfigController generates configuration for PCI rebind. type PCIDriverRebindConfigController struct{} // Name implements controller.Controller interface. func (ctrl *PCIDriverRebindConfigController) Name() string { return "hardware.PCIDriverRebindConfigController" } // Inputs implements controller.Controller interface. func (ctrl *PCIDriverRebindConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PCIDriverRebindConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: hardware.PCIDriverRebindConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *PCIDriverRebindConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) (err error) { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } r.StartTrackingOutputs() if cfg != nil { for _, pciDriverRebindConfig := range cfg.Config().PCIDriverRebindConfig().PCIDriverRebindConfigs() { if err := safe.WriterModify(ctx, r, hardware.NewPCIDriverRebindConfig(pciDriverRebindConfig.PCIID()), func(res *hardware.PCIDriverRebindConfig) error { res.TypedSpec().PCIID = pciDriverRebindConfig.PCIID() res.TypedSpec().TargetDriver = pciDriverRebindConfig.TargetDriver() return nil }); err != nil { return fmt.Errorf("error updating PCI rebind config: %w", err) } } } if err = safe.CleanupOutputs[*hardware.PCIDriverRebindConfig](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/pci_driver_rebind_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/hardware" "github.com/siderolabs/talos/pkg/machinery/config/container" hardwareconfigtype "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/config" hardwareres "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) type PCIDriverRebindConfigSuite struct { ctest.DefaultSuite } func TestPCIDriverRebindConfigSuite(t *testing.T) { suite.Run(t, new(PCIDriverRebindConfigSuite)) } func (suite *PCIDriverRebindConfigSuite) TestPCIDriverRebindConfig() { suite.Require().NoError(suite.Runtime().RegisterController(&hardware.PCIDriverRebindConfigController{})) pciDriverRebindConfig := &hardwareconfigtype.PCIDriverRebindConfigV1Alpha1{ MetaName: "0000:04:00.00", PCITargetDriver: "vfio-pci", } cfg, err := container.New(pciDriverRebindConfig) suite.Require().NoError(err) nCfg := config.NewMachineConfig(cfg) suite.Require().NoError(suite.State().Create(suite.Ctx(), nCfg)) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), pciDriverRebindConfig.MetaName, func(cfg *hardwareres.PCIDriverRebindConfig, asrt *assert.Assertions) { asrt.Equal( "0000:04:00.00", cfg.TypedSpec().PCIID, ) asrt.Equal( "vfio-pci", cfg.TypedSpec().TargetDriver, ) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), nCfg.Metadata())) rtestutils.AssertNoResource[*hardwareres.PCIDriverRebindConfig](suite.Ctx(), suite.T(), suite.State(), pciDriverRebindConfig.MetaName) } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/pcidevices.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "bytes" "context" "errors" "fmt" "io/fs" "os" "path/filepath" "strconv" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pcidb/pkg/pcidb" "go.uber.org/zap" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // PCIDevicesController populates PCI device information. type PCIDevicesController struct { V1Alpha1Mode runtimetalos.Mode } // Name implements controller.Controller interface. func (ctrl *PCIDevicesController) Name() string { return "hardware.PCIDevicesController" } // Inputs implements controller.Controller interface. func (ctrl *PCIDevicesController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("udevd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PCIDevicesController) Outputs() []controller.Output { return []controller.Output{ { Type: hardware.PCIDeviceType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *PCIDevicesController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // PCI device info doesn't make sense inside a container, so skip the controller if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer { return nil } // [TODO]: a single run for now, need to figure out how to trigger rescan for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // we need to wait for udevd to be healthy & running so that we get the driver information too udevdService, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "udevd") if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get udevd service: %w", err) } if udevdService.TypedSpec().Healthy && udevdService.TypedSpec().Running { break } } deviceIDs, err := os.ReadDir("/sys/bus/pci/devices") if err != nil { return fmt.Errorf("error scanning devices: %w", err) } logger.Debug("found PCI devices", zap.Int("count", len(deviceIDs))) r.StartTrackingOutputs() for _, deviceID := range deviceIDs { class, err := readHexPCIInfo(deviceID.Name(), "class") if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return fmt.Errorf("error parsing device %s class: %w", deviceID.Name(), err) } vendor, err := readHexPCIInfo(deviceID.Name(), "vendor") if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return fmt.Errorf("error parsing device %s vendor: %w", deviceID.Name(), err) } product, err := readHexPCIInfo(deviceID.Name(), "device") if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return fmt.Errorf("error parsing device %s product: %w", deviceID.Name(), err) } driver, err := readDriverInfo(deviceID.Name()) if err != nil { return fmt.Errorf("error parsing device %s driver: %w", deviceID.Name(), err) } logger.Debug("found PCI device", zap.String("deviceID", deviceID.Name()), zap.String("driver", driver)) classID := pcidb.Class((class >> 16) & 0xff) subclassID := pcidb.Subclass((class >> 8) & 0xff) vendorID := pcidb.Vendor(vendor) productID := pcidb.Product(product) if err := safe.WriterModify(ctx, r, hardware.NewPCIDeviceInfo(deviceID.Name()), func(r *hardware.PCIDevice) error { r.TypedSpec().ClassID = fmt.Sprintf("0x%02x", classID) r.TypedSpec().SubclassID = fmt.Sprintf("0x%02x", subclassID) r.TypedSpec().VendorID = fmt.Sprintf("0x%04x", vendorID) r.TypedSpec().ProductID = fmt.Sprintf("0x%04x", productID) r.TypedSpec().Class, _ = pcidb.LookupClass(classID) r.TypedSpec().Subclass, _ = pcidb.LookupSubclass(classID, subclassID) r.TypedSpec().Vendor, _ = pcidb.LookupVendor(vendorID) r.TypedSpec().Product, _ = pcidb.LookupProduct(vendorID, productID) r.TypedSpec().Driver = driver return nil }); err != nil { return fmt.Errorf("error modifying output resource: %w", err) } } if err = safe.CleanupOutputs[*hardware.PCIDevice](ctx, r); err != nil { return err } return nil } func readHexPCIInfo(deviceID, info string) (uint64, error) { contents, err := os.ReadFile(filepath.Join("/sys/bus/pci/devices", deviceID, info)) if err != nil { return 0, err } return strconv.ParseUint(string(bytes.TrimSpace(contents)), 0, 64) } func readDriverInfo(deviceID string) (string, error) { link, err := os.Readlink(filepath.Join("/sys/bus/pci/devices", deviceID, "driver")) if err != nil { // ignore if the driver doesn't exist // this can happen if the device is not bound to a driver or a pci root port if errors.Is(err, fs.ErrNotExist) { return "", nil } return "", err } return filepath.Base(link), nil } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/pcr_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "fmt" "maps" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/secureboot" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // PCRStatusController manages TPM PCR extension. type PCRStatusController struct { V1Alpha1Mode runtimetalos.Mode numberOfExtensions int } // Name implements controller.Controller interface. func (ctrl *PCRStatusController) Name() string { return "hardware.PCRStatusController" } // Inputs implements controller.Controller interface. func (ctrl *PCRStatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PCRStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: hardware.PCRStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *PCRStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // PCR status doesn't make sense inside a container, so skip the controller if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } switch ctrl.numberOfExtensions { case 0: // extend the PCR for the first time // this unlock initial PCR extension if err := tpm2.PCRExtend(constants.UKIPCR, []byte(secureboot.EnterMachined)); err != nil { return fmt.Errorf("error performing initial PCR extension: %w", err) } if err := r.Create(ctx, hardware.NewPCCRStatus(constants.UKIPCR)); err != nil { return fmt.Errorf("error creating PCRStatus resource: %w", err) } logger.Info("TPM is ready for disk encryption operations (if available)") ctrl.numberOfExtensions++ case 1: // as long as Volumes were provisioned, we extend the PCR once again locking further access to the TPM volumeStatuses, err := safe.ReaderListAll[*block.VolumeStatus](ctx, r) if err != nil { return fmt.Errorf("error listing volume statuses: %w", err) } volumesReady := map[string]struct{}{} volumesPending := map[string]struct{}{} for volumeStatus := range volumeStatuses.All() { switch volumeStatus.TypedSpec().Type { case block.VolumeTypeDisk, block.VolumeTypePartition: // can be encrypted case block.VolumeTypeDirectory, block.VolumeTypeOverlay, block.VolumeTypeSymlink, block.VolumeTypeTmpfs, block.VolumeTypeExternal: // skip it, not encryptable continue } switch volumeStatus.TypedSpec().Phase { case block.VolumePhaseMissing: // skip it, missing case block.VolumePhaseReady: volumesReady[volumeStatus.Metadata().ID()] = struct{}{} case block.VolumePhaseClosed: // skip it, closed case block.VolumePhaseLocated, block.VolumePhaseWaiting, block.VolumePhaseFailed, block.VolumePhaseProvisioned, block.VolumePhasePrepared: volumesPending[volumeStatus.Metadata().ID()] = struct{}{} } } notReady := false for _, requiredVolumeID := range []string{constants.StatePartitionLabel, constants.EphemeralPartitionLabel} { if _, ready := volumesReady[requiredVolumeID]; !ready { logger.Debug("skipping PCR extension, volume not ready", zap.String("volume", requiredVolumeID)) notReady = true break } } if notReady { continue } if len(volumesPending) > 0 { pendingVolumes := slices.Sorted(maps.Keys(volumesPending)) logger.Debug("skipping PCR extension, volumes not ready", zap.Strings("volumes", pendingVolumes)) continue } // ready to extend readyToDestroy, err := r.Teardown(ctx, hardware.NewPCCRStatus(constants.UKIPCR).Metadata()) if err != nil { return fmt.Errorf("error tearing down PCRStatus resource: %w", err) } if !readyToDestroy { continue } if err = r.Destroy(ctx, hardware.NewPCCRStatus(constants.UKIPCR).Metadata()); err != nil { return fmt.Errorf("error destroying PCRStatus resource: %w", err) } if err := tpm2.PCRExtend(constants.UKIPCR, []byte(secureboot.StartTheWorld)); err != nil { return fmt.Errorf("error performing PCR extension: %w", err) } logger.Info("TPM is locked to block any disk encryption operation (if available)") ctrl.numberOfExtensions++ case 2: // nothing to do, we are done } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/system.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/prometheus/procfs" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-smbios/smbios" "go.uber.org/zap" hwadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/hardware" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" pkgSMBIOS "github.com/siderolabs/talos/internal/pkg/smbios" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // SystemInfoController populates CPU information of the underlying hardware. type SystemInfoController struct { V1Alpha1Mode runtimetalos.Mode SMBIOS *smbios.SMBIOS } // Name implements controller.Controller interface. func (ctrl *SystemInfoController) Name() string { return "hardware.SystemInfoController" } // Inputs implements controller.Controller interface. func (ctrl *SystemInfoController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MetaKeyType, Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MetaLoadedType, ID: optional.Some(runtime.MetaLoadedID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SystemInfoController) Outputs() []controller.Output { return []controller.Output{ { Type: hardware.ProcessorType, Kind: controller.OutputExclusive, }, { Type: hardware.MemoryModuleType, Kind: controller.OutputExclusive, }, { Type: hardware.SystemInformationType, Kind: controller.OutputExclusive, }, } } const memoryModuleUnknown = "UNKNOWN" // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *SystemInfoController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // smbios info is not available inside container, so skip the controller if ctrl.V1Alpha1Mode == runtimetalos.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } _, err := safe.ReaderGetByID[*runtime.MetaLoaded](ctx, r, runtime.MetaLoadedID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting meta loaded resource: %w", err) } if ctrl.SMBIOS == nil { var s *smbios.SMBIOS s, err = pkgSMBIOS.GetSMBIOSInfo() if err != nil { return err } ctrl.SMBIOS = s } r.StartTrackingOutputs() if err := ctrl.reconcileSystemInformation(ctx, r, logger); err != nil { return err } if err := ctrl.reconcileProcessors(ctx, r); err != nil { return err } if err := ctrl.reconcileMemoryModules(ctx, r, logger); err != nil { return err } if err := r.CleanupOutputs(ctx, resource.NewMetadata(hardware.NamespaceName, hardware.SystemInformationType, hardware.SystemInformationID, resource.VersionUndefined), resource.NewMetadata(hardware.NamespaceName, hardware.ProcessorType, "", resource.VersionUndefined), resource.NewMetadata(hardware.NamespaceName, hardware.MemoryModuleType, "", resource.VersionUndefined), ); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } func (ctrl *SystemInfoController) reconcileSystemInformation(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { uuidRewriteRes, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.UUIDOverride)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting meta key resource: %w", err) } var uuidRewrite string if uuidRewriteRes != nil && uuidRewriteRes.TypedSpec().Value != "" { uuidRewrite = uuidRewriteRes.TypedSpec().Value logger.Info("using UUID rewrite", zap.String("uuid", uuidRewrite)) } if err := safe.WriterModify(ctx, r, hardware.NewSystemInformation(hardware.SystemInformationID), func(res *hardware.SystemInformation) error { hwadapter.SystemInformation(res).Update(&ctrl.SMBIOS.SystemInformation, uuidRewrite) return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) } return nil } func (ctrl *SystemInfoController) reconcileProcessors(ctx context.Context, r controller.Runtime) error { for _, p := range ctrl.SMBIOS.ProcessorInformation { // replaces `CPU 0` with `CPU-0` id := strings.ReplaceAll(p.SocketDesignation, " ", "-") if err := safe.WriterModify(ctx, r, hardware.NewProcessorInfo(id), func(res *hardware.Processor) error { hwadapter.Processor(res).Update(&p) return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) } } return nil } func (ctrl *SystemInfoController) reconcileMemoryModules(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for _, m := range ctrl.SMBIOS.MemoryDevices { // replaces `SIMM 0` with `SIMM-0` id := strings.ReplaceAll(m.DeviceLocator, " ", "-") if err := safe.WriterModify(ctx, r, hardware.NewMemoryModuleInfo(id), func(res *hardware.MemoryModule) error { hwadapter.MemoryModule(res).Update(&m) return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) } } if len(ctrl.SMBIOS.MemoryDevices) == 0 { logger.Debug("no memory devices found, attempting to retrieve memory information from procfs") proc, err := procfs.NewDefaultFS() if err != nil { return err } info, err := proc.Meminfo() if err != nil { return err } if err := safe.WriterModify(ctx, r, hardware.NewMemoryModuleInfo(memoryModuleUnknown), func(res *hardware.MemoryModule) error { if info.MemTotalBytes != nil { hwadapter.MemoryModule(res).TypedSpec().Size = uint32(*info.MemTotal / 1024) } hwadapter.MemoryModule(res).TypedSpec().Manufacturer = memoryModuleUnknown return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/hardware/system_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware_test import ( "io" "os" "testing" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/go-smbios/smbios" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" hardwarectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/hardware" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type SystemInfoSuite struct { HardwareSuite } func (suite *SystemInfoSuite) TestPopulateSystemInformation() { stream, err := os.Open("testdata/SuperMicro-Dual-Xeon.dmi") suite.Require().NoError(err) suite.T().Cleanup(func() { suite.NoError(stream.Close()) }) version := smbios.Version{Major: 3, Minor: 3, Revision: 0} // dummy version s, err := smbios.Decode(stream, version) suite.Require().NoError(err) suite.Require().NoError( suite.runtime.RegisterController( &hardwarectrl.SystemInfoController{ SMBIOS: s, }, ), ) suite.startRuntime() suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded())) systemInformation := hardware.SystemInformationSpec{ Manufacturer: "Supermicro", ProductName: "SYS-1027R-WRF", Version: "0123456789", SerialNumber: "E09626824801435", UUID: "00000000-0000-0000-0000-002590eb9628", WakeUpType: "Power Switch", SKUNumber: "", } cpuSpecs := map[string]hardware.ProcessorSpec{ "CPU-1": { Socket: "CPU 1", Manufacturer: "Intel", ProductName: "Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz", MaxSpeed: 4000, BootSpeed: 2600, Status: 65, AssetTag: "3A65E8E29D76BF8D", CoreCount: 8, CoreEnabled: 8, ThreadCount: 16, }, "CPU-2": { Socket: "CPU 2", Manufacturer: "Intel", ProductName: "Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz", MaxSpeed: 4000, BootSpeed: 2600, Status: 65, CoreCount: 8, CoreEnabled: 8, ThreadCount: 16, }, } memorySpecs := map[string]hardware.MemoryModuleSpec{ "P1-DIMMA1": { Size: 4096, DeviceLocator: "P1-DIMMA1", BankLocator: "P0_Node0_Channel0_Dimm0", Speed: 1333, Manufacturer: "Micron", SerialNumber: "346C4A12", AssetTag: "Dimm0_AssetTag", ProductName: "18KSF51272PZ-1G4K", }, "P1-DIMMA2": { Size: 4096, DeviceLocator: "P1-DIMMA2", BankLocator: "P0_Node0_Channel0_Dimm1", Speed: 1333, Manufacturer: "Kingston", SerialNumber: "D2166C8B", AssetTag: "Dimm1_AssetTag", ProductName: "HP647647-071-HYE", }, } ctest.AssertResource(suite, hardware.SystemInformationID, func(r *hardware.SystemInformation, assertions *assert.Assertions) { assertions.Equal(systemInformation, *r.TypedSpec()) }) for k, v := range cpuSpecs { ctest.AssertResource(suite, k, func(r *hardware.Processor, assertions *assert.Assertions) { assertions.Equal(v, *r.TypedSpec()) }) } for k, v := range memorySpecs { ctest.AssertResource(suite, k, func(r *hardware.MemoryModule, assertions *assert.Assertions) { assertions.Equal(v, *r.TypedSpec()) }) } } func (suite *SystemInfoSuite) TestPopulateSystemInformationEmpty() { version := smbios.Version{Major: 3, Minor: 3, Revision: 0} // dummy version s, err := smbios.Decode(io.NewSectionReader(nil, 0, 0), version) suite.Require().NoError(err) suite.Require().NoError( suite.runtime.RegisterController( &hardwarectrl.SystemInfoController{ SMBIOS: s, }, ), ) suite.startRuntime() suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded())) memorySpecs := map[string]hardware.MemoryModuleSpec{ "UNKNOWN": { Manufacturer: "UNKNOWN", }, } for k, v := range memorySpecs { ctest.AssertResource(suite, k, func(r *hardware.MemoryModule, assertions *assert.Assertions) { assertions.Equal(v.DeviceLocator, r.TypedSpec().DeviceLocator) assertions.NotZero(r.TypedSpec().Size) }) } } func (suite *SystemInfoSuite) TestUUIDOverwrite() { stream, err := os.Open("testdata/SuperMicro-Dual-Xeon.dmi") suite.Require().NoError(err) suite.T().Cleanup(func() { suite.NoError(stream.Close()) }) version := smbios.Version{Major: 3, Minor: 3, Revision: 0} // dummy version s, err := smbios.Decode(stream, version) suite.Require().NoError(err) suite.Require().NoError( suite.runtime.RegisterController( &hardwarectrl.SystemInfoController{ SMBIOS: s, }, ), ) suite.startRuntime() suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded())) key := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.UUIDOverride)) key.TypedSpec().Value = "00000000-0000-0000-0000-000000000001" suite.Require().NoError(suite.state.Create(suite.ctx, key)) ctest.AssertResource(suite, hardware.SystemInformationID, func(r *hardware.SystemInformation, assertions *assert.Assertions) { assertions.Equal("00000000-0000-0000-0000-000000000001", r.TypedSpec().UUID) }) } func (suite *SystemInfoSuite) TestPopulateSystemInformationIsDisabledInContainerMode() { suite.Require().NoError( suite.runtime.RegisterController( &hardwarectrl.SystemInfoController{ V1Alpha1Mode: runtimetalos.ModeContainer, }, ), ) suite.startRuntime() suite.Require().NoError(suite.state.Create(suite.ctx, runtime.NewMetaLoaded())) suite.Assert().NoError(retry.Constant(1*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(suite.assertNoResource(*hardware.NewSystemInformation("systeminformation").Metadata()))) } func TestSystemInfoSyncSuite(t *testing.T) { suite.Run(t, new(SystemInfoSuite)) } func (suite *SystemInfoSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/address_filter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // AddressFilterController creates NodeAddressFilters based on machine configuration. type AddressFilterController struct{} // Name implements controller.Controller interface. func (ctrl *AddressFilterController) Name() string { return "k8s.AddressFilterController" } // Inputs implements controller.Controller interface. func (ctrl *AddressFilterController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *AddressFilterController) Outputs() []controller.Output { return []controller.Output{ { Type: network.NodeAddressFilterType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *AddressFilterController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } nodeName, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting nodename: %w", err) } var nodeStatus *k8s.NodeStatus if nodeName != nil { nodeStatus, err = safe.ReaderGetByID[*k8s.NodeStatus](ctx, r, nodeName.TypedSpec().Nodename) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting nodename: %w", err) } } r.StartTrackingOutputs() if cfg != nil && cfg.Config().Cluster() != nil { cfgProvider := cfg.Config() var podCIDRs, serviceCIDRs []netip.Prefix for _, cidr := range cfgProvider.Cluster().Network().PodCIDRs() { var ipPrefix netip.Prefix ipPrefix, err = netip.ParsePrefix(cidr) if err != nil { return fmt.Errorf("error parsing podCIDR: %w", err) } podCIDRs = append(podCIDRs, ipPrefix) } if nodeStatus != nil { podCIDRs = append(podCIDRs, nodeStatus.TypedSpec().PodCIDRs...) } for _, cidr := range cfgProvider.Cluster().Network().ServiceCIDRs() { var ipPrefix netip.Prefix ipPrefix, err = netip.ParsePrefix(cidr) if err != nil { return fmt.Errorf("error parsing serviceCIDR: %w", err) } serviceCIDRs = append(serviceCIDRs, ipPrefix) } if err = safe.WriterModify(ctx, r, network.NewNodeAddressFilter(network.NamespaceName, k8s.NodeAddressFilterNoK8s), func(r *network.NodeAddressFilter) error { r.TypedSpec().ExcludeSubnets = slices.Concat(podCIDRs, serviceCIDRs) return nil }); err != nil { return fmt.Errorf("error updating output resource: %w", err) } if err = safe.WriterModify(ctx, r, network.NewNodeAddressFilter(network.NamespaceName, k8s.NodeAddressFilterOnlyK8s), func(r *network.NodeAddressFilter) error { r.TypedSpec().IncludeSubnets = slices.Concat(podCIDRs, serviceCIDRs) return nil }); err != nil { return fmt.Errorf("error updating output resource: %w", err) } } if err = safe.CleanupOutputs[*network.NodeAddressFilter](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/address_filter_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "fmt" "net/netip" "net/url" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type K8sAddressFilterSuite struct { ctest.DefaultSuite } func (suite *K8sAddressFilterSuite) TestReconcile() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ ServiceSubnet: []string{ "10.200.0.0/22", "fd40:10:200::/112", }, PodSubnet: []string{ "10.32.0.0/12", "fd00:10:32::/102", }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResource(suite, k8s.NodeAddressFilterOnlyK8s, func(res *network.NodeAddressFilter, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal( "[10.32.0.0/12 fd00:10:32::/102 10.200.0.0/22 fd40:10:200::/112]", fmt.Sprintf("%s", spec.IncludeSubnets), ) asrt.Empty(spec.ExcludeSubnets) }) ctest.AssertResource(suite, k8s.NodeAddressFilterNoK8s, func(res *network.NodeAddressFilter, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Empty(spec.IncludeSubnets) asrt.Equal( "[10.32.0.0/12 fd00:10:32::/102 10.200.0.0/22 fd40:10:200::/112]", fmt.Sprintf("%s", spec.ExcludeSubnets), ) }) // create NodeStatus with PodCIDRs nodeName := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodeName.TypedSpec().Nodename = "test-node" suite.Create(nodeName) nodeStatus := k8s.NewNodeStatus(k8s.NamespaceName, "test-node") nodeStatus.TypedSpec().PodCIDRs = []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/24"), } suite.Create(nodeStatus) ctest.AssertResource(suite, k8s.NodeAddressFilterOnlyK8s, func(res *network.NodeAddressFilter, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal( "[10.32.0.0/12 fd00:10:32::/102 192.168.0.0/24 10.200.0.0/22 fd40:10:200::/112]", fmt.Sprintf("%s", spec.IncludeSubnets), ) asrt.Empty(spec.ExcludeSubnets) }) ctest.AssertResource(suite, k8s.NodeAddressFilterNoK8s, func(res *network.NodeAddressFilter, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Empty(spec.IncludeSubnets) asrt.Equal( "[10.32.0.0/12 fd00:10:32::/102 192.168.0.0/24 10.200.0.0/22 fd40:10:200::/112]", fmt.Sprintf("%s", spec.ExcludeSubnets), ) }) } func TestK8sAddressFilterSuite(t *testing.T) { t.Parallel() suite.Run(t, &K8sAddressFilterSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&k8sctrl.AddressFilterController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/control_plane.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "slices" "strconv" "strings" "github.com/blang/semver/v4" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kubernetes/kubernetes/compatibility" "go.uber.org/zap" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/pkg/argsbuilder" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/kubernetes" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // controlplaneMapFunc is a shared "map" func for transform controller which guards on: // * machine config is there // * it has cluster & machine parts // * machine is controlplane one. func controlplaneMapFunc[Output generic.ResourceWithRD](output Output) func(cfg *config.MachineConfig) optional.Optional[Output] { return func(cfg *config.MachineConfig) optional.Optional[Output] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[Output]() } if cfg.Config().Cluster() == nil || cfg.Config().Machine() == nil { return optional.None[Output]() } if !cfg.Config().Machine().Type().IsControlPlane() { return optional.None[Output]() } return optional.Some(output) } } // ControlPlaneAdmissionControlController manages k8s.AdmissionControlConfig based on configuration. type ControlPlaneAdmissionControlController = transform.Controller[*config.MachineConfig, *k8s.AdmissionControlConfig] // NewControlPlaneAdmissionControlController instanciates the controller. func NewControlPlaneAdmissionControlController() *ControlPlaneAdmissionControlController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.AdmissionControlConfig]{ Name: "k8s.ControlPlaneAdmissionControlController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewAdmissionControlConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.AdmissionControlConfig) error { cfgProvider := machineConfig.Config() res.TypedSpec().Config = nil for _, cfg := range cfgProvider.Cluster().APIServer().AdmissionControl() { res.TypedSpec().Config = append(res.TypedSpec().Config, k8s.AdmissionPluginSpec{ Name: cfg.Name(), Configuration: cfg.Configuration(), }, ) } return nil }, }, ) } // ControlPlaneAuditPolicyController manages k8s.AuditPolicyConfig based on configuration. type ControlPlaneAuditPolicyController = transform.Controller[*config.MachineConfig, *k8s.AuditPolicyConfig] // NewControlPlaneAuditPolicyController instanciates the controller. func NewControlPlaneAuditPolicyController() *ControlPlaneAuditPolicyController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.AuditPolicyConfig]{ Name: "k8s.ControlPlaneAuditPolicyController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewAuditPolicyConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.AuditPolicyConfig) error { cfgProvider := machineConfig.Config() res.TypedSpec().Config = cfgProvider.Cluster().APIServer().AuditPolicy() return nil }, }, ) } // ControlPlaneAuthorizationController manages k8s.AuthorizationConfig based on configuration. type ControlPlaneAuthorizationController = transform.Controller[*config.MachineConfig, *k8s.AuthorizationConfig] // NewControlPlaneAuthorizationController instanciates the controller. func NewControlPlaneAuthorizationController() *ControlPlaneAuthorizationController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.AuthorizationConfig]{ Name: "k8s.ControlPlaneAuthorizationPolicyController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewAuthorizationConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.AuthorizationConfig) error { cfgProvider := machineConfig.Config() res.TypedSpec().Image = cfgProvider.Cluster().APIServer().Image() if !compatibility.VersionFromImageRef(cfgProvider.Cluster().APIServer().Image()).KubeAPIServerSupportsAuthorizationConfigFile() { return nil } if cfgProvider.Cluster().APIServer().AuthorizationConfig() == nil { res.TypedSpec().Config = v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers return nil } var authorizers []k8s.AuthorizationAuthorizersSpec for _, authorizer := range cfgProvider.Cluster().APIServer().AuthorizationConfig() { authorizers = slices.Concat(authorizers, []k8s.AuthorizationAuthorizersSpec{ { Type: authorizer.Type(), Name: authorizer.Name(), Webhook: authorizer.Webhook(), }, }) } if !slices.ContainsFunc(authorizers, func(a k8s.AuthorizationAuthorizersSpec) bool { return a.Type == "Node" }) { authorizers = slices.Insert(authorizers, 0, k8s.AuthorizationAuthorizersSpec{ Type: "Node", Name: "node", }) } if !slices.ContainsFunc(authorizers, func(a k8s.AuthorizationAuthorizersSpec) bool { return a.Type == "RBAC" }) { authorizers = slices.Insert(authorizers, 1, k8s.AuthorizationAuthorizersSpec{ Type: "RBAC", Name: "rbac", }) } res.TypedSpec().Config = authorizers return nil }, }, ) } // ControlPlaneAPIServerController manages k8s.APIServerConfig based on configuration. type ControlPlaneAPIServerController = transform.Controller[*config.MachineConfig, *k8s.APIServerConfig] // NewControlPlaneAPIServerController instanciates the controller. func NewControlPlaneAPIServerController() *ControlPlaneAPIServerController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.APIServerConfig]{ Name: "k8s.ControlPlaneAPIServerController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewAPIServerConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.APIServerConfig) error { cfgProvider := machineConfig.Config() var cloudProvider string if cfgProvider.Cluster().ExternalCloudProvider().Enabled() { cloudProvider = CloudProviderExternal } advertisedAddress := "$(POD_IP)" if cfgProvider.Machine().Kubelet().SkipNodeRegistration() { advertisedAddress = "" } extraArgs := make(map[string]k8s.ArgValues, len(cfgProvider.Cluster().APIServer().ExtraArgs())) for k, v := range cfgProvider.Cluster().APIServer().ExtraArgs() { extraArgs[k] = k8s.ArgValues{Values: v} } *res.TypedSpec() = k8s.APIServerConfigSpec{ Image: cfgProvider.Cluster().APIServer().Image(), CloudProvider: cloudProvider, ControlPlaneEndpoint: cfgProvider.Cluster().Endpoint().String(), EtcdServers: []string{fmt.Sprintf("https://%s", nethelpers.JoinHostPort("127.0.0.1", constants.EtcdClientPort))}, LocalPort: cfgProvider.Cluster().LocalAPIServerPort(), ServiceCIDRs: cfgProvider.Cluster().Network().ServiceCIDRs(), ExtraArgs: extraArgs, ExtraVolumes: convertVolumes(cfgProvider.Cluster().APIServer().ExtraVolumes()), EnvironmentVariables: cfgProvider.Cluster().APIServer().Env(), AdvertisedAddress: advertisedAddress, Resources: convertResources(cfgProvider.Cluster().APIServer().Resources()), } return nil }, }, ) } // ControlPlaneControllerManagerController manages k8s.ControllerManagerConfig based on configuration. type ControlPlaneControllerManagerController = transform.Controller[*config.MachineConfig, *k8s.ControllerManagerConfig] // NewControlPlaneControllerManagerController instanciates the controller. func NewControlPlaneControllerManagerController() *ControlPlaneControllerManagerController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.ControllerManagerConfig]{ Name: "k8s.ControlPlaneControllerManagerController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewControllerManagerConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.ControllerManagerConfig) error { cfgProvider := machineConfig.Config() var cloudProvider string if cfgProvider.Cluster().ExternalCloudProvider().Enabled() { cloudProvider = CloudProviderExternal } extraArgs := make(map[string]k8s.ArgValues, len(cfgProvider.Cluster().ControllerManager().ExtraArgs())) for k, v := range cfgProvider.Cluster().ControllerManager().ExtraArgs() { extraArgs[k] = k8s.ArgValues{Values: v} } *res.TypedSpec() = k8s.ControllerManagerConfigSpec{ Enabled: !cfgProvider.Machine().Controlplane().ControllerManager().Disabled(), Image: cfgProvider.Cluster().ControllerManager().Image(), CloudProvider: cloudProvider, PodCIDRs: cfgProvider.Cluster().Network().PodCIDRs(), ServiceCIDRs: cfgProvider.Cluster().Network().ServiceCIDRs(), ExtraArgs: extraArgs, ExtraVolumes: convertVolumes(cfgProvider.Cluster().ControllerManager().ExtraVolumes()), EnvironmentVariables: cfgProvider.Cluster().ControllerManager().Env(), Resources: convertResources(cfgProvider.Cluster().ControllerManager().Resources()), } return nil }, }, ) } // ControlPlaneSchedulerController manages k8s.SchedulerConfig based on configuration. type ControlPlaneSchedulerController = transform.Controller[*config.MachineConfig, *k8s.SchedulerConfig] // NewControlPlaneSchedulerController instanciates the controller. func NewControlPlaneSchedulerController() *ControlPlaneSchedulerController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.SchedulerConfig]{ Name: "k8s.ControlPlaneSchedulerController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewSchedulerConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.SchedulerConfig) error { cfgProvider := machineConfig.Config() extraArgs := make(map[string]k8s.ArgValues, len(cfgProvider.Cluster().Scheduler().ExtraArgs())) for k, v := range cfgProvider.Cluster().Scheduler().ExtraArgs() { extraArgs[k] = k8s.ArgValues{Values: v} } *res.TypedSpec() = k8s.SchedulerConfigSpec{ Enabled: !cfgProvider.Machine().Controlplane().Scheduler().Disabled(), Image: cfgProvider.Cluster().Scheduler().Image(), ExtraArgs: extraArgs, ExtraVolumes: convertVolumes(cfgProvider.Cluster().Scheduler().ExtraVolumes()), EnvironmentVariables: cfgProvider.Cluster().Scheduler().Env(), Resources: convertResources(cfgProvider.Cluster().Scheduler().Resources()), Config: cfgProvider.Cluster().Scheduler().Config(), } return nil }, }, ) } // ControlPlaneBootstrapManifestsController manages k8s.BootstrapManifestsConfig based on configuration. type ControlPlaneBootstrapManifestsController = transform.Controller[*config.MachineConfig, *k8s.BootstrapManifestsConfig] // NewControlPlaneBootstrapManifestsController instanciates the controller. func NewControlPlaneBootstrapManifestsController() *ControlPlaneBootstrapManifestsController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.BootstrapManifestsConfig]{ Name: "k8s.ControlPlaneBootstrapManifestsController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewBootstrapManifestsConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.BootstrapManifestsConfig) error { cfgProvider := machineConfig.Config() dnsServiceIPs, err := cfgProvider.Cluster().Network().DNSServiceIPs() if err != nil { return fmt.Errorf("error calculating DNS service IPs: %w", err) } dnsServiceIP := "" dnsServiceIPv6 := "" for _, ip := range dnsServiceIPs { if dnsServiceIP == "" && ip.Is4() { dnsServiceIP = ip.String() } if dnsServiceIPv6 == "" && ip.Is6() { dnsServiceIPv6 = ip.String() } } images := images.List(cfgProvider) proxyArgs, err := getProxyArgs(cfgProvider) if err != nil { return err } var ( server string flannelKubeServiceHost, flannelKubeServicePort string ) if cfgProvider.Machine().Features().KubePrism().Enabled() { server = fmt.Sprintf("https://127.0.0.1:%d", cfgProvider.Machine().Features().KubePrism().Port()) flannelKubeServiceHost = "127.0.0.1" flannelKubeServicePort = strconv.Itoa(cfgProvider.Machine().Features().KubePrism().Port()) } else { server = cfgProvider.Cluster().Endpoint().String() } *res.TypedSpec() = k8s.BootstrapManifestsConfigSpec{ Server: server, ClusterDomain: cfgProvider.Cluster().Network().DNSDomain(), PodCIDRs: cfgProvider.Cluster().Network().PodCIDRs(), ProxyEnabled: cfgProvider.Cluster().Proxy().Enabled(), ProxyImage: cfgProvider.Cluster().Proxy().Image(), ProxyArgs: proxyArgs, CoreDNSEnabled: cfgProvider.Cluster().CoreDNS().Enabled(), CoreDNSImage: cfgProvider.Cluster().CoreDNS().Image(), DNSServiceIP: dnsServiceIP, DNSServiceIPv6: dnsServiceIPv6, FlannelEnabled: cfgProvider.Cluster().Network().CNI().Name() == constants.FlannelCNI, FlannelImage: images.Flannel.String(), FlannelExtraArgs: cfgProvider.Cluster().Network().CNI().Flannel().ExtraArgs(), FlannelKubeServiceHost: flannelKubeServiceHost, FlannelKubeServicePort: flannelKubeServicePort, FlannelKubeNetworkPoliciesEnabled: cfgProvider.Cluster().Network().CNI().Flannel().KubeNetworkPoliciesEnabled(), FlannelKubeNetworkPoliciesImage: images.KubeNetworkPolicies.String(), TalosAPIServiceEnabled: cfgProvider.Machine().Features().KubernetesTalosAPIAccess().Enabled(), CNIName: cfgProvider.Cluster().Network().CNI().Name(), } return nil }, }, transform.WithExtraInputs( controller.Input{ Namespace: network.NamespaceName, Type: network.HostDNSConfigType, ID: optional.Some(network.HostDNSConfigID), Kind: controller.InputWeak, }, ), ) } // ControlPlaneExtraManifestsController manages k8s.ExtraManifestsConfig based on configuration. type ControlPlaneExtraManifestsController = transform.Controller[*config.MachineConfig, *k8s.ExtraManifestsConfig] // NewControlPlaneExtraManifestsController instanciates the controller. func NewControlPlaneExtraManifestsController() *ControlPlaneExtraManifestsController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.ExtraManifestsConfig]{ Name: "k8s.ControlPlaneExtraManifestsController", MapMetadataOptionalFunc: controlplaneMapFunc(k8s.NewExtraManifestsConfig()), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.ExtraManifestsConfig) error { cfgProvider := machineConfig.Config() spec := k8s.ExtraManifestsConfigSpec{} for _, url := range cfgProvider.Cluster().Network().CNI().URLs() { spec.ExtraManifests = append(spec.ExtraManifests, k8s.ExtraManifest{ Name: url, URL: url, Priority: "05", // push CNI to the top }) } for _, url := range cfgProvider.Cluster().ExternalCloudProvider().ManifestURLs() { spec.ExtraManifests = append(spec.ExtraManifests, k8s.ExtraManifest{ Name: url, URL: url, Priority: "30", // after default manifests }) } for _, url := range cfgProvider.Cluster().ExtraManifestURLs() { spec.ExtraManifests = append(spec.ExtraManifests, k8s.ExtraManifest{ Name: url, URL: url, Priority: "99", // make sure extra manifests come last, when PSP is already created ExtraHeaders: cfgProvider.Cluster().ExtraManifestHeaderMap(), }) } for _, manifest := range cfgProvider.Cluster().InlineManifests() { spec.ExtraManifests = append(spec.ExtraManifests, k8s.ExtraManifest{ Name: manifest.Name(), Priority: "99", // make sure extra manifests come last, when PSP is already created InlineManifest: manifest.Contents(), }) } *res.TypedSpec() = spec return nil }, }, ) } func convertVolumes(volumes []talosconfig.VolumeMount) []k8s.ExtraVolume { return xslices.Map(volumes, func(v talosconfig.VolumeMount) k8s.ExtraVolume { return k8s.ExtraVolume{ Name: v.Name(), HostPath: v.HostPath(), MountPath: v.MountPath(), ReadOnly: v.ReadOnly(), } }) } func convertResources(resources talosconfig.Resources) k8s.Resources { var convertedLimits map[string]string cpuLimits := resources.CPULimits() memoryLimits := resources.MemoryLimits() if cpuLimits != "" || memoryLimits != "" { convertedLimits = map[string]string{} if cpuLimits != "" { convertedLimits[string(v1.ResourceCPU)] = cpuLimits } if memoryLimits != "" { convertedLimits[string(v1.ResourceMemory)] = memoryLimits } } return k8s.Resources{ Requests: map[string]string{ string(v1.ResourceCPU): resources.CPURequests(), string(v1.ResourceMemory): resources.MemoryRequests(), }, Limits: convertedLimits, } } func getProxyArgs(cfgProvider talosconfig.Config) ([]string, error) { clusterCidr := strings.Join(cfgProvider.Cluster().Network().PodCIDRs(), ",") proxyMode := cfgProvider.Cluster().Proxy().Mode() if proxyMode == "" { // determine proxy mode based on kube-proxy version via the image, use 'nftables' for Kubernetes >= 1.31 if kubernetes.VersionGTE(cfgProvider.Cluster().Proxy().Image(), semver.MustParse("1.31.0")) { proxyMode = "nftables" } else { proxyMode = "iptables" } } builder := argsbuilder.Args{ "cluster-cidr": {clusterCidr}, "hostname-override": {"$(NODE_NAME)"}, "kubeconfig": {"/etc/kubernetes/kubeconfig"}, "proxy-mode": {proxyMode}, "conntrack-max-per-core": {"0"}, } policies := argsbuilder.MergePolicies{ "kubeconfig": argsbuilder.MergeDenied, } if err := builder.Merge(cfgProvider.Cluster().Proxy().ExtraArgs(), argsbuilder.WithMergePolicies(policies)); err != nil { return nil, err } return builder.Args(), nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/control_plane_static_pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "path/filepath" "slices" "strconv" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kubernetes/kubernetes/compatibility" "go.uber.org/zap" v1 "k8s.io/api/core/v1" apiresource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/pkg/argsbuilder" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // systemCriticalPriority is copied from scheduling.SystemCriticalPriority in Kubernetes internals. const systemCriticalPriority int32 = 2000000000 // GoGCMemLimitPercentage set the percentage of memorylimit to use for the golang garbage collection target limit. const GoGCMemLimitPercentage = 95 // ControlPlaneStaticPodController manages k8s.StaticPod based on control plane configuration. type ControlPlaneStaticPodController struct{} // Name implements controller.Controller interface. func (ctrl *ControlPlaneStaticPodController) Name() string { return "k8s.ControlPlaneStaticPodController" } // Inputs implements controller.Controller interface. func (ctrl *ControlPlaneStaticPodController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.APIServerConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.ControllerManagerConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.SchedulerConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.SecretsStatusType, ID: optional.Some(k8s.StaticPodSecretsStaticPodID), Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.ConfigStatusType, ID: optional.Some(k8s.ConfigStatusStaticPodID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("etcd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ControlPlaneStaticPodController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.StaticPodType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *ControlPlaneStaticPodController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // wait for etcd to be healthy as kube-apiserver is using local etcd instance etcdResource, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "etcd") if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } if !etcdResource.TypedSpec().Healthy { continue } secretsStatusResource, err := safe.ReaderGetByID[*k8s.SecretsStatus](ctx, r, k8s.StaticPodSecretsStaticPodID) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } secretsVersion := secretsStatusResource.TypedSpec().Version configStatusResource, err := safe.ReaderGetByID[*k8s.ConfigStatus](ctx, r, k8s.ConfigStatusStaticPodID) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } configVersion := configStatusResource.TypedSpec().Version touchedIDs := map[string]struct{}{} for _, pod := range []struct { f func(context.Context, controller.Runtime, *zap.Logger, resource.Resource, string, string) (string, error) md *resource.Metadata }{ { f: ctrl.manageAPIServer, md: k8s.NewAPIServerConfig().Metadata(), }, { f: ctrl.manageControllerManager, md: k8s.NewControllerManagerConfig().Metadata(), }, { f: ctrl.manageScheduler, md: k8s.NewSchedulerConfig().Metadata(), }, } { res, err := r.Get(ctx, pod.md) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting control plane config: %w", err) } var podID string if podID, err = pod.f(ctx, r, logger, res, secretsVersion, configVersion); err != nil { return fmt.Errorf("error updating static pod for %q: %w", pod.md.Type(), err) } if podID != "" { touchedIDs[podID] = struct{}{} } } // clean up static pods which haven't been touched { list, err := r.List(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.StaticPodType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if _, ok := touchedIDs[res.Metadata().ID()]; ok { continue } if res.Metadata().Owner() != ctrl.Name() { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } } r.ResetRestartBackoff() } } func (ctrl *ControlPlaneStaticPodController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.StaticPodType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } func volumeMounts(volumes []k8s.ExtraVolume) []v1.VolumeMount { return xslices.Map(volumes, func(vol k8s.ExtraVolume) v1.VolumeMount { return v1.VolumeMount{ Name: vol.Name, MountPath: vol.MountPath, ReadOnly: vol.ReadOnly, } }) } func volumes(volumes []k8s.ExtraVolume) []v1.Volume { return xslices.Map(volumes, func(vol k8s.ExtraVolume) v1.Volume { return v1.Volume{ Name: vol.Name, VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: vol.HostPath, }, }, } }) } func envVars(environment map[string]string) []v1.EnvVar { if len(environment) == 0 { return nil } keys := maps.Keys(environment) slices.Sort(keys) return xslices.Map(keys, func(key string) v1.EnvVar { // Kubernetes supports variable references in variable values, so escape '$' to prevent that. return v1.EnvVar{ Name: key, Value: strings.ReplaceAll(environment[key], "$", "$$"), } }) } func resources(resourcesConfig k8s.Resources, defaultCPU, defaultMemory string) (v1.ResourceRequirements, error) { resources := v1.ResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceCPU: apiresource.MustParse(defaultCPU), v1.ResourceMemory: apiresource.MustParse(defaultMemory), }, Limits: v1.ResourceList{}, } if cpu := resourcesConfig.Requests[string(v1.ResourceCPU)]; cpu != "" { parsedCPU, err := apiresource.ParseQuantity(cpu) if err != nil { return v1.ResourceRequirements{}, fmt.Errorf("error parsing CPU request: %w", err) } resources.Requests[v1.ResourceCPU] = parsedCPU } if memory := resourcesConfig.Requests[string(v1.ResourceMemory)]; memory != "" { parsedMemory, err := apiresource.ParseQuantity(memory) if err != nil { return v1.ResourceRequirements{}, fmt.Errorf("error parsing memory request: %w", err) } resources.Requests[v1.ResourceMemory] = parsedMemory } if cpu := resourcesConfig.Limits[string(v1.ResourceCPU)]; cpu != "" { parsedCPU, err := apiresource.ParseQuantity(cpu) if err != nil { return v1.ResourceRequirements{}, fmt.Errorf("error parsing CPU limit: %w", err) } resources.Limits[v1.ResourceCPU] = parsedCPU } if memory := resourcesConfig.Limits[string(v1.ResourceMemory)]; memory != "" { parsedMemory, err := apiresource.ParseQuantity(memory) if err != nil { return v1.ResourceRequirements{}, fmt.Errorf("error parsing memory limit: %w", err) } resources.Limits[v1.ResourceMemory] = parsedMemory } return resources, nil } func goGCEnvFromResources(resources v1.ResourceRequirements) (envVar v1.EnvVar) { memoryLimit := resources.Limits[v1.ResourceMemory] if memoryLimit.Value() > 0 { gcMemLimit := memoryLimit.Value() * GoGCMemLimitPercentage / 100 envVar = v1.EnvVar{ Name: "GOMEMLIMIT", Value: strconv.FormatInt(gcMemLimit, 10), } } return envVar } func (ctrl *ControlPlaneStaticPodController) manageAPIServer(ctx context.Context, r controller.Runtime, _ *zap.Logger, configResource resource.Resource, secretsVersion, configVersion string, ) (string, error) { cfg := configResource.(*k8s.APIServerConfig).TypedSpec() enabledAdmissionPlugins := []string{"NodeRestriction"} args := []string{ //nolint:prealloc // very dynamic length "/usr/local/bin/kube-apiserver", } builder := argsbuilder.Args{ "admission-control-config-file": {filepath.Join(constants.KubernetesAPIServerConfigDir, "admission-control-config.yaml")}, "allow-privileged": {"true"}, // Do not accept anonymous requests by default. Otherwise the kube-apiserver will set the request's group to system:unauthenticated exposing endpoints like /version etc. "anonymous-auth": {"false"}, "api-audiences": {cfg.ControlPlaneEndpoint}, "bind-address": {"0.0.0.0"}, "client-ca-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "ca.crt")}, "enable-admission-plugins": {strings.Join(enabledAdmissionPlugins, ",")}, "requestheader-client-ca-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "aggregator-ca.crt")}, "requestheader-allowed-names": {"front-proxy-client"}, "requestheader-extra-headers-prefix": {"X-Remote-Extra-"}, "requestheader-group-headers": {"X-Remote-Group"}, "requestheader-username-headers": {"X-Remote-User"}, "proxy-client-cert-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "front-proxy-client.crt")}, "proxy-client-key-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "front-proxy-client.key")}, "enable-bootstrap-token-auth": {"true"}, // NB: using TLS 1.2 instead of 1.3 here for interoperability, since this is an externally-facing service. "tls-min-version": {"VersionTLS12"}, "tls-cipher-suites": {"TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"}, //nolint:lll "encryption-provider-config": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "encryptionconfig.yaml")}, "audit-policy-file": {filepath.Join(constants.KubernetesAPIServerConfigDir, "auditpolicy.yaml")}, "audit-log-path": {filepath.Join(constants.KubernetesAuditLogDir, "kube-apiserver.log")}, "audit-log-maxage": {"30"}, "audit-log-maxbackup": {"10"}, "audit-log-maxsize": {"100"}, "profiling": {"false"}, "etcd-cafile": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "etcd-client-ca.crt")}, "etcd-certfile": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "etcd-client.crt")}, "etcd-keyfile": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "etcd-client.key")}, "etcd-servers": {strings.Join(cfg.EtcdServers, ",")}, "kubelet-client-certificate": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "apiserver-kubelet-client.crt")}, "kubelet-client-key": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "apiserver-kubelet-client.key")}, "secure-port": {strconv.FormatInt(int64(cfg.LocalPort), 10)}, "service-account-issuer": {cfg.ControlPlaneEndpoint}, "service-account-key-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "service-account.pub")}, "service-account-signing-key-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "service-account.key")}, "service-cluster-ip-range": {strings.Join(cfg.ServiceCIDRs, ",")}, "tls-cert-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "apiserver.crt")}, "tls-private-key-file": {filepath.Join(constants.KubernetesAPIServerSecretsDir, "apiserver.key")}, "kubelet-preferred-address-types": {"InternalIP,ExternalIP,Hostname"}, } if cfg.AdvertisedAddress != "" { builder.Set("advertise-address", argsbuilder.Value{cfg.AdvertisedAddress}) } k8sVersion := compatibility.VersionFromImageRef(cfg.Image) if cfg.CloudProvider != "" && !k8sVersion.CloudProviderFlagRemoved() { builder.Set("cloud-provider", argsbuilder.Value{cfg.CloudProvider}) } extraArgs := make(argsbuilder.Args, len(cfg.ExtraArgs)) for k, v := range cfg.ExtraArgs { extraArgs[k] = v.Values } handleKubeAPIServerAuthorizationFlags(k8sVersion, builder, extraArgs) mergePolicies := argsbuilder.MergePolicies{ "enable-admission-plugins": argsbuilder.MergeAdditive, "feature-gates": argsbuilder.MergeAdditive, "authorization-mode": argsbuilder.MergeAdditive, "tls-cipher-suites": argsbuilder.MergeAdditive, "etcd-servers": argsbuilder.MergeDenied, "client-ca-file": argsbuilder.MergeDenied, "requestheader-client-ca-file": argsbuilder.MergeDenied, "proxy-client-cert-file": argsbuilder.MergeDenied, "proxy-client-key-file": argsbuilder.MergeDenied, "encryption-provider-config": argsbuilder.MergeDenied, "etcd-cafile": argsbuilder.MergeDenied, "etcd-certfile": argsbuilder.MergeDenied, "etcd-keyfile": argsbuilder.MergeDenied, "kubelet-client-certificate": argsbuilder.MergeDenied, "kubelet-client-key": argsbuilder.MergeDenied, "service-account-issuer": argsbuilder.MergeAppend, "service-account-key-file": argsbuilder.MergeDenied, "service-account-signing-key-file": argsbuilder.MergeDenied, "tls-cert-file": argsbuilder.MergeDenied, "tls-private-key-file": argsbuilder.MergeDenied, "authorization-config": argsbuilder.MergeDenied, } if err := builder.Merge(extraArgs, argsbuilder.WithMergePolicies(mergePolicies)); err != nil { return "", err } args = append(args, builder.Args()...) resources, err := resources(cfg.Resources, "200m", "512Mi") if err != nil { return "", err } env := envVars(cfg.EnvironmentVariables) if goGCEnv := goGCEnvFromResources(resources); goGCEnv.Name != "" { env = append(env, goGCEnv) } return k8s.APIServerID, safe.WriterModify(ctx, r, k8s.NewStaticPod(k8s.NamespaceName, k8s.APIServerID), func(r *k8s.StaticPod) error { return k8sadapter.StaticPod(r).SetPod(&v1.Pod{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", Kind: "Pod", }, ObjectMeta: metav1.ObjectMeta{ Name: k8s.APIServerID, Namespace: "kube-system", Annotations: map[string]string{ constants.AnnotationStaticPodSecretsVersion: secretsVersion, constants.AnnotationStaticPodConfigFileVersion: configVersion, constants.AnnotationStaticPodConfigVersion: configResource.Metadata().Version().String(), }, Labels: map[string]string{ "tier": "control-plane", "k8s-app": k8s.APIServerID, "component": k8s.APIServerID, "app.kubernetes.io/name": k8s.APIServerID, "app.kubernetes.io/version": k8sVersion.String(), "app.kubernetes.io/component": "control-plane", "app.kubernetes.io/managed-by": "Talos", }, }, Spec: v1.PodSpec{ Priority: new(systemCriticalPriority), PriorityClassName: "system-cluster-critical", Containers: []v1.Container{ { Name: k8s.APIServerID, Image: cfg.Image, Command: args, Env: append( []v1.EnvVar{ { Name: "POD_IP", ValueFrom: &v1.EnvVarSource{ FieldRef: &v1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, }, env...), VolumeMounts: append([]v1.VolumeMount{ { Name: "secrets", MountPath: constants.KubernetesAPIServerSecretsDir, ReadOnly: true, }, { Name: "config", MountPath: constants.KubernetesAPIServerConfigDir, ReadOnly: true, }, { Name: "audit", MountPath: constants.KubernetesAuditLogDir, ReadOnly: false, }, }, volumeMounts(cfg.ExtraVolumes)...), Resources: resources, SecurityContext: &v1.SecurityContext{ AllowPrivilegeEscalation: new(false), Capabilities: &v1.Capabilities{ Drop: []v1.Capability{"ALL"}, // kube-apiserver binary has cap_net_bind_service=+ep set. // It does not matter if ports < 1024 are configured, the setcap flag causes a capability dependency. // https://github.com/kubernetes/kubernetes/blob/5b92e46b2238b4d84358451013e634361084ff7d/build/server-image/kube-apiserver/Dockerfile#L26 Add: []v1.Capability{"NET_BIND_SERVICE"}, }, SeccompProfile: &v1.SeccompProfile{ Type: v1.SeccompProfileTypeRuntimeDefault, }, }, }, }, HostNetwork: true, SecurityContext: &v1.PodSecurityContext{ RunAsNonRoot: new(true), RunAsUser: new(int64(constants.KubernetesAPIServerRunUser)), RunAsGroup: new(int64(constants.KubernetesAPIServerRunGroup)), }, Volumes: append([]v1.Volume{ { Name: "secrets", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAPIServerSecretsDir, }, }, }, { Name: "config", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAPIServerConfigDir, }, }, }, { Name: "audit", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAuditLogDir, }, }, }, }, volumes(cfg.ExtraVolumes)...), }, }) }) } func (ctrl *ControlPlaneStaticPodController) manageControllerManager(ctx context.Context, r controller.Runtime, _ *zap.Logger, configResource resource.Resource, secretsVersion, _ string, ) (string, error) { cfg := configResource.(*k8s.ControllerManagerConfig).TypedSpec() if !cfg.Enabled { return "", nil } args := []string{ //nolint:prealloc // very dynamic length "/usr/local/bin/kube-controller-manager", "--use-service-account-credentials", } builder := argsbuilder.Args{ "allocate-node-cidrs": {"true"}, "bind-address": {"127.0.0.1"}, "cluster-cidr": {strings.Join(cfg.PodCIDRs, ",")}, "service-cluster-ip-range": {strings.Join(cfg.ServiceCIDRs, ",")}, "cluster-signing-cert-file": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "ca.crt")}, "cluster-signing-key-file": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "ca.key")}, "controllers": {"*,tokencleaner"}, "configure-cloud-routes": {"false"}, "kubeconfig": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "kubeconfig")}, "authentication-kubeconfig": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "kubeconfig")}, "authorization-kubeconfig": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "kubeconfig")}, "leader-elect": {"true"}, "root-ca-file": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "ca.crt")}, "service-account-private-key-file": {filepath.Join(constants.KubernetesControllerManagerSecretsDir, "service-account.key")}, "profiling": {"false"}, "tls-min-version": {"VersionTLS13"}, } k8sVersion := compatibility.VersionFromImageRef(cfg.Image) if cfg.CloudProvider != "" && !k8sVersion.CloudProviderFlagRemoved() { builder.Set("cloud-provider", argsbuilder.Value{cfg.CloudProvider}) } mergePolicies := argsbuilder.MergePolicies{ "service-cluster-ip-range": argsbuilder.MergeAdditive, "controllers": argsbuilder.MergeAdditive, "cluster-signing-cert-file": argsbuilder.MergeDenied, "cluster-signing-key-file": argsbuilder.MergeDenied, "authentication-kubeconfig": argsbuilder.MergeDenied, "authorization-kubeconfig": argsbuilder.MergeDenied, "root-ca-file": argsbuilder.MergeDenied, "service-account-private-key-file": argsbuilder.MergeDenied, } extraArgs := make(argsbuilder.Args, len(cfg.ExtraArgs)) for k, v := range cfg.ExtraArgs { extraArgs[k] = v.Values } if err := builder.Merge(extraArgs, argsbuilder.WithMergePolicies(mergePolicies)); err != nil { return "", err } args = append(args, builder.Args()...) resources, err := resources(cfg.Resources, "50m", "256Mi") if err != nil { return "", err } env := envVars(cfg.EnvironmentVariables) if goGCEnv := goGCEnvFromResources(resources); goGCEnv.Name != "" { env = append(env, goGCEnv) } return k8s.ControllerManagerID, safe.WriterModify(ctx, r, k8s.NewStaticPod(k8s.NamespaceName, k8s.ControllerManagerID), func(r *k8s.StaticPod) error { return k8sadapter.StaticPod(r).SetPod(&v1.Pod{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", Kind: "Pod", }, ObjectMeta: metav1.ObjectMeta{ Name: k8s.ControllerManagerID, Namespace: "kube-system", Annotations: map[string]string{ constants.AnnotationStaticPodSecretsVersion: secretsVersion, constants.AnnotationStaticPodConfigVersion: configResource.Metadata().Version().String(), }, Labels: map[string]string{ "tier": "control-plane", "k8s-app": k8s.ControllerManagerID, "component": k8s.ControllerManagerID, "app.kubernetes.io/name": k8s.ControllerManagerID, "app.kubernetes.io/version": compatibility.VersionFromImageRef(cfg.Image).String(), "app.kubernetes.io/component": "control-plane", "app.kubernetes.io/managed-by": "Talos", }, }, Spec: v1.PodSpec{ Priority: new(systemCriticalPriority), PriorityClassName: "system-cluster-critical", Containers: []v1.Container{ { Name: k8s.ControllerManagerID, Image: cfg.Image, Command: args, Env: append( []v1.EnvVar{ { Name: "POD_IP", ValueFrom: &v1.EnvVarSource{ FieldRef: &v1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, }, env...), VolumeMounts: append([]v1.VolumeMount{ { Name: "secrets", MountPath: constants.KubernetesControllerManagerSecretsDir, ReadOnly: true, }, }, volumeMounts(cfg.ExtraVolumes)...), StartupProbe: &v1.Probe{ ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: "/healthz", Host: "localhost", Port: intstr.FromInt(10257), Scheme: v1.URISchemeHTTPS, }, }, // Give 60 seconds for the container to start up PeriodSeconds: 5, FailureThreshold: 12, TerminationGracePeriodSeconds: nil, }, LivenessProbe: &v1.Probe{ ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: "/healthz", Host: "localhost", Port: intstr.FromInt(10257), Scheme: v1.URISchemeHTTPS, }, }, TimeoutSeconds: 15, }, Resources: resources, SecurityContext: &v1.SecurityContext{ AllowPrivilegeEscalation: new(false), Capabilities: &v1.Capabilities{ Drop: []v1.Capability{"ALL"}, }, SeccompProfile: &v1.SeccompProfile{ Type: v1.SeccompProfileTypeRuntimeDefault, }, }, }, }, HostNetwork: true, SecurityContext: &v1.PodSecurityContext{ RunAsNonRoot: new(true), RunAsUser: new(int64(constants.KubernetesControllerManagerRunUser)), RunAsGroup: new(int64(constants.KubernetesControllerManagerRunGroup)), }, Volumes: append([]v1.Volume{ { Name: "secrets", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesControllerManagerSecretsDir, }, }, }, }, volumes(cfg.ExtraVolumes)...), }, }) }) } func (ctrl *ControlPlaneStaticPodController) manageScheduler(ctx context.Context, r controller.Runtime, _ *zap.Logger, configResource resource.Resource, secretsVersion, _ string, ) (string, error) { cfg := configResource.(*k8s.SchedulerConfig).TypedSpec() if !cfg.Enabled { return "", nil } args := []string{ //nolint:prealloc // very dynamic length "/usr/local/bin/kube-scheduler", } builder := argsbuilder.Args{ "config": {filepath.Join(constants.KubernetesSchedulerConfigDir, "scheduler-config.yaml")}, "authentication-tolerate-lookup-failure": {"false"}, "authentication-kubeconfig": {filepath.Join(constants.KubernetesSchedulerSecretsDir, "kubeconfig")}, "authorization-kubeconfig": {filepath.Join(constants.KubernetesSchedulerSecretsDir, "kubeconfig")}, "bind-address": {"127.0.0.1"}, "leader-elect": {"true"}, "profiling": {"false"}, "tls-min-version": {"VersionTLS13"}, } mergePolicies := argsbuilder.MergePolicies{ "kubeconfig": argsbuilder.MergeDenied, "authentication-kubeconfig": argsbuilder.MergeDenied, "authorization-kubeconfig": argsbuilder.MergeDenied, "config": argsbuilder.MergeDenied, } extraArgs := make(argsbuilder.Args, len(cfg.ExtraArgs)) for k, v := range cfg.ExtraArgs { extraArgs[k] = v.Values } if err := builder.Merge(extraArgs, argsbuilder.WithMergePolicies(mergePolicies)); err != nil { return "", err } args = append(args, builder.Args()...) resources, err := resources(cfg.Resources, "10m", "64Mi") if err != nil { return "", err } env := envVars(cfg.EnvironmentVariables) if goGCEnv := goGCEnvFromResources(resources); goGCEnv.Name != "" { env = append(env, goGCEnv) } kubeSchedulerVersion := compatibility.VersionFromImageRef(cfg.Image) livenessProbe := &v1.Probe{ ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: kubeSchedulerVersion.KubeSchedulerHealthLivenessEndpoint(), Host: "localhost", Port: intstr.FromInt(10259), Scheme: v1.URISchemeHTTPS, }, }, } readinessProbe := &v1.Probe{ ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: kubeSchedulerVersion.KubeSchedulerHealthReadinessEndpoint(), Host: "localhost", Port: intstr.FromInt(10259), Scheme: v1.URISchemeHTTPS, }, }, } startupProbe := &v1.Probe{ ProbeHandler: v1.ProbeHandler{ HTTPGet: &v1.HTTPGetAction{ Path: kubeSchedulerVersion.KubeSchedulerHealthStartupEndpoint(), Host: "localhost", Port: intstr.FromInt(10259), Scheme: v1.URISchemeHTTPS, }, }, } return k8s.SchedulerID, safe.WriterModify(ctx, r, k8s.NewStaticPod(k8s.NamespaceName, k8s.SchedulerID), func(r *k8s.StaticPod) error { return k8sadapter.StaticPod(r).SetPod(&v1.Pod{ TypeMeta: metav1.TypeMeta{ APIVersion: "v1", Kind: "Pod", }, ObjectMeta: metav1.ObjectMeta{ Name: k8s.SchedulerID, Namespace: "kube-system", Annotations: map[string]string{ constants.AnnotationStaticPodSecretsVersion: secretsVersion, constants.AnnotationStaticPodConfigVersion: configResource.Metadata().Version().String(), }, Labels: map[string]string{ "tier": "control-plane", "k8s-app": k8s.SchedulerID, "component": k8s.SchedulerID, "app.kubernetes.io/name": k8s.SchedulerID, "app.kubernetes.io/version": compatibility.VersionFromImageRef(cfg.Image).String(), "app.kubernetes.io/component": "control-plane", "app.kubernetes.io/managed-by": "Talos", }, }, Spec: v1.PodSpec{ Priority: new(systemCriticalPriority), PriorityClassName: "system-cluster-critical", Containers: []v1.Container{ { Name: k8s.SchedulerID, Image: cfg.Image, Command: args, Env: append( []v1.EnvVar{ { Name: "POD_IP", ValueFrom: &v1.EnvVarSource{ FieldRef: &v1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, }, env...), VolumeMounts: append([]v1.VolumeMount{ { Name: "secrets", MountPath: constants.KubernetesSchedulerSecretsDir, ReadOnly: true, }, { Name: "config", MountPath: constants.KubernetesSchedulerConfigDir, ReadOnly: true, }, }, volumeMounts(cfg.ExtraVolumes)...), StartupProbe: startupProbe, LivenessProbe: livenessProbe, ReadinessProbe: readinessProbe, Resources: resources, SecurityContext: &v1.SecurityContext{ AllowPrivilegeEscalation: new(false), Capabilities: &v1.Capabilities{ Drop: []v1.Capability{"ALL"}, }, SeccompProfile: &v1.SeccompProfile{ Type: v1.SeccompProfileTypeRuntimeDefault, }, }, }, }, HostNetwork: true, SecurityContext: &v1.PodSecurityContext{ RunAsNonRoot: new(true), RunAsUser: new(int64(constants.KubernetesSchedulerRunUser)), RunAsGroup: new(int64(constants.KubernetesSchedulerRunGroup)), }, Volumes: append([]v1.Volume{ { Name: "secrets", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesSchedulerSecretsDir, }, }, }, { Name: "config", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesSchedulerConfigDir, }, }, }, }, volumes(cfg.ExtraVolumes)...), }, }) }) } func kubeAPIServerExtraArgsHasAuthorizationWebhooFlags(extraArgs map[string][]string) bool { return slices.ContainsFunc(maps.Keys(extraArgs), func(arg string) bool { return strings.HasPrefix(arg, "authorization-webhook-") }) } func kubeAPIServerExtraArgsHasAuthorizationModeFlag(extraArgs map[string][]string) bool { _, ok := extraArgs["authorization-mode"] return ok } func handleKubeAPIServerAuthorizationFlags(kubeVersion compatibility.Version, argBuilder argsbuilder.Args, extraArgs map[string][]string) { // this handle multiple cases: // 1. user already has set `authorization-mode` flag, we'll just merge our default `authorization-mode` flag if kubeAPIServerExtraArgsHasAuthorizationModeFlag(extraArgs) { argBuilder.Set("authorization-mode", argsbuilder.Value{"Node,RBAC"}) return } // 2. user has set `authorization-webhook-*` flags, we'll just merge our default `authorization-mode` flag if kubeAPIServerExtraArgsHasAuthorizationWebhooFlags(extraArgs) { argBuilder.Set("authorization-mode", argsbuilder.Value{"Node,RBAC"}) return } // 3. user has not set `authorization-mode` flag and the kube-apiserver version doesn't support `authorization-config` flag // machine config validation should handle the case where either of `authorization-mode` or `authorization-webhook-*` flags are set // along with `authorizationConfig` if !kubeVersion.KubeAPIServerSupportsAuthorizationConfigFile() { argBuilder.Set("authorization-mode", argsbuilder.Value{"Node,RBAC"}) return } if !kubeVersion.FeatureFlagStructuredAuthorizationConfigurationEnabledByDefault() { // feature-gates flag can be set multiple times, since it has merge addictive policy argBuilder.Set("feature-gates", argsbuilder.Value{"StructuredAuthorizationConfiguration=true"}) } argBuilder.Set("authorization-config", argsbuilder.Value{filepath.Join(constants.KubernetesAPIServerConfigDir, "authorization-config.yaml")}) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/control_plane_static_pod_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "fmt" "path/filepath" "strconv" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" v1 "k8s.io/api/core/v1" apiresource "k8s.io/apimachinery/pkg/api/resource" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) type ControlPlaneStaticPodSuite struct { ctest.DefaultSuite } func (suite *ControlPlaneStaticPodSuite) TestReconcileDefaults() { secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) configAPIServer := k8s.NewAPIServerConfig() configControllerManager := k8s.NewControllerManagerConfig() configControllerManager.TypedSpec().Enabled = true configScheduler := k8s.NewSchedulerConfig() configScheduler.TypedSpec().Enabled = true suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configControllerManager)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configScheduler)) rtestutils.AssertResources( suite.Ctx(), suite.T(), suite.State(), []resource.ID{ k8s.APIServerID, k8s.ControllerManagerID, k8s.SchedulerID, }, func(*k8s.StaticPod, *assert.Assertions) {}, ) } func (suite *ControlPlaneStaticPodSuite) TestReconcileExtraMounts() { secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) configAPIServer := k8s.NewAPIServerConfig() *configAPIServer.TypedSpec() = k8s.APIServerConfigSpec{ ExtraVolumes: []k8s.ExtraVolume{ { Name: "foo", HostPath: "/var/lib", MountPath: "/var/foo", ReadOnly: true, }, }, } configControllerManager := k8s.NewControllerManagerConfig() configControllerManager.TypedSpec().Enabled = true configScheduler := k8s.NewSchedulerConfig() configScheduler.TypedSpec().Enabled = true suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configControllerManager)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configScheduler)) rtestutils.AssertResources( suite.Ctx(), suite.T(), suite.State(), []resource.ID{ k8s.APIServerID, k8s.ControllerManagerID, k8s.SchedulerID, }, func(*k8s.StaticPod, *assert.Assertions) {}, ) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { apiServerPod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) suite.Assert().Len(apiServerPod.Spec.Volumes, 4) suite.Assert().Len(apiServerPod.Spec.Containers[0].VolumeMounts, 4) suite.Assert().Equal([]v1.Volume{ { Name: "secrets", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAPIServerSecretsDir, }, }, }, { Name: "config", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAPIServerConfigDir, }, }, }, { Name: "audit", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: constants.KubernetesAuditLogDir, }, }, }, { Name: "foo", VolumeSource: v1.VolumeSource{ HostPath: &v1.HostPathVolumeSource{ Path: "/var/lib", }, }, }, }, apiServerPod.Spec.Volumes, ) suite.Assert().Equal([]v1.VolumeMount{ { Name: "secrets", MountPath: constants.KubernetesAPIServerSecretsDir, ReadOnly: true, }, { Name: "config", MountPath: constants.KubernetesAPIServerConfigDir, ReadOnly: true, }, { Name: "audit", MountPath: constants.KubernetesAuditLogDir, ReadOnly: false, }, { Name: "foo", MountPath: "/var/foo", ReadOnly: true, }, }, apiServerPod.Spec.Containers[0].VolumeMounts, ) }) } func (suite *ControlPlaneStaticPodSuite) TestReconcileExtraArgsK8s() { tests := []struct { k8sVersion string args map[string]k8s.ArgValues expected map[string][]string expectError bool }{ { k8sVersion: "v1.28.0", // authorization-config not supported and `authorization-mode` is not set args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "authorization-mode": {"Node,RBAC"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"PodNodeSelector=true"}, }, }, { k8sVersion: "v1.28.0", // authorization-config not supported args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "authorization-mode": {Values: []string{"Webhook"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "authorization-mode": {"Node,RBAC,Webhook"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"PodNodeSelector=true"}, }, }, { k8sVersion: "v1.29.0", // authorization-config supported, but feature-gates is alpha args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"StructuredAuthorizationConfiguration=true,PodNodeSelector=true"}, "authorization-config": {filepath.Join(constants.KubernetesAPIServerConfigDir, "authorization-config.yaml")}, }, }, { k8sVersion: "v1.29.0", // authorization-config supported, but feature-gates is alpha, upgrade scenario where `authorization-mode` is already set args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, "authorization-mode": {Values: []string{"Webhook,Node"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"PodNodeSelector=true"}, "authorization-mode": {"Node,RBAC,Webhook"}, }, }, { k8sVersion: "v1.30.0", // authorization-config supported, feature-gates is beta (enabled by default), upgrade scenario where `authorization-webhook-*` is already set args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, "authorization-webhook-version": {Values: []string{"v1"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"PodNodeSelector=true"}, "authorization-mode": {"Node,RBAC"}, "authorization-webhook-version": {"v1"}, }, }, { k8sVersion: "v1.30.0", // authorization-config supported, feature-gates is beta (enabled by default) args: map[string]k8s.ArgValues{ "enable-admission-plugins": {Values: []string{"NodeRestriction,PodNodeSelector"}}, "bind-address": {Values: []string{"127.0.0.1"}}, "audit-log-batch-max-size": {Values: []string{"2"}}, "feature-gates": {Values: []string{"PodNodeSelector=true"}}, }, expected: map[string][]string{ "enable-admission-plugins": {"NodeRestriction,PodNodeSelector"}, "bind-address": {"127.0.0.1"}, "audit-log-batch-max-size": {"2"}, "feature-gates": {"PodNodeSelector=true"}, "authorization-config": {filepath.Join(constants.KubernetesAPIServerConfigDir, "authorization-config.yaml")}, }, }, { args: map[string]k8s.ArgValues{ "proxy-client-key-file": {Values: []string{"front-proxy-client.key"}}, }, expectError: true, }, } configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configAPIServer := k8s.NewAPIServerConfig() suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) {}) for _, test := range tests { configAPIServer.TypedSpec().ExtraArgs = test.args if test.k8sVersion != "" { configAPIServer.TypedSpec().Image = fmt.Sprintf("k8s.gcr.io/kube-apiserver:%s", test.k8sVersion) } oldData := configAPIServer.TypedSpec().ExtraArgs suite.Require().NoError(suite.State().Update(suite.Ctx(), configAPIServer)) if test.expectError { // wait for some time to ensure that controller has picked the input time.Sleep(500 * time.Millisecond) // if the test expects an error, we should not have updated the extra args suite.Assert().Equal(oldData, configAPIServer.TypedSpec().ExtraArgs) continue } rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { apiServerPod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) assert.NotEmpty(apiServerPod.Spec.Containers) assertArg := func(arg string, equals []string) { actual := make([]string, 0, len(equals)) for _, param := range apiServerPod.Spec.Containers[0].Command { if strings.HasPrefix(param, fmt.Sprintf("--%s", arg)) { key, value, ok := strings.Cut(param, "=") assert.True(ok, "expected '=' in %s", param) assert.Equal("--"+arg, key) actual = append(actual, value) } } assert.Equal(len(equals), len(actual)) assert.ElementsMatch(equals, actual) } for k, v := range test.expected { assertArg(k, v) } }) } } func (suite *ControlPlaneStaticPodSuite) TestReconcileEnvironmentVariables() { configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configAPIServer := k8s.NewAPIServerConfig() suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) tests := []struct { env map[string]string expected []v1.EnvVar }{ { env: nil, expected: []v1.EnvVar{ { Name: "POD_IP", ValueFrom: &v1.EnvVarSource{ FieldRef: &v1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, }, }, { env: map[string]string{ "foo": "bar", "baz": "$(foo)", }, expected: []v1.EnvVar{ { Name: "POD_IP", ValueFrom: &v1.EnvVarSource{ FieldRef: &v1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, { Name: "baz", Value: "$$(foo)", }, { Name: "foo", Value: "bar", }, }, }, } rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) {}) for _, test := range tests { configAPIServer.TypedSpec().EnvironmentVariables = test.env suite.Require().NoError(suite.State().Update(suite.Ctx(), configAPIServer)) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { apiServerPod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) assert.ElementsMatch(test.expected, apiServerPod.Spec.Containers[0].Env) }) } } func (suite *ControlPlaneStaticPodSuite) TestReconcileAdvertisedAddressArg() { configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) configAPIServer := k8s.NewAPIServerConfig() configAPIServer.TypedSpec().AdvertisedAddress = "$(POD_IP)" suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { apiServerPod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) assert.NotEmpty(apiServerPod.Spec.Containers) assert.Contains(apiServerPod.Spec.Containers[0].Command, "--advertise-address=$(POD_IP)") }) configAPIServer.TypedSpec().AdvertisedAddress = "" suite.Assert().NoError(suite.State().Update(suite.Ctx(), configAPIServer)) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), k8s.APIServerID, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { apiServerPod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) assert.NotEmpty(apiServerPod.Spec.Containers) assert.NotContains(apiServerPod.Spec.Containers[0].Command, "--advertise-address") }) } func (suite *ControlPlaneStaticPodSuite) TestControlPlaneStaticPodsExceptScheduler() { configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configAPIServer := k8s.NewAPIServerConfig() configControllerManager := k8s.NewControllerManagerConfig() configControllerManager.TypedSpec().Enabled = true configScheduler := k8s.NewSchedulerConfig() configScheduler.TypedSpec().Enabled = true suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configControllerManager)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configScheduler)) rtestutils.AssertResources( suite.Ctx(), suite.T(), suite.State(), []resource.ID{ k8s.APIServerID, k8s.ControllerManagerID, k8s.SchedulerID, }, func(*k8s.StaticPod, *assert.Assertions) {}, ) configScheduler.TypedSpec().Enabled = false suite.Require().NoError(suite.State().Update(suite.Ctx(), configScheduler)) rtestutils.AssertResources( suite.Ctx(), suite.T(), suite.State(), []resource.ID{ k8s.APIServerID, k8s.ControllerManagerID, }, func(*k8s.StaticPod, *assert.Assertions) {}, ) } func (suite *ControlPlaneStaticPodSuite) TestReconcileStaticPodResources() { configStatus := k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID) secretStatus := k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID) configAPIServer := k8s.NewAPIServerConfig() configControllerManager := k8s.NewControllerManagerConfig() configControllerManager.TypedSpec().Enabled = true configScheduler := k8s.NewSchedulerConfig() configScheduler.TypedSpec().Enabled = true suite.Require().NoError(suite.State().Create(suite.Ctx(), configStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), secretStatus)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configAPIServer)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configControllerManager)) suite.Require().NoError(suite.State().Create(suite.Ctx(), configScheduler)) tests := []struct { resources k8s.Resources expected v1.ResourceRequirements expectedEnv v1.EnvVar }{ { resources: k8s.Resources{ Requests: map[string]string{ string(v1.ResourceCPU): "100m", string(v1.ResourceMemory): "256Mi", }, }, expected: v1.ResourceRequirements{ Requests: map[v1.ResourceName]apiresource.Quantity{ v1.ResourceCPU: apiresource.MustParse("100m"), v1.ResourceMemory: apiresource.MustParse("256Mi"), }, }, }, { resources: k8s.Resources{ Requests: map[string]string{ string(v1.ResourceCPU): "100m", string(v1.ResourceMemory): "256Mi", }, Limits: map[string]string{ string(v1.ResourceCPU): "1", string(v1.ResourceMemory): "1Gi", }, }, expected: v1.ResourceRequirements{ Requests: map[v1.ResourceName]apiresource.Quantity{ v1.ResourceCPU: apiresource.MustParse("100m"), v1.ResourceMemory: apiresource.MustParse("256Mi"), }, Limits: map[v1.ResourceName]apiresource.Quantity{ v1.ResourceCPU: apiresource.MustParse("1"), v1.ResourceMemory: apiresource.MustParse("1Gi"), }, }, expectedEnv: v1.EnvVar{ Name: "GOMEMLIMIT", Value: strconv.FormatInt(1024*1024*1024*k8sctrl.GoGCMemLimitPercentage/100, 10), }, }, } for _, test := range tests { configAPIServer.TypedSpec().Resources = test.resources configControllerManager.TypedSpec().Resources = test.resources configScheduler.TypedSpec().Resources = test.resources suite.Require().NoError(suite.State().Update(suite.Ctx(), configAPIServer)) suite.Require().NoError(suite.State().Update(suite.Ctx(), configControllerManager)) suite.Require().NoError(suite.State().Update(suite.Ctx(), configScheduler)) rtestutils.AssertResources( suite.Ctx(), suite.T(), suite.State(), []resource.ID{ k8s.APIServerID, k8s.ControllerManagerID, k8s.SchedulerID, }, func(staticPod *k8s.StaticPod, assert *assert.Assertions) { pod, err := k8sadapter.StaticPod(staticPod).Pod() suite.Require().NoError(err) assert.NotEmpty(pod.Spec.Containers) assert.Equal(test.expected, pod.Spec.Containers[0].Resources) if test.expectedEnv.Name != "" { assert.Contains(pod.Spec.Containers[0].Env, test.expectedEnv) } }, ) } } func TestControlPlaneStaticPodSuite(t *testing.T) { t.Parallel() suite.Run(t, &ControlPlaneStaticPodSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&k8sctrl.ControlPlaneStaticPodController{})) etcdService := v1alpha1.NewService("etcd") etcdService.TypedSpec().Running = true etcdService.TypedSpec().Healthy = true suite.Require().NoError(suite.State().Create(suite.Ctx(), etcdService)) }, AfterTearDown: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.State().Destroy(suite.Ctx(), v1alpha1.NewService("etcd").Metadata())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/control_plane_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/url" "slices" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type K8sControlPlaneSuite struct { ctest.DefaultSuite } // setupMachine creates a machine with given configuration, waits for it to become ready, // and returns API server's spec. func (suite *K8sControlPlaneSuite) setupMachine(cfg *config.MachineConfig) { suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AdmissionControlConfigID}, func(*k8s.AdmissionControlConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuditPolicyConfigID}, func(*k8s.AuditPolicyConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(*k8s.AuthorizationConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(*k8s.APIServerConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControllerManagerConfigID}, func(*k8s.ControllerManagerConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.SchedulerConfigID}, func(*k8s.SchedulerConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(*k8s.BootstrapManifestsConfig, *assert.Assertions) {}) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ExtraManifestsConfigID}, func(*k8s.ExtraManifestsConfig, *assert.Assertions) {}) } func (suite *K8sControlPlaneSuite) TestReconcileDefaults() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(apiServer *k8s.APIServerConfig, assert *assert.Assertions) { apiServerCfg := apiServer.TypedSpec() assert.Empty(apiServerCfg.CloudProvider) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControllerManagerConfigID}, func(controllerManager *k8s.ControllerManagerConfig, assert *assert.Assertions) { assert.Empty(controllerManager.TypedSpec().CloudProvider) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(bootstrapConfig *k8s.BootstrapManifestsConfig, assert *assert.Assertions) { assert.Equal("10.96.0.10", bootstrapConfig.TypedSpec().DNSServiceIP) assert.Equal("", bootstrapConfig.TypedSpec().DNSServiceIPv6) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers, authorizationConfig.TypedSpec().Config) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileEmptyAuthorizationConfigForK8sLessThanv128() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ContainerImage: "k8s.gcr.io/kube-apiserver:v1.28.0", AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{}, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResource[*k8s.AuthorizationConfig](suite.Ctx(), suite.T(), suite.State(), k8s.AuthorizationConfigID, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(&k8s.AuthorizationConfigSpec{ Image: "k8s.gcr.io/kube-apiserver:v1.28.0", }, authorizationConfig.TypedSpec()) }) } func (suite *K8sControlPlaneSuite) TestReconcileEmptyAuthorizationConfigAuthorizers() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{}, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers, authorizationConfig.TypedSpec().Config) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAuthorizers() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{ { AuthorizerType: "Webhook", AuthorizerName: "webhook", AuthorizerWebhook: v1alpha1.Unstructured{ Object: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, }, }, }, }, }, ), ) suite.setupMachine(cfg) expectedAuthorizers := slices.Concat(v1alpha1.APIServerDefaultAuthorizationConfigAuthorizers, []k8s.AuthorizationAuthorizersSpec{ { Type: "Webhook", Name: "webhook", Webhook: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(expectedAuthorizers, authorizationConfig.TypedSpec().Config) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAuthorizersWithDefaultsSet() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{ { AuthorizerType: "RBAC", AuthorizerName: "foo", }, { AuthorizerType: "Webhook", AuthorizerName: "webhook", AuthorizerWebhook: v1alpha1.Unstructured{ Object: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, }, { AuthorizerType: "Node", AuthorizerName: "bar", }, }, }, }, }, ), ) suite.setupMachine(cfg) expectedAuthorizers := []k8s.AuthorizationAuthorizersSpec{ { Type: "RBAC", Name: "foo", }, { Type: "Webhook", Name: "webhook", Webhook: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, { Type: "Node", Name: "bar", }, } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(expectedAuthorizers, authorizationConfig.TypedSpec().Config) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileAdditionalAuthorizationConfigAuthorizersWithOnlyNodeSet() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{ { AuthorizerType: "Node", AuthorizerName: "foo", }, { AuthorizerType: "Webhook", AuthorizerName: "webhook", AuthorizerWebhook: v1alpha1.Unstructured{ Object: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, }, }, }, }, }, ), ) suite.setupMachine(cfg) expectedAuthorizers := []k8s.AuthorizationAuthorizersSpec{ { Type: "Node", Name: "foo", }, { Type: "RBAC", Name: "rbac", }, { Type: "Webhook", Name: "webhook", Webhook: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.AuthorizationConfigID}, func(authorizationConfig *k8s.AuthorizationConfig, assert *assert.Assertions) { assert.Equal(expectedAuthorizers, authorizationConfig.TypedSpec().Config) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileTransitionWorker() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.setupMachine(cfg) cfg.Container().RawV1Alpha1().MachineConfig.MachineType = "worker" suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) rtestutils.AssertNoResource[*k8s.AdmissionControlConfig](suite.Ctx(), suite.T(), suite.State(), k8s.AdmissionControlConfigID) rtestutils.AssertNoResource[*k8s.AuditPolicyConfig](suite.Ctx(), suite.T(), suite.State(), k8s.AuditPolicyConfigID) rtestutils.AssertNoResource[*k8s.AuthorizationConfig](suite.Ctx(), suite.T(), suite.State(), k8s.AuthorizationConfigID) rtestutils.AssertNoResource[*k8s.APIServerConfig](suite.Ctx(), suite.T(), suite.State(), k8s.APIServerConfigID) rtestutils.AssertNoResource[*k8s.ControllerManagerConfig](suite.Ctx(), suite.T(), suite.State(), k8s.ControllerManagerConfigID) rtestutils.AssertNoResource[*k8s.SchedulerConfig](suite.Ctx(), suite.T(), suite.State(), k8s.SchedulerConfigID) rtestutils.AssertNoResource[*k8s.BootstrapManifestsConfig](suite.Ctx(), suite.T(), suite.State(), k8s.BootstrapManifestsConfigID) rtestutils.AssertNoResource[*k8s.ExtraManifestsConfig](suite.Ctx(), suite.T(), suite.State(), k8s.ExtraManifestsConfigID) } func (suite *K8sControlPlaneSuite) TestReconcileIPv6() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ PodSubnet: []string{constants.DefaultIPv6PodNet}, ServiceSubnet: []string{constants.DefaultIPv6ServiceNet}, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(bootstrapConfig *k8s.BootstrapManifestsConfig, assert *assert.Assertions) { assert.Equal("", bootstrapConfig.TypedSpec().DNSServiceIP) assert.Equal("fc00:db8:20::a", bootstrapConfig.TypedSpec().DNSServiceIPv6) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileDualStack() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ PodSubnet: []string{constants.DefaultIPv4PodNet, constants.DefaultIPv6PodNet}, ServiceSubnet: []string{constants.DefaultIPv4ServiceNet, constants.DefaultIPv6ServiceNet}, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(bootstrapConfig *k8s.BootstrapManifestsConfig, assert *assert.Assertions) { assert.Equal("10.96.0.10", bootstrapConfig.TypedSpec().DNSServiceIP) assert.Equal("fc00:db8:20::a", bootstrapConfig.TypedSpec().DNSServiceIPv6) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileExtraVolumes() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ExtraVolumesConfig: []v1alpha1.VolumeMountConfig{ { VolumeHostPath: "/var/lib", VolumeMountPath: "/var/foo/", }, { VolumeHostPath: "/var/lib/a.foo", VolumeMountPath: "/var/foo/b.foo", }, }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(apiServer *k8s.APIServerConfig, assert *assert.Assertions) { apiServerCfg := apiServer.TypedSpec() assert.Equal( []k8s.ExtraVolume{ { Name: "var-foo", HostPath: "/var/lib", MountPath: "/var/foo/", ReadOnly: false, }, { Name: "var-foo-b-foo", HostPath: "/var/lib/a.foo", MountPath: "/var/foo/b.foo", ReadOnly: false, }, }, apiServerCfg.ExtraVolumes, ) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileEnvironment() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ EnvConfig: v1alpha1.Env{ "HTTP_PROXY": "foo", }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(apiServer *k8s.APIServerConfig, assert *assert.Assertions) { apiServerCfg := apiServer.TypedSpec() assert.Equal( map[string]string{ "HTTP_PROXY": "foo", }, apiServerCfg.EnvironmentVariables, ) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileResources() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Requests: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": "100m", "memory": "1Gi", }, }, Limits: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": 2, "memory": "1500Mi", }, }, }, }, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Requests: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": "50m", "memory": "500Mi", }, }, Limits: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": 1, "memory": "1000Mi", }, }, }, }, SchedulerConfig: &v1alpha1.SchedulerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Requests: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": "150m", "memory": "2Gi", }, }, Limits: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": 3, "memory": "2000Mi", }, }, }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(apiServer *k8s.APIServerConfig, assert *assert.Assertions) { apiServerCfg := apiServer.TypedSpec() assert.Equal( k8s.Resources{ Requests: map[string]string{ "cpu": "100m", "memory": "1Gi", }, Limits: map[string]string{ "cpu": "2", "memory": "1500Mi", }, }, apiServerCfg.Resources, ) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControllerManagerConfigID}, func(controllerManager *k8s.ControllerManagerConfig, assert *assert.Assertions) { controllerManagerCfg := controllerManager.TypedSpec() assert.Equal( k8s.Resources{ Requests: map[string]string{ "cpu": "50m", "memory": "500Mi", }, Limits: map[string]string{ "cpu": "1", "memory": "1000Mi", }, }, controllerManagerCfg.Resources, ) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.SchedulerConfigID}, func(scheduler *k8s.SchedulerConfig, assert *assert.Assertions) { schedulerCfg := scheduler.TypedSpec() assert.Equal( k8s.Resources{ Requests: map[string]string{ "cpu": "150m", "memory": "2Gi", }, Limits: map[string]string{ "cpu": "3", "memory": "2000Mi", }, }, schedulerCfg.Resources, ) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileExternalCloudProvider() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalEnabled: new(true), ExternalManifests: []string{ "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.APIServerConfigID}, func(apiServer *k8s.APIServerConfig, assert *assert.Assertions) { apiServerCfg := apiServer.TypedSpec() assert.Equal(k8sctrl.CloudProviderExternal, apiServerCfg.CloudProvider) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControllerManagerConfigID}, func(controllerManager *k8s.ControllerManagerConfig, assert *assert.Assertions) { assert.Equal(k8sctrl.CloudProviderExternal, controllerManager.TypedSpec().CloudProvider) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ExtraManifestsConfigID}, func(extraManifests *k8s.ExtraManifestsConfig, assert *assert.Assertions) { assert.Equal( &k8s.ExtraManifestsConfigSpec{ ExtraManifests: []k8s.ExtraManifest{ { Name: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", URL: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", Priority: "30", }, { Name: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", URL: "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", Priority: "30", }, }, }, extraManifests.TypedSpec()) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileInlineManifests() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterInlineManifests: v1alpha1.ClusterInlineManifests{ { InlineManifestName: "namespace-ci", InlineManifestContents: strings.TrimSpace( ` apiVersion: v1 kind: Namespace metadata: name: ci `, ), }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ExtraManifestsConfigID}, func(extraManifests *k8s.ExtraManifestsConfig, assert *assert.Assertions) { assert.Equal( &k8s.ExtraManifestsConfigSpec{ ExtraManifests: []k8s.ExtraManifest{ { Name: "namespace-ci", Priority: "99", InlineManifest: "apiVersion: v1\nkind: Namespace\nmetadata:\n\tname: ci", }, }, }, extraManifests.TypedSpec()) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileKubeProxyMode() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(cfg *k8s.BootstrapManifestsConfig, assert *assert.Assertions) { assert.Contains( cfg.TypedSpec().ProxyArgs, "--proxy-mode=nftables", ) }, ) } func (suite *K8sControlPlaneSuite) TestReconcileKubeProxyModeLegacy() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ProxyConfig: &v1alpha1.ProxyConfig{ ContainerImage: constants.KubeProxyImage + ":v1.30.0", }, }, }, ), ) suite.setupMachine(cfg) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.BootstrapManifestsConfigID}, func(cfg *k8s.BootstrapManifestsConfig, assert *assert.Assertions) { assert.Contains( cfg.TypedSpec().ProxyArgs, "--proxy-mode=iptables", ) }, ) } func TestK8sControlPlaneSuite(t *testing.T) { t.Parallel() suite.Run(t, &K8sControlPlaneSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneAPIServerController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneAdmissionControlController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneAuditPolicyController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneAuthorizationController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneBootstrapManifestsController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneControllerManagerController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneExtraManifestsController())) suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewControlPlaneSchedulerController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // EndpointController looks up control plane endpoints. type EndpointController struct{} // Name implements controller.Controller interface. func (ctrl *EndpointController) Name() string { return "k8s.EndpointController" } // Inputs implements controller.Controller interface. func (ctrl *EndpointController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *EndpointController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.EndpointType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if err := r.UpdateInputs([]controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } machineTypeRes, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting machine type: %w", err) } machineType := machineTypeRes.MachineType() switch machineType { //nolint:exhaustive case machine.TypeWorker: if err = ctrl.watchEndpointsOnWorker(ctx, r, logger); err != nil { return err } case machine.TypeControlPlane, machine.TypeInit: if err = ctrl.watchEndpointsOnControlPlane(ctx, r, logger); err != nil { return err } } r.ResetRestartBackoff() } } func (ctrl *EndpointController) watchEndpointsOnWorker(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { logger.Debug("waiting for kubelet client config", zap.String("file", constants.KubeletKubeconfig)) if err := conditions.WaitForKubeconfigReady(constants.KubeletKubeconfig).Wait(ctx); err != nil { return err } client, err := kubernetes.NewClientFromKubeletKubeconfig() if err != nil { return fmt.Errorf("error building Kubernetes client: %w", err) } defer client.Close() //nolint:errcheck r.QueueReconcile() for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } if err = ctrl.watchKubernetesEndpointSlices(ctx, r, logger, client); err != nil { return err } } } //nolint:gocyclo func (ctrl *EndpointController) watchEndpointsOnControlPlane(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if err := r.UpdateInputs([]controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesType, ID: optional.Some(secrets.KubernetesID), Kind: controller.InputWeak, }, }); err != nil { return err } r.QueueReconcile() for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } secretsResources, err := safe.ReaderGetByID[*secrets.Kubernetes](ctx, r, secrets.KubernetesID) if err != nil { if state.IsNotFoundError(err) { return nil } return err } secrets := secretsResources.TypedSpec() kubeconfig, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { // using here kubeconfig with cluster control plane endpoint, as endpoint discovery should work before local API server is ready return clientcmd.Load([]byte(secrets.AdminKubeconfig)) }) if err != nil { return fmt.Errorf("error loading kubeconfig: %w", err) } // closure to capture the deferred close on client watch := func() error { client, err := kubernetes.NewForConfig(kubeconfig) if err != nil { return fmt.Errorf("error building Kubernetes client: %w", err) } defer client.Close() //nolint:errcheck if err = ctrl.watchKubernetesEndpointSlices(ctx, r, logger, client); err != nil { return err } return nil } if err = watch(); err != nil { return err } } } //nolint:gocyclo func (ctrl *EndpointController) updateEndpointsResource( ctx context.Context, r controller.Runtime, logger *zap.Logger, object *discoveryv1.EndpointSlice, ) error { var addrs []netip.Addr for _, endpoint := range object.Endpoints { for _, addr := range endpoint.Addresses { ip, err := netip.ParseAddr(addr) if err == nil { addrs = append(addrs, ip) } } } slices.SortFunc(addrs, func(a, b netip.Addr) int { return a.Compare(b) }) if err := safe.WriterModify(ctx, r, k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, k8s.ControlPlaneAPIServerEndpointsID), func(r *k8s.Endpoint) error { if !slices.Equal(r.TypedSpec().Addresses, addrs) { logger.Debug("updated controlplane endpoints", zap.Any("endpoints", addrs)) } var addrIPv4, addrIPv6 []netip.Addr for _, addr := range r.TypedSpec().Addresses { switch { case addr.Is4(): addrIPv4 = append(addrIPv4, addr) case addr.Is6(): addrIPv6 = append(addrIPv6, addr) } } switch object.AddressType { case discoveryv1.AddressTypeIPv4: addrIPv4 = addrs case discoveryv1.AddressTypeIPv6: addrIPv6 = addrs case discoveryv1.AddressTypeFQDN: fallthrough default: // ignore all other cases } r.TypedSpec().Addresses = slices.Concat(addrIPv4, addrIPv6) return nil }, ); err != nil { return fmt.Errorf("error updating endpoints: %w", err) } return nil } func (ctrl *EndpointController) watchKubernetesEndpointSlices(ctx context.Context, r controller.Runtime, logger *zap.Logger, client *kubernetes.Client) error { // abort the watch on any return from this function ctx, cancel := context.WithCancel(ctx) defer cancel() notifyCh, watchCloser, err := kubernetesEndpointSliceWatcher(ctx, logger, client) if err != nil { return fmt.Errorf("error watching Kubernetes endpoint slice: %w", err) } defer func() { cancel() // cancel the context before stopping the watcher watchCloser() }() for { select { case endpoints := <-notifyCh: if err = ctrl.updateEndpointsResource(ctx, r, logger, endpoints); err != nil { return err } case <-ctx.Done(): return nil case <-r.EventCh(): // something got updated, probably kubeconfig, restart the watch r.QueueReconcile() return nil } } } func kubernetesEndpointSliceWatcher(ctx context.Context, logger *zap.Logger, client *kubernetes.Client) (chan *discoveryv1.EndpointSlice, func(), error) { informerFactory := informers.NewSharedInformerFactoryWithOptions( client.Clientset, constants.KubernetesInformerDefaultResyncPeriod, informers.WithNamespace(corev1.NamespaceDefault), informers.WithTweakListOptions(func(options *v1.ListOptions) { options.FieldSelector = fields.OneTermEqualSelector("metadata.name", "kubernetes").String() }), ) notifyCh := make(chan *discoveryv1.EndpointSlice, 1) informer := informerFactory.Discovery().V1().EndpointSlices().Informer() if err := informer.SetWatchErrorHandler(func(r *cache.Reflector, err error) { logger.Error("kubernetes endpoint watch error", zap.Error(err)) }); err != nil { return nil, nil, fmt.Errorf("error setting watch error handler: %w", err) } if _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj any) { notifyCh <- obj.(*discoveryv1.EndpointSlice) }, DeleteFunc: func(_ any) { notifyCh <- &discoveryv1.EndpointSlice{} }, UpdateFunc: func(_, obj any) { notifyCh <- obj.(*discoveryv1.EndpointSlice) }, }); err != nil { return nil, nil, fmt.Errorf("error adding watch event handler: %w", err) } informerFactory.Start(ctx.Done()) return notifyCh, informerFactory.Shutdown, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/extra_manifest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/http" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-getter/v2" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "go.uber.org/zap" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ExtraManifestController renders manifests based on templates and config/secrets. type ExtraManifestController struct{} // Name implements controller.Controller interface. func (ctrl *ExtraManifestController) Name() string { return "k8s.ExtraManifestController" } // Inputs implements controller.Controller interface. func (ctrl *ExtraManifestController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.ExtraManifestsConfigType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ExtraManifestController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.ManifestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ExtraManifestController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // wait for network to be ready as networking is required to download extra manifests networkResource, err := safe.ReaderGetByID[*network.Status](ctx, r, network.StatusID) if err != nil { if state.IsNotFoundError(err) { continue } return err } networkStatus := networkResource.TypedSpec() if !(networkStatus.AddressReady && networkStatus.ConnectivityReady) { continue } configResource, err := safe.ReaderGetByID[*k8s.ExtraManifestsConfig](ctx, r, k8s.ExtraManifestsConfigID) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } config := *configResource.TypedSpec() var multiErr *multierror.Error presentManifests := map[resource.ID]struct{}{} for _, manifest := range config.ExtraManifests { var id resource.ID id, err = ctrl.process(ctx, r, logger, manifest) if err != nil { multiErr = multierror.Append(multiErr, err) } presentManifests[id] = struct{}{} } if multiErr.ErrorOrNil() != nil { return multiErr.ErrorOrNil() } allManifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing extra manifests: %w", err) } for _, manifest := range allManifests.Items { if manifest.Metadata().Owner() != ctrl.Name() { continue } if _, exists := presentManifests[manifest.Metadata().ID()]; !exists { if err = r.Destroy(ctx, manifest.Metadata()); err != nil { return fmt.Errorf("error cleaning up extra manifest: %w", err) } } } r.ResetRestartBackoff() } } func (ctrl *ExtraManifestController) process(ctx context.Context, r controller.Runtime, logger *zap.Logger, manifest k8s.ExtraManifest) (id resource.ID, err error) { id = fmt.Sprintf("%s-%s", manifest.Priority, manifest.Name) // inline manifests don't require download if manifest.InlineManifest != "" { return id, ctrl.processInline(ctx, r, manifest, id) } return id, ctrl.processURL(ctx, r, logger, manifest, id) } func (ctrl *ExtraManifestController) processURL(ctx context.Context, r controller.Runtime, logger *zap.Logger, manifest k8s.ExtraManifest, id resource.ID) (err error) { var tmpDir string tmpDir, err = os.MkdirTemp("", "talos") if err != nil { return err } defer os.RemoveAll(tmpDir) //nolint:errcheck // I wish we never used go-getter package, as it doesn't allow downloading into memory. // But there's not much we can do about it right now, as it supports lots of magic // users might rely upon. // Disable netrc since we don't have getent installed, and most likely // never will. httpGetter := &getter.HttpGetter{ Netrc: false, Client: &http.Client{ Transport: httpdefaults.PatchTransport(cleanhttp.DefaultTransport()), }, } httpGetter.Header = make(http.Header) for k, v := range manifest.ExtraHeaders { httpGetter.Header.Add(k, v) } client := &getter.Client{ Getters: []getter.Getter{ httpGetter, }, } dst := filepath.Join(tmpDir, "manifest.yaml") if _, err = client.Get(ctx, &getter.Request{ Src: manifest.URL, Dst: dst, Pwd: tmpDir, GetMode: getter.ModeFile, }); err != nil { err = fmt.Errorf("error downloading %q: %w", manifest.URL, err) return err } logger.Sugar().Infof("downloaded manifest %q", manifest.URL) var contents []byte contents, err = os.ReadFile(dst) if err != nil { return err } if err = safe.WriterModify(ctx, r, k8s.NewManifest(k8s.ControlPlaneNamespaceName, id), func(r *k8s.Manifest) error { return k8sadapter.Manifest(r).SetYAML(contents) }); err != nil { err = fmt.Errorf("error updating manifests: %w", err) return err } return nil } func (ctrl *ExtraManifestController) processInline(ctx context.Context, r controller.Runtime, manifest k8s.ExtraManifest, id resource.ID) error { err := safe.WriterModify( ctx, r, k8s.NewManifest(k8s.ControlPlaneNamespaceName, id), func(r *k8s.Manifest) error { return k8sadapter.Manifest(r).SetYAML([]byte(manifest.InlineManifest)) }, ) if err != nil { return fmt.Errorf("error updating manifests: %w", err) } return nil } //nolint:dupl func (ctrl *ExtraManifestController) teardownAll(ctx context.Context, r controller.Runtime) error { manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing extra manifests: %w", err) } for _, manifest := range manifests.Items { if manifest.Metadata().Owner() != ctrl.Name() { continue } if err = r.Destroy(ctx, manifest.Metadata()); err != nil { return fmt.Errorf("error destroying extra manifest: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/extra_manifest_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "context" "slices" "strings" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ExtraManifestSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *ExtraManifestSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(&k8sctrl.ExtraManifestController{})) suite.startRuntime() } func (suite *ExtraManifestSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } //nolint:dupl func (suite *ExtraManifestSuite) assertExtraManifests(manifests []string) error { resources, err := suite.state.List( suite.ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined), ) if err != nil { return err } ids := xslices.Map(resources.Items, func(r resource.Resource) string { return r.Metadata().ID() }) if !slices.Equal(manifests, ids) { return retry.ExpectedErrorf("expected %q, got %q", manifests, ids) } return nil } func (suite *ExtraManifestSuite) TestReconcileInlineManifests() { configExtraManifests := k8s.NewExtraManifestsConfig() *configExtraManifests.TypedSpec() = k8s.ExtraManifestsConfigSpec{ ExtraManifests: []k8s.ExtraManifest{ { Name: "namespaces", Priority: "99", InlineManifest: strings.TrimSpace( ` apiVersion: v1 kind: Namespace metadata: name: ci --- apiVersion: v1 kind: Namespace metadata: name: build `, ), }, }, } statusNetwork := network.NewStatus(network.NamespaceName, network.StatusID) statusNetwork.TypedSpec().AddressReady = true statusNetwork.TypedSpec().ConnectivityReady = true suite.Require().NoError(suite.state.Create(suite.ctx, configExtraManifests)) suite.Require().NoError(suite.state.Create(suite.ctx, statusNetwork)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertExtraManifests( []string{ "99-namespaces", }, ) }, ), ) r, err := suite.state.Get( suite.ctx, resource.NewMetadata( k8s.ControlPlaneNamespaceName, k8s.ManifestType, "99-namespaces", resource.VersionUndefined, ), ) suite.Require().NoError(err) manifest := r.(*k8s.Manifest) //nolint:forcetypeassert suite.Assert().Len(k8sadapter.Manifest(manifest).Objects(), 2) suite.Assert().Equal("ci", k8sadapter.Manifest(manifest).Objects()[0].GetName()) suite.Assert().Equal("build", k8sadapter.Manifest(manifest).Objects()[1].GetName()) } func (suite *ExtraManifestSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestExtraManifestSuite(t *testing.T) { t.Parallel() suite.Run(t, new(ExtraManifestSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/apiserver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" apiserverv1 "k8s.io/apiserver/pkg/apis/apiserver/v1" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // APIServerEncryptionConfig returns the encryption configuration for the API server. func APIServerEncryptionConfig(rootK8sSecrets *secrets.KubernetesRootSpec) runtime.Object { obj := apiserverv1.EncryptionConfiguration{ TypeMeta: v1.TypeMeta{ Kind: "EncryptionConfig", APIVersion: apiserverv1.SchemeGroupVersion.Version, }, Resources: []apiserverv1.ResourceConfiguration{ { Resources: []string{"secrets"}, Providers: []apiserverv1.ProviderConfiguration{}, }, }, } if rootK8sSecrets.SecretboxEncryptionSecret != "" { obj.Resources[0].Providers = append(obj.Resources[0].Providers, apiserverv1.ProviderConfiguration{ Secretbox: &apiserverv1.SecretboxConfiguration{ Keys: []apiserverv1.Key{ { Name: "key2", Secret: rootK8sSecrets.SecretboxEncryptionSecret, }, }, }, }) } if rootK8sSecrets.AESCBCEncryptionSecret != "" { obj.Resources[0].Providers = append(obj.Resources[0].Providers, apiserverv1.ProviderConfiguration{ AESCBC: &apiserverv1.AESConfiguration{ Keys: []apiserverv1.Key{ { Name: "key1", Secret: rootK8sSecrets.AESCBCEncryptionSecret, }, }, }, }) } obj.Resources[0].Providers = append(obj.Resources[0].Providers, apiserverv1.ProviderConfiguration{ Identity: &apiserverv1.IdentityConfiguration{}, }) return &obj } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/coredns.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( "cmp" "fmt" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // CoreDNSService returns the CoreDNS service object. func CoreDNSService(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { obj := &corev1.Service{ TypeMeta: v1.TypeMeta{ Kind: "Service", APIVersion: corev1.SchemeGroupVersion.Version, }, ObjectMeta: v1.ObjectMeta{ Name: "kube-dns", Namespace: "kube-system", Annotations: map[string]string{ "prometheus.io/scrape": "true", "prometheus.io/port": "9153", }, Labels: map[string]string{ "k8s-app": "kube-dns", "kubernetes.io/cluster-service": "true", "kubernetes.io/name": "CoreDNS", }, }, Spec: corev1.ServiceSpec{ Selector: map[string]string{ "k8s-app": "kube-dns", }, ClusterIP: cmp.Or(spec.DNSServiceIP, spec.DNSServiceIPv6), Ports: []corev1.ServicePort{ { Name: "dns", Port: 53, Protocol: corev1.ProtocolUDP, TargetPort: intstr.FromInt(53), }, { Name: "dns-tcp", Port: 53, Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromInt(53), }, { Name: "metrics", Port: 9153, Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromInt(9153), }, }, }, } if spec.DNSServiceIP != "" { obj.Spec.ClusterIPs = append(obj.Spec.ClusterIPs, spec.DNSServiceIP) obj.Spec.IPFamilies = append(obj.Spec.IPFamilies, corev1.IPv4Protocol) } if spec.DNSServiceIPv6 != "" { obj.Spec.ClusterIPs = append(obj.Spec.ClusterIPs, spec.DNSServiceIPv6) obj.Spec.IPFamilies = append(obj.Spec.IPFamilies, corev1.IPv6Protocol) } if spec.DNSServiceIP != "" && spec.DNSServiceIPv6 != "" { obj.Spec.IPFamilyPolicy = new(corev1.IPFamilyPolicyRequireDualStack) } else { obj.Spec.IPFamilyPolicy = new(corev1.IPFamilyPolicySingleStack) } return obj } // CoreDNSServiceAccount returns the CoreDNS service account object. func CoreDNSServiceAccount() runtime.Object { return &corev1.ServiceAccount{ TypeMeta: v1.TypeMeta{ Kind: "ServiceAccount", APIVersion: corev1.SchemeGroupVersion.Version, }, ObjectMeta: v1.ObjectMeta{ Name: "coredns", Namespace: "kube-system", }, } } // CoreDNSClusterRoleBinding returns the CoreDNS ClusterRoleBinding object. func CoreDNSClusterRoleBinding() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system:coredns", Labels: map[string]string{ "kubernetes.io/bootstrapping": "rbac-defaults", }, Annotations: map[string]string{ "rbac.authorization.kubernetes.io/autoupdate": "true", }, }, RoleRef: rbacv1.RoleRef{ APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "system:coredns", }, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", Name: "coredns", Namespace: "kube-system", }, }, } } // CoreDNSClusterRole returns the CoreDNS ClusterRole object. func CoreDNSClusterRole() runtime.Object { return &rbacv1.ClusterRole{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRole", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system:coredns", Labels: map[string]string{ "kubernetes.io/bootstrapping": "rbac-defaults", }, }, Rules: []rbacv1.PolicyRule{ { APIGroups: []string{""}, Resources: []string{"endpoints", "services", "pods", "namespaces"}, Verbs: []string{"list", "watch"}, }, { APIGroups: []string{"discovery.k8s.io"}, Resources: []string{"endpointslices"}, Verbs: []string{"list", "watch"}, }, }, } } // CoreDNSConfigMap returns the CoreDNS ConfigMap object. func CoreDNSConfigMap(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { coreDNSConfig := fmt.Sprintf(`.:53 { errors health { lameduck 5s } ready log . { class error } prometheus :9153 kubernetes %s in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } forward . /etc/resolv.conf { max_concurrent 1000 } cache 30`, spec.ClusterDomain) if spec.ClusterDomain != "" { coreDNSConfig += fmt.Sprintf(` { disable success %s disable denial %s } `, spec.ClusterDomain, spec.ClusterDomain) } else { coreDNSConfig += "\n" } coreDNSConfig += ` loop reload loadbalance } ` return &corev1.ConfigMap{ TypeMeta: v1.TypeMeta{ Kind: "ConfigMap", APIVersion: corev1.SchemeGroupVersion.Version, }, ObjectMeta: v1.ObjectMeta{ Name: "coredns", Namespace: "kube-system", }, Data: map[string]string{ "Corefile": coreDNSConfig, }, } } // CoreDNSDeployment returns the CoreDNS Deployment object. func CoreDNSDeployment(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { return &appsv1.Deployment{ TypeMeta: v1.TypeMeta{ Kind: "Deployment", APIVersion: appsv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "coredns", Namespace: "kube-system", Labels: map[string]string{ "k8s-app": "kube-dns", "kubernetes.io/name": "CoreDNS", }, }, Spec: appsv1.DeploymentSpec{ Replicas: new(int32(2)), Strategy: appsv1.DeploymentStrategy{ Type: appsv1.RollingUpdateDeploymentStrategyType, RollingUpdate: &appsv1.RollingUpdateDeployment{ MaxUnavailable: new(intstr.FromInt(1)), }, }, Selector: &v1.LabelSelector{ MatchLabels: map[string]string{ "k8s-app": "kube-dns", }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: map[string]string{ "k8s-app": "kube-dns", }, }, Spec: corev1.PodSpec{ NodeSelector: map[string]string{ "kubernetes.io/os": "linux", }, Affinity: &corev1.Affinity{ PodAntiAffinity: &corev1.PodAntiAffinity{ PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ { Weight: 100, PodAffinityTerm: corev1.PodAffinityTerm{ LabelSelector: &v1.LabelSelector{ MatchExpressions: []v1.LabelSelectorRequirement{ { Key: "k8s-app", Operator: v1.LabelSelectorOpIn, Values: []string{"kube-dns"}, }, }, }, TopologyKey: "kubernetes.io/hostname", }, }, }, }, }, ServiceAccountName: "coredns", PriorityClassName: "system-cluster-critical", Tolerations: []corev1.Toleration{ { Key: "node-role.kubernetes.io/control-plane", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoSchedule, }, { Key: "node.cloudprovider.kubernetes.io/uninitialized", Operator: corev1.TolerationOpExists, Effect: corev1.TaintEffectNoSchedule, }, }, Containers: []corev1.Container{ { Name: "coredns", Image: spec.CoreDNSImage, ImagePullPolicy: corev1.PullIfNotPresent, Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ corev1.ResourceMemory: resource.MustParse("170Mi"), }, Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("100m"), corev1.ResourceMemory: resource.MustParse("70Mi"), }, }, Env: []corev1.EnvVar{ { Name: "GOMEMLIMIT", Value: "161MiB", }, }, Args: []string{"-conf", "/etc/coredns/Corefile"}, VolumeMounts: []corev1.VolumeMount{ { Name: "config-volume", MountPath: "/etc/coredns", ReadOnly: true, }, }, Ports: []corev1.ContainerPort{ { Name: "dns", Protocol: corev1.ProtocolUDP, ContainerPort: 53, }, { Name: "dns-tcp", Protocol: corev1.ProtocolTCP, ContainerPort: 53, }, { Name: "metrics", Protocol: corev1.ProtocolTCP, ContainerPort: 9153, }, }, LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: "/health", Port: intstr.FromInt(8080), Scheme: corev1.URISchemeHTTP, }, }, InitialDelaySeconds: 60, TimeoutSeconds: 5, SuccessThreshold: 1, FailureThreshold: 5, }, ReadinessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ HTTPGet: &corev1.HTTPGetAction{ Path: "/ready", Port: intstr.FromInt(8181), Scheme: corev1.URISchemeHTTP, }, }, }, SecurityContext: &corev1.SecurityContext{ AllowPrivilegeEscalation: new(false), Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_BIND_SERVICE"}, Drop: []corev1.Capability{"ALL"}, }, ReadOnlyRootFilesystem: new(true), }, }, }, DNSPolicy: corev1.DNSDefault, Volumes: []corev1.Volume{ { Name: "config-volume", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "coredns", }, Items: []corev1.KeyToPath{ { Key: "Corefile", Path: "Corefile", }, }, }, }, }, }, }, }, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/crds.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" ) // TalosServiceAccountCRDTemplate returns the template of the CRD which // allows injecting Talos API credentials for Kubernetes pods. func TalosServiceAccountCRDTemplate() runtime.Object { return &apiextensions.CustomResourceDefinition{ TypeMeta: v1.TypeMeta{ APIVersion: apiextensions.SchemeGroupVersion.String(), Kind: "CustomResourceDefinition", }, ObjectMeta: v1.ObjectMeta{ Name: constants.ServiceAccountResourcePlural + "." + constants.ServiceAccountResourceGroup, }, Spec: apiextensions.CustomResourceDefinitionSpec{ Conversion: &apiextensions.CustomResourceConversion{ Strategy: apiextensions.NoneConverter, }, Group: constants.ServiceAccountResourceGroup, Names: apiextensions.CustomResourceDefinitionNames{ Kind: constants.ServiceAccountResourceKind, ListKind: constants.ServiceAccountResourceKind + "List", Plural: constants.ServiceAccountResourcePlural, Singular: constants.ServiceAccountResourceSingular, ShortNames: []string{constants.ServiceAccountResourceShortName}, }, Scope: apiextensions.NamespaceScoped, Versions: []apiextensions.CustomResourceDefinitionVersion{ { Name: constants.ServiceAccountResourceVersion, Served: true, Storage: true, Schema: &apiextensions.CustomResourceValidation{ OpenAPIV3Schema: &apiextensions.JSONSchemaProps{ Type: "object", Properties: map[string]apiextensions.JSONSchemaProps{ "spec": { Type: "object", Properties: map[string]apiextensions.JSONSchemaProps{ "roles": { Type: "array", Items: &apiextensions.JSONSchemaPropsOrArray{ Schema: &apiextensions.JSONSchemaProps{ Type: "string", }, }, }, }, }, "status": { Type: "object", Properties: map[string]apiextensions.JSONSchemaProps{ "failureReason": { Type: "string", }, }, }, }, }, }, }, }, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/csr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) // CSRNodeBootstrapTemplate returns the CSR node bootstrap template. func CSRNodeBootstrapTemplate() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system-bootstrap-node-bootstrapper", }, Subjects: []rbacv1.Subject{ { Kind: "Group", Name: "system:bootstrappers:nodes", APIGroup: rbacv1.GroupName, }, { Kind: "Group", Name: "system:nodes", APIGroup: rbacv1.GroupName, }, }, RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", Name: "system:node-bootstrapper", APIGroup: rbacv1.GroupName, }, } } // CSRApproverRoleBindingTemplate returns the CSR approver role binding template. func CSRApproverRoleBindingTemplate() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system-bootstrap-approve-node-client-csr", }, Subjects: []rbacv1.Subject{ { Kind: "Group", Name: "system:bootstrappers:nodes", APIGroup: rbacv1.GroupName, }, }, RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", Name: "system:certificates.k8s.io:certificatesigningrequests:nodeclient", APIGroup: rbacv1.GroupName, }, } } // CSRRenewalRoleBindingTemplate returns the CSR renewal role binding template. func CSRRenewalRoleBindingTemplate() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system-bootstrap-node-renewal", }, Subjects: []rbacv1.Subject{ { Kind: "Group", Name: "system:nodes", APIGroup: rbacv1.GroupName, }, }, RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", Name: "system:certificates.k8s.io:certificatesigningrequests:selfnodeclient", APIGroup: rbacv1.GroupName, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/flannel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( "encoding/json" "fmt" "slices" "strings" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/resource" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // FlannelClusterRoleTemplate returns the template of the ClusterRole // for the flannel CNI plugin. func FlannelClusterRoleTemplate(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { rules := []rbacv1.PolicyRule{ { APIGroups: []string{""}, Resources: []string{"pods", "nodes", "namespaces"}, Verbs: []string{"get", "list", "watch"}, }, { APIGroups: []string{""}, Resources: []string{"nodes/status"}, Verbs: []string{"patch"}, }, } if spec.FlannelKubeNetworkPoliciesEnabled { rules = append(rules, []rbacv1.PolicyRule{ { APIGroups: []string{"networking.k8s.io"}, Resources: []string{"networkpolicies"}, Verbs: []string{"list", "watch"}, }, { APIGroups: []string{"policy.networking.k8s.io"}, Resources: []string{"adminnetworkpolicies", "baselineadminnetworkpolicies"}, Verbs: []string{"list", "watch"}, }, }...) } return &rbacv1.ClusterRole{ TypeMeta: v1.TypeMeta{ APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRole", }, ObjectMeta: v1.ObjectMeta{ Name: "flannel", Labels: map[string]string{ "k8s-app": "flannel", }, }, Rules: rules, } } // FlannelClusterRoleBindingTemplate returns the template of the // ClusterRoleBinding for the flannel CNI plugin. func FlannelClusterRoleBindingTemplate() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ APIVersion: rbacv1.SchemeGroupVersion.String(), Kind: "ClusterRoleBinding", }, ObjectMeta: v1.ObjectMeta{ Name: "flannel", Labels: map[string]string{ "k8s-app": "flannel", }, }, RoleRef: rbacv1.RoleRef{ APIGroup: rbacv1.SchemeGroupVersion.Group, Kind: "ClusterRole", Name: "flannel", }, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", Name: "flannel", Namespace: "kube-system", }, }, } } // FlannelServiceAccountTemplate returns the template of the // ServiceAccount for the flannel CNI plugin. func FlannelServiceAccountTemplate() runtime.Object { return &corev1.ServiceAccount{ TypeMeta: v1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ServiceAccount", }, ObjectMeta: v1.ObjectMeta{ Name: "flannel", Namespace: "kube-system", Labels: map[string]string{ "k8s-app": "flannel", }, }, } } // FlannelConfigMapTemplate returns the template of the ConfigMap // for the flannel CNI plugin. func FlannelConfigMapTemplate(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { data := map[string]string{ "cni-conf.json": `{ "name": "cbr0", "cniVersion": "1.0.0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] }`, } var netConf struct { Network string `json:"Network,omitempty"` IPv6Network string `json:"IPv6Network,omitempty"` EnableIPv6 *bool `json:"EnableIPv6,omitempty"` EnableIPv4 *bool `json:"EnableIPv4,omitempty"` Backend struct { Type string `json:"Type"` Port int `json:"Port"` } `json:"Backend"` } netConf.Backend.Type = "vxlan" netConf.Backend.Port = 4789 hasIPv4 := false for _, cidr := range spec.PodCIDRs { if strings.Contains(cidr, ".") { netConf.Network = cidr hasIPv4 = true } else { netConf.IPv6Network = cidr netConf.EnableIPv6 = new(true) } } if !hasIPv4 { netConf.EnableIPv4 = new(false) } netConfJSON, err := json.MarshalIndent(netConf, "", " ") if err != nil { // should never happen panic(fmt.Sprintf("failed to marshal net-conf.json: %s", err)) } data["net-conf.json"] = string(netConfJSON) return &corev1.ConfigMap{ TypeMeta: v1.TypeMeta{ APIVersion: corev1.SchemeGroupVersion.String(), Kind: "ConfigMap", }, ObjectMeta: v1.ObjectMeta{ Name: "kube-flannel-cfg", Namespace: "kube-system", Labels: map[string]string{ "k8s-app": "flannel", "tier": "node", }, }, Data: data, } } // FlannelDaemonSetTemplate returns the template of the DaemonSet // for the flannel CNI plugin. func FlannelDaemonSetTemplate(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { envVars := []corev1.EnvVar{ { Name: "POD_NAME", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.name", }, }, }, { Name: "POD_NAMESPACE", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "metadata.namespace", }, }, }, { Name: "EVENT_QUEUE_DEPTH", Value: "5000", }, { Name: "CONT_WHEN_CACHE_NOT_READY", Value: "false", }, } if spec.FlannelKubeServiceHost != "" { envVars = append(envVars, corev1.EnvVar{ Name: "KUBERNETES_SERVICE_HOST", Value: spec.FlannelKubeServiceHost, }) } if spec.FlannelKubeServicePort != "" { envVars = append(envVars, corev1.EnvVar{ Name: "KUBERNETES_SERVICE_PORT", Value: spec.FlannelKubeServicePort, }) } volumes := []corev1.Volume{ {Name: "run", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/run/flannel"}}}, {Name: "cni", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/etc/cni/net.d"}}}, {Name: "flannel-cfg", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{Name: "kube-flannel-cfg"}, }, }}, } if spec.FlannelKubeNetworkPoliciesEnabled { volumes = append(volumes, corev1.Volume{ Name: "lib-modules", VolumeSource: corev1.VolumeSource{HostPath: &corev1.HostPathVolumeSource{Path: "/usr/lib/modules"}}, }) } containers := []corev1.Container{ { Name: "kube-flannel", Image: spec.FlannelImage, Command: []string{"/opt/bin/flanneld"}, Args: slices.Concat( []string{ "--ip-masq", "--kube-subnet-mgr", }, spec.FlannelExtraArgs, ), Env: envVars, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("100m"), corev1.ResourceMemory: resource.MustParse("50Mi"), }, }, SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN", "NET_RAW"}, }, Privileged: new(false), }, VolumeMounts: []corev1.VolumeMount{ { Name: "run", MountPath: "/run/flannel", }, { Name: "flannel-cfg", MountPath: "/etc/kube-flannel/", }, }, }, } if spec.FlannelKubeNetworkPoliciesEnabled { containers = append(containers, corev1.Container{ Name: "kube-network-policies", Image: spec.FlannelKubeNetworkPoliciesImage, Command: []string{ "/bin/netpol", "--hostname-override=$(MY_NODE_NAME)", "--v=2", }, Env: []corev1.EnvVar{ { Name: "MY_NODE_NAME", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "spec.nodeName", }, }, }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("100m"), corev1.ResourceMemory: resource.MustParse("50Mi"), }, }, SecurityContext: &corev1.SecurityContext{ Capabilities: &corev1.Capabilities{ Add: []corev1.Capability{"NET_ADMIN"}, }, Privileged: new(true), }, VolumeMounts: []corev1.VolumeMount{ { Name: "lib-modules", MountPath: "/lib/modules", ReadOnly: true, }, }, }) } return &appsv1.DaemonSet{ TypeMeta: v1.TypeMeta{ APIVersion: appsv1.SchemeGroupVersion.String(), Kind: "DaemonSet", }, ObjectMeta: v1.ObjectMeta{ Name: "kube-flannel", Namespace: "kube-system", Labels: map[string]string{ "k8s-app": "flannel", "tier": "node", }, }, Spec: appsv1.DaemonSetSpec{ Selector: &v1.LabelSelector{ MatchLabels: map[string]string{ "k8s-app": "flannel", "tier": "node", }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: map[string]string{ "k8s-app": "flannel", "tier": "node", }, }, Spec: corev1.PodSpec{ Affinity: &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ NodeSelectorTerms: []corev1.NodeSelectorTerm{ { MatchExpressions: []corev1.NodeSelectorRequirement{ { Key: "kubernetes.io/os", Operator: corev1.NodeSelectorOpIn, Values: []string{"linux"}, }, }, }, }, }, }, }, Containers: containers, InitContainers: []corev1.Container{ { Name: "install-config", Image: spec.FlannelImage, Command: []string{"cp"}, Args: []string{"-f", "/etc/kube-flannel/cni-conf.json", "/etc/cni/net.d/10-flannel.conflist"}, VolumeMounts: []corev1.VolumeMount{ {Name: "cni", MountPath: "/etc/cni/net.d"}, {Name: "flannel-cfg", MountPath: "/etc/kube-flannel/"}, }, }, }, HostNetwork: true, PriorityClassName: "system-node-critical", ServiceAccountName: "flannel", Tolerations: []corev1.Toleration{ {Effect: corev1.TaintEffectNoSchedule, Operator: corev1.TolerationOpExists}, {Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpExists}, }, Volumes: volumes, }, }, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/k8stemplates.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8stemplates contains templates for Kubernetes resources. package k8stemplates import ( "bytes" "fmt" "io" "sync" "k8s.io/apimachinery/pkg/runtime" k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json" ) var serializer = sync.OnceValue(func() *k8sjson.Serializer { return k8sjson.NewSerializerWithOptions( k8sjson.DefaultMetaFactory, nil, nil, k8sjson.SerializerOptions{ Yaml: true, Pretty: true, Strict: true, }, ) }) // Marshal serializes the given object into YAML format. func Marshal(obj runtime.Object) ([]byte, error) { var buf bytes.Buffer if err := MarshalTo(obj, &buf); err != nil { return nil, err } return buf.Bytes(), nil } // MarshalTo serializes the given object into YAML format and writes it to the provided buffer. func MarshalTo(obj runtime.Object, w io.Writer) error { if err := serializer().Encode(obj, w); err != nil { return fmt.Errorf("error marshaling object %s: %w", obj.GetObjectKind().GroupVersionKind().String(), err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/k8stemplates_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/k8stemplates" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestTemplates(t *testing.T) { t.Parallel() const recordResults = false if recordResults { t.Log("recording test is enabled, failing the test") t.Fail() } for _, test := range []struct { name string obj func() runtime.Object }{ { name: "apiserver-encryption-secretbox", obj: func() runtime.Object { return k8stemplates.APIServerEncryptionConfig(&secrets.KubernetesRootSpec{ SecretboxEncryptionSecret: "/FYehPLp5F8POCNQRVDEUb7Hmt+KkV44e+fQL4HMexs=", }) }, }, { name: "apiserver-encryption-aescbc", obj: func() runtime.Object { return k8stemplates.APIServerEncryptionConfig(&secrets.KubernetesRootSpec{ AESCBCEncryptionSecret: "/sFYehPLp5F8POCNQRVDEUb7Hmt+KkV44e+fQL4HMexs=", }) }, }, { name: "coredns-service-ipv4", obj: func() runtime.Object { return k8stemplates.CoreDNSService(&k8s.BootstrapManifestsConfigSpec{ DNSServiceIP: "10.96.0.10", }) }, }, { name: "coredns-service-ipv6", obj: func() runtime.Object { return k8stemplates.CoreDNSService(&k8s.BootstrapManifestsConfigSpec{ DNSServiceIPv6: "fd00::10", }) }, }, { name: "coredns-service-dual", obj: func() runtime.Object { return k8stemplates.CoreDNSService(&k8s.BootstrapManifestsConfigSpec{ DNSServiceIP: "10.96.0.10", DNSServiceIPv6: "fd00::10", }) }, }, { name: "coredns-service-account", obj: k8stemplates.CoreDNSServiceAccount, }, { name: "coredns-cluster-role-binding", obj: k8stemplates.CoreDNSClusterRoleBinding, }, { name: "coredns-cluster-role", obj: k8stemplates.CoreDNSClusterRole, }, { name: "coredns-configmap-cluster-domain", obj: func() runtime.Object { return k8stemplates.CoreDNSConfigMap(&k8s.BootstrapManifestsConfigSpec{ ClusterDomain: "cluster.local", }) }, }, { name: "coredns-configmap-no-cluster-domain", obj: func() runtime.Object { return k8stemplates.CoreDNSConfigMap(&k8s.BootstrapManifestsConfigSpec{}) }, }, { name: "coredns-deployment", obj: func() runtime.Object { return k8stemplates.CoreDNSDeployment(&k8s.BootstrapManifestsConfigSpec{ CoreDNSImage: "coredns/coredns:1.9.3", }) }, }, { name: "kubelet-bootstrapping-token", obj: func() runtime.Object { return k8stemplates.KubeletBootstrapTokenSecret(&secrets.KubernetesRootSpec{ BootstrapTokenID: "25p8ak", BootstrapTokenSecret: "vshybadgp2mhtvm7", }) }, }, { name: "csr-node-bootstrap", obj: k8stemplates.CSRNodeBootstrapTemplate, }, { name: "csr-approver-role-binding", obj: k8stemplates.CSRApproverRoleBindingTemplate, }, { name: "csr-renewal-role-binding", obj: k8stemplates.CSRRenewalRoleBindingTemplate, }, { name: "kubeconfig-in-cluster", obj: func() runtime.Object { return k8stemplates.KubeconfigInClusterTemplate(&k8s.BootstrapManifestsConfigSpec{ Server: "https://localhost:6443", }) }, }, { name: "talos-nodes-rbac-cluster-role-binding", obj: k8stemplates.TalosNodesRBACClusterRoleBinding, }, { name: "talos-nodes-rbac-cluster-role", obj: k8stemplates.TalosNodesRBACClusterRole, }, { name: "kube-proxy-daemonset", obj: func() runtime.Object { return k8stemplates.KubeProxyDaemonSetTemplate(&k8s.BootstrapManifestsConfigSpec{ ProxyImage: "k8s.gcr.io/kube-proxy:v1.27.0", ProxyArgs: []string{"--proxy-mode=iptables"}, }) }, }, { name: "kube-proxy-service-account", obj: k8stemplates.KubeProxyServiceAccount, }, { name: "kube-proxy-cluster-role-binding", obj: k8stemplates.KubeProxyClusterRoleBinding, }, { name: "talos-service-account-crd", obj: k8stemplates.TalosServiceAccountCRDTemplate, }, { name: "flannel-cluster-role", obj: func() runtime.Object { return k8stemplates.FlannelClusterRoleTemplate(&k8s.BootstrapManifestsConfigSpec{}) }, }, { name: "flannel-cluster-role-with-network-policies", obj: func() runtime.Object { return k8stemplates.FlannelClusterRoleTemplate(&k8s.BootstrapManifestsConfigSpec{ FlannelKubeNetworkPoliciesEnabled: true, }) }, }, { name: "flannel-cluster-role-binding", obj: k8stemplates.FlannelClusterRoleBindingTemplate, }, { name: "flannel-service-account", obj: k8stemplates.FlannelServiceAccountTemplate, }, { name: "flannel-configmap-v4", obj: func() runtime.Object { return k8stemplates.FlannelConfigMapTemplate(&k8s.BootstrapManifestsConfigSpec{ PodCIDRs: []string{"10.96.0.0/12"}, }) }, }, { name: "flannel-configmap-v6", obj: func() runtime.Object { return k8stemplates.FlannelConfigMapTemplate(&k8s.BootstrapManifestsConfigSpec{ PodCIDRs: []string{"fd00::/112"}, }) }, }, { name: "flannel-configmap-dual", obj: func() runtime.Object { return k8stemplates.FlannelConfigMapTemplate(&k8s.BootstrapManifestsConfigSpec{ PodCIDRs: []string{"10.96.0.0/12", "fd00::/112"}, }) }, }, { name: "flannel-daemonset", obj: func() runtime.Object { return k8stemplates.FlannelDaemonSetTemplate(&k8s.BootstrapManifestsConfigSpec{ FlannelImage: "quay.io/coreos/flannel:v0.14.0", FlannelExtraArgs: []string{"--foo=bar"}, }) }, }, { name: "flannel-daemonset-with-network-policies", obj: func() runtime.Object { return k8stemplates.FlannelDaemonSetTemplate(&k8s.BootstrapManifestsConfigSpec{ FlannelImage: "quay.io/coreos/flannel:v0.14.0", FlannelExtraArgs: []string{"--foo=bar"}, FlannelKubeNetworkPoliciesEnabled: true, FlannelKubeNetworkPoliciesImage: "registry.k8s.io/networking/kube-network-policies:v0.7.0", }) }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() obj := test.obj() out, err := k8stemplates.Marshal(obj) require.NoError(t, err) goldenPath := filepath.Join("testdata", test.name+".yaml") if recordResults { require.NoError(t, os.WriteFile(goldenPath, out, 0o644), "failed to write golden file %s", goldenPath) } else { golden, err := os.ReadFile(goldenPath) require.NoError(t, err, "failed to read golden file %s", goldenPath) require.Equal(t, string(golden), string(out), "output does not match golden file %s", goldenPath) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/kube-proxy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( "slices" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubeProxyDaemonSetTemplate generates a DaemonSet for kube-proxy. func KubeProxyDaemonSetTemplate(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { return &appsv1.DaemonSet{ TypeMeta: v1.TypeMeta{ Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "kube-proxy", Namespace: "kube-system", Labels: map[string]string{ "tier": "node", "k8s-app": "kube-proxy", }, }, Spec: appsv1.DaemonSetSpec{ Selector: &v1.LabelSelector{ MatchLabels: map[string]string{ "tier": "node", "k8s-app": "kube-proxy", }, }, UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ Type: appsv1.RollingUpdateDaemonSetStrategyType, RollingUpdate: &appsv1.RollingUpdateDaemonSet{ MaxUnavailable: new(intstr.FromInt(1)), }, }, Template: corev1.PodTemplateSpec{ ObjectMeta: v1.ObjectMeta{ Labels: map[string]string{ "tier": "node", "k8s-app": "kube-proxy", }, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "kube-proxy", Image: spec.ProxyImage, Command: slices.Concat( []string{"/usr/local/bin/kube-proxy"}, spec.ProxyArgs, ), Env: []corev1.EnvVar{ { Name: "NODE_NAME", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "spec.nodeName", }, }, }, { Name: "POD_IP", ValueFrom: &corev1.EnvVarSource{ FieldRef: &corev1.ObjectFieldSelector{ FieldPath: "status.podIP", }, }, }, }, SecurityContext: &corev1.SecurityContext{ Privileged: new(true), }, VolumeMounts: []corev1.VolumeMount{ { Name: "lib-modules", MountPath: "/lib/modules", ReadOnly: true, }, { Name: "ssl-certs-host", MountPath: "/etc/ssl/certs", ReadOnly: true, }, { Name: "kubeconfig", MountPath: "/etc/kubernetes", ReadOnly: true, }, }, }, }, HostNetwork: true, PriorityClassName: "system-cluster-critical", ServiceAccountName: "kube-proxy", Tolerations: []corev1.Toleration{ { Effect: corev1.TaintEffectNoSchedule, Operator: corev1.TolerationOpExists, }, { Effect: corev1.TaintEffectNoExecute, Operator: corev1.TolerationOpExists, }, }, Volumes: []corev1.Volume{ { Name: "lib-modules", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ Path: "/usr/lib/modules", }, }, }, { Name: "ssl-certs-host", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ Path: "/etc/ssl/certs", }, }, }, { Name: "kubeconfig", VolumeSource: corev1.VolumeSource{ ConfigMap: &corev1.ConfigMapVolumeSource{ LocalObjectReference: corev1.LocalObjectReference{ Name: "kubeconfig-in-cluster", }, }, }, }, }, }, }, }, } } // KubeProxyServiceAccount returns the ServiceAccount for kube-proxy. func KubeProxyServiceAccount() runtime.Object { return &corev1.ServiceAccount{ TypeMeta: v1.TypeMeta{ Kind: "ServiceAccount", APIVersion: corev1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "kube-proxy", Namespace: "kube-system", }, } } // KubeProxyClusterRoleBinding returns the ClusterRoleBinding for kube-proxy. func KubeProxyClusterRoleBinding() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "kube-proxy", }, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", Name: "kube-proxy", Namespace: "kube-system", }, }, RoleRef: rbacv1.RoleRef{ Kind: "ClusterRole", Name: "system:node-proxier", APIGroup: rbacv1.GroupName, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/kubeconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubeconfigInClusterTemplate generates a ConfigMap containing the kubeconfig for in-cluster access. func KubeconfigInClusterTemplate(spec *k8s.BootstrapManifestsConfigSpec) runtime.Object { cfg := clientcmdapi.Config{ APIVersion: "v1", Clusters: map[string]*clientcmdapi.Cluster{ "local": { Server: spec.Server, CertificateAuthority: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", }, }, AuthInfos: map[string]*clientcmdapi.AuthInfo{ "service-account": { TokenFile: "/var/run/secrets/kubernetes.io/serviceaccount/token", }, }, Contexts: map[string]*clientcmdapi.Context{ "local": { Cluster: "local", AuthInfo: "service-account", }, }, CurrentContext: "local", } kubeconfig, err := clientcmd.Write(cfg) if err != nil { panic(err) // This should never happen, as the config is valid. } return &corev1.ConfigMap{ TypeMeta: v1.TypeMeta{ Kind: "ConfigMap", APIVersion: corev1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "kubeconfig-in-cluster", Namespace: "kube-system", }, Data: map[string]string{ "kubeconfig": string(kubeconfig), }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( corev1 "k8s.io/api/core/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // KubeletBootstrapTokenSecret returns the kubelet bootstrap token secret. func KubeletBootstrapTokenSecret(secrets *secrets.KubernetesRootSpec) runtime.Object { return &corev1.Secret{ TypeMeta: v1.TypeMeta{ Kind: "Secret", APIVersion: corev1.SchemeGroupVersion.Version, }, ObjectMeta: v1.ObjectMeta{ Name: "bootstrap-token-" + secrets.BootstrapTokenID, Namespace: "kube-system", }, Type: corev1.SecretType("bootstrap.kubernetes.io/token"), StringData: map[string]string{ "token-id": secrets.BootstrapTokenID, "token-secret": secrets.BootstrapTokenSecret, "usage-bootstrap-authentication": "true", "auth-extra-groups": "system:bootstrappers:nodes", }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/talos.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8stemplates import ( rbacv1 "k8s.io/api/rbac/v1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) // TalosNodesRBACClusterRoleBinding is the template of the RBAC rules which allow // Talos to discover the nodes in the Kubernetes cluster and assign // endpoints for the internal discovery. func TalosNodesRBACClusterRoleBinding() runtime.Object { return &rbacv1.ClusterRoleBinding{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRoleBinding", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system:talos-nodes", Labels: map[string]string{ "kubernetes.io/bootstrapping": "rbac-defaults", }, Annotations: map[string]string{ "rbac.authorization.kubernetes.io/autoupdate": "true", }, }, RoleRef: rbacv1.RoleRef{ APIGroup: rbacv1.GroupName, Kind: "ClusterRole", Name: "system:talos-nodes", }, Subjects: []rbacv1.Subject{ { Kind: "Group", Name: "system:nodes", APIGroup: rbacv1.GroupName, }, }, } } // TalosNodesRBACClusterRole is the template of the RBAC rules which allow // Talos to discover the nodes in the Kubernetes cluster and assign // endpoints for the internal discovery. func TalosNodesRBACClusterRole() runtime.Object { return &rbacv1.ClusterRole{ TypeMeta: v1.TypeMeta{ Kind: "ClusterRole", APIVersion: rbacv1.SchemeGroupVersion.String(), }, ObjectMeta: v1.ObjectMeta{ Name: "system:talos-nodes", Labels: map[string]string{ "kubernetes.io/bootstrapping": "rbac-defaults", }, }, Rules: []rbacv1.PolicyRule{ { APIGroups: []string{"discovery.k8s.io"}, Resources: []string{"endpointslices"}, Verbs: []string{"get", "list", "watch"}, }, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/apiserver-encryption-aescbc.yaml ================================================ apiVersion: v1 kind: EncryptionConfig resources: - providers: - aescbc: keys: - name: key1 secret: /sFYehPLp5F8POCNQRVDEUb7Hmt+KkV44e+fQL4HMexs= - identity: {} resources: - secrets ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/apiserver-encryption-secretbox.yaml ================================================ apiVersion: v1 kind: EncryptionConfig resources: - providers: - secretbox: keys: - name: key2 secret: /FYehPLp5F8POCNQRVDEUb7Hmt+KkV44e+fQL4HMexs= - identity: {} resources: - secrets ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-cluster-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:coredns roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:coredns subjects: - kind: ServiceAccount name: coredns namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-cluster-role.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: kubernetes.io/bootstrapping: rbac-defaults name: system:coredns rules: - apiGroups: - "" resources: - endpoints - services - pods - namespaces verbs: - list - watch - apiGroups: - discovery.k8s.io resources: - endpointslices verbs: - list - watch ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-configmap-cluster-domain.yaml ================================================ apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } ready log . { class error } prometheus :9153 kubernetes cluster.local in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } forward . /etc/resolv.conf { max_concurrent 1000 } cache 30 { disable success cluster.local disable denial cluster.local } loop reload loadbalance } kind: ConfigMap metadata: name: coredns namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-configmap-no-cluster-domain.yaml ================================================ apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } ready log . { class error } prometheus :9153 kubernetes in-addr.arpa ip6.arpa { pods insecure fallthrough in-addr.arpa ip6.arpa ttl 30 } forward . /etc/resolv.conf { max_concurrent 1000 } cache 30 loop reload loadbalance } kind: ConfigMap metadata: name: coredns namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-deployment.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: labels: k8s-app: kube-dns kubernetes.io/name: CoreDNS name: coredns namespace: kube-system spec: replicas: 2 selector: matchLabels: k8s-app: kube-dns strategy: rollingUpdate: maxUnavailable: 1 type: RollingUpdate template: metadata: labels: k8s-app: kube-dns spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - podAffinityTerm: labelSelector: matchExpressions: - key: k8s-app operator: In values: - kube-dns topologyKey: kubernetes.io/hostname weight: 100 containers: - args: - -conf - /etc/coredns/Corefile env: - name: GOMEMLIMIT value: 161MiB image: coredns/coredns:1.9.3 imagePullPolicy: IfNotPresent livenessProbe: failureThreshold: 5 httpGet: path: /health port: 8080 scheme: HTTP initialDelaySeconds: 60 successThreshold: 1 timeoutSeconds: 5 name: coredns ports: - containerPort: 53 name: dns protocol: UDP - containerPort: 53 name: dns-tcp protocol: TCP - containerPort: 9153 name: metrics protocol: TCP readinessProbe: httpGet: path: /ready port: 8181 scheme: HTTP resources: limits: memory: 170Mi requests: cpu: 100m memory: 70Mi securityContext: allowPrivilegeEscalation: false capabilities: add: - NET_BIND_SERVICE drop: - ALL readOnlyRootFilesystem: true volumeMounts: - mountPath: /etc/coredns name: config-volume readOnly: true dnsPolicy: Default nodeSelector: kubernetes.io/os: linux priorityClassName: system-cluster-critical serviceAccountName: coredns tolerations: - effect: NoSchedule key: node-role.kubernetes.io/control-plane operator: Exists - effect: NoSchedule key: node.cloudprovider.kubernetes.io/uninitialized operator: Exists volumes: - configMap: items: - key: Corefile path: Corefile name: coredns name: config-volume status: {} ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-service-account.yaml ================================================ apiVersion: v1 kind: ServiceAccount metadata: name: coredns namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-service-dual.yaml ================================================ apiVersion: v1 kind: Service metadata: annotations: prometheus.io/port: "9153" prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: CoreDNS name: kube-dns namespace: kube-system spec: clusterIP: 10.96.0.10 clusterIPs: - 10.96.0.10 - fd00::10 ipFamilies: - IPv4 - IPv6 ipFamilyPolicy: RequireDualStack ports: - name: dns port: 53 protocol: UDP targetPort: 53 - name: dns-tcp port: 53 protocol: TCP targetPort: 53 - name: metrics port: 9153 protocol: TCP targetPort: 9153 selector: k8s-app: kube-dns status: loadBalancer: {} ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-service-ipv4.yaml ================================================ apiVersion: v1 kind: Service metadata: annotations: prometheus.io/port: "9153" prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: CoreDNS name: kube-dns namespace: kube-system spec: clusterIP: 10.96.0.10 clusterIPs: - 10.96.0.10 ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - name: dns port: 53 protocol: UDP targetPort: 53 - name: dns-tcp port: 53 protocol: TCP targetPort: 53 - name: metrics port: 9153 protocol: TCP targetPort: 9153 selector: k8s-app: kube-dns status: loadBalancer: {} ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/coredns-service-ipv6.yaml ================================================ apiVersion: v1 kind: Service metadata: annotations: prometheus.io/port: "9153" prometheus.io/scrape: "true" labels: k8s-app: kube-dns kubernetes.io/cluster-service: "true" kubernetes.io/name: CoreDNS name: kube-dns namespace: kube-system spec: clusterIP: fd00::10 clusterIPs: - fd00::10 ipFamilies: - IPv6 ipFamilyPolicy: SingleStack ports: - name: dns port: 53 protocol: UDP targetPort: 53 - name: dns-tcp port: 53 protocol: TCP targetPort: 53 - name: metrics port: 9153 protocol: TCP targetPort: 9153 selector: k8s-app: kube-dns status: loadBalancer: {} ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/csr-approver-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system-bootstrap-approve-node-client-csr roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:nodeclient subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:bootstrappers:nodes ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/csr-node-bootstrap.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system-bootstrap-node-bootstrapper roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:node-bootstrapper subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:bootstrappers:nodes - apiGroup: rbac.authorization.k8s.io kind: Group name: system:nodes ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/csr-renewal-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: system-bootstrap-node-renewal roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:nodes ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-cluster-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: k8s-app: flannel name: flannel roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: flannel subjects: - kind: ServiceAccount name: flannel namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-cluster-role-with-network-policies.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: k8s-app: flannel name: flannel rules: - apiGroups: - "" resources: - pods - nodes - namespaces verbs: - get - list - watch - apiGroups: - "" resources: - nodes/status verbs: - patch - apiGroups: - networking.k8s.io resources: - networkpolicies verbs: - list - watch - apiGroups: - policy.networking.k8s.io resources: - adminnetworkpolicies - baselineadminnetworkpolicies verbs: - list - watch ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-cluster-role.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: k8s-app: flannel name: flannel rules: - apiGroups: - "" resources: - pods - nodes - namespaces verbs: - get - list - watch - apiGroups: - "" resources: - nodes/status verbs: - patch ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-configmap-dual.yaml ================================================ apiVersion: v1 data: cni-conf.json: |- { "name": "cbr0", "cniVersion": "1.0.0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: |- { "Network": "10.96.0.0/12", "IPv6Network": "fd00::/112", "EnableIPv6": true, "Backend": { "Type": "vxlan", "Port": 4789 } } kind: ConfigMap metadata: labels: k8s-app: flannel tier: node name: kube-flannel-cfg namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-configmap-v4.yaml ================================================ apiVersion: v1 data: cni-conf.json: |- { "name": "cbr0", "cniVersion": "1.0.0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: |- { "Network": "10.96.0.0/12", "Backend": { "Type": "vxlan", "Port": 4789 } } kind: ConfigMap metadata: labels: k8s-app: flannel tier: node name: kube-flannel-cfg namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-configmap-v6.yaml ================================================ apiVersion: v1 data: cni-conf.json: |- { "name": "cbr0", "cniVersion": "1.0.0", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] } net-conf.json: |- { "IPv6Network": "fd00::/112", "EnableIPv6": true, "EnableIPv4": false, "Backend": { "Type": "vxlan", "Port": 4789 } } kind: ConfigMap metadata: labels: k8s-app: flannel tier: node name: kube-flannel-cfg namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-daemonset-with-network-policies.yaml ================================================ apiVersion: apps/v1 kind: DaemonSet metadata: labels: k8s-app: flannel tier: node name: kube-flannel namespace: kube-system spec: selector: matchLabels: k8s-app: flannel tier: node template: metadata: labels: k8s-app: flannel tier: node spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/os operator: In values: - linux containers: - args: - --ip-masq - --kube-subnet-mgr - --foo=bar command: - /opt/bin/flanneld env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: EVENT_QUEUE_DEPTH value: "5000" - name: CONT_WHEN_CACHE_NOT_READY value: "false" image: quay.io/coreos/flannel:v0.14.0 name: kube-flannel resources: requests: cpu: 100m memory: 50Mi securityContext: capabilities: add: - NET_ADMIN - NET_RAW privileged: false volumeMounts: - mountPath: /run/flannel name: run - mountPath: /etc/kube-flannel/ name: flannel-cfg - command: - /bin/netpol - --hostname-override=$(MY_NODE_NAME) - --v=2 env: - name: MY_NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName image: registry.k8s.io/networking/kube-network-policies:v0.7.0 name: kube-network-policies resources: requests: cpu: 100m memory: 50Mi securityContext: capabilities: add: - NET_ADMIN privileged: true volumeMounts: - mountPath: /lib/modules name: lib-modules readOnly: true hostNetwork: true initContainers: - args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist command: - cp image: quay.io/coreos/flannel:v0.14.0 name: install-config resources: {} volumeMounts: - mountPath: /etc/cni/net.d name: cni - mountPath: /etc/kube-flannel/ name: flannel-cfg priorityClassName: system-node-critical serviceAccountName: flannel tolerations: - effect: NoSchedule operator: Exists - effect: NoExecute operator: Exists volumes: - hostPath: path: /run/flannel name: run - hostPath: path: /etc/cni/net.d name: cni - configMap: name: kube-flannel-cfg name: flannel-cfg - hostPath: path: /usr/lib/modules name: lib-modules updateStrategy: {} status: currentNumberScheduled: 0 desiredNumberScheduled: 0 numberMisscheduled: 0 numberReady: 0 ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-daemonset.yaml ================================================ apiVersion: apps/v1 kind: DaemonSet metadata: labels: k8s-app: flannel tier: node name: kube-flannel namespace: kube-system spec: selector: matchLabels: k8s-app: flannel tier: node template: metadata: labels: k8s-app: flannel tier: node spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/os operator: In values: - linux containers: - args: - --ip-masq - --kube-subnet-mgr - --foo=bar command: - /opt/bin/flanneld env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: EVENT_QUEUE_DEPTH value: "5000" - name: CONT_WHEN_CACHE_NOT_READY value: "false" image: quay.io/coreos/flannel:v0.14.0 name: kube-flannel resources: requests: cpu: 100m memory: 50Mi securityContext: capabilities: add: - NET_ADMIN - NET_RAW privileged: false volumeMounts: - mountPath: /run/flannel name: run - mountPath: /etc/kube-flannel/ name: flannel-cfg hostNetwork: true initContainers: - args: - -f - /etc/kube-flannel/cni-conf.json - /etc/cni/net.d/10-flannel.conflist command: - cp image: quay.io/coreos/flannel:v0.14.0 name: install-config resources: {} volumeMounts: - mountPath: /etc/cni/net.d name: cni - mountPath: /etc/kube-flannel/ name: flannel-cfg priorityClassName: system-node-critical serviceAccountName: flannel tolerations: - effect: NoSchedule operator: Exists - effect: NoExecute operator: Exists volumes: - hostPath: path: /run/flannel name: run - hostPath: path: /etc/cni/net.d name: cni - configMap: name: kube-flannel-cfg name: flannel-cfg updateStrategy: {} status: currentNumberScheduled: 0 desiredNumberScheduled: 0 numberMisscheduled: 0 numberReady: 0 ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/flannel-service-account.yaml ================================================ apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: flannel name: flannel namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/kube-proxy-cluster-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kube-proxy roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:node-proxier subjects: - kind: ServiceAccount name: kube-proxy namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/kube-proxy-daemonset.yaml ================================================ apiVersion: apps/v1 kind: DaemonSet metadata: labels: k8s-app: kube-proxy tier: node name: kube-proxy namespace: kube-system spec: selector: matchLabels: k8s-app: kube-proxy tier: node template: metadata: labels: k8s-app: kube-proxy tier: node spec: containers: - command: - /usr/local/bin/kube-proxy - --proxy-mode=iptables env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP image: k8s.gcr.io/kube-proxy:v1.27.0 name: kube-proxy resources: {} securityContext: privileged: true volumeMounts: - mountPath: /lib/modules name: lib-modules readOnly: true - mountPath: /etc/ssl/certs name: ssl-certs-host readOnly: true - mountPath: /etc/kubernetes name: kubeconfig readOnly: true hostNetwork: true priorityClassName: system-cluster-critical serviceAccountName: kube-proxy tolerations: - effect: NoSchedule operator: Exists - effect: NoExecute operator: Exists volumes: - hostPath: path: /usr/lib/modules name: lib-modules - hostPath: path: /etc/ssl/certs name: ssl-certs-host - configMap: name: kubeconfig-in-cluster name: kubeconfig updateStrategy: rollingUpdate: maxUnavailable: 1 type: RollingUpdate status: currentNumberScheduled: 0 desiredNumberScheduled: 0 numberMisscheduled: 0 numberReady: 0 ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/kube-proxy-service-account.yaml ================================================ apiVersion: v1 kind: ServiceAccount metadata: name: kube-proxy namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/kubeconfig-in-cluster.yaml ================================================ apiVersion: v1 data: kubeconfig: | apiVersion: v1 clusters: - cluster: certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt server: https://localhost:6443 name: local contexts: - context: cluster: local user: service-account name: local current-context: local kind: Config users: - name: service-account user: tokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token kind: ConfigMap metadata: name: kubeconfig-in-cluster namespace: kube-system ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/kubelet-bootstrapping-token.yaml ================================================ apiVersion: v1 kind: Secret metadata: name: bootstrap-token-25p8ak namespace: kube-system stringData: auth-extra-groups: system:bootstrappers:nodes token-id: 25p8ak token-secret: vshybadgp2mhtvm7 usage-bootstrap-authentication: "true" type: bootstrap.kubernetes.io/token ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/talos-nodes-rbac-cluster-role-binding.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:talos-nodes roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:talos-nodes subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:nodes ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/talos-nodes-rbac-cluster-role.yaml ================================================ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: kubernetes.io/bootstrapping: rbac-defaults name: system:talos-nodes rules: - apiGroups: - discovery.k8s.io resources: - endpointslices verbs: - get - list - watch ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/k8stemplates/testdata/talos-service-account-crd.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: serviceaccounts.talos.dev spec: conversion: strategy: None group: talos.dev names: kind: ServiceAccount listKind: ServiceAccountList plural: serviceaccounts shortNames: - tsa singular: serviceaccount scope: Namespaced versions: - name: v1alpha1 schema: openAPIV3Schema: properties: spec: properties: roles: items: type: string type: array type: object status: properties: failureReason: type: string type: object type: object served: true storage: true status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/nodename/nodename.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package nodename provides utility functions to generate nodenames. package nodename import ( "fmt" "strings" ) // FromHostname converts a hostname to Kubernetes Node name. // // UNIX hostname has almost no restrictions, but Kubernetes Node name has // to be RFC 1123 compliant. This function converts a hostname to a valid // Kubernetes Node name (if possible). // // The allowed format is: // // [a-z0-9]([-a-z0-9]*[a-z0-9])? // //nolint:gocyclo func FromHostname(hostname string) (string, error) { nodename := strings.Map(func(r rune) rune { switch { case r >= 'a' && r <= 'z': // allow lowercase return r case r >= 'A' && r <= 'Z': // lowercase uppercase letters return r - 'A' + 'a' case r >= '0' && r <= '9': // allow digits return r case r == '-' || r == '_': // allow dash, convert underscore to dash return '-' case r == '.': // allow dot return '.' default: // drop anything else return -1 } }, hostname) // now drop any dashes/dots at the beginning or end nodename = strings.Trim(nodename, "-.") if len(nodename) == 0 { return "", fmt.Errorf("could not convert hostname %q to a valid Kubernetes Node name", hostname) } return nodename, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/nodename/nodename_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nodename_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/nodename" ) func TestFromHostname(t *testing.T) { for _, test := range []struct { hostname string expectedNodeName string expectedError string }{ { hostname: "foo", expectedNodeName: "foo", }, { hostname: "foo_ია", expectedNodeName: "foo", }, { hostname: "Node1", expectedNodeName: "node1", }, { hostname: "MY_test_server_", expectedNodeName: "my-test-server", }, { hostname: "123", expectedNodeName: "123", }, { hostname: "-my-server-", expectedNodeName: "my-server", }, { hostname: "კომპიუტერი", expectedError: "could not convert hostname \"კომპიუტერი\" to a valid Kubernetes Node name", }, { hostname: "foo.bar.tld.", expectedNodeName: "foo.bar.tld", }, } { t.Run(test.hostname, func(t *testing.T) { nodename, err := nodename.FromHostname(test.hostname) if test.expectedError != "" { require.EqualError(t, err, test.expectedError) } else { require.NoError(t, err) require.Equal(t, test.expectedNodeName, nodename) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/internal/nodewatch/nodewatch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package nodewatch implements Kubernetes node watcher. package nodewatch import ( "context" "fmt" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/client-go/informers" informersv1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/tools/cache" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" ) // NodeWatcher defines a NodeWatcher-based node watcher. type NodeWatcher struct { client *kubernetes.Client nodename string nodes informersv1.NodeInformer } // NewNodeWatcher creates new Kubernetes node watcher. func NewNodeWatcher(client *kubernetes.Client, nodename string) *NodeWatcher { return &NodeWatcher{ nodename: nodename, client: client, } } // Nodename returns the watched nodename. func (r *NodeWatcher) Nodename() string { return r.nodename } // Get returns the Node resource. func (r *NodeWatcher) Get() (*corev1.Node, error) { return r.nodes.Lister().Get(r.nodename) } // Watch starts watching Node state and notifies on updates via notify channel. func (r *NodeWatcher) Watch(ctx context.Context, logger *zap.Logger) (<-chan struct{}, <-chan error, func(), error) { logger.Debug("starting node watcher", zap.String("nodename", r.nodename)) informerFactory := informers.NewSharedInformerFactoryWithOptions( r.client.Clientset, constants.KubernetesInformerDefaultResyncPeriod, informers.WithTweakListOptions( func(opts *metav1.ListOptions) { opts.FieldSelector = fields.OneTermEqualSelector(metav1.ObjectNameField, r.nodename).String() }, ), ) notifyCh := make(chan struct{}, 1) watchErrCh := make(chan error, 1) notify := func(_ any) { select { case notifyCh <- struct{}{}: default: } } r.nodes = informerFactory.Core().V1().Nodes() if err := r.nodes.Informer().SetWatchErrorHandler(func(r *cache.Reflector, err error) { select { case watchErrCh <- err: default: } }); err != nil { return nil, nil, nil, fmt.Errorf("failed to set watch error handler: %w", err) } if _, err := r.nodes.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: notify, DeleteFunc: notify, UpdateFunc: func(_, _ any) { notify(nil) }, }); err != nil { return nil, nil, nil, fmt.Errorf("failed to add event handler: %w", err) } informerFactory.Start(ctx.Done()) go func() { logger.Debug("waiting for node cache sync") result := informerFactory.WaitForCacheSync(ctx.Done()) var synced bool // result should contain a single entry for _, v := range result { synced = v } logger.Debug("node cache sync done", zap.Bool("synced", synced)) select { case notifyCh <- struct{}{}: default: } }() return notifyCh, watchErrCh, informerFactory.Shutdown, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides controllers which manage Kubernetes resources. package k8s import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" ) // CloudProviderExternal is a constant for the external cloud provider. const CloudProviderExternal = "external" func init() { // ugly hack, but it doesn't look like there's better API // cut out error handler which logs error to standard logger utilruntime.ErrorHandlers = utilruntime.ErrorHandlers[len(utilruntime.ErrorHandlers)-1:] //nolint:reassign } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubeletConfigController renders kubelet configuration based on machine config. type KubeletConfigController = transform.Controller[*config.MachineConfig, *k8s.KubeletConfig] // NewKubeletConfigController instanciates the config controller. func NewKubeletConfigController() *KubeletConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.KubeletConfig]{ Name: "k8s.KubeletConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*k8s.KubeletConfig] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*k8s.KubeletConfig]() } if cfg.Config().Cluster() == nil || cfg.Config().Machine() == nil { return optional.None[*k8s.KubeletConfig]() } return optional.Some(k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *k8s.KubeletConfig) error { staticPodURL, err := safe.ReaderGetByID[*k8s.StaticPodServerStatus](ctx, r, k8s.StaticPodServerStatusResourceID) if err != nil { if state.IsNotFoundError(err) { return xerrors.NewTaggedf[transform.SkipReconcileTag]("static pod server status resource not found; not creating kubelet config") } return err } kubeletConfig := res.TypedSpec() cfgProvider := cfg.Config() kubeletConfig.Image = cfgProvider.Machine().Kubelet().Image() kubeletConfig.ClusterDNS = cfgProvider.Machine().Kubelet().ClusterDNS() if len(kubeletConfig.ClusterDNS) == 0 { addrs, err := cfgProvider.Cluster().Network().DNSServiceIPs() if err != nil { return fmt.Errorf("error building DNS service IPs: %w", err) } kubeletConfig.ClusterDNS = xslices.Map(addrs, netip.Addr.String) } extraArgs := make(map[string]k8s.ArgValues, len(cfgProvider.Machine().Kubelet().ExtraArgs())) for k, v := range cfgProvider.Machine().Kubelet().ExtraArgs() { extraArgs[k] = k8s.ArgValues{Values: v} } kubeletConfig.ClusterDomain = cfgProvider.Cluster().Network().DNSDomain() kubeletConfig.ExtraArgs = extraArgs kubeletConfig.ExtraMounts = cfgProvider.Machine().Kubelet().ExtraMounts() kubeletConfig.ExtraConfig = cfgProvider.Machine().Kubelet().ExtraConfig() kubeletConfig.CloudProviderExternal = cfgProvider.Cluster().ExternalCloudProvider().Enabled() kubeletConfig.DefaultRuntimeSeccompEnabled = cfgProvider.Machine().Kubelet().DefaultRuntimeSeccompProfileEnabled() kubeletConfig.SkipNodeRegistration = cfgProvider.Machine().Kubelet().SkipNodeRegistration() kubeletConfig.StaticPodListURL = staticPodURL.TypedSpec().URL kubeletConfig.DisableManifestsDirectory = cfgProvider.Machine().Kubelet().DisableManifestsDirectory() kubeletConfig.EnableFSQuotaMonitoring = cfgProvider.Machine().Features().DiskQuotaSupportEnabled() kubeletConfig.CredentialProviderConfig = cfgProvider.Machine().Kubelet().CredentialProviderConfig() kubeletConfig.AllowSchedulingOnControlPlane = cfgProvider.Cluster().ScheduleOnControlPlanes() return nil }, }, transform.WithExtraInputs( controller.Input{ Namespace: k8s.NamespaceName, Type: k8s.StaticPodServerStatusType, ID: optional.Some(k8s.StaticPodServerStatusResourceID), Kind: controller.InputWeak, }, ), ) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "context" "net/url" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type KubeletConfigSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *KubeletConfigSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(k8sctrl.NewKubeletConfigController())) suite.startRuntime() } func (suite *KubeletConfigSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *KubeletConfigSuite) createStaticPodServerStatus() { staticPodServerStatus := k8s.NewStaticPodServerStatus(k8s.NamespaceName, k8s.StaticPodServerStatusResourceID) staticPodServerStatus.TypedSpec().URL = "http://127.0.0.1:12345" suite.Require().NoError(suite.state.Create(suite.ctx, staticPodServerStatus)) } func (suite *KubeletConfigSuite) TestReconcile() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) suite.createStaticPodServerStatus() cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: "kubelet", KubeletClusterDNS: []string{"10.0.0.1"}, KubeletExtraArgs: v1alpha1.Args{ "enable-feature": v1alpha1.NewArgValue("foo", nil), }, KubeletExtraMounts: []v1alpha1.ExtraMount{ { Destination: "/tmp", Source: "/var", Type: "tmpfs", }, }, KubeletExtraConfig: v1alpha1.Unstructured{ Object: map[string]any{ "serverTLSBootstrap": true, }, }, KubeletDefaultRuntimeSeccompProfileEnabled: new(true), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalEnabled: new(true), }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ DNSDomain: "service.svc", }, }, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { kubeletConfig, err := suite.state.Get( suite.ctx, resource.NewMetadata( k8s.NamespaceName, k8s.KubeletConfigType, k8s.KubeletID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := kubeletConfig.(*k8s.KubeletConfig).TypedSpec() suite.Assert().Equal("kubelet", spec.Image) suite.Assert().Equal([]string{"10.0.0.1"}, spec.ClusterDNS) suite.Assert().Equal("service.svc", spec.ClusterDomain) suite.Assert().Equal( map[string]k8s.ArgValues{ "enable-feature": {Values: []string{"foo"}}, }, spec.ExtraArgs, ) suite.Assert().Equal( []specs.Mount{ { Destination: "/tmp", Source: "/var", Type: "tmpfs", }, }, spec.ExtraMounts, ) suite.Assert().Equal( map[string]any{ "serverTLSBootstrap": true, }, spec.ExtraConfig, ) suite.Assert().True(spec.CloudProviderExternal) suite.Assert().True(spec.DefaultRuntimeSeccompEnabled) return nil }, ), ) } func (suite *KubeletConfigSuite) TestReconcileDefaults() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) suite.createStaticPodServerStatus() cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: "kubelet", }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ ServiceSubnet: []string{constants.DefaultIPv4ServiceNet}, }, }, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { kubeletConfig, err := suite.state.Get( suite.ctx, resource.NewMetadata( k8s.NamespaceName, k8s.KubeletConfigType, k8s.KubeletID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := kubeletConfig.(*k8s.KubeletConfig).TypedSpec() suite.Assert().Equal("kubelet", spec.Image) suite.Assert().Equal([]string{"10.96.0.10"}, spec.ClusterDNS) suite.Assert().Equal(constants.DefaultDNSDomain, spec.ClusterDomain) suite.Assert().Empty(spec.ExtraArgs) suite.Assert().Empty(spec.ExtraMounts) suite.Assert().False(spec.CloudProviderExternal) return nil }, ), ) } func (suite *KubeletConfigSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestKubeletConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, new(KubeletConfigSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "bytes" "context" "crypto/x509" stdjson "encoding/json" "encoding/pem" "errors" "fmt" "net/url" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" talosx509 "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" kubeletv1config "k8s.io/kubelet/config/v1" kubeletconfig "k8s.io/kubelet/config/v1beta1" runtimetalos "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // ServiceManager is the interface to the v1alpha1 services subsystems. type ServiceManager interface { IsRunning(id string) (system.Service, bool, error) Load(services ...system.Service) []string Stop(ctx context.Context, serviceIDs ...string) (err error) Start(serviceIDs ...string) error } // KubeletServiceController renders kubelet configuration files and controls kubelet service lifecycle. type KubeletServiceController struct { V1Alpha1Services ServiceManager V1Alpha1Mode runtimetalos.Mode } // Name implements controller.Controller interface. func (ctrl *KubeletServiceController) Name() string { return "k8s.KubeletServiceController" } // Inputs implements controller.Controller interface. func (ctrl *KubeletServiceController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KubeletServiceController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KubeletServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // initially, wait for the machine-id to be generated and /var to be mounted if err := r.UpdateInputs([]controller.Input{ { Namespace: files.NamespaceName, Type: files.EtcFileStatusType, ID: optional.Some("machine-id"), Kind: controller.InputWeak, }, { Namespace: runtimeres.NamespaceName, Type: runtimeres.MountStatusType, ID: optional.Some(constants.EphemeralPartitionLabel), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } _, err := r.Get(ctx, resource.NewMetadata(files.NamespaceName, files.EtcFileStatusType, "machine-id", resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting etc file status: %w", err) } _, err = r.Get(ctx, resource.NewMetadata(runtimeres.NamespaceName, runtimeres.MountStatusType, constants.EphemeralPartitionLabel, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { // in container mode EPHEMERAL is always mounted if ctrl.V1Alpha1Mode != runtimetalos.ModeContainer { // wait for the EPHEMERAL to be mounted continue } } else { return fmt.Errorf("error getting ephemeral mount status: %w", err) } } break } // normal reconcile loop if err := r.UpdateInputs([]controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.KubeletSpecType, ID: optional.Some(k8s.KubeletID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubeletType, ID: optional.Some(secrets.KubeletID), Kind: controller.InputWeak, }, }); err != nil { return err } r.QueueReconcile() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*k8s.KubeletSpec](ctx, r, k8s.KubeletID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } cfgSpec := cfg.TypedSpec() secret, err := safe.ReaderGetByID[*secrets.Kubelet](ctx, r, secrets.KubeletID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets: %w", err) } secretSpec := secret.TypedSpec() if err = ctrl.writePKI(secretSpec); err != nil { return fmt.Errorf("error writing kubelet PKI: %w", err) } if err = ctrl.writeConfig(cfgSpec); err != nil { return fmt.Errorf("error writing kubelet configuration: %w", err) } if err = ctrl.writeKubeletCredentialProviderConfig(cfgSpec); err != nil { return fmt.Errorf("error writing kubelet credential provider configuration: %w", err) } _, running, err := ctrl.V1Alpha1Services.IsRunning("kubelet") if err != nil { ctrl.V1Alpha1Services.Load(&services.Kubelet{}) } if running { if err = ctrl.V1Alpha1Services.Stop(ctx, "kubelet"); err != nil { return fmt.Errorf("error stopping kubelet service: %w", err) } } if err = ctrl.refreshKubeletCerts(cfgSpec.ExpectedNodename, secretSpec.AcceptedCAs, logger); err != nil { return err } if err = ctrl.handlePolicyChange(cfgSpec, logger); err != nil { return err } if err = ctrl.refreshSelfServingCert(); err != nil { return err } if err = ctrl.updateKubeconfig(secretSpec.Endpoint, secretSpec.AcceptedCAs, logger); err != nil { return err } if err = ctrl.V1Alpha1Services.Start("kubelet"); err != nil { return fmt.Errorf("error starting kubelet service: %w", err) } r.ResetRestartBackoff() } } // handlePolicyChange handles the cpuManagerPolicy change. func (ctrl *KubeletServiceController) handlePolicyChange(cfgSpec *k8s.KubeletSpecSpec, logger *zap.Logger) error { const managerFilename = "/var/lib/kubelet/cpu_manager_state" oldPolicy, err := loadPolicyFromFile(managerFilename) switch { case errors.Is(err, os.ErrNotExist): return nil // no cpu_manager_state file, nothing to do case err != nil: return fmt.Errorf("error loading cpu_manager_state file: %w", err) } policy, err := getFromMap[string](cfgSpec.Config, "cpuManagerPolicy") if err != nil { return err } newPolicy := policy.ValueOrZero() if equalPolicy(oldPolicy, newPolicy) { return nil } logger.Info("cpuManagerPolicy changed", zap.String("old", oldPolicy), zap.String("new", newPolicy)) err = os.Remove(managerFilename) if err != nil { return fmt.Errorf("error removing cpu_manager_state file: %w", err) } return nil } func loadPolicyFromFile(filename string) (string, error) { raw, err := os.ReadFile(filename) if err != nil { return "", err } cpuManagerState := struct { Policy string `json:"policyName"` }{} if err = stdjson.Unmarshal(raw, &cpuManagerState); err != nil { return "", err } return cpuManagerState.Policy, nil } func equalPolicy(current, newOne string) bool { if current == "none" { current = "" } if newOne == "none" { newOne = "" } return current == newOne } func getFromMap[T any](m map[string]any, key string) (optional.Optional[T], error) { var zero optional.Optional[T] res, ok := m[key] if !ok { return zero, nil } if res, ok := res.(T); ok { return optional.Some(res), nil } return zero, fmt.Errorf("unexpected type for key %q: found %T, expected %T", key, res, *new(T)) } func (ctrl *KubeletServiceController) writePKI(secretSpec *secrets.KubeletSpec) error { acceptedCAs := bytes.Join(xslices.Map(secretSpec.AcceptedCAs, func(ca *talosx509.PEMEncodedCertificate) []byte { return ca.Crt }), nil) bootstrapKubeconfig := clientcmdapi.Config{ APIVersion: "v1", Kind: "Config", Clusters: map[string]*clientcmdapi.Cluster{ "local": { Server: secretSpec.Endpoint.String(), CertificateAuthorityData: acceptedCAs, }, }, AuthInfos: map[string]*clientcmdapi.AuthInfo{ "kubelet@local": { Token: fmt.Sprintf("%s.%s", secretSpec.BootstrapTokenID, secretSpec.BootstrapTokenSecret), }, }, Contexts: map[string]*clientcmdapi.Context{ "kubelet@local": { Cluster: "local", AuthInfo: "kubelet@local", }, }, CurrentContext: "kubelet@local", } marshaledKubeConfig, err := clientcmd.Write(bootstrapKubeconfig) if err != nil { return fmt.Errorf("error marshaling kubeconfig: %w", err) } if err := os.WriteFile(constants.KubeletBootstrapKubeconfig, marshaledKubeConfig, 0o600); err != nil { return err } if err := os.MkdirAll(filepath.Dir(constants.KubernetesCACert), 0o700); err != nil { return err } return os.WriteFile(constants.KubernetesCACert, acceptedCAs, 0o400) } func (ctrl *KubeletServiceController) writeConfig(cfgSpec *k8s.KubeletSpecSpec) error { var kubeletConfiguration kubeletconfig.KubeletConfiguration if err := runtime.DefaultUnstructuredConverter.FromUnstructured(cfgSpec.Config, &kubeletConfiguration); err != nil { return fmt.Errorf("error converting kubelet configuration from unstructured: %w", err) } serializer := json.NewSerializerWithOptions( json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ Yaml: true, }, ) var buf bytes.Buffer if err := serializer.Encode(&kubeletConfiguration, &buf); err != nil { return err } return os.WriteFile("/etc/kubernetes/kubelet.yaml", buf.Bytes(), 0o600) } func (ctrl *KubeletServiceController) writeKubeletCredentialProviderConfig(cfgSpec *k8s.KubeletSpecSpec) error { if cfgSpec.CredentialProviderConfig == nil { return os.RemoveAll(constants.KubeletCredentialProviderConfig) } var kubeletCredentialProviderConfig kubeletv1config.CredentialProviderConfig if err := runtime.DefaultUnstructuredConverter.FromUnstructured(cfgSpec.CredentialProviderConfig, &kubeletCredentialProviderConfig); err != nil { return fmt.Errorf("error converting kubelet credentialprovider configuration from unstructured: %w", err) } serializer := json.NewSerializerWithOptions( json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ Yaml: true, }, ) var buf bytes.Buffer if err := serializer.Encode(&kubeletCredentialProviderConfig, &buf); err != nil { return err } return os.WriteFile(constants.KubeletCredentialProviderConfig, buf.Bytes(), 0o600) } // updateKubeconfig updates the kubeconfig of kubelet with the given endpoint if it exists. func (ctrl *KubeletServiceController) updateKubeconfig(newEndpoint *url.URL, acceptedCAs []*talosx509.PEMEncodedCertificate, logger *zap.Logger) error { config, err := clientcmd.LoadFromFile(constants.KubeletKubeconfig) if errors.Is(err, os.ErrNotExist) { return nil } if err != nil { return err } context := config.Contexts[config.CurrentContext] if context == nil { // this should never happen, but we can't fix kubeconfig if it is malformed logger.Error("kubeconfig is missing current context", zap.String("context", config.CurrentContext)) return nil } cluster := config.Clusters[context.Cluster] if cluster == nil { // this should never happen, but we can't fix kubeconfig if it is malformed logger.Error("kubeconfig is missing cluster", zap.String("context", config.CurrentContext), zap.String("cluster", context.Cluster)) return nil } cluster.Server = newEndpoint.String() cluster.CertificateAuthorityData = bytes.Join(xslices.Map(acceptedCAs, func(ca *talosx509.PEMEncodedCertificate) []byte { return ca.Crt }), nil) return clientcmd.WriteToFile(*config, constants.KubeletKubeconfig) } // refreshKubeletCerts checks if the existing kubelet certificates match the node hostname and expected CA. // If they don't match, it clears the certificate directory and the removes kubelet's kubeconfig so that // they can be regenerated next time kubelet is started. // //nolint:gocyclo func (ctrl *KubeletServiceController) refreshKubeletCerts(expectedNodename string, acceptedCAs []*talosx509.PEMEncodedCertificate, logger *zap.Logger) error { cert, err := ctrl.readKubeletClientCertificate() if err != nil { return err } if cert == nil { return nil } valid := true // refresh certs only if we are managing the node name (not overridden by the user) if expectedNodename != "" { expectedCommonName := fmt.Sprintf("system:node:%s", expectedNodename) valid = valid && expectedCommonName == cert.Subject.CommonName if !valid { logger.Info("kubelet client certificate does not match expected nodename, removing", zap.String("expected", expectedCommonName), zap.String("actual", cert.Subject.CommonName), ) } } // check against CAs if valid { rootCAs := x509.NewCertPool() for _, ca := range acceptedCAs { if !rootCAs.AppendCertsFromPEM(ca.Crt) { return fmt.Errorf("error adding CA to root pool: %w", err) } } _, verifyErr := cert.Verify(x509.VerifyOptions{ Roots: rootCAs, KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, }) valid = valid && verifyErr == nil if !valid { logger.Info("kubelet client certificate does not match any accepted CAs, removing", zap.NamedError("verify_error", verifyErr)) } } if valid { // certificate looks good, no need to refresh return nil } // remove the pki directory err = os.RemoveAll(constants.KubeletPKIDir) if err != nil { return err } // clear the kubelet kubeconfig err = os.Remove(constants.KubeletKubeconfig) if errors.Is(err, os.ErrNotExist) { return nil } return err } // refreshSelfServingCert removes the self-signed serving certificate (if exists) to force the kubelet to renew it. func (ctrl *KubeletServiceController) refreshSelfServingCert() error { for _, filename := range []string{ "kubelet.crt", "kubelet.key", } { path := filepath.Join(constants.KubeletPKIDir, filename) _, err := os.Stat(path) if err == nil { err = os.Remove(path) if err != nil { return fmt.Errorf("error removing self-signed certificate: %w", err) } } } return nil } func (ctrl *KubeletServiceController) readKubeletClientCertificate() (*x509.Certificate, error) { raw, err := os.ReadFile(filepath.Join(constants.KubeletPKIDir, "kubelet-client-current.pem")) if errors.Is(err, os.ErrNotExist) { return nil, nil } if err != nil { return nil, err } for { block, rest := pem.Decode(raw) if block == nil { return nil, nil } raw = rest if block.Type != "CERTIFICATE" { continue } var cert *x509.Certificate cert, err = x509.ParseCertificate(block.Bytes) if err != nil { return nil, err } if !cert.IsCA { return cert, nil } } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "slices" "strings" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kubernetes/kubernetes/compatibility" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" kubeletconfig "k8s.io/kubelet/config/v1beta1" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/cgroup" "github.com/siderolabs/talos/pkg/argsbuilder" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/kubelet" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubeletSpecController renders manifests based on templates and config/secrets. type KubeletSpecController struct { V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *KubeletSpecController) Name() string { return "k8s.KubeletSpecController" } // Inputs implements controller.Controller interface. func (ctrl *KubeletSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.KubeletConfigType, ID: optional.Some(k8s.KubeletID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeIPType, ID: optional.Some(k8s.KubeletID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubeletSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.KubeletSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KubeletSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*k8s.KubeletConfig](ctx, r, k8s.KubeletID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } cfgSpec := cfg.TypedSpec() kubeletVersion := compatibility.VersionFromImageRef(cfgSpec.Image) machineType, err := safe.ReaderGetByID[*config.MachineType](ctx, r, config.MachineTypeID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting machine type: %w", err) } nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting nodename: %w", err) } expectedNodename := nodename.TypedSpec().Nodename args := argsbuilder.Args{ "config": argsbuilder.Value{"/etc/kubernetes/kubelet.yaml"}, "cert-dir": argsbuilder.Value{constants.KubeletPKIDir}, "hostname-override": argsbuilder.Value{expectedNodename}, } if !cfgSpec.SkipNodeRegistration { args["bootstrap-kubeconfig"] = argsbuilder.Value{constants.KubeletBootstrapKubeconfig} args["kubeconfig"] = argsbuilder.Value{constants.KubeletKubeconfig} } if cfgSpec.CloudProviderExternal { // we still need to specify `--cloud-provider=external` for the kubelet // to get the node properly tainted so that it gets picked up by the external CCM args["cloud-provider"] = argsbuilder.Value{CloudProviderExternal} } if !kubeletVersion.SupportsKubeletConfigContainerRuntimeEndpoint() { args["container-runtime-endpoint"] = argsbuilder.Value{constants.CRIContainerdAddress} } extraArgs := make(argsbuilder.Args, len(cfgSpec.ExtraArgs)) for k, v := range cfgSpec.ExtraArgs { extraArgs[k] = v.Values } // if the user supplied a hostname override, we do not manage it anymore if extraArgs.Contains("hostname-override") { expectedNodename = "" } // if the user supplied node-ip via extra args, no need to pick automatically if !extraArgs.Contains("node-ip") { nodeIP, nodeErr := safe.ReaderGetByID[*k8s.NodeIP](ctx, r, k8s.KubeletID) if nodeErr != nil { if state.IsNotFoundError(nodeErr) { continue } return fmt.Errorf("error getting node IPs: %w", nodeErr) } nodeIPsString := xslices.Map(nodeIP.TypedSpec().Addresses, netip.Addr.String) args["node-ip"] = argsbuilder.Value{strings.Join(nodeIPsString, ",")} // NOTE: flag has string type, cannot be multiple } if err = args.Merge(extraArgs, argsbuilder.WithMergePolicies( argsbuilder.MergePolicies{ "bootstrap-kubeconfig": argsbuilder.MergeDenied, "kubeconfig": argsbuilder.MergeDenied, "container-runtime": argsbuilder.MergeDenied, "container-runtime-endpoint": argsbuilder.MergeDenied, "config": argsbuilder.MergeDenied, "cert-dir": argsbuilder.MergeDenied, }, )); err != nil { return fmt.Errorf("error merging arguments: %w", err) } // these flags are present from v1.24 if cfgSpec.CredentialProviderConfig != nil { args["image-credential-provider-bin-dir"] = argsbuilder.Value{constants.KubeletCredentialProviderBinDir} args["image-credential-provider-config"] = argsbuilder.Value{constants.KubeletCredentialProviderConfig} } kubeletConfig, err := NewKubeletConfiguration(cfgSpec, kubeletVersion, machineType.MachineType()) if err != nil { return fmt.Errorf("error creating kubelet configuration: %w", err) } // If our platform is container, we cannot rely on the ability to change kernel parameters. // Therefore, we need to NOT attempt to enforce the kernel parameter checking done by the kubelet // when the `ProtectKernelDefaults` setting is enabled. if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { kubeletConfig.ProtectKernelDefaults = false } unstructuredConfig, err := runtime.DefaultUnstructuredConverter.ToUnstructured(kubeletConfig) if err != nil { return fmt.Errorf("error converting to unstructured: %w", err) } if err = safe.WriterModify( ctx, r, k8s.NewKubeletSpec(k8s.NamespaceName, k8s.KubeletID), func(r *k8s.KubeletSpec) error { kubeletSpec := r.TypedSpec() kubeletSpec.Image = cfgSpec.Image kubeletSpec.ExtraMounts = cfgSpec.ExtraMounts kubeletSpec.Args = args.Args() kubeletSpec.Config = unstructuredConfig kubeletSpec.ExpectedNodename = expectedNodename kubeletSpec.CredentialProviderConfig = cfgSpec.CredentialProviderConfig return nil }, ); err != nil { return fmt.Errorf("error modifying KubeletSpec resource: %w", err) } r.ResetRestartBackoff() } } func prepareExtraConfig(extraConfig map[string]any) (*kubeletconfig.KubeletConfiguration, error) { // check for fields that can't be overridden via extraConfig var multiErr *multierror.Error for _, field := range kubelet.ProtectedConfigurationFields { if _, exists := extraConfig[field]; exists { multiErr = multierror.Append(multiErr, fmt.Errorf("field %q can't be overridden", field)) } } if err := multiErr.ErrorOrNil(); err != nil { return nil, err } var config kubeletconfig.KubeletConfiguration // unmarshal extra config into the config structure // as unmarshalling zeroes the missing fields, we can't do that after setting the defaults if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(extraConfig, &config, true); err != nil { return nil, fmt.Errorf("error unmarshalling extra kubelet configuration: %w", err) } return &config, nil } // NewKubeletConfiguration builds kubelet configuration with defaults and overrides from extraConfig. // //nolint:gocyclo,cyclop func NewKubeletConfiguration(cfgSpec *k8s.KubeletConfigSpec, kubeletVersion compatibility.Version, machineType machine.Type) (*kubeletconfig.KubeletConfiguration, error) { config, err := prepareExtraConfig(cfgSpec.ExtraConfig) if err != nil { return nil, err } // required fields (always set) config.TypeMeta = metav1.TypeMeta{ APIVersion: kubeletconfig.SchemeGroupVersion.String(), Kind: "KubeletConfiguration", } if cfgSpec.DisableManifestsDirectory { config.StaticPodPath = "" } else { config.StaticPodPath = constants.ManifestsDirectory } config.StaticPodURL = cfgSpec.StaticPodListURL config.Port = constants.KubeletPort config.Authentication = kubeletconfig.KubeletAuthentication{ X509: kubeletconfig.KubeletX509Authentication{ ClientCAFile: constants.KubernetesCACert, }, Webhook: kubeletconfig.KubeletWebhookAuthentication{ Enabled: new(true), }, Anonymous: kubeletconfig.KubeletAnonymousAuthentication{ Enabled: new(false), }, } config.Authorization = kubeletconfig.KubeletAuthorization{ Mode: kubeletconfig.KubeletAuthorizationModeWebhook, } config.CgroupRoot = cgroup.Root() config.SystemCgroups = cgroup.Path(constants.CgroupSystem) config.KubeletCgroups = cgroup.Path(constants.CgroupKubelet) config.RotateCertificates = true config.ProtectKernelDefaults = true if kubeletVersion.SupportsKubeletConfigContainerRuntimeEndpoint() { config.ContainerRuntimeEndpoint = "unix://" + constants.CRIContainerdAddress } if cfgSpec.DefaultRuntimeSeccompEnabled { config.SeccompDefault = new(true) } if cfgSpec.EnableFSQuotaMonitoring { if _, overridden := config.FeatureGates["LocalStorageCapacityIsolationFSQuotaMonitoring"]; !overridden { if config.FeatureGates == nil { config.FeatureGates = map[string]bool{} } config.FeatureGates["LocalStorageCapacityIsolationFSQuotaMonitoring"] = true } } if cfgSpec.SkipNodeRegistration { config.Authentication.Webhook.Enabled = new(false) config.Authorization.Mode = kubeletconfig.KubeletAuthorizationModeAlwaysAllow } else if machineType.IsControlPlane() && !cfgSpec.AllowSchedulingOnControlPlane { // register with taint to prevent scheduling on control plane nodes race with NodeApplyController applying the initial taint // NodeApplyController will take ownership of the taint after the first successful apply if slices.IndexFunc(config.RegisterWithTaints, func(t corev1.Taint) bool { return t.Key == constants.LabelNodeRoleControlPlane }) == -1 { // don't add the taint if it's already in the config if cfgSpec.ExtraArgs["register-with-taints"].Values == nil { // don't clash with taints provided via extraArgs, it is deprecated on kubelet side config.RegisterWithTaints = append(config.RegisterWithTaints, corev1.Taint{ Key: constants.LabelNodeRoleControlPlane, Effect: corev1.TaintEffectNoSchedule, }, ) } } } // fields which can be overridden if config.Address == "" { config.Address = "0.0.0.0" } if config.OOMScoreAdj == nil { config.OOMScoreAdj = new(int32(constants.KubeletOOMScoreAdj)) } if config.ClusterDomain == "" { config.ClusterDomain = cfgSpec.ClusterDomain } if len(config.ClusterDNS) == 0 { config.ClusterDNS = cfgSpec.ClusterDNS } if config.SerializeImagePulls == nil { config.SerializeImagePulls = new(false) } if config.FailSwapOn == nil { config.FailSwapOn = new(false) } if len(config.SystemReserved) == 0 { config.SystemReserved = map[string]string{ "cpu": constants.KubeletSystemReservedCPU, "pid": constants.KubeletSystemReservedPid, "ephemeral-storage": constants.KubeletSystemReservedEphemeralStorage, } if machineType.IsControlPlane() { config.SystemReserved["memory"] = constants.KubeletSystemReservedMemoryControlPlane } else { config.SystemReserved["memory"] = constants.KubeletSystemReservedMemoryWorker } } if config.Logging.Format == "" { config.Logging.Format = "json" } extraConfig := cfgSpec.ExtraConfig if _, overridden := extraConfig["shutdownGracePeriod"]; !overridden && config.ShutdownGracePeriod.Duration == 0 { config.ShutdownGracePeriod = metav1.Duration{Duration: constants.KubeletShutdownGracePeriod} } if _, overridden := extraConfig["shutdownGracePeriodCriticalPods"]; !overridden && config.ShutdownGracePeriodCriticalPods.Duration == 0 { config.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: constants.KubeletShutdownGracePeriodCriticalPods} } if config.StreamingConnectionIdleTimeout.Duration == 0 { config.StreamingConnectionIdleTimeout = metav1.Duration{Duration: 5 * time.Minute} } if config.TLSMinVersion == "" { config.TLSMinVersion = "VersionTLS13" } config.ResolverConfig = new(constants.PodResolvConfPath) return config, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:goconst package k8s_test import ( "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-kubernetes/kubernetes/compatibility" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" v1 "k8s.io/component-base/logs/api/v1" kubeletconfig "k8s.io/kubelet/config/v1beta1" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type KubeletSpecSuite struct { ctest.DefaultSuite } func (suite *KubeletSpecSuite) TestReconcileDefault() { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v1.29.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.10"} cfg.TypedSpec().ClusterDomain = "cluster.local" cfg.TypedSpec().ExtraArgs = map[string]k8s.ArgValues{"foo": {Values: []string{"bar"}}} cfg.TypedSpec().ExtraMounts = []specs.Mount{ { Destination: "/tmp", Source: "/var", Type: "tmpfs", }, } cfg.TypedSpec().CloudProviderExternal = true suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) nodeIP := k8s.NewNodeIP(k8s.NamespaceName, k8s.KubeletID) nodeIP.TypedSpec().Addresses = []netip.Addr{netip.MustParseAddr("172.20.0.2")} suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeIP)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "example.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(kubeletSpec *k8s.KubeletSpec, asrt *assert.Assertions) { spec := kubeletSpec.TypedSpec() asrt.Equal(cfg.TypedSpec().Image, spec.Image) asrt.Equal( []string{ "--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubeconfig", "--cert-dir=/var/lib/kubelet/pki", "--cloud-provider=external", "--config=/etc/kubernetes/kubelet.yaml", "--foo=bar", "--hostname-override=example.com", "--kubeconfig=/etc/kubernetes/kubeconfig-kubelet", "--node-ip=172.20.0.2", }, spec.Args, ) asrt.Equal(cfg.TypedSpec().ExtraMounts, spec.ExtraMounts) asrt.Equal([]any{"10.96.0.10"}, spec.Config["clusterDNS"]) asrt.Equal("cluster.local", spec.Config["clusterDomain"]) }) } func (suite *KubeletSpecSuite) TestReconcileWithExplicitNodeIP() { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v1.29.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.10"} cfg.TypedSpec().ClusterDomain = "cluster.local" cfg.TypedSpec().ExtraArgs = map[string]k8s.ArgValues{"node-ip": {Values: []string{"10.0.0.1"}}} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "example.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(kubeletSpec *k8s.KubeletSpec, asrt *assert.Assertions) { spec := kubeletSpec.TypedSpec() asrt.Equal(cfg.TypedSpec().Image, spec.Image) asrt.Equal( []string{ "--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubeconfig", "--cert-dir=/var/lib/kubelet/pki", "--config=/etc/kubernetes/kubelet.yaml", "--hostname-override=example.com", "--kubeconfig=/etc/kubernetes/kubeconfig-kubelet", "--node-ip=10.0.0.1", }, spec.Args, ) }) } func (suite *KubeletSpecSuite) TestReconcileWithContainerRuntimeEndpointFlag() { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v1.25.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.10"} cfg.TypedSpec().ClusterDomain = "cluster.local" cfg.TypedSpec().ExtraArgs = map[string]k8s.ArgValues{"node-ip": {Values: []string{"10.0.0.1"}}} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "example.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(kubeletSpec *k8s.KubeletSpec, asrt *assert.Assertions) { spec := kubeletSpec.TypedSpec() asrt.Equal(cfg.TypedSpec().Image, spec.Image) asrt.Equal( []string{ "--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubeconfig", "--cert-dir=/var/lib/kubelet/pki", "--config=/etc/kubernetes/kubelet.yaml", "--container-runtime-endpoint=/run/containerd/containerd.sock", "--hostname-override=example.com", "--kubeconfig=/etc/kubernetes/kubeconfig-kubelet", "--node-ip=10.0.0.1", }, spec.Args, ) var kubeletConfiguration kubeletconfig.KubeletConfiguration if err := k8sruntime.DefaultUnstructuredConverter.FromUnstructured( spec.Config, &kubeletConfiguration, ); err != nil { asrt.NoError(err) return } asrt.Empty(kubeletConfiguration.ContainerRuntimeEndpoint) }) } func (suite *KubeletSpecSuite) TestReconcileWithExtraConfig() { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v2.0.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.11"} cfg.TypedSpec().ClusterDomain = "some.local" cfg.TypedSpec().ExtraConfig = map[string]any{ "serverTLSBootstrap": true, } suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "foo.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) nodeIP := k8s.NewNodeIP(k8s.NamespaceName, k8s.KubeletID) nodeIP.TypedSpec().Addresses = []netip.Addr{netip.MustParseAddr("172.20.0.3")} suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeIP)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(kubeletSpec *k8s.KubeletSpec, asrt *assert.Assertions) { spec := kubeletSpec.TypedSpec() var kubeletConfiguration kubeletconfig.KubeletConfiguration if err := k8sruntime.DefaultUnstructuredConverter.FromUnstructured( spec.Config, &kubeletConfiguration, ); err != nil { asrt.NoError(err) return } asrt.Equal("/", kubeletConfiguration.CgroupRoot) asrt.Equal(cfg.TypedSpec().ClusterDomain, kubeletConfiguration.ClusterDomain) asrt.True(kubeletConfiguration.ServerTLSBootstrap) }) } func (suite *KubeletSpecSuite) TestReconcileWithSkipNodeRegistration() { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v2.0.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.11"} cfg.TypedSpec().ClusterDomain = "some.local" cfg.TypedSpec().SkipNodeRegistration = true suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "foo.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) nodeIP := k8s.NewNodeIP(k8s.NamespaceName, k8s.KubeletID) nodeIP.TypedSpec().Addresses = []netip.Addr{netip.MustParseAddr("172.20.0.3")} suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeIP)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeWorker) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(kubeletSpec *k8s.KubeletSpec, asrt *assert.Assertions) { spec := kubeletSpec.TypedSpec() var kubeletConfiguration kubeletconfig.KubeletConfiguration if err := k8sruntime.DefaultUnstructuredConverter.FromUnstructured( spec.Config, &kubeletConfiguration, ); err != nil { asrt.NoError(err) return } asrt.Equal("/", kubeletConfiguration.CgroupRoot) asrt.Equal(cfg.TypedSpec().ClusterDomain, kubeletConfiguration.ClusterDomain) asrt.Equal([]string{ "--cert-dir=/var/lib/kubelet/pki", "--config=/etc/kubernetes/kubelet.yaml", "--hostname-override=foo.com", "--node-ip=172.20.0.3", }, spec.Args) }) } func TestKubeletSpecSuite(t *testing.T) { t.Parallel() suite.Run(t, &KubeletSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&k8sctrl.KubeletSpecController{})) }, }, }) } func TestNewKubeletConfigurationFail(t *testing.T) { t.Parallel() for _, tt := range []struct { name string cfgSpec *k8s.KubeletConfigSpec expectedErr string }{ { name: "wrong fields", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.96.0.10"}, ClusterDomain: "cluster.svc", ExtraConfig: map[string]any{ "API": "v1", "foo": "bar", "Port": "xyz", }, }, expectedErr: "error unmarshalling extra kubelet configuration: strict decoding error: unknown field \"API\", unknown field \"Port\", unknown field \"foo\"", }, { name: "wrong field type", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.96.0.10"}, ClusterDomain: "cluster.svc", ExtraConfig: map[string]any{ "oomScoreAdj": "v1", }, }, expectedErr: "error unmarshalling extra kubelet configuration: unrecognized type: int32", }, { name: "not overridable", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.96.0.10"}, ClusterDomain: "cluster.svc", ExtraConfig: map[string]any{ "oomScoreAdj": -300, "port": 81, "authentication": nil, }, }, expectedErr: "2 errors occurred:\n\t* field \"authentication\" can't be overridden\n\t* field \"port\" can't be overridden\n\n", }, } { t.Run( tt.name, func(t *testing.T) { t.Parallel() _, err := k8sctrl.NewKubeletConfiguration(tt.cfgSpec, compatibility.VersionFromImageRef(""), machine.TypeWorker) require.Error(t, err) assert.EqualError(t, err, tt.expectedErr) }, ) } } func TestNewKubeletConfigurationMerge(t *testing.T) { t.Parallel() defaultKubeletConfig := kubeletconfig.KubeletConfiguration{ TypeMeta: metav1.TypeMeta{ APIVersion: kubeletconfig.SchemeGroupVersion.String(), Kind: "KubeletConfiguration", }, Port: constants.KubeletPort, Authentication: kubeletconfig.KubeletAuthentication{ X509: kubeletconfig.KubeletX509Authentication{ ClientCAFile: constants.KubernetesCACert, }, Webhook: kubeletconfig.KubeletWebhookAuthentication{ Enabled: new(true), }, Anonymous: kubeletconfig.KubeletAnonymousAuthentication{ Enabled: new(false), }, }, Authorization: kubeletconfig.KubeletAuthorization{ Mode: kubeletconfig.KubeletAuthorizationModeWebhook, }, CgroupRoot: "/", SystemCgroups: constants.CgroupSystem, KubeletCgroups: constants.CgroupKubelet, RotateCertificates: true, ProtectKernelDefaults: true, Address: "0.0.0.0", OOMScoreAdj: new(int32(constants.KubeletOOMScoreAdj)), ClusterDomain: "cluster.local", ClusterDNS: []string{"10.0.0.5"}, SerializeImagePulls: new(false), FailSwapOn: new(false), SystemReserved: map[string]string{ "cpu": constants.KubeletSystemReservedCPU, "memory": constants.KubeletSystemReservedMemoryWorker, "pid": constants.KubeletSystemReservedPid, "ephemeral-storage": constants.KubeletSystemReservedEphemeralStorage, }, Logging: v1.LoggingConfiguration{ Format: "json", }, ShutdownGracePeriod: metav1.Duration{Duration: constants.KubeletShutdownGracePeriod}, ShutdownGracePeriodCriticalPods: metav1.Duration{Duration: constants.KubeletShutdownGracePeriodCriticalPods}, StreamingConnectionIdleTimeout: metav1.Duration{Duration: 5 * time.Minute}, TLSMinVersion: "VersionTLS13", StaticPodPath: constants.ManifestsDirectory, ContainerRuntimeEndpoint: "unix://" + constants.CRIContainerdAddress, ResolverConfig: new(constants.PodResolvConfPath), } for _, tt := range []struct { name string cfgSpec *k8s.KubeletConfigSpec kubeletVersion compatibility.Version expectedOverrides func(*kubeletconfig.KubeletConfiguration) machineType machine.Type }{ { name: "override some", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", ExtraConfig: map[string]any{ "oomScoreAdj": -300, "enableDebuggingHandlers": true, }, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.OOMScoreAdj = new(int32(-300)) kc.EnableDebuggingHandlers = new(true) }, machineType: machine.TypeWorker, }, { name: "controlplane", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.SystemReserved["memory"] = constants.KubeletSystemReservedMemoryControlPlane kc.RegisterWithTaints = []corev1.Taint{ { Key: constants.LabelNodeRoleControlPlane, Effect: corev1.TaintEffectNoSchedule, }, } }, machineType: machine.TypeControlPlane, }, { name: "disable graceful shutdown", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", ExtraConfig: map[string]any{ "shutdownGracePeriod": "0s", "shutdownGracePeriodCriticalPods": "0s", }, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.ShutdownGracePeriod = metav1.Duration{} kc.ShutdownGracePeriodCriticalPods = metav1.Duration{} }, machineType: machine.TypeWorker, }, { name: "enable seccomp default", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", DefaultRuntimeSeccompEnabled: true, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.SeccompDefault = new(true) }, machineType: machine.TypeWorker, }, { name: "enable skipNodeRegistration", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", SkipNodeRegistration: true, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.Authentication.Webhook.Enabled = new(false) kc.Authorization.Mode = kubeletconfig.KubeletAuthorizationModeAlwaysAllow }, machineType: machine.TypeWorker, }, { name: "disable manifests directory", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", DisableManifestsDirectory: true, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.StaticPodPath = "" }, machineType: machine.TypeWorker, }, { name: "enable local FS quota monitoring", cfgSpec: &k8s.KubeletConfigSpec{ ClusterDNS: []string{"10.0.0.5"}, ClusterDomain: "cluster.local", EnableFSQuotaMonitoring: true, }, kubeletVersion: compatibility.VersionFromImageRef("ghcr.io/siderolabs/kubelet:v1.29.0"), expectedOverrides: func(kc *kubeletconfig.KubeletConfiguration) { kc.FeatureGates = map[string]bool{ "LocalStorageCapacityIsolationFSQuotaMonitoring": true, } }, machineType: machine.TypeWorker, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() expected := defaultKubeletConfig.DeepCopy() tt.expectedOverrides(expected) config, err := k8sctrl.NewKubeletConfiguration(tt.cfgSpec, tt.kubeletVersion, tt.machineType) require.NoError(t, err) assert.Equal(t, expected, config) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubelet_static_pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/pkg/kubernetes/kubelet" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // KubeletStaticPodController renders static pod definitions and manages k8s.StaticPodStatus. type KubeletStaticPodController struct{} // Name implements controller.Controller interface. func (ctrl *KubeletStaticPodController) Name() string { return "k8s.KubeletStaticPodController" } // Inputs implements controller.Controller interface. func (ctrl *KubeletStaticPodController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("kubelet"), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesDynamicCertsType, ID: optional.Some(secrets.KubernetesDynamicCertsID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubeletStaticPodController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.StaticPodStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KubeletStaticPodController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { var kubeletClient *kubelet.Client refreshTicker := time.NewTicker(15 * time.Second) // refresh kubelet pods status every 15 seconds defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-refreshTicker.C: if kubeletClient != nil { if err := ctrl.refreshPodStatus(ctx, r, kubeletClient); err != nil { return fmt.Errorf("error refreshing pod status: %w", err) } } continue case <-r.EventCh(): } kubeletService, err := safe.ReaderGet[*v1alpha1.Service](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "kubelet", resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { kubeletClient = nil if err = ctrl.teardownStatuses(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } if !kubeletService.TypedSpec().Running { kubeletClient = nil if err = ctrl.teardownStatuses(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } // on worker nodes, there's no way to connect to the kubelet to fetch the pod status (only API server can do that) // on control plane nodes, use API servers' client kubelet certificate to fetch statuses rootSecrets, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { kubeletClient = nil continue } return err } certsResource, err := safe.ReaderGet[*secrets.KubernetesDynamicCerts]( ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesDynamicCertsType, secrets.KubernetesDynamicCertsID, resource.VersionUndefined), ) if err != nil { if state.IsNotFoundError(err) { kubeletClient = nil continue } return err } certs := certsResource.TypedSpec() nodename, err := safe.ReaderGet[*k8s.Nodename](ctx, r, resource.NewMetadata(k8s.NamespaceName, k8s.NodenameType, k8s.NodenameID, resource.VersionUndefined)) if err != nil { // nodename should exist if the kubelet is running return err } kubeletClient, err = kubelet.NewClient( nodename.TypedSpec().Nodename, certs.APIServerKubeletClient.Crt, certs.APIServerKubeletClient.Key, rootSecrets.TypedSpec().IssuingCA.Crt, ) if err != nil { return fmt.Errorf("error building kubelet client: %w", err) } r.ResetRestartBackoff() } } func (ctrl *KubeletStaticPodController) teardownStatuses(ctx context.Context, r controller.Runtime) error { statuses, err := r.List(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.StaticPodStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing pod statuses: %w", err) } for _, status := range statuses.Items { // TODO: proper teardown sequence? if err = r.Destroy(ctx, status.Metadata()); err != nil { return fmt.Errorf("error destroying stale pod status: %w", err) } } return nil } func (ctrl *KubeletStaticPodController) refreshPodStatus(ctx context.Context, r controller.Runtime, kubeletClient *kubelet.Client) error { podList, err := kubeletClient.Pods(ctx) if err != nil { return fmt.Errorf("error fetching pod status: %w", err) } podsSeen := map[string]struct{}{} for _, pod := range podList.Items { switch pod.Metadata.Annotations.ConfigSource { case "file": // static pod from a file source case "http": // static pod from an HTTP source default: // anything else is not a static pod, skip it continue } statusID := fmt.Sprintf("%s/%s", pod.Metadata.Namespace, pod.Metadata.Name) podsSeen[statusID] = struct{}{} if err = safe.WriterModify(ctx, r, k8s.NewStaticPodStatus(k8s.NamespaceName, statusID), func(r *k8s.StaticPodStatus) error { return k8sadapter.StaticPodStatus(r).SetStatus(&pod.Status) }); err != nil { return fmt.Errorf("error updating pod status: %w", err) } } statuses, err := r.List(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.StaticPodStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing pod statuses: %w", err) } for _, status := range statuses.Items { if _, exists := podsSeen[status.Metadata().ID()]; !exists { // TODO: proper teardown sequence? if err = r.Destroy(ctx, status.Metadata()); err != nil { return fmt.Errorf("error destroying stale pod status: %w", err) } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubeprism.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net" "strconv" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-loadbalancer/controlplane" "github.com/siderolabs/go-loadbalancer/upstream" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubePrismController creates KubePrism load balancer based on KubePrismEndpointsType resource. type KubePrismController struct { balancerHost string balancerPort int lb *controlplane.LoadBalancer ticker *time.Ticker upstreamCh chan []string } // Name implements controller.Controller interface. func (ctrl *KubePrismController) Name() string { return "k8s.KubePrismController" } // Inputs implements controller.Controller interface. func (ctrl *KubePrismController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.KubePrismConfigType, ID: optional.Some(k8s.KubePrismConfigID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubePrismController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.KubePrismStatusesType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KubePrismController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { logger = logger.Named("kubeprism") defer func() { if ctrl.lb == nil { return } ctrl.stopKubePrism(logger) //nolint:errcheck }() for { select { case <-ctx.Done(): return nil case <-ctrl.takeTickerC(): err := ctrl.writeKubePrismStatus(ctx, r) if err != nil { return err } continue case <-r.EventCh(): } lbCfg, err := safe.ReaderGetByID[*k8s.KubePrismConfig](ctx, r, k8s.KubePrismConfigID) if err != nil && !state.IsNotFoundError(err) { return err } switch { case ctrl.lb == nil && lbCfg != nil: err = ctrl.startKubePrism(lbCfg, logger) if err != nil { return err } case ctrl.lb != nil && lbCfg == nil: err = ctrl.stopKubePrism(logger) if err != nil { return err } case ctrl.lb != nil && lbCfg != nil: if lbCfg.TypedSpec().Host != ctrl.balancerHost || lbCfg.TypedSpec().Port != ctrl.balancerPort { err = ctrl.stopKubePrism(logger) if err != nil { return err } err = ctrl.startKubePrism(lbCfg, logger) if err != nil { return err } } else { ctrl.upstreamChan() <- makeEndpoints(lbCfg.TypedSpec()) } } err = ctrl.writeKubePrismStatus(ctx, r) if err != nil { return err } r.ResetRestartBackoff() } } //nolint:gocyclo func (ctrl *KubePrismController) writeKubePrismStatus( ctx context.Context, r controller.Runtime, ) error { if ctrl.lb != nil && ctrl.endpoint() != "" { healthy, err := ctrl.lb.Healthy() if err != nil { return fmt.Errorf("failed to check KubePrism health: %w", err) } got, err := safe.ReaderGetByID[*k8s.KubePrismStatuses](ctx, r, k8s.KubePrismStatusesID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get KubePrism status: %w", err) } if got != nil && got.TypedSpec().Healthy == healthy { return nil } err = safe.WriterModify( ctx, r, k8s.NewKubePrismStatuses(k8s.NamespaceName, k8s.KubePrismStatusesID), func(res *k8s.KubePrismStatuses) error { res.TypedSpec().Host = ctrl.endpoint() res.TypedSpec().Healthy = healthy return nil }, ) if err != nil { return fmt.Errorf("failed to write KubePrism status: %w", err) } } // list keys for cleanup list, err := safe.ReaderListAll[*k8s.KubePrismStatuses](ctx, r) if err != nil { return fmt.Errorf("error listing KubePrism resources: %w", err) } for res := range list.All() { if ctrl.lb == nil || res.Metadata().ID() != k8s.KubePrismStatusesID { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up KubePrism specs: %w", err) } } } return nil } func (ctrl *KubePrismController) startKubePrism(lbCfg *k8s.KubePrismConfig, logger *zap.Logger) error { spec := lbCfg.TypedSpec() ctrl.balancerHost = spec.Host ctrl.balancerPort = spec.Port lb, err := controlplane.NewLoadBalancer(ctrl.balancerHost, ctrl.balancerPort, logger.WithOptions(zap.IncreaseLevel(zap.ErrorLevel)), // silence the load balancer logs controlplane.WithDialTimeout(constants.KubePrismDialTimeout), controlplane.WithKeepAlivePeriod(constants.KubePrismKeepAlivePeriod), controlplane.WithTCPUserTimeout(constants.KubePrismTCPUserTimeout), controlplane.WithHealthCheckOptions( upstream.WithHealthcheckInterval(constants.KubePrismHealthCheckInterval), upstream.WithHealthcheckTimeout(constants.KubePrismHealthCheckTimeout), ), ) if err != nil { return fmt.Errorf("failed to create KubePrism: %w", err) } err = lb.Start(ctrl.upstreamChan()) if err != nil { return fmt.Errorf("failed to start KubePrism: %w", err) } logger.Info("KubePrism is enabled", zap.String("endpoint", ctrl.endpoint())) ctrl.upstreamChan() <- makeEndpoints(spec) ctrl.lb = lb return nil } func makeEndpoints(spec *k8s.KubePrismConfigSpec) []string { return xslices.Map(spec.Endpoints, func(e k8s.KubePrismEndpoint) string { return net.JoinHostPort(e.Host, strconv.FormatUint(uint64(e.Port), 10)) }) } func (ctrl *KubePrismController) takeTickerC() <-chan time.Time { switch { case ctrl.lb == nil && ctrl.ticker == nil: return nil case ctrl.lb != nil && ctrl.ticker == nil: ctrl.ticker = time.NewTicker(5 * time.Second) return ctrl.ticker.C case ctrl.lb == nil: ticker := replaceWithZero(&ctrl.ticker) if ticker != nil { ticker.Stop() } return nil default: return ctrl.ticker.C } } func (ctrl *KubePrismController) endpoint() string { return net.JoinHostPort(ctrl.balancerHost, strconv.FormatUint(uint64(ctrl.balancerPort), 10)) } func (ctrl *KubePrismController) upstreamChan() chan []string { if ctrl.upstreamCh == nil { ctrl.upstreamCh = make(chan []string) } return ctrl.upstreamCh } func (ctrl *KubePrismController) stopKubePrism(logger *zap.Logger) error { replaceWithZero(&ctrl.upstreamCh) lb := replaceWithZero(&ctrl.lb) err := lb.Shutdown() if err != nil { logger.Error("failed to shutdown KubePrism", zap.Error(err)) return err } logger.Info("KubePrism is disabled") return nil } func replaceWithZero[T any](v *T) T { var zero T result := *v *v = zero return result } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubeprism_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "strconv" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xerrors" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubePrismConfigController creates config for KubePrism. type KubePrismConfigController = transform.Controller[*config.MachineConfig, *k8s.KubePrismConfig] // NewKubePrismConfigController instanciates the controller. func NewKubePrismConfigController() *KubePrismConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.KubePrismConfig]{ Name: "k8s.KubePrismConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*k8s.KubePrismConfig] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*k8s.KubePrismConfig]() } if cfg.Config().Machine() == nil { return optional.None[*k8s.KubePrismConfig]() } if !cfg.Config().Machine().Features().KubePrism().Enabled() { return optional.None[*k8s.KubePrismConfig]() } return optional.Some(k8s.NewKubePrismConfig(k8s.NamespaceName, k8s.KubePrismConfigID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *k8s.KubePrismConfig) error { endpt, err := safe.ReaderGetByID[*k8s.KubePrismEndpoints](ctx, r, k8s.KubePrismEndpointsID) if err != nil { if state.IsNotFoundError(err) { return xerrors.NewTaggedf[transform.SkipReconcileTag]("KubePrism endpoints resource not found; not creating KubePrism config") } return err } spec := res.TypedSpec() spec.Endpoints = endpt.TypedSpec().Endpoints spec.Host = "127.0.0.1" spec.Port = cfg.Config().Machine().Features().KubePrism().Port() return nil }, }, transform.WithExtraInputs( safe.Input[*k8s.KubePrismEndpoints](controller.InputWeak), ), ) } func toPort(port string) uint32 { if port == "" { return 443 } p, err := strconv.ParseUint(port, 10, 32) if err != nil { return 443 } return uint32(p) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubeprism_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type KubePrismConfigControllerSuite struct { ctest.DefaultSuite } func (suite *KubePrismConfigControllerSuite) TestGeneration() { cfg := &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ KubePrismSupport: &v1alpha1.KubePrism{ ServerEnabled: new(true), ServerPort: 7445, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: must(url.Parse("https://example.com"))(suite.Require()), }, LocalAPIServerPort: 6445, }, }, } mc := config.NewMachineConfig(container.NewV1Alpha1(cfg)) suite.Create(mc) endpoints := k8s.NewKubePrismEndpoints(k8s.NamespaceName, k8s.KubePrismEndpointsID) endpoints.TypedSpec().Endpoints = []k8s.KubePrismEndpoint{ {Host: "example.com", Port: 443}, {Host: "localhost", Port: 6445}, {Host: "192.168.3.4", Port: 6446}, {Host: "192.168.3.6", Port: 6443}, } suite.Create(endpoints) ctest.AssertResource(suite, k8s.KubePrismConfigID, func(e *k8s.KubePrismConfig, asrt *assert.Assertions) { asrt.Equal( &k8s.KubePrismConfigSpec{ Host: "127.0.0.1", Port: 7445, Endpoints: []k8s.KubePrismEndpoint{ {Host: "example.com", Port: 443}, {Host: "localhost", Port: 6445}, {Host: "192.168.3.4", Port: 6446}, {Host: "192.168.3.6", Port: 6443}, }, }, e.TypedSpec(), ) }) ctest.UpdateWithConflicts(suite, mc, func(cfg *config.MachineConfig) error { balancer := cfg.Config().Machine().Features().KubePrism().(*v1alpha1.KubePrism) balancer.ServerEnabled = new(false) return nil }) ctest.AssertNoResource[*k8s.KubePrismConfig](suite, k8s.KubePrismConfigID) ctest.UpdateWithConflicts(suite, mc, func(cfg *config.MachineConfig) error { balancer := cfg.Config().Machine().Features().KubePrism().(*v1alpha1.KubePrism) balancer.ServerEnabled = new(true) balancer.ServerPort = 7446 return nil }) ctest.AssertResource(suite, k8s.KubePrismConfigID, func(e *k8s.KubePrismConfig, asrt *assert.Assertions) { asrt.Equal( &k8s.KubePrismConfigSpec{ Host: "127.0.0.1", Port: 7446, Endpoints: []k8s.KubePrismEndpoint{ {Host: "example.com", Port: 443}, {Host: "localhost", Port: 6445}, {Host: "192.168.3.4", Port: 6446}, {Host: "192.168.3.6", Port: 6443}, }, }, e.TypedSpec(), ) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), mc.Metadata())) ctest.AssertNoResource[*k8s.KubePrismConfig](suite, k8s.KubePrismConfigID) suite.Create(mc) ctest.AssertResource(suite, k8s.KubePrismConfigID, func(e *k8s.KubePrismConfig, asrt *assert.Assertions) { asrt.Equal( &k8s.KubePrismConfigSpec{ Host: "127.0.0.1", Port: 7445, Endpoints: []k8s.KubePrismEndpoint{ {Host: "example.com", Port: 443}, {Host: "localhost", Port: 6445}, {Host: "192.168.3.4", Port: 6446}, {Host: "192.168.3.6", Port: 6443}, }, }, e.TypedSpec(), ) }) } func TestEndpointsBalancerConfigControllerSuite(t *testing.T) { t.Parallel() suite.Run(t, &KubePrismConfigControllerSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(clusterctrl.NewKubePrismConfigController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubeprism_endpoints.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // KubePrismEndpointsController creates a list of API server endpoints. type KubePrismEndpointsController = transform.Controller[*config.MachineConfig, *k8s.KubePrismEndpoints] // NewKubePrismEndpointsController instanciates the controller. // //nolint:gocyclo func NewKubePrismEndpointsController() *KubePrismEndpointsController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.KubePrismEndpoints]{ Name: "k8s.KubePrismEndpointsController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*k8s.KubePrismEndpoints] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*k8s.KubePrismEndpoints]() } if cfg.Config().Cluster() == nil || cfg.Config().Machine() == nil { return optional.None[*k8s.KubePrismEndpoints]() } return optional.Some(k8s.NewKubePrismEndpoints(k8s.NamespaceName, k8s.KubePrismEndpointsID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, machineConfig *config.MachineConfig, res *k8s.KubePrismEndpoints) error { members, err := safe.ReaderListAll[*cluster.Member](ctx, r) if err != nil { return fmt.Errorf("error listing affiliates: %w", err) } var endpoints []k8s.KubePrismEndpoint ce := machineConfig.Config().Cluster().Endpoint() if ce != nil { endpoints = append(endpoints, k8s.KubePrismEndpoint{ Host: ce.Hostname(), Port: toPort(ce.Port()), }) } if machineConfig.Config().Machine().Type().IsControlPlane() { endpoints = append(endpoints, k8s.KubePrismEndpoint{ Host: "localhost", Port: uint32(machineConfig.Config().Cluster().LocalAPIServerPort()), }) } for member := range members.All() { memberSpec := member.TypedSpec() if len(memberSpec.Addresses) > 0 && memberSpec.ControlPlane != nil { for _, addr := range memberSpec.Addresses { endpoints = append(endpoints, k8s.KubePrismEndpoint{ Host: addr.String(), Port: uint32(memberSpec.ControlPlane.APIServerPort), }) } } } res.TypedSpec().Endpoints = endpoints return nil }, }, transform.WithExtraInputs( safe.Input[*cluster.Member](controller.InputWeak), ), ) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/kubeprism_endpoints_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/netip" "net/url" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" clusterctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type KubePrismControllerSuite struct { ctest.DefaultSuite } func (suite *KubePrismControllerSuite) TestGeneration() { nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Create(nodeIdentity) mc := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: must(url.Parse("https://example.com"))(suite.Require()), }, LocalAPIServerPort: 6445, }, }, })) suite.Create(mc) member1 := cluster.NewMember(cluster.NamespaceName, "service/7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *member1.TypedSpec() = cluster.MemberSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, ControlPlane: &cluster.ControlPlane{APIServerPort: 6446}, } suite.Create(member1) member2 := cluster.NewMember(cluster.NamespaceName, "service/xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F") *member2.TypedSpec() = cluster.MemberSpec{ NodeID: nodeIdentity.TypedSpec().NodeID, Hostname: "foo2.com", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.6")}, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, } suite.Create(member2) member3 := cluster.NewMember(cluster.NamespaceName, "service/9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F") *member3.TypedSpec() = cluster.MemberSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Hostname: "worker-1", MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")}, } suite.Create(member3) ctest.AssertResource(suite, k8s.KubePrismEndpointsID, func(e *k8s.KubePrismEndpoints, asrt *assert.Assertions) { asrt.Equal( &k8s.KubePrismEndpointsSpec{ Endpoints: []k8s.KubePrismEndpoint{ { Host: "example.com", Port: 443, }, { Host: "localhost", Port: 6445, }, { Host: "192.168.3.4", Port: 6446, }, { Host: "192.168.3.6", Port: 6443, }, }, }, e.TypedSpec(), ) }) } func must[T any](res T, err error) func(t *require.Assertions) T { return func(t *require.Assertions) T { t.NoError(err) return res } } func TestEndpointsBalancerControllerSuite(t *testing.T) { t.Parallel() suite.Run(t, &KubePrismControllerSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(clusterctrl.NewKubePrismEndpointsController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/manifest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/k8stemplates" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // ManifestController renders manifests based on templates and config/secrets. type ManifestController struct{} // Name implements controller.Controller interface. func (ctrl *ManifestController) Name() string { return "k8s.ManifestController" } // Inputs implements controller.Controller interface. func (ctrl *ManifestController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.BootstrapManifestsConfigType, Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ManifestController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.ManifestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ManifestController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } configResource, err := safe.ReaderGetByID[*k8s.BootstrapManifestsConfig](ctx, r, k8s.BootstrapManifestsConfigID) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } config := *configResource.TypedSpec() secretsResources, err := safe.ReaderGetByID[*secrets.KubernetesRoot](ctx, r, secrets.KubernetesRootID) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error tearing down: %w", err) } continue } return err } secrets := secretsResources.TypedSpec() renderedManifests, err := ctrl.render(config, secrets) if err != nil { return err } for _, renderedManifest := range renderedManifests { if err = safe.WriterModify(ctx, r, k8s.NewManifest(k8s.ControlPlaneNamespaceName, renderedManifest.name), func(r *k8s.Manifest) error { return k8sadapter.Manifest(r).SetObjects(renderedManifest.objs) }); err != nil { return fmt.Errorf("error updating manifest %q: %w", renderedManifest.name, err) } } // remove any manifests which weren't rendered manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing manifests: %w", err) } manifestsToDelete := make(map[string]struct{}, len(manifests.Items)) for _, manifest := range manifests.Items { if manifest.Metadata().Owner() != ctrl.Name() { continue } manifestsToDelete[manifest.Metadata().ID()] = struct{}{} } for _, renderedManifest := range renderedManifests { delete(manifestsToDelete, renderedManifest.name) } for id := range manifestsToDelete { if err = r.Destroy(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, id, resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning up manifests: %w", err) } } r.ResetRestartBackoff() } } type renderedManifest struct { name string objs []runtime.Object } func (ctrl *ManifestController) render(cfg k8s.BootstrapManifestsConfigSpec, scrt *secrets.KubernetesRootSpec) ([]renderedManifest, error) { manifests := []renderedManifest{ { "00-kubelet-bootstrapping-token", []runtime.Object{ k8stemplates.KubeletBootstrapTokenSecret(scrt), }, }, { "01-csr-node-bootstrap", []runtime.Object{ k8stemplates.CSRNodeBootstrapTemplate(), }, }, { "01-csr-approver-role-binding", []runtime.Object{ k8stemplates.CSRApproverRoleBindingTemplate(), }, }, { "01-csr-renewal-role-binding", []runtime.Object{ k8stemplates.CSRRenewalRoleBindingTemplate(), }, }, { "11-kube-config-in-cluster", []runtime.Object{ k8stemplates.KubeconfigInClusterTemplate(&cfg), }, }, { "11-talos-node-rbac-template", []runtime.Object{ k8stemplates.TalosNodesRBACClusterRoleBinding(), k8stemplates.TalosNodesRBACClusterRole(), }, }, } if cfg.CoreDNSEnabled { manifests = append(manifests, renderedManifest{ "11-core-dns", []runtime.Object{ k8stemplates.CoreDNSServiceAccount(), k8stemplates.CoreDNSClusterRole(), k8stemplates.CoreDNSClusterRoleBinding(), k8stemplates.CoreDNSConfigMap(&cfg), k8stemplates.CoreDNSDeployment(&cfg), }, }, renderedManifest{ "11-core-dns-svc", []runtime.Object{ k8stemplates.CoreDNSService(&cfg), }, }, ) } if cfg.FlannelEnabled { manifests = append(manifests, renderedManifest{ "05-flannel", []runtime.Object{ k8stemplates.FlannelClusterRoleTemplate(&cfg), k8stemplates.FlannelClusterRoleBindingTemplate(), k8stemplates.FlannelServiceAccountTemplate(), k8stemplates.FlannelConfigMapTemplate(&cfg), k8stemplates.FlannelDaemonSetTemplate(&cfg), }, }, ) } if cfg.ProxyEnabled { manifests = append(manifests, renderedManifest{ "10-kube-proxy", []runtime.Object{ k8stemplates.KubeProxyDaemonSetTemplate(&cfg), k8stemplates.KubeProxyServiceAccount(), k8stemplates.KubeProxyClusterRoleBinding(), }, }, ) } if cfg.PodSecurityPolicyEnabled { return nil, fmt.Errorf("pod security policies are not supported anymore, please remove the flag from the configuration") } if cfg.TalosAPIServiceEnabled { manifests = append(manifests, renderedManifest{ "13-talos-service-account-crd", []runtime.Object{ k8stemplates.TalosServiceAccountCRDTemplate(), }, }, ) } return manifests, nil } //nolint:dupl func (ctrl *ManifestController) teardownAll(ctx context.Context, r controller.Runtime) error { manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing manifests: %w", err) } for _, manifest := range manifests.Items { if manifest.Metadata().Owner() != ctrl.Name() { continue } if err = r.Destroy(ctx, manifest.Metadata()); err != nil { return fmt.Errorf("error destroying manifest: %w", err) } } return nil } // TalosServiceAccount is a struct used by the template engine which contains the needed variables to // be able to construct the Talos Service Account CRD. type TalosServiceAccount struct { Group string Version string Kind string ResourceSingular string ResourcePlural string ShortName string } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/manifest_apply.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "cmp" "context" "errors" "fmt" "slices" "sort" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kubernetes/kubernetes/ssa" "github.com/siderolabs/go-kubernetes/kubernetes/ssa/object" "go.uber.org/zap" "go.uber.org/zap/zapcore" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/logging" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // ManifestApplyController applies manifests via control plane endpoint. type ManifestApplyController struct{} // Name implements controller.Controller interface. func (ctrl *ManifestApplyController) Name() string { return "k8s.ManifestApplyController" } // Inputs implements controller.Controller interface. func (ctrl *ManifestApplyController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.KubernetesType, ID: optional.Some(secrets.KubernetesID), Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.ManifestType, Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some("etcd"), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ManifestApplyController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.ManifestStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ManifestApplyController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } secretsResources, err := safe.ReaderGetByID[*secrets.Kubernetes](ctx, r, secrets.KubernetesID) if err != nil { if state.IsNotFoundError(err) { continue } return err } secrets := secretsResources.TypedSpec() // wait for etcd to be healthy as controller relies on etcd for locking etcdResource, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "etcd") if err != nil { if state.IsNotFoundError(err) { continue } return err } if !etcdResource.TypedSpec().Healthy { continue } manifests, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing manifests: %w", err) } slices.SortFunc(manifests.Items, func(a, b resource.Resource) int { return cmp.Compare(a.Metadata().ID(), b.Metadata().ID()) }) if len(manifests.Items) > 0 { if err = ctrl.applyManifests(ctx, logger, manifests, secrets); err != nil { return err } } if err = safe.WriterModify(ctx, r, k8s.NewManifestStatus(k8s.ControlPlaneNamespaceName), func(r *k8s.ManifestStatus) error { status := r.TypedSpec() status.ManifestsApplied = xslices.Map(manifests.Items, func(m resource.Resource) string { return m.Metadata().ID() }) return nil }); err != nil { return fmt.Errorf("error updating manifest status: %w", err) } r.ResetRestartBackoff() } } func (ctrl *ManifestApplyController) applyManifests( ctx context.Context, logger *zap.Logger, manifests resource.List, secrets *secrets.KubernetesCertsSpec, ) error { kubeconfig, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load([]byte(secrets.LocalhostAdminKubeconfig)) }) if err != nil { return fmt.Errorf("error loading kubeconfig: %w", err) } kubeconfig.WarningHandler = rest.NewWarningWriter(logging.NewWriter(logger, zapcore.WarnLevel), rest.WarningWriterOptions{ Deduplicate: true, }) httpClient, err := rest.HTTPClientFor(kubeconfig) if err != nil { return fmt.Errorf("error building HTTP client for kubeconfig: %w", err) } defer httpClient.CloseIdleConnections() discoveryClient, err := discovery.NewDiscoveryClientForConfigAndClient(kubeconfig, httpClient) if err != nil { return fmt.Errorf("error building discovery client: %w", err) } dyn, err := dynamic.NewForConfigAndClient(kubeconfig, httpClient) if err != nil { return fmt.Errorf("error building dynamic client: %w", err) } dc := memory.NewMemCacheClient(discoveryClient) mapper := restmapper.NewDeferredDiscoveryRESTMapper(dc) k8sClient, err := kubernetes.NewForConfigAndClient(kubeconfig, httpClient) if err != nil { return err } if err = etcd.WithLock(ctx, constants.EtcdTalosManifestApplyMutex, logger, func() error { inv, err := ssa.GetInventory(ctx, k8sClient, constants.KubernetesInventoryNamespace, constants.KubernetesBootstrapManifestsInventoryName) if err != nil { return fmt.Errorf("error getting inventory: %w", err) } inventoryContents := inv.Get() inventoryContents, applyErr := ctrl.apply(ctx, logger, mapper, dyn, manifests, inventoryContents) inv.Update(inventoryContents) // update inventory even if the apply process failed half way through err = inv.Write(ctx) if err != nil { err = fmt.Errorf("updating inventory failed: %w", err) } return errors.Join(applyErr, err) }); err != nil { return err } return nil } //nolint:gocyclo,cyclop func (ctrl *ManifestApplyController) apply( ctx context.Context, logger *zap.Logger, mapper *restmapper.DeferredDiscoveryRESTMapper, dyn dynamic.Interface, manifests resource.List, inv object.ObjMetadataSet, ) (object.ObjMetadataSet, error) { // flatten list of objects to be applied objects := xslices.FlatMap(manifests.Items, func(m resource.Resource) []*unstructured.Unstructured { return k8sadapter.Manifest(m.(*k8s.Manifest)).Objects() }) // sort the list so that namespaces come first, followed by CRDs and everything else after that sort.SliceStable(objects, func(i, j int) bool { objL := objects[i] objR := objects[j] gvkL := objL.GroupVersionKind() gvkR := objR.GroupVersionKind() if isNamespace(gvkL) { if !isNamespace(gvkR) { return true } return objL.GetName() < objR.GetName() } if isNamespace(gvkR) { return false } if isCRD(gvkL) { if !isCRD(gvkR) { return true } return objL.GetName() < objR.GetName() } if isCRD(gvkR) { return false } return false }) var multiErr *multierror.Error for _, obj := range objects { gvk := obj.GroupVersionKind() objName := fmt.Sprintf("%s/%s/%s/%s", gvk.Group, gvk.Version, gvk.Kind, obj.GetName()) objMeta, err := object.RuntimeToObjMeta(obj) if err != nil { return nil, fmt.Errorf("failed to retrieve object metadata of %q: %w", objName, err) } // check if the resource is already in the inventory, if so, skip applying it if inv.Contains(objMeta) { continue } mapping, err := mapper.RESTMapping(obj.GroupVersionKind().GroupKind(), obj.GroupVersionKind().Version) if err != nil { switch { case apierrors.IsNotFound(err): fallthrough case apierrors.IsInvalid(err): fallthrough case meta.IsNoMatchError(err): // most probably a problem with the manifest, so we should continue with other manifests multiErr = multierror.Append(multiErr, fmt.Errorf("error creating mapping for object %s: %w", objName, err)) continue default: // connection errors, etc.; it makes no sense to continue with other manifests return nil, fmt.Errorf("error creating mapping for object %s: %w", objName, err) } } var dr dynamic.ResourceInterface if mapping.Scope.Name() == meta.RESTScopeNameNamespace { // default the namespace if it's not set in the manifest if obj.GetNamespace() == "" { obj.SetNamespace(corev1.NamespaceDefault) } // namespaced resources should specify the namespace dr = dyn.Resource(mapping.Resource).Namespace(obj.GetNamespace()) } else { // for cluster-wide resources dr = dyn.Resource(mapping.Resource) } _, err = dr.Get(ctx, obj.GetName(), metav1.GetOptions{}) if err == nil { // already exists, // backfill the inventory if the resource is missing (to migrate to inventory-based apply) inv = inv.Union(object.ObjMetadataSet{objMeta}) continue } if !apierrors.IsNotFound(err) { return nil, fmt.Errorf("error checking resource existence: %w", err) } // Set inventory annotation. annotations := obj.GetAnnotations() if annotations == nil { annotations = make(map[string]string) } inventoryAnnotation, inventoryAnnotationSet := annotations[ssa.InventoryAnnotationKey] if inventoryAnnotationSet && inventoryAnnotation != constants.KubernetesBootstrapManifestsInventoryName { multiErr = multierror.Append(multiErr, fmt.Errorf("unexpected foreign inventory annotation on %s ", objName)) continue } annotations[ssa.InventoryAnnotationKey] = constants.KubernetesBootstrapManifestsInventoryName obj.SetAnnotations(annotations) _, err = dr.Apply(ctx, obj.GetName(), obj, metav1.ApplyOptions{ FieldManager: constants.KubernetesFieldManagerName, }) if err != nil { switch { case apierrors.IsMethodNotSupported(err): fallthrough case apierrors.IsBadRequest(err): fallthrough case apierrors.IsInvalid(err): // resource is malformed, continue with other manifests multiErr = multierror.Append(multiErr, fmt.Errorf("error creating %s: %w", objName, err)) default: // connection errors, etc.; it makes no sense to continue with other manifests return nil, fmt.Errorf("error creating %s: %w", objName, err) } } else { logger.Sugar().Infof("created %s", objName) inv = inv.Union(object.ObjMetadataSet{objMeta}) } } return inv, multiErr.ErrorOrNil() } func isNamespace(gvk schema.GroupVersionKind) bool { return gvk.Kind == "Namespace" && gvk.Version == "v1" } func isCRD(gvk schema.GroupVersionKind) bool { return gvk.Kind == "CustomResourceDefinition" && gvk.Group == "apiextensions.k8s.io" } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/manifest_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "context" "fmt" "slices" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) type ManifestSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *ManifestSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(&k8sctrl.ManifestController{})) suite.startRuntime() } func (suite *ManifestSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } //nolint:dupl func (suite *ManifestSuite) assertManifests(manifests []string) error { resources, err := suite.state.List( suite.ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.ManifestType, "", resource.VersionUndefined), ) if err != nil { return err } ids := xslices.Map(resources.Items, func(r resource.Resource) string { return r.Metadata().ID() }) if !slices.Equal(manifests, ids) { return retry.ExpectedErrorf("expected %q, got %q", manifests, ids) } return nil } var defaultManifestSpec = k8s.BootstrapManifestsConfigSpec{ Server: "127.0.0.1", ClusterDomain: "cluster.", PodCIDRs: []string{constants.DefaultIPv4PodNet}, ProxyEnabled: true, ProxyImage: "foo/bar", ProxyArgs: []string{ fmt.Sprintf("--cluster-cidr=%s", constants.DefaultIPv4PodNet), "--hostname-override=$(NODE_NAME)", "--kubeconfig=/etc/kubernetes/kubeconfig", "--proxy-mode=iptables", "--conntrack-max-per-core=0", }, CoreDNSEnabled: true, CoreDNSImage: "foo/bar", DNSServiceIP: "192.168.0.1", FlannelEnabled: true, FlannelImage: "foo/bar", PodSecurityPolicyEnabled: false, } func (suite *ManifestSuite) TestReconcileDefaults() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) manifestConfig := k8s.NewBootstrapManifestsConfig() *manifestConfig.TypedSpec() = defaultManifestSpec suite.Require().NoError(suite.state.Create(suite.ctx, rootSecrets)) suite.Require().NoError(suite.state.Create(suite.ctx, manifestConfig)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertManifests( []string{ "00-kubelet-bootstrapping-token", "01-csr-approver-role-binding", "01-csr-node-bootstrap", "01-csr-renewal-role-binding", "05-flannel", "10-kube-proxy", "11-core-dns", "11-core-dns-svc", "11-kube-config-in-cluster", "11-talos-node-rbac-template", }, ) }, ), ) } func (suite *ManifestSuite) TestReconcileDisableKubeProxy() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) manifestConfig := k8s.NewBootstrapManifestsConfig() spec := defaultManifestSpec spec.ProxyEnabled = false *manifestConfig.TypedSpec() = spec suite.Require().NoError(suite.state.Create(suite.ctx, rootSecrets)) suite.Require().NoError(suite.state.Create(suite.ctx, manifestConfig)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertManifests( []string{ "00-kubelet-bootstrapping-token", "01-csr-approver-role-binding", "01-csr-node-bootstrap", "01-csr-renewal-role-binding", "05-flannel", "11-core-dns", "11-core-dns-svc", "11-kube-config-in-cluster", "11-talos-node-rbac-template", }, ) }, ), ) } func (suite *ManifestSuite) TestReconcileKubeProxyExtraArgs() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) manifestConfig := k8s.NewBootstrapManifestsConfig() spec := defaultManifestSpec spec.ProxyArgs = append(spec.ProxyArgs, "--bind-address=\"::\"") *manifestConfig.TypedSpec() = spec suite.Require().NoError(suite.state.Create(suite.ctx, rootSecrets)) suite.Require().NoError(suite.state.Create(suite.ctx, manifestConfig)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertManifests( []string{ "00-kubelet-bootstrapping-token", "01-csr-approver-role-binding", "01-csr-node-bootstrap", "01-csr-renewal-role-binding", "05-flannel", "10-kube-proxy", "11-core-dns", "11-core-dns-svc", "11-kube-config-in-cluster", "11-talos-node-rbac-template", }, ) }, ), ) r, err := suite.state.Get( suite.ctx, resource.NewMetadata( k8s.ControlPlaneNamespaceName, k8s.ManifestType, "10-kube-proxy", resource.VersionUndefined, ), ) suite.Require().NoError(err) manifest := r.(*k8s.Manifest) //nolint:forcetypeassert suite.Assert().Len(k8sadapter.Manifest(manifest).Objects(), 3) suite.Assert().Equal("DaemonSet", k8sadapter.Manifest(manifest).Objects()[0].GetKind()) ds := k8sadapter.Manifest(manifest).Objects()[0].Object containerSpec := ds["spec"].(map[string]any)["template"].(map[string]any)["spec"].(map[string]any)["containers"].([]any)[0] args := containerSpec.(map[string]any)["command"].([]any) //nolint:forcetypeassert suite.Assert().Equal("--bind-address=\"::\"", args[len(args)-1]) } func (suite *ManifestSuite) TestReconcileIPv6() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) manifestConfig := k8s.NewBootstrapManifestsConfig() spec := defaultManifestSpec spec.PodCIDRs = []string{constants.DefaultIPv6PodNet} spec.DNSServiceIP = "" spec.DNSServiceIPv6 = "fc00:db8:10::10" *manifestConfig.TypedSpec() = spec suite.Require().NoError(suite.state.Create(suite.ctx, rootSecrets)) suite.Require().NoError(suite.state.Create(suite.ctx, manifestConfig)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertManifests( []string{ "00-kubelet-bootstrapping-token", "01-csr-approver-role-binding", "01-csr-node-bootstrap", "01-csr-renewal-role-binding", "05-flannel", "10-kube-proxy", "11-core-dns", "11-core-dns-svc", "11-kube-config-in-cluster", "11-talos-node-rbac-template", }, ) }, ), ) r, err := suite.state.Get( suite.ctx, resource.NewMetadata( k8s.ControlPlaneNamespaceName, k8s.ManifestType, "11-core-dns-svc", resource.VersionUndefined, ), ) suite.Require().NoError(err) manifest := r.(*k8s.Manifest) //nolint:forcetypeassert suite.Assert().Len(k8sadapter.Manifest(manifest).Objects(), 1) service := k8sadapter.Manifest(manifest).Objects()[0] suite.Assert().Equal("Service", service.GetKind()) v, _, _ := unstructured.NestedString(service.Object, "spec", "clusterIP") //nolint:errcheck suite.Assert().Equal(spec.DNSServiceIPv6, v) vv, _, _ := unstructured.NestedStringSlice(service.Object, "spec", "clusterIPs") //nolint:errcheck suite.Assert().Equal([]string{spec.DNSServiceIPv6}, vv) vv, _, _ = unstructured.NestedStringSlice(service.Object, "spec", "ipFamilies") //nolint:errcheck suite.Assert().Equal([]string{"IPv6"}, vv) v, _, _ = unstructured.NestedString(service.Object, "spec", "ipFamilyPolicy") //nolint:errcheck suite.Assert().Equal("SingleStack", v) r, err = suite.state.Get( suite.ctx, resource.NewMetadata( k8s.ControlPlaneNamespaceName, k8s.ManifestType, "05-flannel", resource.VersionUndefined, ), ) suite.Require().NoError(err) manifest = r.(*k8s.Manifest) //nolint:forcetypeassert suite.Assert().Len(k8sadapter.Manifest(manifest).Objects(), 5) configmap := k8sadapter.Manifest(manifest).Objects()[3] suite.Assert().Equal("ConfigMap", configmap.GetKind()) v, _, _ = unstructured.NestedString(configmap.Object, "data", "net-conf.json") //nolint:errcheck suite.Assert().Contains(v, `"EnableIPv4": false`) suite.Assert().Contains(v, `"EnableIPv6": true`) suite.Assert().Contains(v, fmt.Sprintf(`"IPv6Network": "%s"`, constants.DefaultIPv6PodNet)) } func (suite *ManifestSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestManifestSuite(t *testing.T) { t.Parallel() suite.Run(t, new(ManifestSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_annotation_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "maps" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/labels" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NodeAnnotationSpecController manages k8s.NodeAnnotationsConfig based on configuration. type NodeAnnotationSpecController struct{} // Name implements controller.Controller interface. func (ctrl *NodeAnnotationSpecController) Name() string { return "k8s.NodeAnnotationSpecController" } // Inputs implements controller.Controller interface. func (ctrl *NodeAnnotationSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.ExtensionStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeAnnotationSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeAnnotationSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeAnnotationSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } r.StartTrackingOutputs() nodeAnnotations := map[string]string{} if cfg != nil && cfg.Config().Machine() != nil { maps.Copy(nodeAnnotations, cfg.Config().Machine().NodeAnnotations()) } if err = extensionsToNodeKV( ctx, r, nodeAnnotations, func(annotationValue string) bool { return labels.ValidateLabelValue(annotationValue) != nil }, ); err != nil { return fmt.Errorf("error converting extensions to node annotations: %w", err) } for key, value := range nodeAnnotations { if err = safe.WriterModify(ctx, r, k8s.NewNodeAnnotationSpec(key), func(k *k8s.NodeAnnotationSpec) error { k.TypedSpec().Key = key k.TypedSpec().Value = value return nil }); err != nil { return fmt.Errorf("error updating node label spec: %w", err) } } if err = safe.CleanupOutputs[*k8s.NodeAnnotationSpec](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_annotation_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type NodeAnnotationsSuite struct { ctest.DefaultSuite } func TestNodeAnnotationsSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeAnnotationsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodeAnnotationSpecController{})) }, }, }) } func (suite *NodeAnnotationsSuite) updateMachineConfig(annotations map[string]string) { cfg, err := safe.StateGetByID[*config.MachineConfig](suite.Ctx(), suite.State(), config.ActiveID) if err != nil && !state.IsNotFoundError(err) { suite.Require().NoError(err) } if cfg == nil { cfg = config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineNodeAnnotations: annotations, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) } else { cfg.Container().RawV1Alpha1().MachineConfig.MachineNodeAnnotations = annotations suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) } } func (suite *NodeAnnotationsSuite) TestChangeLabel() { // given expectedAnnotation := "some/annotation" oldValue := "oldValue" expectedValue := "newValue" // when suite.updateMachineConfig(map[string]string{ expectedAnnotation: oldValue, }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedAnnotation}, func(labelSpec *k8s.NodeAnnotationSpec, asrt *assert.Assertions) { asrt.Equal(oldValue, labelSpec.TypedSpec().Value) }) suite.updateMachineConfig(map[string]string{ expectedAnnotation: expectedValue, }) // then rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedAnnotation}, func(labelSpec *k8s.NodeAnnotationSpec, asrt *assert.Assertions) { asrt.Equal(expectedValue, labelSpec.TypedSpec().Value) }) } func (suite *NodeAnnotationsSuite) TestExtensionAnnotations() { ext1 := runtime.NewExtensionStatus(runtime.NamespaceName, "0") ext1.TypedSpec().Metadata = extensions.Metadata{ Name: "zfs", Version: "2.2.4", } ext2 := runtime.NewExtensionStatus(runtime.NamespaceName, "1") ext2.TypedSpec().Metadata = extensions.Metadata{ Name: "drbd", Version: "9.2.8-v1.7.5", } ext3 := runtime.NewExtensionStatus(runtime.NamespaceName, "2") ext3.TypedSpec().Metadata = extensions.Metadata{ Name: "schematic", Version: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", } suite.Require().NoError(suite.State().Create(suite.Ctx(), ext1)) suite.Require().NoError(suite.State().Create(suite.Ctx(), ext2)) suite.Require().NoError(suite.State().Create(suite.Ctx(), ext3)) rtestutils.AssertNoResource[*k8s.NodeAnnotationSpec](suite.Ctx(), suite.T(), suite.State(), "extensions.talos.dev/zfs") rtestutils.AssertNoResource[*k8s.NodeAnnotationSpec](suite.Ctx(), suite.T(), suite.State(), "extensions.talos.dev/drbd") rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"extensions.talos.dev/schematic"}, func(labelSpec *k8s.NodeAnnotationSpec, asrt *assert.Assertions) { asrt.Equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", labelSpec.TypedSpec().Value) }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_apply.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "encoding/json" "fmt" "slices" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "go.uber.org/zap" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // NodeApplyController watches k8s.NodeLabelSpecs, k8s.NodeTaintSpecs and applies them to the k8s Node object. type NodeApplyController struct{} // Name implements controller.Controller interface. func (ctrl *NodeApplyController) Name() string { return "k8s.NodeApplyController" } // Inputs implements controller.Controller interface. func (ctrl *NodeApplyController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.NodeAnnotationSpecType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeLabelSpecType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeTaintSpecType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeCordonedSpecType, Kind: controller.InputWeak, }, { // NodeStatus is used to trigger the controller on node status updates. Namespace: k8s.NamespaceName, Type: k8s.NodeStatusType, Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeApplyController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *NodeApplyController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if err := ctrl.reconcileWithK8s(ctx, r, logger); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *NodeApplyController) getNodeLabelSpecs(ctx context.Context, r controller.Runtime) (map[string]string, error) { items, err := safe.ReaderListAll[*k8s.NodeLabelSpec](ctx, r) if err != nil { return nil, fmt.Errorf("error listing node label spec resources: %w", err) } result := make(map[string]string, items.Len()) for res := range items.All() { result[res.TypedSpec().Key] = res.TypedSpec().Value } return result, nil } func (ctrl *NodeApplyController) getNodeAnnotationSpecs(ctx context.Context, r controller.Runtime) (map[string]string, error) { items, err := safe.ReaderListAll[*k8s.NodeAnnotationSpec](ctx, r) if err != nil { return nil, fmt.Errorf("error listing node annotation spec resources: %w", err) } result := make(map[string]string, items.Len()) for res := range items.All() { result[res.TypedSpec().Key] = res.TypedSpec().Value } return result, nil } func (ctrl *NodeApplyController) getNodeTaintSpecs(ctx context.Context, r controller.Runtime) ([]k8s.NodeTaintSpecSpec, error) { items, err := safe.ReaderListAll[*k8s.NodeTaintSpec](ctx, r) if err != nil { return nil, fmt.Errorf("error listing node taint spec resources: %w", err) } result := make([]k8s.NodeTaintSpecSpec, 0, items.Len()) for res := range items.All() { result = append(result, *res.TypedSpec()) } return result, nil } func (ctrl *NodeApplyController) getNodeCordoned(ctx context.Context, r controller.Runtime) (bool, error) { items, err := safe.ReaderListAll[*k8s.NodeCordonedSpec](ctx, r) if err != nil { return false, fmt.Errorf("error listing node cordoned spec resources: %w", err) } return items.Len() > 0, nil } func (ctrl *NodeApplyController) getK8sClient(ctx context.Context, r controller.Runtime, logger *zap.Logger) (*kubernetes.Client, error) { machineType, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil { return nil, fmt.Errorf("error getting machine type: %w", err) } if machineType.MachineType().IsControlPlane() { return kubernetes.NewTemporaryClientControlPlane(ctx, r) } logger.Debug("waiting for kubelet client config", zap.String("file", constants.KubeletKubeconfig)) if err := conditions.WaitForKubeconfigReady(constants.KubeletKubeconfig).Wait(ctx); err != nil { return nil, err } return kubernetes.NewClientFromKubeletKubeconfig() } func (ctrl *NodeApplyController) reconcileWithK8s( ctx context.Context, r controller.Runtime, logger *zap.Logger, ) error { nodenameResource, err := safe.ReaderGet[*k8s.Nodename](ctx, r, resource.NewMetadata(k8s.NamespaceName, k8s.NodenameType, k8s.NodenameID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { return nil } return err } if nodenameResource.TypedSpec().SkipNodeRegistration { // if the node registration is skipped, we don't need to do anything return nil } nodename := nodenameResource.TypedSpec().Nodename k8sClient, err := ctrl.getK8sClient(ctx, r, logger) if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } if k8sClient == nil { // not ready yet return nil } defer k8sClient.Close() //nolint:errcheck nodeLabelSpecs, err := ctrl.getNodeLabelSpecs(ctx, r) if err != nil { return err } nodeAnnotationSpecs, err := ctrl.getNodeAnnotationSpecs(ctx, r) if err != nil { return err } nodeTaintSpecs, err := ctrl.getNodeTaintSpecs(ctx, r) if err != nil { return err } nodeShouldCordon, err := ctrl.getNodeCordoned(ctx, r) if err != nil { return err } return ctrl.sync(ctx, logger, k8sClient, nodename, nodeLabelSpecs, nodeAnnotationSpecs, nodeTaintSpecs, nodeShouldCordon) } func (ctrl *NodeApplyController) sync( ctx context.Context, logger *zap.Logger, k8sClient *kubernetes.Client, nodeName string, nodeLabelSpecs, nodeAnnotationSpecs map[string]string, nodeTaintSpecs []k8s.NodeTaintSpecSpec, nodeShouldCordon bool, ) error { // run several attempts retrying conflict errors return retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).RetryWithContext(ctx, func(ctx context.Context) error { err := ctrl.syncOnce(ctx, logger, k8sClient, nodeName, nodeLabelSpecs, nodeAnnotationSpecs, nodeTaintSpecs, nodeShouldCordon) if err != nil && (apierrors.IsConflict(err) || apierrors.IsForbidden(err)) { return retry.ExpectedError(err) } return err }) } func umarshalOwnedAnnotation(node *v1.Node, annotation string) (map[string]struct{}, error) { ownedJSON := []byte(node.Annotations[annotation]) var owned []string if len(ownedJSON) > 0 { if err := json.Unmarshal(ownedJSON, &owned); err != nil { return nil, err } } ownedMap := xslices.ToSet(owned) if ownedMap == nil { ownedMap = map[string]struct{}{} } return ownedMap, nil } func marshalOwnedAnnotation(node *v1.Node, annotation string, ownedMap map[string]struct{}) error { owned := maps.Keys(ownedMap) slices.Sort(owned) if len(owned) > 0 { ownedJSON, err := json.Marshal(owned) if err != nil { return err } node.Annotations[annotation] = string(ownedJSON) } else { delete(node.Annotations, annotation) } return nil } func (ctrl *NodeApplyController) syncOnce( ctx context.Context, logger *zap.Logger, k8sClient *kubernetes.Client, nodeName string, nodeLabelSpecs, nodeAnnotationSpecs map[string]string, nodeTaintSpecs []k8s.NodeTaintSpecSpec, nodeShouldCordon bool, ) error { node, err := k8sClient.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) if err != nil { return fmt.Errorf("error getting node: %w", err) } if node.Labels == nil { node.Labels = make(map[string]string) } ownedLabelsMap, err := umarshalOwnedAnnotation(node, constants.AnnotationOwnedLabels) if err != nil { return fmt.Errorf("error unmarshaling owned labels: %w", err) } ownedAnnotationsMap, err := umarshalOwnedAnnotation(node, constants.AnnotationOwnedAnnotations) if err != nil { return fmt.Errorf("error unmarshaling owned annotations: %w", err) } ownedTaintsMap, err := umarshalOwnedAnnotation(node, constants.AnnotationOwnedTaints) if err != nil { return fmt.Errorf("error unmarshaling owned taints: %w", err) } ctrl.ApplyLabels(logger, node, ownedLabelsMap, nodeLabelSpecs) ctrl.ApplyAnnotations(logger, node, ownedAnnotationsMap, nodeAnnotationSpecs) ctrl.ApplyTaints(logger, node, ownedTaintsMap, nodeTaintSpecs) ctrl.ApplyCordoned(logger, node, nodeShouldCordon) if err = marshalOwnedAnnotation(node, constants.AnnotationOwnedLabels, ownedLabelsMap); err != nil { return fmt.Errorf("error marshaling owned labels: %w", err) } if err = marshalOwnedAnnotation(node, constants.AnnotationOwnedAnnotations, ownedAnnotationsMap); err != nil { return fmt.Errorf("error marshaling owned annotations: %w", err) } if err = marshalOwnedAnnotation(node, constants.AnnotationOwnedTaints, ownedTaintsMap); err != nil { return fmt.Errorf("error marshaling owned taints: %w", err) } _, err = k8sClient.CoreV1().Nodes().Update(ctx, node, metav1.UpdateOptions{}) return err } func (ctrl *NodeApplyController) applyNodeKV(logger *zap.Logger, nodeKV map[string]string, owned map[string]struct{}, spec map[string]string) { // set labels from the spec for key, value := range spec { currentValue, exists := nodeKV[key] // label is not set on the node yet, so take it over if !exists { nodeKV[key] = value owned[key] = struct{}{} continue } // no change to the label, skip it if currentValue == value { owned[key] = struct{}{} continue } if _, owned := owned[key]; !owned { logger.Debug("skipping label update, label is not owned", zap.String("key", key), zap.String("value", value)) continue } nodeKV[key] = value } // remove labels which are owned but are not in the spec for key := range owned { if _, exists := spec[key]; !exists { delete(nodeKV, key) delete(owned, key) } } } // ApplyLabels performs the inner loop of the node label reconciliation. // // This method is exported for testing purposes. func (ctrl *NodeApplyController) ApplyLabels(logger *zap.Logger, node *v1.Node, ownedLabels map[string]struct{}, nodeLabelSpecs map[string]string) { ctrl.applyNodeKV(logger, node.Labels, ownedLabels, nodeLabelSpecs) } // ApplyAnnotations performs the inner loop of the node annotation reconciliation. // // This method is exported for testing purposes. func (ctrl *NodeApplyController) ApplyAnnotations(logger *zap.Logger, node *v1.Node, ownedAnnotations map[string]struct{}, nodeAnnotationSpecs map[string]string) { ctrl.applyNodeKV(logger, node.Annotations, ownedAnnotations, nodeAnnotationSpecs) } // ApplyTaints performs the inner loop of the node taints reconciliation. // // This method is exported for testing purposes. // //nolint:gocyclo func (ctrl *NodeApplyController) ApplyTaints(logger *zap.Logger, node *v1.Node, ownedTaints map[string]struct{}, nodeTaints []k8s.NodeTaintSpecSpec) { // set taints from the spec for _, taint := range nodeTaints { var currentValue *v1.Taint for i, nodeTaint := range node.Spec.Taints { if nodeTaint.Key == taint.Key { currentValue = &node.Spec.Taints[i] } } if currentValue == nil { // taint is not set on the node yet, so take it over node.Spec.Taints = append(node.Spec.Taints, v1.Taint{ Key: taint.Key, Value: taint.Value, Effect: v1.TaintEffect(taint.Effect), }) ownedTaints[taint.Key] = struct{}{} } else { // taint with the same key exists, check if it is owned if _, owned := ownedTaints[taint.Key]; owned { // taint is owned, so update it currentValue.Value = taint.Value currentValue.Effect = v1.TaintEffect(taint.Effect) } else if currentValue.Value == taint.Value && currentValue.Effect == v1.TaintEffect(taint.Effect) { // no change to the taint, skip it, but mark it as owned ownedTaints[taint.Key] = struct{}{} } else { logger.Debug("skipping taint update, taint is not owned", zap.String("key", taint.Key), zap.String("value", taint.Value), zap.String("effect", taint.Effect)) } } } // remove taints which are owned but are not in the spec node.Spec.Taints = xslices.FilterInPlace(node.Spec.Taints, func(nodeTaint v1.Taint) bool { if _, owned := ownedTaints[nodeTaint.Key]; !owned { return true } for _, taint := range nodeTaints { if nodeTaint.Key == taint.Key { return true } } delete(ownedTaints, nodeTaint.Key) return false }) } // ApplyCordoned marks the node as unschedulable if it is cordoned. // // This method is exported for testing purposes. func (ctrl *NodeApplyController) ApplyCordoned(logger *zap.Logger, node *v1.Node, shouldCordon bool) { switch { case shouldCordon && !node.Spec.Unschedulable: node.Spec.Unschedulable = true if node.Annotations == nil { node.Annotations = map[string]string{} } node.Annotations[constants.AnnotationCordonedKey] = constants.AnnotationCordonedValue case !shouldCordon && node.Spec.Unschedulable: if _, exists := node.Annotations[constants.AnnotationCordonedKey]; !exists { // not cordoned by Talos, skip return } node.Spec.Unschedulable = false delete(node.Annotations, constants.AnnotationCordonedKey) } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_apply_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "slices" "testing" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "go.uber.org/zap/zaptest" v1 "k8s.io/api/core/v1" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) func TestApplyLabels(t *testing.T) { //nolint:dupl t.Parallel() ctrl := &k8sctrl.NodeApplyController{} logger := zaptest.NewLogger(t) for _, tt := range []struct { name string inputLabels map[string]string ownedLabels []string labelSpec map[string]string expectedLabels map[string]string expectedOwnedLabels []string }{ { name: "empty", inputLabels: map[string]string{}, ownedLabels: []string{}, labelSpec: map[string]string{}, expectedLabels: map[string]string{}, expectedOwnedLabels: []string{}, }, { name: "initial set labels", inputLabels: map[string]string{ "hostname": "foo", }, ownedLabels: []string{}, labelSpec: map[string]string{ "label1": "value1", "label2": "value2", }, expectedLabels: map[string]string{ "hostname": "foo", "label1": "value1", "label2": "value2", }, expectedOwnedLabels: []string{ "label1", "label2", }, }, { name: "update owned labels", inputLabels: map[string]string{ "hostname": "foo", "label1": "value1", "label2": "value2", }, ownedLabels: []string{ "label1", "label2", }, labelSpec: map[string]string{ "label1": "value3", }, expectedLabels: map[string]string{ "hostname": "foo", "label1": "value3", }, expectedOwnedLabels: []string{ "label1", }, }, { name: "ignore not owned labels", inputLabels: map[string]string{ "hostname": "foo", "label1": "value1", "label2": "value2", "label3": "value3", }, ownedLabels: []string{}, labelSpec: map[string]string{ "label1": "value3", "label2": "value2", }, expectedLabels: map[string]string{ "hostname": "foo", "label1": "value1", "label2": "value2", "label3": "value3", }, expectedOwnedLabels: []string{ "label2", }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() node := &v1.Node{} node.Labels = tt.inputLabels ownedLabels := xslices.ToSet(tt.ownedLabels) if ownedLabels == nil { ownedLabels = map[string]struct{}{} } ctrl.ApplyLabels(logger, node, ownedLabels, tt.labelSpec) newOwnedLabels := maps.Keys(ownedLabels) if newOwnedLabels == nil { newOwnedLabels = []string{} } slices.Sort(newOwnedLabels) assert.Equal(t, tt.expectedLabels, node.Labels) assert.Equal(t, tt.expectedOwnedLabels, newOwnedLabels) }) } } func TestApplyAnnotations(t *testing.T) { //nolint:dupl t.Parallel() ctrl := &k8sctrl.NodeApplyController{} logger := zaptest.NewLogger(t) for _, tt := range []struct { name string inputAnnotations map[string]string ownedAnnotations []string annotationSpec map[string]string expectedAnnotations map[string]string expectedOwnedAnnotations []string }{ { name: "empty", inputAnnotations: map[string]string{}, ownedAnnotations: []string{}, annotationSpec: map[string]string{}, expectedAnnotations: map[string]string{}, expectedOwnedAnnotations: []string{}, }, { name: "initial annotations", inputAnnotations: map[string]string{ "hostname": "foo", }, ownedAnnotations: []string{}, annotationSpec: map[string]string{ "talos/foo": "value1", "talos/bar": "value2", }, expectedAnnotations: map[string]string{ "hostname": "foo", "talos/foo": "value1", "talos/bar": "value2", }, expectedOwnedAnnotations: []string{ "talos/bar", "talos/foo", }, }, { name: "update owned annotations", inputAnnotations: map[string]string{ "hostname": "foo", "label1": "value1", "label2": "value2", }, ownedAnnotations: []string{ "label1", "label2", }, annotationSpec: map[string]string{ "label1": "value3", }, expectedAnnotations: map[string]string{ "hostname": "foo", "label1": "value3", }, expectedOwnedAnnotations: []string{ "label1", }, }, { name: "ignore not owned annotations", inputAnnotations: map[string]string{ "hostname": "foo", "ann1": "value1", "ann2": "value2", "ann3": "value3", }, ownedAnnotations: []string{}, annotationSpec: map[string]string{ "ann1": "value3", "ann2": "value2", }, expectedAnnotations: map[string]string{ "hostname": "foo", "ann1": "value1", "ann2": "value2", "ann3": "value3", }, expectedOwnedAnnotations: []string{ "ann2", }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() node := &v1.Node{} node.Annotations = tt.inputAnnotations ownedAnnotations := xslices.ToSet(tt.ownedAnnotations) if ownedAnnotations == nil { ownedAnnotations = map[string]struct{}{} } ctrl.ApplyAnnotations(logger, node, ownedAnnotations, tt.annotationSpec) newOwnedAnnotations := maps.Keys(ownedAnnotations) if newOwnedAnnotations == nil { newOwnedAnnotations = []string{} } slices.Sort(newOwnedAnnotations) assert.Equal(t, tt.expectedAnnotations, node.Annotations) assert.Equal(t, tt.expectedOwnedAnnotations, newOwnedAnnotations) }) } } func TestApplyTaints(t *testing.T) { t.Parallel() ctrl := &k8sctrl.NodeApplyController{} logger := zaptest.NewLogger(t) for _, tt := range []struct { name string inputTaints []v1.Taint ownedTaints []string taintSpec []k8s.NodeTaintSpecSpec expectedTaints []v1.Taint expectedOwnedTaints []string }{ { name: "empty", inputTaints: nil, ownedTaints: []string{}, taintSpec: nil, expectedTaints: nil, expectedOwnedTaints: []string{}, }, { name: "initial set taints", inputTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, }, ownedTaints: []string{}, taintSpec: []k8s.NodeTaintSpecSpec{ { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value2", }, }, expectedTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value2", }, }, expectedOwnedTaints: []string{ "taint1", "taint2", }, }, { name: "update owned taints", inputTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value2", }, }, ownedTaints: []string{ "taint1", "taint2", }, taintSpec: []k8s.NodeTaintSpecSpec{ { Key: "taint1", Value: "value3", }, }, expectedTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, { Key: "taint1", Value: "value3", }, }, expectedOwnedTaints: []string{ "taint1", }, }, { name: "ignore not owned taints", inputTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value2", }, }, ownedTaints: []string{}, taintSpec: []k8s.NodeTaintSpecSpec{ { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value3", }, }, expectedTaints: []v1.Taint{ { Key: "foo", Value: "bar", }, { Key: "taint1", Value: "value1", Effect: "NoSchedule", }, { Key: "taint2", Value: "value2", }, }, expectedOwnedTaints: []string{ "taint1", }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() node := &v1.Node{} node.Spec.Taints = tt.inputTaints ownedTaints := xslices.ToSet(tt.ownedTaints) if ownedTaints == nil { ownedTaints = map[string]struct{}{} } ctrl.ApplyTaints(logger, node, ownedTaints, tt.taintSpec) newOwnedTaints := maps.Keys(ownedTaints) if newOwnedTaints == nil { newOwnedTaints = []string{} } slices.Sort(newOwnedTaints) assert.Equal(t, tt.expectedTaints, node.Spec.Taints) assert.Equal(t, tt.expectedOwnedTaints, newOwnedTaints) }) } } func TestApplyCordoned(t *testing.T) { t.Parallel() ctrl := &k8sctrl.NodeApplyController{} logger := zaptest.NewLogger(t) for _, tt := range []struct { name string inputAnnotations map[string]string inputUnschedulable bool shouldCordon bool expectedUnschedulable bool expectedAnnotations map[string]string }{ { name: "not cordoned - uncordon", inputAnnotations: nil, inputUnschedulable: false, shouldCordon: false, expectedUnschedulable: false, expectedAnnotations: nil, }, { name: "not cordoned - cordon", inputAnnotations: nil, inputUnschedulable: false, shouldCordon: true, expectedUnschedulable: true, expectedAnnotations: map[string]string{constants.AnnotationCordonedKey: constants.AnnotationCordonedValue}, }, { name: "cordoned - no annotation - cordon", inputAnnotations: nil, inputUnschedulable: true, shouldCordon: true, expectedUnschedulable: true, expectedAnnotations: nil, }, { name: "cordoned - with annotation - cordon", inputAnnotations: map[string]string{constants.AnnotationCordonedKey: constants.AnnotationCordonedValue}, inputUnschedulable: true, shouldCordon: true, expectedUnschedulable: true, expectedAnnotations: map[string]string{constants.AnnotationCordonedKey: constants.AnnotationCordonedValue}, }, { name: "cordoned - with annotation - uncordon", inputAnnotations: map[string]string{constants.AnnotationCordonedKey: constants.AnnotationCordonedValue}, inputUnschedulable: true, shouldCordon: false, expectedUnschedulable: false, expectedAnnotations: map[string]string{}, }, { name: "cordoned - no annotation - uncordon", inputAnnotations: map[string]string{"foo": "bar"}, inputUnschedulable: true, shouldCordon: false, expectedUnschedulable: true, expectedAnnotations: map[string]string{"foo": "bar"}, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() node := &v1.Node{} node.Annotations = tt.inputAnnotations node.Spec.Unschedulable = tt.inputUnschedulable ctrl.ApplyCordoned(logger, node, tt.shouldCordon) assert.Equal(t, tt.expectedUnschedulable, node.Spec.Unschedulable) assert.Equal(t, tt.expectedAnnotations, node.Annotations) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_cordoned_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NodeCordonedSpecController manages node cordoned status based on configuration. type NodeCordonedSpecController struct{} // Name implements controller.Controller interface. func (ctrl *NodeCordonedSpecController) Name() string { return "k8s.NodeCordonedSpecController" } // Inputs implements controller.Controller interface. func (ctrl *NodeCordonedSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MachineStatusType, ID: optional.Some(runtime.MachineStatusID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeCordonedSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeCordonedSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeCordonedSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } status, err := safe.ReaderGetByID[*runtime.MachineStatus](ctx, r, runtime.MachineStatusID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } var shouldCordon bool switch status.TypedSpec().Stage { //nolint:exhaustive case runtime.MachineStageShuttingDown, runtime.MachineStageUpgrading, runtime.MachineStageResetting: shouldCordon = true case runtime.MachineStageBooting, runtime.MachineStageRunning: shouldCordon = false default: // don't change cordoned status continue } if shouldCordon { if err = safe.WriterModify(ctx, r, k8s.NewNodeCordonedSpec(k8s.NodeCordonedID), func(k *k8s.NodeCordonedSpec) error { return nil }); err != nil { return fmt.Errorf("error updating node cordoned spec: %w", err) } } else { nodeCordoned, err := safe.ReaderListAll[*k8s.NodeCordonedSpec](ctx, r) if err != nil { return fmt.Errorf("error getting node cordoned specs: %w", err) } for res := range nodeCordoned.All() { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error destroying node cordoned spec: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_cordoned_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type NodeCordonedSuite struct { ctest.DefaultSuite } func TestNodeCordonedSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeCordonedSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodeCordonedSpecController{})) }, }, }) } func (suite *NodeCordonedSuite) updateMachineStage(stage runtime.MachineStage) { status, err := safe.StateGetByID[*runtime.MachineStatus](suite.Ctx(), suite.State(), runtime.MachineStatusID) if err != nil && !state.IsNotFoundError(err) { suite.Require().NoError(err) } if status == nil { status = runtime.NewMachineStatus() status.TypedSpec().Stage = stage suite.Require().NoError(suite.State().Create(suite.Ctx(), status)) } else { status.TypedSpec().Stage = stage suite.Require().NoError(suite.State().Update(suite.Ctx(), status)) } } func (suite *NodeCordonedSuite) TestBootingRunning() { suite.updateMachineStage(runtime.MachineStageBooting) rtestutils.AssertNoResource[*k8s.NodeCordonedSpec](suite.Ctx(), suite.T(), suite.State(), k8s.NodeCordonedID) suite.updateMachineStage(runtime.MachineStageRunning) rtestutils.AssertNoResource[*k8s.NodeCordonedSpec](suite.Ctx(), suite.T(), suite.State(), k8s.NodeCordonedID) } func (suite *NodeCordonedSuite) TestResetting() { suite.updateMachineStage(runtime.MachineStageRunning) rtestutils.AssertNoResource[*k8s.NodeCordonedSpec](suite.Ctx(), suite.T(), suite.State(), k8s.NodeCordonedID) suite.updateMachineStage(runtime.MachineStageResetting) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{k8s.NodeCordonedID}, func(*k8s.NodeCordonedSpec, *assert.Assertions) {}) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_label_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "maps" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/labels" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NodeLabelSpecController manages k8s.NodeLabelsConfig based on configuration. type NodeLabelSpecController struct{} // Name implements controller.Controller interface. func (ctrl *NodeLabelSpecController) Name() string { return "k8s.NodeLabelSpecController" } // Inputs implements controller.Controller interface. func (ctrl *NodeLabelSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.ExtensionStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeLabelSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeLabelSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeLabelSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } r.StartTrackingOutputs() nodeLabels := map[string]string{} if cfg != nil && cfg.Config().Machine() != nil { maps.Copy(nodeLabels, cfg.Config().Machine().NodeLabels()) if cfg.Config().Machine().Type().IsControlPlane() { nodeLabels[constants.LabelNodeRoleControlPlane] = "" } } if err = extensionsToNodeKV( ctx, r, nodeLabels, func(labelValue string) bool { return labels.ValidateLabelValue(labelValue) == nil }, ); err != nil { return fmt.Errorf("error converting extensions to node labels: %w", err) } for key, value := range nodeLabels { if err = safe.WriterModify(ctx, r, k8s.NewNodeLabelSpec(key), func(k *k8s.NodeLabelSpec) error { k.TypedSpec().Key = key k.TypedSpec().Value = value return nil }); err != nil { return fmt.Errorf("error updating node label spec: %w", err) } } if err = safe.CleanupOutputs[*k8s.NodeLabelSpec](ctx, r); err != nil { return err } } } func extensionsToNodeKV(ctx context.Context, r controller.Reader, spec map[string]string, valueFilter func(string) bool) error { extensionStatuses, err := safe.ReaderListAll[*runtime.ExtensionStatus](ctx, r) if err != nil { return fmt.Errorf("error listing extension statuses: %w", err) } for extensionStatus := range extensionStatuses.All() { if extensionStatus.TypedSpec().Metadata.Name == "" { continue } name := constants.K8sExtensionPrefix + extensionStatus.TypedSpec().Metadata.Name value := extensionStatus.TypedSpec().Metadata.Version if labels.ValidateQualifiedName(name) == nil && valueFilter(value) { spec[name] = value } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_label_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type NodeLabelsSuite struct { ctest.DefaultSuite } func TestNodeLabelsSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeLabelsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodeLabelSpecController{})) }, }, }) } func (suite *NodeLabelsSuite) updateMachineConfig(machineType machine.Type, labels map[string]string) { cfg, err := safe.StateGetByID[*config.MachineConfig](suite.Ctx(), suite.State(), config.ActiveID) if err != nil && !state.IsNotFoundError(err) { suite.Require().NoError(err) } if cfg == nil { cfg = config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineType: machineType.String(), MachineNodeLabels: labels, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) } else { cfg.Container().RawV1Alpha1().MachineConfig.MachineNodeLabels = labels cfg.Container().RawV1Alpha1().MachineConfig.MachineType = machineType.String() suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) } } func (suite *NodeLabelsSuite) TestAddLabel() { // given expectedLabel := "expectedLabel" expectedValue := "expectedValue" // when suite.updateMachineConfig(machine.TypeWorker, map[string]string{ expectedLabel: expectedValue, }) // then rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedLabel}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal(expectedValue, labelSpec.TypedSpec().Value) }) rtestutils.AssertNoResource[*k8s.NodeLabelSpec](suite.Ctx(), suite.T(), suite.State(), constants.LabelNodeRoleControlPlane) } func (suite *NodeLabelsSuite) TestChangeLabel() { // given expectedLabel := "someLabel" oldValue := "oldValue" expectedValue := "newValue" // when suite.updateMachineConfig(machine.TypeControlPlane, map[string]string{ expectedLabel: oldValue, }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedLabel}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal(oldValue, labelSpec.TypedSpec().Value) }) suite.updateMachineConfig(machine.TypeControlPlane, map[string]string{ expectedLabel: expectedValue, }) // then rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedLabel}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal(expectedValue, labelSpec.TypedSpec().Value) }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{constants.LabelNodeRoleControlPlane}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Empty(labelSpec.TypedSpec().Value) }) } func (suite *NodeLabelsSuite) TestDeleteLabel() { // given expectedLabel := "label" expectedValue := "labelValue" // when suite.updateMachineConfig(machine.TypeWorker, map[string]string{ expectedLabel: expectedValue, }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{expectedLabel}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal(expectedValue, labelSpec.TypedSpec().Value) }) suite.updateMachineConfig(machine.TypeWorker, map[string]string{}) // then rtestutils.AssertNoResource[*k8s.NodeLabelSpec](suite.Ctx(), suite.T(), suite.State(), expectedLabel) rtestutils.AssertNoResource[*k8s.NodeLabelSpec](suite.Ctx(), suite.T(), suite.State(), constants.LabelNodeRoleControlPlane) } func (suite *NodeLabelsSuite) TestExtensionLabels() { ext1 := runtime.NewExtensionStatus(runtime.NamespaceName, "0") ext1.TypedSpec().Metadata = extensions.Metadata{ Name: "zfs", Version: "2.2.4", } ext2 := runtime.NewExtensionStatus(runtime.NamespaceName, "1") ext2.TypedSpec().Metadata = extensions.Metadata{ Name: "drbd", Version: "9.2.8-v1.7.5", } ext3 := runtime.NewExtensionStatus(runtime.NamespaceName, "2") ext3.TypedSpec().Metadata = extensions.Metadata{ Name: "schematic", Version: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", } suite.Require().NoError(suite.State().Create(suite.Ctx(), ext1)) suite.Require().NoError(suite.State().Create(suite.Ctx(), ext2)) suite.Require().NoError(suite.State().Create(suite.Ctx(), ext3)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"extensions.talos.dev/zfs"}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal("2.2.4", labelSpec.TypedSpec().Value) }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"extensions.talos.dev/drbd"}, func(labelSpec *k8s.NodeLabelSpec, asrt *assert.Assertions) { asrt.Equal("9.2.8-v1.7.5", labelSpec.TypedSpec().Value) }) rtestutils.AssertNoResource[*k8s.NodeLabelSpec](suite.Ctx(), suite.T(), suite.State(), "extensions.talos.dev/schematic") } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/nodewatch" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // watchErrorsThreshold is the number of consecutive watch errors before the controller stops watching. const watchErrorsThreshold = 5 // NodeStatusController pulls list of Affiliate resource from the Kubernetes registry. type NodeStatusController struct{} // Name implements controller.Controller interface. func (ctrl *NodeStatusController) Name() string { return "k8s.NodeStatusController" } // Inputs implements controller.Controller interface. func (ctrl *NodeStatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *NodeStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( kubernetesClient *kubernetes.Client nodewatcher *nodewatch.NodeWatcher watchCtxCancel context.CancelFunc notifyCh <-chan struct{} watchErrCh <-chan error notifyCloser func() watchErrors int watchReady bool ) closeWatcher := func() { if watchCtxCancel != nil { watchCtxCancel() watchCtxCancel = nil } if notifyCloser != nil { notifyCloser() notifyCloser = nil notifyCh = nil watchErrCh = nil } if kubernetesClient != nil { kubernetesClient.Close() //nolint:errcheck kubernetesClient = nil } watchErrors = 0 watchReady = false nodewatcher = nil } defer closeWatcher() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-notifyCh: watchErrors = 0 watchReady = true case watchErr := <-watchErrCh: logger.Error("node watch error", zap.Error(watchErr), zap.Int("error_count", watchErrors)) watchErrors++ if watchErrors >= watchErrorsThreshold { closeWatcher() } else { // keep waiting continue } } nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting nodename: %w", err) } continue } if nodename.TypedSpec().SkipNodeRegistration { // node is not registered with Kubernetes, so we can't pull the status closeWatcher() continue } if err = conditions.WaitForKubeconfigReady(constants.KubeletKubeconfig).Wait(ctx); err != nil { return err } if nodewatcher != nil && nodewatcher.Nodename() != nodename.TypedSpec().Nodename { // nodename changed, so we need to reinitialize the watcher closeWatcher() } if kubernetesClient == nil { kubernetesClient, err = kubernetes.NewClientFromKubeletKubeconfig() if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } } if nodewatcher == nil { nodewatcher = nodewatch.NewNodeWatcher(kubernetesClient, nodename.TypedSpec().Nodename) } if notifyCh == nil { var watchCtx context.Context watchCtx, watchCtxCancel = context.WithCancel(ctx) //nolint:govet notifyCh, watchErrCh, notifyCloser, err = nodewatcher.Watch(watchCtx, logger) if err != nil { return fmt.Errorf("error setting up node watcher: %w", err) //nolint:govet } } if !watchReady { // node watcher is not ready yet, skip updating output resource continue } touchedIDs := make(map[resource.ID]struct{}) node, err := nodewatcher.Get() if err != nil && !apierrors.IsNotFound(err) { return fmt.Errorf("error getting node: %w", err) } if node != nil { podCIDRs := make([]netip.Prefix, 0, len(node.Spec.PodCIDRs)) for _, cidr := range node.Spec.PodCIDRs { prefix, err := netip.ParsePrefix(cidr) if err != nil { logger.Warn("error parsing pod CIDR", zap.String("cidr", cidr), zap.Error(err)) continue } podCIDRs = append(podCIDRs, prefix) } if err = safe.WriterModify(ctx, r, k8s.NewNodeStatus(k8s.NamespaceName, node.Name), func(res *k8s.NodeStatus) error { res.TypedSpec().Nodename = node.Name res.TypedSpec().Unschedulable = node.Spec.Unschedulable res.TypedSpec().Labels = node.Labels res.TypedSpec().Annotations = node.Annotations res.TypedSpec().NodeReady = false res.TypedSpec().PodCIDRs = podCIDRs for _, condition := range node.Status.Conditions { if condition.Type == v1.NodeReady { res.TypedSpec().NodeReady = condition.Status == v1.ConditionTrue } } return nil }, ); err != nil { return err } touchedIDs[node.Name] = struct{}{} } items, err := safe.ReaderListAll[*k8s.NodeStatus](ctx, r) if err != nil { return fmt.Errorf("error listing node statuses: %w", err) } for res := range items.All() { if _, touched := touchedIDs[res.Metadata().ID()]; touched { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error destroying node status: %w", err) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_taint_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // NodeTaintSpecController manages k8s.NodeTaintSpec based on configuration. type NodeTaintSpecController struct{} // Name implements controller.Controller interface. func (ctrl *NodeTaintSpecController) Name() string { return "k8s.NodeTaintSpecController" } // Inputs implements controller.Controller interface. func (ctrl *NodeTaintSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeTaintSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeTaintSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeTaintSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } r.StartTrackingOutputs() if cfg != nil && cfg.Config().Machine() != nil { if cfg.Config().Cluster() != nil { if cfg.Config().Machine().Type().IsControlPlane() && !cfg.Config().Cluster().ScheduleOnControlPlanes() { if err = createTaint(ctx, r, constants.LabelNodeRoleControlPlane, "", string(v1.TaintEffectNoSchedule)); err != nil { return err } } } for key, val := range cfg.Config().Machine().NodeTaints() { value, effect, found := strings.Cut(val, ":") if !found { effect = value value = "" } if err = createTaint(ctx, r, key, value, effect); err != nil { return err } } } if err = safe.CleanupOutputs[*k8s.NodeTaintSpec](ctx, r); err != nil { return err } } } func createTaint(ctx context.Context, r controller.Runtime, key string, val string, effect string) error { if err := safe.WriterModify(ctx, r, k8s.NewNodeTaintSpec(key), func(k *k8s.NodeTaintSpec) error { k.TypedSpec().Key = key k.TypedSpec().Value = val k.TypedSpec().Effect = effect return nil }); err != nil { return fmt.Errorf("error updating node taint spec: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/node_taint_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type NodeTaintsSuite struct { ctest.DefaultSuite } func TestNodeTaintsSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeTaintsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodeTaintSpecController{})) }, }, }) } func (suite *NodeTaintsSuite) updateMachineConfig(machineType machine.Type, allowScheduling bool, taints ...customTaint) { cfg, err := safe.StateGetByID[*config.MachineConfig](suite.Ctx(), suite.State(), config.ActiveID) if err != nil && !state.IsNotFoundError(err) { suite.Require().NoError(err) } nodeTaints := xslices.ToMap(taints, func(t customTaint) (string, string) { return t.key, t.value }) if cfg == nil { cfg = config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineType: machineType.String(), MachineNodeTaints: nodeTaints, }, ClusterConfig: &v1alpha1.ClusterConfig{ AllowSchedulingOnControlPlanes: new(allowScheduling), }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) } else { cfg.Container().RawV1Alpha1().ClusterConfig.AllowSchedulingOnControlPlanes = new(allowScheduling) cfg.Container().RawV1Alpha1().MachineConfig.MachineType = machineType.String() cfg.Container().RawV1Alpha1().MachineConfig.MachineNodeTaints = nodeTaints suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) } } func (suite *NodeTaintsSuite) TestWorker() { suite.updateMachineConfig(machine.TypeWorker, false) rtestutils.AssertNoResource[*k8s.NodeTaintSpec](suite.Ctx(), suite.T(), suite.State(), constants.LabelNodeRoleControlPlane) } func (suite *NodeTaintsSuite) TestControlplane() { suite.updateMachineConfig(machine.TypeControlPlane, false) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{constants.LabelNodeRoleControlPlane}, func(labelSpec *k8s.NodeTaintSpec, asrt *assert.Assertions) { asrt.Empty(labelSpec.TypedSpec().Value) asrt.Equal(string(v1.TaintEffectNoSchedule), labelSpec.TypedSpec().Effect) }) suite.updateMachineConfig(machine.TypeControlPlane, true) rtestutils.AssertNoResource[*k8s.NodeTaintSpec](suite.Ctx(), suite.T(), suite.State(), constants.LabelNodeRoleControlPlane) } func (suite *NodeTaintsSuite) TestCustomTaints() { const customTaintKey = "key1" suite.updateMachineConfig(machine.TypeControlPlane, false, customTaint{ key: customTaintKey, value: "value1:NoSchedule", }) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{customTaintKey}, func(labelSpec *k8s.NodeTaintSpec, asrt *assert.Assertions) { asrt.Equal(customTaintKey, labelSpec.TypedSpec().Key) asrt.Equal("value1", labelSpec.TypedSpec().Value) asrt.Equal(string(v1.TaintEffectNoSchedule), labelSpec.TypedSpec().Effect) }) suite.updateMachineConfig(machine.TypeControlPlane, false) rtestutils.AssertNoResource[*k8s.NodeTaintSpec](suite.Ctx(), suite.T(), suite.State(), customTaintKey) } type customTaint struct { key string value string } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodeip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/net" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NodeIPController renders manifests based on templates and config/secrets. type NodeIPController struct{} // Name implements controller.Controller interface. func (ctrl *NodeIPController) Name() string { return "k8s.NodeIPController" } // Inputs implements controller.Controller interface. func (ctrl *NodeIPController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.NodeIPConfigType, ID: optional.Some(k8s.KubeletID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeIPController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodeIPType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodeIPController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGet[*k8s.NodeIPConfig](ctx, r, resource.NewMetadata(k8s.NamespaceName, k8s.NodeIPConfigType, k8s.KubeletID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } cfgSpec := cfg.TypedSpec() nodeAddrs, err := safe.ReaderGet[*network.NodeAddress]( ctx, r, resource.NewMetadata( network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting addresses: %w", err) } addrs := nodeAddrs.TypedSpec().IPs() cidrs := slices.Concat( cfgSpec.ValidSubnets, xslices.Map(cfgSpec.ExcludeSubnets, func(cidr string) string { return "!" + cidr }), ) ips, err := net.FilterIPs(addrs, cidrs) if err != nil { return fmt.Errorf("error filtering IPs: %w", err) } if len(ips) == 0 { logger.Warn("no suitable node IP found, please make sure .machine.kubelet.nodeIP filters and pod/service subnets are set up correctly") continue } // filter down to make sure only one IPv4 and one IPv6 address stays var hasIPv4, hasIPv6 bool nodeIPs := make([]netip.Addr, 0, 2) for _, ip := range ips { switch { case ip.Is4(): if !hasIPv4 { nodeIPs = append(nodeIPs, ip) hasIPv4 = true } else { logger.Warn("node IP skipped, please use .machine.kubelet.nodeIP to provide explicit subnet for the node IP", zap.Stringer("address", ip)) } case ip.Is6(): if !hasIPv6 { nodeIPs = append(nodeIPs, ip) hasIPv6 = true } else { logger.Warn("node IP skipped, please use .machine.kubelet.nodeIP to provide explicit subnet for the node IP", zap.Stringer("address", ip)) } } } if err = safe.WriterModify( ctx, r, k8s.NewNodeIP(k8s.NamespaceName, k8s.KubeletID), func(r *k8s.NodeIP) error { spec := r.TypedSpec() spec.Addresses = nodeIPs return nil }, ); err != nil { return fmt.Errorf("error modifying NodeIP resource: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodeip_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // NodeIPConfigController configures k8s.NodeIP based on machine config. type NodeIPConfigController = transform.Controller[*config.MachineConfig, *k8s.NodeIPConfig] // NewNodeIPConfigController instanciates the controller. // //nolint:gocyclo func NewNodeIPConfigController() *NodeIPConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *k8s.NodeIPConfig]{ Name: "k8s.NodeIPConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*k8s.NodeIPConfig] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*k8s.NodeIPConfig]() } if cfg.Config().Machine() == nil || cfg.Config().Cluster() == nil { return optional.None[*k8s.NodeIPConfig]() } return optional.Some(k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *k8s.NodeIPConfig) error { spec := res.TypedSpec() cfgProvider := cfg.Config() spec.ValidSubnets = cfgProvider.Machine().Kubelet().NodeIP().ValidSubnets() if len(spec.ValidSubnets) == 0 { // automatically deduce validsubnets from ServiceCIDRs var err error spec.ValidSubnets, err = ipSubnetsFromServiceCIDRs(cfgProvider.Cluster().Network().ServiceCIDRs()) if err != nil { return fmt.Errorf("error building valid subnets: %w", err) } } spec.ExcludeSubnets = nil // filter out Pod & Service CIDRs, they can't be kubelet IPs spec.ExcludeSubnets = append( append( spec.ExcludeSubnets, cfgProvider.Cluster().Network().PodCIDRs()..., ), cfgProvider.Cluster().Network().ServiceCIDRs()..., ) // filter out any virtual IPs, they can't be node IPs either for _, device := range cfgProvider.Machine().Network().Devices() { if device.VIPConfig() != nil { spec.ExcludeSubnets = append(spec.ExcludeSubnets, device.VIPConfig().IP()) } for _, vlan := range device.Vlans() { if vlan.VIPConfig() != nil { spec.ExcludeSubnets = append(spec.ExcludeSubnets, vlan.VIPConfig().IP()) } } } for _, doc := range cfgProvider.NetworkVirtualIPConfigs() { spec.ExcludeSubnets = append(spec.ExcludeSubnets, doc.VIP().String()) } return nil }, }, ) } func ipSubnetsFromServiceCIDRs(serviceCIDRs []string) ([]string, error) { // automatically configure valid IP subnets based on service CIDRs // if the primary service CIDR is IPv4, primary kubelet node IP should be IPv4 as well, and so on result := make([]string, 0, len(serviceCIDRs)) for _, cidr := range serviceCIDRs { network, err := netip.ParsePrefix(cidr) if err != nil { return nil, fmt.Errorf("failed to parse subnet: %w", err) } if network.Addr().Is6() { result = append(result, "::/0") } else { result = append(result, "0.0.0.0/0") } } return result, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodeip_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "net/url" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type NodeIPConfigSuite struct { ctest.DefaultSuite } func (suite *NodeIPConfigSuite) TestReconcileWithSubnets() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{ KubeletNodeIP: &v1alpha1.KubeletNodeIPConfig{ KubeletNodeIPValidSubnets: []string{"10.0.0.0/24"}, }, }, MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy controller NetworkInterfaces: []*v1alpha1.Device{ { DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "1.2.3.4", }, DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 100, VlanVIP: &v1alpha1.DeviceVIPConfig{ SharedIP: "5.6.7.8", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ ServiceSubnet: []string{constants.DefaultIPv4ServiceNet}, PodSubnet: []string{constants.DefaultIPv4PodNet}, }, }, }, ), ) suite.Create(cfg) ctest.AssertResource(suite, k8s.KubeletID, func(cfg *k8s.NodeIPConfig, asrt *assert.Assertions) { spec := cfg.TypedSpec() asrt.Equal([]string{"10.0.0.0/24"}, spec.ValidSubnets) asrt.Equal( []string{"10.244.0.0/16", "10.96.0.0/12", "1.2.3.4", "5.6.7.8"}, spec.ExcludeSubnets, ) }) } func (suite *NodeIPConfigSuite) TestReconcileWithNewVIPs() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfgV1Alpha1 := &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ ServiceSubnet: []string{constants.DefaultIPv4ServiceNet}, PodSubnet: []string{constants.DefaultIPv4PodNet}, }, }, } cfgVIP := network.NewLayer2VIPConfigV1Alpha1("5.6.7.8") cfgVIP.LinkName = "eth0" ctr, err := container.New(cfgV1Alpha1, cfgVIP) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResource(suite, k8s.KubeletID, func(cfg *k8s.NodeIPConfig, asrt *assert.Assertions) { spec := cfg.TypedSpec() asrt.Equal([]string{"0.0.0.0/0"}, spec.ValidSubnets) asrt.Equal( []string{"10.244.0.0/16", "10.96.0.0/12", "5.6.7.8"}, spec.ExcludeSubnets, ) }) } func (suite *NodeIPConfigSuite) TestReconcileDefaults() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ ServiceSubnet: []string{constants.DefaultIPv4ServiceNet, constants.DefaultIPv6ServiceNet}, PodSubnet: []string{constants.DefaultIPv4PodNet, constants.DefaultIPv6PodNet}, }, }, }, ), ) suite.Create(cfg) ctest.AssertResource(suite, k8s.KubeletID, func(cfg *k8s.NodeIPConfig, asrt *assert.Assertions) { spec := cfg.TypedSpec() asrt.Equal([]string{"0.0.0.0/0", "::/0"}, spec.ValidSubnets) asrt.Equal( []string{"10.244.0.0/16", "fc00:db8:10::/56", "10.96.0.0/12", "fc00:db8:20::/112"}, spec.ExcludeSubnets, ) }) } func TestNodeIPConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeIPConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(k8sctrl.NewNodeIPConfigController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodeip_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type NodeIPSuite struct { ctest.DefaultSuite } func (suite *NodeIPSuite) TestReconcileIPv4() { cfg := k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().ValidSubnets = []string{"10.0.0.0/24", "::/0"} cfg.TypedSpec().ExcludeSubnets = []string{"10.0.0.2"} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) addresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), ) addresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.0.0.2/32"), // excluded explicitly netip.MustParsePrefix("10.0.0.5/24"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), addresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) { asrt.Equal("[10.0.0.5]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses)) }) } func (suite *NodeIPSuite) TestReconcileDefaultSubnets() { cfg := k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().ValidSubnets = []string{"0.0.0.0/0", "::/0"} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) addresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), ) addresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.0.0.5/24"), netip.MustParsePrefix("192.168.1.1/24"), netip.MustParsePrefix("2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"), netip.MustParsePrefix("2001:0db8:85a3:0000:0000:8a2e:0370:7335/64"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), addresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) { asrt.Equal("[10.0.0.5 2001:db8:85a3::8a2e:370:7334]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses)) }) } func (suite *NodeIPSuite) TestReconcileNoMatch() { cfg := k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().ValidSubnets = []string{"0.0.0.0/0"} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) addresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), ) addresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.0.0.2/32"), netip.MustParsePrefix("10.0.0.5/24"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), addresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) { asrt.Equal("[10.0.0.2]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses)) }) cfg.TypedSpec().ValidSubnets = nil cfg.TypedSpec().ExcludeSubnets = []string{"10.0.0.2"} suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) // the node IP doesn't change, as there's no match for the filter rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) { asrt.Equal("[10.0.0.2]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses)) }) } func (suite *NodeIPSuite) TestReconcileIPv6Denies() { cfg := k8s.NewNodeIPConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().ValidSubnets = []string{"::/0", "!fd01:cafe::f14c:9fa1:8496:557f/128"} suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) addresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s), ) addresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("fd01:cafe::f14c:9fa1:8496:557f/128"), netip.MustParsePrefix("fd01:cafe::5054:ff:fe1f:c7bd/64"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), addresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.KubeletID}, func(nodeIP *k8s.NodeIP, asrt *assert.Assertions) { asrt.Equal("[fd01:cafe::5054:ff:fe1f:c7bd]", fmt.Sprintf("%s", nodeIP.TypedSpec().Addresses)) }) } func TestNodeIPSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeIPSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodeIPController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodename.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/nodename" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NodenameController renders manifests based on templates and config/secrets. type NodenameController struct{} // Name implements controller.Controller interface. func (ctrl *NodenameController) Name() string { return "k8s.NodenameController" } // Inputs implements controller.Controller interface. func (ctrl *NodenameController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodenameController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.NodenameType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NodenameController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting config: %w", err) } cfgProvider := cfg.Config() if cfgProvider.Machine() == nil { continue } hostnameStatus, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID) if err != nil { if state.IsNotFoundError(err) { continue } return err } if err = safe.WriterModify( ctx, r, k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID), func(res *k8s.Nodename) error { var hostname string if cfgProvider.Machine().Kubelet().RegisterWithFQDN() { hostname = hostnameStatus.TypedSpec().FQDN() } else { hostname = hostnameStatus.TypedSpec().Hostname } res.TypedSpec().Nodename, err = nodename.FromHostname(hostname) if err != nil { return err } res.TypedSpec().HostnameVersion = hostnameStatus.Metadata().Version().String() res.TypedSpec().SkipNodeRegistration = cfgProvider.Machine().Kubelet().SkipNodeRegistration() return nil }, ); err != nil { return fmt.Errorf("error modifying nodename resource: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/nodename_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type NodenameSuite struct { ctest.DefaultSuite } func (suite *NodenameSuite) assertNodename(expected string) { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.NodenameID}, func(nodename *k8s.Nodename, asrt *assert.Assertions) { asrt.Equal(expected, nodename.TypedSpec().Nodename) }) } func (suite *NodenameSuite) TestDefault() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "Foo-" hostnameStatus.TypedSpec().Domainname = "bar.ltd" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) suite.assertNodename("foo") } func (suite *NodenameSuite) TestFQDN() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKubelet: &v1alpha1.KubeletConfig{ KubeletRegisterWithFQDN: new(true), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "foo" hostnameStatus.TypedSpec().Domainname = "bar.ltd" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) suite.assertNodename("foo.bar.ltd") } func TestNodenameSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodenameSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&k8sctrl.NodenameController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/render_config_static_pods.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "bytes" "context" "encoding/json" "fmt" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-kubernetes/kubernetes/compatibility" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json" apiserverv1 "k8s.io/apiserver/pkg/apis/apiserver/v1" auditv1 "k8s.io/apiserver/pkg/apis/audit/v1" schedulerv1 "k8s.io/kube-scheduler/config/v1" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // RenderConfigsStaticPodController manages k8s.ConfigsReady and renders configs for the control plane. type RenderConfigsStaticPodController struct{} // Name implements controller.Controller interface. func (ctrl *RenderConfigsStaticPodController) Name() string { return "k8s.RenderConfigsStaticPodController" } // Inputs implements controller.Controller interface. func (ctrl *RenderConfigsStaticPodController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.AdmissionControlConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.AuditPolicyConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.AuthorizationConfigType, Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.SchedulerConfigType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *RenderConfigsStaticPodController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.ConfigStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *RenderConfigsStaticPodController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } admissionRes, err := safe.ReaderGetByID[*k8s.AdmissionControlConfig](ctx, r, k8s.AdmissionControlConfigID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting admission config resource: %w", err) } admissionConfig := admissionRes.TypedSpec() auditRes, err := safe.ReaderGetByID[*k8s.AuditPolicyConfig](ctx, r, k8s.AuditPolicyConfigID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting audit config resource: %w", err) } auditConfig := auditRes.TypedSpec() authorizerConfigRes, err := safe.ReaderGetByID[*k8s.AuthorizationConfig](ctx, r, k8s.AuthorizationConfigID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting authorization config resource: %w", err) } authorizerConfig := authorizerConfigRes.TypedSpec() kubeAPIServerVersion := compatibility.VersionFromImageRef(authorizerConfig.Image) kubeSchedulerRes, err := safe.ReaderGetByID[*k8s.SchedulerConfig](ctx, r, k8s.SchedulerConfigID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting scheduler config resource: %w", err) } kubeSchedulerConfig := kubeSchedulerRes.TypedSpec() type configFile struct { filename string f func() (runtime.Object, error) } serializer := k8sjson.NewSerializerWithOptions( k8sjson.DefaultMetaFactory, nil, nil, k8sjson.SerializerOptions{ Yaml: true, Pretty: true, Strict: true, }, ) for _, pod := range []struct { name string directory string selinuxLabel string uid int gid int configs []configFile }{ { name: "kube-apiserver", directory: constants.KubernetesAPIServerConfigDir, selinuxLabel: constants.KubernetesAPIServerConfigDirSELinuxLabel, uid: constants.KubernetesAPIServerRunUser, gid: constants.KubernetesAPIServerRunGroup, configs: []configFile{ { filename: "admission-control-config.yaml", f: admissionControlConfig(admissionConfig), }, { filename: "auditpolicy.yaml", f: auditPolicyConfig(auditConfig), }, { filename: "authorization-config.yaml", f: authorizationConfig(authorizerConfig, kubeAPIServerVersion), }, }, }, { name: "kube-scheduler", directory: constants.KubernetesSchedulerConfigDir, selinuxLabel: constants.KubernetesSchedulerConfigDirSELinuxLabel, uid: constants.KubernetesSchedulerRunUser, gid: constants.KubernetesSchedulerRunGroup, configs: []configFile{ { filename: "scheduler-config.yaml", f: schedulerConfig(kubeSchedulerConfig), }, }, }, } { if err = os.MkdirAll(pod.directory, 0o755); err != nil { return fmt.Errorf("error creating config directory for %q: %w", pod.name, err) } if err = selinux.SetLabel(pod.directory, pod.selinuxLabel); err != nil { return err } for _, configFile := range pod.configs { var obj runtime.Object obj, err = configFile.f() if err != nil { return fmt.Errorf("error generating configuration %q for %q: %w", configFile.filename, pod.name, err) } var buf bytes.Buffer if err = serializer.Encode(obj, &buf); err != nil { return fmt.Errorf("error marshaling configuration %q for %q: %w", configFile.filename, pod.name, err) } if err = os.WriteFile(filepath.Join(pod.directory, configFile.filename), buf.Bytes(), 0o400); err != nil { return fmt.Errorf("error writing configuration %q for %q: %w", configFile.filename, pod.name, err) } if err = os.Chown(filepath.Join(pod.directory, configFile.filename), pod.uid, pod.gid); err != nil { return fmt.Errorf("error chowning %q for %q: %w", configFile.filename, pod.name, err) } } } if err = safe.WriterModify(ctx, r, k8s.NewConfigStatus(k8s.ControlPlaneNamespaceName, k8s.ConfigStatusStaticPodID), func(r *k8s.ConfigStatus) error { r.TypedSpec().Ready = true r.TypedSpec().Version = admissionRes.Metadata().Version().String() + auditRes.Metadata().Version().String() + authorizerConfigRes.Metadata().Version().String() + kubeSchedulerRes.Metadata().Version().String() return nil }); err != nil { return err } r.ResetRestartBackoff() } } func admissionControlConfig(spec *k8s.AdmissionControlConfigSpec) func() (runtime.Object, error) { return func() (runtime.Object, error) { var cfg apiserverv1.AdmissionConfiguration cfg.APIVersion = apiserverv1.SchemeGroupVersion.String() cfg.Kind = "AdmissionConfiguration" cfg.Plugins = []apiserverv1.AdmissionPluginConfiguration{} for _, plugin := range spec.Config { raw, err := json.Marshal(plugin.Configuration) if err != nil { return nil, fmt.Errorf("error marshaling configuration for plugin %q: %w", plugin.Name, err) } cfg.Plugins = append(cfg.Plugins, apiserverv1.AdmissionPluginConfiguration{ Name: plugin.Name, Configuration: &runtime.Unknown{ Raw: raw, }, }, ) } return &cfg, nil } } func auditPolicyConfig(spec *k8s.AuditPolicyConfigSpec) func() (runtime.Object, error) { return func() (runtime.Object, error) { var cfg auditv1.Policy if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(spec.Config, &cfg, true); err != nil { return nil, fmt.Errorf("error unmarshaling audit policy configuration: %w", err) } return &cfg, nil } } func schedulerConfig(spec *k8s.SchedulerConfigSpec) func() (runtime.Object, error) { return func() (runtime.Object, error) { var cfg schedulerv1.KubeSchedulerConfiguration if err := runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(spec.Config, &cfg, false); err != nil { return nil, fmt.Errorf("error unmarshaling scheduler configuration: %w", err) } cfg.APIVersion = "kubescheduler.config.k8s.io/v1" cfg.Kind = "KubeSchedulerConfiguration" cfg.ClientConnection.Kubeconfig = filepath.Join(constants.KubernetesSchedulerSecretsDir, "kubeconfig") return &cfg, nil } } func authorizationConfig(spec *k8s.AuthorizationConfigSpec, kubeAPIServerVersion compatibility.Version) func() (runtime.Object, error) { return func() (runtime.Object, error) { var cfg apiserverv1.AuthorizationConfiguration cfg.APIVersion = kubeAPIServerVersion.KubeAPIServerAuthorizationConfigAPIVersion() cfg.Kind = "AuthorizationConfiguration" cfg.Authorizers = []apiserverv1.AuthorizerConfiguration{} for _, authorizer := range spec.Config { authorizerConfig := apiserverv1.AuthorizerConfiguration{ Name: authorizer.Name, Type: authorizer.Type, } if authorizer.Webhook != nil { var webhookCfg apiserverv1.WebhookConfiguration if err := runtime.DefaultUnstructuredConverter.FromUnstructured(authorizer.Webhook, &webhookCfg); err != nil { return nil, fmt.Errorf("error unmarshaling authorizer webhook configuration: %w", err) } authorizerConfig.Webhook = &webhookCfg } cfg.Authorizers = append(cfg.Authorizers, authorizerConfig) } return &cfg, nil } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/render_secrets_static_pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "bytes" "context" "fmt" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s/internal/k8stemplates" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // RenderSecretsStaticPodController manages k8s.SecretsReady and renders secrets from secrets.Kubernetes. type RenderSecretsStaticPodController struct{} // Name implements controller.Controller interface. func (ctrl *RenderSecretsStaticPodController) Name() string { return "k8s.RenderSecretsStaticPodController" } // Inputs implements controller.Controller interface. func (ctrl *RenderSecretsStaticPodController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.EtcdRootType, ID: optional.Some(secrets.EtcdRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesType, ID: optional.Some(secrets.KubernetesID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesDynamicCertsType, ID: optional.Some(secrets.KubernetesDynamicCertsID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.EtcdType, ID: optional.Some(secrets.EtcdID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *RenderSecretsStaticPodController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.SecretsStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *RenderSecretsStaticPodController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } secretsRes, err := safe.ReaderGet[*secrets.Kubernetes](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesType, secrets.KubernetesID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets resource: %w", err) } certsRes, err := safe.ReaderGet[*secrets.KubernetesDynamicCerts]( ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesDynamicCertsType, secrets.KubernetesDynamicCertsID, resource.VersionUndefined), ) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting certificates resource: %w", err) } etcdRes, err := safe.ReaderGet[*secrets.Etcd](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdType, secrets.EtcdID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets resource: %w", err) } rootEtcdRes, err := safe.ReaderGet[*secrets.EtcdRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdRootType, secrets.EtcdRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets resource: %w", err) } rootK8sRes, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting secrets resource: %w", err) } rootEtcdSecrets := rootEtcdRes.TypedSpec() rootK8sSecrets := rootK8sRes.TypedSpec() etcdSecrets := etcdRes.TypedSpec() k8sSecrets := secretsRes.TypedSpec() k8sCerts := certsRes.TypedSpec() serviceAccountKey, err := rootK8sSecrets.ServiceAccount.GetKey() if err != nil { return fmt.Errorf("error parsing service account key: %w", err) } type secret struct { getter func() *x509.PEMEncodedCertificateAndKey certFilename string keyFilename string } type file struct { filename string contentFunc func() ([]byte, error) } for _, pod := range []struct { name string directory string selinuxLabel string uid int gid int secrets []secret files []file }{ { name: "kube-apiserver", directory: constants.KubernetesAPIServerSecretsDir, selinuxLabel: constants.KubernetesAPIServerSecretsDirSELinuxLabel, uid: constants.KubernetesAPIServerRunUser, gid: constants.KubernetesAPIServerRunGroup, secrets: []secret{ { getter: func() *x509.PEMEncodedCertificateAndKey { return rootEtcdSecrets.EtcdCA }, certFilename: "etcd-client-ca.crt", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return etcdSecrets.EtcdAPIServer }, certFilename: "etcd-client.crt", keyFilename: "etcd-client.key", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return &x509.PEMEncodedCertificateAndKey{ Crt: bytes.Join(xslices.Map(rootK8sSecrets.AcceptedCAs, func(ca *x509.PEMEncodedCertificate) []byte { return ca.Crt }), nil), } }, certFilename: "ca.crt", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return k8sCerts.APIServer }, certFilename: "apiserver.crt", keyFilename: "apiserver.key", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return k8sCerts.APIServerKubeletClient }, certFilename: "apiserver-kubelet-client.crt", keyFilename: "apiserver-kubelet-client.key", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return &x509.PEMEncodedCertificateAndKey{ Crt: serviceAccountKey.GetPublicKeyPEM(), Key: serviceAccountKey.GetPrivateKeyPEM(), } }, certFilename: "service-account.pub", keyFilename: "service-account.key", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return rootK8sSecrets.AggregatorCA }, certFilename: "aggregator-ca.crt", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return k8sCerts.FrontProxy }, certFilename: "front-proxy-client.crt", keyFilename: "front-proxy-client.key", }, }, files: []file{ { filename: "encryptionconfig.yaml", contentFunc: func() ([]byte, error) { return k8stemplates.Marshal(k8stemplates.APIServerEncryptionConfig(rootK8sSecrets)) }, }, }, }, { name: "kube-controller-manager", directory: constants.KubernetesControllerManagerSecretsDir, selinuxLabel: constants.KubernetesControllerManagerSecretsDirSELinuxLabel, uid: constants.KubernetesControllerManagerRunUser, gid: constants.KubernetesControllerManagerRunGroup, secrets: []secret{ { getter: func() *x509.PEMEncodedCertificateAndKey { return rootK8sSecrets.IssuingCA }, certFilename: "ca.crt", keyFilename: "ca.key", }, { getter: func() *x509.PEMEncodedCertificateAndKey { return &x509.PEMEncodedCertificateAndKey{ Crt: serviceAccountKey.GetPublicKeyPEM(), Key: serviceAccountKey.GetPrivateKeyPEM(), } }, keyFilename: "service-account.key", }, }, files: []file{ { filename: "kubeconfig", contentFunc: func() ([]byte, error) { return []byte(k8sSecrets.ControllerManagerKubeconfig), nil }, }, }, }, { name: "kube-scheduler", directory: constants.KubernetesSchedulerSecretsDir, selinuxLabel: constants.KubernetesSchedulerSecretsDirSELinuxLabel, uid: constants.KubernetesSchedulerRunUser, gid: constants.KubernetesSchedulerRunGroup, files: []file{ { filename: "kubeconfig", contentFunc: func() ([]byte, error) { return []byte(k8sSecrets.SchedulerKubeconfig), nil }, }, }, }, } { if err = os.MkdirAll(pod.directory, 0o755); err != nil { return fmt.Errorf("error creating secrets directory for %q: %w", pod.name, err) } if err = selinux.SetLabel(pod.directory, pod.selinuxLabel); err != nil { return err } for _, secret := range pod.secrets { certAndKey := secret.getter() if secret.certFilename != "" { if err = os.WriteFile(filepath.Join(pod.directory, secret.certFilename), certAndKey.Crt, 0o400); err != nil { return fmt.Errorf("error writing certificate %q for %q: %w", secret.certFilename, pod.name, err) } if err = os.Chown(filepath.Join(pod.directory, secret.certFilename), pod.uid, pod.gid); err != nil { return fmt.Errorf("error chowning %q for %q: %w", secret.certFilename, pod.name, err) } } if secret.keyFilename != "" { if err = os.WriteFile(filepath.Join(pod.directory, secret.keyFilename), certAndKey.Key, 0o400); err != nil { return fmt.Errorf("error writing key %q for %q: %w", secret.keyFilename, pod.name, err) } if err = os.Chown(filepath.Join(pod.directory, secret.keyFilename), pod.uid, pod.gid); err != nil { return fmt.Errorf("error chowning %q for %q: %w", secret.keyFilename, pod.name, err) } } } for _, file := range pod.files { fileContent, err := file.contentFunc() if err != nil { return fmt.Errorf("error getting content for file %q for %q: %w", file.filename, pod.name, err) } if err = os.WriteFile(filepath.Join(pod.directory, file.filename), fileContent, 0o400); err != nil { return fmt.Errorf("error writing file %q for %q: %w", file.filename, pod.name, err) } if err = os.Chown(filepath.Join(pod.directory, file.filename), pod.uid, pod.gid); err != nil { return fmt.Errorf("error chowning %q for %q: %w", file.filename, pod.name, err) } } } if err = safe.WriterModify(ctx, r, k8s.NewSecretsStatus(k8s.ControlPlaneNamespaceName, k8s.StaticPodSecretsStaticPodID), func(r *k8s.SecretsStatus) error { r.TypedSpec().Ready = true r.TypedSpec().Version = secretsRes.Metadata().Version().String() return nil }); err != nil { return err } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // StaticEndpointController injects endpoints based on machine configuration. type StaticEndpointController struct{} // Name implements controller.Controller interface. func (ctrl *StaticEndpointController) Name() string { return "k8s.StaticEndpointController" } // Inputs implements controller.Controller interface. func (ctrl *StaticEndpointController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *StaticEndpointController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.EndpointType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *StaticEndpointController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } machineConfig, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } r.StartTrackingOutputs() if machineConfig != nil && machineConfig.Config().Cluster() != nil { cpHostname := machineConfig.Config().Cluster().Endpoint().Hostname() var ( resolver net.Resolver addrs []netip.Addr hosts []string ) addrs, err = resolver.LookupNetIP(ctx, "ip", cpHostname) if err != nil { return fmt.Errorf("error resolving %q: %w", cpHostname, err) } addrs = xslices.Map(addrs, netip.Addr.Unmap) if len(addrs) != 1 || addrs[0].String() != cpHostname { hosts = []string{cpHostname} } if err = safe.WriterModify(ctx, r, k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, k8s.ControlPlaneKubernetesEndpointsID), func(endpoint *k8s.Endpoint) error { endpoint.TypedSpec().Addresses = addrs endpoint.TypedSpec().Hosts = hosts return nil }); err != nil { return fmt.Errorf("error modifying endpoint: %w", err) } } if err = safe.CleanupOutputs[*k8s.Endpoint](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/netip" "net/url" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type StaticEndpointControllerSuite struct { ctest.DefaultSuite } func (suite *StaticEndpointControllerSuite) TestReconcile() { u, err := url.Parse("https://[2001:db8::1]:6443/") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControlPlaneKubernetesEndpointsID}, func(endpoint *k8s.Endpoint, assert *assert.Assertions) { assert.Equal([]netip.Addr{netip.MustParseAddr("2001:db8::1")}, endpoint.TypedSpec().Addresses) assert.Empty(endpoint.TypedSpec().Hosts) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), cfg.Metadata())) rtestutils.AssertNoResource[*k8s.Endpoint](suite.Ctx(), suite.T(), suite.State(), k8s.ControlPlaneKubernetesEndpointsID) } func (suite *StaticEndpointControllerSuite) TestReconcileHostname() { u, err := url.Parse("https://localhost:6443/") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{k8s.ControlPlaneKubernetesEndpointsID}, func(endpoint *k8s.Endpoint, assert *assert.Assertions) { // localhost might resolve to ::1 as well, check only for 127.0.0.1 assert.Contains(endpoint.TypedSpec().Addresses, netip.MustParseAddr("127.0.0.1")) assert.Equal([]string{"localhost"}, endpoint.TypedSpec().Hosts) }) } func TestStaticEndpointControllerSuite(t *testing.T) { t.Parallel() suite.Run(t, &StaticEndpointControllerSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&k8sctrl.StaticEndpointController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_pod_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // StaticPodConfigController manages k8s.StaticPod based on machine configuration. type StaticPodConfigController struct{} // Name implements controller.Controller interface. func (ctrl *StaticPodConfigController) Name() string { return "k8s.StaticPodConfigController" } // Inputs implements controller.Controller interface. func (ctrl *StaticPodConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *StaticPodConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.StaticPodType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *StaticPodConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } r.StartTrackingOutputs() if cfg != nil && cfg.Config().Machine() != nil { cfgProvider := cfg.Config() for _, pod := range cfgProvider.Machine().Pods() { var ( name, namespace string ok bool ) name, ok, err = unstructured.NestedString(pod, "metadata", "name") if err != nil { return fmt.Errorf("error getting name from static pod: %w", err) } if !ok { return errors.New("name is missing in static pod metadata") } namespace, ok, err = unstructured.NestedString(pod, "metadata", "namespace") if err != nil { return fmt.Errorf("error getting namespace from static pod: %w", err) } if !ok { namespace = corev1.NamespaceDefault } id := fmt.Sprintf("%s-%s", namespace, name) if err = safe.WriterModify(ctx, r, k8s.NewStaticPod(k8s.NamespaceName, id), func(r *k8s.StaticPod) error { r.TypedSpec().Pod = pod return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } } } // clean up static pods which haven't been touched if err = safe.CleanupOutputs[*k8s.StaticPod](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_pod_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "context" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type StaticPodConfigSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *StaticPodConfigSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(&k8sctrl.StaticPodConfigController{})) suite.startRuntime() } func (suite *StaticPodConfigSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *StaticPodConfigSuite) assertResource( md resource.Metadata, check func(res resource.Resource) error, ) func() error { return func() error { r, err := suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } return check(r) } } func (suite *StaticPodConfigSuite) assertNoResource(md resource.Metadata) func() error { return func() error { _, err := suite.state.Get(suite.ctx, md) if err == nil { return retry.ExpectedErrorf("resource %s still exists", md) } if state.IsNotFoundError(err) { return nil } return err } } func (suite *StaticPodConfigSuite) TestReconcile() { cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachinePods: []v1alpha1.Unstructured{ { Object: map[string]any{ "apiVersion": "v1", "kind": "pod", "metadata": map[string]any{ "name": "nginx", }, "spec": map[string]any{ "containers": []any{ map[string]any{ "name": "nginx", "image": "nginx", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, )) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( *k8s.NewStaticPod(k8s.NamespaceName, "default-nginx").Metadata(), func(res resource.Resource) error { v, ok, err := unstructured.NestedString(res.(*k8s.StaticPod).TypedSpec().Pod, "kind") suite.Require().NoError(err) suite.Assert().True(ok) suite.Assert().Equal("pod", v) return nil }, ), ), ) // update the pod changing the namespace cfg.Container().RawV1Alpha1().MachineConfig.MachinePods[0].Object["metadata"].(map[string]any)["namespace"] = "custom" suite.Require().NoError(suite.state.Update(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertNoResource( *k8s.NewStaticPod(k8s.NamespaceName, "default-nginx").Metadata(), ), ), ) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( *k8s.NewStaticPod(k8s.NamespaceName, "custom-nginx").Metadata(), func(res resource.Resource) error { v, ok, err := unstructured.NestedString( res.(*k8s.StaticPod).TypedSpec().Pod, "metadata", "namespace", ) suite.Require().NoError(err) suite.Assert().True(ok) suite.Assert().Equal("custom", v) return nil }, ), ), ) // remove all pods cfg.Container().RawV1Alpha1().MachineConfig.MachinePods = nil suite.Require().NoError(suite.state.Update(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertNoResource( *k8s.NewStaticPod(k8s.NamespaceName, "custom-nginx").Metadata(), ), ), ) } func (suite *StaticPodConfigSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestStaticPodConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, new(StaticPodConfigSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_pod_server.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "context" "fmt" "net" "net/http" "sync" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // StaticPodServerController renders all static pod definitions as a PodList and serves it as YAML via HTTP. type StaticPodServerController struct { podList []byte podListMu sync.Mutex staticPodVersions map[string]string } // Name implements controller.Controller interface. func (ctrl *StaticPodServerController) Name() string { return "k8s.StaticPodServerController" } // Inputs implements controller.Controller interface. func (ctrl *StaticPodServerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: k8s.NamespaceName, Type: k8s.StaticPodType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *StaticPodServerController) Outputs() []controller.Output { return []controller.Output{ { Type: k8s.StaticPodServerStatusType, Kind: controller.OutputExclusive, }, } } type pod map[string]any type podList struct { Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"` Items []pod `json:"items" protobuf:"bytes,2,rep,name=items"` APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,3,opt,name=apiVersion"` } // Run implements controller.Controller interface. func (ctrl *StaticPodServerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { ctrl.staticPodVersions = map[string]string{} shutdownServer, serverError, err := ctrl.createServer(ctx, r, logger) if err != nil { return fmt.Errorf("failed to start http server to serve static pod list: %w", err) } defer shutdownServer() for { select { case <-ctx.Done(): return nil case err := <-serverError: return fmt.Errorf("http server closed unexpectedly: %w", err) case <-r.EventCh(): staticPodList, err := ctrl.buildPodList(ctx, r, logger) if err != nil { logger.Error("error building static pod list", zap.Error(err)) } ctrl.podListMu.Lock() ctrl.podList = staticPodList ctrl.podListMu.Unlock() } r.ResetRestartBackoff() } } func (ctrl *StaticPodServerController) buildPodList(ctx context.Context, r controller.Runtime, logger *zap.Logger) ([]byte, error) { staticPods, err := safe.ReaderListAll[*k8s.StaticPod](ctx, r) if err != nil { return nil, fmt.Errorf("error listing static pods: %w", err) } pl := podList{ Kind: "PodList", APIVersion: "v1", } touchedPodIDs := map[string]struct{}{} for staticPod := range staticPods.All() { id := staticPod.Metadata().ID() version := staticPod.Metadata().Version().String() if oldVersion, exists := ctrl.staticPodVersions[id]; !exists || oldVersion != version { ctrl.staticPodVersions[id] = version if !exists { logger.Info("rendered new static pod", zap.String("id", id)) } else { logger.Info("rendered updated static pod", zap.String("id", id), zap.String("old_version", oldVersion), zap.String("new_version", version)) } } staticPodSpec := staticPod.TypedSpec() pl.Items = append(pl.Items, staticPodSpec.Pod) touchedPodIDs[id] = struct{}{} } for id := range ctrl.staticPodVersions { if _, exists := touchedPodIDs[id]; exists { continue } logger.Info("removed static pod", zap.String("id", id)) delete(ctrl.staticPodVersions, id) } manifestContent, err := yaml.Marshal(pl) if err != nil { return nil, fmt.Errorf("error rendering list of static pods as yaml: %w", err) } return manifestContent, nil } func (ctrl *StaticPodServerController) createServer(ctx context.Context, r controller.Runtime, logger *zap.Logger) (func(), <-chan error, error) { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { ctrl.podListMu.Lock() staticPodList := ctrl.podList ctrl.podListMu.Unlock() logger.Debug("serving static pod manifests", zap.Int("size", len(staticPodList))) if staticPodList == nil { http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } _, err := w.Write(staticPodList) if err != nil { logger.Error("failed to serve static pod manifests", zap.Error(err)) } }) listener, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:0") if err != nil { return nil, nil, fmt.Errorf("failed to create listener for serving static pod manifests: %w", err) } httpServer := &http.Server{ Handler: mux, } shutdownServer := func() { if err := httpServer.Shutdown(ctx); err != nil { logger.Error("failed to shut down HTTP server, serving static pod manifests", zap.Error(err)) } } go func() { <-ctx.Done() shutdownServer() }() if err := safe.WriterModify(ctx, r, k8s.NewStaticPodServerStatus(k8s.NamespaceName, k8s.StaticPodServerStatusResourceID), func(r *k8s.StaticPodServerStatus) error { url := fmt.Sprintf("http://%s", listener.Addr().String()) r.TypedSpec().URL = url return nil }); err != nil { return nil, nil, fmt.Errorf("error modifying StaticPodListURL resource: %w", err) } serverError := make(chan error, 1) go func() { serverError <- httpServer.Serve(listener) }() return shutdownServer, serverError, nil } ================================================ FILE: internal/app/machined/pkg/controllers/k8s/static_pod_server_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package k8s_test import ( "context" "io" "net/http" "strings" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" k8sctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type StaticPodListSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *StaticPodListSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(&k8sctrl.StaticPodServerController{})) suite.startRuntime() } func (suite *StaticPodListSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *StaticPodListSuite) assertResource( md resource.Metadata, check func(res resource.Resource) error, ) func() error { return func() error { r, err := suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } return check(r) } } func (suite *StaticPodListSuite) getResource( md resource.Metadata, ) resource.Resource { var ret resource.Resource suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { r, err := suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } ret = r return nil })) return ret } func newTestPod(name string) *k8s.StaticPod { testPod := k8s.NewStaticPod(k8s.NamespaceName, name) testPod.TypedSpec().Pod = map[string]any{ "metadata": name, "spec": "testSpec", } return testPod } func (suite *StaticPodListSuite) TestCreatesStaticPodServerStatus() { // given testPod := newTestPod("testPod") // when suite.Require().NoError(suite.state.Create(suite.ctx, testPod)) // then expectedPodListURL := k8s.NewStaticPodServerStatus(k8s.NamespaceName, k8s.StaticPodServerStatusResourceID) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource(*expectedPodListURL.Metadata(), func(res resource.Resource) error { suite.Require().True(strings.HasPrefix( res.(*k8s.StaticPodServerStatus).TypedSpec().URL, "http://127.0.0.1:", ), ) return nil }, ), ), ) } func (suite *StaticPodListSuite) TestServesStaticPodList() { // given testPod1 := newTestPod("testPod1") testPod2 := newTestPod("testPod2") // when suite.Require().NoError(suite.state.Create(suite.ctx, testPod1)) suite.Require().NoError(suite.state.Create(suite.ctx, testPod2)) // then expectedPodListURL := k8s.NewStaticPodServerStatus(k8s.NamespaceName, k8s.StaticPodServerStatusResourceID) podListURL := suite.getResource(*expectedPodListURL.Metadata()) suite.Require().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { resp, err := http.Get(podListURL.(*k8s.StaticPodServerStatus).TypedSpec().URL) //nolint:noctx if err != nil { return retry.ExpectedError(err) } defer resp.Body.Close() //nolint:errcheck content, err := io.ReadAll(resp.Body) suite.Assert().NoError(err) suite.Require().Equal("kind: PodList\nitems:\n - metadata: testPod1\n spec: testSpec\n - metadata: testPod2\n spec: testSpec\napiversion: v1\n", string(content)) return nil }), ) } func (suite *StaticPodListSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestStaticPodListSuite(t *testing.T) { t.Parallel() suite.Run(t, new(StaticPodListSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" ) // ConfigController watches v1alpha1.Config, updates Talos API access config. type ConfigController = transform.Controller[*config.MachineConfig, *kubeaccess.Config] // NewConfigController instanciates the config controller. func NewConfigController() *ConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *kubeaccess.Config]{ Name: "kubeaccess.ConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*kubeaccess.Config] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*kubeaccess.Config]() } if cfg.Config().Machine() == nil { return optional.None[*kubeaccess.Config]() } if !cfg.Config().Machine().Type().IsControlPlane() { return optional.None[*kubeaccess.Config]() } return optional.Some(kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *kubeaccess.Config) error { spec := res.TypedSpec() *spec = kubeaccess.ConfigSpec{} if cfg != nil && cfg.Config().Machine() != nil { c := cfg.Config() spec.Enabled = c.Machine().Features().KubernetesTalosAPIAccess().Enabled() spec.AllowedAPIRoles = c.Machine().Features().KubernetesTalosAPIAccess().AllowedRoles() spec.AllowedKubernetesNamespaces = c.Machine().Features().KubernetesTalosAPIAccess().AllowedKubernetesNamespaces() } return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubeaccessctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubeaccess" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" ) type ConfigSuite struct { ctest.DefaultSuite } func (suite *ConfigSuite) TestReconcileConfig() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineFeatures: &v1alpha1.FeaturesConfig{ KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{ AccessEnabled: new(true), AccessAllowedRoles: []string{"os:admin"}, AccessAllowedKubernetesNamespaces: []string{"kube-system"}, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{kubeaccess.ConfigID}, func(r *kubeaccess.Config, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.True(spec.Enabled) asrt.Equal([]string{"os:admin"}, spec.AllowedAPIRoles) asrt.Equal([]string{"kube-system"}, spec.AllowedKubernetesNamespaces) }) } func (suite *ConfigSuite) TestReconcileDisabled() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "init", }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{kubeaccess.ConfigID}, func(r *kubeaccess.Config, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.False(spec.Enabled) asrt.Empty(spec.AllowedAPIRoles) asrt.Empty(spec.AllowedKubernetesNamespaces) }) } func (suite *ConfigSuite) TestReconcileWorker() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineFeatures: &v1alpha1.FeaturesConfig{ KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{ AccessEnabled: new(true), AccessAllowedRoles: []string{"os:admin"}, AccessAllowedKubernetesNamespaces: []string{"kube-system"}, }, }, }, })) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) // worker should have feature disabled even if it is enabled in the config rtestutils.AssertNoResource[*kubeaccess.Config](suite.Ctx(), suite.T(), suite.State(), kubeaccess.ConfigID) } func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(kubeaccessctrl.NewConfigController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess import ( "context" "fmt" "reflect" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // EndpointController manages Kubernetes endpoints resource for Talos API endpoints. type EndpointController struct{} // Name implements controller.Controller interface. func (ctrl *EndpointController) Name() string { return "kubeaccess.EndpointController" } // Inputs implements controller.Controller interface. func (ctrl *EndpointController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubeaccess.ConfigType, ID: optional.Some(kubeaccess.ConfigID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesType, ID: optional.Some(secrets.KubernetesID), Kind: controller.InputWeak, }, { Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.EndpointType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EndpointController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } kubeaccessConfig, err := safe.ReaderGet[*kubeaccess.Config](ctx, r, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID).Metadata()) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error fetching kubeaccess config: %w", err) } } if kubeaccessConfig == nil || !kubeaccessConfig.TypedSpec().Enabled { // disabled, do not do anything continue } // use only api-server endpoints to leave only kubelet node IPs endpointResource, err := safe.ReaderGet[*k8s.Endpoint](ctx, r, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.EndpointType, k8s.ControlPlaneAPIServerEndpointsID, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting endpoints resources: %w", err) } } var endpointAddrs k8s.EndpointList if endpointResource != nil { endpointAddrs = endpointAddrs.Merge(endpointResource) } if endpointAddrs.IsEmpty() { continue } secretsResources, err := safe.ReaderGet[*secrets.Kubernetes](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesType, secrets.KubernetesID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } secrets := secretsResources.TypedSpec() kubeconfig, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load([]byte(secrets.LocalhostAdminKubeconfig)) }) if err != nil { return fmt.Errorf("error loading kubeconfig: %w", err) } if err = ctrl.manageEndpoints(ctx, logger, kubeconfig, endpointAddrs); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *EndpointController) manageEndpoints(ctx context.Context, logger *zap.Logger, kubeconfig *rest.Config, endpointAddrs k8s.EndpointList) error { client, err := kubernetes.NewForConfig(kubeconfig) if err != nil { return fmt.Errorf("error building Kubernetes client: %w", err) } defer client.Close() //nolint:errcheck // create the Service before creating the Endpoints, as Kubernetes EndpointController will clean up orphaned Endpoints if err = ctrl.ensureTalosService(ctx, client); err != nil { return fmt.Errorf("error ensuring Talos API service: %w", err) } // now create or update the EndpointSlices resource if err = ctrl.ensureTalosEndpointSlices(ctx, logger, client, endpointAddrs); err != nil { return fmt.Errorf("error ensuring Talos API endpoint slices: %w", err) } // clean-up deprecated endpoints if err = ctrl.cleanupTalosEndpoints(ctx, logger, client); err != nil { return fmt.Errorf("error cleaning up dangling Talos API endpoints: %w", err) } return nil } func (ctrl *EndpointController) ensureTalosService(ctx context.Context, client *kubernetes.Client) error { _, err := client.CoreV1().Services(constants.KubernetesTalosAPIServiceNamespace).Get(ctx, constants.KubernetesTalosAPIServiceName, metav1.GetOptions{}) if err == nil { // service already exists, nothing to do return nil } if !apierrors.IsNotFound(err) { return fmt.Errorf("error getting Talos API service: %w", err) } // create the service if it does not exist newService := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: constants.KubernetesTalosAPIServiceName, Namespace: constants.KubernetesTalosAPIServiceNamespace, Labels: map[string]string{ "provider": constants.KubernetesTalosProvider, "component": "apid", }, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { Name: "apid", Port: constants.ApidPort, Protocol: corev1.ProtocolTCP, TargetPort: intstr.FromInt(constants.ApidPort), }, }, }, } _, err = client.CoreV1().Services(constants.KubernetesTalosAPIServiceNamespace).Create(ctx, newService, metav1.CreateOptions{}) if err != nil && !apierrors.IsAlreadyExists(err) { return fmt.Errorf("error creating Talos API service: %w", err) } return nil } //nolint:gocyclo func (ctrl *EndpointController) ensureTalosEndpointSlices(ctx context.Context, logger *zap.Logger, client *kubernetes.Client, endpointAddrs k8s.EndpointList) error { var ( addrsIPv4 k8s.EndpointList addrsIPv6 k8s.EndpointList ) for _, addr := range endpointAddrs.Addresses { switch { case addr.Is4(): addrsIPv4.Addresses = append(addrsIPv4.Addresses, addr) case addr.Is6(): addrsIPv6.Addresses = append(addrsIPv6.Addresses, addr) default: // ignore other address types } } if len(addrsIPv4.Addresses) == 0 { if err := ctrl.deleteTalosEndpointSlicesTyped(ctx, logger, client, discoveryv1.AddressTypeIPv4); err != nil { return fmt.Errorf("error deleting Talos API endpoint slices for IPv4: %w", err) } } else { if err := ctrl.ensureTalosEndpointSlicesTyped(ctx, logger, client, addrsIPv4, discoveryv1.AddressTypeIPv4); err != nil { return fmt.Errorf("error ensuring Talos API endpoint slices for IPv4: %w", err) } } if len(addrsIPv6.Addresses) == 0 { if err := ctrl.deleteTalosEndpointSlicesTyped(ctx, logger, client, discoveryv1.AddressTypeIPv6); err != nil { return fmt.Errorf("error deleting Talos API endpoint slices for IPv6: %w", err) } } else { if err := ctrl.ensureTalosEndpointSlicesTyped(ctx, logger, client, addrsIPv6, discoveryv1.AddressTypeIPv6); err != nil { return fmt.Errorf("error ensuring Talos API endpoint slices for IPv6: %w", err) } } return nil } func (ctrl *EndpointController) deleteTalosEndpointSlicesTyped(ctx context.Context, logger *zap.Logger, client *kubernetes.Client, addressType discoveryv1.AddressType) error { endpointSliceName := constants.KubernetesTalosAPIServiceName + "-" + strings.ToLower(string(addressType)) err := client.DiscoveryV1().EndpointSlices(constants.KubernetesTalosAPIServiceNamespace).Delete(ctx, endpointSliceName, metav1.DeleteOptions{}) if err != nil && !apierrors.IsNotFound(err) { return fmt.Errorf("error deleting Talos API endpoint slices: %w", err) } logger.Info("deleted Talos API endpoint slices in Kubernetes", zap.String("addressType", string(addressType))) return nil } //nolint:gocyclo func (ctrl *EndpointController) ensureTalosEndpointSlicesTyped( ctx context.Context, logger *zap.Logger, client *kubernetes.Client, endpointAddrs k8s.EndpointList, addressType discoveryv1.AddressType, ) error { for { esc := client.DiscoveryV1().EndpointSlices(constants.KubernetesTalosAPIServiceNamespace) name := constants.KubernetesTalosAPIServiceName + "-" + strings.ToLower(string(addressType)) oldEndpointSlice, err := esc.Get(ctx, name, metav1.GetOptions{}) if err != nil && !apierrors.IsNotFound(err) { return fmt.Errorf("error getting endpoints: %w", err) } var newEndpointSlice *discoveryv1.EndpointSlice if apierrors.IsNotFound(err) { newEndpointSlice = &discoveryv1.EndpointSlice{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: constants.KubernetesTalosAPIServiceNamespace, Labels: map[string]string{ "kubernetes.io/service-name": constants.KubernetesTalosAPIServiceName, "provider": constants.KubernetesTalosProvider, "component": "apid", }, }, AddressType: addressType, } oldEndpointSlice = nil } else { newEndpointSlice = oldEndpointSlice.DeepCopy() } PopulateEndpointSlice(newEndpointSlice, endpointAddrs) if oldEndpointSlice != nil && (reflect.DeepEqual(oldEndpointSlice.Endpoints, newEndpointSlice.Endpoints) && reflect.DeepEqual(oldEndpointSlice.Ports, newEndpointSlice.Ports)) { // no change, bail out return nil } if oldEndpointSlice == nil { _, err = client.DiscoveryV1().EndpointSlices(constants.KubernetesTalosAPIServiceNamespace).Create(ctx, newEndpointSlice, metav1.CreateOptions{}) } else { _, err = client.DiscoveryV1().EndpointSlices(constants.KubernetesTalosAPIServiceNamespace).Update(ctx, newEndpointSlice, metav1.UpdateOptions{}) } switch { case err == nil: logger.Info("updated Talos API endpoint slices in Kubernetes", zap.Strings("endpoints", endpointAddrs.Strings())) return nil case apierrors.IsConflict(err) || apierrors.IsAlreadyExists(err): // retry default: return fmt.Errorf("error updating Kubernetes Talos API endpoint slices: %w", err) } } } // PopulateEndpointSlice populates the given EndpointSlice with ports and endpoints from the given endpoint addresses. // // The EndpointSlice's existing Endpoints and Ports fields are overwritten. func PopulateEndpointSlice(endpointSlice *discoveryv1.EndpointSlice, endpointAddrs k8s.EndpointList) { endpointSlice.Ports = []discoveryv1.EndpointPort{ { Name: new("apid"), Port: new(int32(constants.ApidPort)), Protocol: new(corev1.ProtocolTCP), }, } endpointSlice.Endpoints = nil for _, addr := range endpointAddrs.Addresses { endpointSlice.Endpoints = append( endpointSlice.Endpoints, discoveryv1.Endpoint{ Addresses: []string{addr.String()}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, ) } endpointSlice.Endpoints = xslices.Deduplicate(endpointSlice.Endpoints, func(e discoveryv1.Endpoint) string { return e.Addresses[0] }) } //nolint:gocyclo func (ctrl *EndpointController) cleanupTalosEndpoints(ctx context.Context, logger *zap.Logger, client *kubernetes.Client) error { for { err := client.CoreV1().Endpoints(constants.KubernetesTalosAPIServiceNamespace).Delete(ctx, constants.KubernetesTalosAPIServiceName, metav1.DeleteOptions{}) if err != nil && !apierrors.IsNotFound(err) { return fmt.Errorf("error getting endpoints: %w", err) } switch { case err == nil: logger.Info("deleted dangling Talos API endpoints in Kubernetes") return nil case apierrors.IsNotFound(err): logger.Info("no dangling Talos API endpoints in Kubernetes") return nil default: return fmt.Errorf("error deleting dangling Kubernetes Talos API endpoints: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" kubeaccessctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubeaccess" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) func TestPopulateEndpointSlice(t *testing.T) { t.Parallel() //nolint:dupl for _, tt := range []struct { name string existingSlice *discoveryv1.EndpointSlice endpointAddrs k8s.EndpointList expectedEndpoint []discoveryv1.Endpoint }{ { name: "empty endpoint slice, single address", existingSlice: &discoveryv1.EndpointSlice{}, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"192.168.1.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, { name: "empty endpoint slice, multiple addresses", existingSlice: &discoveryv1.EndpointSlice{}, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.2"), netip.MustParseAddr("192.168.1.3"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"192.168.1.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"192.168.1.2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"192.168.1.3"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, { name: "stale endpoints are removed", existingSlice: &discoveryv1.EndpointSlice{ Endpoints: []discoveryv1.Endpoint{ { Addresses: []string{"10.0.0.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"10.0.0.2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"10.0.0.3"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("10.0.0.1"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"10.0.0.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, { name: "all stale endpoints replaced with new ones", existingSlice: &discoveryv1.EndpointSlice{ Endpoints: []discoveryv1.Endpoint{ { Addresses: []string{"10.0.0.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"10.0.0.2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.2"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"192.168.1.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"192.168.1.2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, { name: "duplicate addresses are deduplicated", existingSlice: &discoveryv1.EndpointSlice{ Endpoints: []discoveryv1.Endpoint{ { Addresses: []string{"10.0.0.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.1"), netip.MustParseAddr("192.168.1.2"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"192.168.1.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"192.168.1.2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, { name: "empty address list clears endpoints", existingSlice: &discoveryv1.EndpointSlice{ Endpoints: []discoveryv1.Endpoint{ { Addresses: []string{"10.0.0.1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, endpointAddrs: k8s.EndpointList{}, expectedEndpoint: nil, }, { name: "IPv6 addresses", existingSlice: &discoveryv1.EndpointSlice{}, endpointAddrs: k8s.EndpointList{ Addresses: []netip.Addr{ netip.MustParseAddr("fd00::1"), netip.MustParseAddr("fd00::2"), }, }, expectedEndpoint: []discoveryv1.Endpoint{ { Addresses: []string{"fd00::1"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, { Addresses: []string{"fd00::2"}, Conditions: discoveryv1.EndpointConditions{ Ready: new(true), Serving: new(true), Terminating: new(false), }, }, }, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() kubeaccessctrl.PopulateEndpointSlice(tt.existingSlice, tt.endpointAddrs) assert.Equal(t, tt.expectedEndpoint, tt.existingSlice.Endpoints) // verify ports are always set correctly require.Len(t, tt.existingSlice.Ports, 1) assert.Equal(t, "apid", *tt.existingSlice.Ports[0].Name) assert.Equal(t, int32(constants.ApidPort), *tt.existingSlice.Ports[0].Port) assert.Equal(t, corev1.ProtocolTCP, *tt.existingSlice.Ports[0].Protocol) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/kubeaccess.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubeaccess provides controllers which manage Talos API access from Kubernetes workloads. package kubeaccess ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/serviceaccount/crd_controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package serviceaccount import ( "bytes" "context" stdlibx509 "crypto/x509" "encoding/base64" "encoding/pem" "errors" "fmt" "net/http" "slices" "sync" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" kubeerrors "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" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/dynamic/dynamiclister" kubeinformers "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes/scheme" typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" corelisters "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/rest" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" "k8s.io/client-go/util/workqueue" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) const ( certTTL = time.Hour * 6 certRenewThreshold = time.Hour * 1 successResourceSynced = "Synced" messageResourceSynced = "Synced successfully" errResourceExists = "ErrResourceExists" messageResourceExists = "%s already exists and is not managed by controller: %s" errRolesNotFound = "ErrRolesNotFound" messageRolesNotFound = "Roles not found" errNamespaceNotAllowed = "ErrNamespaceNotAllowed" messageNamespaceNotAllowed = "Namespace is not allowed: %s" errRolesNotAllowed = "ErrRolesNotAllowed" messageRolesNotAllowed = "Roles not allowed: %v" controllerAgentName = "talos-sa-controller" informerResyncPeriod = time.Minute * 1 talosconfigContextName = "default" endpoint = constants.KubernetesTalosAPIServiceName + "." + constants.KubernetesTalosAPIServiceNamespace kindSecret = "Secret" ) var ( talosSAGV = schema.GroupVersion{ Group: constants.ServiceAccountResourceGroup, Version: constants.ServiceAccountResourceVersion, } talosSAGVR = talosSAGV.WithResource(constants.ServiceAccountResourcePlural) talosSAGVK = talosSAGV.WithKind(constants.ServiceAccountResourceKind) ) // CRDController is the controller implementation for TalosServiceAccount resources. type CRDController struct { talosCA *x509.PEMEncodedCertificateAndKey allowedNamespaces []string allowedRoles map[string]struct{} queue workqueue.TypedRateLimitingInterface[string] kubeInformerFactory kubeinformers.SharedInformerFactory dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory kubeClient kubernetes.Interface dynamicClient dynamic.Interface httpClient *http.Client secretsSynced cache.InformerSynced talosSAsSynced cache.InformerSynced secretsLister corelisters.SecretLister dynamicLister dynamiclister.Lister eventRecorder record.EventRecorder logger *zap.Logger } // NewCRDController creates a new CRD controller. func NewCRDController( talosCA *x509.PEMEncodedCertificateAndKey, kubeconfig *rest.Config, allowedNamespaces []string, allowedRoles []string, logger *zap.Logger, ) (*CRDController, error) { httpClient, err := rest.HTTPClientFor(kubeconfig) if err != nil { return nil, err } kubeCli, err := kubernetes.NewForConfigAndClient(kubeconfig, httpClient) if err != nil { return nil, err } dynCli, err := dynamic.NewForConfigAndClient(kubeconfig, httpClient) if err != nil { return nil, err } dynamicInformerFactory := dynamicinformer.NewDynamicSharedInformerFactory(dynCli, informerResyncPeriod) resourceInformer := dynamicInformerFactory.ForResource(talosSAGVR) informer := resourceInformer.Informer() indexer := informer.GetIndexer() lister := dynamiclister.New(indexer, talosSAGVR) kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeCli, informerResyncPeriod) secrets := kubeInformerFactory.Core().V1().Secrets() logger.Debug("creating event broadcaster") eventBroadcaster := record.NewBroadcaster() eventBroadcaster.StartStructuredLogging(0) eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeCli.CoreV1().Events("")}) recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) controller := CRDController{ talosCA: talosCA, allowedNamespaces: allowedNamespaces, allowedRoles: xslices.ToSet(allowedRoles), dynamicInformerFactory: dynamicInformerFactory, kubeInformerFactory: kubeInformerFactory, kubeClient: kubeCli, dynamicClient: dynCli, httpClient: httpClient, dynamicLister: lister, queue: workqueue.NewTypedRateLimitingQueue( workqueue.DefaultTypedControllerRateLimiter[string](), ), logger: logger, secretsSynced: secrets.Informer().HasSynced, talosSAsSynced: informer.HasSynced, eventRecorder: recorder, secretsLister: secrets.Lister(), } if _, err = informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.enqueueTalosSA, UpdateFunc: func(oldTalosSA, newTalosSA any) { controller.enqueueTalosSA(newTalosSA) }, }); err != nil { return nil, err } if _, err = secrets.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.handleSecret, UpdateFunc: func(oldSec, newSec any) { newSecret := newSec.(*corev1.Secret) oldSecret := oldSec.(*corev1.Secret) if newSecret.ResourceVersion == oldSecret.ResourceVersion { return } controller.handleSecret(newSec) }, DeleteFunc: controller.handleSecret, }); err != nil { return nil, err } return &controller, nil } // Run starts the CRD controller. func (t *CRDController) Run(ctx context.Context, workers int) error { var wg sync.WaitGroup defer func() { t.queue.ShutDown() t.httpClient.CloseIdleConnections() wg.Wait() t.logger.Debug("all workers have shut down") }() t.kubeInformerFactory.Start(ctx.Done()) t.dynamicInformerFactory.Start(ctx.Done()) t.logger.Sugar().Debugf("starting %s controller", constants.ServiceAccountResourceKind) t.logger.Debug("waiting for informer caches to sync") if ok := cache.WaitForCacheSync(ctx.Done(), t.secretsSynced, t.talosSAsSynced); !ok { return errors.New("failed to wait for caches to sync") } t.logger.Debug("starting workers") wg.Add(workers) for range workers { go func() { wait.Until(func() { t.runWorker(ctx) }, time.Second, ctx.Done()) wg.Done() }() } t.logger.Debug("started workers") <-ctx.Done() t.logger.Debug("shutting down workers") t.kubeInformerFactory.Shutdown() return nil } func (t *CRDController) runWorker(ctx context.Context) { for t.processNextWorkItem(ctx) { } } func (t *CRDController) processNextWorkItem(ctx context.Context) bool { obj, shutdown := t.queue.Get() if shutdown { return false } err := func(obj string) error { defer t.queue.Done(obj) if err := t.syncHandler(ctx, obj); err != nil { t.queue.AddRateLimited(obj) return fmt.Errorf("error syncing '%s': %s, requeuing", obj, err.Error()) } t.queue.Forget(obj) t.logger.Sugar().Debugf("successfully synced '%s'", obj) return nil }(obj) if err != nil { utilruntime.HandleError(err) return true } return true } //nolint:gocyclo,cyclop func (t *CRDController) syncHandler(ctx context.Context, key string) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { utilruntime.HandleError(fmt.Errorf("invalid resource key: %s", key)) return nil //nolint:nilerr } talosSA, err := t.dynamicLister.Namespace(namespace).Get(name) if err != nil { if kubeerrors.IsNotFound(err) { utilruntime.HandleError(fmt.Errorf("talosSA '%s' in work queue no longer exists", key)) return nil } return err } secret, err := t.secretsLister.Secrets(namespace).Get(name) secretNotFound := kubeerrors.IsNotFound(err) if err != nil && !secretNotFound { return err } if !secretNotFound && !metav1.IsControlledBy(secret, talosSA) { msg := fmt.Sprintf(messageResourceExists, kindSecret, key) err = t.updateTalosSAStatus(ctx, talosSA, msg) if err != nil { return err } t.eventRecorder.Event(talosSA, corev1.EventTypeWarning, errResourceExists, msg) return errors.New(msg) } desiredRoles, found, err := unstructured.NestedStringSlice(talosSA.UnstructuredContent(), "spec", "roles") if err != nil || !found { msg := messageRolesNotFound updateErr := t.updateTalosSAStatus(ctx, talosSA, msg) if updateErr != nil { return updateErr } t.eventRecorder.Event(talosSA, corev1.EventTypeWarning, errRolesNotFound, messageRolesNotFound) if err != nil { return fmt.Errorf("%s: %w", msg, err) } return errors.New(msg) } desiredRoleSet, _ := role.Parse(desiredRoles) if !slices.ContainsFunc(t.allowedNamespaces, func(allowedNS string) bool { return allowedNS == namespace }) { msg := fmt.Sprintf(messageNamespaceNotAllowed, namespace) err = t.updateTalosSAStatus(ctx, talosSA, msg) if err != nil { return err } t.eventRecorder.Event(talosSA, corev1.EventTypeWarning, errNamespaceNotAllowed, msg) return nil } var unallowedRoles []string for _, desiredRole := range desiredRoles { _, allowed := t.allowedRoles[desiredRole] if !allowed { unallowedRoles = append(unallowedRoles, desiredRole) } } if len(unallowedRoles) > 0 { msg := fmt.Sprintf(messageRolesNotAllowed, unallowedRoles) err = t.updateTalosSAStatus(ctx, talosSA, msg) if err != nil { return err } t.eventRecorder.Event(talosSA, corev1.EventTypeWarning, errRolesNotAllowed, msg) return nil } if secretNotFound { var newSecret *corev1.Secret newSecret, err = t.newSecret(talosSA, desiredRoleSet) if err != nil { return err } _, err = t.kubeClient.CoreV1().Secrets(namespace).Create(ctx, newSecret, metav1.CreateOptions{}) if err != nil { return err } } else if t.needsUpdate(secret, desiredRoleSet.Strings()) { var newTalosconfigBytes []byte newTalosconfigBytes, err = t.generateTalosconfig(desiredRoleSet) if err != nil { return err } secret.Data[constants.TalosconfigFilename] = newTalosconfigBytes _, err = t.kubeClient.CoreV1().Secrets(namespace).Update(ctx, secret, metav1.UpdateOptions{}) if err != nil { return err } } err = t.updateTalosSAStatus(ctx, talosSA, "") if err != nil { return err } t.eventRecorder.Event(talosSA, corev1.EventTypeNormal, successResourceSynced, messageResourceSynced) return nil } func (t *CRDController) enqueueTalosSA(obj any) { key, err := cache.MetaNamespaceKeyFunc(obj) if err != nil { utilruntime.HandleError(err) return } t.queue.Add(key) } func (t *CRDController) handleSecret(obj any) { var object metav1.Object var ok bool if object, ok = obj.(metav1.Object); !ok { tombstone, tombstoneOK := obj.(cache.DeletedFinalStateUnknown) if !tombstoneOK { utilruntime.HandleError(errors.New("error decoding object, invalid type")) return } object, tombstoneOK = tombstone.Obj.(metav1.Object) if !tombstoneOK { utilruntime.HandleError(errors.New("error decoding object tombstone, invalid type")) return } t.logger.Sugar().Debugf("recovered deleted object '%s' from tombstone", object.GetName()) } t.logger.Sugar().Debugf("processing object: %s", object.GetName()) if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { if ownerRef.Kind != constants.ServiceAccountResourceKind { return } talosSA, err := t.dynamicLister.Namespace(object.GetNamespace()).Get(ownerRef.Name) if err != nil { t.logger.Sugar().Debugf("ignoring orphaned object '%s/%s' of %s '%s'", object.GetNamespace(), object.GetName(), constants.ServiceAccountResourceKind, ownerRef.Name) return } t.enqueueTalosSA(talosSA) return } } func (t *CRDController) updateTalosSAStatus( ctx context.Context, talosSA *unstructured.Unstructured, failureReason string, ) error { var err error talosSACopy := talosSA.DeepCopy() if failureReason == "" { unstructured.RemoveNestedField(talosSACopy.UnstructuredContent(), "status", "failureReason") } else { err = unstructured.SetNestedField(talosSACopy.UnstructuredContent(), failureReason, "status", "failureReason") if err != nil { return err } } _, err = t.dynamicClient.Resource(talosSAGVR). Namespace(talosSACopy.GetNamespace()). Update(ctx, talosSACopy, metav1.UpdateOptions{}) return err } //nolint:gocyclo func (t *CRDController) needsUpdate(secret *corev1.Secret, desiredRoles []string) bool { talosconfigInSecret, ok := secret.Data[constants.TalosconfigFilename] if !ok { t.logger.Debug("talosconfig not found in secret", zap.String("key", constants.TalosconfigFilename)) return true } parsedTalosconfigInSecret, err := clientconfig.ReadFrom(bytes.NewReader(talosconfigInSecret)) if err != nil { t.logger.Debug("error parsing talosconfig in secret", zap.Error(err)) return true } talosconfigCtx := parsedTalosconfigInSecret.Contexts[parsedTalosconfigInSecret.Context] talosconfigCA, err := base64.StdEncoding.DecodeString(talosconfigCtx.CA) if err != nil { t.logger.Debug("error decoding talosconfig CA", zap.Error(err)) return true } if !bytes.Equal(t.talosCA.Crt, talosconfigCA) { t.logger.Debug("ca mismatch detected") return true } if len(talosconfigCtx.Endpoints) != 1 || talosconfigCtx.Endpoints[0] != endpoint { t.logger.Debug( "endpoint mismatch detected", zap.Strings("actual", talosconfigCtx.Endpoints), zap.Strings("expected", []string{endpoint}), ) return true } talosconfigCRT, err := base64.StdEncoding.DecodeString(talosconfigCtx.Crt) if err != nil { t.logger.Debug("error decoding talosconfig CRT", zap.Error(err)) return true } block, _ := pem.Decode(talosconfigCRT) if block == nil { t.logger.Debug("could not decode talosconfig CRT") return true } certificate, err := stdlibx509.ParseCertificate(block.Bytes) if err != nil { t.logger.Debug("error parsing certificate in talosconfig of secret", zap.Error(err)) return true } if certificate.NotAfter.IsZero() { t.logger.Debug("certificate in talosconfig of secret has no expiration date", zap.Error(err)) return true } if time.Now().Add(certTTL).Before(certificate.NotAfter) { t.logger.Debug( "certificate in talosconfig has expiration date too far in the future", zap.Time("expiration", certificate.NotAfter), ) return true } if time.Now().Add(certRenewThreshold).After(certificate.NotAfter) { t.logger.Debug( "certificate in talosconfig needs renewal", zap.Time("expiration", certificate.NotAfter), ) return true } actualRoles := certificate.Subject.Organization slices.Sort(actualRoles) slices.Sort(desiredRoles) if !slices.Equal(actualRoles, desiredRoles) { t.logger.Debug("roles in certificate do not match desired roles", zap.Strings("actual", actualRoles), zap.Strings("desired", desiredRoles)) return true } return false } func (t *CRDController) newSecret(talosSA *unstructured.Unstructured, roles role.Set) (*corev1.Secret, error) { config, err := t.generateTalosconfig(roles) if err != nil { return nil, err } return &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: talosSA.GetName(), OwnerReferences: []metav1.OwnerReference{ *metav1.NewControllerRef(talosSA, talosSAGVK), }, }, Data: map[string][]byte{ constants.TalosconfigFilename: config, }, }, nil } func (t *CRDController) generateTalosconfig(roles role.Set) ([]byte, error) { var newCert *x509.PEMEncodedCertificateAndKey newCert, err := secrets.NewAdminCertificateAndKey(time.Now(), t.talosCA, roles, certTTL) if err != nil { return nil, err } newTalosconfig := clientconfig.NewConfig(talosconfigContextName, []string{endpoint}, t.talosCA.Crt, newCert) return newTalosconfig.Bytes() } ================================================ FILE: internal/app/machined/pkg/controllers/kubeaccess/serviceaccount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess import ( "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubeaccess/serviceaccount" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // CRDController manages Kubernetes endpoints resource for Talos API endpoints. type CRDController struct{} // Name implements controller.Controller interface. func (ctrl *CRDController) Name() string { return "kubeaccess.CRDController" } // Inputs implements controller.Controller interface. func (ctrl *CRDController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubeaccess.ConfigType, ID: optional.Some(kubeaccess.ConfigID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.KubernetesType, ID: optional.Some(secrets.KubernetesID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.OSRootType, ID: optional.Some(secrets.OSRootID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *CRDController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *CRDController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var crdControllerCtxCancel context.CancelFunc crdControllerErrCh := make(chan error, 1) stopCRDController := func() { if crdControllerCtxCancel != nil { crdControllerCtxCancel() <-crdControllerErrCh crdControllerCtxCancel = nil } } defer stopCRDController() for { select { case <-ctx.Done(): return nil //nolint:govet case <-r.EventCh(): case err := <-crdControllerErrCh: if crdControllerCtxCancel != nil { crdControllerCtxCancel() } crdControllerCtxCancel = nil if err != nil && !errors.Is(err, context.Canceled) { return fmt.Errorf("error from crd controller: %w", err) } } kubeaccessConfig, err := safe.ReaderGet[*kubeaccess.Config](ctx, r, kubeaccess.NewConfig(config.NamespaceName, kubeaccess.ConfigID).Metadata()) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error fetching kubeaccess config: %w", err) } continue } var kubeaccessConfigSpec *kubeaccess.ConfigSpec if kubeaccessConfig != nil { kubeaccessConfigSpec = kubeaccessConfig.TypedSpec() } if kubeaccessConfig == nil || kubeaccessConfigSpec == nil || !kubeaccessConfigSpec.Enabled { stopCRDController() continue } kubeSecretsResources, err := safe.ReaderGet[*secrets.Kubernetes](ctx, r, resource.NewMetadata( secrets.NamespaceName, secrets.KubernetesType, secrets.KubernetesID, resource.VersionUndefined, )) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error fetching kubernetes secrets: %w", err) } continue } kubeSecretsSpec := kubeSecretsResources.TypedSpec() osSecretsResource, err := safe.ReaderGet[*secrets.OSRoot](ctx, r, resource.NewMetadata( secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined, )) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error fetching os secrets: %w", err) } continue } osSecretsSpec := osSecretsResource.TypedSpec() kubeconfig, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load([]byte(kubeSecretsSpec.LocalhostAdminKubeconfig)) }) if err != nil { return fmt.Errorf("error loading kubeconfig: %w", err) } stopCRDController() var crdControllerCtx context.Context crdControllerCtx, crdControllerCtxCancel = context.WithCancel(ctx) //nolint:govet go func() { crdControllerErrCh <- ctrl.runCRDController( crdControllerCtx, osSecretsSpec.IssuingCA, kubeconfig, kubeaccessConfigSpec, logger, ) }() r.ResetRestartBackoff() } } func (ctrl *CRDController) runCRDController( ctx context.Context, talosCA *x509.PEMEncodedCertificateAndKey, kubeconfig *rest.Config, kubeaccessCfgSpec *kubeaccess.ConfigSpec, logger *zap.Logger, ) error { return etcd.WithLock(ctx, constants.EtcdTalosServiceAccountCRDControllerMutex, logger, func() error { crdCtrl, err := serviceaccount.NewCRDController( talosCA, kubeconfig, kubeaccessCfgSpec.AllowedKubernetesNamespaces, kubeaccessCfgSpec.AllowedAPIRoles, logger, ) if err != nil { return err } return crdCtrl.Run(ctx, 1) }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) // ConfigController watches v1alpha1.Config, updates KubeSpan config. type ConfigController = transform.Controller[*config.MachineConfig, *kubespan.Config] // NewConfigController instanciates the config controller. func NewConfigController() *ConfigController { return transform.NewController( transform.Settings[*config.MachineConfig, *kubespan.Config]{ Name: "kubespan.ConfigController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*kubespan.Config] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*kubespan.Config]() } if cfg.Config().Machine() == nil || cfg.Config().Cluster() == nil { return optional.None[*kubespan.Config]() } return optional.Some(kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *kubespan.Config) error { spec := res.TypedSpec() *spec = kubespan.ConfigSpec{} if cfg != nil && cfg.Config().Machine() != nil { c := cfg.Config() if c.NetworkKubeSpanConfig() != nil { res.TypedSpec().Enabled = c.NetworkKubeSpanConfig().Enabled() res.TypedSpec().ForceRouting = c.NetworkKubeSpanConfig().ForceRouting() res.TypedSpec().AdvertiseKubernetesNetworks = c.NetworkKubeSpanConfig().AdvertiseKubernetesNetworks() res.TypedSpec().HarvestExtraEndpoints = c.NetworkKubeSpanConfig().HarvestExtraEndpoints() res.TypedSpec().MTU = c.NetworkKubeSpanConfig().MTU() if c.NetworkKubeSpanConfig().Filters() != nil { res.TypedSpec().EndpointFilters = c.NetworkKubeSpanConfig().Filters().Endpoints() res.TypedSpec().ExcludeAdvertisedNetworks = c.NetworkKubeSpanConfig().Filters().ExcludeAdvertisedNetworks() } } res.TypedSpec().ClusterID = c.Cluster().ID() res.TypedSpec().SharedSecret = c.Cluster().Secret() res.TypedSpec().ExtraEndpoints = c.KubespanConfig().ExtraAnnouncedEndpoints() } return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "fmt" "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubespanctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) type ConfigSuite struct { ctest.DefaultSuite } func (suite *ConfigSuite) TestReconcileConfig() { ctr, err := container.New( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ //nolint:staticcheck // legacy config KubeSpanEnabled: new(true), KubeSpanFilters: &v1alpha1.KubeSpanFilters{ KubeSpanFiltersExcludeAdvertisedNetworks: []string{"10.0.0.0/8"}, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", ClusterSecret: "I+1In7fLnpcRIjUmEoeugZnSyFoTF6MztLxICL5Yu0s=", }, }, &network.KubespanEndpointsConfigV1Alpha1{ ExtraAnnouncedEndpointsConfig: []netip.AddrPort{ netip.MustParseAddrPort("192.168.33.11:1001"), }, }, ) suite.Require().NoError(err) suite.Create(config.NewMachineConfig(ctr)) ctest.AssertResource(suite, kubespan.ConfigID, func(res *kubespan.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.True(spec.Enabled) asrt.Equal("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", spec.ClusterID) asrt.Equal("I+1In7fLnpcRIjUmEoeugZnSyFoTF6MztLxICL5Yu0s=", spec.SharedSecret) asrt.True(spec.ForceRouting) asrt.False(spec.AdvertiseKubernetesNetworks) asrt.False(spec.HarvestExtraEndpoints) asrt.Equal("[\"192.168.33.11:1001\"]", fmt.Sprintf("%q", spec.ExtraEndpoints)) asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")}, spec.ExcludeAdvertisedNetworks) }) } func (suite *ConfigSuite) TestReconcileDisabled() { cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{}, })) suite.Create(cfg) ctest.AssertResource(suite, kubespan.ConfigID, func(res *kubespan.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.False(spec.Enabled) }) } func (suite *ConfigSuite) TestReconcileMultiDoc() { kubeSpanCfg := network.NewKubeSpanV1Alpha1() kubeSpanCfg.ConfigEnabled = new(true) kubeSpanCfg.ConfigMTU = new(uint32(1380)) kubeSpanCfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigEndpoints: []string{"0.0.0.0/0", "::/0"}, ConfigExcludeAdvertisedNetworks: []network.Prefix{{Prefix: netip.MustParsePrefix("10.0.0.0/8")}}, } ctr, err := container.New( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "test-cluster-id-multi-doc", ClusterSecret: "test-cluster-secret-multi-doc", }, }, kubeSpanCfg, ) suite.Require().NoError(err) suite.Create(config.NewMachineConfig(ctr)) ctest.AssertResource(suite, kubespan.ConfigID, func(res *kubespan.Config, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.True(spec.Enabled) asrt.Equal("test-cluster-id-multi-doc", spec.ClusterID) asrt.Equal("test-cluster-secret-multi-doc", spec.SharedSecret) asrt.Equal(uint32(1380), spec.MTU) asrt.Equal([]string{"0.0.0.0/0", "::/0"}, spec.EndpointFilters) asrt.Equal([]netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")}, spec.ExcludeAdvertisedNetworks) }, ) } func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(kubespanctrl.NewConfigController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) // EndpointController watches KubeSpanPeerStatuses, Affiliates and harvests additional endpoints for the peers. type EndpointController struct{} // Name implements controller.Controller interface. func (ctrl *EndpointController) Name() string { return "kubespan.EndpointController" } // Inputs implements controller.Controller interface. func (ctrl *EndpointController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubespan.ConfigType, ID: optional.Some(kubespan.ConfigID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.AffiliateType, Kind: controller.InputWeak, }, { Namespace: kubespan.NamespaceName, Type: kubespan.PeerStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EndpointController) Outputs() []controller.Output { return []controller.Output{ { Type: kubespan.EndpointType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EndpointController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan configuration: %w", err) } r.StartTrackingOutputs() if cfg == nil || !cfg.TypedSpec().HarvestExtraEndpoints { // not enabled, short-circuit early if err = safe.CleanupOutputs[*kubespan.Endpoint](ctx, r); err != nil { return err } continue } // for every kubespan peer, if it's up and has endpoint, harvest that endpoint peerStatuses, err := safe.ReaderListAll[*kubespan.PeerStatus](ctx, r) if err != nil { return fmt.Errorf("error listing cluster affiliates: %w", err) } affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r) if err != nil { return fmt.Errorf("error listing cluster affiliates: %w", err) } // build lookup table of affiliate's kubespan public key back to affiliate ID affiliateLookup := make(map[string]string) for affiliate := range affiliates.All() { affiliateSpec := affiliate.TypedSpec() if affiliateSpec.KubeSpan.PublicKey != "" { affiliateLookup[affiliateSpec.KubeSpan.PublicKey] = affiliateSpec.NodeID } } for res := range peerStatuses.All() { peerStatus := res.TypedSpec() if peerStatus.State != kubespan.PeerStateUp { continue } if value.IsZero(peerStatus.Endpoint) { continue } affiliateID, ok := affiliateLookup[res.Metadata().ID()] if !ok { continue } if err = safe.WriterModify(ctx, r, kubespan.NewEndpoint(kubespan.NamespaceName, res.Metadata().ID()), func(res *kubespan.Endpoint) error { *res.TypedSpec() = kubespan.EndpointSpec{ AffiliateID: affiliateID, Endpoint: peerStatus.Endpoint, } return nil }); err != nil { return err } } if err = safe.CleanupOutputs[*kubespan.Endpoint](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubespanctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) type EndpointSuite struct { ctest.DefaultSuite } func (suite *EndpointSuite) TestReconcile() { cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().HarvestExtraEndpoints = true suite.Create(cfg) // create some affiliates and peer statuses affiliate1 := cluster.NewAffiliate(cluster.NamespaceName, "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate1.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, }, } affiliate2 := cluster.NewAffiliate(cluster.NamespaceName, "roLng5hmP0Gv9S5Pbfzaa93JSZjsdpXNAn7vzuCfsc8") *affiliate2.TypedSpec() = cluster.AffiliateSpec{ NodeID: "roLng5hmP0Gv9S5Pbfzaa93JSZjsdpXNAn7vzuCfsc8", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "1CXkdhWBm58c36kTpchR8iGlXHG1ruHa5W8gsFqD8Qs=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e1"), }, } suite.Create(affiliate1) suite.Create(affiliate2) peerStatus1 := kubespan.NewPeerStatus(kubespan.NamespaceName, affiliate1.TypedSpec().KubeSpan.PublicKey) *peerStatus1.TypedSpec() = kubespan.PeerStatusSpec{ Endpoint: netip.MustParseAddrPort("10.3.4.8:278"), State: kubespan.PeerStateUp, } peerStatus2 := kubespan.NewPeerStatus(kubespan.NamespaceName, affiliate2.TypedSpec().KubeSpan.PublicKey) *peerStatus2.TypedSpec() = kubespan.PeerStatusSpec{ Endpoint: netip.MustParseAddrPort("10.3.4.9:279"), State: kubespan.PeerStateUnknown, } peerStatus3 := kubespan.NewPeerStatus(kubespan.NamespaceName, "LoXPyyYh3kZwyKyWfCcf9VvgVv588cKhSKXavuUZqDg=") *peerStatus3.TypedSpec() = kubespan.PeerStatusSpec{ Endpoint: netip.MustParseAddrPort("10.3.4.10:270"), State: kubespan.PeerStateUp, } suite.Create(peerStatus1) suite.Create(peerStatus2) suite.Create(peerStatus3) // peer1 is up and has matching affiliate ctest.AssertResource(suite, peerStatus1.Metadata().ID(), func(res *kubespan.Endpoint, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(peerStatus1.TypedSpec().Endpoint, spec.Endpoint) asrt.Equal(affiliate1.TypedSpec().NodeID, spec.AffiliateID) }, ) // peer2 is not up, it shouldn't be published as an endpoint ctest.AssertNoResource[*kubespan.Endpoint](suite, peerStatus2.Metadata().ID()) // peer3 is up, but has not matching affiliate ctest.AssertNoResource[*kubespan.Endpoint](suite, peerStatus3.Metadata().ID()) } func TestEndpointSuite(t *testing.T) { t.Parallel() suite.Run(t, &EndpointSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&kubespanctrl.EndpointController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "context" "fmt" "net" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" ) // IdentityController watches KubeSpan configuration, updates KubeSpan Identity. type IdentityController struct { stateMachine blockautomaton.VolumeMounterAutomaton } // Name implements controller.Controller interface. func (ctrl *IdentityController) Name() string { return "kubespan.IdentityController" } // Inputs implements controller.Controller interface. func (ctrl *IdentityController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubespan.ConfigType, ID: optional.Some(kubespan.ConfigID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HardwareAddrType, ID: optional.Some(network.FirstHardwareAddr), Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *IdentityController) Outputs() []controller.Output { return []controller.Output{ { Type: kubespan.IdentityType, Kind: controller.OutputExclusive, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *IdentityController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan configuration: %w", err) } firstMAC, err := safe.ReaderGetByID[*network.HardwareAddr](ctx, r, network.FirstHardwareAddr) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting first MAC address: %w", err) } _, err = safe.ReaderGetByID[*kubespan.Identity](ctx, r, kubespan.LocalIdentity) alreadyHasIdentity := err == nil if cfg != nil && firstMAC != nil && cfg.TypedSpec().Enabled { if fipsmode.Strict() { return fmt.Errorf("KubeSpan is not supported in strict FIPS mode") } if ctrl.stateMachine == nil && !alreadyHasIdentity { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.establishIdentity(cfg, firstMAC), blockautomaton.WithDetached(true), ) } } else if alreadyHasIdentity { if err = r.Destroy(ctx, kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity).Metadata()); err != nil { return fmt.Errorf("error cleaning up identity: %w", err) } } if ctrl.stateMachine != nil { if err := ctrl.stateMachine.Run(ctx, r, logger, automaton.WithAfterFunc(func() error { ctrl.stateMachine = nil return nil }), ); err != nil { return fmt.Errorf("error running volume mounter machine: %w", err) } } r.ResetRestartBackoff() } } func (ctrl *IdentityController) establishIdentity( cfg *kubespan.Config, firstMAC *network.HardwareAddr, ) func( ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus, ) error { return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { var localIdentity kubespan.IdentitySpec if err := controllers.LoadOrNewFromFile(root, constants.KubeSpanIdentityFilename, &localIdentity, func(v *kubespan.IdentitySpec) error { return kubespanadapter.IdentitySpec(v).GenerateKey() }); err != nil { return fmt.Errorf("error caching kubespan identity: %w", err) } kubespanCfg := cfg.TypedSpec() mac := firstMAC.TypedSpec() if err := kubespanadapter.IdentitySpec(&localIdentity).UpdateAddress(kubespanCfg.ClusterID, net.HardwareAddr(mac.HardwareAddr)); err != nil { return fmt.Errorf("error updating KubeSpan address: %w", err) } return safe.WriterModify(ctx, r, kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity), func(res *kubespan.Identity) error { *res.TypedSpec() = localIdentity return nil }) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/identity_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "net" "os" "path/filepath" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubespanctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type IdentitySuite struct { ctest.DefaultSuite } func (suite *IdentitySuite) TestGenerate() { cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ClusterID = "8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=" suite.Create(cfg) firstMac := network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr) mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee") suite.Require().NoError(err) firstMac.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(mac) suite.Create(firstMac) statePath := suite.T().TempDir() mountID := (&kubespanctrl.IdentityController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) ctest.AssertNoResource[*kubespan.Identity](suite, kubespan.LocalIdentity) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, kubespan.LocalIdentity, func(identity *kubespan.Identity, asrt *assert.Assertions) { spec := identity.TypedSpec() _, err := wgtypes.ParseKey(spec.PrivateKey) asrt.NoError(err) _, err = wgtypes.ParseKey(spec.PublicKey) asrt.NoError(err) asrt.Equal("fd7f:175a:b97c:5602:e871:1bff:feb2:ccee/128", spec.Address.String()) asrt.Equal("fd7f:175a:b97c:5602::/64", spec.Subnet.String()) }) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) } func (suite *IdentitySuite) TestLoad() { statePath := suite.T().TempDir() mountID := (&kubespanctrl.IdentityController{}).Name() + "-" + constants.StatePartitionLabel // using verbatim data here to make sure nodeId representation is supported in future version of Talos const identityYaml = `address: "" subnet: "" privateKey: sF45u5ePau58WeeCUY3T8D9foEKaQ8Opx4cGC8g4XE4= publicKey: Oak2fBEWngBhwslBxDVgnRNHXs88OAp4kjroSX0uqUE= ` suite.Require().NoError(os.WriteFile(filepath.Join(statePath, constants.KubeSpanIdentityFilename), []byte(identityYaml), 0o600)) cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ClusterID = "8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=" suite.Create(cfg) firstMac := network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr) mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee") suite.Require().NoError(err) firstMac.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(mac) suite.Create(firstMac) ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) ctest.AssertNoResource[*kubespan.Identity](suite, kubespan.LocalIdentity) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, kubespan.LocalIdentity, func(identity *kubespan.Identity, asrt *assert.Assertions) { spec := identity.TypedSpec() asrt.Equal("sF45u5ePau58WeeCUY3T8D9foEKaQ8Opx4cGC8g4XE4=", spec.PrivateKey) asrt.Equal("Oak2fBEWngBhwslBxDVgnRNHXs88OAp4kjroSX0uqUE=", spec.PublicKey) asrt.Equal("fd7f:175a:b97c:5602:e871:1bff:feb2:ccee/128", spec.Address.String()) asrt.Equal("fd7f:175a:b97c:5602::/64", spec.Subnet.String()) }) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) } func TestIdentitySuite(t *testing.T) { t.Parallel() if fipsmode.Strict() { t.Skip("skipping test in FIPS mode") } if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } suite.Run(t, &IdentitySuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&kubespanctrl.IdentityController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/kubespan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubespan provides controllers which manage Talos KubeSpan feature. package kubespan ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/manager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "context" "errors" "fmt" "net/netip" "os" "slices" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "go.uber.org/zap" "go4.org/netipx" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DefaultPeerReconcileInterval is interval between peer status reconciliation on timer. // // Peers might be reconciled more often e.g. when peerSpecs are updated. const DefaultPeerReconcileInterval = 30 * time.Second // ManagerController sets up Wireguard networking based on KubeSpan configuration, watches and updates peer statuses. type ManagerController struct { WireguardClientFactory WireguardClientFactory PeerReconcileInterval time.Duration } // Name implements controller.Controller interface. func (ctrl *ManagerController) Name() string { return "kubespan.ManagerController" } // WireguardClientFactory allows mocking Wireguard client. type WireguardClientFactory func() (WireguardClient, error) // WireguardClient allows mocking Wireguard client. type WireguardClient interface { Device(string) (*wgtypes.Device, error) Close() error } // Inputs implements controller.Controller interface. func (ctrl *ManagerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubespan.ConfigType, ID: optional.Some(kubespan.ConfigID), Kind: controller.InputWeak, }, { Namespace: kubespan.NamespaceName, Type: kubespan.PeerSpecType, Kind: controller.InputWeak, }, { Namespace: kubespan.NamespaceName, Type: kubespan.IdentityType, ID: optional.Some(kubespan.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ManagerController) Outputs() []controller.Output { return []controller.Output{ { Type: network.LinkSpecType, Kind: controller.OutputShared, }, { Type: network.AddressSpecType, Kind: controller.OutputShared, }, { Type: network.RouteSpecType, Kind: controller.OutputShared, }, { Type: network.NfTablesChainType, Kind: controller.OutputShared, }, { Type: network.RoutingRuleSpecType, Kind: controller.OutputShared, }, { Type: kubespan.PeerStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( tickerC <-chan time.Time ticker *time.Ticker ) if ctrl.WireguardClientFactory == nil { ctrl.WireguardClientFactory = func() (WireguardClient, error) { return wgctrl.New() } } if ctrl.PeerReconcileInterval == 0 { ctrl.PeerReconcileInterval = DefaultPeerReconcileInterval } var wgClient WireguardClient defer func() { if wgClient != nil { wgClient.Close() //nolint:errcheck } }() for { var updateSpecs bool select { case <-ctx.Done(): return nil case <-r.EventCh(): updateSpecs = true case <-tickerC: } cfg, err := safe.ReaderGet[*kubespan.Config](ctx, r, resource.NewMetadata(config.NamespaceName, kubespan.ConfigType, kubespan.ConfigID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan configuration: %w", err) } if cfg == nil || !cfg.TypedSpec().Enabled { if ticker != nil { ticker.Stop() tickerC = nil } // KubeSpan is not enabled, cleanup everything if err = ctrl.cleanup(ctx, r); err != nil { return err } continue } if wgClient == nil { wgClient, err = ctrl.WireguardClientFactory() if err != nil { return fmt.Errorf("error creating wireguard client: %w", err) } } if ticker == nil { ticker = time.NewTicker(ctrl.PeerReconcileInterval) tickerC = ticker.C } cfgSpec := cfg.TypedSpec() localIdentity, err := safe.ReaderGet[*kubespan.Identity](ctx, r, resource.NewMetadata(kubespan.NamespaceName, kubespan.IdentityType, kubespan.LocalIdentity, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting local KubeSpan identity: %w", err) } localSpec := localIdentity.TypedSpec() // fetch PeerSpecs and PeerStatuses and sync them peerSpecList, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.PeerSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing peer specs: %w", err) } peerSpecs := make(map[string]*kubespan.PeerSpecSpec, len(peerSpecList.Items)) for _, res := range peerSpecList.Items { peerSpecs[res.Metadata().ID()] = res.(*kubespan.PeerSpec).TypedSpec() } peerStatusList, err := r.List(ctx, resource.NewMetadata(kubespan.NamespaceName, kubespan.PeerStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing peer status: %w", err) } peerStatuses := make(map[string]*kubespan.PeerStatusSpec, len(peerStatusList.Items)) for _, res := range peerStatusList.Items { // drop any peer statuses which are not in the peer specs if _, ok := peerSpecs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error destroying peer status: %w", err) } continue } peerStatuses[res.Metadata().ID()] = res.(*kubespan.PeerStatus).TypedSpec() } // create missing peer statuses for pubKey, peerSpec := range peerSpecs { if _, ok := peerStatuses[pubKey]; !ok { peerStatuses[pubKey] = &kubespan.PeerStatusSpec{ Label: peerSpec.Label, } } } // update peer status from Wireguard data wgDevice, err := wgClient.Device(constants.KubeSpanLinkName) if err != nil && !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("error fetching wireguard link status: %w", err) } if wgDevice != nil { // wgDevice might be nil if the link is not created yet for _, peerInfo := range wgDevice.Peers { if peerStatus, ok := peerStatuses[peerInfo.PublicKey.String()]; ok { kubespanadapter.PeerStatusSpec(peerStatus).UpdateFromWireguard(peerInfo) } } } // calculate peer status connection state for _, peerStatus := range peerStatuses { kubespanadapter.PeerStatusSpec(peerStatus).CalculateState() } // build wireguard peer configuration wgPeers := make([]network.WireguardPeer, 0, len(peerSpecs)) for pubKey, peerSpec := range peerSpecs { // list of statuses and specs should be in sync at this point peerStatus := peerStatuses[pubKey] var endpoint string // check if the endpoint should be updated if kubespanadapter.PeerStatusSpec(peerStatus).ShouldChangeEndpoint() { newEndpoint := kubespanadapter.PeerStatusSpec(peerStatus).PickNewEndpoint(peerSpec.Endpoints) if !value.IsZero(newEndpoint) { logger.Debug("updating endpoint for the peer", zap.String("peer", pubKey), zap.String("label", peerSpec.Label), zap.Stringer("endpoint", newEndpoint)) endpoint = newEndpoint.String() kubespanadapter.PeerStatusSpec(peerStatus).UpdateEndpoint(newEndpoint) updateSpecs = true } } // re-establish the endpoint if it wasn't applied to the Wireguard config completely if !value.IsZero(peerStatus.LastUsedEndpoint) && (value.IsZero(peerStatus.Endpoint) || peerStatus.Endpoint == peerStatus.LastUsedEndpoint) { endpoint = peerStatus.LastUsedEndpoint.String() peerStatus.Endpoint = peerStatus.LastUsedEndpoint updateSpecs = true } wgPeers = append(wgPeers, network.WireguardPeer{ PublicKey: pubKey, PresharedKey: cfgSpec.SharedSecret, Endpoint: endpoint, PersistentKeepaliveInterval: constants.KubeSpanDefaultPeerKeepalive, AllowedIPs: slices.Clone(peerSpec.AllowedIPs), }) } // build a full set of routed over KubeSpan IPs, // note this doesn't include KubeSpan ULA addresses var routedIPsBuilder netipx.IPSetBuilder for pubKey, peerSpec := range peerSpecs { // list of statuses and specs should be in sync at this point peerStatus := peerStatuses[pubKey] // add allowedIPs to the nftables set if either routing is forced (for any peer state) // or if the peer connection state is up. if cfgSpec.ForceRouting || peerStatus.State == kubespan.PeerStateUp { for _, prefix := range peerSpec.AllowedIPs { if !network.IsULA(prefix.Addr(), network.ULAKubeSpan) { routedIPsBuilder.AddPrefix(prefix) } } } } routedIPsSet, err := routedIPsBuilder.IPSet() if err != nil { return fmt.Errorf("failed building allowed IPs set: %w", err) } // update peer statuses for pubKey, peerStatus := range peerStatuses { if err = safe.WriterModify(ctx, r, kubespan.NewPeerStatus( kubespan.NamespaceName, pubKey, ), func(r *kubespan.PeerStatus) error { *r.TypedSpec() = *peerStatus return nil }, ); err != nil { return fmt.Errorf("error modifying peer status: %w", err) } } mtu := cfgSpec.MTU // always update the firewall rules, as allowedIPsSet might change at any moment due to peer up/down events if err = safe.WriterModify(ctx, r, network.NewNfTablesChain( network.NamespaceName, "kubespan_prerouting", ), func(r *network.NfTablesChain) error { spec := r.TypedSpec() spec.Type = nethelpers.ChainTypeFilter spec.Hook = nethelpers.ChainHookPrerouting spec.Priority = nethelpers.ChainPriorityFilter spec.Policy = nethelpers.VerdictAccept spec.Rules = []network.NfTablesRule{ { MatchMark: &network.NfTablesMark{ Mask: constants.KubeSpanDefaultFirewallMask, Value: constants.KubeSpanDefaultFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: routedIPsSet.Prefixes(), }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultForceFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, } return nil }, ); err != nil { return fmt.Errorf("error modifying nftables chain: %w", err) } if err = safe.WriterModify(ctx, r, network.NewNfTablesChain( network.NamespaceName, "kubespan_outgoing", ), func(r *network.NfTablesChain) error { spec := r.TypedSpec() spec.Type = nethelpers.ChainTypeRoute spec.Hook = nethelpers.ChainHookOutput spec.Priority = nethelpers.ChainPriorityFilter spec.Policy = nethelpers.VerdictAccept spec.Rules = []network.NfTablesRule{ { MatchMark: &network.NfTablesMark{ Mask: constants.KubeSpanDefaultFirewallMask, Value: constants.KubeSpanDefaultFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, { MatchOIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{"lo"}, }, Verdict: new(nethelpers.VerdictAccept), }, { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: routedIPsSet.Prefixes(), }, ClampMSS: &network.NfTablesClampMSS{ MTU: uint16(mtu), }, }, { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: routedIPsSet.Prefixes(), }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultForceFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, } return nil }, ); err != nil { return fmt.Errorf("error modifying nftables chain: %w", err) } if !updateSpecs { // micro-optimization: skip updating specs if there are no changes to the incoming resources and no endpoint changes r.ResetRestartBackoff() continue } if err = safe.WriterModify(ctx, r, network.NewAddressSpec( network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID(constants.KubeSpanLinkName, localSpec.Address)), ), func(r *network.AddressSpec) error { spec := r.TypedSpec() spec.Address = netip.PrefixFrom(localSpec.Address.Addr(), localSpec.Subnet.Bits()) spec.ConfigLayer = network.ConfigOperator spec.Family = nethelpers.FamilyInet6 spec.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent) spec.LinkName = constants.KubeSpanLinkName spec.Scope = nethelpers.ScopeGlobal return nil }, ); err != nil { return fmt.Errorf("error modifying address: %w", err) } for _, spec := range []network.RouteSpecSpec{ { Family: nethelpers.FamilyInet4, Destination: netip.Prefix{}, Source: netip.Addr{}, Gateway: netip.Addr{}, MTU: mtu, OutLinkName: constants.KubeSpanLinkName, Table: nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), Priority: 1, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Flags: 0, Protocol: nethelpers.ProtocolStatic, ConfigLayer: network.ConfigOperator, }, { Family: nethelpers.FamilyInet6, Destination: netip.Prefix{}, Source: netip.Addr{}, Gateway: netip.Addr{}, MTU: mtu, OutLinkName: constants.KubeSpanLinkName, Table: nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), Priority: 1, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Flags: 0, Protocol: nethelpers.ProtocolStatic, ConfigLayer: network.ConfigOperator, }, } { if err = safe.WriterModify(ctx, r, network.NewRouteSpec( network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.RouteID(spec.Table, spec.Family, spec.Destination, spec.Gateway, spec.Priority, spec.OutLinkName)), ), func(r *network.RouteSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return fmt.Errorf("error modifying route spec: %w", err) } } if err = safe.WriterModify(ctx, r, network.NewLinkSpec( network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.LinkID(constants.KubeSpanLinkName)), ), func(r *network.LinkSpec) error { spec := r.TypedSpec() spec.ConfigLayer = network.ConfigOperator spec.Name = constants.KubeSpanLinkName spec.Type = nethelpers.LinkNone spec.Kind = "wireguard" spec.Up = true spec.Logical = true spec.MTU = mtu spec.Wireguard = network.WireguardSpec{ PrivateKey: localSpec.PrivateKey, ListenPort: constants.KubeSpanDefaultPort, FirewallMark: constants.KubeSpanDefaultFirewallMark, Peers: wgPeers, } spec.Wireguard.Sort() return nil }, ); err != nil { return fmt.Errorf("error modifying link spec: %w", err) } for _, ruleSpec := range []network.RoutingRuleSpecSpec{ { Family: nethelpers.FamilyInet4, Table: nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), Action: nethelpers.RoutingRuleActionUnicast, FwMark: constants.KubeSpanDefaultForceFirewallMark, FwMask: constants.KubeSpanDefaultFirewallMask, Priority: constants.KubeSpanDefaultRulePriority, ConfigLayer: network.ConfigOperator, }, { Family: nethelpers.FamilyInet6, Table: nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), Action: nethelpers.RoutingRuleActionUnicast, FwMark: constants.KubeSpanDefaultForceFirewallMark, FwMask: constants.KubeSpanDefaultFirewallMask, Priority: constants.KubeSpanDefaultRulePriority, ConfigLayer: network.ConfigOperator, }, } { if err = safe.WriterModify(ctx, r, network.NewRoutingRuleSpec( network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.RoutingRuleID(ruleSpec.Family, ruleSpec.Priority)), ), func(r *network.RoutingRuleSpec) error { *r.TypedSpec() = ruleSpec return nil }, ); err != nil { return fmt.Errorf("error modifying routing rule spec: %w", err) } } r.ResetRestartBackoff() } } func (ctrl *ManagerController) cleanup(ctx context.Context, r controller.Runtime) error { for _, item := range []struct { namespace resource.Namespace typ resource.Type }{ { namespace: network.ConfigNamespaceName, typ: network.LinkSpecType, }, { namespace: network.ConfigNamespaceName, typ: network.AddressSpecType, }, { namespace: network.ConfigNamespaceName, typ: network.RouteSpecType, }, { namespace: network.ConfigNamespaceName, typ: network.RoutingRuleSpecType, }, { namespace: network.NamespaceName, typ: network.NfTablesChainType, }, { namespace: kubespan.NamespaceName, typ: kubespan.PeerStatusType, }, } { // list keys for cleanup list, err := r.List(ctx, resource.NewMetadata(item.namespace, item.typ, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up resource %s: %w", res, err) } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/manager_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "net" "net/netip" "os" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" kubespanadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/kubespan" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubespanctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ManagerSuite struct { ctest.DefaultSuite mockWireguard *mockWireguardClient } func (suite *ManagerSuite) TestDisabled() { cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = false suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) ctest.AssertNoResource[*network.NfTablesChain](suite, "kubespan_outgoing") } type mockWireguardClient struct { deviceStateMu sync.Mutex deviceState *wgtypes.Device } func (mock *mockWireguardClient) update(newState *wgtypes.Device) { mock.deviceStateMu.Lock() defer mock.deviceStateMu.Unlock() mock.deviceState = newState } func (mock *mockWireguardClient) Device(name string) (*wgtypes.Device, error) { mock.deviceStateMu.Lock() defer mock.deviceStateMu.Unlock() if mock.deviceState != nil { return mock.deviceState, nil } return nil, os.ErrNotExist } func (mock *mockWireguardClient) Close() error { return nil } //nolint:dupl func (suite *ManagerSuite) TestReconcile() { if fipsmode.Strict() { suite.T().Skip("skipping test in strict FIPS mode") } cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().SharedSecret = "TPbGXrYlvuXgAl8dERpwjlA5tnEMoihPDPxlovcLtVg=" cfg.TypedSpec().ForceRouting = true suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) mac, err := net.ParseMAC("ea:71:1b:b2:cc:ee") suite.Require().NoError(err) localIdentity := kubespan.NewIdentity(kubespan.NamespaceName, kubespan.LocalIdentity) suite.Require().NoError(kubespanadapter.IdentitySpec(localIdentity.TypedSpec()).GenerateKey()) suite.Require().NoError( kubespanadapter.IdentitySpec(localIdentity.TypedSpec()).UpdateAddress( "v16UCWpO2iOm82n6F8dGCJ41ZXXBvDrjRDs2su7C_zs=", mac, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), localIdentity)) // initial setup: link should be created without any peers ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.KubeSpanLinkName)), func(res *network.LinkSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(network.ConfigOperator, spec.ConfigLayer) asrt.Equal(constants.KubeSpanLinkName, spec.Name) asrt.Equal(nethelpers.LinkNone, spec.Type) asrt.Equal("wireguard", spec.Kind) asrt.True(spec.Up) asrt.True(spec.Logical) asrt.Equal(localIdentity.TypedSpec().PrivateKey, spec.Wireguard.PrivateKey) asrt.Equal(constants.KubeSpanDefaultPort, spec.Wireguard.ListenPort) asrt.Equal(constants.KubeSpanDefaultFirewallMark, spec.Wireguard.FirewallMark) asrt.Len(spec.Wireguard.Peers, 0) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResource(suite, network.LayeredID( network.ConfigOperator, network.AddressID(constants.KubeSpanLinkName, localIdentity.TypedSpec().Address), ), func(res *network.AddressSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(localIdentity.TypedSpec().Address.Addr(), spec.Address.Addr()) asrt.Equal(localIdentity.TypedSpec().Subnet.Bits(), spec.Address.Bits()) asrt.Equal(network.ConfigOperator, spec.ConfigLayer) asrt.Equal(nethelpers.FamilyInet6, spec.Family) asrt.Equal(nethelpers.AddressFlags(nethelpers.AddressPermanent), spec.Flags) asrt.Equal(constants.KubeSpanLinkName, spec.LinkName) asrt.Equal(nethelpers.ScopeGlobal, spec.Scope) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) ctest.AssertResource(suite, network.LayeredID( network.ConfigOperator, network.RouteID( constants.KubeSpanDefaultRoutingTable, nethelpers.FamilyInet4, netip.Prefix{}, netip.Addr{}, 1, "kubespan", ), ), func(res *network.RouteSpec, asrt *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResource(suite, network.LayeredID( network.ConfigOperator, network.RouteID( constants.KubeSpanDefaultRoutingTable, nethelpers.FamilyInet6, netip.Prefix{}, netip.Addr{}, 1, "kubespan", ), ), func(res *network.RouteSpec, asrt *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // check routing rules (IPv4 + IPv6) ctest.AssertResource(suite, network.LayeredID( network.ConfigOperator, network.RoutingRuleID( nethelpers.FamilyInet4, constants.KubeSpanDefaultRulePriority, ), ), func(res *network.RoutingRuleSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(nethelpers.FamilyInet4, spec.Family) asrt.Equal(nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), spec.Table) asrt.Equal(nethelpers.RoutingRuleActionUnicast, spec.Action) asrt.Equal(uint32(constants.KubeSpanDefaultForceFirewallMark), spec.FwMark) asrt.Equal(uint32(constants.KubeSpanDefaultFirewallMask), spec.FwMask) asrt.Equal(uint32(constants.KubeSpanDefaultRulePriority), spec.Priority) asrt.Equal(network.ConfigOperator, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResource(suite, network.LayeredID( network.ConfigOperator, network.RoutingRuleID( nethelpers.FamilyInet6, constants.KubeSpanDefaultRulePriority, ), ), func(res *network.RoutingRuleSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(nethelpers.FamilyInet6, spec.Family) asrt.Equal(nethelpers.RoutingTable(constants.KubeSpanDefaultRoutingTable), spec.Table) asrt.Equal(nethelpers.RoutingRuleActionUnicast, spec.Action) asrt.Equal(uint32(constants.KubeSpanDefaultForceFirewallMark), spec.FwMark) asrt.Equal(uint32(constants.KubeSpanDefaultFirewallMask), spec.FwMask) asrt.Equal(uint32(constants.KubeSpanDefaultRulePriority), spec.Priority) asrt.Equal(network.ConfigOperator, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // add two peers, they should be added to the wireguard link spec and should be tracked in peer statuses peer1 := kubespan.NewPeerSpec(kubespan.NamespaceName, "3FxU7UuwektMjbyuJBs7i1hDj2rQA6tHnbNB6WrQxww=") peer1.TypedSpec().Address = netip.MustParseAddr("fd8a:4396:731e:e702:145e:c4ff:fe41:1ef9") peer1.TypedSpec().Label = "worker-1" peer1.TypedSpec().AllowedIPs = []netip.Prefix{ netip.MustParsePrefix("10.244.1.0/24"), } peer1.TypedSpec().Endpoints = []netip.AddrPort{ netip.MustParseAddrPort("172.20.0.3:51280"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), peer1)) key1, err := wgtypes.ParseKey(peer1.Metadata().ID()) suite.Require().NoError(err) peer2 := kubespan.NewPeerSpec(kubespan.NamespaceName, "tQuicRD0tqCu48M+zrySTe4slT15JxWhWIboZOB4tWs=") peer2.TypedSpec().Address = netip.MustParseAddr("fd8a:4396:731e:e702:9c83:cbff:fed0:f94b") peer2.TypedSpec().Label = "worker-2" peer2.TypedSpec().AllowedIPs = []netip.Prefix{ netip.MustParsePrefix("10.244.2.0/24"), } peer2.TypedSpec().Endpoints = []netip.AddrPort{ netip.MustParseAddrPort("172.20.0.4:51280"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), peer2)) key2, err := wgtypes.ParseKey(peer2.Metadata().ID()) suite.Require().NoError(err) ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.KubeSpanLinkName)), func(res *network.LinkSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Len(spec.Wireguard.Peers, 2) if len(spec.Wireguard.Peers) != 2 { return } for i, peer := range []*kubespan.PeerSpec{peer1, peer2} { asrt.Equal(peer.Metadata().ID(), spec.Wireguard.Peers[i].PublicKey) asrt.Equal(cfg.TypedSpec().SharedSecret, spec.Wireguard.Peers[i].PresharedKey) asrt.Equal(peer.TypedSpec().AllowedIPs, spec.Wireguard.Peers[i].AllowedIPs) asrt.Equal(peer.TypedSpec().Endpoints[0].String(), spec.Wireguard.Peers[i].Endpoint) } }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) for _, peer := range []*kubespan.PeerSpec{peer1, peer2} { ctest.AssertResource(suite, peer.Metadata().ID(), func(res *kubespan.PeerStatus, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(peer.TypedSpec().Label, spec.Label) asrt.Equal(kubespan.PeerStateUnknown, spec.State) asrt.Equal(peer.TypedSpec().Endpoints[0], spec.Endpoint) asrt.Equal(peer.TypedSpec().Endpoints[0], spec.LastUsedEndpoint) asrt.WithinDuration(time.Now(), spec.LastEndpointChange, 3*time.Second) }, ) } // check firewall rules ctest.AssertResource(suite, "kubespan_prerouting", func(res *network.NfTablesChain, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(nethelpers.ChainTypeFilter, spec.Type) asrt.Equal(nethelpers.ChainHookPrerouting, spec.Hook) asrt.Equal(nethelpers.ChainPriorityFilter, spec.Priority) asrt.Equal(nethelpers.VerdictAccept, spec.Policy) asrt.Len(spec.Rules, 2) if len(spec.Rules) != 2 { return } asrt.Equal( network.NfTablesRule{ MatchMark: &network.NfTablesMark{ Mask: constants.KubeSpanDefaultFirewallMask, Value: constants.KubeSpanDefaultFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, spec.Rules[0], ) asrt.Equal( network.NfTablesRule{ MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.244.1.0/24"), netip.MustParsePrefix("10.244.2.0/24"), }, }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultForceFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, spec.Rules[1], ) }, ) // update config and disable force routing, nothing should be routed cfg.TypedSpec().ForceRouting = false suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) ctest.AssertResource(suite, "kubespan_prerouting", func(res *network.NfTablesChain, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal( network.NfTablesRule{ MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{}, }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultForceFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, spec.Rules[1], ) }, ) // report up status via wireguard mock suite.mockWireguard.update( &wgtypes.Device{ Peers: []wgtypes.Peer{ { PublicKey: key1, Endpoint: asUDP(peer1.TypedSpec().Endpoints[0]), LastHandshakeTime: time.Now(), }, { PublicKey: key2, Endpoint: asUDP(peer2.TypedSpec().Endpoints[0]), LastHandshakeTime: time.Now(), }, }, }, ) for _, peer := range []*kubespan.PeerSpec{peer1, peer2} { ctest.AssertResource(suite, peer.Metadata().ID(), func(res *kubespan.PeerStatus, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(kubespan.PeerStateUp, spec.State) }, ) } ctest.AssertResource(suite, "kubespan_prerouting", func(res *network.NfTablesChain, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal( network.NfTablesRule{ MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.244.1.0/24"), netip.MustParsePrefix("10.244.2.0/24"), }, }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultForceFirewallMark, }, Verdict: new(nethelpers.VerdictAccept), }, spec.Rules[1], ) }, ) // update config and disable wireguard, everything should be cleaned up cfg.TypedSpec().Enabled = false suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) ctest.AssertNoResource[*network.LinkSpec]( suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.KubeSpanLinkName)), rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertNoResource[*network.NfTablesChain]( suite, "kubespan_prerouting", ) ctest.AssertNoResource[*network.RoutingRuleSpec]( suite, network.LayeredID( network.ConfigOperator, network.RoutingRuleID( nethelpers.FamilyInet4, constants.KubeSpanDefaultRulePriority, ), ), rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertNoResource[*network.RoutingRuleSpec]( suite, network.LayeredID( network.ConfigOperator, network.RoutingRuleID( nethelpers.FamilyInet6, constants.KubeSpanDefaultRulePriority, ), ), rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func asUDP(addr netip.AddrPort) *net.UDPAddr { return &net.UDPAddr{ IP: addr.Addr().AsSlice(), Port: int(addr.Port()), Zone: addr.Addr().Zone(), } } func TestManagerSuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("requires root") } mockWireguard := &mockWireguardClient{} suite.Run(t, &ManagerSuite{ mockWireguard: mockWireguard, DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&kubespanctrl.ManagerController{ WireguardClientFactory: func() (kubespanctrl.WireguardClient, error) { return mockWireguard, nil }, PeerReconcileInterval: time.Second, })) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/peer_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "context" "fmt" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "go4.org/netipx" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) // PeerSpecController watches cluster.Affiliates updates PeerSpec. type PeerSpecController struct{} // Name implements controller.Controller interface. func (ctrl *PeerSpecController) Name() string { return "kubespan.PeerSpecController" } // Inputs implements controller.Controller interface. func (ctrl *PeerSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: kubespan.ConfigType, ID: optional.Some(kubespan.ConfigID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.AffiliateType, Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PeerSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: kubespan.PeerSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *PeerSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*kubespan.Config](ctx, r, kubespan.ConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting kubespan configuration: %w", err) } localIdentity, err := safe.ReaderGetByID[*cluster.Identity](ctx, r, cluster.LocalIdentity) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting first MAC address: %w", err) } affiliates, err := safe.ReaderListAll[*cluster.Affiliate](ctx, r) if err != nil { return fmt.Errorf("error listing cluster affiliates: %w", err) } touchedIDs := map[resource.ID]struct{}{} if cfg != nil && localIdentity != nil && cfg.TypedSpec().Enabled { localAffiliateID := localIdentity.TypedSpec().NodeID peerIPSets := make(map[string]*netipx.IPSet, affiliates.Len()) affiliateLoop: for affiliate := range affiliates.All() { if affiliate.Metadata().ID() == localAffiliateID { // skip local affiliate, it's not a peer continue } spec := affiliate.TypedSpec() if spec.KubeSpan.PublicKey == "" { // no kubespan information, skip it continue } var builder netipx.IPSetBuilder for _, ipPrefix := range spec.KubeSpan.AdditionalAddresses { builder.AddPrefix(ipPrefix) } for _, ip := range spec.Addresses { builder.Add(ip) } for _, ipPrefix := range spec.KubeSpan.ExcludeAdvertisedNetworks { builder.RemovePrefix(ipPrefix) } builder.Add(spec.KubeSpan.Address) var ipSet *netipx.IPSet ipSet, err = builder.IPSet() if err != nil { logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err)) continue } for otherPublicKey, otherIPSet := range peerIPSets { if otherIPSet.Overlaps(ipSet) { logger.Warn("peer address overlap", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey), zap.Strings("this_ips", dumpSet(ipSet)), zap.Strings("other_ips", dumpSet(otherIPSet))) // exclude overlapping IPs from the ipSet var bldr netipx.IPSetBuilder // ipSet = ipSet & ~otherIPSet bldr.AddSet(otherIPSet) bldr.Complement() bldr.Intersect(ipSet) ipSet, err = bldr.IPSet() if err != nil { logger.Warn("failed building list of IP ranges for the peer", zap.String("ignored_peer", spec.KubeSpan.PublicKey), zap.String("label", spec.Nodename), zap.Error(err)) continue affiliateLoop } if len(ipSet.Ranges()) == 0 { logger.Warn("conflict resolution removed all ranges", zap.String("this_peer", spec.KubeSpan.PublicKey), zap.String("other_peer", otherPublicKey)) } } } peerIPSets[spec.KubeSpan.PublicKey] = ipSet if err = safe.WriterModify(ctx, r, kubespan.NewPeerSpec(kubespan.NamespaceName, spec.KubeSpan.PublicKey), func(res *kubespan.PeerSpec) error { *res.TypedSpec() = kubespan.PeerSpecSpec{ Address: spec.KubeSpan.Address, AllowedIPs: ipSet.Prefixes(), Endpoints: slices.Clone(spec.KubeSpan.Endpoints), Label: spec.Nodename, } return nil }); err != nil { return err } touchedIDs[spec.KubeSpan.PublicKey] = struct{}{} } } // list keys for cleanup list, err := safe.ReaderListAll[*kubespan.PeerSpec](ctx, r) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for res := range list.All() { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } // dumpSet converts IPSet to a form suitable for logging. func dumpSet(set *netipx.IPSet) []string { return xslices.Map(set.Ranges(), netipx.IPRange.String) } ================================================ FILE: internal/app/machined/pkg/controllers/kubespan/peer_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" clusteradapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" kubespanctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) type PeerSpecSuite struct { ctest.DefaultSuite statePath string } func (suite *PeerSpecSuite) TestReconcile() { suite.statePath = suite.T().TempDir() stateMount := runtimeres.NewMountStatus(v1alpha1.NamespaceName, constants.StatePartitionLabel) suite.Create(stateMount) cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = true suite.Create(cfg) nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Create(nodeIdentity) affiliate1 := cluster.NewAffiliate(cluster.NamespaceName, "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate1.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24"), netip.MustParsePrefix("10.244.3.0/32")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("10.244.3.128/25")}, }, } affiliate2 := cluster.NewAffiliate(cluster.NamespaceName, "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F") *affiliate2.TypedSpec() = cluster.AffiliateSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Hostname: "worker-1", Nodename: "worker-1", MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.5")}, } affiliate3 := cluster.NewAffiliate(cluster.NamespaceName, "xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F") *affiliate3.TypedSpec() = cluster.AffiliateSpec{ NodeID: "xCnFFfxylOf9i5ynhAkt6ZbfcqaLDGKfIa3gwpuaxe7F", MachineType: machine.TypeWorker, Nodename: "worker-2", Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.6")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "mB6WlFOR66Jx5rtPMIpxJ3s4XHyer9NCzqWPP7idGRo", Address: netip.MustParseAddr("fdc8:8aee:4e2d:1202:f073:9cff:fe6c:4d67"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.4.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("192.168.3.6:51820")}, }, } // local node affiliate, should be skipped as a peer affiliate4 := cluster.NewAffiliate(cluster.NamespaceName, nodeIdentity.TypedSpec().NodeID) *affiliate4.TypedSpec() = cluster.AffiliateSpec{ NodeID: nodeIdentity.TypedSpec().NodeID, MachineType: machine.TypeWorker, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.7")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "27E8I+ekrqT21cq2iW6+fDe+H7WBw6q9J7vqLCeswiM=", Address: netip.MustParseAddr("fdc8:8aee:4e2d:1202:f073:9cff:fe6c:4d67"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.5.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("192.168.3.7:51820")}, }, } for _, r := range []resource.Resource{affiliate1, affiliate2, affiliate3, affiliate4} { suite.Create(r) } // affiliate2 shouldn't be rendered as a peer, as it doesn't have kubespan data ctest.AssertResources(suite, []resource.ID{ affiliate1.TypedSpec().KubeSpan.PublicKey, affiliate3.TypedSpec().KubeSpan.PublicKey, }, func(*kubespan.PeerSpec, *assert.Assertions) {}, ) ctest.AssertNoResource[*kubespan.PeerSpec](suite, affiliate2.TypedSpec().KubeSpan.PublicKey) ctest.AssertResource(suite, affiliate1.TypedSpec().KubeSpan.PublicKey, func(res *kubespan.PeerSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0", spec.Address.String()) asrt.Equal("[10.244.3.0/25 192.168.3.4/32 fd50:8d60:4238:6302:f857:23ff:fe21:d1e0/128]", fmt.Sprintf("%v", spec.AllowedIPs)) asrt.Equal([]netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, spec.Endpoints) asrt.Equal("bar", spec.Label) }, ) ctest.AssertResource(suite, affiliate3.TypedSpec().KubeSpan.PublicKey, func(res *kubespan.PeerSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal("fdc8:8aee:4e2d:1202:f073:9cff:fe6c:4d67", spec.Address.String()) asrt.Equal("[10.244.4.0/24 192.168.3.6/32 fdc8:8aee:4e2d:1202:f073:9cff:fe6c:4d67/128]", fmt.Sprintf("%v", spec.AllowedIPs)) asrt.Equal([]netip.AddrPort{netip.MustParseAddrPort("192.168.3.6:51820")}, spec.Endpoints) asrt.Equal("worker-2", spec.Label) }, ) // disabling kubespan should remove all peers cfg.TypedSpec().Enabled = false suite.Update(cfg) ctest.AssertNoResource[*kubespan.PeerSpec](suite, affiliate1.TypedSpec().KubeSpan.PublicKey) ctest.AssertNoResource[*kubespan.PeerSpec](suite, affiliate2.TypedSpec().KubeSpan.PublicKey) } func (suite *PeerSpecSuite) TestIPOverlap() { suite.statePath = suite.T().TempDir() stateMount := runtimeres.NewMountStatus(v1alpha1.NamespaceName, constants.StatePartitionLabel) suite.Create(stateMount) cfg := kubespan.NewConfig(config.NamespaceName, kubespan.ConfigID) cfg.TypedSpec().Enabled = true suite.Create(cfg) nodeIdentity := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) suite.Require().NoError(clusteradapter.IdentitySpec(nodeIdentity.TypedSpec()).Generate()) suite.Create(nodeIdentity) affiliate1 := cluster.NewAffiliate(cluster.NamespaceName, "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC") *affiliate1.TypedSpec() = cluster.AffiliateSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Nodename: "bar", MachineType: machine.TypeControlPlane, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24"), netip.MustParsePrefix("10.244.3.0/32")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, }, } affiliate2 := cluster.NewAffiliate(cluster.NamespaceName, "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F") *affiliate2.TypedSpec() = cluster.AffiliateSpec{ NodeID: "9dwHNUViZlPlIervqX9Qo256RUhrfhgO0xBBnKcKl4F", Hostname: "worker-1", Nodename: "worker-1", MachineType: machine.TypeWorker, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "Zr5ewpUm2Ywo1c+/59WFKIBjZ3c/nVbIWsT5elbjwCU=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e1"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.2.0/23"), netip.MustParsePrefix("192.168.3.0/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, }, } for _, r := range []resource.Resource{affiliate1, affiliate2} { suite.Create(r) } // affiliate2 should be rendered as a peer, but with reduced address as its AdditionalAddresses overlap with affiliate1 addresses ctest.AssertResource(suite, affiliate1.TypedSpec().KubeSpan.PublicKey, func(res *kubespan.PeerSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(`["10.244.3.0/24" "fd50:8d60:4238:6302:f857:23ff:fe21:d1e0/128"]`, fmt.Sprintf("%q", spec.AllowedIPs)) }, ) ctest.AssertResource(suite, affiliate2.TypedSpec().KubeSpan.PublicKey, func(res *kubespan.PeerSpec, asrt *assert.Assertions) { spec := res.TypedSpec() asrt.Equal(`["10.244.2.0/24" "192.168.3.0/24" "fd50:8d60:4238:6302:f857:23ff:fe21:d1e1/128"]`, fmt.Sprintf("%q", spec.AllowedIPs)) }, ) } func TestPeerSpecSuite(t *testing.T) { t.Parallel() suite.Run(t, &PeerSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&kubespanctrl.PeerSpecController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" cfg "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // AddressConfigController manages network.AddressSpec based on machine configuration, kernel cmdline and some built-in defaults. type AddressConfigController struct { Cmdline *procfs.Cmdline V1Alpha1Mode runtime.Mode } // Name implements controller.Controller interface. func (ctrl *AddressConfigController) Name() string { return "network.AddressConfigController" } // Inputs implements controller.Controller interface. func (ctrl *AddressConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.DeviceConfigSpecType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *AddressConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.AddressSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *AddressConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() // apply defaults for the loopback interface if err := ctrl.apply(ctx, r, ctrl.loopbackDefaults()); err != nil { return fmt.Errorf("error generating loopback interface defaults: %w", err) } devices, err := safe.ReaderListAll[*network.DeviceConfigSpec](ctx, r) if err != nil { return fmt.Errorf("error getting config: %w", err) } ignoredInterfaces := map[string]struct{}{} for device := range devices.All() { if device.TypedSpec().Device.Ignore() { ignoredInterfaces[device.TypedSpec().Device.Interface()] = struct{}{} } } // parse kernel cmdline for the address cmdlineAddresses := ctrl.parseCmdline(logger) for _, cmdlineAddress := range cmdlineAddresses { if _, ignored := ignoredInterfaces[cmdlineAddress.LinkName]; !ignored { if err = ctrl.apply(ctx, r, []network.AddressSpecSpec{cmdlineAddress}); err != nil { return fmt.Errorf("error applying cmdline address: %w", err) } } } // parse machine configuration for static addresses (legacy first) if devices.Len() > 0 { addresses := ctrl.processDevicesConfiguration(logger, devices) if err = ctrl.apply(ctx, r, addresses); err != nil { return fmt.Errorf("error applying machine configuration address: %w", err) } } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error reading machine config: %w", err) } } if cfg != nil { if err = ctrl.apply(ctx, r, ctrl.processMachineConfig(cfg.Config().NetworkCommonLinkConfigs())); err != nil { return fmt.Errorf("error applying machine configuration addresses: %w", err) } } if err := r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.AddressSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error during cleanup: %w", err) } } } //nolint:dupl func (ctrl *AddressConfigController) apply(ctx context.Context, r controller.Runtime, addresses []network.AddressSpecSpec) error { for _, address := range addresses { id := network.LayeredID(address.ConfigLayer, network.AddressID(address.LinkName, address.Address)) if err := safe.WriterModify( ctx, r, network.NewAddressSpec(network.ConfigNamespaceName, id), func(r *network.AddressSpec) error { *r.TypedSpec() = address return nil }, ); err != nil { return err } } return nil } func (ctrl *AddressConfigController) loopbackDefaults() []network.AddressSpecSpec { if ctrl.V1Alpha1Mode == runtime.ModeContainer { // skip configuring lo addresses in container mode return nil } return []network.AddressSpecSpec{ { Address: netip.PrefixFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 8), Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeHost, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), LinkName: "lo", ConfigLayer: network.ConfigDefault, }, } } func (ctrl *AddressConfigController) parseCmdline(logger *zap.Logger) (addresses []network.AddressSpecSpec) { if ctrl.Cmdline == nil { return addresses } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, network.NewEmptyLinkResolver()) if err != nil { logger.Info("ignoring cmdline parse failure", zap.Error(err)) return addresses } for _, linkConfig := range settings.LinkConfigs { if value.IsZero(linkConfig.Address) { continue } var address network.AddressSpecSpec address.Address = linkConfig.Address if address.Address.Addr().Is6() { address.Family = nethelpers.FamilyInet6 } else { address.Family = nethelpers.FamilyInet4 } address.Scope = nethelpers.ScopeGlobal address.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent) address.ConfigLayer = network.ConfigCmdline address.LinkName = linkConfig.LinkName addresses = append(addresses, address) } return addresses } func parseIPOrIPPrefix(address string) (netip.Prefix, error) { if strings.IndexByte(address, '/') >= 0 { return netip.ParsePrefix(address) } // parse as IP address and assume netmask of all ones ip, err := netip.ParseAddr(address) if err != nil { return netip.Prefix{}, err } return netip.PrefixFrom(ip, ip.BitLen()), nil } func (ctrl *AddressConfigController) processDevicesConfiguration(logger *zap.Logger, devices safe.List[*network.DeviceConfigSpec]) (addresses []network.AddressSpecSpec) { for item := range devices.All() { device := item.TypedSpec().Device if device.Ignore() { continue } for _, cidr := range device.Addresses() { ipPrefix, err := parseIPOrIPPrefix(cidr) if err != nil { logger.Info(fmt.Sprintf("skipping address %q on interface %q", cidr, device.Interface()), zap.Error(err)) continue } address := network.AddressSpecSpec{ Address: ipPrefix, Scope: nethelpers.ScopeGlobal, LinkName: device.Interface(), ConfigLayer: network.ConfigMachineConfiguration, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } if address.Address.Addr().Is6() { address.Family = nethelpers.FamilyInet6 } else { address.Family = nethelpers.FamilyInet4 } addresses = append(addresses, address) } for _, vlan := range device.Vlans() { for _, cidr := range vlan.Addresses() { ipPrefix, err := netip.ParsePrefix(cidr) if err != nil { logger.Info(fmt.Sprintf("skipping address %q on interface %q vlan %d", cidr, device.Interface(), vlan.ID()), zap.Error(err)) continue } address := network.AddressSpecSpec{ Address: ipPrefix, Scope: nethelpers.ScopeGlobal, LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), ConfigLayer: network.ConfigMachineConfiguration, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } if address.Address.Addr().Is6() { address.Family = nethelpers.FamilyInet6 } else { address.Family = nethelpers.FamilyInet4 } addresses = append(addresses, address) } } } return addresses } func (ctrl *AddressConfigController) processMachineConfig(linkConfigs []cfg.NetworkCommonLinkConfig) (addresses []network.AddressSpecSpec) { for _, linkConfig := range linkConfigs { for _, addr := range linkConfig.Addresses() { address := network.AddressSpecSpec{ Address: addr.Address(), Scope: nethelpers.ScopeGlobal, LinkName: linkConfig.Name(), ConfigLayer: network.ConfigMachineConfiguration, Priority: addr.RoutePriority().ValueOrZero(), Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } if address.Address.Addr().Is6() { address.Family = nethelpers.FamilyInet6 } else { address.Family = nethelpers.FamilyInet4 } addresses = append(addresses, address) } } return addresses } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "cmp" "fmt" "net" "net/netip" "net/url" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type AddressConfigSuite struct { ctest.DefaultSuite } func (suite *AddressConfigSuite) TestLoopback() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressConfigController{})) ctest.AssertResource( suite, "default/lo/127.0.0.1/8", func(r *network.AddressSpec, asrt *assert.Assertions) { asrt.Equal("lo", r.TypedSpec().LinkName) asrt.Equal(nethelpers.ScopeHost, r.TypedSpec().Scope) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *AddressConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.AddressConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1::::: ip=eth3:dhcp ip=10.3.5.7::10.3.5.1:255.255.255.0::eth4"), }, ), ) ctest.AssertResources( suite, []string{ "cmdline/eth1/172.20.0.2/24", "cmdline/eth4/10.3.5.7/24", }, func(r *network.AddressSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "cmdline/eth1/172.20.0.2/24": asrt.Equal("eth1", r.TypedSpec().LinkName) case "cmdline/eth4/10.3.5.7/24": asrt.Equal("eth4", r.TypedSpec().LinkName) } }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *AddressConfigSuite) TestCmdlineNoNetmask() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.AddressConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1"), }, ), ) ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty slices.SortFunc(ifaces, func(a, b net.Interface) int { return cmp.Compare(a.Name, b.Name) }) ifaceName := "" for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 { continue } ifaceName = iface.Name break } suite.Assert().NotEmpty(ifaceName) ctest.AssertResource( suite, fmt.Sprintf("cmdline/%s/172.20.0.2/32", ifaceName), func(r *network.AddressSpec, asrt *assert.Assertions) { asrt.Equal(ifaceName, r.TypedSpec().LinkName) asrt.Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *AddressConfigSuite) TestMachineConfigurationLegacy() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressConfigController{})) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DeviceConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth3", DeviceCIDR: "192.168.0.24/28", }, { DeviceIgnore: new(true), DeviceInterface: "eth4", DeviceCIDR: "192.168.0.24/28", }, { DeviceInterface: "eth2", DeviceCIDR: "2001:470:6d:30e:8ed2:b60c:9d2f:803a/64", }, { DeviceInterface: "eth5", DeviceCIDR: "10.5.0.7", }, { DeviceInterface: "eth6", DeviceAddresses: []string{ "10.5.0.8", "2001:470:6d:30e:8ed2:b60c:9d2f:803b/64", }, }, { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 24, VlanCIDR: "10.0.0.1/8", }, { VlanID: 25, VlanAddresses: []string{ "11.0.0.1/8", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/eth2/2001:470:6d:30e:8ed2:b60c:9d2f:803a/64", "configuration/eth3/192.168.0.24/28", "configuration/eth5/10.5.0.7/32", "configuration/eth6/10.5.0.8/32", "configuration/eth6/2001:470:6d:30e:8ed2:b60c:9d2f:803b/64", "configuration/eth0.24/10.0.0.1/8", "configuration/eth0.25/11.0.0.1/8", }, func(r *network.AddressSpec, asrt *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *AddressConfigSuite) TestMachineConfiguration() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressConfigController{})) lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkAddresses = []networkcfg.AddressConfig{ { AddressAddress: netip.MustParsePrefix("10.12.3.4/24"), }, } lc2 := networkcfg.NewLinkConfigV1Alpha1("enp0s3") lc2.LinkAddresses = []networkcfg.AddressConfig{ { AddressAddress: netip.MustParsePrefix("172.20.0.1/20"), AddressPriority: new(uint32(100)), }, } ctr, err := container.New(lc1, lc2) suite.Require().NoError(err) suite.Create(config.NewMachineConfig(ctr)) ctest.AssertResources( suite, []string{ "configuration/enp0s2/10.12.3.4/24", "configuration/enp0s3/172.20.0.1/20", }, func(r *network.AddressSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) if r.Metadata().ID() == "configuration/enp0s3/172.20.0.1/20" { asrt.Equal(uint32(100), r.TypedSpec().Priority) } }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func TestAddressConfigSuite(t *testing.T) { suite.Run(t, &AddressConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_event.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // AddressEventController reports aggregated enpoints state from hostname statuses and k8s endpoints // to the events stream. type AddressEventController struct { V1Alpha1Events runtime.Publisher } // Name implements controller.Controller interface. func (ctrl *AddressEventController) Name() string { return "network.AddressEventController" } // Inputs implements controller.Controller interface. func (ctrl *AddressEventController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.NodeAddressType, Kind: controller.InputWeak, ID: optional.Some(network.FilteredNodeAddressID( network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s)), }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, Kind: controller.InputWeak, ID: optional.Some(network.HostnameID), }, } } // Outputs implements controller.Controller interface. func (ctrl *AddressEventController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *AddressEventController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { ticker := time.NewTicker(time.Minute * 10) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-ticker.C: case <-r.EventCh(): } var addresses []string nodeAddr, err := safe.ReaderGet[*network.NodeAddress]( ctx, r, resource.NewMetadata( network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID( network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined), ) if err != nil { if !state.IsNotFoundError(err) { return err } } else { for _, addr := range nodeAddr.TypedSpec().Addresses { addresses = append( addresses, addr.Addr().String(), ) } } var hostname string hostnameStatus, err := safe.ReaderGet[*network.HostnameStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return err } } else { hostname = hostnameStatus.TypedSpec().Hostname } ctrl.V1Alpha1Events.Publish(ctx, &machine.AddressEvent{ Hostname: hostname, Addresses: addresses, }) r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_event_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "context" "errors" "net/netip" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" networkresource "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type mockEventsStream struct { messagesMu sync.Mutex messages []proto.Message } func (s *mockEventsStream) Publish(_ context.Context, m proto.Message) { s.messagesMu.Lock() defer s.messagesMu.Unlock() s.messages = append(s.messages, m) } type AddressEventsSuite struct { suite.Suite events *mockEventsStream state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *AddressEventsSuite) SetupTest() { suite.events = &mockEventsStream{ messages: []proto.Message{}, } suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError( suite.runtime.RegisterController( &network.AddressEventController{ V1Alpha1Events: suite.events, }, ), ) suite.startRuntime() } func (suite *AddressEventsSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *AddressEventsSuite) TestReconcile() { hostname := networkresource.NewHostnameStatus(networkresource.NamespaceName, networkresource.HostnameID) hostname.TypedSpec().Hostname = "localhost" suite.Require().NoError(suite.state.Create(suite.ctx, hostname)) var event *machine.AddressEvent suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { suite.events.messagesMu.Lock() defer suite.events.messagesMu.Unlock() if len(suite.events.messages) == 0 { return retry.ExpectedErrorf("no events created") } m := suite.events.messages[len(suite.events.messages)-1] var ok bool event, ok = m.(*machine.AddressEvent) if !ok { return errors.New("not an endpoint event") } if event.Hostname == "" { return retry.ExpectedErrorf("expected hostname to be set") } return nil }, ), ) suite.Require().Equal(hostname.TypedSpec().Hostname, event.Hostname) suite.Require().Empty(event.Addresses) nodeAddress := networkresource.NewNodeAddress( networkresource.NamespaceName, networkresource.FilteredNodeAddressID( networkresource.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s, ), ) addrs := []string{ "10.5.0.2", "127.0.0.2", } nodeAddress.TypedSpec().Addresses = append( nodeAddress.TypedSpec().Addresses, netip.PrefixFrom(netip.MustParseAddr(addrs[0]), 32), netip.PrefixFrom(netip.MustParseAddr(addrs[1]), 32), ) suite.Require().NoError(suite.state.Create(suite.ctx, nodeAddress)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { suite.events.messagesMu.Lock() defer suite.events.messagesMu.Unlock() if len(suite.events.messages) == 0 { return retry.ExpectedErrorf("no events created") } m := suite.events.messages[len(suite.events.messages)-1] var ok bool event, ok = m.(*machine.AddressEvent) if !ok { return errors.New("not an address event") } if len(event.Addresses) == 0 { return retry.ExpectedErrorf("expected addresses to be set") } return nil }, ), ) suite.Require().Equal(addrs, event.Addresses) } func (suite *AddressEventsSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestAddressEventsSuite(t *testing.T) { suite.Run(t, new(AddressEventsSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewAddressMergeController initializes a AddressMergeController. // // AddressMergeController merges network.AddressSpec in network.ConfigNamespace and produces final network.AddressSpec in network.Namespace. func NewAddressMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.AddressSpec]) map[resource.ID]*network.AddressSpecSpec { // address is allowed as long as it's not duplicate, for duplicate higher layer takes precedence addresses := map[resource.ID]*network.AddressSpecSpec{} for address := range list.All() { id := network.AddressID(address.TypedSpec().LinkName, address.TypedSpec().Address) existing, ok := addresses[id] if ok && existing.ConfigLayer > address.TypedSpec().ConfigLayer { // skip this address, as existing one is higher layer continue } addresses[id] = address.TypedSpec() } return addresses }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type AddressMergeSuite struct { ctest.DefaultSuite } func (suite *AddressMergeSuite) assertAddresses(requiredIDs []string, check func(*network.AddressSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *AddressMergeSuite) assertNoAddress(id string) { ctest.AssertNoResource[*network.AddressSpec](suite, id) } func (suite *AddressMergeSuite) TestMerge() { loopback := network.NewAddressSpec(network.ConfigNamespaceName, "default/lo/127.0.0.1/8") *loopback.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("127.0.0.1/8"), LinkName: "lo", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeHost, ConfigLayer: network.ConfigDefault, } dhcp := network.NewAddressSpec(network.ConfigNamespaceName, "dhcp/eth0/10.0.0.1/8") *dhcp.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.1/8"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, ConfigLayer: network.ConfigOperator, } static := network.NewAddressSpec(network.ConfigNamespaceName, "configuration/eth0/10.0.0.35/32") *static.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.35/32"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, ConfigLayer: network.ConfigMachineConfiguration, } override := network.NewAddressSpec(network.ConfigNamespaceName, "configuration/eth0/10.0.0.1/8") *override.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.1/8"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeHost, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{loopback, dhcp, static, override} { suite.Create(res) } suite.assertAddresses( []string{ "lo/127.0.0.1/8", "eth0/10.0.0.1/8", "eth0/10.0.0.35/32", }, func(r *network.AddressSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "lo/127.0.0.1/8": asrt.Equal(*loopback.TypedSpec(), *r.TypedSpec()) case "eth0/10.0.0.1/8": asrt.Equal(*override.TypedSpec(), *r.TypedSpec()) case "eth0/10.0.0.35/32": asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) } }, ) suite.Destroy(static) suite.assertAddresses( []string{ "lo/127.0.0.1/8", "eth0/10.0.0.1/8", }, func(*network.AddressSpec, *assert.Assertions) {}, ) suite.assertNoAddress("eth0/10.0.0.35/32") } func (suite *AddressMergeSuite) TestMergeFlapping() { // simulate two conflicting address definitions which are getting removed/added constantly dhcp := network.NewAddressSpec(network.ConfigNamespaceName, "dhcp/eth0/10.0.0.1/8") *dhcp.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.1/8"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, ConfigLayer: network.ConfigOperator, } override := network.NewAddressSpec(network.ConfigNamespaceName, "configuration/eth0/10.0.0.1/8") *override.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.1/8"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeHost, ConfigLayer: network.ConfigMachineConfiguration, } testMergeFlapping(&suite.DefaultSuite, []*network.AddressSpec{dhcp, override}, "eth0/10.0.0.1/8", override) } func TestAddressMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &AddressMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewAddressMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "net" "net/netip" "os" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/jsimonetti/rtnetlink/v2" "github.com/mdlayher/arp" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // AddressSpecController applies network.AddressSpec to the actual interfaces. type AddressSpecController struct{} // Name implements controller.Controller interface. func (ctrl *AddressSpecController) Name() string { return "network.AddressSpecController" } // Inputs implements controller.Controller interface. func (ctrl *AddressSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.AddressSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *AddressSpecController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *AddressSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // watch link changes as some address might need to be re-applied if the link appears watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_LINK) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source network configuration resources list, err := safe.ReaderList[*network.AddressSpec](ctx, r, resource.NewMetadata(network.NamespaceName, network.AddressSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing source network addresses: %w", err) } // add finalizers for all live resources for res := range list.All() { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // list rtnetlink links (interfaces) links, err := conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } // list rtnetlink addresses addrs, err := conn.Address.List() if err != nil { return fmt.Errorf("error listing addresses: %w", err) } // loop over addresses and make reconcile decision for address := range list.All() { if err = ctrl.syncAddress(ctx, r, logger, conn, links, addrs, address); err != nil { return err } } r.ResetRestartBackoff() } } func resolveLinkName(links []rtnetlink.LinkMessage, linkName string) uint32 { if linkName == "" { return 0 // should never match } // first, lookup by name for _, link := range links { if link.Attributes.Name == linkName { return link.Index } } // then, lookup by alias/altname for _, link := range links { if pointer.SafeDeref(link.Attributes.Alias) == linkName { return link.Index } if slices.Index(link.Attributes.AltNames, linkName) != -1 { return link.Index } } return 0 } func findAddress(addrs []rtnetlink.AddressMessage, linkIndex uint32, ipPrefix netip.Prefix) *rtnetlink.AddressMessage { for i, addr := range addrs { if addr.Index != linkIndex { continue } if int(addr.PrefixLength) != ipPrefix.Bits() { continue } if !addr.Attributes.Address.Equal(ipPrefix.Addr().AsSlice()) { continue } return &addrs[i] } return nil } //nolint:gocyclo func (ctrl *AddressSpecController) syncAddress(ctx context.Context, r controller.Runtime, logger *zap.Logger, conn *rtnetlink.Conn, links []rtnetlink.LinkMessage, addrs []rtnetlink.AddressMessage, address *network.AddressSpec, ) error { linkIndex := resolveLinkName(links, address.TypedSpec().LinkName) switch address.Metadata().Phase() { case resource.PhaseTearingDown: if linkIndex == 0 { // address should be deleted, but link is gone, so assume address is gone if err := r.RemoveFinalizer(ctx, address.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } return nil } if existing := findAddress(addrs, linkIndex, address.TypedSpec().Address); existing != nil { // delete address if err := conn.Address.Delete(existing); err != nil { return fmt.Errorf("error removing address: %w", err) } logger.Sugar().Infof("removed address %s from %q", address.TypedSpec().Address, address.TypedSpec().LinkName) } // now remove finalizer as address was deleted if err := r.RemoveFinalizer(ctx, address.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: if linkIndex == 0 { // address can't be assigned as link doesn't exist (yet), skip it return nil } if existing := findAddress(addrs, linkIndex, address.TypedSpec().Address); existing != nil { // clear out tentative flag, it is set by the kernel, we shouldn't try to enforce it existing.Flags &= ^uint8(nethelpers.AddressTentative) existing.Attributes.Flags &= ^uint32(nethelpers.AddressTentative) // check if existing matches the spec: if it does, skip update if existing.Scope == uint8(address.TypedSpec().Scope) && existing.Flags == uint8(address.TypedSpec().Flags) && existing.Attributes.Flags == uint32(address.TypedSpec().Flags) && existing.Attributes.Priority == address.TypedSpec().Priority { return nil } logger.Debug("replacing address", zap.Stringer("address", address.TypedSpec().Address), zap.String("link", address.TypedSpec().LinkName), zap.Stringer("old_scope", nethelpers.Scope(existing.Scope)), zap.Stringer("new_scope", address.TypedSpec().Scope), zap.Stringer("old_flags", nethelpers.AddressFlags(existing.Attributes.Flags)), zap.Stringer("new_flags", address.TypedSpec().Flags), zap.Uint32("old_priority", existing.Attributes.Priority), zap.Uint32("new_priority", address.TypedSpec().Priority), ) // delete address to get new one assigned below if err := conn.Address.Delete(existing); err != nil { return fmt.Errorf("error removing address: %w", err) } logger.Info("removed address", zap.Stringer("address", address.TypedSpec().Address), zap.String("link", address.TypedSpec().LinkName)) } // add address if err := conn.Address.New(&rtnetlink.AddressMessage{ Family: uint8(address.TypedSpec().Family), PrefixLength: uint8(address.TypedSpec().Address.Bits()), Flags: uint8(address.TypedSpec().Flags), Scope: uint8(address.TypedSpec().Scope), Index: linkIndex, Attributes: &rtnetlink.AddressAttributes{ Address: address.TypedSpec().Address.Addr().AsSlice(), Local: address.TypedSpec().Address.Addr().AsSlice(), Broadcast: addressutil.BroadcastAddr(address.TypedSpec().Address), Flags: uint32(address.TypedSpec().Flags), Priority: address.TypedSpec().Priority, }, }); err != nil { // ignore EEXIST error if !errors.Is(err, os.ErrExist) { return fmt.Errorf("error adding address %s to %q: %w", address.TypedSpec().Address, address.TypedSpec().LinkName, err) } } logger.Info("assigned address", zap.Stringer("address", address.TypedSpec().Address), zap.String("link", address.TypedSpec().LinkName)) if address.TypedSpec().AnnounceWithARP { if err := ctrl.gratuitousARP(logger, linkIndex, address.TypedSpec().Address.Addr()); err != nil { logger.Warn("failure sending gratuitous ARP", zap.Stringer("address", address.TypedSpec().Address), zap.String("link", address.TypedSpec().LinkName), zap.Error(err)) } } } return nil } func (ctrl *AddressSpecController) gratuitousARP(logger *zap.Logger, linkIndex uint32, ip netip.Addr) error { etherBroadcast := net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} if !ip.Is4() { return nil } iface, err := net.InterfaceByIndex(int(linkIndex)) if err != nil { return err } if len(iface.HardwareAddr) != 6 { // not ethernet return nil } cli, err := arp.Dial(iface) if err != nil { return fmt.Errorf("error creating arp client: %w", err) } defer cli.Close() //nolint:errcheck packet, err := arp.NewPacket(arp.OperationRequest, cli.HardwareAddr(), ip, cli.HardwareAddr(), ip) if err != nil { return fmt.Errorf("error building packet: %w", err) } if err = cli.WriteTo(packet, etherBroadcast); err != nil { return fmt.Errorf("error sending gratuitous ARP: %w", err) } logger.Info("sent gratuitous ARP", zap.Stringer("address", ip), zap.String("link", iface.Name)) return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "fmt" "math/rand/v2" "net" "net/netip" "os" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/jsimonetti/rtnetlink/v2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type AddressSpecSuite struct { ctest.DefaultSuite } func (suite *AddressSpecSuite) uniqueDummyInterface() string { return fmt.Sprintf("dummy%02x%02x%02x", rand.Int32()&0xff, rand.Int32()&0xff, rand.Int32()&0xff) } func assertLinkAddress(asrt *assert.Assertions, linkName, address string) { addr := netip.MustParsePrefix(address) iface, err := net.InterfaceByName(linkName) asrt.NoError(err) conn, err := rtnetlink.Dial(nil) asrt.NoError(err) defer conn.Close() //nolint:errcheck linkAddresses, err := conn.Address.List() asrt.NoError(err) for _, linkAddress := range linkAddresses { if linkAddress.Index != uint32(iface.Index) { continue } if int(linkAddress.PrefixLength) != addr.Bits() { continue } if !linkAddress.Attributes.Address.Equal(addr.Addr().AsSlice()) { continue } return } asrt.Failf("address not found", "address %s not found on %q", addr, linkName) } func assertNoLinkAddress(asrt *assert.Assertions, linkName, address string) { addr := netip.MustParsePrefix(address) iface, err := net.InterfaceByName(linkName) asrt.NoError(err) conn, err := rtnetlink.Dial(nil) asrt.NoError(err) defer conn.Close() //nolint:errcheck linkAddresses, err := conn.Address.List() asrt.NoError(err) for _, linkAddress := range linkAddresses { if linkAddress.Index == uint32(iface.Index) && int(linkAddress.PrefixLength) == addr.Bits() && linkAddress.Attributes.Address.Equal(addr.Addr().AsSlice()) { asrt.Failf("address is still there", "address %s is assigned to %q", addr, linkName) } } } func (suite *AddressSpecSuite) TestLoopback() { loopback := network.NewAddressSpec(network.NamespaceName, "lo/127.0.0.1/8") *loopback.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("127.11.0.1/32"), LinkName: "lo", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeHost, ConfigLayer: network.ConfigDefault, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } for _, res := range []resource.Resource{loopback} { suite.Create(res) } suite.Assert().EventuallyWithT(func(collect *assert.CollectT) { assertLinkAddress(assert.New(collect), "lo", "127.11.0.1/32") }, 3*time.Second, 10*time.Millisecond) // teardown the address _, err := suite.State().Teardown(suite.Ctx(), loopback.Metadata()) suite.Require().NoError(err) _, err = suite.State().WatchFor(suite.Ctx(), loopback.Metadata(), state.WithFinalizerEmpty()) suite.Require().NoError(err) // torn down address should be removed immediately suite.Assert().EventuallyWithT(func(collect *assert.CollectT) { assertNoLinkAddress(assert.New(collect), "lo", "127.11.0.1/32") }, 3*time.Second, 10*time.Millisecond) suite.Destroy(loopback) } func (suite *AddressSpecSuite) TestDummy() { dummyInterface := suite.uniqueDummyInterface() conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck dummy := network.NewAddressSpec(network.NamespaceName, "dummy/10.0.0.1/8") *dummy.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.1/8"), LinkName: dummyInterface, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, ConfigLayer: network.ConfigDefault, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } // it's fine to create the address before the interface is actually created for _, res := range []resource.Resource{dummy} { suite.Create(res) } // create dummy interface suite.Require().NoError( conn.Link.New( &rtnetlink.LinkMessage{ Type: unix.ARPHRD_ETHER, Attributes: &rtnetlink.LinkAttributes{ Name: dummyInterface, MTU: 1400, Info: &rtnetlink.LinkInfo{ Kind: "dummy", }, }, }, ), ) iface, err := net.InterfaceByName(dummyInterface) suite.Require().NoError(err) defer conn.Link.Delete(uint32(iface.Index)) //nolint:errcheck suite.Assert().EventuallyWithT(func(collect *assert.CollectT) { assertLinkAddress(assert.New(collect), dummyInterface, "10.0.0.1/8") }, 3*time.Second, 10*time.Millisecond) // delete dummy interface, address should be unassigned automatically suite.Require().NoError(conn.Link.Delete(uint32(iface.Index))) // teardown the address _, err = suite.State().Teardown(suite.Ctx(), dummy.Metadata()) suite.Require().NoError(err) _, err = suite.State().WatchFor(suite.Ctx(), dummy.Metadata(), state.WithFinalizerEmpty()) suite.Require().NoError(err) suite.Destroy(dummy) } func (suite *AddressSpecSuite) TestDummyAlias() { dummyInterface := suite.uniqueDummyInterface() dummyAlias := suite.uniqueDummyInterface() suite.T().Logf("dummyInterface: %s, dummyAlias: %s", dummyInterface, dummyAlias) conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck dummy := network.NewAddressSpec(network.NamespaceName, "dummy/10.0.0.5/8") *dummy.TypedSpec() = network.AddressSpecSpec{ Address: netip.MustParsePrefix("10.0.0.5/8"), LinkName: dummyAlias, // use alias name instead of the actual interface name Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, ConfigLayer: network.ConfigDefault, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), } // it's fine to create the address before the interface is actually created for _, res := range []resource.Resource{dummy} { suite.Create(res) } // create dummy interface suite.Require().NoError( conn.Link.New( &rtnetlink.LinkMessage{ Type: unix.ARPHRD_ETHER, Attributes: &rtnetlink.LinkAttributes{ Name: dummyInterface, MTU: 1400, Info: &rtnetlink.LinkInfo{ Kind: "dummy", }, }, }, ), ) iface, err := net.InterfaceByName(dummyInterface) suite.Require().NoError(err) // set alias name suite.Require().NoError( conn.Link.Set( &rtnetlink.LinkMessage{ Index: uint32(iface.Index), Attributes: &rtnetlink.LinkAttributes{ Alias: &dummyAlias, }, }, ), ) defer conn.Link.Delete(uint32(iface.Index)) //nolint:errcheck suite.Assert().EventuallyWithT(func(collect *assert.CollectT) { assertLinkAddress(assert.New(collect), dummyInterface, "10.0.0.5/8") }, 3*time.Second, 10*time.Millisecond) } func TestAddressSpecSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("requires root") } suite.Run(t, &AddressSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressSpecController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/jsimonetti/rtnetlink/v2" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // AddressStatusController manages secrets.Etcd based on configuration. type AddressStatusController struct{} // Name implements controller.Controller interface. func (ctrl *AddressStatusController) Name() string { return "network.AddressStatusController" } // Inputs implements controller.Controller interface. func (ctrl *AddressStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *AddressStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: network.AddressStatusType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *AddressStatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_LINK|unix.RTMGRP_IPV4_IFADDR|unix.RTMGRP_IPV6_IFADDR) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() // build links lookup table links, err := conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } linkLookup := make(map[uint32]string, len(links)) for _, link := range links { linkLookup[link.Index] = link.Attributes.Name } addrs, err := conn.Address.List() if err != nil { return fmt.Errorf("error listing addresses: %w", err) } for _, addr := range addrs { // TODO: should we use local address actually? // from if_addr.h: // IFA_ADDRESS is prefix address, rather than local interface address. // * It makes no difference for normally configured broadcast interfaces, // * but for point-to-point IFA_ADDRESS is DESTINATION address, // * local address is supplied in IFA_LOCAL attribute. ipAddr, _ := netip.AddrFromSlice(addr.Attributes.Address) ipPrefix := netip.PrefixFrom(ipAddr, int(addr.PrefixLength)) id := network.AddressID(linkLookup[addr.Index], ipPrefix) if err = safe.WriterModify(ctx, r, network.NewAddressStatus(network.NamespaceName, id), func(r *network.AddressStatus) error { status := r.TypedSpec() status.Address = ipPrefix status.Local, _ = netip.AddrFromSlice(addr.Attributes.Local) status.Broadcast, _ = netip.AddrFromSlice(addr.Attributes.Broadcast) status.Anycast, _ = netip.AddrFromSlice(addr.Attributes.Anycast) status.Multicast, _ = netip.AddrFromSlice(addr.Attributes.Multicast) status.LinkIndex = addr.Index status.LinkName = linkLookup[addr.Index] status.Family = nethelpers.Family(addr.Family) status.Scope = nethelpers.Scope(addr.Scope) status.Flags = nethelpers.AddressFlags(addr.Attributes.Flags) status.Priority = addr.Attributes.Priority return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } } if err := safe.CleanupOutputs[*network.AddressStatus](ctx, r); err != nil { return fmt.Errorf("error doing cleanup: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/network/address_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type AddressStatusSuite struct { ctest.DefaultSuite } func (suite *AddressStatusSuite) TestLoopback() { ctest.AssertResource(suite, "lo/127.0.0.1/8", func(r *network.AddressStatus, asrt *assert.Assertions) {}) } func TestAddressStatusSuite(t *testing.T) { t.Parallel() suite.Run(t, &AddressStatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressStatusController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/cmdline.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "errors" "fmt" "net" "net/netip" "slices" "strconv" "strings" "github.com/siderolabs/gen/pair/ordered" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // CmdlineNetworking contains parsed cmdline networking settings. type CmdlineNetworking struct { LinkConfigs []CmdlineLinkConfig Hostname string DNSAddresses []netip.Addr NTPAddresses []netip.Addr IgnoreInterfaces []string NetworkLinkSpecs []network.LinkSpecSpec } // CmdlineLinkConfig contains parsed cmdline networking settings for a single link. type CmdlineLinkConfig struct { LinkName string Address netip.Prefix Gateway netip.Addr DHCP bool } // splitIPArgument splits the `ip=` kernel argument honoring the IPv6 addresses in square brackets. func splitIPArgument(val string) []string { var ( squared, prev int parts []string ) for i, c := range val { switch c { case '[': squared++ case ']': squared-- case ':': if squared != 0 { continue } parts = append(parts, strings.Trim(val[prev:i], "[]")) prev = i + 1 } } parts = append(parts, strings.Trim(val[prev:], "[]")) return parts } const autoconfDHCP = "dhcp" // ParseCmdlineNetwork parses `ip=` and Talos specific kernel cmdline argument producing all the available configuration options. // //nolint:gocyclo,cyclop func ParseCmdlineNetwork(cmdline *procfs.Cmdline, linkNameResolver *network.LinkResolver) (CmdlineNetworking, error) { var ( settings CmdlineNetworking err error linkSpecSpecs []network.LinkSpecSpec ) // process Talos specific kernel params cmdlineHostname := cmdline.Get(constants.KernelParamHostname).First() if cmdlineHostname != nil { settings.Hostname = *cmdlineHostname } ignoreInterfaces := cmdline.Get(constants.KernelParamNetworkInterfaceIgnore) for i := 0; ignoreInterfaces.Get(i) != nil; i++ { settings.IgnoreInterfaces = append(settings.IgnoreInterfaces, linkNameResolver.Resolve(*ignoreInterfaces.Get(i))) } // standard ip= ipSettings := cmdline.Get("ip") for idx := 0; ipSettings.Get(idx) != nil; idx++ { // https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt // https://man7.org/linux/man-pages/man7/dracut.cmdline.7.html // // supported formats: // ip=::::::::: // ip=dhcp (ignored) // ip=:dhcp fields := splitIPArgument(*ipSettings.Get(idx)) switch { case len(fields) == 1 && fields[0] == autoconfDHCP: // ignore case len(fields) == 2 && fields[1] == autoconfDHCP: // ip=:dhcp linkConfig := CmdlineLinkConfig{ LinkName: linkNameResolver.Resolve(fields[0]), DHCP: true, } linkSpecSpecs = append(linkSpecSpecs, network.LinkSpecSpec{ Name: linkConfig.LinkName, Up: true, ConfigLayer: network.ConfigCmdline, }) settings.LinkConfigs = append(settings.LinkConfigs, linkConfig) default: linkConfig := CmdlineLinkConfig{} for i := range fields { if fields[i] == "" { continue } switch i { case 0: var ip netip.Addr ip, err = netip.ParseAddr(fields[0]) if err != nil { return settings, fmt.Errorf("cmdline address parse failure: %s", err) } // default is to have complete address masked linkConfig.Address = netip.PrefixFrom(ip, ip.BitLen()) case 2: linkConfig.Gateway, err = netip.ParseAddr(fields[2]) if err != nil { return settings, fmt.Errorf("cmdline gateway parse failure: %s", err) } case 3: var ( netmask netip.Addr ones int ) ones, err = strconv.Atoi(fields[3]) if err != nil { netmask, err = netip.ParseAddr(fields[3]) if err != nil { return settings, fmt.Errorf("cmdline netmask parse failure: %s", err) } ones, _ = net.IPMask(netmask.AsSlice()).Size() } linkConfig.Address = netip.PrefixFrom(linkConfig.Address.Addr(), ones) case 4: if settings.Hostname == "" { settings.Hostname = fields[4] } case 5: linkConfig.LinkName = linkNameResolver.Resolve(fields[5]) case 6: if fields[6] == autoconfDHCP { linkConfig.DHCP = true } case 7, 8: var dnsIP netip.Addr dnsIP, err = netip.ParseAddr(fields[i]) if err != nil { return settings, fmt.Errorf("error parsing DNS IP: %w", err) } settings.DNSAddresses = append(settings.DNSAddresses, dnsIP) case 9: var ntpIP netip.Addr ntpIP, err = netip.ParseAddr(fields[i]) if err != nil { return settings, fmt.Errorf("error parsing NTP IP: %w", err) } settings.NTPAddresses = append(settings.NTPAddresses, ntpIP) } } // if interface name is not set, pick the first non-loopback interface if linkConfig.LinkName == "" { ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty slices.SortFunc(ifaces, func(a, b net.Interface) int { return cmp.Compare(a.Name, b.Name) }) for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 { continue } linkConfig.LinkName = iface.Name break } } linkSpecSpecs = append(linkSpecSpecs, network.LinkSpecSpec{ Name: linkConfig.LinkName, Up: true, ConfigLayer: network.ConfigCmdline, }) settings.LinkConfigs = append(settings.LinkConfigs, linkConfig) } } // dracut bond= // ref: https://man7.org/linux/man-pages/man7/dracut.cmdline.7.html bondSettings := cmdline.Get(constants.KernelParamBonding).First() if bondSettings != nil { var ( bondName, bondMTU string bondSlaves []string bondOptions v1alpha1.Bond ) // bond=[::[:[:]]] fields := strings.Split(*bondSettings, ":") for i := range fields { if fields[i] == "" { continue } switch i { case 0: bondName = fields[0] case 1: bondSlaves = strings.Split(fields[1], ",") case 2: bondOptions, err = parseBondOptions(fields[2]) if err != nil { return settings, err } case 3: bondMTU = fields[3] } } // set defaults as per https://man7.org/linux/man-pages/man7/dracut.cmdline.7.html // Talos by default sets bond mode to balance-rr if bondSlaves == nil { bondSlaves = []string{ "eth0", "eth1", } } // resolve bond slave names via aliases as needed bondSlaves = xslices.Map(bondSlaves, linkNameResolver.Resolve) bondLinkSpec := network.LinkSpecSpec{ Name: bondName, Up: true, ConfigLayer: network.ConfigCmdline, } if bondMTU != "" { mtu, err := strconv.Atoi(bondMTU) if err != nil { return settings, fmt.Errorf("error parsing bond MTU: %w", err) } bondLinkSpec.MTU = uint32(mtu) } if err := SetBondMasterLegacy(&bondLinkSpec, &bondOptions); err != nil { return settings, fmt.Errorf("error setting bond master: %w", err) } linkSpecSpecs = append(linkSpecSpecs, bondLinkSpec) for idx, slave := range bondSlaves { slaveLinkSpec := network.LinkSpecSpec{ Name: slave, Up: true, ConfigLayer: network.ConfigCmdline, } SetBondSlave(&slaveLinkSpec, ordered.MakePair(bondName, idx)) linkSpecSpecs = append(linkSpecSpecs, slaveLinkSpec) } } // dracut vlan=: vlanSettings := cmdline.Get(constants.KernelParamVlan).First() if vlanSettings != nil { vlanName, phyDevice, ok := strings.Cut(*vlanSettings, ":") if !ok { return settings, fmt.Errorf("malformed vlan commandline argument: %s", *vlanSettings) } _, vlanNumberString, ok := strings.Cut(vlanName, ".") if !ok { vlanNumberString, ok = strings.CutPrefix(vlanName, "vlan") if !ok { return settings, fmt.Errorf("malformed vlan commandline argument: %s", *vlanSettings) } } vlanID, err := strconv.Atoi(vlanNumberString) if vlanID < 1 || 4095 < vlanID { return settings, fmt.Errorf("invalid vlanID=%d, must be in the range 1..4095: %s", vlanID, *vlanSettings) } if err != nil || vlanNumberString == "" { return settings, errors.New("unable to parse vlan") } vlanSpec := network.VLANSpec{ VID: uint16(vlanID), Protocol: nethelpers.VLANProtocol8021Q, } vlanName = nethelpers.VLANLinkName(phyDevice, uint16(vlanID)) phyDevice = linkNameResolver.Resolve(phyDevice) linkSpecUpdated := false for i, linkSpec := range linkSpecSpecs { if linkSpec.Name == vlanName { vlanLink(&linkSpecSpecs[i], vlanName, phyDevice, vlanSpec) linkSpecUpdated = true break } } if !linkSpecUpdated { linkSpec := network.LinkSpecSpec{ Name: vlanName, Up: true, ConfigLayer: network.ConfigCmdline, } vlanLink(&linkSpec, vlanName, phyDevice, vlanSpec) linkSpecSpecs = append(linkSpecSpecs, linkSpec) } } settings.NetworkLinkSpecs = linkSpecSpecs return settings, nil } // parseBondOptions parses the options string into v1alpha1.Bond // v1alpha1.Bond was chosen to re-use the `SetBondMaster` and `SetBondSlave` functions // ref: modinfo bonding // //nolint:gocyclo,cyclop,dupword func parseBondOptions(options string) (v1alpha1.Bond, error) { var bond v1alpha1.Bond for opt := range strings.SplitSeq(options, ",") { optionPair := strings.Split(opt, "=") switch optionPair[0] { case "arp_ip_target": bond.BondARPIPTarget = strings.Split(optionPair[1], ";") case "mode": bond.BondMode = optionPair[1] case "xmit_hash_policy": bond.BondHashPolicy = optionPair[1] case "lacp_rate": bond.BondLACPRate = optionPair[1] case "arp_validate": bond.BondARPValidate = optionPair[1] case "arp_all_targets": bond.BondARPAllTargets = optionPair[1] case "primary": bond.BondPrimary = optionPair[1] case "primary_reselect": bond.BondPrimaryReselect = optionPair[1] case "fail_over_mac": bond.BondFailOverMac = optionPair[1] case "ad_select": bond.BondADSelect = optionPair[1] case "miimon": miimon, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option miimon: %w", err) } bond.BondMIIMon = uint32(miimon) case "updelay": updelay, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option updelay: %w", err) } bond.BondUpDelay = uint32(updelay) case "downdelay": downdelay, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option downdelay: %w", err) } bond.BondDownDelay = uint32(downdelay) case "arp_interval": arpInterval, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option arp_interval: %w", err) } bond.BondARPInterval = uint32(arpInterval) case "resend_igmp": resendIGMP, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option resend_igmp: %w", err) } bond.BondResendIGMP = uint32(resendIGMP) case "min_links": minLinks, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option min_links: %w", err) } bond.BondMinLinks = uint32(minLinks) case "lp_interval": lpInterval, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option lp_interval: %w", err) } bond.BondLPInterval = uint32(lpInterval) case "packets_per_slave": packetsPerSlave, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option packets_per_slave: %w", err) } bond.BondPacketsPerSlave = uint32(packetsPerSlave) case "num_grat_arp": numGratArp, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option num_grat_arp: %w", err) } bond.BondNumPeerNotif = uint8(numGratArp) case "num_unsol_na": numGratArp, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option num_unsol_na: %w", err) } bond.BondNumPeerNotif = uint8(numGratArp) case "all_slaves_active": allSlavesActive, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option all_slaves_active: %w", err) } bond.BondAllSlavesActive = uint8(allSlavesActive) case "use_carrier": useCarrier, err := strconv.Atoi(optionPair[1]) if err != nil { return bond, fmt.Errorf("error parsing bond option use_carrier: %w", err) } if useCarrier == 1 { bond.BondUseCarrier = new(true) } default: return bond, fmt.Errorf("unknown bond option: %s", optionPair[0]) } } return bond, nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/cmdline_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "cmp" "fmt" "iter" "net" "net/netip" "slices" "testing" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" netconfig "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestCmdlineParse(t *testing.T) { // [NOTE]: this test is not safe to run in parallel, as defaultIfaceName might flip if some interface is created concurrently ifaces, _ := net.Interfaces() //nolint:errcheck // ignoring error here as ifaces will be empty slices.SortFunc(ifaces, func(a, b net.Interface) int { return cmp.Compare(a.Name, b.Name) }) defaultIfaceName := "" for _, iface := range ifaces { if iface.Flags&net.FlagLoopback != 0 { continue } defaultIfaceName = iface.Name break } defaultBondSettings := network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "bond1", Kind: "bond", Type: nethelpers.LinkEther, Logical: true, Up: true, ConfigLayer: netconfig.ConfigCmdline, BondMaster: netconfig.BondMasterSpec{ Mode: nethelpers.BondModeRoundrobin, ResendIGMP: 1, LPInterval: 1, PacketsPerSlave: 1, NumPeerNotif: 1, TLBDynamicLB: 1, UseCarrier: true, PrimaryIndex: new(uint32(0)), ADLACPActive: nethelpers.ADLACPActiveOn, MissedMax: 2, }, }, { Name: "eth0", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 0, }, }, { Name: "eth1", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 1, }, }, }, } for _, test := range []struct { name string cmdline string expectedSettings network.CmdlineNetworking expectedError string }{ { name: "zero", cmdline: "", }, { name: "static IP", cmdline: "ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1:::::", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/24"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: "eth1", }, }, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "no iface", cmdline: "ip=172.20.0.2::172.20.0.1", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/32"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: defaultIfaceName, }, }, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: defaultIfaceName, Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "no iface by mac address", cmdline: "ip=172.20.0.2::172.20.0.1:255.255.255.0::enx001122aabbcc", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/24"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: "enx001122aabbcc", }, }, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "enx001122aabbcc", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "complete", cmdline: "ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1:eth1::10.0.0.1:10.0.0.2:10.0.0.1", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/24"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: "eth1", }, }, Hostname: "master1", DNSAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1"), netip.MustParseAddr("10.0.0.2")}, NTPAddresses: []netip.Addr{netip.MustParseAddr("10.0.0.1")}, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "another config", cmdline: "ip=10.105.155.21::10.105.155.30:255.255.255.240:pve1:eth0:off", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { LinkName: "eth0", Address: netip.MustParsePrefix("10.105.155.21/28"), Gateway: netip.MustParseAddr("10.105.155.30"), }, }, Hostname: "pve1", NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth0", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "ipv6", cmdline: "ip=[2001:db8::a]:[2001:db8::b]:[fe80::1]::master1:eth1::[2001:4860:4860::6464]:[2001:4860:4860::64]:[2001:4860:4806::]", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("2001:db8::a/128"), Gateway: netip.MustParseAddr("fe80::1"), LinkName: "eth1", }, }, Hostname: "master1", DNSAddresses: []netip.Addr{netip.MustParseAddr("2001:4860:4860::6464"), netip.MustParseAddr("2001:4860:4860::64")}, NTPAddresses: []netip.Addr{netip.MustParseAddr("2001:4860:4806::")}, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "ipv6-mask", cmdline: "ip=[2a03:1:2::12]::[2a03:1:2::11]:[ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8]:master:eth0:off:[2001:4860:4860::8888]:[2606:4700::1111]:[2606:4700:f1::1]", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("2a03:1:2::12/125"), Gateway: netip.MustParseAddr("2a03:1:2::11"), LinkName: "eth0", }, }, Hostname: "master", DNSAddresses: []netip.Addr{netip.MustParseAddr("2001:4860:4860::8888"), netip.MustParseAddr("2606:4700::1111")}, NTPAddresses: []netip.Addr{netip.MustParseAddr("2606:4700:f1::1")}, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth0", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "ipv6-mask-number", cmdline: "ip=[2a03:1:2::12]::[2a03:1:2::11]:125:master:eth0:off:[2001:4860:4860::8888]:[2606:4700::1111]:[2606:4700:f1::1]", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("2a03:1:2::12/125"), Gateway: netip.MustParseAddr("2a03:1:2::11"), LinkName: "eth0", }, }, Hostname: "master", DNSAddresses: []netip.Addr{netip.MustParseAddr("2001:4860:4860::8888"), netip.MustParseAddr("2606:4700::1111")}, NTPAddresses: []netip.Addr{netip.MustParseAddr("2606:4700:f1::1")}, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth0", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "unparseable IP", cmdline: "ip=xyz:", expectedError: "cmdline address parse failure: ParseAddr(\"xyz\"): unable to parse IP", }, { name: "hostname override", cmdline: "ip=::::master1:eth1 talos.hostname=master2", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { LinkName: "eth1", }, }, Hostname: "master2", NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, { name: "only hostname", cmdline: "talos.hostname=master2", expectedSettings: network.CmdlineNetworking{ Hostname: "master2", }, }, { name: "ignore interfaces", cmdline: "talos.network.interface.ignore=eth2 talos.network.interface.ignore=eth3 talos.network.interface.ignore=enxa", expectedSettings: network.CmdlineNetworking{ IgnoreInterfaces: []string{"eth2", "eth3", "eth31"}, }, }, { name: "bond with no interfaces and no options set", cmdline: "bond=bond1", expectedSettings: defaultBondSettings, }, { name: "bond with no interfaces and empty options set", cmdline: "bond=bond1:::", expectedSettings: defaultBondSettings, }, { name: "bond with interfaces and no options set", cmdline: "bond=bond1:eth3,eth4", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ defaultBondSettings.NetworkLinkSpecs[0], { Name: "eth3", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 0, }, }, { Name: "eth4", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 1, }, }, }, }, }, { name: "bond with aliased interfaces", cmdline: "bond=bond1:enxa,enxb", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ defaultBondSettings.NetworkLinkSpecs[0], { Name: "eth31", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 0, }, }, { Name: "eth32", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 1, }, }, }, }, }, { name: "bond with interfaces, options and mtu set", cmdline: "bond=bond1:eth3,eth4:mode=802.3ad,xmit_hash_policy=layer2+3:1450", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "bond1", Kind: "bond", Type: nethelpers.LinkEther, Logical: true, Up: true, MTU: 1450, ConfigLayer: netconfig.ConfigCmdline, BondMaster: netconfig.BondMasterSpec{ Mode: nethelpers.BondMode8023AD, HashPolicy: nethelpers.BondXmitPolicyLayer23, ADActorSysPrio: 65535, ResendIGMP: 1, LPInterval: 1, PacketsPerSlave: 1, NumPeerNotif: 1, TLBDynamicLB: 1, UseCarrier: true, PrimaryIndex: new(uint32(0)), ADLACPActive: nethelpers.ADLACPActiveOn, }, }, { Name: "eth3", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 0, }, }, { Name: "eth4", Up: true, Logical: false, ConfigLayer: netconfig.ConfigCmdline, BondSlave: netconfig.BondSlave{ MasterName: "bond1", SlaveIndex: 1, }, }, }, }, }, { name: "unparseable bond options", cmdline: "bond=bond0:eth1,eth2:mod=balance-rr", expectedError: "unknown bond option: mod", }, { name: "vlan configuration", cmdline: "vlan=eth1.169:eth1 ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1.169:::::", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/24"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: "eth1.169", }, }, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1.169", Logical: true, Up: true, Kind: netconfig.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: "eth1", ConfigLayer: netconfig.ConfigCmdline, VLAN: netconfig.VLANSpec{ VID: 169, Protocol: nethelpers.VLANProtocol8021Q, }, }, }, }, }, { name: "vlan configuration without ip configuration", cmdline: "vlan=eth1.5:eth1", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1.5", Logical: true, Up: true, Kind: netconfig.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: "eth1", ConfigLayer: netconfig.ConfigCmdline, VLAN: netconfig.VLANSpec{ VID: 5, Protocol: nethelpers.VLANProtocol8021Q, }, }, }, }, }, { name: "vlan configuration with alternative link name vlan0008", cmdline: "vlan=vlan0008:eth1", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1.8", Logical: true, Up: true, Kind: netconfig.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: "eth1", ConfigLayer: netconfig.ConfigCmdline, VLAN: netconfig.VLANSpec{ VID: 8, Protocol: nethelpers.VLANProtocol8021Q, }, }, }, }, }, { name: "vlan configuration with alternative link name vlan1", cmdline: "vlan=vlan4095:eth1", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1.4095", Logical: true, Up: true, Kind: netconfig.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: "eth1", ConfigLayer: netconfig.ConfigCmdline, VLAN: netconfig.VLANSpec{ VID: 4095, Protocol: nethelpers.VLANProtocol8021Q, }, }, }, }, }, { name: "vlan configuration with alias link name", cmdline: "vlan=vlan4095:enxa", expectedSettings: network.CmdlineNetworking{ NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "enxa.4095", Logical: true, Up: true, Kind: netconfig.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: "eth31", ConfigLayer: netconfig.ConfigCmdline, VLAN: netconfig.VLANSpec{ VID: 4095, Protocol: nethelpers.VLANProtocol8021Q, }, }, }, }, }, { name: "vlan configuration with invalid vlan ID 4096", cmdline: "vlan=eth1.4096:eth1", expectedError: fmt.Sprintf("invalid vlanID=%d, must be in the range 1..4095: %s", 4096, "eth1.4096:eth1"), }, { name: "vlan configuration with invalid vlan ID 0", cmdline: "vlan=eth1.0:eth1", expectedError: fmt.Sprintf("invalid vlanID=%d, must be in the range 1..4095: %s", 0, "eth1.0:eth1"), }, { name: "multiple ip configurations", cmdline: "ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1::::: ip=eth3:dhcp ip=:::::eth4:dhcp::::", expectedSettings: network.CmdlineNetworking{ LinkConfigs: []network.CmdlineLinkConfig{ { Address: netip.MustParsePrefix("172.20.0.2/24"), Gateway: netip.MustParseAddr("172.20.0.1"), LinkName: "eth1", }, { LinkName: "eth3", DHCP: true, }, { LinkName: "eth4", DHCP: true, }, }, NetworkLinkSpecs: []netconfig.LinkSpecSpec{ { Name: "eth1", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, { Name: "eth3", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, { Name: "eth4", Up: true, ConfigLayer: netconfig.ConfigCmdline, }, }, }, }, } { t.Run(test.name, func(t *testing.T) { cmdline := procfs.NewCmdline(test.cmdline) link1 := netconfig.NewLinkStatus(netconfig.NamespaceName, "eth31") link1.TypedSpec().Alias = "enxa" link2 := netconfig.NewLinkStatus(netconfig.NamespaceName, "eth32") link2.TypedSpec().Alias = "enxb" settings, err := network.ParseCmdlineNetwork( cmdline, netconfig.NewLinkResolver( func() iter.Seq[*netconfig.LinkStatus] { return slices.Values([]*netconfig.LinkStatus{link1, link2}) }, ), ) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { require.NoError(t, err) assert.Equal(t, test.expectedSettings, settings) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/device_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" glob "github.com/ryanuber/go-glob" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DeviceConfigController manages network.DeviceConfig based on configuration. type DeviceConfigController struct { devices map[string]networkDevice } //nolint:unused type networkDevice struct { hardwareAddress string busPrefix string driver string pciID string } // Name implements controller.Controller interface. func (ctrl *DeviceConfigController) Name() string { return "network.DeviceConfigController" } // Inputs implements controller.Controller interface. func (ctrl *DeviceConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DeviceConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.DeviceConfigSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DeviceConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { ctrl.devices = map[string]networkDevice{} for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } links, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return err } var cfgProvider talosconfig.Config cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { cfgProvider = cfg.Config() } r.StartTrackingOutputs() if cfgProvider != nil && cfgProvider.Machine() != nil { for index, device := range cfgProvider.Machine().Network().Devices() { out := []talosconfig.Device{device} if device.Selector() != nil { var matched []*v1alpha1.Device matched, err = ctrl.getDevicesBySelector(device, links) if err != nil { logger.Warn("failed to select an interface for a device", zap.Error(err)) continue } out = xslices.Map(matched, func(device *v1alpha1.Device) talosconfig.Device { return device }) } else if device.Bond() != nil && len(device.Bond().Selectors()) > 0 { dev := device.(*v1alpha1.Device).DeepCopy() device = dev err = ctrl.expandBondSelector(dev, links) if err != nil { logger.Warn("failed to select interfaces for a bond device", zap.Error(err)) continue } out = []talosconfig.Device{device} } for j, outDevice := range out { id := fmt.Sprintf("%s/%03d", outDevice.Interface(), index) if len(out) > 1 { id = fmt.Sprintf("%s/%03d", id, j) } if err = safe.WriterModify( ctx, r, network.NewDeviceConfig(id, outDevice), func(r *network.DeviceConfigSpec) error { r.TypedSpec().Device = outDevice return nil }, ); err != nil { return err } } } } if err = safe.CleanupOutputs[*network.DeviceConfigSpec](ctx, r); err != nil { return err } } } func (ctrl *DeviceConfigController) getDevicesBySelector(device talosconfig.Device, links safe.List[*network.LinkStatus]) ([]*v1alpha1.Device, error) { selector := device.Selector() matches := ctrl.selectDevices(selector, links) if len(matches) == 0 { return nil, fmt.Errorf("no matching network device for defined selector: %+v", selector) } out := make([]*v1alpha1.Device, len(matches)) for i, link := range matches { out[i] = device.(*v1alpha1.Device).DeepCopy() out[i].DeviceInterface = link.Metadata().ID() } return out, nil } func (ctrl *DeviceConfigController) expandBondSelector(device *v1alpha1.Device, links safe.List[*network.LinkStatus]) error { matches := make([]*network.LinkStatus, 0, len(device.Bond().Selectors())) for _, selector := range device.Bond().Selectors() { matches = append(matches, // filter out bond device itself, as it will inherit the MAC address of the first link xslices.Filter( ctrl.selectDevices(selector, links), func(link *network.LinkStatus) bool { return link.Metadata().ID() != device.Interface() })...) } device.DeviceBond.BondInterfaces = xslices.Map(matches, func(link *network.LinkStatus) string { return link.Metadata().ID() }) if len(device.DeviceBond.BondInterfaces) == 0 { return fmt.Errorf("no matching network device for defined bond selectors: %v", xslices.Map(device.Bond().Selectors(), func(selector talosconfig.NetworkDeviceSelector) string { return fmt.Sprintf("%+v", selector) }, ), ) } device.DeviceBond.BondDeviceSelectors = nil return nil } func (ctrl *DeviceConfigController) selectDevices(selector talosconfig.NetworkDeviceSelector, links safe.List[*network.LinkStatus]) []*network.LinkStatus { var result []*network.LinkStatus for linkStatus := range links.All() { linkStatusSpec := linkStatus.TypedSpec() var match optional.Optional[bool] for _, pair := range [][]string{ {selector.HardwareAddress(), linkStatusSpec.HardwareAddr.String()}, {selector.PermanentAddress(), linkStatusSpec.PermanentAddr.String()}, {selector.PCIID(), linkStatusSpec.PCIID}, {selector.KernelDriver(), linkStatusSpec.Driver}, {selector.Bus(), linkStatusSpec.BusPath}, } { if pair[0] == "" { continue } if !glob.Glob(pair[0], pair[1]) { match = optional.Some(false) break } match = optional.Some(true) } if selector.Physical() != nil && match.ValueOr(true) { match = optional.Some(*selector.Physical() == linkStatusSpec.Physical()) } if match.ValueOrZero() { result = append(result, linkStatus) } } return result } ================================================ FILE: internal/app/machined/pkg/controllers/network/device_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "fmt" "net" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/maps" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" configs "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type DeviceConfigSpecSuite struct { ctest.DefaultSuite } func (suite *DeviceConfigSpecSuite) TestDeviceConfigs() { cfgProvider := container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy controller NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceAddresses: []string{"192.168.2.0/24"}, DeviceMTU: 1500, }, { DeviceInterface: "bond0", DeviceAddresses: []string{"192.168.2.0/24"}, DeviceBond: &v1alpha1.Bond{ BondMode: "balance-rr", BondInterfaces: []string{"eth1", "eth2"}, }, }, { DeviceInterface: "eth0", DeviceAddresses: []string{"192.168.3.0/24"}, }, }, }, }, }) cfg := config.NewMachineConfig(cfgProvider) devices := map[string]configs.Device{} for index, item := range cfgProvider.Machine().Network().Devices() { devices[fmt.Sprintf("%s/%03d", item.Interface(), index)] = item } suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), maps.Keys(devices), func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal(r.TypedSpec().Device, devices[r.Metadata().ID()]) }, ) } func (suite *DeviceConfigSpecSuite) TestSelectors() { kernelDriver := "thedriver" cfgProvider := container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy controller NetworkInterfaces: []*v1alpha1.Device{ // device selector selecing a single interface { DeviceSelector: &v1alpha1.NetworkDeviceSelector{ NetworkDeviceKernelDriver: kernelDriver, }, DeviceAddresses: []string{"192.168.2.0/24"}, DeviceMTU: 1500, }, // no device selector (explicit name) { DeviceInterface: "eth0", DeviceAddresses: []string{"192.168.3.0/24"}, }, // device selector which doesn't match anything { DeviceSelector: &v1alpha1.NetworkDeviceSelector{ NetworkDeviceKernelDriver: "no-match", }, DeviceAddresses: []string{"192.168.4.0/24"}, }, // device selector which matches multiple interfaces { DeviceSelector: &v1alpha1.NetworkDeviceSelector{ NetworkDeviceBus: "0000:01*", }, DeviceAddresses: []string{"192.168.5.0/24"}, }, // device selector which matches physical interfaces { DeviceSelector: &v1alpha1.NetworkDeviceSelector{ NetworkDevicePhysical: new(true), }, DeviceAddresses: []string{"192.168.6.0/24"}, }, }, }, }, }) cfg := config.NewMachineConfig(cfgProvider) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) status := network.NewLinkStatus(network.NamespaceName, "eth0") status.TypedSpec().Driver = kernelDriver status.TypedSpec().BusPath = "0000:01:00.0" status.TypedSpec().Type = nethelpers.LinkEther // physical suite.Require().NoError(suite.State().Create(suite.Ctx(), status)) status = network.NewLinkStatus(network.NamespaceName, "eth1") status.TypedSpec().BusPath = "0000:01:01.0" suite.Require().NoError(suite.State().Create(suite.Ctx(), status)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"eth0/000"}, func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal(1500, r.TypedSpec().Device.MTU()) assert.Equal([]string{"192.168.2.0/24"}, r.TypedSpec().Device.Addresses()) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"eth0/001"}, func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal([]string{"192.168.3.0/24"}, r.TypedSpec().Device.Addresses()) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"eth0/003/000", "eth1/003/001"}, func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal([]string{"192.168.5.0/24"}, r.TypedSpec().Device.Addresses()) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"eth0/004"}, func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal([]string{"192.168.6.0/24"}, r.TypedSpec().Device.Addresses()) }, ) } func (suite *DeviceConfigSpecSuite) TestBondSelectors() { cfgProvider := container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy controller NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceAddresses: []string{"192.168.2.0/24"}, DeviceMTU: 1500, DeviceBond: &v1alpha1.Bond{ BondMode: "balance-rr", BondDeviceSelectors: []v1alpha1.NetworkDeviceSelector{ { NetworkDevicePermanentAddress: "00:*", }, { NetworkDevicePermanentAddress: "01:*", }, }, }, }, }, }, }, }) cfg := config.NewMachineConfig(cfgProvider) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) for _, link := range []string{"eth0", "eth1"} { status := network.NewLinkStatus(network.NamespaceName, link) suite.Require().NoError(suite.State().Create(suite.Ctx(), status)) } rtestutils.AssertNoResource[*network.DeviceConfigSpec](suite.Ctx(), suite.T(), suite.State(), "bond0/000") for _, link := range []struct { name string hwaddr string }{ { name: "bond0", hwaddr: "00:11:22:33:44:55", // bond0 will inherit MAC of the first link }, { name: "eth3", hwaddr: "00:11:22:33:44:55", }, { name: "eth4", hwaddr: "01:11:22:33:44:55", }, { name: "eth5", hwaddr: "01:11:22:33:44:ef", }, { name: "eth6", hwaddr: "02:11:22:33:44:55", }, } { hwaddr, err := net.ParseMAC(link.hwaddr) suite.Require().NoError(err) status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().PermanentAddr = nethelpers.HardwareAddr(hwaddr) suite.Require().NoError(suite.State().Create(suite.Ctx(), status)) } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []string{"bond0/000"}, func(r *network.DeviceConfigSpec, assert *assert.Assertions) { assert.Equal(1500, r.TypedSpec().Device.MTU()) assert.Equal([]string{"192.168.2.0/24"}, r.TypedSpec().Device.Addresses()) assert.Equal([]string{"eth3", "eth4", "eth5"}, r.TypedSpec().Device.Bond().Interfaces()) }, ) } func TestDeviceConfigSpecSuite(t *testing.T) { suite.Run(t, &DeviceConfigSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DeviceConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/dns_resolve_cache.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "context" "fmt" "iter" "net/netip" "sync" "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xiter" "github.com/thejerf/suture/v4" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/pkg/dns" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DNSResolveCacheController starts dns server on both udp and tcp ports based on finalized network configuration. type DNSResolveCacheController struct { State state.State Logger *zap.Logger mx sync.Mutex manager *dns.Manager reconcile chan struct{} } // Name implements controller.Controller interface. func (ctrl *DNSResolveCacheController) Name() string { return "network.DNSResolveCacheController" } // Inputs implements controller.Controller interface. func (ctrl *DNSResolveCacheController) Inputs() []controller.Input { return []controller.Input{ safe.Input[*network.DNSUpstream](controller.InputWeak), { Namespace: network.NamespaceName, Type: network.HostDNSConfigType, ID: optional.Some(network.HostDNSConfigID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DNSResolveCacheController) Outputs() []controller.Output { return []controller.Output{ { Type: network.DNSResolveCacheType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *DNSResolveCacheController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { ctrl.init(ctx) ctrl.mx.Lock() defer ctrl.mx.Unlock() defer func() { if err := ctrl.manager.ClearAll(ctx.Err() == nil); err != nil { ctrl.Logger.Error("error stopping dns runners", zap.Error(err)) } if ctx.Err() != nil { ctrl.Logger.Info("manager finished", zap.Error(<-ctrl.manager.Done())) } }() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ctrl.reconcile: } if err := ctrl.run(ctx, r); err != nil { return err } } } //nolint:gocyclo func (ctrl *DNSResolveCacheController) run(ctx context.Context, r controller.Runtime) (resErr error) { r.StartTrackingOutputs() defer cleanupOutputs(ctx, r, &resErr) cfg, err := safe.ReaderGetByID[*network.HostDNSConfig](ctx, r, network.HostDNSConfigID) switch { case state.IsNotFoundError(err): return nil case err != nil: return fmt.Errorf("error getting host dns config: %w", err) } ctrl.manager.AllowNodeResolving(cfg.TypedSpec().ResolveMemberNames) if !cfg.TypedSpec().Enabled { return ctrl.manager.ClearAll(false) } pairs := allAddressPairs(cfg.TypedSpec().ListenAddresses) forwardKubeDNSToHost := cfg.TypedSpec().ServiceHostDNSAddress.IsValid() for runCfg, runErr := range ctrl.manager.RunAll(pairs, forwardKubeDNSToHost) { switch { case runErr != nil && (runCfg.Network == "tcp6" || runCfg.Network == "udp6"): // Ignore ipv6 errors ctrl.Logger.Warn("ignoring ipv6 dns runner error", zap.Error(runErr)) case runErr != nil: return fmt.Errorf("error updating dns runner '%v': %w", runCfg, runErr) case runCfg.Status == dns.StatusRemoved: // Removed runned, no reason to update status continue } if err = ctrl.writeDNSStatus(ctx, r, runCfg.AddressPair); err != nil { return fmt.Errorf("error writing dns status: %w", err) } } upstreams, err := safe.ReaderListAll[*network.DNSUpstream](ctx, r) if err != nil { return fmt.Errorf("error getting resolver status: %w", err) } prxs := xiter.Map( // We are using iterator here to preserve finalizer on func(upstream *network.DNSUpstream) *proxy.Proxy { return upstream.TypedSpec().Value.Conn.Proxy().(*proxy.Proxy) }, upstreams.All(), ) if ctrl.manager.SetUpstreams(prxs) { ctrl.Logger.Info("updated dns server nameservers", zap.Array("addrs", addrsArr(upstreams))) } return nil } func cleanupOutputs(ctx context.Context, r controller.Runtime, resErr *error) { if err := safe.CleanupOutputs[*network.DNSResolveCache](ctx, r); err != nil { *resErr = cmp.Or(*resErr, fmt.Errorf("error cleaning up dns resolve cache: %w", err)) } } func (ctrl *DNSResolveCacheController) writeDNSStatus(ctx context.Context, r controller.Runtime, config dns.AddressPair) error { res := network.NewDNSResolveCache(fmt.Sprintf("%s-%s", config.Network, config.Addr)) return safe.WriterModify(ctx, r, res, func(drc *network.DNSResolveCache) error { drc.TypedSpec().Status = "running" return nil }) } func (ctrl *DNSResolveCacheController) init(ctx context.Context) { if ctrl.manager == nil { ctrl.manager = dns.NewManager(&memberReader{st: ctrl.State}, ctrl.eventHook, ctrl.Logger) // Ensure we stop all runners when the context is canceled, no matter where we are currently. // For example if we are in Controller runtime sleeping after error and ctx is canceled, we should stop all runners // but, we will never call Run method again, so we need to ensure this happens regardless of the current state. context.AfterFunc(ctx, func() { ctrl.mx.Lock() defer ctrl.mx.Unlock() if err := ctrl.manager.ClearAll(false); err != nil { ctrl.Logger.Error("error ctx stopping dns runners", zap.Error(err)) } }) } ctrl.manager.ServeBackground(ctx) } func (ctrl *DNSResolveCacheController) eventHook(event suture.Event) { ctrl.Logger.Info("dns-resolve-cache-runners event", zap.String("event", event.String())) select { case ctrl.reconcile <- struct{}{}: default: } } type memberReader struct{ st state.State } func (m *memberReader) ReadMembers(ctx context.Context) (iter.Seq[*cluster.Member], error) { list, err := safe.ReaderListAll[*cluster.Member](ctx, m.st) if err != nil { return nil, err } return list.All(), nil } type addrsArr safe.List[*network.DNSUpstream] func (a addrsArr) MarshalLogArray(encoder zapcore.ArrayEncoder) error { list := safe.List[*network.DNSUpstream](a) for u := range list.All() { encoder.AppendString(u.TypedSpec().Value.Conn.Addr()) } return nil } func allAddressPairs(addresses []netip.AddrPort) iter.Seq[dns.AddressPair] { return func(yield func(dns.AddressPair) bool) { for _, addr := range addresses { networks := [...]string{"udp", "tcp"} if addr.Addr().Is6() { networks = [...]string{"udp6", "tcp6"} } for _, netwk := range networks { if !yield(dns.AddressPair{ Network: netwk, Addr: addr, }) { return } } } } } ================================================ FILE: internal/app/machined/pkg/controllers/network/dns_resolve_cache_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "errors" "net" "net/netip" "slices" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/miekg/dns" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type DNSServer struct { ctest.DefaultSuite } func expectedDNSRunners(port string) []resource.ID { return []resource.ID{ "tcp-127.0.0.53:" + port, "udp-127.0.0.53:" + port, // our dns server makes no promises about actually starting on IPv6, so we don't check it here either } } func (suite *DNSServer) TestResolving() { dnsSlice := []string{"8.8.8.8", "1.1.1.1"} port := getDynamicPort(suite.T()) cfg := network.NewHostDNSConfig(network.HostDNSConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ListenAddresses = makeAddrs(port) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) resolverSpec := network.NewResolverStatus(network.NamespaceName, network.ResolverID) resolverSpec.TypedSpec().DNSServers = xslices.Map(dnsSlice, netip.MustParseAddr) suite.Require().NoError(suite.State().Create(suite.Ctx(), resolverSpec)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), expectedDNSRunners(port), func(r *network.DNSResolveCache, assert *assert.Assertions) { assert.Equal("running", r.TypedSpec().Status) }, ) rtestutils.AssertLength[*network.DNSUpstream](suite.Ctx(), suite.T(), suite.State(), len(dnsSlice)) msg := &dns.Msg{ MsgHdr: dns.MsgHdr{ Id: dns.Id(), RecursionDesired: true, }, Question: []dns.Question{ { Name: dns.Fqdn("google.com"), Qtype: dns.TypeA, Qclass: dns.ClassINET, }, }, } var res *dns.Msg err := retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { r, err := dns.Exchange(msg, "127.0.0.53:"+port) if err != nil { return retry.ExpectedError(err) } if r.Rcode != dns.RcodeSuccess { return retry.ExpectedErrorf("expected rcode %d, got %d", dns.RcodeSuccess, r.Rcode) } res = r return nil }) suite.Require().NoError(err) suite.Require().Equal(dns.RcodeSuccess, res.Rcode, res) } func (suite *DNSServer) TestSetupStartStop() { dnsSlice := []string{"8.8.8.8", "1.1.1.1"} port := getDynamicPort(suite.T()) resolverSpec := network.NewResolverStatus(network.NamespaceName, network.ResolverID) resolverSpec.TypedSpec().DNSServers = xslices.Map(dnsSlice, netip.MustParseAddr) suite.Require().NoError(suite.State().Create(suite.Ctx(), resolverSpec)) cfg := network.NewHostDNSConfig(network.HostDNSConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ListenAddresses = makeAddrs(port) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), expectedDNSRunners(port), func(r *network.DNSResolveCache, assert *assert.Assertions) { assert.Equal("running", r.TypedSpec().Status) }) rtestutils.AssertLength[*network.DNSUpstream](suite.Ctx(), suite.T(), suite.State(), len(dnsSlice)) // stop dns resolver cfg.TypedSpec().Enabled = false suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) for _, runner := range expectedDNSRunners(port) { ctest.AssertNoResource[*network.DNSResolveCache](suite, runner) } for _, d := range dnsSlice { ctest.AssertNoResource[*network.DNSUpstream](suite, d) } // start dns resolver again cfg.TypedSpec().Enabled = true suite.Require().NoError(suite.State().Update(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), expectedDNSRunners(port), func(r *network.DNSResolveCache, assert *assert.Assertions) { assert.Equal("running", r.TypedSpec().Status) }) rtestutils.AssertLength[*network.DNSUpstream](suite.Ctx(), suite.T(), suite.State(), len(dnsSlice)) } func (suite *DNSServer) TestResolveMembers() { port := getDynamicPort(suite.T()) const ( id = "talos-default-controlplane-1" id2 = "foo.example.com." ) member := cluster.NewMember(cluster.NamespaceName, id) *member.TypedSpec() = cluster.MemberSpec{ NodeID: id, Addresses: []netip.Addr{ netip.MustParseAddr("172.20.0.2"), }, Hostname: id, MachineType: machine.TypeControlPlane, OperatingSystem: "Talos dev", ControlPlane: nil, } suite.Require().NoError(suite.State().Create(suite.Ctx(), member)) member = cluster.NewMember(cluster.NamespaceName, id2) *member.TypedSpec() = cluster.MemberSpec{ NodeID: id2, Addresses: []netip.Addr{ netip.MustParseAddr("172.20.0.3"), }, Hostname: id2, MachineType: machine.TypeWorker, OperatingSystem: "Talos dev", ControlPlane: nil, } suite.Require().NoError(suite.State().Create(suite.Ctx(), member)) cfg := network.NewHostDNSConfig(network.HostDNSConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ListenAddresses = makeAddrs(port) cfg.TypedSpec().ResolveMemberNames = true suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), expectedDNSRunners(port), func(r *network.DNSResolveCache, assert *assert.Assertions) { assert.Equal("running", r.TypedSpec().Status) }, ) suite.Require().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { exchange, err := dns.Exchange( &dns.Msg{ MsgHdr: dns.MsgHdr{Id: dns.Id(), RecursionDesired: true}, Question: []dns.Question{ {Name: dns.Fqdn(id), Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, "127.0.0.53:"+port, ) if err != nil { return retry.ExpectedError(err) } if exchange.Rcode != dns.RcodeSuccess { return retry.ExpectedErrorf("expected rcode %d, got %d for %q", dns.RcodeSuccess, exchange.Rcode, id) } proper := dns.Fqdn(id) if exchange.Answer[0].Header().Name != proper { return retry.ExpectedErrorf("expected answer name %q, got %q", proper, exchange.Answer[0].Header().Name) } return nil })) suite.Require().NoError(retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { exchange, err := dns.Exchange( &dns.Msg{ MsgHdr: dns.MsgHdr{Id: dns.Id(), RecursionDesired: true}, Question: []dns.Question{ {Name: dns.Fqdn("foo"), Qtype: dns.TypeA, Qclass: dns.ClassINET}, }, }, "127.0.0.53:"+port, ) if err != nil { return retry.ExpectedError(err) } if exchange.Rcode != dns.RcodeSuccess { return retry.ExpectedErrorf("expected rcode %d, got %d for %q", dns.RcodeSuccess, exchange.Rcode, id2) } if !exchange.Answer[0].(*dns.A).A.Equal(net.ParseIP("172.20.0.3")) { return retry.ExpectedError(errors.New("unexpected ip")) } return nil })) } func TestDNSServer(t *testing.T) { t.Parallel() suite.Run(t, &DNSServer{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DNSUpstreamController{})) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DNSResolveCacheController{ Logger: zaptest.NewLogger(t), State: suite.State(), })) }, }, }) } func getDynamicPort(t *testing.T) string { t.Helper() l, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", "127.0.0.1:0") require.NoError(t, err) addr := l.Addr().String() require.NoError(t, l.Close()) _, port, err := net.SplitHostPort(addr) require.NoError(t, err) return port } func makeAddrs(port string) []netip.AddrPort { return []netip.AddrPort{ netip.MustParseAddrPort("127.0.0.53:" + port), } } type DNSUpstreams struct { ctest.DefaultSuite } func (suite *DNSUpstreams) TestOrder() { port := getDynamicPort(suite.T()) cfg := network.NewHostDNSConfig(network.HostDNSConfigID) cfg.TypedSpec().Enabled = true cfg.TypedSpec().ListenAddresses = makeAddrs(port) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) resolverSpec := network.NewResolverStatus(network.NamespaceName, network.ResolverID) for i, addrs := range [][]string{ {"1.0.0.1", "8.8.8.8", "1.1.1.1"}, {"1.1.1.1", "8.8.8.8", "1.0.0.1", "8.0.0.8"}, {"192.168.0.1"}, } { if !suite.Run(strings.Join(addrs, ","), func() { resolverSpec.TypedSpec().DNSServers = xslices.Map(addrs, netip.MustParseAddr) switch i { case 0: suite.Require().NoError(suite.State().Create(suite.Ctx(), resolverSpec)) default: suite.Require().NoError(suite.State().Update(suite.Ctx(), resolverSpec)) } expected := xslices.Map(addrs, func(t string) string { return t + ":53" }) rtestutils.AssertLength[*network.DNSUpstream](suite.Ctx(), suite.T(), suite.State(), len(addrs)) var actual []string defer func() { suite.Require().Equal(expected, actual) }() for suite.Ctx().Err() == nil { upstreams, err := safe.ReaderListAll[*network.DNSUpstream](suite.Ctx(), suite.State()) suite.Require().NoError(err) actual = safe.ToSlice(upstreams, func(u *network.DNSUpstream) string { return u.TypedSpec().Value.Conn.Addr() }) if slices.Equal(expected, actual) { break } } }) { break } } } func TestDNSUpstreams(t *testing.T) { t.Parallel() suite.Run(t, &DNSUpstreams{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DNSUpstreamController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/dns_upstream.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net" "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DNSUpstreamController is a controller that manages DNS upstreams. type DNSUpstreamController struct{} // Name implements controller.Controller interface. func (ctrl *DNSUpstreamController) Name() string { return "network.DNSUpstreamController" } // Inputs implements controller.Controller interface. func (ctrl *DNSUpstreamController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.HostDNSConfigType, ID: optional.Some(network.HostDNSConfigID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.ResolverStatusType, ID: optional.Some(network.ResolverID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DNSUpstreamController) Outputs() []controller.Output { return []controller.Output{ { Type: network.DNSUpstreamType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *DNSUpstreamController) Run(ctx context.Context, r controller.Runtime, l *zap.Logger) error { defer cleanupUpstream(context.Background(), r, nil, l) for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if err := ctrl.run(ctx, r, l); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *DNSUpstreamController) run(ctx context.Context, r controller.Runtime, l *zap.Logger) error { touchedIDs := map[resource.ID]struct{}{} defer cleanupUpstream(ctx, r, touchedIDs, l) cfg, err := safe.ReaderGetByID[*network.HostDNSConfig](ctx, r, network.HostDNSConfigID) if err != nil { if state.IsNotFoundError(err) { return nil } return err } if !cfg.TypedSpec().Enabled { // host DNS is disabled, cleanup all upstreams return nil } rs, err := safe.ReaderGetByID[*network.ResolverStatus](ctx, r, network.ResolverID) if err != nil { if state.IsNotFoundError(err) { return nil } return err } initConn, err := existingConnections(ctx, r) if err != nil { return err } for i, srv := range rs.TypedSpec().DNSServers { remoteHost := srv.String() if err = safe.WriterModify[*network.DNSUpstream]( ctx, r, network.NewDNSUpstream(fmt.Sprintf("#%03d %s", i, remoteHost)), func(u *network.DNSUpstream) error { touchedIDs[u.Metadata().ID()] = struct{}{} initConn(&u.TypedSpec().Value, remoteHost, l) return nil }, ); err != nil { return err } } return nil } func existingConnections(ctx context.Context, r controller.Runtime) (func(*network.DNSUpstreamSpecSpec, string, *zap.Logger), error) { upstream, err := safe.ReaderListAll[*network.DNSUpstream](ctx, r) if err != nil { return nil, err } existingConn := make(map[string]*network.DNSConn, upstream.Len()) for u := range upstream.All() { existingConn[u.TypedSpec().Value.Conn.Addr()] = u.TypedSpec().Value.Conn } return func(spec *network.DNSUpstreamSpecSpec, remoteHost string, l *zap.Logger) { remoteAddr := net.JoinHostPort(remoteHost, "53") if spec.Conn != nil && spec.Conn.Addr() == remoteAddr { l.Debug("reusing existing upstream spec", zap.String("addr", remoteAddr)) return } defer func(c *network.DNSConn) { if c != nil { c.Close() } }(spec.Conn) if conn, ok := existingConn[remoteAddr]; ok { spec.Conn = conn.NewRef() l.Debug("reusing existing upstream connection", zap.String("addr", remoteAddr)) return } spec.Conn = network.NewDNSConn(proxy.NewProxy(remoteHost, remoteAddr, "dns")) l.Debug("created new upstream connection", zap.String("addr", remoteAddr)) existingConn[remoteAddr] = spec.Conn }, nil } func cleanupUpstream(ctx context.Context, r controller.Runtime, touchedIDs map[resource.ID]struct{}, l *zap.Logger) { list, err := safe.ReaderListAll[*network.DNSUpstream](ctx, r) if err != nil { l.Error("error listing upstreams", zap.Error(err)) return } for val := range list.All() { md := val.Metadata() if _, ok := touchedIDs[md.ID()]; ok { continue } if conn := val.TypedSpec().Value.Conn; conn != nil { conn.Close() } if err = r.Destroy(ctx, md); err != nil { l.Error("error destroying upstream", zap.Error(err), zap.String("id", md.ID())) return } l.Debug("destroyed dns upstream", zap.String("addr", md.ID())) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/etcfile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "bytes" "context" "fmt" "iter" "maps" "net/netip" "os" "path/filepath" "slices" "strings" "text/tabwriter" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xiter" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" efiles "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/mount/v3" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" ) // EtcFileController creates /etc/hostname and /etc/resolv.conf files based on finalized network configuration. type EtcFileController struct { V1Alpha1Mode runtime.Mode EtcRoot xfs.Root BindMountTarget string bindMountCreated bool } // Name implements controller.Controller interface. func (ctrl *EtcFileController) Name() string { return "network.EtcFileController" } // Inputs implements controller.Controller interface. func (ctrl *EtcFileController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.ResolverStatusType, ID: optional.Some(network.ResolverID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.NodeAddressDefaultID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostDNSConfigType, ID: optional.Some(network.HostDNSConfigID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EtcFileController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *EtcFileController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var cfgProvider talosconfig.Config cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { cfgProvider = cfg.Config() } hostnameStatus, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting hostname status: %w", err) } } nodeAddressStatus, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.NodeAddressDefaultID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting network address status: %w", err) } } resolverStatus, err := safe.ReaderGetByID[*network.ResolverStatus](ctx, r, network.ResolverID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error resolver status: %w", err) } } hostDNSCfg, err := safe.ReaderGetByID[*network.HostDNSConfig](ctx, r, network.HostDNSConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting host dns config: %w", err) } } if resolverStatus != nil && hostDNSCfg != nil && !ctrl.V1Alpha1Mode.InContainer() { // in container mode, keep the original resolv.conf to use the resolvers supplied by the container runtime if err = safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "resolv.conf"), func(r *files.EtcFileSpec) error { r.TypedSpec().Contents = renderResolvConf( pickNameservers(hostDNSCfg, resolverStatus), resolverStatus.TypedSpec().SearchDomains, ) r.TypedSpec().Mode = 0o644 r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return nil }); err != nil { return fmt.Errorf("error modifying resolv.conf: %w", err) } } if resolverStatus != nil && hostDNSCfg != nil { dnsServers := xslices.FilterInPlace( []netip.Addr{hostDNSCfg.TypedSpec().ServiceHostDNSAddress}, netip.Addr.IsValid, ) if len(dnsServers) == 0 { dnsServers = resolverStatus.TypedSpec().DNSServers } src := "resolv.conf" dst := filepath.Join(ctrl.BindMountTarget, src) conf := renderResolvConf(slices.All(dnsServers), resolverStatus.TypedSpec().SearchDomains) if err := efiles.UpdateFile(ctrl.EtcRoot, src, conf, 0o644, constants.EtcSelinuxLabel); err != nil { return fmt.Errorf("error writing pod resolv.conf: %w", err) } if ctrl.EtcRoot.FSType() != "os" { if !ctrl.bindMountCreated { if err := createBindMountFileFd(ctrl.EtcRoot, src, dst, 0o644); err != nil { return fmt.Errorf("failed to create shadow bind mount %q -> %q: %w", src, dst, err) } ctrl.bindMountCreated = true } } } if err = safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, "hosts"), func(r *files.EtcFileSpec) error { r.TypedSpec().Contents, err = ctrl.renderHosts(hostnameStatus, nodeAddressStatus, cfgProvider) r.TypedSpec().Mode = 0o644 r.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel return err }); err != nil { return fmt.Errorf("error modifying hosts: %w", err) } r.ResetRestartBackoff() } } var localDNS = xiter.Single2(0, netip.MustParseAddr("127.0.0.53")) func pickNameservers(hostDNSCfg *network.HostDNSConfig, resolverStatus *network.ResolverStatus) iter.Seq2[int, netip.Addr] { if hostDNSCfg.TypedSpec().Enabled { // local dns resolve cache enabled, route host dns requests to 127.0.0.1 return localDNS } return slices.All(resolverStatus.TypedSpec().DNSServers) } func renderResolvConf(nameservers iter.Seq2[int, netip.Addr], searchDomains []string) []byte { var buf bytes.Buffer for i, ns := range nameservers { if i >= 3 { // only use first 3 nameservers, see MAXNS in https://linux.die.net/man/5/resolv.conf break } fmt.Fprintf(&buf, "nameserver %s\n", ns) } if len(searchDomains) > 0 { fmt.Fprintf(&buf, "\nsearch %s\n", strings.Join(searchDomains, " ")) } return buf.Bytes() } func (ctrl *EtcFileController) renderHosts(hostnameStatus *network.HostnameStatus, nodeAddressStatus *network.NodeAddress, cfgProvider talosconfig.Config) ([]byte, error) { var buf bytes.Buffer tabW := tabwriter.NewWriter(&buf, 0, 0, 1, ' ', 0) write := func(s string) { tabW.Write([]byte(s)) } //nolint:errcheck write("127.0.0.1\tlocalhost\n") if nodeAddressStatus != nil && hostnameStatus != nil { write(fmt.Sprintf("%s\t%s", nodeAddressStatus.TypedSpec().Addresses[0].Addr(), hostnameStatus.TypedSpec().FQDN())) if hostnameStatus.TypedSpec().Hostname != hostnameStatus.TypedSpec().FQDN() { write(" " + hostnameStatus.TypedSpec().Hostname) } write("\n") } write("::1\tlocalhost ip6-localhost ip6-loopback\n") write("ff02::1\tip6-allnodes\n") write("ff02::2\tip6-allrouters\n") hostMap := map[string][]string{} if cfgProvider != nil { for _, extraHost := range cfgProvider.NetworkStaticHostConfig() { hostMap[extraHost.IP()] = append(hostMap[extraHost.IP()], extraHost.Aliases()...) } } for _, addr := range slices.Sorted(maps.Keys(hostMap)) { write(fmt.Sprintf("%s\t%s\n", addr, strings.Join(hostMap[addr], " "))) } if err := tabW.Flush(); err != nil { return nil, err } return buf.Bytes(), nil } // createBindMountFileFd creates a common way to create a writable source file with a // bind mounted destination. func createBindMountFileFd(root xfs.Root, src, dst string, mode os.FileMode) (err error) { if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { return fmt.Errorf("error creating bind mount dir for resolv.conf: %w", err) } f, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { return fmt.Errorf("error creating bind mount target %q for resolv.conf: %w", dst, err) } if err := f.Close(); err != nil { return fmt.Errorf("error closing bind mount target %q for resolv.conf: %w", dst, err) } if err = xfs.MkdirAll(root, filepath.Dir(src), 0o755); err != nil { return err } fsrc, err := xfs.OpenFile(root, src, os.O_WRONLY|os.O_CREATE, mode) if err != nil { return err } defer fsrc.Close() //nolint:errcheck return mount.BindReadonlyFd(int(fsrc.Fd()), dst) } ================================================ FILE: internal/app/machined/pkg/controllers/network/etcfile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "context" "errors" "net/netip" "net/url" "os" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/opentree" ) type EtcFileConfigSuite struct { ctest.DefaultSuite cfg *config.MachineConfig defaultAddress *network.NodeAddress hostnameStatus *network.HostnameStatus resolverStatus *network.ResolverStatus hostDNSConfig *network.HostDNSConfig bindMountTarget string podResolvConfPath string etcRoot xfs.Root } func (suite *EtcFileConfigSuite) ExtraSetup() { ok, err := v1alpha1runtime.KernelCapabilities().OpentreeOnAnonymousFS() suite.Require().NoError(err) if ok { suite.etcRoot = &xfs.UnixRoot{FS: opentree.NewFromPath(suite.T().TempDir())} suite.bindMountTarget = suite.T().TempDir() suite.podResolvConfPath = filepath.Join(suite.bindMountTarget, "resolv.conf") } else { suite.etcRoot = &xfs.OSRoot{Shadow: suite.T().TempDir()} suite.podResolvConfPath = filepath.Join(suite.etcRoot.Source(), "resolv.conf") } suite.Require().NoError(suite.etcRoot.OpenFS()) suite.Assert().NoFileExists(suite.podResolvConfPath) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.EtcFileController{ V1Alpha1Mode: v1alpha1runtime.ModeMetal, EtcRoot: suite.etcRoot, BindMountTarget: suite.bindMountTarget, })) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) suite.cfg = config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config ExtraHostEntries: []*v1alpha1.ExtraHost{ { HostIP: "10.0.0.1", HostAliases: []string{"a", "b"}, }, { HostIP: "10.0.0.2", HostAliases: []string{"c", "d"}, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.defaultAddress = network.NewNodeAddress(network.NamespaceName, network.NodeAddressDefaultID) suite.defaultAddress.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("33.11.22.44/32")} suite.hostnameStatus = network.NewHostnameStatus(network.NamespaceName, network.HostnameID) suite.hostnameStatus.TypedSpec().Hostname = "foo" suite.hostnameStatus.TypedSpec().Domainname = "example.com" suite.resolverStatus = network.NewResolverStatus(network.NamespaceName, network.ResolverID) suite.resolverStatus.TypedSpec().DNSServers = []netip.Addr{ netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3"), netip.MustParseAddr("4.4.4.4"), } suite.hostDNSConfig = network.NewHostDNSConfig(network.HostDNSConfigID) suite.hostDNSConfig.TypedSpec().Enabled = true suite.hostDNSConfig.TypedSpec().ListenAddresses = []netip.AddrPort{ netip.MustParseAddrPort("127.0.0.53:53"), netip.MustParseAddrPort("169.254.116.108:53"), } suite.hostDNSConfig.TypedSpec().ServiceHostDNSAddress = netip.MustParseAddr("169.254.116.108") } type etcFileContents struct { hosts string resolvConf string resolvGlobalConf string } //nolint:gocyclo func (suite *EtcFileConfigSuite) testFiles(resources []resource.Resource, contents etcFileContents) { for _, r := range resources { suite.Create(r) } var ( expectedIDs []string unexpectedIDs []string ) if contents.resolvConf != "" { expectedIDs = append(expectedIDs, "resolv.conf") } else { unexpectedIDs = append(unexpectedIDs, "resolv.conf") } if contents.hosts != "" { expectedIDs = append(expectedIDs, "hosts") } else { unexpectedIDs = append(unexpectedIDs, "hosts") } ctest.AssertResources( suite, expectedIDs, func(r *files.EtcFileSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "hosts": asrt.Equal(contents.hosts, string(r.TypedSpec().Contents)) case "resolv.conf": asrt.Equal(contents.resolvConf, string(r.TypedSpec().Contents)) } }, ) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { if contents.resolvGlobalConf == "" { _, err := os.Lstat(suite.podResolvConfPath) switch { case err == nil: return retry.ExpectedErrorf("unexpected pod %s", suite.podResolvConfPath) case errors.Is(err, os.ErrNotExist): return nil default: return err } } file, err := os.ReadFile(suite.podResolvConfPath) switch { case errors.Is(err, os.ErrNotExist): return retry.ExpectedErrorf("missing pod %s", suite.podResolvConfPath) case err != nil: return err case len(file) == 0: return retry.ExpectedErrorf("empty pod %s", suite.podResolvConfPath) default: suite.Assert().Equal(contents.resolvGlobalConf, string(file)) return nil } }), ) for _, id := range unexpectedIDs { ctest.AssertNoResource[*files.EtcFileSpec](suite, id) } } func (suite *EtcFileConfigSuite) TestComplete() { suite.resolverStatus.TypedSpec().SearchDomains = []string{"foo.example.com"} suite.testFiles( []resource.Resource{suite.cfg, suite.defaultAddress, suite.hostnameStatus, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n10.0.0.1 a b\n10.0.0.2 c d\n", //nolint:lll resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n", resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n", }, ) } func (suite *EtcFileConfigSuite) TestExtraHostsNoHostname() { suite.resolverStatus.TypedSpec().SearchDomains = []string{"foo.example.com"} suite.testFiles( []resource.Resource{suite.cfg, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n10.0.0.1 a b\n10.0.0.2 c d\n", resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n", resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n", }, ) } func (suite *EtcFileConfigSuite) TestNoExtraHosts() { suite.resolverStatus.TypedSpec().SearchDomains = []string{"foo.example.com"} suite.testFiles( []resource.Resource{suite.defaultAddress, suite.hostnameStatus, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "nameserver 127.0.0.53\n\nsearch foo.example.com\n", resolvGlobalConf: "nameserver 169.254.116.108\n\nsearch foo.example.com\n", }, ) } func (suite *EtcFileConfigSuite) TestNoSearchDomainLegacy() { cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkDisableSearchDomain: new(true), }, }, }, ), ) suite.testFiles( []resource.Resource{cfg, suite.defaultAddress, suite.hostnameStatus, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "nameserver 127.0.0.53\n", resolvGlobalConf: "nameserver 169.254.116.108\n", }, ) } func (suite *EtcFileConfigSuite) TestNoSearchDomainNewStyle() { hc := networkcfg.NewResolverConfigV1Alpha1() hc.ResolverSearchDomains = networkcfg.SearchDomainsConfig{ SearchDisableDefault: new(true), } ctr, err := container.New(hc) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.testFiles( []resource.Resource{cfg, suite.defaultAddress, suite.hostnameStatus, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "nameserver 127.0.0.53\n", resolvGlobalConf: "nameserver 169.254.116.108\n", }, ) } func (suite *EtcFileConfigSuite) TestNoDomainname() { suite.hostnameStatus.TypedSpec().Domainname = "" suite.testFiles( []resource.Resource{suite.defaultAddress, suite.hostnameStatus, suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "nameserver 127.0.0.53\n", resolvGlobalConf: "nameserver 169.254.116.108\n", }, ) } func (suite *EtcFileConfigSuite) TestOnlyResolvers() { suite.testFiles( []resource.Resource{suite.resolverStatus, suite.hostDNSConfig}, etcFileContents{ hosts: "127.0.0.1 localhost\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "nameserver 127.0.0.53\n", resolvGlobalConf: "nameserver 169.254.116.108\n", }, ) } func (suite *EtcFileConfigSuite) TestOnlyHostname() { suite.testFiles( []resource.Resource{suite.defaultAddress, suite.hostnameStatus}, etcFileContents{ hosts: "127.0.0.1 localhost\n33.11.22.44 foo.example.com foo\n::1 localhost ip6-localhost ip6-loopback\nff02::1 ip6-allnodes\nff02::2 ip6-allrouters\n", resolvConf: "", resolvGlobalConf: "", }, ) } func (suite *EtcFileConfigSuite) ExtraTearDown() { if _, err := os.Lstat(suite.podResolvConfPath); err == nil { if suite.etcRoot.FSType() == "os" { suite.Require().NoError(os.Remove(suite.podResolvConfPath)) } else { suite.Require().NoError(mount.SafeUnmount(context.Background(), nil, suite.podResolvConfPath, false)) } } if suite.etcRoot != nil { suite.Require().NoError(os.RemoveAll(suite.bindMountTarget)) suite.etcRoot.Close() //nolint:errcheck } } func TestEtcFileConfigSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } s := &EtcFileConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, }, } s.AfterSetup = func(*ctest.DefaultSuite) { s.ExtraSetup() } s.AfterTearDown = func(*ctest.DefaultSuite) { s.ExtraTearDown() } suite.Run(t, s) } ================================================ FILE: internal/app/machined/pkg/controllers/network/ethernet_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" configtypes "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EthernetConfigController manages network.EthernetSpec based on machine configuration. type EthernetConfigController struct{} // Name implements controller.Controller interface. func (ctrl *EthernetConfigController) Name() string { return "network.EthernetConfigController" } // Inputs implements controller.Controller interface. func (ctrl *EthernetConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EthernetConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.EthernetSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *EthernetConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error reading machine configuration: %w", err) } if cfg != nil { if err = ctrl.apply(ctx, r, cfg.Config().EthernetConfigs()); err != nil { return fmt.Errorf("error applying EthernetSpec: %w", err) } } if err = safe.CleanupOutputs[*network.EthernetSpec](ctx, r); err != nil { return fmt.Errorf("error cleaning up EthernetSpec: %w", err) } } } func (ctrl *EthernetConfigController) apply(ctx context.Context, r controller.Runtime, configs []configtypes.EthernetConfig) error { for _, cfg := range configs { if err := safe.WriterModify(ctx, r, network.NewEthernetSpec(network.NamespaceName, cfg.Name()), func(spec *network.EthernetSpec) error { spec.TypedSpec().Rings = network.EthernetRingsSpec(cfg.Rings()) spec.TypedSpec().Channels = network.EthernetChannelsSpec(cfg.Channels()) spec.TypedSpec().Features = cfg.Features() spec.TypedSpec().WakeOnLAN = cfg.WakeOnLAN() return nil }); err != nil { return fmt.Errorf("error writing EthernetSpec: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/ethernet_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type EthernetConfigSuite struct { ctest.DefaultSuite } func (suite *EthernetConfigSuite) TestReconcile() { cfg1 := networkcfg.NewEthernetConfigV1Alpha1("enp0s1") cfg1.ChannelsConfig = &networkcfg.EthernetChannelsConfig{ RX: new(uint32(4)), } ctr, err := container.New(cfg1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResource(suite, "enp0s1", func(spec *network.EthernetSpec, asrt *assert.Assertions) { asrt.Equal(uint32(4), pointer.SafeDeref(spec.TypedSpec().Channels.RX)) }) cfg2 := networkcfg.NewEthernetConfigV1Alpha1("enp0s2") cfg2.FeaturesConfig = map[string]bool{ "tx-checksum-ipv4": true, } cfg2.RingsConfig = &networkcfg.EthernetRingsConfig{ RX: new(uint32(16)), } ctr, err = container.New(cfg1, cfg2) suite.Require().NoError(err) cfgNew := config.NewMachineConfig(ctr) cfgNew.Metadata().SetVersion(cfg.Metadata().Version()) suite.Update(cfgNew) ctest.AssertResource(suite, "enp0s1", func(spec *network.EthernetSpec, asrt *assert.Assertions) { asrt.Equal(uint32(4), pointer.SafeDeref(spec.TypedSpec().Channels.RX)) }) ctest.AssertResource(suite, "enp0s2", func(spec *network.EthernetSpec, asrt *assert.Assertions) { asrt.Equal(uint32(16), pointer.SafeDeref(spec.TypedSpec().Rings.RX)) asrt.Equal(true, spec.TypedSpec().Features["tx-checksum-ipv4"]) }) suite.Destroy(cfgNew) ctest.AssertNoResource[*network.EthernetSpec](suite, "enp0s1") ctest.AssertNoResource[*network.EthernetSpec](suite, "enp0s2") } func TestEthernetConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &EthernetConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.EthernetConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/ethernet_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/mdlayher/ethtool" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EthernetSpecController reports Ethernet link statuses. type EthernetSpecController struct{} // Name implements controller.Controller interface. func (ctrl *EthernetSpecController) Name() string { return "network.EthernetSpecController" } // Inputs implements controller.Controller interface. func (ctrl *EthernetSpecController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *EthernetSpecController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *EthernetSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy, which implies that all link renames are done if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.EthernetSpecType, Kind: controller.InputWeak, }, }, ); err != nil { return err } ethClient, err := ethtool.New() if err != nil { logger.Warn("error dialing ethtool socket", zap.Error(err)) return nil } defer ethClient.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } specs, err := safe.ReaderListAll[*network.EthernetSpec](ctx, r) if err != nil { return fmt.Errorf("error reading EthernetSpec resources: %w", err) } var errs error for spec := range specs.All() { if err = ctrl.apply(ethClient, spec); err != nil { errs = errors.Join(errs, fmt.Errorf("error configuring %q: %w", spec.Metadata().ID(), err)) } } if errs != nil { return fmt.Errorf("failed to reconcile Ethernet specs: %w", errs) } r.ResetRestartBackoff() } } func optionalFromPtr[T any](ptr *T) optional.Optional[T] { if ptr == nil { return optional.None[T]() } return optional.Some(*ptr) } func (ctrl *EthernetSpecController) apply( ethClient *ethtool.Client, spec *network.EthernetSpec, ) error { ringSpec := spec.TypedSpec().Rings if !value.IsZero(ringSpec) { if err := ethClient.SetRings(ethtool.Rings{ Interface: ethtool.Interface{ Name: spec.Metadata().ID(), }, RX: optionalFromPtr(ringSpec.RX), RXMini: optionalFromPtr(ringSpec.RXMini), RXJumbo: optionalFromPtr(ringSpec.RXJumbo), TX: optionalFromPtr(ringSpec.TX), RXBufLen: optionalFromPtr(ringSpec.RXBufLen), CQESize: optionalFromPtr(ringSpec.CQESize), TXPush: optionalFromPtr(ringSpec.TXPush), RXPush: optionalFromPtr(ringSpec.RXPush), TXPushBufLen: optionalFromPtr(ringSpec.TXPushBufLen), TCPDataSplit: optionalFromPtr(ringSpec.TCPDataSplit), }); err != nil { return fmt.Errorf("error updating rings: %w", err) } } featureSpec := spec.TypedSpec().Features if len(featureSpec) > 0 { if err := ethClient.SetFeatures( ethtool.Interface{ Name: spec.Metadata().ID(), }, featureSpec, ); err != nil { return fmt.Errorf("error updating features: %w", err) } } channelsSpec := spec.TypedSpec().Channels if !value.IsZero(channelsSpec) { if err := ethClient.SetChannels(ethtool.Channels{ Interface: ethtool.Interface{ Name: spec.Metadata().ID(), }, RXCount: optionalFromPtr(channelsSpec.RX), TXCount: optionalFromPtr(channelsSpec.TX), OtherCount: optionalFromPtr(channelsSpec.Other), CombinedCount: optionalFromPtr(channelsSpec.Combined), }); err != nil { return fmt.Errorf("error updating channels: %w", err) } } if spec.TypedSpec().WakeOnLAN != nil { var wolModes nethelpers.WOLMode for _, mode := range spec.TypedSpec().WakeOnLAN { wolModes |= mode } if err := ethClient.SetWakeOnLAN(ethtool.WakeOnLAN{ Interface: ethtool.Interface{ Name: spec.Metadata().ID(), }, Modes: ethtool.WOLMode(wolModes), }); err != nil { return fmt.Errorf("error updating wake-on-lan: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/ethernet_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "os" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/mdlayher/ethtool" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EthernetStatusController reports Ethernet link statuses. type EthernetStatusController struct{} // Name implements controller.Controller interface. func (ctrl *EthernetStatusController) Name() string { return "network.EthernetStatusController" } // Inputs implements controller.Controller interface. func (ctrl *EthernetStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *EthernetStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: network.EthernetStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *EthernetStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy, which implies that all link renames are done if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.LinkSpecType, Kind: controller.InputWeak, }, }, ); err != nil { return err } // create watch connections to ethtool via genetlink // these connections are used only to join multicast groups and receive notifications on changes // other connections are used to send requests and receive responses, as we can't mix the notifications and request/responses ethtoolWatcher, err := watch.NewEthtool(watch.NewDefaultRateLimitedTrigger(ctx, r)) if err != nil { logger.Warn("ethtool watcher failed to start", zap.Error(err)) return nil } defer ethtoolWatcher.Done() ethClient, err := ethtool.New() if err != nil { logger.Warn("error dialing ethtool socket", zap.Error(err)) return nil } defer ethClient.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() if err = ctrl.reconcile(ctx, r, logger, ethClient); err != nil { return err } if err = safe.CleanupOutputs[*network.EthernetStatus](ctx, r); err != nil { return err } } } // reconcile function runs for every reconciliation loop querying the ethtool state and updating resources. // //nolint:gocyclo,cyclop func (ctrl *EthernetStatusController) reconcile( ctx context.Context, r controller.Runtime, logger *zap.Logger, ethClient *ethtool.Client, ) error { linkInfos, err := ethClient.LinkInfos() if err != nil { return fmt.Errorf("error listing links: %w", err) } for _, linkInfo := range linkInfos { iface := linkInfo.Interface lgger := logger.With(zap.String("interface", iface.Name)) linkState, err := ethClient.LinkState(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting link state", zap.Error(err)) } linkMode, err := ethClient.LinkMode(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting link mode", zap.Error(err)) } rings, err := ethClient.Rings(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting rings", zap.Error(err)) } features, err := ethClient.Features(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting features", zap.Error(err)) } channels, err := ethClient.Channels(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting channels", zap.Error(err)) } wolMode, err := ethClient.WakeOnLAN(iface) if err != nil && !errors.Is(err, os.ErrNotExist) { lgger.Warn("error getting Wake-on-LAN", zap.Error(err)) } if err := safe.WriterModify(ctx, r, network.NewEthernetStatus(network.NamespaceName, iface.Name), func(res *network.EthernetStatus) error { res.TypedSpec().Port = nethelpers.Port(linkInfo.Port) if linkMode != nil { res.TypedSpec().Duplex = nethelpers.Duplex(linkMode.Duplex) res.TypedSpec().OurModes = xslices.Map(linkMode.Ours, func(m ethtool.AdvertisedLinkMode) string { return m.Name }) res.TypedSpec().PeerModes = xslices.Map(linkMode.Peer, func(m ethtool.AdvertisedLinkMode) string { return m.Name }) } else { res.TypedSpec().Duplex = nethelpers.Duplex(0) } if linkState == nil { res.TypedSpec().LinkState = nil } else { res.TypedSpec().LinkState = new(linkState.Link) } if rings == nil { res.TypedSpec().Rings = nil } else { res.TypedSpec().Rings = &network.EthernetRingsStatus{ RXMax: rings.RXMax.Ptr(), RXMiniMax: rings.RXMiniMax.Ptr(), RXJumboMax: rings.RXJumboMax.Ptr(), TXMax: rings.TXMax.Ptr(), TXPushBufLenMax: rings.TXPushBufLenMax.Ptr(), RX: rings.RX.Ptr(), RXMini: rings.RXMini.Ptr(), RXJumbo: rings.RXJumbo.Ptr(), TX: rings.TX.Ptr(), RXBufLen: rings.RXBufLen.Ptr(), CQESize: rings.CQESize.Ptr(), TXPush: rings.TXPush.Ptr(), RXPush: rings.RXPush.Ptr(), TXPushBufLen: rings.TXPushBufLen.Ptr(), TCPDataSplit: rings.TCPDataSplit.Ptr(), } } if features == nil { res.TypedSpec().Features = nil } else { res.TypedSpec().Features = xslices.Map(features, func(f ethtool.FeatureInfo) network.EthernetFeatureStatus { return network.EthernetFeatureStatus{ Name: f.Name, Status: f.State() + f.Suffix(), } }) } if channels == nil { res.TypedSpec().Channels = nil } else { res.TypedSpec().Channels = &network.EthernetChannelsStatus{ RXMax: channels.RXMax.Ptr(), TXMax: channels.TXMax.Ptr(), OtherMax: channels.OtherMax.Ptr(), CombinedMax: channels.CombinedMax.Ptr(), RX: channels.RXCount.Ptr(), TX: channels.TXCount.Ptr(), Other: channels.OtherCount.Ptr(), Combined: channels.CombinedCount.Ptr(), } } res.TypedSpec().WakeOnLAN = nil if wolMode != nil { for mode := nethelpers.WOLModeMin; mode <= nethelpers.WOLModeMax; mode <<= 1 { if (nethelpers.WOLMode(wolMode.Modes) & mode) == mode { res.TypedSpec().WakeOnLAN = append(res.TypedSpec().WakeOnLAN, mode) } } } return nil }); err != nil { return fmt.Errorf("error updating EthernetStatus resource: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/generic_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" ) type genericMergeFunc[T typed.DeepCopyable[T], E typed.Extension] func(logger *zap.Logger, in safe.List[*typed.Resource[T, E]]) map[resource.ID]*T // GenericMergeController initializes a generic merge controller for network resources. func GenericMergeController[T typed.DeepCopyable[T], E typed.Extension](namespaceIn, namespaceOut resource.Namespace, mergeFunc genericMergeFunc[T, E]) controller.Controller { var zeroE E controllerName := strings.ReplaceAll(zeroE.ResourceDefinition().Type, "Spec", "MergeController") return &genericMergeController[T, E]{ controllerName: controllerName, resourceType: zeroE.ResourceDefinition().Type, namespaceIn: namespaceIn, namespaceOut: namespaceOut, mergeFunc: mergeFunc, } } type genericMergeController[T typed.DeepCopyable[T], E typed.Extension] struct { controllerName string resourceType resource.Type namespaceIn resource.Namespace namespaceOut resource.Namespace mergeFunc genericMergeFunc[T, E] } func (ctrl *genericMergeController[T, E]) Name() string { return ctrl.controllerName } func (ctrl *genericMergeController[T, E]) Inputs() []controller.Input { return []controller.Input{ { Namespace: ctrl.namespaceIn, Type: ctrl.resourceType, Kind: controller.InputWeak, }, { Namespace: ctrl.namespaceOut, Type: ctrl.resourceType, Kind: controller.InputDestroyReady, }, } } func (ctrl *genericMergeController[T, E]) Outputs() []controller.Output { return []controller.Output{ { Type: ctrl.resourceType, Kind: controller.OutputShared, }, } } //nolint:gocyclo func (ctrl *genericMergeController[T, E]) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } type R = typed.Resource[T, E] // list source network configuration resources in, err := safe.ReaderList[*R](ctx, r, resource.NewMetadata(ctrl.namespaceIn, ctrl.resourceType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing source network resources: %w", err) } merged := ctrl.mergeFunc(logger, in) // cleanup resources, detecting conflicts on the way out, err := safe.ReaderList[*R](ctx, r, resource.NewMetadata(ctrl.namespaceOut, ctrl.resourceType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing output resources: %w", err) } for res := range out.All() { shouldBeDestroyed := false if _, ok := merged[res.Metadata().ID()]; !ok { shouldBeDestroyed = true } isTearingDown := res.Metadata().Phase() == resource.PhaseTearingDown if shouldBeDestroyed || isTearingDown { var okToDestroy bool okToDestroy, err = r.Teardown(ctx, res.Metadata()) if err != nil { return fmt.Errorf("error cleaning up addresses: %w", err) } if okToDestroy { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up addresses: %w", err) } } else if !shouldBeDestroyed { // resource is not ready to be destroyed yet, skip it delete(merged, res.Metadata().ID()) } } } var zeroT T for id, spec := range merged { if err = safe.WriterModify(ctx, r, typed.NewResource[T, E](resource.NewMetadata(ctrl.namespaceOut, ctrl.resourceType, id, resource.VersionUndefined), zeroT), func(r *R) error { *r.TypedSpec() = *spec return nil }); err != nil { return fmt.Errorf("error updating resource: %w", err) } logger.Debug("merged spec", zap.String("id", id), zap.Any("spec", spec)) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/hardware_addr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // HardwareAddrController manages secrets.Etcd based on configuration. type HardwareAddrController struct{} // Name implements controller.Controller interface. func (ctrl *HardwareAddrController) Name() string { return "network.HardwareAddrController" } // Inputs implements controller.Controller interface. func (ctrl *HardwareAddrController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *HardwareAddrController) Outputs() []controller.Output { return []controller.Output{ { Type: network.HardwareAddrType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *HardwareAddrController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list the existing HardwareAddr resources and mark them all to be deleted, as the actual link is discovered via netlink, resource ID is removed from the list list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.HardwareAddrType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } itemsToDelete := map[resource.ID]struct{}{} for _, r := range list.Items { itemsToDelete[r.Metadata().ID()] = struct{}{} } // list links and find the first physical link links, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range links.Items { link := res.(*network.LinkStatus) //nolint:forcetypeassert if !link.TypedSpec().Physical() { continue } if err = safe.WriterModify(ctx, r, network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr), func(r *network.HardwareAddr) error { spec := r.TypedSpec() spec.HardwareAddr = link.TypedSpec().HardwareAddr spec.Name = link.Metadata().ID() return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } delete(itemsToDelete, network.FirstHardwareAddr) // as link status are listed in sorted order, first physical link in the list is the one we need break } for id := range itemsToDelete { if err = r.Destroy(ctx, resource.NewMetadata(network.NamespaceName, network.HardwareAddrType, id, resource.VersionUndefined)); err != nil { return fmt.Errorf("error deleting resource %q: %w", id, err) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/hardware_addr_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type HardwareAddrSuite struct { ctest.DefaultSuite } func (suite *HardwareAddrSuite) TestFirst() { mustParseMAC := func(addr string) nethelpers.HardwareAddr { mac, err := net.ParseMAC(addr) suite.Require().NoError(err) return nethelpers.HardwareAddr(mac) } eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().Type = nethelpers.LinkEther eth0.TypedSpec().HardwareAddr = mustParseMAC("56:a0:a0:87:1c:fa") eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().Type = nethelpers.LinkEther eth1.TypedSpec().HardwareAddr = mustParseMAC("6a:2b:bd:b2:fc:e0") bond0 := network.NewLinkStatus(network.NamespaceName, "bond0") bond0.TypedSpec().Type = nethelpers.LinkEther bond0.TypedSpec().Kind = "bond" bond0.TypedSpec().HardwareAddr = mustParseMAC("56:a0:a0:87:1c:fb") suite.Create(bond0) suite.Create(eth1) ctest.AssertResource( suite, network.FirstHardwareAddr, func(r *network.HardwareAddr, asrt *assert.Assertions) { asrt.Equal(eth1.Metadata().ID(), r.TypedSpec().Name) asrt.Equal("6a:2b:bd:b2:fc:e0", net.HardwareAddr(r.TypedSpec().HardwareAddr).String()) }, ) suite.Create(eth0) ctest.AssertResource( suite, network.FirstHardwareAddr, func(r *network.HardwareAddr, asrt *assert.Assertions) { asrt.Equal(eth0.Metadata().ID(), r.TypedSpec().Name) asrt.Equal("56:a0:a0:87:1c:fa", net.HardwareAddr(r.TypedSpec().HardwareAddr).String()) }, ) suite.Destroy(eth0) suite.Destroy(eth1) ctest.AssertNoResource[*network.HardwareAddr](suite, network.FirstHardwareAddr) } func TestHardwareAddrSuite(t *testing.T) { t.Parallel() suite.Run(t, &HardwareAddrSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HardwareAddrController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostdns_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // HostDNSConfigController manages network.HostDNSConfig based on machine configuration. type HostDNSConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *HostDNSConfigController) Name() string { return "network.HostDNSConfigController" } // Inputs implements controller.Controller interface. func (ctrl *HostDNSConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *HostDNSConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.HostDNSConfigType, Kind: controller.OutputExclusive, }, { Type: network.AddressSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *HostDNSConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var cfgProvider talosconfig.Config r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else if cfg.Config().Machine() != nil { cfgProvider = cfg.Config() } newServiceAddrs := make([]netip.Addr, 0, 2) if err := safe.WriterModify(ctx, r, network.NewHostDNSConfig(network.HostDNSConfigID), func(res *network.HostDNSConfig) error { res.TypedSpec().ListenAddresses = []netip.AddrPort{ netip.MustParseAddrPort("127.0.0.53:53"), } res.TypedSpec().ServiceHostDNSAddress = netip.Addr{} if cfgProvider == nil { res.TypedSpec().Enabled = false return nil } res.TypedSpec().Enabled = cfgProvider.Machine().Features().HostDNS().Enabled() res.TypedSpec().ResolveMemberNames = cfgProvider.Machine().Features().HostDNS().ResolveMemberNames() if !cfgProvider.Machine().Features().HostDNS().ForwardKubeDNSToHost() { return nil } if slices.ContainsFunc( cfgProvider.Cluster().Network().PodCIDRs(), func(cidr string) bool { return netip.MustParsePrefix(cidr).Addr().Is4() }, ) { parsed := netip.MustParseAddr(constants.HostDNSAddress) newServiceAddrs = append(newServiceAddrs, parsed) res.TypedSpec().ListenAddresses = append(res.TypedSpec().ListenAddresses, netip.AddrPortFrom(parsed, 53)) res.TypedSpec().ServiceHostDNSAddress = parsed } return nil }); err != nil { return fmt.Errorf("error writing host dns config: %w", err) } for _, newServiceAddr := range newServiceAddrs { err := updateSpec(ctx, r, newServiceAddr, logger) if err != nil { return err } } if err = safe.CleanupOutputs[*network.HostDNSConfig](ctx, r); err != nil { return err } } } func updateSpec(ctx context.Context, r controller.Runtime, newServiceAddr netip.Addr, logger *zap.Logger) error { newDNSAddrPrefix := netip.PrefixFrom(newServiceAddr, newServiceAddr.BitLen()) logger.Debug("creating new host dns address spec", zap.String("address", newServiceAddr.String())) err := safe.WriterModify( ctx, r, network.NewAddressSpec( network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID("lo", newDNSAddrPrefix)), ), func(r *network.AddressSpec) error { spec := r.TypedSpec() spec.Address = newDNSAddrPrefix spec.ConfigLayer = network.ConfigOperator if newServiceAddr.Is4() { spec.Family = nethelpers.FamilyInet4 } else { spec.Family = nethelpers.FamilyInet6 } spec.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent) spec.LinkName = "lo" if newServiceAddr.Is6() && newServiceAddr.IsPrivate() { spec.Scope = nethelpers.ScopeGlobal } else { spec.Scope = nethelpers.ScopeHost } return nil }, ) if err != nil { return fmt.Errorf("error modifying address: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "crypto/sha256" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/martinlindhe/base36" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // HostnameConfigController manages network.HostnameSpec based on machine configuration, kernel cmdline. type HostnameConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *HostnameConfigController) Name() string { return "network.HostnameConfigController" } // Inputs implements controller.Controller interface. func (ctrl *HostnameConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.NodeAddressDefaultID), Kind: controller.InputWeak, }, { Namespace: cluster.NamespaceName, Type: cluster.IdentityType, ID: optional.Some(cluster.LocalIdentity), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *HostnameConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.HostnameSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *HostnameConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() var cfgProvider talosconfig.Config cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { cfgProvider = cfg.Config() } var specs []network.HostnameSpecSpec // parse kernel cmdline for the default gateway cmdlineHostname := ctrl.parseCmdline(logger) if cmdlineHostname.Hostname != "" { specs = append(specs, cmdlineHostname) } // parse machine configuration for specs if cfgProvider != nil { configHostname := ctrl.parseMachineConfiguration(logger, cfgProvider) if configHostname.Hostname != "" { specs = append(specs, configHostname) } if cfgProvider.NetworkHostnameConfig() != nil { switch cfgProvider.NetworkHostnameConfig().AutoHostname() { case nethelpers.AutoHostnameKindOff: case nethelpers.AutoHostnameKindAddr: defaultAddr, err := safe.ReaderGet[*network.NodeAddress](ctx, r, resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.NodeAddressDefaultID, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { specs = append(specs, ctrl.getDefault(defaultAddr)) } case nethelpers.AutoHostnameKindStable: var identity *cluster.Identity identity, err = safe.ReaderGet[*cluster.Identity](ctx, r, resource.NewMetadata(cluster.NamespaceName, cluster.IdentityType, cluster.LocalIdentity, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting local identity: %w", err) } } else { nodeID := identity.TypedSpec().NodeID stableHostname := ctrl.getStableDefault(nodeID) specs = append(specs, *stableHostname) } } } } if err = ctrl.apply(ctx, r, specs); err != nil { return fmt.Errorf("error applying specs: %w", err) } if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.HostnameSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning up outputs: %w", err) } } } //nolint:dupl func (ctrl *HostnameConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.HostnameSpecSpec) error { for _, spec := range specs { id := network.LayeredID(spec.ConfigLayer, network.HostnameID) if err := safe.WriterModify( ctx, r, network.NewHostnameSpec(network.ConfigNamespaceName, id), func(r *network.HostnameSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return err } } return nil } func (ctrl *HostnameConfigController) getStableDefault(nodeID string) *network.HostnameSpecSpec { hashBytes := sha256.Sum256([]byte(nodeID)) b36 := strings.ToLower(base36.EncodeBytes(hashBytes[:8])) hostname := fmt.Sprintf("talos-%s-%s", b36[1:4], b36[4:7]) return &network.HostnameSpecSpec{ Hostname: hostname, ConfigLayer: network.ConfigDefault, } } func (ctrl *HostnameConfigController) getDefault(defaultAddr *network.NodeAddress) (spec network.HostnameSpecSpec) { if defaultAddr == nil || len(defaultAddr.TypedSpec().Addresses) != 1 { return spec } spec.Hostname = fmt.Sprintf("talos-%s", strings.ReplaceAll(strings.ReplaceAll(defaultAddr.TypedSpec().Addresses[0].Addr().String(), ":", ""), ".", "-")) spec.ConfigLayer = network.ConfigDefault return spec } func (ctrl *HostnameConfigController) parseCmdline(logger *zap.Logger) (spec network.HostnameSpecSpec) { if ctrl.Cmdline == nil { return spec } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, network.NewEmptyLinkResolver()) if err != nil { logger.Warn("ignoring error", zap.Error(err)) return spec } if settings.Hostname == "" { return spec } if err = spec.ParseFQDN(settings.Hostname); err != nil { logger.Warn("ignoring error", zap.Error(err)) return network.HostnameSpecSpec{} } spec.ConfigLayer = network.ConfigCmdline return spec } func (ctrl *HostnameConfigController) parseMachineConfiguration(logger *zap.Logger, cfgProvider talosconfig.Config) (spec network.HostnameSpecSpec) { var hostname string if cfgProvider.NetworkHostnameConfig() != nil { hostname = cfgProvider.NetworkHostnameConfig().Hostname() } if hostname == "" { return spec } if err := spec.ParseFQDN(hostname); err != nil { logger.Warn("ignoring error", zap.Error(err)) return network.HostnameSpecSpec{} } spec.ConfigLayer = network.ConfigMachineConfiguration return spec } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "net/url" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type HostnameConfigSuite struct { ctest.DefaultSuite } func (suite *HostnameConfigSuite) TestNoDefaultWithoutMachineConfig() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) defaultAddress := network.NewNodeAddress(network.NamespaceName, network.NodeAddressDefaultID) defaultAddress.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("33.11.22.44/32")} suite.Create(defaultAddress) ctest.AssertNoResource[*network.HostnameSpec](suite, "default/hostname", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestDefaultIPBasedHostname() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) suite.Create(config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ConfigVersion: "v1alpha1"}))) defaultAddress := network.NewNodeAddress(network.NamespaceName, network.NodeAddressDefaultID) defaultAddress.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("33.11.22.44/32")} suite.Create(defaultAddress) ctest.AssertResource(suite, "default/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("talos-33-11-22-44", r.TypedSpec().Hostname) asrt.Equal("", r.TypedSpec().Domainname) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestDefaultStableHostname() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) suite.Create(config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ StableHostname: new(true), }, }, }, ), )) id := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) id.TypedSpec().NodeID = "fGdOI05hVrx3YMagLo0Bwxa2Nm9BAswWm8XLeEj0aS4" suite.Create(id) ctest.AssertResource(suite, "default/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("talos-hwz-sw5", r.TypedSpec().Hostname) asrt.Equal("", r.TypedSpec().Domainname) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.HostnameConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1.domain.tld:eth1::10.0.0.1:10.0.0.2:10.0.0.1"), }, ), ) ctest.AssertResource(suite, "cmdline/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("master1", r.TypedSpec().Hostname) asrt.Equal("domain.tld", r.TypedSpec().Domainname) asrt.Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestLegacyMachineConfiguration() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkHostname: "foo", }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResource( suite, "configuration/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("foo", r.TypedSpec().Hostname) asrt.Equal("", r.TypedSpec().Domainname) asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error { r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NetworkHostname = strings.Repeat("a", 128) //nolint:staticcheck // using legacy field in the test return nil }) suite.Require().NoError(err) ctest.AssertNoResource[*network.HostnameSpec](suite, "configuration/hostname", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestMachineConfigurationStaticHostname() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) hostnameCfg := networkcfg.NewHostnameConfigV1Alpha1() hostnameCfg.ConfigAuto = new(nethelpers.AutoHostnameKindOff) hostnameCfg.ConfigHostname = "my-hostname" ctr, err := container.New(hostnameCfg) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResource( suite, "configuration/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("my-hostname", r.TypedSpec().Hostname) asrt.Equal("", r.TypedSpec().Domainname) asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertNoResource[*network.HostnameSpec](suite, "default/hostname", rtestutils.WithNamespace(network.ConfigNamespaceName)) suite.Destroy(cfg) ctest.AssertNoResource[*network.HostnameSpec](suite, "configuration/hostname", rtestutils.WithNamespace(network.ConfigNamespaceName)) ctest.AssertNoResource[*network.HostnameSpec](suite, "default/hostname", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *HostnameConfigSuite) TestMachineConfigurationDefaultStable() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.HostnameConfigController{})) hostnameCfg := networkcfg.NewHostnameConfigV1Alpha1() hostnameCfg.ConfigAuto = new(nethelpers.AutoHostnameKindStable) ctr, err := container.New(hostnameCfg) suite.Require().NoError(err) id := cluster.NewIdentity(cluster.NamespaceName, cluster.LocalIdentity) id.TypedSpec().NodeID = "fGdOI05hVrx3YMagLo0Bwxa2Nm9BAswWm8XLeEj0aS4" suite.Create(id) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResource(suite, "default/hostname", func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("talos-hwz-sw5", r.TypedSpec().Hostname) asrt.Equal("", r.TypedSpec().Domainname) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func TestHostnameConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &HostnameConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewHostnameMergeController initializes a HostnameMergeController. // // HostnameMergeController merges network.HostnameSpec in network.ConfigNamespace and produces final network.HostnameSpec in network.Namespace. func NewHostnameMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.HostnameSpec]) map[resource.ID]*network.HostnameSpecSpec { // simply merge by layers, overriding with the next configuration layer var final network.HostnameSpecSpec for spec := range list.All() { if final.Hostname != "" && spec.TypedSpec().ConfigLayer <= final.ConfigLayer { // skip this spec, as existing one is higher layer continue } final = *spec.TypedSpec() } if final.Hostname != "" { return map[resource.ID]*network.HostnameSpecSpec{ network.HostnameID: &final, } } return nil }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type HostnameMergeSuite struct { ctest.DefaultSuite } func (suite *HostnameMergeSuite) assertHostnames(requiredIDs []string, check func(*network.HostnameSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *HostnameMergeSuite) TestMerge() { def := network.NewHostnameSpec(network.ConfigNamespaceName, "default/hostname") *def.TypedSpec() = network.HostnameSpecSpec{ Hostname: "foo", Domainname: "tld", ConfigLayer: network.ConfigDefault, } dhcp1 := network.NewHostnameSpec(network.ConfigNamespaceName, "dhcp/eth0") *dhcp1.TypedSpec() = network.HostnameSpecSpec{ Hostname: "eth-0", ConfigLayer: network.ConfigOperator, } dhcp2 := network.NewHostnameSpec(network.ConfigNamespaceName, "dhcp/eth1") *dhcp2.TypedSpec() = network.HostnameSpecSpec{ Hostname: "eth-1", ConfigLayer: network.ConfigOperator, } static := network.NewHostnameSpec(network.ConfigNamespaceName, "configuration/hostname") *static.TypedSpec() = network.HostnameSpecSpec{ Hostname: "bar", Domainname: "com", ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{def, dhcp1, dhcp2, static} { suite.Create(res) } suite.assertHostnames( []string{ "hostname", }, func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("bar.com", r.TypedSpec().FQDN()) asrt.Equal("bar", r.TypedSpec().Hostname) asrt.Equal("com", r.TypedSpec().Domainname) }, ) suite.Destroy(static) suite.assertHostnames( []string{ "hostname", }, func(r *network.HostnameSpec, asrt *assert.Assertions) { asrt.Equal("eth-0", r.TypedSpec().FQDN()) }, ) } func TestHostnameMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &HostnameMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewHostnameMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "golang.org/x/sys/unix" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // HostnameSpecController applies network.HostnameSpec to the actual interfaces. type HostnameSpecController struct { V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *HostnameSpecController) Name() string { return "network.HostnameSpecController" } // Inputs implements controller.Controller interface. func (ctrl *HostnameSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.HostnameSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *HostnameSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: network.HostnameStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *HostnameSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source network configuration resources list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.HostnameSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing source network addresses: %w", err) } // add finalizers for all live resources for _, res := range list.Items { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // loop over specs and sync to statuses for _, res := range list.Items { spec := res.(*network.HostnameSpec) //nolint:forcetypeassert switch spec.Metadata().Phase() { case resource.PhaseTearingDown: if err = r.Destroy(ctx, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, spec.Metadata().ID(), resource.VersionUndefined)); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying status: %w", err) } if err = r.RemoveFinalizer(ctx, spec.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: if err = safe.WriterModify(ctx, r, network.NewHostnameStatus(network.NamespaceName, spec.Metadata().ID()), func(status *network.HostnameStatus) error { status.TypedSpec().Hostname = spec.TypedSpec().Hostname status.TypedSpec().Domainname = spec.TypedSpec().Domainname return nil }); err != nil { return fmt.Errorf("error modifying status: %w", err) } // apply hostname unless running in container mode if ctrl.V1Alpha1Mode != v1alpha1runtime.ModeContainer { logger.Info("setting hostname", zap.String("hostname", spec.TypedSpec().Hostname), zap.String("domainname", spec.TypedSpec().Domainname)) if err = unix.Sethostname([]byte(spec.TypedSpec().Hostname)); err != nil { return fmt.Errorf("error setting hostname: %w", err) } if err = unix.Setdomainname([]byte(spec.TypedSpec().Domainname)); err != nil { return fmt.Errorf("error setting domainname: %w", err) } } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/hostname_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "context" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type HostnameSpecSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *HostnameSpecSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError( suite.runtime.RegisterController( &netctrl.HostnameSpecController{ V1Alpha1Mode: v1alpha1runtime.ModeContainer, // run in container mode to skip _actually_ setting hostname }, ), ) suite.startRuntime() } func (suite *HostnameSpecSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *HostnameSpecSuite) assertStatus(id string, fqdn string) error { r, err := suite.state.Get( suite.ctx, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, id, resource.VersionUndefined), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } status := r.(*network.HostnameStatus) //nolint:forcetypeassert if status.TypedSpec().FQDN() != fqdn { return retry.ExpectedErrorf("fqdn mismatch: %q != %q", status.TypedSpec().FQDN(), fqdn) } return nil } func (suite *HostnameSpecSuite) TestSpec() { spec := network.NewHostnameSpec(network.NamespaceName, "hostname") *spec.TypedSpec() = network.HostnameSpecSpec{ Hostname: "foo", Domainname: "bar", ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{spec} { suite.Require().NoError(suite.state.Create(suite.ctx, res), "%v", res.Spec()) } suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertStatus("hostname", "foo.bar") }, ), ) } func (suite *HostnameSpecSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestHostnameSpecSuite(t *testing.T) { suite.Run(t, new(HostnameSpecSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/addressutil.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package addressutil contains helpers working with addresses. package addressutil import "net/netip" // DeduplicateIPPrefixes removes duplicates from the given list of prefixes. // // The input list must be sorted. // DeduplicateIPPrefixes performs in-place deduplication. func DeduplicateIPPrefixes(in []netip.Prefix) []netip.Prefix { // assumes that current is sorted n := 0 var prev netip.Prefix for _, x := range in { if prev != x { in[n] = x n++ } prev = x } return in[:n] } // FilterIPs filters the given list of IP prefixes based on the given include and exclude subnets. // // If includeSubnets is not empty, only IPs that are in one of the subnets are included. // If excludeSubnets is not empty, IPs that are in one of the subnets are excluded. func FilterIPs(addrs []netip.Prefix, includeSubnets, excludeSubnets []netip.Prefix) []netip.Prefix { result := make([]netip.Prefix, 0, len(addrs)) outer: for _, ip := range addrs { if len(includeSubnets) > 0 { matchesAny := false for _, subnet := range includeSubnets { if subnet.Contains(ip.Addr()) { matchesAny = true break } } if !matchesAny { continue outer } } for _, subnet := range excludeSubnets { if subnet.Contains(ip.Addr()) { continue outer } } result = append(result, ip) } return result } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/addressutil_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package addressutil_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" ) func TestDeduplicateIPPrefixes(t *testing.T) { t.Parallel() for _, test := range []struct { name string in []netip.Prefix out []netip.Prefix }{ { name: "empty", }, { name: "single", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/32")}, out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, }, { name: "many", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/24"), netip.MustParsePrefix("2000::aebc/64"), netip.MustParsePrefix("2000::aebc/64")}, out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("1.2.3.4/24"), netip.MustParsePrefix("2000::aebc/64")}, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() got := addressutil.DeduplicateIPPrefixes(test.in) assert.Equal(t, test.out, got) }) } } // TestFilterIPs tests the FilterIPs function. func TestFilterIPs(t *testing.T) { t.Parallel() for _, test := range []struct { name string in []netip.Prefix include []netip.Prefix exclude []netip.Prefix out []netip.Prefix }{ { name: "empty filters", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")}, out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")}, }, { name: "v4 only", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")}, include: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}, out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, }, { name: "v6 only", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("2000::aebc/64")}, exclude: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}, out: []netip.Prefix{netip.MustParsePrefix("2000::aebc/64")}, }, { name: "include and exclude", in: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32"), netip.MustParsePrefix("3.4.5.6/24"), netip.MustParsePrefix("2000::aebc/64")}, include: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}, exclude: []netip.Prefix{netip.MustParsePrefix("3.0.0.0/8")}, out: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/32")}, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() got := addressutil.FilterIPs(test.in, test.include, test.exclude) assert.Equal(t, test.out, got) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/broadcast.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package addressutil import ( "net" "net/netip" "go4.org/netipx" ) // BroadcastAddr calculates the broadcast address for the given IPv4 prefix. // // If the address is not IPv4 or the prefix length is 31 or 32, nil is returned. func BroadcastAddr(addr netip.Prefix) net.IP { if !addr.Addr().Is4() { return nil } if addr.Bits() >= 31 { return nil } ipnet := netipx.PrefixIPNet(addr) ip := ipnet.IP.To4() if ip == nil { return nil } mask := net.IP(ipnet.Mask).To4() n := len(ip) if n != len(mask) { return nil } out := make(net.IP, n) for i := range n { out[i] = ip[i] | ^mask[i] } return out } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/broadcast_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package addressutil_test import ( "net" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" ) func TestBroadcastAddr(t *testing.T) { t.Parallel() tests := []struct { name string prefix string want string wantNil bool }{ { name: "IPv4 /24 network", prefix: "10.255.255.231/24", want: "10.255.255.255", }, { name: "IPv4 /16 network", prefix: "192.168.0.1/16", want: "192.168.255.255", }, { name: "IPv4 /32 host route (VIP case)", prefix: "10.255.255.230/32", wantNil: true, // Should return nil, not set broadcast }, { name: "Another /32 host route", prefix: "192.168.1.100/32", wantNil: true, // Should return nil, not set broadcast }, { name: "IPv4 /31 point-to-point", prefix: "10.0.0.1/31", wantNil: true, // RFC 3021 - /31 is point-to-point, no broadcast }, { name: "IPv4 /8 network", prefix: "10.0.0.1/8", want: "10.255.255.255", }, { name: "IPv6 address (no broadcast)", prefix: "2001:db8::1/64", wantNil: true, // IPv6 doesn't have broadcast }, { name: "IPv6 /128 address", prefix: "2001:db8::1/128", wantNil: true, // IPv6 doesn't have broadcast }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() prefix, err := netip.ParsePrefix(tt.prefix) require.NoError(t, err) got := addressutil.BroadcastAddr(prefix) if tt.wantNil { assert.Nil(t, got, "expected nil broadcast for %s", tt.prefix) } else { assert.NotNil(t, got, "expected broadcast address for %s", tt.prefix) want := net.ParseIP(tt.want) assert.True(t, got.Equal(want), "expected %v, got %v for %s", want, got, tt.prefix) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/compare.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package addressutil import ( "cmp" "fmt" "net/netip" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // CompareByAlgorithm returns a comparison function based on the given algorithm. func CompareByAlgorithm(algorithm nethelpers.AddressSortAlgorithm) func(a, b netip.Prefix) int { switch algorithm { case nethelpers.AddressSortAlgorithmV1: return ComparePrefixesLegacy case nethelpers.AddressSortAlgorithmV2: return ComparePrefixNew } panic(fmt.Sprintf("unknown address sort algorithm: %s", algorithm)) } // ComparePrefixesLegacy is the old way to sort prefixes. // // It only compares addresses and does not take prefix length into account. func ComparePrefixesLegacy(a, b netip.Prefix) int { if c := a.Addr().Compare(b.Addr()); c != 0 { return c } // note: this was missing in the previous implementation, but this makes sorting stable return cmp.Compare(a.Bits(), b.Bits()) } func family(a netip.Prefix) int { if a.Addr().Is4() { return 4 } return 6 } // ComparePrefixNew compares two prefixes by address family, address, and prefix length. // // It prefers more specific prefixes. func ComparePrefixNew(a, b netip.Prefix) int { // (1): first, compare address families if c := cmp.Compare(family(a), family(b)); c != 0 { return c } // (2): if addresses are equal, Contains will report that one prefix contains the other, so compare prefix lengths if a.Addr() == b.Addr() { return -cmp.Compare(a.Bits(), b.Bits()) } // (3): if one prefix contains another, the more specific one should come first // but if both prefixes contain each other, proceed to compare addresses aContainsB := a.Contains(b.Addr()) bContainsA := b.Contains(a.Addr()) switch { case aContainsB && !bContainsA: return 1 case !aContainsB && bContainsA: return -1 } // (4): compare addresses, they are not equal at this point (see (2)) return a.Addr().Compare(b.Addr()) } // CompareAddressStatuses compares two address statuses with the prefix comparison func. // // The comparison of AddressStatuses sorts by link name and then by address. func CompareAddressStatuses(comparePrefixes func(a, b netip.Prefix) int) func(a, b *network.AddressStatus) int { return func(a, b *network.AddressStatus) int { if c := cmp.Compare(a.TypedSpec().LinkName, b.TypedSpec().LinkName); c != 0 { return c } return comparePrefixes(a.TypedSpec().Address, b.TypedSpec().Address) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/addressutil/compare_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package addressutil_test import ( "math/rand/v2" "net/netip" "slices" "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" ) func toNetip(prefixes ...string) []netip.Prefix { return xslices.Map(prefixes, netip.MustParsePrefix) } func toString(prefixes []netip.Prefix) []string { return xslices.Map(prefixes, netip.Prefix.String) } func TestCompare(t *testing.T) { t.Parallel() for _, test := range []struct { name string in []netip.Prefix outLegacy []netip.Prefix outNew []netip.Prefix }{ { name: "ipv4", in: toNetip("10.3.4.1/24", "10.3.4.5/24", "10.3.4.5/32", "1.2.3.4/26", "192.168.35.11/24", "192.168.36.10/24"), outLegacy: toNetip("1.2.3.4/26", "10.3.4.1/24", "10.3.4.5/24", "10.3.4.5/32", "192.168.35.11/24", "192.168.36.10/24"), outNew: toNetip("1.2.3.4/26", "10.3.4.5/32", "10.3.4.1/24", "10.3.4.5/24", "192.168.35.11/24", "192.168.36.10/24"), }, { name: "ipv6", in: toNetip("2001:db8::1/64", "2001:db8::1/128", "2001:db8::2/64", "2001:db8::2/128", "2001:db8::3/64", "2001:db8::3/128"), outLegacy: toNetip("2001:db8::1/64", "2001:db8::1/128", "2001:db8::2/64", "2001:db8::2/128", "2001:db8::3/64", "2001:db8::3/128"), outNew: toNetip("2001:db8::1/128", "2001:db8::2/128", "2001:db8::3/128", "2001:db8::1/64", "2001:db8::2/64", "2001:db8::3/64"), }, { name: "mixed", in: toNetip("fd01:cafe::5054:ff:fe1f:c7bd/64", "fd01:cafe::f14c:9fa1:8496:557f/128", "192.168.3.4/24", "10.5.0.0/16"), outLegacy: toNetip("10.5.0.0/16", "192.168.3.4/24", "fd01:cafe::5054:ff:fe1f:c7bd/64", "fd01:cafe::f14c:9fa1:8496:557f/128"), outNew: toNetip("10.5.0.0/16", "192.168.3.4/24", "fd01:cafe::f14c:9fa1:8496:557f/128", "fd01:cafe::5054:ff:fe1f:c7bd/64"), }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() // add more randomness to ensure the sorting is stable in := slices.Clone(test.in) rand.Shuffle(len(in), func(i, j int) { in[i], in[j] = in[j], in[i] }) legacy := slices.Clone(in) slices.SortFunc(legacy, addressutil.ComparePrefixesLegacy) assert.Equal(t, test.outLegacy, legacy, "expected %q but got %q", toString(test.outLegacy), toString(legacy)) newer := slices.Clone(in) slices.SortFunc(newer, addressutil.ComparePrefixNew) assert.Equal(t, test.outNew, newer, "expected %q but got %q", toString(test.outNew), toString(newer)) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/probe/probe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package probe contains implementation of the network probe runners. package probe import ( "context" "errors" "net" "sync" "syscall" "time" "github.com/siderolabs/gen/channel" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // Runner describes a state of running probe. type Runner struct { ID string Spec network.ProbeSpecSpec cancel context.CancelFunc wg sync.WaitGroup } // Notification of a runner status. type Notification struct { ID string Status network.ProbeStatusSpec } // Start a runner with a given context. func (runner *Runner) Start(ctx context.Context, notifyCh chan<- Notification, logger *zap.Logger) { runner.wg.Add(1) ctx, runner.cancel = context.WithCancel(ctx) go func() { defer runner.wg.Done() runner.run(ctx, notifyCh, logger) }() } // Stop a runner. func (runner *Runner) Stop() { runner.cancel() runner.wg.Wait() } // run a probe. // //nolint:gocyclo func (runner *Runner) run(ctx context.Context, notifyCh chan<- Notification, logger *zap.Logger) { logger = logger.With(zap.String("probe", runner.ID)) ticker := time.NewTicker(runner.Spec.Interval) defer ticker.Stop() consecutiveFailures := 0 firstIteration := true for { if !firstIteration { select { case <-ctx.Done(): return case <-ticker.C: } } else { firstIteration = false } err := runner.probe(ctx) if err == nil { if consecutiveFailures > 0 { logger.Info("probe succeeded") } consecutiveFailures = 0 if !channel.SendWithContext(ctx, notifyCh, Notification{ ID: runner.ID, Status: network.ProbeStatusSpec{ Success: true, }, }) { return } continue } if consecutiveFailures == runner.Spec.FailureThreshold { logger.Error("probe failed", zap.Error(err)) } consecutiveFailures++ if consecutiveFailures < runner.Spec.FailureThreshold { continue } if !channel.SendWithContext(ctx, notifyCh, Notification{ ID: runner.ID, Status: network.ProbeStatusSpec{ Success: false, LastError: err.Error(), }, }) { return } } } // probe runs a probe. func (runner *Runner) probe(ctx context.Context) error { var zeroTCP network.TCPProbeSpec switch { case runner.Spec.TCP != zeroTCP: return runner.probeTCP(ctx) default: return errors.New("no probe type specified") } } // probeTCP runs a TCP probe. func (runner *Runner) probeTCP(ctx context.Context) error { dialer := &net.Dialer{ // The dialer reduces the TIME-WAIT period to 1 seconds instead of the OS default of 60 seconds. Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { syscall.SetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, &syscall.Linger{Onoff: 1, Linger: 1}) //nolint: errcheck }) }, } ctx, cancel := context.WithTimeout(ctx, runner.Spec.TCP.Timeout) defer cancel() conn, err := dialer.DialContext(ctx, "tcp", runner.Spec.TCP.Endpoint) if err != nil { return err } return conn.Close() } ================================================ FILE: internal/app/machined/pkg/controllers/network/internal/probe/probe_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package probe_test import ( "context" "net/http" "net/http/httptest" "net/url" "testing" "testing/synctest" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/probe" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestProbeHTTP(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) t.Cleanup(server.Close) u, err := url.Parse(server.URL) require.NoError(t, err) p := probe.Runner{ ID: "test", Spec: network.ProbeSpecSpec{ Interval: 10 * time.Millisecond, TCP: network.TCPProbeSpec{ Endpoint: u.Host, Timeout: time.Second, }, }, } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) t.Cleanup(cancel) notifyCh := make(chan probe.Notification) p.Start(ctx, notifyCh, zaptest.NewLogger(t)) t.Cleanup(p.Stop) // probe should always succeed for range 3 { assert.Equal(t, probe.Notification{ ID: "test", Status: network.ProbeStatusSpec{ Success: true, }, }, <-notifyCh) } // stop the test server, probe should fail server.Close() for { notification := <-notifyCh if notification.Status.Success { continue } assert.Equal(t, "test", notification.ID) assert.False(t, notification.Status.Success) assert.Contains(t, notification.Status.LastError, "connection refused") break } } func TestProbeConsecutiveFailures(t *testing.T) { // Use synctest.Test to run the test in a controlled time bubble. // This allows us to test time-dependent behavior without actual delays, // making the test both faster and more deterministic. synctest.Test(t, func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })) defer server.Close() u, err := url.Parse(server.URL) require.NoError(t, err) p := probe.Runner{ ID: "consecutive-failures", Spec: network.ProbeSpecSpec{ Interval: 10 * time.Millisecond, FailureThreshold: 3, TCP: network.TCPProbeSpec{ Endpoint: u.Host, Timeout: time.Second, }, }, } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() notifyCh := make(chan probe.Notification) p.Start(ctx, notifyCh, zaptest.NewLogger(t)) defer p.Stop() // first iteration should succeed assert.Equal(t, probe.Notification{ ID: "consecutive-failures", Status: network.ProbeStatusSpec{ Success: true, }, }, <-notifyCh) // stop the test server, probe should fail server.Close() for range p.Spec.FailureThreshold - 1 { // probe should fail, but no notification should be sent yet (failure threshold not reached) // synctest.Wait() waits until all goroutines in the bubble are durably blocked, // which happens when the ticker in the probe runner is waiting for the next interval synctest.Wait() select { case ev := <-notifyCh: require.Fail(t, "unexpected notification", "got: %v", ev) default: // Expected: no notification yet } } // wait for next interval to trigger failure notification synctest.Wait() notify := <-notifyCh assert.Equal(t, "consecutive-failures", notify.ID) assert.False(t, notify.Status.Success) assert.Contains(t, notify.Status.LastError, "connection refused") // wait for next interval to trigger another failure notification synctest.Wait() notify = <-notifyCh assert.Equal(t, "consecutive-failures", notify.ID) assert.False(t, notify.Status.Success) }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_alias_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "bytes" "context" "fmt" "slices" "strconv" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" networkpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/network" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LinkAliasConfigController manages network.LinkAliasSpec based on machine configuration, list of links, etc. type LinkAliasConfigController struct{} // Name implements controller.Controller interface. func (ctrl *LinkAliasConfigController) Name() string { return "network.LinkAliasConfigController" } // Inputs implements controller.Controller interface. func (ctrl *LinkAliasConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *LinkAliasConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.LinkAliasSpecType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *LinkAliasConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } } linkStatuses, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing link statuses: %w", err) } // we are only interested in physical links physicalLinks := xslices.Filter(slices.Collect(linkStatuses.All()), func(item *network.LinkStatus) bool { return item.TypedSpec().Physical() }) // sort the links by MAC address to ensure consistent alias assignment for pattern-based configs slices.SortFunc(physicalLinks, func(a, b *network.LinkStatus) int { addrA := a.TypedSpec().PermanentAddr if len(addrA) == 0 { addrA = a.TypedSpec().HardwareAddr } addrB := b.TypedSpec().PermanentAddr if len(addrB) == 0 { addrB = b.TypedSpec().HardwareAddr } return bytes.Compare(addrA, addrB) }) physicalLinkSpecs := make([]*networkpb.LinkStatusSpec, 0, len(physicalLinks)) for _, link := range physicalLinks { var spec networkpb.LinkStatusSpec if err = proto.ResourceSpecToProto(link, &spec); err != nil { return fmt.Errorf("error converting link spec (%s) to proto: %w", link.Metadata().ID(), err) } physicalLinkSpecs = append(physicalLinkSpecs, &spec) } var linkAliasConfigs []configconfig.NetworkLinkAliasConfig if cfg != nil { linkAliasConfigs = cfg.Config().NetworkLinkAliasConfigs() } // sort the link alias configs by name to ensure deterministic processing order slices.SortFunc(linkAliasConfigs, func(a, b configconfig.NetworkLinkAliasConfig) int { return strings.Compare(a.Name(), b.Name()) }) linkAliases := map[string]string{} for _, lac := range linkAliasConfigs { var matchedLinks []*network.LinkStatus for idx, link := range physicalLinkSpecs { matches, err := lac.LinkSelector().EvalBool(celenv.LinkLocator(), map[string]any{ "link": link, }) if err != nil { return fmt.Errorf("error evaluating link selector: %w", err) } if matches { matchedLinks = append(matchedLinks, physicalLinks[idx]) } } // Fixed name: require exactly one match if len(matchedLinks) > 1 && !lac.IsPatternAlias() { logger.Warn("link selector matched multiple links, skipping", zap.String("selector", lac.LinkSelector().String()), zap.String("alias", lac.Name()), zap.Strings("links", xslices.Map(matchedLinks, func(item *network.LinkStatus) string { return item.Metadata().ID() })), ) continue } matchedLinks = xslices.Filter(matchedLinks, func(matchedLink *network.LinkStatus) bool { _, alreadyAliased := linkAliases[matchedLink.Metadata().ID()] if alreadyAliased { logger.Warn("link already has an alias, skipping", zap.String("link", matchedLink.Metadata().ID()), zap.String("existing_alias", linkAliases[matchedLink.Metadata().ID()]), zap.String("new_alias", lac.Name()), ) } return !alreadyAliased }) if len(matchedLinks) == 0 { continue } if lac.IsPatternAlias() { // Pattern-based name: create sequential aliases for each matched link in name order for counter, matchedLink := range matchedLinks { linkAliases[matchedLink.Metadata().ID()] = strings.Replace(lac.Name(), "%d", strconv.Itoa(counter), 1) } } else { matchedLink := matchedLinks[0] linkAliases[matchedLink.Metadata().ID()] = lac.Name() } } for linkID, alias := range linkAliases { if err = safe.WriterModify( ctx, r, network.NewLinkAliasSpec(network.NamespaceName, linkID), func(r *network.LinkAliasSpec) error { r.TypedSpec().Alias = alias return nil }, ); err != nil { return fmt.Errorf("error writing link alias spec for link %q: %w", linkID, err) } } if err := safe.CleanupOutputs[*network.LinkAliasSpec](ctx, r); err != nil { return fmt.Errorf("error cleaning up link alias specs: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_alias_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:goconst package network_test import ( "net" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type LinkAliasConfigSuite struct { ctest.DefaultSuite } type testLink struct { name string permanentAddr string } func (suite *LinkAliasConfigSuite) createLinks(links []testLink) { for _, link := range links { pAddr, err := net.ParseMAC(link.permanentAddr) suite.Require().NoError(err) status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().PermanentAddr = nethelpers.HardwareAddr(pAddr) status.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(pAddr) status.TypedSpec().Type = nethelpers.LinkEther suite.Create(status) } } func (suite *LinkAliasConfigSuite) TestMachineConfigurationNewStyle() { lc1 := networkcfg.NewLinkAliasConfigV1Alpha1("net0") lc1.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`glob("00:1a:2b:*", mac(link.permanent_addr))`, celenv.LinkLocator())) lc2 := networkcfg.NewLinkAliasConfigV1Alpha1("net1") lc2.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`glob("33:44:55:*", mac(link.permanent_addr))`, celenv.LinkLocator())) ctr, err := container.New(lc1, lc2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.createLinks([]testLink{ {name: "enp0s2", permanentAddr: "00:1a:2b:33:44:55"}, {name: "enp1s3", permanentAddr: "33:44:55:66:77:88"}, {name: "enp1s4", permanentAddr: "33:44:55:66:77:89"}, }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) rtestutils.AssertNoResource[*network.LinkAliasSpec](suite.Ctx(), suite.T(), suite.State(), "enp1s3") rtestutils.AssertNoResource[*network.LinkAliasSpec](suite.Ctx(), suite.T(), suite.State(), "enp1s4") suite.Destroy(cfg) rtestutils.AssertNoResource[*network.LinkAliasSpec](suite.Ctx(), suite.T(), suite.State(), "enp0s2") } func (suite *LinkAliasConfigSuite) TestMachineConfigurationTwoAliasesSameLink() { lc1 := networkcfg.NewLinkAliasConfigV1Alpha1("net1") lc1.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`glob("00:1a:2b:*", mac(link.permanent_addr))`, celenv.LinkLocator())) lc2 := networkcfg.NewLinkAliasConfigV1Alpha1("net0") lc2.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`glob("00:1a:2b:33:*", mac(link.permanent_addr))`, celenv.LinkLocator())) ctr, err := container.New(lc1, lc2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.createLinks([]testLink{ {name: "enp0s2", permanentAddr: "00:1a:2b:33:44:55"}, }) // the "smallest" alias (net0) should win, net1 should be ignored since it conflicts with net0 rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) } func (suite *LinkAliasConfigSuite) TestPatternAliasSortsByMAC() { // Test that pattern aliases are assigned in alphabetical order, regardless of creation order lc1 := networkcfg.NewLinkAliasConfigV1Alpha1("net%d") lc1.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`link.type == 1`, celenv.LinkLocator())) ctr, err := container.New(lc1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) // Create links out of order suite.createLinks([]testLink{ {name: "enp1s4", permanentAddr: "33:44:55:66:77:88"}, {name: "enp0s2", permanentAddr: "00:1a:2b:33:44:55"}, {name: "enp1s3", permanentAddr: "33:44:55:66:77:89"}, }) // Aliases should follow alphabetical order of link name rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s3", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net2", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s4", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net1", spec.TypedSpec().Alias) }) suite.Destroy(cfg) } func (suite *LinkAliasConfigSuite) TestPatternSkipsAlreadyAliased() { // Test that a fixed-name config claims a link, and a subsequent pattern config skips it lc1 := networkcfg.NewLinkAliasConfigV1Alpha1("mgmt0") lc1.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`mac(link.permanent_addr) == "00:1a:2b:33:44:55"`, celenv.LinkLocator())) lc2 := networkcfg.NewLinkAliasConfigV1Alpha1("net%d") lc2.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`link.type == 1`, celenv.LinkLocator())) ctr, err := container.New(lc1, lc2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.createLinks([]testLink{ {name: "enp0s2", permanentAddr: "00:1a:2b:33:44:55"}, {name: "enp1s3", permanentAddr: "33:44:55:66:77:88"}, {name: "enp1s4", permanentAddr: "33:44:55:66:77:89"}, }) // enp0s2 gets mgmt0 from the fixed-name config rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("mgmt0", spec.TypedSpec().Alias) }) // enp1s3 and enp1s4 get net0 and net1 from the pattern config (enp0s2 skipped) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s3", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s4", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net1", spec.TypedSpec().Alias) }) suite.Destroy(cfg) } func (suite *LinkAliasConfigSuite) TestPatternReconcileOnLinkChange() { // Test that when links change, pattern aliases are reconciled (re-numbered) lc1 := networkcfg.NewLinkAliasConfigV1Alpha1("net%d") lc1.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`link.type == 1`, celenv.LinkLocator())) ctr, err := container.New(lc1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.createLinks([]testLink{ {name: "enp0s2", permanentAddr: "00:1a:2b:33:44:55"}, {name: "enp1s3", permanentAddr: "33:44:55:66:77:88"}, {name: "enp1s4", permanentAddr: "33:44:55:66:77:89"}, }) // Initial state: net0, net1, net2 rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s3", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net1", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s4", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net2", spec.TypedSpec().Alias) }) // Remove the middle link — aliases should be re-numbered suite.Destroy(network.NewLinkStatus(network.NamespaceName, "enp1s3")) // enp1s3 alias should be cleaned up, enp1s4 re-numbered to net1 rtestutils.AssertNoResource[*network.LinkAliasSpec](suite.Ctx(), suite.T(), suite.State(), "enp1s3") rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp0s2", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net0", spec.TypedSpec().Alias) }) rtestutils.AssertResource(suite.Ctx(), suite.T(), suite.State(), "enp1s4", func(spec *network.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal("net1", spec.TypedSpec().Alias) }) suite.Destroy(cfg) } func TestLinkAliasConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &LinkAliasConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.LinkAliasConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_alias_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-multierror" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LinkAliasSpecController applies network.LinkAliasSpec to the actual interfaces. type LinkAliasSpecController struct{} // Name implements controller.Controller interface. func (ctrl *LinkAliasSpecController) Name() string { return "network.LinkAliasSpecController" } // Inputs implements controller.Controller interface. func (ctrl *LinkAliasSpecController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *LinkAliasSpecController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *LinkAliasSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy, which implies that all link renames are done if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.LinkAliasSpecType, Kind: controller.InputWeak, }, }, ); err != nil { return err } // watch link changes as some routes might need to be re-applied if the link appears watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_LINK) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source link alias specs (what should be aliased) linkAliasSpecs, err := safe.ReaderListAll[*network.LinkAliasSpec](ctx, r) if err != nil { return fmt.Errorf("error listing link alias specs: %w", err) } linkAliasSpecLookup := make(map[string]string, linkAliasSpecs.Len()) for linkAliasSpec := range linkAliasSpecs.All() { linkAliasSpecLookup[linkAliasSpec.Metadata().ID()] = linkAliasSpec.TypedSpec().Alias } logger.Debug("reconciling link aliases", zap.Any("desired", linkAliasSpecLookup)) // list rtnetlink links (interfaces) links, err := conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } // loop over links and make reconcile decision var multiErr *multierror.Error for _, link := range links { if link.Attributes == nil { continue } if link.Attributes.Info != nil || nethelpers.LinkType(link.Type) != nethelpers.LinkEther { // skip non-physical links continue } expectedAlias, shouldHaveAlias := linkAliasSpecLookup[link.Attributes.Name] currentAlias := pointer.SafeDeref(link.Attributes.Alias) if !shouldHaveAlias && currentAlias != "" { // should not have alias, but has one - remove it logger.Info("removing link alias", zap.String("link", link.Attributes.Name), zap.String("alias", currentAlias), ) if err = conn.Link.Set(&rtnetlink.LinkMessage{ Index: link.Index, Attributes: &rtnetlink.LinkAttributes{ Alias: new(""), }, }); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("error removing alias %q from link %q: %w", currentAlias, link.Attributes.Name, err)) } } else if shouldHaveAlias && currentAlias != expectedAlias { // should have alias, but doesn't have it or it's different - set it logger.Info("setting link alias", zap.String("link", link.Attributes.Name), zap.String("alias", expectedAlias), ) if err = conn.Link.Set(&rtnetlink.LinkMessage{ Index: link.Index, Attributes: &rtnetlink.LinkAttributes{ Alias: new(expectedAlias), }, }); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("error setting alias %q on link %q: %w", expectedAlias, link.Attributes.Name, err)) } } } if err = multiErr.ErrorOrNil(); err != nil { return err } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/pair/ordered" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LinkConfigController manages network.LinkSpec based on machine configuration, kernel cmdline. type LinkConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *LinkConfigController) Name() string { return "network.LinkConfigController" } // Inputs implements controller.Controller interface. func (ctrl *LinkConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.DeviceConfigSpecType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *LinkConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.LinkSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *LinkConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } } devices, err := safe.ReaderListAll[*network.DeviceConfigSpec](ctx, r) if err != nil { return fmt.Errorf("error getting device config: %w", err) } ignoredInterfaces := map[string]struct{}{} for item := range devices.All() { device := item.TypedSpec().Device if device.Ignore() { ignoredInterfaces[device.Interface()] = struct{}{} } } linkStatuses, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing link statuses: %w", err) } linkNameResolver := network.NewLinkResolver(linkStatuses.All) // bring up loopback interface { if err = ctrl.apply(ctx, r, []network.LinkSpecSpec{ { Name: "lo", Up: true, ConfigLayer: network.ConfigDefault, }, }); err != nil { return fmt.Errorf("error applying cmdline route: %w", err) } } // parse kernel cmdline for the interface name cmdlineLinks, cmdlineIgnored := ctrl.parseCmdline(logger, linkNameResolver) for _, cmdlineLink := range cmdlineLinks { if cmdlineLink.Name != "" { if _, ignored := ignoredInterfaces[cmdlineLink.Name]; !ignored { if err = ctrl.apply(ctx, r, []network.LinkSpecSpec{cmdlineLink}); err != nil { return fmt.Errorf("error applying cmdline route: %w", err) } } } } // parse machine configuration for link specs links := ctrl.processMachineConfiguration(logger, cfg, devices, linkNameResolver) if err = ctrl.apply(ctx, r, links); err != nil { return fmt.Errorf("error applying machine configuration address: %w", err) } // bring up any physical link not mentioned explicitly in the machine configuration // only in the mode when we run default DHCP operators shouldRunDefaultDHCPOperators := cfg == nil || cfg.Config().RunDefaultDHCPOperators() if shouldRunDefaultDHCPOperators { configuredLinks := map[string]struct{}{} for _, linkName := range cmdlineIgnored { configuredLinks[linkName] = struct{}{} } for _, cmdlineLink := range cmdlineLinks { if cmdlineLink.Name != "" { configuredLinks[cmdlineLink.Name] = struct{}{} } } if devices.Len() > 0 { for item := range devices.All() { device := item.TypedSpec().Device configuredLinks[device.Interface()] = struct{}{} if device.Bond() != nil { for _, link := range device.Bond().Interfaces() { configuredLinks[link] = struct{}{} } } if device.Bridge() != nil { for _, link := range device.Bridge().Interfaces() { configuredLinks[link] = struct{}{} } } } } // if we have new-style link config documents, they disable shouldRunDefaultDHCPOperators, so // we don't need to add them to configuredLinks here outer: for linkStatus := range linkStatuses.All() { for linkAlias := range network.AllLinkNames(linkStatus) { if _, configured := configuredLinks[linkAlias]; configured { continue outer } } if linkStatus.TypedSpec().Physical() { if err = ctrl.apply(ctx, r, []network.LinkSpecSpec{ { Name: linkStatus.Metadata().ID(), Up: true, ConfigLayer: network.ConfigDefault, }, }); err != nil { return fmt.Errorf("error applying default link up: %w", err) } } } } if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning up outputs: %w", err) } r.ResetRestartBackoff() } } func (ctrl *LinkConfigController) apply(ctx context.Context, r controller.Runtime, links []network.LinkSpecSpec) error { for _, link := range links { id := network.LayeredID(link.ConfigLayer, network.LinkID(link.Name)) if err := safe.WriterModify( ctx, r, network.NewLinkSpec(network.ConfigNamespaceName, id), func(r *network.LinkSpec) error { *r.TypedSpec() = link return nil }, ); err != nil { return err } } return nil } func (ctrl *LinkConfigController) parseCmdline(logger *zap.Logger, linkNameResolver *network.LinkResolver) ([]network.LinkSpecSpec, []string) { if ctrl.Cmdline == nil { return []network.LinkSpecSpec{}, nil } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, linkNameResolver) if err != nil { logger.Info("ignoring error", zap.Error(err)) return []network.LinkSpecSpec{}, nil } return settings.NetworkLinkSpecs, settings.IgnoreInterfaces } func (ctrl *LinkConfigController) processMachineConfiguration( logger *zap.Logger, cfg *config.MachineConfig, devices safe.List[*network.DeviceConfigSpec], linkNameResolver *network.LinkResolver, ) []network.LinkSpecSpec { linkMap := map[string]*network.LinkSpecSpec{} ctrl.processDevicesConfiguration(logger, linkMap, devices, linkNameResolver) ctrl.processLinkConfigs(logger, linkMap, cfg, linkNameResolver) return maps.ValuesFunc(linkMap, func(link *network.LinkSpecSpec) network.LinkSpecSpec { return *link }) } //nolint:gocyclo,cyclop func (ctrl *LinkConfigController) processDevicesConfiguration( logger *zap.Logger, linkMap map[string]*network.LinkSpecSpec, devices safe.List[*network.DeviceConfigSpec], linkNameResolver *network.LinkResolver, ) { // scan for the bonds or bridges bondedLinks := map[string]ordered.Pair[string, int]{} // mapping physical interface -> bond interface bridgedLinks := map[string]string{} // mapping physical interface -> bridge interface for item := range devices.All() { device := item.TypedSpec().Device if device.Ignore() { continue } deviceInterface := linkNameResolver.Resolve(device.Interface()) if device.Bond() != nil { for idx, linkName := range device.Bond().Interfaces() { linkName = linkNameResolver.Resolve(linkName) if bondData, exists := bondedLinks[linkName]; exists && bondData.F1 != deviceInterface { logger.Sugar().Warnf("link %q is included in both bonds %q and %q", linkName, bondData.F1, deviceInterface) } if bridgeName, exists := bridgedLinks[linkName]; exists { logger.Sugar().Warnf("link %q is included in both bond %q and bridge %q", linkName, bridgeName, deviceInterface) } bondedLinks[linkName] = ordered.MakePair(deviceInterface, idx) } } if device.Bridge() != nil { for _, linkName := range device.Bridge().Interfaces() { linkName = linkNameResolver.Resolve(linkName) if bridgeName, exists := bridgedLinks[linkName]; exists && bridgeName != deviceInterface { logger.Sugar().Warnf("link %q is included in both bridges %q and %q", linkName, bridgeName, deviceInterface) } if bondData, exists := bondedLinks[linkName]; exists { logger.Sugar().Warnf("link %q is included in both bond %q and bridge %q", linkName, bondData.F1, deviceInterface) } bridgedLinks[linkName] = deviceInterface } } if device.BridgePort() != nil { bridgePortMaster := linkNameResolver.Resolve(device.BridgePort().Master()) if bridgeName, exists := bridgedLinks[deviceInterface]; exists && bridgeName != bridgePortMaster { logger.Sugar().Warnf("link %q is included in both bridges %q and %q", deviceInterface, bridgeName, bridgePortMaster) } if bondData, exists := bondedLinks[deviceInterface]; exists { logger.Sugar().Warnf("link %q is included into both bond %q and bridge %q", deviceInterface, bondData.F1, bridgePortMaster) } bridgedLinks[deviceInterface] = bridgePortMaster } } for item := range devices.All() { device := item.TypedSpec().Device if device.Ignore() { continue } deviceInterface := linkNameResolver.Resolve(device.Interface()) if _, exists := linkMap[deviceInterface]; !exists { linkMap[deviceInterface] = &network.LinkSpecSpec{ Name: deviceInterface, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } if device.MTU() != 0 { linkMap[deviceInterface].MTU = uint32(device.MTU()) } if device.Bond() != nil { if err := SetBondMasterLegacy(linkMap[deviceInterface], device.Bond()); err != nil { logger.Error("error parsing bond config", zap.Error(err)) } } if device.Bridge() != nil { if err := SetBridgeMasterLegacy(linkMap[deviceInterface], device.Bridge()); err != nil { logger.Error("error parsing bridge config", zap.Error(err)) } } if device.WireguardConfig() != nil { if err := wireguardLinkLegacy(linkMap[deviceInterface], device.WireguardConfig()); err != nil { logger.Error("error parsing wireguard config", zap.Error(err)) } } if device.Dummy() { dummyLink(linkMap[deviceInterface]) } for _, vlan := range device.Vlans() { vlanName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) // [NOTE]: VLAN uses the original interface name (before resolving aliases) linkMap[vlanName] = &network.LinkSpecSpec{ Up: true, ConfigLayer: network.ConfigMachineConfiguration, } vlanLink(linkMap[vlanName], vlanName, deviceInterface, vlan) } } for slaveName, bondData := range bondedLinks { if _, exists := linkMap[slaveName]; !exists { linkMap[slaveName] = &network.LinkSpecSpec{ Name: slaveName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } SetBondSlave(linkMap[slaveName], bondData) } for slaveName, bridgeIface := range bridgedLinks { if _, exists := linkMap[slaveName]; !exists { linkMap[slaveName] = &network.LinkSpecSpec{ Name: slaveName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } SetBridgeSlave(linkMap[slaveName], bridgeIface) } } //nolint:gocyclo,cyclop func (ctrl *LinkConfigController) processLinkConfigs(logger *zap.Logger, linkMap map[string]*network.LinkSpecSpec, cfg *config.MachineConfig, linkNameResolver *network.LinkResolver) { if cfg == nil { return } for _, linkConfig := range cfg.Config().NetworkCommonLinkConfigs() { linkName := linkConfig.Name() linkName = linkNameResolver.Resolve(linkName) if _, exists := linkMap[linkName]; !exists { linkMap[linkName] = &network.LinkSpecSpec{ Name: linkName, ConfigLayer: network.ConfigMachineConfiguration, } } linkMap[linkName].Up = linkConfig.Up().ValueOr(true) if mtu, ok := linkConfig.MTU().Get(); ok { linkMap[linkName].MTU = mtu } if hwAddrConfig, ok := linkConfig.(talosconfig.NetworkHardwareAddressConfig); ok { if hwAddr, ok := hwAddrConfig.HardwareAddress().Get(); ok { linkMap[linkName].HardwareAddress = hwAddr } } else { linkMap[linkName].HardwareAddress = nil } if multicast, ok := linkConfig.Multicast().Get(); ok { linkMap[linkName].Multicast = new(multicast) } switch specificLinkConfig := linkConfig.(type) { case talosconfig.NetworkPhysicalLinkConfig: // nothing specific for physical links case talosconfig.NetworkDummyLinkConfig: dummyLink(linkMap[linkName]) case talosconfig.NetworkVLANConfig: parentLink := linkNameResolver.Resolve(specificLinkConfig.ParentLink()) vlanLink(linkMap[linkName], linkName, parentLink, networkVLANConfigToVlaner{specificLinkConfig}) case talosconfig.NetworkBondConfig: SendBondMaster(linkMap[linkName], specificLinkConfig) bondedLinks := xslices.Map(specificLinkConfig.Links(), linkNameResolver.Resolve) for idx, slaveLinkName := range bondedLinks { if _, exists := linkMap[slaveLinkName]; !exists { linkMap[slaveLinkName] = &network.LinkSpecSpec{ Name: slaveLinkName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } SetBondSlave(linkMap[slaveLinkName], ordered.MakePair(linkName, idx)) } case talosconfig.NetworkBridgeConfig: SetBridgeMaster(linkMap[linkName], specificLinkConfig) bridgedLinks := xslices.Map(specificLinkConfig.Links(), linkNameResolver.Resolve) for _, slaveLinkName := range bridgedLinks { if _, exists := linkMap[slaveLinkName]; !exists { linkMap[slaveLinkName] = &network.LinkSpecSpec{ Name: slaveLinkName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } SetBridgeSlave(linkMap[slaveLinkName], linkName) } case talosconfig.NetworkVRFConfig: SetVRFMaster(linkMap[linkName], specificLinkConfig) vrfLinks := xslices.Map(specificLinkConfig.Links(), linkNameResolver.Resolve) for _, slaveLinkName := range vrfLinks { if _, exists := linkMap[slaveLinkName]; !exists { linkMap[slaveLinkName] = &network.LinkSpecSpec{ Name: slaveLinkName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } SetVRFSlave(linkMap[slaveLinkName], linkName) } case talosconfig.NetworkWireguardConfig: wireguardLink(linkMap[linkName], specificLinkConfig) default: logger.Error("unknown link config type", zap.String("linkName", linkName), zap.String("type", fmt.Sprintf("%T", specificLinkConfig))) } } // if we have DHCP config, bring up the link implicitly if it hasn't been configured yet for _, dhcpConfig := range cfg.Config().NetworkDHCPConfigs() { linkName := dhcpConfig.Name() linkName = linkNameResolver.Resolve(linkName) if _, exists := linkMap[linkName]; !exists { linkMap[linkName] = &network.LinkSpecSpec{ Name: linkName, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } } } } type vlaner interface { ID() uint16 Mode() nethelpers.VLANProtocol } type networkVLANConfigToVlaner struct { talosconfig.NetworkVLANConfig } func (v networkVLANConfigToVlaner) ID() uint16 { return v.VLANID() } func (v networkVLANConfigToVlaner) Mode() nethelpers.VLANProtocol { return v.VLANMode().ValueOr(nethelpers.VLANProtocol8021Q) } func vlanLink(link *network.LinkSpecSpec, vlanName, linkName string, vlan vlaner) { link.Name = vlanName link.Logical = true link.Up = true // only legacy config specifies MTUs on VLANs this way if mtuConfig, ok := vlan.(interface{ MTU() uint32 }); ok { link.MTU = mtuConfig.MTU() } link.Kind = network.LinkKindVLAN link.Type = nethelpers.LinkEther link.ParentName = linkName link.VLAN = network.VLANSpec{ VID: vlan.ID(), Protocol: vlan.Mode(), } } func wireguardLinkLegacy(link *network.LinkSpecSpec, config talosconfig.WireguardConfig) error { link.Logical = true link.Kind = network.LinkKindWireguard link.Type = nethelpers.LinkNone link.Wireguard = network.WireguardSpec{ PrivateKey: config.PrivateKey(), ListenPort: config.ListenPort(), FirewallMark: config.FirewallMark(), } for _, peer := range config.Peers() { allowedIPs := make([]netip.Prefix, 0, len(peer.AllowedIPs())) for _, allowedIP := range peer.AllowedIPs() { ip, err := netip.ParsePrefix(allowedIP) if err != nil { return err } allowedIPs = append(allowedIPs, ip) } link.Wireguard.Peers = append(link.Wireguard.Peers, network.WireguardPeer{ PublicKey: peer.PublicKey(), Endpoint: peer.Endpoint(), PersistentKeepaliveInterval: peer.PersistentKeepaliveInterval(), AllowedIPs: allowedIPs, }) } return nil } func wireguardLink(link *network.LinkSpecSpec, config talosconfig.NetworkWireguardConfig) { link.Logical = true link.Kind = network.LinkKindWireguard link.Type = nethelpers.LinkNone link.Wireguard = network.WireguardSpec{ PrivateKey: config.PrivateKey(), ListenPort: config.ListenPort().ValueOr(0), FirewallMark: config.FirewallMark().ValueOr(0), } for _, peer := range config.Peers() { link.Wireguard.Peers = append(link.Wireguard.Peers, network.WireguardPeer{ PublicKey: peer.PublicKey(), PresharedKey: peer.PresharedKey().ValueOr(""), Endpoint: peer.Endpoint().ValueOr(""), PersistentKeepaliveInterval: peer.PersistentKeepalive().ValueOr(0), AllowedIPs: peer.AllowedIPs(), }) } } func dummyLink(link *network.LinkSpecSpec) { link.Logical = true link.Kind = "dummy" link.Type = nethelpers.LinkEther } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:goconst,dupl package network_test import ( "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type LinkConfigSuite struct { ctest.DefaultSuite } func (suite *LinkConfigSuite) assertLinks(requiredIDs []string, check func(*network.LinkSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *LinkConfigSuite) assertNoLinks(unexpectedIDs []string) { for _, id := range unexpectedIDs { ctest.AssertNoResource[*network.LinkSpec](suite, id, rtestutils.WithNamespace(network.ConfigNamespaceName)) } } func (suite *LinkConfigSuite) TestLoopback() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) suite.assertLinks( []string{ "default/lo", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal("lo", r.TypedSpec().Name) asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, ) } func (suite *LinkConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.LinkConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1:::::"), }, ), ) suite.assertLinks( []string{ "cmdline/eth1", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal("eth1", r.TypedSpec().Name) asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer) }, ) } //nolint:gocyclo func (suite *LinkConfigSuite) TestMachineConfiguration() { const kernelDriver = "somekerneldriver" suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 24, VlanMTU: 1000, VlanAddresses: []string{ "10.0.0.1/8", }, }, { VlanID: 48, VlanAddresses: []string{ "10.0.0.2/8", }, }, }, }, { DeviceInterface: "eth1", DeviceAddresses: []string{"192.168.0.24/28"}, }, { DeviceInterface: "eth1", DeviceMTU: 9001, }, { DeviceIgnore: new(true), DeviceInterface: "eth2", DeviceAddresses: []string{"192.168.0.24/28"}, }, { DeviceInterface: "eth2", }, { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{"eth2", "eth3"}, BondMode: "balance-xor", }, }, { DeviceInterface: "bond1", DeviceBond: &v1alpha1.Bond{ BondDeviceSelectors: []v1alpha1.NetworkDeviceSelector{{ NetworkDeviceKernelDriver: kernelDriver, }}, BondMode: "balance-xor", }, }, { DeviceInterface: "eth4", DeviceAddresses: []string{"192.168.0.42/24"}, }, { DeviceInterface: "eth5", DeviceAddresses: []string{"192.168.0.43/24"}, }, { DeviceInterface: "eth8", DeviceBridgePort: &v1alpha1.BridgePort{ BridgePortMaster: "br1", }, }, { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{"eth4", "eth5"}, BridgeSTP: &v1alpha1.STP{ STPEnabled: new(false), }, }, }, { DeviceInterface: "br1", DeviceBridge: &v1alpha1.Bridge{}, }, { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ BridgeSTP: &v1alpha1.STP{ STPEnabled: new(true), }, BridgeVLAN: &v1alpha1.BridgeVLAN{ BridgeVLANFiltering: new(true), }, }, }, { DeviceInterface: "dummy0", DeviceDummy: new(true), }, { DeviceInterface: "wireguard0", DeviceWireguardConfig: &v1alpha1.DeviceWireguardConfig{ WireguardPrivateKey: "ABC", WireguardPeers: []*v1alpha1.DeviceWireguardPeer{ { WireguardPublicKey: "DEF", WireguardEndpoint: "10.0.0.1:3000", WireguardAllowedIPs: []string{ "10.2.3.0/24", "10.2.4.0/24", }, }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) for _, name := range []string{"eth6", "eth7"} { status := network.NewLinkStatus(network.NamespaceName, name) status.TypedSpec().Driver = kernelDriver suite.Create(status) } suite.assertLinks( []string{ "configuration/eth0", "configuration/eth0.24", "configuration/eth0.48", "configuration/eth1", "configuration/eth2", "configuration/eth3", "configuration/eth6", "configuration/eth7", "configuration/eth8", "configuration/bond0", "configuration/bond1", "configuration/br0", "configuration/br1", "configuration/dummy0", "configuration/wireguard0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) switch r.TypedSpec().Name { case "eth0", "eth1": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) if r.TypedSpec().Name == "eth0" { asrt.EqualValues(0, r.TypedSpec().MTU) } else { asrt.EqualValues(9001, r.TypedSpec().MTU) } asrt.Nil(r.TypedSpec().Multicast) case "eth0.24", "eth0.48": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal("eth0", r.TypedSpec().ParentName) asrt.Equal(nethelpers.VLANProtocol8021Q, r.TypedSpec().VLAN.Protocol) if r.TypedSpec().Name == "eth0.24" { asrt.EqualValues(24, r.TypedSpec().VLAN.VID) asrt.EqualValues(1000, r.TypedSpec().MTU) } else { asrt.EqualValues(48, r.TypedSpec().VLAN.VID) asrt.EqualValues(0, r.TypedSpec().MTU) } case "eth2", "eth3": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("bond0", r.TypedSpec().BondSlave.MasterName) case "eth6", "eth7": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("bond1", r.TypedSpec().BondSlave.MasterName) case "bond0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Equal(nethelpers.BondModeXOR, r.TypedSpec().BondMaster.Mode) asrt.True(r.TypedSpec().BondMaster.UseCarrier) case "bond1": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Equal(nethelpers.BondModeXOR, r.TypedSpec().BondMaster.Mode) asrt.True(r.TypedSpec().BondMaster.UseCarrier) case "eth4", "eth5": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("br0", r.TypedSpec().BridgeSlave.MasterName) case "eth8": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("br1", r.TypedSpec().BridgeSlave.MasterName) case "br0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled) asrt.True(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) case "br1": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled) asrt.False(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) case "wireguard0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkNone, r.TypedSpec().Type) asrt.Equal(network.LinkKindWireguard, r.TypedSpec().Kind) asrt.Equal( network.WireguardSpec{ PrivateKey: "ABC", Peers: []network.WireguardPeer{ { PublicKey: "DEF", Endpoint: "10.0.0.1:3000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.2.3.0/24"), netip.MustParsePrefix("10.2.4.0/24"), }, }, }, }, r.TypedSpec().Wireguard, ) } }, ) } func (suite *LinkConfigSuite) TestMachineConfigurationWithAliases() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "enx0123", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 24, VlanMTU: 1000, }, }, }, { DeviceInterface: "enx0123", DeviceMTU: 9001, }, { DeviceIgnore: new(true), DeviceInterface: "enx0456", }, { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{"enxa", "enxb"}, BondMode: "balance-xor", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) for _, link := range []struct { name string aliases []string }{ { name: "eth0", aliases: []string{"enx0123"}, }, { name: "eth1", aliases: []string{"enx0456"}, }, { name: "eth2", aliases: []string{"enxa"}, }, { name: "eth3", aliases: []string{"enxb"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases suite.Create(status) } suite.assertLinks( []string{ "configuration/eth0", "configuration/enx0123.24", "configuration/eth2", "configuration/eth3", "configuration/bond0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) switch r.TypedSpec().Name { case "eth0": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.EqualValues(9001, r.TypedSpec().MTU) case "eth2", "eth3": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.Equal("bond0", r.TypedSpec().BondSlave.MasterName) case "eth0.24": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal("eth0", r.TypedSpec().ParentName) asrt.Equal(nethelpers.VLANProtocol8021Q, r.TypedSpec().VLAN.Protocol) asrt.EqualValues(24, r.TypedSpec().VLAN.VID) asrt.EqualValues(1000, r.TypedSpec().MTU) case "bond0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Equal(nethelpers.BondModeXOR, r.TypedSpec().BondMaster.Mode) asrt.True(r.TypedSpec().BondMaster.UseCarrier) asrt.Equal(nethelpers.ADLACPActiveOn, r.TypedSpec().BondMaster.ADLACPActive) } }, ) } func (suite *LinkConfigSuite) TestMachineConfigurationNewStyle() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkMTU = 9001 dc1 := networkcfg.NewDummyLinkConfigV1Alpha1("dummy1") dc1.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x42, 0xac, 0x11, 0x00, 0x02} dc1.LinkUp = new(true) vl1 := networkcfg.NewVLANConfigV1Alpha1("dummy1.100") vl1.VLANIDConfig = 100 vl1.ParentLinkConfig = "dummy1" vl1.VLANModeConfig = new(nethelpers.VLANProtocol8021AD) vl1.LinkMTU = 200 vl1.LinkUp = new(true) dc2 := networkcfg.NewDummyLinkConfigV1Alpha1("dummy2") dc3 := networkcfg.NewDummyLinkConfigV1Alpha1("dummy3") bc1 := networkcfg.NewBondConfigV1Alpha1("bond357") bc1.BondMode = new(nethelpers.BondModeActiveBackup) bc1.BondLinks = []string{"dummy2", "dummy3"} bc1.BondUpDelay = new(uint32(200)) br1 := networkcfg.NewBridgeConfigV1Alpha1("br0") br1.BridgeLinks = []string{"enp0s2", "eth1"} br1.BridgeSTP.BridgeSTPEnabled = new(true) br1.BridgeVLAN.BridgeVLANFiltering = new(true) ctr, err := container.New(dc1, lc1, vl1, dc2, dc3, bc1, br1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) for _, link := range []struct { name string aliases []string }{ { name: "eth0", aliases: []string{"enp0s2"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases suite.Create(status) } suite.assertLinks( []string{ "configuration/eth0", "configuration/dummy1", "configuration/dummy2", "configuration/dummy3", "configuration/dummy1.100", "configuration/bond357", "configuration/br0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) switch r.TypedSpec().Name { case "eth0": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.EqualValues(9001, r.TypedSpec().MTU) asrt.Equal("br0", r.TypedSpec().BridgeSlave.MasterName) case "eth1": asrt.True(r.TypedSpec().Up) asrt.Equal("br0", r.TypedSpec().BridgeSlave.MasterName) case "dummy1", "dummy2", "dummy3": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal("dummy", r.TypedSpec().Kind) if r.TypedSpec().Name == "dummy2" || r.TypedSpec().Name == "dummy3" { asrt.Equal("bond357", r.TypedSpec().BondSlave.MasterName) } case "dummy1.100": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal("dummy1", r.TypedSpec().ParentName) asrt.Equal(nethelpers.VLANProtocol8021AD, r.TypedSpec().VLAN.Protocol) asrt.EqualValues(100, r.TypedSpec().VLAN.VID) asrt.EqualValues(200, r.TypedSpec().MTU) case "bond357": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Equal(nethelpers.BondModeActiveBackup, r.TypedSpec().BondMaster.Mode) asrt.EqualValues(200, r.TypedSpec().BondMaster.UpDelay) asrt.Equal(nethelpers.ADLACPActiveOn, r.TypedSpec().BondMaster.ADLACPActive) case "br0": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.True(r.TypedSpec().BridgeMaster.STP.Enabled) asrt.True(r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) } }, ) } func (suite *LinkConfigSuite) TestMachineConfigurationNewStyleVRF() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkMTU = 9001 dc1 := networkcfg.NewDummyLinkConfigV1Alpha1("dummy1") dc1.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x42, 0xac, 0x11, 0x00, 0x02} dc1.LinkUp = new(true) vrf := networkcfg.NewVRFConfigV1Alpha1("vrf-blue") vrf.VRFLinks = []string{"enp0s2", "dummy1"} vrf.VRFTable = nethelpers.RoutingTable(123) ctr, err := container.New(lc1, dc1, vrf) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) for _, link := range []struct { name string aliases []string }{ { name: "eth0", aliases: []string{"enp0s2"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases suite.Create(status) } suite.assertLinks( []string{ "configuration/eth0", "configuration/dummy1", "configuration/vrf-blue", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) switch r.TypedSpec().Name { case "eth0": asrt.True(r.TypedSpec().Up) asrt.False(r.TypedSpec().Logical) asrt.EqualValues(9001, r.TypedSpec().MTU) asrt.Equal("vrf-blue", r.TypedSpec().VRFSlave.MasterName) case "dummy1": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal("vrf-blue", r.TypedSpec().VRFSlave.MasterName) case "vrf-blue": asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkEther, r.TypedSpec().Type) asrt.Equal(network.LinkKindVRF, r.TypedSpec().Kind) asrt.Equal(nethelpers.RoutingTable(123), r.TypedSpec().VRFMaster.Table) } }, ) } func (suite *LinkConfigSuite) TestMachineConfigurationNewStyleNotFIPS() { if fipsmode.Strict() { suite.T().Skip("skipping test in strict FIPS mode") } suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) privKey, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) pskKey, err := wgtypes.GenerateKey() suite.Require().NoError(err) peerKey, err := wgtypes.GenerateKey() suite.Require().NoError(err) wc1 := networkcfg.NewWireguardConfigV1Alpha1("wg0") wc1.LinkUp = new(true) wc1.WireguardPrivateKey = privKey.String() wc1.WireguardListenPort = 12345 wc1.WireguardPeers = []networkcfg.WireguardPeer{ { WireguardPublicKey: peerKey.PublicKey().String(), WireguardPresharedKey: pskKey.String(), WireguardAllowedIPs: []networkcfg.Prefix{{Prefix: netip.MustParsePrefix("10.0.0.0/24")}}, }, } ctr, err := container.New(wc1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.assertLinks( []string{ "configuration/wg0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) asrt.True(r.TypedSpec().Up) asrt.True(r.TypedSpec().Logical) asrt.Equal(nethelpers.LinkNone, r.TypedSpec().Type) asrt.Equal(network.LinkKindWireguard, r.TypedSpec().Kind) asrt.Equal( network.WireguardSpec{ PrivateKey: privKey.String(), ListenPort: 12345, FirewallMark: 0, Peers: []network.WireguardPeer{ { PublicKey: peerKey.PublicKey().String(), PresharedKey: pskKey.String(), AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/24"), }, }, }, }, r.TypedSpec().Wireguard, ) }, ) } func (suite *LinkConfigSuite) TestDefaultUp() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.LinkConfigController{ Cmdline: procfs.NewCmdline("talos.network.interface.ignore=eth2"), }, ), ) for _, link := range []string{"eth5", "eth1", "eth2", "eth3", "eth4"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true if link == "eth5" { linkStatus.TypedSpec().AltNames = []string{"enp0s2"} } suite.Create(linkStatus) } u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 24, VlanAddresses: []string{ "10.0.0.1/8", }, }, { VlanID: 48, VlanAddresses: []string{ "10.0.0.2/8", }, }, }, }, { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth3", "eth4", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) suite.assertLinks( []string{ "default/eth1", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) asrt.True(r.TypedSpec().Up) }, ) suite.assertNoLinks( []string{ "default/eth0", "default/eth2", "default/eth3", "default/eth4", }, ) } func (suite *LinkConfigSuite) TestNoDefaultUp() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) for _, link := range []string{"eth5", "eth1", "eth2", "eth3", "eth4"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true if link == "eth5" { linkStatus.TypedSpec().AltNames = []string{"eth0"} } suite.Create(linkStatus) } // default link up suite.assertLinks( []string{ "default/eth1", "default/eth2", "default/eth3", "default/eth4", "default/eth5", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) asrt.True(r.TypedSpec().Up) }, ) // create config lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkMTU = 9001 ctr, err := container.New(lc1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) // no default links up since we have config now suite.assertNoLinks( []string{ "default/eth0", "default/eth1", "default/eth2", "default/eth3", "default/eth4", "default/eth5", }, ) } func (suite *LinkConfigSuite) TestMulticast() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkConfigController{})) lc1 := networkcfg.NewLinkConfigV1Alpha1("enp1s1") lc1.LinkMulticast = new(false) lc2 := networkcfg.NewLinkConfigV1Alpha1("enp1s2") lc2.LinkMulticast = new(true) ctr, err := container.New(lc1, lc2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.assertLinks( []string{ "configuration/enp1s1", "configuration/enp1s2", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) switch r.TypedSpec().Name { case "enp1s1": asrt.False(*r.TypedSpec().Multicast) case "enp1s2": asrt.True(*r.TypedSpec().Multicast) } }, ) } func TestLinkConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &LinkConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.DeviceConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "cmp" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewLinkMergeController initializes a LinkMergeController. // // LinkMergeController merges network.LinkSpec in network.ConfigNamespace and produces final network.AddressSpec in network.Namespace. func NewLinkMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.LinkSpec]) map[resource.ID]*network.LinkSpecSpec { // sort by link name, configuration layer list.SortFunc(func(left, right *network.LinkSpec) int { if res := cmp.Compare(left.TypedSpec().Name, right.TypedSpec().Name); res != 0 { return res } return cmp.Compare(left.TypedSpec().ConfigLayer, right.TypedSpec().ConfigLayer) }) // build final link definition merging multiple layers links := make(map[string]*network.LinkSpecSpec, list.Len()) for link := range list.All() { id := network.LinkID(link.TypedSpec().Name) existing, ok := links[id] if !ok { links[id] = link.TypedSpec() } else if err := existing.Merge(link.TypedSpec()); err != nil { logger.Warn("error merging links", zap.Error(err)) } } return links }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:goconst package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type LinkMergeSuite struct { ctest.DefaultSuite } func (suite *LinkMergeSuite) assertLinks(requiredIDs []string, check func(*network.LinkSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *LinkMergeSuite) assertNoLinks(id string) { ctest.AssertNoResource[*network.LinkSpec](suite, id) } func (suite *LinkMergeSuite) TestMerge() { loopback := network.NewLinkSpec(network.ConfigNamespaceName, "default/lo") *loopback.TypedSpec() = network.LinkSpecSpec{ Name: "lo", Up: true, ConfigLayer: network.ConfigDefault, } dhcp := network.NewLinkSpec(network.ConfigNamespaceName, "dhcp/eth0") *dhcp.TypedSpec() = network.LinkSpecSpec{ Name: "eth0", Up: true, MTU: 1450, ConfigLayer: network.ConfigOperator, } static := network.NewLinkSpec(network.ConfigNamespaceName, "configuration/eth0") *static.TypedSpec() = network.LinkSpecSpec{ Name: "eth0", Up: true, MTU: 1500, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{loopback, dhcp, static} { suite.Create(res) } suite.assertLinks( []string{ "lo", "eth0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "lo": asrt.Equal(*loopback.TypedSpec(), *r.TypedSpec()) case "eth0": asrt.EqualValues(1500, r.TypedSpec().MTU) // static should override dhcp } }, ) suite.Destroy(static) suite.assertLinks( []string{ "lo", "eth0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "lo": asrt.Equal(*loopback.TypedSpec(), *r.TypedSpec()) case "eth0": // reconcile happens eventually, so give it some time asrt.EqualValues(1450, r.TypedSpec().MTU) } }, ) suite.Destroy(loopback) suite.assertNoLinks("lo") } func (suite *LinkMergeSuite) TestMergeLogicalLink() { bondPlatform := network.NewLinkSpec(network.ConfigNamespaceName, "platform/bond0") *bondPlatform.TypedSpec() = network.LinkSpecSpec{ Name: "bond0", Logical: true, Up: true, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondMode8023AD, }, ConfigLayer: network.ConfigPlatform, } bondMachineConfig := network.NewLinkSpec(network.ConfigNamespaceName, "config/bond0") *bondMachineConfig.TypedSpec() = network.LinkSpecSpec{ Name: "bond0", MTU: 1450, Up: true, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{bondPlatform, bondMachineConfig} { suite.Create(res) } suite.assertLinks( []string{ "bond0", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.True(r.TypedSpec().Logical) asrt.EqualValues(1450, r.TypedSpec().MTU) }, ) } func (suite *LinkMergeSuite) TestMergeFlapping() { // simulate two conflicting link definitions which are getting removed/added constantly dhcp := network.NewLinkSpec(network.ConfigNamespaceName, "dhcp/eth0") *dhcp.TypedSpec() = network.LinkSpecSpec{ Name: "eth0", Up: true, MTU: 1450, ConfigLayer: network.ConfigOperator, } static := network.NewLinkSpec(network.ConfigNamespaceName, "configuration/eth0") *static.TypedSpec() = network.LinkSpecSpec{ Name: "eth0", Up: true, MTU: 1500, ConfigLayer: network.ConfigMachineConfiguration, } testMergeFlapping(&suite.DefaultSuite, []*network.LinkSpec{dhcp, static}, "eth0", static) } func (suite *LinkMergeSuite) TestMergeWireguard() { static := network.NewLinkSpec(network.ConfigNamespaceName, "configuration/kubespan") *static.TypedSpec() = network.LinkSpecSpec{ Name: "kubespan", Wireguard: network.WireguardSpec{ ListenPort: 1234, Peers: []network.WireguardPeer{ { PublicKey: "bGsc2rOpl6JHd/Pm4fYrIkEABL0ZxW7IlaSyh77IMhw=", Endpoint: "127.0.0.1:9999", }, }, }, ConfigLayer: network.ConfigMachineConfiguration, } kubespanOperator := network.NewLinkSpec(network.ConfigNamespaceName, "kubespan/kubespan") *kubespanOperator.TypedSpec() = network.LinkSpecSpec{ Name: "kubespan", Wireguard: network.WireguardSpec{ PrivateKey: "IG9MqCII7z54Ysof1fQ9a7WcMNG+qNJRMyRCQz3JTUY=", ListenPort: 3456, Peers: []network.WireguardPeer{ { PublicKey: "RXdQkMTD1Jcxd/Wizr9k8syw8ANs57l5jTormDVHAVs=", Endpoint: "127.0.0.1:1234", }, }, }, ConfigLayer: network.ConfigOperator, } for _, res := range []resource.Resource{static, kubespanOperator} { suite.Create(res) } suite.assertLinks( []string{ "kubespan", }, func(r *network.LinkSpec, asrt *assert.Assertions) { asrt.Equal( "IG9MqCII7z54Ysof1fQ9a7WcMNG+qNJRMyRCQz3JTUY=", r.TypedSpec().Wireguard.PrivateKey, ) asrt.Equal(1234, r.TypedSpec().Wireguard.ListenPort) asrt.Len(r.TypedSpec().Wireguard.Peers, 2) if len(r.TypedSpec().Wireguard.Peers) != 2 { return } asrt.Equal( network.WireguardPeer{ PublicKey: "RXdQkMTD1Jcxd/Wizr9k8syw8ANs57l5jTormDVHAVs=", Endpoint: "127.0.0.1:1234", }, r.TypedSpec().Wireguard.Peers[0], ) asrt.Equal( network.WireguardPeer{ PublicKey: "bGsc2rOpl6JHd/Pm4fYrIkEABL0ZxW7IlaSyh77IMhw=", Endpoint: "127.0.0.1:9999", }, r.TypedSpec().Wireguard.Peers[1], ) }, ) suite.Destroy(kubespanOperator) suite.Destroy(static) suite.assertNoLinks("kubespan") } func TestLinkMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &LinkMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewLinkMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "bytes" "context" "errors" "fmt" "net" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-multierror" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/gen/pair/ordered" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "golang.zx2c4.com/wireguard/wgctrl" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LinkSpecController applies network.LinkSpec to the actual interfaces. type LinkSpecController struct{} // Name implements controller.Controller interface. func (ctrl *LinkSpecController) Name() string { return "network.LinkSpecController" } // Inputs implements controller.Controller interface. func (ctrl *LinkSpecController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *LinkSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: network.LinkRefreshType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *LinkSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy, which implies that all link renames are done if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.LinkSpecType, Kind: controller.InputStrong, }, }, ); err != nil { return err } // watch link changes as some routes might need to be re-applied if the link appears watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_LINK) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck wgClient, err := wgctrl.New() if err != nil { logger.Warn("error creating wireguard client", zap.Error(err)) } else { defer wgClient.Close() //nolint:errcheck } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source network configuration resources list, err := safe.ReaderList[*network.LinkSpec](ctx, r, resource.NewMetadata(network.NamespaceName, network.LinkSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing source network addresses: %w", err) } // add finalizers for all live resources for res := range list.All() { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // list rtnetlink links (interfaces) links, err := conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } // loop over links and make reconcile decision var multiErr *multierror.Error SortBonds(&list) for link := range list.All() { if err = ctrl.syncLink(ctx, r, logger, conn, wgClient, &links, link); err != nil { multiErr = multierror.Append(multiErr, err) } } if err = multiErr.ErrorOrNil(); err != nil { return err } r.ResetRestartBackoff() } } // SortBonds sort resources in increasing order, except it places slave interfaces right after the bond // in proper order. func SortBonds(items *safe.List[*network.LinkSpec]) { items.SortFunc(func(ll, rr *network.LinkSpec) int { left := ll.TypedSpec() right := rr.TypedSpec() l := ordered.MakeTriple(left.Name, 0, "") if left.BondSlave.MasterName != "" { l = ordered.MakeTriple(left.BondSlave.MasterName, left.BondSlave.SlaveIndex, left.Name) } r := ordered.MakeTriple(right.Name, 0, "") if right.BondSlave.MasterName != "" { r = ordered.MakeTriple(right.BondSlave.MasterName, right.BondSlave.SlaveIndex, right.Name) } return l.Compare(r) }) } func findLink(links []rtnetlink.LinkMessage, name string, allowAliases bool) *rtnetlink.LinkMessage { if name == "" { return nil // should never match } for i, link := range links { if link.Attributes.Name == name { return &links[i] } } if !allowAliases { return nil } for i, link := range links { if pointer.SafeDeref(link.Attributes.Alias) == name { return &links[i] } if slices.Index(link.Attributes.AltNames, name) != -1 { return &links[i] } } return nil } // syncLink syncs kernel state with the LinkSpec link. // // This method is really long, but it's hard to break it down in multiple pieces, are those pieces and steps are inter-dependent, so, instead, // I'm going to provide high-level flow of the method here to help understand it: // // First of all, if the spec is being torn down - remove the link from the kernel, done. // If the link spec is not being torn down, start the sync process: // // - for physical links, there's not much we can sync - only MTU and 'UP' flag // - for logical links, controller handles creation and sync of the settings depending on the interface type // // If the logical link kind or type got changed (for example, "link0" was a bond, and now it's wireguard interface), the link // is dropped and replaced with the new one. // Same replace flow is used for VLAN links, as VLAN settings can't be changed on the fly. // // For bonded links, there are two sync steps applied: // // - bond slave interfaces are enslaved to be part of the bond (by changing MasterIndex) // - bond master link settings are synced with the spec: some settings can't be applied on UP bond and a bond which has slaves, // so slaves are removed and bond is brought down (these settings are going to be reconciled back in the next sync cycle) // // For wireguard links, only settings are synced with the diff generated by the WireguardSpec. // //nolint:gocyclo,cyclop,dupl func (ctrl *LinkSpecController) syncLink(ctx context.Context, r controller.Runtime, logger *zap.Logger, conn *rtnetlink.Conn, wgClient *wgctrl.Client, links *[]rtnetlink.LinkMessage, link *network.LinkSpec, ) error { logger = logger.With(zap.String("link", link.TypedSpec().Name)) switch link.Metadata().Phase() { case resource.PhaseTearingDown: // TODO: should we bring link down if it's physical and the spec was torn down? if link.TypedSpec().Logical { existing := findLink(*links, link.TypedSpec().Name, false) // logical links don't have aliases if existing != nil { if err := conn.Link.Delete(existing.Index); err != nil { return fmt.Errorf("error deleting link %q: %w", link.TypedSpec().Name, err) } logger.Info("deleted link") // refresh links as the link list got changed var err error *links, err = conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } } } // now remove finalizer as link was deleted if err := r.RemoveFinalizer(ctx, link.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: existing := findLink(*links, link.TypedSpec().Name, !link.TypedSpec().Logical) // allow aliases for physical links var existingRawLinkData []byte if existing != nil && existing.Attributes != nil && existing.Attributes.Info != nil && existing.Attributes.Info.Data != nil { if existingLinkData, ok := existing.Attributes.Info.Data.(*rtnetlink.LinkData); ok { existingRawLinkData = existingLinkData.Data } } // check if type/kind matches for the existing logical link if existing != nil && link.TypedSpec().Logical { replace := false if existing.Attributes.Info == nil { logger.Warn("requested logical link has no info, skipping sync", zap.String("name", existing.Attributes.Name), zap.Stringer("type", nethelpers.LinkType(existing.Type)), zap.Uint32("index", existing.Index), ) return nil } // if type/kind doesn't match, recreate the link to change it if existing.Type != uint16(link.TypedSpec().Type) || existing.Attributes.Info.Kind != link.TypedSpec().Kind { logger.Info("replacing logical link", zap.String("old_kind", existing.Attributes.Info.Kind), zap.String("new_kind", link.TypedSpec().Kind), zap.Stringer("old_type", nethelpers.LinkType(existing.Type)), zap.Stringer("new_type", link.TypedSpec().Type), ) replace = true } // sync VLAN spec, as it can't be modified on the fly if !replace && link.TypedSpec().Kind == network.LinkKindVLAN { var existingVLAN network.VLANSpec if existingRawLinkData == nil { return fmt.Errorf("existing link %q has no data, can't decode VLAN settings", link.TypedSpec().Name) } if err := networkadapter.VLANSpec(&existingVLAN).Decode(existingRawLinkData); err != nil { return fmt.Errorf("error decoding VLAN properties on %q: %w", link.TypedSpec().Name, err) } if existingVLAN != link.TypedSpec().VLAN { logger.Info("replacing VLAN link", zap.Uint16("old_id", existingVLAN.VID), zap.Uint16("new_id", link.TypedSpec().VLAN.VID), zap.Stringer("old_protocol", existingVLAN.Protocol), zap.Stringer("new_protocol", link.TypedSpec().VLAN.Protocol), ) replace = true } } // sync VRF spec, as it can't be modified on the fly if !replace && link.TypedSpec().Kind == network.LinkKindVRF { var existingVRF network.VRFMasterSpec if existingRawLinkData == nil { return fmt.Errorf("existing link %q has no data, can't decode vrf settings", link.TypedSpec().Name) } if err := networkadapter.VRFMasterSpec(&existingVRF).Decode(existingRawLinkData); err != nil { return fmt.Errorf("error decoding vrf properties on %q: %w", link.TypedSpec().Name, err) } if existingVRF != link.TypedSpec().VRFMaster { logger.Info("replacing vrf link", zap.Stringer("old_table", existingVRF.Table), zap.Stringer("new_table", link.TypedSpec().VRFMaster.Table), ) replace = true } } if replace { if err := conn.Link.Delete(existing.Index); err != nil { return fmt.Errorf("error deleting link %q: %w", link.TypedSpec().Name, err) } // not refreshing links, as the link is set to be re-created existing = nil } } if existing == nil { if !link.TypedSpec().Logical { // physical interface doesn't exist yet, nothing to be done return nil } // create logical interface var ( masterIndex *uint32 parentIndex uint32 data []byte err error ) // VLAN settings should be set on interface creation (parent + VLAN settings) if link.TypedSpec().ParentName != "" { parent := findLink(*links, link.TypedSpec().ParentName, true) // allow aliases for physical links/parents if parent == nil { // parent doesn't exist yet, skip it return nil } parentIndex = parent.Index } if link.TypedSpec().Kind == network.LinkKindVLAN { data, err = networkadapter.VLANSpec(&link.TypedSpec().VLAN).Encode() if err != nil { return fmt.Errorf("error encoding VLAN attributes for link %q: %w", link.TypedSpec().Name, err) } } // vrf settings should be set on interface creation (parent + vrf settings) if link.TypedSpec().VRFSlave.MasterName != "" { master := findLink(*links, link.TypedSpec().VRFSlave.MasterName, false) if master == nil { // master doesn't exist yet, skip it return nil } masterIndex = &master.Index logger.Info("creating vrf slave link", zap.Uint32p("master_index", masterIndex), ) } if link.TypedSpec().Kind == network.LinkKindVRF { data, err = networkadapter.VRFMasterSpec(&link.TypedSpec().VRFMaster).Encode() if err != nil { return fmt.Errorf("error encoding vrf attributes for link %q: %w", link.TypedSpec().Name, err) } } if err = conn.Link.New(&rtnetlink.LinkMessage{ Type: uint16(link.TypedSpec().Type), Attributes: &rtnetlink.LinkAttributes{ Name: link.TypedSpec().Name, Address: net.HardwareAddr(link.TypedSpec().HardwareAddress), Type: parentIndex, Master: masterIndex, Info: &rtnetlink.LinkInfo{ Kind: link.TypedSpec().Kind, Data: &rtnetlink.LinkData{ Name: link.TypedSpec().Kind, Data: data, }, }, }, }); err != nil { return fmt.Errorf("error creating logical link %q: %w", link.TypedSpec().Name, err) } logger.Info("created new link", zap.String("kind", link.TypedSpec().Kind)) // refresh links as the link list got changed *links, err = conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } existing = findLink(*links, link.TypedSpec().Name, false) // link is created by name if existing == nil { return fmt.Errorf("created link %q not found in the link list", link.TypedSpec().Name) } } // sync bond settings if link.TypedSpec().Kind == network.LinkKindBond { var existingBond network.BondMasterSpec if existingRawLinkData == nil { return fmt.Errorf("existing link %q has no data, can't decode bond settings", link.TypedSpec().Name) } if err := networkadapter.BondMasterSpec(&existingBond).Decode(existingRawLinkData); err != nil { return fmt.Errorf("error parsing bond attributes for %q: %w", link.TypedSpec().Name, err) } // primaryIndex might be reported from the kernel, but if it's nil in the spec, we should treat it as equal if existingBond.PrimaryIndex != nil && link.TypedSpec().BondMaster.PrimaryIndex == nil { existingBond.PrimaryIndex = nil } if !existingBond.Equal(&link.TypedSpec().BondMaster) { logger.Debug("updating bond settings", zap.String("old", fmt.Sprintf("%+v", existingBond)), zap.String("new", fmt.Sprintf("%+v", link.TypedSpec().BondMaster)), ) data, err := networkadapter.BondMasterSpec(&link.TypedSpec().BondMaster).Encode() if err != nil { return fmt.Errorf("error encoding bond attributes for %q: %w", link.TypedSpec().Name, err) } // bring bond down if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Flags: 0, Change: unix.IFF_UP, }); err != nil { return fmt.Errorf("error changing flags for %q: %w", link.TypedSpec().Name, err) } // unslave all slaves for i, slave := range *links { if slave.Attributes.Master != nil && *slave.Attributes.Master == existing.Index { if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: slave.Family, Type: slave.Type, Index: slave.Index, Attributes: &rtnetlink.LinkAttributes{ Master: new(uint32(0)), }, }); err != nil { return fmt.Errorf("error unslaving link %q under %q: %w", slave.Attributes.Name, link.TypedSpec().BondSlave.MasterName, err) } (*links)[i].Attributes.Master = nil } } // update settings if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Attributes: &rtnetlink.LinkAttributes{ Info: &rtnetlink.LinkInfo{ Kind: existing.Attributes.Info.Kind, Data: &rtnetlink.LinkData{ Name: existing.Attributes.Info.Kind, Data: data, }, }, }, }); err != nil { return fmt.Errorf("error updating bond settings for %q: %w", link.TypedSpec().Name, err) } logger.Info("updated bond settings") } } // sync bridge settings if link.TypedSpec().Kind == network.LinkKindBridge { var existingBridge network.BridgeMasterSpec if existingRawLinkData == nil { return fmt.Errorf("existing link %q has no data, can't decode bridge settings", link.TypedSpec().Name) } if err := networkadapter.BridgeMasterSpec(&existingBridge).Decode(existingRawLinkData); err != nil { return fmt.Errorf("error parsing bridge attributes for %q: %w", link.TypedSpec().Name, err) } if existingBridge != link.TypedSpec().BridgeMaster { logger.Debug("updating bridge settings", zap.String("old", fmt.Sprintf("%+v", existingBridge)), zap.String("new", fmt.Sprintf("%+v", link.TypedSpec().BridgeMaster)), ) data, err := networkadapter.BridgeMasterSpec(&link.TypedSpec().BridgeMaster).Encode() if err != nil { return fmt.Errorf("error encoding bridge attributes for %q: %w", link.TypedSpec().Name, err) } // bring bridge down if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Flags: 0, Change: unix.IFF_UP, }); err != nil { return fmt.Errorf("error changing flags for %q: %w", link.TypedSpec().Name, err) } // unslave all slaves for i, slave := range *links { if slave.Attributes.Master != nil && *slave.Attributes.Master == existing.Index { if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: slave.Family, Type: slave.Type, Index: slave.Index, Attributes: &rtnetlink.LinkAttributes{ Master: new(uint32(0)), }, }); err != nil { return fmt.Errorf("error unslaving link %q under %q: %w", slave.Attributes.Name, link.TypedSpec().BridgeSlave.MasterName, err) } (*links)[i].Attributes.Master = nil } } // update settings if err = conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Attributes: &rtnetlink.LinkAttributes{ Info: &rtnetlink.LinkInfo{ Kind: existing.Attributes.Info.Kind, Data: &rtnetlink.LinkData{ Name: existing.Attributes.Info.Kind, Data: data, }, }, }, }); err != nil { return fmt.Errorf("error updating bridge settings for %q: %w", link.TypedSpec().Name, err) } logger.Info("updated bridge settings") } } // sync wireguard settings if link.TypedSpec().Kind == network.LinkKindWireguard { if wgClient == nil { return fmt.Errorf("wireguard client not available, cannot configure wireguard link %q", link.TypedSpec().Name) } wgDev, err := wgClient.Device(link.TypedSpec().Name) if err != nil { return fmt.Errorf("error getting wireguard settings for %q: %w", link.TypedSpec().Name, err) } var existingSpec network.WireguardSpec networkadapter.WireguardSpec(&existingSpec).Decode(wgDev, false) existingSpec.Sort() link.TypedSpec().Wireguard.Sort() // order here is important: we allow listenPort to be zero in the configuration if !existingSpec.Equal(&link.TypedSpec().Wireguard) { config, err := networkadapter.WireguardSpec(&link.TypedSpec().Wireguard).Encode(&existingSpec) if err != nil { return fmt.Errorf("error creating wireguard config patch for %q: %w", link.TypedSpec().Name, err) } if err = wgClient.ConfigureDevice(link.TypedSpec().Name, *config); err != nil { return fmt.Errorf("error configuring wireguard device %q: %w", link.TypedSpec().Name, err) } logger.Info("reconfigured wireguard link", zap.Int("peers", len(link.TypedSpec().Wireguard.Peers))) // notify link status controller, as wireguard updates can't be watched via netlink API if err = safe.WriterModify[*network.LinkRefresh](ctx, r, network.NewLinkRefresh(network.NamespaceName, network.LinkKindWireguard), func(r *network.LinkRefresh) error { r.TypedSpec().Bump() return nil }); err != nil { return errors.New("error bumping link refresh") } } } // sync UP flag existingUp := existing.Flags&unix.IFF_UP == unix.IFF_UP if existingUp != link.TypedSpec().Up { flags := uint32(0) if link.TypedSpec().Up { flags = unix.IFF_UP } if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Flags: flags, Change: unix.IFF_UP, }); err != nil { return fmt.Errorf("error changing flags for %q: %w", link.TypedSpec().Name, err) } logger.Debug("brought link up/down", zap.Bool("up", link.TypedSpec().Up)) } // sync MTU if it's set in the spec if link.TypedSpec().MTU != 0 && existing.Attributes.MTU != link.TypedSpec().MTU { if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Attributes: &rtnetlink.LinkAttributes{ MTU: link.TypedSpec().MTU, }, }); err != nil { return fmt.Errorf("error setting MTU for %q: %w", link.TypedSpec().Name, err) } existing.Attributes.MTU = link.TypedSpec().MTU logger.Info("changed MTU for the link", zap.Uint32("mtu", link.TypedSpec().MTU)) } // sync hardware address if it's set in the spec if len(link.TypedSpec().HardwareAddress) != 0 && !bytes.Equal(net.HardwareAddr(link.TypedSpec().HardwareAddress), existing.Attributes.Address) { if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Attributes: &rtnetlink.LinkAttributes{ Address: net.HardwareAddr(link.TypedSpec().HardwareAddress), }, }); err != nil { return fmt.Errorf("error setting hardware address for %q: %w", link.TypedSpec().Name, err) } existing.Attributes.Address = net.HardwareAddr(link.TypedSpec().HardwareAddress) logger.Info("changed hardware address for the link", zap.String("hwaddr", net.HardwareAddr(link.TypedSpec().HardwareAddress).String())) } // sync multicast flag if it's set in the spec if link.TypedSpec().Multicast != nil && ((existing.Flags&unix.IFF_MULTICAST == unix.IFF_MULTICAST) != *link.TypedSpec().Multicast) { flags := uint32(0) if *link.TypedSpec().Multicast { flags = unix.IFF_MULTICAST } if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Flags: flags, Change: unix.IFF_MULTICAST, }); err != nil { return fmt.Errorf("error changing multicast flag for %q: %w", link.TypedSpec().Name, err) } logger.Info("changed multicast flag for the link", zap.Bool("multicast", *link.TypedSpec().Multicast)) } // sync master index (for links which are bridge or bond slaves) var masterIndex uint32 var masterName string bondMasterName := link.TypedSpec().BondSlave.MasterName if bondMasterName != "" { if master := findLink(*links, bondMasterName, false); master != nil { // bond master can't be an alias masterName = bondMasterName masterIndex = master.Index } } bridgeMasterName := link.TypedSpec().BridgeSlave.MasterName if bridgeMasterName != "" { if master := findLink(*links, bridgeMasterName, false); master != nil { // bridge master can't be an alias masterName = bridgeMasterName masterIndex = master.Index } } vrfMasterName := link.TypedSpec().VRFSlave.MasterName if vrfMasterName != "" { if master := findLink(*links, vrfMasterName, false); master != nil { // vrf master can't be an alias masterName = vrfMasterName masterIndex = master.Index } } if (existing.Attributes.Master == nil && masterIndex != 0) || (existing.Attributes.Master != nil && *existing.Attributes.Master != masterIndex) { if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Change: unix.IFF_UP, }); err != nil { return fmt.Errorf("error bring down link %q before enslaving under %q: %w", link.TypedSpec().Name, masterName, err) } if err := conn.Link.Set(&rtnetlink.LinkMessage{ Family: existing.Family, Type: existing.Type, Index: existing.Index, Attributes: &rtnetlink.LinkAttributes{ Master: new(masterIndex), }, }); err != nil { return fmt.Errorf("error enslaving/unslaving link %q under %q: %w", link.TypedSpec().Name, masterName, err) } existing.Attributes.Master = new(masterIndex) logger.Info("enslaved/unslaved link", zap.String("parent", masterName)) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "fmt" "math/rand/v2" "net" "net/netip" "os" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "golang.org/x/sys/unix" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type LinkSpecSuite struct { ctest.DefaultSuite } func (suite *LinkSpecSuite) uniqueDummyInterface() string { return fmt.Sprintf("dummy%02x%02x%02x", rand.Int32()&0xff, rand.Int32()&0xff, rand.Int32()&0xff) } func (suite *LinkSpecSuite) TestLoopback() { loopback := network.NewLinkSpec(network.NamespaceName, "lo") *loopback.TypedSpec() = network.LinkSpecSpec{ Name: "lo", Up: true, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{loopback} { suite.Create(res) } ctest.AssertResource(suite, "lo", func(r *network.LinkStatus, asrt *assert.Assertions) {}) } func (suite *LinkSpecSuite) TestDummy() { dummyInterface := suite.uniqueDummyInterface() dummy := network.NewLinkSpec(network.NamespaceName, dummyInterface) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyInterface, Type: nethelpers.LinkEther, Kind: "dummy", MTU: 1400, Up: true, Logical: true, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{dummy} { suite.Create(res) } newHardwareAddr := net.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) asrt.EqualValues(1400, r.TypedSpec().MTU) asrt.NotEqual(newHardwareAddr, net.HardwareAddr(r.TypedSpec().HardwareAddr)) }) // attempt to change the hardware address ctest.UpdateWithConflicts(suite, dummy, func(r *network.LinkSpec) error { r.TypedSpec().HardwareAddress = nethelpers.HardwareAddr(newHardwareAddr) return nil }) ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(newHardwareAddr, net.HardwareAddr(r.TypedSpec().HardwareAddr)) }) // check default multicast behavior (disabled on dummy interfaces) ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(r.TypedSpec().Flags&unix.IFF_MULTICAST == unix.IFF_MULTICAST, false) }) // attempt to change multicast flag ctest.UpdateWithConflicts(suite, dummy, func(r *network.LinkSpec) error { r.TypedSpec().Multicast = new(true) return nil }) ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(r.TypedSpec().Flags&unix.IFF_MULTICAST == unix.IFF_MULTICAST, true) }) // attempt to disable multicast ctest.UpdateWithConflicts(suite, dummy, func(r *network.LinkSpec) error { r.TypedSpec().Multicast = new(bool) r.TypedSpec().Multicast = new(false) return nil }) ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(r.TypedSpec().Flags&unix.IFF_MULTICAST == unix.IFF_MULTICAST, false) }) suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), dummy.Metadata())) ctest.AssertNoResource[*network.LinkSpec](suite, dummyInterface) } func (suite *LinkSpecSuite) TestDummyWithMAC() { dummyInterface := suite.uniqueDummyInterface() newHardwareAddr := net.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} dummy := network.NewLinkSpec(network.NamespaceName, dummyInterface) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyInterface, Type: nethelpers.LinkEther, Kind: "dummy", HardwareAddress: nethelpers.HardwareAddr(newHardwareAddr), Up: true, Logical: true, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{dummy} { suite.Create(res) } ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(newHardwareAddr, net.HardwareAddr(r.TypedSpec().HardwareAddr)) }) suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), dummy.Metadata())) ctest.AssertNoResource[*network.LinkSpec](suite, dummyInterface) } //nolint:gocyclo func (suite *LinkSpecSuite) TestVLAN() { dummyInterface := suite.uniqueDummyInterface() dummy := network.NewLinkSpec(network.NamespaceName, dummyInterface) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyInterface, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, ConfigLayer: network.ConfigDefault, } vlanName1 := fmt.Sprintf("%s.%d", dummyInterface, 2) vlan1 := network.NewLinkSpec(network.NamespaceName, vlanName1) *vlan1.TypedSpec() = network.LinkSpecSpec{ Name: vlanName1, Type: nethelpers.LinkEther, Kind: network.LinkKindVLAN, Up: true, Logical: true, ParentName: dummyInterface, ConfigLayer: network.ConfigDefault, VLAN: network.VLANSpec{ VID: 2, Protocol: nethelpers.VLANProtocol8021Q, }, } vlanName2 := fmt.Sprintf("%s.%d", dummyInterface, 4) vlan2 := network.NewLinkSpec(network.NamespaceName, vlanName2) *vlan2.TypedSpec() = network.LinkSpecSpec{ Name: vlanName2, Type: nethelpers.LinkEther, Kind: network.LinkKindVLAN, Up: true, Logical: true, ParentName: dummyInterface, ConfigLayer: network.ConfigDefault, VLAN: network.VLANSpec{ VID: 4, Protocol: nethelpers.VLANProtocol8021Q, }, } for _, res := range []resource.Resource{dummy, vlan1, vlan2} { suite.Create(res) } ctest.AssertResources(suite, []string{dummyInterface, vlanName1, vlanName2}, func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case dummyInterface: asrt.Equal("dummy", r.TypedSpec().Kind) case vlanName1, vlanName2: asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal(nethelpers.VLANProtocol8021Q, r.TypedSpec().VLAN.Protocol) if r.Metadata().ID() == vlanName1 { asrt.EqualValues(2, r.TypedSpec().VLAN.VID) } else { asrt.EqualValues(4, r.TypedSpec().VLAN.VID) } } asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) }) // attempt to change VLAN ID ctest.UpdateWithConflicts(suite, vlan1, func(r *network.LinkSpec) error { r.TypedSpec().VLAN.VID = 42 return nil }) ctest.AssertResource(suite, vlanName1, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal(nethelpers.VLANProtocol8021Q, r.TypedSpec().VLAN.Protocol) asrt.EqualValues(42, r.TypedSpec().VLAN.VID) }) // teardown the links for _, r := range []resource.Resource{vlan1, vlan2, dummy} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, dummyInterface) ctest.AssertNoResource[*network.LinkStatus](suite, vlanName1) ctest.AssertNoResource[*network.LinkStatus](suite, vlanName2) } //nolint:gocyclo func (suite *LinkSpecSuite) TestVLANViaAlias() { dummyInterface := suite.uniqueDummyInterface() dummy := network.NewLinkSpec(network.NamespaceName, dummyInterface) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyInterface, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, ConfigLayer: network.ConfigDefault, } suite.Create(dummy) // create dummy interface, and create an alias for it manually ctest.AssertResource(suite, dummyInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) }) conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck iface, err := net.InterfaceByName(dummyInterface) suite.Require().NoError(err) dummyAlias := suite.uniqueDummyInterface() suite.Require().NoError( conn.Link.Set( &rtnetlink.LinkMessage{ Index: uint32(iface.Index), Attributes: &rtnetlink.LinkAttributes{ Alias: &dummyAlias, }, }, ), ) vlanName1 := fmt.Sprintf("%s.%d", dummyAlias, 2) vlan1 := network.NewLinkSpec(network.NamespaceName, vlanName1) *vlan1.TypedSpec() = network.LinkSpecSpec{ Name: vlanName1, Type: nethelpers.LinkEther, Kind: network.LinkKindVLAN, Up: true, Logical: true, ParentName: dummyAlias, ConfigLayer: network.ConfigDefault, VLAN: network.VLANSpec{ VID: 2, Protocol: nethelpers.VLANProtocol8021Q, }, } vlanName2 := fmt.Sprintf("%s.%d", dummyAlias, 4) vlan2 := network.NewLinkSpec(network.NamespaceName, vlanName2) *vlan2.TypedSpec() = network.LinkSpecSpec{ Name: vlanName2, Type: nethelpers.LinkEther, Kind: network.LinkKindVLAN, Up: true, Logical: true, ParentName: dummyAlias, ConfigLayer: network.ConfigDefault, VLAN: network.VLANSpec{ VID: 4, Protocol: nethelpers.VLANProtocol8021Q, }, } for _, res := range []resource.Resource{vlan1, vlan2} { suite.Create(res) } ctest.AssertResources(suite, []string{dummyInterface, vlanName1, vlanName2}, func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case dummyInterface: asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(dummyAlias, r.TypedSpec().Alias) case vlanName1, vlanName2: asrt.Equal(network.LinkKindVLAN, r.TypedSpec().Kind) asrt.Equal(nethelpers.VLANProtocol8021Q, r.TypedSpec().VLAN.Protocol) if r.Metadata().ID() == vlanName1 { asrt.EqualValues(2, r.TypedSpec().VLAN.VID) } else { asrt.EqualValues(4, r.TypedSpec().VLAN.VID) } } asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) }) // teardown the links for _, r := range []resource.Resource{vlan1, vlan2, dummy} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, dummyInterface) ctest.AssertNoResource[*network.LinkStatus](suite, vlanName1) ctest.AssertNoResource[*network.LinkStatus](suite, vlanName2) } //nolint:gocyclo func (suite *LinkSpecSuite) TestBond() { bondName := suite.uniqueDummyInterface() bond := network.NewLinkSpec(network.NamespaceName, bondName) *bond.TypedSpec() = network.LinkSpecSpec{ Name: bondName, Type: nethelpers.LinkEther, Kind: network.LinkKindBond, Up: true, Logical: true, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondModeActiveBackup, ARPAllTargets: nethelpers.ARPAllTargetsAll, PrimaryReselect: nethelpers.PrimaryReselectBetter, FailOverMac: nethelpers.FailOverMACFollow, ADSelect: nethelpers.ADSelectBandwidth, MIIMon: 100, DownDelay: 100, ResendIGMP: 2, UseCarrier: true, }, ConfigLayer: network.ConfigDefault, } networkadapter.BondMasterSpec(&bond.TypedSpec().BondMaster).FillDefaults() dummy0Name := suite.uniqueDummyInterface() dummy0 := network.NewLinkSpec(network.NamespaceName, dummy0Name) *dummy0.TypedSpec() = network.LinkSpecSpec{ Name: dummy0Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BondSlave: network.BondSlave{ MasterName: bondName, SlaveIndex: 0, }, ConfigLayer: network.ConfigDefault, } dummy1Name := suite.uniqueDummyInterface() dummy1 := network.NewLinkSpec(network.NamespaceName, dummy1Name) *dummy1.TypedSpec() = network.LinkSpecSpec{ Name: dummy1Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BondSlave: network.BondSlave{ MasterName: bondName, SlaveIndex: 1, }, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{dummy0, dummy1, bond} { suite.Create(res) } ctest.AssertResources(suite, []string{dummy0Name, dummy1Name, bondName}, func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case bondName: asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) case dummy0Name, dummy1Name: asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(nethelpers.OperStateUnknown, r.TypedSpec().OperationalState) asrt.NotZero(r.TypedSpec().MasterIndex) } }) // attempt to change bond type ctest.UpdateWithConflicts(suite, bond, func(r *network.LinkSpec) error { r.TypedSpec().BondMaster.Mode = nethelpers.BondModeRoundrobin return nil }) ctest.AssertResource(suite, bondName, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.BondModeRoundrobin, r.TypedSpec().BondMaster.Mode) }) // unslave one of the interfaces ctest.UpdateWithConflicts(suite, dummy0, func(r *network.LinkSpec) error { r.TypedSpec().BondSlave.MasterName = "" return nil }) ctest.AssertResource(suite, dummy0Name, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Zero(r.TypedSpec().MasterIndex) }) // teardown the links for _, r := range []resource.Resource{dummy0, dummy1, bond} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, dummy0Name) ctest.AssertNoResource[*network.LinkStatus](suite, dummy1Name) ctest.AssertNoResource[*network.LinkStatus](suite, bondName) } func (suite *LinkSpecSuite) TestBondActiveBackup() { bondName := suite.uniqueDummyInterface() bond := network.NewLinkSpec(network.NamespaceName, bondName) *bond.TypedSpec() = network.LinkSpecSpec{ Name: bondName, Type: nethelpers.LinkEther, Kind: network.LinkKindBond, Up: true, Logical: true, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondModeActiveBackup, HashPolicy: nethelpers.BondXmitPolicyLayer2, LACPRate: nethelpers.LACPRateSlow, ARPValidate: nethelpers.ARPValidateNone, ARPAllTargets: nethelpers.ARPAllTargetsAny, PrimaryReselect: nethelpers.PrimaryReselectAlways, FailOverMac: nethelpers.FailOverMACNone, }, ConfigLayer: network.ConfigDefault, } networkadapter.BondMasterSpec(&bond.TypedSpec().BondMaster).FillDefaults() for idx := range 2 { dummyName := suite.uniqueDummyInterface() dummy := network.NewLinkSpec(network.NamespaceName, dummyName) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyName, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BondSlave: network.BondSlave{ MasterName: bondName, SlaveIndex: idx, }, ConfigLayer: network.ConfigDefault, } suite.Create(dummy) } suite.Create(bond) ctest.AssertResource(suite, bondName, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) }) } //nolint:gocyclo func (suite *LinkSpecSuite) TestBond8023ad() { bondName := suite.uniqueDummyInterface() bond := network.NewLinkSpec(network.NamespaceName, bondName) *bond.TypedSpec() = network.LinkSpecSpec{ Name: bondName, Type: nethelpers.LinkEther, Kind: network.LinkKindBond, MTU: 9000, Up: true, Logical: true, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondMode8023AD, LACPRate: nethelpers.LACPRateFast, UseCarrier: true, }, ConfigLayer: network.ConfigDefault, } networkadapter.BondMasterSpec(&bond.TypedSpec().BondMaster).FillDefaults() //nolint:prealloc var ( dummies []resource.Resource dummyNames []string ) for range 4 { dummyName := suite.uniqueDummyInterface() dummy := network.NewLinkSpec(network.NamespaceName, dummyName) *dummy.TypedSpec() = network.LinkSpecSpec{ Name: dummyName, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BondSlave: network.BondSlave{ MasterName: bondName, SlaveIndex: 0, }, ConfigLayer: network.ConfigDefault, } dummies = append(dummies, dummy) dummyNames = append(dummyNames, dummyName) } for _, res := range append(dummies, bond) { suite.Create(res) } ctest.AssertResources(suite, slices.Concat(dummyNames, []string{bondName}), func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case bondName: asrt.Equal(network.LinkKindBond, r.TypedSpec().Kind) asrt.EqualValues(9000, r.TypedSpec().MTU) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) default: asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(nethelpers.OperStateUnknown, r.TypedSpec().OperationalState) asrt.NotZero(r.TypedSpec().MasterIndex) } }) // teardown the links for _, r := range append(dummies, bond) { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, bondName) for _, n := range dummyNames { ctest.AssertNoResource[*network.LinkStatus](suite, n) } } //nolint:gocyclo func (suite *LinkSpecSuite) TestBridge() { bridgeName := suite.uniqueDummyInterface() bridge := network.NewLinkSpec(network.NamespaceName, bridgeName) *bridge.TypedSpec() = network.LinkSpecSpec{ Name: bridgeName, Type: nethelpers.LinkEther, Kind: network.LinkKindBridge, Up: true, Logical: true, BridgeMaster: network.BridgeMasterSpec{ STP: network.STPSpec{ Enabled: false, }, }, ConfigLayer: network.ConfigDefault, } dummy0Name := suite.uniqueDummyInterface() dummy0 := network.NewLinkSpec(network.NamespaceName, dummy0Name) *dummy0.TypedSpec() = network.LinkSpecSpec{ Name: dummy0Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BridgeSlave: network.BridgeSlave{ MasterName: bridgeName, }, ConfigLayer: network.ConfigDefault, } dummy1Name := suite.uniqueDummyInterface() dummy1 := network.NewLinkSpec(network.NamespaceName, dummy1Name) *dummy1.TypedSpec() = network.LinkSpecSpec{ Name: dummy1Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, BridgeSlave: network.BridgeSlave{ MasterName: bridgeName, }, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{dummy0, dummy1, bridge} { suite.Create(res) } ctest.AssertResources(suite, []string{dummy0Name, dummy1Name, bridgeName}, func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case bridgeName: asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) case dummy0Name, dummy1Name: asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(nethelpers.OperStateUnknown, r.TypedSpec().OperationalState) asrt.NotZero(r.TypedSpec().MasterIndex) } }) // attempt to enable STP & VLAN filtering ctest.UpdateWithConflicts(suite, bridge, func(r *network.LinkSpec) error { r.TypedSpec().BridgeMaster.STP.Enabled = true r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled = true return nil }) ctest.AssertResource(suite, bridgeName, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(network.LinkKindBridge, r.TypedSpec().Kind) asrt.EqualValues(true, r.TypedSpec().BridgeMaster.STP.Enabled) asrt.EqualValues(true, r.TypedSpec().BridgeMaster.VLAN.FilteringEnabled) }) // unslave one of the interfaces ctest.UpdateWithConflicts(suite, dummy0, func(r *network.LinkSpec) error { r.TypedSpec().BridgeSlave.MasterName = "" return nil }) ctest.AssertResource(suite, dummy0Name, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Zero(r.TypedSpec().MasterIndex) }) // teardown the links for _, r := range []resource.Resource{dummy0, dummy1, bridge} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, dummy0Name) ctest.AssertNoResource[*network.LinkStatus](suite, dummy1Name) ctest.AssertNoResource[*network.LinkStatus](suite, bridgeName) } //nolint:gocyclo func (suite *LinkSpecSuite) TestVRF() { vrfName := suite.uniqueDummyInterface() vrf := network.NewLinkSpec(network.NamespaceName, vrfName) *vrf.TypedSpec() = network.LinkSpecSpec{ Name: vrfName, Type: nethelpers.LinkEther, Kind: network.LinkKindVRF, Up: true, Logical: true, VRFMaster: network.VRFMasterSpec{ Table: 123, }, ConfigLayer: network.ConfigDefault, } dummy0Name := suite.uniqueDummyInterface() dummy0 := network.NewLinkSpec(network.NamespaceName, dummy0Name) *dummy0.TypedSpec() = network.LinkSpecSpec{ Name: dummy0Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, VRFSlave: network.VRFSlave{ MasterName: vrfName, }, ConfigLayer: network.ConfigDefault, } dummy1Name := suite.uniqueDummyInterface() dummy1 := network.NewLinkSpec(network.NamespaceName, dummy1Name) *dummy1.TypedSpec() = network.LinkSpecSpec{ Name: dummy1Name, Type: nethelpers.LinkEther, Kind: "dummy", Up: true, Logical: true, VRFSlave: network.VRFSlave{ MasterName: vrfName, }, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{dummy0, dummy1, vrf} { suite.Create(res) } ctest.AssertResources(suite, []string{dummy0Name, dummy1Name, vrfName}, func(r *network.LinkStatus, asrt *assert.Assertions) { switch r.Metadata().ID() { case vrfName: asrt.Equal(network.LinkKindVRF, r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) case dummy0Name, dummy1Name: asrt.Equal("dummy", r.TypedSpec().Kind) asrt.Equal(nethelpers.OperStateUnknown, r.TypedSpec().OperationalState) asrt.NotZero(r.TypedSpec().MasterIndex) } }) // attempt to change the vrf table ctest.UpdateWithConflicts(suite, vrf, func(r *network.LinkSpec) error { r.TypedSpec().VRFMaster.Table = nethelpers.RoutingTable(124) return nil }) ctest.AssertResource(suite, vrfName, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(network.LinkKindVRF, r.TypedSpec().Kind) asrt.Equal(nethelpers.RoutingTable(124), r.TypedSpec().VRFMaster.Table) }) // unslave one of the interfaces ctest.UpdateWithConflicts(suite, dummy0, func(r *network.LinkSpec) error { r.TypedSpec().VRFSlave.MasterName = "" return nil }) ctest.AssertResource(suite, dummy0Name, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Zero(r.TypedSpec().MasterIndex) }) // teardown the links for _, r := range []resource.Resource{dummy0, dummy1, vrf} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, dummy0Name) ctest.AssertNoResource[*network.LinkStatus](suite, dummy1Name) ctest.AssertNoResource[*network.LinkStatus](suite, vrfName) } //nolint:gocyclo func (suite *LinkSpecSuite) TestWireguard() { if fipsmode.Strict() { suite.T().Skip("skipping test in strict FIPS mode") } priv, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) pub1, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) pub2, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) wgInterface := suite.uniqueDummyInterface() wg := network.NewLinkSpec(network.NamespaceName, wgInterface) *wg.TypedSpec() = network.LinkSpecSpec{ Name: wgInterface, Type: nethelpers.LinkNone, Kind: "wireguard", Up: true, Logical: true, Wireguard: network.WireguardSpec{ PrivateKey: priv.String(), FirewallMark: 1, Peers: []network.WireguardPeer{ { PublicKey: pub1.PublicKey().String(), Endpoint: "10.2.0.3:20000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.24.0.0/16"), }, }, { PublicKey: pub2.PublicKey().String(), AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("172.25.0.0/24"), }, }, }, }, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{wg} { suite.Create(res) } ctest.AssertResource(suite, wgInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal("wireguard", r.TypedSpec().Kind) asrt.Contains([]nethelpers.OperationalState{nethelpers.OperStateUp, nethelpers.OperStateUnknown}, r.TypedSpec().OperationalState) asrt.Equal(priv.PublicKey().String(), r.TypedSpec().Wireguard.PublicKey) asrt.Len(r.TypedSpec().Wireguard.Peers, 2) }) // attempt to change wireguard private key priv2, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) ctest.UpdateWithConflicts(suite, wg, func(r *network.LinkSpec) error { r.TypedSpec().Wireguard.PrivateKey = priv2.String() return nil }) ctest.AssertResource(suite, wgInterface, func(r *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(priv2.PublicKey().String(), r.TypedSpec().Wireguard.PublicKey) }) // teardown the links for _, r := range []resource.Resource{wg} { suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), r.Metadata())) } ctest.AssertNoResource[*network.LinkStatus](suite, wgInterface) } func TestLinkSpecSuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("requires root") } suite.Run(t, &LinkSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 15 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { // create fake device ready status deviceStatus := runtimeres.NewDevicesStatus(runtimeres.NamespaceName, runtimeres.DevicesID) deviceStatus.TypedSpec().Ready = true suite.Create(deviceStatus) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkSpecController{})) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkStatusController{})) }, }, }) } func TestSortBonds(t *testing.T) { expected := toResources([]network.LinkSpecSpec{ { Name: "A", }, { Name: "G", BondSlave: network.BondSlave{ MasterName: "A", SlaveIndex: 0, }, }, { Name: "C", }, { Name: "E", BondSlave: network.BondSlave{ MasterName: "C", SlaveIndex: 0, }, }, { Name: "F", BondSlave: network.BondSlave{ MasterName: "C", SlaveIndex: 1, }, }, { Name: "B", BondSlave: network.BondSlave{ MasterName: "C", SlaveIndex: 2, }, }, }) seed := time.Now().Unix() rnd := rand.New(rand.NewPCG(uint64(time.Now().Unix()), uint64(time.Now().Unix()))) for i := range 100 { res := safe.NewList[*network.LinkSpec](resource.List{ Items: safe.ToSlice(expected, func(r *network.LinkSpec) resource.Resource { return r }), }) rnd.Shuffle(res.Len(), res.Swap) netctrl.SortBonds(&res) require.Equal(t, expected, res, "failed with seed %d iteration %d", seed, i) } } func toResources(slice []network.LinkSpecSpec) safe.List[*network.LinkSpec] { return safe.NewList[*network.LinkSpec](resource.List{ Items: xslices.Map(slice, func(spec network.LinkSpecSpec) resource.Resource { link := network.NewLinkSpec(network.NamespaceName, "bar") *link.TypedSpec() = spec return link }), }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "net" "os" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/jsimonetti/rtnetlink/v2" "github.com/mdlayher/ethtool" ethtoolioctl "github.com/safchain/ethtool" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/internal/pkg/pci" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // LinkStatusController manages secrets.Etcd based on configuration. type LinkStatusController struct{} // Name implements controller.Controller interface. func (ctrl *LinkStatusController) Name() string { return "network.LinkStatusController" } // Inputs implements controller.Controller interface. func (ctrl *LinkStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *LinkStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: network.LinkStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *LinkStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for udevd to be healthy, which implies that all link renames are done if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.LinkSpecType, Kind: controller.InputStrong, }, }, ); err != nil { return err } // create watch connections to rtnetlink and ethtool via genetlink // these connections are used only to join multicast groups and receive notifications on changes // other connections are used to send requests and receive responses, as we can't mix the notifications and request/responses rtnetlinkWatcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_LINK) if err != nil { return err } defer rtnetlinkWatcher.Done() ethtoolWatcher, err := watch.NewEthtool(watch.NewDefaultRateLimitedTrigger(ctx, r)) if err != nil { logger.Warn("ethtool watcher failed to start", zap.Error(err)) } else { defer ethtoolWatcher.Done() } conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck ethClient, err := ethtool.New() if err != nil { logger.Warn("error dialing ethtool socket", zap.Error(err)) } else { defer ethClient.Close() //nolint:errcheck } ethIoctlClient, err := ethtoolioctl.NewEthtool() if err != nil { logger.Warn("error dialing ethtool ioctl socket", zap.Error(err)) } else { defer ethIoctlClient.Close() } wgClient, err := wgctrl.New() if err != nil { logger.Warn("error creating wireguard client", zap.Error(err)) } else { defer wgClient.Close() //nolint:errcheck } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if err = ctrl.reconcile(ctx, r, logger, conn, ethClient, ethIoctlClient, wgClient); err != nil { return err } r.ResetRestartBackoff() } } // reconcile function runs for every reconciliation loop querying the netlink state and updating resources. // //nolint:gocyclo,cyclop func (ctrl *LinkStatusController) reconcile( ctx context.Context, r controller.Runtime, logger *zap.Logger, conn *rtnetlink.Conn, ethClient *ethtool.Client, ethtoolIoctlClient *ethtoolioctl.Ethtool, wgClient *wgctrl.Client, ) error { // list the existing LinkStatus resources and mark them all to be deleted, as the actual link is discovered via netlink, resource ID is removed from the list list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } itemsToDelete := map[resource.ID]struct{}{} for _, r := range list.Items { itemsToDelete[r.Metadata().ID()] = struct{}{} } links, err := conn.Link.List() if err != nil { return fmt.Errorf("error listing links: %w", err) } // for every rtnetlink discovered link for _, link := range links { var ( ethState *ethtool.LinkState ethInfo *ethtool.LinkInfo ethMode *ethtool.LinkMode driverInfo ethtoolioctl.DrvInfo permanentAddr net.HardwareAddr ) if ethClient != nil { // query additional information via ethtool (if supported) ethState, err = ethClient.LinkState(ethtool.Interface{ Index: int(link.Index), }) if err != nil && !errors.Is(err, os.ErrNotExist) { logger.Warn("error querying ethtool link state", zap.String("link", link.Attributes.Name), zap.Error(err)) } // skip if previous call failed (e.g. not supported) if err == nil { ethInfo, err = ethClient.LinkInfo(ethtool.Interface{ Index: int(link.Index), }) if err != nil && !errors.Is(err, os.ErrNotExist) { logger.Warn("error querying ethtool link info", zap.String("link", link.Attributes.Name), zap.Error(err)) } } // skip if previous call failed (e.g. not supported) if err == nil { ethMode, err = ethClient.LinkMode(ethtool.Interface{ Index: int(link.Index), }) if err != nil && !errors.Is(err, os.ErrNotExist) { logger.Warn("error querying ethtool link mode", zap.String("link", link.Attributes.Name), zap.Error(err)) } } } if ethtoolIoctlClient != nil { driverInfo, _ = ethtoolIoctlClient.DriverInfo(link.Attributes.Name) //nolint:errcheck var permAddr string permAddr, err = ethtoolIoctlClient.PermAddr(link.Attributes.Name) if err == nil && permAddr != "" { permanentAddr, _ = net.ParseMAC(permAddr) //nolint:errcheck } if ethState == nil { state, err := ethtoolIoctlClient.LinkState(link.Attributes.Name) if err == nil { ethState = ðtool.LinkState{ Interface: ethtool.Interface{ Index: int(link.Index), }, Link: state > 0, } } } } if err = safe.WriterModify(ctx, r, network.NewLinkStatus(network.NamespaceName, link.Attributes.Name), func(r *network.LinkStatus) error { status := r.TypedSpec() status.Alias = pointer.SafeDeref(link.Attributes.Alias) status.AltNames = slices.Clone(link.Attributes.AltNames) status.Index = link.Index status.HardwareAddr = nethelpers.HardwareAddr(link.Attributes.Address) status.PermanentAddr = nethelpers.HardwareAddr(permanentAddr) status.BroadcastAddr = nethelpers.HardwareAddr(link.Attributes.Broadcast) status.LinkIndex = link.Attributes.Type status.Flags = nethelpers.LinkFlags(link.Flags) status.Type = nethelpers.LinkType(link.Type) status.QueueDisc = link.Attributes.QueueDisc status.MTU = link.Attributes.MTU if link.Attributes.Master != nil { status.MasterIndex = *link.Attributes.Master } else { status.MasterIndex = 0 } status.OperationalState = nethelpers.OperationalState(link.Attributes.OperationalState) if link.Attributes.Info != nil { status.Kind = link.Attributes.Info.Kind status.SlaveKind = link.Attributes.Info.SlaveKind } else { status.Kind = "" status.SlaveKind = "" } if ethState != nil { status.LinkState = ethState.Link } else { status.LinkState = false } if ethInfo != nil { status.Port = nethelpers.Port(ethInfo.Port) } else { status.Port = nethelpers.Port(ethtool.Other) } if ethMode != nil { status.SpeedMegabits = ethMode.SpeedMegabits status.Duplex = nethelpers.Duplex(ethMode.Duplex) } else { status.SpeedMegabits = 0 status.Duplex = nethelpers.Duplex(ethtool.Unknown) } var deviceInfo *nethelpers.DeviceInfo deviceInfo, err = nethelpers.GetDeviceInfo(link.Attributes.Name) if err != nil { logger.Warn("failure getting device information from /sys/class/net/*", zap.Error(err), zap.String("link", link.Attributes.Name)) } if deviceInfo != nil { status.BusPath = deviceInfo.BusPath status.Driver = deviceInfo.Driver status.PCIID = deviceInfo.PCIID } if status.Driver == "" { status.Driver = driverInfo.Driver } if status.BusPath == "" { status.BusPath = driverInfo.BusInfo } var pciDev *pci.Device pciDev, err = pci.SysfsDeviceInfo(driverInfo.BusInfo) if err != nil { logger.Warn("failure looking up sysfs PCI info", zap.Error(err), zap.String("link", link.Attributes.Name)) } if pciDev != nil { pciDev.LookupDB() status.VendorID = fmt.Sprintf("0x%04x", pciDev.VendorID) status.ProductID = fmt.Sprintf("0x%04x", pciDev.ProductID) status.Vendor = pciDev.Vendor status.Product = pciDev.Product } status.DriverVersion = driverInfo.Version status.FirmwareVersion = driverInfo.FwVersion // link.Attributes.Info will be non-nil, because we set status.Kind above using link.Attributes.Info.Kind var rawLinkData []byte if link.Attributes.Info != nil && link.Attributes.Info.Data != nil { if linkData, ok := link.Attributes.Info.Data.(*rtnetlink.LinkData); ok { rawLinkData = linkData.Data } } switch status.Kind { case network.LinkKindVLAN: if rawLinkData == nil { logger.Warn("VLAN link data is nil", zap.String("link", link.Attributes.Name)) } else if err = networkadapter.VLANSpec(&status.VLAN).Decode(rawLinkData); err != nil { logger.Warn("failure decoding VLAN attributes", zap.Error(err), zap.String("link", link.Attributes.Name)) } case network.LinkKindBond: if rawLinkData == nil { logger.Warn("bond link data is nil", zap.String("link", link.Attributes.Name)) } else if err = networkadapter.BondMasterSpec(&status.BondMaster).Decode(rawLinkData); err != nil { logger.Warn("failure decoding bond attributes", zap.Error(err), zap.String("link", link.Attributes.Name)) } case network.LinkKindBridge: if rawLinkData == nil { logger.Warn("bridge link data is nil", zap.String("link", link.Attributes.Name)) } else if err = networkadapter.BridgeMasterSpec(&status.BridgeMaster).Decode(rawLinkData); err != nil { logger.Warn("failure decoding bridge attributes", zap.Error(err), zap.String("link", link.Attributes.Name)) } case network.LinkKindVRF: if rawLinkData == nil { logger.Warn("vrf link data is nil", zap.String("link", link.Attributes.Name)) } else if err = networkadapter.VRFMasterSpec(&status.VRFMaster).Decode(rawLinkData); err != nil { logger.Warn("failure decoding vrf attributes", zap.Error(err), zap.String("link", link.Attributes.Name)) } case network.LinkKindWireguard: if wgClient == nil { return fmt.Errorf("wireguard client not available, but wireguard interface was discovered: %q", link.Attributes.Name) } var wgDev *wgtypes.Device wgDev, err = wgClient.Device(link.Attributes.Name) if err != nil { logger.Warn("failure getting wireguard attributes", zap.Error(err), zap.String("link", link.Attributes.Name)) } else { networkadapter.WireguardSpec(&status.Wireguard).Decode(wgDev, true) } } return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } delete(itemsToDelete, link.Attributes.Name) } for id := range itemsToDelete { if err = r.Destroy(ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, id, resource.VersionUndefined)); err != nil { return fmt.Errorf("error deleting link status %q: %w", id, err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/link_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "context" "errors" "fmt" "math/rand/v2" "net" "os" "strings" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/jsimonetti/rtnetlink/v2" "github.com/mdlayher/netlink" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "golang.org/x/sys/unix" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type LinkStatusSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *LinkStatusSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) // create fake device ready status deviceStatus := runtimeres.NewDevicesStatus(runtimeres.NamespaceName, runtimeres.DevicesID) deviceStatus.TypedSpec().Ready = true suite.Require().NoError(suite.state.Create(suite.ctx, deviceStatus)) suite.Require().NoError(suite.runtime.RegisterController(&netctrl.LinkStatusController{})) suite.startRuntime() } func (suite *LinkStatusSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *LinkStatusSuite) uniqueDummyInterface() string { return fmt.Sprintf("dummy%02x%02x%02x", rand.Int32()&0xff, rand.Int32()&0xff, rand.Int32()&0xff) } func (suite *LinkStatusSuite) assertInterfaces(requiredIDs []string, check func(*network.LinkStatus) error) error { missingIDs := make(map[string]struct{}, len(requiredIDs)) for _, id := range requiredIDs { missingIDs[id] = struct{}{} } resources, err := suite.state.List( suite.ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined), ) if err != nil { return err } for _, res := range resources.Items { _, required := missingIDs[res.Metadata().ID()] if !required { continue } delete(missingIDs, res.Metadata().ID()) if err = check(res.(*network.LinkStatus)); err != nil { return retry.ExpectedError(err) } } if len(missingIDs) > 0 { return retry.ExpectedErrorf("some resources are missing: %q", missingIDs) } return nil } func (suite *LinkStatusSuite) assertNoInterface(id string) error { resources, err := suite.state.List( suite.ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined), ) if err != nil { return err } for _, res := range resources.Items { if res.Metadata().ID() == id { return retry.ExpectedErrorf("interface %q is still there", id) } } return nil } func (suite *LinkStatusSuite) TestInterfaceHwInfo() { errNoInterfaces := errors.New("no suitable interfaces found") err := retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { resources, err := suite.state.List( suite.ctx, resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "", resource.VersionUndefined), ) suite.Require().NoError(err) for _, res := range resources.Items { spec := res.(*network.LinkStatus).TypedSpec() //nolint:forcetypeassert if !spec.Physical() { continue } if spec.Type != nethelpers.LinkEther { continue } var emptyFields []string for key, value := range map[string]string{ "hw addr": spec.HardwareAddr.String(), "perm addr": spec.PermanentAddr.String(), "driver": spec.Driver, "bus path": spec.BusPath, "PCI id": spec.PCIID, } { if value == "" { emptyFields = append(emptyFields, key) } } if len(emptyFields) > 0 { return fmt.Errorf("the interface %s has the following fields empty: %s", res.Metadata().ID(), strings.Join(emptyFields, ", ")) } return nil } return retry.ExpectedError(errNoInterfaces) }, ) if errors.Is(err, errNoInterfaces) { suite.T().Skip(err.Error()) } suite.Require().NoError(err) } func (suite *LinkStatusSuite) TestLoopbackInterface() { suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertInterfaces( []string{"lo"}, func(r *network.LinkStatus) error { suite.Assert().Equal("loopback", r.TypedSpec().Type.String()) suite.Assert().EqualValues(65536, r.TypedSpec().MTU) return nil }, ) }, ), ) } func (suite *LinkStatusSuite) TestDummyInterface() { if os.Geteuid() != 0 { suite.T().Skip("requires root") } dummyInterface := suite.uniqueDummyInterface() conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck suite.Require().NoError( conn.Link.New( &rtnetlink.LinkMessage{ Type: unix.ARPHRD_ETHER, Attributes: &rtnetlink.LinkAttributes{ Name: dummyInterface, MTU: 1400, Info: &rtnetlink.LinkInfo{ Kind: "dummy", }, }, }, ), ) iface, err := net.InterfaceByName(dummyInterface) suite.Require().NoError(err) defer conn.Link.Delete(uint32(iface.Index)) //nolint:errcheck suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertInterfaces( []string{dummyInterface}, func(r *network.LinkStatus) error { suite.Assert().Equal("ether", r.TypedSpec().Type.String()) suite.Assert().EqualValues(1400, r.TypedSpec().MTU) suite.Assert().Equal(nethelpers.OperStateDown, r.TypedSpec().OperationalState) return nil }, ) }, ), ) suite.Require().NoError( conn.Link.Set( &rtnetlink.LinkMessage{ Type: unix.ARPHRD_ETHER, Index: uint32(iface.Index), Flags: unix.IFF_UP, Change: unix.IFF_UP, }, ), ) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertInterfaces( []string{dummyInterface}, func(r *network.LinkStatus) error { if r.TypedSpec().OperationalState != nethelpers.OperStateUp && r.TypedSpec().OperationalState != nethelpers.OperStateUnknown { return retry.ExpectedErrorf( "operational state is not up: %s", r.TypedSpec().OperationalState, ) } return nil }, ) }, ), ) suite.Require().NoError(conn.Link.Delete(uint32(iface.Index))) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoInterface(dummyInterface) }, ), ) } func (suite *LinkStatusSuite) TestBridgeInterface() { if os.Geteuid() != 0 { suite.T().Skip("requires root") } bridgeInterface := suite.uniqueDummyInterface() conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck bridgeData, err := encodeBridgeData(true) suite.Require().NoError(err) suite.Require().NoError( conn.Link.New( &rtnetlink.LinkMessage{ Type: unix.ARPHRD_ETHER, Attributes: &rtnetlink.LinkAttributes{ Name: bridgeInterface, Info: &rtnetlink.LinkInfo{ Kind: "bridge", Data: &rtnetlink.LinkData{ Name: "bridge", Data: bridgeData, }, }, }, }, ), ) bridgeIface, err := net.InterfaceByName(bridgeInterface) suite.Require().NoError(err) defer conn.Link.Delete(uint32(bridgeIface.Index)) //nolint:errcheck suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertInterfaces( []string{bridgeInterface}, func(r *network.LinkStatus) error { suite.Assert().Equal("ether", r.TypedSpec().Type.String()) suite.Assert().True(r.TypedSpec().BridgeMaster.STP.Enabled) return nil }, ) }, ), ) } func encodeBridgeData(stpEnabled bool) ([]byte, error) { encoder := netlink.NewAttributeEncoder() var stpState uint32 if stpEnabled { stpState = 1 } encoder.Uint32(unix.IFLA_BR_STP_STATE, stpState) return encoder.Encode() } func (suite *LinkStatusSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestLinkStatusSuite(t *testing.T) { suite.Run(t, new(LinkStatusSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/network/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "net" "github.com/siderolabs/gen/pair/ordered" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // SetBondSlave sets the bond slave spec. func SetBondSlave(link *network.LinkSpecSpec, bond ordered.Pair[string, int]) { link.BondSlave = network.BondSlave{ MasterName: bond.F1, SlaveIndex: bond.F2, } } // SendBondMaster sets the bond master spec. func SendBondMaster(link *network.LinkSpecSpec, bond talosconfig.NetworkBondConfig) { link.Logical = true link.Kind = network.LinkKindBond link.Type = nethelpers.LinkEther link.BondMaster.Mode = bond.Mode() link.BondMaster.MIIMon = bond.MIIMon().ValueOrZero() link.BondMaster.UpDelay = bond.UpDelay().ValueOrZero() link.BondMaster.DownDelay = bond.DownDelay().ValueOrZero() link.BondMaster.HashPolicy = bond.XmitHashPolicy().ValueOrZero() link.BondMaster.ARPInterval = bond.ARPInterval().ValueOrZero() link.BondMaster.ARPIPTargets = bond.ARPIPTargets() link.BondMaster.NSIP6Targets = bond.NSIP6Targets() link.BondMaster.ARPValidate = bond.ARPValidate().ValueOrZero() link.BondMaster.ARPAllTargets = bond.ARPAllTargets().ValueOrZero() link.BondMaster.LACPRate = bond.LACPRate().ValueOrZero() link.BondMaster.FailOverMac = bond.FailOverMAC().ValueOrZero() link.BondMaster.ADSelect = bond.ADSelect().ValueOrZero() link.BondMaster.ADActorSysPrio = bond.ADActorSysPrio().ValueOrZero() link.BondMaster.ADUserPortKey = bond.ADUserPortKey().ValueOrZero() link.BondMaster.ADLACPActive = bond.ADLACPActive().ValueOr(nethelpers.ADLACPActiveOn) link.BondMaster.PrimaryReselect = bond.PrimaryReselect().ValueOrZero() link.BondMaster.ResendIGMP = bond.ResendIGMP().ValueOrZero() link.BondMaster.MinLinks = bond.MinLinks().ValueOrZero() link.BondMaster.LPInterval = bond.LPInterval().ValueOrZero() link.BondMaster.PacketsPerSlave = bond.PacketsPerSlave().ValueOrZero() link.BondMaster.NumPeerNotif = bond.NumPeerNotif().ValueOrZero() link.BondMaster.TLBDynamicLB = bond.TLBDynamicLB().ValueOrZero() link.BondMaster.AllSlavesActive = bond.AllSlavesActive().ValueOrZero() link.BondMaster.PeerNotifyDelay = bond.PeerNotifyDelay().ValueOrZero() link.BondMaster.MissedMax = bond.MissedMax().ValueOrZero() networkadapter.BondMasterSpec(&link.BondMaster).FillDefaults() } // SetBondMasterLegacy sets the bond master spec. // //nolint:gocyclo func SetBondMasterLegacy(link *network.LinkSpecSpec, bond talosconfig.Bond) error { link.Logical = true link.Kind = network.LinkKindBond link.Type = nethelpers.LinkEther bondMode, err := nethelpers.BondModeByName(bond.Mode()) if err != nil { return err } hashPolicy, err := nethelpers.BondXmitHashPolicyByName(bond.HashPolicy()) if err != nil { return err } lacpRate, err := nethelpers.LACPRateByName(bond.LACPRate()) if err != nil { return err } arpValidate, err := nethelpers.ARPValidateByName(bond.ARPValidate()) if err != nil { return err } arpAllTargets, err := nethelpers.ARPAllTargetsByName(bond.ARPAllTargets()) if err != nil { return err } var primary uint32 if bond.Primary() != "" { var iface *net.Interface iface, err = net.InterfaceByName(bond.Primary()) if err != nil { return err } primary = uint32(iface.Index) } primaryReselect, err := nethelpers.PrimaryReselectByName(bond.PrimaryReselect()) if err != nil { return err } failOverMAC, err := nethelpers.FailOverMACByName(bond.FailOverMac()) if err != nil { return err } adSelect, err := nethelpers.ADSelectByName(bond.ADSelect()) if err != nil { return err } link.BondMaster = network.BondMasterSpec{ Mode: bondMode, HashPolicy: hashPolicy, LACPRate: lacpRate, ARPValidate: arpValidate, ARPAllTargets: arpAllTargets, PrimaryIndex: new(primary), PrimaryReselect: primaryReselect, FailOverMac: failOverMAC, ADSelect: adSelect, MIIMon: bond.MIIMon(), UpDelay: bond.UpDelay(), DownDelay: bond.DownDelay(), ARPInterval: bond.ARPInterval(), ResendIGMP: bond.ResendIGMP(), MinLinks: bond.MinLinks(), LPInterval: bond.LPInterval(), PacketsPerSlave: bond.PacketsPerSlave(), NumPeerNotif: bond.NumPeerNotif(), TLBDynamicLB: bond.TLBDynamicLB(), AllSlavesActive: bond.AllSlavesActive(), ADActorSysPrio: bond.ADActorSysPrio(), ADUserPortKey: bond.ADUserPortKey(), PeerNotifyDelay: bond.PeerNotifyDelay(), ADLACPActive: nethelpers.ADLACPActiveOn, } networkadapter.BondMasterSpec(&link.BondMaster).FillDefaults() return nil } // SetBridgeSlave sets the bridge slave spec. func SetBridgeSlave(link *network.LinkSpecSpec, bridge string) { link.BridgeSlave = network.BridgeSlave{ MasterName: bridge, } } // SetBridgeMasterLegacy sets the bridge master spec. func SetBridgeMasterLegacy(link *network.LinkSpecSpec, bridge talosconfig.Bridge) error { link.Logical = true link.Kind = network.LinkKindBridge link.Type = nethelpers.LinkEther if bridge != nil { link.BridgeMaster = network.BridgeMasterSpec{ STP: network.STPSpec{ Enabled: bridge.STP().Enabled(), }, VLAN: network.BridgeVLANSpec{ FilteringEnabled: bridge.VLAN().FilteringEnabled(), }, } } return nil } // SetBridgeMaster sets the bridge master spec. func SetBridgeMaster(link *network.LinkSpecSpec, bridge talosconfig.NetworkBridgeConfig) { link.Logical = true link.Kind = network.LinkKindBridge link.Type = nethelpers.LinkEther link.BridgeMaster = network.BridgeMasterSpec{ STP: network.STPSpec{ Enabled: bridge.STP().Enabled().ValueOrZero(), }, VLAN: network.BridgeVLANSpec{ FilteringEnabled: bridge.VLAN().FilteringEnabled().ValueOrZero(), }, } } // SetVRFSlave sets the vrf slave spec. func SetVRFSlave(link *network.LinkSpecSpec, vrf string) { link.VRFSlave = network.VRFSlave{ MasterName: vrf, } } // SetVRFMaster sets the vrf master spec. func SetVRFMaster(link *network.LinkSpecSpec, vrf talosconfig.NetworkVRFConfig) { link.Logical = true link.Kind = network.LinkKindVRF link.Type = nethelpers.LinkEther link.VRFMaster = network.VRFMasterSpec{ Table: vrf.Table(), } } ================================================ FILE: internal/app/machined/pkg/controllers/network/network_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "go.uber.org/goleak" ) func TestMain(m *testing.M) { goleak.VerifyTestMain(m, goleak.IgnoreTopFunction("github.com/thejerf/suture/v4.(*Supervisor).removeService.func1.1"), ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/nftables_chain.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "slices" "strconv" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/google/nftables" "github.com/google/nftables/expr" "go.uber.org/zap" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NfTablesChainController applies network.NfTablesChain to the Linux nftables interface. type NfTablesChainController struct { TableName string } // Name implements controller.Controller interface. func (ctrl *NfTablesChainController) Name() string { return "network.NfTablesChainController" } // Inputs implements controller.Controller interface. func (ctrl *NfTablesChainController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.NfTablesChainType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NfTablesChainController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *NfTablesChainController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.TableName == "" { ctrl.TableName = constants.DefaultNfTablesTableName } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var conn nftables.Conn if err := ctrl.preCreateIptablesNFTable(logger, &conn); err != nil { return fmt.Errorf("error pre-creating iptables-nft table: %w", err) } list, err := safe.ReaderListAll[*network.NfTablesChain](ctx, r) if err != nil { return fmt.Errorf("error listing nftables chains: %w", err) } existingTables, err := conn.ListTablesOfFamily(nftables.TableFamilyINet) if err != nil { return fmt.Errorf("error listing existing nftables tables: %w", err) } var talosTable *nftables.Table if idx := slices.IndexFunc(existingTables, func(t *nftables.Table) bool { return t.Name == ctrl.TableName }); idx != -1 { talosTable = existingTables[idx] } if talosTable == nil { talosTable = &nftables.Table{ Family: nftables.TableFamilyINet, Name: ctrl.TableName, } conn.AddTable(talosTable) } // drop all chains, they will be re-created existingChains, err := conn.ListChains() if err != nil { return fmt.Errorf("error listing existing nftables chains: %w", err) } for _, chain := range existingChains { if chain.Table.Name != ctrl.TableName { // not our chain continue } conn.DelChain(chain) } setID := uint32(0) for chain := range list.All() { nfChain := conn.AddChain(&nftables.Chain{ Name: chain.Metadata().ID(), Table: talosTable, Hooknum: new(nftables.ChainHook(chain.TypedSpec().Hook)), Priority: new(nftables.ChainPriority(chain.TypedSpec().Priority)), Type: nftables.ChainType(chain.TypedSpec().Type), Policy: new(nftables.ChainPolicy(chain.TypedSpec().Policy)), }) for _, rule := range chain.TypedSpec().Rules { compiled, err := networkadapter.NfTablesRule(&rule).Compile() if err != nil { return fmt.Errorf("error compiling nftables rule for chain %s: %w", nfChain.Name, err) } for _, compiledRule := range compiled.Rules { // check for lookup rules and add/fix up the set ID if needed for i := range compiledRule { if lookup, ok := compiledRule[i].(*expr.Lookup); ok { if lookup.SetID >= uint32(len(compiled.Sets)) { return fmt.Errorf("invalid set ID %d in lookup", lookup.SetID) } set := compiled.Sets[lookup.SetID] setName := "__set" + strconv.Itoa(int(setID)) if err = conn.AddSet(&nftables.Set{ Table: talosTable, ID: setID, Name: setName, Anonymous: true, Constant: true, Interval: set.IsInterval(), KeyType: set.KeyType(), }, set.SetElements()); err != nil { return fmt.Errorf("error adding nftables set for chain %s: %w", nfChain.Name, err) } lookupOp := *lookup lookupOp.SetID = setID lookupOp.SetName = setName compiledRule[i] = &lookupOp setID++ } } conn.AddRule(&nftables.Rule{ Table: talosTable, Chain: nfChain, Exprs: compiledRule, }) } } } if err := conn.Flush(); err != nil { return fmt.Errorf("error flushing nftables: %w", err) } chainNames := safe.ToSlice(list, func(chain *network.NfTablesChain) string { return chain.Metadata().ID() }) logger.Info("nftables chains updated", zap.Strings("chains", chainNames)) r.ResetRestartBackoff() } } func (ctrl *NfTablesChainController) preCreateIptablesNFTable(logger *zap.Logger, conn *nftables.Conn) error { // Pre-create the iptables-nft table, if it doesn't exist. // This is required to ensure that the iptables universal binary prefers iptables-nft over // iptables-legacy can be used to manage the nftables rules. tables, err := conn.ListTablesOfFamily(nftables.TableFamilyIPv4) if err != nil { return fmt.Errorf("error listing existing nftables tables: %w", err) } if slices.IndexFunc(tables, func(t *nftables.Table) bool { return t.Name == "mangle" }) != -1 { return nil } table := &nftables.Table{ Family: nftables.TableFamilyIPv4, Name: "mangle", } conn.AddTable(table) chain := &nftables.Chain{ Name: "KUBE-IPTABLES-HINT", Table: table, Type: nftables.ChainTypeNAT, } conn.AddChain(chain) logger.Info("pre-created iptables-nft table 'mangle'/'KUBE-IPTABLES-HINT'") return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/nftables_chain_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // Chain names. const ( IngressChainName = "ingress" PreroutingChainName = "prerouting" ) // NfTablesChainConfigController generates nftables rules based on machine configuration. type NfTablesChainConfigController struct{} // Name implements controller.Controller interface. func (ctrl *NfTablesChainConfigController) Name() string { return "network.NfTablesChainConfigController" } // Inputs implements controller.Controller interface. func (ctrl *NfTablesChainConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NfTablesChainConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.NfTablesChainType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *NfTablesChainConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) (err error) { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } // try first to get filtered node addresses, if not available, use non-filtered one // this handles case of being part of Kubernetes cluster and not being part of it as well nodeAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting filtered node addresses: %w", err) } if nodeAddresses == nil { nodeAddresses, err = safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.NodeAddressRoutedID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting node addresses: %w", err) } } r.StartTrackingOutputs() if cfg != nil && !(cfg.Config().NetworkRules().DefaultAction() == nethelpers.DefaultActionAccept && cfg.Config().NetworkRules().Rules() == nil) { if err = safe.WriterModify(ctx, r, network.NewNfTablesChain(network.NamespaceName, IngressChainName), ctrl.buildIngressChain(cfg)); err != nil { return err } if nodeAddresses != nil { if err = safe.WriterModify(ctx, r, network.NewNfTablesChain(network.NamespaceName, PreroutingChainName), ctrl.buildPreroutingChain(cfg, nodeAddresses)); err != nil { return err } } } if err = safe.CleanupOutputs[*network.NfTablesChain](ctx, r); err != nil { return err } } } func (ctrl *NfTablesChainConfigController) buildIngressChain(cfg *config.MachineConfig) func(*network.NfTablesChain) error { return func(chain *network.NfTablesChain) error { spec := chain.TypedSpec() spec.Type = nethelpers.ChainTypeFilter spec.Hook = nethelpers.ChainHookInput spec.Priority = nethelpers.ChainPriorityMangle + 10 spec.Policy = nethelpers.VerdictAccept // preamble spec.Rules = []network.NfTablesRule{ // trusted interfaces: loopback, siderolink and kubespan { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, } defaultAction := cfg.Config().NetworkRules().DefaultAction() if defaultAction == nethelpers.DefaultActionBlock { spec.Policy = nethelpers.VerdictDrop spec.Rules = append(spec.Rules, // conntrack network.NfTablesRule{ MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateEstablished, nethelpers.ConntrackStateRelated, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, network.NfTablesRule{ MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateInvalid, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, // CVE-1999-0524 mitigation: drop timestamp and address mask ICMP requests network.NfTablesRule{ MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, MatchICMPType: &network.NfTablesICMPTypeMatch{ Types: []nethelpers.ICMPType{ nethelpers.ICMPTypeTimestampRequest, nethelpers.ICMPTypeTimestampReply, nethelpers.ICMPTypeAddressMaskRequest, nethelpers.ICMPTypeAddressMaskReply, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, // allow ICMP and ICMPv6 explicitly network.NfTablesRule{ MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, }, MatchLimit: &network.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, network.NfTablesRule{ MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMPv6, }, MatchLimit: &network.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, ) if cfg.Config().Machine() != nil && cfg.Config().Cluster() != nil { if cfg.Config().Machine().Features().HostDNS().ForwardKubeDNSToHost() { hostDNSIP := netip.MustParseAddr(constants.HostDNSAddress) // allow traffic to host DNS for _, protocol := range []nethelpers.Protocol{nethelpers.ProtocolUDP, nethelpers.ProtocolTCP} { spec.Rules = append(spec.Rules, network.NfTablesRule{ MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: xslices.Map( slices.Concat( cfg.Config().Cluster().Network().PodCIDRs(), cfg.Config().Cluster().Network().ServiceCIDRs(), ), netip.MustParsePrefix, ), }, MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{netip.PrefixFrom(hostDNSIP, hostDNSIP.BitLen())}, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: protocol, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{{Lo: 53, Hi: 53}}, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, ) } } } if cfg.Config().Cluster() != nil { spec.Rules = append(spec.Rules, // allow Kubernetes pod/service traffic network.NfTablesRule{ MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: xslices.Map( slices.Concat(cfg.Config().Cluster().Network().PodCIDRs(), cfg.Config().Cluster().Network().ServiceCIDRs()), netip.MustParsePrefix, ), }, MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: xslices.Map( slices.Concat(cfg.Config().Cluster().Network().PodCIDRs(), cfg.Config().Cluster().Network().ServiceCIDRs()), netip.MustParsePrefix, ), }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, ) } } for _, rule := range cfg.Config().NetworkRules().Rules() { portRanges := rule.PortRanges() // sort port ranges, machine config validation ensures that there are no overlaps slices.SortFunc(portRanges, func(a, b [2]uint16) int { return cmp.Compare(a[0], b[0]) }) // if default accept, drop anything that doesn't match the rule verdict := nethelpers.VerdictDrop if defaultAction == nethelpers.DefaultActionBlock { verdict = nethelpers.VerdictAccept } spec.Rules = append(spec.Rules, network.NfTablesRule{ MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: rule.Subnets(), ExcludeSubnets: rule.ExceptSubnets(), Invert: defaultAction == nethelpers.DefaultActionAccept, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: rule.Protocol(), MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: xslices.Map(portRanges, func(pr [2]uint16) network.PortRange { return network.PortRange{Lo: pr[0], Hi: pr[1]} }), }, }, AnonCounter: true, Verdict: new(verdict), }, ) } return nil } } func (ctrl *NfTablesChainConfigController) buildPreroutingChain(cfg *config.MachineConfig, nodeAddresses *network.NodeAddress) func(*network.NfTablesChain) error { // convert CIDRs to /32 (/128) prefixes matching only the address itself myAddresses := xslices.Map(nodeAddresses.TypedSpec().Addresses, func(addr netip.Prefix) netip.Prefix { return netip.PrefixFrom(addr.Addr(), addr.Addr().BitLen()) }, ) return func(chain *network.NfTablesChain) error { spec := chain.TypedSpec() spec.Type = nethelpers.ChainTypeFilter spec.Hook = nethelpers.ChainHookPrerouting spec.Priority = nethelpers.ChainPriorityNATDest - 10 spec.Policy = nethelpers.VerdictAccept defaultAction := cfg.Config().NetworkRules().DefaultAction() // preamble spec.Rules = []network.NfTablesRule{ // trusted interfaces: loopback, siderolink and kubespan { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, } // if the traffic is not addressed to the machine, ignore (accept it) spec.Rules = append(spec.Rules, network.NfTablesRule{ MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: myAddresses, Invert: true, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, ) // drop any 'new' connections to ports outside of the allowed ranges for _, rule := range cfg.Config().NetworkRules().Rules() { portRanges := rule.PortRanges() // sort port ranges, machine config validation ensures that there are no overlaps slices.SortFunc(portRanges, func(a, b [2]uint16) int { return cmp.Compare(a[0], b[0]) }) verdict := nethelpers.VerdictDrop if defaultAction == nethelpers.DefaultActionBlock { verdict = nethelpers.VerdictAccept } spec.Rules = append(spec.Rules, network.NfTablesRule{ MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: rule.Subnets(), ExcludeSubnets: rule.ExceptSubnets(), Invert: defaultAction == nethelpers.DefaultActionAccept, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: rule.Protocol(), MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: xslices.Map(portRanges, func(pr [2]uint16) network.PortRange { return network.PortRange{Lo: pr[0], Hi: pr[1]} }), }, }, AnonCounter: true, Verdict: new(verdict), }, ) } if defaultAction == nethelpers.DefaultActionBlock { // drop any TCP/UDP new connections spec.Rules = append(spec.Rules, network.NfTablesRule{ MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, network.NfTablesRule{ MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolUDP, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, ) } return nil } } ================================================ FILE: internal/app/machined/pkg/controllers/network/nftables_chain_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" configtypes "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type NfTablesChainConfigTestSuite struct { ctest.DefaultSuite } func (suite *NfTablesChainConfigTestSuite) injectConfig(block bool) { kubeletIngressCfg := networkcfg.NewRuleConfigV1Alpha1() kubeletIngressCfg.MetaName = "kubelet-ingress" kubeletIngressCfg.PortSelector.Ports = []networkcfg.PortRange{ { Lo: 10250, Hi: 10250, }, } kubeletIngressCfg.PortSelector.Protocol = nethelpers.ProtocolTCP kubeletIngressCfg.Ingress = []networkcfg.IngressRule{ { Subnet: netip.MustParsePrefix("10.0.0.0/8"), Except: networkcfg.Prefix{Prefix: netip.MustParsePrefix("10.3.0.0/16")}, }, { Subnet: netip.MustParsePrefix("192.168.0.0/16"), }, } apidIngressCfg := networkcfg.NewRuleConfigV1Alpha1() apidIngressCfg.MetaName = "apid-ingress" apidIngressCfg.PortSelector.Ports = []networkcfg.PortRange{ { Lo: 50000, Hi: 50000, }, } apidIngressCfg.PortSelector.Protocol = nethelpers.ProtocolTCP apidIngressCfg.Ingress = []networkcfg.IngressRule{ { Subnet: netip.MustParsePrefix("0.0.0.0/0"), }, } configs := []configtypes.Document{kubeletIngressCfg, apidIngressCfg} if block { defaultActionCfg := networkcfg.NewDefaultActionConfigV1Alpha1() defaultActionCfg.Ingress = nethelpers.DefaultActionBlock configs = append(configs, defaultActionCfg) } cfg, err := container.New(configs...) suite.Require().NoError(err) suite.Create(config.NewMachineConfig(cfg)) nodeAddresses := network.NewNodeAddress(network.NamespaceName, network.NodeAddressRoutedID) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.3.4.5/24")} suite.Create(nodeAddresses) } func (suite *NfTablesChainConfigTestSuite) TestDefaultAccept() { ctest.AssertNoResource[*network.NfTablesChain](suite, netctrl.IngressChainName) suite.injectConfig(false) ctest.AssertResource(suite, netctrl.IngressChainName, func(chain *network.NfTablesChain, asrt *assert.Assertions) { spec := chain.TypedSpec() asrt.Equal(nethelpers.ChainTypeFilter, spec.Type) asrt.Equal(nethelpers.ChainPriorityMangle+10, spec.Priority) asrt.Equal(nethelpers.ChainHookInput, spec.Hook) asrt.Equal(nethelpers.VerdictAccept, spec.Policy) asrt.Equal( []network.NfTablesRule{ { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("192.168.0.0/16"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 10250, Hi: 10250, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 50000, Hi: 50000, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, }, spec.Rules) }) ctest.AssertResource(suite, netctrl.PreroutingChainName, func(chain *network.NfTablesChain, asrt *assert.Assertions) { spec := chain.TypedSpec() asrt.Equal(nethelpers.ChainTypeFilter, spec.Type) asrt.Equal(nethelpers.ChainPriorityNATDest-10, spec.Priority) asrt.Equal(nethelpers.ChainHookPrerouting, spec.Hook) asrt.Equal(nethelpers.VerdictAccept, spec.Policy) asrt.Equal( []network.NfTablesRule{ { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.4.5/32"), }, Invert: true, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("192.168.0.0/16"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 10250, Hi: 10250, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 50000, Hi: 50000, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, }, spec.Rules) }) } func (suite *NfTablesChainConfigTestSuite) TestDefaultBlock() { ctest.AssertNoResource[*network.NfTablesChain](suite, netctrl.IngressChainName) suite.injectConfig(true) ctest.AssertResource(suite, netctrl.IngressChainName, func(chain *network.NfTablesChain, asrt *assert.Assertions) { spec := chain.TypedSpec() asrt.Equal(nethelpers.ChainTypeFilter, spec.Type) asrt.Equal(nethelpers.ChainPriorityMangle+10, spec.Priority) asrt.Equal(nethelpers.ChainHookInput, spec.Hook) asrt.Equal(nethelpers.VerdictDrop, spec.Policy) asrt.Equal( []network.NfTablesRule{ { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateEstablished, nethelpers.ConntrackStateRelated, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateInvalid, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, { MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, MatchICMPType: &network.NfTablesICMPTypeMatch{ Types: []nethelpers.ICMPType{ nethelpers.ICMPTypeTimestampRequest, nethelpers.ICMPTypeTimestampReply, nethelpers.ICMPTypeAddressMaskRequest, nethelpers.ICMPTypeAddressMaskReply, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, { MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, }, MatchLimit: &network.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMPv6, }, MatchLimit: &network.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("192.168.0.0/16"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 10250, Hi: 10250, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 50000, Hi: 50000, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, }, spec.Rules) }) ctest.AssertResource(suite, netctrl.PreroutingChainName, func(chain *network.NfTablesChain, asrt *assert.Assertions) { spec := chain.TypedSpec() asrt.Equal(nethelpers.ChainTypeFilter, spec.Type) asrt.Equal(nethelpers.ChainPriorityNATDest-10, spec.Priority) asrt.Equal(nethelpers.ChainHookPrerouting, spec.Hook) asrt.Equal(nethelpers.VerdictAccept, spec.Policy) asrt.Equal( []network.NfTablesRule{ { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{ "lo", constants.SideroLinkName, constants.KubeSpanLinkName, }, Operator: nethelpers.OperatorEqual, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.4.5/32"), }, Invert: true, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("192.168.0.0/16"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 10250, Hi: 10250, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 50000, Hi: 50000, }, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateNew, }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolUDP, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, }, spec.Rules) }) } func TestNfTablesChainConfig(t *testing.T) { t.Parallel() suite.Run(t, &NfTablesChainConfigTestSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.NfTablesChainConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/nftables_chain_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "os" "os/exec" "slices" "strings" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type NfTablesChainSuite struct { ctest.DefaultSuite } func (s *NfTablesChainSuite) nftOutput() string { out, err := exec.CommandContext(s.T().Context(), "nft", "list", "table", "inet", "talos-test").CombinedOutput() if err != nil { if strings.Contains(string(out), "No such file or directory") || strings.Contains(string(out), "No such table") { return "table inet talos-test {\n}" } } s.Require().NoError(err, "nft list table inet talos-test failed: %s", string(out)) return string(out) } func (s *NfTablesChainSuite) checkNftOutput(expected ...string) { s.T().Helper() var prevOutput string s.Eventually(func() bool { output := s.nftOutput() matches := slices.Contains(expected, strings.TrimSpace(output)) if output != prevOutput { if !matches { s.T().Logf("nft list table inet talos-test:\n%s", output) s.T().Logf("expected:\n%s", strings.Join(expected, "\n")) } prevOutput = output } return matches }, 5*time.Second, 100*time.Millisecond) } func (s *NfTablesChainSuite) TestEmpty() { s.checkNftOutput(`table inet talos-test { }`) } func (s *NfTablesChainSuite) TestAcceptLo() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPrioritySecurity chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchOIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{"lo"}, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority security; policy accept; oifname "lo" accept } }`) } func (s *NfTablesChainSuite) TestAcceptMultipleIfnames() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPrioritySecurity chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchIIfName: &network.NfTablesIfNameMatch{ InterfaceNames: []string{"eth0", "eth1"}, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) // this seems to be a bug in the nft cli, it doesn't decoded the ifname anonymous set correctly // it might be that google/nftables doesn't set some magic on the anonymous set for the nft CLI to pick it up (?) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority security; policy accept; iifname { "", "" } accept } }`) } func (s *NfTablesChainSuite) TestPolicyDrop() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPrioritySecurity chain.TypedSpec().Policy = nethelpers.VerdictDrop chain.TypedSpec().Rules = []network.NfTablesRule{ { Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority security; policy drop; accept } }`) } func (s *NfTablesChainSuite) TestICMPLimit() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPrioritySecurity chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, }, MatchLimit: &network.NfTablesLimitMatch{ PacketRatePerSecond: 5, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority security; policy accept; meta l4proto icmp limit rate 5/second burst 5 packets accept } }`) } func (s *NfTablesChainSuite) TestConntrackCounter() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPrioritySecurity chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateEstablished, nethelpers.ConntrackStateRelated, }, }, Verdict: new(nethelpers.VerdictAccept), }, { MatchConntrackState: &network.NfTablesConntrackStateMatch{ States: []nethelpers.ConntrackState{ nethelpers.ConntrackStateEstablished, // this rule should never match, as previous rule matches it }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority security; policy accept; ct state { 0x2000000, 0x4000000 } accept ct state established counter packets 0 bytes 0 drop } }`) } func (s *NfTablesChainSuite) TestMatchMarksSubnets() { chain1 := network.NewNfTablesChain(network.NamespaceName, "test1") chain1.TypedSpec().Type = nethelpers.ChainTypeFilter chain1.TypedSpec().Hook = nethelpers.ChainHookInput chain1.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain1.TypedSpec().Policy = nethelpers.VerdictAccept chain1.TypedSpec().Rules = []network.NfTablesRule{ { MatchMark: &network.NfTablesMark{ Mask: constants.KubeSpanDefaultFirewallMask, Value: constants.KubeSpanDefaultFirewallMark, }, MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("0::/0"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, Invert: true, }, MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/24"), }, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain1)) chain2 := network.NewNfTablesChain(network.NamespaceName, "test2") chain2.TypedSpec().Type = nethelpers.ChainTypeFilter chain2.TypedSpec().Hook = nethelpers.ChainHookInput chain2.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain2.TypedSpec().Policy = nethelpers.VerdictAccept chain2.TypedSpec().Rules = []network.NfTablesRule{ { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.3.5/32"), }, }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultFirewallMark, }, }, } s.Require().NoError(s.State().Create(s.Ctx(), chain2)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority filter; policy accept; meta mark & 0x00000060 == 0x00000020 ip saddr != { 10.0.0.0-10.2.255.255, 10.4.0.0-10.255.255.255 } ip daddr { 192.168.0.0/24 } accept } chain test2 { type filter hook input priority filter; policy accept; ip daddr { 192.168.3.5 } meta mark set meta mark & 0xffffffbf | 0x00000020 } }`) } func (s *NfTablesChainSuite) TestUpdateChains() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.3.0.0/16"), }, Invert: true, }, MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("192.168.0.0/24"), }, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority filter; policy accept; ip saddr != { 10.0.0.0-10.2.255.255, 10.4.0.0-10.255.255.255 } ip daddr { 192.168.0.0/24 } accept meta nfproto ipv6 accept } }`) chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.4.0.0/16"), }, Invert: true, }, SetMark: &network.NfTablesMark{ Mask: ^uint32(constants.KubeSpanDefaultFirewallMask), Xor: constants.KubeSpanDefaultFirewallMark, }, }, } s.Require().NoError(s.State().Update(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority filter; policy accept; ip saddr != { 10.0.0.0/14, 10.5.0.0-10.255.255.255 } meta mark set meta mark & 0xffffffbf | 0x00000020 meta nfproto ipv6 meta mark set meta mark & 0xffffffbf | 0x00000020 } }`) s.Require().NoError(s.State().Destroy(s.Ctx(), chain.Metadata())) s.checkNftOutput(`table inet talos-test { }`) } func (s *NfTablesChainSuite) TestClampMSS() { chain := network.NewNfTablesChain(network.NamespaceName, "test1") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchDestinationAddress: &network.NfTablesAddressMatch{ Invert: true, // match all addresses }, ClampMSS: &network.NfTablesClampMSS{ MTU: constants.KubeSpanLinkMTU, }, }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) // several versions here for different version of `nft` CLI decoding the rules s.checkNftOutput(`table inet talos-test { chain test1 { type filter hook input priority filter; policy accept; meta nfproto ipv4 tcp flags syn / syn,rst tcp option maxseg size > 1368 tcp option maxseg size set 1368 meta nfproto ipv6 tcp flags syn / syn,rst tcp option maxseg size > 1348 tcp option maxseg size set 1348 } }`, `table inet talos-test { chain test1 { type filter hook input priority filter; policy accept; meta nfproto ipv4 tcp flags & (syn | rst) == syn tcp option maxseg size > 1368 tcp option maxseg size set 1368 meta nfproto ipv6 tcp flags & (syn | rst) == syn tcp option maxseg size > 1348 tcp option maxseg size set 1348 } }`) } func (s *NfTablesChainSuite) TestL4Match() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchDestinationAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("2001::/16"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 1023, Hi: 1025, }, { Lo: 1027, Hi: 1029, }, }, }, }, Verdict: new(nethelpers.VerdictDrop), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; ip daddr { 10.0.0.0/8 } tcp dport { 1023-1025, 1027-1029 } drop ip6 daddr { 2001::/16 } tcp dport { 1023-1025, 1027-1029 } drop } }`) } func (s *NfTablesChainSuite) TestL4Match2() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 1023, Hi: 1023, }, { Lo: 1024, Hi: 1024, }, }, }, }, Verdict: new(nethelpers.VerdictDrop), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; ip saddr != { 10.0.0.0/8 } tcp dport { 1023-1024 } drop meta nfproto ipv6 tcp dport { 1023-1024 } drop } }`) } func (s *NfTablesChainSuite) TestL4MatchAdjacentPorts() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), }, Invert: true, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 5000, Hi: 5000, }, { Lo: 5001, Hi: 5001, }, { Lo: 10250, Hi: 10250, }, { Lo: 4240, Hi: 4240, }, }, }, }, Verdict: new(nethelpers.VerdictDrop), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; ip saddr != { 10.0.0.0/8 } tcp dport { 4240, 5000-5001, 10250 } drop meta nfproto ipv6 tcp dport { 4240, 5000-5001, 10250 } drop } }`) } func (s *NfTablesChainSuite) TestL4MatchAny() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 1023, Hi: 1023, }, }, }, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; meta nfproto ipv4 tcp dport { 1023 } accept } }`) } func (s *NfTablesChainSuite) TestL4MatchAnyWithHole() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchSourceAddress: &network.NfTablesAddressMatch{ IncludeSubnets: []netip.Prefix{ netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0"), }, ExcludeSubnets: []netip.Prefix{ netip.MustParsePrefix("10.1.2.3/32"), netip.MustParsePrefix("fe80::1/128"), }, }, MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolTCP, MatchDestinationPort: &network.NfTablesPortMatch{ Ranges: []network.PortRange{ { Lo: 1023, Hi: 1023, }, }, }, }, Verdict: new(nethelpers.VerdictAccept), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; ip saddr { 0.0.0.0-10.1.2.2, 10.1.2.4-255.255.255.255 } tcp dport { 1023 } accept ip6 saddr { ::-fe80::, fe80::2-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff } tcp dport { 1023 } accept } }`) } func (s *NfTablesChainSuite) TestICMPTypeMatch() { chain := network.NewNfTablesChain(network.NamespaceName, "test-tcp") chain.TypedSpec().Type = nethelpers.ChainTypeFilter chain.TypedSpec().Hook = nethelpers.ChainHookInput chain.TypedSpec().Priority = nethelpers.ChainPriorityFilter chain.TypedSpec().Policy = nethelpers.VerdictAccept chain.TypedSpec().Rules = []network.NfTablesRule{ { MatchLayer4: &network.NfTablesLayer4Match{ Protocol: nethelpers.ProtocolICMP, MatchICMPType: &network.NfTablesICMPTypeMatch{ Types: []nethelpers.ICMPType{ nethelpers.ICMPTypeTimestampRequest, nethelpers.ICMPTypeTimestampReply, nethelpers.ICMPTypeAddressMaskRequest, nethelpers.ICMPTypeAddressMaskReply, }, }, }, AnonCounter: true, Verdict: new(nethelpers.VerdictDrop), }, } s.Require().NoError(s.State().Create(s.Ctx(), chain)) s.checkNftOutput(`table inet talos-test { chain test-tcp { type filter hook input priority filter; policy accept; icmp type { timestamp-request, timestamp-reply, address-mask-request, address-mask-reply } counter packets 0 bytes 0 drop } }`) } func TestNftablesChainSuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("requires root") } if exec.CommandContext(t.Context(), "nft", "list", "tables").Run() != nil { t.Skip("requires nftables CLI to be installed") } suite.Run(t, &NfTablesChainSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { // try to see if the table is there if exec.CommandContext(s.Ctx(), "nft", "list", "table", "inet", "talos-test").Run() == nil { s.Require().NoError(exec.CommandContext(s.Ctx(), "nft", "delete", "table", "inet", "talos-test").Run()) } s.Require().NoError(s.Runtime().RegisterController(&netctrl.NfTablesChainController{TableName: "talos-test"})) }, AfterTearDown: func(s *ctest.DefaultSuite) { s.Require().NoError(exec.CommandContext(s.T().Context(), "nft", "delete", "table", "inet", "talos-test").Run()) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/node_address.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NodeAddressController manages secrets.Etcd based on configuration. type NodeAddressController struct{} // Name implements controller.Controller interface. func (ctrl *NodeAddressController) Name() string { return "network.NodeAddressController" } // Inputs implements controller.Controller interface. func (ctrl *NodeAddressController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.AddressStatusType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressFilterType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressSortAlgorithmType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeAddressController) Outputs() []controller.Output { return []controller.Output{ { Type: network.NodeAddressType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *NodeAddressController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { var addressStatusController AddressStatusController addressStatusControllerName := addressStatusController.Name() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // get algorithm to use algoRes, err := safe.ReaderGetByID[*network.NodeAddressSortAlgorithm](ctx, r, network.NodeAddressSortAlgorithmID) if err != nil { if state.IsNotFoundError(err) { // wait for the resource to appear continue } return fmt.Errorf("error getting sort algorithm: %w", err) } algo := algoRes.TypedSpec().Algorithm // fetch link and address status resources links, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing links: %w", err) } // build "link up" lookup table linksUp := make(map[uint32]struct{}) for link := range links.All() { if link.TypedSpec().OperationalState == nethelpers.OperStateUp || link.TypedSpec().OperationalState == nethelpers.OperStateUnknown { // skip physical interfaces without carrier if !link.TypedSpec().Physical() || link.TypedSpec().LinkState { linksUp[link.TypedSpec().Index] = struct{}{} } } } // fetch list of filters filters, err := safe.ReaderListAll[*network.NodeAddressFilter](ctx, r) if err != nil { return fmt.Errorf("error listing address filters: %w", err) } addressesList, err := safe.ReaderListAll[*network.AddressStatus](ctx, r) if err != nil { return fmt.Errorf("error listing links: %w", err) } addresses := safe.ToSlice(addressesList, func(a *network.AddressStatus) *network.AddressStatus { return a }) compareFunc := addressutil.CompareByAlgorithm(algo) // filter out addresses which should be ignored addresses = xslices.FilterInPlace(addresses, func(addr *network.AddressStatus) bool { if addr.TypedSpec().Scope >= nethelpers.ScopeLink { return false } ip := addr.TypedSpec().Address if ip.Addr().IsLoopback() || ip.Addr().IsMulticast() || ip.Addr().IsLinkLocalUnicast() { return false } return true }) slices.SortFunc(addresses, addressutil.CompareAddressStatuses(compareFunc)) var ( defaultAddress netip.Prefix current []netip.Prefix routed []netip.Prefix accumulative []netip.Prefix ) for _, addr := range addresses { ip := addr.TypedSpec().Address // set defaultAddress to the smallest IP from the alphabetically first link if addr.Metadata().Owner() == addressStatusControllerName { if value.IsZero(defaultAddress) { defaultAddress = ip } } // assume addresses from external IPs to be always up if _, up := linksUp[addr.TypedSpec().LinkIndex]; up || addr.TypedSpec().LinkName == externalLink { current = append(current, ip) } // routed: filter out external addresses and addresses from SideroLink if _, up := linksUp[addr.TypedSpec().LinkIndex]; up && addr.TypedSpec().LinkName != externalLink { if network.NotSideroLinkIP(ip.Addr()) { routed = append(routed, ip) } } accumulative = append(accumulative, ip) } // sort current addresses slices.SortFunc(current, compareFunc) slices.SortFunc(routed, compareFunc) // remove duplicates from current addresses current = addressutil.DeduplicateIPPrefixes(current) routed = addressutil.DeduplicateIPPrefixes(routed) touchedIDs := make(map[resource.ID]struct{}) // update output resources if !value.IsZero(defaultAddress) { if err = safe.WriterModify(ctx, r, network.NewNodeAddress(network.NamespaceName, network.NodeAddressDefaultID), func(r *network.NodeAddress) error { spec := r.TypedSpec() // never overwrite default address if it's already set // we should start handing default address updates, but for now we're not ready // // at the same time check that recorded default address is still on the host, if it's not => replace it // also replace default address on algorithm change if spec.SortAlgorithm == algo && len(spec.Addresses) > 0 && slices.ContainsFunc(current, func(addr netip.Prefix) bool { return spec.Addresses[0] == addr }) { return nil } spec.Addresses = []netip.Prefix{defaultAddress} spec.SortAlgorithm = algo return nil }); err != nil { return fmt.Errorf("error updating output resource: %w", err) } touchedIDs[network.NodeAddressDefaultID] = struct{}{} } if err = ctrl.updateCurrentAddresses(ctx, r, network.NodeAddressCurrentID, current, algo); err != nil { return err } touchedIDs[network.NodeAddressCurrentID] = struct{}{} if err = ctrl.updateCurrentAddresses(ctx, r, network.NodeAddressRoutedID, routed, algo); err != nil { return err } touchedIDs[network.NodeAddressRoutedID] = struct{}{} if err = ctrl.updateAccumulativeAddresses(ctx, r, network.NodeAddressAccumulativeID, accumulative, algo, compareFunc); err != nil { return err } touchedIDs[network.NodeAddressAccumulativeID] = struct{}{} // update filtered resources for filterRes := range filters.All() { filterID := filterRes.Metadata().ID() filter := filterRes.TypedSpec() filteredCurrent := addressutil.FilterIPs(current, filter.IncludeSubnets, filter.ExcludeSubnets) filteredRouted := addressutil.FilterIPs(routed, filter.IncludeSubnets, filter.ExcludeSubnets) filteredAccumulative := addressutil.FilterIPs(accumulative, filter.IncludeSubnets, filter.ExcludeSubnets) if err = ctrl.updateCurrentAddresses(ctx, r, network.FilteredNodeAddressID(network.NodeAddressCurrentID, filterID), filteredCurrent, algo); err != nil { return err } if err = ctrl.updateCurrentAddresses(ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, filterID), filteredRouted, algo); err != nil { return err } if err = ctrl.updateAccumulativeAddresses(ctx, r, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filterID), filteredAccumulative, algo, compareFunc); err != nil { return err } touchedIDs[network.FilteredNodeAddressID(network.NodeAddressCurrentID, filterID)] = struct{}{} touchedIDs[network.FilteredNodeAddressID(network.NodeAddressRoutedID, filterID)] = struct{}{} touchedIDs[network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filterID)] = struct{}{} } // list keys for cleanup list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.NodeAddressType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } func (ctrl *NodeAddressController) updateCurrentAddresses(ctx context.Context, r controller.Runtime, id resource.ID, current []netip.Prefix, algo nethelpers.AddressSortAlgorithm) error { if err := safe.WriterModify(ctx, r, network.NewNodeAddress(network.NamespaceName, id), func(r *network.NodeAddress) error { spec := r.TypedSpec() spec.Addresses = current spec.SortAlgorithm = algo return nil }); err != nil { return fmt.Errorf("error updating output resource: %w", err) } return nil } func (ctrl *NodeAddressController) updateAccumulativeAddresses( ctx context.Context, r controller.Runtime, id resource.ID, accumulative []netip.Prefix, algo nethelpers.AddressSortAlgorithm, compare func(a, b netip.Prefix) int, ) error { if err := safe.WriterModify(ctx, r, network.NewNodeAddress(network.NamespaceName, id), func(r *network.NodeAddress) error { spec := r.TypedSpec() for _, ip := range accumulative { // find insert position using binary search pos, _ := slices.BinarySearchFunc(spec.Addresses, ip, compare) if pos < len(spec.Addresses) && compare(spec.Addresses[pos], ip) == 0 { continue } // insert at position i spec.Addresses = slices.Insert(spec.Addresses, pos, ip) } spec.SortAlgorithm = algo return nil }); err != nil { return fmt.Errorf("error updating output resource: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/node_address_sort_algorithm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NodeAddressSortAlgorithmController manages NodeAddressSortAlgorithm based on configuration. type NodeAddressSortAlgorithmController struct{} // Name implements controller.Controller interface. func (ctrl *NodeAddressSortAlgorithmController) Name() string { return "network.NodeAddressSortAlgorithmController" } // Inputs implements controller.Controller interface. func (ctrl *NodeAddressSortAlgorithmController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *NodeAddressSortAlgorithmController) Outputs() []controller.Output { return []controller.Output{ { Type: network.NodeAddressSortAlgorithmType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *NodeAddressSortAlgorithmController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get %s: %w", config.MachineConfigType, err) } algorithm := nethelpers.AddressSortAlgorithmV1 if cfg != nil && cfg.Config().Machine() != nil { algorithm = cfg.Provider().Machine().Features().NodeAddressSortAlgorithm() } if err = safe.WriterModify(ctx, r, network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID), func(res *network.NodeAddressSortAlgorithm) error { res.TypedSpec().Algorithm = algorithm return nil }); err != nil { return fmt.Errorf("failed to update %s: %w", network.NodeAddressSortAlgorithmType, err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/node_address_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net/netip" "slices" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/addressutil" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type NodeAddressSuite struct { ctest.DefaultSuite } func (suite *NodeAddressSuite) TestDefaults() { // create fake device ready status deviceStatus := runtimeres.NewDevicesStatus(runtimeres.NamespaceName, runtimeres.DevicesID) deviceStatus.TypedSpec().Ready = true suite.Require().NoError(suite.State().Create(suite.Ctx(), deviceStatus)) sortAlgorithm := network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID) sortAlgorithm.TypedSpec().Algorithm = nethelpers.AddressSortAlgorithmV1 suite.Require().NoError(suite.State().Create(suite.Ctx(), sortAlgorithm)) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.AddressStatusController{})) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.LinkStatusController{})) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressRoutedID, network.NodeAddressAccumulativeID, }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses suite.T().Logf("id %q val %s", r.Metadata().ID(), addrs) asrt.True( slices.IsSortedFunc( addrs, addressutil.ComparePrefixesLegacy, ), "addresses %s", addrs, ) if r.Metadata().ID() == network.NodeAddressDefaultID { asrt.Len(addrs, 1) } else { asrt.NotEmpty(addrs) } }, ) } func (suite *NodeAddressSuite) newAddress(addr netip.Prefix, link *network.LinkStatus) { var addressStatusController netctrl.AddressStatusController addressStatus := network.NewAddressStatus(network.NamespaceName, network.AddressID(link.Metadata().ID(), addr)) addressStatus.TypedSpec().Address = addr addressStatus.TypedSpec().LinkName = link.Metadata().ID() addressStatus.TypedSpec().LinkIndex = link.TypedSpec().Index suite.Require().NoError( suite.State().Create( suite.Ctx(), addressStatus, state.WithCreateOwner(addressStatusController.Name()), ), ) } func (suite *NodeAddressSuite) newExternalAddress(addr netip.Prefix) { var platformConfigController netctrl.PlatformConfigApplyController addressStatus := network.NewAddressStatus(network.NamespaceName, network.AddressID("external", addr)) addressStatus.TypedSpec().Address = addr addressStatus.TypedSpec().LinkName = "external" suite.Require().NoError( suite.State().Create( suite.Ctx(), addressStatus, state.WithCreateOwner(platformConfigController.Name()), ), ) } //nolint:gocyclo func (suite *NodeAddressSuite) TestFilters() { linkUp := network.NewLinkStatus(network.NamespaceName, "eth0") linkUp.TypedSpec().Type = nethelpers.LinkEther linkUp.TypedSpec().LinkState = true linkUp.TypedSpec().Index = 1 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkUp)) linkDown := network.NewLinkStatus(network.NamespaceName, "eth1") linkDown.TypedSpec().Type = nethelpers.LinkEther linkDown.TypedSpec().LinkState = false linkDown.TypedSpec().Index = 2 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkDown)) sortAlgorithm := network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID) sortAlgorithm.TypedSpec().Algorithm = nethelpers.AddressSortAlgorithmV1 suite.Require().NoError(suite.State().Create(suite.Ctx(), sortAlgorithm)) for _, addr := range []string{ "10.0.0.1/8", "25.3.7.9/32", "2001:470:6d:30e:4a62:b3ba:180b:b5b8/64", "127.0.0.1/8", "fdae:41e4:649b:9303:7886:731d:1ce9:4d4/128", } { suite.newAddress(netip.MustParsePrefix(addr), linkUp) } for _, addr := range []string{"10.0.0.2/8", "192.168.3.7/24"} { suite.newAddress(netip.MustParsePrefix(addr), linkDown) } for _, addr := range []string{"1.2.3.4/32", "25.3.7.9/32"} { // duplicate with link address: 25.3.7.9 suite.newExternalAddress(netip.MustParsePrefix(addr)) } filter1 := network.NewNodeAddressFilter(network.NamespaceName, "no-k8s") filter1.TypedSpec().ExcludeSubnets = []netip.Prefix{netip.MustParsePrefix("10.0.0.0/8")} suite.Require().NoError(suite.State().Create(suite.Ctx(), filter1)) filter2 := network.NewNodeAddressFilter(network.NamespaceName, "only-k8s") filter2.TypedSpec().IncludeSubnets = []netip.Prefix{ netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("192.168.0.0/16"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), filter2)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressRoutedID, network.NodeAddressAccumulativeID, network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter2.Metadata().ID()), }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressDefaultID: asrt.Equal("10.0.0.1/8", stringifyIPs(addrs)) case network.NodeAddressCurrentID: asrt.Equal( "1.2.3.4/32 10.0.0.1/8 25.3.7.9/32 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64 fdae:41e4:649b:9303:7886:731d:1ce9:4d4/128", stringifyIPs(addrs), ) case network.NodeAddressRoutedID: asrt.Equal( "10.0.0.1/8 25.3.7.9/32 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64", stringifyIPs(addrs), ) case network.NodeAddressAccumulativeID: asrt.Equal( "1.2.3.4/32 10.0.0.1/8 10.0.0.2/8 25.3.7.9/32 192.168.3.7/24 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64 fdae:41e4:649b:9303:7886:731d:1ce9:4d4/128", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter1.Metadata().ID()): asrt.Equal( "1.2.3.4/32 25.3.7.9/32 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64 fdae:41e4:649b:9303:7886:731d:1ce9:4d4/128", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter1.Metadata().ID()): asrt.Equal( "25.3.7.9/32 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter1.Metadata().ID()): asrt.Equal( "1.2.3.4/32 25.3.7.9/32 192.168.3.7/24 2001:470:6d:30e:4a62:b3ba:180b:b5b8/64 fdae:41e4:649b:9303:7886:731d:1ce9:4d4/128", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter2.Metadata().ID()): asrt.Equal("10.0.0.1/8", stringifyIPs(addrs)) case network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter2.Metadata().ID()): asrt.Equal("10.0.0.1/8 10.0.0.2/8 192.168.3.7/24", stringifyIPs(addrs)) } }, ) } func (suite *NodeAddressSuite) TestSortAlgorithmV2() { linkUp := network.NewLinkStatus(network.NamespaceName, "eth0") linkUp.TypedSpec().Type = nethelpers.LinkEther linkUp.TypedSpec().LinkState = true linkUp.TypedSpec().Index = 1 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkUp)) linkDown := network.NewLinkStatus(network.NamespaceName, "eth1") linkDown.TypedSpec().Type = nethelpers.LinkEther linkDown.TypedSpec().LinkState = false linkDown.TypedSpec().Index = 2 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkDown)) sortAlgorithm := network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID) sortAlgorithm.TypedSpec().Algorithm = nethelpers.AddressSortAlgorithmV2 suite.Require().NoError(suite.State().Create(suite.Ctx(), sortAlgorithm)) for _, addr := range []string{ "1.2.3.4/26", // insert default address first, otherwise the test would be flaky, as default address is immutable "10.3.4.1/24", "10.3.4.5/24", "10.3.4.5/32", "192.168.35.11/24", "192.168.36.10/24", "127.0.0.1/8", "::1/128", "fd01:cafe::5054:ff:fe1f:c7bd/64", "fd01:cafe::f14c:9fa1:8496:557f/128", } { suite.newAddress(netip.MustParsePrefix(addr), linkUp) } for _, addr := range []string{"10.0.0.2/8", "192.168.3.7/24"} { suite.newAddress(netip.MustParsePrefix(addr), linkDown) } for _, addr := range []string{"1.2.3.4/26"} { // duplicate with link address: 1.2.3.4 suite.newExternalAddress(netip.MustParsePrefix(addr)) } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressRoutedID, network.NodeAddressAccumulativeID, }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressDefaultID: asrt.Equal("1.2.3.4/26", stringifyIPs(addrs)) case network.NodeAddressCurrentID, network.NodeAddressRoutedID: asrt.Equal( "1.2.3.4/26 10.3.4.5/32 10.3.4.1/24 10.3.4.5/24 192.168.35.11/24 192.168.36.10/24 fd01:cafe::f14c:9fa1:8496:557f/128 fd01:cafe::5054:ff:fe1f:c7bd/64", stringifyIPs(addrs), ) case network.NodeAddressAccumulativeID: asrt.Equal( "1.2.3.4/26 10.3.4.5/32 10.3.4.1/24 10.3.4.5/24 10.0.0.2/8 192.168.3.7/24 192.168.35.11/24 192.168.36.10/24 fd01:cafe::f14c:9fa1:8496:557f/128 fd01:cafe::5054:ff:fe1f:c7bd/64", stringifyIPs(addrs), ) } }, ) } func (suite *NodeAddressSuite) TestFilterOverlappingSubnets() { linkUp := network.NewLinkStatus(network.NamespaceName, "eth0") linkUp.TypedSpec().Type = nethelpers.LinkEther linkUp.TypedSpec().LinkState = true linkUp.TypedSpec().Index = 1 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkUp)) sortAlgorithm := network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID) sortAlgorithm.TypedSpec().Algorithm = nethelpers.AddressSortAlgorithmV1 suite.Require().NoError(suite.State().Create(suite.Ctx(), sortAlgorithm)) for _, addr := range []string{ "10.0.0.1/8", "10.96.0.2/32", "25.3.7.9/32", } { suite.newAddress(netip.MustParsePrefix(addr), linkUp) } filter1 := network.NewNodeAddressFilter(network.NamespaceName, "no-k8s") filter1.TypedSpec().ExcludeSubnets = []netip.Prefix{netip.MustParsePrefix("10.96.0.0/12")} suite.Require().NoError(suite.State().Create(suite.Ctx(), filter1)) filter2 := network.NewNodeAddressFilter(network.NamespaceName, "only-k8s") filter2.TypedSpec().IncludeSubnets = []netip.Prefix{netip.MustParsePrefix("10.96.0.0/12")} suite.Require().NoError(suite.State().Create(suite.Ctx(), filter2)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressCurrentID, network.NodeAddressRoutedID, network.NodeAddressAccumulativeID, network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter2.Metadata().ID()), }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressCurrentID, network.NodeAddressRoutedID, network.NodeAddressAccumulativeID: asrt.Equal( "10.0.0.1/8 10.96.0.2/32 25.3.7.9/32", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter1.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter1.Metadata().ID()): asrt.Equal( "10.0.0.1/8 25.3.7.9/32", stringifyIPs(addrs), ) case network.FilteredNodeAddressID(network.NodeAddressCurrentID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressRoutedID, filter2.Metadata().ID()), network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, filter2.Metadata().ID()): asrt.Equal( "10.96.0.2/32", stringifyIPs(addrs), ) } }, ) } //nolint:gocyclo func (suite *NodeAddressSuite) TestDefaultAddressChange() { var addressStatusController netctrl.AddressStatusController linkUp := network.NewLinkStatus(network.NamespaceName, "eth0") linkUp.TypedSpec().Type = nethelpers.LinkEther linkUp.TypedSpec().LinkState = true linkUp.TypedSpec().Index = 1 suite.Require().NoError(suite.State().Create(suite.Ctx(), linkUp)) sortAlgorithm := network.NewNodeAddressSortAlgorithm(network.NamespaceName, network.NodeAddressSortAlgorithmID) sortAlgorithm.TypedSpec().Algorithm = nethelpers.AddressSortAlgorithmV1 suite.Require().NoError(suite.State().Create(suite.Ctx(), sortAlgorithm)) for _, addr := range []string{ "10.0.0.5/8", "25.3.7.9/32", "127.0.0.1/8", } { suite.newAddress(netip.MustParsePrefix(addr), linkUp) } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressAccumulativeID, }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressDefaultID: asrt.Equal("10.0.0.5/8", stringifyIPs(addrs)) case network.NodeAddressCurrentID: asrt.Equal( "10.0.0.5/8 25.3.7.9/32", stringifyIPs(addrs), ) case network.NodeAddressAccumulativeID: asrt.Equal( "10.0.0.5/8 25.3.7.9/32", stringifyIPs(addrs), ) } }, ) // add another address which is "smaller", but default address shouldn't change suite.newAddress(netip.MustParsePrefix("1.1.1.1/32"), linkUp) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressAccumulativeID, }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressDefaultID: asrt.Equal("10.0.0.5/8", stringifyIPs(addrs)) case network.NodeAddressCurrentID: asrt.Equal( "1.1.1.1/32 10.0.0.5/8 25.3.7.9/32", stringifyIPs(addrs), ) case network.NodeAddressAccumulativeID: asrt.Equal( "1.1.1.1/32 10.0.0.5/8 25.3.7.9/32", stringifyIPs(addrs), ) } }, ) // remove the previous default address, now default address should change suite.Require().NoError(suite.State().Destroy(suite.Ctx(), network.NewAddressStatus(network.NamespaceName, network.AddressID(linkUp.Metadata().ID(), netip.MustParsePrefix("10.0.0.5/8"))).Metadata(), state.WithDestroyOwner(addressStatusController.Name()), )) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{ network.NodeAddressDefaultID, network.NodeAddressCurrentID, network.NodeAddressAccumulativeID, }, func(r *network.NodeAddress, asrt *assert.Assertions) { addrs := r.TypedSpec().Addresses switch r.Metadata().ID() { case network.NodeAddressDefaultID: asrt.Equal("1.1.1.1/32", stringifyIPs(addrs)) case network.NodeAddressCurrentID: asrt.Equal( "1.1.1.1/32 25.3.7.9/32", stringifyIPs(addrs), ) case network.NodeAddressAccumulativeID: asrt.Equal( "1.1.1.1/32 10.0.0.5/8 25.3.7.9/32", stringifyIPs(addrs), ) } }, ) } func TestNodeAddressSuite(t *testing.T) { t.Parallel() suite.Run(t, &NodeAddressSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.NodeAddressController{})) }, }, }) } func stringifyIPs(ips []netip.Prefix) string { return strings.Join(xslices.Map(ips, netip.Prefix.String), " ") } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/client_identifier.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package operator import ( "context" "encoding/hex" "fmt" "net" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv6" "github.com/insomniacslk/dhcp/iana" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // GetDHCPv6ClientIdentifier returns the DHCPv6 client identifier to use. func GetDHCPv6ClientIdentifier(ctx context.Context, st state.State, logger *zap.Logger, linkName string, spec network.ClientIdentifierSpec) ([]dhcpv6.Modifier, error) { switch spec.ClientIdentifier { case nethelpers.ClientIdentifierNone: return nil, nil //nolint:nilerr case nethelpers.ClientIdentifierMAC: link, err := safe.StateGetByID[*network.LinkStatus](ctx, st, linkName) if err != nil { return nil, fmt.Errorf("error getting link %q: %w", linkName, err) } if len(link.TypedSpec().HardwareAddr) == 0 { return nil, fmt.Errorf("link %q has no hardware address", linkName) } duid := dhcpv6.DUIDLL{ HWType: iana.HWTypeEthernet, LinkLayerAddr: net.HardwareAddr(link.TypedSpec().HardwareAddr), } return []dhcpv6.Modifier{dhcpv6.WithClientID(&duid)}, nil case nethelpers.ClientIdentifierDUID: if spec.DUIDRawHex == "" { return nil, fmt.Errorf("duidRawHex must be set when clientIdentifier is DUID") } duidBin, err := hex.DecodeString(spec.DUIDRawHex) if err != nil { logger.Error("failed to parse DUID, ignored", zap.String("link", linkName)) return nil, nil //nolint:nilerr } duid, err := dhcpv6.DUIDFromBytes(duidBin) if err != nil { logger.Error("failed to parse DUID, ignored", zap.String("link", linkName)) return nil, nil //nolint:nilerr } return []dhcpv6.Modifier{dhcpv6.WithClientID(duid)}, nil default: return nil, fmt.Errorf("unknown client identifier %d", spec.ClientIdentifier) } } // GetDHCP4ClientIdentifier returns the DHCP client identifier to use. func GetDHCP4ClientIdentifier(ctx context.Context, st state.State, logger *zap.Logger, linkName string, spec network.ClientIdentifierSpec) ([]dhcpv4.Modifier, error) { switch spec.ClientIdentifier { case nethelpers.ClientIdentifierNone: return nil, nil //nolint:nilerr case nethelpers.ClientIdentifierMAC: link, err := safe.StateGetByID[*network.LinkStatus](ctx, st, linkName) if err != nil { return nil, fmt.Errorf("error getting link %q: %w", linkName, err) } if len(link.TypedSpec().HardwareAddr) == 0 { return nil, fmt.Errorf("link %q has no hardware address", linkName) } // per RFC 2132, section 9.14 identifier := append([]byte{byte(iana.HWTypeEthernet)}, link.TypedSpec().HardwareAddr...) return []dhcpv4.Modifier{dhcpv4.WithOption(dhcpv4.OptClientIdentifier(identifier))}, nil case nethelpers.ClientIdentifierDUID: if spec.DUIDRawHex == "" { return nil, fmt.Errorf("duidRawHex must be set when clientIdentifier is DUID") } duidBin, err := hex.DecodeString(spec.DUIDRawHex) if err != nil { logger.Error("failed to parse DUID, ignored", zap.String("link", linkName)) return nil, nil //nolint:nilerr } _, err = dhcpv6.DUIDFromBytes(duidBin) if err != nil { logger.Error("failed to parse DUID, ignored", zap.String("link", linkName)) return nil, nil //nolint:nilerr } return []dhcpv4.Modifier{dhcpv4.WithOption(dhcpv4.OptClientIdentifier(duidBin))}, nil default: return nil, fmt.Errorf("unknown client identifier %d", spec.ClientIdentifier) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/dhcp4.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package operator import ( "context" "errors" "fmt" "net" "net/netip" "strings" "sync" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4/nclient4" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "go4.org/netipx" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DHCP4 implements the DHCPv4 network operator. type DHCP4 struct { logger *zap.Logger state state.State linkName string routeMetric uint32 clientIdentifier network.ClientIdentifierSpec skipHostnameRequest bool requestMTU bool lease *nclient4.Lease mu sync.Mutex addresses []network.AddressSpecSpec links []network.LinkSpecSpec routes []network.RouteSpecSpec hostname []network.HostnameSpecSpec resolvers []network.ResolverSpecSpec timeservers []network.TimeServerSpecSpec } // NewDHCP4 creates DHCPv4 operator. func NewDHCP4(logger *zap.Logger, linkName string, config network.DHCP4OperatorSpec, platform runtime.Platform, state state.State) *DHCP4 { return &DHCP4{ logger: logger, state: state, linkName: linkName, routeMetric: config.RouteMetric, skipHostnameRequest: config.SkipHostnameRequest, clientIdentifier: config.ClientIdentifier, // <3 azure // When including dhcp.OptionInterfaceMTU we don't get a dhcp offer back on azure. // So we'll need to explicitly exclude adding this option for azure. requestMTU: platform.Name() != "azure", } } // Prefix returns unique operator prefix which gets prepended to each spec. func (d *DHCP4) Prefix() string { return fmt.Sprintf("dhcp4/%s", d.linkName) } // extractHostname extracts a hostname from the given resource if it is a valid network.HostnameStatus. func extractHostname(res resource.Resource) network.HostnameStatusSpec { if res, ok := res.(*network.HostnameStatus); ok { return *res.TypedSpec() } return network.HostnameStatusSpec{} } // setupHostnameWatch returns the initial hostname and a channel that outputs all events related to hostname changes. func (d *DHCP4) setupHostnameWatch(ctx context.Context) (<-chan state.Event, error) { hostnameWatchCh := make(chan state.Event) if err := d.state.Watch(ctx, resource.NewMetadata( network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined, ), hostnameWatchCh); err != nil { return nil, err } return hostnameWatchCh, nil } // knownHostname checks if the given hostname has been defined by this operator. func (d *DHCP4) knownHostname(hostname network.HostnameStatusSpec) bool { d.mu.Lock() defer d.mu.Unlock() for i := range d.hostname { if d.hostname[i].FQDN() == hostname.FQDN() { return true } } return false } // waitForNetworkReady waits for the network to be ready and the leased address to // be assigned to the associated so that unicast operations can bind successfully. func (d *DHCP4) waitForNetworkReady(ctx context.Context) error { // If an IP address has been registered, wait for the address association to be ready if addresses := d.AddressSpecs(); len(addresses) > 0 { _, err := d.state.WatchFor(ctx, resource.NewMetadata( network.NamespaceName, network.AddressStatusType, network.AddressID(d.linkName, addresses[0].Address), resource.VersionUndefined, ), state.WithPhases(resource.PhaseRunning), ) if err != nil { return fmt.Errorf("failed to wait for the address association to be ready: %w", err) } } // Wait for the network (address and connectivity) to be ready if err := network.NewReadyCondition(d.state, network.AddressReady, network.ConnectivityReady).Wait(ctx); err != nil { return fmt.Errorf("failed to wait for the network address and connectivity to be ready: %w", err) } return nil } // Run the operator loop. // //nolint:gocyclo,cyclop func (d *DHCP4) Run(ctx context.Context, notifyCh chan<- struct{}) { const minRenewDuration = 5 * time.Second // Protect from renewing too often dhcpStartTime := time.Now() // Time when client began address acquisition or renewal process renewInterval := minRenewDuration // Never send the hostname on the first iteration, to have a chance to query the hostname from the DHCP server. // If the DHCP server doesn't provide a hostname, or if the hostname is overridden e.g. via machine config. // we'll restart the sequence and send the hostname. var hostname network.HostnameStatusSpec hostnameWatchCh, err := d.setupHostnameWatch(ctx) if err != nil && !errors.Is(err, context.Canceled) { d.logger.Warn("failed to watch for hostname changes", zap.Error(err)) } for { // Track if we need to acquire a new lease newLease := d.lease == nil // Perform a lease request or renewal leaseTime, err := d.requestRenew(ctx, hostname, uint16(time.Since(dhcpStartTime).Seconds())) if err != nil && !errors.Is(err, context.Canceled) { d.logger.Warn("DHCP request/renew failed", zap.Error(err), zap.String("link", d.linkName)) } if err == nil { // Notify the underlying controller about the new lease if !channel.SendWithContext(ctx, notifyCh, struct{}{}) { return } if newLease { // Wait for networking to be established before transitioning to unicast operations if err = d.waitForNetworkReady(ctx); err != nil && !errors.Is(err, context.Canceled) { d.logger.Warn("failed to wait for networking to become ready", zap.Error(err)) } } } if leaseTime > 0 { renewInterval = leaseTime / 2 } else { renewInterval /= 2 } renewInterval = max(renewInterval, minRenewDuration) for { select { case <-ctx.Done(): return case <-time.After(renewInterval): if leaseTime != 0 { // set the dhcpStartTime as the process of renewal has begun. dhcpStartTime = time.Now() } case event := <-hostnameWatchCh: // Attempt to drain the hostname watch channel coalescing multiple events into a single // change to the DHCP. drainLoop: for { select { case event = <-hostnameWatchCh: case <-ctx.Done(): return case <-time.After(time.Second): break drainLoop } } // If the hostname resource was deleted entirely, we must still inform the DHCP // server that the node has no hostname anymore. `extractHostname` will return a // blank hostname for a Tombstone resource generated by a deletion event. oldHostname := hostname hostname = extractHostname(event.Resource) d.logger.Debug("detected hostname change", zap.String("old", oldHostname.FQDN()), zap.String("new", hostname.FQDN()), ) // If, on first invocation, the DHCP server has given a new hostname for the node, // and the `network.HostnameSpecController` decides to apply it as a preferred // hostname, this operator would unnecessarily drop the lease and restart DHCP // discovery. Thus, if the selected hostname has been sourced from this operator, // we don't need to do anything. if (oldHostname == network.HostnameStatusSpec{} && d.knownHostname(hostname)) || oldHostname == hostname { continue } // While updating the hostname together with a RENEW request works with dnsmasq, it // doesn't work with the Windows Server DHCP + DNS. A hostname update via an // INIT-REBOOT request also gets ignored. Thus, the only reliable way to update the // hostname seems to be to forget the old release and initiate a new DISCOVER flow // with the new hostname. RFC 2131 doesn't define any better way to do this, and, // as a DISCOVER request cannot be targeted at the previous lessor according to the // spec, the node may switch DHCP servers on hostname change. However, this is not // a major concern, since a single network should not host multiple competing DHCP // servers in the first place. d.lease = nil d.logger.Debug("restarting DHCP sequence due to hostname change", zap.Strings("dhcp_hostname", xslices.Map(d.HostnameSpecs(), func(spec network.HostnameSpecSpec) string { return spec.Hostname })), ) // set the dhcpStartTime as the DHCP sequence has begun. dhcpStartTime = time.Now() } break } } } // AddressSpecs implements Operator interface. func (d *DHCP4) AddressSpecs() []network.AddressSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.addresses } // LinkSpecs implements Operator interface. func (d *DHCP4) LinkSpecs() []network.LinkSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.links } // RouteSpecs implements Operator interface. func (d *DHCP4) RouteSpecs() []network.RouteSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.routes } // HostnameSpecs implements Operator interface. func (d *DHCP4) HostnameSpecs() []network.HostnameSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.hostname } // ResolverSpecs implements Operator interface. func (d *DHCP4) ResolverSpecs() []network.ResolverSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.resolvers } // TimeServerSpecs implements Operator interface. func (d *DHCP4) TimeServerSpecs() []network.TimeServerSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.timeservers } //nolint:gocyclo func (d *DHCP4) parseNetworkConfigFromAck(ack *dhcpv4.DHCPv4, useHostname bool) { d.mu.Lock() defer d.mu.Unlock() addr, _ := netipx.FromStdIPNet(&net.IPNet{ IP: ack.YourIPAddr, Mask: ack.SubnetMask(), }) d.addresses = []network.AddressSpecSpec{ { Address: addr, LinkName: d.linkName, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Priority: d.routeMetric, ConfigLayer: network.ConfigOperator, }, } mtu, err := dhcpv4.GetUint16(dhcpv4.OptionInterfaceMTU, ack.Options) if err == nil { d.links = []network.LinkSpecSpec{ { Name: d.linkName, MTU: uint32(mtu), Up: true, }, } } else { d.links = nil } // rfc3442: // If the DHCP server returns both a Classless Static Routes option and // a Router option, the DHCP client MUST ignore the Router option. d.routes = nil if len(ack.ClasslessStaticRoute()) > 0 { for _, route := range ack.ClasslessStaticRoute() { gw, _ := netipx.FromStdIP(route.Router) dst, _ := netipx.FromStdIPNet(route.Dest) d.routes = append(d.routes, network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Destination: dst, Source: addr.Addr(), Gateway: gw, OutLinkName: d.linkName, Table: nethelpers.TableMain, Priority: d.routeMetric, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolBoot, ConfigLayer: network.ConfigOperator, }) } } else { for _, router := range ack.Router() { gw, _ := netipx.FromStdIP(router) d.routes = append(d.routes, network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Gateway: gw, Source: addr.Addr(), OutLinkName: d.linkName, Table: nethelpers.TableMain, Priority: d.routeMetric, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolBoot, ConfigLayer: network.ConfigOperator, }) if !addr.Contains(gw) { // Add an interface route for the gateway if it's not in the same network d.routes = append(d.routes, network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Destination: netip.PrefixFrom(gw, gw.BitLen()), Source: addr.Addr(), OutLinkName: d.linkName, Table: nethelpers.TableMain, Priority: d.routeMetric, Scope: nethelpers.ScopeLink, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolBoot, ConfigLayer: network.ConfigOperator, }) } } } for i := range d.routes { d.routes[i].Normalize() } if useHostname { d.hostname = nil hostname := strings.TrimRight(ack.HostName(), "\x00") if hostname != "" { spec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigOperator, } if err := spec.ParseFQDN(hostname); err == nil { domainName := strings.TrimRight(ack.DomainName(), "\x00") if domainName != "" { spec.Domainname = domainName } d.hostname = []network.HostnameSpecSpec{ spec, } } } } if len(ack.DNS()) > 0 { convertIP := func(ip net.IP) netip.Addr { result, _ := netipx.FromStdIP(ip) return result } d.resolvers = []network.ResolverSpecSpec{ { DNSServers: xslices.Map(ack.DNS(), convertIP), ConfigLayer: network.ConfigOperator, }, } } else { d.resolvers = nil } if len(ack.NTPServers()) > 0 { convertIP := func(ip net.IP) string { result, _ := netipx.FromStdIP(ip) return result.String() } d.timeservers = []network.TimeServerSpecSpec{ { NTPServers: xslices.Map(ack.NTPServers(), convertIP), ConfigLayer: network.ConfigOperator, }, } } else { d.timeservers = nil } } func (d *DHCP4) newClient() (*nclient4.Client, error) { var clientOpts []nclient4.ClientOpt // We have an existing lease, target the server with unicast. if d.lease != nil && d.lease.ACK.ServerIdentifier() != nil { // RFC 2131, section 4.3.2: // DHCPREQUEST generated during RENEWING state: // ... This message will be unicast, so no relay // agents will be involved in its transmission. clientOpts = append(clientOpts, nclient4.WithServerAddr(&net.UDPAddr{ IP: d.lease.ACK.ServerIdentifier(), Port: nclient4.ServerPort, }), // WithUnicast must be specified manually, WithServerAddr is not enough nclient4.WithUnicast(&net.UDPAddr{ IP: d.lease.ACK.YourIPAddr, Port: nclient4.ClientPort, }), ) } // Create a new client, the caller is responsible for closing it return nclient4.New(d.linkName, clientOpts...) } //nolint:gocyclo func (d *DHCP4) requestRenew(ctx context.Context, hostname network.HostnameStatusSpec, secs uint16) (time.Duration, error) { opts := []dhcpv4.OptionCode{ dhcpv4.OptionClasslessStaticRoute, dhcpv4.OptionDomainNameServer, // TODO(twelho): This is unused until network.ResolverSpec supports search domains dhcpv4.OptionDNSDomainSearchList, dhcpv4.OptionNTPServers, } if d.requestMTU { opts = append(opts, dhcpv4.OptionInterfaceMTU) } sendHostnameRequest := !d.skipHostnameRequest if hostname.Hostname != "" && !d.knownHostname(hostname) { // If we are supposed to publish a hostname, don't request one from the DHCP server. // // DHCP hostname parroting protection: if, e.g., `dnsmasq` receives a request that both // sends a hostname and requests one, it will "parrot" the sent hostname back if no other // name has been defined for the requesting host. This causes update anomalies, since // removing a hostname defined previously by, e.g., the configuration layer, causes a copy // of that hostname to live on in a spec defined by this operator, even though it isn't // sourced from DHCP. // // To avoid this issue, never send and request a hostname in the same operation. When // negotiating a new lease, first send the current hostname when acquiring the lease, and // then follow up with a dedicated INFORM request asking the server for a DHCP-defined // hostname. When renewing a lease, we're free to always request a hostname with an INFORM // (to detect server-side changes), since any changes to the node hostname will cause a // lease invalidation and re-start the negotiation process. More details below. sendHostnameRequest = false } if sendHostnameRequest { opts = append(opts, dhcpv4.OptionHostName, dhcpv4.OptionDomainName) } mods := []dhcpv4.Modifier{dhcpv4.WithRequestedOptions(opts...), WithNumSeconds(secs)} if !sendHostnameRequest { // If the node has a hostname, always send it to the DHCP // server with option 12 during lease acquisition and renewal if len(hostname.Hostname) > 0 { mods = append(mods, dhcpv4.WithOption(dhcpv4.OptHostName(hostname.Hostname))) } if len(hostname.Domainname) > 0 { mods = append(mods, dhcpv4.WithOption(dhcpv4.OptDomainName(hostname.Domainname))) } } clientIdentifierModes, err := GetDHCP4ClientIdentifier(ctx, d.state, d.logger, d.linkName, d.clientIdentifier) if err != nil { return 0, fmt.Errorf("failed to get client identifier: %w", err) } mods = append(mods, clientIdentifierModes...) client, err := d.newClient() if err != nil { return 0, err } //nolint:errcheck defer client.Close() addresses := d.AddressSpecs() switch { case d.lease != nil && d.lease.ACK.ServerIdentifier() != nil: d.logger.Debug("DHCP RENEW", zap.String("link", d.linkName)) d.lease, err = client.Renew(ctx, d.lease, mods...) case d.lease != nil && d.lease.Offer != nil: d.logger.Debug("DHCP REQUEST FROM OFFER", zap.String("link", d.linkName)) d.lease, err = client.RequestFromOffer(ctx, d.lease.Offer, mods...) case len(addresses) >= 1: previousIPAddress := net.IP(addresses[0].Address.Addr().AsSlice()) d.logger.Debug("DHCP REQUEST with previous IP", zap.String("link", d.linkName), zap.Stringer("previous_ip", previousIPAddress)) d.lease, err = client.Request(ctx, dhcpv4.PrependModifiers(mods, dhcpv4.WithOption(dhcpv4.OptRequestedIPAddress(previousIPAddress)), )...) default: d.logger.Debug("DHCP REQUEST", zap.String("link", d.linkName)) d.lease, err = client.Request(ctx, mods...) } if err != nil { // explicitly clear the lease on failure to start with the discovery sequence next time d.lease = nil return 0, err } d.logger.Debug("DHCP ACK", zap.String("link", d.linkName), zap.String("dhcp", collapseSummary(d.lease.ACK.Summary()))) d.parseNetworkConfigFromAck(d.lease.ACK, sendHostnameRequest) return d.lease.ACK.IPAddressLeaseTime(time.Minute * 30), nil } func collapseSummary(summary string) string { lines := strings.Split(summary, "\n")[1:] for i := range lines { lines[i] = strings.TrimSpace(lines[i]) } if len(lines) > 0 && lines[len(lines)-1] == "" { lines = lines[:len(lines)-1] } return strings.Join(lines, ", ") } // WithNumSeconds sets the secs field of a DHCPv4 packet. func WithNumSeconds(secs uint16) dhcpv4.Modifier { return func(d *dhcpv4.DHCPv4) { d.NumSeconds = secs } } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/dhcp6.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package operator import ( "context" "errors" "fmt" "net" "net/netip" "strings" "sync" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/insomniacslk/dhcp/dhcpv6" "github.com/insomniacslk/dhcp/dhcpv6/nclient6" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "go.uber.org/zap" "go4.org/netipx" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // DHCP6 implements the DHCPv6 network operator. type DHCP6 struct { logger *zap.Logger state state.State linkName string clientIdentifier network.ClientIdentifierSpec skipHostnameRequest bool mu sync.Mutex addresses []network.AddressSpecSpec hostname []network.HostnameSpecSpec resolvers []network.ResolverSpecSpec timeservers []network.TimeServerSpecSpec } // NewDHCP6 creates DHCPv6 operator. func NewDHCP6(logger *zap.Logger, linkName string, config network.DHCP6OperatorSpec, state state.State) *DHCP6 { return &DHCP6{ logger: logger, state: state, linkName: linkName, clientIdentifier: config.ClientIdentifier, skipHostnameRequest: config.SkipHostnameRequest, } } // Prefix returns unique operator prefix which gets prepended to each spec. func (d *DHCP6) Prefix() string { return fmt.Sprintf("dhcp6/%s", d.linkName) } // Run the operator loop. // //nolint:gocyclo func (d *DHCP6) Run(ctx context.Context, notifyCh chan<- struct{}) { iface, err := net.InterfaceByName(d.linkName) if err != nil { d.logger.Warn("link not found", zap.String("link", d.linkName)) } else if err = d.waitIPv6LinkReady(ctx, iface); err != nil { d.logger.Warn("error waiting for IPv6 ready", zap.Error(err), zap.String("link", d.linkName)) } const minRenewDuration = 5 * time.Second // protect from renewing too often renewInterval := minRenewDuration for { leaseTime, err := d.renew(ctx) if err != nil && !errors.Is(err, context.Canceled) { d.logger.Warn("renew failed", zap.Error(err), zap.String("link", d.linkName)) } if err == nil { select { case notifyCh <- struct{}{}: case <-ctx.Done(): return } } if leaseTime > 0 { renewInterval = leaseTime / 2 } else { renewInterval /= 2 } renewInterval = max(renewInterval, minRenewDuration) select { case <-ctx.Done(): return case <-time.After(renewInterval): } } } // AddressSpecs implements Operator interface. func (d *DHCP6) AddressSpecs() []network.AddressSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.addresses } // LinkSpecs implements Operator interface. func (d *DHCP6) LinkSpecs() []network.LinkSpecSpec { return nil } // RouteSpecs implements Operator interface. func (d *DHCP6) RouteSpecs() []network.RouteSpecSpec { return nil } // HostnameSpecs implements Operator interface. func (d *DHCP6) HostnameSpecs() []network.HostnameSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.hostname } // ResolverSpecs implements Operator interface. func (d *DHCP6) ResolverSpecs() []network.ResolverSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.resolvers } // TimeServerSpecs implements Operator interface. func (d *DHCP6) TimeServerSpecs() []network.TimeServerSpecSpec { d.mu.Lock() defer d.mu.Unlock() return d.timeservers } func (d *DHCP6) parseReply(reply *dhcpv6.Message) (leaseTime time.Duration) { d.mu.Lock() defer d.mu.Unlock() if reply.Options.OneIANA() != nil && reply.Options.OneIANA().Options.OneAddress() != nil { addr, _ := netipx.FromStdIPNet(&net.IPNet{ IP: reply.Options.OneIANA().Options.OneAddress().IPv6Addr, Mask: net.CIDRMask(128, 128), }) d.addresses = []network.AddressSpecSpec{ { Address: addr, LinkName: d.linkName, Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigOperator, }, } leaseTime = reply.Options.OneIANA().Options.OneAddress().ValidLifetime } else { d.addresses = nil } if len(reply.Options.DNS()) > 0 { convertIP := func(ip net.IP) netip.Addr { result, _ := netipx.FromStdIP(ip) return result } d.resolvers = []network.ResolverSpecSpec{ { DNSServers: xslices.Map(reply.Options.DNS(), convertIP), ConfigLayer: network.ConfigOperator, }, } } else { d.resolvers = nil } if reply.Options.FQDN() != nil && len(reply.Options.FQDN().DomainName.Labels) > 0 && !d.skipHostnameRequest { d.hostname = []network.HostnameSpecSpec{ { Hostname: reply.Options.FQDN().DomainName.Labels[0], Domainname: strings.Join(reply.Options.FQDN().DomainName.Labels[1:], "."), ConfigLayer: network.ConfigOperator, }, } } else { d.hostname = nil } if len(reply.Options.NTPServers()) > 0 { convertIP := func(ip net.IP) string { result, _ := netipx.FromStdIP(ip) return result.String() } d.timeservers = []network.TimeServerSpecSpec{ { NTPServers: xslices.Map(reply.Options.NTPServers(), convertIP), ConfigLayer: network.ConfigOperator, }, } } else { d.timeservers = nil } return leaseTime } func (d *DHCP6) renew(ctx context.Context) (time.Duration, error) { cli, err := nclient6.New(d.linkName) if err != nil { return 0, err } defer cli.Close() //nolint:errcheck clientIdentifierModifiers, err := GetDHCPv6ClientIdentifier(ctx, d.state, d.logger, d.linkName, d.clientIdentifier) if err != nil { return 0, fmt.Errorf("error getting DHCPv6 client identifier: %w", err) } reply, err := cli.RapidSolicit(ctx, clientIdentifierModifiers...) if err != nil { return 0, err } d.logger.Debug("DHCP6 REPLY", zap.String("link", d.linkName), zap.String("dhcp", collapseSummary(reply.Summary()))) return d.parseReply(reply), nil } func (d *DHCP6) waitIPv6LinkReady(ctx context.Context, iface *net.Interface) error { conn, err := rtnetlink.Dial(nil) if err != nil { return err } defer conn.Close() //nolint:errcheck return retry.Constant(30*time.Second, retry.WithUnits(100*time.Millisecond)).RetryWithContext(ctx, func(ctx context.Context) error { ready, err := d.isIPv6LinkReady(iface, conn) if err != nil { return err } if !ready { return retry.ExpectedErrorf("IPv6 address is still tentative") } return nil }) } // isIPv6LinkReady returns true if the interface has a link-local address // which is not tentative. func (d *DHCP6) isIPv6LinkReady(iface *net.Interface, conn *rtnetlink.Conn) (bool, error) { addrs, err := conn.Address.List() if err != nil { return false, err } for _, addr := range addrs { if addr.Index != uint32(iface.Index) { continue } if addr.Family != unix.AF_INET6 { continue } if addr.Attributes.Address.IsLinkLocalUnicast() && (addr.Flags&unix.IFA_F_TENTATIVE == 0) { if addr.Flags&unix.IFA_F_DADFAILED != 0 { d.logger.Warn("DADFAILED for %v, continuing anyhow", zap.Stringer("address", addr.Attributes.Address), zap.String("link", d.linkName)) } return true, nil } } return false, nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/operator.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package operator implements network operators. package operator import ( "context" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // Operator describes common interface of the operators. type Operator interface { Run(ctx context.Context, notifyCh chan<- struct{}) Prefix() string AddressSpecs() []network.AddressSpecSpec RouteSpecs() []network.RouteSpecSpec LinkSpecs() []network.LinkSpecSpec HostnameSpecs() []network.HostnameSpecSpec ResolverSpecs() []network.ResolverSpecSpec TimeServerSpecs() []network.TimeServerSpecSpec } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip/equinix_metal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vip import ( "context" "encoding/json" "fmt" "path" "github.com/packethost/packngo" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EquinixMetalHandler implements assignment and release of Virtual IPs using API. type EquinixMetalHandler struct { client *packngo.Client logger *zap.Logger vip string projectID string deviceID string assignmentID string } // NewEquinixMetalHandler creates new EquinixMetalHandler. func NewEquinixMetalHandler(logger *zap.Logger, vip string, spec network.VIPEquinixMetalSpec) *EquinixMetalHandler { return &EquinixMetalHandler{ client: packngo.NewClientWithAuth("talos", spec.APIToken, nil), logger: logger, vip: vip, projectID: spec.ProjectID, deviceID: spec.DeviceID, } } // Acquire implements Handler interface. func (handler *EquinixMetalHandler) Acquire(ctx context.Context) error { ips, _, err := handler.client.ProjectIPs.List(handler.projectID, &packngo.ListOptions{}) if err != nil { return fmt.Errorf("error listing project IPs: %w", err) } // look up assignments for the VIP and unassign it for _, ip := range ips { if ip.Address != handler.vip { continue } for _, assignment := range ip.Assignments { assignmentID := path.Base(assignment.Href) if _, err = handler.client.DeviceIPs.Unassign(assignmentID); err != nil { return fmt.Errorf("error removing assignment %s: %w", assignment.String(), err) } handler.logger.Info("cleared previous Equinix Metal IP assignment", zap.String("assignment", assignmentID), zap.String("vip", handler.vip)) } } // assign the VIP to this device assignment, _, err := handler.client.DeviceIPs.Assign(handler.deviceID, &packngo.AddressStruct{ Address: handler.vip, }) if err != nil { return fmt.Errorf("error assigning %q to %q: %w", handler.vip, handler.deviceID, err) } handler.logger.Info("assigned Equinix Metal IP", zap.String("vip", handler.vip), zap.String("device_id", handler.deviceID), zap.String("assignment", assignment.ID)) handler.assignmentID = assignment.ID return nil } // Release implements Handler interface. func (handler *EquinixMetalHandler) Release(ctx context.Context) error { if handler.assignmentID == "" { return nil } _, err := handler.client.DeviceIPs.Unassign(handler.assignmentID) if err != nil { return fmt.Errorf("error removing assignment %s: %w", handler.assignmentID, err) } handler.logger.Info("unassigned Equinix Metal IP", zap.String("assignment", handler.assignmentID), zap.String("vip", handler.vip)) return nil } // EquinixMetalMetaDataEndpoint is the local endpoint for machine info like networking. const EquinixMetalMetaDataEndpoint = "https://metadata.platformequinix.com/metadata" // GetProjectAndDeviceIDs fills in parts of the spec based on the API token and instance metadata. func GetProjectAndDeviceIDs(ctx context.Context, spec *network.VIPEquinixMetalSpec) error { metadataConfig, err := download.Download(ctx, EquinixMetalMetaDataEndpoint) if err != nil { return fmt.Errorf("error downloading metadata: %w", err) } type Metadata struct { ID string `json:"id"` } var unmarshalledMetadataConfig Metadata if err = json.Unmarshal(metadataConfig, &unmarshalledMetadataConfig); err != nil { return fmt.Errorf("error unmarshaling metadata: %w", err) } spec.DeviceID = unmarshalledMetadataConfig.ID client := packngo.NewClientWithAuth("talos", spec.APIToken, nil) device, _, err := client.Devices.Get(spec.DeviceID, &packngo.GetOptions{ Includes: []string{"project"}, }) if err != nil { return fmt.Errorf("error getting device: %w", err) } spec.ProjectID = device.Project.ID return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip/equinix_metal_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vip_test import ( "os" "testing" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator/vip" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestEquinixMetalHandler(t *testing.T) { // WARNING: this test requires interaction with Equinix Metal API with real device IDs and API token // it is skipped by default unless following variables are set: // TALOS_EM_API_TOKEN // TALOS_EM_PROJECT_ID // TALOS_EM_DEVICE_ID_1 // TALOS_EM_DEVICE_ID_2 // TALOS_EM_VIP settings := map[string]string{} for _, variable := range []string{ "TALOS_EM_API_TOKEN", "TALOS_EM_PROJECT_ID", "TALOS_EM_DEVICE_ID_1", "TALOS_EM_DEVICE_ID_2", "TALOS_EM_VIP", } { var ok bool settings[variable], ok = os.LookupEnv(variable) if !ok { t.Skip("skipping the test as the environment variable is not set", variable) } } logger := zaptest.NewLogger(t) handler1 := vip.NewEquinixMetalHandler(logger, settings["TALOS_EM_VIP"], network.VIPEquinixMetalSpec{ ProjectID: settings["TALOS_EM_PROJECT_ID"], DeviceID: settings["TALOS_EM_DEVICE_ID_1"], APIToken: settings["TALOS_EM_API_TOKEN"], }) handler2 := vip.NewEquinixMetalHandler(logger, settings["TALOS_EM_VIP"], network.VIPEquinixMetalSpec{ ProjectID: settings["TALOS_EM_PROJECT_ID"], DeviceID: settings["TALOS_EM_DEVICE_ID_2"], APIToken: settings["TALOS_EM_API_TOKEN"], }) // not graceful require.NoError(t, handler1.Acquire(t.Context())) require.NoError(t, handler2.Acquire(t.Context())) // graceful require.NoError(t, handler1.Acquire(t.Context())) require.NoError(t, handler1.Release(t.Context())) require.NoError(t, handler2.Acquire(t.Context())) require.NoError(t, handler2.Release(t.Context())) } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip/hcloud.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vip import ( "context" "fmt" "net" "net/netip" "strconv" "github.com/hetznercloud/hcloud-go/v2/hcloud" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/version" ) // HCloudHandler implements assignment and release of Virtual IPs using API. type HCloudHandler struct { client *hcloud.Client logger *zap.Logger vip string deviceID int64 floatingID int64 networkID int64 } // NewHCloudHandler creates new NewEHCloudHandler. func NewHCloudHandler(logger *zap.Logger, vip string, spec network.VIPHCloudSpec) *HCloudHandler { return &HCloudHandler{ client: hcloud.NewClient( hcloud.WithToken(spec.APIToken), hcloud.WithApplication(version.Name, version.Tag), ), logger: logger, vip: vip, deviceID: spec.DeviceID, networkID: spec.NetworkID, } } // Acquire implements Handler interface. func (handler *HCloudHandler) Acquire(ctx context.Context) error { if handler.networkID > 0 { var action *hcloud.Action alias := hcloud.ServerChangeAliasIPsOpts{ Network: &hcloud.Network{ID: handler.networkID}, AliasIPs: []net.IP{}, } // trying to find the old active server // and remove alias IP from it serverList, err := handler.client.Server.All(ctx) if err != nil { return fmt.Errorf("error getting server list: %w", err) } oldDeviceID := findServerByAlias(serverList, handler.networkID, handler.vip) if oldDeviceID != 0 { handler.logger.Info("trying to remove previous Hetzner Cloud IP alias", zap.String("vip", handler.vip), zap.Int64("device_id", oldDeviceID), zap.Int64("network_id", handler.networkID), ) action, _, err = handler.client.Server.ChangeAliasIPs(ctx, &hcloud.Server{ID: oldDeviceID}, hcloud.ServerChangeAliasIPsOpts{ Network: &hcloud.Network{ID: handler.networkID}, AliasIPs: []net.IP{}, }, ) if err != nil { return fmt.Errorf("error remove Hetzner Cloud IP alias %q from server %d: %w", handler.vip, oldDeviceID, err) } handler.logger.Info("cleared previous Hetzner Cloud IP alias", zap.String("vip", handler.vip), zap.Int64("device_id", oldDeviceID), zap.String("status", string(action.Status))) } netIP := net.ParseIP(handler.vip) alias.AliasIPs = []net.IP{netIP} action, _, err = handler.client.Server.ChangeAliasIPs(ctx, &hcloud.Server{ID: handler.deviceID}, alias) if err != nil { return fmt.Errorf("error change alias IPs %q to server %d: %w", handler.vip, handler.deviceID, err) } handler.logger.Info("assigned Hetzner Cloud IP alias", zap.String("vip", handler.vip), zap.Int64("device_id", handler.deviceID), zap.Int64("network_id", handler.networkID), zap.String("status", string(action.Status))) return nil } floatips, err := handler.client.FloatingIP.All(ctx) if err != nil { return fmt.Errorf("error getting floatingIPs list: %w", err) } for _, floatip := range floatips { if floatip.IP.String() == handler.vip { action, _, err := handler.client.FloatingIP.Assign(ctx, floatip, &hcloud.Server{ID: handler.deviceID}) if err != nil { return fmt.Errorf("error assigning %q on server %d: %w", handler.vip, handler.deviceID, err) } handler.logger.Info("assigned Hetzner Cloud floating IP", zap.String("vip", handler.vip), zap.Int64("device_id", handler.deviceID), zap.String("status", string(action.Status))) handler.floatingID = floatip.ID return nil } } return fmt.Errorf("error assigning %q to server %d in network %d: floating IP / alias IP is not found", handler.vip, handler.deviceID, handler.networkID) } // Release implements Handler interface. func (handler *HCloudHandler) Release(ctx context.Context) error { if handler.networkID > 0 { alias := hcloud.ServerChangeAliasIPsOpts{ Network: &hcloud.Network{ID: handler.networkID}, AliasIPs: []net.IP{}, } action, _, err := handler.client.Server.ChangeAliasIPs(ctx, &hcloud.Server{ID: handler.deviceID}, alias) if err != nil { return fmt.Errorf("error remove alias IPs %q on server %d: %w", handler.vip, handler.deviceID, err) } handler.logger.Info("unassigned Hetzner Cloud alias IP", zap.String("vip", handler.vip), zap.Int64("device_id", handler.deviceID), zap.Int64("network_id", handler.networkID), zap.String("status", string(action.Status))) return nil } if handler.floatingID > 0 { floatip, _, err := handler.client.FloatingIP.GetByID(ctx, handler.floatingID) if err != nil { return fmt.Errorf("error getting floatingIP info: %w", err) } if floatip.Server == nil || floatip.Server.ID != handler.deviceID { handler.logger.Info("unassigned Hetzner Cloud floating IP", zap.String("vip", handler.vip), zap.Int64("device_id", handler.deviceID)) } handler.floatingID = 0 } return nil } // HCloudMetaDataEndpoint is the local endpoint for machine info like networking. const HCloudMetaDataEndpoint = "http://169.254.169.254/hetzner/v1/metadata/instance-id" // GetNetworkAndDeviceIDs fills in parts of the spec based on the API token and instance metadata. func GetNetworkAndDeviceIDs(ctx context.Context, spec *network.VIPHCloudSpec, vip netip.Addr, logger *zap.Logger) error { metadataInstanceID, err := download.Download(ctx, HCloudMetaDataEndpoint) if err != nil { return fmt.Errorf("error downloading instance-id: %w", err) } spec.DeviceID, err = strconv.ParseInt(string(metadataInstanceID), 10, 64) if err != nil { return fmt.Errorf("error getting instance-id id: %w", err) } client := hcloud.NewClient( hcloud.WithToken(spec.APIToken), hcloud.WithApplication(version.Name, version.Tag), ) server, _, err := client.Server.GetByID(ctx, spec.DeviceID) if err != nil { return fmt.Errorf("error getting server info: %w", err) } if vip.IsPrivate() { // find private network for private vip (alias IP) assignedPrivateNetworks := server.PrivateNet if len(assignedPrivateNetworks) == 0 { return fmt.Errorf("trying to assign private vip (alias IP) %q, but no private network is assigned to the server", vip.String()) } for _, assignedPrivateNetwork := range assignedPrivateNetworks { privateNetwork, _, err := client.Network.GetByID(ctx, assignedPrivateNetwork.Network.ID) if err != nil { return fmt.Errorf("error getting network info: %w", err) } if privateNetwork.IPRange.Contains(vip.AsSlice()) { spec.NetworkID = assignedPrivateNetwork.Network.ID logger.Info("found private network for private vip (alias IP)", zap.String("vip", vip.String()), zap.Int64("network_id", spec.NetworkID)) break } } } else { // the public vip (floating IP) doesn't require a private network spec.NetworkID = 0 } return nil } func findServerByAlias(serverList []*hcloud.Server, networkID int64, vip string) (deviceID int64) { for _, server := range serverList { for _, network := range server.PrivateNet { if network.Network.ID == networkID { for _, alias := range network.Aliases { if alias.String() == vip { return server.ID } } } } } return 0 } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip/nop.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vip import ( "context" ) // NopHandler does nothing. type NopHandler struct{} // Acquire implements Handler interface. func (handler NopHandler) Acquire(ctx context.Context) error { return nil } // Release implements Handler interface. func (handler NopHandler) Release(ctx context.Context) error { return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip/vip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vip contains implementations of specific methods to acquire/release virtual IPs. package vip import "context" // Handler implements custom actions to manage virtual IP assignment. type Handler interface { Acquire(ctx context.Context) error Release(ctx context.Context) error } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator/vip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package operator import ( "context" "errors" "fmt" "net/netip" "os" "strings" "sync" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator/vip" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) const campaignRetryInterval = time.Second // VIP implements the Virtual (Shared) IP network operator. type VIP struct { logger *zap.Logger linkName string sharedIP netip.Addr gratuitousARP bool state state.State mu sync.Mutex leader bool handler vip.Handler } // NewVIP creates Virtual IP operator. func NewVIP(logger *zap.Logger, linkName string, spec network.VIPOperatorSpec, state state.State) *VIP { var handler vip.Handler switch { case spec.EquinixMetal != network.VIPEquinixMetalSpec{}: handler = vip.NewEquinixMetalHandler(logger, spec.IP.String(), spec.EquinixMetal) case spec.HCloud != network.VIPHCloudSpec{}: handler = vip.NewHCloudHandler(logger, spec.IP.String(), spec.HCloud) default: handler = vip.NopHandler{} } return &VIP{ logger: logger, linkName: linkName, sharedIP: spec.IP, gratuitousARP: spec.GratuitousARP, state: state, handler: handler, } } // Prefix returns unique operator prefix which gets prepended to each spec. func (vip *VIP) Prefix() string { return fmt.Sprintf("vip/%s", vip.linkName) } // Run the operator loop. func (vip *VIP) Run(ctx context.Context, notifyCh chan<- struct{}) { for { err := vip.campaign(ctx, notifyCh) if err != nil { if !errors.Is(err, context.Canceled) { vip.logger.Warn("campaign failure", zap.Error(err), zap.String("link", vip.linkName), zap.Stringer("ip", vip.sharedIP)) } select { case <-time.After(campaignRetryInterval): case <-ctx.Done(): return } } } } // AddressSpecs implements Operator interface. func (vip *VIP) AddressSpecs() []network.AddressSpecSpec { vip.mu.Lock() defer vip.mu.Unlock() if !vip.leader { return nil } family := nethelpers.FamilyInet6 gratuitousARP := false if vip.sharedIP.Is4() { family = nethelpers.FamilyInet4 gratuitousARP = vip.gratuitousARP } return []network.AddressSpecSpec{ { Address: netip.PrefixFrom(vip.sharedIP, vip.sharedIP.BitLen()), LinkName: vip.linkName, Family: family, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), AnnounceWithARP: gratuitousARP, ConfigLayer: network.ConfigOperator, }, } } // LinkSpecs implements Operator interface. func (vip *VIP) LinkSpecs() []network.LinkSpecSpec { return nil } // RouteSpecs implements Operator interface. func (vip *VIP) RouteSpecs() []network.RouteSpecSpec { return nil } // HostnameSpecs implements Operator interface. func (vip *VIP) HostnameSpecs() []network.HostnameSpecSpec { return nil } // ResolverSpecs implements Operator interface. func (vip *VIP) ResolverSpecs() []network.ResolverSpecSpec { return nil } // TimeServerSpecs implements Operator interface. func (vip *VIP) TimeServerSpecs() []network.TimeServerSpecSpec { return nil } func (vip *VIP) etcdElectionKey() string { return fmt.Sprintf("%s:vip:election:%s", constants.EtcdRootTalosKey, vip.sharedIP.String()) } func (vip *VIP) waitForPreconditions(ctx context.Context) error { // wait for the etcd to be up _, err := vip.state.WatchFor(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "etcd", resource.VersionUndefined), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } svc := r.(*v1alpha1.Service) //nolint:forcetypeassert return svc.TypedSpec().Running && svc.TypedSpec().Healthy, nil })) if err != nil { return fmt.Errorf("etcd health wait failure: %w", err) } // wait for the kubelet lifecycle to be up, and not being torn down _, err = vip.state.WatchFor(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.KubeletLifecycleType, k8s.KubeletLifecycleID, resource.VersionUndefined), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } if r.Metadata().Phase() == resource.PhaseTearingDown { return false, nil } return true, nil })) if err != nil { return fmt.Errorf("kubelet lifecycle wait failure: %w", err) } return nil } //nolint:gocyclo,cyclop func (vip *VIP) campaign(ctx context.Context, notifyCh chan<- struct{}) error { ctx, cancel := context.WithCancel(ctx) defer cancel() if err := vip.waitForPreconditions(ctx); err != nil { return fmt.Errorf("error waiting for preconditions: %w", err) } // put a finalizer on the kubelet lifecycle and remove once the campaign is done kubeletLifecycle := resource.NewMetadata(k8s.NamespaceName, k8s.KubeletLifecycleType, k8s.KubeletLifecycleID, resource.VersionUndefined) if err := vip.state.AddFinalizer(ctx, kubeletLifecycle, vip.Prefix()); err != nil { return fmt.Errorf("error adding kubelet lifecycle finalizer: %w", err) } defer func() { vip.state.RemoveFinalizer(ctx, kubeletLifecycle, vip.Prefix()) //nolint:errcheck }() hostname, err := os.Hostname() // TODO: this should be etcd nodename if err != nil { return errors.New("refusing to join election without a hostname") } ec, err := etcd.NewLocalClient(ctx) if err != nil { return fmt.Errorf("failed to create local etcd client: %w", err) } defer ec.Close() //nolint:errcheck sess, err := concurrency.NewSession(ec.Client) if err != nil { return fmt.Errorf("failed to create concurrency session: %w", err) } defer sess.Close() //nolint:errcheck election := concurrency.NewElection(sess, vip.etcdElectionKey()) node, err := election.Leader(ctx) if err != nil { if err != concurrency.ErrElectionNoLeader { return fmt.Errorf("failed getting current leader: %w", err) } } else if string(node.Kvs[0].Value) == hostname { vip.logger.Info("resigning from previous election") // we are still leader from the previous election, attempt to resign to force new election resumedElection := concurrency.ResumeElection(sess, vip.etcdElectionKey(), string(node.Kvs[0].Key), node.Kvs[0].CreateRevision) if err = resumedElection.Resign(ctx); err != nil { return fmt.Errorf("failed resigning from previous elections: %w", err) } } campaignErrCh := make(chan error) go func() { campaignErrCh <- election.Campaign(ctx, hostname) }() watchCh := make(chan state.Event) if err = vip.state.Watch(ctx, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, "etcd", resource.VersionUndefined), watchCh); err != nil { return fmt.Errorf("error setting up etcd watch: %w", err) } if err = vip.state.Watch(ctx, kubeletLifecycle, watchCh); err != nil { return fmt.Errorf("error setting up etcd watch: %w", err) } err = vip.state.WatchKind(ctx, resource.NewMetadata(k8s.NamespaceName, k8s.StaticPodStatusType, "", resource.VersionUndefined), watchCh) if err != nil { return fmt.Errorf("kube-apiserver health wait failure: %w", err) } // wait for the etcd election campaign to be complete // while waiting, also observe the kubelet lifecycle object (if the node is shutting down) and etcd status campaignLoop: for { select { case err = <-campaignErrCh: if err != nil { return fmt.Errorf("failed to conduct campaign: %w", err) } // node won the election campaign! break campaignLoop case <-sess.Done(): vip.logger.Info("etcd session closed") return nil case <-ctx.Done(): return nil case event := <-watchCh: // note: here we don't wait for kube-apiserver, as it might not be up on cluster bootstrap, but VIP should be still assigned // break the loop when etcd is stopped if event.Type == state.Destroyed && event.Resource.Metadata().ID() == "etcd" { return nil } // break the loop if the kubelet lifecycle is entering teardown phase if event.Resource != nil { if event.Resource.Metadata().Type() == kubeletLifecycle.Type() && event.Resource.Metadata().ID() == kubeletLifecycle.ID() && event.Resource.Metadata().Phase() == resource.PhaseTearingDown { return nil } } } } defer func() { // use a new context to resign, as `ctx` might be canceled resignCtx, resignCancel := context.WithTimeout(context.Background(), 10*time.Second) defer resignCancel() election.Resign(resignCtx) //nolint:errcheck }() if err = vip.markAsLeader(ctx, notifyCh, true); err != nil { return err } defer func() { if err = vip.markAsLeader(ctx, notifyCh, false); err != nil && !errors.Is(err, context.Canceled) { vip.logger.Info("failed disabling shared IP", zap.String("link", vip.linkName), zap.Stringer("ip", vip.sharedIP), zap.Error(err)) } vip.logger.Info("removing shared IP", zap.String("link", vip.linkName), zap.Stringer("ip", vip.sharedIP)) }() vip.logger.Info("enabled shared IP", zap.String("link", vip.linkName), zap.Stringer("ip", vip.sharedIP)) observe := election.Observe(ctx) observeLoop: for { select { case <-sess.Done(): vip.logger.Info("etcd session closed") break observeLoop case <-ctx.Done(): break observeLoop case resp, ok := <-observe: if !ok { break observeLoop } if string(resp.Kvs[0].Value) != hostname { vip.logger.Info("detected new leader", zap.ByteString("leader", resp.Kvs[0].Value)) break observeLoop } case event := <-watchCh: // break the loop when etcd is stopped or kube-apiserver is stopped if event.Type == state.Destroyed { if event.Resource.Metadata().ID() == "etcd" || strings.HasPrefix(event.Resource.Metadata().ID(), "kube-system/kube-apiserver-") { break observeLoop } } // break the loop if the kubelet lifecycle is entering teardown phase if event.Resource != nil { if event.Resource.Metadata().Type() == kubeletLifecycle.Type() && event.Resource.Metadata().ID() == kubeletLifecycle.ID() && event.Resource.Metadata().Phase() == resource.PhaseTearingDown { break observeLoop } } } } return nil } func (vip *VIP) markAsLeader(ctx context.Context, notifyCh chan<- struct{}, leader bool) error { var handlerErr error if leader { handlerErr = vip.handler.Acquire(ctx) if handlerErr != nil { // if failed to acquire, we are not a leader, we will resign from the election // so don't mark as leader, so that Talos doesn't announce IPs on the host leader = false } } else { handlerErr = vip.handler.Release(ctx) } func() { vip.mu.Lock() defer vip.mu.Unlock() vip.leader = leader }() select { case <-ctx.Done(): return ctx.Err() case notifyCh <- struct{}{}: return handlerErr } } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "encoding/hex" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // OperatorConfigController manages network.OperatorSpec based on machine configuration, kernel cmdline. type OperatorConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *OperatorConfigController) Name() string { return "network.OperatorConfigController" } // Inputs implements controller.Controller interface. func (ctrl *OperatorConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.DeviceConfigSpecType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, { Namespace: network.ConfigNamespaceName, Type: network.LinkSpecType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *OperatorConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.OperatorSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *OperatorConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } } linkStatuses, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing link statuses: %w", err) } linkNameResolver := network.NewLinkResolver(linkStatuses.All) var ( specs []network.OperatorSpecSpec specErrors *multierror.Error ) ignoredInterfaces := map[string]struct{}{} if ctrl.Cmdline != nil { var settings CmdlineNetworking settings, err = ParseCmdlineNetwork(ctrl.Cmdline, linkNameResolver) if err != nil { logger.Warn("ignored cmdline parse failure", zap.Error(err)) } for _, link := range settings.IgnoreInterfaces { ignoredInterfaces[link] = struct{}{} } for _, linkConfig := range settings.LinkConfigs { if !linkConfig.DHCP { continue } specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: linkNameResolver.Resolve(linkConfig.LinkName), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigCmdline, }) } } items, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.DeviceConfigSpecType, "", resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } devices := xslices.Map(items.Items, func(item resource.Resource) talosconfig.Device { return item.(*network.DeviceConfigSpec).TypedSpec().Device }) // operators from the config if len(devices) > 0 { for _, device := range devices { if device.Ignore() { ignoredInterfaces[linkNameResolver.Resolve(device.Interface())] = struct{}{} } if _, ignore := ignoredInterfaces[linkNameResolver.Resolve(device.Interface())]; ignore { continue } if device.DHCP() && device.DHCPOptions().IPv4() { routeMetric := device.DHCPOptions().RouteMetric() if routeMetric == 0 { routeMetric = network.DefaultRouteMetric } specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: linkNameResolver.Resolve(device.Interface()), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: routeMetric, }, ConfigLayer: network.ConfigMachineConfiguration, }) } if device.DHCP() && device.DHCPOptions().IPv6() { routeMetric := device.DHCPOptions().RouteMetric() if routeMetric == 0 { routeMetric = network.DefaultRouteMetric } clientIdentifier := network.ClientIdentifierSpec{} if duid := device.DHCPOptions().DUIDv6(); len(duid) > 0 { clientIdentifier = network.ClientIdentifierSpec{ ClientIdentifier: nethelpers.ClientIdentifierDUID, DUIDRawHex: duid, } } specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: linkNameResolver.Resolve(device.Interface()), RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: routeMetric, ClientIdentifier: clientIdentifier, }, ConfigLayer: network.ConfigMachineConfiguration, }) } for _, vlan := range device.Vlans() { if vlan.DHCP() && vlan.DHCPOptions().IPv4() { routeMetric := vlan.DHCPOptions().RouteMetric() if routeMetric == 0 { routeMetric = network.DefaultRouteMetric } specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: routeMetric, }, ConfigLayer: network.ConfigMachineConfiguration, }) } if vlan.DHCP() && vlan.DHCPOptions().IPv6() { routeMetric := vlan.DHCPOptions().RouteMetric() if routeMetric == 0 { routeMetric = network.DefaultRouteMetric } clientIdentifier := network.ClientIdentifierSpec{} if duid := vlan.DHCPOptions().DUIDv6(); len(duid) > 0 { clientIdentifier = network.ClientIdentifierSpec{ ClientIdentifier: nethelpers.ClientIdentifierDUID, DUIDRawHex: duid, } } specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: nethelpers.VLANLinkName(device.Interface(), vlan.ID()), RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: routeMetric, ClientIdentifier: clientIdentifier, }, ConfigLayer: network.ConfigMachineConfiguration, }) } } } } // operator configs from machine config (new-style) if cfg != nil { for _, dhcp4 := range cfg.Config().NetworkDHCPv4Configs() { specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: linkNameResolver.Resolve(dhcp4.Name()), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: dhcp4.RouteMetric().ValueOr(network.DefaultRouteMetric), SkipHostnameRequest: dhcp4.IgnoreHostname().ValueOrZero(), ClientIdentifier: network.ClientIdentifierSpec{ ClientIdentifier: dhcp4.ClientIdentifier(), DUIDRawHex: hex.EncodeToString(dhcp4.DUIDRaw().ValueOrZero()), }, }, ConfigLayer: network.ConfigMachineConfiguration, }) } for _, dhcp6 := range cfg.Config().NetworkDHCPv6Configs() { specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: linkNameResolver.Resolve(dhcp6.Name()), RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: dhcp6.RouteMetric().ValueOr(network.DefaultRouteMetric), SkipHostnameRequest: dhcp6.IgnoreHostname().ValueOrZero(), ClientIdentifier: network.ClientIdentifierSpec{ ClientIdentifier: dhcp6.ClientIdentifier(), DUIDRawHex: hex.EncodeToString(dhcp6.DUIDRaw().ValueOrZero()), }, }, ConfigLayer: network.ConfigMachineConfiguration, }) } } // run default DHCP operators if enabled shouldRunDefaultDHCPOperators := cfg == nil || cfg.Config().RunDefaultDHCPOperators() if shouldRunDefaultDHCPOperators { // build configuredInterfaces from linkSpecs in `network-config` namespace // any link which has any configuration derived from the machine configuration or platform configuration should be ignored configuredInterfaces := map[string]struct{}{} linkSpecs, err := safe.ReaderList[*network.LinkSpec](ctx, r, resource.NewMetadata(network.ConfigNamespaceName, network.LinkSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing link specs: %w", err) } for link := range linkSpecs.All() { linkSpec := link.TypedSpec() switch linkSpec.ConfigLayer { case network.ConfigDefault: // ignore default link specs case network.ConfigOperator: // specs produced by operators, ignore case network.ConfigCmdline, network.ConfigMachineConfiguration, network.ConfigPlatform: // interface is configured explicitly, don't run default dhcp4 configuredInterfaces[linkNameResolver.Resolve(linkSpec.Name)] = struct{}{} } } // operators from defaults for linkStatus := range linkStatuses.All() { if linkStatus.TypedSpec().Physical() { if _, configured := configuredInterfaces[linkStatus.Metadata().ID()]; !configured { if _, ignored := ignoredInterfaces[linkStatus.Metadata().ID()]; !ignored { // enable DHCPv4 operator on physical interfaces which don't have any explicit configuration and are not ignored specs = append(specs, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: linkStatus.Metadata().ID(), RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, ClientIdentifier: network.ClientIdentifierSpec{ ClientIdentifier: nethelpers.ClientIdentifierMAC, }, }, ConfigLayer: network.ConfigDefault, }) } } } } } if err = ctrl.apply(ctx, r, specs); err != nil { return fmt.Errorf("error applying operator specs: %w", err) } // list specs for cleanup if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.OperatorSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning up operator specs: %w", err) } // last, check if some specs failed to build; fail last so that other operator specs are applied successfully if err = specErrors.ErrorOrNil(); err != nil { return err } r.ResetRestartBackoff() } } //nolint:dupl func (ctrl *OperatorConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.OperatorSpecSpec) error { for _, spec := range specs { id := network.LayeredID(spec.ConfigLayer, network.OperatorID(spec)) if err := safe.WriterModify( ctx, r, network.NewOperatorSpec(network.ConfigNamespaceName, id), func(r *network.OperatorSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type OperatorConfigSuite struct { ctest.DefaultSuite } func (suite *OperatorConfigSuite) assertOperators(requiredIDs []string, check func(*network.OperatorSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *OperatorConfigSuite) assertNoOperators(unexpectedIDs []string) { for _, id := range unexpectedIDs { ctest.AssertNoResource[*network.OperatorSpec](suite, id, rtestutils.WithNamespace(network.ConfigNamespaceName)) } } func (suite *OperatorConfigSuite) TestDefaultDHCP() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.OperatorConfigController{ Cmdline: procfs.NewCmdline("talos.network.interface.ignore=eth2"), }, ), ) for _, link := range []string{"eth0", "eth1", "eth2"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true suite.Create(linkStatus) } suite.assertOperators( []string{ "default/dhcp4/eth0", "default/dhcp4/eth1", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) switch r.Metadata().ID() { case "default/dhcp4/eth0": asrt.Equal("eth0", r.TypedSpec().LinkName) case "default/dhcp4/eth1": asrt.Equal("eth1", r.TypedSpec().LinkName) } }, ) } func (suite *OperatorConfigSuite) TestNoDefaultDHCP() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.OperatorConfigController{})) for _, link := range []string{"eth0", "eth1", "eth2"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true suite.Create(linkStatus) } // operators start suite.assertOperators( []string{ "default/dhcp4/eth0", "default/dhcp4/eth1", "default/dhcp4/eth2", }, func(r *network.OperatorSpec, asrt *assert.Assertions) {}, ) // create config lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkMTU = 9001 ctr, err := container.New(lc1) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) // operators stop suite.assertNoOperators( []string{ "default/dhcp4/eth0", "default/dhcp4/eth1", "default/dhcp4/eth2", }, ) } func (suite *OperatorConfigSuite) TestDefaultDHCPCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.OperatorConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1::::: ip=eth3:dhcp"), }, ), ) for _, link := range []string{"eth0", "eth1", "eth2"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true suite.Create(linkStatus) } suite.assertOperators( []string{ "default/dhcp4/eth0", "default/dhcp4/eth2", "cmdline/dhcp4/eth3", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) switch r.Metadata().ID() { case "default/dhcp4/eth0": asrt.Equal("eth0", r.TypedSpec().LinkName) case "default/dhcp4/eth2": asrt.Equal("eth2", r.TypedSpec().LinkName) case "cmdline/dhcp4/eth3": asrt.Equal("eth3", r.TypedSpec().LinkName) } }, ) // remove link suite.Require().NoError( suite.State().Destroy( suite.Ctx(), resource.NewMetadata(network.NamespaceName, network.LinkStatusType, "eth2", resource.VersionUndefined), ), ) suite.assertNoOperators( []string{ "default/dhcp4/eth2", }, ) } func (suite *OperatorConfigSuite) TestMachineConfigurationDHCP4() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.OperatorConfigController{ Cmdline: procfs.NewCmdline("talos.network.interface.ignore=eth5"), }, ), ) // add LinkConfig controller to produce link specs based on machine configuration suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.LinkConfigController{ Cmdline: procfs.NewCmdline("talos.network.interface.ignore=eth5"), }, ), ) for _, link := range []string{"eth0", "eth1", "eth2"} { linkStatus := network.NewLinkStatus(network.NamespaceName, link) linkStatus.TypedSpec().Type = nethelpers.LinkEther linkStatus.TypedSpec().LinkState = true suite.Create(linkStatus) } u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", }, { DeviceInterface: "eth1", DeviceDHCP: new(true), }, { DeviceIgnore: new(true), DeviceInterface: "eth2", DeviceDHCP: new(true), }, { DeviceInterface: "eth3", DeviceDHCP: new(true), DeviceDHCPOptions: &v1alpha1.DHCPOptions{ DHCPIPv4: new(true), DHCPRouteMetric: 256, }, }, { DeviceInterface: "eth4", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 25, VlanDHCP: new(true), }, { VlanID: 26, }, { VlanID: 27, VlanDHCPOptions: &v1alpha1.DHCPOptions{ DHCPRouteMetric: 256, }, }, }, }, { DeviceInterface: "eth5", DeviceDHCP: new(true), }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) suite.assertOperators( []string{ "configuration/dhcp4/eth1", "configuration/dhcp4/eth3", "configuration/dhcp4/eth4.25", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/dhcp4/eth1": asrt.Equal("eth1", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/eth3": asrt.Equal("eth3", r.TypedSpec().LinkName) asrt.EqualValues(256, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/eth4.25": asrt.Equal("eth4.25", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/eth4.26": asrt.Equal("eth4.26", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/eth4.27": asrt.Equal("eth4.27", r.TypedSpec().LinkName) asrt.EqualValues(256, r.TypedSpec().DHCP4.RouteMetric) } }, ) suite.assertNoOperators( []string{ "configuration/dhcp4/eth0", "default/dhcp4/eth0", "configuration/dhcp4/eth2", "default/dhcp4/eth2", "configuration/dhcp4/eth4.26", }, ) } func (suite *OperatorConfigSuite) TestMachineConfigurationDHCP6() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.OperatorConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth1", DeviceDHCP: new(true), DeviceDHCPOptions: &v1alpha1.DHCPOptions{ DHCPIPv4: new(true), }, }, { DeviceInterface: "eth2", DeviceDHCP: new(true), DeviceDHCPOptions: &v1alpha1.DHCPOptions{ DHCPIPv6: new(true), }, }, { DeviceInterface: "eth3", DeviceDHCP: new(true), DeviceDHCPOptions: &v1alpha1.DHCPOptions{ DHCPIPv6: new(true), DHCPRouteMetric: 512, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) suite.assertOperators( []string{ "configuration/dhcp6/eth2", "configuration/dhcp6/eth3", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorDHCP6, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/dhcp6/eth2": asrt.Equal("eth2", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP6.RouteMetric) case "configuration/dhcp6/eth3": asrt.Equal("eth3", r.TypedSpec().LinkName) asrt.EqualValues(512, r.TypedSpec().DHCP6.RouteMetric) } }, ) suite.assertNoOperators( []string{ "configuration/dhcp6/eth1", }, ) } func (suite *OperatorConfigSuite) TestMachineConfigurationNewStyle() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.OperatorConfigController{})) dhcp1 := networkcfg.NewDHCPv4ConfigV1Alpha1("eth0") dhcp1.ConfigRouteMetric = 256 dhcp1.ConfigIgnoreHostname = new(true) dhcp2 := networkcfg.NewDHCPv6ConfigV1Alpha1("eth0") dhcp2.ConfigRouteMetric = 512 dhcp2.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) dhcp2.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01} dhcp3 := networkcfg.NewDHCPv4ConfigV1Alpha1("eth23") dhcp4 := networkcfg.NewDHCPv4ConfigV1Alpha1("eth4") ctr, err := container.New(dhcp1, dhcp2, dhcp3, dhcp4) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.assertOperators( []string{ "configuration/dhcp4/eth0", "configuration/dhcp4/eth23", "configuration/dhcp4/eth4", "configuration/dhcp6/eth0", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/dhcp4/eth0": asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.Equal("eth0", r.TypedSpec().LinkName) asrt.EqualValues(256, r.TypedSpec().DHCP4.RouteMetric) asrt.True(r.TypedSpec().DHCP4.SkipHostnameRequest) asrt.Equal(nethelpers.ClientIdentifierMAC, r.TypedSpec().DHCP4.ClientIdentifier.ClientIdentifier) case "configuration/dhcp4/eth23": asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.Equal("eth23", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) asrt.False(r.TypedSpec().DHCP4.SkipHostnameRequest) asrt.Equal(nethelpers.ClientIdentifierMAC, r.TypedSpec().DHCP4.ClientIdentifier.ClientIdentifier) case "configuration/dhcp4/eth2": asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.Equal("eth2", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) asrt.False(r.TypedSpec().DHCP4.SkipHostnameRequest) asrt.Equal(nethelpers.ClientIdentifierMAC, r.TypedSpec().DHCP4.ClientIdentifier.ClientIdentifier) case "configuration/dhcp6/eth0": asrt.Equal(network.OperatorDHCP6, r.TypedSpec().Operator) asrt.Equal("eth0", r.TypedSpec().LinkName) asrt.EqualValues(512, r.TypedSpec().DHCP6.RouteMetric) asrt.False(r.TypedSpec().DHCP6.SkipHostnameRequest) asrt.Equal(nethelpers.ClientIdentifierDUID, r.TypedSpec().DHCP6.ClientIdentifier.ClientIdentifier) asrt.NotEmpty(r.TypedSpec().DHCP6.ClientIdentifier.DUIDRawHex) } }, ) } func (suite *OperatorConfigSuite) TestMachineConfigurationWithAliases() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.OperatorConfigController{}, ), ) // add LinkConfig controller to produce link specs based on machine configuration suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.LinkConfigController{}, ), ) for _, link := range []struct { name string aliases []string }{ { name: "eth0", aliases: []string{"enx0123"}, }, { name: "eth1", aliases: []string{"enx0456"}, }, { name: "eth2", aliases: []string{"enxa"}, }, { name: "eth3", aliases: []string{"enxb"}, }, { name: "eth4", aliases: []string{"enxc"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases status.TypedSpec().Type = nethelpers.LinkEther status.TypedSpec().LinkState = true suite.Create(status) } u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "enx0123", }, { DeviceInterface: "enx0456", DeviceDHCP: new(true), }, { DeviceIgnore: new(true), DeviceInterface: "enxa", DeviceDHCP: new(true), }, { DeviceInterface: "enxb", DeviceDHCP: new(true), DeviceDHCPOptions: &v1alpha1.DHCPOptions{ DHCPIPv4: new(true), DHCPRouteMetric: 256, }, }, { DeviceInterface: "enxc", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 25, VlanDHCP: new(true), }, { VlanID: 26, }, { VlanID: 27, VlanDHCPOptions: &v1alpha1.DHCPOptions{ DHCPRouteMetric: 256, }, }, }, }, { DeviceInterface: "enxd", DeviceDHCP: new(true), }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) suite.assertOperators( []string{ "configuration/dhcp4/eth1", "configuration/dhcp4/eth3", "configuration/dhcp4/enxc.25", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorDHCP4, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/dhcp4/eth1": asrt.Equal("eth1", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/eth3": asrt.Equal("eth3", r.TypedSpec().LinkName) asrt.EqualValues(256, r.TypedSpec().DHCP4.RouteMetric) case "configuration/dhcp4/enxc.25": asrt.Equal("enxc.25", r.TypedSpec().LinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().DHCP4.RouteMetric) } }, ) suite.assertNoOperators( []string{ "configuration/dhcp4/eth0", "default/dhcp4/eth0", "configuration/dhcp4/eth2", "default/dhcp4/eth2", "configuration/dhcp4/eth4.26", }, ) } func TestOperatorConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &OperatorConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.DeviceConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewOperatorMergeController initializes a OperatorMergeController. // // OperatorMergeController merges network.OperatorSpec in network.ConfigNamespace and produces final network.OperatorSpec in network.Namespace. func NewOperatorMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.OperatorSpec]) map[resource.ID]*network.OperatorSpecSpec { // operator is allowed as long as it's not duplicate, for duplicate higher layer takes precedence operators := map[string]*network.OperatorSpecSpec{} for operator := range list.All() { id := network.OperatorID(*operator.TypedSpec()) existing, ok := operators[id] if ok && existing.ConfigLayer > operator.TypedSpec().ConfigLayer { // skip this operator, as existing one is higher layer continue } operators[id] = operator.TypedSpec() } return operators }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type OperatorMergeSuite struct { ctest.DefaultSuite } func (suite *OperatorMergeSuite) assertOperators(requiredIDs []string, check func(*network.OperatorSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *OperatorMergeSuite) assertNoOperator(id string) { ctest.AssertNoResource[*network.OperatorSpec](suite, id) } func (suite *OperatorMergeSuite) TestMerge() { dhcp1 := network.NewOperatorSpec(network.ConfigNamespaceName, "default/dhcp4/eth0") *dhcp1.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", ConfigLayer: network.ConfigDefault, } dhcp2 := network.NewOperatorSpec(network.ConfigNamespaceName, "configuration/dhcp4/eth0") *dhcp2.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, ConfigLayer: network.ConfigMachineConfiguration, } dhcp6 := network.NewOperatorSpec(network.ConfigNamespaceName, "configuration/dhcp6/eth0") *dhcp6.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: "eth0", RequireUp: true, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{dhcp1, dhcp2, dhcp6} { suite.Create(res) } suite.assertOperators( []string{ "dhcp4/eth0", "dhcp6/eth0", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "dhcp4/eth0": asrt.Equal(*dhcp2.TypedSpec(), *r.TypedSpec()) case "dhcp6/eth0": asrt.Equal(*dhcp6.TypedSpec(), *r.TypedSpec()) } }, ) suite.Destroy(dhcp6) suite.assertOperators( []string{ "dhcp4/eth0", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(*dhcp2.TypedSpec(), *r.TypedSpec()) }, ) suite.assertNoOperator("dhcp6/eth0") } func (suite *OperatorMergeSuite) TestMergeFlapping() { // simulate two conflicting operator definitions which are getting removed/added constantly dhcp := network.NewOperatorSpec(network.ConfigNamespaceName, "default/dhcp4/eth0") *dhcp.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", ConfigLayer: network.ConfigDefault, } override := network.NewOperatorSpec(network.ConfigNamespaceName, "configuration/dhcp4/eth0") *override.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, ConfigLayer: network.ConfigMachineConfiguration, } testMergeFlapping(&suite.DefaultSuite, []*network.OperatorSpec{dhcp, override}, "dhcp4/eth0", override) } func TestOperatorMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &OperatorMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(netctrl.NewOperatorMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "sync" "time" "github.com/cenkalti/backoff/v4" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // OperatorSpecController applies network.OperatorSpec to the actual interfaces. type OperatorSpecController struct { V1alpha1Platform v1alpha1runtime.Platform State state.State // Factory can be overridden for unit-testing. Factory OperatorFactory operators map[string]*operatorRunState } // Name implements controller.Controller interface. func (ctrl *OperatorSpecController) Name() string { return "network.OperatorSpecController" } // Inputs implements controller.Controller interface. func (ctrl *OperatorSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.OperatorSpecType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *OperatorSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: network.AddressSpecType, Kind: controller.OutputShared, }, { Type: network.LinkSpecType, Kind: controller.OutputShared, }, { Type: network.RouteSpecType, Kind: controller.OutputShared, }, { Type: network.HostnameSpecType, Kind: controller.OutputShared, }, { Type: network.ResolverSpecType, Kind: controller.OutputShared, }, { Type: network.TimeServerSpecType, Kind: controller.OutputShared, }, } } // operatorRunState describes a state of running operator. type operatorRunState struct { Operator operator.Operator Spec network.OperatorSpecSpec cancel context.CancelFunc wg sync.WaitGroup } func (state *operatorRunState) Start(ctx context.Context, notifyCh chan<- struct{}, logger *zap.Logger, id string) { state.wg.Add(1) ctx, state.cancel = context.WithCancel(ctx) go func() { defer state.wg.Done() state.runWithRestarts(ctx, notifyCh, logger, id) }() } func (state *operatorRunState) runWithRestarts(ctx context.Context, notifyCh chan<- struct{}, logger *zap.Logger, id string) { backoff := backoff.NewExponentialBackOff() // disable number of retries limit backoff.MaxElapsedTime = 0 for ctx.Err() == nil { if err := state.runWithPanicHandler(ctx, notifyCh, logger, id); err == nil { // operator finished without an error return } interval := backoff.NextBackOff() logger.Debug("restarting operator", zap.Duration("interval", interval), zap.String("operator", id)) select { case <-ctx.Done(): return case <-time.After(interval): } } } func (state *operatorRunState) runWithPanicHandler(ctx context.Context, notifyCh chan<- struct{}, logger *zap.Logger, id string) (err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("panic: %v", p) logger.Error("operator panicked", zap.Stack("stack"), zap.Error(err), zap.String("operator", id)) } }() state.Operator.Run(ctx, notifyCh) return nil } func (state *operatorRunState) Stop() { state.cancel() state.wg.Wait() } // Run implements controller.Controller interface. func (ctrl *OperatorSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { notifyCh := make(chan struct{}) ctrl.operators = make(map[string]*operatorRunState) defer func() { for _, operator := range ctrl.operators { operator.Stop() } }() if ctrl.Factory == nil { ctrl.Factory = ctrl.newOperator } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): if err := ctrl.reconcileOperators(ctx, r, logger, notifyCh); err != nil { return err } case <-notifyCh: if err := ctrl.reconcileOperatorOutputs(ctx, r); err != nil { return err } } r.ResetRestartBackoff() } } //nolint:gocyclo func (ctrl *OperatorSpecController) reconcileOperators(ctx context.Context, r controller.Runtime, logger *zap.Logger, notifyCh chan<- struct{}) error { // build link up statuses linkStatuses := make(map[string]bool) linkStatusList, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing link statuses: %w", err) } for linkStatus := range linkStatusList.All() { linkStatuses[linkStatus.Metadata().ID()] = linkStatus.TypedSpec().OperationalState == nethelpers.OperStateUnknown || linkStatus.TypedSpec().OperationalState == nethelpers.OperStateUp } // list operator specs operatorSpecs, err := safe.ReaderListAll[*network.OperatorSpec](ctx, r) if err != nil { return fmt.Errorf("error listing operator specs: %w", err) } // figure out which operators should run shouldRun := make(map[string]*network.OperatorSpecSpec) for operatorSpec := range operatorSpecs.All() { up, exists := linkStatuses[operatorSpec.TypedSpec().LinkName] // link doesn't exist, skip operator if !exists { continue } // link is down and operator requires link to be up, skip it if operatorSpec.TypedSpec().RequireUp && !up { continue } shouldRun[operatorSpec.Metadata().ID()] = operatorSpec.TypedSpec() } // stop running operators which shouldn't run for id := range ctrl.operators { if _, exists := shouldRun[id]; !exists { logger.Debug("stopping operator", zap.String("operator", id)) // stop operator ctrl.operators[id].Stop() delete(ctrl.operators, id) } else if !ctrl.operators[id].Spec.Equal(*shouldRun[id]) { logger.Debug("replacing operator", zap.String("operator", id)) // stop operator ctrl.operators[id].Stop() delete(ctrl.operators, id) } } // start operators which aren't running for id := range shouldRun { if _, exists := ctrl.operators[id]; !exists { ctrl.operators[id] = &operatorRunState{ Operator: ctrl.Factory(logger, shouldRun[id]), Spec: *shouldRun[id], } logger.Debug("starting operator", zap.String("operator", id)) ctrl.operators[id].Start(ctx, notifyCh, logger, id) } } // now reconcile outputs as the operators might have changed return ctrl.reconcileOperatorOutputs(ctx, r) } //nolint:gocyclo,cyclop func (ctrl *OperatorSpecController) reconcileOperatorOutputs(ctx context.Context, r controller.Runtime) error { r.StartTrackingOutputs() for _, op := range ctrl.operators { for _, addressSpec := range op.Operator.AddressSpecs() { if err := safe.WriterModify( ctx, r, network.NewAddressSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.AddressID(addressSpec.LinkName, addressSpec.Address)), ), func(r *network.AddressSpec) error { *r.TypedSpec() = addressSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } for _, routeSpec := range op.Operator.RouteSpecs() { if err := safe.WriterModify( ctx, r, network.NewRouteSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.RouteID(routeSpec.Table, routeSpec.Family, routeSpec.Destination, routeSpec.Gateway, routeSpec.Priority, routeSpec.OutLinkName), ), ), func(r *network.RouteSpec) error { *r.TypedSpec() = routeSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } for _, linkSpec := range op.Operator.LinkSpecs() { if err := safe.WriterModify( ctx, r, network.NewLinkSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.LinkID(linkSpec.Name)), ), func(r *network.LinkSpec) error { *r.TypedSpec() = linkSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } for _, hostnameSpec := range op.Operator.HostnameSpecs() { if err := safe.WriterModify( ctx, r, network.NewHostnameSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.HostnameID), ), func(r *network.HostnameSpec) error { *r.TypedSpec() = hostnameSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } for _, resolverSpec := range op.Operator.ResolverSpecs() { if err := safe.WriterModify( ctx, r, network.NewResolverSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.ResolverID), ), func(r *network.ResolverSpec) error { *r.TypedSpec() = resolverSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } for _, timeserverSpec := range op.Operator.TimeServerSpecs() { if err := safe.WriterModify( ctx, r, network.NewTimeServerSpec( network.ConfigNamespaceName, fmt.Sprintf("%s/%s", op.Operator.Prefix(), network.TimeServerID), ), func(r *network.TimeServerSpec) error { *r.TypedSpec() = timeserverSpec return nil }, ); err != nil { return fmt.Errorf("error applying spec: %w", err) } } } // clean up not touched specs if err := r.CleanupOutputs(ctx, xslices.Map([]resource.Type{ network.AddressSpecType, network.LinkSpecType, network.RouteSpecType, network.HostnameSpecType, network.ResolverSpecType, network.TimeServerSpecType, }, func(t resource.Type) resource.Kind { return resource.NewMetadata(network.ConfigNamespaceName, t, "", resource.VersionUndefined) })..., ); err != nil { return fmt.Errorf("error during outputs cleanup: %w", err) } return nil } // OperatorFactory creates operator based on the spec. type OperatorFactory func(*zap.Logger, *network.OperatorSpecSpec) operator.Operator func (ctrl *OperatorSpecController) newOperator(logger *zap.Logger, spec *network.OperatorSpecSpec) operator.Operator { switch spec.Operator { case network.OperatorDHCP4: logger = logger.With(zap.String("operator", "dhcp4")) return operator.NewDHCP4(logger, spec.LinkName, spec.DHCP4, ctrl.V1alpha1Platform, ctrl.State) case network.OperatorDHCP6: logger = logger.With(zap.String("operator", "dhcp6")) return operator.NewDHCP6(logger, spec.LinkName, spec.DHCP6, ctrl.State) case network.OperatorVIP: logger = logger.With(zap.String("operator", "vip")) return operator.NewVIP(logger, spec.LinkName, spec.VIP, ctrl.State) default: panic(fmt.Sprintf("unexpected operator %s", spec.Operator)) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "context" "fmt" "net/netip" "slices" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type OperatorSpecSuite struct { ctest.DefaultSuite } type mockOperator struct { spec network.OperatorSpecSpec notifyCh chan<- struct{} panicked bool mu sync.Mutex addresses []network.AddressSpecSpec links []network.LinkSpecSpec routes []network.RouteSpecSpec hostname []network.HostnameSpecSpec resolvers []network.ResolverSpecSpec timeservers []network.TimeServerSpecSpec } var ( runningOperators = map[string]*mockOperator{} runningOperatorsMu sync.Mutex ) func (mock *mockOperator) Prefix() string { return fmt.Sprintf("%s/%s", mock.spec.Operator, mock.spec.LinkName) } func (mock *mockOperator) Run(ctx context.Context, notifyCh chan<- struct{}) { mock.notifyCh = notifyCh { runningOperatorsMu.Lock() runningOperators[mock.Prefix()] = mock runningOperatorsMu.Unlock() } defer func() { runningOperatorsMu.Lock() delete(runningOperators, mock.Prefix()) runningOperatorsMu.Unlock() }() if mock.spec.Operator == network.OperatorDHCP6 { // DHCP6 operator panics on odd run if !mock.panicked { mock.panicked = true panic("oh no, IPv6!!!") } } <-ctx.Done() } func (mock *mockOperator) notify() { mock.notifyCh <- struct{}{} } func (mock *mockOperator) AddressSpecs() []network.AddressSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.addresses } func (mock *mockOperator) LinkSpecs() []network.LinkSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.links } func (mock *mockOperator) RouteSpecs() []network.RouteSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.routes } func (mock *mockOperator) HostnameSpecs() []network.HostnameSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.hostname } func (mock *mockOperator) ResolverSpecs() []network.ResolverSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.resolvers } func (mock *mockOperator) TimeServerSpecs() []network.TimeServerSpecSpec { mock.mu.Lock() defer mock.mu.Unlock() return mock.timeservers } func (suite *OperatorSpecSuite) newOperator(_ *zap.Logger, spec *network.OperatorSpecSpec) operator.Operator { return &mockOperator{ spec: *spec, } } func (suite *OperatorSpecSuite) assertRunning(runningIDs []string, assertFunc func(*mockOperator) error) error { runningOperatorsMu.Lock() defer runningOperatorsMu.Unlock() for _, id := range runningIDs { op, exists := runningOperators[id] if !exists { return retry.ExpectedErrorf("operator %q is not running", id) } if err := assertFunc(op); err != nil { return retry.ExpectedError(err) } } for id := range runningOperators { found := slices.Contains(runningIDs, id) if !found { return retry.ExpectedErrorf("operator %s should not be running", id) } } return nil } func (suite *OperatorSpecSuite) TestScheduling() { specDHCP := network.NewOperatorSpec(network.NamespaceName, "dhcp4/eth0") *specDHCP.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: 1024, }, } specVIP := network.NewOperatorSpec(network.NamespaceName, "vip/eth0") *specVIP.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorVIP, LinkName: "eth0", RequireUp: false, VIP: network.VIPOperatorSpec{ IP: netip.MustParseAddr("1.2.3.4"), }, } suite.Create(specDHCP) suite.Create(specVIP) // operators shouldn't be running yet, as link state is not known yet suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( nil, func(op *mockOperator) error { return nil }, ) }, ), ) linkState := network.NewLinkStatus(network.NamespaceName, "eth0") *linkState.TypedSpec() = network.LinkStatusSpec{ OperationalState: nethelpers.OperStateDown, } suite.Create(linkState) // vip operator should be scheduled now, as VIP operator doesn't require link to be up suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( []string{"vip/eth0"}, func(op *mockOperator) error { suite.Assert().Equal(netip.MustParseAddr("1.2.3.4"), op.spec.VIP.IP) return nil }, ) }, ), ) ctest.UpdateWithConflicts(suite, linkState, func(r *network.LinkStatus) error { r.TypedSpec().OperationalState = nethelpers.OperStateUp return nil }) // now all operators should be scheduled suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( []string{"dhcp4/eth0", "vip/eth0"}, func(op *mockOperator) error { switch op.spec.Operator { //nolint:exhaustive case network.OperatorDHCP4: suite.Assert().EqualValues(1024, op.spec.DHCP4.RouteMetric) case network.OperatorVIP: suite.Assert().Equal(netip.MustParseAddr("1.2.3.4"), op.spec.VIP.IP) default: panic("unreachable") } return nil }, ) }, ), ) // change the spec, operator should be rescheduled ctest.UpdateWithConflicts(suite, specVIP, func(r *network.OperatorSpec) error { r.TypedSpec().VIP.IP = netip.MustParseAddr("3.4.5.6") return nil }) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( []string{"dhcp4/eth0", "vip/eth0"}, func(op *mockOperator) error { switch op.spec.Operator { //nolint:exhaustive case network.OperatorDHCP4: suite.Assert().EqualValues(1024, op.spec.DHCP4.RouteMetric) case network.OperatorVIP: if op.spec.VIP.IP.Compare(netip.MustParseAddr("3.4.5.6")) != 0 { return retry.ExpectedErrorf("unexpected vip: %s", op.spec.VIP.IP) } default: panic("unreachable") } return nil }, ) }, ), ) // bring down the interface, operator should be stopped ctest.UpdateWithConflicts(suite, linkState, func(r *network.LinkStatus) error { r.TypedSpec().OperationalState = nethelpers.OperStateDown return nil }) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( []string{"vip/eth0"}, func(op *mockOperator) error { return nil }, ) }, ), ) } func (suite *OperatorSpecSuite) TestPanic() { specPanic := network.NewOperatorSpec(network.NamespaceName, "dhcp6/eth0") *specPanic.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: "eth0", RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 1024, }, } suite.Create(specPanic) linkState := network.NewLinkStatus(network.NamespaceName, "eth0") *linkState.TypedSpec() = network.LinkStatusSpec{ OperationalState: nethelpers.OperStateUp, } suite.Create(linkState) // DHCP6 operator should panic and then restart suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning([]string{"dhcp6/eth0"}, func(op *mockOperator) error { return nil }) }, ), ) // bring down the interface, operator should be stopped ctest.UpdateWithConflicts(suite, linkState, func(r *network.LinkStatus) error { r.TypedSpec().OperationalState = nethelpers.OperStateDown return nil }) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( nil, func(op *mockOperator) error { return nil }, ) }, ), ) } func (suite *OperatorSpecSuite) TestOperatorOutputs() { specDHCP := network.NewOperatorSpec(network.NamespaceName, "dhcp4/eth0") *specDHCP.TypedSpec() = network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: 1024, }, } suite.Create(specDHCP) linkState := network.NewLinkStatus(network.NamespaceName, "eth0") *linkState.TypedSpec() = network.LinkStatusSpec{ OperationalState: nethelpers.OperStateUp, } suite.Create(linkState) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRunning( []string{"dhcp4/eth0"}, func(op *mockOperator) error { return nil }, ) }, ), ) // pretend dhcp has some specs ready runningOperatorsMu.Lock() dhcpMock := runningOperators["dhcp4/eth0"] runningOperatorsMu.Unlock() dhcpMock.mu.Lock() dhcpMock.addresses = []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("10.5.0.2/24"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigOperator, }, } dhcpMock.links = []network.LinkSpecSpec{ { Name: "eth0", Up: true, ConfigLayer: network.ConfigOperator, }, } dhcpMock.hostname = []network.HostnameSpecSpec{ { Hostname: "foo", ConfigLayer: network.ConfigOperator, }, } dhcpMock.mu.Unlock() dhcpMock.notify() ctest.AssertResources(suite, []resource.ID{"dhcp4/eth0/eth0/10.5.0.2/24"}, func(*network.AddressSpec, *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResources(suite, []resource.ID{"dhcp4/eth0/eth0"}, func(*network.LinkSpec, *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResources(suite, []resource.ID{"dhcp4/eth0/hostname"}, func(*network.HostnameSpec, *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // update specs dhcpMock.mu.Lock() dhcpMock.addresses = []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("10.5.0.3/24"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigOperator, }, } dhcpMock.mu.Unlock() dhcpMock.notify() ctest.AssertResources(suite, []resource.ID{"dhcp4/eth0/eth0/10.5.0.3/24"}, func(*network.AddressSpec, *assert.Assertions) {}, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func TestOperatorSpecSuite(t *testing.T) { t.Parallel() operatorSuite := &OperatorSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, } operatorSuite.DefaultSuite.AfterSetup = func(suite *ctest.DefaultSuite) { runningOperators = map[string]*mockOperator{} suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.OperatorSpecController{ Factory: operatorSuite.newOperator, }, ), ) } suite.Run(t, operatorSuite) } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_vip_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/operator/vip" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // OperatorVIPConfigController manages network.OperatorSpec for virtual IPs based on machine configuration. type OperatorVIPConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *OperatorVIPConfigController) Name() string { return "network.OperatorVIPConfigController" } // Inputs implements controller.Controller interface. func (ctrl *OperatorVIPConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.DeviceConfigSpecType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.LinkStatusType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *OperatorVIPConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.OperatorSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *OperatorVIPConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } devices, err := safe.ReaderListAll[*network.DeviceConfigSpec](ctx, r) if err != nil { return fmt.Errorf("error listing device config specs: %w", err) } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } linkStatuses, err := safe.ReaderListAll[*network.LinkStatus](ctx, r) if err != nil { return fmt.Errorf("error listing link statuses: %w", err) } linkNameResolver := network.NewLinkResolver(linkStatuses.All) ignoredInterfaces := map[string]struct{}{} if ctrl.Cmdline != nil { var settings CmdlineNetworking settings, err = ParseCmdlineNetwork(ctrl.Cmdline, linkNameResolver) if err != nil { logger.Warn("ignored cmdline parse failure", zap.Error(err)) } for _, link := range settings.IgnoreInterfaces { ignoredInterfaces[link] = struct{}{} } } var ( specs []network.OperatorSpecSpec specErrors *multierror.Error ) // operators from the legacy config for dev := range devices.All() { device := dev.TypedSpec().Device if device.Ignore() { ignoredInterfaces[linkNameResolver.Resolve(device.Interface())] = struct{}{} } if _, ignore := ignoredInterfaces[linkNameResolver.Resolve(device.Interface())]; ignore { continue } if device.VIPConfig() != nil { if spec, specErr := ctrl.handleVIPLegacy(ctx, device.VIPConfig(), linkNameResolver.Resolve(device.Interface()), logger); specErr != nil { specErrors = multierror.Append(specErrors, specErr) } else { specs = append(specs, spec) } } for _, vlan := range device.Vlans() { if vlan.VIPConfig() != nil { linkName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) if spec, specErr := ctrl.handleVIPLegacy(ctx, vlan.VIPConfig(), linkName, logger); specErr != nil { specErrors = multierror.Append(specErrors, specErr) } else { specs = append(specs, spec) } } } } // new-style config operators if cfg != nil { for _, doc := range cfg.Config().NetworkVirtualIPConfigs() { if spec, specErr := ctrl.handleVIPConfigDoc(ctx, doc, linkNameResolver.Resolve(doc.Link()), logger); specErr != nil { specErrors = multierror.Append(specErrors, specErr) } else { specs = append(specs, spec) } } } r.StartTrackingOutputs() if err := ctrl.apply(ctx, r, specs); err != nil { return fmt.Errorf("error applying operator specs: %w", err) } // last, check if some specs failed to build; fail last so that other operator specs are applied successfully if err = specErrors.ErrorOrNil(); err != nil { return err } if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.OperatorSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning up operator specs: %w", err) } } } //nolint:dupl func (ctrl *OperatorVIPConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.OperatorSpecSpec) error { for _, spec := range specs { id := network.LayeredID(spec.ConfigLayer, network.OperatorID(spec)) if err := safe.WriterModify( ctx, r, network.NewOperatorSpec(network.ConfigNamespaceName, id), func(r *network.OperatorSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return err } } return nil } func (ctrl *OperatorVIPConfigController) handleVIPLegacy(ctx context.Context, vipConfig talosconfig.VIPConfig, deviceName string, logger *zap.Logger) (network.OperatorSpecSpec, error) { var sharedIP netip.Addr sharedIP, err := netip.ParseAddr(vipConfig.IP()) if err != nil { logger.Warn("ignoring vip parse failure", zap.Error(err), zap.String("link", deviceName)) return network.OperatorSpecSpec{}, err } spec := network.OperatorSpecSpec{ Operator: network.OperatorVIP, LinkName: deviceName, RequireUp: true, VIP: network.VIPOperatorSpec{ IP: sharedIP, GratuitousARP: true, }, ConfigLayer: network.ConfigMachineConfiguration, } switch { // Equinix Metal VIP case vipConfig.EquinixMetal() != nil: spec.VIP.GratuitousARP = false spec.VIP.EquinixMetal.APIToken = vipConfig.EquinixMetal().APIToken() if err = vip.GetProjectAndDeviceIDs(ctx, &spec.VIP.EquinixMetal); err != nil { return network.OperatorSpecSpec{}, err } // Hetzner Cloud VIP case vipConfig.HCloud() != nil: spec.VIP.GratuitousARP = false spec.VIP.HCloud.APIToken = vipConfig.HCloud().APIToken() if err = vip.GetNetworkAndDeviceIDs(ctx, &spec.VIP.HCloud, sharedIP, logger); err != nil { return network.OperatorSpecSpec{}, err } // Regular layer 2 VIP default: } return spec, nil } func (ctrl *OperatorVIPConfigController) handleVIPConfigDoc(ctx context.Context, cfg talosconfig.NetworkVirtualIPConfig, deviceName string, logger *zap.Logger) (network.OperatorSpecSpec, error) { spec := network.OperatorSpecSpec{ Operator: network.OperatorVIP, LinkName: deviceName, RequireUp: true, VIP: network.VIPOperatorSpec{ IP: cfg.VIP(), GratuitousARP: true, }, ConfigLayer: network.ConfigMachineConfiguration, } switch v := cfg.(type) { case talosconfig.NetworkHCloudVIPConfig: spec.VIP.GratuitousARP = false spec.VIP.HCloud.APIToken = v.HCloudAPIToken() if err := vip.GetNetworkAndDeviceIDs(ctx, &spec.VIP.HCloud, cfg.VIP(), logger); err != nil { return network.OperatorSpecSpec{}, err } default: // nothing to do } return spec, nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/operator_vip_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type OperatorVIPConfigSuite struct { ctest.DefaultSuite } func (suite *OperatorVIPConfigSuite) assertOperators( requiredIDs []string, check func(*network.OperatorSpec, *assert.Assertions), ) { ctest.AssertResources(suite, requiredIDs, check, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *OperatorVIPConfigSuite) TestMachineConfigurationLegacyVIP() { for _, link := range []struct { name string aliases []string }{ { name: "eth5", aliases: []string{"enxa"}, }, { name: "eth6", aliases: []string{"enxb"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases suite.Create(status) } u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth1", DeviceDHCP: new(true), DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "2.3.4.5", }, }, { DeviceInterface: "eth2", DeviceDHCP: new(true), DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "fd7a:115c:a1e0:ab12:4843:cd96:6277:2302", }, }, { DeviceInterface: "eth3", DeviceDHCP: new(true), DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 26, VlanVIP: &v1alpha1.DeviceVIPConfig{ SharedIP: "5.5.4.4", }, }, }, }, { DeviceInterface: "enxa", DeviceDHCP: new(true), DeviceVIPConfig: &v1alpha1.DeviceVIPConfig{ SharedIP: "2.3.4.5", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) suite.assertOperators( []string{ "configuration/vip/eth1/2.3.4.5", "configuration/vip/eth2/fd7a:115c:a1e0:ab12:4843:cd96:6277:2302", "configuration/vip/eth3.26/5.5.4.4", "configuration/vip/eth5/2.3.4.5", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorVIP, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/vip/eth1/2.3.4.5": asrt.Equal("eth1", r.TypedSpec().LinkName) asrt.EqualValues(netip.MustParseAddr("2.3.4.5"), r.TypedSpec().VIP.IP) case "configuration/vip/eth5/2.3.4.5": asrt.Equal("eth5", r.TypedSpec().LinkName) asrt.EqualValues(netip.MustParseAddr("2.3.4.5"), r.TypedSpec().VIP.IP) case "configuration/vip/eth2/fd7a:115c:a1e0:ab12:4843:cd96:6277:2302": asrt.Equal("eth2", r.TypedSpec().LinkName) asrt.EqualValues( netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:cd96:6277:2302"), r.TypedSpec().VIP.IP, ) case "configuration/vip/eth3.26/5.5.4.4": asrt.Equal("eth3.26", r.TypedSpec().LinkName) asrt.EqualValues(netip.MustParseAddr("5.5.4.4"), r.TypedSpec().VIP.IP) } }, ) } func (suite *OperatorVIPConfigSuite) TestMachineConfigurationVIP() { for _, link := range []struct { name string aliases []string }{ { name: "eth5", aliases: []string{"enxa"}, }, { name: "eth6", aliases: []string{"enxb"}, }, } { status := network.NewLinkStatus(network.NamespaceName, link.name) status.TypedSpec().AltNames = link.aliases suite.Create(status) } vip1 := networkcfg.NewLayer2VIPConfigV1Alpha1("2.3.4.5") vip1.LinkName = "eth33" vip2 := networkcfg.NewLayer2VIPConfigV1Alpha1("fd7a:115c:a1e0:ab12:4843:cd96:6277:2302") vip2.LinkName = "enxa" ctr, err := container.New(vip1, vip2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) suite.assertOperators( []string{ "configuration/vip/eth33/2.3.4.5", "configuration/vip/eth5/fd7a:115c:a1e0:ab12:4843:cd96:6277:2302", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { asrt.Equal(network.OperatorVIP, r.TypedSpec().Operator) asrt.True(r.TypedSpec().RequireUp) switch r.Metadata().ID() { case "configuration/vip/eth33/2.3.4.5": asrt.Equal("eth33", r.TypedSpec().LinkName) asrt.EqualValues(netip.MustParseAddr("2.3.4.5"), r.TypedSpec().VIP.IP) case "configuration/vip/eth5/fd7a:115c:a1e0:ab12:4843:cd96:6277:2302": asrt.Equal("eth5", r.TypedSpec().LinkName) asrt.EqualValues( netip.MustParseAddr("fd7a:115c:a1e0:ab12:4843:cd96:6277:2302"), r.TypedSpec().VIP.IP, ) } }, ) } func TestOperatorVIPConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &OperatorVIPConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&netctrl.DeviceConfigController{})) s.Require().NoError(s.Runtime().RegisterController(&netctrl.OperatorVIPConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "sync" "time" "github.com/cenkalti/backoff/v4" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // PlatformConfigController runs the platform config acquire code and publishes the result as a resource. type PlatformConfigController struct { V1alpha1Platform v1alpha1runtime.Platform PlatformState state.State } // Name implements controller.Controller interface. func (ctrl *PlatformConfigController) Name() string { return "network.PlatformConfigController" } // Inputs implements controller.Controller interface. func (ctrl *PlatformConfigController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *PlatformConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.PlatformConfigType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *PlatformConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if ctrl.V1alpha1Platform == nil { // no platform, no work to be done return nil } platformCtx, platformCtxCancel := context.WithCancel(ctx) defer platformCtxCancel() platformCh := make(chan *v1alpha1runtime.PlatformNetworkConfig, 1) var platformWg sync.WaitGroup platformWg.Go(func() { ctrl.runWithRestarts(platformCtx, logger, func() error { return ctrl.V1alpha1Platform.NetworkConfiguration(platformCtx, ctrl.PlatformState, platformCh) }) }) defer platformWg.Wait() r.QueueReconcile() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case networkConfig := <-platformCh: if networkConfig == nil { continue } if err := safe.WriterModify(ctx, r, network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID), func(out *network.PlatformConfig) error { *out.TypedSpec() = *networkConfig return nil }, ); err != nil { return fmt.Errorf("error modifying active network config: %w", err) } } r.ResetRestartBackoff() } } func (ctrl *PlatformConfigController) runWithRestarts(ctx context.Context, logger *zap.Logger, f func() error) { backoff := backoff.NewExponentialBackOff() // disable number of retries limit backoff.MaxElapsedTime = 0 for ctx.Err() == nil { var err error if err = ctrl.runWithPanicHandler(logger, f); err == nil { // operator finished without an error return } // skip restarting if context is already done select { case <-ctx.Done(): return default: } interval := backoff.NextBackOff() logger.Error("restarting platform network config", zap.Duration("interval", interval), zap.Error(err)) select { case <-ctx.Done(): return case <-time.After(interval): } } } func (ctrl *PlatformConfigController) runWithPanicHandler(logger *zap.Logger, f func() error) (err error) { defer func() { if p := recover(); p != nil { err = fmt.Errorf("panic: %v", p) logger.Error("platform panicked", zap.Stack("stack"), zap.Error(err)) } }() err = f() return err } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_apply.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Virtual link name for external IPs. const externalLink = "external" // PlatformConfigApplyController applies active (or cached) platform network config to the network stack. type PlatformConfigApplyController struct { V1alpha1Platform v1alpha1runtime.Platform } // Name implements controller.Controller interface. func (ctrl *PlatformConfigApplyController) Name() string { return "network.PlatformConfigApplyController" } // Inputs implements controller.Controller interface. func (ctrl *PlatformConfigApplyController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.PlatformConfigType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *PlatformConfigApplyController) Outputs() []controller.Output { return []controller.Output{ { Type: network.AddressSpecType, Kind: controller.OutputShared, }, { Type: network.LinkSpecType, Kind: controller.OutputShared, }, { Type: network.RouteSpecType, Kind: controller.OutputShared, }, { Type: network.HostnameSpecType, Kind: controller.OutputShared, }, { Type: network.ResolverSpecType, Kind: controller.OutputShared, }, { Type: network.TimeServerSpecType, Kind: controller.OutputShared, }, { Type: network.AddressStatusType, Kind: controller.OutputShared, }, { Type: network.OperatorSpecType, Kind: controller.OutputShared, }, { Type: network.ProbeSpecType, Kind: controller.OutputShared, }, { Type: runtimeres.PlatformMetadataType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *PlatformConfigApplyController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } platformConfigs, err := safe.ReaderListAll[*network.PlatformConfig](ctx, r) if err != nil { return fmt.Errorf("error listing platform configs: %w", err) } var platformConfig *network.PlatformConfig // we always prefer "active" to "cached" for cfg := range platformConfigs.All() { switch cfg.Metadata().ID() { case network.PlatformConfigActiveID: platformConfig = cfg case network.PlatformConfigCachedID: if platformConfig == nil { platformConfig = cfg } } } // if we don't have any config yet, populate a minimal one // to ensure that platform name is populated in the resource if platformConfig == nil { platformConfig = network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Metadata = &runtimeres.PlatformMetadataSpec{ Platform: ctrl.V1alpha1Platform.Name(), } } if err := ctrl.apply(ctx, r, platformConfig); err != nil { return err } r.ResetRestartBackoff() } } //nolint:dupl,gocyclo func (ctrl *PlatformConfigApplyController) apply(ctx context.Context, r controller.Runtime, platformConfig *network.PlatformConfig) error { networkConfig := platformConfig.TypedSpec() metadataLength := 0 if networkConfig.Metadata != nil { metadataLength = 1 } // handle all network specs in a loop as all specs can be handled in a similar way for _, specType := range []struct { length int getter func(i int) any idBuilder func(spec any) (resource.ID, error) resourceBuilder func(id string) resource.Resource resourceModifier func(newSpec any) func(r resource.Resource) error }{ // AddressSpec { length: len(networkConfig.Addresses), getter: func(i int) any { return networkConfig.Addresses[i] }, idBuilder: func(spec any) (resource.ID, error) { addressSpec := spec.(network.AddressSpecSpec) //nolint:forcetypeassert return network.LayeredID(network.ConfigPlatform, network.AddressID(addressSpec.LinkName, addressSpec.Address)), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewAddressSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.AddressSpec).TypedSpec() *spec = newSpec.(network.AddressSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // LinkSpec { length: len(networkConfig.Links), getter: func(i int) any { return networkConfig.Links[i] }, idBuilder: func(spec any) (resource.ID, error) { linkSpec := spec.(network.LinkSpecSpec) //nolint:forcetypeassert return network.LayeredID(network.ConfigPlatform, network.LinkID(linkSpec.Name)), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewLinkSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.LinkSpec).TypedSpec() *spec = newSpec.(network.LinkSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform if spec.Kind == network.LinkKindBond { networkadapter.BondMasterSpec(&spec.BondMaster).FillDefaults() } return nil } }, }, // RouteSpec { length: len(networkConfig.Routes), getter: func(i int) any { return networkConfig.Routes[i] }, idBuilder: func(spec any) (resource.ID, error) { routeSpec := spec.(network.RouteSpecSpec) //nolint:forcetypeassert return network.LayeredID( network.ConfigPlatform, network.RouteID(routeSpec.Table, routeSpec.Family, routeSpec.Destination, routeSpec.Gateway, routeSpec.Priority, routeSpec.OutLinkName), ), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewRouteSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.RouteSpec).TypedSpec() *spec = newSpec.(network.RouteSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // HostnameSpec { length: len(networkConfig.Hostnames), getter: func(i int) any { return networkConfig.Hostnames[i] }, idBuilder: func(spec any) (resource.ID, error) { return network.LayeredID(network.ConfigPlatform, network.HostnameID), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewHostnameSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.HostnameSpec).TypedSpec() *spec = newSpec.(network.HostnameSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // ResolverSpec { length: len(networkConfig.Resolvers), getter: func(i int) any { return networkConfig.Resolvers[i] }, idBuilder: func(spec any) (resource.ID, error) { return network.LayeredID(network.ConfigPlatform, network.ResolverID), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewResolverSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.ResolverSpec).TypedSpec() *spec = newSpec.(network.ResolverSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // TimeServerSpec { length: len(networkConfig.TimeServers), getter: func(i int) any { return networkConfig.TimeServers[i] }, idBuilder: func(spec any) (resource.ID, error) { return network.LayeredID(network.ConfigPlatform, network.TimeServerID), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewTimeServerSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.TimeServerSpec).TypedSpec() *spec = newSpec.(network.TimeServerSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // OperatorSpec { length: len(networkConfig.Operators), getter: func(i int) any { return networkConfig.Operators[i] }, idBuilder: func(spec any) (resource.ID, error) { operatorSpec := spec.(network.OperatorSpecSpec) //nolint:forcetypeassert return network.LayeredID(network.ConfigPlatform, network.OperatorID(operatorSpec)), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewOperatorSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.OperatorSpec).TypedSpec() *spec = newSpec.(network.OperatorSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // ExternalIPs { length: len(networkConfig.ExternalIPs), getter: func(i int) any { return networkConfig.ExternalIPs[i] }, idBuilder: func(spec any) (resource.ID, error) { ipAddr := spec.(netip.Addr) //nolint:forcetypeassert ipPrefix := netip.PrefixFrom(ipAddr, ipAddr.BitLen()) return network.AddressID(externalLink, ipPrefix), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewAddressStatus(network.NamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { ipAddr := newSpec.(netip.Addr) //nolint:forcetypeassert ipPrefix := netip.PrefixFrom(ipAddr, ipAddr.BitLen()) status := r.(*network.AddressStatus).TypedSpec() status.Address = ipPrefix status.LinkName = externalLink if ipAddr.Is4() { status.Family = nethelpers.FamilyInet4 } else { status.Family = nethelpers.FamilyInet6 } status.Scope = nethelpers.ScopeGlobal return nil } }, }, // ProbeSpec { length: len(networkConfig.Probes), getter: func(i int) any { return networkConfig.Probes[i] }, idBuilder: func(spec any) (resource.ID, error) { probeSpec := spec.(network.ProbeSpecSpec) //nolint:forcetypeassert id, err := probeSpec.ID() if err != nil { return "", err } return network.LayeredID(network.ConfigPlatform, id), nil }, resourceBuilder: func(id string) resource.Resource { return network.NewProbeSpec(network.ConfigNamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { spec := r.(*network.ProbeSpec).TypedSpec() *spec = newSpec.(network.ProbeSpecSpec) //nolint:forcetypeassert spec.ConfigLayer = network.ConfigPlatform return nil } }, }, // Platform metadata { length: metadataLength, getter: func(i int) any { return networkConfig.Metadata }, idBuilder: func(spec any) (resource.ID, error) { return runtimeres.PlatformMetadataID, nil }, resourceBuilder: func(id string) resource.Resource { return runtimeres.NewPlatformMetadataSpec(runtimeres.NamespaceName, id) }, resourceModifier: func(newSpec any) func(r resource.Resource) error { return func(r resource.Resource) error { metadata := newSpec.(*runtimeres.PlatformMetadataSpec) //nolint:forcetypeassert *r.(*runtimeres.PlatformMetadata).TypedSpec() = *metadata return nil } }, }, } { touchedIDs := make(map[resource.ID]struct{}, specType.length) resourceEmpty := specType.resourceBuilder("") resourceNamespace := resourceEmpty.Metadata().Namespace() resourceType := resourceEmpty.Metadata().Type() for i := range specType.length { spec := specType.getter(i) id, err := specType.idBuilder(spec) if err != nil { return fmt.Errorf("error building resource %s ID: %w", resourceType, err) } if err = r.Modify(ctx, specType.resourceBuilder(id), specType.resourceModifier(spec)); err != nil { return fmt.Errorf("error modifying resource %s: %w", resourceType, err) } touchedIDs[id] = struct{}{} } list, err := r.List(ctx, resource.NewMetadata(resourceNamespace, resourceType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { continue } if _, ok := touchedIDs[res.Metadata().ID()]; ok { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error deleting %s: %w", res, err) } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_apply_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type PlatformConfigApplySuite struct { ctest.DefaultSuite } func (suite *PlatformConfigApplySuite) TestHostname() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigCachedID) platformConfig.TypedSpec().Hostnames = []network.HostnameSpecSpec{ { Hostname: "talos-e2e-897b4e49-gcp-controlplane-jvcnl", Domainname: "c.talos-testbed.internal", ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResource(suite, "platform/hostname", func(hostname *network.HostnameSpec, asrt *assert.Assertions) { spec := hostname.TypedSpec() asrt.Equal("talos-e2e-897b4e49-gcp-controlplane-jvcnl", spec.Hostname) asrt.Equal("c.talos-testbed.internal", spec.Domainname) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestHostnameNoDomain() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Hostnames = []network.HostnameSpecSpec{ { Hostname: "talos-e2e-897b4e49-gcp-controlplane-jvcnl", ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResource(suite, "platform/hostname", func(hostname *network.HostnameSpec, asrt *assert.Assertions) { spec := hostname.TypedSpec() asrt.Equal("talos-e2e-897b4e49-gcp-controlplane-jvcnl", spec.Hostname) asrt.Equal("", spec.Domainname) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestAddresses() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Addresses = []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("192.168.1.24/24"), LinkName: "eth0", Family: nethelpers.FamilyInet4, ConfigLayer: network.ConfigPlatform, }, { Address: netip.MustParsePrefix("2001:fd::3/64"), LinkName: "eth0", Family: nethelpers.FamilyInet6, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/eth0/192.168.1.24/24", "platform/eth0/2001:fd::3/64", }, func(r *network.AddressSpec, asrt *assert.Assertions) { spec := r.TypedSpec() switch r.Metadata().ID() { case "platform/eth0/192.168.1.24/24": asrt.Equal(nethelpers.FamilyInet4, spec.Family) asrt.Equal("192.168.1.24/24", spec.Address.String()) case "platform/eth0/2001:fd::3/64": asrt.Equal(nethelpers.FamilyInet6, spec.Family) asrt.Equal("2001:fd::3/64", spec.Address.String()) } asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestLinks() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Links = []network.LinkSpecSpec{ { Name: "eth0", Up: true, ConfigLayer: network.ConfigPlatform, }, { Name: "eth1", Up: true, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/eth0", "platform/eth1", }, func(r *network.LinkSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.True(spec.Up) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestRoutes() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Routes = []network.RouteSpecSpec{ { Family: nethelpers.FamilyInet4, Gateway: netip.MustParseAddr("10.0.0.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Priority: 1024, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/inet4/10.0.0.1//1024", }, func(r *network.RouteSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal("10.0.0.1", spec.Gateway.String()) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestOperators() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Operators = []network.OperatorSpecSpec{ { ConfigLayer: network.ConfigPlatform, LinkName: "eth1", Operator: network.OperatorDHCP4, DHCP4: network.DHCP4OperatorSpec{}, }, { ConfigLayer: network.ConfigPlatform, LinkName: "eth2", Operator: network.OperatorDHCP4, DHCP4: network.DHCP4OperatorSpec{}, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/dhcp4/eth1", "platform/dhcp4/eth2", }, func(r *network.OperatorSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(network.OperatorDHCP4, spec.Operator) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestResolvers() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Resolvers = []network.ResolverSpecSpec{ { DNSServers: []netip.Addr{netip.MustParseAddr("1.1.1.1")}, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal("[1.1.1.1]", fmt.Sprintf("%s", spec.DNSServers)) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestTimeServers() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().TimeServers = []network.TimeServerSpecSpec{ { NTPServers: []string{"pool.ntp.org"}, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal("[pool.ntp.org]", fmt.Sprintf("%s", spec.NTPServers)) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *PlatformConfigApplySuite) TestProbes() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Probes = []network.ProbeSpecSpec{ { Interval: time.Second, TCP: network.TCPProbeSpec{ Endpoint: "example.com:80", Timeout: time.Second, }, ConfigLayer: network.ConfigPlatform, }, { Interval: time.Second, TCP: network.TCPProbeSpec{ Endpoint: "example.com:443", Timeout: time.Second, }, ConfigLayer: network.ConfigPlatform, }, } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "platform/tcp:example.com:80", "platform/tcp:example.com:443", }, func(r *network.ProbeSpec, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal(time.Second, spec.Interval) asrt.Equal(network.ConfigPlatform, spec.ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *PlatformConfigApplySuite) TestExternalIPs() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().ExternalIPs = []netip.Addr{ netip.MustParseAddr("10.3.4.5"), netip.MustParseAddr("2001:470:6d:30e:96f4:4219:5733:b860"), } suite.Create(platformConfig) ctest.AssertResources(suite, []string{ "external/10.3.4.5/32", "external/2001:470:6d:30e:96f4:4219:5733:b860/128", }, func(r *network.AddressStatus, asrt *assert.Assertions) { spec := r.TypedSpec() asrt.Equal("external", spec.LinkName) asrt.Equal(nethelpers.ScopeGlobal, spec.Scope) if r.Metadata().ID() == "external/10.3.4.5/32" { asrt.Equal(nethelpers.FamilyInet4, spec.Family) } else { asrt.Equal(nethelpers.FamilyInet6, spec.Family) } }) } func (suite *PlatformConfigApplySuite) TestMetadata() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Metadata = &runtimeres.PlatformMetadataSpec{ Platform: "mock", Zone: "mock-zone", } suite.Create(platformConfig) ctest.AssertResource(suite, runtimeres.PlatformMetadataID, func(r *runtimeres.PlatformMetadata, asrt *assert.Assertions) { asrt.Equal("mock", r.TypedSpec().Platform) asrt.Equal("mock-zone", r.TypedSpec().Zone) }) } func (suite *PlatformConfigApplySuite) TestNoPlatformConfig() { ctest.AssertResource(suite, runtimeres.PlatformMetadataID, func(r *runtimeres.PlatformMetadata, asrt *assert.Assertions) { asrt.Equal("metal", r.TypedSpec().Platform) }) } func TestPlatformConfigApplySuite(t *testing.T) { t.Parallel() suite.Run(t, &PlatformConfigApplySuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.PlatformConfigApplyController{ V1alpha1Platform: &metal.Metal{}, }, )) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_load.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "io/fs" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "go.yaml.in/yaml/v4" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" ) // PlatformConfigLoadController loads cached platform network config from STATE. type PlatformConfigLoadController struct { stateMachine blockautomaton.VolumeMounterAutomaton } // Name implements controller.Controller interface. func (ctrl *PlatformConfigLoadController) Name() string { return "network.PlatformConfigLoadController" } // Inputs implements controller.Controller interface. func (ctrl *PlatformConfigLoadController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *PlatformConfigLoadController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, { Type: network.PlatformConfigType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *PlatformConfigLoadController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if ctrl.stateMachine == nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.load(), blockautomaton.WithReadOnly(true), blockautomaton.WithDetached(true), ) } if err := ctrl.stateMachine.Run(ctx, r, logger, automaton.WithAfterFunc(func() error { ctrl.stateMachine = nil return nil }), ); err != nil { return fmt.Errorf("error running volume mounter machine: %w", err) } if ctrl.stateMachine == nil { // we read only once, so once read, we should stop return nil } r.ResetRestartBackoff() } } func (ctrl *PlatformConfigLoadController) load() func( ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus, ) error { return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { cachedNetworkConfig, err := ctrl.loadConfig(root, constants.PlatformNetworkConfigFilename) if err != nil { logger.Warn("ignored failure loading cached platform network config", zap.Error(err)) } else if cachedNetworkConfig != nil { logger.Debug("loaded cached platform network config") } if cachedNetworkConfig != nil { if err := safe.WriterModify(ctx, r, network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigCachedID), func(out *network.PlatformConfig) error { *out.TypedSpec() = *cachedNetworkConfig return nil }, ); err != nil { return fmt.Errorf("error modifying cached platform network config: %w", err) } } return nil }) } } func (ctrl *PlatformConfigLoadController) loadConfig(root xfs.Root, path string) (*network.PlatformConfigSpec, error) { marshaled, err := xfs.ReadFile(root, path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, err } var networkConfig network.PlatformConfigSpec if err = yaml.Unmarshal(marshaled, &networkConfig); err != nil { return nil, fmt.Errorf("error unmarshaling network config: %w", err) } return &networkConfig, nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_load_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net/netip" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type PlatformConfigLoadSuite struct { ctest.DefaultSuite } func (suite *PlatformConfigLoadSuite) TestLoadConfig() { statePath := suite.T().TempDir() mountID := (&netctrl.PlatformConfigLoadController{}).Name() + "-" + constants.StatePartitionLabel suite.Require().NoError( os.WriteFile( filepath.Join(statePath, constants.PlatformNetworkConfigFilename), []byte(sampleStoredConfig), 0o400, ), ) ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) suite.Destroy(volumeMountStatus) ctest.AssertResource(suite, network.PlatformConfigCachedID, func(cachedConfig *network.PlatformConfig, asrt *assert.Assertions) { asrt.Equal( []network.HostnameSpecSpec{ { Hostname: "talos-e2e-897b4e49-gcp-controlplane-jvcnl", }, }, cachedConfig.TypedSpec().Hostnames, ) asrt.Equal( []netip.Addr{ netip.MustParseAddr("10.3.4.5"), netip.MustParseAddr("2001:470:6d:30e:96f4:4219:5733:b860"), }, cachedConfig.TypedSpec().ExternalIPs, ) }) } func TestPlatformConfigLoadSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } suite.Run(t, &PlatformConfigLoadSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError( suite.Runtime().RegisterController(&netctrl.PlatformConfigLoadController{}), ) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_store.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "bytes" "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "go.yaml.in/yaml/v4" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" ) // PlatformConfigStoreController stores (caches) active platform network config in STATE. type PlatformConfigStoreController struct { stateMachine blockautomaton.VolumeMounterAutomaton configToStore, lastStoredConfig *network.PlatformConfig } // Name implements controller.Controller interface. func (ctrl *PlatformConfigStoreController) Name() string { return "network.PlatformConfigStoreController" } // Inputs implements controller.Controller interface. func (ctrl *PlatformConfigStoreController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.PlatformConfigType, ID: optional.Some(network.PlatformConfigActiveID), Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *PlatformConfigStoreController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *PlatformConfigStoreController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } activeConfig, err := safe.ReaderGetByID[*network.PlatformConfig](ctx, r, network.PlatformConfigActiveID) if err != nil { if state.IsNotFoundError(err) { // no active network config found, wait more continue } return fmt.Errorf("error getting active network config: %w", err) } // if we haven't stored any config yet, or the active config has changed if ctrl.lastStoredConfig == nil || !activeConfig.TypedSpec().Equal(ctrl.lastStoredConfig.TypedSpec()) { ctrl.configToStore = activeConfig } if ctrl.stateMachine == nil && ctrl.configToStore != nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.store(), blockautomaton.WithDetached(true), ) } if ctrl.stateMachine != nil { if err := ctrl.stateMachine.Run(ctx, r, logger, automaton.WithAfterFunc(func() error { ctrl.stateMachine = nil return nil }), ); err != nil { return fmt.Errorf("error running volume mounter machine: %w", err) } } r.ResetRestartBackoff() } } func (ctrl *PlatformConfigStoreController) store() func( ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus, ) error { return func(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { if err := ctrl.storeConfig(root, constants.PlatformNetworkConfigFilename, ctrl.configToStore); err != nil { return fmt.Errorf("error saving platform network config: %w", err) } // remember last stored config ctrl.lastStoredConfig, ctrl.configToStore = ctrl.configToStore, nil logger.Debug("stored active platform network config") return nil }) } } func (ctrl *PlatformConfigStoreController) storeConfig(root xfs.Root, path string, networkConfig *network.PlatformConfig) error { marshaled, err := yaml.Marshal(networkConfig.TypedSpec()) if err != nil { return fmt.Errorf("error marshaling network config: %w", err) } if _, err := xfs.Stat(root, path); err == nil { existing, err := xfs.ReadFile(root, path) if err == nil && bytes.Equal(marshaled, existing) { // existing contents are identical, skip writing to avoid no-op writes return nil } } return xfs.WriteFile(root, path, marshaled, 0o400) } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_store_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net/netip" "os" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type PlatformConfigStoreSuite struct { ctest.DefaultSuite } const sampleStoredConfig = "addresses: []\nlinks: []\nroutes: []\nhostnames:\n - hostname: talos-e2e-897b4e49-gcp-controlplane-jvcnl\n domainname: \"\"\n layer: default\nresolvers: []\ntimeServers: []\noperators: []\nexternalIPs:\n - 10.3.4.5\n - 2001:470:6d:30e:96f4:4219:5733:b860\n" //nolint:lll func (suite *PlatformConfigStoreSuite) TestStoreConfig() { platformConfig := network.NewPlatformConfig(network.NamespaceName, network.PlatformConfigActiveID) platformConfig.TypedSpec().Hostnames = []network.HostnameSpecSpec{ { Hostname: "talos-e2e-897b4e49-gcp-controlplane-jvcnl", }, } platformConfig.TypedSpec().ExternalIPs = []netip.Addr{ netip.MustParseAddr("10.3.4.5"), netip.MustParseAddr("2001:470:6d:30e:96f4:4219:5733:b860"), } suite.Create(platformConfig) statePath := suite.T().TempDir() mountID := (&netctrl.PlatformConfigStoreController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) contents, err := os.ReadFile(filepath.Join(statePath, constants.PlatformNetworkConfigFilename)) asrt.NoError(err) asrt.Equal(sampleStoredConfig, string(contents)) }, time.Second, 10*time.Millisecond) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) // do an update which should not trigger store operation platformConfig.Metadata().Labels().Set("foo", "bar") suite.Update(platformConfig) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) // now update configuration platformConfig.TypedSpec().Hostnames = nil suite.Update(platformConfig) ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) } func TestPlatformConfigStoreSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } suite.Run(t, &PlatformConfigStoreSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError( suite.Runtime().RegisterController(&netctrl.PlatformConfigStoreController{}), ) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/platform_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "context" "net/netip" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type PlatformConfigSuite struct { ctest.DefaultSuite } func (suite *PlatformConfigSuite) TestNoPlatform() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.PlatformConfigController{})) ctest.AssertNoResource[*network.PlatformConfig](suite, network.PlatformConfigActiveID) } func (suite *PlatformConfigSuite) TestPlatform() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.PlatformConfigController{ V1alpha1Platform: &platformMock{ hostname: []byte("talos-e2e-897b4e49-gcp-controlplane-jvcnl.c.talos-testbed.internal"), addresses: []netip.Prefix{ netip.MustParsePrefix("192.168.1.24/24"), netip.MustParsePrefix("2001:fd::3/64"), }, defaultRoutes: []netip.Addr{netip.MustParseAddr("10.0.0.1")}, linksUp: []string{"eth0", "eth1"}, dhcp4Links: []string{"eth1", "eth2"}, resolvers: []netip.Addr{netip.MustParseAddr("1.1.1.1")}, timeServers: []string{"pool.ntp.org"}, tcpProbes: []string{"example.com:80", "example.com:443"}, externalIPs: []netip.Addr{ netip.MustParseAddr("10.3.4.5"), netip.MustParseAddr("2001:470:6d:30e:96f4:4219:5733:b860"), }, metadata: &runtimeres.PlatformMetadataSpec{ Platform: "mock", Zone: "mock-zone", }, }, }, ), ) ctest.AssertResource(suite, network.PlatformConfigActiveID, func(cfg *network.PlatformConfig, asrt *assert.Assertions) { spec := cfg.TypedSpec() asrt.Equal( []string{"talos-e2e-897b4e49-gcp-controlplane-jvcnl.c.talos-testbed.internal"}, xslices.Map(spec.Hostnames, func(h network.HostnameSpecSpec) string { return h.FQDN() }), ) asrt.Equal( []string{"192.168.1.24/24", "2001:fd::3/64"}, xslices.Map(spec.Addresses, func(a network.AddressSpecSpec) string { return a.Address.String() }), ) asrt.Equal( []string{"10.0.0.1"}, xslices.Map(spec.Routes, func(r network.RouteSpecSpec) string { return r.Gateway.String() }), ) asrt.Equal( []string{"eth0", "eth1"}, xslices.Map(spec.Links, func(l network.LinkSpecSpec) string { return l.Name }), ) asrt.Equal( []string{"eth1", "eth2"}, xslices.Map(spec.Operators, func(l network.OperatorSpecSpec) string { return l.LinkName }), ) asrt.Equal( []string{"1.1.1.1"}, xslices.Map(spec.Resolvers, func(r network.ResolverSpecSpec) string { return strings.Join(xslices.Map(r.DNSServers, netip.Addr.String), ", ") }), ) asrt.Equal( []string{"pool.ntp.org"}, xslices.Map(spec.TimeServers, func(t network.TimeServerSpecSpec) string { return strings.Join(t.NTPServers, ", ") }), ) asrt.Equal( []string{"example.com:80", "example.com:443"}, xslices.Map(spec.Probes, func(p network.ProbeSpecSpec) string { return p.TCP.Endpoint }), ) asrt.Equal( []string{"10.3.4.5", "2001:470:6d:30e:96f4:4219:5733:b860"}, xslices.Map(spec.ExternalIPs, netip.Addr.String), ) asrt.Equal( "mock", spec.Metadata.Platform, ) asrt.Equal( "mock-zone", spec.Metadata.Zone, ) }) } func TestPlatformConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &PlatformConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } type platformMock struct { noData bool hostname []byte externalIPs []netip.Addr addresses []netip.Prefix defaultRoutes []netip.Addr linksUp []string resolvers []netip.Addr timeServers []string dhcp4Links []string tcpProbes []string metadata *runtimeres.PlatformMetadataSpec } func (mock *platformMock) Name() string { return "mock" } func (mock *platformMock) Configuration(context.Context, state.State) ([]byte, error) { return nil, nil } func (mock *platformMock) Metadata(context.Context, state.State) (runtimeres.PlatformMetadataSpec, error) { return runtimeres.PlatformMetadataSpec{Platform: mock.Name()}, nil } func (mock *platformMock) Mode() v1alpha1runtime.Mode { return v1alpha1runtime.ModeCloud } func (mock *platformMock) KernelArgs(string, quirks.Quirks) procfs.Parameters { return nil } //nolint:gocyclo func (mock *platformMock) NetworkConfiguration( ctx context.Context, st state.State, ch chan<- *v1alpha1runtime.PlatformNetworkConfig, ) error { if mock.noData { return nil } networkConfig := &v1alpha1runtime.PlatformNetworkConfig{ ExternalIPs: mock.externalIPs, } if mock.hostname != nil { hostnameSpec := network.HostnameSpecSpec{} if err := hostnameSpec.ParseFQDN(string(mock.hostname)); err != nil { return err } networkConfig.Hostnames = []network.HostnameSpecSpec{hostnameSpec} } for _, addr := range mock.addresses { family := nethelpers.FamilyInet4 if addr.Addr().Is6() { family = nethelpers.FamilyInet6 } networkConfig.Addresses = append( networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: addr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) } for _, gw := range mock.defaultRoutes { family := nethelpers.FamilyInet4 if gw.Is6() { family = nethelpers.FamilyInet6 } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: 1024, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } for _, link := range mock.linksUp { networkConfig.Links = append( networkConfig.Links, network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: link, Up: true, }, ) } if len(mock.resolvers) > 0 { networkConfig.Resolvers = append( networkConfig.Resolvers, network.ResolverSpecSpec{ ConfigLayer: network.ConfigPlatform, DNSServers: mock.resolvers, }, ) } if len(mock.timeServers) > 0 { networkConfig.TimeServers = append( networkConfig.TimeServers, network.TimeServerSpecSpec{ ConfigLayer: network.ConfigPlatform, NTPServers: mock.timeServers, }, ) } for _, link := range mock.dhcp4Links { networkConfig.Operators = append( networkConfig.Operators, network.OperatorSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: link, Operator: network.OperatorDHCP4, DHCP4: network.DHCP4OperatorSpec{}, }, ) } for _, endpoint := range mock.tcpProbes { networkConfig.Probes = append( networkConfig.Probes, network.ProbeSpecSpec{ Interval: time.Second, TCP: network.TCPProbeSpec{ Endpoint: endpoint, Timeout: time.Second, }, ConfigLayer: network.ConfigPlatform, }) } networkConfig.Metadata = mock.metadata for range 5 { // send the network config multiple times to test duplicate suppression select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/internal/probe" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ProbeController runs network probes configured with ProbeSpecs and outputs ProbeStatuses. type ProbeController struct { runners map[string]*probe.Runner } // Name implements controller.Controller interface. func (ctrl *ProbeController) Name() string { return "network.ProbeController" } // Inputs implements controller.Controller interface. func (ctrl *ProbeController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.ProbeSpecType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ProbeController) Outputs() []controller.Output { return []controller.Output{ { Type: network.ProbeStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *ProbeController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { notifyCh := make(chan probe.Notification) ctrl.runners = make(map[string]*probe.Runner) defer func() { for _, runner := range ctrl.runners { runner.Stop() } }() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): if err := ctrl.reconcileRunners(ctx, r, logger, notifyCh); err != nil { return err } case ev := <-notifyCh: if err := ctrl.reconcileOutputs(ctx, r, ev); err != nil { return err } } r.ResetRestartBackoff() } } //nolint:gocyclo func (ctrl *ProbeController) reconcileRunners(ctx context.Context, r controller.Runtime, logger *zap.Logger, notifyCh chan<- probe.Notification) error { specList, err := safe.ReaderListAll[*network.ProbeSpec](ctx, r) if err != nil { return fmt.Errorf("error listing probe specs: %w", err) } // figure out which operators should run shouldRun := make(map[string]network.ProbeSpecSpec) for probeSpec := range specList.All() { shouldRun[probeSpec.Metadata().ID()] = *probeSpec.TypedSpec() } // stop running probes which shouldn't run for id := range ctrl.runners { if _, exists := shouldRun[id]; !exists { logger.Debug("stopping probe", zap.String("probe", id)) ctrl.runners[id].Stop() delete(ctrl.runners, id) } else if !shouldRun[id].Equal(ctrl.runners[id].Spec) { logger.Debug("replacing probe", zap.String("probe", id)) ctrl.runners[id].Stop() delete(ctrl.runners, id) } } // start probes which aren't running for id := range shouldRun { if _, exists := ctrl.runners[id]; !exists { ctrl.runners[id] = &probe.Runner{ ID: id, Spec: shouldRun[id], } logger.Debug("starting probe", zap.String("probe", id)) ctrl.runners[id].Start(ctx, notifyCh, logger) } } // clean up statuses which should no longer exist statusList, err := safe.ReaderListAll[*network.ProbeStatus](ctx, r) if err != nil { return fmt.Errorf("error listing probe statuses: %w", err) } for res := range statusList.All() { if _, exists := shouldRun[res.Metadata().ID()]; exists { continue } if err = r.Destroy(ctx, res.Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying probe status: %w", err) } } return nil } func (ctrl *ProbeController) reconcileOutputs(ctx context.Context, r controller.Runtime, ev probe.Notification) error { if _, exists := ctrl.runners[ev.ID]; !exists { // probe was already removed, late notification, ignore it return nil } return safe.WriterModify(ctx, r, network.NewProbeStatus(network.NamespaceName, ev.ID), func(status *network.ProbeStatus) error { *status.TypedSpec() = ev.Status return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ProbeConfigController manages network.ProbeSpec based on ProbeConfig documents in machine configuration. type ProbeConfigController struct{} // Name implements controller.Controller interface. func (ctrl *ProbeConfigController) Name() string { return "network.ProbeConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ProbeConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ProbeConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.ProbeSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *ProbeConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } var specs []network.ProbeSpecSpec // parse machine configuration for probe config documents if cfg != nil { configSpecs := ctrl.parseMachineConfiguration(cfg) specs = append(specs, configSpecs...) } if err = ctrl.apply(ctx, r, specs); err != nil { return fmt.Errorf("error applying specs: %w", err) } if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.ProbeSpecType, "", resource.VersionUndefined), ); err != nil { return fmt.Errorf("error cleaning up outputs: %w", err) } r.ResetRestartBackoff() } } func (ctrl *ProbeConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.ProbeSpecSpec) error { for _, spec := range specs { id, err := spec.ID() if err != nil { return fmt.Errorf("error getting probe spec ID: %w", err) } if err := safe.WriterModify( ctx, r, network.NewProbeSpec(network.ConfigNamespaceName, network.LayeredID(spec.ConfigLayer, id)), func(r *network.ProbeSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return fmt.Errorf("error modifying probe spec: %w", err) } } return nil } func (ctrl *ProbeConfigController) parseMachineConfiguration(cfg *config.MachineConfig) []network.ProbeSpecSpec { probeConfigs := cfg.Config().NetworkProbeConfigs() specs := make([]network.ProbeSpecSpec, 0, len(probeConfigs)) for _, probeConfig := range probeConfigs { spec := network.ProbeSpecSpec{ Interval: probeConfig.Interval(), FailureThreshold: probeConfig.FailureThreshold(), ConfigLayer: network.ConfigMachineConfiguration, } switch probeConfig := probeConfig.(type) { case configconfig.NetworkTCPProbeConfig: spec.TCP = network.TCPProbeSpec{ Endpoint: probeConfig.Endpoint(), Timeout: probeConfig.Timeout(), } default: panic(fmt.Sprintf("unsupported probe config type: %T", probeConfig)) } specs = append(specs, spec) } return specs } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ProbeConfigSuite struct { ctest.DefaultSuite } func (suite *ProbeConfigSuite) TestNoConfig() { // With no config, no ProbeSpec resources should be created ctest.AssertNoResource[*network.ProbeSpec](suite, "tcp:proxy.example.com:3128", rtestutils.WithNamespace(network.NamespaceName)) } func (suite *ProbeConfigSuite) TestSingleProbe() { probeConfig := networkcfg.NewTCPProbeConfigV1Alpha1("proxy-check") probeConfig.ProbeInterval = time.Second probeConfig.ProbeFailureThreshold = 3 probeConfig.TCPEndpoint = "proxy.example.com:3128" probeConfig.TCPTimeout = 10 * time.Second ctr, err := container.New(probeConfig) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/tcp:proxy.example.com:3128", }, func(r *network.ProbeSpec, asrt *assert.Assertions) { asrt.Equal(time.Second, r.TypedSpec().Interval) asrt.Equal(3, r.TypedSpec().FailureThreshold) asrt.Equal("proxy.example.com:3128", r.TypedSpec().TCP.Endpoint) asrt.Equal(10*time.Second, r.TypedSpec().TCP.Timeout) asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // Update the probe config ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error { docs := r.Container().Documents() probeDoc := docs[0].(*networkcfg.TCPProbeConfigV1Alpha1) probeDoc.ProbeFailureThreshold = 5 return nil }) ctest.AssertResources( suite, []string{ "configuration/tcp:proxy.example.com:3128", }, func(r *network.ProbeSpec, asrt *assert.Assertions) { asrt.Equal(5, r.TypedSpec().FailureThreshold) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // Remove the config suite.Destroy(cfg) ctest.AssertNoResource[*network.ProbeSpec](suite, "configuration/tcp:proxy.example.com:3128", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *ProbeConfigSuite) TestMultipleProbes() { // Create first probe probeConfig1 := networkcfg.NewTCPProbeConfigV1Alpha1("proxy-check") probeConfig1.ProbeInterval = time.Second probeConfig1.ProbeFailureThreshold = 3 probeConfig1.TCPEndpoint = "proxy.example.com:3128" probeConfig1.TCPTimeout = 10 * time.Second // Create second probe probeConfig2 := networkcfg.NewTCPProbeConfigV1Alpha1("dns-check") probeConfig2.ProbeInterval = 5 * time.Second probeConfig2.ProbeFailureThreshold = 2 probeConfig2.TCPEndpoint = "8.8.8.8:53" probeConfig2.TCPTimeout = 5 * time.Second ctr, err := container.New(probeConfig1, probeConfig2) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) // Verify both probes are created ctest.AssertResources( suite, []string{ "configuration/tcp:proxy.example.com:3128", }, func(r *network.ProbeSpec, asrt *assert.Assertions) { asrt.Equal("proxy.example.com:3128", r.TypedSpec().TCP.Endpoint) asrt.Equal(3, r.TypedSpec().FailureThreshold) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResources( suite, []string{ "configuration/tcp:8.8.8.8:53", }, func(r *network.ProbeSpec, asrt *assert.Assertions) { asrt.Equal("8.8.8.8:53", r.TypedSpec().TCP.Endpoint) asrt.Equal(2, r.TypedSpec().FailureThreshold) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) suite.Destroy(cfg) // Verify both probes are removed ctest.AssertNoResource[*network.ProbeSpec](suite, "configuration/tcp:proxy.example.com:3128", rtestutils.WithNamespace(network.ConfigNamespaceName)) ctest.AssertNoResource[*network.ProbeSpec](suite, "configuration/tcp:8.8.8.8:53", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func TestProbeConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ProbeConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ProbeConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "cmp" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewProbeMergeController initializes a ProbeMergeController. // // ProbeMergeController merges network.ProbeSpec in network.ConfigNamespace and produces final network.ProbeSpec in network.Namespace. func NewProbeMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.ProbeSpec]) map[resource.ID]*network.ProbeSpecSpec { // sort by link name, configuration layer list.SortFunc(func(left, right *network.ProbeSpec) int { return cmp.Compare(left.TypedSpec().ConfigLayer, right.TypedSpec().ConfigLayer) }) // build final probe definition merging multiple layers probes := make(map[string]*network.ProbeSpecSpec, list.Len()) for probe := range list.All() { id, err := probe.TypedSpec().ID() if err != nil { logger.Warn("error getting probe ID", zap.Error(err)) continue } // no way to actually have multiple probes with the same ID in different layers, // so we can just merge them one by one probes[id] = probe.TypedSpec() } return probes }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ProbeMergeSuite struct { ctest.DefaultSuite } func (suite *ProbeMergeSuite) TestMerge() { p1 := network.NewProbeSpec(network.ConfigNamespaceName, "configuration/tcp:proxy.example.com:3128") *p1.TypedSpec() = network.ProbeSpecSpec{ Interval: time.Second, FailureThreshold: 3, TCP: network.TCPProbeSpec{ Endpoint: "proxy.example.com:3128", Timeout: 10 * time.Second, }, ConfigLayer: network.ConfigMachineConfiguration, } p2 := network.NewProbeSpec(network.ConfigNamespaceName, "platform/tcp:proxy.example.com:3128") *p2.TypedSpec() = network.ProbeSpecSpec{ Interval: 5 * time.Second, FailureThreshold: 5, TCP: network.TCPProbeSpec{ Endpoint: "proxy.example.com:3128", Timeout: 5 * time.Second, }, ConfigLayer: network.ConfigPlatform, } p3 := network.NewProbeSpec(network.ConfigNamespaceName, "configuration/tcp:google.com:80") *p3.TypedSpec() = network.ProbeSpecSpec{ Interval: 2 * time.Second, FailureThreshold: 4, TCP: network.TCPProbeSpec{ Endpoint: "google.com:80", Timeout: 3 * time.Second, }, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{p1, p2, p3} { suite.Create(res) } ctest.AssertResources(suite, []resource.ID{"tcp:proxy.example.com:3128", "tcp:google.com:80"}, func(p *network.ProbeSpec, asrt *assert.Assertions) { if p.Metadata().ID() == "tcp:proxy.example.com:3128" { asrt.Equal(time.Second, p.TypedSpec().Interval) asrt.Equal(3, p.TypedSpec().FailureThreshold) asrt.Equal("proxy.example.com:3128", p.TypedSpec().TCP.Endpoint) asrt.Equal(10*time.Second, p.TypedSpec().TCP.Timeout) } }, ) suite.Destroy(p3) ctest.AssertNoResource[*network.ProbeSpec](suite, "tcp:google.com:80") } func TestProbeMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &ProbeMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewProbeMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/probe_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" networkctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ProbeSuite struct { ctest.DefaultSuite } func (suite *ProbeSuite) TestReconcile() { googleProbeSpec := network.ProbeSpecSpec{ Interval: 100 * time.Millisecond, TCP: network.TCPProbeSpec{ Endpoint: "google.com:80", Timeout: 5 * time.Second, }, } googleProbeSpecID, err := googleProbeSpec.ID() suite.Require().NoError(err) probeGoogle := network.NewProbeSpec(network.NamespaceName, googleProbeSpecID) *probeGoogle.TypedSpec() = googleProbeSpec suite.Require().NoError(suite.State().Create(suite.Ctx(), probeGoogle)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{googleProbeSpecID}, func(r *network.ProbeStatus, assert *assert.Assertions) { assert.Equal(network.ProbeStatusSpec{ Success: true, }, *r.TypedSpec()) }) failingProbeSpec := network.ProbeSpecSpec{ Interval: 100 * time.Millisecond, FailureThreshold: 1, TCP: network.TCPProbeSpec{ Endpoint: "google.com:81", Timeout: time.Second, }, } failingProbeSpecID, err := failingProbeSpec.ID() suite.Require().NoError(err) probeFailing := network.NewProbeSpec(network.NamespaceName, failingProbeSpecID) *probeFailing.TypedSpec() = failingProbeSpec suite.Require().NoError(suite.State().Create(suite.Ctx(), probeFailing)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{failingProbeSpecID}, func(r *network.ProbeStatus, assert *assert.Assertions) { assert.False(r.TypedSpec().Success) }) probeFailing.TypedSpec().TCP.Endpoint = "google.com:443" suite.Require().NoError(suite.State().Update(suite.Ctx(), probeFailing)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{failingProbeSpecID}, func(r *network.ProbeStatus, assert *assert.Assertions) { assert.Equal(network.ProbeStatusSpec{ Success: true, }, *r.TypedSpec()) }) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), probeFailing.Metadata())) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), probeGoogle.Metadata())) rtestutils.AssertNoResource[*network.ProbeStatus](suite.Ctx(), suite.T(), suite.State(), failingProbeSpecID) rtestutils.AssertNoResource[*network.ProbeStatus](suite.Ctx(), suite.T(), suite.State(), googleProbeSpecID) } // TestProbeSuite runs the ProbeSuite. func TestProbeSuite(t *testing.T) { suite.Run(t, &ProbeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 20 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&networkctrl.ProbeController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ResolverConfigController manages network.ResolverSpec based on machine configuration, kernel cmdline. type ResolverConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *ResolverConfigController) Name() string { return "network.ResolverConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ResolverConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ResolverConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.ResolverSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ResolverConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } touchedIDs := make(map[resource.ID]struct{}) var cfgProvider talosconfig.Config cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { cfgProvider = cfg.Config() } var specs []network.ResolverSpecSpec hostnameStatus, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting hostname status: %w", err) } } var hostnameStatusSpec *network.HostnameStatusSpec if hostnameStatus != nil { hostnameStatusSpec = hostnameStatus.TypedSpec() } // defaults specs = append(specs, ctrl.getDefault(cfgProvider, hostnameStatusSpec)) // parse kernel cmdline for the default gateway cmdlineServers := ctrl.parseCmdline(logger) if cmdlineServers.DNSServers != nil { specs = append(specs, cmdlineServers) } // parse machine configuration for specs if cfgProvider != nil { if configServers, ok := ctrl.parseMachineConfiguration(cfgProvider); ok { specs = append(specs, configServers) } } var ids []string ids, err = ctrl.apply(ctx, r, specs) if err != nil { return fmt.Errorf("error applying specs: %w", err) } for _, id := range ids { touchedIDs[id] = struct{}{} } // list specs for cleanup list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.ResolverSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { // skip specs created by other controllers continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } //nolint:dupl func (ctrl *ResolverConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.ResolverSpecSpec) ([]resource.ID, error) { ids := make([]string, 0, len(specs)) for _, spec := range specs { id := network.LayeredID(spec.ConfigLayer, network.ResolverID) if err := safe.WriterModify( ctx, r, network.NewResolverSpec(network.ConfigNamespaceName, id), func(r *network.ResolverSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return ids, err } ids = append(ids, id) } return ids, nil } func (ctrl *ResolverConfigController) getDefault(cfg talosconfig.Config, hostnameStatus *network.HostnameStatusSpec) (spec network.ResolverSpecSpec) { spec.DNSServers = []netip.Addr{netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver)} spec.ConfigLayer = network.ConfigDefault if cfg == nil || cfg.NetworkResolverConfig() == nil || cfg.NetworkResolverConfig().DisableSearchDomain() || hostnameStatus == nil || hostnameStatus.Domainname == "" { return spec } spec.SearchDomains = []string{hostnameStatus.Domainname} return spec } func (ctrl *ResolverConfigController) parseCmdline(logger *zap.Logger) (spec network.ResolverSpecSpec) { if ctrl.Cmdline == nil { return spec } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, network.NewEmptyLinkResolver()) if err != nil { logger.Warn("ignoring error", zap.Error(err)) return spec } if len(settings.DNSAddresses) == 0 { return spec } spec.DNSServers = settings.DNSAddresses spec.ConfigLayer = network.ConfigCmdline return spec } func (ctrl *ResolverConfigController) parseMachineConfiguration(cfgProvider talosconfig.Config) (network.ResolverSpecSpec, bool) { var spec network.ResolverSpecSpec if cfgProvider.NetworkResolverConfig() == nil { return spec, false } resolvers := cfgProvider.NetworkResolverConfig().Resolvers() searchDomains := cfgProvider.NetworkResolverConfig().SearchDomains() if len(resolvers) == 0 && len(searchDomains) == 0 { return spec, false } spec.DNSServers = slices.Clone(resolvers) spec.SearchDomains = slices.Clone(searchDomains) spec.ConfigLayer = network.ConfigMachineConfiguration return spec, true } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ResolverConfigSuite struct { ctest.DefaultSuite } func (suite *ResolverConfigSuite) TestDefaults() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{})) ctest.AssertResources( suite, []string{ "default/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal( []netip.Addr{ netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver), }, r.TypedSpec().DNSServers, ) asrt.Empty(r.TypedSpec().SearchDomains) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *ResolverConfigSuite) TestWithHostnameStatus() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{})) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "irrelevant" hostnameStatus.TypedSpec().Domainname = "example.org" suite.Create(hostnameStatus) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{}, //nolint:staticcheck // legacy config }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResources( suite, []string{ "default/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal( []netip.Addr{ netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver), }, r.TypedSpec().DNSServers, ) asrt.Equal([]string{"example.org"}, r.TypedSpec().SearchDomains) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // make domain name empty hostnameStatus.TypedSpec().Domainname = "" suite.Update(hostnameStatus) ctest.AssertResources( suite, []string{ "default/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().SearchDomains) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // bring back domain name, but disable via machine config hostnameStatus.TypedSpec().Domainname = "example.org" suite.Update(hostnameStatus) cfg.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NetworkDisableSearchDomain = new(true) //nolint:staticcheck suite.Update(cfg) ctest.AssertResources( suite, []string{ "default/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().SearchDomains) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *ResolverConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.ResolverConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1:eth1::10.0.0.1:10.0.0.2:10.0.0.1"), }, ), ) ctest.AssertResources( suite, []string{ "cmdline/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal( []netip.Addr{ netip.MustParseAddr("10.0.0.1"), netip.MustParseAddr("10.0.0.2"), }, r.TypedSpec().DNSServers, ) asrt.Empty(r.TypedSpec().SearchDomains) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *ResolverConfigSuite) TestMachineConfigurationLegacy() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NameServers: []string{"2.2.2.2", "3.3.3.3"}, Searches: []string{"example.com", "example.org"}, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal( []netip.Addr{ netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3"), }, r.TypedSpec().DNSServers, ) asrt.Equal( []string{"example.com", "example.org"}, r.TypedSpec().SearchDomains, ) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error { r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.NameServers = nil //nolint:staticcheck r.Container().RawV1Alpha1().MachineConfig.MachineNetwork.Searches = nil //nolint:staticcheck return nil }) ctest.AssertNoResource[*network.ResolverSpec](suite, "configuration/resolvers", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *ResolverConfigSuite) TestMachineConfigurationNewStyle() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.ResolverConfigController{})) rc := networkcfg.NewResolverConfigV1Alpha1() rc.ResolverNameservers = []networkcfg.NameserverConfig{ { Address: networkcfg.Addr{Addr: netip.MustParseAddr("2.2.2.2")}, }, { Address: networkcfg.Addr{Addr: netip.MustParseAddr("3.3.3.3")}, }, } rc.ResolverSearchDomains = networkcfg.SearchDomainsConfig{ SearchDomains: []string{"example.com", "example.org"}, } ctr, err := container.New(rc) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal( []netip.Addr{ netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3"), }, r.TypedSpec().DNSServers, ) asrt.Equal( []string{"example.com", "example.org"}, r.TypedSpec().SearchDomains, ) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) suite.Destroy(cfg) ctest.AssertNoResource[*network.ResolverSpec](suite, "configuration/resolvers", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func TestResolverConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ResolverConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "cmp" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewResolverMergeController initializes a ResolverMergeController. // // ResolverMergeController merges network.ResolverSpec in network.ConfigNamespace and produces final network.ResolverSpec in network.Namespace. func NewResolverMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.ResolverSpec]) map[resource.ID]*network.ResolverSpecSpec { // sort by config layer list.SortFunc(func(l, r *network.ResolverSpec) int { return cmp.Compare(l.TypedSpec().ConfigLayer, r.TypedSpec().ConfigLayer) }) // simply merge by layers, overriding with the next configuration layer var final network.ResolverSpecSpec for res := range list.All() { spec := res.TypedSpec() final.SearchDomains = slices.Insert(final.SearchDomains, 0, spec.SearchDomains...) switch spec.ConfigLayer { //nolint:exhaustive case final.ConfigLayer: // simply append server lists on the same layer final.DNSServers = append(final.DNSServers, spec.DNSServers...) case network.ConfigMachineConfiguration: // machine configuration layer overrides any previous layers completely final.DNSServers = slices.Clone(spec.DNSServers) default: // otherwise, do a smart merge across IPv4/IPv6 mergeDNSServers(&final.DNSServers, spec.DNSServers) } final.ConfigLayer = spec.ConfigLayer } if final.DNSServers != nil { return map[resource.ID]*network.ResolverSpecSpec{ network.ResolverID: &final, } } return nil }, ) } func mergeDNSServers(dst *[]netip.Addr, src []netip.Addr) { if *dst == nil { *dst = slices.Clone(src) return } srcHasV4 := slices.IndexFunc(src, netip.Addr.Is4) != -1 srcHasV6 := slices.IndexFunc(src, netip.Addr.Is6) != -1 dstHasV4 := slices.IndexFunc(*dst, netip.Addr.Is4) != -1 dstHasV6 := slices.IndexFunc(*dst, netip.Addr.Is6) != -1 // if old set has IPv4, and new one doesn't, preserve IPv4 // and same vice versa for IPv6 switch { case dstHasV4 && !srcHasV4: *dst = slices.Concat(src, xslices.Filter(*dst, netip.Addr.Is4)) case dstHasV6 && !srcHasV6: *dst = slices.Concat(src, xslices.Filter(*dst, netip.Addr.Is6)) default: *dst = slices.Clone(src) } } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ResolverMergeSuite struct { ctest.DefaultSuite } func (suite *ResolverMergeSuite) assertResolvers(requiredIDs []string, check func(*network.ResolverSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *ResolverMergeSuite) TestMerge() { def := network.NewResolverSpec(network.ConfigNamespaceName, "default/resolvers") *def.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{ netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver), }, ConfigLayer: network.ConfigDefault, } dhcp1 := network.NewResolverSpec(network.ConfigNamespaceName, "dhcp/eth0") *dhcp1.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("1.1.2.0")}, ConfigLayer: network.ConfigOperator, } dhcp2 := network.NewResolverSpec(network.ConfigNamespaceName, "dhcp/eth1") *dhcp2.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("1.1.2.1")}, ConfigLayer: network.ConfigOperator, } static := network.NewResolverSpec(network.ConfigNamespaceName, "configuration/resolvers") *static.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("2.2.2.2")}, SearchDomains: []string{"example.com", "example.org", "example.net"}, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{def, dhcp1, dhcp2, static} { suite.Create(res) } suite.assertResolvers( []string{ "resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) asrt.Equal([]string{"example.com", "example.org", "example.net"}, r.TypedSpec().SearchDomains) }, ) suite.Destroy(static) suite.assertResolvers( []string{ "resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal([]netip.Addr{netip.MustParseAddr("1.1.2.0"), netip.MustParseAddr("1.1.2.1")}, r.TypedSpec().DNSServers) }, ) } func (suite *ResolverMergeSuite) TestMergeIPv46() { def := network.NewResolverSpec(network.ConfigNamespaceName, "default/resolvers") *def.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{ netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver), }, ConfigLayer: network.ConfigDefault, } platform := network.NewResolverSpec(network.ConfigNamespaceName, "platform/resolvers") *platform.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("1.1.2.0"), netip.MustParseAddr("fe80::1")}, ConfigLayer: network.ConfigPlatform, } dhcp := network.NewResolverSpec(network.ConfigNamespaceName, "dhcp/eth1") *dhcp.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("1.1.2.1")}, ConfigLayer: network.ConfigOperator, } for _, res := range []resource.Resource{def, platform, dhcp} { suite.Create(res) } suite.assertResolvers( []string{ "resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigOperator, r.TypedSpec().ConfigLayer) asrt.Equal(`["1.1.2.1" "fe80::1"]`, fmt.Sprintf("%q", r.TypedSpec().DNSServers)) }, ) } func (suite *ResolverMergeSuite) TestMergeIPv6OnlyConfig() { def := network.NewResolverSpec(network.ConfigNamespaceName, "default/resolvers") *def.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{ netip.MustParseAddr(constants.DefaultPrimaryResolver), netip.MustParseAddr(constants.DefaultSecondaryResolver), }, ConfigLayer: network.ConfigDefault, } cfg := network.NewResolverSpec(network.ConfigNamespaceName, "cfg/resolvers") *cfg.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("fe80::1")}, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{def, cfg} { suite.Create(res) } suite.assertResolvers( []string{ "resolvers", }, func(r *network.ResolverSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) asrt.Equal(`["fe80::1"]`, fmt.Sprintf("%q", r.TypedSpec().DNSServers)) }, ) } func TestResolverMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &ResolverMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewResolverMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ResolverSpecController applies network.ResolverSpec to the actual interfaces. type ResolverSpecController struct{} // Name implements controller.Controller interface. func (ctrl *ResolverSpecController) Name() string { return "network.ResolverSpecController" } // Inputs implements controller.Controller interface. func (ctrl *ResolverSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.ResolverSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *ResolverSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: network.ResolverStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ResolverSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source network configuration resources list, err := safe.ReaderListAll[*network.ResolverSpec](ctx, r) if err != nil { return fmt.Errorf("error listing source network addresses: %w", err) } // add finalizers for all live resources for res := range list.All() { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // loop over specs and sync to statuses for spec := range list.All() { switch spec.Metadata().Phase() { case resource.PhaseTearingDown: if err = r.Destroy(ctx, resource.NewMetadata(network.NamespaceName, network.ResolverStatusType, spec.Metadata().ID(), resource.VersionUndefined)); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying status: %w", err) } if err = r.RemoveFinalizer(ctx, spec.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: logger.Info( "setting resolvers", zap.Stringers("resolvers", spec.TypedSpec().DNSServers), zap.Strings("searchDomains", spec.TypedSpec().SearchDomains), ) if err = safe.WriterModify(ctx, r, network.NewResolverStatus(network.NamespaceName, spec.Metadata().ID()), func(r *network.ResolverStatus) error { r.TypedSpec().DNSServers = spec.TypedSpec().DNSServers r.TypedSpec().SearchDomains = spec.TypedSpec().SearchDomains return nil }); err != nil { return fmt.Errorf("error modifying status: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/resolver_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "context" "net/netip" "slices" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type ResolverSpecSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *ResolverSpecSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.Require().NoError(suite.runtime.RegisterController(&netctrl.ResolverSpecController{})) suite.startRuntime() } func (suite *ResolverSpecSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *ResolverSpecSuite) assertStatus(id string, servers ...netip.Addr) error { r, err := suite.state.Get( suite.ctx, resource.NewMetadata(network.NamespaceName, network.ResolverStatusType, id, resource.VersionUndefined), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } status := r.(*network.ResolverStatus) //nolint:forcetypeassert if !slices.Equal(status.TypedSpec().DNSServers, servers) { return retry.ExpectedErrorf("server list mismatch: %q != %q", status.TypedSpec().DNSServers, servers) } return nil } func (suite *ResolverSpecSuite) TestSpec() { spec := network.NewResolverSpec(network.NamespaceName, "resolvers") *spec.TypedSpec() = network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr(constants.DefaultPrimaryResolver)}, ConfigLayer: network.ConfigDefault, } for _, res := range []resource.Resource{spec} { suite.Require().NoError(suite.state.Create(suite.ctx, res), "%v", res.Spec()) } suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertStatus("resolvers", netip.MustParseAddr(constants.DefaultPrimaryResolver)) }, ), ) } func (suite *ResolverSpecSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestResolverSpecSuite(t *testing.T) { suite.Run(t, new(ResolverSpecSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/network/route_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" cfg "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // RouteConfigController manages network.RouteSpec based on machine configuration, kernel cmdline. type RouteConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *RouteConfigController) Name() string { return "network.RouteConfigController" } // Inputs implements controller.Controller interface. func (ctrl *RouteConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.DeviceConfigSpecType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *RouteConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.RouteSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *RouteConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() devices, err := safe.ReaderListAll[*network.DeviceConfigSpec](ctx, r) if err != nil { return fmt.Errorf("error getting config: %w", err) } ignoredInterfaces := map[string]struct{}{} for device := range devices.All() { if device.TypedSpec().Device.Ignore() { ignoredInterfaces[device.TypedSpec().Device.Interface()] = struct{}{} } } // parse kernel cmdline for the default gateway cmdlineRoutes := ctrl.parseCmdline(logger) for _, cmdlineRoute := range cmdlineRoutes { if _, ignored := ignoredInterfaces[cmdlineRoute.OutLinkName]; !ignored { if err = ctrl.apply(ctx, r, []network.RouteSpecSpec{cmdlineRoute}); err != nil { return fmt.Errorf("error applying cmdline route: %w", err) } } } // parse machine configuration for static routes (legacy) if devices.Len() > 0 { routes := ctrl.processDevicesConfiguration(logger, devices) if err = ctrl.apply(ctx, r, routes); err != nil { return fmt.Errorf("error applying machine configuration routes: %w", err) } } // parse machine configuration (modern) cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } } if cfg != nil { if err = ctrl.apply(ctx, r, ctrl.processMachineConfig(cfg.Config().NetworkCommonLinkConfigs(), cfg.Config().NetworkBlackholeRouteConfigs())); err != nil { return fmt.Errorf("error applying machine configuration routes: %w", err) } } if err = r.CleanupOutputs(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.RouteSpecType, "", resource.VersionUndefined)); err != nil { return fmt.Errorf("error cleaning outputs: %w", err) } } } func (ctrl *RouteConfigController) apply(ctx context.Context, r controller.Runtime, routes []network.RouteSpecSpec) error { for _, route := range routes { id := network.LayeredID(route.ConfigLayer, network.RouteID(route.Table, route.Family, route.Destination, route.Gateway, route.Priority, route.OutLinkName)) if err := safe.WriterModify( ctx, r, network.NewRouteSpec(network.ConfigNamespaceName, id), func(r *network.RouteSpec) error { *r.TypedSpec() = route return nil }, ); err != nil { return err } } return nil } func (ctrl *RouteConfigController) parseCmdline(logger *zap.Logger) (routes []network.RouteSpecSpec) { if ctrl.Cmdline == nil { return routes } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, network.NewEmptyLinkResolver()) if err != nil { logger.Info("ignoring error", zap.Error(err)) return routes } for idx, linkConfig := range settings.LinkConfigs { if value.IsZero(linkConfig.Gateway) { continue } // add a default gateway route defaultGatewayRoute := network.RouteSpecSpec{ Gateway: linkConfig.Gateway, Scope: nethelpers.ScopeGlobal, Table: nethelpers.TableMain, Priority: network.DefaultRouteMetric + uint32(idx), // set different priorities to avoid a conflict Protocol: nethelpers.ProtocolBoot, Type: nethelpers.TypeUnicast, OutLinkName: linkConfig.LinkName, ConfigLayer: network.ConfigCmdline, } if defaultGatewayRoute.Gateway.Is6() { defaultGatewayRoute.Family = nethelpers.FamilyInet6 } else { defaultGatewayRoute.Family = nethelpers.FamilyInet4 } defaultGatewayRoute.Normalize() routes = append(routes, defaultGatewayRoute) // for IPv4, if the gateway is not directly reachable on the link, add a link-scope route for the gateway if linkConfig.Gateway.Is4() && !linkConfig.Address.Contains(linkConfig.Gateway) { routes = append(routes, network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Destination: netip.PrefixFrom(linkConfig.Gateway, linkConfig.Gateway.BitLen()), Source: linkConfig.Address.Addr(), OutLinkName: linkConfig.LinkName, Table: nethelpers.TableMain, Priority: defaultGatewayRoute.Priority, Scope: nethelpers.ScopeLink, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolBoot, ConfigLayer: network.ConfigCmdline, }) } } return routes } //nolint:gocyclo,cyclop func (ctrl *RouteConfigController) processDevicesConfiguration(logger *zap.Logger, devices safe.List[*network.DeviceConfigSpec]) (routes []network.RouteSpecSpec) { convert := func(linkName string, in cfg.Route) (route network.RouteSpecSpec, err error) { if in.Network() != "" { route.Destination, err = netip.ParsePrefix(in.Network()) if err != nil { return route, fmt.Errorf("error parsing route network: %w", err) } } if in.Gateway() != "" { route.Gateway, err = netip.ParseAddr(in.Gateway()) if err != nil { return route, fmt.Errorf("error parsing route gateway: %w", err) } } if in.Source() != "" { route.Source, err = netip.ParseAddr(in.Source()) if err != nil { return route, fmt.Errorf("error parsing route source: %w", err) } } normalizedFamily := route.Normalize() route.Priority = in.Metric() if route.Priority == 0 { route.Priority = network.DefaultRouteMetric } route.MTU = in.MTU() switch { case !value.IsZero(route.Gateway) && route.Gateway.Is6(): route.Family = nethelpers.FamilyInet6 case !value.IsZero(route.Destination) && route.Destination.Addr().Is6(): route.Family = nethelpers.FamilyInet6 case normalizedFamily != 0: route.Family = normalizedFamily default: route.Family = nethelpers.FamilyInet4 } route.Table = nethelpers.TableMain route.Protocol = nethelpers.ProtocolStatic route.OutLinkName = linkName route.ConfigLayer = network.ConfigMachineConfiguration route.Type = nethelpers.TypeUnicast if route.Destination.Addr().IsMulticast() { route.Type = nethelpers.TypeMulticast } return route, nil } for item := range devices.All() { device := item.TypedSpec().Device if device.Ignore() { continue } for _, route := range device.Routes() { routeSpec, err := convert(device.Interface(), route) if err != nil { logger.Sugar().Infof("skipping route %q -> %q on interface %q: %s", route.Network(), route.Gateway(), device.Interface(), err) continue } routes = append(routes, routeSpec) } for _, vlan := range device.Vlans() { vlanLinkName := nethelpers.VLANLinkName(device.Interface(), vlan.ID()) for _, route := range vlan.Routes() { routeSpec, err := convert(vlanLinkName, route) if err != nil { logger.Sugar().Infof("skipping route %q -> %q on interface %q: %s", route.Network(), route.Gateway(), vlanLinkName, err) continue } routes = append(routes, routeSpec) } } } return routes } //nolint:gocyclo func (ctrl *RouteConfigController) processMachineConfig(linkConfigs []cfg.NetworkCommonLinkConfig, blackholeRouteConfigs []cfg.NetworkBlackholeRouteConfig) (routes []network.RouteSpecSpec) { for _, linkConfig := range linkConfigs { for _, spec := range linkConfig.Routes() { var route network.RouteSpecSpec route.Destination = spec.Destination().ValueOrZero() route.Gateway = spec.Gateway().ValueOrZero() route.Source = spec.Source().ValueOrZero() normalizedFamily := route.Normalize() route.Priority = spec.Metric().ValueOr(network.DefaultRouteMetric) route.MTU = spec.MTU().ValueOrZero() switch { case !value.IsZero(route.Gateway) && route.Gateway.Is6(): route.Family = nethelpers.FamilyInet6 case !value.IsZero(route.Destination) && route.Destination.Addr().Is6(): route.Family = nethelpers.FamilyInet6 case normalizedFamily != 0: route.Family = normalizedFamily default: route.Family = nethelpers.FamilyInet4 } route.Table = nethelpers.TableMain route.Protocol = nethelpers.ProtocolStatic route.OutLinkName = linkConfig.Name() route.ConfigLayer = network.ConfigMachineConfiguration route.Type = nethelpers.TypeUnicast if route.Destination.Addr().IsMulticast() { route.Type = nethelpers.TypeMulticast } routes = append(routes, route) } } for _, blackholeRouteConfig := range blackholeRouteConfigs { destination, err := netip.ParsePrefix(blackholeRouteConfig.Name()) if err != nil { // validated in the machine config continue } family := nethelpers.FamilyInet4 if destination.Addr().Is6() { family = nethelpers.FamilyInet6 } route := network.RouteSpecSpec{ Destination: destination, Family: family, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeBlackhole, OutLinkName: "lo", ConfigLayer: network.ConfigMachineConfiguration, Priority: blackholeRouteConfig.Metric().ValueOr(network.DefaultRouteMetric), } routes = append(routes, route) } return routes } ================================================ FILE: internal/app/machined/pkg/controllers/network/route_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type RouteConfigSuite struct { ctest.DefaultSuite } func (suite *RouteConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.RouteConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1:255.255.255.0::eth1::::: ip=eth3:dhcp ip=10.3.5.7::10.3.5.1:255.255.255.0::eth4"), }, ), ) ctest.AssertResources( suite, []string{ "cmdline/inet4/172.20.0.1//1024", "cmdline/inet4/10.3.5.1//1026", }, func(r *network.RouteSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) switch r.Metadata().ID() { case "cmdline/inet4/172.20.0.1//1024": asrt.Equal("eth1", r.TypedSpec().OutLinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) case "cmdline/inet4/10.3.5.1//1025": asrt.Equal("eth4", r.TypedSpec().OutLinkName) asrt.EqualValues(network.DefaultRouteMetric+2, r.TypedSpec().Priority) } }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *RouteConfigSuite) TestCmdlineNotReachable() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.RouteConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2::172.20.0.1:255.255.255.255::eth1:::::"), }, ), ) ctest.AssertResources( suite, []string{ "cmdline/inet4/172.20.0.1//1024", "cmdline/inet4//172.20.0.1/32/1024", }, func(r *network.RouteSpec, asrt *assert.Assertions) { asrt.Equal(network.ConfigCmdline, r.TypedSpec().ConfigLayer) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) switch r.Metadata().ID() { case "cmdline/inet4/172.20.0.1//1024": asrt.Equal("eth1", r.TypedSpec().OutLinkName) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) case "cmdline/inet4//172.20.0.1/32/1024": asrt.Equal("eth1", r.TypedSpec().OutLinkName) asrt.Equal(netip.Addr{}, r.TypedSpec().Gateway) asrt.Equal(netip.MustParsePrefix("172.20.0.1/32"), r.TypedSpec().Destination) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) } }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *RouteConfigSuite) TestMachineConfigurationLegacy() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.RouteConfigController{})) suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.DeviceConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth3", DeviceAddresses: []string{"192.168.0.24/28"}, DeviceRoutes: []*v1alpha1.Route{ { RouteNetwork: "192.168.0.0/18", RouteGateway: "192.168.0.25", RouteMetric: 25, }, { RouteNetwork: "169.254.254.254/32", }, }, }, { DeviceIgnore: new(true), DeviceInterface: "eth4", DeviceAddresses: []string{"192.168.0.24/28"}, DeviceRoutes: []*v1alpha1.Route{ { RouteNetwork: "192.168.0.0/18", RouteGateway: "192.168.0.26", RouteMetric: 25, }, }, }, { DeviceInterface: "eth2", DeviceAddresses: []string{"2001:470:6d:30e:8ed2:b60c:9d2f:803a/64"}, DeviceRoutes: []*v1alpha1.Route{ { RouteGateway: "2001:470:6d:30e:8ed2:b60c:9d2f:803b", }, }, }, { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 24, VlanAddresses: []string{ "10.0.0.1/8", }, VlanRoutes: []*v1alpha1.Route{ { RouteNetwork: "10.0.3.0/24", RouteGateway: "10.0.3.1", }, }, }, }, }, { DeviceInterface: "eth1", DeviceRoutes: []*v1alpha1.Route{ { RouteNetwork: "192.244.0.0/24", RouteGateway: "192.244.0.1", RouteSource: "192.244.0.10", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/eth2/inet6/2001:470:6d:30e:8ed2:b60c:9d2f:803b//1024", "configuration/inet4/10.0.3.1/10.0.3.0/24/1024", "configuration/inet4/192.168.0.25/192.168.0.0/18/25", "configuration/inet4/192.244.0.1/192.244.0.0/24/1024", "configuration/inet4//169.254.254.254/32/1024", }, func(r *network.RouteSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "configuration/inet6/2001:470:6d:30e:8ed2:b60c:9d2f:803b//1024": asrt.Equal("eth2", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet6, r.TypedSpec().Family) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) case "configuration/inet4/10.0.3.1/10.0.3.0/24/1024": asrt.Equal("eth0.24", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) case "configuration/inet4/192.168.0.25/192.168.0.0/18/25": asrt.Equal("eth3", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(25, r.TypedSpec().Priority) case "configuration/inet4/192.244.0.1/192.244.0.0/24/1024": asrt.Equal("eth1", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) asrt.EqualValues(netip.MustParseAddr("192.244.0.10"), r.TypedSpec().Source) case "configuration/inet4//169.254.254.254/32/1024": asrt.Equal("eth3", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) asrt.Equal(nethelpers.ScopeLink, r.TypedSpec().Scope) asrt.Equal("169.254.254.254/32", r.TypedSpec().Destination.String()) } asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *RouteConfigSuite) TestMachineConfiguration() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.RouteConfigController{})) lc1 := networkcfg.NewLinkConfigV1Alpha1("enp0s2") lc1.LinkRoutes = []networkcfg.RouteConfig{ { RouteDestination: networkcfg.Prefix{Prefix: netip.MustParsePrefix("10.12.3.0/24")}, RouteGateway: networkcfg.Addr{Addr: netip.MustParseAddr("10.12.3.1")}, }, } lc2 := networkcfg.NewLinkConfigV1Alpha1("enp0s3") lc2.LinkRoutes = []networkcfg.RouteConfig{ { RouteGateway: networkcfg.Addr{Addr: netip.MustParseAddr("2001:470:6d:30e:8ed2:b60c:9d2f:803b")}, RouteMetric: 200, }, } bc1 := networkcfg.NewBlackholeRouteConfigV1Alpha1("10.1.3.4/32") bc1.RouteMetric = 300 ctr, err := container.New(lc1, lc2, bc1) suite.Require().NoError(err) suite.Create(config.NewMachineConfig(ctr)) ctest.AssertResources( suite, []string{ "configuration/enp0s3/inet6/2001:470:6d:30e:8ed2:b60c:9d2f:803b//200", "configuration/inet4/10.12.3.1/10.12.3.0/24/1024", "configuration/inet4//10.1.3.4/32/300", }, func(r *network.RouteSpec, asrt *assert.Assertions) { switch r.Metadata().ID() { case "configuration/enp0s3/inet6/2001:470:6d:30e:8ed2:b60c:9d2f:803b//200": asrt.Equal("enp0s3", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet6, r.TypedSpec().Family) asrt.EqualValues(200, r.TypedSpec().Priority) case "configuration/inet4/10.12.3.1/10.12.3.0/24/1024": asrt.Equal("enp0s2", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(network.DefaultRouteMetric, r.TypedSpec().Priority) case "configuration/inet4//10.1.3.4/32/300": asrt.Equal("lo", r.TypedSpec().OutLinkName) asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.EqualValues(300, r.TypedSpec().Priority) asrt.Equal(nethelpers.TypeBlackhole, r.TypedSpec().Type) } asrt.Equal(network.ConfigMachineConfiguration, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func TestRouteConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &RouteConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/route_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewRouteMergeController initializes a RouteMergeController. // // RouteMergeController merges network.RouteSpec in network.ConfigNamespace and produces final network.RouteSpec in network.Namespace. func NewRouteMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.RouteSpec]) map[resource.ID]*network.RouteSpecSpec { // route is allowed as long as it's not duplicate, for duplicate higher layer takes precedence routes := map[string]*network.RouteSpecSpec{} for route := range list.All() { id := network.RouteID(route.TypedSpec().Table, route.TypedSpec().Family, route.TypedSpec().Destination, route.TypedSpec().Gateway, route.TypedSpec().Priority, route.TypedSpec().OutLinkName) existing, ok := routes[id] if ok && existing.ConfigLayer > route.TypedSpec().ConfigLayer { // skip this route, as existing one is higher layer continue } routes[id] = route.TypedSpec() } return routes }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/route_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "math/rand/v2" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type RouteMergeSuite struct { ctest.DefaultSuite } func (suite *RouteMergeSuite) assertRoutes(requiredIDs []string, check func(*network.RouteSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *RouteMergeSuite) assertNoRoute(id string) { ctest.AssertNoResource[*network.RouteSpec](suite, id) } func (suite *RouteMergeSuite) TestMerge() { cmdline := network.NewRouteSpec(network.ConfigNamespaceName, "cmdline/inet4//10.5.0.3/50") *cmdline.TypedSpec() = network.RouteSpecSpec{ Gateway: netip.MustParseAddr("10.5.0.3"), OutLinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Table: nethelpers.TableMain, Priority: 50, ConfigLayer: network.ConfigCmdline, } dhcp := network.NewRouteSpec(network.ConfigNamespaceName, "dhcp/inet4//10.5.0.3/50") *dhcp.TypedSpec() = network.RouteSpecSpec{ Gateway: netip.MustParseAddr("10.5.0.3"), OutLinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Table: nethelpers.TableMain, Priority: 50, ConfigLayer: network.ConfigOperator, } static := network.NewRouteSpec(network.ConfigNamespaceName, "configuration/inet4/10.0.0.35/32/10.0.0.34/1024") *static.TypedSpec() = network.RouteSpecSpec{ Destination: netip.MustParsePrefix("10.0.0.35/32"), Gateway: netip.MustParseAddr("10.0.0.34"), OutLinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Table: nethelpers.TableMain, Priority: 1024, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{cmdline, dhcp, static} { suite.Create(res) } suite.assertRoutes( []string{ "inet4/10.5.0.3//50", "inet4/10.0.0.34/10.0.0.35/32/1024", }, func(r *network.RouteSpec, asrt *assert.Assertions) { asrt.Equal(resource.PhaseRunning, r.Metadata().Phase()) switch r.Metadata().ID() { case "inet4/10.5.0.3//50": asrt.Equal(*dhcp.TypedSpec(), *r.TypedSpec()) case "inet4/10.0.0.34/10.0.0.35/32/1024": asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) } }, ) suite.Destroy(dhcp) suite.assertRoutes( []string{ "inet4/10.5.0.3//50", "inet4/10.0.0.34/10.0.0.35/32/1024", }, func(r *network.RouteSpec, asrt *assert.Assertions) { asrt.Equal(resource.PhaseRunning, r.Metadata().Phase()) switch r.Metadata().ID() { case "inet4/10.5.0.3//50": asrt.Equal(*cmdline.TypedSpec(), *r.TypedSpec()) case "inet4/10.0.0.34/10.0.0.35/32/1024": asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) } }, ) suite.Destroy(static) suite.assertNoRoute("inet4/10.0.0.34/10.0.0.35/32/1024") } func testMergeFlapping[R rtestutils.ResourceWithRD](suite *ctest.DefaultSuite, resources []R, outputID string, mergedResource R) { var zeroR R outputMetadata := resource.NewMetadata( zeroR.ResourceDefinition().DefaultNamespace, zeroR.ResourceDefinition().Type, outputID, resource.VersionUndefined, ) for range 30 { // pick a set of input resources to create, each bit in the choice represents a resource choice := rand.IntN(1 << len(resources)) if choice == 0 { continue } for idx, res := range resources { if choice&(1< rule.TypedSpec().ConfigLayer { // skip this rule, as existing one is higher layer continue } rules[id] = rule.TypedSpec() } return rules }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/routing_rule_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type RoutingRuleMergeSuite struct { ctest.DefaultSuite } func (suite *RoutingRuleMergeSuite) assertRoutingRules(requiredIDs []string, check func(*network.RoutingRuleSpec, *assert.Assertions)) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *RoutingRuleMergeSuite) assertNoRoutingRule(id string) { ctest.AssertNoResource[*network.RoutingRuleSpec](suite, id) } func (suite *RoutingRuleMergeSuite) TestMerge() { // Create two rules with the same key (family/src/dst/priority) but different config layers. // The higher layer should win. cmdline := network.NewRoutingRuleSpec(network.ConfigNamespaceName, "cmdline/inet4/01000") *cmdline.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Src: netip.MustParsePrefix("10.0.0.0/8"), Table: nethelpers.RoutingTable(100), Priority: 1000, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigCmdline, } machineConfig := network.NewRoutingRuleSpec(network.ConfigNamespaceName, "configuration/inet4/01000") *machineConfig.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Src: netip.MustParsePrefix("10.0.0.0/8"), Table: nethelpers.RoutingTable(200), Priority: 1000, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } // A unique rule with no conflict. static := network.NewRoutingRuleSpec(network.ConfigNamespaceName, "configuration/inet4/02000") *static.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Dst: netip.MustParsePrefix("192.168.0.0/16"), Table: nethelpers.RoutingTable(123), Priority: 2000, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{cmdline, machineConfig, static} { suite.Create(res) } suite.assertRoutingRules( []string{ "inet4/01000", "inet4/02000", }, func(r *network.RoutingRuleSpec, asrt *assert.Assertions) { asrt.Equal(resource.PhaseRunning, r.Metadata().Phase()) switch r.Metadata().ID() { case "inet4/01000": // machineConfig (ConfigMachineConfiguration) has higher layer than cmdline (ConfigCmdline) asrt.Equal(*machineConfig.TypedSpec(), *r.TypedSpec()) case "inet4/02000": asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) } }, ) // Remove the higher-layer resource; cmdline should now surface. suite.Destroy(machineConfig) suite.assertRoutingRules( []string{ "inet4/01000", "inet4/02000", }, func(r *network.RoutingRuleSpec, asrt *assert.Assertions) { asrt.Equal(resource.PhaseRunning, r.Metadata().Phase()) switch r.Metadata().ID() { case "inet4/01000": asrt.Equal(*cmdline.TypedSpec(), *r.TypedSpec()) case "inet4/02000": asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) } }, ) // Destroy the static rule and verify it disappears. suite.Destroy(static) suite.assertNoRoutingRule("inet4/02000") } //nolint:gocyclo func (suite *RoutingRuleMergeSuite) TestMergeFlapping() { // Simulate two conflicting rule definitions which are getting removed/added constantly. cmdline := network.NewRoutingRuleSpec(network.ConfigNamespaceName, "cmdline/inet4/00500") *cmdline.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Src: netip.MustParsePrefix("10.0.0.0/8"), Table: nethelpers.RoutingTable(100), Priority: 500, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigCmdline, } machineConfig := network.NewRoutingRuleSpec(network.ConfigNamespaceName, "configuration/inet4/00500") *machineConfig.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Src: netip.MustParsePrefix("10.0.0.0/8"), Table: nethelpers.RoutingTable(200), Priority: 500, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } testMergeFlapping(&suite.DefaultSuite, []*network.RoutingRuleSpec{cmdline, machineConfig}, "inet4/00500", machineConfig) } func TestRoutingRuleMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &RoutingRuleMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewRoutingRuleMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/routing_rule_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "errors" "fmt" "net" "net/netip" "os" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-multierror" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // RoutingRuleSpecController applies network.RoutingRuleSpec to the kernel. type RoutingRuleSpecController struct{} // Name implements controller.Controller interface. func (ctrl *RoutingRuleSpecController) Name() string { return "network.RoutingRuleSpecController" } // Inputs implements controller.Controller interface. func (ctrl *RoutingRuleSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.RoutingRuleSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *RoutingRuleSpecController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *RoutingRuleSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // watch link changes as some routes might need to be re-applied if the link appears watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_IPV4_RULE) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // list source network configuration resources list, err := safe.ReaderListAll[*network.RoutingRuleSpec](ctx, r) if err != nil { return fmt.Errorf("error listing source routing rules: %w", err) } // add finalizers for all live resources for res := range list.All() { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // list existing kernel rules existingRules, err := conn.Rule.List() if err != nil { return fmt.Errorf("error listing kernel rules: %w", err) } var multiErr *multierror.Error // loop over rules and make reconcile decision for rule := range list.All() { if err = ctrl.syncRule(ctx, r, logger, conn, existingRules, rule); err != nil { multiErr = multierror.Append(multiErr, err) } } if err = multiErr.ErrorOrNil(); err != nil { return err } r.ResetRestartBackoff() } } //nolint:gocyclo,cyclop func (ctrl *RoutingRuleSpecController) syncRule( ctx context.Context, r controller.Runtime, logger *zap.Logger, conn *rtnetlink.Conn, existingRules []rtnetlink.RuleMessage, rule *network.RoutingRuleSpec, ) error { spec := rule.TypedSpec() switch rule.Metadata().Phase() { case resource.PhaseTearingDown: for i := range existingRules { if ctrl.matchesRuleKey(&existingRules[i], spec) { if err := conn.Rule.Delete(&existingRules[i]); err != nil { if !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("error removing routing rule: %w", err) } } logger.Info("deleted routing rule", zap.Uint8("family", existingRules[i].Family), zap.Uint8("table", existingRules[i].Table), zap.Uint8("action", existingRules[i].Action), zap.Uint32("priority", pointer.SafeDeref(existingRules[i].Attributes.Priority)), zap.Stringer("src", pointer.SafeDeref(existingRules[i].Attributes.Src)), zap.Stringer("dst", pointer.SafeDeref(existingRules[i].Attributes.Dst)), zap.String("iif", pointer.SafeDeref(existingRules[i].Attributes.IIFName)), zap.String("oif", pointer.SafeDeref(existingRules[i].Attributes.OIFName)), zap.Uint32("fwmark", pointer.SafeDeref(existingRules[i].Attributes.FwMark)), zap.Uint32("fwmask", pointer.SafeDeref(existingRules[i].Attributes.FwMask)), ) } } // remove finalizer if err := r.RemoveFinalizer(ctx, rule.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: existingIdx := []int{} for i := range existingRules { // find rules that match the unique key but differ in other attributes - these need to be deleted and re-created to update if ctrl.matchesRuleKey(&existingRules[i], spec) && !ctrl.matchesRule(&existingRules[i], spec) { existingIdx = append(existingIdx, i) } } msg := ctrl.buildRuleMessage(spec) for _, idx := range existingIdx { if err := conn.Rule.Delete(&existingRules[idx]); err != nil { return fmt.Errorf("error deleting routing rule during update: %w, spec %+v", err, *spec) } } if err := conn.Rule.Add(msg); err != nil { // If the rule already exists, it means there was no change in attributes and we can ignore the error. if !errors.Is(err, os.ErrExist) { return fmt.Errorf("error adding routing rule: %w, spec %+v", err, *spec) } return nil } action := "created" if len(existingIdx) > 0 { action = "replaced" } logger.Info(action+" routing rule", zap.Stringer("family", spec.Family), zap.Stringer("src", spec.Src), zap.Stringer("dst", spec.Dst), zap.Stringer("table", spec.Table), zap.Uint32("priority", spec.Priority), zap.Stringer("action", spec.Action), zap.String("iif", spec.IIFName), zap.String("oif", spec.OIFName), zap.Uint32("fwmark", spec.FwMark), zap.Uint32("fwmask", spec.FwMask), ) } return nil } //nolint:gocyclo func (ctrl *RoutingRuleSpecController) matchesRule(existing *rtnetlink.RuleMessage, spec *network.RoutingRuleSpecSpec) bool { // Compare priority only - this is the unique key existingPriority := pointer.SafeDeref(existing.Attributes.Priority) if existingPriority != spec.Priority { return false } // Compare family if existing.Family != uint8(spec.Family) { return false } // Compare action if existing.Action != uint8(spec.Action) { return false } // compare table existingTable := uint32(existing.Table) if existing.Attributes.Table != nil { existingTable = *existing.Attributes.Table } if existingTable != uint32(spec.Table) { return false } // compare src if !ctrl.matchesPrefix(existing.Attributes.Src, existing.SrcLength, spec.Src, spec.Family) { return false } // compare dst if !ctrl.matchesPrefix(existing.Attributes.Dst, existing.DstLength, spec.Dst, spec.Family) { return false } // compare iif/oif if pointer.SafeDeref(existing.Attributes.IIFName) != spec.IIFName { return false } if pointer.SafeDeref(existing.Attributes.OIFName) != spec.OIFName { return false } // compare fwmark/fwmask if pointer.SafeDeref(existing.Attributes.FwMark) != spec.FwMark { return false } if pointer.SafeDeref(existing.Attributes.FwMask) != spec.FwMask { return false } return true } //nolint:gocyclo func (ctrl *RoutingRuleSpecController) matchesPrefix(existingIP *net.IP, existingLen uint8, specPrefix netip.Prefix, family nethelpers.Family) bool { if specPrefix.IsValid() { if family == nethelpers.FamilyInet4 && !specPrefix.Addr().Is4() { return false } if family == nethelpers.FamilyInet6 && !specPrefix.Addr().Is6() { return false } } if !specPrefix.IsValid() || specPrefix.Bits() == 0 { return existingLen == 0 } if existingLen != uint8(specPrefix.Bits()) { return false } if existingIP == nil { return false } existingAddr, ok := netip.AddrFromSlice(*existingIP) if !ok { return false } return existingAddr == specPrefix.Addr() } func (ctrl *RoutingRuleSpecController) matchesRuleKey(existing *rtnetlink.RuleMessage, spec *network.RoutingRuleSpecSpec) bool { if pointer.SafeDeref(existing.Attributes.Priority) != spec.Priority { return false } if existing.Family != uint8(spec.Family) { return false } return true } func (ctrl *RoutingRuleSpecController) buildRuleMessage(spec *network.RoutingRuleSpecSpec) *rtnetlink.RuleMessage { msg := &rtnetlink.RuleMessage{ Family: uint8(spec.Family), Table: uint8(spec.Table), Action: uint8(spec.Action), Attributes: &rtnetlink.RuleAttributes{ Priority: new(spec.Priority), Table: new(uint32(spec.Table)), }, } if spec.Src.IsValid() && spec.Src.Bits() > 0 { msg.SrcLength = uint8(spec.Src.Bits()) srcIP := net.IP(spec.Src.Addr().AsSlice()) msg.Attributes.Src = &srcIP } if spec.Dst.IsValid() && spec.Dst.Bits() > 0 { msg.DstLength = uint8(spec.Dst.Bits()) dstIP := net.IP(spec.Dst.Addr().AsSlice()) msg.Attributes.Dst = &dstIP } if spec.IIFName != "" { msg.Attributes.IIFName = new(spec.IIFName) } if spec.OIFName != "" { msg.Attributes.OIFName = new(spec.OIFName) } if spec.FwMark != 0 { msg.Attributes.FwMark = new(spec.FwMark) } if spec.FwMask != 0 { msg.Attributes.FwMask = new(spec.FwMask) } // set protocol to indicate this rule was created by us proto := uint8(unix.RTPROT_STATIC) msg.Attributes.Protocol = &proto return msg } ================================================ FILE: internal/app/machined/pkg/controllers/network/routing_rule_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "fmt" "net" "net/netip" "os" "testing" "time" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type RoutingRuleSpecSuite struct { ctest.DefaultSuite } func (suite *RoutingRuleSpecSuite) assertRule( family nethelpers.Family, src, dst netip.Prefix, priority uint32, check func(rtnetlink.RuleMessage) error, ) error { conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck rules, err := conn.Rule.List() suite.Require().NoError(err) for _, rule := range rules { if rule.Family != uint8(family) { continue } if pointer.SafeDeref(rule.Attributes.Priority) != priority { continue } if !matchPrefix(rule.Attributes.Src, rule.SrcLength, src) { continue } if !matchPrefix(rule.Attributes.Dst, rule.DstLength, dst) { continue } if err = check(rule); err != nil { return retry.ExpectedError(err) } return nil } return retry.ExpectedErrorf("rule family=%s src=%s dst=%s priority=%d not found", family, src, dst, priority) } func (suite *RoutingRuleSpecSuite) assertNoRule( family nethelpers.Family, src, dst netip.Prefix, priority uint32, ) error { conn, err := rtnetlink.Dial(nil) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck rules, err := conn.Rule.List() suite.Require().NoError(err) for _, rule := range rules { if rule.Family != uint8(family) { continue } if pointer.SafeDeref(rule.Attributes.Priority) != priority { continue } if !matchPrefix(rule.Attributes.Src, rule.SrcLength, src) { continue } if !matchPrefix(rule.Attributes.Dst, rule.DstLength, dst) { continue } return retry.ExpectedErrorf("rule family=%s src=%q dst=%q priority=%d is still present", family, src, dst, priority) } return nil } func matchPrefix(ip *net.IP, length uint8, prefix netip.Prefix) bool { if !prefix.IsValid() || prefix.Bits() == 0 { return length == 0 } if length != uint8(prefix.Bits()) { return false } if ip == nil { return false } addr, ok := netip.AddrFromSlice(*ip) if !ok { return false } return addr == prefix.Addr() } //nolint:dupl func (suite *RoutingRuleSpecSuite) TestCreateAndDelete() { priority := uint32(31000) // Use a high priority number to avoid conflicting with default rules. rule := network.NewRoutingRuleSpec(network.NamespaceName, "test-rule") *rule.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Src: netip.MustParsePrefix("10.99.0.0/16"), Table: nethelpers.RoutingTable(100), Priority: priority, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } suite.Create(rule) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet4, netip.MustParsePrefix("10.99.0.0/16"), netip.Prefix{}, priority, func(r rtnetlink.RuleMessage) error { table := uint32(r.Table) if r.Attributes.Table != nil { table = *r.Attributes.Table } if table != 100 { return fmt.Errorf("unexpected table: got %d, want %d", table, 100) } if r.Action != unix.FR_ACT_TO_TBL { return fmt.Errorf("unexpected action: got %d, want %d", r.Action, unix.FR_ACT_TO_TBL) } return nil }, ) }, ), ) // Teardown the rule. suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), rule.Metadata())) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoRule( nethelpers.FamilyInet4, netip.MustParsePrefix("10.99.0.0/16"), netip.Prefix{}, priority, ) }, ), ) } //nolint:dupl func (suite *RoutingRuleSpecSuite) TestFwMarkUpdate() { priority := uint32(31002) rule := network.NewRoutingRuleSpec(network.NamespaceName, "fwmark-update-rule") *rule.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Table: nethelpers.RoutingTable(100), Priority: priority, Action: nethelpers.RoutingRuleActionUnicast, FwMark: 0x100, FwMask: 0xff00, ConfigLayer: network.ConfigMachineConfiguration, } suite.Create(rule) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.Prefix{}, priority, func(r rtnetlink.RuleMessage) error { if pointer.SafeDeref(r.Attributes.FwMark) != 0x100 { return fmt.Errorf("unexpected fwmark: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMark), 0x100) } if pointer.SafeDeref(r.Attributes.FwMask) != 0xff00 { return fmt.Errorf("unexpected fwmask: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMask), 0xff00) } return nil }, ) }, ), ) // finalizer updates the rule, so we need to fetch the latest version before proceeding with the update. r, err := suite.State().Get(suite.Ctx(), rule.Metadata()) suite.Require().NoError(err) rule = r.(*network.RoutingRuleSpec) rule.TypedSpec().FwMark = 0x200 suite.Update(rule) suite.Assert().NoError( retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.Prefix{}, priority, func(r rtnetlink.RuleMessage) error { if pointer.SafeDeref(r.Attributes.FwMark) != 0x200 { return fmt.Errorf("unexpected fwmark after update: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMark), 0x200) } if pointer.SafeDeref(r.Attributes.FwMask) != 0xff00 { return fmt.Errorf("unexpected fwmask after update: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMask), 0xff00) } return nil }, ) }, ), ) suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), rule.Metadata())) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.Prefix{}, priority, ) }, ), ) } //nolint:dupl func (suite *RoutingRuleSpecSuite) TestFwMark() { priority := uint32(31001) rule := network.NewRoutingRuleSpec(network.NamespaceName, "fwmark-rule") *rule.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Table: nethelpers.RoutingTable(100), Priority: priority, Action: nethelpers.RoutingRuleActionUnicast, FwMark: 0x100, FwMask: 0xff00, ConfigLayer: network.ConfigMachineConfiguration, } suite.Create(rule) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.Prefix{}, priority, func(r rtnetlink.RuleMessage) error { if pointer.SafeDeref(r.Attributes.FwMark) != 0x100 { return fmt.Errorf("unexpected fwmark: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMark), 0x100) } if pointer.SafeDeref(r.Attributes.FwMask) != 0xff00 { return fmt.Errorf("unexpected fwmask: got %x, want %x", pointer.SafeDeref(r.Attributes.FwMask), 0xff00) } return nil }, ) }, ), ) // Teardown. suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), rule.Metadata())) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.Prefix{}, priority, ) }, ), ) } //nolint:dupl func (suite *RoutingRuleSpecSuite) TestIPv6() { priority := uint32(31003) rule := network.NewRoutingRuleSpec(network.NamespaceName, "ipv6-rule") *rule.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet6, Src: netip.MustParsePrefix("fd00::/8"), Table: nethelpers.RoutingTable(100), Priority: priority, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } suite.Create(rule) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet6, netip.MustParsePrefix("fd00::/8"), netip.Prefix{}, priority, func(r rtnetlink.RuleMessage) error { if r.Action != unix.FR_ACT_TO_TBL { return fmt.Errorf("unexpected action: got %d, want %d", r.Action, unix.FR_ACT_TO_TBL) } return nil }, ) }, ), ) suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), rule.Metadata())) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoRule( nethelpers.FamilyInet6, netip.MustParsePrefix("fd00::/8"), netip.Prefix{}, priority, ) }, ), ) } //nolint:dupl func (suite *RoutingRuleSpecSuite) TestDstPrefix() { priority := uint32(31004) rule := network.NewRoutingRuleSpec(network.NamespaceName, "dst-prefix-rule") *rule.TypedSpec() = network.RoutingRuleSpecSpec{ Family: nethelpers.FamilyInet4, Dst: netip.MustParsePrefix("192.168.0.0/16"), Table: nethelpers.RoutingTable(100), Priority: priority, Action: nethelpers.RoutingRuleActionUnicast, ConfigLayer: network.ConfigMachineConfiguration, } suite.Create(rule) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.MustParsePrefix("192.168.0.0/16"), priority, func(r rtnetlink.RuleMessage) error { if r.Action != unix.FR_ACT_TO_TBL { return fmt.Errorf("unexpected action: got %d, want %d", r.Action, unix.FR_ACT_TO_TBL) } return nil }, ) }, ), ) suite.Require().NoError(suite.State().TeardownAndDestroy(suite.Ctx(), rule.Metadata())) suite.Assert().NoError( retry.Constant(3*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertNoRule( nethelpers.FamilyInet4, netip.Prefix{}, netip.MustParsePrefix("192.168.0.0/16"), priority, ) }, ), ) } func TestRoutingRuleSpecSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("requires root") } suite.Run(t, &RoutingRuleSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 15 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.RoutingRuleSpecController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/routing_rule_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/go-pointer" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // RoutingRuleStatusController observes kernel routing rules and publishes them as resources. type RoutingRuleStatusController struct{} // Name implements controller.Controller interface. func (ctrl *RoutingRuleStatusController) Name() string { return "network.RoutingRuleStatusController" } // Inputs implements controller.Controller interface. func (ctrl *RoutingRuleStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *RoutingRuleStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: network.RoutingRuleStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *RoutingRuleStatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // watch link changes as some routes might need to be re-applied if the link appears watcher, err := watch.NewRtNetlink(watch.NewDefaultRateLimitedTrigger(ctx, r), unix.RTMGRP_IPV4_RULE, unix.RTNLGRP_IPV4_RULE, unix.RTNLGRP_IPV6_RULE) if err != nil { return err } defer watcher.Done() conn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rtnetlink socket: %w", err) } defer conn.Close() //nolint:errcheck for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() rules, err := conn.Rule.List() if err != nil { return fmt.Errorf("error listing kernel rules: %w", err) } for _, rule := range rules { family := nethelpers.Family(rule.Family) var src netip.Prefix if rule.Attributes.Src != nil { srcAddr, ok := netip.AddrFromSlice(*rule.Attributes.Src) if ok { src = netip.PrefixFrom(srcAddr, int(rule.SrcLength)) } } var dst netip.Prefix if rule.Attributes.Dst != nil { dstAddr, ok := netip.AddrFromSlice(*rule.Attributes.Dst) if ok { dst = netip.PrefixFrom(dstAddr, int(rule.DstLength)) } } priority := pointer.SafeDeref(rule.Attributes.Priority) table := uint32(rule.Table) if rule.Attributes.Table != nil { table = *rule.Attributes.Table } id := network.RoutingRuleID(family, priority) if err = safe.WriterModify(ctx, r, network.NewRoutingRuleStatus(network.NamespaceName, id), func(res *network.RoutingRuleStatus) error { status := res.TypedSpec() status.Family = family status.Src = src status.Dst = dst status.Table = nethelpers.RoutingTable(table) status.Priority = priority status.Action = nethelpers.RoutingRuleAction(rule.Action) status.IIFName = pointer.SafeDeref(rule.Attributes.IIFName) status.OIFName = pointer.SafeDeref(rule.Attributes.OIFName) status.FwMark = pointer.SafeDeref(rule.Attributes.FwMark) status.FwMask = pointer.SafeDeref(rule.Attributes.FwMask) return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } } if err := safe.CleanupOutputs[*network.RoutingRuleStatus](ctx, r); err != nil { return fmt.Errorf("error doing cleanup: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/network/routing_rule_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type RoutingRuleStatusSuite struct { ctest.DefaultSuite } func (suite *RoutingRuleStatusSuite) TestRules() { // Every Linux system has default rules: // 0: from all lookup local // 32766: from all lookup main // 32767: from all lookup default // // Assert that at least the "from all lookup local" rule (priority 0, table 255/local) is published. ctest.AssertResource( suite, "inet4/00000", func(r *network.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet4, r.TypedSpec().Family) asrt.Equal(nethelpers.RoutingTable(255), r.TypedSpec().Table) // local table asrt.EqualValues(0, r.TypedSpec().Priority) asrt.Equal(nethelpers.RoutingRuleActionUnicast, r.TypedSpec().Action) }, ) } func TestRoutingRuleStatusSuite(t *testing.T) { t.Parallel() suite.Run(t, &RoutingRuleStatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.RoutingRuleStatusController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/value" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // StatusController manages network.Status based on state of other resources. type StatusController struct { V1Alpha1Mode runtime.Mode } // Name implements controller.Controller interface. func (ctrl *StatusController) Name() string { return "network.StatusController" } // Inputs implements controller.Controller interface. func (ctrl *StatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.NodeAddressCurrentID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.RouteStatusType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, Kind: controller.InputWeak, }, { Namespace: files.NamespaceName, Type: files.EtcFileStatusType, Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.ProbeStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *StatusController) Outputs() []controller.Output { return []controller.Output{ { Type: network.StatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *StatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } result := network.StatusSpec{} // addresses currentAddresses, err := safe.ReaderGet[*network.NodeAddress](ctx, r, resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.NodeAddressCurrentID, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting resource: %w", err) } } else { result.AddressReady = len(currentAddresses.TypedSpec().Addresses) > 0 } // connectivity // if any probes are defined, use their status, otherwise rely on presence of the default gateway probeStatuses, err := safe.ReaderListAll[*network.ProbeStatus](ctx, r) if err != nil { return fmt.Errorf("error getting probe statuses: %w", err) } allProbesSuccess := true for res := range probeStatuses.All() { if !res.TypedSpec().Success { allProbesSuccess = false break } } if probeStatuses.Len() > 0 && allProbesSuccess { result.ConnectivityReady = true } else if probeStatuses.Len() == 0 { var routes safe.List[*network.RouteStatus] routes, err = safe.ReaderListAll[*network.RouteStatus](ctx, r) if err != nil { return fmt.Errorf("error getting routes: %w", err) } for route := range routes.All() { if value.IsZero(route.TypedSpec().Destination) { result.ConnectivityReady = true break } } } // hostname _, err = r.Get(ctx, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting resource: %w", err) } } else { result.HostnameReady = true } // etc files result.EtcFilesReady = true for _, requiredFile := range []string{"hosts", "resolv.conf"} { // in container mode, ignore resolv.conf, it's managed by the container runtime if ctrl.V1Alpha1Mode.InContainer() && requiredFile == "resolv.conf" { continue } _, err = r.Get(ctx, resource.NewMetadata(files.NamespaceName, files.EtcFileStatusType, requiredFile, resource.VersionUndefined)) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting resource: %w", err) } result.EtcFilesReady = false break } } // update output status if err = safe.WriterModify(ctx, r, network.NewStatus(network.NamespaceName, network.StatusID), func(r *network.Status) error { *r.TypedSpec() = result return nil }); err != nil { return fmt.Errorf("error modifying output status: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type StatusSuite struct { ctest.DefaultSuite } func (suite *StatusSuite) TestNone() { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{}, *r.TypedSpec()) }) } func (suite *StatusSuite) TestAddresses() { nodeAddress := network.NewNodeAddress(network.NamespaceName, network.NodeAddressCurrentID) nodeAddress.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.0.0.1/24")} suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeAddress)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{AddressReady: true}, *r.TypedSpec()) }) } func (suite *StatusSuite) TestRoutes() { route := network.NewRouteStatus(network.NamespaceName, "foo") route.TypedSpec().Gateway = netip.MustParseAddr("10.0.0.1") suite.Require().NoError(suite.State().Create(suite.Ctx(), route)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{ConnectivityReady: true}, *r.TypedSpec()) }) } func (suite *StatusSuite) TestProbeStatuses() { probeStatus := network.NewProbeStatus(network.NamespaceName, "foo") probeStatus.TypedSpec().Success = true suite.Require().NoError(suite.State().Create(suite.Ctx(), probeStatus)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{ConnectivityReady: true}, *r.TypedSpec()) }) // failing probe make status not ready route := network.NewRouteStatus(network.NamespaceName, "foo") route.TypedSpec().Gateway = netip.MustParseAddr("10.0.0.1") suite.Require().NoError(suite.State().Create(suite.Ctx(), route)) probeStatusFail := network.NewProbeStatus(network.NamespaceName, "failing") suite.Require().NoError(suite.State().Create(suite.Ctx(), probeStatusFail)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{}, *r.TypedSpec()) }) } func (suite *StatusSuite) TestHostname() { hostname := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostname.TypedSpec().Hostname = "foo" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostname)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{HostnameReady: true}, *r.TypedSpec()) }) } func (suite *StatusSuite) TestEtcFiles() { for _, f := range []string{"hosts", "resolv.conf"} { suite.Require().NoError(suite.State().Create(suite.Ctx(), files.NewEtcFileStatus(files.NamespaceName, f))) } rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{network.StatusID}, func(r *network.Status, assert *assert.Assertions) { assert.Equal(network.StatusSpec{EtcFilesReady: true}, *r.TypedSpec()) }) } func TestStatusSuite(t *testing.T) { suite.Run(t, &StatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController( &netctrl.StatusController{ V1Alpha1Mode: runtime.ModeMetal, }, )) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // TimeServerConfigController manages network.TimeServerSpec based on machine configuration, kernel cmdline. type TimeServerConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *TimeServerConfigController) Name() string { return "network.TimeServerConfigController" } // Inputs implements controller.Controller interface. func (ctrl *TimeServerConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *TimeServerConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: network.TimeServerSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *TimeServerConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } touchedIDs := make(map[resource.ID]struct{}) var cfgProvider talosconfig.Config cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } else { cfgProvider = cfg.Config() } var specs []network.TimeServerSpecSpec // defaults specs = append(specs, ctrl.getDefault()) // parse kernel cmdline for the default gateway cmdlineServers := ctrl.parseCmdline(logger) if cmdlineServers.NTPServers != nil { specs = append(specs, cmdlineServers) } // parse machine configuration for specs if cfgProvider != nil { configServers := ctrl.parseMachineConfiguration(cfgProvider) if configServers.NTPServers != nil { specs = append(specs, configServers) } } var ids []string ids, err = ctrl.apply(ctx, r, specs) if err != nil { return fmt.Errorf("error applying specs: %w", err) } for _, id := range ids { touchedIDs[id] = struct{}{} } // list specs for cleanup list, err := r.List(ctx, resource.NewMetadata(network.ConfigNamespaceName, network.TimeServerSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing resources: %w", err) } for _, res := range list.Items { if res.Metadata().Owner() != ctrl.Name() { // skip specs created by other controllers continue } if _, ok := touchedIDs[res.Metadata().ID()]; !ok { if err = r.Destroy(ctx, res.Metadata()); err != nil { return fmt.Errorf("error cleaning up specs: %w", err) } } } r.ResetRestartBackoff() } } //nolint:dupl func (ctrl *TimeServerConfigController) apply(ctx context.Context, r controller.Runtime, specs []network.TimeServerSpecSpec) ([]resource.ID, error) { ids := make([]string, 0, len(specs)) for _, spec := range specs { id := network.LayeredID(spec.ConfigLayer, network.TimeServerID) if err := safe.WriterModify( ctx, r, network.NewTimeServerSpec(network.ConfigNamespaceName, id), func(r *network.TimeServerSpec) error { *r.TypedSpec() = spec return nil }, ); err != nil { return ids, err } ids = append(ids, id) } return ids, nil } func (ctrl *TimeServerConfigController) getDefault() (spec network.TimeServerSpecSpec) { spec.NTPServers = []string{constants.DefaultNTPServer} spec.ConfigLayer = network.ConfigDefault return spec } func (ctrl *TimeServerConfigController) parseCmdline(logger *zap.Logger) (spec network.TimeServerSpecSpec) { if ctrl.Cmdline == nil { return spec } settings, err := ParseCmdlineNetwork(ctrl.Cmdline, network.NewEmptyLinkResolver()) if err != nil { logger.Warn("ignoring error", zap.Error(err)) return spec } if len(settings.NTPAddresses) == 0 { return spec } spec.NTPServers = make([]string, len(settings.NTPAddresses)) spec.ConfigLayer = network.ConfigCmdline for i := range settings.NTPAddresses { spec.NTPServers[i] = settings.NTPAddresses[i].String() } return spec } func (ctrl *TimeServerConfigController) parseMachineConfiguration(cfgProvider talosconfig.Config) (spec network.TimeServerSpecSpec) { if cfgProvider.NetworkTimeSyncConfig() == nil { return spec } spec.NTPServers = slices.Clone(cfgProvider.NetworkTimeSyncConfig().Servers()) spec.ConfigLayer = network.ConfigMachineConfiguration return spec } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/config/container" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type TimeServerConfigSuite struct { ctest.DefaultSuite } func (suite *TimeServerConfigSuite) TestDefaults() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{})) ctest.AssertResources( suite, []string{ "default/timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal([]string{constants.DefaultNTPServer}, r.TypedSpec().NTPServers) asrt.Equal(network.ConfigDefault, r.TypedSpec().ConfigLayer) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *TimeServerConfigSuite) TestCmdline() { suite.Require().NoError( suite.Runtime().RegisterController( &netctrl.TimeServerConfigController{ Cmdline: procfs.NewCmdline("ip=172.20.0.2:172.21.0.1:172.20.0.1:255.255.255.0:master1:eth1::10.0.0.1:10.0.0.2:10.0.0.1"), }, ), ) ctest.AssertResources( suite, []string{ "cmdline/timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal([]string{"10.0.0.1"}, r.TypedSpec().NTPServers) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *TimeServerConfigSuite) TestMachineConfigurationLegacy() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{})) u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeServers: []string{"za.pool.ntp.org", "pool.ntp.org"}, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, }, }, ), ) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal([]string{"za.pool.ntp.org", "pool.ntp.org"}, r.TypedSpec().NTPServers) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error { r.Container().RawV1Alpha1().MachineConfig.MachineTime = nil //nolint:staticcheck return nil }) ctest.AssertNoResource[*network.TimeServerSpec](suite, "configuration/timeservers", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func (suite *TimeServerConfigSuite) TestMachineConfigurationNewStyle() { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerConfigController{})) tsc := networkcfg.NewTimeSyncConfigV1Alpha1() tsc.TimeNTP = &networkcfg.NTPConfig{ Servers: []string{"za.pool.ntp.org", "pool.ntp.org"}, } ctr, err := container.New(tsc) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertResources( suite, []string{ "configuration/timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal([]string{"za.pool.ntp.org", "pool.ntp.org"}, r.TypedSpec().NTPServers) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) suite.Destroy(cfg) ctest.AssertNoResource[*network.TimeServerSpec](suite, "configuration/timeservers", rtestutils.WithNamespace(network.ConfigNamespaceName)) } func TestTimeServerConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &TimeServerConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides controllers which manage network resources. package network import ( "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NewTimeServerMergeController initializes a TimeServerMergeController. // // TimeServerMergeController merges network.TimeServerSpec in network.ConfigNamespace and produces final network.TimeServerSpec in network.Namespace. func NewTimeServerMergeController() controller.Controller { return GenericMergeController( network.ConfigNamespaceName, network.NamespaceName, func(logger *zap.Logger, list safe.List[*network.TimeServerSpec]) map[resource.ID]*network.TimeServerSpecSpec { // simply merge by layers, overriding with the next configuration layer var final network.TimeServerSpecSpec for spec := range list.All() { if final.NTPServers != nil && spec.TypedSpec().ConfigLayer < final.ConfigLayer { // skip this spec, as existing one is higher layer continue } if spec.TypedSpec().ConfigLayer == final.ConfigLayer { // merge server lists on the same level final.NTPServers = append(final.NTPServers, spec.TypedSpec().NTPServers...) } else { // otherwise, replace the lists final = *spec.TypedSpec() } } if final.NTPServers != nil { return map[resource.ID]*network.TimeServerSpecSpec{ network.TimeServerID: &final, } } return nil }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type TimeServerMergeSuite struct { ctest.DefaultSuite } func (suite *TimeServerMergeSuite) assertTimeServers( requiredIDs []string, check func(*network.TimeServerSpec, *assert.Assertions), ) { ctest.AssertResources(suite, requiredIDs, check) } func (suite *TimeServerMergeSuite) TestMerge() { def := network.NewTimeServerSpec(network.ConfigNamespaceName, "default/timeservers") *def.TypedSpec() = network.TimeServerSpecSpec{ NTPServers: []string{constants.DefaultNTPServer}, ConfigLayer: network.ConfigDefault, } dhcp1 := network.NewTimeServerSpec(network.ConfigNamespaceName, "dhcp/eth0") *dhcp1.TypedSpec() = network.TimeServerSpecSpec{ NTPServers: []string{"ntp.eth0"}, ConfigLayer: network.ConfigOperator, } dhcp2 := network.NewTimeServerSpec(network.ConfigNamespaceName, "dhcp/eth1") *dhcp2.TypedSpec() = network.TimeServerSpecSpec{ NTPServers: []string{"ntp.eth1"}, ConfigLayer: network.ConfigOperator, } static := network.NewTimeServerSpec(network.ConfigNamespaceName, "configuration/timeservers") *static.TypedSpec() = network.TimeServerSpecSpec{ NTPServers: []string{"my.ntp"}, ConfigLayer: network.ConfigMachineConfiguration, } for _, res := range []resource.Resource{def, dhcp1, dhcp2, static} { suite.Create(res) } suite.assertTimeServers( []string{ "timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal(*static.TypedSpec(), *r.TypedSpec()) }, ) suite.Destroy(static) suite.assertTimeServers( []string{ "timeservers", }, func(r *network.TimeServerSpec, asrt *assert.Assertions) { asrt.Equal([]string{"ntp.eth0", "ntp.eth1"}, r.TypedSpec().NTPServers) }, ) } func TestTimeServerMergeSuite(t *testing.T) { t.Parallel() suite.Run(t, &TimeServerMergeSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(netctrl.NewTimeServerMergeController())) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // TimeServerSpecController applies network.TimeServerSpec to the actual interfaces. type TimeServerSpecController struct{} // Name implements controller.Controller interface. func (ctrl *TimeServerSpecController) Name() string { return "network.TimeServerSpecController" } // Inputs implements controller.Controller interface. func (ctrl *TimeServerSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.TimeServerSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *TimeServerSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: network.TimeServerStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *TimeServerSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // as there's nothing to do actually apply time servers, simply copy spec to status // list source network configuration resources list, err := r.List(ctx, resource.NewMetadata(network.NamespaceName, network.TimeServerSpecType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error listing source network addresses: %w", err) } // add finalizers for all live resources for _, res := range list.Items { if res.Metadata().Phase() != resource.PhaseRunning { continue } if err = r.AddFinalizer(ctx, res.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer: %w", err) } } // loop over specs and sync to statuses for _, res := range list.Items { spec := res.(*network.TimeServerSpec) //nolint:forcetypeassert switch spec.Metadata().Phase() { case resource.PhaseTearingDown: if err = r.Destroy(ctx, resource.NewMetadata(network.NamespaceName, network.TimeServerStatusType, spec.Metadata().ID(), resource.VersionUndefined)); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying status: %w", err) } if err = r.RemoveFinalizer(ctx, spec.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer: %w", err) } case resource.PhaseRunning: ntps := make([]string, len(spec.TypedSpec().NTPServers)) for i := range ntps { ntps[i] = spec.TypedSpec().NTPServers[i] } logger.Info("setting time servers", zap.Strings("addresses", ntps)) if err = safe.WriterModify(ctx, r, network.NewTimeServerStatus(network.NamespaceName, spec.Metadata().ID()), func(status *network.TimeServerStatus) error { status.TypedSpec().NTPServers = spec.TypedSpec().NTPServers return nil }); err != nil { return fmt.Errorf("error modifying status: %w", err) } } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/timeserver_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" netctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) type TimeServerSpecSuite struct { ctest.DefaultSuite } func (suite *TimeServerSpecSuite) TestSpec() { spec := network.NewTimeServerSpec(network.NamespaceName, "timeservers") *spec.TypedSpec() = network.TimeServerSpecSpec{ NTPServers: []string{constants.DefaultNTPServer}, ConfigLayer: network.ConfigDefault, } suite.Create(spec) ctest.AssertResource( suite, "timeservers", func(status *network.TimeServerStatus, asrt *assert.Assertions) { asrt.Equal([]string{constants.DefaultNTPServer}, status.TypedSpec().NTPServers) }, ) } func TestTimeServerSpecSuite(t *testing.T) { t.Parallel() suite.Run(t, &TimeServerSpecSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&netctrl.TimeServerSpecController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/network/utils/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package networkutils provides utilities for controllers to interact with network resources. package networkutils import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // WaitForNetworkReady waits for devices to be ready. // // It is a helper function for controllers. func WaitForNetworkReady(ctx context.Context, r controller.Runtime, condition func(*network.StatusSpec) bool, nextInputs []controller.Input) error { // set inputs temporarily to a service only if err := r.UpdateInputs([]controller.Input{ { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return ctx.Err() case <-r.EventCh(): } status, err := safe.ReaderGetByID[*network.Status](ctx, r, network.StatusID) if err != nil { if state.IsNotFoundError(err) { continue } return err } if condition(status.TypedSpec()) { // condition met break } } // restore inputs if err := r.UpdateInputs(nextInputs); err != nil { return err } // queue an update to reprocess with new inputs r.QueueReconcile() return nil } ================================================ FILE: internal/app/machined/pkg/controllers/network/watch/ethtool.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package watch import ( "errors" "fmt" "sync" "github.com/mdlayher/genetlink" "golang.org/x/sys/unix" ) type ethtoolWatcher struct { wg sync.WaitGroup conn *genetlink.Conn } // NewEthtool starts ethtool watch. func NewEthtool(trigger Trigger) (Watcher, error) { watcher := ðtoolWatcher{} var err error watcher.conn, err = genetlink.Dial(nil) if err != nil { return nil, fmt.Errorf("error dialing ethtool watch socket: %w", err) } ethFamily, err := watcher.conn.GetFamily(unix.ETHTOOL_GENL_NAME) if err != nil { return nil, fmt.Errorf("error getting family information for ethtool: %w", err) } var monitorID uint32 for _, g := range ethFamily.Groups { if g.Name == unix.ETHTOOL_MCGRP_MONITOR_NAME { monitorID = g.ID break } } if monitorID == 0 { return nil, errors.New("could not find monitor multicast group ID for ethtool") } if err = watcher.conn.JoinGroup(monitorID); err != nil { return nil, fmt.Errorf("error joing multicast group for ethtool: %w", err) } watcher.wg.Go(func() { for { _, _, watchErr := watcher.conn.Receive() if watchErr != nil { return } trigger.QueueReconcile() } }) return watcher, nil } func (watcher *ethtoolWatcher) Done() { watcher.conn.Close() //nolint:errcheck watcher.wg.Wait() } ================================================ FILE: internal/app/machined/pkg/controllers/network/watch/rtnetlink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package watch import ( "fmt" "sync" "github.com/jsimonetti/rtnetlink/v2" "github.com/mdlayher/netlink" ) type rtnetlinkWatcher struct { wg sync.WaitGroup conn *rtnetlink.Conn } // NewRtNetlink starts rtnetlink watch over specified groups. func NewRtNetlink(trigger Trigger, groups uint32, netlinkGroups ...uint32) (Watcher, error) { watcher := &rtnetlinkWatcher{} var err error watcher.conn, err = rtnetlink.Dial(&netlink.Config{ Groups: groups, }) if err != nil { return nil, fmt.Errorf("error dialing watch socket: %w", err) } for _, group := range netlinkGroups { if err := watcher.conn.JoinGroup(group); err != nil { return nil, fmt.Errorf("error joining group %d: %w", group, err) } } watcher.wg.Go(func() { for { _, _, watchErr := watcher.conn.Receive() if watchErr != nil { return } trigger.QueueReconcile() } }) return watcher, nil } func (watcher *rtnetlinkWatcher) Done() { watcher.conn.Close() //nolint:errcheck watcher.wg.Wait() } ================================================ FILE: internal/app/machined/pkg/controllers/network/watch/trigger.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package watch import ( "context" "golang.org/x/time/rate" ) // RateLimitedTrigger wraps a Trigger with rate limiting. type RateLimitedTrigger struct { trigger Trigger limiter *rate.Limiter ch chan struct{} } // Interface check. var _ Trigger = &RateLimitedTrigger{} // NewRateLimitedTrigger creates a new RateLimitedTrigger with specified params. // // Trigger's goroutine exists when the context is canceled. func NewRateLimitedTrigger(ctx context.Context, trigger Trigger, rateLimit rate.Limit, burst int) *RateLimitedTrigger { t := &RateLimitedTrigger{ trigger: trigger, limiter: rate.NewLimiter(rateLimit, burst), ch: make(chan struct{}), } go t.run(ctx) return t } // NewDefaultRateLimitedTrigger creates a new RateLimitedTrigger with default params. func NewDefaultRateLimitedTrigger(ctx context.Context, trigger Trigger) *RateLimitedTrigger { const ( defaultRate = 10 // 10 events per second defaultBurst = 5 // 5 events ) return NewRateLimitedTrigger(ctx, trigger, defaultRate, defaultBurst) } // QueueReconcile implements Trigger interface. // // The event is queued if the goroutine is ready to accept it (otherwise it's already // busy processing a previous event). // This function returns immediately. func (t *RateLimitedTrigger) QueueReconcile() { select { case t.ch <- struct{}{}: default: } } func (t *RateLimitedTrigger) run(ctx context.Context) { for { select { case <-ctx.Done(): return case <-t.ch: } if err := t.limiter.Wait(ctx); err != nil { return } t.trigger.QueueReconcile() } } ================================================ FILE: internal/app/machined/pkg/controllers/network/watch/trigger_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package watch_test import ( "context" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/watch" ) type mockTrigger struct { count atomic.Int64 } func (t *mockTrigger) QueueReconcile() { t.count.Add(1) } func (t *mockTrigger) Get() int64 { return t.count.Load() } func TestRateLimitedTrigger(t *testing.T) { mock := &mockTrigger{} ctx, cancel := context.WithCancel(t.Context()) t.Cleanup(cancel) trigger := watch.NewRateLimitedTrigger(ctx, mock, 10, 5) start := time.Now() for time.Since(start) < time.Second { trigger.QueueReconcile() } assert.InDelta(t, int64(14), mock.Get(), 5) } ================================================ FILE: internal/app/machined/pkg/controllers/network/watch/watch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package watch provides netlink watchers via multicast groups. package watch // Watcher interface allows to stop watching. type Watcher interface { Done() } // Trigger is used by watcher to trigger reconcile loops. type Trigger interface { QueueReconcile() } ================================================ FILE: internal/app/machined/pkg/controllers/perf/perf.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import ( "context" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/prometheus/procfs" "go.uber.org/zap" perfadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/perf" "github.com/siderolabs/talos/pkg/machinery/resources/perf" ) const updateInterval = time.Second * 30 // StatsController manages v1alpha1.Stats which is the current snaphot of the machine CPU and Memory consumption. type StatsController struct{} // Name implements controller.StatsController interface. func (ctrl *StatsController) Name() string { return "perf.StatsController" } // Inputs implements controller.StatsController interface. func (ctrl *StatsController) Inputs() []controller.Input { return nil } // Outputs implements controller.StatsController interface. func (ctrl *StatsController) Outputs() []controller.Output { return []controller.Output{ { Type: perf.CPUType, Kind: controller.OutputExclusive, }, { Type: perf.MemoryType, Kind: controller.OutputExclusive, }, } } // Run implements controller.StatsController interface. func (ctrl *StatsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { ticker := time.NewTicker(updateInterval) defer ticker.Stop() var ( fs procfs.FS err error ) fs, err = procfs.NewDefaultFS() if err != nil { return err } for { select { case <-r.EventCh(): case <-ctx.Done(): return nil case <-ticker.C: } if err := ctrl.updateMemory(ctx, r, &fs); err != nil { return err } if err := ctrl.updateCPU(ctx, r, &fs); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *StatsController) updateCPU(ctx context.Context, r controller.Runtime, fs *procfs.FS) error { cpu := perf.NewCPU() stat, err := fs.Stat() if err != nil { return err } return safe.WriterModify(ctx, r, cpu, func(r *perf.CPU) error { perfadapter.CPU(r).Update(&stat) return nil }) } func (ctrl *StatsController) updateMemory(ctx context.Context, r controller.Runtime, fs *procfs.FS) error { mem := perf.NewMemory() info, err := fs.Meminfo() if err != nil { return err } return safe.WriterModify(ctx, r, mem, func(r *perf.Memory) error { perfadapter.Memory(r).Update(&info) return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/perf/perf_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf_test import ( "context" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/perf" perfresource "github.com/siderolabs/talos/pkg/machinery/resources/perf" ) type PerfSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup //nolint:containedctx ctx context.Context ctxCancel context.CancelFunc } func (suite *PerfSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) } func (suite *PerfSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *PerfSuite) TestReconcile() { suite.Require().NoError(suite.runtime.RegisterController(&perf.StatsController{})) suite.startRuntime() suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { cpu, err := suite.state.Get( suite.ctx, resource.NewMetadata( perfresource.NamespaceName, perfresource.CPUType, perfresource.CPUID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } mem, err := suite.state.Get( suite.ctx, resource.NewMetadata( perfresource.NamespaceName, perfresource.MemoryType, perfresource.MemoryID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } cpuSpec := cpu.(*perfresource.CPU).TypedSpec() memSpec := mem.(*perfresource.Memory).TypedSpec() if len(cpuSpec.CPU) == 0 || memSpec.MemTotal == 0 { return retry.ExpectedErrorf("cpu spec does not contain any CPU or Total memory is zero") } return nil }, ), ) } func (suite *PerfSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func TestPerfSuite(t *testing.T) { suite.Run(t, new(PerfSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/api_service_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // APIServiceConfigController provides apid service configuration. type APIServiceConfigController struct{} // Name implements controller.Controller interface. func (ctrl *APIServiceConfigController) Name() string { return "runtime.APIServiceConfigController" } // Inputs implements controller.Controller interface. func (ctrl *APIServiceConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MaintenanceServiceRequestType, ID: optional.Some(runtime.MaintenanceServiceRequestID), Kind: controller.InputStrong, }, { Namespace: runtime.NamespaceName, Type: runtime.MaintenanceServiceConfigType, ID: optional.Some(runtime.MaintenanceServiceConfigID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.APIType, ID: optional.Some(secrets.APIID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *APIServiceConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.APIServiceConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *APIServiceConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } request, err := safe.ReaderGetByID[*runtime.MaintenanceServiceRequest](ctx, r, runtime.MaintenanceServiceRequestID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get maintenance service request: %w", err) } if request != nil && request.Metadata().Phase() == resource.PhaseTearingDown { // remove the finalizer if err = r.RemoveFinalizer(ctx, request.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to remove finalizer: %w", err) } request = nil } // immediately add a finalizer if request != nil { if err = r.AddFinalizer(ctx, request.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("failed to add finalizer: %w", err) } } cfg, err := safe.ReaderGetByID[*runtime.MaintenanceServiceConfig](ctx, r, runtime.MaintenanceServiceConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get maintenance service config: %w", err) } cert, err := safe.ReaderGetByID[*secrets.API](ctx, r, secrets.APIID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get API secret: %w", err) } r.StartTrackingOutputs() // decide whether to create maintenance mode API or not if request != nil { if cfg != nil { if err = safe.WriterModify(ctx, r, runtime.NewAPIServiceConfig(), func(r *runtime.APIServiceConfig) error { r.TypedSpec().ListenAddress = cfg.TypedSpec().ListenAddress r.TypedSpec().NodeRoutingDisabled = true r.TypedSpec().ReadonlyRoleMode = true r.TypedSpec().SkipVerifyingClientCert = true return nil }, ); err != nil { return fmt.Errorf("failed to create API service config: %w", err) } } } else if cert != nil && !cert.TypedSpec().SkipVerifyingClientCert { if err = safe.WriterModify(ctx, r, runtime.NewAPIServiceConfig(), func(r *runtime.APIServiceConfig) error { r.TypedSpec().ListenAddress = fmt.Sprintf(":%d", constants.ApidPort) r.TypedSpec().NodeRoutingDisabled = false r.TypedSpec().ReadonlyRoleMode = false r.TypedSpec().SkipVerifyingClientCert = false return nil }, ); err != nil { return fmt.Errorf("failed to create API service config: %w", err) } } if err = safe.CleanupOutputs[*runtime.APIServiceConfig](ctx, r); err != nil { return fmt.Errorf("failed to cleanup API service config outputs: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/api_service_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) type APIServiceConfigControllerSuite struct { ctest.DefaultSuite } func TestAPIServiceConfigControllerSuite(t *testing.T) { t.Parallel() suite.Run(t, &APIServiceConfigControllerSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&runtime.APIServiceConfigController{})) }, }, }) } func (suite *APIServiceConfigControllerSuite) TestMaintenanceMode() { request := runtimeres.NewMaintenanceServiceRequest() suite.Create(request) ctest.AssertResource(suite, runtimeres.MaintenanceServiceRequestID, func(req *runtimeres.MaintenanceServiceRequest, asrt *assert.Assertions) { asrt.False(req.Metadata().Finalizers().Empty()) }, ) cfg := runtimeres.NewMaintenanceServiceConfig() cfg.TypedSpec().ListenAddress = ":1" suite.Create(cfg) ctest.AssertResource(suite, runtimeres.APIServiceConfigID, func(cfg *runtimeres.APIServiceConfig, asrt *assert.Assertions) { asrt.Equal(":1", cfg.TypedSpec().ListenAddress) asrt.True(cfg.TypedSpec().NodeRoutingDisabled) asrt.True(cfg.TypedSpec().ReadonlyRoleMode) asrt.True(cfg.TypedSpec().SkipVerifyingClientCert) }, ) _, err := suite.State().Teardown(suite.Ctx(), request.Metadata()) suite.Require().NoError(err) ctest.AssertNoResource[*runtimeres.APIServiceConfig](suite, runtimeres.APIServiceConfigID) ctest.AssertResource(suite, runtimeres.MaintenanceServiceRequestID, func(req *runtimeres.MaintenanceServiceRequest, asrt *assert.Assertions) { asrt.True(req.Metadata().Finalizers().Empty()) }, ) suite.Destroy(request) } func (suite *APIServiceConfigControllerSuite) TestRegularMode() { cert := secrets.NewAPI() suite.Create(cert) ctest.AssertResource(suite, runtimeres.APIServiceConfigID, func(cfg *runtimeres.APIServiceConfig, asrt *assert.Assertions) { asrt.Equal(":50000", cfg.TypedSpec().ListenAddress) asrt.False(cfg.TypedSpec().NodeRoutingDisabled) asrt.False(cfg.TypedSpec().ReadonlyRoleMode) asrt.False(cfg.TypedSpec().SkipVerifyingClientCert) }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/booted_entry.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // BootedEntryController is a controller that updates the booted entry resource. type BootedEntryController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *BootedEntryController) Name() string { return "runtime.BootedEntryController" } // Inputs implements controller.Controller interface. func (ctrl *BootedEntryController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: runtimeres.SecurityStateType, ID: optional.Some(runtimeres.SecurityStateID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *BootedEntryController) Outputs() []controller.Output { return []controller.Output{ { Type: runtimeres.BootedEntryType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *BootedEntryController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // If we're booted in Container mode, short-circuit the controller. if ctrl.V1Alpha1Mode == machineruntime.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // wait for the SecurityState resource to be created st, err := safe.ReaderGetByID[*runtimeres.SecurityState](ctx, r, runtimeres.SecurityStateID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get machined state: %w", err) } // If we're not booted with UKI, we don't need to create the BootedEntry resource. // This is because the BootedEntry resource is only relevant when UKI is used with systemd-boot. if !st.TypedSpec().BootedWithUKI { return nil } // Read `LoaderEntryOneShot`, `LoaderEntryRebootReason`, `LoaderEntrySelected` and `LoaderEntryBooted` resources // to determine the booted entry. loaderEntryOneShot, err := sdboot.ReadVariable(sdboot.LoaderEntryOneShotName) if err != nil { return fmt.Errorf("failed to read LoaderEntryOneShot variable: %w", err) } loaderEntryRebootReason, err := sdboot.ReadVariable(sdboot.LoaderEntryRebootReasonName) if err != nil { return fmt.Errorf("failed to read LoaderEntryRebootReason variable: %w", err) } loaderEntrySelected, err := sdboot.ReadVariable(sdboot.LoaderEntrySelectedName) if err != nil { return fmt.Errorf("failed to read LoaderEntrySelected variable: %w", err) } loaderEntryDefault, err := sdboot.ReadVariable(sdboot.LoaderEntryDefaultName) if err != nil { return fmt.Errorf("failed to read LoaderEntryDefault variable: %w", err) } var bootedEntry string switch { // in this case `LoaderEntryOneShot` is set to "kexec reboot" and `LoaderEntryRebootReason` is set to "reboot" // by the kernel, the system was installed/upgraded via kexec and `LoaderEntryDefault` is the correct booted entry // Ref: https://cateee.net/lkddb/web-lkddb/EFI_BOOTLOADER_CONTROL.html case loaderEntryRebootReason == "reboot" && loaderEntryOneShot == "kexec reboot": if loaderEntryDefault == "" { return fmt.Errorf("LoaderEntryDefault variable is empty, cannot determine booted entry") } bootedEntry = loaderEntryDefault // this case is when we have a `LoaderEntryDefault` set by the installer and during a reboot the user selected // a different entry, so we set the `LoaderEntrySelected` as the booted entry // we can use this information later to decide which UKI's to clean up case loaderEntryOneShot == "" && loaderEntryDefault != "" && loaderEntrySelected != "": bootedEntry = loaderEntrySelected // this case is when we have a `LoaderEntryDefault` set by the installer and the system was rebooted/upgraded // with kexec, so `sd-boot` is not involved and nothing sets the `LoaderEntrySelected` case loaderEntryOneShot == "" && loaderEntryDefault != "" && loaderEntrySelected == "": bootedEntry = loaderEntryDefault // this is the case when we just booted with UKI/kernel+initrd and bootloader is not installed // this case is only currently applicable when locally developing Talos case loaderEntryOneShot == "" && loaderEntryDefault == "" && loaderEntrySelected != "": bootedEntry = loaderEntrySelected } if err := safe.WriterModify(ctx, r, runtimeres.NewBootedEntrySpec(), func(entry *runtimeres.BootedEntry) error { entry.TypedSpec().BootedEntry = bootedEntry return nil }); err != nil { return fmt.Errorf("failed to update BootedEntry resource: %w", err) } // terminating the controller here, as we need to only populate the BootedEntry resource once return nil } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/common_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "errors" "sync" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" ) const ( fsFileMax = "fs.file-max" procSysfsFileMax = "proc.sys.fs.file-max" sysfsFileMax = "sys.fs.file-max" ) type RuntimeSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *RuntimeSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) } func (suite *RuntimeSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *RuntimeSuite) assertResource(md resource.Metadata, compare func(res resource.Resource) bool) func() error { return func() error { r, err := suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } if !compare(r) { return errors.New("resource is not equal to the expected one") } return nil } } func (suite *RuntimeSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/devices_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/v1alpha1" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // DevicesStatusController loads extensions.yaml and updates DevicesStatus resources. type DevicesStatusController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *DevicesStatusController) Name() string { return "runtime.DevicesStatusController" } // Inputs implements controller.Controller interface. func (ctrl *DevicesStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *DevicesStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.DevicesStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *DevicesStatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // in container mode, devices are always ready if ctrl.V1Alpha1Mode != machineruntime.ModeContainer { if err := v1alpha1.WaitForServiceHealthy(ctx, r, "udevd", nil); err != nil { return err } } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if err := safe.WriterModify(ctx, r, runtime.NewDevicesStatus(runtime.NamespaceName, runtime.DevicesID), func(status *runtime.DevicesStatus) error { status.TypedSpec().Ready = true return nil }); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/diagnostics.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/diagnostics" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // DiagnosticsController analyzes state of Talos Linux system and provides warnings on common problems. type DiagnosticsController struct{} // Name implements controller.Controller interface. func (ctrl *DiagnosticsController) Name() string { return "runtime.DiagnosticsController" } // Inputs implements controller.Controller interface. func (ctrl *DiagnosticsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.NodeAddressType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DiagnosticsController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.DiagnosticType, Kind: controller.OutputExclusive, }, } } const ( diagnosticsCheckTimeout = time.Minute diagnostricsCheckInterval = time.Minute ) // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DiagnosticsController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // firstDiscovery is used to track when a warning was first discovered. firstDiscovered := map[string]time.Time{} ticker := time.NewTicker(diagnostricsCheckInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ticker.C: } r.StartTrackingOutputs() for _, checkDescription := range diagnostics.Checks() { if err := func() error { checkCtx, checkCtxCancel := context.WithTimeout(ctx, diagnosticsCheckTimeout) defer checkCtxCancel() warning, err := checkDescription.Check(checkCtx, r, logger) if err != nil { logger.Debug("diagnostic check failed", zap.String("check", checkDescription.ID), zap.Error(err)) return nil } if warning == nil { delete(firstDiscovered, checkDescription.ID) return nil } firstDiscoveredTime, ok := firstDiscovered[checkDescription.ID] if !ok { firstDiscoveredTime = time.Now() firstDiscovered[checkDescription.ID] = firstDiscoveredTime } if time.Since(firstDiscoveredTime) < checkDescription.Hysteresis { // don't publish it yet return nil } return safe.WriterModify(ctx, r, runtime.NewDiagnostic(runtime.NamespaceName, checkDescription.ID), func(res *runtime.Diagnostic) error { *res.TypedSpec() = *warning return nil }) }(); err != nil { return err } } if err := safe.CleanupOutputs[*runtime.Diagnostic](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/diagnostics_logger.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // DiagnosticsLoggerController logs warnings generated by DiagnosticsController. type DiagnosticsLoggerController struct{} // Name implements controller.Controller interface. func (ctrl *DiagnosticsLoggerController) Name() string { return "runtime.DiagnosticsLoggerController" } // Inputs implements controller.Controller interface. func (ctrl *DiagnosticsLoggerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.DiagnosticType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DiagnosticsLoggerController) Outputs() []controller.Output { return nil } const diagnosticsReportInterval = 5 * time.Minute // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DiagnosticsLoggerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { reportedWarnings := map[string]struct{}{} ticker := time.NewTicker(diagnosticsReportInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): warnings, err := safe.ReaderListAll[*runtime.Diagnostic](ctx, r) if err != nil { return fmt.Errorf("error listing diagnostics: %w", err) } seenWarnings := map[string]struct{}{} for warning := range warnings.All() { seenWarnings[warning.Metadata().ID()] = struct{}{} if _, reported := reportedWarnings[warning.Metadata().ID()]; !reported { logger.Warn("new diagnostic", zap.String("id", warning.Metadata().ID()), zap.String("message", warning.TypedSpec().Message), zap.Strings("details", warning.TypedSpec().Details), zap.String("url", warning.TypedSpec().DocumentationURL(warning.Metadata().ID())), ) reportedWarnings[warning.Metadata().ID()] = struct{}{} } } for id := range reportedWarnings { if _, seen := seenWarnings[id]; !seen { logger.Info("diagnostic resolved", zap.String("id", id)) delete(reportedWarnings, id) } } case <-ticker.C: if len(reportedWarnings) == 0 { continue } warnings, err := safe.ReaderListAll[*runtime.Diagnostic](ctx, r) if err != nil { return fmt.Errorf("error listing diagnostics: %w", err) } for warning := range warnings.All() { logger.Warn("diagnostic still active", zap.String("id", warning.Metadata().ID()), zap.String("message", warning.TypedSpec().Message), zap.Strings("details", warning.TypedSpec().Details), zap.String("url", warning.TypedSpec().DocumentationURL(warning.Metadata().ID())), ) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // MetaProvider wraps acquiring meta. type MetaProvider interface { Meta() machineruntime.Meta } // DropUpgradeFallbackController removes upgrade fallback key once machine reaches ready & running. type DropUpgradeFallbackController struct { MetaProvider MetaProvider } // Name implements controller.Controller interface. func (ctrl *DropUpgradeFallbackController) Name() string { return "runtime.DropUpgradeFallbackController" } // Inputs implements controller.Controller interface. func (ctrl *DropUpgradeFallbackController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MachineStatusType, ID: optional.Some(runtime.MachineStatusID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *DropUpgradeFallbackController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *DropUpgradeFallbackController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } machineStatus, err := safe.ReaderGetByID[*runtime.MachineStatus](ctx, r, runtime.MachineStatusID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting machine status: %w", err) } if !(machineStatus.TypedSpec().Stage == runtime.MachineStageRunning && machineStatus.TypedSpec().Status.Ready) { continue } ok, err := ctrl.MetaProvider.Meta().DeleteTag(ctx, meta.Upgrade) if err != nil { return err } if ok { logger.Info("removing fallback entry") if err = ctrl.MetaProvider.Meta().Flush(); err != nil { return err } } // terminating the controller here, as removing fallback is required only once on boot after upgrade return nil } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/drop_upgrade_fallback_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "os" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/meta" metaconsts "github.com/siderolabs/talos/pkg/machinery/meta" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type DropUpgradeFallbackControllerSuite struct { ctest.DefaultSuite meta *meta.Meta } type metaProvider struct { meta *meta.Meta } func (m metaProvider) Meta() machineruntime.Meta { return m.meta } func TestUpgradeFallbackControllerSuite(t *testing.T) { tmpDir := t.TempDir() path := filepath.Join(tmpDir, "meta") f, err := os.Create(path) require.NoError(t, err) require.NoError(t, f.Truncate(1024*1024)) require.NoError(t, f.Close()) st := state.WrapCore(namespaced.NewState(inmem.Build)) m, err := meta.New(t.Context(), st, meta.WithFixedPath(path)) require.NoError(t, err) suite.Run(t, &DropUpgradeFallbackControllerSuite{ meta: m, DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&runtime.DropUpgradeFallbackController{ MetaProvider: metaProvider{meta: m}, })) }, }, }) } func (suite *DropUpgradeFallbackControllerSuite) TestDropUpgradeFallback() { _, err := suite.meta.SetTag(suite.Ctx(), metaconsts.Upgrade, "A") suite.Require().NoError(err) machineStatus := runtimeres.NewMachineStatus() machineStatus.TypedSpec().Stage = runtimeres.MachineStageBooting machineStatus.TypedSpec().Status.Ready = false suite.Require().NoError(suite.State().Create(suite.Ctx(), machineStatus)) time.Sleep(time.Second) // controller should not remove the tag val, ok := suite.meta.ReadTag(metaconsts.Upgrade) suite.Require().True(ok) suite.Require().Equal("A", val) // update machine status to ready machineStatus.TypedSpec().Status.Ready = true machineStatus.TypedSpec().Stage = runtimeres.MachineStageRunning suite.Require().NoError(suite.State().Update(suite.Ctx(), machineStatus)) suite.AssertWithin(time.Second, 10*time.Millisecond, func() error { _, ok = suite.meta.ReadTag(metaconsts.Upgrade) if ok { return retry.ExpectedErrorf("tag is still present") } return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "os" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // EnvironmentController watches v1alpha1.Config and sets environment variables accordingly. type EnvironmentController struct{} // Name implements controller.Controller interface. func (ctrl *EnvironmentController) Name() string { return "runtime.EnvironmentController" } // Inputs implements controller.Controller interface. func (ctrl *EnvironmentController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), }, } } // Outputs implements controller.Controller interface. func (ctrl *EnvironmentController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.EnvironmentType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EnvironmentController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } r.StartTrackingOutputs() if cfg != nil && cfg.Config().Environment() != nil { for key, value := range cfg.Config().Environment().Variables() { if err := os.Setenv(key, value); err != nil { return fmt.Errorf("error setting env var: \"%s=%s\": %w", key, value, err) } } } item := runtime.NewEnvironment("machined") if err = safe.WriterModify(ctx, r, item, func(res *runtime.Environment) error { env := os.Environ() slices.Sort(env) res.TypedSpec().Variables = env return nil }); err != nil { return err } if err = safe.CleanupOutputs[*runtime.Environment](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/environment_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type EnvironmentSuite struct { ctest.DefaultSuite } func TestEnvironmentSuite(t *testing.T) { suite.Run(t, new(EnvironmentSuite)) } func (suite *EnvironmentSuite) TestEnvironmentNone() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.EnvironmentController{})) rtestutils.AssertResource[*runtime.Environment](suite.Ctx(), suite.T(), suite.State(), "machined", func(r *runtime.Environment, asrt *assert.Assertions) { asrt.NotEmpty(r.TypedSpec().Variables) }) } func (suite *EnvironmentSuite) TestEnvironmentMachineConfig() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.EnvironmentController{})) cfg, err := container.New(&runtimecfg.EnvironmentV1Alpha1{ EnvironmentVariables: map[string]string{ "TEST": "value", }, }) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources[*runtime.Environment](suite.Ctx(), suite.T(), suite.State(), []resource.ID{"machined"}, func(cfg *runtime.Environment, asrt *assert.Assertions) { asrt.Contains( cfg.TypedSpec().Variables, "TEST=value", ) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/events_sink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/rs/xid" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/optional" "github.com/siderolabs/siderolink/api/events" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/protobuf/types/known/anypb" networkutils "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/utils" machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/client/dialer" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // EventsSinkController watches events and forwards them to the events sink server // if it's configured. type EventsSinkController struct { V1Alpha1Events machinedruntime.Watcher Drainer *machinedruntime.Drainer drainSub *machinedruntime.DrainSubscription eventID xid.ID } // Name implements controller.Controller interface. func (ctrl *EventsSinkController) Name() string { return "v1alpha1.EventsSinkController" } // Inputs implements controller.Controller interface. func (ctrl *EventsSinkController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *EventsSinkController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *EventsSinkController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { ctx, cancel := context.WithCancel(ctx) defer cancel() if ctrl.drainSub == nil { ctrl.drainSub = ctrl.Drainer.Subscribe() } defer func() { if ctrl.drainSub != nil { ctrl.drainSub.Cancel() } }() if err := networkutils.WaitForNetworkReady(ctx, r, func(status *network.StatusSpec) bool { return status.AddressReady }, []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.EventSinkConfigType, ID: optional.Some(runtime.EventSinkConfigID), Kind: controller.InputWeak, }, }, ); err != nil { return fmt.Errorf("error waiting for network: %w", err) } var ( conn *grpc.ClientConn client events.EventSinkServiceClient watchCh, consumeWatchCh chan machinedruntime.EventInfo backlog int draining bool ) defer func() { if conn != nil { conn.Close() //nolint:errcheck } }() for { select { case <-ctx.Done(): return nil case <-ctrl.drainSub.EventCh(): // drain started, return immediately if there's no backlog draining = true if backlog == 0 { return nil } case event := <-consumeWatchCh: // if consumeWatchCh is not nil, client connection was established backlog = event.Backlog data, err := anypb.New(event.Payload) if err != nil { return err } req := &events.EventRequest{ Id: event.ID.String(), Data: data, ActorId: event.ActorID, } _, err = client.Publish(ctx, req) if err != nil { return fmt.Errorf("error publishing event: %w", err) } // adjust last consumed event ctrl.eventID = event.ID // if draining and backlog is 0, return immediately if draining && backlog == 0 { return nil } case <-r.EventCh(): // configuration changed, re-establish connection cfg, err := safe.ReaderGetByID[*runtime.EventSinkConfig](ctx, r, runtime.EventSinkConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting event sink config: %w", err) } if conn != nil { logger.Debug("closing connection to event sink") conn.Close() //nolint:errcheck conn = nil client = nil consumeWatchCh = nil // stop consuming events backlog = 0 } if cfg == nil { // no config, no event streaming continue } // establish connection logger.Debug("establishing connection to event sink", zap.String("endpoint", cfg.TypedSpec().Endpoint)) conn, err = grpc.NewClient( cfg.TypedSpec().Endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithSharedWriteBuffer(true), grpc.WithContextDialer(dialer.DynamicProxyDialerWithTLSConfig(httpdefaults.RootCAsTLSConfig)), ) if err != nil { return fmt.Errorf("error establishing connection to event sink: %w", err) } client = events.NewEventSinkServiceClient(conn) // start watching events if we haven't already done so // // watch is only established with the first live connection to make sure we don't miss any events if watchCh == nil { watchCh = make(chan machinedruntime.EventInfo) var opts []machinedruntime.WatchOptionFunc if ctrl.eventID.IsNil() { opts = append(opts, machinedruntime.WithTailEvents(-1)) } else { opts = append(opts, machinedruntime.WithTailID(ctrl.eventID)) } // Watch returns immediately, setting up a goroutine which will copy events to `watchCh` if err = ctrl.V1Alpha1Events.Watch(func(eventCh <-chan machinedruntime.EventInfo) { for { select { case <-ctx.Done(): return case event := <-eventCh: if !channel.SendWithContext(ctx, watchCh, event) { return } } } }, opts...); err != nil { return err } } consumeWatchCh = watchCh } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/events_sink_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // EventsSinkConfigController generates configuration for kmsg log delivery. type EventsSinkConfigController struct { Cmdline *procfs.Cmdline V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *EventsSinkConfigController) Name() string { return "runtime.EventsSinkConfigController" } // Inputs implements controller.Controller interface. func (ctrl *EventsSinkConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EventsSinkConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.EventSinkConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EventsSinkConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) (err error) { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var endpoint string if ctrl.Cmdline != nil && ctrl.V1Alpha1Mode != v1alpha1runtime.ModeContainer { if val := ctrl.Cmdline.Get(constants.KernelParamEventsSink).First(); val != nil { endpoint = *val } } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } if cfg != nil && cfg.Config().Runtime().EventsEndpoint() != nil { endpoint = *cfg.Config().Runtime().EventsEndpoint() } r.StartTrackingOutputs() if endpoint != "" { if err = safe.WriterModify(ctx, r, runtime.NewEventSinkConfig(), func(cfg *runtime.EventSinkConfig) error { cfg.TypedSpec().Endpoint = endpoint return nil }); err != nil { return fmt.Errorf("error updating kmsg log config: %w", err) } } if err = safe.CleanupOutputs[*runtime.EventSinkConfig](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/events_sink_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type EventsSinkConfigSuite struct { ctest.DefaultSuite } func TestEventsSinkConfigSuite(t *testing.T) { suite.Run(t, new(EventsSinkConfigSuite)) } func (suite *EventsSinkConfigSuite) TestEventSinkConfigNone() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.EventsSinkConfigController{})) rtestutils.AssertNoResource[*runtime.EventSinkConfig](suite.Ctx(), suite.T(), suite.State(), runtime.EventSinkConfigID) } func (suite *EventsSinkConfigSuite) TestEventSinkConfigMachineConfig() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.EventsSinkConfigController{})) eventSinkConfig := &runtimecfg.EventSinkV1Alpha1{ Endpoint: "10.0.0.2:4444", } cfg, err := container.New(eventSinkConfig) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources[*runtime.EventSinkConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.EventSinkConfigID}, func(cfg *runtime.EventSinkConfig, asrt *assert.Assertions) { asrt.Equal( "10.0.0.2:4444", cfg.TypedSpec().Endpoint, ) }) } func (suite *EventsSinkConfigSuite) TestEventSinkConfigCmdline() { cmdline := procfs.NewCmdline("") cmdline.Append(constants.KernelParamEventsSink, "10.0.0.1:3333") cfg, err := container.New() suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.EventsSinkConfigController{ Cmdline: cmdline, })) rtestutils.AssertResources[*runtime.EventSinkConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.EventSinkConfigID}, func(cfg *runtime.EventSinkConfig, asrt *assert.Assertions) { asrt.Equal( "10.0.0.1:3333", cfg.TypedSpec().Endpoint, ) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/events_sink_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "net" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" eventsapi "github.com/siderolabs/siderolink/api/events" "github.com/siderolabs/siderolink/pkg/events" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" "golang.org/x/sync/errgroup" "google.golang.org/grpc" controllerruntime "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" talosruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type handler struct { eventsMu sync.Mutex events []events.Event } // HandleEvent implements events.Adapter. func (s *handler) HandleEvent(ctx context.Context, e events.Event) error { s.eventsMu.Lock() defer s.eventsMu.Unlock() s.events = append(s.events, e) return nil } type EventsSinkSuite struct { suite.Suite events *v1alpha1.Events state state.State handler *handler server *grpc.Server sink *events.Sink runtime *runtime.Runtime drainer *talosruntime.Drainer wg sync.WaitGroup eg errgroup.Group ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *EventsSinkSuite) SetupTest() { suite.events = v1alpha1.NewEvents(1000, 10) suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) suite.handler = &handler{} suite.drainer = talosruntime.NewDrainer() suite.Require().NoError( suite.runtime.RegisterController( &controllerruntime.EventsSinkController{ V1Alpha1Events: suite.events, Drainer: suite.drainer, }, ), ) status := network.NewStatus(network.NamespaceName, network.StatusID) status.TypedSpec().AddressReady = true suite.Require().NoError(suite.state.Create(suite.ctx, status)) suite.startRuntime() } func (suite *EventsSinkSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *EventsSinkSuite) startServer(ctx context.Context) string { suite.sink = events.NewSink( suite.handler, []proto.Message{ &machine.AddressEvent{}, &machine.PhaseEvent{}, }) lis, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "localhost:0") suite.Require().NoError(err) suite.server = grpc.NewServer() eventsapi.RegisterEventSinkServiceServer(suite.server, suite.sink) suite.eg.Go( func() error { <-ctx.Done() suite.server.Stop() return nil }, ) suite.eg.Go( func() error { return suite.server.Serve(lis) }, ) return lis.Addr().String() } func (suite *EventsSinkSuite) TestPublish() { ctx, cancel := context.WithCancel(suite.ctx) defer cancel() suite.events.Publish( ctx, &machine.AddressEvent{ Hostname: "localhost", }, ) suite.events.Publish( ctx, &machine.PhaseEvent{ Phase: "test", Action: machine.PhaseEvent_START, }, ) suite.Require().Equal(0, len(suite.handler.events)) endpoint := suite.startServer(ctx) config := runtimeres.NewEventSinkConfig() config.TypedSpec().Endpoint = endpoint suite.Require().NoError(suite.state.Create(ctx, config)) suite.Require().NoError(retry.Constant(time.Second*5, retry.WithUnits(time.Millisecond*100)).Retry( func() error { suite.handler.eventsMu.Lock() defer suite.handler.eventsMu.Unlock() if len(suite.handler.events) != 2 { return retry.ExpectedErrorf("expected 2 events, got %d", len(suite.handler.events)) } return nil }, )) suite.events.Publish( ctx, &machine.PhaseEvent{ Phase: "test", Action: machine.PhaseEvent_STOP, }, ) suite.Require().NoError(retry.Constant(time.Second*5, retry.WithUnits(time.Millisecond*100)).Retry( func() error { suite.handler.eventsMu.Lock() defer suite.handler.eventsMu.Unlock() if len(suite.handler.events) != 3 { return retry.ExpectedErrorf("expected 3 events, got %d", len(suite.handler.events)) } return nil }, )) } func (suite *EventsSinkSuite) TestDrain() { ctx, cancel := context.WithCancel(suite.ctx) defer cancel() for range 10 { suite.events.Publish( ctx, &machine.PhaseEvent{ Phase: "test", Action: machine.PhaseEvent_START, }, ) suite.events.Publish( ctx, &machine.PhaseEvent{ Phase: "test", Action: machine.PhaseEvent_STOP, }, ) } suite.Require().Equal(0, len(suite.handler.events)) // first, publish wrong endpoint badLis, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "localhost:0") suite.Require().NoError(err) badEndpoint := badLis.Addr().String() suite.Require().NoError(badLis.Close()) config := runtimeres.NewEventSinkConfig() config.TypedSpec().Endpoint = badEndpoint suite.Require().NoError(suite.state.Create(ctx, config)) suite.T().Logf("%s starting bad server at %s", time.Now().Format(time.RFC3339), badEndpoint) time.Sleep(time.Second * 1) drainCtx, drainCtxCancel := context.WithTimeout(ctx, time.Second*5) defer drainCtxCancel() var eg errgroup.Group eg.Go( func() error { suite.T().Logf("%s starting drain", time.Now().Format(time.RFC3339)) return suite.drainer.Drain(drainCtx) }, ) eg.Go( func() error { // start real server with delay time.Sleep(300 * time.Millisecond) endpoint := suite.startServer(ctx) suite.T().Logf("%s starting real server at %s", time.Now().Format(time.RFC3339), endpoint) _, updateErr := safe.StateUpdateWithConflicts( ctx, suite.state, runtimeres.NewEventSinkConfig().Metadata(), func(cfg *runtimeres.EventSinkConfig) error { cfg.TypedSpec().Endpoint = endpoint return nil }) return updateErr }, ) suite.Require().NoError(retry.Constant(time.Second*5, retry.WithUnits(time.Millisecond*100)).Retry( func() error { suite.handler.eventsMu.Lock() defer suite.handler.eventsMu.Unlock() if len(suite.handler.events) != 20 { return retry.ExpectedErrorf("expected 20 events, got %d", len(suite.handler.events)) } return nil }, )) suite.Require().NoError(eg.Wait()) } func (suite *EventsSinkSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.Require().NoError(suite.eg.Wait()) suite.wg.Wait() } func TestEventsSinkSuite(t *testing.T) { suite.Run(t, new(EventsSinkSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" extservices "github.com/siderolabs/talos/pkg/machinery/extensions/services" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ServiceManager is the interface to the v1alpha1 services subsystems. type ServiceManager interface { IsRunning(id string) (system.Service, bool, error) Load(services ...system.Service) []string Stop(ctx context.Context, serviceIDs ...string) (err error) Start(serviceIDs ...string) error } // ExtensionServiceController creates extension services based on the extension service configuration found in the rootfs. type ExtensionServiceController struct { V1Alpha1Services ServiceManager ConfigPath string configStatusCache map[string]string } // Name implements controller.Controller interface. func (ctrl *ExtensionServiceController) Name() string { return "runtime.ExtensionServiceController" } // Inputs implements controller.Controller interface. func (ctrl *ExtensionServiceController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.ExtensionServiceConfigStatusType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *ExtensionServiceController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *ExtensionServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { // wait for controller runtime to be ready select { case <-ctx.Done(): return nil case <-r.EventCh(): } // extensions loading only needs to run once, as services are static serviceFiles, err := os.ReadDir(ctrl.ConfigPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { // directory not present, skip completely logger.Debug("extension service directory is not found") return nil } return err } // load initial state of configStatuses if ctrl.configStatusCache == nil { configStatuses, err := safe.ReaderListAll[*runtime.ExtensionServiceConfigStatus](ctx, r) if err != nil { return fmt.Errorf("error listing extension services config: %w", err) } ctrl.configStatusCache = make(map[string]string, configStatuses.Len()) for res := range configStatuses.All() { ctrl.configStatusCache[res.Metadata().ID()] = res.TypedSpec().SpecVersion } } // load services from definitions into the service runner framework extServices := map[string]struct{}{} for _, serviceFile := range serviceFiles { if filepath.Ext(serviceFile.Name()) != ".yaml" { logger.Debug("skipping config file", zap.String("filename", serviceFile.Name())) continue } spec, err := ctrl.loadSpec(filepath.Join(ctrl.ConfigPath, serviceFile.Name())) if err != nil { logger.Error("error loading extension service spec", zap.String("filename", serviceFile.Name()), zap.Error(err)) continue } if err = spec.Validate(); err != nil { logger.Error("error validating extension service spec", zap.String("filename", serviceFile.Name()), zap.Error(err)) continue } if _, exists := extServices[spec.Name]; exists { logger.Error("duplicate service spec", zap.String("filename", serviceFile.Name()), zap.String("name", spec.Name)) continue } extServices[spec.Name] = struct{}{} svc := &services.Extension{ Spec: spec, } ctrl.V1Alpha1Services.Load(svc) if err = ctrl.V1Alpha1Services.Start(svc.ID(nil)); err != nil { return fmt.Errorf("error starting %q service: %w", spec.Name, err) } } // watch for changes in the configStatuses for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } configStatuses, err := safe.ReaderListAll[*runtime.ExtensionServiceConfigStatus](ctx, r) if err != nil { return fmt.Errorf("error listing extension services config: %w", err) } configStatusesPresent := map[string]struct{}{} for res := range configStatuses.All() { configStatusesPresent[res.Metadata().ID()] = struct{}{} if ctrl.configStatusCache[res.Metadata().ID()] == res.TypedSpec().SpecVersion { continue } if err = ctrl.handleRestart(ctx, logger, "ext-"+res.Metadata().ID(), res.TypedSpec().SpecVersion); err != nil { return err } ctrl.configStatusCache[res.Metadata().ID()] = res.TypedSpec().SpecVersion } // cleanup configStatusesCache for id := range ctrl.configStatusCache { if _, ok := configStatusesPresent[id]; !ok { if err = ctrl.handleRestart(ctx, logger, "ext-"+id, "nan"); err != nil { return err } delete(ctrl.configStatusCache, id) } } } } func (ctrl *ExtensionServiceController) loadSpec(path string) (extservices.Spec, error) { var spec extservices.Spec f, err := os.Open(path) if err != nil { return spec, err } defer f.Close() //nolint:errcheck if err = yaml.NewDecoder(f).Decode(&spec); err != nil { return spec, fmt.Errorf("error unmarshalling extension service config: %w", err) } return spec, nil } func (ctrl *ExtensionServiceController) handleRestart(ctx context.Context, logger *zap.Logger, svcName, specVersion string) error { _, running, err := ctrl.V1Alpha1Services.IsRunning(svcName) if err != nil { return nil //nolint:nilerr // IsRunning returns an error only if the service is not found, so ignore it } // this means it's a new config and the service runner is already waiting for the config to start the service // we don't need restart it again since it will be started automatically if running && specVersion == "1" { return nil } logger.Warn("extension service config changed, restarting", zap.String("service", svcName)) if running { if err = ctrl.V1Alpha1Services.Stop(ctx, svcName); err != nil { return fmt.Errorf("error stopping extension service %s: %w", svcName, err) } } if err = ctrl.V1Alpha1Services.Start(svcName); err != nil { return fmt.Errorf("error starting extension service %s: %w", svcName, err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" extconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ExtensionServiceConfigController watches v1alpha1.Config, creates/updates/deletes extension services config. type ExtensionServiceConfigController struct{} // Name implements controller.Controller interface. func (ctrl *ExtensionServiceConfigController) Name() string { return "runtime.ExtensionServiceConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ExtensionServiceConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ExtensionServiceConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.ExtensionServiceConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ExtensionServiceConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } r.StartTrackingOutputs() if cfg != nil && cfg.Config() != nil { for _, extConfig := range cfg.Config().ExtensionServiceConfigs() { if err = safe.WriterModify(ctx, r, runtime.NewExtensionServiceConfigSpec(runtime.NamespaceName, extConfig.Name()), func(spec *runtime.ExtensionServiceConfig) error { spec.TypedSpec().Files = xslices.Map(extConfig.ConfigFiles(), func(c extconfig.ExtensionServiceConfigFile) runtime.ExtensionServiceConfigFile { return runtime.ExtensionServiceConfigFile{ Content: c.Content(), MountPath: c.MountPath(), } }) spec.TypedSpec().Environment = extConfig.Environment() return nil }); err != nil { return err } } } if err = safe.CleanupOutputs[*runtime.ExtensionServiceConfig](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service_config_files.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "io/fs" "os" "path/filepath" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ExtensionServiceConfigFilesController writes down the config files for extension services. type ExtensionServiceConfigFilesController struct { V1Alpha1Mode v1alpha1runtime.Mode ExtensionsConfigBaseDir string } // Name implements controller.Controller interface. func (ctrl *ExtensionServiceConfigFilesController) Name() string { return "runtime.ExtensionServiceConfigFilesController" } // Inputs implements controller.Controller interface. func (ctrl *ExtensionServiceConfigFilesController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.ExtensionServiceConfigType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *ExtensionServiceConfigFilesController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.ExtensionServiceConfigStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ExtensionServiceConfigFilesController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { return nil } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } list, err := safe.ReaderListAll[*runtime.ExtensionServiceConfig](ctx, r) if err != nil { return fmt.Errorf("error listing extension services config: %w", err) } r.StartTrackingOutputs() touchedFiles := map[string]struct{}{} for res := range list.All() { extensionConfigPath := filepath.Join(ctrl.ExtensionsConfigBaseDir, res.Metadata().ID()) if err = os.MkdirAll(extensionConfigPath, 0o755); err != nil { return fmt.Errorf("error creating directory %q: %w", extensionConfigPath, err) } touchedFiles[extensionConfigPath] = struct{}{} for _, file := range res.TypedSpec().Files { fileName := filepath.Join(extensionConfigPath, strings.ReplaceAll(strings.TrimPrefix(file.MountPath, "/"), "/", "-")) if err = updateFile(fileName, []byte(file.Content), 0o644); err != nil { return fmt.Errorf("error writing file %q: %w", fileName, err) } touchedFiles[fileName] = struct{}{} } if err = safe.WriterModify(ctx, r, runtime.NewExtensionServiceConfigStatusSpec(runtime.NamespaceName, res.Metadata().ID()), func(spec *runtime.ExtensionServiceConfigStatus) error { spec.TypedSpec().SpecVersion = res.Metadata().Version().String() return nil }); err != nil { return err } } // remove all files not managed by us if err = filepath.WalkDir(ctrl.ExtensionsConfigBaseDir, func(path string, d fs.DirEntry, walkErr error) error { if _, ok := touchedFiles[path]; path != ctrl.ExtensionsConfigBaseDir && !ok { if err = os.RemoveAll(path); err != nil { return err } } return nil }); err != nil { return err } if err = safe.CleanupOutputs[*runtime.ExtensionServiceConfigStatus](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service_config_files_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "os" "path/filepath" "strings" "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type ExtensionServiceConfigFilesSuite struct { ctest.DefaultSuite extensionsConfigDir string } func TestExtensionServiceConfigFilesSuite(t *testing.T) { extensionsConfigDir := t.TempDir() suite.Run(t, &ExtensionServiceConfigFilesSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtime.ExtensionServiceConfigFilesController{ ExtensionsConfigBaseDir: extensionsConfigDir, })) }, }, extensionsConfigDir: extensionsConfigDir, }) } func (suite *ExtensionServiceConfigFilesSuite) TestReconcileExtensionServiceConfigFiles() { for _, tt := range []struct { extensionName string configFiles []struct { content string mountPath string } }{ { extensionName: "test-extension-a", configFiles: []struct { content string mountPath string }{ { content: "test-content-a", mountPath: "/etc/test", }, }, }, { extensionName: "test-extension-b", configFiles: []struct { content string mountPath string }{ { content: "test-content-b", mountPath: "/etc/bar", }, { content: "test-content-c", mountPath: "/var/etc/foo", }, }, }, } { extensionServiceConfigFiles := runtimeres.NewExtensionServiceConfigSpec(runtimeres.NamespaceName, tt.extensionName) extensionServiceConfigFiles.TypedSpec().Files = xslices.Map(tt.configFiles, func(config struct { content string mountPath string }, ) runtimeres.ExtensionServiceConfigFile { return runtimeres.ExtensionServiceConfigFile{ Content: config.content, MountPath: config.mountPath, } }) suite.Require().NoError(suite.State().Create(suite.Ctx(), extensionServiceConfigFiles)) ctest.AssertResource(suite, tt.extensionName, func(status *runtimeres.ExtensionServiceConfigStatus, asrt *assert.Assertions) { asrt.Equal(extensionServiceConfigFiles.Metadata().Version().String(), status.TypedSpec().SpecVersion) }, ) for _, file := range tt.configFiles { content, err := os.ReadFile(filepath.Join(suite.extensionsConfigDir, tt.extensionName, strings.ReplaceAll(strings.TrimPrefix(file.mountPath, "/"), "/", "-"))) suite.Require().NoError(err) suite.Assert().Equal(file.content, string(content)) } } // create a directory and file manually in the extensions config directory // ensure that the controller deletes the manually created directory/file // also ensure that an update doesn't update existing files timestamp suite.Assert().NoError(os.Mkdir(filepath.Join(suite.extensionsConfigDir, "test"), 0o755)) suite.Assert().NoError(os.WriteFile(filepath.Join(suite.extensionsConfigDir, "test", "testdata"), []byte("{}"), 0o644)) extensionAConfigFileInfo, err := os.Stat(filepath.Join(suite.extensionsConfigDir, "test-extension-a", "etc-test")) suite.Assert().NoError(err) // delete test-extension-b resource suite.Assert().NoError(suite.State().Destroy(suite.Ctx(), runtimeres.NewExtensionServiceConfigSpec(runtimeres.NamespaceName, "test-extension-b").Metadata())) ctest.AssertNoResource[*runtimeres.ExtensionServiceConfigStatus](suite, "test-extension-b") suite.Assert().NoFileExists(filepath.Join(suite.extensionsConfigDir, "test", "testdata")) suite.Assert().NoDirExists(filepath.Join(suite.extensionsConfigDir, "test")) suite.Assert().NoFileExists(filepath.Join(suite.extensionsConfigDir, "test-extension-b", "etc-bar")) suite.Assert().NoFileExists(filepath.Join(suite.extensionsConfigDir, "test-extension-b", "var-etc-foo")) suite.Assert().NoDirExists(filepath.Join(suite.extensionsConfigDir, "test-extension-b")) extensionAConfigFileInfoAfterUpdate, err := os.Stat(filepath.Join(suite.extensionsConfigDir, "test-extension-a", "etc-test")) suite.Require().NoError(err) suite.Assert().Equal(extensionAConfigFileInfo.ModTime(), extensionAConfigFileInfoAfterUpdate.ModTime()) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" cntrconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions" "github.com/siderolabs/talos/pkg/machinery/resources/config" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type ExtensionServiceConfigSuite struct { ctest.DefaultSuite } func TestExtensionServiceConfigSuite(t *testing.T) { suite.Run(t, &ExtensionServiceConfigSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtime.ExtensionServiceConfigController{})) }, }, }) } func (suite *ExtensionServiceConfigSuite) TestReconcileExtensionServiceConfig() { extensionServiceConfigs := []struct { extensionName string configFiles []struct { content string mountPath string } environment []string }{ { extensionName: "test-extension-a", configFiles: []struct { content string mountPath string }{ { content: "test-content-a", mountPath: "/etc/test", }, }, }, { extensionName: "test-extension-b", configFiles: []struct { content string mountPath string }{ { content: "test-content-b", mountPath: "/etc/bar", }, { content: "test-content-c", mountPath: "/var/etc/foo", }, }, environment: []string{ "FOO=BAR", }, }, } cfgs := xslices.Map(extensionServiceConfigs, func(tt struct { extensionName string configFiles []struct { content string mountPath string } environment []string }, ) cntrconfig.Document { cfg := extensions.NewServicesConfigV1Alpha1() cfg.ServiceName = tt.extensionName cfg.ServiceConfigFiles = xslices.Map(tt.configFiles, func(config struct { content string mountPath string }, ) extensions.ConfigFile { return extensions.ConfigFile{ ConfigFileContent: config.content, ConfigFileMountPath: config.mountPath, } }) cfg.ServiceEnvironment = tt.environment return cfg }) cntr, err := container.New(cfgs...) suite.Require().NoError(err) machineConfig := config.NewMachineConfig(cntr) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineConfig)) for _, tt := range extensionServiceConfigs { ctest.AssertResource(suite, tt.extensionName, func(config *runtimeres.ExtensionServiceConfig, asrt *assert.Assertions) { spec := config.TypedSpec() configFileData := xslices.Map(tt.configFiles, func(config struct { content string mountPath string }, ) runtimeres.ExtensionServiceConfigFile { return runtimeres.ExtensionServiceConfigFile{ Content: config.content, MountPath: config.mountPath, } }) suite.Assert().Equal(configFileData, spec.Files) suite.Assert().Equal(tt.environment, spec.Environment) }) } // test deletion cfg := extensions.NewServicesConfigV1Alpha1() cfg.ServiceName = "test-extension-a" cntr, err = container.New(cfg) suite.Require().NoError(err) machineConfig = config.NewMachineConfig(cntr) suite.Require().NoError(suite.State().Destroy(suite.Ctx(), machineConfig.Metadata())) ctest.AssertNoResource[*runtimeres.ExtensionServiceConfig](suite, "test-extension-a") } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_service_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "fmt" "reflect" "slices" "sync" "testing" "time" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type ExtensionServiceSuite struct { RuntimeSuite } type serviceMock struct { mu sync.Mutex services map[string]system.Service running map[string]bool timesStarted map[string]int timesStopped map[string]int } func (mock *serviceMock) Load(services ...system.Service) []string { mock.mu.Lock() defer mock.mu.Unlock() ids := make([]string, 0, len(services)) for _, svc := range services { mock.services[svc.ID(nil)] = svc ids = append(ids, svc.ID(nil)) } return ids } func (mock *serviceMock) Start(serviceIDs ...string) error { mock.mu.Lock() defer mock.mu.Unlock() for _, id := range serviceIDs { mock.running[id] = true mock.timesStarted[id]++ } return nil } func (mock *serviceMock) IsRunning(id string) (system.Service, bool, error) { mock.mu.Lock() defer mock.mu.Unlock() svc, exists := mock.services[id] if !exists { return nil, false, fmt.Errorf("service %q not found", id) } _, running := mock.running[id] return svc, running, nil } func (mock *serviceMock) Stop(ctx context.Context, serviceIDs ...string) error { mock.mu.Lock() defer mock.mu.Unlock() for _, id := range serviceIDs { mock.running[id] = false mock.timesStopped[id]++ } return nil } func (mock *serviceMock) getIDs() []string { mock.mu.Lock() defer mock.mu.Unlock() ids := make([]string, 0, len(mock.services)) for id := range mock.services { ids = append(ids, id) } slices.Sort(ids) return ids } type serviceStartStopInfo struct { started int stopped int } func (mock *serviceMock) getTimesStartedStopped() map[string]serviceStartStopInfo { mock.mu.Lock() defer mock.mu.Unlock() result := map[string]serviceStartStopInfo{} for id := range mock.services { result[id] = serviceStartStopInfo{ started: mock.timesStarted[id], stopped: mock.timesStopped[id], } } return result } func (mock *serviceMock) get(id string) system.Service { mock.mu.Lock() defer mock.mu.Unlock() return mock.services[id] } func (suite *ExtensionServiceSuite) TestReconcile() { svcMock := &serviceMock{ services: map[string]system.Service{}, running: map[string]bool{}, timesStarted: map[string]int{}, timesStopped: map[string]int{}, } suite.Require().NoError(suite.runtime.RegisterController(&runtimecontrollers.ExtensionServiceController{ V1Alpha1Services: svcMock, ConfigPath: "testdata/extservices/", })) suite.startRuntime() suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { ids := svcMock.getIDs() if !slices.Equal(ids, []string{"ext-frr", "ext-hello-world"}) { return retry.ExpectedErrorf("services registered: %q", ids) } return nil }, )) helloSvc := svcMock.get("ext-hello-world") suite.Require().IsType(&services.Extension{}, helloSvc) suite.Assert().Equal("./hello-world", helloSvc.(*services.Extension).Spec.Container.Entrypoint) suite.Assert().Equal( map[string]serviceStartStopInfo{ "ext-hello-world": { started: 1, }, "ext-frr": { started: 1, }, }, svcMock.getTimesStartedStopped(), ) helloConfig := runtime.NewExtensionServiceConfigStatusSpec(runtime.NamespaceName, "hello-world") helloConfig.TypedSpec().SpecVersion = "1" suite.Require().NoError(suite.state.Create(suite.ctx, helloConfig)) assertTimesStartedStopped := func(expected map[string]serviceStartStopInfo) { suite.Assert().NoError(retry.Constant(5*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { actual := svcMock.getTimesStartedStopped() if !reflect.DeepEqual(actual, expected) { return retry.ExpectedErrorf("services restart status expected %v, actual %v", expected, actual) } return nil }, )) } // specVersion is 1, and ext-hello-world is already started, so it should not be restarted assertTimesStartedStopped(map[string]serviceStartStopInfo{ "ext-hello-world": { started: 1, stopped: 0, }, "ext-frr": { started: 1, }, }) unexpectedConfig := runtime.NewExtensionServiceConfigStatusSpec(runtime.NamespaceName, "unexpected") unexpectedConfig.TypedSpec().SpecVersion = "1" suite.Require().NoError(suite.state.Create(suite.ctx, unexpectedConfig)) assertTimesStartedStopped(map[string]serviceStartStopInfo{ "ext-hello-world": { started: 1, stopped: 0, }, "ext-frr": { started: 1, }, }) // update config for hello service helloConfig.TypedSpec().SpecVersion = "2" suite.Require().NoError(suite.state.Update(suite.ctx, helloConfig)) assertTimesStartedStopped(map[string]serviceStartStopInfo{ "ext-hello-world": { started: 2, stopped: 1, }, "ext-frr": { started: 1, }, }) // destroy config for hello service suite.Require().NoError(suite.state.Destroy(suite.ctx, helloConfig.Metadata())) assertTimesStartedStopped(map[string]serviceStartStopInfo{ "ext-hello-world": { started: 3, stopped: 2, }, "ext-frr": { started: 1, }, }) } func TestExtensionServiceSuite(t *testing.T) { suite.Run(t, new(ExtensionServiceSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/extension_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "io" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ExtensionStatusController loads extensions.yaml and updates ExtensionStatus resources. type ExtensionStatusController struct{} // Name implements controller.Controller interface. func (ctrl *ExtensionStatusController) Name() string { return "runtime.ExtensionStatusController" } // Inputs implements controller.Controller interface. func (ctrl *ExtensionStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *ExtensionStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.ExtensionStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *ExtensionStatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // controller runs once, as extensions are static select { case <-ctx.Done(): return nil case <-r.EventCh(): } var cfg extensions.Config if err := cfg.Read(constants.ExtensionsRuntimeConfigFile); err != nil { if errors.Is(err, io.EOF) { // no extensions installed return nil } return fmt.Errorf("failed loading extensions config: %w", err) } for _, layer := range cfg.Layers { id := strings.TrimSuffix(layer.Image, ".sqsh") if err := safe.WriterModify(ctx, r, runtime.NewExtensionStatus(runtime.NamespaceName, id), func(res *runtime.ExtensionStatus) error { *res.TypedSpec() = *layer return nil }); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/diagnostics/address_overlap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package diagnostics import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // AddressOverlapCheck checks for overlapping host and Kubernetes pod/service CIDR addresses. func AddressOverlapCheck(ctx context.Context, r controller.Reader, logger *zap.Logger) (*runtime.DiagnosticSpec, error) { hostAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.NodeAddressRoutedID) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error reading host addresses: %w", err) } hostMinusK8s, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error reading host minus k8s addresses: %w", err) } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error reading machine configuration: %w", err) } if len(hostAddresses.TypedSpec().Addresses) > 0 && len(hostMinusK8s.TypedSpec().Addresses) == 0 { details := []string{ fmt.Sprintf("host routed addresses: %q", xslices.Map(hostAddresses.TypedSpec().Addresses, netip.Prefix.String)), } if cfg.Config().Cluster() != nil { details = append(details, fmt.Sprintf("Kubernetes pod CIDRs: %q", cfg.Config().Cluster().Network().PodCIDRs())) details = append(details, fmt.Sprintf("Kubernetes service CIDRs: %q", cfg.Config().Cluster().Network().ServiceCIDRs())) } return &runtime.DiagnosticSpec{ Message: "host and Kubernetes pod/service CIDR addresses overlap", Details: details, }, nil } return nil, nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/diagnostics/address_overlap_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package diagnostics_test import ( "context" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/diagnostics" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestAddressOverlapCheck(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(t.Context(), time.Minute) t.Cleanup(cancel) for _, test := range []struct { name string setup func(t *testing.T, ctx context.Context, st state.State) expectedWarning *runtime.DiagnosticSpec }{ { name: "no addresses", setup: func(t *testing.T, ctx context.Context, st state.State) {}, }, { name: "no overlap", setup: func(t *testing.T, ctx context.Context, st state.State) { hostAddresses := network.NewNodeAddress(network.NamespaceName, network.NodeAddressRoutedID) hostAddresses.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.0.0.1/8"), netip.MustParsePrefix("10.244.1.3/32")} require.NoError(t, st.Create(ctx, hostAddresses)) hostMinusK8s := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) hostMinusK8s.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.0.0.1/8")} require.NoError(t, st.Create(ctx, hostMinusK8s)) }, }, { name: "with overlap", setup: func(t *testing.T, ctx context.Context, st state.State) { hostAddresses := network.NewNodeAddress(network.NamespaceName, network.NodeAddressRoutedID) hostAddresses.TypedSpec().Addresses = []netip.Prefix{netip.MustParsePrefix("10.244.3.4/24"), netip.MustParsePrefix("10.244.1.3/32")} require.NoError(t, st.Create(ctx, hostAddresses)) hostMinusK8s := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s)) hostMinusK8s.TypedSpec().Addresses = []netip.Prefix{} require.NoError(t, st.Create(ctx, hostMinusK8s)) }, expectedWarning: &runtime.DiagnosticSpec{ Message: "host and Kubernetes pod/service CIDR addresses overlap", Details: []string{ "host routed addresses: [\"10.244.3.4/24\" \"10.244.1.3/32\"]", "Kubernetes pod CIDRs: [\"10.244.0.0/16\"]", "Kubernetes service CIDRs: [\"10.96.0.0/12\"]", }, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() logger := zaptest.NewLogger(t) st := state.WrapCore(namespaced.NewState(inmem.Build)) in, err := generate.NewInput("test-cluster", "https://localhost", constants.DefaultKubernetesVersion) require.NoError(t, err) cfg, err := in.Config(machine.TypeWorker) require.NoError(t, err) cfgResource := config.NewMachineConfig(cfg) require.NoError(t, st.Create(ctx, cfgResource)) test.setup(t, ctx, st) spec, err := diagnostics.AddressOverlapCheck(ctx, st, logger) require.NoError(t, err) if test.expectedWarning == nil { require.Nil(t, spec) } else { require.Equal(t, test.expectedWarning, spec) } }) } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/diagnostics/diagnostic.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package diagnostics provides Talos diagnostics specific checks. package diagnostics import ( "context" "time" "github.com/cosi-project/runtime/pkg/controller" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Check defines a function that checks for a specific issue. // // If the check produces a warning, it should return a non-nil warning and nil error. // If the check produces an error, the error will be logged, and other checks will proceed running. type Check func(ctx context.Context, r controller.Reader, logger *zap.Logger) (*runtime.DiagnosticSpec, error) // CheckDescription combines a check with a semantic ID. type CheckDescription struct { // Semantic ID is used to identify the check and help message. ID string // Hysteresis time to wait before announcing the warning after the first appearance. Hysteresis time.Duration // Check function to run. Check Check } // Checks returns a list of checks to be run by the diagnostics engine. func Checks() []CheckDescription { return []CheckDescription{ { ID: "address-overlap", Hysteresis: 30 * time.Second, Check: AddressOverlapCheck, }, { ID: "kubelet-csr", Hysteresis: 30 * time.Second, Check: KubeletCSRNotApprovedCheck, }, } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/diagnostics/kubelet_csr_not_approved.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package diagnostics import ( "context" "crypto/tls" "errors" "fmt" "net" "strings" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" v1 "k8s.io/api/certificates/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // KubeletCSRNotApprovedCheck checks for kubelet server certificate rotation and no CSR approvers. // //nolint:gocyclo func KubeletCSRNotApprovedCheck(ctx context.Context, r controller.Reader, logger *zap.Logger) (*runtime.DiagnosticSpec, error) { // check kubelet status to make sure it's running & health before proceeding any further kubeletService, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "kubelet") if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error reading kubelet service: %w", err) } if !kubeletService.TypedSpec().Running || !kubeletService.TypedSpec().Healthy { return nil, nil } // fetch nodename nodeName, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error reading nodename: %w", err) } // try to access kubelet API to see if we get 'tls: internal error' c, err := (&tls.Dialer{ NetDialer: &net.Dialer{Timeout: 5 * time.Second}, Config: &tls.Config{ InsecureSkipVerify: true, }, }).DialContext(ctx, "tcp", "127.0.0.1:10250") if err == nil { return nil, c.Close() } var netError *net.OpError if !errors.As(err, &netError) { // not our error return nil, nil } if !(netError.Op == "remote error" && netError.Err.Error() == tls.AlertError(80).Error()) { // remote error: tls: internal error return nil, nil } k8sClient, err := kubernetes.NewClientFromKubeletKubeconfig() if err != nil { return nil, fmt.Errorf("error creating k8s client: %w", err) } defer k8sClient.Close() //nolint:errcheck csrs, err := k8sClient.Clientset.CertificatesV1().CertificateSigningRequests().List(ctx, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("spec.signerName", "kubernetes.io/kubelet-serving").String(), }, ) if err != nil { // error getting CSRs return nil, fmt.Errorf("error listing CSRs: %w", err) } expectedUsername := fmt.Sprintf("system:node:%s", nodeName.TypedSpec().Nodename) csrs.Items = xslices.Filter(csrs.Items, func(csr v1.CertificateSigningRequest) bool { if csr.Spec.Username != expectedUsername { return false } for _, condition := range csr.Status.Conditions { if condition.Type == v1.CertificateApproved { return false } } return true }) if len(csrs.Items) == 0 { return nil, nil } return &runtime.DiagnosticSpec{ Message: "kubelet server certificate rotation is enabled, but CSR is not approved", Details: []string{ fmt.Sprintf("kubelet API error: %s", netError), fmt.Sprintf("pending CSRs: %s", strings.Join( xslices.Map(csrs.Items, func(csr v1.CertificateSigningRequest) string { return csr.Name }), ", ", ), ), }, }, nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/filehash/filehash.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package filehash implements a specialized file watcher that detects changes in pseudo-files like /proc/modules. package filehash import ( "crypto/sha256" "io" "os" "time" ) // Watcher monitors a file for changes by comparing its hash every second. type Watcher struct { filepath string lastHash [32]byte quit chan struct{} } // NewWatcher creates a new file watcher for the specified filepath. func NewWatcher(filepath string) (*Watcher, error) { return &Watcher{ filepath: filepath, quit: make(chan struct{}), }, nil } // Close stops the watcher and releases resources. func (w *Watcher) Close() error { close(w.quit) return nil } // Run polls the file every second and emits the path if the hash changes. func (w *Watcher) Run() (<-chan string, <-chan error) { eventCh := make(chan string, 1) errCh := make(chan error, 1) go func() { ticker := time.NewTicker(time.Second) defer ticker.Stop() defer close(eventCh) defer close(errCh) for { select { case <-w.quit: return case <-ticker.C: hash, err := hashFile(w.filepath) if err != nil { errCh <- err continue } if hash != w.lastHash { w.lastHash = hash eventCh <- w.filepath } } } }() return eventCh, errCh } func hashFile(path string) ([32]byte, error) { var zero [32]byte f, err := os.Open(path) if err != nil { return zero, err } defer f.Close() //nolint:errcheck h := sha256.New() if _, err := io.Copy(h, f); err != nil { return zero, err } var sum [32]byte copy(sum[:], h.Sum(nil)) return sum, nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/filehash/filehash_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package filehash_test import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/filehash" ) func assertEvent(t *testing.T, eventCh <-chan string, errCh <-chan error, expected string) { t.Helper() select { case path := <-eventCh: require.Equal(t, expected, path) case err := <-errCh: require.FailNow(t, "unexpected error: %v", err) case <-time.After(2 * time.Second): require.FailNow(t, "timeout waiting for event") } } func assertNoEvent(t *testing.T, eventCh <-chan string, errCh <-chan error) { t.Helper() select { case path := <-eventCh: require.FailNow(t, "unexpected event: %v", path) case err := <-errCh: require.FailNow(t, "unexpected error: %v", err) case <-time.After(500 * time.Millisecond): } } func TestWatcherDetectsChange(t *testing.T) { t.Parallel() dir := t.TempDir() file := filepath.Join(dir, "testfile") require.NoError(t, os.WriteFile(file, []byte("foo"), 0o644)) watcher, err := filehash.NewWatcher(file) require.NoError(t, err) defer watcher.Close() //nolint:errcheck eventCh, errCh := watcher.Run() // Initial change should be detected assertEvent(t, eventCh, errCh, file) // No change, so no event assertNoEvent(t, eventCh, errCh) // Modify file require.NoError(t, os.WriteFile(file, []byte("bar"), 0o644)) assertEvent(t, eventCh, errCh, file) // No change, so no event assertNoEvent(t, eventCh, errCh) } func TestWatcherHandlesMissingFile(t *testing.T) { t.Parallel() dir := t.TempDir() file := filepath.Join(dir, "missingfile") watcher, err := filehash.NewWatcher(file) require.NoError(t, err) defer watcher.Close() //nolint:errcheck eventCh, errCh := watcher.Run() // Should get an error because file does not exist select { case <-eventCh: require.FailNow(t, "unexpected event for missing file") case err := <-errCh: require.Error(t, err) case <-time.After(2 * time.Second): require.FailNow(t, "timeout waiting for error") } } func TestWatcherClose(t *testing.T) { t.Parallel() dir := t.TempDir() file := filepath.Join(dir, "testfile") require.NoError(t, os.WriteFile(file, []byte("foo"), 0o644)) watcher, err := filehash.NewWatcher(file) require.NoError(t, err) eventCh, errCh := watcher.Run() watcher.Close() //nolint:errcheck // Channels should be closed _, ok1 := <-eventCh _, ok2 := <-errCh require.False(t, ok1) require.False(t, ok2) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/logfile/logfile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package logfile implements a buffered, rotating log file. package logfile import ( "bufio" "fmt" "io" "os" "sync" ) // LogFile is an implementation of a buffered and rotated log file. type LogFile struct { mu sync.Mutex file *os.File buf bufio.Writer path string size int64 rotationThreshold int64 } // NewLogFile creates a LogFile. func NewLogFile(path string, rotationThreshold int64) *LogFile { return &LogFile{ path: path, rotationThreshold: rotationThreshold, } } // Write appends a line to the end of file, handling file creation and rotation. func (lf *LogFile) Write(line []byte) error { var err error lf.mu.Lock() defer lf.mu.Unlock() if lf.file == nil { lf.file, err = os.OpenFile(lf.path, os.O_CREATE|os.O_WRONLY, 0o640) if err != nil { return fmt.Errorf("error opening log file %q: %w", lf.path, err) } lf.size, err = lf.file.Seek(0, io.SeekEnd) if err != nil { return fmt.Errorf("error determining log file %q length: %w", lf.path, err) } lf.buf.Reset(lf.file) } var n int if n, err = lf.buf.Write(append(line, '\n')); err != nil { return fmt.Errorf("error writing log line to file %q: %w", lf.path, err) } lf.size += int64(n) if lf.size < lf.rotationThreshold { return nil } if err = lf.close(); err != nil { return err } if err = os.Rename(lf.path, lf.path+".1"); err != nil { return fmt.Errorf("error renaming log file %q: %w", lf.path, err) } return nil } func (lf *LogFile) flush() error { if err := lf.buf.Flush(); err != nil { return fmt.Errorf("failed to flush log file %s buffer: %w", lf.path, err) } return nil } // Flush flushes the internal buffer to persist data to the filesystem. func (lf *LogFile) Flush() error { lf.mu.Lock() defer lf.mu.Unlock() return lf.flush() } func (lf *LogFile) close() error { if err := lf.flush(); err != nil { return err } lf.buf.Reset(nil) if lf.file == nil { return nil } err := lf.file.Close() lf.file = nil if err != nil { return fmt.Errorf("failed to close log file %s: %w", lf.path, err) } return nil } // Close flushes and closes the underlying file. func (lf *LogFile) Close() error { lf.mu.Lock() defer lf.mu.Unlock() return lf.close() } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/logfile/logfile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logfile_test import ( "bytes" "os" "path/filepath" "strconv" "sync" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/logfile" ) func TestWrite(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 1024) defer require.NoError(t, lf.Close()) err := lf.Write([]byte("hello world")) require.NoError(t, err) // Expect write to retain data in the buffer st, err := os.Stat(path) require.NoError(t, err) require.Equal(t, int64(0), st.Size(), "file should be empty before flush") require.NoError(t, lf.Flush()) // After flush, check the data got written to the file content, err := os.ReadFile(path) require.NoError(t, err) require.Equal(t, "hello world\n", string(content)) } func TestWriteMultipleLines(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 1024) defer require.NoError(t, lf.Close()) lines := []string{"line1", "line2", "line3"} for _, line := range lines { require.NoError(t, lf.Write([]byte(line))) } require.NoError(t, lf.Flush()) content, err := os.ReadFile(path) require.NoError(t, err) require.Equal(t, "line1\nline2\nline3\n", string(content)) } func TestLogRotation(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") expectedRotatedPath := path + ".1" lf := logfile.NewLogFile(path, 50) defer require.NoError(t, lf.Close()) // We write 4 lines (indices 0-3) // expecting 0-2 to be written before rotation and 3 after rotation for i := range 4 { line := []byte("_20_character_line_" + strconv.Itoa(i)) require.NoError(t, lf.Write(line)) } _, err := os.Stat(expectedRotatedPath) require.NoError(t, err) // Verify the rotated file contains the written data rotatedContent, err := os.ReadFile(expectedRotatedPath) require.NoError(t, err) require.Len(t, rotatedContent, 63) require.Contains(t, string(rotatedContent), "_20_character_line_2") require.NoError(t, lf.Flush()) currentContent, err := os.ReadFile(path) require.NoError(t, err) require.Len(t, currentContent, 21) require.Contains(t, string(currentContent), "_20_character_line_3") } func TestLogRotationMultipleTimes(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") rotatedPath := path + ".1" lf := logfile.NewLogFile(path, 40) defer require.NoError(t, lf.Close()) for i := range 10 { line := []byte("_20_character_line_" + strconv.Itoa(i)) require.NoError(t, lf.Write(line)) } // Rotated file should exist and contain most recent events before the current rotatedContent, err := os.ReadFile(rotatedPath) require.NoError(t, err) require.Len(t, rotatedContent, 42) require.Contains(t, string(rotatedContent), "_20_character_line_8") require.Contains(t, string(rotatedContent), "_20_character_line_9") } func TestFlushWithoutFile(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 1024) defer require.NoError(t, lf.Close()) require.NoError(t, lf.Flush()) } func TestClose(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 1024) require.NoError(t, lf.Write([]byte("data"))) err := lf.Close() require.NoError(t, err) // Expect Close to have flushed the buffer content, err := os.ReadFile(path) require.NoError(t, err) require.Contains(t, string(content), "data") } func TestCloseWithoutWrite(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 1024) require.NoError(t, lf.Close()) } func TestConcurrentWrites(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") // Do not rotate while the test runs lf := logfile.NewLogFile(path, 100000) defer require.NoError(t, lf.Close()) var wg sync.WaitGroup numGoroutines := 10 writesPerGoroutine := 100 for range numGoroutines { wg.Go(func() { for range writesPerGoroutine { require.NoError(t, lf.Write([]byte("goroutine write"))) } }) } wg.Wait() require.NoError(t, lf.Flush()) content, err := os.ReadFile(path) require.NoError(t, err) // Count lines to verify all writes succeeded lineCount := bytes.Count(content, []byte("\n")) expectedLines := numGoroutines * writesPerGoroutine require.Equal(t, expectedLines, lineCount) } func TestConcurrentWriteAndFlush(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") lf := logfile.NewLogFile(path, 10000) defer require.NoError(t, lf.Close()) var wg sync.WaitGroup // Writer goroutines for range 5 { wg.Go(func() { for range 50 { require.NoError(t, lf.Write([]byte("concurrent data"))) } }) } // Flusher goroutines for range 3 { wg.Go(func() { for range 10 { require.NoError(t, lf.Flush()) } }) } wg.Wait() require.NoError(t, lf.Flush()) } func TestConcurrentWritesWithRotation(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.log") // Small threshold to trigger rotation during concurrent writes lf := logfile.NewLogFile(path, 100) defer require.NoError(t, lf.Close()) var wg sync.WaitGroup numGoroutines := 5 writesPerGoroutine := 50 for range numGoroutines { wg.Go(func() { for range writesPerGoroutine { require.NoError(t, lf.Write([]byte("rotation test line"))) } }) } wg.Wait() _, err := os.Stat(path + ".1") require.NoError(t, err) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/oom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package oom contains utilities for OOM handler. package oom import ( "fmt" "io/fs" "math" "os" "path/filepath" "time" "github.com/google/cel-go/common/types" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/cgroups" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // RankedCgroup contains information about a cgroup used for OOM handling. type RankedCgroup struct { Class runtime.QoSCgroupClass Path string MemoryCurrent cgroups.Value MemoryPeak cgroups.Value MemoryMax cgroups.Value } func cgroupValueToOptionalUint(v cgroups.Value, evalContext map[string]any, key string) { if !v.IsSet || v.IsMax || v.Frac > 0 || v.Val < 0 { evalContext[key] = types.OptionalNone } else { evalContext[key] = types.OptionalOf(types.Uint(v.Val)) } } // CalculateScore calculates the score of the cgroup for OOM handling. // // Higher score means the cgroup is more likely to be killed. func (cgroup *RankedCgroup) CalculateScore(expr *cel.Expression) (float64, error) { evalContext := map[string]any{ "class": int(cgroup.Class), "path": cgroup.Path, } cgroupValueToOptionalUint(cgroup.MemoryCurrent, evalContext, "memory_current") cgroupValueToOptionalUint(cgroup.MemoryPeak, evalContext, "memory_peak") cgroupValueToOptionalUint(cgroup.MemoryMax, evalContext, "memory_max") return expr.EvalDouble(celenv.OOMCgroupScoring(), evalContext) } // EvaluateTrigger is a method obtaining data and evaluating the trigger expression. // When the result is true, designated OOM action is to be executed. func EvaluateTrigger(triggerExpr cel.Expression, evalContext map[string]any) (bool, error) { trigger, err := triggerExpr.EvalBool(celenv.OOMTrigger(), evalContext) if err != nil { return false, fmt.Errorf("cannot evaluate expression: %w", err) } return trigger, nil } // PopulatePsiToCtx populates the context with PSI data from a cgroup. // //nolint:gocyclo func PopulatePsiToCtx(cgroup string, evalContext map[string]any, oldValues map[string]float64, sampleInterval time.Duration) error { if sampleInterval <= 0 { return fmt.Errorf("sample interval must be greater than zero") } for _, subtree := range []struct { path string qos runtime.QoSCgroupClass }{ {"", -1}, {"init", runtime.QoSCgroupClassSystem}, {"system", runtime.QoSCgroupClassSystem}, {"podruntime", runtime.QoSCgroupClassPodruntime}, {"kubepods/besteffort", runtime.QoSCgroupClassBesteffort}, {"kubepods/burstable", runtime.QoSCgroupClassBurstable}, {"kubepods/guaranteed", runtime.QoSCgroupClassGuaranteed}, } { node, err := cgroups.GetCgroupProperty(filepath.Join(cgroup, subtree.path), "memory.pressure") for _, psiType := range []string{"some", "full"} { for _, span := range []string{"avg10", "avg60", "avg300", "total"} { value := 0. // Default non-existent cgroups to all-zero, e.g. during system boot if err == nil { value, err = extractPsiEntry(node, psiType, span) if err != nil { return err } } // calculate delta psiPath := subtree.path + "/" + "memory_" + psiType + "_" + span diff := 0. if oldValue, ok := oldValues[psiPath]; ok { diff = (value - oldValue) / sampleInterval.Seconds() } oldValues[psiPath] = value if subtree.qos == -1 { evalContext["d_memory_"+psiType+"_"+span] = diff evalContext["memory_"+psiType+"_"+span] = value } else { valuesMap, ok := evalContext["qos_memory_"+psiType+"_"+span] if !ok { valuesMap = map[int]float64{} evalContext["qos_memory_"+psiType+"_"+span] = valuesMap } valuesMap.(map[int]float64)[int(subtree.qos)] += value dValuesMap, ok := evalContext["d_qos_memory_"+psiType+"_"+span] if !ok { dValuesMap = map[int]float64{} evalContext["d_qos_memory_"+psiType+"_"+span] = dValuesMap } dValuesMap.(map[int]float64)[int(subtree.qos)] += diff } } } node = &cgroups.Node{} // Best effort, if any is not present it will return NaN cgroups.ReadCgroupfsProperty(node, filepath.Join(cgroup, subtree.path), "memory.current") //nolint:errcheck cgroups.ReadCgroupfsProperty(node, filepath.Join(cgroup, subtree.path), "memory.max") //nolint:errcheck cgroups.ReadCgroupfsProperty(node, filepath.Join(cgroup, subtree.path), "memory.peak") //nolint:errcheck if subtree.qos == -1 { continue } for _, parameter := range []struct { name string value float64 }{ {"current", node.MemoryCurrent.Float64()}, {"max", node.MemoryMax.Float64()}, {"peak", node.MemoryPeak.Float64()}, } { value := parameter.value // These values cannot be expressed in JSON if math.IsNaN(value) || math.IsInf(value, 0) { value = 0.0 } valuesMap, ok := evalContext["qos_memory_"+parameter.name] if !ok { valuesMap = map[int]float64{} evalContext["qos_memory_"+parameter.name] = valuesMap } valuesMap.(map[int]float64)[int(subtree.qos)] += value oldPath := subtree.path + "/" + "memory_" + parameter.name diff := 0. if oldValue, ok := oldValues[oldPath]; ok { diff = (value - oldValue) / sampleInterval.Seconds() } dValuesMap, ok := evalContext["d_qos_memory_"+parameter.name] if !ok { dValuesMap = map[int]float64{} evalContext["d_qos_memory_"+parameter.name] = dValuesMap } dValuesMap.(map[int]float64)[int(subtree.qos)] += diff } } return nil } func extractPsiEntry(node *cgroups.Node, psiType string, span string) (float64, error) { spans, ok := node.MemoryPressure[psiType] if !ok { return 0, fmt.Errorf("cannot find memory pressure type: type: %s", psiType) } cgValue, ok := spans[span] if !ok { return 0, fmt.Errorf("cannot find memory pressure span: span: %s", span) } if !cgValue.IsSet || cgValue.IsMax { return 0, fmt.Errorf("PSI is not defined") } return cgValue.Float64(), nil } // RankCgroups ranks cgroups using a scoring expression and returns a map. func RankCgroups(logger *zap.Logger, root string, scoringExpr cel.Expression) map[RankedCgroup]float64 { ranking := map[RankedCgroup]float64{} for _, cg := range []struct { dir string class runtime.QoSCgroupClass }{ {"kubepods/besteffort", runtime.QoSCgroupClassBesteffort}, {"kubepods/burstable", runtime.QoSCgroupClassBurstable}, {"kubepods/guaranteed", runtime.QoSCgroupClassGuaranteed}, {constants.CgroupPodRuntimeRoot, runtime.QoSCgroupClassPodruntime}, {constants.CgroupSystem, runtime.QoSCgroupClassSystem}, } { entries, err := os.ReadDir(filepath.Join(root, cg.dir)) if err != nil && !os.IsNotExist(err) { logger.Error("cannot list cgroup members", zap.String("dir", cg.dir), zap.Error(err)) continue } for _, leaf := range entries { if !leaf.IsDir() { continue } leafDir := filepath.Join(root, cg.dir, leaf.Name()) node := cgroups.Node{} for _, prop := range []string{"memory.current", "memory.peak", "memory.max"} { err := cgroups.ReadCgroupfsProperty(&node, leafDir, prop) if err != nil { logger.Error("cannot read property for cgroup", zap.String("dir", leafDir), zap.String("propery", prop), zap.Error(err), ) continue } } cgroup := RankedCgroup{ Path: leafDir, Class: cg.class, MemoryCurrent: node.MemoryCurrent, MemoryPeak: node.MemoryPeak, MemoryMax: node.MemoryMax, } ranking[cgroup], err = cgroup.CalculateScore(&scoringExpr) if err != nil { logger.Error("cannot calculate score for cgroup", zap.String("dir", cgroup.Path), zap.Error(err), ) continue } } } return ranking } // ListCgroupProcs returns a list of process IDs for a given cgroup path. func ListCgroupProcs(cgroupPath string) []int { processes := []int{} // Ignore errors, find as many processes as possible //nolint:errcheck filepath.WalkDir(cgroupPath, func(path string, d fs.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if !d.IsDir() { return nil } node, err := cgroups.GetCgroupProperty(path, "cgroup.procs") if err != nil { return err } for _, p := range node.CgroupProcs { processes = append(processes, int(p.Val)) } return nil }) return processes } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/oom_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package oom_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/oom" "github.com/siderolabs/talos/internal/pkg/cgroups" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) const expr1 = constants.DefaultOOMCgroupRankingExpression func TestCalculateScore(t *testing.T) { t.Parallel() for _, test := range []struct { name string expr string cgroup oom.RankedCgroup expect float64 }{ { name: "basic", expr: expr1, cgroup: oom.RankedCgroup{ Class: runtime.QoSCgroupClassBurstable, Path: "/some/path", MemoryCurrent: cgroups.Value{Val: 42, IsSet: true}, MemoryPeak: cgroups.Value{Val: 50, IsSet: true}, MemoryMax: cgroups.Value{IsSet: true, IsMax: true}, }, expect: 21, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() parsedExpr, err := cel.ParseDoubleExpression(test.expr, celenv.OOMCgroupScoring()) require.NoError(t, err) score, err := test.cgroup.CalculateScore(&parsedExpr) require.NoError(t, err) assert.Equal(t, test.expect, score) }, ) } } func TestRankCgroups(t *testing.T) { t.Parallel() for _, test := range []struct { name string dir string expr string expect map[oom.RankedCgroup]float64 }{ { name: "basic", dir: "./testdata/rank1", expr: expr1, expect: map[oom.RankedCgroup]float64{ { Class: runtime.QoSCgroupClassBesteffort, Path: "testdata/rank1/kubepods/besteffort/pod123", MemoryCurrent: cgroups.Value{Val: 222593024, IsSet: true}, MemoryPeak: cgroups.Value{Val: 371011584, IsSet: true}, MemoryMax: cgroups.Value{IsMax: true, IsSet: true}, }: 2.22593024e+08, { Class: runtime.QoSCgroupClassBurstable, Path: "testdata/rank1/kubepods/burstable/podABC", MemoryCurrent: cgroups.Value{Val: 42, IsSet: true}, MemoryPeak: cgroups.Value{Val: 50, IsSet: true}, MemoryMax: cgroups.Value{IsSet: true, IsMax: true}, }: 21, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() logger := zap.New(nil) parsedExpr, err := cel.ParseDoubleExpression(test.expr, celenv.OOMCgroupScoring()) require.NoError(t, err) result := oom.RankCgroups(logger, test.dir, parsedExpr) assert.Equal(t, test.expect, result) }) } } func TestPopulatePsiToCtx(t *testing.T) { t.Parallel() for _, test := range []struct { name string dir string expectErr string expect map[string]any }{ //nolint:dupl { name: "empty", dir: "./testdata/empty", expectErr: "", expect: map[string]any{ "memory_full_avg10": 0.0, "memory_full_avg300": 0.0, "memory_full_avg60": 0.0, "memory_full_total": 0.0, "memory_some_avg10": 0.0, "memory_some_avg300": 0.0, "memory_some_avg60": 0.0, "memory_some_total": 0.0, "d_memory_full_avg10": 0.0, "d_memory_full_avg300": 0.0, "d_memory_full_avg60": 0.0, "d_memory_full_total": 0.0, "d_memory_some_avg10": 0.0, "d_memory_some_avg300": 0.0, "d_memory_some_avg60": 0.0, "d_memory_some_total": 0.0, "qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, }, }, //nolint:dupl { name: "false", dir: "./testdata/trigger-false", expectErr: "", expect: map[string]any{ "memory_full_avg10": 2.4, "memory_full_avg300": 1.71, "memory_full_avg60": 5.16, "memory_full_total": 1.0654831e+07, "memory_some_avg10": 2.82, "memory_some_avg300": 1.97, "memory_some_avg60": 5.95, "memory_some_total": 1.217234e+07, "d_memory_full_avg10": 0.0, "d_memory_full_avg300": 0.0, "d_memory_full_avg60": 0.0, "d_memory_full_total": 0.0, "d_memory_some_avg10": 0.0, "d_memory_some_avg300": 0.0, "d_memory_some_avg60": 0.0, "d_memory_some_total": 0.0, "qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 2.82, int(runtime.QoSCgroupClassSystem): 5.64, }, "qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 5.95, int(runtime.QoSCgroupClassSystem): 11.9, }, "qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.97, int(runtime.QoSCgroupClassSystem): 3.94, }, "qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.217234e+07, int(runtime.QoSCgroupClassSystem): 2.434468e+07, }, "qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 2.4, int(runtime.QoSCgroupClassSystem): 4.8, }, "qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 5.16, int(runtime.QoSCgroupClassSystem): 10.32, }, "qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.71, int(runtime.QoSCgroupClassSystem): 3.42, }, "qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.0654831e+07, int(runtime.QoSCgroupClassSystem): 1.0654937e+07, }, "d_qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, }, }, // //nolint:dupl { name: "true", dir: "./testdata/trigger-true", expectErr: "", expect: map[string]any{ "memory_full_avg10": 14.54, "memory_full_avg60": 6.97, "memory_full_avg300": 1.82, "memory_full_total": 1.0654831e+07, "memory_some_avg10": 17.06, "memory_some_avg60": 8.04, "memory_some_avg300": 2.1, "memory_some_total": 1.217234e+07, "d_memory_full_avg10": 0.0, "d_memory_full_avg300": 0.0, "d_memory_full_avg60": 0.0, "d_memory_full_total": 0.0, "d_memory_some_avg10": 0.0, "d_memory_some_avg300": 0.0, "d_memory_some_avg60": 0.0, "d_memory_some_total": 0.0, "qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 17.06, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 17.06, int(runtime.QoSCgroupClassSystem): 34.12, }, "qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 8.04, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 8.04, int(runtime.QoSCgroupClassSystem): 16.08, }, "qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 2.1, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 2.1, int(runtime.QoSCgroupClassSystem): 4.2, }, "qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 1.217234e+07, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.217234e+07, int(runtime.QoSCgroupClassSystem): 2.434468e+07, }, "qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 14.54, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 14.54, int(runtime.QoSCgroupClassSystem): 29.08, }, "qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 6.97, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 6.97, int(runtime.QoSCgroupClassSystem): 13.94, }, "qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 1.82, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.82, int(runtime.QoSCgroupClassSystem): 3.64, }, "qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 1.0654831e+07, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 1.0654831e+07, int(runtime.QoSCgroupClassSystem): 2.1309662e+07, }, "d_qos_memory_some_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_some_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg10": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg60": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_avg300": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_full_total": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_current": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_peak": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, "d_qos_memory_max": map[int]float64{ int(runtime.QoSCgroupClassBesteffort): 0.0, int(runtime.QoSCgroupClassBurstable): 0.0, int(runtime.QoSCgroupClassGuaranteed): 0.0, int(runtime.QoSCgroupClassPodruntime): 0.0, int(runtime.QoSCgroupClassSystem): 0.0, }, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() ctx := map[string]any{} err := oom.PopulatePsiToCtx(test.dir, ctx, make(map[string]float64), 500*time.Millisecond) if test.expectErr == "" { require.NoError(t, err) assert.Equal(t, test.expect, ctx) } else { assert.ErrorContains(t, err, test.expectErr) } }) } } func TestEvaluateTrigger(t *testing.T) { t.Parallel() triggerExpr1 := cel.MustExpression(cel.ParseBooleanExpression( constants.DefaultOOMTriggerExpression, celenv.OOMTrigger(), )) for _, test := range []struct { name string dir string ctx map[string]any triggerExpr cel.Expression expect bool expectErr string }{ { name: "empty", dir: "./testdata/empty", ctx: map[string]any{ "time_since_trigger": 3 * time.Second, }, triggerExpr: triggerExpr1, expect: false, expectErr: "", }, { name: "cgroup-false", dir: "./testdata/trigger-false", ctx: map[string]any{ "time_since_trigger": 3 * time.Second, }, triggerExpr: triggerExpr1, expect: false, expectErr: "", }, { name: "cgroup-true-cool", dir: "./testdata/trigger-true", ctx: map[string]any{ "time_since_trigger": 3 * time.Second, }, triggerExpr: triggerExpr1, expect: true, expectErr: "", }, { name: "cgroup-true-hot", dir: "./testdata/trigger-true", ctx: map[string]any{ "time_since_trigger": 300 * time.Millisecond, }, triggerExpr: cel.MustExpression(cel.ParseBooleanExpression( `memory_full_avg10 > 12.0 && time_since_trigger > duration("500ms")`, celenv.OOMTrigger(), )), expect: false, expectErr: "", }, { name: "cgroup-true-hot-overridden", dir: "./testdata/trigger-true", ctx: map[string]any{ "time_since_trigger": 300 * time.Millisecond, }, triggerExpr: cel.MustExpression(cel.ParseBooleanExpression( `memory_full_avg10 > 12.0 && time_since_trigger > duration("250ms")`, celenv.OOMTrigger(), )), expect: true, expectErr: "", }, { name: "test multiply_qos", ctx: map[string]any{}, dir: "./testdata/trigger-true", triggerExpr: cel.MustExpression(cel.ParseBooleanExpression( // 5 * 1 + 2 * -1 + 0 * 3 == 3 `multiply_qos_vectors({Besteffort: 5.0, Burstable: 2.0, Guaranteed: 0.0, System: 1.0}, {Besteffort: 1.0, Burstable: -1.0, Guaranteed: 3.0}) == 3.0`, celenv.OOMTrigger(), )), expect: true, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() err := oom.PopulatePsiToCtx(test.dir, test.ctx, map[string]float64{ "memory_full_avg10": 0, "memory_full_avg300": 0, "memory_full_avg60": 0, "memory_full_total": 0, "memory_some_avg10": 0, "memory_some_avg300": 0, "memory_some_avg60": 0, "memory_some_total": 0, "init/memory_full_total": 0, }, 500*time.Millisecond) if test.expectErr == "" { require.NoError(t, err) trigger, err := oom.EvaluateTrigger(test.triggerExpr, test.ctx) assert.Equal(t, test.expect, trigger) require.NoError(t, err) } else { assert.ErrorContains(t, err, test.expectErr) } }) } } func TestListCgroupProcs(t *testing.T) { t.Parallel() for _, test := range []struct { name string dir string expect []int }{ { name: "pod123", dir: "testdata/rank1/kubepods/besteffort/pod123", expect: []int{1}, }, { name: "podABC", dir: "testdata/rank1/kubepods/burstable/podABC", expect: []int{132, 142536}, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() assert.Equal(t, test.expect, oom.ListCgroupProcs(test.dir)) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/besteffort/pod123/cgroup.procs ================================================ 1 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/besteffort/pod123/memory.current ================================================ 222593024 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/besteffort/pod123/memory.max ================================================ max ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/besteffort/pod123/memory.peak ================================================ 371011584 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/burstable/podABC/cgroup.procs ================================================ 132 142536 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/burstable/podABC/memory.current ================================================ 42 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/burstable/podABC/memory.max ================================================ max ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/rank1/kubepods/burstable/podABC/memory.peak ================================================ 50 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-false/init/memory.pressure ================================================ some avg10=2.82 avg60=5.95 avg300=1.97 total=12172340 full avg10=2.40 avg60=5.16 avg300=1.71 total=106 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-false/memory.pressure ================================================ some avg10=2.82 avg60=5.95 avg300=1.97 total=12172340 full avg10=2.40 avg60=5.16 avg300=1.71 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-false/podruntime/memory.pressure ================================================ some avg10=2.82 avg60=5.95 avg300=1.97 total=12172340 full avg10=2.40 avg60=5.16 avg300=1.71 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-false/system/memory.pressure ================================================ some avg10=2.82 avg60=5.95 avg300=1.97 total=12172340 full avg10=2.40 avg60=5.16 avg300=1.71 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-true/init/memory.pressure ================================================ some avg10=17.06 avg60=8.04 avg300=2.10 total=12172340 full avg10=14.54 avg60=6.97 avg300=1.82 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-true/kubepods/besteffort/memory.pressure ================================================ some avg10=17.06 avg60=8.04 avg300=2.10 total=12172340 full avg10=14.54 avg60=6.97 avg300=1.82 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-true/memory.pressure ================================================ some avg10=17.06 avg60=8.04 avg300=2.10 total=12172340 full avg10=14.54 avg60=6.97 avg300=1.82 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-true/podruntime/memory.pressure ================================================ some avg10=17.06 avg60=8.04 avg300=2.10 total=12172340 full avg10=14.54 avg60=6.97 avg300=1.82 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/internal/oom/testdata/trigger-true/system/memory.pressure ================================================ some avg10=17.06 avg60=8.04 avg300=2.10 total=12172340 full avg10=14.54 avg60=6.97 avg300=1.82 total=10654831 ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_cmdline.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "os" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelCmdlineController presents /proc/cmdline as a resource. type KernelCmdlineController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *KernelCmdlineController) Name() string { return "runtime.KernelCmdlineController" } // Inputs implements controller.Controller interface. func (ctrl *KernelCmdlineController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KernelCmdlineController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelCmdlineType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *KernelCmdlineController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.V1Alpha1Mode.InContainer() { // no cmdline in containers return nil } select { case <-ctx.Done(): return nil case <-r.EventCh(): } contents, err := os.ReadFile("/proc/cmdline") if err != nil { return fmt.Errorf("error reading /proc/cmdline: %w", err) } if err := safe.WriterModify(ctx, r, runtime.NewKernelCmdline(), func(res *runtime.KernelCmdline) error { res.TypedSpec().Cmdline = strings.TrimSpace(string(contents)) return nil }, ); err != nil { return fmt.Errorf("error updating KernelCmdline resource: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_cmdline_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestKernelCmdlineSuite(t *testing.T) { t.Parallel() suite.Run(t, &KernelCmdlineSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrl.KernelCmdlineController{})) }, }, }) } type KernelCmdlineSuite struct { ctest.DefaultSuite } func (suite *KernelCmdlineSuite) TestKernelCmdline() { ctest.AssertResource(suite, runtime.KernelCmdlineID, func(res *runtime.KernelCmdline, asrt *assert.Assertions) { asrt.NotEmpty(res.TypedSpec().Cmdline) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_module_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelModuleConfigController watches v1alpha1.Config, creates/updates/deletes kernel module specs. type KernelModuleConfigController struct{} // Name implements controller.Controller interface. func (ctrl *KernelModuleConfigController) Name() string { return "runtime.KernelModuleConfigController" } // Inputs implements controller.Controller interface. func (ctrl *KernelModuleConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), }, } } // Outputs implements controller.Controller interface. func (ctrl *KernelModuleConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelModuleSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KernelModuleConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } r.StartTrackingOutputs() if cfg != nil && cfg.Config().Machine() != nil { for _, module := range cfg.Config().Machine().Kernel().Modules() { item := runtime.NewKernelModuleSpec(runtime.NamespaceName, module.Name()) if err = safe.WriterModify(ctx, r, item, func(res *runtime.KernelModuleSpec) error { res.TypedSpec().Name = module.Name() res.TypedSpec().Parameters = module.Parameters() return nil }); err != nil { return err } } } if err = safe.CleanupOutputs[*runtime.KernelModuleSpec](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_module_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" runtimeresource "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type KernelModuleConfigSuite struct { RuntimeSuite } func (suite *KernelModuleConfigSuite) TestReconcileConfig() { suite.Require().NoError(suite.runtime.RegisterController(&runtimecontrollers.KernelModuleConfigController{})) suite.startRuntime() cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKernel: &v1alpha1.KernelConfig{ KernelModules: []*v1alpha1.KernelModuleConfig{ { ModuleName: "btrfs", }, { ModuleName: "e1000", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) specMD := resource.NewMetadata(runtimeresource.NamespaceName, runtimeresource.KernelModuleSpecType, "e1000", resource.VersionUndefined) suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( specMD, func(res resource.Resource) bool { return res.(*runtimeresource.KernelModuleSpec).TypedSpec().Name == "e1000" }, ), )) old := cfg.Metadata().Version() cfg = config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineKernel: nil, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) cfg.Metadata().SetVersion(old) suite.Require().NoError(suite.state.Update(suite.ctx, cfg)) var err error // wait for the resource to be removed suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { for _, md := range []resource.Metadata{specMD} { _, err = suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return nil } return err } } return retry.ExpectedErrorf("resource still exists") }, )) } func TestKernelModuleConfigSuite(t *testing.T) { suite.Run(t, new(KernelModuleConfigSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_module_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/pmorjan/kmod" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelModuleSpecController watches KernelModuleSpecs, sets/resets kernel params. type KernelModuleSpecController struct { V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *KernelModuleSpecController) Name() string { return "runtime.KernelModuleSpecController" } // Inputs implements controller.Controller interface. func (ctrl *KernelModuleSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.KernelModuleSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *KernelModuleSpecController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *KernelModuleSpecController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { // not supported in container mode return nil } manager, err := kmod.New() if err != nil { return fmt.Errorf("error initializing kmod manager: %w", err) } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } modules, err := safe.ReaderListAll[*runtime.KernelModuleSpec](ctx, r) if err != nil { return err } var multiErr error // note: this code doesn't support module unloading in any way for now for module := range modules.All() { moduleSpec := module.TypedSpec() parameters := strings.Join(moduleSpec.Parameters, " ") if err = manager.Load(moduleSpec.Name, parameters, 0); err != nil { multiErr = errors.Join(multiErr, fmt.Errorf("error loading module %q: %w", moduleSpec.Name, err)) } } if multiErr != nil { return multiErr } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelParamConfigController watches v1alpha1.Config, creates/updates/deletes kernel param specs. type KernelParamConfigController struct{} // Name implements controller.Controller interface. func (ctrl *KernelParamConfigController) Name() string { return "runtime.KernelParamConfigController" } // Inputs implements controller.Controller interface. func (ctrl *KernelParamConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), }, } } // Outputs implements controller.Controller interface. func (ctrl *KernelParamConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelParamSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KernelParamConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } r.StartTrackingOutputs() setKernelParam := func(kind, key, value string) error { item := runtime.NewKernelParamSpec(runtime.NamespaceName, kind+"."+key) return safe.WriterModify(ctx, r, item, func(res *runtime.KernelParamSpec) error { res.TypedSpec().Value = value return nil }) } if cfg != nil && cfg.Config().Machine() != nil { for key, value := range cfg.Config().Machine().Sysctls() { if err = setKernelParam(kernel.Sysctl, key, value); err != nil { return err } } for key, value := range cfg.Config().Machine().Sysfs() { if err = setKernelParam(kernel.Sysfs, key, value); err != nil { return err } } } if err = safe.CleanupOutputs[*runtime.KernelParamSpec](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" runtimeresource "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type KernelParamConfigSuite struct { RuntimeSuite } func (suite *KernelParamConfigSuite) TestReconcileConfig() { suite.Require().NoError(suite.runtime.RegisterController(&runtimecontrollers.KernelParamConfigController{})) suite.startRuntime() value := "500000" valueSysfs := "600000" cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineSysctls: map[string]string{ fsFileMax: value, }, MachineSysfs: map[string]string{ fsFileMax: valueSysfs, }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) sysctlMD := resource.NewMetadata(runtimeresource.NamespaceName, runtimeresource.KernelParamSpecType, procSysfsFileMax, resource.VersionUndefined) suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( sysctlMD, func(res resource.Resource) bool { spec := res.(*runtimeresource.KernelParamSpec).TypedSpec() return suite.Assert().Equal(value, spec.Value) }, ), )) sysfsMD := resource.NewMetadata(runtimeresource.NamespaceName, runtimeresource.KernelParamSpecType, sysfsFileMax, resource.VersionUndefined) suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( sysfsMD, func(res resource.Resource) bool { spec := res.(*runtimeresource.KernelParamSpec).TypedSpec() return suite.Assert().Equal(valueSysfs, spec.Value) }, ), )) old := cfg.Metadata().Version() cfg = config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineSysctls: map[string]string{}, MachineSysfs: map[string]string{ fsFileMax: valueSysfs, }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) cfg.Metadata().SetVersion(old) suite.Require().NoError(suite.state.Update(suite.ctx, cfg)) var err error // wait for the resource to be removed suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { for _, md := range []resource.Metadata{sysctlMD} { _, err = suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return nil } return err } } return retry.ExpectedErrorf("resource still exists") }, )) } func TestKernelParamConfigSuite(t *testing.T) { suite.Run(t, new(KernelParamConfigSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_defaults.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "os" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/kernel/kspp" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelParamDefaultsController creates default kernel params. type KernelParamDefaultsController struct { V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *KernelParamDefaultsController) Name() string { return "runtime.KernelParamDefaultsController" } // Inputs implements controller.Controller interface. func (ctrl *KernelParamDefaultsController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KernelParamDefaultsController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelParamDefaultSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *KernelParamDefaultsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { select { case <-ctx.Done(): return nil case <-r.EventCh(): kernelParams := ctrl.getKernelParams() if ctrl.V1Alpha1Mode != v1alpha1runtime.ModeContainer { kernelParams = append(kernelParams, kspp.GetKernelParams()...) } for _, prop := range kernelParams { value := prop.Value item := runtime.NewKernelParamDefaultSpec(runtime.NamespaceName, prop.Key) if err := safe.WriterModify(ctx, r, item, func(res *runtime.KernelParamDefaultSpec) error { res.TypedSpec().Value = value return nil }); err != nil { return err } } } return nil } func (ctrl *KernelParamDefaultsController) getKernelParams() []*kernel.Param { res := []*kernel.Param{ { Key: "proc.sys.net.ipv4.ip_forward", Value: "1", }, { Key: "proc.sys.net.ipv4.icmp_ignore_bogus_error_responses", Value: "1", }, { Key: "proc.sys.net.ipv4.icmp_echo_ignore_broadcasts", Value: "1", }, } // block apid and trustd from the ephemeral port range res = append(res, []*kernel.Param{ { Key: "proc.sys.net.ipv4.ip_local_reserved_ports", Value: fmt.Sprintf("%d,%d", constants.ApidPort, constants.TrustdPort), }, }...) if ctrl.V1Alpha1Mode != v1alpha1runtime.ModeContainer { res = append(res, []*kernel.Param{ { Key: "proc.sys.net.bridge.bridge-nf-call-iptables", Value: "1", }, { Key: "proc.sys.net.bridge.bridge-nf-call-ip6tables", Value: "1", }, }...) } // Apply IPv6 defaults only if IPv6 is enabled. // NB: we only prevent the application of these rules if the IPv6 node does not exist. // Other errors should be ignored here so that they bubble up later, where errors can be logged and handled. _, err := os.Stat("/proc/sys/net/ipv6/conf/default/accept_ra") if err == nil || !errors.Is(err, os.ErrNotExist) { res = append(res, []*kernel.Param{ { Key: "proc.sys.net.ipv6.conf.default.forwarding", Value: "1", }, { Key: "proc.sys.net.ipv6.conf.default.accept_ra", Value: "2", }, }...) } res = append(res, []*kernel.Param{ // ipvs/conntrack tcp keepalive refresh. { Key: "proc.sys.net.ipv4.tcp_keepalive_time", Value: "600", }, { Key: "proc.sys.net.ipv4.tcp_keepalive_intvl", Value: "60", }, { Key: "proc.sys.kernel.panic", Value: "10", }, { Key: "proc.sys.kernel.pid_max", Value: "262144", }, { Key: "proc.sys.vm.overcommit_memory", Value: "1", }, }...) // kernel optimization for kubernetes workloads. res = append(res, []*kernel.Param{ // configs inotify. { Key: "proc.sys.fs.inotify.max_user_instances", Value: "8192", }, { Key: "proc.sys.fs.aio-max-nr", Value: "1048576", }, }...) return res } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_defaults_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/kernel" runtimeresource "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type KernelParamDefaultsSuite struct { ctest.DefaultSuite } func getParams(mode runtime.Mode) []*kernel.Param { res := []*kernel.Param{ { Key: "proc.sys.net.ipv4.ip_forward", Value: "1", }, { Key: "proc.sys.net.ipv6.conf.default.forwarding", Value: "1", }, { Key: "proc.sys.net.ipv6.conf.default.accept_ra", Value: "2", }, { Key: "proc.sys.kernel.panic", Value: "10", }, { Key: "proc.sys.kernel.pid_max", Value: "262144", }, { Key: "proc.sys.vm.overcommit_memory", Value: "1", }, { Key: "proc.sys.net.ipv4.ip_local_reserved_ports", Value: "50000,50001", }, } if mode != runtime.ModeContainer { res = append(res, []*kernel.Param{ { Key: "proc.sys.net.bridge.bridge-nf-call-iptables", Value: "1", }, { Key: "proc.sys.net.bridge.bridge-nf-call-ip6tables", Value: "1", }, { Key: "proc.sys.fs.protected_fifos", Value: "2", }, }...) } return res } //nolint:dupl func (suite *KernelParamDefaultsSuite) TestContainerMode() { controller := &runtimecontrollers.KernelParamDefaultsController{ runtime.ModeContainer, } suite.Require().NoError(suite.Runtime().RegisterController(controller)) for _, prop := range getParams(runtime.ModeContainer) { ctest.AssertResource(suite, prop.Key, func(param *runtimeresource.KernelParamDefaultSpec, asrt *assert.Assertions) { asrt.Equal(prop.Value, param.TypedSpec().Value) }) } } //nolint:dupl func (suite *KernelParamDefaultsSuite) TestMetalMode() { controller := &runtimecontrollers.KernelParamDefaultsController{ runtime.ModeMetal, } suite.Require().NoError(suite.Runtime().RegisterController(controller)) for _, prop := range getParams(runtime.ModeMetal) { ctest.AssertResource(suite, prop.Key, func(param *runtimeresource.KernelParamDefaultSpec, asrt *assert.Assertions) { asrt.Equal(prop.Value, param.TypedSpec().Value) }) } } func TestKernelParamDefaultsSuite(t *testing.T) { t.Parallel() suite.Run(t, &KernelParamDefaultsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "os" "slices" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-multierror" "go.uber.org/zap" krnl "github.com/siderolabs/talos/pkg/kernel" "github.com/siderolabs/talos/pkg/kernel/kspp" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelParamSpecController watches KernelParamSpecs, sets/resets kernel params. type KernelParamSpecController struct { defaults map[string]string state map[string]string } // Name implements controller.Controller interface. func (ctrl *KernelParamSpecController) Name() string { return "runtime.KernelParamSpecController" } // Inputs implements controller.Controller interface. func (ctrl *KernelParamSpecController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.KernelParamDefaultSpecType, Kind: controller.InputStrong, }, { Namespace: runtime.NamespaceName, Type: runtime.KernelParamSpecType, Kind: controller.InputStrong, }, } } // Outputs implements controller.Controller interface. func (ctrl *KernelParamSpecController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KernelParamStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KernelParamSpecController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.state == nil { ctrl.state = map[string]string{} } if ctrl.defaults == nil { ctrl.defaults = map[string]string{} } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): ksppParams := map[string]struct{}{} for _, param := range kspp.GetKernelParams() { ksppParams[param.Key] = struct{}{} } defaults, err := r.List(ctx, resource.NewMetadata(runtime.NamespaceName, runtime.KernelParamDefaultSpecType, "", resource.VersionUndefined)) if err != nil { return err } configs, err := r.List(ctx, resource.NewMetadata(runtime.NamespaceName, runtime.KernelParamSpecType, "", resource.VersionUndefined)) if err != nil { return err } configsCounts := len(configs.Items) list := slices.Concat(configs.Items, defaults.Items) touchedIDs := map[string]string{} var errs *multierror.Error for i, item := range list { spec := item.(runtime.KernelParam).TypedSpec() id := item.Metadata().ID() if value, duplicate := touchedIDs[id]; i >= configsCounts && duplicate { if _, ok := ksppParams[id]; ok { logger.Warn("overriding KSPP enforced parameter, this is not recommended", zap.String("key", id), zap.String("value", value)) } continue } if err = ctrl.updateKernelParam(ctx, r, id, spec.Value); err != nil { if errors.Is(err, os.ErrNotExist) && spec.IgnoreErrors { status := runtime.NewKernelParamStatus(runtime.NamespaceName, id) if e := safe.WriterModify(ctx, r, status, func(res *runtime.KernelParamStatus) error { res.TypedSpec().Unsupported = true return nil }); e != nil { errs = multierror.Append(errs, e) } } else { errs = multierror.Append(errs, err) } continue } touchedIDs[id] = spec.Value } for key := range ctrl.state { if _, ok := touchedIDs[key]; ok { continue } if err = ctrl.resetKernelParam(ctx, r, key); err != nil { errs = multierror.Append(errs, err) } } if errs != nil { return errs } } r.ResetRestartBackoff() } } func (ctrl *KernelParamSpecController) updateKernelParam(ctx context.Context, r controller.Runtime, key, value string) error { prop := &kernel.Param{Key: key, Value: value} if _, ok := ctrl.defaults[key]; !ok { if data, err := krnl.ReadParam(prop); err == nil { ctrl.defaults[key] = string(data) } else if !errors.Is(err, os.ErrNotExist) { return err } } if err := krnl.WriteParam(prop); err != nil { return err } ctrl.state[key] = value status := runtime.NewKernelParamStatus(runtime.NamespaceName, key) return safe.WriterModify(ctx, r, status, func(res *runtime.KernelParamStatus) error { res.TypedSpec().Current = value res.TypedSpec().Default = strings.TrimSpace(ctrl.defaults[key]) return nil }) } func (ctrl *KernelParamSpecController) resetKernelParam(ctx context.Context, r controller.Runtime, key string) error { var err error if def, ok := ctrl.defaults[key]; ok { err = krnl.WriteParam(&kernel.Param{Key: key, Value: def}) } else { err = krnl.DeleteParam(&kernel.Param{Key: key}) } if err != nil { return err } delete(ctrl.defaults, key) delete(ctrl.state, key) return r.Destroy(ctx, resource.NewMetadata(runtime.NamespaceName, runtime.KernelParamStatusType, key, resource.VersionUndefined)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kernel_param_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "os" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" krnl "github.com/siderolabs/talos/pkg/kernel" "github.com/siderolabs/talos/pkg/machinery/kernel" runtimeresource "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type KernelParamSpecSuite struct { RuntimeSuite } func (suite *KernelParamSpecSuite) TestParamsSynced() { suite.Require().NoError(suite.runtime.RegisterController(&runtimecontrollers.KernelParamSpecController{})) suite.startRuntime() value := "500000" def := "" spec := runtimeresource.NewKernelParamSpec(runtimeresource.NamespaceName, procSysfsFileMax) spec.TypedSpec().Value = value param := &kernel.Param{Key: procSysfsFileMax} suite.Require().NoError(suite.state.Create(suite.ctx, spec)) statusMD := resource.NewMetadata(runtimeresource.NamespaceName, runtimeresource.KernelParamStatusType, procSysfsFileMax, resource.VersionUndefined) suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( statusMD, func(res resource.Resource) bool { def = res.(*runtimeresource.KernelParamStatus).TypedSpec().Default return res.(*runtimeresource.KernelParamStatus).TypedSpec().Current == value }, ), )) prop, err := krnl.ReadParam(param) suite.Assert().NoError(err) suite.Require().Equal(value, strings.TrimSpace(string(prop))) suite.Require().NoError(suite.state.Destroy(suite.ctx, spec.Metadata())) // wait for the resource to be removed suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { for _, md := range []resource.Metadata{statusMD} { _, err = suite.state.Get(suite.ctx, md) if err != nil { if state.IsNotFoundError(err) { return nil } return err } } return retry.ExpectedErrorf("resource still exists") }, )) prop, err = krnl.ReadParam(param) suite.Assert().NoError(err) suite.Require().Equal(def, strings.TrimSpace(string(prop))) } func (suite *KernelParamSpecSuite) TestParamsUnsupported() { suite.Require().NoError(suite.runtime.RegisterController(&runtimecontrollers.KernelParamSpecController{})) suite.startRuntime() id := "proc.sys.some.really.not.existing.sysctl" spec := runtimeresource.NewKernelParamSpec(runtimeresource.NamespaceName, id) spec.TypedSpec().Value = "value" spec.TypedSpec().IgnoreErrors = true suite.Require().NoError(suite.state.Create(suite.ctx, spec)) statusMD := resource.NewMetadata(runtimeresource.NamespaceName, runtimeresource.KernelParamStatusType, id, resource.VersionUndefined) suite.Assert().NoError(retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( suite.assertResource( statusMD, func(res resource.Resource) bool { return res.(*runtimeresource.KernelParamStatus).TypedSpec().Unsupported == true }, ), )) } func TestKernelParamSpecSuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("skipping test because it requires root privileges") } suite.Run(t, new(KernelParamSpecSuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kmsg_log.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "net/url" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-kmsg" "go.uber.org/zap" "go.uber.org/zap/zapcore" networkutils "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/utils" machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) const ( drainTimeout = 100 * time.Millisecond logSendTimeout = 5 * time.Second logRetryTimeout = 1 * time.Second logCloseTimeout = 5 * time.Second ) // KmsgLogDeliveryController watches events and forwards them to the events sink server // if it's configured. type KmsgLogDeliveryController struct { Drainer *machinedruntime.Drainer drainSub *machinedruntime.DrainSubscription } // Name implements controller.Controller interface. func (ctrl *KmsgLogDeliveryController) Name() string { return "runtime.KmsgLogDeliveryController" } // Inputs implements controller.Controller interface. func (ctrl *KmsgLogDeliveryController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KmsgLogDeliveryController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *KmsgLogDeliveryController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if err := networkutils.WaitForNetworkReady(ctx, r, func(status *network.StatusSpec) bool { return status.AddressReady }, []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.KmsgLogConfigType, ID: optional.Some(runtime.KmsgLogConfigID), Kind: controller.InputWeak, }, }, ); err != nil { return fmt.Errorf("error waiting for network: %w", err) } // initilalize kmsg reader early, so that we don't lose position on config changes reader, err := kmsg.NewReader(kmsg.Follow()) if err != nil { return fmt.Errorf("error reading kernel messages: %w", err) } defer reader.Close() //nolint:errcheck kmsgCh := reader.Scan(ctx) for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*runtime.KmsgLogConfig](ctx, r, runtime.KmsgLogConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting configuration: %w", err) } if cfg == nil { // no config, wait for the next event continue } if err = ctrl.deliverLogs(ctx, r, logger, kmsgCh, cfg.TypedSpec().Destinations); err != nil { return fmt.Errorf("error delivering logs: %w", err) } r.ResetRestartBackoff() } } type logConfig struct { endpoint *url.URL } func (c logConfig) Format() string { return constants.LoggingFormatJSONLines } func (c logConfig) Endpoint() *url.URL { return c.endpoint } func (c logConfig) ExtraTags() map[string]string { return nil } //nolint:gocyclo func (ctrl *KmsgLogDeliveryController) deliverLogs(ctx context.Context, r controller.Runtime, logger *zap.Logger, kmsgCh <-chan kmsg.Packet, destURLs []*url.URL) error { if ctrl.drainSub == nil { ctrl.drainSub = ctrl.Drainer.Subscribe() } // initialize all log senders destLogConfigs := xslices.Map(destURLs, func(u *url.URL) config.LoggingDestination { return logConfig{endpoint: u} }) senders := xslices.Map(destLogConfigs, logging.NewJSONLines) defer func() { closeCtx, closeCtxCancel := context.WithTimeout(context.Background(), logCloseTimeout) defer closeCtxCancel() for _, sender := range senders { if err := sender.Close(closeCtx); err != nil { logger.Error("error closing log sender", zap.Error(err)) } } }() var ( drainTimer *time.Timer drainTimerCh <-chan time.Time ) for { var msg kmsg.Packet select { case <-ctx.Done(): ctrl.drainSub.Cancel() return nil case <-r.EventCh(): // config changed, restart the loop return nil case <-ctrl.drainSub.EventCh(): // drain started, assume that ksmg is drained if there're no new messages in drainTimeout drainTimer = time.NewTimer(drainTimeout) drainTimerCh = drainTimer.C continue case <-drainTimerCh: ctrl.drainSub.Cancel() return nil case msg = <-kmsgCh: if drainTimer != nil { // if draining, reset the timer as there's a new message if !drainTimer.Stop() { <-drainTimer.C } drainTimer.Reset(drainTimeout) } } if msg.Err != nil { return fmt.Errorf("error receiving kernel logs: %w", msg.Err) } event := machinedruntime.LogEvent{ Msg: msg.Message.Message, Time: msg.Message.Timestamp, Level: kmsgPriorityToLevel(msg.Message.Priority), Fields: map[string]any{ "facility": msg.Message.Facility.String(), "seq": msg.Message.SequenceNumber, "clock": msg.Message.Clock, "priority": msg.Message.Priority.String(), }, } if err := ctrl.resend(ctx, r, logger, senders, &event); err != nil { return fmt.Errorf("error sending log event: %w", err) } } } //nolint:gocyclo func (ctrl *KmsgLogDeliveryController) resend(ctx context.Context, r controller.Runtime, logger *zap.Logger, senders []machinedruntime.LogSender, e *machinedruntime.LogEvent) error { for { sendCtx, sendCancel := context.WithTimeout(ctx, logSendTimeout) sendErrors := make(chan error, len(senders)) for _, sender := range senders { go func() { sendErrors <- sender.Send(sendCtx, e) }() } var dontRetry bool for range senders { err := <-sendErrors // don't retry if at least one sender succeed to avoid implementing per-sender queue, etc if err == nil { dontRetry = true continue } logger.Debug("error sending log event", zap.Error(err)) if errors.Is(err, machinedruntime.ErrDontRetry) || errors.Is(err, context.Canceled) { dontRetry = true } } sendCancel() if dontRetry { return nil } select { case <-ctx.Done(): return nil case <-r.EventCh(): // config changed, restart the loop return errors.New("config changed") case <-time.After(logRetryTimeout): } } } func kmsgPriorityToLevel(pri kmsg.Priority) zapcore.Level { switch pri { case kmsg.Alert, kmsg.Crit, kmsg.Emerg, kmsg.Err: return zapcore.ErrorLevel case kmsg.Debug: return zapcore.DebugLevel case kmsg.Info, kmsg.Notice: return zapcore.InfoLevel case kmsg.Warning: return zapcore.WarnLevel default: return zapcore.ErrorLevel } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kmsg_log_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "net/url" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KmsgLogConfigController generates configuration for kmsg log delivery. type KmsgLogConfigController struct { Cmdline *procfs.Cmdline } // Name implements controller.Controller interface. func (ctrl *KmsgLogConfigController) Name() string { return "runtime.KmsgLogConfigController" } // Inputs implements controller.Controller interface. func (ctrl *KmsgLogConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KmsgLogConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.KmsgLogConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KmsgLogConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) (err error) { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } var destinations []*url.URL if ctrl.Cmdline != nil { if val := ctrl.Cmdline.Get(constants.KernelParamLoggingKernel).First(); val != nil { destURL, err := url.Parse(*val) if err != nil { return fmt.Errorf("error parsing %q: %w", constants.KernelParamLoggingKernel, err) } destinations = append(destinations, destURL) } } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } if cfg != nil { // remove duplicate URLs in case same destination is specified in both machine config and kernel args destinations = append(destinations, xslices.Filter(cfg.Config().Runtime().KmsgLogURLs(), func(u *url.URL) bool { return !slices.ContainsFunc(destinations, func(v *url.URL) bool { return v.String() == u.String() }) })...) } r.StartTrackingOutputs() if len(destinations) > 0 { if err = safe.WriterModify(ctx, r, runtime.NewKmsgLogConfig(), func(cfg *runtime.KmsgLogConfig) error { cfg.TypedSpec().Destinations = destinations return nil }); err != nil { return fmt.Errorf("error updating kmsg log config: %w", err) } } if err = safe.CleanupOutputs[*runtime.KmsgLogConfig](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kmsg_log_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "net/url" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type KmsgLogConfigSuite struct { ctest.DefaultSuite } func TestKmsgLogConfigSuite(t *testing.T) { suite.Run(t, new(KmsgLogConfigSuite)) } func (suite *KmsgLogConfigSuite) TestKmsgLogConfigNone() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.KmsgLogConfigController{})) rtestutils.AssertNoResource[*runtime.KmsgLogConfig](suite.Ctx(), suite.T(), suite.State(), runtime.KmsgLogConfigID) } func (suite *KmsgLogConfigSuite) TestKmsgLogConfigMachineConfig() { cmdline := procfs.NewCmdline("") cmdline.Append(constants.KernelParamLoggingKernel, "https://10.0.0.1:3333/logs") suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.KmsgLogConfigController{ Cmdline: cmdline, })) kmsgLogConfig1 := &runtimecfg.KmsgLogV1Alpha1{ MetaName: "1", KmsgLogURL: meta.URL{ URL: must(url.Parse("https://10.0.0.2:4444/logs")), }, } kmsgLogConfig2 := &runtimecfg.KmsgLogV1Alpha1{ MetaName: "2", KmsgLogURL: meta.URL{ URL: must(url.Parse("https://10.0.0.1:3333/logs")), }, } cfg, err := container.New(kmsgLogConfig1, kmsgLogConfig2) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources[*runtime.KmsgLogConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.KmsgLogConfigID}, func(cfg *runtime.KmsgLogConfig, asrt *assert.Assertions) { asrt.Equal( []string{ "https://10.0.0.1:3333/logs", "https://10.0.0.2:4444/logs", }, xslices.Map(cfg.TypedSpec().Destinations, func(u *url.URL) string { return u.String() }), ) }) } func (suite *KmsgLogConfigSuite) TestKmsgLogConfigCmdline() { cmdline := procfs.NewCmdline("") cmdline.Append(constants.KernelParamLoggingKernel, "https://10.0.0.1:3333/logs") suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.KmsgLogConfigController{ Cmdline: cmdline, })) rtestutils.AssertResources[*runtime.KmsgLogConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.KmsgLogConfigID}, func(cfg *runtime.KmsgLogConfig, asrt *assert.Assertions) { asrt.Equal( []string{"https://10.0.0.1:3333/logs"}, xslices.Map(cfg.TypedSpec().Destinations, func(u *url.URL) string { return u.String() }), ) }) } func must[T any](t T, err error) T { if err != nil { panic(err) } return t } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kmsg_log_storage.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/siderolabs/go-kmsg" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // KmsgLogStorageController presents kernel message log as a 'kernel' log. type KmsgLogStorageController struct { V1Alpha1Logging runtime.LoggingManager V1Alpha1Mode runtime.Mode } // Name implements controller.Controller interface. func (ctrl *KmsgLogStorageController) Name() string { return "runtime.KmsgLogStorageController" } // Inputs implements controller.Controller interface. func (ctrl *KmsgLogStorageController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KmsgLogStorageController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KmsgLogStorageController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.V1Alpha1Mode.InContainer() { return nil } var err error logWriter, err := ctrl.V1Alpha1Logging.ServiceLog("kernel").Writer() if err != nil { return fmt.Errorf("error opening logger: %w", err) } defer logWriter.Close() //nolint:errcheck // initilalize kmsg reader early, so that we don't lose position on config changes reader, err := kmsg.NewReader(kmsg.Follow()) if err != nil { return fmt.Errorf("error reading kernel messages: %w", err) } defer reader.Close() //nolint:errcheck kmsgCh := reader.Scan(ctx) // wait for the initial event to start processing messages select { case <-ctx.Done(): return nil case <-r.EventCh(): } for { var msg kmsg.Packet select { case <-ctx.Done(): return nil case msg = <-kmsgCh: } if msg.Err != nil { return fmt.Errorf("error receiving kernel logs: %w", msg.Err) } if _, err = logWriter.Write( fmt.Appendf(nil, "%s: %7s: [%s]: %s", msg.Message.Facility, msg.Message.Priority, msg.Message.Timestamp.Format(time.RFC3339Nano), msg.Message.Message), ); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/kmsg_log_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "net" "net/netip" "net/url" "os" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/siderolink/pkg/logreceiver" "github.com/stretchr/testify/suite" "go.uber.org/zap/zaptest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" talosruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type logHandler struct { mu sync.Mutex count int } // HandleLog implements logreceiver.Handler. func (s *logHandler) HandleLog(srcAddr netip.Addr, msg map[string]any) { s.mu.Lock() defer s.mu.Unlock() s.count++ } func (s *logHandler) getCount() int { s.mu.Lock() defer s.mu.Unlock() return s.count } type KmsgLogDeliverySuite struct { suite.Suite state state.State runtime *runtime.Runtime drainer *talosruntime.Drainer wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc handler1, handler2 *logHandler listener1, listener2 net.Listener srv1, srv2 *logreceiver.Server } func (suite *KmsgLogDeliverySuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 10*time.Second) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) logger := zaptest.NewLogger(suite.T()) var err error suite.runtime, err = runtime.NewRuntime(suite.state, logger) suite.Require().NoError(err) suite.handler1 = &logHandler{} suite.handler2 = &logHandler{} suite.listener1, err = (&net.ListenConfig{}).Listen(suite.ctx, "tcp", "localhost:0") suite.Require().NoError(err) suite.listener2, err = (&net.ListenConfig{}).Listen(suite.ctx, "tcp", "localhost:0") suite.Require().NoError(err) suite.srv1 = logreceiver.NewServer(logger, suite.listener1, suite.handler1.HandleLog) suite.srv2 = logreceiver.NewServer(logger, suite.listener2, suite.handler2.HandleLog) suite.wg.Go(func() { suite.srv1.Serve() //nolint:errcheck }) suite.wg.Go(func() { suite.srv2.Serve() //nolint:errcheck }) suite.drainer = talosruntime.NewDrainer() suite.Require().NoError( suite.runtime.RegisterController( &runtimectrl.KmsgLogDeliveryController{ Drainer: suite.drainer, }, ), ) status := network.NewStatus(network.NamespaceName, network.StatusID) status.TypedSpec().AddressReady = true suite.Require().NoError(suite.state.Create(suite.ctx, status)) } func (suite *KmsgLogDeliverySuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *KmsgLogDeliverySuite) TestDeliverySingleDestination() { suite.startRuntime() kmsgLogConfig := runtimeres.NewKmsgLogConfig() kmsgLogConfig.TypedSpec().Destinations = []*url.URL{ { Scheme: "tcp", Host: suite.listener1.Addr().String(), }, } suite.Require().NoError(suite.state.Create(suite.ctx, kmsgLogConfig)) // controller should deliver some kernel logs from host's kmsg buffer suite.assertLogsSeen(suite.handler1) } func (suite *KmsgLogDeliverySuite) TestDeliveryMultipleDestinations() { suite.startRuntime() kmsgLogConfig := runtimeres.NewKmsgLogConfig() kmsgLogConfig.TypedSpec().Destinations = []*url.URL{ { Scheme: "tcp", Host: suite.listener1.Addr().String(), }, { Scheme: "tcp", Host: suite.listener2.Addr().String(), }, } suite.Require().NoError(suite.state.Create(suite.ctx, kmsgLogConfig)) // controller should deliver logs to both destinations suite.assertLogsSeen(suite.handler1) suite.assertLogsSeen(suite.handler2) } func (suite *KmsgLogDeliverySuite) TestDeliveryOneDeadDestination() { suite.startRuntime() // stop one listener suite.Require().NoError(suite.listener1.Close()) kmsgLogConfig := runtimeres.NewKmsgLogConfig() kmsgLogConfig.TypedSpec().Destinations = []*url.URL{ { Scheme: "tcp", Host: suite.listener1.Addr().String(), }, { Scheme: "tcp", Host: suite.listener2.Addr().String(), }, } suite.Require().NoError(suite.state.Create(suite.ctx, kmsgLogConfig)) // controller should deliver logs to live destination suite.assertLogsSeen(suite.handler2) } func (suite *KmsgLogDeliverySuite) TestDeliveryAllDeadDestinations() { suite.startRuntime() // stop all listeners suite.Require().NoError(suite.listener1.Close()) suite.Require().NoError(suite.listener2.Close()) kmsgLogConfig := runtimeres.NewKmsgLogConfig() kmsgLogConfig.TypedSpec().Destinations = []*url.URL{ { Scheme: "tcp", Host: suite.listener1.Addr().String(), }, { Scheme: "tcp", Host: suite.listener2.Addr().String(), }, } suite.Require().NoError(suite.state.Create(suite.ctx, kmsgLogConfig)) } func (suite *KmsgLogDeliverySuite) TestDrain() { suite.startRuntime() kmsgLogConfig := runtimeres.NewKmsgLogConfig() kmsgLogConfig.TypedSpec().Destinations = []*url.URL{ { Scheme: "tcp", Host: suite.listener1.Addr().String(), }, } suite.Require().NoError(suite.state.Create(suite.ctx, kmsgLogConfig)) // wait for controller to start delivering some logs suite.assertLogsSeen(suite.handler1) // drain should be successful, i.e. controller should stop on its own before context is canceled suite.Assert().NoError(suite.drainer.Drain(suite.ctx)) } func (suite *KmsgLogDeliverySuite) assertLogsSeen(handler *logHandler) { err := retry.Constant(time.Second*5, retry.WithUnits(time.Millisecond*100)).Retry( func() error { if handler.getCount() == 0 { return retry.ExpectedErrorf("no logs received") } return nil }, ) suite.Require().NoError(err) } func (suite *KmsgLogDeliverySuite) TearDownTest() { suite.srv1.Stop() suite.srv2.Stop() suite.ctxCancel() suite.wg.Wait() } func TestKmsgLogDeliverySuite(t *testing.T) { if os.Geteuid() != 0 { t.Skip("requires root") } suite.Run(t, new(KmsgLogDeliverySuite)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/loaded_kernel_module.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "bufio" "context" "fmt" "io" "os" "slices" "strconv" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/filehash" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // LoadedKernelModuleController presents /proc/modules as a resource. type LoadedKernelModuleController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *LoadedKernelModuleController) Name() string { return "runtime.LoadedKernelModuleController" } // Inputs implements controller.Controller interface. func (ctrl *LoadedKernelModuleController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *LoadedKernelModuleController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.LoadedKernelModuleType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *LoadedKernelModuleController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.V1Alpha1Mode.InContainer() { // no modules in containers return nil } watcher, err := filehash.NewWatcher("/proc/modules") if err != nil { return fmt.Errorf("error creating filehash watcher: %w", err) } notifyCh, notifyErrCh := watcher.Run() defer watcher.Close() //nolint:errcheck for { select { case updatedPath := <-notifyCh: if err := ctrl.reconcile(ctx, r, updatedPath); err != nil { return fmt.Errorf("error reconciling LoadedKernelModule resources: %w", err) } case err = <-notifyErrCh: return fmt.Errorf("error watching /proc/modules: %w", err) case <-ctx.Done(): return nil case <-r.EventCh(): } } } // Module represents a kernel module parsed from /proc/modules. type Module struct { Name string Size int ReferenceCount int Dependencies []string State string Address string } // ParseModules parses the contents of /proc/modules from the given reader // and returns a slice of module structs representing each loaded kernel module. func ParseModules(r io.Reader) ([]Module, error) { var modules []Module scanner := bufio.NewScanner(r) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 6 { continue // malformed line } name := fields[0] size, err := strconv.Atoi(fields[1]) if err != nil { return nil, fmt.Errorf("invalid size for module %s: %v", name, err) } refCount, err := strconv.Atoi(fields[2]) if err != nil { return nil, fmt.Errorf("invalid instance count for module %s: %v", name, err) } deps := []string{} if fields[3] != "-" { deps = slices.DeleteFunc( strings.Split(fields[3], ","), func(s string) bool { return s == "" }, ) } modules = append(modules, Module{ Name: name, Size: size, Dependencies: deps, ReferenceCount: refCount, State: fields[4], Address: fields[5], }) } if err := scanner.Err(); err != nil { return nil, fmt.Errorf("error scanning modules: %w", err) } return modules, nil } func (ctrl *LoadedKernelModuleController) reconcile(ctx context.Context, r controller.Runtime, path string) error { r.StartTrackingOutputs() f, err := os.OpenFile(path, os.O_RDONLY, 0) if err != nil { return fmt.Errorf("error opening %s: %w", path, err) } rawModules, err := ParseModules(f) if err != nil { return fmt.Errorf("error parsing modules from %s: %w", path, err) } // create a map to track which modules were touched for _, module := range rawModules { if err := safe.WriterModify(ctx, r, runtime.NewLoadedKernelModule(runtime.NamespaceName, module.Name), func(res *runtime.LoadedKernelModule) error { res.TypedSpec().Size = module.Size res.TypedSpec().ReferenceCount = module.ReferenceCount res.TypedSpec().Dependencies = module.Dependencies res.TypedSpec().State = module.State res.TypedSpec().Address = module.Address return nil }, ); err != nil { return fmt.Errorf("error updating LoadedKernelModule resource: %w", err) } } return safe.CleanupOutputs[*runtime.LoadedKernelModule](ctx, r) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/loaded_kernel_module_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "strings" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" ) func TestLoadedKernelModuleSuite(t *testing.T) { t.Parallel() suite.Run(t, &LoadedKernelModuleSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrl.LoadedKernelModuleController{})) }, }, }) } type LoadedKernelModuleSuite struct { ctest.DefaultSuite } func (suite *LoadedKernelModuleSuite) TestParseModules() { for _, tc := range []struct { name string input string expected []runtimectrl.Module }{ { name: "empty", input: "", expected: nil, }, { name: "single module", input: `module1 12345 0 - Live 0x00000000`, expected: []runtimectrl.Module{ {Name: "module1", Size: 12345, ReferenceCount: 0, Dependencies: []string{}, State: "Live", Address: "0x00000000"}, }, }, { name: "multiple modules", input: `module1 12345 0 - Live 0x00000000 module2 67890 1 module1 Live 0x00000001 module3 54321 2 module1,module2 Live 0x00000002`, expected: []runtimectrl.Module{ {Name: "module1", Size: 12345, ReferenceCount: 0, Dependencies: []string{}, State: "Live", Address: "0x00000000"}, {Name: "module2", Size: 67890, ReferenceCount: 1, Dependencies: []string{"module1"}, State: "Live", Address: "0x00000001"}, {Name: "module3", Size: 54321, ReferenceCount: 2, Dependencies: []string{"module1", "module2"}, State: "Live", Address: "0x00000002"}, }, }, { name: "malformed lines", input: `module1 12345 0 - Live 0x00000000 module2 67890 1 module1 Live 0x00000001 module3 54321 2 module1,module2 Live 0x00000002 invalid_line module4 11111 0 - Live 0x00000003`, expected: []runtimectrl.Module{ {Name: "module1", Size: 12345, ReferenceCount: 0, Dependencies: []string{}, State: "Live", Address: "0x00000000"}, {Name: "module2", Size: 67890, ReferenceCount: 1, Dependencies: []string{"module1"}, State: "Live", Address: "0x00000001"}, {Name: "module3", Size: 54321, ReferenceCount: 2, Dependencies: []string{"module1", "module2"}, State: "Live", Address: "0x00000002"}, {Name: "module4", Size: 11111, ReferenceCount: 0, Dependencies: []string{}, State: "Live", Address: "0x00000003"}, }, }, } { suite.Run(tc.name, func() { modules, err := runtimectrl.ParseModules(strings.NewReader(tc.input)) suite.Require().NoError(err) suite.Require().Equal(tc.expected, modules) }) } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/log_persistence.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "path/filepath" "sync" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/concurrent" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/logfile" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // LogPersistenceController is a controller that persists logs in files. type LogPersistenceController struct { V1Alpha1Logging runtime.LoggingManager startup sync.Once // RLocked by the log writers, Locked by volume handlers canLog sync.RWMutex files *concurrent.HashTrieMap[string, *logfile.LogFile] logMountPoint string } // Name implements controller.Controller interface. func (ctrl *LogPersistenceController) Name() string { return "runtime.LogPersistenceController" } // Inputs implements controller.Controller interface. func (ctrl *LogPersistenceController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *LogPersistenceController) Outputs() []controller.Output { return []controller.Output{ { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // WriteLog writes a single log line into the corresponding file. func (ctrl *LogPersistenceController) WriteLog(id string, line []byte) error { ctrl.canLog.RLock() defer ctrl.canLog.RUnlock() lf, _ := ctrl.files.LoadOrStore( id, logfile.NewLogFile( filepath.Join(ctrl.logMountPoint, id+".log"), constants.LogRotateThreshold, ), ) return lf.Write(line) } func (ctrl *LogPersistenceController) startLogging(vms *block.VolumeMountStatus) { // here we can start logging activities ctrl.logMountPoint = vms.TypedSpec().Target ctrl.canLog.Unlock() } func (ctrl *LogPersistenceController) stopLogging() error { // Stop all logging activities, close files // after this call we should not hold /var/log ctrl.canLog.Lock() for _, f := range ctrl.files.All() { if err := f.Close(); err != nil { return fmt.Errorf("failed to close log buffer %w", err) } } ctrl.files.Clear() return nil } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *LogPersistenceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { ctrl.startup.Do(func() { ctrl.files = concurrent.NewHashTrieMap[string, *logfile.LogFile]() // Block writes until /var/log is ready ctrl.canLog.Lock() ctrl.V1Alpha1Logging.SetLineWriter(ctrl) }) ticker := time.NewTicker(constants.LogFlushPeriod) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-ticker.C: for _, f := range ctrl.files.All() { if err := f.Flush(); err != nil { return fmt.Errorf("failed to flush log buffer %w", err) } } continue case <-r.EventCh(): } requestID := ctrl.Name() + "-" + constants.LogMountPoint // create a volume mount request for the logs volume mount point // to keep it alive and prevent it from being torn down if err := safe.WriterModify(ctx, r, block.NewVolumeMountRequest(block.NamespaceName, requestID), func(v *block.VolumeMountRequest) error { v.TypedSpec().Requester = ctrl.Name() v.TypedSpec().VolumeID = constants.LogMountPoint return nil }, ); err != nil { return fmt.Errorf("error creating volume mount request for user volume mount point: %w", err) } vms, err := safe.ReaderGetByID[*block.VolumeMountStatus](ctx, r, requestID) if err != nil { if state.IsNotFoundError(err) { // volume mount not ready yet, wait more continue } return fmt.Errorf("error getting volume mount status for log volume: %w", err) } switch vms.Metadata().Phase() { case resource.PhaseRunning: if !vms.Metadata().Finalizers().Has(ctrl.Name()) { if err = r.AddFinalizer(ctx, vms.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error adding finalizer to volume mount status for log volume: %w", err) } ctrl.startLogging(vms) } case resource.PhaseTearingDown: if vms.Metadata().Finalizers().Has(ctrl.Name()) { if err = ctrl.stopLogging(); err != nil { return fmt.Errorf("error stopping persistent logging: %w", err) } if err = r.RemoveFinalizer(ctx, vms.Metadata(), ctrl.Name()); err != nil { return fmt.Errorf("error removing finalizer from volume mount status for log volume: %w", err) } } } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/log_persistence_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestLogPersistenceSuite(t *testing.T) { t.Parallel() suite.Run(t, &LogPersistenceSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, }, }) } type LogPersistenceSuite struct { ctest.DefaultSuite } type loggingMock struct{} func (loggingMock) ServiceLog(service string) runtime.LogHandler { return nil } func (loggingMock) SetSenders(senders []runtime.LogSender) []runtime.LogSender { return nil } func (loggingMock) SetLineWriter(w runtime.LogWriter) {} func (loggingMock) RegisteredLogs() []string { return nil } func (suite *LogPersistenceSuite) TestDefault() { ctrl := &runtimectrl.LogPersistenceController{ V1Alpha1Logging: loggingMock{}, } suite.Require().NoError(suite.Runtime().RegisterController(ctrl)) requestID := ctrl.Name() + "-" + constants.LogMountPoint ctest.AssertResource(suite, requestID, func(*block.VolumeMountRequest, *assert.Assertions) {}) errCh := make(chan error, 1) go func() { errCh <- ctrl.WriteLog("service1", []byte("line1")) }() select { case <-errCh: suite.Fail("expected WriteLog to block") case <-time.After(10 * time.Millisecond): // expected } logDir := suite.T().TempDir() vms := block.NewVolumeMountStatus(block.NamespaceName, requestID) vms.TypedSpec().Target = logDir suite.Create(vms) ctest.AssertResource(suite, requestID, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Has(ctrl.Name())) }) select { case err := <-errCh: suite.NoError(err) case <-time.After(500 * time.Millisecond): suite.Fail("expected WriteLog to complete after mount") } suite.Assert().FileExists(filepath.Join(logDir, "service1.log")) _, err := suite.State().Teardown(suite.Ctx(), vms.Metadata()) suite.Require().NoError(err) ctest.AssertResource(suite, requestID, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.False(vms.Metadata().Finalizers().Has(ctrl.Name())) }) st, err := os.Stat(filepath.Join(logDir, "service1.log")) suite.Require().NoError(err) suite.Assert().Equal(int64(6), st.Size()) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/machine_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "strings" "sync" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" v1 "k8s.io/api/core/v1" k8sadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/k8s" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // MachineStatusController watches MachineStatuss, sets/resets kernel params. type MachineStatusController struct { V1Alpha1Events v1alpha1runtime.Watcher setupOnce sync.Once notifyOnce sync.Once notifyCh chan struct{} mu sync.Mutex currentStage runtime.MachineStage } // Name implements controller.Controller interface. func (ctrl *MachineStatusController) Name() string { return "runtime.MachineStatusController" } // Inputs implements controller.Controller interface. func (ctrl *MachineStatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: time.StatusType, ID: optional.Some(time.StatusID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.StaticPodStatusType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodenameType, ID: optional.Some(k8s.NodenameID), Kind: controller.InputWeak, }, { Namespace: k8s.NamespaceName, Type: k8s.NodeStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MachineStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.MachineStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *MachineStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { ctrl.setupOnce.Do(func() { // watcher is started once and runs for all controller runs, as if we reconnect to the event stream, // we might lose some state which was in the events, but it got "scrolled away" from the buffer. ctrl.notifyCh = make(chan struct{}, 1) go ctrl.watchEvents() }) for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ctrl.notifyCh: } machineTypeResource, err := safe.ReaderGet[*config.MachineType](ctx, r, config.NewMachineType().Metadata()) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine type: %w", err) } } var machineType machine.Type if machineTypeResource != nil { machineType = machineTypeResource.MachineType() } ctrl.mu.Lock() currentStage := ctrl.currentStage ctrl.mu.Unlock() ready := true var unmetConditions []runtime.UnmetCondition for _, check := range ctrl.getReadinessChecks(currentStage, machineType) { if err := check.f(ctx, r); err != nil { ready = false unmetConditions = append(unmetConditions, runtime.UnmetCondition{ Name: check.name, Reason: err.Error(), }) } } if err := safe.WriterModify(ctx, r, runtime.NewMachineStatus(), func(ms *runtime.MachineStatus) error { ms.TypedSpec().Stage = currentStage ms.TypedSpec().Status.Ready = ready ms.TypedSpec().Status.UnmetConditions = unmetConditions return nil }); err != nil { return fmt.Errorf("error updating machine status: %w", err) } if currentStage == runtime.MachineStageRunning && ready { ctrl.notifyOnce.Do(func() { logger.Info("machine is running and ready") }) } r.ResetRestartBackoff() } } type readinessCheck struct { name string f func(context.Context, controller.Runtime) error } func (ctrl *MachineStatusController) getReadinessChecks(stage runtime.MachineStage, machineType machine.Type) []readinessCheck { requiredServices := []string{ "apid", "machined", "kubelet", } if machineType.IsControlPlane() { requiredServices = append(requiredServices, "etcd", "trustd", ) } switch stage { //nolint:exhaustive case runtime.MachineStageBooting, runtime.MachineStageRunning: return []readinessCheck{ { name: "time", f: ctrl.timeSyncCheck, }, { name: "network", f: ctrl.networkReadyCheck, }, { name: "services", f: ctrl.servicesCheck(requiredServices), }, { name: "staticPods", f: ctrl.staticPodsCheck, }, { name: "nodeReady", f: ctrl.nodeReadyCheck, }, } default: return nil } } func (ctrl *MachineStatusController) timeSyncCheck(ctx context.Context, r controller.Runtime) error { timeSyncStatus, err := safe.ReaderGet[*time.Status](ctx, r, time.NewStatus().Metadata()) if err != nil { return err } if !timeSyncStatus.TypedSpec().Synced { return errors.New("time is not synced") } return nil } func (ctrl *MachineStatusController) networkReadyCheck(ctx context.Context, r controller.Runtime) error { networkStatus, err := safe.ReaderGet[*network.Status](ctx, r, network.NewStatus(network.NamespaceName, network.StatusID).Metadata()) if err != nil { return err } var notReady []string if !networkStatus.TypedSpec().AddressReady { notReady = append(notReady, "address") } if !networkStatus.TypedSpec().ConnectivityReady { notReady = append(notReady, "connectivity") } if !networkStatus.TypedSpec().EtcFilesReady { notReady = append(notReady, "etc-files") } if !networkStatus.TypedSpec().HostnameReady { notReady = append(notReady, "hostname") } if len(notReady) == 0 { return nil } return fmt.Errorf("waiting on: %s", strings.Join(notReady, ", ")) } func (ctrl *MachineStatusController) servicesCheck(requiredServices []string) func(ctx context.Context, r controller.Runtime) error { return func(ctx context.Context, r controller.Runtime) error { serviceList, err := safe.ReaderListAll[*v1alpha1.Service](ctx, r) if err != nil { return err } var problems []string runningServices := map[string]struct{}{} for service := range serviceList.All() { if !service.TypedSpec().Running { problems = append(problems, fmt.Sprintf("%s not running", service.Metadata().ID())) continue } runningServices[service.Metadata().ID()] = struct{}{} if !service.TypedSpec().Unknown && !service.TypedSpec().Healthy { problems = append(problems, fmt.Sprintf("%s not healthy", service.Metadata().ID())) } } for _, svc := range requiredServices { if _, running := runningServices[svc]; !running { problems = append(problems, fmt.Sprintf("%s not running", svc)) } } if len(problems) == 0 { return nil } return fmt.Errorf("%s", strings.Join(problems, ", ")) } } //nolint:gocyclo func (ctrl *MachineStatusController) staticPodsCheck(ctx context.Context, r controller.Runtime) error { staticPodList, err := safe.ReaderListAll[*k8s.StaticPodStatus](ctx, r) if err != nil { return err } var problems []string for staticPod := range staticPodList.All() { status, err := k8sadapter.StaticPodStatus(staticPod).Status() if err != nil { return err } switch status.Phase { case v1.PodPending, v1.PodFailed, v1.PodUnknown: problems = append(problems, fmt.Sprintf("%s %s", staticPod.Metadata().ID(), strings.ToLower(string(status.Phase)))) case v1.PodSucceeded: // do nothing, terminal phase case v1.PodRunning: // check readiness ready := false for _, condition := range status.Conditions { if condition.Type == v1.PodReady { ready = condition.Status == v1.ConditionTrue break } } if !ready { problems = append(problems, fmt.Sprintf("%s not ready", staticPod.Metadata().ID())) } } } if len(problems) == 0 { return nil } return fmt.Errorf("%s", strings.Join(problems, ", ")) } func (ctrl *MachineStatusController) nodeReadyCheck(ctx context.Context, r controller.Runtime) error { nodename, err := safe.ReaderGetByID[*k8s.Nodename](ctx, r, k8s.NodenameID) if err != nil { if state.IsNotFoundError(err) { // nodename not established yet, skip return nil } return fmt.Errorf("failed to get nodename: %w", err) } if nodename.TypedSpec().SkipNodeRegistration { // node registration skipped, skip the check return nil } nodeStatus, err := safe.ReaderGetByID[*k8s.NodeStatus](ctx, r, nodename.TypedSpec().Nodename) if err != nil { if state.IsNotFoundError(err) { // node not established yet, skip return fmt.Errorf("node %q status is not available yet", nodename.TypedSpec().Nodename) } return fmt.Errorf("failed to get node status: %w", err) } if !nodeStatus.TypedSpec().NodeReady { return fmt.Errorf("node %q is not ready", nodename.TypedSpec().Nodename) } return nil } //nolint:gocyclo,cyclop func (ctrl *MachineStatusController) watchEvents() { // the interface of the Watch function is weird (blaming myself @smira) // // at the same time as it is events based, it's impossible to reconcile the current state // from the events, so what we're doing is watching the events forever as soon as the controller starts, // and aggregating the state into the stage variable, notifying the controller whenever the state changes. ctrl.V1Alpha1Events.Watch(func(eventCh <-chan v1alpha1runtime.EventInfo) { //nolint:errcheck var ( oldStage runtime.MachineStage currentSequence string ) for ev := range eventCh { newStage := oldStage switch event := ev.Event.Payload.(type) { case *machineapi.SequenceEvent: currentSequence = event.Sequence switch event.Action { case machineapi.SequenceEvent_START: // mostly interested in sequence start events switch event.Sequence { case v1alpha1runtime.SequenceBoot.String(), v1alpha1runtime.SequenceInitialize.String(): newStage = runtime.MachineStageBooting case v1alpha1runtime.SequenceInstall.String(): // install sequence is run always, even if the machine is already installed, so we'll catch it by phase name case v1alpha1runtime.SequenceShutdown.String(): newStage = runtime.MachineStageShuttingDown case v1alpha1runtime.SequenceUpgrade.String(), v1alpha1runtime.SequenceStageUpgrade.String(), v1alpha1runtime.SequenceMaintenanceUpgrade.String(): newStage = runtime.MachineStageUpgrading case v1alpha1runtime.SequenceReset.String(): newStage = runtime.MachineStageResetting case v1alpha1runtime.SequenceReboot.String(): newStage = runtime.MachineStageRebooting } case machineapi.SequenceEvent_NOOP: if event.Error != nil && event.Error.Code == common.Code_FATAL { // fatal errors lead to reboot newStage = runtime.MachineStageRebooting } case machineapi.SequenceEvent_STOP: if event.Sequence == v1alpha1runtime.SequenceBoot.String() && event.Error == nil { newStage = runtime.MachineStageRunning } // sequence finished, it doesn't matter whether if it was successful or not currentSequence = "" } case *machineapi.PhaseEvent: if event.Action == machineapi.PhaseEvent_START { switch { case currentSequence == v1alpha1runtime.SequenceInstall.String() && event.Phase == "install": newStage = runtime.MachineStageInstalling case (currentSequence == v1alpha1runtime.SequenceInstall.String() || currentSequence == v1alpha1runtime.SequenceUpgrade.String() || currentSequence == v1alpha1runtime.SequenceStageUpgrade.String() || currentSequence == v1alpha1runtime.SequenceMaintenanceUpgrade.String()) && event.Phase == "kexec": newStage = runtime.MachineStageRebooting } } case *machineapi.TaskEvent: if event.Task == "runningMaintenance" { switch event.Action { case machineapi.TaskEvent_START: newStage = runtime.MachineStageMaintenance case machineapi.TaskEvent_STOP: newStage = runtime.MachineStageBooting } } } if oldStage != newStage { ctrl.mu.Lock() ctrl.currentStage = newStage ctrl.mu.Unlock() select { case ctrl.notifyCh <- struct{}{}: default: } } oldStage = newStage } }, v1alpha1runtime.WithTailEvents(-1)) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/machine_status_publisher.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // MachineStatusPublisherController watches MachineStatusPublishers, sets/resets kernel params. type MachineStatusPublisherController struct { V1Alpha1Events v1alpha1runtime.Publisher } // Name implements controller.Controller interface. func (ctrl *MachineStatusPublisherController) Name() string { return "runtime.MachineStatusPublisherController" } // Inputs implements controller.Controller interface. func (ctrl *MachineStatusPublisherController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MachineStatusType, ID: optional.Some(runtime.MachineStatusID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MachineStatusPublisherController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. func (ctrl *MachineStatusPublisherController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } machineStatus, err := safe.ReaderGet[*runtime.MachineStatus](ctx, r, runtime.NewMachineStatus().Metadata()) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error reading machine status: %w", err) } ctrl.V1Alpha1Events.Publish(ctx, &machine.MachineStatusEvent{ Stage: machine.MachineStatusEvent_MachineStage(machineStatus.TypedSpec().Stage), Status: &machine.MachineStatusEvent_MachineStatus{ Ready: machineStatus.TypedSpec().Status.Ready, UnmetConditions: xslices.Map(machineStatus.TypedSpec().Status.UnmetConditions, func(unmetCondition runtime.UnmetCondition) *machine.MachineStatusEvent_MachineStatus_UnmetCondition { return &machine.MachineStatusEvent_MachineStatus_UnmetCondition{ Name: unmetCondition.Name, Reason: unmetCondition.Reason, } }), }, }) r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/machine_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" timeres "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) func TestMachineStatusSuite(t *testing.T) { eventCh := make(chan v1alpha1runtime.EventInfo) suite.Run(t, &MachineStatusSuite{ eventCh: eventCh, DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrl.MachineStatusController{ V1Alpha1Events: &mockWatcher{eventCh: eventCh}, })) }, }, }) } type mockWatcher struct { eventCh chan v1alpha1runtime.EventInfo } func (m *mockWatcher) Watch(f v1alpha1runtime.WatchFunc, opt ...v1alpha1runtime.WatchOptionFunc) error { f(m.eventCh) return nil } type MachineStatusSuite struct { ctest.DefaultSuite eventCh chan v1alpha1runtime.EventInfo } func (suite *MachineStatusSuite) assertMachineStatus(stage runtime.MachineStage, ready bool, unmetConditions []string) { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.MachineStatusID}, func(machineStatus *runtime.MachineStatus, asrt *assert.Assertions) { asrt.Equal(stage, machineStatus.TypedSpec().Stage) asrt.Equal(ready, machineStatus.TypedSpec().Status.Ready) asrt.Equal(unmetConditions, xslices.Map(machineStatus.TypedSpec().Status.UnmetConditions, func(c runtime.UnmetCondition) string { return c.Name })) }) } func (suite *MachineStatusSuite) TestReconcile() { suite.assertMachineStatus(runtime.MachineStageUnknown, true, nil) suite.eventCh <- v1alpha1runtime.EventInfo{ Event: v1alpha1runtime.Event{ Payload: &machineapi.SequenceEvent{ Sequence: v1alpha1runtime.SequenceInitialize.String(), Action: machineapi.SequenceEvent_START, }, }, } suite.assertMachineStatus(runtime.MachineStageBooting, false, []string{"time", "network", "services"}) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) timeStatus := timeres.NewStatus() timeStatus.TypedSpec().Synced = true suite.Require().NoError(suite.State().Create(suite.Ctx(), timeStatus)) suite.eventCh <- v1alpha1runtime.EventInfo{ Event: v1alpha1runtime.Event{ Payload: &machineapi.SequenceEvent{ Sequence: v1alpha1runtime.SequenceBoot.String(), Action: machineapi.SequenceEvent_START, }, }, } suite.assertMachineStatus(runtime.MachineStageBooting, false, []string{"network", "services"}) suite.eventCh <- v1alpha1runtime.EventInfo{ Event: v1alpha1runtime.Event{ Payload: &machineapi.SequenceEvent{ Sequence: v1alpha1runtime.SequenceBoot.String(), Action: machineapi.SequenceEvent_STOP, }, }, } networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true networkStatus.TypedSpec().ConnectivityReady = true networkStatus.TypedSpec().EtcFilesReady = true networkStatus.TypedSpec().HostnameReady = true suite.Require().NoError(suite.State().Create(suite.Ctx(), networkStatus)) suite.assertMachineStatus(runtime.MachineStageRunning, false, []string{"services"}) for _, service := range []string{"apid", "etcd", "kubelet", "machined", "trustd"} { serviceStatus := v1alpha1.NewService(service) serviceStatus.TypedSpec().Running = true serviceStatus.TypedSpec().Healthy = true suite.Require().NoError(suite.State().Create(suite.Ctx(), serviceStatus)) } suite.assertMachineStatus(runtime.MachineStageRunning, true, nil) nodename := k8s.NewNodename(k8s.NamespaceName, k8s.NodenameID) nodename.TypedSpec().Nodename = "test" suite.Require().NoError(suite.State().Create(suite.Ctx(), nodename)) suite.assertMachineStatus(runtime.MachineStageRunning, false, []string{"nodeReady"}) nodeStatus := k8s.NewNodeStatus(k8s.NamespaceName, "test") suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeStatus)) suite.assertMachineStatus(runtime.MachineStageRunning, false, []string{"nodeReady"}) nodeStatus.TypedSpec().NodeReady = true suite.Require().NoError(suite.State().Update(suite.Ctx(), nodeStatus)) suite.assertMachineStatus(runtime.MachineStageRunning, true, nil) suite.eventCh <- v1alpha1runtime.EventInfo{ Event: v1alpha1runtime.Event{ Payload: &machineapi.SequenceEvent{ Sequence: v1alpha1runtime.SequenceReboot.String(), Action: machineapi.SequenceEvent_START, }, }, } suite.assertMachineStatus(runtime.MachineStageRebooting, true, nil) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/maintenance_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) // MaintenanceConfigController manages Maintenance Service config: which address it should listen on, etc. type MaintenanceConfigController struct{} // Name implements controller.Controller interface. func (ctrl *MaintenanceConfigController) Name() string { return "runtime.MaintenanceConfigController" } // Inputs implements controller.Controller interface. func (ctrl *MaintenanceConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: siderolink.ConfigType, ID: optional.Some(siderolink.ConfigID), }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.NodeAddressCurrentID), }, } } // Outputs implements controller.Controller interface. func (ctrl *MaintenanceConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.MaintenanceServiceConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *MaintenanceConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } nodeAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.NodeAddressCurrentID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting node address: %w", err) } var ( listenAddress string reachableAddresses []netip.Addr ) if nodeAddresses != nil { reachableAddresses = nodeAddresses.TypedSpec().IPs() } _, err = safe.ReaderGetByID[*siderolink.Config](ctx, r, siderolink.ConfigID) // check if SideroLink config exists: switch { // * if it exists, find the SideroLink address and listen only on it case err == nil: if nodeAddresses != nil { sideroLinkAddresses := xslices.Filter(nodeAddresses.TypedSpec().IPs(), func(addr netip.Addr) bool { return network.IsULA(addr, network.ULASideroLink) }) if len(sideroLinkAddresses) > 0 { listenAddress = nethelpers.JoinHostPort(sideroLinkAddresses[0].String(), constants.ApidPort) reachableAddresses = sideroLinkAddresses[:1] } } // * if it doesn't exist, listen on '*' case state.IsNotFoundError(err): listenAddress = fmt.Sprintf(":%d", constants.ApidPort) default: return fmt.Errorf("error getting siderolink config: %w", err) } if listenAddress == "" { // drop config if err = r.Destroy(ctx, runtime.NewMaintenanceServiceConfig().Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying maintenance config: %w", err) } } else { // create/update config if err = safe.WriterModify[*runtime.MaintenanceServiceConfig](ctx, r, runtime.NewMaintenanceServiceConfig(), func(config *runtime.MaintenanceServiceConfig) error { config.TypedSpec().ListenAddress = listenAddress config.TypedSpec().ReachableAddresses = reachableAddresses return nil }); err != nil { return fmt.Errorf("error updating maintenance config: %w", err) } } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/maintenance_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) func TestMaintenanceConfigSuite(t *testing.T) { suite.Run(t, &MaintenanceConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrl.MaintenanceConfigController{})) }, }, }) } type MaintenanceConfigSuite struct { ctest.DefaultSuite } func (suite *MaintenanceConfigSuite) TestReconcile() { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.MaintenanceServiceConfigID}, func(cfg *runtime.MaintenanceServiceConfig, asrt *assert.Assertions) { asrt.Equal(":50000", cfg.TypedSpec().ListenAddress) asrt.Nil(cfg.TypedSpec().ReachableAddresses) }) siderolinkConfig := siderolink.NewConfig(config.NamespaceName, siderolink.ConfigID) suite.Require().NoError(suite.State().Create(suite.Ctx(), siderolinkConfig)) rtestutils.AssertNoResource[*runtime.MaintenanceServiceConfig](suite.Ctx(), suite.T(), suite.State(), runtime.MaintenanceServiceConfigID) nodeAddresses := network.NewNodeAddress(network.NamespaceName, network.NodeAddressCurrentID) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("172.16.0.1/24"), netip.MustParsePrefix("fdae:41e4:649b:9303:2a07:9c7:5b08:aef7/64"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeAddresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.MaintenanceServiceConfigID}, func(cfg *runtime.MaintenanceServiceConfig, asrt *assert.Assertions) { asrt.Equal("[fdae:41e4:649b:9303:2a07:9c7:5b08:aef7]:50000", cfg.TypedSpec().ListenAddress) asrt.Equal([]netip.Addr{netip.MustParseAddr("fdae:41e4:649b:9303:2a07:9c7:5b08:aef7")}, cfg.TypedSpec().ReachableAddresses) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/maintenance_service_inform.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" machinedruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // MaintenanceServiceInformController provides logging when the maintenance service is running. type MaintenanceServiceInformController struct { V1Alpha1Mode machinedruntime.Mode } // Name implements controller.Controller interface. func (ctrl *MaintenanceServiceInformController) Name() string { return "runtime.MaintenanceServiceInformController" } // Inputs implements controller.Controller interface. func (ctrl *MaintenanceServiceInformController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MaintenanceServiceRequestType, ID: optional.Some(runtime.MaintenanceServiceRequestID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MaintenanceServiceConfigType, ID: optional.Some(runtime.MaintenanceServiceConfigID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.APIType, ID: optional.Some(secrets.APIID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MaintenanceServiceInformController) Outputs() []controller.Output { return nil } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *MaintenanceServiceInformController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( lastReachableAddresses []string lastCertificateFingerprint string usagePrinted bool ) for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } request, err := safe.ReaderGetByID[*runtime.MaintenanceServiceRequest](ctx, r, runtime.MaintenanceServiceRequestID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get maintenance service request: %w", err) } if request == nil { // no request, nothing to do continue } cfg, err := safe.ReaderGetByID[*runtime.MaintenanceServiceConfig](ctx, r, runtime.MaintenanceServiceConfigID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get maintenance service config: %w", err) } cert, err := safe.ReaderGetByID[*secrets.API](ctx, r, secrets.APIID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get API secret: %w", err) } if cert != nil && !cert.TypedSpec().SkipVerifyingClientCert { // not a maintenance mode yet cert = nil } // print additional information for the user on important state changes reachableAddresses := xslices.Map(cfg.TypedSpec().ReachableAddresses, netip.Addr.String) if !slices.Equal(lastReachableAddresses, reachableAddresses) { logger.Info("this machine is reachable at:") for _, addr := range reachableAddresses { logger.Info("\t" + addr) } lastReachableAddresses = reachableAddresses } if cert != nil { certificateFingerprint, err := x509.SPKIFingerprintFromPEM(cert.TypedSpec().Server.Crt) if err != nil { return fmt.Errorf("failed to get certificate fingerprint: %w", err) } fingerprint := certificateFingerprint.String() if fingerprint != lastCertificateFingerprint { logger.Info("server certificate issued", zap.String("fingerprint", fingerprint)) } lastCertificateFingerprint = fingerprint } if !usagePrinted && len(reachableAddresses) > 0 && lastCertificateFingerprint != "" && !ctrl.V1Alpha1Mode.IsAgent() { firstIP := reachableAddresses[0] logger.Sugar().Info("upload configuration using talosctl:") logger.Sugar().Infof("\ttalosctl apply-config --insecure --nodes %s --file ", firstIP) logger.Sugar().Info("optionally with node fingerprint check:") logger.Sugar().Infof("\ttalosctl apply-config --insecure --nodes %s --cert-fingerprint '%s' --file ", firstIP, lastCertificateFingerprint) usagePrinted = true } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // MountStatusController transforms block.MountStatus resources into legacy v1alpha1.MountStatus. // // It only exists to provide backwards compatibility with legacy consumers. type MountStatusController struct{} // Name implements controller.Controller interface. func (ctrl *MountStatusController) Name() string { return "runtime.MountStatusController" } // Inputs implements controller.Controller interface. func (ctrl *MountStatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.MountStatusType, Kind: controller.InputWeak, }, { Namespace: block.NamespaceName, Type: block.VolumeStatusType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MountStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.MountStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *MountStatusController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-r.EventCh(): case <-ctx.Done(): return nil } mountStatuses, err := safe.ReaderListAll[*block.MountStatus](ctx, r) if err != nil { return fmt.Errorf("failed to read mount statuses: %w", err) } r.StartTrackingOutputs() for mountStatus := range mountStatuses.All() { volumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, r, mountStatus.TypedSpec().Spec.VolumeID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get volume status %q: %w", mountStatus.TypedSpec().Spec.VolumeID, err) } if volumeStatus.TypedSpec().Type != block.VolumeTypePartition && volumeStatus.TypedSpec().Type != block.VolumeTypeDisk { // legacy volume statuses shouldn't show up for non-partition/disk volumes continue } if err = safe.WriterModify(ctx, r, runtime.NewMountStatus(runtime.NamespaceName, volumeStatus.Metadata().ID()), func(res *runtime.MountStatus) error { res.TypedSpec().Source = mountStatus.TypedSpec().Source res.TypedSpec().Target = mountStatus.TypedSpec().Target res.TypedSpec().FilesystemType = volumeStatus.TypedSpec().Filesystem.String() res.TypedSpec().Encrypted = volumeStatus.TypedSpec().EncryptionProvider != block.EncryptionProviderNone res.TypedSpec().EncryptionProviders = volumeStatus.TypedSpec().ConfiguredEncryptionKeys return nil }, ); err != nil { return fmt.Errorf("failed to write mount status: %w", err) } } if err := safe.CleanupOutputs[*runtime.MountStatus](ctx, r); err != nil { return fmt.Errorf("failed to cleanup mount statuses: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/oom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "bytes" "context" "encoding/json" "fmt" "math" "os" "path/filepath" "strconv" "sync" "syscall" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime/internal/oom" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type actionLogItem struct { runtimeres.OOMActionSpec ID int } // OOMController is a controller that monitors memory PSI and handles near-OOM situations. type OOMController struct { CgroupRoot string ActionTriggered time.Time V1Alpha1Mode runtime.Mode actionLog []actionLogItem idSeq int oldValues map[string]float64 } // Name implements controller.Controller interface. func (ctrl *OOMController) Name() string { return "runtime.OOMController" } // Inputs implements controller.Controller interface. func (ctrl *OOMController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *OOMController) Outputs() []controller.Output { return []controller.Output{ { Type: runtimeres.OOMActionType, Kind: controller.OutputExclusive, }, } } var defaultTriggerExpr = sync.OnceValue(func() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression( constants.DefaultOOMTriggerExpression, celenv.OOMTrigger(), )) }) // Sort processes by the following hierarchy: // First, sort by high-level group: // kubepods (workloads) // podruntime (CRI, kubelet, etcd) // runtime (core containerd, system services) // init // Second, inside kubepods we have QoS groups: // first priority: BestEffort // second: Burstable // last: Guaranteed // Third, look into other attributes, e.g. OOM score. // Fourth, look into memory max - memory current (if memory max is set). // // Sort to make the most prioritized to OOM-kill cgroup to the first place var defaultScoringExpr = sync.OnceValue(func() cel.Expression { return cel.MustExpression(cel.ParseDoubleExpression( constants.DefaultOOMCgroupRankingExpression, celenv.OOMCgroupScoring(), )) }) const defaultSampleInterval = 500 * time.Millisecond // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *OOMController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.V1Alpha1Mode.InContainer() { return nil } triggerExpr := defaultTriggerExpr() scoringExpr := defaultScoringExpr() sampleInterval := defaultSampleInterval ctrl.oldValues = make(map[string]float64) ticker := time.NewTicker(sampleInterval) tickerC := ticker.C if ctrl.CgroupRoot == "" { ctrl.CgroupRoot = constants.CgroupMountPath } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("cannot get active machine config: %w", err) } var newInterval time.Duration triggerExpr, scoringExpr, newInterval = ctrl.getConfig(cfg) if sampleInterval != newInterval { ticker.Reset(newInterval) sampleInterval = newInterval } case <-tickerC: } evalContext := map[string]any{ "time_since_trigger": time.Since(ctrl.ActionTriggered), } err := oom.PopulatePsiToCtx(ctrl.CgroupRoot, evalContext, ctrl.oldValues, sampleInterval) if err != nil { logger.Error("cannot populate PSI context", zap.Error(err)) continue } trigger, err := oom.EvaluateTrigger(triggerExpr, evalContext) if err != nil { logger.Error("cannot evaluate OOM trigger expression", zap.Error(err)) continue } r.StartTrackingOutputs() for _, action := range ctrl.actionLog { if err := safe.WriterModify(ctx, r, runtimeres.NewOOMActionSpec(runtimeres.NamespaceName, strconv.Itoa(action.ID)), func(item *runtimeres.OOMAction) error { *item.TypedSpec() = action.OOMActionSpec return nil }); err != nil { return fmt.Errorf("failed to create OOM action log: %w", err) } } if err = safe.CleanupOutputs[*runtimeres.OOMAction](ctx, r); err != nil { return err } if trigger { score, processes := ctrl.OomAction(logger, ctrl.CgroupRoot, scoringExpr) ctxString, err := json.Marshal(evalContext) if err != nil { return fmt.Errorf("failed to marshal trigger context: %w", err) } ctrl.actionLog = append(ctrl.actionLog, actionLogItem{ ID: ctrl.idSeq, OOMActionSpec: runtimeres.OOMActionSpec{ TriggerContext: string(ctxString), Processes: processes, Score: score, }, }) ctrl.idSeq++ if len(ctrl.actionLog) > constants.OOMActionLogKeep { ctrl.actionLog = ctrl.actionLog[len(ctrl.actionLog)-constants.OOMActionLogKeep:] } ctrl.ActionTriggered = time.Now() } } } func (*OOMController) getConfig(cfg *config.MachineConfig) (cel.Expression, cel.Expression, time.Duration) { triggerExpr := defaultTriggerExpr() if cfg != nil { if oomCfg := cfg.Config().OOMConfig(); oomCfg != nil { if expr, ok := oomCfg.TriggerExpression().Get(); ok { triggerExpr = expr } } } scoringExpr := defaultScoringExpr() if cfg != nil { if oomCfg := cfg.Config().OOMConfig(); oomCfg != nil { if expr, ok := oomCfg.CgroupRankingExpression().Get(); ok { scoringExpr = expr } } } newInterval := defaultSampleInterval if cfg != nil { if oomCfg := cfg.Config().OOMConfig(); oomCfg != nil { if interval, ok := oomCfg.SampleInterval().Get(); ok { newInterval = interval } } } return triggerExpr, scoringExpr, newInterval } // OomAction handles out of memory conditions by selecting and killing cgroups based on memory usage data. func (ctrl *OOMController) OomAction(logger *zap.Logger, root string, scoringExpr cel.Expression) (float64, []string) { logger.Info("OOM controller triggered") ranking := oom.RankCgroups(logger, root, scoringExpr) if len(ranking) == 0 { return 0, []string{} } var ( maxScore = math.Inf(-1) cgroupToKill oom.RankedCgroup ) for cgroup, score := range ranking { if score > maxScore { maxScore = score cgroupToKill = cgroup } } processes, err := reapCg(logger, cgroupToKill.Path) if err != nil { logger.Error("cannot reap cgroup", zap.String("cgroup", cgroupToKill.Path), zap.Error(err)) } return maxScore, processes } func reapCg(logger *zap.Logger, cgroupPath string) ([]string, error) { logger.Warn("Sending SIGKILL to cgroup", zap.String("cgroup", cgroupPath)) processes := oom.ListCgroupProcs(cgroupPath) logger.Info("victim processes:", zap.Any("processes", processes)) // Open pidfd's of all the processes in cgroup to accelerate kernel // garbage-collecting those processes via mrelease. pidfds := []int{} cmdlines := []string{} for _, pid := range processes { cmdBytes, err := os.ReadFile(filepath.Join("/proc", strconv.Itoa(pid), "cmdline")) if err == nil { cmdlines = append( cmdlines, string(bytes.ReplaceAll(bytes.TrimRight(cmdBytes, "\x00"), []byte{0}, []byte{' '})), ) } // pidfd is always opened with CLOEXEC: // https://github.com/torvalds/linux/blob/bf40f4b87761e2ec16efc8e49b9ca0d81f4115d8/kernel/pid.c#L637 pidfd, err := unix.PidfdOpen(pid, 0) if err != nil { logger.Error("failed to open pidfd", zap.Int("pid", pid), zap.Error(err)) continue } defer unix.Close(pidfd) //nolint:errcheck pidfds = append(pidfds, pidfd) } err := os.WriteFile(filepath.Join(cgroupPath, "cgroup.kill"), []byte{'1'}, 0o644) if err != nil { logger.Error("failed to send SIGKILL", zap.String("cgroup", cgroupPath), zap.Error(err)) return cmdlines, err } for _, pidfd := range pidfds { _, _, errno := syscall.Syscall(unix.SYS_PROCESS_MRELEASE, uintptr(pidfd), uintptr(0), uintptr(0)) if errno != 0 && errno != syscall.ESRCH { // FIXME: tolerate some errors esp given that some processes might have been freed already. logger.Error("failed to call mrelease", zap.Int("errno", int(errno))) continue } } return cmdlines, nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package runtime provides the runtime implementation. package runtime ================================================ FILE: internal/app/machined/pkg/controllers/runtime/sbom_item.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "encoding/json" "errors" "fmt" "os" "path/filepath" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/version" ) // SBOMItemController is a controller that publishes Talos SBOMs as resources. type SBOMItemController struct { SPDXPath string ExtensionSPDXPath string } // Name implements controller.Controller interface. func (ctrl *SBOMItemController) Name() string { return "runtime.SBOMItemController" } // Inputs implements controller.Controller interface. func (ctrl *SBOMItemController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *SBOMItemController) Outputs() []controller.Output { return []controller.Output{ { Type: runtimeres.SBOMItemType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *SBOMItemController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.SPDXPath == "" { ctrl.SPDXPath = constants.SPDXPath } if ctrl.ExtensionSPDXPath == "" { ctrl.ExtensionSPDXPath = constants.ExtensionSPDXPath } // the controller runs a single time select { case <-ctx.Done(): return nil case <-r.EventCh(): } for _, spec := range []struct { isExtension bool path string }{ {false, ctrl.SPDXPath}, {true, ctrl.ExtensionSPDXPath}, } { if err := ctrl.processSPDXDirectory(ctx, r, logger, spec.path, spec.isExtension); err != nil { if spec.isExtension && errors.Is(err, os.ErrNotExist) { // Extension SBOM directory is only present if extensions are installed continue } return err } } return nil } func (ctrl *SBOMItemController) processSPDXDirectory(ctx context.Context, r controller.Runtime, logger *zap.Logger, path string, isExtension bool) error { files, err := os.ReadDir(path) if err != nil { return fmt.Errorf("failed to read SBOM directory %q: %w", path, err) } for _, file := range files { if !file.Type().IsRegular() { logger.Debug("skipping non-regular file", zap.String("file", file.Name())) continue } if !strings.HasSuffix(file.Name(), ".spdx.json") { logger.Debug("skipping non-SPDX file", zap.String("file", file.Name())) continue } if err = ctrl.processSPDXFile(ctx, r, filepath.Join(path, file.Name()), isExtension); err != nil { return fmt.Errorf("failed to process SBOM file %q: %w", file.Name(), err) } } return nil } // spdxDocument is a reduced structure of SPDX document. // // We are only interested in some fields. type spdxDocument struct { Packages []spdxPackage `json:"packages"` } type spdxPackage struct { Name string `json:"name"` Version string `json:"versionInfo"` License string `json:"licenseDeclared"` ExternalRefs []spdxExternalRef `json:"externalRefs"` } type spdxExternalRef struct { Type string `json:"referenceType"` Locator string `json:"referenceLocator"` } func (ctrl *SBOMItemController) processSPDXFile(ctx context.Context, r controller.Runtime, path string, isExtension bool) error { in, err := os.Open(path) if err != nil { return fmt.Errorf("failed to open SBOM file %q: %w", path, err) } defer in.Close() //nolint:errcheck var doc spdxDocument if err := json.NewDecoder(in).Decode(&doc); err != nil { return fmt.Errorf("failed to decode SBOM file %q: %w", path, err) } for _, pkg := range doc.Packages { if strings.HasPrefix(pkg.Name, version.Name+" (") { pkg.Name = version.Name } if err := safe.WriterModify(ctx, r, runtimeres.NewSBOMItemSpec(runtimeres.NamespaceName, pkg.Name), func(item *runtimeres.SBOMItem) error { item.TypedSpec().Name = pkg.Name item.TypedSpec().Version = pkg.Version if pkg.License != "NOASSERTION" { item.TypedSpec().License = pkg.License } for _, ref := range pkg.ExternalRefs { switch ref.Type { case "cpe23Type": item.TypedSpec().CPEs = append(item.TypedSpec().CPEs, ref.Locator) case "purl": item.TypedSpec().PURLs = append(item.TypedSpec().PURLs, ref.Locator) } } item.TypedSpec().Extension = isExtension return nil }); err != nil { return fmt.Errorf("failed to create SBOM item for package %q: %w", pkg.Name, err) } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/sbom_item_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type SBOMItemSuite struct { ctest.DefaultSuite } func TestSBOMItemSuite(t *testing.T) { t.Parallel() suite.Run(t, &SBOMItemSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.SBOMItemController{ SPDXPath: "./testdata/spdx", ExtensionSPDXPath: "./testdata/ext-spdx", })) }, }, }) } func (suite *SBOMItemSuite) TestReconcile() { ctest.AssertResource(suite, "apparmor-x86_64", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal("apparmor-x86_64", item.TypedSpec().Name) asrt.Equal("v3.1.7", item.TypedSpec().Version) asrt.Equal("GPL-2.0-or-later", item.TypedSpec().License) asrt.Contains(item.TypedSpec().CPEs, "cpe:2.3:a:apparmor:apparmor:v3.1.7:*:*:*:*:*:*:*") asrt.Contains(item.TypedSpec().CPEs, "cpe:2.3:a:canonical:apparmor:v3.1.7:*:*:*:*:*:*:*") asrt.Empty(item.TypedSpec().PURLs) asrt.False(item.TypedSpec().Extension) }) ctest.AssertResource(suite, "cel.dev/expr", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal("cel.dev/expr", item.TypedSpec().Name) asrt.Equal("v0.24.0", item.TypedSpec().Version) asrt.Empty(item.TypedSpec().License) asrt.Empty(item.TypedSpec().CPEs) asrt.Contains(item.TypedSpec().PURLs, "pkg:golang/cel.dev/expr@v0.24.0") asrt.False(item.TypedSpec().Extension) }) ctest.AssertResource(suite, "tailscale", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal("tailscale", item.TypedSpec().Name) asrt.Equal("1.84.2", item.TypedSpec().Version) asrt.Equal("BSD-3-Clause", item.TypedSpec().License) asrt.Contains(item.TypedSpec().CPEs, "cpe:2.3:a:tailscale:tailscale:1.84.2:*:*:*:*:*:*:*") asrt.Empty(item.TypedSpec().PURLs) asrt.True(item.TypedSpec().Extension) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/security_state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "bytes" "context" "crypto/sha256" "crypto/x509" "encoding/hex" "encoding/pem" "errors" "fmt" "os" "path/filepath" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/foxboron/go-uefi/efi" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" machineruntime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // SecurityStateController is a controller that updates the security state of Talos. type SecurityStateController struct { V1Alpha1Mode machineruntime.Mode } // Name implements controller.Controller interface. func (ctrl *SecurityStateController) Name() string { return "runtime.SecurityStateController" } // Inputs implements controller.Controller interface. func (ctrl *SecurityStateController) Inputs() []controller.Input { return []controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *SecurityStateController) Outputs() []controller.Output { return []controller.Output{ { Type: runtimeres.SecurityStateType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *SecurityStateController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // wait for the `machined` service to start, as by that time initial mounts will be done _, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, "machined") if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("failed to get machined state: %w", err) } var ( secureBootState bool bootedWithUKI bool moduleSignatureEnforced bool pcrSigningKeyFingerprint string ) // in container mode, never populate the fields if ctrl.V1Alpha1Mode != machineruntime.ModeContainer { if efi.GetSecureBoot() && !efi.GetSetupMode() { secureBootState = true } defaultEntry, err := sdboot.ReadVariable(sdboot.LoaderEntryDefaultName) if err == nil { if strings.HasPrefix(defaultEntry, "Talos-") { bootedWithUKI = true } } // if defaultEntry is empty in the case when we booted off a disk image when installer never runs, we can rely on the // stub image identifier to determine if we booted with UKI if defaultEntry == "" { stubImageIdentifier, err := sdboot.ReadVariable(sdboot.StubImageIdentifierName) if err == nil { if strings.HasPrefix(filepath.Base(strings.ReplaceAll(stubImageIdentifier, "\\", "/")), "Talos-") { bootedWithUKI = true } } } if pcrPublicKeyData, err := os.ReadFile(constants.PCRPublicKey); err == nil { block, _ := pem.Decode(pcrPublicKeyData) if block == nil { return errors.New("failed to decode PEM block for PCR public key") } cert := x509.Certificate{ Raw: block.Bytes, } pcrSigningKeyFingerprint = x509CertFingerprint(cert) } } selinuxState, err := getSelinuxState() if err != nil { return fmt.Errorf("failed to get SELinux state: %w", err) } moduleSignatureEnforcedInfo := procfs.ProcCmdline().Get(constants.KernelParamEnforceModuleSigVerify).First() if moduleSignatureEnforcedInfo != nil && *moduleSignatureEnforcedInfo == "1" { moduleSignatureEnforced = true } fipsState := runtimeres.FIPSStateDisabled if fipsmode.Enabled() { if fipsmode.Strict() { fipsState = runtimeres.FIPSStateStrict } else { fipsState = runtimeres.FIPSStateEnabled } } if err := safe.WriterModify(ctx, r, runtimeres.NewSecurityStateSpec(runtimeres.NamespaceName), func(state *runtimeres.SecurityState) error { state.TypedSpec().SecureBoot = secureBootState state.TypedSpec().PCRSigningKeyFingerprint = pcrSigningKeyFingerprint state.TypedSpec().SELinuxState = selinuxState state.TypedSpec().FIPSState = fipsState state.TypedSpec().BootedWithUKI = bootedWithUKI state.TypedSpec().ModuleSignatureEnforced = moduleSignatureEnforced return nil }); err != nil { return err } // terminating the controller here, as we need to only populate securitystate once return nil } } func x509CertFingerprint(cert x509.Certificate) string { hash := sha256.Sum256(cert.Raw) var buf bytes.Buffer for i, b := range hex.EncodeToString(hash[:]) { if i > 0 && i%2 == 0 { buf.WriteByte(':') } buf.WriteString(strings.ToUpper(string(b))) } return buf.String() } func getSelinuxState() (runtimeres.SELinuxState, error) { if !selinux.IsEnabled() { return runtimeres.SELinuxStateDisabled, nil } // Read /sys/fs/selinux/enforce to determine if SELinux is in enforcing mode // Make sure LSM mode is actually enforcing, in case we later allow setenforce // IsEnabled is reliable, since LSM is active whenever SELinuxFS is mounted, which is done accordingly data, err := os.ReadFile("/sys/fs/selinux/enforce") if err != nil { return runtimeres.SELinuxStateDisabled, err } if strings.TrimSpace(string(data)) == "1" { return runtimeres.SELinuxStateEnforcing, nil } return runtimeres.SELinuxStatePermissive, nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/ext-spdx/tailscale.spdx.json ================================================ { "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "sidero-pkgs-tailscale", "documentNamespace": "https://anchore.com/bldr/dir/sidero-pkgs-tailscale-945fd1b5-e1c4-5ceb-8c4e-7de776c0983c", "creationInfo": { "licenseListVersion": "3.25", "creators": [ "Organization: Anchore, Inc", "Tool: bldr-v0.5.1" ], "created": "2022-01-20T18:35:52Z" }, "packages": [ { "name": "tailscale", "SPDXID": "SPDXRef-Package-bldr-package-tailscale-bc56a5bc00e5302c", "versionInfo": "1.84.2", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "sourceInfo": "acquired package info from the following paths: /Pkgfile", "licenseConcluded": "NOASSERTION", "licenseDeclared": "BSD-3-Clause", "copyrightText": "NOASSERTION", "externalRefs": [ { "referenceCategory": "SECURITY", "referenceType": "cpe23Type", "referenceLocator": "cpe:2.3:a:tailscale:tailscale:1.84.2:*:*:*:*:*:*:*" } ] }, { "name": "sidero-pkgs-tailscale", "SPDXID": "SPDXRef-DocumentRoot-Directory-sidero-pkgs-tailscale", "versionInfo": "1.84.2", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "primaryPackagePurpose": "FILE" } ], "files": [ { "fileName": "https://github.com/tailscale/tailscale/archive/refs/tags/v1.84.2.tar.gz", "SPDXID": "SPDXRef-File-...archive-refs-tags-v1.84.2.tar.gz-c5546c6b96afad2e", "checksums": [ { "algorithm": "SHA256", "checksumValue": "32673e5552e1176f1028a6a90a4c892d2475c92d1e952ca16156dc523d14d914" }, { "algorithm": "SHA512", "checksumValue": "f4baaa9070a2ad4f4ddef9bac89fc5333dc1a74db68a976c30a20a7359ebc51d14905ef0098b1046493fb99a829b9b10502e03b12799e94ef543ebffc6018553" } ], "licenseConcluded": "NOASSERTION", "licenseInfoInFiles": [ "NOASSERTION" ], "copyrightText": "NOASSERTION", "comment": "layerID: bldr sources" } ], "relationships": [ { "spdxElementId": "SPDXRef-Package-bldr-package-tailscale-", "relatedSpdxElement": "SPDXRef-File-...archive-refs-tags-v1.84.2.tar.gz-c5546c6b96afad2e", "relationshipType": "CONTAINS" }, { "spdxElementId": "SPDXRef-DocumentRoot-Directory-sidero-pkgs-tailscale", "relatedSpdxElement": "SPDXRef-Package-bldr-package-tailscale-bc56a5bc00e5302c", "relationshipType": "CONTAINS" }, { "spdxElementId": "SPDXRef-DOCUMENT", "relatedSpdxElement": "SPDXRef-DocumentRoot-Directory-sidero-pkgs-tailscale", "relationshipType": "DESCRIBES" } ] } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/extservices/foo.bar ================================================ ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/extservices/frr.yaml ================================================ name: frr container: entrypoint: ./frr args: - --msg - BGP FRR depends: - network: - addresses restart: always ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/extservices/hello.yaml ================================================ name: hello-world container: entrypoint: ./hello-world args: - --msg - Talos Linux Extension Service depends: - network: - addresses restart: always ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/extservices/invalid.yaml ================================================ name: invalid container: entrypoint: ./hello-world args: - --msg - Talos Linux Extension Service depends: - nothing: true restart: random ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/extservices/zduplicate.yaml ================================================ name: hello-world container: entrypoint: ./duplicate args: - should not get registered depends: - network: - addresses restart: always ================================================ FILE: internal/app/machined/pkg/controllers/runtime/testdata/spdx/test.spdx.json ================================================ { "spdxVersion": "SPDX-2.3", "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "Talos (amd64)", "documentNamespace": "https://anchore.com/syft/dir/Talos%20%28amd64%29-ab5f950d-20cd-5a2f-b198-9e345e29f1c8", "creationInfo": { "licenseListVersion": "3.25", "creators": [ "Organization: Anchore, Inc", "Tool: syft-[not provided]" ], "created": "2025-07-07T19:03:36Z" }, "packages": [ { "name": "apparmor-x86_64", "SPDXID": "SPDXRef-Package-bldr-package-apparmor-x86-64-925cc702a977d6be", "versionInfo": "v3.1.7", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "sourceInfo": "acquired package info from SBOM: /apparmor.spdx.json", "licenseConcluded": "NOASSERTION", "licenseDeclared": "GPL-2.0-or-later", "copyrightText": "NOASSERTION", "externalRefs": [ { "referenceCategory": "SECURITY", "referenceType": "cpe23Type", "referenceLocator": "cpe:2.3:a:apparmor:apparmor:v3.1.7:*:*:*:*:*:*:*" }, { "referenceCategory": "SECURITY", "referenceType": "cpe23Type", "referenceLocator": "cpe:2.3:a:canonical:apparmor:v3.1.7:*:*:*:*:*:*:*" } ] }, { "name": "cel.dev/expr", "SPDXID": "SPDXRef-Package-go-module-cel.dev-expr-e4715f3e7652c734", "versionInfo": "v0.24.0", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", "filesAnalyzed": false, "sourceInfo": "acquired package info from go module information: /go.mod", "licenseConcluded": "NOASSERTION", "licenseDeclared": "NOASSERTION", "copyrightText": "NOASSERTION", "externalRefs": [ { "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:golang/cel.dev/expr@v0.24.0" } ] } ], "relationships": [ { "spdxElementId": "SPDXRef-Package-go-module-github.com-jonboulle-clockwork-008a81e8156b0c05", "relatedSpdxElement": "SPDXRef-File-go.mod-3fc5a8d3d86e9790", "relationshipType": "OTHER", "comment": "evident-by: indicates the package's existence is evident by the given file" } ] } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/unique_token.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // UniqueMachineTokenController is a controller that manages SideroLink unique token. type UniqueMachineTokenController struct{} // Name implements controller.Controller interface. func (ctrl *UniqueMachineTokenController) Name() string { return "runtime.UniqueMachineTokenController" } // Inputs implements controller.Controller interface. func (ctrl *UniqueMachineTokenController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.MetaKeyType, ID: optional.Some(runtime.MetaKeyTagToID(meta.UniqueMachineToken)), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.MetaLoadedType, Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *UniqueMachineTokenController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.UniqueMachineTokenType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *UniqueMachineTokenController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() metaLoaded, err := safe.ReaderGetByID[*runtime.MetaLoaded](ctx, r, runtime.MetaLoadedID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get meta loaded: %w", err) } metaKey, err := safe.ReaderGetByID[*runtime.MetaKey](ctx, r, runtime.MetaKeyTagToID(meta.UniqueMachineToken)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get unique token meta key: %w", err) } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get machine config: %w", err) } if metaLoaded != nil { var token string if metaKey != nil { token = metaKey.TypedSpec().Value } else if cfg != nil { if cfg.Config().SideroLink() != nil { token = cfg.Config().SideroLink().UniqueToken() } } if err = safe.WriterModify(ctx, r, runtime.NewUniqueMachineToken(), func(out *runtime.UniqueMachineToken) error { out.TypedSpec().Token = token return nil }); err != nil { return fmt.Errorf("failed to update unique token: %w", err) } } if err = safe.CleanupOutputs[*runtime.UniqueMachineToken](ctx, r); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/unique_token_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type UniqueMachineTokenSuite struct { ctest.DefaultSuite } func TestUniqueMachineTokenSuite(t *testing.T) { t.Parallel() suite.Run(t, &UniqueMachineTokenSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.UniqueMachineTokenController{})) }, }, }) } func (suite *UniqueMachineTokenSuite) TestReconcileNoConfig() { ctest.AssertNoResource[*runtime.UniqueMachineToken](suite, runtime.UniqueMachineTokenID) suite.Create(runtime.NewMetaLoaded()) ctest.AssertResource(suite, runtime.UniqueMachineTokenID, func(token *runtime.UniqueMachineToken, asrt *assert.Assertions) { asrt.Empty(token.TypedSpec().Token) }) metaKey := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.UniqueMachineToken)) metaKey.TypedSpec().Value = "token1" suite.Create(metaKey) ctest.AssertResource(suite, runtime.UniqueMachineTokenID, func(token *runtime.UniqueMachineToken, asrt *assert.Assertions) { asrt.Equal("token1", token.TypedSpec().Token) }) } func (suite *UniqueMachineTokenSuite) TestReconcileWithConfig() { sideroLinkConfig := siderolink.NewConfigV1Alpha1() sideroLinkConfig.UniqueTokenConfig = "token2" ctr, err := container.New(sideroLinkConfig) suite.Require().NoError(err) cfg := config.NewMachineConfig(ctr) suite.Create(cfg) ctest.AssertNoResource[*runtime.UniqueMachineToken](suite, runtime.UniqueMachineTokenID) suite.Create(runtime.NewMetaLoaded()) ctest.AssertResource(suite, runtime.UniqueMachineTokenID, func(token *runtime.UniqueMachineToken, asrt *assert.Assertions) { asrt.Equal("token2", token.TypedSpec().Token) }) metaKey := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.UniqueMachineToken)) metaKey.TypedSpec().Value = "token1" suite.Create(metaKey) ctest.AssertResource(suite, runtime.UniqueMachineTokenID, func(token *runtime.UniqueMachineToken, asrt *assert.Assertions) { asrt.Equal("token1", token.TypedSpec().Token) }) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "bytes" "context" "os" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // WaitForDevicesReady waits for devices to be ready. // // It is a helper function for controllers. func WaitForDevicesReady(ctx context.Context, r controller.Runtime, nextInputs []controller.Input) error { // set inputs temporarily to a service only if err := r.UpdateInputs([]controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.DevicesStatusType, ID: optional.Some(runtime.DevicesID), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return ctx.Err() case <-r.EventCh(): } status, err := safe.ReaderGetByID[*runtime.DevicesStatus](ctx, r, runtime.DevicesID) if err != nil { if state.IsNotFoundError(err) { continue } return err } if status.TypedSpec().Ready { // condition met break } } // restore inputs if err := r.UpdateInputs(nextInputs); err != nil { return err } // queue an update to reprocess with new inputs r.QueueReconcile() return nil } // updateFile is like `os.WriteFile`, but it will only update the file if the // contents have changed. func updateFile(filename string, contents []byte, mode os.FileMode) error { oldContents, err := os.ReadFile(filename) if err == nil && bytes.Equal(oldContents, contents) { return nil } return os.WriteFile(filename, contents, mode) } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/version" ) // VersionController populates the version of currently running Talos. type VersionController struct{} // Name implements controller.Controller interface. func (ctrl *VersionController) Name() string { return "runtime.VersionController" } // Inputs implements controller.Controller interface. func (ctrl *VersionController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *VersionController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.VersionType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *VersionController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if err := safe.WriterModify(ctx, r, runtime.NewVersion(), func(status *runtime.Version) error { status.TypedSpec().Version = version.Tag return nil }); err != nil { return fmt.Errorf("failed to update version status: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/watchdog_timer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "os" "syscall" "time" "unsafe" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // WatchdogTimerController watches v1alpha1.Config, creates/updates/deletes kernel module specs. type WatchdogTimerController struct{} // Name implements controller.Controller interface. func (ctrl *WatchdogTimerController) Name() string { return "runtime.WatchdogTimerController" } // Inputs implements controller.Controller interface. func (ctrl *WatchdogTimerController) Inputs() []controller.Input { return []controller.Input{ { Namespace: runtime.NamespaceName, Type: runtime.WatchdogTimerConfigType, ID: optional.Some(runtime.WatchdogTimerConfigID), }, } } // Outputs implements controller.Controller interface. func (ctrl *WatchdogTimerController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.WatchdogTimerStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *WatchdogTimerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var ( ticker *time.Ticker tickerC <-chan time.Time ) tickerStop := func() { if ticker == nil { return } ticker.Stop() ticker = nil tickerC = nil } defer tickerStop() var wd *os.File wdClose := func() { if wd == nil { return } logger.Info("closing hardware watchdog", zap.String("path", wd.Name())) // Magic close: make sure old watchdog won't trip after we close it if _, err := wd.WriteString("V"); err != nil { logger.Error("failed to send magic close to watchdog", zap.String("path", wd.Name())) } if err := wd.Close(); err != nil { logger.Error("failed to close watchdog", zap.String("path", wd.Name())) } wd = nil } defer wdClose() for { select { case <-ctx.Done(): return nil case <-tickerC: if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), unix.WDIOC_KEEPALIVE, 0); err != 0 { return fmt.Errorf("failed to feed watchdog: %w", err) } continue case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*runtime.WatchdogTimerConfig](ctx, r, runtime.WatchdogTimerConfigID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting watchdog config: %w", err) } } r.StartTrackingOutputs() if cfg == nil { tickerStop() wdClose() } else { // close the watchdog if requested to use new one if wd != nil && wd.Name() != cfg.TypedSpec().Device { wdClose() } if wd == nil { wd, err = os.OpenFile(cfg.TypedSpec().Device, syscall.O_RDWR, 0o600) if err != nil { return fmt.Errorf("failed to open watchdog device: %s", err) } logger.Info("opened hardware watchdog", zap.String("path", cfg.TypedSpec().Device)) } timeout := int(cfg.TypedSpec().Timeout.Seconds()) if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), uintptr(unix.WDIOC_SETTIMEOUT), uintptr(unsafe.Pointer(&timeout))); err != 0 { return fmt.Errorf("failed to set watchdog timeout: %w", err) } tickerStop() // 3 pings per timeout should suffice in any case feedInterval := cfg.TypedSpec().Timeout / 3 ticker = time.NewTicker(feedInterval) tickerC = ticker.C if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, wd.Fd(), uintptr(unix.WDIOC_KEEPALIVE), 0); err != 0 { return fmt.Errorf("failed to feed watchdog: %w", err) } logger.Info("set hardware watchdog timeout", zap.Duration("timeout", cfg.TypedSpec().Timeout), zap.Duration("feed_interval", feedInterval)) if err = safe.WriterModify(ctx, r, runtime.NewWatchdogTimerStatus(cfg.Metadata().ID()), func(status *runtime.WatchdogTimerStatus) error { status.TypedSpec().Device = cfg.TypedSpec().Device status.TypedSpec().Timeout = cfg.TypedSpec().Timeout status.TypedSpec().FeedInterval = feedInterval return nil }); err != nil { return fmt.Errorf("error updating watchdog status: %w", err) } } if err = safe.CleanupOutputs[*runtime.WatchdogTimerStatus](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/watchdog_timer_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // WatchdogTimerConfigController generates configuration for watchdog timers. type WatchdogTimerConfigController struct{} // Name implements controller.Controller interface. func (ctrl *WatchdogTimerConfigController) Name() string { return "runtime.WatchdogTimerConfigController" } // Inputs implements controller.Controller interface. func (ctrl *WatchdogTimerConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *WatchdogTimerConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: runtime.WatchdogTimerConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *WatchdogTimerConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) (err error) { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine config: %w", err) } r.StartTrackingOutputs() if cfg != nil { if watchdogConfig := cfg.Config().Runtime().WatchdogTimer(); watchdogConfig != nil { if err = safe.WriterModify(ctx, r, runtime.NewWatchdogTimerConfig(), func(cfg *runtime.WatchdogTimerConfig) error { cfg.TypedSpec().Device = watchdogConfig.Device() cfg.TypedSpec().Timeout = watchdogConfig.Timeout() return nil }); err != nil { return fmt.Errorf("error updating kmsg log config: %w", err) } } } if err = safe.CleanupOutputs[*runtime.WatchdogTimerConfig](ctx, r); err != nil { return err } } } ================================================ FILE: internal/app/machined/pkg/controllers/runtime/watchdog_timer_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" runtimectrls "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type WatchdogTimerConfigSuite struct { ctest.DefaultSuite } func TestWatchdogTimerConfigSuite(t *testing.T) { suite.Run(t, new(WatchdogTimerConfigSuite)) } func (suite *WatchdogTimerConfigSuite) TestWatchdogTimerConfigNone() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.WatchdogTimerConfigController{})) rtestutils.AssertNoResource[*runtime.WatchdogTimerConfig](suite.Ctx(), suite.T(), suite.State(), runtime.WatchdogTimerConfigID) } func (suite *WatchdogTimerConfigSuite) TestWatchdogTimerConfigMachineConfig() { suite.Require().NoError(suite.Runtime().RegisterController(&runtimectrls.WatchdogTimerConfigController{})) watchdogTimerConfig := &runtimecfg.WatchdogTimerV1Alpha1{ WatchdogDevice: "/dev/watchdog0", } cfg, err := container.New(watchdogTimerConfig) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources[*runtime.WatchdogTimerConfig](suite.Ctx(), suite.T(), suite.State(), []resource.ID{runtime.WatchdogTimerConfigID}, func(cfg *runtime.WatchdogTimerConfig, asrt *assert.Assertions) { asrt.Equal( "/dev/watchdog0", cfg.TypedSpec().Device, ) asrt.Equal( runtimecfg.DefaultWatchdogTimeout, cfg.TypedSpec().Timeout, ) }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/api.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" stdlibx509 "crypto/x509" "errors" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/grpc/gen" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/role" ) // APIController manages secrets.API based on configuration to provide apid certificate. type APIController struct{} // Name implements controller.Controller interface. func (ctrl *APIController) Name() string { return "secrets.APIController" } // Inputs implements controller.Controller interface. func (ctrl *APIController) Inputs() []controller.Input { // initial set of inputs: wait for machine type to be known and network to be partially configured return []controller.Input{ { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *APIController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.APIType, Kind: controller.OutputExclusive, }, } } // errMachineTypeChanged is used to signal that machine type has changed, and the current reconcile loop should be aborted, and restarted with new machine type. var errMachineTypeChanged = errors.New("machine type changed") // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *APIController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // reset inputs back to what they were initially if err := r.UpdateInputs(ctrl.Inputs()); err != nil { return err } machineTypeRes, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine type: %w", err) } // Default to machine.TypeUnknown here (zero value). var machineType machine.Type if machineTypeRes != nil { machineType = machineTypeRes.MachineType() } networkResource, err := safe.ReaderGet[*network.Status](ctx, r, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } networkStatus := networkResource.TypedSpec() if !networkStatus.AddressReady { continue } // machine type is known and network is ready, we can now proceed to one or another reconcile loop switch machineType { case machine.TypeInit, machine.TypeControlPlane: err = ctrl.reconcile(ctx, r, logger, true) case machine.TypeWorker: err = ctrl.reconcile(ctx, r, logger, false) case machine.TypeUnknown: // maintenance mode configuration, use maintenance service root CA, and skip client verification err = ctrl.reconcileMaintenance(ctx, r, logger) default: panic(fmt.Sprintf("unexpected machine type %v", machineType)) } if err != nil { // this is expected error, we just need to restart the reconcile loop // the teardownAll below will take care of tearing down current API secrets if !errors.Is(err, errMachineTypeChanged) { return err } r.QueueReconcile() } if err = ctrl.teardownAll(ctx, r); err != nil { return err } r.ResetRestartBackoff() } } //nolint:gocyclo,cyclop,dupl func (ctrl *APIController) reconcile(ctx context.Context, r controller.Runtime, logger *zap.Logger, isControlplane bool) error { inputs := []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.OSRootType, ID: optional.Some(secrets.OSRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.CertSANType, ID: optional.Some(secrets.CertSANAPIID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, // time status isn't fetched, but the fact that it is in dependencies means // that certs will be regenerated on time sync/jump (as reconcile will be triggered) { Namespace: v1alpha1.NamespaceName, Type: timeresource.StatusType, ID: optional.Some(timeresource.StatusID), Kind: controller.InputWeak, }, } if !isControlplane { // worker nodes depend on endpoint list inputs = append(inputs, controller.Input{ Namespace: k8s.ControlPlaneNamespaceName, Type: k8s.EndpointType, Kind: controller.InputWeak, }) } if err := r.UpdateInputs(inputs); err != nil { return fmt.Errorf("error updating inputs: %w", err) } r.QueueReconcile() refreshTicker := time.NewTicker(x509.DefaultCertificateValidityDuration / 2) defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-refreshTicker.C: } machineTypeRes, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine type: %w", err) } var machineType machine.Type if machineTypeRes != nil { machineType = machineTypeRes.MachineType() } switch machineType { case machine.TypeInit, machine.TypeControlPlane: if !isControlplane { return errMachineTypeChanged } case machine.TypeWorker: if isControlplane { return errMachineTypeChanged } case machine.TypeUnknown: return errMachineTypeChanged default: panic(fmt.Sprintf("unexpected machine type %v", machineType)) } rootResource, err := safe.ReaderGet[*secrets.OSRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting etcd root secrets: %w", err) } rootSpec := rootResource.TypedSpec() certSANResource, err := safe.ReaderGet[*secrets.CertSAN](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.CertSANType, secrets.CertSANAPIID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting certSANs: %w", err) } certSANs := certSANResource.TypedSpec() var endpointsStr []string if !isControlplane { endpointResources, err := r.List(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, k8s.EndpointType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("error getting endpoints resources: %w", err) } var endpointAddrs k8s.EndpointList // merge all endpoints into a single list for _, res := range endpointResources.Items { endpointAddrs = endpointAddrs.Merge(res.(*k8s.Endpoint)) } if endpointAddrs.IsEmpty() { continue } endpointsStr = endpointAddrs.Strings() } if isControlplane { if err := ctrl.generateControlPlane(ctx, r, logger, rootSpec, certSANs); err != nil { return err } } else { if err := ctrl.generateWorker(ctx, r, logger, rootSpec, endpointsStr, certSANs); err != nil { return err } } r.ResetRestartBackoff() } } func (ctrl *APIController) generateControlPlane(ctx context.Context, r controller.Runtime, logger *zap.Logger, rootSpec *secrets.OSRootSpec, certSANs *secrets.CertSANSpec) error { ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(rootSpec.IssuingCA) if err != nil { return fmt.Errorf("failed to parse CA certificate: %w", err) } serverCert, err := x509.NewKeyPair(ca, x509.IPAddresses(certSANs.StdIPs()), x509.DNSNames(certSANs.DNSNames), x509.CommonName(certSANs.FQDN), x509.NotAfter(time.Now().Add(x509.DefaultCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, }), ) if err != nil { return fmt.Errorf("failed to generate API server cert: %w", err) } clientCert, err := x509.NewKeyPair(ca, x509.CommonName(certSANs.FQDN), x509.Organization(string(role.Impersonator)), x509.NotAfter(time.Now().Add(x509.DefaultCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageClientAuth, }), ) if err != nil { return fmt.Errorf("failed to generate API client cert: %w", err) } if err := safe.WriterModify(ctx, r, secrets.NewAPI(), func(r *secrets.API) error { apiSecrets := r.TypedSpec() apiSecrets.AcceptedCAs = rootSpec.AcceptedCAs apiSecrets.Server = x509.NewCertificateAndKeyFromKeyPair(serverCert) apiSecrets.Client = x509.NewCertificateAndKeyFromKeyPair(clientCert) apiSecrets.SkipVerifyingClientCert = false return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } clientFingerprint, _ := x509.SPKIFingerprintFromDER(clientCert.Certificate.Certificate[0]) //nolint:errcheck serverFingerprint, _ := x509.SPKIFingerprintFromDER(serverCert.Certificate.Certificate[0]) //nolint:errcheck logger.Debug("generated new certificates", zap.Stringer("client", clientFingerprint), zap.Stringer("server", serverFingerprint), ) return nil } func (ctrl *APIController) generateWorker(ctx context.Context, r controller.Runtime, logger *zap.Logger, rootSpec *secrets.OSRootSpec, endpointsStr []string, certSANs *secrets.CertSANSpec, ) error { remoteGen, err := gen.NewRemoteGenerator(rootSpec.Token, endpointsStr, rootSpec.AcceptedCAs) if err != nil { return fmt.Errorf("failed creating trustd client: %w", err) } defer remoteGen.Close() //nolint:errcheck // use the last CA in the list of accepted CAs as a template if len(rootSpec.AcceptedCAs) == 0 { return errors.New("no accepted CAs") } acceptedCA, err := rootSpec.AcceptedCAs[len(rootSpec.AcceptedCAs)-1].GetCert() if err != nil { return fmt.Errorf("failed to parse CA certificate: %w", err) } serverCSR, serverCert, err := x509.NewCSRAndIdentityFromCA( acceptedCA, x509.IPAddresses(certSANs.StdIPs()), x509.DNSNames(certSANs.DNSNames), x509.CommonName(certSANs.FQDN), ) if err != nil { return fmt.Errorf("failed to generate API server CSR: %w", err) } logger.Debug("sending CSR", zap.Strings("endpoints", endpointsStr)) var ca []byte // run the CSR generation in a goroutine, so we can abort the request if the inputs change errCh := make(chan error) ctx, cancel := context.WithCancel(ctx) defer cancel() go func() { ca, serverCert.Crt, err = remoteGen.IdentityContext(ctx, serverCSR) errCh <- err }() select { case <-r.EventCh(): // there's an update to the inputs, terminate the attempt, and let the controller handle the retry cancel() // re-queue the reconcile event, so that controller retries with new inputs r.QueueReconcile() // wait for the goroutine to finish, ignoring the error (should be context.Canceled) <-errCh return nil case err = <-errCh: } if err != nil { return fmt.Errorf("failed to sign API server CSR: %w", err) } if err := safe.WriterModify(ctx, r, secrets.NewAPI(), func(r *secrets.API) error { apiSecrets := r.TypedSpec() apiSecrets.AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: ca, }, } apiSecrets.Client = nil apiSecrets.Server = serverCert apiSecrets.SkipVerifyingClientCert = false return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } serverFingerprint, _ := x509.SPKIFingerprintFromPEM(serverCert.Crt) //nolint:errcheck logger.Debug("generated new certificates", zap.Stringer("server", serverFingerprint), ) return nil } //nolint:dupl,gocyclo func (ctrl *APIController) reconcileMaintenance(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { inputs := []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.MaintenanceRootType, ID: optional.Some(secrets.MaintenanceRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.CertSANType, ID: optional.Some(secrets.CertSANMaintenanceID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, // time status isn't fetched, but the fact that it is in dependencies means // that certs will be regenerated on time sync/jump (as reconcile will be triggered) { Namespace: v1alpha1.NamespaceName, Type: timeresource.StatusType, ID: optional.Some(timeresource.StatusID), Kind: controller.InputWeak, }, } if err := r.UpdateInputs(inputs); err != nil { return fmt.Errorf("error updating inputs: %w", err) } r.QueueReconcile() refreshTicker := time.NewTicker(x509.DefaultCertificateValidityDuration / 2) defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-refreshTicker.C: } machineTypeRes, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error getting machine type: %w", err) } var machineType machine.Type if machineTypeRes != nil { machineType = machineTypeRes.MachineType() } if machineType != machine.TypeUnknown { return errMachineTypeChanged } rootSecrets, err := safe.ReaderGetByID[*secrets.MaintenanceRoot](ctx, r, secrets.MaintenanceRootID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting maintenance root secrets: %w", err) } certSANs, err := safe.ReaderGetByID[*secrets.CertSAN](ctx, r, secrets.CertSANMaintenanceID) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting certSANs: %w", err) } ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(rootSecrets.TypedSpec().CA) if err != nil { return fmt.Errorf("failed to parse CA certificate: %w", err) } serverCert, err := x509.NewKeyPair(ca, x509.IPAddresses(certSANs.TypedSpec().StdIPs()), x509.DNSNames(certSANs.TypedSpec().DNSNames), x509.CommonName(certSANs.TypedSpec().FQDN), x509.NotAfter(time.Now().Add(x509.DefaultCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, }), ) if err != nil { return fmt.Errorf("failed to generate maintenance server cert: %w", err) } if err := safe.WriterModify(ctx, r, secrets.NewAPI(), func(r *secrets.API) error { apiSecrets := r.TypedSpec() apiSecrets.AcceptedCAs = nil apiSecrets.Client = nil apiSecrets.Server = x509.NewCertificateAndKeyFromKeyPair(serverCert) apiSecrets.SkipVerifyingClientCert = true return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } serverFingerprint, _ := x509.SPKIFingerprintFromDER(serverCert.Certificate.Certificate[0]) //nolint:errcheck logger.Debug("generated new certificates", zap.Stringer("server", serverFingerprint), ) r.ResetRestartBackoff() } } func (ctrl *APIController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.APIType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/api_cert_sans.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // APICertSANsController manages secrets.APICertSANs based on configuration. type APICertSANsController struct{} // Name implements controller.Controller interface. func (ctrl *APICertSANsController) Name() string { return "secrets.APICertSANsController" } // Inputs implements controller.Controller interface. // //nolint:dupl func (ctrl *APICertSANsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.OSRootType, ID: optional.Some(secrets.OSRootID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s)), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *APICertSANsController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.CertSANType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *APICertSANsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } apiRootRes, err := safe.ReaderGet[*secrets.OSRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting root k8s secrets: %w", err) } apiRoot := apiRootRes.TypedSpec() hostnameResource, err := safe.ReaderGet[*network.HostnameStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } hostnameStatus := hostnameResource.TypedSpec() addressesResource, err := safe.ReaderGet[*network.NodeAddress](ctx, r, resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } nodeAddresses := addressesResource.TypedSpec() if err = safe.WriterModify(ctx, r, secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANAPIID), func(r *secrets.CertSAN) error { spec := r.TypedSpec() spec.Reset() spec.AppendIPs(apiRoot.CertSANIPs...) spec.AppendIPs(nodeAddresses.IPs()...) spec.AppendDNSNames(apiRoot.CertSANDNSNames...) spec.AppendDNSNames(hostnameStatus.Hostname, hostnameStatus.FQDN()) spec.FQDN = hostnameStatus.FQDN() spec.Sort() return nil }); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *APICertSANsController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.CertSANType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if res.Metadata().Owner() == ctrl.Name() { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/api_cert_sans_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "fmt" "net/netip" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestAPICertSANsSuite(t *testing.T) { suite.Run(t, &APICertSANsSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.APICertSANsController{})) }, }, }) } type APICertSANsSuite struct { ctest.DefaultSuite } func (suite *APICertSANsSuite) TestReconcileControlPlane() { rootSecrets := secrets.NewOSRoot(secrets.OSRootID) rootSecrets.TypedSpec().CertSANDNSNames = []string{"some.org"} rootSecrets.TypedSpec().CertSANIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} suite.Require().NoError(suite.State().Create(suite.Ctx(), rootSecrets)) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "bar" hostnameStatus.TypedSpec().Domainname = "some.org" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) nodeAddresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s), ) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.2.1.3/24"), netip.MustParsePrefix("172.16.0.1/32"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeAddresses)) suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error { certSANs, err := ctest.Get[*secrets.CertSAN]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.CertSANType, secrets.CertSANAPIID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := certSANs.TypedSpec() suite.Assert().Equal([]string{"bar", "bar.some.org", "some.org"}, spec.DNSNames) suite.Assert().Equal("[10.2.1.3 10.4.3.2 172.16.0.1]", fmt.Sprintf("%v", spec.IPs)) suite.Assert().Equal("bar.some.org", spec.FQDN) return nil }) ctest.UpdateWithConflicts(suite, rootSecrets, func(rootSecrets *secrets.OSRoot) error { rootSecrets.TypedSpec().CertSANDNSNames = []string{"other.org"} return nil }) suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error { certSANs, err := ctest.Get[*secrets.CertSAN]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.CertSANType, secrets.CertSANAPIID, resource.VersionUndefined, ), ) if err != nil { return err } spec := certSANs.TypedSpec() expectedDNSNames := []string{"bar", "bar.some.org", "other.org"} if !slices.Equal(expectedDNSNames, spec.DNSNames) { return retry.ExpectedErrorf("expected %v, got %v", expectedDNSNames, spec.DNSNames) } return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/api_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( stdlibx509 "crypto/x509" "fmt" "net/netip" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/role" ) func TestAPISuite(t *testing.T) { suite.Run(t, &APISuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.APIController{})) }, }, }) } type APISuite struct { ctest.DefaultSuite } func (suite *APISuite) TestReconcileControlPlane() { rootSecrets := secrets.NewOSRoot(secrets.OSRootID) talosCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("talos"), ) suite.Require().NoError(err) rootSecrets.TypedSpec().IssuingCA = &x509.PEMEncodedCertificateAndKey{ Crt: talosCA.CrtPEM, Key: talosCA.KeyPEM, } rootSecrets.TypedSpec().AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: talosCA.CrtPEM, }, } rootSecrets.TypedSpec().CertSANDNSNames = []string{"example.com"} rootSecrets.TypedSpec().CertSANIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} rootSecrets.TypedSpec().Token = "something" suite.Create(rootSecrets) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Create(machineType) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true suite.Create(networkStatus) certSANs := secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANAPIID) certSANs.TypedSpec().Append( "example.com", "foo", "foo.example.com", "10.2.1.3", "10.4.3.2", "172.16.0.1", ) certSANs.TypedSpec().FQDN = "foo.example.com" suite.Create(certSANs) ctest.AssertResource(suite, secrets.APIID, func(certs *secrets.API, asrt *assert.Assertions) { apiCerts := certs.TypedSpec() asrt.Equal( []*x509.PEMEncodedCertificate{ { Crt: talosCA.CrtPEM, }, }, apiCerts.AcceptedCAs, ) serverCert, err := apiCerts.Server.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"example.com", "foo", "foo.example.com"}, serverCert.DNSNames) asrt.Equal("[10.2.1.3 10.4.3.2 172.16.0.1]", fmt.Sprintf("%v", serverCert.IPAddresses)) asrt.Equal("foo.example.com", serverCert.Subject.CommonName) asrt.Empty(serverCert.Subject.Organization) asrt.Equal( stdlibx509.KeyUsageDigitalSignature, serverCert.KeyUsage, ) asrt.Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageServerAuth}, serverCert.ExtKeyUsage) clientCert, err := apiCerts.Client.GetCert() if !asrt.NoError(err) { return } asrt.Empty(clientCert.DNSNames) asrt.Empty(clientCert.IPAddresses) asrt.Equal("foo.example.com", clientCert.Subject.CommonName) asrt.Equal([]string{string(role.Impersonator)}, clientCert.Subject.Organization) asrt.Equal( stdlibx509.KeyUsageDigitalSignature, clientCert.KeyUsage, ) asrt.Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageClientAuth}, clientCert.ExtKeyUsage) asrt.False(apiCerts.SkipVerifyingClientCert) }, ) // destroy machine type, mocking transition to maintenance mode suite.Destroy(machineType) ctest.AssertNoResource[*secrets.API](suite, secrets.APIID) } func (suite *APISuite) TestReconcileMaintenance() { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.MaintenanceRootController{})) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true suite.Create(networkStatus) certSANs := secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANMaintenanceID) certSANs.TypedSpec().Append( "example.com", "10.2.1.3", ) certSANs.TypedSpec().FQDN = constants.MaintenanceServiceCommonName suite.Create(certSANs) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeUnknown) suite.Create(machineType) ctest.AssertResource(suite, secrets.APIID, func(certs *secrets.API, asrt *assert.Assertions) { apiCerts := certs.TypedSpec() asrt.True(apiCerts.SkipVerifyingClientCert) asrt.Nil(apiCerts.Client) asrt.Nil(apiCerts.AcceptedCAs) serverCert, err := apiCerts.Server.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"example.com"}, serverCert.DNSNames) asrt.Equal("[10.2.1.3]", fmt.Sprintf("%v", serverCert.IPAddresses)) asrt.Equal(constants.MaintenanceServiceCommonName, serverCert.Subject.CommonName) asrt.Empty(serverCert.Subject.Organization) asrt.Equal( stdlibx509.KeyUsageDigitalSignature, serverCert.KeyUsage, ) asrt.Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageServerAuth}, serverCert.ExtKeyUsage) }, ) // create machine type, mocking transition to control plane mode machineType.SetMachineType(machine.TypeControlPlane) suite.Update(machineType) ctest.AssertNoResource[*secrets.API](suite, secrets.APIID) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/data/ca-certificates ================================================ ## ## Bundle of CA Root Certificates ## ## Certificate data from Mozilla as of: Tue Dec 2 04:12:02 2025 GMT ## ## Find updated versions here: https://curl.se/docs/caextract.html ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates ## file (certdata.txt). This file can be found in the mozilla source tree: ## https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt ## ## It contains the certificates in PEM format and therefore ## can be directly used with curl / libcurl / php_curl, or with ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.30. ## SHA256: a903b3cd05231e39332515ef7ebe37e697262f39515a52015c23c62805b73cd0 ## Entrust Root Certification Authority ==================================== -----BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMCVVMxFjAUBgNV BAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0Lm5ldC9DUFMgaXMgaW5jb3Jw b3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMWKGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsG A1UEAxMkRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0 MloXDTI2MTEyNzIwNTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMu MTkwNwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSByZWZlcmVu Y2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNVBAMTJEVudHJ1c3QgUm9v dCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ALaVtkNC+sZtKm9I35RMOVcF7sN5EUFoNu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYsz A9u3g3s+IIRe7bJWKKf44LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOww Cj0Yzfv9KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGIrb68 j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi94DkZfs0Nw4pgHBN rziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOBsDCBrTAOBgNVHQ8BAf8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAigA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1 MzQyWjAfBgNVHSMEGDAWgBRokORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DH hmak8fdLQ/uEvW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9tO1KzKtvn1ISM Y/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6ZuaAGAT/3B+XxFNSRuzFVJ7yVTa v52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTS W3iDVuycNsMm4hH2Z0kdkquM++v/eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0 tHuu2guQOHXvgR1m0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- QuoVadis Root CA 2 ================== -----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMjAeFw0wNjExMjQx ODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQCaGMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6 XJxgFyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55JWpzmM+Yk lvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bBrrcCaoF6qUWD4gXmuVbB lDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp+ARz8un+XJiM9XOva7R+zdRcAitMOeGy lZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt 66/3FsvbzSUr5R/7mp/iUcw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1Jdxn wQ5hYIizPtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og/zOh D7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UHoycR7hYQe7xFSkyy BNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuIyV77zGHcizN300QyNQliBJIWENie J0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1Ud DgQWBBQahGK8SEwzJQTU7tD2A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGU a6FJpEcwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2fBluornFdLwUv Z+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzng/iN/Ae42l9NLmeyhP3ZRPx3 UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2BlfF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodm VjB3pjd4M1IQWK4/YY7yarHvGH5KWWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK +JDSV6IZUaUtl0HaB0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrW IozchLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPRTUIZ3Ph1 WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWDmbA4CD/pXvk1B+TJYm5X f6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0ZohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II 4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8 VCLAAVBpQ570su9t+Oza8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u -----END CERTIFICATE----- QuoVadis Root CA 3 ================== -----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0xGTAXBgNVBAoT EFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJvb3QgQ0EgMzAeFw0wNjExMjQx OTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM aW1pdGVkMRswGQYDVQQDExJRdW9WYWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDMV0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNgg DhoB4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUrH556VOij KTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd8lyyBTNvijbO0BNO/79K DDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9CabwvvWhDFlaJKjdhkf2mrk7AyxRllDdLkgbv BNDInIjbC3uBr7E9KsRlOni27tyAsdLTmZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwp p5ijJUMv7/FfJuGITfhebtfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8 nT8KKdjcT5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDtWAEX MJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZc6tsgLjoC2SToJyM Gf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A4iLItLRkT9a6fUg+qGkM17uGcclz uD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYDVR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHT BgkrBgEEAb5YAAMwgcUwgZMGCCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmlj YXRlIGNvbnN0aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVudC4wLQYIKwYB BQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2NwczALBgNVHQ8EBAMCAQYwHQYD VR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4GA1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4 ywLQoUmkRzBFMQswCQYDVQQGEwJCTTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UE AxMSUXVvVmFkaXMgUm9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZV qyM07ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSemd1o417+s hvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd+LJ2w/w4E6oM3kJpK27z POuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2 Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadNt54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp 8kokUvd0/bpO5qgdAm6xDYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBC bjPsMZ57k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6szHXu g/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0jWy10QJLZYxkNc91p vGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeTmJlglFwjz1onl14LBQaTNx47aTbr qZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- DigiCert Assured ID Root CA =========================== -----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzEx MTEwMDAwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0Ew ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7cJpSIqvTO 9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYPmDI2dsze3Tyoou9q+yHy UmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW /lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpy oeb6pNnVFzF1roV9Iq4/AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whf GHdPAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRF 66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkq hkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRCdWKuh+vy1dneVrOfzM4UKLkNl2Bc EkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTffwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38Fn SbNd67IJKusm7Xi+fT8r87cmNW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i 8b5QZ7dsvfPxH2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- DigiCert Global Root CA ======================= -----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAw MDAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsBCSDMAZOn TjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97nh6Vfe63SKMI2tavegw5 BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt43C/dxC//AH2hdmoRBBYMql1GNXRor5H 4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7PT19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y 7vrTC0LUq7dBMtoM1O/4gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQAB o2MwYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbRTLtm 8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEF BQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/EsrhMAtudXH/vTBH1jLuG2cenTnmCmr EbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIt tep3Sp+dWOIrWcBAI+0tKIJFPnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886 UAb3LujEV0lsYSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- DigiCert High Assurance EV Root CA ================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSsw KQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAw MFoXDTMxMTExMDAwMDAwMFowbDELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZ MBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFu Y2UgRVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm+9S75S0t Mqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTWPNt0OKRKzE0lgvdKpVMS OO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEMxChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3 MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFBIk5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQ NAQTXKFx01p8VdteZOE3hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUe h10aUAsgEsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMB Af8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY JhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3NecnzyIZgYIVyHbIUf4KmeqvxgydkAQ V8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6zeM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFp myPInngiK3BD41VHMWEZ71jFhS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkK mNEVX58Svnw2Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep+OkuE6N36B9K -----END CERTIFICATE----- SwissSign Gold CA - G2 ====================== -----BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAkNIMRUw EwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2lnbiBHb2xkIENBIC0gRzIwHhcN MDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBFMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dp c3NTaWduIEFHMR8wHQYDVQQDExZTd2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUq t2/876LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+bbqBHH5C jCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c6bM8K8vzARO/Ws/BtQpg vd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqEemA8atufK+ze3gE/bk3lUIbLtK/tREDF ylqM2tIrfKjuvqblCqoOpd8FUrdVxyJdMmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvR AiTysybUa9oEVeXBCsdtMDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuend jIj3o02yMszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69yFGkO peUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPiaG59je883WX0XaxR 7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxMgI93e2CaHt+28kgeDrpOVG2Y4OGi GqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw AwEB/zAdBgNVHQ4EFgQUWyV7lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64 OfPAeGZe6Drn8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe645R88a7A3hfm 5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczOUYrHUDFu4Up+GC9pWbY9ZIEr 44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOf Mke6UiI0HTJ6CVanfCU2qT1L2sCCbwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6m Gu6uLftIdxf+u+yvGPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxp mo/a77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCChdiDyyJk vC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid392qgQmwLOM7XdVAyksLf KzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEppLd6leNcG2mqeSz53OiATIgHQv2ieY2Br NU0LbbqhPcCT4H8js1WtciVORvnSFu+wZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6Lqj viOvrv1vA+ACOzB2+httQc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- SecureTrust CA ============== -----BEGIN CERTIFICATE----- MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBIMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xFzAVBgNVBAMTDlNlY3VyZVRy dXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIzMTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAe BgNVBAoTF1NlY3VyZVRydXN0IENvcnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQX OZEzZum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO0gMdA+9t DWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIaowW8xQmxSPmjL8xk037uH GFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b 01k/unK8RCSc43Oz969XL0Imnal0ugBS8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmH ursCAwEAAaOBnTCBmjATBgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCegJYYj aHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQAwDQYJ KoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt36Z3q059c4EVlew3KW+JwULKUBRSu SceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHf mbx8IVQr5Fiiu1cprp6poxkmD5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZ nMUFdAvnZyPSCPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR 3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= -----END CERTIFICATE----- Secure Global CA ================ -----BEGIN CERTIFICATE----- MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQG EwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBH bG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkxMjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEg MB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwg Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jx YDiJiQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa/FHtaMbQ bqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJjnIFHovdRIWCQtBJwB1g 8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnIHmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYV HDGA76oYa8J719rO+TMg1fW9ajMtgQT7sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi 0XPnj3pDAgMBAAGjgZ0wgZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCswKaAn oCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsGAQQBgjcVAQQDAgEA MA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0LURYD7xh8yOOvaliTFGCRsoTciE6+ OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXOH0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cn CDpOGR86p1hcF895P4vkp9MmI50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/5 3CYNv6ZHdAbYiNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW -----END CERTIFICATE----- COMODO Certification Authority ============================== -----BEGIN CERTIFICATE----- MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UE BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNVBAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1 dGhvcml0eTAeFw0wNjEyMDEwMDAwMDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEb MBkGA1UECBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFD T01PRE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3UcEbVASY06m/weaKXTuH +7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI2GqGd0S7WWaXUF601CxwRM/aN5VCaTww xHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV 4EajcNxo2f8ESIl33rXp+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA 1KGzqSX+DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5OnKVI rLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW/zAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLmNvbW9k b2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOC AQEAPpiem/Yb6dc5t3iuHXIYSdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CP OGEIqB6BCsAvIC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4zJVSk/BwJVmc IGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5ddBA6+C4OmF4O5MBKgxTMVBbkN +8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IBZQ== -----END CERTIFICATE----- COMODO ECC Certification Authority ================================== -----BEGIN CERTIFICATE----- MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTELMAkGA1UEBhMC R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwHhcNMDgwMzA2MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0Ix GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRo b3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSRFtSrYpn1PlILBs5BAH+X 4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0JcfRK9ChQtP6IHG4/bC8vCVlbpVsLM5ni wz2J+Wos77LTBumjQjBAMB0GA1UdDgQWBBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8E BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VG FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= -----END CERTIFICATE----- Certigna ======== -----BEGIN CERTIFICATE----- MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNVBAYTAkZSMRIw EAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4XDTA3MDYyOTE1MTMwNVoXDTI3 MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwI Q2VydGlnbmEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7q XOEm7RFHYeGifBZ4QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyH GxnygQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbwzBfsV1/p ogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q130yGLMLLGq/jj8UEYkg DncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKf Irjxwo1p3Po6WAbfAgMBAAGjgbwwgbkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQ tCRZvgHyUtVF9lo53BEwZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJ BgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzjAQ/J SP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG9w0BAQUFAAOCAQEA hQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8hbV6lUmPOEvjvKtpv6zf+EwLHyzs+ ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFncfca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1klu PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY 1gkIl2PlwS6wt0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- ePKI Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBeMQswCQYDVQQG EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xKjAoBgNVBAsMIWVQS0kg Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMx MjdaMF4xCzAJBgNVBAYTAlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEq MCgGA1UECwwhZVBLSSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAHSyZbCUNs IZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAhijHyl3SJCRImHJ7K2RKi lTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3XDZoTM1PRYfl61dd4s5oz9wCGzh1NlDiv qOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX 12ruOzjjK9SXDrkb5wdJfzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0O WQqraffAsgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uUWH1+ ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLSnT0IFaUQAS2zMnao lQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pHdmX2Os+PYhcZewoozRrSgx4hxyy/ vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJipNiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXi Zo1jDiVN1Rmy5nk3pyKdVDECAwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/Qkqi MAwGA1UdEwQFMAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGBuvl2ICO1J2B0 1GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6YlPwZpVnPDimZI+ymBV3QGypzq KOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkPJXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdV xrsStZf0X4OFunHB2WyBEXYKCrC/gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEP NXubrjlpC2JgQCA2j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+r GNm65ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUBo2M3IUxE xJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS/jQ6fbjpKdx2qcgw+BRx gMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2zGp1iro2C6pSe3VkQw63d4k3jMdXH7Ojy sP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTEW9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmOD BCEIZ43ygknQW/2xzQ+DhNQ+IIX3Sj0rnP0qCglN6oH4EZw= -----END CERTIFICATE----- certSIGN ROOT CA ================ -----BEGIN CERTIFICATE----- MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYTAlJPMREwDwYD VQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTAeFw0wNjA3MDQxNzIwMDRa Fw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UE CxMQY2VydFNJR04gUk9PVCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7I JUqOtdu0KBuqV5Do0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHH rfAQUySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5dRdY4zTW2 ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQOA7+j0xbm0bqQfWwCHTD 0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwvJoIQ4uNllAoEwF73XVv4EOLQunpL+943 AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B Af8EBAMCAcYwHQYDVR0OBBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IB AQA+0hyJLjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecYMnQ8 SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ44gx+FkagQnIl6Z0 x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6IJd1hJyMctTEHBDa0GpC9oHRxUIlt vBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNwi/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7Nz TogVZ96edhBiIL5VaZVDADlN9u6wWk5JRFRYX0KD -----END CERTIFICATE----- NetLock Arany (Class Gold) Főtanúsítvány ======================================== -----BEGIN CERTIFICATE----- MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQGEwJIVTERMA8G A1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3MDUGA1UECwwuVGFuw7pzw610 dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNlcnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBB cmFueSAoQ2xhc3MgR29sZCkgRsWRdGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgx MjA2MTUwODIxWjCBpzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxO ZXRMb2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlmaWNhdGlv biBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNzIEdvbGQpIEbFkXRhbsO6 c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxCRec75LbRTDofTjl5Bu 0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrTlF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw /HpYzY6b7cNGbIRwXdrzAZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAk H3B5r9s5VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRGILdw fzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2BJtr+UBdADTHLpl1 neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIB BjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2MU9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwW qZw8UQCgwBEIBaeZ5m8BiFRhbvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTta YtOUZcTh5m2C+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2FuLjbvrW5Kfna NwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2XjG4Kvte9nHfRCaexOYNkbQu dZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= -----END CERTIFICATE----- Microsec e-Szigno Root CA 2009 ============================== -----BEGIN CERTIFICATE----- MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYDVQQGEwJIVTER MA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jv c2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o dTAeFw0wOTA2MTYxMTMwMThaFw0yOTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UE BwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUt U3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvPkd6mJviZpWNwrZuuyjNA fW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tccbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG 0IMZfcChEhyVbUr02MelTTMuhTlAdX4UfIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKA pxn1ntxVUwOXewdI/5n7N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm 1HxdrtbCxkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1+rUC AwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTLD8bf QkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAbBgNVHREE FDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqGSIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0o lZMEyL/azXm4Q5DwpL7v8u8hmLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfX I/OMn74dseGkddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c2Pm2G2JwCz02 yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5tHMN1Rq41Bab2XD0h7lbwyYIi LXpUq3DDfSJlgnCW -----END CERTIFICATE----- GlobalSign Root CA - R3 ======================= -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4GA1UECxMXR2xv YmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2Jh bFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxT aWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2ln bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWt iHL8RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsTgHeMCOFJ 0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmmKPZpO/bLyCiR5Z2KYVc3 rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zdQQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjl OCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZXriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2 xmmFghcCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FI/wS3+oLkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZURUm7 lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMpjjM5RcOO5LlXbKr8 EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK6fBdRoyV3XpYKBovHd7NADdBj+1E bddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQXmcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18 YIvDQVETI53O9zJrlAGomecsMx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7r kpeDMdmztcpHWD9f -----END CERTIFICATE----- Izenpe.com ========== -----BEGIN CERTIFICATE----- MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQG EwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wHhcNMDcxMjEz MTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMu QS4xEzARBgNVBAMMCkl6ZW5wZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ 03rKDx6sp4boFmVqscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAK ClaOxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6HLmYRY2xU +zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFXuaOKmMPsOzTFlUFpfnXC PCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQDyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxT OTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbK F7jJeodWLBoBHmy+E60QrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK 0GqfvEyNBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8Lhij+ 0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIBQFqNeb+Lz0vPqhbB leStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+HMh3/1uaD7euBUbl8agW7EekFwID AQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2luZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+ SVpFTlBFIFMuQS4gLSBDSUYgQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBG NjIgUzgxQzBBBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O BBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUAA4ICAQB4pgwWSp9MiDrAyw6l Fn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWblaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbga kEyrkgPH7UIBzg/YsfqikuFgba56awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8q hT/AQKM6WfxZSzwoJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Cs g1lwLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCTVyvehQP5 aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGkLhObNA5me0mrZJfQRsN5 nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJbUjWumDqtujWTI6cfSN01RpiyEGjkpTHC ClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZo Q0iy2+tzJOeRf1SktoA+naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1Z WrOZyGlsQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== -----END CERTIFICATE----- Go Daddy Root Certificate Authority - G2 ======================================== -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoTEUdvRGFkZHkuY29tLCBJbmMu MTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8G A1UEAxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKDE6bFIEMBO4Tx5oVJnyfq 9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD +qK+ihVqf94Lw7YZFAXK6sOoBJQ7RnwyDfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutd fMh8+7ArU6SSYmlRJQVhGkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMl NAJWJwGRtDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEAAaNC MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFDqahQcQZyi27/a9 BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmXWWcDYfF+OwYxdS2hII5PZYe096ac vNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r 5N9ss4UXnT3ZJE95kTXWXwTrgIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYV N8Gb5DKj7Tjo2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI4uJEvlz36hz1 -----END CERTIFICATE----- Starfield Root Certificate Authority - G2 ========================================= -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVsZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0 eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAw DgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQg VGVjaG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZpY2F0ZSBB dXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3twQP89o/8ArFv W59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMgnLRJdzIpVv257IzdIvpy3Cdhl+72WoTs bhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNk N3mSwOxGXn/hbVNMYq/NHwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7Nf ZTD4p7dNdloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0HZbU JtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0GCSqGSIb3DQEBCwUAA4IBAQARWfol TwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjUsHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx 4mcujJUDJi5DnUox9g61DLu34jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUw F5okxBDgBPfg8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1mMpYjn0q7pBZ c2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 -----END CERTIFICATE----- Starfield Services Root Certificate Authority - G2 ================================================== -----BEGIN CERTIFICATE----- MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMxEDAOBgNVBAgT B0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoTHFN0YXJmaWVsZCBUZWNobm9s b2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVsZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRl IEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNV BAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxT dGFyZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2VydmljZXMg Um9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20pOsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2 h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm28xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4Pa hHQUw2eeBGg6345AWh1KTs9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLP LJGmpufehRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk6mFB rMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+qAdcwKziIorhtSpzyEZGDMA0GCSqG SIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMIbw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPP E95Dz+I0swSdHynVv/heyNXBve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTy xQGjhdByPq1zqwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn0q23KXB56jza YyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCNsSi6 -----END CERTIFICATE----- AffirmTrust Commercial ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMB4XDTEw MDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6Eqdb DuKPHx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yrba0F8PrV C8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPALMeIrJmqbTFeurCA+ukV6 BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1yHp52UKqK39c/s4mT6NmgTWvRLpUHhww MmWd5jyTXlBOeuM61G7MGvv50jeuJCqrVwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNV HQ4EFgQUnZPGU4teyq8/nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYGXUPG hi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNjvbz4YYCanrHOQnDi qX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivtZ8SOyUOyXGsViQK8YvxO8rUzqrJv 0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9gN53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0kh sUlHRUe072o0EclNmsxZt9YCnlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= -----END CERTIFICATE----- AffirmTrust Networking ====================== -----BEGIN CERTIFICATE----- MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMB4XDTEw MDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmly bVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SE Hi3yYJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbuakCNrmreI dIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRLQESxG9fhwoXA3hA/Pe24 /PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gb h+0t+nvujArjqWaJGctB+d1ENmHP4ndGyH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNV HQ4EFgQUBx/S55zawm6iQLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfOtDIu UFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzuQY0x2+c06lkh1QF6 12S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZLgo/bNjR9eUJtGxUAArgFU2HdW23 WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4uolu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9 /ZFvgrG+CJPbFEfxojfHRZ48x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= -----END CERTIFICATE----- AffirmTrust Premium =================== -----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UEBhMCVVMxFDAS BgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMB4XDTEwMDEy OTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRy dXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxBLfqV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtn BKAQJG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ+jjeRFcV 5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrSs8PhaJyJ+HoAVt70VZVs +7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmd GPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d770O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5R p9EixAqnOEhss/n/fauGV+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NI S+LI+H+SqHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S5u04 6uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4IaC1nEWTJ3s7xgaVY5 /bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TXOwF0lkLgAOIua+rF7nKsu7/+6qqo +Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYEFJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB /wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByv MiPIs0laUZx2KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B8OWycvpEgjNC 6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQMKSOyARiqcTtNd56l+0OOF6S L5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK +4w1IX2COPKpVJEZNZOUbWo6xbLQu4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmV BtWVyuEklut89pMFu+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFg IxpHYoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8GKa1qF60 g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaORtGdFNrHF+QFlozEJLUb zxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6eKeC2uAloGRwYQw== -----END CERTIFICATE----- AffirmTrust Premium ECC ======================= -----BEGIN CERTIFICATE----- MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMCVVMxFDASBgNV BAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQcmVtaXVtIEVDQzAeFw0xMDAx MjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJBgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1U cnVzdDEgMB4GA1UEAwwXQWZmaXJtVHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAAQNMF4bFZ0D0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQ N8O9ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0GA1UdDgQW BBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAK BggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/VsaobgxCd05DhT1wV/GzTjxi+zygk8N53X 57hG8f2h4nECMEJZh0PUUd+60wkyWs6Iflc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKM eQ== -----END CERTIFICATE----- Certum Trusted Network CA ========================= -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBMMSIwIAYDVQQK ExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlv biBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBUcnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIy MTIwNzM3WhcNMjkxMjMxMTIwNzM3WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBU ZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 MSIwIAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC AQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rHUV+rpDKmYYe2bg+G0jAC l/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LMTXPb865Px1bVWqeWifrzq2jUI4ZZJ88J J7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVUBBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4 fOQtf/WsX+sWn7Et0brMkUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0 cvW0QM8xAcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNVHRMB Af8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNVHQ8BAf8EBAMCAQYw DQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15ysHhE49wcrwn9I0j6vSrEuVUEtRCj jSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfLI9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1 mS1FhIrlQgnXdAIv94nYmem8J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5aj Zt3hrvJBW8qYVoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- TWCA Root Certification Authority ================================= -----BEGIN CERTIFICATE----- MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJ VEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMzWhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQG EwJUVzESMBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NB IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFEAcK0HMMx QhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HHK3XLfJ+utdGdIzdjp9xC oi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeXRfwZVzsrb+RH9JlF/h3x+JejiB03HFyP 4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/zrX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1r y+UPizgN7gr8/g+YnzAx3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIB BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkqhkiG 9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeCMErJk/9q56YAf4lC mtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdlsXebQ79NqZp4VKIV66IIArB6nCWlW QtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62Dlhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVY T0bf+215WfKEIlKuD8z7fDvnaspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocny Yh0igzyXxfkZYiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== -----END CERTIFICATE----- Security Communication RootCA2 ============================== -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDElMCMGA1UEChMc U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMeU2VjdXJpdHkgQ29tbXVuaWNh dGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoXDTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMC SlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3Vy aXR5IENvbW11bmljYXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB ANAVOVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGrzbl+dp++ +T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVMVAX3NuRFg3sUZdbcDE3R 3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQhNBqyjoGADdH5H5XTz+L62e4iKrFvlNV spHEfbmwhRkGeC7bYRr6hfVKkaHnFtWOojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1K EOtOghY6rCcMU/Gt1SSwawNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8 QIH4D5csOPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEB CwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpFcoJxDjrSzG+ntKEj u/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXcokgfGT+Ok+vx+hfuzU7jBBJV1uXk 3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6q tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- Actalis Authentication Root CA ============================== -----BEGIN CERTIFICATE----- MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UEBhMCSVQxDjAM BgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UE AwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDky MjExMjIwMlowazELMAkGA1UEBhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlz IFMucC5BLi8wMzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNvUTufClrJ wkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX4ay8IMKx4INRimlNAJZa by/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9KK3giq0itFZljoZUj5NDKd45RnijMCO6 zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1f YVEiVRvjRuPjPdA1YprbrxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2 oxgkg4YQ51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2Fbe8l EfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxeKF+w6D9Fz8+vm2/7 hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4Fv6MGn8i1zeQf1xcGDXqVdFUNaBr8 EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbnfpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5 jF66CyCU3nuDuP/jVo23Eek7jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLY iDrIn3hm7YnzezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQALe3KHwGCmSUyI WOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70jsNjLiNmsGe+b7bAEzlgqqI0 JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDzWochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKx K3JCaKygvU5a2hi/a5iB0P2avl4VSM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+ Xlff1ANATIGk0k9jpwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC 4yyXX04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+OkfcvHlXHo 2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7RK4X9p2jIugErsWx0Hbhz lefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btUZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXem OR/qnuOf0GZvBeyqdn6/axag67XH/JJULysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9 vwGYT7JZVEc+NHt4bVaTLnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== -----END CERTIFICATE----- Buypass Class 2 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMiBSb290IENBMB4X DTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1owTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1 g1Lr6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPVL4O2fuPn 9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC911K2GScuVr1QGbNgGE41b /+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHxMlAQTn/0hpPshNOOvEu/XAFOBz3cFIqU CqTqc/sLUegTBxj6DvEr0VQVfTzh97QZQmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeff awrbD02TTqigzXsu8lkBarcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgI zRFo1clrUs3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLiFRhn Bkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRSP/TizPJhk9H9Z2vX Uq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN9SG9dKpN6nIDSdvHXx1iY8f93ZHs M+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxPAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFMmAd+BikoL1RpzzuvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAU18h9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3tOluwlN5E40EI osHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo+fsicdl9sz1Gv7SEr5AcD48S aq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYd DnkM/crqJIByw5c/8nerQyIKx+u2DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWD LfJ6v9r9jv6ly0UsH8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0 oyLQI+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK75t98biGC wWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h3PFaTWwyI0PurKju7koS CTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPzY11aWOIv4x3kqdbQCtCev9eBCfHJxyYN rJgWVqA= -----END CERTIFICATE----- Buypass Class 3 Root CA ======================= -----BEGIN CERTIFICATE----- MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU QnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3MgQ2xhc3MgMyBSb290IENBMB4X DTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFowTjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1 eXBhc3MgQVMtOTgzMTYzMzI3MSAwHgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRH sJ8YZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3EN3coTRiR 5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9tznDDgFHmV0ST9tD+leh 7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX0DJq1l1sDPGzbjniazEuOQAnFN44wOwZ ZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH 2xc519woe2v1n/MuwU8XKhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV /afmiSTYzIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvSO1UQ RwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D34xFMFbG02SrZvPA Xpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgPK9Dx2hzLabjKSWJtyNBjYt1gD1iq j6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFEe4zf/lb+74suwvTg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsF AAOCAgEAACAjQTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXSIGrs/CIBKM+G uIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2HJLw5QY33KbmkJs4j1xrG0aG Q0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsaO5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8 ZORK15FTAaggiG6cX0S5y2CBNOxv033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2 KSb12tjE8nVhz36udmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz 6MkEkbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg413OEMXbug UZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvDu79leNKGef9JOxqDDPDe eOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq4/g7u9xN12TyUb7mqqta6THuBrxzvxNi Cp/HuZc= -----END CERTIFICATE----- T-TeleSec GlobalRoot Class 3 ============================ -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgx MDAxMTAyOTU2WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN8ELg63iIVl6bmlQdTQyK 9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/RLyTPWGrTs0NvvAgJ1gORH8EGoel15YU NpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZF iP0Zf3WHHx+xGwpzJFu5ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W 0eDrXltMEnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1A/d2O2GCahKqGFPr AyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOyWL6ukK2YJ5f+AbGwUgC4TeQbIXQb fsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzT ucpH9sry9uetuUg/vBa3wW306gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7h P0HHRwA11fXT91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4pTpPDpFQUWw== -----END CERTIFICATE----- D-TRUST Root Class 3 CA 2 2009 ============================== -----BEGIN CERTIFICATE----- MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTAe Fw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NThaME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxE LVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOAD ER03UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42tSHKXzlA BF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9RySPocq60vFYJfxLLHLGv KZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsMlFqVlNpQmvH/pStmMaTJOKDfHR+4CS7z p+hnUquVH+BGPtikw8paxTGA6Eian5Rp/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUC AwEAAaOCARowggEWMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ 4PGEMA4GA1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVjdG9y eS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUyMENBJTIwMiUyMDIw MDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRlcmV2b2NhdGlvbmxpc3QwQ6BBoD+G PWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAw OS5jcmwwDQYJKoZIhvcNAQELBQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm 2H6NMLVwMeniacfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4KzCUqNQT4YJEV dT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8PIWmawomDeCTmGCufsYkl4ph X5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3YJohw1+qRzT65ysCQblrGXnRl11z+o+I= -----END CERTIFICATE----- D-TRUST Root Class 3 CA 2 EV 2009 ================================= -----BEGIN CERTIFICATE----- MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw OTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUwNDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQK DAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAw OTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfS egpnljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM03TP1YtHh zRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6ZqQTMFexgaDbtCHu39b+T 7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lRp75mpoo6Kr3HGrHhFPC+Oh25z1uxav60 sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure35 11H3a6UCAwEAAaOCASQwggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyv cop9NteaHNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFwOi8v ZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xhc3MlMjAzJTIwQ0El MjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRp b25saXN0MEagRKBChkBodHRwOi8vd3d3LmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xh c3NfM19jYV8yX2V2XzIwMDkuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+ PPoeUSbrh/Yp3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNFCSuGdXzfX2lX ANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7naxpeG0ILD5EJt/rDiZE4OJudA NCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqXKVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVv w9y4AyHqnxbxLFS1 -----END CERTIFICATE----- CA Disig Root R2 ================ -----BEGIN CERTIFICATE----- MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNVBAYTAlNLMRMw EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp ZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQyMDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sx EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp c2lnIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbC w3OeNcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNHPWSb6Wia xswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3Ix2ymrdMxp7zo5eFm1tL7 A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbeQTg06ov80egEFGEtQX6sx3dOy1FU+16S GBsEWmjGycT6txOgmLcRK7fWV8x8nhfRyyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqV g8NTEQxzHQuyRpDRQjrOQG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa 5Beny912H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJQfYE koopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUDi/ZnWejBBhG93c+A Ak9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORsnLMOPReisjQS1n6yqEm70XooQL6i Fh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNV HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5u Qu0wDQYJKoZIhvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqfGopTpti72TVV sRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkblvdhuDvEK7Z4bLQjb/D907Je dR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W8 1k/BfDxujRNt+3vrMNDcTa/F1balTFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjx mHHEt38OFdAlab0inSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01 utI3gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18DrG5gPcFw0 sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3OszMOl6W8KjptlwlCFtaOg UxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8xL4ysEr3vQCj8KWefshNPZiTEUxnpHikV 7+ZtsH8tZ/3zbBt1RqPlShfppNcL -----END CERTIFICATE----- ACCVRAIZ1 ========= -----BEGIN CERTIFICATE----- MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UEAwwJQUNDVlJB SVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQswCQYDVQQGEwJFUzAeFw0xMTA1 MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQBgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwH UEtJQUNDVjENMAsGA1UECgwEQUNDVjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQCbqau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gM jmoYHtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWoG2ioPej0 RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpAlHPrzg5XPAOBOp0KoVdD aaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhrIA8wKFSVf+DuzgpmndFALW4ir50awQUZ 0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDG WuzndN9wrqODJerWx5eHk6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs7 8yM2x/474KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMOm3WR 5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpacXpkatcnYGMN285J 9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPluUsXQA+xtrn13k/c4LOsOxFwYIRK Q26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYIKwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRw Oi8vd3d3LmFjY3YuZXMvZmlsZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEu Y3J0MB8GCCsGAQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeTVfZW6oHlNsyM Hj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIGCCsGAQUFBwICMIIBFB6CARAA QQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUAcgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBh AO0AegAgAGQAZQAgAGwAYQAgAEEAQwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUA YwBuAG8AbABvAGcA7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBj AHQAcgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAAQwBQAFMA IABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUAczAwBggrBgEFBQcCARYk aHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2MuaHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0 dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRtaW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2 MV9kZXIuY3JsMA4GA1UdDwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZI hvcNAQEFBQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdpD70E R9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gUJyCpZET/LtZ1qmxN YEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+mAM/EKXMRNt6GGT6d7hmKG9Ww7Y49 nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepDvV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJ TS+xJlsndQAJxGJ3KQhfnlmstn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3 sCPdK6jT2iWH7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szAh1xA2syVP1Xg Nce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xFd3+YJ5oyXSrjhO7FmGYvliAd 3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2HpPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3p EfbRD0tVNEYqi4Y7 -----END CERTIFICATE----- TWCA Global Root CA =================== -----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcxEjAQBgNVBAoT CVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMTVFdDQSBHbG9iYWwgUm9vdCBD QTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQK EwlUQUlXQU4tQ0ExEDAOBgNVBAsTB1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3Qg Q0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2C nJfF10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz0ALfUPZV r2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfChMBwqoJimFb3u/Rk28OKR Q4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbHzIh1HrtsBv+baz4X7GGqcXzGHaL3SekV tTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1W KKD+u4ZqyPpcC1jcxkt2yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99 sy2sbZCilaLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYPoA/p yJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQABDzfuBSO6N+pjWxn kjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcEqYSjMq+u7msXi7Kx/mzhkIyIqJdI zshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6g cFGn90xHNcgL1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WFH6vPNOw/KP4M 8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNoRI2T9GRwoD2dKAXDOXC4Ynsg /eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlg lPx4mI88k1HtQJAH32RjJMtOcQWh15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryP A9gK8kxkRr05YuWW6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3m i4TWnsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5jwa19hAM8 EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWzaGHQRiapIVJpLesux+t3 zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmyKwbQBM0= -----END CERTIFICATE----- TeliaSonera Root CA v1 ====================== -----BEGIN CERTIFICATE----- MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAwNzEUMBIGA1UE CgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJvb3QgQ0EgdjEwHhcNMDcxMDE4 MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYDVQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwW VGVsaWFTb25lcmEgUm9vdCBDQSB2MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+ 6yfwIaPzaSZVfp3FVRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA 3GV17CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+XZ75Ljo1k B1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+/jXh7VB7qTCNGdMJjmhn Xb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxH oLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkmdtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3 F0fUTPHSiXk+TT2YqGHeOh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJ oWjiUIMusDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4pgd7 gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fsslESl1MpWtTwEhDc TwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQarMCpgKIv7NHfirZ1fpoeDVNAgMB AAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qW DNXr+nuqF+gTEjANBgkqhkiG9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNm zqjMDfz1mgbldxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx 0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1TjTQpgcmLNkQfW pb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBedY2gea+zDTYa4EzAvXUYNR0PV G6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpc c41teyWRyu5FrgZLAMzTsVlQ2jqIOylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOT JsjrDNYmiLbAJM+7vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2 qReWt88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcnHL/EVlP6 Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVxSK236thZiNSQvxaz2ems WWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= -----END CERTIFICATE----- T-TeleSec GlobalRoot Class 2 ============================ -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoM IlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBU cnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgx MDAxMTA0MDE0WhcNMzMxMDAxMjM1OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lz dGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBD ZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUdAqSzm1nzHoqvNK38DcLZ SBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiCFoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/F vudocP05l03Sx5iRUKrERLMjfTlH6VJi1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx970 2cu+fjOlbpSD8DT6IavqjnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGV WOHAD3bZwI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/WSA2AHmgoCJrjNXy YdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhyNsZt+U2e+iKo4YFWz827n+qrkRk4 r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPACuvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNf vNoBYimipidx5joifsFvHZVwIEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR 3p1m0IvVVGb6g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN 9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlPBSeOE6Fuwg== -----END CERTIFICATE----- Atos TrustedRoot 2011 ===================== -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UEAwwVQXRvcyBU cnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQGEwJERTAeFw0xMTA3MDcxNDU4 MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMMFUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsG A1UECgwEQXRvczELMAkGA1UEBhMCREUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV hTuXbyo7LjvPpvMpNb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr 54rMVD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+SZFhyBH+ DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ4J7sVaE3IqKHBAUsR320 HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0Lcp2AMBYHlT8oDv3FdU9T1nSatCQujgKR z3bFmx5VdJx4IbHwLfELn8LVlhgf8FQieowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7R l+lwrrw7GWzbITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZ bNshMBgGA1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB CwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8jvZfza1zv7v1Apt+h k6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kPDpFrdRbhIfzYJsdHt6bPWHJxfrrh TZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pcmaHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a9 61qn8FYiqTxlVMYVqL2Gns2Dlmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G 3mB/ufNPRJLvKrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed -----END CERTIFICATE----- QuoVadis Root CA 1 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakE PBtVwedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWerNrwU8lm PNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF34168Xfuw6cwI2H44g4hWf6 Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh4Pw5qlPafX7PGglTvF0FBM+hSo+LdoIN ofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXpUhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/l g6AnhF4EwfWQvTA9xO+oabw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV 7qJZjqlc3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/GKubX 9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSthfbZxbGL0eUQMk1f iyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KOTk0k+17kBL5yG6YnLUlamXrXXAkg t3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOtzCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZI hvcNAQELBQADggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2cDMT/uFPpiN3 GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUNqXsCHKnQO18LwIE6PWThv6ct Tr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP +V04ikkwj+3x6xn0dxoxGE1nVGwvb2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh 3jRJjehZrJ3ydlo28hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fa wx/kNSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNjZgKAvQU6 O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhpq1467HxpvMc7hU6eFbm0 FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFtnh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOV hMJKzRwuJIczYOXD -----END CERTIFICATE----- QuoVadis Root CA 2 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFh ZiFfqq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMWn4rjyduY NM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ymc5GQYaYDFCDy54ejiK2t oIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+O7q414AB+6XrW7PFXmAqMaCvN+ggOp+o MiwMzAkd056OXbxMmO7FGmh77FOm6RQ1o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+l V0POKa2Mq1W/xPtbAd0jIaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZo L1NesNKqIcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz8eQQ sSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43ehvNURG3YBZwjgQQvD 6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l7ZizlWNof/k19N+IxWA1ksB8aRxh lRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALGcC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZI hvcNAQELBQADggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RCroijQ1h5fq7K pVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0GaW/ZZGYjeVYg3UQt4XAoeo0L9 x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4nlv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgz dWqTHBLmYF5vHX/JHyPLhGGfHoJE+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6X U/IyAgkwo1jwDQHVcsaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+Nw mNtddbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNgKCLjsZWD zYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeMHVOyToV7BjjHLPj4sHKN JeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4WSr2Rz0ZiC3oheGe7IUIarFsNMkd7Egr O3jtZsSOeWmD3n+M -----END CERTIFICATE----- QuoVadis Root CA 3 G3 ===================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQELBQAwSDELMAkG A1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAcBgNVBAMTFVF1b1ZhZGlzIFJv b3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJN MRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMg RzMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286 IxSR/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNuFoM7pmRL Mon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXRU7Ox7sWTaYI+FrUoRqHe 6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+cra1AdHkrAj80//ogaX3T7mH1urPnMNA3 I4ZyYUUpSFlob3emLoG+B01vr87ERRORFHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3U VDmrJqMz6nWB2i3ND0/kA9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f7 5li59wzweyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634RylsSqi Md5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBpVzgeAVuNVejH38DM dyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0QA4XN8f+MFrXBsj6IbGB/kE+V9/Yt rQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZI hvcNAQELBQADggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnIFUBhynLWcKzS t/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5WvvoxXqA/4Ti2Tk08HS6IT7SdEQ TXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFgu/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9Du DcpmvJRPpq3t/O5jrFc/ZSXPsoaP0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGib Ih6BJpsQBJFxwAYf3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmD hPbl8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+DhcI00iX 0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HNPlopNLk9hM6xZdRZkZFW dSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ywaZWWDYWGWVjUTR939+J399roD1B0y2 PpxxVJkES/1Y+Zj0 -----END CERTIFICATE----- DigiCert Assured ID Root G2 =========================== -----BEGIN CERTIFICATE----- MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQw IgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgw MTE1MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQL ExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSAn61UQbVH 35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4HteccbiJVMWWXvdMX0h5i89vq bFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9HpEgjAALAcKxHad3A2m67OeYfcgnDmCXRw VWmvo2ifv922ebPynXApVfSr/5Vh88lAbx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OP YLfykqGxvYmJHzDNw6YuYjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+Rn lTGNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTO w0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPIQW5pJ6d1Ee88hjZv 0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I0jJmwYrA8y8678Dj1JGG0VDjA9tz d29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4GnilmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAW hsI6yLETcDbYz+70CjTVW0z9B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0M jomZmWzwPDCvON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo IhNzbM8m9Yop5w== -----END CERTIFICATE----- DigiCert Assured ID Root G3 =========================== -----BEGIN CERTIFICATE----- MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 MTIwMDAwWjBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQ BgcqhkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJfZn4f5dwb RXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17QRSAPWXYQ1qAk8C3eNvJs KTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgF UaFNN6KDec6NHSrkhDAKBggqhkjOPQQDAwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5Fy YZ5eEJJZVrmDxxDnOOlYJjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy 1vUhZscv6pZjamVFkpUBtA== -----END CERTIFICATE----- DigiCert Global Root G2 ======================= -----BEGIN CERTIFICATE----- MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBhMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAw HgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUx MjAwMDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 dy5kaWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkq hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI2/Ou8jqJ kTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx1x7e/dfgy5SDN67sH0NO 3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQq2EGnI/yuum06ZIya7XzV+hdG82MHauV BJVJ8zUtluNJbd134/tJS7SsVQepj5WztCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyM UNGPHgm+F6HmIcr9g+UQvIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQAB o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV5uNu 5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY1Yl9PMWLSn/pvtsr F9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4NeF22d+mQrvHRAiGfzZ0JFrabA0U WTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NGFdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBH QRFXGU7Aj64GxJUTFy8bJZ918rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/ iyK5S9kJRaTepLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl MrY= -----END CERTIFICATE----- DigiCert Global Root G3 ======================= -----BEGIN CERTIFICATE----- MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQswCQYDVQQGEwJV UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYD VQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAw MDBaMGExCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5k aWdpY2VydC5jb20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0C AQYFK4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FGfp4tn+6O YwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPOZ9wj/wMco+I+o0IwQDAP BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNp Yim8S8YwCgYIKoZIzj0EAwMDaAAwZQIxAK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y 3maTD/HMsQmP3Wyr+mt/oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34 VOKa5Vt8sycX -----END CERTIFICATE----- DigiCert Trusted Root G4 ======================== -----BEGIN CERTIFICATE----- MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEw HwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1 MTIwMDAwWjBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3yithZwuEp pz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9o k3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7Fsa vOvJz82sNEBfsXpm7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGY QJB5w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6 MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCiEhtm mnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7 f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFH dL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8 oR7FwI+isX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud DwEB/wQEAwIBhjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2SV1EY+CtnJYY ZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd+SeuMIW59mdNOj6PWTkiU0Tr yF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWcfFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy 7zBZLq7gcfJW5GqXb5JQbZaNaHqasjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iah ixTXTBmyUEFxPT9NcCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN 5r5N0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie4u1Ki7wb /UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mIr/OSmbaz5mEP0oUA51Aa 5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tK G48BtieVU+i2iW1bvGjUI+iLUaJW+fCmgKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP 82Z+ -----END CERTIFICATE----- COMODO RSA Certification Authority ================================== -----BEGIN CERTIFICATE----- MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCBhTELMAkGA1UE BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwHhcNMTAwMTE5MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMC R0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE ChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR6FSS0gpWsawNJN3Fz0Rn dJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8Xpz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZ FGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+ 5eNu/Nio5JIk2kNrYrhV/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pG x8cgoLEfZd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z+pUX 2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7wqP/0uK3pN/u6uPQL OvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZahSL0896+1DSJMwBGB7FY79tOi4lu3 sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVICu9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+C GCe01a60y1Dma/RMhnEw6abfFobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5 WdYgGq/yapiqcrxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvlwFTPoCWOAvn9sKIN9SCYPBMt rFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+ nq6PK7o9mfjYcwlYRm6mnPTXJ9OV2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSg tZx8jb8uk2IntznaFxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwW sRqZCuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiKboHGhfKp pC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmckejkk9u+UJueBPSZI9FoJA zMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yLS0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHq ZJx64SIDqZxubw5lT2yHh17zbqD5daWbQOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk52 7RH89elWsn2/x20Kk4yl0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7I LaZRfyHBNVOFBkpdn627G190 -----END CERTIFICATE----- USERTrust RSA Certification Authority ===================================== -----BEGIN CERTIFICATE----- MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UE BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UE BhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQK ExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCAEmUXNg7D2wiz 0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2j Y0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFn RghRy4YUVD+8M/5+bJz/Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O +T23LLb2VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq /nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6lZrEpfDKE Y1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8liM2EuLE/pDkP2QKe6xJM lXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8 yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+ eLf8ZxXhyVeEHg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF MAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPFUp/L+M+ZBn8b2kMVn54CVVeW FPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KOVWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ 7l8wXEskEVX/JJpuXior7gtNn3/3ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQ Eg9zKC7F4iRO/Fjs8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM 8WcRiQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYzeSf7dNXGi FSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZXHlKYC6SQK5MNyosycdi yA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9c J2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRBVXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGw sAvgnEzDHNb842m1R0aBL6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gx Q+6IHdfGjjxDah2nGN59PRbxYvnKkKj9 -----END CERTIFICATE----- USERTrust ECC Certification Authority ===================================== -----BEGIN CERTIFICATE----- MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDELMAkGA1UEBhMC VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwHhcNMTAwMjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMC VVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqfloI+d61SRvU8Za2EurxtW2 0eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinngo4N+LZfQYcTxmdwlkWOrfzCjtHDix6Ez nPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0GA1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNV HQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBB HU6+4WMBzzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbWRNZu 9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= -----END CERTIFICATE----- GlobalSign ECC Root CA - R5 =========================== -----BEGIN CERTIFICATE----- MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEkMCIGA1UECxMb R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD EwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoXDTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMb R2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQD EwpHbG9iYWxTaWduMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6 SFkc8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8kehOvRnkmS h5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd BgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYIKoZIzj0EAwMDaAAwZQIxAOVpEslu28Yx uglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7 yFz9SO8NdCKoCOJuxUnOxwy8p2Fp8fc74SrL+SvzZpA3 -----END CERTIFICATE----- IdenTrust Commercial Root CA 1 ============================== -----BEGIN CERTIFICATE----- MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBKMQswCQYDVQQG EwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBS b290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQwMTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzES MBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENB IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ld hNlT3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU+ehcCuz/ mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gpS0l4PJNgiCL8mdo2yMKi 1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1bVoE/c40yiTcdCMbXTMTEl3EASX2MN0C XZ/g1Ue9tOsbobtJSdifWwLziuQkkORiT0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl 3ZBWzvurpWCdxJ35UrCLvYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzy NeVJSQjKVsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZKdHzV WYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHTc+XvvqDtMwt0viAg xGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hvl7yTmvmcEpB4eoCHFddydJxVdHix uuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5NiGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZI hvcNAQELBQADggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH 6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwtLRvM7Kqas6pg ghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93nAbowacYXVKV7cndJZ5t+qnt ozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmV YjzlVYA211QC//G5Xc7UI2/YRYRKW2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUX feu+h1sXIFRRk0pTAwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/ro kTLql1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG4iZZRHUe 2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZmUlO+KWA2yUPHGNiiskz Z2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7R cGzM7vRX+Bi6hG6H -----END CERTIFICATE----- IdenTrust Public Sector Root CA 1 ================================= -----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG EwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3Rv ciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcNMzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJV UzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBS b290IENBIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTy P4o7ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGyRBb06tD6 Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlSbdsHyo+1W/CD80/HLaXI rcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF/YTLNiCBWS2ab21ISGHKTN9T0a9SvESf qy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoS mJxZZoY+rfGwyj4GD3vwEUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFn ol57plzy9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9VGxyh LrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ2fjXctscvG29ZV/v iDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsVWaFHVCkugyhfHMKiq3IXAAaOReyL 4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gDW/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8B Af8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMw DQYJKoZIhvcNAQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHVDRDtfULAj+7A mgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9TaDKQGXSc3z1i9kKlT/YPyNt GtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8GlwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFt m6/n6J91eEyrRjuazr8FGF1NFTwWmhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMx NRF4eKLg6TCMf4DfWN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4 Mhn5+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJtshquDDI ajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhAGaQdp/lLQzfcaFpPz+vC ZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ 3Wl9af0AVqW3rLatt8o+Ae+c -----END CERTIFICATE----- Entrust Root Certification Authority - G2 ========================================= -----BEGIN CERTIFICATE----- MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMCVVMxFjAUBgNV BAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVy bXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ug b25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIw HhcNMDkwNzA3MTcyNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoT DUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMx OTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25s eTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP /vaCeb9zYQYKpSfYs1/TRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXz HHfV1IWNcCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hWwcKU s/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1U1+cPvQXLOZprE4y TGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0jaWvYkxN4FisZDQSA/i2jZRjJKRx AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ6 0B7vfec7aVHUbI2fkBJmqzANBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5Z iXMRrEPR9RP/jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v1fN2D807iDgi nWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4RnAuknZoh8/CbCzB428Hch0P+ vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmHVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xO e4pIb4tF9g== -----END CERTIFICATE----- Entrust Root Certification Authority - EC1 ========================================== -----BEGIN CERTIFICATE----- MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkGA1UEBhMCVVMx FjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVn YWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXpl ZCB1c2Ugb25seTEzMDEGA1UEAxMqRW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 IC0gRUMxMB4XDTEyMTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYw FAYDVQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0L2xlZ2Fs LXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0gZm9yIGF1dGhvcml6ZWQg dXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt IEVDMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHy AsWfoPZb1YsGGYZPUxBtByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef 9eNi1KlHBz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE FLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVCR98crlOZF7ZvHH3h vxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nXhTcGtXsI/esni0qU+eH6p44mCOh8 kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G -----END CERTIFICATE----- CFCA EV ROOT ============ -----BEGIN CERTIFICATE----- MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJDTjEwMC4GA1UE CgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNB IEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkxMjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEw MC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQD DAxDRkNBIEVWIFJPT1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnV BU03sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpLTIpTUnrD 7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5/ZOkVIBMUtRSqy5J35DN uF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp7hZZLDRJGqgG16iI0gNyejLi6mhNbiyW ZXvKWfry4t3uMCz7zEasxGPrb382KzRzEpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7 xzbh72fROdOXW3NiGUgthxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9f py25IGvPa931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqotaK8K gWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNgTnYGmE69g60dWIol hdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfVPKPtl8MeNPo4+QgO48BdK4PRVmrJ tqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hvcWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAf BgNVHSMEGDAWgBTj/i39KNALtbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB /wQEAwIBBjAdBgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObTej/tUxPQ4i9q ecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdLjOztUmCypAbqTuv0axn96/Ua 4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBSESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sG E5uPhnEFtC+NiWYzKXZUmhH4J/qyP5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfX BDrDMlI1Dlb4pd19xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjn aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su -----END CERTIFICATE----- OISTE WISeKey Global Root GB CA =============================== -----BEGIN CERTIFICATE----- MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQG EwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAw MzJaFw0zOTEyMDExNTEwMzFaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYD VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEds b2JhbCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3HEokKtaX scriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGxWuR51jIjK+FTzJlFXHtP rby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk 9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNku7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4o Qnc/nSMbsrY9gBQHTC5P99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvg GUpuuy9rM2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZI hvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrghcViXfa43FK8+5/ea4n32cZiZBKpD dHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0 VQreUGdNZtGn//3ZwLWoo4rOZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEui HZeeevJuQHHfaPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= -----END CERTIFICATE----- SZAFIR ROOT CA2 =============== -----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6ZW5pb3dhIFMuQS4xGDAWBgNV BAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkwNzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJ BgNVBAYTAlBMMSgwJgYDVQQKDB9LcmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYD VQQDDA9TWkFGSVIgUk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5Q qEvNQLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT3PSQ1hNK DJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw3gAeqDRHu5rr/gsUvTaE 2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr63fE9biCloBK0TXC5ztdyO4mTp4CEHCdJ ckm1/zuVnsHMyAHs6A6KCpbns6aH5db5BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwi ieDhZNRnvDF5YTy7ykHNXGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsFAAOC AQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw8PRBEew/R40/cof5 O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOGnXkZ7/e7DDWQw4rtTw/1zBLZpD67 oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCPoky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul 4+vJhaAlIDf7js4MNIThPIGyd05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6 +/NNIxuZMzSgLvWpCz/UXeHPhJ/iGcJfitYgHuNztw== -----END CERTIFICATE----- Certum Trusted Network CA 2 =========================== -----BEGIN CERTIFICATE----- MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCBgDELMAkGA1UE BhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1 bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29y ayBDQSAyMCIYDzIwMTExMDA2MDgzOTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQ TDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENl cnRpZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENB IDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWADGSdhhuWZGc/IjoedQF9 7/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+o CgCXhVqqndwpyeI1B+twTUrWwbNWuKFBOJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40b Rr5HMNUuctHFY9rnY3lEfktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2p uTRZCr+ESv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1mo130 GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02isx7QBlrd9pPPV3WZ 9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOWOZV7bIBaTxNyxtd9KXpEulKkKtVB Rgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgezTv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pye hizKV/Ma5ciSixqClnrDvFASadgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vM BhBgu4M1t15n3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZI hvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQF/xlhMcQSZDe28cmk4gmb3DW Al45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTfCVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuA L55MYIR4PSFk1vtBHxgP58l1cb29XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMo clm2q8KMZiYcdywmdjWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tM pkT/WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jbAoJnwTnb w3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksqP/ujmv5zMnHCnsZy4Ypo J/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Kob7a6bINDd82Kkhehnlt4Fj1F4jNy3eFm ypnTycUm/Q1oBEauttmbjL4ZvrHG8hnjXALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLX is7VmFxWlgPF7ncGNf/P5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7 zAYspsbiDrW5viSP -----END CERTIFICATE----- Hellenic Academic and Research Institutions RootCA 2015 ======================================================= -----BEGIN CERTIFICATE----- MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcT BkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0 aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNl YXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAx MTIxWjCBpjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMg QWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNV BAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9vdENBIDIw MTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDC+Kk/G4n8PDwEXT2QNrCROnk8Zlrv bTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+eh iGsxr/CL0BgzuNtFajT0AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+ 6PAQZe104S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06CojXd FPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV9Cz82XBST3i4vTwr i5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrDgfgXy5I2XdGj2HUb4Ysn6npIQf1F GQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2 fu/Z8VFRfS0myGlZYeCsargqNhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9mu iNX6hME6wGkoLfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNVHRMBAf8EBTAD AQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVdctA4GGqd83EkVAswDQYJKoZI hvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0IXtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+ D1hYc2Ryx+hFjtyp8iY/xnmMsVMIM4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrM d/K4kPFox/la/vot9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+y d+2VZ5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/eaj8GsGsVn 82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnhX9izjFk0WaSrT2y7Hxjb davYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQl033DlZdwJVqwjbDG2jJ9SrcR5q+ss7F Jej6A7na+RZukYT1HCjI/CbM1xyQVqdfbzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVt J94Cj8rDtSvK6evIIVM4pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGa JI7ZjnHKe7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0vm9q p/UsQu0yrbYhnr68 -----END CERTIFICATE----- Hellenic Academic and Research Institutions ECC RootCA 2015 =========================================================== -----BEGIN CERTIFICATE----- MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzANBgNVBAcTBkF0 aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u cyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJj aCBJbnN0aXR1dGlvbnMgRUNDIFJvb3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEw MzcxMlowgaoxCzAJBgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmlj IEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUQwQgYD VQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIEVDQyBSb290 Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKgQehLgoRc4vgxEZmGZE4JJS+dQS8KrjVP dJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJajq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoK Vlp8aQuqgAkkbH7BRqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0O BBYEFLQiC4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaeplSTA GiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7SofTUwJCA3sS61kFyjn dc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR -----END CERTIFICATE----- ISRG Root X1 ============ -----BEGIN CERTIFICATE----- MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAwTzELMAkGA1UE BhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2VhcmNoIEdyb3VwMRUwEwYDVQQD EwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQG EwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMT DElTUkcgUm9vdCBYMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54r Vygch77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+0TM8ukj1 3Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6UA5/TR5d8mUgjU+g4rk8K b4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sWT8KOEUt+zwvo/7V3LvSye0rgTBIlDHCN Aymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyHB5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ 4Q7e2RCOFvu396j3x+UCB5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf 1b0SHzUvKBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWnOlFu hjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTnjh8BCNAw1FtxNrQH usEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbwqHyGO0aoSCqI3Haadr8faqU9GY/r OPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CIrU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4G A1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY 9umbbjANBgkqhkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ3BebYhtF8GaV 0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KKNFtY2PwByVS5uCbMiogziUwt hDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJw TdwJx4nLCgdNbOhdjsnvzqvHu7UrTkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nx e5AW0wdeRlN8NwdCjNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZA JzVcoyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq4RgqsahD YVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPAmRGunUHBcnWEvgJBQl9n JEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57demyPxgcYxn/eR44/KJ4EBs+lVDR3veyJ m+kXQ99b21/+jh5Xos1AnX5iItreGCc= -----END CERTIFICATE----- AC RAIZ FNMT-RCM ================ -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou 08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ 47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW +YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7 Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d 8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm 5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM= -----END CERTIFICATE----- Amazon Root CA 1 ================ -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsFADA5MQswCQYD VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAxMB4XDTE1 MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALJ4gHHKeNXjca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgH FzZM9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qwIFAGbHrQ gLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6VOujw5H5SNz/0egwLX0t dHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L93FcXmn/6pUCyziKrlA4b9v7LWIbxcce VOF34GfID5yHI9Y/QCB/IIDEgEw+OyQmjgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB /zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3 DQEBCwUAA4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDIU5PM CCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUsN+gDS63pYaACbvXy 8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vvo/ufQJVtMVT8QtPHRh8jrdkPSHCa 2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2 xJNDd2ZhwLnoQdeXeGADbkpyrqXRfboQnoZsG4q5WTP468SQvvG5 -----END CERTIFICATE----- Amazon Root CA 2 ================ -----BEGIN CERTIFICATE----- MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwFADA5MQswCQYD VQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAyMB4XDTE1 MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpv bjEZMBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC ggIBAK2Wny2cSkxKgXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4 kHbZW0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg1dKmSYXp N+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K8nu+NQWpEjTj82R0Yiw9 AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvd fLC6HM783k81ds8P+HgfajZRRidhW+mez/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAEx kv8LV/SasrlX6avvDXbR8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSS btqDT6ZjmUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz7Mt0 Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6+XUyo05f7O0oYtlN c/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI0u1ufm8/0i2BWSlmy5A5lREedCf+ 3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSw DPBMMPQFWAJI/TPlUq9LhONmUjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oA A7CXDpO8Wqj2LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY +gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kSk5Nrp+gvU5LE YFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl7uxMMne0nxrpS10gxdr9HIcW xkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygmbtmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQ gj9sAq+uEjonljYE1x2igGOpm/HlurR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbW aQbLU8uz/mtBzUF+fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoV Yh63n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE76KlXIx3 KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H9jVlpNMKVv/1F2Rs76gi JUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT4PsJYGw= -----END CERTIFICATE----- Amazon Root CA 3 ================ -----BEGIN CERTIFICATE----- MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5MQswCQYDVQQG EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSAzMB4XDTE1MDUy NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZB f8ANm+gBG1bG8lKlui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjr Zt6jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSrttvXBp43 rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkrBqWTrBqYaGFy+uGh0Psc eGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteMYyRIHN8wfdVoOw== -----END CERTIFICATE----- Amazon Root CA 4 ================ -----BEGIN CERTIFICATE----- MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5MQswCQYDVQQG EwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24gUm9vdCBDQSA0MB4XDTE1MDUy NjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZ MBcGA1UEAxMQQW1hem9uIFJvb3QgQ0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN /sGKe0uoe0ZLY7Bi9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri 83BkM6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV HQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WBMAoGCCqGSM49BAMDA2gA MGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlwCkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1 AE47xDqUEpHJWEadIRNyp4iciuRMStuW1KyLa2tJElMzrdfkviT8tQp21KW8EA== -----END CERTIFICATE----- TUBITAK Kamu SM SSL Kok Sertifikasi - Surum 1 ============================================= -----BEGIN CERTIFICATE----- MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIxGDAWBgNVBAcT D0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxpbXNlbCB2ZSBUZWtub2xvamlr IEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0wKwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24g TWVya2V6aSAtIEthbXUgU00xNjA0BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRp ZmlrYXNpIC0gU3VydW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYD VQQGEwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXllIEJpbGlt c2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklUQUsxLTArBgNVBAsTJEth bXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBTTTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11 IFNNIFNTTCBLb2sgU2VydGlmaWthc2kgLSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAr3UwM6q7a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y8 6Ij5iySrLqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INrN3wc wv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2XYacQuFWQfw4tJzh0 3+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/iSIzL+aFCr2lqBs23tPcLG07xxO9 WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4fAJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQU ZT/HiobGPN08VFw1+DrtUgxHV8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJ KoZIhvcNAQELBQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPfIPP54+M638yc lNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4lzwDGrpDxpa5RXI4s6ehlj2R e37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0j q5Rm+K37DwhuJi1/FwcJsoz7UMCflo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= -----END CERTIFICATE----- GDCA TrustAUTH R5 ROOT ====================== -----BEGIN CERTIFICATE----- MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCQ04xMjAw BgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8wHQYDVQQD DBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVow YjELMAkGA1UEBhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJjDp6L3TQs AlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBjTnnEt1u9ol2x8kECK62p OqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+uKU49tm7srsHwJ5uu4/Ts765/94Y9cnrr pftZTqfrlYwiOXnhLQiPzLyRuEH3FMEjqcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ 9Cy5WmYqsBebnh52nUpmMUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQ xXABZG12ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloPzgsM R6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3GkL30SgLdTMEZeS1SZ D2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeCjGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4 oR24qoAATILnsn8JuLwwoC8N9VKejveSswoAHQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx 9hoh49pwBiFYFIeFd3mqgnkCAwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlR MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZmDRd9FBUb1Ov9 H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5COmSdI31R9KrO9b7eGZONn35 6ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ryL3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd +PwyvzeG5LuOmCd+uh8W4XAR8gPfJWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQ HtZa37dG/OaG+svgIHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBD F8Io2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV09tL7ECQ 8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQXR4EzzffHqhmsYzmIGrv /EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrqT8p+ck0LcIymSLumoRT2+1hEmRSuqguT aaApJUqlyyvdimYHFngVV3Eb7PVHhPOeMTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== -----END CERTIFICATE----- SSL.com Root Certification Authority RSA ======================================== -----BEGIN CERTIFICATE----- MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxDjAM BgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24x MTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYw MjEyMTczOTM5WhcNNDEwMjEyMTczOTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NM LmNvbSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2RxFdHaxh3a3by/ZPkPQ/C Fp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aXqhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8 P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcCC52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/ge oeOy3ZExqysdBP+lSgQ36YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkp k8zruFvh/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrFYD3Z fBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93EJNyAKoFBbZQ+yODJ gUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVcUS4cK38acijnALXRdMbX5J+tB5O2 UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi8 1xtZPCvM8hnIk2snYxnP/Okm+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4s bE6x/c+cCbqiM+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGVcpNxJK1ok1iOMq8bs3AD/CUr dIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBcHadm47GUBwwyOabqG7B52B2ccETjit3E+ZUf ijhDPwGFpUenPUayvOUiaPd7nNgsPgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAsl u1OJD7OAUN5F7kR/q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjq erQ0cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jra6x+3uxj MxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90IH37hVZkLId6Tngr75qNJ vTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/YK9f1JmzJBjSWFupwWRoyeXkLtoh/D1JI Pb9s2KJELtFOt3JY04kTlf5Eq/jXixtunLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406y wKBjYZC6VWg3dGq2ktufoYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NI WuuA8ShYIc2wBlX7Jz9TkHCpBB5XJ7k= -----END CERTIFICATE----- SSL.com Root Certification Authority ECC ======================================== -----BEGIN CERTIFICATE----- MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMCVVMxDjAMBgNV BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xMTAv BgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEy MTgxNDAzWhcNNDEwMjEyMTgxNDAzWjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAO BgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuBBAAiA2IA BEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI7Z4INcgn64mMU1jrYor+ 8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPgCemB+vNH06NjMGEwHQYDVR0OBBYEFILR hXMw5zUE044CkvvlpNHEIejNMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTT jgKS++Wk0cQh6M0wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCW e+0F+S8Tkdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+gA0z 5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl -----END CERTIFICATE----- SSL.com EV Root Certification Authority RSA R2 ============================================== -----BEGIN CERTIFICATE----- MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNVBAYTAlVTMQ4w DAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9u MTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy MB4XDTE3MDUzMTE4MTQzN1oXDTQyMDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQI DAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYD VQQDDC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMIICIjAN BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvqM0fNTPl9fb69LT3w23jh hqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssufOePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7w cXHswxzpY6IXFJ3vG2fThVUCAtZJycxa4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTO Zw+oz12WGQvE43LrrdF9HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+ B6KjBSYRaZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcAb9Zh CBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQGp8hLH94t2S42Oim 9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQVPWKchjgGAGYS5Fl2WlPAApiiECto RHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMOpgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+Slm JuwgUHfbSguPvuUCYHBBXtSuUDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48 +qvWBkofZ6aYMBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa49QaAJadz20Zp qJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBWs47LCp1Jjr+kxJG7ZhcFUZh1 ++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nx Y/hoLVUE0fKNsKTPvDxeH3jnpaAgcLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2G guDKBAdRUNf/ktUM79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDz OFSz/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXtll9ldDz7 CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEmKf7GUmG6sXP/wwyc5Wxq lD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKKQbNmC1r7fSOl8hqw/96bg5Qu0T/fkreR rwU7ZcegbLHNYhLDkBvjJc40vG93drEQw/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1 hlMYegouCRw2n5H9gooiS9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX 9hwJ1C07mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== -----END CERTIFICATE----- SSL.com EV Root Certification Authority ECC =========================================== -----BEGIN CERTIFICATE----- MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMCVVMxDjAMBgNV BAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9TU0wgQ29ycG9yYXRpb24xNDAy BgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYw MjEyMTgxNTIzWhcNNDEwMjEyMTgxNTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMx EDAOBgNVBAcMB0hvdXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NM LmNvbSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB BAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMAVIbc/R/fALhBYlzccBYy 3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1KthkuWnBaBu2+8KGwytAJKaNjMGEwHQYDVR0O BBYEFFvKXuXe0oGqzagtZFG22XKbl+ZPMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe 5d7SgarNqC1kUbbZcpuX5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJ N+vp1RPZytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZgh5Mm m7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== -----END CERTIFICATE----- GlobalSign Root CA - R6 ======================= -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEgMB4GA1UECxMX R2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQxMjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9i YWxTaWduIFJvb3QgQ0EgLSBSNjETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFs U2lnbjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQss grRIxutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1kZguSgMpE 3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxDaNc9PIrFsmbVkJq3MQbF vuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJwLnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqM PKq0pPbzlUoSB239jLKJz9CgYXfIWHSw1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+ azayOeSsJDa38O+2HBNXk7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05O WgtH8wY2SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/hbguy CLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4nWUx2OVvq+aWh2IMP 0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpYrZxCRXluDocZXFSxZba/jJvcE+kN b7gu3GduyYsRtYQUigAZcIN5kZeR1BonvzceMgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQE AwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNV HSMEGDAWgBSubAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGtIxg93eFyRJa0 lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr6155wsTLxDKZmOMNOsIeDjHfrY BzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLjvUYAGm0CuiVdjaExUd1URhxN25mW7xocBFym Fe944Hn+Xds+qkxV/ZoVqW/hpvvfcDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr 3TsTjxKM4kEaSHpzoHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB1 0jZpnOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfspA9MRf/T uTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+vJJUEeKgDu+6B5dpffItK oZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+t JDfLRVpOoERIyNiwmcUVhAn21klJwGW45hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= -----END CERTIFICATE----- OISTE WISeKey Global Root GC CA =============================== -----BEGIN CERTIFICATE----- MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQswCQYDVQQGEwJD SDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNlZDEo MCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRa Fw00MjA1MDkwOTU4MzNaMG0xCzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQL ExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh bCBSb290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4nieUqjFqdr VCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4Wp2OQ0jnUsYd4XxiWD1Ab NTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAd BgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7TrYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0E AwMDaAAwZQIwJsdpW9zV57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtk AjEA2zQgMgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 -----END CERTIFICATE----- UCA Global G2 Root ================== -----BEGIN CERTIFICATE----- MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9MQswCQYDVQQG EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBHbG9iYWwgRzIgUm9vdDAeFw0x NjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0xCzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlU cnVzdDEbMBkGA1UEAwwSVUNBIEdsb2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxeYrb3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmT oni9kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzmVHqUwCoV 8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/RVogvGjqNO7uCEeBHANBS h6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDcC/Vkw85DvG1xudLeJ1uK6NjGruFZfc8o LTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIjtm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/ R+zvWr9LesGtOxdQXGLYD0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBe KW4bHAyvj5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6DlNaBa 4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6iIis7nCs+dwp4wwc OxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznPO6Q0ibd5Ei9Hxeepl2n8pndntd97 8XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFIHEjMz15DD/pQwIX4wVZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo 5sOASD0Ee/ojL3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl1qnN3e92mI0A Ds0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oUb3n09tDh05S60FdRvScFDcH9 yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LVPtateJLbXDzz2K36uGt/xDYotgIVilQsnLAX c47QN6MUPJiVAAwpBVueSUmxX8fjy88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHo jhJi6IjMtX9Gl8CbEGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZk bxqgDMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI+Vg7RE+x ygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGyYiGqhkCyLmTTX8jjfhFn RR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bXUB+K+wb1whnw0A== -----END CERTIFICATE----- UCA Extended Validation Root ============================ -----BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG EwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9u IFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMxMDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8G A1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIi MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrs iWogD4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvSsPGP2KxF Rv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aopO2z6+I9tTcg1367r3CTu eUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dksHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR 59mzLC52LqGj3n5qiAno8geK+LLNEOfic0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH 0mK1lTnj8/FtDw5lhIpjVMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KR el7sFsLzKuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/TuDv B0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41Gsx2VYVdWf6/wFlth WG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs1+lvK9JKBZP8nm9rZ/+I8U6laUpS NwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQDfwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS 3H5aBZ8eNJr34RQwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQEL BQADggIBADaNl8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQVBcZEhrxH9cM aVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5c6sq1WnIeJEmMX3ixzDx/BR4 dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb +7lsq+KePRXBOy5nAliRn+/4Qh8st2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOW F3sGPjLtx7dCvHaj2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwi GpWOvpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2CxR9GUeOc GMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmxcmtpzyKEC2IPrNkZAJSi djzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbMfjKaiJUINlK73nZfdklJrX+9ZSCyycEr dhh2n1ax -----END CERTIFICATE----- Certigna Root CA ================ -----BEGIN CERTIFICATE----- MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAwWjELMAkGA1UE BhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAwMiA0ODE0NjMwODEwMDAzNjEZ MBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0xMzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjda MFoxCzAJBgNVBAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYz MDgxMDAwMzYxGTAXBgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4IC DwAwggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sOty3tRQgX stmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9MCiBtnyN6tMbaLOQdLNyz KNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPuI9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8 JXrJhFwLrN1CTivngqIkicuQstDuI7pmTLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16 XdG+RCYyKfHx9WzMfgIhC59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq 4NYKpkDfePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3YzIoej wpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWTCo/1VTp2lc5ZmIoJ lXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1kJWumIWmbat10TWuXekG9qxf5kBdI jzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp/ /TBt2dzhauH8XwIDAQABo4IBGjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw HQYDVR0OBBYEFBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of 1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczovL3d3d3cuY2Vy dGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilodHRwOi8vY3JsLmNlcnRpZ25h LmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYraHR0cDovL2NybC5kaGlteW90aXMuY29tL2Nl cnRpZ25hcm9vdGNhLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOIt OoldaDgvUSILSo3L6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxP TGRGHVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH60BGM+RFq 7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncBlA2c5uk5jR+mUYyZDDl3 4bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdio2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd 8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS 6Cvu5zHbugRqh5jnxV/vfaci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaY tlu3zM63Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayhjWZS aX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw3kAP+HwV96LOPNde E4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= -----END CERTIFICATE----- emSign Root CA - G1 =================== -----BEGIN CERTIFICATE----- MIIDlDCCAnygAwIBAgIKMfXkYgxsWO3W2DANBgkqhkiG9w0BAQsFADBnMQswCQYDVQQGEwJJTjET MBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRl ZDEcMBoGA1UEAxMTZW1TaWduIFJvb3QgQ0EgLSBHMTAeFw0xODAyMTgxODMwMDBaFw00MzAyMTgx ODMwMDBaMGcxCzAJBgNVBAYTAklOMRMwEQYDVQQLEwplbVNpZ24gUEtJMSUwIwYDVQQKExxlTXVk aHJhIFRlY2hub2xvZ2llcyBMaW1pdGVkMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEcxMIIB IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAk0u76WaK7p1b1TST0Bsew+eeuGQzf2N4aLTN LnF115sgxk0pvLZoYIr3IZpWNVrzdr3YzZr/k1ZLpVkGoZM0Kd0WNHVO8oG0x5ZOrRkVUkr+PHB1 cM2vK6sVmjM8qrOLqs1D/fXqcP/tzxE7lM5OMhbTI0Aqd7OvPAEsbO2ZLIvZTmmYsvePQbAyeGHW DV/D+qJAkh1cF+ZwPjXnorfCYuKrpDhMtTk1b+oDafo6VGiFbdbyL0NVHpENDtjVaqSW0RM8LHhQ 6DqS0hdW5TUaQBw+jSztOd9C4INBdN+jzcKGYEho42kLVACL5HZpIQ15TjQIXhTCzLG3rdd8cIrH hQIDAQABo0IwQDAdBgNVHQ4EFgQU++8Nhp6w492pufEhF38+/PB3KxowDgYDVR0PAQH/BAQDAgEG MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAFn/8oz1h31xPaOfG1vR2vjTnGs2 vZupYeveFix0PZ7mddrXuqe8QhfnPZHr5X3dPpzxz5KsbEjMwiI/aTvFthUvozXGaCocV685743Q NcMYDHsAVhzNixl03r4PEuDQqqE/AjSxcM6dGNYIAwlG7mDgfrbESQRRfXBgvKqy/3lyeqYdPV8q +Mri/Tm3R7nrft8EI6/6nAYH6ftjk4BAtcZsCjEozgyfz7MjNYBBjWzEN3uBL4ChQEKF6dk4jeih U80Bv2noWgbyRQuQ+q7hv53yrlc8pa6yVvSLZUDp/TGBLPQ5Cdjua6e0ph0VpZj3AYHYhX3zUVxx iN66zB+Afko= -----END CERTIFICATE----- emSign ECC Root CA - G3 ======================= -----BEGIN CERTIFICATE----- MIICTjCCAdOgAwIBAgIKPPYHqWhwDtqLhDAKBggqhkjOPQQDAzBrMQswCQYDVQQGEwJJTjETMBEG A1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEg MB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0gRzMwHhcNMTgwMjE4MTgzMDAwWhcNNDMwMjE4 MTgzMDAwWjBrMQswCQYDVQQGEwJJTjETMBEGA1UECxMKZW1TaWduIFBLSTElMCMGA1UEChMcZU11 ZGhyYSBUZWNobm9sb2dpZXMgTGltaXRlZDEgMB4GA1UEAxMXZW1TaWduIEVDQyBSb290IENBIC0g RzMwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQjpQy4LRL1KPOxst3iAhKAnjlfSU2fySU0WXTsuwYc 58Byr+iuL+FBVIcUqEqy6HyC5ltqtdyzdc6LBtCGI79G1Y4PPwT01xySfvalY8L1X44uT6EYGQIr MgqCZH0Wk9GjQjBAMB0GA1UdDgQWBBR8XQKEE9TMipuBzhccLikenEhjQjAOBgNVHQ8BAf8EBAMC AQYwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNpADBmAjEAvvNhzwIQHWSVB7gYboiFBS+D CBeQyh+KTOgNG3qxrdWBCUfvO6wIBHxcmbHtRwfSAjEAnbpV/KlK6O3t5nYBQnvI+GDZjVGLVTv7 jHvrZQnD+JbNR6iC8hZVdyR+EhCVBCyj -----END CERTIFICATE----- emSign Root CA - C1 =================== -----BEGIN CERTIFICATE----- MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkGA1UEBhMCVVMx EzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNp Z24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UE BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQD ExNlbVNpZ24gUm9vdCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+up ufGZBczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZHdPIWoU/ Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH3DspVpNqs8FqOp099cGX OFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvHGPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4V I5b2P/AgNBbeCsbEBEV5f6f9vtKppa+cxSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleooms lMuoaJuvimUnzYnu3Yy1aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+ XJGFehiqTbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQAD ggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87/kOXSTKZEhVb3xEp /6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4kqNPEjE2NuLe/gDEo2APJ62gsIq1 NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrGYQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9 wC68AivTxEDkigcxHpvOJpkT+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQ BmIMMMAVSKeoWXzhriKi4gp6D/piq1JM4fHfyr6DDUI= -----END CERTIFICATE----- emSign ECC Root CA - C3 ======================= -----BEGIN CERTIFICATE----- MIICKzCCAbGgAwIBAgIKe3G2gla4EnycqDAKBggqhkjOPQQDAzBaMQswCQYDVQQGEwJVUzETMBEG A1UECxMKZW1TaWduIFBLSTEUMBIGA1UEChMLZU11ZGhyYSBJbmMxIDAeBgNVBAMTF2VtU2lnbiBF Q0MgUm9vdCBDQSAtIEMzMB4XDTE4MDIxODE4MzAwMFoXDTQzMDIxODE4MzAwMFowWjELMAkGA1UE BhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMSAwHgYDVQQD ExdlbVNpZ24gRUNDIFJvb3QgQ0EgLSBDMzB2MBAGByqGSM49AgEGBSuBBAAiA2IABP2lYa57JhAd 6bciMK4G9IGzsUJxlTm801Ljr6/58pc1kjZGDoeVjbk5Wum739D+yAdBPLtVb4OjavtisIGJAnB9 SMVK4+kiVCJNk7tCDK93nCOmfddhEc5lx/h//vXyqaNCMEAwHQYDVR0OBBYEFPtaSNCAIEDyqOkA B2kZd6fmw/TPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gA MGUCMQC02C8Cif22TGK6Q04ThHK1rt0c3ta13FaPWEBaLd4gTCKDypOofu4SQMfWh0/434UCMBwU ZOR8loMRnLDRWmFLpg9J0wD8ofzkpf9/rdcw0Md3f76BB1UwUCAU9Vc4CqgxUQ== -----END CERTIFICATE----- Hongkong Post Root CA 3 ======================= -----BEGIN CERTIFICATE----- MIIFzzCCA7egAwIBAgIUCBZfikyl7ADJk0DfxMauI7gcWqQwDQYJKoZIhvcNAQELBQAwbzELMAkG A1UEBhMCSEsxEjAQBgNVBAgTCUhvbmcgS29uZzESMBAGA1UEBxMJSG9uZyBLb25nMRYwFAYDVQQK Ew1Ib25na29uZyBQb3N0MSAwHgYDVQQDExdIb25na29uZyBQb3N0IFJvb3QgQ0EgMzAeFw0xNzA2 MDMwMjI5NDZaFw00MjA2MDMwMjI5NDZaMG8xCzAJBgNVBAYTAkhLMRIwEAYDVQQIEwlIb25nIEtv bmcxEjAQBgNVBAcTCUhvbmcgS29uZzEWMBQGA1UEChMNSG9uZ2tvbmcgUG9zdDEgMB4GA1UEAxMX SG9uZ2tvbmcgUG9zdCBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz iNfqzg8gTr7m1gNt7ln8wlffKWihgw4+aMdoWJwcYEuJQwy51BWy7sFOdem1p+/l6TWZ5Mwc50tf jTMwIDNT2aa71T4Tjukfh0mtUC1Qyhi+AViiE3CWu4mIVoBc+L0sPOFMV4i707mV78vH9toxdCim 5lSJ9UExyuUmGs2C4HDaOym71QP1mbpV9WTRYA6ziUm4ii8F0oRFKHyPaFASePwLtVPLwpgchKOe sL4jpNrcyCse2m5FHomY2vkALgbpDDtw1VAliJnLzXNg99X/NWfFobxeq81KuEXryGgeDQ0URhLj 0mRiikKYvLTGCAj4/ahMZJx2Ab0vqWwzD9g/KLg8aQFChn5pwckGyuV6RmXpwtZQQS4/t+TtbNe/ JgERohYpSms0BpDsE9K2+2p20jzt8NYt3eEV7KObLyzJPivkaTv/ciWxNoZbx39ri1UbSsUgYT2u y1DhCDq+sI9jQVMwCFk8mB13umOResoQUGC/8Ne8lYePl8X+l2oBlKN8W4UdKjk60FSh0Tlxnf0h +bV78OLgAo9uliQlLKAeLKjEiafv7ZkGL7YKTE/bosw3Gq9HhS2KX8Q0NEwA/RiTZxPRN+ZItIsG xVd7GYYKecsAyVKvQv83j+GjHno9UKtjBucVtT+2RTeUN7F+8kjDf8V1/peNRY8apxpyKBpADwID AQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBQXnc0e i9Y5K3DTXNSguB+wAPzFYTAdBgNVHQ4EFgQUF53NHovWOStw01zUoLgfsAD8xWEwDQYJKoZIhvcN AQELBQADggIBAFbVe27mIgHSQpsY1Q7XZiNc4/6gx5LS6ZStS6LG7BJ8dNVI0lkUmcDrudHr9Egw W62nV3OZqdPlt9EuWSRY3GguLmLYauRwCy0gUCCkMpXRAJi70/33MvJJrsZ64Ee+bs7Lo3I6LWld y8joRTnU+kLBEUx3XZL7av9YROXrgZ6voJmtvqkBZss4HTzfQx/0TW60uhdG/H39h4F5ag0zD/ov +BS5gLNdTaqX4fnkGMX41TiMJjz98iji7lpJiCzfeT2OnpA8vUFKOt1b9pq0zj8lMH8yfaIDlNDc eqFS3m6TjRgm/VWsvY+b0s+v54Ysyx8Jb6NvqYTUc79NoXQbTiNg8swOqn+knEwlqLJmOzj/2ZQw 9nKEvmhVEA/GcywWaZMH/rFF7buiVWqw2rVKAiUnhde3t4ZEFolsgCs+l6mc1X5VTMbeRRAc6uk7 nwNT7u56AQIWeNTowr5GdogTPyK7SBIdUgC0An4hGh6cJfTzPV4e0hz5sy229zdcxsshTrD3mUcY hcErulWuBurQB7Lcq9CClnXO0lD+mefPL5/ndtFhKvshuzHQqp9HpLIiyhY6UFfEW0NnxWViA0kB 60PZ2Pierc+xYw5F9KBaLJstxabArahH9CdMOA0uG0k7UvToiIMrVCjU8jVStDKDYmlkDJGcn5fq dBb9HxEGmpv0 -----END CERTIFICATE----- Microsoft ECC Root Certificate Authority 2017 ============================================= -----BEGIN CERTIFICATE----- MIICWTCCAd+gAwIBAgIQZvI9r4fei7FK6gxXMQHC7DAKBggqhkjOPQQDAzBlMQswCQYDVQQGEwJV UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQgRUND IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjMwNjQ1WhcNNDIwNzE4 MjMxNjA0WjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYw NAYDVQQDEy1NaWNyb3NvZnQgRUNDIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQ BgcqhkjOPQIBBgUrgQQAIgNiAATUvD0CQnVBEyPNgASGAlEvaqiBYgtlzPbKnR5vSmZRogPZnZH6 thaxjG7efM3beaYvzrvOcS/lpaso7GMEZpn4+vKTEAXhgShC48Zo9OYbhGBKia/teQ87zvH2RPUB eMCjVDBSMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIy5lycFIM +Oa+sgRXKSrPQhDtNTAQBgkrBgEEAYI3FQEEAwIBADAKBggqhkjOPQQDAwNoADBlAjBY8k3qDPlf Xu5gKcs68tvWMoQZP3zVL8KxzJOuULsJMsbG7X7JNpQS5GiFBqIb0C8CMQCZ6Ra0DvpWSNSkMBaR eNtUjGUBiudQZsIxtzm6uBoiB078a1QWIP8rtedMDE2mT3M= -----END CERTIFICATE----- Microsoft RSA Root Certificate Authority 2017 ============================================= -----BEGIN CERTIFICATE----- MIIFqDCCA5CgAwIBAgIQHtOXCV/YtLNHcB6qvn9FszANBgkqhkiG9w0BAQwFADBlMQswCQYDVQQG EwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTYwNAYDVQQDEy1NaWNyb3NvZnQg UlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwHhcNMTkxMjE4MjI1MTIyWhcNNDIw NzE4MjMwMDIzWjBlMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u MTYwNAYDVQQDEy1NaWNyb3NvZnQgUlNBIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcw ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKW76UM4wplZEWCpW9R2LBifOZNt9GkMml 7Xhqb0eRaPgnZ1AzHaGm++DlQ6OEAlcBXZxIQIJTELy/xztokLaCLeX0ZdDMbRnMlfl7rEqUrQ7e S0MdhweSE5CAg2Q1OQT85elss7YfUJQ4ZVBcF0a5toW1HLUX6NZFndiyJrDKxHBKrmCk3bPZ7Pw7 1VdyvD/IybLeS2v4I2wDwAW9lcfNcztmgGTjGqwu+UcF8ga2m3P1eDNbx6H7JyqhtJqRjJHTOoI+ dkC0zVJhUXAoP8XFWvLJjEm7FFtNyP9nTUwSlq31/niol4fX/V4ggNyhSyL71Imtus5Hl0dVe49F yGcohJUcaDDv70ngNXtk55iwlNpNhTs+VcQor1fznhPbRiefHqJeRIOkpcrVE7NLP8TjwuaGYaRS MLl6IE9vDzhTyzMMEyuP1pq9KsgtsRx9S1HKR9FIJ3Jdh+vVReZIZZ2vUpC6W6IYZVcSn2i51BVr lMRpIpj0M+Dt+VGOQVDJNE92kKz8OMHY4Xu54+OU4UZpyw4KUGsTuqwPN1q3ErWQgR5WrlcihtnJ 0tHXUeOrO8ZV/R4O03QK0dqq6mm4lyiPSMQH+FJDOvTKVTUssKZqwJz58oHhEmrARdlns87/I6KJ ClTUFLkqqNfs+avNJVgyeY+QW5g5xAgGwax/Dj0ApQIDAQABo1QwUjAOBgNVHQ8BAf8EBAMCAYYw DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUCctZf4aycI8awznjwNnpv7tNsiMwEAYJKwYBBAGC NxUBBAMCAQAwDQYJKoZIhvcNAQEMBQADggIBAKyvPl3CEZaJjqPnktaXFbgToqZCLgLNFgVZJ8og 6Lq46BrsTaiXVq5lQ7GPAJtSzVXNUzltYkyLDVt8LkS/gxCP81OCgMNPOsduET/m4xaRhPtthH80 dK2Jp86519efhGSSvpWhrQlTM93uCupKUY5vVau6tZRGrox/2KJQJWVggEbbMwSubLWYdFQl3JPk +ONVFT24bcMKpBLBaYVu32TxU5nhSnUgnZUP5NbcA/FZGOhHibJXWpS2qdgXKxdJ5XbLwVaZOjex /2kskZGT4d9Mozd2TaGf+G0eHdP67Pv0RR0Tbc/3WeUiJ3IrhvNXuzDtJE3cfVa7o7P4NHmJweDy AmH3pvwPuxwXC65B2Xy9J6P9LjrRk5Sxcx0ki69bIImtt2dmefU6xqaWM/5TkshGsRGRxpl/j8nW ZjEgQRCHLQzWwa80mMpkg/sTV9HB8Dx6jKXB/ZUhoHHBk2dxEuqPiAppGWSZI1b7rCoucL5mxAyE 7+WL85MB+GqQk2dLsmijtWKP6T+MejteD+eMuMZ87zf9dOLITzNy4ZQ5bb0Sr74MTnB8G2+NszKT c0QWbej09+CVgI+WXTik9KveCjCHk9hNAHFiRSdLOkKEW39lt2c0Ui2cFmuqqNh7o0JMcccMyj6D 5KbvtwEwXlGjefVwaaZBRA+GsCyRxj3qrg+E -----END CERTIFICATE----- e-Szigno Root CA 2017 ===================== -----BEGIN CERTIFICATE----- MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNVBAYTAkhVMREw DwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUt MjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJvb3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZa Fw00MjA4MjIxMjA3MDZaMHExCzAJBgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UE CgwNTWljcm9zZWMgTHRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3pp Z25vIFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtvxie+RJCx s1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+HWyx7xf58etqjYzBhMA8G A1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSHERUI0arBeAyxr87GyZDv vzAEwDAfBgNVHSMEGDAWgBSHERUI0arBeAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEA tVfd14pVCzbhhkT61NlojbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxO svxyqltZ+efcMQ== -----END CERTIFICATE----- certSIGN Root CA G2 =================== -----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIJEQA0tk7GNi02MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNVBAYTAlJPMRQw EgYDVQQKEwtDRVJUU0lHTiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjAeFw0xNzAy MDYwOTI3MzVaFw00MjAyMDYwOTI3MzVaMEExCzAJBgNVBAYTAlJPMRQwEgYDVQQKEwtDRVJUU0lH TiBTQTEcMBoGA1UECxMTY2VydFNJR04gUk9PVCBDQSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAMDFdRmRfUR0dIf+DjuW3NgBFszuY5HnC2/OOwppGnzC46+CjobXXo9X69MhWf05 N0IwvlDqtg+piNguLWkh59E3GE59kdUWX2tbAMI5Qw02hVK5U2UPHULlj88F0+7cDBrZuIt4Imfk abBoxTzkbFpG583H+u/E7Eu9aqSs/cwoUe+StCmrqzWaTOTECMYmzPhpn+Sc8CnTXPnGFiWeI8Mg wT0PPzhAsP6CRDiqWhqKa2NYOLQV07YRaXseVO6MGiKscpc/I1mbySKEwQdPzH/iV8oScLumZfNp dWO9lfsbl83kqK/20U6o2YpxJM02PbyWxPFsqa7lzw1uKA2wDrXKUXt4FMMgL3/7FFXhEZn91Qqh ngLjYl/rNUssuHLoPj1PrCy7Lobio3aP5ZMqz6WryFyNSwb/EkaseMsUBzXgqd+L6a8VTxaJW732 jcZZroiFDsGJ6x9nxUWO/203Nit4ZoORUSs9/1F3dmKh7Gc+PoGD4FapUB8fepmrY7+EF3fxDTvf 95xhszWYijqy7DwaNz9+j5LP2RIUZNoQAhVB/0/E6xyjyfqZ90bp4RjZsbgyLcsUDFDYg2WD7rlc z8sFWkz6GZdr1l0T08JcVLwyc6B49fFtHsufpaafItzRUZ6CeWRgKRM+o/1Pcmqr4tTluCRVLERL iohEnMqE0yo7AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1Ud DgQWBBSCIS1mxteg4BXrzkwJd8RgnlRuAzANBgkqhkiG9w0BAQsFAAOCAgEAYN4auOfyYILVAzOB ywaK8SJJ6ejqkX/GM15oGQOGO0MBzwdw5AgeZYWR5hEit/UCI46uuR59H35s5r0l1ZUa8gWmr4UC b6741jH/JclKyMeKqdmfS0mbEVeZkkMR3rYzpMzXjWR91M08KCy0mpbqTfXERMQlqiCA2ClV9+BB /AYm/7k29UMUA2Z44RGx2iBfRgB4ACGlHgAoYXhvqAEBj500mv/0OJD7uNGzcgbJceaBxXntC6Z5 8hMLnPddDnskk7RI24Zf3lCGeOdA5jGokHZwYa+cNywRtYK3qq4kNFtyDGkNzVmf9nGvnAvRCjj5 BiKDUyUM/FHE5r7iOZULJK2v0ZXkltd0ZGtxTgI8qoXzIKNDOXZbbFD+mpwUHmUUihW9o4JFWklW atKcsWMy5WHgUyIOpwpJ6st+H6jiYoD2EEVSmAYY3qXNL3+q1Ok+CHLsIwMCPKaq2LxndD0UF/tU Sxfj03k9bWtJySgOLnRQvwzZRjoQhsmnP+mg7H/rpXdYaXHmgwo38oZJar55CJD2AhZkPuXaTH4M NMn5X7azKFGnpyuqSfqNZSlO42sTp5SjLVFteAxEy9/eCG/Oo2Sr05WE1LlSVHJ7liXMvGnjSG4N 0MedJ5qq+BOS3R7fY581qRY27Iy4g/Q9iY/NtBde17MXQRBdJ3NghVdJIgc= -----END CERTIFICATE----- Trustwave Global Certification Authority ======================================== -----BEGIN CERTIFICATE----- MIIF2jCCA8KgAwIBAgIMBfcOhtpJ80Y1LrqyMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u IEF1dGhvcml0eTAeFw0xNzA4MjMxOTM0MTJaFw00MjA4MjMxOTM0MTJaMIGIMQswCQYDVQQGEwJV UzERMA8GA1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xITAfBgNVBAoMGFRydXN0d2F2 ZSBIb2xkaW5ncywgSW5jLjExMC8GA1UEAwwoVHJ1c3R3YXZlIEdsb2JhbCBDZXJ0aWZpY2F0aW9u IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALldUShLPDeS0YLOvR29 zd24q88KPuFd5dyqCblXAj7mY2Hf8g+CY66j96xz0XznswuvCAAJWX/NKSqIk4cXGIDtiLK0thAf LdZfVaITXdHG6wZWiYj+rDKd/VzDBcdu7oaJuogDnXIhhpCujwOl3J+IKMujkkkP7NAP4m1ET4Bq stTnoApTAbqOl5F2brz81Ws25kCI1nsvXwXoLG0R8+eyvpJETNKXpP7ScoFDB5zpET71ixpZfR9o WN0EACyW80OzfpgZdNmcc9kYvkHHNHnZ9GLCQ7mzJ7Aiy/k9UscwR7PJPrhq4ufogXBeQotPJqX+ OsIgbrv4Fo7NDKm0G2x2EOFYeUY+VM6AqFcJNykbmROPDMjWLBz7BegIlT1lRtzuzWniTY+HKE40 Cz7PFNm73bZQmq131BnW2hqIyE4bJ3XYsgjxroMwuREOzYfwhI0Vcnyh78zyiGG69Gm7DIwLdVcE uE4qFC49DxweMqZiNu5m4iK4BUBjECLzMx10coos9TkpoNPnG4CELcU9402x/RpvumUHO1jsQkUm +9jaJXLE9gCxInm943xZYkqcBW89zubWR2OZxiRvchLIrH+QtAuRcOi35hYQcRfO3gZPSEF9NUqj ifLJS3tBEW1ntwiYTOURGa5CgNz7kAXU+FDKvuStx8KU1xad5hePrzb7AgMBAAGjQjBAMA8GA1Ud EwEB/wQFMAMBAf8wHQYDVR0OBBYEFJngGWcNYtt2s9o9uFvo/ULSMQ6HMA4GA1UdDwEB/wQEAwIB BjANBgkqhkiG9w0BAQsFAAOCAgEAmHNw4rDT7TnsTGDZqRKGFx6W0OhUKDtkLSGm+J1WE2pIPU/H PinbbViDVD2HfSMF1OQc3Og4ZYbFdada2zUFvXfeuyk3QAUHw5RSn8pk3fEbK9xGChACMf1KaA0H ZJDmHvUqoai7PF35owgLEQzxPy0QlG/+4jSHg9bP5Rs1bdID4bANqKCqRieCNqcVtgimQlRXtpla 4gt5kNdXElE1GYhBaCXUNxeEFfsBctyV3lImIJgm4nb1J2/6ADtKYdkNy1GTKv0WBpanI5ojSP5R vbbEsLFUzt5sQa0WZ37b/TjNuThOssFgy50X31ieemKyJo90lZvkWx3SD92YHJtZuSPTMaCm/zjd zyBP6VhWOmfD0faZmZ26NraAL4hHT4a/RDqA5Dccprrql5gR0IRiR2Qequ5AvzSxnI9O4fKSTx+O 856X3vOmeWqJcU9LJxdI/uz0UA9PSX3MReO9ekDFQdxhVicGaeVyQYHTtgGJoC86cnn+OjC/QezH Yj6RS8fZMXZC+fc8Y+wmjHMMfRod6qh8h6jCJ3zhM0EPz8/8AKAigJ5Kp28AsEFFtyLKaEjFQqKu 3R3y4G5OBVixwJAWKqQ9EEC+j2Jjg6mcgn0tAumDMHzLJ8n9HmYAsC7TIS+OMxZsmO0QqAfWzJPP 29FpHOTKyeC2nOnOcXHebD8WpHk= -----END CERTIFICATE----- Trustwave Global ECC P256 Certification Authority ================================================= -----BEGIN CERTIFICATE----- MIICYDCCAgegAwIBAgIMDWpfCD8oXD5Rld9dMAoGCCqGSM49BAMCMIGRMQswCQYDVQQGEwJVUzER MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1NiBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM1MTBaFw00MjA4MjMxOTM1MTBaMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDI1 NiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABH77bOYj 43MyCMpg5lOcunSNGLB4kFKA3TjASh3RqMyTpJcGOMoNFWLGjgEqZZ2q3zSRLoHB5DOSMcT9CTqm P62jQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcGADAdBgNVHQ4EFgQUo0EGrJBt 0UrrdaVKEJmzsaGLSvcwCgYIKoZIzj0EAwIDRwAwRAIgB+ZU2g6gWrKuEZ+Hxbb/ad4lvvigtwjz RM4q3wghDDcCIC0mA6AFvWvR9lz4ZcyGbbOcNEhjhAnFjXca4syc4XR7 -----END CERTIFICATE----- Trustwave Global ECC P384 Certification Authority ================================================= -----BEGIN CERTIFICATE----- MIICnTCCAiSgAwIBAgIMCL2Fl2yZJ6SAaEc7MAoGCCqGSM49BAMDMIGRMQswCQYDVQQGEwJVUzER MA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRydXN0d2F2ZSBI b2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4NCBDZXJ0aWZp Y2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MjMxOTM2NDNaFw00MjA4MjMxOTM2NDNaMIGRMQswCQYD VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xITAfBgNVBAoTGFRy dXN0d2F2ZSBIb2xkaW5ncywgSW5jLjE6MDgGA1UEAxMxVHJ1c3R3YXZlIEdsb2JhbCBFQ0MgUDM4 NCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGvaDXU1CDFH Ba5FmVXxERMuSvgQMSOjfoPTfygIOiYaOs+Xgh+AtycJj9GOMMQKmw6sWASr9zZ9lCOkmwqKi6vr /TklZvFe/oyujUF5nQlgziip04pt89ZF1PKYhDhloKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNV HQ8BAf8EBQMDBwYAMB0GA1UdDgQWBBRVqYSJ0sEyvRjLbKYHTsjnnb6CkDAKBggqhkjOPQQDAwNn ADBkAjA3AZKXRRJ+oPM+rRk6ct30UJMDEr5E0k9BpIycnR+j9sKS50gU/k6bpZFXrsY3crsCMGcl CrEMXu6pY5Jv5ZAL/mYiykf9ijH3g/56vxC+GCsej/YpHpRZ744hN8tRmKVuSw== -----END CERTIFICATE----- NAVER Global Root Certification Authority ========================================= -----BEGIN CERTIFICATE----- MIIFojCCA4qgAwIBAgIUAZQwHqIL3fXFMyqxQ0Rx+NZQTQ0wDQYJKoZIhvcNAQEMBQAwaTELMAkG A1UEBhMCS1IxJjAkBgNVBAoMHU5BVkVSIEJVU0lORVNTIFBMQVRGT1JNIENvcnAuMTIwMAYDVQQD DClOQVZFUiBHbG9iYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xNzA4MTgwODU4 NDJaFw0zNzA4MTgyMzU5NTlaMGkxCzAJBgNVBAYTAktSMSYwJAYDVQQKDB1OQVZFUiBCVVNJTkVT UyBQTEFURk9STSBDb3JwLjEyMDAGA1UEAwwpTkFWRVIgR2xvYmFsIFJvb3QgQ2VydGlmaWNhdGlv biBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC21PGTXLVAiQqrDZBb UGOukJR0F0Vy1ntlWilLp1agS7gvQnXp2XskWjFlqxcX0TM62RHcQDaH38dq6SZeWYp34+hInDEW +j6RscrJo+KfziFTowI2MMtSAuXaMl3Dxeb57hHHi8lEHoSTGEq0n+USZGnQJoViAbbJAh2+g1G7 XNr4rRVqmfeSVPc0W+m/6imBEtRTkZazkVrd/pBzKPswRrXKCAfHcXLJZtM0l/aM9BhK4dA9WkW2 aacp+yPOiNgSnABIqKYPszuSjXEOdMWLyEz59JuOuDxp7W87UC9Y7cSw0BwbagzivESq2M0UXZR4 Yb8ObtoqvC8MC3GmsxY/nOb5zJ9TNeIDoKAYv7vxvvTWjIcNQvcGufFt7QSUqP620wbGQGHfnZ3z VHbOUzoBppJB7ASjjw2i1QnK1sua8e9DXcCrpUHPXFNwcMmIpi3Ua2FzUCaGYQ5fG8Ir4ozVu53B A0K6lNpfqbDKzE0K70dpAy8i+/Eozr9dUGWokG2zdLAIx6yo0es+nPxdGoMuK8u180SdOqcXYZai cdNwlhVNt0xz7hlcxVs+Qf6sdWA7G2POAN3aCJBitOUt7kinaxeZVL6HSuOpXgRM6xBtVNbv8ejy YhbLgGvtPe31HzClrkvJE+2KAQHJuFFYwGY6sWZLxNUxAmLpdIQM201GLQIDAQABo0IwQDAdBgNV HQ4EFgQU0p+I36HNLL3s9TsBAZMzJ7LrYEswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMB Af8wDQYJKoZIhvcNAQEMBQADggIBADLKgLOdPVQG3dLSLvCkASELZ0jKbY7gyKoNqo0hV4/GPnrK 21HUUrPUloSlWGB/5QuOH/XcChWB5Tu2tyIvCZwTFrFsDDUIbatjcu3cvuzHV+YwIHHW1xDBE1UB jCpD5EHxzzp6U5LOogMFDTjfArsQLtk70pt6wKGm+LUx5vR1yblTmXVHIloUFcd4G7ad6Qz4G3bx hYTeodoS76TiEJd6eN4MUZeoIUCLhr0N8F5OSza7OyAfikJW4Qsav3vQIkMsRIz75Sq0bBwcupTg E34h5prCy8VCZLQelHsIJchxzIdFV4XTnyliIoNRlwAYl3dqmJLJfGBs32x9SuRwTMKeuB330DTH D8z7p/8Dvq1wkNoL3chtl1+afwkyQf3NosxabUzyqkn+Zvjp2DXrDige7kgvOtB5CTh8piKCk5XQ A76+AqAF3SAi428diDRgxuYKuQl1C/AH6GmWNcf7I4GOODm4RStDeKLRLBT/DShycpWbXgnbiUSY qqFJu3FS8r/2/yehNq+4tneI3TqkbZs0kNwUXTC/t+sX5Ie3cdCh13cV1ELX8vMxmV2b3RZtP+oG I/hGoiLtk/bdmuYqh7GYVPEi92tF4+KOdh2ajcQGjTa3FPOdVGm3jjzVpG2Tgbet9r1ke8LJaDmg kpzNNIaRkPpkUZ3+/uul9XXeifdy -----END CERTIFICATE----- AC RAIZ FNMT-RCM SERVIDORES SEGUROS =================================== -----BEGIN CERTIFICATE----- MIICbjCCAfOgAwIBAgIQYvYybOXE42hcG2LdnC6dlTAKBggqhkjOPQQDAzB4MQswCQYDVQQGEwJF UzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVzMRgwFgYDVQRhDA9WQVRFUy1RMjgy NjAwNEoxLDAqBgNVBAMMI0FDIFJBSVogRk5NVC1SQ00gU0VSVklET1JFUyBTRUdVUk9TMB4XDTE4 MTIyMDA5MzczM1oXDTQzMTIyMDA5MzczM1oweDELMAkGA1UEBhMCRVMxETAPBgNVBAoMCEZOTVQt UkNNMQ4wDAYDVQQLDAVDZXJlczEYMBYGA1UEYQwPVkFURVMtUTI4MjYwMDRKMSwwKgYDVQQDDCNB QyBSQUlaIEZOTVQtUkNNIFNFUlZJRE9SRVMgU0VHVVJPUzB2MBAGByqGSM49AgEGBSuBBAAiA2IA BPa6V1PIyqvfNkpSIeSX0oNnnvBlUdBeh8dHsVnyV0ebAAKTRBdp20LHsbI6GA60XYyzZl2hNPk2 LEnb80b8s0RpRBNm/dfF/a82Tc4DTQdxz69qBdKiQ1oKUm8BA06Oi6NCMEAwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFAG5L++/EYZg8k/QQW6rcx/n0m5JMAoGCCqG SM49BAMDA2kAMGYCMQCuSuMrQMN0EfKVrRYj3k4MGuZdpSRea0R7/DjiT8ucRRcRTBQnJlU5dUoD zBOQn5ICMQD6SmxgiHPz7riYYqnOK8LZiqZwMR2vsJRM60/G49HzYqc8/5MuB1xJAWdpEgJyv+c= -----END CERTIFICATE----- GlobalSign Root R46 =================== -----BEGIN CERTIFICATE----- MIIFWjCCA0KgAwIBAgISEdK7udcjGJ5AXwqdLdDfJWfRMA0GCSqGSIb3DQEBDAUAMEYxCzAJBgNV BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJv b3QgUjQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAX BgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBSNDYwggIi MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsrHQy6LNl5brtQyYdpokNRbopiLKkHWPd08Es CVeJOaFV6Wc0dwxu5FUdUiXSE2te4R2pt32JMl8Nnp8semNgQB+msLZ4j5lUlghYruQGvGIFAha/ r6gjA7aUD7xubMLL1aa7DOn2wQL7Id5m3RerdELv8HQvJfTqa1VbkNud316HCkD7rRlr+/fKYIje 2sGP1q7Vf9Q8g+7XFkyDRTNrJ9CG0Bwta/OrffGFqfUo0q3v84RLHIf8E6M6cqJaESvWJ3En7YEt bWaBkoe0G1h6zD8K+kZPTXhc+CtI4wSEy132tGqzZfxCnlEmIyDLPRT5ge1lFgBPGmSXZgjPjHvj K8Cd+RTyG/FWaha/LIWFzXg4mutCagI0GIMXTpRW+LaCtfOW3T3zvn8gdz57GSNrLNRyc0NXfeD4 12lPFzYE+cCQYDdF3uYM2HSNrpyibXRdQr4G9dlkbgIQrImwTDsHTUB+JMWKmIJ5jqSngiCNI/on ccnfxkF0oE32kRbcRoxfKWMxWXEM2G/CtjJ9++ZdU6Z+Ffy7dXxd7Pj2Fxzsx2sZy/N78CsHpdls eVR2bJ0cpm4O6XkMqCNqo98bMDGfsVR7/mrLZqrcZdCinkqaByFrgY/bxFn63iLABJzjqls2k+g9 vXqhnQt2sQvHnf3PmKgGwvgqo6GDoLclcqUC4wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYD VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA1yrc4GHqMywptWU4jaWSf8FmSwwDQYJKoZIhvcNAQEM BQADggIBAHx47PYCLLtbfpIrXTncvtgdokIzTfnvpCo7RGkerNlFo048p9gkUbJUHJNOxO97k4Vg JuoJSOD1u8fpaNK7ajFxzHmuEajwmf3lH7wvqMxX63bEIaZHU1VNaL8FpO7XJqti2kM3S+LGteWy gxk6x9PbTZ4IevPuzz5i+6zoYMzRx6Fcg0XERczzF2sUyQQCPtIkpnnpHs6i58FZFZ8d4kuaPp92 CC1r2LpXFNqD6v6MVenQTqnMdzGxRBF6XLE+0xRFFRhiJBPSy03OXIPBNvIQtQ6IbbjhVp+J3pZm OUdkLG5NrmJ7v2B0GbhWrJKsFjLtrWhV/pi60zTe9Mlhww6G9kuEYO4Ne7UyWHmRVSyBQ7N0H3qq JZ4d16GLuc1CLgSkZoNNiTW2bKg2SnkheCLQQrzRQDGQob4Ez8pn7fXwgNNgyYMqIgXQBztSvwye qiv5u+YfjyW6hY0XHgL+XVAEV8/+LbzvXMAaq7afJMbfc2hIkCwU9D9SGuTSyxTDYWnP4vkYxboz nxSjBF25cfe1lNj2M8FawTSLfJvdkzrnE6JwYZ+vj+vYxXX4M2bUdGc6N3ec592kD3ZDZopD8p/7 DEJ4Y9HiD2971KE9dJeFt0g5QdYg/NA6s/rob8SKunE3vouXsXgxT7PntgMTzlSdriVZzH81Xwj3 QEUxeCp6 -----END CERTIFICATE----- GlobalSign Root E46 =================== -----BEGIN CERTIFICATE----- MIICCzCCAZGgAwIBAgISEdK7ujNu1LzmJGjFDYQdmOhDMAoGCCqGSM49BAMDMEYxCzAJBgNVBAYT AkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRwwGgYDVQQDExNHbG9iYWxTaWduIFJvb3Qg RTQ2MB4XDTE5MDMyMDAwMDAwMFoXDTQ2MDMyMDAwMDAwMFowRjELMAkGA1UEBhMCQkUxGTAXBgNV BAoTEEdsb2JhbFNpZ24gbnYtc2ExHDAaBgNVBAMTE0dsb2JhbFNpZ24gUm9vdCBFNDYwdjAQBgcq hkjOPQIBBgUrgQQAIgNiAAScDrHPt+ieUnd1NPqlRqetMhkytAepJ8qUuwzSChDH2omwlwxwEwkB jtjqR+q+soArzfwoDdusvKSGN+1wCAB16pMLey5SnCNoIwZD7JIvU4Tb+0cUB+hflGddyXqBPCCj QjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQxCpCPtsad0kRL gLWi5h+xEk8blTAKBggqhkjOPQQDAwNoADBlAjEA31SQ7Zvvi5QCkxeCmb6zniz2C5GMn0oUsfZk vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+ CAezNIm8BZ/3Hobui3A= -----END CERTIFICATE----- GLOBALTRUST 2020 ================ -----BEGIN CERTIFICATE----- MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw 4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9 iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS 8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2 HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918 oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg== -----END CERTIFICATE----- ANF Secure Server Root CA ========================= -----BEGIN CERTIFICATE----- MIIF7zCCA9egAwIBAgIIDdPjvGz5a7EwDQYJKoZIhvcNAQELBQAwgYQxEjAQBgNVBAUTCUc2MzI4 NzUxMDELMAkGA1UEBhMCRVMxJzAlBgNVBAoTHkFORiBBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lv bjEUMBIGA1UECxMLQU5GIENBIFJhaXoxIjAgBgNVBAMTGUFORiBTZWN1cmUgU2VydmVyIFJvb3Qg Q0EwHhcNMTkwOTA0MTAwMDM4WhcNMzkwODMwMTAwMDM4WjCBhDESMBAGA1UEBRMJRzYzMjg3NTEw MQswCQYDVQQGEwJFUzEnMCUGA1UEChMeQU5GIEF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uMRQw EgYDVQQLEwtBTkYgQ0EgUmFpejEiMCAGA1UEAxMZQU5GIFNlY3VyZSBTZXJ2ZXIgUm9vdCBDQTCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANvrayvmZFSVgpCjcqQZAZ2cC4Ffc0m6p6zz BE57lgvsEeBbphzOG9INgxwruJ4dfkUyYA8H6XdYfp9qyGFOtibBTI3/TO80sh9l2Ll49a2pcbnv T1gdpd50IJeh7WhM3pIXS7yr/2WanvtH2Vdy8wmhrnZEE26cLUQ5vPnHO6RYPUG9tMJJo8gN0pcv B2VSAKduyK9o7PQUlrZXH1bDOZ8rbeTzPvY1ZNoMHKGESy9LS+IsJJ1tk0DrtSOOMspvRdOoiXse zx76W0OLzc2oD2rKDF65nkeP8Nm2CgtYZRczuSPkdxl9y0oukntPLxB3sY0vaJxizOBQ+OyRp1RM VwnVdmPF6GUe7m1qzwmd+nxPrWAI/VaZDxUse6mAq4xhj0oHdkLePfTdsiQzW7i1o0TJrH93PB0j 7IKppuLIBkwC/qxcmZkLLxCKpvR/1Yd0DVlJRfbwcVw5Kda/SiOL9V8BY9KHcyi1Swr1+KuCLH5z JTIdC2MKF4EA/7Z2Xue0sUDKIbvVgFHlSFJnLNJhiQcND85Cd8BEc5xEUKDbEAotlRyBr+Qc5RQe 8TZBAQIvfXOn3kLMTOmJDVb3n5HUA8ZsyY/b2BzgQJhdZpmYgG4t/wHFzstGH6wCxkPmrqKEPMVO Hj1tyRRM4y5Bu8o5vzY8KhmqQYdOpc5LMnndkEl/AgMBAAGjYzBhMB8GA1UdIwQYMBaAFJxf0Gxj o1+TypOYCK2Mh6UsXME3MB0GA1UdDgQWBBScX9BsY6Nfk8qTmAitjIelLFzBNzAOBgNVHQ8BAf8E BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEATh65isagmD9uw2nAalxJ UqzLK114OMHVVISfk/CHGT0sZonrDUL8zPB1hT+L9IBdeeUXZ701guLyPI59WzbLWoAAKfLOKyzx j6ptBZNscsdW699QIyjlRRA96Gejrw5VD5AJYu9LWaL2U/HANeQvwSS9eS9OICI7/RogsKQOLHDt dD+4E5UGUcjohybKpFtqFiGS3XNgnhAY3jyB6ugYw3yJ8otQPr0R4hUDqDZ9MwFsSBXXiJCZBMXM 5gf0vPSQ7RPi6ovDj6MzD8EpTBNO2hVWcXNyglD2mjN8orGoGjR0ZVzO0eurU+AagNjqOknkJjCb 5RyKqKkVMoaZkgoQI1YS4PbOTOK7vtuNknMBZi9iPrJyJ0U27U1W45eZ/zo1PqVUSlJZS2Db7v54 EX9K3BR5YLZrZAPbFYPhor72I5dQ8AkzNqdxliXzuUJ92zg/LFis6ELhDtjTO0wugumDLmsx2d1H hk9tl5EuT+IocTUW0fJz/iUrB0ckYyfI+PbZa/wSMVYIwFNCr5zQM378BvAxRAMU8Vjq8moNqRGy g77FGr8H6lnco4g175x2MjxNBiLOFeXdntiP2t7SxDnlF4HPOEfrf4htWRvfn0IUrn7PqLBmZdo3 r5+qPeoott7VMVgWglvquxl1AnMaykgaIZOQCo6ThKd9OyMYkomgjaw= -----END CERTIFICATE----- Certum EC-384 CA ================ -----BEGIN CERTIFICATE----- MIICZTCCAeugAwIBAgIQeI8nXIESUiClBNAt3bpz9DAKBggqhkjOPQQDAzB0MQswCQYDVQQGEwJQ TDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2Vy dGlmaWNhdGlvbiBBdXRob3JpdHkxGTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwHhcNMTgwMzI2 MDcyNDU0WhcNNDMwMzI2MDcyNDU0WjB0MQswCQYDVQQGEwJQTDEhMB8GA1UEChMYQXNzZWNvIERh dGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx GTAXBgNVBAMTEENlcnR1bSBFQy0zODQgQ0EwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATEKI6rGFtq vm5kN2PkzeyrOvfMobgOgknXhimfoZTy42B4mIF4Bk3y7JoOV2CDn7TmFy8as10CW4kjPMIRBSqn iBMY81CE1700LCeJVf/OTOffph8oxPBUw7l8t1Ot68KjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFI0GZnQkdjrzife81r1HfS+8EF9LMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNo ADBlAjADVS2m5hjEfO/JUG7BJw+ch69u1RsIGL2SKcHvlJF40jocVYli5RsJHrpka/F2tNQCMQC0 QoSZ/6vnnvuRlydd3LBbMHHOXjgaatkl5+r3YZJW+OraNsKHZZYuciUvf9/DE8k= -----END CERTIFICATE----- Certum Trusted Root CA ====================== -----BEGIN CERTIFICATE----- MIIFwDCCA6igAwIBAgIQHr9ZULjJgDdMBvfrVU+17TANBgkqhkiG9w0BAQ0FADB6MQswCQYDVQQG EwJQTDEhMB8GA1UEChMYQXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0g Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0Ew HhcNMTgwMzE2MTIxMDEzWhcNNDMwMzE2MTIxMDEzWjB6MQswCQYDVQQGEwJQTDEhMB8GA1UEChMY QXNzZWNvIERhdGEgU3lzdGVtcyBTLkEuMScwJQYDVQQLEx5DZXJ0dW0gQ2VydGlmaWNhdGlvbiBB dXRob3JpdHkxHzAdBgNVBAMTFkNlcnR1bSBUcnVzdGVkIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEB AQUAA4ICDwAwggIKAoICAQDRLY67tzbqbTeRn06TpwXkKQMlzhyC93yZn0EGze2jusDbCSzBfN8p fktlL5On1AFrAygYo9idBcEq2EXxkd7fO9CAAozPOA/qp1x4EaTByIVcJdPTsuclzxFUl6s1wB52 HO8AU5853BSlLCIls3Jy/I2z5T4IHhQqNwuIPMqw9MjCoa68wb4pZ1Xi/K1ZXP69VyywkI3C7Te2 fJmItdUDmj0VDT06qKhF8JVOJVkdzZhpu9PMMsmN74H+rX2Ju7pgE8pllWeg8xn2A1bUatMn4qGt g/BKEiJ3HAVz4hlxQsDsdUaakFjgao4rpUYwBI4Zshfjvqm6f1bxJAPXsiEodg42MEx51UGamqi4 NboMOvJEGyCI98Ul1z3G4z5D3Yf+xOr1Uz5MZf87Sst4WmsXXw3Hw09Omiqi7VdNIuJGmj8PkTQk fVXjjJU30xrwCSss0smNtA0Aq2cpKNgB9RkEth2+dv5yXMSFytKAQd8FqKPVhJBPC/PgP5sZ0jeJ P/J7UhyM9uH3PAeXjA6iWYEMspA90+NZRu0PqafegGtaqge2Gcu8V/OXIXoMsSt0Puvap2ctTMSY njYJdmZm/Bo/6khUHL4wvYBQv3y1zgD2DGHZ5yQD4OMBgQ692IU0iL2yNqh7XAjlRICMb/gv1SHK HRzQ+8S1h9E6Tsd2tTVItQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSM+xx1 vALTn04uSNn5YFSqxLNP+jAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQENBQADggIBAEii1QAL LtA/vBzVtVRJHlpr9OTy4EA34MwUe7nJ+jW1dReTagVphZzNTxl4WxmB82M+w85bj/UvXgF2Ez8s ALnNllI5SW0ETsXpD4YN4fqzX4IS8TrOZgYkNCvozMrnadyHncI013nR03e4qllY/p0m+jiGPp2K h2RX5Rc64vmNueMzeMGQ2Ljdt4NR5MTMI9UGfOZR0800McD2RrsLrfw9EAUqO0qRJe6M1ISHgCq8 CYyqOhNf6DR5UMEQGfnTKB7U0VEwKbOukGfWHwpjscWpxkIxYxeU72nLL/qMFH3EQxiJ2fAyQOaA 4kZf5ePBAFmo+eggvIksDkc0C+pXwlM2/KfUrzHN/gLldfq5Jwn58/U7yn2fqSLLiMmq0Uc9Nneo WWRrJ8/vJ8HjJLWG965+Mk2weWjROeiQWMODvA8s1pfrzgzhIMfatz7DP78v3DSk+yshzWePS/Tj 6tQ/50+6uaWTRRxmHyH6ZF5v4HaUMst19W7l9o/HuKTMqJZ9ZPskWkoDbGs4xugDQ5r3V7mzKWmT OPQD8rv7gmsHINFSH5pkAnuYZttcTVoP0ISVoDwUQwbKytu4QTbaakRnh6+v40URFWkIsr4WOZck bxJF0WddCajJFdr60qZfE2Efv4WstK2tBZQIgx51F9NxO5NQI1mg7TyRVJ12AMXDuDjb -----END CERTIFICATE----- TunTrust Root CA ================ -----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIUEwLV4kBMkkaGFmddtLu7sms+/BMwDQYJKoZIhvcNAQELBQAwYTELMAkG A1UEBhMCVE4xNzA1BgNVBAoMLkFnZW5jZSBOYXRpb25hbGUgZGUgQ2VydGlmaWNhdGlvbiBFbGVj dHJvbmlxdWUxGTAXBgNVBAMMEFR1blRydXN0IFJvb3QgQ0EwHhcNMTkwNDI2MDg1NzU2WhcNNDQw NDI2MDg1NzU2WjBhMQswCQYDVQQGEwJUTjE3MDUGA1UECgwuQWdlbmNlIE5hdGlvbmFsZSBkZSBD ZXJ0aWZpY2F0aW9uIEVsZWN0cm9uaXF1ZTEZMBcGA1UEAwwQVHVuVHJ1c3QgUm9vdCBDQTCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMPN0/y9BFPdDCA61YguBUtB9YOCfvdZn56eY+hz 2vYGqU8ftPkLHzmMmiDQfgbU7DTZhrx1W4eI8NLZ1KMKsmwb60ksPqxd2JQDoOw05TDENX37Jk0b bjBU2PWARZw5rZzJJQRNmpA+TkBuimvNKWfGzC3gdOgFVwpIUPp6Q9p+7FuaDmJ2/uqdHYVy7BG7 NegfJ7/Boce7SBbdVtfMTqDhuazb1YMZGoXRlJfXyqNlC/M4+QKu3fZnz8k/9YosRxqZbwUN/dAd gjH8KcwAWJeRTIAAHDOFli/LQcKLEITDCSSJH7UP2dl3RxiSlGBcx5kDPP73lad9UKGAwqmDrViW VSHbhlnUr8a83YFuB9tgYv7sEG7aaAH0gxupPqJbI9dkxt/con3YS7qC0lH4Zr8GRuR5KiY2eY8f Tpkdso8MDhz/yV3A/ZAQprE38806JG60hZC/gLkMjNWb1sjxVj8agIl6qeIbMlEsPvLfe/ZdeikZ juXIvTZxi11Mwh0/rViizz1wTaZQmCXcI/m4WEEIcb9PuISgjwBUFfyRbVinljvrS5YnzWuioYas DXxU5mZMZl+QviGaAkYt5IPCgLnPSz7ofzwB7I9ezX/SKEIBlYrilz0QIX32nRzFNKHsLA4KUiwS VXAkPcvCFDVDXSdOvsC9qnyW5/yeYa1E0wCXAgMBAAGjYzBhMB0GA1UdDgQWBBQGmpsfU33x9aTI 04Y+oXNZtPdEITAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFAaamx9TffH1pMjThj6hc1m0 90QhMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAqgVutt0Vyb+zxiD2BkewhpMl 0425yAA/l/VSJ4hxyXT968pk21vvHl26v9Hr7lxpuhbI87mP0zYuQEkHDVneixCwSQXi/5E/S7fd Ao74gShczNxtr18UnH1YeA32gAm56Q6XKRm4t+v4FstVEuTGfbvE7Pi1HE4+Z7/FXxttbUcoqgRY YdZ2vyJ/0Adqp2RT8JeNnYA/u8EH22Wv5psymsNUk8QcCMNE+3tjEUPRahphanltkE8pjkcFwRJp adbGNjHh/PqAulxPxOu3Mqz4dWEX1xAZufHSCe96Qp1bWgvUxpVOKs7/B9dPfhgGiPEZtdmYu65x xBzndFlY7wyJz4sfdZMaBBSSSFCp61cpABbjNhzI+L/wM9VBD8TMPN3pM0MBkRArHtG5Xc0yGYuP jCB31yLEQtyEFpslbei0VXF/sHyz03FJuc9SpAQ/3D2gu68zngowYI7bnV2UqL1g52KAdoGDDIzM MEZJ4gzSqK/rYXHv5yJiqfdcZGyfFoxnNidF9Ql7v/YQCvGwjVRDjAS6oz/v4jXH+XTgbzRB0L9z ZVcg+ZtnemZoJE6AZb0QmQZZ8mWvuMZHu/2QeItBcy6vVR/cO5JyboTT0GFMDcx2V+IthSIVNg3r AZ3r2OvEhJn7wAzMMujjd9qDRIueVSjAi1jTkD5OGwDxFa2DK5o= -----END CERTIFICATE----- HARICA TLS RSA Root CA 2021 =========================== -----BEGIN CERTIFICATE----- MIIFpDCCA4ygAwIBAgIQOcqTHO9D88aOk8f0ZIk4fjANBgkqhkiG9w0BAQsFADBsMQswCQYDVQQG EwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9u cyBDQTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBSU0EgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTEwNTUz OFoXDTQ1MDIxMzEwNTUzN1owbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRl bWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgUlNB IFJvb3QgQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAIvC569lmwVnlskN JLnQDmT8zuIkGCyEf3dRywQRNrhe7Wlxp57kJQmXZ8FHws+RFjZiPTgE4VGC/6zStGndLuwRo0Xu a2s7TL+MjaQenRG56Tj5eg4MmOIjHdFOY9TnuEFE+2uva9of08WRiFukiZLRgeaMOVig1mlDqa2Y Ulhu2wr7a89o+uOkXjpFc5gH6l8Cct4MpbOfrqkdtx2z/IpZ525yZa31MJQjB/OCFks1mJxTuy/K 5FrZx40d/JiZ+yykgmvwKh+OC19xXFyuQnspiYHLA6OZyoieC0AJQTPb5lh6/a6ZcMBaD9YThnEv dmn8kN3bLW7R8pv1GmuebxWMevBLKKAiOIAkbDakO/IwkfN4E8/BPzWr8R0RI7VDIp4BkrcYAuUR 0YLbFQDMYTfBKnya4dC6s1BG7oKsnTH4+yPiAwBIcKMJJnkVU2DzOFytOOqBAGMUuTNe3QvboEUH GjMJ+E20pwKmafTCWQWIZYVWrkvL4N48fS0ayOn7H6NhStYqE613TBoYm5EPWNgGVMWX+Ko/IIqm haZ39qb8HOLubpQzKoNQhArlT4b4UEV4AIHrW2jjJo3Me1xR9BQsQL4aYB16cmEdH2MtiKrOokWQ CPxrvrNQKlr9qEgYRtaQQJKQCoReaDH46+0N0x3GfZkYVVYnZS6NRcUk7M7jAgMBAAGjQjBAMA8G A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFApII6ZgpJIKM+qTW8VX6iVNvRLuMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAPpBIqm5iFSVmewzVjIuJndftTgfvnNAUX15QvWiWkKQU EapobQk1OUAJ2vQJLDSle1mESSmXdMgHHkdt8s4cUCbjnj1AUz/3f5Z2EMVGpdAgS1D0NTsY9FVq QRtHBmg8uwkIYtlfVUKqrFOFrJVWNlar5AWMxajaH6NpvVMPxP/cyuN+8kyIhkdGGvMA9YCRotxD QpSbIPDRzbLrLFPCU3hKTwSUQZqPJzLB5UkZv/HywouoCjkxKLR9YjYsTewfM7Z+d21+UPCfDtcR j88YxeMn/ibvBZ3PzzfF0HvaO7AWhAw6k9a+F9sPPg4ZeAnHqQJyIkv3N3a6dcSFA1pj1bF1BcK5 vZStjBWZp5N99sXzqnTPBIWUmAD04vnKJGW/4GKvyMX6ssmeVkjaef2WdhW+o45WxLM0/L5H9MG0 qPzVMIho7suuyWPEdr6sOBjhXlzPrjoiUevRi7PzKzMHVIf6tLITe7pTBGIBnfHAT+7hOtSLIBD6 Alfm78ELt5BGnBkpjNxvoEppaZS3JGWg/6w/zgH7IS79aPib8qXPMThcFarmlwDB31qlpzmq6YR/ PFGoOtmUW4y/Twhx5duoXNTSpv4Ao8YWxw/ogM4cKGR0GQjTQuPOAF1/sdwTsOEFy9EgqoZ0njnn kf3/W9b3raYvAwtt41dU63ZTGI0RmLo= -----END CERTIFICATE----- HARICA TLS ECC Root CA 2021 =========================== -----BEGIN CERTIFICATE----- MIICVDCCAdugAwIBAgIQZ3SdjXfYO2rbIvT/WeK/zjAKBggqhkjOPQQDAzBsMQswCQYDVQQGEwJH UjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBD QTEkMCIGA1UEAwwbSEFSSUNBIFRMUyBFQ0MgUm9vdCBDQSAyMDIxMB4XDTIxMDIxOTExMDExMFoX DTQ1MDIxMzExMDEwOVowbDELMAkGA1UEBhMCR1IxNzA1BgNVBAoMLkhlbGxlbmljIEFjYWRlbWlj IGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ0ExJDAiBgNVBAMMG0hBUklDQSBUTFMgRUNDIFJv b3QgQ0EgMjAyMTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDgI/rGgltJ6rK9JOtDA4MM7KKrxcm1l AEeIhPyaJmuqS7psBAqIXhfyVYf8MLA04jRYVxqEU+kw2anylnTDUR9YSTHMmE5gEYd103KUkE+b ECUqqHgtvpBBWJAVcqeht6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUyRtTgRL+BNUW 0aq8mm+3oJUZbsowDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2cAMGQCMBHervjcToiwqfAi rcJRQO9gcS3ujwLEXQNwSaSS6sUUiHCm0w2wqsosQJz76YJumgIwK0eaB8bRwoF8yguWGEEbo/Qw CZ61IygNnxS2PFOiTAZpffpskcYqSUXm7LcT4Tps -----END CERTIFICATE----- Autoridad de Certificacion Firmaprofesional CIF A62634068 ========================================================= -----BEGIN CERTIFICATE----- MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCRVMxQjBA BgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1hcHJvZmVzaW9uYWwgQ0lGIEE2 MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIw QAYDVQQDDDlBdXRvcmlkYWQgZGUgQ2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBB NjI2MzQwNjgwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDD Utd9thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQMcas9UX4P B99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefGL9ItWY16Ck6WaVICqjaY 7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15iNA9wBj4gGFrO93IbJWyTdBSTo3OxDqqH ECNZXyAFGUftaI6SEspd/NYrspI8IM/hX68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyI plD9amML9ZMWGxmPsu2bm8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctX MbScyJCyZ/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirjaEbsX LZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/TKI8xWVvTyQKmtFLK bpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF6NkBiDkal4ZkQdU7hwxu+g/GvUgU vzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVhOSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1Ud DgQWBBRlzeurNR4APn7VdMActHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4w gZswgZgGBFUdIAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABCAG8AbgBhAG4A bwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAwADEANzAOBgNVHQ8BAf8EBAMC AQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9miWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL 4QjbEwj4KKE1soCzC1HA01aajTNFSa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDb LIpgD7dvlAceHabJhfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1il I45PVf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZEEAEeiGaP cjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV1aUsIC+nmCjuRfzxuIgA LI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2tCsvMo2ebKHTEm9caPARYpoKdrcd7b/+A lun4jWq9GJAd/0kakFI3ky88Al2CdgtR5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH 9IBk9W6VULgRfhVwOEqwf9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpf NIbnYrX9ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNKGbqE ZycPvEJdvSRUDewdcAZfpLz6IHxV -----END CERTIFICATE----- vTrus ECC Root CA ================= -----BEGIN CERTIFICATE----- MIICDzCCAZWgAwIBAgIUbmq8WapTvpg5Z6LSa6Q75m0c1towCgYIKoZIzj0EAwMwRzELMAkGA1UE BhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBS b290IENBMB4XDTE4MDczMTA3MjY0NFoXDTQzMDczMTA3MjY0NFowRzELMAkGA1UEBhMCQ04xHDAa BgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xGjAYBgNVBAMTEXZUcnVzIEVDQyBSb290IENBMHYw EAYHKoZIzj0CAQYFK4EEACIDYgAEZVBKrox5lkqqHAjDo6LN/llWQXf9JpRCux3NCNtzslt188+c ToL0v/hhJoVs1oVbcnDS/dtitN9Ti72xRFhiQgnH+n9bEOf+QP3A2MMrMudwpremIFUde4BdS49n TPEQo0IwQDAdBgNVHQ4EFgQUmDnNvtiyjPeyq+GtJK97fKHbH88wDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwCgYIKoZIzj0EAwMDaAAwZQIwV53dVvHH4+m4SVBrm2nDb+zDfSXkV5UT QJtS0zvzQBm8JsctBp61ezaf9SXUY2sAAjEA6dPGnlaaKsyh2j/IZivTWJwghfqrkYpwcBE4YGQL YgmRWAD5Tfs0aNoJrSEGGJTO -----END CERTIFICATE----- vTrus Root CA ============= -----BEGIN CERTIFICATE----- MIIFVjCCAz6gAwIBAgIUQ+NxE9izWRRdt86M/TX9b7wFjUUwDQYJKoZIhvcNAQELBQAwQzELMAkG A1UEBhMCQ04xHDAaBgNVBAoTE2lUcnVzQ2hpbmEgQ28uLEx0ZC4xFjAUBgNVBAMTDXZUcnVzIFJv b3QgQ0EwHhcNMTgwNzMxMDcyNDA1WhcNNDMwNzMxMDcyNDA1WjBDMQswCQYDVQQGEwJDTjEcMBoG A1UEChMTaVRydXNDaGluYSBDby4sTHRkLjEWMBQGA1UEAxMNdlRydXMgUm9vdCBDQTCCAiIwDQYJ KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL1VfGHTuB0EYgWgrmy3cLRB6ksDXhA/kFocizuwZots SKYcIrrVQJLuM7IjWcmOvFjai57QGfIvWcaMY1q6n6MLsLOaXLoRuBLpDLvPbmyAhykUAyyNJJrI ZIO1aqwTLDPxn9wsYTwaP3BVm60AUn/PBLn+NvqcwBauYv6WTEN+VRS+GrPSbcKvdmaVayqwlHeF XgQPYh1jdfdr58tbmnDsPmcF8P4HCIDPKNsFxhQnL4Z98Cfe/+Z+M0jnCx5Y0ScrUw5XSmXX+6KA YPxMvDVTAWqXcoKv8R1w6Jz1717CbMdHflqUhSZNO7rrTOiwCcJlwp2dCZtOtZcFrPUGoPc2BX70 kLJrxLT5ZOrpGgrIDajtJ8nU57O5q4IikCc9Kuh8kO+8T/3iCiSn3mUkpF3qwHYw03dQ+A0Em5Q2 AXPKBlim0zvc+gRGE1WKyURHuFE5Gi7oNOJ5y1lKCn+8pu8fA2dqWSslYpPZUxlmPCdiKYZNpGvu /9ROutW04o5IWgAZCfEF2c6Rsffr6TlP9m8EQ5pV9T4FFL2/s1m02I4zhKOQUqqzApVg+QxMaPnu 1RcN+HFXtSXkKe5lXa/R7jwXC1pDxaWG6iSe4gUH3DRCEpHWOXSuTEGC2/KmSNGzm/MzqvOmwMVO 9fSddmPmAsYiS8GVP1BkLFTltvA8Kc9XAgMBAAGjQjBAMB0GA1UdDgQWBBRUYnBj8XWEQ1iO0RYg scasGrz2iTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOC AgEAKbqSSaet8PFww+SX8J+pJdVrnjT+5hpk9jprUrIQeBqfTNqK2uwcN1LgQkv7bHbKJAs5EhWd nxEt/Hlk3ODg9d3gV8mlsnZwUKT+twpw1aA08XXXTUm6EdGz2OyC/+sOxL9kLX1jbhd47F18iMjr jld22VkE+rxSH0Ws8HqA7Oxvdq6R2xCOBNyS36D25q5J08FsEhvMKar5CKXiNxTKsbhm7xqC5PD4 8acWabfbqWE8n/Uxy+QARsIvdLGx14HuqCaVvIivTDUHKgLKeBRtRytAVunLKmChZwOgzoy8sHJn xDHO2zTlJQNgJXtxmOTAGytfdELSS8VZCAeHvsXDf+eW2eHcKJfWjwXj9ZtOyh1QRwVTsMo554Wg icEFOwE30z9J4nfrI8iIZjs9OXYhRvHsXyO466JmdXTBQPfYaJqT4i2pLr0cox7IdMakLXogqzu4 sEb9b91fUlV1YvCXoHzXOP0l382gmxDPi7g4Xl7FtKYCNqEeXxzP4padKar9mK5S4fNBUvupLnKW nyfjqnN9+BojZns7q2WwMgFLFT49ok8MKzWixtlnEjUwzXYuFrOZnk1PTi07NEPhmg4NpGaXutIc SkwsKouLgU9xGqndXHt7CMUADTdA43x7VF8vhV929vensBxXVsFy6K2ir40zSbofitzmdHxghm+H l3s= -----END CERTIFICATE----- ISRG Root X2 ============ -----BEGIN CERTIFICATE----- MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQswCQYDVQQGEwJV UzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElT UkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVT MSkwJwYDVQQKEyBJbnRlcm5ldCBTZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNS RyBSb290IFgyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0H ttwW+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9ItgKbppb d9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV HQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZIzj0EAwMDaAAwZQIwe3lORlCEwkSHRhtF cP9Ymd70/aTSVaYgLXTWNLxBo1BfASdWtL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5 U6VR5CmD1/iQMVtCnwr1/q4AaOeMSQ+2b1tbFfLn -----END CERTIFICATE----- HiPKI Root CA - G1 ================== -----BEGIN CERTIFICATE----- MIIFajCCA1KgAwIBAgIQLd2szmKXlKFD6LDNdmpeYDANBgkqhkiG9w0BAQsFADBPMQswCQYDVQQG EwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0ZC4xGzAZBgNVBAMMEkhpUEtJ IFJvb3QgQ0EgLSBHMTAeFw0xOTAyMjIwOTQ2MDRaFw0zNzEyMzExNTU5NTlaME8xCzAJBgNVBAYT AlRXMSMwIQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEbMBkGA1UEAwwSSGlQS0kg Um9vdCBDQSAtIEcxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9B5/UnMyDHPkvRN0 o9QwqNCuS9i233VHZvR85zkEHmpwINJaR3JnVfSl6J3VHiGh8Ge6zCFovkRTv4354twvVcg3Px+k wJyz5HdcoEb+d/oaoDjq7Zpy3iu9lFc6uux55199QmQ5eiY29yTw1S+6lZgRZq2XNdZ1AYDgr/SE YYwNHl98h5ZeQa/rh+r4XfEuiAU+TCK72h8q3VJGZDnzQs7ZngyzsHeXZJzA9KMuH5UHsBffMNsA GJZMoYFL3QRtU6M9/Aes1MU3guvklQgZKILSQjqj2FPseYlgSGDIcpJQ3AOPgz+yQlda22rpEZfd hSi8MEyr48KxRURHH+CKFgeW0iEPU8DtqX7UTuybCeyvQqww1r/REEXgphaypcXTT3OUM3ECoWqj 1jOXTyFjHluP2cFeRXF3D4FdXyGarYPM+l7WjSNfGz1BryB1ZlpK9p/7qxj3ccC2HTHsOyDry+K4 9a6SsvfhhEvyovKTmiKe0xRvNlS9H15ZFblzqMF8b3ti6RZsR1pl8w4Rm0bZ/W3c1pzAtH2lsN0/ Vm+h+fbkEkj9Bn8SV7apI09bA8PgcSojt/ewsTu8mL3WmKgMa/aOEmem8rJY5AIJEzypuxC00jBF 8ez3ABHfZfjcK0NVvxaXxA/VLGGEqnKG/uY6fsI/fe78LxQ+5oXdUG+3Se0CAwEAAaNCMEAwDwYD VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ncX+l6o/vY9cdVouslGDDjYr7AwDgYDVR0PAQH/BAQD AgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBQUfB13HAE4/+qddRxosuej6ip0691x1TPOhwEmSKsxBHi 7zNKpiMdDg1H2DfHb680f0+BazVP6XKlMeJ45/dOlBhbQH3PayFUhuaVevvGyuqcSE5XCV0vrPSl tJczWNWseanMX/mF+lLFjfiRFOs6DRfQUsJ748JzjkZ4Bjgs6FzaZsT0pPBWGTMpWmWSBUdGSquE wx4noR8RkpkndZMPvDY7l1ePJlsMu5wP1G4wB9TcXzZoZjmDlicmisjEOf6aIW/Vcobpf2Lll07Q JNBAsNB1CI69aO4I1258EHBGG3zgiLKecoaZAeO/n0kZtCW+VmWuF2PlHt/o/0elv+EmBYTksMCv 5wiZqAxeJoBF1PhoL5aPruJKHJwWDBNvOIf2u8g0X5IDUXlwpt/L9ZlNec1OvFefQ05rLisY+Gpz jLrFNe85akEez3GoorKGB1s6yeHvP2UEgEcyRHCVTjFnanRbEEV16rCf0OY1/k6fi8wrkkVbbiVg hUbN0aqwdmaTd5a+g744tiROJgvM7XpWGuDpWsZkrUx6AEhEL7lAuxM+vhV4nYWBSipX3tUZQ9rb yltHhoMLP7YNdnhzeSJesYAfz77RP1YQmCuVh6EfnWQUYDksswBVLuT1sw5XxJFBAJw/6KXf6vb/ yPCtbVKoF6ubYfwSUTXkJf2vqmqGOQ== -----END CERTIFICATE----- GlobalSign ECC Root CA - R4 =========================== -----BEGIN CERTIFICATE----- MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYDVQQLExtHbG9i YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgwMTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9i YWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkds b2JhbFNpZ24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkW ymOxuYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNVHQ8BAf8E BAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/+wpu+74zyTyjhNUwCgYI KoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147bmF0774BxL4YSFlhgjICICadVGNA3jdg UM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm -----END CERTIFICATE----- GTS Root R1 =========== -----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg UjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7wCl7raKb0 xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjwTcLCeoiKu7rPWRnWr4+w B7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0PfyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXW nOunVmSPlk9orj2XwoSPwLxAwAtcvfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk 9+aCEI3oncKKiPo4Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zq kUspzBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92wO1A K/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70paDPvOmbsB4om3xPX V2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDW cfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQAD ggIBAJ+qQibbC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuyh6f88/qBVRRi ClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM47HLwEXWdyzRSjeZ2axfG34ar J45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8JZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYci NuaCp+0KueIHoI17eko8cdLiA6EfMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5me LMFrUKTX5hgUvYU/Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJF fbdT6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ0E6yove+ 7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm2tIMPNuzjsmhDYAPexZ3 FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bbbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3 gm3c -----END CERTIFICATE----- GTS Root R2 =========== -----BEGIN CERTIFICATE----- MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQswCQYDVQQGEwJV UzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3Qg UjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UE ChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY6Dlo7JUl e3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAuMC6C/Pq8tBcKSOWIm8Wb a96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS +LFjKBC4swm4VndAoiaYecb+3yXuPuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7M kogwTZq9TwtImoS1mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJG r61K8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RWIr9q S34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKaG73VululycslaVNV J1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCqgc7dGtxRcw1PcOnlthYhGXmy5okL dWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0T AQH/BAUwAwEB/zAdBgNVHQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQAD ggIBAB/Kzt3HvqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8 0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyCB19m3H0Q/gxh swWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2uNmSRXbBoGOqKYcl3qJfEycel /FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMgyALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVn jWQye+mew4K6Ki3pHrTgSAai/GevHyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y5 9PYjJbigapordwj6xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M 7YNRTOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924SgJPFI/2R8 0L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV7LXTWtiBmelDGDfrs7vR WGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjW HYbL -----END CERTIFICATE----- GTS Root R3 =========== -----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMw HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjO PQIBBgUrgQQAIgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout 736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL24CejQjBA MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTB8Sa6oC2uhYHP0/Eq Er24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azT L818+FsuVbu/3ZL3pAzcMeGiAjEA/JdmZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV 11RZt+cRLInUue4X -----END CERTIFICATE----- GTS Root R4 =========== -----BEGIN CERTIFICATE----- MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYDVQQGEwJVUzEi MCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQw HhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZ R29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjO PQIBBgUrgQQAIgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvRHYqjQjBA MA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSATNbrdP9JNqPV2Py1 PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/C r8deVl5c1RxYIigL9zC2L7F8AjEA8GE8p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh 4rsUecrNIdSUtUlD -----END CERTIFICATE----- Telia Root CA v2 ================ -----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIPAWdfJ9b+euPkrL4JWwWeMA0GCSqGSIb3DQEBCwUAMEQxCzAJBgNVBAYT AkZJMRowGAYDVQQKDBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2 MjAeFw0xODExMjkxMTU1NTRaFw00MzExMjkxMTU1NTRaMEQxCzAJBgNVBAYTAkZJMRowGAYDVQQK DBFUZWxpYSBGaW5sYW5kIE95ajEZMBcGA1UEAwwQVGVsaWEgUm9vdCBDQSB2MjCCAiIwDQYJKoZI hvcNAQEBBQADggIPADCCAgoCggIBALLQPwe84nvQa5n44ndp586dpAO8gm2h/oFlH0wnrI4AuhZ7 6zBqAMCzdGh+sq/H1WKzej9Qyow2RCRj0jbpDIX2Q3bVTKFgcmfiKDOlyzG4OiIjNLh9vVYiQJ3q 9HsDrWj8soFPmNB06o3lfc1jw6P23pLCWBnglrvFxKk9pXSW/q/5iaq9lRdU2HhE8Qx3FZLgmEKn pNaqIJLNwaCzlrI6hEKNfdWV5Nbb6WLEWLN5xYzTNTODn3WhUidhOPFZPY5Q4L15POdslv5e2QJl tI5c0BE0312/UqeBAMN/mUWZFdUXyApT7GPzmX3MaRKGwhfwAZ6/hLzRUssbkmbOpFPlob/E2wnW 5olWK8jjfN7j/4nlNW4o6GwLI1GpJQXrSPjdscr6bAhR77cYbETKJuFzxokGgeWKrLDiKca5JLNr RBH0pUPCTEPlcDaMtjNXepUugqD0XBCzYYP2AgWGLnwtbNwDRm41k9V6lS/eINhbfpSQBGq6WT0E BXWdN6IOLj3rwaRSg/7Qa9RmjtzG6RJOHSpXqhC8fF6CfaamyfItufUXJ63RDolUK5X6wK0dmBR4 M0KGCqlztft0DbcbMBnEWg4cJ7faGND/isgFuvGqHKI3t+ZIpEYslOqodmJHixBTB0hXbOKSTbau BcvcwUpej6w9GU7C7WB1K9vBykLVAgMBAAGjYzBhMB8GA1UdIwQYMBaAFHKs5DN5qkWH9v2sHZ7W xy+G2CQ5MB0GA1UdDgQWBBRyrOQzeapFh/b9rB2e1scvhtgkOTAOBgNVHQ8BAf8EBAMCAQYwDwYD VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAoDtZpwmUPjaE0n4vOaWWl/oRrfxn83EJ 8rKJhGdEr7nv7ZbsnGTbMjBvZ5qsfl+yqwE2foH65IRe0qw24GtixX1LDoJt0nZi0f6X+J8wfBj5 tFJ3gh1229MdqfDBmgC9bXXYfef6xzijnHDoRnkDry5023X4blMMA8iZGok1GTzTyVR8qPAs5m4H eW9q4ebqkYJpCh3DflminmtGFZhb069GHWLIzoBSSRE/yQQSwxN8PzuKlts8oB4KtItUsiRnDe+C y748fdHif64W1lZYudogsYMVoe+KTTJvQS8TUoKU1xrBeKJR3Stwbbca+few4GeXVtt8YVMJAygC QMez2P2ccGrGKMOF6eLtGpOg3kuYooQ+BXcBlj37tCAPnHICehIv1aO6UXivKitEZU61/Qrowc15 h2Er3oBXRb9n8ZuRXqWk7FlIEA04x7D6w0RtBPV4UBySllva9bguulvP5fBqnUsvWHMtTy3EHD70 sz+rFQ47GUGKpMFXEmZxTPpT41frYpUJnlTd0cI8Vzy9OK2YZLe4A5pTVmBds9hCG1xLEooc6+t9 xnppxyd/pPiL8uSUZodL6ZQHCRJ5irLrdATczvREWeAWysUsWNc8e89ihmpQfTU2Zqf7N+cox9jQ raVplI/owd8k+BsHMYeB2F326CjYSlKArBPuUBQemMc= -----END CERTIFICATE----- D-TRUST BR Root CA 1 2020 ========================= -----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQfMmPK4TX3+oPyWWa00tNljAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0EgMSAy MDIwMB4XDTIwMDIxMTA5NDUwMFoXDTM1MDIxMTA5NDQ1OVowSDELMAkGA1UEBhMCREUxFTATBgNV BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDEgMjAyMDB2MBAG ByqGSM49AgEGBSuBBAAiA2IABMbLxyjR+4T1mu9CFCDhQ2tuda38KwOE1HaTJddZO0Flax7mNCq7 dPYSzuht56vkPE4/RAiLzRZxy7+SmfSk1zxQVFKQhYN4lGdnoxwJGT11NIXe7WB9xwy0QVK5buXu QqOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHOREKv/VbNafAkl1bK6CKBrqx9t MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu bmV0L2NybC9kLXRydXN0X2JyX3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwQlIlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD AwNpADBmAjEAlJAtE/rhY/hhY+ithXhUkZy4kzg+GkHaQBZTQgjKL47xPoFWwKrY7RjEsK70Pvom AjEA8yjixtsrmfu3Ubgko6SUeho/5jbiA1czijDLgsfWFBHVdWNbFJWcHwHP2NVypw87 -----END CERTIFICATE----- D-TRUST EV Root CA 1 2020 ========================= -----BEGIN CERTIFICATE----- MIIC2zCCAmCgAwIBAgIQXwJB13qHfEwDo6yWjfv/0DAKBggqhkjOPQQDAzBIMQswCQYDVQQGEwJE RTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0EgMSAy MDIwMB4XDTIwMDIxMTEwMDAwMFoXDTM1MDIxMTA5NTk1OVowSDELMAkGA1UEBhMCREUxFTATBgNV BAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDEgMjAyMDB2MBAG ByqGSM49AgEGBSuBBAAiA2IABPEL3YZDIBnfl4XoIkqbz52Yv7QFJsnL46bSj8WeeHsxiamJrSc8 ZRCC/N/DnU7wMyPE0jL1HLDfMxddxfCxivnvubcUyilKwg+pf3VlSSowZ/Rk99Yad9rDwpdhQntJ raOCAQ0wggEJMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFH8QARY3OqQo5FD4pPfsazK2/umL MA4GA1UdDwEB/wQEAwIBBjCBxgYDVR0fBIG+MIG7MD6gPKA6hjhodHRwOi8vY3JsLmQtdHJ1c3Qu bmV0L2NybC9kLXRydXN0X2V2X3Jvb3RfY2FfMV8yMDIwLmNybDB5oHegdYZzbGRhcDovL2RpcmVj dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwRVYlMjBSb290JTIwQ0ElMjAxJTIwMjAyMCxP PUQtVHJ1c3QlMjBHbWJILEM9REU/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDAKBggqhkjOPQQD AwNpADBmAjEAyjzGKnXCXnViOTYAYFqLwZOZzNnbQTs7h5kXO9XMT8oi96CAy/m0sRtW9XLS/BnR AjEAkfcwkz8QRitxpNA7RJvAKQIFskF3UfN5Wp6OFKBOQtJbgfM0agPnIjhQW+0ZT0MW -----END CERTIFICATE----- DigiCert TLS ECC P384 Root G5 ============================= -----BEGIN CERTIFICATE----- MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURpZ2lDZXJ0IFRMUyBFQ0MgUDM4 NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMx FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQg Um9vdCBHNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1Tzvd lHJS7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp0zVozptj n4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICISB4CIfBFqMA4GA1UdDwEB /wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMDA2gAMGUCMQCJao1H5+z8blUD2Wds Jk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQLgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIx AJSdYsiJvRmEFOml+wG4DXZDjC5Ty3zfDBeWUA== -----END CERTIFICATE----- DigiCert TLS RSA4096 Root G5 ============================ -----BEGIN CERTIFICATE----- MIIFZjCCA06gAwIBAgIQCPm0eKj6ftpqMzeJ3nzPijANBgkqhkiG9w0BAQwFADBNMQswCQYDVQQG EwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0 MDk2IFJvb3QgRzUwHhcNMjEwMTE1MDAwMDAwWhcNNDYwMTE0MjM1OTU5WjBNMQswCQYDVQQGEwJV UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJTAjBgNVBAMTHERpZ2lDZXJ0IFRMUyBSU0E0MDk2 IFJvb3QgRzUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCz0PTJeRGd/fxmgefM1eS8 7IE+ajWOLrfn3q/5B03PMJ3qCQuZvWxX2hhKuHisOjmopkisLnLlvevxGs3npAOpPxG02C+JFvuU AT27L/gTBaF4HI4o4EXgg/RZG5Wzrn4DReW+wkL+7vI8toUTmDKdFqgpwgscONyfMXdcvyej/Ces tyu9dJsXLfKB2l2w4SMXPohKEiPQ6s+d3gMXsUJKoBZMpG2T6T867jp8nVid9E6P/DsjyG244gXa zOvswzH016cpVIDPRFtMbzCe88zdH5RDnU1/cHAN1DrRN/BsnZvAFJNY781BOHW8EwOVfH/jXOnV DdXifBBiqmvwPXbzP6PosMH976pXTayGpxi0KcEsDr9kvimM2AItzVwv8n/vFfQMFawKsPHTDU9q TXeXAaDxZre3zu/O7Oyldcqs4+Fj97ihBMi8ez9dLRYiVu1ISf6nL3kwJZu6ay0/nTvEF+cdLvvy z6b84xQslpghjLSR6Rlgg/IwKwZzUNWYOwbpx4oMYIwo+FKbbuH2TbsGJJvXKyY//SovcfXWJL5/ MZ4PbeiPT02jP/816t9JXkGPhvnxd3lLG7SjXi/7RgLQZhNeXoVPzthwiHvOAbWWl9fNff2C+MIk wcoBOU+NosEUQB+cZtUMCUbW8tDRSHZWOkPLtgoRObqME2wGtZ7P6wIDAQABo0IwQDAdBgNVHQ4E FgQUUTMc7TZArxfTJc1paPKvTiM+s0EwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8w DQYJKoZIhvcNAQEMBQADggIBAGCmr1tfV9qJ20tQqcQjNSH/0GEwhJG3PxDPJY7Jv0Y02cEhJhxw GXIeo8mH/qlDZJY6yFMECrZBu8RHANmfGBg7sg7zNOok992vIGCukihfNudd5N7HPNtQOa27PShN lnx2xlv0wdsUpasZYgcYQF+Xkdycx6u1UQ3maVNVzDl92sURVXLFO4uJ+DQtpBflF+aZfTCIITfN MBc9uPK8qHWgQ9w+iUuQrm0D4ByjoJYJu32jtyoQREtGBzRj7TG5BO6jm5qu5jF49OokYTurWGT/ u4cnYiWB39yhL/btp/96j1EuMPikAdKFOV8BmZZvWltwGUb+hmA+rYAQCd05JS9Yf7vSdPD3Rh9G OUrYU9DzLjtxpdRv/PNn5AeP3SYZ4Y1b+qOTEZvpyDrDVWiakuFSdjjo4bq9+0/V77PnSIMx8IIh 47a+p6tv75/fTM8BuGJqIz3nCU2AG3swpMPdB380vqQmsvZB6Akd4yCYqjdP//fx4ilwMUc/dNAU FvohigLVigmUdy7yWSiLfFCSCmZ4OIN1xLVaqBHG5cGdZlXPU8Sv13WFqUITVuwhd4GTWgzqltlJ yqEI8pc7bZsEGCREjnwB8twl2F6GmrE52/WRMmrRpnCKovfepEWFJqgejF0pW8hL2JpqA15w8oVP bEtoL8pU9ozaMv7Da4M/OMZ+ -----END CERTIFICATE----- Certainly Root R1 ================= -----BEGIN CERTIFICATE----- MIIFRzCCAy+gAwIBAgIRAI4P+UuQcWhlM1T01EQ5t+AwDQYJKoZIhvcNAQELBQAwPTELMAkGA1UE BhMCVVMxEjAQBgNVBAoTCUNlcnRhaW5seTEaMBgGA1UEAxMRQ2VydGFpbmx5IFJvb3QgUjEwHhcN MjEwNDAxMDAwMDAwWhcNNDYwNDAxMDAwMDAwWjA9MQswCQYDVQQGEwJVUzESMBAGA1UEChMJQ2Vy dGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBSMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBANA21B/q3avk0bbm+yLA3RMNansiExyXPGhjZjKcA7WNpIGD2ngwEc/csiu+kr+O 5MQTvqRoTNoCaBZ0vrLdBORrKt03H2As2/X3oXyVtwxwhi7xOu9S98zTm/mLvg7fMbedaFySpvXl 8wo0tf97ouSHocavFwDvA5HtqRxOcT3Si2yJ9HiG5mpJoM610rCrm/b01C7jcvk2xusVtyWMOvwl DbMicyF0yEqWYZL1LwsYpfSt4u5BvQF5+paMjRcCMLT5r3gajLQ2EBAHBXDQ9DGQilHFhiZ5shGI XsXwClTNSaa/ApzSRKft43jvRl5tcdF5cBxGX1HpyTfcX35pe0HfNEXgO4T0oYoKNp43zGJS4YkN KPl6I7ENPT2a/Z2B7yyQwHtETrtJ4A5KVpK8y7XdeReJkd5hiXSSqOMyhb5OhaRLWcsrxXiOcVTQ AjeZjOVJ6uBUcqQRBi8LjMFbvrWhsFNunLhgkR9Za/kt9JQKl7XsxXYDVBtlUrpMklZRNaBA2Cnb rlJ2Oy0wQJuK0EJWtLeIAaSHO1OWzaMWj/Nmqhexx2DgwUMFDO6bW2BvBlyHWyf5QBGenDPBt+U1 VwV/J84XIIwc/PH72jEpSe31C4SnT8H2TsIonPru4K8H+zMReiFPCyEQtkA6qyI6BJyLm4SGcprS p6XEtHWRqSsjAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud DgQWBBTgqj8ljZ9EXME66C6ud0yEPmcM9DANBgkqhkiG9w0BAQsFAAOCAgEAuVevuBLaV4OPaAsz HQNTVfSVcOQrPbA56/qJYv331hgELyE03fFo8NWWWt7CgKPBjcZq91l3rhVkz1t5BXdm6ozTaw3d 8VkswTOlMIAVRQdFGjEitpIAq5lNOo93r6kiyi9jyhXWx8bwPWz8HA2YEGGeEaIi1wrykXprOQ4v MMM2SZ/g6Q8CRFA3lFV96p/2O7qUpUzpvD5RtOjKkjZUbVwlKNrdrRT90+7iIgXr0PK3aBLXWopB GsaSpVo7Y0VPv+E6dyIvXL9G+VoDhRNCX8reU9ditaY1BMJH/5n9hN9czulegChB8n3nHpDYT3Y+ gjwN/KUD+nsa2UUeYNrEjvn8K8l7lcUq/6qJ34IxD3L/DCfXCh5WAFAeDJDBlrXYFIW7pw0WwfgH JBu6haEaBQmAupVjyTrsJZ9/nbqkRxWbRHDxakvWOF5D8xh+UG7pWijmZeZ3Gzr9Hb4DJqPb1OG7 fpYnKx3upPvaJVQTA945xsMfTZDsjxtK0hzthZU4UHlG1sGQUDGpXJpuHfUzVounmdLyyCwzk5Iw x06MZTMQZBf9JBeW0Y3COmor6xOLRPIh80oat3df1+2IpHLlOR+Vnb5nwXARPbv0+Em34yaXOp/S X3z7wJl8OSngex2/DaeP0ik0biQVy96QXr8axGbqwua6OV+KmalBWQewLK8= -----END CERTIFICATE----- Certainly Root E1 ================= -----BEGIN CERTIFICATE----- MIIB9zCCAX2gAwIBAgIQBiUzsUcDMydc+Y2aub/M+DAKBggqhkjOPQQDAzA9MQswCQYDVQQGEwJV UzESMBAGA1UEChMJQ2VydGFpbmx5MRowGAYDVQQDExFDZXJ0YWlubHkgUm9vdCBFMTAeFw0yMTA0 MDEwMDAwMDBaFw00NjA0MDEwMDAwMDBaMD0xCzAJBgNVBAYTAlVTMRIwEAYDVQQKEwlDZXJ0YWlu bHkxGjAYBgNVBAMTEUNlcnRhaW5seSBSb290IEUxMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE3m/4 fxzf7flHh4axpMCK+IKXgOqPyEpeKn2IaKcBYhSRJHpcnqMXfYqGITQYUBsQ3tA3SybHGWCA6TS9 YBk2QNYphwk8kXr2vBMj3VlOBF7PyAIcGFPBMdjaIOlEjeR2o0IwQDAOBgNVHQ8BAf8EBAMCAQYw DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU8ygYy2R17ikq6+2uI1g4hevIIgcwCgYIKoZIzj0E AwMDaAAwZQIxALGOWiDDshliTd6wT99u0nCK8Z9+aozmut6Dacpps6kFtZaSF4fC0urQe87YQVt8 rgIwRt7qy12a7DLCZRawTDBcMPPaTnOGBtjOiQRINzf43TNRnXCve1XYAS59BWQOhriR -----END CERTIFICATE----- Security Communication ECC RootCA1 ================================== -----BEGIN CERTIFICATE----- MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo 5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e N9k= -----END CERTIFICATE----- BJCA Global Root CA1 ==================== -----BEGIN CERTIFICATE----- MIIFdDCCA1ygAwIBAgIQVW9l47TZkGobCdFsPsBsIDANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQG EwJDTjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJK Q0EgR2xvYmFsIFJvb3QgQ0ExMB4XDTE5MTIxOTAzMTYxN1oXDTQ0MTIxMjAzMTYxN1owVDELMAkG A1UEBhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQD DBRCSkNBIEdsb2JhbCBSb290IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPFm CL3ZxRVhy4QEQaVpN3cdwbB7+sN3SJATcmTRuHyQNZ0YeYjjlwE8R4HyDqKYDZ4/N+AZspDyRhyS sTphzvq3Rp4Dhtczbu33RYx2N95ulpH3134rhxfVizXuhJFyV9xgw8O558dnJCNPYwpj9mZ9S1Wn P3hkSWkSl+BMDdMJoDIwOvqfwPKcxRIqLhy1BDPapDgRat7GGPZHOiJBhyL8xIkoVNiMpTAK+BcW yqw3/XmnkRd4OJmtWO2y3syJfQOcs4ll5+M7sSKGjwZteAf9kRJ/sGsciQ35uMt0WwfCyPQ10WRj eulumijWML3mG90Vr4TqnMfK9Q7q8l0ph49pczm+LiRvRSGsxdRpJQaDrXpIhRMsDQa4bHlW/KNn MoH1V6XKV0Jp6VwkYe/iMBhORJhVb3rCk9gZtt58R4oRTklH2yiUAguUSiz5EtBP6DF+bHq/pj+b OT0CFqMYs2esWz8sgytnOYFcuX6U1WTdno9uruh8W7TXakdI136z1C2OVnZOz2nxbkRs1CTqjSSh GL+9V/6pmTW12xB3uD1IutbB5/EjPtffhZ0nPNRAvQoMvfXnjSXWgXSHRtQpdaJCbPdzied9v3pK H9MiyRVVz99vfFXQpIsHETdfg6YmV6YBW37+WGgHqel62bno/1Afq8K0wM7o6v0PvY1NuLxxAgMB AAGjQjBAMB0GA1UdDgQWBBTF7+3M2I0hxkjk49cULqcWk+WYATAPBgNVHRMBAf8EBTADAQH/MA4G A1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAUoKsITQfI/Ki2Pm4rzc2IInRNwPWaZ+4 YRC6ojGYWUfo0Q0lHhVBDOAqVdVXUsv45Mdpox1NcQJeXyFFYEhcCY5JEMEE3KliawLwQ8hOnThJ dMkycFRtwUf8jrQ2ntScvd0g1lPJGKm1Vrl2i5VnZu69mP6u775u+2D2/VnGKhs/I0qUJDAnyIm8 60Qkmss9vk/Ves6OF8tiwdneHg56/0OGNFK8YT88X7vZdrRTvJez/opMEi4r89fO4aL/3Xtw+zuh TaRjAv04l5U/BXCga99igUOLtFkNSoxUnMW7gZ/NfaXvCyUeOiDbHPwfmGcCCtRzRBPbUYQaVQNW 4AB+dAb/OMRyHdOoP2gxXdMJxy6MW2Pg6Nwe0uxhHvLe5e/2mXZgLR6UcnHGCyoyx5JO1UbXHfmp GQrI+pXObSOYqgs4rZpWDW+N8TEAiMEXnM0ZNjX+VVOg4DwzX5Ze4jLp3zO7Bkqp2IRzznfSxqxx 4VyjHQy7Ct9f4qNx2No3WqB4K/TUfet27fJhcKVlmtOJNBir+3I+17Q9eVzYH6Eze9mCUAyTF6ps 3MKCuwJXNq+YJyo5UOGwifUll35HaBC07HPKs5fRJNz2YqAo07WjuGS3iGJCz51TzZm+ZGiPTx4S SPfSKcOYKMryMguTjClPPGAyzQWWYezyr/6zcCwupvI= -----END CERTIFICATE----- BJCA Global Root CA2 ==================== -----BEGIN CERTIFICATE----- MIICJTCCAaugAwIBAgIQLBcIfWQqwP6FGFkGz7RK6zAKBggqhkjOPQQDAzBUMQswCQYDVQQGEwJD TjEmMCQGA1UECgwdQkVJSklORyBDRVJUSUZJQ0FURSBBVVRIT1JJVFkxHTAbBgNVBAMMFEJKQ0Eg R2xvYmFsIFJvb3QgQ0EyMB4XDTE5MTIxOTAzMTgyMVoXDTQ0MTIxMjAzMTgyMVowVDELMAkGA1UE BhMCQ04xJjAkBgNVBAoMHUJFSUpJTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZMR0wGwYDVQQDDBRC SkNBIEdsb2JhbCBSb290IENBMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABJ3LgJGNU2e1uVCxA/jl SR9BIgmwUVJY1is0j8USRhTFiy8shP8sbqjV8QnjAyEUxEM9fMEsxEtqSs3ph+B99iK++kpRuDCK /eHeGBIK9ke35xe/J4rUQUyWPGCWwf0VHKNCMEAwHQYDVR0OBBYEFNJKsVF/BvDRgh9Obl+rg/xI 1LCRMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMDA2gAMGUCMBq8 W9f+qdJUDkpd0m2xQNz0Q9XSSpkZElaA94M04TVOSG0ED1cxMDAtsaqdAzjbBgIxAMvMh1PLet8g UXOQwKhbYdDFUDn9hf7B43j4ptZLvZuHjw/l1lOWqzzIQNph91Oj9w== -----END CERTIFICATE----- Sectigo Public Server Authentication Root E46 ============================================= -----BEGIN CERTIFICATE----- MIICOjCCAcGgAwIBAgIQQvLM2htpN0RfFf51KBC49DAKBggqhkjOPQQDAzBfMQswCQYDVQQGEwJH QjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBTZXJ2 ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1OTU5 WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0 aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBFNDYwdjAQBgcqhkjOPQIBBgUr gQQAIgNiAAR2+pmpbiDt+dd34wc7qNs9Xzjoq1WmVk/WSOrsfy2qw7LFeeyZYX8QeccCWvkEN/U0 NSt3zn8gj1KjAIns1aeibVvjS5KToID1AZTc8GgHHs3u/iVStSBDHBv+6xnOQ6OjQjBAMB0GA1Ud DgQWBBTRItpMWfFLXyY4qp3W7usNw/upYTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB /zAKBggqhkjOPQQDAwNnADBkAjAn7qRaqCG76UeXlImldCBteU/IvZNeWBj7LRoAasm4PdCkT0RH lAFWovgzJQxC36oCMB3q4S6ILuH5px0CMk7yn2xVdOOurvulGu7t0vzCAxHrRVxgED1cf5kDW21U SAGKcw== -----END CERTIFICATE----- Sectigo Public Server Authentication Root R46 ============================================= -----BEGIN CERTIFICATE----- MIIFijCCA3KgAwIBAgIQdY39i658BwD6qSWn4cetFDANBgkqhkiG9w0BAQwFADBfMQswCQYDVQQG EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1TZWN0aWdvIFB1YmxpYyBT ZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNNDYwMzIxMjM1 OTU5WjBfMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTYwNAYDVQQDEy1T ZWN0aWdvIFB1YmxpYyBTZXJ2ZXIgQXV0aGVudGljYXRpb24gUm9vdCBSNDYwggIiMA0GCSqGSIb3 DQEBAQUAA4ICDwAwggIKAoICAQCTvtU2UnXYASOgHEdCSe5jtrch/cSV1UgrJnwUUxDaef0rty2k 1Cz66jLdScK5vQ9IPXtamFSvnl0xdE8H/FAh3aTPaE8bEmNtJZlMKpnzSDBh+oF8HqcIStw+Kxwf GExxqjWMrfhu6DtK2eWUAtaJhBOqbchPM8xQljeSM9xfiOefVNlI8JhD1mb9nxc4Q8UBUQvX4yMP FF1bFOdLvt30yNoDN9HWOaEhUTCDsG3XME6WW5HwcCSrv0WBZEMNvSE6Lzzpng3LILVCJ8zab5vu ZDCQOc2TZYEhMbUjUDM3IuM47fgxMMxF/mL50V0yeUKH32rMVhlATc6qu/m1dkmU8Sf4kaWD5Qaz Yw6A3OASVYCmO2a0OYctyPDQ0RTp5A1NDvZdV3LFOxxHVp3i1fuBYYzMTYCQNFu31xR13NgESJ/A wSiItOkcyqex8Va3e0lMWeUgFaiEAin6OJRpmkkGj80feRQXEgyDet4fsZfu+Zd4KKTIRJLpfSYF plhym3kT2BFfrsU4YjRosoYwjviQYZ4ybPUHNs2iTG7sijbt8uaZFURww3y8nDnAtOFr94MlI1fZ EoDlSfB1D++N6xybVCi0ITz8fAr/73trdf+LHaAZBav6+CuBQug4urv7qv094PPK306Xlynt8xhW 6aWWrL3DkJiy4Pmi1KZHQ3xtzwIDAQABo0IwQDAdBgNVHQ4EFgQUVnNYZJX5khqwEioEYnmhQBWI IUkwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAC9c mTz8Bl6MlC5w6tIyMY208FHVvArzZJ8HXtXBc2hkeqK5Duj5XYUtqDdFqij0lgVQYKlJfp/imTYp E0RHap1VIDzYm/EDMrraQKFz6oOht0SmDpkBm+S8f74TlH7Kph52gDY9hAaLMyZlbcp+nv4fjFg4 exqDsQ+8FxG75gbMY/qB8oFM2gsQa6H61SilzwZAFv97fRheORKkU55+MkIQpiGRqRxOF3yEvJ+M 0ejf5lG5Nkc/kLnHvALcWxxPDkjBJYOcCj+esQMzEhonrPcibCTRAUH4WAP+JWgiH5paPHxsnnVI 84HxZmduTILA7rpXDhjvLpr3Etiga+kFpaHpaPi8TD8SHkXoUsCjvxInebnMMTzD9joiFgOgyY9m pFuiTdaBJQbpdqQACj7LzTWb4OE4y2BThihCQRxEV+ioratF4yUQvNs+ZUH7G6aXD+u5dHn5Hrwd Vw1Hr8Mvn4dGp+smWg9WY7ViYG4A++MnESLn/pmPNPW56MORcr3Ywx65LvKRRFHQV80MNNVIIb/b E/FmJUNS0nAiNs2fxBx1IK1jcmMGDw4nztJqDby1ORrp0XZ60Vzk50lJLVU3aPAaOpg+VBeHVOmm J1CJeyAvP/+/oYtKR5j/K3tJPsMpRmAYQqszKbrAKbkTidOIijlBO8n9pu0f9GBj39ItVQGL -----END CERTIFICATE----- SSL.com TLS RSA Root CA 2022 ============================ -----BEGIN CERTIFICATE----- MIIFiTCCA3GgAwIBAgIQb77arXO9CEDii02+1PdbkTANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQG EwJVUzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBSU0Eg Um9vdCBDQSAyMDIyMB4XDTIyMDgyNTE2MzQyMloXDTQ2MDgxOTE2MzQyMVowTjELMAkGA1UEBhMC VVMxGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgUlNBIFJv b3QgQ0EgMjAyMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANCkCXJPQIgSYT41I57u 9nTPL3tYPc48DRAokC+X94xI2KDYJbFMsBFMF3NQ0CJKY7uB0ylu1bUJPiYYf7ISf5OYt6/wNr/y 7hienDtSxUcZXXTzZGbVXcdotL8bHAajvI9AI7YexoS9UcQbOcGV0insS657Lb85/bRi3pZ7Qcac oOAGcvvwB5cJOYF0r/c0WRFXCsJbwST0MXMwgsadugL3PnxEX4MN8/HdIGkWCVDi1FW24IBydm5M R7d1VVm0U3TZlMZBrViKMWYPHqIbKUBOL9975hYsLfy/7PO0+r4Y9ptJ1O4Fbtk085zx7AGL0SDG D6C1vBdOSHtRwvzpXGk3R2azaPgVKPC506QVzFpPulJwoxJF3ca6TvvC0PeoUidtbnm1jPx7jMEW TO6Af77wdr5BUxIzrlo4QqvXDz5BjXYHMtWrifZOZ9mxQnUjbvPNQrL8VfVThxc7wDNY8VLS+YCk 8OjwO4s4zKTGkH8PnP2L0aPP2oOnaclQNtVcBdIKQXTbYxE3waWglksejBYSd66UNHsef8JmAOSq g+qKkK3ONkRN0VHpvB/zagX9wHQfJRlAUW7qglFA35u5CCoGAtUjHBPW6dvbxrB6y3snm/vg1UYk 7RBLY0ulBY+6uB0rpvqR4pJSvezrZ5dtmi2fgTIFZzL7SAg/2SW4BCUvAgMBAAGjYzBhMA8GA1Ud EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU+y437uOEeicuzRk1sTN8/9REQrkwHQYDVR0OBBYEFPsu N+7jhHonLs0ZNbEzfP/UREK5MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAjYlt hEUY8U+zoO9opMAdrDC8Z2awms22qyIZZtM7QbUQnRC6cm4pJCAcAZli05bg4vsMQtfhWsSWTVTN j8pDU/0quOr4ZcoBwq1gaAafORpR2eCNJvkLTqVTJXojpBzOCBvfR4iyrT7gJ4eLSYwfqUdYe5by iB0YrrPRpgqU+tvT5TgKa3kSM/tKWTcWQA673vWJDPFs0/dRa1419dvAJuoSc06pkZCmF8NsLzjU o3KUQyxi4U5cMj29TH0ZR6LDSeeWP4+a0zvkEdiLA9z2tmBVGKaBUfPhqBVq6+AL8BQx1rmMRTqo ENjwuSfr98t67wVylrXEj5ZzxOhWc5y8aVFjvO9nHEMaX3cZHxj4HCUp+UmZKbaSPaKDN7Egkaib MOlqbLQjk2UEqxHzDh1TJElTHaE/nUiSEeJ9DU/1172iWD54nR4fK/4huxoTtrEoZP2wAgDHbICi vRZQIA9ygV/MlP+7mea6kMvq+cYMwq7FGc4zoWtcu358NFcXrfA/rs3qr5nsLFR+jM4uElZI7xc7 P0peYNLcdDa8pUNjyw9bowJWCZ4kLOGGgYz+qxcs+sjiMho6/4UIyYOf8kpIEFR3N+2ivEC+5BB0 9+Rbu7nzifmPQdjH5FCQNYA+HLhNkNPU98OwoX6EyneSMSy4kLGCenROmxMmtNVQZlR4rmA= -----END CERTIFICATE----- SSL.com TLS ECC Root CA 2022 ============================ -----BEGIN CERTIFICATE----- MIICOjCCAcCgAwIBAgIQFAP1q/s3ixdAW+JDsqXRxDAKBggqhkjOPQQDAzBOMQswCQYDVQQGEwJV UzEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMSUwIwYDVQQDDBxTU0wuY29tIFRMUyBFQ0MgUm9v dCBDQSAyMDIyMB4XDTIyMDgyNTE2MzM0OFoXDTQ2MDgxOTE2MzM0N1owTjELMAkGA1UEBhMCVVMx GDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjElMCMGA1UEAwwcU1NMLmNvbSBUTFMgRUNDIFJvb3Qg Q0EgMjAyMjB2MBAGByqGSM49AgEGBSuBBAAiA2IABEUpNXP6wrgjzhR9qLFNoFs27iosU8NgCTWy JGYmacCzldZdkkAZDsalE3D07xJRKF3nzL35PIXBz5SQySvOkkJYWWf9lCcQZIxPBLFNSeR7T5v1 5wj4A4j3p8OSSxlUgaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSJjy+j6CugFFR7 81a4Jl9nOAuc0DAdBgNVHQ4EFgQUiY8vo+groBRUe/NWuCZfZzgLnNAwDgYDVR0PAQH/BAQDAgGG MAoGCCqGSM49BAMDA2gAMGUCMFXjIlbp15IkWE8elDIPDAI2wv2sdDJO4fscgIijzPvX6yv/N33w 7deedWo1dlJF4AIxAMeNb0Igj762TVntd00pxCAgRWSGOlDGxK0tk/UYfXLtqc/ErFc2KAhl3zx5 Zn6g6g== -----END CERTIFICATE----- Atos TrustedRoot Root CA ECC TLS 2021 ===================================== -----BEGIN CERTIFICATE----- MIICFTCCAZugAwIBAgIQPZg7pmY9kGP3fiZXOATvADAKBggqhkjOPQQDAzBMMS4wLAYDVQQDDCVB dG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgRUNDIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQswCQYD VQQGEwJERTAeFw0yMTA0MjIwOTI2MjNaFw00MTA0MTcwOTI2MjJaMEwxLjAsBgNVBAMMJUF0b3Mg VHJ1c3RlZFJvb3QgUm9vdCBDQSBFQ0MgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNVBAYT AkRFMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEloZYKDcKZ9Cg3iQZGeHkBQcfl+3oZIK59sRxUM6K DP/XtXa7oWyTbIOiaG6l2b4siJVBzV3dscqDY4PMwL502eCdpO5KTlbgmClBk1IQ1SQ4AjJn8ZQS b+/Xxd4u/RmAo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBR2KCXWfeBmmnoJsmo7jjPX NtNPojAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwW5kp85wxtolrbNa9d+F851F+ uDrNozZffPc8dz7kUK2o59JZDCaOMDtuCCrCp1rIAjEAmeMM56PDr9NJLkaCI2ZdyQAUEv049OGY a3cpetskz2VAv9LcjBHo9H1/IISpQuQo -----END CERTIFICATE----- Atos TrustedRoot Root CA RSA TLS 2021 ===================================== -----BEGIN CERTIFICATE----- MIIFZDCCA0ygAwIBAgIQU9XP5hmTC/srBRLYwiqipDANBgkqhkiG9w0BAQwFADBMMS4wLAYDVQQD DCVBdG9zIFRydXN0ZWRSb290IFJvb3QgQ0EgUlNBIFRMUyAyMDIxMQ0wCwYDVQQKDARBdG9zMQsw CQYDVQQGEwJERTAeFw0yMTA0MjIwOTIxMTBaFw00MTA0MTcwOTIxMDlaMEwxLjAsBgNVBAMMJUF0 b3MgVHJ1c3RlZFJvb3QgUm9vdCBDQSBSU0EgVExTIDIwMjExDTALBgNVBAoMBEF0b3MxCzAJBgNV BAYTAkRFMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtoAOxHm9BYx9sKOdTSJNy/BB l01Z4NH+VoyX8te9j2y3I49f1cTYQcvyAh5x5en2XssIKl4w8i1mx4QbZFc4nXUtVsYvYe+W/CBG vevUez8/fEc4BKkbqlLfEzfTFRVOvV98r61jx3ncCHvVoOX3W3WsgFWZkmGbzSoXfduP9LVq6hdK ZChmFSlsAvFr1bqjM9xaZ6cF4r9lthawEO3NUDPJcFDsGY6wx/J0W2tExn2WuZgIWWbeKQGb9Cpt 0xU6kGpn8bRrZtkh68rZYnxGEFzedUlnnkL5/nWpo63/dgpnQOPF943HhZpZnmKaau1Fh5hnstVK PNe0OwANwI8f4UDErmwh3El+fsqyjW22v5MvoVw+j8rtgI5Y4dtXz4U2OLJxpAmMkokIiEjxQGMY sluMWuPD0xeqqxmjLBvk1cbiZnrXghmmOxYsL3GHX0WelXOTwkKBIROW1527k2gV+p2kHYzygeBY Br3JtuP2iV2J+axEoctr+hbxx1A9JNr3w+SH1VbxT5Aw+kUJWdo0zuATHAR8ANSbhqRAvNncTFd+ rrcztl524WWLZt+NyteYr842mIycg5kDcPOvdO3GDjbnvezBc6eUWsuSZIKmAMFwoW4sKeFYV+xa fJlrJaSQOoD0IJ2azsct+bJLKZWD6TWNp0lIpw9MGZHQ9b8Q4HECAwEAAaNCMEAwDwYDVR0TAQH/ BAUwAwEB/zAdBgNVHQ4EFgQUdEmZ0f+0emhFdcN+tNzMzjkz2ggwDgYDVR0PAQH/BAQDAgGGMA0G CSqGSIb3DQEBDAUAA4ICAQAjQ1MkYlxt/T7Cz1UAbMVWiLkO3TriJQ2VSpfKgInuKs1l+NsW4AmS 4BjHeJi78+xCUvuppILXTdiK/ORO/auQxDh1MoSf/7OwKwIzNsAQkG8dnK/haZPso0UvFJ/1TCpl Q3IM98P4lYsU84UgYt1UU90s3BiVaU+DR3BAM1h3Egyi61IxHkzJqM7F78PRreBrAwA0JrRUITWX AdxfG/F851X6LWh3e9NpzNMOa7pNdkTWwhWaJuywxfW70Xp0wmzNxbVe9kzmWy2B27O3Opee7c9G slA9hGCZcbUztVdF5kJHdWoOsAgMrr3e97sPWD2PAzHoPYJQyi9eDF20l74gNAf0xBLh7tew2Vkt afcxBPTy+av5EzH4AXcOPUIjJsyacmdRIXrMPIWo6iFqO9taPKU0nprALN+AnCng33eU0aKAQv9q TFsR0PXNor6uzFFcw9VUewyu1rkGd4Di7wcaaMxZUa1+XGdrudviB0JbuAEFWDlN5LuYo7Ey7Nmj 1m+UI/87tyll5gfp77YZ6ufCOB0yiJA8EytuzO+rdwY0d4RPcuSBhPm5dDTedk+SKlOxJTnbPP/l PqYO5Wue/9vsL3SD3460s6neFE3/MaNFcyT6lSnMEpcEoji2jbDwN/zIIX8/syQbPYtuzE2wFg2W HYMfRsCbvUOZ58SWLs5fyQ== -----END CERTIFICATE----- TrustAsia Global Root CA G3 =========================== -----BEGIN CERTIFICATE----- MIIFpTCCA42gAwIBAgIUZPYOZXdhaqs7tOqFhLuxibhxkw8wDQYJKoZIhvcNAQEMBQAwWjELMAkG A1UEBhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMM G1RydXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHMzAeFw0yMTA1MjAwMjEwMTlaFw00NjA1MTkwMjEw MTlaMFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMu MSQwIgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzMwggIiMA0GCSqGSIb3DQEBAQUA A4ICDwAwggIKAoICAQDAMYJhkuSUGwoqZdC+BqmHO1ES6nBBruL7dOoKjbmzTNyPtxNST1QY4Sxz lZHFZjtqz6xjbYdT8PfxObegQ2OwxANdV6nnRM7EoYNl9lA+sX4WuDqKAtCWHwDNBSHvBm3dIZwZ Q0WhxeiAysKtQGIXBsaqvPPW5vxQfmZCHzyLpnl5hkA1nyDvP+uLRx+PjsXUjrYsyUQE49RDdT/V P68czH5GX6zfZBCK70bwkPAPLfSIC7Epqq+FqklYqL9joDiR5rPmd2jE+SoZhLsO4fWvieylL1Ag dB4SQXMeJNnKziyhWTXAyB1GJ2Faj/lN03J5Zh6fFZAhLf3ti1ZwA0pJPn9pMRJpxx5cynoTi+jm 9WAPzJMshH/x/Gr8m0ed262IPfN2dTPXS6TIi/n1Q1hPy8gDVI+lhXgEGvNz8teHHUGf59gXzhqc D0r83ERoVGjiQTz+LISGNzzNPy+i2+f3VANfWdP3kXjHi3dqFuVJhZBFcnAvkV34PmVACxmZySYg WmjBNb9Pp1Hx2BErW+Canig7CjoKH8GB5S7wprlppYiU5msTf9FkPz2ccEblooV7WIQn3MSAPmea mseaMQ4w7OYXQJXZRe0Blqq/DPNL0WP3E1jAuPP6Z92bfW1K/zJMtSU7/xxnD4UiWQWRkUF3gdCF TIcQcf+eQxuulXUtgQIDAQABo2MwYTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEDk5PIj 7zjKsK5Xf/IhMBY027ySMB0GA1UdDgQWBBRA5OTyI+84yrCuV3/yITAWNNu8kjAOBgNVHQ8BAf8E BAMCAQYwDQYJKoZIhvcNAQEMBQADggIBACY7UeFNOPMyGLS0XuFlXsSUT9SnYaP4wM8zAQLpw6o1 D/GUE3d3NZ4tVlFEbuHGLige/9rsR82XRBf34EzC4Xx8MnpmyFq2XFNFV1pF1AWZLy4jVe5jaN/T G3inEpQGAHUNcoTpLrxaatXeL1nHo+zSh2bbt1S1JKv0Q3jbSwTEb93mPmY+KfJLaHEih6D4sTNj duMNhXJEIlU/HHzp/LgV6FL6qj6jITk1dImmasI5+njPtqzn59ZW/yOSLlALqbUHM/Q4X6RJpstl cHboCoWASzY9M/eVVHUl2qzEc4Jl6VL1XP04lQJqaTDFHApXB64ipCz5xUG3uOyfT0gA+QEEVcys +TIxxHWVBqB/0Y0n3bOppHKH/lmLmnp0Ft0WpWIp6zqW3IunaFnT63eROfjXy9mPX1onAX1daBli 2MjN9LdyR75bl87yraKZk62Uy5P2EgmVtqvXO9A/EcswFi55gORngS1d7XB4tmBZrOFdRWOPyN9y aFvqHbgB8X7754qz41SgOAngPN5C8sLtLpvzHzW2NtjjgKGLzZlkD8Kqq7HK9W+eQ42EVJmzbsAS ZthwEPEGNTNDqJwuuhQxzhB/HIbjj9LV+Hfsm6vxL2PZQl/gZ4FkkfGXL/xuJvYz+NO1+MRiqzFR JQJ6+N1rZdVtTTDIZbpoFGWsJwt0ivKH -----END CERTIFICATE----- TrustAsia Global Root CA G4 =========================== -----BEGIN CERTIFICATE----- MIICVTCCAdygAwIBAgIUTyNkuI6XY57GU4HBdk7LKnQV1tcwCgYIKoZIzj0EAwMwWjELMAkGA1UE BhMCQ04xJTAjBgNVBAoMHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xJDAiBgNVBAMMG1Ry dXN0QXNpYSBHbG9iYWwgUm9vdCBDQSBHNDAeFw0yMTA1MjAwMjEwMjJaFw00NjA1MTkwMjEwMjJa MFoxCzAJBgNVBAYTAkNOMSUwIwYDVQQKDBxUcnVzdEFzaWEgVGVjaG5vbG9naWVzLCBJbmMuMSQw IgYDVQQDDBtUcnVzdEFzaWEgR2xvYmFsIFJvb3QgQ0EgRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi AATxs8045CVD5d4ZCbuBeaIVXxVjAd7Cq92zphtnS4CDr5nLrBfbK5bKfFJV4hrhPVbwLxYI+hW8 m7tH5j/uqOFMjPXTNvk4XatwmkcN4oFBButJ+bAp3TPsUKV/eSm4IJijYzBhMA8GA1UdEwEB/wQF MAMBAf8wHwYDVR0jBBgwFoAUpbtKl86zK3+kMd6Xg1mDpm9xy94wHQYDVR0OBBYEFKW7SpfOsyt/ pDHel4NZg6ZvccveMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBe8usGzEkxn0AA bbd+NvBNEU/zy4k6LHiRUKNbwMp1JvK/kF0LgoxgKJ/GcJpo5PECMFxYDlZ2z1jD1xCMuo6u47xk dUfFVZDj/bpV6wfEU6s3qe4hsiFbYI89MvHVI5TWWA== -----END CERTIFICATE----- Telekom Security TLS ECC Root 2020 ================================== -----BEGIN CERTIFICATE----- MIICQjCCAcmgAwIBAgIQNjqWjMlcsljN0AFdxeVXADAKBggqhkjOPQQDAzBjMQswCQYDVQQGEwJE RTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJUZWxl a29tIFNlY3VyaXR5IFRMUyBFQ0MgUm9vdCAyMDIwMB4XDTIwMDgyNTA3NDgyMFoXDTQ1MDgyNTIz NTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJpdHkg R21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgRUNDIFJvb3QgMjAyMDB2MBAGByqG SM49AgEGBSuBBAAiA2IABM6//leov9Wq9xCazbzREaK9Z0LMkOsVGJDZos0MKiXrPk/OtdKPD/M1 2kOLAoC+b1EkHQ9rK8qfwm9QMuU3ILYg/4gND21Ju9sGpIeQkpT0CdDPf8iAC8GXs7s1J8nCG6NC MEAwHQYDVR0OBBYEFONyzG6VmUex5rNhTNHLq+O6zd6fMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMAoGCCqGSM49BAMDA2cAMGQCMHVSi7ekEE+uShCLsoRbQuHmKjYC2qBuGT8lv9pZ Mo7k+5Dck2TOrbRBR2Diz6fLHgIwN0GMZt9Ba9aDAEH9L1r3ULRn0SyocddDypwnJJGDSA3PzfdU ga/sf+Rn27iQ7t0l -----END CERTIFICATE----- Telekom Security TLS RSA Root 2023 ================================== -----BEGIN CERTIFICATE----- MIIFszCCA5ugAwIBAgIQIZxULej27HF3+k7ow3BXlzANBgkqhkiG9w0BAQwFADBjMQswCQYDVQQG EwJERTEnMCUGA1UECgweRGV1dHNjaGUgVGVsZWtvbSBTZWN1cml0eSBHbWJIMSswKQYDVQQDDCJU ZWxla29tIFNlY3VyaXR5IFRMUyBSU0EgUm9vdCAyMDIzMB4XDTIzMDMyODEyMTY0NVoXDTQ4MDMy NzIzNTk1OVowYzELMAkGA1UEBhMCREUxJzAlBgNVBAoMHkRldXRzY2hlIFRlbGVrb20gU2VjdXJp dHkgR21iSDErMCkGA1UEAwwiVGVsZWtvbSBTZWN1cml0eSBUTFMgUlNBIFJvb3QgMjAyMzCCAiIw DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO01oYGA88tKaVvC+1GDrib94W7zgRJ9cUD/h3VC KSHtgVIs3xLBGYSJwb3FKNXVS2xE1kzbB5ZKVXrKNoIENqil/Cf2SfHVcp6R+SPWcHu79ZvB7JPP GeplfohwoHP89v+1VmLhc2o0mD6CuKyVU/QBoCcHcqMAU6DksquDOFczJZSfvkgdmOGjup5czQRx UX11eKvzWarE4GC+j4NSuHUaQTXtvPM6Y+mpFEXX5lLRbtLevOP1Czvm4MS9Q2QTps70mDdsipWo l8hHD/BeEIvnHRz+sTugBTNoBUGCwQMrAcjnj02r6LX2zWtEtefdi+zqJbQAIldNsLGyMcEWzv/9 FIS3R/qy8XDe24tsNlikfLMR0cN3f1+2JeANxdKz+bi4d9s3cXFH42AYTyS2dTd4uaNir73Jco4v zLuu2+QVUhkHM/tqty1LkCiCc/4YizWN26cEar7qwU02OxY2kTLvtkCJkUPg8qKrBC7m8kwOFjQg rIfBLX7JZkcXFBGk8/ehJImr2BrIoVyxo/eMbcgByU/J7MT8rFEz0ciD0cmfHdRHNCk+y7AO+oML KFjlKdw/fKifybYKu6boRhYPluV75Gp6SG12mAWl3G0eQh5C2hrgUve1g8Aae3g1LDj1H/1Joy7S WWO/gLCMk3PLNaaZlSJhZQNg+y+TS/qanIA7AgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAdBgNV HQ4EFgQUtqeXgj10hZv3PJ+TmpV5dVKMbUcwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBS2 p5eCPXSFm/c8n5OalXl1UoxtRzANBgkqhkiG9w0BAQwFAAOCAgEAqMxhpr51nhVQpGv7qHBFfLp+ sVr8WyP6Cnf4mHGCDG3gXkaqk/QeoMPhk9tLrbKmXauw1GLLXrtm9S3ul0A8Yute1hTWjOKWi0Fp kzXmuZlrYrShF2Y0pmtjxrlO8iLpWA1WQdH6DErwM807u20hOq6OcrXDSvvpfeWxm4bu4uB9tPcy /SKE8YXJN3nptT+/XOR0so8RYgDdGGah2XsjX/GO1WfoVNpbOms2b/mBsTNHM3dA+VKq3dSDz4V4 mZqTuXNnQkYRIer+CqkbGmVps4+uFrb2S1ayLfmlyOw7YqPta9BO1UAJpB+Y1zqlklkg5LB9zVtz aL1txKITDmcZuI1CfmwMmm6gJC3VRRvcxAIU/oVbZZfKTpBQCHpCNfnqwmbU+AGuHrS+w6jv/naa oqYfRvaE7fzbzsQCzndILIyy7MMAo+wsVRjBfhnu4S/yrYObnqsZ38aKL4x35bcF7DvB7L6Gs4a8 wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0 o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A= -----END CERTIFICATE----- FIRMAPROFESIONAL CA ROOT-A WEB ============================== -----BEGIN CERTIFICATE----- MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4 MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2 WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6 iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB /wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ pYXFuXqUPoeovQA= -----END CERTIFICATE----- TWCA CYBER Root CA ================== -----BEGIN CERTIFICATE----- MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK /c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF 2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83 QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq /p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X -----END CERTIFICATE----- SecureSign Root CA12 ==================== -----BEGIN CERTIFICATE----- MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3 emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA== -----END CERTIFICATE----- SecureSign Root CA14 ==================== -----BEGIN CERTIFICATE----- MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh 1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb 1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa /d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0 dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/ OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S -----END CERTIFICATE----- SecureSign Root CA15 ==================== -----BEGIN CERTIFICATE----- MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1 cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB 2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4= -----END CERTIFICATE----- D-TRUST BR Root CA 2 2023 ========================= -----BEGIN CERTIFICATE----- MIIFqTCCA5GgAwIBAgIQczswBEhb2U14LnNLyaHcZjANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEJSIFJvb3QgQ0Eg MiAyMDIzMB4XDTIzMDUwOTA4NTYzMVoXDTM4MDUwOTA4NTYzMFowSDELMAkGA1UEBhMCREUxFTAT BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBCUiBSb290IENBIDIgMjAyMzCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK7/CVmRgApKaOYkP7in5Mg6CjoWzckjYaCT cfKri3OPoGdlYNJUa2NRb0kz4HIHE304zQaSBylSa053bATTlfrdTIzZXcFhfUvnKLNEgXtRr90z sWh81k5M/itoucpmacTsXld/9w3HnDY25QdgrMBM6ghs7wZ8T1soegj8k12b9py0i4a6Ibn08OhZ WiihNIQaJZG2tY/vsvmA+vk9PBFy2OMvhnbFeSzBqZCTRphny4NqoFAjpzv2gTng7fC5v2Xx2Mt6 ++9zA84A9H3X4F07ZrjcjrqDy4d2A/wl2ecjbwb9Z/Pg/4S8R7+1FhhGaRTMBffb00msa8yr5LUL QyReS2tNZ9/WtT5PeB+UcSTq3nD88ZP+npNa5JRal1QMNXtfbO4AHyTsA7oC9Xb0n9Sa7YUsOCIv x9gvdhFP/Wxc6PWOJ4d/GUohR5AdeY0cW/jPSoXk7bNbjb7EZChdQcRurDhaTyN0dKkSw/bSuREV MweR2Ds3OmMwBtHFIjYoYiMQ4EbMl6zWK11kJNXuHA7e+whadSr2Y23OC0K+0bpwHJwh5Q8xaRfX /Aq03u2AnMuStIv13lmiWAmlY0cL4UEyNEHZmrHZqLAbWt4NDfTisl01gLmB1IRpkQLLddCNxbU9 CZEJjxShFHR5PtbJFR2kWVki3PaKRT08EtY+XTIvAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUZ5Dw1t61GNVGKX5cq/ieCLxklRAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfYnJfcm9vdF9jYV8y XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQA097N3U9swFrktpSHxQCF16+tIFoE9c+CeJyrr d6kTpGoKWloUMz1oH4Guaf2Mn2VsNELZLdB/eBaxOqwjMa1ef67nriv6uvw8l5VAk1/DLQOj7aRv U9f6QA4w9QAgLABMjDu0ox+2v5Eyq6+SmNMW5tTRVFxDWy6u71cqqLRvpO8NVhTaIasgdp4D/Ca4 nj8+AybmTNudX0KEPUUDAxxZiMrcLmEkWqTqJwtzEr5SswrPMhfiHocaFpVIbVrg0M8JkiZmkdij YQ6qgYF/6FKC0ULn4B0Y+qSFNueG4A3rvNTJ1jxD8V1Jbn6Bm2m1iWKPiFLY1/4nwSPFyysCu7Ff /vtDhQNGvl3GyiEm/9cCnnRK3PgTFbGBVzbLZVzRHTF36SXDw7IyN9XxmAnkbWOACKsGkoHU6XCP pz+y7YaMgmo1yEJagtFSGkUPFaUA8JR7ZSdXOUPPfH/mvTWze/EZTN46ls/pdu4D58JDUjxqgejB WoC9EV2Ta/vH5mQ/u2kc6d0li690yVRAysuTEwrt+2aSEcr1wPrYg1UDfNPFIkZ1cGt5SAYqgpq/ 5usWDiJFAbzdNpQ0qTUmiteXue4Icr80knCDgKs4qllo3UCkGJCy89UDyibK79XH4I9TjvAA46jt n/mtd+ArY0+ew+43u3gJhJ65bvspmZDogNOfJA== -----END CERTIFICATE----- TrustAsia TLS ECC Root CA ========================= -----BEGIN CERTIFICATE----- MIICMTCCAbegAwIBAgIUNnThTXxlE8msg1UloD5Sfi9QaMcwCgYIKoZIzj0EAwMwWDELMAkGA1UE BhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xIjAgBgNVBAMTGVRy dXN0QXNpYSBUTFMgRUNDIFJvb3QgQ0EwHhcNMjQwNTE1MDU0MTU2WhcNNDQwNTE1MDU0MTU1WjBY MQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEiMCAG A1UEAxMZVHJ1c3RBc2lhIFRMUyBFQ0MgUm9vdCBDQTB2MBAGByqGSM49AgEGBSuBBAAiA2IABLh/ pVs/AT598IhtrimY4ZtcU5nb9wj/1WrgjstEpvDBjL1P1M7UiFPoXlfXTr4sP/MSpwDpguMqWzJ8 S5sUKZ74LYO1644xST0mYekdcouJtgq7nDM1D9rs3qlKH8kzsaNCMEAwDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQULIVTu7FDzTLqnqOH/qKYqKaT6RAwDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49 BAMDA2gAMGUCMFRH18MtYYZI9HlaVQ01L18N9mdsd0AaRuf4aFtOJx24mH1/k78ITcTaRTChD15K eAIxAKORh/IRM4PDwYqROkwrULG9IpRdNYlzg8WbGf60oenUoWa2AaU2+dhoYSi3dOGiMQ== -----END CERTIFICATE----- TrustAsia TLS RSA Root CA ========================= -----BEGIN CERTIFICATE----- MIIFgDCCA2igAwIBAgIUHBjYz+VTPyI1RlNUJDxsR9FcSpwwDQYJKoZIhvcNAQEMBQAwWDELMAkG A1UEBhMCQ04xJTAjBgNVBAoTHFRydXN0QXNpYSBUZWNobm9sb2dpZXMsIEluYy4xIjAgBgNVBAMT GVRydXN0QXNpYSBUTFMgUlNBIFJvb3QgQ0EwHhcNMjQwNTE1MDU0MTU3WhcNNDQwNTE1MDU0MTU2 WjBYMQswCQYDVQQGEwJDTjElMCMGA1UEChMcVHJ1c3RBc2lhIFRlY2hub2xvZ2llcywgSW5jLjEi MCAGA1UEAxMZVHJ1c3RBc2lhIFRMUyBSU0EgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAMMWuBtqpERz5dZO9LnPWwvB0ZqB9WOwj0PBuwhaGnrhB3YmH49pVr7+NmDQDIPN lOrnxS1cLwUWAp4KqC/lYCZUlviYQB2srp10Zy9U+5RjmOMmSoPGlbYJQ1DNDX3eRA5gEk9bNb2/ mThtfWza4mhzH/kxpRkQcwUqwzIZheo0qt1CHjCNP561HmHVb70AcnKtEj+qpklz8oYVlQwQX1Fk zv93uMltrOXVmPGZLmzjyUT5tUMnCE32ft5EebuyjBza00tsLtbDeLdM1aTk2tyKjg7/D8OmYCYo zza/+lcK7Fs/6TAWe8TbxNRkoDD75f0dcZLdKY9BWN4ArTr9PXwaqLEX8E40eFgl1oUh63kd0Nyr z2I8sMeXi9bQn9P+PN7F4/w6g3CEIR0JwqH8uyghZVNgepBtljhb//HXeltt08lwSUq6HTrQUNoy IBnkiz/r1RYmNzz7dZ6wB3C4FGB33PYPXFIKvF1tjVEK2sUYyJtt3LCDs3+jTnhMmCWr8n4uIF6C FabW2I+s5c0yhsj55NqJ4js+k8UTav/H9xj8Z7XvGCxUq0DTbE3txci3OE9kxJRMT6DNrqXGJyV1 J23G2pyOsAWZ1SgRxSHUuPzHlqtKZFlhaxP8S8ySpg+kUb8OWJDZgoM5pl+z+m6Ss80zDoWo8SnT q1mt1tve1CuBAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFLgHkXlcBvRG/XtZ ylomkadFK/hTMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQwFAAOCAgEAIZtqBSBdGBanEqT3 Rz/NyjuujsCCztxIJXgXbODgcMTWltnZ9r96nBO7U5WS/8+S4PPFJzVXqDuiGev4iqME3mmL5Dw8 veWv0BIb5Ylrc5tvJQJLkIKvQMKtuppgJFqBTQUYo+IzeXoLH5Pt7DlK9RME7I10nYEKqG/odv6L TytpEoYKNDbdgptvT+Bz3Ul/KD7JO6NXBNiT2Twp2xIQaOHEibgGIOcberyxk2GaGUARtWqFVwHx tlotJnMnlvm5P1vQiJ3koP26TpUJg3933FEFlJ0gcXax7PqJtZwuhfG5WyRasQmr2soaB82G39tp 27RIGAAtvKLEiUUjpQ7hRGU+isFqMB3iYPg6qocJQrmBktwliJiJ8Xw18WLK7nn4GS/+X/jbh87q qA8MpugLoDzga5SYnH+tBuYc6kIQX+ImFTw3OffXvO645e8D7r0i+yiGNFjEWn9hongPXvPKnbwb PKfILfanIhHKA9jnZwqKDss1jjQ52MjqjZ9k4DewbNfFj8GQYSbbJIweSsCI3zWQzj8C9GRh3sfI B5XeMhg6j6JCQCTl1jNdfK7vsU1P1FeQNWrcrgSXSYk0ly4wBOeY99sLAZDBHwo/+ML+TvrbmnNz FrwFuHnYWa8G5z9nODmxfKuU4CkUpijy323imttUQ/hHWKNddBWcwauwxzQ= -----END CERTIFICATE----- D-TRUST EV Root CA 2 2023 ========================= -----BEGIN CERTIFICATE----- MIIFqTCCA5GgAwIBAgIQaSYJfoBLTKCnjHhiU19abzANBgkqhkiG9w0BAQ0FADBIMQswCQYDVQQG EwJERTEVMBMGA1UEChMMRC1UcnVzdCBHbWJIMSIwIAYDVQQDExlELVRSVVNUIEVWIFJvb3QgQ0Eg MiAyMDIzMB4XDTIzMDUwOTA5MTAzM1oXDTM4MDUwOTA5MTAzMlowSDELMAkGA1UEBhMCREUxFTAT BgNVBAoTDEQtVHJ1c3QgR21iSDEiMCAGA1UEAxMZRC1UUlVTVCBFViBSb290IENBIDIgMjAyMzCC AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANiOo4mAC7JXUtypU0w3uX9jFxPvp1sjW2l1 sJkKF8GLxNuo4MwxusLyzV3pt/gdr2rElYfXR8mV2IIEUD2BCP/kPbOx1sWy/YgJ25yE7CUXFId/ MHibaljJtnMoPDT3mfd/06b4HEV8rSyMlD/YZxBTfiLNTiVR8CUkNRFeEMbsh2aJgWi6zCudR3Mf vc2RpHJqnKIbGKBv7FD0fUDCqDDPvXPIEysQEx6Lmqg6lHPTGGkKSv/BAQP/eX+1SH977ugpbzZM lWGG2Pmic4ruri+W7mjNPU0oQvlFKzIbRlUWaqZLKfm7lVa/Rh3sHZMdwGWyH6FDrlaeoLGPaxK3 YG14C8qKXO0elg6DpkiVjTujIcSuWMYAsoS0I6SWhjW42J7YrDRJmGOVxcttSEfi8i4YHtAxq910 7PncjLgcjmgjutDzUNzPZY9zOjLHfP7KgiJPvo5iR2blzYfi6NUPGJ/lBHJLRjwQ8kTCZFZxTnXo nMkmdMV9WdEKWw9t/p51HBjGGjp82A0EzM23RWV6sY+4roRIPrN6TagD4uJ+ARZZaBhDM7DS3LAa QzXupdqpRlyuhoFBAUp0JuyfBr/CBTdkdXgpaP3F9ev+R/nkhbDhezGdpn9yo7nELC7MmVcOIQxF AZRl62UJxmMiCzNJkkg8/M3OsD6Onov4/knFNXJHAgMBAAGjgY4wgYswDwYDVR0TAQH/BAUwAwEB /zAdBgNVHQ4EFgQUqvyREBuHkV8Wub9PS5FeAByxMoAwDgYDVR0PAQH/BAQDAgEGMEkGA1UdHwRC MEAwPqA8oDqGOGh0dHA6Ly9jcmwuZC10cnVzdC5uZXQvY3JsL2QtdHJ1c3RfZXZfcm9vdF9jYV8y XzIwMjMuY3JsMA0GCSqGSIb3DQEBDQUAA4ICAQCTy6UfmRHsmg1fLBWTxj++EI14QvBukEdHjqOS Mo1wj/Zbjb6JzkcBahsgIIlbyIIQbODnmaprxiqgYzWRaoUlrRc4pZt+UPJ26oUFKidBK7GB0aL2 QHWpDsvxVUjY7NHss+jOFKE17MJeNRqrphYBBo7q3C+jisosketSjl8MmxfPy3MHGcRqwnNU73xD UmPBEcrCRbH0O1P1aa4846XerOhUt7KR/aypH/KH5BfGSah82ApB9PI+53c0BFLd6IHyTS9URZ0V 4U/M5d40VxDJI3IXcI1QcB9WbMy5/zpaT2N6w25lBx2Eof+pDGOJbbJAiDnXH3dotfyc1dZnaVuo dNv8ifYbMvekJKZ2t0dT741Jj6m2g1qllpBFYfXeA08mD6iL8AOWsKwV0HFaanuU5nCT2vFp4LJi TZ6P/4mdm13NRemUAiKN4DV/6PEEeXFsVIP4M7kFMhtYVRFP0OUnR3Hs7dpn1mKmS00PaaLJvOwi S5THaJQXfuKOKD62xur1NGyfN4gHONuGcfrNlUhDbqNPgofXNJhuS5N5YHVpD/Aa1VP6IQzCP+k/ HxiMkl14p3ZnGbuy6n/pcAlWVqOwDAstNl7F6cTVg8uGF5csbBNvh1qvSaYd2804BC5f4ko1Di1L +KIkBI3Y4WNeApI02phhXBxvWHZks/wCuPWdCg== -----END CERTIFICATE----- SwissSign RSA TLS Root CA 2022 - 1 ================================== -----BEGIN CERTIFICATE----- MIIFkzCCA3ugAwIBAgIUQ/oMX04bgBhE79G0TzUfRPSA7cswDQYJKoZIhvcNAQELBQAwUTELMAkG A1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzErMCkGA1UEAxMiU3dpc3NTaWduIFJTQSBU TFMgUm9vdCBDQSAyMDIyIC0gMTAeFw0yMjA2MDgxMTA4MjJaFw00NzA2MDgxMTA4MjJaMFExCzAJ BgNVBAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxKzApBgNVBAMTIlN3aXNzU2lnbiBSU0Eg VExTIFJvb3QgQ0EgMjAyMiAtIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDLKmji C8NXvDVjvHClO/OMPE5Xlm7DTjak9gLKHqquuN6orx122ro10JFwB9+zBvKK8i5VUXu7LCTLf5Im gKO0lPaCoaTo+nUdWfMHamFk4saMla+ju45vVs9xzF6BYQ1t8qsCLqSX5XH8irCRIFucdFJtrhUn WXjyCcplDn/L9Ovn3KlMd/YrFgSVrpxxpT8q2kFC5zyEEPThPYxr4iuRR1VPuFa+Rd4iUU1OKNlf GUEGjw5NBuBwQCMBauTLE5tzrE0USJIt/m2n+IdreXXhvhCxqohAWVTXz8TQm0SzOGlkjIHRI36q OTw7D59Ke4LKa2/KIj4x0LDQKhySio/YGZxH5D4MucLNvkEM+KRHBdvBFzA4OmnczcNpI/2aDwLO EGrOyvi5KaM2iYauC8BPY7kGWUleDsFpswrzd34unYyzJ5jSmY0lpx+Gs6ZUcDj8fV3oT4MM0ZPl EuRU2j7yrTrePjxF8CgPBrnh25d7mUWe3f6VWQQvdT/TromZhqwUtKiE+shdOxtYk8EXlFXIC+OC eYSf8wCENO7cMdWP8vpPlkwGqnj73mSiI80fPsWMvDdUDrtaclXvyFu1cvh43zcgTFeRc5JzrBh3 Q4IgaezprClG5QtO+DdziZaKHG29777YtvTKwP1H8K4LWCDFyB02rpeNUIMmJCn3nTsPBQIDAQAB o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBRvjmKLk0Ow 4UD2p8P98Q+4DxU4pTAdBgNVHQ4EFgQUb45ii5NDsOFA9qfD/fEPuA8VOKUwDQYJKoZIhvcNAQEL BQADggIBAKwsKUF9+lz1GpUYvyypiqkkVHX1uECry6gkUSsYP2OprphWKwVDIqO310aewCoSPY6W lkDfDDOLazeROpW7OSltwAJsipQLBwJNGD77+3v1dj2b9l4wBlgzHqp41eZUBDqyggmNzhYzWUUo 8aWjlw5DI/0LIICQ/+Mmz7hkkeUFjxOgdg3XNwwQiJb0Pr6VvfHDffCjw3lHC1ySFWPtUnWK50Zp y1FVCypM9fJkT6lc/2cyjlUtMoIcgC9qkfjLvH4YoiaoLqNTKIftV+Vlek4ASltOU8liNr3Cjlvr zG4ngRhZi0Rjn9UMZfQpZX+RLOV/fuiJz48gy20HQhFRJjKKLjpHE7iNvUcNCfAWpO2Whi4Z2L6M OuhFLhG6rlrnub+xzI/goP+4s9GFe3lmozm1O2bYQL7Pt2eLSMkZJVX8vY3PXtpOpvJpzv1/THfQ wUY1mFwjmwJFQ5Ra3bxHrSL+ul4vkSkphnsh3m5kt8sNjzdbowhq6/TdAo9QAwKxuDdollDruF/U KIqlIgyKhPBZLtU30WHlQnNYKoH3dtvi4k0NX/a3vgW0rk4N3hY9A4GzJl5LuEsAz/+MF7psYC0n hzck5npgL7XTgwSqT0N1osGDsieYK7EOgLrAhV5Cud+xYJHT6xh+cHiudoO+cVrQkOPKwRYlZ0rw tnu64ZzZ -----END CERTIFICATE----- OISTE Server Root ECC G1 ======================== -----BEGIN CERTIFICATE----- MIICNTCCAbqgAwIBAgIQI/nD1jWvjyhLH/BU6n6XnTAKBggqhkjOPQQDAzBLMQswCQYDVQQGEwJD SDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwYT0lTVEUgU2VydmVyIFJvb3Qg RUNDIEcxMB4XDTIzMDUzMTE0NDIyOFoXDTQ4MDUyNDE0NDIyN1owSzELMAkGA1UEBhMCQ0gxGTAX BgNVBAoMEE9JU1RFIEZvdW5kYXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IEVDQyBH MTB2MBAGByqGSM49AgEGBSuBBAAiA2IABBcv+hK8rBjzCvRE1nZCnrPoH7d5qVi2+GXROiFPqOuj vqQycvO2Ackr/XeFblPdreqqLiWStukhEaivtUwL85Zgmjvn6hp4LrQ95SjeHIC6XG4N2xml4z+c KrhAS93mT6NjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBQ3TYhlz/w9itWj8UnATgwQ b0K0nDAdBgNVHQ4EFgQUN02IZc/8PYrVo/FJwE4MEG9CtJwwDgYDVR0PAQH/BAQDAgGGMAoGCCqG SM49BAMDA2kAMGYCMQCpKjAd0MKfkFFRQD6VVCHNFmb3U2wIFjnQEnx/Yxvf4zgAOdktUyBFCxxg ZzFDJe0CMQCSia7pXGKDYmH5LVerVrkR3SW+ak5KGoJr3M/TvEqzPNcum9v4KGm8ay3sMaE641c= -----END CERTIFICATE----- OISTE Server Root RSA G1 ========================= -----BEGIN CERTIFICATE----- MIIFgzCCA2ugAwIBAgIQVaXZZ5Qoxu0M+ifdWwFNGDANBgkqhkiG9w0BAQwFADBLMQswCQYDVQQG EwJDSDEZMBcGA1UECgwQT0lTVEUgRm91bmRhdGlvbjEhMB8GA1UEAwwYT0lTVEUgU2VydmVyIFJv b3QgUlNBIEcxMB4XDTIzMDUzMTE0MzcxNloXDTQ4MDUyNDE0MzcxNVowSzELMAkGA1UEBhMCQ0gx GTAXBgNVBAoMEE9JU1RFIEZvdW5kYXRpb24xITAfBgNVBAMMGE9JU1RFIFNlcnZlciBSb290IFJT QSBHMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKqu9KuCz/vlNwvn1ZatkOhLKdxV YOPMvLO8LZK55KN68YG0nnJyQ98/qwsmtO57Gmn7KNByXEptaZnwYx4M0rH/1ow00O7brEi56rAU jtgHqSSY3ekJvqgiG1k50SeH3BzN+Puz6+mTeO0Pzjd8JnduodgsIUzkik/HEzxux9UTl7Ko2yRp g1bTacuCErudG/L4NPKYKyqOBGf244ehHa1uzjZ0Dl4zO8vbUZeUapU8zhhabkvG/AePLhq5Svdk NCncpo1Q4Y2LS+VIG24ugBA/5J8bZT8RtOpXaZ+0AOuFJJkk9SGdl6r7NH8CaxWQrbueWhl/pIzY +m0o/DjH40ytas7ZTpOSjswMZ78LS5bOZmdTaMsXEY5Z96ycG7mOaES3GK/m5Q9l3JUJsJMStR8+ lKXHiHUhsd4JJCpM4rzsTGdHwimIuQq6+cF0zowYJmXa92/GjHtoXAvuY8BeS/FOzJ8vD+HomnqT 8eDI278n5mUpezbgMxVz8p1rhAhoKzYHKyfMeNhqhw5HdPSqoBNdZH702xSu+zrkL8Fl47l6QGzw Brd7KJvX4V84c5Ss2XCTLdyEr0YconosP4EmQufU2MVshGYRi3drVByjtdgQ8K4p92cIiBdcuJd5 z+orKu5YM+Vt6SmqZQENghPsJQtdLEByFSnTkCz3GkPVavBpAgMBAAGjYzBhMA8GA1UdEwEB/wQF MAMBAf8wHwYDVR0jBBgwFoAU8snBDw1jALvsRQ5KH7WxszbNDo0wHQYDVR0OBBYEFPLJwQ8NYwC7 7EUOSh+1sbM2zQ6NMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQwFAAOCAgEANGd5sjrG5T33 I3K5Ce+SrScfoE4KsvXaFwyihdJ+klH9FWXXXGtkFu6KRcoMQzZENdl//nk6HOjG5D1rd9QhEOP2 8yBOqb6J8xycqd+8MDoX0TJD0KqKchxRKEzdNsjkLWd9kYccnbz8qyiWXmFcuCIzGEgWUOrKL+ml Sdx/PKQZvDatkuK59EvV6wit53j+F8Bdh3foZ3dPAGav9LEDOr4SfEE15fSmG0eLy3n31r8Xbk5l 8PjaV8GUgeV6Vg27Rn9vkf195hfkgSe7BYhW3SCl95gtkRlpMV+bMPKZrXJAlszYd2abtNUOshD+ FKrDgHGdPY3ofRRsYWSGRqbXVMW215AWRqWFyp464+YTFrYVI8ypKVL9AMb2kI5Wj4kI3Zaq5tNq qYY19tVFeEJKRvwDyF7YZvZFZSS0vod7VSCd9521Kvy5YhnLbDuv0204bKt7ph6N/Ome/msVuduC msuY33OhkKCgxeDoAaijFJzIwZqsFVAzje18KotzlUBDJvyBpCpfOZC3J8tRd/iWkx7P8nd9H0aT olkelUTFLXVksNb54Dxp6gS1HAviRkRNQzuXSXERvSS2wq1yVAb+axj5d9spLFKebXd7Yv0PTY6Y MjAwcRLWJTXjn/hvnLXrahut6hDTlhZyBiElxky8j3C7DOReIoMt0r7+hVu05L0= -----END CERTIFICATE----- ================================================ FILE: internal/app/machined/pkg/controllers/secrets/encryption_salt.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" blockadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/block" secretsadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/secrets" "github.com/siderolabs/talos/internal/app/machined/pkg/automaton/blockautomaton" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/xfs" ) // EncryptionSaltController manages secrets.EncryptionSalt in STATE. type EncryptionSaltController struct { stateMachine blockautomaton.VolumeMounterAutomaton } // Name implements controller.Controller interface. func (ctrl *EncryptionSaltController) Name() string { return "secrets.EncryptionSaltController" } // Inputs implements controller.Controller interface. func (ctrl *EncryptionSaltController) Inputs() []controller.Input { return []controller.Input{ { Namespace: block.NamespaceName, Type: block.VolumeMountStatusType, Kind: controller.InputStrong, }, { Namespace: block.NamespaceName, Type: block.VolumeMountRequestType, Kind: controller.InputDestroyReady, }, } } // Outputs implements controller.Controller interface. func (ctrl *EncryptionSaltController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.EncryptionSaltType, Kind: controller.OutputShared, }, { Type: block.VolumeMountRequestType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EncryptionSaltController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } if ctrl.stateMachine == nil { ctrl.stateMachine = blockautomaton.NewVolumeMounter( ctrl.Name(), constants.StatePartitionLabel, ctrl.establishEncryptionSalt, blockautomaton.WithDetached(true), ) } if err := ctrl.stateMachine.Run(ctx, r, logger); err != nil { return fmt.Errorf("error running volume mounter machine: %w", err) } r.ResetRestartBackoff() } } func (ctrl *EncryptionSaltController) establishEncryptionSalt(ctx context.Context, r controller.ReaderWriter, logger *zap.Logger, mountStatus *block.VolumeMountStatus) error { return blockadapter.VolumeMountStatus(mountStatus).WithRoot(logger, func(root xfs.Root) error { var salt secrets.EncryptionSaltSpec if err := controllers.LoadOrNewFromFile(root, constants.EncryptionSaltFilename, &salt, func(v *secrets.EncryptionSaltSpec) error { return secretsadapter.EncryptionSalt(v).Generate() }); err != nil { return fmt.Errorf("error caching node identity: %w", err) } if err := safe.WriterModify(ctx, r, secrets.NewEncryptionSalt(), func(r *secrets.EncryptionSalt) error { *r.TypedSpec() = salt return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } // encryption salt thumbprint for debugging purposes saltHMAC := hmac.New(sha256.New, salt.DiskSalt) saltHMAC.Write([]byte("encryption salt checksum")) saltChecksum := saltHMAC.Sum(nil) logger.Info("encryption salt established", zap.String("salt_checksum", hex.EncodeToString(saltChecksum)), ) return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/encryption_salt_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "log" "os" "path/filepath" "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) type EncryptionSaltSuite struct { ctest.DefaultSuite } func (suite *EncryptionSaltSuite) TestDefault() { statePath := suite.T().TempDir() mountID := (&secretsctrl.EncryptionSaltController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) ctest.AssertNoResource[*secrets.EncryptionSalt](suite, secrets.EncryptionSaltID) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, secrets.EncryptionSaltID, func(*secrets.EncryptionSalt, *assert.Assertions) {}) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) suite.Assert().FileExists(filepath.Join(statePath, constants.EncryptionSaltFilename), "encryption salt file should exist") contents, err := os.ReadFile(filepath.Join(statePath, constants.EncryptionSaltFilename)) suite.Require().NoError(err, "should be able to read encryption salt file") log.Printf("contents: %q", contents) } func (suite *EncryptionSaltSuite) TestLoad() { statePath := suite.T().TempDir() mountID := (&secretsctrl.EncryptionSaltController{}).Name() + "-" + constants.StatePartitionLabel ctest.AssertResource(suite, mountID, func(mountRequest *block.VolumeMountRequest, asrt *assert.Assertions) { asrt.Equal(constants.StatePartitionLabel, mountRequest.TypedSpec().VolumeID) }) // using verbatim data here to make sure salt representation is supported in future version fo Talos suite.Require().NoError(os.WriteFile(filepath.Join(statePath, constants.EncryptionSaltFilename), []byte("diskSalt:\n - 240\n - 180\n - 79\n - 128\n - 31\n - 0\n - 19\n - 124\n - 165\n - 74\n - 113\n - 220\n - 27\n - 83\n - 46\n - 74\n - 204\n - 190\n - 217\n - 96\n - 221\n - 2\n - 165\n - 98\n - 245\n - 36\n - 165\n - 151\n - 149\n - 66\n - 113\n - 16\n"), //nolint:lll 0o600)) ctest.AssertNoResource[*secrets.EncryptionSalt](suite, secrets.EncryptionSaltID) volumeMountStatus := block.NewVolumeMountStatus(block.NamespaceName, mountID) volumeMountStatus.TypedSpec().Target = statePath suite.Create(volumeMountStatus) ctest.AssertResource(suite, secrets.EncryptionSaltID, func(encryptionSalt *secrets.EncryptionSalt, asrt *assert.Assertions) { asrt.Equal( []byte{0xf0, 0xb4, 0x4f, 0x80, 0x1f, 0x0, 0x13, 0x7c, 0xa5, 0x4a, 0x71, 0xdc, 0x1b, 0x53, 0x2e, 0x4a, 0xcc, 0xbe, 0xd9, 0x60, 0xdd, 0x2, 0xa5, 0x62, 0xf5, 0x24, 0xa5, 0x97, 0x95, 0x42, 0x71, 0x10}, encryptionSalt.TypedSpec().DiskSalt, ) }) ctest.AssertResources(suite, []resource.ID{volumeMountStatus.Metadata().ID()}, func(vms *block.VolumeMountStatus, asrt *assert.Assertions) { asrt.True(vms.Metadata().Finalizers().Empty()) }) suite.Destroy(volumeMountStatus) ctest.AssertNoResource[*block.VolumeMountRequest](suite, mountID) } func TestEncryptionSaltSuite(t *testing.T) { t.Parallel() if os.Geteuid() != 0 { t.Skip("skipping test that requires root privileges") } suite.Run(t, &EncryptionSaltSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.EncryptionSaltController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // EtcdController manages secrets.Etcd based on configuration. type EtcdController struct{} // Name implements controller.Controller interface. func (ctrl *EtcdController) Name() string { return "secrets.EtcdController" } // Inputs implements controller.Controller interface. func (ctrl *EtcdController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.EtcdRootType, ID: optional.Some(secrets.EtcdRootID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: time.StatusType, ID: optional.Some(time.StatusID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s)), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *EtcdController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.EtcdType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *EtcdController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } etcdRootRes, err := safe.ReaderGet[*secrets.EtcdRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdRootType, secrets.EtcdRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting etcd root secrets: %w", err) } etcdRoot := etcdRootRes.TypedSpec() // wait for network to be ready as it might change IPs/hostname networkResource, err := safe.ReaderGet[*network.Status](ctx, r, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } networkStatus := networkResource.TypedSpec() if !(networkStatus.AddressReady && networkStatus.HostnameReady) { continue } // wait for time sync as certs depend on current time timeSyncResource, err := safe.ReaderGet[*time.Status](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, time.StatusType, time.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } if !timeSyncResource.TypedSpec().Synced { continue } hostnameStatus, err := safe.ReaderGet[*network.HostnameStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting hostname status: %w", err) } nodeAddrs, err := safe.ReaderGet[*network.NodeAddress]( ctx, r, resource.NewMetadata( network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting addresses: %w", err) } if err = safe.WriterModify(ctx, r, secrets.NewEtcd(), func(r *secrets.Etcd) error { return ctrl.updateSecrets(etcdRoot, nodeAddrs, hostnameStatus, r.TypedSpec()) }); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *EtcdController) updateSecrets(etcdRoot *secrets.EtcdRootSpec, nodeAddress *network.NodeAddress, hostnameStatus *network.HostnameStatus, etcdCerts *secrets.EtcdCertsSpec) error { generator := etcd.CertificateGenerator{ CA: etcdRoot.EtcdCA, NodeAddresses: nodeAddress, HostnameStatus: hostnameStatus, } var err error etcdCerts.Etcd, err = generator.GenerateServerCert() if err != nil { return fmt.Errorf("error generating etcd client certs: %w", err) } etcdCerts.EtcdPeer, err = generator.GeneratePeerCert() if err != nil { return fmt.Errorf("error generating etcd peer certs: %w", err) } etcdCerts.EtcdAdmin, err = generator.GenerateClientCert("talos") if err != nil { return fmt.Errorf("error generating admin client certs: %w", err) } etcdCerts.EtcdAPIServer, err = generator.GenerateClientCert("kube-apiserver") if err != nil { return fmt.Errorf("error generating kube-apiserver etcd client certs: %w", err) } return nil } func (ctrl *EtcdController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.EtcdType, "", resource.VersionUndefined)) if err != nil { return err } // TODO: change this to proper teardown sequence for _, res := range list.Items { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/etcd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "fmt" "net/netip" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeres "github.com/siderolabs/talos/pkg/machinery/resources/time" ) func TestEtcdSuite(t *testing.T) { t.Parallel() suite.Run(t, &EtcdSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.EtcdController{})) }, }, }) } type EtcdSuite struct { ctest.DefaultSuite } func (suite *EtcdSuite) TestReconcile() { rootSecrets := secrets.NewEtcdRoot(secrets.EtcdRootID) etcdCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("talos"), x509.ECDSA(true), ) suite.Require().NoError(err) rootSecrets.TypedSpec().EtcdCA = &x509.PEMEncodedCertificateAndKey{ Crt: etcdCA.CrtPEM, Key: etcdCA.KeyPEM, } suite.Create(rootSecrets) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true networkStatus.TypedSpec().HostnameReady = true suite.Create(networkStatus) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "host" hostnameStatus.TypedSpec().Domainname = "domain" suite.Create(hostnameStatus) nodeAddresses := network.NewNodeAddress(network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s)) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.3.4.5/24"), netip.MustParsePrefix("2001:db8::1eaf/64"), } suite.Create(nodeAddresses) timeSync := timeres.NewStatus() timeSync.TypedSpec().Synced = true suite.Create(timeSync) ctest.AssertResource(suite, secrets.EtcdID, func(certs *secrets.Etcd, asrt *assert.Assertions) { etcdCerts := certs.TypedSpec() serverCert, err := etcdCerts.Etcd.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"host", "host.domain", "localhost"}, serverCert.DNSNames) asrt.Equal("[10.3.4.5 2001:db8::1eaf 127.0.0.1 ::1]", fmt.Sprintf("%v", serverCert.IPAddresses)) asrt.Equal("host", serverCert.Subject.CommonName) peerCert, err := etcdCerts.EtcdPeer.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"host", "host.domain"}, peerCert.DNSNames) asrt.Equal("[10.3.4.5 2001:db8::1eaf]", fmt.Sprintf("%v", peerCert.IPAddresses)) asrt.Equal("host", peerCert.Subject.CommonName) adminCert, err := etcdCerts.EtcdAdmin.GetCert() if !asrt.NoError(err) { return } asrt.Empty(adminCert.DNSNames) asrt.Empty(adminCert.IPAddresses) asrt.Equal("talos", adminCert.Subject.CommonName) kubeAPICert, err := etcdCerts.EtcdAPIServer.GetCert() if !asrt.NoError(err) { return } asrt.Empty(kubeAPICert.DNSNames) asrt.Empty(kubeAPICert.IPAddresses) asrt.Equal("kube-apiserver", kubeAPICert.Subject.CommonName) }) // update node addresses, certs should be updated nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.3.4.5/24"), } suite.Update(nodeAddresses) ctest.AssertResource(suite, secrets.EtcdID, func(certs *secrets.Etcd, asrt *assert.Assertions) { etcdCerts := certs.TypedSpec() serverCert, err := etcdCerts.Etcd.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"host", "host.domain", "localhost"}, serverCert.DNSNames) asrt.Equal("[10.3.4.5 127.0.0.1 ::1]", fmt.Sprintf("%v", serverCert.IPAddresses)) asrt.Equal("host", serverCert.Subject.CommonName) peerCert, err := etcdCerts.EtcdPeer.GetCert() if !asrt.NoError(err) { return } asrt.Equal([]string{"host", "host.domain"}, peerCert.DNSNames) asrt.Equal("[10.3.4.5]", fmt.Sprintf("%v", peerCert.IPAddresses)) asrt.Equal("host", peerCert.Subject.CommonName) }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "errors" "fmt" "net/url" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // KubeletController manages secrets.Kubelet based on configuration. type KubeletController = transform.Controller[*config.MachineConfig, *secrets.Kubelet] // NewKubeletController instanciates the controller. func NewKubeletController() *KubeletController { return transform.NewController( transform.Settings[*config.MachineConfig, *secrets.Kubelet]{ Name: "secrets.KubeletController", MapMetadataOptionalFunc: func(cfg *config.MachineConfig) optional.Optional[*secrets.Kubelet] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[*secrets.Kubelet]() } if cfg.Config().Cluster() == nil || cfg.Config().Machine() == nil { return optional.None[*secrets.Kubelet]() } return optional.Some(secrets.NewKubelet(secrets.KubeletID)) }, TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *secrets.Kubelet) error { cfgProvider := cfg.Config() kubeletSecrets := res.TypedSpec() switch { case cfgProvider.Machine().Features().KubePrism().Enabled(): // use cluster endpoint for controlplane nodes with loadbalancer support localEndpoint, err := url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfgProvider.Machine().Features().KubePrism().Port())) if err != nil { return err } kubeletSecrets.Endpoint = localEndpoint case cfgProvider.Machine().Type().IsControlPlane(): // use localhost endpoint for controlplane nodes localEndpoint, err := url.Parse(fmt.Sprintf("https://localhost:%d", cfgProvider.Cluster().LocalAPIServerPort())) if err != nil { return err } kubeletSecrets.Endpoint = localEndpoint default: // use cluster endpoint for workers kubeletSecrets.Endpoint = cfgProvider.Cluster().Endpoint() } kubeletSecrets.AcceptedCAs = nil if cfgProvider.Cluster().IssuingCA() != nil { kubeletSecrets.AcceptedCAs = append(kubeletSecrets.AcceptedCAs, &x509.PEMEncodedCertificate{Crt: cfgProvider.Cluster().IssuingCA().Crt}) } kubeletSecrets.AcceptedCAs = append(kubeletSecrets.AcceptedCAs, cfgProvider.Cluster().AcceptedCAs()...) if len(kubeletSecrets.AcceptedCAs) == 0 { return errors.New("missing accepted Kubernetes CAs") } kubeletSecrets.BootstrapTokenID = cfgProvider.Cluster().Token().ID() kubeletSecrets.BootstrapTokenSecret = cfgProvider.Cluster().Token().Secret() return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubelet_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestKubeletSuite(t *testing.T) { suite.Run(t, &KubeletSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(secretsctrl.NewKubeletController())) }, }, }) } type KubeletSuite struct { ctest.DefaultSuite } func (suite *KubeletSuite) TestReconcile() { u, err := url.Parse("https://foo:6443") suite.Require().NoError(err) ca, err := x509.NewSelfSignedCertificateAuthority(x509.RSA(false)) suite.Require().NoError(err) k8sCA := x509.NewCertificateAndKeyFromCertificateAuthority(ca) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, ClusterCA: k8sCA, BootstrapToken: "abc.def", }, }, ), ) suite.Require().NoError(suite.State().Create(suite.Ctx(), cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { kubeletSecrets, err := ctest.Get[*secrets.Kubelet]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.KubeletType, secrets.KubeletID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := kubeletSecrets.TypedSpec() suite.Assert().Equal("https://foo:6443", spec.Endpoint.String()) suite.Assert().Equal([]*x509.PEMEncodedCertificate{{Crt: k8sCA.Crt}}, spec.AcceptedCAs) suite.Assert().Equal("abc", spec.BootstrapTokenID) suite.Assert().Equal("def", spec.BootstrapTokenSecret) return nil }, ), ) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "bytes" "context" "fmt" "net/url" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/kubeconfig" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // KubernetesCertificateValidityDuration is the validity duration for the certificates created with this controller. // // Controller automatically refreshes certs at 50% of CertificateValidityDuration. const KubernetesCertificateValidityDuration = constants.KubernetesDefaultCertificateValidityDuration // KubernetesController manages secrets.Kubernetes based on configuration. type KubernetesController struct{} // Name implements controller.Controller interface. func (ctrl *KubernetesController) Name() string { return "secrets.KubernetesController" } // Inputs implements controller.Controller interface. func (ctrl *KubernetesController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: timeresource.StatusType, ID: optional.Some(timeresource.StatusID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubernetesController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.KubernetesType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KubernetesController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { refreshTicker := time.NewTicker(KubernetesCertificateValidityDuration / 2) defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-refreshTicker.C: } k8sRoot, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting root k8s secrets: %w", err) } // wait for time sync as certs depend on current time timeSync, err := safe.ReaderGet[*timeresource.Status](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, timeresource.StatusType, timeresource.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } if !timeSync.TypedSpec().Synced { continue } if err = safe.WriterModify(ctx, r, secrets.NewKubernetes(), func(r *secrets.Kubernetes) error { return ctrl.updateSecrets(k8sRoot.TypedSpec(), r.TypedSpec()) }); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *KubernetesController) updateSecrets(k8sRoot *secrets.KubernetesRootSpec, k8sSecrets *secrets.KubernetesCertsSpec) error { var buf bytes.Buffer if err := kubeconfig.Generate(&kubeconfig.GenerateInput{ ClusterName: k8sRoot.Name, IssuingCA: k8sRoot.IssuingCA, AcceptedCAs: k8sRoot.AcceptedCAs, CertificateLifetime: KubernetesCertificateValidityDuration, CommonName: constants.KubernetesControllerManagerOrganization, Organization: constants.KubernetesControllerManagerOrganization, Endpoint: k8sRoot.LocalEndpoint.String(), Username: constants.KubernetesControllerManagerOrganization, ContextName: "default", }, &buf); err != nil { return fmt.Errorf("failed to generate controller manager kubeconfig: %w", err) } k8sSecrets.ControllerManagerKubeconfig = buf.String() buf.Reset() if err := kubeconfig.Generate(&kubeconfig.GenerateInput{ ClusterName: k8sRoot.Name, IssuingCA: k8sRoot.IssuingCA, AcceptedCAs: k8sRoot.AcceptedCAs, CertificateLifetime: KubernetesCertificateValidityDuration, CommonName: constants.KubernetesSchedulerOrganization, Organization: constants.KubernetesSchedulerOrganization, Endpoint: k8sRoot.LocalEndpoint.String(), Username: constants.KubernetesSchedulerOrganization, ContextName: "default", }, &buf); err != nil { return fmt.Errorf("failed to generate scheduler kubeconfig: %w", err) } k8sSecrets.SchedulerKubeconfig = buf.String() buf.Reset() if err := kubeconfig.GenerateAdmin(&generateAdminAdapter{ k8sRoot: k8sRoot, endpoint: k8sRoot.Endpoint, }, &buf); err != nil { return fmt.Errorf("failed to generate admin kubeconfig: %w", err) } k8sSecrets.AdminKubeconfig = buf.String() buf.Reset() if err := kubeconfig.GenerateAdmin(&generateAdminAdapter{ k8sRoot: k8sRoot, endpoint: k8sRoot.LocalEndpoint, }, &buf); err != nil { return fmt.Errorf("failed to generate admin kubeconfig: %w", err) } k8sSecrets.LocalhostAdminKubeconfig = buf.String() return nil } func (ctrl *KubernetesController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesType, "", resource.VersionUndefined)) if err != nil { return err } // TODO: change this to proper teardown sequence for _, res := range list.Items { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } // generateAdminAdapter allows to translate input config into GenerateAdmin input. type generateAdminAdapter struct { k8sRoot *secrets.KubernetesRootSpec endpoint *url.URL } func (adapter *generateAdminAdapter) Name() string { return adapter.k8sRoot.Name } func (adapter *generateAdminAdapter) Endpoint() *url.URL { return adapter.endpoint } func (adapter *generateAdminAdapter) IssuingCA() *x509.PEMEncodedCertificateAndKey { return adapter.k8sRoot.IssuingCA } func (adapter *generateAdminAdapter) AcceptedCAs() []*x509.PEMEncodedCertificate { return adapter.k8sRoot.AcceptedCAs } func (adapter *generateAdminAdapter) AdminKubeconfig() config.AdminKubeconfig { return adapter } func (adapter *generateAdminAdapter) CertLifetime() time.Duration { // this certificate is not delivered to the user, it's used only internally by control plane components return KubernetesCertificateValidityDuration } func (adapter *generateAdminAdapter) CommonName() string { return constants.KubernetesTalosAdminCertCommonName } func (adapter *generateAdminAdapter) CertOrganization() string { return constants.KubernetesAdminCertOrganization } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes_cert_sans.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // KubernetesCertSANsController manages secrets.KubernetesCertSANs based on configuration. type KubernetesCertSANsController struct{} // Name implements controller.Controller interface. func (ctrl *KubernetesCertSANsController) Name() string { return "secrets.KubernetesCertSANsController" } // Inputs implements controller.Controller interface. // //nolint:dupl func (ctrl *KubernetesCertSANsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s)), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *KubernetesCertSANsController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.CertSANType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *KubernetesCertSANsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } k8sRootRes, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting root k8s secrets: %w", err) } k8sRoot := k8sRootRes.TypedSpec() hostnameResource, err := safe.ReaderGet[*network.HostnameStatus](ctx, r, resource.NewMetadata(network.NamespaceName, network.HostnameStatusType, network.HostnameID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } hostnameStatus := hostnameResource.TypedSpec() addressesResource, err := safe.ReaderGet[*network.NodeAddress](ctx, r, resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } nodeAddresses := addressesResource.TypedSpec() if err = safe.WriterModify(ctx, r, secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANKubernetesID), func(r *secrets.CertSAN) error { spec := r.TypedSpec() spec.Reset() spec.Append(k8sRoot.Endpoint.Hostname()) spec.Append(k8sRoot.CertSANs...) spec.AppendDNSNames( "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc."+k8sRoot.DNSDomain, "localhost", ) spec.Append( hostnameStatus.Hostname, hostnameStatus.FQDN(), ) spec.AppendIPs(k8sRoot.APIServerIPs...) spec.AppendIPs(nodeAddresses.IPs()...) spec.AppendIPs(netip.MustParseAddr("127.0.0.1")) spec.Sort() return nil }); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *KubernetesCertSANsController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.CertSANType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if res.Metadata().Owner() == ctrl.Name() { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes_cert_sans_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "fmt" "net/netip" "net/url" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) type KubernetesCertSANsSuite struct { ctest.DefaultSuite } func TestKubernetesCertSANsSuite(t *testing.T) { suite.Run(t, &KubernetesCertSANsSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.KubernetesCertSANsController{})) }, }, }) } func (suite *KubernetesCertSANsSuite) TestReconcile() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) var err error rootSecrets.TypedSpec().CertSANs = []string{"example.com"} rootSecrets.TypedSpec().APIServerIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} rootSecrets.TypedSpec().DNSDomain = "cluster.remote" rootSecrets.TypedSpec().Endpoint, err = url.Parse("https://some.url:6443/") suite.Require().NoError(err) rootSecrets.TypedSpec().LocalEndpoint, err = url.Parse("https://localhost:6443/") suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), rootSecrets)) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "foo" hostnameStatus.TypedSpec().Domainname = "example.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) nodeAddresses := network.NewNodeAddress( network.NamespaceName, network.FilteredNodeAddressID(network.NodeAddressAccumulativeID, k8s.NodeAddressFilterNoK8s), ) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.2.1.3/24"), netip.MustParsePrefix("172.16.0.1/32"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeAddresses)) suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error { certSANs, err := ctest.Get[*secrets.CertSAN]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.CertSANType, secrets.CertSANKubernetesID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } spec := certSANs.TypedSpec() suite.Assert().Equal( []string{ "example.com", "foo", "foo.example.com", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.remote", "localhost", "some.url", }, spec.DNSNames, ) suite.Assert().Equal("[10.2.1.3 10.4.3.2 127.0.0.1 172.16.0.1]", fmt.Sprintf("%v", spec.IPs)) return nil }) ctest.UpdateWithConflicts(suite, rootSecrets, func(rootSecrets *secrets.KubernetesRoot) error { var err error rootSecrets.TypedSpec().Endpoint, err = url.Parse("https://some.other.url:6443/") return err }) suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error { var certSANs resource.Resource certSANs, err := ctest.Get[*secrets.CertSAN]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.CertSANType, secrets.CertSANKubernetesID, resource.VersionUndefined, ), ) if err != nil { return err } spec := certSANs.(*secrets.CertSAN).TypedSpec() expectedDNSNames := []string{ "example.com", "foo", "foo.example.com", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.remote", "localhost", "some.other.url", } if !slices.Equal(spec.DNSNames, expectedDNSNames) { return retry.ExpectedErrorf("expected %v, got %v", expectedDNSNames, spec.DNSNames) } return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes_dynamic_certs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" stdlibx509 "crypto/x509" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // KubernetesDynamicCertsController manages secrets.KubernetesDynamicCerts based on configuration. type KubernetesDynamicCertsController struct{} // Name implements controller.Controller interface. func (ctrl *KubernetesDynamicCertsController) Name() string { return "secrets.KubernetesDynamicCertsController" } // Inputs implements controller.Controller interface. func (ctrl *KubernetesDynamicCertsController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *KubernetesDynamicCertsController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.KubernetesDynamicCertsType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *KubernetesDynamicCertsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // wait for the network to be ready first, then switch to regular inputs if err := r.UpdateInputs([]controller.Input{ { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, }); err != nil { return fmt.Errorf("error updating inputs: %w", err) } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // wait for network to be ready as it might change IPs/hostname networkStatus, err := safe.ReaderGet[*network.Status](ctx, r, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } if networkStatus.TypedSpec().AddressReady && networkStatus.TypedSpec().HostnameReady { break } } // switch to regular inputs once the network is ready if err := r.UpdateInputs([]controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.KubernetesRootType, ID: optional.Some(secrets.KubernetesRootID), Kind: controller.InputWeak, }, { Namespace: v1alpha1.NamespaceName, Type: timeresource.StatusType, ID: optional.Some(timeresource.StatusID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.CertSANType, ID: optional.Some(secrets.CertSANKubernetesID), Kind: controller.InputWeak, }, }); err != nil { return fmt.Errorf("error updating inputs: %w", err) } r.QueueReconcile() refreshTicker := time.NewTicker(KubernetesCertificateValidityDuration / 2) defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-refreshTicker.C: } k8sRoot, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting root k8s secrets: %w", err) } // wait for time sync as certs depend on current time timeSync, err := safe.ReaderGet[*timeresource.Status](ctx, r, resource.NewMetadata(v1alpha1.NamespaceName, timeresource.StatusType, timeresource.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } if !timeSync.TypedSpec().Synced { continue } certSANs, err := safe.ReaderGet[*secrets.CertSAN](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.CertSANType, secrets.CertSANKubernetesID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } if err = safe.WriterModify(ctx, r, secrets.NewKubernetesDynamicCerts(), func(r *secrets.KubernetesDynamicCerts) error { return ctrl.updateSecrets(k8sRoot.TypedSpec(), r.TypedSpec(), certSANs.TypedSpec()) }); err != nil { return err } r.ResetRestartBackoff() } } func (ctrl *KubernetesDynamicCertsController) updateSecrets(k8sRoot *secrets.KubernetesRootSpec, k8sCerts *secrets.KubernetesDynamicCertsSpec, certSANs *secrets.CertSANSpec, ) error { ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sRoot.IssuingCA) if err != nil { return fmt.Errorf("failed to parse CA certificate: %w", err) } apiServer, err := x509.NewKeyPair(ca, x509.IPAddresses(certSANs.StdIPs()), x509.DNSNames(certSANs.DNSNames), x509.CommonName("kube-apiserver"), x509.Organization("kube-master"), x509.NotAfter(time.Now().Add(KubernetesCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, }), ) if err != nil { return fmt.Errorf("failed to generate api-server cert: %w", err) } k8sCerts.APIServer = x509.NewCertificateAndKeyFromKeyPair(apiServer) apiServerKubeletClient, err := x509.NewKeyPair(ca, x509.CommonName(constants.KubernetesAPIServerKubeletClientCommonName), x509.Organization(constants.KubernetesAdminCertOrganization), x509.NotAfter(time.Now().Add(KubernetesCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageClientAuth, }), ) if err != nil { return fmt.Errorf("failed to generate api-server cert: %w", err) } k8sCerts.APIServerKubeletClient = x509.NewCertificateAndKeyFromKeyPair(apiServerKubeletClient) aggregatorCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(k8sRoot.AggregatorCA) if err != nil { return fmt.Errorf("failed to parse aggregator CA: %w", err) } frontProxy, err := x509.NewKeyPair(aggregatorCA, x509.CommonName("front-proxy-client"), x509.NotAfter(time.Now().Add(KubernetesCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageClientAuth, }), ) if err != nil { return fmt.Errorf("failed to generate aggregator cert: %w", err) } k8sCerts.FrontProxy = x509.NewCertificateAndKeyFromKeyPair(frontProxy) return nil } func (ctrl *KubernetesDynamicCertsController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesDynamicCertsType, "", resource.VersionUndefined)) if err != nil { return err } // TODO: change this to proper teardown sequence for _, res := range list.Items { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes_dynamic_certs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( stdlibx509 "crypto/x509" "fmt" "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" ) func TestKubernetesDynamicCertsSuite(t *testing.T) { suite.Run(t, &KubernetesDynamicCertsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.KubernetesDynamicCertsController{})) }, }, }) } type KubernetesDynamicCertsSuite struct { ctest.DefaultSuite } func (suite *KubernetesDynamicCertsSuite) TestReconcile() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) k8sCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("kubernetes"), x509.ECDSA(true), ) suite.Require().NoError(err) aggregatorCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("kubernetes"), x509.ECDSA(true), ) suite.Require().NoError(err) serviceAccount, err := x509.NewECDSAKey() suite.Require().NoError(err) rootSecrets.TypedSpec().Name = "cluster1" rootSecrets.TypedSpec().Endpoint, err = url.Parse("https://some.url:6443/") suite.Require().NoError(err) rootSecrets.TypedSpec().LocalEndpoint, err = url.Parse("https://localhost:6443/") suite.Require().NoError(err) rootSecrets.TypedSpec().IssuingCA = &x509.PEMEncodedCertificateAndKey{ Crt: k8sCA.CrtPEM, Key: k8sCA.KeyPEM, } rootSecrets.TypedSpec().AggregatorCA = &x509.PEMEncodedCertificateAndKey{ Crt: aggregatorCA.CrtPEM, Key: aggregatorCA.KeyPEM, } rootSecrets.TypedSpec().ServiceAccount = &x509.PEMEncodedKey{ Key: serviceAccount.KeyPEM, } rootSecrets.TypedSpec().CertSANs = []string{"example.com"} rootSecrets.TypedSpec().APIServerIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} rootSecrets.TypedSpec().DNSDomain = "cluster.remote" suite.Require().NoError(suite.State().Create(suite.Ctx(), rootSecrets)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true networkStatus.TypedSpec().HostnameReady = true suite.Require().NoError(suite.State().Create(suite.Ctx(), networkStatus)) certSANs := secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANKubernetesID) certSANs.TypedSpec().Append( "example.com", "foo", "foo.example.com", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.remote", "localhost", "some.url", "10.2.1.3", "10.4.3.2", "172.16.0.1", ) suite.Require().NoError(suite.State().Create(suite.Ctx(), certSANs)) timeSync := timeresource.NewStatus() *timeSync.TypedSpec() = timeresource.StatusSpec{ Synced: true, } suite.Require().NoError(suite.State().Create(suite.Ctx(), timeSync)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.KubernetesDynamicCertsID}, func(certs *secrets.KubernetesDynamicCerts, assertion *assert.Assertions) { kubernetesCerts := certs.TypedSpec() apiCert, err := kubernetesCerts.APIServer.GetCert() assertion.NoError(err) if err != nil { return } assertion.Equal( []string{ "example.com", "foo", "foo.example.com", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.remote", "localhost", "some.url", }, apiCert.DNSNames, ) assertion.Equal("[10.2.1.3 10.4.3.2 172.16.0.1]", fmt.Sprintf("%v", apiCert.IPAddresses)) assertion.Equal("kube-apiserver", apiCert.Subject.CommonName) assertion.Equal([]string{"kube-master"}, apiCert.Subject.Organization) assertion.Equal( stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment, apiCert.KeyUsage, ) assertion.Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageServerAuth}, apiCert.ExtKeyUsage) clientCert, err := kubernetesCerts.APIServerKubeletClient.GetCert() assertion.NoError(err) if err != nil { return } assertion.Empty(clientCert.DNSNames) assertion.Empty(clientCert.IPAddresses) assertion.Equal( constants.KubernetesAPIServerKubeletClientCommonName, clientCert.Subject.CommonName, ) assertion.Equal( []string{constants.KubernetesAdminCertOrganization}, clientCert.Subject.Organization, ) assertion.Equal( stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment, clientCert.KeyUsage, ) assertion.Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageClientAuth}, clientCert.ExtKeyUsage) frontProxyCert, err := kubernetesCerts.FrontProxy.GetCert() assertion.NoError(err) if err != nil { return } assertion.Empty(frontProxyCert.DNSNames) assertion.Empty(frontProxyCert.IPAddresses) assertion.Equal("front-proxy-client", frontProxyCert.Subject.CommonName) assertion.Empty(frontProxyCert.Subject.Organization) assertion.Equal( stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment, frontProxyCert.KeyUsage, ) assertion.Equal( []stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageClientAuth}, frontProxyCert.ExtKeyUsage, ) }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/kubernetes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "net/netip" "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" ) func TestKubernetesSuite(t *testing.T) { suite.Run(t, &KubernetesSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.KubernetesController{})) }, }, }) } type KubernetesSuite struct { ctest.DefaultSuite } func (suite *KubernetesSuite) TestReconcile() { rootSecrets := secrets.NewKubernetesRoot(secrets.KubernetesRootID) k8sCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("kubernetes"), x509.ECDSA(true), ) suite.Require().NoError(err) aggregatorCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("kubernetes"), x509.ECDSA(true), ) suite.Require().NoError(err) serviceAccount, err := x509.NewECDSAKey() suite.Require().NoError(err) rootSecrets.TypedSpec().Name = "cluster1" rootSecrets.TypedSpec().Endpoint, err = url.Parse("https://some.url:6443/") suite.Require().NoError(err) rootSecrets.TypedSpec().LocalEndpoint, err = url.Parse("https://localhost:6443/") suite.Require().NoError(err) rootSecrets.TypedSpec().IssuingCA = &x509.PEMEncodedCertificateAndKey{ Crt: k8sCA.CrtPEM, Key: k8sCA.KeyPEM, } rootSecrets.TypedSpec().AggregatorCA = &x509.PEMEncodedCertificateAndKey{ Crt: aggregatorCA.CrtPEM, Key: aggregatorCA.KeyPEM, } rootSecrets.TypedSpec().ServiceAccount = &x509.PEMEncodedKey{ Key: serviceAccount.KeyPEM, } rootSecrets.TypedSpec().CertSANs = []string{"example.com"} rootSecrets.TypedSpec().APIServerIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} rootSecrets.TypedSpec().DNSDomain = "cluster.svc" suite.Require().NoError(suite.State().Create(suite.Ctx(), rootSecrets)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) timeSync := timeresource.NewStatus() *timeSync.TypedSpec() = timeresource.StatusSpec{ Synced: true, } suite.Require().NoError(suite.State().Create(suite.Ctx(), timeSync)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.KubernetesID}, func(certs *secrets.Kubernetes, assertion *assert.Assertions) { kubernetesCerts := certs.TypedSpec() for _, kubeconfig := range []string{ kubernetesCerts.ControllerManagerKubeconfig, kubernetesCerts.SchedulerKubeconfig, kubernetesCerts.LocalhostAdminKubeconfig, kubernetesCerts.AdminKubeconfig, } { config, err := clientcmd.Load([]byte(kubeconfig)) assertion.NoError(err) if err != nil { return } assertion.NoError(clientcmd.ConfirmUsable(*config, config.CurrentContext)) } }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/maintenance_cert_sans.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // MaintenanceCertSANsController manages secrets.APICertSANs based on configuration. type MaintenanceCertSANsController struct{} // Name implements controller.Controller interface. func (ctrl *MaintenanceCertSANsController) Name() string { return "secrets.MaintenanceCertSANsController" } // Inputs implements controller.Controller interface. func (ctrl *MaintenanceCertSANsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: network.NamespaceName, Type: network.HostnameStatusType, ID: optional.Some(network.HostnameID), Kind: controller.InputWeak, }, { Namespace: network.NamespaceName, Type: network.NodeAddressType, ID: optional.Some(network.NodeAddressAccumulativeID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *MaintenanceCertSANsController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.CertSANType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *MaintenanceCertSANsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } hostnameStatus, err := safe.ReaderGetByID[*network.HostnameStatus](ctx, r, network.HostnameID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get hostname status: %w", err) } nodeAddresses, err := safe.ReaderGetByID[*network.NodeAddress](ctx, r, network.NodeAddressAccumulativeID) if err != nil { if state.IsNotFoundError(err) { continue } return err } if err = safe.WriterModify(ctx, r, secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANMaintenanceID), func(r *secrets.CertSAN) error { spec := r.TypedSpec() spec.Reset() spec.AppendIPs(nodeAddresses.TypedSpec().IPs()...) spec.AppendIPs(netip.MustParseAddr("127.0.0.1")) spec.AppendIPs(netip.MustParseAddr("::1")) if hostnameStatus != nil { spec.AppendDNSNames(hostnameStatus.TypedSpec().DNSNames()...) } spec.FQDN = constants.MaintenanceServiceCommonName spec.Sort() return nil }); err != nil { return err } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/maintenance_cert_sans_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestMaintenanceCertSANsSuite(t *testing.T) { suite.Run(t, &MaintenanceCertSANsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 2 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.MaintenanceCertSANsController{})) }, }, }) } type MaintenanceCertSANsSuite struct { ctest.DefaultSuite } func (suite *MaintenanceCertSANsSuite) TestReconcile() { nodeAddresses := network.NewNodeAddress( network.NamespaceName, network.NodeAddressAccumulativeID, ) nodeAddresses.TypedSpec().Addresses = []netip.Prefix{ netip.MustParsePrefix("10.2.1.3/24"), netip.MustParsePrefix("172.16.0.1/32"), } suite.Require().NoError(suite.State().Create(suite.Ctx(), nodeAddresses)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.CertSANMaintenanceID}, func(certSANs *secrets.CertSAN, asrt *assert.Assertions) { asrt.Empty(certSANs.TypedSpec().DNSNames) asrt.Equal("[10.2.1.3 127.0.0.1 172.16.0.1 ::1]", fmt.Sprintf("%v", certSANs.TypedSpec().IPs)) asrt.Equal(constants.MaintenanceServiceCommonName, certSANs.TypedSpec().FQDN) }) hostnameStatus := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameStatus.TypedSpec().Hostname = "bar" hostnameStatus.TypedSpec().Domainname = "some.org" suite.Require().NoError(suite.State().Create(suite.Ctx(), hostnameStatus)) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.CertSANMaintenanceID}, func(certSANs *secrets.CertSAN, asrt *assert.Assertions) { asrt.Equal([]string{"bar", "bar.some.org"}, certSANs.TypedSpec().DNSNames) }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/maintenance_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/crypto/x509" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // MaintenanceRootController manages secrets.Root based on configuration. type MaintenanceRootController struct{} // Name implements controller.Controller interface. func (ctrl *MaintenanceRootController) Name() string { return "secrets.MaintenanceRootController" } // Inputs implements controller.Controller interface. func (ctrl *MaintenanceRootController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *MaintenanceRootController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.MaintenanceRootType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *MaintenanceRootController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { // run this controller only once, as the CA never changes select { case <-ctx.Done(): return nil case <-r.EventCh(): } return safe.WriterModify(ctx, r, secrets.NewMaintenanceRoot(secrets.MaintenanceRootID), func(root *secrets.MaintenanceRoot) error { ca, err := x509.NewSelfSignedCertificateAuthority() if err != nil { return fmt.Errorf("failed to generate self-signed CA: %w", err) } root.TypedSpec().CA = x509.NewCertificateAndKeyFromCertificateAuthority(ca) return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/maintenance_root_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestMaintenanceRootSuite(t *testing.T) { suite.Run(t, &MaintenanceRootSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.MaintenanceRootController{})) }, }, }) } type MaintenanceRootSuite struct { ctest.DefaultSuite } func (suite *MaintenanceRootSuite) TestReconcile() { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.MaintenanceRootID}, func(root *secrets.MaintenanceRoot, asrt *assert.Assertions) { asrt.NotEmpty(root.TypedSpec().CA) }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "errors" "fmt" "net/netip" "net/url" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/controller/generic" "github.com/cosi-project/runtime/pkg/controller/generic/transform" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func rootMapFunc[Output generic.ResourceWithRD](output Output, requireControlPlane bool) func(cfg *config.MachineConfig) optional.Optional[Output] { return func(cfg *config.MachineConfig) optional.Optional[Output] { if cfg.Metadata().ID() != config.ActiveID { return optional.None[Output]() } if cfg.Config().Cluster() == nil || cfg.Config().Machine() == nil { return optional.None[Output]() } if requireControlPlane && !cfg.Config().Machine().Type().IsControlPlane() { return optional.None[Output]() } return optional.Some(output) } } // RootEtcdController manages secrets.EtcdRoot based on configuration. type RootEtcdController = transform.Controller[*config.MachineConfig, *secrets.EtcdRoot] // NewRootEtcdController instanciates the controller. func NewRootEtcdController() *RootEtcdController { return transform.NewController( transform.Settings[*config.MachineConfig, *secrets.EtcdRoot]{ Name: "secrets.RootEtcdController", MapMetadataOptionalFunc: rootMapFunc(secrets.NewEtcdRoot(secrets.EtcdRootID), true), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *secrets.EtcdRoot) error { cfgProvider := cfg.Config() etcdSecrets := res.TypedSpec() etcdSecrets.EtcdCA = cfgProvider.Cluster().Etcd().CA() if etcdSecrets.EtcdCA == nil { return errors.New("missing cluster.etcdCA secret") } return nil }, }, ) } // RootKubernetesController manages secrets.KubernetesRoot based on configuration. type RootKubernetesController = transform.Controller[*config.MachineConfig, *secrets.KubernetesRoot] // NewRootKubernetesController instanciates the controller. func NewRootKubernetesController() *RootKubernetesController { return transform.NewController( transform.Settings[*config.MachineConfig, *secrets.KubernetesRoot]{ Name: "secrets.RootKubernetesController", MapMetadataOptionalFunc: rootMapFunc(secrets.NewKubernetesRoot(secrets.KubernetesRootID), true), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *secrets.KubernetesRoot) error { cfgProvider := cfg.Config() k8sSecrets := res.TypedSpec() var ( err error localEndpoint *url.URL ) if cfgProvider.Machine().Features().KubePrism().Enabled() { localEndpoint, err = url.Parse(fmt.Sprintf("https://127.0.0.1:%d", cfgProvider.Machine().Features().KubePrism().Port())) if err != nil { return err } } else { localEndpoint, err = url.Parse(fmt.Sprintf("https://localhost:%d", cfgProvider.Cluster().LocalAPIServerPort())) if err != nil { return err } } k8sSecrets.Name = cfgProvider.Cluster().Name() k8sSecrets.Endpoint = cfgProvider.Cluster().Endpoint() k8sSecrets.LocalEndpoint = localEndpoint k8sSecrets.CertSANs = cfgProvider.Cluster().CertSANs() k8sSecrets.DNSDomain = cfgProvider.Cluster().Network().DNSDomain() k8sSecrets.APIServerIPs, err = cfgProvider.Cluster().Network().APIServerIPs() if err != nil { return fmt.Errorf("error building API service IPs: %w", err) } k8sSecrets.AggregatorCA = cfgProvider.Cluster().AggregatorCA() if k8sSecrets.AggregatorCA == nil { return errors.New("missing cluster.aggregatorCA secret") } k8sSecrets.IssuingCA = cfgProvider.Cluster().IssuingCA() k8sSecrets.AcceptedCAs = cfgProvider.Cluster().AcceptedCAs() if k8sSecrets.IssuingCA != nil { k8sSecrets.AcceptedCAs = append(k8sSecrets.AcceptedCAs, &x509.PEMEncodedCertificate{ Crt: k8sSecrets.IssuingCA.Crt, }) } if len(k8sSecrets.IssuingCA.Key) == 0 { // drop incomplete issuing CA, as the machine config for workers contains just the cert k8sSecrets.IssuingCA = nil } if len(k8sSecrets.AcceptedCAs) == 0 { return errors.New("missing cluster.CA secret") } k8sSecrets.ServiceAccount = cfgProvider.Cluster().ServiceAccount() k8sSecrets.AESCBCEncryptionSecret = cfgProvider.Cluster().AESCBCEncryptionSecret() k8sSecrets.SecretboxEncryptionSecret = cfgProvider.Cluster().SecretboxEncryptionSecret() k8sSecrets.BootstrapTokenID = cfgProvider.Cluster().Token().ID() k8sSecrets.BootstrapTokenSecret = cfgProvider.Cluster().Token().Secret() return nil }, }, ) } // RootOSController manages secrets.OSRoot based on configuration. type RootOSController = transform.Controller[*config.MachineConfig, *secrets.OSRoot] // NewRootOSController instanciates the controller. func NewRootOSController() *RootOSController { return transform.NewController( transform.Settings[*config.MachineConfig, *secrets.OSRoot]{ Name: "secrets.RootOSController", MapMetadataOptionalFunc: rootMapFunc(secrets.NewOSRoot(secrets.OSRootID), false), TransformFunc: func(ctx context.Context, r controller.Reader, logger *zap.Logger, cfg *config.MachineConfig, res *secrets.OSRoot) error { cfgProvider := cfg.Config() osSecrets := res.TypedSpec() osSecrets.IssuingCA = cfgProvider.Machine().Security().IssuingCA() osSecrets.AcceptedCAs = cfgProvider.Machine().Security().AcceptedCAs() if osSecrets.IssuingCA != nil { osSecrets.AcceptedCAs = append(osSecrets.AcceptedCAs, &x509.PEMEncodedCertificate{ Crt: osSecrets.IssuingCA.Crt, }) if len(osSecrets.IssuingCA.Key) == 0 { // drop incomplete issuing CA, as the machine config for workers contains just the cert osSecrets.IssuingCA = nil } } osSecrets.CertSANIPs = nil osSecrets.CertSANDNSNames = nil for _, san := range cfgProvider.Machine().Security().CertSANs() { if ip, err := netip.ParseAddr(san); err == nil { osSecrets.CertSANIPs = append(osSecrets.CertSANIPs, ip) } else { osSecrets.CertSANDNSNames = append(osSecrets.CertSANDNSNames, san) } } if cfgProvider.Machine().Features().KubernetesTalosAPIAccess().Enabled() { // add Kubernetes Talos service name to the list of SANs osSecrets.CertSANDNSNames = append(osSecrets.CertSANDNSNames, constants.KubernetesTalosAPIServiceName, constants.KubernetesTalosAPIServiceName+"."+constants.KubernetesTalosAPIServiceNamespace, ) } osSecrets.Token = cfgProvider.Machine().Security().Token() return nil }, }, ) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/root_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestRootSuite(t *testing.T) { t.Parallel() suite.Run(t, &RootSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(secretsctrl.NewRootEtcdController())) suite.Require().NoError(suite.Runtime().RegisterController(secretsctrl.NewRootKubernetesController())) suite.Require().NoError(suite.Runtime().RegisterController(secretsctrl.NewRootOSController())) }, }, }) } type RootSuite struct { ctest.DefaultSuite } func (suite *RootSuite) genConfig(controlplane bool) talosconfig.Config { input, err := generate.NewInput("test-cluster", "http://localhost:6443", "") suite.Require().NoError(err) var cfg talosconfig.Provider if controlplane { cfg, err = input.Config(machine.TypeControlPlane) } else { cfg, err = input.Config(machine.TypeWorker) } suite.Require().NoError(err) machineCfg := config.NewMachineConfig(cfg) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineCfg)) return cfg } func (suite *RootSuite) TestReconcileControlPlane() { cfg := suite.genConfig(true) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.EtcdRootID}, func(res *secrets.EtcdRoot, asrt *assert.Assertions) { asrt.Equal(res.TypedSpec().EtcdCA, cfg.Cluster().Etcd().CA()) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.KubernetesRootID}, func(res *secrets.KubernetesRoot, asrt *assert.Assertions) { asrt.Equal(res.TypedSpec().IssuingCA, cfg.Cluster().IssuingCA()) asrt.Equal( []*x509.PEMEncodedCertificate{ { Crt: cfg.Cluster().IssuingCA().Crt, }, }, res.TypedSpec().AcceptedCAs, ) }, ) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.OSRootID}, func(res *secrets.OSRoot, asrt *assert.Assertions) { asrt.Equal(res.TypedSpec().IssuingCA, cfg.Machine().Security().IssuingCA()) asrt.Equal( []*x509.PEMEncodedCertificate{ { Crt: cfg.Machine().Security().IssuingCA().Crt, }, }, res.TypedSpec().AcceptedCAs, ) }, ) } func (suite *RootSuite) TestReconcileWorker() { cfg := suite.genConfig(false) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{secrets.OSRootID}, func(res *secrets.OSRoot, asrt *assert.Assertions) { asrt.Nil(res.TypedSpec().IssuingCA) asrt.Equal( []*x509.PEMEncodedCertificate{ { Crt: cfg.Machine().Security().IssuingCA().Crt, }, }, res.TypedSpec().AcceptedCAs, ) }, ) rtestutils.AssertNoResource[*secrets.Etcd](suite.Ctx(), suite.T(), suite.State(), secrets.EtcdRootID) rtestutils.AssertNoResource[*secrets.Kubernetes](suite.Ctx(), suite.T(), suite.State(), secrets.KubernetesRootID) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/secrets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package secrets provides controllers which manage secret resources. package secrets ================================================ FILE: internal/app/machined/pkg/controllers/secrets/trustd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" stdlibx509 "crypto/x509" "errors" "fmt" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // TrustdController manages secrets.API based on configuration to provide apid certificate. type TrustdController struct{} // Name implements controller.Controller interface. func (ctrl *TrustdController) Name() string { return "secrets.TrustdController" } // Inputs implements controller.Controller interface. func (ctrl *TrustdController) Inputs() []controller.Input { // initial set of inputs: wait for machine type to be known and network to be partially configured return []controller.Input{ { Namespace: network.NamespaceName, Type: network.StatusType, ID: optional.Some(network.StatusID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *TrustdController) Outputs() []controller.Output { return []controller.Output{ { Type: secrets.TrustdType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *TrustdController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } // reset inputs back to what they were initially if err := r.UpdateInputs(ctrl.Inputs()); err != nil { return err } machineTypeRes, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting machine type: %w", err) } machineType := machineTypeRes.MachineType() networkResource, err := safe.ReaderGet[*network.Status](ctx, r, resource.NewMetadata(network.NamespaceName, network.StatusType, network.StatusID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return err } networkStatus := networkResource.TypedSpec() if !(networkStatus.AddressReady && networkStatus.HostnameReady) { continue } // machine type is known and network is ready, we can now proceed to one or another reconcile loop if machineType.IsControlPlane() { if err = ctrl.reconcile(ctx, r, logger); err != nil { return err } } else { if err = ctrl.teardownAll(ctx, r); err != nil { return err } } r.ResetRestartBackoff() } } //nolint:gocyclo,dupl func (ctrl *TrustdController) reconcile(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { inputs := []controller.Input{ { Namespace: secrets.NamespaceName, Type: secrets.OSRootType, ID: optional.Some(secrets.OSRootID), Kind: controller.InputWeak, }, { Namespace: secrets.NamespaceName, Type: secrets.CertSANType, ID: optional.Some(secrets.CertSANAPIID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineTypeType, ID: optional.Some(config.MachineTypeID), Kind: controller.InputWeak, }, // time status isn't fetched, but the fact that it is in dependencies means // that certs will be regenerated on time sync/jump (as reconcile will be triggered) { Namespace: v1alpha1.NamespaceName, Type: timeresource.StatusType, ID: optional.Some(timeresource.StatusID), Kind: controller.InputWeak, }, } if err := r.UpdateInputs(inputs); err != nil { return fmt.Errorf("error updating inputs: %w", err) } r.QueueReconcile() refreshTicker := time.NewTicker(x509.DefaultCertificateValidityDuration / 2) defer refreshTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-refreshTicker.C: } machineTypeRes, err := safe.ReaderGet[*config.MachineType](ctx, r, resource.NewMetadata(config.NamespaceName, config.MachineTypeType, config.MachineTypeID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting machine type: %w", err) } machineType := machineTypeRes.MachineType() if !machineType.IsControlPlane() { return errors.New("machine type changed") } rootResource, err := safe.ReaderGet[*secrets.OSRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { if err = ctrl.teardownAll(ctx, r); err != nil { return fmt.Errorf("error destroying resources: %w", err) } continue } return fmt.Errorf("error getting etcd root secrets: %w", err) } rootSpec := rootResource.TypedSpec() certSANResource, err := safe.ReaderGet[*secrets.CertSAN](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.CertSANType, secrets.CertSANAPIID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { continue } return fmt.Errorf("error getting certSANs: %w", err) } certSANs := certSANResource.TypedSpec() if err := ctrl.generateControlPlane(ctx, r, logger, rootSpec, certSANs); err != nil { return err } } } func (ctrl *TrustdController) generateControlPlane(ctx context.Context, r controller.Runtime, logger *zap.Logger, rootSpec *secrets.OSRootSpec, certSANs *secrets.CertSANSpec) error { ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(rootSpec.IssuingCA) if err != nil { return fmt.Errorf("failed to parse CA certificate: %w", err) } serverCert, err := x509.NewKeyPair(ca, x509.IPAddresses(certSANs.StdIPs()), x509.DNSNames(certSANs.DNSNames), x509.CommonName(certSANs.FQDN), x509.NotAfter(time.Now().Add(x509.DefaultCertificateValidityDuration)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, }), ) if err != nil { return fmt.Errorf("failed to generate API server cert: %w", err) } if err := safe.WriterModify(ctx, r, secrets.NewTrustd(), func(r *secrets.Trustd) error { trustdSecrets := r.TypedSpec() trustdSecrets.AcceptedCAs = rootSpec.AcceptedCAs trustdSecrets.Server = x509.NewCertificateAndKeyFromKeyPair(serverCert) return nil }); err != nil { return fmt.Errorf("error modifying resource: %w", err) } serverFingerprint, _ := x509.SPKIFingerprintFromDER(serverCert.Certificate.Certificate[0]) //nolint:errcheck logger.Debug("generated new certificates", zap.Stringer("server", serverFingerprint), ) return nil } func (ctrl *TrustdController) teardownAll(ctx context.Context, r controller.Runtime) error { list, err := r.List(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.TrustdType, "", resource.VersionUndefined)) if err != nil { return err } for _, res := range list.Items { if err = r.Destroy(ctx, res.Metadata()); err != nil { return err } } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/trustd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( stdlibx509 "crypto/x509" "fmt" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestTrustdSuite(t *testing.T) { t.Parallel() suite.Run(t, &TrustdSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.TrustdController{})) }, }, }) } type TrustdSuite struct { ctest.DefaultSuite } func (suite *TrustdSuite) TestReconcileControlPlane() { rootSecrets := secrets.NewOSRoot(secrets.OSRootID) talosCA, err := x509.NewSelfSignedCertificateAuthority( x509.Organization("talos"), ) suite.Require().NoError(err) rootSecrets.TypedSpec().IssuingCA = &x509.PEMEncodedCertificateAndKey{ Crt: talosCA.CrtPEM, Key: talosCA.KeyPEM, } rootSecrets.TypedSpec().AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: talosCA.CrtPEM, }, } rootSecrets.TypedSpec().CertSANDNSNames = []string{"example.com"} rootSecrets.TypedSpec().CertSANIPs = []netip.Addr{netip.MustParseAddr("10.4.3.2"), netip.MustParseAddr("10.2.1.3")} rootSecrets.TypedSpec().Token = "something" suite.Require().NoError(suite.State().Create(suite.Ctx(), rootSecrets)) machineType := config.NewMachineType() machineType.SetMachineType(machine.TypeControlPlane) suite.Require().NoError(suite.State().Create(suite.Ctx(), machineType)) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true networkStatus.TypedSpec().HostnameReady = true suite.Require().NoError(suite.State().Create(suite.Ctx(), networkStatus)) certSANs := secrets.NewCertSAN(secrets.NamespaceName, secrets.CertSANAPIID) certSANs.TypedSpec().Append( "example.com", "foo", "foo.example.com", "10.2.1.3", "10.4.3.2", "172.16.0.1", ) certSANs.TypedSpec().FQDN = "foo.example.com" suite.Require().NoError(suite.State().Create(suite.Ctx(), certSANs)) suite.AssertWithin(10*time.Second, 100*time.Millisecond, func() error { certs, err := ctest.Get[*secrets.Trustd]( suite, resource.NewMetadata( secrets.NamespaceName, secrets.TrustdType, secrets.TrustdID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } trustdCerts := certs.TypedSpec() suite.Assert().Equal( []*x509.PEMEncodedCertificate{ { Crt: talosCA.CrtPEM, }, }, trustdCerts.AcceptedCAs, ) serverCert, err := trustdCerts.Server.GetCert() suite.Require().NoError(err) suite.Assert().Equal([]string{"example.com", "foo", "foo.example.com"}, serverCert.DNSNames) suite.Assert().Equal("[10.2.1.3 10.4.3.2 172.16.0.1]", fmt.Sprintf("%v", serverCert.IPAddresses)) suite.Assert().Equal("foo.example.com", serverCert.Subject.CommonName) suite.Assert().Empty(serverCert.Subject.Organization) suite.Assert().Equal( stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment, serverCert.KeyUsage, ) suite.Assert().Equal([]stdlibx509.ExtKeyUsage{stdlibx509.ExtKeyUsageServerAuth}, serverCert.ExtKeyUsage) return nil }) } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/trusted_roots.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" _ "embed" "fmt" "slices" "strings" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) //go:embed data/ca-certificates var defaultCACertificates []byte // TrustedRootsController manages CA trusted roots based on configuration. type TrustedRootsController struct{} // Name implements controller.Controller interface. func (ctrl *TrustedRootsController) Name() string { return "secrets.TrustedRootsController" } // Inputs implements controller.Controller interface. func (ctrl *TrustedRootsController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *TrustedRootsController) Outputs() []controller.Output { return []controller.Output{ { Type: files.EtcFileSpecType, Kind: controller.OutputShared, }, } } // Run implements controller.Controller interface. func (ctrl *TrustedRootsController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get machine config: %w", err) } contents := slices.Clone(defaultCACertificates) if cfg != nil { contents = slices.Concat(contents, []byte(strings.Join(cfg.Config().TrustedRoots().ExtraTrustedRootCertificates(), "\n\n"))) } if err = safe.WriterModify(ctx, r, files.NewEtcFileSpec(files.NamespaceName, constants.DefaultTrustedRelativeCAFile), func(spec *files.EtcFileSpec) error { spec.TypedSpec().Mode = 0o644 spec.TypedSpec().Contents = contents return nil }); err != nil { return fmt.Errorf("failed to write trusted roots: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/secrets/trusted_roots_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" secretsctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) func TestTrustedRootsSuite(t *testing.T) { t.Parallel() suite.Run(t, &TrustedRootsSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 10 * time.Second, AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&secretsctrl.TrustedRootsController{})) }, }, }) } type TrustedRootsSuite struct { ctest.DefaultSuite } func (suite *TrustedRootsSuite) TestReconcileDefault() { ctest.AssertResources(suite, []string{constants.DefaultTrustedRelativeCAFile}, func(r *files.EtcFileSpec, asrt *assert.Assertions) { asrt.EqualValues(0o644, r.TypedSpec().Mode) asrt.Contains(string(r.TypedSpec().Contents), "Bundle of CA Root Certificates") }) } func (suite *TrustedRootsSuite) TestReconcileExtraCAs() { trustedRoot1 := security.NewTrustedRootsConfigV1Alpha1() trustedRoot1.MetaName = "root1" trustedRoot1.Certificates = "-- BEGIN1 --" trustedRoot2 := security.NewTrustedRootsConfigV1Alpha1() trustedRoot2.MetaName = "root2" trustedRoot2.Certificates = "-- BEGIN2 --" cfg, err := container.New(trustedRoot1, trustedRoot2) suite.Require().NoError(err) mc := config.NewMachineConfig(cfg) suite.Require().NoError(suite.State().Create(suite.Ctx(), mc)) ctest.AssertResources(suite, []string{constants.DefaultTrustedRelativeCAFile}, func(r *files.EtcFileSpec, asrt *assert.Assertions) { asrt.EqualValues(0o644, r.TypedSpec().Mode) asrt.Contains(string(r.TypedSpec().Contents), "Bundle of CA Root Certificates") for _, contains := range []string{ trustedRoot1.MetaName, trustedRoot1.Certificates, trustedRoot2.MetaName, trustedRoot2.Certificates, } { asrt.Contains(string(r.TypedSpec().Contents), contains) } }) } ================================================ FILE: internal/app/machined/pkg/controllers/security/image_verification_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package security import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" configres "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) // ImageVerificationConfigController watches machine config and produces ImageVerificationRule resource. type ImageVerificationConfigController struct{} // Name implements controller.Controller interface. func (ctrl *ImageVerificationConfigController) Name() string { return "security.ImageVerificationConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ImageVerificationConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: configres.NamespaceName, Type: configres.MachineConfigType, ID: optional.Some(configres.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ImageVerificationConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: security.ImageVerificationRuleType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ImageVerificationConfigController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } r.StartTrackingOutputs() machineConfig, err := safe.ReaderGetByID[*configres.MachineConfig](ctx, r, configres.ActiveID) if err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("failed to get machine config: %w", err) } if machineConfig != nil { if cfg := machineConfig.Config().ImageVerificationConfig(); cfg != nil { for idx, rule := range cfg.Rules() { if err := safe.WriterModify(ctx, r, security.NewImageVerificationRule(fmt.Sprintf("%04d", idx)), func(r *security.ImageVerificationRule) error { r.TypedSpec().ImagePattern = rule.ImagePattern() r.TypedSpec().Skip = rule.Skip() r.TypedSpec().Deny = rule.Deny() if kv := rule.VerifierKeyless(); kv != nil { r.TypedSpec().KeylessVerifier = &security.ImageKeylessVerifierSpec{ Issuer: kv.Issuer(), Subject: kv.Subject(), SubjectRegex: kv.SubjectRegex(), } } else { r.TypedSpec().KeylessVerifier = nil } if cv := rule.VerifierPublicKey(); cv != nil { r.TypedSpec().PublicKeyVerifier = &security.ImagePublicKeyVerifierSpec{ Certificate: cv.Certificate(), } } else { r.TypedSpec().PublicKeyVerifier = nil } return nil }, ); err != nil { return fmt.Errorf("failed to create/update image verification rule: %w", err) } } } } if err := safe.CleanupOutputs[*security.ImageVerificationRule](ctx, r); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } ================================================ FILE: internal/app/machined/pkg/controllers/security/image_verification_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" securityctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/security" "github.com/siderolabs/talos/pkg/machinery/config/container" securitycfg "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) type ImageVerificationConfigSuite struct { ctest.DefaultSuite } func (suite *ImageVerificationConfigSuite) TestReconcileNoConfig() { ctest.AssertNoResource[*security.ImageVerificationRule](suite, "0000") } func (suite *ImageVerificationConfigSuite) TestReconcileNoVerificationConfig() { cfg := config.NewMachineConfig(container.NewV1Alpha1(&v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, })) suite.Create(cfg) ctest.AssertNoResource[*security.ImageVerificationRule](suite, "0000") } func (suite *ImageVerificationConfigSuite) TestReconcileWithRules() { verificationCfg := securitycfg.NewImageVerificationConfigV1Alpha1() verificationCfg.ConfigRules = []securitycfg.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", RuleSkip: new(true), }, { RuleImagePattern: "ghcr.io/myorg/*", RuleKeylessVerifier: &securitycfg.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/myorg/.*", }, }, { RuleImagePattern: "quay.io/*", RulePublicKeyVerifier: &securitycfg.ImagePublicKeyVerifierV1Alpha1{ ConfigCertificate: "TEST", }, }, } cont, err := container.New(verificationCfg) suite.Require().NoError(err) cfg := config.NewMachineConfig(cont) suite.Create(cfg) ctest.AssertResource(suite, "0000", func(rule *security.ImageVerificationRule, asrt *assert.Assertions) { asrt.Equal("docker.io/*", rule.TypedSpec().ImagePattern) asrt.True(rule.TypedSpec().Skip) asrt.False(rule.TypedSpec().Deny) asrt.Nil(rule.TypedSpec().KeylessVerifier) asrt.Nil(rule.TypedSpec().PublicKeyVerifier) }) ctest.AssertResource(suite, "0001", func(rule *security.ImageVerificationRule, asrt *assert.Assertions) { asrt.Equal("ghcr.io/myorg/*", rule.TypedSpec().ImagePattern) asrt.False(rule.TypedSpec().Skip) asrt.False(rule.TypedSpec().Deny) asrt.NotNil(rule.TypedSpec().KeylessVerifier) asrt.Equal("https://token.actions.githubusercontent.com", rule.TypedSpec().KeylessVerifier.Issuer) asrt.Equal("https://github.com/myorg/.*", rule.TypedSpec().KeylessVerifier.SubjectRegex) asrt.Nil(rule.TypedSpec().PublicKeyVerifier) }) ctest.AssertResource(suite, "0002", func(rule *security.ImageVerificationRule, asrt *assert.Assertions) { asrt.Equal("quay.io/*", rule.TypedSpec().ImagePattern) asrt.False(rule.TypedSpec().Skip) asrt.False(rule.TypedSpec().Deny) asrt.Nil(rule.TypedSpec().KeylessVerifier) asrt.NotNil(rule.TypedSpec().PublicKeyVerifier) asrt.Equal("TEST", rule.TypedSpec().PublicKeyVerifier.Certificate) }) suite.Destroy(cfg) ctest.AssertNoResource[*security.ImageVerificationRule](suite, "0000") ctest.AssertNoResource[*security.ImageVerificationRule](suite, "0001") ctest.AssertNoResource[*security.ImageVerificationRule](suite, "0002") } func TestImageVerificationConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ImageVerificationConfigSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&securityctrl.ImageVerificationConfigController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/security/tuf_trusted_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package security import ( "context" "fmt" "net/http" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-cleanhttp" "github.com/sigstore/sigstore-go/pkg/tuf" "github.com/theupdateframework/go-tuf/v2/metadata/fetcher" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) // TUFTrustedRootController fetches root TUF trusted roots. type TUFTrustedRootController struct { RefreshInterval time.Duration lastRefresh time.Time } // DefaultTUFRefreshInterval is the default interval for refreshing TUF trusted roots. const DefaultTUFRefreshInterval = 24 * time.Hour // Name implements controller.Controller interface. func (ctrl *TUFTrustedRootController) Name() string { return "security.TUFTrustedRootController" } // Inputs implements controller.Controller interface. func (ctrl *TUFTrustedRootController) Inputs() []controller.Input { return []controller.Input{ { Namespace: security.NamespaceName, Type: security.ImageVerificationRuleType, Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *TUFTrustedRootController) Outputs() []controller.Output { return []controller.Output{ { Type: security.TUFTrustedRootType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *TUFTrustedRootController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.RefreshInterval == 0 { ctrl.RefreshInterval = DefaultTUFRefreshInterval } ticker := time.NewTicker(ctrl.RefreshInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ticker.C: } // first, determine if we need to fetch TUF at all rules, err := safe.ReaderListAll[*security.ImageVerificationRule](ctx, r) if err != nil { return fmt.Errorf("failed to list image verification rules: %w", err) } needsTUF := false for rule := range rules.All() { if rule.TypedSpec().KeylessVerifier != nil { needsTUF = true break } } // suppress TUF refresh if not needed if needsTUF && time.Since(ctrl.lastRefresh) < ctrl.RefreshInterval { continue } r.StartTrackingOutputs() if needsTUF { tufData, err := ctrl.getTrustedRootTarget(security.TrustedRootID) if err != nil { return fmt.Errorf("failed to get TUF trusted root: %w", err) } ctrl.lastRefresh = time.Now() if err := safe.WriterModify(ctx, r, security.NewTUFTrustedRoot(security.TrustedRootID), func(root *security.TUFTrustedRoot) error { root.TypedSpec().JSONData = string(tufData) root.TypedSpec().LastRefreshTime = ctrl.lastRefresh return nil }, ); err != nil { return fmt.Errorf("failed to create/update TUF trusted root: %w", err) } logger.Info("refreshed TUF trusted root") } else { // we are going to remove TUF, so reset last refresh time ctrl.lastRefresh = time.Time{} } if err := safe.CleanupOutputs[*security.TUFTrustedRoot](ctx, r); err != nil { return fmt.Errorf("failed to cleanup outputs: %w", err) } } } func (ctrl *TUFTrustedRootController) getTrustedRootTarget(id string) ([]byte, error) { transport := httpdefaults.PatchTransport(cleanhttp.DefaultTransport()) httpClient := &http.Client{ Transport: transport, } fetcher := fetcher.NewDefaultFetcher() fetcher.SetHTTPClient(httpClient) fetcher.SetHTTPUserAgent(httpdefaults.UserAgent()) opts := tuf.Options{ Root: tuf.DefaultRoot(), RepositoryBaseURL: tuf.DefaultMirror, DisableLocalCache: true, Fetcher: fetcher, } client, err := tuf.New(&opts) if err != nil { return nil, fmt.Errorf("failed to create TUF client: %w", err) } return client.GetTarget(id) } ================================================ FILE: internal/app/machined/pkg/controllers/security/tuf_trusted_root_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security_test import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" securityctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/security" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) type TUFTrustedRootSuite struct { ctest.DefaultSuite } func (suite *TUFTrustedRootSuite) TestReconcileNoConfig() { ctest.AssertNoResource[*security.TUFTrustedRoot](suite, security.TrustedRootID) } func (suite *TUFTrustedRootSuite) TestReconcileNoKeylessRules() { rule := security.NewImageVerificationRule("0000") rule.TypedSpec().ImagePattern = "ghcr.io/myorg/*" suite.Create(rule) ctest.AssertNoResource[*security.TUFTrustedRoot](suite, security.TrustedRootID) } func (suite *TUFTrustedRootSuite) TestReconcileWithRules() { rule := security.NewImageVerificationRule("0000") rule.TypedSpec().ImagePattern = "ghcr.io/myorg/*" rule.TypedSpec().KeylessVerifier = &security.ImageKeylessVerifierSpec{ Issuer: "https://token.actions.githubusercontent.com", SubjectRegex: "https://github.com/myorg/.*", } suite.Create(rule) ctest.AssertResource(suite, security.TrustedRootID, func(root *security.TUFTrustedRoot, asrt *assert.Assertions) { asrt.NotEmpty(root.TypedSpec().JSONData) }) suite.Destroy(rule) ctest.AssertNoResource[*security.TUFTrustedRoot](suite, security.TrustedRootID) } func TestTUFTrustedRootSuite(t *testing.T) { t.Parallel() suite.Run(t, &TUFTrustedRootSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 5 * time.Second, AfterSetup: func(s *ctest.DefaultSuite) { s.Require().NoError(s.Runtime().RegisterController(&securityctrl.TUFTrustedRootController{})) }, }, }) } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/endpoint" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) // ConfigController interacts with SideroLink API and brings up the SideroLink Wireguard interface. type ConfigController struct { Cmdline *procfs.Cmdline V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *ConfigController) Name() string { return "siderolink.ConfigController" } // Inputs implements controller.Controller interface. func (ctrl *ConfigController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *ConfigController) Outputs() []controller.Output { return []controller.Output{ { Type: siderolink.ConfigType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ConfigController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { for { select { case <-ctx.Done(): return nil case <-r.EventCh(): } cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil && !state.IsNotFoundError(err) { return err } r.StartTrackingOutputs() if ep := ctrl.apiEndpoint(cfg); ep != "" { var parsed endpoint.Endpoint parsed, err = endpoint.Parse(ep) if err != nil { return fmt.Errorf("failed to parse siderolink API endpoint: %w", err) } if err = safe.WriterModify(ctx, r, siderolink.NewConfig(config.NamespaceName, siderolink.ConfigID), func(c *siderolink.Config) error { c.TypedSpec().APIEndpoint = ep c.TypedSpec().Host = parsed.Host c.TypedSpec().JoinToken = parsed.GetParam("jointoken") c.TypedSpec().Insecure = parsed.Insecure c.TypedSpec().Tunnel = parsed.GetParam("grpc_tunnel") == "true" || parsed.GetParam("grpc_tunnel") == "y" return nil }); err != nil { return fmt.Errorf("failed to update config: %w", err) } } if err = safe.CleanupOutputs[*siderolink.Config](ctx, r); err != nil { return err } } } func (ctrl *ConfigController) apiEndpoint(machineConfig *config.MachineConfig) string { if machineConfig != nil && machineConfig.Config().SideroLink() != nil && machineConfig.Config().SideroLink().APIUrl() != nil { return machineConfig.Config().SideroLink().APIUrl().String() } if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { return "" } if ctrl.Cmdline == nil || ctrl.Cmdline.Get(constants.KernelParamSideroLink).First() == nil { return "" } return *ctrl.Cmdline.Get(constants.KernelParamSideroLink).First() } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink_test import ( "net/url" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" siderolinkctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/siderolink" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" siderolinkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) type ConfigSuite struct { ctest.DefaultSuite } func TestConfigSuite(t *testing.T) { t.Parallel() suite.Run(t, &ConfigSuite{ DefaultSuite: ctest.DefaultSuite{ AfterSetup: func(suite *ctest.DefaultSuite) { suite.Require().NoError(suite.Runtime().RegisterController(&siderolinkctrl.ConfigController{})) }, Timeout: time.Second, }, }) } func (suite *ConfigSuite) TestConfig() { rtestutils.AssertNoResource[*siderolink.Config](suite.Ctx(), suite.T(), suite.State(), siderolink.ConfigID) siderolinkConfig := &siderolinkcfg.ConfigV1Alpha1{ APIUrlConfig: meta.URL{ URL: must.Value(url.Parse("https://api.sidero.dev:334"))(suite.T()), }, } cfg, err := container.New(siderolinkConfig) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{siderolink.ConfigID}, func(c *siderolink.Config, assert *assert.Assertions) { assert.Equal("https://api.sidero.dev:334", c.TypedSpec().APIEndpoint) assert.Equal("api.sidero.dev:334", c.TypedSpec().Host) }) } func (suite *ConfigSuite) TestConfigTunnel() { rtestutils.AssertNoResource[*siderolink.Config](suite.Ctx(), suite.T(), suite.State(), siderolink.ConfigID) siderolinkConfig := &siderolinkcfg.ConfigV1Alpha1{ APIUrlConfig: meta.URL{ URL: must.Value(url.Parse("https://api.sidero.dev?grpc_tunnel=true"))(suite.T()), }, } cfg, err := container.New(siderolinkConfig) suite.Require().NoError(err) suite.Require().NoError(suite.State().Create(suite.Ctx(), config.NewMachineConfig(cfg))) rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{siderolink.ConfigID}, func(c *siderolink.Config, assert *assert.Assertions) { assert.Equal("https://api.sidero.dev?grpc_tunnel=true", c.TypedSpec().APIEndpoint) assert.True(c.TypedSpec().Tunnel) }) } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/manager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "bytes" "context" "crypto/tls" "errors" "fmt" "net" "net/netip" "os" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" pb "github.com/siderolabs/siderolink/api/siderolink" "github.com/siderolabs/siderolink/pkg/wireguard" "go.uber.org/zap" "golang.zx2c4.com/wireguard/wgctrl" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" networkutils "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network/utils" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/client/dialer" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" "github.com/siderolabs/talos/pkg/machinery/version" ) // ManagerController interacts with SideroLink API and brings up the SideroLink Wireguard interface. type ManagerController struct { nodeKey wgtypes.Key pd provisionData } // Name implements controller.Controller interface. func (ctrl *ManagerController) Name() string { return "siderolink.ManagerController" } // Inputs implements controller.Controller interface. func (ctrl *ManagerController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *ManagerController) Outputs() []controller.Output { return []controller.Output{ { Type: network.AddressSpecType, Kind: controller.OutputShared, }, { Type: network.LinkSpecType, Kind: controller.OutputShared, }, { Type: siderolink.TunnelType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *ManagerController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if fipsmode.Strict() { logger.Warn("SideroLink is not supported in strict FIPS mode") return nil } // initially, wait for the network address status to be ready if err := networkutils.WaitForNetworkReady(ctx, r, func(status *network.StatusSpec) bool { return status.AddressReady }, []controller.Input{ { Namespace: config.NamespaceName, Type: siderolink.ConfigType, ID: optional.Some(siderolink.ConfigID), Kind: controller.InputWeak, }, { Namespace: hardware.NamespaceName, Type: hardware.SystemInformationType, ID: optional.Some(hardware.SystemInformationID), Kind: controller.InputWeak, }, { Namespace: runtime.NamespaceName, Type: runtime.UniqueMachineTokenType, ID: optional.Some(runtime.UniqueMachineTokenID), Kind: controller.InputWeak, }, }, ); err != nil { return fmt.Errorf("error waiting for network: %w", err) } // normal reconcile loop wgClient, wgClientErr := wgctrl.New() if wgClientErr != nil { return wgClientErr } defer func() { if closeErr := wgClient.Close(); closeErr != nil { logger.Error("failed to close wg client", zap.Error(closeErr)) } }() var zeroKey wgtypes.Key if bytes.Equal(ctrl.nodeKey[:], zeroKey[:]) { var err error ctrl.nodeKey, err = wgtypes.GeneratePrivateKey() if err != nil { return fmt.Errorf("error generating Wireguard key: %w", err) } } ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() // default name, actual name is set based on the provision API response: // whether we use the Wireguard tunnel over gRPC or not linkName := constants.SideroLinkName for { select { case <-ctx.Done(): return nil case <-ticker.C: reconnect, err := peerDown(wgClient, linkName) if err != nil { if errors.Is(err, os.ErrNotExist) { // no Wireguard device, so no need to reconnect continue } return err } if !reconnect { // nothing to do continue } case <-r.EventCh(): // if the SideroLink configuration changed (either config itself, or machine UUID or Unique Token), // clear any previous provision data we had so that Talos doesn't cycle through endpoints from // stale provisioning data until it reaches out to SideroLink Provision API again ctrl.pd = provisionData{} } if ctrl.pd.IsEmpty() { provision, err := ctrl.provision(ctx, r, logger) if err != nil { return fmt.Errorf("error provisioning: %w", err) } if !provision.IsPresent() { continue } ctrl.pd = provision.ValueOrZero() } useWgTunnel := ctrl.pd.grpcPeerAddrPort != "" if useWgTunnel { linkName = constants.SideroLinkTunnelName } else { linkName = constants.SideroLinkName } serverAddress, err := netip.ParseAddr(ctrl.pd.ServerAddress) if err != nil { return fmt.Errorf("error parsing server address: %w", err) } nodeAddress, err := netip.ParsePrefix(ctrl.pd.NodeAddressPrefix) if err != nil { return fmt.Errorf("error parsing node address: %w", err) } linkSpec := network.NewLinkSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.LinkID(linkName))) addressSpec := network.NewAddressSpec(network.ConfigNamespaceName, network.LayeredID(network.ConfigOperator, network.AddressID(linkName, nodeAddress))) // Rotate through the endpoints. ep, ok := ctrl.pd.TakeEndpoint() if !ok { return errors.New("host returned no endpoints") } // in case the endpoint is a hostname, resolve it to an IP address each time we reconnect // if the IP behind the DNS name changes, it will trigger a change in the LinkSpec, and // it will update the Wireguard peer endpoint accordingly resolvedEndpoint, err := net.ResolveUDPAddr("udp", ep) if err != nil { return fmt.Errorf("error resolving endpoint %q: %w", ep, err) } logger.Info( "configuring siderolink connection", zap.String("peer_endpoint", resolvedEndpoint.String()), zap.String("next_peer_endpoint", ctrl.pd.PeekNextEndpoint()), ) if err = safe.WriterModify(ctx, r, linkSpec, func(res *network.LinkSpec) error { spec := res.TypedSpec() spec.ConfigLayer = network.ConfigOperator spec.Name = linkName spec.Type = nethelpers.LinkNone spec.Kind = "wireguard" // if using wg-tunnel, the actual link will be created in the userspace // as a tunnel device // if using native, we create a native kernel wireguard interface if useWgTunnel { spec.Logical = false // the controller does not create the link } else { spec.Logical = true // allow controller to create the link } spec.Up = true spec.MTU = wireguard.LinkMTU spec.Wireguard = network.WireguardSpec{ PrivateKey: ctrl.nodeKey.String(), Peers: []network.WireguardPeer{ { PublicKey: ctrl.pd.ServerPublicKey, Endpoint: resolvedEndpoint.String(), AllowedIPs: []netip.Prefix{ netip.PrefixFrom(serverAddress, serverAddress.BitLen()), }, // make sure Talos pings SideroLink endpoint, so that tunnel is established: // SideroLink doesn't know Talos endpoint. PersistentKeepaliveInterval: constants.SideroLinkDefaultPeerKeepalive, }, }, } spec.Wireguard.Sort() return nil }); err != nil { return fmt.Errorf("error creating siderolink spec: %w", err) } if err = safe.WriterModify(ctx, r, addressSpec, func(res *network.AddressSpec) error { spec := res.TypedSpec() spec.ConfigLayer = network.ConfigOperator spec.Address = nodeAddress spec.Family = nethelpers.FamilyInet6 spec.Flags = nethelpers.AddressFlags(nethelpers.AddressPermanent) spec.LinkName = linkName spec.Scope = nethelpers.ScopeGlobal return nil }); err != nil { return fmt.Errorf("error creating address spec: %w", err) } if ctrl.pd.grpcPeerAddrPort != "" { var ourAddr netip.AddrPort ourAddr, err = netip.ParseAddrPort(ctrl.pd.grpcPeerAddrPort) if err != nil { return err } if err = safe.WriterModify(ctx, r, siderolink.NewTunnel(), func(tunnel *siderolink.Tunnel) error { tunnel.TypedSpec().APIEndpoint = ctrl.pd.apiEndpont tunnel.TypedSpec().LinkName = linkName tunnel.TypedSpec().MTU = wireguard.LinkMTU tunnel.TypedSpec().NodeAddress = ourAddr return nil }, ); err != nil { return fmt.Errorf("error creating tunnel spec: %w", err) } } else { if err = r.Destroy(ctx, siderolink.NewTunnel().Metadata()); err != nil && !state.IsNotFoundError(err) { return fmt.Errorf("error destroying tunnel spec: %w", err) } } keepLinkSpecSet := map[resource.ID]struct{}{ linkSpec.Metadata().ID(): {}, } keepAddressSpecSet := map[resource.ID]struct{}{ addressSpec.Metadata().ID(): {}, } if err := ctrl.cleanup(ctx, r, keepLinkSpecSet, keepAddressSpecSet, logger); err != nil { return err } logger.Info( "siderolink connection configured", zap.String("endpoint", ctrl.pd.apiEndpont), zap.String("node_uuid", ctrl.pd.nodeUUID), zap.String("node_address", nodeAddress.String()), ) } } //nolint:gocyclo func (ctrl *ManagerController) provision(ctx context.Context, r controller.Runtime, logger *zap.Logger) (optional.Optional[provisionData], error) { cfg, err := safe.ReaderGetByID[*siderolink.Config](ctx, r, siderolink.ConfigID) if err != nil { if state.IsNotFoundError(err) { if cleanupErr := ctrl.cleanup(ctx, r, nil, nil, logger); cleanupErr != nil { return optional.None[provisionData](), fmt.Errorf("failed to do cleanup: %w", cleanupErr) } // no config return optional.None[provisionData](), nil } return optional.None[provisionData](), fmt.Errorf("failed to get siderolink config: %w", err) } sysInfo, err := safe.ReaderGetByID[*hardware.SystemInformation](ctx, r, hardware.SystemInformationID) if err != nil { if state.IsNotFoundError(err) { // no system information return optional.None[provisionData](), nil } return optional.None[provisionData](), fmt.Errorf("failed to get system information: %w", err) } nodeUUID := sysInfo.TypedSpec().UUID provision := func() (*pb.ProvisionResponse, error) { // set a timeout for the provisioning call ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() conn, connErr := grpc.NewClient( cfg.TypedSpec().Host, withTransportCredentials(cfg.TypedSpec().Insecure), grpc.WithSharedWriteBuffer(true), grpc.WithContextDialer(dialer.DynamicProxyDialerWithTLSConfig(httpdefaults.RootCAsTLSConfig)), ) if connErr != nil { return nil, fmt.Errorf("error dialing SideroLink endpoint %q: %w", cfg.TypedSpec().Host, connErr) } defer func() { if closeErr := conn.Close(); closeErr != nil { logger.Error("failed to close SideroLink provisioning GRPC connection", zap.Error(closeErr)) } }() uniqTokenRes, rdrErr := safe.ReaderGetByID[*runtime.UniqueMachineToken](ctx, r, runtime.UniqueMachineTokenID) if rdrErr != nil { return nil, fmt.Errorf("failed to get unique token: %w", rdrErr) } var wgOverGRPC *bool if cfg.TypedSpec().Tunnel { wgOverGRPC = new(true) } sideroLinkClient := pb.NewProvisionServiceClient(conn) request := &pb.ProvisionRequest{ NodeUuid: nodeUUID, NodePublicKey: ctrl.nodeKey.PublicKey().String(), NodeUniqueToken: new(uniqTokenRes.TypedSpec().Token), TalosVersion: new(version.Tag), WireguardOverGrpc: wgOverGRPC, } token := cfg.TypedSpec().JoinToken if token != "" { request.JoinToken = new(token) } return sideroLinkClient.Provision(ctx, request) } resp, err := provision() if err != nil { return optional.None[provisionData](), err } return optional.Some(provisionData{ nodeUUID: nodeUUID, apiEndpont: cfg.TypedSpec().APIEndpoint, ServerAddress: resp.ServerAddress, ServerPublicKey: resp.ServerPublicKey, NodeAddressPrefix: resp.NodeAddressPrefix, endpoints: resp.GetEndpoints(), grpcPeerAddrPort: resp.GrpcPeerAddrPort, }), nil } type provisionData struct { nodeUUID string apiEndpont string ServerAddress string ServerPublicKey string NodeAddressPrefix string endpoints []string grpcPeerAddrPort string } func (d *provisionData) IsEmpty() bool { return d == nil || len(d.endpoints) == 0 } func (d *provisionData) TakeEndpoint() (string, bool) { if d.IsEmpty() { return "", false } ep := d.endpoints[0] d.endpoints = d.endpoints[1:] return ep, true } func (d *provisionData) PeekNextEndpoint() string { if d.IsEmpty() { return "" } return d.endpoints[0] } func (ctrl *ManagerController) cleanup( ctx context.Context, r controller.Runtime, keepLinkSpecIDSet, keepAddressSpecIDSet map[resource.ID]struct{}, logger *zap.Logger, ) error { if err := ctrl.cleanupLinkSpecs(ctx, r, keepLinkSpecIDSet, logger); err != nil { return err } return ctrl.cleanupAddressSpecs(ctx, r, keepAddressSpecIDSet, logger) } //nolint:dupl func (ctrl *ManagerController) cleanupLinkSpecs(ctx context.Context, r controller.Runtime, keepSet map[resource.ID]struct{}, logger *zap.Logger) error { list, err := safe.ReaderList[*network.LinkSpec](ctx, r, network.NewLinkSpec(network.ConfigNamespaceName, "").Metadata()) if err != nil { return err } for link := range list.All() { if link.Metadata().Owner() != ctrl.Name() { continue } if _, ok := keepSet[link.Metadata().ID()]; ok { continue } if destroyErr := r.Destroy(ctx, link.Metadata()); destroyErr != nil && !state.IsNotFoundError(destroyErr) { return destroyErr } logger.Info("destroyed link spec", zap.String("link_id", link.Metadata().ID())) } return nil } //nolint:dupl func (ctrl *ManagerController) cleanupAddressSpecs(ctx context.Context, r controller.Runtime, keepSet map[resource.ID]struct{}, logger *zap.Logger) error { list, err := safe.ReaderList[*network.AddressSpec](ctx, r, network.NewAddressSpec(network.ConfigNamespaceName, "").Metadata()) if err != nil { return err } for address := range list.All() { if address.Metadata().Owner() != ctrl.Name() { continue } if _, ok := keepSet[address.Metadata().ID()]; ok { continue } if destroyErr := r.Destroy(ctx, address.Metadata()); destroyErr != nil && !state.IsNotFoundError(destroyErr) { return destroyErr } logger.Info("destroyed address spec", zap.String("address_id", address.Metadata().ID())) } return nil } func withTransportCredentials(insec bool) grpc.DialOption { var transportCredentials credentials.TransportCredentials if insec { transportCredentials = insecure.NewCredentials() } else { transportCredentials = credentials.NewTLS(&tls.Config{ RootCAs: httpdefaults.RootCAs(), }) } return grpc.WithTransportCredentials(transportCredentials) } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/manager_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink_test import ( "context" "fmt" "net" "net/netip" "testing" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" pb "github.com/siderolabs/siderolink/api/siderolink" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "google.golang.org/grpc" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" siderolinkctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/siderolink" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) func TestManagerSuite(t *testing.T) { t.Parallel() if fipsmode.Strict() { t.Skip("skipping test in strict FIPS mode") } suite.Run(t, &ManagerSuite{}) } type ManagerSuite struct { ctest.DefaultSuite s *grpc.Server } type mockServer struct { pb.UnimplementedProvisionServiceServer suite *ManagerSuite endpoints []string } const ( mockNodeUUID = "71233efd-7a07-43f8-b6ba-da90fae0e88b" mockUniqueToken = "random-token" mockServerEndpoint1 = "127.0.0.11:51820" mockServerEndpoint2 = "localhost:51821" mockServerAddress = "fdae:41e4:649b:9303:b6db:d99c:215e:dfc4" mockServerPublicKey = "2aq/V91QyrHAoH24RK0bldukgo2rWk+wqE5Eg6TArCM=" mockNodeAddressPrefix = "fdae:41e4:649b:9303:2a07:9c7:5b08:aef7/64" ) func (srv mockServer) Provision(_ context.Context, req *pb.ProvisionRequest) (*pb.ProvisionResponse, error) { srv.suite.Assert().Equal(mockNodeUUID, req.GetNodeUuid()) srv.suite.Assert().Empty(req.GetJoinToken()) srv.suite.Assert().False(req.GetWireguardOverGrpc()) srv.suite.Assert().Equal(mockUniqueToken, req.GetNodeUniqueToken()) return &pb.ProvisionResponse{ ServerEndpoint: pb.MakeEndpoints(srv.endpoints...), ServerAddress: mockServerAddress, ServerPublicKey: mockServerPublicKey, NodeAddressPrefix: mockNodeAddressPrefix, }, nil } func (suite *ManagerSuite) initialSetup(endpoints ...string) { lis, err := (&net.ListenConfig{}).Listen(suite.Ctx(), "tcp", "localhost:0") suite.Require().NoError(err) suite.s = grpc.NewServer() pb.RegisterProvisionServiceServer(suite.s, mockServer{ suite: suite, endpoints: endpoints, }) suite.T().Cleanup(suite.s.Stop) go func() { suite.Require().NoError(suite.s.Serve(lis)) }() cmdline := procfs.NewCmdline(fmt.Sprintf("%s=%s", constants.KernelParamSideroLink, lis.Addr().String())) configController := siderolinkctrl.ConfigController{Cmdline: cmdline} suite.Require().NoError(suite.Runtime().RegisterController(&siderolinkctrl.ManagerController{})) suite.Require().NoError(suite.Runtime().RegisterController(&configController)) networkStatus := network.NewStatus(network.NamespaceName, network.StatusID) networkStatus.TypedSpec().AddressReady = true suite.Create(networkStatus) systemInformation := hardware.NewSystemInformation(hardware.SystemInformationID) systemInformation.TypedSpec().UUID = mockNodeUUID suite.Create(systemInformation) uniqToken := runtime.NewUniqueMachineToken() uniqToken.TypedSpec().Token = mockUniqueToken suite.Create(uniqToken) } func (suite *ManagerSuite) TestReconcile() { suite.initialSetup(mockServerEndpoint1) nodeAddress := netip.MustParsePrefix(mockNodeAddressPrefix) ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.AddressID(constants.SideroLinkName, nodeAddress)), func(r *network.AddressSpec, asrt *assert.Assertions) { address := r.TypedSpec() asrt.Equal(nodeAddress, address.Address) asrt.Equal(network.ConfigOperator, address.ConfigLayer) asrt.Equal(nethelpers.FamilyInet6, address.Family) asrt.Equal(constants.SideroLinkName, address.LinkName) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)), func(r *network.LinkSpec, asrt *assert.Assertions) { link := r.TypedSpec() asrt.Equal("wireguard", link.Kind) asrt.Equal(network.ConfigOperator, link.ConfigLayer) asrt.NotEmpty(link.Wireguard.PrivateKey) asrt.Len(link.Wireguard.Peers, 1) asrt.Equal(mockServerEndpoint1, link.Wireguard.Peers[0].Endpoint) asrt.Equal(mockServerPublicKey, link.Wireguard.Peers[0].PublicKey) asrt.Equal( []netip.Prefix{ netip.PrefixFrom( netip.MustParseAddr(mockServerAddress), 128, ), }, link.Wireguard.Peers[0].AllowedIPs, ) asrt.Equal( constants.SideroLinkDefaultPeerKeepalive, link.Wireguard.Peers[0].PersistentKeepaliveInterval, ) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) // remove config configPtr := siderolink.NewConfig(config.NamespaceName, siderolink.ConfigID).Metadata() destroyErr := suite.State().Destroy(suite.Ctx(), configPtr, state.WithDestroyOwner(new(siderolinkctrl.ConfigController{}).Name())) suite.Require().NoError(destroyErr) ctest.AssertNoResource[*network.LinkSpec](suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)), rtestutils.WithNamespace(network.ConfigNamespaceName), ) ctest.AssertNoResource[*network.AddressSpec](suite, network.LayeredID(network.ConfigOperator, network.AddressID(constants.SideroLinkName, nodeAddress)), rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *ManagerSuite) TestMultipleEndpoints() { suite.initialSetup(mockServerEndpoint1, mockServerEndpoint2) ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)), func(r *network.LinkSpec, asrt *assert.Assertions) { link := r.TypedSpec() asrt.Len(link.Wireguard.Peers, 1) // Talos should pick the first endpoint from the list. asrt.Equal(mockServerEndpoint1, link.Wireguard.Peers[0].Endpoint) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } func (suite *ManagerSuite) TestResolveEndpoints() { suite.initialSetup(mockServerEndpoint2) ctest.AssertResource(suite, network.LayeredID(network.ConfigOperator, network.LinkID(constants.SideroLinkName)), func(r *network.LinkSpec, asrt *assert.Assertions) { link := r.TypedSpec() asrt.Len(link.Wireguard.Peers, 1) // Talos should resolve the hostname to an IP address. asrt.Equal("127.0.0.1:51821", link.Wireguard.Peers[0].Endpoint) }, rtestutils.WithNamespace(network.ConfigNamespaceName), ) } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/siderolink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package siderolink provides controllers which manage file resources. package siderolink import ( "fmt" "time" "github.com/siderolabs/siderolink/pkg/wireguard" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" ) // WireguardClient allows mocking Wireguard client. type WireguardClient interface { Device(string) (*wgtypes.Device, error) Close() error } func peerDown(wgClient WireguardClient, linkName string) (bool, error) { wgDevice, err := wgClient.Device(linkName) if err != nil { return false, fmt.Errorf("error reading Wireguard device: %w", err) } if len(wgDevice.Peers) != 1 { return false, fmt.Errorf("unexpected number of Wireguard peers: %d", len(wgDevice.Peers)) } peer := wgDevice.Peers[0] since := time.Since(peer.LastHandshakeTime) return since >= wireguard.PeerDownInterval, nil } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "context" "errors" "fmt" "net" "os" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "golang.zx2c4.com/wireguard/wgctrl" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) // DefaultStatusUpdateInterval is the default interval between status updates. const DefaultStatusUpdateInterval = 30 * time.Second // StatusController reports siderolink status. type StatusController struct { // WGClientFunc is a function that returns a WireguardClient. // // When nil, it defaults to an actual Wireguard client. WGClientFunc func() (WireguardClient, error) // Interval is the time between peer status checks. // // When zero, it defaults to DefaultStatusUpdateInterval. Interval time.Duration } // Name implements controller.Controller interface. func (ctrl *StatusController) Name() string { return "siderolink.StatusController" } // Inputs implements controller.Controller interface. func (ctrl *StatusController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: siderolink.ConfigType, ID: optional.Some(siderolink.ConfigID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: siderolink.TunnelType, ID: optional.Some(siderolink.TunnelID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *StatusController) Outputs() []controller.Output { return []controller.Output{ { Type: siderolink.StatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *StatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { interval := ctrl.Interval if interval == 0 { interval = DefaultStatusUpdateInterval } ticker := time.NewTicker(interval) defer ticker.Stop() wgClientFunc := ctrl.WGClientFunc if wgClientFunc == nil { wgClientFunc = func() (WireguardClient, error) { return wgctrl.New() } } wgClient, err := wgClientFunc() if err != nil { return fmt.Errorf("failed to create wireguard client: %w", err) } for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-ticker.C: } r.StartTrackingOutputs() if err = ctrl.reconcileStatus(ctx, r, wgClient); err != nil { return err } if err = safe.CleanupOutputs[*siderolink.Status](ctx, r); err != nil { return err } r.ResetRestartBackoff() } } //nolint:gocyclo func (ctrl *StatusController) reconcileStatus(ctx context.Context, r controller.Runtime, wgClient WireguardClient) (err error) { cfg, err := safe.ReaderGetByID[*siderolink.Config](ctx, r, siderolink.ConfigID) if err != nil { if state.IsNotFoundError(err) { return nil } return err } if cfg.TypedSpec().APIEndpoint == "" { return nil } tunnelConfig, err := safe.ReaderGetByID[*siderolink.Tunnel](ctx, r, siderolink.TunnelID) if err != nil && !state.IsNotFoundError(err) { return err } linkName := constants.SideroLinkName if tunnelConfig != nil { linkName = constants.SideroLinkTunnelName } host, _, err := net.SplitHostPort(cfg.TypedSpec().Host) if err != nil { host = cfg.TypedSpec().Host } down, err := peerDown(wgClient, linkName) if err != nil { if !errors.Is(err, os.ErrNotExist) { return err } down = true // wireguard device does not exist, we mark it as down } if err = safe.WriterModify(ctx, r, siderolink.NewStatus(), func(status *siderolink.Status) error { status.TypedSpec().Host = host status.TypedSpec().Connected = !down status.TypedSpec().LinkName = linkName status.TypedSpec().GRPCTunnel = tunnelConfig != nil return nil }); err != nil { return fmt.Errorf("failed to update status: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink_test import ( "os" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" siderolinkctrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/siderolink" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) type StatusSuite struct { ctest.DefaultSuite } func TestStatusSuite(t *testing.T) { t.Parallel() suite.Run(t, &StatusSuite{ DefaultSuite: ctest.DefaultSuite{ Timeout: 3 * time.Second, }, }) } func (suite *StatusSuite) TestStatus() { wgClient := &mockWgClient{ device: &wgtypes.Device{ Peers: []wgtypes.Peer{ { LastHandshakeTime: time.Now().Add(-time.Minute), }, }, }, } suite.Require().NoError(suite.Runtime().RegisterController(&siderolinkctrl.StatusController{ WGClientFunc: func() (siderolinkctrl.WireguardClient, error) { return wgClient, nil }, Interval: 100 * time.Millisecond, })) rtestutils.AssertNoResource[*siderolink.Status](suite.Ctx(), suite.T(), suite.State(), siderolink.StatusID) siderolinkConfig := siderolink.NewConfig(config.NamespaceName, siderolink.ConfigID) siderolinkConfig.TypedSpec().APIEndpoint = "https://siderolink.example.org:1234?jointoken=supersecret&foo=bar#some=fragment" siderolinkConfig.TypedSpec().Host = "siderolink.example.org:1234" suite.Require().NoError(suite.State().Create(suite.Ctx(), siderolinkConfig)) suite.assertStatus("siderolink.example.org", true) // disconnect the peer wgClient.setDevice(&wgtypes.Device{ Peers: []wgtypes.Peer{ {LastHandshakeTime: time.Now().Add(-time.Hour)}, }, }) // no device wgClient.setDevice(nil) suite.assertStatus("siderolink.example.org", false) // reconnect the peer wgClient.setDevice(&wgtypes.Device{ Peers: []wgtypes.Peer{ {LastHandshakeTime: time.Now().Add(-5 * time.Second)}, }, }) suite.assertStatus("siderolink.example.org", true) // update API endpoint siderolinkConfig.TypedSpec().APIEndpoint = "https://new.example.org?jointoken=supersecret" siderolinkConfig.TypedSpec().Host = "new.example.org" suite.Require().NoError(suite.State().Update(suite.Ctx(), siderolinkConfig)) suite.assertStatus("new.example.org", true) // no config suite.Require().NoError(suite.State().Destroy(suite.Ctx(), siderolinkConfig.Metadata())) rtestutils.AssertNoResource[*siderolink.Status](suite.Ctx(), suite.T(), suite.State(), siderolink.StatusID) } func (suite *StatusSuite) assertStatus(endpoint string, connected bool) { rtestutils.AssertResources(suite.Ctx(), suite.T(), suite.State(), []resource.ID{siderolink.StatusID}, func(c *siderolink.Status, assert *assert.Assertions) { assert.Equal(endpoint, c.TypedSpec().Host) assert.Equal(connected, c.TypedSpec().Connected) }) } type mockWgClient struct { mu sync.Mutex device *wgtypes.Device } func (m *mockWgClient) setDevice(device *wgtypes.Device) { m.mu.Lock() defer m.mu.Unlock() m.device = device } func (m *mockWgClient) Device(string) (*wgtypes.Device, error) { m.mu.Lock() defer m.mu.Unlock() if m.device == nil { return nil, os.ErrNotExist } return m.device, nil } func (m *mockWgClient) Close() error { return nil } ================================================ FILE: internal/app/machined/pkg/controllers/siderolink/userspace.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "context" "fmt" "net/netip" "sync" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/siderolink/pkg/wgtunnel" "github.com/siderolabs/siderolink/pkg/wgtunnel/wgbind" "github.com/siderolabs/siderolink/pkg/wgtunnel/wggrpc" "go.uber.org/zap" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/pkg/ctxutil" "github.com/siderolabs/talos/internal/pkg/endpoint" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) // UserspaceWireguardController imlements a controller that manages a Wireguard over GRPC tunnel in userspace. type UserspaceWireguardController struct { RelayRetryTimeout time.Duration DebugDataStream bool } // Name implements controller.Controller interface. func (ctrl *UserspaceWireguardController) Name() string { return "siderolink.UserspaceWireguardController" } // Inputs implements controller.Controller interface. func (ctrl *UserspaceWireguardController) Inputs() []controller.Input { return []controller.Input{ { Namespace: config.NamespaceName, Type: siderolink.TunnelType, ID: optional.Some(siderolink.TunnelID), Kind: controller.InputWeak, }, } } // Outputs implements controller.Controller interface. func (ctrl *UserspaceWireguardController) Outputs() []controller.Output { return []controller.Output{} } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *UserspaceWireguardController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { eg, ctx := errgroup.WithContext(ctx) var ( relayRetryTimer resettableTimer tunnelDevice tunnelDeviceProps tunnelRelay tunnelProps ) defer func() { tunnelRelay.relay.Close() tunnelDevice.device.Close() }() const ( // maxPendingServerMessages is the maximum number of messages that can be pending in the queue before blocking. maxPendingServerMessages = 100 // maxPendingClientMessages is the maximum number of messages that can be pending in the ring before being overwritten. maxPendingClientMessages = 100 ) qp := wgbind.NewQueuePair(maxPendingServerMessages, maxPendingClientMessages) for { select { case <-ctx.Done(): return ctxutil.Cause(ctx) case <-r.EventCh(): case <-relayRetryTimer.C(): relayRetryTimer.Clear() } res, err := safe.ReaderGetByID[*siderolink.Tunnel](ctx, r, siderolink.TunnelID) if err != nil { if state.IsNotFoundError(err) { tunnelRelay.relay.Close() tunnelDevice.device.Close() continue } return fmt.Errorf("failed to read link spec: %w", err) } if tunnelDevice.device.IsClosed() { tunnelDevice.device.Close() dev, err := wgtunnel.NewTunnelDevice(res.TypedSpec().LinkName, res.TypedSpec().MTU, qp, ctrl.makeLogger(logger)) if err != nil { return fmt.Errorf("failed to create tunnel device: %w", err) } // Store in outer scope because modifying the same variable will lead to the data race below tunnelDevice = tunnelDeviceProps{device: dev, linkName: res.TypedSpec().LinkName, mtu: res.TypedSpec().MTU} logger.Info("wg over grpc tunnel device created", zap.String("link_name", res.TypedSpec().LinkName)) eg.Go(func() error { logger.Debug("tunnel device running") defer logger.Debug("tunnel device exited") return dev.Run() }) } ep, err := endpoint.Parse(res.TypedSpec().APIEndpoint) if err != nil { return fmt.Errorf("failed to parse siderolink API endpoint: %w", err) } dstHost := ep.Host ourAddrPort := res.TypedSpec().NodeAddress if tunnelRelay.relay.IsClosed() || tunnelRelay.dstHost != dstHost || tunnelRelay.ourAddrPort != ourAddrPort { // Reset timer because we are going to start tunnel anyway relayRetryTimer.Reset(0) tunnelRelay.relay.Close() logger.Info( "updating tunnel relay", zap.String("old_endpoint", tunnelRelay.dstHost), zap.Stringer("old_node_address", tunnelRelay.ourAddrPort), zap.String("new_endpoint", dstHost), zap.Stringer("new_node_address", ourAddrPort), ) relay, err := wggrpc.NewRelayToHost(dstHost, ctrl.RelayRetryTimeout, qp, ourAddrPort, withTransportCredentials(ep.Insecure)) if err != nil { return fmt.Errorf("failed to create tunnel relay: %w", err) } // Store in outer scope because modifying the same variable will lead to the data race below tunnelRelay = tunnelProps{relay: relay, dstHost: dstHost, ourAddrPort: ourAddrPort} eg.Go(func() error { logger.Debug("running tunnel relay") err := relay.Run(ctx, ctrl.makeLogger(logger)) if err == nil { logger.Debug("tunnel relay exited gracefully", zap.String("endpoint", dstHost), zap.Stringer("node_address", ourAddrPort), ) return nil } // Relay returned an error, close the relay and print the error, device should be kept running. relay.Close() const retryIn = 5 * time.Second logger.Error("tunnel relay failed, retrying", zap.Duration("timeout", retryIn), zap.String("endpoint", dstHost), zap.Stringer("node_address", ourAddrPort), zap.Error(err), ) relayRetryTimer.Reset(retryIn) return nil }) } } } // makeLogger ensures that we do not spam like crazy into our ring buffer loggers unless we explicitly want to. func (ctrl *UserspaceWireguardController) makeLogger(logger *zap.Logger) *zap.Logger { if ctrl.DebugDataStream { return logger } return logger.WithOptions(zap.IncreaseLevel(zap.InfoLevel)) } type tunnelProps struct { relay *wggrpc.Relay dstHost string ourAddrPort netip.AddrPort } type tunnelDeviceProps struct { device *wgtunnel.TunnelDevice linkName string mtu int } // resettableTimer wraps time.Timer to allow resetting the timer to any duration. type resettableTimer struct { mx sync.Mutex timer *time.Timer } // Reset resets the timer to the given duration. // // If the duration is zero, the timer is removed (and stopped as needed). // If the duration is non-zero, the timer is created if it doesn't exist, or reset if it does. func (rt *resettableTimer) Reset(delay time.Duration) { rt.mx.Lock() defer rt.mx.Unlock() if delay == 0 { if rt.timer != nil { if !rt.timer.Stop() { <-rt.timer.C } rt.timer = nil } } else { if rt.timer == nil { rt.timer = time.NewTimer(delay) } else { if !rt.timer.Stop() { <-rt.timer.C } rt.timer.Reset(delay) } } } // Clear should be called after receiving from the timer channel. func (rt *resettableTimer) Clear() { rt.mx.Lock() defer rt.mx.Unlock() rt.timer = nil } // C returns the timer channel. // // If the timer was not reset to a non-zero duration, nil is returned. func (rt *resettableTimer) C() <-chan time.Time { rt.mx.Lock() defer rt.mx.Unlock() if rt.timer == nil { return nil } return rt.timer.C } ================================================ FILE: internal/app/machined/pkg/controllers/time/adjtime_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time import ( "context" "fmt" stdtime "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "golang.org/x/sys/unix" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/timex" "github.com/siderolabs/talos/pkg/machinery/resources/time" ) // AdjtimeStatusController manages time.AdjtimeStatus based on Linux kernel info. type AdjtimeStatusController struct { V1Alpha1Mode v1alpha1runtime.Mode } // Name implements controller.Controller interface. func (ctrl *AdjtimeStatusController) Name() string { return "time.AdjtimeStatusController" } // Inputs implements controller.Controller interface. func (ctrl *AdjtimeStatusController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *AdjtimeStatusController) Outputs() []controller.Output { return []controller.Output{ { Type: time.AdjtimeStatusType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. func (ctrl *AdjtimeStatusController) Run(ctx context.Context, r controller.Runtime, _ *zap.Logger) error { if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { // in container mode, clock is managed by the host return nil } const pollInterval = 30 * stdtime.Second pollTicker := stdtime.NewTicker(pollInterval) defer pollTicker.Stop() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-pollTicker.C: } var timexBuf unix.Timex state, err := timex.Adjtimex(&timexBuf) if err != nil { return fmt.Errorf("failed to get adjtimex state: %w", err) } scale := stdtime.Nanosecond if timexBuf.Status&unix.STA_NANO == 0 { scale = stdtime.Microsecond } if err := safe.WriterModify(ctx, r, time.NewAdjtimeStatus(), func(status *time.AdjtimeStatus) error { status.TypedSpec().Offset = stdtime.Duration(timexBuf.Offset) * scale //nolint:durationcheck status.TypedSpec().FrequencyAdjustmentRatio = 1 + float64(timexBuf.Freq)/65536.0/1000000.0 status.TypedSpec().MaxError = stdtime.Duration(timexBuf.Maxerror) * stdtime.Microsecond //nolint:durationcheck status.TypedSpec().EstError = stdtime.Duration(timexBuf.Esterror) * stdtime.Microsecond //nolint:durationcheck status.TypedSpec().Status = timex.Status(timexBuf.Status).String() status.TypedSpec().State = state.String() status.TypedSpec().Constant = int(timexBuf.Constant) status.TypedSpec().SyncStatus = timexBuf.Status&unix.STA_UNSYNC == 0 return nil }); err != nil { return fmt.Errorf("failed to update adjtime status: %w", err) } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/time/sync.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time import ( "context" "fmt" "sync" stdtime "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/ntp" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/time" ) // SyncController manages v1alpha1.TimeSync based on configuration and NTP sync process. type SyncController struct { V1Alpha1Mode v1alpha1runtime.Mode NewNTPSyncer NewNTPSyncerFunc bootTime stdtime.Time } // Name implements controller.Controller interface. func (ctrl *SyncController) Name() string { return "time.SyncController" } // Inputs implements controller.Controller interface. func (ctrl *SyncController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *SyncController) Outputs() []controller.Output { return []controller.Output{ { Type: time.StatusType, Kind: controller.OutputExclusive, }, } } // NTPSyncer interface is implemented by ntp.Syncer, interface for mocking. type NTPSyncer interface { Run(ctx context.Context) Synced() <-chan struct{} EpochChange() <-chan struct{} SetTimeServers([]string) } // NewNTPSyncerFunc function allows to replace ntp.Syncer with the mock. type NewNTPSyncerFunc func(*zap.Logger, []string) NTPSyncer // Run implements controller.Controller interface. // //nolint:gocyclo,cyclop func (ctrl *SyncController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { if ctrl.bootTime.IsZero() { ctrl.bootTime = stdtime.Now() } if ctrl.NewNTPSyncer == nil { ctrl.NewNTPSyncer = func(logger *zap.Logger, timeServers []string) NTPSyncer { return ntp.NewSyncer(logger, timeServers) } } // wait for udevd to be healthy, which implies that all RTC devices if err := runtime.WaitForDevicesReady(ctx, r, []controller.Input{ { Namespace: network.NamespaceName, Type: network.TimeServerStatusType, ID: optional.Some(network.TimeServerID), Kind: controller.InputWeak, }, { Namespace: config.NamespaceName, Type: config.MachineConfigType, ID: optional.Some(config.ActiveID), }, }, ); err != nil { return err } var ( syncCtx context.Context syncCtxCancel context.CancelFunc syncWg sync.WaitGroup syncCh <-chan struct{} epochCh <-chan struct{} syncer NTPSyncer timeSynced bool epoch int timeSyncTimeoutTimer *stdtime.Timer timeSyncTimeoutCh <-chan stdtime.Time ) defer func() { if syncer != nil { syncCtxCancel() syncWg.Wait() } if timeSyncTimeoutTimer != nil { timeSyncTimeoutTimer.Stop() } }() for { select { case <-ctx.Done(): return nil case <-r.EventCh(): case <-syncCh: syncCh = nil timeSynced = true case <-epochCh: epoch++ case <-timeSyncTimeoutCh: timeSynced = true timeSyncTimeoutTimer = nil } timeServersStatus, err := safe.ReaderGet[*network.TimeServerStatus]( ctx, r, resource.NewMetadata(network.NamespaceName, network.TimeServerStatusType, network.TimeServerID, resource.VersionUndefined), ) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting time server status: %w", err) } // time server list is not ready yet, wait for the next reconcile continue } timeServers := timeServersStatus.TypedSpec().NTPServers cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, r, config.ActiveID) if err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error getting config: %w", err) } } var syncTimeout stdtime.Duration syncDisabled := false if ctrl.V1Alpha1Mode == v1alpha1runtime.ModeContainer { syncDisabled = true } if cfg != nil && cfg.Config().NetworkTimeSyncConfig() != nil { if cfg.Config().NetworkTimeSyncConfig().Disabled() { syncDisabled = true } syncTimeout = cfg.Config().NetworkTimeSyncConfig().BootTimeout() } if !timeSynced { sinceBoot := stdtime.Since(ctrl.bootTime) switch { case syncTimeout == 0: // disable sync timeout if timeSyncTimeoutTimer != nil { timeSyncTimeoutTimer.Stop() } timeSyncTimeoutCh = nil case sinceBoot > syncTimeout: // over sync timeout already, so in sync timeSynced = true default: // make sure timer fires in whatever time is left till the timeout if timeSyncTimeoutTimer == nil || !timeSyncTimeoutTimer.Reset(syncTimeout-sinceBoot) { timeSyncTimeoutTimer = stdtime.NewTimer(syncTimeout - sinceBoot) timeSyncTimeoutCh = timeSyncTimeoutTimer.C } } } switch { case syncDisabled && syncer != nil: // stop syncing syncCtxCancel() syncWg.Wait() syncer = nil syncCh = nil epochCh = nil case !syncDisabled && syncer == nil: // start syncing syncer = ctrl.NewNTPSyncer(logger, timeServers) syncCh = syncer.Synced() epochCh = syncer.EpochChange() timeSynced = false syncCtx, syncCtxCancel = context.WithCancel(ctx) //nolint:govet,fatcontext syncWg.Go(func() { syncer.Run(syncCtx) }) } if syncer != nil { syncer.SetTimeServers(timeServers) } if syncDisabled { timeSynced = true } if err = safe.WriterModify(ctx, r, time.NewStatus(), func(r *time.Status) error { *r.TypedSpec() = time.StatusSpec{ Epoch: epoch, Synced: timeSynced, SyncDisabled: syncDisabled, } return nil }); err != nil { return fmt.Errorf("error updating objects: %w", err) //nolint:govet } r.ResetRestartBackoff() } } ================================================ FILE: internal/app/machined/pkg/controllers/time/sync_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time_test import ( "context" "slices" "sync" "testing" "time" "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/ctest" timectrl "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/time" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" v1alpha1resource "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) type SyncSuite struct { suite.Suite state state.State runtime *runtime.Runtime wg sync.WaitGroup ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc syncerMu sync.Mutex syncer *mockSyncer } func (suite *SyncSuite) State() state.State { return suite.state } func (suite *SyncSuite) Ctx() context.Context { return suite.ctx } func (suite *SyncSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) suite.state = state.WrapCore(namespaced.NewState(inmem.Build)) var err error suite.runtime, err = runtime.NewRuntime(suite.state, zaptest.NewLogger(suite.T())) suite.Require().NoError(err) // create fake device ready status deviceStatus := runtimeres.NewDevicesStatus(runtimeres.NamespaceName, runtimeres.DevicesID) deviceStatus.TypedSpec().Ready = true suite.Require().NoError(suite.state.Create(suite.ctx, deviceStatus)) } func (suite *SyncSuite) startRuntime() { suite.wg.Go(func() { suite.Assert().NoError(suite.runtime.Run(suite.ctx)) }) } func (suite *SyncSuite) assertTimeStatus(spec timeresource.StatusSpec) error { r, err := suite.state.Get( suite.ctx, resource.NewMetadata( v1alpha1resource.NamespaceName, timeresource.StatusType, timeresource.StatusID, resource.VersionUndefined, ), ) if err != nil { if state.IsNotFoundError(err) { return retry.ExpectedError(err) } return err } status := r.(*timeresource.Status) //nolint:forcetypeassert if *status.TypedSpec() != spec { return retry.ExpectedErrorf("time status doesn't match: %v != %v", *status.TypedSpec(), spec) } return nil } func (suite *SyncSuite) TestReconcileContainerMode() { suite.Require().NoError( suite.runtime.RegisterController( &timectrl.SyncController{ V1Alpha1Mode: v1alpha1runtime.ModeContainer, NewNTPSyncer: suite.newMockSyncer, }, ), ) timeServers := network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID) timeServers.TypedSpec().NTPServers = []string{constants.DefaultNTPServer} suite.Require().NoError(suite.state.Create(suite.ctx, timeServers)) suite.startRuntime() suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 0, SyncDisabled: true, }, ) }, ), ) } func (suite *SyncSuite) TestReconcileSyncDisabled() { suite.Require().NoError( suite.runtime.RegisterController( &timectrl.SyncController{ V1Alpha1Mode: v1alpha1runtime.ModeMetal, NewNTPSyncer: suite.newMockSyncer, }, ), ) suite.startRuntime() timeServers := network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID) timeServers.TypedSpec().NTPServers = []string{constants.DefaultNTPServer} suite.Require().NoError(suite.state.Create(suite.ctx, timeServers)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: false, Epoch: 0, SyncDisabled: false, }, ) }, ), ) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeDisabled: new(true), }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 0, SyncDisabled: true, }, ) }, ), ) } func (suite *SyncSuite) TestReconcileSyncDefaultConfig() { suite.Require().NoError( suite.runtime.RegisterController( &timectrl.SyncController{ V1Alpha1Mode: v1alpha1runtime.ModeMetal, NewNTPSyncer: suite.newMockSyncer, }, ), ) suite.startRuntime() timeServers := network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID) timeServers.TypedSpec().NTPServers = []string{constants.DefaultNTPServer} suite.Require().NoError(suite.state.Create(suite.ctx, timeServers)) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: false, Epoch: 0, SyncDisabled: false, }, ) }, ), ) } func (suite *SyncSuite) TestReconcileSyncChangeConfig() { suite.Require().NoError( suite.runtime.RegisterController( &timectrl.SyncController{ V1Alpha1Mode: v1alpha1runtime.ModeMetal, NewNTPSyncer: suite.newMockSyncer, }, ), ) suite.startRuntime() timeServers := network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID) timeServers.TypedSpec().NTPServers = []string{constants.DefaultNTPServer} suite.Require().NoError(suite.state.Create(suite.ctx, timeServers)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: false, Epoch: 0, SyncDisabled: false, }, ) }, ), ) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{}, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) var mockSyncer *mockSyncer suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { mockSyncer = suite.getMockSyncer() if mockSyncer == nil { return retry.ExpectedErrorf("syncer not created yet") } return nil }, ), ) suite.Assert().Equal([]string{constants.DefaultNTPServer}, mockSyncer.getTimeServers()) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: false, Epoch: 0, SyncDisabled: false, }, ) }, ), ) close(mockSyncer.syncedCh) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 0, SyncDisabled: false, }, ) }, ), ) ctest.UpdateWithConflicts(suite, timeServers, func(r *network.TimeServerStatus) error { r.TypedSpec().NTPServers = []string{"127.0.0.1"} return nil }) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { if !slices.Equal(mockSyncer.getTimeServers(), []string{"127.0.0.1"}) { return retry.ExpectedErrorf("time servers not updated yet") } return nil }, ), ) mockSyncer.epochCh <- struct{}{} suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 1, SyncDisabled: false, }, ) }, ), ) ctest.UpdateWithConflicts(suite, cfg, func(r *config.MachineConfig) error { r.Container().RawV1Alpha1().MachineConfig.MachineTime = &v1alpha1.TimeConfig{ //nolint:staticcheck TimeDisabled: new(true), } return nil }) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 1, SyncDisabled: true, }, ) }, ), ) } func (suite *SyncSuite) TestReconcileSyncBootTimeout() { suite.Require().NoError( suite.runtime.RegisterController( &timectrl.SyncController{ V1Alpha1Mode: v1alpha1runtime.ModeMetal, NewNTPSyncer: suite.newMockSyncer, }, ), ) suite.startRuntime() timeServers := network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID) timeServers.TypedSpec().NTPServers = []string{constants.DefaultNTPServer} suite.Require().NoError(suite.state.Create(suite.ctx, timeServers)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: false, Epoch: 0, SyncDisabled: false, }, ) }, ), ) cfg := config.NewMachineConfig( container.NewV1Alpha1( &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeBootTimeout: 5 * time.Second, }, }, ClusterConfig: &v1alpha1.ClusterConfig{}, }, ), ) suite.Require().NoError(suite.state.Create(suite.ctx, cfg)) suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry( func() error { return suite.assertTimeStatus( timeresource.StatusSpec{ Synced: true, Epoch: 0, SyncDisabled: false, }, ) }, ), ) } func (suite *SyncSuite) TearDownTest() { suite.T().Log("tear down") suite.ctxCancel() suite.wg.Wait() } func (suite *SyncSuite) newMockSyncer(logger *zap.Logger, servers []string) timectrl.NTPSyncer { suite.syncerMu.Lock() defer suite.syncerMu.Unlock() suite.syncer = newMockSyncer(logger, servers) return suite.syncer } func (suite *SyncSuite) getMockSyncer() *mockSyncer { suite.syncerMu.Lock() defer suite.syncerMu.Unlock() return suite.syncer } func TestSyncSuite(t *testing.T) { suite.Run(t, new(SyncSuite)) } type mockSyncer struct { mu sync.Mutex timeServers []string syncedCh chan struct{} epochCh chan struct{} } func (mock *mockSyncer) Run(ctx context.Context) { <-ctx.Done() } func (mock *mockSyncer) Synced() <-chan struct{} { return mock.syncedCh } func (mock *mockSyncer) EpochChange() <-chan struct{} { return mock.epochCh } func (mock *mockSyncer) getTimeServers() (servers []string) { mock.mu.Lock() defer mock.mu.Unlock() return slices.Clone(mock.timeServers) } func (mock *mockSyncer) SetTimeServers(servers []string) { mock.mu.Lock() defer mock.mu.Unlock() mock.timeServers = slices.Clone(servers) } func newMockSyncer(_ *zap.Logger, servers []string) *mockSyncer { return &mockSyncer{ timeServers: slices.Clone(servers), syncedCh: make(chan struct{}, 1), epochCh: make(chan struct{}, 1), } } ================================================ FILE: internal/app/machined/pkg/controllers/time/time.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package time contains controllers managing time, synchronization, etc. package time ================================================ FILE: internal/app/machined/pkg/controllers/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package controllers provides common methods for controller operations. package controllers import ( "errors" "fmt" "io/fs" "os" "reflect" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/xfs" ) // LoadOrNewFromFile either loads value from file.yaml or generates new values and saves as file.yaml. // //nolint:gocyclo func LoadOrNewFromFile[T any](root xfs.Root, path string, empty T, generate func(T) error) error { f, err := xfs.OpenFile(root, path, os.O_RDONLY, 0) if err != nil && !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error reading state file %q: %w", path, err) } // file doesn't exist yet, generate new value and save it if f == nil || errors.Is(err, fs.ErrNotExist) { if err = generate(empty); err != nil { return err } f, err = xfs.OpenFile(root, path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0o600) if err != nil { return fmt.Errorf("error creating state file %q: %w", path, err) } defer f.Close() //nolint:errcheck encoder := yaml.NewEncoder(f) if err = encoder.Encode(empty); err != nil { return fmt.Errorf("error marshaling %q: %w", path, err) } if err = encoder.Close(); err != nil { return err } return f.Close() } // read existing cached value defer f.Close() //nolint:errcheck if err = yaml.NewDecoder(f).Decode(empty); err != nil { return fmt.Errorf("error unmarshaling %q: %w", path, err) } if reflect.ValueOf(empty).Elem().IsZero() { return fmt.Errorf("value of %q is still zero after unmarshaling", path) } return f.Close() } ================================================ FILE: internal/app/machined/pkg/controllers/v1alpha1/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "sync" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // ServiceController manages v1alpha1.Service based on services subsystem state. type ServiceController struct { V1Alpha1Events runtime.Watcher } // Name implements controller.Controller interface. func (ctrl *ServiceController) Name() string { return "v1alpha1.ServiceController" } // Inputs implements controller.Controller interface. func (ctrl *ServiceController) Inputs() []controller.Input { return nil } // Outputs implements controller.Controller interface. func (ctrl *ServiceController) Outputs() []controller.Output { return []controller.Output{ { Type: v1alpha1.ServiceType, Kind: controller.OutputExclusive, }, } } // Run implements controller.Controller interface. // //nolint:gocyclo func (ctrl *ServiceController) Run(ctx context.Context, r controller.Runtime, logger *zap.Logger) error { var wg sync.WaitGroup wg.Add(1) if err := ctrl.V1Alpha1Events.Watch(func(eventCh <-chan runtime.EventInfo) { defer wg.Done() for { var ( event runtime.EventInfo ok bool ) select { case <-ctx.Done(): return case event, ok = <-eventCh: if !ok { return } } if msg, ok := event.Payload.(*machine.ServiceStateEvent); ok { service := v1alpha1.NewService(msg.Service) switch msg.Action { //nolint:exhaustive case machine.ServiceStateEvent_RUNNING: if err := safe.WriterModify(ctx, r, service, func(svc *v1alpha1.Service) error { *svc.TypedSpec() = v1alpha1.ServiceSpec{ Running: true, Healthy: msg.GetHealth().GetHealthy(), Unknown: msg.GetHealth().GetUnknown(), } return nil }); err != nil { logger.Info("failed creating service resource", zap.String("id", service.Metadata().ID()), zap.Error(err)) } default: if err := r.Destroy(ctx, service.Metadata()); err != nil && !state.IsNotFoundError(err) { logger.Info("failed destroying service resource", zap.String("id", service.Metadata().ID()), zap.Error(err)) } } } } }, runtime.WithTailEvents(-1)); err != nil { return err } wg.Wait() return nil } ================================================ FILE: internal/app/machined/pkg/controllers/v1alpha1/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // WaitForServiceHealthy waits for a service to be healthy. // // It is a helper function for controllers. func WaitForServiceHealthy(ctx context.Context, r controller.Runtime, serviceID string, nextInputs []controller.Input) error { // set inputs temporarily to a service only if err := r.UpdateInputs([]controller.Input{ { Namespace: v1alpha1.NamespaceName, Type: v1alpha1.ServiceType, ID: optional.Some(serviceID), Kind: controller.InputWeak, }, }); err != nil { return err } for { select { case <-ctx.Done(): return ctx.Err() case <-r.EventCh(): } service, err := safe.ReaderGetByID[*v1alpha1.Service](ctx, r, serviceID) if err != nil { if state.IsNotFoundError(err) { continue } return err } if service.TypedSpec().Running && service.TypedSpec().Healthy { // condition met break } } // restore inputs if err := r.UpdateInputs(nextInputs); err != nil { return err } // queue an update to reprocess with new inputs r.QueueReconcile() return nil } ================================================ FILE: internal/app/machined/pkg/controllers/v1alpha1/v1alpha1.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package v1alpha1 provides controllers managing v1alpha1 resources. package v1alpha1 ================================================ FILE: internal/app/machined/pkg/runtime/controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "log" "github.com/cosi-project/runtime/pkg/controller" "go.uber.org/zap" ) // TaskSetupFunc defines the function that a task will execute for a specific runtime // mode. type TaskSetupFunc func(seq Sequence, data any) (TaskExecutionFunc, string) // TaskExecutionFunc defines the function that a task will execute for a specific runtime // mode. type TaskExecutionFunc func(context.Context, *log.Logger, Runtime) error // Phase represents a collection of tasks to be performed concurrently. type Phase struct { Name string Tasks []TaskSetupFunc CheckFunc func() bool } // LockOptions represents the options for a controller. type LockOptions struct { Takeover bool } // LockOption represents an option setter. type LockOption func(o *LockOptions) error // WithTakeover sets the take option to true. func WithTakeover() LockOption { return func(o *LockOptions) error { o.Takeover = true return nil } } // DefaultControllerOptions returns the default controller options. func DefaultControllerOptions() LockOptions { return LockOptions{} } // Controller represents the controller responsible for managing the execution // of sequences. type Controller interface { Runtime() Runtime Sequencer() Sequencer Run(context.Context, Sequence, any, ...LockOption) error V1Alpha2() V1Alpha2Controller } // V1Alpha2Controller provides glue into v2alpha1 controller runtime. type V1Alpha2Controller interface { Run(context.Context, *Drainer) error DependencyGraph() (*controller.DependencyGraph, error) MakeLogger(serviceName string) (*zap.Logger, error) } ================================================ FILE: internal/app/machined/pkg/runtime/disk/disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package disk contains abstract utility function to filter disks in MachineState.Disk call. package disk ================================================ FILE: internal/app/machined/pkg/runtime/disk/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package disk // Option defines a function that can alter MachineState.Disk() method output. type Option func(options *Options) // Options contains disk selection options. type Options struct { Label string } // WithPartitionLabel select a disk which has the partition labeled. func WithPartitionLabel(label string) Option { return func(opts *Options) { opts.Label = label } } ================================================ FILE: internal/app/machined/pkg/runtime/doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package runtime defines interfaces for accessing runtime specific settings, // and state. package runtime ================================================ FILE: internal/app/machined/pkg/runtime/drainer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "slices" "sync" ) // NewDrainer creates new drainer. func NewDrainer() *Drainer { return &Drainer{ shutdown: make(chan struct{}, 1), } } // Drainer is used in controllers to ensure graceful shutdown. type Drainer struct { subscriptionsMu sync.Mutex draining bool subscriptions []*DrainSubscription shutdown chan struct{} } // Drain initializes drain sequence waits for it to succeed until the context is canceled. func (d *Drainer) Drain(ctx context.Context) error { d.subscriptionsMu.Lock() if d.draining { d.subscriptionsMu.Unlock() return errors.New("already draining") } d.draining = true for _, s := range d.subscriptions { select { case s.events <- DrainEvent{}: default: } } d.subscriptionsMu.Unlock() for { d.subscriptionsMu.Lock() l := len(d.subscriptions) d.subscriptionsMu.Unlock() if l == 0 { return nil } select { case <-d.shutdown: case <-ctx.Done(): return ctx.Err() } } } // Subscribe should be called from a controller that needs graceful shutdown. func (d *Drainer) Subscribe() *DrainSubscription { d.subscriptionsMu.Lock() defer d.subscriptionsMu.Unlock() subscription := &DrainSubscription{ events: make(chan DrainEvent, 1), drainer: d, } if d.draining { subscription.events <- DrainEvent{} } d.subscriptions = append(d.subscriptions, subscription) return subscription } // DrainSubscription keeps ingoing and outgoing events channels. type DrainSubscription struct { drainer *Drainer events chan DrainEvent } // EventCh returns drain events channel. func (s *DrainSubscription) EventCh() <-chan DrainEvent { return s.events } // Cancel the subscription which triggers drain to shutdown. func (s *DrainSubscription) Cancel() { s.drainer.subscriptionsMu.Lock() for i, sub := range s.drainer.subscriptions { if sub == s { s.drainer.subscriptions = slices.Delete(s.drainer.subscriptions, i, i+1) break } } s.drainer.subscriptionsMu.Unlock() select { case s.drainer.shutdown <- struct{}{}: default: } } // DrainEvent is sent to the events channel when drainer starts the shutdown sequence. type DrainEvent struct{} ================================================ FILE: internal/app/machined/pkg/runtime/drainer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) //nolint:gocyclo func TestDrainer(t *testing.T) { drainer := runtime.NewDrainer() sub1 := drainer.Subscribe() sub2 := drainer.Subscribe() errCh := make(chan error) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() go func() { errCh <- drainer.Drain(ctx) }() select { case <-sub1.EventCh(): case <-time.After(time.Second): require.Fail(t, "should be notified") } select { case <-sub2.EventCh(): case <-time.After(time.Second): require.Fail(t, "should be notified") } select { case <-errCh: require.Fail(t, "shouldn't be drained now") default: } sub1.Cancel() select { case <-errCh: require.Fail(t, "shouldn't be drained now") default: } sub3 := drainer.Subscribe() select { case <-sub3.EventCh(): case <-time.After(time.Second): require.Fail(t, "should be notified") } sub3.Cancel() sub2.Cancel() select { case err := <-errCh: assert.NoError(t, err) case <-time.After(time.Second): require.Fail(t, "should be drained now") } } func TestDrainTimeout(t *testing.T) { drainer := runtime.NewDrainer() drainer.Subscribe() errCh := make(chan error) ctx, cancel := context.WithTimeout(t.Context(), time.Second) defer cancel() go func() { errCh <- drainer.Drain(ctx) }() select { case err := <-errCh: assert.ErrorIs(t, err, context.DeadlineExceeded) case <-time.After(5 * time.Second): require.Fail(t, "should be drained now") } } ================================================ FILE: internal/app/machined/pkg/runtime/emergency/emergency.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package emergency provides values to handle emergency (panic/unrecoverable error) handling for machined. package emergency import ( "sync/atomic" "golang.org/x/sys/unix" ) // RebootCmd is a command to reboot the system after an unrecoverable error. var RebootCmd atomic.Int64 func init() { RebootCmd.Store(unix.LINUX_REBOOT_CMD_RESTART) } ================================================ FILE: internal/app/machined/pkg/runtime/errors.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "errors" "fmt" ) var ( // ErrLocked indicates that the sequencer is currently locked, and processing // another sequence. ErrLocked = errors.New("locked") // ErrInvalidSequenceData indicates that the sequencer got data the wrong // data type for a sequence. ErrInvalidSequenceData = errors.New("invalid sequence data") // ErrUndefinedRuntime indicates that the sequencer's runtime is not defined. ErrUndefinedRuntime = errors.New("undefined runtime") ) // RebootError encapsulates unix.Reboot() cmd argument. type RebootError struct { Cmd int } func (e RebootError) Error() string { return fmt.Sprintf("unix.Reboot(%x)", e.Cmd) } // IsRebootError checks whether given error is RebootError. func IsRebootError(err error) bool { var rebootErr RebootError return errors.As(err, &rebootErr) } ================================================ FILE: internal/app/machined/pkg/runtime/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "fmt" "time" "github.com/rs/xid" "google.golang.org/protobuf/types/known/anypb" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ActorIDCtxKey is the context key used for event actor id. type ActorIDCtxKey struct{} // Event is what is sent on the wire. type Event struct { TypeURL string ID xid.ID Payload proto.Message ActorID string } // EventInfo unifies event and queue information for the WatchFunc. type EventInfo struct { Event Backlog int } // WatchFunc defines the watcher callback function. type WatchFunc func(<-chan EventInfo) // WatchOptions defines options for the watch call. // // Only one of TailEvents, TailID or TailDuration should be non-zero. type WatchOptions struct { // Return that many past events. // // If TailEvents is negative, return all the events available. TailEvents int // Start at ID > specified. TailID xid.ID // Start at timestamp Now() - TailDuration. TailDuration time.Duration // ActorID to ID of the actor to filter events by. ActorID string } // WatchOptionFunc defines the options for the watcher. type WatchOptionFunc func(opts *WatchOptions) error // WithTailEvents sets up Watcher to return specified number of past events. // // If number is negative, all the available past events are returned. func WithTailEvents(number int) WatchOptionFunc { return func(opts *WatchOptions) error { if !opts.TailID.IsNil() || opts.TailDuration != 0 { return errors.New("WithTailEvents can't be specified at the same time with WithTailID or WithTailDuration") } opts.TailEvents = number return nil } } // WithTailID sets up Watcher to return events with ID > TailID. func WithTailID(id xid.ID) WatchOptionFunc { return func(opts *WatchOptions) error { if opts.TailEvents != 0 || opts.TailDuration != 0 { return errors.New("WithTailID can't be specified at the same time with WithTailEvents or WithTailDuration") } opts.TailID = id return nil } } // WithTailDuration sets up Watcher to return events with timestamp >= (now - tailDuration). func WithTailDuration(dur time.Duration) WatchOptionFunc { return func(opts *WatchOptions) error { if opts.TailEvents != 0 || !opts.TailID.IsNil() { return errors.New("WithTailDuration can't be specified at the same time with WithTailEvents or WithTailID") } opts.TailDuration = dur return nil } } // WithActorID sets up Watcher to return events filtered by given actor id. func WithActorID(actorID string) WatchOptionFunc { return func(opts *WatchOptions) error { opts.ActorID = actorID return nil } } // Watcher defines a runtime event watcher. type Watcher interface { Watch(WatchFunc, ...WatchOptionFunc) error } // Publisher defines a runtime event publisher. type Publisher interface { Publish(context.Context, proto.Message) } // EventStream defines the runtime event stream. type EventStream interface { Watcher Publisher } // NewEvent creates a new event with the provided payload and actor ID. func NewEvent(payload proto.Message, actorID string) Event { typeURL := "" if payload != nil { typeURL = fmt.Sprintf("talos/runtime/%s", payload.ProtoReflect().Descriptor().FullName()) } return Event{ // In the future, we can publish `talos/runtime`, and // `talos/plugin/` (or something along those lines) events. // TypeURL: fmt.Sprintf("talos/runtime/%s", protoreflect.MessageDescriptor.FullName(msg)), TypeURL: typeURL, Payload: payload, ID: xid.New(), ActorID: actorID, } } // ToMachineEvent serializes Event as proto message machine.Event. func (event *Event) ToMachineEvent() (*machine.Event, error) { value, err := proto.Marshal(event.Payload) if err != nil { return nil, err } return &machine.Event{ Data: &anypb.Any{ TypeUrl: event.TypeURL, Value: value, }, Id: event.ID.String(), ActorId: event.ActorID, }, nil } ================================================ FILE: internal/app/machined/pkg/runtime/kernel_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package runtime import ( "errors" "fmt" "sync" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/xfs/fsopen" "github.com/siderolabs/talos/pkg/xfs/opentree" ) // KernelCap represents kernel capabilities that we can check at runtime. type KernelCap interface { // OpentreeOnAnonymousFS returns true if the kernel supports opentree on anonymous filesystems. OpentreeOnAnonymousFS() (bool, error) } // KernelCapabilities returns a singleton instance of KernelCap. var KernelCapabilities = sync.OnceValue(func() KernelCap { return &kernelCap{ opentreeOnAnonymousFSOnce: sync.OnceValues(canOpnetreeOnAnonymousFS), } }) type kernelCap struct { opentreeOnAnonymousFSOnce func() (bool, error) } // opentreeOnAnonymousFSOnce implements KernelCap. func (k *kernelCap) OpentreeOnAnonymousFS() (bool, error) { return k.opentreeOnAnonymousFSOnce() } func canOpnetreeOnAnonymousFS() (bool, error) { tmpfs := fsopen.New("tmpfs") mntfd, err := tmpfs.Open() if err != nil { return false, fmt.Errorf("unexpected error while checking for opentree on anonymous fs support: %w", err) } defer tmpfs.Close() //nolint:errcheck otfs := opentree.NewFromFd(mntfd) _, err = otfs.Open() defer otfs.Close() //nolint:errcheck if err == nil { return true, nil // yes, the kernel supports this } if errors.Is(err, unix.EINVAL) { return false, nil // no, the kernel does not supports this } return false, fmt.Errorf("unexpected error while checking for opentree on anonymous fs support: %w", err) } ================================================ FILE: internal/app/machined/pkg/runtime/logging/circular.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "bufio" "bytes" "context" "errors" "fmt" "io" "log" "maps" "sync" "time" corezstd "github.com/klauspost/compress/zstd" "github.com/siderolabs/go-circular" "github.com/siderolabs/go-circular/zstd" "github.com/siderolabs/go-debug" "github.com/siderolabs/go-tail" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // These constants should some day move to config. const ( // Overall capacity of the log buffer (in raw bytes, memory size will be smaller due to compression). DesiredCapacity = 1048576 // Some logs are tiny, no need to reserve too much memory. InitialCapacity = 16384 // Chunk capacity is the length of each chunk, it should be // big enough for the compression to be efficient. ChunkCapacity = 65536 // Number of zstd-compressed chunks to keep. NumCompressedChunks = (DesiredCapacity / ChunkCapacity) - 1 // Safety gap to avoid buffer overruns, can be lowered as with compression we don't need much. SafetyGap = 1 ) // CircularBufferLoggingManager implements logging to circular fixed size buffer. type CircularBufferLoggingManager struct { fallbackLogger *log.Logger buffers sync.Map compressor circular.Compressor sendersRW sync.RWMutex senders []runtime.LogSender sendersChanged chan struct{} lineWriter chan runtime.LogWriter } // NewCircularBufferLoggingManager initializes new CircularBufferLoggingManager. func NewCircularBufferLoggingManager(fallbackLogger *log.Logger) *CircularBufferLoggingManager { compressor, err := zstd.NewCompressor( corezstd.WithEncoderConcurrency(1), corezstd.WithWindowSize(2*corezstd.MinWindowSize), ) if err != nil { // should not happen panic(fmt.Sprintf("failed to create zstd compressor: %s", err)) } return &CircularBufferLoggingManager{ fallbackLogger: fallbackLogger, sendersChanged: make(chan struct{}), compressor: compressor, lineWriter: make(chan runtime.LogWriter, 1), } } // ServiceLog implements runtime.LoggingManager interface. func (manager *CircularBufferLoggingManager) ServiceLog(id string) runtime.LogHandler { return &circularHandler{ manager: manager, id: id, fields: map[string]any{ // use field name that is not used by anything else "talos-service": id, }, } } // SetSenders implements runtime.LoggingManager interface. func (manager *CircularBufferLoggingManager) SetSenders(senders []runtime.LogSender) []runtime.LogSender { manager.sendersRW.Lock() prevChanged := manager.sendersChanged manager.sendersChanged = make(chan struct{}) prevSenders := manager.senders manager.senders = senders manager.sendersRW.Unlock() close(prevChanged) return prevSenders } // SetLineWriter implements runtime.LoggingManager interface. func (manager *CircularBufferLoggingManager) SetLineWriter(w runtime.LogWriter) { select { case manager.lineWriter <- w: default: <-manager.lineWriter manager.lineWriter <- w } } // getSenders waits for senders to be set and returns them. func (manager *CircularBufferLoggingManager) getSenders() []runtime.LogSender { for { manager.sendersRW.RLock() senders, changed := manager.senders, manager.sendersChanged manager.sendersRW.RUnlock() if len(senders) > 0 { return senders } <-changed } } func (manager *CircularBufferLoggingManager) getBuffer(id string, create bool) (*circular.Buffer, bool, error) { buf, ok := manager.buffers.Load(id) if ok { return buf.(*circular.Buffer), false, nil } if !create { return nil, false, nil } b, err := circular.NewBuffer( circular.WithInitialCapacity(InitialCapacity), circular.WithMaxCapacity(ChunkCapacity), circular.WithNumCompressedChunks(NumCompressedChunks, manager.compressor), circular.WithSafetyGap(SafetyGap)) if err != nil { return nil, false, err // only configuration issue might raise error } buf, _ = manager.buffers.LoadOrStore(id, b) return buf.(*circular.Buffer), true, nil } // RegisteredLogs implements runtime.LoggingManager interface. func (manager *CircularBufferLoggingManager) RegisteredLogs() []string { var result []string manager.buffers.Range(func(key, val any) bool { result = append(result, key.(string)) return true }) return result } type circularHandler struct { manager *CircularBufferLoggingManager id string fields map[string]any buf *circular.Buffer } type nopCloser struct { io.Writer } func (nopCloser) Close() error { return nil } // Writer implements runtime.LogHandler interface. func (handler *circularHandler) Writer() (io.WriteCloser, error) { if handler.buf == nil { var ( created bool err error ) handler.buf, created, err = handler.manager.getBuffer(handler.id, true) if err != nil { return nil, err } if created { go func() { defer func() { if r := recover(); r != nil { handler.manager.fallbackLogger.Printf("log sender panic: %v", r) } }() if err := handler.runSenders(); err != nil { handler.manager.fallbackLogger.Printf("log senders stopped: %s", err) } }() go func() { defer func() { if r := recover(); r != nil { handler.manager.fallbackLogger.Printf("log writer panic: %v", r) } }() if err := handler.runLineWriter(); err != nil { handler.manager.fallbackLogger.Printf("log writer stopped: %s", err) } }() } } switch handler.id { case "machined": return &timeStampWriter{w: handler.buf}, nil default: return nopCloser{handler.buf}, nil } } // Reader implements runtime.LogHandler interface. func (handler *circularHandler) Reader(opts ...runtime.LogOption) (io.ReadCloser, error) { if handler.buf == nil { var err error handler.buf, _, err = handler.manager.getBuffer(handler.id, false) if err != nil { return nil, err } if handler.buf == nil { // only Writer() operation creates new buffers return nil, fmt.Errorf("log %q was not registered", handler.id) } } var opt runtime.LogOptions for _, o := range opts { if err := o(&opt); err != nil { return nil, err } } var r interface { io.ReadCloser io.Seeker } if opt.Follow { r = handler.buf.GetStreamingReader() } else { r = handler.buf.GetReader() } if opt.TailLines != nil { err := tail.SeekLines(r, *opt.TailLines) if err != nil { r.Close() //nolint:errcheck return nil, fmt.Errorf("error tailing log: %w", err) } } return r, nil } func (handler *circularHandler) runSenders() error { r, err := handler.Reader(runtime.WithFollow()) if err != nil { return err } defer r.Close() //nolint:errcheck scanner := bufio.NewScanner(r) for scanner.Scan() { l := bytes.TrimSpace(scanner.Bytes()) if len(l) == 0 { continue } e := parseLogLine(l, time.Now()) if e.Fields == nil { e.Fields = handler.fields } else { maps.Copy(e.Fields, handler.fields) } handler.resend(e) } return fmt.Errorf("scanner: %w", scanner.Err()) } // resend sends and resends given event until success or ErrDontRetry error. func (handler *circularHandler) resend(e *runtime.LogEvent) { for { senders := handler.manager.getSenders() sendCtx, sendCancel := context.WithTimeout(context.TODO(), 5*time.Second) sendErrors := make(chan error, len(senders)) for _, sender := range senders { go func() { sendErrors <- sender.Send(sendCtx, e) }() } var dontRetry bool for range senders { err := <-sendErrors // don't retry if at least one sender succeed to avoid implementing per-sender queue, etc if err == nil { dontRetry = true continue } if debug.Enabled { handler.manager.fallbackLogger.Print(err) } if errors.Is(err, runtime.ErrDontRetry) { dontRetry = true } } sendCancel() if dontRetry { return } time.Sleep(time.Second) } } func (handler *circularHandler) runLineWriter() error { r, err := handler.Reader(runtime.WithFollow()) if err != nil { return err } defer r.Close() //nolint:errcheck w := <-handler.manager.lineWriter select { case handler.manager.lineWriter <- w: default: } scanner := bufio.NewScanner(r) for scanner.Scan() { l := scanner.Bytes() err = w.WriteLog(handler.id, l) if err != nil { return fmt.Errorf("line writer: %w", err) } } return fmt.Errorf("scanner: %w", scanner.Err()) } // timeStampWriter is a writer that adds a timestamp to each line. type timeStampWriter struct { w io.WriteCloser } // Write implements the io.Writer interface. func (t *timeStampWriter) Write(p []byte) (int, error) { buf := make([]byte, 0, len(p)+27) // Current log.Logger implementation always adds a newline to the message, so we don't need to wait for it. buf = time.Now().AppendFormat(buf, "2006/01/02 15:04:05.000000") buf = append(buf, ' ') buf = append(buf, p...) n, err := t.w.Write(buf) switch { case err == nil && n == len(buf): return len(p), nil // success, return original length case err == nil && n != len(buf): return n, fmt.Errorf("time stamp writer error: %w", io.ErrShortWrite) default: return n, fmt.Errorf("time stamp writer internal error: %w", err) } } // Close implements the io.Closer interface. func (t *timeStampWriter) Close() error { return t.w.Close() } ================================================ FILE: internal/app/machined/pkg/runtime/logging/extract.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "bytes" "encoding/json" "math" "strings" "time" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) var maxEpochTS = float64(time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()) //nolint:gocyclo func parseLogLine(l []byte, now time.Time) *runtime.LogEvent { msg, m := parseJSONLogLine(l) e := &runtime.LogEvent{ Msg: msg, Time: now, Level: zapcore.InfoLevel, } if m == nil { return e } for _, k := range []string{"time", "ts"} { var t time.Time switch ts := m[k].(type) { case string: t, _ = time.Parse(time.RFC3339Nano, ts) //nolint:errcheck case float64: // seconds or milliseconds since epoch sec, fsec := math.Modf(ts) if sec > maxEpochTS { sec, fsec = math.Modf(ts / 1000) } t = time.Unix(int64(sec), int64(fsec*float64(time.Second))) } if !t.IsZero() { e.Time = t.UTC() delete(m, k) break } } if levelS, ok := m["level"].(string); ok { levelS = strings.ToLower(levelS) // convert containerd's logrus' level to zap's level if levelS == "warning" { levelS = "warn" } var level zapcore.Level if err := level.UnmarshalText([]byte(levelS)); err == nil { e.Level = level delete(m, "level") } } if msgS, ok := m["msg"].(string); ok { // in case we have both message before JSON and "msg" JSON field if e.Msg != "" { e.Msg += " " } e.Msg += strings.TrimSpace(msgS) delete(m, "msg") } if errS, ok := m["err"].(string); ok { if e.Level < zap.WarnLevel { e.Level = zap.WarnLevel } if e.Msg != "" { e.Msg += ": " } e.Msg += strings.TrimSpace(errS) delete(m, "err") } e.Fields = m return e } func parseJSONLogLine(l []byte) (msg string, m map[string]any) { // the whole line is valid JSON if err := json.Unmarshal(l, &m); err == nil { return msg, m } // the line is a message followed by JSON if i := bytes.Index(l, []byte("{")); i != -1 { if err := json.Unmarshal(l[i:], &m); err == nil { msg = string(bytes.TrimSpace(l[:i])) return msg, m } } // no JSON found msg = string(l) return msg, m } ================================================ FILE: internal/app/machined/pkg/runtime/logging/extract_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging //nolint:testpackage import ( "testing" "time" "github.com/stretchr/testify/assert" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) func TestParseLogLine(t *testing.T) { t.Parallel() now := time.Date(2021, 10, 19, 12, 42, 37, 123456789, time.UTC) for name, tc := range map[string]struct { l string expected *runtime.LogEvent }{ "machined": { l: `[talos] task updateBootloader (1/1): done, 219.885384ms`, expected: &runtime.LogEvent{ Msg: `[talos] task updateBootloader (1/1): done, 219.885384ms`, Time: now, Level: zapcore.InfoLevel, }, }, "controller-runtime": { l: `reconfigured wireguard link {"component": "controller-runtime", "controller": "network.LinkSpecController", "link": "kubespan", "peers": 4}`, expected: &runtime.LogEvent{ Msg: `reconfigured wireguard link`, Time: now, Level: zapcore.InfoLevel, Fields: map[string]any{ "component": "controller-runtime", "controller": "network.LinkSpecController", "link": "kubespan", "peers": float64(4), }, }, }, "etcd-zap": { l: `{"level":"info","ts":"2021-10-19T14:53:05.815Z","caller":"mvcc/kvstore_compaction.go:57","msg":"finished scheduled compaction","compact-revision":34567,"took":"21.041639ms"}`, expected: &runtime.LogEvent{ Msg: `finished scheduled compaction`, Time: time.Date(2021, 10, 19, 14, 53, 5, 815000000, time.UTC), Level: zapcore.InfoLevel, Fields: map[string]any{ "caller": "mvcc/kvstore_compaction.go:57", "compact-revision": float64(34567), "took": "21.041639ms", }, }, }, "cri-logrus": { l: `{"level":"warning","msg":"cleanup warnings time=\"2021-10-19T14:52:20Z\" level=info msg=\"starting signal loop\" namespace=k8s.io pid=2629\n","time":"2021-10-19T14:52:20.578858689Z"}`, expected: &runtime.LogEvent{ Msg: `cleanup warnings time="2021-10-19T14:52:20Z" level=info msg="starting signal loop" namespace=k8s.io pid=2629`, Time: time.Date(2021, 10, 19, 14, 52, 20, 578858689, time.UTC), Level: zapcore.WarnLevel, Fields: map[string]any{}, }, }, "kubelet": { l: `{"ts":1635266764792.703,"caller":"topologymanager/scope.go:110","msg":"RemoveContainer","v":0,"containerID":"0194fac91ac1d3949497f6912f3c7e73a062c3bf29b6d3da05557d4db2f8482b"}`, expected: &runtime.LogEvent{ Msg: `RemoveContainer`, Time: time.Date(2021, 10, 26, 16, 46, 4, 792702913, time.UTC), Level: zapcore.InfoLevel, Fields: map[string]any{ "caller": "topologymanager/scope.go:110", "containerID": "0194fac91ac1d3949497f6912f3c7e73a062c3bf29b6d3da05557d4db2f8482b", "v": float64(0), }, }, }, "kubelet-err": { l: `{"ts":1635266751595.943,"caller":"kubelet/kubelet.go:1703","msg":"Failed creating a mirror pod for",` + `"pod":"kube-system/kube-controller-manager-talos-dev-qemu-master-1","err":"pods \"kube-controller-manager-talos-dev-qemu-master-1\" already exists"}`, expected: &runtime.LogEvent{ Msg: `Failed creating a mirror pod for: pods "kube-controller-manager-talos-dev-qemu-master-1" already exists`, Time: time.Date(2021, 10, 26, 16, 45, 51, 595943212, time.UTC), Level: zapcore.WarnLevel, Fields: map[string]any{ "caller": "kubelet/kubelet.go:1703", "pod": "kube-system/kube-controller-manager-talos-dev-qemu-master-1", }, }, }, } { t.Run(name, func(t *testing.T) { t.Parallel() actual := parseLogLine([]byte(tc.l), now) assert.Equal(t, tc.expected, actual) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/logging/file.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "context" "errors" "fmt" "io" "os" "path/filepath" "strings" "github.com/siderolabs/gen/containers" "github.com/siderolabs/go-tail" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/follow" ) // FileLoggingManager implements simple logging to files. type FileLoggingManager struct { logDirectory string registeredLogs containers.ConcurrentMap[string, struct{}] } // NewFileLoggingManager initializes new FileLoggingManager. func NewFileLoggingManager(logDirectory string) *FileLoggingManager { return &FileLoggingManager{ logDirectory: logDirectory, } } // ServiceLog implements runtime.LoggingManager interface. func (manager *FileLoggingManager) ServiceLog(id string) runtime.LogHandler { return &fileLogHandler{ logDirectory: manager.logDirectory, id: id, manager: manager, } } // SetSenders implements runtime.LoggingManager interface (by doing nothing). func (manager *FileLoggingManager) SetSenders([]runtime.LogSender) []runtime.LogSender { return nil } // SetLineWriter implements runtime.LoggingManager interface (by doing nothing). func (manager *FileLoggingManager) SetLineWriter(runtime.LogWriter) {} // RegisteredLogs implements runtime.LoggingManager interface. func (manager *FileLoggingManager) RegisteredLogs() []string { var result []string manager.registeredLogs.ForEach(func(key string, _ struct{}) { result = append(result, key) }) return result } type fileLogHandler struct { path string logDirectory string id string manager *FileLoggingManager } func (handler *fileLogHandler) buildPath() error { if strings.ContainsAny(handler.id, string(os.PathSeparator)+".") { return errors.New("service ID is invalid") } handler.path = filepath.Join(handler.logDirectory, handler.id+".log") return nil } // Writer implements runtime.LogHandler interface. func (handler *fileLogHandler) Writer() (io.WriteCloser, error) { if err := handler.buildPath(); err != nil { return nil, err } result, err := os.OpenFile(handler.path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o666) if err != nil { return nil, err } handler.manager.registeredLogs.GetOrCreate(handler.id, struct{}{}) return result, nil } // Reader implements runtime.LogHandler interface. func (handler *fileLogHandler) Reader(opts ...runtime.LogOption) (io.ReadCloser, error) { var opt runtime.LogOptions for _, o := range opts { if err := o(&opt); err != nil { return nil, err } } if err := handler.buildPath(); err != nil { return nil, err } f, err := os.OpenFile(handler.path, os.O_RDONLY, 0) if err != nil { return nil, err } if opt.TailLines != nil { err = tail.SeekLines(f, *opt.TailLines) if err != nil { f.Close() //nolint:errcheck return nil, fmt.Errorf("error tailing log: %w", err) } } if opt.Follow { return follow.NewReader(context.Background(), f), nil } return f, nil } ================================================ FILE: internal/app/machined/pkg/runtime/logging/logging.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package logging provides implementations of runtime.LoggingManager. package logging ================================================ FILE: internal/app/machined/pkg/runtime/logging/null.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "io" "os" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // NullLoggingManager sends all the logs to /dev/null. type NullLoggingManager struct{} // NewNullLoggingManager initializes NullLoggingManager. func NewNullLoggingManager() *NullLoggingManager { return &NullLoggingManager{} } // ServiceLog implements LoggingManager. func (*NullLoggingManager) ServiceLog(id string) runtime.LogHandler { return &nullLogHandler{} } // SetSenders implements runtime.LoggingManager interface (by doing nothing). func (*NullLoggingManager) SetSenders([]runtime.LogSender) []runtime.LogSender { return nil } // SetLineWriter implements runtime.LoggingManager interface (by doing nothing). func (*NullLoggingManager) SetLineWriter(runtime.LogWriter) {} // RegisteredLogs implements runtime.LoggingManager interface (by doing nothing). func (*NullLoggingManager) RegisteredLogs() []string { return nil } type nullLogHandler struct{} func (*nullLogHandler) Writer() (io.WriteCloser, error) { return os.OpenFile(os.DevNull, os.O_WRONLY, 0) } func (*nullLogHandler) Reader(...runtime.LogOption) (io.ReadCloser, error) { return os.OpenFile(os.DevNull, os.O_RDONLY, 0) } ================================================ FILE: internal/app/machined/pkg/runtime/logging/sender_jsonlines.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "context" "encoding/json" "fmt" "maps" "net" "net/url" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/config/config" ) type jsonLinesSender struct { endpoint *url.URL extraTags map[string]string sema chan struct{} conn net.Conn } // NewJSONLines returns log sender that sends logs in JSON over TCP (newline-delimited) // or UDP (one message per packet). func NewJSONLines(cfg config.LoggingDestination) runtime.LogSender { sema := make(chan struct{}, 1) sema <- struct{}{} return &jsonLinesSender{ endpoint: cfg.Endpoint(), extraTags: cfg.ExtraTags(), sema: sema, } } func (j *jsonLinesSender) tryLock(ctx context.Context) (unlock func()) { select { case <-j.sema: unlock = func() { j.sema <- struct{}{} } case <-ctx.Done(): unlock = nil } return unlock } func (j *jsonLinesSender) marshalJSON(e *runtime.LogEvent) ([]byte, error) { m := make(map[string]any, len(e.Fields)+3) maps.Copy(m, e.Fields) m["msg"] = e.Msg m["talos-time"] = e.Time.Format(time.RFC3339Nano) m["talos-level"] = e.Level.String() for k, v := range j.extraTags { m[k] = v } return json.Marshal(m) } // Send implements LogSender interface. func (j *jsonLinesSender) Send(ctx context.Context, e *runtime.LogEvent) error { b, err := j.marshalJSON(e) if err != nil { return fmt.Errorf("%w: %s", runtime.ErrDontRetry, err) } if j.endpoint.Scheme == "tcp" { b = append(b, '\n') } unlock := j.tryLock(ctx) if unlock == nil { return ctx.Err() } defer unlock() // Connect (or "connect" for UDP) if no connection is established already. if j.conn == nil { conn, err := new(net.Dialer).DialContext(ctx, j.endpoint.Scheme, j.endpoint.Host) if err != nil { return err } j.conn = conn } d, _ := ctx.Deadline() j.conn.SetWriteDeadline(d) //nolint:errcheck // Close connection on send error. if n, err := j.conn.Write(b); err != nil { j.conn.Close() //nolint:errcheck j.conn = nil // skip partially sent events to avoid partial duplicates in the receiver if n > 0 { err = fmt.Errorf("%w: %s", runtime.ErrDontRetry, err) } return err } return nil } // Close implements LogSender interface. func (j *jsonLinesSender) Close(ctx context.Context) error { unlock := j.tryLock(ctx) if unlock == nil { return ctx.Err() } defer unlock() if j.conn == nil { return nil } conn := j.conn j.conn = nil closed := make(chan error, 1) go func() { closed <- conn.Close() }() select { case <-ctx.Done(): return ctx.Err() case err := <-closed: return err } } ================================================ FILE: internal/app/machined/pkg/runtime/logging/sender_jsonlines_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging_test import ( "bufio" "context" "encoding/json" "net" "net/url" "sync" "testing" "time" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/pkg/machinery/constants" ) func udpHandler(ctx context.Context, t *testing.T, conn net.PacketConn, sendCh chan<- []byte) { t.Helper() for { select { case <-ctx.Done(): return default: } if err := conn.SetReadDeadline(time.Now().Add(10 * time.Millisecond)); err != nil { t.Logf("failed to set read deadline: %v", err) return } buf := make([]byte, 1024) n, _, err := conn.ReadFrom(buf) if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } t.Logf("failed to read from UDP connection: %v", err) return } if !channel.SendWithContext(ctx, sendCh, buf[:n]) { return } } } func tcpHandler(ctx context.Context, t *testing.T, conn net.Listener, sendCh chan<- []byte) { t.Helper() for { select { case <-ctx.Done(): return default: } if err := conn.(*net.TCPListener).SetDeadline(time.Now().Add(10 * time.Millisecond)); err != nil { t.Logf("failed to set accept deadline: %v", err) return } c, err := conn.Accept() if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { continue } t.Logf("failed to accept UDP connection: %v", err) return } go func() { defer c.Close() //nolint:errcheck scanner := bufio.NewScanner(c) for scanner.Scan() { if !channel.SendWithContext(ctx, sendCh, scanner.Bytes()) { return } } }() } } type loggingDestination struct { endpoint *url.URL extraTags map[string]string } func (l *loggingDestination) Endpoint() *url.URL { return l.endpoint } func (l *loggingDestination) ExtraTags() map[string]string { return l.extraTags } func (l *loggingDestination) Format() string { return constants.LoggingFormatJSONLines } func TestSenderJSONLines(t *testing.T) { //nolint:tparallel t.Parallel() lisUDP, err := (&net.ListenConfig{}).ListenPacket(t.Context(), "udp", "127.0.0.1:0") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, lisUDP.Close()) }) lisTCP, err := (&net.ListenConfig{}).Listen(t.Context(), "tcp", "127.0.0.1:0") require.NoError(t, err) t.Cleanup(func() { require.NoError(t, lisTCP.Close()) }) udpEndpoint := lisUDP.LocalAddr().String() tcpEndpoint := lisTCP.Addr().String() ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) t.Cleanup(cancel) sendCh := make(chan []byte, 32) var wg sync.WaitGroup wg.Go(func() { udpHandler(ctx, t, lisUDP, sendCh) }) wg.Go(func() { tcpHandler(ctx, t, lisTCP, sendCh) }) t.Cleanup(wg.Wait) for _, test := range []struct { name string endpoint *url.URL extraTags map[string]string messages []*runtime.LogEvent expected []map[string]any }{ { name: "UDP", endpoint: ensure.Value(url.Parse("udp://" + udpEndpoint)), messages: []*runtime.LogEvent{ { Msg: "msg1", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:00Z")), Level: zapcore.InfoLevel, Fields: map[string]any{ "field1": "value1", }, }, { Msg: "msg2", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:01Z")), Level: zapcore.DebugLevel, }, }, expected: []map[string]any{ { "field1": "value1", "msg": "msg1", "talos-level": "info", "talos-time": "2021-01-01T00:00:00Z", }, { "msg": "msg2", "talos-level": "debug", "talos-time": "2021-01-01T00:00:01Z", }, }, }, { name: "UDP with extra tags", endpoint: ensure.Value(url.Parse("udp://" + udpEndpoint)), extraTags: map[string]string{ "extra1": "value1", }, messages: []*runtime.LogEvent{ { Msg: "msg1", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:00Z")), Level: zapcore.InfoLevel, Fields: map[string]any{ "field1": "value1", }, }, { Msg: "msg2", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:01Z")), Level: zapcore.DebugLevel, }, }, expected: []map[string]any{ { "field1": "value1", "extra1": "value1", "msg": "msg1", "talos-level": "info", "talos-time": "2021-01-01T00:00:00Z", }, { "msg": "msg2", "extra1": "value1", "talos-level": "debug", "talos-time": "2021-01-01T00:00:01Z", }, }, }, { name: "TCP", endpoint: ensure.Value(url.Parse("tcp://" + tcpEndpoint)), messages: []*runtime.LogEvent{ { Msg: "hello", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:00Z")), Level: zapcore.InfoLevel, Fields: map[string]any{ "field1": "value1", }, }, }, expected: []map[string]any{ { "field1": "value1", "msg": "hello", "talos-level": "info", "talos-time": "2021-01-01T00:00:00Z", }, }, }, { name: "TCP with extra tags", endpoint: ensure.Value(url.Parse("tcp://" + tcpEndpoint)), extraTags: map[string]string{ "extra1": "value1", }, messages: []*runtime.LogEvent{ { Msg: "hello", Time: ensure.Value(time.Parse(time.RFC3339Nano, "2021-01-01T00:00:00Z")), Level: zapcore.InfoLevel, Fields: map[string]any{ "field1": "value1", }, }, }, expected: []map[string]any{ { "field1": "value1", "extra1": "value1", "msg": "hello", "talos-level": "info", "talos-time": "2021-01-01T00:00:00Z", }, }, }, } { t.Run(test.name, func(t *testing.T) { // not parallel - need sequential execution loggingCfg := &loggingDestination{ endpoint: test.endpoint, extraTags: test.extraTags, } sender := logging.NewJSONLines(loggingCfg) for _, msg := range test.messages { require.NoError(t, sender.Send(ctx, msg)) } for _, expected := range test.expected { select { case <-time.After(time.Second): t.Fatalf("timed out waiting for message") case msg := <-sendCh: var m map[string]any require.NoError(t, json.Unmarshal(msg, &m)) require.Equal(t, expected, m) } } require.NoError(t, sender.Close(ctx)) }) } cancel() } ================================================ FILE: internal/app/machined/pkg/runtime/logging.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "errors" "io" "time" "go.uber.org/zap/zapcore" ) // LoggingManager provides unified interface to publish and consume logs. type LoggingManager interface { // ServiceLog privides a log handler for a given service (that may not exist). ServiceLog(service string) LogHandler // SetSenders sets log senders for all derived log handlers // and returns the previous ones for closing. // // SetSenders should be thread-safe. SetSenders(senders []LogSender) []LogSender // SetLineWriter sets a writer that will receive raw log lines. // // SetLineWriter can be only singularly called, subsequent calls override previous writer. SetLineWriter(w LogWriter) // RegisteredLogs returns a list of registered logs containers. RegisteredLogs() []string } // LogOptions for LogHandler.Reader. type LogOptions struct { Follow bool TailLines *int } // LogOption provides functional options for LogHandler.Reader. type LogOption func(*LogOptions) error // WithFollow enables follow mode for the logs. func WithFollow() LogOption { return func(o *LogOptions) error { o.Follow = true return nil } } // WithTailLines starts log reading from lines from the tail of the log. func WithTailLines(lines int) LogOption { return func(o *LogOptions) error { o.TailLines = &lines return nil } } // LogHandler provides interface to access particular log source. type LogHandler interface { Writer() (io.WriteCloser, error) Reader(opt ...LogOption) (io.ReadCloser, error) } // LogEvent represents a log message to be send. type LogEvent struct { Msg string Time time.Time Level zapcore.Level Fields map[string]any } // ErrDontRetry indicates that log event should not be resent. var ErrDontRetry = errors.New("don't retry") // LogSender provides common interface for log senders. type LogSender interface { // Send tries to send the log event once, exiting on success, error, or context cancelation. // // Returned error is nil on success, non-nil otherwise. // As a special case, Send can return (possibly wrapped) ErrDontRetry if the log event should not be resent // (if it is invalid, if it was sent partially, etc). // // Send should be thread-safe. Send(ctx context.Context, e *LogEvent) error // Close stops the sender gracefully if possible, or forcefully on context cancelation. // // Close should be thread-safe. Close(ctx context.Context) error } // LogWriter provider common interface for text-based log writers. type LogWriter interface { // WriteLog writes a single log line. // // WriteLog should be thread-safe. WriteLog(id string, line []byte) error } ================================================ FILE: internal/app/machined/pkg/runtime/mode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "fmt" ) // Mode is a runtime mode. type Mode int // ModeCapability describes mode capability flags. type ModeCapability uint64 const ( // ModeCloud is the cloud runtime mode. ModeCloud Mode = iota // ModeContainer is the container runtime mode. ModeContainer // ModeMetal is the metal runtime mode. ModeMetal // ModeMetalAgent is the metal agent runtime mode. ModeMetalAgent ) const ( // Reboot node reboot. Reboot ModeCapability = 1 << iota // Rollback node rollback. Rollback // Shutdown node shutdown. Shutdown // Upgrade node upgrade. Upgrade // MetaKV is META partition. MetaKV ) const ( cloud = "cloud" container = "container" metal = "metal" metalAgent = "metal-agent" ) // String returns the string representation of a Mode. func (m Mode) String() string { return [...]string{cloud, container, metal, metalAgent}[m] } // RequiresInstall implements config.RuntimeMode. func (m Mode) RequiresInstall() bool { return m == ModeMetal } // InContainer implements config.RuntimeMode. func (m Mode) InContainer() bool { return m == ModeContainer } // Supports returns mode capability. func (m Mode) Supports(feature ModeCapability) bool { return (m.capabilities() & uint64(feature)) != 0 } // IsAgent returns true if the mode is an agent mode (i.e. metal agent mode). func (m Mode) IsAgent() bool { return m == ModeMetalAgent } // ParseMode returns a `Mode` that matches the specified string. func ParseMode(s string) (mod Mode, err error) { switch s { case cloud: mod = ModeCloud case container: mod = ModeContainer case metal: mod = ModeMetal case metalAgent: mod = ModeMetalAgent default: return mod, fmt.Errorf("unknown runtime mode: %q", s) } return mod, nil } func (m Mode) capabilities() uint64 { all := ^uint64(0) return [...]uint64{ // metal all, // container all ^ uint64(Reboot|Shutdown|Upgrade|Rollback|MetaKV), // cloud all, }[m] } ================================================ FILE: internal/app/machined/pkg/runtime/mode_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint package runtime_test import ( "testing" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) func TestMode_String(t *testing.T) { tests := []struct { name string m runtime.Mode want string }{ { name: "cloud", m: runtime.ModeCloud, want: "cloud", }, { name: "container", m: runtime.ModeContainer, want: "container", }, { name: "metal", m: runtime.ModeMetal, want: "metal", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.m.String(); got != tt.want { t.Errorf("Mode.String() = %v, want %v", got, tt.want) } }) } } func TestParseMode(t *testing.T) { type args struct { s string } tests := []struct { name string args args wantM runtime.Mode wantErr bool }{ { name: "cloud", args: args{"cloud"}, wantM: runtime.ModeCloud, wantErr: false, }, { name: "container", args: args{"container"}, wantM: runtime.ModeContainer, wantErr: false, }, { name: "metal", args: args{"metal"}, wantM: runtime.ModeMetal, wantErr: false, }, { name: "invalid", args: args{"invalid"}, wantM: 0, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotM, err := runtime.ParseMode(tt.args.s) if (err != nil) != tt.wantErr { t.Errorf("ParseMode() error = %v, wantErr %v", err, tt.wantErr) return } if gotM != tt.wantM { t.Errorf("ParseMode() = %v, want %v", gotM, tt.wantM) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/platform.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // Platform defines the requirements for a platform. type Platform interface { // Name returns platform name. Name() string // Mode returns platform mode (metal, cloud or container). Mode() Mode // Configuration fetches the machine configuration from platform-specific location. // // On cloud-like platform it is user-data in metadata service. // For metal platform that is either `talos.config=` URL or mounted ISO image. Configuration(context.Context, state.State) ([]byte, error) // KernelArgs returns additional kernel arguments which should be injected for the kernel boot. KernelArgs(arch string, quirks quirks.Quirks) procfs.Parameters // NetworkConfiguration fetches network configuration from the platform metadata. // // Controller will run this in function a separate goroutine, restarting it // on error. Platform is expected to deliver network configuration over the channel, // including updates to the configuration over time. NetworkConfiguration(context.Context, state.State, chan<- *PlatformNetworkConfig) error } // PlatformNetworkConfig describes the network configuration produced by the platform. // // This structure is marshaled to STATE partition to persist cached network configuration across // reboots. type PlatformNetworkConfig = network.PlatformConfigSpec ================================================ FILE: internal/app/machined/pkg/runtime/runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "time" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // Runtime defines the runtime parameters. type Runtime interface { //nolint:interfacebloat Config() config.Config ConfigContainer() config.Container ConfigCompleteForBoot() bool RollbackToConfigAfter(time.Duration) error CancelConfigRollbackTimeout() SetConfig(config.Provider) error SetPersistedConfig(config.Provider) error CanApplyImmediate(config.Provider) error State() State Events() EventStream Logging() LoggingManager NodeName() (string, error) IsBootstrapAllowed() bool GetSystemInformation(ctx context.Context) (*hardware.SystemInformation, error) } ================================================ FILE: internal/app/machined/pkg/runtime/sequencer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "fmt" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // Sequence represents a sequence type. type Sequence int const ( // SequenceNoop is the noop sequence. SequenceNoop Sequence = iota // SequenceBoot is the boot sequence. SequenceBoot // SequenceInitialize is the initialize sequence. SequenceInitialize // SequenceInstall is the install sequence. SequenceInstall // SequenceShutdown is the shutdown sequence. SequenceShutdown // SequenceUpgrade is the upgrade sequence. SequenceUpgrade // SequenceStageUpgrade is the stage upgrade sequence. SequenceStageUpgrade // SequenceMaintenanceUpgrade is the upgrade sequence in maintenance mode. SequenceMaintenanceUpgrade // SequenceReset is the reset sequence. SequenceReset // SequenceReboot is the reboot sequence. SequenceReboot ) const ( boot = "boot" initialize = "initialize" install = "install" shutdown = "shutdown" upgrade = "upgrade" stageUpgrade = "stageUpgrade" maintenanceUpgrade = "maintenanceUpgrade" reset = "reset" reboot = "reboot" noop = "noop" ) var sequenceTakeOver = map[Sequence]map[Sequence]struct{}{ SequenceInitialize: { SequenceMaintenanceUpgrade: {}, }, SequenceBoot: { SequenceReboot: {}, SequenceReset: {}, SequenceUpgrade: {}, }, SequenceReboot: { SequenceReboot: {}, }, SequenceReset: { SequenceReboot: {}, }, } // String returns the string representation of a `Sequence`. func (s Sequence) String() string { return [...]string{noop, boot, initialize, install, shutdown, upgrade, stageUpgrade, maintenanceUpgrade, reset, reboot}[s] } // CanTakeOver defines sequences priority. // // | what is running (columns) what is requested (rows) | boot | reboot | reset | upgrade | // |----------------------------------------------------|------|--------|-------|---------| // | reboot | Y | Y | Y | N | // | reset | Y | N | N | N | // | upgrade | Y | N | N | N |. func (s Sequence) CanTakeOver(running Sequence) bool { if running == SequenceNoop { return true } if sequences, ok := sequenceTakeOver[running]; ok { if _, ok = sequences[s]; ok { return true } } return false } // ParseSequence returns a `Sequence` that matches the specified string. // //nolint:gocyclo func ParseSequence(s string) (seq Sequence, err error) { switch s { case boot: seq = SequenceBoot case initialize: seq = SequenceInitialize case install: seq = SequenceInstall case shutdown: seq = SequenceShutdown case upgrade: seq = SequenceUpgrade case stageUpgrade: seq = SequenceStageUpgrade case maintenanceUpgrade: seq = SequenceMaintenanceUpgrade case reset: seq = SequenceReset case reboot: seq = SequenceReboot case noop: seq = SequenceNoop default: return seq, fmt.Errorf("unknown runtime sequence: %q", s) } return seq, nil } // ResetOptions are parameters to Reset sequence. type ResetOptions interface { GetGraceful() bool GetReboot() bool GetMode() machine.ResetRequest_WipeMode GetUserDisksToWipe() []string GetSystemDiskTargets() []PartitionTarget GetSystemDiskPaths() []string } // PartitionTarget provides interface to the disk partition. type PartitionTarget interface { Wipe(context.Context, func(string, ...any)) error GetLabel() string } // Sequencer describes the set of sequences required for the lifecycle // management of the operating system. type Sequencer interface { Boot(Runtime) []Phase Initialize(Runtime) []Phase Install(Runtime) []Phase Reboot(Runtime, *machine.RebootRequest) []Phase Reset(Runtime, ResetOptions) []Phase Shutdown(Runtime, *machine.ShutdownRequest) []Phase StageUpgrade(Runtime, *machine.UpgradeRequest) []Phase Upgrade(Runtime, *machine.UpgradeRequest) []Phase MaintenanceUpgrade(Runtime, *machine.UpgradeRequest) []Phase } // EventSequenceStart represents the sequence start event. type EventSequenceStart struct { Sequence Sequence } // EventFatalSequencerError represents a fatal sequencer error. type EventFatalSequencerError struct { Error error Sequence Sequence } ================================================ FILE: internal/app/machined/pkg/runtime/sequencer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint package runtime_test import ( "testing" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) func TestSequence_String(t *testing.T) { tests := []struct { name string s runtime.Sequence want string }{ { name: "boot", s: runtime.SequenceBoot, want: "boot", }, { name: "initialize", s: runtime.SequenceInitialize, want: "initialize", }, { name: "shutdown", s: runtime.SequenceShutdown, want: "shutdown", }, { name: "upgrade", s: runtime.SequenceUpgrade, want: "upgrade", }, { name: "stageUpgrade", s: runtime.SequenceStageUpgrade, want: "stageUpgrade", }, { name: "reboot", s: runtime.SequenceReboot, want: "reboot", }, { name: "reset", s: runtime.SequenceReset, want: "reset", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := tt.s.String(); got != tt.want { t.Errorf("Sequence.String() = %v, want %v", got, tt.want) } }) } } func TestParseSequence(t *testing.T) { type args struct { s string } tests := []struct { name string args args wantSeq runtime.Sequence wantErr bool }{ { name: "boot", args: args{"boot"}, wantSeq: runtime.SequenceBoot, wantErr: false, }, { name: "initialize", args: args{"initialize"}, wantSeq: runtime.SequenceInitialize, wantErr: false, }, { name: "shutdown", args: args{"shutdown"}, wantSeq: runtime.SequenceShutdown, wantErr: false, }, { name: "upgrade", args: args{"upgrade"}, wantSeq: runtime.SequenceUpgrade, wantErr: false, }, { name: "stageUpgrade", args: args{"stageUpgrade"}, wantSeq: runtime.SequenceStageUpgrade, wantErr: false, }, { name: "reboot", args: args{"reboot"}, wantSeq: runtime.SequenceReboot, wantErr: false, }, { name: "reset", args: args{"reset"}, wantSeq: runtime.SequenceReset, wantErr: false, }, { name: "invalid", args: args{"invalid"}, wantSeq: 0, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotSeq, err := runtime.ParseSequence(tt.args.s) if (err != nil) != tt.wantErr { t.Errorf("ParseSequence() error = %v, wantErr %v", err, tt.wantErr) return } if gotSeq != tt.wantSeq { t.Errorf("ParseSequence() = %v, want %v", gotSeq, tt.wantSeq) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/registry" configcore "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" ) // State defines the state. type State interface { Platform() Platform Machine() MachineState Cluster() ClusterState V1Alpha2() V1Alpha2State } // Machine defines the runtime parameters. type Machine interface { State() MachineState Config() config.MachineConfig } // MachineState defines the machined state. type MachineState interface { Installed() bool IsInstallStaged() bool StagedInstallImageRef() string StagedInstallOptions() []byte KexecPrepared(bool) IsKexecPrepared() bool DBus() DBusState Meta() Meta } // Meta defines the access to META partition. type Meta interface { ReadTag(t uint8) (val string, ok bool) ReadTagBytes(t uint8) (val []byte, ok bool) SetTag(ctx context.Context, t uint8, val string) (bool, error) SetTagBytes(ctx context.Context, t uint8, val []byte) (bool, error) DeleteTag(ctx context.Context, t uint8) (bool, error) Reload(ctx context.Context) error Flush() error } // ClusterState defines the cluster state. type ClusterState any // V1Alpha2State defines the next generation (v2) interface binding into v1 runtime. type V1Alpha2State interface { Resources() state.State NamespaceRegistry() *registry.NamespaceRegistry ResourceRegistry() *registry.ResourceRegistry GetConfig(context.Context) (configcore.Provider, error) SetConfig(context.Context, string, configcore.Provider) error } // DBusState defines the D-Bus logind mock. type DBusState interface { Start() error Stop() error WaitShutdown(ctx context.Context) error } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/acpi/acpi.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package acpi import ( "errors" "fmt" "log" "os" "strings" "github.com/hashicorp/go-multierror" "github.com/mdlayher/genetlink" "github.com/mdlayher/netlink" ) const ( // PowerButtonEvent is the ACPI event name associated with the power off // button. PowerButtonEvent = "button/power" // See https://github.com/torvalds/linux/blob/master/drivers/acpi/event.c acpiGenlFamilyName = "acpi_event" acpiGenlMcastGroupName = "acpi_mc_group" ) // StartACPIListener starts listening for ACPI netlink events. // //nolint:gocyclo func StartACPIListener() (err error) { // Get the acpi_event family. conn, err := genetlink.Dial(nil) if err != nil { return err } f, err := conn.GetFamily(acpiGenlFamilyName) if errors.Is(err, os.ErrNotExist) { //nolint:errcheck conn.Close() return fmt.Errorf(acpiGenlFamilyName+" not available: %w", err) } var id uint32 for _, group := range f.Groups { if group.Name == acpiGenlMcastGroupName { id = group.ID } } if err = conn.JoinGroup(id); err != nil { //nolint:errcheck conn.Close() return err } //nolint:errcheck defer conn.Close() for { msgs, _, err := conn.Receive() if err != nil { return fmt.Errorf("error reading from ACPI channel: %w", err) } if len(msgs) > 0 { ok, err := parse(msgs, PowerButtonEvent) if err != nil { log.Printf("failed to parse netlink message: %v", err) continue } if !ok { continue } return nil } } } func parse(msgs []genetlink.Message, event string) (bool, error) { var result *multierror.Error for _, msg := range msgs { ad, err := netlink.NewAttributeDecoder(msg.Data) if err != nil { result = multierror.Append(result, fmt.Errorf("failed to create attribute decoder: %w", err)) continue } for ad.Next() { if strings.HasPrefix(ad.String(), event) { return true, nil } log.Printf("ignoring ACPI event: %q", ad.String()) } } return false, result.ErrorOrNil() } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/acpi/acpi_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint,testpackage package acpi import ( "testing" "github.com/mdlayher/genetlink" ) func Test_parse(t *testing.T) { type args struct { msgs []genetlink.Message event string } tests := []struct { name string args args want bool wantErr bool }{ { name: PowerButtonEvent, args: args{ msgs: []genetlink.Message{ { Header: genetlink.Header{ Command: 1, Version: 1, }, Data: []byte{48, 0, 1, 0, 98, 117, 116, 116, 111, 110, 47, 112, 111, 119, 101, 114, 0, 0, 0, 0, 0, 0, 0, 0, 76, 78, 88, 80, 87, 82, 66, 78, 58, 48, 48, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0}, }, }, event: PowerButtonEvent, }, want: true, wantErr: false, }, { name: "battery", args: args{ msgs: []genetlink.Message{ { Header: genetlink.Header{ Command: 1, Version: 1, }, Data: []byte{48, 0, 1, 0, 98, 117, 116, 116, 111, 110, 47, 112, 111, 119, 101, 114, 0, 0, 0, 0, 0, 0, 0, 0, 76, 78, 88, 80, 87, 82, 66, 78, 58, 48, 48, 0, 0, 0, 0, 0, 128, 0, 0, 0, 1, 0, 0, 0}, }, }, event: "battery", }, want: false, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parse(tt.args.msgs, tt.args.event) if (err != nil) != tt.wantErr { t.Errorf("parse() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("parse() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package bootloader provides bootloader implementation. package bootloader import ( "fmt" "os" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/dual" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/imageropts" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Bootloader describes a bootloader. type Bootloader interface { GenerateAssets(options options.InstallOptions) ([]partition.Options, error) // Install the bootloader. // // Install mounts the partitions as required. Install(options options.InstallOptions) (*options.InstallResult, error) // Upgrade upgrades the bootloader installation. // // Upgrade mounts the partitions as required. Upgrade(options options.InstallOptions) (*options.InstallResult, error) // Revert reverts the bootloader entry to the previous state. // // Revert mounts the partitions as required. Revert(disk string) error // KexecLoad does a kexec_file_load using the current entry of the bootloader. KexecLoad(r runtime.Runtime, disk string) error } // Probe checks if any supported bootloaders are installed. // // Returns nil if it cannot detect any supported bootloader. func Probe(disk string, options options.ProbeOptions) (Bootloader, error) { options.Logf("probing bootloader on %q", disk) grubBootloader, err := grub.Probe(disk, options) if err != nil { return nil, err } if grubBootloader != nil { options.Logf("found GRUB bootloader on %q", disk) return grubBootloader, nil } sdbootBootloader, err := sdboot.Probe(disk, options) if err != nil { return nil, err } if sdbootBootloader != nil { options.Logf("found sd-boot bootloader on %q", disk) return sdbootBootloader, nil } return nil, os.ErrNotExist } // NewAuto returns a new bootloader based on auto-detection. func NewAuto() Bootloader { if sdboot.IsUEFIBoot() { return sdboot.New() } return grub.NewConfig() } // New returns a new bootloader based on the secureboot flag and architecture. func New(bootloader, talosVersion, arch string) (Bootloader, error) { switch bootloader { case imageropts.BootLoaderKindGrub.String(): g := grub.NewConfig() g.AddResetOption = quirks.New(talosVersion).SupportsResetGRUBOption() return g, nil case imageropts.BootLoaderKindSDBoot.String(): return sdboot.New(), nil case imageropts.BootLoaderKindDualBoot.String(): return dual.New(), nil default: return nil, fmt.Errorf("unsupported bootloader %q", bootloader) } } // CleanupBootloader cleans up the alternate bootloader when booting off via BIOS or UEFI. func CleanupBootloader(disk string, sdboot bool) error { dev, err := block.NewFromPath(disk, block.OpenForWrite()) if err != nil { return err } defer dev.Close() //nolint:errcheck if err := dev.Lock(true); err != nil { return fmt.Errorf("failed to lock device: %w", err) } defer dev.Unlock() //nolint:errcheck gptDev, err := gpt.DeviceFromBlockDevice(dev) if err != nil { return fmt.Errorf("failed to get GPT device: %w", err) } gptTable, err := gpt.Read(gptDev) if err != nil { return fmt.Errorf("failed to read GPT: %w", err) } if sdboot { // we wipe upto 446 bytes where the protective MBR is located if _, err := dev.WipeRange(0, 446); err != nil { return fmt.Errorf("failed to wipe MBR: %w", err) } if err := deletePartitions(gptTable, constants.BIOSGrubPartitionLabel, constants.BootPartitionLabel); err != nil { return err } } else { // means we are using GRUB if err := deletePartitions(gptTable, constants.EFIPartitionLabel); err != nil { return err } } if err := gptTable.Write(); err != nil { return fmt.Errorf("failed to write GPT: %w", err) } return nil } func deletePartitions(gptTable *gpt.Table, labels ...string) error { for i, part := range gptTable.Partitions() { if part == nil { continue } for _, label := range labels { if part.Name == label { if err := gptTable.DeletePartition(i); err != nil { return fmt.Errorf("failed to delete partition %s %d: %w", part.Name, i, err) } } } } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package bootloader_test import ( "errors" randv2 "math/rand/v2" "os" "path/filepath" "sync" "testing" "time" "github.com/freddierice/go-losetup/v2" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) func checkRequirements(t *testing.T) { t.Helper() if os.Geteuid() != 0 { t.Skip("test requires root privileges") } if hostname, _ := os.Hostname(); hostname == "buildkitsandbox" { //nolint: errcheck t.Skip("test not supported under buildkit as partition devices are not propagated from /dev") } } func losetupAttachHelper(t *testing.T, rawImage string, readonly bool) losetup.Device { t.Helper() for range 10 { loDev, err := losetup.Attach(rawImage, 0, readonly) if err != nil { if errors.Is(err, unix.EBUSY) { spraySleep := max(randv2.ExpFloat64(), 2.0) t.Logf("retrying after %v seconds", spraySleep) time.Sleep(time.Duration(spraySleep * float64(time.Second))) continue } } require.NoError(t, err) return loDev } t.Fatal("failed to attach loop device") //nolint:revive panic("unreachable") } func prepareRawImage(t *testing.T, size int64) string { t.Helper() tmpDir := t.TempDir() rawImage := filepath.Join(tmpDir, "image.raw") f, err := os.Create(rawImage) require.NoError(t, err) require.NoError(t, f.Truncate(size)) require.NoError(t, f.Close()) loDev := losetupAttachHelper(t, rawImage, false) t.Cleanup(func() { assert.NoError(t, loDev.Detach()) }) return loDev.Path() } const mib = 1024 * 1024 func TestCleanup(t *testing.T) { checkRequirements(t) disk := prepareRawImage(t, 2*1024*mib) dev, err := block.NewFromPath(disk, block.OpenForWrite()) assert.NoError(t, err) cleanupFunc := sync.OnceValue(dev.Close) t.Cleanup(func() { assert.NoError(t, cleanupFunc()) }) gptDev, err := gpt.DeviceFromBlockDevice(dev) assert.NoError(t, err) pt, err := gpt.New(gptDev, gpt.WithMarkPMBRBootable()) assert.NoError(t, err) quirk := quirks.New("") partitions := []partition.Options{ //nolint:prealloc // this is a test partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.EFIPartitionLabel)), partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.BIOSGrubPartitionLabel)), partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.BootPartitionLabel)), } partitions = append(partitions, partition.NewPartitionOptions(false, quirks.New(""), partition.WithLabel(constants.MetaPartitionLabel))) for _, p := range partitions { size := p.Size if size == 0 { size = pt.LargestContiguousAllocatable() } partitionTyp := uuid.MustParse(p.PartitionType) _, _, err = pt.AllocatePartition(size, p.PartitionLabel, partitionTyp, p.PartitionOpts...) assert.NoError(t, err) } assert.NoError(t, pt.Write()) // close operations on the disk assert.NoError(t, cleanupFunc()) assert.NoError(t, bootloader.CleanupBootloader(disk, false)) testPartitionsWiped(t, disk, []string{constants.BIOSGrubPartitionLabel, constants.BootPartitionLabel, constants.MetaPartitionLabel}, false) assert.NoError(t, bootloader.CleanupBootloader(disk, true)) testPartitionsWiped(t, disk, []string{constants.MetaPartitionLabel}, true) } func testPartitionsWiped(t *testing.T, disk string, expectedLabels []string, sdboot bool) { dev, err := block.NewFromPath(disk) assert.NoError(t, err) t.Cleanup(func() { assert.NoError(t, dev.Close()) }) gptDev, err := gpt.DeviceFromBlockDevice(dev) assert.NoError(t, err) pt, err := gpt.Read(gptDev) assert.NoError(t, err) labels := xslices.Filter(xslices.Map(pt.Partitions(), func(p *gpt.Partition) string { if p == nil { return "" } return p.Name }), func(label string) bool { return label != "" }) assert.Equal(t, expectedLabels, labels) if sdboot { var mbrData [446]byte _, err = dev.File().Read(mbrData[:]) assert.NoError(t, err) assert.Equal(t, [446]byte{}, mbrData) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/dual/dual.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package dual provides dual-boot bootloader implementation. package dual import ( "fmt" "os" "path/filepath" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Config describes a dual-boot bootloader. // this is a dummy implementation of the bootloader interface // allowing to install GRUB for BIOS and sd-boot for UEFI // so we only care about `GenerateAssets()`. type Config struct{} // New creates a new bootloader. func New() *Config { return &Config{} } // GenerateAssets generates the dual-boot bootloader assets and returns the partition options with source directory set. func (c *Config) GenerateAssets(opts options.InstallOptions) ([]partition.Options, error) { if opts.Arch == "arm64" { return nil, fmt.Errorf("dual-boot bootloader is not supported on arm64 architecture, either GRUB or sd-boot must be used") } // here we'll use the grub and sd-boot GenerateAssets logic // and remove the grub `EFI` directory after we're done if _, err := grub.NewConfig().GenerateAssets(opts); err != nil { return nil, fmt.Errorf("failed to install GRUB bootloader: %w", err) } if err := os.RemoveAll(filepath.Join(opts.MountPrefix, constants.EFIMountPoint)); err != nil { return nil, fmt.Errorf("failed to cleanup GRUB EFI assets directory: %w", err) } if _, err := sdboot.New().GenerateAssets(opts); err != nil { return nil, fmt.Errorf("failed to generate sd-boot assets: %w", err) } quirk := quirks.New(opts.Version) partitionOptions := []partition.Options{ partition.NewPartitionOptions( true, quirk, partition.WithLabel(constants.EFIPartitionLabel), partition.WithSourceDirectory(filepath.Join(opts.MountPrefix, "EFI")), ), partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.BIOSGrubPartitionLabel)), partition.NewPartitionOptions( false, quirk, partition.WithLabel(constants.BootPartitionLabel), partition.WithSourceDirectory(filepath.Join(opts.MountPrefix, constants.BootMountPoint)), ), } if opts.ImageMode { partitionOptions = xslices.Map(partitionOptions, func(o partition.Options) partition.Options { o.Reproducible = true return o }) } return partitionOptions, nil } // Install installs the bootloader. func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, error) { return nil, fmt.Errorf("dual-boot bootloader is only supported in image mode, installation is not implemented") } // Upgrade is not implemented since dual-boot is only supported in image mode. func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, error) { return nil, fmt.Errorf("dual-boot bootloader is only supported in image mode, upgrade is not implemented") } // Revert is not implemented. func (c *Config) Revert(disk string) error { return fmt.Errorf("dual-boot bootloader is only supported in image mode, revert is not implemented") } // KexecLoad is not implemented. func (c *Config) KexecLoad(r runtime.Runtime, disk string) error { return fmt.Errorf("dual-boot bootloader is only supported in image mode, kexec load is not implemented") } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/efiutils/efiutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package efiutils provides common bootloader utils. package efiutils import ( "fmt" "path/filepath" ) // Name returns the standard EFI file path for the given architecture. func Name(arch string) (string, error) { basePath := filepath.Join("EFI", "boot") switch arch { case "amd64": return filepath.Join(basePath, "BOOTX64.efi"), nil case "arm64": return filepath.Join(basePath, "BOOTAA64.efi"), nil default: return "", fmt.Errorf("unsupported architecture: %s", arch) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/blocklist.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "encoding/binary" "fmt" "os" "path/filepath" ) // PatchBlocklistsForDiskImage patches the GRUB boot.img and core.img with blocklist information // for GPT+BIOS boot. This should be called after the disk partition layout is finalized. // // References (GRUB source tree inside orb VM): // - grub-core/boot/i386/pc/boot.S: defines GRUB_BOOT_MACHINE_KERNEL_SECTOR and MBR code // - include/grub/i386/pc/boot.h: GRUB_BOOT_MACHINE_KERNEL_SECTOR == 0x5c // - util/setup.c: write_rootdev() patches boot.img fields and writes sector in LE64 // - core image embedded blocklist continuation at core.img offset 0x1F4. func PatchBlocklistsForDiskImage(sectorSize uint, biosBootStartSector uint64, mountPrefix string) error { if sectorSize == 0 { return fmt.Errorf("sector size must be set to patch GRUB blocklists") } // Talos partition layout (GPT): EFI (gpt1, efiPartitionSizeBytes), BIOS (gpt2, 1MiB), BOOT (gpt3) // BIOS boot partition starts immediately after the EFI partition. const ( bootImgKernelSectorOffset = 0x5c // include/grub/i386/pc/boot.h (GRUB_BOOT_MACHINE_KERNEL_SECTOR) bootImgJumpOffset = 0x66 // patched to NOP NOP (0x90 0x90) by grub-install on GPT coreImgBlocklistOffset = 0x1f4 // embedded blocklist continuation inside core.img ) bootImgPath := filepath.Join(mountPrefix, "boot.img") coreImgPath := filepath.Join(mountPrefix, "core.img") bootImg, err := os.ReadFile(bootImgPath) if err != nil { return fmt.Errorf("failed to read boot.img: %w", err) } // validate bootImgKernelSectorOffset and bootImgJumpOffset can be patched into bootImg if len(bootImg) < bootImgKernelSectorOffset+8 { return fmt.Errorf("boot.img is too small (%d bytes) to patch kernel sector offset at 0x%x", len(bootImg), bootImgKernelSectorOffset) } if len(bootImg) < bootImgJumpOffset+2 { return fmt.Errorf("boot.img is too small (%d bytes) to patch jump offset at 0x%x", len(bootImg), bootImgJumpOffset) } // Patch 1: tell boot.img where to find core.img (LE64 sector number at 0x5C) binary.LittleEndian.PutUint64(bootImg[bootImgKernelSectorOffset:], biosBootStartSector) // Patch 2: NOP the short jump at 0x66 for GPT installs (matches grub-install behavior) bootImg[bootImgJumpOffset] = 0x90 bootImg[bootImgJumpOffset+1] = 0x90 if err := os.WriteFile(bootImgPath, bootImg, 0o644); err != nil { return fmt.Errorf("failed to write patched boot.img: %w", err) } coreImg, err := os.ReadFile(coreImgPath) if err != nil { return fmt.Errorf("failed to read core.img: %w", err) } // validate coreImgBlocklistOffset can be patched into coreImg if len(coreImg) < coreImgBlocklistOffset+8 { return fmt.Errorf("core.img is too small (%d bytes) to patch blocklist offset at 0x%x", len(coreImg), coreImgBlocklistOffset) } // Patch 3: core.img embedded blocklist continuation (LE64) points to start+1 // // The boot.img only loads the first sector of core.img, so the embedded blocklist // continuation must point to the second sector of core.img. binary.LittleEndian.PutUint64(coreImg[coreImgBlocklistOffset:], biosBootStartSector+1) if err := os.WriteFile(coreImgPath, coreImg, 0o644); err != nil { return fmt.Errorf("failed to write patched core.img: %w", err) } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/boot_label.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "fmt" "strings" ) // flipBootLabel flips the boot label. func flipBootLabel(e BootLabel) (BootLabel, error) { switch e { case BootA: return BootB, nil case BootB: return BootA, nil case BootReset: fallthrough default: return "", fmt.Errorf("invalid entry: %s", e) } } // Flip flips the default boot label. func (c *Config) flip() error { if _, exists := c.Entries[c.Default]; !exists { return nil } current := c.Default next, err := flipBootLabel(c.Default) if err != nil { return err } c.Default = next c.Fallback = current return nil } // ParseBootLabel parses the given human-readable boot label to a BootLabel. func ParseBootLabel(name string) (BootLabel, error) { switch { case strings.HasPrefix(name, string(BootA)): return BootA, nil case strings.HasPrefix(name, string(BootB)): return BootB, nil case strings.HasPrefix(name, "Reset"): return BootReset, nil default: return "", fmt.Errorf("could not parse boot entry from name: %s", name) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "github.com/siderolabs/talos/pkg/machinery/constants" ) // BootLabel represents a boot label, e.g. A or B. type BootLabel string const ( // ConfigPath is the path to the grub config. ConfigPath = constants.BootMountPoint + "/grub/grub.cfg" // BootA is a bootloader label. BootA BootLabel = "A" // BootB is a bootloader label. BootB BootLabel = "B" // BootReset is a bootloader label. BootReset BootLabel = "Reset" ) const ( bootloaderNotInstalled = "bootloader not installed" ) type bootloaderNotInstalledError struct{} ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/decode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "errors" "fmt" "os" "regexp" ) var ( defaultEntryRegex = regexp.MustCompile(`(?m)^\s*set default="(.*)"\s*$`) fallbackEntryRegex = regexp.MustCompile(`(?m)^\s*set fallback="(.*)"\s*$`) menuEntryRegex = regexp.MustCompile(`(?ms)^menuentry\s+"(.+?)" {(.+?)[^\\]}`) linuxRegex = regexp.MustCompile(`(?m)^\s*linux\s+(.+?)\s+(.*)$`) initrdRegex = regexp.MustCompile(`(?m)^\s*initrd\s+(.+)$`) ) // Read reads the grub configuration from the disk. func Read(path string) (*Config, error) { c, err := os.ReadFile(path) if errors.Is(err, os.ErrNotExist) { return nil, nil } if err != nil { return nil, err } return Decode(c) } // Decode parses the grub configuration from the given bytes. func Decode(c []byte) (*Config, error) { defaultEntryMatches := defaultEntryRegex.FindAllSubmatch(c, -1) if len(defaultEntryMatches) != 1 { return nil, errors.New("failed to find default") } fallbackEntryMatches := fallbackEntryRegex.FindAllSubmatch(c, -1) if len(fallbackEntryMatches) > 1 { return nil, errors.New("found multiple fallback entries") } var fallbackEntry BootLabel if len(fallbackEntryMatches) == 1 { if len(fallbackEntryMatches[0]) != 2 { return nil, errors.New("failed to parse fallback entry") } entry, err := ParseBootLabel(string(fallbackEntryMatches[0][1])) if err != nil { return nil, err } fallbackEntry = entry } if len(defaultEntryMatches[0]) != 2 { return nil, fmt.Errorf("default entry: expected 2 matches, got %d", len(defaultEntryMatches[0])) } defaultEntry, err := ParseBootLabel(string(defaultEntryMatches[0][1])) if err != nil { return nil, err } entries, hasResetOption, err := parseEntries(c) if err != nil { return nil, err } conf := Config{ Default: defaultEntry, Fallback: fallbackEntry, Entries: entries, AddResetOption: hasResetOption, } return &conf, nil } func parseEntries(conf []byte) (map[BootLabel]MenuEntry, bool, error) { entries := make(map[BootLabel]MenuEntry) hasResetOption := false matches := menuEntryRegex.FindAllSubmatch(conf, -1) for _, m := range matches { if len(m) != 3 { return nil, false, fmt.Errorf("conf block: expected 3 matches, got %d", len(m)) } confBlock := m[2] linux, cmdline, initrd, err := parseConfBlock(confBlock) if err != nil { return nil, false, err } name := string(m[1]) bootEntry, err := ParseBootLabel(name) if err != nil { return nil, false, err } if bootEntry == BootReset { hasResetOption = true continue } entries[bootEntry] = MenuEntry{ Name: name, Linux: linux, Cmdline: cmdline, Initrd: initrd, } } return entries, hasResetOption, nil } func parseConfBlock(block []byte) (linux, cmdline, initrd string, err error) { block = []byte(Unquote(string(block))) linuxMatches := linuxRegex.FindAllSubmatch(block, -1) if len(linuxMatches) != 1 { return "", "", "", fmt.Errorf("linux: expected 1 match, got %d", len(linuxMatches)) } if len(linuxMatches[0]) != 3 { return "", "", "", fmt.Errorf("linux: expected 3 matches, got %d", len(linuxMatches[0])) } linux = string(linuxMatches[0][1]) cmdline = string(linuxMatches[0][2]) initrdMatches := initrdRegex.FindAllSubmatch(block, -1) if len(initrdMatches) != 1 { return "", "", "", fmt.Errorf("initrd: expected 1 match, got %d: %s", len(initrdMatches), string(block)) } if len(initrdMatches[0]) != 2 { return "", "", "", fmt.Errorf("initrd: expected 2 matches, got %d", len(initrdMatches[0])) } initrd = string(initrdMatches[0][1]) return linux, cmdline, initrd, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/encode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "bytes" "fmt" "io" "os" "path/filepath" ) // Write the grub configuration to the given file. func (c *Config) Write(path string, printf func(string, ...any)) error { dir := filepath.Dir(path) if err := os.MkdirAll(dir, 0o700); err != nil { return err } wr := new(bytes.Buffer) err := c.Encode(wr) if err != nil { return err } printf("writing %s to disk", path) return os.WriteFile(path, wr.Bytes(), 0o600) } // Encode writes the grub configuration to the given writer. func (c *Config) Encode(wr io.Writer) error { if err := c.validate(); err != nil { return err } fmt.Fprintf(wr, "set default=\"%s\"\n", c.Entries[c.Default].Name) if fallback, ok := c.Entries[c.Fallback]; ok { fmt.Fprintf(wr, "set fallback=\"%s\"\n", fallback.Name) } fmt.Fprint(wr, ` set timeout=3 insmod all_video terminal_input console terminal_output console `) for _, entry := range c.Entries { fmt.Fprintf(wr, `menuentry "%s" { set gfxmode=auto set gfxpayload=text linux %s %s initrd %s } `, entry.Name, entry.Linux, Quote(entry.Cmdline), entry.Initrd) } if c.AddResetOption { defaultEntry := c.Entries[c.Default] fmt.Fprintf(wr, `menuentry "Reset Talos installation and return to maintenance mode" { set gfxmode=auto set gfxpayload=text linux %s %s talos.experimental.wipe=system:EPHEMERAL,STATE initrd %s } `, defaultEntry.Linux, Quote(defaultEntry.Cmdline), defaultEntry.Initrd) } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package grub provides the interface to the GRUB bootloader: config management, installation, etc. package grub import ( "errors" "fmt" "log" "os" "path/filepath" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/kexec" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/version" ) // Config represents a grub configuration file (grub.cfg). type Config struct { Default BootLabel Fallback BootLabel Entries map[BootLabel]MenuEntry AddResetOption bool } // MenuEntry represents a grub menu entry in the grub config file. type MenuEntry struct { Name string Linux string Cmdline string Initrd string } func (e bootloaderNotInstalledError) Error() string { return bootloaderNotInstalled } // NewConfig creates a new grub configuration (nothing is written to disk). func NewConfig() *Config { return &Config{ Default: BootA, Entries: map[BootLabel]MenuEntry{}, AddResetOption: true, } } // KexecLoad does a kexec using the bootloader config. func (c *Config) KexecLoad(r runtime.Runtime, disk string) error { _, err := ProbeWithCallback(disk, options.ProbeOptions{}, func(grubConf *Config) error { defaultEntry, ok := grubConf.Entries[grubConf.Default] if !ok { return nil } kernelPath := filepath.Join(constants.BootMountPoint, defaultEntry.Linux) initrdPath := filepath.Join(constants.BootMountPoint, defaultEntry.Initrd) kernel, err := os.Open(kernelPath) if err != nil { return err } defer kernel.Close() //nolint:errcheck initrd, err := os.Open(initrdPath) if err != nil { return err } defer initrd.Close() //nolint:errcheck cmdline := strings.TrimSpace(defaultEntry.Cmdline) if err = kexec.Load(r, kernel, int(initrd.Fd()), cmdline); err != nil { return err } log.Printf("prepared kexec environment kernel=%q initrd=%q cmdline=%q", kernelPath, initrdPath, cmdline) return nil }) return err } // GenerateAssets generates the bootloader assets and returns partition options to create the bootloader partitions. func (c *Config) GenerateAssets(opts options.InstallOptions) ([]partition.Options, error) { if err := c.generateAssets(opts); err != nil { return nil, err } quirk := quirks.New(opts.Version) efiFormatOptions := []partition.FormatOption{ partition.WithLabel(constants.EFIPartitionLabel), } if opts.ImageMode { // in bios install mode grub generated assets only contains the grub config file and kernel and initramfs // so we don't need to set the source directory for the EFI partition efiFormatOptions = append( efiFormatOptions, partition.WithSourceDirectory(filepath.Join(opts.MountPrefix, "EFI")), ) } partitionOptions := []partition.Options{ partition.NewPartitionOptions( false, quirk, efiFormatOptions..., ), partition.NewPartitionOptions(false, quirk, partition.WithLabel(constants.BIOSGrubPartitionLabel)), partition.NewPartitionOptions( false, quirk, partition.WithLabel(constants.BootPartitionLabel), partition.WithSourceDirectory(filepath.Join(opts.MountPrefix, constants.BootMountPoint)), ), } if opts.ImageMode { partitionOptions = xslices.Map(partitionOptions, func(o partition.Options) partition.Options { o.Reproducible = true return o }) } if opts.ExtraInstallStep != nil { if err := opts.ExtraInstallStep(); err != nil { return nil, err } } return partitionOptions, nil } // Put puts a new menu entry to the grub config (nothing is written to disk). func (c *Config) Put(entry BootLabel, cmdline, version string) error { c.Entries[entry] = buildMenuEntry(entry, cmdline, version) return nil } func (c *Config) validate() error { if _, ok := c.Entries[c.Default]; !ok { return fmt.Errorf("invalid default entry: %s", c.Default) } if c.Fallback != "" { if _, ok := c.Entries[c.Fallback]; !ok { return fmt.Errorf("invalid fallback entry: %s", c.Fallback) } } if c.Default == c.Fallback { return errors.New("default and fallback entries must not be the same") } return nil } func buildMenuEntry(entry BootLabel, cmdline, versionTag string) MenuEntry { return MenuEntry{ Name: fmt.Sprintf("%s - %s %s", entry, version.Name, versionTag), Linux: filepath.Join("/", string(entry), constants.KernelAsset), Cmdline: cmdline, Initrd: filepath.Join("/", string(entry), constants.InitramfsAsset), } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub_test import ( "bufio" "bytes" _ "embed" "fmt" "io" "os" "regexp" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" "github.com/siderolabs/talos/pkg/machinery/version" ) var ( //go:embed testdata/grub_parse_test.cfg grubCfg []byte //go:embed testdata/grub_write_test.cfg newConfig string //go:embed testdata/grub_write_no_reset_test.cfg newNoResetConfig string ) func TestDecode(t *testing.T) { conf, err := grub.Decode(grubCfg) assert.NoError(t, err) assert.Equal(t, grub.BootA, conf.Default) assert.Equal(t, grub.BootB, conf.Fallback) assert.Len(t, conf.Entries, 2) a := conf.Entries[grub.BootA] assert.Equal(t, "A - v1", a.Name) assert.True(t, strings.HasPrefix(a.Linux, "/A/")) assert.True(t, strings.HasPrefix(a.Initrd, "/A/")) assert.Equal(t, "cmdline A", a.Cmdline) b := conf.Entries[grub.BootB] assert.Equal(t, "B - v2", b.Name) assert.Equal(t, "cmdline B", b.Cmdline) assert.True(t, strings.HasPrefix(b.Linux, "/B/")) assert.True(t, strings.HasPrefix(b.Initrd, "/B/")) assert.True(t, conf.AddResetOption) } func TestEncodeDecode(t *testing.T) { config := grub.NewConfig() require.NoError(t, config.Put(grub.BootA, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?hostname=${hostname}&mac=${mac}", "v1.2.3")) require.NoError(t, config.Put(grub.BootB, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?uuid=${uuid}", "v1.3.4")) var b bytes.Buffer require.NoError(t, config.Encode(&b)) t.Logf("config encoded to:\n%s", b.String()) config2, err := grub.Decode(b.Bytes()) require.NoError(t, err) assert.Equal(t, config, config2) } func TestParseBootLabel(t *testing.T) { label, err := grub.ParseBootLabel("A - v1") assert.NoError(t, err) assert.Equal(t, grub.BootA, label) label, err = grub.ParseBootLabel("B - v2") assert.NoError(t, err) assert.Equal(t, grub.BootB, label) label, err = grub.ParseBootLabel("Reset Talos installation and return to maintenance mode\n") assert.NoError(t, err) assert.Equal(t, grub.BootReset, label) _, err = grub.ParseBootLabel("C - v3") assert.Error(t, err) } //nolint:errcheck func TestWrite(t *testing.T) { oldName := version.Name t.Cleanup(func() { version.Name = oldName }) version.Name = "Test" tempFile, _ := os.CreateTemp(t.TempDir(), "talos-test-grub-*.cfg") config := grub.NewConfig() require.NoError(t, config.Put(grub.BootA, "cmdline A", "v0.0.1")) err := config.Write(tempFile.Name(), t.Logf) assert.NoError(t, err) written, _ := os.ReadFile(tempFile.Name()) assert.Equal(t, newConfig, string(written)) } //nolint:errcheck func TestWriteNoReset(t *testing.T) { oldName := version.Name t.Cleanup(func() { version.Name = oldName }) version.Name = "TestOld" tempFile, _ := os.CreateTemp(t.TempDir(), "talos-test-grub-*.cfg") config := grub.NewConfig() config.AddResetOption = false require.NoError(t, config.Put(grub.BootA, "cmdline A", "v0.0.1")) err := config.Write(tempFile.Name(), t.Logf) assert.NoError(t, err) written, _ := os.ReadFile(tempFile.Name()) assert.Equal(t, newNoResetConfig, string(written)) } func TestPut(t *testing.T) { config := grub.NewConfig() require.NoError(t, config.Put(grub.BootA, "cmdline A", "v1.2.3")) err := config.Put(grub.BootB, "cmdline B", "v1.0.0") assert.NoError(t, err) assert.Len(t, config.Entries, 2) assert.Equal(t, "cmdline B", config.Entries[grub.BootB].Cmdline) err = config.Put(grub.BootA, "cmdline A 2", "v1.3.4") assert.NoError(t, err) assert.Equal(t, "cmdline A 2", config.Entries[grub.BootA].Cmdline) } //nolint:errcheck func TestFallback(t *testing.T) { config := grub.NewConfig() require.NoError(t, config.Put(grub.BootA, "cmdline A", "v1.0.0")) _ = config.Put(grub.BootB, "cmdline B", "1.2.0") config.Fallback = grub.BootB var buf bytes.Buffer _ = config.Encode(&buf) result := buf.String() assert.Contains(t, result, `set fallback="B - `) buf.Reset() config.Fallback = "" _ = config.Encode(&buf) result = buf.String() assert.NotContains(t, result, "set fallback") } type bootEntry struct { Linux string Initrd string Cmdline string } // oldParser is the kexec parser used before the GRUB parser was rewritten. // // This makes sure Talos 0.14 can kexec into newly written GRUB config. // //nolint:gocyclo func oldParser(r io.Reader) (*bootEntry, error) { scanner := bufio.NewScanner(r) entry := &bootEntry{} var ( defaultEntry string currentEntry string ) for scanner.Scan() { line := scanner.Text() switch { case strings.HasPrefix(line, "set default"): matches := regexp.MustCompile(`set default="(.*)"`).FindStringSubmatch(line) if len(matches) != 2 { return nil, fmt.Errorf("malformed default entry: %q", line) } defaultEntry = matches[1] case strings.HasPrefix(line, "menuentry"): matches := regexp.MustCompile(`menuentry "(.*)"`).FindStringSubmatch(line) if len(matches) != 2 { return nil, fmt.Errorf("malformed menuentry: %q", line) } currentEntry = matches[1] case strings.HasPrefix(line, " linux "): if currentEntry != defaultEntry { continue } parts := strings.SplitN(line[8:], " ", 2) entry.Linux = parts[0] if len(parts) == 2 { entry.Cmdline = parts[1] } case strings.HasPrefix(line, " initrd "): if currentEntry != defaultEntry { continue } entry.Initrd = line[9:] } } if entry.Linux == "" || entry.Initrd == "" { return nil, scanner.Err() } return entry, scanner.Err() } func TestBackwardsCompat(t *testing.T) { oldName := version.Name t.Cleanup(func() { version.Name = oldName }) version.Name = "Test" var buf bytes.Buffer config := grub.NewConfig() require.NoError(t, config.Put(grub.BootA, "cmdline A", "v0.0.1")) require.NoError(t, config.Put(grub.BootB, "cmdline B", "v0.0.1")) config.Default = grub.BootB err := config.Encode(&buf) assert.NoError(t, err) entry, err := oldParser(&buf) require.NoError(t, err) assert.Equal(t, &bootEntry{ Linux: "/B/vmlinuz", Initrd: "/B/initramfs.xz", Cmdline: "cmdline B", }, entry) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "context" "fmt" "io" "os" "path/filepath" "runtime" "slices" "strings" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-cmd/pkg/cmd" bootloaderutils "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/efiutils" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/internal/pkg/smbios" "github.com/siderolabs/talos/internal/pkg/uki" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( amd64 = "amd64" arm64 = "arm64" ) // Install validates the grub configuration and writes it to the disk. func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, error) { mountSpecs := []mount.Spec{ { PartitionLabel: constants.BootPartitionLabel, FilesystemType: partition.FilesystemTypeXFS, MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint), }, } efiMountSpec := mount.Spec{ PartitionLabel: constants.EFIPartitionLabel, FilesystemType: partition.FilesystemTypeVFAT, MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint), } var efiFound bool // check if the EFI partition is present if err := mount.PartitionOp( opts.BootDisk, []mount.Spec{efiMountSpec}, func() error { return nil }, []blkid.ProbeOption{ blkid.WithSkipLocking(true), }, nil, nil, opts.BlkidInfo, ); err == nil { efiFound = true } if efiFound { mountSpecs = append(mountSpecs, efiMountSpec) } err := mount.PartitionOp( opts.BootDisk, mountSpecs, func() error { if err := c.runGrubInstall(context.Background(), opts, efiFound); err != nil { return err } return nil }, []blkid.ProbeOption{ // installation happens with locked blockdevice blkid.WithSkipLocking(true), }, nil, nil, opts.BlkidInfo, ) return &options.InstallResult{ PreviousLabel: string(c.Fallback), }, err } func (c *Config) generateGrubImage(ctx context.Context, opts options.InstallOptions) error { var copyInstructions []utils.CopyInstruction grubSourceDirectory := "/usr/lib/grub" grubModules := []string{ "part_gpt", "ext2", "fat", "xfs", "normal", "configfile", "linux", "boot", "search", "search_fs_uuid", "search_fs_file", "ls", "cat", "echo", "test", "help", "reboot", "halt", "all_video", } const grubPrefix = "(hd0,gpt3)/grub" // EFI, BIOS, BOOT // in amd64 mode only, install GRUB BIOS mode if opts.Arch == "amd64" { grub32Modules := []string{ "biosdisk", "part_msdos", } args := []string{ //nolint:prealloc // very dynamic length "--format", "i386-pc", "--output", filepath.Join(opts.MountPrefix, "core.img"), "--prefix", grubPrefix, } args = append(args, slices.Concat(grubModules, grub32Modules)...) if _, err := cmd.RunWithOptions( ctx, "grub-mkimage", args, ); err != nil { return fmt.Errorf("failed to generate grub core image: %w", err) } copyInstructions = append(copyInstructions, utils.SourceDestination( filepath.Join(grubSourceDirectory, "i386-pc", "boot.img"), filepath.Join(opts.MountPrefix, "boot.img"), )) } grubEFIPath := filepath.Join(opts.MountPrefix, "grub-efi.img") var platform string // install GRUB in UEFI mode switch opts.Arch { case "amd64": platform = "x86_64-efi" case "arm64": platform = "arm64-efi" default: return fmt.Errorf("unsupported architecture for grub image: %s", opts.Arch) } args := []string{ //nolint:prealloc // very dynamic length "--format", platform, "--output", grubEFIPath, "--prefix", grubPrefix, "--compression", "xz", } args = append(args, grubModules...) if _, err := cmd.RunWithOptions( ctx, "grub-mkimage", args, ); err != nil { return fmt.Errorf("failed to generate grub efi image: %w", err) } efiFile, err := bootloaderutils.Name(opts.Arch) if err != nil { return err } copyInstructions = append(copyInstructions, utils.SourceDestination( grubEFIPath, filepath.Join(opts.MountPrefix, constants.EFIMountPoint, efiFile), )) if err := utils.CopyFiles( opts.Printf, copyInstructions..., ); err != nil { return fmt.Errorf("failed to copy grub generated img files: %w", err) } return nil } //nolint:gocyclo func (c *Config) generateAssets(opts options.InstallOptions) error { cmdline := opts.Cmdline // if we have a kernel path, assume that the kernel and initramfs are available if _, err := os.Stat(opts.BootAssets.KernelPath); err == nil { if err := utils.CopyFiles( opts.Printf, utils.SourceDestination( opts.BootAssets.KernelPath, filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.KernelAsset), ), utils.SourceDestination( opts.BootAssets.InitramfsPath, filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.InitramfsAsset), ), ); err != nil { return err } if opts.GrubUseUKICmdline { return fmt.Errorf("cannot use UKI cmdline when boot assets are not UKI") } } else { // if the kernel path does not exist, assume that the kernel and initramfs are in the UKI assetInfo, err := uki.Extract(opts.BootAssets.UKIPath) if err != nil { return err } defer func() { if assetInfo.Closer != nil { assetInfo.Close() //nolint:errcheck } }() if err := utils.CopyReader( opts.Printf, utils.ReaderDestination( assetInfo.Kernel, filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.KernelAsset), ), utils.ReaderDestination( assetInfo.Initrd, filepath.Join(opts.MountPrefix, constants.BootMountPoint, string(c.Default), constants.InitramfsAsset), ), ); err != nil { return err } if opts.GrubUseUKICmdline { cmdlineBytes, err := io.ReadAll(assetInfo.Cmdline) if err != nil { return fmt.Errorf("failed to read cmdline from UKI: %w", err) } cmdline = string(cmdlineBytes) if extraCmdline, err := smbios.ReadOEMVariable(constants.SDStubCmdlineExtraOEMVar); err == nil { for _, extra := range extraCmdline { cmdline += " " + extra } } } } if err := c.Put(c.Default, cmdline, opts.Version); err != nil { return err } if err := c.Write(filepath.Join(opts.MountPrefix, ConfigPath), opts.Printf); err != nil { return err } if opts.ImageMode { return c.generateGrubImage(context.Background(), opts) } return nil } //nolint:gocyclo func (c *Config) runGrubInstall(ctx context.Context, opts options.InstallOptions, efiMode bool) error { var platforms []string switch opts.Arch { case amd64: if efiMode { platforms = append(platforms, "x86_64-efi") } platforms = append(platforms, "i386-pc") case arm64: platforms = []string{"arm64-efi"} } if runtime.GOARCH == amd64 && opts.Arch == amd64 { // let grub choose the platform automatically if not building an image platforms = []string{""} } for _, platform := range platforms { args := []string{ "--boot-directory=" + filepath.Join(opts.MountPrefix, constants.BootMountPoint), "--removable", } if efiMode { args = append(args, "--efi-directory="+filepath.Join(opts.MountPrefix, constants.EFIMountPoint)) } if opts.ImageMode { args = append(args, "--no-nvram") } if platform != "" { args = append(args, "--target="+platform) } args = append(args, opts.BootDisk) opts.Printf("executing: grub-install %s", strings.Join(args, " ")) if _, err := cmd.RunWithOptions(ctx, "grub-install", args); err != nil { return fmt.Errorf("failed to install grub: %w", err) } } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/probe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package grub provides the interface to the GRUB bootloader: config management, installation, etc. package grub import ( "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ProbeWithCallback probes the GRUB bootloader, and calls the callback function with the Config. func ProbeWithCallback(disk string, options options.ProbeOptions, callback func(*Config) error) (*Config, error) { var grubConf *Config if err := mount.PartitionOp( disk, []mount.Spec{ { PartitionLabel: constants.BootPartitionLabel, FilesystemType: partition.FilesystemTypeXFS, MountTarget: constants.BootMountPoint, }, }, func() error { var err error grubConf, err = Read(ConfigPath) if err != nil { return err } if grubConf != nil && callback != nil { return callback(grubConf) } if grubConf == nil { options.Logf("GRUB: config not found") } return nil }, options.BlockProbeOptions, []mountv3.ManagerOption{ mountv3.WithSkipIfMounted(), mountv3.WithReadOnly(), }, nil, nil, ); err != nil { if xerrors.TagIs[mount.NotFoundTag](err) { // if partitions are not found, it means GRUB is not installed options.Logf("GRUB: BOOT partition not found, skipping probing") return nil, nil } return nil, err } return grubConf, nil } // Probe probes a block device for GRUB bootloader. func Probe(disk string, options options.ProbeOptions) (*Config, error) { return ProbeWithCallback(disk, options, nil) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/quote.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "strings" ) // Quote according to (incomplete) GRUB quoting rules. // // See https://www.gnu.org/software/grub/manual/grub/html_node/Shell_002dlike-scripting.html func Quote(s string) string { for _, c := range `\{}&$|;<>"` { s = strings.ReplaceAll(s, string(c), `\`+string(c)) } return s } // Unquote according to (incomplete) GRUB quoting rules. func Unquote(s string) string { for _, c := range `{}&$|;<>\"` { s = strings.ReplaceAll(s, `\`+string(c), string(c)) } return s } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/quote_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub_test import ( "testing" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" ) //nolint:dupl func TestQuote(t *testing.T) { t.Parallel() for _, test := range []struct { name string input string expected string }{ { name: "empty", input: "", expected: "", }, { name: "no special characters", input: "foo", expected: "foo", }, { name: "backslash", input: `foo\`, expected: `foo\\`, }, { name: "escaped backslash", input: `foo\$`, expected: `foo\\\$`, }, { name: "url", input: "http://my-host/config.yaml?uuid=${uuid}&serial=${serial}&mac=${mac}&hostname=${hostname}", expected: "http://my-host/config.yaml?uuid=\\$\\{uuid\\}\\&serial=\\$\\{serial\\}\\&mac=\\$\\{mac\\}\\&hostname=\\$\\{hostname\\}", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() actual := grub.Quote(test.input) if actual != test.expected { t.Fatalf("expected %q, got %q", test.expected, actual) } }) } } //nolint:dupl func TestUnquote(t *testing.T) { t.Parallel() for _, test := range []struct { name string input string expected string }{ { name: "empty", input: "", expected: "", }, { name: "no special characters", input: "foo", expected: "foo", }, { name: "backslash", input: `foo\\`, expected: `foo\`, }, { name: "escaped backslash", input: `foo\\\$`, expected: `foo\$`, }, { name: "url", input: "http://my-host/config.yaml?uuid=\\$\\{uuid\\}\\&serial=\\$\\{serial\\}\\&mac=\\$\\{mac\\}\\&hostname=\\$\\{hostname\\}", expected: "http://my-host/config.yaml?uuid=${uuid}&serial=${serial}&mac=${mac}&hostname=${hostname}", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() actual := grub.Unquote(test.input) if actual != test.expected { t.Fatalf("expected %q, got %q", test.expected, actual) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package grub provides the interface to the GRUB bootloader: config management, installation, etc. package grub import ( "errors" "fmt" "log" "os" "path/filepath" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Revert reverts the bootloader to the previous version. func (c *Config) Revert(disk string) error { if c == nil { return fmt.Errorf("cannot revert bootloader: %w", bootloaderNotInstalledError{}) } err := mount.PartitionOp( disk, []mount.Spec{ { PartitionLabel: constants.BootPartitionLabel, FilesystemType: partition.FilesystemTypeXFS, MountTarget: constants.BootMountPoint, }, }, c.revert, nil, []mountv3.ManagerOption{ mountv3.WithSkipIfMounted(), }, nil, nil, ) if err != nil && !xerrors.TagIs[mount.NotFoundTag](err) { return err } return nil } func (c *Config) revert() error { if err := c.flip(); err != nil { return err } if _, err := os.Stat(filepath.Join(constants.BootMountPoint, string(c.Default))); errors.Is(err, os.ErrNotExist) { return fmt.Errorf("cannot rollback to %q, label does not exist", "") } if err := c.Write(ConfigPath, log.Printf); err != nil { return fmt.Errorf("failed to revert bootloader: %v", err) } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/testdata/grub_parse_test.cfg ================================================ set default="A - v1" set timeout=3 set fallback="B - v2" insmod all_video terminal_input console terminal_output console menuentry "A - v1" { set gfxmode=auto set gfxpayload=text linux /A/vmlinuz cmdline A initrd /A/initramfs.xz } menuentry "B - v2" { set gfxmode=auto set gfxpayload=text linux /B/vmlinuz cmdline B initrd /B/initramfs.xz } menuentry "Reset Talos installation and return to maintenance mode" { set gfxmode=auto set gfxpayload=text linux /A/vmlinuz cmdline A talos.experimental.wipe=system:EPHEMERAL,STATE initrd /A/initramfs.xz } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/testdata/grub_write_no_reset_test.cfg ================================================ set default="A - TestOld v0.0.1" set timeout=3 insmod all_video terminal_input console terminal_output console menuentry "A - TestOld v0.0.1" { set gfxmode=auto set gfxpayload=text linux /A/vmlinuz cmdline A initrd /A/initramfs.xz } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/testdata/grub_write_test.cfg ================================================ set default="A - Test v0.0.1" set timeout=3 insmod all_video terminal_input console terminal_output console menuentry "A - Test v0.0.1" { set gfxmode=auto set gfxpayload=text linux /A/vmlinuz cmdline A initrd /A/initramfs.xz } menuentry "Reset Talos installation and return to maintenance mode" { set gfxmode=auto set gfxpayload=text linux /A/vmlinuz cmdline A talos.experimental.wipe=system:EPHEMERAL,STATE initrd /A/initramfs.xz } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package grub import ( "context" "path/filepath" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Upgrade copies new boot assets and updates grub configuration on an existing installation. func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, error) { mountSpecs := []mount.Spec{ { PartitionLabel: constants.BootPartitionLabel, FilesystemType: partition.FilesystemTypeXFS, MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint), }, } efiMountSpec := mount.Spec{ PartitionLabel: constants.EFIPartitionLabel, FilesystemType: partition.FilesystemTypeVFAT, MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint), } var efiFound bool // check if the EFI partition is present if err := mount.PartitionOp( opts.BootDisk, []mount.Spec{efiMountSpec}, func() error { return nil }, []blkid.ProbeOption{ blkid.WithSkipLocking(true), }, nil, nil, opts.BlkidInfo, ); err == nil { efiFound = true } if efiFound { mountSpecs = append(mountSpecs, efiMountSpec) } err := mount.PartitionOp( opts.BootDisk, mountSpecs, func() error { if err := c.flip(); err != nil { return err } if err := c.generateAssets(opts); err != nil { return err } if err := c.runGrubInstall(context.Background(), opts, efiFound); err != nil { return err } if opts.ExtraInstallStep != nil { if err := opts.ExtraInstallStep(); err != nil { return err } } return nil }, []blkid.ProbeOption{ // installation happens with locked blockdevice blkid.WithSkipLocking(true), }, nil, nil, opts.BlkidInfo, ) return &options.InstallResult{ PreviousLabel: string(c.Fallback), }, err } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/kexec/kexec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kexec call unix.KexecFileLoad with error handling. package kexec import ( "errors" "fmt" "io" "log" "os" goruntime "runtime" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/zboot" ) // Load handles zboot for arm64 and calls unix.KexecFileLoad with error handling and sets the machine state to kexec prepared. func Load(r runtime.Runtime, kernel *os.File, initrdFD int, cmdline string) error { kernelFD := int(kernel.Fd()) // on arm64 we need to extract the kernel from the zboot image if it's compressed if goruntime.GOARCH == "arm64" { var ( fileCloser io.Closer extractErr error ) kernelFD, fileCloser, extractErr = zboot.Extract(kernel) if extractErr != nil { return fmt.Errorf("failed to extract kernel from zboot: %w", extractErr) } defer func() { if fileCloser != nil { fileCloser.Close() //nolint:errcheck } }() } if err := unix.KexecFileLoad(kernelFD, initrdFD, cmdline, 0); err != nil { switch { case errors.Is(err, unix.ENOSYS): log.Printf("kexec support is disabled in the kernel") return nil case errors.Is(err, unix.EPERM): log.Printf("kexec support is disabled via sysctl") return nil case errors.Is(err, unix.EBUSY): log.Printf("kexec is busy") return nil default: return fmt.Errorf("error loading kernel for kexec: %w", err) } } r.State().Machine().KexecPrepared(true) return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount/mount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package mount provides bootloader mount operations. package mount import ( "fmt" "slices" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-blockdevice/v2/partitioning" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) // Spec specifies what has to be mounted. type Spec struct { PartitionLabel string FilesystemType string MountTarget string } // NotFoundTag is a tag for a partition not found/mismatch errors. type NotFoundTag struct{} // PartitionOp mounts specified partitions with the specified label, executes the operation func, and unmounts the partition(s). func PartitionOp( disk string, specs []Spec, opFunc func() error, probeOptions []blkid.ProbeOption, mountOptions []mount.ManagerOption, filesystemOptions []fsopen.Option, info *blkid.Info, // might be nil ) error { if info == nil { var err error info, err = blkid.ProbePath(disk, probeOptions...) if err != nil { return fmt.Errorf("error probing disk %s: %w", disk, err) } } var managers mount.Managers for _, spec := range specs { var found bool for _, partition := range info.Parts { if pointer.SafeDeref(partition.PartitionLabel) == spec.PartitionLabel { if partition.Name != spec.FilesystemType { return xerrors.NewTaggedf[NotFoundTag]("partition %d with label %s is not of type %s (actual %q)", partition.PartitionIndex, *partition.PartitionLabel, spec.FilesystemType, partition.Name) } manager := mount.NewManager(slices.Concat( []mount.ManagerOption{ mount.WithTarget(spec.MountTarget), mount.WithFsopen( spec.FilesystemType, slices.Concat( []fsopen.Option{ fsopen.WithSource(partitioning.DevName(disk, partition.PartitionIndex)), }, filesystemOptions, )..., ), }, mountOptions, )...) managers = append(managers, manager, ) found = true break } } if !found { return xerrors.NewTaggedf[NotFoundTag]("partition with label %s not found", spec.PartitionLabel) } } unmounter, err := managers.Mount() if err != nil { return fmt.Errorf("error mounting partitions: %w", err) } defer unmounter() //nolint:errcheck return opFunc() } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package options provides bootloader options. package options import ( "fmt" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/talos/pkg/machinery/constants" ) // InstallOptions configures bootloader installation. type InstallOptions struct { // The disk to install to. BootDisk string // Target architecture. Arch string // Kernel command line (grub only, and only if GrubUseUKICmdline is false). Cmdline string // Whether to use the UKI cmdline instead of building it on the host (grub only). GrubUseUKICmdline bool // Talos version. Version string // Are we running in image mode? ImageMode bool // Mount prefix for /boot-like partitions. MountPrefix string // Boot assets to install. BootAssets BootAssets // ExtraInstallStep is a function to run after the bootloader is installed. ExtraInstallStep func() error // Printf-like function to use. Printf func(format string, v ...any) // Optional: blkid probe result. BlkidInfo *blkid.Info } // InstallResult is the result of the installation. type InstallResult struct { // Previous label (if upgrading). PreviousLabel string } // BootAssets describes the assets to be installed by the bootloader. type BootAssets struct { KernelPath string InitramfsPath string UKIPath string SDBootPath string } // FillDefaults fills in default paths to be used when in the context of the installer. func (assets *BootAssets) FillDefaults(arch string) { if assets.KernelPath == "" { assets.KernelPath = fmt.Sprintf(constants.KernelAssetPath, arch) } if assets.InitramfsPath == "" { assets.InitramfsPath = fmt.Sprintf(constants.InitramfsAssetPath, arch) } if assets.UKIPath == "" { assets.UKIPath = fmt.Sprintf(constants.UKIAssetPath, arch) } if assets.SDBootPath == "" { assets.SDBootPath = fmt.Sprintf(constants.SDBootAssetPath, arch) } } // ProbeOptions configures bootloader probing. type ProbeOptions struct { BlockProbeOptions []blkid.ProbeOption Logger func(format string, v ...any) } // Logf logs the message using the provided logger. func (options *ProbeOptions) Logf(format string, v ...any) { if options.Logger == nil { return } options.Logger(format, v...) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/efivars.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sdboot import ( "errors" "fmt" "io/fs" "maps" "math" "os" "slices" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/talos/internal/pkg/efivarfs" "github.com/siderolabs/talos/pkg/machinery/constants" ) // TalosBootEntryDescription is the description of the Talos Linux UKI UEFI boot entry. const TalosBootEntryDescription = "Talos Linux UKI" // SystemdBootStubInfoPath is the path to the SystemdBoot StubInfo EFI variable. var SystemdBootStubInfoPath = constants.EFIVarsMountPoint + "/" + "StubInfo-" + efivarfs.ScopeSystemd.String() // Variable names. const ( LoaderConfigTimeoutName = "LoaderConfigTimeout" LoaderEntryDefaultName = "LoaderEntryDefault" LoaderEntryOneShotName = "LoaderEntryOneShot" LoaderEntryRebootReasonName = "LoaderEntryRebootReason" LoaderEntrySelectedName = "LoaderEntrySelected" StubImageIdentifierName = "StubImageIdentifier" ) // ReadVariable reads a SystemdBoot EFI variable. func ReadVariable(name string) (string, error) { efi, err := efivarfs.NewFilesystemReaderWriter(false) if err != nil { return "", fmt.Errorf("failed to create efivarfs reader/writer: %w", err) } defer efi.Close() //nolint:errcheck data, _, err := efi.Read(efivarfs.ScopeSystemd, name) if err != nil { // if the variable does not exist, return an empty string if errors.Is(err, os.ErrNotExist) { return "", nil } return "", err } out := make([]byte, len(data)) decoder := efivarfs.Encoding.NewDecoder() n, _, err := decoder.Transform(out, data, true) if err != nil { return "", err } if n > 0 && out[n-1] == 0 { n-- } return string(out[:n]), nil } // WriteVariable reads a SystemdBoot EFI variable. func WriteVariable(name, value string) error { efi, err := efivarfs.NewFilesystemReaderWriter(true) if err != nil { return fmt.Errorf("failed to create efivarfs reader/writer: %w", err) } defer efi.Close() //nolint:errcheck out := make([]byte, (len(value)+1)*2) encoder := efivarfs.Encoding.NewEncoder() n, _, err := encoder.Transform(out, []byte(value), true) if err != nil { return err } out = append(out[:n], 0, 0) return efi.Write(efivarfs.ScopeSystemd, name, efivarfs.AttrBootserviceAccess|efivarfs.AttrRuntimeAccess|efivarfs.AttrNonVolatile, out) } // CreateBootEntry creates a UEFI boot entry named "Talos Linux UKI" and sets it as the first in the `BootOrder` // The entry will point to the SystemdBoot PE binary located at the specified install disk path. // //nolint:gocyclo,cyclop func CreateBootEntry(rw efivarfs.ReadWriter, blkidInfo *blkid.Info, printf func(format string, args ...any), sdBootFilePath string) error { efiPartInfo := xslices.Filter(blkidInfo.Parts, func(part blkid.NestedProbeResult) bool { return part.PartitionLabel != nil && *part.PartitionLabel == constants.EFIPartitionLabel }) if len(efiPartInfo) == 0 { return fmt.Errorf("EFI partition not found on install disk %q", blkidInfo.Name) } if len(efiPartInfo) > 1 { return fmt.Errorf("multiple EFI partitions found on install disk %q, expected only one", blkidInfo.Name) } partitionUUID := efiPartInfo[0].PartitionUUID if partitionUUID == nil { return fmt.Errorf("EFI partition UUID not found on install disk %q", blkidInfo.Name) } printf("using disk %s with partition %d and UUID %s", blkidInfo.Name, efiPartInfo[0].PartitionIndex, partitionUUID.String()) bootOrder, err := efivarfs.GetBootOrder(rw) if err != nil { if errors.Is(err, fs.ErrNotExist) { bootOrder = efivarfs.BootOrder{} } else { return fmt.Errorf("failed to get BootOrder: %w", err) } } printf("Current BootOrder: %v", bootOrder) bootEntries, err := efivarfs.ListBootEntries(rw) if err != nil { return fmt.Errorf("failed to list existing Talos boot entries: %w", err) } printf("Existing boot entries: %v", slices.Collect(maps.Keys(bootEntries))) var existingTalosBootEntryIndexes []int // Find all boot entries with the Talos Linux UKI description. for idx, entry := range bootEntries { if entry.Description == TalosBootEntryDescription { existingTalosBootEntryIndexes = append(existingTalosBootEntryIndexes, idx) } } // we sort the indexes to make sure we always keep the lowest index // when removing duplicate Talos Linux UKI boot entries slices.Sort(existingTalosBootEntryIndexes) printf("Found existing Talos Linux UKI boot entries: %v", existingTalosBootEntryIndexes) // Remove any existing Talos Linux UKI boot entries from the BootOrder. // We need to do this since Talos 1.11.x release assumed that the boot order set by the code stays even after a reboot, // but UEFI firmware settings can set a different boot order on boot, which lead to multiple Talos Linux UKI entries in the boot order, // causing some UEFI firmwares to fail to boot at all. // See https://github.com/siderolabs/talos/issues/11829 // find the next minimal available index for the new Talos Linux UKI boot entry nextMinimalIndex := -1 for i := range math.MaxUint16 { if _, ok := bootEntries[i]; !ok { nextMinimalIndex = i break } } if nextMinimalIndex == -1 { return errors.New("all 2^16 boot entry variables are occupied") } // remove all existing Talos Linux UKI boot entries except the first one // and use its index for the new/updated entry for i, idx := range existingTalosBootEntryIndexes { if i == 0 { nextMinimalIndex = idx continue } printf("Removing existing Talos Linux UKI boot entry at index %d", idx) if err := efivarfs.DeleteBootEntry(rw, idx); err != nil { return fmt.Errorf("failed to delete existing Talos boot entry at index %d: %w", idx, err) } } if err := efivarfs.SetBootEntry(rw, nextMinimalIndex, &efivarfs.LoadOption{ Description: TalosBootEntryDescription, FilePath: efivarfs.DevicePath{ &efivarfs.HardDrivePath{ PartitionNumber: uint32(efiPartInfo[0].PartitionIndex), PartitionStartBlock: efiPartInfo[0].PartitionOffset / uint64(blkidInfo.SectorSize), PartitionSizeBlocks: efiPartInfo[0].PartitionSize / uint64(blkidInfo.SectorSize), PartitionMatch: &efivarfs.PartitionGPT{ PartitionUUID: *partitionUUID, }, }, efivarfs.FilePath("/" + sdBootFilePath), }, }); err != nil { return fmt.Errorf("failed to create Talos Linux UKI boot entry at index %d: %w", nextMinimalIndex, err) } printf("created Talos Linux UKI boot entry at index %d", nextMinimalIndex) return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/efivars_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sdboot_test import ( "fmt" "strings" "testing" "github.com/google/uuid" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" "github.com/siderolabs/talos/internal/pkg/efivarfs" "github.com/siderolabs/talos/pkg/machinery/constants" ) type mockLogger struct { strings.Builder } func (m *mockLogger) Printf(format string, v ...any) { m.WriteString(fmt.Sprintf(format, v...) + "\n") } func TestSetBootEntry(t *testing.T) { t.Parallel() loadOption := &efivarfs.LoadOption{ Description: "Default Boot Entry", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/default.efi"), }, } defaultBootEntry, err := loadOption.Marshal() require.NoError(t, err) talosLoadOption := &efivarfs.LoadOption{ Description: sdboot.TalosBootEntryDescription, FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/EFI/TALOS/UKI.efi"), }, } talosBootEntry, err := talosLoadOption.Marshal() require.NoError(t, err) blkidInfo := &blkid.Info{ ProbeResult: blkid.ProbeResult{ Name: "loop0", }, SectorSize: 512, Parts: []blkid.NestedProbeResult{ { NestedResult: blkid.NestedResult{ PartitionUUID: new(uuid.MustParse("3c8f4e2e-1dd2-4a5b-9f6d-8f3c9e6d7c3b")), PartitionLabel: new(constants.EFIPartitionLabel), PartitionOffset: 2048, PartitionSize: 409600, PartitionIndex: 1, PartitionType: new(uuid.MustParse("c12a7328-f81f-11d2-ba4b-00a0c93ec93b")), }, }, }, } for _, testData := range []struct { name string efivarfsMock *efivarfs.Mock expectedEntries map[int]string }{ { name: "empty efivarfs", // both BootOrder and BootEntries are initially empty efivarfsMock: &efivarfs.Mock{}, expectedEntries: map[int]string{ 0: sdboot.TalosBootEntryDescription, }, }, { name: "existing BootEntry but empty BootOrder", // BootOrder is empty but there is already a BootEntry efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, }, }, { name: "existing BootOrder but empty BootEntries", // BootOrder has an entry but there are no BootEntries efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x00, 0x00}, }, }, }, }, expectedEntries: map[int]string{ 0: sdboot.TalosBootEntryDescription, }, }, { name: "existing BootOrder and BootEntries matching", // both BootOrder and BootEntries have an entry and they match efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x00, 0x00}, // BootOrder: [0] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, }, }, { name: "existing BootOrder and BootEntries not matching", // both BootOrder and BootEntries have an entry but they don't match efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00}, // BootOrder: [1] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, }, }, { name: "existing BootOrder and BootEntries not matching multiple", // both BootOrder and BootEntries have an entry but they don't match efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00}, // BootOrder: [1, 0, 3, 2] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, "Boot0002": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, 2: "Default Boot Entry", }, }, { name: "existing BootOrder and BootEntries not matching multiple-1", // both BootOrder and BootEntries have an entry but they don't match efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00}, // BootOrder: [5, 0, 3, 2] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, "Boot0003": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, 3: "Default Boot Entry", }, }, { name: "duplicate entries in BootOrder but not BootEntries", // BootOrder has duplicate entries and no matching BootEntries efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x00}, // BootOrder: [1, 0, 0, 3, 2, 3] }, }, }, }, expectedEntries: map[int]string{ 0: sdboot.TalosBootEntryDescription, }, }, { name: "duplicate Talos entries in BootEntries", // BootOrder has unique entries but there are multiple Talos BootEntries efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00, 0x02, 0x00}, // BootOrder: [1, 2] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, "Boot0001": { Attrs: 0, Data: talosBootEntry, }, "Boot0002": { Attrs: 0, Data: talosBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, }, }, { name: "duplicate Talos entries in BootEntries and duplicate BootOrder", // BootOrder has duplicate entries and there are multiple Talos BootEntries efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00}, // BootOrder: [1, 2, 2, 0] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, "Boot0001": { Attrs: 0, Data: talosBootEntry, }, "Boot0002": { Attrs: 0, Data: talosBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: sdboot.TalosBootEntryDescription, }, }, { name: "duplicate entries in BootOrder and BootEntries", // BootOrder has duplicate entries and has multiple BootEntries efivarfsMock: &efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: []byte{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x03, 0x00}, // BootOrder: [1, 0, 0, 3, 2, 3] }, "Boot0000": { Attrs: 0, Data: defaultBootEntry, }, "Boot0001": { Attrs: 0, Data: defaultBootEntry, }, "Boot0003": { Attrs: 0, Data: defaultBootEntry, }, "Boot002a": { Attrs: 0, Data: defaultBootEntry, }, }, }, }, expectedEntries: map[int]string{ 0: "Default Boot Entry", 1: "Default Boot Entry", 2: sdboot.TalosBootEntryDescription, 3: "Default Boot Entry", 42: "Default Boot Entry", }, }, } { t.Run(testData.name, func(t *testing.T) { t.Parallel() if testData.efivarfsMock == nil { t.Fatal("efivarfsMock must be set") } logger := &mockLogger{} require.NoError(t, sdboot.CreateBootEntry(testData.efivarfsMock, blkidInfo, logger.Printf, "test-entry")) bootEntries, err := efivarfs.ListBootEntries(testData.efivarfsMock) require.NoError(t, err) require.Len(t, bootEntries, len(testData.expectedEntries), "number of boot entries does not match expected value") for idx, desc := range testData.expectedEntries { entry, err := efivarfs.GetBootEntry(testData.efivarfsMock, idx) require.NoError(t, err) require.Equal(t, desc, entry.Description, "boot entry description does not match expected value") } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sdboot // exported for testing only. var ( FindMatchingUKIFile = findMatchingUKIFile GenerateNextUKIName = generateNextUKIName ) ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/loader.conf ================================================ # systemd-boot configuration timeout 10 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package sdboot provides the interface to the Systemd-Boot bootloader: config management, installation, etc. package sdboot import ( _ "embed" "errors" "fmt" "io" "log" "os" "path/filepath" "slices" "strconv" "strings" "github.com/foxboron/go-uefi/efi" "github.com/siderolabs/gen/xerrors" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/blkid" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" bootloaderutils "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/efiutils" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/kexec" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/mount" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/efivarfs" mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/internal/pkg/smbios" "github.com/siderolabs/talos/internal/pkg/uki" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // LoaderConfBytes is the content of the loader.conf file. // //go:embed loader.conf var LoaderConfBytes []byte // Config describe sd-boot state. type Config struct { Default string Fallback string } // IsUEFIBoot returns true if the system is booted using UEFI. func IsUEFIBoot() bool { // https://renenyffenegger.ch/notes/Linux/fhs/sys/firmware/efi/index _, err := os.Stat("/sys/firmware/efi") return err == nil } // IsBootedUsingSDBoot returns true if the system is booted using sd-boot. func IsBootedUsingSDBoot() bool { // https://www.freedesktop.org/software/systemd/man/systemd-stub.html#EFI%20Variables // https://www.freedesktop.org/software/systemd/man/systemd-stub.html#StubInfo _, err := os.Stat(SystemdBootStubInfoPath) return err == nil } // New creates a new sdboot bootloader config. func New() *Config { return &Config{} } // ProbeWithCallback probes the sd-boot bootloader, and calls the callback function with the Config. // this is called when we upgrade, do KexecLoad, or for reverting the bootloader. // //nolint:gocyclo func ProbeWithCallback(disk string, options options.ProbeOptions, callback func(*Config) error) (*Config, error) { // if not UEFI boot, nothing to do if !IsUEFIBoot() { options.Logf("sd-boot: not booted using UEFI, skipping probing") return nil, nil } var sdbootConf *Config // read /boot/EFI and find if sd-boot is already being used // this is to make sure sd-boot from Talos is being used and not sd-boot from another distro if err := mount.PartitionOp( disk, []mount.Spec{ { PartitionLabel: constants.EFIPartitionLabel, FilesystemType: partition.FilesystemTypeVFAT, MountTarget: constants.EFIMountPoint, }, }, func() error { // list existing boot*.efi files in boot folder files, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "boot", "BOOT*.efi")) if err != nil { return err } if len(files) == 0 { return fmt.Errorf("no boot*.efi files found in %s", filepath.Join(constants.EFIMountPoint, "EFI", "boot")) } // list existing UKIs, and check if the current one is present ukiFiles, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) if err != nil { return err } if len(ukiFiles) == 0 { return fmt.Errorf("no UKI files found in %q", filepath.Join(constants.EFIMountPoint, "EFI", "Linux")) } options.Logf("sd-boot: found UKI files: %v", xslices.Map(ukiFiles, filepath.Base)) // If we booted of UKI/Kernel+Initramfs/ISO Talos installer will always be run which // sets the `LoaderEntryDefault` to the UKI file name, so either for reboot with Kexec or upgrade // we will always have the UKI file name in the `LoaderEntryDefault` // and we can use it to determine the default entry. loaderEntryDefault, err := ReadVariable(LoaderEntryDefaultName) if err != nil { return err } options.Logf("sd-boot: LoaderEntryDefault: %s", loaderEntryDefault) // If we booted of a Disk image, only `LoaderEntrySelected` will be set until we do an upgrade // which will set the `LoaderEntryDefault` to the UKI file name. // So for reboot with Kexec we will have to read the `LoaderEntrySelected` // upgrades will always have `LoaderEntryDefault` set to the UKI file name. loaderEntrySelected, err := ReadVariable(LoaderEntrySelectedName) if err != nil { return err } options.Logf("sd-boot: LoaderEntrySelected: %s", loaderEntrySelected) if loaderEntrySelected == "" && loaderEntryDefault == "" { return errors.New("sd-boot: no LoaderEntryDefault or LoaderEntrySelected found, cannot continue") } var ( bootEntry string bootEntryOk bool ) // first try to find the default entry, then the selected one bootEntry, bootEntryOk = findMatchingUKIFile(ukiFiles, loaderEntryDefault) if !bootEntryOk { bootEntry, bootEntryOk = findMatchingUKIFile(ukiFiles, loaderEntrySelected) if !bootEntryOk { return errors.New("sd-boot: no valid boot entry found matching LoaderEntryDefault or LoaderEntrySelected") } } options.Logf("sd-boot: found boot entry: %s", bootEntry) sdbootConf = &Config{ Default: bootEntry, } options.Logf("sd-boot: using %s as default entry", sdbootConf.Default) if callback != nil { return callback(sdbootConf) } return nil }, options.BlockProbeOptions, []mountv3.ManagerOption{ mountv3.WithSkipIfMounted(), mountv3.WithReadOnly(), }, nil, nil, ); err != nil { if xerrors.TagIs[mount.NotFoundTag](err) { return nil, nil } return nil, err } return sdbootConf, nil } // Probe for existing sd-boot bootloader. func Probe(disk string, options options.ProbeOptions) (*Config, error) { return ProbeWithCallback(disk, options, nil) } // KexecLoad does a kexec using the bootloader config. // //nolint:gocyclo func (c *Config) KexecLoad(r runtime.Runtime, disk string) error { _, err := ProbeWithCallback(disk, options.ProbeOptions{}, func(conf *Config) error { var kernelFd int assetInfo, err := uki.Extract(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", conf.Default)) if err != nil { return fmt.Errorf("failed to extract kernel and initrd from uki: %w", err) } defer func() { if assetInfo.Closer != nil { assetInfo.Close() //nolint:errcheck } }() kernelFd, err = unix.MemfdCreate("vmlinux", 0) if err != nil { return fmt.Errorf("memfdCreate: %v", err) } kernelMemfd := os.NewFile(uintptr(kernelFd), "vmlinux") defer kernelMemfd.Close() //nolint:errcheck if _, err := io.Copy(kernelMemfd, assetInfo.Kernel); err != nil { return fmt.Errorf("failed to read kernel from uki: %w", err) } if _, err = kernelMemfd.Seek(0, io.SeekStart); err != nil { return fmt.Errorf("failed to seek kernel: %w", err) } initrdFd, err := unix.MemfdCreate("initrd", 0) if err != nil { return fmt.Errorf("memfdCreate: %v", err) } initrdMemfd := os.NewFile(uintptr(initrdFd), "initrd") defer initrdMemfd.Close() //nolint:errcheck if _, err := io.Copy(initrdMemfd, assetInfo.Initrd); err != nil { return fmt.Errorf("failed to read initrd from uki: %w", err) } if _, err = initrdMemfd.Seek(0, io.SeekStart); err != nil { return fmt.Errorf("failed to seek initrd: %w", err) } var cmdline strings.Builder if _, err := io.Copy(&cmdline, assetInfo.Cmdline); err != nil { return fmt.Errorf("failed to read cmdline from uki: %w", err) } if !efi.GetSecureBoot() { if extraCmdline, err := smbios.ReadOEMVariable(constants.SDStubCmdlineExtraOEMVar); err == nil { for _, s := range extraCmdline { cmdline.WriteString(" " + s) } } } if err := kexec.Load(r, kernelMemfd, initrdFd, cmdline.String()); err != nil { return fmt.Errorf("failed to load kernel for kexec: %w", err) } log.Printf("prepared kexec environment with kernel and initrd extracted from uki, cmdline=%q", cmdline.String()) return nil }) return err } // GenerateAssets generates the sd-boot bootloader assets and returns the partition options with source directory set. func (c *Config) GenerateAssets(opts options.InstallOptions) ([]partition.Options, error) { ukiFileName, err := generateNextUKIName(opts.Version, nil) if err != nil { return nil, err } if err := c.generateAssets(opts, ukiFileName); err != nil { return nil, err } quirk := quirks.New(opts.Version) partitionOptions := []partition.Options{ partition.NewPartitionOptions( true, quirk, partition.WithLabel(constants.EFIPartitionLabel), partition.WithSourceDirectory(filepath.Join(opts.MountPrefix, "EFI")), ), } if opts.ImageMode { partitionOptions = xslices.Map(partitionOptions, func(o partition.Options) partition.Options { o.Reproducible = true return o }) } return partitionOptions, nil } // Install the bootloader. // here we don't need to mount anything since we just need to write the EFI variables // since the partitions are already pre-populated. func (c *Config) Install(opts options.InstallOptions) (*options.InstallResult, error) { ukiFileName, err := generateNextUKIName(opts.Version, nil) if err != nil { return nil, err } return c.setup(opts, ukiFileName) } // Upgrade the bootloader. // On upgrade we mount the EFI partition, cleanup old UKIs, copy the new UKI and sd-boot.efi, and update the EFI variables. func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, error) { var installResult *options.InstallResult err := mount.PartitionOp( opts.BootDisk, []mount.Spec{ { PartitionLabel: constants.EFIPartitionLabel, FilesystemType: partition.FilesystemTypeVFAT, MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint), }, }, func() error { // list existing UKIs, and clean up all but the current one (used to boot) files, err := filepath.Glob(filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) if err != nil { return err } opts.Printf("sd-boot: found existing UKIs during upgrade: %v", xslices.Map(files, filepath.Base)) ukiPath, err := generateNextUKIName(opts.Version, files) if err != nil { return fmt.Errorf("failed to generate next UKI name: %w", err) } for _, file := range files { if strings.EqualFold(filepath.Base(file), c.Default) { if !strings.EqualFold(c.Default, ukiPath) { // set fallback to the current default unless it matches the new install c.Fallback = c.Default } continue } opts.Printf("removing old UKI: %s", file) if err = os.Remove(file); err != nil { return err } } if err := c.generateAssets(opts, ukiPath); err != nil { return err } installResult, err = c.setup(opts, ukiPath) if err != nil { return err } return nil }, []blkid.ProbeOption{ // installation happens with locked blockdevice blkid.WithSkipLocking(true), }, nil, nil, opts.BlkidInfo, ) return installResult, err } // Install the bootloader. // // Assumes that EFI partition is already mounted. // Writes down the UKI and updates the EFI variables. // //nolint:gocyclo,cyclop func (c *Config) setup(opts options.InstallOptions, ukiFileName string) (*options.InstallResult, error) { opts.Printf("updating EFI variables") // set the new entry as a default one if err := WriteVariable(LoaderEntryDefaultName, ukiFileName); err != nil { return nil, err } // set default 5 second boot timeout if err := WriteVariable(LoaderConfigTimeoutName, "5"); err != nil { return nil, err } efiRW, err := efivarfs.NewFilesystemReaderWriter(true) if err != nil { return nil, fmt.Errorf("failed to create efivarfs reader/writer: %w", err) } defer efiRW.Close() //nolint:errcheck blkidInfo, err := blkid.ProbePath(opts.BootDisk, blkid.WithSkipLocking(true)) if err != nil { return nil, fmt.Errorf("failed to probe block device %s: %w", opts.BootDisk, err) } sdbootFilename, err := bootloaderutils.Name(opts.Arch) if err != nil { return nil, fmt.Errorf("failed to get sd-boot file path: %w", err) } if err := CreateBootEntry(efiRW, blkidInfo, opts.Printf, sdbootFilename); err != nil { return nil, fmt.Errorf("failed to create boot entry: %w", err) } if opts.ExtraInstallStep != nil { if err := opts.ExtraInstallStep(); err != nil { return nil, err } } return &options.InstallResult{ PreviousLabel: c.Fallback, }, nil } func (c *Config) generateAssets(opts options.InstallOptions, ukiFileName string) error { if err := os.MkdirAll(filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "loader"), 0o755); err != nil { return err } if err := os.WriteFile(filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "loader", "loader.conf"), LoaderConfBytes, 0o644); err != nil { return err } sdbootFilename, err := bootloaderutils.Name(opts.Arch) if err != nil { return fmt.Errorf("failed to get sd-boot file path: %w", err) } if err := utils.CopyFiles( opts.Printf, utils.SourceDestination( opts.BootAssets.UKIPath, filepath.Join(opts.MountPrefix, constants.EFIMountPoint, "EFI", "Linux", ukiFileName), ), utils.SourceDestination( opts.BootAssets.SDBootPath, filepath.Join(opts.MountPrefix, constants.EFIMountPoint, sdbootFilename), ), ); err != nil { return err } return nil } // generateNextUKIName generates the next UKI name based on the version and existing files. // It checks for existing files and increments the index if necessary. func generateNextUKIName(version string, existingFiles []string) (string, error) { maxIndex := -1 for _, file := range existingFiles { base := strings.TrimSuffix(filepath.Base(file), ".efi") if !strings.HasPrefix(base, "Talos-") { continue } suffix := strings.TrimPrefix(base, "Talos-") parts := strings.SplitN(suffix, "~", 2) if parts[0] != version { continue } if len(parts) == 1 { // Talos-{version}.efi format if maxIndex < 0 { maxIndex = 0 } } else if len(parts) == 2 { // Talos-{version}+{index}.efi format if idx, err := strconv.Atoi(parts[1]); err == nil && idx > maxIndex { maxIndex = idx } } } if maxIndex >= 0 { return fmt.Sprintf("Talos-%s~%d.efi", version, maxIndex+1), nil } return fmt.Sprintf("Talos-%s.efi", version), nil } // Revert the bootloader to the previous version. func (c *Config) Revert(disk string) error { err := mount.PartitionOp( disk, []mount.Spec{ { PartitionLabel: constants.EFIPartitionLabel, FilesystemType: partition.FilesystemTypeVFAT, MountTarget: constants.EFIMountPoint, }, }, c.revert, nil, []mountv3.ManagerOption{ mountv3.WithSkipIfMounted(), }, nil, nil, ) if err != nil && !xerrors.TagIs[mount.NotFoundTag](err) { return err } return nil } func (c *Config) revert() error { files, err := filepath.Glob(filepath.Join(constants.EFIMountPoint, "EFI", "Linux", "Talos-*.efi")) if err != nil { return err } for _, file := range files { if strings.EqualFold(filepath.Base(file), c.Default) { continue } log.Printf("reverting to previous UKI: %s", file) return WriteVariable(LoaderEntryDefaultName, filepath.Base(file)) } return errors.New("previous UKI not found") } func findMatchingUKIFile(ukiFiles []string, entry string) (string, bool) { if slices.ContainsFunc(ukiFiles, func(file string) bool { return strings.EqualFold(filepath.Base(file), entry) }) { return entry, true } return "", false } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package sdboot_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" ) func TestGenerateNextUKIFileName(t *testing.T) { t.Parallel() for _, testData := range []struct { name string version string existingFiles []string expectedFileName string }{ { name: "empty_existing_files", version: "1.10.0", expectedFileName: "Talos-1.10.0.efi", }, { name: "initial_upgrade_to_same_version", version: "1.10.0", existingFiles: []string{"Talos-1.10.0.efi"}, expectedFileName: "Talos-1.10.0~1.efi", }, { name: "second_upgrade_to_same_version", version: "1.10.0", existingFiles: []string{"Talos-1.10.0.efi", "Talos-1.10.0~1.efi"}, expectedFileName: "Talos-1.10.0~2.efi", }, { name: "third_upgrade_to_same_version", version: "1.10.0", existingFiles: []string{"Talos-1.10.0~1.efi", "Talos-1.10.0~2.efi"}, expectedFileName: "Talos-1.10.0~3.efi", }, { name: "upgrade_with_missing_version_in_index", version: "1.10.0", existingFiles: []string{"Talos-1.10.0~1.efi", "Talos-1.10.0~3.efi"}, expectedFileName: "Talos-1.10.0~4.efi", }, { name: "upgrade_with_non-suffixed_file", version: "1.10.0", existingFiles: []string{"Talos-1.10.0.efi", "Talos-1.10.0~2.efi"}, expectedFileName: "Talos-1.10.0~3.efi", }, { name: "direct_upgrade_to_different_version", version: "1.11.0", existingFiles: []string{"Talos-1.10.0.efi"}, expectedFileName: "Talos-1.11.0.efi", }, { name: "direct_upgrade_to_different_version_with_different_files", version: "1.11.0", existingFiles: []string{"Talos-1.10.0.efi", "Talos-1.10.0~1.efi"}, expectedFileName: "Talos-1.11.0.efi", }, { name: "downgrade", version: "1.10.0", existingFiles: []string{"Talos-1.10.0.efi", "Talos-1.11.0.efi"}, expectedFileName: "Talos-1.10.0~1.efi", }, { name: "downgrade_with_suffixed_version", version: "1.10.0", existingFiles: []string{"Talos-1.10.0~1.efi", "Talos-1.11.0.efi"}, expectedFileName: "Talos-1.10.0~2.efi", }, { name: "dirty_version_initial", version: "v1.11.0-alpha.3-40-ge4c24983e-dirty", existingFiles: []string{"Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty.efi"}, expectedFileName: "Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty~1.efi", }, { name: "dirty_suffixed_version", version: "v1.11.0-alpha.3-40-ge4c24983e-dirty", existingFiles: []string{"Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty~1.efi", "Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty.efi"}, expectedFileName: "Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty~2.efi", }, } { t.Run(testData.name, func(t *testing.T) { t.Parallel() ukiPath, err := sdboot.GenerateNextUKIName(testData.version, testData.existingFiles) require.NoError(t, err) require.Equal(t, testData.expectedFileName, ukiPath) }) } } func TestFindMatchingUKIFile(t *testing.T) { t.Parallel() existingFiles := []string{ "/EFI/boot/Linux/Talos-1.10.0.efi", "/EFI/boot/Linux/Talos-1.10.0~1.efi", "/EFI/boot/Linux/talos-1.11.0.efi", "/EFI/boot/Linux/Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty.efi", "/EFI/boot/Linux/Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty~1.efi", } tests := []struct { existingFiles []string entry string expectedFile string expectingFound bool }{ { existingFiles: existingFiles, entry: "Talos-1.10.0.efi", expectedFile: "Talos-1.10.0.efi", expectingFound: true, }, { existingFiles: existingFiles, entry: "Talos-1.11.0.efi", expectedFile: "Talos-1.11.0.efi", expectingFound: true, }, { existingFiles: existingFiles, entry: "Talos-1.12.0.efi", expectedFile: "", expectingFound: false, }, { existingFiles: existingFiles, entry: "Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty.efi", expectedFile: "Talos-v1.11.0-alpha.3-40-ge4c24983e-dirty.efi", expectingFound: true, }, { entry: "Talos-v1.11.0.efi", expectedFile: "", expectingFound: false, }, { entry: "", expectedFile: "", expectingFound: false, }, } for _, test := range tests { foundFile, found := sdboot.FindMatchingUKIFile(test.existingFiles, test.entry) require.Equal(t, test.expectingFound, found) require.Equal(t, test.expectedFile, foundFile) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package v1alpha1 implements a `Runtime`. package v1alpha1 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/akamai/akamai.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package akamai contains the Akamai implementation of the [platform.Platform]. package akamai import ( "context" "fmt" "net/netip" "strconv" "strings" "github.com/cosi-project/runtime/pkg/state" akametadata "github.com/linode/go-metadata" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Akamai is the concrete type that implements the platform.Platform interface. type Akamai struct{} // Name implements the platform.Platform interface. func (a *Akamai) Name() string { return "akamai" } // ParseMetadata converts Akamai platform metadata into platform network config. func (a *Akamai) ParseMetadata(metadata *akametadata.InstanceData, interfaceAddresses *akametadata.NetworkData) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Label != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Label); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } publicIPs := make([]string, 0, len(interfaceAddresses.IPv4.Public)+len(interfaceAddresses.IPv6.Ranges)) // external IP for _, iface := range interfaceAddresses.IPv4.Public { publicIPs = append(publicIPs, iface.Addr().String()) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: iface, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) } for _, iface := range interfaceAddresses.IPv4.Private { networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: iface, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) } for _, iface := range interfaceAddresses.IPv6.Ranges { publicIPs = append(publicIPs, iface.Addr().String()) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: iface, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressManagementTemp), Family: nethelpers.FamilyInet6, }, ) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: interfaceAddresses.IPv6.LinkLocal, Scope: nethelpers.ScopeLink, Family: nethelpers.FamilyInet6, }, ) ipv6gw, err := netip.ParseAddr(strings.Split(interfaceAddresses.IPv6.LinkLocal.String(), ":")[0] + "::1") if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: ipv6gw, OutLinkName: "eth0", Destination: interfaceAddresses.IPv6.LinkLocal, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 1024, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: a.Name(), Hostname: metadata.Label, Region: metadata.Region, InstanceType: metadata.Type, InstanceID: strconv.Itoa(metadata.ID), ProviderID: fmt.Sprintf("linode://%d", metadata.ID), } return networkConfig, nil } // Configuration implements the platform.Platform interface. func (a *Akamai) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } metadataClient, err := akametadata.NewClient(ctx) if err != nil { return nil, fmt.Errorf("new metadata client: %w", err) } userData, err := metadataClient.GetUserData(ctx) if err != nil { return nil, fmt.Errorf("get user data: %w", err) } if userData == "" { return nil, errors.ErrNoConfigSource } return []byte(userData), nil } // Mode implements the platform.Platform interface. func (a *Akamai) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (a *Akamai) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyS0").Append("tty0").Append("tty1"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (a *Akamai) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { metadataClient, err := akametadata.NewClient(ctx) if err != nil { return fmt.Errorf("new metadata client: %w", err) } metadata, err := metadataClient.GetInstance(ctx) if err != nil { return fmt.Errorf("get instance data: %w", err) } metadataNetworkConfig, err := metadataClient.GetNetwork(ctx) if err != nil { return fmt.Errorf("get network data: %w", err) } networkConfig, err := a.ParseMetadata(metadata, metadataNetworkConfig) if err != nil { return fmt.Errorf("parse metadata: %w", err) } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/akamai/akamai_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package akamai_test import ( _ "embed" "encoding/json" "testing" akametadata "github.com/linode/go-metadata" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/akamai" ) //go:embed testdata/instance.json var rawMetadata []byte //go:embed testdata/network.json var rawNetwork []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &akamai.Akamai{} var metadata akametadata.InstanceData var interfaceConfig akametadata.NetworkData require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) require.NoError(t, json.Unmarshal(rawNetwork, &interfaceConfig)) networkConfig, err := p.ParseMetadata(&metadata, &interfaceConfig) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/akamai/testdata/expected.yaml ================================================ addresses: - address: 172.1.2.3/32 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 192.1.2.3/32 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 2600:3c05:d011:797::/64 linkName: eth0 family: inet6 scope: global flags: mngmtmpaddr layer: platform - address: fe80::f03c:93ff:fe6e:5cd9/128 linkName: eth0 family: inet6 scope: link flags: "" layer: platform links: [] routes: - family: inet6 dst: fe80::f03c:93ff:fe6e:5cd9/128 src: "" gateway: fe80::1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 172.1.2.3 - '2600:3c05:d011:797::' metadata: platform: akamai hostname: talos region: us-east instanceType: g6-standard-1 instanceId: "123456" providerId: linode://123456 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/akamai/testdata/instance.json ================================================ { "id": 123456, "label": "talos", "region": "us-east", "type": "g6-standard-1", "specs": { "vcpus": 1, "memory": 2048, "gpus": 0, "transfer": 2000, "disk": 51200 }, "backups": { "enabled": false, "status": null }, "host_uuid": "0c2897331ea446f483f754852b18a67c", "tags": [] } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/akamai/testdata/network.json ================================================ { "interfaces": [], "ipv4": { "public": [ "172.1.2.3/32" ], "private": [ "192.1.2.3/32" ], "shared": [] }, "ipv6": { "slaac": "2600:3c06::f03c:93ff:fe6e:5cd9/128", "ranges": [ "2600:3c05:d011:797::/64" ], "link_local": "fe80::f03c:93ff:fe6e:5cd9/128", "shared_ranges": [] } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package aws contains the AWS implementation of the [platform.Platform]. package aws import ( "context" "fmt" "io" "log" "net/netip" "strings" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // AWS is the concrete type that implements the runtime.Platform interface. type AWS struct { metadataClient *imds.Client } // NewAWS initializes AWS platform building the IMDS client. func NewAWS() (*AWS, error) { a := &AWS{} cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { return nil, fmt.Errorf("error initializing AWS default config: %w", err) } a.metadataClient = imds.NewFromConfig(cfg) return a, nil } // ParseMetadata converts AWS platform metadata into platform network config. func (a *AWS) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{ TimeServers: []network.TimeServerSpecSpec{ { NTPServers: []string{ // See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configure-ec2-ntp.html // // Include both IPv4 & IPv6 addresses for the NTP servers, Talos would lock to one of them (whichever works), // but it would be compatible with v4-only and v6-only deployments. "169.254.169.123", "fd00:ec2::123", }, ConfigLayer: network.ConfigPlatform, }, }, } if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var publicIPs []string if metadata.PublicIPv4 != "" { publicIPs = append(publicIPs, metadata.PublicIPv4) } if metadata.PublicIPv6 != "" { publicIPs = append(publicIPs, metadata.PublicIPv6) } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: a.Name(), Hostname: metadata.Hostname, Region: metadata.Region, Zone: metadata.Zone, InstanceType: metadata.InstanceType, InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("aws:///%s/%s", metadata.Zone, metadata.InstanceID), Spot: metadata.InstanceLifeCycle == "spot", InternalDNS: metadata.InternalDNS, ExternalDNS: metadata.ExternalDNS, Tags: metadata.Tags, } return networkConfig, nil } // Name implements the runtime.Platform interface. func (a *AWS) Name() string { return "aws" } // Configuration implements the runtime.Platform interface. func (a *AWS) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from AWS") userdata, err := netutils.RetryFetch(ctx, a.fetchConfiguration) if err != nil { return nil, err } if strings.TrimSpace(userdata) == "" { return nil, errors.ErrNoConfigSource } return []byte(userdata), nil } func (a *AWS) fetchConfiguration(ctx context.Context) (string, error) { resp, err := a.metadataClient.GetUserData(ctx, &imds.GetUserDataInput{}) if err != nil { if isNotFoundError(err) { return "", errors.ErrNoConfigSource } return "", retry.ExpectedErrorf("failed to fetch EC2 userdata: %w", err) } defer resp.Content.Close() //nolint:errcheck userdata, err := io.ReadAll(resp.Content) return string(userdata), err } // Mode implements the runtime.Platform interface. func (a *AWS) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (a *AWS) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (a *AWS) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching aws instance config") metadata, err := a.getMetadata(ctx) if err != nil { return err } networkConfig, err := a.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/aws/aws_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package aws_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/aws" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestEmpty(t *testing.T) { p := &aws.AWS{} var metadata aws.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) networkConfig, err := p.ParseMetadata(&metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/aws/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package aws import ( "context" "errors" "fmt" "io" "net/http" "strings" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" smithyhttp "github.com/aws/smithy-go/transport/http" ) // MetadataConfig represents a metadata AWS instance. type MetadataConfig struct { Hostname string `json:"hostname,omitempty"` InstanceID string `json:"instance-id,omitempty"` InstanceType string `json:"instance-type,omitempty"` InstanceLifeCycle string `json:"instance-life-cycle,omitempty"` PublicIPv4 string `json:"public-ipv4,omitempty"` PublicIPv6 string `json:"ipv6,omitempty"` InternalDNS string `json:"local-hostname,omitempty"` ExternalDNS string `json:"public-hostname,omitempty"` Region string `json:"region,omitempty"` Zone string `json:"zone,omitempty"` Tags map[string]string `json:"tags,omitempty"` } //nolint:gocyclo func (a *AWS) getMetadata(ctx context.Context) (*MetadataConfig, error) { // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html getMetadataKey := func(key string) (string, error) { resp, err := a.metadataClient.GetMetadata(ctx, &imds.GetMetadataInput{ Path: key, }) if err != nil { if isNotFoundError(err) { return "", nil } return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, err) } defer resp.Content.Close() //nolint:errcheck v, err := io.ReadAll(resp.Content) return string(v), err } var ( metadata MetadataConfig err error ) if metadata.Hostname, err = getMetadataKey("hostname"); err != nil { return nil, err } if metadata.InstanceType, err = getMetadataKey("instance-type"); err != nil { return nil, err } if metadata.InstanceLifeCycle, err = getMetadataKey("instance-life-cycle"); err != nil { return nil, err } if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil { return nil, err } if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil { return nil, err } if metadata.PublicIPv6, err = getMetadataKey("ipv6"); err != nil { return nil, err } if metadata.InternalDNS, err = getMetadataKey("local-hostname"); err != nil { return nil, err } if metadata.ExternalDNS, err = getMetadataKey("public-hostname"); err != nil { return nil, err } if metadata.Region, err = getMetadataKey("placement/region"); err != nil { return nil, err } if metadata.Zone, err = getMetadataKey("placement/availability-zone"); err != nil { return nil, err } if tags, err := getMetadataKey("tags/instance"); err == nil { metadata.Tags = make(map[string]string) for key := range strings.FieldsSeq(tags) { if value, err := getMetadataKey("tags/instance/" + key); err == nil { metadata.Tags[key] = value } } } return &metadata, nil } func isNotFoundError(err error) bool { var awsErr *smithyhttp.ResponseError if errors.As(err, &awsErr) { return awsErr.HTTPStatusCode() == http.StatusNotFound } return false } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/expected.yaml ================================================ addresses: [] links: [] routes: [] hostnames: - hostname: talos domainname: "" layer: platform resolvers: [] timeServers: - timeServers: - 169.254.169.123 - fd00:ec2::123 layer: platform operators: [] externalIPs: - 1.2.3.4 metadata: platform: aws hostname: talos region: us-east-1 zone: us-east-1a instanceId: i-0a0a0a0a0a0a0a0a0 providerId: aws:///us-east-1a/i-0a0a0a0a0a0a0a0a0 tags: cluster: mycluster ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/aws/testdata/metadata.json ================================================ { "hostname": "talos", "instance-id": "i-0a0a0a0a0a0a0a0a0", "public-ipv4": "1.2.3.4", "region": "us-east-1", "zone": "us-east-1a", "tags": {"cluster": "mycluster"} } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package azure contains the Azure implementation of the [platform.Platform]. package azure import ( "context" "encoding/base64" "encoding/json" "encoding/xml" stderrors "errors" "fmt" "io/fs" "log" "net/netip" "os" "path/filepath" "regexp" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NetworkConfig holds network interface meta config. type NetworkConfig struct { IPv4 struct { IPAddresses []IPAddresses `json:"ipAddress"` } `json:"ipv4"` IPv6 struct { IPAddresses []IPAddresses `json:"ipAddress"` } `json:"ipv6"` } // IPAddresses holds public/private IPs. type IPAddresses struct { PrivateIPAddress string `json:"privateIpAddress"` PublicIPAddress string `json:"publicIpAddress"` } // LoadBalancerMetadata represents load balancer metadata in IMDS. type LoadBalancerMetadata struct { LoadBalancer struct { PublicIPAddresses []struct { FrontendIPAddress string `json:"frontendIpAddress,omitempty"` PrivateIPAddress string `json:"privateIpAddress,omitempty"` } `json:"publicIpAddresses,omitempty"` } `json:"loadbalancer"` } // Azure is the concrete type that implements the platform.Platform interface. type Azure struct{} // ovfXML is a simple struct to help us fish custom data out from the ovf-env.xml file. type ovfXML struct { XMLName xml.Name `xml:"Environment"` CustomData string `xml:"ProvisioningSection>LinuxProvisioningConfigurationSet>CustomData"` } // Name implements the platform.Platform interface. func (a *Azure) Name() string { return "azure" } // ParseMetadata parses Azure network metadata into the platform network config. // //nolint:gocyclo func (a *Azure) ParseMetadata(metadata *ComputeMetadata, interfaceAddresses []NetworkConfig, host []byte) (*runtime.PlatformNetworkConfig, error) { var networkConfig runtime.PlatformNetworkConfig // hostname if len(host) > 0 { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(string(host)); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var publicIPs []string // external IP for _, iface := range interfaceAddresses { for _, ipv4addr := range iface.IPv4.IPAddresses { publicIPs = append(publicIPs, ipv4addr.PublicIPAddress) } for _, ipv6addr := range iface.IPv6.IPAddresses { publicIPs = append(publicIPs, ipv6addr.PublicIPAddress) } } // DHCP6 for enabled interfaces for idx, iface := range interfaceAddresses { ipv6 := false for _, ipv6addr := range iface.IPv6.IPAddresses { ipv6 = ipv6addr.PublicIPAddress != "" || ipv6addr.PrivateIPAddress != "" } if ipv6 { ifname := fmt.Sprintf("eth%d", idx) networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: ifname, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 2 * network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) // If accept_ra is not set, use the default gateway. route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: netip.MustParseAddr("fe80::1234:5678:9abc"), OutLinkName: ifname, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 4 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } zone := metadata.FaultDomain if metadata.Zone != "" { zone = fmt.Sprintf("%s-%s", metadata.Location, metadata.Zone) } providerID, err := convertResourceGroupNameToLower(metadata.ResourceID) if err != nil { return nil, err } networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, ConfigLayer: network.ConfigPlatform, }) networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: "eth0", Up: true, MTU: 1400, ConfigLayer: network.ConfigPlatform, }, ) networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: a.Name(), Hostname: metadata.OSProfile.ComputerName, Region: strings.ToLower(metadata.Location), Zone: strings.ToLower(zone), InstanceType: metadata.VMSize, InstanceID: metadata.ResourceID, ProviderID: fmt.Sprintf("azure://%s", providerID), Spot: metadata.EvictionPolicy != "", } return &networkConfig, nil } // ParseLoadBalancerIP parses Azure LoadBalancer metadata into the platform external ip list. func (a *Azure) ParseLoadBalancerIP(lbConfig LoadBalancerMetadata, exIP []netip.Addr) ([]netip.Addr, error) { lbAddresses := exIP for _, addr := range lbConfig.LoadBalancer.PublicIPAddresses { ipaddr := addr.FrontendIPAddress if i := strings.IndexByte(ipaddr, ']'); i != -1 { ipaddr = strings.TrimPrefix(ipaddr[:i], "[") } if ip, err := netip.ParseAddr(ipaddr); err == nil { lbAddresses = append(lbAddresses, ip) } } return lbAddresses, nil } // Configuration implements the platform.Platform interface. func (a *Azure) Configuration(ctx context.Context, r state.State) ([]byte, error) { defer func() { if err := netutils.Wait(ctx, r); err != nil { log.Printf("failed to wait for network, err: %s", err) } if err := linuxAgent(ctx); err != nil { log.Printf("failed to update instance status, err: %s", err) } }() log.Printf("fetching machine config from ovf-env.xml") // Custom data is not available in IMDS, so trying to find it on CDROM. return a.configFromCD() } // Mode implements the platform.Platform interface. func (a *Azure) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (a *Azure) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyS0,115200n8"), procfs.NewParameter("earlyprintk").Append("ttyS0,115200"), procfs.NewParameter("rootdelay").Append("300"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"), // disable 'kexec' as Azure VMs sometimes are stuck on kexec, and normal soft reboot // doesn't take much longer on VMs procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"), } } // configFromCD handles looking for devices and trying to mount/fetch xml to get the custom data. // //nolint:gocyclo func (a *Azure) configFromCD() ([]byte, error) { devList, err := os.ReadDir("/dev") if err != nil { return nil, err } diskRegex := regexp.MustCompile("(sr[0-9]|hd[c-z]|cdrom[0-9]|cd[0-9])") for _, dev := range devList { if diskRegex.MatchString(dev.Name()) { fmt.Printf("found matching device. checking for ovf-env.xml: %s\n", dev.Name()) // Mount and slurp xml from disk if err = unix.Mount(filepath.Join("/dev", dev.Name()), mnt, "udf", unix.MS_RDONLY, ""); err != nil { fmt.Printf("unable to mount %s, possibly not udf: %s", dev.Name(), err.Error()) continue } ovfEnvFile, err := os.ReadFile(filepath.Join(mnt, "ovf-env.xml")) if err != nil { // Device mount worked, but it wasn't the "CD" that contains the xml file if stderrors.Is(err, fs.ErrNotExist) { continue } return nil, fmt.Errorf("failed to read config: %w", err) } if err = unix.Unmount(mnt, 0); err != nil { return nil, fmt.Errorf("failed to unmount: %w", err) } // Unmarshall xml we slurped ovfEnvData := ovfXML{} err = xml.Unmarshal(ovfEnvFile, &ovfEnvData) if err != nil { return nil, err } if len(ovfEnvData.CustomData) > 0 { b64CustomData, err := base64.StdEncoding.DecodeString(ovfEnvData.CustomData) if err != nil { return nil, err } return b64CustomData, nil } return nil, errors.ErrNoConfigSource } } return nil, errors.ErrNoConfigSource } // NetworkConfiguration implements the runtime.Platform interface. // //nolint:gocyclo func (a *Azure) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { metadata, apiVersion, err := a.getMetadata(ctx) if err != nil { return err } interfacesEndpoint := fmt.Sprintf(AzureInterfacesEndpoint, apiVersion) log.Printf("fetching network config from %q", interfacesEndpoint) metadataNetworkConfig, err := download.Download(ctx, interfacesEndpoint, download.WithHeaders(map[string]string{"Metadata": "true"})) if err != nil { return fmt.Errorf("failed to fetch network config from metadata service: %w", err) } var interfaceAddresses []NetworkConfig if err = json.Unmarshal(metadataNetworkConfig, &interfaceAddresses); err != nil { return err } networkConfig, err := a.ParseMetadata(metadata, interfaceAddresses, []byte(metadata.OSProfile.ComputerName)) if err != nil { return fmt.Errorf("failed to parse network metadata: %w", err) } loadbalancerEndpoint := fmt.Sprintf(AzureLoadbalancerEndpoint, apiVersion) log.Printf("fetching load balancer metadata from: %q", loadbalancerEndpoint) var loadBalancerAddresses LoadBalancerMetadata lbConfig, err := download.Download(ctx, loadbalancerEndpoint, download.WithHeaders(map[string]string{"Metadata": "true"}), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if err != nil && !stderrors.Is(err, errors.ErrNoConfigSource) { log.Printf("failed to fetch load balancer config from metadata service: %s", err) lbConfig = nil } if len(lbConfig) > 0 { if err = json.Unmarshal(lbConfig, &loadBalancerAddresses); err != nil { return fmt.Errorf("failed to parse loadbalancer metadata: %w", err) } networkConfig.ExternalIPs, err = a.ParseLoadBalancerIP(loadBalancerAddresses, networkConfig.ExternalIPs) if err != nil { return fmt.Errorf("failed to define externalIPs: %w", err) } } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } // convertResourceGroupNameToLower converts the resource group name in the resource ID to be lowered. // https://github.com/kubernetes-sigs/cloud-provider-azure/blob/4192b264611aebef8070505dd56680a862acfbbf/pkg/provider/azure_wrap.go#L91 func convertResourceGroupNameToLower(resourceID string) (string, error) { // https://github.com/kubernetes-sigs/cloud-provider-azure/blob/4192b264611aebef8070505dd56680a862acfbbf/pkg/provider/azure_wrap.go#L37 azureResourceGroupNameRE := regexp.MustCompile(`.*/subscriptions/(?:.*)/resourceGroups/(.+)/providers/(?:.*)`) matches := azureResourceGroupNameRE.FindStringSubmatch(resourceID) if len(matches) != 2 { return "", fmt.Errorf("%q isn't in Azure resource ID format %q", resourceID, azureResourceGroupNameRE.String()) } resourceGroup := matches[1] return strings.Replace(resourceID, resourceGroup, strings.ToLower(resourceGroup), 1), nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/azure_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/azure" ) //go:embed testdata/interfaces.json var rawInterfaces []byte //go:embed testdata/compute.json var rawCompute []byte //go:embed testdata/loadbalancer.json var rawLoadBalancerMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { a := &azure.Azure{} var interfacesMetadata []azure.NetworkConfig require.NoError(t, json.Unmarshal(rawInterfaces, &interfacesMetadata)) var computeMetadata azure.ComputeMetadata require.NoError(t, json.Unmarshal(rawCompute, &computeMetadata)) networkConfig, err := a.ParseMetadata(&computeMetadata, interfacesMetadata, []byte("some.fqdn")) require.NoError(t, err) var lb azure.LoadBalancerMetadata require.NoError(t, json.Unmarshal(rawLoadBalancerMetadata, &lb)) networkConfig.ExternalIPs, err = a.ParseLoadBalancerIP(lb, networkConfig.ExternalIPs) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure import ( "context" "encoding/json" stderrors "errors" "fmt" "log" "github.com/siderolabs/talos/pkg/download" ) const ( // AzureMetadata documentation // ref: https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service // ref: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/imds/data-plane/Microsoft.InstanceMetadataService/stable/2023-07-01/examples/GetInstanceMetadata.json // AzureVersion is the version of the Azure metadata service. AzureVersion = "2021-12-13" // AzureVersionFallback is the fallback version of the Azure metadata service (e.g. Azure Stack Hub). AzureVersionFallback = "2019-06-01" // AzureInternalEndpoint is the Azure Internal Channel IP // https://blogs.msdn.microsoft.com/mast/2015/05/18/what-is-the-ip-address-168-63-129-16/ AzureInternalEndpoint = "http://168.63.129.16" // AzureMetadataEndpoint is the local endpoint for the metadata. AzureMetadataEndpoint = "http://169.254.169.254/metadata/instance/compute?api-version=%s&format=json" // AzureInterfacesEndpoint is the local endpoint to get external IPs. AzureInterfacesEndpoint = "http://169.254.169.254/metadata/instance/network/interface?api-version=%s&format=json" // AzureLoadbalancerEndpoint is the local endpoint for load balancer config. AzureLoadbalancerEndpoint = "http://169.254.169.254/metadata/loadbalancer?api-version=%s&format=json" mnt = "/mnt" ) // ComputeMetadata represents metadata compute information. type ComputeMetadata struct { Environment string `json:"azEnvironment,omitempty"` SKU string `json:"sku,omitempty"` Name string `json:"name,omitempty"` Zone string `json:"zone,omitempty"` VMSize string `json:"vmSize,omitempty"` OSType string `json:"osType,omitempty"` OSProfile struct { ComputerName string `json:"computerName,omitempty"` } `json:"osProfile"` Location string `json:"location,omitempty"` FaultDomain string `json:"platformFaultDomain,omitempty"` PlatformSubFaultDomain string `json:"platformSubFaultDomain,omitempty"` UpdateDomain string `json:"platformUpdateDomain,omitempty"` ResourceGroup string `json:"resourceGroupName,omitempty"` ResourceID string `json:"resourceId,omitempty"` VMScaleSetName string `json:"vmScaleSetName,omitempty"` SubscriptionID string `json:"subscriptionId,omitempty"` EvictionPolicy string `json:"evictionPolicy,omitempty"` } func (a *Azure) getMetadata(ctx context.Context) (*ComputeMetadata, string, error) { apiVersion := AzureVersion errBadRequest := stderrors.New("bad request") metadataEndpoint := fmt.Sprintf(AzureMetadataEndpoint, apiVersion) log.Printf("fetching azure instance config from: %q", metadataEndpoint) metadataDl, err := download.Download(ctx, metadataEndpoint, download.WithHeaders(map[string]string{"Metadata": "true"}), download.WithErrorOnBadRequest(errBadRequest), ) if err != nil && stderrors.Is(err, errBadRequest) { apiVersion = AzureVersionFallback metadataEndpoint = fmt.Sprintf(AzureMetadataEndpoint, apiVersion) log.Printf("fetching azure instance config from: %q", metadataEndpoint) metadataDl, err = download.Download(ctx, metadataEndpoint, download.WithHeaders(map[string]string{"Metadata": "true"}), ) } if err != nil { return nil, "", fmt.Errorf("error fetching metadata: %w", err) } var metadata ComputeMetadata if err = json.Unmarshal(metadataDl, &metadata); err != nil { return nil, "", fmt.Errorf("failed to parse compute metadata: %w", err) } return &metadata, apiVersion, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/register.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure import ( "bytes" "context" "encoding/xml" "fmt" "io" "net/http" "net/url" "github.com/siderolabs/talos/pkg/download" ) // This should provide the bare minimum to trigger a node in ready condition to allow // azure to be happy with the node and let it on it's lawn. func linuxAgent(ctx context.Context) (err error) { var gs *GoalState gs, err = goalState(ctx) if err != nil { return fmt.Errorf("failed to register with Azure and fetch GoalState XML: %w", err) } return reportHealth(ctx, gs.Incarnation, gs.Container.ContainerID, gs.Container.RoleInstanceList.RoleInstance.InstanceID) } func goalState(ctx context.Context) (gs *GoalState, err error) { body, err := download.Download(ctx, AzureInternalEndpoint+"/machine/?comp=goalstate", download.WithHeaders(map[string]string{ "x-ms-agent-name": "WALinuxAgent", "x-ms-version": "2015-04-05", "Content-Type": "text/xml;charset=utf-8", })) if err != nil { return nil, err } gs = &GoalState{} err = xml.Unmarshal(body, gs) return gs, err } func reportHealth(ctx context.Context, gsIncarnation, gsContainerID, gsInstanceID string) (err error) { // Construct health response h := &Health{ Xsi: "http://www.w3.org/2001/XMLSchema-instance", Xsd: "http://www.w3.org/2001/XMLSchema", WAAgent: WAAgent{ GoalStateIncarnation: gsIncarnation, Container: &Container{ ContainerID: gsContainerID, RoleInstanceList: &RoleInstanceList{ Role: &RoleInstance{ InstanceID: gsInstanceID, Health: &HealthStatus{ State: "Ready", }, }, }, }, }, } // Encode health response as xml b := new(bytes.Buffer) b.WriteString(xml.Header) err = xml.NewEncoder(b).Encode(h) if err != nil { return err } var u *url.URL u, err = url.Parse(AzureInternalEndpoint + "/machine/?comp=health") if err != nil { return nil } var ( req *http.Request resp *http.Response ) req, err = http.NewRequestWithContext(ctx, http.MethodPost, u.String(), b) if err != nil { return err } addHeaders(req) client := &http.Client{} resp, err = client.Do(req) if err != nil { return err } // TODO probably should do some better check here ( verify status code ) //nolint:errcheck defer resp.Body.Close() _, err = io.ReadAll(resp.Body) if err != nil { return err } return err } func addHeaders(req *http.Request) { req.Header.Add("X-Ms-Agent-Name", "WALinuxAgent") req.Header.Add("X-Ms-Version", "2015-04-05") req.Header.Add("Content-Type", "text/xml;charset=utf-8") } // GoalState is the response from the Azure platform when a machine // starts up. Ref: // https://github.com/Azure/WALinuxAgent/blob/b26feb7822f7d4a19507b6762fe1bd280c2ba2de/bin/waagent2.0#L4331 // https://github.com/Azure/WALinuxAgent/blob/3be3e1fbf2330303f76961b87d891672e847ce4e/azurelinuxagent/common/protocol/wire.py#L216 type GoalState struct { XMLName xml.Name `xml:"GoalState"` Xsi string `xml:"xsi,attr"` Xsd string `xml:"xsd,attr"` WAAgent //nolint:embeddedstructfieldcheck } // Health is the response from the local machine to Azure to denote current // machine state. type Health struct { XMLName xml.Name `xml:"Health"` Xsi string `xml:"xmlns:xsi,attr"` Xsd string `xml:"xmlns:xsd,attr"` WAAgent //nolint:embeddedstructfieldcheck } // WAAgent contains the meat of the data format that is passed between the // Azure platform and the machine. // Mostly, we just care about the Incarnation and Container fields here. type WAAgent struct { Text string `xml:",chardata"` Version string `xml:"Version,omitempty"` Incarnation string `xml:"Incarnation,omitempty"` GoalStateIncarnation string `xml:"GoalStateIncarnation,omitempty"` Machine *Machine `xml:"Machine,omitempty"` Container *Container `xml:"Container,omitempty"` } // Container holds the interesting details about a provisioned machine. type Container struct { Text string `xml:",chardata"` ContainerID string `xml:"ContainerId"` RoleInstanceList *RoleInstanceList `xml:"RoleInstanceList"` } // RoleInstanceList is a list but only has a single item which is cool I guess. type RoleInstanceList struct { Text string `xml:",chardata"` RoleInstance *RoleInstance `xml:"RoleInstance,omitempty"` Role *RoleInstance `xml:"Role,omitempty"` } // RoleInstance contains the specifics for the provisioned VM. type RoleInstance struct { Text string `xml:",chardata"` InstanceID string `xml:"InstanceId"` State string `xml:"State,omitempty"` Configuration *Configuration `xml:"Configuration,omitempty"` Health *HealthStatus `xml:"Health,omitempty"` } // Configuration seems important but isnt really used right now. We could // very well not include it because we have no use for it right now, but // since we want completeness, we're going to include it. type Configuration struct { Text string `xml:",chardata"` HostingEnvironmentConfig string `xml:"HostingEnvironmentConfig"` SharedConfig string `xml:"SharedConfig"` ExtensionsConfig string `xml:"ExtensionsConfig"` FullConfig string `xml:"FullConfig"` Certificates string `xml:"Certificates"` ConfigName string `xml:"ConfigName"` } // Machine holds no useful information for us. type Machine struct { Text string `xml:",chardata"` ExpectedState string `xml:"ExpectedState"` StopRolesDeadlineHint string `xml:"StopRolesDeadlineHint"` LBProbePorts *struct { Text string `xml:",chardata"` Port string `xml:"Port"` } `xml:"LBProbePorts,omitempty"` ExpectHealthReport string `xml:"ExpectHealthReport"` } // HealthStatus provides mechanism to trigger Azure to understand that our // machine has transitioned to a 'Ready' state and is good to go. // We can fill out details if we want to be more verbose... type HealthStatus struct { Text string `xml:",chardata"` State string `xml:"State"` Details *struct { Text string `xml:",chardata"` SubStatus string `xml:"SubStatus"` Description string `xml:"Description"` } `xml:"Details,omitempty"` } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/compute.json ================================================ { "location": "CentralUS", "name": "IMDSCanary", "offer": "RHEL", "osProfile": { "computerName": "examplevmname" }, "osType": "Linux", "platformFaultDomain": "0", "platformUpdateDomain": "0", "publisher": "RedHat", "resourceId": "/subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/Test/providers/Microsoft.Compute/virtualMachines/examplevmname", "sku": "7.2", "version": "7.2.20161026", "vmId": "5c08b38e-4d57-4c23-ac45-aca61037f084", "vmSize": "Standard_DS2" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/expected.yaml ================================================ addresses: [] links: - name: eth0 logical: false up: true mtu: 1400 kind: "" type: netrom layer: platform routes: - family: inet6 dst: "" src: "" gateway: fe80::1234:5678:9abc outLinkName: eth0 table: main priority: 4096 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: some domainname: fqdn layer: platform resolvers: [] timeServers: [] operators: - operator: dhcp6 linkName: eth0 requireUp: true dhcp6: routeMetric: 2048 layer: platform - operator: dhcp4 linkName: eth0 requireUp: true layer: platform externalIPs: - 1.2.3.4 - 2603:1020:10:5::34 - 20.10.5.34 metadata: platform: azure hostname: examplevmname region: centralus zone: "0" instanceType: Standard_DS2 instanceId: /subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/Test/providers/Microsoft.Compute/virtualMachines/examplevmname providerId: azure:///subscriptions/xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx/resourceGroups/test/providers/Microsoft.Compute/virtualMachines/examplevmname ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/interfaces.json ================================================ [ { "ipv4": { "ipAddress": [ { "privateIpAddress": "172.18.1.10", "publicIpAddress": "1.2.3.4" } ], "subnet": [ { "address": "172.18.1.0", "prefix": "24" } ] }, "ipv6": { "ipAddress": [ { "privateIpAddress": "fd00::10", "publicIpAddress": "" } ] }, "macAddress": "000D3AD631EE" } ] ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/azure/testdata/loadbalancer.json ================================================ { "loadbalancer": { "publicIpAddresses": [ { "frontendIpAddress": "[2603:1020:10:5::34]", "privateIpAddress": "[fd00::10]" }, { "frontendIpAddress": "20.10.5.34", "privateIpAddress": "172.18.1.10" } ], "inboundRules": [ { "frontendIpAddress": "[fd60:172:16:88::5]", "protocol": "Tcp", "frontendPort": 6443, "backendPort": 6443, "privateIpAddress": "[fd00::10]" }, { "frontendIpAddress": "172.16.136.5", "protocol": "Tcp", "frontendPort": 6443, "backendPort": 6443, "privateIpAddress": "172.18.1.10" } ], "outboundRules": [] } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cloudstack contains the Cloudstack platform implementation. package cloudstack import ( "context" "fmt" "log" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Cloudstack is the concrete type that implements the runtime.Platform interface. type Cloudstack struct{} // ParseMetadata converts Cloudstack platform metadata into platform network config. func (e *Cloudstack) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } if metadata.PublicIPv4 != "" { if ip, err := netip.ParseAddr(metadata.PublicIPv4); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: e.Name(), Hostname: metadata.Hostname, Region: metadata.Zone, Zone: metadata.Zone, InstanceType: strings.ToLower(strings.SplitN(metadata.InstanceType, " ", 2)[0]), InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("cloudstack://%s", metadata.InstanceID), } return networkConfig, nil } // Name implements the runtime.Platform interface. func (e *Cloudstack) Name() string { return "cloudstack" } // Configuration implements the runtime.Platform interface. func (e *Cloudstack) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from %q", CloudstackUserDataEndpoint) return download.Download(ctx, CloudstackUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the runtime.Platform interface. func (e *Cloudstack) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (e *Cloudstack) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (e *Cloudstack) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching cloudstack instance config from: %q", CloudstackMetadataEndpoint) metadata, err := e.getMetadata(ctx) if err != nil { return err } networkConfig, err := e.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/cloudstack_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cloudstack_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestEmpty(t *testing.T) { p := &cloudstack.Cloudstack{} var m cloudstack.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &m)) networkConfig, err := p.ParseMetadata(&m) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cloudstack import ( "context" stderrors "errors" "fmt" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/download" ) const ( // CloudstackMetadataEndpoint is the local Cloudstack endpoint. CloudstackMetadataEndpoint = "http://data-server./latest/meta-data" // CloudstackUserDataEndpoint is the local Cloudstack endpoint for the config. CloudstackUserDataEndpoint = "http://data-server./latest/user-data" ) // MetadataConfig represents a metadata Cloudstack instance. type MetadataConfig struct { Hostname string `json:"local-hostname,omitempty"` InstanceID string `json:"instance-id,omitempty"` InstanceType string `json:"service-offering,omitempty"` PublicIPv4 string `json:"public-ipv4,omitempty"` Zone string `json:"availability-zone,omitempty"` } /* local-ipv4 public-hostname vm-id public-keys cloud-identifier hypervisor-host-name */ func (e *Cloudstack) getMetadata(ctx context.Context) (metadata *MetadataConfig, err error) { getMetadataKey := func(key string) (string, error) { res, metaerr := download.Download(ctx, fmt.Sprintf("%s/%s", CloudstackMetadataEndpoint, key), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if metaerr != nil && !stderrors.Is(metaerr, errors.ErrNoConfigSource) { return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, metaerr) } return string(res), nil } metadata = &MetadataConfig{} if metadata.Hostname, err = getMetadataKey("local-hostname"); err != nil { return nil, err } if metadata.InstanceType, err = getMetadataKey("service-offering"); err != nil { return nil, err } if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil { return nil, err } if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil { return nil, err } if metadata.Zone, err = getMetadataKey("availability-zone"); err != nil { return nil, err } return metadata, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/expected.yaml ================================================ addresses: [] links: [] routes: [] hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 1.2.3.4 metadata: platform: cloudstack hostname: talos.fqdn instanceType: standard.tiny instanceId: 3fe6b28a-669e-4eb2-bffd-4180c572c410 providerId: cloudstack://3fe6b28a-669e-4eb2-bffd-4180c572c410 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack/testdata/metadata.json ================================================ { "local-hostname": "talos.fqdn", "instance-id": "3fe6b28a-669e-4eb2-bffd-4180c572c410", "public-ipv4": "1.2.3.4", "service-offering": "standard.tiny" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/container.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package container contains the Container implementation of the [platform.Platform]. package container import ( "context" "encoding/base64" "log" "os" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Container is a platform for installing Talos via an Container image. type Container struct{} // Name implements the platform.Platform interface. func (c *Container) Name() string { return "container" } // Configuration implements the platform.Platform interface. func (c *Container) Configuration(context.Context, state.State) ([]byte, error) { log.Printf("fetching machine config from: USERDATA environment variable") s := os.Getenv("USERDATA") if s == "" { return nil, errors.ErrNoConfigSource } decoded, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, err } return decoded, nil } // Mode implements the platform.Platform interface. func (c *Container) Mode() runtime.Mode { return runtime.ModeContainer } // KernelArgs implements the runtime.Platform interface. func (c *Container) KernelArgs(string, quirks.Quirks) procfs.Parameters { return nil } // NetworkConfiguration implements the runtime.Platform interface. func (c *Container) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { networkConfig := &runtime.PlatformNetworkConfig{} hostnameSpec, err := files.ReadHostname("/etc/hostname") if err != nil { return err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) resolverSpec, err := files.ReadResolvConf("/etc/resolv.conf") if err != nil { return err } if len(resolverSpec.DNSServers) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, resolverSpec) } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: c.Name(), Hostname: hostnameSpec.FQDN(), InstanceType: os.Getenv("TALOSSKU"), } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/hostname.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package files provides internal methods to container platform to read files. package files import ( "bytes" "os" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ReadHostname reads and parses /etc/hostname file. func ReadHostname(path string) (network.HostnameSpecSpec, error) { hostname, err := os.ReadFile(path) if err != nil { return network.HostnameSpecSpec{}, err } hostname = bytes.TrimSpace(hostname) hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err = hostnameSpec.ParseFQDN(string(hostname)); err != nil { return network.HostnameSpecSpec{}, err } return hostnameSpec, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/hostname_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files" ) func TestReadHostname(t *testing.T) { t.Parallel() spec, err := files.ReadHostname("testdata/hostname") require.NoError(t, err) require.Equal(t, "foo", spec.Hostname) require.Equal(t, "example.com", spec.Domainname) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/resolv.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "bytes" "net/netip" "os" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // ReadResolvConf reads and parses /etc/resolv.conf file. func ReadResolvConf(path string) (network.ResolverSpecSpec, error) { resolverSpec := network.ResolverSpecSpec{ ConfigLayer: network.ConfigPlatform, } resolvers, err := os.ReadFile(path) if err != nil { return resolverSpec, err } for line := range bytes.SplitSeq(resolvers, []byte("\n")) { line = bytes.TrimSpace(line) line, _, _ = bytes.Cut(line, []byte("#")) if !bytes.HasPrefix(line, []byte("nameserver")) { continue } line = bytes.TrimSpace(bytes.TrimPrefix(line, []byte("nameserver"))) if addr, err := netip.ParseAddr(string(line)); err == nil { resolverSpec.DNSServers = append(resolverSpec.DNSServers, addr) } } return resolverSpec, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/resolv_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files_test import ( "net/netip" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files" ) func TestReadResolvConf(t *testing.T) { t.Parallel() spec, err := files.ReadResolvConf("testdata/resolv.conf") require.NoError(t, err) require.Equal(t, []netip.Addr{ netip.MustParseAddr("127.0.0.53"), netip.MustParseAddr("::1"), }, spec.DNSServers) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/testdata/hostname ================================================ foo.example.com ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/container/internal/files/testdata/resolv.conf ================================================ # This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8). # Do not edit. nameserver 127.0.0.53 # v4 one options edns0 trust-ad search . nameserver ::1 # this is V6 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package digitalocean contains the Digital Ocean implementation of the [platform.Platform]. package digitalocean import ( "context" "fmt" "log" "net/netip" "strconv" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/address" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // DigitalOcean is the concrete type that implements the platform.Platform interface. type DigitalOcean struct{} // Name implements the platform.Platform interface. func (d *DigitalOcean) Name() string { return "digital-ocean" } // ParseMetadata converts DigitalOcean platform metadata into platform network config. // //nolint:gocyclo,cyclop func (d *DigitalOcean) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } if len(metadata.DNS.Nameservers) > 0 { var dnsIPs []netip.Addr for _, dnsIP := range metadata.DNS.Nameservers { if ip, err := netip.ParseAddr(dnsIP); err == nil { dnsIPs = append(dnsIPs, ip) } } networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: "eth0", Up: true, ConfigLayer: network.ConfigPlatform, }) var publicIPs []string for _, iface := range metadata.Interfaces["public"] { if iface.IPv4 != nil { ifAddr, err := address.IPPrefixFrom(iface.IPv4.IPAddress, iface.IPv4.Netmask) if err != nil { return nil, fmt.Errorf("failed to parse ip address: %w", err) } publicIPs = append(publicIPs, iface.IPv4.IPAddress) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: ifAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) if iface.IPv4.Gateway != "" { gw, err := netip.ParseAddr(iface.IPv4.Gateway) if err != nil { return nil, fmt.Errorf("failed to parse gateway ip: %w", err) } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) metaServer, _ := netip.ParsePrefix("169.254.169.254/32") //nolint:errcheck networkConfig.Routes = append(networkConfig.Routes, network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, OutLinkName: "eth0", Destination: metaServer, Gateway: gw, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: 512, }) } } if iface.IPv6 != nil { ifAddr, err := address.IPPrefixFrom(iface.IPv6.IPAddress, strconv.Itoa(iface.IPv6.CIDR)) if err != nil { return nil, fmt.Errorf("failed to parse ip address: %w", err) } publicIPs = append(publicIPs, iface.IPv6.IPAddress) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: ifAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet6, }, ) if iface.IPv6.Gateway != "" { gw, err := netip.ParseAddr(iface.IPv6.Gateway) if err != nil { return nil, fmt.Errorf("failed to parse gateway ip: %w", err) } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } if iface.AnchorIPv4 != nil { ifAddr, err := address.IPPrefixFrom(iface.AnchorIPv4.IPAddress, iface.AnchorIPv4.Netmask) if err != nil { return nil, fmt.Errorf("failed to parse ip address: %w", err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: ifAddr, Scope: nethelpers.ScopeLink, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) } } for idx, iface := range metadata.Interfaces["private"] { ifName := fmt.Sprintf("eth%d", idx+1) networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: ifName, Up: true, ConfigLayer: network.ConfigPlatform, }) if iface.IPv4 != nil { ifAddr, err := address.IPPrefixFrom(iface.IPv4.IPAddress, iface.IPv4.Netmask) if err != nil { return nil, fmt.Errorf("failed to parse ip address: %w", err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: ifName, Address: ifAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) } } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: d.Name(), Hostname: metadata.Hostname, Region: metadata.Region, InstanceID: strconv.Itoa(metadata.DropletID), ProviderID: fmt.Sprintf("digitalocean://%d", metadata.DropletID), } return networkConfig, nil } // Configuration implements the platform.Platform interface. func (d *DigitalOcean) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", DigitalOceanUserDataEndpoint) return download.Download(ctx, DigitalOceanUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the platform.Platform interface. func (d *DigitalOcean) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (d *DigitalOcean) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyS0").Append("tty0").Append("tty1"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (d *DigitalOcean) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching DigitalOcean instance config from: %q ", DigitalOceanMetadataEndpoint) metadata, err := d.getMetadata(ctx) if err != nil { return err } networkConfig, err := d.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/digitalocean_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package digitalocean_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &digitalocean.DigitalOcean{} var metadata digitalocean.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) networkConfig, err := p.ParseMetadata(&metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package digitalocean import ( "context" "encoding/json" stderrors "errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/download" ) const ( // DigitalOceanExternalIPEndpoint displays all external addresses associated with the instance. DigitalOceanExternalIPEndpoint = "http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address" // DigitalOceanMetadataEndpoint is the local endpoint for the platform metadata. DigitalOceanMetadataEndpoint = "http://169.254.169.254/metadata/v1.json" // DigitalOceanUserDataEndpoint is the local endpoint for the config. DigitalOceanUserDataEndpoint = "http://169.254.169.254/metadata/v1/user-data" ) // MetadataConfig represents a metadata Digital Ocean instance. type MetadataConfig struct { Hostname string `json:"hostname,omitempty"` DropletID int `json:"droplet_id,omitempty"` Region string `json:"region,omitempty"` PublicIPv4 string `json:"public-ipv4,omitempty"` Tags []string `json:"tags,omitempty"` DNS struct { Nameservers []string `json:"nameservers,omitempty"` } `json:"dns"` Interfaces map[string][]struct { MACAddress string `json:"mac,omitempty"` Type string `json:"type,omitempty"` IPv4 *struct { IPAddress string `json:"ip_address,omitempty"` Netmask string `json:"netmask,omitempty"` Gateway string `json:"gateway,omitempty"` } `json:"ipv4,omitempty"` IPv6 *struct { IPAddress string `json:"ip_address,omitempty"` CIDR int `json:"cidr,omitempty"` Gateway string `json:"gateway,omitempty"` } `json:"ipv6,omitempty"` AnchorIPv4 *struct { IPAddress string `json:"ip_address,omitempty"` Netmask string `json:"netmask,omitempty"` Gateway string `json:"gateway,omitempty"` } `json:"anchor_ipv4,omitempty"` } `json:"interfaces,omitempty"` } func (d *DigitalOcean) getMetadata(ctx context.Context) (*MetadataConfig, error) { metaConfigDl, err := download.Download(ctx, DigitalOceanMetadataEndpoint, download.WithErrorOnNotFound(errors.ErrNoHostname), download.WithErrorOnEmptyResponse(errors.ErrNoHostname)) if err != nil && !stderrors.Is(err, errors.ErrNoHostname) { return nil, err } var metadata MetadataConfig if err = json.Unmarshal(metaConfigDl, &metadata); err != nil { return nil, err } extIP, err := download.Download(ctx, DigitalOceanExternalIPEndpoint, download.WithErrorOnNotFound(errors.ErrNoExternalIPs), download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs)) if err != nil && !stderrors.Is(err, errors.ErrNoExternalIPs) { return nil, err } metadata.PublicIPv4 = string(extIP) return &metadata, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/expected.yaml ================================================ addresses: - address: 128.199.52.32/19 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 2a03:b0c0:2:d0::1478:3001/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform - address: 10.18.0.5/16 linkName: eth0 family: inet4 scope: link flags: permanent layer: platform - address: 10.133.0.2/16 linkName: eth1 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: "" src: "" gateway: 128.199.32.1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 169.254.169.254/32 src: "" gateway: 128.199.32.1 outLinkName: eth0 table: main priority: 512 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: 2a03:b0c0:2:d0::1 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: debian-s-1vcpu-512mb-10gb-ams3-01 domainname: "" layer: platform resolvers: - dnsServers: - 67.207.67.2 - 67.207.67.3 layer: platform timeServers: [] operators: [] externalIPs: - 128.199.52.32 - 2a03:b0c0:2:d0::1478:3001 metadata: platform: digital-ocean hostname: debian-s-1vcpu-512mb-10gb-ams3-01 region: ams3 instanceId: "320206672" providerId: digitalocean://320206672 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean/testdata/metadata.json ================================================ { "droplet_id": 320206672, "hostname": "debian-s-1vcpu-512mb-10gb-ams3-01", "user_data": "", "vendor_data": "", "public_keys": [], "auth_key": "490eac0a2fc04503267ef85064407f2f", "region": "ams3", "interfaces": { "private": [ { "ipv4": { "ip_address": "10.133.0.2", "netmask": "255.255.0.0", "gateway": "10.133.0.1" }, "mac": "2a:3c:79:3d:f3:b7", "type": "private" } ], "public": [ { "ipv4": { "ip_address": "128.199.52.32", "netmask": "255.255.224.0", "gateway": "128.199.32.1" }, "ipv6": { "ip_address": "2A03:B0C0:0002:00D0:0000:0000:1478:3001", "cidr": 64, "gateway": "2a03:b0c0:2:d0::1" }, "anchor_ipv4": { "ip_address": "10.18.0.5", "netmask": "255.255.0.0", "gateway": "10.18.0.1" }, "mac": "12:2f:49:0c:eb:c0", "type": "public" } ] }, "floating_ip": { "ipv4": { "active": false } }, "reserved_ip": { "ipv4": { "active": false } }, "dns": { "nameservers": [ "67.207.67.2", "67.207.67.3" ] }, "tags": [ "label123" ], "features": { "dhcp_enabled": false }, "modify_index": 113261986, "dotty_status": "running", "ssh_info": { "port": 22 } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package equinixmetal contains the Equinix Metal implementation of the [platform.Platform]. package equinixmetal import ( "bytes" "context" "encoding/json" "fmt" "io" "log" "net/http" "net/netip" "slices" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Event holds data to pass to the Equinix Metal event URL. type Event struct { Type string `json:"type"` Message string `json:"msg"` } // Network holds network info from the equinixmetal metadata. type Network struct { Bonding Bonding `json:"bonding"` Interfaces []Interface `json:"interfaces"` Addresses []Address `json:"addresses"` } // Bonding holds bonding info from the equinixmetal metadata. type Bonding struct { Mode int `json:"mode"` } // Interface holds interface info from the equinixmetal metadata. type Interface struct { Name string `json:"name"` MAC string `json:"mac"` Bond string `json:"bond"` } // Address holds address info from the equinixmetal metadata. type Address struct { Public bool `json:"public"` Management bool `json:"management"` Enabled bool `json:"enabled"` CIDR int `json:"cidr"` Family int `json:"address_family"` Netmask string `json:"netmask"` Network string `json:"network"` Address string `json:"address"` Gateway string `json:"gateway"` } // BGPNeighbor holds BGP neighbor info from the equinixmetal metadata. type BGPNeighbor struct { AddressFamily int `json:"address_family"` PeerIPs []string `json:"peer_ips"` } const ( // EquinixMetalUserDataEndpoint is the local metadata endpoint for Equinix. EquinixMetalUserDataEndpoint = "https://metadata.platformequinix.com/userdata" // EquinixMetalMetaDataEndpoint is the local endpoint for machine info like networking. EquinixMetalMetaDataEndpoint = "https://metadata.platformequinix.com/metadata" ) // EquinixMetal is a platform for EquinixMetal Metal cloud. type EquinixMetal struct{} // Name implements the platform.Platform interface. func (p *EquinixMetal) Name() string { return "equinixMetal" } // Configuration implements the platform.Platform interface. func (p *EquinixMetal) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", EquinixMetalUserDataEndpoint) return download.Download(ctx, EquinixMetalUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the platform.Platform interface. func (p *EquinixMetal) Mode() runtime.Mode { return runtime.ModeMetal } // KernelArgs implements the runtime.Platform interface. func (p *EquinixMetal) KernelArgs(arch string, _ quirks.Quirks) procfs.Parameters { switch arch { case "amd64": return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyS1,115200n8"), } case "arm64": return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyAMA0,115200"), } default: return nil } } // ParseMetadata converts Equinix Metal metadata into Talos network configuration. // //nolint:gocyclo,cyclop func (p *EquinixMetal) ParseMetadata(ctx context.Context, equinixMetadata *MetadataConfig, st state.State) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} // 1. Links // translate the int returned from bond mode metadata to the type needed by network resources bondMode := nethelpers.BondMode(uint8(equinixMetadata.Network.Bonding.Mode)) hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st) if err != nil { return nil, fmt.Errorf("error listing host interfaces: %w", err) } bondSlaveIndexes := map[string]int{} firstBond := "" for _, iface := range equinixMetadata.Network.Interfaces { if iface.Bond == "" { continue } if firstBond == "" { firstBond = iface.Bond } found := false for hostInterface := range hostInterfaces.All() { // match using permanent MAC address: // - bond interfaces don't have permanent addresses set, so we skip them this way // - if the bond is already configured, regular hardware address is overwritten with bond address if hostInterface.TypedSpec().PermanentAddr.String() == iface.MAC { found = true slaveIndex := bondSlaveIndexes[iface.Bond] networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: hostInterface.Metadata().ID(), Up: true, BondSlave: network.BondSlave{ MasterName: iface.Bond, SlaveIndex: slaveIndex, }, ConfigLayer: network.ConfigPlatform, }) bondSlaveIndexes[iface.Bond]++ break } } if !found { log.Printf("interface with MAC %q wasn't found on the host, adding with the name from metadata", iface.MAC) slaveIndex := bondSlaveIndexes[iface.Bond] networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: iface.Name, Up: true, BondSlave: network.BondSlave{ MasterName: iface.Bond, SlaveIndex: slaveIndex, }, }) bondSlaveIndexes[iface.Bond]++ } } bondNames := maps.Keys(bondSlaveIndexes) slices.Sort(bondNames) for _, bondName := range bondNames { bondLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: bondName, Logical: true, Up: true, Kind: network.LinkKindBond, Type: nethelpers.LinkEther, BondMaster: network.BondMasterSpec{ Mode: bondMode, DownDelay: 200, MIIMon: 100, UpDelay: 200, HashPolicy: nethelpers.BondXmitPolicyLayer34, }, } if bondMode == nethelpers.BondMode8023AD { bondLink.BondMaster.ADLACPActive = nethelpers.ADLACPActiveOn } networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults() networkConfig.Links = append(networkConfig.Links, bondLink) } // 2. addresses var publicIPs []string for _, addr := range equinixMetadata.Network.Addresses { if !(addr.Enabled && addr.Management) { continue } if addr.Public { publicIPs = append(publicIPs, addr.Address) } ipAddr, err := netip.ParsePrefix(fmt.Sprintf("%s/%d", addr.Address, addr.CIDR)) if err != nil { return nil, err } family := nethelpers.FamilyInet4 if ipAddr.Addr().Is6() { family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: firstBond, Address: ipAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } // 3. routes var privateGateway netip.Addr for _, addr := range equinixMetadata.Network.Addresses { if !(addr.Enabled && addr.Management) { continue } ipAddr, err := netip.ParsePrefix(fmt.Sprintf("%s/%d", addr.Address, addr.CIDR)) if err != nil { return nil, err } family := nethelpers.FamilyInet4 if ipAddr.Addr().Is6() { family = nethelpers.FamilyInet6 } if addr.Public { // for "Public" address add the default route gw, err := netip.ParseAddr(addr.Gateway) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: firstBond, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: network.DefaultRouteMetric, } if addr.Family == 6 { route.Priority = 2 * network.DefaultRouteMetric } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } else { // for "Private" addresses, we add a route that goes out the gateway for the private subnets. for _, privSubnet := range equinixMetadata.PrivateSubnets { gw, err := netip.ParseAddr(addr.Gateway) if err != nil { return nil, err } privateGateway = gw dest, err := netip.ParsePrefix(privSubnet) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, Destination: dest, OutLinkName: firstBond, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } } // 4. hostname if equinixMetadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(equinixMetadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } // 5. platform metadata networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: p.Name(), Hostname: equinixMetadata.Hostname, Region: equinixMetadata.Metro, Zone: equinixMetadata.Facility, InstanceType: equinixMetadata.Plan, InstanceID: equinixMetadata.ID, ProviderID: fmt.Sprintf("equinixmetal://%s", equinixMetadata.ID), } // 6. BGP neighbors for _, bgpNeighbor := range equinixMetadata.BGPNeighbors { if bgpNeighbor.AddressFamily != 4 { continue } for _, peerIP := range bgpNeighbor.PeerIPs { peer, err := netip.ParseAddr(peerIP) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: privateGateway, Destination: netip.PrefixFrom(peer, 32), OutLinkName: firstBond, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } return networkConfig, nil } // NetworkConfiguration implements the runtime.Platform interface. func (p *EquinixMetal) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching equinix network config from: %q", EquinixMetalMetaDataEndpoint) metadataConfig, err := download.Download(ctx, EquinixMetalMetaDataEndpoint) if err != nil { return err } var meta MetadataConfig if err = json.Unmarshal(metadataConfig, &meta); err != nil { return err } networkConfig, err := p.ParseMetadata(ctx, &meta, st) if err != nil { return err } select { case <-ctx.Done(): return ctx.Err() case ch <- networkConfig: } return nil } // FireEvent will take an event and pass it to an events server. // nb: This is currently only used with Equinix Metal but we may find interesting ways // to extend it for other event servers (Azure may have something similar?) func (p *EquinixMetal) FireEvent(ctx context.Context, event Event) error { var eventURL *string if eventURL = procfs.ProcCmdline().Get(constants.KernelParamEquinixMetalEvents).First(); eventURL == nil { return errors.ErrNoEventURL } eventData, err := json.Marshal(event) if err != nil { return err } err = retry.Constant(5*time.Minute, retry.WithUnits(time.Second), retry.WithErrorLogging(true)).RetryWithContext( ctx, func(ctx context.Context) error { req, reqErr := http.NewRequestWithContext(ctx, http.MethodPost, *eventURL, bytes.NewReader(eventData)) if reqErr != nil { return reqErr } resp, reqErr := http.DefaultClient.Do(req) if resp != nil { io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024*1024)) //nolint:errcheck resp.Body.Close() //nolint:errcheck } return retry.ExpectedError(reqErr) }, ) return err } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/equinix_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package equinixmetal_test import ( _ "embed" "encoding/json" "fmt" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string //go:embed testdata/metadata-2bonds.json var rawMetadata2Bonds []byte //go:embed testdata/expected-2bonds.yaml var expectedNetworkConfig2Bonds string func TestParseMetadata(t *testing.T) { p := &equinixmetal.EquinixMetal{} var m equinixmetal.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &m)) ctx := t.Context() st := state.WrapCore(namespaced.NewState(inmem.Build)) eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf8} require.NoError(t, st.Create(ctx, eth1)) eth2 := network.NewLinkStatus(network.NamespaceName, "eth2") eth2.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf9} require.NoError(t, st.Create(ctx, eth2)) networkConfig, err := p.ParseMetadata(ctx, &m, st) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } func TestParseMetadata2Bonds(t *testing.T) { p := &equinixmetal.EquinixMetal{} var m equinixmetal.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata2Bonds, &m)) ctx := t.Context() st := state.WrapCore(namespaced.NewState(inmem.Build)) eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xe4, 0x43, 0x4b, 0xd0, 0x7b, 0x50} require.NoError(t, st.Create(ctx, eth0)) eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xe4, 0x43, 0x4b, 0xd0, 0x7b, 0x51} require.NoError(t, st.Create(ctx, eth1)) eth2 := network.NewLinkStatus(network.NamespaceName, "eth2") eth2.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xe4, 0x43, 0x4b, 0xd0, 0x7b, 0x52} require.NoError(t, st.Create(ctx, eth2)) eth3 := network.NewLinkStatus(network.NamespaceName, "eth3") eth3.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xe4, 0x43, 0x4b, 0xd0, 0x7b, 0x53} require.NoError(t, st.Create(ctx, eth3)) networkConfig, err := p.ParseMetadata(ctx, &m, st) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) fmt.Println(string(marshaled)) assert.Equal(t, expectedNetworkConfig2Bonds, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package equinixmetal // MetadataConfig holds equinixmetal metadata info. type MetadataConfig struct { ID string `json:"id"` Hostname string `json:"hostname"` Plan string `json:"plan"` Metro string `json:"metro"` Facility string `json:"facility"` Network Network `json:"network"` BGPNeighbors []BGPNeighbor `json:"bgp_neighbors"` PrivateSubnets []string `json:"private_subnets"` } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected-2bonds.yaml ================================================ addresses: - address: 147.28.162.183/31 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform - address: 2604:1380:45f2:6f00::1/127 linkName: bond0 family: inet6 scope: global flags: permanent layer: platform - address: 10.68.217.1/31 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond1 layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 slaveIndex: 1 layer: platform - name: eth3 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond1 slaveIndex: 1 layer: platform - name: bond0 logical: true up: true mtu: 0 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: slow arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform - name: bond1 logical: true up: true mtu: 0 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: slow arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform routes: - family: inet4 dst: "" src: "" gateway: 147.28.162.182 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: '2604:1380:45f2:6f00::' outLinkName: bond0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 10.68.217.0 outLinkName: bond0 table: main scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: devcluster-38uvi6 domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 147.28.162.183 - 2604:1380:45f2:6f00::1 metadata: platform: equinixMetal hostname: devcluster-38uvi6 region: dc zone: dc13 instanceType: n2.xlarge.x86 instanceId: b45359b0-5a13-454b-82eb-d4959924f9f0 providerId: equinixmetal://b45359b0-5a13-454b-82eb-d4959924f9f0 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/expected.yaml ================================================ addresses: - address: 147.75.78.41/31 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform - address: 2604:1380:45d1:fd00::11/127 linkName: bond0 family: inet6 scope: global flags: permanent layer: platform - address: 10.66.142.17/31 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform links: - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 slaveIndex: 1 layer: platform - name: bond0 logical: true up: true mtu: 0 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: slow arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform routes: - family: inet4 dst: "" src: "" gateway: 147.75.78.40 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: 2604:1380:45d1:fd00::10 outLinkName: bond0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 10.66.142.16 outLinkName: bond0 table: main scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 169.254.255.1/32 src: "" gateway: 10.66.142.16 outLinkName: bond0 table: main scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 169.254.255.2/32 src: "" gateway: 10.66.142.16 outLinkName: bond0 table: main scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: infra-green-ci domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 147.75.78.41 - 2604:1380:45d1:fd00::11 metadata: platform: equinixMetal hostname: infra-green-ci region: ny zone: ny5 instanceType: c3.medium.x86 instanceId: X providerId: equinixmetal://X ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/metadata-2bonds.json ================================================ { "id": "b45359b0-5a13-454b-82eb-d4959924f9f0", "hostname": "devcluster-38uvi6", "iqn": "iqn.2024-05.net.packet:device.b45359b0", "operating_system": { "slug": "custom_ipxe", "version": "1", "distro": "custom_ipxe", "license_activation": { "state": "unlicensed" }, "image_tag": null }, "plan": "n2.xlarge.x86", "reserved": false, "class": "n2.xlarge.x86", "facility": "dc13", "metro": "dc", "private_subnets": ["10.0.0.0/8"], "tags": [], "ssh_keys": [ ], "customdata": {}, "specs": { "cpus": [ { "count": 2, "type": "Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz" } ], "memory": { "total": "384GB" }, "drives": [ { "count": 2, "size": "120GB", "type": "SSD", "category": "boot" }, { "count": 1, "size": "3.8TB", "type": "NVME", "category": "storage" } ], "nics": [{ "count": 4, "type": "10Gbps" }], "features": { "uefi": false } }, "switch_short_id": "5b2b2148", "state": "active", "storage_source": "default", "storage": { "disks": [ { "device": "/dev/sda", "wipeTable": true, "partitions": [ { "label": "BIOS", "number": 1, "size": 4096 }, { "label": "SWAP", "number": 2, "size": "3993600" }, { "label": "ROOT", "number": 3, "size": 0 } ] } ], "filesystems": [ { "mount": { "device": "/dev/sda3", "format": "ext4", "point": "/", "create": { "options": ["-L", "ROOT"] } } }, { "mount": { "device": "/dev/sda2", "format": "swap", "point": "none", "create": { "options": ["-L", "SWAP"] } } } ] }, "volumes": [], "boot_drive_hint": "DELLBOSS VD", "network": { "bonding": { "mode": 4, "link_aggregation": "mlag_ha", "mac": "e4:43:4b:d0:7b:50" }, "interfaces": [ { "name": "eth0", "mac": "e4:43:4b:d0:7b:50", "bond": "bond0" }, { "name": "eth1", "mac": "e4:43:4b:d0:7b:51", "bond": "bond1" }, { "name": "eth2", "mac": "e4:43:4b:d0:7b:52", "bond": "bond0" }, { "name": "eth3", "mac": "e4:43:4b:d0:7b:53", "bond": "bond1" } ], "addresses": [ { "id": "e5e5c1db-ef6d-4461-958e-50b88a96833e", "address_family": 4, "netmask": "255.255.255.254", "created_at": "2024-05-22T17:59:09Z", "public": true, "cidr": 31, "management": true, "enabled": true, "network": "147.28.162.182", "address": "147.28.162.183", "gateway": "147.28.162.182", "parent_block": { "network": "147.28.162.182", "netmask": "255.255.255.254", "cidr": 31, "href": "/ips/909dbc49-1640-4b0a-b9c4-20e4cef044e8" } }, { "id": "1cc68fbc-c0d3-42e2-a186-a73923e65bec", "address_family": 6, "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "created_at": "2024-05-22T17:59:09Z", "public": true, "cidr": 127, "management": true, "enabled": true, "network": "2604:1380:45f2:6f00::", "address": "2604:1380:45f2:6f00::1", "gateway": "2604:1380:45f2:6f00::", "parent_block": { "network": "2604:1380:45f2:6f00:0000:0000:0000:0000", "netmask": "ffff:ffff:ffff:ff00:0000:0000:0000:0000", "cidr": 56, "href": "/ips/77b6f5dd-11e9-47bd-9c5c-6db6ec29fe42" } }, { "id": "8d4d7e88-730c-409e-97b6-99b2f69aeede", "address_family": 4, "netmask": "255.255.255.254", "created_at": "2024-05-22T17:59:09Z", "public": false, "cidr": 31, "management": true, "enabled": true, "network": "10.68.217.0", "address": "10.68.217.1", "gateway": "10.68.217.0", "parent_block": { "network": "10.68.217.0", "netmask": "255.255.255.128", "cidr": 25, "href": "/ips/6258119f-c7c4-4158-a54a-fb317263a3ce" } } ], "metal_gateways": [] }, "api_url": "https://metadata.packet.net", "phone_home_url": "http://tinkerbell.dc13.packet.net/phone-home", "user_state_url": "http://tinkerbell.dc13.packet.net/events" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal/testdata/metadata.json ================================================ { "id": "X", "hostname": "infra-green-ci", "plan": "c3.medium.x86", "reserved": false, "class": "c3.medium.x86", "facility": "ny5", "metro": "ny", "private_subnets": [ "10.0.0.0/8" ], "tags": [], "ssh_keys": [ ], "customdata": {}, "network": { "bonding": { "mode": 4, "link_aggregation": "mlag_ha", "mac": "68:05:ca:b8:f1:f8" }, "interfaces": [ { "name": "eth0", "mac": "68:05:ca:b8:f1:f8", "bond": "bond0" }, { "name": "eth1", "mac": "68:05:ca:b8:f1:f9", "bond": "bond0" } ], "addresses": [ { "id": "d6be5d63-50f8-452c-b5cd-6cba42fbd5b3", "address_family": 4, "netmask": "255.255.255.254", "created_at": "2021-11-24T20:24:54Z", "public": true, "cidr": 31, "management": true, "enabled": true, "network": "147.75.78.40", "address": "147.75.78.41", "gateway": "147.75.78.40", "parent_block": { "network": "147.75.78.40", "netmask": "255.255.255.254", "cidr": 31, "href": "/ips/e5cc5a4e-1d80-42c8-b5ea-39644effb407" } }, { "id": "09c743e9-52e4-4125-9e88-da56c8e62ae4", "address_family": 6, "netmask": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe", "created_at": "2021-11-24T20:24:54Z", "public": true, "cidr": 127, "management": true, "enabled": true, "network": "2604:1380:45d1:fd00::10", "address": "2604:1380:45d1:fd00::11", "gateway": "2604:1380:45d1:fd00::10", "parent_block": { "network": "2604:1380:45d1:fd00:0000:0000:0000:0000", "netmask": "ffff:ffff:ffff:ff00:0000:0000:0000:0000", "cidr": 56, "href": "/ips/a76e6dd1-a22a-4f8a-a04d-7b68b4f358e5" } }, { "id": "c7d3cd31-beae-460a-b008-29776c95562b", "address_family": 4, "netmask": "255.255.255.255", "created_at": "2021-12-10T13:41:14Z", "public": true, "cidr": 32, "management": false, "enabled": true, "network": "147.75.195.143", "address": "147.75.195.143", "gateway": "147.75.195.143", "parent_block": { "network": "147.75.195.143", "netmask": "255.255.255.255", "cidr": 32, "href": "/ips/77e054ac-cd40-473a-9c59-8f6c322c5a20" } }, { "id": "5e0bc796-9e7f-46a5-9472-ced35b8acb6d", "address_family": 4, "netmask": "255.255.255.254", "created_at": "2021-11-24T20:24:54Z", "public": false, "cidr": 31, "management": true, "enabled": true, "network": "10.66.142.16", "address": "10.66.142.17", "gateway": "10.66.142.16", "parent_block": { "network": "10.66.142.0", "netmask": "255.255.255.128", "cidr": 25, "href": "/ips/045b5dd5-6a32-48e6-870d-8ea9a39169d6" } } ], "metal_gateways": [] }, "bgp_neighbors": [ { "address_family": 4, "customer_as": 65000, "customer_ip": "10.67.50.1", "md5_enabled": false, "md5_password": null, "multihop": true, "peer_as": 65530, "peer_ips": [ "169.254.255.1", "169.254.255.2" ], "routes_in": [], "routes_out": [] }, { "address_family": 6, "customer_as": 65000, "customer_ip": "2604:1380:45e1:5000::1", "md5_enabled": false, "md5_password": null, "multihop": true, "peer_as": 65530, "peer_ips": [ "fc00:0000:0000:0000:0000:0000:0000:000e", "fc00:0000:0000:0000:0000:0000:0000:000f" ], "routes_in": [], "routes_out": [] } ], "api_url": "https://metadata.packet.net", "phone_home_url": "http://tinkerbell.ny5.packet.net/phone-home", "user_state_url": "http://tinkerbell.ny5.packet.net/events" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/errors/errors.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package errors contains errors used by the platform package. package errors import "errors" // ErrNoConfigSource indicates that the platform does not have a configured source for the configuration. var ErrNoConfigSource = errors.New("no configuration source") // ErrNoHostname indicates that the meta server does not have a instance hostname. var ErrNoHostname = errors.New("failed to fetch hostname from metadata service") // ErrNoExternalIPs indicates that the meta server does not have a external addresses. var ErrNoExternalIPs = errors.New("failed to fetch external addresses from metadata service") // ErrNoEventURL indicates that the platform does not have an expected events URL in the kernel params. var ErrNoEventURL = errors.New("no event URL") // ErrMetadataNotReady indicates that the platform does not have metadata yet. var ErrMetadataNotReady = errors.New("platform metadata is not ready") ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale/exoscale.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package exoscale contains the Exoscale platform implementation. package exoscale import ( "context" "fmt" "log" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Exoscale is the concrete type that implements the runtime.Platform interface. type Exoscale struct{} // ParseMetadata converts Exoscale platform metadata into platform network config. func (e *Exoscale) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } if metadata.PublicIPv4 != "" { if ip, err := netip.ParseAddr(metadata.PublicIPv4); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: e.Name(), Hostname: metadata.Hostname, Region: metadata.Zone, Zone: metadata.Zone, InstanceType: strings.ToLower(strings.SplitN(metadata.InstanceType, " ", 2)[0]), InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("exoscale://%s", metadata.InstanceID), } return networkConfig, nil } // Name implements the runtime.Platform interface. func (e *Exoscale) Name() string { return "exoscale" } // Configuration implements the runtime.Platform interface. func (e *Exoscale) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from %q", ExoscaleUserDataEndpoint) return download.Download(ctx, ExoscaleUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the runtime.Platform interface. func (e *Exoscale) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (e *Exoscale) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (e *Exoscale) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching exoscale instance config from: %q", ExoscaleMetadataEndpoint) metadata, err := e.getMetadata(ctx) if err != nil { return err } networkConfig, err := e.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale/exoscale_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package exoscale_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestEmpty(t *testing.T) { p := &exoscale.Exoscale{} var m exoscale.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &m)) networkConfig, err := p.ParseMetadata(&m) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package exoscale import ( "context" stderrors "errors" "fmt" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/download" ) const ( // ExoscaleMetadataEndpoint is the local Exoscale endpoint. ExoscaleMetadataEndpoint = "http://169.254.169.254/1.0/meta-data" // ExoscaleUserDataEndpoint is the local Exoscale endpoint for the config. ExoscaleUserDataEndpoint = "http://169.254.169.254/1.0/user-data" ) // MetadataConfig represents a metadata Exoscale instance. type MetadataConfig struct { Hostname string `json:"local-hostname,omitempty"` InstanceID string `json:"instance-id,omitempty"` InstanceType string `json:"service-offering,omitempty"` PublicIPv4 string `json:"public-ipv4,omitempty"` Zone string `json:"availability-zone,omitempty"` } func (e *Exoscale) getMetadata(ctx context.Context) (metadata *MetadataConfig, err error) { getMetadataKey := func(key string) (string, error) { res, metaerr := download.Download(ctx, fmt.Sprintf("%s/%s", ExoscaleMetadataEndpoint, key), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if metaerr != nil && !stderrors.Is(metaerr, errors.ErrNoConfigSource) { return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, metaerr) } return string(res), nil } metadata = &MetadataConfig{} if metadata.Hostname, err = getMetadataKey("local-hostname"); err != nil { return nil, err } if metadata.InstanceType, err = getMetadataKey("service-offering"); err != nil { return nil, err } if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil { return nil, err } if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil { return nil, err } if metadata.Zone, err = getMetadataKey("availability-zone"); err != nil { return nil, err } return metadata, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale/testdata/expected.yaml ================================================ addresses: [] links: [] routes: [] hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 1.2.3.4 metadata: platform: exoscale hostname: talos.fqdn instanceType: standard.tiny instanceId: 3085c764-b270-45b0-b974-68c55a9c2d53 providerId: exoscale://3085c764-b270-45b0-b974-68c55a9c2d53 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale/testdata/metadata.json ================================================ { "local-hostname": "talos.fqdn", "instance-id": "3085c764-b270-45b0-b974-68c55a9c2d53", "public-ipv4": "1.2.3.4", "service-offering": "standard.tiny" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package gcp contains the GCP implementation of the [platform.Platform]. package gcp import ( "context" "fmt" "log" "net/netip" "strconv" "strings" "cloud.google.com/go/compute/metadata" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // GCP is the concrete type that implements the platform.Platform interface. type GCP struct{} // Name implements the platform.Platform interface. func (g *GCP) Name() string { return "gcp" } // ParseMetadata converts GCP platform metadata into platform network config. // //nolint:gocyclo func (g *GCP) ParseMetadata(metadata *MetadataConfig, interfaces []NetworkInterfaceConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } dns, _ := netip.ParseAddr(gcpResolverServer) //nolint:errcheck networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: []netip.Addr{dns}, ConfigLayer: network.ConfigPlatform, }) networkConfig.TimeServers = append(networkConfig.TimeServers, network.TimeServerSpecSpec{ NTPServers: []string{gcpTimeServer}, ConfigLayer: network.ConfigPlatform, }) region := metadata.Zone if idx := strings.LastIndex(region, "-"); idx != -1 { region = region[:idx] } for idx, iface := range interfaces { ifname := fmt.Sprintf("eth%d", idx) networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: ifname, Up: true, MTU: uint32(iface.MTU), ConfigLayer: network.ConfigPlatform, }) networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: ifname, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, RequireUp: true, ConfigLayer: network.ConfigPlatform, }) for _, ipv6addr := range iface.IPv6 { if ipv6addr == "" || iface.GatewayIPv6 == "" { continue } ipPrefix, err := netip.ParsePrefix(ipv6addr) if err != nil { return nil, fmt.Errorf("failed to parse ip address: %w", err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: ifname, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet6, }, ) gw, err := netip.ParseAddr(iface.GatewayIPv6) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: ifname, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } for _, iface := range interfaces { for _, ipStr := range iface.AccessConfigs { if ipStr.Type == "ONE_TO_ONE_NAT" { if ip, err := netip.ParseAddr(ipStr.ExternalIP); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } } } preempted, _ := strconv.ParseBool(metadata.Preempted) //nolint:errcheck var tags map[string]string if len(metadata.Tags) > 0 { tags = make(map[string]string, len(metadata.Tags)) for _, tag := range metadata.Tags { tags[tag] = "" } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: g.Name(), Hostname: metadata.Hostname, Region: region, Zone: metadata.Zone, InstanceType: metadata.InstanceType, InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("gce://%s/%s/%s", metadata.ProjectID, metadata.Zone, metadata.Name), Spot: preempted, Tags: tags, } return networkConfig, nil } // Configuration implements the platform.Platform interface. func (g *GCP) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from GCP metadata service") userdata, err := netutils.RetryFetch(ctx, g.fetchConfiguration) if err != nil { return nil, err } if strings.TrimSpace(userdata) == "" { return nil, errors.ErrNoConfigSource } return []byte(userdata), nil } func (g *GCP) fetchConfiguration(ctx context.Context) (string, error) { userdata, err := metadata.InstanceAttributeValueWithContext(ctx, "user-data") if err != nil { if _, ok := err.(metadata.NotDefinedError); ok { return "", errors.ErrNoConfigSource } return "", retry.ExpectedError(err) } return userdata, nil } // Mode implements the platform.Platform interface. func (g *GCP) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (g *GCP) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"), // disable 'kexec' as GCP VMs sometimes are stuck on kexec, and normal soft reboot // doesn't take much longer on VMs procfs.NewParameter("sysctl.kernel.kexec_load_disabled").Append("1"), } } // NetworkConfiguration implements the runtime.Platform interface. func (g *GCP) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching gcp instance config") metadata, err := g.getMetadata(ctx) if err != nil { return fmt.Errorf("failed to receive GCP metadata: %w", err) } network, err := g.getNetworkMetadata(ctx) if err != nil { return fmt.Errorf("failed to receive GCP network metadata: %w", err) } networkConfig, err := g.ParseMetadata(metadata, network) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gcp_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/interfaces.json var rawInterfaces []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &gcp.GCP{} var ( metadata gcp.MetadataConfig interfaces []gcp.NetworkInterfaceConfig ) require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) require.NoError(t, json.Unmarshal(rawInterfaces, &interfaces)) networkConfig, err := p.ParseMetadata(&metadata, interfaces) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gcp import ( "context" "encoding/json" "strings" "cloud.google.com/go/compute/metadata" ) const ( // https://cloud.google.com/compute/docs/metadata/overview gcpResolverServer = "169.254.169.254" gcpTimeServer = "metadata.google.internal" ) // MetadataConfig holds meta info. type MetadataConfig struct { ProjectID string `json:"project-id"` Name string `json:"name,omitempty"` Hostname string `json:"hostname,omitempty"` Zone string `json:"zone,omitempty"` InstanceType string `json:"machine-type"` InstanceID string `json:"id"` Preempted string `json:"preempted"` Tags []string `json:"tags,omitempty"` } // NetworkInterfaceConfig holds network meta info. type NetworkInterfaceConfig struct { AccessConfigs []struct { ExternalIP string `json:"externalIp,omitempty"` Type string `json:"type,omitempty"` } `json:"accessConfigs,omitempty"` GatewayIPv4 string `json:"gateway,omitempty"` GatewayIPv6 string `json:"gatewayIpv6,omitempty"` IPv4 string `json:"ip,omitempty"` IPv6 []string `json:"ipv6,omitempty"` MTU int `json:"mtu,omitempty"` } func (g *GCP) getMetadata(ctx context.Context) (*MetadataConfig, error) { var ( meta MetadataConfig err error ) if meta.ProjectID, err = metadata.ProjectIDWithContext(ctx); err != nil { return nil, err } if meta.Name, err = metadata.InstanceNameWithContext(ctx); err != nil { return nil, err } instanceType, err := metadata.GetWithContext(ctx, "instance/machine-type") if err != nil { return nil, err } meta.InstanceType = strings.TrimSpace(instanceType[strings.LastIndex(instanceType, "/")+1:]) if meta.InstanceID, err = metadata.InstanceIDWithContext(ctx); err != nil { return nil, err } if meta.Hostname, err = metadata.HostnameWithContext(ctx); err != nil { return nil, err } if meta.Zone, err = metadata.ZoneWithContext(ctx); err != nil { return nil, err } meta.Preempted, err = metadata.GetWithContext(ctx, "instance/scheduling/preemptible") if err != nil { return nil, err } meta.Tags, err = metadata.InstanceTagsWithContext(ctx) if err != nil { return nil, err } return &meta, nil } func (g *GCP) getNetworkMetadata(ctx context.Context) ([]NetworkInterfaceConfig, error) { metadataNetworkConfigDl, err := metadata.GetWithContext(ctx, "instance/network-interfaces/?recursive=true&alt=json") if err != nil { return nil, err } var unmarshalledNetworkConfig []NetworkInterfaceConfig if err = json.Unmarshal([]byte(metadataNetworkConfigDl), &unmarshalledNetworkConfig); err != nil { return nil, err } return unmarshalledNetworkConfig, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/expected.yaml ================================================ addresses: - address: fd20:172:1610:7003:0:1::/96 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 1500 kind: "" type: netrom layer: platform routes: - family: inet6 dst: "" src: "" gateway: fe80::4001:acff:fe10:1 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: - dnsServers: - 169.254.169.254 layer: platform timeServers: - timeServers: - metadata.google.internal layer: platform operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 layer: platform externalIPs: - 35.1.2.3 metadata: platform: gcp hostname: talos region: us-central1 zone: us-central1-a instanceType: n1-standard-1 instanceId: "0" providerId: gce://123/us-central1-a/my-server tags: tag1: "" tag2: "" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/interfaces.json ================================================ [ { "accessConfigs": [ { "externalIp": "35.1.2.3", "type": "ONE_TO_ONE_NAT" } ], "dhcpv6Refresh": "2219726792944608985", "dnsServers": [ "169.254.169.254" ], "forwardedIps": [ "172.16.0.230" ], "gateway": "172.16.0.1", "gatewayIpv6": "fe80::4001:acff:fe10:1", "ip": "172.16.0.4", "ipAliases": [], "ipv6": [ "fd20:172:1610:7003:0:1::/96" ], "ipv6s": [ "fd20:172:1610:7003:0:1:0:0" ], "mac": "42:01:ac:10:00:04", "mtu": 1500, "network": "projects/123/networks/main", "subnetmask": "255.255.255.0", "targetInstanceIps": [] } ] ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/testdata/metadata.json ================================================ { "project-id": "123", "hostname": "talos", "id": "0", "zone": "us-central1-a", "name": "my-server", "machine-type": "n1-standard-1", "preempted": "FALSE", "tags": [ "tag1", "tag2" ] } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hcloud // MaybeBase64Decode is exported for testing. func MaybeBase64Decode(data []byte) []byte { return maybeBase64Decode(data) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package hcloud contains the Hcloud implementation of the [platform.Platform]. package hcloud import ( "context" "encoding/base64" "fmt" "log" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Hcloud is the concrete type that implements the runtime.Platform interface. type Hcloud struct{} // Name implements the runtime.Platform interface. func (h *Hcloud) Name() string { return "hcloud" } // ParseMetadata converts HCloud metadata to platform network configuration. // //nolint:gocyclo func (h *Hcloud) ParseMetadata(unmarshalledNetworkConfig *NetworkConfig, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var publicIPs []string if metadata.PublicIPv4 != "" { publicIPs = append(publicIPs, metadata.PublicIPv4) } for _, ntwrk := range unmarshalledNetworkConfig.Config { if ntwrk.Type != "physical" { continue } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: ntwrk.Interfaces, Up: true, ConfigLayer: network.ConfigPlatform, }) for _, subnet := range ntwrk.Subnets { if subnet.Type == "dhcp" && subnet.Ipv4 { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: ntwrk.Interfaces, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } if subnet.Type == "static" { ipAddr, err := netip.ParsePrefix(subnet.Address) if err != nil { return nil, err } family := nethelpers.FamilyInet4 if ipAddr.Addr().Is6() { publicIPs = append(publicIPs, strings.SplitN(subnet.Address, "/", 2)[0]) family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: ntwrk.Interfaces, Address: ipAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) } if subnet.Gateway != "" && subnet.Ipv6 { gw, err := netip.ParseAddr(subnet.Gateway) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: ntwrk.Interfaces, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: h.Name(), Hostname: metadata.Hostname, Region: metadata.Region, Zone: metadata.AvailabilityZone, InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("hcloud://%s", metadata.InstanceID), } return networkConfig, nil } // Configuration implements the runtime.Platform interface. func (h *Hcloud) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", HCloudUserDataEndpoint) configBytes, err := download.Download(ctx, HCloudUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if err != nil { return nil, err } // Try to parse the downloaded config bytes as base64 string, so that users can provide the config in base64 format. // This also allows users to gzip this data, since the calling code will try to un-gzip the data if it detects it. return maybeBase64Decode(configBytes), nil } // maybeBase64Decode tries to interpret the provided bytes as base64 string and decode them. // If the provided bytes are not a valid base64 string, the original bytes are returned. func maybeBase64Decode(data []byte) []byte { out, err := base64.StdEncoding.AppendDecode(nil, data) if err != nil { return data } return out } // Mode implements the runtime.Platform interface. func (h *Hcloud) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (h *Hcloud) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (h *Hcloud) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { metadata, err := h.getMetadata(ctx) if err != nil { return err } log.Printf("fetching hcloud network config from: %q", HCloudNetworkEndpoint) metadataNetworkConfig, err := download.Download(ctx, HCloudNetworkEndpoint) if err != nil { return fmt.Errorf("failed to fetch network config from metadata service: %w", err) } var unmarshalledNetworkConfig NetworkConfig if err = yaml.Unmarshal(metadataNetworkConfig, &unmarshalledNetworkConfig); err != nil { return err } if unmarshalledNetworkConfig.Version != 1 { return fmt.Errorf("network-config metadata version=%d is not supported", unmarshalledNetworkConfig.Version) } networkConfig, err := h.ParseMetadata(&unmarshalledNetworkConfig, metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/hcloud_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hcloud_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud" ) //go:embed testdata/metadata.yaml var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { h := &hcloud.Hcloud{} metadata := &hcloud.MetadataConfig{ Hostname: "talos.fqdn", PublicIPv4: "1.2.3.4", InstanceID: "0", Region: "hel1", AvailabilityZone: "hel1-dc2", } var m hcloud.NetworkConfig require.NoError(t, yaml.Unmarshal(rawMetadata, &m)) networkConfig, err := h.ParseMetadata(&m, metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } //go:embed testdata/userdata-plain.yaml var userdataPlain []byte //go:embed testdata/userdata-base64.txt var userdataBase64 []byte func TestParseUserdata(t *testing.T) { decodedUserdataPlain := hcloud.MaybeBase64Decode(userdataPlain) decodedUserdataBase64 := hcloud.MaybeBase64Decode(userdataBase64) assert.Equal(t, decodedUserdataPlain, decodedUserdataBase64) assert.Equal(t, userdataPlain, decodedUserdataBase64) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hcloud import ( "context" stderrors "errors" "fmt" "strings" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/download" ) const ( // HCloudMetadataEndpoint is the local HCloud metadata endpoint. HCloudMetadataEndpoint = "http://169.254.169.254/hetzner/v1/metadata" // HCloudNetworkEndpoint is the local HCloud metadata endpoint for the network-config. HCloudNetworkEndpoint = "http://169.254.169.254/hetzner/v1/metadata/network-config" // HCloudUserDataEndpoint is the local HCloud metadata endpoint for the config. HCloudUserDataEndpoint = "http://169.254.169.254/hetzner/v1/userdata" ) // MetadataConfig holds meta info. type MetadataConfig struct { Hostname string `yaml:"hostname,omitempty"` Region string `yaml:"region,omitempty"` AvailabilityZone string `yaml:"availability-zone,omitempty"` InstanceID string `yaml:"instance-id,omitempty"` PublicIPv4 string `yaml:"public-ipv4,omitempty"` } // NetworkConfig holds hcloud network-config info. type NetworkConfig struct { Version int `yaml:"version"` Config []struct { Mac string `yaml:"mac_address"` Interfaces string `yaml:"name"` Subnets []struct { NameServers []string `yaml:"dns_nameservers,omitempty"` Address string `yaml:"address,omitempty"` Gateway string `yaml:"gateway,omitempty"` Ipv4 bool `yaml:"ipv4,omitempty"` Ipv6 bool `yaml:"ipv6,omitempty"` Type string `yaml:"type"` } `yaml:"subnets"` Type string `yaml:"type"` } `yaml:"config"` } func (h *Hcloud) getMetadata(ctx context.Context) (metadata *MetadataConfig, err error) { getMetadataKey := func(key string) (string, error) { res, metaerr := download.Download(ctx, fmt.Sprintf("%s/%s", HCloudMetadataEndpoint, key), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if metaerr != nil && !stderrors.Is(metaerr, errors.ErrNoConfigSource) { return "", fmt.Errorf("failed to fetch %q from IMDS: %w", key, metaerr) } return string(res), nil } metadata = &MetadataConfig{} if metadata.Hostname, err = getMetadataKey("hostname"); err != nil { return nil, err } if metadata.InstanceID, err = getMetadataKey("instance-id"); err != nil { return nil, err } if metadata.AvailabilityZone, err = getMetadataKey("availability-zone"); err != nil { return nil, err } // Original CCM/CSI uses first part of availability-zone to define region name. // But metadata has different value. // We will follow official behavior. metadata.Region = strings.SplitN(metadata.AvailabilityZone, "-", 2)[0] if metadata.PublicIPv4, err = getMetadataKey("public-ipv4"); err != nil { return nil, err } return metadata, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/expected.yaml ================================================ addresses: - address: 2a01:4f8:1:2::1/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet6 dst: "" src: "" gateway: fe80::1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: [] timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: false dhcp4: routeMetric: 1024 layer: platform externalIPs: - 1.2.3.4 - 2a01:4f8:1:2::1 metadata: platform: hcloud hostname: talos.fqdn region: hel1 zone: hel1-dc2 instanceId: "0" providerId: hcloud://0 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/metadata.yaml ================================================ config: - mac_address: 96:00:00:1:2:3 name: eth0 subnets: - ipv4: true type: dhcp - address: 2a01:4f8:1:2::1/64 gateway: fe80::1 ipv6: true type: static type: physical - address: - 185.12.64.2 - 185.12.64.1 interface: eth0 type: nameserver version: 1 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/userdata-base64.txt ================================================ dmVyc2lvbjogdjFhbHBoYTEKZGVidWc6IGZhbHNlCnBlcnNpc3Q6IHRydWUKbWFjaGluZToKICB0eXBlOiBjb250cm9scGxhbmUKICBjZXJ0U0FOczoKICAgIC0gMTAuMC4xLjEwMQogICAgLSAxMC4wLjEuMTAwCiAga3ViZWxldDoKICAgIGltYWdlOiBnaGNyLmlvL3NpZGVyb2xhYnMva3ViZWxldDp2MS4zMS4xCiAgICBleHRyYUFyZ3M6CiAgICAgIGNsb3VkLXByb3ZpZGVyOiBleHRlcm5hbAogICAgICByb3RhdGUtc2VydmVyLWNlcnRpZmljYXRlczogInRydWUiCiAgICBkZWZhdWx0UnVudGltZVNlY2NvbXBQcm9maWxlRW5hYmxlZDogdHJ1ZQogICAgbm9kZUlQOgogICAgICB2YWxpZFN1Ym5ldHM6CiAgICAgICAgLSAxMC4wLjEuMC8yNAogICAgZGlzYWJsZU1hbmlmZXN0c0RpcmVjdG9yeTogdHJ1ZQogIG5ldHdvcms6CiAgICBob3N0bmFtZTogY29udHJvbHBsYW5lLTAwMQogICAgaW50ZXJmYWNlczoKICAgICAgLSBpbnRlcmZhY2U6IGV0aDAKICAgICAgICBkaGNwOiB0cnVlCiAgICBrdWJlc3BhbjoKICAgICAgZW5hYmxlZDogZmFsc2UKICBpbnN0YWxsOgogICAgZGlzazogL2Rldi9zZGEKICAgIGV4dHJhS2VybmVsQXJnczoKICAgICAgLSBpcHY2LmRpc2FibGU9MQogICAgaW1hZ2U6IGdoY3IuaW8vc2lkZXJvbGFicy9pbnN0YWxsZXI6djEuOC4wCiAgICB3aXBlOiBmYWxzZQogIHN5c2N0bHM6CiAgICBuZXQuY29yZS5uZXRkZXZfbWF4X2JhY2tsb2c6ICI0MDk2IgogICAgbmV0LmNvcmUuc29tYXhjb25uOiAiNjU1MzUiCiAgZmVhdHVyZXM6CiAgICByYmFjOiB0cnVlCiAgICBzdGFibGVIb3N0bmFtZTogdHJ1ZQogICAga3ViZXJuZXRlc1RhbG9zQVBJQWNjZXNzOgogICAgICBlbmFibGVkOiB0cnVlCiAgICAgIGFsbG93ZWRSb2xlczoKICAgICAgICAtIG9zOnJlYWRlcgogICAgICBhbGxvd2VkS3ViZXJuZXRlc05hbWVzcGFjZXM6CiAgICAgICAgLSBrdWJlLXN5c3RlbQogICAgYXBpZENoZWNrRXh0S2V5VXNhZ2U6IHRydWUKICAgIGRpc2tRdW90YVN1cHBvcnQ6IHRydWUKICAgIGt1YmVQcmlzbToKICAgICAgZW5hYmxlZDogdHJ1ZQogICAgICBwb3J0OiA3NDQ1CiAgICBob3N0RE5TOgogICAgICBlbmFibGVkOiB0cnVlCiAgICAgIGZvcndhcmRLdWJlRE5TVG9Ib3N0OiB0cnVlCiAgICAgIHJlc29sdmVNZW1iZXJOYW1lczogdHJ1ZQogIGtlcm5lbDoge30KICBub2RlTGFiZWxzOgogICAgbm9kZS5rdWJlcm5ldGVzLmlvL2V4Y2x1ZGUtZnJvbS1leHRlcm5hbC1sb2FkLWJhbGFuY2VyczogIiIKY2x1c3RlcjoKICBjb250cm9sUGxhbmU6CiAgICBlbmRwb2ludDogaHR0cHM6Ly8xMC4wLjEuMTAwOjY0NDMKICBjbHVzdGVyTmFtZTogdGVzdC1jbHVzdGVyCiAgbmV0d29yazoKICAgIGNuaToKICAgICAgbmFtZTogbm9uZQogICAgZG5zRG9tYWluOiBjbHVzdGVyLmxvY2FsCiAgICBwb2RTdWJuZXRzOgogICAgICAtIDEwLjAuMTYuMC8yMAogICAgc2VydmljZVN1Ym5ldHM6CiAgICAgIC0gMTAuMC44LjAvMjEKICBhcGlTZXJ2ZXI6CiAgICBpbWFnZTogcmVnaXN0cnkuazhzLmlvL2t1YmUtYXBpc2VydmVyOnYxLjMxLjEKICAgIGNlcnRTQU5zOgogICAgICAtIDEwLjAuMS4xMDAKICAgICAgLSAxMC4wLjEuMTAxCiAgICAgIC0gMTAuMC4xLjEwMAogICAgZGlzYWJsZVBvZFNlY3VyaXR5UG9saWN5OiB0cnVlCiAgICBhZG1pc3Npb25Db250cm9sOgogICAgICAtIG5hbWU6IFBvZFNlY3VyaXR5CiAgICAgICAgY29uZmlndXJhdGlvbjoKICAgICAgICAgIGFwaVZlcnNpb246IHBvZC1zZWN1cml0eS5hZG1pc3Npb24uY29uZmlnLms4cy5pby92MWFscGhhMQogICAgICAgICAgZGVmYXVsdHM6CiAgICAgICAgICAgIGF1ZGl0OiByZXN0cmljdGVkCiAgICAgICAgICAgIGF1ZGl0LXZlcnNpb246IGxhdGVzdAogICAgICAgICAgICBlbmZvcmNlOiBiYXNlbGluZQogICAgICAgICAgICBlbmZvcmNlLXZlcnNpb246IGxhdGVzdAogICAgICAgICAgICB3YXJuOiByZXN0cmljdGVkCiAgICAgICAgICAgIHdhcm4tdmVyc2lvbjogbGF0ZXN0CiAgICAgICAgICBleGVtcHRpb25zOgogICAgICAgICAgICBuYW1lc3BhY2VzOgogICAgICAgICAgICAgIC0ga3ViZS1zeXN0ZW0KICAgICAgICAgICAgcnVudGltZUNsYXNzZXM6IFtdCiAgICAgICAgICAgIHVzZXJuYW1lczogW10KICAgICAgICAgIGtpbmQ6IFBvZFNlY3VyaXR5Q29uZmlndXJhdGlvbgogICAgYXVkaXRQb2xpY3k6CiAgICAgIGFwaVZlcnNpb246IGF1ZGl0Lms4cy5pby92MQogICAgICBraW5kOiBQb2xpY3kKICAgICAgcnVsZXM6CiAgICAgICAgLSBsZXZlbDogTWV0YWRhdGEKICBjb250cm9sbGVyTWFuYWdlcjoKICAgIGltYWdlOiByZWdpc3RyeS5rOHMuaW8va3ViZS1jb250cm9sbGVyLW1hbmFnZXI6djEuMzEuMQogICAgZXh0cmFBcmdzOgogICAgICBiaW5kLWFkZHJlc3M6IDAuMC4wLjAKICAgICAgY2xvdWQtcHJvdmlkZXI6IGV4dGVybmFsCiAgICAgIG5vZGUtY2lkci1tYXNrLXNpemUtaXB2NDogIjI0IgogIHByb3h5OgogICAgZGlzYWJsZWQ6IHRydWUKICAgIGltYWdlOiByZWdpc3RyeS5rOHMuaW8va3ViZS1wcm94eTp2MS4zMS4xCiAgc2NoZWR1bGVyOgogICAgaW1hZ2U6IHJlZ2lzdHJ5Lms4cy5pby9rdWJlLXNjaGVkdWxlcjp2MS4zMS4xCiAgICBleHRyYUFyZ3M6CiAgICAgIGJpbmQtYWRkcmVzczogMC4wLjAuMAogIGRpc2NvdmVyeToKICAgIGVuYWJsZWQ6IHRydWUKICAgIHJlZ2lzdHJpZXM6CiAgICAgIGt1YmVybmV0ZXM6CiAgICAgICAgZGlzYWJsZWQ6IHRydWUKICAgICAgc2VydmljZToge30KICBldGNkOgogICAgZXh0cmFBcmdzOgogICAgICBsaXN0ZW4tbWV0cmljcy11cmxzOiBodHRwOi8vMC4wLjAuMDoyMzgxCiAgICBhZHZlcnRpc2VkU3VibmV0czoKICAgICAgLSAxMC4wLjEuMC8yNAogIGNvcmVETlM6CiAgICBkaXNhYmxlZDogZmFsc2UKICBleHRlcm5hbENsb3VkUHJvdmlkZXI6CiAgICBlbmFibGVkOiB0cnVlCg== ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud/testdata/userdata-plain.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane certSANs: - 10.0.1.101 - 10.0.1.100 kubelet: image: ghcr.io/siderolabs/kubelet:v1.31.1 extraArgs: cloud-provider: external rotate-server-certificates: "true" defaultRuntimeSeccompProfileEnabled: true nodeIP: validSubnets: - 10.0.1.0/24 disableManifestsDirectory: true network: hostname: controlplane-001 interfaces: - interface: eth0 dhcp: true kubespan: enabled: false install: disk: /dev/sda extraKernelArgs: - ipv6.disable=1 image: ghcr.io/siderolabs/installer:v1.8.0 wipe: false sysctls: net.core.netdev_max_backlog: "4096" net.core.somaxconn: "65535" features: rbac: true stableHostname: true kubernetesTalosAPIAccess: enabled: true allowedRoles: - os:reader allowedKubernetesNamespaces: - kube-system apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true resolveMemberNames: true kernel: {} nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: controlPlane: endpoint: https://10.0.1.100:6443 clusterName: test-cluster network: cni: name: none dnsDomain: cluster.local podSubnets: - 10.0.16.0/20 serviceSubnets: - 10.0.8.0/21 apiServer: image: registry.k8s.io/kube-apiserver:v1.31.1 certSANs: - 10.0.1.100 - 10.0.1.101 - 10.0.1.100 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.31.1 extraArgs: bind-address: 0.0.0.0 cloud-provider: external node-cidr-mask-size-ipv4: "24" proxy: disabled: true image: registry.k8s.io/kube-proxy:v1.31.1 scheduler: image: registry.k8s.io/kube-scheduler:v1.31.1 extraArgs: bind-address: 0.0.0.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: extraArgs: listen-metrics-urls: http://0.0.0.0:2381 advertisedSubnets: - 10.0.1.0/24 coreDNS: disabled: false externalCloudProvider: enabled: true ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/internal/address/address.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package address provides utility functions for address parsing. package address import ( "errors" "fmt" "net" "net/netip" "strconv" "strings" ) // IPPrefixFrom make netip.Prefix from cidr-address and netmask strings. // address can be IP or CIDR (1.1.1.1 or 1.1.1.1/8 or 1.1.1.1/255.0.0.0) // netmask can be IP or number (255.255.255.0 or 24 or empty). func IPPrefixFrom(address, netmask string) (netip.Prefix, error) { cidr := strings.SplitN(address, "/", 2) if len(cidr) == 1 { address = cidr[0] } else { address = cidr[0] netmask = cidr[1] } ip, err := netip.ParseAddr(address) if err != nil { return netip.Prefix{}, fmt.Errorf("failed to parse ip address: %w", err) } if netmask == "" { if ip.Is4() { netmask = "32" } else { netmask = "128" } } bits, err := strconv.Atoi(netmask) if err != nil { netmask, err := netip.ParseAddr(netmask) if err != nil { return netip.Prefix{}, fmt.Errorf("failed to parse netmask: %w", err) } mask, _ := netmask.MarshalBinary() //nolint:errcheck // never fails bits, _ = net.IPMask(mask).Size() } if ip.Is4() && bits > 32 { return netip.Prefix{}, errors.New("failed netmask should be the same address family") } return netip.PrefixFrom(ip, bits), nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils/blockutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package blockutils provides volume-related helpers for platform implementation. package blockutils import ( "context" "fmt" "io/fs" "log" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) // VolumeMatch returns a CEL expression that matches a volume by filesystem or partition label. func VolumeMatch(labels []string) (*cel.Expression, error) { builder := cel.NewBuilder(celenv.VolumeLocator()) // "(volume.label in ['%s', ...] || volume.partition_label in ['%s', ...]) && volume.name != ''" expr := builder.NewCall( builder.NextID(), operators.LogicalAnd, builder.NewCall( builder.NextID(), operators.LogicalOr, builder.NewCall( builder.NextID(), operators.In, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "label", ), builder.NewList( builder.NextID(), xslices.Map(labels, func(label string) ast.Expr { return builder.NewLiteral(builder.NextID(), types.String(label)) }), nil, ), ), builder.NewCall( builder.NextID(), operators.In, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "partition_label", ), builder.NewList( builder.NextID(), xslices.Map(labels, func(label string) ast.Expr { return builder.NewLiteral(builder.NextID(), types.String(label)) }), nil, ), ), ), builder.NewCall( builder.NextID(), operators.NotEquals, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "volume"), "name", ), builder.NewLiteral(builder.NextID(), types.String("")), ), ) boolExpr, err := builder.ToBooleanExpression(expr) if err != nil { return nil, fmt.Errorf("error creating boolean expression: %w", err) } return boolExpr, nil } // ReadFromVolume tries to find a volume with the given label, mounts it // as read-only, calls the provided function with xfs.Root and unmounts it. // // If the volume wasn't found, fs.ErrNotExist is returned. func ReadFromVolume(ctx context.Context, r state.State, labels []string, cb func(xfs.Root, *block.VolumeStatus) error) error { if len(labels) == 0 { panic("at least one label must be provided") } volumeID := "platform/" + labels[0] + "/config" matchExr, err := VolumeMatch(labels) if err != nil { return fmt.Errorf("error creating volume match expression: %w", err) } // create a volume which matches the expected filesystem label vc := block.NewVolumeConfig(block.NamespaceName, volumeID) vc.Metadata().Labels().Set(block.PlatformLabel, "") vc.TypedSpec().Type = block.VolumeTypePartition vc.TypedSpec().Locator = block.LocatorSpec{ Match: *matchExr, } if err := r.Create(ctx, vc); err != nil && !state.IsConflictError(err) { return fmt.Errorf("error creating user disk volume configuration: %w", err) } defer func() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := r.TeardownAndDestroy(ctx, vc.Metadata()); err != nil { log.Printf("error destroying volume config %s/%s: %v", vc.Metadata().Namespace(), vc.Metadata().ID(), err) } }() // wait for the volume to be either ready or missing (includes waiting for devices to be ready) volumeStatus, err := safe.StateWatchFor[*block.VolumeStatus](ctx, r, block.NewVolumeStatus(vc.Metadata().Namespace(), vc.Metadata().ID()).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { phase := r.(*block.VolumeStatus).TypedSpec().Phase return phase == block.VolumePhaseReady || phase == block.VolumePhaseMissing, nil }), ) if err != nil { return fmt.Errorf("failed to watch for volume status: %w", err) } if volumeStatus.TypedSpec().Phase == block.VolumePhaseMissing { return fmt.Errorf("failed to find volume with machine configuration %s: %w", vc.TypedSpec().Locator.Match, fs.ErrNotExist) } manager := mount.NewManager( mount.WithReadOnly(), mount.WithPrinter(log.Printf), mount.WithFsopen( volumeStatus.TypedSpec().Filesystem.String(), fsopen.WithSource(volumeStatus.TypedSpec().MountLocation), fsopen.WithBoolParameter("ro"), ), mount.WithDetached(), ) // mount the volume, unmount when done p, err := manager.Mount() if err != nil { return fmt.Errorf("failed to mount volume: %w", err) } defer manager.Unmount() //nolint:errcheck return cb(p.Root(), volumeStatus) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils/blockutils_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package blockutils_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestVolumeMatch(t *testing.T) { t.Parallel() expr, err := blockutils.VolumeMatch([]string{constants.MetalConfigISOLabel}) require.NoError(t, err) assert.Equal(t, `(volume.label in ["metal-iso"] || volume.partition_label in ["metal-iso"]) && volume.name != ""`, expr.String()) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils/netutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package netutils provides network-related helpers for platform implementation. package netutils import ( "context" "log" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Wait for the network to be ready to interact with platform metadata services. func Wait(ctx context.Context, r state.State) error { log.Printf("waiting for network to be ready") return network.NewReadyCondition(r, network.AddressReady).Wait(ctx) } // WaitForDevicesReady waits for devices to be ready. func WaitForDevicesReady(ctx context.Context, r state.State) error { log.Printf("waiting for devices to be ready...") return runtime.NewDevicesStatusCondition(r).Wait(ctx) } // RetryFetch retries fetching from metadata service. func RetryFetch(ctx context.Context, f func(ctx context.Context) (string, error)) (string, error) { var ( userdata string err error ) err = retry.Exponential( constants.ConfigLoadTimeout, retry.WithUnits(time.Second), retry.WithJitter(time.Second), retry.WithErrorLogging(true), ).RetryWithContext( ctx, func(ctx context.Context) error { userdata, err = f(ctx) return err }) if err != nil { return "", err } return userdata, err } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package metal contains the metal implementation of the [platform.Platform]. package metal import ( "context" stderrors "errors" "fmt" "io/fs" "log" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/channel" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/oauth2" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/xfs" ) // Metal is a discoverer for non-cloud environments. type Metal struct { IsAgent bool } // Name implements the platform.Platform interface. func (m *Metal) Name() string { return constants.PlatformMetal } // Configuration implements the platform.Platform interface. func (m *Metal) Configuration(ctx context.Context, r state.State) ([]byte, error) { var option *string if option = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); option == nil { return nil, errors.ErrNoConfigSource } if *option == constants.ConfigNone { return nil, errors.ErrNoConfigSource } getURL := func(ctx context.Context) (string, error) { // give a shorter timeout to populate the URL, leave the rest of the time to the actual download ctx, cancel := context.WithTimeout(ctx, constants.ConfigLoadAttemptTimeout/2) defer cancel() downloadEndpoint, err := url.Populate(ctx, *option, r) if err != nil { log.Printf("failed to populate talos.config fetch URL %q: %s", *option, err.Error()) } log.Printf("fetching machine config from: %q", downloadEndpoint) return downloadEndpoint, nil } switch *option { case constants.MetalConfigISOLabel: return readConfigFromISO(ctx, r) default: if err := netutils.Wait(ctx, r); err != nil { return nil, err } oauth2Cfg, err := oauth2.NewConfig(procfs.ProcCmdline(), *option) if err != nil && !stderrors.Is(err, fs.ErrNotExist) { return nil, fmt.Errorf("failed to parse OAuth2 config: %w", err) } var extraHeaders map[string]string // perform OAuth2 device auth flow first to acquire extra headers if oauth2Cfg != nil { if err = retry.Constant(constants.ConfigLoadTimeout, retry.WithUnits(30*time.Second)).RetryWithContext(ctx, func(ctx context.Context) error { return oauth2Cfg.DeviceAuthFlow(ctx, r) }); err != nil { return nil, fmt.Errorf("OAuth2 device auth flow failed: %w", err) } extraHeaders = oauth2Cfg.ExtraHeaders() } return download.Download( ctx, *option, download.WithEndpointFunc(getURL), download.WithTimeout(constants.ConfigLoadTimeout), download.WithRetryOptions( // give a timeout per attempt, max 50% of that is dedicated for URL interpolation, the rest is for the actual download retry.WithAttemptTimeout(constants.ConfigLoadAttemptTimeout), ), download.WithHeaders(extraHeaders), ) } } // Mode implements the platform.Platform interface. func (m *Metal) Mode() runtime.Mode { if m.IsAgent { return runtime.ModeMetalAgent } return runtime.ModeMetal } func readConfigFromISO(ctx context.Context, r state.State) ([]byte, error) { var b []byte err := blockutils.ReadFromVolume(ctx, r, []string{constants.MetalConfigISOLabel}, func(root xfs.Root, volumeStatus *block.VolumeStatus) error { var err error b, err = xfs.ReadFile(root, constants.ConfigFilename) if err != nil { return fmt.Errorf("read config: %w", err) } log.Printf("read machine config from volume: %s (filesystem %q, UUID %q, size %s)", volumeStatus.TypedSpec().Location, volumeStatus.TypedSpec().Filesystem, volumeStatus.TypedSpec().UUID, volumeStatus.TypedSpec().PrettySize, ) return nil }) return b, err } // KernelArgs implements the runtime.Platform interface. func (m *Metal) KernelArgs(arch string, quirks quirks.Quirks) procfs.Parameters { switch arch { case "amd64": if quirks.SupportsMetalPlatformConsoleTTYS0() { return procfs.Parameters{ procfs.NewParameter("console").Append("ttyS0").Append("tty0"), } } return procfs.Parameters{ procfs.NewParameter("console").Append("tty0"), } case "arm64": return procfs.Parameters{ procfs.NewParameter("console").Append("ttyAMA0").Append("tty0"), } default: return nil } } // NetworkConfiguration implements the runtime.Platform interface. // //nolint:gocyclo func (m *Metal) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { ctx, cancel := context.WithCancel(ctx) defer cancel() watchCh := make(chan state.Event) if err := st.Watch(ctx, hardware.NewSystemInformation(hardware.SystemInformationID).Metadata(), watchCh); err != nil { return err } if err := st.Watch(ctx, runtimeres.NewMetaKey(runtimeres.NamespaceName, runtimeres.MetaKeyTagToID(meta.MetalNetworkPlatformConfig)).Metadata(), watchCh); err != nil { return err } // network config from META partition var metaCfg runtime.PlatformNetworkConfig // fixed metadata filled by this function metadata := &runtimeres.PlatformMetadataSpec{} metadata.Platform = m.Name() if option := procfs.ProcCmdline().Get(constants.KernelParamHostname).First(); option != nil { metadata.Hostname = *option } for { var event state.Event select { case <-ctx.Done(): return ctx.Err() case event = <-watchCh: } switch event.Type { case state.Errored: return fmt.Errorf("watch failed: %w", event.Error) case state.Bootstrapped, state.Noop: // ignored, should not happen case state.Created, state.Updated: switch r := event.Resource.(type) { case *hardware.SystemInformation: metadata.InstanceID = r.TypedSpec().UUID case *runtimeres.MetaKey: metaCfg = runtime.PlatformNetworkConfig{} if err := yaml.Unmarshal([]byte(r.TypedSpec().Value), &metaCfg); err != nil { return fmt.Errorf("failed to unmarshal metal network config from META: %w", err) } } case state.Destroyed: switch event.Resource.(type) { case *hardware.SystemInformation: metadata.InstanceID = "" case *runtimeres.MetaKey: metaCfg = runtime.PlatformNetworkConfig{} } } cfg := metaCfg cfg.Metadata = new(metadata.DeepCopy()) if !channel.SendWithContext(ctx, ch, &cfg) { return ctx.Err() } } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/metal_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package metal_test import ( "context" "fmt" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) //nolint:gocyclo func TestNetworkConfig(t *testing.T) { ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) t.Cleanup(cancel) p := &metal.Metal{} ch := make(chan *runtime.PlatformNetworkConfig, 1) st := state.WrapCore(namespaced.NewState(inmem.Build)) uuid := hardware.NewSystemInformation(hardware.SystemInformationID) uuid.TypedSpec().UUID = "0123-4567-89ab-cdef" require.NoError(t, st.Create(ctx, uuid)) errCh := make(chan error) go func() { errCh <- p.NetworkConfiguration(ctx, st, ch) }() // platform might see updates coming in different order, so we need to wait a bit for the final state outerLoop: for { select { case <-ctx.Done(): require.FailNow(t, "timed out waiting for network config") case cfg := <-ch: assert.Equal(t, constants.PlatformMetal, cfg.Metadata.Platform) if cfg.Metadata.InstanceID == "" { continue } assert.Equal(t, uuid.TypedSpec().UUID, cfg.Metadata.InstanceID) break outerLoop } } metaKey := runtimeres.NewMetaKey(runtimeres.NamespaceName, runtimeres.MetaKeyTagToID(meta.MetalNetworkPlatformConfig)) metaKey.TypedSpec().Value = `{"externalIPs": ["1.2.3.4"]}` require.NoError(t, st.Create(ctx, metaKey)) // platform might see updates coming in different order, so we need to wait a bit for the final state outerLoop2: for { select { case <-ctx.Done(): require.FailNow(t, "timed out waiting for network config") case cfg := <-ch: assert.Equal(t, constants.PlatformMetal, cfg.Metadata.Platform) assert.Equal(t, uuid.TypedSpec().UUID, cfg.Metadata.InstanceID) if len(cfg.ExternalIPs) == 0 { continue } assert.Equal(t, "[1.2.3.4]", fmt.Sprintf("%v", cfg.ExternalIPs)) break outerLoop2 } } metaKey.TypedSpec().Value = `{"hostnames": [{"hostname": "talos", "domainname": "fqdn", "layer": "platform"}]}` require.NoError(t, st.Update(ctx, metaKey)) select { case <-ctx.Done(): require.FailNow(t, "timed out waiting for network config") case cfg := <-ch: assert.Equal(t, constants.PlatformMetal, cfg.Metadata.Platform) assert.Equal(t, uuid.TypedSpec().UUID, cfg.Metadata.InstanceID) assert.Equal(t, "[]", fmt.Sprintf("%v", cfg.ExternalIPs)) assert.Equal(t, "[{talos fqdn platform}]", fmt.Sprintf("%v", cfg.Hostnames)) } require.NoError(t, st.Destroy(ctx, metaKey.Metadata())) select { case <-ctx.Done(): require.FailNow(t, "timed out waiting for network config") case cfg := <-ch: assert.Equal(t, constants.PlatformMetal, cfg.Metadata.Platform) assert.Equal(t, uuid.TypedSpec().UUID, cfg.Metadata.InstanceID) assert.Equal(t, "[]", fmt.Sprintf("%v", cfg.ExternalIPs)) assert.Equal(t, "[]", fmt.Sprintf("%v", cfg.Hostnames)) } cancel() require.ErrorIs(t, <-errCh, context.Canceled) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/oauth2/oauth2.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package oauth2 implements OAuth2 Device Flow to authenticate machine config download. package oauth2 import ( "bytes" "context" "fmt" "log" "net/http" "net/url" "os" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-cleanhttp" "github.com/mdp/qrterminal/v3" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/oauth2" metalurl "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Config represents the OAuth2 configuration. type Config struct { ClientID string ClientSecret string Audience string Scopes []string ExtraVariables []string DeviceAuthURL string TokenURL string extraHeaders map[string]string } // NewConfig returns a new Config from cmdline. // // If OAuth2 is not configured, it returns os.ErrNotExist. // //nolint:gocyclo func NewConfig(cmdline *procfs.Cmdline, downloadURL string) (*Config, error) { var cfg Config clientID := cmdline.Get(constants.KernelParamConfigOAuthClientID).First() if clientID == nil { return nil, os.ErrNotExist } cfg.ClientID = *clientID if clientSecret := cmdline.Get(constants.KernelParamConfigOAuthClientSecret).First(); clientSecret != nil { cfg.ClientSecret = *clientSecret } if audience := cmdline.Get(constants.KernelParamConfigOAuthAudience).First(); audience != nil { cfg.Audience = *audience } for i := 0; ; i++ { scope := cmdline.Get(constants.KernelParamConfigOAuthScope).Get(i) if scope == nil { break } cfg.Scopes = append(cfg.Scopes, *scope) } for i := 0; ; i++ { extra := cmdline.Get(constants.KernelParamConfigOAuthExtraVariable).Get(i) if extra == nil { break } cfg.ExtraVariables = append(cfg.ExtraVariables, *extra) } if deviceAuthURL := cmdline.Get(constants.KernelParamConfigOAuthDeviceAuthURL).First(); deviceAuthURL != nil { cfg.DeviceAuthURL = *deviceAuthURL } else { u, err := url.Parse(downloadURL) if err != nil { return nil, err } u.Path = "/device/code" cfg.DeviceAuthURL = u.String() } if tokenURL := cmdline.Get(constants.KernelParamConfigOAuthTokenURL).First(); tokenURL != nil { cfg.TokenURL = *tokenURL } else { u, err := url.Parse(downloadURL) if err != nil { return nil, err } u.Path = "/token" cfg.TokenURL = u.String() } return &cfg, nil } // DeviceAuthFlow represents the device auth flow response. func (c *Config) DeviceAuthFlow(ctx context.Context, st state.State) error { transport := httpdefaults.PatchTransport(cleanhttp.DefaultTransport()) client := &http.Client{ Transport: transport, } // register the HTTP client with OAuth2 flow ctx = context.WithValue(ctx, oauth2.HTTPClient, client) cfg := oauth2.Config{ ClientID: c.ClientID, Scopes: c.Scopes, Endpoint: oauth2.Endpoint{ DeviceAuthURL: c.DeviceAuthURL, TokenURL: c.TokenURL, }, } log.Printf("[OAuth] starting the authentication device flow with the following settings:") log.Printf("[OAuth] - client ID: %q", c.ClientID) log.Printf("[OAuth] - device auth URL: %q", c.DeviceAuthURL) log.Printf("[OAuth] - token URL: %q", c.TokenURL) log.Printf("[OAuth] - extra variables: %q", c.ExtraVariables) // acquire device variables variables, err := c.getVariableValues(ctx, st) if err != nil { return fmt.Errorf("failed to get variable values: %w", err) } var deviceAuthOptions []oauth2.AuthCodeOption //nolint:prealloc if c.Audience != "" { deviceAuthOptions = append(deviceAuthOptions, oauth2.SetAuthURLParam("audience", c.Audience)) } for k, v := range variables { deviceAuthOptions = append(deviceAuthOptions, oauth2.SetAuthURLParam(k, v)) } deviceAuthResponse, err := cfg.DeviceAuth(ctx, deviceAuthOptions...) if err != nil { return fmt.Errorf("failed to get device auth response: %w", err) } log.Printf("[OAuth] please visit the URL %s and enter the code %s", deviceAuthResponse.VerificationURI, deviceAuthResponse.UserCode) if deviceAuthResponse.VerificationURIComplete != "" { var qrBuf bytes.Buffer qrterminal.GenerateHalfBlock(deviceAuthResponse.VerificationURIComplete, qrterminal.L, &qrBuf) log.Printf("[OAuth] or scan the following QR code:\n%s", qrBuf.String()) } log.Printf("[OAuth] waiting for the device to be authorized (expires at %s)...", deviceAuthResponse.Expiry.Format("15:04:05")) if c.ClientSecret != "" { deviceAuthOptions = append(deviceAuthOptions, oauth2.SetAuthURLParam("client_secret", c.ClientSecret)) } token, err := cfg.DeviceAccessToken(ctx, deviceAuthResponse, deviceAuthOptions...) if err != nil { return fmt.Errorf("failed to get device access token: %w", err) } log.Printf("[OAuth] device authorized successfully") c.extraHeaders = map[string]string{ "Authorization": token.Type() + " " + token.AccessToken, } return nil } // getVariableValues returns the variable values to include in the device auth request. func (c *Config) getVariableValues(ctx context.Context, st state.State) (map[string]string, error) { ctx, cancel := context.WithTimeout(ctx, constants.ConfigLoadAttemptTimeout/2) defer cancel() return metalurl.MapValues(ctx, st, c.ExtraVariables) } // ExtraHeaders returns the extra headers to include in the download request. func (c *Config) ExtraHeaders() map[string]string { return c.extraHeaders } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/oauth2/oauth2_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package oauth2_test import ( "context" "errors" "io/fs" "net/http" "net/http/httptest" "testing" "time" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/oauth2" ) func TestNewConfig(t *testing.T) { //nolint:tparallel t.Parallel() for _, test := range []struct { name string cmdline string expected *oauth2.Config }{ { name: "no config", }, { name: "only client ID", cmdline: `talos.config.oauth.client_id=device_client_id`, expected: &oauth2.Config{ ClientID: "device_client_id", TokenURL: "https://example.com/token", DeviceAuthURL: "https://example.com/device/code", }, }, { name: "client ID and custom URLs", cmdline: `talos.config.oauth.client_id=device_client_id talos.config.oauth.token_url=https://google.com/token talos.config.oauth.device_auth_url=https://google.com/device/code`, expected: &oauth2.Config{ ClientID: "device_client_id", TokenURL: "https://google.com/token", DeviceAuthURL: "https://google.com/device/code", }, }, { name: "complete config", cmdline: `talos.config.oauth.client_id=device_client_id talos.config.oauth.client_secret=device_secret ` + `talos.config.oauth.token_url=https://google.com/token talos.config.oauth.device_auth_url=https://google.com/device/code ` + `talos.config.oauth.scope=foo talos.config.oauth.scope=bar talos.config.oauth.audience=world ` + `talos.config.oauth.extra_variable=uuid talos.config.oauth.extra_variable=mac`, expected: &oauth2.Config{ ClientID: "device_client_id", ClientSecret: "device_secret", Audience: "world", Scopes: []string{"foo", "bar"}, ExtraVariables: []string{"uuid", "mac"}, TokenURL: "https://google.com/token", DeviceAuthURL: "https://google.com/device/code", }, }, } { t.Run(test.name, func(t *testing.T) { cfg, err := oauth2.NewConfig(procfs.NewCmdline(test.cmdline), "https://example.com/my/config") if test.expected == nil { require.Error(t, err) assert.True(t, errors.Is(err, fs.ErrNotExist)) return } require.NoError(t, err) assert.Equal(t, test.expected, cfg) }) } } func TestDeviceAuthFlow(t *testing.T) { t.Parallel() cfg := &oauth2.Config{ ClientID: "device_client_id", } ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() //nolint:errcheck t.Logf("received request: %s %s", r.Method, r.RequestURI) switch r.Method + r.RequestURI { case "POST/device/code": w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{"device_code":"abcd", "user_code":"1234", "verification_uri":"https://example.com/verify","verification_uri_complete":"https://example.com/verify/1234","interval":1,"expires_in":36000}`)) //nolint:errcheck,lll case "POST/token": w.Header().Add("Content-Type", "application/json") w.Write([]byte(`{"access_token":"abcd","token_type":"bearer","expires_in":3600,"refresh_token":"efgh","id_token":"ijkl"}`)) //nolint:errcheck default: w.WriteHeader(http.StatusNotFound) } })) t.Cleanup(ts.Close) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) t.Cleanup(cancel) cfg.DeviceAuthURL = ts.URL + "/device/code" cfg.TokenURL = ts.URL + "/token" require.NoError(t, cfg.DeviceAuthFlow(ctx, nil)) assert.Equal(t, map[string]string{"Authorization": "Bearer abcd"}, cfg.ExtraHeaders()) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/map.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url import ( "context" "fmt" "log" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" ) // MapValues maps variable names to values. // //nolint:gocyclo func MapValues(ctx context.Context, st state.State, variableNames []string) (map[string]string, error) { // happy case if len(variableNames) == 0 { return nil, nil } availableVariables := AllVariables() activeVariables := make(map[string]*Variable, len(variableNames)) for _, variableName := range variableNames { if v, ok := availableVariables[variableName]; ok { activeVariables[variableName] = v } else { return nil, fmt.Errorf("unsupported variable name: %q", variableName) } } // setup watches ctx, cancel := context.WithCancel(ctx) defer cancel() watchCh := make(chan state.Event) for _, variable := range activeVariables { if err := variable.Value.RegisterWatch(ctx, st, watchCh); err != nil { return nil, fmt.Errorf("error watching variable %q: %w", variable.Key, err) } } pendingVariables := xslices.ToSet(maps.Values(activeVariables)) // wait for all variables to be populated waitLoop: for len(pendingVariables) > 0 { log.Printf("waiting for variables: %v", xslices.Map(maps.Keys(pendingVariables), func(v *Variable) string { return v.Key })) var ev state.Event select { case <-ctx.Done(): // context was canceled, return what we have break waitLoop case ev = <-watchCh: } switch ev.Type { case state.Errored: return nil, fmt.Errorf("error watching variables: %w", ev.Error) case state.Bootstrapped, state.Noop: // ignored case state.Created, state.Updated, state.Destroyed: for _, variable := range activeVariables { handled, err := variable.Value.EventHandler(ev) if err != nil { return nil, fmt.Errorf("error handling variable %q: %w", variable.Key, err) } if handled { delete(pendingVariables, variable) } } } } return maps.Map(activeVariables, func(k string, v *Variable) (string, string) { return k, v.Value.Get() }), nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/map_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url_test import ( "context" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" ) func TestMapValues(t *testing.T) { t.Parallel() for _, test := range []struct { name string variableNames []string preSetup []setupFunc parallelSetup []setupFunc expected map[string]string }{ { name: "no variables", }, { name: "multiple variables", variableNames: []string{"uuid", "mac", "hostname", "code"}, expected: map[string]string{ "code": "top-secret", "hostname": "some-node", "mac": "12:34:56:78:90:ce", "uuid": "0000-0000", }, preSetup: []setupFunc{ createSysInfo("0000-0000", "12345"), createMac("12:34:56:78:90:ce"), createHostname("some-node"), createCode("top-secret"), }, }, { name: "mixed wait variables", variableNames: []string{"uuid", "mac", "hostname", "code"}, expected: map[string]string{ "code": "", "hostname": "another-node", "mac": "12:34:56:78:90:ab", "uuid": "0000-1234", }, preSetup: []setupFunc{ createSysInfo("0000-1234", "12345"), createMac("12:34:56:78:90:ab"), createHostname("example-node"), }, parallelSetup: []setupFunc{ sleep(time.Second), updateHostname("another-node"), sleep(time.Second / 2), }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() st := state.WrapCore(namespaced.NewState(inmem.Build)) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() for _, f := range test.preSetup { f(ctx, t, st) } errCh := make(chan error) var result map[string]string go func() { var e error result, e = url.MapValues(ctx, st, test.variableNames) errCh <- e }() for _, f := range test.parallelSetup { f(ctx, t, st) } err := <-errCh require.NoError(t, err) assert.Equal(t, test.expected, result) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/url.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package url handles expansion of the download URL for the config. package url import ( "context" "fmt" "log" "net/url" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" ) // Populate populates the config download URL with values replacing variables. func Populate(ctx context.Context, downloadURL string, st state.State) (string, error) { return PopulateVariables(ctx, downloadURL, st, maps.Values(AllVariables())) } // PopulateVariables populates the config download URL with values replacing variables. // //nolint:gocyclo func PopulateVariables(ctx context.Context, downloadURL string, st state.State, variables []*Variable) (string, error) { u, err := url.Parse(downloadURL) if err != nil { return "", fmt.Errorf("failed to parse URL: %w", err) } query := u.Query() var activeVariables []*Variable for _, variable := range variables { if variable.Matches(query) { activeVariables = append(activeVariables, variable) } } // happy path: no variables if len(activeVariables) == 0 { return downloadURL, nil } // setup watches ctx, cancel := context.WithCancel(ctx) defer cancel() watchCh := make(chan state.Event) for _, variable := range activeVariables { if err = variable.Value.RegisterWatch(ctx, st, watchCh); err != nil { return "", fmt.Errorf("error watching variable %q: %w", variable.Key, err) } } pendingVariables := xslices.ToSet(activeVariables) // wait for all variables to be populated for len(pendingVariables) > 0 { log.Printf("waiting for URL variables: %v", xslices.Map(maps.Keys(pendingVariables), func(v *Variable) string { return v.Key })) var ev state.Event select { case <-ctx.Done(): // context was canceled, return the URL as is u.RawQuery = query.Encode() return u.String(), ctx.Err() case ev = <-watchCh: } switch ev.Type { case state.Errored: return "", fmt.Errorf("error watching variables: %w", ev.Error) case state.Bootstrapped, state.Noop: // ignored case state.Created, state.Updated, state.Destroyed: anyHandled := false for _, variable := range activeVariables { handled, err := variable.Value.EventHandler(ev) if err != nil { return "", fmt.Errorf("error handling variable %q: %w", variable.Key, err) } if handled { delete(pendingVariables, variable) anyHandled = true } } if !anyHandled { continue } // perform another round of replacing query = u.Query() for _, variable := range activeVariables { if _, pending := pendingVariables[variable]; pending { continue } variable.Replace(query) } } } u.RawQuery = query.Encode() return u.String(), nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/url_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url_test import ( "context" "net" "testing" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) type setupFunc func(context.Context, *testing.T, state.State) func TestPopulate(t *testing.T) { t.Parallel() for _, test := range []struct { name string url string preSetup []setupFunc parallelSetup []setupFunc expected string }{ { name: "no variables", url: "https://example.com?foo=bar", expected: "https://example.com?foo=bar", }, { name: "legacy UUID", url: "https://example.com?uuid=", expected: "https://example.com?uuid=0000-0000", preSetup: []setupFunc{ createSysInfo("0000-0000", ""), }, }, { name: "sys info", url: "https://example.com?uuid=${uuid}&no=${serial}", expected: "https://example.com?no=12345&uuid=0000-0000", preSetup: []setupFunc{ createSysInfo("0000-0000", "12345"), }, }, { name: "multiple variables", url: "https://example.com?uuid=${uuid}&mac=${mac}&hostname=${hostname}&code=${code}", expected: "https://example.com?code=top-secret&hostname=example-node&mac=12%3A34%3A56%3A78%3A90%3Aab&uuid=0000-0000", preSetup: []setupFunc{ createSysInfo("0000-0000", "12345"), createMac("12:34:56:78:90:ab"), createHostname("example-node"), createCode("top-secret"), }, }, { name: "mixed wait variables", url: "https://example.com?uuid=${uuid}&mac=${mac}&hostname=${hostname}&code=${code}", expected: "https://example.com?code=top-secret&hostname=another-node&mac=12%3A34%3A56%3A78%3A90%3Aab&uuid=0000-1234", preSetup: []setupFunc{ createSysInfo("0000-1234", "12345"), createMac("12:34:56:78:90:ab"), createHostname("example-node"), }, parallelSetup: []setupFunc{ sleep(time.Second), updateHostname("another-node"), sleep(time.Second), createCode("top-secret"), }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() st := state.WrapCore(namespaced.NewState(inmem.Build)) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() for _, f := range test.preSetup { f(ctx, t, st) } errCh := make(chan error) var result string go func() { var e error result, e = url.Populate(ctx, test.url, st) errCh <- e }() for _, f := range test.parallelSetup { f(ctx, t, st) } err := <-errCh require.NoError(t, err) assert.Equal(t, test.expected, result) }) } } func createSysInfo(uuid, serial string) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { sysInfo := hardware.NewSystemInformation(hardware.SystemInformationID) sysInfo.TypedSpec().UUID = uuid sysInfo.TypedSpec().SerialNumber = serial require.NoError(t, st.Create(ctx, sysInfo)) } } func createMac(mac string) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { addr, err := net.ParseMAC(mac) require.NoError(t, err) hwAddr := network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr) hwAddr.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(addr) require.NoError(t, st.Create(ctx, hwAddr)) } } func createHostname(hostname string) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { hn := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hn.TypedSpec().Hostname = hostname require.NoError(t, st.Create(ctx, hn)) } } func updateHostname(hostname string) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { hn, err := safe.StateGet[*network.HostnameStatus](ctx, st, network.NewHostnameStatus(network.NamespaceName, network.HostnameID).Metadata()) require.NoError(t, err) hn.TypedSpec().Hostname = hostname require.NoError(t, st.Update(ctx, hn)) } } func createCode(code string) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { mk := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.DownloadURLCode)) mk.TypedSpec().Value = code require.NoError(t, st.Create(ctx, mk)) } } func sleep(d time.Duration) setupFunc { return func(ctx context.Context, t *testing.T, st state.State) { time.Sleep(d) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/value.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url import ( "context" "sync" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Value of a variable. type Value interface { // Get the value. Get() string // RegisterWatch handles registering a watch for the variable. RegisterWatch(ctx context.Context, st state.State, ch chan<- state.Event) error // EventHandler is called for each watch event, returns when the variable value is ready. EventHandler(event state.Event) (bool, error) } type value struct { mu sync.Mutex val string registerWatch func(ctx context.Context, st state.State, ch chan<- state.Event) error eventHandler func(event state.Event) (string, error) } func (v *value) Get() string { v.mu.Lock() defer v.mu.Unlock() return v.val } func (v *value) RegisterWatch(ctx context.Context, st state.State, ch chan<- state.Event) error { return v.registerWatch(ctx, st, ch) } func (v *value) EventHandler(event state.Event) (bool, error) { val, err := v.eventHandler(event) if err != nil { return false, err } if val == "" { return false, nil } v.mu.Lock() v.val = val v.mu.Unlock() return true, nil } // UUIDValue is a value for UUID variable. func UUIDValue() Value { return &value{ registerWatch: func(ctx context.Context, st state.State, ch chan<- state.Event) error { return st.Watch(ctx, hardware.NewSystemInformation(hardware.SystemInformationID).Metadata(), ch) }, eventHandler: func(event state.Event) (string, error) { sysInfo, ok := event.Resource.(*hardware.SystemInformation) if !ok { return "", nil } return sysInfo.TypedSpec().UUID, nil }, } } // SerialNumberValue is a value for SerialNumber variable. func SerialNumberValue() Value { return &value{ registerWatch: func(ctx context.Context, st state.State, ch chan<- state.Event) error { return st.Watch(ctx, hardware.NewSystemInformation(hardware.SystemInformationID).Metadata(), ch) }, eventHandler: func(event state.Event) (string, error) { sysInfo, ok := event.Resource.(*hardware.SystemInformation) if !ok { return "", nil } return sysInfo.TypedSpec().SerialNumber, nil }, } } // MACValue is a value for MAC variable. func MACValue() Value { return &value{ registerWatch: func(ctx context.Context, st state.State, ch chan<- state.Event) error { return st.Watch(ctx, network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr).Metadata(), ch) }, eventHandler: func(event state.Event) (string, error) { hwAddr, ok := event.Resource.(*network.HardwareAddr) if !ok { return "", nil } return hwAddr.TypedSpec().HardwareAddr.String(), nil }, } } // HostnameValue is a value for Hostname variable. func HostnameValue() Value { return &value{ registerWatch: func(ctx context.Context, st state.State, ch chan<- state.Event) error { return st.Watch(ctx, network.NewHostnameStatus(network.NamespaceName, network.HostnameID).Metadata(), ch) }, eventHandler: func(event state.Event) (string, error) { hostname, ok := event.Resource.(*network.HostnameStatus) if !ok { return "", nil } return hostname.TypedSpec().Hostname, nil }, } } // CodeValue is a value for Code variable. func CodeValue() Value { return &value{ registerWatch: func(ctx context.Context, st state.State, ch chan<- state.Event) error { return st.Watch(ctx, runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(meta.DownloadURLCode)).Metadata(), ch) }, eventHandler: func(event state.Event) (string, error) { code, ok := event.Resource.(*runtime.MetaKey) if !ok { return "", nil } return code.TypedSpec().Value, nil }, } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/variable.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url import ( "net/url" "regexp" "slices" "strings" "sync" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Variable represents a variable substitution in the download URL. type Variable struct { // Key is the variable name. Key string // MatchOnArg is set for variables which are match on the arg name with empty value. // // Required to support legacy `?uuid=` style of the download URL. MatchOnArg bool // Value is the variable value. Value Value rOnce sync.Once r *regexp.Regexp } // AllVariables is a map of all supported variables. func AllVariables() map[string]*Variable { return map[string]*Variable{ constants.UUIDKey: { Key: constants.UUIDKey, MatchOnArg: true, Value: UUIDValue(), }, constants.SerialNumberKey: { Key: constants.SerialNumberKey, Value: SerialNumberValue(), }, constants.MacKey: { Key: constants.MacKey, Value: MACValue(), }, constants.HostnameKey: { Key: constants.HostnameKey, Value: HostnameValue(), }, constants.CodeKey: { Key: constants.CodeKey, Value: CodeValue(), }, } } func keyToVar(key string) string { return `${` + key + `}` } func (v *Variable) init() { v.rOnce.Do(func() { v.r = regexp.MustCompile(`(?i)` + regexp.QuoteMeta(keyToVar(v.Key))) }) } // Matches checks if the variable is present in the URL. func (v *Variable) Matches(query url.Values) bool { v.init() for arg, values := range query { if v.MatchOnArg { if arg == v.Key && !(len(values) == 1 && strings.TrimSpace(values[0]) != "") { return true } } if slices.ContainsFunc(values, v.r.MatchString) { return true } } return false } // Replace modifies the URL query replacing the variable with the value. func (v *Variable) Replace(query url.Values) { v.init() for arg, values := range query { if v.MatchOnArg { if arg == v.Key && !(len(values) == 1 && strings.TrimSpace(values[0]) != "") { query.Set(arg, v.Value.Get()) continue } } for idx, value := range values { values[idx] = v.r.ReplaceAllString(value, v.Value.Get()) } query[arg] = values } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url/variable_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package url_test import ( "context" neturl "net/url" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestVariableMatches(t *testing.T) { t.Parallel() for _, test := range []struct { name string url string shouldMatch map[string]struct{} }{ { name: "no matches", url: "https://example.com?foo=bar", }, { name: "legacy UUID", url: "https://example.com?uuid=&foo=bar", shouldMatch: map[string]struct{}{ constants.UUIDKey: {}, }, }, { name: "UUID static", url: "https://example.com?uuid=0000-0000&foo=bar", }, { name: "more variables", url: "https://example.com?uuid=${uuid}&foo=bar&serial=${serial}&mac=${mac}&hostname=fixed&hostname=${hostname}", shouldMatch: map[string]struct{}{ constants.UUIDKey: {}, constants.SerialNumberKey: {}, constants.MacKey: {}, constants.HostnameKey: {}, }, }, { name: "case insensitive", url: "https://example.com?uuid=${UUId}&foo=bar&serial=${SeRiaL}", shouldMatch: map[string]struct{}{ constants.UUIDKey: {}, constants.SerialNumberKey: {}, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() u, err := neturl.Parse(test.url) require.NoError(t, err) for _, variable := range url.AllVariables() { if _, ok := test.shouldMatch[variable.Key]; ok { assert.True(t, variable.Matches(u.Query())) } else { assert.False(t, variable.Matches(u.Query())) } } }) } } type mockValue struct { value string } func (v mockValue) Get() string { return v.value } func (v mockValue) RegisterWatch(context.Context, state.State, chan<- state.Event) error { return nil } func (v mockValue) EventHandler(state.Event) (bool, error) { return true, nil } func TestVariableReplace(t *testing.T) { t.Parallel() var1 := &url.Variable{ Key: "var1", MatchOnArg: true, Value: mockValue{ value: "value1", }, } var2 := &url.Variable{ Key: "var2", Value: mockValue{ value: "value2", }, } for _, test := range []struct { name string url string expected string }{ { name: "no matches", url: "https://example.com?foo=bar", expected: "https://example.com?foo=bar", }, { name: "legacy match", url: "https://example.com?var1=&foo=bar", expected: "https://example.com?foo=bar&var1=value1", }, { name: "variable match", url: "https://example.com?a=${var1}-suffix&foo=bar&b=${var2}&b=xyz&b=${var2}", expected: "https://example.com?a=value1-suffix&b=value2&b=xyz&b=value2&foo=bar", }, { name: "case insensitive", url: "https://example.com?a=${VAR1}", expected: "https://example.com?a=value1", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() u, err := neturl.Parse(test.url) require.NoError(t, err) query := u.Query() for _, variable := range []*url.Variable{var1, var2} { variable.Replace(query) } u.RawQuery = query.Encode() assert.Equal(t, test.expected, u.String()) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/metal/url_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package metal_test import ( "context" "net" "net/http" "net/http/httptest" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func createOrUpdate(ctx context.Context, st state.State, r resource.Resource) error { oldRes, err := st.Get(ctx, r.Metadata()) if err != nil && !state.IsNotFoundError(err) { return err } if oldRes == nil { err = st.Create(ctx, r) if err != nil { return err } } else { r.Metadata().SetVersion(oldRes.Metadata().Version()) err = st.Update(ctx, r) if err != nil { return err } } return nil } func setup(ctx context.Context, t *testing.T, st state.State, mockUUID, mockSerialNumber, mockHostname, mockMAC string) { sysInfo := hardware.NewSystemInformation(hardware.SystemInformationID) sysInfo.TypedSpec().UUID = mockUUID sysInfo.TypedSpec().SerialNumber = mockSerialNumber assert.NoError(t, createOrUpdate(ctx, st, sysInfo)) hostnameSpec := network.NewHostnameStatus(network.NamespaceName, network.HostnameID) hostnameSpec.TypedSpec().Hostname = mockHostname assert.NoError(t, createOrUpdate(ctx, st, hostnameSpec)) linkStatusSpec := network.NewHardwareAddr(network.NamespaceName, network.FirstHardwareAddr) parsedMockMAC, err := net.ParseMAC(mockMAC) assert.NoError(t, err) linkStatusSpec.TypedSpec().HardwareAddr = nethelpers.HardwareAddr(parsedMockMAC) assert.NoError(t, createOrUpdate(ctx, st, linkStatusSpec)) netStatus := network.NewStatus(network.NamespaceName, network.StatusID) netStatus.TypedSpec().AddressReady = true assert.NoError(t, createOrUpdate(ctx, st, netStatus)) } func TestRepopulateOnRetry(t *testing.T) { st := state.WrapCore(namespaced.NewState(inmem.Build)) ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() nCalls := 0 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch nCalls { case 0: assert.Equal(t, "h=myTestHostname&m=52%3A2f%3Afd%3Adf%3Afc%3Ac0&s=0OCZJ19N65&u=40dcbd19-3b10-444e-bfff-aaee44a51fda", r.URL.RawQuery) w.WriteHeader(http.StatusNotFound) // After the first call we change the resources that should be substituted in the next call. uuid2 := "9fba530f-767d-40f9-9410-bb1fed5d2134" mac2 := "aa:aa:bb:bb:cc:cc" serialNumber2 := "111AAA9N65" hostname2 := "anotherHostname" setup(ctx, t, st, uuid2, serialNumber2, hostname2, mac2) case 1: // Before the second call Configuration() should have resubstituted all the new parameters in the URL. assert.Equal(t, "h=anotherHostname&m=aa%3Aaa%3Abb%3Abb%3Acc%3Acc&s=111AAA9N65&u=9fba530f-767d-40f9-9410-bb1fed5d2134", r.URL.RawQuery) w.WriteHeader(http.StatusOK) } nCalls++ })) defer server.Close() uuid1 := "40dcbd19-3b10-444e-bfff-aaee44a51fda" mac1 := "52:2f:fd:df:fc:c0" serialNumber1 := "0OCZJ19N65" hostname1 := "myTestHostname" setup(ctx, t, st, uuid1, serialNumber1, hostname1, mac1) downloadURL := server.URL + "/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}" param := procfs.NewParameter(constants.KernelParamConfig) param.Append(downloadURL) procfs.ProcCmdline().Set(constants.KernelParamConfig, param) defer procfs.ProcCmdline().Set(constants.KernelParamConfig, nil) go func() { testObj := metal.Metal{} _, err := testObj.Configuration(ctx, st) assert.NoError(t, err) cancel() }() <-ctx.Done() } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package nocloud provides the NoCloud platform implementation. package nocloud import ( "bufio" "bytes" "context" stderrors "errors" "fmt" "io/fs" "log" "net" "net/netip" "net/url" "slices" "strings" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" yaml "go.yaml.in/yaml/v4" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/internal/pkg/smbios" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/xfs" ) const ( configISOLabel = "cidata" configNetworkConfigPath = "network-config" configMetaDataPath = "meta-data" configUserDataPath = "user-data" ) // NetworkCloudInitConfig wraps nocloud network config to match cloud-init format. type NetworkCloudInitConfig struct { Config NetworkConfig `yaml:"network"` } // NetworkConfig holds network-config info. type NetworkConfig struct { Version int `yaml:"version"` Config []ConfigV1 `yaml:"config,omitempty"` Ethernets map[string]Ethernet `yaml:"ethernets,omitempty"` Bonds map[string]Bond `yaml:"bonds,omitempty"` VLANs map[string]VLAN `yaml:"vlans,omitempty"` } // ConfigV1 holds nocloud v1 config. type ConfigV1 struct { Mac string `yaml:"mac_address,omitempty"` Interfaces string `yaml:"name,omitempty"` MTU uint32 `yaml:"mtu,omitempty"` Subnets []struct { Address string `yaml:"address,omitempty"` Netmask string `yaml:"netmask,omitempty"` Gateway string `yaml:"gateway,omitempty"` Type string `yaml:"type"` } `yaml:"subnets,omitempty"` Address []string `yaml:"address,omitempty"` Type string `yaml:"type"` BondInterfaces []string `yaml:"bond_interfaces,omitempty"` VlanID uint16 `yaml:"vlan_id,omitempty"` VlanLink string `yaml:"vlan_link,omitempty"` Params NetworkParams `yaml:"params,omitempty"` } // Ethernet holds network interface info. type Ethernet struct { Match struct { Name string `yaml:"name,omitempty"` HWAddr string `yaml:"macaddress,omitempty"` } `yaml:"match,omitempty"` DHCPv4 bool `yaml:"dhcp4,omitempty"` DHCPv6 bool `yaml:"dhcp6,omitempty"` Address []string `yaml:"addresses,omitempty"` Gateway4 string `yaml:"gateway4,omitempty"` Gateway6 string `yaml:"gateway6,omitempty"` MTU uint32 `yaml:"mtu,omitempty"` NameServers struct { Search []string `yaml:"search,omitempty"` Address []string `yaml:"addresses,omitempty"` } `yaml:"nameservers,omitempty"` Routes []struct { To string `yaml:"to,omitempty"` Via string `yaml:"via,omitempty"` Metric uint32 `yaml:"metric,omitempty"` Table uint32 `yaml:"table,omitempty"` OnLink bool `yaml:"on-link,omitempty"` } `yaml:"routes,omitempty"` RoutingPolicy []struct { // TODO From string `yaml:"froom,omitempty"` Table uint32 `yaml:"table,omitempty"` } `yaml:"routing-policy,omitempty"` } // Bond holds bonding interface info. type Bond struct { Ethernet `yaml:",inline"` Interfaces []string `yaml:"interfaces,omitempty"` Params struct { Mode string `yaml:"mode,omitempty"` LACPRate string `yaml:"lacp-rate,omitempty"` HashPolicy string `yaml:"transmit-hash-policy,omitempty"` MIIMon uint32 `yaml:"mii-monitor-interval,omitempty"` UpDelay uint32 `yaml:"up-delay,omitempty"` DownDelay uint32 `yaml:"down-delay,omitempty"` } `yaml:"parameters,omitempty"` } // VLAN holds vlan interface info. type VLAN struct { Ethernet `yaml:",inline"` ID uint16 `yaml:"id,omitempty"` Link string `yaml:"link,omitempty"` } // MetadataConfig holds meta info. type MetadataConfig struct { Hostname string `yaml:"hostname,omitempty"` InternalDNS string `yaml:"local-hostname,omitempty"` ExternalDNS string `yaml:"public-hostname,omitempty"` InstanceID string `yaml:"instance-id,omitempty"` InstanceType string `yaml:"instance-type,omitempty"` ProviderID string `yaml:"provider-id,omitempty"` Region string `yaml:"region,omitempty"` Zone string `yaml:"zone,omitempty"` } // NetworkParams holds network parameters (mostly bond for v1 network-config). type NetworkParams struct { BondLACPRate string `yaml:"bond-lacp-rate,omitempty"` BondMiimon uint32 `yaml:"bond-miimon,omitempty"` BondMode string `yaml:"bond-mode,omitempty"` BondXmitHashPolicy string `yaml:"bond-xmit-hash-policy,omitempty"` UpDelay uint32 `yaml:"up-delay,omitempty"` DownDelay uint32 `yaml:"down-delay,omitempty"` } func (n *Nocloud) configFromNetwork(ctx context.Context, metaBaseURL string, r state.State) (metaConfig []byte, networkConfig []byte, machineConfig []byte, err error) { log.Printf("fetching meta config from: %q", metaBaseURL+configMetaDataPath) if err = netutils.Wait(ctx, r); err != nil { return nil, nil, nil, err } metaConfig, err = download.Download(ctx, metaBaseURL+configMetaDataPath) if err != nil { metaConfig = nil } log.Printf("fetching network config from: %q", metaBaseURL+configNetworkConfigPath) networkConfig, err = download.Download(ctx, metaBaseURL+configNetworkConfigPath) if err != nil { networkConfig = nil } log.Printf("fetching machine config from: %q", metaBaseURL+configUserDataPath) machineConfig, err = download.Download(ctx, metaBaseURL+configUserDataPath, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) return metaConfig, networkConfig, machineConfig, err } //nolint:gocyclo func (n *Nocloud) configFromCD(ctx context.Context, r state.State) (metaConfig []byte, networkConfig []byte, machineConfig []byte, err error) { err = blockutils.ReadFromVolume(ctx, r, []string{strings.ToLower(configISOLabel), strings.ToUpper(configISOLabel)}, func(root xfs.Root, volumeStatus *block.VolumeStatus) error { log.Printf("found config disk (cidata) at %s", volumeStatus.TypedSpec().Location) log.Printf("fetching meta config from: cidata/%s", configMetaDataPath) metaConfig, err = xfs.ReadFile(root, configMetaDataPath) if err != nil { log.Printf("failed to read %s: %s", configMetaDataPath, err) metaConfig = nil } log.Printf("fetching network config from: cidata/%s", configNetworkConfigPath) networkConfig, err = xfs.ReadFile(root, configNetworkConfigPath) if err != nil { log.Printf("failed to read %s: %s", configNetworkConfigPath, err) networkConfig = nil } log.Printf("fetching machine config from: cidata/%s", configUserDataPath) machineConfig, err = xfs.ReadFile(root, configUserDataPath) if err != nil { log.Printf("failed to read %s: %s", configUserDataPath, err) machineConfig = nil } return nil }, ) if err != nil { if stderrors.Is(err, fs.ErrNotExist) { return nil, nil, nil, errors.ErrNoConfigSource } return nil, nil, nil, err } if machineConfig == nil { err = errors.ErrNoConfigSource } return metaConfig, networkConfig, machineConfig, err } //nolint:gocyclo func (n *Nocloud) acquireConfig(ctx context.Context, r state.State) (metadataNetworkConfigDl, machineConfigDl []byte, metadata *MetadataConfig, err error) { s, err := smbios.GetSMBIOSInfo() if err != nil { return nil, nil, nil, err } var ( metaBaseURL, hostname, instanceID string networkSource bool ) for option := range strings.SplitSeq(s.SystemInformation.SerialNumber, ";") { parts := strings.SplitN(option, "=", 2) if len(parts) == 2 { switch parts[0] { case "ds": if parts[1] == "nocloud-net" { networkSource = true } case "s": var u *url.URL u, err = url.Parse(parts[1]) if err == nil && strings.HasPrefix(u.Scheme, "http") { if strings.HasSuffix(u.Path, "/") { metaBaseURL = parts[1] } else { metaBaseURL = parts[1] + "/" } } case "h": hostname = parts[1] case "i": instanceID = parts[1] } } } var metadataConfigDl []byte if networkSource && metaBaseURL != "" { metadataConfigDl, metadataNetworkConfigDl, machineConfigDl, err = n.configFromNetwork(ctx, metaBaseURL, r) } else { metadataConfigDl, metadataNetworkConfigDl, machineConfigDl, err = n.configFromCD(ctx, r) } metadata = &MetadataConfig{} if metadataConfigDl != nil { _ = yaml.Unmarshal(metadataConfigDl, metadata) //nolint:errcheck } if hostname != "" { metadata.Hostname = hostname } if instanceID != "" { metadata.InstanceID = instanceID } // Some providers may provide the hostname via user-data instead of meta-data (e.g. Proxmox VE) // As long as the user doesn't use it for machine config, it can still be used to obtain the hostname if metadata.Hostname == "" && metadata.InternalDNS == "" && machineConfigDl != nil { fallbackMetadata := &MetadataConfig{} _ = yaml.Unmarshal(machineConfigDl, fallbackMetadata) //nolint:errcheck metadata.Hostname = fallbackMetadata.Hostname metadata.InternalDNS = fallbackMetadata.InternalDNS } return metadataNetworkConfigDl, machineConfigDl, metadata, err } //nolint:gocyclo,cyclop func (n *Nocloud) applyNetworkConfigV1(ctx context.Context, config *NetworkConfig, st state.State, networkConfig *runtime.PlatformNetworkConfig) (bool, error) { hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st) if err != nil { return false, fmt.Errorf("error listing host interfaces: %w", err) } var needsReconcile bool parseSubnets := func(ntwrk ConfigV1, name string) error { for _, subnet := range ntwrk.Subnets { switch subnet.Type { case "dhcp", "dhcp4": networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: name, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) case "static", "static6": family := nethelpers.FamilyInet4 if subnet.Type == "static6" { family = nethelpers.FamilyInet6 } ipPrefix, err := netip.ParsePrefix(subnet.Address) if err != nil { ip, err := netip.ParseAddr(subnet.Address) if err != nil { return err } netmask, err := netip.ParseAddr(subnet.Netmask) if err != nil { return err } mask, _ := netmask.MarshalBinary() //nolint:errcheck // never fails ones, _ := net.IPMask(mask).Size() ipPrefix = netip.PrefixFrom(ip, ones) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: name, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) if subnet.Gateway != "" { gw, err := netip.ParseAddr(subnet.Gateway) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: name, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: network.DefaultRouteMetric, } if family == nethelpers.FamilyInet6 { route.Priority = 2 * network.DefaultRouteMetric } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } case "ipv6_dhcpv6-stateful": networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: name, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 2 * network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } } return nil } physicalNameMap := map[string]string{} type enslavedLink struct { bondName string slaveIndex int } enslavedLinks := map[string]enslavedLink{} for _, ntwrk := range config.Config { switch ntwrk.Type { case "nameserver": dnsIPs := make([]netip.Addr, 0, len(ntwrk.Address)) for i := range ntwrk.Address { if ip, err := netip.ParseAddr(ntwrk.Address[i]); err == nil { dnsIPs = append(dnsIPs, ip) } else { return false, err } } networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) case "bond": name := ntwrk.Interfaces mode, err := nethelpers.BondModeByName(ntwrk.Params.BondMode) if err != nil { return false, fmt.Errorf("invalid mode: %w", err) } hashPolicy, err := nethelpers.BondXmitHashPolicyByName(ntwrk.Params.BondXmitHashPolicy) if err != nil { return false, fmt.Errorf("invalid transmit-hash-policy: %w", err) } lacpRate, err := nethelpers.LACPRateByName(ntwrk.Params.BondLACPRate) if err != nil { return false, fmt.Errorf("invalid lacp-rate: %w", err) } bondLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: name, Logical: true, Up: true, Kind: network.LinkKindBond, Type: nethelpers.LinkEther, BondMaster: network.BondMasterSpec{ Mode: mode, HashPolicy: hashPolicy, MIIMon: ntwrk.Params.BondMiimon, UpDelay: ntwrk.Params.UpDelay, DownDelay: ntwrk.Params.DownDelay, LACPRate: lacpRate, }, } if mode == nethelpers.BondMode8023AD { bondLink.BondMaster.ADLACPActive = nethelpers.ADLACPActiveOn } if ntwrk.MTU != 0 { bondLink.MTU = ntwrk.MTU } networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults() networkConfig.Links = append(networkConfig.Links, bondLink) for idx, slave := range ntwrk.BondInterfaces { enslavedLinks[slave] = enslavedLink{ bondName: name, slaveIndex: idx, } } if err = parseSubnets(ntwrk, name); err != nil { return false, err } case "vlan": name := ntwrk.Interfaces parentName, ok := physicalNameMap[ntwrk.VlanLink] if !ok { parentName = ntwrk.VlanLink } linkSpec := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: name, Logical: true, Up: true, Kind: network.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: parentName, VLAN: network.VLANSpec{ VID: ntwrk.VlanID, Protocol: nethelpers.VLANProtocol8021Q, }, } if ntwrk.MTU != 0 { linkSpec.MTU = ntwrk.MTU } networkConfig.Links = append(networkConfig.Links, linkSpec) if err = parseSubnets(ntwrk, name); err != nil { return false, err } case "physical": name := ntwrk.Interfaces if ntwrk.Mac != "" { macAddressMatched := false for hostInterface := range hostInterfaces.All() { if !hostInterface.TypedSpec().Physical() { continue } macAddress := hostInterface.TypedSpec().PermanentAddr.String() if macAddress == "" { macAddress = hostInterface.TypedSpec().HardwareAddr.String() } if strings.EqualFold(macAddress, ntwrk.Mac) { name = hostInterface.Metadata().ID() macAddressMatched = true break } } if !macAddressMatched { log.Printf("nocloud: no link with matching MAC address %q, defaulted to use name %s instead", ntwrk.Mac, name) needsReconcile = true } } physicalNameMap[ntwrk.Interfaces] = name linkSpec := network.LinkSpecSpec{ Name: name, Up: true, ConfigLayer: network.ConfigPlatform, } if ntwrk.MTU != 0 { linkSpec.MTU = ntwrk.MTU } networkConfig.Links = append(networkConfig.Links, linkSpec) if err = parseSubnets(ntwrk, name); err != nil { return false, err } } } for slaveName, enslavedLink := range enslavedLinks { physicalName, ok := physicalNameMap[slaveName] if !ok { physicalName = slaveName } for idx := range networkConfig.Links { if networkConfig.Links[idx].Name != physicalName { continue } networkConfig.Links[idx].BondSlave = network.BondSlave{ MasterName: enslavedLink.bondName, SlaveIndex: enslavedLink.slaveIndex, } } } return needsReconcile, nil } //nolint:gocyclo,cyclop func applyNetworkConfigV2Ethernet(name string, eth Ethernet, networkConfig *runtime.PlatformNetworkConfig, dnsIPs *[]netip.Addr) error { if eth.DHCPv4 { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: name, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } if eth.DHCPv6 { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: name, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } for _, addr := range eth.Address { ipPrefix, err := netip.ParsePrefix(addr) if err != nil { return err } family := nethelpers.FamilyInet4 if ipPrefix.Addr().Is6() { family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: name, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) } if eth.Gateway4 != "" { gw, err := netip.ParseAddr(eth.Gateway4) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: name, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } if eth.Gateway6 != "" { gw, err := netip.ParseAddr(eth.Gateway6) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: name, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } for _, addr := range eth.NameServers.Address { if ip, err := netip.ParseAddr(addr); err == nil { *dnsIPs = append(*dnsIPs, ip) } else { return err } } for _, route := range eth.Routes { gw, err := netip.ParseAddr(route.Via) if err != nil { return fmt.Errorf("failed to parse route gateway: %w", err) } if route.To == "default" { if gw.Is4() { route.To = "0.0.0.0/0" } else { route.To = "::/0" } } dest, err := netip.ParsePrefix(route.To) if err != nil { return fmt.Errorf("failed to parse route destination: %w", err) } routeSpec := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: dest, Gateway: gw, OutLinkName: name, Table: withDefault(nethelpers.RoutingTable(route.Table), nethelpers.TableMain), Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: withDefault(route.Metric, network.DefaultRouteMetric), } if gw.Is6() { routeSpec.Family = nethelpers.FamilyInet6 if routeSpec.Priority == network.DefaultRouteMetric { routeSpec.Priority = 2 * network.DefaultRouteMetric } } routeSpec.Normalize() networkConfig.Routes = append(networkConfig.Routes, routeSpec) if route.OnLink && gw.Is4() { // This assumes an interface with multiple routes will never have multiple statically set ips. ipPrefix, err := netip.ParsePrefix(eth.Address[0]) if err != nil { return fmt.Errorf("failed to parse route source: %w", err) } routeSpec := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: netip.PrefixFrom(gw, gw.BitLen()), Source: ipPrefix.Addr(), OutLinkName: name, Scope: nethelpers.ScopeLink, Table: withDefault(nethelpers.RoutingTable(route.Table), nethelpers.TableMain), Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: withDefault(route.Metric, network.DefaultRouteMetric), } networkConfig.Routes = append(networkConfig.Routes, routeSpec) } } return nil } //nolint:gocyclo,cyclop func (n *Nocloud) applyNetworkConfigV2(ctx context.Context, config *NetworkConfig, st state.State, networkConfig *runtime.PlatformNetworkConfig) (bool, error) { var ( dnsIPs []netip.Addr needsReconcile bool ) hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st) if err != nil { return false, fmt.Errorf("error listing host interfaces: %w", err) } ethernetNames := maps.Keys(config.Ethernets) slices.Sort(ethernetNames) for _, name := range ethernetNames { eth := config.Ethernets[name] var bondSlave network.BondSlave for bondName, bond := range config.Bonds { for idx, iface := range bond.Interfaces { if iface == name { bondSlave.MasterName = bondName bondSlave.SlaveIndex = idx } } } if eth.Match.HWAddr != "" { var availableMACAddresses []string macAddressMatched := false for hostInterface := range hostInterfaces.All() { if !hostInterface.TypedSpec().Physical() { continue } macAddress := hostInterface.TypedSpec().PermanentAddr.String() if macAddress == "" { macAddress = hostInterface.TypedSpec().HardwareAddr.String() } if strings.EqualFold(macAddress, eth.Match.HWAddr) { name = hostInterface.Metadata().ID() macAddressMatched = true break } availableMACAddresses = append(availableMACAddresses, macAddress) } if !macAddressMatched { log.Printf("nocloud: no link with matching MAC address %q (available %v), defaulted to use name %s instead", eth.Match.HWAddr, availableMACAddresses, name) needsReconcile = true } } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: name, Up: true, MTU: eth.MTU, ConfigLayer: network.ConfigPlatform, BondSlave: bondSlave, }) err := applyNetworkConfigV2Ethernet(name, eth, networkConfig, &dnsIPs) if err != nil { return false, err } } bondNames := maps.Keys(config.Bonds) slices.Sort(bondNames) for _, bondName := range bondNames { bond := config.Bonds[bondName] mode, err := nethelpers.BondModeByName(bond.Params.Mode) if err != nil { return false, fmt.Errorf("invalid mode: %w", err) } hashPolicy, err := nethelpers.BondXmitHashPolicyByName(bond.Params.HashPolicy) if err != nil { return false, fmt.Errorf("invalid transmit-hash-policy: %w", err) } lacpRate, err := nethelpers.LACPRateByName(bond.Params.LACPRate) if err != nil { return false, fmt.Errorf("invalid lacp-rate: %w", err) } bondLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: bondName, Logical: true, Up: true, MTU: bond.Ethernet.MTU, Kind: network.LinkKindBond, Type: nethelpers.LinkEther, BondMaster: network.BondMasterSpec{ Mode: mode, HashPolicy: hashPolicy, MIIMon: bond.Params.MIIMon, UpDelay: bond.Params.UpDelay, DownDelay: bond.Params.DownDelay, LACPRate: lacpRate, }, } if mode == nethelpers.BondMode8023AD { bondLink.BondMaster.ADLACPActive = nethelpers.ADLACPActiveOn } networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults() networkConfig.Links = append(networkConfig.Links, bondLink) err = applyNetworkConfigV2Ethernet(bondName, bond.Ethernet, networkConfig, &dnsIPs) if err != nil { return false, err } } vlanNames := maps.Keys(config.VLANs) slices.Sort(vlanNames) for _, vlanName := range vlanNames { vlan := config.VLANs[vlanName] vlanLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: vlanName, Logical: true, Up: true, MTU: vlan.Ethernet.MTU, Kind: network.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: vlan.Link, VLAN: network.VLANSpec{ VID: vlan.ID, Protocol: nethelpers.VLANProtocol8021Q, }, } networkConfig.Links = append(networkConfig.Links, vlanLink) err = applyNetworkConfigV2Ethernet(vlanName, vlan.Ethernet, networkConfig, &dnsIPs) if err != nil { return false, err } } if len(dnsIPs) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) } return needsReconcile, nil } func withDefault[T comparable](v T, defaultValue T) T { var zeroT T if v == zeroT { return defaultValue } return v } // FetchInclude fetches nocloud #include configuration from the URL specified in the body. func (n *Nocloud) FetchInclude(ctx context.Context, body []byte, st state.State) ([]byte, error) { u, err := ExtractIncludeURL(body) if err != nil { return nil, err } log.Printf("fetching the nocloud #include configuration from: %q", u.String()) if err = netutils.Wait(ctx, st); err != nil { return nil, err } return download.Download(ctx, u.String(), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // ExtractIncludeURL extracts the URL from the body of a nocloud #include configuration. // // Note: only a single URL is expected in the body. func ExtractIncludeURL(body []byte) (*url.URL, error) { var urlLine string scanner := bufio.NewScanner(bytes.NewReader(body)) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } if urlLine != "" { return nil, fmt.Errorf("multiple #include URLs found in nocloud configuration: %q and %q", urlLine, line) } urlLine = line } if err := scanner.Err(); err != nil { return nil, err } if urlLine == "" { return nil, stderrors.New("no #include URL found in nocloud configuration") } return url.Parse(urlLine) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nocloud import ( "bytes" "context" stderrors "errors" "fmt" "time" "github.com/cenkalti/backoff/v4" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-procfs/procfs" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Nocloud is the concrete type that implements the runtime.Platform interface. type Nocloud struct{} // Name implements the runtime.Platform interface. func (n *Nocloud) Name() string { return "nocloud" } // ParseMetadata converts nocloud metadata to platform network config. func (n *Nocloud) ParseMetadata(ctx context.Context, unmarshalledNetworkConfig *NetworkConfig, st state.State, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, bool, error) { networkConfig := &runtime.PlatformNetworkConfig{} hostname := metadata.Hostname if hostname == "" { hostname = metadata.InternalDNS } if hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(hostname); err != nil { return nil, false, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var ( needsReconcile bool err error ) switch unmarshalledNetworkConfig.Version { case 1: if needsReconcile, err = n.applyNetworkConfigV1(ctx, unmarshalledNetworkConfig, st, networkConfig); err != nil { return nil, false, err } case 2: if needsReconcile, err = n.applyNetworkConfigV2(ctx, unmarshalledNetworkConfig, st, networkConfig); err != nil { return nil, false, err } default: return nil, false, fmt.Errorf("network-config metadata version=%d is not supported", unmarshalledNetworkConfig.Version) } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: n.Name(), Hostname: hostname, InstanceID: metadata.InstanceID, InstanceType: metadata.InstanceType, ProviderID: metadata.ProviderID, Region: metadata.Region, Zone: metadata.Zone, InternalDNS: metadata.InternalDNS, ExternalDNS: metadata.ExternalDNS, } return networkConfig, needsReconcile, nil } // Configuration implements the runtime.Platform interface. func (n *Nocloud) Configuration(ctx context.Context, r state.State) ([]byte, error) { _, machineConfigDl, _, err := n.acquireConfig(ctx, r) if err != nil { return nil, err } firstLine, rest, _ := bytes.Cut(machineConfigDl, []byte("\n")) firstLine = bytes.TrimSpace(firstLine) switch { case bytes.Equal(firstLine, []byte("#cloud-config")): // ignore cloud-config, Talos does not support it return nil, errors.ErrNoConfigSource case bytes.Equal(firstLine, []byte("#include")): return n.FetchInclude(ctx, rest, r) } return machineConfigDl, nil } // Mode implements the runtime.Platform interface. func (n *Nocloud) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (n *Nocloud) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. // //nolint:gocyclo func (n *Nocloud) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { // wait for devices to be ready before proceeding if err := netutils.WaitForDevicesReady(ctx, st); err != nil { return fmt.Errorf("error waiting for devices to be ready: %w", err) } metadataNetworkConfigDl, _, metadata, err := n.acquireConfig(ctx, st) if stderrors.Is(err, errors.ErrNoConfigSource) { err = nil } if err != nil { return err } if metadataNetworkConfigDl == nil { // no data, use cached network configuration if available return nil } unmarshalledNetworkConfig, err := DecodeNetworkConfig(metadataNetworkConfigDl) if err != nil { return err } // do a loop to retry network config remap in case of missing links // on each try, export the configuration as it is, and if the network is reconciled next time, export the reconciled configuration bckoff := backoff.NewExponentialBackOff() for { networkConfig, needsReconcile, err := n.ParseMetadata(ctx, unmarshalledNetworkConfig, st, metadata) if err != nil { return err } if !channel.SendWithContext(ctx, ch, networkConfig) { return ctx.Err() } if !needsReconcile { return nil } // wait for for backoff to retry network config remap nextBackoff := bckoff.NextBackOff() if nextBackoff == backoff.Stop { return nil } select { case <-ctx.Done(): return ctx.Err() case <-time.After(nextBackoff): } } } // DecodeNetworkConfig decodes the network configuration guessing the format from the content. func DecodeNetworkConfig(content []byte) (*NetworkConfig, error) { var decoded map[string]any err := yaml.Unmarshal(content, &decoded) if err != nil { return nil, err } if _, ok := decoded["network"]; ok { var ciNetworkConfig NetworkCloudInitConfig err = yaml.Unmarshal(content, &ciNetworkConfig) if err != nil { return nil, err } return &ciNetworkConfig.Config, nil } // If it is not plain *v2 cloud-init* config then we attempt to decode *nocloud* if _, ok := decoded["version"]; ok { var nc NetworkConfig err = yaml.Unmarshal(content, &nc) if err != nil { return nil, err } return &nc, nil } return nil, fmt.Errorf("failed to decode network configuration, keys: %v", maps.Keys(decoded)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/nocloud_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nocloud_test import ( "context" _ "embed" "net" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) //go:embed testdata/in-v1.yaml var rawNetworkConfigV1 []byte //go:embed testdata/in-v1-pnap.yaml var rawNetworkConfigV1Pnap []byte //go:embed testdata/in-v2-nocloud.yaml var rawNetworkConfigV2Nocloud []byte //go:embed testdata/in-v2-cloud-init.yaml var rawNetworkConfigV2CloudInit []byte //go:embed testdata/in-v2-serverscom.yaml var rawNetworkConfigV2Serverscom []byte //go:embed testdata/expected-v1.yaml var expectedNetworkConfigV1 string //go:embed testdata/expected-v1-pnap.yaml var expectedNetworkConfigV1Pnap string //go:embed testdata/expected-v2.yaml var expectedNetworkConfigV2 string //go:embed testdata/expected-v2-serverscom.yaml var expectedNetworkConfigV2Serverscom string func TestParseNetworkConfig(t *testing.T) { t.Parallel() for _, tt := range []struct { name string raw []byte expected string expectedNeedsRecocile bool }{ { name: "V1", raw: rawNetworkConfigV1, expected: expectedNetworkConfigV1, }, { name: "V1-pnap", raw: rawNetworkConfigV1Pnap, expected: expectedNetworkConfigV1Pnap, }, { name: "V2-nocloud", raw: rawNetworkConfigV2Nocloud, expected: expectedNetworkConfigV2, expectedNeedsRecocile: true, }, { name: "V2-cloud-init", raw: rawNetworkConfigV2CloudInit, expected: expectedNetworkConfigV2, expectedNeedsRecocile: true, }, { name: "V2-servers.com", raw: rawNetworkConfigV2Serverscom, expected: expectedNetworkConfigV2Serverscom, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) t.Cleanup(cancel) n := &nocloud.Nocloud{} st := state.WrapCore(namespaced.NewState(inmem.Build)) devicesReady := runtime.NewDevicesStatus(runtime.NamespaceName, runtime.DevicesID) devicesReady.TypedSpec().Ready = true require.NoError(t, st.Create(ctx, devicesReady)) bond0 := network.NewLinkStatus(network.NamespaceName, "bond0") bond0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf7} // this link is not a physical one, so it should be ignored bond0.TypedSpec().Type = nethelpers.LinkEther bond0.TypedSpec().Kind = "bond" require.NoError(t, st.Create(ctx, bond0)) eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf7} eth0.TypedSpec().Type = nethelpers.LinkEther eth0.TypedSpec().Kind = "" require.NoError(t, st.Create(ctx, eth0)) eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().HardwareAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf9} // this link has a permanent address, so hardware addr should be ignored eth1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf8} eth1.TypedSpec().Type = nethelpers.LinkEther eth1.TypedSpec().Kind = "" require.NoError(t, st.Create(ctx, eth1)) eth2 := network.NewLinkStatus(network.NamespaceName, "eth2") eth2.TypedSpec().HardwareAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf9} // this link doesn't have a permanent address, but only a hardware address eth2.TypedSpec().Type = nethelpers.LinkEther eth2.TypedSpec().Kind = "" require.NoError(t, st.Create(ctx, eth2)) eno1np0 := network.NewLinkStatus(network.NamespaceName, "eno1np0") eno1np0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr(must.Value(net.ParseMAC("3c:ec:ef:e0:45:28"))(t)) eno1np0.TypedSpec().Type = nethelpers.LinkEther eno1np0.TypedSpec().Kind = "" require.NoError(t, st.Create(ctx, eno1np0)) eno2np1 := network.NewLinkStatus(network.NamespaceName, "eno2np1") eno2np1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr(must.Value(net.ParseMAC("3c:ec:ef:e0:45:29"))(t)) eno2np1.TypedSpec().Type = nethelpers.LinkEther eno2np1.TypedSpec().Kind = "" require.NoError(t, st.Create(ctx, eno2np1)) m, err := nocloud.DecodeNetworkConfig(tt.raw) require.NoError(t, err) mc := nocloud.MetadataConfig{ Hostname: "talos.fqdn", InternalDNS: "talos.fqdn", InstanceID: "0", } mc2 := nocloud.MetadataConfig{ InternalDNS: "talos.fqdn", InstanceID: "0", } networkConfig, needsReconcile, err := n.ParseMetadata(ctx, m, st, &mc) require.NoError(t, err) networkConfig2, needsReconcile2, err := n.ParseMetadata(ctx, m, st, &mc2) require.NoError(t, err) assert.Equal(t, needsReconcile, needsReconcile2) assert.Equal(t, tt.expectedNeedsRecocile, needsReconcile) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) marshaled2, err := yaml.Marshal(networkConfig2) require.NoError(t, err) assert.Equal(t, tt.expected, string(marshaled)) assert.Equal(t, tt.expected, string(marshaled2)) }) } } //go:embed testdata/metadata-nocloud.yaml var rawMetadataNocloud []byte func TestMedatada(t *testing.T) { t.Parallel() var md nocloud.MetadataConfig err := yaml.Unmarshal(rawMetadataNocloud, &md) require.NoError(t, err) assert.Equal(t, nocloud.MetadataConfig{ InstanceID: "80d6927ecb30c1707b12f38ed1211535930ff16e", InternalDNS: "talos-worker-3", }, md) } func TestExtractURL(t *testing.T) { t.Parallel() for _, test := range []struct { name string userdata []byte expectedURL string expectedError string }{ { name: "valid include userdata URL", userdata: []byte(`https://metadataserver/userdata `), expectedURL: "https://metadataserver/userdata", }, { name: "multiple URLs is invalid", userdata: []byte(` https://metadataserver1/userdata https://metadataserver2/userdata https://metadataserver3/userdata `), expectedError: "multiple #include URLs found", }, { name: "invalid URL", userdata: []byte(` :/invalidurl/userdata`), expectedError: "missing protocol scheme", }, { name: "no URL found", userdata: []byte(` `), expectedError: "no #include URL found in nocloud configuration", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() u, err := nocloud.ExtractIncludeURL(test.userdata) if test.expectedError != "" { require.Error(t, err) require.ErrorContains(t, err, test.expectedError) } else { require.NoError(t, err) assert.Equal(t, test.expectedURL, u.String()) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1-pnap.yaml ================================================ addresses: - address: 1.2.3.4/29 linkName: bond0.2 family: inet4 scope: global flags: permanent layer: platform - address: 10.0.0.11/24 linkName: bond0.4 family: inet4 scope: global flags: permanent layer: platform links: - name: eno1np0 logical: false up: true mtu: 9000 kind: "" type: netrom masterName: bond0 layer: platform - name: eno2np1 logical: false up: true mtu: 9000 kind: "" type: netrom masterName: bond0 slaveIndex: 1 layer: platform - name: bond0 logical: true up: true mtu: 9000 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: fast arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform - name: bond0.2 logical: true up: true mtu: 9000 kind: vlan type: ether parentName: bond0 vlan: vlanID: 2 vlanProtocol: 802.1q layer: platform - name: bond0.4 logical: true up: true mtu: 9000 kind: vlan type: ether parentName: bond0 vlan: vlanID: 4 vlanProtocol: 802.1q layer: platform routes: - family: inet4 dst: "" src: "" gateway: 1.2.3.5 outLinkName: bond0.2 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: - dnsServers: - 8.8.8.8 - 8.8.4.4 layer: platform timeServers: [] operators: [] externalIPs: [] metadata: platform: nocloud hostname: talos.fqdn instanceId: "0" internalDNS: talos.fqdn ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v1.yaml ================================================ addresses: - address: 192.168.1.11/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 2001:2:3:4:5:6:7:f7/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform - address: 192.168.2.11/24 linkName: eth2 family: inet4 scope: global flags: permanent layer: platform - address: 2001:2:3:4:5:6:7:f9/64 linkName: eth2 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: fe80::1 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: "" src: "" gateway: 192.168.2.1 outLinkName: eth2 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: fe80::2 outLinkName: eth2 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: - dnsServers: - 192.168.1.1 layer: platform timeServers: [] operators: [] externalIPs: [] metadata: platform: nocloud hostname: talos.fqdn instanceId: "0" internalDNS: talos.fqdn ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2-serverscom.yaml ================================================ addresses: - address: 188.42.48.188/29 linkName: agge family: inet4 scope: global flags: permanent layer: platform - address: 10.26.98.92/29 linkName: aggi family: inet4 scope: global flags: permanent layer: platform links: - name: eno1np0 logical: false up: true mtu: 0 kind: "" type: netrom masterName: agge layer: platform - name: eno2np1 logical: false up: true mtu: 0 kind: "" type: netrom masterName: agge slaveIndex: 1 layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom masterName: aggi layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom masterName: aggi slaveIndex: 1 layer: platform - name: agge logical: true up: true mtu: 0 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: slow arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform - name: aggi logical: true up: true mtu: 0 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: slow arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform routes: - family: inet4 dst: "" src: "" gateway: 188.42.48.187 outLinkName: agge table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 10.26.98.91 outLinkName: aggi table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 192.168.0.0/16 src: "" gateway: 10.26.98.91 outLinkName: aggi table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 188.42.208.0/21 src: "" gateway: 10.26.98.91 outLinkName: aggi table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: [] metadata: platform: nocloud hostname: talos.fqdn instanceId: "0" internalDNS: talos.fqdn ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/expected-v2.yaml ================================================ addresses: - address: 192.168.14.2/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 2001:1::1/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform - address: 10.22.14.2/32 linkName: eth1 family: inet4 scope: global flags: permanent layer: platform - address: 10.10.4.140/29 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform - address: 192.34.34.34/32 linkName: bond0.4 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 slaveIndex: 1 layer: platform - name: bond0 logical: true up: true mtu: 1500 kind: bond type: ether bondMaster: mode: 802.3ad xmitHashPolicy: layer3+4 lacpRate: fast arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform - name: bond0.4 logical: true up: true mtu: 1500 kind: vlan type: ether parentName: bond0 vlan: vlanID: 4 vlanProtocol: 802.1q layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.14.1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: 2001:1::2 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: "" src: "" gateway: 192.168.14.1 outLinkName: eth1 table: main priority: 100 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 192.168.14.1/32 src: 10.22.14.2 gateway: "" outLinkName: eth1 table: main priority: 100 scope: link type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 10.10.4.147 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 192.168.0.0/16 src: "" gateway: 10.10.4.147 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 188.42.208.0/21 src: "" gateway: 10.10.4.147 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: fqdn layer: platform resolvers: - dnsServers: - 8.8.8.8 - 1.1.1.1 - 2.2.2.2 layer: platform timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 layer: platform externalIPs: [] metadata: platform: nocloud hostname: talos.fqdn instanceId: "0" internalDNS: talos.fqdn ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/in-v1-pnap.yaml ================================================ version: 1 config: - type: physical name: eno1np0 mac_address: "3c:ec:ef:e0:45:28" mtu: 9000 - type: physical name: eno2np1 mac_address: "3c:ec:ef:e0:45:29" mtu: 9000 - type: bond name: bond0 mac_address: "3c:ec:ef:e0:45:28" mtu: 9000 bond_interfaces: - eno1np0 - eno2np1 params: bond-lacp-rate: fast bond-miimon: 100 bond-mode: 802.3ad bond-xmit-hash-policy: layer3+4 up-delay: 0 down-delay: 0 # public frontend MERGED_FRONTEND vlan 2 - type: vlan name: bond0.2 mtu: 9000 vlan_id: 2 vlan_link: bond0 subnets: - address: 1.2.3.4/29 gateway: 1.2.3.5 type: static # private backend vlan 4 - type: vlan mtu: 9000 name: bond0.4 vlan_id: 4 vlan_link: bond0 subnets: - type: static address: 10.0.0.11/24 - type: nameserver address: - 8.8.8.8 - 8.8.4.4 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/in-v1.yaml ================================================ version: 1 config: - type: physical name: eth0 mac_address: '68:05:ca:b8:f1:f7' subnets: - type: static address: '192.168.1.11' netmask: '255.255.255.0' gateway: '192.168.1.1' - type: static6 address: '2001:2:3:4:5:6:7:f7/64' gateway: 'fe80::1' - type: physical name: eth1 mac_address: '68:05:ca:b8:f1:f9' subnets: - type: static address: '192.168.2.11' netmask: '255.255.255.0' gateway: '192.168.2.1' - type: static6 address: '2001:2:3:4:5:6:7:f9/64' gateway: 'fe80::2' - type: nameserver address: - '192.168.1.1' search: - 'lan' ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/in-v2-cloud-init.yaml ================================================ network: version: 2 ethernets: eth0: match: macaddress: "00:20:6e:1f:f9:a8" dhcp4: true addresses: - 192.168.14.2/24 - 2001:1::1/64 gateway4: 192.168.14.1 gateway6: 2001:1::2 nameservers: search: [foo.local, bar.local] addresses: [8.8.8.8] eth1: match: macaddress: '00:20:6e:1f:f9:a9' addresses: - 10.22.14.2/32 nameservers: search: [ foo.local, bar.local ] routes: - to: default via: "192.168.14.1" metric: 100 on-link: true ext1: match: macaddress: 68:05:ca:b8:f1:f8 ext2: match: macaddress: 68:05:ca:b8:f1:f9 bonds: bond0: interfaces: - ext1 - ext2 macaddress: e4:3d:1a:4d:6a:28 mtu: 1500 parameters: mode: 802.3ad mii-monitor-interval: 100 down-delay: 200 up-delay: 200 lacp-rate: fast transmit-hash-policy: layer3+4 addresses: - 10.10.4.140/29 nameservers: addresses: - 1.1.1.1 - 2.2.2.2 routes: - to: 10.0.0.0/8 via: 10.10.4.147 - to: 192.168.0.0/16 via: 10.10.4.147 - to: 188.42.208.0/21 via: 10.10.4.147 vlans: bond0.4: id: 4 link: bond0 mtu: 1500 addresses: - 192.34.34.34/32 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/in-v2-nocloud.yaml ================================================ version: 2 ethernets: eth0: match: macaddress: "00:20:6e:1f:f9:a8" dhcp4: true addresses: - 192.168.14.2/24 - 2001:1::1/64 gateway4: 192.168.14.1 gateway6: 2001:1::2 nameservers: search: [foo.local, bar.local] addresses: [8.8.8.8] eth1: match: macaddress: '00:20:6e:1f:f9:a9' addresses: - 10.22.14.2/32 nameservers: search: [ foo.local, bar.local ] routes: - to: "0.0.0.0/0" via: "192.168.14.1" metric: 100 on-link: true ext1: match: macaddress: 68:05:ca:b8:f1:f8 ext2: match: macaddress: 68:05:ca:b8:f1:f9 bonds: bond0: interfaces: - ext1 - ext2 macaddress: e4:3d:1a:4d:6a:28 mtu: 1500 parameters: mode: 802.3ad mii-monitor-interval: 100 down-delay: 200 up-delay: 200 lacp-rate: fast transmit-hash-policy: layer3+4 addresses: - 10.10.4.140/29 nameservers: addresses: - 1.1.1.1 - 2.2.2.2 routes: - to: 10.0.0.0/8 via: 10.10.4.147 - to: 192.168.0.0/16 via: 10.10.4.147 - to: 188.42.208.0/21 via: 10.10.4.147 vlans: bond0.4: id: 4 link: bond0 mtu: 1500 addresses: - 192.34.34.34/32 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/in-v2-serverscom.yaml ================================================ version: 2 bonds: aggi: interfaces: - int0 - int1 addresses: - 10.26.98.92/29 parameters: mode: 802.3ad lacp-rate: slow mii-monitor-interval: 100 up-delay: 200 down-delay: 200 transmit-hash-policy: layer3+4 routes: - to: 10.0.0.0/8 via: 10.26.98.91 - to: 192.168.0.0/16 via: 10.26.98.91 - to: 188.42.208.0/21 via: 10.26.98.91 agge: interfaces: - ext0 - ext1 addresses: - 188.42.48.188/29 parameters: mode: 802.3ad lacp-rate: slow mii-monitor-interval: 100 up-delay: 200 down-delay: 200 transmit-hash-policy: layer3+4 gateway4: 188.42.48.187 ethernets: int0: match: macaddress: 68:05:ca:b8:f1:f8 int1: match: macaddress: 68:05:ca:b8:f1:f9 ext0: match: macaddress: 3c:ec:EF:e0:45:28 ext1: match: macaddress: 3c:EC:ef:e0:45:29 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud/testdata/metadata-nocloud.yaml ================================================ instance-id: 80d6927ecb30c1707b12f38ed1211535930ff16e local-hostname: talos-worker-3 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/aliases_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const aliasContextBase = `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" NAME = "test" ` // aliasContext builds a minimal context string for alias testing. func aliasContext(extra string) []byte { return []byte(aliasContextBase + extra) } func TestParseAliases(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) alias0IPv4 := network.AddressSpecSpec{ Address: netip.MustParsePrefix("192.168.1.100/24"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, } alias1IPv4 := network.AddressSpecSpec{ Address: netip.MustParsePrefix("192.168.1.101/24"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, } alias0IPv6 := network.AddressSpecSpec{ Address: netip.MustParsePrefix("2001:db8::100/64"), LinkName: "eth0", Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, } for _, tc := range []struct { name string extra string wantAliasAddr []network.AddressSpecSpec }{ { name: "IPv4 alias included", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv4}, }, { name: "EXTERNAL=YES skips alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "YES" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: nil, }, { name: "EXTERNAL=NO includes alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv4}, }, { name: "DETACH non-empty skips alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = "yes"`, wantAliasAddr: nil, }, { name: "DETACH empty includes alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv4}, }, { name: "both DETACH non-empty and EXTERNAL=YES skips alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "YES" ETH0_ALIAS0_DETACH = "yes"`, wantAliasAddr: nil, }, { name: "multiple aliases sorted deterministically", extra: `ETH0_ALIAS1_MAC = "02:00:c0:a8:01:65" ETH0_ALIAS1_IP = "192.168.1.101" ETH0_ALIAS1_MASK = "255.255.255.0" ETH0_ALIAS1_EXTERNAL = "NO" ETH0_ALIAS1_DETACH = "" ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, // ALIAS0 must appear before ALIAS1 regardless of map iteration order wantAliasAddr: []network.AddressSpecSpec{alias0IPv4, alias1IPv4}, }, { name: "no alias keys — no extra addresses", extra: "", wantAliasAddr: nil, }, { name: "IPv6 alias included", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv6}, }, { name: "ETH*_ALIAS*_IPV6 legacy alias used when IP6 absent", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IPV6 = "2001:db8::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv6}, }, { name: "IPv6 alias explicit prefix length respected", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_IP6_PREFIX_LENGTH = "48" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("2001:db8::100/48"), LinkName: "eth0", Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }, }, }, { name: "EXTERNAL=YES skips IPv6 alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_EXTERNAL = "YES" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: nil, }, { name: "DETACH non-empty skips IPv6 alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = "yes"`, wantAliasAddr: nil, }, { name: "mixed IPv4 and IPv6 aliases both emitted", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{alias0IPv4, alias0IPv6}, }, { name: "IPv6 ULA alias emits two IPv6 addresses", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_IP6_ULA = "fd00::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{ alias0IPv6, { Address: netip.MustParsePrefix("fd00::100/64"), LinkName: "eth0", Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }, }, }, { name: "EXTERNAL=YES skips IPv6 ULA alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6_ULA = "fd00::100" ETH0_ALIAS0_EXTERNAL = "YES" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: nil, }, { name: "DETACH non-empty skips IPv6 ULA alias", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6_ULA = "fd00::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = "yes"`, wantAliasAddr: nil, }, { name: "mixed IPv4 and IPv6 and ULA alias emits all three addresses", extra: `ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_IP6 = "2001:db8::100" ETH0_ALIAS0_IP6_ULA = "fd00::100" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`, wantAliasAddr: []network.AddressSpecSpec{ alias0IPv4, alias0IPv6, { Address: netip.MustParsePrefix("fd00::100/64"), LinkName: "eth0", Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }, }, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() networkConfig, err := o.ParseMetadata(st, aliasContext(tc.extra)) require.NoError(t, err) // The first address is always the primary ETH0 address; aliases follow. var aliasAddrs []network.AddressSpecSpec if len(networkConfig.Addresses) > 1 { aliasAddrs = networkConfig.Addresses[1:] } assert.Equal(t, tc.wantAliasAddr, aliasAddrs) }) } } func TestParseErrors(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) t.Run("malformed alias IPv4 returns descriptive error", func(t *testing.T) { t.Parallel() ctx := aliasContext(`ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP = "notanip" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`) _, err := o.ParseMetadata(st, ctx) require.ErrorContains(t, err, "ETH0_ALIAS0") require.ErrorContains(t, err, "IPv4") }) t.Run("malformed alias IPv6 returns descriptive error", func(t *testing.T) { t.Parallel() ctx := aliasContext(`ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6 = "notanip" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`) _, err := o.ParseMetadata(st, ctx) require.ErrorContains(t, err, "ETH0_ALIAS0") require.ErrorContains(t, err, "IPv6") }) t.Run("malformed alias IPv6 ULA returns error containing alias name and ULA", func(t *testing.T) { t.Parallel() ctx := aliasContext(`ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_IP6_ULA = "notanip" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_DETACH = ""`) _, err := o.ParseMetadata(st, ctx) require.ErrorContains(t, err, "ETH0_ALIAS0") require.ErrorContains(t, err, "ULA") }) t.Run("malformed interface IPv6 address returns descriptive error", func(t *testing.T) { t.Parallel() ctx := aliasContext("ETH0_IP6 = \"notanip\"") _, err := o.ParseMetadata(st, ctx) require.ErrorContains(t, err, "ETH0") require.ErrorContains(t, err, "IPv6") }) t.Run("malformed IPv6 gateway returns descriptive error", func(t *testing.T) { t.Parallel() ctx := aliasContext("ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"notanip\"") _, err := o.ParseMetadata(st, ctx) require.ErrorContains(t, err, "ETH0") require.ErrorContains(t, err, "gateway") }) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/dns_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" ) func TestDNSMerge(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) mac := `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" NAME = "test" ` for _, tc := range []struct { name string extra string wantDNS []string wantSearch []string wantNoResolver bool }{ { name: "global DNS only", extra: `DNS = "9.9.9.9 1.1.1.1"`, wantDNS: []string{"9.9.9.9", "1.1.1.1"}, wantSearch: nil, wantNoResolver: false, }, { name: "per-interface DNS only", extra: `ETH0_DNS = "192.168.1.1 8.8.8.8"`, wantDNS: []string{"192.168.1.1", "8.8.8.8"}, wantSearch: nil, wantNoResolver: false, }, { name: "global and per-interface DNS merged, global first", extra: "DNS = \"9.9.9.9\"\nETH0_DNS = \"192.168.1.1\"", wantDNS: []string{"9.9.9.9", "192.168.1.1"}, wantSearch: nil, wantNoResolver: false, }, { name: "global SEARCH_DOMAIN only", extra: `SEARCH_DOMAIN = "global.example.com"`, wantDNS: nil, wantSearch: []string{"global.example.com"}, wantNoResolver: false, }, { name: "per-interface search domain only", extra: `ETH0_SEARCH_DOMAIN = "example.com"`, wantDNS: nil, wantSearch: []string{"example.com"}, wantNoResolver: false, }, { name: "global and per-interface search domains merged, global first", extra: "SEARCH_DOMAIN = \"global.example.com\"\nETH0_SEARCH_DOMAIN = \"example.com\"", wantDNS: nil, wantSearch: []string{"global.example.com", "example.com"}, wantNoResolver: false, }, { name: "neither global nor per-interface set — no resolver emitted", extra: "", wantNoResolver: true, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() input := []byte(mac + tc.extra) networkConfig, err := o.ParseMetadata(st, input) require.NoError(t, err) if tc.wantNoResolver { assert.Empty(t, networkConfig.Resolvers) return } require.Len(t, networkConfig.Resolvers, 1) resolver := networkConfig.Resolvers[0] var dnsStrs []string for _, ip := range resolver.DNSServers { dnsStrs = append(dnsStrs, ip.String()) } assert.Equal(t, tc.wantDNS, dnsStrs) assert.Equal(t, tc.wantSearch, resolver.SearchDomains) }) } } func TestDNSMergeError(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) base := `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" NAME = "test" ` t.Run("malformed global DNS returns error", func(t *testing.T) { t.Parallel() _, err := o.ParseMetadata(st, []byte(base+`DNS = "notanip"`)) require.ErrorContains(t, err, "failed to parse global DNS server") require.ErrorContains(t, err, "notanip") }) t.Run("malformed per-interface DNS returns error with interface name", func(t *testing.T) { t.Parallel() _, err := o.ParseMetadata(st, []byte(base+`ETH0_DNS = "notanip"`)) require.ErrorContains(t, err, "ETH0") require.ErrorContains(t, err, "notanip") }) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/hostname_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" ) // minimalContext returns the minimum context bytes needed to exercise hostname // parsing without triggering ETH* processing. func minimalContext(vars string) []byte { return []byte("ETH0_MAC = \"02:00:c0:a8:01:5c\"\nETH0_IP = \"10.0.0.1\"\nETH0_MASK = \"255.255.255.0\"\n" + vars) } func TestSanitizeHostname(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) for _, tc := range []struct { name string setHostname string wantHostname string wantDomainname string }{ { name: "clean hostname passes through unchanged", setHostname: "myhost", wantHostname: "myhost", wantDomainname: "", }, { name: "FQDN is split on first dot", setHostname: "myhost.example.com", wantHostname: "myhost", wantDomainname: "example.com", }, { name: "invalid chars replaced with hyphen", setHostname: "my_host", wantHostname: "my-host", wantDomainname: "", }, { name: "leading and trailing hyphens stripped", setHostname: "-myhost-", wantHostname: "myhost", wantDomainname: "", }, { name: "per-label hyphen trimming", setHostname: "my-.host", wantHostname: "my", wantDomainname: "host", }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() ctx := minimalContext("SET_HOSTNAME = \"" + tc.setHostname + "\"") networkConfig, err := o.ParseMetadata(st, ctx) require.NoError(t, err) require.Len(t, networkConfig.Hostnames, 1) assert.Equal(t, tc.wantHostname, networkConfig.Hostnames[0].Hostname) assert.Equal(t, tc.wantDomainname, networkConfig.Hostnames[0].Domainname) }) } t.Run("empty SET_HOSTNAME produces no hostname entry", func(t *testing.T) { t.Parallel() ctx := minimalContext("SET_HOSTNAME = \"\"") networkConfig, err := o.ParseMetadata(st, ctx) require.NoError(t, err) assert.Empty(t, networkConfig.Hostnames) }) } func TestParseMetadataHostname(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) for _, tc := range []struct { name string vars string wantHostnames int wantHostname string wantDomainname string }{ { name: "SET_HOSTNAME is used as hostname", vars: "SET_HOSTNAME = \"myhost\"", wantHostnames: 1, wantHostname: "myhost", }, { name: "FQDN in SET_HOSTNAME is split into Hostname and Domainname", vars: "SET_HOSTNAME = \"myhost.example.com\"", wantHostnames: 1, wantHostname: "myhost", wantDomainname: "example.com", }, { name: "HOSTNAME variable is ignored", vars: "HOSTNAME = \"fromhostname\"", wantHostnames: 0, }, { name: "NAME variable is ignored", vars: "NAME = \"fromname\"", wantHostnames: 0, }, { name: "SET_HOSTNAME takes precedence over HOSTNAME and NAME", vars: "SET_HOSTNAME = \"correct\"\nHOSTNAME = \"wrong\"\nNAME = \"alsowrong\"", wantHostnames: 1, wantHostname: "correct", }, { name: "DNS_HOSTNAME=YES is not used as a hostname value", vars: "DNS_HOSTNAME = \"YES\"", wantHostnames: 0, }, { name: "absent SET_HOSTNAME produces no hostname entry", vars: "", wantHostnames: 0, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() ctx := minimalContext(tc.vars) networkConfig, err := o.ParseMetadata(st, ctx) require.NoError(t, err) require.Len(t, networkConfig.Hostnames, tc.wantHostnames) if tc.wantHostnames > 0 { assert.Equal(t, tc.wantHostname, networkConfig.Hostnames[0].Hostname) assert.Equal(t, tc.wantDomainname, networkConfig.Hostnames[0].Domainname) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/ipv6_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const ipv6ContextBase = `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" NAME = "test" ` func ipv6Context(extra string) []byte { return []byte(ipv6ContextBase + extra) } func TestParseIPv6(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) gw6Route := func(gw string, priority uint32) network.RouteSpecSpec { return network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: netip.MustParseAddr(gw), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: priority, Scope: nethelpers.ScopeGlobal, } } dhcp6Op := func(metric uint32) network.OperatorSpecSpec { return network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: "eth0", RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: metric, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, } } for _, tc := range []struct { name string extra string wantAddrs []netip.Prefix wantRoutes []network.RouteSpecSpec wantOperators []network.OperatorSpecSpec }{ { name: "static IPv6 address with explicit prefix length", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_PREFIX_LENGTH = \"48\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/48")}, }, { name: "ETH*_IPV6 legacy alias used when ETH*_IP6 absent", extra: "ETH0_IPV6 = \"2001:db8::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, }, { name: "prefix length defaults to 64", extra: "ETH0_IP6 = \"2001:db8::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, }, { name: "explicit prefix length respected", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_PREFIX_LENGTH = \"56\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/56")}, }, { name: "ULA address emitted as second AddressSpecSpec", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_ULA = \"fd00::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64"), netip.MustParsePrefix("fd00::1/64")}, }, { name: "IPv6 gateway emits default route with metric 1", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 1)}, }, { name: "ETH*_GATEWAY6 legacy alias used when ETH*_IP6_GATEWAY absent", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_GATEWAY6 = \"2001:db8::fffe\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 1)}, }, { name: "ETH*_IP6_METRIC overrides default metric of 1", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"\nETH0_IP6_METRIC = \"100\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 100)}, }, { name: "no IPv6 variables — no IPv6 output", extra: "", }, { name: "IP6_METHOD=dhcp emits OperatorDHCP6 with default metric 1", extra: "ETH0_IP6_METHOD = \"dhcp\"", wantOperators: []network.OperatorSpecSpec{dhcp6Op(1)}, }, { name: "IP6_METHOD=dhcp with IP6_METRIC uses custom metric", extra: "ETH0_IP6_METHOD = \"dhcp\"\nETH0_IP6_METRIC = \"200\"", wantOperators: []network.OperatorSpecSpec{dhcp6Op(200)}, }, { name: "IP6_METHOD=auto emits nothing", extra: "ETH0_IP6_METHOD = \"auto\"\nETH0_IP6 = \"2001:db8::1\"", }, { name: "IP6_METHOD=disable emits nothing even if IP6 is set", extra: "ETH0_IP6_METHOD = \"disable\"\nETH0_IP6 = \"2001:db8::1\"", }, { name: "IP6_METHOD=static with IP6 set emits address", extra: "ETH0_IP6_METHOD = \"static\"\nETH0_IP6 = \"2001:db8::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, }, { name: "IP6_METHOD absent and IP6 set uses static path", extra: "ETH0_IP6 = \"2001:db8::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, }, { name: "METHOD=dhcp with no IP6_METHOD inherits dhcp and emits OperatorDHCP6", extra: "ETH0_METHOD = \"dhcp\"", wantOperators: []network.OperatorSpecSpec{dhcp6Op(1)}, }, { name: "METHOD=dhcp with IP6_METHOD=static and IP6 set uses static IPv6", extra: "ETH0_METHOD = \"dhcp\"\nETH0_IP6_METHOD = \"static\"\nETH0_IP6 = \"2001:db8::1\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, }, { name: "METHOD=dhcp with IP6_METHOD=disable emits no IPv6 config", extra: "ETH0_METHOD = \"dhcp\"\nETH0_IP6_METHOD = \"disable\"", }, { name: "METHOD=static with no IP6_METHOD and no IP6 emits no IPv6 config", extra: "ETH0_METHOD = \"static\"", }, { name: "METRIC=200 with no IP6_METRIC cascades to IPv6 gateway metric", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"\nETH0_METRIC = \"200\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 200)}, }, { name: "METRIC=200 with IP6_METRIC=50 uses explicit IP6_METRIC", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"\nETH0_METRIC = \"200\"\nETH0_IP6_METRIC = \"50\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 50)}, }, { name: "METRIC=200 with IP6_METHOD=dhcp and no IP6_METRIC cascades to DHCPv6 metric", extra: "ETH0_IP6_METHOD = \"dhcp\"\nETH0_METRIC = \"200\"", wantOperators: []network.OperatorSpecSpec{dhcp6Op(200)}, }, { name: "no METRIC and no IP6_METRIC uses IPv6 default of 1", extra: "ETH0_IP6 = \"2001:db8::1\"\nETH0_IP6_GATEWAY = \"2001:db8::fffe\"", wantAddrs: []netip.Prefix{netip.MustParsePrefix("2001:db8::1/64")}, wantRoutes: []network.RouteSpecSpec{gw6Route("2001:db8::fffe", 1)}, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() networkConfig, err := o.ParseMetadata(st, ipv6Context(tc.extra)) require.NoError(t, err) var ip6Addrs []netip.Prefix for _, a := range networkConfig.Addresses { if a.Family == nethelpers.FamilyInet6 { ip6Addrs = append(ip6Addrs, a.Address) } } assert.Equal(t, tc.wantAddrs, ip6Addrs) var ip6Routes []network.RouteSpecSpec for _, r := range networkConfig.Routes { if r.Family == nethelpers.FamilyInet6 { ip6Routes = append(ip6Routes, r) } } assert.Equal(t, tc.wantRoutes, ip6Routes) var ip6Operators []network.OperatorSpecSpec for _, op := range networkConfig.Operators { if op.Operator == network.OperatorDHCP6 { ip6Operators = append(ip6Operators, op) } } assert.Equal(t, tc.wantOperators, ip6Operators) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package opennebula provides the OpenNebula platform implementation. package opennebula import ( "context" "fmt" "log" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/xfs" ) const ( configISOLabel = "context" oneContextPath = "context.sh" ) func (o *OpenNebula) contextFromCD(ctx context.Context, r state.State) (oneContext []byte, err error) { err = blockutils.ReadFromVolume(ctx, r, []string{strings.ToLower(configISOLabel), strings.ToUpper(configISOLabel)}, func(root xfs.Root, volumeStatus *block.VolumeStatus) error { log.Printf("found config disk (context) at %s", volumeStatus.TypedSpec().Location) log.Printf("fetching context from: %s/", oneContextPath) oneContext, err = xfs.ReadFile(root, oneContextPath) if err != nil { return fmt.Errorf("read config: %w", err) } return nil }, ) if err != nil { return nil, err } if oneContext == nil { return nil, errors.ErrNoConfigSource } return oneContext, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/onegate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "net/netip" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // staticIfaceContext builds a minimal context with a static ETH0 and an // optional ONEGATE_ENDPOINT. func staticIfaceContext(endpoint string) []byte { ctx := `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" ` if endpoint != "" { ctx += `ONEGATE_ENDPOINT = "` + endpoint + `"` + "\n" } return []byte(ctx) } func linkLocalRoute(ip, outLink string) network.RouteSpecSpec { return network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: netip.PrefixFrom(netip.MustParseAddr(ip), 32), OutLinkName: outLink, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeLink, } } func scopeLinkRoutes(routes []network.RouteSpecSpec) []network.RouteSpecSpec { var out []network.RouteSpecSpec for _, r := range routes { if r.Scope == nethelpers.ScopeLink { out = append(out, r) } } return out } func TestOnegateProxyRoute(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) tests := []struct { name string endpoint string wantRoute *network.RouteSpecSpec }{ { name: "link-local with port and path emits scope-link /32 route", endpoint: "http://169.254.16.9:5030/RPC2", wantRoute: func() *network.RouteSpecSpec { r := linkLocalRoute("169.254.16.9", "eth0") return &r }(), }, { name: "link-local without port emits route", endpoint: "http://169.254.16.9/RPC2", wantRoute: func() *network.RouteSpecSpec { r := linkLocalRoute("169.254.16.9", "eth0") return &r }(), }, { name: "non-link-local IP emits no route", endpoint: "http://10.0.0.1:5030/RPC2", wantRoute: nil, }, { name: "absent ONEGATE_ENDPOINT emits no route", endpoint: "", wantRoute: nil, }, { name: "IPv6 URL emits no route", endpoint: "http://[::1]:5030/RPC2", wantRoute: nil, }, { name: "malformed endpoint emits no route without panic", endpoint: "not-a-url", wantRoute: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() cfg, err := o.ParseMetadata(st, staticIfaceContext(tt.endpoint)) require.NoError(t, err) routes := scopeLinkRoutes(cfg.Routes) if tt.wantRoute == nil { assert.Empty(t, routes) } else { require.Len(t, routes, 1) assert.Equal(t, *tt.wantRoute, routes[0]) } }) } } func TestOnegateRouteAttachedToFirstStaticInterface(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) // ETH0=dhcp, ETH1=static — route must be on eth1 (first static). ctx := []byte(`ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_METHOD = "dhcp" ETH1_MAC = "02:00:c0:a8:01:5d" ETH1_IP = "192.168.1.92" ETH1_MASK = "255.255.255.0" ONEGATE_ENDPOINT = "http://169.254.16.9:5030/RPC2" `) cfg, err := o.ParseMetadata(st, ctx) require.NoError(t, err) routes := scopeLinkRoutes(cfg.Routes) require.Len(t, routes, 1) assert.Equal(t, "eth1", routes[0].OutLinkName) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/opennebula.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula import ( "bytes" "context" "encoding/base64" stderrors "errors" "fmt" "net/netip" "slices" "strconv" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-envparse" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/address" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) const methodSkip = "skip" // OpenNebula is the concrete type that implements the runtime.Platform interface. type OpenNebula struct{} // Name implements the runtime.Platform interface. func (o *OpenNebula) Name() string { return "opennebula" } // isDigitsOnly returns true if s is non-empty and contains only ASCII digits. func isDigitsOnly(s string) bool { for _, c := range s { if c < '0' || c > '9' { return false } } return s != "" } // collectAliasNames scans oneContext for keys of the form // _MAC and returns the sorted list of alias base names // (e.g. "ETH0_ALIAS0", "ETH0_ALIAS1"). func collectAliasNames(oneContext map[string]string, aliasPrefix string) []string { seen := map[string]bool{} var aliasNames []string for key := range oneContext { if !strings.HasPrefix(key, aliasPrefix) || !strings.HasSuffix(key, "_MAC") { continue } middle := strings.TrimPrefix(strings.TrimSuffix(key, "_MAC"), aliasPrefix) if !isDigitsOnly(middle) { continue } aliasName := aliasPrefix + middle if !seen[aliasName] { seen[aliasName] = true aliasNames = append(aliasNames, aliasName) } } slices.Sort(aliasNames) return aliasNames } // parseAlias parses the addresses for a single alias entry. Returns nil, nil // when the alias should be skipped (DETACH non-empty or EXTERNAL=YES). func parseAlias(oneContext map[string]string, aliasName, ifaceNameLower string) ([]network.AddressSpecSpec, error) { // Skip detached aliases — reference: [ -z "${detach}" ] if oneContext[aliasName+"_DETACH"] != "" { return nil, nil } // Skip externally managed aliases — reference: ! is_true "${external}" if strings.EqualFold(oneContext[aliasName+"_EXTERNAL"], "yes") { return nil, nil } var addrs []network.AddressSpecSpec if ipStr := oneContext[aliasName+"_IP"]; ipStr != "" { ipPrefix, err := address.IPPrefixFrom(ipStr, oneContext[aliasName+"_MASK"]) if err != nil { return nil, fmt.Errorf("alias %s: failed to parse IPv4: %w", aliasName, err) } addrs = append(addrs, network.AddressSpecSpec{ Address: ipPrefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } ip6Str := oneContext[aliasName+"_IP6"] if ip6Str == "" { ip6Str = oneContext[aliasName+"_IPV6"] } if ip6Str != "" { ip6Prefix, err := ip6PrefixFrom(ip6Str, oneContext[aliasName+"_IP6_PREFIX_LENGTH"]) if err != nil { return nil, fmt.Errorf("alias %s: failed to parse IPv6: %w", aliasName, err) } addrs = append(addrs, network.AddressSpecSpec{ Address: ip6Prefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } if ulaStr := oneContext[aliasName+"_IP6_ULA"]; ulaStr != "" { ulaPrefix, err := ip6PrefixFrom(ulaStr, "64") if err != nil { return nil, fmt.Errorf("alias %s: failed to parse IPv6 ULA: %w", aliasName, err) } addrs = append(addrs, network.AddressSpecSpec{ Address: ulaPrefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } return addrs, nil } // parseAliases collects ETHn_ALIASm_* address entries for a given interface. // An alias is skipped when DETACH is non-empty OR EXTERNAL=YES, matching the // reference netcfg-networkd behavior (lines 395-400). func parseAliases(oneContext map[string]string, ifaceName, ifaceNameLower string) ([]network.AddressSpecSpec, error) { aliasNames := collectAliasNames(oneContext, ifaceName+"_ALIAS") var addrs []network.AddressSpecSpec for _, aliasName := range aliasNames { aliasAddrs, err := parseAlias(oneContext, aliasName, ifaceNameLower) if err != nil { return nil, err } addrs = append(addrs, aliasAddrs...) } return addrs, nil } // sanitizeHostname replaces characters invalid in DNS labels with hyphens, // strips leading/trailing hyphens from the whole string and from each label. // This mirrors the reference sanitization in one-apps/context-linux: // // sed -e 's/[^-a-zA-Z0-9\.]/-/g' -e 's/^-*//g' -e 's/-*$//g' // // Talos is intentionally stricter: it also trims hyphens per-label so every // label is RFC-1123-valid (no label may start or end with a hyphen). func sanitizeHostname(raw string) string { var b strings.Builder for _, r := range raw { switch { case r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r >= '0' && r <= '9', r == '-', r == '.': b.WriteRune(r) default: b.WriteRune('-') } } s := strings.Trim(b.String(), "-") labels := strings.Split(s, ".") for i, l := range labels { labels[i] = strings.Trim(l, "-") } return strings.Join(labels, ".") } // parseRouteFields extracts the destination prefix, gateway string, and optional // metric string from the fields of a single route entry. // // The reference one-apps implementation (context-linux) always parses routes as: // // rsplit=( ${route} ); dst="${rsplit[0]}"; gw="${rsplit[2]}" // // meaning token[1] is always skipped and the gateway is always at token[2]. // The canonical format is "DEST/PREFIX via GW" where "via" occupies token[1]. // The legacy dotted-mask format "DEST MASK GW" follows the same index layout. // // As a Talos extension, an optional bare metric may follow the gateway. func parseRouteFields(parts []string) (dest netip.Prefix, gwStr, metricStr string, err error) { // Both CIDR ("DEST/PREFIX via GW") and legacy ("DEST MASK GW") formats // require at least 3 tokens, with the gateway always at index 2. if len(parts) < 3 { return dest, "", "", fmt.Errorf("expected at least 3 fields (DEST/PREFIX via GW or DEST MASK GW)") } if strings.Contains(parts[0], "/") { // CIDR format: "DEST/PREFIX via GW [METRIC]" // parts[1] is the separator token (conventionally "via") and is skipped, // matching the reference rsplit[1] which is never read. dest, err = netip.ParsePrefix(parts[0]) if err != nil { return dest, "", "", fmt.Errorf("failed to parse destination: %w", err) } dest = dest.Masked() } else { // Legacy format: "DEST MASK GW [METRIC]" var prefix netip.Prefix prefix, err = address.IPPrefixFrom(parts[0], parts[1]) if err != nil { return dest, "", "", fmt.Errorf("failed to parse destination: %w", err) } dest = prefix.Masked() } gwStr = parts[2] if len(parts) >= 4 { metricStr = parts[3] } return dest, gwStr, metricStr, nil } // parseRouteEntry parses a single trimmed route entry into a RouteSpecSpec. func parseRouteEntry(entry, linkName string) (network.RouteSpecSpec, error) { dest, gwStr, metricStr, err := parseRouteFields(strings.Fields(entry)) if err != nil { return network.RouteSpecSpec{}, fmt.Errorf("route entry %q: %w", entry, err) } gw, err := netip.ParseAddr(gwStr) if err != nil { return network.RouteSpecSpec{}, fmt.Errorf("route entry %q: failed to parse gateway: %w", entry, err) } metric := uint32(network.DefaultRouteMetric) if metricStr != "" { m, err := strconv.ParseUint(metricStr, 10, 32) if err != nil { return network.RouteSpecSpec{}, fmt.Errorf("route entry %q: failed to parse metric: %w", entry, err) } metric = uint32(m) } family := nethelpers.FamilyInet4 if gw.Is6() { family = nethelpers.FamilyInet6 } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: dest, Gateway: gw, OutLinkName: linkName, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: metric, } route.Normalize() return route, nil } // ParseRoutes parses the ETH*_ROUTES variable into RouteSpecSpec entries. // Multiple routes are separated by commas. func ParseRoutes(routesStr, linkName string) ([]network.RouteSpecSpec, error) { var routes []network.RouteSpecSpec for entry := range strings.SplitSeq(routesStr, ",") { entry = strings.TrimSpace(entry) if entry == "" { continue } route, err := parseRouteEntry(entry, linkName) if err != nil { return nil, err } routes = append(routes, route) } return routes, nil } // parseIPv4StaticConfig handles the static addressing path for an interface: // address, link, gateway route, extra static routes, and per-interface DNS. func parseIPv4StaticConfig( oneContext map[string]string, ifaceName, ifaceNameLower string, routeMetric uint32, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string, ) error { ipPrefix, err := address.IPPrefixFrom(oneContext[ifaceName+"_IP"], oneContext[ifaceName+"_MASK"]) if err != nil { return fmt.Errorf("failed to parse IP address: %w", err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ Address: ipPrefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), AnnounceWithARP: false, ConfigLayer: network.ConfigPlatform, }, ) var mtu uint32 if mtuStr := oneContext[ifaceName+"_MTU"]; mtuStr != "" { mtu64, err := strconv.ParseUint(mtuStr, 10, 32) if err != nil { return fmt.Errorf("failed to parse MTU: %w", err) } mtu = uint32(mtu64) } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: ifaceNameLower, Logical: false, Up: true, MTU: mtu, Kind: "", Type: nethelpers.LinkEther, ParentName: "", ConfigLayer: network.ConfigPlatform, }, ) if gwStr := oneContext[ifaceName+"_GATEWAY"]; gwStr != "" { gateway, err := netip.ParseAddr(gwStr) if err != nil { return fmt.Errorf("failed to parse gateway ip: %w", err) } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gateway, OutLinkName: ifaceNameLower, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: routeMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } if routesStr := oneContext[ifaceName+"_ROUTES"]; routesStr != "" { staticRoutes, err := ParseRoutes(routesStr, ifaceNameLower) if err != nil { return fmt.Errorf("interface %s: %w", ifaceName, err) } networkConfig.Routes = append(networkConfig.Routes, staticRoutes...) } for s := range strings.FieldsSeq(oneContext[ifaceName+"_DNS"]) { ip, err := netip.ParseAddr(s) if err != nil { return fmt.Errorf("interface %s: failed to parse DNS server %q: %w", ifaceName, s, err) } *allDNSIPs = append(*allDNSIPs, ip) } *allSearchDomains = append(*allSearchDomains, strings.Fields(oneContext[ifaceName+"_SEARCH_DOMAIN"])...) return nil } // parseIPv4Metric reads ETH*_METRIC and returns the parsed value, or 0 when // the variable is absent. Callers apply their own default (e.g. // network.DefaultRouteMetric for IPv4, 1 for IPv6 via parseIPv6Metric). func parseIPv4Metric(oneContext map[string]string, ifaceName string) (uint32, error) { if metricStr := oneContext[ifaceName+"_METRIC"]; metricStr != "" { m, err := strconv.ParseUint(metricStr, 10, 32) if err != nil { return 0, fmt.Errorf("interface %s: failed to parse metric: %w", ifaceName, err) } return uint32(m), nil } return 0, nil } // parseIPv6Metric reads ETH*_IP6_METRIC; falls back to ipv4Metric (when > 0), // then to 1, matching the reference [ -z "$ip6_metric" ] && ip6_metric="${metric}". func parseIPv6Metric(oneContext map[string]string, ifaceName string, ipv4Metric uint32) (uint32, error) { if metricStr := oneContext[ifaceName+"_IP6_METRIC"]; metricStr != "" { m, err := strconv.ParseUint(metricStr, 10, 32) if err != nil { return 0, fmt.Errorf("interface %s: failed to parse IPv6 metric: %w", ifaceName, err) } return uint32(m), nil } if ipv4Metric > 0 { return ipv4Metric, nil } return 1, nil } // parseInterfaceIPv4 configures the IPv4 stack for one interface. // Dispatches to DHCP4 operator or static config based on ETH*_METHOD. func parseInterfaceIPv4( oneContext map[string]string, ifaceName, ifaceNameLower string, routeMetric uint32, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string, ) error { if oneContext[ifaceName+"_METHOD"] == methodSkip { return nil } if routeMetric == 0 { routeMetric = uint32(network.DefaultRouteMetric) } if oneContext[ifaceName+"_METHOD"] == "dhcp" { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: ifaceNameLower, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: routeMetric, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, }, ) return nil } return parseIPv4StaticConfig(oneContext, ifaceName, ifaceNameLower, routeMetric, networkConfig, allDNSIPs, allSearchDomains) } // ip6PrefixFrom builds a netip.Prefix from an IPv6 address string and an // optional prefix-length string (default 64). The prefix is not masked so the // full host address is preserved on the interface. func ip6PrefixFrom(ipStr, prefixLenStr string) (netip.Prefix, error) { ip, err := netip.ParseAddr(ipStr) if err != nil { return netip.Prefix{}, fmt.Errorf("failed to parse IPv6 address %q: %w", ipStr, err) } bits := 64 if prefixLenStr != "" { n, err := strconv.Atoi(prefixLenStr) if err != nil { return netip.Prefix{}, fmt.Errorf("failed to parse IPv6 prefix length %q: %w", prefixLenStr, err) } bits = n } return netip.PrefixFrom(ip, bits), nil } // parseIPv6Gateway reads ETH*_IP6_GATEWAY (or legacy GATEWAY6) and emits the // default IPv6 route (::/0) with metric from parseIPv6Metric. func parseIPv6Gateway(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error { gwStr := oneContext[ifaceName+"_IP6_GATEWAY"] if gwStr == "" { gwStr = oneContext[ifaceName+"_GATEWAY6"] } if gwStr == "" { return nil } gw, err := netip.ParseAddr(gwStr) if err != nil { return fmt.Errorf("interface %s: failed to parse IPv6 gateway %q: %w", ifaceName, gwStr, err) } metric, err := parseIPv6Metric(oneContext, ifaceName, ipv4Metric) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: ifaceNameLower, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: metric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) return nil } // parseIPv6DHCP emits a DHCPv6 operator for an interface, with metric from // parseIPv6Metric. func parseIPv6DHCP(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error { metric, err := parseIPv6Metric(oneContext, ifaceName, ipv4Metric) if err != nil { return err } networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: ifaceNameLower, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: metric, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, }) return nil } // parseInterfaceIPv6 configures the IPv6 stack for one interface. // Dispatches on the effective IP6_METHOD: disable/skip (no-op), auto (SLAAC), // dhcp (DHCPv6 operator), or static/empty (static address path). // When IP6_METHOD is unset, ipv4Method is used as fallback, matching the // reference: [ -z "$ip6_method" ] && ip6_method="${method}". func parseInterfaceIPv6(oneContext map[string]string, ifaceName, ifaceNameLower string, ipv4Method string, ipv4Metric uint32, networkConfig *runtime.PlatformNetworkConfig) error { ip6Method := strings.ToLower(oneContext[ifaceName+"_IP6_METHOD"]) if ip6Method == "" { ip6Method = ipv4Method } switch ip6Method { case "disable", methodSkip: return nil case "auto": // SLAAC: the kernel accepts Router Advertisements by default in Talos; // no operator or sysctl is required to enable address auto-configuration. return nil case "dhcp": return parseIPv6DHCP(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig) } ip6Str := oneContext[ifaceName+"_IP6"] if ip6Str == "" { ip6Str = oneContext[ifaceName+"_IPV6"] } prefixLenStr := oneContext[ifaceName+"_IP6_PREFIX_LENGTH"] if ip6Str != "" { ip6Prefix, err := ip6PrefixFrom(ip6Str, prefixLenStr) if err != nil { return fmt.Errorf("interface %s: %w", ifaceName, err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ Address: ip6Prefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } if ulaStr := oneContext[ifaceName+"_IP6_ULA"]; ulaStr != "" { ulaPrefix, err := ip6PrefixFrom(ulaStr, "64") if err != nil { return fmt.Errorf("interface %s ULA: %w", ifaceName, err) } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ Address: ulaPrefix, LinkName: ifaceNameLower, Family: nethelpers.FamilyInet6, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } return parseIPv6Gateway(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig) } // parseInterface runs all per-interface configuration (IPv4, IPv6, aliases). func parseInterface(oneContext map[string]string, ifaceName string, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string) error { ifaceNameLower := strings.ToLower(ifaceName) ipv4Method := strings.ToLower(oneContext[ifaceName+"_METHOD"]) ip6Method := strings.ToLower(oneContext[ifaceName+"_IP6_METHOD"]) if ip6Method == "" { ip6Method = ipv4Method } if ipv4Method == methodSkip && (ip6Method == "" || ip6Method == methodSkip || ip6Method == "disable") { return nil } ipv4Metric, err := parseIPv4Metric(oneContext, ifaceName) if err != nil { return err } if err := parseInterfaceIPv4(oneContext, ifaceName, ifaceNameLower, ipv4Metric, networkConfig, allDNSIPs, allSearchDomains); err != nil { return err } if err := parseInterfaceIPv6(oneContext, ifaceName, ifaceNameLower, ipv4Method, ipv4Metric, networkConfig); err != nil { return err } aliasAddrs, err := parseAliases(oneContext, ifaceName, ifaceNameLower) if err != nil { return err } networkConfig.Addresses = append(networkConfig.Addresses, aliasAddrs...) return nil } // ethInterfaceName returns the interface name (e.g. "ETH0") from a context map // key of the form ETH_MAC, or ("", false) for any other key. func ethInterfaceName(key string) (string, bool) { if !strings.HasPrefix(key, "ETH") || !strings.HasSuffix(key, "_MAC") { return "", false } name := strings.TrimSuffix(key, "_MAC") if !isDigitsOnly(strings.TrimPrefix(name, "ETH")) { return "", false } return name, true } // resolveHostname reads SET_HOSTNAME from the context map and sanitizes it, // matching the reference net-15-hostname script precedence. HOSTNAME and NAME // are not used — the reference never reads them for hostname configuration. // DNS_HOSTNAME is a server-side flag that triggers a reverse DNS lookup // (a live network operation) and cannot be honored inside ParseMetadata. func resolveHostname(oneContext map[string]string) string { return sanitizeHostname(oneContext["SET_HOSTNAME"]) } // extractIPv4FromEndpoint extracts the host IPv4 address from a URL-like // string (e.g. "http://169.254.16.9:5030"). Returns an invalid Addr if no // IPv4 address can be parsed from the host portion. func extractIPv4FromEndpoint(endpoint string) netip.Addr { s := endpoint // Strip scheme (e.g. "http://"). if idx := strings.Index(s, "://"); idx >= 0 { s = s[idx+3:] } // Strip path, query, and port in order to isolate the bare host. for _, sep := range []string{"/", "?", ":"} { if idx := strings.Index(s, sep); idx >= 0 { s = s[:idx] } } addr, err := netip.ParseAddr(s) if err != nil { return netip.Addr{} } return addr } // parseOnegateProxyRoute emits a /32 scope-link host route to the ONEGATE // endpoint when its host is a link-local IPv4 address (169.254.x.x). The // route is attached to outLink (the first static interface), matching the // reference add_onegate_proxy_route behavior. func parseOnegateProxyRoute(oneContext map[string]string, outLink string, networkConfig *runtime.PlatformNetworkConfig) { endpoint := oneContext["ONEGATE_ENDPOINT"] if endpoint == "" { return } ip := extractIPv4FromEndpoint(endpoint) if !ip.IsValid() || !ip.Is4() || !ip.IsLinkLocalUnicast() { return } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: netip.PrefixFrom(ip, 32), OutLinkName: outLink, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeLink, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } // processInterfaces iterates ETHn interfaces in sorted order, configures each // one, and returns the name of the first static interface link (used to attach // the ONEGATE proxy route). Sorted order matches the reference behavior of // env | grep ... | sort (ETH0, ETH1, ETH2, ...). func processInterfaces( oneContext map[string]string, networkConfig *runtime.PlatformNetworkConfig, allDNSIPs *[]netip.Addr, allSearchDomains *[]string, ) (firstStaticLink string, err error) { var ifaceNames []string for key := range oneContext { if ifaceName, ok := ethInterfaceName(key); ok { ifaceNames = append(ifaceNames, ifaceName) } } slices.Sort(ifaceNames) for _, ifaceName := range ifaceNames { if err := parseInterface(oneContext, ifaceName, networkConfig, allDNSIPs, allSearchDomains); err != nil { return "", err } if firstStaticLink == "" { method := strings.ToLower(oneContext[ifaceName+"_METHOD"]) if method == "" || method == "static" { firstStaticLink = strings.ToLower(ifaceName) } } } return firstStaticLink, nil } // ParseMetadata converts opennebula metadata to platform network config. func (o *OpenNebula) ParseMetadata(st state.State, oneContextPlain []byte) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} oneContext, err := envparse.Parse(bytes.NewReader(oneContextPlain)) if err != nil { return nil, fmt.Errorf("failed to parse context file %q: %w", oneContextPlain, err) } hostnameValue := resolveHostname(oneContext) // Seed the merged DNS/search-domain slices with global variables (DNS, // SEARCH_DOMAIN). These are applied regardless of interface, matching the // reference get_nameservers()/get_searchdomains() which processes global // variables before per-interface ones. var allDNSIPs []netip.Addr for s := range strings.FieldsSeq(oneContext["DNS"]) { ip, err := netip.ParseAddr(s) if err != nil { return nil, fmt.Errorf("failed to parse global DNS server %q: %w", s, err) } allDNSIPs = append(allDNSIPs, ip) } allSearchDomains := append([]string(nil), strings.Fields(oneContext["SEARCH_DOMAIN"])...) firstStaticLink, err := processInterfaces(oneContext, networkConfig, &allDNSIPs, &allSearchDomains) if err != nil { return nil, err } if firstStaticLink != "" { parseOnegateProxyRoute(oneContext, firstStaticLink, networkConfig) } if len(allDNSIPs)+len(allSearchDomains) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: allDNSIPs, SearchDomains: allSearchDomains, ConfigLayer: network.ConfigPlatform, }) } hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if hostnameValue != "" { if err := hostnameSpec.ParseFQDN(hostnameValue); err != nil { return nil, fmt.Errorf("failed to parse hostname: %w", err) } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: o.Name(), Hostname: hostnameSpec.Hostname, InstanceID: oneContext["VMID"], } return networkConfig, nil } // Configuration implements the runtime.Platform interface. func (o *OpenNebula) Configuration(ctx context.Context, r state.State) (machineConfig []byte, err error) { oneContextPlain, err := o.contextFromCD(ctx, r) if err != nil { return nil, err } oneContext, err := envparse.Parse(bytes.NewReader(oneContextPlain)) if err != nil { return nil, fmt.Errorf("failed to parse environment file %q: %w", oneContextPlain, err) } userData, ok := oneContext["USER_DATA"] if !ok { // Legacy fallback: reference does USER_DATA="${USER_DATA:-${USERDATA}}". userData, ok = oneContext["USERDATA"] } if !ok { return nil, errors.ErrNoConfigSource } machineConfig, err = base64.StdEncoding.DecodeString(userData) if err != nil { return nil, fmt.Errorf("failed to decode USER_DATA: %v", err) } return machineConfig, nil } // Mode implements the runtime.Platform interface. func (o *OpenNebula) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (o *OpenNebula) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (o *OpenNebula) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { oneContext, err := o.contextFromCD(ctx, st) if stderrors.Is(err, errors.ErrNoConfigSource) { err = nil } if err != nil { return err } networkConfig, err := o.ParseMetadata(st, oneContext) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/opennebula_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( _ "embed" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" ) //go:embed testdata/metadata.yaml var oneContextPlain []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string //go:embed testdata/metadata_no_network_flag.yaml var oneContextPlainNoNetworkFlag []byte //go:embed testdata/expected_no_network_flag.yaml var expectedNetworkConfigNoNetworkFlag string func TestParseMetadata(t *testing.T) { o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) networkConfig, err := o.ParseMetadata(st, oneContextPlain) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } // TestParseMetadataNoNetworkFlag verifies that ETH*_ variables are processed // regardless of the NETWORK context variable value. NETWORK=YES is a // server-side OpenNebula directive and should not gate guest-side processing. func TestParseMetadataNoNetworkFlag(t *testing.T) { o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) networkConfig, err := o.ParseMetadata(st, oneContextPlainNoNetworkFlag) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedNetworkConfigNoNetworkFlag, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/routes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestParseRoutes(t *testing.T) { t.Parallel() for _, tc := range []struct { name string routesStr string linkName string expected []network.RouteSpecSpec errMsg string }{ { name: "empty string", routesStr: "", linkName: "eth0", expected: nil, }, { name: "whitespace only", routesStr: " , , ", linkName: "eth0", expected: nil, }, { name: "legacy single route default metric", routesStr: "10.0.0.0 255.0.0.0 192.168.1.1", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("10.0.0.0/8"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "legacy single route custom metric", routesStr: "172.16.0.0 255.255.0.0 192.168.1.1 500", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("172.16.0.0/16"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: 500, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "cidr single route", routesStr: "10.0.0.0/8 via 192.168.1.1", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("10.0.0.0/8"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "cidr single route with metric", routesStr: "10.0.0.0/8 via 192.168.1.1 200", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("10.0.0.0/8"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: 200, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "multiple routes comma separated", routesStr: "10.0.0.0 255.0.0.0 192.168.1.1, 172.16.0.0 255.255.0.0 192.168.1.1 500", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("10.0.0.0/8"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, Scope: nethelpers.ScopeGlobal, }, { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("172.16.0.0/16"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: 500, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "cidr host bits masked", routesStr: "10.1.2.0/8 via 192.168.1.1", linkName: "eth0", expected: []network.RouteSpecSpec{ { ConfigLayer: network.ConfigPlatform, Destination: netip.MustParsePrefix("10.0.0.0/8"), Gateway: netip.MustParseAddr("192.168.1.1"), OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, Scope: nethelpers.ScopeGlobal, }, }, }, { name: "malformed gateway", routesStr: "10.0.0.0/8 via notanip", linkName: "eth0", errMsg: "failed to parse gateway", }, { name: "malformed cidr destination", routesStr: "notaprefix/8 via 192.168.1.1", linkName: "eth0", errMsg: "failed to parse destination", }, { name: "malformed legacy destination", routesStr: "notanip 255.0.0.0 192.168.1.1", linkName: "eth0", errMsg: "failed to parse destination", }, { name: "malformed metric", routesStr: "10.0.0.0/8 via 192.168.1.1 notanumber", linkName: "eth0", errMsg: "failed to parse metric", }, { name: "too few fields", routesStr: "10.0.0.0/8 via", linkName: "eth0", errMsg: "expected at least 3 fields", }, { name: "legacy too few fields", routesStr: "10.0.0.0 255.0.0.0", linkName: "eth0", errMsg: "expected at least 3 fields", }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() routes, err := opennebula.ParseRoutes(tc.routesStr, tc.linkName) if tc.errMsg != "" { require.ErrorContains(t, err, tc.errMsg) return } require.NoError(t, err) assert.Equal(t, tc.expected, routes) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/skip_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package opennebula_test import ( "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const skipContextBase = `ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_IP = "192.168.1.92" ETH0_MASK = "255.255.255.0" NAME = "test" ` func skipContext(extra string) []byte { return []byte(skipContextBase + extra) } func TestParseMethodSkip(t *testing.T) { t.Parallel() o := &opennebula.OpenNebula{} st := state.WrapCore(namespaced.NewState(inmem.Build)) for _, tc := range []struct { name string extra string wantAddrs int wantLinks int wantRoutes int wantOperators []network.OperatorSpecSpec }{ { name: "METHOD=skip omits interface entirely", extra: `ETH0_METHOD = "skip"`, wantAddrs: 0, wantLinks: 0, wantRoutes: 0, }, { name: "METHOD=skip with IP6_METHOD=dhcp emits DHCPv6 operator only", extra: "ETH0_METHOD = \"skip\"\nETH0_IP6_METHOD = \"dhcp\"", wantOperators: []network.OperatorSpecSpec{ { Operator: network.OperatorDHCP6, LinkName: "eth0", RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 1, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, }, }, }, { name: "METHOD=skip with IP6_METHOD=disable omits interface entirely", extra: "ETH0_METHOD = \"skip\"\nETH0_IP6_METHOD = \"disable\"", wantAddrs: 0, wantLinks: 0, wantRoutes: 0, }, { name: "METHOD=skip with IP6_METHOD=skip omits interface entirely", extra: "ETH0_METHOD = \"skip\"\nETH0_IP6_METHOD = \"skip\"", wantAddrs: 0, wantLinks: 0, wantRoutes: 0, }, } { t.Run(tc.name, func(t *testing.T) { t.Parallel() networkConfig, err := o.ParseMetadata(st, skipContext(tc.extra)) require.NoError(t, err) assert.Len(t, networkConfig.Addresses, tc.wantAddrs) assert.Len(t, networkConfig.Links, tc.wantLinks) assert.Len(t, networkConfig.Routes, tc.wantRoutes) assert.Equal(t, tc.wantOperators, networkConfig.Operators) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/testdata/expected.yaml ================================================ addresses: - address: 192.168.1.92/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 192.168.1.100/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: ether layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 100 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 172.16.0.0/16 src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 500 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: code-server domainname: "" layer: platform resolvers: - dnsServers: - 9.9.9.9 - 192.168.1.1 - 8.8.8.8 - 1.1.1.1 layer: platform searchDomains: - global.example.com - example.com timeServers: [] operators: - operator: dhcp4 linkName: eth1 requireUp: true dhcp4: routeMetric: 200 skipHostnameRequest: true layer: platform - operator: dhcp6 linkName: eth1 requireUp: true dhcp6: routeMetric: 200 skipHostnameRequest: true layer: platform externalIPs: [] metadata: platform: opennebula hostname: code-server instanceId: "14" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/testdata/expected_no_network_flag.yaml ================================================ addresses: - address: 192.168.1.92/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 192.168.1.100/24 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: ether layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 100 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 10.0.0.0/8 src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 172.16.0.0/16 src: "" gateway: 192.168.1.1 outLinkName: eth0 table: main priority: 500 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: code-server domainname: "" layer: platform resolvers: - dnsServers: - 9.9.9.9 - 192.168.1.1 - 8.8.8.8 - 1.1.1.1 layer: platform searchDomains: - global.example.com - example.com timeServers: [] operators: - operator: dhcp4 linkName: eth1 requireUp: true dhcp4: routeMetric: 200 skipHostnameRequest: true layer: platform - operator: dhcp6 linkName: eth1 requireUp: true dhcp6: routeMetric: 200 skipHostnameRequest: true layer: platform externalIPs: [] metadata: platform: opennebula hostname: code-server instanceId: "14" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/testdata/metadata.yaml ================================================ # Context variables generated by OpenNebula DISK_ID = "1" DNS = "9.9.9.9" ETH0_DNS = "192.168.1.1 8.8.8.8 1.1.1.1" ETH0_ALIAS0_DETACH = "" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_EXTERNAL = "" ETH0_GATEWAY = "192.168.1.1" ETH0_IP = "192.168.1.92" ETH0_IP6 = "" ETH0_IP6_GATEWAY = "" ETH0_IP6_METHOD = "" ETH0_IP6_METRIC = "" ETH0_IP6_PREFIX_LENGTH = "" ETH0_IP6_ULA = "" ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_MASK = "255.255.255.0" ETH0_METHOD = "" ETH0_METRIC = "100" ETH0_MTU = "" ETH0_NETWORK = "192.168.1.0" ETH0_ROUTES = "10.0.0.0 255.0.0.0 192.168.1.1, 172.16.0.0 255.255.0.0 192.168.1.1 500" ETH0_SEARCH_DOMAIN = "example.com" ETH0_VLAN_ID = "3" ETH0_VROUTER_IP = "" ETH0_VROUTER_IP6 = "" ETH0_VROUTER_MANAGEMENT = "" SEARCH_DOMAIN = "global.example.com" NETWORK = "YES" SSH_PUBLIC_KEY = "" TARGET = "hda" VMID = "14" ETH1_MAC = "02:00:c0:a8:01:5d" ETH1_METHOD = "dhcp" ETH1_METRIC = "200" SET_HOSTNAME = "code-server" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula/testdata/metadata_no_network_flag.yaml ================================================ # Context variables generated by OpenNebula with NETWORK=NO. # ETH*_ variables are manually specified (e.g. for ETHER-type address ranges # where NETWORK=YES would cause the server to overwrite them with empty values). DISK_ID = "1" DNS = "9.9.9.9" ETH0_DNS = "192.168.1.1 8.8.8.8 1.1.1.1" ETH0_ALIAS0_DETACH = "" ETH0_ALIAS0_EXTERNAL = "NO" ETH0_ALIAS0_IP = "192.168.1.100" ETH0_ALIAS0_MAC = "02:00:c0:a8:01:64" ETH0_ALIAS0_MASK = "255.255.255.0" ETH0_EXTERNAL = "" ETH0_GATEWAY = "192.168.1.1" ETH0_IP = "192.168.1.92" ETH0_IP6 = "" ETH0_IP6_GATEWAY = "" ETH0_IP6_METHOD = "" ETH0_IP6_METRIC = "" ETH0_IP6_PREFIX_LENGTH = "" ETH0_IP6_ULA = "" ETH0_MAC = "02:00:c0:a8:01:5c" ETH0_MASK = "255.255.255.0" ETH0_METHOD = "" ETH0_METRIC = "100" ETH0_MTU = "" ETH0_NETWORK = "192.168.1.0" ETH0_ROUTES = "10.0.0.0 255.0.0.0 192.168.1.1, 172.16.0.0 255.255.0.0 192.168.1.1 500" ETH0_SEARCH_DOMAIN = "example.com" ETH0_VLAN_ID = "3" ETH0_VROUTER_IP = "" ETH0_VROUTER_IP6 = "" ETH0_VROUTER_MANAGEMENT = "" SEARCH_DOMAIN = "global.example.com" NETWORK = "NO" SSH_PUBLIC_KEY = "" TARGET = "hda" VMID = "14" ETH1_MAC = "02:00:c0:a8:01:5d" ETH1_METHOD = "dhcp" ETH1_METRIC = "200" SET_HOSTNAME = "code-server" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package openstack import ( "context" stderrors "errors" "io/fs" "log" "net/netip" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/blockutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/xfs" ) const ( // config-drive configs path. configISOLabel = "config-2" configMetadataPath = "openstack/latest/meta_data.json" configNetworkDataPath = "openstack/latest/network_data.json" configUserDataPath = "openstack/latest/user_data" endpoint = "http://169.254.169.254/" // OpenStackExternalIPEndpoint is the local OpenStack endpoint for the external IP. OpenStackExternalIPEndpoint = endpoint + "latest/meta-data/public-ipv4" // OpenStackInstanceTypeEndpoint is the local OpenStack endpoint for the instance-type. OpenStackInstanceTypeEndpoint = endpoint + "latest/meta-data/instance-type" // OpenStackMetaDataEndpoint is the local OpenStack endpoint for the meta config. OpenStackMetaDataEndpoint = endpoint + configMetadataPath // OpenStackNetworkDataEndpoint is the local OpenStack endpoint for the network config. OpenStackNetworkDataEndpoint = endpoint + configNetworkDataPath // OpenStackUserDataEndpoint is the local OpenStack endpoint for the config. OpenStackUserDataEndpoint = endpoint + configUserDataPath ) // NetworkConfig holds NetworkData config. type NetworkConfig struct { Links []struct { ID string `json:"id,omitempty"` Type string `json:"type"` Mac string `json:"ethernet_mac_address,omitempty"` MTU int `json:"mtu,omitempty"` BondMode string `json:"bond_mode,omitempty"` BondLinks []string `json:"bond_links,omitempty"` BondMIIMon uint32 `json:"bond_miimon,string,omitempty"` BondHashPolicy string `json:"bond_xmit_hash_policy,omitempty"` VlanID uint16 `json:"vlan_id,omitempty"` VlanLink string `json:"vlan_link,omitempty"` VlanMac string `json:"vlan_mac_address,omitempty"` } `json:"links"` Networks []struct { ID string `json:"id,omitempty"` Link string `json:"link"` Type string `json:"type"` Address string `json:"ip_address,omitempty"` Netmask string `json:"netmask,omitempty"` Gateway string `json:"gateway,omitempty"` Routes []struct { Network string `json:"network,omitempty"` Netmask string `json:"netmask,omitempty"` Gateway string `json:"gateway,omitempty"` } `json:"routes,omitempty"` } `json:"networks"` Services []struct { Type string `json:"type"` Address string `json:"address"` } `json:"services,omitempty"` } // MetadataConfig holds meta info. type MetadataConfig struct { UUID string `json:"uuid,omitempty"` Hostname string `json:"hostname,omitempty"` AvailabilityZone string `json:"availability_zone,omitempty"` ProjectID string `json:"project_id"` InstanceType string `json:"instance_type"` } func (o *OpenStack) configFromNetwork(ctx context.Context) (metaConfig []byte, networkConfig []byte, machineConfig []byte, err error) { log.Printf("fetching meta config from: %q", OpenStackMetaDataEndpoint) metaConfig, err = download.Download(ctx, OpenStackMetaDataEndpoint) if err != nil { metaConfig = nil } log.Printf("fetching network config from: %q", OpenStackNetworkDataEndpoint) networkConfig, err = download.Download(ctx, OpenStackNetworkDataEndpoint) if err != nil { networkConfig = nil } log.Printf("fetching machine config from: %q", OpenStackUserDataEndpoint) machineConfig, err = download.Download(ctx, OpenStackUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) return metaConfig, networkConfig, machineConfig, err } //nolint:gocyclo func (o *OpenStack) configFromCD(ctx context.Context, r state.State) (metaConfig []byte, networkConfig []byte, machineConfig []byte, err error) { err = blockutils.ReadFromVolume(ctx, r, []string{configISOLabel}, func(root xfs.Root, volumeStatus *block.VolumeStatus) error { log.Printf("found config disk (config-drive) at %s", volumeStatus.TypedSpec().Location) log.Printf("fetching meta config from: config-drive/%s", configMetadataPath) metaConfig, err = xfs.ReadFile(root, configMetadataPath) if err != nil { log.Printf("failed to read %s", configMetadataPath) metaConfig = nil } log.Printf("fetching network config from: config-drive/%s", configNetworkDataPath) networkConfig, err = xfs.ReadFile(root, configNetworkDataPath) if err != nil { log.Printf("failed to read %s", configNetworkDataPath) networkConfig = nil } log.Printf("fetching machine config from: config-drive/%s", configUserDataPath) machineConfig, err = xfs.ReadFile(root, configUserDataPath) if err != nil { log.Printf("failed to read %s", configUserDataPath) machineConfig = nil } return nil }) if err != nil { if stderrors.Is(err, fs.ErrNotExist) { return nil, nil, nil, errors.ErrNoConfigSource } return nil, nil, nil, err } if machineConfig == nil { err = errors.ErrNoConfigSource } return metaConfig, networkConfig, machineConfig, err } func (o *OpenStack) instanceType(ctx context.Context) string { log.Printf("fetching instance-type from: %q", OpenStackInstanceTypeEndpoint) sku, err := download.Download(ctx, OpenStackInstanceTypeEndpoint) if err != nil { return "" } return string(sku) } func (o *OpenStack) externalIPs(ctx context.Context) (addrs []netip.Addr) { log.Printf("fetching externalIP from: %q", OpenStackExternalIPEndpoint) exIP, err := download.Download(ctx, OpenStackExternalIPEndpoint, download.WithErrorOnNotFound(errors.ErrNoExternalIPs), download.WithErrorOnEmptyResponse(errors.ErrNoExternalIPs)) if err != nil { log.Printf("failed to fetch external IPs, ignored: %s", err) return nil } if addr, err := netip.ParseAddr(string(exIP)); err == nil { addrs = append(addrs, addr) } return addrs } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package openstack provides the OpenStack platform implementation. package openstack import ( "bytes" "context" "encoding/json" stderrors "errors" "fmt" "log" "net" "net/netip" "strings" "time" "github.com/cenkalti/backoff/v4" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" networkadapter "github.com/siderolabs/talos/internal/app/machined/pkg/adapters/network" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/address" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // OpenStack is the concrete type that implements the runtime.Platform interface. type OpenStack struct{} // Name implements the runtime.Platform interface. func (o *OpenStack) Name() string { return "openstack" } // ParseMetadata converts OpenStack metadata to platform network configuration. // //nolint:gocyclo,cyclop func (o *OpenStack) ParseMetadata( ctx context.Context, unmarshalledNetworkConfig *NetworkConfig, extIPs []netip.Addr, metadata *MetadataConfig, st state.State, ) (*runtime.PlatformNetworkConfig, bool, error) { networkConfig := &runtime.PlatformNetworkConfig{} needsReconcile := false if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, false, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } networkConfig.ExternalIPs = extIPs var dnsIPs []netip.Addr for _, netsvc := range unmarshalledNetworkConfig.Services { if netsvc.Type == "dns" && netsvc.Address != "" { if ip, err := netip.ParseAddr(netsvc.Address); err == nil { dnsIPs = append(dnsIPs, ip) } else { return nil, false, fmt.Errorf("failed to parse dns service ip: %w", err) } } } if len(dnsIPs) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) } hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st) if err != nil { return nil, false, fmt.Errorf("error listing host interfaces: %w", err) } ifaces := make(map[string]string) bondLinks := make(map[string]string) // Bonds bondIndex := 0 for _, netLink := range unmarshalledNetworkConfig.Links { if netLink.Type != "bond" { continue } mode, err := nethelpers.BondModeByName(netLink.BondMode) if err != nil { return nil, false, fmt.Errorf("invalid bond_mode: %w", err) } hashPolicy, err := nethelpers.BondXmitHashPolicyByName(netLink.BondHashPolicy) if err != nil { return nil, false, fmt.Errorf("invalid bond_xmit_hash_policy: %w", err) } bondName := fmt.Sprintf("bond%d", bondIndex) ifaces[netLink.ID] = bondName bondLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: bondName, Logical: true, Up: true, MTU: uint32(netLink.MTU), Kind: network.LinkKindBond, Type: nethelpers.LinkEther, BondMaster: network.BondMasterSpec{ Mode: mode, MIIMon: netLink.BondMIIMon, HashPolicy: hashPolicy, UpDelay: 200, DownDelay: 200, LACPRate: nethelpers.LACPRateFast, }, } if netLink.Mac != "" { mac, err := net.ParseMAC(netLink.Mac) if err != nil { return nil, false, fmt.Errorf("invalid bond MAC address %q: %w", netLink.Mac, err) } bondLink.HardwareAddress = nethelpers.HardwareAddr(mac) } if mode == nethelpers.BondMode8023AD { bondLink.BondMaster.ADLACPActive = nethelpers.ADLACPActiveOn } networkadapter.BondMasterSpec(&bondLink.BondMaster).FillDefaults() networkConfig.Links = append(networkConfig.Links, bondLink) for _, link := range netLink.BondLinks { bondLinks[link] = bondName } bondIndex++ } bondSlaveIndexes := make(map[string]int) // Interfaces for idx, netLink := range unmarshalledNetworkConfig.Links { // OpenStack network metadata schema: // "type": { // "$id": "#/definitions/l2_link/properties/type", // "type": "string", // "enum": [ // "bridge", // "dvs", // "hw_veb", // "hyperv", // "ovs", // "tap", // "vhostuser", // "vif", // "phy" // ], // "title": "Interface type", // "examples": [ // "bridge" // ] // }, // "vif_id": { // "$ref": "#/definitions/l2_vif_id" // } switch netLink.Type { case "phy", "vif", "ovs", "bridge", "tap", "vhostuser", "hw_veb": linkName := "" if netLink.Mac != "" { for hostInterface := range hostInterfaces.All() { macAddress := hostInterface.TypedSpec().PermanentAddr.String() if macAddress == "" { macAddress = hostInterface.TypedSpec().HardwareAddr.String() } if strings.EqualFold(macAddress, netLink.Mac) { linkName = hostInterface.Metadata().ID() break } } } if linkName == "" { linkName = fmt.Sprintf("eth%d", idx) log.Printf("failed to find interface with MAC %q, using %q", netLink.Mac, linkName) needsReconcile = true } ifaces[netLink.ID] = linkName link := network.LinkSpecSpec{ Name: ifaces[netLink.ID], Up: true, MTU: uint32(netLink.MTU), ConfigLayer: network.ConfigPlatform, } if bondName, ok := bondLinks[netLink.ID]; ok { link.BondSlave = network.BondSlave{ MasterName: bondName, SlaveIndex: bondSlaveIndexes[bondName], } bondSlaveIndexes[bondName]++ } networkConfig.Links = append(networkConfig.Links, link) } } // VLANs for _, netLink := range unmarshalledNetworkConfig.Links { if netLink.Type != "vlan" { continue } parentName, ok := ifaces[netLink.VlanLink] if !ok { parentName = netLink.VlanLink } vlanName := fmt.Sprintf("%s.%d", parentName, netLink.VlanID) ifaces[netLink.ID] = vlanName vlanLink := network.LinkSpecSpec{ ConfigLayer: network.ConfigPlatform, Name: vlanName, Logical: true, Up: true, Kind: network.LinkKindVLAN, Type: nethelpers.LinkEther, ParentName: parentName, VLAN: network.VLANSpec{ VID: netLink.VlanID, Protocol: nethelpers.VLANProtocol8021Q, }, } if netLink.MTU != 0 { vlanLink.MTU = uint32(netLink.MTU) } networkConfig.Links = append(networkConfig.Links, vlanLink) } for _, ntwrk := range unmarshalledNetworkConfig.Networks { if ntwrk.ID == "" || ifaces[ntwrk.Link] == "" { continue } iface := ifaces[ntwrk.Link] switch ntwrk.Type { case "ipv4_dhcp": networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: iface, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, }) case "ipv6_dhcp", "ipv6_dhcpv6-stateless", "ipv6_dhcpv6-stateful": networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: iface, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 2 * network.DefaultRouteMetric, SkipHostnameRequest: true, }, ConfigLayer: network.ConfigPlatform, }) case "ipv4", "ipv6", "ipv6_slaac": // FIXME: we need to switch on/off slaac here default: log.Printf("network type %s is not supported", ntwrk.Type) continue } if ntwrk.Address != "" { ipPrefix, err := address.IPPrefixFrom(ntwrk.Address, ntwrk.Netmask) if err != nil { return nil, false, fmt.Errorf("failed to parse ip address: %w", err) } family := nethelpers.FamilyInet4 if ipPrefix.Addr().Is6() { family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: iface, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) if ntwrk.Gateway != "" { gw, err := netip.ParseAddr(ntwrk.Gateway) if err != nil { return nil, false, fmt.Errorf("failed to parse gateway ip: %w", err) } priority := uint32(network.DefaultRouteMetric) if family == nethelpers.FamilyInet6 { priority *= 2 } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: iface, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: priority, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } for _, route := range ntwrk.Routes { gw, err := netip.ParseAddr(route.Gateway) if err != nil { return nil, false, fmt.Errorf("failed to parse route gateway: %w", err) } dest, err := address.IPPrefixFrom(route.Network, route.Netmask) if err != nil { return nil, false, fmt.Errorf("failed to parse route network: %w", err) } family := nethelpers.FamilyInet4 if dest.Addr().Is6() { family = nethelpers.FamilyInet6 } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Destination: dest, Gateway: gw, OutLinkName: iface, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: network.DefaultRouteMetric, } route.Normalize() // double the priority of the route if it is actually the default gateway and IPv6 if route.Destination == (netip.Prefix{}) && family == nethelpers.FamilyInet6 { route.Priority *= 2 } networkConfig.Routes = append(networkConfig.Routes, route) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: o.Name(), Hostname: metadata.Hostname, Zone: metadata.AvailabilityZone, InstanceID: metadata.UUID, InstanceType: metadata.InstanceType, ProviderID: fmt.Sprintf("openstack:///%s", metadata.UUID), } return networkConfig, needsReconcile, nil } // Configuration implements the runtime.Platform interface. func (o *OpenStack) Configuration(ctx context.Context, r state.State) (machineConfig []byte, err error) { _, _, machineConfig, err = o.configFromCD(ctx, r) if err != nil { if err = netutils.Wait(ctx, r); err != nil { return nil, err } _, _, machineConfig, err = o.configFromNetwork(ctx) if err != nil { return nil, err } } // Some openstack setups does not allow you to change user-data, // so skip this case. if bytes.HasPrefix(machineConfig, []byte("#cloud-config")) { return nil, errors.ErrNoConfigSource } return machineConfig, nil } // Mode implements the runtime.Platform interface. func (o *OpenStack) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (o *OpenStack) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. // //nolint:gocyclo func (o *OpenStack) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { // wait for devices to be ready before proceeding, otherwise we might not find network interfaces by MAC if err := netutils.WaitForDevicesReady(ctx, st); err != nil { return fmt.Errorf("error waiting for devices to be ready: %w", err) } networkSource := false metadataConfigDl, metadataNetworkConfigDl, _, err := o.configFromCD(ctx, st) if err != nil { metadataConfigDl, metadataNetworkConfigDl, _, err = o.configFromNetwork(ctx) if stderrors.Is(err, errors.ErrNoConfigSource) { err = nil } if err != nil { return err } networkSource = true } var ( meta MetadataConfig unmarshalledNetworkConfig NetworkConfig ) // ignore errors unmarshaling, empty configs work just fine as empty default _ = json.Unmarshal(metadataConfigDl, &meta) //nolint:errcheck _ = json.Unmarshal(metadataNetworkConfigDl, &unmarshalledNetworkConfig) //nolint:errcheck var extIPs []netip.Addr if networkSource { extIPs = o.externalIPs(ctx) if meta.InstanceType == "" { meta.InstanceType = o.instanceType(ctx) } } // do a loop to retry network config remap in case of missing links // on each try, export the configuration as it is, and if the network is reconciled next time, export the reconciled configuration bckoff := backoff.NewExponentialBackOff() for { networkConfig, needsReconcile, err := o.ParseMetadata(ctx, &unmarshalledNetworkConfig, extIPs, &meta, st) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } if !needsReconcile { return nil } // wait for backoff to retry network config remap nextBackoff := bckoff.NextBackOff() if nextBackoff == backoff.Stop { return nil } select { case <-ctx.Done(): return ctx.Err() case <-time.After(nextBackoff): } } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/openstack_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package openstack_test import ( "context" _ "embed" "encoding/json" "net/netip" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/network.json var rawNetwork []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { t.Parallel() for _, tt := range []struct { name string networkJSON []byte metadataJSON []byte extIPs []netip.Addr setupState func(t *testing.T, ctx context.Context, st state.State) expectedNeedsReconcile bool expected string checkResult func(t *testing.T, cfg *runtime.PlatformNetworkConfig) }{ { name: "full config", networkJSON: rawNetwork, metadataJSON: rawMetadata, extIPs: []netip.Addr{netip.MustParseAddr("1.2.3.4")}, setupState: func(t *testing.T, ctx context.Context, st state.State) { eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xa4, 0xbf, 0x00, 0x10, 0x20, 0x30} require.NoError(t, st.Create(ctx, eth0)) eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xa4, 0xbf, 0x00, 0x10, 0x20, 0x31} require.NoError(t, st.Create(ctx, eth1)) eth2 := network.NewLinkStatus(network.NamespaceName, "eth2") eth2.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0xa4, 0xbf, 0x00, 0x10, 0x20, 0x33} require.NoError(t, st.Create(ctx, eth2)) eth3 := network.NewLinkStatus(network.NamespaceName, "eth3") eth3.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x4c, 0xd9, 0x8f, 0xb3, 0x34, 0xf8} require.NoError(t, st.Create(ctx, eth3)) eth4 := network.NewLinkStatus(network.NamespaceName, "eth4") eth4.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x4c, 0xd9, 0x8f, 0xb3, 0x34, 0xf7} require.NoError(t, st.Create(ctx, eth4)) }, expected: expectedNetworkConfig, }, { name: "HardwareAddr fallback", networkJSON: []byte(`{"links":[{"id":"iface1","type":"phy","ethernet_mac_address":"aa:bb:cc:dd:ee:ff","mtu":1500}],"networks":[{"id":"net1","link":"iface1","type":"ipv4_dhcp"}]}`), setupState: func(t *testing.T, ctx context.Context, st state.State) { eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().HardwareAddr = nethelpers.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff} require.NoError(t, st.Create(ctx, eth0)) }, checkResult: func(t *testing.T, cfg *runtime.PlatformNetworkConfig) { require.Len(t, cfg.Links, 1) assert.Equal(t, "eth0", cfg.Links[0].Name) }, }, { name: "empty MAC does not match", networkJSON: []byte(`{"links":[{"id":"iface1","type":"phy","ethernet_mac_address":"","mtu":1500}],"networks":[{"id":"net1","link":"iface1","type":"ipv4_dhcp"}]}`), setupState: func(t *testing.T, ctx context.Context, st state.State) { eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") require.NoError(t, st.Create(ctx, eth0)) }, expectedNeedsReconcile: true, }, { name: "MAC mismatch triggers reconcile", networkJSON: []byte(`{"links":[{"id":"iface1","type":"phy","ethernet_mac_address":"aa:bb:cc:dd:ee:ff","mtu":1500}],"networks":[{"id":"net1","link":"iface1","type":"ipv4_dhcp"}]}`), setupState: func(t *testing.T, ctx context.Context, st state.State) { eth0 := network.NewLinkStatus(network.NamespaceName, "eth0") eth0.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x11, 0x22, 0x33, 0x44, 0x55, 0x66} require.NoError(t, st.Create(ctx, eth0)) }, expectedNeedsReconcile: true, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctx := t.Context() st := state.WrapCore(namespaced.NewState(inmem.Build)) tt.setupState(t, ctx, st) var ( metadata openstack.MetadataConfig n openstack.NetworkConfig ) if tt.metadataJSON != nil { require.NoError(t, json.Unmarshal(tt.metadataJSON, &metadata)) } require.NoError(t, json.Unmarshal(tt.networkJSON, &n)) o := &openstack.OpenStack{} networkConfig, needsReconcile, err := o.ParseMetadata(ctx, &n, tt.extIPs, &metadata, st) require.NoError(t, err) assert.Equal(t, tt.expectedNeedsReconcile, needsReconcile) if tt.expected != "" { marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, tt.expected, string(marshaled)) } if tt.checkResult != nil { tt.checkResult(t, networkConfig) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/expected.yaml ================================================ addresses: - address: 2000:0:100::/56 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform - address: 10.184.0.244/20 linkName: eth1 family: inet4 scope: global flags: permanent layer: platform - address: 2001:db8::3257:9652/64 linkName: eth1 family: inet6 scope: global flags: permanent layer: platform - address: fd60:172:16:84:f816:3eff:fe73:5901/64 linkName: eth2 family: inet6 scope: global flags: permanent layer: platform - address: 94.156.45.48/24 linkName: bond0 family: inet4 scope: global flags: permanent layer: platform links: - name: bond0 logical: true up: true mtu: 1500 kind: bond type: ether hardwareAddr: 4c:d9:8f:b3:34:f7 bondMaster: mode: 802.3ad xmitHashPolicy: layer2+3 lacpRate: fast arpValidate: none arpAllTargets: any primaryReselect: always failOverMac: none miimon: 100 updelay: 200 downdelay: 200 resendIgmp: 1 lpInterval: 1 packetsPerSlave: 1 numPeerNotif: 1 tlbLogicalLb: 1 useCarrier: true adActorSysPrio: 65535 adLacpActive: "on" layer: platform - name: eth0 logical: false up: true mtu: 1450 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 9000 kind: "" type: netrom layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth3 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 layer: platform - name: eth4 logical: false up: true mtu: 0 kind: "" type: netrom masterName: bond0 slaveIndex: 1 layer: platform - name: bond0.100 logical: true up: true mtu: 1400 kind: vlan type: ether parentName: bond0 vlan: vlanID: 100 vlanProtocol: 802.1q layer: platform routes: - family: inet6 dst: "" src: "" gateway: 2000:0:100:2fff:ff:ff:ff:ff outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: 2000:0:100:2f00::/58 src: "" gateway: 2000:0:100:2fff:ff:ff:ff:f0 outLinkName: eth0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: 192.168.0.0/16 src: "" gateway: 10.184.0.1 outLinkName: eth1 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: "" src: "" gateway: 10.184.0.1 outLinkName: eth1 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: fd00::1 outLinkName: eth1 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform - family: inet4 dst: "" src: "" gateway: 94.156.45.1 outLinkName: bond0 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: - dnsServers: - 8.8.8.8 - 1.1.1.1 layer: platform timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 skipHostnameRequest: true layer: platform - operator: dhcp6 linkName: eth2 requireUp: true dhcp6: routeMetric: 2048 skipHostnameRequest: true layer: platform - operator: dhcp4 linkName: bond0.100 requireUp: true dhcp4: routeMetric: 1024 skipHostnameRequest: true layer: platform externalIPs: - 1.2.3.4 metadata: platform: openstack hostname: talos zone: nova instanceId: 39073b0a-1234-1234-1234-5e76a4bd64b2 providerId: openstack:///39073b0a-1234-1234-1234-5e76a4bd64b2 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/metadata.json ================================================ { "availability_zone": "nova", "devices": [], "hostname": "talos", "keys": [], "launch_index": 0, "name": "talos", "project_id": "39073b0a-1234-1234-1234-5e76a4bd64b2", "public_keys": {}, "uuid": "39073b0a-1234-1234-1234-5e76a4bd64b2" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/openstack/testdata/network.json ================================================ { "links": [ { "ethernet_mac_address": "A4:BF:00:10:20:30", "id": "aae16046-6c74-4f33-acf2-a16e9ab093eb", "type": "phy", "mtu": 1450, "vif_id": "7607af2d-c24d-4bfb-909e-c447b119f4e2" }, { "ethernet_mac_address": "A4:BF:00:10:20:31", "id": "aae16046-6c74-4f33-acf2-a16e9ab093ec", "type": "ovs", "mtu": 9000, "vif_id": "c816df7e-7bcc-45ca-9eb2-3d3d3dca0639" }, { "ethernet_mac_address": "A4:BF:00:10:20:33", "id": "aae16046-6c74-4f33-acf2-a16e9ab093ed", "type": "vif", "vif_id": "c816df7e-7bcc-45ca-9eb2-3d3d3dca063a" }, { "id": "tap7819ff08-20", "vif_id": "7819ff08-204b-4193-8c4d-1fff242932e5", "type": "bond", "mtu": 1500, "ethernet_mac_address": "4c:d9:8f:b3:34:f7", "bond_mode": "802.3ad", "bond_links": [ "411f3980-d8f9-4bf0-a09a-9c01c81e1022", "83f59825-bf2d-4ea7-98be-edc772fe82de" ], "bond_miimon": "100", "bond_xmit_hash_policy": "layer2+3" }, { "id": "411f3980-d8f9-4bf0-a09a-9c01c81e1022", "type": "bridge", "ethernet_mac_address": "4c:d9:8f:b3:34:f8" }, { "id": "83f59825-bf2d-4ea7-98be-edc772fe82de", "type": "phy", "ethernet_mac_address": "4c:d9:8f:b3:34:f7" }, { "id": "vlan-interface-001", "vif_id": "acf55ef1-9a79-4c59-bec7-1ebf01a9a918", "type": "vlan", "mtu": 1400, "vlan_mac_address": null, "vlan_link": "tap7819ff08-20", "vlan_id": 100 } ], "networks": [ { "id": "publicnet-ipv4", "link": "aae16046-6c74-4f33-acf2-a16e9ab093eb", "network_id": "66374c4d-5123-4f11-8fa9-8a6dea2b4fe7", "type": "ipv4_dhcp" }, { "routes": [ { "network": "2000:0:100:2f00::", "gateway": "2000:0:100:2fff:ff:ff:ff:f0", "netmask": "ffff:ffff:ffff:ffc0::" } ], "dns_nameservers": [ "2000:0:100::1" ], "gateway": "2000:0:100:2fff:ff:ff:ff:ff", "link": "aae16046-6c74-4f33-acf2-a16e9ab093eb", "ip_address": "2000:0:100::/56", "network_id": "39b48637-d98a-4dfc-a05b-d61e8d88fafe", "id": "publicnet-ipv6", "type": "ipv6" }, { "id": "privatnet-ipv4", "link": "aae16046-6c74-4f33-acf2-a16e9ab093ec", "network_id": "66374c4d-5123-4f11-8fa9-8a6dea2b4fe7", "type": "ipv4", "ip_address": "10.184.0.244", "netmask": "255.255.240.0", "routes": [ { "network": "192.168.0.0", "netmask": "255.255.0.0", "gateway": "10.184.0.1" }, { "network": "0.0.0.0", "netmask": "0.0.0.0", "gateway": "10.184.0.1" } ] }, { "id": "privatnet-ipv6", "link": "aae16046-6c74-4f33-acf2-a16e9ab093ec", "network_id": "66374c4d-5123-4f11-8fa9-8a6dea2b4fe7", "type": "ipv6", "ip_address": "2001:db8::3257:9652", "netmask": "ffff:ffff:ffff:ffff::", "routes": [ { "network": "::", "netmask": "::", "gateway": "fd00::1" } ] }, { "id": "privatnet-ipv6-2", "link": "aae16046-6c74-4f33-acf2-a16e9ab093ed", "network_id": "66374c4d-5123-4f11-8fa9-8a6dea2b4fe7", "type": "ipv6_dhcpv6-stateless", "ip_address": "fd60:172:16:84:f816:3eff:fe73:5901", "netmask": "ffff:ffff:ffff:ffff::", "routes": [] }, { "id": "network0", "type": "ipv4", "link": "tap7819ff08-20", "ip_address": "94.156.45.48", "netmask": "255.255.255.0", "routes": [ { "network": "0.0.0.0", "netmask": "0.0.0.0", "gateway": "94.156.45.1" } ], "network_id": "dc4b2e65-869b-46b1-b53b-538774dbdc16", "services": [ { "type": "dns", "address": "8.8.8.8" }, { "type": "dns", "address": "8.8.4.4" } ] }, { "id": "vlan-network", "type": "ipv4_dhcp", "link": "vlan-interface-001", "network_id": "5ee412a7-13c5-4306-ad4d-85c06241e5f4" } ], "services": [ { "address": "8.8.8.8", "type": "dns" }, { "address": "1.1.1.1", "type": "dns" } ] } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package oracle import ( "context" "encoding/json" "fmt" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/download" ) // Ref: https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm const ( // OracleMetadataEndpoint is the local metadata endpoint for the hostname. OracleMetadataEndpoint = "http://169.254.169.254/opc/v2/instance/" // OracleUserDataEndpoint is the local metadata endpoint inside of Oracle Cloud. OracleUserDataEndpoint = "http://169.254.169.254/opc/v2/instance/metadata/user_data" // OracleNetworkEndpoint is the local network metadata endpoint inside of Oracle Cloud. OracleNetworkEndpoint = "http://169.254.169.254/opc/v2/vnics/" oracleResolverServer = "169.254.169.254" oracleTimeServer = "169.254.169.254" ) // MetadataConfig represents a metadata Oracle instance. type MetadataConfig struct { Hostname string `json:"hostname,omitempty"` ID string `json:"id,omitempty"` Region string `json:"region,omitempty"` AvailabilityDomain string `json:"availabilityDomain,omitempty"` FaultDomain string `json:"faultDomain,omitempty"` Shape string `json:"shape,omitempty"` } func (o *Oracle) getMetadata(ctx context.Context) (*MetadataConfig, error) { metaConfigDl, err := download.Download(ctx, OracleMetadataEndpoint, download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}), download.WithErrorOnNotFound(errors.ErrNoHostname), download.WithErrorOnEmptyResponse(errors.ErrNoHostname)) if err != nil { return nil, fmt.Errorf("error fetching metadata: %w", err) } var meta MetadataConfig if err = json.Unmarshal(metaConfigDl, &meta); err != nil { return nil, err } return &meta, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package oracle provides the Oracle platform implementation. package oracle import ( "context" "encoding/base64" "encoding/json" "fmt" "log" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NetworkConfig holds network interface meta config. type NetworkConfig struct { HWAddr string `json:"macAddr"` PrivateIP string `json:"privateIp"` VirtualRouterIP string `json:"virtualRouterIp"` SubnetCidrBlock string `json:"subnetCidrBlock"` Ipv6SubnetCidrBlock string `json:"ipv6SubnetCidrBlock,omitempty"` Ipv6VirtualRouterIP string `json:"ipv6VirtualRouterIp,omitempty"` Ipv6Addresses []string `json:"ipv6Addresses,omitempty"` } // Oracle is the concrete type that implements the platform.Platform interface. type Oracle struct{} // Name implements the platform.Platform interface. func (o *Oracle) Name() string { return "oracle" } // ParseMetadata converts Oracle Cloud metadata into platform network configuration. func (o *Oracle) ParseMetadata(interfaceAddresses []NetworkConfig, metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } for idx, iface := range interfaceAddresses { ifname := fmt.Sprintf("eth%d", idx) if iface.Ipv6SubnetCidrBlock != "" && iface.Ipv6VirtualRouterIP != "" { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: ifname, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) gw, err := netip.ParseAddr(iface.Ipv6VirtualRouterIP) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: ifname, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } dns, _ := netip.ParseAddr(oracleResolverServer) //nolint:errcheck networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: []netip.Addr{dns}, ConfigLayer: network.ConfigPlatform, }) networkConfig.TimeServers = append(networkConfig.TimeServers, network.TimeServerSpecSpec{ NTPServers: []string{oracleTimeServer}, ConfigLayer: network.ConfigPlatform, }) zone := metadata.AvailabilityDomain if idx := strings.LastIndex(zone, ":"); idx != -1 { zone = zone[idx+1:] } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: o.Name(), Hostname: metadata.Hostname, Region: metadata.Region, Zone: zone, InstanceType: metadata.Shape, InstanceID: metadata.ID, ProviderID: fmt.Sprintf("oci://%s", metadata.ID), } return networkConfig, nil } // Configuration implements the platform.Platform interface. func (o *Oracle) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", OracleUserDataEndpoint) machineConfigDl, err := download.Download(ctx, OracleUserDataEndpoint, download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"}), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) if err != nil { return nil, err } machineConfig, err := base64.StdEncoding.DecodeString(string(machineConfigDl)) if err != nil { return nil, errors.ErrNoConfigSource } return machineConfig, nil } // Mode implements the platform.Platform interface. func (o *Oracle) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (o *Oracle) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"), } } // NetworkConfiguration implements the runtime.Platform interface. func (o *Oracle) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching oracle metadata from: %q", OracleMetadataEndpoint) metadata, err := o.getMetadata(ctx) if err != nil { return err } log.Printf("fetching network config from %q", OracleNetworkEndpoint) metadataNetworkConfigDl, err := download.Download(ctx, OracleNetworkEndpoint, download.WithHeaders(map[string]string{"Authorization": "Bearer Oracle"})) if err != nil { return fmt.Errorf("failed to fetch network config from: %w", err) } var interfaceAddresses []NetworkConfig if err = json.Unmarshal(metadataNetworkConfigDl, &interfaceAddresses); err != nil { return err } networkConfig, err := o.ParseMetadata(interfaceAddresses, metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/oracle_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package oracle_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/metadatanetwork.json var rawMetadataNetwork []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &oracle.Oracle{} var metadata oracle.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) var m []oracle.NetworkConfig require.NoError(t, json.Unmarshal(rawMetadataNetwork, &m)) networkConfig, err := p.ParseMetadata(m, &metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/expected.yaml ================================================ addresses: [] links: [] routes: - family: inet6 dst: "" src: "" gateway: fe80::a:b:c:d outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: - dnsServers: - 169.254.169.254 layer: platform timeServers: - timeServers: - 169.254.169.254 layer: platform operators: - operator: dhcp6 linkName: eth0 requireUp: true dhcp6: routeMetric: 1024 layer: platform externalIPs: [] metadata: platform: oracle hostname: talos region: phx zone: PHX-AD-1 instanceType: VM.Standard.E3.Flex instanceId: ocid1.instance.oc1.phx.exampleuniqueID providerId: oci://ocid1.instance.oc1.phx.exampleuniqueID ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadata.json ================================================ { "availabilityDomain": "EMIr:PHX-AD-1", "faultDomain": "FAULT-DOMAIN-3", "compartmentId": "ocid1.tenancy.oc1..exampleuniqueID", "displayName": "talos-instance", "hostname": "talos", "id": "ocid1.instance.oc1.phx.exampleuniqueID", "image": "ocid1.image.oc1.phx.exampleuniqueID", "metadata": {}, "region": "phx", "canonicalRegionName": "us-phoenix-1", "ociAdName": "phx-ad-1", "regionInfo": { "realmKey": "oc1", "realmDomainComponent": "oraclecloud.com", "regionKey": "PHX", "regionIdentifier": "us-phoenix-1" }, "shape": "VM.Standard.E3.Flex", "state": "Running", "timeCreated": 1600381928581, "agentConfig": { "monitoringDisabled": false, "managementDisabled": false, "allPluginsDisabled": false, "pluginsConfig": [ { "name": "OS Management Service Agent", "desiredState": "ENABLED" }, { "name": "Custom Logs Monitoring", "desiredState": "ENABLED" }, { "name": "Compute Instance Run Command", "desiredState": "ENABLED" }, { "name": "Compute Instance Monitoring", "desiredState": "ENABLED" } ] }, "freeformTags": { "Department": "Finance" }, "definedTags": { "Operations": { "CostCenter": "42" } } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/oracle/testdata/metadatanetwork.json ================================================ [ { "vnicId": "ocid1.vnic.oc1.eu-amsterdam-1.asdasd", "privateIp": "172.16.1.11", "vlanTag": 1, "macAddr": "02:00:17:00:00:00", "virtualRouterIp": "172.16.1.1", "subnetCidrBlock": "172.16.1.0/24", "ipv6SubnetCidrBlock": "2603:a:b:c::/64", "ipv6VirtualRouterIp": "fe80::a:b:c:d", "ipv6Addresses": [ "2603:a:b:c::1234" ] } ] ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/platform.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package platform provides functions to get the [runtime.Platform]. package platform import ( "context" "errors" "fmt" "log" "os" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/akamai" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/aws" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/azure" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/cloudstack" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/container" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/digitalocean" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/equinixmetal" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/exoscale" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/gcp" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/hcloud" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/metal" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/nocloud" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/opennebula" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/openstack" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/oracle" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr" "github.com/siderolabs/talos/internal/pkg/containermode" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Event is a struct used below in FireEvent // in hopes that we can reuse some of this eventing in other platforms if possible. type Event struct { Type string Message string Error error } // nb: these events currently map to those expected by // equinix metal. if/when we do other platforms, we should // maybe generalize this and map the events inside each platform. const ( // EventTypeActivate is the activate event string. EventTypeActivate = "activate" // EventTypeFailure is the failure event string. EventTypeFailure = "failure" // EventTypeInfo is the info event string. EventTypeInfo = "info" // EventTypeConfigLoaded is the config loaded event string. EventTypeConfigLoaded = "talos.prov.config.loaded" // EventTypeRebooted is the reboot event string. EventTypeRebooted = "talos.prov.host.rebooted" // EventTypeInstalled is the installation event string. EventTypeInstalled = "talos.prov.os.installed" // EventTypeUpgraded is the upgrade event string. EventTypeUpgraded = "talos.prov.os.upgraded" ) // CurrentPlatform is a helper func for discovering the current platform. func CurrentPlatform() (p runtime.Platform, err error) { if containermode.InContainer() { return newPlatform("container") } var platform string if p := procfs.ProcCmdline().Get(constants.KernelParamPlatform).First(); p != nil { platform = *p } if p, ok := os.LookupEnv("PLATFORM"); ok { platform = p } if platform == "" { return nil, errors.New("failed to determine platform") } return newPlatform(platform) } // NewPlatform initializes and returns a runtime.Platform. func NewPlatform(platform string) (p runtime.Platform, err error) { return newPlatform(platform) } //nolint:gocyclo,cyclop func newPlatform(platform string) (p runtime.Platform, err error) { switch platform { case "akamai": p = &akamai.Akamai{} case "aws": return aws.NewAWS() case "azure": p = &azure.Azure{} case "cloudstack": p = &cloudstack.Cloudstack{} case "container": p = &container.Container{} case "digital-ocean": p = &digitalocean.DigitalOcean{} case "gcp": p = &gcp.GCP{} case "hcloud": p = &hcloud.Hcloud{} case constants.PlatformMetal: _, metalAgentCheckErr := os.Stat(constants.MetalAgentModeFlagPath) p = &metal.Metal{ IsAgent: metalAgentCheckErr == nil, } case "opennebula": p = &opennebula.OpenNebula{} case "openstack": p = &openstack.OpenStack{} case "oracle": p = &oracle.Oracle{} case "nocloud": p = &nocloud.Nocloud{} // "packet" kept for backwards compatibility case "equinixMetal", "packet": p = &equinixmetal.EquinixMetal{} case "exoscale": p = &exoscale.Exoscale{} case "scaleway": p = &scaleway.Scaleway{} case "upcloud": p = &upcloud.UpCloud{} case "vmware": p = &vmware.VMware{} case "vultr": p = &vultr.Vultr{} default: return nil, fmt.Errorf("unknown platform: %q", platform) } return p, nil } // FireEvent will call the implemented platform's event function if we know it has one. // Error logging is handled in this function and we don't return any error values to the sequencer. func FireEvent(ctx context.Context, p runtime.Platform, e Event) { switch platType := p.(type) { case *equinixmetal.EquinixMetal: emEvent := equinixmetal.Event{ Type: e.Type, Message: e.Message, } if e.Error != nil { emEvent.Message = fmt.Sprintf("%s: %s", e.Message, e.Error) } eventErr := platType.FireEvent(ctx, emEvent) if eventErr != nil { log.Printf("failed sending event: %s", eventErr) } default: // Treat anything else as a no-op b/c we don't support event firing return } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package scaleway import ( "context" "encoding/json" "fmt" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/siderolabs/talos/pkg/download" ) const ( // ScalewayMetadataEndpoint is the local Scaleway endpoint. ScalewayMetadataEndpoint = "http://169.254.42.42/conf?format=json" // ScalewayUserDataEndpoint is the local Scaleway endpoint for the config. ScalewayUserDataEndpoint = "http://169.254.42.42/user_data/cloud-init" ) func (u *Scaleway) getMetadata(ctx context.Context) (*instance.Metadata, error) { metaConfigDl, err := download.Download(ctx, ScalewayMetadataEndpoint) if err != nil { return nil, fmt.Errorf("error fetching metadata: %w", err) } var meta instance.Metadata if err = json.Unmarshal(metaConfigDl, &meta); err != nil { return nil, err } return &meta, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package scaleway provides the Scaleway platform implementation. package scaleway import ( "context" "fmt" "log" "net/netip" "strconv" "github.com/cosi-project/runtime/pkg/state" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/scaleway/scaleway-sdk-go/scw" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Scaleway is the concrete type that implements the runtime.Platform interface. type Scaleway struct{} // Name implements the runtime.Platform interface. func (s *Scaleway) Name() string { return "scaleway" } // ParseMetadata converts Scaleway platform metadata into platform network config. // //nolint:gocyclo func (s *Scaleway) ParseMetadata(metadata *instance.Metadata) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var publicIPs []string if metadata.PublicIP.Address != "" && metadata.PublicIP.Family == "inet" { publicIPs = append(publicIPs, metadata.PublicIP.Address) } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: "eth0", Up: true, ConfigLayer: network.ConfigPlatform, }) gw, _ := netip.ParsePrefix("169.254.42.42/32") //nolint:errcheck route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, OutLinkName: "eth0", Destination: gw, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: 4 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = []network.RouteSpecSpec{route} if len(metadata.PublicIpsV4) > 0 { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } if metadata.IPv6.Address != "" || len(metadata.PublicIpsV6) > 0 { address := metadata.IPv6.Address netmask := metadata.IPv6.Netmask gateway := metadata.IPv6.Gateway if address == "" || netmask == "" || gateway == "" { address = metadata.PublicIpsV6[0].Address netmask = metadata.PublicIpsV6[0].Netmask gateway = metadata.PublicIpsV6[0].Gateway } bits, err := strconv.Atoi(netmask) if err != nil { return nil, err } ip, err := netip.ParseAddr(address) if err != nil { return nil, err } addr := netip.PrefixFrom(ip, bits) publicIPs = append(publicIPs, address) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: "eth0", Address: addr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet6, }, ) gw, err := netip.ParseAddr(gateway) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: "eth0", Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } zone, err := scw.ParseZone(metadata.Location.ZoneID) if err != nil { return nil, err } region, err := zone.Region() if err != nil { return nil, err } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: s.Name(), Hostname: metadata.Hostname, Region: region.String(), Zone: zone.String(), InstanceType: metadata.CommercialType, InstanceID: metadata.ID, ProviderID: fmt.Sprintf("scaleway://instance/%s/%s", zone.String(), metadata.ID), } return networkConfig, nil } // Configuration implements the runtime.Platform interface. // //nolint:stylecheck func (s *Scaleway) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from %q", ScalewayUserDataEndpoint) return download.Download(ctx, ScalewayUserDataEndpoint, download.WithLowSrcPort(), download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the runtime.Platform interface. func (s *Scaleway) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (s *Scaleway) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter("console").Append("tty1").Append("ttyS0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), procfs.NewParameter(constants.KernelParamDashboardDisabled).Append("1"), } } // NetworkConfiguration implements the runtime.Platform interface. func (s *Scaleway) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching scaleway instance config from: %q", ScalewayMetadataEndpoint) metadata, err := s.getMetadata(ctx) if err != nil { return err } networkConfig, err := s.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/scaleway_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package scaleway_test import ( _ "embed" "encoding/json" "testing" "github.com/scaleway/scaleway-sdk-go/api/instance/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway" ) //go:embed testdata/metadata-v1.json var rawMetadataV1 []byte //go:embed testdata/metadata-v2.json var rawMetadataV2 []byte //go:embed testdata/metadata-v3.json var rawMetadataV3 []byte //go:embed testdata/expected-v1.yaml var expectedNetworkConfigV1 string //go:embed testdata/expected-v2.yaml var expectedNetworkConfigV2 string //go:embed testdata/expected-v3.yaml var expectedNetworkConfigV3 string func TestParseMetadata(t *testing.T) { p := &scaleway.Scaleway{} for _, tt := range []struct { name string raw []byte expected string }{ { name: "V1", raw: rawMetadataV1, expected: expectedNetworkConfigV1, }, { name: "V2", raw: rawMetadataV2, expected: expectedNetworkConfigV2, }, { name: "V3", raw: rawMetadataV3, expected: expectedNetworkConfigV3, }, } { t.Run(tt.name, func(t *testing.T) { var metadata instance.Metadata require.NoError(t, json.Unmarshal(tt.raw, &metadata)) networkConfig, err := p.ParseMetadata(&metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, tt.expected, string(marshaled)) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected-v1.yaml ================================================ addresses: - address: 2001:111:222:3333::1/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: 169.254.42.42/32 src: "" gateway: "" outLinkName: eth0 table: main priority: 4096 scope: link type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: '2001:111:222:3333::' outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: scw-talos domainname: "" layer: platform resolvers: [] timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 layer: platform externalIPs: - 11.22.222.222 - 2001:111:222:3333::1 metadata: platform: scaleway hostname: scw-talos region: fr-par zone: fr-par-1 instanceType: DEV1-S instanceId: 11111111-1111-1111-1111-111111111111 providerId: scaleway://instance/fr-par-1/11111111-1111-1111-1111-111111111111 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected-v2.yaml ================================================ addresses: - address: 2001:111:222:3333::1/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: 169.254.42.42/32 src: "" gateway: "" outLinkName: eth0 table: main priority: 4096 scope: link type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: fe80::dc00:ff:fe12:3456 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: scw-talos domainname: "" layer: platform resolvers: [] timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 layer: platform externalIPs: - 11.22.222.222 - 2001:111:222:3333::1 metadata: platform: scaleway hostname: scw-talos region: fr-par zone: fr-par-2 instanceType: DEV1-S instanceId: 11111111-1111-1111-1111-111111111111 providerId: scaleway://instance/fr-par-2/11111111-1111-1111-1111-111111111111 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/expected-v3.yaml ================================================ addresses: - address: 2001:111:222:3333::1/64 linkName: eth0 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: 169.254.42.42/32 src: "" gateway: "" outLinkName: eth0 table: main priority: 4096 scope: link type: unicast flags: "" protocol: static layer: platform - family: inet6 dst: "" src: "" gateway: fe80::dc00:ff:fe12:3456 outLinkName: eth0 table: main priority: 2048 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: scw-talos domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 2001:111:222:3333::1 metadata: platform: scaleway hostname: scw-talos region: nl-ams zone: nl-ams-1 instanceType: DEV1-S instanceId: 11111111-1111-1111-1111-111111111111 providerId: scaleway://instance/nl-ams-1/11111111-1111-1111-1111-111111111111 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/metadata-v1.json ================================================ { "id": "11111111-1111-1111-1111-111111111111", "name": "scw-talos", "commercial_type": "DEV1-S", "hostname": "scw-talos", "tags": [], "state_detail": "booted", "public_ip": { "id": "11111111-1111-1111-1111-111111111111", "address": "11.22.222.222", "dynamic": false, "family": "inet" }, "public_ips_v4": [ { "address": "11.22.222.222", "dynamic": false, "family": "inet", "gateway": null, "id": "11111111-1111-1111-1111-111111111111", "ipam_id": null, "netmask": "32", "provisioning_mode": "dhcp", "state": "detached", "tags": [] } ], "public_ips_v6": [], "private_ip": "10.00.222.222", "ipv6": { "address": "2001:111:222:3333::1", "gateway": "2001:111:222:3333::", "netmask": "64" }, "location": { "zone_id": "fr-par-1" } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/metadata-v2.json ================================================ { "id": "11111111-1111-1111-1111-111111111111", "name": "scw-talos", "commercial_type": "DEV1-S", "hostname": "scw-talos", "tags": [], "state_detail": "booted", "public_ip": { "id": "11111111-1111-1111-1111-111111111111", "address": "11.22.222.222", "dynamic": false, "family": "inet" }, "public_ips_v4": [ { "address": "11.22.222.222", "dynamic": false, "family": "inet", "gateway": "11.22.222.1", "netmask": "32" } ], "public_ips_v6": [ { "address": "2001:111:222:3333::1", "dynamic": false, "family": "inet6", "gateway": "fe80::dc00:ff:fe12:3456", "netmask": "64" } ], "location": { "zone_id": "fr-par-2" } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/scaleway/testdata/metadata-v3.json ================================================ { "id": "11111111-1111-1111-1111-111111111111", "name": "scw-talos", "commercial_type": "DEV1-S", "hostname": "scw-talos", "tags": [], "state_detail": "booted", "public_ip": { "id": "11111111-1111-1111-1111-111111111111", "address": "2001:111:222:3333::1", "dynamic": false }, "public_ips_v4": [], "public_ips_v6": [ { "address": "2001:111:222:3333::1", "dynamic": false, "family": "inet6", "gateway": "fe80::dc00:ff:fe12:3456", "netmask": "64" } ], "location": { "zone_id": "ams1" } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package upcloud import ( "context" "encoding/json" "fmt" "github.com/siderolabs/talos/pkg/download" ) const ( // UpCloudMetadataEndpoint is the local UpCloud endpoint. UpCloudMetadataEndpoint = "http://169.254.169.254/metadata/v1.json" // UpCloudUserDataEndpoint is the local UpCloud endpoint for the config. UpCloudUserDataEndpoint = "http://169.254.169.254/metadata/v1/user_data" ) // MetadataConfig represents a metadata Upcloud instance. type MetadataConfig struct { Hostname string `json:"hostname,omitempty"` InstanceID string `json:"instance_id,omitempty"` PublicKeys []string `json:"public_keys,omitempty"` Zone string `json:"region,omitempty"` Tags []string `json:"tags,omitempty"` Network struct { Interfaces []struct { Index int `json:"index,omitempty"` IPAddresses []struct { Address string `json:"address,omitempty"` DHCP bool `json:"dhcp,omitempty"` DNS []string `json:"dns,omitempty"` Family string `json:"family,omitempty"` Floating bool `json:"floating,omitempty"` Gateway string `json:"gateway,omitempty"` Network string `json:"network,omitempty"` } `json:"ip_addresses,omitempty"` MAC string `json:"mac,omitempty"` NetworkType string `json:"type,omitempty"` NetworkID string `json:"network_id,omitempty"` } `json:"interfaces,omitempty"` DNS []string `json:"dns,omitempty"` } `json:"network"` } func (u *UpCloud) getMetadata(ctx context.Context) (*MetadataConfig, error) { metaConfigDl, err := download.Download(ctx, UpCloudMetadataEndpoint) if err != nil { return nil, fmt.Errorf("error fetching metadata: %w", err) } var meta MetadataConfig if err = json.Unmarshal(metaConfigDl, &meta); err != nil { return nil, err } return &meta, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/expected.yaml ================================================ addresses: - address: 185.70.197.3/32 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 2a04:3544:8000:1000:0:1111:2222:3333/64 linkName: eth2 family: inet6 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet6 dst: 2a04:3544:8000:1000::/64 src: "" gateway: 2a04:3544:8000:1000::1 outLinkName: eth2 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: - dnsServers: - 94.237.127.9 - 94.237.40.9 - 2a04:3540:53::1 - 2a04:3544:53::1 layer: platform timeServers: [] operators: - operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 layer: platform - operator: dhcp4 linkName: eth1 requireUp: true dhcp4: routeMetric: 1024 layer: platform externalIPs: - 185.70.197.2 - 2a04:3544:8000:1000:0:1111:2222:3333 metadata: platform: upcloud hostname: talos instanceId: 00123456-1111-2222-3333-123456789012 providerId: upcloud://00123456-1111-2222-3333-123456789012 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/testdata/metadata.json ================================================ { "cloud_name": "upcloud", "instance_id": "00123456-1111-2222-3333-123456789012", "hostname": "talos", "network": { "interfaces": [ { "index": 1, "ip_addresses": [ { "address": "185.70.197.2", "dhcp": true, "dns": [ "94.237.127.9", "94.237.40.9" ], "family": "IPv4", "floating": false, "gateway": "185.70.196.1", "network": "185.70.196.0/22" }, { "address": "185.70.197.3", "dhcp": false, "dns": null, "family": "IPv4", "floating": true, "gateway": "", "network": "185.70.197.3/32" } ], "mac": "5e:bf:5e:02:28:07", "network_id": "035ef879-1111-2222-3333-123456789012", "type": "public" }, { "index": 2, "ip_addresses": [ { "address": "10.11.0.2", "dhcp": true, "dns": null, "family": "IPv4", "floating": false, "gateway": "10.11.0.1", "network": "10.11.0.0/22" } ], "mac": "5e:bf:5e:02:cd:e0", "network_id": "031c9f9c-1111-2222-3333-123456789012", "type": "utility" }, { "index": 3, "ip_addresses": [ { "address": "2a04:3544:8000:1000:0000:1111:2222:3333", "dhcp": false, "dns": [ "2a04:3540:53::1", "2a04:3544:53::1" ], "family": "IPv6", "floating": false, "gateway": "2a04:3544:8000:1000::1", "network": "2a04:3544:8000:1000::/64" } ], "mac": "5e:bf:5e:02:78:a4", "network_id": "03b326a2-1111-2222-3333-123456789012", "type": "public" } ], "dns": [ "94.237.127.9", "94.237.40.9" ] }, "storage": {}, "tags": [], "user_data": "", "vendor_data": "" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package upcloud provides the UpCloud platform implementation. package upcloud import ( "context" "fmt" "log" "net/netip" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // UpCloud is the concrete type that implements the runtime.Platform interface. type UpCloud struct{} // Name implements the runtime.Platform interface. func (u *UpCloud) Name() string { return "upcloud" } // ParseMetadata converts Upcloud metadata into platform network configuration. // //nolint:gocyclo func (u *UpCloud) ParseMetadata(metadata *MetadataConfig) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var ( publicIPs []string dnsIPs []netip.Addr ) firstIP := true for _, addr := range metadata.Network.Interfaces { if addr.Index <= 0 { // protect from negative interface name continue } iface := fmt.Sprintf("eth%d", addr.Index-1) networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: iface, Up: true, ConfigLayer: network.ConfigPlatform, }) for _, ip := range addr.IPAddresses { if firstIP { publicIPs = append(publicIPs, ip.Address) firstIP = false } for _, addr := range ip.DNS { if ipAddr, err := netip.ParseAddr(addr); err == nil { dnsIPs = append(dnsIPs, ipAddr) } } if ip.DHCP && ip.Family == "IPv4" { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: iface, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } if !ip.DHCP { ntwrk, err := netip.ParsePrefix(ip.Network) if err != nil { return nil, err } addr, err := netip.ParseAddr(ip.Address) if err != nil { return nil, err } ipPrefix := netip.PrefixFrom(addr, ntwrk.Bits()) family := nethelpers.FamilyInet4 if addr.Is6() { publicIPs = append(publicIPs, ip.Address) family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: iface, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) if ip.Gateway != "" { gw, err := netip.ParseAddr(ip.Gateway) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, Destination: ntwrk, OutLinkName: iface, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: family, Priority: network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } } } if len(dnsIPs) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: u.Name(), Hostname: metadata.Hostname, Zone: metadata.Zone, InstanceID: metadata.InstanceID, ProviderID: fmt.Sprintf("upcloud://%s", metadata.InstanceID), } return networkConfig, nil } // Configuration implements the runtime.Platform interface. func (u *UpCloud) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", UpCloudUserDataEndpoint) return download.Download(ctx, UpCloudUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the runtime.Platform interface. func (u *UpCloud) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (u *UpCloud) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (u *UpCloud) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching UpCloud instance config from: %q", UpCloudMetadataEndpoint) metadata, err := u.getMetadata(ctx) if err != nil { return err } networkConfig, err := u.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud/upcloud_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package upcloud_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/upcloud" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &upcloud.UpCloud{} var metadata upcloud.MetadataConfig require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) networkConfig, err := p.ParseMetadata(&metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vmware provides the VMware platform implementation. package vmware import ( "context" "fmt" "log" "net/netip" "strings" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NetworkConfig maps to VMware GuestInfo metadata. // See also definition of GuestInfo in CAPV https://github.com/kubernetes-sigs/cluster-api-provider-vsphere/blob/main/pkg/util/constants.go type NetworkConfig struct { InstanceID string `yaml:"instance-id"` LocalHostname string `yaml:"local-hostname"` // Talos doesn't block on network, it will reconfigure itself as network information becomes available. WaitOnNetwork is not used. WaitOnNetwork struct { Ipv4 bool `yaml:"ipv4"` Ipv6 bool `yaml:"ipv6"` } `yaml:"wait-on-network,omitempty"` Network struct { Version int `yaml:"version"` Ethernets map[string]Ethernet `yaml:"ethernets"` } Routes []Route `yaml:"routes,omitempty"` } // Ethernet holds network interface info. type Ethernet struct { Match struct { Name string `yaml:"name,omitempty"` HWAddr string `yaml:"macaddress,omitempty"` } `yaml:"match,omitempty"` SetName string `yaml:"set-name,omitempty"` Wakeonlan bool `yaml:"wakeonlan,omitempty"` DHCPv4 bool `yaml:"dhcp4,omitempty"` DHCP4Overrides DHCPOverrides `yaml:"dhcp4-overrides,omitempty"` DHCPv6 bool `yaml:"dhcp6,omitempty"` DHCP6Overrides DHCPOverrides `yaml:"dhcp6-overrides,omitempty"` Address []string `yaml:"addresses,omitempty"` Gateway4 string `yaml:"gateway4,omitempty"` Gateway6 string `yaml:"gateway6,omitempty"` MTU int `yaml:"mtu,omitempty"` NameServers struct { Search []string `yaml:"search,omitempty"` Address []string `yaml:"addresses,omitempty"` } `yaml:"nameservers,omitempty"` Routes []Route `yaml:"routes,omitempty"` } // Route configuration. Not used. type Route struct { To string `yaml:"to,omitempty"` Via string `yaml:"via,omitempty"` Metric string `yaml:"metric,omitempty"` } // DHCPOverrides is partial implemented. Only RouteMetric is use, the other elements are not processed. type DHCPOverrides struct { Hostname string `yaml:"hostname,omitempty"` RouteMetric uint32 `yaml:"route-metric,omitempty"` SendHostname string `yaml:"send-hostname,omitempty"` UseDNS string `yaml:"use-dns,omitempty"` UseDomains string `yaml:"use-domains,omitempty"` UseHostname string `yaml:"use-hostname,omitempty"` UseMTU string `yaml:"use-mtu,omitempty"` UseNTP string `yaml:"use-ntp,omitempty"` UseRoutes string `yaml:"use-routes,omitempty"` } // ApplyNetworkConfigV2 gets GuestInfo and applies to the Talos runtime platform network configuration. // //nolint:gocyclo,cyclop func (v *VMware) ApplyNetworkConfigV2(ctx context.Context, st state.State, config *NetworkConfig, networkConfig *runtime.PlatformNetworkConfig) error { var dnsIPs []netip.Addr hostInterfaces, err := safe.StateListAll[*network.LinkStatus](ctx, st) if err != nil { return fmt.Errorf("error listing host interfaces: %w", err) } for name, eth := range config.Network.Ethernets { if eth.SetName != "" { name = eth.SetName } if !strings.HasPrefix(name, "eth") { continue } if eth.Match.HWAddr != "" { var availableMACAddresses []string macAddressMatched := false for hostInterface := range hostInterfaces.All() { macAddress := hostInterface.TypedSpec().PermanentAddr.String() if macAddress == eth.Match.HWAddr { name = hostInterface.Metadata().ID() macAddressMatched = true break } availableMACAddresses = append(availableMACAddresses, macAddress) } if !macAddressMatched { log.Printf("vmware: no link with matching MAC address %q (available %v), defaulted to use name %s instead", eth.Match.HWAddr, availableMACAddresses, name) } } networkConfig.Links = append(networkConfig.Links, network.LinkSpecSpec{ Name: name, Up: true, MTU: uint32(eth.MTU), ConfigLayer: network.ConfigPlatform, }) if eth.DHCPv4 { routeMetric := uint32(network.DefaultRouteMetric) if eth.DHCP4Overrides.RouteMetric != 0 { routeMetric = eth.DHCP4Overrides.RouteMetric } networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: name, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: routeMetric, }, ConfigLayer: network.ConfigPlatform, }) } if eth.DHCPv6 { routeMetric := uint32(2 * network.DefaultRouteMetric) if eth.DHCP4Overrides.RouteMetric != 0 { routeMetric = eth.DHCP6Overrides.RouteMetric } networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP6, LinkName: name, RequireUp: true, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: routeMetric, }, ConfigLayer: network.ConfigPlatform, }) } for _, addr := range eth.Address { ipPrefix, err := netip.ParsePrefix(addr) if err != nil { return err } family := nethelpers.FamilyInet4 if ipPrefix.Addr().Is6() { family = nethelpers.FamilyInet6 } networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: name, Address: ipPrefix, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: family, }, ) } if eth.Gateway4 != "" { gw, err := netip.ParseAddr(eth.Gateway4) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: name, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, Priority: network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } if eth.Gateway6 != "" { gw, err := netip.ParseAddr(eth.Gateway6) if err != nil { return err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: name, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet6, Priority: 2 * network.DefaultRouteMetric, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } for _, addr := range eth.NameServers.Address { if ip, err := netip.ParseAddr(addr); err == nil { dnsIPs = append(dnsIPs, ip) } else { return err } } } if config.LocalHostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(config.LocalHostname); err != nil { return err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } if len(dnsIPs) > 0 { networkConfig.Resolvers = append(networkConfig.Resolvers, network.ResolverSpecSpec{ DNSServers: dnsIPs, ConfigLayer: network.ConfigPlatform, }) } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/testdata/expected-match-by-mac.yaml ================================================ addresses: - address: 192.168.0.230/24 linkName: eth2 family: inet4 scope: global flags: permanent layer: platform links: - name: eth2 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.0.1 outLinkName: eth2 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: vmware-test-controlplane-zhnhr domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: [] ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/testdata/expected-match-by-name.yaml ================================================ addresses: - address: 192.168.0.230/24 linkName: eth1 family: inet4 scope: global flags: permanent layer: platform links: - name: eth1 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform routes: - family: inet4 dst: "" src: "" gateway: 192.168.0.1 outLinkName: eth1 table: main priority: 1024 scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: vmware-test-controlplane-zhnhr domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: [] ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/testdata/metadata-match-by-mac.yaml ================================================ instance-id: "1" local-hostname: "vmware-test-controlplane-zhnhr" wait-on-network: ipv4: false ipv6: false network: version: 2 ethernets: id0: match: macaddress: "68:05:ca:b8:f1:f9" set-name: "eth0" wakeonlan: true addresses: - "192.168.0.230/24" gateway4: "192.168.0.1" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/testdata/metadata-match-by-name.yaml ================================================ instance-id: "1" local-hostname: "vmware-test-controlplane-zhnhr" wait-on-network: ipv4: false ipv6: false network: version: 2 ethernets: id0: match: name: "eth1" set-name: "eth1" wakeonlan: true addresses: - "192.168.0.230/24" gateway4: "192.168.0.1" ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vmware provides the VMware platform implementation. package vmware import ( "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // VMware is the concrete type that implements the platform.Platform interface. type VMware struct{} // Name implements the platform.Platform interface. func (v *VMware) Name() string { return "vmware" } // Mode implements the platform.Platform interface. func (v *VMware) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (v *VMware) KernelArgs(arch string, _ quirks.Quirks) procfs.Parameters { switch arch { case "amd64": return []*procfs.Parameter{ procfs.NewParameter(constants.KernelParamConfig).Append(constants.ConfigGuestInfo), procfs.NewParameter("console").Append("tty0").Append("ttyS0"), procfs.NewParameter("earlyprintk").Append("ttyS0,115200"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } case "arm64": return []*procfs.Parameter{ procfs.NewParameter(constants.KernelParamConfig).Append(constants.ConfigGuestInfo), procfs.NewParameter("console").Append("tty0").Append("ttyAMA0"), procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } default: return nil // not supported } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !(amd64 || arm64) package vmware import ( "context" "errors" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // Configuration implements the platform.Platform interface. func (v *VMware) Configuration(context.Context, state.State) ([]byte, error) { return nil, errors.New("arch not supported") } // NetworkConfiguration implements the runtime.Platform interface. func (v *VMware) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_supported.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // the build is constrained to architectures supported by the hypercall package //go:build amd64 || arm64 package vmware import ( "context" "encoding/base64" "encoding/xml" "errors" "fmt" "log" "log/slog" "github.com/cosi-project/runtime/pkg/state" "github.com/equinix-ms/go-vmw-guestrpc/pkg/hypercall" "github.com/equinix-ms/go-vmw-guestrpc/pkg/nanotoolbox" "github.com/siderolabs/go-procfs/procfs" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" platformerrors "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/pkg/machinery/constants" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Read and de-base64 a property from `extraConfig`. This is commonly referred to as `guestinfo`. func readConfigFromExtraConfig(rpci *nanotoolbox.RPCI, key string) ([]byte, error) { val, err := rpci.InfoGet(constants.VMwareGuestInfoPrefix+key, "") if err != nil { return nil, fmt.Errorf("failed to get extraConfig %s: %w", key, err) } if val == "" { // not present log.Printf("empty (thus absent) %s", key) return nil, nil } decoded, err := base64.StdEncoding.DecodeString(val) if err != nil { return nil, fmt.Errorf("failed to decode extraConfig %s: %w", key, err) } if len(decoded) == 0 { log.Printf("skipping zero-length config in extraConfig") return nil, nil } return decoded, nil } // ofvEnv and related types are extracted from github.com/vmware/govmomi/ovf/env.go. type ovfEnvFile struct { XMLName xml.Name `xml:"http://schemas.dmtf.org/ovf/environment/1 Environment"` ID string `xml:"id,attr"` EsxID string `xml:"http://www.vmware.com/schema/ovfenv esxId,attr"` Platform *ovfPlatformSection `xml:"PlatformSection"` Property *ovfPropertySection `xml:"PropertySection"` } type ovfPlatformSection struct { Kind string `xml:"Kind"` Version string `xml:"Version"` Vendor string `xml:"Vendor"` Locale string `xml:"Locale"` } type ovfPropertySection struct { Properties []ovfEnvProperty `xml:"Property"` } type ovfEnvProperty struct { Key string `xml:"key,attr"` Value string `xml:"value,attr"` } // Read and de-base64 a property from the OVF env. This is different way to pass data to your VM. // This is how data gets passed when using vCloud Director. func readConfigFromOvf(rpci *nanotoolbox.RPCI, key string) ([]byte, error) { ovfXML, err := rpci.InfoGet(constants.VMwareGuestInfoPrefix+constants.VMwareGuestInfoOvfEnvKey, "") if err != nil { return nil, fmt.Errorf("failed to read extraConfig var '%s': %w", key, err) } if ovfXML == "" { // value empty (probably because not present) return nil, nil } var ovfEnv ovfEnvFile err = xml.Unmarshal([]byte(ovfXML), &ovfEnv) if err != nil { return nil, fmt.Errorf("failed to unmarshall XML from OVF env: %w", err) } if ovfEnv.Property == nil || ovfEnv.Property.Properties == nil { // no data in OVF env log.Printf("empty OVF env") return nil, nil } log.Printf("searching for property '%s' in OVF", key) for _, property := range ovfEnv.Property.Properties { // iterate to check if our key is present if property.Key == key { log.Printf("it is there, decoding") decoded, err := base64.StdEncoding.DecodeString(property.Value) if err != nil { return nil, fmt.Errorf("failed to decode OVF property %s: %w", property.Key, err) } if len(decoded) == 0 { log.Printf("skipping zero-length config in OVF") return nil, nil } return decoded, nil } } return nil, nil } func initializeRPCI() (*nanotoolbox.RPCI, error) { inVMWare, err := hypercall.IsVMWareVM() if err != nil { return nil, fmt.Errorf("could not determine if we are running in VMWare VM: %w", err) } if !inVMWare { return nil, errors.New("this is not a VMWare VM") } rpci, err := nanotoolbox.NewRPCI(slog.New(slog.NewTextHandler(log.Writer(), nil))) if err != nil { return nil, fmt.Errorf("could not initialize RPCI: %w", err) } if err = rpci.Start(); err != nil { return nil, fmt.Errorf("could not start RPCI: %w", err) } return rpci, nil } // Configuration implements the platform.Platform interface. // //nolint:gocyclo func (v *VMware) Configuration(context.Context, state.State) ([]byte, error) { var option *string if option = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); option == nil { return nil, fmt.Errorf("%s not found", constants.KernelParamConfig) } if *option == constants.ConfigGuestInfo { log.Printf("fetching machine config from VMware extraConfig or OVF env") rpci, err := initializeRPCI() if err != nil { return nil, fmt.Errorf("error initiliazing RPCI: %w", err) } defer rpci.Stop() //nolint:errcheck // try to fetch `talos.config` from plain extraConfig (ie, the old behavior) log.Printf("trying to find '%s' in extraConfig", constants.VMwareGuestInfoConfigKey) config, err := readConfigFromExtraConfig(rpci, constants.VMwareGuestInfoConfigKey) if err != nil { return nil, err } if config != nil { return config, nil } // try to fetch `userdata` from plain extraConfig (ie, the old behavior) log.Printf("trying to find '%s' in extraConfig", constants.VMwareGuestInfoFallbackKey) config, err = readConfigFromExtraConfig(rpci, constants.VMwareGuestInfoFallbackKey) if err != nil { return nil, err } if config != nil { return config, nil } // try to fetch `talos.config` from OVF log.Printf("trying to find '%s' in OVF env", constants.VMwareGuestInfoConfigKey) config, err = readConfigFromOvf(rpci, constants.VMwareGuestInfoConfigKey) if err != nil { return nil, err } if config != nil { return config, nil } // try to fetch `userdata` from OVF log.Printf("trying to find '%s' in OVF env", constants.VMwareGuestInfoFallbackKey) config, err = readConfigFromOvf(rpci, constants.VMwareGuestInfoFallbackKey) if err != nil { return nil, err } if config != nil { return config, nil } return nil, platformerrors.ErrNoConfigSource } return nil, nil } // Read VMware GuestInfo metadata if available. func (v *VMware) readMetadata(rpci *nanotoolbox.RPCI) ([]byte, error) { guestInfoMetadata, err := readConfigFromExtraConfig(rpci, constants.VMwareGuestInfoMetadataKey) if err != nil { return nil, err } if guestInfoMetadata == nil { guestInfoMetadata, err = readConfigFromOvf(rpci, constants.VMwareGuestInfoMetadataKey) } if err != nil { return nil, err } return guestInfoMetadata, nil } // NetworkConfiguration implements the runtime.Platform interface. func (v *VMware) NetworkConfiguration(ctx context.Context, st state.State, ch chan<- *runtime.PlatformNetworkConfig) error { rpci, err := initializeRPCI() if err != nil { return fmt.Errorf("error initiliazing RPCI: %w", err) } defer rpci.Stop() //nolint:errcheck guestInfoMetadata, err := v.readMetadata(rpci) if err != nil { return fmt.Errorf("failed to read GuestInfo: %w", err) } networkConfig := &runtime.PlatformNetworkConfig{ Metadata: &runtimeres.PlatformMetadataSpec{Platform: v.Name()}, } if guestInfoMetadata != nil { var unmarshalledNetworkConfig NetworkConfig if err = yaml.Unmarshal(guestInfoMetadata, &unmarshalledNetworkConfig); err != nil { return fmt.Errorf("failed to unmarshall metadata '%s'. Error '%w'", guestInfoMetadata, err) } switch unmarshalledNetworkConfig.Network.Version { case 2: err := v.ApplyNetworkConfigV2(ctx, st, &unmarshalledNetworkConfig, networkConfig) if err != nil { return fmt.Errorf("failed to apply metadata '%s'. Error '%w'", guestInfoMetadata, err) } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: v.Name(), Hostname: unmarshalledNetworkConfig.LocalHostname, InstanceID: unmarshalledNetworkConfig.InstanceID, } default: return fmt.Errorf("GuestInfo version=%d is not supported. GuestInfo: %s", unmarshalledNetworkConfig.Network.Version, guestInfoMetadata) } } select { case <-ctx.Done(): return ctx.Err() case ch <- networkConfig: } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vmware_test import ( _ "embed" "fmt" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) //go:embed testdata/metadata-match-by-mac.yaml var rawMetadataMatchByMAC []byte //go:embed testdata/expected-match-by-mac.yaml var expectedNetworkConfigMatchByMAC string //go:embed testdata/metadata-match-by-name.yaml var rawMetadataMatchByName []byte //go:embed testdata/expected-match-by-name.yaml var expectedNetworkConfigMatchByName string func TestApplyNetworkConfigV2a(t *testing.T) { for _, tt := range []struct { name string raw []byte expected string }{ { name: "byMAC", raw: rawMetadataMatchByMAC, expected: expectedNetworkConfigMatchByMAC, }, { name: "byName", raw: rawMetadataMatchByName, expected: expectedNetworkConfigMatchByName, }, } { t.Run(tt.name, func(t *testing.T) { ctx := t.Context() st := state.WrapCore(namespaced.NewState(inmem.Build)) eth1 := network.NewLinkStatus(network.NamespaceName, "eth1") eth1.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf8} require.NoError(t, st.Create(ctx, eth1)) eth2 := network.NewLinkStatus(network.NamespaceName, "eth2") eth2.TypedSpec().PermanentAddr = nethelpers.HardwareAddr{0x68, 0x05, 0xca, 0xb8, 0xf1, 0xf9} require.NoError(t, st.Create(ctx, eth2)) var metadata vmware.NetworkConfig require.NoError(t, yaml.Unmarshal(tt.raw, &metadata)) v := &vmware.VMware{} networkConfig := &runtime.PlatformNetworkConfig{} err := v.ApplyNetworkConfigV2(ctx, st, &metadata, networkConfig) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) fmt.Print(string(marshaled)) assert.Equal(t, tt.expected, string(marshaled)) }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vultr import ( "context" "encoding/json" "fmt" "github.com/vultr/metadata" "github.com/siderolabs/talos/pkg/download" ) const ( // VultrMetadataEndpoint is the local Vultr endpoint fot the instance metadata. VultrMetadataEndpoint = "http://169.254.169.254/v1.json" // VultrExternalIPEndpoint is the local Vultr endpoint for the external IP. VultrExternalIPEndpoint = "http://169.254.169.254/latest/meta-data/public-ipv4" // VultrUserDataEndpoint is the local Vultr endpoint for the config. VultrUserDataEndpoint = "http://169.254.169.254/latest/user-data" ) func (g *Vultr) getMetadata(ctx context.Context) (*metadata.MetaData, error) { metaConfigDl, err := download.Download(ctx, VultrMetadataEndpoint) if err != nil { return nil, fmt.Errorf("error fetching metadata: %w", err) } var meta metadata.MetaData if err = json.Unmarshal(metaConfigDl, &meta); err != nil { return nil, err } return &meta, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/expected.yaml ================================================ addresses: - address: 95.111.222.111/23 linkName: eth0 family: inet4 scope: global flags: permanent layer: platform - address: 10.7.96.3/20 linkName: eth1 family: inet4 scope: global flags: permanent layer: platform links: - name: eth0 logical: false up: true mtu: 0 kind: "" type: netrom layer: platform - name: eth1 logical: false up: true mtu: 1450 kind: "" type: netrom layer: platform routes: - family: inet4 dst: "" src: "" gateway: 95.111.222.1 outLinkName: eth0 table: main scope: global type: unicast flags: "" protocol: static layer: platform hostnames: - hostname: talos domainname: "" layer: platform resolvers: [] timeServers: [] operators: [] externalIPs: - 95.111.222.111 - 2001:19f0:5001:2095:1111:2222:3333:4444 metadata: platform: vultr hostname: talos region: AMS instanceId: 91b07056-af72-4551-b15b-d57d34071be9 providerId: vultr://91b07056-af72-4551-b15b-d57d34071be9 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/testdata/metadata.json ================================================ { "bgp": { "ipv4": { "my-address": "", "my-asn": "", "peer-address": "", "peer-asn": "" }, "ipv6": { "my-address": "", "my-asn": "", "peer-address": "", "peer-asn": "" } }, "hostname": "talos", "instance-v2-id": "91b07056-af72-4551-b15b-d57d34071be9", "instanceid": "50190000", "interfaces": [ { "ipv4": { "additional": [], "address": "95.111.222.111", "gateway": "95.111.222.1", "netmask": "255.255.254.0" }, "ipv6": { "additional": [], "address": "2001:19f0:5001:2095:1111:2222:3333:4444", "network": "2001:19f0:5001:2095::", "prefix": "64" }, "mac": "56:00:03:89:53:e0", "network-type": "public" }, { "ipv4": { "additional": [], "address": "10.7.96.3", "gateway": "", "netmask": "255.255.240.0" }, "ipv6": { "additional": [], "network": "", "prefix": "" }, "mac": "5a:00:03:89:53:e0", "network-type": "private", "network-v2-id": "dadc2b30-0b55-4fa1-8c29-f67215bd5ac4", "networkid": "net6126811851cd7" } ], "public-keys": [ "ssh-ed25519" ], "region": { "regioncode": "AMS" }, "user-defined": [] } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vultr provides the Vultr platform implementation. package vultr import ( "context" "fmt" "log" "net" "net/netip" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-procfs/procfs" "github.com/vultr/metadata" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/errors" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/internal/netutils" "github.com/siderolabs/talos/pkg/download" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Vultr is the concrete type that implements the runtime.Platform interface. type Vultr struct{} // Name implements the runtime.Platform interface. func (v *Vultr) Name() string { return "vultr" } // ParseMetadata converts Vultr platform metadata into platform network config. // //nolint:gocyclo func (v *Vultr) ParseMetadata(metadata *metadata.MetaData) (*runtime.PlatformNetworkConfig, error) { networkConfig := &runtime.PlatformNetworkConfig{} if metadata.Hostname != "" { hostnameSpec := network.HostnameSpecSpec{ ConfigLayer: network.ConfigPlatform, } if err := hostnameSpec.ParseFQDN(metadata.Hostname); err != nil { return nil, err } networkConfig.Hostnames = append(networkConfig.Hostnames, hostnameSpec) } var publicIPs []string for i, addr := range metadata.Interfaces { iface := fmt.Sprintf("eth%d", i) link := network.LinkSpecSpec{ Name: iface, Up: true, ConfigLayer: network.ConfigPlatform, } if addr.NetworkType == "private" { link.MTU = 1450 } networkConfig.Links = append(networkConfig.Links, link) if addr.IPv4.Address != "" { if addr.NetworkType == "public" { publicIPs = append(publicIPs, addr.IPv4.Address) } ip, err := netip.ParseAddr(addr.IPv4.Address) if err != nil { return nil, err } netmask, err := netip.ParseAddr(addr.IPv4.Netmask) if err != nil { return nil, err } mask, _ := netmask.MarshalBinary() //nolint:errcheck // never fails ones, _ := net.IPMask(mask).Size() ipAddr := netip.PrefixFrom(ip, ones) networkConfig.Addresses = append(networkConfig.Addresses, network.AddressSpecSpec{ ConfigLayer: network.ConfigPlatform, LinkName: iface, Address: ipAddr, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), Family: nethelpers.FamilyInet4, }, ) if addr.IPv4.Gateway != "" { gw, err := netip.ParseAddr(addr.IPv4.Gateway) if err != nil { return nil, err } route := network.RouteSpecSpec{ ConfigLayer: network.ConfigPlatform, Gateway: gw, OutLinkName: iface, Table: nethelpers.TableMain, Protocol: nethelpers.ProtocolStatic, Type: nethelpers.TypeUnicast, Family: nethelpers.FamilyInet4, } route.Normalize() networkConfig.Routes = append(networkConfig.Routes, route) } } else { networkConfig.Operators = append(networkConfig.Operators, network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: iface, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: network.DefaultRouteMetric, }, ConfigLayer: network.ConfigPlatform, }) } if addr.IPv6.Address != "" { if addr.NetworkType == "public" { publicIPs = append(publicIPs, addr.IPv6.Address) } } } for _, ipStr := range publicIPs { if ip, err := netip.ParseAddr(ipStr); err == nil { networkConfig.ExternalIPs = append(networkConfig.ExternalIPs, ip) } } networkConfig.Metadata = &runtimeres.PlatformMetadataSpec{ Platform: v.Name(), Hostname: metadata.Hostname, Region: metadata.Region.RegionCode, InstanceID: metadata.InstanceV2ID, ProviderID: fmt.Sprintf("vultr://%s", metadata.InstanceV2ID), } return networkConfig, nil } // Configuration implements the runtime.Platform interface. // //nolint:stylecheck func (v *Vultr) Configuration(ctx context.Context, r state.State) ([]byte, error) { if err := netutils.Wait(ctx, r); err != nil { return nil, err } log.Printf("fetching machine config from: %q", VultrUserDataEndpoint) return download.Download(ctx, VultrUserDataEndpoint, download.WithErrorOnNotFound(errors.ErrNoConfigSource), download.WithErrorOnEmptyResponse(errors.ErrNoConfigSource)) } // Mode implements the runtime.Platform interface. func (v *Vultr) Mode() runtime.Mode { return runtime.ModeCloud } // KernelArgs implements the runtime.Platform interface. func (v *Vultr) KernelArgs(string, quirks.Quirks) procfs.Parameters { return []*procfs.Parameter{ procfs.NewParameter(constants.KernelParamNetIfnames).Append("0"), } } // NetworkConfiguration implements the runtime.Platform interface. func (v *Vultr) NetworkConfiguration(ctx context.Context, _ state.State, ch chan<- *runtime.PlatformNetworkConfig) error { log.Printf("fetching Vultr instance metadata from: %q", VultrMetadataEndpoint) metadata, err := v.getMetadata(ctx) if err != nil { return err } networkConfig, err := v.ParseMetadata(metadata) if err != nil { return err } select { case ch <- networkConfig: case <-ctx.Done(): return ctx.Err() } return nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/platform/vultr/vultr_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vultr_test import ( _ "embed" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/vultr/metadata" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform/vultr" ) //go:embed testdata/metadata.json var rawMetadata []byte //go:embed testdata/expected.yaml var expectedNetworkConfig string func TestParseMetadata(t *testing.T) { p := &vultr.Vultr{} var metadata metadata.MetaData require.NoError(t, json.Unmarshal(rawMetadata, &metadata)) networkConfig, err := p.ParseMetadata(&metadata) require.NoError(t, err) marshaled, err := yaml.Marshal(networkConfig) require.NoError(t, err) assert.Equal(t, expectedNetworkConfig, string(marshaled)) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "errors" "fmt" "io" "log" "os" "os/signal" "syscall" "time" "github.com/siderolabs/go-kmsg" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/acpi" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha2" krnl "github.com/siderolabs/talos/pkg/kernel" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/kernel" ) // Controller represents the controller responsible for managing the execution // of sequences. type Controller struct { s runtime.Sequencer r *Runtime v2 *v1alpha2.Controller priorityLock *PriorityLock[runtime.Sequence] } // NewController intializes and returns a controller. func NewController() (*Controller, error) { s, err := NewState() if err != nil { return nil, err } // TODO: this should be streaming capacity and probably some constant e := NewEvents(1000, 10) l := logging.NewCircularBufferLoggingManager(log.New(os.Stdout, "machined fallback logger: ", log.Flags())) ctlr := &Controller{ r: NewRuntime(s, e, l), s: NewSequencer(), priorityLock: NewPriorityLock[runtime.Sequence](), } ctlr.v2, err = v1alpha2.NewController(ctlr.r) if err != nil { return nil, err } if err := ctlr.setupLogging(); err != nil { return nil, err } return ctlr, nil } func (c *Controller) setupLogging() error { machinedLog, err := c.r.Logging().ServiceLog("machined").Writer() if err != nil { return err } if c.r.State().Platform().Mode() == runtime.ModeContainer { // send all the logs to machinedLog as well, but skip /dev/kmsg logging log.SetOutput(io.MultiWriter(log.Writer(), machinedLog)) log.SetPrefix("[talos] ") return nil } // disable ratelimiting for kmsg, otherwise logs might be not visible. // this should be set via kernel arg, but in case it's not set, try to force it. if err = krnl.WriteParam(&kernel.Param{ Key: "proc.sys.kernel.printk_devkmsg", Value: "on\n", }); err != nil { var serr syscall.Errno if !(errors.As(err, &serr) && serr == syscall.EINVAL) { // ignore EINVAL which is returned when kernel arg is set log.Printf("failed setting kernel.printk_devkmsg: %s, error ignored", err) } } if err = kmsg.SetupLogger(nil, "[talos]", machinedLog); err != nil { return fmt.Errorf("failed to setup logging: %w", err) } // Ensure that the logger is initialized and writer returns properly. if err = log.Output(1, "machined logger initialized"); err != nil { return fmt.Errorf("failed on log initialization: %w", err) } return nil } // Run executes all phases known to the controller in serial. `Controller` // aborts immediately if any phase fails. func (c *Controller) Run(ctx context.Context, seq runtime.Sequence, data any, setters ...runtime.LockOption) error { // We must ensure that the runtime is configured since all sequences depend // on the runtime. if c.r == nil { return runtime.ErrUndefinedRuntime } ctx, err := c.priorityLock.Lock(ctx, time.Minute, seq, setters...) if err != nil { if errors.Is(err, runtime.ErrLocked) { c.Runtime().Events().Publish(context.Background(), &machine.SequenceEvent{ Sequence: seq.String(), Action: machine.SequenceEvent_NOOP, Error: &common.Error{ Code: common.Code_LOCKED, Message: fmt.Sprintf("sequence not started: %s", runtime.ErrLocked.Error()), }, }) } return err } defer c.priorityLock.Unlock() phases, err := c.phases(seq, data) if err != nil { return err } err = c.run(ctx, seq, phases, data) if err != nil { code := common.Code_FATAL if errors.Is(err, context.Canceled) { code = common.Code_CANCELED } c.Runtime().Events().Publish(ctx, &machine.SequenceEvent{ Sequence: seq.String(), Action: machine.SequenceEvent_NOOP, Error: &common.Error{ Code: code, Message: fmt.Sprintf("sequence failed: %s", err.Error()), }, }) return err } return nil } // V1Alpha2 implements the controller interface. func (c *Controller) V1Alpha2() runtime.V1Alpha2Controller { return c.v2 } // Runtime implements the controller interface. func (c *Controller) Runtime() runtime.Runtime { return c.r } // Sequencer implements the controller interface. func (c *Controller) Sequencer() runtime.Sequencer { return c.s } // ListenForEvents starts the event listener. The listener will trigger a // shutdown in response to a SIGTERM signal and ACPI button/power event. func (c *Controller) ListenForEvents(ctx context.Context) error { sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGTERM) errCh := make(chan error, 2) go func() { <-sigs signal.Stop(sigs) log.Printf("shutdown via SIGTERM received") if err := c.Run(ctx, runtime.SequenceShutdown, &machine.ShutdownRequest{Force: true}, runtime.WithTakeover()); err != nil { if !runtime.IsRebootError(err) { log.Printf("shutdown failed: %v", err) } } errCh <- nil }() if c.r.State().Platform().Mode() == runtime.ModeContainer { return nil } go func() { if err := acpi.StartACPIListener(); err != nil { errCh <- err return } log.Printf("shutdown via ACPI received") if err := c.Run(ctx, runtime.SequenceShutdown, &machine.ShutdownRequest{Force: true}, runtime.WithTakeover()); err != nil { if !runtime.IsRebootError(err) { log.Printf("failed to run shutdown sequence: %s", err) } } errCh <- nil }() err := <-errCh return err } func (c *Controller) run(ctx context.Context, seq runtime.Sequence, phases []runtime.Phase, data any) error { c.Runtime().Events().Publish(ctx, &machine.SequenceEvent{ Sequence: seq.String(), Action: machine.SequenceEvent_START, }) defer c.Runtime().Events().Publish(ctx, &machine.SequenceEvent{ Sequence: seq.String(), Action: machine.SequenceEvent_STOP, }) start := time.Now() var ( number int phase runtime.Phase err error ) log.Printf("%s sequence: %d phase(s)", seq.String(), len(phases)) defer func() { if err != nil { if !runtime.IsRebootError(err) { log.Printf("%s sequence: failed", seq.String()) } } else { log.Printf("%s sequence: done: %s", seq.String(), time.Since(start)) } }() for number, phase = range phases { if phase.CheckFunc != nil && !phase.CheckFunc() { continue } // Make the phase number human friendly. number++ start := time.Now() progress := fmt.Sprintf("%d/%d", number, len(phases)) log.Printf("phase %s (%s): %d tasks(s)", phase.Name, progress, len(phase.Tasks)) if err = c.runPhase(ctx, phase, seq, data); err != nil { if !runtime.IsRebootError(err) { log.Printf("phase %s (%s): failed", phase.Name, progress) } return fmt.Errorf("error running phase %d in %s sequence: %w", number, seq.String(), err) } log.Printf("phase %s (%s): done, %s", phase.Name, progress, time.Since(start)) select { case <-ctx.Done(): return ctx.Err() default: } } return nil } func (c *Controller) runPhase(ctx context.Context, phase runtime.Phase, seq runtime.Sequence, data any) error { c.Runtime().Events().Publish(ctx, &machine.PhaseEvent{ Phase: phase.Name, Action: machine.PhaseEvent_START, }) defer c.Runtime().Events().Publish(ctx, &machine.PhaseEvent{ Phase: phase.Name, Action: machine.PhaseEvent_STOP, }) eg, ctx := errgroup.WithContext(ctx) for number, task := range phase.Tasks { // Make the task number human friendly. number++ eg.Go(func() error { progress := fmt.Sprintf("%d/%d", number, len(phase.Tasks)) if err := c.runTask(ctx, progress, task, seq, data); err != nil { return fmt.Errorf("task %s: failed, %w", progress, err) } return nil }) } return eg.Wait() } func (c *Controller) runTask(ctx context.Context, progress string, f runtime.TaskSetupFunc, seq runtime.Sequence, data any) error { task, taskName := f(seq, data) if task == nil { return nil } start := time.Now() c.Runtime().Events().Publish(ctx, &machine.TaskEvent{ Task: taskName, Action: machine.TaskEvent_START, }) var err error log.Printf("task %s (%s): starting", taskName, progress) defer func() { if err != nil { if !runtime.IsRebootError(err) { log.Printf("task %s (%s): failed: %s", taskName, progress, err) } } else { log.Printf("task %s (%s): done, %s", taskName, progress, time.Since(start)) } }() defer c.Runtime().Events().Publish(ctx, &machine.TaskEvent{ Task: taskName, Action: machine.TaskEvent_STOP, }) logger := log.New(log.Writer(), fmt.Sprintf("[talos] task %s (%s): ", taskName, progress), log.Flags()) err = task(ctx, logger, c.r) return err } //nolint:gocyclo func (c *Controller) phases(seq runtime.Sequence, data any) ([]runtime.Phase, error) { var phases []runtime.Phase switch seq { case runtime.SequenceBoot: phases = c.s.Boot(c.r) case runtime.SequenceInitialize: phases = c.s.Initialize(c.r) case runtime.SequenceInstall: phases = c.s.Install(c.r) case runtime.SequenceShutdown: in, ok := data.(*machine.ShutdownRequest) if !ok { return nil, runtime.ErrInvalidSequenceData } phases = c.s.Shutdown(c.r, in) case runtime.SequenceReboot: var in *machine.RebootRequest if req, ok := data.(*machine.RebootRequest); ok { in = req } else { log.Printf("warning: API reboot missing reboot request") } phases = c.s.Reboot(c.r, in) case runtime.SequenceUpgrade: in, ok := data.(*machine.UpgradeRequest) if !ok { return nil, runtime.ErrInvalidSequenceData } phases = c.s.Upgrade(c.r, in) case runtime.SequenceStageUpgrade: in, ok := data.(*machine.UpgradeRequest) if !ok { return nil, runtime.ErrInvalidSequenceData } phases = c.s.StageUpgrade(c.r, in) case runtime.SequenceMaintenanceUpgrade: in, ok := data.(*machine.UpgradeRequest) if !ok { return nil, runtime.ErrInvalidSequenceData } phases = c.s.MaintenanceUpgrade(c.r, in) case runtime.SequenceReset: in, ok := data.(runtime.ResetOptions) if !ok { return nil, runtime.ErrInvalidSequenceData } phases = c.s.Reset(c.r, in) case runtime.SequenceNoop: default: return nil, fmt.Errorf("sequence not implemented: %q", seq) } return phases, nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_controller_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint,testpackage package v1alpha1 import ( "context" "errors" "fmt" "log" "os" "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" v1alpha1server "github.com/siderolabs/talos/internal/app/machined/internal/server/v1alpha1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) type mockSequencer struct { callsMu sync.Mutex calls map[runtime.Sequence]int phases map[runtime.Sequence]PhaseList } func (m *mockSequencer) Boot(r runtime.Runtime) []runtime.Phase { return m.phases[runtime.SequenceBoot] } func (m *mockSequencer) Initialize(r runtime.Runtime) []runtime.Phase { return m.phases[runtime.SequenceInitialize] } func (m *mockSequencer) Install(r runtime.Runtime) []runtime.Phase { return m.phases[runtime.SequenceInstall] } func (m *mockSequencer) Reboot(r runtime.Runtime, _ *machine.RebootRequest) []runtime.Phase { return m.phases[runtime.SequenceReboot] } func (m *mockSequencer) Reset(r runtime.Runtime, opts runtime.ResetOptions) []runtime.Phase { return m.phases[runtime.SequenceReset] } func (m *mockSequencer) Shutdown(r runtime.Runtime, req *machine.ShutdownRequest) []runtime.Phase { return m.phases[runtime.SequenceShutdown] } func (m *mockSequencer) StageUpgrade(r runtime.Runtime, req *machine.UpgradeRequest) []runtime.Phase { return m.phases[runtime.SequenceStageUpgrade] } func (m *mockSequencer) MaintenanceUpgrade(r runtime.Runtime, req *machine.UpgradeRequest) []runtime.Phase { return m.phases[runtime.SequenceMaintenanceUpgrade] } func (m *mockSequencer) Upgrade(r runtime.Runtime, req *machine.UpgradeRequest) []runtime.Phase { return m.phases[runtime.SequenceUpgrade] } func (m *mockSequencer) trackCall(name string, doneCh chan struct{}) func(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(seq runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if doneCh != nil { defer func() { select { case doneCh <- struct{}{}: case <-time.After(time.Second): } }() } m.callsMu.Lock() defer m.callsMu.Unlock() m.calls[seq]++ return nil }, name } } func TestRun(t *testing.T) { tests := []struct { name string from runtime.Sequence to runtime.Sequence expectError error dataFrom any dataTo any }{ { name: "reboot should take over boot", from: runtime.SequenceBoot, to: runtime.SequenceReboot, expectError: context.Canceled, }, { name: "reset should take over boot", from: runtime.SequenceBoot, to: runtime.SequenceReset, expectError: context.Canceled, dataTo: &v1alpha1server.ResetOptions{}, }, { name: "upgrade should take over boot", from: runtime.SequenceBoot, to: runtime.SequenceUpgrade, expectError: context.Canceled, dataTo: &machine.UpgradeRequest{}, }, { name: "boot should not take over reboot", from: runtime.SequenceReboot, to: runtime.SequenceBoot, expectError: runtime.ErrLocked, }, { name: "reset should not take over upgrade", from: runtime.SequenceUpgrade, to: runtime.SequenceReset, expectError: runtime.ErrLocked, dataFrom: &machine.UpgradeRequest{}, dataTo: &v1alpha1server.ResetOptions{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { require := require.New(t) assert := assert.New(t) e := NewEvents(1000, 10) t.Setenv("PLATFORM", "container") s, err := NewState() require.NoError(err) sequencer := &mockSequencer{ calls: map[runtime.Sequence]int{}, phases: map[runtime.Sequence]PhaseList{}, } var ( eg errgroup.Group doneCh = make(chan struct{}) ) sequencer.phases[tt.from] = sequencer.phases[tt.from]. Append(tt.from.String(), sequencer.trackCall(tt.from.String(), doneCh)). Append("wait", wait) sequencer.phases[tt.to] = sequencer.phases[tt.to].Append(tt.to.String(), sequencer.trackCall(tt.to.String(), nil)) l := logging.NewCircularBufferLoggingManager(log.New(os.Stdout, "machined fallback logger: ", log.Flags())) r := NewRuntime(s, e, l) controller := Controller{ r: r, s: sequencer, priorityLock: NewPriorityLock[runtime.Sequence](), } ctx, cancel := context.WithTimeout(t.Context(), time.Millisecond*200) defer cancel() eg.Go(func() error { t.Logf("starting %s sequence", tt.from.String()) seqErr := controller.Run(ctx, tt.from, tt.dataFrom) t.Logf("sequence %s finished with error: %v", tt.from.String(), seqErr) return seqErr }) eg.Go(func() error { select { case <-doneCh: case <-time.After(time.Second): return fmt.Errorf("timed out waiting for %s sequence to start", tt.from.String()) } t.Logf("starting %s sequence", tt.to.String()) seqErr := controller.Run(ctx, tt.to, tt.dataTo) t.Logf("sequence %s finished with error: %v", tt.to.String(), seqErr) return seqErr }) require.ErrorIs(eg.Wait(), tt.expectError) if errors.Is(tt.expectError, runtime.ErrLocked) { return } sequencer.callsMu.Lock() defer sequencer.callsMu.Unlock() assert.Equal(1, sequencer.calls[tt.to]) }) } } func wait(seq runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Second * 1): } return nil }, "wait" } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_dbus.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "errors" "os" "path/filepath" "time" "github.com/siderolabs/talos/internal/pkg/logind" "github.com/siderolabs/talos/pkg/machinery/constants" ) // DBusState implements the logind mock. type DBusState struct { broker *logind.DBusBroker logindMock *logind.ServiceMock errCh chan error cancel context.CancelFunc } // Start the D-Bus broker and logind mock. func (dbus *DBusState) Start() error { for _, path := range []string{constants.DBusServiceSocketPath, constants.DBusClientSocketPath} { if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { return err } } var ( err error ctx context.Context ) ctx, dbus.cancel = context.WithCancel(context.Background()) dbus.broker, err = logind.NewBroker(ctx, constants.DBusServiceSocketPath, constants.DBusClientSocketPath) if err != nil { return err } dbus.errCh = make(chan error) go func() { dbus.errCh <- dbus.broker.Run(ctx) }() dbus.logindMock, err = logind.NewServiceMock(constants.DBusServiceSocketPath) return err } // Stop the D-Bus broker and logind mock. func (dbus *DBusState) Stop() error { if dbus.cancel == nil { return nil } dbus.cancel() if dbus.logindMock == nil || dbus.broker == nil { return nil } if err := dbus.logindMock.Close(); err != nil { return err } if err := dbus.broker.Close(); err != nil { return err } select { case <-time.After(time.Second): return errors.New("timed out stopping D-Bus broker") case err := <-dbus.errCh: return err } } // WaitShutdown signals the shutdown over the D-Bus and waits for the inhibit lock to be released. func (dbus *DBusState) WaitShutdown(ctx context.Context) error { if dbus.logindMock == nil { return nil } if err := dbus.logindMock.EmitShutdown(); err != nil { return err } return dbus.logindMock.WaitLockRelease(ctx) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "sort" "sync" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/proto" ) // Events represents the runtime event stream. // // Events internally is implemented as circular buffer of `runtime.Event`. // `e.stream` slice is allocated to the initial capacity and slice size doesn't change // throughout the lifetime of Events. // // To explain the internals, let's call `Publish()` method 'Publisher' (there might be // multiple callers for it), and each `Watch()` handler as 'Consumer'. // // For Publisher, `Events` keeps `e.writePos`, `e.writePos` is write offset into `e.stream`. // Offset `e.writePos` is always incremeneted, real write index is `e.writePos % e.cap` // // Each Consumer captures initial position it starts consumption from as `pos` which is // local to each Consumer, as Consumers are free to work on their own pace. Following diagram shows // Publisher and three Consumers: // // Consumer 3 Consumer 2 // pos = 27 pos = 34 // e.stream []Event | | // | | // +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |17 | // +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ // | | // | | // Consumer 1 Publisher // pos = 43 e.writePos = 50 // // Capacity of Events in this diagram is 18, Publisher published already 50 events, so it // already overwrote `e.stream` twice fully. // // Consumer1 is trying to keep up with the publisher, it has 14-7 = 7 events to catch up. // // Consumer2 is reading events published by Publisher before last wraparound, it has // 50-34 = 16 events to catch up. Consumer 2 has a lot of events to catch up, but as it stays // on track, it can still do that. // // Consumer3 is doing bad: 50-27 = 23 > 18 (capacity), so its read position has already been // overwritten, it can't read consistent data, soit should error out. // // Synchronization: at the moment single mutex protects `e.stream` and `e.writePos`, consumers keep their // position as local variable, so it doesn't require synchronization. If Consumer catches up with Publisher, // it sleeps on condition variable to be woken up by Publisher on next publish. type Events struct { // stream is used as ring buffer of events stream []runtime.Event // writePos is the index in streams for the next write (publish) // // writePos gets always incremented, real position in slice is (writePos % cap) writePos int64 // cap is a capacity of the stream cap int // gap is a safety gap between consumers and publishers gap int // mutext protects access to writePos and stream mu sync.Mutex c *sync.Cond } // NewEvents initializes and returns the v1alpha1 runtime event stream. // // Argument cap is a maximum event stream capacity (available event history). // Argument gap is a safety gap to separate consumer from the publisher. // Maximum available event history is (cap-gap). func NewEvents(capacity, gap int) *Events { e := &Events{ stream: make([]runtime.Event, capacity), cap: capacity, gap: gap, } if gap >= capacity { // we should never reach this, but if we do, panic so that we know. panic("NewEvents: gap >= capacity") } e.c = sync.NewCond(&e.mu) return e } // Watch implements the Events interface. // //nolint:gocyclo func (e *Events) Watch(f runtime.WatchFunc, opt ...runtime.WatchOptionFunc) error { var opts runtime.WatchOptions for _, o := range opt { if err := o(&opts); err != nil { return err } } // context is used to abort the loop when WatchFunc exits ctx, ctxCancel := context.WithCancel(context.Background()) ch := make(chan runtime.EventInfo) go func() { defer ctxCancel() f(ch) }() e.mu.Lock() // capture initial consumer position: by default, consumer starts consuming from the next // event to be published pos := e.writePos minPos := e.writePos - int64(e.cap-e.gap) minPos = max(minPos, 0) // calculate initial position based on options switch { case opts.TailEvents != 0: if opts.TailEvents < 0 { pos = minPos } else { pos -= int64(opts.TailEvents) pos = max(pos, minPos) } case !opts.TailID.IsNil(): pos = minPos + int64(sort.Search(int(pos-minPos), func(i int) bool { event := e.stream[(minPos+int64(i))%int64(e.cap)] return event.ID.Compare(opts.TailID) > 0 })) case opts.TailDuration != 0: timestamp := time.Now().Add(-opts.TailDuration) pos = minPos + int64(sort.Search(int(pos-minPos), func(i int) bool { event := e.stream[(minPos+int64(i))%int64(e.cap)] return event.ID.Time().After(timestamp) })) } e.mu.Unlock() go func() { defer close(ch) for { e.mu.Lock() // while there's no data to consume (pos == e.writePos), wait for Condition variable signal, // then recheck the condition to be true. for pos == e.writePos { e.c.Wait() select { case <-ctx.Done(): e.mu.Unlock() return default: } } if e.writePos-pos >= int64(e.cap) { // buffer overrun, there's no way to signal error in this case, // so for now just return e.mu.Unlock() return } event := e.stream[pos%int64(e.cap)] pos++ backlog := int(e.writePos - pos) e.mu.Unlock() // if actor id filter is specified and does not match the event, skip it if opts.ActorID != "" && event.ActorID != opts.ActorID { continue } // send event to WatchFunc, wait for it to process the event select { case ch <- runtime.EventInfo{ Event: event, Backlog: backlog, }: case <-ctx.Done(): return } } }() return nil } // Publish implements the Events interface. func (e *Events) Publish(ctx context.Context, msg proto.Message) { actorID, ok := ctx.Value(runtime.ActorIDCtxKey{}).(string) if !ok { actorID = "" } event := runtime.NewEvent(msg, actorID) e.mu.Lock() defer e.mu.Unlock() e.stream[e.writePos%int64(e.cap)] = event e.writePos++ e.c.Broadcast() } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_events_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint,testpackage package v1alpha1 import ( "fmt" "strconv" "sync" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" "golang.org/x/time/rate" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) func TestEvents_Publish(t *testing.T) { tests := []struct { name string cap int watchers int messages int }{ { name: "nowatchers", cap: 100, watchers: 0, messages: 100, }, { name: "onemessage", cap: 100, watchers: 10, messages: 1, }, { name: "manymessages_singlewatcher", cap: 100, watchers: 1, messages: 50, }, { name: "manymessages_manywatchers", cap: 100, watchers: 20, messages: 50, }, { name: "manymessages_overcap", cap: 10, watchers: 5, messages: 200, }, { name: "megamessages_overcap", cap: 1000, watchers: 1, messages: 2000, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { e := NewEvents(tt.cap, tt.cap/10) var wg sync.WaitGroup wg.Add(tt.watchers) got := uint32(0) for range tt.watchers { if err := e.Watch(func(events <-chan runtime.EventInfo) { defer wg.Done() l := rate.NewLimiter(500, tt.cap*8/10) for j := range tt.messages { event, ok := <-events if !ok { // on buffer overrun Watch() closes the channel t.Fatalf("buffer overrun") } seq, err := strconv.Atoi(event.Payload.(*machine.SequenceEvent).Sequence) if err != nil { t.Fatalf("failed to convert sequence to number: %s", err) } if seq != j { t.Fatalf("unexpected sequence: %d != %d", seq, j) } atomic.AddUint32(&got, 1) _ = l.Wait(t.Context()) //nolint:errcheck } }); err != nil { t.Errorf("Watch error %s", err) } } l := rate.NewLimiter(500, tt.cap/2) for i := range tt.messages { _ = l.Wait(t.Context()) //nolint:errcheck e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } wg.Wait() if got != uint32(tt.messages*tt.watchers) { t.Errorf("Watch() = got %v, want %v", got, tt.messages*tt.watchers) } }) } } func receive(t *testing.T, e runtime.Watcher, n int, opts ...runtime.WatchOptionFunc) (result []runtime.EventInfo) { var wg sync.WaitGroup wg.Add(1) if err := e.Watch(func(events <-chan runtime.EventInfo) { defer wg.Done() for range n { event, ok := <-events if !ok { t.Fatalf("Watch: chanel closed") } result = append(result, event) } select { case _, ok := <-events: if ok { t.Fatal("received extra events") } else { t.Fatalf("Watch: chanel closed") } case <-time.After(50 * time.Millisecond): } }, opts...); err != nil { t.Fatalf("Watch() error %s", err) } wg.Wait() return result } func extractSeq(t *testing.T, events []runtime.EventInfo) (result []int) { for _, event := range events { seq, err := strconv.Atoi(event.Payload.(*machine.SequenceEvent).Sequence) if err != nil { t.Fatalf("failed to convert sequence to number: %s", err) } result = append(result, seq) } return result } func gen(k, l int) (result []int) { for j := k; j < l; j++ { result = append(result, j) } return result } func TestEvents_WatchOptionsTailEvents(t *testing.T) { e := NewEvents(100, 10) for i := range 200 { e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } assert.Equal(t, []int(nil), extractSeq(t, receive(t, e, 0))) assert.Equal(t, gen(199, 200), extractSeq(t, receive(t, e, 1, runtime.WithTailEvents(1)))) assert.Equal(t, gen(195, 200), extractSeq(t, receive(t, e, 5, runtime.WithTailEvents(5)))) assert.Equal(t, gen(111, 200), extractSeq(t, receive(t, e, 89, runtime.WithTailEvents(89)))) assert.Equal(t, gen(110, 200), extractSeq(t, receive(t, e, 90, runtime.WithTailEvents(90)))) assert.Equal(t, gen(110, 200), extractSeq(t, receive(t, e, 90, runtime.WithTailEvents(91)))) // can't tail more than cap-gap assert.Equal(t, gen(110, 200), extractSeq(t, receive(t, e, 90, runtime.WithTailEvents(1000)))) // can't tail more than cap-gap assert.Equal(t, gen(110, 200), extractSeq(t, receive(t, e, 90, runtime.WithTailEvents(-1)))) // tail all events e = NewEvents(100, 10) for i := range 30 { e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } assert.Equal(t, []int(nil), extractSeq(t, receive(t, e, 0))) assert.Equal(t, gen(29, 30), extractSeq(t, receive(t, e, 1, runtime.WithTailEvents(1)))) assert.Equal(t, gen(28, 30), extractSeq(t, receive(t, e, 2, runtime.WithTailEvents(2)))) assert.Equal(t, gen(25, 30), extractSeq(t, receive(t, e, 5, runtime.WithTailEvents(5)))) assert.Equal(t, gen(0, 30), extractSeq(t, receive(t, e, 30, runtime.WithTailEvents(40)))) } func TestEvents_WatchOptionsTailSeconds(t *testing.T) { e := NewEvents(100, 10) for i := range 20 { e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } // sleep to get time gap between two series of events time.Sleep(3 * time.Second) for i := 20; i < 30; i++ { e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } assert.Equal(t, []int(nil), extractSeq(t, receive(t, e, 0, runtime.WithTailDuration(0)))) assert.Equal(t, gen(20, 30), extractSeq(t, receive(t, e, 10, runtime.WithTailDuration(2*time.Second)))) assert.Equal(t, gen(0, 30), extractSeq(t, receive(t, e, 30, runtime.WithTailDuration(10*time.Second)))) } func TestEvents_WatchOptionsTailID(t *testing.T) { e := NewEvents(100, 10) for i := range 20 { e.Publish(t.Context(), &machine.SequenceEvent{ Sequence: strconv.Itoa(i), }) } events := receive(t, e, 20, runtime.WithTailEvents(-1)) for i, event := range events { assert.Equal(t, gen(i+1, 20), extractSeq(t, receive(t, e, 20-i-1, runtime.WithTailID(event.ID)))) } } func BenchmarkWatch(b *testing.B) { e := NewEvents(100, 10) var wg sync.WaitGroup wg.Add(b.N) for range b.N { _ = e.Watch(func(events <-chan runtime.EventInfo) { wg.Done() }) //nolint:errcheck } wg.Wait() } func BenchmarkPublish(bb *testing.B) { for _, watchers := range []int{0, 1, 10} { bb.Run(fmt.Sprintf("Watchers-%d", watchers), func(b *testing.B) { e := NewEvents(10000, 10) var wg sync.WaitGroup watchers := 10 wg.Add(watchers) for range watchers { _ = e.Watch(func(events <-chan runtime.EventInfo) { //nolint:errcheck defer wg.Done() for range b.N { if _, ok := <-events; !ok { return } } }) } ev := machine.SequenceEvent{} b.ResetTimer() for range b.N { e.Publish(bb.Context(), &ev) } wg.Wait() }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_priority_lock.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "errors" "sync" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // Priority describes the running priority of a process. // // If CanTakeOver returns true, current process with "lower" priority // will be canceled and "higher" priority process will be run. type Priority[T any] interface { comparable CanTakeOver(another T) bool } // PriorityLock is a lock that makes sure that only a single process can run at a time. // // If a process with "higher" priority tries to acquire the lock, previous process is stopped // and new process with "higher" priority is run. type PriorityLock[T Priority[T]] struct { runningCh chan struct{} takeoverCh chan struct{} mu sync.Mutex runningPriority T cancelCtx context.CancelFunc } // NewPriorityLock returns a new PriorityLock. func NewPriorityLock[T Priority[T]]() *PriorityLock[T] { runningCh := make(chan struct{}, 1) runningCh <- struct{}{} return &PriorityLock[T]{ runningCh: runningCh, takeoverCh: make(chan struct{}, 1), } } func (lock *PriorityLock[T]) getRunningPriority() (T, context.CancelFunc) { lock.mu.Lock() defer lock.mu.Unlock() return lock.runningPriority, lock.cancelCtx } func (lock *PriorityLock[T]) setRunningPriority(seq T, cancelCtx context.CancelFunc) { lock.mu.Lock() defer lock.mu.Unlock() var zeroSeq T if seq == zeroSeq && lock.cancelCtx != nil { lock.cancelCtx() } lock.runningPriority, lock.cancelCtx = seq, cancelCtx } // Lock acquires the lock according the priority rules and returns a context that should be used within the process. // // Process should terminate as soon as the context is canceled. // Argument seq defines the priority of the process. // Argument takeOverTimeout defines the maximum time to wait for the low-priority process to terminate. func (lock *PriorityLock[T]) Lock(ctx context.Context, takeOverTimeout time.Duration, seq T, options ...runtime.LockOption) (context.Context, error) { opts := runtime.DefaultControllerOptions() for _, o := range options { if err := o(&opts); err != nil { return nil, err } } takeOverTimer := time.NewTimer(takeOverTimeout) defer takeOverTimer.Stop() select { case lock.takeoverCh <- struct{}{}: case <-takeOverTimer.C: return nil, errors.New("failed to acquire lock: timeout") } defer func() { <-lock.takeoverCh }() sequence, cancelCtx := lock.getRunningPriority() if !seq.CanTakeOver(sequence) && !opts.Takeover { return nil, runtime.ErrLocked } if cancelCtx != nil { cancelCtx() } select { case <-lock.runningCh: seqCtx, seqCancel := context.WithCancel(ctx) lock.setRunningPriority(seq, seqCancel) return seqCtx, nil case <-takeOverTimer.C: return nil, errors.New("failed to acquire lock: timeout") } } // Unlock releases the lock. func (lock *PriorityLock[T]) Unlock() { var zeroSeq T lock.setRunningPriority(zeroSeq, nil) lock.runningCh <- struct{}{} } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_priority_lock_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1" ) type testSequenceNumber int func (candidate testSequenceNumber) CanTakeOver(running testSequenceNumber) bool { return candidate > running } func TestPriorityLock(t *testing.T) { require := require.New(t) lock := v1alpha1.NewPriorityLock[testSequenceNumber]() ctx := t.Context() ctx1, err := lock.Lock(ctx, time.Second, 2) require.NoError(err) select { case <-ctx1.Done(): require.FailNow("should not be canceled") default: } _, err = lock.Lock(ctx, time.Millisecond, 1) require.Error(err) require.EqualError(err, runtime.ErrLocked.Error()) errCh := make(chan error) go func() { _, err2 := lock.Lock(ctx, time.Second, 3) errCh <- err2 }() select { case <-ctx1.Done(): case <-time.After(time.Second): require.FailNow("should be canceled") } select { case <-errCh: require.FailNow("should not be reached") default: } lock.Unlock() select { case err = <-errCh: require.NoError(err) case <-time.After(time.Second): require.FailNow("should be canceled") } } func TestPriorityLockSequential(t *testing.T) { require := require.New(t) lock := v1alpha1.NewPriorityLock[testSequenceNumber]() ctx := t.Context() _, err := lock.Lock(ctx, time.Second, 2) require.NoError(err) lock.Unlock() _, err = lock.Lock(ctx, time.Second, 1) require.NoError(err) lock.Unlock() } //nolint:gocyclo func TestPriorityLockConcurrent(t *testing.T) { require := require.New(t) lock := v1alpha1.NewPriorityLock[testSequenceNumber]() globalCtx, globalCtxCancel := context.WithCancel(t.Context()) defer globalCtxCancel() var eg errgroup.Group sequenceCh := make(chan testSequenceNumber) for seq := testSequenceNumber(1); seq <= 20; seq++ { eg.Go(func() error { ctx, err := lock.Lock(globalCtx, time.Second, seq) if errors.Is(err, runtime.ErrLocked) { return nil } if err != nil { return err } select { case sequenceCh <- seq: <-ctx.Done() case <-ctx.Done(): } lock.Unlock() return nil }) } timer := time.NewTimer(5 * time.Second) defer timer.Stop() var prevSeq testSequenceNumber for { select { case <-timer.C: require.FailNow("timeout") case seq := <-sequenceCh: t.Logf("sequence running: %d", seq) if prevSeq >= seq { require.FailNowf("can take over inversion", "sequence %d should be greater than %d", seq, prevSeq) } prevSeq = seq } if prevSeq == 20 { globalCtxCancel() break } } require.NoError(eg.Wait()) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "errors" "fmt" "log" "reflect" "sync" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configdiff" "github.com/siderolabs/talos/pkg/machinery/config/container" machineconfig "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // Runtime implements the Runtime interface. type Runtime struct { s runtime.State e runtime.EventStream l runtime.LoggingManager rollbackTimerMu sync.Mutex rollbackTimer *time.Timer } // NewRuntime initializes and returns the v1alpha1 runtime. func NewRuntime(s runtime.State, e runtime.EventStream, l runtime.LoggingManager) *Runtime { return &Runtime{ s: s, e: e, l: l, } } func (r *Runtime) configProvider() config.Provider { cfg, err := r.s.V1Alpha2().GetConfig(context.TODO()) if err != nil { panic(err) } return cfg } // Config implements the Runtime interface. func (r *Runtime) Config() config.Config { cfg := r.configProvider() if cfg == nil { return nil } return cfg } // ConfigCompleteForBoot implements the Runtime interface. func (r *Runtime) ConfigCompleteForBoot() bool { cfg := r.configProvider() if cfg == nil { return false } return cfg.CompleteForBoot() } // ConfigContainer implements the Runtime interface. func (r *Runtime) ConfigContainer() config.Container { cfg := r.configProvider() if cfg == nil { return nil } return cfg } // RollbackToConfigAfter implements the Runtime interface. func (r *Runtime) RollbackToConfigAfter(timeout time.Duration) error { cfgProvider := r.configProvider() r.CancelConfigRollbackTimeout() r.rollbackTimer = time.AfterFunc(timeout, func() { log.Println("rolling back the configuration") if err := r.SetConfig(cfgProvider); err != nil { log.Printf("config rollback failed %s", err) } }) return nil } // CancelConfigRollbackTimeout implements the Runtime interface. func (r *Runtime) CancelConfigRollbackTimeout() { r.rollbackTimerMu.Lock() defer r.rollbackTimerMu.Unlock() if r.rollbackTimer != nil { r.rollbackTimer.Stop() r.rollbackTimer = nil } } // SetConfig implements the Runtime interface. func (r *Runtime) SetConfig(cfg config.Provider) error { return r.s.V1Alpha2().SetConfig(context.TODO(), machineconfig.ActiveID, cfg) } // SetPersistedConfig implements the Runtime interface. func (r *Runtime) SetPersistedConfig(cfg config.Provider) error { return r.s.V1Alpha2().SetConfig(context.TODO(), machineconfig.PersistentID, cfg) } // CanApplyImmediate implements the Runtime interface. func (r *Runtime) CanApplyImmediate(cfg config.Provider) error { cfgProv := r.configProvider() if cfgProv == nil { return errors.New("no current config") } currentConfig := cfgProv.RawV1Alpha1() if currentConfig == nil { return errors.New("current config is not v1alpha1") } newConfig := cfg.RawV1Alpha1() if newConfig == nil { return errors.New("new config is not v1alpha1") } // copy the config as we're going to modify it newConfig = newConfig.DeepCopy() // the config changes allowed to be applied immediately are: // * .debug // * .cluster // * .machine.ca // * .machine.acceptedCAs // * .machine.time // * .machine.certCANs // * .machine.install // * .machine.network // * .machine.sysfs // * .machine.sysctls // * .machine.logging // * .machine.controlplane // * .machine.kubelet // * .machine.kernel // * .machine.registries (note that auth is not applied immediately, containerd limitation) // * .machine.pods // * .machine.seccompProfiles // * .machine.nodeAnnotations // * .machine.nodeLabels // * .machine.nodeTaints // * .machine.features.kubernetesTalosAPIAccess // * .machine.features.kubePrism // * .machine.features.hostDNS // * .machine.features.imageCache // * .machine.features.nodeAddressSortAlgorithm newConfig.ConfigDebug = currentConfig.ConfigDebug newConfig.ClusterConfig = currentConfig.ClusterConfig if newConfig.MachineConfig != nil && currentConfig.MachineConfig != nil { newConfig.MachineConfig.MachineCA = currentConfig.MachineConfig.MachineCA newConfig.MachineConfig.MachineAcceptedCAs = currentConfig.MachineConfig.MachineAcceptedCAs newConfig.MachineConfig.MachineTime = currentConfig.MachineConfig.MachineTime //nolint:staticcheck newConfig.MachineConfig.MachineCertSANs = currentConfig.MachineConfig.MachineCertSANs newConfig.MachineConfig.MachineInstall = currentConfig.MachineConfig.MachineInstall newConfig.MachineConfig.MachineNetwork = currentConfig.MachineConfig.MachineNetwork //nolint:staticcheck newConfig.MachineConfig.MachineSysfs = currentConfig.MachineConfig.MachineSysfs newConfig.MachineConfig.MachineSysctls = currentConfig.MachineConfig.MachineSysctls newConfig.MachineConfig.MachineLogging = currentConfig.MachineConfig.MachineLogging newConfig.MachineConfig.MachineControlPlane = currentConfig.MachineConfig.MachineControlPlane newConfig.MachineConfig.MachineKubelet = currentConfig.MachineConfig.MachineKubelet newConfig.MachineConfig.MachineKernel = currentConfig.MachineConfig.MachineKernel newConfig.MachineConfig.MachineRegistries = currentConfig.MachineConfig.MachineRegistries //nolint:staticcheck // backwards compatibility newConfig.MachineConfig.MachinePods = currentConfig.MachineConfig.MachinePods newConfig.MachineConfig.MachineSeccompProfiles = currentConfig.MachineConfig.MachineSeccompProfiles newConfig.MachineConfig.MachineNodeAnnotations = currentConfig.MachineConfig.MachineNodeAnnotations newConfig.MachineConfig.MachineNodeLabels = currentConfig.MachineConfig.MachineNodeLabels newConfig.MachineConfig.MachineNodeTaints = currentConfig.MachineConfig.MachineNodeTaints if newConfig.MachineConfig.MachineFeatures != nil && currentConfig.MachineConfig.MachineFeatures != nil { newConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig = currentConfig.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig newConfig.MachineConfig.MachineFeatures.KubePrismSupport = currentConfig.MachineConfig.MachineFeatures.KubePrismSupport newConfig.MachineConfig.MachineFeatures.HostDNSSupport = currentConfig.MachineConfig.MachineFeatures.HostDNSSupport newConfig.MachineConfig.MachineFeatures.ImageCacheSupport = currentConfig.MachineConfig.MachineFeatures.ImageCacheSupport newConfig.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm = currentConfig.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm } } if !reflect.DeepEqual(currentConfig, newConfig) { diff, err := configdiff.DiffConfigs(container.NewV1Alpha1(currentConfig), container.NewV1Alpha1(newConfig)) if err != nil { return fmt.Errorf("error calculating diff: %w", err) } return fmt.Errorf("this config change can't be applied in immediate mode\ndiff:\n%s", diff) } return nil } // State implements the Runtime interface. func (r *Runtime) State() runtime.State { return r.s } // Events implements the Runtime interface. func (r *Runtime) Events() runtime.EventStream { return r.e } // Logging implements the Runtime interface. func (r *Runtime) Logging() runtime.LoggingManager { return r.l } // NodeName implements the Runtime interface. func (r *Runtime) NodeName() (string, error) { nodenameResource, err := safe.ReaderGet[*k8s.Nodename]( context.Background(), r.s.V1Alpha2().Resources(), resource.NewMetadata(k8s.NamespaceName, k8s.NodenameType, k8s.NodenameID, resource.VersionUndefined), ) if err != nil { return "", fmt.Errorf("error getting nodename resource: %w", err) } return nodenameResource.TypedSpec().Nodename, nil } // IsBootstrapAllowed checks for CRI to be up, checked in the bootstrap method. func (r *Runtime) IsBootstrapAllowed() bool { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() svc := &services.CRI{} if err := system.WaitForService(system.StateEventUp, svc.ID(r)).Wait(ctx); err != nil { return false } return true } // GetSystemInformation returns system information resource if it exists. func (r *Runtime) GetSystemInformation(ctx context.Context) (*hardware.SystemInformation, error) { return safe.StateGet[*hardware.SystemInformation](ctx, r.State().V1Alpha2().Resources(), hardware.NewSystemInformation(hardware.SystemInformationID).Metadata()) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "strconv" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/imageropts" "github.com/siderolabs/talos/pkg/machinery/meta" ) // Sequencer implements the sequencer interface. type Sequencer struct{} // NewSequencer intializes and returns a sequencer. func NewSequencer() *Sequencer { return &Sequencer{} } // PhaseList represents a list of phases. type PhaseList []runtime.Phase // Append appends a task to the phase list. func (p PhaseList) Append(name string, tasks ...runtime.TaskSetupFunc) PhaseList { p = append(p, runtime.Phase{ Name: name, Tasks: tasks, }) return p } // AppendWhen appends a task to the phase list when `when` is `true`. func (p PhaseList) AppendWhen(when bool, name string, tasks ...runtime.TaskSetupFunc) PhaseList { if when { p = p.Append(name, tasks...) } return p } // AppendWithDeferredCheck appends a task to the phase list but skips the sequence if `check func()` returns `false` during execution. func (p PhaseList) AppendWithDeferredCheck(check func() bool, name string, tasks ...runtime.TaskSetupFunc) PhaseList { p = append(p, runtime.Phase{ Name: name, Tasks: tasks, CheckFunc: check, }) return p } // AppendList appends an additional PhaseList to the existing one. func (p PhaseList) AppendList(list PhaseList) PhaseList { return append(p, list...) } // Initialize is the initialize sequence. The primary goals of this sequence is // to load the config and enforce kernel security requirements. func (*Sequencer) Initialize(r runtime.Runtime) []runtime.Phase { mode := r.State().Platform().Mode() phases := PhaseList{} switch mode { //nolint:exhaustive case runtime.ModeContainer: phases = phases.Append( "machined", StartApid, StartMachined, StartContainerd, ).Append( "config", LoadConfig, ) default: phases = phases.Append( "systemRequirements", EnforceKSPPRequirements, ).Append( "earlyServices", StartUdevd, StartMachined, StartApid, StartAuditd, StartSyslogd, StartContainerd, ).Append( "usb", WaitForUSB, ).Append( "meta", ReloadMeta, ).AppendWithDeferredCheck( func() bool { val, ok := r.State().Machine().Meta().ReadTag(meta.DiskImageBootloader) if !ok { return false } return r.State().Machine().Installed() && val == imageropts.BootLoaderKindDualBoot.String() }, "cleanupBootloader", CleanupBootloader, ).AppendWithDeferredCheck( func() bool { if mode == runtime.ModeMetalAgent { return false } disabledStr := procfs.ProcCmdline().Get(constants.KernelParamDashboardDisabled).First() disabled, _ := strconv.ParseBool(pointer.SafeDeref(disabledStr)) //nolint:errcheck return !disabled }, "dashboard", StartDashboard, ).AppendWithDeferredCheck( func() bool { wipe := procfs.ProcCmdline().Get(constants.KernelParamWipe).First() return pointer.SafeDeref(wipe) != "" }, "wipeDisks", ResetSystemDiskPartitions, ).AppendWithDeferredCheck( func() bool { haltIfInstalledStr := procfs.ProcCmdline().Get(constants.KernelParamHaltIfInstalled).First() haltIfInstalled, _ := strconv.ParseBool(pointer.SafeDeref(haltIfInstalledStr)) //nolint:errcheck return r.State().Machine().Installed() && haltIfInstalled }, "haltIfInstalled", haltIfInstalled, ).Append( "config", LoadConfig, ) } return phases } // Install is the install sequence. func (*Sequencer) Install(r runtime.Runtime) []runtime.Phase { phases := PhaseList{} switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: return nil default: if !r.State().Machine().Installed() || r.State().Machine().IsInstallStaged() { phases = phases.Append( "env", SetUserEnvVars, ).Append( "install", Install, ).Append( "meta", ReloadMeta, ).Append( "saveMeta", // saving META here to merge in-memory changes with the on-disk ones from the installer FlushMeta, ).Append( "volumeFinalize", TeardownVolumeLifecycle, ).Append( "stopEverything", StopAllServices, ).Append( "kexec", KexecPrepare, ).Append( "reboot", Reboot, ) } } return phases } // Boot is the boot sequence. This primary goal if this sequence is to apply // user supplied settings and start the services for the specific machine type. // This sequence should never be reached if an installation is not found. func (*Sequencer) Boot(r runtime.Runtime) []runtime.Phase { phases := PhaseList{} phases = phases.Append( "memorySizeCheck", MemorySizeCheck, ).Append( "diskSizeCheck", DiskSizeCheck, ).Append( "env", SetUserEnvVars, WaitForCARoots, ).Append( "dbus", StartDBus, ).AppendWhen( r.State().Platform().Mode() == runtime.ModeContainer, "sharedFilesystems", SetupSharedFilesystems, ).Append( "ephemeral", MountEphemeralPartition, ).AppendWhen( r.State().Platform().Mode() != runtime.ModeContainer, "udevSetup", WriteUdevRules, ).AppendWhen( r.State().Platform().Mode() != runtime.ModeContainer, "userDisks", pauseOnFailure(MountUserDisks, constants.FailurePauseTimeout), ).Append( "userSetup", pauseOnFailure(WriteUserFiles, constants.FailurePauseTimeout), ).Append( "startEverything", StartAllServices, ) return phases } // Reboot is the reboot sequence. func (*Sequencer) Reboot(r runtime.Runtime, in *machineapi.RebootRequest) []runtime.Phase { phases := PhaseList{} if in.GetMode() != machineapi.RebootRequest_FORCE { phases = phases. Append( "cleanup", StopAllPods, ). Append( "dbus", StopDBus, ). AppendList(stopAllPhaselist(r, true)) } return phases.Append("reboot", Reboot) } // Reset is the reset sequence. // //nolint:gocyclo func (*Sequencer) Reset(r runtime.Runtime, in runtime.ResetOptions) []runtime.Phase { phases := PhaseList{} // Use kexec if we don't wipe the boot partition. withKexec := false if len(in.GetSystemDiskTargets()) > 0 { withKexec = !bootPartitionInTargets(in.GetSystemDiskTargets()) } var ( resetUserDisks bool resetSystemDisk bool ) switch in.GetMode() { case machineapi.ResetRequest_ALL: resetUserDisks = true resetSystemDisk = true case machineapi.ResetRequest_USER_DISKS: resetUserDisks = true case machineapi.ResetRequest_SYSTEM_DISK: resetSystemDisk = true } switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: phases = phases.AppendList(stopAllPhaselist(r, false)). Append( "shutdown", Shutdown, ) default: phases = phases.AppendWhen( in.GetGraceful() && !r.Config().Machine().Kubelet().SkipNodeRegistration(), "drain", taskErrorHandler(logError, CordonAndDrainNode), ).AppendWhen( in.GetGraceful(), "cleanup", taskErrorHandler(logError, RemoveAllPods), ).AppendWhen( !in.GetGraceful(), "cleanup", taskErrorHandler(logError, StopAllPods), ).Append( "dbus", StopDBus, ).AppendWhen( in.GetGraceful() && (r.Config().Machine().Type() != machine.TypeWorker), "leave", LeaveEtcd, ).Append( "preReset", SendResetSignal, ).AppendList( phaseListErrorHandler(logError, stopAllPhaselist(r, withKexec)...), ).Append( "forceCleanup", ForceCleanup, ).AppendWhen( len(in.GetSystemDiskTargets()) == 0 && resetSystemDisk, "reset", ResetSystemDisk, ).AppendWhen( len(in.GetSystemDiskTargets()) > 0 && resetSystemDisk, "resetSpec", ResetSystemDiskSpec, ).AppendWhen( len(in.GetUserDisksToWipe()) > 0 && resetUserDisks, "resetUserDisks", ResetUserDisks, ).AppendWhen( in.GetReboot(), "reboot", Reboot, ).AppendWhen( !in.GetReboot(), "shutdown", Shutdown, ) } return phases } // Shutdown is the shutdown sequence. func (*Sequencer) Shutdown(r runtime.Runtime, in *machineapi.ShutdownRequest) []runtime.Phase { skipNodeRegistration := r.Config() != nil && r.Config().Machine() != nil && r.Config().Machine().Kubelet().SkipNodeRegistration() phases := PhaseList{}.Append( "storeShutdown", StoreShutdownEmergency, ).AppendWhen( !in.GetForce() && !skipNodeRegistration, "drain", CordonAndDrainNode, ).Append( "cleanup", StopAllPods, ).Append( "dbus", StopDBus, ). AppendList(stopAllPhaselist(r, false)). Append("shutdown", Shutdown) return phases } // StageUpgrade is the stage upgrade sequence. func (*Sequencer) StageUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []runtime.Phase { phases := PhaseList{} switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: return nil default: phases = phases.Append( "cleanup", StopAllPods, ).Append( "dbus", StopDBus, ).AppendList( stopAllPhaselist(r, in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT), ).Append( "reboot", Reboot, ) } return phases } // MaintenanceUpgrade is the upgrade sequence in maintenance mode. func (*Sequencer) MaintenanceUpgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []runtime.Phase { phases := PhaseList{} switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: return nil default: phases = phases.Append( "upgrade", Upgrade, ).Append( "meta", ReloadMeta, ).AppendWhen( in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT, "kexec", KexecPrepare, ).Append( "stopEverything", StopAllServices, ).Append( "reboot", Reboot, ) } return phases } // Upgrade is the upgrade sequence. func (*Sequencer) Upgrade(r runtime.Runtime, in *machineapi.UpgradeRequest) []runtime.Phase { phases := PhaseList{} switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: return nil default: phases = phases.AppendWhen( !r.Config().Machine().Kubelet().SkipNodeRegistration(), "drain", CordonAndDrainNode, ).Append( "cleanup", StopAllPods, ).Append( "dbus", StopDBus, ).Append( "stopServices", StopServicesEphemeral, ).Append( "unmount", UnmountPodMounts, ).Append( "unmountBind", UnmountSystemDiskBindMounts, ).Append( "unmountSystem", UnmountEphemeralPartition, ).Append( "volumeFinalize", TeardownVolumeLifecycle, ).Append( "upgrade", Upgrade, ).Append( "meta", ReloadMeta, ).AppendWhen( in.GetRebootMode() == machineapi.UpgradeRequest_DEFAULT, "kexec", KexecPrepare, ).Append( "stopEverything", StopAllServices, ).Append( "reboot", Reboot, ) } return phases } func stopAllPhaselist(r runtime.Runtime, enableKexec bool) PhaseList { phases := PhaseList{} switch r.State().Platform().Mode() { //nolint:exhaustive case runtime.ModeContainer: phases = phases.Append( "stopEverything", StopAllServices, ) default: phases = phases.Append( "stopServices", StopServicesEphemeral, ).Append( "umount", UnmountPodMounts, ).Append( "unmountBind", UnmountSystemDiskBindMounts, ).Append( "unmountSystem", UnmountEphemeralPartition, ).Append( "volumeFinalize", TeardownVolumeLifecycle, ).AppendWhen( enableKexec, "kexec", KexecPrepare, ).Append( "stopEverything", StopAllServices, ) } return phases } func bootPartitionInTargets(targets []runtime.PartitionTarget) bool { for _, target := range targets { if target.GetLabel() == constants.BootPartitionLabel { return true } } return false } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "bufio" "bytes" "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "fmt" "io/fs" "log" "os" "path/filepath" goruntime "runtime" "strconv" "strings" "syscall" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/dustin/go-humanize" "github.com/foxboron/go-uefi/efi" "github.com/hashicorp/go-multierror" pprocfs "github.com/prometheus/procfs" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/go-cmd/pkg/cmd/proc" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" clientv3 "go.etcd.io/etcd/client/v3" "golang.org/x/sys/unix" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/emergency" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/internal/pkg/cri" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/internal/pkg/install" "github.com/siderolabs/talos/internal/pkg/logind" mountv3 "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/kernel/kspp" "github.com/siderolabs/talos/pkg/kubernetes" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/block/blockhelpers" "github.com/siderolabs/talos/pkg/machinery/constants" metamachinery "github.com/siderolabs/talos/pkg/machinery/meta" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" crires "github.com/siderolabs/talos/pkg/machinery/resources/cri" resourcefiles "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" resourceruntime "github.com/siderolabs/talos/pkg/machinery/resources/runtime" resourcev1alpha1 "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" "github.com/siderolabs/talos/pkg/minimal" ) // WaitForUSB represents the WaitForUSB task. func WaitForUSB(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { // Wait for USB storage in the case that the install disk is supplied over // USB. If we don't wait, there is the chance that we will fail to detect the // install disk. file := "/sys/module/usb_storage/parameters/delay_use" _, err := os.Stat(file) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return err } b, err := os.ReadFile(file) if err != nil { return err } val := strings.TrimSuffix(string(b), "\n") var i int i, err = strconv.Atoi(val) if err != nil { return err } logger.Printf("waiting %d second(s) for USB storage", i) time.Sleep(time.Duration(i) * time.Second) return nil }, "waitForUSB" } // EnforceKSPPRequirements represents the EnforceKSPPRequirements task. func EnforceKSPPRequirements(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { if err = resourceruntime.NewKernelParamsSetCondition(r.State().V1Alpha2().Resources(), kspp.GetKernelParams()...).Wait(ctx); err != nil { return err } return kspp.EnforceKSPPKernelParameters() }, "enforceKSPPRequirements" } // LoadConfig represents the LoadConfig task. func LoadConfig(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { // create a request to initialize the process acquisition process request := resourcev1alpha1.NewAcquireConfigSpec() if err := r.State().V1Alpha2().Resources().Create(ctx, request); err != nil { return fmt.Errorf("failed to create config request: %w", err) } // wait for the config to be acquired status := resourcev1alpha1.NewAcquireConfigStatus() if _, err := r.State().V1Alpha2().Resources().WatchFor(ctx, status.Metadata(), state.WithEventTypes(state.Created)); err != nil { return err } // clean up request to make sure controller doesn't work after this point return r.State().V1Alpha2().Resources().Destroy(ctx, request.Metadata()) }, "loadConfig" } // Sleep represents the Sleep task. func Sleep(d time.Duration) func(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(_ runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { select { case <-time.After(d): case <-ctx.Done(): return ctx.Err() } return nil }, "sleep" } } // MemorySizeCheck represents the MemorySizeCheck task. func MemorySizeCheck(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if r.State().Platform().Mode() == runtime.ModeContainer { logger.Println("skipping memory size check in the container") return nil } pc, err := pprocfs.NewDefaultFS() if err != nil { return fmt.Errorf("failed to open procfs: %w", err) } info, err := pc.Meminfo() if err != nil { return fmt.Errorf("failed to read meminfo: %w", err) } minimum, recommended, err := minimal.Memory(r.Config().Machine().Type()) if err != nil { return err } switch memTotal := pointer.SafeDeref(info.MemTotal) * humanize.KiByte; { case memTotal < minimum: logger.Println("WARNING: memory size is less than recommended") logger.Println("WARNING: Talos may not work properly") logger.Println("WARNING: minimum memory size is", minimum/humanize.MiByte, "MiB") logger.Println("WARNING: recommended memory size is", recommended/humanize.MiByte, "MiB") logger.Println("WARNING: current total memory size is", memTotal/humanize.MiByte, "MiB") case memTotal < recommended: logger.Println("NOTE: recommended memory size is", recommended/humanize.MiByte, "MiB") logger.Println("NOTE: current total memory size is", memTotal/humanize.MiByte, "MiB") default: logger.Println("memory size is OK") logger.Println("memory size is", memTotal/humanize.MiByte, "MiB") } return nil }, "memorySizeCheck" } // DiskSizeCheck represents the DiskSizeCheck task. func DiskSizeCheck(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if r.State().Platform().Mode() == runtime.ModeContainer { logger.Println("skipping disk size check in the container") return nil } volumeStatus, err := r.State().V1Alpha2().Resources().WatchFor(ctx, blockres.NewVolumeStatus(blockres.NamespaceName, constants.EphemeralPartitionLabel).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { volumeStatus, ok := r.(*blockres.VolumeStatus) if !ok { return false, nil } return volumeStatus.TypedSpec().Size > 0, nil }), ) if err != nil { return fmt.Errorf("error waiting for volume %q to be discovered: %w", constants.EphemeralPartitionLabel, err) } diskSize := volumeStatus.(*blockres.VolumeStatus).TypedSpec().Size if minimum := minimal.DiskSize(); diskSize < minimum { logger.Println("WARNING: disk size is less than recommended") logger.Println("WARNING: Talos may not work properly") logger.Println("WARNING: minimum recommended disk size is", minimum/humanize.MiByte, "MiB") logger.Println("WARNING: current total disk size is", diskSize/humanize.MiByte, "MiB") } else { logger.Println("disk size is OK") logger.Println("disk size is", diskSize/humanize.MiByte, "MiB") } return nil }, "diskSizeCheck" } // SetUserEnvVars represents the SetUserEnvVars task. func SetUserEnvVars(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { for _, env := range environment.Get(r.Config()) { key, val, _ := strings.Cut(env, "=") if err = os.Setenv(key, val); err != nil { return fmt.Errorf("failed to set enivronment variable: %w", err) } } return nil }, "setUserEnvVars" } // StartContainerd represents the task to start containerd. func StartContainerd(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { svc := &services.Containerd{} system.Services(r).LoadAndStart(svc) ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() return system.WaitForService(system.StateEventUp, svc.ID(r)).Wait(ctx) }, "startContainerd" } // WriteUdevRules is the task that writes udev rules to a udev rules file. // TODO: frezbo: move this to controller based since writing udev rules doesn't need a restart. func WriteUdevRules(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { rules := r.Config().Machine().Udev().Rules() var content strings.Builder for _, rule := range rules { content.WriteString(strings.ReplaceAll(rule, "\n", "\\\n")) content.WriteByte('\n') } if err = os.WriteFile(constants.UdevRulesPath, []byte(content.String()), 0o644); err != nil { return fmt.Errorf("failed writing custom udev rules: %w", err) } if err = selinux.SetLabel(constants.UdevRulesPath, constants.UdevRulesLabel); err != nil { return fmt.Errorf("failed labeling custom udev rules: %w", err) } if len(rules) > 0 { if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"control", "--reload"}); err != nil { return err } if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"trigger", "--type=devices", "--action=add"}); err != nil { return err } if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"trigger", "--type=subsystems", "--action=add"}); err != nil { return err } // This ensures that `udevd` finishes processing kernel events, triggered by // `udevd trigger`, to prevent a race condition when a user specifies a path // under `/dev/disk/*` in any disk definitions. _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"settle", "--timeout=50"}) return err } return nil }, "writeUdevRules" } // StartMachined represents the task to start machined. func StartMachined(_ runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { svc := &services.Machined{} id := svc.ID(r) err := system.Services(r).Start(id) if err != nil { return fmt.Errorf("failed to start machined service: %w", err) } ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() return system.WaitForService(system.StateEventUp, id).Wait(ctx) }, "startMachined" } // StartSyslogd represents the task to start syslogd. func StartSyslogd(r runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(_ context.Context, _ *log.Logger, r runtime.Runtime) error { system.Services(r).LoadAndStart(&services.Syslogd{}) return nil }, "startSyslogd" } // StartApid represents the task to start apid. func StartApid(r runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(_ context.Context, _ *log.Logger, r runtime.Runtime) error { system.Services(r).LoadAndStart(&services.APID{}) return nil }, "startApid" } // StartAuditd represents the task to start auditd. func StartAuditd(r runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(_ context.Context, logger *log.Logger, r runtime.Runtime) error { if !r.State().Platform().Mode().InContainer() { disabledStr := procfs.ProcCmdline().Get(constants.KernelParamAuditdDisabled).First() disabled, _ := strconv.ParseBool(pointer.SafeDeref(disabledStr)) //nolint:errcheck if disabled { logger.Printf("auditd is disabled by kernel parameter %s", constants.KernelParamAuditdDisabled) return nil } } system.Services(r).LoadAndStart(&services.Auditd{}) return nil }, "startAuditd" } // StartDashboard represents the task to start dashboard. func StartDashboard(_ runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { return func(_ context.Context, _ *log.Logger, r runtime.Runtime) error { system.Services(r).LoadAndStart(&services.Dashboard{}) return nil }, "startDashboard" } // StartUdevd represents the task to start udevd. func StartUdevd(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { mp := mountv3.NewSystemOverlay( []string{constants.UdevDir}, constants.UdevDir, logger.Printf, mountv3.WithShared(), mountv3.WithSelinuxLabel(constants.UdevRulesLabel), ) if _, err = mp.Mount(); err != nil { return err } var extraSettleTime time.Duration settleTimeStr := procfs.ProcCmdline().Get(constants.KernelParamDeviceSettleTime).First() if settleTimeStr != nil { extraSettleTime, err = time.ParseDuration(*settleTimeStr) if err != nil { return fmt.Errorf("failed to parse %s: %w", constants.KernelParamDeviceSettleTime, err) } logger.Printf("extra settle time: %s", extraSettleTime) } svc := &services.Udevd{ ExtraSettleTime: extraSettleTime, } system.Services(r).LoadAndStart(svc) ctx, cancel := context.WithTimeout(ctx, 10*time.Minute) defer cancel() return system.WaitForService(system.StateEventUp, svc.ID(r)).Wait(ctx) }, "startUdevd" } // StartAllServices represents the task to start the system services. func StartAllServices(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { // nb: Treating the beginning of "service starts" as the activate event for a normal // non-maintenance mode boot. At this point, we'd expect the user to // start interacting with the system for troubleshooting at least. platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeActivate, Message: "Talos is ready for user interaction.", }, ) svcs := system.Services(r) // load the kubelet service, but don't start it; // KubeletServiceController will start it once it's ready. svcs.Load( &services.Kubelet{}, ) serviceList := []system.Service{ &services.CRI{}, } switch t := r.Config().Machine().Type(); t { case machine.TypeInit: serviceList = append(serviceList, &services.Trustd{}, &services.Etcd{Bootstrap: true}, ) case machine.TypeControlPlane: serviceList = append(serviceList, &services.Trustd{}, &services.Etcd{}, ) case machine.TypeWorker: // nothing case machine.TypeUnknown: fallthrough default: panic(fmt.Sprintf("unexpected machine type %v", t)) } svcs.LoadAndStart(serviceList...) all := make([]conditions.Condition, 0, len(svcs.List())) logger.Printf("waiting for %d services", len(svcs.List())) for _, svc := range svcs.List() { cond := system.WaitForService(system.StateEventUp, svc.AsProto().GetId()) all = append(all, cond) } ctx, cancel := context.WithTimeout(ctx, constants.BootTimeout) defer cancel() aggregateCondition := conditions.WaitForAll(all...) errChan := make(chan error) go func() { errChan <- aggregateCondition.Wait(ctx) }() ticker := time.NewTicker(15 * time.Second) defer ticker.Stop() for { logger.Printf("%s", aggregateCondition.String()) select { case err := <-errChan: return err case <-ticker.C: } } }, "startAllServices" } // StopServicesEphemeral represents the StopServicesEphemeral task. func StopServicesEphemeral(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { // stopping 'cri' service stops everything which depends on it (kubelet, etcd, ...) return system.Services(nil).StopWithRevDepenencies(ctx, "cri", "trustd") }, "stopServicesForUpgrade" } // StopAllServices represents the StopAllServices task. func StopAllServices(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { system.Services(nil).Shutdown(ctx) return nil }, "stopAllServices" } // SetupSharedFilesystems represents the SetupSharedFilesystems task. func SetupSharedFilesystems(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { targets := []string{"/", "/var", "/etc/cni", "/run"} for _, t := range targets { if err = unix.Mount("", t, "", unix.MS_SHARED|unix.MS_REC, ""); err != nil { return err } } return nil }, "setupSharedFilesystems" } // MountUserDisks represents the MountUserDisks task. func MountUserDisks(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { // wait for user disk config to be ready _, err := r.State().V1Alpha2().Resources().WatchFor(ctx, blockres.NewUserDiskConfigStatus(blockres.NamespaceName, blockres.UserDiskConfigStatusID).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { return r.(*blockres.UserDiskConfigStatus).TypedSpec().Ready, nil }), ) return err }, "mountUserDisks" } // WriteUserFiles represents the WriteUserFiles task. // //nolint:gocyclo,cyclop func WriteUserFiles(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { var result *multierror.Error files, err := r.Config().Machine().Files() if err != nil { return fmt.Errorf("error generating extra files: %w", err) } for _, f := range files { content := f.Content() switch f.Op() { case "create": // Allow create at all times. case "overwrite": if err = existsAndIsFile(f.Path()); err != nil { result = multierror.Append(result, err) continue } case "append": if err = existsAndIsFile(f.Path()); err != nil { result = multierror.Append(result, err) continue } var existingFileContents []byte existingFileContents, err = os.ReadFile(f.Path()) if err != nil { result = multierror.Append(result, err) continue } content = string(existingFileContents) + "\n" + f.Content() default: result = multierror.Append(result, fmt.Errorf("unknown operation for file %q: %q", f.Path(), f.Op())) continue } if filepath.Dir(f.Path()) == constants.ManifestsDirectory { if err = os.WriteFile(f.Path(), []byte(content), f.Permissions()); err != nil { result = multierror.Append(result, err) continue } if err = os.Chmod(f.Path(), f.Permissions()); err != nil { result = multierror.Append(result, err) continue } continue } // CRI configuration customization if f.Path() == filepath.Join("/etc", constants.CRICustomizationConfigPart) { if err = injectCRIConfigPatch(ctx, r.State().V1Alpha2().Resources(), []byte(f.Content())); err != nil { result = multierror.Append(result, err) } continue } // Determine if supplied path is in /var or not. // If not, we'll write it to /var anyways and bind mount below p := f.Path() inVar := true parts := strings.Split( strings.TrimLeft(f.Path(), "/"), string(os.PathSeparator), ) if parts[0] != "var" { p = filepath.Join("/var", f.Path()) inVar = false } // We do not want to support creating new files anywhere outside of // /var. If a valid use case comes up, we can reconsider then. if !inVar && f.Op() == "create" { return fmt.Errorf("create operation not allowed outside of /var: %q", f.Path()) } if err = os.MkdirAll(filepath.Dir(p), 0o755); err != nil { result = multierror.Append(result, err) continue } if err = os.WriteFile(p, []byte(content), f.Permissions()); err != nil { result = multierror.Append(result, err) continue } if err = os.Chmod(p, f.Permissions()); err != nil { result = multierror.Append(result, err) continue } if !inVar { if err = unix.Mount(p, f.Path(), "", unix.MS_BIND|unix.MS_RDONLY, ""); err != nil { result = multierror.Append(result, fmt.Errorf("failed to create bind mount for %s: %w", p, err)) } } } return result.ErrorOrNil() }, "writeUserFiles" } func injectCRIConfigPatch(ctx context.Context, st state.State, content []byte) error { // limit overall waiting time ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() etcFileSpec := resourcefiles.NewEtcFileSpec(resourcefiles.NamespaceName, constants.CRICustomizationConfigPart) etcFileSpec.TypedSpec().Mode = 0o600 etcFileSpec.TypedSpec().Contents = content etcFileSpec.TypedSpec().SelinuxLabel = constants.EtcSelinuxLabel if err := st.Create(ctx, etcFileSpec); err != nil { return err } checksumRaw := sha256.Sum256(content) expectedChecksum := hex.EncodeToString(checksumRaw[:]) expectedAnnotation := resourcefiles.SourceFileAnnotation + ":" + filepath.Join("/etc", etcFileSpec.Metadata().ID()) fileSpec, err := st.WatchFor(ctx, resourcefiles.NewEtcFileSpec(resourcefiles.NamespaceName, constants.CRIConfig).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { spec, ok := r.(*resourcefiles.EtcFileSpec) if !ok { return false, nil } value, ok := spec.Metadata().Annotations().Get(expectedAnnotation) return ok && value == expectedChecksum, nil })) if err != nil { return fmt.Errorf("error waiting for file %q to be updated: %w", constants.CRIConfig, err) } // wait for the file to be rendered _, err = st.WatchFor(ctx, resourcefiles.NewEtcFileStatus(resourcefiles.NamespaceName, constants.CRIConfig).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { fileStatus, ok := r.(*resourcefiles.EtcFileStatus) if !ok { return false, nil } return fileStatus.TypedSpec().SpecVersion == fileSpec.Metadata().Version().String(), nil })) return err } func existsAndIsFile(p string) (err error) { var info os.FileInfo info, err = os.Stat(p) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return err } return fmt.Errorf("file must exist: %q", p) } if !info.Mode().IsRegular() { return fmt.Errorf("invalid mode: %q", info.Mode().String()) } return nil } // UnmountPodMounts represents the UnmountPodMounts task. func UnmountPodMounts(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { var b []byte if b, err = os.ReadFile("/proc/self/mounts"); err != nil { return err } rdr := bytes.NewReader(b) scanner := bufio.NewScanner(rdr) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 2 { continue } mountpoint := fields[1] if strings.HasPrefix(mountpoint, constants.EphemeralMountPoint+"/") { logger.Printf("unmounting %s\n", mountpoint) if err = mountv3.SafeUnmount(ctx, logger.Printf, mountpoint, false); err != nil { if errors.Is(err, syscall.EINVAL) { log.Printf("ignoring unmount error %s: %v", mountpoint, err) } else { return fmt.Errorf("error unmounting %s: %w", mountpoint, err) } } } } return scanner.Err() }, "unmountPodMounts" } // UnmountSystemDiskBindMounts represents the UnmountSystemDiskBindMounts task. // //nolint:gocyclo func UnmountSystemDiskBindMounts(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { ephemeralStatus, err := safe.StateGetByID[*blockres.VolumeStatus](ctx, r.State().V1Alpha2().Resources(), constants.EphemeralPartitionLabel) if err != nil && !state.IsNotFoundError(err) { return err } if ephemeralStatus == nil { return nil } devname := ephemeralStatus.TypedSpec().MountLocation if devname == "" { return nil } f, err := os.Open("/proc/mounts") if err != nil { return err } defer f.Close() //nolint:errcheck scanner := bufio.NewScanner(f) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 2 { continue } device, mountpoint := fields[0], fields[1] if device != devname || mountpoint == constants.EphemeralMountPoint { continue } logger.Printf("unmounting %s\n", mountpoint) if err = mountv3.SafeUnmount(ctx, logger.Printf, mountpoint, false); err != nil { if errors.Is(err, syscall.EINVAL) { log.Printf("ignoring unmount error %s: %v", mountpoint, err) } else { return fmt.Errorf("error unmounting %s: %w", mountpoint, err) } } } return scanner.Err() }, "unmountSystemDiskBindMounts" } // CordonAndDrainNode represents the task for stop all containerd tasks in the // k8s.io namespace. func CordonAndDrainNode(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { // skip not exist error as it means that the node hasn't fully joined yet if _, err = os.Stat("/var/lib/kubelet/pki/kubelet-client-current.pem"); err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return err } var nodename string if nodename, err = r.NodeName(); err != nil { return err } // controllers will automatically cordon the node when the node enters appropriate phase, // so here we just wait for the node to be cordoned if err = waitForNodeCordoned(ctx, logger, r, nodename); err != nil { return err } var kubeHelper *kubernetes.Client if kubeHelper, err = kubernetes.NewClientFromKubeletKubeconfig(); err != nil { return err } defer kubeHelper.Close() //nolint:errcheck return kubeHelper.Drain(ctx, nodename) }, "cordonAndDrainNode" } func waitForNodeCordoned(ctx context.Context, logger *log.Logger, r runtime.Runtime, nodename string) error { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() logger.Print("waiting for node to be cordoned") _, err := r.State().V1Alpha2().Resources().WatchFor( ctx, k8s.NewNodeStatus(k8s.NamespaceName, nodename).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } nodeStatus, ok := r.(*k8s.NodeStatus) if !ok { return false, nil } return nodeStatus.TypedSpec().Unschedulable, nil }), ) return err } // LeaveEtcd represents the task for removing a control plane node from etcd. // //nolint:gocyclo func LeaveEtcd(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { _, err = os.Stat(filepath.Join(constants.EtcdDataPath, "/member")) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return err } etcdID := (&services.Etcd{}).ID(r) services := system.Services(r).List() shouldLeaveEtcd := false for _, service := range services { if service.AsProto().Id != etcdID { continue } switch service.GetState() { //nolint:exhaustive case events.StateRunning: fallthrough case events.StateStopping: fallthrough case events.StateFailed: shouldLeaveEtcd = true } break } if !shouldLeaveEtcd { return nil } client, err := etcd.NewClientFromControlPlaneIPs(ctx, r.State().V1Alpha2().Resources()) if err != nil { return fmt.Errorf("failed to create etcd client: %w", err) } //nolint:errcheck defer client.Close() ctx = clientv3.WithRequireLeader(ctx) if err = client.LeaveCluster(ctx, r.State().V1Alpha2().Resources()); err != nil { return fmt.Errorf("failed to leave cluster: %w", err) } return nil }, "leaveEtcd" } // RemoveAllPods represents the task for stopping and removing all pods. func RemoveAllPods(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return stopAndRemoveAllPods(cri.StopAndRemove), "removeAllPods" } // StopAllPods represents the task for stopping all pods. func StopAllPods(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return stopAndRemoveAllPods(cri.StopOnly), "stopAllPods" } func waitForKubeletLifecycleFinalizers(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { logger.Printf("waiting for kubelet lifecycle finalizers") ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() lifecycle := resource.NewMetadata(k8s.NamespaceName, k8s.KubeletLifecycleType, k8s.KubeletLifecycleID, resource.VersionUndefined) for { ok, err := r.State().V1Alpha2().Resources().Teardown(ctx, lifecycle) if err != nil { return err } if ok { break } _, err = r.State().V1Alpha2().Resources().WatchFor(ctx, lifecycle, state.WithFinalizerEmpty()) if err != nil { return err } } return r.State().V1Alpha2().Resources().Destroy(ctx, lifecycle) } func stopAndRemoveAllPods(stopAction cri.StopAction) runtime.TaskExecutionFunc { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { if err = waitForKubeletLifecycleFinalizers(ctx, logger, r); err != nil { logger.Printf("failed waiting for kubelet lifecycle finalizers: %s", err) } logger.Printf("shutting down kubelet gracefully") shutdownCtx, shutdownCtxCancel := context.WithTimeout(ctx, logind.InhibitMaxDelay) defer shutdownCtxCancel() if err = r.State().Machine().DBus().WaitShutdown(shutdownCtx); err != nil { logger.Printf("failed waiting for inhibit shutdown lock: %s", err) } if err = system.Services(nil).Stop(ctx, "kubelet"); err != nil { return err } // check that the CRI is running and the socket is available, if not, skip the rest if _, err = os.Stat(constants.CRIContainerdAddress); errors.Is(err, fs.ErrNotExist) { return nil } client, err := cri.NewClient("unix://"+constants.CRIContainerdAddress, 10*time.Second) if err != nil { return err } //nolint:errcheck defer client.Close() ctx, cancel := context.WithTimeout(ctx, time.Minute*3) defer cancel() // We remove pods with POD network mode first so that the CNI can perform // any cleanup tasks. If we don't do this, we run the risk of killing the // CNI, preventing the CRI from cleaning up the pod's networking. if err = client.StopAndRemovePodSandboxes(ctx, stopAction, runtimeapi.NamespaceMode_POD, runtimeapi.NamespaceMode_CONTAINER); err != nil { logger.Printf("failed to stop and remove pods with POD network mode: %s", err) } // With the POD network mode pods out of the way, we kill the remaining // pods. if err = client.StopAndRemovePodSandboxes(ctx, stopAction); err != nil { logger.Printf("failed to stop and remove pods: %s", err) } return nil } } // ResetSystemDiskPartitions represents the task for wiping the system disk partitions. func ResetSystemDiskPartitions(seq runtime.Sequence, _ any) (runtime.TaskExecutionFunc, string) { wipeStr := procfs.ProcCmdline().Get(constants.KernelParamWipe).First() reboot, _ := Reboot(seq, nil) if pointer.SafeDeref(wipeStr) == "" { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { return errors.New("no wipe target specified") }, "wipeSystemDisk" } if *wipeStr == "system" { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { systemDiskPaths, err := blockres.GetSystemDiskPaths(ctx, r.State().V1Alpha2().Resources()) if err != nil { return err } targets := targets{ systemDiskPaths: systemDiskPaths, } logger.Printf("resetting system disks") resetSystemDisk, _ := ResetSystemDisk(seq, targets) err = resetSystemDisk(ctx, logger, r) if err != nil { logger.Printf("resetting system disks failed") return err } logger.Printf("finished resetting system disks") return reboot(ctx, logger, r) // only reboot when we wiped boot partition }, "wipeSystemDisk" } return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { targets, err := parseTargets(ctx, r, *wipeStr) if err != nil { return err } fn, _ := ResetSystemDiskSpec(seq, targets) diskTargets := targets.GetSystemDiskTargets() logger.Printf("resetting system disks %s", diskTargets) err = fn(ctx, logger, r) if err != nil { logger.Printf("resetting system disks %s failed", diskTargets) return err } logger.Printf("finished resetting system disks %s", diskTargets) return reboot(ctx, logger, r) }, "wipeSystemDiskPartitions" } // ResetSystemDisk represents the task to reset the system disk. // //nolint:gocyclo func ResetSystemDisk(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { in, ok := data.(SystemDiskTargets) if !ok { return errors.New("unexpected runtime data") } for _, systemDiskPath := range in.GetSystemDiskPaths() { if err := func(devPath string) error { logger.Printf("wiping system disk %s", devPath) dev, err := block.NewFromPath(devPath, block.OpenForWrite()) if err != nil { return err } if err = dev.RetryLockWithTimeout(ctx, true, time.Minute); err != nil { return fmt.Errorf("failed to lock device %s: %w", devPath, err) } defer dev.Close() //nolint:errcheck if err = partition.WipeWithSignatures(dev, devPath, logger.Printf); err != nil { return err } return nil }(systemDiskPath); err != nil { return fmt.Errorf("failed to wipe system disk %s: %w", systemDiskPath, err) } } return nil }, "resetSystemDisk" } // ResetUserDisks represents the task to reset the user disks. func ResetUserDisks(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { in, ok := data.(runtime.ResetOptions) if !ok { return errors.New("unexpected runtime data") } wipeDevice := func(deviceName string) error { dev, err := block.NewFromPath(deviceName, block.OpenForWrite()) if err != nil { return err } defer func() { if closeErr := dev.Close(); closeErr != nil { logger.Printf("failed to close device %s: %s", deviceName, closeErr) } }() if err = dev.RetryLockWithTimeout(ctx, true, time.Minute); err != nil { return fmt.Errorf("failed to lock device %s: %w", deviceName, err) } defer dev.Unlock() //nolint:errcheck logger.Printf("wiping user disk %s", deviceName) if err = partition.WipeWithSignatures(dev, deviceName, logger.Printf); err != nil { return err } return nil } for _, deviceName := range in.GetUserDisksToWipe() { if err := wipeDevice(deviceName); err != nil { return err } } return nil }, "resetUserDisks" } type targets struct { systemDiskTargets []*partition.VolumeWipeTarget systemDiskPaths []string } var _ SystemDiskTargets = targets{} func (opt targets) GetSystemDiskTargets() []runtime.PartitionTarget { return xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) runtime.PartitionTarget { return t }) } func (opt targets) GetSystemDiskPaths() []string { return opt.systemDiskPaths } func (opt targets) String() string { return strings.Join(xslices.Map(opt.systemDiskTargets, func(t *partition.VolumeWipeTarget) string { return t.String() }), ", ") } func parseTargets(ctx context.Context, r runtime.Runtime, wipeStr string) (SystemDiskTargets, error) { after, found := strings.CutPrefix(wipeStr, "system:") if !found { return targets{}, fmt.Errorf("invalid wipe labels string: %q", wipeStr) } var result []*partition.VolumeWipeTarget // in this early phase, we don't have VolumeStatus resources, so instead we'd use DiscoveredVolumes // to get the volume paths discoveredVolumes, err := safe.StateListAll[*blockres.DiscoveredVolume](ctx, r.State().V1Alpha2().Resources()) if err != nil { return targets{}, err } for label := range strings.SplitSeq(after, ",") { found := false for discoveredVolume := range discoveredVolumes.All() { if discoveredVolume.TypedSpec().PartitionLabel != label { continue } result = append(result, partition.VolumeWipeTargetFromDiscoveredVolume(discoveredVolume)) found = true break } if !found { return targets{}, fmt.Errorf("failed to get volume status with label %q", label) } } if len(result) == 0 { return targets{}, errors.New("no wipe labels specified") } return targets{systemDiskTargets: result}, nil } // SystemDiskTargets represents the interface for getting the system disk targets. // It's a subset of [runtime.ResetOptions]. type SystemDiskTargets interface { GetSystemDiskTargets() []runtime.PartitionTarget GetSystemDiskPaths() []string fmt.Stringer } // ResetSystemDiskSpec represents the task to reset the system disk by spec. func ResetSystemDiskSpec(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { //nolint:gocyclo return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { in, ok := data.(SystemDiskTargets) if !ok { return errors.New("unexpected runtime data") } var ( metaWiped, stateWiped bool wipeErrors *multierror.Error ) for _, target := range in.GetSystemDiskTargets() { if err := target.Wipe(ctx, logger.Printf); err != nil { wipeErrors = multierror.Append(wipeErrors, fmt.Errorf("failed wiping partition %s: %w", target, err)) continue } switch target.GetLabel() { case constants.MetaPartitionLabel: metaWiped = true case constants.StatePartitionLabel: stateWiped = true } } if stateWiped && !metaWiped { removed, err := r.State().Machine().Meta().DeleteTag(ctx, metamachinery.StateEncryptionConfig) if err != nil { return fmt.Errorf("failed to remove state encryption META config tag: %w", err) } if removed { if err = r.State().Machine().Meta().Flush(); err != nil { return fmt.Errorf("failed to flush META: %w", err) } logger.Printf("reset the state encryption META config tag") } } if err := wipeErrors.ErrorOrNil(); err != nil { return err } logger.Printf("successfully reset system disk by the spec") return nil }, "resetSystemDiskSpec" } // Upgrade represents the task for performing an upgrade. func Upgrade(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { // This should be checked by the gRPC server, but we double check here just // to be safe. in, ok := data.(*machineapi.UpgradeRequest) if !ok { return runtime.ErrInvalidSequenceData } systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) if err != nil { return err } if systemDisk == nil { return fmt.Errorf("system disk not found") } devname := systemDisk.DevPath logger.Printf("performing upgrade via %q", in.GetImage()) // We pull the installer image when we receive an upgrade request. No need // to pull it again. err = install.RunInstallerContainer( devname, r.State().Platform().Name(), in.GetImage(), r.Config(), r.ConfigContainer(), r.State().V1Alpha2().Resources(), crires.RegistryBuilder(r.State().V1Alpha2().Resources()), install.OptionsFromUpgradeRequest(r, in)..., ) if err != nil { return err } logger.Println("upgrade successful") return nil }, "upgrade" } // Reboot represents the Reboot task. func Reboot(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { rebootCmd := unix.LINUX_REBOOT_CMD_RESTART if r.State().Machine().IsKexecPrepared() { rebootCmd = unix.LINUX_REBOOT_CMD_KEXEC } r.Events().Publish(ctx, &machineapi.RestartEvent{ Cmd: int64(rebootCmd), }) platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeRebooted, Message: "Talos rebooted.", }, ) return runtime.RebootError{Cmd: rebootCmd} }, "reboot" } // Shutdown represents the Shutdown task. func Shutdown(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { cmd := unix.LINUX_REBOOT_CMD_POWER_OFF if p := procfs.ProcCmdline().Get(constants.KernelParamShutdown).First(); p != nil { if *p == "halt" { cmd = unix.LINUX_REBOOT_CMD_HALT } } r.Events().Publish(ctx, &machineapi.RestartEvent{ Cmd: int64(cmd), }) return runtime.RebootError{Cmd: cmd} }, "shutdown" } // haltIfInstalled halts the boot process if Talos is installed to disk but booted from ISO. func haltIfInstalled(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { ctx, cancel := context.WithTimeout(ctx, constants.BootTimeout) defer cancel() timer := time.NewTicker(30 * time.Second) defer timer.Stop() for { logger.Printf("Talos is already installed to disk but booted from another media and %s kernel parameter is set. Please reboot from the disk.", constants.KernelParamHaltIfInstalled) select { case <-timer.C: case <-ctx.Done(): return ctx.Err() } } }, "haltIfInstalled" } // CleanupBootloader cleans up the ununsed bootloader if booted from a disk image with both bootloaders present. func CleanupBootloader(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) if err != nil { return err } if systemDisk == nil { return nil // no system disk, we can't do anything } if err := bootloader.CleanupBootloader(systemDisk.DevPath, sdboot.IsBootedUsingSDBoot()); err != nil { return err } if _, err := r.State().Machine().Meta().DeleteTag(ctx, metamachinery.DiskImageBootloader); err != nil { return fmt.Errorf("failed to delete tag %d: %w", metamachinery.DiskImageBootloader, err) } return r.State().Machine().Meta().Flush() }, "cleanupBootloader" } // MountEphemeralPartition mounts the ephemeral partition. func MountEphemeralPartition(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { mountRequest := blockres.NewVolumeMountRequest(blockres.NamespaceName, constants.EphemeralPartitionLabel) mountRequest.TypedSpec().VolumeID = constants.EphemeralPartitionLabel mountRequest.TypedSpec().Requester = "sequencer" if cfg := r.Config(); cfg != nil { vol, _ := cfg.Volumes().ByName(constants.EphemeralPartitionLabel) mountRequest.TypedSpec().Secure = vol.Mount().Secure() } if err := r.State().V1Alpha2().Resources().Create(ctx, mountRequest); err != nil { return fmt.Errorf("failed to create EPHEMERAL mount request: %w", err) } if _, err := r.State().V1Alpha2().Resources().WatchFor( ctx, blockres.NewVolumeMountStatus(blockres.NamespaceName, constants.EphemeralPartitionLabel).Metadata(), state.WithEventTypes(state.Created, state.Updated), ); err != nil { return fmt.Errorf("failed to wait for EPHEMERAL to be mounted: %w", err) } return nil }, "mountEphemeralPartition" } // UnmountEphemeralPartition unmounts the ephemeral partition. func UnmountEphemeralPartition(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { mountRequest := blockres.NewVolumeMountRequest(blockres.NamespaceName, constants.EphemeralPartitionLabel).Metadata() err := r.State().V1Alpha2().Resources().Destroy(ctx, mountRequest) if err != nil { if state.IsNotFoundError(err) { return nil } return fmt.Errorf("failed to destroy EPHEMERAL mount request: %w", err) } return nil }, "unmountEphemeralPartition" } // Install mounts or installs the system partitions. // //nolint:gocyclo,cyclop func Install(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { switch { case !r.State().Machine().Installed(): installerImage := r.Config().Machine().Install().Image() if installerImage == "" { installerImage = images.DefaultInstallerImage } logger.Printf("waiting for the image cache") if err = crires.WaitForImageCache(ctx, r.State().V1Alpha2().Resources()); err != nil { return fmt.Errorf("failed to wait for the image cache: %w", err) } var disk string matchExpr, err := r.Config().Machine().Install().DiskMatchExpression() if err != nil { return fmt.Errorf("failed to get disk match expression: %w", err) } switch { case matchExpr != nil: logger.Printf("using disk match expression: %s", matchExpr) matchedDisks, err := blockhelpers.MatchDisks(ctx, r.State().V1Alpha2().Resources(), matchExpr) if err != nil { return err } if len(matchedDisks) == 0 { return fmt.Errorf("no disks matched the expression: %s", matchExpr) } disk = matchedDisks[0].TypedSpec().DevPath case r.Config().Machine().Install().Disk() != "": disk = r.Config().Machine().Install().Disk() } disk, err = filepath.EvalSymlinks(disk) if err != nil { return err } logger.Printf("installing Talos to disk %s", disk) err = install.RunInstallerContainer( disk, r.State().Platform().Name(), installerImage, r.Config(), r.ConfigContainer(), r.State().V1Alpha2().Resources(), crires.RegistryBuilder(r.State().V1Alpha2().Resources()), install.WithForce(true), install.WithZero(r.Config().Machine().Install().Zero()), install.WithExtraKernelArgs(r.Config().Machine().Install().ExtraKernelArgs()), ) if err != nil { platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeFailure, Message: "Talos install failed.", Error: err, }, ) return err } platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeInstalled, Message: "Talos installed successfully.", }, ) logger.Println("install successful") logger.Printf("waiting for the image cache copy") if err = crires.WaitForImageCacheCopy(ctx, r.State().V1Alpha2().Resources()); err != nil { return fmt.Errorf("failed to wait for the image cache: %w", err) } case r.State().Machine().IsInstallStaged(): systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) if err != nil { return err } if systemDisk == nil { return fmt.Errorf("system disk not found") } devname := systemDisk.DevPath var options install.Options if err = json.Unmarshal(r.State().Machine().StagedInstallOptions(), &options); err != nil { return fmt.Errorf("error unserializing install options: %w", err) } logger.Printf("waiting for the image cache") if err = crires.WaitForImageCache(ctx, r.State().V1Alpha2().Resources()); err != nil { return fmt.Errorf("failed to wait for the image cache: %w", err) } logger.Printf("performing staged upgrade via %q", r.State().Machine().StagedInstallImageRef()) err = install.RunInstallerContainer( devname, r.State().Platform().Name(), r.State().Machine().StagedInstallImageRef(), r.Config(), r.ConfigContainer(), r.State().V1Alpha2().Resources(), crires.RegistryBuilder(r.State().V1Alpha2().Resources()), install.WithOptions(options), ) if err != nil { platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeFailure, Message: "Talos staged upgrade failed.", Error: err, }, ) return err } // nb: we don't fire an "activate" event after this one // b/c we'd only ever get here if Talos was already // installed I believe. platform.FireEvent( ctx, r.State().Platform(), platform.Event{ Type: platform.EventTypeUpgraded, Message: "Talos staged upgrade successful.", }, ) logger.Println("staged upgrade successful") default: return errors.New("unsupported configuration for install task") } return nil }, "install" } // KexecPrepare loads next boot kernel via kexec_file_load. func KexecPrepare(_ runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if req, ok := data.(*machineapi.RebootRequest); ok { if req.Mode == machineapi.RebootRequest_POWERCYCLE { log.Print("kexec skipped as reboot with power cycle was requested") return nil } } if efi.GetSecureBoot() { log.Print("kexec skipped as secure boot is enabled") return nil } if goruntime.GOARCH == "arm64" { // see https://lkml.org/lkml/2025/11/27/178 // [TODO]: remove this once the kernel issue is resolved // see also https://github.com/siderolabs/talos/pull/12396 log.Print("kexec skipped as kexec has issues on arm64") return nil } systemDisk, err := blockres.GetSystemDisk(ctx, r.State().V1Alpha2().Resources()) if err != nil { return err } if systemDisk == nil { log.Print("kexec skipped as system disk is not found") return nil // no system disk, no kexec } dev, err := block.NewFromPath(systemDisk.DevPath) if err != nil { return err } defer dev.Close() //nolint:errcheck if err = dev.RetryLockWithTimeout(ctx, false, 3*time.Minute); err != nil { log.Print("kexec skipped as system disk is busy") return nil } defer dev.Unlock() //nolint:errcheck bootloaderInfo, err := bootloader.Probe(systemDisk.DevPath, options.ProbeOptions{ Logger: log.Printf, }) if err != nil { return fmt.Errorf("failed to probe system disk: %w", err) } return bootloaderInfo.KexecLoad(r, systemDisk.DevPath) }, "kexecPrepare" } // StartDBus starts the D-Bus mock. func StartDBus(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { return r.State().Machine().DBus().Start() }, "startDBus" } // StopDBus stops the D-Bus mock. func StopDBus(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if err := r.State().Machine().DBus().Stop(); err != nil { logger.Printf("error stopping D-Bus: %s, ignored", err) } return nil }, "stopDBus" } // ForceCleanup kills remaining procs and forces partitions unmount. func ForceCleanup(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { if err := proc.KillAll(); err != nil { logger.Printf("error killing all procs: %s", err) } if err := mountv3.UnmountAll(); err != nil { logger.Printf("error unmounting: %s", err) } return nil }, "forceCleanup" } // ReloadMeta reloads META partition after disk mount, installer run, etc. // //nolint:gocyclo func ReloadMeta(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { err := r.State().Machine().Meta().Reload(ctx) if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } // attempt to populate meta from the environment if Talos is not installed (yet) if errors.Is(err, fs.ErrNotExist) { env := environment.Get(r.Config()) prefix := constants.MetaValuesEnvVar + "=" for _, e := range env { if !strings.HasPrefix(e, prefix) { continue } values, err := metamachinery.DecodeValues(e[len(prefix):]) if err != nil { return fmt.Errorf("error decoding meta values: %w", err) } for _, value := range values { _, err = r.State().Machine().Meta().SetTag(ctx, value.Key, value.Value) if err != nil { return fmt.Errorf("error setting meta tag %x: %w", value.Key, err) } } } } if _, err := safe.ReaderGetByID[*resourceruntime.MetaLoaded]( ctx, r.State().V1Alpha2().Resources(), resourceruntime.MetaLoadedID, ); err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("error reading MetaLoaded resource: %w", err) } // create MetaLoaded resource signaling that META is now loaded loaded := resourceruntime.NewMetaLoaded() loaded.TypedSpec().Done = true err = r.State().V1Alpha2().Resources().Create(ctx, loaded) if err != nil { return fmt.Errorf("error creating MetaLoaded resource: %w", err) } } return nil }, "reloadMeta" } // FlushMeta flushes META partition after install run. func FlushMeta(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { // META partition should be created at this point. if _, err := waitForVolumeReady(ctx, r, constants.MetaPartitionLabel); err != nil { return err } return r.State().Machine().Meta().Flush() }, "flushMeta" } // StoreShutdownEmergency stores shutdown emergency state. func StoreShutdownEmergency(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { // for shutdown sequence, store power_off as the intent, it will be picked up // by emergency handled in machined/main.go if the Shutdown sequence fails emergency.RebootCmd.Store(unix.LINUX_REBOOT_CMD_POWER_OFF) return nil }, "storeShutdownEmergency" } // SendResetSignal func represents the task to send the final reset signal. func SendResetSignal(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { return r.State().V1Alpha2().Resources().Create(ctx, resourceruntime.NewMachineResetSignal()) }, "sendResetSignal" } // WaitForCARoots represents the WaitForCARoots task. // //nolint:gocyclo func WaitForCARoots(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) (err error) { // watch EtcFileSpec & Status for CA roots and ensure they match ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() ch := make(chan state.Event) if err = r.State().V1Alpha2().Resources().Watch(ctx, resourcefiles.NewEtcFileSpec(resourcefiles.NamespaceName, constants.DefaultTrustedRelativeCAFile).Metadata(), ch); err != nil { return err } if err = r.State().V1Alpha2().Resources().Watch(ctx, resourcefiles.NewEtcFileStatus(resourcefiles.NamespaceName, constants.DefaultTrustedRelativeCAFile).Metadata(), ch); err != nil { return err } var specVersion, statusVersion string for { select { case <-ctx.Done(): return ctx.Err() case e := <-ch: switch e.Type { case state.Errored: return e.Error case state.Bootstrapped, state.Destroyed, state.Noop: // ignore case state.Created, state.Updated: switch res := e.Resource.(type) { case *resourcefiles.EtcFileSpec: specVersion = res.Metadata().Version().String() case *resourcefiles.EtcFileStatus: statusVersion = res.TypedSpec().SpecVersion } } } if specVersion != "" && statusVersion != "" && specVersion == statusVersion { // success return nil } } }, "waitForCARoots" } // TeardownVolumeLifecycle tears down volume lifecycle resource. func TeardownVolumeLifecycle(runtime.Sequence, any) (runtime.TaskExecutionFunc, string) { return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() volumeLifecycle := blockres.NewVolumeLifecycle(blockres.NamespaceName, blockres.VolumeLifecycleID).Metadata() _, err := r.State().V1Alpha2().Resources().Teardown(ctx, volumeLifecycle) if err != nil { if state.IsNotFoundError(err) { return nil } return err } _, err = r.State().V1Alpha2().Resources().WatchFor(ctx, volumeLifecycle, state.WithFinalizerEmpty()) if err != nil { return err } return r.State().V1Alpha2().Resources().Destroy(ctx, volumeLifecycle) }, "teardownLifecycle" } func pauseOnFailure(callback func(runtime.Sequence, any) (runtime.TaskExecutionFunc, string), timeout time.Duration, ) func(seq runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { return func(seq runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { f, name := callback(seq, data) return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { err := f(ctx, logger, r) if err != nil { logger.Printf("%s failed, rebooting in %.0f minutes. You can use talosctl apply-config or talosctl edit mc to fix the issues, error:\n%s", name, timeout.Minutes(), err) timer := time.NewTimer(time.Minute * 5) defer timer.Stop() select { case <-timer.C: case <-ctx.Done(): } } return err }, name } } func taskErrorHandler(handler func(error, *log.Logger) error, task runtime.TaskSetupFunc) runtime.TaskSetupFunc { return func(seq runtime.Sequence, data any) (runtime.TaskExecutionFunc, string) { f, name := task(seq, data) return func(ctx context.Context, logger *log.Logger, r runtime.Runtime) error { err := f(ctx, logger, r) if err != nil { return handler(err, logger) } return nil }, name } } func phaseListErrorHandler(handler func(error, *log.Logger) error, phases ...runtime.Phase) PhaseList { for _, phase := range phases { for i, task := range phase.Tasks { phase.Tasks[i] = taskErrorHandler(handler, task) } } return phases } func logError(err error, logger *log.Logger) error { logger.Printf("WARNING: task failed: %s", err) return nil } func waitForVolumeReady(ctx context.Context, r runtime.Runtime, volumeID string) (*blockres.VolumeStatus, error) { return blockres.WaitForVolumePhase(ctx, r.State().V1Alpha2().Resources(), volumeID, blockres.VolumePhaseReady) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:scopelint,testpackage package v1alpha1 import ( "reflect" "slices" "testing" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) func TestNewSequencer(t *testing.T) { tests := []struct { name string want *Sequencer }{ { name: "test", want: &Sequencer{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := NewSequencer(); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewSequencer() = %v, want %v", got, tt.want) } }) } } func TestPhaseList_Append(t *testing.T) { t.Skip("temporarily disabling until reflect.DeepEqual responds as expected") type args struct { name string tasks []runtime.TaskSetupFunc } tests := []struct { name string p PhaseList args args want PhaseList }{ { name: "test", p: PhaseList{}, args: args{ name: "mount", tasks: []runtime.TaskSetupFunc{KexecPrepare}, }, want: PhaseList{runtime.Phase{Name: "mount", Tasks: []runtime.TaskSetupFunc{KexecPrepare}}}, }, } cmp := func(a, b runtime.Phase) bool { return a.Name == b.Name } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.p = tt.p.Append(tt.args.name, tt.args.tasks...); !slices.EqualFunc(tt.p, tt.want, cmp) { t.Errorf("PhaseList.Append() = %v, want %v", tt.p, tt.want) } }) } } func TestPhaseList_AppendWhen(t *testing.T) { t.Skip("temporarily disabling until reflect.DeepEqual responds as expected") type args struct { when bool name string tasks []runtime.TaskSetupFunc } tests := []struct { name string p PhaseList args args want PhaseList }{ { name: "true", p: PhaseList{}, args: args{ when: true, name: "mount", tasks: []runtime.TaskSetupFunc{KexecPrepare}, }, want: PhaseList{runtime.Phase{Name: "mount", Tasks: []runtime.TaskSetupFunc{KexecPrepare}}}, }, { name: "false", p: PhaseList{}, args: args{ when: false, tasks: []runtime.TaskSetupFunc{KexecPrepare}, }, want: PhaseList{}, }, } cmp := func(a, b runtime.Phase) bool { return a.Name == b.Name } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.p = tt.p.AppendWhen(tt.args.when, tt.args.name, tt.args.tasks...); !slices.EqualFunc(tt.p, tt.want, cmp) { t.Errorf("PhaseList.AppendWhen() = %v, want %v", tt.p, tt.want) } }) } } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "log" "os" "sync" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha2" "github.com/siderolabs/talos/internal/pkg/meta" "github.com/siderolabs/talos/pkg/machinery/constants" metaconsts "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // State implements the state interface. type State struct { platform runtime.Platform machine *MachineState cluster *ClusterState v2 runtime.V1Alpha2State } // MachineState represents the machine's state. type MachineState struct { platform runtime.Platform resources state.State meta *meta.Meta metaOnce sync.Once stagedInstall bool stagedInstallImageRef string stagedInstallOptions []byte kexecPrepared bool dbus DBusState } // ClusterState represents the cluster's state. type ClusterState struct{} // NewState initializes and returns the v1alpha1 state. func NewState() (s *State, err error) { p, err := platform.CurrentPlatform() if err != nil { return nil, err } v2State, err := v1alpha2.NewState() if err != nil { return nil, err } machine := &MachineState{ platform: p, resources: v2State.Resources(), } cluster := &ClusterState{} s = &State{ platform: p, cluster: cluster, machine: machine, v2: v2State, } return s, nil } // Platform implements the state interface. func (s *State) Platform() runtime.Platform { return s.platform } // Machine implements the state interface. func (s *State) Machine() runtime.MachineState { return s.machine } // Cluster implements the state interface. func (s *State) Cluster() runtime.ClusterState { return s.cluster } // V1Alpha2 implements the state interface. func (s *State) V1Alpha2() runtime.V1Alpha2State { return s.v2 } // Meta implements the runtime.MachineState interface. func (s *MachineState) Meta() runtime.Meta { // no META in container mode if s.platform.Mode() == runtime.ModeContainer { return s } var ( justLoaded bool loadErr error ) s.metaOnce.Do(func() { s.meta, loadErr = meta.New(context.Background(), s.resources) if loadErr != nil { if !os.IsNotExist(loadErr) { log.Printf("META: failed to load: %s", loadErr) } } else { s.probeMeta() } justLoaded = true }) return metaWrapper{ MachineState: s, justLoaded: justLoaded, loadErr: loadErr, } } // ReadTag implements the runtime.Meta interface. func (s *MachineState) ReadTag(t uint8) (val string, ok bool) { if s.platform.Mode() == runtime.ModeContainer { return "", false } return s.meta.ReadTag(t) } // ReadTagBytes implements the runtime.Meta interface. func (s *MachineState) ReadTagBytes(t uint8) (val []byte, ok bool) { if s.platform.Mode() == runtime.ModeContainer { return nil, false } return s.meta.ReadTagBytes(t) } // SetTag implements the runtime.Meta interface. func (s *MachineState) SetTag(ctx context.Context, t uint8, val string) (bool, error) { if s.platform.Mode() == runtime.ModeContainer { return false, nil } return s.meta.SetTag(ctx, t, val) } // SetTagBytes implements the runtime.Meta interface. func (s *MachineState) SetTagBytes(ctx context.Context, t uint8, val []byte) (bool, error) { if s.platform.Mode() == runtime.ModeContainer { return false, nil } return s.meta.SetTagBytes(ctx, t, val) } // DeleteTag implements the runtime.Meta interface. func (s *MachineState) DeleteTag(ctx context.Context, t uint8) (bool, error) { if s.platform.Mode() == runtime.ModeContainer { return false, nil } return s.meta.DeleteTag(ctx, t) } // Reload implements the runtime.Meta interface. func (s *MachineState) Reload(ctx context.Context) error { if s.platform.Mode() == runtime.ModeContainer { return nil } err := s.meta.Reload(ctx) if err == nil { s.probeMeta() } return err } // Flush implements the runtime.Meta interface. func (s *MachineState) Flush() error { if s.platform.Mode() == runtime.ModeContainer { return nil } return s.meta.Flush() } func (s *MachineState) probeMeta() { stagedInstallImageRef, ok1 := s.meta.ReadTag(metaconsts.StagedUpgradeImageRef) stagedInstallOptions, ok2 := s.meta.ReadTag(metaconsts.StagedUpgradeInstallOptions) s.stagedInstall = ok1 && ok2 if s.stagedInstall { // clear the staged install flags _, err1 := s.meta.DeleteTag(context.Background(), metaconsts.StagedUpgradeImageRef) _, err2 := s.meta.DeleteTag(context.Background(), metaconsts.StagedUpgradeInstallOptions) if err := s.meta.Flush(); err != nil || err1 != nil || err2 != nil { // failed to delete staged install tags, clear the stagedInstall to prevent boot looping s.stagedInstall = false } s.stagedInstallImageRef = stagedInstallImageRef s.stagedInstallOptions = []byte(stagedInstallOptions) } } // Installed implements the machine state interface. func (s *MachineState) Installed() bool { // undefined in container mode if s.platform.Mode() == runtime.ModeContainer { return true } // legacy flow, no context available ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) defer cancel() metaStatus, err := safe.StateWatchFor[*block.VolumeStatus]( ctx, s.resources, block.NewVolumeStatus(block.NamespaceName, constants.MetaPartitionLabel).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { vs, ok := r.(*block.VolumeStatus) if !ok { return false, nil } switch vs.TypedSpec().Phase { //nolint:exhaustive case block.VolumePhaseMissing: // no META, talos is not installed return true, nil case block.VolumePhaseReady: // META found return true, nil default: return false, nil } }), ) if err != nil { return false } return metaStatus.TypedSpec().Phase == block.VolumePhaseReady } // IsInstallStaged implements the machine state interface. func (s *MachineState) IsInstallStaged() bool { return s.stagedInstall } // StagedInstallImageRef implements the machine state interface. func (s *MachineState) StagedInstallImageRef() string { return s.stagedInstallImageRef } // StagedInstallOptions implements the machine state interface. func (s *MachineState) StagedInstallOptions() []byte { return s.stagedInstallOptions } // KexecPrepared implements the machine state interface. func (s *MachineState) KexecPrepared(prepared bool) { s.kexecPrepared = prepared } // IsKexecPrepared implements the machine state interface. func (s *MachineState) IsKexecPrepared() bool { return s.kexecPrepared } // DBus implements the machine state interface. func (s *MachineState) DBus() runtime.DBusState { return &s.dbus } type metaWrapper struct { *MachineState justLoaded bool loadErr error } func (m metaWrapper) Reload(ctx context.Context) error { if m.justLoaded { return m.loadErr } return m.MachineState.Reload(ctx) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha2/adapters.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha2 import ( "context" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/config" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" ) // platformConfigurator adapts a runtime.Platform to the config.PlatformConfigurator interface. type platformConfigurator struct { platform runtime.Platform state state.State } // Check interfaces. var ( _ config.PlatformConfigurator = &platformConfigurator{} ) func (p *platformConfigurator) Name() string { return p.platform.Name() } func (p *platformConfigurator) Configuration(ctx context.Context) ([]byte, error) { return p.platform.Configuration(ctx, p.state) } // platformEventer adapts a runtime.Platform to the config.PlatformEventer interface. type platformEventer struct { platform runtime.Platform } // Check interfaces. var ( _ config.PlatformEventer = &platformEventer{} ) func (p *platformEventer) FireEvent(ctx context.Context, event platform.Event) { platform.FireEvent(ctx, p.platform, event) } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha2/v1alpha2.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package v1alpha2 provides runtime implementation based on os-runtime. package v1alpha2 ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha2 import ( "context" "fmt" "net/url" "sync" "time" "github.com/cosi-project/runtime/pkg/controller" osruntime "github.com/cosi-project/runtime/pkg/controller/runtime" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/block" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cluster" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/config" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/cri" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/etcd" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/files" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/hardware" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/k8s" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubeaccess" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/kubespan" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/network" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/perf" runtimecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/secrets" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/security" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/siderolink" timecontrollers "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/time" "github.com/siderolabs/talos/internal/app/machined/pkg/controllers/v1alpha1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" runtimelogging "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/pkg/logging" talosconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" configresource "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) // Controller implements runtime.V1alpha2Controller. type Controller struct { controllerRuntime *osruntime.Runtime loggingManager runtime.LoggingManager consoleLogLevel zap.AtomicLevel logger *zap.Logger v1alpha1Runtime runtime.Runtime } // NewController creates Controller. func NewController(v1alpha1Runtime runtime.Runtime) (*Controller, error) { ctrl := &Controller{ consoleLogLevel: zap.NewAtomicLevel(), loggingManager: v1alpha1Runtime.Logging(), v1alpha1Runtime: v1alpha1Runtime, } var err error ctrl.logger, err = ctrl.MakeLogger("controller-runtime") if err != nil { return nil, err } ctrl.controllerRuntime, err = osruntime.NewRuntime(v1alpha1Runtime.State().V1Alpha2().Resources(), ctrl.logger) return ctrl, err } // Run the controller runtime. func (ctrl *Controller) Run(ctx context.Context, drainer *runtime.Drainer) error { // adjust the log level based on machine configuration go ctrl.watchMachineConfig(ctx) dnsCacheLogger, err := ctrl.MakeLogger("dns-resolve-cache") if err != nil { return err } var ( etcRoot xfs.Root networkEtcRoot xfs.Root networkBindMountTarget string ) etcRoot = &xfs.UnixRoot{ FS: fsopen.New( "tmpfs", fsopen.WithStringParameter("mode", "0755"), fsopen.WithStringParameter("size", "8M"), ), } networkEtcRoot = &xfs.UnixRoot{ FS: fsopen.New( "tmpfs", fsopen.WithStringParameter("mode", "0755"), fsopen.WithStringParameter("size", "4M"), ), } networkBindMountTarget = constants.SystemResolvedPath // While running in container, we don't have control over kernel version // shipped with the machine. If the kernel does not support open_tree syscall // on anonymous filesystem file descriptors, we need to fallback to the classic, // less secure mode. This capability was added in kernel 6.15.0. if ctrl.v1alpha1Runtime.State().Platform().Mode().InContainer() { opentreeOnAnonymous, err := runtime.KernelCapabilities().OpentreeOnAnonymousFS() if err != nil { return err } if !opentreeOnAnonymous { etcRoot = &xfs.OSRoot{ Shadow: constants.SystemEtcPath, } networkEtcRoot = &xfs.OSRoot{ Shadow: constants.SystemResolvedPath, } networkBindMountTarget = "" } } if err := etcRoot.OpenFS(); err != nil { return fmt.Errorf("failed to open etc root: %w", err) } defer etcRoot.Close() //nolint:errcheck if err := networkEtcRoot.OpenFS(); err != nil { return fmt.Errorf("failed to open network etc root: %w", err) } defer networkEtcRoot.Close() //nolint:errcheck for _, c := range []controller.Controller{ &block.DevicesController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &block.DiscoveryController{}, &block.DisksController{}, &block.LVMActivationController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &block.MountController{}, &block.MountRequestController{}, &block.MountStatusController{}, &block.SwapStatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &block.SymlinksController{}, &block.SystemDiskController{}, &block.UserDiskConfigController{}, &block.VolumeConfigController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), MetaProvider: ctrl.v1alpha1Runtime.State().Machine(), }, &block.VolumeManagerController{}, &block.ZswapConfigController{}, &block.ZswapStatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &cluster.AffiliateMergeController{}, cluster.NewConfigController(), &cluster.DiscoveryServiceController{}, &cluster.EndpointController{}, cluster.NewInfoController(), &cluster.KubernetesPullController{}, &cluster.KubernetesPushController{}, &cluster.LocalAffiliateController{}, &cluster.MemberController{}, &cluster.NodeIdentityController{}, &config.AcquireController{ PlatformConfiguration: &platformConfigurator{ platform: ctrl.v1alpha1Runtime.State().Platform(), state: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), }, PlatformEvent: &platformEventer{ platform: ctrl.v1alpha1Runtime.State().Platform(), }, Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), CmdlineGetter: procfs.ProcCmdline, ConfigSetter: ctrl.v1alpha1Runtime, EventPublisher: ctrl.v1alpha1Runtime.Events(), ValidationMode: ctrl.v1alpha1Runtime.State().Platform().Mode(), ResourceState: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), }, &config.MachineTypeController{}, &config.PersistenceController{}, &cri.ImageCacheConfigController{ V1Alpha1ServiceManager: system.Services(ctrl.v1alpha1Runtime), }, cri.NewImageGCController("containerd", false), cri.NewImageGCController("cri", true), &cri.RegistriesConfigController{}, &cri.SeccompProfileController{}, &cri.SeccompProfileFileController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), SeccompProfilesDirectory: constants.SeccompProfilesDirectory, }, &etcd.AdvertisedPeerController{}, etcd.NewConfigController(), &etcd.PKIController{}, &etcd.SpecController{}, &etcd.MemberController{}, &files.CRIBaseRuntimeSpecController{}, &files.CRIConfigPartsController{}, &files.CRIRegistryConfigController{ EtcRoot: etcRoot, EtcPath: "/etc", }, &files.EtcFileController{ EtcRoot: etcRoot, EtcPath: "/etc", }, &files.IQNController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &files.NQNController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &hardware.PCIDevicesController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &hardware.PCIDriverRebindConfigController{}, &hardware.PCIDriverRebindController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &hardware.PCRStatusController{}, &hardware.SystemInfoController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &k8s.AddressFilterController{}, k8s.NewControlPlaneAPIServerController(), k8s.NewControlPlaneAdmissionControlController(), k8s.NewControlPlaneAuditPolicyController(), k8s.NewControlPlaneAuthorizationController(), k8s.NewControlPlaneBootstrapManifestsController(), k8s.NewControlPlaneControllerManagerController(), k8s.NewControlPlaneExtraManifestsController(), k8s.NewControlPlaneSchedulerController(), &k8s.ControlPlaneStaticPodController{}, &k8s.EndpointController{}, &k8s.ExtraManifestController{}, k8s.NewKubeletConfigController(), &k8s.KubeletServiceController{ V1Alpha1Services: system.Services(ctrl.v1alpha1Runtime), V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &k8s.KubeletSpecController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &k8s.KubeletStaticPodController{}, k8s.NewKubePrismEndpointsController(), k8s.NewKubePrismConfigController(), &k8s.KubePrismController{}, &k8s.ManifestApplyController{}, &k8s.ManifestController{}, k8s.NewNodeIPConfigController(), &k8s.NodeIPController{}, &k8s.NodeAnnotationSpecController{}, &k8s.NodeApplyController{}, &k8s.NodeCordonedSpecController{}, &k8s.NodeLabelSpecController{}, &k8s.NodeStatusController{}, &k8s.NodeTaintSpecController{}, &k8s.NodenameController{}, &k8s.RenderConfigsStaticPodController{}, &k8s.RenderSecretsStaticPodController{}, &k8s.StaticEndpointController{}, &k8s.StaticPodConfigController{}, &k8s.StaticPodServerController{}, kubeaccess.NewConfigController(), &kubeaccess.CRDController{}, &kubeaccess.EndpointController{}, kubespan.NewConfigController(), &kubespan.EndpointController{}, &kubespan.IdentityController{}, &kubespan.ManagerController{}, &kubespan.PeerSpecController{}, &network.AddressConfigController{ Cmdline: procfs.ProcCmdline(), V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &network.AddressEventController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), }, network.NewAddressMergeController(), &network.AddressSpecController{}, &network.AddressStatusController{}, &network.DeviceConfigController{}, &network.DNSResolveCacheController{ State: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), Logger: dnsCacheLogger, }, &network.DNSUpstreamController{}, &network.EtcFileController{ EtcRoot: networkEtcRoot, BindMountTarget: networkBindMountTarget, V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &network.EthernetConfigController{}, &network.EthernetSpecController{}, &network.EthernetStatusController{}, &network.HardwareAddrController{}, &network.HostDNSConfigController{}, &network.HostnameConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewHostnameMergeController(), &network.HostnameSpecController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &network.LinkAliasConfigController{}, &network.LinkAliasSpecController{}, &network.LinkConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewLinkMergeController(), &network.LinkSpecController{}, &network.LinkStatusController{}, &network.NfTablesChainConfigController{}, &network.NfTablesChainController{}, &network.NodeAddressController{}, &network.NodeAddressSortAlgorithmController{}, &network.OperatorConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewOperatorMergeController(), &network.OperatorSpecController{ V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(), State: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), }, &network.OperatorVIPConfigController{ Cmdline: procfs.ProcCmdline(), }, &network.PlatformConfigApplyController{ V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(), }, &network.PlatformConfigController{ V1alpha1Platform: ctrl.v1alpha1Runtime.State().Platform(), PlatformState: ctrl.v1alpha1Runtime.State().V1Alpha2().Resources(), }, &network.PlatformConfigLoadController{}, &network.PlatformConfigStoreController{}, &network.ProbeController{}, &network.ProbeConfigController{}, network.NewProbeMergeController(), &network.ResolverConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewResolverMergeController(), &network.ResolverSpecController{}, &network.RouteConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewRouteMergeController(), &network.RouteSpecController{}, &network.RouteStatusController{}, &network.RoutingRuleConfigController{}, network.NewRoutingRuleMergeController(), &network.RoutingRuleSpecController{}, &network.RoutingRuleStatusController{}, &network.StatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &network.TimeServerConfigController{ Cmdline: procfs.ProcCmdline(), }, network.NewTimeServerMergeController(), &network.TimeServerSpecController{}, &perf.StatsController{}, &runtimecontrollers.APIServiceConfigController{}, &runtimecontrollers.BootedEntryController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.DevicesStatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.DiagnosticsController{}, &runtimecontrollers.DiagnosticsLoggerController{}, &runtimecontrollers.DropUpgradeFallbackController{ MetaProvider: ctrl.v1alpha1Runtime.State().Machine(), }, &runtimecontrollers.EnvironmentController{}, &runtimecontrollers.ExtensionServiceConfigController{}, &runtimecontrollers.ExtensionServiceConfigFilesController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), ExtensionsConfigBaseDir: constants.ExtensionServiceUserConfigPath, }, &runtimecontrollers.EventsSinkConfigController{ Cmdline: procfs.ProcCmdline(), V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.EventsSinkController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), Drainer: drainer, }, &runtimecontrollers.ExtensionServiceController{ V1Alpha1Services: system.Services(ctrl.v1alpha1Runtime), ConfigPath: constants.ExtensionServiceConfigPath, }, &runtimecontrollers.ExtensionStatusController{}, &runtimecontrollers.KernelCmdlineController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.KernelModuleConfigController{}, &runtimecontrollers.KernelModuleSpecController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.KernelParamConfigController{}, &runtimecontrollers.KernelParamDefaultsController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.KernelParamSpecController{}, &runtimecontrollers.KmsgLogConfigController{ Cmdline: procfs.ProcCmdline(), }, &runtimecontrollers.KmsgLogDeliveryController{ Drainer: drainer, }, &runtimecontrollers.KmsgLogStorageController{ V1Alpha1Logging: ctrl.v1alpha1Runtime.Logging(), V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.LogPersistenceController{ V1Alpha1Logging: ctrl.v1alpha1Runtime.Logging(), }, &runtimecontrollers.LoadedKernelModuleController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.MaintenanceConfigController{}, &runtimecontrollers.MaintenanceServiceInformController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.MachineStatusController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), }, &runtimecontrollers.MachineStatusPublisherController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), }, &runtimecontrollers.MountStatusController{}, &runtimecontrollers.SBOMItemController{}, &runtimecontrollers.SecurityStateController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &runtimecontrollers.UniqueMachineTokenController{}, &runtimecontrollers.VersionController{}, &runtimecontrollers.WatchdogTimerConfigController{}, &runtimecontrollers.WatchdogTimerController{}, &runtimecontrollers.OOMController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &secrets.APICertSANsController{}, &secrets.APIController{}, &secrets.EncryptionSaltController{}, &secrets.EtcdController{}, secrets.NewKubeletController(), &secrets.KubernetesCertSANsController{}, &secrets.KubernetesDynamicCertsController{}, &secrets.KubernetesController{}, &secrets.MaintenanceCertSANsController{}, &secrets.MaintenanceRootController{}, secrets.NewRootEtcdController(), secrets.NewRootKubernetesController(), secrets.NewRootOSController(), &secrets.TrustedRootsController{}, &secrets.TrustdController{}, &security.ImageVerificationConfigController{}, &security.TUFTrustedRootController{}, &siderolink.ConfigController{ Cmdline: procfs.ProcCmdline(), V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &siderolink.ManagerController{}, &siderolink.StatusController{}, &siderolink.UserspaceWireguardController{ RelayRetryTimeout: 10 * time.Second, }, &timecontrollers.AdjtimeStatusController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &timecontrollers.SyncController{ V1Alpha1Mode: ctrl.v1alpha1Runtime.State().Platform().Mode(), }, &v1alpha1.ServiceController{ V1Alpha1Events: ctrl.v1alpha1Runtime.Events(), }, } { if err := ctrl.controllerRuntime.RegisterController(c); err != nil { return err } } return ctrl.controllerRuntime.Run(ctx) } // DependencyGraph returns controller-resources dependencies. func (ctrl *Controller) DependencyGraph() (*controller.DependencyGraph, error) { return ctrl.controllerRuntime.GetDependencyGraph() } type loggingDestination struct { Format string Endpoint *url.URL ExtraTags map[string]string } func (a *loggingDestination) Equal(b *loggingDestination) bool { if a.Format != b.Format { return false } if a.Endpoint.String() != b.Endpoint.String() { return false } if len(a.ExtraTags) != len(b.ExtraTags) { return false } for k, v := range a.ExtraTags { if vv, ok := b.ExtraTags[k]; !ok || vv != v { return false } } return true } func (ctrl *Controller) watchMachineConfig(ctx context.Context) { watchCh := make(chan state.Event) if err := ctrl.v1alpha1Runtime.State().V1Alpha2().Resources().Watch( ctx, resource.NewMetadata(configresource.NamespaceName, configresource.MachineConfigType, configresource.ActiveID, resource.VersionUndefined), watchCh, ); err != nil { ctrl.logger.Warn("error watching machine configuration", zap.Error(err)) return } var loggingDestinations []loggingDestination for { var cfg talosconfig.Config select { case event := <-watchCh: if event.Type != state.Created && event.Type != state.Updated { continue } cfg = event.Resource.(*configresource.MachineConfig).Config() case <-ctx.Done(): return } ctrl.updateConsoleLoggingConfig(cfg.Debug()) if cfg.Machine() == nil { ctrl.updateLoggingConfig(ctx, nil, &loggingDestinations) } else { ctrl.updateLoggingConfig(ctx, cfg.Machine().Logging().Destinations(), &loggingDestinations) } } } func (ctrl *Controller) updateConsoleLoggingConfig(debug bool) { newLogLevel := zapcore.InfoLevel if debug { newLogLevel = zapcore.DebugLevel } if newLogLevel != ctrl.consoleLogLevel.Level() { ctrl.logger.Info("setting console log level", zap.Stringer("level", newLogLevel)) ctrl.consoleLogLevel.SetLevel(newLogLevel) } } func (ctrl *Controller) updateLoggingConfig(ctx context.Context, dests []talosconfig.LoggingDestination, prevLoggingDestinations *[]loggingDestination) { loggingDestinations := make([]loggingDestination, len(dests)) for i, dest := range dests { switch f := dest.Format(); f { case constants.LoggingFormatJSONLines: loggingDestinations[i] = loggingDestination{ Format: f, Endpoint: dest.Endpoint(), ExtraTags: dest.ExtraTags(), } default: // should not be possible due to validation panic(fmt.Sprintf("unhandled log destination format %q", f)) } } loggingChanged := len(*prevLoggingDestinations) != len(loggingDestinations) if !loggingChanged { for i, u := range *prevLoggingDestinations { if !u.Equal(&loggingDestinations[i]) { loggingChanged = true break } } } if !loggingChanged { return } *prevLoggingDestinations = loggingDestinations var prevSenders []runtime.LogSender if len(loggingDestinations) > 0 { senders := xslices.Map(dests, runtimelogging.NewJSONLines) ctrl.logger.Info("enabling JSON logging") prevSenders = ctrl.loggingManager.SetSenders(senders) } else { ctrl.logger.Info("disabling JSON logging") prevSenders = ctrl.loggingManager.SetSenders(nil) } closeCtx, closeCancel := context.WithTimeout(ctx, 3*time.Second) defer closeCancel() var wg sync.WaitGroup for _, sender := range prevSenders { wg.Go(func() { err := sender.Close(closeCtx) ctrl.logger.Info("log sender closed", zap.Error(err)) }) } wg.Wait() } // MakeLogger creates a logger for a service. func (ctrl *Controller) MakeLogger(serviceName string) (*zap.Logger, error) { logWriter, err := ctrl.loggingManager.ServiceLog(serviceName).Writer() if err != nil { return nil, err } return logging.ZapLogger( logging.NewLogDestination(logWriter, zapcore.DebugLevel, logging.WithColoredLevels(), ), logging.NewLogDestination(logging.StdWriter, ctrl.consoleLogLevel, logging.WithoutTimestamp(), logging.WithoutLogLevels(), logging.WithControllerErrorSuppressor(constants.ConsoleLogErrorSuppressThreshold), ), ).With(logging.Component(serviceName)), nil } ================================================ FILE: internal/app/machined/pkg/runtime/v1alpha2/v1alpha2_state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha2 import ( "context" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" talosconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/files" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/perf" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/security" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" "github.com/siderolabs/talos/pkg/machinery/resources/time" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // State implements runtime.V1alpha2State interface. type State struct { resources state.State namespaceRegistry *registry.NamespaceRegistry resourceRegistry *registry.ResourceRegistry } // NewState creates State. func NewState() (*State, error) { s := &State{} ctx := context.TODO() s.resources = state.WrapCore(namespaced.NewState( func(ns string) state.CoreState { return inmem.NewStateWithOptions( inmem.WithHistoryInitialCapacity(8), inmem.WithHistoryMaxCapacity(1024), inmem.WithHistoryGap(4), )(ns) }, )) s.namespaceRegistry = registry.NewNamespaceRegistry(s.resources) s.resourceRegistry = registry.NewResourceRegistry(s.resources) if err := s.namespaceRegistry.RegisterDefault(ctx); err != nil { return nil, err } if err := s.resourceRegistry.RegisterDefault(ctx); err != nil { return nil, err } // register Talos namespaces for _, ns := range []struct { name string description string }{ {v1alpha1.NamespaceName, "Talos v1alpha1 subsystems glue resources."}, {cluster.NamespaceName, "Cluster configuration and discovery resources."}, {cluster.RawNamespaceName, "Cluster unmerged raw resources."}, {config.NamespaceName, "Talos node configuration."}, {etcd.NamespaceName, "etcd resources."}, {files.NamespaceName, "Files and file-like resources."}, {hardware.NamespaceName, "Hardware resources."}, {k8s.NamespaceName, "Kubernetes all node types resources."}, {k8s.ControlPlaneNamespaceName, "Kubernetes control plane resources."}, {kubespan.NamespaceName, "KubeSpan resources."}, {network.NamespaceName, "Networking resources."}, {network.ConfigNamespaceName, "Networking configuration resources."}, {cri.NamespaceName, "CRI Seccomp resources."}, {secrets.NamespaceName, "Resources with secret material."}, {security.NamespaceName, "Security resources."}, {perf.NamespaceName, "Stats resources."}, } { if err := s.namespaceRegistry.Register(ctx, ns.name, ns.description); err != nil { return nil, err } } // register Talos resources for _, r := range []meta.ResourceWithRD{ &block.Device{}, &block.DiscoveredVolume{}, &block.DiscoveryRefreshRequest{}, &block.DiscoveryRefreshStatus{}, &block.Disk{}, &block.MountRequest{}, &block.MountStatus{}, &block.SwapStatus{}, &block.Symlink{}, &block.SystemDisk{}, &block.UserDiskConfigStatus{}, &block.VolumeConfig{}, &block.VolumeLifecycle{}, &block.VolumeMountRequest{}, &block.VolumeMountStatus{}, &block.VolumeStatus{}, &block.ZswapStatus{}, &cluster.Affiliate{}, &cluster.Config{}, &cluster.Identity{}, &cluster.Info{}, &cluster.Member{}, &config.MachineConfig{}, &config.MachineType{}, &cri.ImageCacheConfig{}, &cri.SeccompProfile{}, &etcd.Config{}, &etcd.PKIStatus{}, &etcd.Spec{}, &etcd.Member{}, &files.EtcFileSpec{}, &files.EtcFileStatus{}, &hardware.MemoryModule{}, &hardware.PCIDevice{}, &hardware.PCIDriverRebindConfig{}, &hardware.PCIDriverRebindStatus{}, &hardware.PCRStatus{}, &hardware.Processor{}, &hardware.SystemInformation{}, &k8s.AdmissionControlConfig{}, &k8s.AuditPolicyConfig{}, &k8s.AuthorizationConfig{}, &k8s.APIServerConfig{}, &k8s.KubePrismEndpoints{}, &k8s.ConfigStatus{}, &k8s.ControllerManagerConfig{}, &k8s.Endpoint{}, &k8s.ExtraManifestsConfig{}, &k8s.KubeletConfig{}, &k8s.KubeletLifecycle{}, &k8s.KubeletSpec{}, &k8s.KubePrismConfig{}, &k8s.KubePrismStatuses{}, &k8s.Manifest{}, &k8s.ManifestStatus{}, &k8s.BootstrapManifestsConfig{}, &k8s.NodeAnnotationSpec{}, &k8s.NodeCordonedSpec{}, &k8s.NodeIP{}, &k8s.NodeIPConfig{}, &k8s.NodeLabelSpec{}, &k8s.Nodename{}, &k8s.NodeStatus{}, &k8s.NodeTaintSpec{}, &k8s.SchedulerConfig{}, &k8s.StaticPod{}, &k8s.StaticPodServerStatus{}, &k8s.StaticPodStatus{}, &k8s.SecretsStatus{}, &kubeaccess.Config{}, &kubespan.Config{}, &kubespan.Endpoint{}, &kubespan.Identity{}, &kubespan.PeerSpec{}, &kubespan.PeerStatus{}, &network.AddressStatus{}, &network.AddressSpec{}, &network.DeviceConfigSpec{}, &network.DNSResolveCache{}, &network.DNSUpstream{}, &network.EthernetSpec{}, &network.EthernetStatus{}, &network.HardwareAddr{}, &network.HostDNSConfig{}, &network.HostnameStatus{}, &network.HostnameSpec{}, &network.LinkAliasSpec{}, &network.LinkRefresh{}, &network.LinkStatus{}, &network.LinkSpec{}, &network.NfTablesChain{}, &network.NodeAddress{}, &network.NodeAddressFilter{}, &network.NodeAddressSortAlgorithm{}, &network.OperatorSpec{}, &network.PlatformConfig{}, &network.ProbeSpec{}, &network.ProbeStatus{}, &network.ResolverStatus{}, &network.ResolverSpec{}, &network.RouteStatus{}, &network.RouteSpec{}, &network.RoutingRuleSpec{}, &network.RoutingRuleStatus{}, &network.Status{}, &network.TimeServerStatus{}, &network.TimeServerSpec{}, &perf.CPU{}, &perf.Memory{}, &cri.RegistriesConfig{}, &runtime.APIServiceConfig{}, &runtime.BootedEntry{}, &runtime.DevicesStatus{}, &runtime.Diagnostic{}, &runtime.Environment{}, &runtime.EventSinkConfig{}, &runtime.ExtensionServiceConfig{}, &runtime.ExtensionServiceConfigStatus{}, &runtime.ExtensionStatus{}, &runtime.KernelCmdline{}, &runtime.KernelModuleSpec{}, &runtime.KernelParamSpec{}, &runtime.KernelParamDefaultSpec{}, &runtime.KernelParamStatus{}, &runtime.KmsgLogConfig{}, &runtime.LoadedKernelModule{}, &runtime.MaintenanceServiceConfig{}, &runtime.MaintenanceServiceRequest{}, &runtime.MachineResetSignal{}, &runtime.MachineStatus{}, &runtime.MetaKey{}, &runtime.MetaLoaded{}, &runtime.MountStatus{}, &runtime.OOMAction{}, &runtime.PlatformMetadata{}, &runtime.SBOMItem{}, &runtime.SecurityState{}, &runtime.UniqueMachineToken{}, &runtime.Version{}, &runtime.WatchdogTimerConfig{}, &runtime.WatchdogTimerStatus{}, &secrets.API{}, &secrets.CertSAN{}, &secrets.EncryptionSalt{}, &secrets.Etcd{}, &secrets.EtcdRoot{}, &secrets.Kubelet{}, &secrets.Kubernetes{}, &secrets.KubernetesDynamicCerts{}, &secrets.KubernetesRoot{}, &secrets.MaintenanceRoot{}, &secrets.OSRoot{}, &secrets.Trustd{}, &security.ImageVerificationRule{}, &security.TUFTrustedRoot{}, &siderolink.Config{}, &siderolink.Status{}, &siderolink.Tunnel{}, &time.AdjtimeStatus{}, &time.Status{}, &v1alpha1.AcquireConfigSpec{}, &v1alpha1.AcquireConfigStatus{}, &v1alpha1.Service{}, } { if err := s.resourceRegistry.Register(ctx, r); err != nil { return nil, err } } return s, nil } // Resources implements runtime.V1alpha2State interface. func (s *State) Resources() state.State { return s.resources } // NamespaceRegistry implements runtime.V1alpha2State interface. func (s *State) NamespaceRegistry() *registry.NamespaceRegistry { return s.namespaceRegistry } // ResourceRegistry implements runtime.V1alpha2State interface. func (s *State) ResourceRegistry() *registry.ResourceRegistry { return s.resourceRegistry } // GetConfig implements runtime.V1alpha2State interface. func (s *State) GetConfig(ctx context.Context) (talosconfig.Provider, error) { cfg, err := safe.ReaderGetByID[*config.MachineConfig](ctx, s.resources, config.ActiveID) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, err } return cfg.Provider(), nil } // SetConfig implements runtime.V1alpha2State interface. func (s *State) SetConfig(ctx context.Context, id string, cfg talosconfig.Provider) error { cfgResource := config.NewMachineConfigWithID(cfg, id) oldCfg, err := s.resources.Get(ctx, cfgResource.Metadata()) if err != nil { if state.IsNotFoundError(err) { return s.resources.Create(ctx, cfgResource) } return err } cfgResource.Metadata().SetVersion(oldCfg.Metadata().Version()) return s.resources.Update(ctx, cfgResource) } ================================================ FILE: internal/app/machined/pkg/startup/cgroups.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package startup import ( "context" "errors" "fmt" "github.com/containerd/cgroups/v3" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/cgroup" "github.com/siderolabs/talos/pkg/machinery/constants" ) // CreateSystemCgroups creates system cgroups. func CreateSystemCgroups(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { // in container mode cgroups mode depends on cgroups provided by the container runtime if !rt.State().Platform().Mode().InContainer() { // assert that cgroupsv2 is being used when running not in container mode, // as Talos sets up cgroupsv2 on its own if cgroups.Mode() != cgroups.Unified { return errors.New("cgroupsv2 should be used") } } // Initialize cgroups root path. if err := cgroup.InitRoot(); err != nil { return fmt.Errorf("error initializing cgroups root path: %w", err) } log.Info("initializing cgroups", zap.String("root", cgroup.Root())) groups := []string{ constants.CgroupInit, constants.CgroupSystem, constants.CgroupPodRuntimeRoot, } for _, c := range groups { _, err := cgroup.CreateCgroup(c) if err != nil { return err } } return next()(ctx, log, rt, next) } ================================================ FILE: internal/app/machined/pkg/startup/startup.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package startup provides machined startup tasks. package startup import ( "context" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" ) // Task is a function that performs a startup task. // // It is supposed to call the next task in the chain. type Task func(context.Context, *zap.Logger, runtime.Runtime, NextTaskFunc) error // NextTaskFunc is a function which returns the next task in the chain. type NextTaskFunc func() Task // RunTasks runs the given tasks in order. func RunTasks(ctx context.Context, log *zap.Logger, rt runtime.Runtime, tasks ...Task) error { var idx int nextTaskFunc := func() Task { idx++ return tasks[idx] } return tasks[0](ctx, log, rt, nextTaskFunc) } // DefaultTasks returns the default startup tasks. func DefaultTasks() []Task { return []Task{ LogMode, MountPseudoLate, SetupSystemDirectories, InitVolumeLifecycle, MountCgroups, SetRLimit, SetEnvironmentVariables, CreateSystemCgroups, } } ================================================ FILE: internal/app/machined/pkg/startup/tasks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package startup import ( "context" "errors" "fmt" "os" "strings" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "go.uber.org/zap" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // LogMode prints the current mode. func LogMode(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { log.Info("platform information", zap.Stringer("mode", rt.State().Platform().Mode())) log.Info("FIPS mode", zap.Bool("enabled", fipsmode.Enabled()), zap.Bool("strict", fipsmode.Strict())) return next()(ctx, log, rt, next) } // SetupSystemDirectories creates system default directories. func SetupSystemDirectories(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { for _, dir := range []struct { path string perm os.FileMode label string }{ {constants.SystemEtcPath, 0o700, constants.EtcSelinuxLabel}, {constants.SystemVarPath, 0o700, constants.SystemVarSelinuxLabel}, {constants.StateMountPoint, 0o700, ""}, {constants.SystemRunPath, 0o751, "system_u:object_r:system_run_t:s0"}, {"/system/run/containerd", 0o711, "system_u:object_r:sys_containerd_run_t:s0"}, {"/run/containerd", 0o711, "system_u:object_r:pod_containerd_run_t:s0"}, } { if err := os.MkdirAll(dir.path, dir.perm); err != nil { return fmt.Errorf("setupSystemDirectories: %w", err) } if dir.label != "" { if err := selinux.SetLabel(dir.path, dir.label); err != nil { return fmt.Errorf("setupSystemDirectories: %w", err) } } } return next()(ctx, log, rt, next) } // InitVolumeLifecycle initializes volume lifecycle resource. func InitVolumeLifecycle(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { if err := rt.State().V1Alpha2().Resources().Create(ctx, block.NewVolumeLifecycle(block.NamespaceName, block.VolumeLifecycleID)); err != nil { return fmt.Errorf("initVolumeLifecycle: %w", err) } return next()(ctx, log, rt, next) } // MountCgroups represents mounts the cgroupfs (only in !container). func MountCgroups(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { if rt.State().Platform().Mode().InContainer() { return next()(ctx, log, rt, next) } if pointer.SafeDeref(procfs.ProcCmdline().Get(constants.KernelParamCGroups).First()) == "0" { log.Warn(fmt.Sprintf("kernel argument %v is no longer supported", constants.KernelParamCGroups)) } cgroup := mount.NewCgroup2() if _, err := cgroup.Mount(); err != nil { return fmt.Errorf("mountCgroups: %w", err) } defer func() { if err := cgroup.Unmount(); err != nil { log.Warn("failed to unmount cgroups", zap.Error(err)) } }() return next()(ctx, log, rt, next) } // MountPseudoLate mounts the late pseudo filesystems (only in !container). func MountPseudoLate(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { if rt.State().Platform().Mode().InContainer() { return next()(ctx, log, rt, next) } late := mount.PseudoLate(log.Sugar().Infof) unmounter, err := late.Mount() if err != nil { return fmt.Errorf("mountPseudoLate: %w", err) } defer func() { if err := unmounter(); err != nil { log.Warn("failed to unmount pseudo late", zap.Error(err)) } }() return next()(ctx, log, rt, next) } // SetRLimit sets the file descriptor limit. func SetRLimit(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { if rt.State().Platform().Mode().InContainer() { return next()(ctx, log, rt, next) } if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &unix.Rlimit{Cur: 1048576, Max: 1048576}); err != nil { return fmt.Errorf("setRLimit: %w", err) } return next()(ctx, log, rt, next) } // SetEnvironmentVariables sets the environment variables. func SetEnvironmentVariables(ctx context.Context, log *zap.Logger, rt runtime.Runtime, next NextTaskFunc) error { // Set the PATH env var. if err := os.Setenv("PATH", constants.PATH); err != nil { return errors.New("error setting PATH") } if !rt.State().Platform().Mode().InContainer() { // in container mode, ignore cmdline for _, env := range environment.Get(nil) { key, val, _ := strings.Cut(env, "=") if err := os.Setenv(key, val); err != nil { return fmt.Errorf("error setting %s: %w", val, err) } } } return next()(ctx, log, rt, next) } ================================================ FILE: internal/app/machined/pkg/system/events/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package events import ( "time" "github.com/siderolabs/gen/xslices" "google.golang.org/protobuf/types/known/timestamppb" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // MaxEventsToKeep is maximum number of events to keep per service before dropping old entries. const MaxEventsToKeep = 64 // ServiceState is enum of service run states. type ServiceState int // ServiceState constants. const ( StateInitialized ServiceState = iota StatePreparing StateWaiting StateRunning StateStopping StateFinished StateFailed StateSkipped StateStarting ) func (state ServiceState) String() string { switch state { case StateInitialized: return "Initialized" case StateStarting: return "Starting" case StatePreparing: return "Preparing" case StateWaiting: return "Waiting" case StateRunning: return "Running" case StateStopping: return "Stopping" case StateFinished: return "Finished" case StateFailed: return "Failed" case StateSkipped: return "Skipped" default: return "Unknown" } } // ServiceEvent describes state change of the running service. type ServiceEvent struct { Message string State ServiceState Health health.Status Timestamp time.Time } // AsProto returns protobuf representation of respective machined event. func (event *ServiceEvent) AsProto(service string) *machineapi.ServiceStateEvent { return &machineapi.ServiceStateEvent{ Service: service, Action: machineapi.ServiceStateEvent_Action(event.State), Message: event.Message, Health: event.Health.AsProto(), } } // ServiceEvents is a fixed length history of events. type ServiceEvents struct { events []ServiceEvent pos int discarded uint } // Push appends new event to the history popping out oldest event on overflow. func (events *ServiceEvents) Push(event ServiceEvent) { if events.events == nil { events.events = make([]ServiceEvent, MaxEventsToKeep) } if events.events[events.pos].Message != "" { // overwriting some entry events.discarded++ } events.events[events.pos] = event events.pos = (events.pos + 1) % len(events.events) } // Get return a copy of event history, with most recent event being the last one. func (events *ServiceEvents) Get(count int) (result []ServiceEvent) { if events.events == nil { return result } if count > MaxEventsToKeep { count = MaxEventsToKeep } n := len(events.events) for i := (events.pos - count + n) % n; count > 0; i = (i + 1) % n { if events.events[i].Message != "" { result = append(result, events.events[i]) } count-- } return result } // AsProto returns protobuf-ready serialized snapshot. func (events *ServiceEvents) AsProto(count int) *machineapi.ServiceEvents { eventList := events.Get(count) fn := func(event ServiceEvent) *machineapi.ServiceEvent { tspb := timestamppb.New(event.Timestamp) return &machineapi.ServiceEvent{ Msg: event.Message, State: event.State.String(), Ts: tspb, } } return &machineapi.ServiceEvents{ Events: xslices.Map(eventList, fn), } } // Recorder adds new event to the history of events, formatting message with args using Sprintf. type Recorder func(newstate ServiceState, message string, args ...any) // NullRecorder discards events. func NullRecorder(newstate ServiceState, message string, args ...any) { } ================================================ FILE: internal/app/machined/pkg/system/events/events_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package events_test import ( "strconv" "testing" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" ) type EventsSuite struct { suite.Suite } func (suite *EventsSuite) assertEvents(expectedMessages []string, evs []events.ServiceEvent) { messages := xslices.Map(evs, func(ev events.ServiceEvent) string { return ev.Message }) suite.Assert().Equal(expectedMessages, messages) } func (suite *EventsSuite) TestEmpty() { var e events.ServiceEvents suite.Assert().Equal([]events.ServiceEvent(nil), e.Get(100)) } func (suite *EventsSuite) TestSome() { var e events.ServiceEvents for i := range 5 { e.Push(events.ServiceEvent{ Message: strconv.Itoa(i), }) } suite.Assert().Equal([]events.ServiceEvent(nil), e.Get(0)) suite.assertEvents([]string{"4"}, e.Get(1)) suite.assertEvents([]string{"1", "2", "3", "4"}, e.Get(4)) suite.assertEvents([]string{"0", "1", "2", "3", "4"}, e.Get(5)) suite.assertEvents([]string{"0", "1", "2", "3", "4"}, e.Get(6)) suite.assertEvents([]string{"0", "1", "2", "3", "4"}, e.Get(100)) protoEvents := e.AsProto(1) suite.Assert().Len(protoEvents.Events, 1) suite.Assert().Equal("4", protoEvents.Events[0].Msg) suite.Assert().Equal("Initialized", protoEvents.Events[0].State) } func (suite *EventsSuite) TestOverflow() { var e events.ServiceEvents numEvents := events.MaxEventsToKeep*2 + 3 for i := range numEvents { e.Push(events.ServiceEvent{ Message: strconv.Itoa(i), }) } suite.Assert().Equal([]events.ServiceEvent(nil), e.Get(0)) suite.assertEvents([]string{strconv.Itoa(numEvents - 1)}, e.Get(1)) var expected []string for i := numEvents - events.MaxEventsToKeep; i < numEvents; i++ { expected = append(expected, strconv.Itoa(i)) } suite.assertEvents(expected, e.Get(events.MaxEventsToKeep*10)) suite.assertEvents(expected[len(expected)-3:], e.Get(3)) } func TestEventsSuite(t *testing.T) { suite.Run(t, new(EventsSuite)) } ================================================ FILE: internal/app/machined/pkg/system/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/conditions" ) func NewServices(runtime runtime.Runtime) *singleton { //nolint:revive return newServices(runtime) } func WaitForServiceWithInstance(instance *singleton, event StateEvent, service string) conditions.Condition { return waitForService(instance, event, service) } ================================================ FILE: internal/app/machined/pkg/system/health/check.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package health import ( "context" "time" ) // Check runs the health check under given context. // // Healthcheck is considered successful when func returns no error. // Func should terminate when context is canceled. type Check func(ctx context.Context) error // Run the health check publishing the results to state. // // Run aborts when context is canceled. func Run(ctx context.Context, settings *Settings, state *State, check Check) error { state.Init() select { case <-ctx.Done(): return ctx.Err() case <-time.After(settings.InitialDelay): } ticker := time.NewTicker(settings.Period) defer ticker.Stop() var ( err error healthy bool message string checkCtx context.Context checkCtxCancel context.CancelFunc ) for { err = func() error { checkCtx, checkCtxCancel = context.WithTimeout(ctx, settings.Timeout) //nolint:fatcontext defer checkCtxCancel() return check(checkCtx) }() healthy = err == nil message = "" if !healthy { message = err.Error() } state.Update(healthy, message) select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } } } ================================================ FILE: internal/app/machined/pkg/system/health/health_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package health_test import ( "context" "errors" "sync/atomic" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" ) type CheckSuite struct { suite.Suite } func (suite *CheckSuite) TestHealthy() { settings := health.Settings{ InitialDelay: time.Millisecond, Period: 10 * time.Millisecond, Timeout: time.Millisecond, } var called uint32 //nolint:unparam check := func(context.Context) error { atomic.AddUint32(&called, 1) return nil } var state health.State errCh := make(chan error) ctx, ctxCancel := context.WithCancel(context.Background()) go func() { errCh <- health.Run(ctx, &settings, &state, check) }() for range 20 { time.Sleep(10 * time.Millisecond) if atomic.LoadUint32(&called) > 2 { break } } ctxCancel() suite.Assert().EqualError(<-errCh, context.Canceled.Error()) suite.Assert().True(called > 2) protoHealth := state.AsProto() suite.Assert().False(protoHealth.Unknown) suite.Assert().True(protoHealth.Healthy) suite.Assert().Equal("", protoHealth.LastMessage) } func (suite *CheckSuite) TestHealthChange() { settings := health.Settings{ InitialDelay: time.Millisecond, Period: time.Millisecond, Timeout: time.Millisecond, } var healthy atomic.Uint32 check := func(context.Context) error { if healthy.Load() == 1 { return nil } return errors.New("health failed") } var state health.State notifyCh := make(chan health.StateChange, 2) state.Subscribe(notifyCh) errCh := make(chan error) ctx, ctxCancel := context.WithCancel(context.Background()) go func() { errCh <- health.Run(ctx, &settings, &state, check) }() // wait for the first health change for range 20 { if state.Get().Healthy != nil { break } time.Sleep(50 * time.Millisecond) } suite.Require().False(*state.Get().Healthy) suite.Require().Equal("health failed", state.Get().LastMessage) healthy.Store(1) for range 10 { time.Sleep(20 * time.Millisecond) if *state.Get().Healthy { break } } suite.Require().True(*state.Get().Healthy) suite.Require().Equal("", state.Get().LastMessage) ctxCancel() suite.Assert().EqualError(<-errCh, context.Canceled.Error()) state.Unsubscribe(notifyCh) close(notifyCh) change := <-notifyCh suite.Assert().Nil(change.Old.Healthy) suite.Assert().False(*change.New.Healthy) change = <-notifyCh suite.Assert().False(*change.Old.Healthy) suite.Assert().True(*change.New.Healthy) } func (suite *CheckSuite) TestCheckAbort() { settings := health.Settings{ InitialDelay: time.Millisecond, Period: time.Millisecond, Timeout: time.Millisecond, } check := func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case <-time.After(50 * time.Second): // should never be triggered, as Timeout is 1ms return nil } } var state health.State errCh := make(chan error) ctx, ctxCancel := context.WithCancel(context.Background()) go func() { errCh <- health.Run(ctx, &settings, &state, check) }() // wait for the first health change for range 20 { if state.Get().Healthy != nil { break } time.Sleep(50 * time.Millisecond) } suite.Require().False(*state.Get().Healthy) suite.Require().Equal("context deadline exceeded", state.Get().LastMessage) ctxCancel() suite.Assert().EqualError(<-errCh, context.Canceled.Error()) } func (suite *CheckSuite) TestInitialState() { settings := health.Settings{ InitialDelay: 5 * time.Minute, } var state health.State ctx, ctxCancel := context.WithCancel(context.Background()) errCh := make(chan error) go func() { errCh <- health.Run(ctx, &settings, &state, nil) }() time.Sleep(100 * time.Millisecond) suite.Require().Nil(state.Get().Healthy) suite.Require().Equal("Unknown", state.Get().LastMessage) ctxCancel() suite.Assert().EqualError(<-errCh, context.Canceled.Error()) } func TestCheckSuite(t *testing.T) { suite.Run(t, new(CheckSuite)) } ================================================ FILE: internal/app/machined/pkg/system/health/settings.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package health import "time" // Settings configures health check // // Fields are similar to k8s pod probe definitions. type Settings struct { InitialDelay time.Duration Period time.Duration Timeout time.Duration } // DefaultSettings provides some default health check settings. var DefaultSettings = Settings{ InitialDelay: time.Second, Period: 5 * time.Second, Timeout: 500 * time.Millisecond, } ================================================ FILE: internal/app/machined/pkg/system/health/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package health import ( "slices" "sync" "time" "google.golang.org/protobuf/types/known/timestamppb" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // Status of the healthcheck. type Status struct { Healthy *bool LastChange time.Time LastMessage string } // AsProto returns protobuf-ready health state. func (status *Status) AsProto() *machineapi.ServiceHealth { tspb := timestamppb.New(status.LastChange) return &machineapi.ServiceHealth{ Unknown: status.Healthy == nil, Healthy: status.Healthy != nil && *status.Healthy, LastMessage: status.LastMessage, LastChange: tspb, } } // StateChange is used to notify about status changes. type StateChange struct { Old Status New Status } // State provides proper locking around health state. type State struct { sync.Mutex status Status subscribers []chan<- StateChange } // Update health status (locked). func (state *State) Update(healthy bool, message string) { state.Lock() oldStatus := state.status notify := false if state.status.Healthy == nil || *state.status.Healthy != healthy { notify = true state.status.Healthy = &healthy state.status.LastChange = time.Now() } state.status.LastMessage = message newStatus := state.status var subscribers []chan<- StateChange if notify { subscribers = slices.Clone(state.subscribers) } state.Unlock() if notify { for _, ch := range subscribers { select { case ch <- StateChange{oldStatus, newStatus}: default: // drop messages to clients which don't consume them } } } } // Subscribe for the notifications on state changes. func (state *State) Subscribe(ch chan<- StateChange) { state.Lock() defer state.Unlock() state.subscribers = append(state.subscribers, ch) } // Unsubscribe from state changes. func (state *State) Unsubscribe(ch chan<- StateChange) { state.Lock() defer state.Unlock() for i := 0; i < len(state.subscribers); { if state.subscribers[i] == ch { state.subscribers[i] = state.subscribers[len(state.subscribers)-1] state.subscribers[len(state.subscribers)-1] = nil state.subscribers = state.subscribers[:len(state.subscribers)-1] } else { i++ } } } // Init health status (locked). func (state *State) Init() { state.Lock() defer state.Unlock() state.status.LastMessage = "Unknown" state.status.LastChange = time.Now() state.status.Healthy = nil } // Get returns health status (locked). func (state *State) Get() Status { state.Lock() defer state.Unlock() return state.status } // AsProto returns protobuf-ready health state. func (state *State) AsProto() *machineapi.ServiceHealth { status := state.Get() return status.AsProto() } ================================================ FILE: internal/app/machined/pkg/system/integration_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system_test import ( "context" "io" "testing" "time" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/pkg/conditions" ) type TestCondition struct{} func (TestCondition) String() string { return "test-condition" } func (TestCondition) Wait(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case <-time.After(10 * time.Millisecond): return nil } } type TestService struct{} func (TestService) ID(runtime.Runtime) string { return "test-service" } func (TestService) PreFunc(ctx context.Context, r runtime.Runtime) error { select { case <-ctx.Done(): return ctx.Err() default: return nil } } func (TestService) Runner(r runtime.Runtime) (runner.Runner, error) { return goroutine.NewRunner(r, "test-service", func(ctx context.Context, r runtime.Runtime, logOutput io.Writer) error { <-ctx.Done() return nil }), nil } func (TestService) PostFunc(runtime.Runtime, events.ServiceState) error { return nil } func (TestService) Condition(runtime.Runtime) conditions.Condition { return TestCondition{} } func (TestService) DependsOn(runtime.Runtime) []string { return nil } func (TestService) Volumes(runtime.Runtime) []string { return nil } func TestRestartService(t *testing.T) { deadline, ok := t.Deadline() if !ok { deadline = time.Now().Add(15 * time.Second) } ctx, cancel := context.WithDeadline(t.Context(), deadline) defer cancel() services := system.NewServices(nil) services.Load(TestService{}) for range 100 { require.NoError(t, services.Start("test-service")) require.NoError(t, system.WaitForServiceWithInstance(services, system.StateEventUp, "test-service").Wait(ctx)) require.NoError(t, services.Stop(ctx, "test-service")) } } ================================================ FILE: internal/app/machined/pkg/system/mocks_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system_test import ( "context" "errors" "sync/atomic" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/pkg/conditions" ) type MockService struct { name string preError error runnerError error nilRunner bool runner runner.Runner condition conditions.Condition postError error dependencies []string } func (m *MockService) ID(runtime.Runtime) string { if m.name != "" { return m.name } return "MockRunner" } func (m *MockService) PreFunc(context.Context, runtime.Runtime) error { return m.preError } func (m *MockService) Runner(runtime.Runtime) (runner.Runner, error) { if m.runner != nil { return m.runner, m.runnerError } if m.nilRunner { return nil, nil } return &MockRunner{exitCh: make(chan error)}, m.runnerError } func (m *MockService) PostFunc(runtime.Runtime, events.ServiceState) error { return m.postError } func (m *MockService) Condition(runtime.Runtime) conditions.Condition { return m.condition } func (m *MockService) DependsOn(runtime.Runtime) []string { return m.dependencies } func (m *MockService) Volumes(runtime.Runtime) []string { return nil } type MockHealthcheckedService struct { MockService notHealthy atomic.Uint32 } func (m *MockHealthcheckedService) SetHealthy(healthy bool) { if healthy { m.notHealthy.Store(0) } else { m.notHealthy.Store(1) } } func (m *MockHealthcheckedService) HealthFunc(runtime.Runtime) health.Check { return func(context.Context) error { if m.notHealthy.Load() == 0 { return nil } return errors.New("not healthy") } } func (m *MockHealthcheckedService) HealthSettings(runtime.Runtime) *health.Settings { return &health.Settings{ InitialDelay: time.Millisecond, Timeout: time.Second, Period: time.Millisecond, } } type MockRunner struct { exitCh chan error } func (m *MockRunner) Open() error { return nil } func (m *MockRunner) Close() error { return nil } func (m *MockRunner) Run(eventSink events.Recorder) error { eventSink(events.StateRunning, "Running") return <-m.exitCh } func (m *MockRunner) Stop() error { close(m.exitCh) return nil } func (m *MockRunner) String() string { return "MockRunner()" } type MockCondition struct { done chan struct{} desc string } func NewMockCondition(desc string) *MockCondition { return &MockCondition{ done: make(chan struct{}), desc: desc, } } func (m *MockCondition) String() string { return m.desc } func (m *MockCondition) Wait(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case <-m.done: return nil } } ================================================ FILE: internal/app/machined/pkg/system/runner/containerd/containerd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd import ( "bytes" "context" "errors" "fmt" "io" "io/fs" "log" "syscall" "time" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/contrib/seccomp" "github.com/containerd/containerd/v2/pkg/cio" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/errdefs" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/internal/lastlog" "github.com/siderolabs/talos/internal/pkg/cgroup" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" ) // containerdRunner is a runner.Runner that runs container in containerd. type containerdRunner struct { args *runner.Args opts *runner.Options logToConsole bool stop chan struct{} stopped chan struct{} client *containerd.Client ctx context.Context //nolint:containedctx container containerd.Container stdinCloser *StdinCloser } // NewRunner creates runner.Runner that runs a container in containerd. func NewRunner(logToConsole bool, args *runner.Args, setters ...runner.Option) runner.Runner { r := &containerdRunner{ args: args, opts: runner.DefaultOptions(), logToConsole: logToConsole, stop: make(chan struct{}), stopped: make(chan struct{}), } for _, setter := range setters { setter(r.opts) } return r } // Open implements the Runner interface. func (c *containerdRunner) Open() error { // Create the containerd client. var err error c.ctx = namespaces.WithNamespace(context.Background(), c.opts.Namespace) c.client, err = containerd.New(c.opts.ContainerdAddress) if err != nil { return err } var image containerd.Image if c.opts.ContainerImage != "" { image, err = c.client.GetImage(c.ctx, c.opts.ContainerImage) if err != nil { return err } } // See if there's previous container/snapshot to clean up var oldcontainer containerd.Container if oldcontainer, err = c.client.LoadContainer(c.ctx, c.args.ID); err == nil { if err = oldcontainer.Delete(c.ctx, containerd.WithSnapshotCleanup); err != nil { return fmt.Errorf("error deleting old container instance: %w", err) } } if err = c.client.SnapshotService("").Remove(c.ctx, c.args.ID); err != nil && !errdefs.IsNotFound(err) { return fmt.Errorf("error cleaning up stale snapshot: %w", err) } // Create the container. specOpts := c.newOCISpecOpts(image) containerOpts := c.newContainerOpts(image, specOpts) c.container, err = c.client.NewContainer( c.ctx, c.args.ID, containerOpts..., ) if err != nil { return fmt.Errorf("failed to create container %q: %w", c.args.ID, err) } return nil } // Close implements runner.Runner interface. func (c *containerdRunner) Close() error { if c.container != nil { err := c.container.Delete(c.ctx, containerd.WithSnapshotCleanup) if err != nil { return err } } if c.client == nil { return nil } return c.client.Close() } // Run implements runner.Runner interface // //nolint:gocyclo,cyclop func (c *containerdRunner) Run(eventSink events.Recorder) error { defer close(c.stopped) var ( task containerd.Task logW io.WriteCloser err error ) // attempt to clean up a task if it already exists task, err = c.container.Task(c.ctx, nil) if err == nil { var s <-chan containerd.ExitStatus s, err = task.Wait(c.ctx) if err != nil { return fmt.Errorf("failed to wait for the task %q: %w", c.args.ID, err) } err = task.Kill(c.ctx, syscall.SIGKILL, containerd.WithKillAll) if err != nil && !errdefs.IsNotFound(err) { return fmt.Errorf("failed to kill the task %q: %w", c.args.ID, err) } select { case <-s: case <-c.stop: return nil } if _, err = task.Delete(c.ctx); err != nil { return fmt.Errorf("failed to clean up task %q: %w", c.args.ID, err) } } logW, err = c.opts.LoggingManager.ServiceLog(c.args.ID).Writer() if err != nil { return fmt.Errorf("error creating log: %w", err) } cg, err := cgroup.CreateCgroup(c.opts.CgroupPath) if err != nil { return fmt.Errorf("error creating cgroup: %w", err) } // If the task is not cleaned up by containerd or another error // happens during the lifecycle, remove the cgroup before exiting // if one still exists defer func() { err := cg.Delete() if err != nil && !errors.Is(err, fs.ErrNotExist) { eventSink(events.StateStopping, "Failed to remove cgroup for %s, %s", c, err) } }() defer logW.Close() //nolint:errcheck var ( lastLog lastlog.Writer w = io.MultiWriter(logW, &lastLog) ) if c.logToConsole { w = io.MultiWriter(w, log.Writer()) } r, err := c.StdinReader() if err != nil { return fmt.Errorf("failed to create stdin reader: %w", err) } creator := cio.NewCreator(cio.WithStreams(r, w, w)) // Create the task and start it. task, err = c.container.NewTask(c.ctx, creator) if err != nil { return fmt.Errorf("failed to create task: %q: %w", c.args.ID, err) } if r != nil { // See https://github.com/containerd/containerd/issues/4489. go c.stdinCloser.WaitAndClose(c.ctx, task) } defer task.Delete(c.ctx) //nolint:errcheck if err = task.Start(c.ctx); err != nil { return fmt.Errorf("failed to start task: %q: %w", c.args.ID, err) } eventSink(events.StateRunning, "Started task %s (PID %d) for container %s", task.ID(), task.Pid(), c.container.ID()) statusC, err := task.Wait(c.ctx) if err != nil { return fmt.Errorf("failed waiting for task: %q: %w", c.args.ID, err) } select { case status := <-statusC: code := status.ExitCode() if code != 0 { return fmt.Errorf("task %q failed: exit code %d (last log %q)", c.args.ID, code, lastLog.GetLastLog()) } return nil case <-c.stop: // graceful stop the task eventSink( events.StateStopping, "Sending SIGTERM to task %s (PID %d, container %s)", task.ID(), task.Pid(), c.container.ID(), ) if err = task.Kill(c.ctx, syscall.SIGTERM, containerd.WithKillAll); err != nil { return fmt.Errorf("error sending SIGTERM: %w", err) } } select { case <-statusC: // stopped process exited return nil case <-time.After(c.opts.GracefulShutdownTimeout): // kill the process eventSink( events.StateStopping, "Sending SIGKILL to task %s (PID %d, container %s)", task.ID(), task.Pid(), c.container.ID(), ) if err = task.Kill(c.ctx, syscall.SIGKILL, containerd.WithKillAll); err != nil { return fmt.Errorf("error sending SIGKILL: %w", err) } } <-statusC return logW.Close() } // Stop implements runner.Runner interface. func (c *containerdRunner) Stop() error { close(c.stop) <-c.stopped c.stop = make(chan struct{}) c.stopped = make(chan struct{}) return nil } func (c *containerdRunner) newContainerOpts( image containerd.Image, specOpts []oci.SpecOpts, ) []containerd.NewContainerOpts { var containerOpts []containerd.NewContainerOpts if image != nil { containerOpts = append( containerOpts, containerd.WithImage(image), containerd.WithNewSnapshot(c.args.ID, image), ) } containerOpts = append( containerOpts, containerd.WithNewSpec(specOpts...), ) containerOpts = append( containerOpts, c.opts.ContainerOpts..., ) return containerOpts } func (c *containerdRunner) newOCISpecOpts(image oci.Image) []oci.SpecOpts { var specOpts []oci.SpecOpts if image != nil { specOpts = append( specOpts, oci.WithImageConfig(image), ) } specOpts = append( specOpts, oci.WithProcessArgs(c.args.ProcessArgs...), oci.WithEnv(c.opts.Env), oci.WithHostHostsFile, oci.WithHostResolvconf, oci.WithNoNewPrivileges, ) if c.opts.OOMScoreAdj != 0 { specOpts = append( specOpts, WithOOMScoreAdj(c.opts.OOMScoreAdj), ) } if c.opts.CgroupPath != "" { specOpts = append( specOpts, oci.WithCgroup(cgroup.Path(c.opts.CgroupPath)), ) } specOpts = append( specOpts, c.opts.OCISpecOpts..., ) if c.opts.OverrideSeccompProfile != nil { specOpts = append( specOpts, WithCustomSeccompProfile(c.opts.OverrideSeccompProfile), ) } else { specOpts = append( specOpts, seccomp.WithDefaultProfile(), // add seccomp profile last, as it depends on process capabilities ) } if selinux.IsEnabled() { if c.opts.SelinuxLabel != "" { specOpts = append( specOpts, oci.WithSelinuxLabel(c.opts.SelinuxLabel), ) } else { specOpts = append( specOpts, oci.WithSelinuxLabel(constants.SelinuxLabelUnconfinedSysContainer), ) } } return specOpts } func (c *containerdRunner) String() string { return fmt.Sprintf("Containerd(%v)", c.args.ID) } func (c *containerdRunner) StdinReader() (io.Reader, error) { if c.opts.Stdin == nil { return nil, nil } if _, err := c.opts.Stdin.Seek(0, 0); err != nil { return nil, err } // copy the input buffer as containerd API seems to be buggy: // * if the task fails to start, IO loop is not stopped properly, so after a restart there are two goroutines concurrently reading from stdin contents, err := io.ReadAll(c.opts.Stdin) if err != nil { return nil, err } c.stdinCloser = &StdinCloser{ Stdin: bytes.NewReader(contents), Closer: make(chan struct{}), } return c.stdinCloser, nil } ================================================ FILE: internal/app/machined/pkg/system/runner/containerd/containerd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd_test import ( "bytes" "context" "encoding/hex" "fmt" "io" "log" "os" "path/filepath" "sync" "testing" "time" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/google/uuid" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" containerdrunner "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( busyboxImage = "docker.io/library/busybox:latest" ) func MockEventSink(state events.ServiceState, message string, args ...any) { log.Printf("state %s: %s", state, fmt.Sprintf(message, args...)) } type ContainerdSuite struct { suite.Suite tmpDir string loggingManager runtime.LoggingManager containerdNamespace string containerdRunner runner.Runner containerdWg sync.WaitGroup containerdAddress string containerID string client *containerd.Client image containerd.Image } func (suite *ContainerdSuite) SetupSuite() { var err error suite.tmpDir = suite.T().TempDir() suite.loggingManager = logging.NewFileLoggingManager(suite.tmpDir) stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root") suite.Require().NoError(os.Mkdir(stateDir, 0o777)) suite.Require().NoError(os.Mkdir(rootDir, 0o777)) if cgroups.Mode() == cgroups.Unified { var manager *cgroup2.Manager manager, err = cgroup2.NewManager(constants.CgroupMountPath, "/"+suite.T().Name(), &cgroup2.Resources{}) suite.Require().NoError(err) // when using buildkit runner, parent `cgroup.type` is set to `domain threaded`, so child cgroups have to explicitly specify // `cgroup.type` to "threaded" https://www.kernel.org/doc/html/v5.0/admin-guide/cgroup-v2.html#threads suite.Require().NoError(os.WriteFile(filepath.Join(constants.CgroupMountPath, suite.T().Name(), "cgroup.type"), []byte("threaded"), 0o644)) defer manager.Delete() //nolint:errcheck } else { var manager cgroup1.Cgroup manager, err = cgroup1.New(cgroup1.NestedPath(suite.tmpDir), &specs.LinuxResources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock") args := &runner.Args{ ID: "containerd", ProcessArgs: []string{ "/bin/containerd", "--address", suite.containerdAddress, "--state", stateDir, "--root", rootDir, "--config", constants.CRIContainerdConfig, }, } suite.containerdRunner = process.NewRunner( false, args, runner.WithLoggingManager(suite.loggingManager), runner.WithEnv([]string{constants.EnvPathWithBin}), runner.WithCgroupPath("/"+suite.T().Name()), ) suite.Require().NoError(suite.containerdRunner.Open()) suite.containerdWg.Go(func() { defer suite.containerdRunner.Close() //nolint:errcheck suite.containerdRunner.Run(MockEventSink) //nolint:errcheck }) suite.client, err = containerd.New(suite.containerdAddress) suite.Require().NoError(err) namespace := ([16]byte)(uuid.New()) suite.containerdNamespace = "talos" + hex.EncodeToString(namespace[:]) ctx := namespaces.WithNamespace(context.Background(), suite.containerdNamespace) suite.image, err = suite.client.Pull(ctx, busyboxImage, containerd.WithPullUnpack) suite.Require().NoError(err) } func (suite *ContainerdSuite) SetupTest() { suite.containerID = uuid.New().String() } func (suite *ContainerdSuite) TearDownSuite() { suite.Require().NoError(suite.client.Close()) suite.Require().NoError(suite.containerdRunner.Stop()) suite.containerdWg.Wait() } func (suite *ContainerdSuite) getLogContents(filename string) []byte { logFile, err := os.Open(filepath.Join(suite.tmpDir, filename)) suite.Assert().NoError(err) //nolint:errcheck defer logFile.Close() logContents, err := io.ReadAll(logFile) suite.Assert().NoError(err) return logContents } func (suite *ContainerdSuite) TestRunSuccess() { r := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "exit 0"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink)) // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) } func (suite *ContainerdSuite) TestRunTwice() { r := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "exit 0"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() // running same container twice should be fine // (checks that containerd state is cleaned up properly) for i := range 2 { suite.Assert().NoError(r.Run(MockEventSink)) // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) if i == 0 { // wait a bit to let containerd clean up the state time.Sleep(100 * time.Millisecond) } } } func (suite *ContainerdSuite) TestContainerCleanup() { // create two runners with the same container ID // // open first runner, but don't close it; second runner should be // able to start the container by cleaning up container created by the first // runner r1 := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "exit 1"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r1.Open()) r2 := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "exit 0"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r2.Open()) defer func() { suite.Assert().NoError(r2.Close()) }() suite.Assert().NoError(r2.Run(MockEventSink)) // calling stop when Run has finished is no-op suite.Assert().NoError(r2.Stop()) } func (suite *ContainerdSuite) TestRunLogs() { r := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "echo -n \"Test 1\nTest 2\n\""}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink)) logFile, err := os.Open(filepath.Join(suite.tmpDir, suite.containerID+".log")) suite.Assert().NoError(err) //nolint:errcheck defer logFile.Close() logContents, err := io.ReadAll(logFile) suite.Assert().NoError(err) suite.Assert().Equal([]byte("Test 1\nTest 2\n"), logContents) } func (suite *ContainerdSuite) TestStopFailingAndRestarting() { testDir := filepath.Join(suite.tmpDir, "test") suite.Assert().NoError(os.Mkdir(testDir, 0o770)) testFile := filepath.Join(testDir, "talos-test") //nolint:errcheck _ = os.Remove(testFile) r := restart.New(containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "test -f " + testFile + " && echo ok || (echo fail; false)"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithOCISpecOpts( oci.WithMounts([]specs.Mount{ {Type: "bind", Destination: testDir, Source: testDir, Options: []string{"bind", "ro"}}, }), ), runner.WithContainerdAddress(suite.containerdAddress), ), restart.WithType(restart.Forever), restart.WithRestartInterval(5*time.Millisecond), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink) }() for range 10 { time.Sleep(500 * time.Millisecond) if bytes.Contains(suite.getLogContents(suite.containerID+".log"), []byte("fail\n")) { break } } select { case err := <-done: suite.Assert().Failf("task should be running", "error: %s", err) return default: } f, err := os.Create(testFile) suite.Assert().NoError(err) suite.Assert().NoError(f.Close()) for range 10 { time.Sleep(500 * time.Millisecond) if bytes.Contains(suite.getLogContents(suite.containerID+".log"), []byte("ok\n")) { break } } select { case err = <-done: suite.Assert().Failf("task should be running", "error: %s", err) return default: } suite.Assert().NoError(r.Stop()) <-done logContents := suite.getLogContents(suite.containerID + ".log") suite.Assert().Truef(bytes.Contains(logContents, []byte("ok\n")), "logContents doesn't contain success entry: %v", logContents) suite.Assert().Truef(bytes.Contains(logContents, []byte("fail\n")), "logContents doesn't contain fail entry: %v", logContents) } func (suite *ContainerdSuite) TestStopSigKill() { if cgroups.Mode() == cgroups.Unified { suite.T().Skip("test doesn't pass under cgroupsv2") } r := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "trap -- '' SIGTERM; while :; do :; done"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithGracefulShutdownTimeout(10*time.Millisecond), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink) }() time.Sleep(50 * time.Millisecond) select { case <-done: suite.Assert().Fail("container should be still running") default: } time.Sleep(100 * time.Millisecond) suite.Assert().NoError(r.Stop()) suite.Assert().NoError(<-done) } func (suite *ContainerdSuite) TestContainerStdin() { stdin := bytes.Repeat([]byte{0xde, 0xad, 0xbe, 0xef}, 2000) r := containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/cat"}, }, runner.WithStdin(bytes.NewReader(stdin)), runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), ) suite.Require().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink)) logFile, err := os.Open(filepath.Join(suite.tmpDir, suite.containerID+".log")) suite.Assert().NoError(err) //nolint:errcheck defer logFile.Close() logContents, err := io.ReadAll(logFile) suite.Assert().NoError(err) suite.Assert().Equal(stdin, logContents) } func TestContainerdSuite(t *testing.T) { if os.Getuid() != 0 { t.Skip("can't run the test as non-root") } _, err := os.Stat("/bin/containerd") if err != nil { t.Skip("containerd binary is not available, skipping the test") } suite.Run(t, new(ContainerdSuite)) } ================================================ FILE: internal/app/machined/pkg/system/runner/containerd/opts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd import ( "context" "github.com/containerd/containerd/v2/contrib/seccomp" "github.com/containerd/containerd/v2/core/containers" "github.com/containerd/containerd/v2/pkg/oci" specs "github.com/opencontainers/runtime-spec/specs-go" ) // WithRootfsPropagation sets the root filesystem propagation. func WithRootfsPropagation(rp string) oci.SpecOpts { return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { s.Linux.RootfsPropagation = rp return nil } } // WithOOMScoreAdj sets the oom score. func WithOOMScoreAdj(score int) oci.SpecOpts { return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { s.Process.OOMScoreAdj = &score return nil } } // WithCustomSeccompProfile allows to override default seccomp profile. func WithCustomSeccompProfile(override func(*specs.LinuxSeccomp)) oci.SpecOpts { return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { s.Linux.Seccomp = seccomp.DefaultProfile(s) override(s.Linux.Seccomp) return nil } } ================================================ FILE: internal/app/machined/pkg/system/runner/containerd/stdin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd import ( "context" "io" containerd "github.com/containerd/containerd/v2/client" ) // StdinCloser wraps io.Reader providing a signal when reader is read till EOF. type StdinCloser struct { Stdin io.Reader Closer chan struct{} } func (s *StdinCloser) Read(p []byte) (int, error) { n, err := s.Stdin.Read(p) if err == io.EOF { close(s.Closer) } return n, err } // WaitAndClose closes containerd task stdin when StdinCloser is exhausted. func (s *StdinCloser) WaitAndClose(ctx context.Context, task containerd.Task) { select { case <-ctx.Done(): return case <-s.Closer: //nolint:errcheck task.CloseIO(ctx, containerd.WithStdinCloser) } } ================================================ FILE: internal/app/machined/pkg/system/runner/goroutine/goroutine.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package goroutine import ( "context" "errors" "fmt" "io" stdlibruntime "runtime" "sync" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" ) // ErrAborted is returned by the service when it's aborted (doesn't stop on timeout). var ErrAborted = errors.New("service aborted") // goroutineRunner is a runner.Runner that runs a service in a goroutine. type goroutineRunner struct { main FuncMain id string runtime runtime.Runtime opts *runner.Options ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc wg sync.WaitGroup } // FuncMain is a entrypoint into the service. // // Service should abort and return when ctx is canceled. type FuncMain func(ctx context.Context, r runtime.Runtime, logOutput io.Writer) error // NewRunner creates runner.Runner that runs a service as goroutine. func NewRunner(r runtime.Runtime, id string, main FuncMain, setters ...runner.Option) runner.Runner { run := &goroutineRunner{ id: id, runtime: r, main: main, opts: runner.DefaultOptions(), } run.ctx, run.ctxCancel = context.WithCancel(context.Background()) for _, setter := range setters { setter(run.opts) } return run } // Open implements the Runner interface. func (r *goroutineRunner) Open() error { return nil } // Run implements the Runner interface. func (r *goroutineRunner) Run(eventSink events.Recorder) error { r.wg.Add(1) defer r.wg.Done() eventSink(events.StateRunning, "Service started as goroutine") errCh := make(chan error) ctx := r.ctx go func() { errCh <- r.wrappedMain(ctx) }() select { case <-r.ctx.Done(): eventSink(events.StateStopping, "Service stopping") case err := <-errCh: // service finished on its own return err } select { case <-time.After(r.opts.GracefulShutdownTimeout * 2): eventSink(events.StateStopping, "Service hasn't stopped gracefully on timeout, aborting") return ErrAborted case err := <-errCh: return err } } func (r *goroutineRunner) wrappedMain(ctx context.Context) (err error) { defer func() { if r := recover(); r != nil { buf := make([]byte, 8192) n := stdlibruntime.Stack(buf, false) err = fmt.Errorf("panic in service: %v\n%s", r, string(buf[:n])) } }() w, err := r.opts.LoggingManager.ServiceLog(r.id).Writer() if err != nil { return fmt.Errorf("service log handler: %w", err) } writerCloser := sync.OnceValue(w.Close) defer writerCloser() //nolint:errcheck if err = r.main(ctx, r.runtime, w); !errors.Is(err, context.Canceled) { return err // return error if it's not context.Canceled (service was not aborted) } return writerCloser() } // Stop implements the Runner interface. func (r *goroutineRunner) Stop() error { r.ctxCancel() r.wg.Wait() r.ctx, r.ctxCancel = context.WithCancel(context.Background()) return nil } // Close implements the Runner interface. func (r *goroutineRunner) Close() error { return nil } func (r *goroutineRunner) String() string { return fmt.Sprintf("Goroutine(%q)", r.id) } ================================================ FILE: internal/app/machined/pkg/system/runner/goroutine/goroutine_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package goroutine_test import ( "context" "errors" "fmt" "io" "log" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/pkg/machinery/config/container" v1alpha1cfg "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func MockEventSink(state events.ServiceState, message string, args ...any) { log.Printf("state %s: %s", state, fmt.Sprintf(message, args...)) } type GoroutineSuite struct { suite.Suite r runtime.Runtime tmpDir string loggingManager runtime.LoggingManager } func (suite *GoroutineSuite) SetupSuite() { var err error suite.tmpDir = suite.T().TempDir() suite.loggingManager = logging.NewFileLoggingManager(suite.tmpDir) s, err := v1alpha1.NewState() suite.Require().NoError(err) cfg, err := container.New(&v1alpha1cfg.Config{}) suite.Require().NoError(err) e := v1alpha1.NewEvents(100, 10) r := v1alpha1.NewRuntime(s, e, suite.loggingManager) suite.Require().NoError(r.SetConfig(cfg)) suite.r = r } func (suite *GoroutineSuite) TestRunSuccess() { r := goroutine.NewRunner(suite.r, "testsuccess", func(context.Context, runtime.Runtime, io.Writer) error { return nil }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink)) // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) } func (suite *GoroutineSuite) TestRunFail() { r := goroutine.NewRunner(suite.r, "testfail", func(context.Context, runtime.Runtime, io.Writer) error { return errors.New("service failed") }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().EqualError(r.Run(MockEventSink), "service failed") // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) } func (suite *GoroutineSuite) TestRunPanic() { r := goroutine.NewRunner(suite.r, "testpanic", func(context.Context, runtime.Runtime, io.Writer) error { panic("service panic") }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() err := r.Run(MockEventSink) suite.Assert().Error(err) suite.Assert().Regexp("^panic in service: service panic.*", err.Error()) // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) } func (suite *GoroutineSuite) TestStop() { r := goroutine.NewRunner(suite.r, "teststop", func(ctx context.Context, data runtime.Runtime, logger io.Writer) error { <-ctx.Done() return ctx.Err() }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() errCh := make(chan error) go func() { errCh <- r.Run(MockEventSink) }() time.Sleep(20 * time.Millisecond) select { case <-errCh: suite.Require().Fail("should not return yet") default: } suite.Assert().NoError(r.Stop()) suite.Assert().NoError(<-errCh) } func (suite *GoroutineSuite) TestStuckOnStop() { r := goroutine.NewRunner(suite.r, "teststop", func(ctx context.Context, data runtime.Runtime, logger io.Writer) error { // hanging forever select {} }, runner.WithLoggingManager(suite.loggingManager), runner.WithGracefulShutdownTimeout(10*time.Millisecond), ) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() errCh := make(chan error) go func() { errCh <- r.Run(MockEventSink) }() time.Sleep(20 * time.Millisecond) select { case <-errCh: suite.Require().Fail("should not return yet") default: } suite.Assert().NoError(r.Stop()) suite.Assert().ErrorIs(<-errCh, goroutine.ErrAborted) } func (suite *GoroutineSuite) TestRunLogs() { r := goroutine.NewRunner(suite.r, "logtest", func(ctx context.Context, data runtime.Runtime, logger io.Writer) error { //nolint:errcheck _, _ = logger.Write([]byte("Test 1\nTest 2\n")) return nil }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink)) logFile, err := os.Open(filepath.Join(suite.tmpDir, "logtest.log")) suite.Assert().NoError(err) //nolint:errcheck defer logFile.Close() logContents, err := io.ReadAll(logFile) suite.Assert().NoError(err) suite.Assert().Equal([]byte("Test 1\nTest 2\n"), logContents) } func TestGoroutineSuite(t *testing.T) { t.Setenv("PLATFORM", "metal") suite.Run(t, new(GoroutineSuite)) } ================================================ FILE: internal/app/machined/pkg/system/runner/internal/lastlog/lastlog.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package lastlog provides utilities for capturing last log line(s) with a writer. package lastlog import ( "bytes" "sync" ) // limit is the maximum number of bytes to capture. const limit = 512 // Writer is a writer that captures the last log line(s). type Writer struct { mu sync.Mutex buf []byte } // Writer implements io.Writer. func (w *Writer) Write(p []byte) (n int, err error) { w.mu.Lock() defer w.mu.Unlock() if w.buf == nil { w.buf = make([]byte, 0, limit) } if len(p) >= limit { cut := p for len(cut) > limit { nlIndex := bytes.IndexByte(cut, '\n') if nlIndex == -1 { // no newline found, append the last part of the cut if len(cut) > limit { cut = cut[len(cut)-limit:] } break } cut = cut[nlIndex+1:] } w.buf = append(w.buf[:0], cut...) return len(p), nil } bufPos := 0 for len(w.buf)-bufPos+len(p) > limit { nlIndex := bytes.IndexByte(w.buf[bufPos:], '\n') if nlIndex == -1 { // no newline found, drop the whole buffer bufPos = len(w.buf) } // we are over the limit, drop the beginning of the buffer bufPos += nlIndex + 1 } if bufPos > 0 { // drop the beginning of the buffer copy(w.buf, w.buf[bufPos:]) w.buf = w.buf[:len(w.buf)-bufPos] } w.buf = append(w.buf, p...) return len(p), nil } // GetLastLog returns the last log line(s) captured by the writer. func (w *Writer) GetLastLog() string { w.mu.Lock() defer w.mu.Unlock() return string(bytes.TrimRight(w.buf, "\n")) } ================================================ FILE: internal/app/machined/pkg/system/runner/internal/lastlog/lastlog_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package lastlog_test import ( "bytes" _ "embed" "fmt" "io" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/internal/lastlog" ) //go:embed testdata/kubelet.log var lastLogData []byte type readerWrap struct { io.Reader } func TestLastLog(t *testing.T) { t.Parallel() for _, step := range []int{1, 7, 13, 29, 37, 64, 128, 256, 512, 1024} { t.Run(fmt.Sprintf("step=%d", step), func(t *testing.T) { t.Parallel() w := &lastlog.Writer{} for i := 0; i < len(lastLogData); i += step { n := min(step, len(lastLogData)-i) out, err := w.Write(lastLogData[i : i+n]) require.NoError(t, err) require.Equal(t, n, out) } assert.Equal(t, "172.20.0.2: {\"ts\":1758106728112.3425,\"caller\":\"topologymanager/scope.go:117\",\"msg\":\"RemoveContainer\",\"v\":0,\"containerID\":\"d89f847ed0a3500dd712577fcb52dbbc5169bac1b7017d2c6a3f5f809693806c\"}\n172.20.0.2: {\"ts\":1758106728119.877,\"caller\":\"topologymanager/scope.go:117\",\"msg\":\"RemoveContainer\",\"v\":0,\"containerID\":\"56aeb1c5d4785d1d5cc51984dd244fdd4fb672f91e82cfa21e02c92d0f7c3be3\"}", w.GetLastLog()) //nolint:lll }) } } func BenchmarkWrites(b *testing.B) { buf := make([]byte, 128) var r bytes.Reader b.ReportAllocs() b.ResetTimer() w := &lastlog.Writer{} for b.Loop() { r.Reset(lastLogData) src := readerWrap{&r} n, err := io.CopyBuffer(w, src, buf) require.NoError(b, err) require.Equal(b, int64(len(lastLogData)), n) } } ================================================ FILE: internal/app/machined/pkg/system/runner/internal/lastlog/testdata/kubelet.log ================================================ 172.20.0.2: {"ts":1758106668007.8376,"caller":"app/server.go:529","msg":"Kubelet version","v":0,"kubeletVersion":"v1.34.1"} 172.20.0.2: {"ts":1758106668007.8638,"caller":"app/server.go:531","msg":"Golang settings","v":0,"GOGC":"","GOMAXPROCS":"","GOTRACEBACK":""} 172.20.0.2: {"ts":1758106668008.2092,"caller":"watchdog/watchdog_linux.go:95","msg":"Systemd watchdog is not enabled","v":0} 172.20.0.2: {"ts":1758106668008.2358,"caller":"watchdog/watchdog_linux.go:137","msg":"Systemd watchdog is not enabled or the interval is invalid, so health checking will not be started.","v":0} 172.20.0.2: {"ts":1758106668008.5452,"caller":"app/server.go:956","msg":"Client rotation is on, will bootstrap in background","v":0} 172.20.0.2: {"ts":1758106668009.3418,"caller":"certificate/certificate_store.go:147","msg":"Loading cert/key pair from a file","v":0,"filePath":"/var/lib/kubelet/pki/kubelet-client-current.pem"} 172.20.0.2: {"ts":1758106668011.8005,"caller":"dynamiccertificates/dynamic_cafile_content.go:161","msg":"Starting controller","v":0,"name":"client-ca-bundle::/etc/kubernetes/pki/ca.crt"} 172.20.0.2: {"ts":1758106668017.9783,"caller":"app/server.go:1423","msg":"Using cgroup driver setting received from the CRI runtime","v":0,"cgroupDriver":"cgroupfs"} 172.20.0.2: {"ts":1758106668023.9102,"caller":"app/server.go:842","msg":"NoSwap is set due to memorySwapBehavior not specified","v":0,"memorySwapBehavior":"","FailSwapOn":false} 172.20.0.2: {"ts":1758106668025.0066,"caller":"cm/container_manager_linux.go:270","msg":"Container manager verified user specified cgroup-root exists","v":0,"cgroupRoot":[]} 172.20.0.2: {"ts":1758106668025.0332,"caller":"cm/container_manager_linux.go:275","msg":"Creating Container Manager object based on Node Config","v":0,"nodeConfig":{"NodeName":"talos-default-controlplane-1","RuntimeCgroupsName":"","SystemCgroupsName":"/system","KubeletCgroupsName":"/podruntime/kubelet","KubeletOOMScoreAdj":-450,"ContainerRuntime":"","CgroupsPerQOS":true,"CgroupRoot":"/","CgroupDriver":"cgroupfs","KubeletRootDir":"/var/lib/kubelet","ProtectKernelDefaults":true,"KubeReservedCgroupName":"","SystemReservedCgroupName":"","ReservedSystemCPUs":{},"EnforceNodeAllocatable":{"pods":{}},"KubeReserved":null,"SystemReserved":{"cpu":"50m","ephemeral-storage":"256Mi","memory":"512Mi","pid":"100"},"HardEvictionThresholds":[{"Signal":"memory.available","Operator":"LessThan","Value":{"Quantity":"100Mi","Percentage":0},"GracePeriod":0,"MinReclaim":null},{"Signal":"nodefs.available","Operator":"LessThan","Value":{"Quantity":null,"Percentage":0.1},"GracePeriod":0,"MinReclaim":null},{"Signal":"nodefs.inodesFree","Operator":"LessThan","Value":{"Quantity":null,"Percentage":0.05},"GracePeriod":0,"MinReclaim":null},{"Signal":"imagefs.available","Operator":"LessThan","Value":{"Quantity":null,"Percentage":0.15},"GracePeriod":0,"MinReclaim":null},{"Signal":"imagefs.inodesFree","Operator":"LessThan","Value":{"Quantity":null,"Percentage":0.05},"GracePeriod":0,"MinReclaim":null}],"QOSReserved":{},"CPUManagerPolicy":"none","CPUManagerPolicyOptions":null,"TopologyManagerScope":"container","CPUManagerReconcilePeriod":10000000000,"MemoryManagerPolicy":"None","MemoryManagerReservedMemory":null,"PodPidsLimit":-1,"EnforceCPULimits":true,"CPUCFSQuotaPeriod":100000000,"TopologyManagerPolicy":"none","TopologyManagerPolicyOptions":null,"CgroupVersion":2}} 172.20.0.2: {"ts":1758106668025.5054,"caller":"topologymanager/topology_manager.go:138","msg":"Creating topology manager with none policy","v":0} 172.20.0.2: {"ts":1758106668025.517,"caller":"cm/container_manager_linux.go:306","msg":"Creating device plugin manager","v":0} 172.20.0.2: {"ts":1758106668025.5674,"caller":"cm/container_manager_linux.go:315","msg":"Creating Dynamic Resource Allocation (DRA) manager","v":0} 172.20.0.2: {"ts":1758106668026.719,"caller":"state/state_mem.go:36","msg":"Initialized new in-memory state store","v":0} 172.20.0.2: {"ts":1758106668027.4104,"caller":"kubelet/kubelet.go:475","msg":"Attempting to sync node with API server","v":0} 172.20.0.2: {"ts":1758106668027.427,"caller":"kubelet/kubelet.go:382","msg":"Adding pod URL with HTTP header","v":0,"URL":"http://127.0.0.1:34045","header":{}} 172.20.0.2: {"ts":1758106668027.6672,"caller":"kubelet/kubelet.go:387","msg":"Adding apiserver pod source","v":0} 172.20.0.2: {"ts":1758106668027.6926,"caller":"config/apiserver.go:42","msg":"Waiting for node sync before watching apiserver pods","v":0} 172.20.0.2: {"ts":1758106668031.0547,"caller":"kuberuntime/kuberuntime_manager.go:291","msg":"Container runtime initialized","v":0,"containerRuntime":"containerd","version":"v2.1.4","apiVersion":"v1"} 172.20.0.2: {"ts":1758106668031.528,"caller":"kubelet/kubelet.go:940","msg":"Not starting ClusterTrustBundle informer because we are in static kubelet mode or the ClusterTrustBundleProjection featuregate is disabled","v":0} 172.20.0.2: {"ts":1758106668031.6064,"caller":"kubelet/kubelet.go:964","msg":"Not starting PodCertificateRequest manager because we are in static kubelet mode or the PodCertificateProjection feature gate is disabled","v":0} 172.20.0.2: {"ts":1758106668033.0266,"caller":"nodeshutdown/nodeshutdown_manager_linux.go:105","msg":"Creating node shutdown manager","v":0,"shutdownGracePeriodRequested":"30s","shutdownGracePeriodCriticalPods":"10s","shutdownGracePeriodByPodPriority":[{"Priority":0,"ShutdownGracePeriodSeconds":20},{"Priority":2000000000,"ShutdownGracePeriodSeconds":10}]} 172.20.0.2: {"ts":1758106668034.3818,"caller":"app/server.go:1262","msg":"Started kubelet","v":0} 172.20.0.2: {"ts":1758106668035.7124,"caller":"stats/fs_resource_analyzer.go:67","msg":"Starting FS ResourceAnalyzer","v":0} 172.20.0.2: {"ts":1758106668037.3792,"caller":"record/event.go:368","msg":"Unable to write event (may retry after sleeping)","event":"&Event{ObjectMeta:{talos-default-controlplane-1.18660cb3216ebb50 default 0 0001-01-01 00:00:00 +0000 UTC map[] map[] [] [] []},InvolvedObject:ObjectReference{Kind:Node,Namespace:,Name:talos-default-controlplane-1,UID:talos-default-controlplane-1,APIVersion:,ResourceVersion:,FieldPath:,},Reason:Starting,Message:Starting kubelet.,Source:EventSource{Component:kubelet,Host:talos-default-controlplane-1,},FirstTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,LastTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,Count:1,Type:Normal,EventTime:0001-01-01 00:00:00 +0000 UTC,Series:nil,Action:,Related:nil,ReportingController:kubelet,ReportingInstance:talos-default-controlplane-1,}","err":"Post \"https://127.0.0.1:7445/api/v1/namespaces/default/events\": EOF"} 172.20.0.2: {"ts":1758106668042.905,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"6808d56050203041f12f406d3f0c9baa812282a5a9e2f5f8d7b3ddbd8fd04c5c"} 172.20.0.2: {"ts":1758106668042.9885,"caller":"server/server.go:180","msg":"Starting to listen","v":0,"address":"0.0.0.0","port":10250} 172.20.0.2: {"ts":1758106668043.6199,"caller":"server/server.go:310","msg":"Adding debug handlers to kubelet server","v":0} 172.20.0.2: {"ts":1758106668047.2854,"caller":"grpc/ratelimit.go:56","msg":"Setting rate limiting for endpoint","v":0,"service":"podresources","qps":100,"burstTokens":10} 172.20.0.2: {"ts":1758106668047.3354,"caller":"podresources/server_v1.go:49","msg":"podresources","v":0,"method":"list","useActivePods":true} 172.20.0.2: {"ts":1758106668047.4949,"caller":"server/server.go:249","msg":"Starting to serve the podresources API","v":0,"endpoint":"unix:/var/lib/kubelet/pod-resources/kubelet.sock"} 172.20.0.2: {"ts":1758106668048.1006,"caller":"dynamiccertificates/dynamic_serving_content.go:135","msg":"Starting controller","v":0,"name":"kubelet-server-cert-files::/var/lib/kubelet/pki/kubelet.crt::/var/lib/kubelet/pki/kubelet.key"} 172.20.0.2: {"ts":1758106668051.4663,"caller":"volumemanager/volume_manager.go:313","msg":"Starting Kubelet Volume Manager","v":0} 172.20.0.2: {"ts":1758106668051.6091,"caller":"kubelet/kubelet_node_status.go:404","msg":"Error getting the current node from lister","err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106668051.839,"caller":"populator/desired_state_of_world_populator.go:146","msg":"Desired state populator starts to run","v":0} 172.20.0.2: {"ts":1758106668052.162,"caller":"reconciler/reconciler.go:29","msg":"Reconciler: start to sync state","v":0} 172.20.0.2: {"ts":1758106668055.2058,"caller":"container/factory.go:223","msg":"Registration of the containerd container factory successfully","v":0} 172.20.0.2: {"ts":1758106668055.2915,"caller":"container/factory.go:223","msg":"Registration of the systemd container factory successfully","v":0} 172.20.0.2: {"ts":1758106668055.45,"caller":"container/factory.go:221","msg":"Registration of the crio container factory failed: Get \"http://%2Fvar%2Frun%2Fcrio%2Fcrio.sock/info\": dial unix /var/run/crio/crio.sock: connect: no such file or directory","v":0} 172.20.0.2: {"ts":1758106668058.806,"caller":"kubelet/kubelet.go:1615","msg":"Image garbage collection failed once. Stats initialization may not have completed yet","err":"invalid capacity 0 on image filesystem"} 172.20.0.2: {"ts":1758106668063.6414,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"6874b1c5fe7136331a47236461534fa835053e0b750101a80ee2ea29f3980d39"} 172.20.0.2: {"ts":1758106668076.3552,"caller":"cpumanager/cpu_manager.go:221","msg":"Starting CPU manager","v":0,"policy":"none"} 172.20.0.2: {"ts":1758106668076.446,"caller":"cpumanager/cpu_manager.go:222","msg":"Reconciling","v":0,"reconcilePeriod":"10s"} 172.20.0.2: {"ts":1758106668076.5164,"caller":"state/state_mem.go:36","msg":"Initialized new in-memory state store","v":0} 172.20.0.2: {"ts":1758106668076.7607,"caller":"state/state_mem.go:88","msg":"Updated default CPUSet","v":0,"cpuSet":""} 172.20.0.2: {"ts":1758106668076.8374,"caller":"state/state_mem.go:96","msg":"Updated CPUSet assignments","v":0,"assignments":{}} 172.20.0.2: {"ts":1758106668076.8877,"caller":"cpumanager/policy_none.go:49","msg":"None policy: Start","v":0} 172.20.0.2: {"ts":1758106668077.186,"caller":"memorymanager/memory_manager.go:187","msg":"Starting memorymanager","v":0,"policy":"None"} 172.20.0.2: {"ts":1758106668077.2717,"logger":"Memory Manager state checkpoint","caller":"state/state_mem.go:36","msg":"Initializing new in-memory state store","v":0} 172.20.0.2: {"ts":1758106668077.8845,"logger":"Memory Manager state checkpoint","caller":"state/state_mem.go:77","msg":"Updated machine memory state","v":0} 172.20.0.2: {"ts":1758106668077.9558,"caller":"memorymanager/policy_none.go:47","msg":"Start","v":0} 172.20.0.2: {"ts":1758106668083.122,"caller":"devicemanager/manager.go:513","msg":"Failed to read data from checkpoint","checkpoint":"kubelet_internal_checkpoint","err":"checkpoint is not found"} 172.20.0.2: {"ts":1758106668085.8274,"caller":"eviction/eviction_manager.go:189","msg":"Eviction manager: starting control loop","v":0} 172.20.0.2: {"ts":1758106668085.876,"caller":"logs/container_log_manager.go:146","msg":"Initializing container log rotate workers","v":0,"workers":1,"monitorPeriod":"10s"} 172.20.0.2: {"ts":1758106668086.1648,"caller":"pluginmanager/plugin_manager.go:118","msg":"Starting Kubelet Plugin Manager","v":0} 172.20.0.2: {"ts":1758106668089.1938,"caller":"eviction/eviction_manager.go:267","msg":"eviction manager: failed to check if we have separate container filesystem. Ignoring.","err":"no imagefs label for configured runtime"} 172.20.0.2: {"ts":1758106668089.3218,"caller":"eviction/eviction_manager.go:292","msg":"Eviction manager: failed to get summary stats","err":"failed to get node info: node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106668093.0972,"caller":"kubelet/kubelet_network_linux.go:54","msg":"Initialized iptables rules.","v":0,"protocol":"IPv4"} 172.20.0.2: {"ts":1758106668094.1462,"caller":"kubelet/kubelet_network_linux.go:54","msg":"Initialized iptables rules.","v":0,"protocol":"IPv6"} 172.20.0.2: {"ts":1758106668094.1702,"caller":"status/status_manager.go:244","msg":"Starting to sync pod status with apiserver","v":0} 172.20.0.2: {"ts":1758106668094.3052,"caller":"kubelet/kubelet.go:2427","msg":"Starting kubelet main sync loop","v":0} 172.20.0.2: {"ts":1758106668094.4678,"caller":"kubelet/kubelet.go:2451","msg":"Skipping pod synchronization","err":"PLEG is not healthy: pleg has yet to be successful","errCauses":[{"error":"PLEG is not healthy: pleg has yet to be successful"}]} 172.20.0.2: {"ts":1758106668191.7249,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106668192.5737,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106668194.6191,"caller":"kubelet/pod_container_deletor.go:80","msg":"Container not found in pod's containers","v":0,"containerID":"5f3eade1f306db00c4b8671d76bb0548472d98a974b2b3a635ff2ee83c91c3f3"} 172.20.0.2: {"ts":1758106668194.635,"caller":"kubelet/pod_container_deletor.go:80","msg":"Container not found in pod's containers","v":0,"containerID":"6f6aaa0f4a9268afb3b97bc7dc492508435cfac4c5d36c0025ae17564a502a4d"} 172.20.0.2: {"ts":1758106668194.6438,"caller":"kubelet/pod_container_deletor.go:80","msg":"Container not found in pod's containers","v":0,"containerID":"e6125235fae9c2154cca503ade1e11dee01ef40d31c916ae3bfc77b519658e8d"} 172.20.0.2: {"ts":1758106668194.6877,"caller":"kubelet/pod_container_deletor.go:80","msg":"Container not found in pod's containers","v":0,"containerID":"af6833318327a76e36f22d0e1dc6de31e3c1a3b68facb070839eadd0952e2cd4"} 172.20.0.2: {"ts":1758106668194.697,"caller":"kubelet/pod_container_deletor.go:80","msg":"Container not found in pod's containers","v":0,"containerID":"2988531464a3c775dafc369acbee0e3c4b244a02736ee211689b62fcf8f3aecf"} 172.20.0.2: {"ts":1758106668393.494,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106668394.5703,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106668796.2822,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106668797.1108,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106669598.726,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106669599.78,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106671200.8167,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106671201.587,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106674402.306,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106674403.182,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106675621.227,"caller":"record/event.go:368","msg":"Unable to write event (may retry after sleeping)","event":"&Event{ObjectMeta:{talos-default-controlplane-1.18660cb3216ebb50 default 0 0001-01-01 00:00:00 +0000 UTC map[] map[] [] [] []},InvolvedObject:ObjectReference{Kind:Node,Namespace:,Name:talos-default-controlplane-1,UID:talos-default-controlplane-1,APIVersion:,ResourceVersion:,FieldPath:,},Reason:Starting,Message:Starting kubelet.,Source:EventSource{Component:kubelet,Host:talos-default-controlplane-1,},FirstTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,LastTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,Count:1,Type:Normal,EventTime:0001-01-01 00:00:00 +0000 UTC,Series:nil,Action:,Related:nil,ReportingController:kubelet,ReportingInstance:talos-default-controlplane-1,}","err":"Post \"https://127.0.0.1:7445/api/v1/namespaces/default/events\": EOF"} 172.20.0.2: {"ts":1758106678042.8213,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.Service","err":"failed to list *v1.Service: Get \"https://127.0.0.1:7445/api/v1/services?fieldSelector=spec.clusterIP%21%3DNone&limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106678042.8445,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.Node","err":"failed to list *v1.Node: Get \"https://127.0.0.1:7445/api/v1/nodes?fieldSelector=metadata.name%3Dtalos-default-controlplane-1&limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106678052.8208,"caller":"lease/controller.go:145","msg":"Failed to ensure lease exists, will retry","interval":"200ms","err":"Get \"https://127.0.0.1:7445/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/talos-default-controlplane-1?timeout=10s\": context deadline exceeded - error from a previous attempt: EOF"} 172.20.0.2: {"ts":1758106678067.7424,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.CSIDriver","err":"failed to list *v1.CSIDriver: Get \"https://127.0.0.1:7445/apis/storage.k8s.io/v1/csidrivers?limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106678067.744,"caller":"reconciler/reconstruct.go:189","msg":"Failed to get Node status to reconstruct device paths","err":"Get \"https://127.0.0.1:7445/api/v1/nodes/talos-default-controlplane-1\": EOF"} 172.20.0.2: {"ts":1758106678089.3804,"caller":"eviction/eviction_manager.go:292","msg":"Eviction manager: failed to get summary stats","err":"failed to get node info: node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106678109.1099,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.RuntimeClass","err":"failed to list *v1.RuntimeClass: Get \"https://127.0.0.1:7445/apis/node.k8s.io/v1/runtimeclasses?limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106678503.5498,"caller":"webhook/webhook.go:269","msg":"Failed to make webhook authorizer request: Post \"https://127.0.0.1:7445/apis/authorization.k8s.io/v1/subjectaccessreviews\": EOF"} 172.20.0.2: {"ts":1758106678503.5718,"caller":"server/server.go:347","msg":"Authorization error","user":"apiserver-kubelet-client","verb":"get","resource":"nodes","subresource":"pods","err":"Post \"https://127.0.0.1:7445/apis/authorization.k8s.io/v1/subjectaccessreviews\": EOF"} 172.20.0.2: {"ts":1758106680804.753,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106680805.5906,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106685622.703,"caller":"record/event.go:368","msg":"Unable to write event (may retry after sleeping)","event":"&Event{ObjectMeta:{talos-default-controlplane-1.18660cb3216ebb50 default 0 0001-01-01 00:00:00 +0000 UTC map[] map[] [] [] []},InvolvedObject:ObjectReference{Kind:Node,Namespace:,Name:talos-default-controlplane-1,UID:talos-default-controlplane-1,APIVersion:,ResourceVersion:,FieldPath:,},Reason:Starting,Message:Starting kubelet.,Source:EventSource{Component:kubelet,Host:talos-default-controlplane-1,},FirstTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,LastTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,Count:1,Type:Normal,EventTime:0001-01-01 00:00:00 +0000 UTC,Series:nil,Action:,Related:nil,ReportingController:kubelet,ReportingInstance:talos-default-controlplane-1,}","err":"Post \"https://127.0.0.1:7445/api/v1/namespaces/default/events\": EOF"} 172.20.0.2: {"ts":1758106687806.421,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106687807.2507,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106688040.816,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106688046.192,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106688048.2466,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106688089.7534,"caller":"eviction/eviction_manager.go:292","msg":"Eviction manager: failed to get summary stats","err":"failed to get node info: node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106688183.5593,"caller":"reconciler/reconstruct.go:189","msg":"Failed to get Node status to reconstruct device paths","err":"Get \"https://127.0.0.1:7445/api/v1/nodes/talos-default-controlplane-1\": EOF"} 172.20.0.2: {"ts":1758106688253.933,"caller":"lease/controller.go:145","msg":"Failed to ensure lease exists, will retry","interval":"400ms","err":"Get \"https://127.0.0.1:7445/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/talos-default-controlplane-1?timeout=10s\": context deadline exceeded - error from a previous attempt: EOF"} 172.20.0.2: {"ts":1758106688284.332,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"secrets\" (UniqueName: \"kubernetes.io/host-path/cf26aa92a3daac8c6fe4af9557491017-secrets\") pod \"kube-scheduler-talos-default-controlplane-1\" (UID: \"cf26aa92a3daac8c6fe4af9557491017\") ","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106688284.3755,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"config\" (UniqueName: \"kubernetes.io/host-path/cf26aa92a3daac8c6fe4af9557491017-config\") pod \"kube-scheduler-talos-default-controlplane-1\" (UID: \"cf26aa92a3daac8c6fe4af9557491017\") ","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106688284.3943,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"secrets\" (UniqueName: \"kubernetes.io/host-path/981612585a0b5246fd0e4918415cf15a-secrets\") pod \"kube-apiserver-talos-default-controlplane-1\" (UID: \"981612585a0b5246fd0e4918415cf15a\") ","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106688284.4104,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"config\" (UniqueName: \"kubernetes.io/host-path/981612585a0b5246fd0e4918415cf15a-config\") pod \"kube-apiserver-talos-default-controlplane-1\" (UID: \"981612585a0b5246fd0e4918415cf15a\") ","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106688284.4329,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"audit\" (UniqueName: \"kubernetes.io/host-path/981612585a0b5246fd0e4918415cf15a-audit\") pod \"kube-apiserver-talos-default-controlplane-1\" (UID: \"981612585a0b5246fd0e4918415cf15a\") ","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106688284.4543,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"secrets\" (UniqueName: \"kubernetes.io/host-path/cdadad5a0038b90e98f6a53c15c67a5d-secrets\") pod \"kube-controller-manager-talos-default-controlplane-1\" (UID: \"cdadad5a0038b90e98f6a53c15c67a5d\") ","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106689355.9502,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.Service","err":"failed to list *v1.Service: Get \"https://127.0.0.1:7445/api/v1/services?fieldSelector=spec.clusterIP%21%3DNone&limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106689368.3943,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.RuntimeClass","err":"failed to list *v1.RuntimeClass: Get \"https://127.0.0.1:7445/apis/node.k8s.io/v1/runtimeclasses?limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106689436.3508,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.Node","err":"failed to list *v1.Node: Get \"https://127.0.0.1:7445/api/v1/nodes?fieldSelector=metadata.name%3Dtalos-default-controlplane-1&limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106689593.513,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.CSIDriver","err":"failed to list *v1.CSIDriver: Get \"https://127.0.0.1:7445/apis/storage.k8s.io/v1/csidrivers?limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106693994.0713,"caller":"webhook/webhook.go:269","msg":"Failed to make webhook authorizer request: Post \"https://127.0.0.1:7445/apis/authorization.k8s.io/v1/subjectaccessreviews\": EOF"} 172.20.0.2: {"ts":1758106693994.0884,"caller":"server/server.go:347","msg":"Authorization error","user":"apiserver-kubelet-client","verb":"get","resource":"nodes","subresource":"pods","err":"Post \"https://127.0.0.1:7445/apis/authorization.k8s.io/v1/subjectaccessreviews\": EOF"} 172.20.0.2: {"ts":1758106694808.4841,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106694809.2092,"caller":"kubelet/kubelet_node_status.go:107","msg":"Unable to register node with API server","node":{"name":"talos-default-controlplane-1"},"err":"Post \"https://127.0.0.1:7445/api/v1/nodes\": EOF"} 172.20.0.2: {"ts":1758106695624.5645,"caller":"record/event.go:368","msg":"Unable to write event (may retry after sleeping)","event":"&Event{ObjectMeta:{talos-default-controlplane-1.18660cb3216ebb50 default 0 0001-01-01 00:00:00 +0000 UTC map[] map[] [] [] []},InvolvedObject:ObjectReference{Kind:Node,Namespace:,Name:talos-default-controlplane-1,UID:talos-default-controlplane-1,APIVersion:,ResourceVersion:,FieldPath:,},Reason:Starting,Message:Starting kubelet.,Source:EventSource{Component:kubelet,Host:talos-default-controlplane-1,},FirstTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,LastTimestamp:2025-09-17 10:57:48.034341712 +0000 UTC m=+0.515208206,Count:1,Type:Normal,EventTime:0001-01-01 00:00:00 +0000 UTC,Series:nil,Action:,Related:nil,ReportingController:kubelet,ReportingInstance:talos-default-controlplane-1,}","err":"Post \"https://127.0.0.1:7445/api/v1/namespaces/default/events\": EOF"} 172.20.0.2: {"ts":1758106698090.628,"caller":"eviction/eviction_manager.go:292","msg":"Eviction manager: failed to get summary stats","err":"failed to get node info: node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106698298.185,"caller":"reconciler/reconstruct.go:189","msg":"Failed to get Node status to reconstruct device paths","err":"Get \"https://127.0.0.1:7445/api/v1/nodes/talos-default-controlplane-1\": EOF"} 172.20.0.2: {"ts":1758106698654.832,"caller":"lease/controller.go:145","msg":"Failed to ensure lease exists, will retry","interval":"800ms","err":"Get \"https://127.0.0.1:7445/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/talos-default-controlplane-1?timeout=10s\": context deadline exceeded - error from a previous attempt: EOF"} 172.20.0.2: {"ts":1758106699156.1372,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106699158.0012,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106699159.5417,"caller":"kubelet/kubelet.go:3215","msg":"No need to create a mirror pod, since failed to get node info from the cluster","node":{"name":"talos-default-controlplane-1"},"err":"node \"talos-default-controlplane-1\" not found"} 172.20.0.2: {"ts":1758106701067.7002,"logger":"UnhandledError","caller":"cache/reflector.go:205","msg":"Failed to watch","reflector":"k8s.io/client-go/informers/factory.go:160","type":"*v1.Service","err":"failed to list *v1.Service: Get \"https://127.0.0.1:7445/api/v1/services?fieldSelector=spec.clusterIP%21%3DNone&limit=500&resourceVersion=0\": EOF"} 172.20.0.2: {"ts":1758106701552.0469,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106701560.222,"caller":"kubelet/kubelet.go:3221","msg":"Failed creating a mirror pod","pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"},"err":"pods \"kube-scheduler-talos-default-controlplane-1\" already exists"} 172.20.0.2: {"ts":1758106701560.2449,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106701567.1072,"caller":"kubelet/kubelet.go:3221","msg":"Failed creating a mirror pod","pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"},"err":"pods \"kube-apiserver-talos-default-controlplane-1\" already exists"} 172.20.0.2: {"ts":1758106701567.1692,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106701573.502,"caller":"kubelet/kubelet.go:3221","msg":"Failed creating a mirror pod","pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"},"err":"pods \"kube-controller-manager-talos-default-controlplane-1\" already exists"} 172.20.0.2: {"ts":1758106701810.7104,"caller":"kubelet/kubelet_node_status.go:75","msg":"Attempting to register node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106701818.06,"caller":"kubelet/kubelet_node_status.go:124","msg":"Node was previously registered","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106701818.2197,"caller":"kubelet/kubelet_node_status.go:78","msg":"Successfully registered node","v":0,"node":{"name":"talos-default-controlplane-1"}} 172.20.0.2: {"ts":1758106701818.2822,"caller":"kuberuntime/kuberuntime_manager.go:1828","msg":"Updating runtime config through cri with podcidr","v":0,"CIDR":"10.244.0.0/24"} 172.20.0.2: {"ts":1758106701818.729,"caller":"kubelet/kubelet_network.go:47","msg":"Updating Pod CIDR","v":0,"originalPodCIDR":"","newPodCIDR":"10.244.0.0/24"} 172.20.0.2: {"ts":1758106701819.1897,"caller":"nodestatus/setters.go:543","msg":"Node became not ready","v":0,"node":{"name":"talos-default-controlplane-1"},"condition":{"type":"Ready","status":"False","lastHeartbeatTime":"2025-09-17T10:58:21Z","lastTransitionTime":"2025-09-17T10:58:21Z","reason":"KubeletNotReady","message":"CSINode is not yet initialized"}} 172.20.0.2: {"ts":1758106702045.5874,"caller":"config/apiserver.go:52","msg":"Watching apiserver","v":0} 172.20.0.2: {"ts":1758106702052.1104,"caller":"kubelet/kubelet.go:3202","msg":"Trying to delete pod","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"},"podUID":"ff721e54-8d9f-4456-8f05-b43954afb707"} 172.20.0.2: {"ts":1758106702052.2893,"caller":"populator/desired_state_of_world_populator.go:154","msg":"Finished populating initial desired state of world","v":0} 172.20.0.2: {"ts":1758106702053.334,"caller":"kubelet/kubelet.go:3202","msg":"Trying to delete pod","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"},"podUID":"5cf1af57-2c52-4664-a870-335bb359bac1"} 172.20.0.2: {"ts":1758106702053.4333,"caller":"kubelet/kubelet.go:3202","msg":"Trying to delete pod","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"},"podUID":"0079b8ef-61bc-436e-ae79-0a386af0b448"} 172.20.0.2: {"ts":1758106702100.5447,"caller":"kubelet/kubelet_volumes.go:163","msg":"Cleaned up orphaned pod volumes dir","v":0,"podUID":"52bfc7fae30f5922b2215aa79f5b3751","path":"/var/lib/kubelet/pods/52bfc7fae30f5922b2215aa79f5b3751/volumes"} 172.20.0.2: {"ts":1758106702100.7961,"caller":"kubelet/kubelet_volumes.go:163","msg":"Cleaned up orphaned pod volumes dir","v":0,"podUID":"56cf311ad81b6b54947482a9ebbce301","path":"/var/lib/kubelet/pods/56cf311ad81b6b54947482a9ebbce301/volumes"} 172.20.0.2: {"ts":1758106702101.0337,"caller":"kubelet/kubelet_volumes.go:163","msg":"Cleaned up orphaned pod volumes dir","v":0,"podUID":"797f25ac28f9618d52a80b171838d145","path":"/var/lib/kubelet/pods/797f25ac28f9618d52a80b171838d145/volumes"} 172.20.0.2: {"ts":1758106702104.23,"caller":"kubelet/kubelet.go:3208","msg":"Deleted mirror pod as it didn't match the static Pod","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702104.2478,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702104.6187,"caller":"kubelet/kubelet.go:3208","msg":"Deleted mirror pod as it didn't match the static Pod","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702104.6335,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702112.9602,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"run\" (UniqueName: \"kubernetes.io/host-path/5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb-run\") pod \"kube-flannel-bhgc2\" (UID: \"5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb\") ","v":0,"pod":{"name":"kube-flannel-bhgc2","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702113.0017,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"cni\" (UniqueName: \"kubernetes.io/host-path/5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb-cni\") pod \"kube-flannel-bhgc2\" (UID: \"5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb\") ","v":0,"pod":{"name":"kube-flannel-bhgc2","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702113.0527,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"lib-modules\" (UniqueName: \"kubernetes.io/host-path/e06f16fc-4f9b-47d5-89cb-f0d6f2891f60-lib-modules\") pod \"kube-proxy-pvlfh\" (UID: \"e06f16fc-4f9b-47d5-89cb-f0d6f2891f60\") ","v":0,"pod":{"name":"kube-proxy-pvlfh","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702113.0637,"caller":"reconciler/reconciler_common.go:251","msg":"operationExecutor.VerifyControllerAttachedVolume started for volume \"ssl-certs-host\" (UniqueName: \"kubernetes.io/host-path/e06f16fc-4f9b-47d5-89cb-f0d6f2891f60-ssl-certs-host\") pod \"kube-proxy-pvlfh\" (UID: \"e06f16fc-4f9b-47d5-89cb-f0d6f2891f60\") ","v":0,"pod":{"name":"kube-proxy-pvlfh","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702117.5718,"caller":"kubelet/kubelet.go:3208","msg":"Deleted mirror pod as it didn't match the static Pod","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702117.6743,"caller":"kubelet/kubelet.go:3219","msg":"Creating a mirror pod for static pod","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"}} 172.20.0.2: {"ts":1758106702127.056,"caller":"status/status_manager.go:1018","msg":"Failed to get status for pod","podUID":"cdadad5a0038b90e98f6a53c15c67a5d","pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"},"err":"pods \"kube-controller-manager-talos-default-controlplane-1\" is forbidden: User \"system:node:talos-default-controlplane-1\" cannot get resource \"pods\" in API group \"\" in the namespace \"kube-system\": no relationship found between node 'talos-default-controlplane-1' and this object"} 172.20.0.2: {"ts":1758106702136.1614,"caller":"kubelet/kubelet_node_status.go:439","msg":"Fast updating node status as it just became ready","v":0} 172.20.0.2: {"ts":1758106702168.9478,"caller":"kubelet/kubelet.go:3202","msg":"Trying to delete pod","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"},"podUID":"0079b8ef-61bc-436e-ae79-0a386af0b448"} 172.20.0.2: {"ts":1758106702201.8083,"caller":"util/pod_startup_latency_tracker.go:104","msg":"Observed pod startup duration","v":0,"pod":{"name":"kube-scheduler-talos-default-controlplane-1","namespace":"kube-system"},"podStartSLOduration":0.201783166,"podStartE2EDuration":"201.783166ms","podCreationTimestamp":1758106702201.819,"firstStartedPulling":1758106702201.8193,"lastFinishedPulling":1758106702201.8196,"observedRunningTime":1758106702201.8198,"watchObservedRunningTime":1758106702201.82} 172.20.0.2: {"ts":1758106702241.6372,"caller":"util/pod_startup_latency_tracker.go:104","msg":"Observed pod startup duration","v":0,"pod":{"name":"kube-apiserver-talos-default-controlplane-1","namespace":"kube-system"},"podStartSLOduration":0.241612236,"podStartE2EDuration":"241.612236ms","podCreationTimestamp":1758106702241.6472,"firstStartedPulling":1758106702241.6477,"lastFinishedPulling":1758106702241.6477,"observedRunningTime":1758106702241.648,"watchObservedRunningTime":1758106702241.6482} 172.20.0.2: {"ts":1758106702241.9497,"caller":"util/pod_startup_latency_tracker.go:104","msg":"Observed pod startup duration","v":0,"pod":{"name":"kube-controller-manager-talos-default-controlplane-1","namespace":"kube-system"},"podStartSLOduration":0.241937002,"podStartE2EDuration":"241.937002ms","podCreationTimestamp":1758106702241.9705,"firstStartedPulling":1758106702241.9707,"lastFinishedPulling":1758106702241.9707,"observedRunningTime":1758106702241.971,"watchObservedRunningTime":1758106702241.9712} 172.20.0.2: {"ts":1758106702355.4495,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"3fc7b56fc3907f2a62b1b38f10ac6d2865146017c70a3af57455874d4712969f"} 172.20.0.2: {"ts":1758106702431.3733,"logger":"UnhandledError","caller":"kuberuntime/kuberuntime_manager.go:1449","msg":"Unhandled Error","err":"container kube-proxy start failed in pod kube-proxy-pvlfh_kube-system(e06f16fc-4f9b-47d5-89cb-f0d6f2891f60): CreateContainerConfigError: services have not yet been read at least once, cannot construct envvars"} 172.20.0.2: {"ts":1758106702431.404,"caller":"kubelet/pod_workers.go:1324","msg":"Error syncing pod, skipping","pod":{"name":"kube-proxy-pvlfh","namespace":"kube-system"},"podUID":"e06f16fc-4f9b-47d5-89cb-f0d6f2891f60","err":"failed to \"StartContainer\" for \"kube-proxy\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\"","errCauses":[{"error":"failed to \"StartContainer\" for \"kube-proxy\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\""}]} 172.20.0.2: {"ts":1758106702471.5806,"logger":"UnhandledError","caller":"kuberuntime/kuberuntime_manager.go:1449","msg":"Unhandled Error","err":"init container install-config start failed in pod kube-flannel-bhgc2_kube-system(5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb): CreateContainerConfigError: services have not yet been read at least once, cannot construct envvars"} 172.20.0.2: {"ts":1758106702471.6323,"caller":"kubelet/pod_workers.go:1324","msg":"Error syncing pod, skipping","pod":{"name":"kube-flannel-bhgc2","namespace":"kube-system"},"podUID":"5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb","err":"failed to \"StartContainer\" for \"install-config\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\"","errCauses":[{"error":"failed to \"StartContainer\" for \"install-config\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\""}]} 172.20.0.2: {"ts":1758106703171.0913,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"7ab9f8e1e9db423676249bcbf5c6321989fe0025db751f991ee4a37e3349b118"} 172.20.0.2: {"ts":1758106703171.7766,"logger":"UnhandledError","caller":"kuberuntime/kuberuntime_manager.go:1449","msg":"Unhandled Error","err":"container kube-proxy start failed in pod kube-proxy-pvlfh_kube-system(e06f16fc-4f9b-47d5-89cb-f0d6f2891f60): CreateContainerConfigError: services have not yet been read at least once, cannot construct envvars"} 172.20.0.2: {"ts":1758106703171.8262,"caller":"kubelet/pod_workers.go:1324","msg":"Error syncing pod, skipping","pod":{"name":"kube-proxy-pvlfh","namespace":"kube-system"},"podUID":"e06f16fc-4f9b-47d5-89cb-f0d6f2891f60","err":"failed to \"StartContainer\" for \"kube-proxy\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\"","errCauses":[{"error":"failed to \"StartContainer\" for \"kube-proxy\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\""}]} 172.20.0.2: {"ts":1758106703175.387,"logger":"UnhandledError","caller":"kuberuntime/kuberuntime_manager.go:1449","msg":"Unhandled Error","err":"init container install-config start failed in pod kube-flannel-bhgc2_kube-system(5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb): CreateContainerConfigError: services have not yet been read at least once, cannot construct envvars"} 172.20.0.2: {"ts":1758106703175.4192,"caller":"kubelet/pod_workers.go:1324","msg":"Error syncing pod, skipping","pod":{"name":"kube-flannel-bhgc2","namespace":"kube-system"},"podUID":"5a5b2b3a-c43e-477f-a3dc-de8bdab94eeb","err":"failed to \"StartContainer\" for \"install-config\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\"","errCauses":[{"error":"failed to \"StartContainer\" for \"install-config\" with CreateContainerConfigError: \"services have not yet been read at least once, cannot construct envvars\""}]} 172.20.0.2: {"ts":1758106715095.6794,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"7ab9f8e1e9db423676249bcbf5c6321989fe0025db751f991ee4a37e3349b118"} 172.20.0.2: {"ts":1758106719206.4553,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"aef247c9c003f94ef52aff29ac354d0bb7652c97603f518a0b9de716415f1858"} 172.20.0.2: {"ts":1758106728097.1938,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"4e1d8b1e0e84f8a5a025422b1ca3c4596c7323009b31f6c5c47977bfab9daac8"} 172.20.0.2: {"ts":1758106728112.3425,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"d89f847ed0a3500dd712577fcb52dbbc5169bac1b7017d2c6a3f5f809693806c"} 172.20.0.2: {"ts":1758106728119.877,"caller":"topologymanager/scope.go:117","msg":"RemoveContainer","v":0,"containerID":"56aeb1c5d4785d1d5cc51984dd244fdd4fb672f91e82cfa21e02c92d0f7c3be3"} ================================================ FILE: internal/app/machined/pkg/system/runner/process/process.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package process import ( "fmt" "io" "io/fs" "log" "os" "path" "slices" "syscall" "time" "unsafe" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" "github.com/containerd/containerd/v2/pkg/sys" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-cmd/pkg/cmd/proc/reaper" "kernel.org/pub/linux/libs/security/libcap/cap" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/internal/lastlog" "github.com/siderolabs/talos/internal/pkg/cgroup" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" ) // processRunner is a runner.Runner that runs a process on the host. type processRunner struct { args *runner.Args opts *runner.Options debug bool stop chan struct{} stopped chan struct{} } // NewRunner creates runner.Runner that runs a process on the host. func NewRunner(debug bool, args *runner.Args, setters ...runner.Option) runner.Runner { r := &processRunner{ args: args, opts: runner.DefaultOptions(), debug: debug, stop: make(chan struct{}), stopped: make(chan struct{}), } for _, setter := range setters { setter(r.opts) } return r } // Open implements the Runner interface. func (p *processRunner) Open() error { return nil } // Run implements the Runner interface. func (p *processRunner) Run(eventSink events.Recorder) error { defer close(p.stopped) return p.run(eventSink) } // Stop implements the Runner interface. func (p *processRunner) Stop() error { close(p.stop) <-p.stopped p.stop = make(chan struct{}) p.stopped = make(chan struct{}) return nil } // Close implements the Runner interface. func (p *processRunner) Close() error { return nil } type commandWrapper struct { launcher *cap.Launcher ctty optional.Optional[int] selinuxLabel string cgroupFile *os.File stdin *os.File stdout *os.File stderr *os.File afterStart func() } func dropCaps(droppedCapabilities []string, launcher *cap.Launcher) error { dropCaps := xslices.Map(droppedCapabilities, func(c string) cap.Value { capability, capErr := cap.FromName(c) if capErr != nil { panic(fmt.Errorf("failed to parse capability: %s", capErr)) } return capability }) iab := cap.IABGetProc() if err := iab.SetVector(cap.Bound, true, dropCaps...); err != nil { return fmt.Errorf("failed to set capabilities: %w", err) } launcher.SetIAB(iab) return nil } // This callback is run in the thread before executing child process. func beforeExecCallback(pa *syscall.ProcAttr, data any) error { wrapper, ok := data.(*commandWrapper) if !ok { return fmt.Errorf("failed to get command info") } ctty, cttySet := wrapper.ctty.Get() if cttySet { if pa.Sys == nil { pa.Sys = &syscall.SysProcAttr{} } pa.Sys.Ctty = ctty pa.Sys.Setsid = true pa.Sys.Setctty = true } pa.Files = []uintptr{ wrapper.stdin.Fd(), wrapper.stdout.Fd(), wrapper.stderr.Fd(), } // It is only set in case we should use CgroupFD if wrapper.cgroupFile != nil { if pa.Sys == nil { pa.Sys = &syscall.SysProcAttr{} } pa.Sys.UseCgroupFD = true pa.Sys.CgroupFD = int(wrapper.cgroupFile.Fd()) } // Use /proc/thread-self (Linux 3.17+) to avoid races between current // process threads leading to loss of the domain transition if selinux.IsEnabled() { if wrapper.selinuxLabel != "" { err := os.WriteFile("/proc/thread-self/attr/exec", []byte(wrapper.selinuxLabel), 0o777) if err != nil { log.Fatalf("%s", err) } } else { err := os.WriteFile("/proc/thread-self/attr/exec", []byte(constants.SelinuxLabelUnconfinedService), 0o777) if err != nil { log.Fatalf("%s", err) } } } return nil } //nolint:gocyclo func (p *processRunner) build(extraLogWriter io.Writer) (commandWrapper, error) { wrapper := commandWrapper{} env := slices.Concat([]string{constants.EnvPath}, p.opts.Env, os.Environ()) launcher := cap.NewLauncher(p.args.ProcessArgs[0], p.args.ProcessArgs, env) if p.opts.UID > 0 { launcher.SetUID(int(p.opts.UID)) } // reduce capabilities and assign them to launcher if err := dropCaps(p.opts.DroppedCapabilities, launcher); err != nil { return commandWrapper{}, err } launcher.Callback(beforeExecCallback) // Setup logging. logSink, err := p.opts.LoggingManager.ServiceLog(p.args.ID).Writer() if err != nil { return commandWrapper{}, fmt.Errorf("service log handler: %w", err) } logWriter := io.MultiWriter(logSink, extraLogWriter) if p.debug { logWriter = io.MultiWriter(logSink, log.Writer()) } // As MultiWriter is not a file, we need to create a pipe // Pipe writer is passed to the child process while we read from the read side pr, pw, err := os.Pipe() if err != nil { return commandWrapper{}, err } go func() { defer pr.Close() //nolint:errcheck defer logSink.Close() //nolint:errcheck io.Copy(logWriter, pr) //nolint:errcheck }() // close the writer if we exit early due to an error closeWriter := true afterStartClosers := []io.Closer{pw} closeLogging := func() { for _, closer := range afterStartClosers { closer.Close() //nolint:errcheck } } defer func() { if closeWriter { closeLogging() } }() if p.opts.StdinFile != "" { stdin, err := os.Open(p.opts.StdinFile) if err != nil { return commandWrapper{}, err } wrapper.stdin = stdin afterStartClosers = append(afterStartClosers, stdin) } if p.opts.StdoutFile != "" { stdout, err := os.OpenFile(p.opts.StdoutFile, os.O_WRONLY, 0) if err != nil { return commandWrapper{}, err } wrapper.stdout = stdout afterStartClosers = append(afterStartClosers, stdout) } else { // Do not close the fd in this case, it'll be done by closeLogger wrapper.stdout = pw } if p.opts.StderrFile != "" { stderr, err := os.OpenFile(p.opts.StderrFile, os.O_WRONLY, 0) if err != nil { return commandWrapper{}, err } wrapper.stderr = stderr afterStartClosers = append(afterStartClosers, stderr) } else { // Do not close the fd in this case, it'll be done by closeLogger wrapper.stderr = pw } closeWriter = false wrapper.launcher = launcher wrapper.afterStart = closeLogging wrapper.ctty = p.opts.Ctty wrapper.selinuxLabel = p.opts.SelinuxLabel cgroupFdSupported := false platform, err := platform.CurrentPlatform() if err == nil { cgroupFdSupported = platform.Mode() != runtime.ModeContainer } // cgroupfd is more reliable, use it when possible if cgroups.Mode() == cgroups.Unified && cgroupFdSupported && p.opts.UID == 0 { cg, err := os.Open(path.Join(constants.CgroupMountPath, cgroup.Path(p.opts.CgroupPath))) if err == nil { wrapper.cgroupFile = cg afterStartClosers = append(afterStartClosers, cg) } } return wrapper, nil } // Apply cgroup and OOM score after the process is launched. // //nolint:gocyclo,cyclop func applyProperties(p *processRunner, pid int) error { if p.opts.CgroupPath != "" { path := cgroup.Path(p.opts.CgroupPath) if cgroups.Mode() == cgroups.Unified { cgv2, err := cgroup2.Load(path) if err != nil { return fmt.Errorf("failed to load cgroup %s: %w", path, err) } // No such process error can happen in case the process is terminated before this code runs if err := cgv2.AddProc(uint64(pid)); err != nil { pathError, ok := err.(*fs.PathError) if !ok || pathError.Err != syscall.ESRCH { return fmt.Errorf("failed to move process %s to cgroup: %w", p, err) } } } else { cgv1, err := cgroup1.Load(cgroup1.StaticPath(path)) if err != nil { return fmt.Errorf("failed to load cgroup %s: %w", path, err) } if err := cgv1.Add(cgroup1.Process{ Pid: pid, }); err != nil { pathError, ok := err.(*fs.PathError) if !ok || pathError.Err != syscall.ESRCH { return fmt.Errorf("failed to move process %s to cgroup: %w", p, err) } } } } if p.opts.OOMScoreAdj != 0 { if err := sys.AdjustOOMScore(pid, p.opts.OOMScoreAdj); err != nil { pathError, ok := err.(*fs.PathError) if !ok || pathError.Err != syscall.ENOENT { return fmt.Errorf("failed to change OOMScoreAdj of process %s to %d: %w", p, p.opts.OOMScoreAdj, err) } } } if p.opts.Priority != 0 { if err := syscall.Setpriority(syscall.PRIO_PROCESS, pid, p.opts.Priority); err != nil { return fmt.Errorf("failed to set priority of process %s to %d: %w", p, p.opts.Priority, err) } } if ioPriority, ioPrioritySet := p.opts.IOPriority.Get(); ioPrioritySet { err := setIOPriority(p, pid, ioPriority) if err != nil { return err } } if schedulingPolicy, schedulingPolicySet := p.opts.SchedulingPolicy.Get(); schedulingPolicySet { err := setSchedulingPolicy(p, pid, schedulingPolicy) if err != nil { return err } } return nil } func setIOPriority(p *processRunner, pid int, ioPriority runner.IOPriorityParam) error { if ioPriority.Class > runner.IoprioClassIdle { return fmt.Errorf("failed to set IO priority of process %s: class %d is not valid", p, ioPriority.Class) } if ioPriority.Priority > 7 { return fmt.Errorf("failed to set IO priority of process %s: priority %d is not valid", p, ioPriority.Priority) } classPos := 13 // IOPRIO_CLASS_SHIFT priorityValue := ioPriority.Class< runner.SchedulingPolicyDeadline { return fmt.Errorf("failed to set scheduling policy of process %s: policy %d is not valid", p, schedulingPolicy) } options := struct{ Priority int32 }{ Priority: int32(0), } if _, _, syscallError := syscall.Syscall( syscall.SYS_SCHED_SETSCHEDULER, uintptr(pid), uintptr(schedulingPolicy), uintptr(unsafe.Pointer(&options)), ); syscallError != 0 { return fmt.Errorf("failed to set scheduling policy of process %s to %d: syscall failed with %s", p, schedulingPolicy, syscallError.Error()) } return nil } //nolint:gocyclo func (p *processRunner) run(eventSink events.Recorder) error { cg, err := cgroup.CreateCgroup(p.opts.CgroupPath) if err != nil { return fmt.Errorf("error creating cgroup: %w", err) } defer func() { err := cg.Delete() if err != nil { eventSink(events.StateStopping, "Failed to remove cgroup for %s, %s", p, err) } }() var lastLog lastlog.Writer cmdWrapper, err := p.build(&lastLog) if err != nil { return fmt.Errorf("error building command: %w", err) } defer cmdWrapper.afterStart() notifyCh := make(chan reaper.ProcessInfo, 8) usingReaper := reaper.Notify(notifyCh) if usingReaper { defer reaper.Stop(notifyCh) } pid, err := cmdWrapper.launcher.Launch(&cmdWrapper) if err != nil { return fmt.Errorf("error starting process: %w", err) } if err := applyProperties(p, pid); err != nil { return err } cmdWrapper.afterStart() eventSink(events.StateRunning, "Process %s started with PID %d", p, pid) process, err := os.FindProcess(pid) if err != nil { return fmt.Errorf("could not find process: %w", err) } waitCh := make(chan error) go func() { waitCh <- reaper.ProcessWaitWrapper(usingReaper, notifyCh, process) }() select { case err = <-waitCh: // process exited if err != nil { err = fmt.Errorf("%w (last log %q)", err, lastLog.GetLastLog()) } return err case <-p.stop: // graceful stop the service eventSink(events.StateStopping, "Sending SIGTERM to %s", p) //nolint:errcheck _ = process.Signal(syscall.SIGTERM) } select { case <-waitCh: // stopped process exited return nil case <-time.After(p.opts.GracefulShutdownTimeout): // kill the process eventSink(events.StateStopping, "Sending SIGKILL to %s", p) //nolint:errcheck _ = process.Signal(syscall.SIGKILL) } // wait for process to terminate <-waitCh return nil } func (p *processRunner) String() string { return fmt.Sprintf("Process(%q)", p.args.ProcessArgs) } ================================================ FILE: internal/app/machined/pkg/system/runner/process/process_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package process_test import ( "fmt" "io" "os" "path/filepath" "strconv" "strings" "sync" "syscall" "testing" "time" "github.com/siderolabs/go-cmd/pkg/cmd/proc/reaper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "go.uber.org/goleak" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" ) func MockEventSink(t *testing.T) func(state events.ServiceState, message string, args ...any) { return func(state events.ServiceState, message string, args ...any) { t.Logf("state %s: %s", state, fmt.Sprintf(message, args...)) } } type ProcessSuite struct { suite.Suite tmpDir string runReaper bool loggingManager runtime.LoggingManager } func (suite *ProcessSuite) SetupSuite() { suite.tmpDir = suite.T().TempDir() suite.loggingManager = logging.NewFileLoggingManager(suite.tmpDir) if suite.runReaper { reaper.Run() } } func (suite *ProcessSuite) TearDownSuite() { if suite.runReaper { reaper.Shutdown() } } func (suite *ProcessSuite) TestRunSuccess() { r := process.NewRunner(false, &runner.Args{ ID: "test", ProcessArgs: []string{"/bin/bash", "-c", "exit 0"}, }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink(suite.T()))) // calling stop when Run has finished is no-op suite.Assert().NoError(r.Stop()) } func (suite *ProcessSuite) TestRunLogs() { r := process.NewRunner(false, &runner.Args{ ID: "logtest", ProcessArgs: []string{"/bin/bash", "-c", "echo -n \"Test 1\nTest 2\n\""}, }, runner.WithLoggingManager(suite.loggingManager)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() suite.Assert().NoError(r.Run(MockEventSink(suite.T()))) // the log file is written asynchronously, so we need to wait a bit suite.EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) logContents, err := os.ReadFile(filepath.Join(suite.tmpDir, "logtest.log")) asrt.NoError(err) asrt.Equal([]byte("Test 1\nTest 2\n"), logContents) }, time.Second, 10*time.Millisecond) } func (suite *ProcessSuite) TestRunRestartFailed() { testFile := filepath.Join(suite.tmpDir, "talos-test") //nolint:errcheck _ = os.Remove(testFile) r := restart.New(process.NewRunner(false, &runner.Args{ ID: "restarter", ProcessArgs: []string{"/bin/bash", "-c", "echo \"ran\"; test -f " + testFile}, }, runner.WithLoggingManager(suite.loggingManager)), restart.WithType(restart.UntilSuccess), restart.WithRestartInterval(time.Millisecond)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() var wg sync.WaitGroup wg.Go(func() { suite.Assert().NoError(r.Run(MockEventSink(suite.T()))) }) fetchLog := func() []byte { logFile, err := os.Open(filepath.Join(suite.tmpDir, "restarter.log")) suite.Assert().NoError(err) //nolint:errcheck defer logFile.Close() logContents, err := io.ReadAll(logFile) suite.Assert().NoError(err) return logContents } for range 20 { time.Sleep(100 * time.Millisecond) if len(fetchLog()) > 20 { break } } f, err := os.Create(testFile) suite.Assert().NoError(err) suite.Assert().NoError(f.Close()) wg.Wait() suite.Assert().GreaterOrEqual(len(fetchLog()), 20, fetchLog()) } func (suite *ProcessSuite) TestStopFailingAndRestarting() { testFile := filepath.Join(suite.tmpDir, "talos-test") //nolint:errcheck _ = os.Remove(testFile) r := restart.New(process.NewRunner(false, &runner.Args{ ID: "endless", ProcessArgs: []string{"/bin/bash", "-c", "test -f " + testFile}, }, runner.WithLoggingManager(suite.loggingManager)), restart.WithType(restart.Forever), restart.WithRestartInterval(5*time.Millisecond)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink(suite.T())) }() time.Sleep(40 * time.Millisecond) select { case <-done: suite.Assert().Fail("task should be running") return default: } f, err := os.Create(testFile) suite.Assert().NoError(err) suite.Assert().NoError(f.Close()) time.Sleep(40 * time.Millisecond) select { case <-done: suite.Assert().Fail("task should be running") return default: } suite.Assert().NoError(r.Stop()) <-done } func (suite *ProcessSuite) TestStopSigKill() { r := process.NewRunner(false, &runner.Args{ ID: "nokill", ProcessArgs: []string{"/bin/bash", "-c", "trap -- '' SIGTERM; while :; do :; done"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithGracefulShutdownTimeout(10*time.Millisecond), ) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink(suite.T())) }() time.Sleep(100 * time.Millisecond) suite.Assert().NoError(r.Stop()) <-done } func (suite *ProcessSuite) TestPriority() { if os.Geteuid() != 0 { suite.T().Skip("skipping test, need root privileges") } pidFile := filepath.Join(suite.tmpDir, "talos-test-pid-prio") //nolint:errcheck _ = os.Remove(pidFile) currentPriority, err := syscall.Getpriority(syscall.PRIO_PROCESS, os.Getpid()) suite.Assert().NoError(err) if currentPriority <= 3 { suite.T().Skipf("skipping test, we already have low priority %d", currentPriority) } r := process.NewRunner(false, &runner.Args{ ID: "nokill", ProcessArgs: []string{"/bin/bash", "-c", "echo $BASHPID >> " + pidFile + "; trap -- '' SIGTERM; while :; do :; done"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithGracefulShutdownTimeout(10*time.Millisecond), runner.WithPriority(17), ) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink(suite.T())) }() time.Sleep(10 * time.Millisecond) pidString, err := os.ReadFile(pidFile) suite.Assert().NoError(err) pid, err := strconv.ParseUint(strings.Trim(string(pidString), "\r\n"), 10, 32) suite.Assert().NoError(err) currentPriority, err = syscall.Getpriority(syscall.PRIO_PROCESS, int(pid)) suite.Assert().NoError(err) // 40..1 corresponds to -20..19 since system call interface must reserve -1 for error suite.Assert().Equalf(3, currentPriority, "process priority should be 3 (nice 17), got %d", currentPriority) time.Sleep(1000 * time.Millisecond) suite.Assert().NoError(r.Stop()) <-done } func (suite *ProcessSuite) TestIOPriority() { if os.Geteuid() != 0 { suite.T().Skip("skipping test, need root privileges") } pidFile := filepath.Join(suite.tmpDir, "talos-test-pid-ionice") //nolint:errcheck _ = os.Remove(pidFile) //nolint:errcheck ioprio, _, _ := syscall.Syscall(syscall.SYS_IOPRIO_GET, uintptr(1), uintptr(os.Getpid()), 0) suite.Assert().NotEqual(-1, int(ioprio)) if ioprio>>13 == runner.IoprioClassIdle { suite.T().Skipf("skipping test, we already have idle IO priority %d", ioprio) } r := process.NewRunner(false, &runner.Args{ ID: "nokill", ProcessArgs: []string{"/bin/bash", "-c", "echo $BASHPID >> " + pidFile + "; trap -- '' SIGTERM; while :; do :; done"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithGracefulShutdownTimeout(10*time.Millisecond), runner.WithIOPriority(runner.IoprioClassIdle, 6), ) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink(suite.T())) }() time.Sleep(10 * time.Millisecond) pidString, err := os.ReadFile(pidFile) suite.Assert().NoError(err) pid, err := strconv.ParseUint(strings.Trim(string(pidString), "\r\n"), 10, 32) suite.Assert().NoError(err) //nolint:errcheck ioprio, _, _ = syscall.Syscall(syscall.SYS_IOPRIO_GET, uintptr(1), uintptr(pid), 0) suite.Assert().NotEqual(-1, int(ioprio)) suite.Assert().Equal(runner.IoprioClassIdle<<13+6, int(ioprio)) time.Sleep(10 * time.Millisecond) suite.Assert().NoError(r.Stop()) <-done } func (suite *ProcessSuite) TestSchedulingPolicy() { if os.Geteuid() != 0 { suite.T().Skip("skipping test, need root privileges") } pidFile := filepath.Join(suite.tmpDir, "talos-test-pid-sched") //nolint:errcheck _ = os.Remove(pidFile) pol, _, errno := syscall.Syscall(syscall.SYS_SCHED_GETSCHEDULER, uintptr(os.Getpid()), 0, 0) suite.Assert().Equal(0, int(errno)) if pol == runner.SchedulingPolicyIdle { suite.T().Skipf("skipping test, we already have idle scheduling policy") } r := process.NewRunner(false, &runner.Args{ ID: "nokill", ProcessArgs: []string{"/bin/bash", "-c", "echo $BASHPID >> " + pidFile + "; trap -- '' SIGTERM; while :; do :; done"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithGracefulShutdownTimeout(10*time.Millisecond), runner.WithSchedulingPolicy(runner.SchedulingPolicyIdle), ) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() done := make(chan error, 1) go func() { done <- r.Run(MockEventSink(suite.T())) }() time.Sleep(10 * time.Millisecond) pidString, err := os.ReadFile(pidFile) suite.Assert().NoError(err) pid, err := strconv.ParseUint(strings.Trim(string(pidString), "\r\n"), 10, 32) suite.Assert().NoError(err) pol, _, errno = syscall.Syscall(syscall.SYS_SCHED_GETSCHEDULER, uintptr(pid), 0, 0) suite.Assert().Equal(0, int(errno)) suite.Assert().Equal(runner.SchedulingPolicyIdle, int(pol)) time.Sleep(10 * time.Millisecond) suite.Assert().NoError(r.Stop()) <-done } func TestProcessSuite(t *testing.T) { for _, runReaper := range []bool{true, false} { t.Run(fmt.Sprintf("runReaper=%v", runReaper), func(t *testing.T) { suite.Run(t, &ProcessSuite{runReaper: runReaper}) }, ) } } func TestMain(m *testing.M) { goleak.VerifyTestMain(m) } ================================================ FILE: internal/app/machined/pkg/system/runner/restart/restart.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package restart import ( "fmt" "time" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" ) type restarter struct { wrappedRunner runner.Runner opts *Options stop chan struct{} stopped chan struct{} } // New wraps runner.Runner with restart policy. func New(wrapRunner runner.Runner, opts ...Option) runner.Runner { r := &restarter{ wrappedRunner: wrapRunner, opts: DefaultOptions(), stop: make(chan struct{}), stopped: make(chan struct{}), } for _, opt := range opts { opt(r.opts) } return r } // Options is the functional options struct. type Options struct { // Type describes the service's restart policy. Type Type // RestartInterval is the interval between restarts for failed runs. RestartInterval time.Duration } // Option is the functional option func. type Option func(*Options) // Type represents the service's restart policy. type Type int const ( // Forever will always restart a process. Forever Type = iota // Once will run process exactly once. Once // UntilSuccess will restart process until run succeeds. UntilSuccess ) func (t Type) String() string { switch t { case Forever: return "Forever" case Once: return "Once" case UntilSuccess: return "UntilSuccess" default: return "Unknown" } } // DefaultOptions describes the default options to a runner. func DefaultOptions() *Options { return &Options{ Type: Forever, RestartInterval: 5 * time.Second, } } // WithType sets the type of a service. func WithType(o Type) Option { return func(args *Options) { args.Type = o } } // WithRestartInterval sets the interval between restarts of the failed task. func WithRestartInterval(interval time.Duration) Option { return func(args *Options) { args.RestartInterval = interval } } // Open implements the Runner interface. func (r *restarter) Open() error { return r.wrappedRunner.Open() } // Run implements the Runner interface // //nolint:gocyclo func (r *restarter) Run(eventSink events.Recorder) error { defer close(r.stopped) for { errCh := make(chan error) go func() { errCh <- r.wrappedRunner.Run(eventSink) }() var err error select { case <-r.stop: //nolint:errcheck _ = r.wrappedRunner.Stop() return <-errCh case err = <-errCh: } errStop := r.wrappedRunner.Stop() if errStop != nil { return errStop } switch r.opts.Type { case Once: return err case UntilSuccess: if err == nil { return nil } eventSink(events.StateWaiting, "Error running %s, going to restart until it succeeds: %v", r.wrappedRunner, err) case Forever: if err == nil { eventSink(events.StateWaiting, "Runner %s exited without error, going to restart it", r.wrappedRunner) } else { eventSink(events.StateWaiting, "Error running %v, going to restart forever: %v", r.wrappedRunner, err) } } select { case <-r.stop: eventSink(events.StateStopping, "Aborting restart sequence") return nil case <-time.After(r.opts.RestartInterval): } } } // Stop implements the Runner interface. func (r *restarter) Stop() error { close(r.stop) <-r.stopped return nil } // Close implements the Runner interface. func (r *restarter) Close() error { return r.wrappedRunner.Close() } // String implements the Runner interface. func (r *restarter) String() string { return fmt.Sprintf("Restart(%s, %s)", r.opts.Type, r.wrappedRunner) } ================================================ FILE: internal/app/machined/pkg/system/runner/restart/restart_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package restart_test import ( "errors" "fmt" "log" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" ) type RestartSuite struct { suite.Suite } type MockRunner struct { exitCh chan error times int stop chan struct{} stopped chan struct{} } func (m *MockRunner) Open() error { m.stop = make(chan struct{}) m.stopped = make(chan struct{}) return nil } func (m *MockRunner) Close() error { close(m.exitCh) return nil } func (m *MockRunner) Run(eventSink events.Recorder) error { defer close(m.stopped) select { case err := <-m.exitCh: m.times++ return err case <-m.stop: return nil } } func (m *MockRunner) Stop() error { close(m.stop) <-m.stopped m.stop = make(chan struct{}) m.stopped = make(chan struct{}) return nil } func (m *MockRunner) String() string { return "MockRunner()" } func MockEventSink(state events.ServiceState, message string, args ...any) { log.Printf("state %s: %s", state, fmt.Sprintf(message, args...)) } func (suite *RestartSuite) TestString() { suite.Assert().Equal("Restart(UntilSuccess, MockRunner())", restart.New(&MockRunner{}, restart.WithType(restart.UntilSuccess)).String()) } func (suite *RestartSuite) TestRunOnce() { mock := MockRunner{ exitCh: make(chan error), } r := restart.New(&mock, restart.WithType(restart.Once)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() failed := errors.New("failed") go func() { mock.exitCh <- failed }() suite.Assert().EqualError(r.Run(MockEventSink), failed.Error()) suite.Assert().NoError(r.Stop()) } func (suite *RestartSuite) TestRunOnceStop() { mock := MockRunner{ exitCh: make(chan error), } r := restart.New(&mock, restart.WithType(restart.Once)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() errCh := make(chan error) go func() { errCh <- r.Run(MockEventSink) }() suite.Assert().NoError(r.Stop()) suite.Assert().NoError(<-errCh) } func (suite *RestartSuite) TestRunUntilSuccess() { mock := MockRunner{ exitCh: make(chan error), } r := restart.New(&mock, restart.WithType(restart.UntilSuccess), restart.WithRestartInterval(time.Millisecond)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() failed := errors.New("failed") errCh := make(chan error) go func() { errCh <- r.Run(MockEventSink) }() mock.exitCh <- failed mock.exitCh <- failed mock.exitCh <- failed mock.exitCh <- nil suite.Assert().NoError(<-errCh) suite.Assert().NoError(r.Stop()) suite.Assert().Equal(4, mock.times) } func (suite *RestartSuite) TestRunForever() { mock := MockRunner{ exitCh: make(chan error), } r := restart.New(&mock, restart.WithType(restart.Forever), restart.WithRestartInterval(time.Millisecond)) suite.Assert().NoError(r.Open()) defer func() { suite.Assert().NoError(r.Close()) }() failed := errors.New("failed") errCh := make(chan error) go func() { errCh <- r.Run(MockEventSink) }() mock.exitCh <- failed mock.exitCh <- nil mock.exitCh <- failed mock.exitCh <- nil select { case <-errCh: suite.Assert().Fail("runner should be still running") default: } suite.Assert().NoError(r.Stop()) suite.Assert().NoError(<-errCh) suite.Assert().Equal(4, mock.times) } func TestRestartSuite(t *testing.T) { suite.Run(t, new(RestartSuite)) } ================================================ FILE: internal/app/machined/pkg/system/runner/runner.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package runner provides a runner for running services. package runner import ( "fmt" "io" "time" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/oci" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Runner describes the requirements for running a process. type Runner interface { fmt.Stringer Open() error Run(events.Recorder) error Stop() error Close() error } // Args represents the required options for services. type Args struct { ID string ProcessArgs []string } // IOPriorityParam represents the combination of IO scheduling class and priority. type IOPriorityParam struct { Class uint Priority uint } // Options is the functional options struct. type Options struct { // LoggingManager provides service log handling. LoggingManager runtime.LoggingManager // Env describes the service's environment variables. Elements should be in // the format Env []string // ContainerdAddress is containerd socket address. ContainerdAddress string // ContainerOpts describes the container options. ContainerOpts []containerd.NewContainerOpts // OCISpecOpts describes the OCI spec options. OCISpecOpts []oci.SpecOpts // ContainerImage is the container's image. ContainerImage string // Namespace is the containerd namespace. Namespace string // GracefulShutdownTimeout is the time to wait for process to exit after SIGTERM // before sending SIGKILL GracefulShutdownTimeout time.Duration // Stdin is the process standard input. Stdin io.ReadSeeker // Specify an oom_score_adj for the process. OOMScoreAdj int // CgroupPath (optional) sets the cgroup path to use CgroupPath string // OverrideSeccompProfile default Linux seccomp profile. OverrideSeccompProfile func(*specs.LinuxSeccomp) // DroppedCapabilities is the list of capabilities to drop. DroppedCapabilities []string // SelinuxLabel is the SELinux label to be assigned SelinuxLabel string // StdinFile is the path to the file to use as stdin. StdinFile string // StdoutFile is the path to the file to use as stdout. StdoutFile string // StderrFile is the path to the file to use as stderr. StderrFile string // Ctty is the controlling tty. Ctty optional.Optional[int] // UID is the user id of the process. UID uint32 // Priority is the niceness value of the process. Priority int // IOPriority is the IO priority value and class of the process. IOPriority optional.Optional[IOPriorityParam] // SchedulingPolicy is the scheduling policy of the process. SchedulingPolicy optional.Optional[uint] } // Option is the functional option func. type Option func(*Options) // DefaultOptions describes the default options to a runner. func DefaultOptions() *Options { return &Options{ LoggingManager: logging.NewNullLoggingManager(), Env: []string{}, Namespace: constants.SystemContainerdNamespace, GracefulShutdownTimeout: 10 * time.Second, ContainerdAddress: constants.CRIContainerdAddress, Stdin: nil, OOMScoreAdj: 0, } } // WithEnv sets the environment variables of a service. func WithEnv(o []string) Option { return func(args *Options) { args.Env = o } } // WithNamespace sets the tar file to load. func WithNamespace(o string) Option { return func(args *Options) { args.Namespace = o } } // WithContainerdAddress sets the containerd socket path. func WithContainerdAddress(a string) Option { return func(args *Options) { args.ContainerdAddress = a } } // WithContainerImage sets the image ref. func WithContainerImage(o string) Option { return func(args *Options) { args.ContainerImage = o } } // WithContainerOpts sets the containerd container options. func WithContainerOpts(o ...containerd.NewContainerOpts) Option { return func(args *Options) { args.ContainerOpts = o } } // WithOCISpecOpts sets the OCI spec options. func WithOCISpecOpts(o ...oci.SpecOpts) Option { return func(args *Options) { args.OCISpecOpts = o } } // WithLoggingManager sets the LoggingManager option. func WithLoggingManager(manager runtime.LoggingManager) Option { return func(args *Options) { args.LoggingManager = manager } } // WithGracefulShutdownTimeout sets the timeout for the task to terminate before sending SIGKILL. func WithGracefulShutdownTimeout(timeout time.Duration) Option { return func(args *Options) { args.GracefulShutdownTimeout = timeout } } // WithStdin sets the standard input. func WithStdin(stdin io.ReadSeeker) Option { return func(args *Options) { args.Stdin = stdin } } // WithOOMScoreAdj sets the oom_score_adj. func WithOOMScoreAdj(score int) Option { return func(args *Options) { args.OOMScoreAdj = score } } // WithCgroupPath sets the cgroup path. func WithCgroupPath(path string) Option { return func(args *Options) { args.CgroupPath = path } } // WithSelinuxLabel sets the SELinux label. func WithSelinuxLabel(label string) Option { return func(args *Options) { args.SelinuxLabel = label } } // WithCustomSeccompProfile sets the function to override seccomp profile. func WithCustomSeccompProfile(override func(*specs.LinuxSeccomp)) Option { return func(args *Options) { args.OverrideSeccompProfile = override } } // WithDroppedCapabilities sets the list of capabilities to drop. func WithDroppedCapabilities(caps map[string]struct{}) Option { return func(args *Options) { args.DroppedCapabilities = maps.Keys(caps) } } // WithStdinFile sets the path to the file to use as stdin. func WithStdinFile(path string) Option { return func(args *Options) { args.StdinFile = path } } // WithStdoutFile sets the path to the file to use as stdout. func WithStdoutFile(path string) Option { return func(args *Options) { args.StdoutFile = path } } // WithStderrFile sets the path to the file to use as stderr. func WithStderrFile(path string) Option { return func(args *Options) { args.StdoutFile = path } } // WithCtty sets the controlling tty. func WithCtty(ctty int) Option { return func(args *Options) { args.Ctty = optional.Some(ctty) } } // WithUID sets the user id of the process. func WithUID(uid uint32) Option { return func(args *Options) { args.UID = uid } } // WithPriority sets the priority of the process. func WithPriority(priority int) Option { return func(args *Options) { args.Priority = priority } } const ( // IoprioClassNone represents IOPRIO_CLASS_NONE. IoprioClassNone = iota // IoprioClassRt represents IOPRIO_CLASS_RT. IoprioClassRt // IoprioClassBe represents IOPRIO_CLASS_BE. IoprioClassBe // IoprioClassIdle represents IOPRIO_CLASS_IDLE. IoprioClassIdle ) // WithIOPriority sets the IO priority and class of the process. func WithIOPriority(class, priority uint) Option { return func(args *Options) { args.IOPriority = optional.Some(IOPriorityParam{ Class: class, Priority: priority, }) } } const ( // SchedulingPolicyNormal represents SCHED_NORMAL. SchedulingPolicyNormal = iota // SchedulingPolicyFIFO represents SCHED_FIFO. SchedulingPolicyFIFO // SchedulingPolicyRR represents SCHED_RR. SchedulingPolicyRR // SchedulingPolicyBatch represents SCHED_BATCH. SchedulingPolicyBatch // SchedulingPolicyIsoUnimplemented represents SCHED_ISO. SchedulingPolicyIsoUnimplemented // SchedulingPolicyIdle represents SCHED_IDLE. SchedulingPolicyIdle // SchedulingPolicyDeadline represents SCHED_DEADLINE. SchedulingPolicyDeadline ) // WithSchedulingPolicy sets the scheduling policy of the process. func WithSchedulingPolicy(policy uint) Option { return func(args *Options) { args.SchedulingPolicy = optional.Some(policy) } } ================================================ FILE: internal/app/machined/pkg/system/runner/runner_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runner_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: internal/app/machined/pkg/system/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "context" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/pkg/conditions" ) // Service is an interface describing a system service. type Service interface { // ID is the service id. ID(runtime.Runtime) string // PreFunc is invoked before a runner is created PreFunc(context.Context, runtime.Runtime) error // Runner creates runner for the service Runner(runtime.Runtime) (runner.Runner, error) // PostFunc is invoked after a runner is closed. PostFunc(runtime.Runtime, events.ServiceState) error // Condition describes the conditions under which a service should // start. Condition(runtime.Runtime) conditions.Condition // DependsOn returns list of service IDs this service depends on. DependsOn(runtime.Runtime) []string // Volumes returns a list of volume IDs the service needs. Volumes(runtime.Runtime) []string } // HealthcheckedService is a service which provides health check. type HealthcheckedService interface { // HealtFunc provides function that checks health of the service HealthFunc(runtime.Runtime) health.Check // HealthSettings returns settings for the health check HealthSettings(runtime.Runtime) *health.Settings } // APIStartableService is a service which allows to be started via API. type APIStartableService interface { APIStartAllowed(runtime.Runtime) bool } // APIStoppableService is a service which allows to be stopped via API. type APIStoppableService interface { APIStopAllowed(runtime.Runtime) bool } // APIRestartableService is a service which allows to be restarted via API. type APIRestartableService interface { APIRestartAllowed(runtime.Runtime) bool } ================================================ FILE: internal/app/machined/pkg/system/service_events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "context" "fmt" "sync" "time" "github.com/siderolabs/talos/pkg/conditions" ) // StateEvent is a service event (e.g. 'up', 'down'). type StateEvent string // Service event list. const ( StateEventUp = StateEvent("up") StateEventDown = StateEvent("down") StateEventFinished = StateEvent("finished") ) type serviceCondition struct { mu sync.Mutex waitingRegister bool instance *singleton event StateEvent service string } func (sc *serviceCondition) Wait(ctx context.Context) error { sc.instance.mu.Lock() svcrunner := sc.instance.state[sc.service] sc.instance.mu.Unlock() if svcrunner == nil { return sc.waitRegister(ctx) } return sc.waitEvent(ctx, svcrunner) } func (sc *serviceCondition) waitEvent(ctx context.Context, svcrunner *ServiceRunner) error { notifyCh := make(chan struct{}, 1) svcrunner.Subscribe(sc.event, notifyCh) defer svcrunner.Unsubscribe(sc.event, notifyCh) select { case <-ctx.Done(): return ctx.Err() case <-notifyCh: return nil } } func (sc *serviceCondition) waitRegister(ctx context.Context) error { sc.mu.Lock() sc.waitingRegister = true sc.mu.Unlock() ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() var svcrunner *ServiceRunner for { sc.instance.mu.Lock() svcrunner = sc.instance.state[sc.service] sc.instance.mu.Unlock() if svcrunner != nil { break } select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } } sc.mu.Lock() sc.waitingRegister = false sc.mu.Unlock() return sc.waitEvent(ctx, svcrunner) } func (sc *serviceCondition) String() string { sc.mu.Lock() waitingRegister := sc.waitingRegister sc.mu.Unlock() if waitingRegister { return fmt.Sprintf("service %q to be registered", sc.service) } return fmt.Sprintf("service %q to be %q", sc.service, string(sc.event)) } // WaitForService waits for service to reach some state event. func WaitForService(event StateEvent, service string) conditions.Condition { return waitForService(instance, event, service) } func waitForService(instance *singleton, event StateEvent, service string) conditions.Condition { return &serviceCondition{ instance: instance, event: event, service: service, } } ================================================ FILE: internal/app/machined/pkg/system/service_runner.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "context" "errors" "fmt" "log" "slices" "sync" "time" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/pkg/conditions" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // WaitConditionCheckInterval is time between checking for wait condition // description changes. // // Exposed here for unit-tests to override. var WaitConditionCheckInterval = time.Second // ServiceRunner wraps the state of the service (running, stopped, ...). type ServiceRunner struct { mu sync.Mutex runtime runtime.Runtime service Service id string instance *singleton state events.ServiceState events events.ServiceEvents healthState health.State stateSubscribers map[StateEvent][]chan<- struct{} stopCh chan struct{} } // NewServiceRunner creates new ServiceRunner around Service instance. func NewServiceRunner(instance *singleton, service Service, runtime runtime.Runtime) *ServiceRunner { return &ServiceRunner{ service: service, instance: instance, runtime: runtime, id: service.ID(runtime), state: events.StateInitialized, stateSubscribers: make(map[StateEvent][]chan<- struct{}), stopCh: make(chan struct{}, 1), } } // GetState implements events.Recorder. func (svcrunner *ServiceRunner) GetState() events.ServiceState { svcrunner.mu.Lock() defer svcrunner.mu.Unlock() return svcrunner.state } // UpdateState implements events.Recorder. func (svcrunner *ServiceRunner) UpdateState(ctx context.Context, newstate events.ServiceState, message string, args ...any) { svcrunner.mu.Lock() event := events.ServiceEvent{ Message: fmt.Sprintf(message, args...), State: newstate, Timestamp: time.Now(), } svcrunner.state = newstate svcrunner.events.Push(event) log.Printf("service[%s](%s): %s", svcrunner.id, svcrunner.state, event.Message) isUp := svcrunner.inStateLocked(StateEventUp) isDown := svcrunner.inStateLocked(StateEventDown) isFinished := svcrunner.inStateLocked(StateEventFinished) svcrunner.mu.Unlock() if svcrunner.runtime != nil { svcrunner.runtime.Events().Publish(ctx, event.AsProto(svcrunner.id)) } if isUp { svcrunner.notifyEvent(StateEventUp) } if isDown { svcrunner.notifyEvent(StateEventDown) } if isFinished { svcrunner.notifyEvent(StateEventFinished) } } func (svcrunner *ServiceRunner) healthUpdate(ctx context.Context, change health.StateChange) { svcrunner.mu.Lock() // service not running, suppress event if svcrunner.state != events.StateRunning { svcrunner.mu.Unlock() return } var message string if *change.New.Healthy { message = "Health check successful" } else { message = fmt.Sprintf("Health check failed: %s", change.New.LastMessage) } event := events.ServiceEvent{ Message: message, State: svcrunner.state, Health: change.New, Timestamp: time.Now(), } svcrunner.events.Push(event) log.Printf("service[%s](%s): %s", svcrunner.id, svcrunner.state, event.Message) isUp := svcrunner.inStateLocked(StateEventUp) svcrunner.mu.Unlock() if isUp { svcrunner.notifyEvent(StateEventUp) } if svcrunner.runtime != nil { svcrunner.runtime.Events().Publish(ctx, event.AsProto(svcrunner.id)) } } // GetEventHistory returns history of events for this service. func (svcrunner *ServiceRunner) GetEventHistory(count int) []events.ServiceEvent { svcrunner.mu.Lock() defer svcrunner.mu.Unlock() return svcrunner.events.Get(count) } func (svcrunner *ServiceRunner) waitFor(ctx context.Context, condition conditions.Condition) error { description := condition.String() svcrunner.UpdateState(ctx, events.StateWaiting, "Waiting for %s", description) errCh := make(chan error) go func() { errCh <- condition.Wait(ctx) }() ticker := time.NewTicker(WaitConditionCheckInterval) defer ticker.Stop() // update state if condition description changes (some conditions are satisfied) for { select { case err := <-errCh: return err case <-ticker.C: newDescription := condition.String() if newDescription != description && newDescription != "" { description = newDescription svcrunner.UpdateState(ctx, events.StateWaiting, "Waiting for %s", description) } } } } // ErrSkip is returned by Run when service is skipped. var ErrSkip = errors.New("service skipped") type volumeRequest struct { volumeID string requestID string requester string } // Run initializes the service and runs it. // // Run returns an error when a service stops. // // Run should be run in a goroutine. // //nolint:gocyclo func (svcrunner *ServiceRunner) Run(notifyChannels ...chan<- struct{}) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() go func() { select { case <-ctx.Done(): return case <-svcrunner.stopCh: cancel() } }() svcrunner.UpdateState(ctx, events.StateStarting, "Starting service") for _, notifyCh := range notifyChannels { close(notifyCh) } condition := svcrunner.service.Condition(svcrunner.runtime) if dependencies := svcrunner.service.DependsOn(svcrunner.runtime); len(dependencies) > 0 { serviceConditions := xslices.Map(dependencies, func(dep string) conditions.Condition { return waitForService(instance, StateEventUp, dep) }) serviceDependencies := conditions.WaitForAll(serviceConditions...) condition = conditions.WaitForAll(serviceDependencies, condition) } if volumeIDs := svcrunner.service.Volumes(svcrunner.runtime); len(volumeIDs) > 0 { // create volume mount request for each volume requested volumeRequests := make([]volumeRequest, 0, len(volumeIDs)) for _, volumeID := range volumeIDs { requester := "service/" + svcrunner.id requestID := requester + "-" + volumeID volumeRequests = append(volumeRequests, volumeRequest{volumeID: volumeID, requestID: requestID, requester: requester}) } condition = conditions.WaitForAll(WaitForVolumesToBeMounted(svcrunner.runtime.State().V1Alpha2().Resources(), volumeRequests), condition) // cleanup volume mounts defer func() { cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), 10*time.Second) defer cleanupCancel() if err := svcrunner.deleteVolumeMountRequest(cleanupCtx, volumeRequests); err != nil { svcrunner.UpdateState(cleanupCtx, events.StateFailed, "Failed to clean up volumes: %v", err) } }() } if condition != nil { if err := svcrunner.waitFor(ctx, condition); err != nil { return fmt.Errorf("condition failed: %w", err) } } svcrunner.UpdateState(ctx, events.StatePreparing, "Running pre state") if err := svcrunner.service.PreFunc(ctx, svcrunner.runtime); err != nil { return fmt.Errorf("failed to run pre stage: %w", err) } svcrunner.UpdateState(ctx, events.StatePreparing, "Creating service runner") runnr, err := svcrunner.service.Runner(svcrunner.runtime) if err != nil { return fmt.Errorf("failed to create runner: %w", err) } defer func() { // PostFunc passes in the state so that we can take actions that depend on the outcome of the run state := svcrunner.GetState() if err := svcrunner.service.PostFunc(svcrunner.runtime, state); err != nil { svcrunner.UpdateState(ctx, events.StateFailed, "Failed to run post stage: %v", err) } }() if runnr == nil { return ErrSkip } if err := svcrunner.run(ctx, runnr); err != nil { return fmt.Errorf("failed running service: %w", err) } return nil } //nolint:gocyclo func (svcrunner *ServiceRunner) run(ctx context.Context, runnr runner.Runner) error { if runnr == nil { // special case - run nothing (TODO: we should handle it better, e.g. in PreFunc) return nil } if err := runnr.Open(); err != nil { return fmt.Errorf("error opening runner: %w", err) } //nolint:errcheck defer runnr.Close() errCh := make(chan error) go func() { errCh <- runnr.Run(func(s events.ServiceState, msg string, args ...any) { svcrunner.UpdateState(ctx, s, msg, args...) if _, healthSupported := svcrunner.service.(HealthcheckedService); healthSupported && s != events.StateRunning { svcrunner.healthState.Update(false, "service not running") } }) }() if healthSvc, ok := svcrunner.service.(HealthcheckedService); ok { var healthWg sync.WaitGroup defer healthWg.Wait() healthWg.Go(func() { //nolint:errcheck health.Run( ctx, healthSvc.HealthSettings(svcrunner.runtime), &svcrunner.healthState, healthSvc.HealthFunc(svcrunner.runtime), ) }) notifyCh := make(chan health.StateChange, 2) svcrunner.healthState.Subscribe(notifyCh) defer svcrunner.healthState.Unsubscribe(notifyCh) healthWg.Go(func() { for { select { case <-ctx.Done(): return case change := <-notifyCh: svcrunner.healthUpdate(ctx, change) } } }) } select { case <-ctx.Done(): err := runnr.Stop() <-errCh if err != nil { return fmt.Errorf("error stopping service: %w", err) } case err := <-errCh: if err != nil { return fmt.Errorf("error running service: %w", err) } } return nil } // Shutdown initiates shutdown of the service runner // // Shutdown completes when Start() returns. func (svcrunner *ServiceRunner) Shutdown() { select { case svcrunner.stopCh <- struct{}{}: default: } } // AsProto returns protobuf struct with the state of the service runner. func (svcrunner *ServiceRunner) AsProto() *machineapi.ServiceInfo { svcrunner.mu.Lock() defer svcrunner.mu.Unlock() return &machineapi.ServiceInfo{ Id: svcrunner.id, State: svcrunner.state.String(), Events: svcrunner.events.AsProto(events.MaxEventsToKeep), Health: svcrunner.healthState.AsProto(), } } // Subscribe to a specific event for this service. // // Channel `ch` should be buffered or it should have listener attached to it, // as event might be delivered before Subscribe() returns. func (svcrunner *ServiceRunner) Subscribe(event StateEvent, ch chan<- struct{}) { svcrunner.mu.Lock() if svcrunner.inStateLocked(event) { svcrunner.mu.Unlock() // svcrunner is already in expected state, notify immediately select { case ch <- struct{}{}: default: } return } svcrunner.stateSubscribers[event] = append(svcrunner.stateSubscribers[event], ch) svcrunner.mu.Unlock() } // Unsubscribe cancels subscription established with Subscribe. func (svcrunner *ServiceRunner) Unsubscribe(event StateEvent, ch chan<- struct{}) { svcrunner.mu.Lock() defer svcrunner.mu.Unlock() channels := svcrunner.stateSubscribers[event] for i := 0; i < len(channels); { if channels[i] == ch { channels[i], channels[len(channels)-1] = channels[len(channels)-1], nil channels = channels[:len(channels)-1] } else { i++ } } svcrunner.stateSubscribers[event] = channels } func (svcrunner *ServiceRunner) notifyEvent(event StateEvent) { svcrunner.mu.Lock() channels := slices.Clone(svcrunner.stateSubscribers[event]) svcrunner.mu.Unlock() for _, ch := range channels { select { case ch <- struct{}{}: default: } } } func (svcrunner *ServiceRunner) inStateLocked(event StateEvent) bool { switch event { case StateEventUp: // up when: // a) either skipped or already finished // b) or running and healthy (if supports health checks) switch svcrunner.state { //nolint:exhaustive case events.StateSkipped, events.StateFinished: return true case events.StateRunning: // check if service supports health checks _, supportsHealth := svcrunner.service.(HealthcheckedService) health := svcrunner.healthState.Get() return !supportsHealth || (health.Healthy != nil && *health.Healthy) default: return false } case StateEventDown: // down when in any of the terminal states switch svcrunner.state { //nolint:exhaustive case events.StateFailed, events.StateFinished, events.StateSkipped: return true default: return false } case StateEventFinished: if svcrunner.state == events.StateFinished { return true } return false default: panic("unsupported event") } } ================================================ FILE: internal/app/machined/pkg/system/service_runner_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system_test import ( "errors" "testing" "time" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/pkg/conditions" ) type ServiceRunnerSuite struct { suite.Suite } func (suite *ServiceRunnerSuite) assertStateSequence(expectedStates []events.ServiceState, sr *system.ServiceRunner) { states := make([]events.ServiceState, 0, 1000) for _, event := range sr.GetEventHistory(1000) { states = append(states, event.State) } suite.Assert().Equal(expectedStates, states) } func (suite *ServiceRunnerSuite) TestFullFlow() { sr := system.NewServiceRunner(system.Services(nil), &MockService{ condition: conditions.None(), }, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateRunning.String() { return retry.ExpectedErrorf("service should be running") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, }, sr) protoService := sr.AsProto() suite.Assert().Equal("MockRunner", protoService.Id) suite.Assert().Equal("Running", protoService.State) suite.Assert().True(protoService.Health.Unknown) suite.Assert().Len(protoService.Events.Events, 5) } func (suite *ServiceRunnerSuite) TestFullFlowHealthy() { sr := system.NewServiceRunner(system.Services(nil), &MockHealthcheckedService{}, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { health := sr.AsProto().Health if health.Unknown || !health.Healthy { return retry.ExpectedErrorf("service should be healthy") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StatePreparing, events.StatePreparing, events.StateRunning, events.StateRunning, // one more notification when service is healthy }, sr) } func (suite *ServiceRunnerSuite) TestFullFlowHealthChanges() { m := MockHealthcheckedService{ MockService: MockService{ condition: conditions.None(), }, } sr := system.NewServiceRunner(system.Services(nil), &m, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { health := sr.AsProto().Health if health.Unknown || !health.Healthy { return retry.ExpectedErrorf("service should be healthy") } return nil })) m.SetHealthy(false) suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { health := sr.AsProto().Health if health.Unknown || health.Healthy { return retry.ExpectedErrorf("service should be not healthy") } return nil })) m.SetHealthy(true) suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { health := sr.AsProto().Health if health.Unknown || !health.Healthy { return retry.ExpectedErrorf("service should be healthy") } return nil })) sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, events.StateRunning, // initial: healthy events.StateRunning, // not healthy events.StateRunning, // once again healthy }, sr) } func (suite *ServiceRunnerSuite) TestWaitingDescriptionChange() { oldWaitConditionCheckInterval := system.WaitConditionCheckInterval system.WaitConditionCheckInterval = 10 * time.Millisecond defer func() { system.WaitConditionCheckInterval = oldWaitConditionCheckInterval }() cond1 := NewMockCondition("cond1") cond2 := NewMockCondition("cond2") sr := system.NewServiceRunner(system.Services(nil), &MockService{ condition: conditions.WaitForAll(cond1, cond2), }, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateWaiting.String() { return retry.ExpectedErrorf("service should be waiting") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } close(cond1.done) suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { events := sr.AsProto().Events.Events lastMsg := events[len(events)-1].Msg if lastMsg != "Waiting for cond2" { return retry.ExpectedErrorf("service should be waiting on 2nd condition") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } close(cond2.done) suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateRunning.String() { return retry.ExpectedErrorf("service should be running") } return nil })) sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, }, sr) events := sr.GetEventHistory(10000) suite.Assert().Equal("Waiting for cond1, cond2", events[1].Message) suite.Assert().Equal("Waiting for cond2", events[2].Message) } func (suite *ServiceRunnerSuite) TestPreStageFail() { svc := &MockService{ preError: errors.New("pre failed"), } sr := system.NewServiceRunner(system.Services(nil), svc, nil) err := sr.Run() suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StatePreparing, }, sr) suite.Assert().EqualError(err, "failed to run pre stage: pre failed") } func (suite *ServiceRunnerSuite) TestRunnerStageFail() { svc := &MockService{ runnerError: errors.New("runner failed"), } sr := system.NewServiceRunner(system.Services(nil), svc, nil) err := sr.Run() suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StatePreparing, events.StatePreparing, }, sr) suite.Assert().EqualError(err, "failed to create runner: runner failed") } func (suite *ServiceRunnerSuite) TestRunnerStageSkipped() { svc := &MockService{ nilRunner: true, } sr := system.NewServiceRunner(system.Services(nil), svc, nil) err := sr.Run() suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StatePreparing, events.StatePreparing, }, sr) suite.Assert().ErrorIs(err, system.ErrSkip) } func (suite *ServiceRunnerSuite) TestAbortOnCondition() { svc := &MockService{ condition: conditions.WaitForFileToExist("/doesntexistever"), } sr := system.NewServiceRunner(system.Services(nil), svc, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateWaiting.String() { return retry.ExpectedErrorf("service should be waiting") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } sr.Shutdown() suite.Assert().EqualError(<-errCh, "condition failed: context canceled") suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, }, sr) } func (suite *ServiceRunnerSuite) TestPostStateFail() { svc := &MockService{ condition: conditions.None(), postError: errors.New("post failed"), } sr := system.NewServiceRunner(system.Services(nil), svc, nil) errCh := make(chan error) runNotify := make(chan struct{}) go func() { errCh <- sr.Run(runNotify) }() <-runNotify sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, events.StateFailed, }, sr) } func (suite *ServiceRunnerSuite) TestRunFail() { runner := &MockRunner{exitCh: make(chan error)} svc := &MockService{runner: runner} sr := system.NewServiceRunner(system.Services(nil), svc, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() runner.exitCh <- errors.New("run failed") suite.Assert().EqualError(<-errCh, "failed running service: error running service: run failed") suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StatePreparing, events.StatePreparing, events.StateRunning, }, sr) } func (suite *ServiceRunnerSuite) TestFullFlowRestart() { sr := system.NewServiceRunner(system.Services(nil), &MockService{ condition: conditions.None(), }, nil) errCh := make(chan error) go func() { errCh <- sr.Run() }() suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateRunning.String() { return retry.ExpectedErrorf("service should be running") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } sr.Shutdown() suite.Assert().NoError(<-errCh) notifyCh := make(chan struct{}) go func() { errCh <- sr.Run(notifyCh) }() <-notifyCh suite.Require().NoError(retry.Constant(time.Minute, retry.WithUnits(10*time.Millisecond)).Retry(func() error { state := sr.AsProto().State if state != events.StateRunning.String() { return retry.ExpectedErrorf("service should be running") } return nil })) select { case <-errCh: suite.Require().Fail("service running should be still running") default: } sr.Shutdown() suite.Assert().NoError(<-errCh) suite.assertStateSequence([]events.ServiceState{ events.StateStarting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, events.StateStarting, events.StateWaiting, events.StatePreparing, events.StatePreparing, events.StateRunning, }, sr) } func TestServiceRunnerSuite(t *testing.T) { suite.Run(t, new(ServiceRunnerSuite)) } ================================================ FILE: internal/app/machined/pkg/system/services/apid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:golint package services import ( "context" "errors" "fmt" "net" "os" "path/filepath" "strings" "time" "github.com/containerd/containerd/v2/pkg/cap" "github.com/containerd/containerd/v2/pkg/oci" "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/server" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-debug" "google.golang.org/grpc" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) var _ system.HealthcheckedService = (*APID)(nil) // APID implements the Service interface. It serves as the concrete type with // the required methods. type APID struct { runtimeServer *grpc.Server } // ID implements the Service interface. func (o *APID) ID(r runtime.Runtime) string { return "apid" } // apidResourceFilter filters access to COSI state for apid. // //nolint:gocyclo func apidResourceFilter(_ context.Context, access state.Access) error { if !access.Verb.Readonly() { return errors.New("write access denied") } switch { case access.ResourceNamespace == runtimeres.NamespaceName && access.ResourceType == runtimeres.APIServiceConfigType && access.ResourceID == runtimeres.APIServiceConfigID: // allowed, contains apid service configuration case access.ResourceNamespace == secrets.NamespaceName && access.ResourceType == secrets.APIType && access.ResourceID == secrets.APIID: // allowed, contains apid certificates case access.ResourceNamespace == network.NamespaceName && access.ResourceType == network.NodeAddressType: // allowed, contains local node addresses case access.ResourceNamespace == network.NamespaceName && access.ResourceType == network.HostnameStatusType: // allowed, contains local node hostname default: return errors.New("access denied") } return nil } // PreFunc implements the Service interface. func (o *APID) PreFunc(ctx context.Context, r runtime.Runtime) error { // filter apid access to make sure apid can only access its certificates resources := state.Filter(r.State().V1Alpha2().Resources(), apidResourceFilter) // ensure socket dir exists if err := os.MkdirAll(filepath.Dir(constants.APIRuntimeSocketPath), 0o750); err != nil { return err } // set the final leaf to be world-executable to make apid connect to the socket if err := os.Chmod(filepath.Dir(constants.APIRuntimeSocketPath), 0o751); err != nil { return err } // clean up the socket if it already exists (important for Talos in a container) if err := os.RemoveAll(constants.APIRuntimeSocketPath); err != nil { return err } listener, err := (&net.ListenConfig{}).Listen(ctx, "unix", constants.APIRuntimeSocketPath) if err != nil { return err } if err := selinux.SetLabel(constants.APIRuntimeSocketPath, constants.APIRuntimeSocketLabel); err != nil { return err } // chown the socket path to make it accessible to the apid if err := os.Chown(constants.APIRuntimeSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil { return err } o.runtimeServer = grpc.NewServer( grpc.SharedWriteBuffer(true), ) v1alpha1.RegisterStateServer(o.runtimeServer, server.NewState(resources)) go o.runtimeServer.Serve(listener) //nolint:errcheck return prepareRootfs(o.ID(r)) } // PostFunc implements the Service interface. func (o *APID) PostFunc(runtime.Runtime, events.ServiceState) (err error) { o.runtimeServer.Stop() return os.RemoveAll(constants.APIRuntimeSocketPath) } // Condition implements the Service interface. func (o *APID) Condition(r runtime.Runtime) conditions.Condition { return conditions.WaitForAll( secrets.NewAPIReadyCondition(r.State().V1Alpha2().Resources()), runtimeres.NewAPIServiceConfigCondition(r.State().V1Alpha2().Resources()), ) } // DependsOn implements the Service interface. func (o *APID) DependsOn(runtime.Runtime) []string { return []string{"containerd"} } // Volumes implements the Service interface. func (o *APID) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. // //nolint:gocyclo func (o *APID) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := runner.Args{ ID: o.ID(r), ProcessArgs: []string{ "/apid", }, } // Set the mounts. mounts := []specs.Mount{ {Type: "bind", Destination: "/etc/ssl", Source: "/etc/ssl", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: filepath.Dir(constants.MachineSocketPath), Source: filepath.Dir(constants.MachineSocketPath), Options: []string{"rbind", "ro"}}, {Type: "bind", Destination: filepath.Dir(constants.APIRuntimeSocketPath), Source: filepath.Dir(constants.APIRuntimeSocketPath), Options: []string{"rbind", "rw"}}, } mounts = bindMountContainerMarker(mounts) env := []string{ constants.EnvTcellMinimizeEnvironment, constants.EnvGRPCEnforccceALPNEnabled, constants.EnvApidGomemlimit(), } for _, value := range environment.Get(r.Config()) { key, _, _ := strings.Cut(value, "=") switch strings.ToLower(key) { // explicitly exclude proxy variables from apid since this will // negatively impact grpc connections. // ref: https://github.com/grpc/grpc-go/blob/0f32486dd3c9bc29705535bd7e2e43801824cbc4/clientconn.go#L199-L206 // ref: https://github.com/grpc/grpc-go/blob/63ae68c9686cc0dd26c4f7476d66bb2f5c31789f/proxy.go#L118-L144 case "no_proxy": case "http_proxy": case "https_proxy": default: env = append(env, value) } } if debug.RaceEnabled { env = append(env, constants.EnvGoraceHaltOnError) } if fipsmode.Strict() { env = append(env, constants.EnvFIPS140ModeStrict) } var debug bool if r.Config() != nil { debug = r.Config().Debug() } return restart.New(containerd.NewRunner( debug, &args, runner.WithLoggingManager(r.Logging()), runner.WithContainerdAddress(constants.SystemContainerdAddress), runner.WithEnv(env), runner.WithGracefulShutdownTimeout(15*time.Second), runner.WithCgroupPath(constants.CgroupApid), runner.WithSelinuxLabel(constants.SelinuxLabelApid), runner.WithOCISpecOpts( oci.WithDroppedCapabilities(cap.Known()), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithMounts(mounts), oci.WithRootFSPath(filepath.Join(constants.SystemLibexecPath, o.ID(r))), oci.WithRootFSReadonly(), oci.WithUser(fmt.Sprintf("%d:%d", constants.ApidUserID, constants.ApidUserID)), ), runner.WithOOMScoreAdj(-998), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (o *APID) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { var d net.Dialer conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", "127.0.0.1", constants.ApidPort)) if err != nil { return err } return conn.Close() } } // HealthSettings implements the HealthcheckedService interface. func (o *APID) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/auditd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "github.com/siderolabs/talos/internal/app/auditd" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/pkg/conditions" ) const auditdServiceID = "auditd" var _ system.HealthcheckedService = (*Auditd)(nil) // Auditd implements the Service interface. It serves as the concrete type with // the required methods. type Auditd struct{} // ID implements the Service interface. func (s *Auditd) ID(runtime.Runtime) string { return auditdServiceID } // PreFunc implements the Service interface. func (s *Auditd) PreFunc(context.Context, runtime.Runtime) error { return nil } // PostFunc implements the Service interface. func (s *Auditd) PostFunc(runtime.Runtime, events.ServiceState) error { return nil } // Condition implements the Service interface. func (s *Auditd) Condition(runtime.Runtime) conditions.Condition { return nil } // DependsOn implements the Service interface. func (s *Auditd) DependsOn(runtime.Runtime) []string { return nil } // Volumes implements the Service interface. func (s *Auditd) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (s *Auditd) Runner(r runtime.Runtime) (runner.Runner, error) { return goroutine.NewRunner(r, auditdServiceID, auditd.Main, runner.WithLoggingManager(r.Logging())), nil } // HealthFunc implements the HealthcheckedService interface. func (s *Auditd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { return nil } } // HealthSettings implements the HealthcheckedService interface. func (s *Auditd) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/containerd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "fmt" "path/filepath" containerd "github.com/containerd/containerd/v2/client" "google.golang.org/grpc/health/grpc_health_v1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" ) var _ system.HealthcheckedService = (*Containerd)(nil) // Containerd implements the Service interface. It serves as the concrete type with // the required methods. type Containerd struct { // client is a lazy-initialized containerd client. It should be accessed using the Client() method. client *containerd.Client } // Client lazy-initializes the containerd client if needed and returns it. func (c *Containerd) Client() (*containerd.Client, error) { if c.client != nil { return c.client, nil } client, err := containerd.New(constants.SystemContainerdAddress) if err != nil { return nil, err } c.client = client return c.client, err } // ID implements the Service interface. func (c *Containerd) ID(runtime.Runtime) string { return "containerd" } // PreFunc implements the Service interface. func (c *Containerd) PreFunc(context.Context, runtime.Runtime) error { return nil } // PostFunc implements the Service interface. func (c *Containerd) PostFunc(runtime.Runtime, events.ServiceState) (err error) { if c.client != nil { return c.client.Close() } return nil } // Condition implements the Service interface. func (c *Containerd) Condition(runtime.Runtime) conditions.Condition { return nil } // DependsOn implements the Service interface. func (c *Containerd) DependsOn(runtime.Runtime) []string { return nil } // Volumes implements the Service interface. func (c *Containerd) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (c *Containerd) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := &runner.Args{ ID: c.ID(r), ProcessArgs: []string{ "/bin/containerd", "--address", constants.SystemContainerdAddress, "--state", filepath.Join(constants.SystemRunPath, "containerd"), "--root", filepath.Join(constants.SystemVarPath, "lib", "containerd"), }, } debug := false if r.Config() != nil { debug = r.Config().Debug() } return restart.New(process.NewRunner( debug, args, runner.WithLoggingManager(r.Logging()), runner.WithEnv(append( environment.Get(r.Config()), constants.EnvXDGRuntimeDir, )), runner.WithOOMScoreAdj(-999), runner.WithCgroupPath(constants.CgroupSystemRuntime), runner.WithSelinuxLabel(constants.SelinuxLabelSystemRuntime), runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (c *Containerd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { client, err := c.Client() if err != nil { return err } resp, err := client.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}) if err != nil { return err } if resp.Status != grpc_health_v1.HealthCheckResponse_SERVING { return fmt.Errorf("unexpected serving status: %d", resp.Status) } return nil } } // HealthSettings implements the HealthcheckedService interface. func (c *Containerd) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "fmt" "os" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/defaults" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc/health/grpc_health_v1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) var _ system.HealthcheckedService = (*CRI)(nil) // CRI implements the Service interface. It serves as the concrete type with // the required methods. type CRI struct { // client is a lazy-initialized containerd client. It should be accessed using the Client() method. client *containerd.Client } // Client lazy-initializes the containerd client if needed and returns it. func (c *CRI) Client() (*containerd.Client, error) { if c.client != nil { return c.client, nil } client, err := containerd.New(constants.CRIContainerdAddress) if err != nil { return nil, err } c.client = client return c.client, err } // ID implements the Service interface. func (c *CRI) ID(runtime.Runtime) string { return "cri" } // PreFunc implements the Service interface. func (c *CRI) PreFunc(context.Context, runtime.Runtime) error { return os.MkdirAll(defaults.DefaultRootDir, os.ModeDir) } // PostFunc implements the Service interface. func (c *CRI) PostFunc(runtime.Runtime, events.ServiceState) (err error) { if c.client != nil { return c.client.Close() } return nil } // Condition implements the Service interface. func (c *CRI) Condition(r runtime.Runtime) conditions.Condition { return network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady) } // DependsOn implements the Service interface. func (c *CRI) DependsOn(runtime.Runtime) []string { return nil } // Volumes implements the Service interface. func (c *CRI) Volumes(r runtime.Runtime) []string { volumes := []string{ "/var/lib", "/var/lib/cni", "/var/lib/containerd", "/var/run", "/var/run/lock", } if !r.State().Platform().Mode().InContainer() { volumes = append(volumes, xslices.Map(constants.Overlays, func(target constants.SELinuxLabeledPath) string { return target.Path })..., ) } return volumes } // Runner implements the Service interface. func (c *CRI) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := &runner.Args{ ID: c.ID(r), ProcessArgs: []string{ "/bin/containerd", "--address", constants.CRIContainerdAddress, "--config", constants.CRIContainerdConfig, }, } return restart.New(process.NewRunner( r.Config().Debug(), args, runner.WithLoggingManager(r.Logging()), runner.WithEnv(append( environment.Get(r.Config()), constants.EnvXDGRuntimeDir, )), runner.WithOOMScoreAdj(-500), runner.WithCgroupPath(constants.CgroupPodRuntime), runner.WithSelinuxLabel(constants.SelinuxLabelPodRuntime), runner.WithDroppedCapabilities(constants.DefaultDroppedCapabilities), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (c *CRI) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { client, err := c.Client() if err != nil { return err } resp, err := client.HealthService().Check(ctx, &grpc_health_v1.HealthCheckRequest{}) if err != nil { return err } if resp.Status != grpc_health_v1.HealthCheckResponse_SERVING { return fmt.Errorf("unexpected serving status: %d", resp.Status) } return nil } } // HealthSettings implements the HealthcheckedService interface. func (c *CRI) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/dashboard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:golint package services import ( "context" "fmt" "strings" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/console" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Dashboard implements the Service interface. It serves as the concrete type with // the required methods. type Dashboard struct{} // getCustomConsole returns the custom console parameter value if specified, empty string otherwise. func (d *Dashboard) getCustomConsole() string { consoleParam := procfs.ProcCmdline().Get(constants.KernelParamDashboardConsole).First() return pointer.SafeDeref(consoleParam) } // hasCustomConsole checks if a custom console is specified via kernel parameter. func (d *Dashboard) hasCustomConsole() bool { return d.getCustomConsole() != "" } // getConsoleDevice returns the console device path to use for the dashboard. func (d *Dashboard) getConsoleDevice() (string, error) { consoleName := d.getCustomConsole() if consoleName != "" { // Validate that the console name starts with "tty" if !strings.HasPrefix(consoleName, "tty") || strings.Contains(consoleName, "/") { return "", fmt.Errorf("invalid console name %q: must start with 'tty'", consoleName) } return fmt.Sprintf("/dev/%s", consoleName), nil } // Default to the standard dashboard TTY return fmt.Sprintf("/dev/tty%d", constants.DashboardTTY), nil } // ID implements the Service interface. func (d *Dashboard) ID(_ runtime.Runtime) string { return "dashboard" } // PreFunc implements the Service interface. func (d *Dashboard) PreFunc(_ context.Context, _ runtime.Runtime) error { // Skip TTY switching if a custom console is specified if d.hasCustomConsole() { return nil } return console.Switch(constants.DashboardTTY) } // PostFunc implements the Service interface. func (d *Dashboard) PostFunc(_ runtime.Runtime, _ events.ServiceState) error { // Skip TTY switching if a custom console is specified if d.hasCustomConsole() { return nil } return console.Switch(constants.KernelLogsTTY) } // Condition implements the Service interface. func (d *Dashboard) Condition(_ runtime.Runtime) conditions.Condition { return conditions.WaitForFileToExist(constants.MachineSocketPath) } // DependsOn implements the Service interface. func (d *Dashboard) DependsOn(_ runtime.Runtime) []string { return []string{machinedServiceID} } // Volumes implements the Service interface. func (d *Dashboard) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (d *Dashboard) Runner(r runtime.Runtime) (runner.Runner, error) { tty, err := d.getConsoleDevice() if err != nil { return nil, fmt.Errorf("failed to determine console device: %w", err) } return restart.New(process.NewRunner(false, &runner.Args{ ID: d.ID(r), ProcessArgs: []string{"/sbin/dashboard"}, }, runner.WithLoggingManager(r.Logging()), runner.WithEnv([]string{ constants.EnvTerm, constants.EnvTcellMinimizeEnvironment, constants.EnvDashboardGomemlimit(), }), runner.WithStdinFile(tty), runner.WithStdoutFile(tty), runner.WithCtty(0), runner.WithOOMScoreAdj(-400), runner.WithDroppedCapabilities(capability.AllCapabilitiesSetLowercase()), runner.WithSelinuxLabel(constants.SelinuxLabelDashboard), runner.WithCgroupPath(constants.CgroupDashboard), runner.WithUID(constants.DashboardUserID), runner.WithPriority(constants.DashboardPriority), ), restart.WithType(restart.Forever), ), nil } ================================================ FILE: internal/app/machined/pkg/system/services/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "errors" "fmt" "io" "log" "net/netip" "os" goruntime "runtime" "slices" "strings" "time" containerdapi "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/cap" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/etcdutl/v3/snapshot" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/console" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/etcd" "github.com/siderolabs/talos/pkg/argsbuilder" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/filetree" "github.com/siderolabs/talos/pkg/logging" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/cri" etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" ) var _ system.HealthcheckedService = (*Etcd)(nil) // Etcd implements the Service interface. It serves as the concrete type with // the required methods. type Etcd struct { Bootstrap bool RecoverFromSnapshot bool RecoverSkipHashCheck bool args []string client *etcd.Client imgRef string // if the new member was added as a learner during the service start, its ID is kept here learnerMemberID uint64 promoteCtxCancel context.CancelFunc } // ID implements the Service interface. func (e *Etcd) ID(runtime.Runtime) string { return "etcd" } // PreFunc implements the Service interface. // //nolint:gocyclo func (e *Etcd) PreFunc(ctx context.Context, r runtime.Runtime) error { client, err := containerdapi.New(constants.CRIContainerdAddress) if err != nil { return err } //nolint:errcheck defer client.Close() // Pull the image and unpack it. containerdctx := namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace) spec, err := safe.ReaderGet[*etcdresource.Spec](ctx, r.State().V1Alpha2().Resources(), etcdresource.NewSpec(etcdresource.NamespaceName, etcdresource.SpecID).Metadata()) if err != nil { // spec should be ready return fmt.Errorf("failed to get etcd spec: %w", err) } img, err := image.Pull(containerdctx, cri.RegistryBuilder(r.State().V1Alpha2().Resources()), r.State().V1Alpha2().Resources(), client, spec.TypedSpec().Image, image.WithSkipIfAlreadyPulled(), image.WithProgressReporter(console.NewProgressReporter), ) if err != nil { return fmt.Errorf("failed to pull image %q: %w", spec.TypedSpec().Image, err) } e.imgRef = img.Target().Digest.String() // Clear any previously set learner member ID e.learnerMemberID = 0 switch t := r.Config().Machine().Type(); t { case machine.TypeInit: if err = e.argsForInit(ctx, r, spec.TypedSpec()); err != nil { return err } case machine.TypeControlPlane: if err = e.argsForControlPlane(ctx, r, spec.TypedSpec()); err != nil { return err } case machine.TypeWorker: return fmt.Errorf("unexpected machine type: %v", t) case machine.TypeUnknown: fallthrough default: panic(fmt.Sprintf("unexpected machine type %v", t)) } if err = waitPKI(ctx, r); err != nil { return fmt.Errorf("failed to generate etcd PKI: %w", err) } return nil } // PostFunc implements the Service interface. func (e *Etcd) PostFunc(runtime.Runtime, events.ServiceState) (err error) { if e.promoteCtxCancel != nil { e.promoteCtxCancel() } if e.client != nil { e.client.Close() //nolint:errcheck } e.client = nil return nil } // Condition implements the Service interface. func (e *Etcd) Condition(r runtime.Runtime) conditions.Condition { return conditions.WaitForAll( timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()), network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady), etcdresource.NewSpecReadyCondition(r.State().V1Alpha2().Resources()), ) } // DependsOn implements the Service interface. func (e *Etcd) DependsOn(runtime.Runtime) []string { return []string{"cri"} } // Volumes implements the Service interface. func (e *Etcd) Volumes(runtime.Runtime) []string { return []string{ "/var/lib", constants.EtcdDataVolumeID, } } // Runner implements the Service interface. func (e *Etcd) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := runner.Args{ ID: e.ID(r), ProcessArgs: append([]string{"/usr/local/bin/etcd"}, e.args...), } mounts := []specs.Mount{ {Type: "bind", Destination: constants.EtcdPKIPath, Source: constants.EtcdPKIPath, Options: []string{"rbind", "ro"}}, {Type: "bind", Destination: constants.EtcdDataPath, Source: constants.EtcdDataPath, Options: []string{"rbind", "rw"}}, } env := environment.Get(r.Config()) // NOTE: leave it here for future unsupported architectures, so we can know where to add them if slices.Contains([]string{}, goruntime.GOARCH) { env = append(env, "ETCD_UNSUPPORTED_ARCH="+goruntime.GOARCH) } env = append(env, "ETCD_CIPHER_SUITES=TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305") //nolint:lll if e.learnerMemberID != 0 { var promoteCtx context.Context promoteCtx, e.promoteCtxCancel = context.WithCancel(context.Background()) go func() { if err := promoteMember(promoteCtx, r, e.learnerMemberID); err != nil && !errors.Is(err, context.Canceled) { log.Printf("failed promoting member: %s", err) } else if err == nil { log.Printf("successfully promoted etcd member") } }() } return restart.New(containerd.NewRunner( r.Config().Debug(), &args, runner.WithLoggingManager(r.Logging()), runner.WithNamespace(constants.SystemContainerdNamespace), runner.WithContainerImage(e.imgRef), runner.WithEnv(env), runner.WithCgroupPath(constants.CgroupEtcd), runner.WithSelinuxLabel(constants.SELinuxLabelEtcd), runner.WithOCISpecOpts( oci.WithDroppedCapabilities(cap.Known()), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithMounts(mounts), oci.WithUser(fmt.Sprintf("%d:%d", constants.EtcdUserID, constants.EtcdUserID)), ), runner.WithOOMScoreAdj(-998), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (e *Etcd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { if e.client == nil { var err error e.client, err = etcd.NewLocalClient(ctx) if err != nil { return err } } return e.client.ValidateQuorum(ctx) } } // HealthSettings implements the HealthcheckedService interface. func (e *Etcd) HealthSettings(runtime.Runtime) *health.Settings { return &health.Settings{ InitialDelay: 5 * time.Second, Period: 20 * time.Second, Timeout: 15 * time.Second, } } func waitPKI(ctx context.Context, r runtime.Runtime) error { _, err := r.State().V1Alpha2().Resources().WatchFor(ctx, resource.NewMetadata(etcdresource.NamespaceName, etcdresource.PKIStatusType, etcdresource.PKIID, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), ) return err } func addMember(ctx context.Context, r runtime.Runtime, addrs []string, name string) (*clientv3.MemberListResponse, uint64, error) { client, err := etcd.NewClientFromControlPlaneIPs(ctx, r.State().V1Alpha2().Resources()) if err != nil { return nil, 0, err } //nolint:errcheck defer client.Close() ctx = clientv3.WithRequireLeader(ctx) list, err := client.MemberList(ctx) if err != nil { return nil, 0, fmt.Errorf("error getting etcd member list: %w", err) } for _, member := range list.Members { // addMember only gets called when the etcd data directory is empty, so the node is about to join the etcd cluster // if there's already a member with same hostname, it should be removed, as there will be a conflict between the existing // member and a new joining member. // here we assume that control plane nodes have unique hostnames (if that's not the case, it will be a problem anyways) if member.Name == name { if _, err = client.MemberRemove(ctx, member.ID); err != nil { return nil, 0, fmt.Errorf("error removing self from the member list: %w", err) } } } add, err := client.MemberAddAsLearner(ctx, addrs) if err != nil { return nil, 0, fmt.Errorf("error adding member: %w", err) } list, err = client.MemberList(ctx) if err != nil { return nil, 0, fmt.Errorf("error getting second etcd member list: %w", err) } return list, add.Member.ID, nil } func buildInitialCluster(ctx context.Context, r runtime.Runtime, name string, peerAddrs []string) (initial string, learnerMemberID uint64, err error) { var ( id uint64 lastNag time.Time ) err = retry.Constant(constants.EtcdJoinTimeout, retry.WithUnits(3*time.Second), retry.WithJitter(time.Second), retry.WithErrorLogging(true), ).RetryWithContext(ctx, func(ctx context.Context) error { var resp *clientv3.MemberListResponse if time.Since(lastNag) > 30*time.Second { lastNag = time.Now() log.Printf("etcd is waiting to join the cluster, if this node is the first node in the cluster, please run `talosctl bootstrap` against one of the following IPs:") // we "allow" a failure here since we want to fallthrough and attempt to add the etcd member regardless of // whether we can print our IPs currentAddresses, addrErr := safe.ReaderGet[*network.NodeAddress]( ctx, r.State().V1Alpha2().Resources(), resource.NewMetadata(network.NamespaceName, network.NodeAddressType, network.FilteredNodeAddressID(network.NodeAddressCurrentID, k8s.NodeAddressFilterNoK8s), resource.VersionUndefined), ) if addrErr != nil { log.Printf("error getting node addresses: %s", addrErr.Error()) } else { ips := currentAddresses.TypedSpec().IPs() log.Printf("%s", ips) } } attemptCtx, attemptCtxCancel := context.WithTimeout(ctx, 30*time.Second) defer attemptCtxCancel() resp, id, err = addMember(attemptCtx, r, peerAddrs, name) if err != nil { if errors.Is(err, context.Canceled) { return err } // TODO(andrewrynhard): We should check the error type here and // handle the specific error accordingly. return retry.ExpectedError(err) } var conf []string for _, memb := range resp.Members { for _, u := range memb.PeerURLs { n := memb.Name if memb.ID == id { n = name } conf = append(conf, fmt.Sprintf("%s=%s", n, u)) } } initial = strings.Join(conf, ",") return nil }) if err != nil { return "", 0, fmt.Errorf("failed to build cluster arguments: %w", err) } return initial, id, nil } //nolint:gocyclo func (e *Etcd) argsForInit(ctx context.Context, r runtime.Runtime, spec *etcdresource.SpecSpec) error { var upgraded bool _, upgraded = r.State().Machine().Meta().ReadTag(meta.Upgrade) denyListArgs := argsbuilder.Args{ "name": {spec.Name}, "auto-tls": {"false"}, "peer-auto-tls": {"false"}, "data-dir": {constants.EtcdDataPath}, "listen-peer-urls": {formatEtcdURLs(spec.ListenPeerAddresses, constants.EtcdPeerPort)}, "listen-client-urls": {formatEtcdURLs(spec.ListenClientAddresses, constants.EtcdClientPort)}, "client-cert-auth": {"true"}, "cert-file": {constants.EtcdCert}, "key-file": {constants.EtcdKey}, "trusted-ca-file": {constants.EtcdCACert}, "peer-client-cert-auth": {"true"}, "peer-cert-file": {constants.EtcdPeerCert}, "peer-key-file": {constants.EtcdPeerKey}, "peer-trusted-ca-file": {constants.EtcdCACert}, "experimental-initial-corrupt-check": {"true"}, "experimental-watch-progress-notify-interval": {"5s"}, "experimental-compact-hash-check-enabled": {"true"}, } extraArgs := make(argsbuilder.Args, len(spec.ExtraArgs)) for k, v := range spec.ExtraArgs { extraArgs[k] = v.Values } denyList := argsbuilder.WithDenyList(denyListArgs) if !extraArgs.Contains("initial-cluster-state") { denyListArgs.Set("initial-cluster-state", argsbuilder.Value{"new"}) } // If the initial cluster isn't explicitly defined, we need to discover any // existing members. if !extraArgs.Contains("initial-cluster") { ok, err := IsDirEmpty(constants.EtcdDataPath) if err != nil { return err } if ok { initialCluster := formatClusterURLs(spec.Name, getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)) if upgraded { denyListArgs.Set("initial-cluster-state", argsbuilder.Value{"existing"}) initialCluster, e.learnerMemberID, err = buildInitialCluster(ctx, r, spec.Name, getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)) if err != nil { return err } } denyListArgs.Set("initial-cluster", argsbuilder.Value{initialCluster}) } else { denyListArgs.Set("initial-cluster-state", argsbuilder.Value{"existing"}) } } if !extraArgs.Contains("initial-advertise-peer-urls") { denyListArgs.Set("initial-advertise-peer-urls", argsbuilder.Value{formatEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)}, ) } if !extraArgs.Contains("advertise-client-urls") { denyListArgs.Set("advertise-client-urls", argsbuilder.Value{formatEtcdURLs(spec.AdvertisedAddresses, constants.EtcdClientPort)}, ) } if err := denyListArgs.Merge(extraArgs, denyList); err != nil { return err } e.args = denyListArgs.Args() return nil } //nolint:gocyclo func (e *Etcd) argsForControlPlane(ctx context.Context, r runtime.Runtime, spec *etcdresource.SpecSpec) error { denyListArgs := argsbuilder.Args{ "name": {spec.Name}, "auto-tls": {"false"}, "peer-auto-tls": {"false"}, "data-dir": {constants.EtcdDataPath}, "listen-peer-urls": {formatEtcdURLs(spec.ListenPeerAddresses, constants.EtcdPeerPort)}, "listen-client-urls": {formatEtcdURLs(spec.ListenClientAddresses, constants.EtcdClientPort)}, "client-cert-auth": {"true"}, "cert-file": {constants.EtcdCert}, "key-file": {constants.EtcdKey}, "trusted-ca-file": {constants.EtcdCACert}, "peer-client-cert-auth": {"true"}, "peer-cert-file": {constants.EtcdPeerCert}, "peer-key-file": {constants.EtcdPeerKey}, "peer-trusted-ca-file": {constants.EtcdCACert}, "experimental-initial-corrupt-check": {"true"}, "experimental-watch-progress-notify-interval": {"5s"}, "experimental-compact-hash-check-enabled": {"true"}, } extraArgs := make(argsbuilder.Args, len(spec.ExtraArgs)) for k, v := range spec.ExtraArgs { extraArgs[k] = v.Values } denyList := argsbuilder.WithDenyList(denyListArgs) if e.RecoverFromSnapshot { if err := e.recoverFromSnapshot(spec); err != nil { return err } } ok, err := IsDirEmpty(constants.EtcdDataPath) if err != nil { return err } // The only time that we need to build the initial cluster args, is when we // don't have any data. if ok { if !extraArgs.Contains("initial-cluster-state") { if e.Bootstrap { denyListArgs.Set("initial-cluster-state", argsbuilder.Value{"new"}) } else { denyListArgs.Set("initial-cluster-state", argsbuilder.Value{"existing"}) } } if !extraArgs.Contains("initial-cluster") { var initialCluster string if e.Bootstrap { initialCluster = formatClusterURLs(spec.Name, getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)) } else { initialCluster, e.learnerMemberID, err = buildInitialCluster(ctx, r, spec.Name, getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)) if err != nil { return fmt.Errorf("failed to build initial etcd cluster: %w", err) } } denyListArgs.Set("initial-cluster", argsbuilder.Value{initialCluster}) } } if !extraArgs.Contains("advertise-client-urls") { denyListArgs.Set("advertise-client-urls", argsbuilder.Value{formatEtcdURLs(spec.AdvertisedAddresses, constants.EtcdClientPort)}, ) } if !extraArgs.Contains("initial-advertise-peer-urls") { denyListArgs.Set("initial-advertise-peer-urls", argsbuilder.Value{formatEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)}, ) } if err = denyListArgs.Merge(extraArgs, denyList); err != nil { return err } e.args = denyListArgs.Args() return nil } // recoverFromSnapshot recovers etcd data directory from the snapshot uploaded previously. func (e *Etcd) recoverFromSnapshot(spec *etcdresource.SpecSpec) error { manager := snapshot.NewV3(logging.Wrap(log.Writer())) status, err := manager.Status(constants.EtcdRecoverySnapshotPath) if err != nil { return fmt.Errorf("error verifying snapshot: %w", err) } log.Printf("recovering etcd from snapshot: hash %08x, revision %d, total keys %d, total size %d\n", status.Hash, status.Revision, status.TotalKey, status.TotalSize) if err = manager.Restore(snapshot.RestoreConfig{ SnapshotPath: constants.EtcdRecoverySnapshotPath, Name: spec.Name, OutputDataDir: constants.EtcdDataPath, PeerURLs: getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort), InitialCluster: formatClusterURLs(spec.Name, getEtcdURLs(spec.AdvertisedAddresses, constants.EtcdPeerPort)), SkipHashCheck: e.RecoverSkipHashCheck, }); err != nil { return fmt.Errorf("error recovering from the snapshot: %w", err) } if err = os.Remove(constants.EtcdRecoverySnapshotPath); err != nil { return fmt.Errorf("error deleting snapshot: %w", err) } return filetree.ChownRecursive(constants.EtcdDataPath, constants.EtcdUserID, constants.EtcdUserID) } func promoteMember(ctx context.Context, r runtime.Runtime, memberID uint64) error { // try to promote a member until it succeeds (call might fail until the member catches up with the leader) // promote member call will fail until member catches up with the master // // iterate over all endpoints until we find the one which works // if we stick with the default behavior, we might hit the member being promoted, and that will never // promote itself. idx := 0 return retry.Constant(10*time.Minute, retry.WithUnits(15*time.Second), retry.WithAttemptTimeout(30*time.Second), retry.WithJitter(time.Second), retry.WithErrorLogging(true), ).RetryWithContext(ctx, func(ctx context.Context) error { endpoints, err := etcd.GetEndpoints(ctx, r.State().V1Alpha2().Resources()) if err != nil { return retry.ExpectedError(err) } if len(endpoints) == 0 { return retry.ExpectedErrorf("no endpoints") } // try to iterate all available endpoints in the time available for an attempt for range endpoints { select { case <-ctx.Done(): return retry.ExpectedError(ctx.Err()) default: } endpoint := endpoints[idx%len(endpoints)] idx++ err = attemptPromote(ctx, endpoint, memberID) if err == nil { return nil } } return retry.ExpectedError(err) }) } func attemptPromote(ctx context.Context, endpoint string, memberID uint64) error { client, err := etcd.NewClient(ctx, []string{endpoint}) if err != nil { return err } defer client.Close() //nolint:errcheck _, err = client.MemberPromote(ctx, memberID) return err } // IsDirEmpty checks if a directory is empty or not. func IsDirEmpty(name string) (bool, error) { f, err := os.Open(name) if err != nil { return false, err } //nolint:errcheck defer f.Close() _, err = f.Readdirnames(1) if err == io.EOF { return true, nil } return false, err } // BootstrapEtcd bootstraps the etcd cluster. // // Current instance of etcd (not joined yet) is stopped, and new instance is started in bootstrap mode. func BootstrapEtcd(ctx context.Context, r runtime.Runtime, req *machineapi.BootstrapRequest) error { if err := system.Services(r).Stop(ctx, "etcd"); err != nil { return fmt.Errorf("failed to stop etcd: %w", err) } // This is hack. We need to fake a finished state so that we can get the // wait in the boot sequence to unblock. for _, svc := range system.Services(r).List() { if svc.AsProto().GetId() == "etcd" { svc.UpdateState(ctx, events.StateFinished, "Bootstrap requested") break } } if entries, _ := os.ReadDir(constants.EtcdDataPath); len(entries) > 0 { //nolint:errcheck return errors.New("etcd data directory is not empty") } svc := &Etcd{ Bootstrap: true, RecoverFromSnapshot: req.RecoverEtcd, RecoverSkipHashCheck: req.RecoverSkipHashCheck, } if err := system.Services(r).Unload(ctx, svc.ID(r)); err != nil { return err } system.Services(r).Load(svc) if err := system.Services(r).Start(svc.ID(r)); err != nil { return fmt.Errorf("error starting etcd in bootstrap mode: %w", err) } return nil } func formatEtcdURL(addr netip.Addr, port int) string { return fmt.Sprintf("https://%s", nethelpers.JoinHostPort(addr.String(), port)) } func getEtcdURLs(addrs []netip.Addr, port int) []string { return xslices.Map(addrs, func(addr netip.Addr) string { return formatEtcdURL(addr, port) }) } func formatEtcdURLs(addrs []netip.Addr, port int) string { return strings.Join(getEtcdURLs(addrs, port), ",") } func formatClusterURLs(name string, urls []string) string { return strings.Join(xslices.Map(urls, func(url string) string { return fmt.Sprintf("%s=%s", name, url) }), ",") } ================================================ FILE: internal/app/machined/pkg/system/services/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import "github.com/containerd/containerd/v2/pkg/oci" // GetOCIOptions gets all OCI options from an Extension. func (svc *Extension) GetOCIOptions() ([]oci.SpecOpts, error) { envVars, err := svc.parseEnvironment() if err != nil { return nil, err } return svc.getOCIOptions(envVars, svc.Spec.Container.Mounts), nil } ================================================ FILE: internal/app/machined/pkg/system/services/extension.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "errors" "fmt" "os" "path/filepath" "strings" "github.com/containerd/containerd/v2/pkg/oci" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-envparse" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/gen/maps" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" extservices "github.com/siderolabs/talos/pkg/machinery/extensions/services" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/time" ) // Extension service is a generic wrapper around extension services spec. type Extension struct { Spec extservices.Spec overlayUnmounter func() error } // ID implements the Service interface. func (svc *Extension) ID(r runtime.Runtime) string { return "ext-" + svc.Spec.Name } // PreFunc implements the Service interface. func (svc *Extension) PreFunc(ctx context.Context, r runtime.Runtime) error { // re-mount service rootfs as overlay rw mount to allow containerd to mount there /dev, /proc, etc. rootfsPath := filepath.Join(constants.ExtensionServiceRootfsPath, svc.Spec.Name) // TODO: label system extensions overlay := mount.NewSystemOverlay( []string{rootfsPath}, rootfsPath, nil, ) if _, err := overlay.Mount(); err != nil { return err } svc.overlayUnmounter = overlay.Unmount return nil } // PostFunc implements the Service interface. func (svc *Extension) PostFunc(r runtime.Runtime, state events.ServiceState) (err error) { return svc.overlayUnmounter() } // Condition implements the Service interface. func (svc *Extension) Condition(r runtime.Runtime) conditions.Condition { var conds []conditions.Condition if svc.Spec.Container.EnvironmentFile != "" { // add a dependency on the environment file conds = append(conds, conditions.WaitForFileToExist(svc.Spec.Container.EnvironmentFile)) } for _, dep := range svc.Spec.Depends { switch { case dep.Path != "": conds = append(conds, conditions.WaitForFileToExist(dep.Path)) case len(dep.Network) > 0: conds = append(conds, network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.StatusChecksFromStatuses(dep.Network...)...)) case dep.Time: conds = append(conds, time.NewSyncCondition(r.State().V1Alpha2().Resources())) case dep.Configuration: conds = append(conds, runtimeres.NewExtensionServiceConfigStatusCondition(r.State().V1Alpha2().Resources(), svc.Spec.Name)) } } if len(conds) == 0 { return nil } return conditions.WaitForAll(conds...) } // DependsOn implements the Service interface. func (svc *Extension) DependsOn(r runtime.Runtime) []string { deps := []string{"containerd"} for _, dep := range svc.Spec.Depends { if dep.Service != "" { deps = append(deps, dep.Service) } } return deps } // Volumes implements the Service interface. func (svc *Extension) Volumes(runtime.Runtime) []string { return nil } func (svc *Extension) getOCIOptions(envVars []string, mounts []specs.Mount) []oci.SpecOpts { ociOpts := []oci.SpecOpts{ oci.WithRootFSPath(filepath.Join(constants.ExtensionServiceRootfsPath, svc.Spec.Name)), containerd.WithRootfsPropagation(svc.Spec.Container.Security.RootfsPropagation), oci.WithMounts(mounts), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithSelinuxLabel(""), oci.WithApparmorProfile(""), oci.WithCapabilities(capability.AllGrantableCapabilities()), oci.WithAllDevicesAllowed, oci.WithEnv(envVars), } if !svc.Spec.Container.Security.WriteableRootfs { ociOpts = append(ociOpts, oci.WithRootFSReadonly()) } if svc.Spec.Container.Security.WriteableSysfs { ociOpts = append(ociOpts, oci.WithWriteableSysfs) } if svc.Spec.Container.Security.MaskedPaths != nil { ociOpts = append(ociOpts, oci.WithMaskedPaths(svc.Spec.Container.Security.MaskedPaths)) } if svc.Spec.Container.Security.ReadonlyPaths != nil { ociOpts = append(ociOpts, oci.WithReadonlyPaths(svc.Spec.Container.Security.ReadonlyPaths)) } return ociOpts } // Runner implements the Service interface. // //nolint:gocyclo func (svc *Extension) Runner(r runtime.Runtime) (runner.Runner, error) { args := runner.Args{ ID: svc.ID(r), ProcessArgs: append([]string{svc.Spec.Container.Entrypoint}, svc.Spec.Container.Args...), } for _, mount := range svc.Spec.Container.Mounts { if _, err := os.Stat(mount.Source); err == nil { // already exists, skip continue } else if !errors.Is(err, os.ErrNotExist) { return nil, err } if err := os.MkdirAll(mount.Source, 0o700); err != nil { return nil, err } } mounts := append([]specs.Mount{}, svc.Spec.Container.Mounts...) mounts = bindMountContainerMarker(mounts) envVars, err := svc.parseEnvironment() if err != nil { return nil, err } configSpec, err := safe.StateGetByID[*runtimeres.ExtensionServiceConfig](context.Background(), r.State().V1Alpha2().Resources(), svc.Spec.Name) if err == nil { spec := configSpec.TypedSpec() for _, ext := range spec.Files { mounts = append(mounts, specs.Mount{ Source: filepath.Join(constants.ExtensionServiceUserConfigPath, svc.Spec.Name, strings.ReplaceAll(strings.TrimPrefix(ext.MountPath, "/"), "/", "-")), Destination: ext.MountPath, Type: "bind", Options: []string{"ro", "bind"}, }) } envVars = append(envVars, spec.Environment...) } else if !state.IsNotFoundError(err) { return nil, err } var restartType restart.Type switch svc.Spec.Restart { case extservices.RestartAlways: restartType = restart.Forever case extservices.RestartNever: restartType = restart.Once case extservices.RestartUntilSuccess: restartType = restart.UntilSuccess } ociSpecOpts := svc.getOCIOptions(envVars, mounts) logToConsole := false if r.Config() != nil { logToConsole = r.Config().Debug() } if svc.Spec.LogToConsole { logToConsole = true } return restart.New(containerd.NewRunner( logToConsole, &args, runner.WithLoggingManager(r.Logging()), runner.WithNamespace(constants.SystemContainerdNamespace), runner.WithContainerdAddress(constants.SystemContainerdAddress), runner.WithEnv(environment.Get(r.Config())), runner.WithOCISpecOpts(ociSpecOpts...), runner.WithCgroupPath(filepath.Join(constants.CgroupExtensions, svc.Spec.Name)), runner.WithOOMScoreAdj(-600), ), restart.WithType(restartType), ), nil } // APIRestartAllowed implements APIRestartableService. func (svc *Extension) APIRestartAllowed(runtime.Runtime) bool { return true } // APIStartAllowed implements APIStartableService. func (svc *Extension) APIStartAllowed(runtime.Runtime) bool { return true } // APIStopAllowed implements APIStoppableService. func (svc *Extension) APIStopAllowed(runtime.Runtime) bool { return true } func (svc *Extension) parseEnvironment() ([]string, error) { var envVars []string if svc.Spec.Container.EnvironmentFile != "" { envFile, err := os.OpenFile(svc.Spec.Container.EnvironmentFile, os.O_RDONLY, 0) if err != nil { return nil, err } defer func() { if closeErr := envFile.Close(); err != nil { err = closeErr } }() parsedEnvVars, err := envparse.Parse(envFile) if err != nil { return nil, fmt.Errorf("failed to parse environment file %q: %w", svc.Spec.Container.EnvironmentFile, err) } envVarsSlice := maps.ToSlice(parsedEnvVars, func(k, v string) string { return fmt.Sprintf("%s=%s", k, v) }) envVars = append(envVars, envVarsSlice...) } if svc.Spec.Container.Environment != nil { envVars = append(envVars, svc.Spec.Container.Environment...) } return envVars, nil } ================================================ FILE: internal/app/machined/pkg/system/services/extension_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services_test import ( "os" "testing" "github.com/containerd/containerd/v2/core/containers" "github.com/containerd/containerd/v2/core/snapshots" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services/mocks" extservices "github.com/siderolabs/talos/pkg/machinery/extensions/services" ) type MockClient struct { controller *gomock.Controller } func (c *MockClient) SnapshotService(snapshotterName string) snapshots.Snapshotter { return mocks.NewMockSnapshotter(c.controller) } func TestGetOCIOptions(t *testing.T) { mockClient := MockClient{ controller: gomock.NewController(t), } defer mockClient.controller.Finish() generateOCISpec := func(svc *services.Extension) (*oci.Spec, error) { ociOpts, err := svc.GetOCIOptions() if err != nil { return nil, err } return oci.GenerateSpec(namespaces.WithNamespace(t.Context(), "testNamespace"), &mockClient, &containers.Container{}, ociOpts...) } t.Run("default configurations are cleared away if user passes empty arrays for MaskedPaths and ReadonlyPaths", func(t *testing.T) { // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ Security: extservices.Security{ MaskedPaths: []string{}, ReadonlyPaths: []string{}, }, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, []string{}, spec.Linux.MaskedPaths) assert.Equal(t, []string{}, spec.Linux.ReadonlyPaths) }) t.Run("default configuration applies if user passes nil for MaskedPaths and ReadonlyPaths", func(t *testing.T) { // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ Security: extservices.Security{ MaskedPaths: nil, ReadonlyPaths: nil, }, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, []string{ "/proc/acpi", "/proc/asound", "/proc/kcore", "/proc/keys", "/proc/latency_stats", "/proc/timer_list", "/proc/timer_stats", "/proc/sched_debug", "/sys/firmware", "/sys/devices/virtual/powercap", "/proc/scsi", }, spec.Linux.MaskedPaths) assert.Equal(t, []string{ "/proc/bus", "/proc/fs", "/proc/irq", "/proc/sys", "/proc/sysrq-trigger", }, spec.Linux.ReadonlyPaths) }) t.Run("root fs is readonly unless explicitly enabled", func(t *testing.T) { // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ Security: extservices.Security{ WriteableRootfs: true, }, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, false, spec.Root.Readonly) }) t.Run("root fs is readonly by default", func(t *testing.T) { // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ Security: extservices.Security{}, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, true, spec.Root.Readonly) }) t.Run("allows setting extra env vars", func(t *testing.T) { // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ Environment: []string{ "FOO=BAR", }, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, []string{"FOO=BAR"}, spec.Process.Env) }) t.Run("allows setting extra envFile", func(t *testing.T) { tempDir := t.TempDir() envFile := tempDir + "/envfile" assert.NoError(t, os.WriteFile(envFile, []byte("FOO=BARFROMENVFILE"), 0o644)) // given svc := &services.Extension{ Spec: extservices.Spec{ Container: extservices.Container{ EnvironmentFile: envFile, }, }, } // when spec, err := generateOCISpec(svc) // then assert.NoError(t, err) assert.Equal(t, []string{"FOO=BARFROMENVFILE"}, spec.Process.Env) }) } ================================================ FILE: internal/app/machined/pkg/system/services/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "fmt" "net/http" "os" "sync" "time" containerdapi "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/console" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" ) var _ system.HealthcheckedService = (*Kubelet)(nil) // Kubelet implements the Service interface. It serves as the concrete type with // the required methods. type Kubelet struct { imgRef string } // ID implements the Service interface. func (k *Kubelet) ID(runtime.Runtime) string { return "kubelet" } // PreFunc implements the Service interface. func (k *Kubelet) PreFunc(ctx context.Context, r runtime.Runtime) error { specResource, err := safe.ReaderGet[*k8s.KubeletSpec](ctx, r.State().V1Alpha2().Resources(), resource.NewMetadata(k8s.NamespaceName, k8s.KubeletSpecType, k8s.KubeletID, resource.VersionUndefined)) if err != nil { return err } spec := specResource.TypedSpec() client, err := containerdapi.New(constants.CRIContainerdAddress) if err != nil { return err } //nolint:errcheck defer client.Close() // Pull the image and unpack it. containerdctx := namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace) img, err := image.Pull(containerdctx, cri.RegistryBuilder(r.State().V1Alpha2().Resources()), r.State().V1Alpha2().Resources(), client, spec.Image, image.WithSkipIfAlreadyPulled(), image.WithProgressReporter(console.NewProgressReporter), ) if err != nil { return err } k.imgRef = img.Target().Digest.String() // Create lifecycle resource to signal that the kubelet is about to start. err = r.State().V1Alpha2().Resources().Create(ctx, k8s.NewKubeletLifecycle(k8s.NamespaceName, k8s.KubeletLifecycleID)) if err != nil && !state.IsConflictError(err) { // ignore if the lifecycle resource already exists return err } return nil } // PostFunc implements the Service interface. func (k *Kubelet) PostFunc(runtime.Runtime, events.ServiceState) (err error) { return nil } // Condition implements the Service interface. func (k *Kubelet) Condition(r runtime.Runtime) conditions.Condition { return conditions.WaitForAll( timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()), network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady, network.EtcFilesReady), ) } // DependsOn implements the Service interface. func (k *Kubelet) DependsOn(runtime.Runtime) []string { return []string{"cri"} } // Volumes implements the Service interface. func (k *Kubelet) Volumes(runtime.Runtime) []string { return []string{ "/var/lib", "/var/lib/kubelet", constants.LogMountPoint, "/var/log/audit", "/var/log/containers", "/var/log/pods", "/var/lib/kubelet/seccomp", constants.SeccompProfilesDirectory, constants.KubernetesAuditLogDir, constants.UserVolumeMountPoint, } } // Runner implements the Service interface. func (k *Kubelet) Runner(r runtime.Runtime) (runner.Runner, error) { specResource, err := safe.ReaderGet[*k8s.KubeletSpec]( context.Background(), r.State().V1Alpha2().Resources(), resource.NewMetadata(k8s.NamespaceName, k8s.KubeletSpecType, k8s.KubeletID, resource.VersionUndefined), ) if err != nil { return nil, err } spec := specResource.TypedSpec() // Set the process arguments. args := runner.Args{ ID: k.ID(r), ProcessArgs: append([]string{"/usr/local/bin/kubelet"}, spec.Args...), } // Set the required kubelet mounts. mounts := []specs.Mount{ {Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"bind", "rw"}}, {Type: "sysfs", Destination: "/sys", Source: "/sys", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: constants.CgroupMountPath, Source: constants.CgroupMountPath, Options: []string{"bind", "rw"}}, {Type: "bind", Destination: "/lib/modules", Source: "/usr/lib/modules", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/kubernetes", Source: "/etc/kubernetes", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: constants.KubeletCredentialProviderBinDir, Source: constants.KubeletCredentialProviderBinDir, Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/nfsmount.conf", Source: "/etc/nfsmount.conf", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/machine-id", Source: "/etc/machine-id", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/os-release", Source: "/etc/os-release", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: constants.PodResolvConfPath, Source: constants.PodResolvConfPath, Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/etc/cni", Source: "/etc/cni", Options: []string{"bind", "ro"}}, {Type: "bind", Destination: "/usr/libexec/kubernetes", Source: "/usr/libexec/kubernetes", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: "/var/run", Source: "/run", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: "/var/lib/containerd", Source: "/var/lib/containerd", Options: []string{"rbind", "rw"}}, {Type: "bind", Destination: "/var/lib/kubelet", Source: "/var/lib/kubelet", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: "/var/log/containers", Source: "/var/log/containers", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: "/var/log/pods", Source: "/var/log/pods", Options: []string{"bind", "rw"}}, {Type: "bind", Destination: constants.UserVolumeMountPoint, Source: constants.UserVolumeMountPoint, Options: []string{"rbind", "ro"}}, } if _, err := os.Stat("/sys/kernel/security"); err == nil { mounts = append(mounts, specs.Mount{Type: "securityfs", Destination: "/sys/kernel/security", Source: "/sys/kernel/security", Options: []string{"bind", "ro"}}, ) } // Add extra mounts. // TODO(andrewrynhard): We should verify that the mount source is // allowlisted. There is the potential that a user can expose // sensitive information. for _, mount := range spec.ExtraMounts { if err = os.MkdirAll(mount.Source, 0o700); err != nil { return nil, err } mounts = append(mounts, mount) } return restart.New(containerd.NewRunner( r.Config().Debug() && r.Config().Machine().Type() == machine.TypeWorker, // enable debug logs only for the worker nodes &args, runner.WithLoggingManager(r.Logging()), runner.WithNamespace(constants.SystemContainerdNamespace), runner.WithContainerImage(k.imgRef), runner.WithEnv(environment.Get(r.Config())), runner.WithCgroupPath(constants.CgroupKubelet), runner.WithSelinuxLabel(constants.SelinuxLabelKubelet), runner.WithOCISpecOpts( containerd.WithRootfsPropagation("shared"), oci.WithMounts(mounts), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostNamespace(specs.PIDNamespace), oci.WithParentCgroupDevices, oci.WithMaskedPaths(nil), oci.WithReadonlyPaths(nil), oci.WithWriteableSysfs, oci.WithWriteableCgroupfs, oci.WithApparmorProfile(""), oci.WithAllDevicesAllowed, oci.WithCapabilities(capability.AllGrantableCapabilities()), // TODO: kubelet doesn't need all of these, we should consider limiting capabilities ), runner.WithOOMScoreAdj(constants.KubeletOOMScoreAdj), runner.WithCustomSeccompProfile(kubeletSeccomp), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (k *Kubelet) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { return simpleHealthCheck(ctx, "http://127.0.0.1:10248/healthz") } } // HealthSettings implements the HealthcheckedService interface. func (k *Kubelet) HealthSettings(runtime.Runtime) *health.Settings { settings := health.DefaultSettings settings.InitialDelay = 2 * time.Second // increase initial delay as kubelet is slow on startup return &settings } // APIRestartAllowed implements APIRestartableService. func (k *Kubelet) APIRestartAllowed(runtime.Runtime) bool { return true } // APIStartAllowed implements APIStartableService. func (k *Kubelet) APIStartAllowed(runtime.Runtime) bool { return true } func kubeletSeccomp(seccomp *specs.LinuxSeccomp) { // for cephfs mounts seccomp.Syscalls = append(seccomp.Syscalls, specs.LinuxSyscall{ Names: []string{ "add_key", "request_key", }, Action: specs.ActAllow, Args: []specs.LinuxSeccompArg{}, }, ) } func simpleHealthCheck(ctx context.Context, url string) error { req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return err } resp, err := http.DefaultClient.Do(req) //nolint:bodyclose if err != nil { return err } bodyCloser := sync.OnceValue(resp.Body.Close) defer bodyCloser() //nolint:errcheck if resp.StatusCode != http.StatusOK { return fmt.Errorf("expected HTTP status OK, got %s", resp.Status) } return bodyCloser() } ================================================ FILE: internal/app/machined/pkg/system/services/machined.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "io" "log" "net" "os" "path/filepath" "time" "github.com/siderolabs/go-debug" "go.uber.org/zap/zapcore" "google.golang.org/grpc" v1alpha1server "github.com/siderolabs/talos/internal/app/machined/internal/server/v1alpha1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/grpc/factory" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/logging" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) const machinedServiceID = "machined" var rules = map[string]role.Set{ "/cluster.ClusterService/HealthCheck": role.MakeSet(role.Admin, role.Operator, role.Reader), "/inspect.InspectService/ControllerRuntimeDependencies": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.ImageService/Import": role.MakeSet(role.Admin), "/machine.ImageService/List": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.ImageService/Pull": role.MakeSet(role.Admin, role.Operator), "/machine.ImageService/Remove": role.MakeSet(role.Admin), "/machine.ImageService/Verify": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.DebugService/ContainerRun": role.MakeSet(role.Admin), "/machine.LifecycleService/Install": role.MakeSet(role.Admin), "/machine.LifecycleService/Upgrade": role.MakeSet(role.Admin), "/machine.MachineService/ApplyConfiguration": role.MakeSet( role.Admin, // for maintenance only, verified in the handler role.Reader, ), "/machine.MachineService/Bootstrap": role.MakeSet(role.Admin), "/machine.MachineService/CPUInfo": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/CPUFreqStats": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Containers": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Copy": role.MakeSet(role.Admin), "/machine.MachineService/DiskStats": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/DiskUsage": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Dmesg": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/EtcdAlarmList": role.MakeSet(role.Admin, role.Operator, role.Reader, role.EtcdBackup), "/machine.MachineService/EtcdAlarmDisarm": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/EtcdDefragment": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/EtcdForfeitLeadership": role.MakeSet(role.Admin), "/machine.MachineService/EtcdLeaveCluster": role.MakeSet(role.Admin), "/machine.MachineService/EtcdMemberList": role.MakeSet(role.Admin, role.Operator, role.Reader, role.EtcdBackup), "/machine.MachineService/EtcdRecover": role.MakeSet(role.Admin), "/machine.MachineService/EtcdRemoveMemberByID": role.MakeSet(role.Admin), "/machine.MachineService/EtcdSnapshot": role.MakeSet(role.Admin, role.Operator, role.EtcdBackup), "/machine.MachineService/EtcdStatus": role.MakeSet(role.Admin, role.Operator, role.Reader, role.EtcdBackup), "/machine.MachineService/EtcdDowngradeCancel": role.MakeSet(role.Admin), "/machine.MachineService/EtcdDowngradeEnable": role.MakeSet(role.Admin), "/machine.MachineService/EtcdDowngradeValidate": role.MakeSet(role.Admin), "/machine.MachineService/Events": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/GenerateClientConfiguration": role.MakeSet(role.Admin), "/machine.MachineService/Hostname": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/ImageList": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/ImagePull": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Kubeconfig": role.MakeSet(role.Admin), "/machine.MachineService/List": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/LoadAvg": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Logs": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/LogsContainers": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Memory": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/MetaWrite": role.MakeSet(role.Admin), "/machine.MachineService/MetaDelete": role.MakeSet(role.Admin), "/machine.MachineService/Mounts": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/NetworkDeviceStats": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Netstat": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/PacketCapture": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Processes": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Read": role.MakeSet(role.Admin), "/machine.MachineService/Reboot": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Reset": role.MakeSet(role.Admin), "/machine.MachineService/Restart": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Rollback": role.MakeSet(role.Admin), "/machine.MachineService/ServiceList": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/ServiceRestart": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/ServiceStart": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/ServiceStop": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Shutdown": role.MakeSet(role.Admin, role.Operator), "/machine.MachineService/Stats": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/SystemStat": role.MakeSet(role.Admin, role.Operator, role.Reader), "/machine.MachineService/Upgrade": role.MakeSet(role.Admin), "/machine.MachineService/Version": role.MakeSet(role.Admin, role.Operator, role.Reader), // per-type authorization is handled by the service itself "/cosi.resource.State/Create": role.MakeSet(role.Admin), "/cosi.resource.State/Destroy": role.MakeSet(role.Admin), "/cosi.resource.State/Get": role.MakeSet(role.Admin, role.Operator, role.Reader), "/cosi.resource.State/List": role.MakeSet(role.Admin, role.Operator, role.Reader), "/cosi.resource.State/Update": role.MakeSet(role.Admin), "/cosi.resource.State/Watch": role.MakeSet(role.Admin, role.Operator, role.Reader), "/storage.StorageService/Disks": role.MakeSet(role.Admin, role.Operator, role.Reader), "/storage.StorageService/BlockDeviceWipe": role.MakeSet(role.Admin), "/time.TimeService/Time": role.MakeSet(role.Admin, role.Operator, role.Reader), "/time.TimeService/TimeCheck": role.MakeSet(role.Admin, role.Operator, role.Reader), } type machinedService struct { c runtime.Controller } // Main is an entrypoint to the API service. func (s *machinedService) Main(ctx context.Context, _ runtime.Runtime, logWriter io.Writer) error { injector := &authz.Injector{ Mode: authz.MetadataOnly, } if debug.Enabled { injector.Logger = log.New(logWriter, "machined/authz/injector ", log.Flags()).Printf } authorizer := &authz.Authorizer{ Rules: rules, FallbackRoles: role.MakeSet(role.Admin), Logger: log.New(logWriter, "machined/authz/authorizer ", log.Flags()).Printf, } logger := logging.ZapLogger( logging.NewLogDestination(logWriter, zapcore.DebugLevel, logging.WithColoredLevels(), ), ) // Start the API server. server := factory.NewServer( //nolint:contextcheck &v1alpha1server.Server{ Controller: s.c, // breaking the import loop cycle between services/ package and v1alpha1_server.go EtcdBootstrapper: BootstrapEtcd, Logger: logger, ShutdownCtx: ctx, }, factory.WithLog("machined ", logWriter), factory.ServerOptions( grpc.MaxRecvMsgSize(constants.GRPCMaxMessageSize), ), factory.WithUnaryInterceptor(injector.UnaryInterceptor()), factory.WithStreamInterceptor(injector.StreamInterceptor()), //nolint:contextcheck factory.WithUnaryInterceptor(authorizer.UnaryInterceptor()), factory.WithStreamInterceptor(authorizer.StreamInterceptor()), //nolint:contextcheck ) // ensure socket dir exists if err := os.MkdirAll(filepath.Dir(constants.MachineSocketPath), 0o770); err != nil { return err } // set the final leaf to be world-executable to make apid connect to the socket if err := os.Chmod(filepath.Dir(constants.MachineSocketPath), 0o771); err != nil { return err } listener, err := factory.NewListener(ctx, factory.Network("unix"), factory.SocketPath(constants.MachineSocketPath)) //nolint:contextcheck if err != nil { return err } if err := selinux.SetLabel(constants.MachineSocketPath, constants.MachineSocketLabel); err != nil { return err } // chown the socket path to make it accessible to the apid if err := os.Chown(constants.MachineSocketPath, constants.ApidUserID, constants.ApidUserID); err != nil { return err } ctx, cancel := context.WithCancel(ctx) defer cancel() closed := make(chan struct{}) context.AfterFunc(ctx, func() { defer close(closed) shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() factory.ServerGracefulStop(server, shutdownCtx) //nolint:contextcheck }) err = server.Serve(listener) cancel() <-closed return err } var _ system.HealthcheckedService = (*Machined)(nil) // Machined implements the Service interface. It serves as the concrete type with // the required methods. type Machined struct { Controller runtime.Controller } // ID implements the Service interface. func (m *Machined) ID(runtime.Runtime) string { return machinedServiceID } // PreFunc implements the Service interface. func (m *Machined) PreFunc(context.Context, runtime.Runtime) error { return nil } // PostFunc implements the Service interface. func (m *Machined) PostFunc(runtime.Runtime, events.ServiceState) (err error) { return nil } // Condition implements the Service interface. func (m *Machined) Condition(runtime.Runtime) conditions.Condition { return nil } // DependsOn implements the Service interface. func (m *Machined) DependsOn(runtime.Runtime) []string { return nil } // Volumes implements the Service interface. func (m *Machined) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (m *Machined) Runner(r runtime.Runtime) (runner.Runner, error) { svc := &machinedService{m.Controller} return goroutine.NewRunner(r, machinedServiceID, svc.Main, runner.WithLoggingManager(r.Logging())), nil } // HealthFunc implements the HealthcheckedService interface. func (m *Machined) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { var d net.Dialer conn, err := d.DialContext(ctx, "unix", constants.MachineSocketPath) if err != nil { return err } return conn.Close() } } // HealthSettings implements the HealthcheckedService interface. func (m *Machined) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/machined_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services //nolint:testpackage // to test unexported variable import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/api" ) func collectMethods(t *testing.T) map[string]struct{} { methods := make(map[string]struct{}) for _, service := range api.TalosAPIdAllAPIs() { for i := range service.Services().Len() { svc := service.Services().Get(i) for j := range svc.Methods().Len() { method := svc.Methods().Get(j) s := fmt.Sprintf("/%s/%s", svc.FullName(), method.Name()) require.NotContains(t, methods, s) methods[s] = struct{}{} } } } return methods } func TestRules(t *testing.T) { t.Parallel() methods := collectMethods(t) // check that there are no rules without matching methods t.Run("NoMethodForRule", func(t *testing.T) { t.Parallel() for rule := range rules { _, ok := methods[rule] assert.True(t, ok, "no method for rule %q", rule) } }) // check that there are no methods without matching rules t.Run("NoRuleForMethod", func(t *testing.T) { t.Parallel() for method := range methods { _, ok := rules[method] assert.True(t, ok, "no rule for method %q", method) } }) } ================================================ FILE: internal/app/machined/pkg/system/services/mocks/snapshotter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by MockGen. DO NOT EDIT. // Source: ~/go/pkg/mod/github.com/containerd/containerd@v1.6.4/snapshots/snapshotter.go // Package mocks is a generated GoMock package. package mocks import ( context "context" reflect "reflect" mount "github.com/containerd/containerd/v2/core/mount" snapshots "github.com/containerd/containerd/v2/core/snapshots" gomock "github.com/golang/mock/gomock" ) // MockSnapshotter is a mock of Snapshotter interface. type MockSnapshotter struct { ctrl *gomock.Controller recorder *MockSnapshotterMockRecorder } // MockSnapshotterMockRecorder is the mock recorder for MockSnapshotter. type MockSnapshotterMockRecorder struct { mock *MockSnapshotter } // NewMockSnapshotter creates a new mock instance. func NewMockSnapshotter(ctrl *gomock.Controller) *MockSnapshotter { mock := &MockSnapshotter{ctrl: ctrl} mock.recorder = &MockSnapshotterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSnapshotter) EXPECT() *MockSnapshotterMockRecorder { return m.recorder } // Close mocks base method. func (m *MockSnapshotter) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockSnapshotterMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSnapshotter)(nil).Close)) } // Commit mocks base method. func (m *MockSnapshotter) Commit(ctx context.Context, name, key string, opts ...snapshots.Opt) error { m.ctrl.T.Helper() varargs := []any{ctx, name, key} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Commit", varargs...) ret0, _ := ret[0].(error) return ret0 } // Commit indicates an expected call of Commit. func (mr *MockSnapshotterMockRecorder) Commit(ctx, name, key any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, name, key}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockSnapshotter)(nil).Commit), varargs...) } // Mounts mocks base method. func (m *MockSnapshotter) Mounts(ctx context.Context, key string) ([]mount.Mount, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Mounts", ctx, key) ret0, _ := ret[0].([]mount.Mount) ret1, _ := ret[1].(error) return ret0, ret1 } // Mounts indicates an expected call of Mounts. func (mr *MockSnapshotterMockRecorder) Mounts(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mounts", reflect.TypeOf((*MockSnapshotter)(nil).Mounts), ctx, key) } // Prepare mocks base method. func (m *MockSnapshotter) Prepare(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { m.ctrl.T.Helper() varargs := []any{ctx, key, parent} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Prepare", varargs...) ret0, _ := ret[0].([]mount.Mount) ret1, _ := ret[1].(error) return ret0, ret1 } // Prepare indicates an expected call of Prepare. func (mr *MockSnapshotterMockRecorder) Prepare(ctx, key, parent any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key, parent}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prepare", reflect.TypeOf((*MockSnapshotter)(nil).Prepare), varargs...) } // Remove mocks base method. func (m *MockSnapshotter) Remove(ctx context.Context, key string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Remove", ctx, key) ret0, _ := ret[0].(error) return ret0 } // Remove indicates an expected call of Remove. func (mr *MockSnapshotterMockRecorder) Remove(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockSnapshotter)(nil).Remove), ctx, key) } // Stat mocks base method. func (m *MockSnapshotter) Stat(ctx context.Context, key string) (snapshots.Info, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Stat", ctx, key) ret0, _ := ret[0].(snapshots.Info) ret1, _ := ret[1].(error) return ret0, ret1 } // Stat indicates an expected call of Stat. func (mr *MockSnapshotterMockRecorder) Stat(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockSnapshotter)(nil).Stat), ctx, key) } // Update mocks base method. func (m *MockSnapshotter) Update(ctx context.Context, info snapshots.Info, fieldpaths ...string) (snapshots.Info, error) { m.ctrl.T.Helper() varargs := []any{ctx, info} for _, a := range fieldpaths { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Update", varargs...) ret0, _ := ret[0].(snapshots.Info) ret1, _ := ret[1].(error) return ret0, ret1 } // Update indicates an expected call of Update. func (mr *MockSnapshotterMockRecorder) Update(ctx, info any, fieldpaths ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, info}, fieldpaths...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Update", reflect.TypeOf((*MockSnapshotter)(nil).Update), varargs...) } // Usage mocks base method. func (m *MockSnapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Usage", ctx, key) ret0, _ := ret[0].(snapshots.Usage) ret1, _ := ret[1].(error) return ret0, ret1 } // Usage indicates an expected call of Usage. func (mr *MockSnapshotterMockRecorder) Usage(ctx, key any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Usage", reflect.TypeOf((*MockSnapshotter)(nil).Usage), ctx, key) } // View mocks base method. func (m *MockSnapshotter) View(ctx context.Context, key, parent string, opts ...snapshots.Opt) ([]mount.Mount, error) { m.ctrl.T.Helper() varargs := []any{ctx, key, parent} for _, a := range opts { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "View", varargs...) ret0, _ := ret[0].([]mount.Mount) ret1, _ := ret[1].(error) return ret0, ret1 } // View indicates an expected call of View. func (mr *MockSnapshotterMockRecorder) View(ctx, key, parent any, opts ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, key, parent}, opts...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "View", reflect.TypeOf((*MockSnapshotter)(nil).View), varargs...) } // Walk mocks base method. func (m *MockSnapshotter) Walk(ctx context.Context, fn snapshots.WalkFunc, filters ...string) error { m.ctrl.T.Helper() varargs := []any{ctx, fn} for _, a := range filters { varargs = append(varargs, a) } ret := m.ctrl.Call(m, "Walk", varargs...) ret0, _ := ret[0].(error) return ret0 } // Walk indicates an expected call of Walk. func (mr *MockSnapshotterMockRecorder) Walk(ctx, fn any, filters ...any) *gomock.Call { mr.mock.ctrl.T.Helper() varargs := append([]any{ctx, fn}, filters...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Walk", reflect.TypeOf((*MockSnapshotter)(nil).Walk), varargs...) } // MockCleaner is a mock of Cleaner interface. type MockCleaner struct { ctrl *gomock.Controller recorder *MockCleanerMockRecorder } // MockCleanerMockRecorder is the mock recorder for MockCleaner. type MockCleanerMockRecorder struct { mock *MockCleaner } // NewMockCleaner creates a new mock instance. func NewMockCleaner(ctrl *gomock.Controller) *MockCleaner { mock := &MockCleaner{ctrl: ctrl} mock.recorder = &MockCleanerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCleaner) EXPECT() *MockCleanerMockRecorder { return m.recorder } // Cleanup mocks base method. func (m *MockCleaner) Cleanup(ctx context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Cleanup", ctx) ret0, _ := ret[0].(error) return ret0 } // Cleanup indicates an expected call of Cleanup. func (mr *MockCleanerMockRecorder) Cleanup(ctx any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockCleaner)(nil).Cleanup), ctx) } ================================================ FILE: internal/app/machined/pkg/system/services/registry/app/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "context" "fmt" "os" "os/signal" "path/filepath" "go.uber.org/zap" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services/registry" ) func main() { if err := app(); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } func app() error { ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt) defer cancel() development, err := zap.NewDevelopment() if err != nil { return fmt.Errorf("failed to create development logger: %w", err) } homeDir, err := os.UserHomeDir() if err != nil { return fmt.Errorf("failed to get user home directory: %w", err) } it := func(yield func(string) bool) { for _, root := range []string{"registry-cache-2", "registry-cache"} { if !yield(filepath.Join(homeDir, root)) { return } } } return registry.NewService(registry.NewMultiPathFS(it), development).Run(ctx) } ================================================ FILE: internal/app/machined/pkg/system/services/registry/fs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "io/fs" "iter" "os" "path/filepath" "github.com/hashicorp/go-multierror" ) // MultiPathFS is a [fs.FS] that reads from multiple paths sequentially until it finds the file. type MultiPathFS struct { fsIt iter.Seq[string] } // NewMultiPathFS creates a new MultiPathFS. It takes an iterator of FSs which can be used multiple times asynchrously. func NewMultiPathFS(it iter.Seq[string]) *MultiPathFS { return &MultiPathFS{fsIt: it} } // Open opens the named file. func (m *MultiPathFS) Open(name string) (fs.File, error) { var multiErr *multierror.Error for root := range m.fsIt { abs, err := filepath.Abs(root) if err != nil { return nil, err } r, err := os.Open(filepath.Join(abs, name)) if err == nil { return r, nil } multiErr = multierror.Append(multiErr, err) } if multiErr == nil { return nil, os.ErrNotExist } return nil, multiErr.ErrorOrNil() } // Stat returns a [fs.FileInfo] describing the named file. func (m *MultiPathFS) Stat(name string) (fs.FileInfo, error) { var multiErr *multierror.Error for root := range m.fsIt { abs, err := filepath.Abs(root) if err != nil { return nil, err } r, err := os.Stat(filepath.Join(abs, name)) if err == nil { return r, nil } multiErr = multierror.Append(multiErr, err) } if multiErr == nil { return nil, os.ErrNotExist } return nil, multiErr.ErrorOrNil() } ================================================ FILE: internal/app/machined/pkg/system/services/registry/params.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "net/http" "path" "strings" "github.com/distribution/reference" "github.com/siderolabs/gen/xerrors" ) func extractParams(req *http.Request) (params, error) { registry := req.URL.Query().Get("ns") value := req.PathValue("args") parts := strings.Split(path.Clean(value), "/") if len(parts) < 3 { return params{}, xerrors.NewTaggedf[notFoundTag]("incorrect args value '%s'", value) } numParts := len(parts) isBlob := parts[numParts-2] == "blobs" isManifest := parts[numParts-2] == "manifests" if !isBlob && !isManifest { return params{}, xerrors.NewTaggedf[notFoundTag]("incorrect ref: '%s'", parts[numParts-2]) } name := strings.Join(parts[:numParts-2], "/") dig := parts[numParts-1] if !reference.NameRegexp.MatchString(name) { return params{}, xerrors.NewTaggedf[badRequestTag]("incorrect name: '%s'", name) } return params{registry: registry, name: name, dig: dig, isBlob: isBlob}, nil } type params struct { registry string name string dig string isBlob bool } func (p params) String() string { var result strings.Builder if p.registry != "" { result.WriteString(p.registry) result.WriteByte('/') } result.WriteString(p.name) if strings.HasPrefix(p.dig, "sha256:") { result.WriteByte('@') result.WriteString(p.dig) } else { result.WriteByte(':') result.WriteString(p.dig) } return result.String() } ================================================ FILE: internal/app/machined/pkg/system/services/registry/readers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "errors" "fmt" "io" "io/fs" "github.com/containerd/containerd/v2/core/content" "github.com/containerd/errdefs" ) var ( errInvalidSize = errors.New("readerat: invalid size") errSeekToInvalidWhence = errors.New("readerat: seek to invalid whence") errSeekToNegativePosition = errors.New("readerat: seek to negative position") ) // readSeeker is an io.ReadSeeker implementation based on an io.ReaderAt (and // an int64 size). // // For example, an os.File is both an io.ReaderAt and an io.ReadSeeker, but its // io.ReadSeeker methods are not safe to use concurrently. In comparison, // multiple readerat.readSeeker values (using the same os.File as their // io.ReaderAt) are safe to use concurrently. Each can Read and Seek // independently. // // A single readerat.readSeeker is not safe to use concurrently. // // Do not modify its exported fields after calling any of its methods. type readSeeker struct { ReaderAt io.ReaderAt Size int64 offset int64 } // Read implements io.Reader. func (r *readSeeker) Read(p []byte) (int, error) { if r.Size < 0 { return 0, errInvalidSize } else if r.Size <= r.offset { return 0, io.EOF } if length := r.Size - r.offset; int64(len(p)) > length { p = p[:length] } if len(p) == 0 { return 0, nil } actual, err := r.ReaderAt.ReadAt(p, r.offset) r.offset += int64(actual) if err == nil && r.offset == r.Size { err = io.EOF } return actual, err } // Seek implements io.Seeker. func (r *readSeeker) Seek(offset int64, whence int) (int64, error) { if r.Size < 0 { return 0, errInvalidSize } switch whence { case io.SeekStart: // No-op. case io.SeekCurrent: offset += r.offset case io.SeekEnd: offset += r.Size default: return 0, errSeekToInvalidWhence } if offset < 0 { return 0, errSeekToNegativePosition } r.offset = offset return r.offset, nil } // openReaderAt creates ReaderAt from a file. func openReaderAt(p string, statFS fs.StatFS) (content.ReaderAt, error) { fi, err := statFS.Stat(p) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return nil, err } return nil, fmt.Errorf("blob not found: %w", errdefs.ErrNotFound) } fp, err := statFS.Open(p) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return nil, err } return nil, fmt.Errorf("blob not found: %w", errdefs.ErrNotFound) } f, ok := fp.(fsFileReaderAt) if !ok { return nil, fmt.Errorf("not a fsFileReaderAt: %T, details: %v", fp, fp) } return sizeReaderAt{size: fi.Size(), fp: f}, nil } // readerat implements io.ReaderAt in a completely stateless manner by opening // the referenced file for each call to ReadAt. type sizeReaderAt struct { size int64 fp fsFileReaderAt } func (ra sizeReaderAt) ReadAt(p []byte, offset int64) (int, error) { return ra.fp.ReadAt(p, offset) } func (ra sizeReaderAt) Size() int64 { return ra.size } func (ra sizeReaderAt) Close() error { return ra.fp.Close() } func (ra sizeReaderAt) Reader() io.Reader { return io.LimitReader(ra.fp, ra.size) } type fsFileReaderAt interface { io.ReaderAt fs.File } ================================================ FILE: internal/app/machined/pkg/system/services/registry/registry.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package registry provides a simple container registry service. package registry import ( "bytes" "cmp" "context" "crypto/sha256" "encoding/json" "errors" "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "time" "github.com/containerd/containerd/v2/core/content" "github.com/distribution/reference" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/siderolabs/gen/xerrors" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/constants" ) // NewService creates a new instance of the registry service. func NewService(root fs.StatFS, logger *zap.Logger) *Service { return &Service{root: root, logger: logger} } // Service is a container registry service. type Service struct { logger *zap.Logger root fs.StatFS } type config struct { addr string tlsKeyPath string tlsCertPath string } // Option is a functional option for configuring the service. type Option func(*config) // WithTLS enables TLS with the given certificate and key paths. func WithTLS(certPath, keyPath string) Option { return func(c *config) { c.tlsCertPath = certPath c.tlsKeyPath = keyPath } } // WithAddress sets the address to listen on. func WithAddress(addr string) Option { return func(c *config) { c.addr = addr } } // Run is an entrypoint to the API service. func (svc *Service) Run(ctx context.Context, options ...Option) error { mux := http.NewServeMux() mux.HandleFunc("GET /v2/{args...}", svc.serveHTTP) giveOk := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } for _, p := range []string{"v2", "healthz"} { mux.HandleFunc("GET /"+p, giveOk) mux.HandleFunc("GET /"+p+"/{$}", giveOk) } cfg := &config{ addr: constants.RegistrydListenAddress, } for _, option := range options { option(cfg) } server := http.Server{Addr: cfg.addr, Handler: mux} errCh := make(chan error, 1) ctx, cancel := context.WithCancel(ctx) defer cancel() context.AfterFunc(ctx, func() { svc.logger.Info("shutting down registry server", zap.String("addr", server.Addr)) shutdownCtx, shutdownCtxCancel := context.WithTimeout(context.Background(), 5*time.Second) defer shutdownCtxCancel() errCh <- server.Shutdown(shutdownCtx) }) svc.logger.Info("starting registry server", zap.String("addr", server.Addr)) var err error if cfg.tlsCertPath != "" && cfg.tlsKeyPath != "" { err = server.ListenAndServeTLS(cfg.tlsCertPath, cfg.tlsKeyPath) if errors.Is(err, http.ErrServerClosed) { err = nil } } else { err = server.ListenAndServe() if errors.Is(err, http.ErrServerClosed) { err = nil } } cancel() err = cmp.Or(err, <-errCh) svc.logger.Info("registry server stopped", zap.Error(err)) return err } func (svc *Service) serveHTTP(w http.ResponseWriter, req *http.Request) { if err := svc.handler(w, req); err != nil { svc.logger.Error("failed to handle request", zap.Error(err)) w.WriteHeader(getStatusCode(err)) } } //nolint:gocyclo func (svc *Service) handler(w http.ResponseWriter, req *http.Request) error { logger := svc.logger.With( zap.String("method", req.Method), zap.String("url", req.URL.String()), zap.String("remote_addr", req.RemoteAddr), ) p, err := extractParams(req) if err != nil { return fmt.Errorf("failed to extract params: %w", err) } logger.Info( "image request", zap.String("name", p.name), zap.String("digest", p.dig), zap.Bool("is_blob", p.isBlob), zap.String("registry", p.registry), ) if p.registry == "" { p.registry, err = svc.tryFindRegistry(p) if err != nil { return err } if p.registry == "" { return fmt.Errorf("failed to extract params: %w", xerrors.NewTaggedf[badRequestTag]("missing ns")) } } ref, err := svc.resolveCanonicalRef(p) if err != nil { return err } var s content.Store if p.isBlob { s = &singleFileStore{root: svc.root, path: "blob"} } else { refName := handleRegistryWithPort(ref, p) s = &singleFileStore{root: svc.root, path: filepath.Join("manifests", refName, "digest")} } info, err := s.Info(req.Context(), ref.Digest()) if err != nil { return err } w.Header().Set("Content-Length", strconv.FormatInt(info.Size, 10)) w.Header().Set("Docker-Content-Digest", ref.Digest().String()) if !p.isBlob { manType, manBlob, err := getManifestData(req.Context(), s, ref) if err != nil { return err } w.Header().Set("Content-Type", manType) if req.Method == http.MethodHead { return nil // nothing to do here } http.ServeContent(w, req, ref.Digest().String(), info.UpdatedAt, bytes.NewReader(manBlob)) return nil } reader, err := s.ReaderAt(req.Context(), ocispec.Descriptor{Digest: info.Digest}) if err != nil { return xerrors.NewTaggedf[internalErrorTag]("failed to get content reader: %w", err) } readerCloser := sync.OnceValue(reader.Close) defer readerCloser() //nolint:errcheck http.ServeContent(w, req, ref.Digest().String(), info.UpdatedAt, &readSeeker{ReaderAt: reader, Size: info.Size}) return readerCloser() } func (svc *Service) resolveCanonicalRef(p params) (reference.Canonical, error) { ref, err := reference.ParseDockerRef(p.String()) if err != nil { return nil, xerrors.NewTaggedf[badRequestTag]("failed to parse docker ref %q: %w", p.String(), err) } cRef, ok := ref.(reference.Canonical) if ok { return cRef, nil } namedTagged, ok := ref.(reference.NamedTagged) if !ok { return nil, xerrors.NewTaggedf[internalErrorTag]("incorrect reference type: %T", ref) } namedTaggedName := handleRegistryWithPort(namedTagged, p) taggedFile := filepath.Join("manifests", namedTaggedName, "reference", namedTagged.Tag()) ntSum, err := hashFile(taggedFile, svc.root) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil, xerrors.NewTagged[notFoundTag](err) } return nil, xerrors.NewTaggedf[internalErrorTag]("failed to hash manifest: %w", err) } digestString := strings.ReplaceAll(digest.NewDigestFromBytes(digest.SHA256, ntSum).String(), "sha256:", "sha256-") sha256file := filepath.Join("manifests", namedTaggedName, "digest", digestString) sSum, err := hashFile(sha256file, svc.root) if err != nil { return nil, xerrors.NewTaggedf[internalErrorTag]("failed to hash '%x': %w", sSum, err) } if !bytes.Equal(ntSum, sSum) { return nil, xerrors.NewTaggedf[internalErrorTag]("hash for '%s' is not equal for hash to '%s'", taggedFile, sha256file) } return &canonical{ NamedTagged: namedTagged, digest: digest.NewDigestFromBytes(digest.SHA256, ntSum), }, nil } func handleRegistryWithPort(namedTagged reference.Named, p params) string { namedTaggedName := namedTagged.Name() idx := strings.LastIndex(p.registry, ":") if idx > 0 { path := strings.TrimPrefix(namedTaggedName, p.registry) namedTaggedName = p.registry[:idx] + "_" + p.registry[idx+1:] + "_" + path } return namedTaggedName } func createRegistryWithPort(s string) string { parts := strings.SplitN(s, "_", 3) if len(parts) < 3 { return s } return parts[0] + ":" + parts[1] + parts[2] } func hashFile(f string, where fs.FS) (_ []byte, returnErr error) { data, err := where.Open(f) if err != nil { return nil, err } defer func() { returnErr = cmp.Or(returnErr, data.Close()) }() h := sha256.New() if _, err = io.Copy(h, data); err != nil { return nil, err } return h.Sum(nil), nil } func getManifestData(ctx context.Context, store content.Store, ref reference.Canonical) (string, []byte, error) { manifestBlob, err := content.ReadBlob(ctx, store, ocispec.Descriptor{Digest: ref.Digest()}) if err != nil { return "", nil, xerrors.NewTaggedf[internalErrorTag]("failed to read content blob: %w", err) } var manifest struct { MediaType string `json:"mediaType"` Manifests json.RawMessage `json:"manifests"` Layers json.RawMessage `json:"layers"` Config json.RawMessage `json:"config"` } if err = json.Unmarshal(manifestBlob, &manifest); err != nil { return "", nil, xerrors.NewTaggedf[internalErrorTag]("failed to unmarshal manifest: %w", err) } mediaType := manifest.MediaType if mediaType == "" { // OCI manifests may omit the top-level mediaType field; infer from structure. switch { case manifest.Manifests != nil: mediaType = ocispec.MediaTypeImageIndex case manifest.Layers != nil || manifest.Config != nil: mediaType = ocispec.MediaTypeImageManifest default: return "", nil, xerrors.NewTaggedf[internalErrorTag]("media type is empty and cannot be inferred") } } return mediaType, manifestBlob, nil } type canonical struct { reference.NamedTagged digest digest.Digest } func (c *canonical) String() string { return c.NamedTagged.String() + "@" + c.digest.Encoded() } func (c *canonical) Digest() digest.Digest { return c.digest } func getStatusCode(err error) int { switch { case xerrors.TagIs[notFoundTag](err): return http.StatusNotFound case xerrors.TagIs[badRequestTag](err): return http.StatusBadRequest case xerrors.TagIs[internalErrorTag](err): fallthrough default: return http.StatusInternalServerError } } type ( notFoundTag struct{} badRequestTag struct{} internalErrorTag struct{} ) func (svc *Service) tryFindRegistry(p params) (string, error) { entries, err := fs.ReadDir(svc.root, "manifests") if err != nil { return "", xerrors.NewTaggedf[internalErrorTag]("failed to read manifests directory: %w", err) } for _, entry := range entries { p.registry = createRegistryWithPort(entry.Name()) ref, err := reference.ParseDockerRef(p.String()) if err != nil { return "", xerrors.NewTaggedf[badRequestTag]("failed to parse docker ref %q: %w", p.String(), err) } namedTaggedName := handleRegistryWithPort(ref, p) taggedFile := filepath.Join("manifests", namedTaggedName, "reference") if _, err := fs.Stat(svc.root, taggedFile); err == nil { return p.registry, nil } } return "", nil } ================================================ FILE: internal/app/machined/pkg/system/services/registry/registry_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry_test import ( "archive/tar" "cmp" "context" "errors" "fmt" "io" "net/http" "os" "path/filepath" "strings" "sync" "testing" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/siderolabs/gen/xtesting/check" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services/registry" "github.com/siderolabs/talos/pkg/imager/cache" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestRegistry(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") } cacheDir := t.TempDir() images := []string{ fmt.Sprintf("%s:%s", constants.CoreDNSImage, constants.DefaultCoreDNSVersion), fmt.Sprintf("%s:%s", strings.ReplaceAll(constants.CoreDNSImage, "registry.k8s.io", "registry.k8s.io:443"), constants.DefaultCoreDNSVersion), } platform, err := v1.ParsePlatform("linux/amd64") assert.NoError(t, err) assert.NoError(t, cache.Generate(images, []string{platform.String()}, false, "", cacheDir, false, false)) l, err := layout.ImageIndexFromPath(cacheDir) assert.NoError(t, err) m, err := l.IndexManifest() assert.NoError(t, err) image, err := l.Image(m.Manifests[0].Digest) assert.NoError(t, err) registryRoot := t.TempDir() tarExtract(t, image, registryRoot) logger := zaptest.NewLogger(t) it := func(yield func(string) bool) { if !yield(registryRoot) { return } } svc := registry.NewService(registry.NewMultiPathFS(it), logger) var wg sync.WaitGroup wg.Add(1) ctx, cancel := context.WithCancelCause(t.Context()) defer cancel(nil) go func() { defer wg.Done() cancel(cmp.Or(svc.Run(ctx), errors.New("service exited"))) }() defer wg.Wait() for _, image := range images { t.Run(image, func(t *testing.T) { ref, err := name.ParseReference(image) assert.NoError(t, err) manifest, err := crane.Manifest(ref.String()) assert.NoError(t, err) rmt, err := remote.Get(ref, remote.WithPlatform(*platform)) assert.NoError(t, err) for _, path := range []string{ fmt.Sprintf("/v2/%s/manifests/%s?ns=%s", ref.Context().RepositoryStr(), constants.DefaultCoreDNSVersion, ref.Context().RegistryStr()), fmt.Sprintf("/v2/%s/manifests/%s?ns=%s", ref.Context().RepositoryStr(), rmt.Digest.String(), ref.Context().RegistryStr()), } { req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://"+constants.RegistrydListenAddress+path, nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) if resp != nil { t.Cleanup(func() { assert.NoError(t, resp.Body.Close()) }) } assert.Equal(t, http.StatusOK, pointer.SafeDeref(resp).StatusCode, "unexpected status code") assert.Equal(t, string(manifest), readAll(t, resp)) } img, err := rmt.Image() assert.NoError(t, err) layers, err := img.Layers() assert.NoError(t, err) handleLayers(ctx, t, layers, ref) }) } tests := []struct { name string path string method string check check.Check expectedCode int }{ { name: "HEAD /v2/", path: "/v2/", method: http.MethodHead, check: check.NoError(), expectedCode: http.StatusOK, }, { name: "GET /v2/", path: "/v2/", method: http.MethodGet, check: check.NoError(), expectedCode: http.StatusOK, }, { name: "HEAD /healthz", path: "/healthz", method: http.MethodHead, check: check.NoError(), expectedCode: http.StatusOK, }, { name: "GET /healthz", path: "/healthz", method: http.MethodGet, check: check.NoError(), expectedCode: http.StatusOK, }, { name: "GET /v2/alpine/manifests/3.20.3", path: "/v2/alpine/manifests/3.20.3", method: http.MethodGet, check: check.NoError(), expectedCode: http.StatusBadRequest, }, { name: "GET /v2/alpine/manifests/3.20.3?ns=docker.io", path: "/v2/alpine/manifests/3.20.3?ns=docker.io", method: http.MethodGet, check: check.NoError(), expectedCode: http.StatusNotFound, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { req, err := http.NewRequestWithContext(ctx, test.method, "http://"+constants.RegistrydListenAddress+test.path, nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) test.check(t, err) if resp != nil { t.Cleanup(func() { assert.NoError(t, resp.Body.Close()) }) } assert.Equal(t, test.expectedCode, pointer.SafeDeref(resp).StatusCode, "unexpected status code, body is %s", readAll(t, resp)) }) } cancel(nil) wg.Wait() err = ctx.Err() if err == context.Canceled || err == context.DeadlineExceeded { err = nil } assert.NoError(t, err) } func handleLayers(ctx context.Context, t *testing.T, layers []v1.Layer, ref name.Reference) { for _, layer := range layers { dig, err := layer.Digest() assert.NoError(t, err) path := fmt.Sprintf("/v2/%s/blobs/%s?ns=%s", ref.Context().RepositoryStr(), dig, ref.Context().RegistryStr()) req, err := http.NewRequestWithContext(ctx, http.MethodHead, "http://"+constants.RegistrydListenAddress+path, nil) assert.NoError(t, err) resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) if resp != nil { t.Cleanup(func() { assert.NoError(t, resp.Body.Close()) }) } assert.Equal(t, http.StatusOK, pointer.SafeDeref(resp).StatusCode, "unexpected status code") } } func tarExtract(t *testing.T, img v1.Image, dest string) { pipeReader, pipeWriter := io.Pipe() go func() { pipeWriter.CloseWithError(crane.Export(img, pipeWriter)) }() tr := tar.NewReader(pipeReader) for { header, err := tr.Next() if err == io.EOF { break } assert.NoError(t, err) switch header.Typeflag { case tar.TypeDir: assert.NoError(t, os.MkdirAll(filepath.Join(dest, header.Name), 0o755)) case tar.TypeReg: f, err := os.Create(filepath.Join(dest, header.Name)) assert.NoError(t, err) _, err = io.Copy(f, tr) assert.NoError(t, err) default: assert.Failf(t, "unexpected tar entry type", "type: %v", header.Typeflag) } } } func readAll(t *testing.T, resp *http.Response) string { if resp == nil || resp.Body == nil { return "" } var builder strings.Builder _, err := io.Copy(&builder, resp.Body) assert.NoError(t, err) if builder.String() == "" { return "" } return builder.String() } ================================================ FILE: internal/app/machined/pkg/system/services/registry/store.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "context" "errors" "fmt" "io/fs" "path/filepath" "strings" "github.com/containerd/containerd/v2/core/content" "github.com/containerd/errdefs" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/siderolabs/gen/xerrors" ) type singleFileStore struct { root fs.StatFS path string } // Info implements [content.InfoProvider] reading method. func (s *singleFileStore) Info(_ context.Context, dgst digest.Digest) (content.Info, error) { p, err := s.blobPath(dgst) if err != nil { return content.Info{}, fmt.Errorf("calculating blob info path: %w", err) } fi, err := s.root.Stat(p) if err != nil { if errors.Is(err, fs.ErrNotExist) || errors.Is(err, errdefs.ErrNotFound) { return content.Info{}, xerrors.NewTaggedf[notFoundTag]("content '%s': %w", dgst, errdefs.ErrNotFound) } return content.Info{}, xerrors.NewTagged[internalErrorTag](err) } return content.Info{ Digest: dgst, Size: fi.Size(), CreatedAt: fi.ModTime(), UpdatedAt: getATime(fi), }, nil } // ReaderAt implements [content.Provider] reading method. func (s *singleFileStore) ReaderAt(_ context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) { p, err := s.blobPath(desc.Digest) if err != nil { return nil, fmt.Errorf("calculating blob path for ReaderAt: %w", err) } reader, err := openReaderAt(p, s.root) if err != nil { return nil, fmt.Errorf("blob '%s' expected at '%s': %w", desc.Digest, p, err) } return reader, nil } // Status implements [content.IngestManager] ingesting which we don't need. func (s *singleFileStore) Status(context.Context, string) (content.Status, error) { return content.Status{}, errUnimplemented } // ListStatuses implements [content.IngestManager] ingesting which we don't need. func (s *singleFileStore) ListStatuses(context.Context, ...string) ([]content.Status, error) { return nil, errUnimplemented } // Abort implements [content.IngestManager] ingesting which we don't need. func (s *singleFileStore) Abort(context.Context, string) error { return errUnimplemented } // Writer implements [content.Ingester] ingesting which we don't need. func (s *singleFileStore) Writer(context.Context, ...content.WriterOpt) (content.Writer, error) { return nil, errUnimplemented } // Walk implements [content.Manager] ingesting which we don't need. func (s *singleFileStore) Walk(context.Context, content.WalkFunc, ...string) error { return errUnimplemented } // Delete implements [content.Manager] ingesting which we don't need. func (s *singleFileStore) Delete(context.Context, digest.Digest) error { return errUnimplemented } // Update implements [content.Manager] ingesting which we don't need. func (s *singleFileStore) Update(context.Context, content.Info, ...string) (content.Info, error) { return content.Info{}, errUnimplemented } func (s *singleFileStore) blobPath(dgst digest.Digest) (string, error) { if err := dgst.Validate(); err != nil { return "", fmt.Errorf("cannot calculate blob path from invalid digest: %v: %w", err, errdefs.ErrInvalidArgument) } return filepath.Join(s.path, strings.ReplaceAll(dgst.String(), "sha256:", "sha256-")), nil } var errUnimplemented = errors.New("unimplemented") ================================================ FILE: internal/app/machined/pkg/system/services/registry/store_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "os" "syscall" "time" ) func getATime(fi os.FileInfo) time.Time { if st, ok := fi.Sys().(*syscall.Stat_t); ok { return time.Unix(st.Atim.Unix()) } return fi.ModTime() } ================================================ FILE: internal/app/machined/pkg/system/services/registry/store_unix.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build darwin || freebsd || openbsd || netbsd || dragonfly package registry import ( "os" "syscall" "time" ) func getATime(fi os.FileInfo) time.Time { if st, ok := fi.Sys().(*syscall.Stat_t); ok { return time.Unix(int64(st.Atimespec.Sec), int64(st.Atimespec.Nsec)) } return fi.ModTime() } ================================================ FILE: internal/app/machined/pkg/system/services/registry/store_windows.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build windows package registry import ( "os" "time" ) func getATime(fi os.FileInfo) time.Time { return fi.ModTime() } ================================================ FILE: internal/app/machined/pkg/system/services/registryd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "io" "os" "github.com/cosi-project/runtime/pkg/safe" "go.uber.org/zap" "go.uber.org/zap/zapcore" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/internal/app/machined/pkg/system/services/registry" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/logging" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) type registryD struct{} // RegistryID is the ID of the registry service. const RegistryID = "registryd" // NewRegistryD returns a new docker mirror registry service. func NewRegistryD() system.Service { return ®istryD{} } func (r *registryD) ID(runtime.Runtime) string { return RegistryID } func (r *registryD) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } func (r *registryD) PreFunc(context.Context, runtime.Runtime) error { return nil } func (r *registryD) PostFunc(runtime.Runtime, events.ServiceState) error { return nil } func (r *registryD) Condition(runtime.Runtime) conditions.Condition { return nil } func (r *registryD) DependsOn(runtime.Runtime) []string { return nil } func (r *registryD) Volumes(runtime.Runtime) []string { return nil } func (r *registryD) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { return simpleHealthCheck(ctx, "http://"+constants.RegistrydListenAddress+"/healthz") } } func (r *registryD) Runner(rt runtime.Runtime) (runner.Runner, error) { return goroutine.NewRunner(rt, "registryd", func(ctx context.Context, r runtime.Runtime, logOutput io.Writer) error { logger := logging.ZapLogger( logging.NewLogDestination(logOutput, zapcore.DebugLevel, logging.WithColoredLevels()), ) st := r.State().V1Alpha2().Resources() it := func(yield func(string) bool) { imageCacheConfig, err := safe.StateGetByID[*cri.ImageCacheConfig](ctx, st, cri.ImageCacheConfigID) if err != nil { logger.Error("failed to get image cache config", zap.Error(err)) return } for _, root := range imageCacheConfig.TypedSpec().Roots { if _, err = os.Stat(root); err != nil { logger.Error("failed to stat image cache root", zap.String("root", root), zap.Error(err)) continue } if !yield(root) { return } } } return registry.NewService(registry.NewMultiPathFS(it), logger).Run(ctx) }, runner.WithLoggingManager(rt.Logging())), nil } ================================================ FILE: internal/app/machined/pkg/system/services/syslogd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/goroutine" "github.com/siderolabs/talos/internal/app/syslogd" "github.com/siderolabs/talos/pkg/conditions" ) const syslogServiceID = "syslogd" var _ system.HealthcheckedService = (*Syslogd)(nil) // Syslogd implements the Service interface. It serves as the concrete type with // the required methods. type Syslogd struct{} // ID implements the Service interface. func (s *Syslogd) ID(runtime.Runtime) string { return syslogServiceID } // PreFunc implements the Service interface. func (s *Syslogd) PreFunc(context.Context, runtime.Runtime) error { return nil } // PostFunc implements the Service interface. func (s *Syslogd) PostFunc(runtime.Runtime, events.ServiceState) (err error) { return nil } // Condition implements the Service interface. func (s *Syslogd) Condition(runtime.Runtime) conditions.Condition { return nil } // DependsOn implements the Service interface. func (s *Syslogd) DependsOn(runtime.Runtime) []string { return []string{machinedServiceID} } // Volumes implements the Service interface. func (s *Syslogd) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (s *Syslogd) Runner(r runtime.Runtime) (runner.Runner, error) { return goroutine.NewRunner(r, syslogServiceID, syslogd.Main, runner.WithLoggingManager(r.Logging())), nil } // HealthFunc implements the HealthcheckedService interface. func (s *Syslogd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { return nil } } // HealthSettings implements the HealthcheckedService interface. func (s *Syslogd) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/trustd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:golint package services import ( "context" "errors" "fmt" "net" "os" "path/filepath" "time" "github.com/containerd/containerd/v2/pkg/cap" "github.com/containerd/containerd/v2/pkg/oci" "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/server" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-debug" "google.golang.org/grpc" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" timeresource "github.com/siderolabs/talos/pkg/machinery/resources/time" ) var _ system.HealthcheckedService = (*Trustd)(nil) // Trustd implements the Service interface. It serves as the concrete type with // the required methods. type Trustd struct { runtimeServer *grpc.Server } // ID implements the Service interface. func (t *Trustd) ID(runtime.Runtime) string { return "trustd" } // PreFunc implements the Service interface. // //nolint:gocyclo func (t *Trustd) PreFunc(ctx context.Context, r runtime.Runtime) error { // filter apid access to make sure apid can only access its certificates resources := state.Filter( r.State().V1Alpha2().Resources(), func(ctx context.Context, access state.Access) error { if !access.Verb.Readonly() { return errors.New("write access denied") } switch { case access.ResourceNamespace == secrets.NamespaceName && access.ResourceType == secrets.TrustdType && access.ResourceID == secrets.TrustdID: case access.ResourceNamespace == secrets.NamespaceName && access.ResourceType == secrets.OSRootType && access.ResourceID == secrets.OSRootID: default: return errors.New("access denied") } return nil }, ) // ensure socket dir exists if err := os.MkdirAll(filepath.Dir(constants.TrustdRuntimeSocketPath), 0o750); err != nil { return err } // set the final leaf to be world-executable to make trustd connect to the socket if err := os.Chmod(filepath.Dir(constants.TrustdRuntimeSocketPath), 0o751); err != nil { return err } // clean up the socket if it already exists (important for Talos in a container) if err := os.RemoveAll(constants.TrustdRuntimeSocketPath); err != nil { return err } listener, err := (&net.ListenConfig{}).Listen(ctx, "unix", constants.TrustdRuntimeSocketPath) if err != nil { return err } if err := selinux.SetLabel(constants.TrustdRuntimeSocketPath, constants.TrustdRuntimeSocketLabel); err != nil { return err } // chown the socket path to make it accessible to the apid if err := os.Chown(constants.TrustdRuntimeSocketPath, constants.TrustdUserID, constants.TrustdUserID); err != nil { return err } t.runtimeServer = grpc.NewServer( grpc.SharedWriteBuffer(true), ) v1alpha1.RegisterStateServer(t.runtimeServer, server.NewState(resources)) go t.runtimeServer.Serve(listener) //nolint:errcheck return prepareRootfs(t.ID(r)) } // PostFunc implements the Service interface. func (t *Trustd) PostFunc(runtime.Runtime, events.ServiceState) (err error) { t.runtimeServer.Stop() return os.RemoveAll(constants.TrustdRuntimeSocketPath) } // Condition implements the Service interface. func (t *Trustd) Condition(r runtime.Runtime) conditions.Condition { return conditions.WaitForAll( timeresource.NewSyncCondition(r.State().V1Alpha2().Resources()), network.NewReadyCondition(r.State().V1Alpha2().Resources(), network.AddressReady, network.HostnameReady), ) } // DependsOn implements the Service interface. func (t *Trustd) DependsOn(runtime.Runtime) []string { return []string{"containerd"} } // Volumes implements the Service interface. func (t *Trustd) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (t *Trustd) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := runner.Args{ ID: t.ID(r), ProcessArgs: []string{"/trustd"}, } // Set the mounts. mounts := []specs.Mount{ {Type: "bind", Destination: filepath.Dir(constants.TrustdRuntimeSocketPath), Source: filepath.Dir(constants.TrustdRuntimeSocketPath), Options: []string{"rbind", "ro"}}, } mounts = bindMountContainerMarker(mounts) env := environment.Get(r.Config()) env = append(env, constants.EnvTcellMinimizeEnvironment, constants.EnvTrustdGomemlimit(), ) if debug.RaceEnabled { env = append(env, constants.EnvGoraceHaltOnError) } if fipsmode.Strict() { env = append(env, constants.EnvFIPS140ModeStrict) } return restart.New(containerd.NewRunner( r.Config().Debug(), &args, runner.WithLoggingManager(r.Logging()), runner.WithContainerdAddress(constants.SystemContainerdAddress), runner.WithEnv(env), runner.WithCgroupPath(constants.CgroupTrustd), runner.WithGracefulShutdownTimeout(15*time.Second), runner.WithSelinuxLabel(constants.SelinuxLabelTrustd), runner.WithOCISpecOpts( oci.WithDroppedCapabilities(cap.Known()), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithMounts(mounts), oci.WithRootFSPath(filepath.Join(constants.SystemLibexecPath, t.ID(r))), oci.WithRootFSReadonly(), oci.WithUser(fmt.Sprintf("%d:%d", constants.TrustdUserID, constants.TrustdUserID)), ), runner.WithOOMScoreAdj(-998), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. func (t *Trustd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { var d net.Dialer conn, err := d.DialContext(ctx, "tcp", fmt.Sprintf("%s:%d", "127.0.0.1", constants.TrustdPort)) if err != nil { return err } return conn.Close() } } // HealthSettings implements the HealthcheckedService interface. func (t *Trustd) HealthSettings(runtime.Runtime) *health.Settings { return &health.DefaultSettings } ================================================ FILE: internal/app/machined/pkg/system/services/udevd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "context" "fmt" "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/health" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/restart" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/constants" ) var _ system.HealthcheckedService = (*Udevd)(nil) // Udevd implements the Service interface. It serves as the concrete type with // the required methods. type Udevd struct { ExtraSettleTime time.Duration triggered bool extraSettleStart time.Time } // ID implements the Service interface. func (c *Udevd) ID(runtime.Runtime) string { return "udevd" } // PreFunc implements the Service interface. func (c *Udevd) PreFunc(ctx context.Context, r runtime.Runtime) error { _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"hwdb", "--update", "--root=/usr"}) return err } // PostFunc implements the Service interface. func (c *Udevd) PostFunc(runtime.Runtime, events.ServiceState) (err error) { return nil } // Condition implements the Service interface. func (c *Udevd) Condition(runtime.Runtime) conditions.Condition { return nil } // DependsOn implements the Service interface. func (c *Udevd) DependsOn(runtime.Runtime) []string { return nil } // Volumes implements the Service interface. func (c *Udevd) Volumes(runtime.Runtime) []string { return nil } // Runner implements the Service interface. func (c *Udevd) Runner(r runtime.Runtime) (runner.Runner, error) { // Set the process arguments. args := &runner.Args{ ID: c.ID(r), ProcessArgs: []string{ "/sbin/systemd-udevd", "--resolve-names=never", }, } debug := false if r.Config() != nil { debug = r.Config().Debug() } return restart.New(process.NewRunner( debug, args, runner.WithLoggingManager(r.Logging()), runner.WithCgroupPath(constants.CgroupUdevd), runner.WithSelinuxLabel(constants.SelinuxLabelUdevd), runner.WithDroppedCapabilities(constants.UdevdDroppedCapabilities), runner.WithEnv([]string{ constants.EnvXDGRuntimeDir, }), ), restart.WithType(restart.Forever), ), nil } // HealthFunc implements the HealthcheckedService interface. // //nolint:gocyclo func (c *Udevd) HealthFunc(runtime.Runtime) health.Check { return func(ctx context.Context) error { // checking for the existence of the udev control socket is a faster way to check // that udevd is running, but not a complete check since the socket can persist if the process // was not gracefully stopped if err := conditions.WaitForFileToExist("/run/udev/control").Wait(ctx); err != nil { return err } // udevadm trigger returns with an exit code of 0 even if udevd is not fully running, // so running `udevadm control --reload` to ensure that udevd is fully initialized // which returns an exit code of 2 if udevd is not running. This complements the previous check if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"control", "--reload"}); err != nil { return err } if !c.triggered { if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"trigger", "--type=devices", "--action=add"}); err != nil { return err } if _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"trigger", "--type=subsystems", "--action=add"}); err != nil { return err } c.triggered = true } // This ensures that `udevd` finishes processing kernel events, triggered by // `udevd trigger`, to prevent a race condition when a user specifies a path // under `/dev/disk/*` in any disk definitions. _, err := cmd.RunWithOptions(ctx, "/sbin/udevadm", []string{"settle", "--timeout=50"}) // timeout here should be less than health.Settings.Timeout if err != nil { return err } // If we got to the point where everything is settled, and the healthcheck would report // success, we start the extra settle timer. if c.extraSettleStart.IsZero() { c.extraSettleStart = time.Now() } // Wait for c.ExtraSettleTime before returning success (if configured). if c.ExtraSettleTime <= 0 { return nil } settleEnd := c.extraSettleStart.Add(c.ExtraSettleTime) if time.Now().After(settleEnd) { return nil } // Can we wait until the health check deadline? if deadline, ok := ctx.Deadline(); ok { // if the deadline is before the settleEnd, we should wait until the deadline if settleEnd.Before(deadline) { time.Sleep(time.Until(settleEnd)) return nil } } return fmt.Errorf("waiting for udevd for extra settle timeout") } } // HealthSettings implements the HealthcheckedService interface. func (c *Udevd) HealthSettings(runtime.Runtime) *health.Settings { return &health.Settings{ InitialDelay: 100 * time.Millisecond, Period: time.Minute, Timeout: 55 * time.Second, } } ================================================ FILE: internal/app/machined/pkg/system/services/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services import ( "fmt" "os" "path/filepath" specs "github.com/opencontainers/runtime-spec/specs-go" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/containermode" "github.com/siderolabs/talos/pkg/machinery/constants" ) // prepareRootfs creates /system/libexec/ rootfs and bind-mounts /sbin/init there. func prepareRootfs(id string) error { rootfsPath := filepath.Join(constants.SystemLibexecPath, id) if err := os.MkdirAll(rootfsPath, 0o711); err != nil { // rwx--x--x, non-root programs should be able to follow path return fmt.Errorf("failed to create rootfs %q: %w", rootfsPath, err) } executablePath := filepath.Join(rootfsPath, id) if err := os.WriteFile(executablePath, nil, 0o555); err != nil { // r-xr-xr-x, non-root programs should be able to execute & read return fmt.Errorf("failed to create empty executable %q: %w", executablePath, err) } if err := unix.Mount("/sbin/init", executablePath, "", unix.MS_BIND, ""); err != nil { return fmt.Errorf("failed to create bind mount for %q: %w", executablePath, err) } return nil } // bindMountContainerMarker bind-mounts a file used for container detection into a container service. func bindMountContainerMarker(mounts []specs.Mount) []specs.Mount { if containermode.InContainer() { mounts = append( mounts, specs.Mount{Type: "bind", Destination: constants.ContainerMarkerFilePath, Source: constants.ContainerMarkerFilePath, Options: []string{"bind", "ro"}}, ) } return mounts } ================================================ FILE: internal/app/machined/pkg/system/system.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "cmp" "context" "errors" "fmt" "log" stdmaps "maps" "slices" "strings" "sync" "time" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/pkg/conditions" ) // singleton the system services API interface. type singleton struct { runtime runtime.Runtime // State of running services by ID state map[string]*ServiceRunner // List of running services at the moment. // // Service might be in any state, but service ID in the map // implies ServiceRunner.Start() method is running at the momemnt runningMu sync.Mutex running map[string]struct{} mu sync.Mutex wg sync.WaitGroup terminating bool } var ( instance *singleton once sync.Once ) func newServices(runtime runtime.Runtime) *singleton { return &singleton{ runtime: runtime, state: map[string]*ServiceRunner{}, running: map[string]struct{}{}, } } // Services returns the instance of the system services API. // //nolint:revive,golint func Services(runtime runtime.Runtime) *singleton { once.Do(func() { instance = newServices(runtime) }) return instance } // Load adds service to the list of services managed by the runner. // // Load returns service IDs for each of the services. func (s *singleton) Load(services ...Service) []string { s.mu.Lock() defer s.mu.Unlock() if s.terminating { return nil } ids := make([]string, 0, len(services)) for _, service := range services { id := service.ID(s.runtime) ids = append(ids, id) if _, exists := s.state[id]; exists { // service already loaded, ignore continue } svcrunner := NewServiceRunner(s, service, s.runtime) s.state[id] = svcrunner } return ids } // Unload stops the service and removes it from the list of running services. // // It is not an error to unload a service which was already removed or stopped. func (s *singleton) Unload(ctx context.Context, serviceIDs ...string) error { s.mu.Lock() if s.terminating { s.mu.Unlock() return nil } servicesToRemove := make([]string, 0, len(serviceIDs)) for _, id := range serviceIDs { if _, exists := s.state[id]; exists { servicesToRemove = append(servicesToRemove, id) } } s.mu.Unlock() if err := s.Stop(ctx, servicesToRemove...); err != nil { return fmt.Errorf("error stopping services %v: %w", servicesToRemove, err) } s.mu.Lock() defer s.mu.Unlock() s.runningMu.Lock() defer s.runningMu.Unlock() for _, id := range servicesToRemove { delete(s.state, id) delete(s.running, id) // this fixes an edge case when defer() in Start() doesn't have time to remove stopped service from running } return nil } // Start will invoke the service's Pre, Condition, and Type funcs. If any // error occurs in the Pre or Condition invocations, it is up to the caller to // restart the service. func (s *singleton) Start(serviceIDs ...string) error { s.mu.Lock() defer s.mu.Unlock() if s.terminating { return nil } var multiErr *multierror.Error for _, id := range serviceIDs { svcrunner := s.state[id] if svcrunner == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("service %q not defined", id)) } s.runningMu.Lock() _, running := s.running[id] if !running { s.running[id] = struct{}{} } s.runningMu.Unlock() if running { // service already running, skip continue } runNotify := make(chan struct{}) s.wg.Add(1) go func(id string, svcrunner *ServiceRunner) { err := func() error { defer func() { s.runningMu.Lock() delete(s.running, id) s.runningMu.Unlock() }() defer s.wg.Done() return svcrunner.Run(runNotify) }() switch { case err == nil: svcrunner.UpdateState(context.Background(), events.StateFinished, "Service finished successfully") case errors.Is(err, ErrSkip): svcrunner.UpdateState(context.Background(), events.StateSkipped, "Service skipped") default: msg := err.Error() if len(msg) > 0 { msg = strings.ToUpper(msg[:1]) + msg[1:] } svcrunner.UpdateState(context.Background(), events.StateFailed, "%s", msg) } }(id, svcrunner) // wait for svcrunner.Run to enter the running phase, and then return <-runNotify } return multiErr.ErrorOrNil() } // StartAll starts all the services. func (s *singleton) StartAll() { s.mu.Lock() serviceIDs := maps.Keys(s.state) s.mu.Unlock() //nolint:errcheck s.Start(serviceIDs...) } // LoadAndStart combines Load and Start into single call. func (s *singleton) LoadAndStart(services ...Service) { err := s.Start(s.Load(services...)...) if err != nil { // should never happen panic(err) } } // Shutdown all the services. func (s *singleton) Shutdown(ctx context.Context) { s.mu.Lock() if s.terminating { s.mu.Unlock() return } s.terminating = true _ = s.stopServices(ctx, nil, true) //nolint:errcheck } // Stop will initiate a shutdown of the specified service. func (s *singleton) Stop(ctx context.Context, serviceIDs ...string) (err error) { if len(serviceIDs) == 0 { return err } s.mu.Lock() if s.terminating { s.mu.Unlock() return nil } return s.stopServices(ctx, serviceIDs, false) } // StopWithRevDepenencies will initiate a shutdown of the specified services waiting for reverse dependencies to finish first. // // If reverse dependency is not stopped, this method might block waiting on it being stopped for up to 30 seconds. func (s *singleton) StopWithRevDepenencies(ctx context.Context, serviceIDs ...string) (err error) { if len(serviceIDs) == 0 { return err } s.mu.Lock() if s.terminating { s.mu.Unlock() return nil } return s.stopServices(ctx, serviceIDs, true) } //nolint:gocyclo func (s *singleton) stopServices(ctx context.Context, services []string, waitForRevDependencies bool) error { servicesToStop := map[string]*ServiceRunner{} if services == nil { stdmaps.Copy(servicesToStop, s.state) } else { for _, name := range services { if _, ok := s.state[name]; !ok { continue } servicesToStop[name] = s.state[name] } } // build reverse dependencies, and expand the list of services to stop // with services which depend on the one being stopped reverseDependencies := map[string][]string{} if waitForRevDependencies { // expand the list of services to stop with the list of services which depend // on the ones being stopped // the loop is run as long as more dependencies are added to the list for { expanded := false for name, svcrunner := range s.state { if _, scheduledToStop := servicesToStop[name]; scheduledToStop { continue } dependencies := svcrunner.service.DependsOn(s.runtime) shouldStopService := false for _, dependency := range dependencies { for scheduledService := range servicesToStop { if scheduledService == dependency { shouldStopService = true break } } if shouldStopService { break } } if shouldStopService { servicesToStop[name] = svcrunner expanded = true } } if !expanded { break } } // build a list of dependencies to wait for before stopping each of the services for name, svcrunner := range servicesToStop { for _, dependency := range svcrunner.service.DependsOn(s.runtime) { reverseDependencies[dependency] = append(reverseDependencies[dependency], name) } } } s.mu.Unlock() // shutdown all the services waiting for rev deps var shutdownWg sync.WaitGroup // wait max 30 seconds for reverse deps to shut down shutdownCtx, shutdownCtxCancel := context.WithTimeout(ctx, 30*time.Second) defer shutdownCtxCancel() stoppedConds := make([]conditions.Condition, 0, len(servicesToStop)) for name, svcrunner := range servicesToStop { shutdownWg.Add(1) stoppedConds = append(stoppedConds, waitForService(s, StateEventDown, name)) go func(svcrunner *ServiceRunner, reverseDeps []string) { defer shutdownWg.Done() conds := xslices.Map(reverseDeps, func(dep string) conditions.Condition { return waitForService(s, StateEventDown, dep) }) allDeps := conditions.WaitForAll(conds...) if err := allDeps.Wait(shutdownCtx); err != nil { log.Printf("gave up on %s while stopping %q", allDeps, svcrunner.id) } svcrunner.Shutdown() }(svcrunner, reverseDependencies[name]) } shutdownWg.Wait() return conditions.WaitForAll(stoppedConds...).Wait(ctx) } // List returns snapshot of ServiceRunner instances. func (s *singleton) List() (result []*ServiceRunner) { s.mu.Lock() defer s.mu.Unlock() result = maps.Values(s.state) // TODO: results should be sorted properly with topological sort on dependencies // but, we don't have dependencies yet, so sort by service id for now to get stable order slices.SortFunc(result, func(a, b *ServiceRunner) int { return cmp.Compare(a.id, b.id) }) return result } // IsRunning checks service status (started/stopped). // // It doesn't check if service runner was started or not, just pure // check for service status in terms of start/stop. func (s *singleton) IsRunning(id string) (Service, bool, error) { s.mu.Lock() runner, exists := s.state[id] s.mu.Unlock() if !exists { return nil, false, fmt.Errorf("service %q not defined", id) } s.runningMu.Lock() _, running := s.running[id] s.runningMu.Unlock() return runner.service, running, nil } // APIStart processes service start request from the API. func (s *singleton) APIStart(ctx context.Context, id string) error { service, running, err := s.IsRunning(id) if err != nil { return err } if running { // already started, skip return nil } if svc, ok := service.(APIStartableService); ok && svc.APIStartAllowed(s.runtime) { return s.Start(id) } return fmt.Errorf("service %q doesn't support start operation via API", id) } // APIStop processes services stop request from the API. func (s *singleton) APIStop(ctx context.Context, id string) error { service, running, err := s.IsRunning(id) if err != nil { return err } if !running { // already stopped, skip return nil } if svc, ok := service.(APIStoppableService); ok && svc.APIStopAllowed(s.runtime) { return s.Stop(ctx, id) } return fmt.Errorf("service %q doesn't support stop operation via API", id) } // APIRestart processes services restart request from the API. func (s *singleton) APIRestart(ctx context.Context, id string) error { service, running, err := s.IsRunning(id) if err != nil { return err } if !running { // restart for not running service is equivalent to Start() return s.APIStart(ctx, id) } if svc, ok := service.(APIRestartableService); ok && svc.APIRestartAllowed(s.runtime) { if err := s.Stop(ctx, id); err != nil { return err } return s.Start(id) } return fmt.Errorf("service %q doesn't support restart operation via API", id) } ================================================ FILE: internal/app/machined/pkg/system/system_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system_test import ( "context" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/system" ) type SystemServicesSuite struct { suite.Suite } func (suite *SystemServicesSuite) TestStartShutdown() { system.Services(nil).LoadAndStart( &MockService{name: "containerd"}, &MockService{name: "trustd", dependencies: []string{"containerd"}}, &MockService{name: "machined", dependencies: []string{"containerd", "trustd"}}, ) time.Sleep(10 * time.Millisecond) suite.Require().NoError(system.Services(nil).Unload(context.Background(), "trustd", "notrunning")) } func (suite *SystemServicesSuite) TestStartStop() { system.Services(nil).LoadAndStart( &MockService{name: "yolo"}, ) time.Sleep(10 * time.Millisecond) err := system.Services(nil).Stop( context.TODO(), "yolo", ) suite.Assert().NoError(err) } func (suite *SystemServicesSuite) TestStopWithRevDeps() { system.Services(nil).LoadAndStart( &MockService{name: "cri"}, &MockService{name: "networkd", dependencies: []string{"cri"}}, &MockService{name: "vland", dependencies: []string{"networkd"}}, ) time.Sleep(10 * time.Millisecond) // stopping cri should stop all services suite.Require().NoError(system.Services(nil).StopWithRevDepenencies(context.Background(), "cri")) // no services should be running for _, name := range []string{"cri", "networkd", "vland"} { svc, running, err := system.Services(nil).IsRunning(name) suite.Require().NoError(err) suite.Assert().NotNil(svc) suite.Assert().False(running) } system.Services(nil).Shutdown(context.TODO()) } func TestSystemServicesSuite(t *testing.T) { suite.Run(t, new(SystemServicesSuite)) } ================================================ FILE: internal/app/machined/pkg/system/volumes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package system import ( "context" "fmt" "slices" "strings" "sync" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func (svcrunner *ServiceRunner) deleteVolumeMountRequest(ctx context.Context, requests []volumeRequest) error { st := svcrunner.runtime.State().V1Alpha2().Resources() requests = slices.Clone(requests) slices.Reverse(requests) for _, request := range requests { if err := st.RemoveFinalizer(ctx, block.NewVolumeMountStatus(block.NamespaceName, request.requestID).Metadata(), "service"); err != nil { if !state.IsNotFoundError(err) { return fmt.Errorf("failed to remove finalizer from mount status %q: %w", request.requestID, err) } } } for _, request := range requests { err := st.Destroy(ctx, block.NewVolumeMountRequest(block.NamespaceName, request.requestID).Metadata()) if err != nil { return fmt.Errorf("failed to destroy volume mount request %q: %w", request.requestID, err) } } for _, request := range requests { if _, err := st.WatchFor(ctx, block.NewVolumeMountStatus(block.NamespaceName, request.requestID).Metadata(), state.WithEventTypes(state.Destroyed)); err != nil { return fmt.Errorf("failed to watch for volume mount status to be destroyed %q: %w", request.requestID, err) } } return nil } type volumesMountedCondition struct { st state.State requests []volumeRequest mu sync.Mutex pendingRequests []volumeRequest } func (cond *volumesMountedCondition) Wait(ctx context.Context) error { ctx, cancel := context.WithCancel(ctx) defer cancel() // we mount all requests sequentially one by one for idx := range cond.requests { req := cond.requests[idx] // create volume mount request mountRequest := block.NewVolumeMountRequest(block.NamespaceName, req.requestID) mountRequest.TypedSpec().Requester = req.requester mountRequest.TypedSpec().VolumeID = req.volumeID if err := cond.st.Create(ctx, mountRequest); err != nil { if !state.IsConflictError(err) { return fmt.Errorf("failed to create mount request: %w", err) } } // wait for the mount status _, err := cond.st.WatchFor(ctx, block.NewVolumeMountStatus(block.NamespaceName, req.requestID).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithPhases(resource.PhaseRunning), ) if err != nil { return err } if err = cond.st.AddFinalizer(ctx, block.NewVolumeMountStatus(block.NamespaceName, req.requestID).Metadata(), "service"); err != nil { return err } cond.mu.Lock() cond.pendingRequests = slices.Clone(cond.requests[idx+1:]) cond.mu.Unlock() } return nil } func (cond *volumesMountedCondition) String() string { cond.mu.Lock() pendingVolumeIDs := xslices.Map(cond.pendingRequests, func(r volumeRequest) string { return r.volumeID }) cond.mu.Unlock() return fmt.Sprintf("volumes %s to be mounted", strings.Join(pendingVolumeIDs, ", ")) } // WaitForVolumesToBeMounted is a service condition that will wait for the volumes to be mounted. func WaitForVolumesToBeMounted(st state.State, requests []volumeRequest) conditions.Condition { return &volumesMountedCondition{ st: st, requests: requests, pendingRequests: slices.Clone(requests), } } ================================================ FILE: internal/app/machined/revert.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "context" "errors" "fmt" "io/fs" "log" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/meta" metaconsts "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) var revertState state.State func revertSetState(s state.State) { revertState = s } func revertBootloader(ctx context.Context) { if revertState == nil { log.Printf("no state to revert bootloader") return } if err := revertBootloadInternal(ctx, revertState); err != nil { log.Printf("failed to revert bootloader: %s", err) } } //nolint:gocyclo func revertBootloadInternal(ctx context.Context, resourceState state.State) error { systemDisk, err := block.GetSystemDisk(ctx, resourceState) if err != nil { return fmt.Errorf("system disk lookup failed: %w", err) } if systemDisk == nil { log.Printf("no system disk found, nothing to revert") return nil } metaState, err := meta.New(ctx, resourceState) if err != nil { if errors.Is(err, fs.ErrNotExist) { // no META, no way to revert return nil } return err } label, ok := metaState.ReadTag(metaconsts.Upgrade) if !ok { return nil } if label == "" { if _, err = metaState.DeleteTag(ctx, metaconsts.Upgrade); err != nil { return err } return metaState.Flush() } log.Printf("reverting failed upgrade, switching to %q", label) if err := func() error { config, err := bootloader.Probe(systemDisk.DevPath, options.ProbeOptions{}) if err != nil { return err } return config.Revert(systemDisk.DevPath) }(); err != nil { return err } if _, err = metaState.DeleteTag(ctx, metaconsts.Upgrade); err != nil { return err } return metaState.Flush() } ================================================ FILE: internal/app/poweroff/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package poweroff import ( "context" "log" "slices" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) // Action is the action to be performed by the poweroff command. type Action string const ( // Shutdown is the action to shutdown the machine. Shutdown Action = "shutdown" // Reboot is the action to reboot the machine. Reboot Action = "reboot" ) // Main is the entrypoint into /sbin/poweroff. func Main(args []string) { ctx := context.Background() md := metadata.Pairs() authz.SetMetadata(md, role.MakeSet(role.Admin)) adminCtx := metadata.NewOutgoingContext(ctx, md) client, err := client.New( adminCtx, client.WithUnixSocket(constants.MachineSocketPath), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ) if err != nil { log.Fatalf("error while creating machinery client: %s", err) } switch ActionFromArgs(args) { case Shutdown: err = client.Shutdown(adminCtx) if err != nil { log.Fatalf("error while sending shutdown command: %s", err) } case Reboot: err = client.Reboot(adminCtx) if err != nil { log.Fatalf("error while sending reboot command: %s", err) } } } // ActionFromArgs returns the action to be performed based on the arguments. func ActionFromArgs(args []string) Action { if len(args) > 1 { if slices.ContainsFunc(args[1:], func(s string) bool { return s == "--halt" || s == "-H" || s == "--poweroff" || s == "-P" || s == "-p" }) { return Shutdown } if slices.ContainsFunc(args[1:], func(s string) bool { return s == "--reboot" || s == "-r" }) { return Reboot } } return Shutdown } ================================================ FILE: internal/app/poweroff/poweroff_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package poweroff_test import ( "testing" "github.com/siderolabs/talos/internal/app/poweroff" ) func TestParseArgs(t *testing.T) { t.Parallel() tests := []struct { name string args []string action poweroff.Action }{ { name: "shutdown no args", args: []string{"shutdown"}, action: poweroff.Shutdown, }, { name: "shutdown with reboot", args: []string{"shutdown", "-r"}, action: poweroff.Reboot, }, { name: "shutdown with reboot long", args: []string{"shutdown", "--reboot"}, action: poweroff.Reboot, }, { name: "shutdown with poweroff", args: []string{"shutdown", "-P"}, action: poweroff.Shutdown, }, { name: "shutdown with poweroff long", args: []string{"shutdown", "--poweroff"}, action: poweroff.Shutdown, }, { name: "shutdown with poweroff and reboot", args: []string{"shutdown", "-h", "-r"}, action: poweroff.Reboot, }, { name: "shutdown with poweroff, reboot and timer", args: []string{"shutdown", "-h", "-r", "+0"}, action: poweroff.Reboot, }, { name: "shutdown with poweroff and halt", args: []string{"shutdown", "-h", "-H"}, action: poweroff.Shutdown, }, { name: "shutdown with poweroff and halt long", args: []string{"shutdown", "-h", "--halt"}, action: poweroff.Shutdown, }, { name: "poweroff no args", args: []string{"poweroff"}, action: poweroff.Shutdown, }, { name: "poweroff with halt", args: []string{"poweroff", "--halt"}, action: poweroff.Shutdown, }, { name: "poweroff with poweroff", args: []string{"poweroff", "-p"}, action: poweroff.Shutdown, }, { name: "poweroff with poweroff long", args: []string{"poweroff", "--poweroff"}, action: poweroff.Shutdown, }, { name: "poweroff with reboot", args: []string{"poweroff", "--reboot"}, action: poweroff.Reboot, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() action := poweroff.ActionFromArgs(tt.args) if action != tt.action { t.Errorf("expected %q, got %q", tt.action, action) } }) } } ================================================ FILE: internal/app/resources/access.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package resources contains shared implementation of COSI resource API. package resources import ( "context" "fmt" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/role" ) // AccessPolicy defines the access policy for resources accessed via the API. func AccessPolicy(st state.State) state.FilteringRule { return func(ctx context.Context, access state.Access) error { if !access.Verb.Readonly() { return status.Error(codes.PermissionDenied, "write access is not allowed") } rd, err := safe.StateGet[*meta.ResourceDefinition](ctx, st, resource.NewMetadata(meta.NamespaceName, meta.ResourceDefinitionType, strings.ToLower(access.ResourceType), resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { return status.Error(codes.PermissionDenied, fmt.Sprintf("resource type %q is not supported", access.ResourceType)) } return err } roles := authz.GetRoles(ctx) spec := rd.TypedSpec() switch spec.Sensitivity { case meta.Sensitive: if !roles.Includes(role.Admin) { return authz.ErrNotAuthorized } case meta.NonSensitive: // nothing default: return fmt.Errorf("unexpected sensitivity %q", spec.Sensitivity) } _, err = safe.StateGet[*meta.Namespace](ctx, st, resource.NewMetadata(meta.NamespaceName, meta.NamespaceType, access.ResourceNamespace, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { return status.Error(codes.PermissionDenied, fmt.Sprintf("namespace %q is not supported", access.ResourceNamespace)) } return err } return nil } } ================================================ FILE: internal/app/storaged/server.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package internal contains server implementation. package internal import ( "context" "errors" "fmt" "log" "path/filepath" "slices" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "golang.org/x/sys/unix" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/partition" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Server implements storage.StorageService. // // It is only kept here for compatibility purposes, proper API is to query `block.Disk` resources. type Server struct { storage.UnimplementedStorageServiceServer Controller runtime.Controller MaintenanceMode bool } // Disks implements storage.StorageService. func (s *Server) Disks(ctx context.Context, in *emptypb.Empty) (reply *storage.DisksResponse, err error) { st := s.Controller.Runtime().State().V1Alpha2().Resources() systemDisk, err := safe.StateGetByID[*block.SystemDisk](ctx, st, block.SystemDiskID) if err != nil && !state.IsNotFoundError(err) { return nil, err } disks, err := safe.StateListAll[*block.Disk](ctx, st) if err != nil { return nil, err } diskConv := func(d *block.Disk) *storage.Disk { var diskType storage.Disk_DiskType switch { case d.TypedSpec().CDROM: diskType = storage.Disk_CD case d.TypedSpec().Transport == "nvme": diskType = storage.Disk_NVME case d.TypedSpec().Transport == "mmc": diskType = storage.Disk_SD case d.TypedSpec().Rotational: diskType = storage.Disk_HDD case d.TypedSpec().Transport != "": diskType = storage.Disk_SSD } return &storage.Disk{ DeviceName: filepath.Join("/dev", d.Metadata().ID()), Model: d.TypedSpec().Model, Size: d.TypedSpec().Size, Serial: d.TypedSpec().Serial, Modalias: d.TypedSpec().Modalias, Wwid: d.TypedSpec().WWID, Uuid: d.TypedSpec().UUID, Type: diskType, BusPath: d.TypedSpec().BusPath, SystemDisk: systemDisk != nil && d.Metadata().ID() == systemDisk.TypedSpec().DiskID, Subsystem: d.TypedSpec().SubSystem, Readonly: d.TypedSpec().Readonly, } } reply = &storage.DisksResponse{ Messages: []*storage.Disks{ { Disks: safe.ToSlice(disks, diskConv), }, }, } return reply, nil } // BlockDeviceWipe implements storage.StorageService. // // It allows to wipe unused block devices, for blockdevices in use (volumes), use a different method. func (s *Server) BlockDeviceWipe(ctx context.Context, req *storage.BlockDeviceWipeRequest) (*storage.BlockDeviceWipeResponse, error) { // the storage server is included both into machined and maintenance service // in apid/machined mode, the normal authz checks are used before reaching this method // in maintenance mode, we allow this method to be accessible, as it only allows to wipe block devices // // validate the list of devices for _, deviceRequest := range req.GetDevices() { if err := s.validateDeviceForWipe(ctx, deviceRequest.GetDevice(), deviceRequest.GetSkipVolumeCheck(), deviceRequest.GetSkipSecondaryCheck()); err != nil { return nil, err } } // perform the actual wipe for _, deviceRequest := range req.GetDevices() { if err := s.wipeDevice(deviceRequest.GetDevice(), deviceRequest.GetMethod(), deviceRequest.GetDropPartition()); err != nil { return nil, err } } return &storage.BlockDeviceWipeResponse{ Messages: []*storage.BlockDeviceWipe{ {}, }, }, nil } //nolint:gocyclo,cyclop func (s *Server) validateDeviceForWipe(ctx context.Context, deviceName string, skipVolumeCheck, skipSecondaryCheck bool) error { // first, resolve the blockdevice and figure out what type it is st := s.Controller.Runtime().State().V1Alpha2().Resources() blockdevice, err := safe.StateGetByID[*block.Device](ctx, st, deviceName) if err != nil { if state.IsNotFoundError(err) { return status.Errorf(codes.NotFound, "blockdevice %q not found", deviceName) } return err } var parent string deviceType := blockdevice.TypedSpec().Type switch deviceType { case block.DeviceTypeDisk: // supported case block.DeviceTypePartition: // supported parent = blockdevice.TypedSpec().Parent default: return status.Errorf(codes.InvalidArgument, "blockdevice %q is of unsupported type %q", deviceName, deviceType) } // check the disk (or parent) var disk *block.Disk if parent != "" { disk, err = safe.StateGetByID[*block.Disk](ctx, st, parent) } else { disk, err = safe.StateGetByID[*block.Disk](ctx, st, deviceName) } if err != nil { return fmt.Errorf("failed to get disk (or parent) for %q: %w", deviceName, err) } if disk.TypedSpec().Readonly { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is read-only", deviceName) } if disk.TypedSpec().CDROM { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is a CD-ROM", deviceName) } // secondaries check if !skipSecondaryCheck { switch deviceType { case block.DeviceTypeDisk: // for disks, check secondaries even if the partition is used as secondary (track via Disk resource) disks, err := safe.StateListAll[*block.Disk](ctx, st) if err != nil { return err } for disk := range disks.All() { if slices.Index(disk.TypedSpec().SecondaryDisks, deviceName) != -1 { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by disk %q", deviceName, disk.Metadata().ID()) } } case block.DeviceTypePartition: // for partitions, check secondaries only if the partition is used as a secondary blockdevices, err := safe.StateListAll[*block.Device](ctx, st) if err != nil { return err } for blockdevice := range blockdevices.All() { if slices.Index(blockdevice.TypedSpec().Secondaries, deviceName) != -1 { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by blockdevice %q", deviceName, blockdevice.Metadata().ID()) } } } } // volume in use checks if !skipVolumeCheck { volumeStatuses, err := safe.StateListAll[*block.VolumeStatus](ctx, st) if err != nil { return err } for volumeStatus := range volumeStatuses.All() { for _, location := range []string{ filepath.Base(volumeStatus.TypedSpec().Location), filepath.Base(volumeStatus.TypedSpec().MountLocation), } { for _, dev := range []string{deviceName, parent} { if dev == "" || location == "" { continue } if location == dev { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by volume %q", dev, volumeStatus.Metadata().ID()) } } } if filepath.Base(volumeStatus.TypedSpec().ParentLocation) == deviceName { return status.Errorf(codes.FailedPrecondition, "blockdevice %q is in use by volume %q", deviceName, volumeStatus.Metadata().ID()) } } } return nil } func (s *Server) findParentDevice(deviceName string) (string, int, error) { st := s.Controller.Runtime().State().V1Alpha2().Resources() blockdevice, err := safe.StateGetByID[*block.Device](context.Background(), st, deviceName) if err != nil { return "", 0, err } if blockdevice.TypedSpec().Type == block.DeviceTypePartition { return blockdevice.TypedSpec().Parent, blockdevice.TypedSpec().PartitionNumber, nil } return "", 0, nil } // wipeDevice wipes the block device with the given method. // //nolint:gocyclo,cyclop func (s *Server) wipeDevice(deviceName string, method storage.BlockDeviceWipeDescriptor_Method, dropPartition bool) error { parentName, partitionNumber, err := s.findParentDevice(deviceName) if err != nil { return err } var parentBd *blockdev.Device if parentName != "" { parentBd, err = blockdev.NewFromPath(filepath.Join("/dev", parentName), blockdev.OpenForWrite()) if err != nil { return status.Errorf(codes.Internal, "failed to open block device %q: %v", parentName, err) } defer parentBd.Close() //nolint:errcheck } bd, err := blockdev.NewFromPath(filepath.Join("/dev", deviceName), blockdev.OpenForWrite(), blockdev.OpenAssertNotMounted(), ) if err != nil { if errors.Is(err, unix.EBUSY) { return status.Errorf(codes.FailedPrecondition, "block device %q is mounted or in use", deviceName) } return status.Errorf(codes.Internal, "failed to open block device %q: %v", deviceName, err) } defer bd.Close() //nolint:errcheck // lock the parent device always (if available) if parentBd != nil { log.Printf("locking parent block device %q", parentName) if err = parentBd.Lock(true); err != nil { return status.Errorf(codes.Internal, "failed to lock parent block device %q: %v", parentName, err) } defer parentBd.Unlock() //nolint:errcheck } else { log.Printf("locking block device %q", deviceName) if err = bd.Lock(true); err != nil { return status.Errorf(codes.Internal, "failed to lock block device %q: %v", deviceName, err) } defer bd.Unlock() //nolint:errcheck } switch method { case storage.BlockDeviceWipeDescriptor_ZEROES: log.Printf("wiping block device %q with zeroes", deviceName) method, err := bd.Wipe() if err != nil { return status.Errorf(codes.Internal, "failed to wipe block device %q: %v", deviceName, err) } log.Printf("block device %q wiped with method %q", deviceName, method) case storage.BlockDeviceWipeDescriptor_FAST: log.Printf("wiping block device %q with fast method", deviceName) if err = partition.WipeWithSignatures(bd, deviceName, log.Printf); err != nil { return status.Error(codes.Internal, err.Error()) } default: return status.Errorf(codes.InvalidArgument, "unsupported wipe method %s", method) } if dropPartition && parentBd != nil && partitionNumber != 0 { // first, close the blockdevice, otherwise the partition table cannot be modified if err = bd.Close(); err != nil { return status.Errorf(codes.Internal, "failed to close block device %q: %v", deviceName, err) } gptdev, err := gpt.DeviceFromBlockDevice(parentBd) if err != nil { return status.Errorf(codes.Internal, "failed to get GPT device: %v", err) } pt, err := gpt.Read(gptdev) if err != nil { return status.Errorf(codes.Internal, "failed to read GPT table: %v", err) } if err = pt.DeletePartition(partitionNumber - 1); err != nil { return status.Errorf(codes.Internal, "failed to delete partition: %v", err) } if err = pt.Write(); err != nil { return status.Errorf(codes.Internal, "failed to write GPT table: %v", err) } log.Printf("deleted partition %d from block device %q", partitionNumber, parentName) } return nil } ================================================ FILE: internal/app/syslogd/internal/parser/parse.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package parser provides a syslog parser that can parse both RFC3164 and RFC5424 with best effort. package parser import ( "bytes" "encoding/json" "fmt" "slices" "github.com/jeromer/syslogparser" "github.com/jeromer/syslogparser/rfc3164" "github.com/jeromer/syslogparser/rfc5424" ) // Parse parses a syslog message and returns a json encoded representation of the message. // If an RFC3164 message is detected and there is no hostname field in the message, // the hostname field is set to "localhost". func Parse(b []byte) (string, error) { // Detect the RFC version rfc, err := syslogparser.DetectRFC(b) if err != nil { return "", err } var parser syslogparser.LogParser switch rfc { case syslogparser.RFC_3164: input := slices.Clone(b) tagPresent, hostnamePresent := rfc3164ContainsTagHostname(b) if !tagPresent { input = enhanceRFC3164WithTag(b) } parser = rfc3164.NewParser(input) if !hostnamePresent { parser.WithHostname("localhost") } case syslogparser.RFC_5424: parser = rfc5424.NewParser(b) default: return "", fmt.Errorf("unsupported RFC version: %v", rfc) } if err = parser.Parse(); err != nil { return "", err } msg, err := json.Marshal(parser.Dump()) if err != nil { return "", err } return string(msg), nil } func rfc3164ContainsTagHostname(buf []byte) (bool, bool) { indx := bytes.Index(buf, []byte(`]:`)) //nolint:modernize if indx == -1 { return false, false } // handle case when timestamp is of the format `<6>Mar 3 12:55:18` if len(bytes.Split(buf[:indx], []byte(` `))) > 1 { return true, false } return true, bytes.Count(buf[:indx], []byte(` `)) > 3 } func enhanceRFC3164WithTag(buf []byte) []byte { var count int spaces := 3 singleDigitDayIndex := bytes.Index(buf, []byte(` `)) if singleDigitDayIndex != -1 && singleDigitDayIndex < 8 { spaces = 4 } i := bytes.IndexFunc(buf, func(r rune) bool { if r == rune(' ') { count++ } if count == spaces { return true } return false }, ) initial := buf[:i] remaining := buf[i:] var syslogBytes bytes.Buffer syslogBytes.Write(initial) syslogBytes.WriteString(" unknown:") syslogBytes.Write(remaining) return syslogBytes.Bytes() } ================================================ FILE: internal/app/syslogd/internal/parser/parse_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // package parser provides a syslog parser that can parse both RFC3164 and RFC5424 with best effort. package parser_test import ( "fmt" "testing" "time" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/syslogd/internal/parser" ) func TestParser(t *testing.T) { year := time.Now().Year() for _, tc := range []struct { name string input []byte expected string }{ { name: "RFC3164 without tag and hostname", input: []byte(`<4>Feb 16 17:54:19 time="2024-02-16T17:54:19.857755073Z" level=warning msg="Could not add /dev/mshv to the devices cgroup`), expected: fmt.Sprintf(`{"content":"time=\"2024-02-16T17:54:19.857755073Z\" level=warning msg=\"Could not add /dev/mshv to the devices cgroup","facility":0,"hostname":"localhost","priority":4,"severity":4,"tag":"unknown","timestamp":"%d-02-16T17:54:19Z"}`, year), //nolint:lll }, { name: "RFC3164 timestamp contains single digit day", input: []byte(`<6>Mar 3 12:55:18 syslogd_test[834097]: Hello, syslogd!`), expected: fmt.Sprintf(`{"content":"Hello, syslogd!","facility":0,"hostname":"localhost","priority":6,"severity":6,"tag":"syslogd_test","timestamp":"%d-03-03T12:55:18Z"}`, year), }, { name: "RFC3164 timestamp contains single digit day & without tag and hostname", input: []byte(`<6>Mar 3 12:55:18 Hello, syslogd!`), expected: fmt.Sprintf(`{"content":"Hello, syslogd!","facility":0,"hostname":"localhost","priority":6,"severity":6,"tag":"unknown","timestamp":"%d-03-03T12:55:18Z"}`, year), }, { name: "RFC3164 without hostname", input: []byte(`<4>Feb 16 17:54:19 kata[2569]: time="2024-02-16T17:54:19.857755073Z" level=warning msg="Could not add /dev/mshv to the devices cgroup`), expected: fmt.Sprintf(`{"content":"time=\"2024-02-16T17:54:19.857755073Z\" level=warning msg=\"Could not add /dev/mshv to the devices cgroup","facility":0,"hostname":"localhost","priority":4,"severity":4,"tag":"kata","timestamp":"%d-02-16T17:54:19Z"}`, year), //nolint:lll }, { name: "RFC3164 with hostname", input: []byte(`<4>Feb 16 17:54:19 hostname kata[2569]: time="2024-02-16T17:54:19.857755073Z" level=warning msg="Could not add /dev/mshv to the devices cgroup`), expected: fmt.Sprintf(`{"content":"time=\"2024-02-16T17:54:19.857755073Z\" level=warning msg=\"Could not add /dev/mshv to the devices cgroup","facility":0,"hostname":"hostname","priority":4,"severity":4,"tag":"kata","timestamp":"%d-02-16T17:54:19Z"}`, year), //nolint:lll }, { name: "RFC5424", input: []byte(`<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"] An application event log entry...`), expected: `{"app_name":"evntslog","facility":20,"hostname":"mymachine.example.com","message":"An application event log entry...","msg_id":"ID47","priority":165,"proc_id":"-","severity":5,"structured_data":"[exampleSDID@32473 iut=\"3\" eventSource=\"Application\" eventID=\"1011\"]","timestamp":"2003-10-11T22:14:15.003Z","version":1}`, //nolint:lll }, } { t.Run(tc.name, func(t *testing.T) { parsedJSON, err := parser.Parse(tc.input) require.NoError(t, err) require.Equal(t, tc.expected, parsedJSON) }) } } ================================================ FILE: internal/app/syslogd/syslogd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package syslogd provides a syslogd service that listens on a unix socket package syslogd import ( "context" "fmt" "io" "net" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/syslogd/internal/parser" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Main is an entrypoint to the API service. func Main(ctx context.Context, _ runtime.Runtime, logWriter io.Writer) error { return Run(ctx, logWriter, constants.SyslogListenSocketPath) } // Run starts the syslogd service. func Run(ctx context.Context, logWriter io.Writer, listenSocketPath string) error { unixAddr, err := net.ResolveUnixAddr("unixgram", listenSocketPath) if err != nil { return err } connection, err := net.ListenUnixgram("unixgram", unixAddr) if err != nil { return err } if err = connection.SetReadBuffer(65536); err != nil { return fmt.Errorf("failed to set read buffer: %w", err) } buf := make([]byte, 1024) go func(con *net.UnixConn) { for { n, err := con.Read(buf) if err != nil { continue } syslogJSON, err := parser.Parse(buf[:n]) if err != nil { // if the message is not a valid syslog message, skip it continue } fmt.Fprintln(logWriter, syslogJSON) } }(connection) <-ctx.Done() return connection.Close() } ================================================ FILE: internal/app/syslogd/syslogd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package syslogd_test import ( "context" "encoding/json" "log/syslog" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/syslogd" ) type chanWriter struct { ch chan []byte } func (w *chanWriter) Write(p []byte) (n int, err error) { w.ch <- p return len(p), nil } func TestParsing(t *testing.T) { ch := chanWriter{ ch: make(chan []byte), } ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() socketPath := t.TempDir() + "/syslogd.sock" errChan := make(chan error) go func() { errChan <- syslogd.Run(ctx, &ch, socketPath) }() assert.Eventually(t, func() bool { if _, err := os.Stat(socketPath); err == nil { return true } return false }, 1*time.Second, 100*time.Millisecond) // Send a message to the syslogd service syslog, err := syslog.Dial("unixgram", socketPath, syslog.LOG_INFO, "syslogd_test") require.NoError(t, err) _, err = syslog.Write([]byte("Hello, syslogd!")) require.NoError(t, err) defer syslog.Close() //nolint:errcheck select { case msg := <-ch.ch: var parsed map[string]any require.NoError(t, json.Unmarshal(msg, &parsed)) // {"content":"Hello, syslogd!\n","facility":0,"hostname":"localhost","priority":6,"severity":6,"tag":"syslogd_test","timestamp":"2024-02-20T00:20:55Z" assert.Equal(t, "syslogd_test", parsed["tag"]) assert.Equal(t, "localhost", parsed["hostname"]) assert.Equal(t, float64(6), parsed["priority"]) assert.Equal(t, float64(6), parsed["severity"]) assert.Equal(t, float64(0), parsed["facility"]) assert.Equal(t, "Hello, syslogd!\n", parsed["content"]) assert.NotEmpty(t, parsed["timestamp"]) case <-ctx.Done(): require.Fail(t, "timed out waiting for message") } cancel() require.NoError(t, <-errChan) } ================================================ FILE: internal/app/trustd/internal/provider/provider.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package provider provides TLS config for client & server. package provider import ( "bytes" "context" stdlibtls "crypto/tls" stdx509 "crypto/x509" "fmt" "log" "sync" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/tls" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // TLSConfig provides client & server TLS configs for trustd. type TLSConfig struct { certificateProvider *certificateProvider watchCh <-chan state.Event } // NewTLSConfig builds provider from configuration and endpoints. func NewTLSConfig(ctx context.Context, resources state.State) (*TLSConfig, error) { watchCh := make(chan state.Event) if err := resources.Watch(ctx, resource.NewMetadata(secrets.NamespaceName, secrets.TrustdType, secrets.TrustdID, resource.VersionUndefined), watchCh); err != nil { return nil, fmt.Errorf("error setting up watch: %w", err) } // wait for the first event to set up certificate provider provider := &certificateProvider{} for { var event state.Event select { case <-ctx.Done(): return nil, ctx.Err() case event = <-watchCh: } switch event.Type { case state.Created, state.Updated: // expected case state.Destroyed, state.Bootstrapped, state.Noop: // ignore, we'll get another event continue case state.Errored: return nil, fmt.Errorf("error watching for trustd certificates: %w", event.Error) } trustdCerts := event.Resource.(*secrets.Trustd) //nolint:forcetypeassert if err := provider.Update(trustdCerts); err != nil { return nil, err } return &TLSConfig{ certificateProvider: provider, watchCh: watchCh, }, nil } } // Watch for updates to trustd certificates. func (tlsConfig *TLSConfig) Watch(ctx context.Context) error { for { var event state.Event select { case <-ctx.Done(): return nil case event = <-tlsConfig.watchCh: } switch event.Type { case state.Created, state.Updated: // expected case state.Destroyed, state.Bootstrapped, state.Noop: // ignore, we'll get another event continue case state.Errored: log.Printf("error watching for trustd certificates: %s", event.Error) } trustdCerts := event.Resource.(*secrets.Trustd) //nolint:forcetypeassert if err := tlsConfig.certificateProvider.Update(trustdCerts); err != nil { return fmt.Errorf("failed updating cert: %w", err) } } } // ServerConfig generates server-side tls.Config. func (tlsConfig *TLSConfig) ServerConfig() (*stdlibtls.Config, error) { ca, err := tlsConfig.certificateProvider.GetCA() if err != nil { return nil, fmt.Errorf("failed to get root CA: %w", err) } return tls.New( tls.WithClientAuthType(tls.ServerOnly), tls.WithCACertPEM(ca), tls.WithServerCertificateProvider(tlsConfig.certificateProvider), ) } type certificateProvider struct { mu sync.Mutex ca []byte caCertPool *stdx509.CertPool serverCert *stdlibtls.Certificate } func (p *certificateProvider) Update(trustdCerts *secrets.Trustd) error { p.mu.Lock() defer p.mu.Unlock() p.ca = bytes.Join( xslices.Map( trustdCerts.TypedSpec().AcceptedCAs, func(cert *x509.PEMEncodedCertificate) []byte { return cert.Crt }, ), nil, ) p.caCertPool = stdx509.NewCertPool() if !p.caCertPool.AppendCertsFromPEM(p.ca) { return fmt.Errorf("failed to parse root CA") } serverCert, err := stdlibtls.X509KeyPair(trustdCerts.TypedSpec().Server.Crt, trustdCerts.TypedSpec().Server.Key) if err != nil { return fmt.Errorf("failed to parse server cert and key into a TLS Certificate: %w", err) } p.serverCert = &serverCert return nil } func (p *certificateProvider) GetCA() ([]byte, error) { p.mu.Lock() defer p.mu.Unlock() return p.ca, nil } func (p *certificateProvider) GetCACertPool() (*stdx509.CertPool, error) { p.mu.Lock() defer p.mu.Unlock() return p.caCertPool, nil } func (p *certificateProvider) GetCertificate(h *stdlibtls.ClientHelloInfo) (*stdlibtls.Certificate, error) { p.mu.Lock() defer p.mu.Unlock() return p.serverCert, nil } func (p *certificateProvider) GetClientCertificate(*stdlibtls.CertificateRequestInfo) (*stdlibtls.Certificate, error) { return nil, nil } ================================================ FILE: internal/app/trustd/internal/reg/reg.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package reg import ( "bytes" "context" stdx509 "crypto/x509" "crypto/x509/pkix" "encoding/pem" "log" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/peer" "google.golang.org/grpc/status" securityapi "github.com/siderolabs/talos/pkg/machinery/api/security" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) // Registrator is the concrete type that implements the factory.Registrator and // securityapi.SecurityServiceServer interfaces. type Registrator struct { securityapi.UnimplementedSecurityServiceServer Resources state.State } // Register implements the factory.Registrator interface. // //nolint:interfacer func (r *Registrator) Register(s *grpc.Server) { securityapi.RegisterSecurityServiceServer(s, r) } // Certificate implements the securityapi.SecurityServer interface. // // This API is called by Talos worker nodes to request a server certificate for apid running on the node. // Control plane nodes generate certificates (client and server) directly from machine config PKI. func (r *Registrator) Certificate(ctx context.Context, in *securityapi.CertificateRequest) (resp *securityapi.CertificateResponse, err error) { remotePeer, ok := peer.FromContext(ctx) if !ok { return nil, status.Error(codes.PermissionDenied, "peer not found") } osRoot, err := safe.StateGet[*secrets.OSRoot](ctx, r.Resources, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined)) if err != nil { return nil, err } // decode and validate CSR csrPemBlock, _ := pem.Decode(in.Csr) if csrPemBlock == nil { return nil, status.Errorf(codes.InvalidArgument, "failed to decode CSR") } request, err := stdx509.ParseCertificateRequest(csrPemBlock.Bytes) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "failed to parse CSR: %s", err) } log.Printf("received CSR signing request from %s: subject %s dns names %s addresses %s", remotePeer.Addr, request.Subject, request.DNSNames, request.IPAddresses) // allow only server auth certificates x509Opts := []x509.Option{ x509.KeyUsage(stdx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdx509.ExtKeyUsage{stdx509.ExtKeyUsageServerAuth}), } // don't allow any certificates which can be used for client authentication // // we don't return an error here, as otherwise workers running old versions of Talos // will fail to provision client certificate and will never launch apid // // instead, the returned certificate will be rejected when being used if len(request.Subject.Organization) > 0 { log.Printf("removing client auth organization from CSR: %s", request.Subject.Organization) x509Opts = append(x509Opts, x509.OverrideSubject(func(subject *pkix.Name) { subject.Organization = nil })) } // TODO: Verify that the request is coming from the IP address declared in // the CSR. signed, err := x509.NewCertificateFromCSRBytes( osRoot.TypedSpec().IssuingCA.Crt, osRoot.TypedSpec().IssuingCA.Key, in.Csr, x509Opts..., ) if err != nil { return nil, status.Errorf(codes.Internal, "failed to sign CSR: %s", err) } resp = &securityapi.CertificateResponse{ Ca: bytes.Join( xslices.Map( osRoot.TypedSpec().AcceptedCAs, func(cert *x509.PEMEncodedCertificate) []byte { return cert.Crt }, ), nil, ), Crt: signed.X509CertificatePEM, } return resp, nil } ================================================ FILE: internal/app/trustd/internal/reg/reg_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package reg_test import ( "context" stdx509 "crypto/x509" "net" "net/netip" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/internal/app/trustd/internal/reg" "github.com/siderolabs/talos/pkg/machinery/api/security" gensecrets "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/role" ) func TestCertificate(t *testing.T) { ctx, cancel := context.WithCancel(t.Context()) defer cancel() resources := state.WrapCore(namespaced.NewState(inmem.Build)) ca, err := gensecrets.NewTalosCA(time.Now()) require.NoError(t, err) osRoot := secrets.NewOSRoot(secrets.OSRootID) osRoot.TypedSpec().IssuingCA = &x509.PEMEncodedCertificateAndKey{ Crt: ca.CrtPEM, Key: ca.KeyPEM, } osRoot.TypedSpec().AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: ca.CrtPEM, }, } require.NoError(t, resources.Create(ctx, osRoot)) ctx = peer.NewContext(ctx, &peer.Peer{ Addr: &net.TCPAddr{ IP: netip.MustParseAddr("127.0.0.1").AsSlice(), Port: 30000, }, }) r := ®.Registrator{ Resources: resources, } for _, tt := range []struct { name string csrSetters []x509.Option }{ { name: "server certificate", csrSetters: []x509.Option{ x509.IPAddresses([]net.IP{netip.MustParseAddr("10.5.0.4").AsSlice()}), x509.DNSNames([]string{"talos-default-worker-1"}), x509.CommonName("talos-default-worker-1"), }, }, { name: "attempt at client certificate", csrSetters: []x509.Option{ x509.CommonName("talos-default-worker-1"), x509.Organization(string(role.Impersonator)), }, }, } { t.Run(tt.name, func(t *testing.T) { var ( serverCSR *x509.CertificateSigningRequest serverCert *x509.PEMEncodedCertificateAndKey ) serverCSR, serverCert, err = x509.NewEd25519CSRAndIdentity(tt.csrSetters...) require.NoError(t, err) resp, err := r.Certificate(ctx, &security.CertificateRequest{ Csr: serverCSR.X509CertificateRequestPEM, }) require.NoError(t, err) assert.Equal(t, resp.Ca, ca.CrtPEM) serverCert.Crt = resp.Crt cert, err := serverCert.GetCert() require.NoError(t, err) assert.Equal(t, stdx509.KeyUsageDigitalSignature, cert.KeyUsage) assert.Equal(t, []stdx509.ExtKeyUsage{stdx509.ExtKeyUsageServerAuth}, cert.ExtKeyUsage) assert.Equal(t, "talos-default-worker-1", cert.Subject.CommonName) assert.Equal(t, []string(nil), cert.Subject.Organization) }) } } ================================================ FILE: internal/app/trustd/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package trustd implements trustd functionality. package trustd import ( "context" "flag" "fmt" "log" "os/signal" "syscall" "time" "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/client" debug "github.com/siderolabs/go-debug" "golang.org/x/sync/errgroup" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "github.com/siderolabs/talos/internal/app/trustd/internal/provider" "github.com/siderolabs/talos/internal/app/trustd/internal/reg" "github.com/siderolabs/talos/pkg/grpc/factory" "github.com/siderolabs/talos/pkg/grpc/middleware/auth/basic" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/startup" ) func runDebugServer(ctx context.Context) { const debugAddr = ":9983" debugLogFunc := func(msg string) { log.Print(msg) } if err := debug.ListenAndServe(ctx, debugAddr, debugLogFunc); err != nil { log.Fatalf("failed to start debug server: %s", err) } } // Main is the entrypoint into trustd. func Main() { if err := trustdMain(); err != nil { log.Fatal(err) } } func trustdMain() error { ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT) defer cancel() log.SetFlags(log.Lshortfile | log.Ldate | log.Lmicroseconds | log.Ltime) flag.Parse() go runDebugServer(ctx) startup.LimitMaxProcs(constants.TrustdMaxProcs) var err error runtimeConn, err := grpc.NewClient( "unix://"+constants.TrustdRuntimeSocketPath, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithSharedWriteBuffer(true), grpc.WithNoProxy(), ) if err != nil { return fmt.Errorf("failed to dial runtime connection: %w", err) } stateClient := v1alpha1.NewStateClient(runtimeConn) resources := state.WrapCore(client.NewAdapter(stateClient)) tlsConfig, err := provider.NewTLSConfig(ctx, resources) if err != nil { return fmt.Errorf("failed to create remote certificate provider: %w", err) } serverTLSConfig, err := tlsConfig.ServerConfig() if err != nil { return fmt.Errorf("failed to create OS-level TLS configuration: %w", err) } creds := basic.NewTokenCredentialsDynamic(tokenGetter(resources)) networkListener, err := factory.NewListener( ctx, factory.Port(constants.TrustdPort), ) if err != nil { return fmt.Errorf("error creating listener: %w", err) } networkServer := factory.NewServer( ®.Registrator{Resources: resources}, factory.WithDefaultLog(), factory.WithUnaryInterceptor(creds.UnaryInterceptor()), factory.ServerOptions( grpc.Creds( credentials.NewTLS(serverTLSConfig), ), ), ) errGroup, ctx := errgroup.WithContext(ctx) errGroup.Go(func() error { return networkServer.Serve(networkListener) }) errGroup.Go(func() error { return tlsConfig.Watch(ctx) }) errGroup.Go(func() error { <-ctx.Done() shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() factory.ServerGracefulStop(networkServer, shutdownCtx) return nil }) return errGroup.Wait() } func tokenGetter(state state.State) basic.TokenGetterFunc { return func(ctx context.Context) (string, error) { osRoot, err := safe.StateGet[*secrets.OSRoot](ctx, state, resource.NewMetadata(secrets.NamespaceName, secrets.OSRootType, secrets.OSRootID, resource.VersionUndefined)) if err != nil { return "", err } return osRoot.TypedSpec().Token, nil } } ================================================ FILE: internal/integration/api/api.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package api provides API integration tests for Talos package api import "github.com/stretchr/testify/suite" var allSuites []suite.TestingSuite // GetAllSuites returns all the suites for API test. // // Depending on build tags, this might return different lists. func GetAllSuites() []suite.TestingSuite { return allSuites } ================================================ FILE: internal/integration/api/apid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bytes" "context" "encoding/base64" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/dustin/go-humanize" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" cfg "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/role" ) // ApidSuite verifies Discovery API. type ApidSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ApidSuite) SuiteName() string { return "api.ApidSuite" } // SetupTest ... func (suite *ApidSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), time.Minute) } // TearDownTest ... func (suite *ApidSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestControlPlaneRouting verify access to all nodes via each control plane node as an endpoints. func (suite *ApidSuite) TestControlPlaneRouting() { if suite.Cluster == nil { suite.T().Skip("information about routable endpoints is not available") } if suite.APISuite.Endpoint != "" { suite.T().Skip("test skipped as custom endpoint is set") } endpoints := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, endpoint := range endpoints { suite.Run(endpoint, func() { cli, err := client.New(suite.ctx, client.WithConfig(suite.Talosconfig), client.WithEndpoints(endpoint), ) suite.Require().NoError(err) defer cli.Close() //nolint:errcheck // try with multiple nodes resp, err := cli.Version(client.WithNodes(suite.ctx, nodes...)) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, len(nodes)) // try with 'nodes' but a single node at a time for _, node := range nodes { resp, err = cli.Version(client.WithNodes(suite.ctx, node)) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) } // try with 'node' for _, node := range nodes { resp, err = cli.Version(client.WithNode(suite.ctx, node)) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) } // try without any nodes set resp, err = cli.Version(suite.ctx) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) }) } } // TestWorkerNoRouting verifies that worker nodes perform no routing. func (suite *ApidSuite) TestWorkerNoRouting() { if suite.Cluster == nil { suite.T().Skip("information about routable endpoints is not available") } if suite.APISuite.Endpoint != "" { suite.T().Skip("test skipped as custom endpoint is set") } endpoints := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker) nodes := suite.DiscoverNodeInternalIPs(suite.ctx) if len(endpoints) == 0 { suite.T().Skip("no worker nodes found") } _, err := safe.StateGetByID[*network.NfTablesChain](client.WithNode(suite.ctx, endpoints[0]), suite.Client.COSI, "ingress") if err == nil { suite.T().Skip("worker nodes have ingress firewall enabled, skipping") } for _, endpoint := range endpoints { suite.Run(endpoint, func() { cli, err := client.New(suite.ctx, client.WithConfig(suite.Talosconfig), client.WithEndpoints(endpoint), ) suite.Require().NoError(err) defer cli.Close() //nolint:errcheck // try every other node but the one we're connected to // there should be no routing for _, node := range nodes { if node == endpoint { continue } // 'nodes' _, err = cli.Version(client.WithNodes(suite.ctx, node)) suite.Require().Error(err) suite.Assert().Equal(codes.PermissionDenied, client.StatusCode(err)) // 'node' _, err = cli.Version(client.WithNode(suite.ctx, node)) suite.Require().Error(err) suite.Assert().Equal(codes.PermissionDenied, client.StatusCode(err)) } // try with 'nodes' but a single node (node itself) resp, err := cli.Version(client.WithNodes(suite.ctx, endpoint)) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) // try with 'node' (node itself) resp, err = cli.Version(client.WithNode(suite.ctx, endpoint)) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) // try without any nodes set resp, err = cli.Version(suite.ctx) suite.Require().NoError(err) suite.Assert().Len(resp.Messages, 1) }) } } // TestBigPayload verifies that big payloads are handled correctly. func (suite *ApidSuite) TestBigPayload() { if testing.Short() { suite.T().Skip("skipping test in short mode") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing big payload on node %s", node) // we are going to simulate a big payload by making machine configuration big enough cfg, err := safe.StateGetByID[*config.MachineConfig](nodeCtx, suite.Client.COSI, config.ActiveID) suite.Require().NoError(err) originalCfg, err := cfg.Container().Bytes() suite.Require().NoError(err) // the config is encoded twice in the resource gRPC message, so ensure that we can get to the one third of the size const targetConfigSize = constants.GRPCMaxMessageSize / 3 suite.T().Logf("original config size: %d (%s), target size is %d (%s)", len(originalCfg), humanize.Bytes(uint64(len(originalCfg))), targetConfigSize, humanize.Bytes(uint64(targetConfigSize)), ) bytesToAdd := targetConfigSize - len(originalCfg) if bytesToAdd <= 0 { suite.T().Skip("configuration is already big enough") } const commentLine = "# this is a comment line added to make the config bigger and bigger and bigger and bigger all the way\n" newConfig := slices.Concat(originalCfg, bytes.Repeat([]byte(commentLine), bytesToAdd/len(commentLine)+1)) suite.Assert().Greater(len(newConfig), targetConfigSize) _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: newConfig, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) suite.Require().NoError(err) // now get the machine configuration back several times for range 5 { cfg, err = safe.StateGetByID[*config.MachineConfig](nodeCtx, suite.Client.COSI, config.ActiveID) suite.Require().NoError(err) // check that the configuration is the same newCfg, err := cfg.Container().Bytes() suite.Require().NoError(err) suite.Assert().Equal(newConfig, newCfg) } // revert the configuration _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: originalCfg, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) suite.Require().NoError(err) } // TestPKIMismatch verifies that PKI mismatch is handled correctly. func (suite *ApidSuite) TestPKIMismatch() { bundle, err := secrets.NewBundle(secrets.NewClock(), cfg.TalosVersionCurrent) suite.Require().NoError(err) cert, err := bundle.GenerateTalosAPIClientCertificate(role.MakeSet(role.Admin)) suite.Require().NoError(err) suite.Require().Contains(suite.Talosconfig.Contexts, suite.Talosconfig.Context) caCrt, err := base64.StdEncoding.DecodeString(suite.Talosconfig.Contexts[suite.Talosconfig.Context].CA) suite.Require().NoError(err) wrongConfig := clientconfig.NewConfig("wrong", suite.Talosconfig.Contexts[suite.Talosconfig.Context].Endpoints, caCrt, cert) wrongClient, err := client.New(suite.ctx, client.WithConfig(wrongConfig)) suite.Require().NoError(err) _, err = wrongClient.Version(suite.ctx) suite.Require().Error(err) suite.Assert().Equal(codes.Unavailable, client.StatusCode(err)) suite.Assert().ErrorContains(err, "remote error: tls: unknown certificate authority") suite.Require().NoError(wrongClient.Close()) } func init() { allSuites = append(allSuites, new(ApidSuite)) } ================================================ FILE: internal/integration/api/apply-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "encoding/json" "fmt" "net/url" "os" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/ensure" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/protobuf/types/known/durationpb" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" mc "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // Sysctl to use for testing config changes. // This sysctl should: // // - default to a non-Go-null value in the kernel // // - not be defined by the default Talos configuration // // - be generally harmless const applyConfigTestSysctl = "net.ipv6.conf.all.accept_ra_mtu" const applyConfigTestSysctlVal = "1" const applyConfigNoRebootTestSysctl = "fs.file-max" const applyConfigNoRebootTestSysctlVal = "500000" const assertRebootedRebootTimeout = 10 * time.Minute // ApplyConfigSuite ... type ApplyConfigSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ApplyConfigSuite) SuiteName() string { return "api.ApplyConfigSuite" } // SetupTest ... func (suite *ApplyConfigSuite) SetupTest() { // make sure we abort at some point in time, but give enough room for Recovers suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Minute) } // TearDownTest ... func (suite *ApplyConfigSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestApply verifies the apply config API. func (suite *ApplyConfigSuite) TestApply() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot") } suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q", node) cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { if cfg.MachineConfig.MachineSysctls == nil { cfg.MachineConfig.MachineSysctls = make(map[string]string) } cfg.MachineConfig.MachineSysctls[applyConfigTestSysctl] = applyConfigTestSysctlVal }) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_REBOOT, }, ) if err != nil { return fmt.Errorf("failed to apply configuration (node %q): %w", node, err) } return nil }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) // Verify configuration change var newProvider config.Provider suite.Require().NoErrorf( retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry( func() error { newProvider, err = suite.ReadConfigFromNode(nodeCtx) if err != nil { return retry.ExpectedError(err) } return nil }, ), "failed to read updated configuration from node %q", node, ) suite.Assert().Equal( newProvider.Machine().Sysctls()[applyConfigTestSysctl], applyConfigTestSysctlVal, "expected sysctl %s to be set to %s, got %s on node %q", applyConfigTestSysctl, applyConfigTestSysctlVal, newProvider.Machine().Sysctls()[applyConfigTestSysctl], node, ) } // TestApplyNoOpCRIPatch verifies the apply config with no-op CRI patch. func (suite *ApplyConfigSuite) TestApplyNoOpCRIPatch() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot") } suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q", node) // this CRI patch is a no-op, as NRI is already disabled by default, this verifies that CRI config generation handles it correctly. cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { cfg.MachineConfig.MachineFiles = xslices.Filter(cfg.MachineConfig.MachineFiles, func(file *v1alpha1.MachineFile) bool { return file.FilePath != "/etc/cri/conf.d/20-customization.part" }) cfg.MachineConfig.MachineFiles = append(cfg.MachineConfig.MachineFiles, &v1alpha1.MachineFile{ FilePath: "/etc/cri/conf.d/20-customization.part", FileOp: "create", FileContent: `[plugins] [plugins."io.containerd.nri.v1.nri"] disable = true`, }, ) }) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_REBOOT, }, ) suite.Assert().NoErrorf(err, "failed to apply configuration (node %q)", node) return nil }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) suite.ClearConnectionRefused(suite.ctx, node) // revert the patch provider, err = suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q", node) // this CRI patch is a no-op, as NRI is already disabled by default, this verifies that CRI config generation handles it correctly. cfgDataOut = suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { cfg.MachineConfig.MachineFiles = xslices.Filter(cfg.MachineConfig.MachineFiles, func(file *v1alpha1.MachineFile) bool { return file.FilePath != "/etc/cri/conf.d/20-customization.part" }) }) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_REBOOT, }, ) suite.Assert().NoErrorf(err, "failed to apply configuration (node %q)", node) return nil }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) } // TestApplyWithoutReboot verifies the apply config API without reboot. func (suite *ApplyConfigSuite) TestApplyWithoutReboot() { for _, mode := range []machineapi.ApplyConfigurationRequest_Mode{ machineapi.ApplyConfigurationRequest_AUTO, machineapi.ApplyConfigurationRequest_STAGED, } { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err, "failed to read existing config from node %q", node) cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { if cfg.MachineConfig.MachineSysctls == nil { cfg.MachineConfig.MachineSysctls = make(map[string]string) } cfg.MachineConfig.MachineSysctls[applyConfigNoRebootTestSysctl] = applyConfigNoRebootTestSysctlVal }) _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: mode, }, ) suite.Require().NoError(err, "failed to apply deferred configuration (node %q)", node) // Verify configuration change var newProvider config.Provider newProvider, err = suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err, "failed to read updated configuration from node %q", node) if mode == machineapi.ApplyConfigurationRequest_AUTO { suite.Assert().Equal( newProvider.Machine().Sysctls()[applyConfigNoRebootTestSysctl], applyConfigNoRebootTestSysctlVal, ) } else { suite.Assert().NotContains(newProvider.Machine().Sysctls(), applyConfigNoRebootTestSysctl) } cfgDataOut = suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { // revert back delete(cfg.MachineConfig.MachineSysctls, applyConfigNoRebootTestSysctl) }) _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: mode, }, ) suite.Require().NoError(err, "failed to apply deferred configuration (node %q)", node) } } // TestApplyConfigRotateEncryptionSecrets verify key rotation by sequential apply config calls. func (suite *ApplyConfigSuite) TestApplyConfigRotateEncryptionSecrets() { if testing.Short() { suite.T().Skip("skipping in short mode") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) ephemeralCfg, _ := provider.Volumes().ByName(constants.EphemeralPartitionLabel) encryption := ephemeralCfg.Encryption() if encryption == nil { suite.T().Skip("skipped in not encrypted mode") } suite.WaitForBootDone(suite.ctx) suite.T().Logf("testing encryption key rotation on node %s", node) cfg, ok := encryption.(blockcfg.EncryptionSpec) suite.Require().True(ok, "expected blockcfg.EncryptionSpec, got %T", encryption) existing := cfg.EncryptionKeys[0] slot := existing.Slot() + 1 keySets := [][]blockcfg.EncryptionKey{ { existing, { KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "AlO93jayutOpsDxDS=-", }, KeySlot: slot, }, }, { { KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "AlO93jayutOpsDxDS=-", }, KeySlot: slot, }, }, { existing, { KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "AlO93jayutOpsDxDS=-", }, KeySlot: slot, }, }, { existing, { KeyNodeID: &blockcfg.EncryptionKeyNodeID{}, KeySlot: slot, }, }, { { KeyNodeID: &blockcfg.EncryptionKeyNodeID{}, KeySlot: slot, }, }, } for _, keys := range keySets { suite.T().Logf("applying encryption keys %s on node %s", toJSONString(suite.T(), keys), node) // prepare a patch to apply, first removing existing keys removeKeysPatch := map[string]any{ "apiVersion": "v1alpha1", "kind": "VolumeConfig", "name": constants.EphemeralPartitionLabel, "encryption": map[string]any{ "keys": map[string]any{ "$patch": "delete", }, }, } newEphemeralCfg := blockcfg.NewVolumeConfigV1Alpha1() newEphemeralCfg.MetaName = constants.EphemeralPartitionLabel newEphemeralCfg.EncryptionSpec.EncryptionKeys = keys // right now, patching encryption keys doesn't reboot and doesn't rotate the secrets either suite.PatchMachineConfig(nodeCtx, removeKeysPatch, newEphemeralCfg) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) suite.ClearConnectionRefused(suite.ctx, node) // Verify configuration change var newProvider config.Provider suite.Require().NoError( retry.Constant(time.Minute, retry.WithUnits(time.Second)).Retry( func() error { newProvider, err = suite.ReadConfigFromNode(nodeCtx) if err != nil { return retry.ExpectedError(err) } return nil }, ), "failed to read updated configuration from node %q", node, ) newEphemeral, _ := newProvider.Volumes().ByName(constants.EphemeralPartitionLabel) e := newEphemeral.Encryption() for i, k := range e.Keys() { if keys[i].KeyStatic == nil { suite.Require().Nil(k.Static()) } else { suite.Require().Equal(keys[i].Static().Key(), k.Static().Key()) } if keys[i].KeyNodeID == nil { suite.Require().Nil(k.NodeID()) } else { suite.Require().NotNil(keys[i].NodeID()) } suite.Require().Equal(keys[i].Slot(), k.Slot()) suite.Require().Equal(keys[i].Slot(), k.Slot()) } suite.WaitForBootDone(suite.ctx) // verify that encryption key sync has no failures rtestutils.AssertAll(nodeCtx, suite.T(), suite.Client.COSI, func(vs *block.VolumeStatus, asrt *assert.Assertions) { suite.Assert().Contains([]block.VolumePhase{block.VolumePhaseReady, block.VolumePhaseMissing}, vs.TypedSpec().Phase) suite.Assert().Empty(vs.TypedSpec().EncryptionFailedSyncs) }) } } func toJSONString(t *testing.T, v any) string { t.Helper() out, err := json.Marshal(v) require.NoError(t, err) return string(out) } // TestApplyNoReboot verifies the apply config API fails if NoReboot mode is requested on a field that can not be applied immediately. func (suite *ApplyConfigSuite) TestApplyNoReboot() { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q: %s", node, err) cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { // this won't be possible without a reboot cfg.MachineConfig.MachineType = "controlplane" }) _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }, ) suite.Require().Error(err) suite.Require().Equal(codes.InvalidArgument, client.StatusCode(err)) } // TestApplyDryRun verifies the apply config API with dry run enabled. func (suite *ApplyConfigSuite) TestApplyDryRun() { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q: %s", node, err) cfgDataOut := suite.PatchV1Alpha1Config(provider, func(cfg *v1alpha1.Config) { // this won't be possible without a reboot cfg.MachineConfig.MachineFiles = append(cfg.MachineConfig.MachineFiles, &v1alpha1.MachineFile{ FileContent: "test", FilePermissions: v1alpha1.FileMode(os.ModePerm), FilePath: "/var/lib/test", FileOp: "create", }, ) }) reply, err := suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_AUTO, DryRun: true, }, ) suite.Require().NoErrorf(err, "failed to apply configuration (node %q): %s", node, err) suite.Assert().Contains(reply.Messages[0].ModeDetails, "Dry run summary") } // TestApplyDryRunDocuments verifies the apply config API with multi doc and dry run enabled. func (suite *ApplyConfigSuite) TestApplyDryRunDocuments() { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q: %s", node, err) kmsg := runtime.NewKmsgLogV1Alpha1() kmsg.MetaName = "omni-kmsg" kmsg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://[fdae:41e4:649b:9303::1]:8092")) cont, err := container.New(provider.RawV1Alpha1(), kmsg) suite.Require().NoErrorf(err, "failed to create container: %s", err) cfgDataOut, err := cont.Bytes() suite.Require().NoErrorf(err, "failed to marshal container: %s", err) reply, err := suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_AUTO, DryRun: true, }, ) suite.Require().NoErrorf(err, "failed to apply configuration (node %q): %s", node, err) suite.Assert().Contains(reply.Messages[0].ModeDetails, "Dry run summary") suite.Assert().Contains(reply.Messages[0].ModeDetails, "omni-kmsg") suite.Assert().Contains(reply.Messages[0].ModeDetails, "tcp://[fdae:41e4:649b:9303::1]:8092") } // TestApplyTry applies the config in try mode with a short timeout. func (suite *ApplyConfigSuite) TestApplyTry() { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) dummyCfg := network.NewDummyLinkConfigV1Alpha1("dummy-try") suite.PatchMachineConfigWithModeSetter(nodeCtx, func(acr *machineapi.ApplyConfigurationRequest) { acr.Mode = machineapi.ApplyConfigurationRequest_TRY acr.TryModeTimeout = durationpb.New(time.Second * 1) }, dummyCfg) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoErrorf(err, "failed to read existing config from node %q", node) assertDummyInterface := func(provider config.Provider) bool { docs := provider.Documents() for _, doc := range docs { if doc.Kind() == network.DummyLinkKind { if namedDocument, ok := doc.(configconfig.NamedDocument); ok && namedDocument.Name() == "dummy-try" { return true } } } return false } suite.Assert().Truef(assertDummyInterface(provider), "dummy interface wasn't found") rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, mc.ActiveID, func(r *mc.MachineConfig, asrt *assert.Assertions) { asrt.False(assertDummyInterface(r.Provider())) }) } // TestApplyRemovingV1Alpha1 verifies the apply config doesn't accept removal of v1alpha1 config. func (suite *ApplyConfigSuite) TestApplyRemovingV1Alpha1() { suite.WaitForBootDone(suite.ctx) node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("applying configuration to node %q", node) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) // create a simple multi-doc config without v1alpha1 cfgDocument := runtime.NewWatchdogTimerV1Alpha1() cfgDocument.WatchdogDevice = "/dev/watchdog0" cfgDocument.WatchdogTimeout = 120 * time.Second ctr, err := container.New(cfgDocument) suite.Require().NoError(err, "failed to create container") cfgDataOut, err := ctr.Bytes() suite.Require().NoError(err, "failed to marshal container") // Talos should deny a request that effectively removes the v1alpha1 config _, err = suite.Client.ApplyConfiguration( nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: cfgDataOut, Mode: machineapi.ApplyConfigurationRequest_AUTO, }, ) suite.Require().Error(err) suite.Require().Equal(codes.InvalidArgument, client.StatusCode(err)) suite.Require().ErrorContains(err, "the applied machine configuration doesn't contain v1alpha1 config") } func init() { allSuites = append(allSuites, new(ApplyConfigSuite)) } ================================================ FILE: internal/integration/api/cgroups.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "io" "path/filepath" "time" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // CGroupsSuite ... type CGroupsSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *CGroupsSuite) SuiteName() string { return "api.CGroupsSuite" } // SetupTest ... func (suite *CGroupsSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *CGroupsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestCGroupsVersion tests that cgroups mount match expected version. func (suite *CGroupsSuite) TestCGroupsVersion() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) stream, err := suite.Client.MachineClient.List(ctx, &machineapi.ListRequest{Root: constants.CgroupMountPath}) suite.Require().NoError(err) names := map[string]struct{}{} for { var info *machineapi.FileInfo info, err = stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { break } suite.Require().NoError(err) } names[filepath.Base(info.Name)] = struct{}{} } suite.T().Log("detected cgroups v2") for _, subpath := range []string{ "cgroup.controllers", "cgroup.max.depth", "cgroup.max.descendants", "cgroup.procs", "cgroup.stat", "cgroup.subtree_control", "cgroup.threads", "cpu.stat", "cpuset.cpus.effective", "cpuset.mems.effective", "init", "io.stat", "kubepods", "memory.numa_stat", "memory.stat", "podruntime", "system", } { suite.Assert().Contains(names, subpath) } } func init() { allSuites = append(allSuites, new(CGroupsSuite)) } ================================================ FILE: internal/integration/api/common.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "strings" "testing" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // CommonSuite verifies some default settings such as ulimits. type CommonSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *CommonSuite) SuiteName() string { return "api.CommonSuite" } // SetupTest ... func (suite *CommonSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Minute) } // TearDownTest ... func (suite *CommonSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestVirtioModulesLoaded verifies that the virtio modules are loaded. func (suite *CommonSuite) TestVirtioModulesLoaded() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping virtio test since provisioner is not qemu") } expectedVirtIOModules := map[string]string{ "virtio_balloon": "virtio_balloon.ko", "virtio_pci": "virtio_pci.ko", "virtio_pci_legacy_dev": "virtio_pci_legacy_dev.ko", "virtio_pci_modern_dev": "virtio_pci_modern_dev.ko", } node := suite.RandomDiscoveredNodeInternalIP() suite.AssertExpectedModules(suite.ctx, node, expectedVirtIOModules) } // TestCommonDefaults verifies that the default ulimits are set. func (suite *CommonSuite) TestCommonDefaults() { if suite.Cluster != nil && suite.Cluster.Provisioner() == base.ProvisionerDocker { suite.T().Skip("skipping ulimits test since provisioner is docker") } expectedUlimit := ` core file size (blocks) (-c) 0 data seg size (kb) (-d) unlimited scheduling priority (-e) 0 file size (blocks) (-f) unlimited max locked memory (kb) (-l) 8192 max memory size (kb) (-m) unlimited open files (-n) 1048576 POSIX message queues (bytes) (-q) 819200 real-time priority (-r) 0 stack size (kb) (-s) 8192 cpu time (seconds) (-t) unlimited virtual memory (kb) (-v) unlimited file locks (-x) unlimited ` defaultsTestPodDef, err := suite.NewPod("defaults-ulimits-test") suite.Require().NoError(err) suite.Require().NoError(defaultsTestPodDef.Create(suite.ctx, 5*time.Minute)) defer defaultsTestPodDef.Delete(suite.ctx) //nolint:errcheck stdout, stderr, err := defaultsTestPodDef.Exec( suite.ctx, "ulimit -c -d -e -f -l -m -n -q -r -s -t -v -x", ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Equal(strings.TrimPrefix(expectedUlimit, "\n"), stdout) } // TestDNSResolver verifies that external DNS resolving works from a pod. func (suite *CommonSuite) TestDNSResolver() { if suite.Airgapped { suite.T().Skip("skipping test in airgapped mode") } if suite.Cluster != nil { // cluster should be healthy for kube-dns resolving to work suite.AssertClusterHealthy(suite.ctx) } dnsTestPodDef, err := suite.NewPod("dns-test") suite.Require().NoError(err) suite.Require().NoError(dnsTestPodDef.Create(suite.ctx, 5*time.Minute)) defer dnsTestPodDef.Delete(suite.ctx) //nolint:errcheck stdout, stderr, err := dnsTestPodDef.Exec( suite.ctx, "wget -S https://www.google.com/", ) suite.Assert().NoError(err) suite.Assert().Equal("", stdout) suite.Assert().Contains(stderr, "'index.html' saved") if suite.T().Failed() { suite.LogPodLogsByLabel(suite.ctx, "kube-system", "k8s-app", "kube-dns") for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.DumpLogs(suite.ctx, node, "dns-resolve-cache", "google") } suite.T().FailNow() } _, stderr, err = dnsTestPodDef.Exec( suite.ctx, "apk add --update bind-tools", ) suite.Assert().NoError(err) suite.Assert().Empty(stderr, "stderr: %s", stderr) if suite.T().Failed() { suite.T().FailNow() } stdout, stderr, err = dnsTestPodDef.Exec( suite.ctx, "dig really-long-record.dev.siderolabs.io", ) suite.Assert().NoError(err) suite.Assert().Contains(stdout, "status: NOERROR") suite.Assert().Contains(stdout, "ANSWER: 34") suite.Assert().NotContains(stdout, "status: NXDOMAIN") suite.Assert().Equal(stderr, "") if suite.T().Failed() { suite.T().FailNow() } } // TestBaseOCISpec verifies that the base OCI spec can be modified. func (suite *CommonSuite) TestBaseOCISpec() { if suite.Cluster != nil && suite.Cluster.Provisioner() == base.ProvisionerDocker { suite.T().Skip("skipping ulimits test since provisioner is docker") } if testing.Short() { suite.T().Skip("skipping test in short mode.") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name suite.T().Logf("adjusting base OCI specs on %s/%s", node, nodeName) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { suite.PatchMachineConfig(nodeCtx, &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineBaseRuntimeSpecOverrides: v1alpha1.Unstructured{ Object: map[string]any{ "process": map[string]any{ "rlimits": []map[string]any{ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024, }, }, }, }, }, }, }) return nil }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) suite.ClearConnectionRefused(suite.ctx, node) ociUlimits1PodDef, err := suite.NewPod("oci-ulimits-test-1") suite.Require().NoError(err) ociUlimits1PodDef = ociUlimits1PodDef.WithNodeName(nodeName) suite.Require().NoError(ociUlimits1PodDef.Create(suite.ctx, 5*time.Minute)) defer func() { suite.Assert().NoError(ociUlimits1PodDef.Delete(suite.ctx)) }() stdout, stderr, err := ociUlimits1PodDef.Exec( suite.ctx, "ulimit -n", ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Equal("1024\n", stdout) // delete immediately, as we're going to reboot the node suite.Assert().NoError(ociUlimits1PodDef.Delete(suite.ctx)) // revert the patch suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { suite.PatchMachineConfig(nodeCtx, map[string]any{ "machine": map[string]any{ "baseRuntimeSpecOverrides": map[string]any{ "$patch": "delete", }, }, }) return nil }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) suite.ClearConnectionRefused(suite.ctx, node) ociUlimits2PodDef, err := suite.NewPod("oci-ulimits-test-2") suite.Require().NoError(err) ociUlimits2PodDef = ociUlimits2PodDef.WithNodeName(nodeName) suite.Require().NoError(ociUlimits2PodDef.Create(suite.ctx, 5*time.Minute)) defer func() { suite.Assert().NoError(ociUlimits2PodDef.Delete(suite.ctx)) }() stdout, stderr, err = ociUlimits2PodDef.Exec( suite.ctx, "ulimit -n", ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Equal("1048576\n", stdout) } func init() { allSuites = append(allSuites, &CommonSuite{}) } ================================================ FILE: internal/integration/api/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api const ( // NvidiaGPUOperatorChartVersion is the version of the NVIDA device plugin chart to use // renovate: datasource=helm versioning=helm depName=gpu-operator registryUrl=https://helm.ngc.nvidia.com/nvidia NvidiaGPUOperatorChartVersion = "v25.10.1" // NvidiaCUDATestImageVersion is the version of the NVIDIA CUDA test image to use // renovate: datasource=docker versioning=docker depName=nvcr.io/nvidia/k8s/cuda-sample NvidiaCUDATestImageVersion = "vectoradd-cuda12.5.0" ) ================================================ FILE: internal/integration/api/containers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ContainersSuite ... type ContainersSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ContainersSuite) SuiteName() string { return "api.ContainersSuite" } // SetupTest ... func (suite *ContainersSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), time.Minute) } // TearDownTest ... func (suite *ContainersSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestSandboxImage verifies sandbox image. func (suite *ContainersSuite) TestSandboxImage() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) ctx := client.WithNode(suite.ctx, node) resp, err := suite.Client.Containers(ctx, constants.K8sContainerdNamespace, common.ContainerDriver_CRI) suite.Require().NoError(err) suite.Assert().NotEmpty(resp.GetMessages()) for _, message := range resp.GetMessages() { suite.Assert().NotEmpty(message.GetContainers()) matched := false for _, ctr := range message.GetContainers() { if ctr.PodId == ctr.Id { suite.Assert().Equal(images.DefaultSandboxImage, ctr.Image) matched = true } } suite.Assert().True(matched, "no pods found, node %s", node) } } func init() { allSuites = append(allSuites, new(ContainersSuite)) } ================================================ FILE: internal/integration/api/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "errors" "io" "strings" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) // DebugSuite ... type DebugSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *DebugSuite) SuiteName() string { return "api.DebugSuite" } // SetupTest ... func (suite *DebugSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) } // TearDownTest ... func (suite *DebugSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestRunAlpine tests running a simple alpine container via DebugService. func (suite *DebugSuite) TestRunAlpine() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) image := "docker.io/library/alpine:3.23" for _, driver := range []common.ContainerDriver{common.ContainerDriver_CRI, common.ContainerDriver_CONTAINERD} { rcv, err := suite.Client.ImageClient.Pull(ctx, &machine.ImageServicePullRequest{ Containerd: &common.ContainerdInstance{ Driver: driver, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, ImageRef: image, }) suite.Require().NoError(err) var pulledImage string for { msg, err := rcv.Recv() if err != nil { if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } // ignore progress messages, but the last message should contain the image name pulledImage = msg.GetName() } cli, err := suite.Client.DebugClient.ContainerRun(ctx) suite.Require().NoError(err) suite.Require().NoError(cli.Send(&machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_Spec{ Spec: &machine.DebugContainerRunRequestSpec{ Containerd: &common.ContainerdInstance{ Driver: driver, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, ImageName: pulledImage, Profile: machine.DebugContainerRunRequestSpec_PROFILE_PRIVILEGED, Tty: true, }, }, })) readUntil := func(needle string) { var out strings.Builder for { msg, err := cli.Recv() suite.Require().NoError(err) if msg.GetStdoutData() != nil { out.Write(msg.GetStdoutData()) } if strings.Contains(out.String(), needle) { return } } } readUntil("/ # ") suite.Require().NoError(cli.Send(&machine.DebugContainerRunRequest{ Request: &machine.DebugContainerRunRequest_StdinData{ StdinData: []byte("uname\n"), }, })) readUntil("Linux") suite.Require().NoError(cli.CloseSend()) for { msg, err := cli.Recv() suite.Require().NoError(err) if exitCode, ok := msg.GetResp().(*machine.DebugContainerRunResponse_ExitCode); ok { // SIGHUP is 128 + 1 suite.Equal(int32(129), exitCode.ExitCode) break } suite.T().Logf("got extra stdout: %q", string(msg.GetStdoutData())) } } } func init() { allSuites = append(allSuites, new(DebugSuite)) } ================================================ FILE: internal/integration/api/discovery.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "math/rand/v2" "net/netip" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) // DiscoverySuite verifies Discovery API. type DiscoverySuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *DiscoverySuite) SuiteName() string { return "api.DiscoverySuite" } // SetupTest ... func (suite *DiscoverySuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) // check that cluster has discovery enabled node := suite.RandomDiscoveredNodeInternalIP() suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) if !provider.Cluster().Discovery().Enabled() { suite.T().Skip("cluster discovery is disabled") } } // TearDownTest ... func (suite *DiscoverySuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestMembers checks that `talosctl get members` matches expected cluster discovery. // //nolint:gocyclo func (suite *DiscoverySuite) TestMembers() { nodes := suite.DiscoverNodes(suite.ctx).Nodes() expectedTalosVersion := fmt.Sprintf("Talos (%s)", suite.Version) for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node.InternalIP.String()) members := suite.getMembers(nodeCtx) suite.Assert().Len(members, len(nodes)) // do basic check against discovered nodes for _, expectedNode := range nodes { nodeAddresses := xslices.Map(expectedNode.IPs, func(t netip.Addr) string { return t.String() }) found := false for _, member := range members { memberAddresses := xslices.Map(member.TypedSpec().Addresses, func(t netip.Addr) string { return t.String() }) if maps.Contains(xslices.ToSet(memberAddresses), nodeAddresses) { found = true break } if found { break } } suite.Assert().True(found, "addr %q", nodeAddresses) } // if cluster information is available, perform additional checks if suite.Cluster == nil { continue } memberByName := xslices.ToMap(members, func(member *cluster.Member) (string, *cluster.Member) { return member.Metadata().ID(), member }, ) memberByIP := make(map[netip.Addr]*cluster.Member) for _, member := range members { for _, addr := range member.TypedSpec().Addresses { memberByIP[addr] = member } } nodesInfo := suite.Cluster.Info().Nodes for _, nodeInfo := range nodesInfo { matchingMember := memberByName[nodeInfo.Name] var matchingMemberByIP *cluster.Member for _, nodeIP := range nodeInfo.IPs { matchingMemberByIP = memberByIP[nodeIP] break } // if hostnames are not set via DHCP, use match by IP if matchingMember == nil { matchingMember = matchingMemberByIP } suite.Require().NotNil(matchingMember) suite.Assert().Equal(nodeInfo.Type, matchingMember.TypedSpec().MachineType) suite.Assert().Equal(expectedTalosVersion, matchingMember.TypedSpec().OperatingSystem) for _, nodeIP := range nodeInfo.IPs { found := false for _, memberAddr := range matchingMember.TypedSpec().Addresses { if memberAddr.Compare(nodeIP) == 0 { found = true break } } suite.Assert().True(found, "addr %s", nodeIP) } } } } // TestRegistries checks that all registries produce same raw Affiliate data. func (suite *DiscoverySuite) TestRegistries() { node := suite.RandomDiscoveredNodeInternalIP() suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) var registries []string if provider.Cluster().Discovery().Registries().Kubernetes().Enabled() { registries = append(registries, "k8s/") } if provider.Cluster().Discovery().Registries().Service().Enabled() { registries = append(registries, "service/") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node) members := suite.getMembers(nodeCtx) localIdentity := suite.getNodeIdentity(nodeCtx) // raw affiliates don't contain the local node expectedRawAffiliates := len(registries) * (len(members) - 1) var rawAffiliates []*cluster.Affiliate for range 30 { rawAffiliates = suite.getAffiliates(nodeCtx, cluster.RawNamespaceName) if len(rawAffiliates) == expectedRawAffiliates { break } suite.T().Logf("waiting for cluster affiliates to be discovered: %d expected, %d found", expectedRawAffiliates, len(rawAffiliates)) time.Sleep(2 * time.Second) } suite.Assert().Len(rawAffiliates, expectedRawAffiliates) rawAffiliatesByID := make(map[string]*cluster.Affiliate) for _, rawAffiliate := range rawAffiliates { rawAffiliatesByID[rawAffiliate.Metadata().ID()] = rawAffiliate } // every member except for local identity member should be discovered via each registry for _, member := range members { if member.TypedSpec().NodeID == localIdentity.TypedSpec().NodeID { continue } for _, registry := range registries { rawAffiliate := rawAffiliatesByID[registry+member.TypedSpec().NodeID] suite.Require().NotNil(rawAffiliate) stripDomain := func(s string) string { return strings.Split(s, ".")[0] } // registries can be a bit inconsistent, e.g. whether they report fqdn or just hostname suite.Assert().Contains([]string{member.TypedSpec().Hostname, stripDomain(member.TypedSpec().Hostname)}, rawAffiliate.TypedSpec().Hostname) suite.Assert().Equal(member.TypedSpec().Addresses, rawAffiliate.TypedSpec().Addresses) suite.Assert().Equal(member.TypedSpec().OperatingSystem, rawAffiliate.TypedSpec().OperatingSystem) suite.Assert().Equal(member.TypedSpec().MachineType, rawAffiliate.TypedSpec().MachineType) } } } } // TestKubeSpanPeers verifies that KubeSpan peer specs are populated, and that peer statuses are available. func (suite *DiscoverySuite) TestKubeSpanPeers() { if !suite.Capabilities().RunsTalosKernel { suite.T().Skip("not running Talos kernel") } // check that cluster has KubeSpan enabled node := suite.RandomDiscoveredNodeInternalIP() suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) if kubeSpan := provider.NetworkKubeSpanConfig(); kubeSpan == nil || !kubeSpan.Enabled() { suite.T().Skip("KubeSpan is disabled") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node) rtestutils.AssertLength[*kubespan.PeerSpec](nodeCtx, suite.T(), suite.Client.COSI, len(nodes)-1) rtestutils.AssertLength[*kubespan.PeerStatus](nodeCtx, suite.T(), suite.Client.COSI, len(nodes)-1) rtestutils.AssertAll(nodeCtx, suite.T(), suite.Client.COSI, func(status *kubespan.PeerStatus, asrt *assert.Assertions) { asrt.Equal(kubespan.PeerStateUp, status.TypedSpec().State) asrt.False(value.IsZero(status.TypedSpec().Endpoint)) asrt.Greater(status.TypedSpec().ReceiveBytes, int64(0)) asrt.Greater(status.TypedSpec().TransmitBytes, int64(0)) }) } } // TestKubeSpanExtraEndpoints verifies that KubeSpan peer specs are updated with extra endpoints. func (suite *DiscoverySuite) TestKubeSpanExtraEndpoints() { if !suite.Capabilities().RunsTalosKernel { suite.T().Skip("not running Talos kernel") } // check that cluster has KubeSpan enabled node := suite.RandomDiscoveredNodeInternalIP() suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) if kubeSpan := provider.NetworkKubeSpanConfig(); kubeSpan == nil || !kubeSpan.Enabled() { suite.T().Skip("KubeSpan is disabled") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) if len(nodes) < 2 { suite.T().Skip("need at least two nodes for this test") } perm := rand.Perm(len(nodes)) checkNode := nodes[perm[0]] targetNode := nodes[perm[1]] mockEndpoint := netip.MustParseAddrPort("169.254.121.121:5820") // inject extra endpoint to target node cfgDocument := network.NewKubespanEndpointsV1Alpha1() cfgDocument.ExtraAnnouncedEndpointsConfig = []netip.AddrPort{mockEndpoint} suite.T().Logf("injecting extra endpoint %s to node %s", mockEndpoint, targetNode) suite.PatchMachineConfig(client.WithNode(suite.ctx, targetNode), cfgDocument) targetIdentity, err := safe.ReaderGetByID[*kubespan.Identity](client.WithNode(suite.ctx, targetNode), suite.Client.COSI, kubespan.LocalIdentity) suite.Require().NoError(err) suite.T().Logf("checking extra endpoint %s on node %s", mockEndpoint, checkNode) rtestutils.AssertResources(client.WithNode(suite.ctx, checkNode), suite.T(), suite.Client.COSI, []string{targetIdentity.TypedSpec().PublicKey}, func(peer *kubespan.PeerSpec, asrt *assert.Assertions) { asrt.Contains(peer.TypedSpec().Endpoints, mockEndpoint) }, ) // the extra endpoints disappears with a timeout from the discovery service, so can't assert on that suite.T().Logf("removin extra endpoint %s from node %s", mockEndpoint, targetNode) suite.RemoveMachineConfigDocuments(client.WithNode(suite.ctx, targetNode), cfgDocument.MetaKind) } func (suite *DiscoverySuite) getMembers(nodeCtx context.Context) []*cluster.Member { items, err := safe.StateListAll[*cluster.Member](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) return safe.ToSlice(items, func(m *cluster.Member) *cluster.Member { return m }) } func (suite *DiscoverySuite) getNodeIdentity(nodeCtx context.Context) *cluster.Identity { identity, err := safe.StateGet[*cluster.Identity](nodeCtx, suite.Client.COSI, resource.NewMetadata(cluster.NamespaceName, cluster.IdentityType, cluster.LocalIdentity, resource.VersionUndefined)) suite.Require().NoError(err) return identity } func (suite *DiscoverySuite) getAffiliates(nodeCtx context.Context, namespace resource.Namespace) []*cluster.Affiliate { var result []*cluster.Affiliate items, err := safe.StateList[*cluster.Affiliate](nodeCtx, suite.Client.COSI, resource.NewMetadata(namespace, cluster.AffiliateType, "", resource.VersionUndefined)) suite.Require().NoError(err) items.ForEach(func(item *cluster.Affiliate) { result = append(result, item) }) return result } func init() { allSuites = append(allSuites, new(DiscoverySuite)) } ================================================ FILE: internal/integration/api/diskusage.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "io" "time" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) // DiskUsageSuite verifies Logs API. type DiskUsageSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc nodeCtx context.Context //nolint:containedctx } // SuiteName ... func (suite *DiskUsageSuite) SuiteName() string { return "api.DiskUsageSuite" } // SetupTest ... func (suite *DiskUsageSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) suite.nodeCtx = client.WithNodes(suite.ctx, suite.RandomDiscoveredNodeInternalIP()) } // TearDownTest ... func (suite *DiskUsageSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestDiskUsageRequests compares results of disk usage requests with different parameters. func (suite *DiskUsageSuite) TestDiskUsageRequests() { type testParams struct { recursionDepth int32 all bool paths []string } defaultPaths := []string{ "/etc", "/bin", } cases := []*testParams{ { recursionDepth: 0, all: false, paths: defaultPaths, }, { recursionDepth: 1, all: false, paths: defaultPaths, }, { recursionDepth: 0, all: true, paths: defaultPaths, }, { recursionDepth: 1, all: true, paths: defaultPaths, }, { recursionDepth: 0, all: true, paths: append([]string{"/this/is/going/to/fail"}, defaultPaths...), }, } sizes := map[string]int64{} for _, params := range cases { lookupPaths := map[string]bool{} for _, path := range params.paths { lookupPaths[path] = true } stream, err := suite.Client.DiskUsage( suite.nodeCtx, &machineapi.DiskUsageRequest{ Paths: params.paths, RecursionDepth: params.recursionDepth, All: params.all, }, ) suite.Require().NoError(err) responseCount := 0 for { info, err := stream.Recv() responseCount++ if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { break } suite.Require().NoError(err) } if size, ok := sizes[info.Name]; ok { suite.Require().EqualValues(size, info.Size) } sizes[info.Name] = info.Size } suite.Require().Greater(responseCount, 1) } } func init() { allSuites = append(allSuites, new(DiskUsageSuite)) } ================================================ FILE: internal/integration/api/dmesg.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "io" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" ) // DmesgSuite verifies Dmesg API. type DmesgSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *DmesgSuite) SuiteName() string { return "api.DmesgSuite" } // SetupTest ... func (suite *DmesgSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) } // TearDownTest ... func (suite *DmesgSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestNodeHasDmesg verifies that default node has dmesg. func (suite *DmesgSuite) TestNodeHasDmesg() { dmesgStream, err := suite.Client.Dmesg( suite.ctx, false, false, ) suite.Require().NoError(err) logReader, err := client.ReadStream(dmesgStream) suite.Require().NoError(err) n, err := io.Copy(io.Discard, logReader) suite.Require().NoError(err) // dmesg shouldn't be empty suite.Require().Greater(n, int64(1024)) } // TestStreaming verifies that logs are streamed in real-time. func (suite *DmesgSuite) TestStreaming() { dmesgStream, err := suite.Client.Dmesg( suite.ctx, true, false, ) suite.Require().NoError(err) suite.Require().NoError(dmesgStream.CloseSend()) respCh := make(chan *common.Data) errCh := make(chan error, 1) go func() { defer close(respCh) for { msg, err := dmesgStream.Recv() if err != nil { errCh <- err return } respCh <- msg } }() defer func() { suite.ctxCancel() // drain respCh for range respCh { //nolint:revive } }() // drain the stream until flow stops logCount := 0 DrainLoop: for { select { case msg, ok := <-respCh: logCount++ suite.Require().True(ok) suite.Assert().NotEmpty(msg.Bytes) case <-time.After(200 * time.Millisecond): break DrainLoop } } suite.Assert().Greater(logCount, 10) } // TestClusterHasDmesg verifies that all the cluster nodes have dmesg. func (suite *DmesgSuite) TestClusterHasDmesg() { nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) ctx := client.WithNodes(suite.ctx, nodes...) dmesgStream, err := suite.Client.Dmesg( ctx, false, false, ) suite.Require().NoError(err) sizeByNode := map[string]int{} for { msg, err := dmesgStream.Recv() if err != nil { if err == io.EOF { break } suite.Require().NoError(err) } suite.Require().NotNil(msg.Metadata) suite.Assert().Empty(msg.Metadata.Error) sizeByNode[msg.Metadata.Hostname] += len(msg.Bytes) } for _, node := range nodes { suite.Assert().Greater(sizeByNode[node], 1024) } for node := range sizeByNode { suite.Assert().Contains(nodes, node) } } func init() { allSuites = append(allSuites, new(DmesgSuite)) } ================================================ FILE: internal/integration/api/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/safe" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" runtimecfg "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // EnvironmentSuite verifies Environment API. type EnvironmentSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *EnvironmentSuite) SuiteName() string { return "api.EnvironmentSuite" } // SetupTest ... func (suite *EnvironmentSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *EnvironmentSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestEnvironment tests setting environment variables via Environment API. func (suite *EnvironmentSuite) TestEnvironment() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) ctx := client.WithNode(suite.ctx, node) suite.Require().Eventually(func() bool { return suite.validateEnvironment(node, []string{"TALOS_TEST_ENV=1"}, false) }, 5*time.Second, 1*time.Second, "environment variable was there before apply") suite.T().Logf("applying environment configuration") doc := runtimecfg.NewEnvironmentV1Alpha1() doc.EnvironmentVariables = map[string]string{"TALOS_TEST_ENV": "1"} suite.PatchMachineConfig(ctx, doc) suite.Require().Eventually(func() bool { return suite.validateEnvironment(node, []string{"TALOS_TEST_ENV=1"}, true) }, 5*time.Second, 1*time.Second, "environment variable was not set after apply") // now we want to reboot the node and make sure the env is retained suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 5*time.Minute, suite.CleanupFailedPods, ) suite.Require().Eventually(func() bool { return suite.validateEnvironment(node, []string{"TALOS_TEST_ENV=1"}, true) }, 5*time.Second, 1*time.Second, "environment variable was not retained after reboot") suite.T().Logf("removing environment configuration") suite.RemoveMachineConfigDocuments(ctx, runtimecfg.EnvironmentConfigKind) // now we want to reboot the node and make sure the env is removed suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 5*time.Minute, suite.CleanupFailedPods, ) suite.Require().Eventually(func() bool { return suite.validateEnvironment(node, []string{"TALOS_TEST_ENV=1"}, false) }, 5*time.Second, 1*time.Second, "environment variable was not removed after reboot") } func (suite *EnvironmentSuite) validateEnvironment(node string, expectedVariables []string, shouldContain bool) bool { suite.ClearConnectionRefused(suite.ctx, node) ctx := client.WithNode(suite.ctx, node) env, err := safe.StateGetByID[*runtime.Environment](ctx, suite.Client.COSI, "machined") if err != nil { code := client.StatusCode(err) switch code { //nolint:exhaustive case codes.Unavailable, codes.Canceled, codes.NotFound: return false } suite.Require().NoError(err) } for _, v := range expectedVariables { if slices.Contains(env.TypedSpec().Variables, v) != shouldContain { return false } } return true } func init() { allSuites = append(allSuites, new(EnvironmentSuite)) } ================================================ FILE: internal/integration/api/etcd-recover.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bytes" "context" "fmt" "io" "path/filepath" "testing" "time" "github.com/siderolabs/go-retry/retry" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // EtcdRecoverSuite ... type EtcdRecoverSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *EtcdRecoverSuite) SuiteName() string { return "api.EtcdRecoverSuite" } // SetupTest ... func (suite *EtcdRecoverSuite) SetupTest() { if testing.Short() { suite.T().Skip("skipping in short mode") } // make sure we abort at some point in time, but give enough room for Recovers suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 10*time.Minute) } // TearDownTest ... func (suite *EtcdRecoverSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestSnapshotRecover snapshot etcd, wipes control plane nodes and recovers etcd from a snapshot. func (suite *EtcdRecoverSuite) TestSnapshotRecover() { if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot") } if suite.Cluster == nil { suite.T().Skip("without full cluster state reset test is not reliable (can't wait for cluster readiness in between resets)") } // 'init' nodes are not compatible with etcd recovery suite.Require().Empty(suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeInit)) controlPlaneNodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) suite.Require().NotEmpty(controlPlaneNodes) snapshotNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) recoverNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.WaitForBootDone(suite.ctx) suite.T().Logf("taking etcd snapshot at node %q", snapshotNode) var snapshot bytes.Buffer suite.Require().NoError(suite.snapshotEtcd(snapshotNode, &snapshot)) // leave etcd on all nodes but one for _, node := range controlPlaneNodes[1:] { suite.T().Logf("leaving etcd on node %q", node) nodeCtx := client.WithNode(suite.ctx, node) _, err := suite.Client.EtcdForfeitLeadership(nodeCtx, &machineapi.EtcdForfeitLeadershipRequest{}) suite.Require().NoError(err) err = suite.Client.EtcdLeaveCluster(nodeCtx, &machineapi.EtcdLeaveClusterRequest{}) suite.Require().NoError(err) } // wipe ephemeral partition on all control plane nodes, starting with the one that still has etcd running for _, node := range controlPlaneNodes { suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: false, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, }, false) } // verify that etcd data directory doesn't exist on the nodes for _, node := range controlPlaneNodes { stream, err := suite.Client.MachineClient.List(client.WithNode(suite.ctx, node), &machineapi.ListRequest{Root: filepath.Join(constants.EtcdDataPath, "member")}) suite.Require().NoError(err) _, err = stream.Recv() suite.Require().Error(err) suite.Require().Equal(client.StatusCode(err), codes.Unknown) suite.Require().Contains(client.Status(err).Message(), "no such file or directory") } suite.T().Logf("recovering etcd snapshot at node %q", recoverNode) suite.Require().NoError(suite.recoverEtcd(recoverNode, bytes.NewReader(snapshot.Bytes()))) suite.AssertClusterHealthy(suite.ctx) } func (suite *EtcdRecoverSuite) snapshotEtcd(snapshotNode string, dest io.Writer) error { ctx := client.WithNodes(suite.ctx, snapshotNode) r, err := suite.Client.EtcdSnapshot(ctx, &machineapi.EtcdSnapshotRequest{}) if err != nil { return fmt.Errorf("error reading snapshot: %w", err) } defer r.Close() //nolint:errcheck _, err = io.Copy(dest, r) return err } func (suite *EtcdRecoverSuite) recoverEtcd(recoverNode string, src io.ReadSeeker) error { ctx := client.WithNodes(suite.ctx, recoverNode) suite.T().Log("uploading the snapshot") if err := retry.Constant(time.Minute, retry.WithUnits(time.Millisecond*200)).RetryWithContext( ctx, func(ctx context.Context) error { _, err := src.Seek(0, io.SeekStart) if err != nil { return err } _, err = suite.Client.EtcdRecover(ctx, src) if client.StatusCode(err) == codes.FailedPrecondition { return retry.ExpectedError(err) } return err }, ); err != nil { return fmt.Errorf("error uploading snapshot: %w", err) } suite.T().Log("bootstrapping from the snapshot") return retry.Constant(time.Minute, retry.WithUnits(time.Millisecond*200)).RetryWithContext( ctx, func(ctx context.Context) error { err := suite.Client.Bootstrap( ctx, &machineapi.BootstrapRequest{ RecoverEtcd: true, }, ) if client.StatusCode(err) == codes.FailedPrecondition || client.StatusCode(err) == codes.DeadlineExceeded { return retry.ExpectedError(err) } return err }, ) } func init() { allSuites = append(allSuites, new(EtcdRecoverSuite)) } ================================================ FILE: internal/integration/api/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "io" "testing" "time" "github.com/blang/semver/v4" "github.com/cosi-project/runtime/pkg/safe" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EtcdSuite ... type EtcdSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *EtcdSuite) SuiteName() string { return "api.EtcdSuite" } // SetupTest ... func (suite *EtcdSuite) SetupTest() { // make sure we abort at some point in time, but give enough room for Etcds suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Minute) } // TearDownTest ... func (suite *EtcdSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestForfeitLeadership tests moving etcd leadership to another member. func (suite *EtcdSuite) TestForfeitLeadership() { if suite.Cluster == nil { suite.T().Skip("without full cluster state etcd test is not reliable (can't wait for cluster readiness in between resets)") } nodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) if len(nodes) < 3 { suite.T().Skip("test only can be run on HA etcd clusters") } var leader string for _, node := range nodes { resp, err := suite.Client.EtcdForfeitLeadership( client.WithNodes(suite.ctx, node), &machineapi.EtcdForfeitLeadershipRequest{}, ) suite.Require().NoError(err) if resp.Messages[0].GetMember() != "" { leader = resp.Messages[0].GetMember() suite.T().Log("Moved leadership to", leader) } } suite.Assert().NotEmpty(leader) } // TestLeaveCluster tests removing an etcd member. // //nolint:gocyclo func (suite *EtcdSuite) TestLeaveCluster() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot (and reset)") } if suite.Cluster == nil { suite.T().Skip("without full cluster state reset test is not reliable (can't wait for cluster readiness in between resets)") } nodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) if len(nodes) < 3 { suite.T().Skip("test only can be run on HA etcd clusters") } node := nodes[len(nodes)-1] suite.T().Log("Removing etcd member", node) nodeCtx := client.WithNodes(suite.ctx, node) _, err := suite.Client.EtcdForfeitLeadership(nodeCtx, &machineapi.EtcdForfeitLeadershipRequest{}) suite.Require().NoError(err) err = suite.Client.EtcdLeaveCluster(nodeCtx, &machineapi.EtcdLeaveClusterRequest{}) suite.Require().NoError(err) services, err := suite.Client.ServiceInfo(nodeCtx, "etcd") suite.Require().NoError(err) for _, service := range services { if service.Service.Id == "etcd" { suite.Assert().Equal("Finished", service.Service.State) } } stream, err := suite.Client.MachineClient.List(nodeCtx, &machineapi.ListRequest{Root: constants.EtcdDataPath}) suite.Require().NoError(err) for { var info *machineapi.FileInfo info, err = stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { break } } suite.Assert().Equal( "rpc error: code = Unknown desc = lstat /var/lib/etcd: no such file or directory", info.Metadata.Error, ) } // NB: Reboot the node so that it can rejoin the etcd cluster. This allows us // to check the cluster health and catch any issues in rejoining. suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { _, err = suite.Client.MachineClient.Reboot(nodeCtx, &machineapi.RebootRequest{}) return err }, 10*time.Minute, suite.CleanupFailedPods, ) } // TestMembers verifies that etcd members as resources and API response are consistent. func (suite *EtcdSuite) TestMembers() { nodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) // map member ID to hostname etcdMembers := map[string]string{} for _, node := range nodes { member, err := safe.StateGet[*etcd.Member](client.WithNode(suite.ctx, node), suite.Client.COSI, etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID).Metadata()) suite.Require().NoError(err) hostname, err := safe.StateGet[*network.HostnameStatus](client.WithNode(suite.ctx, node), suite.Client.COSI, network.NewHostnameStatus(network.NamespaceName, network.HostnameID).Metadata()) suite.Require().NoError(err) etcdMembers[member.TypedSpec().MemberID] = hostname.TypedSpec().Hostname } suite.Assert().Len(etcdMembers, len(nodes)) resp, err := suite.Client.EtcdMemberList(suite.ctx, &machineapi.EtcdMemberListRequest{}) suite.Require().NoError(err) count := 0 for _, message := range resp.GetMessages() { for _, member := range message.GetMembers() { count++ memberID := etcd.FormatMemberID(member.GetId()) suite.Assert().Contains(etcdMembers, memberID) suite.Assert().Equal(etcdMembers[memberID], member.GetHostname()) } } suite.Assert().Equal(len(etcdMembers), count) } // TestRemoveMember tests removing an etcd member forcefully. func (suite *EtcdSuite) TestRemoveMember() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot (and reset)") } if suite.Cluster == nil { suite.T().Skip("without full cluster state reset test is not reliable (can't wait for cluster readiness in between resets)") } nodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) if len(nodes) < 3 { suite.T().Skip("test only can be run on HA etcd clusters") } controlNode, nodeToRemove := nodes[len(nodes)-1], nodes[0] suite.T().Log("Removing etcd member", nodeToRemove) removeCtx := client.WithNode(suite.ctx, nodeToRemove) controlCtx := client.WithNode(suite.ctx, controlNode) _, err := suite.Client.EtcdForfeitLeadership(removeCtx, &machineapi.EtcdForfeitLeadershipRequest{}) suite.Require().NoError(err) member, err := safe.StateGet[*etcd.Member](removeCtx, suite.Client.COSI, etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID).Metadata()) suite.Require().NoError(err) memberID, err := etcd.ParseMemberID(member.TypedSpec().MemberID) suite.Require().NoError(err) err = suite.Client.EtcdRemoveMemberByID(controlCtx, &machineapi.EtcdRemoveMemberByIDRequest{ MemberId: memberID, }) suite.Require().NoError(err) // verify that memberID disappeared from etcd member list resp, err := suite.Client.EtcdMemberList(controlCtx, &machineapi.EtcdMemberListRequest{}) suite.Require().NoError(err) for _, message := range resp.GetMessages() { for _, member := range message.GetMembers() { suite.Assert().NotEqual(memberID, member.GetId()) } } // NB: Reset the ephemeral the node so that it can rejoin the etcd cluster. This allows us // to check the cluster health and catch any issues in rejoining. suite.AssertRebooted( suite.ctx, nodeToRemove, func(nodeCtx context.Context) error { _, err = suite.Client.MachineClient.Reset(nodeCtx, &machineapi.ResetRequest{ Reboot: true, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, }) return err }, 10*time.Minute, suite.CleanupFailedPods, ) } func (suite *EtcdSuite) downgradesSupported() bool { suite.T().Helper() downgradeFromAtLeast := semver.Version{Major: 3, Minor: 6} status, err := suite.Client.EtcdStatus(suite.ctx) suite.Require().NoError(err) for _, message := range status.Messages { etcdVersion := message.GetMemberStatus().GetProtocolVersion() etcdVersionSemver, err := semver.Parse(etcdVersion) if err != nil { suite.T().Logf("ETCD version parsing failed: %s", err) continue } if etcdVersionSemver.LT(downgradeFromAtLeast) { return false } } return true } // TestDowngrade tests downgrade API of etcd. func (suite *EtcdSuite) TestDowngrade() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot (and reset)") } if !suite.downgradesSupported() { suite.T().Skip("cluster needs to run ETCD 3.6.x to support downgrades") } nodes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) node := nodes[0] downgradeTo := "3.5" // test the downgrade validate API validateResp, err := suite.Client.EtcdDowngradeValidate( client.WithNodes(suite.ctx, node), &machineapi.EtcdDowngradeValidateRequest{Version: downgradeTo}, ) suite.Require().NoError(err) for _, message := range validateResp.GetMessages() { suite.Assert().NotEqual(downgradeTo, message.GetClusterDowngrade().GetClusterVersion()) } // test the downgrade enable API enableResp, err := suite.Client.EtcdDowngradeEnable( client.WithNodes(suite.ctx, node), &machineapi.EtcdDowngradeEnableRequest{Version: downgradeTo}, ) suite.Require().NoError(err) for _, message := range enableResp.GetMessages() { suite.Assert().NotEqual(downgradeTo, message.GetClusterDowngrade().GetClusterVersion()) } // test the downgrade cancel API cancelResp, err := suite.Client.EtcdDowngradeCancel( client.WithNodes(suite.ctx, node), ) suite.Require().NoError(err) for _, message := range cancelResp.GetMessages() { suite.Assert().NotEqual(downgradeTo, message.GetClusterDowngrade().GetClusterVersion()) } } func init() { allSuites = append(allSuites, new(EtcdSuite)) } ================================================ FILE: internal/integration/api/ethernet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "math/rand/v2" "os" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" networkconfig "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // EthernetSuite ... type EthernetSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *EthernetSuite) SuiteName() string { return "api.EthernetSuite" } // SetupTest ... func (suite *EthernetSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute) if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping ethernet test since provisioner is not qemu") } } // TearDownTest ... func (suite *EthernetSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } func getFeatureStatus(t *testing.T, features network.EthernetFeatureStatusList, name string) bool { t.Helper() for _, f := range features { if f.Name == name { switch f.Status { case "on": return true case "off": return false default: require.Fail(t, "unexpected feature status: %s", f.Status) } } } require.Fail(t, "feature %s not found", name) panic("unreachable") } // TestEthernetConfig verifies changing Ethernet settings. func (suite *EthernetSuite) TestEthernetConfig() { // pick up a random node to test the Ethernet on, and use it throughout the test node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("testing Ethernet on node %s", node) // build a Talos API context which is tied to the node nodeCtx := client.WithNode(suite.ctx, node) // pick a Ethernet links ethStatuses, err := safe.StateListAll[*network.EthernetStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) var ( linkName string prevRingConfig *network.EthernetRingsStatus prevFeatures network.EthernetFeatureStatusList ) for ethStatus := range ethStatuses.All() { if ethStatus.TypedSpec().Rings != nil && ethStatus.TypedSpec().Rings.RXMax != nil { linkName = ethStatus.Metadata().ID() prevRingConfig = ethStatus.TypedSpec().Rings prevFeatures = ethStatus.TypedSpec().Features marshaled, err := resource.MarshalYAML(ethStatus) suite.Require().NoError(err) out, err := yaml.Marshal(marshaled) suite.Require().NoError(err) suite.T().Logf("found link %s with: %s", linkName, string(out)) break } } suite.Require().NotEmpty(linkName, "no link provides RX rings") suite.Require().NotEmpty(prevFeatures, "no link provides features") suite.Run("Rings", func() { if os.Getenv("CI") != "" { suite.T().Skip("skipping ethtool test in CI, as QEMU version doesn't support updating RX rings for virtio") } // first, adjust RX rings to be 50% of what it was before newRX := pointer.SafeDeref(prevRingConfig.RXMax) / 2 suite.T().Logf("testing RX rings on link %s: %d -> %d", linkName, pointer.SafeDeref(prevRingConfig.RX), newRX) cfgDocument := networkconfig.NewEthernetConfigV1Alpha1(linkName) cfgDocument.RingsConfig = &networkconfig.EthernetRingsConfig{ RX: new(newRX), } suite.PatchMachineConfig(nodeCtx, cfgDocument) // now EthernetStatus should reflect the new RX rings rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) { asrt.Equal(newRX, pointer.SafeDeref(ethStatus.TypedSpec().Rings.RX)) }, ) // now, let's revert the RX rings to what it was before cfgDocument.RingsConfig.RX = prevRingConfig.RX suite.PatchMachineConfig(nodeCtx, cfgDocument) // now EthernetStatus should reflect the new RX rings rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) { asrt.Equal(pointer.SafeDeref(prevRingConfig.RX), pointer.SafeDeref(ethStatus.TypedSpec().Rings.RX)) }, ) // remove the config document suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) }) suite.Run("Features", func() { const featureName = "tx-tcp-segmentation" // get the initial state initialState := getFeatureStatus(suite.T(), prevFeatures, featureName) suite.T().Logf("testing feature %s on link %s: %v -> %v", featureName, linkName, initialState, !initialState) cfgDocument := networkconfig.NewEthernetConfigV1Alpha1(linkName) cfgDocument.FeaturesConfig = map[string]bool{ featureName: !initialState, } suite.PatchMachineConfig(nodeCtx, cfgDocument) // now EthernetStatus should reflect the new feature status rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) { asrt.Equal(!initialState, getFeatureStatus(suite.T(), ethStatus.TypedSpec().Features, featureName)) }, ) // now, let's revert the RX rings to what it was before cfgDocument.FeaturesConfig[featureName] = initialState suite.PatchMachineConfig(nodeCtx, cfgDocument) // now EthernetStatus should reflect the old feature status rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(ethStatus *network.EthernetStatus, asrt *assert.Assertions) { asrt.Equal(initialState, getFeatureStatus(suite.T(), ethStatus.TypedSpec().Features, featureName)) }, ) // remove the config document suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) }) suite.Run("Channels", func() { suite.T().Skip("channels are not supported by the current QEMU version") }) } // TestBridgeMAC verifies bridge MAC address. func (suite *EthernetSuite) TestBridgeMAC() { // pick up a random node to test the Ethernet on, and use it throughout the test node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("testing bridge MAC on node %s", node) // build a Talos API context which is tied to the node nodeCtx := client.WithNode(suite.ctx, node) randomSuffix := fmt.Sprintf("%04x", rand.Int32()) dc := networkconfig.NewDummyLinkConfigV1Alpha1("dummy" + randomSuffix) bc := networkconfig.NewBridgeConfigV1Alpha1("bridge" + randomSuffix) suite.PatchMachineConfig(nodeCtx, dc, bc) var dummyMAC string // the links should be created rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []string{"dummy" + randomSuffix, "bridge" + randomSuffix}, func(link *network.LinkStatus, _ *assert.Assertions) { if link.TypedSpec().Kind == "dummy" { dummyMAC = link.TypedSpec().HardwareAddr.String() } }) suite.Assert().NotEmpty(dummyMAC, "dummy MAC address is empty") // now, let's put dummy interface into the bridge bc.BridgeLinks = []string{"dummy" + randomSuffix} suite.PatchMachineConfig(nodeCtx, dc, bc) // now bridge should have the same MAC address as dummy rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []string{"dummy" + randomSuffix, "bridge" + randomSuffix}, func(link *network.LinkStatus, asrt *assert.Assertions) { asrt.Equal(dummyMAC, link.TypedSpec().HardwareAddr.String(), "dummy MAC address is not equal to bridge MAC address") }) // revert the changes removing the dummy interface from the bridge patches := []any{ map[string]any{ "apiVersion": "v1alpha1", "kind": networkconfig.BridgeKind, "name": "bridge" + randomSuffix, "$patch": "delete", }, map[string]any{ "apiVersion": "v1alpha1", "kind": networkconfig.DummyLinkKind, "name": "dummy" + randomSuffix, "$patch": "delete", }, } suite.PatchMachineConfig(nodeCtx, patches...) rtestutils.AssertNoResource[*network.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, "dummy"+randomSuffix) rtestutils.AssertNoResource[*network.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, "bridge"+randomSuffix) } func init() { allSuites = append(allSuites, new(EthernetSuite)) } ================================================ FILE: internal/integration/api/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" machinetype "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // EventsSuite verifies Events API. type EventsSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc nodeCtx context.Context //nolint:containedctx } // SuiteName ... func (suite *EventsSuite) SuiteName() string { return "api.EventsSuite" } // SetupTest ... func (suite *EventsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) suite.nodeCtx = client.WithNodes(suite.ctx, suite.RandomDiscoveredNodeInternalIP(machinetype.TypeWorker)) } // TearDownTest ... func (suite *EventsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestEventsWatch verifies events watch API. func (suite *EventsSuite) TestEventsWatch() { receiveEvents := func(opts ...client.EventsOptionFunc) []client.Event { var result []client.Event watchCtx, watchCtxCancel := context.WithCancel(suite.nodeCtx) defer watchCtxCancel() suite.Assert().NoError( suite.Client.EventsWatch( watchCtx, func(ch <-chan client.Event) { defer watchCtxCancel() timer := time.NewTimer(500 * time.Millisecond) defer timer.Stop() for { select { case event, ok := <-ch: if !ok { return } result = append(result, event) case <-timer.C: return } } }, opts..., ), ) return result } allEvents := receiveEvents(client.WithTailEvents(-1)) suite.Require().Greater(len(allEvents), 20) suite.Assert().Len(receiveEvents(), 0) suite.Assert().Len(receiveEvents(client.WithTailEvents(5)), 5) suite.Assert().Len(receiveEvents(client.WithTailEvents(20)), 20) // pick some ID of 15th event in the past; API should return at least 14 events // (as check excludes that event with picked ID) id := allEvents[len(allEvents)-15].ID eventsSinceID := receiveEvents(client.WithTailID(id)) suite.Require().GreaterOrEqual( len(eventsSinceID), 14, ) // there might some new events since allEvents, but at least 15 should be received } func init() { allSuites = append(allSuites, new(EventsSuite)) } ================================================ FILE: internal/integration/api/extensions_nvidia.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" _ "embed" "fmt" "io" "time" "github.com/siderolabs/go-retry/retry" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" ) //go:embed testdata/nvidia-gpu-operator.yaml var nvidiaGPUOperatorHelmChartValues []byte // ExtensionsSuiteNVIDIA verifies Talos is securebooted. type ExtensionsSuiteNVIDIA struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ExtensionsSuiteNVIDIA) SuiteName() string { return "api.ExtensionsSuiteNVIDIA" } // SetupTest ... func (suite *ExtensionsSuiteNVIDIA) SetupTest() { if !suite.ExtensionsNvidia { suite.T().Skip("skipping as nvidia extensions test are not enabled") } // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *ExtensionsSuiteNVIDIA) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestExtensionsNVIDIA verifies that a cuda workload can be run. // //nolint:gocyclo func (suite *ExtensionsSuiteNVIDIA) TestExtensionsNVIDIA() { expectedModulesModDep := map[string]string{ "nvidia": "nvidia.ko", "nvidia_uvm": "nvidia-uvm.ko", "nvidia_drm": "nvidia-drm.ko", "nvidia_modeset": "nvidia-modeset.ko", } // if we're testing NVIDIA stuff we need to get the nodes having NVIDIA GPUs // we query k8s to get the nodes having the label node.kubernetes.io/instance-type. // this label is set by the cloud provider and it's value is the instance type. // the nvidia e2e-aws tests creates gpu nodes one with g4dn.xlarge and another // with p4d.24xlarge for _, nvidiaNode := range suite.getNVIDIANodes("node.kubernetes.io/instance-type in (g4dn.xlarge, p4d.24xlarge)") { suite.AssertExpectedModules(suite.ctx, nvidiaNode, expectedModulesModDep) } nodes := suite.getNVIDIANodes("node.kubernetes.io/instance-type=g4dn.xlarge") for _, node := range nodes { suite.AssertServicesRunning(suite.ctx, node, map[string]string{ "ext-nvidia-persistenced": "Running", "ext-nvidia-cdi-gen": "Finished", }) } // nodes = suite.getNVIDIANodes("node.kubernetes.io/instance-type=p4d.24xlarge") // for _, node := range nodes { // suite.testServicesRunning(node, map[string]string{ // "ext-nvidia-persistenced": "Running", // "ext-nvidia-fabricmanager": "Running", // }) // } _, err := suite.Clientset.CoreV1().Namespaces().Create(suite.ctx, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: "gpu-operator", Labels: map[string]string{ "pod-security.kubernetes.io/enforce": "privileged", }, }, }, metav1.CreateOptions{}) defer suite.Clientset.CoreV1().Namespaces().Delete(suite.ctx, "gpu-operator", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) suite.Require().NoError(suite.HelmInstall( suite.ctx, "gpu-operator", "https://helm.ngc.nvidia.com/nvidia", NvidiaGPUOperatorChartVersion, "gpu-operator", "gpu-operator", nvidiaGPUOperatorHelmChartValues, )) // now we can create a cuda test job _, err = suite.Clientset.BatchV1().Jobs("default").Create(suite.ctx, nvidiaCUDATestJob(), metav1.CreateOptions{}) defer suite.Clientset.BatchV1().Jobs("default").Delete(suite.ctx, "cuda-test", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) // delete all pods with label app.kubernetes.io/name=cuda-test defer func() { podList, listErr := suite.GetPodsWithLabel(suite.ctx, "default", "app.kubernetes.io/name=cuda-test") if listErr != nil { err = listErr } for _, pod := range podList.Items { err = suite.Clientset.CoreV1().Pods("default").Delete(suite.ctx, pod.Name, metav1.DeleteOptions{}) } }() // wait for the pods to be completed suite.Require().NoError(retry.Constant(4*time.Minute, retry.WithUnits(time.Second*10)).Retry( func() error { podList, listErr := suite.GetPodsWithLabel(suite.ctx, "default", "app.kubernetes.io/name=cuda-test") if listErr != nil { return retry.ExpectedErrorf("error getting pod: %s", listErr) } for _, pod := range podList.Items { if pod.Status.Phase == corev1.PodFailed { logData := suite.getPodLogs("default", pod.Name) suite.T().Logf("pod %s logs:\n%s", pod.Name, logData) } } if len(podList.Items) != 1 { return retry.ExpectedErrorf("expected 1 pod, got %d", len(podList.Items)) } for _, pod := range podList.Items { if pod.Status.Phase != corev1.PodSucceeded { return retry.ExpectedErrorf("%s is not completed yet: %s", pod.Name, pod.Status.Phase) } } return nil }, )) // now we can check the logs podList, err := suite.GetPodsWithLabel(suite.ctx, "default", "app.kubernetes.io/name=cuda-test") suite.Require().NoError(err) suite.Require().Len(podList.Items, 1) for _, pod := range podList.Items { logData := suite.getPodLogs("default", pod.Name) suite.Require().Contains(logData, "Test PASSED") } } func (suite *ExtensionsSuiteNVIDIA) getPodLogs(namespace, name string) string { res := suite.Clientset.CoreV1().Pods(namespace).GetLogs(name, &corev1.PodLogOptions{}) stream, err := res.Stream(suite.ctx) suite.Require().NoError(err) defer stream.Close() //nolint:errcheck logData, err := io.ReadAll(stream) suite.Require().NoError(err) return string(logData) } func (suite *ExtensionsSuiteNVIDIA) getNVIDIANodes(labelQuery string) []string { nodes, err := suite.Clientset.CoreV1().Nodes().List(suite.ctx, metav1.ListOptions{ LabelSelector: labelQuery, }) suite.Require().NoError(err) // if we don't have any node with NVIDIA GPUs we fail the test // since we explicitly asked for them suite.Require().NotEmpty(nodes.Items, "no nodes with NVIDIA GPUs matching label selector '%s' found", labelQuery) nodeList := make([]string, len(nodes.Items)) for i, node := range nodes.Items { for _, addr := range node.Status.Addresses { if addr.Type == corev1.NodeInternalIP { nodeList[i] = addr.Address } } } return nodeList } func nvidiaCUDATestJob() *batchv1.Job { return &batchv1.Job{ ObjectMeta: metav1.ObjectMeta{ Name: "cuda-test", }, Spec: batchv1.JobSpec{ Completions: new(int32(1)), Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Name: "cuda-test", Labels: map[string]string{ "app.kubernetes.io/name": "cuda-test", }, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "cuda-test", Image: fmt.Sprintf("nvcr.io/nvidia/k8s/cuda-sample:%s", NvidiaCUDATestImageVersion), }, }, Affinity: &corev1.Affinity{ NodeAffinity: &corev1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &corev1.NodeSelector{ NodeSelectorTerms: []corev1.NodeSelectorTerm{ { MatchExpressions: []corev1.NodeSelectorRequirement{ { Key: "node.kubernetes.io/instance-type", Operator: corev1.NodeSelectorOpIn, Values: []string{"g4dn.xlarge", "p4d.24xlarge"}, }, }, }, }, }, }, }, RestartPolicy: corev1.RestartPolicyNever, RuntimeClassName: new("nvidia"), }, }, }, } } func init() { allSuites = append(allSuites, &ExtensionsSuiteNVIDIA{}) } ================================================ FILE: internal/integration/api/extensions_qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "io" "net" "net/http" "net/url" "os" "path/filepath" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" nodev1 "k8s.io/api/node/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // ExtensionsSuiteQEMU verifies Talos extensions on QEMU. type ExtensionsSuiteQEMU struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ExtensionsSuiteQEMU) SuiteName() string { return "api.ExtensionsSuiteQEMU" } // SetupTest ... func (suite *ExtensionsSuiteQEMU) SetupTest() { if !suite.ExtensionsQEMU { suite.T().Skip("skipping as qemu extensions test are not enabled") } // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *ExtensionsSuiteQEMU) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestExtensionsExpectedPaths verifies expected paths are present. func (suite *ExtensionsSuiteQEMU) TestExtensionsExpectedPaths() { expectedPaths := []string{ "/lib/firmware/amdgpu", "/lib/firmware/amd-ucode", "/lib/firmware/bnx2x", "/lib/firmware/cxgb3", "/lib/firmware/cxgb4/configs", "/lib/firmware/i915", "/lib/firmware/intel/ice/ddp", "/lib/firmware/intel-ucode", } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) ctx := client.WithNode(suite.ctx, node) for _, path := range expectedPaths { stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: path, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { suite.Require().Equal(path, info.Name, "expected %s to exist", path) return nil })) } } // TestExtensionsExpectedModules verifies expected modules are loaded and in modules.dep. func (suite *ExtensionsSuiteQEMU) TestExtensionsExpectedModules() { // expectedModulesModDep is a map of module name to module.dep name expectedModulesModDep := map[string]string{ "asix": "asix.ko", "ax88179_178a": "ax88179_178a.ko", "ax88796b": "ax88796b.ko", "binfmt_misc": "binfmt_misc.ko", "btrfs": "btrfs.ko", "cdc_ether": "cdc_ether.ko", "cdc_mbim": "cdc_mbim.ko", "cdc_ncm": "cdc_ncm.ko", "cdc_subset": "cdc_subset.ko", "cdc_wdm": "cdc-wdm.ko", "cxgb": "cxgb.ko", "cxgb3": "cxgb3.ko", "cxgb4": "cxgb4.ko", "cxgb4vf": "cxgb4vf.ko", "drbd": "drbd.ko", "ena": "ena.ko", "gasket": "gasket.ko", "net1080": "net1080.ko", "option": "option.ko", "qmi_wwan": "qmi_wwan.ko", "r8153_ecm": "r8153_ecm.ko", "thunderbolt": "thunderbolt.ko", "thunderbolt_net": "thunderbolt_net.ko", "usb_wwan": "usb_wwan.ko", "usbnet": "usbnet.ko", "xdma": "xdma.ko", "zaurus": "zaurus.ko", "zfs": "zfs.ko", } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertExpectedModules(suite.ctx, node, expectedModulesModDep) } // TestExtensionsNutClient verifies nut client is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsNutClient() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertServicesRunning(suite.ctx, node, map[string]string{"ext-nut-client": "Running"}) } // TestExtensionsQEMUGuestAgent verifies qemu guest agent is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsQEMUGuestAgent() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertServicesRunning(suite.ctx, node, map[string]string{"ext-qemu-guest-agent": "Running"}) ctx := client.WithNode(suite.ctx, node) hostnameSpec, err := safe.StateWatchFor[*network.HostnameStatus]( ctx, suite.Client.COSI, network.NewHostnameStatus(network.NamespaceName, resource.ID("hostname")).Metadata(), state.WithEventTypes(state.Created, state.Updated), ) suite.Require().NoError(err) clusterStatePath, err := suite.Cluster.StatePath() suite.Require().NoError(err) conn, err := (&net.Dialer{}).DialContext(ctx, "unix", filepath.Join(clusterStatePath, hostnameSpec.TypedSpec().Hostname+".sock")) suite.Require().NoError(err) defer conn.Close() //nolint:errcheck // now we want to reboot the node using the guest agent suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { _, err = conn.Write([]byte(`{"execute":"guest-shutdown", "arguments": {"mode": "reboot"}}`)) return err }, 5*time.Minute, suite.CleanupFailedPods, ) } // TestExtensionsTailscale verifies tailscale is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsTailscale() { // Tailscale service keeps on restarting unless authed, so this test is disabled for now. if ok := os.Getenv("TALOS_INTEGRATION_RUN_TAILSCALE"); ok == "" { suite.T().Skip("skipping as tailscale integration tests are not enabled") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertServicesRunning(suite.ctx, node, map[string]string{"ext-tailscale": "Running"}) ctx := client.WithNode(suite.ctx, node) linkSpec, err := safe.StateWatchFor[*network.LinkStatus]( ctx, suite.Client.COSI, network.NewHostnameStatus(network.NamespaceName, resource.ID("tailscale0")).Metadata(), state.WithEventTypes(state.Created, state.Updated), ) suite.Require().NoError(err) suite.Require().Equal("tun", linkSpec.TypedSpec().Kind) } // TestExtensionsHelloWorldService verifies hello world service is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsHelloWorldService() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertServicesRunning(suite.ctx, node, map[string]string{ "ext-hello-world": "Running", }) url := url.URL{ Scheme: "http", Host: node, } resp, err := http.Get(url.String()) //nolint:noctx suite.Require().NoError(err) defer resp.Body.Close() //nolint:errcheck respBody, err := io.ReadAll(resp.Body) suite.Require().NoError(err) suite.Require().Equal("Hello from Talos Linux Extension Service!", string(respBody)) } // TestExtensionsGvisor verifies gvisor runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsGvisor() { suite.testRuntimeClass("gvisor", "runsc") } // TestExtensionsGvisorKVM verifies gvisor runtime class with kvm platform is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsGvisorKVM() { suite.testRuntimeClass("gvisor-kvm", "runsc-kvm") } // TestExtensionsCrun verifies crun runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsCrun() { suite.testRuntimeClass("crun", "crun") } // TestExtensionsKataContainers verifies gvisor runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsKataContainers() { suite.testRuntimeClass("kata", "kata") } // TestExtensionsYouki verifies youki runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsYouki() { suite.testRuntimeClass("youki", "youki") } func (suite *ExtensionsSuiteQEMU) testRuntimeClass(runtimeClassName, handlerName string) { testName := "nginx-" + runtimeClassName _, err := suite.Clientset.NodeV1().RuntimeClasses().Create(suite.ctx, &nodev1.RuntimeClass{ ObjectMeta: metav1.ObjectMeta{ Name: runtimeClassName, }, Handler: handlerName, }, metav1.CreateOptions{}) if apierrors.IsAlreadyExists(err) { // ignore if the runtime class already exists err = nil } suite.Require().NoError(err) defer suite.Clientset.NodeV1().RuntimeClasses().Delete(suite.ctx, runtimeClassName, metav1.DeleteOptions{}) //nolint:errcheck _, err = suite.Clientset.CoreV1().Pods("default").Create(suite.ctx, &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: testName, }, Spec: corev1.PodSpec{ RuntimeClassName: new(runtimeClassName), Containers: []corev1.Container{ { Name: testName, Image: "nginx", }, }, }, }, metav1.CreateOptions{}) suite.Require().NoError(err) defer suite.Clientset.CoreV1().Pods("default").Delete(suite.ctx, testName, metav1.DeleteOptions{}) //nolint:errcheck // wait for the pod to be ready suite.Require().NoError(suite.WaitForPodToBeRunning(suite.ctx, 5*time.Minute, "default", testName)) } // TestExtensionsStargz verifies stargz snapshotter. func (suite *ExtensionsSuiteQEMU) TestExtensionsStargz() { _, err := suite.Clientset.CoreV1().Pods("default").Create(suite.ctx, &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "stargz-hello", }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "stargz-hello", Image: "ghcr.io/stargz-containers/alpine:3.15.3-esgz", Args: []string{"sleep", "inf"}, }, }, }, }, metav1.CreateOptions{}) defer suite.Clientset.CoreV1().Pods("default").Delete(suite.ctx, "stargz-hello", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) // wait for the pod to be ready suite.Require().NoError(suite.WaitForPodToBeRunning(suite.ctx, 5*time.Minute, "default", "stargz-hello")) } // TestExtensionsMdADM verifies mdadm is working, udev rules work and the raid is mounted on reboot. func (suite *ExtensionsSuiteQEMU) TestExtensionsMdADM() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) userDisks := suite.UserDisks(suite.ctx, node) suite.Require().GreaterOrEqual(len(userDisks), 2, "expected at least two user disks to be available") userDisksJoined := strings.Join(userDisks[:2], " ") mdAdmCreatePodDef, err := suite.NewPrivilegedPod("mdadm-create") suite.Require().NoError(err) suite.Require().NoError(mdAdmCreatePodDef.Create(suite.ctx, 5*time.Minute)) defer mdAdmCreatePodDef.Delete(suite.ctx) //nolint:errcheck stdout, _, err := mdAdmCreatePodDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mdadm --create /dev/md/testmd --raid-devices=2 --metadata=1.2 --level=1 %s", userDisksJoined), ) suite.Require().NoError(err) suite.Require().Contains(stdout, "mdadm: array /dev/md/testmd started.") defer func() { hostNameStatus, err := safe.StateGetByID[*network.HostnameStatus](client.WithNode(suite.ctx, node), suite.Client.COSI, "hostname") suite.Require().NoError(err) hostname := hostNameStatus.TypedSpec().Hostname deletePodDef, err := suite.NewPrivilegedPod("mdadm-destroy") suite.Require().NoError(err) suite.Require().NoError(deletePodDef.Create(suite.ctx, 5*time.Minute)) defer deletePodDef.Delete(suite.ctx) //nolint:errcheck if _, _, err := deletePodDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mdadm --wait --stop /dev/md/%s:testmd", hostname), ); err != nil { suite.T().Logf("failed to stop mdadm array: %v", err) } if _, _, err := deletePodDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mdadm --zero-superblock %s", userDisksJoined), ); err != nil { suite.T().Logf("failed to remove md array backed by volumes %s: %v", userDisksJoined, err) } }() // now we want to reboot the node and make sure the array is still mounted suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 5*time.Minute, suite.CleanupFailedPods, ) suite.Require().True(suite.mdADMArrayExists(), "expected mdadm array to be present") } func (suite *ExtensionsSuiteQEMU) mdADMArrayExists() bool { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) ctx := client.WithNode(suite.ctx, node) disks, err := safe.StateListAll[*block.Disk](ctx, suite.Client.COSI) suite.Require().NoError(err) for disk := range disks.All() { if strings.HasPrefix(disk.TypedSpec().DevPath, "/dev/md") { return true } } return false } // TestExtensionsZFS verifies zfs is working, udev rules work and the pool is mounted on reboot. func (suite *ExtensionsSuiteQEMU) TestExtensionsZFS() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.AssertServicesRunning(suite.ctx, node, map[string]string{"ext-zfs-service": "Running"}) userDisks := suite.UserDisks(suite.ctx, node) suite.Require().NotEmpty(userDisks, "expected at least one user disks to be available") zfsPodDef, err := suite.NewPrivilegedPod("zpool-create") suite.Require().NoError(err) suite.Require().NoError(zfsPodDef.Create(suite.ctx, 5*time.Minute)) defer zfsPodDef.Delete(suite.ctx) //nolint:errcheck stdout, stderr, err := zfsPodDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- zpool create -m /var/tank tank %s", userDisks[0]), ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Equal("", stdout) stdout, stderr, err = zfsPodDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- zfs create -V 1gb tank/vol", ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Equal("", stdout) defer func() { deletePodDef, err := suite.NewPrivilegedPod("zpool-destroy") suite.Require().NoError(err) suite.Require().NoError(deletePodDef.Create(suite.ctx, 5*time.Minute)) defer deletePodDef.Delete(suite.ctx) //nolint:errcheck if _, _, err := deletePodDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- zfs destroy tank/vol", ); err != nil { suite.T().Logf("failed to remove zfs dataset tank/vol: %v", err) } if _, _, err := deletePodDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- zpool destroy tank", ); err != nil { suite.T().Logf("failed to remove zpool tank: %v", err) } }() suite.Require().True(suite.checkZFSPoolMounted(), "expected zfs pool to be mounted") // now we want to reboot the node and make sure the pool is still mounted suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 5*time.Minute, suite.CleanupFailedPods, ) suite.Require().True(suite.checkZFSPoolMounted(), "expected zfs pool to be mounted") } func (suite *ExtensionsSuiteQEMU) checkZFSPoolMounted() bool { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) ctx := client.WithNode(suite.ctx, node) stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: "/dev/zvol/tank/vol", Types: []machineapi.ListRequest_Type{machineapi.ListRequest_REGULAR}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { suite.Require().Equal("/dev/zvol/tank/vol", info.Name, "expected /dev/zvol/tank/vol to exist") suite.Require().Equal("zd0", info.Link, "expected /dev/zvol/tank/vol to be linked to zd0") return nil })) disks, err := safe.StateListAll[*block.Disk](ctx, suite.Client.COSI) suite.Require().NoError(err) for disk := range disks.All() { if strings.HasPrefix(disk.TypedSpec().DevPath, "/dev/zd") { return true } } return false } // TestExtensionsUtilLinuxTools verifies util-linux-tools are working. func (suite *ExtensionsSuiteQEMU) TestExtensionsUtilLinuxTools() { utilLinuxPodDef, err := suite.NewPrivilegedPod("util-linux-tools-test") suite.Require().NoError(err) suite.Require().NoError(utilLinuxPodDef.Create(suite.ctx, 5*time.Minute)) defer utilLinuxPodDef.Delete(suite.ctx) //nolint:errcheck stdout, stderr, err := utilLinuxPodDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- /usr/local/sbin/fstrim --version", ) suite.Require().NoError(err) suite.Require().Equal("", stderr) suite.Require().Contains(stdout, "fstrim from util-linux") } // TestExtensionsWasmEdge verifies wasmedge runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsWasmEdge() { _, err := suite.Clientset.NodeV1().RuntimeClasses().Create(suite.ctx, &nodev1.RuntimeClass{ ObjectMeta: metav1.ObjectMeta{ Name: "wasmedge", }, Handler: "wasmedge", }, metav1.CreateOptions{}) defer suite.Clientset.NodeV1().RuntimeClasses().Delete(suite.ctx, "wasmedge", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) _, err = suite.Clientset.CoreV1().Pods("default").Create(suite.ctx, &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "wasmedge-test", }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "wasmedge-test", Image: "wasmedge/example-wasi:latest", }, }, }, }, metav1.CreateOptions{}) defer suite.Clientset.CoreV1().Pods("default").Delete(suite.ctx, "wasmedge-test", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) // wait for the pod to be ready suite.Require().NoError(suite.WaitForPodToBeRunning(suite.ctx, 5*time.Minute, "default", "wasmedge-test")) } // TestExtensionsSpin verifies spin runtime class is working. func (suite *ExtensionsSuiteQEMU) TestExtensionsSpin() { _, err := suite.Clientset.NodeV1().RuntimeClasses().Create(suite.ctx, &nodev1.RuntimeClass{ ObjectMeta: metav1.ObjectMeta{ Name: "wasmtime-spin-v2", }, Handler: "spin", }, metav1.CreateOptions{}) defer suite.Clientset.NodeV1().RuntimeClasses().Delete(suite.ctx, "wasmtime-spin-v2", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) _, err = suite.Clientset.CoreV1().Pods("default").Create(suite.ctx, &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "spin-test", }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "spin-test", Image: "ghcr.io/spinkube/containerd-shim-spin/examples/spin-rust-hello", Command: []string{"/"}, }, }, RuntimeClassName: new("wasmtime-spin-v2"), }, }, metav1.CreateOptions{}) defer suite.Clientset.CoreV1().Pods("default").Delete(suite.ctx, "spin-test", metav1.DeleteOptions{}) //nolint:errcheck suite.Require().NoError(err) // wait for the pod to be ready suite.Require().NoError(suite.WaitForPodToBeRunning(suite.ctx, 5*time.Minute, "default", "spin-test")) } // TestLoadedKernelModule tests the /proc/modules resource. func (suite *ExtensionsSuiteQEMU) TestLoadedKernelModule() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{ "virtio_balloon", "virtio_pci", "virtio_pci_legacy_dev", "virtio_pci_modern_dev", }, func(res *runtime.LoadedKernelModule, asrt *assert.Assertions) { asrt.NotEmpty(res.TypedSpec().Size, "kernel module size should not be empty") asrt.NotEmpty(res.TypedSpec().Address, "kernel module address should not be empty") asrt.GreaterOrEqual(res.TypedSpec().ReferenceCount, 0, "kernel module instances should be non-negative") }, ) } func init() { allSuites = append(allSuites, &ExtensionsSuiteQEMU{}) } ================================================ FILE: internal/integration/api/firewall.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "crypto/tls" _ "embed" "errors" "fmt" "net" "net/http" "os" "strconv" "strings" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/hashicorp/go-cleanhttp" "golang.org/x/sync/errgroup" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // FirewallSuite ... type FirewallSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *FirewallSuite) SuiteName() string { return "api.FirewallSuite" } // SetupTest ... func (suite *FirewallSuite) SetupTest() { if suite.Cluster == nil { suite.T().Skip("without full cluster state can't guarantee availability of kubelet IPs") } // make sure we abort at some point in time, but give enough room for Resets suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) } // TearDownTest ... func (suite *FirewallSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestKubeletAccess verifies that without firewall kubelet API is available, and not available otherwise. func (suite *FirewallSuite) TestKubeletAccess() { allNodes := suite.DiscoverNodeInternalIPs(suite.ctx) _, err := safe.StateGetByID[*network.NfTablesChain](client.WithNode(suite.ctx, allNodes[0]), suite.Client.COSI, "ingress") firewallEnabled := err == nil eg, ctx := errgroup.WithContext(suite.ctx) transport := cleanhttp.DefaultTransport() transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := http.Client{ Transport: transport, } for _, node := range allNodes { eg.Go(func() error { attemptCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() req, err := http.NewRequestWithContext( attemptCtx, http.MethodGet, fmt.Sprintf("https://%s/healthz", net.JoinHostPort(node, strconv.Itoa(constants.KubeletPort))), nil, ) if err != nil { return err } resp, err := client.Do(req) if resp != nil { resp.Body.Close() //nolint:errcheck } if firewallEnabled { if err == nil { return errors.New("kubelet API should not be available") } if !errors.Is(err, os.ErrDeadlineExceeded) && !errors.Is(err, context.DeadlineExceeded) { return fmt.Errorf("unexpected error: %w", err) } } else if err != nil { return fmt.Errorf("kubelet API should be available: %w", err) } return nil }) } suite.Require().NoError(eg.Wait()) } //go:embed testdata/nodeport.yaml var nodePortServiceYAML []byte // TestNodePortAccess verifies that without firewall NodePort is available, and not available otherwise. // //nolint:gocyclo func (suite *FirewallSuite) TestNodePortAccess() { allNodes := suite.DiscoverNodeInternalIPs(suite.ctx) chain, err := safe.StateGetByID[*network.NfTablesChain](client.WithNode(suite.ctx, allNodes[0]), suite.Client.COSI, "ingress") firewallEnabled := err == nil firewallDefaultBlock := firewallEnabled && chain.TypedSpec().Policy == nethelpers.VerdictDrop // our blocking only works with kube-proxy, so we need to make sure it's running out, err := suite.Clientset.CoreV1().Pods("kube-system").List(suite.ctx, metav1.ListOptions{LabelSelector: "k8s-app=kube-proxy"}) suite.Require().NoError(err) if len(out.Items) == 0 { suite.T().Skip("kube-proxy not running") } // create a deployment with a NodePort service localPathStorage := suite.ParseManifests(nodePortServiceYAML) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, localPathStorage) }) suite.ApplyManifests(suite.ctx, localPathStorage) // fetch the NodePort service // read back Service to figure out the ports svc, err := suite.Clientset.CoreV1().Services("default").Get(suite.ctx, "test-nginx", metav1.GetOptions{}) suite.Require().NoError(err) var nodePort int for _, portSpec := range svc.Spec.Ports { nodePort = int(portSpec.NodePort) } suite.Require().NotZero(nodePort) suite.T().Log("sleeping for 5 seconds to allow kube-proxy to update nftables") time.Sleep(5 * time.Second) eg, ctx := errgroup.WithContext(suite.ctx) for _, node := range allNodes { eg.Go(func() error { attemptCtx, cancel := context.WithTimeout(ctx, time.Second) defer cancel() var d net.Dialer conn, err := d.DialContext(attemptCtx, "tcp", net.JoinHostPort(node, strconv.Itoa(nodePort))) if conn != nil { conn.Close() //nolint:errcheck } if firewallDefaultBlock { if err == nil { return errors.New("nodePort API should not be available") } if !errors.Is(err, os.ErrDeadlineExceeded) && !errors.Is(err, context.DeadlineExceeded) { return fmt.Errorf("unexpected error: %w", err) } } else if err != nil { // ignore connection refused, as it's not firewall, but rather service proxy not ready yet if !strings.Contains(err.Error(), "connection refused") { return fmt.Errorf("nodePort API should be available: %w", err) } } return nil }) } suite.Require().NoError(eg.Wait()) } func init() { allSuites = append(allSuites, new(FirewallSuite)) } ================================================ FILE: internal/integration/api/hardware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/google/uuid" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // HardwareSuite ... type HardwareSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *HardwareSuite) SuiteName() string { return "api.HardwareSuite" } // SetupTest ... func (suite *HardwareSuite) SetupTest() { if !suite.Capabilities().RunsTalosKernel { suite.T().Skipf("doesn't run Talos kernel, skipping") } suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) } // TearDownTest ... func (suite *HardwareSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestSystemInformation tests that SystemInformation is populated. func (suite *HardwareSuite) TestSystemInformation() { node := suite.RandomDiscoveredNodeInternalIP() sysInfo, err := safe.StateGetByID[*hardware.SystemInformation](client.WithNode(suite.ctx, node), suite.Client.COSI, hardware.SystemInformationID) suite.Require().NoError(err) suite.Assert().NotEmpty(sysInfo.TypedSpec().UUID) suite.Assert().NotEqual((uuid.UUID{}).String(), sysInfo.TypedSpec().UUID) } // TestHardwareInfo tests that hardware info is populated. func (suite *HardwareSuite) TestHardwareInfo() { node := suite.RandomDiscoveredNodeInternalIP() resourceList := []resource.Type{ hardware.MemoryModuleType, hardware.ProcessorType, } if suite.Cluster != nil { // cloud VMs might not publish PCI devices resourceList = append(resourceList, hardware.PCIDeviceType) } for _, resourceType := range resourceList { items, err := suite.Client.COSI.List(client.WithNode(suite.ctx, node), resource.NewMetadata(hardware.NamespaceName, resourceType, "", resource.VersionUndefined)) suite.Require().NoError(err) suite.Assert().NotEmpty(items.Items, "resource type %s is not populated", resourceType) } } // TestPCRStatus tests that the PCR was correctly extended. func (suite *HardwareSuite) TestPCRStatus() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertNoResource[*hardware.PCRStatus](ctx, suite.T(), suite.Client.COSI, hardware.NewPCCRStatus(constants.UKIPCR).Metadata().ID()) } func init() { allSuites = append(allSuites, new(HardwareSuite)) } ================================================ FILE: internal/integration/api/images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" _ "embed" "errors" "io" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/constants" securityres "github.com/siderolabs/talos/pkg/machinery/resources/security" ) // ImagesSuite ... type ImagesSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ImagesSuite) SuiteName() string { return "api.ImagesSuite" } // SetupTest ... func (suite *ImagesSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) } // TearDownTest ... func (suite *ImagesSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestList tests ImageService.List(). func (suite *ImagesSuite) TestList() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) rcv, err := suite.Client.ImageClient.List(ctx, &machine.ImageServiceListRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, }) suite.Require().NoError(err) var imageNames []string for { msg, err := rcv.Recv() if err != nil { if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } imageNames = append(imageNames, msg.GetName()) } suite.Require().NotEmpty(imageNames, "expected to receive at least one image from List()") for _, name := range imageNames { if strings.Contains(name, "registry.k8s.io/pause") { return } } suite.Fail("expected to find pause image in the list") } // TestPull tests ImageService.Pull(). func (suite *ImagesSuite) TestPull() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) const ( image = "registry.k8s.io/kube-apiserver:v1.27.1" digestedImage = "registry.k8s.io/kube-apiserver@sha256:a6daed8429c54f0008910fc4ecc17aefa1dfcd7cc2ff0089570854d4f95213ed" ) rcv, err := suite.Client.ImageClient.Pull(ctx, &machine.ImageServicePullRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, ImageRef: image, }) suite.Require().NoError(err) var pulledImage string for { msg, err := rcv.Recv() if err != nil { if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } // ignore progress messages, but the last message should contain the image name pulledImage = msg.GetName() } suite.Require().NotEmpty(pulledImage, "expected pulled image name in the response") // depending on whether the image verification is enabled or not, the pulled image ref can be either the original one (without digest) or the digested one, so we should accept both suite.Assert().Contains([]string{digestedImage, image}, pulledImage, "pulled image name should match requested image") } //go:embed testdata/pause.tar var pauseImageTar []byte // TestImportRemove tests ImageService.Import() and ImageService.Remove(). func (suite *ImagesSuite) TestImportRemove() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) // we can only import the image if it matches Talos architecture versionResp, err := suite.Client.Version(ctx) suite.Require().NoError(err) suite.Require().Len(versionResp.GetMessages(), 1) arch := versionResp.GetMessages()[0].GetVersion().GetArch() if arch != "amd64" { suite.T().Skipf("skipping import test on unsupported architecture %q", arch) } rcv, err := suite.Client.ImageClient.Import(ctx) suite.Require().NoError(err) suite.Require().NoError(rcv.Send(&machine.ImageServiceImportRequest{ Request: &machine.ImageServiceImportRequest_Containerd{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, }, })) const chunkSize = 4 * 1024 for offset := 0; offset < len(pauseImageTar); offset += chunkSize { end := min(offset+chunkSize, len(pauseImageTar)) suite.Require().NoError(rcv.Send(&machine.ImageServiceImportRequest{ Request: &machine.ImageServiceImportRequest_ImageChunk{ ImageChunk: &common.Data{ Bytes: pauseImageTar[offset:end], }, }, })) } suite.Require().NoError(rcv.CloseSend()) resp, err := rcv.CloseAndRecv() suite.Require().NoError(err) suite.Require().NotEmpty(resp.GetName(), "expected imported image name in the response") suite.Assert().Equal("registry.k8s.io/pause:i-was-a-digest", resp.GetName(), "imported image name should match expected") // now remove the imported image _, err = suite.Client.ImageClient.Remove(ctx, &machine.ImageServiceRemoveRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, ImageRef: resp.GetName(), }) suite.Require().NoError(err) // try once again _, err = suite.Client.ImageClient.Remove(ctx, &machine.ImageServiceRemoveRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_CRI, }, ImageRef: resp.GetName(), }) suite.Require().Error(err) suite.Assert().Equal(status.Code(err), codes.NotFound) } // TestVerify tests ImageService.Verify(). // //nolint:gocyclo func (suite *ImagesSuite) TestVerify() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) // query the current image verification config to restore it after the test cfg, err := suite.ReadConfigFromNode(ctx) suite.Require().NoError(err) originalConfig := xslices.Filter(cfg.Documents(), func(doc config.Document) bool { return doc.Kind() == security.ImageVerificationConfigKind }) if len(originalConfig) == 0 { // if the image verification hasn't been enabled, skip the test suite.T().Skip("skipping image verification test since no image verification config is present in the cluster") } // remove any existing image verification config to start with a clean slate suite.RemoveMachineConfigDocuments(ctx, security.ImageVerificationConfigKind) // our custom image verification config for this test imageVerificationConfig := security.NewImageVerificationConfigV1Alpha1() imageVerificationConfig.ConfigRules = security.ImageVerificationRules{ { RuleImagePattern: "do.not.verify/*", RuleSkip: new(true), }, { RuleImagePattern: "registry.k8s.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubject: "krel-trust@k8s-releng-prod.iam.gserviceaccount.com", }, }, { RuleImagePattern: "localhost:4444/*", RuleDeny: new(true), }, } suite.PatchMachineConfig(ctx, imageVerificationConfig) // wait for the configuration to be applied rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{"0000", "0001", "0002"}, func(rule *securityres.ImageVerificationRule, asrt *assert.Assertions) { switch rule.Metadata().ID() { case "0000": asrt.Equal("do.not.verify/*", rule.TypedSpec().ImagePattern) case "0001": asrt.Equal("registry.k8s.io/*", rule.TypedSpec().ImagePattern) case "0002": asrt.Equal("localhost:4444/*", rule.TypedSpec().ImagePattern) } }, ) const etcdImage = constants.EtcdImage + ":" + constants.DefaultEtcdVersion // run the tests, first with an etcd image, which should be in the image cache anyways resp, err := suite.Client.ImageClient.Verify(ctx, &machine.ImageServiceVerifyRequest{ ImageRef: etcdImage, // this image is under registry.k8s.io }) suite.Require().NoError(err) suite.Assert().True(resp.GetVerified(), "expected image to be verified according to our config") suite.Assert().Equal("verified via legacy signature (bundle verified true)", resp.GetMessage()) suite.Assert().Contains(resp.GetDigestedImageRef(), constants.EtcdImage) suite.Assert().Contains(resp.GetDigestedImageRef(), "@sha256:") // now test with an image that should be skipped resp, err = suite.Client.ImageClient.Verify(ctx, &machine.ImageServiceVerifyRequest{ ImageRef: "do.not.verify/myimage:latest", }) suite.Require().NoError(err) suite.Assert().False(resp.GetVerified(), "expected image verification to be skipped according to our config") suite.Assert().Equal("verification skipped by matched rule (0000)", resp.GetMessage()) // finally test with an image that should be denied _, err = suite.Client.ImageClient.Verify(ctx, &machine.ImageServiceVerifyRequest{ ImageRef: "localhost:4444/myimage:latest", }) suite.Require().Error(err) suite.Assert().Equal(codes.PermissionDenied, status.Code(err), "expected image verification to be denied according to our config") suite.Assert().Equal("verification denied by matched rule (0002)", status.Convert(err).Message()) // now test via the image pull flow rcv, err := suite.Client.ImageClient.Pull(ctx, &machine.ImageServicePullRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, ImageRef: etcdImage, }) suite.Require().NoError(err) for { _, err = rcv.Recv() if err != nil { if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } } listResp, err := suite.Client.ImageClient.List(ctx, &machine.ImageServiceListRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CRI, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, }) suite.Require().NoError(err) var found bool for { msg, err := listResp.Recv() if err != nil { if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } if msg.GetName() == etcdImage { found = true suite.Assert().Contains(msg.GetLabels(), constants.ImageLabelVerified) } } suite.Assert().True(found, "expected to find the pulled image in the list response") // remove our config suite.RemoveMachineConfigDocuments(ctx, security.ImageVerificationConfigKind) if len(originalConfig) > 0 { // put back original image verification config suite.PatchMachineConfig(ctx, xslices.Map(originalConfig, func(doc config.Document) any { return doc })...) } } func init() { allSuites = append(allSuites, new(ImagesSuite)) } ================================================ FILE: internal/integration/api/kernel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // KernelSuite ... type KernelSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *KernelSuite) SuiteName() string { return "api.KernelSuite" } // SetupTest ... func (suite *KernelSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 10*time.Second) if !suite.Capabilities().RunsTalosKernel { suite.T().Skip("skipping kernel test since Talos kernel is not running") } } // TearDownTest ... func (suite *KernelSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestCmdline tests the /proc/cmdline resource. func (suite *KernelSuite) TestCmdline() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("using node %s", node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{runtime.KernelCmdlineID}, func(res *runtime.KernelCmdline, asrt *assert.Assertions) { asrt.NotEmpty(res.TypedSpec().Cmdline, "kernel cmdline should not be empty") }, ) } func init() { allSuites = append(allSuites, new(KernelSuite)) } ================================================ FILE: internal/integration/api/logs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bufio" "bytes" "context" "fmt" "io" "path/filepath" "strings" "time" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // LogsSuite verifies Logs API. type LogsSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc nodeCtx context.Context //nolint:containedctx } // SuiteName ... func (suite *LogsSuite) SuiteName() string { return "api.LogsSuite" } // SetupTest ... func (suite *LogsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) suite.nodeCtx = client.WithNodes(suite.ctx, suite.RandomDiscoveredNodeInternalIP()) } // TearDownTest ... func (suite *LogsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestServicesHaveLogs verifies that each service has logs. func (suite *LogsSuite) TestServicesHaveLogs() { servicesReply, err := suite.Client.ServiceList(suite.nodeCtx) suite.Require().NoError(err) suite.Require().Len(servicesReply.Messages, 1) logsSize := int64(0) for _, svc := range servicesReply.Messages[0].Services { logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, svc.Id, false, -1, ) suite.Require().NoError(err) logReader, err := client.ReadStream(logsStream) suite.Require().NoError(err) n, err := io.Copy(io.Discard, logReader) suite.Require().NoError(err) logsSize += n } // overall logs shouldn't be empty suite.Require().Greater(logsSize, int64(1024)) } // TestAuditdLogs verifies that auditd logs are present. func (suite *LogsSuite) TestAuditdLogs() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skip auditd logs test for non-QEMU clusters") } logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "auditd", false, -1, ) suite.Require().NoError(err) logReader, err := client.ReadStream(logsStream) suite.Require().NoError(err) n, err := io.Copy(io.Discard, logReader) suite.Require().NoError(err) // auditd logs shouldn't be empty suite.Require().Greater(n, int64(1024)) } // TestKernelLogs verifies that kernel logs are present and valid. func (suite *LogsSuite) TestKernelLogs() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skip kernel logs test for non-QEMU clusters") } logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "kernel", false, -1, ) suite.Require().NoError(err) logReader, err := client.ReadStream(logsStream) suite.Require().NoError(err) var buf bytes.Buffer n, err := io.Copy(&buf, logReader) suite.Require().NoError(err) // kernel logs shouldn't be empty suite.Require().Greater(n, int64(1024)) version := strings.Contains(buf.String(), fmt.Sprintf("Linux version %s", constants.DefaultKernelVersion)) overflow := n >= logging.DesiredCapacity/2 if !version && overflow { suite.T().Skip("skipping the test as kernel log buffer might have overflowed") } suite.Require().Truef( version, "did not find kernel version (%s) in the log buffer", constants.DefaultKernelVersion, ) } // TestTail verifies that log tail might be requested. func (suite *LogsSuite) TestTail() { // invoke machined enough times to generate // some logs for range 20 { _, err := suite.Client.Version(suite.nodeCtx) suite.Require().NoError(err) } for _, tailLines := range []int32{0, 1, 10} { logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "apid", false, tailLines, ) suite.Require().NoError(err) logReader, err := client.ReadStream(logsStream) suite.Require().NoError(err) scanner := bufio.NewScanner(logReader) lines := 0 for scanner.Scan() { lines++ } suite.Require().NoError(scanner.Err()) suite.Assert().EqualValues(tailLines, lines) } } // TODO: TestContainersHaveLogs (CRI, containerd) // TestServiceNotFound verifies error if service name is not found. func (suite *LogsSuite) TestServiceNotFound() { logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "nosuchservice", false, -1, ) suite.Require().NoError(err) suite.Require().NoError(logsStream.CloseSend()) msg, err := logsStream.Recv() suite.Require().NoError(err) suite.Require().Regexp(`.+log "nosuchservice" was not registered$`, msg.Metadata.Error) } // TestStreaming verifies that logs are streamed in real-time. func (suite *LogsSuite) TestStreaming() { suite.testStreaming(-1) } // TestTailStreaming3 verifies tail + streaming. func (suite *LogsSuite) TestTailStreaming3() { suite.testStreaming(3) } // TestTailStreaming0 verifies tail + streaming. func (suite *LogsSuite) TestTailStreaming0() { suite.testStreaming(0) } //nolint:gocyclo func (suite *LogsSuite) testStreaming(tailLines int32) { if tailLines >= 0 { // invoke machined enough times to generate // some logs for range tailLines { _, err := suite.Client.Stats( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, ) suite.Require().NoError(err) } } logsStream, err := suite.Client.Logs( suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "machined", true, tailLines, ) suite.Require().NoError(err) suite.Require().NoError(logsStream.CloseSend()) respCh := make(chan *common.Data) errCh := make(chan error, 1) go func() { defer close(respCh) for { msg, e := logsStream.Recv() if e != nil { errCh <- e return } respCh <- msg } }() defer func() { suite.ctxCancel() // drain respCh for range respCh { //nolint:revive } }() linesDrained := 0 // first, drain the stream until flow stops DrainLoop: for { select { case msg, ok := <-respCh: suite.Require().True(ok) suite.Assert().NotEmpty(msg.Bytes) linesDrained += bytes.Count(msg.Bytes, []byte{'\n'}) case <-time.After(200 * time.Millisecond): break DrainLoop } } suite.Assert().GreaterOrEqual(int32(linesDrained), tailLines) // invoke machined API _, err = suite.Client.Stats(suite.nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD) suite.Require().NoError(err) // there should be a line in the logs select { case msg, ok := <-respCh: suite.Require().True(ok) suite.Assert().NotEmpty(msg.Bytes) case <-time.After(200 * time.Millisecond): suite.Assert().Fail("no log message received") } select { case err = <-errCh: suite.Require().NoError(err) default: } } // TestPersistent confirms there are persistent logs stored in /var/log. func (suite *LogsSuite) TestPersistent() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) stream, err := suite.Client.MachineClient.List(ctx, &machineapi.ListRequest{Root: constants.LogMountPoint}) suite.Require().NoError(err) sizes := map[string]int64{} for { var info *machineapi.FileInfo info, err = stream.Recv() if err != nil { if err == io.EOF || client.StatusCode(err) == codes.Canceled { break } suite.Require().NoError(err) } sizes[filepath.Base(info.Name)] = info.Size } for _, name := range []string{ "machined.log", "controller-runtime.log", "kubelet.log", } { suite.Assert().Contains(sizes, name) rotatedSize, rotated := sizes[name+".1"] suite.Assert().Truef( sizes[name] > 1000 || (rotated && rotatedSize > 10000), "Expected either more than 1000 bytes in the log file or a rotated file for %s", name, ) } } func init() { allSuites = append(allSuites, new(LogsSuite)) } ================================================ FILE: internal/integration/api/machine-status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // MachineStatusSuite ... type MachineStatusSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *MachineStatusSuite) SuiteName() string { return "api.MachineStatusSuite" } // SetupTest ... func (suite *MachineStatusSuite) SetupTest() { // make sure we abort at some point in time, but give enough room for MachineStatuss suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Minute) } // TearDownTest ... func (suite *MachineStatusSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestMachineStatusReady tests that MachineStatus is eventually ready & running. func (suite *MachineStatusSuite) TestMachineStatusReady() { nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, node := range nodes { suite.Assert().NoError(suite.waitMachineStatusReady(node)) } } func (suite *MachineStatusSuite) waitMachineStatusReady(node string) error { ctx, cancel := context.WithTimeout(client.WithNode(suite.ctx, node), 30*time.Second) defer cancel() watchCh := make(chan safe.WrappedStateEvent[*runtime.MachineStatus]) if err := safe.StateWatch( ctx, suite.Client.COSI, resource.NewMetadata(runtime.NamespaceName, runtime.MachineStatusType, runtime.MachineStatusID, resource.VersionUndefined), watchCh, ); err != nil { return err } for { select { case <-ctx.Done(): return fmt.Errorf("%s: timed out waiting for MachineStatus to be ready", node) case event := <-watchCh: machineStatus, err := event.Resource() if err != nil { return err } if machineStatus.TypedSpec().Stage == runtime.MachineStageRunning && machineStatus.TypedSpec().Status.Ready { return nil } suite.T().Logf( "%s: MachineStatus stage %s ready %v, unmetConditions %v", node, machineStatus.TypedSpec().Stage, machineStatus.TypedSpec().Status.Ready, machineStatus.TypedSpec().Status.UnmetConditions, ) } } } func init() { allSuites = append(allSuites, new(MachineStatusSuite)) } ================================================ FILE: internal/integration/api/monitoring.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" ) // MonitoringSuite ... type MonitoringSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *MonitoringSuite) SuiteName() string { return "api.MonitoringSuite" } // SetupTest ... func (suite *MonitoringSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) } // TearDownTest ... func (suite *MonitoringSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestMonitoringAPIs tests that monitoring APIs are working. func (suite *MonitoringSuite) TestMonitoringAPIs() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) _, err := suite.Client.MachineClient.CPUFreqStats(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.CPUInfo(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.DiskStats(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.LoadAvg(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.Memory(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.NetworkDeviceStats(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) _, err = suite.Client.MachineClient.SystemStat(nodeCtx, &emptypb.Empty{}) suite.Require().NoError(err) } func init() { allSuites = append(allSuites, new(MonitoringSuite)) } ================================================ FILE: internal/integration/api/network-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bufio" "context" "fmt" "math/rand/v2" "net/netip" "strconv" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "golang.zx2c4.com/wireguard/wgctrl/wgtypes" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" networkres "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // NetworkConfigSuite ... type NetworkConfigSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *NetworkConfigSuite) SuiteName() string { return "api.NetworkConfigSuite" } // SetupTest ... func (suite *NetworkConfigSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) } // TearDownTest ... func (suite *NetworkConfigSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestStaticHostConfig tests that /etc/hosts updates are working. func (suite *NetworkConfigSuite) TestStaticHostConfig() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) host1 := network.NewStaticHostConfigV1Alpha1("1.2.3.4") host1.Hostnames = []string{"example.com", "example2"} host2 := network.NewStaticHostConfigV1Alpha1("2001:db8::1") host2.Hostnames = []string{"v6"} suite.PatchMachineConfig(nodeCtx, host1, host2) suite.EventuallyWithT( func(collect *assert.CollectT) { asrt := assert.New(collect) hosts := suite.ReadFile(nodeCtx, "/etc/hosts") scanner := bufio.NewScanner(strings.NewReader(hosts)) var found1, found2 bool for scanner.Scan() { line := scanner.Text() switch { case strings.HasPrefix(line, "1.2.3.4"): found1 = true asrt.Contains(line, "example.com", "expected to find hostname in IPv4 entry") asrt.Contains(line, "example2", "expected to find hostname in IPv4 entry") case strings.HasPrefix(line, "2001:db8::1"): found2 = true asrt.Contains(line, "v6", "expected to find hostname in IPv6 entry") } } asrt.True(found1, "expected to find IPv4 entry in /etc/hosts") asrt.True(found2, "expected to find IPv6 entry in /etc/hosts") }, time.Second, time.Millisecond, "waiting for /etc/hosts to be updated", ) suite.RemoveMachineConfigDocuments(nodeCtx, network.StaticHostKind) suite.EventuallyWithT( func(collect *assert.CollectT) { asrt := assert.New(collect) hosts := suite.ReadFile(nodeCtx, "/etc/hosts") asrt.NotContains(hosts, "1.2.3.4", "expected to not find IPv4 entry in /etc/hosts") asrt.NotContains(hosts, "2001:db8::1", "expected to not find IPv6 entry in /etc/hosts") }, time.Second, time.Millisecond, "waiting for /etc/hosts to be updated", ) } // TestDummyLinkConfig tests creation of dummy link interfaces. func (suite *NetworkConfigSuite) TestDummyLinkConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) dummyName := fmt.Sprintf("dummy%d", rand.IntN(10000)) dummy := network.NewDummyLinkConfigV1Alpha1(dummyName) dummy.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} dummy.LinkUp = new(true) dummy.LinkMTU = 9000 dummy.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("fd13:1234::1/64"), AddressPriority: new(uint32(100)), }, } dummy.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{Prefix: netip.MustParsePrefix("fd13:1235::/64")}, RouteGateway: network.Addr{Addr: netip.MustParseAddr("fd13:1234::ffff")}, }, } addressID := dummyName + "/fd13:1234::1/64" routeID := dummyName + "/inet6/fd13:1234::ffff/fd13:1235::/64/1024" addressRouteID := dummyName + "/inet6//fd13:1234::/64/100" suite.PatchMachineConfig(nodeCtx, dummy) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, dummyName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", link.TypedSpec().Kind) asrt.Equal(dummy.HardwareAddressConfig, link.TypedSpec().HardwareAddr) asrt.Equal(dummy.LinkMTU, link.TypedSpec().MTU) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, addressID, func(addr *networkres.AddressStatus, asrt *assert.Assertions) { asrt.Equal(dummyName, addr.TypedSpec().LinkName) }, ) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []resource.ID{routeID, addressRouteID}, func(route *networkres.RouteStatus, asrt *assert.Assertions) { asrt.Equal(dummyName, route.TypedSpec().OutLinkName) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.DummyLinkKind, dummyName) rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, dummyName) rtestutils.AssertNoResource[*networkres.AddressStatus](nodeCtx, suite.T(), suite.Client.COSI, addressID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, addressRouteID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, routeID) } // TestLinkConfig tests configuring physical links. func (suite *NetworkConfigSuite) TestLinkConfig() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping if cluster is not qemu") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) // find the first physical link links, err := safe.ReaderListAll[*networkres.LinkStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) var linkName string for link := range links.All() { if link.TypedSpec().Physical() { linkName = link.Metadata().ID() break } } suite.Require().NotEmpty(linkName, "expected to find at least one physical link") cfg := network.NewLinkConfigV1Alpha1(linkName) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("fd13:1234::2/64"), AddressPriority: new(uint32(2048)), }, } addressID := linkName + "/fd13:1234::2/64" suite.PatchMachineConfig(nodeCtx, cfg) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, addressID, func(addr *networkres.AddressStatus, asrt *assert.Assertions) { asrt.Equal(linkName, addr.TypedSpec().LinkName) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.LinkKind, linkName) rtestutils.AssertNoResource[*networkres.AddressStatus](nodeCtx, suite.T(), suite.Client.COSI, addressID) } // TestLinkAliasConfig tests configuring physical link aliases. func (suite *NetworkConfigSuite) TestLinkAliasConfig() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping if cluster is not qemu") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) // find the first physical link without an alias links, err := safe.ReaderListAll[*networkres.LinkStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) var ( linkName string permanentAddr string ) for link := range links.All() { if link.TypedSpec().Physical() && link.TypedSpec().Alias == "" { linkName = link.Metadata().ID() permanentAddr = link.TypedSpec().PermanentAddr.String() break } } if linkName != "" { // we have unaliased physical link to test with, try aliasing it const aliasName = "test-alias" cfg := network.NewLinkAliasConfigV1Alpha1(aliasName) cfg.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression("mac(link.permanent_addr) == '"+permanentAddr+"'", celenv.LinkLocator())) suite.PatchMachineConfig(nodeCtx, cfg) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal(aliasName, link.TypedSpec().Alias) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.LinkAliasKind, aliasName) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, linkName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Empty(link.TypedSpec().Alias) }, ) } else { suite.T().Log("all physical links are already aliased, verifying existing aliases") // no unaliased physical links, verify that alias worked properly for link := range links.All() { if link.TypedSpec().Physical() && link.TypedSpec().Alias != "" { rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, link.Metadata().ID(), func(linkAlias *networkres.LinkAliasSpec, asrt *assert.Assertions) { asrt.Equal(link.TypedSpec().Alias, linkAlias.TypedSpec().Alias) }, ) } } } } // TestVirtualIPConfig tests configuring virtual IPs. func (suite *NetworkConfigSuite) TestVirtualIPConfig() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping if cluster is not qemu") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) // find the first physical link links, err := safe.ReaderListAll[*networkres.LinkStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) var linkName string for link := range links.All() { if link.TypedSpec().Physical() { linkName = link.Metadata().ID() break } } suite.Require().NotEmpty(linkName, "expected to find at least one physical link") // using link-local address to avoid kube-apiserver picking it up virtualIP := "169.254.100.100" cfg := network.NewLayer2VIPConfigV1Alpha1(virtualIP) cfg.LinkName = linkName addressID := linkName + "/" + virtualIP + "/32" suite.PatchMachineConfig(nodeCtx, cfg) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, addressID, func(addr *networkres.AddressStatus, asrt *assert.Assertions) { asrt.Equal(linkName, addr.TypedSpec().LinkName) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.Layer2VIPKind, virtualIP) rtestutils.AssertNoResource[*networkres.AddressStatus](nodeCtx, suite.T(), suite.Client.COSI, addressID) } // TestVLANConfig tests creation of VLAN interfaces. func (suite *NetworkConfigSuite) TestVLANConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) dummyName := fmt.Sprintf("dummy%d", rand.IntN(10000)) dummy := network.NewDummyLinkConfigV1Alpha1(dummyName) dummy.LinkUp = new(true) dummy.LinkMTU = 9000 vlanName := dummyName + ".v" vlan := network.NewVLANConfigV1Alpha1(vlanName) vlan.VLANIDConfig = 100 vlan.LinkMTU = 2000 vlan.ParentLinkConfig = dummyName vlan.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("fd13:1234::1/64"), AddressPriority: new(uint32(100)), }, } vlan.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{Prefix: netip.MustParsePrefix("fd13:1235::/64")}, RouteGateway: network.Addr{Addr: netip.MustParseAddr("fd13:1234::ffff")}, }, } addressID := vlanName + "/fd13:1234::1/64" routeID := vlanName + "/inet6/fd13:1234::ffff/fd13:1235::/64/1024" addressRouteID := vlanName + "/inet6//fd13:1234::/64/100" suite.PatchMachineConfig(nodeCtx, dummy, vlan) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, dummyName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", link.TypedSpec().Kind) asrt.Equal(dummy.LinkMTU, link.TypedSpec().MTU) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, vlanName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("vlan", link.TypedSpec().Kind) asrt.Equal(vlan.LinkMTU, link.TypedSpec().MTU) asrt.NotZero(link.TypedSpec().LinkIndex) asrt.Equal(vlan.VLANIDConfig, link.TypedSpec().VLAN.VID) asrt.Equal(nethelpers.VLANProtocol8021Q, link.TypedSpec().VLAN.Protocol) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, addressID, func(addr *networkres.AddressStatus, asrt *assert.Assertions) { asrt.Equal(vlanName, addr.TypedSpec().LinkName) }, ) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []resource.ID{routeID, addressRouteID}, func(route *networkres.RouteStatus, asrt *assert.Assertions) { asrt.Equal(vlanName, route.TypedSpec().OutLinkName) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.VLANKind, vlanName) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.DummyLinkKind, dummyName) rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, dummyName) rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, vlanName) rtestutils.AssertNoResource[*networkres.AddressStatus](nodeCtx, suite.T(), suite.Client.COSI, addressID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, addressRouteID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, routeID) } // TestBondConfig tests creation of bond interfaces. func (suite *NetworkConfigSuite) TestBondConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) dummyNames := xslices.Map([]int{0, 1}, func(int) string { return fmt.Sprintf("dummy%d", rand.IntN(10000)) }) dummyConfigs := xslices.Map(dummyNames, func(name string) any { return network.NewDummyLinkConfigV1Alpha1(name) }) bondName := "agg." + strconv.Itoa(rand.IntN(10000)) bond := network.NewBondConfigV1Alpha1(bondName) bond.BondLinks = dummyNames bond.BondMode = new(nethelpers.BondMode8023AD) bond.BondMIIMon = new(uint32(100)) bond.BondUpDelay = new(uint32(200)) bond.BondDownDelay = new(uint32(300)) bond.BondLACPRate = new(nethelpers.LACPRateSlow) bond.BondADActorSysPrio = new(uint16(65535)) bond.BondResendIGMP = new(uint32(1)) bond.BondPacketsPerSlave = new(uint32(1)) bond.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} bond.LinkUp = new(true) bond.LinkMTU = 2000 bond.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("fd13:1235::1/64"), AddressPriority: new(uint32(100)), }, } bond.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{Prefix: netip.MustParsePrefix("fd13:1236::/64")}, RouteGateway: network.Addr{Addr: netip.MustParseAddr("fd13:1235::ffff")}, }, } addressID := bondName + "/fd13:1235::1/64" routeID := bondName + "/inet6/fd13:1235::ffff/fd13:1236::/64/1024" addressRouteID := bondName + "/inet6//fd13:1235::/64/100" suite.PatchMachineConfig(nodeCtx, append(dummyConfigs, bond)...) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, dummyNames, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", link.TypedSpec().Kind) asrt.NotZero(link.TypedSpec().MasterIndex) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, bondName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("bond", link.TypedSpec().Kind) asrt.Equal(nethelpers.OperStateUp, link.TypedSpec().OperationalState) asrt.Equal(bond.LinkMTU, link.TypedSpec().MTU) asrt.Equal(nethelpers.BondMode8023AD, link.TypedSpec().BondMaster.Mode) asrt.Equal(bond.HardwareAddressConfig, link.TypedSpec().HardwareAddr) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, addressID, func(addr *networkres.AddressStatus, asrt *assert.Assertions) { asrt.Equal(bondName, addr.TypedSpec().LinkName) }, ) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []resource.ID{routeID, addressRouteID}, func(route *networkres.RouteStatus, asrt *assert.Assertions) { asrt.Equal(bondName, route.TypedSpec().OutLinkName) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.BondKind, bondName) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.DummyLinkKind, dummyNames...) for _, dummyName := range dummyNames { rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, dummyName) } rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, bondName) rtestutils.AssertNoResource[*networkres.AddressStatus](nodeCtx, suite.T(), suite.Client.COSI, addressID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, addressRouteID) rtestutils.AssertNoResource[*networkres.RouteStatus](nodeCtx, suite.T(), suite.Client.COSI, routeID) } // TestBridgeConfig tests creation of bridge interfaces. func (suite *NetworkConfigSuite) TestBridgeConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) dummyNames := xslices.Map([]int{0, 1}, func(int) string { return fmt.Sprintf("dummy%d", rand.IntN(10000)) }) dummyConfigs := xslices.Map(dummyNames, func(name string) any { return network.NewDummyLinkConfigV1Alpha1(name) }) bridgeName := "bridge." + strconv.Itoa(rand.IntN(10000)) bridge := network.NewBridgeConfigV1Alpha1(bridgeName) bridge.BridgeLinks = dummyNames bridge.BridgeSTP.BridgeSTPEnabled = new(true) bridge.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} bridge.LinkUp = new(true) suite.PatchMachineConfig(nodeCtx, append(dummyConfigs, bridge)...) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, dummyNames, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", link.TypedSpec().Kind) asrt.NotZero(link.TypedSpec().MasterIndex) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, bridgeName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("bridge", link.TypedSpec().Kind) asrt.Equal(pointer.SafeDeref(bridge.BridgeSTP.BridgeSTPEnabled), link.TypedSpec().BridgeMaster.STP.Enabled) asrt.Equal(bridge.HardwareAddressConfig, link.TypedSpec().HardwareAddr) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.BridgeKind, bridgeName) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.DummyLinkKind, dummyNames...) for _, dummyName := range dummyNames { rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, dummyName) } rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, bridgeName) } // TestVRFConfig tests creation of vrf interfaces. func (suite *NetworkConfigSuite) TestVRFConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) dummyNames := xslices.Map([]int{0, 1}, func(int) string { return fmt.Sprintf("dummy%d", rand.IntN(10000)) }) dummyConfigs := xslices.Map(dummyNames, func(name string) any { return network.NewDummyLinkConfigV1Alpha1(name) }) vrfName := "vrf." + strconv.Itoa(rand.IntN(10000)) vrf := network.NewVRFConfigV1Alpha1(vrfName) vrf.VRFLinks = dummyNames vrf.VRFTable = nethelpers.RoutingTable(123) vrf.HardwareAddressConfig = nethelpers.HardwareAddr{0x02, 0x00, 0x00, 0x00, byte(rand.IntN(256)), byte(rand.IntN(256))} vrf.LinkUp = new(true) suite.PatchMachineConfig(nodeCtx, append(dummyConfigs, vrf)...) rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, dummyNames, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("dummy", link.TypedSpec().Kind) asrt.NotZero(link.TypedSpec().MasterIndex) }, ) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, vrfName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("vrf", link.TypedSpec().Kind) asrt.Equal(vrf.VRFTable, link.TypedSpec().VRFMaster.Table) asrt.Equal(vrf.HardwareAddressConfig, link.TypedSpec().HardwareAddr) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.VRFKind, vrfName) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.DummyLinkKind, dummyNames...) for _, dummyName := range dummyNames { rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, dummyName) } rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, vrfName) } // TestWireguardConfig tests creation of Wireguard interfaces. func (suite *NetworkConfigSuite) TestWireguardConfig() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) wgName := "wg." + strconv.Itoa(rand.IntN(10000)) privateKey, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) peerKey, err := wgtypes.GeneratePrivateKey() suite.Require().NoError(err) wg := network.NewWireguardConfigV1Alpha1(wgName) wg.WireguardPrivateKey = privateKey.String() wg.WireguardListenPort = 3042 wg.WireguardPeers = []network.WireguardPeer{ { WireguardPublicKey: peerKey.PublicKey().String(), WireguardAllowedIPs: []network.Prefix{ { Prefix: netip.MustParsePrefix("192.168.2.0/24"), }, }, }, } wg.LinkUp = new(true) suite.PatchMachineConfig(nodeCtx, wg) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, wgName, func(link *networkres.LinkStatus, asrt *assert.Assertions) { asrt.Equal("wireguard", link.TypedSpec().Kind) asrt.Equal(wg.WireguardListenPort, link.TypedSpec().Wireguard.ListenPort) if asrt.Len(link.TypedSpec().Wireguard.Peers, 1) { asrt.Equal(peerKey.PublicKey().String(), link.TypedSpec().Wireguard.Peers[0].PublicKey) } asrt.Equal(privateKey.PublicKey().String(), link.TypedSpec().Wireguard.PublicKey) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.WireguardKind, wgName) rtestutils.AssertNoResource[*networkres.LinkStatus](nodeCtx, suite.T(), suite.Client.COSI, wgName) } // TestBlackholeRouteConfig tests creation of blackhole routes. func (suite *NetworkConfigSuite) TestBlackholeRouteConfig() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing on node %q", node) const dest = "fd13:1236::/64" cfg := network.NewBlackholeRouteConfigV1Alpha1(dest) suite.PatchMachineConfig(nodeCtx, cfg) const routeBlackholeID = "lo/inet6//" + dest + "/1024" rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []resource.ID{routeBlackholeID}, func(route *networkres.RouteStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.TypeBlackhole, route.TypedSpec().Type) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.BlackholeRouteKind, dest) } // routingRuleTestCtx creates a context with a longer timeout for routing rule tests, // because the RoutingRuleStatusController uses 30s polling (no rtnetlink multicast group for rule changes). func (suite *NetworkConfigSuite) routingRuleTestCtx() (context.Context, context.CancelFunc) { return context.WithTimeout(context.Background(), 2*time.Minute) } // TestRoutingRuleBasic tests creation of a routing rule with a numeric table ID. // //nolint:dupl func (suite *NetworkConfigSuite) TestRoutingRuleBasic() { ctx, cancel := suite.routingRuleTestCtx() defer cancel() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(ctx, node) suite.T().Logf("testing routing rule (basic) on node %q", node) const rulePriority uint32 = 1000 cfg := network.NewRoutingRuleConfigV1Alpha1(rulePriority) cfg.RuleSrc = network.Prefix{Prefix: netip.MustParsePrefix("10.99.0.0/16")} cfg.RuleTable = nethelpers.RoutingTable(100) suite.PatchMachineConfig(nodeCtx, cfg) const ruleStatusID = "inet4/01000" rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID, func(rule *networkres.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet4, rule.TypedSpec().Family) asrt.Equal(netip.MustParsePrefix("10.99.0.0/16"), rule.TypedSpec().Src) asrt.Equal(nethelpers.RoutingTable(100), rule.TypedSpec().Table) asrt.Equal(uint32(1000), rule.TypedSpec().Priority) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.RoutingRuleKind, strconv.FormatUint(uint64(rulePriority), 10)) rtestutils.AssertNoResource[*networkres.RoutingRuleStatus](nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID) } // TestRoutingRuleIPv6 tests creation of an IPv6 routing rule. // //nolint:dupl func (suite *NetworkConfigSuite) TestRoutingRuleIPv6() { ctx, cancel := suite.routingRuleTestCtx() defer cancel() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(ctx, node) suite.T().Logf("testing routing rule (IPv6) on node %q", node) const rulePriority uint32 = 3000 cfg := network.NewRoutingRuleConfigV1Alpha1(rulePriority) cfg.RuleSrc = network.Prefix{Prefix: netip.MustParsePrefix("fd99:1234::/48")} cfg.RuleTable = nethelpers.RoutingTable(100) suite.PatchMachineConfig(nodeCtx, cfg) const ruleStatusID = "inet6/03000" rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID, func(rule *networkres.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet6, rule.TypedSpec().Family) asrt.Equal(netip.MustParsePrefix("fd99:1234::/48"), rule.TypedSpec().Src) asrt.Equal(nethelpers.RoutingTable(100), rule.TypedSpec().Table) asrt.Equal(uint32(3000), rule.TypedSpec().Priority) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.RoutingRuleKind, strconv.FormatUint(uint64(rulePriority), 10)) rtestutils.AssertNoResource[*networkres.RoutingRuleStatus](nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID) } // TestRoutingRuleSrcAndDst tests creation of a routing rule with both source and destination prefixes. func (suite *NetworkConfigSuite) TestRoutingRuleSrcAndDst() { ctx, cancel := suite.routingRuleTestCtx() defer cancel() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(ctx, node) suite.T().Logf("testing routing rule (src+dst) on node %q", node) const rulePriority uint32 = 4000 cfg := network.NewRoutingRuleConfigV1Alpha1(rulePriority) cfg.RuleSrc = network.Prefix{Prefix: netip.MustParsePrefix("10.96.0.0/16")} cfg.RuleDst = network.Prefix{Prefix: netip.MustParsePrefix("192.168.99.0/24")} cfg.RuleTable = nethelpers.RoutingTable(100) suite.PatchMachineConfig(nodeCtx, cfg) const ruleStatusID = "inet4/04000" rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID, func(rule *networkres.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet4, rule.TypedSpec().Family) asrt.Equal(netip.MustParsePrefix("10.96.0.0/16"), rule.TypedSpec().Src) asrt.Equal(netip.MustParsePrefix("192.168.99.0/24"), rule.TypedSpec().Dst) asrt.Equal(nethelpers.RoutingTable(100), rule.TypedSpec().Table) asrt.Equal(uint32(4000), rule.TypedSpec().Priority) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.RoutingRuleKind, strconv.FormatUint(uint64(rulePriority), 10)) rtestutils.AssertNoResource[*networkres.RoutingRuleStatus](nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID) } // TestRoutingRuleBlackholeAction tests creation of a routing rule with blackhole action. func (suite *NetworkConfigSuite) TestRoutingRuleBlackholeAction() { ctx, cancel := suite.routingRuleTestCtx() defer cancel() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(ctx, node) suite.T().Logf("testing routing rule (blackhole action) on node %q", node) const rulePriority uint32 = 5000 cfg := network.NewRoutingRuleConfigV1Alpha1(rulePriority) cfg.RuleSrc = network.Prefix{Prefix: netip.MustParsePrefix("10.95.0.0/16")} cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleAction = nethelpers.RoutingRuleActionBlackhole suite.PatchMachineConfig(nodeCtx, cfg) const ruleStatusID = "inet4/05000" rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID, func(rule *networkres.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet4, rule.TypedSpec().Family) asrt.Equal(nethelpers.RoutingRuleActionBlackhole, rule.TypedSpec().Action) asrt.Equal(uint32(5000), rule.TypedSpec().Priority) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.RoutingRuleKind, strconv.FormatUint(uint64(rulePriority), 10)) rtestutils.AssertNoResource[*networkres.RoutingRuleStatus](nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID) } // TestRoutingRuleFwMark tests creation of a routing rule with firewall mark matching. func (suite *NetworkConfigSuite) TestRoutingRuleFwMark() { ctx, cancel := suite.routingRuleTestCtx() defer cancel() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(ctx, node) suite.T().Logf("testing routing rule (fwmark) on node %q", node) const rulePriority uint32 = 6000 cfg := network.NewRoutingRuleConfigV1Alpha1(rulePriority) cfg.RuleSrc = network.Prefix{Prefix: netip.MustParsePrefix("10.94.0.0/16")} cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleFwMark = 0x100 cfg.RuleFwMask = 0xff00 suite.PatchMachineConfig(nodeCtx, cfg) const ruleStatusID = "inet4/06000" rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID, func(rule *networkres.RoutingRuleStatus, asrt *assert.Assertions) { asrt.Equal(nethelpers.FamilyInet4, rule.TypedSpec().Family) asrt.Equal(nethelpers.RoutingTable(100), rule.TypedSpec().Table) asrt.Equal(uint32(6000), rule.TypedSpec().Priority) asrt.Equal(uint32(0x100), rule.TypedSpec().FwMark) asrt.Equal(uint32(0xff00), rule.TypedSpec().FwMask) }, ) suite.RemoveMachineConfigDocumentsByName(nodeCtx, network.RoutingRuleKind, strconv.FormatUint(uint64(rulePriority), 10)) rtestutils.AssertNoResource[*networkres.RoutingRuleStatus](nodeCtx, suite.T(), suite.Client.COSI, ruleStatusID) } func init() { allSuites = append(allSuites, new(NetworkConfigSuite)) } ================================================ FILE: internal/integration/api/node-annotations.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // NodeAnnotationsSuite verifies updating node annotations via machine config. type NodeAnnotationsSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *NodeAnnotationsSuite) SuiteName() string { return "api.NodeAnnotationsSuite" } // SetupTest ... func (suite *NodeAnnotationsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *NodeAnnotationsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestUpdateControlPlane verifies node annotation updates on control plane nodes. func (suite *NodeAnnotationsSuite) TestUpdateControlPlane() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.testUpdate(node) } // TestUpdateWorker verifies node annotation updates on worker nodes. func (suite *NodeAnnotationsSuite) TestUpdateWorker() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.testUpdate(node) } // testUpdate cycles through a set of node annotation updates reverting the change in the end. func (suite *NodeAnnotationsSuite) testUpdate(node string) { k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) suite.T().Logf("updating annotations on node %q (%q)", node, k8sNode.Name) watchCh := suite.SetupNodeInformer(suite.ctx, k8sNode.Name) // set two new annotation suite.setNodeAnnotations(node, map[string]string{ "talos.dev/ann1": "value1", "talos.dev/ann2": "value2", }) suite.waitUntil(watchCh, map[string]string{ "talos.dev/ann1": "value1", "talos.dev/ann2": "value2", }) // remove one annotation owned by Talos suite.setNodeAnnotations(node, map[string]string{ "talos.dev/ann1": "foo", }) suite.waitUntil(watchCh, map[string]string{ "talos.dev/ann1": "foo", "talos.dev/ann2": "", }) // remove all Talos annoations suite.setNodeAnnotations(node, nil) suite.waitUntil(watchCh, map[string]string{ "talos.dev/ann1": "", "talos.dev/ann2": "", }) } func (suite *NodeAnnotationsSuite) waitUntil(watchCh <-chan *v1.Node, expectedAnnotations map[string]string) { outer: for { select { case k8sNode := <-watchCh: suite.T().Logf("annotations %#v", k8sNode.Annotations) for k, v := range expectedAnnotations { if v == "" { _, ok := k8sNode.Annotations[k] if ok { suite.T().Logf("annotation %q is still present", k) continue outer } } if k8sNode.Annotations[k] != v { suite.T().Logf("annotation %q is %q but expected %q", k, k8sNode.Annotations[k], v) continue outer } } return case <-suite.ctx.Done(): suite.T().Fatal("timeout") } } } func (suite *NodeAnnotationsSuite) setNodeAnnotations(nodeIP string, nodeAnnotations map[string]string) { //nolint:dupl nodeCtx := client.WithNode(suite.ctx, nodeIP) nodeConfig, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) bytes := suite.PatchV1Alpha1Config(nodeConfig, func(nodeConfigRaw *v1alpha1.Config) { nodeConfigRaw.MachineConfig.MachineNodeAnnotations = nodeAnnotations }) _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) suite.Require().NoError(err) } func init() { allSuites = append(allSuites, new(NodeAnnotationsSuite)) } ================================================ FILE: internal/integration/api/node-labels.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" ) // NodeLabelsSuite verifies updating node labels via machine config. type NodeLabelsSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *NodeLabelsSuite) SuiteName() string { return "api.NodeLabelsSuite" } // SetupTest ... func (suite *NodeLabelsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *NodeLabelsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestUpdateControlPlane verifies node label updates on control plane nodes. func (suite *NodeLabelsSuite) TestUpdateControlPlane() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.testUpdate(node, true) } // TestUpdateWorker verifies node label updates on worker nodes. func (suite *NodeLabelsSuite) TestUpdateWorker() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.testUpdate(node, false) } // testUpdate cycles through a set of node label updates reverting the change in the end. func (suite *NodeLabelsSuite) testUpdate(node string, isControlplane bool) { k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) suite.T().Logf("updating labels on node %q (%q)", node, k8sNode.Name) watchCh := suite.SetupNodeInformer(suite.ctx, k8sNode.Name) const stdLabelName = "kubernetes.io/hostname" stdLabelValue := k8sNode.Labels[stdLabelName] // set two new labels suite.setNodeLabels(node, map[string]string{ "talos.dev/test1": "value1", "talos.dev/test2": "value2", }) suite.waitUntil(watchCh, map[string]string{ "talos.dev/test1": "value1", "talos.dev/test2": "value2", }, isControlplane) // remove one label owned by Talos suite.setNodeLabels(node, map[string]string{ "talos.dev/test1": "foo", }) suite.waitUntil(watchCh, map[string]string{ "talos.dev/test1": "foo", "talos.dev/test2": "", }, isControlplane) // on control plane node, try to override a label not owned by Talos if isControlplane { suite.setNodeLabels(node, map[string]string{ "talos.dev/test1": "foo2", stdLabelName: "bar", }) suite.waitUntil(watchCh, map[string]string{ "talos.dev/test1": "foo2", stdLabelName: stdLabelValue, }, isControlplane) } // remove all Talos Labels suite.setNodeLabels(node, nil) suite.waitUntil(watchCh, map[string]string{ "talos.dev/test1": "", "talos.dev/test2": "", }, isControlplane) } // TestAllowScheduling verifies node taints are updated. func (suite *NodeLabelsSuite) TestAllowScheduling() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) suite.T().Logf("updating taints on node %q (%q)", node, k8sNode.Name) watchCh := suite.SetupNodeInformer(suite.ctx, k8sNode.Name) suite.waitUntil(watchCh, nil, true) suite.setAllowScheduling(node, true) suite.waitUntil(watchCh, nil, false) suite.setAllowScheduling(node, false) suite.waitUntil(watchCh, nil, true) } //nolint:gocyclo func (suite *NodeLabelsSuite) waitUntil(watchCh <-chan *v1.Node, expectedLabels map[string]string, taintNoSchedule bool) { outer: for { select { case k8sNode := <-watchCh: suite.T().Logf("labels %#v, taints %#v", k8sNode.Labels, k8sNode.Spec.Taints) for k, v := range expectedLabels { if v == "" { _, ok := k8sNode.Labels[k] if ok { suite.T().Logf("label %q is still present", k) continue outer } } if k8sNode.Labels[k] != v { suite.T().Logf("label %q is %q but expected %q", k, k8sNode.Labels[k], v) continue outer } } var found bool for _, taint := range k8sNode.Spec.Taints { if taint.Key == constants.LabelNodeRoleControlPlane && taint.Effect == v1.TaintEffectNoSchedule { found = true } } if taintNoSchedule && !found { suite.T().Logf("taint %q is not present", constants.LabelNodeRoleControlPlane) continue outer } else if !taintNoSchedule && found { suite.T().Logf("taint %q is still present", constants.LabelNodeRoleControlPlane) continue outer } return case <-suite.ctx.Done(): suite.T().Fatal("timeout") } } } func (suite *NodeLabelsSuite) setNodeLabels(nodeIP string, nodeLabels map[string]string) { //nolint:dupl nodeCtx := client.WithNode(suite.ctx, nodeIP) nodeConfig, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) bytes := suite.PatchV1Alpha1Config(nodeConfig, func(nodeConfigRaw *v1alpha1.Config) { nodeConfigRaw.MachineConfig.MachineNodeLabels = nodeLabels }) _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) suite.Require().NoError(err) } func (suite *NodeLabelsSuite) setAllowScheduling(nodeIP string, allowScheduling bool) { nodeCtx := client.WithNode(suite.ctx, nodeIP) nodeConfig, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) bytes := suite.PatchV1Alpha1Config(nodeConfig, func(nodeConfigRaw *v1alpha1.Config) { if allowScheduling { nodeConfigRaw.ClusterConfig.AllowSchedulingOnControlPlanes = new(true) } else { nodeConfigRaw.ClusterConfig.AllowSchedulingOnControlPlanes = nil } }) _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) suite.Require().NoError(err) } func init() { allSuites = append(allSuites, new(NodeLabelsSuite)) } ================================================ FILE: internal/integration/api/node-taints.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "slices" "strings" "time" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/gen/xtesting/must" v1 "k8s.io/api/core/v1" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" ) // NodeTaintsSuite verifies updating node taints via machine config. type NodeTaintsSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *NodeTaintsSuite) SuiteName() string { return "api.NodeTaintsSuite" } // SetupTest ... func (suite *NodeTaintsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *NodeTaintsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestUpdateControlPlane verifies node taints updates on control plane nodes. func (suite *NodeTaintsSuite) TestUpdateControlPlane() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.testUpdate(node) } // testUpdate cycles through a set of node taints updates reverting the change in the end. func (suite *NodeTaintsSuite) testUpdate(node string) { k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) suite.T().Logf("updating taints on node %q (%q)", node, k8sNode.Name) watchCh := suite.SetupNodeInformer(suite.ctx, k8sNode.Name) // set two new taints suite.setNodeTaints(node, map[string]string{ "talos.dev/test1": "value1:NoSchedule", "talos.dev/test2": "NoSchedule", }) suite.waitUntil(watchCh, map[string]string{ constants.LabelNodeRoleControlPlane: "NoSchedule", "talos.dev/test1": "value1:NoSchedule", "talos.dev/test2": "NoSchedule", }) // remove one taint suite.setNodeTaints(node, map[string]string{ "talos.dev/test1": "value1:NoSchedule", }) suite.waitUntil(watchCh, map[string]string{ constants.LabelNodeRoleControlPlane: "NoSchedule", "talos.dev/test1": "value1:NoSchedule", }) // remove all taints suite.setNodeTaints(node, nil) suite.waitUntil(watchCh, map[string]string{ constants.LabelNodeRoleControlPlane: "NoSchedule", }) } func (suite *NodeTaintsSuite) waitUntil(watchCh <-chan *v1.Node, expectedTaints map[string]string) { outer: for { select { case k8sNode := <-watchCh: suite.T().Logf("labels %#v, taints %#v", k8sNode.Labels, k8sNode.Spec.Taints) taints := xslices.ToMap(k8sNode.Spec.Taints, func(t v1.Taint) (string, string) { switch { case t.Value == "": return t.Key, string(t.Effect) case t.Effect == "": return t.Key, t.Value default: return t.Key, strings.Join([]string{t.Value, string(t.Effect)}, ":") } }) expectedTaintsKeys := maps.Keys(expectedTaints) slices.Sort(expectedTaintsKeys) for _, key := range expectedTaintsKeys { actualValue, ok := taints[key] if !ok { suite.T().Logf("taint %q is not present", key) continue outer } expectedValue := expectedTaints[key] if expectedValue != actualValue { suite.T().Logf("expected taint %q to be %q but was %q", key, expectedValue, actualValue) continue outer } delete(taints, key) } if len(taints) > 0 { keys := maps.Keys(taints) slices.Sort(keys) suite.T().Logf("taints %v are still present", keys) continue outer } return case <-suite.ctx.Done(): suite.T().Fatal("timeout") } } } func (suite *NodeTaintsSuite) setNodeTaints(nodeIP string, nodeTaints map[string]string) { nodeCtx := client.WithNode(suite.ctx, nodeIP) nodeConfig := must.Value(suite.ReadConfigFromNode(nodeCtx))(suite.T()) bytes := suite.PatchV1Alpha1Config(nodeConfig, func(nodeConfigRaw *v1alpha1.Config) { nodeConfigRaw.MachineConfig.MachineNodeTaints = nodeTaints }) must.Value(suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }))(suite.T()) } func init() { allSuites = append(allSuites, new(NodeTaintsSuite)) } ================================================ FILE: internal/integration/api/pci_driver_rebind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "path/filepath" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" hardwareconfigtype "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // PCIDriverRebindSuite is a suite of tests for PCI rebind. type PCIDriverRebindSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName returns the name of the suite. func (suite *PCIDriverRebindSuite) SuiteName() string { return "api.PCIDriverRebindSuite" } // SetupTest sets up the test. func (suite *PCIDriverRebindSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping virtio test since provisioner is not qemu") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) stream, err := suite.Client.LS(nodeCtx, &machineapi.ListRequest{ Root: "/sys/class/iommu", }) suite.Require().NoError(err) var count int suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { if info.GetRelativeName() == "." { return nil } count++ return nil })) if count == 0 { suite.T().Skip("skipping PCI rebind test since IOMMU is not enabled") } } // TearDownTest tears down the test. func (suite *PCIDriverRebindSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestIOMMURebind tests PCI rebind. func (suite *PCIDriverRebindSuite) TestIOMMURebind() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) items, err := suite.Client.COSI.List(nodeCtx, resource.NewMetadata(hardware.NamespaceName, hardware.PCIDeviceType, "", resource.VersionUndefined)) suite.Require().NoError(err) var pciDeviceID string for _, item := range items.Items { pci, ok := item.(*hardware.PCIDevice) suite.Require().True(ok, "expected PCI device, got %T", item) if pci.TypedSpec().Product == "82540EM Gigabit Ethernet Controller" { pciDeviceID = pci.Metadata().ID() break } } // validate that the driver is bound to e1000 initially suite.validateDriver(nodeCtx, pciDeviceID, "e1000") cfgDocument := hardwareconfigtype.NewPCIDriverRebindConfigV1Alpha1() cfgDocument.MetaName = pciDeviceID cfgDocument.PCITargetDriver = "vfio-pci" v1alpha1CfgDocument := &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineKernel: &v1alpha1.KernelConfig{ KernelModules: []*v1alpha1.KernelModuleConfig{ { ModuleName: "vfio-pci", }, }, }, }, } suite.PatchMachineConfig(nodeCtx, v1alpha1CfgDocument, cfgDocument) _, err = suite.Client.COSI.WatchFor(nodeCtx, hardware.NewPCIDriverRebindStatus(pciDeviceID).Metadata(), state.WithEventTypes(state.Created, state.Updated)) suite.Require().NoError(err) defer func() { suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) suite.PatchMachineConfig(nodeCtx, map[string]any{ "machine": map[string]any{ "kernel": map[string]any{ "$patch": "delete", }, }, }) }() // after applying the patch the device should be bound to vfio-pci suite.validateDriver(nodeCtx, pciDeviceID, "vfio-pci") cfgDocument.PCITargetDriver = "e1000" suite.PatchMachineConfig(nodeCtx, cfgDocument) _, err = suite.Client.COSI.WatchFor(nodeCtx, hardware.NewPCIDriverRebindStatus(pciDeviceID).Metadata(), state.WithEventTypes(state.Updated)) suite.Require().NoError(err) // verify that an update to the target driver takes effect suite.validateDriver(nodeCtx, pciDeviceID, "e1000") // switch back to vfio-pci cfgDocument.PCITargetDriver = "vfio-pci" suite.PatchMachineConfig(nodeCtx, cfgDocument) _, err = suite.Client.COSI.WatchFor(nodeCtx, hardware.NewPCIDriverRebindStatus(pciDeviceID).Metadata(), state.WithEventTypes(state.Updated)) suite.Require().NoError(err) // verify that the update has taken effect suite.validateDriver(nodeCtx, pciDeviceID, "vfio-pci") // now remove the pci driver rebind config suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) _, err = suite.Client.COSI.WatchFor(nodeCtx, hardware.NewPCIDriverRebindStatus(pciDeviceID).Metadata(), state.WithEventTypes(state.Destroyed)) suite.Require().NoError(err) // verify that the device is back to original host driver suite.validateDriver(nodeCtx, pciDeviceID, "e1000") } func (suite *PCIDriverRebindSuite) validateDriver(nodeCtx context.Context, pciDeviceID, driver string) { stream, err := suite.Client.LS(nodeCtx, &machineapi.ListRequest{ Root: fmt.Sprintf("/sys/bus/pci/devices/%s", pciDeviceID), Types: []machineapi.ListRequest_Type{machineapi.ListRequest_SYMLINK}, }) suite.Require().NoError(err) var driverLink string suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { if info.GetRelativeName() == "driver" { driverLink = info.Link return nil } return nil })) suite.Require().Equal(driver, filepath.Base(driverLink), "expected driver %q, got %q", driver, filepath.Base(driverLink)) } func init() { allSuites = append(allSuites, &PCIDriverRebindSuite{}) } ================================================ FILE: internal/integration/api/platform.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "go.yaml.in/yaml/v4" v1alpha1runtime "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // PlatformSuite ... type PlatformSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *PlatformSuite) SuiteName() string { return "api.PlatformSuite" } // SetupTest ... func (suite *PlatformSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) } // TearDownTest ... func (suite *PlatformSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestPlatformMetadata verifies platform metadata. func (suite *PlatformSuite) TestPlatformMetadata() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, runtime.PlatformMetadataID, func(md *runtime.PlatformMetadata, asrt *assert.Assertions) { marshaled, err := resource.MarshalYAML(md) suite.Require().NoError(err) yml, err := yaml.Marshal(marshaled) suite.Require().NoError(err) suite.T().Logf("platform metadata:\n%s", string(yml)) if md.TypedSpec().Platform == "aws" || md.TypedSpec().Platform == "gcp" { asrt.NotEmpty(md.TypedSpec().Tags) } }) } // TestMetalPlatformMetadata verifies platform metadata for metal platform. func (suite *PlatformSuite) TestMetalPlatformMetadata() { if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping platform metal test since provisioner is not qemu") } node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) suite.T().Logf("verifying metal platform network config on node %s", node) const linkName = "dummy-platform" platformNetworkConfig := v1alpha1runtime.PlatformNetworkConfig{ Links: []network.LinkSpecSpec{ { Name: linkName, Logical: true, Up: false, MTU: 1500, Type: nethelpers.LinkEther, Kind: "dummy", }, }, } platformNetworkConfigMarshaled, err := yaml.Marshal(platformNetworkConfig) suite.Require().NoError(err) suite.Require().NoError(suite.Client.MetaWrite(ctx, meta.MetalNetworkPlatformConfig, platformNetworkConfigMarshaled)) // link should appear on the node rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, linkName, func(*network.LinkStatus, *assert.Assertions) {}) suite.Require().NoError(suite.Client.MetaDelete(ctx, meta.MetalNetworkPlatformConfig)) // address should be removed rtestutils.AssertNoResource[*network.LinkStatus](ctx, suite.T(), suite.Client.COSI, linkName) } func init() { allSuites = append(allSuites, new(PlatformSuite)) } ================================================ FILE: internal/integration/api/probe-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/types/network" networkres "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const ( googleDNS = "8.8.8.8:53" cfDNS = "1.1.1.1:53" ) // ProbeConfigSuite tests ProbeConfig functionality via the API. type ProbeConfigSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName returns the name of the suite. func (suite *ProbeConfigSuite) SuiteName() string { return "api.ProbeConfigSuite" } // SetupTest initializes test context. func (suite *ProbeConfigSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute) } // TearDownTest cleans up test context. func (suite *ProbeConfigSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestProbeConfig tests that ProbeConfig documents create ProbeSpec resources. func (suite *ProbeConfigSuite) TestProbeConfig() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing ProbeConfig on node %q", node) // Create a probe that checks if we can reach a public DNS server probeConfig := network.NewTCPProbeConfigV1Alpha1("test-probe") probeConfig.ProbeInterval = 2 * time.Second probeConfig.ProbeFailureThreshold = 3 probeConfig.TCPEndpoint = googleDNS probeConfig.TCPTimeout = 5 * time.Second suite.PatchMachineConfig(nodeCtx, probeConfig) // Wait for ProbeSpec resource to be created rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS, func(spec *networkres.ProbeSpec, asrt *assert.Assertions) { asrt.Equal(2*time.Second, spec.TypedSpec().Interval) asrt.Equal(3, spec.TypedSpec().FailureThreshold) asrt.Equal(googleDNS, spec.TypedSpec().TCP.Endpoint) asrt.Equal(5*time.Second, spec.TypedSpec().TCP.Timeout) asrt.Equal(networkres.ConfigMachineConfiguration, spec.TypedSpec().ConfigLayer) }, ) // Update the probe config probeConfig.ProbeFailureThreshold = 5 suite.PatchMachineConfig(nodeCtx, probeConfig) // Wait for ProbeSpec resource to be updated rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS, func(spec *networkres.ProbeSpec, asrt *assert.Assertions) { asrt.Equal(5, spec.TypedSpec().FailureThreshold) }, ) // Remove the ProbeConfig suite.RemoveMachineConfigDocuments(nodeCtx, network.TCPProbeKind) // Wait for ProbeSpec resource to be removed rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS) } // TestMultipleProbes tests that multiple ProbeConfig documents create multiple ProbeSpec resources. func (suite *ProbeConfigSuite) TestMultipleProbes() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing multiple ProbeConfigs on node %q", node) // Create first probe probeConfig1 := network.NewTCPProbeConfigV1Alpha1("proxy-check") probeConfig1.ProbeInterval = 1 * time.Second probeConfig1.ProbeFailureThreshold = 3 probeConfig1.TCPEndpoint = cfDNS probeConfig1.TCPTimeout = 10 * time.Second // Create second probe probeConfig2 := network.NewTCPProbeConfigV1Alpha1("dns-check") probeConfig2.ProbeInterval = 5 * time.Second probeConfig2.ProbeFailureThreshold = 2 probeConfig2.TCPEndpoint = googleDNS probeConfig2.TCPTimeout = 5 * time.Second suite.PatchMachineConfig(nodeCtx, probeConfig1, probeConfig2) // Verify both probes are created rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []string{"tcp:" + cfDNS, "tcp:" + googleDNS}, func(spec *networkres.ProbeSpec, asrt *assert.Assertions) { switch spec.TypedSpec().TCP.Endpoint { case cfDNS: asrt.Equal(1*time.Second, spec.TypedSpec().Interval) asrt.Equal(3, spec.TypedSpec().FailureThreshold) case googleDNS: asrt.Equal(5*time.Second, spec.TypedSpec().Interval) asrt.Equal(2, spec.TypedSpec().FailureThreshold) } asrt.Equal(networkres.ConfigMachineConfiguration, spec.TypedSpec().ConfigLayer) }, ) // Remove all ProbeConfigs suite.RemoveMachineConfigDocuments(nodeCtx, network.TCPProbeKind) // Verify both probes are removed rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+cfDNS) rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS) } // TestProbeStatus tests that ProbeSpec resources create ProbeStatus resources. func (suite *ProbeConfigSuite) TestProbeStatus() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("testing ProbeStatus on node %q", node) // Create a probe with a very short interval probeConfig := network.NewTCPProbeConfigV1Alpha1("dns-status-check") probeConfig.ProbeInterval = 1 * time.Second probeConfig.ProbeFailureThreshold = 1 probeConfig.TCPEndpoint = googleDNS probeConfig.TCPTimeout = 3 * time.Second suite.PatchMachineConfig(nodeCtx, probeConfig) // Wait for ProbeSpec to be created rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS, func(spec *networkres.ProbeSpec, asrt *assert.Assertions) { asrt.Equal(googleDNS, spec.TypedSpec().TCP.Endpoint) }, ) // Give the probe controller time to run at least one probe time.Sleep(3 * time.Second) // Verify ProbeStatus is created and has success/failure data probeStatuses, err := safe.StateListAll[*networkres.ProbeStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) var found bool for status := range probeStatuses.All() { if status.Metadata().ID() == "tcp:"+googleDNS { found = true suite.T().Logf("ProbeStatus: success=%v, lastError=%s", status.TypedSpec().Success, status.TypedSpec().LastError) // The status should have been updated at least once (either success or failure) suite.Assert().True(status.TypedSpec().Success || status.TypedSpec().LastError != "") break } } suite.Assert().True(found, "expected to find ProbeStatus for tcp:"+googleDNS) // Clean up suite.RemoveMachineConfigDocuments(nodeCtx, network.TCPProbeKind) rtestutils.AssertNoResource[*networkres.ProbeSpec](nodeCtx, suite.T(), suite.Client.COSI, "tcp:"+googleDNS) } func init() { allSuites = append(allSuites, &ProbeConfigSuite{}) } ================================================ FILE: internal/integration/api/process.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bytes" "context" "io" "path/filepath" "strconv" "strings" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ProcessSuite ... type ProcessSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ProcessSuite) SuiteName() string { return "api.ProcessSuite" } // SetupTest ... func (suite *ProcessSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { // TODO: should we test caps and cgroups in Docker? suite.T().Skip("skipping process test since provisioner is not qemu") } } // TearDownTest ... func (suite *ProcessSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } func (suite *ProcessSuite) readProcfs(nodeCtx context.Context, pid int32, property string) string { r, err := suite.Client.Read(nodeCtx, filepath.Join("/proc", strconv.Itoa(int(pid)), property)) suite.Require().NoError(err) value, err := io.ReadAll(r) suite.Require().NoError(err) suite.Require().NoError(r.Close()) return string(bytes.TrimSpace(value)) } // TestProcessCapabilities reads capabilities of processes from procfs // and validates system services get necessary capabilities dropped. func (suite *ProcessSuite) TestProcessCapabilities() { nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node) r, err := suite.Client.Processes(nodeCtx) suite.Require().NoError(err) found := 0 for _, msg := range r.Messages { procs := msg.Processes for _, p := range procs { switch p.Command { case "systemd-udevd": found++ // All but cap_sys_boot suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "CapPrm:\t000001ffffbfffff\nCapEff:\t000001ffffbfffff\nCapBnd:\t000001ffffbfffff", ) suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "cgroup"), "0::/system/udevd", ) suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "environ"), constants.EnvXDGRuntimeDir, ) suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "Uid:\t0", ) case "dashboard": found++ // None suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "CapPrm:\t0000000000000000\nCapEff:\t0000000000000000\nCapBnd:\t0000000000000000", ) suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "cgroup"), "0::/system/dashboard", ) suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "oom_score_adj"), "-400", ) suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "environ"), "TERM=linux", ) suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "Uid:\t50", ) case "containerd": found++ // All but cap_sys_boot, cap_sys_module suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "CapPrm:\t000001ffffbeffff\nCapEff:\t000001ffffbeffff\nCapBnd:\t000001ffffbeffff", ) if strings.Contains(p.Args, "/system/run/containerd") { suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "cgroup"), "0::/system/runtime", ) suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "oom_score_adj"), "-999", ) } else { suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "cgroup"), "0::/podruntime/runtime", ) suite.Require().Equal( suite.readProcfs(nodeCtx, p.Pid, "oom_score_adj"), "-500", ) } suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "environ"), constants.EnvXDGRuntimeDir, ) suite.Require().Contains( suite.readProcfs(nodeCtx, p.Pid, "status"), "Uid:\t0", ) } } } suite.Require().Equal(4, found, "Not all processes found") } } func init() { allSuites = append(allSuites, new(ProcessSuite)) } ================================================ FILE: internal/integration/api/reboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "sync" "sync/atomic" "testing" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // RebootSuite ... type RebootSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *RebootSuite) SuiteName() string { return "api.RebootSuite" } // SetupTest ... func (suite *RebootSuite) SetupTest() { if testing.Short() { suite.T().Skip("skipping in short mode") } // make sure we abort at some point in time, but give enough room for reboots suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Minute) } // TearDownTest ... func (suite *RebootSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestRebootNodeByNode reboots cluster node by node, waiting for health between reboots. func (suite *RebootSuite) TestRebootNodeByNode() { if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboots") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) for _, node := range nodes { suite.T().Log("rebooting node", node) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 10*time.Minute, suite.CleanupFailedPods, ) } } // TestForcedReboot force-reboots cluster node by node, // ensuring that the 'cleanup' phase/'stopAllPods' task doesn't run. func (suite *RebootSuite) TestForcedReboot() { //nolint:gocyclo if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboots") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) for _, node := range nodes { suite.T().Log("force rebooting node", node) nodeCtx := client.WithNode(suite.ctx, node) var ( sawStopAllPods atomic.Bool sawCleanupPhase atomic.Bool ) // watch events so we can verify graceful teardown did not happen watchCtx, watchCancel := context.WithCancel(nodeCtx) eventsCh := make(chan client.EventResult) suite.Require().NoError(suite.Client.EventsWatchV2(watchCtx, eventsCh)) go func() { for { select { case <-watchCtx.Done(): return case ev := <-eventsCh: if ev.Error != nil { continue } switch msg := ev.Event.Payload.(type) { case *machineapi.TaskEvent: if msg.GetTask() == "stopAllPods" { sawStopAllPods.Store(true) } case *machineapi.PhaseEvent: if msg.GetPhase() == "cleanup" { sawCleanupPhase.Store(true) } } } } }() suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx, client.WithForce)) }, 10*time.Minute, suite.CleanupFailedPods, ) watchCancel() suite.Require().Falsef(sawCleanupPhase.Load(), "cleanup phase must not run during forced reboot") suite.Require().Falsef(sawStopAllPods.Load(), "stopAllPods task must not run during forced reboot") } suite.WaitForBootDone(suite.ctx) } // TestRebootMultiple reboots a node, issues consequent reboots // reboot should cancel boot sequence, and cancel another reboot. func (suite *RebootSuite) TestRebootMultiple() { if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboots") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNodes(suite.ctx, node) bootID := suite.ReadBootIDWithRetry(nodeCtx, time.Minute*5) // Issue reboot. suite.Require().NoError(base.IgnoreGRPCUnavailable( suite.Client.Reboot(nodeCtx), )) // Issue reboot once again and wait for node to get a new boot id. suite.Require().NoError(base.IgnoreGRPCUnavailable( suite.Client.Reboot(nodeCtx), )) suite.AssertBootIDChanged(nodeCtx, bootID, node, time.Minute*7) bootID = suite.ReadBootIDWithRetry(nodeCtx, time.Minute*5) suite.Require().NoError(retry.Constant(time.Second * 5).Retry(func() error { // Issue reboot while the node is still booting. err := suite.Client.Reboot(nodeCtx) if err != nil { return retry.ExpectedError(err) } // Reboot again and wait for cluster to become healthy. suite.Require().NoError(base.IgnoreGRPCUnavailable( suite.Client.Reboot(nodeCtx), )) return nil })) suite.AssertBootIDChanged(nodeCtx, bootID, node, time.Minute*7) suite.WaitForBootDone(suite.ctx) } // TestRebootAllNodes reboots all cluster nodes at the same time. // //nolint:gocyclo func (suite *RebootSuite) TestRebootAllNodes() { if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboots") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) errCh := make(chan error, len(nodes)) var initialBootID sync.Map for _, node := range nodes { go func(node string) { errCh <- func() error { nodeCtx := client.WithNodes(suite.ctx, node) // read boot_id before reboot bootIDBefore, err := suite.ReadBootID(nodeCtx) if err != nil { return fmt.Errorf("error reading initial bootID (node %q): %w", node, err) } initialBootID.Store(node, bootIDBefore) return nil }() }(node) } for range nodes { suite.Require().NoError(<-errCh) } allNodesCtx := client.WithNodes(suite.ctx, nodes...) err := base.IgnoreGRPCUnavailable(suite.Client.Reboot(allNodesCtx)) suite.Require().NoError(err) for _, node := range nodes { go func(node string) { errCh <- func() error { bootIDBeforeInterface, ok := initialBootID.Load(node) if !ok { return fmt.Errorf("bootID record not found for %q", node) } bootIDBefore := bootIDBeforeInterface.(string) //nolint:forcetypeassert nodeCtx := client.WithNodes(suite.ctx, node) return retry.Constant(10 * time.Minute).Retry( func() error { requestCtx, requestCtxCancel := context.WithTimeout(nodeCtx, 5*time.Second) defer requestCtxCancel() bootIDAfter, err := suite.ReadBootID(requestCtx) if err != nil { // API might be unresponsive during reboot return retry.ExpectedErrorf("error reading bootID for node %q: %w", node, err) } if bootIDAfter == bootIDBefore { // bootID should be different after reboot return retry.ExpectedErrorf( "bootID didn't change for node %q: before %s, after %s", node, bootIDBefore, bootIDAfter, ) } return nil }, ) }() }(node) } for range nodes { suite.Assert().NoError(<-errCh) } if suite.Cluster != nil { // without cluster state we can't do deep checks, but basic reboot test still works // NB: using `ctx` here to have client talking to init node by default suite.AssertClusterHealthy(suite.ctx) } } func init() { allSuites = append(allSuites, new(RebootSuite)) } ================================================ FILE: internal/integration/api/reset.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "slices" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // ResetSuite ... type ResetSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ResetSuite) SuiteName() string { return "api.ResetSuite" } // SetupTest ... func (suite *ResetSuite) SetupTest() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot (and reset)") } if suite.Cluster == nil { suite.T().Skip("without full cluster state reset test is not reliable (can't wait for cluster readiness in between resets)") } // make sure we abort at some point in time, but give enough room for Resets suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Minute) } // TearDownTest ... func (suite *ResetSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestResetNodeByNode Resets cluster node by node, waiting for health between Resets. func (suite *ResetSuite) TestResetNodeByNode() { if suite.Capabilities().SecureBooted { // this is because in secure boot mode, the machine config is only applied and cannot be passed as kernel args suite.T().Skip("skipping as talos is explicitly trusted booted") } nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) slices.Sort(nodes) for _, node := range nodes { suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: true, }, true) } } func (suite *ResetSuite) testResetNoGraceful(nodeType machine.Type) { if suite.Capabilities().SecureBooted { // this is because in secure boot mode, the machine config is only applied and cannot be passed as kernel args suite.T().Skip("skipping as talos is explicitly trusted booted") } node := suite.RandomDiscoveredNodeInternalIP(nodeType) suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: false, }, true) } // TestResetNoGracefulWorker resets a worker in !graceful mode. func (suite *ResetSuite) TestResetNoGracefulWorker() { suite.testResetNoGraceful(machine.TypeWorker) } // TestResetNoGracefulControlplane resets a control plane node in !graceful mode. // // As the node doesn't leave etcd, it relies on Talos to fix the problem on rejoin. func (suite *ResetSuite) TestResetNoGracefulControlplane() { suite.testResetNoGraceful(machine.TypeControlPlane) } // TestResetWithSpecEphemeral resets only ephemeral partition on the node. func (suite *ResetSuite) TestResetWithSpecEphemeral() { node := suite.RandomDiscoveredNodeInternalIP() suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: true, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, }, true) } // TestResetWithSpecStateAndUserDisks resets state partition and user disks on the node. // // As ephemeral partition is not reset, so kubelet cert shouldn't change. // //nolint:gocyclo func (suite *ResetSuite) TestResetWithSpecStateAndUserDisks() { if suite.Capabilities().SecureBooted { // this is because in secure boot mode, the machine config is only applied and cannot be passed as kernel args suite.T().Skip("skipping as talos is explicitly trusted booted") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) suite.T().Logf("resetting STATE + user disk on node %s", node) config, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) // check if EPHEMERAL is encrypted locked to STATE var lockedToState bool if volume, ok := config.Volumes().ByName(constants.EphemeralPartitionLabel); ok && volume.Encryption() != nil { for _, key := range volume.Encryption().Keys() { if key.LockToSTATE() { lockedToState = true } } } disks, err := suite.Client.Disks(nodeCtx) suite.Require().NoError(err) suite.Require().NotEmpty(disks.Messages) userDisksToWipe := xslices.Map( xslices.Filter(disks.Messages[0].Disks, func(disk *storage.Disk) bool { switch { case disk.SystemDisk: return false case disk.Type == storage.Disk_UNKNOWN, disk.Type == storage.Disk_CD, disk.Type == storage.Disk_SD, disk.Type == storage.Disk_NVME: return false case disk.Readonly: return false case disk.BusPath == "/virtual": return false } return true }), func(disk *storage.Disk) string { return disk.DeviceName }, ) if !lockedToState { // if not locked to STATE, wipe will be successful suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: true, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.StatePartitionLabel, Wipe: true, }, }, UserDisksToWipe: userDisksToWipe, }, true) return } suite.T().Logf("verifying that EPHEMERAL partition would fail to unlock after reset, as it is locked to STATE") // if the EPHEMERAL partition is locked to STATE, it will fail to unlock after reset, so let's verify it suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: true, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.StatePartitionLabel, Wipe: true, }, }, UserDisksToWipe: userDisksToWipe, }, false) // wait for EPHEMERAL failure rtestutils.AssertResources(nodeCtx, suite.T(), suite.Client.COSI, []string{constants.EphemeralPartitionLabel}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equal(block.VolumePhaseFailed, vs.TypedSpec().Phase) asrt.Contains(vs.TypedSpec().ErrorMessage, "encryption key rejected") }, ) // now reset EPHEMERAL suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: false, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, }, true) } // TestResetDuringBoot resets the node while it is in boot sequence. func (suite *ResetSuite) TestResetDuringBoot() { node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNodes(suite.ctx, node) suite.T().Log("rebooting node", node) bootIDBefore, err := suite.ReadBootID(nodeCtx) suite.Require().NoError(err) suite.Require().NoError(suite.Client.Reboot(nodeCtx)) suite.AssertBootIDChanged(nodeCtx, bootIDBefore, node, 3*time.Minute) suite.ClearConnectionRefused(suite.ctx, node) // make sure EPHEMERAL is ready rtestutils.AssertResources(client.WithNode(suite.ctx, node), suite.T(), suite.Client.COSI, []string{constants.EphemeralPartitionLabel}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equal(block.VolumePhaseReady, vs.TypedSpec().Phase) }, ) suite.ResetNode(suite.ctx, node, &machineapi.ResetRequest{ Reboot: true, Graceful: true, SystemPartitionsToWipe: []*machineapi.ResetPartitionSpec{ { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, }, true) } func init() { allSuites = append(allSuites, new(ResetSuite)) } ================================================ FILE: internal/integration/api/resources.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // ResourcesSuite ... type ResourcesSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ResourcesSuite) SuiteName() string { return "api.ResourcesSuite" } // SetupTest ... func (suite *ResourcesSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), time.Minute) } // TearDownTest ... func (suite *ResourcesSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestListResources tries to fetch every resource in the system. func (suite *ResourcesSuite) TestListResources() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) ctx := client.WithNode(suite.ctx, node) nsList, err := safe.StateListAll[*meta.Namespace](ctx, suite.Client.COSI) suite.Require().NoError(err) namespaces := safe.ToSlice(nsList, func(ns *meta.Namespace) string { return ns.Metadata().ID() }) rdList, err := safe.StateListAll[*meta.ResourceDefinition](ctx, suite.Client.COSI) suite.Require().NoError(err) resourceTypes := safe.ToSlice(rdList, func(rd *meta.ResourceDefinition) string { return rd.TypedSpec().Type }) eg, egCtx := errgroup.WithContext(ctx) for _, resourceType := range resourceTypes { eg.Go(func() error { for _, namespace := range namespaces { _, err := suite.Client.COSI.List(egCtx, resource.NewMetadata(namespace, resourceType, "", resource.VersionUndefined)) if err != nil { return fmt.Errorf("failed to list resources of type %q in namespace %q: %w", resourceType, namespace, err) } } return nil }) } suite.Assert().NoError(eg.Wait()) } // TestForbiddenOperations verifies that write operations are forbidden. func (suite *ResourcesSuite) TestForbiddenOperations() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) err := suite.Client.COSI.Create(ctx, v1alpha1.NewService("foo")) suite.Require().Error(err) suite.Assert().True(state.IsConflictError(err)) // this is how COSI wraps the error err = suite.Client.COSI.Destroy(ctx, v1alpha1.NewService("kubelet").Metadata()) suite.Require().Error(err) suite.Assert().True(state.IsConflictError(err)) // this is how COSI wraps the error err = suite.Client.COSI.Update(ctx, v1alpha1.NewService("kubelet")) suite.Require().Error(err) suite.Assert().True(state.IsConflictError(err)) // this is how COSI wraps the error } func init() { allSuites = append(allSuites, new(ResourcesSuite)) } ================================================ FILE: internal/integration/api/rotate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "testing" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" secretsres "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/provision/access" "github.com/siderolabs/talos/pkg/rotate/pki/kubernetes" "github.com/siderolabs/talos/pkg/rotate/pki/talos" ) // RotateCASuite verifies rotation of Talos and Kubernetes CAs. type RotateCASuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *RotateCASuite) SuiteName() string { return "api.RotateCASuite" } // SetupTest ... func (suite *RotateCASuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 10*time.Minute) } // TearDownTest ... func (suite *RotateCASuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestTalos updates Talos CA in the cluster. func (suite *RotateCASuite) TestTalos() { if suite.Cluster == nil { suite.T().Skip("cluster information is not available") } suite.T().Logf("capturing current Talos CA") nodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) // save osRoot osRoot, err := safe.StateGetByID[*secretsres.OSRoot](client.WithNode(suite.ctx, nodeInternalIP), suite.Client.COSI, secretsres.OSRootID) suite.Require().NoError(err) suite.T().Logf("rotating current CA -> new CA") newBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) suite.Require().NoError(err) options := talos.Options{ CurrentClient: suite.Client, ClusterInfo: access.NewAdapter(suite.Cluster), ContextName: suite.Talosconfig.Context, Endpoints: suite.Client.GetEndpoints(), NewTalosCA: newBundle.Certs.OS, EncoderOption: encoder.WithComments(encoder.CommentsAll), Printf: suite.T().Logf, } newTalosconfig, err := talos.Rotate(suite.ctx, options) suite.Require().NoError(err) newClient, err := client.New(suite.ctx, client.WithConfig(newTalosconfig)) suite.Require().NoError(err) if !testing.Short() { suite.restartAPIServices(newClient) } suite.T().Logf("rotating back new CA -> old CA") options = talos.Options{ CurrentClient: newClient, ClusterInfo: access.NewAdapter(suite.Cluster), ContextName: suite.Talosconfig.Context, Endpoints: suite.Client.GetEndpoints(), NewTalosCA: osRoot.TypedSpec().IssuingCA, EncoderOption: encoder.WithComments(encoder.CommentsAll), Printf: suite.T().Logf, } _, err = talos.Rotate(suite.ctx, options) suite.Require().NoError(err) suite.AssertClusterHealthy(suite.ctx) suite.ClearConnectionRefused(suite.ctx, suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker)...) } // TestKubernetes updates Kubernetes CA in the cluster. func (suite *RotateCASuite) TestKubernetes() { if suite.Cluster == nil { suite.T().Skip("cluster information is not available") } if testing.Short() { suite.T().Skip("skipping in short mode") } suite.T().Logf("capturing current Kubernetes CA") nodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) // save k8sRoot k8sRoot, err := safe.StateGetByID[*secretsres.KubernetesRoot](client.WithNode(suite.ctx, nodeInternalIP), suite.Client.COSI, secretsres.KubernetesRootID) suite.Require().NoError(err) suite.T().Logf("rotating current CA -> new CA") newBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) suite.Require().NoError(err) options := kubernetes.Options{ TalosClient: suite.Client, ClusterInfo: access.NewAdapter(suite.Cluster), NewKubernetesCA: newBundle.Certs.K8s, EncoderOption: encoder.WithComments(encoder.CommentsAll), Printf: suite.T().Logf, } suite.Require().NoError(kubernetes.Rotate(suite.ctx, options)) suite.AssertClusterHealthy(suite.ctx) suite.T().Logf("rotating back new CA -> old CA") options = kubernetes.Options{ TalosClient: suite.Client, ClusterInfo: access.NewAdapter(suite.Cluster), NewKubernetesCA: k8sRoot.TypedSpec().IssuingCA, EncoderOption: encoder.WithComments(encoder.CommentsAll), Printf: suite.T().Logf, } suite.Require().NoError(kubernetes.Rotate(suite.ctx, options)) suite.AssertClusterHealthy(suite.ctx) } func (suite *RotateCASuite) restartAPIServices(c *client.Client) { suite.T().Logf("restarting API services") var oldClient *client.Client oldClient, suite.Client = suite.Client, c defer func() { suite.Client = oldClient }() for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { suite.T().Logf("restarting API services on %s", node) err := c.Restart(client.WithNode(suite.ctx, node), constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "trustd") suite.Require().NoError(err) suite.ClearConnectionRefused(suite.ctx, node) err = c.Restart(client.WithNode(suite.ctx, node), constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "apid") suite.Require().NoError(err) suite.ClearConnectionRefused(suite.ctx, node) } for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker) { suite.T().Logf("restarting API services on %s", node) err := c.Restart(client.WithNode(suite.ctx, node), constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, "apid") suite.Require().NoError(err) suite.ClearConnectionRefused(suite.ctx, node) } suite.AssertClusterHealthy(suite.ctx) } func init() { allSuites = append(allSuites, new(RotateCASuite)) } ================================================ FILE: internal/integration/api/sbom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/version" ) // SBOMSuite ... type SBOMSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *SBOMSuite) SuiteName() string { return "api.SBOMSuite" } // SetupTest ... func (suite *SBOMSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) } // TearDownTest ... func (suite *SBOMSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestCommon verifies that common SBOM items are available. func (suite *SBOMSuite) TestCommon() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{ // list of common SBOM items which should be present always "Talos", "github.com/siderolabs/go-kubernetes", }, func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.NotEmpty(item.TypedSpec().Name, "SBOM item name should not be empty") asrt.NotEmpty(item.TypedSpec().Version, "SBOM item version should not be empty") }, ) // Talos SBOM item should have a matching version. rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, "Talos", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal(version.Name, item.TypedSpec().Name, "SBOM item name should match Talos version name") // asrt.Equal(version.Tag, item.TypedSpec().Version, "SBOM item version should match Talos version") }, ) // Assert on containerd/runc versions. rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, "containerd", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal("v"+constants.DefaultContainerdVersion, item.TypedSpec().Version) }, ) rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, "runc", func(item *runtime.SBOMItem, asrt *assert.Assertions) { asrt.Equal("v"+constants.RuncVersion, item.TypedSpec().Version) }, ) // Assert on Go version. rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, "golang", func(item *runtime.SBOMItem, asrt *assert.Assertions) { goVersion := strings.TrimPrefix(constants.GoVersion, "go") asrt.Equal(goVersion, item.TypedSpec().Version) }, ) if suite.Capabilities().RunsTalosKernel { // Assert on Talos kernel version. rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, "kernel", func(item *runtime.SBOMItem, asrt *assert.Assertions) { // cut the suffix, first try removing .0 patch version for kernel releases like 6.17 version, _, ok := strings.Cut(constants.DefaultKernelVersion, ".0-") if !ok { version, _, ok = strings.Cut(constants.DefaultKernelVersion, "-") suite.Require().True(ok, "kernel version should have a suffix") } asrt.Equal(version, item.TypedSpec().Version) }, ) } else { rtestutils.AssertNoResource[*runtime.SBOMItem](ctx, suite.T(), suite.Client.COSI, "kernel") } } func init() { allSuites = append(allSuites, new(SBOMSuite)) } ================================================ FILE: internal/integration/api/security.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // SecuritySuite verifies the security state resource. type SecuritySuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName returns the name of the suite. func (suite *SecuritySuite) SuiteName() string { return "api.SecuritySuite" } // SetupTest sets up the test. func (suite *SecuritySuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute) if suite.Cluster != nil && suite.Cluster.Provisioner() == base.ProvisionerDocker { suite.T().Skip("skipping Security test since provisioner is not docker") } } // TearDownTest tears down the test. func (suite *SecuritySuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestSecurityState verifies that the security state resource is present and has valid values. func (suite *SecuritySuite) TestSecurityState() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{runtimeres.SecurityStateID}, func(r *runtimeres.SecurityState, asrt *assert.Assertions) { asrt.True(r.TypedSpec().ModuleSignatureEnforced, "module signature enforcement should be enabled") }, ) } func init() { allSuites = append(allSuites, &SecuritySuite{}) } ================================================ FILE: internal/integration/api/selinux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bytes" "context" "io" "maps" "path/filepath" "slices" "strconv" "strings" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // SELinuxSuite ... type SELinuxSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *SELinuxSuite) SuiteName() string { return "api.SELinuxSuite" } // SetupTest ... func (suite *SELinuxSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 15*time.Second) if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping SELinux test since provisioner is not qemu") } } // TearDownTest ... func (suite *SELinuxSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } func (suite *SELinuxSuite) getLabel(nodeCtx context.Context, pid int32) string { r, err := suite.Client.Read(nodeCtx, filepath.Join("/proc", strconv.Itoa(int(pid)), "attr/current")) suite.Require().NoError(err) value, err := io.ReadAll(r) suite.Require().NoError(err) suite.Require().NoError(r.Close()) return string(bytes.TrimSpace(value)) } // TestFileMountLabels reads labels of runtime-created files and mounts from xattrs // to ensure SELinux labels for files are set when they are created and FS's are mounted with correct labels. // FIXME: cancel the test in case system was upgraded. func (suite *SELinuxSuite) TestFileMountLabels() { workers := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker) controlplanes := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) expectedLabelsWorker := map[string]string{ // Mounts constants.SystemPath: constants.SystemSelinuxLabel, constants.EphemeralMountPoint: constants.EphemeralSelinuxLabel, constants.StateMountPoint: constants.SystemSelinuxLabel, constants.SystemVarPath: constants.SystemVarSelinuxLabel, constants.RunPath: constants.RunSelinuxLabel, "/run/containerd": "system_u:object_r:pod_containerd_run_t:s0", "/run/lock": "system_u:object_r:var_lock_t:s0", constants.SystemRunPath: "system_u:object_r:system_run_t:s0", "/var/run": constants.RunSelinuxLabel, // Runtime files constants.APIRuntimeSocketPath: constants.APIRuntimeSocketLabel, constants.DBusClientSocketPath: constants.DBusClientSocketLabel, constants.UdevRulesPath: constants.UdevRulesLabel, constants.DBusServiceSocketPath: constants.DBusServiceSocketLabel, constants.MachineSocketPath: constants.MachineSocketLabel, // Overlays "/etc/cni": constants.CNISELinuxLabel, constants.KubernetesConfigBaseDir: constants.KubernetesConfigSELinuxLabel, "/usr/libexec/kubernetes": constants.KubeletPluginsSELinuxLabel, "/opt": constants.OptSELinuxLabel, "/opt/cni": "system_u:object_r:cni_plugin_t:s0", "/opt/containerd": "system_u:object_r:containerd_plugin_t:s0", // Directories "/var/lib/containerd": "system_u:object_r:containerd_state_t:s0", "/var/lib/cni": "system_u:object_r:cni_state_t:s0", "/var/lib/kubelet": "system_u:object_r:kubelet_state_t:s0", "/var/lib/kubelet/seccomp": "system_u:object_r:seccomp_profile_t:s0", constants.LogMountPoint: "system_u:object_r:var_log_t:s0", "/var/log/audit": "system_u:object_r:audit_log_t:s0", constants.KubernetesAuditLogDir: "system_u:object_r:kube_log_t:s0", "/var/log/containers": "system_u:object_r:containers_log_t:s0", "/var/log/pods": "system_u:object_r:pods_log_t:s0", // Mounts and runtime-generated files "/etc": constants.EtcSelinuxLabel, constants.SystemEtcPath: constants.EtcSelinuxLabel, } // Only running on controlplane expectedLabelsControlPlane := map[string]string{ constants.EtcdPKIPath: constants.EtcdPKISELinuxLabel, constants.EtcdDataPath: constants.EtcdDataSELinuxLabel, constants.KubernetesAPIServerConfigDir: constants.KubernetesAPIServerConfigDirSELinuxLabel, constants.KubernetesAPIServerSecretsDir: constants.KubernetesAPIServerSecretsDirSELinuxLabel, constants.KubernetesControllerManagerSecretsDir: constants.KubernetesControllerManagerSecretsDirSELinuxLabel, constants.KubernetesSchedulerConfigDir: constants.KubernetesSchedulerConfigDirSELinuxLabel, constants.KubernetesSchedulerSecretsDir: constants.KubernetesSchedulerSecretsDirSELinuxLabel, constants.TrustdRuntimeSocketPath: constants.TrustdRuntimeSocketLabel, } maps.Copy(expectedLabelsControlPlane, expectedLabelsWorker) // Devices labeled by subsystems, labeled by udev expectedLabelsDevices := map[string]string{ "/dev/rtc0": "system_u:object_r:rtc_device_t:s0", "/dev/tpm0": "system_u:object_r:tpm_device_t:s0", "/dev/tpmrm0": "system_u:object_r:tpm_device_t:s0", "/dev/watchdog": "system_u:object_r:wdt_device_t:s0", "/dev/watchdog0": "system_u:object_r:wdt_device_t:s0", "/dev/null": "system_u:object_r:null_device_t:s0", "/dev/zero": "system_u:object_r:null_device_t:s0", } suite.checkFileLabels(workers, expectedLabelsWorker, false) suite.checkFileLabels(controlplanes, expectedLabelsControlPlane, false) suite.checkFileLabels(workers, expectedLabelsDevices, true) suite.checkFileLabels(controlplanes, expectedLabelsDevices, true) } //nolint:gocyclo func (suite *SELinuxSuite) checkFileLabels(nodes []string, expectedLabels map[string]string, allowMissing bool) { paths := make([]string, 0, len(expectedLabels)) for k := range expectedLabels { paths = append(paths, k) } for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node) cmdline := suite.ReadCmdline(nodeCtx) seLinuxEnabled := pointer.SafeDeref(procfs.NewCmdline(cmdline).Get(constants.KernelParamSELinux).First()) != "" if !seLinuxEnabled { suite.T().Skip("skipping SELinux test since SELinux is disabled") } extensions, err := safe.StateListAll[*runtimeres.ExtensionStatus](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) if extensions.Len() > 0 { suite.T().Skip("skipping SELinux test since extensions are running") } for path, label := range expectedLabels { req := &machineapi.ListRequest{ Root: path, ReportXattrs: true, } stream, err := suite.Client.LS(nodeCtx, req) suite.Require().NoError(err) err = helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, node string, multipleNodes bool) error { // E.g. /var/lib should inherit /var label, while /var/run is a new mountpoint if slices.Contains(paths, info.Name) && info.Name != path { return nil } if slices.Contains( []string{ constants.RunPath, constants.SystemRunPath, "/run/containerd", "/var/run", "/var/log/containers", }, path, ) && info.Name != path { return nil } // these are symlinks that comes from files from extensions, and we don't set xattrs for extensions yet // TODO(frezbo): update the test to check for correct labels once we set xattrs for extensions if info.Name == "/etc/ld.so.conf" || info.Name == "/etc/ld.so.cache" || info.Name == "/usr/bin/nvidia-smi" { return nil } suite.Require().NotNil(info.Xattrs, "expected %s to have xattrs (checking %s)", info.Name, path) found := false for _, l := range info.Xattrs { if l.Name == "security.selinux" { got := string(bytes.Trim(l.Data, "\x00\n")) suite.Require().Contains(got, label, "expected %s to have label %s, got %s (checking %s)", info.Name, label, got, path) found = true break } } suite.Require().True(found, "expected to find security.selinux xattr for %s (checking %s)", info.Name, path) return nil }) if allowMissing { if err != nil { suite.Require().Contains(err.Error(), "lstat") suite.Require().Contains(err.Error(), "no such file or directory") } } else { suite.Require().NoError(err) } } } } // TestProcessLabels reads labels of system processes from procfs // to ensure SELinux labels for processes are correctly set // //nolint:gocyclo func (suite *SELinuxSuite) TestProcessLabels() { nodes := suite.DiscoverNodeInternalIPs(suite.ctx) for _, node := range nodes { nodeCtx := client.WithNode(suite.ctx, node) cmdline := suite.ReadCmdline(nodeCtx) seLinuxEnabled := pointer.SafeDeref(procfs.NewCmdline(cmdline).Get(constants.KernelParamSELinux).First()) != "" if !seLinuxEnabled { suite.T().Skip("skipping SELinux test since SELinux is disabled") } r, err := suite.Client.Processes(nodeCtx) suite.Require().NoError(err) for _, msg := range r.Messages { procs := msg.Processes for _, p := range procs { switch p.Command { case "systemd-udevd": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelUdevd, ) case "dashboard": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelDashboard, ) case "containerd": if strings.Contains(p.Args, "/system/run/containerd") { suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelSystemRuntime, ) } else { suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelPodRuntime, ) } case "init": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelMachined, ) case "kubelet": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelKubelet, ) case "apid": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelApid, ) case "trustd": suite.Require().Contains( suite.getLabel(nodeCtx, p.Pid), constants.SelinuxLabelTrustd, ) } } } } } // TestSecurityState validates SecurityState in accordance to -talos.enforcing. func (suite *SELinuxSuite) TestSecurityState() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { nodeCtx := client.WithNode(suite.ctx, node) cmdline := suite.ReadCmdline(nodeCtx) seLinuxEnabled := pointer.SafeDeref(procfs.NewCmdline(cmdline).Get(constants.KernelParamSELinux).First()) != "" if !seLinuxEnabled { continue } rtestutils.AssertResource( nodeCtx, suite.T(), suite.Client.COSI, runtimeres.SecurityStateID, func(state *runtimeres.SecurityState, asrt *assert.Assertions) { if suite.SelinuxEnforcing { asrt.Equal(runtimeres.SELinuxStateEnforcing, state.TypedSpec().SELinuxState) } else { asrt.Equal(runtimeres.SELinuxStatePermissive, state.TypedSpec().SELinuxState) } }, ) } } // TODO: test for system and CRI container labels // TODO: test labels for unconfined system extensions, pods // TODO: test for no avc denials in dmesg // TestNoPtrace confirms ptracing system processes is prohibited in enforcing mode. func (suite *SELinuxSuite) TestNoPtrace() { if !suite.SelinuxEnforcing { suite.T().Skip("skipping SELinux negative tests in permissive mode") } podDef, err := suite.NewPrivilegedPod("pid1-ptrace-test") suite.Require().NoError(err) podDef = podDef.WithQuiet(true) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck _, stderr, err := podDef.Exec( suite.ctx, "apk add --update strace", ) suite.Assert().NoError(err) suite.Assert().Empty(stderr, "stderr: %s", stderr) // if attached, timeout ctx, cancel := context.WithTimeout(suite.ctx, time.Second*5) defer cancel() _, stderr, err = podDef.Exec( ctx, "strace -p 1", ) // in case of successful attach it will be context.DeadlineExceeded suite.Require().Error(err) suite.Assert().ErrorContains(err, "command terminated with exit code 1") // strace first tests ptrace against itself, which we also deny currently suite.Assert().Contains(stderr, "strace: do_test_ptrace_get_syscall_info: PTRACE_TRACEME: Permission denied") suite.Assert().Contains(stderr, "strace: attach: ptrace(PTRACE_SEIZE, 1): Permission denied") suite.Assert().NotContains(stderr, "attached") } // TestNoMachineSocketAccess confirms pods cannot reach machined socket (not apid, but unsecured one). func (suite *SELinuxSuite) TestNoMachineSocketAccess() { if !suite.SelinuxEnforcing { suite.T().Skip("skipping SELinux negative tests in permissive mode") } podDef, err := suite.NewPrivilegedPod("pid1-socket-test") suite.Require().NoError(err) podDef = podDef.WithQuiet(true) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck _, stderr, err := podDef.Exec( suite.ctx, "apk add --update socat", ) suite.Assert().NoError(err) suite.Assert().Empty(stderr, "stderr: %s", stderr) // if attached, timeout ctx, cancel := context.WithTimeout(suite.ctx, time.Second*5) defer cancel() _, stderr, err = podDef.Exec( ctx, "socat - UNIX-CONNECT:/host/system/run/machined/machine.sock", ) // in case of successful attach it will be context.DeadlineExceeded suite.Require().Error(err) suite.Assert().ErrorContains(err, "command terminated with exit code 1") suite.Assert().Contains(stderr, "Permission denied") } // TestNoStateAccess verifies mounting STATE does not allow /system/state/config.yaml access. func (suite *SELinuxSuite) TestNoStateAccess() { if !suite.SelinuxEnforcing { suite.T().Skip("skipping SELinux negative tests in permissive mode") } node := suite.RandomDiscoveredNodeInternalIP() nodeCtx := client.WithNode(suite.ctx, node) state, err := safe.StateGetByID[*block.VolumeStatus](nodeCtx, suite.Client.COSI, "STATE") suite.Assert().NoError(err) podDef, err := suite.NewPrivilegedPod("system-state-test") suite.Require().NoError(err) podDef = podDef.WithQuiet(true) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck _, stderr, err := podDef.Exec( suite.ctx, "mount "+state.TypedSpec().MountLocation+" /mnt", ) suite.Assert().NoError(err) suite.Assert().Empty(stderr, "stderr: %s", stderr) _, stderr, err = podDef.Exec( suite.ctx, "cat /mnt/config.yaml", ) suite.Require().Error(err) suite.Assert().ErrorContains(err, "command terminated with exit code 1") suite.Assert().Contains(stderr, "cat: can't open '/mnt/config.yaml': Permission denied") } func init() { allSuites = append(allSuites, new(SELinuxSuite)) } ================================================ FILE: internal/integration/api/serviceaccount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "time" "github.com/siderolabs/go-retry/retry" corev1 "k8s.io/api/core/v1" eventsv1 "k8s.io/api/events/v1" kubeerrors "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" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" ) var ( serviceAccountGVR = schema.GroupVersionResource{ Group: constants.ServiceAccountResourceGroup, Version: constants.ServiceAccountResourceVersion, Resource: constants.ServiceAccountResourcePlural, } secretGVR = schema.GroupVersionResource{ Group: "", Version: "v1", Resource: "secrets", } jobGVR = schema.GroupVersionResource{ Group: "batch", Version: "v1", Resource: "jobs", } ) // ServiceAccountSuite verifies Talos ServiceAccount. type ServiceAccountSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *ServiceAccountSuite) SuiteName() string { return "api.ServiceAccountSuite" } // SetupTest ... func (suite *ServiceAccountSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) suite.ClearConnectionRefused(suite.ctx, suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker)...) suite.AssertClusterHealthy(suite.ctx) } // TearDownTest ... func (suite *ServiceAccountSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestValid tests Kubernetes service accounts. func (suite *ServiceAccountSuite) TestValid() { name := "test-valid" err := suite.configureAPIAccess(true, []string{"os:reader"}, []string{"kube-system"}) suite.Assert().NoError(err) _, err = suite.getCRD() suite.Assert().NoError(err) sa, err := suite.createServiceAccount("kube-system", name, []string{"os:reader"}) suite.Assert().NoError(err) defer suite.DeleteResource(suite.ctx, serviceAccountGVR, "default", name) //nolint:errcheck err = suite.WaitForEventExists(suite.ctx, "kube-system", func(event eventsv1.Event) bool { return event.Regarding.UID == sa.GetUID() && event.Type == corev1.EventTypeNormal && event.Reason == "Synced" }) suite.Assert().NoError(err) secret, err := suite.waitForSecret("kube-system", name) suite.Assert().NoError(err) talosConfig := secret.Data["config"] conf, err := config.FromBytes(talosConfig) suite.Assert().NoError(err) expectedServiceName := fmt.Sprintf( "%s.%s", constants.KubernetesTalosAPIServiceName, constants.KubernetesTalosAPIServiceNamespace, ) suite.Assert().Equal([]string{expectedServiceName}, conf.Contexts[conf.Context].Endpoints) node := suite.RandomDiscoveredNodeInternalIP() _, err = suite.creteTestJob("kube-system", name, name, node) suite.Assert().NoError(err) err = suite.waitForJobReady(2*time.Minute, "kube-system", name) suite.Assert().NoError(err) err = suite.DeleteResource(suite.ctx, jobGVR, "kube-system", name) suite.Require().NoError(err) err = suite.EnsureResourceIsDeleted(suite.ctx, 30*time.Second, jobGVR, "kube-system", name) suite.Assert().NoError(err) err = suite.DeleteResource(suite.ctx, serviceAccountGVR, "kube-system", name) suite.Require().NoError(err) err = suite.EnsureResourceIsDeleted(suite.ctx, 30*time.Second, secretGVR, "kube-system", name) suite.Assert().NoError(err) } // TestNotAllowedNamespace tests Kubernetes service accounts in not allowed namespaces. func (suite *ServiceAccountSuite) TestNotAllowedNamespace() { name := "test-allowed-ns" err := suite.configureAPIAccess(true, []string{"os:reader"}, []string{"kube-system"}) suite.Require().NoError(err) sa, err := suite.createServiceAccount("default", name, []string{"os:reader"}) suite.Require().NoError(err) defer suite.DeleteResource(suite.ctx, serviceAccountGVR, "default", name) //nolint:errcheck err = suite.WaitForEventExists(suite.ctx, "default", func(event eventsv1.Event) bool { return event.Regarding.UID == sa.GetUID() && event.Type == corev1.EventTypeWarning && event.Reason == "ErrNamespaceNotAllowed" }) suite.Require().NoError(err) } // TestNotAllowedRoles tests Kubernetes service accounts with not allowed roles. func (suite *ServiceAccountSuite) TestNotAllowedRoles() { name := "test-not-allowed-roles" err := suite.configureAPIAccess(true, []string{"os:reader"}, []string{"kube-system"}) suite.Assert().NoError(err) sa, err := suite.createServiceAccount("kube-system", name, []string{"os:admin"}) suite.Assert().NoError(err) suite.Assert().NotNil(sa) defer suite.DeleteResource(suite.ctx, serviceAccountGVR, "kube-system", name) //nolint:errcheck err = suite.WaitForEventExists(suite.ctx, "kube-system", func(event eventsv1.Event) bool { return event.Regarding.UID == sa.GetUID() && event.Type == corev1.EventTypeWarning && event.Reason == "ErrRolesNotAllowed" }) suite.Assert().NoError(err) } // TestFeatureNotEnabled tests Kubernetes service accounts when API access feature is not enabled. func (suite *ServiceAccountSuite) TestFeatureNotEnabled() { name := "test-feature-not-enabled" err := suite.configureAPIAccess(false, []string{"os:reader"}, []string{"kube-system"}) suite.Assert().NoError(err) sa, err := suite.createServiceAccount("kube-system", name, []string{"os:reader"}) if kubeerrors.IsNotFound(err) { // CRD is not created because the feature was never enabled, all good return } suite.Assert().NoError(err) suite.Assert().NotNil(sa) defer suite.DeleteResource(suite.ctx, serviceAccountGVR, "kube-system", name) //nolint:errcheck err = suite.WaitForEventExists(suite.ctx, "kube-system", func(event eventsv1.Event) bool { return event.Regarding.UID == sa.GetUID() && event.Type == corev1.EventTypeWarning && event.Reason == "ErrAccessNotEnabled" }) suite.Assert().NoError(err) } func (suite *ServiceAccountSuite) waitForSecret(ns, name string) (*corev1.Secret, error) { var ( secret *corev1.Secret err error ) err = retry.Constant(1*time.Minute).RetryWithContext(suite.ctx, func(ctx context.Context) error { secret, err = suite.Clientset.CoreV1().Secrets(ns).Get(suite.ctx, name, metav1.GetOptions{}) if kubeerrors.IsNotFound(err) { return retry.ExpectedError(err) } return err }) if err != nil { return nil, err } return secret, nil } func (suite *ServiceAccountSuite) getCRD() (*unstructured.Unstructured, error) { crdName := fmt.Sprintf("%s.%s", constants.ServiceAccountResourcePlural, constants.ServiceAccountResourceGroup) return suite.DynamicClient.Resource(schema.GroupVersionResource{ Group: "apiextensions.k8s.io", Version: "v1", Resource: "customresourcedefinitions", }).Get(suite.ctx, crdName, metav1.GetOptions{}) } func (suite *ServiceAccountSuite) createServiceAccount(ns string, name string, roles []string) (*unstructured.Unstructured, error) { return suite.DynamicClient.Resource(serviceAccountGVR).Namespace(ns).Create(suite.ctx, &unstructured.Unstructured{ Object: map[string]any{ "apiVersion": fmt.Sprintf("%s/%s", constants.ServiceAccountResourceGroup, constants.ServiceAccountResourceVersion), "kind": constants.ServiceAccountResourceKind, "metadata": map[string]any{ "name": name, }, "spec": map[string]any{ "roles": roles, }, }, }, metav1.CreateOptions{}) } func (suite *ServiceAccountSuite) creteTestJob(ns, name, serviceAccount, node string) (*unstructured.Unstructured, error) { return suite.DynamicClient.Resource(jobGVR).Namespace(ns).Create(suite.ctx, &unstructured.Unstructured{ Object: map[string]any{ "apiVersion": fmt.Sprintf("%s/%s", jobGVR.Group, jobGVR.Version), "kind": "Job", "metadata": map[string]any{ "name": name, }, "spec": map[string]any{ "template": map[string]any{ "spec": map[string]any{ "restartPolicy": "Never", "volumes": []map[string]any{ { "name": "talos-secrets", "secret": map[string]any{ "secretName": serviceAccount, }, }, }, "containers": []map[string]any{ { "name": "talosctl", "image": "ghcr.io/siderolabs/talosctl:v1.12.4", // sync with cmd/talos/image.go list "args": []string{ "--nodes", node, "version", }, "volumeMounts": []map[string]any{ { "mountPath": "/var/run/secrets/talos.dev", "name": "talos-secrets", }, }, }, }, }, }, }, }, }, metav1.CreateOptions{}) } func (suite *ServiceAccountSuite) waitForJobReady(duration time.Duration, ns, name string) error { cli := suite.DynamicClient.Resource(jobGVR).Namespace(ns) return retry.Constant(duration).RetryWithContext(suite.ctx, func(ctx context.Context) error { job, err := cli.Get(ctx, name, metav1.GetOptions{}) if kubeerrors.IsNotFound(err) { return retry.ExpectedError(fmt.Errorf("job %s/%s not found", ns, name)) } else if err != nil { return err } if job.Object["status"] == nil { return retry.ExpectedError(fmt.Errorf("job %s/%s status is not set", ns, name)) } status := job.Object["status"].(map[string]any) if status["succeeded"] == nil || status["succeeded"].(int64) == 0 { return retry.ExpectedError(fmt.Errorf("job %s/%s is not ready yet", ns, name)) } return nil }) } // configureAPIAccess configures the API access feature on all control plane nodes. func (suite *ServiceAccountSuite) configureAPIAccess( enabled bool, allowedRoles []string, allowedNamespaces []string, ) error { controlPlaneIPs := suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) for _, ip := range controlPlaneIPs { nodeCtx := client.WithNode(suite.ctx, ip) nodeConfig, err := suite.ReadConfigFromNode(nodeCtx) if err != nil { return err } bytes := suite.PatchV1Alpha1Config(nodeConfig, func(nodeConfigRaw *v1alpha1.Config) { accessConfig := v1alpha1.KubernetesTalosAPIAccessConfig{ AccessEnabled: new(enabled), AccessAllowedRoles: allowedRoles, AccessAllowedKubernetesNamespaces: allowedNamespaces, } nodeConfigRaw.MachineConfig.MachineFeatures.KubernetesTalosAPIAccessConfig = &accessConfig }) _, err = suite.Client.ApplyConfiguration(nodeCtx, &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) if err != nil { return err } } if enabled { // wait for CRD and the Talos endpoint to be created return retry.Constant(30*time.Second).RetryWithContext(suite.ctx, func(ctx context.Context) error { _, err := suite.getCRD() if err != nil { return retry.ExpectedError(err) } _, err = suite.Clientset.CoreV1(). Services(constants.KubernetesTalosAPIServiceNamespace). Get(suite.ctx, constants.KubernetesTalosAPIServiceName, metav1.GetOptions{}) if err != nil { return retry.ExpectedError(err) } return nil }) } return nil } func init() { allSuites = append(allSuites, new(ServiceAccountSuite)) } ================================================ FILE: internal/integration/api/siderolink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "net/url" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" siderolinkconfig "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) // SideroLinkSuite ... type SideroLinkSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *SideroLinkSuite) SuiteName() string { return "api.SideroLinkSuite" } // SetupTest ... func (suite *SideroLinkSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) } // TearDownTest ... func (suite *SideroLinkSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestTunnelSettingFlip enables/disables the tunnel-over-GRPC setting of the link. func (suite *SideroLinkSuite) TestTunnelSettingFlip() { // pick up a random node to test the SideroLink on, and use it throughout the test node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("testing SideroLink on node %s", node) // build a Talos API context which is tied to the node nodeCtx := client.WithNode(suite.ctx, node) // check if SideroLink is enabled sideroLinkConfig, err := safe.StateGetByID[*siderolink.Config](nodeCtx, suite.Client.COSI, siderolink.ConfigID) if err != nil { if state.IsNotFoundError(err) { suite.T().Skip("skipping the test since SideroLink is not enabled") } suite.Require().NoError(err) } // assert that siderolink is connected rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, siderolink.StatusID, func(status *siderolink.Status, asrt *assert.Assertions) { asrt.True(status.TypedSpec().Connected, "SideroLink is not connected") }) apiURL, err := url.Parse(sideroLinkConfig.TypedSpec().APIEndpoint) suite.Require().NoError(err) q := apiURL.Query() // flip the tunnel setting if sideroLinkConfig.TypedSpec().Tunnel { q.Del("grpc_tunnel") suite.T().Log("flipping the tunnel setting to false") } else { q.Set("grpc_tunnel", "true") suite.T().Log("flipping the tunnel setting to true") } apiURL.RawQuery = q.Encode() cfgDocument := siderolinkconfig.NewConfigV1Alpha1() cfgDocument.APIUrlConfig.URL = apiURL // patch settings suite.PatchMachineConfig(nodeCtx, cfgDocument) // first, the config should be updated rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, siderolink.ConfigID, func(config *siderolink.Config, asrt *assert.Assertions) { asrt.Equal(!sideroLinkConfig.TypedSpec().Tunnel, config.TypedSpec().Tunnel, "SideroLink tunnel setting is not updated") }) suite.T().Log("configuration updated, waiting for SideroLink to reconnect...") // second, new status should reflect the change rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, siderolink.StatusID, func(status *siderolink.Status, asrt *assert.Assertions) { asrt.True(status.TypedSpec().Connected, "SideroLink is not connected") asrt.Equal(!sideroLinkConfig.TypedSpec().Tunnel, status.TypedSpec().GRPCTunnel, "SideroLink tunnel setting is not updated") }) } func init() { allSuites = append(allSuites, new(SideroLinkSuite)) } ================================================ FILE: internal/integration/api/testdata/nodeport.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: labels: app: test-nginx name: test-nginx namespace: default spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: test-nginx strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: app: test-nginx spec: containers: - image: nginx imagePullPolicy: Always name: nginx ports: - containerPort: 80 protocol: TCP resources: limits: cpu: 100m memory: 128Mi requests: cpu: 100m memory: 128Mi restartPolicy: Always terminationGracePeriodSeconds: 5 --- apiVersion: v1 kind: Service metadata: labels: app: test-nginx name: test-nginx namespace: default spec: ipFamilies: - IPv4 ipFamilyPolicy: SingleStack ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-nginx type: NodePort ================================================ FILE: internal/integration/api/testdata/nvidia-gpu-operator.yaml ================================================ driver: enabled: false toolkit: enabled: false hostPaths: driverInstallDir: /usr/local/glibc/usr ================================================ FILE: internal/integration/api/trusted-roots.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "io" "strings" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/constants" ) // TrustedRootsSuite ... type TrustedRootsSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *TrustedRootsSuite) SuiteName() string { return "api.TrustedRootsSuite" } // SetupTest ... func (suite *TrustedRootsSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute) } // TearDownTest ... func (suite *TrustedRootsSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } func (suite *TrustedRootsSuite) readTrustedRoots(nodeCtx context.Context) string { r, err := suite.Client.Read(nodeCtx, constants.DefaultTrustedCAFile) suite.Require().NoError(err) value, err := io.ReadAll(r) suite.Require().NoError(err) suite.Require().NoError(r.Close()) return string(value) } // TestTrustedRoots verifies default and custom trusted CA roots. func (suite *TrustedRootsSuite) TestTrustedRoots() { // pick up a random node to test the TrustedRoots on, and use it throughout the test node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("testing TrustedRoots on node %s", node) // build a Talos API context which is tied to the node nodeCtx := client.WithNode(suite.ctx, node) const name = "test-ca" cfgDocument := security.NewTrustedRootsConfigV1Alpha1() cfgDocument.MetaName = name cfgDocument.Certificates = "--- BEGIN CERTIFICATE ---\nMIIC0DCCAbigAwIBAgIUI\n--- END CERTIFICATE ---\n" // clean up custom config if it exists suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) certificates := suite.readTrustedRoots(nodeCtx) suite.Require().Contains(certificates, "Bundle of CA Root Certificates") // enable custom trusted roots suite.PatchMachineConfig(nodeCtx, cfgDocument) suite.Require().Eventually(func() bool { return strings.Contains(suite.readTrustedRoots(nodeCtx), name) }, 5*time.Second, 100*time.Millisecond) // deactivate the TrustedRoots suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) suite.Require().Eventually(func() bool { return !strings.Contains(suite.readTrustedRoots(nodeCtx), name) }, 5*time.Second, 100*time.Millisecond) } func init() { allSuites = append(allSuites, new(TrustedRootsSuite)) } ================================================ FILE: internal/integration/api/trustedboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bufio" "bytes" "context" "io" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // TrustedBootSuite verifies Talos is securebooted. type TrustedBootSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *TrustedBootSuite) SuiteName() string { return "api.TrustedBootSuite" } // SetupTest ... func (suite *TrustedBootSuite) SetupTest() { if !suite.TrustedBoot { suite.T().Skip("skipping since talos.trustedboot is false") } // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 10*time.Minute) } // TearDownTest ... func (suite *TrustedBootSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestTrustedBootState verifies that the system is booted in secure boot mode // and that the disks are encrypted. func (suite *TrustedBootSuite) TestTrustedBootState() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{runtimeres.SecurityStateID}, func(r *runtimeres.SecurityState, asrt *assert.Assertions) { asrt.True(r.TypedSpec().SecureBoot) }, ) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{constants.StatePartitionLabel, constants.EphemeralPartitionLabel}, func(r *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equal(block.VolumePhaseReady, r.TypedSpec().Phase) asrt.Equal(block.EncryptionProviderLUKS2, r.TypedSpec().EncryptionProvider) }, ) dmesgStream, err := suite.Client.Dmesg( suite.ctx, false, false, ) suite.Require().NoError(err) logReader, err := client.ReadStream(dmesgStream) suite.Require().NoError(err) var dmesg bytes.Buffer _, err = io.Copy(bufio.NewWriter(&dmesg), logReader) suite.Require().NoError(err) suite.Require().Contains(dmesg.String(), "Secure boot enabled") } // TestEncryptionConfigRotate verifies that the encryption supports locking and unlocking to different PCRs. func (suite *TrustedBootSuite) TestEncryptionConfigRotate() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) suite.ClearConnectionRefused(suite.ctx, node) nodeCtx := client.WithNode(suite.ctx, node) provider, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) ephemeralCfg, _ := provider.Volumes().ByName(constants.EphemeralPartitionLabel) encryption := ephemeralCfg.Encryption() suite.Require().NotNil(encryption, "encryption config must be set for EPHEMERAL in trustedboot test") suite.WaitForBootDone(suite.ctx) suite.T().Logf("testing encryption key rotation on node %s", node) cfg, ok := encryption.(blockcfg.EncryptionSpec) suite.Require().True(ok, "expected blockcfg.EncryptionSpec, got %T", encryption) // when we start the test we do not know the current encryption provider in use // so let's read the volumestatus to get the information about the slot in use and whether lockToState is set volumeStatus, err := safe.StateGetByID[*block.VolumeStatus](nodeCtx, suite.Client.COSI, constants.EphemeralPartitionLabel) suite.Require().NoError(err) existingEncryptionSlotInUse := *volumeStatus.TypedSpec().EncryptionSlot existing := xslices.Filter(cfg.EncryptionKeys, func(key blockcfg.EncryptionKey) bool { return key.Slot() == existingEncryptionSlotInUse })[0] var ( existingPCRs []int expectedPubKeyPCRs []int ) if existing.TPM() != nil { existingPCRs = existing.TPM().PCRs() expectedPubKeyPCRs = existing.TPM().PubKeyPCRs() } nextSlot := existing.Slot() + 1 for _, test := range []struct { keys []blockcfg.EncryptionKey expectedPCRs []int expectedPubKeyPCRs []int expectedLockedToState bool }{ // for the initial set, let's add a new TPM based key with no PCR options specified // in this case after reboot the new slot will be added for the TPM key and the expected PCRs // and lockToState status will be the same as the existing key { keys: []blockcfg.EncryptionKey{ existing, { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), }, KeySlot: nextSlot, KeyLockToSTATE: new(true), }, }, expectedPCRs: existingPCRs, expectedPubKeyPCRs: expectedPubKeyPCRs, expectedLockedToState: existing.LockToSTATE(), }, // now remove the existing key and add a new TPM based key with no PCRs specified // in this case after a reboot we should have default TPM based encryption values // i.e. PCR is SecureBootStatePCR and lockToState is true { keys: []blockcfg.EncryptionKey{ { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), }, KeySlot: nextSlot, KeyLockToSTATE: new(true), }, }, expectedPCRs: []int{constants.SecureBootStatePCR}, expectedPubKeyPCRs: []int{constants.UKIPCR}, expectedLockedToState: true, }, // now keep the previous TPM based key with no PCRs specified and add a new key with PCRs set // to empty and lockToState set to false, after reboot we should have default TPM based encryption values // i.e. PCR is SecureBootStatePCR and lockToState is true { keys: []blockcfg.EncryptionKey{ { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), }, KeySlot: nextSlot, KeyLockToSTATE: new(true), }, { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), TPMOptions: &blockcfg.EncryptionKeyTPMOptions{ PCRs: []int{}, }, }, KeySlot: nextSlot + 1, KeyLockToSTATE: new(false), }, }, expectedPCRs: []int{constants.SecureBootStatePCR}, expectedPubKeyPCRs: []int{constants.UKIPCR}, expectedLockedToState: true, }, // now only keep the TPM key with PCRs set to empty and lockToState set to false // in this case after a reboot we should have no PCRs and lockToState is false { keys: []blockcfg.EncryptionKey{ { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), TPMOptions: &blockcfg.EncryptionKeyTPMOptions{ PCRs: []int{}, }, }, KeySlot: nextSlot + 1, KeyLockToSTATE: new(false), }, }, expectedPCRs: nil, expectedPubKeyPCRs: []int{constants.UKIPCR}, expectedLockedToState: false, }, // now keep the previous TPM based key with PCRs set to empty and lockToState set to false // and add a new key with PCRs set to [0, SecureBootStatePCR] and lockToState set to true // in this case after a reboot we should have no PCRs and lockToState is false { keys: []blockcfg.EncryptionKey{ { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), TPMOptions: &blockcfg.EncryptionKeyTPMOptions{ PCRs: []int{}, }, }, KeySlot: nextSlot + 1, KeyLockToSTATE: new(false), }, { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), TPMOptions: &blockcfg.EncryptionKeyTPMOptions{ PCRs: []int{0, constants.SecureBootStatePCR}, }, }, KeySlot: nextSlot + 2, KeyLockToSTATE: new(true), }, }, expectedPCRs: nil, expectedPubKeyPCRs: []int{constants.UKIPCR}, expectedLockedToState: false, }, // now only keep the TPM key with PCRs set to [0, SecureBootStatePCR] and lockToState set to true // in this case after a reboot we should have PCRs set to [0, SecureBootStatePCR] and lockToState is true { keys: []blockcfg.EncryptionKey{ { KeyTPM: &blockcfg.EncryptionKeyTPM{ // TPMCheckSecurebootStatusOnEnroll: new(true), TPMOptions: &blockcfg.EncryptionKeyTPMOptions{ PCRs: []int{0, constants.SecureBootStatePCR}, }, }, KeySlot: nextSlot + 2, KeyLockToSTATE: new(true), }, }, expectedPCRs: []int{0, constants.SecureBootStatePCR}, expectedPubKeyPCRs: []int{constants.UKIPCR}, expectedLockedToState: true, }, } { suite.T().Logf("applying encryption keys %s on node %s", toJSONString(suite.T(), test.keys), node) // prepare a patch to apply, first removing existing keys removeKeysPatch := map[string]any{ "apiVersion": "v1alpha1", "kind": "VolumeConfig", "name": constants.EphemeralPartitionLabel, "encryption": map[string]any{ "keys": map[string]any{ "$patch": "delete", }, }, } newEphemeralCfg := blockcfg.NewVolumeConfigV1Alpha1() newEphemeralCfg.MetaName = constants.EphemeralPartitionLabel newEphemeralCfg.EncryptionSpec.EncryptionKeys = test.keys // right now, patching encryption keys doesn't reboot and doesn't rotate the secrets either suite.PatchMachineConfig(nodeCtx, removeKeysPatch, newEphemeralCfg) suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, assertRebootedRebootTimeout, suite.CleanupFailedPods, ) suite.ClearConnectionRefused(suite.ctx, node) suite.WaitForBootDone(suite.ctx) rtestutils.AssertResource(nodeCtx, suite.T(), suite.Client.COSI, constants.EphemeralPartitionLabel, func(r *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equal(test.expectedPCRs, r.TypedSpec().TPMEncryptionOptions.PCRs) asrt.Equal(test.expectedPubKeyPCRs, r.TypedSpec().TPMEncryptionOptions.PubKeyPCRs) asrt.Equal(test.expectedLockedToState, r.TypedSpec().EncryptionLockedToState) }) } } func init() { allSuites = append(allSuites, &TrustedBootSuite{}) } ================================================ FILE: internal/integration/api/uki.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // UKISuite verifies Talos is booted from a UKI. type UKISuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName returns the name of the suite. func (suite *UKISuite) SuiteName() string { return "api.UKISuite" } // SetupTest sets up the test. func (suite *UKISuite) SetupTest() { if suite.Cluster != nil && suite.Cluster.Provisioner() == base.ProvisionerDocker { suite.T().Skip("skipping uki booted test since docker provisioner does not support UKI") } if !suite.VerifyUKIBooted { suite.T().Skip("skipping uki booted test since talos.verifyukibooted is false") } // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 3*time.Minute) } // TearDownTest tears down the test. func (suite *UKISuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestUKIBooted verifies that the system is booted from a UKI. func (suite *UKISuite) TestUKIBooted() { node := suite.RandomDiscoveredNodeInternalIP() ctx := client.WithNode(suite.ctx, node) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{runtimeres.SecurityStateID}, func(r *runtimeres.SecurityState, asrt *assert.Assertions) { asrt.True(r.TypedSpec().BootedWithUKI) }, ) } func init() { allSuites = append(allSuites, &UKISuite{}) } ================================================ FILE: internal/integration/api/update-hostname.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "strings" "testing" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // UpdateHostnameSuite verifies UpdateHostname API. type UpdateHostnameSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *UpdateHostnameSuite) SuiteName() string { return "api.UpdateHostnameSuite" } // SetupTest ... func (suite *UpdateHostnameSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) } // TearDownTest ... func (suite *UpdateHostnameSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestUpdateHostname updates the hostname of a worker node, // then asserts that the node re-joins the cluster with the new hostname. // It reverts the change at the end of the test and asserts that the node is reported again as Ready. func (suite *UpdateHostnameSuite) TestUpdateHostname() { if testing.Short() { suite.T().Skip("skipping in short mode") } if !suite.Capabilities().SupportsReboot { suite.T().Skip("cluster doesn't support reboot") } nodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, nodeInternalIP) node, err := suite.GetK8sNodeByInternalIP(suite.ctx, nodeInternalIP) suite.Require().NoError(err) // ec2.internal and compute.internal are reserved domains in AWS // ref: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-naming.html#instance-naming-ipbn if strings.HasSuffix(node.Name, ".ec2.internal") || strings.HasSuffix(node.Name, ".compute.internal") { suite.T().Skip("aws does not support hostname changes") } oldHostname := node.Name newHostname := "test-update-hostname" suite.T().Logf("updating hostname of node %q to %q (IP: %s)", oldHostname, newHostname, nodeInternalIP) suite.updateHostname(nodeCtx, newHostname) suite.ClearConnectionRefused(suite.ctx, nodeInternalIP) nodeReady := func(status corev1.ConditionStatus) bool { return status == corev1.ConditionTrue } nodeNotReady := func(status corev1.ConditionStatus) bool { return status != corev1.ConditionTrue } defer func() { suite.T().Logf("reverting hostname of node %q to %q (IP: %s)", newHostname, oldHostname, nodeInternalIP) // revert the hostname back to the original one suite.updateHostname(nodeCtx, oldHostname) suite.T().Logf("waiting for node %q to be ready", oldHostname) // expect node status to be Ready again suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, oldHostname, nodeReady)) suite.T().Logf("deleting node %q", newHostname) // Delete the node with the test hostname err = suite.Clientset.CoreV1().Nodes().Delete(suite.ctx, newHostname, metav1.DeleteOptions{}) suite.Require().NoError(err) suite.T().Logf("rebooting node %s", nodeInternalIP) // Reboot node for CNI bridge to be reconfigured: https://stackoverflow.com/questions/61373366 suite.AssertRebooted( suite.ctx, nodeInternalIP, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 10*time.Minute, suite.CleanupFailedPods, ) }() suite.T().Logf("waiting for node %q to be not ready", oldHostname) // expect node with old hostname to become NotReady suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, oldHostname, nodeNotReady)) suite.T().Logf("waiting for node %q to be ready", newHostname) // expect node with new hostname to become Ready suite.Assert().NoError(suite.WaitForK8sNodeReadinessStatus(suite.ctx, newHostname, nodeReady)) suite.T().Logf("deleting node %q", oldHostname) // Delete the node with the old hostname err = suite.Clientset.CoreV1().Nodes().Delete(suite.ctx, oldHostname, metav1.DeleteOptions{}) suite.Require().NoError(err) } func (suite *UpdateHostnameSuite) updateHostname(nodeCtx context.Context, newHostname string) { hostnameConfig := network.NewHostnameConfigV1Alpha1() hostnameConfig.ConfigAuto = new(nethelpers.AutoHostnameKindOff) hostnameConfig.ConfigHostname = newHostname suite.PatchMachineConfig(nodeCtx, hostnameConfig) } func init() { allSuites = append(allSuites, new(UpdateHostnameSuite)) } ================================================ FILE: internal/integration/api/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" configmachine "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // VersionSuite verifies version API. type VersionSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *VersionSuite) SuiteName() string { return "api.VersionSuite" } // SetupTest ... func (suite *VersionSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) } // TearDownTest ... func (suite *VersionSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestExpectedVersionMaster verifies master node version matches expected. func (suite *VersionSuite) TestExpectedVersionMaster() { v, err := suite.Client.Version(suite.ctx) suite.Require().NoError(err) node := suite.RandomDiscoveredNodeInternalIP(configmachine.TypeControlPlane) clientCtx := client.WithNode(suite.ctx, node) version, err := safe.StateGetByID[*runtime.Version](clientCtx, suite.Client.COSI, "version") suite.Require().NoError(err) suite.Require().NotNil(version) suite.Assert().Equal(v.Messages[0].Version.Tag, version.TypedSpec().Version) suite.Assert().Equal(suite.Version, v.Messages[0].Version.Tag) suite.Assert().Equal(suite.GoVersion, v.Messages[0].Version.GoVersion) } // TestSameVersionCluster verifies that all the nodes are on the same version. func (suite *VersionSuite) TestSameVersionCluster() { nodes := suite.DiscoverNodeInternalIPs(suite.ctx) suite.Require().NotEmpty(nodes) ctx := client.WithNodes(suite.ctx, nodes...) var v *machine.VersionResponse err := retry.Constant( time.Minute, ).Retry(func() error { var e error v, e = suite.Client.Version(ctx) return retry.ExpectedError(e) }) suite.Require().NoError(err) suite.Require().Len(v.Messages, len(nodes)) expectedVersion := v.Messages[0].Version.Tag for _, version := range v.Messages { suite.Assert().Equal(expectedVersion, version.Version.Tag) } } func init() { allSuites = append(allSuites, new(VersionSuite)) } ================================================ FILE: internal/integration/api/volumes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "math/rand" "path/filepath" "slices" "strconv" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" blockcfg "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumesSuite ... type VolumesSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *VolumesSuite) SuiteName() string { return "api.VolumesSuite" } // SetupTest ... func (suite *VolumesSuite) SetupTest() { if !suite.Capabilities().SupportsVolumes { suite.T().Skip("cluster doesn't support volumes") } suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 2*time.Minute) } // TearDownTest ... func (suite *VolumesSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestDiscoveredVolumes verifies that standard Talos partitions are discovered. func (suite *VolumesSuite) TestDiscoveredVolumes() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { suite.testDiscoveredVolumes(node) }) } } func (suite *VolumesSuite) testDiscoveredVolumes(node string) { ctx := client.WithNode(suite.ctx, node) volumes, err := safe.StateListAll[*block.DiscoveredVolume](ctx, suite.Client.COSI) suite.Require().NoError(err) expectedVolumes := map[string]struct { Names []string }{ "META": { Names: []string{"talosmeta", ""}, // if META was never written, it will not be detected }, "STATE": { Names: []string{"xfs"}, }, "EPHEMERAL": { Names: []string{"xfs"}, }, } for dv := range volumes.All() { suite.T().Logf("volume: %s %s %s %s", dv.Metadata().ID(), dv.TypedSpec().Name, dv.TypedSpec().PartitionLabel, dv.TypedSpec().Label) partitionLabel := dv.TypedSpec().PartitionLabel filesystemLabel := dv.TypedSpec().Label // this is encrypted partition, skip it, we should see another device with the actual filesystem if dv.TypedSpec().Name == "luks" { continue } // match either by partition or filesystem label id := partitionLabel expected, ok := expectedVolumes[id] if !ok { id = filesystemLabel expected, ok = expectedVolumes[id] if !ok { continue } } suite.Assert().Contains(expected.Names, dv.TypedSpec().Name, "node: %s", node) delete(expectedVolumes, id) } suite.Assert().Empty(expectedVolumes, "node: ", node) if suite.T().Failed() { suite.DumpLogs(suite.ctx, node, "controller-runtime", "block.") } } // TestSystemDisk verifies that Talos system disk is discovered. func (suite *VolumesSuite) TestSystemDisk() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) systemDisk, err := safe.StateGetByID[*block.SystemDisk](ctx, suite.Client.COSI, block.SystemDiskID) suite.Require().NoError(err) suite.Assert().NotEmpty(systemDisk.TypedSpec().DiskID) suite.T().Logf("system disk: %s", systemDisk.TypedSpec().DiskID) }) } } // TestDisks verifies that Talos discovers disks. func (suite *VolumesSuite) TestDisks() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) disks, err := safe.StateListAll[*block.Disk](ctx, suite.Client.COSI) suite.Require().NoError(err) // there should be at least two disks - loop0 for Talos squashfs and a system disk suite.Assert().Greater(disks.Len(), 1) var diskNames []string for disk := range disks.All() { if disk.TypedSpec().Readonly { continue } if !disk.TypedSpec().CDROM { suite.Assert().NotEmpty(disk.TypedSpec().Size, "disk: %s", disk.Metadata().ID()) } suite.Assert().NotEmpty(disk.TypedSpec().Symlinks, "disk: %s", disk.Metadata().ID()) suite.Assert().NotEmpty(disk.TypedSpec().IOSize, "disk: %s", disk.Metadata().ID()) suite.Assert().NotEmpty(disk.TypedSpec().SectorSize, "disk: %s", disk.Metadata().ID()) if suite.Cluster != nil { // running on our own provider, transport should be always detected if disk.TypedSpec().BusPath == "/virtual" { suite.Assert().Empty(disk.TypedSpec().Transport, "disk: %s", disk.Metadata().ID()) } else { suite.Assert().NotEmpty(disk.TypedSpec().Transport, "disk: %s", disk.Metadata().ID()) } } if strings.HasPrefix(disk.Metadata().ID(), "dm-") { // devicemapper disks should have secondaries suite.Assert().NotEmpty(disk.TypedSpec().SecondaryDisks, "disk: %s", disk.Metadata().ID()) suite.T().Logf("disk: %s secondaries: %v", disk.Metadata().ID(), disk.TypedSpec().SecondaryDisks) } diskNames = append(diskNames, disk.Metadata().ID()) } suite.T().Logf("disks: %v", diskNames) }) } } // TestLVMActivation verifies that LVM volume group is activated after reboot. func (suite *VolumesSuite) TestLVMActivation() { if suite.SelinuxEnforcing { suite.T().Skip("skipping tests with nsenter in SELinux enforcing mode") } if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 2 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } userDisksJoined := strings.Join(userDisks[:2], " ") suite.T().Logf("creating LVM volume group on node %s/%s with disks %s", node, nodeName, userDisksJoined) podDef, err := suite.NewPrivilegedPod("pv-create") suite.Require().NoError(err) podDef = podDef.WithNodeName(nodeName) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck stdout, _, err := podDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- vgcreate --nolocking vg0 %s", userDisksJoined), ) suite.Require().NoError(err) suite.Require().Contains(stdout, "Volume group \"vg0\" successfully created") stdout, _, err = podDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- lvcreate --mirrors=1 --type=raid1 --nosync -n lv0 -L 1G vg0", ) suite.Require().NoError(err) suite.Require().Contains(stdout, "Logical volume \"lv0\" created.") stdout, _, err = podDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- lvcreate -n lv1 -L 1G vg0", ) suite.Require().NoError(err) suite.Require().Contains(stdout, "Logical volume \"lv1\" created.") defer suite.deleteLVMVolumes(node, nodeName, userDisksJoined) suite.T().Logf("rebooting node %s/%s", node, nodeName) // now we want to reboot the node and make sure the array is still mounted suite.AssertRebooted( suite.ctx, node, func(nodeCtx context.Context) error { return base.IgnoreGRPCUnavailable(suite.Client.Reboot(nodeCtx)) }, 5*time.Minute, suite.CleanupFailedPods, ) suite.T().Logf("verifying LVM activation %s/%s", node, nodeName) suite.Require().Eventually(func() bool { return suite.lvmVolumeExists(node, []string{"lv0", "lv1"}) }, 5*time.Second, 1*time.Second, "LVM volume group was not activated after reboot") } func (suite *VolumesSuite) deleteLVMVolumes(node, nodeName, userDisksJoined string) { suite.T().Logf("removing LVM volumes %s/%s", node, nodeName) deletePodDef, err := suite.NewPrivilegedPod("pv-destroy") suite.Require().NoError(err) deletePodDef = deletePodDef.WithNodeName(nodeName) suite.Require().NoError(deletePodDef.Create(suite.ctx, 5*time.Minute)) defer deletePodDef.Delete(suite.ctx) //nolint:errcheck if _, _, err := deletePodDef.Exec( suite.ctx, "nsenter --mount=/proc/1/ns/mnt -- vgremove --nolocking --yes vg0", ); err != nil { suite.T().Logf("failed to remove pv vg0: %v", err) } if _, _, err := deletePodDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- pvremove --nolocking --yes %s", userDisksJoined), ); err != nil { suite.T().Logf("failed to remove pv backed by volumes %s: %v", userDisksJoined, err) } } func (suite *VolumesSuite) lvmVolumeExists(node string, expectedVolumes []string) bool { ctx := client.WithNode(suite.ctx, node) disks, err := safe.StateListAll[*block.Disk](ctx, suite.Client.COSI) suite.Require().NoError(err) foundVolumes := xslices.ToSet(expectedVolumes) // device-mapper volumes will have udevd-created symlinks which contain volume name for disk := range disks.All() { if strings.HasPrefix(disk.TypedSpec().DevPath, "/dev/dm") { for _, volumeName := range expectedVolumes { for _, symlink := range disk.TypedSpec().Symlinks { if strings.Contains(symlink, volumeName) { foundVolumes[volumeName] = struct{}{} suite.T().Logf("found LVM volume %s as disk %s with symlink %s", volumeName, disk.Metadata().ID(), symlink) } } } } } return len(foundVolumes) == len(expectedVolumes) } // TestSymlinks that Talos can update disk symlinks on the fly. func (suite *VolumesSuite) TestSymlinks() { if suite.SelinuxEnforcing { suite.T().Skip("skipping tests with nsenter in SELinux enforcing mode") } if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } userDisk := userDisks[0] userDiskName := filepath.Base(userDisk) suite.T().Logf("performing a symlink test %s on %s/%s with disk %s", userDisk, node, nodeName, userDiskName) podDef, err := suite.NewPrivilegedPod("xfs-format") suite.Require().NoError(err) podDef = podDef.WithNodeName(nodeName) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck fsUUID := uuid.New().String() _, _, err = podDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mkfs.xfs -m uuid=%s %s", fsUUID, userDisk), ) suite.Require().NoError(err) expectedSymlink := "/dev/disk/by-uuid/" + fsUUID // Talos should report a symlink to the disk via FS UUID _, err = suite.Client.COSI.WatchFor(client.WithNode(suite.ctx, node), block.NewDisk(block.NamespaceName, userDiskName).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { disk, ok := r.(*block.Disk) if !ok { return false, fmt.Errorf("unexpected resource type: %T", r) } return slices.Index(disk.TypedSpec().Symlinks, expectedSymlink) != -1, nil }), ) suite.Require().NoError(err) suite.T().Logf("wiping user disk %s on %s/%s", userDisk, node, nodeName) suite.Require().NoError(suite.Client.BlockDeviceWipe(client.WithNode(suite.ctx, node), &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: userDiskName, Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) // Talos should remove a symlink to the disk _, err = suite.Client.COSI.WatchFor(client.WithNode(suite.ctx, node), block.NewDisk(block.NamespaceName, userDiskName).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { disk, ok := r.(*block.Disk) if !ok { return false, fmt.Errorf("unexpected resource type: %T", r) } return slices.Index(disk.TypedSpec().Symlinks, expectedSymlink) == -1, nil }), ) suite.Require().NoError(err) } // TestUserVolumesStatus verifies that existing user volumes were provisioned successfully. func (suite *VolumesSuite) TestUserVolumesStatus() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) userVolumeIDs := rtestutils.ResourceIDs[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, state.WithLabelQuery(resource.LabelExists(block.UserVolumeLabel))) // check that the volumes are ready rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) if len(userVolumeIDs) > 0 { suite.T().Logf("found %d user volumes", len(userVolumeIDs)) } // check that the volumes are mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, _ *assert.Assertions) {}, ) }) } } // TestVolumesStatus verifies that all volumes are either ready or missing. func (suite *VolumesSuite) TestVolumesStatus() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) rtestutils.AssertAll(ctx, suite.T(), suite.Client.COSI, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Contains([]block.VolumePhase{block.VolumePhaseReady, block.VolumePhaseMissing}, vs.TypedSpec().Phase) }, ) }) } } // TestUserVolumesPartition performs a series of operations on user volumes: creating, destroying, verifying, etc. func (suite *VolumesSuite) TestUserVolumesPartition() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } suite.T().Logf("verifying user volumes on node %s/%s with disk %s", node, nodeName, userDisks[0]) ctx := client.WithNode(suite.ctx, node) disk, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) volumeName := fmt.Sprintf("%04x", rand.Int31()) + "-" const numVolumes = 3 volumeIDs := make([]string, numVolumes) for i := range numVolumes { volumeIDs[i] = volumeName + strconv.Itoa(i) } userVolumeIDs := xslices.Map(volumeIDs, func(volumeID string) string { return constants.UserVolumePrefix + volumeID }) configDocs := xslices.Map(volumeIDs[:1], func(volumeID string) any { doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("1GiB") return doc }) configDocs = append(configDocs, xslices.Map(volumeIDs[1:2], func(volumeID string) any { doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("-60%") return doc })...) configDocs = append(configDocs, xslices.Map(volumeIDs[2:], func(volumeID string) any { doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("20%") return doc })...) suite.Require().Equal(len(configDocs), len(volumeIDs)) // create user volumes suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // check that the volumes are mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, _ *assert.Assertions) {}) // create a pod using user volumes podDef, err := suite.NewPod("user-volume-test") suite.Require().NoError(err) // using subdirectory here to test that the hostPath mount is properly propagated into the kubelet podDef = podDef.WithNodeName(nodeName). WithNamespace("kube-system"). WithHostVolumeMount(filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data"), "/mnt/data") suite.Require().NoError(podDef.Create(suite.ctx, 1*time.Minute)) _, _, err = podDef.Exec(suite.ctx, "mkdir -p /mnt/data/test") suite.Require().NoError(err) suite.Require().NoError(podDef.Delete(suite.ctx)) // verify that directory exists expectedPath := filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data", "test") stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: expectedPath, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, _ string, _ bool) error { suite.T().Logf("found %s on node %s", info.Name, node) suite.Require().Equal(expectedPath, info.Name, "expected %s to exist", expectedPath) return nil })) // verify that volume labels are set properly expectedLabels := xslices.ToSet(userVolumeIDs) dvs, err := safe.StateListAll[*block.DiscoveredVolume](ctx, suite.Client.COSI) suite.Require().NoError(err) for dv := range dvs.All() { delete(expectedLabels, dv.TypedSpec().PartitionLabel) } suite.Require().Empty(expectedLabels, "expected labels %v to be set on discovered volumes", expectedLabels) // test disableAccessTime mount option configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountDisableAccessTime = new(true) configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountSecure = new(false) suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{userVolumeIDs[0]}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().Spec.Secure, "expected Secure to be false") asrt.True(vs.TypedSpec().Spec.DisableAccessTime, "expected DisableAccessTime to be true") }) // test secure mount option configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountDisableAccessTime = new(false) configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountSecure = new(true) suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{userVolumeIDs[0]}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().Spec.DisableAccessTime, "expected DisableAccessTime to be false") asrt.True(vs.TypedSpec().Spec.Secure, "expected Secure to be true") }) // now, remove one of the volumes, wipe the partition and re-create the volume vs, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, suite.Client.COSI, userVolumeIDs[0]) suite.Require().NoError(err) suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs[0]) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeIDs[0]) suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(vs.TypedSpec().Location), Method: storage.BlockDeviceWipeDescriptor_FAST, DropPartition: true, }, }, })) }, time.Minute, time.Second, "failed to wipe partition %s", vs.TypedSpec().Location) // wait for the discovered volume to disappear rtestutils.AssertNoResource[*block.DiscoveredVolume](ctx, suite.T(), suite.Client.COSI, filepath.Base(vs.TypedSpec().Location)) // re-create the volume with project quota support configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).FilesystemSpec.ProjectQuotaSupportConfig = new(true) suite.PatchMachineConfig(ctx, configDocs[0]) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, asrt *assert.Assertions) { if vs.Metadata().ID() == userVolumeIDs[0] { // check that the project quota support is enabled asrt.True(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be enabled for %s", vs.Metadata().ID()) } else { // check that the project quota support is disabled asrt.False(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be disabled for %s", vs.Metadata().ID()) } }) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs...) for _, userVolumeID := range userVolumeIDs { rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) } suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(userDisks[0]), Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) }, time.Minute, time.Second, "failed to wipe disk %s", userDisks[0]) // wait for the discovered volume reflect wiped status rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(userDisks[0]), func(dv *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Empty(dv.TypedSpec().Name, "expected discovered volume %s to be wiped", dv.Metadata().ID()) }) } // TestUserVolumesDisk performs a series of operations on user volumes: creating, destroying, verifying, etc. func (suite *VolumesSuite) TestUserVolumesDisk() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } suite.T().Logf("verifying user volumes on node %s/%s with disk %s", node, nodeName, userDisks[0]) ctx := client.WithNode(suite.ctx, node) disk, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) volumeName := fmt.Sprintf("%04x", rand.Int31()) + "-" const numVolumes = 1 volumeIDs := make([]string, numVolumes) for i := range numVolumes { volumeIDs[i] = volumeName + strconv.Itoa(i) } userVolumeIDs := xslices.Map(volumeIDs, func(volumeID string) string { return constants.UserVolumePrefix + volumeID }) configDocs := xslices.Map(volumeIDs, func(volumeID string) any { doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.VolumeType = new(block.VolumeTypeDisk) doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) return doc }) // create user volumes suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // check that the volumes are mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, _ *assert.Assertions) {}) // create a pod using user volumes podDef, err := suite.NewPod("user-volume-test") suite.Require().NoError(err) // using subdirectory here to test that the hostPath mount is properly propagated into the kubelet podDef = podDef.WithNodeName(nodeName). WithNamespace("kube-system"). WithHostVolumeMount(filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data"), "/mnt/data") suite.Require().NoError(podDef.Create(suite.ctx, 1*time.Minute)) _, _, err = podDef.Exec(suite.ctx, "mkdir -p /mnt/data/test") suite.Require().NoError(err) suite.Require().NoError(podDef.Delete(suite.ctx)) // verify that directory exists expectedPath := filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data", "test") stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: expectedPath, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, _ string, _ bool) error { suite.T().Logf("found %s on node %s", info.Name, node) suite.Require().Equal(expectedPath, info.Name, "expected %s to exist", expectedPath) return nil })) // test disableAccessTime mount option configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountDisableAccessTime = new(true) configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountSecure = new(false) suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{userVolumeIDs[0]}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().Spec.Secure, "expected Secure to be false") asrt.True(vs.TypedSpec().Spec.DisableAccessTime, "expected DisableAccessTime to be true") }) // test secure mount option configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountDisableAccessTime = new(false) configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).MountSpec.MountSecure = new(true) suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{userVolumeIDs[0]}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().Spec.DisableAccessTime, "expected DisableAccessTime to be false") asrt.True(vs.TypedSpec().Spec.Secure, "expected Secure to be true") }) // now, remove one of the volumes, wipe the partition and re-create the volume vs, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, suite.Client.COSI, userVolumeIDs[0]) suite.Require().NoError(err) suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs[0]) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeIDs[0]) suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(vs.TypedSpec().Location), Method: storage.BlockDeviceWipeDescriptor_FAST, DropPartition: true, }, }, })) }, time.Minute, time.Second, "failed to wipe partition %s", vs.TypedSpec().Location) // wait for the discovered volume to lose filesystem rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(vs.TypedSpec().Location), func(r *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Empty(r.TypedSpec().Name) // no filesystem }) // re-create the volume with project quota support configDocs[0].(*blockcfg.UserVolumeConfigV1Alpha1).FilesystemSpec.ProjectQuotaSupportConfig = new(true) suite.PatchMachineConfig(ctx, configDocs[0]) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, asrt *assert.Assertions) { if vs.Metadata().ID() == userVolumeIDs[0] { // check that the project quota support is enabled asrt.True(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be enabled for %s", vs.Metadata().ID()) } else { // check that the project quota support is disabled asrt.False(vs.TypedSpec().ProjectQuotaSupport, "project quota support should be disabled for %s", vs.Metadata().ID()) } }) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs...) for _, userVolumeID := range userVolumeIDs { rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) } suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(userDisks[0]), Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) }, time.Minute, time.Second, "failed to wipe disk %s", userDisks[0]) // wait for the discovered volume reflect wiped status rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(userDisks[0]), func(dv *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Empty(dv.TypedSpec().Name, "expected discovered volume %s to be wiped", dv.Metadata().ID()) }) } // TestUserVolumesDirectory performs a series of operations on user volumes: creating, destroying, verifying, etc. func (suite *VolumesSuite) TestUserVolumesDirectory() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name ctx := client.WithNode(suite.ctx, node) volumeName := fmt.Sprintf("%04x", rand.Int31()) + "-" const numVolumes = 3 volumeIDs := make([]string, numVolumes) for i := range numVolumes { volumeIDs[i] = volumeName + strconv.Itoa(i) } userVolumeIDs := xslices.Map(volumeIDs, func(volumeID string) string { return constants.UserVolumePrefix + volumeID }) configDocs := xslices.Map(volumeIDs, func(volumeID string) any { doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.VolumeType = new(block.VolumeTypeDirectory) return doc }) // create user volumes suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // check that the volumes are mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.MountStatus, _ *assert.Assertions) {}) // create a pod using user volumes podDef, err := suite.NewPod("user-volume-test") suite.Require().NoError(err) // using subdirectory here to test that the hostPath mount is properly propagated into the kubelet podDef = podDef.WithNodeName(nodeName). WithNamespace("kube-system"). WithHostVolumeMount(filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data"), "/mnt/data") suite.Require().NoError(podDef.Create(suite.ctx, 1*time.Minute)) _, _, err = podDef.Exec(suite.ctx, "mkdir -p /mnt/data/test") suite.Require().NoError(err) suite.Require().NoError(podDef.Delete(suite.ctx)) // verify that directory exists expectedPath := filepath.Join(constants.UserVolumeMountPoint, volumeIDs[0], "data", "test") stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: expectedPath, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, _ string, _ bool) error { suite.T().Logf("found %s on node %s", info.Name, node) suite.Require().Equal(expectedPath, info.Name, "expected %s to exist", expectedPath) return nil })) // now, remove one of the volumes and re-create the volume suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs[0]) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeIDs[0]) // re-create the volume suite.PatchMachineConfig(ctx, configDocs[0]) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, userVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeIDs...) for _, userVolumeID := range userVolumeIDs { rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) } } // TestRawVolumes performs a series of operations on raw volumes: creating, destroying, etc. func (suite *VolumesSuite) TestRawVolumes() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } suite.T().Logf("verifying raw volumes on node %s/%s with disk %s", node, nodeName, userDisks[0]) ctx := client.WithNode(suite.ctx, node) disk, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) volumeName := fmt.Sprintf("%04x", rand.Int31()) + "-" const numVolumes = 2 volumeIDs := make([]string, numVolumes) for i := range numVolumes { volumeIDs[i] = volumeName + strconv.Itoa(i) } rawVolumeIDs := xslices.Map(volumeIDs, func(volumeID string) string { return constants.RawVolumePrefix + volumeID }) configDocs := xslices.Map(volumeIDs, func(volumeID string) any { doc := blockcfg.NewRawVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("500MiB") return doc }) // create raw volumes suite.PatchMachineConfig(ctx, configDocs...) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, rawVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // verify that volume labels are set properly for _, rawVolumeID := range rawVolumeIDs { vs, err := safe.StateGetByID[*block.VolumeStatus](ctx, suite.Client.COSI, rawVolumeID) suite.Require().NoError(err) rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(vs.TypedSpec().Location), func(dv *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Equal(vs.Metadata().ID(), dv.TypedSpec().PartitionLabel, "expected discovered volume %s to have label %s", dv.Metadata().ID(), vs.Metadata().ID()) }) } // now, remove one of the volumes, wipe the partition and re-create the volume vs, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, suite.Client.COSI, rawVolumeIDs[0]) suite.Require().NoError(err) suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.RawVolumeConfigKind, volumeIDs[0]) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, rawVolumeIDs[0]) suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(vs.TypedSpec().Location), Method: storage.BlockDeviceWipeDescriptor_FAST, DropPartition: true, }, }, })) }, time.Minute, time.Second, "failed to wipe partition %s", vs.TypedSpec().Location) // wait for the discovered volume to disappear rtestutils.AssertNoResource[*block.DiscoveredVolume](ctx, suite.T(), suite.Client.COSI, filepath.Base(vs.TypedSpec().Location)) // re-create the volume suite.PatchMachineConfig(ctx, configDocs[0]) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, rawVolumeIDs, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.RawVolumeConfigKind, volumeIDs...) for _, rawVolumeID := range rawVolumeIDs { rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, rawVolumeID) } suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(userDisks[0]), Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) }, time.Minute, time.Second, "failed to wipe disk %s", userDisks[0]) // wait for the discovered volume reflect wiped status rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(userDisks[0]), func(dv *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Empty(dv.TypedSpec().Name, "expected discovered volume %s to be wiped", dv.Metadata().ID()) }) } // TestExistingVolumes performs a series of operations on existing volumes: mount/unmount, etc. // //nolint:gocyclo func (suite *VolumesSuite) TestExistingVolumes() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } suite.T().Logf("verifying existing volumes on node %s/%s with disk %s", node, nodeName, userDisks[0]) ctx := client.WithNode(suite.ctx, node) disk, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) volumeID := fmt.Sprintf("%04x", rand.Int31()) existingVolumeID := constants.ExistingVolumePrefix + volumeID // first, create a user volume config to get the volume created userVolumeID := constants.UserVolumePrefix + volumeID userVolumeDoc := blockcfg.NewUserVolumeConfigV1Alpha1() userVolumeDoc.MetaName = volumeID userVolumeDoc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) userVolumeDoc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") userVolumeDoc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("1GiB") // create user volumes suite.PatchMachineConfig(ctx, userVolumeDoc) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{userVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // now destroy a user volume suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeID) // wait for the user volume to be removed rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) // now, recreate the volume as an existing volume existingVolumeDoc := blockcfg.NewExistingVolumeConfigV1Alpha1() existingVolumeDoc.MetaName = volumeID existingVolumeDoc.VolumeDiscoverySpec.VolumeSelectorConfig.Match = cel.MustExpression( cel.ParseBooleanExpression( fmt.Sprintf("volume.partition_label == '%s' && '%s' in disk.symlinks", userVolumeID, disk.TypedSpec().Symlinks[0]), celenv.VolumeLocator(), ), ) // create existing volume suite.PatchMachineConfig(ctx, existingVolumeDoc) // wait for the existing volume to be discovered rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{existingVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) asrt.Equal(userDisks[0], vs.TypedSpec().ParentLocation) }, ) // check that the volume is mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{existingVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().ReadOnly) }) // create a pod using existing volumes podDef, err := suite.NewPod("existing-volume-test") suite.Require().NoError(err) // using subdirectory here to test that the hostPath mount is properly propagated into the kubelet podDef = podDef.WithNodeName(nodeName). WithNamespace("kube-system"). WithHostVolumeMount(filepath.Join(constants.UserVolumeMountPoint, volumeID, "data"), "/mnt/data") suite.Require().NoError(podDef.Create(suite.ctx, 1*time.Minute)) _, _, err = podDef.Exec(suite.ctx, "mkdir -p /mnt/data/test") suite.Require().NoError(err) suite.Require().NoError(podDef.Delete(suite.ctx)) // verify that directory exists expectedPath := filepath.Join(constants.UserVolumeMountPoint, volumeID, "data", "test") stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: expectedPath, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, _ string, _ bool) error { suite.T().Logf("found %s on node %s", info.Name, node) suite.Require().Equal(expectedPath, info.Name, "expected %s to exist", expectedPath) return nil })) // now, re-mount the existing volume as read-only existingVolumeDoc.MountSpec.MountReadOnly = new(true) suite.PatchMachineConfig(ctx, existingVolumeDoc) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{existingVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.True(vs.TypedSpec().ReadOnly) }) // test disableAccessTime mount option existingVolumeDoc.MountSpec.MountReadOnly = nil existingVolumeDoc.MountSpec.MountDisableAccessTime = new(true) existingVolumeDoc.MountSpec.MountSecure = new(false) suite.PatchMachineConfig(ctx, existingVolumeDoc) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{existingVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.True(vs.TypedSpec().Spec.DisableAccessTime, "expected DisableAccessTime to be true") }) // test secure mount option existingVolumeDoc.MountSpec.MountDisableAccessTime = new(false) existingVolumeDoc.MountSpec.MountSecure = new(true) suite.PatchMachineConfig(ctx, existingVolumeDoc) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{existingVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.True(vs.TypedSpec().Spec.Secure, "expected Secure to be true") }) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.ExistingVolumeConfigKind, volumeID) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, existingVolumeID) suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(userDisks[0]), Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) }, time.Minute, time.Second, "failed to wipe disk %s", userDisks[0]) // wait for the discovered volume reflect wiped status rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, filepath.Base(userDisks[0]), func(dv *block.DiscoveredVolume, asrt *assert.Assertions) { asrt.Empty(dv.TypedSpec().Name, "expected discovered volume %s to be wiped", dv.Metadata().ID()) }) } // TestExternalVolumesVirtiofs performs a series of operations on external virtiofs volumes: mount/unmount, etc. func (suite *VolumesSuite) TestExternalVolumesVirtiofs() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } if !suite.Virtiofsd { suite.T().Skip("skipping test without virtiofsd running") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name ctx := client.WithNode(suite.ctx, node) volumeID := fmt.Sprintf("%04x", rand.Int31()) externalVolumeID := constants.ExternalVolumePrefix + volumeID // create the volume as an external volume externalVolumeDoc := blockcfg.NewExternalVolumeConfigV1Alpha1() externalVolumeDoc.MetaName = volumeID externalVolumeDoc.FilesystemType = block.FilesystemTypeVirtiofs externalVolumeDoc.MountSpec = blockcfg.ExternalMountSpec{ MountVirtiofs: &blockcfg.VirtiofsMountSpec{ VirtiofsTag: "disk0", }, } // create external volume suite.PatchMachineConfig(ctx, externalVolumeDoc) // wait for the external volume to be discovered rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{externalVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // check that the volume is mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{externalVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.False(vs.TypedSpec().ReadOnly) }) // create a pod using external volumes podDef, err := suite.NewPod("external-volume-virtiofs-test") suite.Require().NoError(err) // using subdirectory here to test that the hostPath mount is properly propagated into the kubelet podDef = podDef.WithNodeName(nodeName). WithNamespace("kube-system"). WithHostVolumeMount(filepath.Join(constants.UserVolumeMountPoint, volumeID, "data"), "/mnt/data") suite.Require().NoError(podDef.Create(suite.ctx, 1*time.Minute)) _, _, err = podDef.Exec(suite.ctx, "mkdir -p /mnt/data/test") suite.Require().NoError(err) suite.Require().NoError(podDef.Delete(suite.ctx)) // verify that directory exists expectedPath := filepath.Join(constants.UserVolumeMountPoint, volumeID, "data", "test") stream, err := suite.Client.LS(ctx, &machineapi.ListRequest{ Root: expectedPath, Types: []machineapi.ListRequest_Type{machineapi.ListRequest_DIRECTORY}, }) suite.Require().NoError(err) suite.Require().NoError(helpers.ReadGRPCStream(stream, func(info *machineapi.FileInfo, _ string, _ bool) error { suite.T().Logf("found %s on node %s", info.Name, node) suite.Require().Equal(expectedPath, info.Name, "expected %s to exist", expectedPath) return nil })) // now, re-mount the external volume as read-only externalVolumeDoc.MountSpec.MountReadOnly = new(true) suite.PatchMachineConfig(ctx, externalVolumeDoc) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []resource.ID{externalVolumeID}, func(vs *block.MountStatus, asrt *assert.Assertions) { asrt.True(vs.TypedSpec().ReadOnly) }) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.ExternalVolumeConfigKind, volumeID) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, externalVolumeID) } // TestVolumeProvisioningErrors verifies that volume provisioning fails correctly for invalid disk selectors. func (suite *VolumesSuite) TestVolumeProvisioningErrors() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name ctx := client.WithNode(suite.ctx, node) suite.Run("NoDisksMatchedSelector", func() { volumeID := fmt.Sprintf("nodsks-%04x", rand.Int31()) userVolumeID := constants.UserVolumePrefix + volumeID doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression( "disk.serial == 'nonexistent-serial-xyz-00000'", celenv.DiskLocator(), ), ) doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("500MiB") suite.PatchMachineConfig(ctx, doc) suite.T().Logf("verifying volume %s on node %s/%s fails with no disks matched", userVolumeID, node, nodeName) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []string{userVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseFailed, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseFailed, vs.TypedSpec().Phase, vs.Metadata().ID()) asrt.Contains(vs.TypedSpec().ErrorMessage, "no disks matched selector for volume", "expected error message to contain 'no disks matched selector for volume', got: %s", vs.TypedSpec().ErrorMessage) }, ) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeID) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) }) suite.Run("MultipleDisksMatchedForDiskVolume", func() { userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 2 { suite.T().Skipf("skipping test, need at least 2 user disks on node %s/%s: %q", node, nodeName, userDisks) } volumeID := fmt.Sprintf("multi-%04x", rand.Int31()) userVolumeID := constants.UserVolumePrefix + volumeID // build a selector that matches multiple user disks by using an OR of their symlinks disk0, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) suite.Require().NotEmpty(disk0.TypedSpec().Symlinks, "expected disk0 to have at least one symlink") disk1, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[1])) suite.Require().NoError(err) suite.Require().NotEmpty(disk1.TypedSpec().Symlinks, "expected disk1 to have at least one symlink") matchExpr := fmt.Sprintf("'%s' in disk.symlinks || '%s' in disk.symlinks", disk0.TypedSpec().Symlinks[0], disk1.TypedSpec().Symlinks[0]) doc := blockcfg.NewUserVolumeConfigV1Alpha1() doc.MetaName = volumeID doc.VolumeType = new(block.VolumeTypeDisk) doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(matchExpr, celenv.DiskLocator()), ) suite.PatchMachineConfig(ctx, doc) suite.T().Logf("verifying volume %s on node %s/%s fails with multiple disks matched", userVolumeID, node, nodeName) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []string{userVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseFailed, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseFailed, vs.TypedSpec().Phase, vs.Metadata().ID()) asrt.Contains(vs.TypedSpec().ErrorMessage, "multiple disks matched locator for disk volume", "expected error message to contain 'multiple disks matched locator for disk volume', got: %s", vs.TypedSpec().ErrorMessage) }, ) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.UserVolumeConfigKind, volumeID) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, userVolumeID) }) } // TestSwapStatus verifies that all swap volumes are successfully enabled. func (suite *VolumesSuite) TestSwapStatus() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) swapVolumes, err := safe.StateListAll[*block.VolumeConfig](ctx, suite.Client.COSI, state.WithLabelQuery(resource.LabelExists(block.SwapVolumeLabel))) suite.Require().NoError(err) if swapVolumes.Len() == 0 { suite.T().Skipf("skipping test, no swap volumes found on node %s", node) } rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, xslices.Map(slices.Collect(swapVolumes.All()), func(sv *block.VolumeConfig) string { return sv.Metadata().ID() }), func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) swapVolumesStatus, err := safe.StateListAll[*block.VolumeStatus](ctx, suite.Client.COSI, state.WithLabelQuery(resource.LabelExists(block.SwapVolumeLabel))) suite.Require().NoError(err) deviceNames := xslices.Map(slices.Collect(swapVolumesStatus.All()), func(sv *block.VolumeStatus) string { return sv.TypedSpec().MountLocation }) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, deviceNames, func(vs *block.SwapStatus, asrt *assert.Assertions) {}, ) suite.T().Logf("found swap volumes (%q) on node %s", deviceNames, node) }) } } // TestSwapOnOff performs a series of operations on swap volume: creating, destroying, enabling, disabling, etc. func (suite *VolumesSuite) TestSwapOnOff() { if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } suite.T().Logf("verifying swap on node %s/%s with disk %s", node, nodeName, userDisks[0]) ctx := client.WithNode(suite.ctx, node) disk, err := safe.StateGetByID[*block.Disk](ctx, suite.Client.COSI, filepath.Base(userDisks[0])) suite.Require().NoError(err) volumeName := fmt.Sprintf("%04x", rand.Int31()) doc := blockcfg.NewSwapVolumeConfigV1Alpha1() doc.MetaName = volumeName doc.ProvisioningSpec.DiskSelectorSpec.Match = cel.MustExpression( cel.ParseBooleanExpression(fmt.Sprintf("'%s' in disk.symlinks", disk.TypedSpec().Symlinks[0]), celenv.DiskLocator()), ) doc.EncryptionSpec = blockcfg.EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []blockcfg.EncryptionKey{ { KeySlot: 0, KeyStatic: &blockcfg.EncryptionKeyStatic{ KeyData: "secretswap", }, }, }, } doc.ProvisioningSpec.ProvisioningMinSize = blockcfg.MustByteSize("100MiB") doc.ProvisioningSpec.ProvisioningMaxSize = blockcfg.MustSize("500MiB") // create user volumes suite.PatchMachineConfig(ctx, doc) swapVolumeID := constants.SwapVolumePrefix + doc.MetaName rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []string{swapVolumeID}, func(vs *block.VolumeStatus, asrt *assert.Assertions) { asrt.Equalf(block.VolumePhaseReady, vs.TypedSpec().Phase, "Expected %q, but got %q (%s)", block.VolumePhaseReady, vs.TypedSpec().Phase, vs.Metadata().ID()) }, ) // check that the volumes are mounted rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []string{swapVolumeID}, func(vs *block.MountStatus, _ *assert.Assertions) {}) // check that the swap is enabled volumeStatus, err := safe.ReaderGetByID[*block.VolumeStatus](ctx, suite.Client.COSI, swapVolumeID) suite.Require().NoError(err) rtestutils.AssertResources(ctx, suite.T(), suite.Client.COSI, []string{volumeStatus.TypedSpec().MountLocation}, func(vs *block.SwapStatus, asrt *assert.Assertions) {}) // clean up suite.RemoveMachineConfigDocumentsByName(ctx, blockcfg.SwapVolumeConfigKind, volumeName) rtestutils.AssertNoResource[*block.VolumeStatus](ctx, suite.T(), suite.Client.COSI, swapVolumeID) rtestutils.AssertNoResource[*block.SwapStatus](ctx, suite.T(), suite.Client.COSI, volumeStatus.TypedSpec().MountLocation) rtestutils.AssertNoResource[*block.VolumeConfig](ctx, suite.T(), suite.Client.COSI, swapVolumeID) suite.Require().EventuallyWithT(func(collect *assert.CollectT) { // a little retry loop, as the device might be considered busy for a little while after unmounting asrt := assert.New(collect) asrt.NoError(suite.Client.BlockDeviceWipe(ctx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: filepath.Base(userDisks[0]), Method: storage.BlockDeviceWipeDescriptor_FAST, }, }, })) }, time.Minute, time.Second, "failed to wipe disk %s", userDisks[0]) } // TestZswapStatus verifies that all zswap-enabled machines have zswap running. func (suite *VolumesSuite) TestZswapStatus() { for _, node := range suite.DiscoverNodeInternalIPs(suite.ctx) { suite.Run(node, func() { ctx := client.WithNode(suite.ctx, node) cfg, err := suite.ReadConfigFromNode(ctx) suite.Require().NoError(err) if cfg.ZswapConfig() == nil { suite.T().Skipf("skipping test, zswap is not enabled on node %s", node) } rtestutils.AssertResource(ctx, suite.T(), suite.Client.COSI, block.ZswapStatusID, func(vs *block.ZswapStatus, asrt *assert.Assertions) { suite.T().Logf("zswap total size %s, stored pages %d", vs.TypedSpec().TotalSizeHuman, vs.TypedSpec().StoredPages) }, ) }) } } func init() { allSuites = append(allSuites, new(VolumesSuite)) } ================================================ FILE: internal/integration/api/watchdog.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "bytes" "context" "io" "path/filepath" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // WatchdogSuite ... type WatchdogSuite struct { base.APISuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *WatchdogSuite) SuiteName() string { return "api.WatchdogSuite" } // SetupTest ... func (suite *WatchdogSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 1*time.Minute) if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping watchdog test since provisioner is not qemu") } } // TearDownTest ... func (suite *WatchdogSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } func (suite *WatchdogSuite) readWatchdogSysfs(nodeCtx context.Context, watchdog, property string) string { //nolint:unparam r, err := suite.Client.Read(nodeCtx, filepath.Join("/sys/class/watchdog", watchdog, property)) suite.Require().NoError(err) value, err := io.ReadAll(r) suite.Require().NoError(err) suite.Require().NoError(r.Close()) return string(bytes.TrimSpace(value)) } // TestWatchdogSysfs sets up the watchdog and validates its parameters from the /sys/class/watchdog. func (suite *WatchdogSuite) TestWatchdogSysfs() { // pick up a random node to test the watchdog on, and use it throughout the test node := suite.RandomDiscoveredNodeInternalIP() suite.T().Logf("testing watchdog on node %s", node) // build a Talos API context which is tied to the node nodeCtx := client.WithNode(suite.ctx, node) // pick a watchdog const watchdog = "watchdog0" cfgDocument := runtime.NewWatchdogTimerV1Alpha1() cfgDocument.WatchdogDevice = "/dev/" + watchdog cfgDocument.WatchdogTimeout = 120 * time.Second // deactivate the watchdog suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) _, err := suite.Client.COSI.WatchFor(nodeCtx, runtimeres.NewWatchdogTimerStatus(runtimeres.WatchdogTimerConfigID).Metadata(), state.WithEventTypes(state.Destroyed)) suite.Require().NoError(err) wdState := suite.readWatchdogSysfs(nodeCtx, watchdog, "state") suite.Require().Equal("inactive", wdState) // enable watchdog with 120s timeout suite.PatchMachineConfig(nodeCtx, cfgDocument) _, err = suite.Client.COSI.WatchFor(nodeCtx, runtimeres.NewWatchdogTimerStatus(runtimeres.WatchdogTimerConfigID).Metadata(), state.WithEventTypes(state.Created, state.Updated)) suite.Require().NoError(err) wdState = suite.readWatchdogSysfs(nodeCtx, watchdog, "state") suite.Require().Equal("active", wdState) wdTimeout := suite.readWatchdogSysfs(nodeCtx, watchdog, "timeout") suite.Require().Equal("120", wdTimeout) // update watchdog timeout to 60s cfgDocument.WatchdogTimeout = 60 * time.Second suite.PatchMachineConfig(nodeCtx, cfgDocument) _, err = suite.Client.COSI.WatchFor(nodeCtx, runtimeres.NewWatchdogTimerStatus(runtimeres.WatchdogTimerConfigID).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { return r.(*runtimeres.WatchdogTimerStatus).TypedSpec().Timeout == cfgDocument.WatchdogTimeout, nil }), ) suite.Require().NoError(err) wdState = suite.readWatchdogSysfs(nodeCtx, watchdog, "state") suite.Require().Equal("active", wdState) wdTimeout = suite.readWatchdogSysfs(nodeCtx, watchdog, "timeout") suite.Require().Equal("60", wdTimeout) // deactivate the watchdog suite.RemoveMachineConfigDocuments(nodeCtx, cfgDocument.MetaKind) _, err = suite.Client.COSI.WatchFor(nodeCtx, runtimeres.NewWatchdogTimerStatus(runtimeres.WatchdogTimerConfigID).Metadata(), state.WithEventTypes(state.Destroyed)) suite.Require().NoError(err) wdState = suite.readWatchdogSysfs(nodeCtx, watchdog, "state") suite.Require().Equal("inactive", wdState) } func init() { allSuites = append(allSuites, new(WatchdogSuite)) } ================================================ FILE: internal/integration/api/wipe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package api import ( "context" "fmt" "path/filepath" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // WipeSuite ... type WipeSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName ... func (suite *WipeSuite) SuiteName() string { return "api.WipeSuite" } // SetupTest ... func (suite *WipeSuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) if !suite.Capabilities().SupportsVolumes { suite.T().Skip("cluster doesn't support volumes") } } // TearDownTest ... func (suite *WipeSuite) TearDownTest() { if suite.ctxCancel != nil { suite.ctxCancel() } } // TestWipeBlockDeviceInvalid verifies that invalid wipe requests are rejected. func (suite *WipeSuite) TestWipeBlockDeviceInvalid() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNode(suite.ctx, node) disks, err := safe.StateListAll[*block.Disk](nodeCtx, suite.Client.COSI) suite.Require().NoError(err) for disk := range disks.All() { if disk.TypedSpec().Readonly || disk.TypedSpec().CDROM { suite.T().Logf("invalid wipe request for %s at %s", disk.Metadata().ID(), node) err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: disk.Metadata().ID(), }, }, }) suite.Require().Error(err) suite.Assert().Equal(codes.FailedPrecondition, client.StatusCode(err)) } } err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: "nosuchdevice", }, }, }) suite.Require().Error(err) suite.Assert().Equal(codes.NotFound, client.StatusCode(err)) // try to wipe a system disk systemDisk, err := safe.StateGetByID[*block.SystemDisk](nodeCtx, suite.Client.COSI, block.SystemDiskID) suite.Require().NoError(err) suite.T().Logf("invalid wipe request for %s at %s", systemDisk.TypedSpec().DiskID, node) err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: systemDisk.TypedSpec().DiskID, }, }, }) suite.Require().Error(err) suite.Assert().Equal(codes.FailedPrecondition, client.StatusCode(err)) } // TestWipeFilesystem verifies that the filesystem can be wiped. func (suite *WipeSuite) TestWipeFilesystem() { if suite.SelinuxEnforcing { suite.T().Skip("skipping tests with nsenter in SELinux enforcing mode") } if testing.Short() { suite.T().Skip("skipping test in short mode.") } if suite.Cluster == nil || suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping test for non-qemu provisioner") } node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) k8sNode, err := suite.GetK8sNodeByInternalIP(suite.ctx, node) suite.Require().NoError(err) nodeName := k8sNode.Name suite.T().Logf("creating filesystem on %s/%s", node, nodeName) userDisks := suite.UserDisks(suite.ctx, node) if len(userDisks) < 1 { suite.T().Skipf("skipping test, not enough user disks available on node %s/%s: %q", node, nodeName, userDisks) } userDisk := userDisks[0] podDef, err := suite.NewPrivilegedPod("fs-format") suite.Require().NoError(err) podDef = podDef.WithNodeName(nodeName) suite.Require().NoError(podDef.Create(suite.ctx, 5*time.Minute)) defer podDef.Delete(suite.ctx) //nolint:errcheck _, _, err = podDef.Exec( suite.ctx, fmt.Sprintf("nsenter --mount=/proc/1/ns/mnt -- mkfs.xfs %s", userDisk), ) suite.Require().NoError(err) // now Talos should report the disk as xfs formatted deviceName := filepath.Base(userDisk) nodeCtx := client.WithNode(suite.ctx, node) // wait for Talos to discover xfs _, err = suite.Client.COSI.WatchFor(nodeCtx, block.NewDiscoveredVolume(block.NamespaceName, deviceName).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { return r.(*block.DiscoveredVolume).TypedSpec().Name == "xfs", nil }), ) suite.Require().NoError(err) suite.T().Logf("xfs filesystem created on %s/%s", node, nodeName) // wipe the filesystem err = suite.Client.BlockDeviceWipe(nodeCtx, &storage.BlockDeviceWipeRequest{ Devices: []*storage.BlockDeviceWipeDescriptor{ { Device: deviceName, }, }, }) suite.Require().NoError(err) // wait for Talos to discover that the disk is wiped _, err = suite.Client.COSI.WatchFor(nodeCtx, block.NewDiscoveredVolume(block.NamespaceName, deviceName).Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { return r.(*block.DiscoveredVolume).TypedSpec().Name == "", nil }), ) suite.Require().NoError(err) } func init() { allSuites = append(allSuites, new(WipeSuite)) } ================================================ FILE: internal/integration/base/api.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_api package base import ( "bufio" "context" "crypto/sha256" "encoding/hex" "fmt" "io" "math/rand/v2" "path/filepath" "slices" "strings" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.yaml.in/yaml/v4" "google.golang.org/grpc/backoff" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/cluster/check" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" configres "github.com/siderolabs/talos/pkg/machinery/resources/config" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/access" ) // APISuite is a base suite for API tests. type APISuite struct { suite.Suite TalosSuite Client *client.Client Talosconfig *clientconfig.Config } // SetupSuite initializes Talos API client. func (apiSuite *APISuite) SetupSuite() { var err error apiSuite.Talosconfig, err = clientconfig.Open(apiSuite.TalosConfig) apiSuite.Require().NoError(err) if apiSuite.Endpoint != "" { apiSuite.Client = apiSuite.GetClientWithEndpoints(apiSuite.Endpoint) } else { apiSuite.Client = apiSuite.GetClientWithEndpoints() } // clear any connection refused errors left after the previous tests nodes := apiSuite.DiscoverNodeInternalIPs(context.TODO()) if len(nodes) > 0 { // grpc might trigger backoff on reconnect attempts, so make sure we clear them apiSuite.ClearConnectionRefused(context.Background(), nodes...) } } // GetClientWithEndpoints returns Talos API client with provided endpoints. func (apiSuite *APISuite) GetClientWithEndpoints(endpoints ...string) *client.Client { opts := []client.OptionFunc{ client.WithConfig(apiSuite.Talosconfig), client.WithEndpoints(endpoints...), } cli, err := client.New(context.TODO(), opts...) apiSuite.Require().NoError(err) return cli } // DiscoverNodes provides list of Talos nodes in the cluster. // // As there's no way to provide this functionality via Talos API, it works the following way: // 1. If there's a provided cluster info, it's used. // 2. If integration test was compiled with k8s support, k8s is used. // // The passed ctx is additionally limited to one minute. func (apiSuite *APISuite) DiscoverNodes(ctx context.Context) cluster.Info { discoveredNodes := apiSuite.TalosSuite.DiscoverNodes(ctx) if discoveredNodes != nil { return discoveredNodes } var err error var ctxCancel context.CancelFunc ctx, ctxCancel = context.WithTimeout(ctx, time.Minute) defer ctxCancel() apiSuite.discoveredNodes, err = discoverNodesK8s(ctx, apiSuite.Client, &apiSuite.TalosSuite) apiSuite.Require().NoError(err, "k8s discovery failed") if apiSuite.discoveredNodes == nil { // still no nodes, skip the test apiSuite.T().Skip("no nodes were discovered") } return apiSuite.discoveredNodes } // DiscoverNodeInternalIPs provides list of Talos node internal IPs in the cluster. func (apiSuite *APISuite) DiscoverNodeInternalIPs(ctx context.Context) []string { nodes := apiSuite.DiscoverNodes(ctx).Nodes() return mapNodeInfosToInternalIPs(nodes) } // DiscoverNodeInternalIPsByType provides list of Talos node internal IPs in the cluster for given machine type. func (apiSuite *APISuite) DiscoverNodeInternalIPsByType(ctx context.Context, machineType machine.Type) []string { nodesByType := apiSuite.DiscoverNodes(ctx).NodesByType(machineType) return mapNodeInfosToInternalIPs(nodesByType) } // RandomDiscoveredNodeInternalIP returns the internal IP a random node of the specified type (or any type if no types are specified). func (apiSuite *APISuite) RandomDiscoveredNodeInternalIP(types ...machine.Type) string { nodeInfo := apiSuite.DiscoverNodes(context.TODO()) var nodes []cluster.NodeInfo if len(types) == 0 { nodeInfos := nodeInfo.Nodes() nodes = nodeInfos } else { for _, t := range types { nodeInfosByType := nodeInfo.NodesByType(t) nodes = append(nodes, nodeInfosByType...) } } apiSuite.Require().NotEmpty(nodes) return nodes[rand.IntN(len(nodes))].InternalIP.String() } // Capabilities describes current cluster allowed actions. type Capabilities struct { RunsTalosKernel bool SupportsReboot bool SupportsRecover bool SupportsVolumes bool SecureBooted bool } // Capabilities returns a set of capabilities to skip tests for different environments. func (apiSuite *APISuite) Capabilities() Capabilities { v, err := apiSuite.Client.Version(context.Background()) apiSuite.Require().NoError(err) caps := Capabilities{} if v.Messages[0].Platform != nil { switch v.Messages[0].Platform.Mode { case runtime.ModeContainer.String(): default: caps.RunsTalosKernel = true caps.SupportsReboot = true caps.SupportsRecover = true caps.SupportsVolumes = true } } ctx := context.Background() ctx, ctxCancel := context.WithTimeout(ctx, 2*time.Minute) defer ctxCancel() securityResource, err := safe.StateWatchFor[*runtimeres.SecurityState]( ctx, apiSuite.Client.COSI, runtimeres.NewSecurityStateSpec(runtimeres.NamespaceName).Metadata(), state.WithEventTypes(state.Created, state.Updated), ) apiSuite.Require().NoError(err) caps.SecureBooted = securityResource.TypedSpec().SecureBoot return caps } // AssertClusterHealthy verifies that cluster is healthy using provisioning checks. func (apiSuite *APISuite) AssertClusterHealthy(ctx context.Context) { if apiSuite.Cluster == nil { // can't assert if cluster state was provided apiSuite.T().Skip("cluster health can't be verified when cluster state is not provided") } clusterAccess := access.NewAdapter(apiSuite.Cluster, provision.WithTalosClient(apiSuite.Client)) defer clusterAccess.Close() //nolint:errcheck apiSuite.Require().NoError(check.Wait(ctx, clusterAccess, append(check.DefaultClusterChecks(), check.ExtraClusterChecks()...), check.StderrReporter())) } // ReadBootID reads node boot_id. // // Context provided might have specific node attached for API call. func (apiSuite *APISuite) ReadBootID(ctx context.Context) (string, error) { // set up a short timeout around boot_id read calls to work around // cases when rebooted node doesn't answer for a long time on requests reqCtx, reqCtxCancel := context.WithTimeout(ctx, 10*time.Second) defer reqCtxCancel() reader, err := apiSuite.Client.Read(reqCtx, "/proc/sys/kernel/random/boot_id") if err != nil { return "", err } defer reader.Close() //nolint:errcheck body, err := io.ReadAll(reader) if err != nil { return "", err } bootID := strings.TrimSpace(string(body)) _, err = io.Copy(io.Discard, reader) if err != nil { return "", err } return bootID, reader.Close() } // ReadBootIDWithRetry reads node boot_id. // // Context provided might have specific node attached for API call. func (apiSuite *APISuite) ReadBootIDWithRetry(ctx context.Context, timeout time.Duration) string { var bootID string apiSuite.Require().NoError(retry.Constant(timeout, retry.WithUnits(time.Millisecond*1000)).Retry( func() error { var err error bootID, err = apiSuite.ReadBootID(ctx) if err != nil { return retry.ExpectedError(err) } if bootID == "" { return retry.ExpectedErrorf("boot id is empty") } return nil }, )) return bootID } // AssertRebooted verifies that node got rebooted as result of running some API call. // // Verification happens via reading boot_id of the node. func (apiSuite *APISuite) AssertRebooted(ctx context.Context, node string, rebootFunc func(nodeCtx context.Context) error, timeout time.Duration, extraHooks ...func(context.Context, string)) { apiSuite.AssertRebootedNoChecks(ctx, node, rebootFunc, timeout) apiSuite.WaitForBootDone(ctx) for _, hook := range extraHooks { hook(ctx, node) } if apiSuite.Cluster != nil { // without cluster state we can't do deep checks, but basic reboot test still works // NB: using `ctx` here to have client talking to init node by default apiSuite.AssertClusterHealthy(ctx) } } // AssertRebootedNoChecks waits for node to be rebooted without waiting for cluster to become healthy afterwards. func (apiSuite *APISuite) AssertRebootedNoChecks(ctx context.Context, node string, rebootFunc func(nodeCtx context.Context) error, timeout time.Duration) { // timeout for single node Reset ctx, ctxCancel := context.WithTimeout(ctx, timeout) defer ctxCancel() nodeCtx := client.WithNode(ctx, node) // read boot_id before reboot bootIDBefore, err := apiSuite.ReadBootID(nodeCtx) apiSuite.Require().NoError(err, "failed to read boot_id before reboot") apiSuite.Assert().NotEmpty(bootIDBefore, "boot_id should not be empty") apiSuite.Assert().NoError(rebootFunc(nodeCtx)) apiSuite.AssertBootIDChanged(nodeCtx, bootIDBefore, node, timeout) } // AssertBootIDChanged waits until node boot id changes. func (apiSuite *APISuite) AssertBootIDChanged(nodeCtx context.Context, bootIDBefore, node string, timeout time.Duration) { apiSuite.Require().NoError(retry.Constant(timeout).Retry(func() error { requestCtx, requestCtxCancel := context.WithTimeout(nodeCtx, time.Second) defer requestCtxCancel() bootIDAfter, err := apiSuite.ReadBootID(requestCtx) if err != nil { // API might be unresponsive during reboot return retry.ExpectedError(err) } if bootIDAfter == bootIDBefore { // bootID should be different after reboot return retry.ExpectedErrorf("bootID didn't change for node %q: before %s, after %s", node, bootIDBefore, bootIDAfter) } return nil })) } // WaitForBootDone waits for boot phase done event. func (apiSuite *APISuite) WaitForBootDone(ctx context.Context) { apiSuite.WaitForSequenceDone( ctx, runtime.SequenceBoot, apiSuite.DiscoverNodeInternalIPs(ctx)..., ) } // WaitForSequenceDone waits for sequence done event. func (apiSuite *APISuite) WaitForSequenceDone(ctx context.Context, sequence runtime.Sequence, nodes ...string) { nodesNotDone := make(map[string]struct{}) for _, node := range nodes { nodesNotDone[node] = struct{}{} } apiSuite.Require().NoError(retry.Constant(5*time.Minute, retry.WithUnits(time.Second*10)).Retry(func() error { eventsCtx, cancel := context.WithTimeout(client.WithNodes(ctx, nodes...), 5*time.Second) defer cancel() err := apiSuite.Client.EventsWatch(eventsCtx, func(ch <-chan client.Event) { defer cancel() for event := range ch { if msg, ok := event.Payload.(*machineapi.SequenceEvent); ok { if msg.GetAction() == machineapi.SequenceEvent_STOP && msg.GetSequence() == sequence.String() { delete(nodesNotDone, event.Node) if len(nodesNotDone) == 0 { return } } } } }, client.WithTailEvents(-1)) if err != nil { return retry.ExpectedError(err) } if len(nodesNotDone) > 0 { return retry.ExpectedErrorf("nodes %#v sequence %s is not completed", nodesNotDone, sequence.String()) } return nil })) } // ClearConnectionRefused clears cached connection refused errors which might be left after node reboot. func (apiSuite *APISuite) ClearConnectionRefused(ctx context.Context, nodes ...string) { ctx, cancel := context.WithTimeout(ctx, backoff.DefaultConfig.MaxDelay) defer cancel() controlPlaneNodes := apiSuite.DiscoverNodes(ctx).NodesByType(machine.TypeControlPlane) initNodes := apiSuite.DiscoverNodes(ctx).NodesByType(machine.TypeInit) numMasterNodes := len(controlPlaneNodes) + len(initNodes) if numMasterNodes == 0 { numMasterNodes = 3 } apiSuite.T().Log("run ClearConnectionRefused") apiSuite.Require().NoError(retry.Constant(backoff.DefaultConfig.MaxDelay, retry.WithUnits(time.Second)).Retry(func() error { for range numMasterNodes * 5 { _, err := apiSuite.Client.Version(client.WithNodes(ctx, nodes...)) if err == nil { continue } apiSuite.T().Log(err.Error()) if client.StatusCode(err) == codes.Unavailable || client.StatusCode(err) == codes.Canceled { return retry.ExpectedError(err) } if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "connection reset by peer") { return retry.ExpectedError(err) } return err } return nil })) } // HashKubeletCert returns hash of the kubelet certificate file. // // This function can be used to verify that the node ephemeral partition got wiped. func (apiSuite *APISuite) HashKubeletCert(ctx context.Context, node string) (string, error) { reqCtx, reqCtxCancel := context.WithTimeout(ctx, 10*time.Second) defer reqCtxCancel() reqCtx = client.WithNodes(reqCtx, node) reader, err := apiSuite.Client.Read(reqCtx, "/var/lib/kubelet/pki/kubelet-client-current.pem") if err != nil { return "", err } defer reader.Close() //nolint:errcheck hash := sha256.New() _, err = io.Copy(hash, reader) if err != nil { if client.StatusCode(err) != codes.NotFound { // not found, swallow it return "", err } } return hex.EncodeToString(hash.Sum(nil)), reader.Close() } // ReadConfigFromNode reads machine configuration from the node. func (apiSuite *APISuite) ReadConfigFromNode(nodeCtx context.Context) (config.Provider, error) { cfg, err := safe.StateGetByID[*configres.MachineConfig](nodeCtx, apiSuite.Client.COSI, configres.ActiveID) if err != nil { return nil, fmt.Errorf("error fetching machine config resource: %w", err) } return cfg.Provider(), nil } // UserDisks returns list of user disks not having any partitions present. func (apiSuite *APISuite) UserDisks(ctx context.Context, node string) []string { nodeCtx := client.WithNode(ctx, node) disks, err := safe.ReaderListAll[*block.Disk](nodeCtx, apiSuite.Client.COSI) apiSuite.Require().NoError(err, "failed to list disks") var candidateDisks []string //nolint:prealloc for disk := range disks.All() { // skip CD-ROM, readonly and disks without transport (this is usually lvms, md, zfs devices etc) // also skip iscsi disks (these are created in tests) if disk.TypedSpec().Readonly || disk.TypedSpec().CDROM || disk.TypedSpec().Transport == "" || disk.TypedSpec().Transport == "iscsi" { continue } candidateDisks = append(candidateDisks, disk.Metadata().ID()) } var availableDisks []string for _, disk := range candidateDisks { discoveredVolume, err := safe.ReaderGetByID[*block.DiscoveredVolume](nodeCtx, apiSuite.Client.COSI, disk) apiSuite.Require().NoError(err, "failed to get discovered volume") if discoveredVolume.TypedSpec().Name == "" { availableDisks = append(availableDisks, discoveredVolume.TypedSpec().DevPath) } } return availableDisks } // AssertServicesRunning verifies that services are running on the node. func (apiSuite *APISuite) AssertServicesRunning(ctx context.Context, node string, serviceStatus map[string]string) { nodeCtx := client.WithNode(ctx, node) for svc, state := range serviceStatus { resp, err := apiSuite.Client.ServiceInfo(nodeCtx, svc) apiSuite.Require().NoError(err) apiSuite.Require().NotNil(resp, "expected service %s to be registered", svc) for _, svcInfo := range resp { apiSuite.Require().Equal(state, svcInfo.Service.State, "expected service %s to have state %s", svc, state) } } } // AssertExpectedModules verifies that expected kernel modules are loaded on the node. func (apiSuite *APISuite) AssertExpectedModules(ctx context.Context, node string, expectedModules map[string]string) { nodeCtx := client.WithNode(ctx, node) fileReader, err := apiSuite.Client.Read(nodeCtx, "/proc/modules") apiSuite.Require().NoError(err) defer func() { apiSuite.Require().NoError(fileReader.Close()) }() scanner := bufio.NewScanner(fileReader) var loadedModules []string for scanner.Scan() { loadedModules = append(loadedModules, strings.Split(scanner.Text(), " ")[0]) } apiSuite.Require().NoError(scanner.Err()) fileReader, err = apiSuite.Client.Read(nodeCtx, fmt.Sprintf("/usr/lib/modules/%s/modules.dep", constants.DefaultKernelVersion)) apiSuite.Require().NoError(err) defer func() { apiSuite.Require().NoError(fileReader.Close()) }() scanner = bufio.NewScanner(fileReader) var modulesDep []string for scanner.Scan() { modulesDep = append(modulesDep, filepath.Base(strings.Split(scanner.Text(), ":")[0])) } apiSuite.Require().NoError(scanner.Err()) for module, moduleDep := range expectedModules { apiSuite.Require().Contains(loadedModules, module, "expected %s to be loaded", module) apiSuite.Require().Contains(modulesDep, moduleDep, "expected %s to be in modules.dep", moduleDep) } } // UpdateMachineConfig fetches machine configuration, patches it and applies the changes. func (apiSuite *APISuite) UpdateMachineConfig( nodeCtx context.Context, modeSetter func(*machineapi.ApplyConfigurationRequest), patch func(config.Provider) (config.Provider, error), ) { cfg, err := apiSuite.ReadConfigFromNode(nodeCtx) apiSuite.Require().NoError(err) patchedCfg, err := patch(cfg) apiSuite.Require().NoError(err) bytes, err := patchedCfg.Bytes() apiSuite.Require().NoError(err) req := &machineapi.ApplyConfigurationRequest{ Data: bytes, Mode: machineapi.ApplyConfigurationRequest_AUTO, } modeSetter(req) resp, err := apiSuite.Client.ApplyConfiguration(nodeCtx, req) apiSuite.Require().NoError(err) apiSuite.T().Logf("patched machine config: %s", resp.Messages[0].ModeDetails) } // PatchMachineConfig patches machine configuration on the node. func (apiSuite *APISuite) PatchMachineConfig(nodeCtx context.Context, patches ...any) { apiSuite.PatchMachineConfigWithModeSetter(nodeCtx, func(*machineapi.ApplyConfigurationRequest) {}, patches...) } // PatchMachineConfigWithModeSetter patches machine configuration on the node with a specific mode. func (apiSuite *APISuite) PatchMachineConfigWithModeSetter( nodeCtx context.Context, modeSetter func(*machineapi.ApplyConfigurationRequest), patches ...any, ) { configPatches := make([]configpatcher.Patch, 0, len(patches)) for _, patch := range patches { marshaled, err := yaml.Marshal(patch) apiSuite.Require().NoError(err) configPatch, err := configpatcher.LoadPatch(marshaled) apiSuite.Require().NoError(err) configPatches = append(configPatches, configPatch) } apiSuite.UpdateMachineConfig(nodeCtx, modeSetter, func(cfg config.Provider) (config.Provider, error) { out, err := configpatcher.Apply(configpatcher.WithConfig(cfg), configPatches) if err != nil { return nil, err } return out.Config() }) } // RemoveMachineConfigDocuments removes machine configuration documents of specified type from the node. func (apiSuite *APISuite) RemoveMachineConfigDocuments(nodeCtx context.Context, docTypes ...string) { apiSuite.UpdateMachineConfig(nodeCtx, func(*machineapi.ApplyConfigurationRequest) {}, func(cfg config.Provider) (config.Provider, error) { return container.New(xslices.Filter(cfg.Documents(), func(doc configconfig.Document) bool { return slices.Index(docTypes, doc.Kind()) == -1 })...) }) } // RemoveMachineConfigDocumentsByName removes machine configuration documents of specified type and names from the node. func (apiSuite *APISuite) RemoveMachineConfigDocumentsByName(nodeCtx context.Context, docType string, names ...string) { apiSuite.UpdateMachineConfig(nodeCtx, func(*machineapi.ApplyConfigurationRequest) {}, func(cfg config.Provider) (config.Provider, error) { return container.New(xslices.Filter(cfg.Documents(), func(doc configconfig.Document) bool { if doc.Kind() != docType { return true } namedDoc, ok := doc.(configconfig.NamedDocument) if !ok { return true } return slices.Index(names, namedDoc.Name()) == -1 })...) }) } // PatchV1Alpha1Config patches v1alpha1 config in the config provider. func (apiSuite *APISuite) PatchV1Alpha1Config(provider config.Provider, patch func(*v1alpha1.Config)) []byte { ctr, err := provider.PatchV1Alpha1(func(c *v1alpha1.Config) error { patch(c) return nil }) apiSuite.Require().NoError(err) bytes, err := ctr.Bytes() apiSuite.Require().NoError(err) return bytes } // ResetNode wraps the reset node sequence with checks, waiting for the reset to finish and verifying the result. // //nolint:gocyclo func (apiSuite *APISuite) ResetNode(ctx context.Context, node string, resetSpec *machineapi.ResetRequest, runHealthChecks bool) { apiSuite.T().Logf("resetting node %q with graceful %v mode %s, system %v, user %v", node, resetSpec.Graceful, resetSpec.Mode, resetSpec.SystemPartitionsToWipe, resetSpec.UserDisksToWipe) nodeCtx := client.WithNode(ctx, node) nodeClient := apiSuite.GetClientWithEndpoints(node) defer nodeClient.Close() //nolint:errcheck // any reset should lead to a reboot, so read boot_id before reboot bootIDBefore, err := apiSuite.ReadBootID(nodeCtx) apiSuite.Require().NoError(err) // figure out if EPHEMERAL is going to be reset ephemeralIsGoingToBeReset := false if len(resetSpec.SystemPartitionsToWipe) == 0 && len(resetSpec.UserDisksToWipe) == 0 { ephemeralIsGoingToBeReset = true } else { for _, part := range resetSpec.SystemPartitionsToWipe { if part.Label == constants.EphemeralPartitionLabel { ephemeralIsGoingToBeReset = true break } } } preReset, err := apiSuite.HashKubeletCert(ctx, node) apiSuite.Require().NoError(err) resp, err := nodeClient.ResetGenericWithResponse(nodeCtx, resetSpec) apiSuite.Require().NoError(err) actorID := resp.Messages[0].ActorId eventCh := make(chan client.EventResult) // watch for events apiSuite.Require().NoError(nodeClient.EventsWatchV2(nodeCtx, eventCh, client.WithActorID(actorID), client.WithTailEvents(-1))) waitTimer := time.NewTimer(5 * time.Minute) defer waitTimer.Stop() waitLoop: for { select { case ev := <-eventCh: apiSuite.Require().NoError(ev.Error) switch msg := ev.Event.Payload.(type) { case *machineapi.SequenceEvent: if msg.Error != nil { apiSuite.FailNow("reset failed", "%s: %s", msg.Error.Message, msg.Error.Code) } case *machineapi.PhaseEvent: if msg.Action == machineapi.PhaseEvent_START && msg.Phase == "unmountSystem" { // about to be reset, break waitLoop break waitLoop } if msg.Action == machineapi.PhaseEvent_STOP { apiSuite.T().Logf("reset phase %q finished", msg.Phase) } } case <-waitTimer.C: apiSuite.FailNow("timeout waiting for reset to finish") case <-ctx.Done(): apiSuite.FailNow("context canceled") } } // wait for the apid to be shut down time.Sleep(10 * time.Second) apiSuite.AssertBootIDChanged(nodeCtx, bootIDBefore, node, 3*time.Minute) apiSuite.ClearConnectionRefused(ctx, node) if runHealthChecks { if apiSuite.Cluster != nil { // without cluster state we can't do deep checks, but basic reboot test still works // NB: using `ctx` here to have client talking to init node by default apiSuite.AssertClusterHealthy(ctx) } postReset, err := apiSuite.HashKubeletCert(ctx, node) apiSuite.Require().NoError(err) if ephemeralIsGoingToBeReset { apiSuite.Assert().NotEqual(preReset, postReset, "reset should lead to new kubelet cert being generated") } else { apiSuite.Assert().Equal(preReset, postReset, "ephemeral partition was not reset") } } } // DumpLogs dumps a set of logs from the node. func (apiSuite *APISuite) DumpLogs(ctx context.Context, node string, service, pattern string) { nodeCtx := client.WithNode(ctx, node) logsStream, err := apiSuite.Client.Logs( nodeCtx, constants.SystemContainerdNamespace, common.ContainerDriver_CONTAINERD, service, false, -1, ) apiSuite.Require().NoError(err) logReader, err := client.ReadStream(logsStream) apiSuite.Require().NoError(err) defer logReader.Close() //nolint:errcheck scanner := bufio.NewScanner(logReader) for scanner.Scan() { if pattern == "" || strings.Contains(scanner.Text(), pattern) { apiSuite.T().Logf("%s (%s): %s", node, service, scanner.Text()) } } } // ReadFile reads file from the node. func (apiSuite *APISuite) ReadFile(nodeCtx context.Context, path string) string { reader, err := apiSuite.Client.Read(nodeCtx, path) apiSuite.Require().NoError(err) defer reader.Close() //nolint:errcheck body, err := io.ReadAll(reader) apiSuite.Require().NoError(err) content := strings.TrimSpace(string(body)) _, err = io.Copy(io.Discard, reader) apiSuite.Require().NoError(err) apiSuite.Require().NoError(reader.Close()) return content } // ReadCmdline reads cmdline from the node. func (apiSuite *APISuite) ReadCmdline(nodeCtx context.Context) string { return apiSuite.ReadFile(nodeCtx, "/proc/cmdline") } // TearDownSuite closes Talos API client. func (apiSuite *APISuite) TearDownSuite() { if apiSuite.Client != nil { apiSuite.Assert().NoError(apiSuite.Client.Close()) } } func mapNodeInfosToInternalIPs(nodes []cluster.NodeInfo) []string { ips := make([]string, len(nodes)) for i, node := range nodes { ips[i] = node.InternalIP.String() } return ips } ================================================ FILE: internal/integration/base/base.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package base provides shared definition of base suites for tests package base import ( "context" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/access" ) const ( // ProvisionerQEMU is the name of the QEMU provisioner. ProvisionerQEMU = "qemu" // ProvisionerDocker is the name of the Docker provisioner. ProvisionerDocker = "docker" ) // TalosSuite defines most common settings for integration test suites. type TalosSuite struct { // Endpoint to use to connect, if not set config is used Endpoint string // K8sEndpoint is API server endpoint, if set overrides kubeconfig K8sEndpoint string // Cluster describes provisioned cluster, used for discovery purposes Cluster provision.Cluster // TalosConfig is a path to talosconfig TalosConfig string // Version is the (expected) version of Talos tests are running against Version string // GoVersion is the (expected) version of Go compiler. GoVersion string // TalosctlPath is a path to talosctl binary TalosctlPath string // KubectlPath is a path to kubectl binary KubectlPath string // HelmPath is a path to helm binary HelmPath string // KubeStrPath is a path to kubestr binary KubeStrPath string // ExtensionsQEMU runs tests with qemu and extensions enabled ExtensionsQEMU bool // ExtensionsNvidia runs tests with nvidia extensions enabled ExtensionsNvidia bool // TrustedBoot tells if the cluster is secure booted and disks are encrypted TrustedBoot bool // SelinuxEnforcing tells if the cluster is booted with the image with selinux enforcement enabled SelinuxEnforcing bool // VerifyUKIBooted runs tests to verify Talos is booted from a UKI VerifyUKIBooted bool // TalosImage is the image name for 'talos' container. TalosImage string // CSITestName is the name of the CSI test to run CSITestName string // CSITestTimeout is the timeout for the CSI test CSITestTimeout string // Airgapped marks that cluster has no access to external networks Airgapped bool // Virtiofsd marks that cluster has virtiofs volumes (virtiofsd is running for workers) Virtiofsd bool // Race informs test suites about race detector being enabled (e.g. for skipping incompatible tests) Race bool discoveredNodes cluster.Info } // DiscoverNodes provides basic functionality to discover cluster nodes via test settings. // // This method is overridden in specific suites to allow for specific discovery. func (talosSuite *TalosSuite) DiscoverNodes(_ context.Context) cluster.Info { if talosSuite.discoveredNodes == nil { if talosSuite.Cluster != nil { talosSuite.discoveredNodes = access.NewAdapter(talosSuite.Cluster).Info } } return talosSuite.discoveredNodes } // ConfiguredSuite expects config to be set before running. type ConfiguredSuite interface { SetConfig(config TalosSuite) } // SetConfig implements ConfiguredSuite. func (talosSuite *TalosSuite) SetConfig(config TalosSuite) { *talosSuite = config } // NamedSuite interface provides names for test suites. type NamedSuite interface { SuiteName() string } ================================================ FILE: internal/integration/base/cli.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package base import ( "context" "fmt" "math/rand/v2" "os/exec" "path/filepath" "regexp" "slices" "strings" "testing" "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // CLISuite is a base suite for CLI tests. type CLISuite struct { suite.Suite TalosSuite } // DiscoverNodes provides list of Talos nodes in the cluster. // // As there's no way to provide this functionality via Talos CLI, it relies on cluster info. func (cliSuite *CLISuite) DiscoverNodes(ctx context.Context) cluster.Info { discoveredNodes := cliSuite.TalosSuite.DiscoverNodes(ctx) if discoveredNodes != nil { return discoveredNodes } return cliSuite.discoverKubectl() } // DiscoverNodeInternalIPs provides list of Talos node internal IPs in the cluster. func (cliSuite *CLISuite) DiscoverNodeInternalIPs(ctx context.Context) []string { nodes := cliSuite.DiscoverNodes(ctx) return mapNodeInfosToInternalIPs(nodes.Nodes()) } // DiscoverNodeInternalIPsByType provides list of Talos node internal IPs in the cluster for given machine type. func (cliSuite *CLISuite) DiscoverNodeInternalIPsByType(ctx context.Context, machineType machine.Type) []string { nodesByType := cliSuite.DiscoverNodes(ctx).NodesByType(machineType) return mapNodeInfosToInternalIPs(nodesByType) } // RandomDiscoveredNodeInternalIP returns the internal IP a random node of the specified type (or any type if no types are specified). func (cliSuite *CLISuite) RandomDiscoveredNodeInternalIP(types ...machine.Type) string { nodeInfo := cliSuite.DiscoverNodes(context.TODO()) var nodes []cluster.NodeInfo if len(types) == 0 { nodes = nodeInfo.Nodes() } else { for _, t := range types { nodes = append(nodes, nodeInfo.NodesByType(t)...) } } cliSuite.Require().NotEmpty(nodes) return nodes[rand.IntN(len(nodes))].InternalIP.String() } func (cliSuite *CLISuite) discoverKubectl() cluster.Info { // pull down kubeconfig into temporary directory tempDir := cliSuite.T().TempDir() // rely on `nodes:` being set in talosconfig cliSuite.RunCLI([]string{"kubeconfig", tempDir}, StdoutEmpty()) masterNodes, err := cmd.RunWithOptions(cliSuite.T().Context(), cliSuite.KubectlPath, []string{ "--kubeconfig", filepath.Join(tempDir, "kubeconfig"), "get", "nodes", "-o", "jsonpath={.items[*].status.addresses[?(@.type==\"InternalIP\")].address}", fmt.Sprintf("--selector=%s", constants.LabelNodeRoleControlPlane), }) cliSuite.Require().NoError(err) workerNodes, err := cmd.RunWithOptions(cliSuite.T().Context(), cliSuite.KubectlPath, []string{ "--kubeconfig", filepath.Join(tempDir, "kubeconfig"), "get", "nodes", "-o", "jsonpath={.items[*].status.addresses[?(@.type==\"InternalIP\")].address}", fmt.Sprintf("--selector=!%s", constants.LabelNodeRoleControlPlane), }) cliSuite.Require().NoError(err) nodeInfo, err := newNodeInfo( strings.Fields(strings.TrimSpace(masterNodes)), strings.Fields(strings.TrimSpace(workerNodes)), ) cliSuite.Require().NoError(err) return nodeInfo } // RunCLI runs talosctl binary with the options provided. func (cliSuite *CLISuite) RunCLI(args []string, options ...RunOption) (stdout, stderr string) { return run(cliSuite.T(), cliSuite.MakeCMDFn(args), options...) } // MakeCMDFn returns a function that creates a new exec.Cmd with the provided args. // TalosSuite flags are added at the beginning so they can be overridden by args. func (cliSuite *CLISuite) MakeCMDFn(args []string) func() *exec.Cmd { if cliSuite.Endpoint != "" { args = append([]string{"--endpoints", cliSuite.Endpoint}, args...) } var firstArg string for _, arg := range args { if strings.HasPrefix(arg, "--") { // this is a flag, skip it continue } firstArg = arg break } mgmtCommand := slices.Contains([]string{"completion", "gen", "inject", "machineconfig", "validate"}, firstArg) if !mgmtCommand { args = append([]string{"--talosconfig", cliSuite.TalosConfig}, args...) } path := cliSuite.TalosctlPath return func() *exec.Cmd { return exec.CommandContext(cliSuite.T().Context(), path, args...) } } // RunCLI runs talosctl binary with the options provided. func RunCLI(t *testing.T, f func() *exec.Cmd, options ...RunOption) (stdout, stderr string) { return run(t, f, options...) } // RunAndWaitForMatch retries command until output matches. func (cliSuite *CLISuite) RunAndWaitForMatch(args []string, regex *regexp.Regexp, duration time.Duration, options ...retry.Option) { cliSuite.Assert().NoError(retry.Constant(duration, options...).Retry(func() error { stdout, _, err := runAndWait(cliSuite.Suite.T(), cliSuite.MakeCMDFn(args)(), nil) if err != nil { return err } if !regex.MatchString(stdout.String()) { return retry.ExpectedErrorf("stdout doesn't match: %q", stdout) } return nil })) } ================================================ FILE: internal/integration/base/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration package base import ( "slices" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) type infoWrapper struct { nodeInfos []cluster.NodeInfo nodeInfosByType map[machine.Type][]cluster.NodeInfo } func newNodeInfo(masterNodes, workerNodes []string) (*infoWrapper, error) { controlPlaneNodeInfos, err := cluster.IPsToNodeInfos(masterNodes) if err != nil { return nil, err } workerNodeInfos, err := cluster.IPsToNodeInfos(workerNodes) if err != nil { return nil, err } return &infoWrapper{ nodeInfos: slices.Concat(controlPlaneNodeInfos, workerNodeInfos), nodeInfosByType: map[machine.Type][]cluster.NodeInfo{ machine.TypeControlPlane: controlPlaneNodeInfos, machine.TypeWorker: workerNodeInfos, }, }, nil } func (wrapper *infoWrapper) Nodes() []cluster.NodeInfo { return wrapper.nodeInfos } func (wrapper *infoWrapper) NodesByType(t machine.Type) []cluster.NodeInfo { return wrapper.nodeInfosByType[t] } ================================================ FILE: internal/integration/base/discovery_k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package base import ( "context" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) //nolint:gocyclo func discoverNodesK8s(ctx context.Context, client *client.Client, suite *TalosSuite) (cluster.Info, error) { kubeconfig, err := client.Kubeconfig(ctx) if err != nil { return nil, err } config, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load(kubeconfig) }) if err != nil { return nil, err } // patch timeout if suite.K8sEndpoint != "" { config.Host = suite.K8sEndpoint } clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, err } nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } var masterNodes, workerNodes []string for _, node := range nodes.Items { var address string for _, nodeAddress := range node.Status.Addresses { if nodeAddress.Type == v1.NodeInternalIP { address = nodeAddress.Address break } } if address == "" { continue } if _, ok := node.Labels[constants.LabelNodeRoleControlPlane]; ok { masterNodes = append(masterNodes, address) } else { workerNodes = append(workerNodes, address) } } return newNodeInfo(masterNodes, workerNodes) } ================================================ FILE: internal/integration/base/discovery_nok8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration && !integration_k8s package base import ( "context" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/client" ) func discoverNodesK8s(ctx context.Context, client *client.Client, suite *TalosSuite) (cluster.Info, error) { return nil, nil } ================================================ FILE: internal/integration/base/errors.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration package base import ( "errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // IgnoreGRPCUnavailable searches through unwrapped errors and ignores the error if it is grpc.Unavailable. func IgnoreGRPCUnavailable(err error) error { if err == nil { return nil } unwrappedErr := err for { if s, ok := status.FromError(unwrappedErr); ok && s.Code() == codes.Unavailable { // ignore errors if reboot happens before response is fully received return nil } unwrappedErr = errors.Unwrap(unwrappedErr) if unwrappedErr == nil { break } } return err } ================================================ FILE: internal/integration/base/flags.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration package base import ( "slices" "strings" "github.com/siderolabs/gen/xiter/xstrings" ) // StringList implements flag.Value for list of strings. type StringList []string // String implements flag.Value. func (l *StringList) String() string { return strings.Join(*l, ",") } // Set implements flag.Value. func (l *StringList) Set(value string) error { *l = slices.AppendSeq(*l, xstrings.SplitSeq(value, ",")) return nil } ================================================ FILE: internal/integration/base/k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package base import ( "bufio" "bytes" "context" "crypto/rand" "encoding/json" "fmt" "io" "os" "os/exec" "path/filepath" "slices" "strings" "time" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" eventsv1 "k8s.io/api/events/v1" "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/fields" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" "k8s.io/client-go/dynamic" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/tools/remotecommand" watchtools "k8s.io/client-go/tools/watch" "k8s.io/client-go/util/jsonpath" "k8s.io/kubectl/pkg/scheme" taloskubernetes "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" ) // K8sSuite is a base suite for K8s tests. type K8sSuite struct { APISuite Clientset *kubernetes.Clientset DynamicClient dynamic.Interface DiscoveryClient *discovery.DiscoveryClient RestConfig *rest.Config Mapper *restmapper.DeferredDiscoveryRESTMapper } // SetupSuite initializes Kubernetes client. func (k8sSuite *K8sSuite) SetupSuite() { k8sSuite.APISuite.SetupSuite() kubeconfig, err := k8sSuite.Client.Kubeconfig(context.Background()) k8sSuite.Require().NoError(err) config, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load(kubeconfig) }) k8sSuite.Require().NoError(err) if k8sSuite.K8sEndpoint != "" { config.Host = k8sSuite.K8sEndpoint } k8sSuite.RestConfig = config k8sSuite.Clientset, err = kubernetes.NewForConfig(config) k8sSuite.Require().NoError(err) k8sSuite.DynamicClient, err = dynamic.NewForConfig(config) k8sSuite.Require().NoError(err) k8sSuite.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(config) k8sSuite.Require().NoError(err) k8sSuite.Mapper = restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(k8sSuite.DiscoveryClient)) } // GetK8sNodeByInternalIP returns the kubernetes node by its internal ip or error if it is not found. func (k8sSuite *K8sSuite) GetK8sNodeByInternalIP(ctx context.Context, internalIP string) (*corev1.Node, error) { nodeList, err := k8sSuite.Clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } for _, item := range nodeList.Items { for _, address := range item.Status.Addresses { if address.Type == corev1.NodeInternalIP { if address.Address == internalIP { return &item, nil } } } } return nil, fmt.Errorf("node with internal IP %s not found", internalIP) } // CleanupFailedPods deletes all pods in kube-system namespace with the status Failed. func (k8sSuite *K8sSuite) CleanupFailedPods(ctx context.Context, internalIP string) { nodeName, err := k8sSuite.GetK8sNodeByInternalIP(ctx, internalIP) if err != nil { k8sSuite.T().Logf("failed to get node by internal IP %s: %v", internalIP, err) return } pods, err := k8sSuite.Clientset.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("spec.nodeName", nodeName.Name).String(), }) if err != nil { k8sSuite.T().Logf("failed to list pods in kube-system namespace: %v", err) return } for _, pod := range pods.Items { if pod.Status.Phase == corev1.PodFailed { if err := k8sSuite.Clientset.CoreV1().Pods("kube-system").Delete(ctx, pod.Name, metav1.DeleteOptions{}); err != nil { k8sSuite.T().Logf("failed to delete pod %s: %v", pod.Name, err) } else { k8sSuite.T().Logf("deleted pod %s", pod.Name) } } } } // WaitForK8sNodeReadinessStatus waits for node to have the given status. // It retries until the node with the name is found and matches the expected condition. func (k8sSuite *K8sSuite) WaitForK8sNodeReadinessStatus(ctx context.Context, nodeName string, checkFn func(corev1.ConditionStatus) bool) error { return retry.Constant(5 * time.Minute).Retry(func() error { readinessStatus, err := k8sSuite.GetK8sNodeReadinessStatus(ctx, nodeName) if errors.IsNotFound(err) { return retry.ExpectedError(err) } if taloskubernetes.IsRetryableError(err) { return retry.ExpectedError(err) } if err != nil { return err } if !checkFn(readinessStatus) { return retry.ExpectedErrorf("node readiness status is %s", readinessStatus) } return nil }) } // GetK8sNodeReadinessStatus returns the node readiness status of the node. func (k8sSuite *K8sSuite) GetK8sNodeReadinessStatus(ctx context.Context, nodeName string) (corev1.ConditionStatus, error) { node, err := k8sSuite.Clientset.CoreV1().Nodes().Get(ctx, nodeName, metav1.GetOptions{}) if err != nil { return "", err } for _, condition := range node.Status.Conditions { if condition.Type == corev1.NodeReady { return condition.Status, nil } } return "", fmt.Errorf("node %s has no readiness condition", nodeName) } // DeleteResource deletes the resource with the given GroupVersionResource, namespace and name. // Does not return an error if the resource is not found. func (k8sSuite *K8sSuite) DeleteResource(ctx context.Context, gvr schema.GroupVersionResource, ns, name string) error { err := k8sSuite.DynamicClient.Resource(gvr).Namespace(ns).Delete(ctx, name, metav1.DeleteOptions{}) if errors.IsNotFound(err) { return nil } return err } // EnsureResourceIsDeleted ensures that the resource with the given GroupVersionResource, namespace and name does not exist on Kubernetes. // It repeatedly checks the resource for the given duration. func (k8sSuite *K8sSuite) EnsureResourceIsDeleted( ctx context.Context, duration time.Duration, gvr schema.GroupVersionResource, ns, name string, ) error { return retry.Constant(duration).RetryWithContext(ctx, func(ctx context.Context) error { _, err := k8sSuite.DynamicClient.Resource(gvr).Namespace(ns).Get(ctx, name, metav1.GetOptions{}) if errors.IsNotFound(err) { return nil } if err == nil { return retry.ExpectedErrorf("resource %s %s/%s still exists", gvr, ns, name) } return err }) } // WaitForEventExists waits for the event with the given namespace and check condition to exist on Kubernetes. func (k8sSuite *K8sSuite) WaitForEventExists(ctx context.Context, ns string, checkFn func(event eventsv1.Event) bool) error { return retry.Constant(15*time.Second).RetryWithContext(ctx, func(ctx context.Context) error { events, err := k8sSuite.Clientset.EventsV1().Events(ns).List(ctx, metav1.ListOptions{}) filteredEvents := xslices.Filter(events.Items, func(item eventsv1.Event) bool { return checkFn(item) }) if len(filteredEvents) == 0 { return retry.ExpectedError(err) } return nil }) } type podInfo interface { Name() string WithNodeName(nodeName string) podInfo WithQuiet(quiet bool) podInfo WithNamespace(namespace string) podInfo WithHostVolumeMount(hostPath, mountPath string) podInfo Create(ctx context.Context, waitTimeout time.Duration) error Delete(ctx context.Context) error Exec(ctx context.Context, command string) (string, string, error) } type pod struct { name string namespace string pod *corev1.Pod quiet bool suite *K8sSuite } func (p *pod) Name() string { return p.name } func (p *pod) WithNodeName(nodeName string) podInfo { p.pod.Spec.NodeName = nodeName return p } func (p *pod) WithQuiet(quiet bool) podInfo { p.quiet = quiet return p } func (p *pod) WithNamespace(namespace string) podInfo { p.namespace = namespace return p } func (p *pod) WithHostVolumeMount(hostPath, mountPath string) podInfo { name := filepath.Base(hostPath) p.pod.Spec.Volumes = append(p.pod.Spec.Volumes, corev1.Volume{ Name: name, VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ Path: hostPath, Type: new(corev1.HostPathDirectoryOrCreate), }, }, }) p.pod.Spec.Containers[0].VolumeMounts = append(p.pod.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ Name: name, MountPath: mountPath, }) return p } func (p *pod) Create(ctx context.Context, waitTimeout time.Duration) error { _, err := p.suite.Clientset.CoreV1().Pods(p.namespace).Create(ctx, p.pod, metav1.CreateOptions{}) if err != nil { return err } return p.suite.WaitForPodToBeRunning(ctx, waitTimeout, p.namespace, p.name) } func (p *pod) Exec(ctx context.Context, command string) (string, string, error) { cmd := []string{ "/bin/sh", "-c", command, } req := p.suite.Clientset.CoreV1().RESTClient().Post().Resource("pods").Name(p.name). Namespace(p.namespace).SubResource("exec") option := &corev1.PodExecOptions{ Command: cmd, Stdin: false, Stdout: true, Stderr: true, TTY: false, } req.VersionedParams( option, scheme.ParameterCodec, ) websocketExec, err := remotecommand.NewWebSocketExecutor(p.suite.RestConfig, "GET", req.URL().String()) if err != nil { return "", "", err } var stdout, stderr strings.Builder err = websocketExec.StreamWithContext(ctx, remotecommand.StreamOptions{ Stdout: &stdout, Stderr: &stderr, }) if err != nil && !p.quiet { p.suite.T().Logf( "error executing command in pod %s/%s: %v\n\ncommand %q stdout:\n%s\n\ncommand %q stderr:\n%s", p.namespace, p.name, err, command, stdout.String(), command, stderr.String(), ) } return stdout.String(), stderr.String(), err } func (p *pod) Delete(ctx context.Context) error { err := p.suite.Clientset.CoreV1().Pods(p.namespace).Delete(ctx, p.name, metav1.DeleteOptions{ GracePeriodSeconds: new(int64(0)), }) if err != nil && errors.IsNotFound(err) { return nil } return err } // NewPrivilegedPod creates a new pod definition with a random suffix // in the kube-system namespace with privileged security context. func (k8sSuite *K8sSuite) NewPrivilegedPod(name string) (podInfo, error) { randomSuffix := make([]byte, 4) if _, err := rand.Read(randomSuffix); err != nil { return nil, fmt.Errorf("failed to generate random suffix: %w", err) } podName := fmt.Sprintf("%s-%x", name, randomSuffix) return &pod{ name: podName, namespace: "kube-system", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: podName, Image: "alpine", Command: []string{ "/bin/sh", "-c", "--", }, Args: []string{ "trap : TERM INT; (tail -f /dev/null) & wait", }, SecurityContext: &corev1.SecurityContext{ Privileged: new(true), }, // lvm commands even though executed in the host mount namespace, still need access to /dev 🤷🏼, // otherwise lvcreate commands hangs on semop syscall VolumeMounts: []corev1.VolumeMount{ { Name: "dev", MountPath: "/dev", }, { Name: "host", MountPath: "/host", }, }, }, }, Volumes: []corev1.Volume{ { Name: "dev", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ Path: "/dev", }, }, }, { Name: "host", VolumeSource: corev1.VolumeSource{ HostPath: &corev1.HostPathVolumeSource{ Path: "/", }, }, }, }, HostNetwork: true, HostIPC: true, HostPID: true, }, }, suite: k8sSuite, }, nil } // NewPod creates a new pod definition with a random suffix // in the default namespace. func (k8sSuite *K8sSuite) NewPod(name string) (podInfo, error) { randomSuffix := make([]byte, 4) if _, err := rand.Read(randomSuffix); err != nil { return nil, fmt.Errorf("failed to generate random suffix: %w", err) } podName := fmt.Sprintf("%s-%x", name, randomSuffix) return &pod{ name: podName, namespace: "default", pod: &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: podName, Image: "alpine", Command: []string{ "/bin/sh", "-c", "--", }, Args: []string{ "trap : TERM INT; (tail -f /dev/null) & wait", }, }, }, }, }, suite: k8sSuite, }, nil } // WaitForPodToBeRunning waits for the pod with the given namespace and name to be running. func (k8sSuite *K8sSuite) WaitForPodToBeRunning(ctx context.Context, timeout time.Duration, namespace, podName string) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() watcher, err := k8sSuite.Clientset.CoreV1().Pods(namespace).Watch(ctx, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("metadata.name", podName).String(), }) if err != nil { return err } defer watcher.Stop() for { select { case <-ctx.Done(): return ctx.Err() case event := <-watcher.ResultChan(): if event.Type == watch.Error { return fmt.Errorf("error watching pod: %v", event.Object) } pod, ok := event.Object.(*corev1.Pod) if !ok { continue } if pod.Name == podName && pod.Status.Phase == corev1.PodRunning { return nil } } } } // WaitForDeploymentAvailable waits for the deployment with the given namespace and name to be running with the requested replicas. func (k8sSuite *K8sSuite) WaitForDeploymentAvailable(ctx context.Context, timeout time.Duration, namespace, deplName string, replicas int32) error { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() watcher, err := k8sSuite.Clientset.AppsV1().Deployments(namespace).Watch(ctx, metav1.ListOptions{ FieldSelector: fields.OneTermEqualSelector("metadata.name", deplName).String(), }) if err != nil { return err } defer watcher.Stop() for { select { case <-ctx.Done(): return ctx.Err() case event := <-watcher.ResultChan(): if event.Type == watch.Error { return fmt.Errorf("error watching deployment: %v", event.Object) } deployment, ok := event.Object.(*appsv1.Deployment) if !ok { continue } if deployment.Name == deplName && deployment.Status.AvailableReplicas == replicas { return nil } } } } // LogPodLogsByLabel logs the logs of the pod with the given namespace and label. func (k8sSuite *K8sSuite) LogPodLogsByLabel(ctx context.Context, namespace, label, value string) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() podList, err := k8sSuite.Clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", label, value), }) k8sSuite.Require().NoError(err) for _, pod := range podList.Items { k8sSuite.LogPodLogs(ctx, namespace, pod.Name) } } // LogPodLogs logs the logs of the pod with the given namespace and name. func (k8sSuite *K8sSuite) LogPodLogs(ctx context.Context, namespace, podName string) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() req := k8sSuite.Clientset.CoreV1().Pods(namespace).GetLogs(podName, &corev1.PodLogOptions{}) readCloser, err := req.Stream(ctx) if err != nil { k8sSuite.T().Logf("failed to get pod logs: %s", err) } defer readCloser.Close() //nolint:errcheck scanner := bufio.NewScanner(readCloser) for scanner.Scan() { k8sSuite.T().Logf("%s/%s: %s", namespace, podName, scanner.Text()) } } // HelmInstall installs the Helm chart with the given namespace, repository, version, release name, chart name and values. func (k8sSuite *K8sSuite) HelmInstall(ctx context.Context, namespace, repository, version, releaseName, chartName string, valuesBytes []byte) error { tempFile := filepath.Join(k8sSuite.T().TempDir(), "values.yaml") if err := os.WriteFile(tempFile, valuesBytes, 0o644); err != nil { return err } defer os.Remove(tempFile) //nolint:errcheck args := []string{ "upgrade", "--install", "--cleanup-on-fail", "--create-namespace", "--namespace", namespace, "--wait", "--timeout", k8sSuite.CSITestTimeout, "--repo", repository, "--version", version, "--values", tempFile, releaseName, chartName, } cmd := exec.CommandContext(k8sSuite.T().Context(), k8sSuite.HelmPath, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr k8sSuite.T().Logf("running helm command: %s", strings.Join(cmd.Args, " ")) return cmd.Run() } // WaitForResource waits for the resource with the given group, kind, version, namespace and jsonpath field selector to have the given expected value. // mostly a restructuring of `kubectl wait` from https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/wait/wait.go // //nolint:gocyclo func (k8sSuite *K8sSuite) WaitForResource(ctx context.Context, namespace, group, kind, version, resourceName, jsonPathSelector, expectedValue string) error { j := jsonpath.New("wait").AllowMissingKeys(true) if jsonPathSelector == "" { return fmt.Errorf("jsonpath condition is empty") } if err := j.Parse(jsonPathSelector); err != nil { return fmt.Errorf("error parsing jsonpath condition: %v", err) } mapping, err := k8sSuite.Mapper.RESTMapping(schema.GroupKind{ Group: group, Kind: kind, }, version) if err != nil { return fmt.Errorf("error creating mapping for resource %s/%s/%s", group, kind, version) } dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(namespace) fieldSelector := fields.OneTermEqualSelector("metadata.name", resourceName).String() lw := &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { options.FieldSelector = fieldSelector return dr.List(ctx, options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { options.FieldSelector = fieldSelector return dr.Watch(ctx, options) }, } preconditionFunc := func(store cache.Store) (bool, error) { var exists bool _, exists, err = store.Get(&metav1.ObjectMeta{Namespace: namespace, Name: resourceName}) if err != nil { return true, err } if !exists { return true, errors.NewNotFound(mapping.Resource.GroupResource(), resourceName) } return false, nil } if _, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, preconditionFunc, func(event watch.Event) (bool, error) { obj, ok := event.Object.(*unstructured.Unstructured) if !ok { return false, fmt.Errorf("error converting object to unstructured") } queryObj := obj.UnstructuredContent() k8sSuite.T().Logf("waiting for resource %s/%s/%s/%s to have field %s with value %s", group, version, kind, resourceName, jsonPathSelector, expectedValue) parseResults, err := j.FindResults(queryObj) if err != nil { return false, fmt.Errorf("error finding results: %v", err) } if len(parseResults) == 0 || len(parseResults[0]) == 0 { return false, nil } if len(parseResults) > 1 { return false, fmt.Errorf("given jsonpath expression matches more than one list") } if len(parseResults[0]) > 1 { return false, fmt.Errorf("given jsonpath expression matches more than one value") } switch parseResults[0][0].Interface().(type) { case map[string]any, []any: return false, fmt.Errorf("jsonpath leads to a nested object or list which is not supported") } s := fmt.Sprintf("%v", parseResults[0][0].Interface()) return strings.TrimSpace(s) == strings.TrimSpace(expectedValue), nil }); err != nil { return err } return nil } // WaitForResourceToBeAvailable waits for the resource with the given namespace, group, kind, version and name to be available. func (k8sSuite *K8sSuite) WaitForResourceToBeAvailable(ctx context.Context, duration time.Duration, namespace, group, kind, version, resourceName string) error { return retry.Constant(duration).Retry(func() error { mapping, err := k8sSuite.Mapper.RESTMapping(schema.GroupKind{ Group: group, Kind: kind, }, version) if err != nil { return fmt.Errorf("error creating mapping for resource %s/%s/%s", group, kind, version) } dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(namespace) _, err = dr.Get(ctx, resourceName, metav1.GetOptions{}) if errors.IsNotFound(err) { k8sSuite.T().Logf("resource %s/%s/%s/%s not found, retrying", group, version, kind, resourceName) return retry.ExpectedError(err) } return err }) } // GetUnstructuredResource gets the unstructured resource with the given namespace, group, kind, version and name. func (k8sSuite *K8sSuite) GetUnstructuredResource(ctx context.Context, namespace, group, kind, version, resourceName string) (*unstructured.Unstructured, error) { mapping, err := k8sSuite.Mapper.RESTMapping(schema.GroupKind{ Group: group, Kind: kind, }, version) if err != nil { return nil, fmt.Errorf("error creating mapping for resource %s/%s/%s", group, kind, version) } dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(namespace) result, err := dr.Get(ctx, resourceName, metav1.GetOptions{}) if err != nil { return nil, fmt.Errorf("error getting resource %s/%s/%s/%s: %v", group, version, kind, resourceName, err) } return result, nil } // RunFIOTest runs the FIO test with the given storage class and size using kubestr. func (k8sSuite *K8sSuite) RunFIOTest(ctx context.Context, storageClass, size string) error { args := []string{ "--outfile", fmt.Sprintf("/tmp/fio-%s.json", storageClass), "--output", "json", "fio", "--storageclass", storageClass, "--size", size, } cmd := exec.CommandContext(k8sSuite.T().Context(), k8sSuite.KubeStrPath, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr k8sSuite.T().Logf("running kubestr command: %s", strings.Join(cmd.Args, " ")) return cmd.Run() } // GetPodsWithLabel returns the pods with the given label in the specified namespace. func (k8sSuite *K8sSuite) GetPodsWithLabel(ctx context.Context, namespace, label string) (*corev1.PodList, error) { podList, err := k8sSuite.Clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ LabelSelector: label, }) if err != nil { return nil, err } return podList, nil } // ParseManifests parses YAML manifest bytes into unstructured objects. func (k8sSuite *K8sSuite) ParseManifests(manifests []byte) []unstructured.Unstructured { reader := yaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(manifests))) var parsedManifests []unstructured.Unstructured for { yamlManifest, err := reader.Read() if err != nil { if err == io.EOF { break } k8sSuite.Require().NoError(err) } yamlManifest = bytes.TrimSpace(yamlManifest) if len(yamlManifest) == 0 { continue } jsonManifest, err := yaml.ToJSON(yamlManifest) if err != nil { k8sSuite.Require().NoError(err, "error converting manifest to JSON") } if bytes.Equal(jsonManifest, []byte("null")) || bytes.Equal(jsonManifest, []byte("{}")) { // skip YAML docs which contain only comments continue } var obj unstructured.Unstructured if err = json.Unmarshal(jsonManifest, &obj); err != nil { k8sSuite.Require().NoError(err, "error loading JSON manifest into unstructured") } parsedManifests = append(parsedManifests, obj) } return parsedManifests } // ApplyManifests applies the given manifests to the Kubernetes cluster. func (k8sSuite *K8sSuite) ApplyManifests(ctx context.Context, manifests []unstructured.Unstructured) { for _, obj := range manifests { mapping, err := k8sSuite.Mapper.RESTMapping(obj.GetObjectKind().GroupVersionKind().GroupKind(), obj.GetObjectKind().GroupVersionKind().Version) if err != nil { k8sSuite.Require().NoError(err, "error creating mapping for object %s", obj.GetName()) } if obj.GetNamespace() == "" { k8sSuite.T().Logf("namespace not set for object %s, kind %s", obj.GetName(), obj.GetObjectKind().GroupVersionKind()) } dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(obj.GetNamespace()) _, err = dr.Apply(ctx, obj.GetName(), &obj, metav1.ApplyOptions{ FieldManager: "talos-e2e", }) k8sSuite.Require().NoError(err, "error creating object %s", obj.GetName()) k8sSuite.T().Logf("created object %s/%s/%s", obj.GetObjectKind().GroupVersionKind(), obj.GetNamespace(), obj.GetName()) } } // DeleteManifests deletes the given manifests from the Kubernetes cluster. func (k8sSuite *K8sSuite) DeleteManifests(ctx context.Context, manifests []unstructured.Unstructured) { // process in reverse orderd manifests = slices.Clone(manifests) slices.Reverse(manifests) for _, obj := range manifests { mapping, err := k8sSuite.Mapper.RESTMapping(obj.GetObjectKind().GroupVersionKind().GroupKind(), obj.GetObjectKind().GroupVersionKind().Version) if err != nil { k8sSuite.Require().NoError(err, "error creating mapping for object %s", obj.GetName()) } dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(obj.GetNamespace()) err = dr.Delete(ctx, obj.GetName(), metav1.DeleteOptions{}) if errors.IsNotFound(err) { continue } k8sSuite.Require().NoError(err, "error deleting object %s", obj.GetName()) // wait for the object to be deleted fieldSelector := fields.OneTermEqualSelector("metadata.name", obj.GetName()).String() lw := &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { options.FieldSelector = fieldSelector return dr.List(ctx, options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { options.FieldSelector = fieldSelector return dr.Watch(ctx, options) }, } preconditionFunc := func(store cache.Store) (bool, error) { var exists bool _, exists, err = store.Get(&metav1.ObjectMeta{Namespace: obj.GetNamespace(), Name: obj.GetName()}) if err != nil { return true, err } if !exists { // since we're looking for it to disappear we just return here if it no longer exists return true, nil } return false, nil } _, err = watchtools.UntilWithSync(ctx, lw, &unstructured.Unstructured{}, preconditionFunc, func(event watch.Event) (bool, error) { return event.Type == watch.Deleted, nil }) k8sSuite.Require().NoError(err, "error waiting for the object to be deleted %s/%s/%s", obj.GetObjectKind().GroupVersionKind(), obj.GetNamespace(), obj.GetName()) k8sSuite.T().Logf("deleted object %s/%s/%s", obj.GetObjectKind().GroupVersionKind(), obj.GetNamespace(), obj.GetName()) } } // PatchK8sObject patches the kubernetes object with the given namespace, group, kind, version and name. func (k8sSuite *K8sSuite) PatchK8sObject(ctx context.Context, namespace, group, kind, version, resourceName string, patchBytes []byte) { patchBytes, err := yaml.ToJSON(patchBytes) k8sSuite.Require().NoError(err, "error converting patch to JSON") mapping, err := k8sSuite.Mapper.RESTMapping(schema.GroupKind{ Group: group, Kind: kind, }, version) k8sSuite.Require().NoError(err, "error creating mapping for resource %s/%s/%s", group, kind, version) dr := k8sSuite.DynamicClient.Resource(mapping.Resource).Namespace(namespace) _, err = dr.Patch(ctx, resourceName, types.MergePatchType, patchBytes, metav1.PatchOptions{}) k8sSuite.Require().NoError(err, "error patching resource %s/%s/%s/%s", group, version, kind, resourceName) } // ToUnstructured converts the given runtime.Object to unstructured.Unstructured. func (k8sSuite *K8sSuite) ToUnstructured(obj runtime.Object) unstructured.Unstructured { unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) if err != nil { k8sSuite.Require().NoError(err, "error converting object to unstructured") } u := unstructured.Unstructured{Object: unstructuredObj} u.SetGroupVersionKind(obj.GetObjectKind().GroupVersionKind()) return u } // SetupNodeInformer sets up a node informer for the given node name. func (k8sSuite *K8sSuite) SetupNodeInformer(ctx context.Context, nodeName string) <-chan *corev1.Node { const metadataKeyName = "metadata.name=" watchCh := make(chan *corev1.Node) informerFactory := informers.NewSharedInformerFactoryWithOptions( k8sSuite.Clientset, constants.KubernetesInformerDefaultResyncPeriod, informers.WithTweakListOptions(func(options *metav1.ListOptions) { options.FieldSelector = metadataKeyName + nodeName }), ) nodeInformer := informerFactory.Core().V1().Nodes().Informer() _, err := nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj any) { node, ok := obj.(*corev1.Node) if !ok { return } channel.SendWithContext(ctx, watchCh, node) }, UpdateFunc: func(_, obj any) { node, ok := obj.(*corev1.Node) if !ok { return } channel.SendWithContext(ctx, watchCh, node) }, }) k8sSuite.Require().NoError(err) informerFactory.Start(ctx.Done()) k8sSuite.T().Cleanup(informerFactory.Shutdown) result := informerFactory.WaitForCacheSync(ctx.Done()) for k, v := range result { k8sSuite.Assert().True(v, "informer %q failed to sync", k.String()) } return watchCh } ================================================ FILE: internal/integration/base/run.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package base import ( "bytes" "errors" "io" "os" "os/exec" "regexp" "strings" "testing" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // RunOption configures options for Run. type RunOption func(*runOptions) // MatchFunc runs against output (stdout or stderr). type MatchFunc func(output string) error type runOptions struct { retryer retry.Retryer stdin io.Reader shouldFail bool stdoutEmpty bool stderrNotEmpty bool stdoutRegexps []*regexp.Regexp stdoutNegativeRegexps []*regexp.Regexp stderrRegexps []*regexp.Regexp stderrNegativeRegexps []*regexp.Regexp stdoutMatchers []MatchFunc stderrMatchers []MatchFunc } // WithRetry retries failing command runs. func WithRetry(retryer retry.Retryer) RunOption { return func(opts *runOptions) { opts.retryer = retryer } } // WithStdin sets stdin for the command. func WithStdin(r io.Reader) RunOption { return func(opts *runOptions) { opts.stdin = r } } // ShouldFail tells run command should fail (with non-empty stderr). // // ShouldFail also sets StdErrNotEmpty. func ShouldFail() RunOption { return func(opts *runOptions) { opts.shouldFail = true opts.stderrNotEmpty = true } } // StdoutEmpty tells run that stdout of the command should be empty. func StdoutEmpty() RunOption { return func(opts *runOptions) { opts.stdoutEmpty = true } } // StderrNotEmpty tells run that stderr of the command should not be empty. func StderrNotEmpty() RunOption { return func(opts *runOptions) { opts.stderrNotEmpty = true } } // StdoutShouldMatch appends to the set of regexps stdout contents should match. func StdoutShouldMatch(r *regexp.Regexp) RunOption { return func(opts *runOptions) { opts.stdoutRegexps = append(opts.stdoutRegexps, r) } } // StdoutShouldNotMatch appends to the set of regexps stdout contents should not match. func StdoutShouldNotMatch(r *regexp.Regexp) RunOption { return func(opts *runOptions) { opts.stdoutNegativeRegexps = append(opts.stdoutNegativeRegexps, r) } } // StderrShouldMatch appends to the set of regexps stderr contents should match. // // StderrShouldMatch also sets StdErrNotEmpty. func StderrShouldMatch(r *regexp.Regexp) RunOption { return func(opts *runOptions) { opts.stderrRegexps = append(opts.stderrRegexps, r) opts.stderrNotEmpty = true } } // StderrShouldNotMatch appends to the set of regexps stderr contents should not match. func StderrShouldNotMatch(r *regexp.Regexp) RunOption { return func(opts *runOptions) { opts.stderrNegativeRegexps = append(opts.stderrNegativeRegexps, r) } } // StdoutMatchFunc appends to the list of MatchFuncs to run against stdout. func StdoutMatchFunc(f MatchFunc) RunOption { return func(opts *runOptions) { opts.stdoutMatchers = append(opts.stdoutMatchers, f) } } // StderrMatchFunc appends to the list of MatchFuncs to run against stderr. func StderrMatchFunc(f MatchFunc) RunOption { return func(opts *runOptions) { opts.stderrMatchers = append(opts.stderrMatchers, f) } } // runAndWait launches the command and waits for completion. // // runAndWait doesn't do any assertions on result. func runAndWait(t *testing.T, cmd *exec.Cmd, stdin io.Reader) (stdoutBuf, stderrBuf *bytes.Buffer, err error) { var stdout, stderr bytes.Buffer cmd.Stdin = stdin cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Env = []string{} // filter environment variables for _, keyvalue := range os.Environ() { name, _, ok := strings.Cut(keyvalue, "=") if !ok { continue } switch strings.ToUpper(name) { case "PATH": fallthrough case "HOME": fallthrough case "USERNAME": cmd.Env = append(cmd.Env, keyvalue) } } t.Logf("Running %q", strings.Join(cmd.Args, " ")) require.NoError(t, cmd.Start()) err = cmd.Wait() return &stdout, &stderr, err } // retryRunAndWait retries runAndWait if the command fails to run. func retryRunAndWait(t *testing.T, cmdFunc func() *exec.Cmd, retryer retry.Retryer, stdin io.Reader) (stdoutBuf, stderrBuf *bytes.Buffer, err error) { err = retryer.Retry(func() error { stdoutBuf, stderrBuf, err = runAndWait(t, cmdFunc(), stdin) var exitError *exec.ExitError if errors.As(err, &exitError) { return retry.ExpectedErrorf("command failed, stderr %v: %w", stderrBuf.String(), err) } return err }) return stdoutBuf, stderrBuf, err } // run executes command, asserts on its exit status/output, and returns stdout. // //nolint:gocyclo,nakedret func run(t *testing.T, cmdFunc func() *exec.Cmd, options ...RunOption) (stdout, stderr string) { var opts runOptions for _, o := range options { o(&opts) } var ( stdoutBuf, stderrBuf *bytes.Buffer err error ) if opts.retryer != nil { stdoutBuf, stderrBuf, err = retryRunAndWait(t, cmdFunc, opts.retryer, opts.stdin) } else { stdoutBuf, stderrBuf, err = runAndWait(t, cmdFunc(), opts.stdin) } if err != nil { // check that command failed, not something else happened var exitError *exec.ExitError require.True(t, errors.As(err, &exitError), "%s", err) } if stdoutBuf != nil { stdout = stdoutBuf.String() } if stderrBuf != nil { stderr = stderrBuf.String() } if opts.shouldFail { assert.Error(t, err, "command expected to fail, but did not") } else { assert.NoError(t, err, "command failed, stdout: %q, stderr: %q", stdout, stderr) } if opts.stdoutEmpty { assert.Empty(t, stdout, "stdout should be empty") } else { assert.NotEmpty(t, stdout, "stdout should be not empty") } if opts.stderrNotEmpty { assert.NotEmpty(t, stderr, "stderr should be not empty") } else { assert.Empty(t, stderr, "stderr should be empty") } for _, rx := range opts.stdoutRegexps { assert.Regexp(t, rx, stdout) } for _, rx := range opts.stderrRegexps { assert.Regexp(t, rx, stderr) } for _, rx := range opts.stdoutNegativeRegexps { assert.NotRegexp(t, rx, stdout) } for _, rx := range opts.stderrNegativeRegexps { assert.NotRegexp(t, rx, stderr) } for _, f := range opts.stdoutMatchers { assert.NoError(t, f(stdout), "stdout match: %q", stdout) } for _, f := range opts.stderrMatchers { assert.NoError(t, f(stderr), "stderr match: %q", stderr) } return stdout, stderr } ================================================ FILE: internal/integration/cli/apply-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( _ "embed" "os" "path/filepath" "regexp" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/integration/base" ) // ApplyConfigSuite verifies dmesg command. type ApplyConfigSuite struct { base.CLISuite } // SuiteName ... func (suite *ApplyConfigSuite) SuiteName() string { return "cli.ApplyConfigSuite" } //go:embed testdata/patches/dummy-ap.yaml var dummyAPPatch []byte //go:embed testdata/patches/delete-dummy-ap.yaml var deleteDummyAPPatch []byte // TestApplyWithPatch verifies that . func (suite *ApplyConfigSuite) TestApplyWithPatch() { if suite.Cluster == nil { suite.T().Skip("skipping if cluster is not qemu/docker") } tmpDir := suite.T().TempDir() node := suite.RandomDiscoveredNodeInternalIP() data, _ := suite.RunCLI([]string{"get", "--nodes", node, "mc", "v1alpha1", "-o", "jsonpath={.spec}"}) configPath := filepath.Join(tmpDir, "config.yaml") suite.Require().NoError(os.WriteFile(configPath, []byte(data), 0o777)) patchPath := filepath.Join(tmpDir, "patch.yaml") suite.Require().NoError(os.WriteFile(patchPath, dummyAPPatch, 0o777)) suite.RunCLI([]string{"apply-config", "--nodes", node, "--config-patch", patchPath, "-f", configPath}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrShouldMatch(regexp.MustCompile("Applied configuration without a reboot")), ) // sleep a bit to let the config propagate time.Sleep(1 * time.Second) suite.RunCLI([]string{"get", "--nodes", node, "links"}, base.StdoutShouldMatch(regexp.MustCompile("dummy-ap-patch")), ) // now delete the dummy-ap-patch data, _ = suite.RunCLI([]string{"get", "--nodes", node, "mc", "v1alpha1", "-o", "jsonpath={.spec}"}) suite.Require().NoError(os.WriteFile(configPath, []byte(data), 0o777)) suite.Require().NoError(os.WriteFile(patchPath, deleteDummyAPPatch, 0o777)) suite.RunCLI([]string{"apply-config", "--nodes", node, "--config-patch", patchPath, "-f", configPath}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrShouldMatch(regexp.MustCompile("Applied configuration without a reboot")), ) // sleep a bit to let the config propagate time.Sleep(1 * time.Second) suite.RunCLI([]string{"get", "--nodes", node, "links"}, base.StdoutShouldNotMatch(regexp.MustCompile("dummy-ap-patch")), base.WithRetry(retry.Constant(15*time.Second, retry.WithUnits(time.Second))), ) } func init() { allSuites = append(allSuites, new(ApplyConfigSuite)) } ================================================ FILE: internal/integration/cli/cgroups.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" ) // CgroupsSuite verifies dmesg command. type CgroupsSuite struct { base.CLISuite } // SuiteName ... func (suite *CgroupsSuite) SuiteName() string { return "cli.CgroupsSuite" } // TestPresets verifies successful execution. func (suite *CgroupsSuite) TestPresets() { for _, preset := range []string{"cpu", "cpuset", "io", "memory", "process", "swap"} { suite.Run(preset, func() { suite.RunCLI( []string{ "cgroups", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "--preset", preset, }, base.StdoutShouldMatch(regexp.MustCompile(`apid`))) }) } } func init() { allSuites = append(allSuites, new(CgroupsSuite)) } ================================================ FILE: internal/integration/cli/cli.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package cli provides CLI (talosctl) integration tests for Talos package cli import "github.com/stretchr/testify/suite" var allSuites []suite.TestingSuite // GetAllSuites returns all the suites for CLI test. // // Depending on build tags, this might return different lists. func GetAllSuites() []suite.TestingSuite { return allSuites } ================================================ FILE: internal/integration/cli/completion.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "github.com/siderolabs/talos/internal/integration/base" ) // CompletionSuite verifies dmesg command. type CompletionSuite struct { base.CLISuite } // SuiteName ... func (suite *CompletionSuite) SuiteName() string { return "cli.CompletionSuite" } // TestSuccess runs comand with success. func (suite *CompletionSuite) TestSuccess() { suite.RunCLI([]string{"completion", "bash"}) suite.RunCLI([]string{"completion", "fish"}) suite.RunCLI([]string{"completion", "zsh"}) } func init() { allSuites = append(allSuites, new(CompletionSuite)) } ================================================ FILE: internal/integration/cli/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "encoding/json" "path/filepath" "regexp" "strings" "go.yaml.in/yaml/v4" "google.golang.org/protobuf/encoding/protojson" "github.com/siderolabs/talos/internal/integration/base" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // TalosconfigSuite checks `talosctl config`. type TalosconfigSuite struct { base.CLISuite } // SuiteName implements base.NamedSuite. func (suite *TalosconfigSuite) SuiteName() string { return "cli.TalosconfigSuite" } // TestList checks `talosctl config contexts`. func (suite *TalosconfigSuite) TestList() { suite.RunCLI([]string{"config", "contexts"}, base.StdoutShouldMatch(regexp.MustCompile(`CURRENT`))) } // TestInfo checks `talosctl config info`. func (suite *TalosconfigSuite) TestInfo() { suite.RunCLI([]string{"config", "info"}, // TODO: remove 10 years once the CABPT & TF providers are updated to 1.5.2+ base.StdoutShouldMatch(regexp.MustCompile(`(1 year|10 years) from now`))) suite.RunCLI([]string{"config", "info", "--output", "text"}, // TODO: remove 10 years once the CABPT & TF providers are updated to 1.5.2+ base.StdoutShouldMatch(regexp.MustCompile(`(1 year|10 years) from now`))) suite.RunCLI([]string{"config", "info", "--output", "yaml"}, base.StdoutMatchFunc(func(stdout string) error { var out map[string]any err := yaml.Unmarshal([]byte(stdout), &out) if err != nil { return err } suite.Assert().Contains(out, "endpoints") suite.Assert().Contains(out, "nodes") suite.Assert().Contains(out, "roles") suite.Assert().Contains(out, "context") return nil }), ) suite.RunCLI([]string{"config", "info", "--output", "json"}, base.StdoutMatchFunc(func(stdout string) error { var out map[string]any err := json.Unmarshal([]byte(stdout), &out) if err != nil { return err } suite.Assert().Contains(out, "endpoints") suite.Assert().Contains(out, "nodes") suite.Assert().Contains(out, "roles") suite.Assert().Contains(out, "context") return nil }), ) suite.RunCLI([]string{"config", "info", "--output", "table"}, base.StdoutEmpty(), base.ShouldFail(), base.StderrShouldMatch(regexp.MustCompile(`unknown output format: "table"`)), ) } // TestMerge checks `talosctl config merge`. func (suite *TalosconfigSuite) TestMerge() { tempDir := suite.T().TempDir() suite.RunCLI([]string{"gen", "config", "-o", tempDir, "foo", "https://192.168.0.1:6443"}, base.StdoutEmpty(), base.StderrNotEmpty(), ) talosconfigPath := filepath.Join(tempDir, "talosconfig") suite.Assert().FileExists(talosconfigPath) path := filepath.Join(tempDir, "merged") suite.RunCLI([]string{"config", "merge", "--talosconfig", path, talosconfigPath}, base.StdoutEmpty()) suite.Require().FileExists(path) c, err := clientconfig.Open(path) suite.Require().NoError(err) suite.Require().NotNil(c.Contexts["foo"]) suite.RunCLI([]string{"config", "merge", "--talosconfig", path, talosconfigPath}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrShouldMatch(regexp.MustCompile(`renamed`)), ) c, err = clientconfig.Open(path) suite.Require().NoError(err) suite.Require().NotNil(c.Contexts["foo-1"]) } // TestNewTTL checks `talosctl config new --crt-ttl`. func (suite *TalosconfigSuite) TestNewTTL() { tempDir := suite.T().TempDir() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) talosconfig := filepath.Join(tempDir, "talosconfig") suite.RunCLI([]string{"--nodes", node, "config", "new", "--roles", "os:reader", talosconfig, "--crt-ttl", "17520h"}, base.StdoutEmpty()) suite.RunCLI([]string{"config", "info", "--talosconfig", talosconfig}, base.StdoutShouldMatch(regexp.MustCompile(`2 years from now`))) } // TestNew checks `talosctl config new`. func (suite *TalosconfigSuite) TestNew() { stdout, _ := suite.RunCLI([]string{"version", "--json", "--nodes", suite.RandomDiscoveredNodeInternalIP()}) var v machineapi.Version err := protojson.Unmarshal([]byte(stdout), &v) suite.Require().NoError(err) rbacEnabled := v.Features.GetRbac() tempDir := suite.T().TempDir() node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) readerConfig := filepath.Join(tempDir, "readerconfig") suite.RunCLI([]string{"--nodes", node, "config", "new", "--roles", "os:reader", readerConfig}, base.StdoutEmpty()) operatorConfig := filepath.Join(tempDir, "operatorconfig") suite.RunCLI([]string{"--nodes", node, "config", "new", "--roles", "os:operator", operatorConfig}, base.StdoutEmpty()) // commands that work for admin, operator and reader, with and without RBAC for _, tt := range []struct { args []string opts []base.RunOption }{ { args: []string{"ls", "/etc/hosts"}, opts: []base.RunOption{base.StdoutShouldMatch(regexp.MustCompile(`hosts`))}, }, } { name := strings.Join(tt.args, "_") suite.Run(name, func() { for _, config := range []string{readerConfig, operatorConfig} { args := append([]string{"--nodes", node}, tt.args...) suite.RunCLI(args, tt.opts...) args = append([]string{"--talosconfig", config}, args...) suite.RunCLI(args, tt.opts...) } }) } // commands that work for admin, but not for reader&operator (when RBAC is enabled) for _, tt := range []struct { args []string adminOpts []base.RunOption nonprivOpts []base.RunOption }{ { args: []string{"read", "/etc/hosts"}, adminOpts: []base.RunOption{base.StdoutShouldMatch(regexp.MustCompile(`localhost`))}, nonprivOpts: []base.RunOption{ base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), base.ShouldFail(), }, }, { args: []string{"get", "mc"}, adminOpts: []base.RunOption{base.StdoutShouldMatch(regexp.MustCompile(`MachineConfig`))}, nonprivOpts: []base.RunOption{ base.ShouldFail(), base.StdoutShouldMatch(regexp.MustCompile(`\QNODE NAMESPACE TYPE ID VERSION`)), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), }, }, { args: []string{"get", "osrootsecret"}, adminOpts: []base.RunOption{base.StdoutShouldMatch(regexp.MustCompile(`OSRootSecret`))}, nonprivOpts: []base.RunOption{ base.ShouldFail(), base.StdoutShouldMatch(regexp.MustCompile(`\QNODE NAMESPACE TYPE ID VERSION`)), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), }, }, { args: []string{"kubeconfig", "--force", tempDir}, adminOpts: []base.RunOption{base.StdoutEmpty()}, nonprivOpts: []base.RunOption{ base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), }, }, } { name := strings.Join(tt.args, "_") suite.Run(name, func() { args := append([]string{"--nodes", node}, tt.args...) suite.RunCLI(args, tt.adminOpts...) for _, config := range []string{readerConfig, operatorConfig} { if rbacEnabled { suite.RunCLI(append([]string{"--talosconfig", config}, args...), tt.nonprivOpts...) } else { // check that it works the same way as for admin with reader's config suite.RunCLI(append([]string{"--talosconfig", config}, args...), tt.adminOpts...) } } }) } // commands which work for operator, but not reader (when RBAC is enabled) for _, tt := range []struct { args []string privOpts []base.RunOption nonprivOpts []base.RunOption }{ { args: []string{"etcd", "alarm", "disarm"}, privOpts: []base.RunOption{ base.StdoutEmpty(), }, nonprivOpts: []base.RunOption{ base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), base.ShouldFail(), }, }, } { name := strings.Join(tt.args, "_") suite.Run(name, func() { args := append([]string{"--nodes", node}, tt.args...) suite.RunCLI(args, tt.privOpts...) suite.RunCLI(append([]string{"--talosconfig", operatorConfig}, args...), tt.privOpts...) if rbacEnabled { suite.RunCLI(append([]string{"--talosconfig", readerConfig}, args...), tt.nonprivOpts...) } else { // check that it works the same way as for admin with reader's config suite.RunCLI(append([]string{"--talosconfig", readerConfig}, args...), tt.privOpts...) } }) } // do not test destructive command with disabled RBAC if !rbacEnabled { return } // destructive commands that don't work for reader // (and that we don't test for admin because they are destructive) for _, tt := range []struct { args []string readerOpts []base.RunOption }{ { args: []string{"reboot", "--wait=false"}, readerOpts: []base.RunOption{ base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), }, }, { args: []string{"reset", "--wait=false"}, readerOpts: []base.RunOption{ base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qrpc error: code = PermissionDenied desc = not authorized`)), }, }, } { name := strings.Join(tt.args, "_") suite.Run(name, func() { args := append([]string{"--nodes", node, "--talosconfig", readerConfig}, tt.args...) suite.RunCLI(args, tt.readerOpts...) }) } } func init() { allSuites = append(allSuites, new(TalosconfigSuite)) } ================================================ FILE: internal/integration/cli/containers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // ContainersSuite verifies dmesg command. type ContainersSuite struct { base.CLISuite } // SuiteName ... func (suite *ContainersSuite) SuiteName() string { return "cli.ContainersSuite" } // TestContainerd inspects containers via containerd driver. func (suite *ContainersSuite) TestContainerd() { suite.RunCLI([]string{"containers", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`IMAGE`)), base.StdoutShouldMatch(regexp.MustCompile(`apid`)), ) } // TestCRI inspects containers via CRI driver. func (suite *ContainersSuite) TestCRI() { suite.RunCLI([]string{ "containers", "-k", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), }, base.StdoutShouldMatch(regexp.MustCompile(`kube-system/kube-apiserver`)), ) } func init() { allSuites = append(allSuites, new(ContainersSuite)) } ================================================ FILE: internal/integration/cli/copy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "os" "path/filepath" "regexp" "github.com/siderolabs/talos/internal/integration/base" ) // CopySuite verifies dmesg command. type CopySuite struct { base.CLISuite } // SuiteName ... func (suite *CopySuite) SuiteName() string { return "cli.CopySuite" } // TestSuccess runs comand with success. func (suite *CopySuite) TestSuccess() { tempDir := suite.T().TempDir() suite.RunCLI([]string{"copy", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "/etc/os-release", tempDir}, base.StdoutEmpty()) _, err := os.Stat(filepath.Join(tempDir, "os-release")) suite.Require().NoError(err) } // TestMultiNodeFail verifies that command fails with multiple nodes. func (suite *CopySuite) TestMultiNodeFail() { suite.RunCLI([]string{"copy", "--nodes", "127.0.0.1", "--nodes", "127.0.0.1", "/etc/os-release", "."}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`is not supported with multiple nodes`))) } func init() { allSuites = append(allSuites, new(CopySuite)) } ================================================ FILE: internal/integration/cli/debug.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "strings" "github.com/siderolabs/talos/internal/integration/base" ) // DebugSuite verifies debug command. type DebugSuite struct { base.CLISuite } const debugImage = "docker.io/library/alpine:3.23" // SuiteName ... func (suite *DebugSuite) SuiteName() string { return "cli.DebugSuite" } // TestSuccess runs comand with success. func (suite *DebugSuite) TestSuccess() { suite.RunCLI([]string{"debug", debugImage, "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile("Linux")), base.StderrNotEmpty(), base.WithStdin(strings.NewReader("uname\n")), ) } func init() { allSuites = append(allSuites, new(DebugSuite)) } ================================================ FILE: internal/integration/cli/diskusage.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "errors" "fmt" "strconv" "strings" "github.com/siderolabs/talos/internal/integration/base" ) // DiskUsageSuite verifies dmesg command. type DiskUsageSuite struct { base.CLISuite } // SuiteName ... func (suite *DiskUsageSuite) SuiteName() string { return "cli.DiskUsageSuite" } type duInfo struct { size int64 name string node string } func splitLine(line string) []string { var columns []string for part := range strings.SplitSeq(line, " ") { if part != "" { columns = append(columns, strings.TrimSpace(part)) } } return columns } func parseLine(line string) (*duInfo, error) { columns := splitLine(line) if len(columns) < 2 || len(columns) > 3 { return nil, fmt.Errorf("failed to parse line %s", line) } res := &duInfo{} offset := 0 if len(columns) == 3 { res.node = columns[0] offset++ } size, err := strconv.ParseInt(columns[offset], 10, 64) if err != nil { return nil, err } res.size = size res.name = columns[offset+1] return res, nil } // TestSuccess runs comand with success. func (suite *DiskUsageSuite) TestSuccess() { folder := "/etc" node := suite.RandomDiscoveredNodeInternalIP() var folderSize int64 = 4096 suite.RunCLI([]string{"list", "--nodes", node, folder, "-l"}, base.StdoutMatchFunc(func(stdout string) error { lines := strings.Split(strings.TrimSpace(stdout), "\n") if len(lines) == 1 { return errors.New("expected lines > 0") } parts := splitLine(lines[1]) var err error folderSize, err = strconv.ParseInt(parts[4], 10, 64) if err != nil { return err } return nil })) // check total calculation suite.RunCLI([]string{"usage", "--nodes", node, folder, "-d1", "--all"}, base.StdoutMatchFunc(func(stdout string) error { lines := strings.Split(strings.TrimSpace(stdout), "\n") if len(lines) == 1 { return errors.New("expected lines > 0") } var totalExpected int64 for _, line := range lines[1 : len(lines)-1] { info, err := parseLine(line) if err != nil { return err } totalExpected += info.size } // add folder size totalExpected += folderSize info, err := parseLine(lines[len(lines)-1]) if err != nil { return err } if info.size != totalExpected { return fmt.Errorf("folder size was calculated incorrectly. Expected %d, got %d", totalExpected, info.size) } return nil })) } // TestError runs comand with error. func (suite *DiskUsageSuite) TestError() { suite.RunCLI([]string{ "usage", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "/no/such/folder/here/just/for/sure", }, base.ShouldFail(), base.StderrNotEmpty(), base.StdoutEmpty(), ) } func init() { allSuites = append(allSuites, new(DiskUsageSuite)) } ================================================ FILE: internal/integration/cli/dmesg.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "context" "fmt" "regexp" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/integration/base" ) // DmesgSuite verifies dmesg command. type DmesgSuite struct { base.CLISuite } // SuiteName ... func (suite *DmesgSuite) SuiteName() string { return "cli.DmesgSuite" } // TestHasOutput verifies that dmesg is displayed. func (suite *DmesgSuite) TestHasOutput() { suite.RunCLI([]string{"dmesg", "--nodes", suite.RandomDiscoveredNodeInternalIP()}) // default checks for stdout not empty } // TestClusterHasOutput verifies that each node in the cluster has some output. func (suite *DmesgSuite) TestClusterHasOutput() { nodes := suite.DiscoverNodeInternalIPs(context.TODO()) suite.Require().NotEmpty(nodes) matchers := xslices.Map(nodes, func(node string) base.RunOption { return base.StdoutShouldMatch( regexp.MustCompile(fmt.Sprintf(`(?m)^%s:`, regexp.QuoteMeta(node))), ) }) suite.RunCLI([]string{"--nodes", strings.Join(nodes, ","), "dmesg"}, matchers...) } func init() { allSuites = append(allSuites, new(DmesgSuite)) } ================================================ FILE: internal/integration/cli/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "context" "path/filepath" "regexp" "strings" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // EtcdSuite verifies etcd command. type EtcdSuite struct { base.CLISuite } // SuiteName ... func (suite *EtcdSuite) SuiteName() string { return "cli.EtcdSuite" } // TestMembers etcd members should have some output. func (suite *EtcdSuite) TestMembers() { suite.RunCLI([]string{"etcd", "members", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}) // default checks for stdout not empty } // TestStatus etcd status should have some output. func (suite *EtcdSuite) TestStatus() { cpNodes := suite.DiscoverNodeInternalIPsByType(context.TODO(), machine.TypeControlPlane) suite.RunCLI([]string{"etcd", "status", "--nodes", strings.Join(cpNodes, ",")}) // default checks for stdout not empty } // TestAlarm etcd alarm should have no output. func (suite *EtcdSuite) TestAlarm() { cpNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"etcd", "alarm", "list", "--nodes", cpNode}, base.StdoutEmpty()) suite.RunCLI([]string{"etcd", "alarm", "disarm", "--nodes", cpNode}, base.StdoutEmpty()) } // TestForfeitLeadership etcd forfeit-leadership check. func (suite *EtcdSuite) TestForfeitLeadership() { nodes := suite.DiscoverNodes(context.TODO()).NodesByType(machine.TypeControlPlane) if len(nodes) < 3 { suite.T().Skip("test only can be run on HA etcd clusters") } suite.RunCLI([]string{"etcd", "forfeit-leadership", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}, base.StdoutEmpty(), ) } // TestSnapshot tests etcd snapshot (backup). func (suite *EtcdSuite) TestSnapshot() { tempDir := suite.T().TempDir() dbPath := filepath.Join(tempDir, "snapshot.db") suite.RunCLI([]string{"etcd", "snapshot", dbPath, "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}, base.StdoutShouldMatch(regexp.MustCompile(`etcd snapshot saved to .+\d+ bytes.+`)), ) } // TestDowngrade tests etcd downgrade. func (suite *EtcdSuite) TestDowngrade() { downgradeTo := "3.5" // FIXME: enable tests once the tests run on on ETCD >=3.6.0 suite.T().Skip("ETCD below 3.6.0") node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"etcd", "downgrade", "validate", "--nodes", node, downgradeTo}, base.StdoutShouldMatch(regexp.MustCompile(`downgrade validate success, cluster version \d+\.\d+`)), ) suite.RunCLI([]string{"etcd", "downgrade", "enable", "--nodes", node, downgradeTo}, base.StdoutShouldMatch(regexp.MustCompile(`downgrade enable success, cluster version \d+\.\d+`)), ) suite.RunCLI([]string{"etcd", "downgrade", "cancel", "--nodes", node}, base.StdoutShouldMatch(regexp.MustCompile(`downgrade cancel success, cluster version \d+\.\d+`)), ) } func init() { allSuites = append(allSuites, new(EtcdSuite)) } ================================================ FILE: internal/integration/cli/gen.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "fmt" "os" "path/filepath" "regexp" "strings" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" ) // GenSuite verifies gen command. type GenSuite struct { base.CLISuite tmpDir string savedCwd string } // SuiteName ... func (suite *GenSuite) SuiteName() string { return "cli.GenSuite" } // SetupTest ... func (suite *GenSuite) SetupTest() { suite.tmpDir = suite.T().TempDir() var err error suite.savedCwd, err = os.Getwd() suite.Require().NoError(err) suite.Require().NoError(os.Chdir(suite.tmpDir)) } // TearDownTest ... func (suite *GenSuite) TearDownTest() { if suite.savedCwd != "" { suite.Require().NoError(os.Chdir(suite.savedCwd)) } } // TestCA ... func (suite *GenSuite) TestCA() { suite.RunCLI([]string{"gen", "ca", "--organization", "Foo"}, base.StdoutEmpty()) suite.Assert().FileExists("Foo.crt") suite.Assert().FileExists("Foo.sha256") suite.Assert().FileExists("Foo.key") } // TestKey ... func (suite *GenSuite) TestKey() { suite.RunCLI([]string{"gen", "key", "--name", "Foo"}, base.StdoutEmpty()) suite.Assert().FileExists("Foo.key") } // TestCSR ... func (suite *GenSuite) TestCSR() { suite.RunCLI([]string{"gen", "key", "--name", "Foo"}, base.StdoutEmpty()) suite.RunCLI([]string{"gen", "csr", "--key", "Foo.key", "--ip", "10.0.0.1"}, base.StdoutEmpty()) suite.Assert().FileExists("Foo.csr") } // TestCrt ... func (suite *GenSuite) TestCrt() { suite.RunCLI([]string{"gen", "ca", "--organization", "Foo"}, base.StdoutEmpty()) suite.RunCLI([]string{"gen", "key", "--name", "Bar"}, base.StdoutEmpty()) suite.RunCLI([]string{"gen", "csr", "--key", "Bar.key", "--ip", "10.0.0.1"}, base.StdoutEmpty()) suite.RunCLI([]string{"gen", "crt", "--ca", "Foo", "--csr", "Bar.csr", "--name", "foobar"}, base.StdoutEmpty()) suite.Assert().FileExists("foobar.crt") } // TestKeypair ... func (suite *GenSuite) TestKeypair() { suite.RunCLI([]string{"gen", "keypair", "--organization", "Foo", "--ip", "10.0.0.1"}, base.StdoutEmpty()) suite.Assert().FileExists("Foo.crt") suite.Assert().FileExists("Foo.key") } // TestGenConfigURLValidation ... func (suite *GenSuite) TestGenConfigURLValidation() { suite.RunCLI([]string{"gen", "config", "foo", "192.168.0.1"}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qtry: "https://192.168.0.1:6443"`))) suite.RunCLI([]string{"gen", "config", "foo", "192.168.0.1:6443"}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qtry: "https://192.168.0.1:6443"`))) suite.RunCLI([]string{"gen", "config", "foo", "192.168.0.1:2000"}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qtry: "https://192.168.0.1:2000"`))) suite.RunCLI([]string{"gen", "config", "foo", "http://192.168.0.1:2000"}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`\Qtry: "https://192.168.0.1:2000"`))) } // TestGenConfigPatchStrategic verifies that gen config --config-patch works with strategic merge patches. func (suite *GenSuite) TestGenConfigPatchStrategic() { patch, err := yaml.Marshal(map[string]any{ "cluster": map[string]any{ "clusterName": "bar", }, }) suite.Require().NoError(err) for _, tt := range []struct { flag string shouldAffect map[string]bool }{ { flag: "config-patch", shouldAffect: map[string]bool{ "controlplane.yaml": true, "worker.yaml": true, }, }, { flag: "config-patch-control-plane", shouldAffect: map[string]bool{ "controlplane.yaml": true, }, }, { flag: "config-patch-worker", shouldAffect: map[string]bool{ "worker.yaml": true, }, }, } { suite.Run(tt.flag, func() { suite.RunCLI([]string{"gen", "config", "--force", "foo", "https://192.168.0.1:6443", "--" + tt.flag, string(patch)}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrShouldMatch(regexp.MustCompile("generating PKI and tokens"))) for _, configName := range []string{"controlplane.yaml", "worker.yaml"} { cfg, err := configloader.NewFromFile(configName) suite.Require().NoError(err) switch { case tt.shouldAffect[configName]: suite.Assert().Equal("bar", cfg.Cluster().Name(), "checking %q", configName) default: suite.Assert().Equal("foo", cfg.Cluster().Name(), "checking %q", configName) } } }) } } // TestSecrets ... func (suite *GenSuite) TestSecrets() { suite.RunCLI([]string{"gen", "secrets"}, base.StdoutEmpty()) suite.Assert().FileExists("secrets.yaml") suite.RunCLI([]string{"gen", "secrets"}, base.StdoutEmpty(), base.ShouldFail(), base.StderrMatchFunc(func(output string) error { expected := "file \"secrets.yaml\" already exists, use --force to overwrite" if !strings.Contains(output, expected) { return fmt.Errorf("stdout does not contain %q: %q", expected, output) } return nil })) suite.RunCLI([]string{"gen", "secrets", "--force"}, base.StdoutEmpty()) suite.RunCLI([]string{"gen", "secrets", "--output-file", "secrets2.yaml"}, base.StdoutEmpty()) suite.Assert().FileExists("secrets2.yaml") suite.RunCLI([]string{"gen", "secrets", "-o", "secrets3.yaml", "--talos-version", "v0.8"}, base.StdoutEmpty()) suite.Assert().FileExists("secrets3.yaml") } // TestSecretsWithPKIDirAndToken ... func (suite *GenSuite) TestSecretsWithPKIDirAndToken() { path := "secrets-with-pki-dir-and-token.yaml" suite.Require().NoError(writeKubernetesPKIFiles("k8s-pki/")) suite.RunCLI([]string{ "gen", "secrets", "--from-kubernetes-pki", "k8s-pki/", "--kubernetes-bootstrap-token", "test-token", "--output-file", path, }, base.StdoutEmpty()) suite.Assert().FileExists(path) secretsYaml, err := os.ReadFile(path) suite.Assert().NoError(err) var secretsBundle secrets.Bundle err = yaml.Unmarshal(secretsYaml, &secretsBundle) suite.Assert().NoError(err) suite.Assert().Equal("test-token", secretsBundle.Secrets.BootstrapToken, "bootstrap token does not match") suite.Assert().Equal(pkiCACrt, secretsBundle.Certs.K8s.Crt, "k8s ca cert does not match") suite.Assert().Equal(pkiCAKey, secretsBundle.Certs.K8s.Key, "k8s ca key does not match") suite.Assert().Equal(pkiFrontProxyCACrt, secretsBundle.Certs.K8sAggregator.Crt, "k8s aggregator ca cert does not match") suite.Assert().Equal(pkiFrontProxyCAKey, secretsBundle.Certs.K8sAggregator.Key, "k8s aggregator ca key does not match") suite.Assert().Equal(pkiSAKey, secretsBundle.Certs.K8sServiceAccount.Key, "k8s service account key does not match") suite.Assert().Equal(pkiEtcdCACrt, secretsBundle.Certs.Etcd.Crt, "etcd ca cert does not match") suite.Assert().Equal(pkiEtcdCAKey, secretsBundle.Certs.Etcd.Key, "etcd ca key does not match") } // TestConfigWithSecrets tests the gen config command with secrets provided. func (suite *GenSuite) TestConfigWithSecrets() { suite.RunCLI([]string{"gen", "secrets"}, base.StdoutEmpty()) suite.Assert().FileExists("secrets.yaml") secretsYaml, err := os.ReadFile("secrets.yaml") suite.Assert().NoError(err) suite.RunCLI([]string{"gen", "config", "foo", "https://192.168.0.1:6443", "--with-secrets", "secrets.yaml"}, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.RunCLI([]string{"gen", "secrets", "--from-controlplane-config", "controlplane.yaml", "--output-file", "secrets-from-config.yaml"}, base.StdoutEmpty(), ) configSecretsBundle, err := os.ReadFile("secrets-from-config.yaml") suite.Assert().NoError(err) suite.Assert().YAMLEq(string(secretsYaml), string(configSecretsBundle)) } // TestGenConfigWithDeprecatedOutputDirFlag tests that gen config command still works with the deprecated --output-dir flag. func (suite *GenSuite) TestGenConfigWithDeprecatedOutputDirFlag() { tempDir := suite.T().TempDir() suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-dir", tempDir, }, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.Assert().FileExists(filepath.Join(tempDir, "controlplane.yaml")) suite.Assert().FileExists(filepath.Join(tempDir, "worker.yaml")) suite.Assert().FileExists(filepath.Join(tempDir, "talosconfig")) } // TestGenConfigToStdoutControlPlane tests that the gen config command can output a control plane config to stdout. func (suite *GenSuite) TestGenConfigToStdoutControlPlane() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane", "--output", "-", }, base.StderrNotEmpty(), base.StdoutMatchFunc(func(output string) error { expected := "type: controlplane" if !strings.Contains(output, expected) { return fmt.Errorf("stdout does not contain %q: %q", expected, output) } return nil })) } // TestGenConfigToStdoutWorker tests that the gen config command can output a worker config to stdout. func (suite *GenSuite) TestGenConfigToStdoutWorker() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "worker", "--output", "-", }, base.StderrNotEmpty(), base.StdoutMatchFunc(func(output string) error { expected := "type: worker" if !strings.Contains(output, expected) { return fmt.Errorf("stdout does not contain %q: %q", expected, output) } return nil })) } // TestGenConfigToStdoutTalosconfig tests that the gen config command can output a talosconfig to stdout. func (suite *GenSuite) TestGenConfigToStdoutTalosconfig() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "talosconfig", "--output", "-", }, base.StderrNotEmpty(), base.StdoutMatchFunc(func(output string) error { expected := "context: foo" if !strings.Contains(output, expected) { return fmt.Errorf("stdout does not contain %q: %q", expected, output) } return nil })) } // TestGenConfigToStdoutMultipleTypesError tests that the gen config command fails when // multiple output types are specified and output target is stdout. func (suite *GenSuite) TestGenConfigToStdoutMultipleTypesError() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane,worker", "--output", "-", }, base.StdoutEmpty(), base.ShouldFail(), base.StderrMatchFunc(func(output string) error { expected := "can't use multiple output types with stdout" if !strings.Contains(output, expected) { return fmt.Errorf("stderr does not contain %q: %q", expected, output) } return nil })) } // TestGenConfigMultipleTypesToDirectory tests that the gen config command works as expected // when some output types are specified and output target is a directory. func (suite *GenSuite) TestGenConfigMultipleTypesToDirectory() { tempDir := filepath.Join(suite.T().TempDir(), "inner") suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane,worker", "--output", tempDir, }, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.Assert().FileExists(filepath.Join(tempDir, "controlplane.yaml")) suite.Assert().FileExists(filepath.Join(tempDir, "worker.yaml")) suite.Assert().NoFileExists(filepath.Join(tempDir, "talosconfig")) } // TestGenConfigSingleTypeToFile tests that the gen config command treats // the output flag as a file path and not as a directory when a single output type is requested. func (suite *GenSuite) TestGenConfigSingleTypeToFile() { tempFile := filepath.Join(suite.T().TempDir(), "worker-conf.yaml") suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "worker", "--output", tempFile, }, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.Assert().FileExists(tempFile) } // TestGenConfigSingleTypeWithDeprecatedOutputDirFlagToDirectory tests that the gen config command treats // the output flag still as a directory when the deprecated --output-dir flag is used. func (suite *GenSuite) TestGenConfigSingleTypeWithDeprecatedOutputDirFlagToDirectory() { tempDir := filepath.Join(suite.T().TempDir(), "inner") suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "worker", "--output-dir", tempDir, }, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.Assert().FileExists(filepath.Join(tempDir, "worker.yaml")) } // TestGenConfigInvalidOutputType tests that the gen config command fails when // and invalid output type is requested. func (suite *GenSuite) TestGenConfigInvalidOutputType() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "worker,foobar", }, base.StdoutEmpty(), base.ShouldFail(), base.StderrMatchFunc(func(output string) error { expected := "invalid output type" if !strings.Contains(output, expected) { return fmt.Errorf("stderr does not contain %q: %q", expected, output) } return nil })) } // TestGenConfigNoOutputType tests that the gen config command fails when an empty list of output types is requested. func (suite *GenSuite) TestGenConfigNoOutputType() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "", }, base.StdoutEmpty(), base.ShouldFail(), base.StderrMatchFunc(func(output string) error { expected := "at least one output type must be specified" if !strings.Contains(output, expected) { return fmt.Errorf("stderr does not contain %q: %q", expected, output) } return nil })) } func init() { allSuites = append(allSuites, new(GenSuite)) } ================================================ FILE: internal/integration/cli/health.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "context" "fmt" "regexp" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // HealthSuite verifies health command. type HealthSuite struct { base.CLISuite } // SuiteName ... func (suite *HealthSuite) SuiteName() string { return "cli.HealthSuite" } // TestClientSideWithExplicitNodes does successful health check run from client-side, providing the explicit set of nodes. func (suite *HealthSuite) TestClientSideWithExplicitNodes() { info := suite.DiscoverNodes(context.TODO()) var args []string for _, machineType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { for _, node := range info.NodesByType(machineType) { switch machineType { case machine.TypeInit: args = append(args, "--init-node", node.IPs[0].String()) case machine.TypeControlPlane: args = append(args, "--control-plane-nodes", node.IPs[0].String()) case machine.TypeWorker: args = append(args, "--worker-nodes", node.IPs[0].String()) case machine.TypeUnknown: // skip it default: panic(fmt.Sprintf("unexpected machine type: %v", machineType)) } } } suite.testClientSide(args...) } // TestClientSideWithDiscovery does a health check run from client-side without explicitly specifying the nodes. // It verifies that the check still passes, because the nodes get populated by the discovery service. func (suite *HealthSuite) TestClientSideWithDiscovery() { discoveryEnabled, err := suite.isDiscoveryEnabled() suite.Require().NoError(err) if !discoveryEnabled { suite.T().Skipf("skipping test: discovery is not enabled on the cluster") } suite.testClientSide() } // TestServerSide does successful health check run from server-side. func (suite *HealthSuite) TestServerSide() { randomControlPlaneNodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"health", "--nodes", randomControlPlaneNodeInternalIP}, base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`waiting for all k8s nodes to report ready`)), ) } func (suite *HealthSuite) testClientSide(extraArgs ...string) { args := append([]string{"--server=false"}, extraArgs...) if suite.K8sEndpoint != "" { args = append(args, "--k8s-endpoint", suite.K8sEndpoint) } suite.RunCLI(append([]string{"health"}, args...), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`waiting for all k8s nodes to report ready`)), ) } func (suite *HealthSuite) isDiscoveryEnabled() (bool, error) { temp := struct { Spec cluster.ConfigSpec `yaml:"spec"` }{} randomControlPlaneNodeInternalIP := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) stdout, _ := suite.RunCLI([]string{"--nodes", randomControlPlaneNodeInternalIP, "get", "discoveryconfigs", "-oyaml"}) err := yaml.Unmarshal([]byte(stdout), &temp) if err != nil { return false, err } return temp.Spec.DiscoveryEnabled, nil } func init() { allSuites = append(allSuites, new(HealthSuite)) } ================================================ FILE: internal/integration/cli/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "strings" "testing" "github.com/blang/semver/v4" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/version" ) // ImageSuite verifies the image command. type ImageSuite struct { base.CLISuite } // SuiteName ... func (suite *ImageSuite) SuiteName() string { return "cli.ImageSuite" } // TestDefault verifies default Talos list of images. func (suite *ImageSuite) TestDefault() { suite.RunCLI([]string{"image", "k8s-bundle"}, base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("registry.k8s.io/kube-apiserver"))), ) } var versionRe = regexp.MustCompile(`^[v]?(\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?)`) func normalizeTag(tag string) string { m := versionRe.FindStringSubmatch(tag) if len(m) == 0 { return tag } return m[1] } // TestSourceBundle verifies talos-bundle Talos list of images. func (suite *ImageSuite) TestSourceBundle() { out := `ghcr.io/siderolabs/installer:v1.11.2 ghcr.io/siderolabs/installer-base:v1.11.2 ghcr.io/siderolabs/imager:v1.11.2 ghcr.io/siderolabs/talos:v1.11.2 ghcr.io/siderolabs/talosctl-all:v1.11.2 ghcr.io/siderolabs/overlays:v1.11.2 ghcr.io/siderolabs/extensions:v1.11.2 ghcr.io/siderolabs/amazon-ena:2.15.0-v1.11.2@sha256:e21baee86adfb637b113751a678e9a57e970d3d597a2e488fc13440db1437732 ghcr.io/siderolabs/amd-ucode:20250917@sha256:9b1a5fd03b9c5bc685d3a363ddc471ff6092a304f5fdfe67aa1f4626e34d72eb ghcr.io/siderolabs/amdgpu:20250917-v1.11.2@sha256:09899678ea287b01ff7104f4a0a7c731fd688d9c0047c58a535dfd1d68875364 ghcr.io/siderolabs/binfmt-misc:v1.11.2@sha256:2da530ba50ca5053636405620d91764d1debacbfd7ecd9c5fe91be0d3d4f90ca ghcr.io/siderolabs/bnx2-bnx2x:20250917@sha256:ac6aaaa0d3312e72279a5cde7de0d71fb61774aa2f97a4e56dd914a9f1dde4d1 ghcr.io/siderolabs/btrfs:v1.11.2@sha256:f1d9351b6627807cd8923204662c0f876b3db70a5b57e5363aadf399bea4a8ed ghcr.io/siderolabs/chelsio-drivers:v1.11.2@sha256:d1800384e3a55b8cb1d82cab086c6dbe5af28e1d56f18fd763fa0aabbde538f8 ghcr.io/siderolabs/chelsio-firmware:20250917@sha256:f54cea7351565001abf9a26794dc1c96ba8182150b2c18777e9eed04dc6d3d8d ghcr.io/siderolabs/cloudflared:2024.12.1@sha256:ab2792aca1108b22a2f59a9852edf6f77fbb337ecf5bedcedfa0ae5a12bd0e41 ghcr.io/siderolabs/crun:1.24@sha256:157f1c563931275443dd46fbc44c854c669f5cf4bbc285356636a57c6d33caed ghcr.io/siderolabs/ctr:v2.1.4@sha256:9291fccaf2dcb05a053dd20472437ce69ef3784c95f633a5278758d24eb407a3 ghcr.io/siderolabs/drbd:9.2.14-v1.11.2@sha256:4e8a32cbff6d96c6a61e523636f7c11f5a72984097e3e4a68f9f5354ac070b99 ghcr.io/siderolabs/dvb-cx23885:v1.11.2@sha256:631785a877a36adf2c7d26db5a2b29f64bcf0ea2356b07aa50a5be8493acc33e ghcr.io/siderolabs/dvb-m88ds3103:v1.11.2@sha256:6cb61a2d0b3257168906b6eeda023b60ae96e16d5a31d549486f7f6ca39c5cab ghcr.io/siderolabs/ecr-credential-provider:v1.34.0@sha256:ca6754837ecb19aba419ba2e9fe558a3aff6b2d897a93f897ac44aa01053fab6 ghcr.io/siderolabs/fuse3:3.17.4@sha256:a4770b619691b64e516fbe65e57e7ca54f2132f230d41c1e3bbce6ae6c14547b ghcr.io/siderolabs/gasket-driver:5815ee3-v1.11.2@sha256:430397008e0f214391a44a2748c5009d373dce5a5d689b99d817a6d4372b922b ghcr.io/siderolabs/glibc:2.41@sha256:7b70eccac7f5ee44e2d7558bc21da67988ca36413be45de5dba2679a12317ce8 ghcr.io/siderolabs/gvisor-debug:v1.0.0@sha256:cc443f896ce6f822fc74234cd285e9ecf39f809654040576c98166819b5ef0a6 ghcr.io/siderolabs/gvisor:20250707.0@sha256:3f43bb94c1b09caa4a82559478b88c30b0b735303e37129e6e7bdc2cefe3ebc7 ghcr.io/siderolabs/hailort:4.21.0@sha256:bcdd3088158b7c71cd852ccea0945d5074040669155f43e33c159c567a23cdb8 ghcr.io/siderolabs/hello-world-service:v1.0.0@sha256:7e5de44b094bbe24d5c22cac45828975dc654c4ff4c1169482fe3671b2e490c9 ghcr.io/siderolabs/i915:20250917-v1.11.2@sha256:46251cb415b5036d7ebb460b0babe98f855930ef2cdc4709acdfa6e379278dd6 ghcr.io/siderolabs/intel-ice-firmware:20250917@sha256:c25225c371e81485c64f339864ede410b560f07eb0fc2702a73315e977a6323d ghcr.io/siderolabs/intel-ucode:20250812@sha256:84ada70546d2f8d28d209ccfc7895c2cd0fa5f623815dcd45e6b118a06ad0959 ghcr.io/siderolabs/iscsi-tools:v0.2.0@sha256:ef6e8038ddaca1faad2d0e5b87ef4696fa6359f287682c9492462abfa1e26906 ghcr.io/siderolabs/kata-containers:3.20.0@sha256:4d8a59c058eb9678385b868e38a21dcd531dad64e708c282c7ef5d06dc27c98b ghcr.io/siderolabs/lldpd:1.0.20@sha256:01fc489e506117b157e45f5ec77183a8984bb538c4223855ce1d8102e32c5d3e ghcr.io/siderolabs/mdadm:v4.3@sha256:70576ecb239156c822b419d3169a478baebafb71432a4961cb71305bcddb7a36 ghcr.io/siderolabs/mei:v1.11.2@sha256:ec5978f641f6db18248f452343e93912852ef45c256dc1b836af7f40783786b6 ghcr.io/siderolabs/metal-agent:v0.1.3@sha256:0bb8dfd62e058af4dd85deed4864e7628e2ac5d7705d711570abf1be19a2f507 ghcr.io/siderolabs/nebula:1.9.6@sha256:e234e575cfffd6cc67ce1b67ba1b72d2827ca26793d82459ed389952e842b4c4 ghcr.io/siderolabs/newt:1.4.4@sha256:9226d5c591cafa714743f3f5519b19997202078704fcb7e1a1264c1613c59bff ghcr.io/siderolabs/nfsd:v1.11.2@sha256:faa93d11292e96ee9c64209178fb641b49a89b600b605b5c2b0283a850a28a3f ghcr.io/siderolabs/nfsrahead:2.8.3@sha256:9f140fd2735dfef1793acc7afaec57fdd547b497b559c12fb088ed3ee449c439 ghcr.io/siderolabs/nonfree-kmod-nvidia-lts:535.247.01-v1.11.2@sha256:af6b123c5269f47aa828c9e472d482fca69efb714b9e256a13b2384ea2b13549 ghcr.io/siderolabs/nonfree-kmod-nvidia-production:570.172.08-v1.11.2@sha256:162ade2a1a826403b5d7d1121c744db13825fc1238f380d3b98c74cf6d233916 ghcr.io/siderolabs/nut-client:2.8.4@sha256:6555859aba70aa4f20bd98171123da6f1ef76060d43d283178c05fe99d536715 ghcr.io/siderolabs/nvidia-container-toolkit-lts:535.247.01-v1.17.8@sha256:e425765c607b34029b61b16add2c455492059e360f73057f54e6a1a3d60279ad ghcr.io/siderolabs/nvidia-container-toolkit-production:570.172.08-v1.17.8@sha256:eff6eb15bb5ceabe84904a6d62f872d5547bb98fdf78f119297f739fd775973d ghcr.io/siderolabs/nvidia-fabricmanager-lts:535.247.01@sha256:aaf3d09e87ef9a0bfa2522775ba7518b30ccac4b909d8d2d739a24410daba769 ghcr.io/siderolabs/nvidia-fabricmanager-production:570.172.08@sha256:43d2649dc55582e1922408663425f7bd14f277839831c3601667d588ffcdfdf3 ghcr.io/siderolabs/nvidia-open-gpu-kernel-modules-lts:535.247.01-v1.11.2@sha256:69fafd7fa4b708226672d2334670bfbb0004fced548d6a0b2fab9807517c7971 ghcr.io/siderolabs/nvidia-open-gpu-kernel-modules-production:570.172.08-v1.11.2@sha256:3b68c873acf144048fd28e43e3054131d777b34101cd3cf9ce1b0d90c77da114 ghcr.io/siderolabs/nvme-cli:v2.14@sha256:6d5052488d524ec1791a6d6c3150e5cff3c88a774f8d828a4553d42deece56f7 ghcr.io/siderolabs/panfrost:20250917-v1.11.2@sha256:8339980ff926f3de05df90008643458234769bb6a68351e255030fce8ec08739 ghcr.io/siderolabs/qemu-guest-agent:10.0.2@sha256:9720300de00544eca155bc19369dfd7789d39a0e23d72837a7188f199e13dc6c ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:9a62f7562ebf07392a1184e6f9354eaea786072bdd8b76ef85a08528ad3a7c53 ghcr.io/siderolabs/realtek-firmware:20250917@sha256:6710269640d8684dfcf782b638b6c4e34896df136185218d8fdb0312a2bf00b0 ghcr.io/siderolabs/revpi-firmware:v1.0.0@sha256:9649e42c71862b9c4b9f0887d0929297e78530854ae507436344cecd6176debe ghcr.io/siderolabs/sbc-allwinner:v0.1.2@sha256:c662949ad20bb37a20bc96c21187fefdcce8c6543769d993656f40d82fd88e2b ghcr.io/siderolabs/sbc-jetson:v0.1.2@sha256:896d043ad0780cccbcd6f7984f3db5685d63f2659eb9dc7023542b72639599d6 ghcr.io/siderolabs/sbc-raspberrypi:v0.1.5@sha256:70a1b174a5bddd57da33e377af324c1f93d7ed87b9bc8f6cdefe5ec179abb4c9 ghcr.io/siderolabs/sbc-rockchip:v0.1.5@sha256:406c0ed9708772d72f0a0afc2998e14e673bf889135d2b47d92771c0d3566ff5 ghcr.io/siderolabs/spin:v0.21.0@sha256:b405524de2a2826b22354962d48688237ff4f5f7cf79f80c27d08f057c51b505 ghcr.io/siderolabs/stargz-snapshotter:v0.17.0@sha256:10280ee408e2e73a6d6f96f64efac3a3ef93bf3874e353a515cf2aa34ce7f2a5 ghcr.io/siderolabs/tailscale:1.88.1@sha256:36e484cbd93340b10e4c0d9ffef5b626e48ba435295936167d2711dd21ed4c3b ghcr.io/siderolabs/tenstorrent:1.34@sha256:ea51e09b201548a3856783828b47be0d76e912f7ee7b02ced6b6dfde9ba1f1c2 ghcr.io/siderolabs/thunderbolt:v1.11.2@sha256:bf6482be7df448317443bfcd8b66bf68c6231e86b85d69c1e7c92ddb8aaf70dc ghcr.io/siderolabs/uinput:v1.11.2@sha256:435b1187a17e1c1153b8374cfcd47ca24d4faab9d6efa0949894d85ade8ff3b7 ghcr.io/siderolabs/usb-modem-drivers:v1.11.2@sha256:c9ee048484cb0d2609f746dc7d90b213d26ad01e07342771bdb10841ec09b886 ghcr.io/siderolabs/util-linux-tools:2.41.1@sha256:bcd353bb7635d6dc9e6e913abdbff5b73d00829e3fd14e549b50533df8aa4faf ghcr.io/siderolabs/v4l-uvc-drivers:v1.11.2@sha256:ae29326c8c3d34d1f1bc5aedf9f79f7b16c833722520c9763d7dd76212d041bd ghcr.io/siderolabs/vc4:v1.11.2@sha256:f5c81fbd7cab1b1eecf904a93e501c7334a47db0dcc12232d2b76295d29c537d ghcr.io/siderolabs/vmtoolsd-guest-agent:v1.4.0@sha256:06cc9885433eb9ca8148e76b38f9b7a1b85e375864b2f0bab5da75e9389ad027 ghcr.io/siderolabs/wasmedge:v0.6.0@sha256:aa255c9d3d5c61010943fd4fc9e3818739605243f4288ef2166915e57267b6d9 ghcr.io/siderolabs/xdma-driver:aefa9a1-v1.11.2@sha256:b65cb2033d46a7c88d317cb29a87741f8c72d768869bbfae832cb76121e4ab14 ghcr.io/siderolabs/xen-guest-agent:0.4.0-g5c274e6@sha256:91e08d9ae45e325bf20da77f251e265d0e72cb38751a6dcee930bf21c9adacc1 ghcr.io/siderolabs/youki:0.5.5@sha256:562ceabb69570203024dbb9b8673ba485af1ffdd082210656573e22557235372 ghcr.io/siderolabs/zerotier:1.16.0@sha256:9444baa3acdc665dba56ed16c8a983c81c3f37fc73877be8fd882f9cf8c9fa5a ghcr.io/siderolabs/zfs:2.3.3-v1.11.2@sha256:73782571f334b18995ddf324d24b86ea9a11aa37661a468b4e077da63e0d9714` suite.RunCLI([]string{"image", "talos-bundle", "v1.11.2"}, base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta(out))), ) tag, err := semver.ParseTolerant(normalizeTag(version.Tag)) assert.NoError(suite.T(), err) suite.T().Log(normalizeTag(version.Tag)) suite.T().Log(version.Tag) if strings.TrimLeft(version.Tag, "v") == normalizeTag(version.Tag) { suite.T().Skip("skipping the test for the exact version tag") } suite.RunCLI([]string{"image", "talos-bundle", "v" + tag.String()}, base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("ghcr.io/siderolabs/talos:v"+tag.String()))), ) suite.RunCLI([]string{"image", "talos-bundle", tag.String()}, base.StdoutEmpty(), base.ShouldFail(), ) tag.Patch = 0 assert.NoError(suite.T(), tag.IncrementMinor()) suite.RunCLI([]string{"image", "talos-bundle", "v" + tag.FinalizeVersion()}, base.StdoutEmpty(), base.ShouldFail(), ) } // TestList verifies listing images in the CRI. func (suite *ImageSuite) TestList() { suite.RunCLI([]string{"image", "ls", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}, base.StdoutShouldMatch(regexp.MustCompile(`IMAGE\s+DIGEST\s+SIZE`)), base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("registry.k8s.io/kube-apiserver"))), ) suite.RunCLI([]string{"image", "ls", "--namespace", "system", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`IMAGE\s+DIGEST\s+SIZE`)), base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("ghcr.io/siderolabs/kubelet:"))), ) } // TestPull verifies pulling images to the CRI. func (suite *ImageSuite) TestPull() { const image = "registry.k8s.io/kube-apiserver:v1.27.0" // sync this to e2e.sh `build_image_cache` node := suite.RandomDiscoveredNodeInternalIP() if stdout, _ := suite.RunCLI([]string{"get", "imagecacheconfig", "--nodes", node, "--output", "jsonpath='{.spec.status}'"}); strings.Contains(stdout, "ready") { suite.T().Logf("skipping as the image cache is present") return } suite.RunCLI([]string{"image", "pull", "--nodes", node, image}, base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile( "("+ // either pinned image (verification on) regexp.QuoteMeta("pulled image registry.k8s.io/kube-apiserver@sha256:89b8d9dbef2b905b7d028ca8b7f79d35ebd9baa66b0a3ee2ddd4f3e0e2804b45")+ "|"+ // or original ref (without verification) regexp.QuoteMeta("pulled image "+image)+ ")", )), ) // verify that pulled image appeared, also image aliases should appear suite.RunCLI([]string{"image", "ls", "--nodes", node}, base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta(image))), base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("sha256:89b8d9dbef2b905b7d028ca8b7f79d35ebd9baa66b0a3ee2ddd4f3e0e2804b45"))), base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta("registry.k8s.io/kube-apiserver@sha256:89b8d9dbef2b905b7d028ca8b7f79d35ebd9baa66b0a3ee2ddd4f3e0e2804b45"))), base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta(image))), ) // remove the image suite.RunCLI([]string{"image", "remove", "--nodes", node, image}, base.StdoutEmpty(), ) } // TestCacheCreateOCI verifies creating a cache tarball. func (suite *ImageSuite) TestCacheCreateOCI() { if testing.Short() { suite.T().Skip("skipping in short mode") } stdOut, _ := suite.RunCLI([]string{"image", "k8s-bundle"}) imagesList := strings.Split(strings.Trim(stdOut, "\n"), "\n") imagesArgs := xslices.Map(imagesList[:2], func(image string) string { return "--images=" + image }) cacheDir := suite.T().TempDir() args := []string{"image", "cache-create", "--image-cache-path", cacheDir, "--force", "--layout", "oci"} //nolint:prealloc // this is a test args = append(args, imagesArgs...) suite.RunCLI(args, base.StdoutEmpty(), base.StderrNotEmpty()) assert.FileExistsf(suite.T(), cacheDir+"/index.json", "index.json should exist in the image cache directory") } // TestCacheCreateFlat verifies creating a cache directory. func (suite *ImageSuite) TestCacheCreateFlat() { if testing.Short() { suite.T().Skip("skipping in short mode") } stdOut, _ := suite.RunCLI([]string{"image", "k8s-bundle"}) imagesList := strings.Split(strings.Trim(stdOut, "\n"), "\n") imagesArgs := xslices.Map(imagesList[:2], func(image string) string { return "--images=" + image }) cacheDir := suite.T().TempDir() args := []string{"image", "cache-create", "--image-cache-path", cacheDir, "--force", "--layout", "flat"} //nolint:prealloc // this is a test args = append(args, imagesArgs...) suite.RunCLI(args, base.StdoutEmpty(), base.StderrNotEmpty()) assert.DirExistsf(suite.T(), cacheDir+"/blob", "blob directory should exist in the image cache directory") assert.DirExistsf(suite.T(), cacheDir+"/manifests", "manifests directory should exist in the image cache directory") } func init() { allSuites = append(allSuites, new(ImageSuite)) } ================================================ FILE: internal/integration/cli/inject.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "bytes" _ "embed" "fmt" "io" "os" "path/filepath" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/integration/base" ) var ( //go:embed testdata/inject/talosconfig-input.yaml inputManifests []byte //go:embed testdata/inject/talosconfig-expected.yaml expectedManifests []byte ) // InjectSuite verifies inject command. type InjectSuite struct { base.CLISuite } // SuiteName ... func (suite *InjectSuite) SuiteName() string { return "cli.InjectSuite" } // TestServiceAccount tests inject serviceaccount command. func (suite *InjectSuite) TestServiceAccount() { suite.testServiceAccount(inputManifests) } // TestServiceAccountAlreadyInjectedNoChange tests inject serviceaccount command when the input manifest is already injected, // makes sure that it stays the same. func (suite *InjectSuite) TestServiceAccountAlreadyInjectedNoChange() { suite.testServiceAccount(expectedManifests) } func (suite *InjectSuite) testServiceAccount(input []byte) { expectedDocs, err := yamlDocs(expectedManifests) suite.Require().NoError(err) tempDir := suite.T().TempDir() inputPath := filepath.Join(tempDir, "input.yaml") err = os.WriteFile(inputPath, input, 0o644) suite.Require().NoError(err) stdout, _ := suite.RunCLI([]string{"inject", "serviceaccount", "-f", inputPath, "--roles", "os:reader,os:admin"}) stdoutDocs, err := yamlDocs([]byte(stdout)) suite.Require().NoError(err) suite.Assert().Equal(expectedDocs, stdoutDocs, "inject serviceaccount output did not match expected output") } func yamlDocs(input []byte) ([]map[string]any, error) { decoder := yaml.NewDecoder(bytes.NewReader(input)) var docs []map[string]any for { var doc map[string]any if err := decoder.Decode(&doc); err != nil { if err == io.EOF { break } return nil, fmt.Errorf("document decode failed: %w", err) } docs = append(docs, doc) } return docs, nil } func init() { allSuites = append(allSuites, new(InjectSuite)) } ================================================ FILE: internal/integration/cli/jsonpath.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // JSONPathSuite verifies dmesg command. type JSONPathSuite struct { base.CLISuite } // SuiteName ... func (suite *JSONPathSuite) SuiteName() string { return "cli.JSONPathSuite" } // TestGetScalarPropertyWithJSONPath verifies that the jsonpath filter to the get command can return scalar data. func (suite *JSONPathSuite) TestGetScalarPropertyWithJSONPath() { node := suite.RandomDiscoveredNodeInternalIP() suite.RunCLI([]string{"get", "--nodes", node, "etcfilestatus", "--output", `jsonpath='{.metadata.namespace}'`}, base.StdoutShouldMatch(regexp.MustCompile("files")), base.WithRetry(retry.Constant(15*time.Second, retry.WithUnits(time.Second))), ) } // TestGetWithJSONPathWildcard verifies that the jsonpath filter to the get command accepts a wildcard operator. // It is handy when 'get' requests a list of resources. func (suite *JSONPathSuite) TestGetWithJSONPathWildcard() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"get", "--nodes", node, "manifests", "--output", `jsonpath='{.spec[*].metadata.name}'`}, base.StdoutShouldMatch(regexp.MustCompile("coredns")), base.StdoutShouldMatch(regexp.MustCompile("kube-dns")), base.StdoutShouldMatch(regexp.MustCompile("kubeconfig-in-cluster")), base.WithRetry(retry.Constant(15*time.Second, retry.WithUnits(time.Second))), ) } // TestGetComplexPropertyWithJSONPath verifies that the jsonpath filter to the get command can return JSON. func (suite *JSONPathSuite) TestGetComplexPropertyWithJSONPath() { node := suite.RandomDiscoveredNodeInternalIP() const jsonMetadataRegex = `\{\s*"created":\s".*",\s*"id":\s".*",\s*\s*"namespace":\s".*",\s*"owner":\s".*",\s*"phase":\s".*",\s*"type":\s".*",\s*"updated":\s".*",\s*"version":\s\d\n\}` suite.RunCLI([]string{"get", "--nodes", node, "etcfilestatus", "--output", `jsonpath='{.metadata}'`}, base.StdoutShouldMatch(regexp.MustCompile(jsonMetadataRegex)), base.WithRetry(retry.Constant(15*time.Second, retry.WithUnits(time.Second))), ) } func init() { allSuites = append(allSuites, new(JSONPathSuite)) } ================================================ FILE: internal/integration/cli/kubeconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "os" "path/filepath" "regexp" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // KubeconfigSuite verifies dmesg command. type KubeconfigSuite struct { base.CLISuite } // SuiteName ... func (suite *KubeconfigSuite) SuiteName() string { return "cli.KubeconfigSuite" } // TestDirectory generates kubeconfig in specified directory. func (suite *KubeconfigSuite) TestDirectory() { tempDir := suite.T().TempDir() suite.RunCLI([]string{"kubeconfig", "--merge=false", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), tempDir}, base.StdoutEmpty()) path := filepath.Join(tempDir, "kubeconfig") suite.Require().FileExists(path) _, err := clientcmd.LoadFromFile(path) suite.Require().NoError(err) } // TestCwd generates kubeconfig in cwd. func (suite *KubeconfigSuite) TestCwd() { tempDir := suite.T().TempDir() savedCwd, err := os.Getwd() suite.Require().NoError(err) defer os.Chdir(savedCwd) //nolint:errcheck suite.Require().NoError(os.Chdir(tempDir)) suite.RunCLI([]string{"kubeconfig", "--merge=false", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}, base.StdoutEmpty()) suite.Require().FileExists(filepath.Join(tempDir, "kubeconfig")) } // TestCustomName generates kubeconfig with custom name. func (suite *KubeconfigSuite) TestCustomName() { tempDir := suite.T().TempDir() suite.RunCLI([]string{"kubeconfig", "--merge=false", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), filepath.Join(tempDir, "k8sconfig")}, base.StdoutEmpty()) suite.Require().FileExists(filepath.Join(tempDir, "k8sconfig")) } // TestMultiNodeFail verifies that command fails with multiple nodes. func (suite *KubeconfigSuite) TestMultiNodeFail() { suite.RunCLI([]string{"kubeconfig", "--merge=false", "--nodes", "127.0.0.1", "--nodes", "127.0.0.1", "."}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`is not supported with multiple nodes`))) } // TestMergeRename tests merge config into existing kubeconfig with default rename conflict resolution. func (suite *KubeconfigSuite) TestMergeRename() { tempDir := suite.T().TempDir() path := filepath.Join(tempDir, "config") suite.RunCLI([]string{"kubeconfig", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), path}, base.StdoutEmpty()) suite.RunCLI([]string{"kubeconfig", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), path}) config, err := clientcmd.LoadFromFile(path) suite.Require().NoError(err) suite.Require().Equal(len(config.Contexts), 2) } // TestMergeOverwrite test merge config into existing kubeconfig with overwrite conflict resolution. func (suite *KubeconfigSuite) TestMergeOverwrite() { tempDir := suite.T().TempDir() path := filepath.Join(tempDir, "config") suite.RunCLI([]string{"kubeconfig", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), path}, base.StdoutEmpty()) suite.RunCLI([]string{"kubeconfig", "--force", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), path}, base.StdoutEmpty()) config, err := clientcmd.LoadFromFile(path) suite.Require().NoError(err) suite.Require().Equal(len(config.Contexts), 1) } func init() { allSuites = append(allSuites, new(KubeconfigSuite)) } ================================================ FILE: internal/integration/cli/list.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "os" "os/exec" "regexp" "slices" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // ListSuite verifies dmesg command. type ListSuite struct { base.CLISuite } // SuiteName ... func (suite *ListSuite) SuiteName() string { return "cli.ListSuite" } // TestSuccess runs comand with success. func (suite *ListSuite) TestSuccess() { suite.RunCLI([]string{"list", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "/etc"}, base.StdoutShouldMatch(regexp.MustCompile(`os-release`))) suite.RunCLI([]string{"list", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "/"}, base.StdoutShouldNotMatch(regexp.MustCompile(`os-release`))) } // TestDepth tests various combinations of --recurse and --depth flags. // //nolint:tparallel func (suite *ListSuite) TestDepth() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) // Expected maximum number of separators in the output // In plain terms, it's the maximum depth of the directory tree maxSeps := 5 if stdout, _ := suite.RunCLI([]string{"get", "imagecacheconfig", "--nodes", node, "--output", "yaml"}); strings.Contains(stdout, "ready") { // Image cache paths parts are longer maxSeps = 9 } // checks that enough separators are encountered in the output for _, test := range []struct { separators int flags []string }{ {separators: 0}, {separators: 0, flags: []string{"--recurse=false"}}, {separators: 0, flags: []string{"--depth=-1"}}, {separators: 0, flags: []string{"--depth=0"}}, {separators: 0, flags: []string{"--depth=1"}}, {separators: 1, flags: []string{"--depth=2"}}, {separators: 2, flags: []string{"--depth=3"}}, {separators: -maxSeps, flags: []string{"--recurse=true"}}, // negative means "at least" } { cmdFn := suite.MakeCMDFn(slices.Insert(test.flags, 0, "list", "--nodes", node, "/system")) suite.T().Run(strings.Join(test.flags, ","), func(t *testing.T) { t.Parallel() runAndCheck(t, test.separators, cmdFn, test.flags...) }) } } func runAndCheck(t *testing.T, expectedSeparators int, cmdFn func() *exec.Cmd, flags ...string) { stdout, _ := base.RunCLI(t, cmdFn) lines := strings.Split(strings.TrimSpace(stdout), "\n") assert.Greater(t, len(lines), 2) assert.Equal(t, []string{"NODE", "NAME"}, strings.Fields(lines[0])) assert.Equal(t, []string{"."}, strings.Fields(lines[1])[1:]) var maxActualSeparators int for _, line := range lines[2:] { actualSeparators := strings.Count(strings.Fields(line)[1], string(os.PathSeparator)) if expectedSeparators >= 0 && !assert.LessOrEqual( t, actualSeparators, expectedSeparators, "too many separators, flags: %s\nlines:\n%s", strings.Join(flags, " "), stdout, ) { return } maxActualSeparators = max(maxActualSeparators, actualSeparators) } if expectedSeparators < 0 { assert.LessOrEqual( t, -expectedSeparators, maxActualSeparators, "not enough separators, \nflags: %s\nlines:\n%s", strings.Join(flags, " "), stdout, ) } else { assert.Equal( t, expectedSeparators, maxActualSeparators, "not enough separators, \nflags: %s\nlines:\n%s", strings.Join(flags, " "), stdout, ) } } func init() { allSuites = append(allSuites, new(ListSuite)) } ================================================ FILE: internal/integration/cli/logs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "fmt" "regexp" "strings" "github.com/siderolabs/talos/internal/integration/base" ) // LogsSuite verifies logs command. type LogsSuite struct { base.CLISuite } // SuiteName ... func (suite *LogsSuite) SuiteName() string { return "cli.LogsSuite" } // TestServiceLogs verifies that logs are displayed. func (suite *LogsSuite) TestServiceLogs() { suite.RunCLI([]string{"logs", "kubelet", "--nodes", suite.RandomDiscoveredNodeInternalIP()}) // default checks for stdout not empty } // TestTailLogs verifies that logs can be displayed with tail lines. func (suite *LogsSuite) TestTailLogs() { node := suite.RandomDiscoveredNodeInternalIP() // run some machined API calls to produce enough log lines for range 10 { suite.RunCLI([]string{"-n", node, "version"}) } suite.RunCLI([]string{"logs", "apid", "-n", node, "--tail", "5"}, base.StdoutMatchFunc(func(stdout string) error { lines := strings.Count(stdout, "\n") if lines != 5 { return fmt.Errorf("expected %d lines, found %d lines", 5, lines) } return nil })) } // TestServiceNotFound verifies that logs displays an error if service is not found. func (suite *LogsSuite) TestServiceNotFound() { suite.RunCLI([]string{"logs", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "servicenotfound"}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrShouldMatch(regexp.MustCompile(`ERROR:.+ log "servicenotfound" was not registered`)), base.ShouldFail(), ) } func init() { allSuites = append(allSuites, new(LogsSuite)) } ================================================ FILE: internal/integration/cli/machineconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "errors" "fmt" "os" "path/filepath" "strings" "github.com/hashicorp/go-multierror" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/integration/base" ) // MachineConfigSuite verifies machineconfig command. type MachineConfigSuite struct { base.CLISuite savedCwd string } // SuiteName ... func (suite *MachineConfigSuite) SuiteName() string { return "cli.MachineConfigSuite" } // SetupTest ... func (suite *MachineConfigSuite) SetupTest() { var err error suite.savedCwd, err = os.Getwd() suite.Require().NoError(err) } // TearDownTest ... func (suite *MachineConfigSuite) TearDownTest() { if suite.savedCwd != "" { suite.Require().NoError(os.Chdir(suite.savedCwd)) } } // TestGen tests the gen subcommand. // Identical to GenSuite.TestGenConfigToStdoutControlPlane // The remaining functionality is checked for `talosctl gen config` in GenSuite. func (suite *MachineConfigSuite) TestGen() { suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane", "--output", "-", }, base.StderrNotEmpty(), base.StdoutMatchFunc(func(output string) error { expected := "type: controlplane" if !strings.Contains(output, expected) { return fmt.Errorf("stdout does not contain %q: %q", expected, output) } return nil })) } // TestPatchPrintStdout tests the patch subcommand with output set to stdout // with multiple patches from the command line and from file. func (suite *MachineConfigSuite) TestPatchPrintStdout() { patch1, err := yaml.Marshal(map[string]any{ "cluster": map[string]any{ "clusterName": "replaced", }, }) suite.Require().NoError(err) patch2, err := yaml.Marshal(map[string]any{ "cluster": map[string]any{ "controlPlane": map[string]any{ "endpoint": "https://replaced", }, }, }) suite.Require().NoError(err) patch2Path := filepath.Join(suite.T().TempDir(), "patch2.json") err = os.WriteFile(patch2Path, patch2, 0o644) suite.Require().NoError(err) mc := filepath.Join(suite.T().TempDir(), "input.yaml") suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane", "--output", mc, }, base.StderrNotEmpty(), base.StdoutEmpty(), ) suite.RunCLI([]string{ "machineconfig", "patch", mc, "--patch", string(patch1), "-p", "@" + patch2Path, }, base.StdoutMatchFunc(func(output string) error { var matchErr *multierror.Error if !strings.Contains(output, "clusterName: replaced") { matchErr = multierror.Append(matchErr, errors.New("clusterName not replaced")) } if !strings.Contains(output, "endpoint: https://replaced") { matchErr = multierror.Append(matchErr, errors.New("endpoint not replaced")) } return matchErr.ErrorOrNil() })) } // TestPatchWriteToFile tests the patch subcommand with output set to a file. func (suite *MachineConfigSuite) TestPatchWriteToFile() { patch1, err := yaml.Marshal(map[string]any{ "cluster": map[string]any{ "clusterName": "replaced", }, }) suite.Require().NoError(err) mc := filepath.Join(suite.T().TempDir(), "input2.yaml") suite.RunCLI([]string{ "gen", "config", "foo", "https://192.168.0.1:6443", "--output-types", "controlplane", "--output", mc, }, base.StderrNotEmpty(), base.StdoutEmpty(), ) outputFile := filepath.Join(suite.T().TempDir(), "inner", "output.yaml") suite.RunCLI([]string{ "machineconfig", "patch", mc, "--patch", string(patch1), "--output", outputFile, }, base.StdoutEmpty()) outputBytes, err := os.ReadFile(outputFile) suite.Assert().NoError(err) suite.Assert().Contains(string(outputBytes), "clusterName: replaced") } func init() { allSuites = append(allSuites, new(MachineConfigSuite)) } ================================================ FILE: internal/integration/cli/memory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" ) // MemorySuite verifies dmesg command. type MemorySuite struct { base.CLISuite } // SuiteName ... func (suite *MemorySuite) SuiteName() string { return "cli.MemorySuite" } // TestSuccess verifies successful execution. func (suite *MemorySuite) TestSuccess() { suite.RunCLI([]string{"memory", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`FREE`))) } // TestVerbose verifies verbose mode. func (suite *MemorySuite) TestVerbose() { suite.RunCLI([]string{"memory", "-v", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`MemFree: \d+ kB`))) } func init() { allSuites = append(allSuites, new(MemorySuite)) } ================================================ FILE: internal/integration/cli/meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "strings" "github.com/google/uuid" "github.com/siderolabs/talos/internal/integration/base" ) // MetaSuite verifies meta sub-commands. type MetaSuite struct { base.CLISuite } // SuiteName ... func (suite *MetaSuite) SuiteName() string { return "cli.MetaSuite" } // TestKey writes a META key, deletes it, verifies via resources. func (suite *MetaSuite) TestKey() { node := suite.RandomDiscoveredNodeInternalIP() // detect docker platform and skip the test stdout, _ := suite.RunCLI([]string{"--nodes", node, "get", "platformmetadata"}) if strings.Contains(stdout, "container") { suite.T().Skip("skipping on container platform") } key := "0x05" // unused/reserved key value := uuid.New().String() suite.RunCLI([]string{"--nodes", node, "meta", "write", key, value}, base.StdoutEmpty()) suite.RunCLI([]string{"--nodes", node, "get", "metakeys", key}, base.StdoutShouldMatch(regexp.MustCompile(key)), base.StdoutShouldMatch(regexp.MustCompile(value)), ) suite.RunCLI([]string{"--nodes", node, "meta", "delete", key}, base.StdoutEmpty()) suite.RunCLI([]string{"--nodes", node, "get", "metakeys", key}, base.ShouldFail(), base.StderrShouldMatch(regexp.MustCompile("NotFound")), ) } func init() { allSuites = append(allSuites, new(MetaSuite)) } ================================================ FILE: internal/integration/cli/mounts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "os" "regexp" "strings" "github.com/siderolabs/talos/internal/integration/base" ) // MountsSuite verifies dmesg command. type MountsSuite struct { base.CLISuite } // SuiteName ... func (suite *MountsSuite) SuiteName() string { return "cli.MountsSuite" } // TestSuccess verifies successful execution. func (suite *MountsSuite) TestSuccess() { suite.RunCLI([]string{"mounts", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`(?s)FILESYSTEM.*`))) } // TestUserDisksMounted verifies user disk mounts created. func (suite *MountsSuite) TestUserDisksMounted() { paths := os.Getenv("USER_DISKS_MOUNTS") if paths == "" { suite.T().Skip("USER_DISKS_MOUNTS is not set") } for path := range strings.SplitSeq(paths, ",") { suite.RunCLI([]string{"mounts", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(path))) } } func init() { allSuites = append(allSuites, new(MountsSuite)) } ================================================ FILE: internal/integration/cli/netstat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "context" "regexp" "strings" "github.com/siderolabs/talos/internal/integration/base" ) // NetstatSuite verifies etcd command and coredns container. type NetstatSuite struct { base.CLISuite } // SuiteName ... func (suite *NetstatSuite) SuiteName() string { return "cli.NetstatSuite" } // TestListening verifies that there are listening connections. func (suite *NetstatSuite) TestListening() { suite.RunCLI([]string{"netstat", "--listening", "--programs", "--tcp", "--extend", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`:::50000.+LISTEN.+/apid`))) } // TestContainers verifies that containers are listed. func (suite *NetstatSuite) TestContainers() { nodes := suite.DiscoverNodeInternalIPs(context.TODO()) suite.RunCLI([]string{"netstat", "--listening", "--programs", "--udp", "--ipv6", "--pods", "--nodes", strings.Join(nodes, ",")}, base.StdoutShouldMatch(regexp.MustCompile(`:::53\s+:::\*.+/coredns\s+kube-system/coredns-`))) } func init() { allSuites = append(allSuites, new(NetstatSuite)) } ================================================ FILE: internal/integration/cli/patch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "encoding/json" "fmt" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // PatchSuite verifies dmesg command. type PatchSuite struct { base.CLISuite } // SuiteName ... func (suite *PatchSuite) SuiteName() string { return "cli.PatchSuite" } // TestSuccess successful run. func (suite *PatchSuite) TestSuccess() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) patch := map[string]any{ "cluster": map[string]any{ "proxy": map[string]any{ "image": fmt.Sprintf("%s:v%s", constants.KubeProxyImage, constants.DefaultKubernetesVersion), }, }, } data, err := json.Marshal(patch) suite.Require().NoError(err) suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig", "--mode=no-reboot"}, base.StdoutEmpty(), base.StderrNotEmpty(), ) suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig", "--mode=no-reboot", "--dry-run"}, base.StdoutEmpty(), base.StderrNotEmpty(), ) } // TestError runs comand with error. func (suite *PatchSuite) TestError() { node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) patch := []map[string]any{ { "op": "crash", "path": "/cluster/proxy", "value": map[string]any{ "image": fmt.Sprintf("%s:v%s", constants.KubeProxyImage, constants.DefaultKubernetesVersion), }, }, } data, err := json.Marshal(patch) suite.Require().NoError(err) suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig"}, base.StdoutEmpty(), base.ShouldFail()) suite.RunCLI([]string{"patch", "--nodes", node, "--patch", string(data), "machineconfig", "v1alpha2"}, base.StdoutEmpty(), base.ShouldFail()) suite.RunCLI([]string{"patch", "--nodes", node, "--patch-file", "/nnnope", "machineconfig"}, base.StdoutEmpty(), base.ShouldFail()) suite.RunCLI([]string{"patch", "--nodes", node, "--patch", "it's not even a json", "machineconfig"}, base.StdoutEmpty(), base.ShouldFail()) } func init() { allSuites = append(allSuites, new(PatchSuite)) } ================================================ FILE: internal/integration/cli/pcap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // PcapSuite verifies etcd command. type PcapSuite struct { base.CLISuite } // SuiteName ... func (suite *PcapSuite) SuiteName() string { return "cli.PcapSuite" } // TestLoopback verifies that there are some packets on loopback interface. func (suite *PcapSuite) TestLoopback() { suite.RunCLI([]string{"pcap", "--interface", "lo", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane), "--duration", "2s"}) // default checks for stdout not empty } func init() { allSuites = append(allSuites, new(PcapSuite)) } ================================================ FILE: internal/integration/cli/pki.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cli import ( _ "embed" "os" "path/filepath" ) var ( //go:embed "testdata/pki/ca.crt" pkiCACrt []byte //go:embed "testdata/pki/ca.key" pkiCAKey []byte //go:embed "testdata/pki/front-proxy-ca.crt" pkiFrontProxyCACrt []byte //go:embed "testdata/pki/front-proxy-ca.key" pkiFrontProxyCAKey []byte //go:embed "testdata/pki/sa.key" pkiSAKey []byte //go:embed "testdata/pki/etcd/ca.crt" pkiEtcdCACrt []byte //go:embed "testdata/pki/etcd/ca.key" pkiEtcdCAKey []byte ) func writeKubernetesPKIFiles(dir string) error { if err := os.Mkdir(dir, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(dir, "ca.crt"), pkiCACrt, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(dir, "ca.key"), pkiCAKey, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(dir, "front-proxy-ca.crt"), pkiFrontProxyCACrt, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(dir, "front-proxy-ca.key"), pkiFrontProxyCAKey, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(dir, "sa.key"), pkiSAKey, 0o777); err != nil { return err } etcdDir := filepath.Join(dir, "etcd") if err := os.Mkdir(etcdDir, 0o777); err != nil { return err } if err := os.WriteFile(filepath.Join(etcdDir, "ca.crt"), pkiEtcdCACrt, 0o777); err != nil { return err } return os.WriteFile(filepath.Join(etcdDir, "ca.key"), pkiEtcdCAKey, 0o777) } ================================================ FILE: internal/integration/cli/processes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" ) // ProcessesSuite verifies dmesg command. type ProcessesSuite struct { base.CLISuite } // SuiteName ... func (suite *ProcessesSuite) SuiteName() string { return "cli.ProcessesSuite" } // TestSuccess verifies successful execution. func (suite *ProcessesSuite) TestSuccess() { suite.RunCLI([]string{"processes", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`PID`))) } func init() { allSuites = append(allSuites, new(ProcessesSuite)) } ================================================ FILE: internal/integration/cli/read.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" ) // ReadSuite verifies dmesg command. type ReadSuite struct { base.CLISuite } // SuiteName ... func (suite *ReadSuite) SuiteName() string { return "cli.ReadSuite" } // TestSuccess runs comand with success. func (suite *ReadSuite) TestSuccess() { suite.RunCLI([]string{"read", "--nodes", suite.RandomDiscoveredNodeInternalIP(), "/etc/os-release"}, base.StdoutShouldMatch(regexp.MustCompile(`ID=talos`))) } // TestMultiNodeFail verifies that command fails with multiple nodes. func (suite *ReadSuite) TestMultiNodeFail() { suite.RunCLI([]string{"read", "--nodes", "127.0.0.1", "--nodes", "127.0.0.1", "/etc/os-release"}, base.ShouldFail(), base.StdoutEmpty(), base.StderrShouldMatch(regexp.MustCompile(`is not supported with multiple nodes`))) } func init() { allSuites = append(allSuites, new(ReadSuite)) } ================================================ FILE: internal/integration/cli/reboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "errors" "fmt" "path/filepath" "strings" "testing" "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // RebootSuite tests reboot command. type RebootSuite struct { base.CLISuite } // SuiteName ... func (suite *RebootSuite) SuiteName() string { return "cli.RebootSuite" } // TestReboot tests rebooting nodes. func (suite *RebootSuite) TestReboot() { if testing.Short() { suite.T().Skip("skipping in short mode") } controlPlaneNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) workerNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodes := fmt.Sprintf( "%s,%s", controlPlaneNode, workerNode, ) suite.T().Logf("rebooting nodes %s via the CLI", nodes) suite.RunCLI([]string{"reboot", "-n", nodes, "--debug"}, base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrMatchFunc(func(stdout string) error { if strings.Contains(stdout, "method is not supported") { suite.T().Skip("reboot is not supported") } var err error for _, node := range []string{controlPlaneNode, workerNode} { if !strings.Contains(stdout, fmt.Sprintf("%q: events check condition met", node)) { err = multierror.Append(err, fmt.Errorf("events check condition not met on %v", node)) } if !strings.Contains(stdout, fmt.Sprintf("%q: post check passed", node)) { err = multierror.Append(err, fmt.Errorf("post check not passed on %v", node)) } } return err })) suite.T().Logf("running the cluster health check") // run the health check to make sure cluster is fully healthy after a node reboot args := []string{"--server=false"} if suite.K8sEndpoint != "" { args = append(args, "--k8s-endpoint", strings.Split(suite.K8sEndpoint, ":")[0]) } suite.RunCLI(append([]string{"health"}, args...), base.StdoutEmpty(), base.StderrNotEmpty(), ) } // TestRebootEarlyFailPrintsOutput tests the action tracker used by reboot command to track reboot status // does not suppress the stderr output if there is an error occurring at an early stage, i.e. before the // action status reporting starts. func (suite *RebootSuite) TestRebootEarlyFailPrintsOutput() { controlPlaneNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) invalidTalosconfig := filepath.Join(suite.T().TempDir(), "talosconfig.yaml") suite.T().Logf("attempting to reboot node %q using talosconfig %q", controlPlaneNode, invalidTalosconfig) suite.RunCLI([]string{"--talosconfig", invalidTalosconfig, "reboot", "-n", controlPlaneNode}, base.ShouldFail(), base.StdoutEmpty(), base.StderrNotEmpty(), base.StderrMatchFunc(func(stdout string) error { if strings.Contains(stdout, "method is not supported") { suite.T().Skip("reboot is not supported") } if !strings.Contains(stdout, "failed to determine endpoints") { return errors.New("expected to find 'failed to determine endpoints' in stderr") } return nil })) } func init() { allSuites = append(allSuites, new(RebootSuite)) } ================================================ FILE: internal/integration/cli/restart.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "testing" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // RestartSuite verifies dmesg command. type RestartSuite struct { base.CLISuite } // SuiteName ... func (suite *RestartSuite) SuiteName() string { return "cli.RestartSuite" } // TestSystem restarts system containerd process. func (suite *RestartSuite) TestSystem() { if testing.Short() { suite.T().Skip("skipping in short mode") } // trustd only runs on control plane nodes node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"restart", "-n", node, "trustd"}, base.StdoutEmpty()) time.Sleep(200 * time.Millisecond) suite.RunAndWaitForMatch([]string{"service", "-n", node, "trustd"}, regexp.MustCompile(`EVENTS\s+\[Running\]: Health check successful`), 30*time.Second) } func init() { allSuites = append(allSuites, new(RestartSuite)) } ================================================ FILE: internal/integration/cli/services.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "time" "github.com/siderolabs/talos/internal/integration/base" ) // ServicesSuite verifies dmesg command. type ServicesSuite struct { base.CLISuite } // SuiteName ... func (suite *ServicesSuite) SuiteName() string { return "cli.ServicesSuite" } // TestList verifies service list. func (suite *ServicesSuite) TestList() { suite.RunCLI([]string{"services", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`STATE`)), base.StdoutShouldMatch(regexp.MustCompile(`apid`)), ) } // TestStatus verifies service status. func (suite *ServicesSuite) TestStatus() { suite.RunCLI([]string{"service", "apid", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`STATE`)), base.StdoutShouldMatch(regexp.MustCompile(`apid`)), base.StdoutShouldMatch(regexp.MustCompile(`\[Running\]`)), ) } // TestRestart verifies kubelet restart. func (suite *ServicesSuite) TestRestart() { node := suite.RandomDiscoveredNodeInternalIP() suite.RunCLI([]string{"service", "kubelet", "restart", "--nodes", node}) time.Sleep(200 * time.Millisecond) suite.RunAndWaitForMatch([]string{"service", "-n", node, "kubelet"}, regexp.MustCompile(`EVENTS\s+\[Running\]: Health check successful`), 30*time.Second) } func init() { allSuites = append(allSuites, new(ServicesSuite)) } ================================================ FILE: internal/integration/cli/stats.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // StatsSuite verifies dmesg command. type StatsSuite struct { base.CLISuite } // SuiteName ... func (suite *StatsSuite) SuiteName() string { return "cli.StatsSuite" } // TestContainerd inspects stats via containerd driver. func (suite *StatsSuite) TestContainerd() { suite.RunCLI([]string{"stats", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`CPU`)), base.StdoutShouldMatch(regexp.MustCompile(`apid`)), ) } // TestCRI inspects stats via CRI driver. func (suite *StatsSuite) TestCRI() { suite.RunCLI([]string{"stats", "-k", "--nodes", suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane)}, base.StdoutShouldMatch(regexp.MustCompile(`CPU`)), base.StdoutShouldMatch(regexp.MustCompile(`kube-system/kube-apiserver`)), base.StdoutShouldMatch(regexp.MustCompile(`k8s.io`)), ) } func init() { allSuites = append(allSuites, new(StatsSuite)) } ================================================ FILE: internal/integration/cli/support.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "archive/zip" "fmt" "path/filepath" "strings" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // SupportSuite verifies support command. type SupportSuite struct { base.CLISuite } // SuiteName ... func (suite *SupportSuite) SuiteName() string { return "cli.SupportSuite" } // TestSupport does successful support run. func (suite *SupportSuite) TestSupport() { tempDir := suite.T().TempDir() output := filepath.Join(tempDir, "support.zip") node := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) suite.RunCLI([]string{"support", "--nodes", node, "-w", "5", "-O", output}, base.StderrNotEmpty(), ) archive, err := zip.OpenReader(output) suite.Require().NoError(err) defer archive.Close() //nolint:errcheck files := map[string]struct{}{} for _, f := range archive.File { files[f.Name] = struct{}{} if strings.HasSuffix(f.Name, "dmesg.log") { suite.Require().Greater(f.UncompressedSize64, uint64(0), "dmesg log is empty") } } for _, name := range []string{ "dmesg.log", "service-logs/apid.log", "service-logs/apid.state", "service-logs/machined.log", "service-logs/machined.state", "service-logs/kubelet.log", "service-logs/kubelet.state", "resources/kernelparamstatuses.runtime.talos.dev.yaml", "controller-runtime.log", "mounts", "processes", "io", "summary", } { n := fmt.Sprintf("%s/%s", node, name) suite.Require().Contains(files, n, "File %s doesn't exist in the support bundle", n) } for _, name := range []string{ "kubernetes-logs/kube-system/kube-apiserver-", } { n := fmt.Sprintf("%s/%s", node, name) found := false for fileName := range files { if strings.HasPrefix(fileName, n) { found = true break } } suite.Require().True(found, "File with prefix %s doesn't exist in the support bundle", n) } for _, name := range []string{ "kubernetesResources/nodes.yaml", "kubernetesResources/systemPods.yaml", } { suite.Require().Contains(files, name, "File %s doesn't exist in the support bundle", name) } } func init() { allSuites = append(allSuites, new(SupportSuite)) } ================================================ FILE: internal/integration/cli/testdata/inject/talosconfig-expected.yaml ================================================ apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: donottouch spec: roles: - os:reader - os:admin --- apiVersion: v1 kind: Pod metadata: name: test1 spec: containers: - env: - name: TEST value: test image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets initContainers: - image: busybox name: init1 resources: {} volumeMounts: - mountPath: /tmp/hello name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: busybox name: init2 resources: {} volumeMounts: - mountPath: /tmp/hello name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - emptyDir: {} name: vol1 - name: talos-secrets secret: secretName: test1-talos-secrets status: {} --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: test1-talos-secrets spec: roles: - os:reader - os:admin --- apiVersion: apps/v1 kind: Deployment metadata: name: test1 spec: selector: matchLabels: app: test strategy: {} template: metadata: labels: app: test spec: containers: - image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - emptyDir: {} name: vol1 - name: talos-secrets secret: secretName: test1-talos-secrets status: {} --- apiVersion: apps/v1 kind: DaemonSet metadata: name: test2 spec: selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - emptyDir: {} name: vol1 - name: talos-secrets secret: secretName: test2-talos-secrets updateStrategy: {} status: currentNumberScheduled: 0 desiredNumberScheduled: 0 numberMisscheduled: 0 numberReady: 0 --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: test2-talos-secrets spec: roles: - os:reader - os:admin --- apiVersion: apps/v1 kind: StatefulSet metadata: name: test3 spec: selector: matchLabels: app: test serviceName: test template: metadata: labels: app: test spec: containers: - image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - emptyDir: {} name: vol1 - name: talos-secrets secret: secretName: test3-talos-secrets updateStrategy: {} status: availableReplicas: 0 replicas: 0 --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: test3-talos-secrets spec: roles: - os:reader - os:admin --- apiVersion: batch/v1 kind: CronJob metadata: name: test4 namespace: testns spec: jobTemplate: metadata: labels: app: test spec: template: metadata: labels: app: test spec: containers: - image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - name: talos-secrets secret: secretName: test4-talos-secrets schedule: '*/1 * * * *' status: {} --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: test4-talos-secrets namespace: testns spec: roles: - os:reader - os:admin --- apiVersion: batch/v1 kind: Job metadata: name: test5 namespace: testns2 spec: template: metadata: {} spec: containers: - image: alpine:3 name: container1 resources: {} volumeMounts: - mountPath: /mnt/vol1 name: vol1 - mountPath: /var/run/secrets/talos.dev name: talos-secrets - image: alpine:3 name: container2 resources: {} volumeMounts: - mountPath: /var/run/secrets/talos.dev name: talos-secrets volumes: - name: talos-secrets secret: secretName: test5-talos-secrets status: {} --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: test5-talos-secrets namespace: testns2 spec: roles: - os:reader - os:admin --- ================================================ FILE: internal/integration/cli/testdata/inject/talosconfig-input.yaml ================================================ --- apiVersion: talos.dev/v1alpha1 kind: ServiceAccount metadata: name: donottouch spec: roles: - os:reader - os:admin --- apiVersion: v1 kind: Pod metadata: name: test1 spec: volumes: - name: vol1 emptyDir: {} initContainers: - name: init1 image: busybox volumeMounts: - name: vol1 mountPath: /tmp/hello - name: init2 image: busybox volumeMounts: - name: vol1 mountPath: /tmp/hello containers: - name: container1 image: alpine:3 env: - name: TEST value: test volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 --- apiVersion: apps/v1 kind: Deployment metadata: name: test1 spec: selector: matchLabels: app: test template: metadata: labels: app: test spec: volumes: - name: vol1 emptyDir: {} containers: - name: container1 image: alpine:3 volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 --- apiVersion: apps/v1 kind: DaemonSet metadata: name: test2 spec: selector: matchLabels: app: test template: metadata: labels: app: test spec: volumes: - name: vol1 emptyDir: {} containers: - name: container1 image: alpine:3 volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 --- apiVersion: apps/v1 kind: StatefulSet metadata: name: test3 spec: serviceName: test selector: matchLabels: app: test template: metadata: labels: app: test spec: volumes: - name: vol1 emptyDir: {} containers: - name: container1 image: alpine:3 volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 --- apiVersion: batch/v1 kind: CronJob metadata: name: test4 namespace: testns spec: schedule: "*/1 * * * *" jobTemplate: metadata: labels: app: test spec: template: metadata: labels: app: test spec: containers: - name: container1 image: alpine:3 volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 --- apiVersion: batch/v1 kind: Job metadata: name: test5 namespace: testns2 spec: template: spec: containers: - name: container1 image: alpine:3 volumeMounts: - name: vol1 mountPath: /mnt/vol1 - name: container2 image: alpine:3 ================================================ FILE: internal/integration/cli/testdata/patches/delete-dummy-ap.yaml ================================================ --- apiVersion: v1alpha1 kind: DummyLinkConfig name: dummy-ap-patch $patch: delete ================================================ FILE: internal/integration/cli/testdata/patches/dummy-ap.yaml ================================================ --- apiVersion: v1alpha1 kind: DummyLinkConfig name: dummy-ap-patch ================================================ FILE: internal/integration/cli/testdata/pki/ca.crt ================================================ -----BEGIN CERTIFICATE----- MIIC/jCCAeagAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl cm5ldGVzMB4XDTIyMDcwODExMTM0OFoXDTMyMDcwNTExMTM0OFowFTETMBEGA1UE AxMKa3ViZXJuZXRlczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGI T8ncEbVJ7LtRTZY6Vc2bXlMtagzCqpP+29H7HtsgV4T64QJsPRnfCh0PzOit7JJq SRj526wHZRfSvu0M9wZ2KaC4MVDMP2KhBUKW63nUmdXQMf+z1gHKzCMloAMa0Avb DVsoc9NaiiHX8m59gX328xEHQjNxnNGIretBjsjZw/Xeo2BflVXahnnxMVJqnzEa 0jpnGh5BaO4aPKDrJbyELz8Y8F+NGJ2zkSVtBh0gYZrejnioEiSFkSvcxDw3Xhg2 QL+NUsRrhYHq10apJhuSkPkraCogbHlN33dslJ+I85xszKt437gGkpDoTKXaxe9L f9xB40PgToK9QfAOIFMCAwEAAaNZMFcwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFF06xQ3JTko0LcX5pvAdp+mLFWgMMBUGA1UdEQQO MAyCCmt1YmVybmV0ZXMwDQYJKoZIhvcNAQELBQADggEBAIkhsj6yVvEoN4q7nj97 vY0RpAOyysmhigHK0miioKsd94GDb+aMBYFLKliU48B5/n/KXblu7xsTane8uB3C VeBywkDXLN2a9ax4BaxIkleDOX1xZN4BtxIfdU1QGhFQU0JPDCMxbDjbfN2Kg2Wi iESrsXYKDq2pLNeQdszxPGNlAOjssVHY6IivWOcMRHP0yCDTl5ooq180+U7smFdz NM/6udMOhsgh6bUCeMu9mhaPXMBmK0Lcd68PFunAA8q7a5OfTgIhGC9n7Q0L6CMw 7yXb97bd9bPZqeiuw7G7+UiNkJrBdIMc0AYE+wG44Uxu9usrGZZt6zLYzfnJ2vRZ qac= -----END CERTIFICATE----- ================================================ FILE: internal/integration/cli/testdata/pki/ca.key ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAsYhPydwRtUnsu1FNljpVzZteUy1qDMKqk/7b0fse2yBXhPrh Amw9Gd8KHQ/M6K3skmpJGPnbrAdlF9K+7Qz3BnYpoLgxUMw/YqEFQpbredSZ1dAx /7PWAcrMIyWgAxrQC9sNWyhz01qKIdfybn2BffbzEQdCM3Gc0Yit60GOyNnD9d6j YF+VVdqGefExUmqfMRrSOmcaHkFo7ho8oOslvIQvPxjwX40YnbORJW0GHSBhmt6O eKgSJIWRK9zEPDdeGDZAv41SxGuFgerXRqkmG5KQ+StoKiBseU3fd2yUn4jznGzM q3jfuAaSkOhMpdrF70t/3EHjQ+BOgr1B8A4gUwIDAQABAoIBAQCIvqY2pfw916Mw 5X8NqAFPTc1p5CE7kvYw6K4JH5S01ESVeWi3pQerVdFEcVc0IkOGw7dqNYqvB0Mn Bn1pugLMR1fpI/dYdPqdzclvcTAPt2KG/saEXtEIsFxs9h46RfzaJPA0twQAWEzt pJhn4uRLUlwHUb/8QBa6jrzn6KdCrLC0jc0g8c77x3omkfy0chug7q2s/yX2MwEX p5pbyPiGZkNIjuDeic3D0ssrH7v7gEYBdybgl78LrLkKA8gEFLrGvrT5nuLYhxkF dhRu3+p9mhhKmJQndto8Y4WeYhXZ7qN2YZbfcv1Xni6cq4UhLmr2czRaK0KVZ15H kzYh0I8BAoGBAOTjpoVVc+IgVJnGeglrJP8zOKEiwj3R+eF9DiQuTdnop0SBiqYB JT6/b/apdYEGz2nBs3M1k6CmNrkzGADMNins7m1MRHkljRqu1IHSF3Us2yifPUef lEUHtXf3ANvf3YJWuGO7RT4sird/1vXE/0TCzjWEvkHc6Zvky/DCydTJAoGBAMaP bWsiDSFpLV6iCiq18bmJJoUDnkXqnwsUVw9C978h2Hx5ysYfOUt6S6Q7UsR7hKW5 iay1aiFPBjLQyD4Ac8feZFQEQ5gFYXPEfIV3OYZDz2U+gAZp33ow7/H/73kngelV 6EXhTYgDRUwfTBOMSlzhCZwQb8Rzpv+gC3TFsmY7AoGAWewB2LIYo8bV1c/+08Jv N39VCSERtJ3QkMDDlH1Igop/ZE+MO+mJS1yETSCIFFerlr3NlT6AMAX8y8eB75ZK 1S/K/8+NuxaAl/IFdLcoFhW4R/4/YesUogYESgwVH0yUxobxS+Ufr+xp1ut3dPie 3NG3l5j98fwrHt7FLGIqTtkCgYEAjezfDQCd2g/PuiCgm77JNRDvU4wuiVMWs1iq keIQK7IJh4+WfN68mVKk1pMAqiiPu9VOrwBNB9nwWEoblxXDrE0t8U/K8NKHwbPk PZHmsC2wBHIUGIF8l157Y8LIbRTsKtiY2bodLOcJlUuZmS9hx9migMbO3OC9sWG4 TpMw3RkCgYA8ssuhj5wrSQabCXbOU0pn/qbePHWfvh+DcRUXnTtRoothpqvDQxuS 73j2NB42UG7j0eWRK993ONGTJ/QNMAJxXwLEuLFtoKHdh7pkq3uFolyNzR+T0vna awkSRmJsbuV7C4mDVHgEaqcEzv/VyeAokw/hqB1Ga9fvauj6MDPshw== -----END RSA PRIVATE KEY----- ================================================ FILE: internal/integration/cli/testdata/pki/etcd/ca.crt ================================================ -----BEGIN CERTIFICATE----- MIIC9TCCAd2gAwIBAgIBADANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDEwdldGNk LWNhMB4XDTIyMDcwODExMTM0OFoXDTMyMDcwNTExMTM0OFowEjEQMA4GA1UEAxMH ZXRjZC1jYTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOgw+tA7odeO H5gKPlhWBjijuNoLlMCJSkGG0Ca/fI6Bk2Y+fzuVv8NWsRrhML3dEWJgXccMhoG0 v9MatmvF5nYfm802ZaDCsA6RdJG3PygRTw69pmC4ETfv0+YXpFFfFBeWWb9MsXIP ULooepYy4bWvjG7ZDCeFQ8ACHGE17U0O5rFBqiY0okBjSkl1/oHSzlAVCxa4dOaK /Vj8A8CefT18JZ+jzAbgN7jRm1GWzYTWqACVa5CnRDYRglrZr9DLvaj0ASqck5DD zdJYVQJeVqS7L8qghXfbXzxlkKesRtQsj7V2trpDBuDASJPk6ZmwEWlaI0Wdc3eK nYHgaWybLykCAwEAAaNWMFQwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMB Af8wHQYDVR0OBBYEFKN5e0IuTpOoTqWB9+MQp/k/OgXgMBIGA1UdEQQLMAmCB2V0 Y2QtY2EwDQYJKoZIhvcNAQELBQADggEBAGmGUUqeGe4RXd3kZGTRcdJMo7OkuEK5 hH/FHfrLLc3VNoWQafdq4oVrF9bwqDUPRuPgrXl+QY+s4/ztDyHmuKGdLWIT2dCE 0Ztnpe17VoMrkJxmFvKEWrT74EnT0QIeFs+lf8+SJWTLYKBRpGrsQKq84uds++gV BPLuu8Z0e5vvkAFnX8m65SqyZwKfs3HuzaAnA57VGSJHCBrnKojsP8JdlWeQiStG CtmzePeLqjVJnpNF/n1ST5Ewr4Kq/Cvf707gzs+spn0bt97QyNke6b24ZrkpUNu6 T/0bImLeYiGmSanRF3hAdN7IV0ah7tYOpmwk560kyF8tmd8jcKFgEd8= -----END CERTIFICATE----- ================================================ FILE: internal/integration/cli/testdata/pki/etcd/ca.key ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA6DD60Duh144fmAo+WFYGOKO42guUwIlKQYbQJr98joGTZj5/ O5W/w1axGuEwvd0RYmBdxwyGgbS/0xq2a8Xmdh+bzTZloMKwDpF0kbc/KBFPDr2m YLgRN+/T5hekUV8UF5ZZv0yxcg9Quih6ljLhta+MbtkMJ4VDwAIcYTXtTQ7msUGq JjSiQGNKSXX+gdLOUBULFrh05or9WPwDwJ59PXwln6PMBuA3uNGbUZbNhNaoAJVr kKdENhGCWtmv0Mu9qPQBKpyTkMPN0lhVAl5WpLsvyqCFd9tfPGWQp6xG1CyPtXa2 ukMG4MBIk+TpmbARaVojRZ1zd4qdgeBpbJsvKQIDAQABAoIBAHI8xun0rOfU8Q5o 28uyZ1UumCAPWpxv76zVm0u1Ip8qeU7wqMC0KKj+2hwTd1uyjH8OUpVAQF1IhKhk mCPmNkEfxBPvE4lIwD4qqmOW+OfJvE/QVy924GHZCTRHpXyzfrssKfPI0/T+PAWb LNUBK7OsLzfKagR3uKGbaEMbuSkTn5zeCJ1vrFAuCaLmeauBsnoV7Z9AlbG6t8sp hyD0O+pDPGuc+J99suGiysndwjczqntIKXcdgba2XxRE0QHM+ZYDPpyvCggKfoUH sQ9Vz/UHpqTv8pbrTSZK59E8+16YfFyFC+iwTnTzoBAkO6NIivDTQvLcipweZEzt 5146EEECgYEA6O+Pd1tdkTVHLgSkKrkBo4p3g2iBFl626g7vlZGvDjVNNPBWwk5f j3PbMKPfUfr96YB86lbetDzYdiB5LrLW5xG2GaSRziGI1/Tgdp6aR142TNljiEu7 dv4v3eAR5VfiA5CGera+k1VzKLIygfCDkUWWdG17LvVdHFtjN6XVU2UCgYEA/y6M hZbZqCX9cx58rhJ38YvH9fdWSzw1r9crgM40uPHE4SHvin6/lR/rbNU4Geg/b4bn FWnffCem8ov2tXcFe/ZBM8ZWrsRGDinkTh8dReh8ujE90YOqf3YSRYgUvSx8ITUn zqhe43sGVAelKeVKPW/3Ok7RJXnRRsDIbSmfqnUCgYB7sDmON4XHxXK2jOBfjz2/ iZdMwAFLz59xSd0Onv1FnigRJE3tf5BerDaH7Xx4G78YbpHmHZrEOkr27udqVKyo pk777tc9jbEMe4t1cWKa4vwScpzXkt9IoFDqkEDwd2ocWnIOV1t7ALTVt0n6laxH R5xM1pXCqad3l09oDTbpwQKBgDlHwqVOCkeTV4QayNPuM1xWCymsPoOe3VI+U3aT UwRcyNvcWT/WWbzosFj6t6AhIPQw7PhCjrb406HIRzXOpL2Btnsfv191kWAmiSf8 Ff8WQ8ErwnugOYpo/4r6E+Wu8aImo2vhIYOgnvgHy0xPOs31ryI4hPwLjy15osPW Pw/tAoGBAKaT53bqIl+wzltM2ROtH9ppA98prDPM0GyZMhFed+XMLAkUX3qmFtj5 k51bhaq7ER4yByHX17Q7y3SoJ5NpccmFUhWhttRf/IraWEKsHXW6OY22wvtZN9Lx Eh+rpZsjcozUCaoQWkOf20UpBSltIV5U4GmSpthnj0+zaRrorxWx -----END RSA PRIVATE KEY----- ================================================ FILE: internal/integration/cli/testdata/pki/front-proxy-ca.crt ================================================ -----BEGIN CERTIFICATE----- MIIDCjCCAfKgAwIBAgIBADANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5mcm9u dC1wcm94eS1jYTAeFw0yMjA3MDgxMTEzNDhaFw0zMjA3MDUxMTEzNDhaMBkxFzAV BgNVBAMTDmZyb250LXByb3h5LWNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEA4thL1utf/iXxylt24O3o3IV7oif5tiYR4SN9YnVb/GLyVtVdHuzPpigw j+S7aw3mldNHVWbIPq9GncvVsgvlJYHu53WRzVfV7tAylt/ssSqHFv/p1e6rC2m9 hETHUZjSs8v0APWi8pfh15lJPqLS1lGF3QPVCPdc1t+8dBVnx8wnTgGVE7FVLjmc 3qIcXTR1TfcB4+X1ZnY5FNK7kR/kgIS64uZqntyNSW5W0SCFgWQClwwkzVpo4v/Y sCHiWI7cIuXHTTd7ntHJab1Og1jAPNEmENvEtza1SqGTSEUQs5wjS+p+ii5E09b3 Nufj1e8hJaodTxr+24hoFcYxWIgxhwIDAQABo10wWzAOBgNVHQ8BAf8EBAMCAqQw DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUvw91sUxAgIX7zdqTKwJ0Sdxa9TUw GQYDVR0RBBIwEIIOZnJvbnQtcHJveHktY2EwDQYJKoZIhvcNAQELBQADggEBAGHW snsEJkN3d2vNJuFu6IVKurEZM8UbwHebJ5fQUHpCOKpFqSMdngF8rdcYNmHeUFMv Qf9Fkm9nwKOhO9hApsQad7UgJ98YCzE+heTye7nKjrga+2lsJN/T3SgJGiAEkVjP rq2SQ4MLCp56PyFI5CL9zqtjuXCI8Uhqqfru6tJEA75GA6VJZmfiOgzFQum7DeC2 9dNcvQb8p91LIl5tjvuTgPbJDjF1YX4n0iwoiA05e+rPrsPhyySQnJwFgTi+hqPZ cBUPwVDYeiHseRbj3ODOoCv6PO4koO5m+tiFeUXJTynMbCUkyJqZ11TAYC6snHxi mg+fblveomK3F3hLFdk= -----END CERTIFICATE----- ================================================ FILE: internal/integration/cli/testdata/pki/front-proxy-ca.key ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA4thL1utf/iXxylt24O3o3IV7oif5tiYR4SN9YnVb/GLyVtVd HuzPpigwj+S7aw3mldNHVWbIPq9GncvVsgvlJYHu53WRzVfV7tAylt/ssSqHFv/p 1e6rC2m9hETHUZjSs8v0APWi8pfh15lJPqLS1lGF3QPVCPdc1t+8dBVnx8wnTgGV E7FVLjmc3qIcXTR1TfcB4+X1ZnY5FNK7kR/kgIS64uZqntyNSW5W0SCFgWQClwwk zVpo4v/YsCHiWI7cIuXHTTd7ntHJab1Og1jAPNEmENvEtza1SqGTSEUQs5wjS+p+ ii5E09b3Nufj1e8hJaodTxr+24hoFcYxWIgxhwIDAQABAoIBAQDa55WQCcWxkNZa y5bVimBbZeif2+nKj8RTOZdGyzAAR0/K4c0iCa58jm4GfdkqftiUnrVIwY3dh/Ei V1CZp4byggeUjs0rlmaZNYqMM/zKHtsMI9t4mf+vXNQI7wJVSJ+T5+5IesJLTqwf DQo0ipXhQfxnAsqzA1ow9Ol8MCfdEeFL5SFaVNiNhany/7PR0letngwMIAje6arY 6dPzjPYDsCjMTjoNarUADIZeVxMdbOCmiMy+yGDZlgV80fK0+Fw81axot2KOufhy CvqClUlJV5Csz2egHuThuVd39kX2vAAGXdPEUbXga1JTDB3cQiHXJo0hLUsUGniS 6KdsJOyRAoGBAOmNX/V7awLiVyWsRLTNr1IeoMwHeS3Wdt5/nedqh6p2fLpfbN1D M8R29yqvNn1YmpZsDBhQlnddq7i7OEKjSODwNnLPys6fqalsIY/AIRJPOmHvVJl6 er4scaiGXA/FztvxGGQh4W92+Nb69dCXXXcG1QUljr+uGwrIGXc8aqzpAoGBAPil 4qGf/0s3Fv0tyLj+ZRWVjg5o4s/IsJyGRFc6RplU65y9VL9WsYvi5+d06w7lovac omycJ/z+HiqTLcixdcHse54N9HHO5Ekui6cRbGAOdlzgS84TxYrmQK/FQkFvlcmO rsfkDMmAgBc2yGP3N5IL60u90N2Kf3OiT5aHoCTvAoGAamdwioS6EkxQa+d6Pe1f rMgrdgkJmmqVKXV22VHdkTn+RWLoVD4jvaR9o0LEToMpmtKLCCDfDG7up3EUhreh ommOROyKd2yifX+4Iqfj6VWTQb8qCeqVNUNGXQMpuj3iqq3C8QvGi2PmpvsbNvdf K7U/I+MikA2gYF8dywcJitECgYArDe5UNjQqffuJE2hyP/qY5jCW5ip/+Cw8rjMf N4QKAN5bYZ1PFF/h7QRi26foCHNTaIPncpKqCAaJMLr4yWGulphBIgF1w3FcCqc7 4pR1fYuZQW1e3aWTC5Of2/RBCGVTZVV2X1KngYyseFvyk1gX/eBcWR3VfqnbB/vo AMwGGQKBgB/MXrB2Z61WlxR0Y289k+5gjUSS9foZIkCjr1TFDvLbEStAW8Z3WFcS Gxzjs5s45VO3+KRRd3591WHaPWcVdNze+y5ICUr4u3WSBKg8D+9H3cMKWq8QLCRP bgMDGq8TzTLdGmb9vw3n5OG2aAQUQfwoAp+uUwh2EDMDYixYd2UJ -----END RSA PRIVATE KEY----- ================================================ FILE: internal/integration/cli/testdata/pki/sa.key ================================================ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0H28tj4Zgoj5y+/NF7Dhrf96+TQUolLR5j4YqoxbFRvgWtPE 0JFueoC0n6v4l58z4vog+1JjPHMO8rr0W3q9o/wWaY1CL3XqZJIkHI/fE/Eu1VLv gLfDHsPevPNLczcpwXo7AadqxiUC/HRYZBJY+LA2OzWwO5ylx4YKnOzKxBXQ7a2q qdpwZJSTTmGKyRx5pD6V5mCr47ITVAXqcjorTtJmG3zCFRhUjMscXIWC04S0IY/C +cfMKaK4Jbqj1h2/9uNhbb55r9HdCEV/TLw+QL1Q+S6MWtng1S4Q3hyDXzB1LqfP 1brz7xl1pSLh+l0vF2pXom5zYiHlX4cAddR7MQIDAQABAoIBAAQtULeR/O7Zka+d SU2dNJhI0wzlFzi9Ugk720CneTeuDEuljH7lOwJnS7cbOerHvMFiY4DFgMl4QKdq SXT/u4bqiQRqWRYcVarYJrMPytdacKbDd5rrk5QtNmwwr6VKSKLgsQfyc7gui6XF KvQuTewFk8CR7crz83pQ3CuSrulIwVny17HSE9NlXlv8SlHy7E2/a6H0UeOm7BRn 13An62CfRuBVFRd96oyTMpuNf2l+fcL5nnfM/F+tdr5MRR0pnEW2UKGdCbkTkAKd AJnH2mmhWwbA8Gcx0Zn1X9/zu8Z3S6hTGnQmWIP25EcgipqKe/gocA9ervyR+0nb FgwZZXECgYEA6D/mx3dI6AUY7RVIY9GexiJWLtS9O1d7PddmajoqVpwtjXT9jUf/ 9BAAVWNKcIEs1t4foncJeYph6JnTrQjDz1LORC2kAjPUYhDQod/m6dqHxvjRAIns v0NezDvWQ2v8CgklDjUc5hYtvqj51OkkbKpphRClAdJZvrlxBhXTT1UCgYEA5c/c A6D3xIcGUrSVrjtLWUyJ9hszjjBrI5txtmwMzWDSkV3Hp1VhSfhCdIxqQxELxlxS A8SOU0XKxRB7QiwpgHfnbGxtQ8tJ5JufIecwJkqRcD5fqQk69xva/76fNfes41L8 doNpFiu0fXbVdcB8UNN61VYsz31D/JD0qA9k5G0CgYEA5gq1egkrC7ZQ1DRqeYSd 8b79AnHx5Z9nEQAUD1ABs7wKWrzwkEoqugJHckxg5ULtuP5W80NY/SwWgqArTI8L 9IUejeVvOEdCLMhe/peaTzQHnQvDaPc0qtX+RelW9300LnSUYZg2QajiMqGIpF0x mPjKf+TWrBFAl2tzCgYAQekCgYEAukqjaXWlI/To1UZ6R8DdNchr1cr7Ifpx/21U 4rH4NsyUJS7GWAlIUnQjOuNQiIla6DOScGd3kF11IAZaRKwUAIYyXZwPfvNeNSlJ +Gu2hnPQLhMB7L8Ew6gbAVH/MfpSdfyhl1izaTuIlmQsacXdgI/OdP3kWVaMNEM1 cL755IkCgYAv10G49Mc57WBi6knRHM2cXbYVxjzedJf8zBnAYkQoLdRiYT6uj34U hI1EajSfXYJ2t8PG5nbHMNHAEbYNBikhjDvSrZW+HaTmjsVGV25cqCHPy7giSqVS VftcMlIX/MlTdfcYtNT4wHORZdO2P4wW/y5JSFNol4z3xB4TNXp55A== -----END RSA PRIVATE KEY----- ================================================ FILE: internal/integration/cli/time.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/internal/integration/base" ) // TimeSuite verifies dmesg command. type TimeSuite struct { base.CLISuite } // SuiteName ... func (suite *TimeSuite) SuiteName() string { return "cli.TimeSuite" } // TestDefault runs default time check. func (suite *TimeSuite) TestDefault() { suite.RunCLI([]string{"time", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`NTP-SERVER`)), base.StdoutShouldMatch(regexp.MustCompile(`UTC`)), base.WithRetry(retry.Constant(time.Minute, retry.WithUnits(time.Second))), ) } func init() { allSuites = append(allSuites, new(TimeSuite)) } ================================================ FILE: internal/integration/cli/validate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "fmt" "os" "github.com/siderolabs/talos/internal/integration/base" ) // ValidateSuite verifies dmesg command. type ValidateSuite struct { base.CLISuite tmpDir string savedCwd string } // SuiteName ... func (suite *ValidateSuite) SuiteName() string { return "cli.ValidateSuite" } // SetupTest ... func (suite *ValidateSuite) SetupTest() { suite.tmpDir = suite.T().TempDir() var err error suite.savedCwd, err = os.Getwd() suite.Require().NoError(err) suite.Require().NoError(os.Chdir(suite.tmpDir)) } // TearDownTest ... func (suite *ValidateSuite) TearDownTest() { if suite.savedCwd != "" { suite.Require().NoError(os.Chdir(suite.savedCwd)) } } // TestValidate generates config and validates it for all the modes. func (suite *ValidateSuite) TestValidate() { suite.RunCLI([]string{"gen", "config", "foobar", "https://10.0.0.1"}, base.StdoutEmpty(), base.StderrNotEmpty(), ) for _, configFile := range []string{"controlplane.yaml", "worker.yaml"} { for _, mode := range []string{"cloud", "metal"} { suite.Run(fmt.Sprintf("%s-%s", configFile, mode), func() { suite.RunCLI([]string{"validate", "-m", mode, "-c", configFile, "--strict"}) }) } } } func init() { allSuites = append(allSuites, new(ValidateSuite)) } ================================================ FILE: internal/integration/cli/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_cli package cli import ( "regexp" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/version" ) // VersionSuite verifies version command. type VersionSuite struct { base.CLISuite } // SuiteName ... func (suite *VersionSuite) SuiteName() string { return "cli.VersionSuite" } // TestExpectedVersionMaster verifies master node version matches expected. func (suite *VersionSuite) TestExpectedVersionMaster() { suite.RunCLI([]string{"version", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(`Client:\n\s*Tag:\s*`+regexp.QuoteMeta(suite.Version))), base.StdoutShouldMatch(regexp.MustCompile(`Server:\n(\s*NODE:[^\n]+\n)?\s*Tag:\s*`+regexp.QuoteMeta(suite.Version))), ) } // TestShortVersion verifies short version output. func (suite *VersionSuite) TestShortVersion() { suite.RunCLI([]string{"version", "--short", "--nodes", suite.RandomDiscoveredNodeInternalIP()}, base.StdoutShouldMatch(regexp.MustCompile(regexp.QuoteMeta(version.Name)+`\s*`+regexp.QuoteMeta(suite.Version))), ) } // TestClient verifies only client version output. func (suite *VersionSuite) TestClient() { suite.RunCLI([]string{"version", "--client"}, base.StdoutShouldMatch(regexp.MustCompile(`Client:\n\s*Tag:\s*`+regexp.QuoteMeta(suite.Version))), base.StdoutShouldNotMatch(regexp.MustCompile(`Server`)), ) } func init() { allSuites = append(allSuites, new(VersionSuite)) } ================================================ FILE: internal/integration/integration_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package integration_test contains core runners for integration tests package integration_test import ( "flag" "fmt" "path/filepath" "slices" "testing" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/integration/api" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/internal/integration/cli" "github.com/siderolabs/talos/internal/integration/k8s" provision_test "github.com/siderolabs/talos/internal/integration/provision" "github.com/siderolabs/talos/pkg/images" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers" ) // Accumulated list of all the suites to run. var allSuites []suite.TestingSuite // Flag values. var ( failFast bool trustedBoot bool selinuxEnforcing bool extensionsQEMU bool extensionsNvidia bool verifyUKIBooted bool airgapped bool virtiofsd bool race bool talosConfig string endpoint string k8sEndpoint string expectedVersion string expectedGoVersion string talosctlPath string kubectlPath string helmPath string kubeStrPath string provisionerName string clusterName string stateDir string talosImage string csiTestName string csiTestTimeout string ) // TestIntegration ... // //nolint:gocyclo func TestIntegration(t *testing.T) { if talosConfig == "" { t.Error("--talos.config is not provided") } var ( cluster provision.Cluster provisioner provision.Provisioner err error ) if provisionerName != "" { // use provisioned cluster state as discovery source ctx := t.Context() provisioner, err = providers.Factory(ctx, provisionerName) if err != nil { t.Error("error initializing provisioner", err) } defer provisioner.Close() //nolint:errcheck cluster, err = provisioner.Reflect(ctx, clusterName, stateDir) if err != nil { t.Error("error reflecting cluster via provisioner", err) } if k8sEndpoint == "" && provisionerName == base.ProvisionerDocker { k8sEndpoint = cluster.Info().KubernetesEndpoint } } provision_test.DefaultSettings.CurrentVersion = expectedVersion for _, s := range allSuites { if configuredSuite, ok := s.(base.ConfiguredSuite); ok { configuredSuite.SetConfig(base.TalosSuite{ Endpoint: endpoint, K8sEndpoint: k8sEndpoint, Cluster: cluster, TalosConfig: talosConfig, Version: expectedVersion, GoVersion: expectedGoVersion, TalosctlPath: talosctlPath, KubectlPath: kubectlPath, HelmPath: helmPath, KubeStrPath: kubeStrPath, ExtensionsQEMU: extensionsQEMU, ExtensionsNvidia: extensionsNvidia, TrustedBoot: trustedBoot, SelinuxEnforcing: selinuxEnforcing, VerifyUKIBooted: verifyUKIBooted, TalosImage: talosImage, CSITestName: csiTestName, CSITestTimeout: csiTestTimeout, Airgapped: airgapped, Virtiofsd: virtiofsd, Race: race, }) } var suiteName string if namedSuite, ok := s.(base.NamedSuite); ok { suiteName = namedSuite.SuiteName() } t.Run(suiteName, func(tt *testing.T) { suite.Run(tt, s) //nolint:scopelint }) if failFast && t.Failed() { t.Log("fastfail mode enabled, aborting on first failure") break } } } func init() { defaultTalosConfigs, _ := clientconfig.GetDefaultPaths() //nolint:errcheck defaultStateDir, err := clientconfig.GetTalosDirectory() if err == nil { defaultStateDir = filepath.Join(defaultStateDir, "clusters") } flag.BoolVar(&failFast, "talos.failfast", false, "fail the test run on the first failed test") flag.BoolVar(&trustedBoot, "talos.trustedboot", false, "enable tests for trusted boot mode") flag.BoolVar(&selinuxEnforcing, "talos.enforcing", false, "enable tests for SELinux enforcing mode") flag.BoolVar(&extensionsQEMU, "talos.extensions.qemu", false, "enable tests for qemu extensions") flag.BoolVar(&extensionsNvidia, "talos.extensions.nvidia", false, "enable tests for nvidia extensions") flag.BoolVar(&race, "talos.race", false, "skip tests that are incompatible with race detector") flag.BoolVar(&verifyUKIBooted, "talos.verifyukibooted", true, "enable tests for verifying that Talos was booted using a UKI") flag.StringVar( &talosConfig, "talos.config", defaultTalosConfigs[0].Path, fmt.Sprintf("The path to the Talos configuration file. Defaults to '%s' env variable if set, otherwise '%s' and '%s' in order.", constants.TalosConfigEnvVar, filepath.Join("$HOME", constants.TalosDir, constants.TalosconfigFilename), filepath.Join(constants.ServiceAccountMountPath, constants.TalosconfigFilename), ), ) flag.StringVar(&endpoint, "talos.endpoint", "", "endpoint to use (overrides config)") flag.StringVar(&k8sEndpoint, "talos.k8sendpoint", "", "Kubernetes endpoint to use (overrides kubeconfig)") flag.StringVar(&provisionerName, "talos.provisioner", "", "Talos cluster provisioner to use, if not set cluster state is disabled") flag.StringVar(&stateDir, "talos.state", defaultStateDir, "directory path to store cluster state") flag.StringVar(&clusterName, "talos.name", "talos-default", "the name of the cluster") flag.StringVar(&expectedVersion, "talos.version", version.Tag, "expected Talos version") flag.StringVar(&expectedGoVersion, "talos.go.version", constants.GoVersion, "expected Talos version") flag.StringVar(&talosctlPath, "talos.talosctlpath", "talosctl", "The path to 'talosctl' binary") flag.StringVar(&kubectlPath, "talos.kubectlpath", "kubectl", "The path to 'kubectl' binary") flag.StringVar(&helmPath, "talos.helmpath", "helm", "The path to 'helm' binary") flag.StringVar(&kubeStrPath, "talos.kubestrpath", "kubestr", "The path to 'kubestr' binary") flag.StringVar(&talosImage, "talos.image", images.DefaultTalosImageRepository, "The default 'talos' container image") flag.StringVar(&csiTestName, "talos.csi", "", "CSI test to run") flag.StringVar(&csiTestTimeout, "talos.csi.timeout", "15m", "CSI test timeout") flag.BoolVar(&airgapped, "talos.airgapped", false, "Marker to skip tests that should not be run on airgapped talos cluster") flag.BoolVar(&virtiofsd, "talos.virtiofsd", false, "Marker to skip tests that should not be run without virtiofsd") flag.StringVar(&provision_test.DefaultSettings.CIDR, "talos.provision.cidr", provision_test.DefaultSettings.CIDR, "CIDR to use to provision clusters (provision tests only)") flag.Var(&provision_test.DefaultSettings.RegistryMirrors, "talos.provision.registry-mirror", "registry mirrors to use (provision tests only)") flag.IntVar(&provision_test.DefaultSettings.MTU, "talos.provision.mtu", provision_test.DefaultSettings.MTU, "MTU to use for cluster network (provision tests only)") flag.Int64Var(&provision_test.DefaultSettings.CPUs, "talos.provision.cpu", provision_test.DefaultSettings.CPUs, "CPU count for each VM (provision tests only)") flag.Int64Var(&provision_test.DefaultSettings.MemMB, "talos.provision.mem", provision_test.DefaultSettings.MemMB, "memory (in MiB) for each VM (provision tests only)") flag.Uint64Var(&provision_test.DefaultSettings.DiskGB, "talos.provision.disk", provision_test.DefaultSettings.DiskGB, "disk size (in GiB) for each VM (provision tests only)") flag.IntVar(&provision_test.DefaultSettings.ControlplaneNodes, "talos.provision.controlplanes", provision_test.DefaultSettings.ControlplaneNodes, "controlplane node count (provision tests only)") flag.IntVar(&provision_test.DefaultSettings.WorkerNodes, "talos.provision.workers", provision_test.DefaultSettings.WorkerNodes, "worker node count (provision tests only)") flag.StringVar(&provision_test.DefaultSettings.TargetInstallImageRegistry, "talos.provision.target-installer-registry", provision_test.DefaultSettings.TargetInstallImageRegistry, "image registry for target installer image (provision tests only)") flag.StringVar(&provision_test.DefaultSettings.CustomCNIURL, "talos.provision.custom-cni-url", provision_test.DefaultSettings.CustomCNIURL, "custom CNI URL for the cluster (provision tests only)") flag.StringVar(&provision_test.DefaultSettings.CNIBundleURL, "talos.provision.cni-bundle-url", provision_test.DefaultSettings.CNIBundleURL, "URL to download CNI bundle from") allSuites = slices.Concat(api.GetAllSuites(), cli.GetAllSuites(), k8s.GetAllSuites(), provision_test.GetAllSuites()) } ================================================ FILE: internal/integration/k8s/apparmor.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "bytes" "context" _ "embed" "strings" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // ApparmorSuite verifies that a pod with apparmor security context with `RuntimeDefault` works. type ApparmorSuite struct { base.K8sSuite } //go:embed testdata/apparmor.yaml var apparmorPodSpec []byte // SuiteName returns the name of the suite. func (suite *ApparmorSuite) SuiteName() string { return "k8s.ApparmorSuite" } // TestApparmor verifies that a pod with apparmor security context with `RuntimeDefault` works. func (suite *ApparmorSuite) TestApparmor() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping apparmor test since provisioner is not qemu") } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) suite.T().Cleanup(cancel) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNodes(ctx, node) reader, err := suite.Client.Read(nodeCtx, "/sys/kernel/security/lsm") suite.Require().NoError(err) // read from reader into a buffer var lsm bytes.Buffer _, err = lsm.ReadFrom(reader) suite.Require().NoError(err) if !strings.Contains(lsm.String(), "apparmor") { suite.T().Skip("skipping apparmor test since apparmor is not enabled") } apparmorPodManifest := suite.ParseManifests(apparmorPodSpec) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, apparmorPodManifest) }) suite.ApplyManifests(ctx, apparmorPodManifest) suite.Require().NoError(suite.WaitForPodToBeRunning(ctx, time.Minute, "default", "nginx-apparmor")) } func init() { allSuites = append(allSuites, new(ApparmorSuite)) } ================================================ FILE: internal/integration/k8s/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package k8s provides Kubernetes integration tests. package k8s const ( // RookCephHelmChartVersion is the version of the Rook Ceph Helm chart to use. // renovate: datasource=helm versioning=helm depName=rook-ceph registryUrl=https://charts.rook.io/release RookCephHelmChartVersion = "v1.19.2" // LongHornHelmChartVersion is the version of the Longhorn Helm chart to use. // renovate: datasource=helm versioning=helm depName=longhorn registryUrl=https://charts.longhorn.io LongHornHelmChartVersion = "v1.11.0" // OpenEBSChartVersion is the version of the OpenEBS Helm chart to use. // renovate: datasource=helm versioning=helm depName=openebs registryUrl=https://openebs.github.io/openebs OpenEBSChartVersion = "v4.4.0" ) ================================================ FILE: internal/integration/k8s/k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package k8s provides Kubernetes integration tests for Talos package k8s import "github.com/stretchr/testify/suite" var allSuites []suite.TestingSuite // GetAllSuites returns all the suites for K8s test. // // Depending on build tags, this might return different lists. func GetAllSuites() []suite.TestingSuite { return allSuites } ================================================ FILE: internal/integration/k8s/longhorn.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "bytes" "context" _ "embed" "strings" "text/template" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) var ( //go:embed testdata/longhorn-iscsi-volume.yaml longHornISCSIVolumeManifest []byte //go:embed testdata/longhorn-volumeattachment.yaml longHornISCSIVolumeAttachmentManifestTemplate []byte //go:embed testdata/pod-iscsi-volume.yaml podWithISCSIVolumeTemplate []byte //go:embed testdata/longhorn-v2-engine-values.yaml longhornEngineV2Values []byte //go:embed testdata/longhorn-v2-storageclass.yaml longhornV2StorageClassManifest []byte //go:embed testdata/longhorn-v2-disk-patch.yaml longhornNodeDiskPatch []byte ) // LongHornSuite tests deploying Longhorn. type LongHornSuite struct { base.K8sSuite } // SuiteName returns the name of the suite. func (suite *LongHornSuite) SuiteName() string { return "k8s.LongHornSuite" } // TestDeploy tests deploying Longhorn and running a simple test. func (suite *LongHornSuite) TestDeploy() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if suite.CSITestName != "longhorn" { suite.T().Skip("skipping longhorn test as it is not enabled") } timeout, err := time.ParseDuration(suite.CSITestTimeout) if err != nil { suite.T().Fatalf("failed to parse timeout: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), timeout) suite.T().Cleanup(cancel) if err := suite.HelmInstall( ctx, "longhorn-system", "https://charts.longhorn.io", LongHornHelmChartVersion, "longhorn", "longhorn", longhornEngineV2Values, ); err != nil { suite.T().Fatalf("failed to install Longhorn chart: %v", err) } longhornV2StorageClassunstructured := suite.ParseManifests(longhornV2StorageClassManifest) suite.ApplyManifests(ctx, longhornV2StorageClassunstructured) suite.T().Cleanup(func() { suite.DeleteManifests(ctx, longhornV2StorageClassunstructured) }) nodes := suite.DiscoverNodeInternalIPsByType(ctx, machine.TypeWorker) suite.Require().Equal(3, len(nodes), "expected 3 worker nodes") for _, node := range nodes { k8sNode, err := suite.GetK8sNodeByInternalIP(ctx, node) suite.Require().NoError(err) suite.Require().NoError(suite.WaitForResourceToBeAvailable(ctx, 2*time.Minute, "longhorn-system", "longhorn.io", "Node", "v1beta2", k8sNode.Name)) suite.Require().NoError(suite.WaitForResource(ctx, "longhorn-system", "longhorn.io", "Node", "v1beta2", k8sNode.Name, "{.status.diskStatus.*.conditions[?(@.type==\"Ready\")].status}", "True")) suite.Require().NoError(suite.WaitForResource(ctx, "longhorn-system", "longhorn.io", "Node", "v1beta2", k8sNode.Name, "{.status.diskStatus.*.conditions[?(@.type==\"Schedulable\")].status}", "True")) suite.PatchK8sObject(ctx, "longhorn-system", "longhorn.io", "Node", "v1beta2", k8sNode.Name, longhornNodeDiskPatch) } suite.Run("fio", func() { suite.Require().NoError(suite.RunFIOTest(ctx, "longhorn", "10G")) }) suite.Run("fio-v2", func() { suite.Require().NoError(suite.RunFIOTest(ctx, "longhorn-v2", "10G")) }) suite.Run("iscsi", func() { suite.testDeployISCSI(ctx) }) } //nolint:gocyclo func (suite *LongHornSuite) testDeployISCSI(ctx context.Context) { longHornISCSIVolumeManifestUnstructured := suite.ParseManifests(longHornISCSIVolumeManifest) defer func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, longHornISCSIVolumeManifestUnstructured) }() suite.ApplyManifests(ctx, longHornISCSIVolumeManifestUnstructured) tmpl, err := template.New("longhorn-iscsi-volumeattachment").Parse(string(longHornISCSIVolumeAttachmentManifestTemplate)) suite.Require().NoError(err) var longHornISCSIVolumeAttachmentManifest bytes.Buffer node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeInfo, err := suite.GetK8sNodeByInternalIP(ctx, node) if err != nil { suite.T().Fatalf("failed to get K8s node by internal IP: %v", err) } if err := tmpl.Execute(&longHornISCSIVolumeAttachmentManifest, struct { NodeID string }{ NodeID: nodeInfo.Name, }); err != nil { suite.T().Fatalf("failed to render Longhorn ISCSI volume manifest: %v", err) } longHornISCSIVolumeAttachmentManifestUnstructured := suite.ParseManifests(longHornISCSIVolumeAttachmentManifest.Bytes()) suite.ApplyManifests(ctx, longHornISCSIVolumeAttachmentManifestUnstructured) if err := suite.WaitForResource(ctx, "longhorn-system", "longhorn.io", "Volume", "v1beta2", "iscsi", "{.status.robustness}", "healthy"); err != nil { suite.T().Fatalf("failed to wait for LongHorn Engine to be Ready: %v", err) } if err := suite.WaitForResource(ctx, "longhorn-system", "longhorn.io", "Volume", "v1beta2", "iscsi", "{.status.state}", "attached"); err != nil { suite.T().Fatalf("failed to wait for LongHorn Engine to be Ready: %v", err) } if err := suite.WaitForResource(ctx, "longhorn-system", "longhorn.io", "Engine", "v1beta2", "iscsi-e-0", "{.status.currentState}", "running"); err != nil { suite.T().Fatalf("failed to wait for LongHorn Engine to be Ready: %v", err) } unstructured, err := suite.GetUnstructuredResource(ctx, "longhorn-system", "longhorn.io", "Engine", "v1beta2", "iscsi-e-0") if err != nil { suite.T().Fatalf("failed to get LongHorn Engine resource: %v", err) } var endpointData string if status, ok := unstructured.Object["status"].(map[string]any); ok { endpointData, ok = status["endpoint"].(string) if !ok { suite.T().Fatalf("failed to get LongHorn Engine endpoint") } } tmpl, err = template.New("pod-iscsi-volume").Parse(string(podWithISCSIVolumeTemplate)) suite.Require().NoError(err) // endpoint is of the form `iscsi://10.244.0.5:3260/iqn.2019-10.io.longhorn:iscsi/1` // trim the iscsi:// prefix endpointData = strings.TrimPrefix(endpointData, "iscsi://") // trim the /1 suffix endpointData = strings.TrimSuffix(endpointData, "/1") targetPortal, IQN, ok := strings.Cut(endpointData, "/") if !ok { suite.T().Fatalf("failed to parse endpoint data from %s", endpointData) } var podWithISCSIVolume bytes.Buffer if err := tmpl.Execute(&podWithISCSIVolume, struct { NodeName string TargetPortal string IQN string }{ NodeName: nodeInfo.Name, TargetPortal: targetPortal, IQN: IQN, }); err != nil { suite.T().Fatalf("failed to render pod with ISCSI volume manifest: %v", err) } podWithISCSIVolumeUnstructured := suite.ParseManifests(podWithISCSIVolume.Bytes()) defer func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, podWithISCSIVolumeUnstructured) }() suite.ApplyManifests(ctx, podWithISCSIVolumeUnstructured) suite.Require().NoError(suite.WaitForPodToBeRunning(ctx, 3*time.Minute, "default", "iscsipd")) } func init() { allSuites = append(allSuites, new(LongHornSuite)) } ================================================ FILE: internal/integration/k8s/manifests.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "context" "testing" "time" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/stretchr/testify/assert" "go.yaml.in/yaml/v4" appsv1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/cluster/kubernetes" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/access" ) // ManifestsSuite verifies Kubernetes manifest sync. type ManifestsSuite struct { base.K8sSuite ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } // SuiteName returns the name of the suite. func (suite *ManifestsSuite) SuiteName() string { return "k8s.ManifestsSuite" } // SetupTest ... func (suite *ManifestsSuite) SetupTest() { // make sure API calls have timeout suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 5*time.Minute) suite.ClearConnectionRefused(suite.ctx, suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeWorker)...) suite.AssertClusterHealthy(suite.ctx) } type manifestSyncWriter struct { t *testing.T } func (w manifestSyncWriter) Write(p []byte) (n int, err error) { w.t.Log(" " + string(p)) return len(p), nil } // TestSync verifies that manifest sync works. func (suite *ManifestsSuite) TestSync() { if suite.Cluster == nil { suite.T().Skip("skip without full cluster state") } cpNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) nodeCtx := client.WithNode(suite.ctx, cpNode) config, err := suite.ReadConfigFromNode(nodeCtx) suite.Require().NoError(err) // some tests creates cluster without kube-proxy, skip in this case if !config.Cluster().Proxy().Enabled() { suite.T().Skip("skip when kube-proxy is disabled") } clusterAccess := access.NewAdapter(suite.Cluster, provision.WithTalosClient(suite.Client)) defer clusterAccess.Close() //nolint:errcheck // 1. Patch all controlplane nodes with extra arg for kube-proxy suite.T().Log("adding extra arg to kube-proxy") extraArgPatch := map[string]any{ "cluster": map[string]any{ "proxy": map[string]any{ "extraArgs": map[string]any{ "nodeport-addresses": "0.0.0.0/0", }, }, }, } for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { suite.PatchMachineConfig(client.WithNode(suite.ctx, node), extraArgPatch) } // wait for the manifest to be updated for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { rtestutils.AssertResource(client.WithNode(suite.ctx, node), suite.T(), suite.Client.COSI, "10-kube-proxy", func(manifest *k8s.Manifest, asrt *assert.Assertions) { marshaled, err := yaml.Marshal(manifest.TypedSpec()) suite.Require().NoError(err) asrt.Contains(string(marshaled), "nodeport-addresses=0.0.0.0/0") }, ) } // 2. Roll out manifests suite.Require().NoError(kubernetes.PerformManifestsSync( suite.ctx, clusterAccess, true, kubernetes.UpgradeOptions{ LogOutput: manifestSyncWriter{t: suite.T()}, ReconcileTimeout: 30 * time.Second, }, )) // 3. Assert that kube-proxy has the extra arg pods, err := suite.Clientset.CoreV1().Pods("kube-system").List(suite.ctx, metav1.ListOptions{ LabelSelector: "k8s-app=kube-proxy", }) suite.Require().NoError(err) suite.Require().NotEmpty(pods.Items) for _, pod := range pods.Items { suite.Require().NotEmpty(pod.Spec.Containers) suite.Assert().Contains(pod.Spec.Containers[0].Command, "--nodeport-addresses=0.0.0.0/0") } // 4. Disable kube-proxy suite.T().Log("disabling kube-proxy") disablePatch := map[string]any{ "cluster": map[string]any{ "proxy": map[string]any{ "disabled": true, }, }, } for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { suite.PatchMachineConfig(client.WithNode(suite.ctx, node), disablePatch) } // wait for the manifest to be removed for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { rtestutils.AssertNoResource[*k8s.Manifest](client.WithNode(suite.ctx, node), suite.T(), suite.Client.COSI, "10-kube-proxy", ) } // 5. Roll out manifests suite.Require().NoError(kubernetes.PerformManifestsSync( suite.ctx, clusterAccess, true, kubernetes.UpgradeOptions{ LogOutput: manifestSyncWriter{t: suite.T()}, }, )) // 6. Assert that kube-proxy is removed suite.Require().NoError(suite.EnsureResourceIsDeleted(suite.ctx, 10*time.Second, appsv1.SchemeGroupVersion.WithResource("daemonsets"), "kube-system", "kube-proxy")) // 7. Re-enable kube-proxy suite.T().Log("enabling kube-proxy") enablePatch := map[string]any{ "cluster": map[string]any{ "proxy": map[string]any{ "disabled": map[string]any{ "$patch": "delete", }, "extraArgs": map[string]any{ "$patch": "delete", }, }, }, } for _, node := range suite.DiscoverNodeInternalIPsByType(suite.ctx, machine.TypeControlPlane) { suite.PatchMachineConfig(client.WithNode(suite.ctx, node), enablePatch) } // 8. Assert that kube-proxy is back suite.Require().NoError( suite.WaitForResourceToBeAvailable(suite.ctx, 30*time.Second, "kube-system", appsv1.GroupName, "DaemonSet", appsv1.SchemeGroupVersion.Version, "kube-proxy"), ) suite.AssertClusterHealthy(suite.ctx) } func init() { allSuites = append(allSuites, new(ManifestsSuite)) } ================================================ FILE: internal/integration/k8s/oom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "context" _ "embed" "strings" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/dustin/go-humanize" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // OomSuite verifies that userspace OOM handler will kill excessive replicas of a heavy memory consumer deployment. type OomSuite struct { base.K8sSuite } //go:embed testdata/oom.yaml var oomPodSpec []byte // SuiteName returns the name of the suite. func (suite *OomSuite) SuiteName() string { return "k8s.OomSuite" } // TestOom verifies that system remains stable after handling an OOM event. func (suite *OomSuite) TestOom() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if testing.Short() { suite.T().Skip("skipping in short mode") } if suite.Race { suite.T().Skip("skipping as OOM tests are incompatible with race detector") } if suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping OOM test since provisioner is not qemu") } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) suite.T().Cleanup(cancel) oomPodManifest := suite.ParseManifests(oomPodSpec) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, oomPodManifest) ticker := time.NewTicker(time.Second) done := cleanUpCtx.Done() // Wait for all stress-mem pods to complete terminating for { select { case <-ticker.C: pods, err := suite.Clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{ LabelSelector: "app=stress-mem", }) suite.Require().NoError(err) if len(pods.Items) == 0 { return } case <-done: suite.Require().Fail("Timed out waiting for cleanup") return } } }) suite.ApplyManifests(ctx, oomPodManifest) suite.Require().NoError(suite.WaitForDeploymentAvailable(ctx, time.Minute, "default", "stress-mem", 2)) // Figure out number of replicas, this is ballpark estimation of 15 replicas per 2GB of memory (per worker node) numWorkers := len(suite.DiscoverNodeInternalIPsByType(ctx, machine.TypeWorker)) suite.Require().Greaterf(numWorkers, 0, "at least one worker node is required for the test") memInfo, err := suite.Client.Memory(client.WithNode(ctx, suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker))) suite.Require().NoError(err) memoryBytes := memInfo.GetMessages()[0].GetMeminfo().GetMemtotal() * 1024 numReplicas := int((memoryBytes/1024/1024+2048-1)/2048) * numWorkers * 25 suite.T().Logf("detected memory: %s, workers %d => scaling to %d replicas", humanize.IBytes(memoryBytes), numWorkers, numReplicas) // Scale to discovered number of replicas suite.PatchK8sObject(ctx, "default", "apps", "Deployment", "v1", "stress-mem", patchToReplicas(suite.T(), numReplicas)) // Expect at least one OOM kill of stress-ng within 15 seconds suite.Assert().True(suite.waitForOOMKilled(ctx, 15*time.Second, 2*time.Minute, "stress-ng", 1)) // Scale to 1, wait for deployment to scale down, proving system is operational suite.PatchK8sObject(ctx, "default", "apps", "Deployment", "v1", "stress-mem", patchToReplicas(suite.T(), 1)) suite.Require().NoError(suite.WaitForDeploymentAvailable(ctx, time.Minute, "default", "stress-mem", 1)) // Monitor OOM kills for 15 seconds and make sure no kills other than stress-ng happen // Allow 0 as well: ideally that'd be the case, but fail on anything not containing stress-ng suite.Assert().True(suite.waitForOOMKilled(ctx, 15*time.Second, 2*time.Minute, "stress-ng", 0)) suite.APISuite.AssertClusterHealthy(ctx) } func patchToReplicas(t *testing.T, replicas int) []byte { spec := map[string]any{ "spec": map[string]any{ "replicas": replicas, }, } patch, err := yaml.Marshal(spec) require.NoError(t, err) return patch } // Waits for a period of time and return returns whether or not OOM events containing a specified process have been observed. // //nolint:gocyclo func (suite *OomSuite) waitForOOMKilled(ctx context.Context, timeToObserve, timeout time.Duration, substr string, n int) bool { startTime := time.Now() watchCh := make(chan state.Event) workerNodes := suite.DiscoverNodeInternalIPsByType(ctx, machine.TypeWorker) // start watching OOM events on all worker nodes for _, workerNode := range workerNodes { suite.Assert().NoError(suite.Client.COSI.WatchKind( client.WithNode(ctx, workerNode), runtime.NewOOMActionSpec(runtime.NamespaceName, "").Metadata(), watchCh, )) } timeoutCh := time.After(timeout) timeToObserveCh := time.After(timeToObserve) numOOMObserved := 0 for { select { case <-timeoutCh: suite.T().Logf("observed %d OOM events containing process substring %q", numOOMObserved, substr) return numOOMObserved >= n case <-timeToObserveCh: if numOOMObserved >= n { // if we already observed some OOM events, consider it a success suite.T().Logf("observed %d OOM events containing process substring %q", numOOMObserved, substr) return true } case ev := <-watchCh: if ev.Type != state.Created || ev.Resource.Metadata().Created().Before(startTime) { continue } res := ev.Resource.(*runtime.OOMAction).TypedSpec() bailOut := false for _, proc := range res.Processes { if strings.Contains(proc, substr) { numOOMObserved++ break } // Sometimes OOM catches containers in restart phase (while the // cgroup has previously accumulated OOM score). // Consider an OOM event wrong if something other than that is found. if !strings.Contains(proc, "runc init") && !strings.Contains(proc, "/pause") && proc != "" { bailOut = true } } if bailOut { suite.T().Logf("observed an OOM event not containing process substring %q: %q (%d containing)", substr, res.Processes, numOOMObserved) return false } } } } func init() { allSuites = append(allSuites, new(OomSuite)) } ================================================ FILE: internal/integration/k8s/openebs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "bytes" "context" _ "embed" "path/filepath" "text/template" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) //go:embed testdata/openebs-values.yaml var openEBSValues []byte //go:embed testdata/openebs-diskpool.yaml var openEBSDiskPoolTemplate string // OpenEBSSuite tests deploying OpenEBS. type OpenEBSSuite struct { base.K8sSuite } // SuiteName returns the name of the suite. func (suite *OpenEBSSuite) SuiteName() string { return "k8s.OpenEBSSuite" } // TestDeploy tests deploying OpenEBS and running a simple test. func (suite *OpenEBSSuite) TestDeploy() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if suite.CSITestName != "openebs" { suite.T().Skip("skipping openebs test as it is not enabled") } timeout, err := time.ParseDuration(suite.CSITestTimeout) if err != nil { suite.T().Fatalf("failed to parse timeout: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), timeout) suite.T().Cleanup(cancel) if err := suite.HelmInstall( ctx, "openebs", "https://openebs.github.io/openebs", OpenEBSChartVersion, "openebs", "openebs", openEBSValues, ); err != nil { suite.T().Fatalf("failed to install OpenEBS chart: %v", err) } nodes := suite.DiscoverNodeInternalIPsByType(ctx, machine.TypeWorker) suite.Require().Equal(3, len(nodes), "expected 3 worker nodes") disks := xslices.Map(nodes, func(node string) string { return suite.UserDisks(ctx, node)[0] }) suite.Require().Equal(3, len(disks), "expected 3 disks") for i, disk := range disks { node := nodes[i] k8sNode, err := suite.GetK8sNodeByInternalIP(ctx, node) suite.Require().NoError(err) diskResource, err := safe.ReaderGetByID[*block.Disk](client.WithNode(ctx, node), suite.Client.COSI, filepath.Base(disk)) suite.Require().NoError(err) suite.Require().Greater(len(diskResource.TypedSpec().Symlinks), 1, "disk symlinks should not be empty") diskSymlink := diskResource.TypedSpec().Symlinks[1] tmpl, err := template.New(node).Parse(openEBSDiskPoolTemplate) suite.Require().NoError(err) var result bytes.Buffer suite.Require().NoError(tmpl.Execute(&result, struct { Node string Disk string }{ Node: k8sNode.Name, Disk: diskSymlink, })) diskPoolUnstructured := suite.ParseManifests(result.Bytes()) suite.ApplyManifests(ctx, diskPoolUnstructured) } suite.Require().NoError(suite.RunFIOTest(ctx, "openebs-single-replica", "10G")) } func init() { allSuites = append(allSuites, new(OpenEBSSuite)) } ================================================ FILE: internal/integration/k8s/rook.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "context" _ "embed" "time" "github.com/siderolabs/talos/internal/integration/base" ) //go:embed testdata/rook-ceph-cluster-values.yaml var rookCephClusterValues []byte // RookSuite tests deploying Rook. type RookSuite struct { base.K8sSuite } // SuiteName returns the name of the suite. func (suite *RookSuite) SuiteName() string { return "k8s.RookSuite" } // TestDeploy tests deploying Rook and running a simple test. func (suite *RookSuite) TestDeploy() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if suite.CSITestName != "rook-ceph" { suite.T().Skip("skipping rook-ceph test as it is not enabled") } timeout, err := time.ParseDuration(suite.CSITestTimeout) if err != nil { suite.T().Fatalf("failed to parse timeout: %v", err) } ctx, cancel := context.WithTimeout(context.Background(), timeout) suite.T().Cleanup(cancel) if err := suite.HelmInstall( ctx, "rook-ceph", "https://charts.rook.io/release", RookCephHelmChartVersion, "rook-ceph", "rook-ceph", nil, ); err != nil { suite.T().Fatalf("failed to install Rook chart: %v", err) } if err := suite.HelmInstall( ctx, "rook-ceph", "https://charts.rook.io/release", RookCephHelmChartVersion, "rook-ceph-cluster", "rook-ceph-cluster", rookCephClusterValues, ); err != nil { suite.T().Fatalf("failed to install Rook chart: %v", err) } if err := suite.WaitForResource(ctx, "rook-ceph", "ceph.rook.io", "CephCluster", "v1", "rook-ceph", "{.status.phase}", "Ready"); err != nil { suite.T().Fatalf("failed to wait for CephCluster to be Ready: %v", err) } if err := suite.WaitForResource(ctx, "rook-ceph", "ceph.rook.io", "CephCluster", "v1", "rook-ceph", "{.status.state}", "Created"); err != nil { suite.T().Fatalf("failed to wait for CephCluster to be Created: %v", err) } if err := suite.WaitForResource(ctx, "rook-ceph", "ceph.rook.io", "CephCluster", "v1", "rook-ceph", "{.status.ceph.health}", "HEALTH_OK"); err != nil { suite.T().Fatalf("failed to wait for CephCluster to be HEALTH_OK: %v", err) } suite.Require().NoError(suite.RunFIOTest(ctx, "ceph-block", "10G")) } func init() { allSuites = append(allSuites, new(RookSuite)) } ================================================ FILE: internal/integration/k8s/testdata/apparmor.yaml ================================================ apiVersion: v1 kind: Pod metadata: labels: run: nginx-apparmor name: nginx-apparmor namespace: default spec: containers: - image: nginx name: nginx-apparmor resources: {} dnsPolicy: ClusterFirst securityContext: appArmorProfile: type: RuntimeDefault restartPolicy: Always ================================================ FILE: internal/integration/k8s/testdata/local-path-storage.yaml ================================================ apiVersion: v1 kind: Namespace metadata: labels: pod-security.kubernetes.io/enforce: privileged name: local-path-storage --- apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: annotations: storageclass.kubernetes.io/is-default-class: "true" name: local-path provisioner: rancher.io/local-path reclaimPolicy: Delete volumeBindingMode: WaitForFirstConsumer --- apiVersion: v1 kind: ServiceAccount metadata: name: local-path-provisioner-service-account namespace: local-path-storage --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: local-path-provisioner-role namespace: local-path-storage rules: - apiGroups: - "" resources: - pods verbs: - get - list - watch - create - patch - update - delete --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: local-path-provisioner-role rules: - apiGroups: - "" resources: - nodes - persistentvolumeclaims - configmaps - pods - pods/log verbs: - get - list - watch - apiGroups: - "" resources: - persistentvolumes verbs: - get - list - watch - create - patch - update - delete - apiGroups: - "" resources: - events verbs: - create - patch - apiGroups: - storage.k8s.io resources: - storageclasses verbs: - get - list - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: local-path-provisioner-bind namespace: local-path-storage roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: local-path-provisioner-role subjects: - kind: ServiceAccount name: local-path-provisioner-service-account namespace: local-path-storage --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: local-path-provisioner-bind roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: local-path-provisioner-role subjects: - kind: ServiceAccount name: local-path-provisioner-service-account namespace: local-path-storage --- apiVersion: v1 data: config.json: |- { "nodePathMap":[ { "node":"DEFAULT_PATH_FOR_NON_LISTED_NODES", "paths":["/var/local-path-provisioner"] } ] } helperPod.yaml: |- apiVersion: v1 kind: Pod metadata: name: helper-pod spec: priorityClassName: system-node-critical tolerations: - key: node.kubernetes.io/disk-pressure operator: Exists effect: NoSchedule containers: - name: helper-pod image: busybox imagePullPolicy: IfNotPresent setup: |- #!/bin/sh set -eu mkdir -m 0777 -p "$VOL_DIR" teardown: |- #!/bin/sh set -eu rm -rf "$VOL_DIR" kind: ConfigMap metadata: name: local-path-config namespace: local-path-storage --- apiVersion: apps/v1 kind: Deployment metadata: name: local-path-provisioner namespace: local-path-storage spec: replicas: 1 selector: matchLabels: app: local-path-provisioner template: metadata: labels: app: local-path-provisioner spec: containers: - command: - local-path-provisioner - --debug - start - --config - /etc/config/config.json env: - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace image: rancher/local-path-provisioner:v0.0.26 imagePullPolicy: IfNotPresent name: local-path-provisioner volumeMounts: - mountPath: /etc/config/ name: config-volume serviceAccountName: local-path-provisioner-service-account volumes: - configMap: name: local-path-config name: config-volume ================================================ FILE: internal/integration/k8s/testdata/longhorn-iscsi-volume.yaml ================================================ --- apiVersion: longhorn.io/v1beta2 kind: Volume metadata: labels: longhornvolume: iscsi name: iscsi namespace: longhorn-system spec: frontend: iscsi numberOfReplicas: 1 size: "1073741824" ================================================ FILE: internal/integration/k8s/testdata/longhorn-v2-disk-patch.yaml ================================================ --- spec: disks: nvme: allowScheduling: true evictionRequested: false path: /dev/nvme0n1 storageReserved: 0 tags: [] diskType: block ================================================ FILE: internal/integration/k8s/testdata/longhorn-v2-engine-values.yaml ================================================ defaultSettings: v2DataEngine: true ================================================ FILE: internal/integration/k8s/testdata/longhorn-v2-storageclass.yaml ================================================ kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: longhorn-v2 provisioner: driver.longhorn.io allowVolumeExpansion: true reclaimPolicy: Delete volumeBindingMode: Immediate parameters: numberOfReplicas: "3" staleReplicaTimeout: "2880" fsType: "ext4" dataEngine: "v2" ================================================ FILE: internal/integration/k8s/testdata/longhorn-volumeattachment.yaml ================================================ --- apiVersion: longhorn.io/v1beta2 kind: VolumeAttachment metadata: labels: longhornvolume: iscsi name: iscsi namespace: longhorn-system spec: attachmentTickets: longhorn-ui: generation: 0 id: longhorn-ui nodeID: {{ .NodeID }} parameters: disableFrontend: "false" lastAttachedBy: "" type: longhorn-api volume: iscsi ================================================ FILE: internal/integration/k8s/testdata/oom.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: stress-mem namespace: default spec: replicas: 2 selector: matchLabels: app: stress-mem template: metadata: labels: app: stress-mem spec: containers: - name: stress-mem image: ghcr.io/siderolabs/stress-ng:v1 command: - /usr/bin/stress-ng - --vm - "1" - --vm-bytes - 100M - --vm-hang - "0" - --vm-keep - --page-in - --cpu - "1" - -l - "0" ================================================ FILE: internal/integration/k8s/testdata/openebs-diskpool.yaml ================================================ apiVersion: "openebs.io/v1beta3" kind: DiskPool metadata: name: pool-{{ .Node }} namespace: openebs spec: node: {{ .Node }} disks: ["aio://{{ .Disk }}"] ================================================ FILE: internal/integration/k8s/testdata/openebs-values.yaml ================================================ mayastor: csi: node: initContainers: enabled: false engines: local: lvm: enabled: false zfs: enabled: false ================================================ FILE: internal/integration/k8s/testdata/pod-iscsi-volume.yaml ================================================ --- apiVersion: v1 kind: Pod metadata: name: iscsipd namespace: default spec: containers: - name: iscsipd-rw image: alpine command: ["/bin/sh", "-c", "--"] args: ["trap : TERM INT; (sleep 1000) & wait"] volumeMounts: - mountPath: "/mnt/iscsipd" name: iscsipd-rw nodeName: {{ .NodeName }} volumes: - name: iscsipd-rw iscsi: targetPortal: {{ .TargetPortal }} iqn: {{ .IQN }} lun: 1 fsType: ext4 readOnly: false ================================================ FILE: internal/integration/k8s/testdata/rook-ceph-cluster-values.yaml ================================================ operatorNamespace: rook-ceph cephFileSystems: [] cephObjectStores: [] cephClusterSpec: crashCollector: disable: true logCollector: enabled: false dashboard: enabled: false resources: osd: limits: memory: "2Gi" requests: cpu: "500m" memory: "1Gi" storage: useAllNodes: true useAllDevices: true config: encryptedDevice: "true" ================================================ FILE: internal/integration/k8s/testdata/usernamespace.yaml ================================================ apiVersion: v1 kind: Pod metadata: name: userns namespace: default spec: hostUsers: false nodeName: $NODE$ containers: - name: userns command: ["/bin/sh", "-c", "--"] args: ["trap : TERM INT; (sleep 1000) & wait"] image: alpine ================================================ FILE: internal/integration/k8s/tink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "context" "crypto/tls" _ "embed" "fmt" "net" "net/netip" "strconv" "strings" "testing" "time" "github.com/siderolabs/gen/ensure" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" podsecurity "k8s.io/pod-security-admission/api" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/cluster/check" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" ) // TinkSuite verifies Talos-in-Kubernetes. type TinkSuite struct { base.K8sSuite } // SuiteName ... func (suite *TinkSuite) SuiteName() string { return "k8s.TinkSuite" } //go:embed testdata/local-path-storage.yaml var localPathStorageYAML []byte const ( tinkK8sPort = "k8s-api" tinkTalosPort = "talos-api" ) // TestDeploy verifies that tink can be deployed with a single control-plane node. func (suite *TinkSuite) TestDeploy() { if testing.Short() { suite.T().Skip("skipping in short mode") } if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) suite.T().Cleanup(cancel) suite.AssertClusterHealthy(ctx) localPathStorage := suite.ParseManifests(localPathStorageYAML) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, localPathStorage) }) suite.ApplyManifests(ctx, localPathStorage) const ( namespace = "talos-in-talos" service = "talos" ss = "talos-cp" ) talosImage := fmt.Sprintf("%s:%s", suite.TalosImage, version.Tag) suite.T().Logf("deploying Talos-in-Kubernetes from image %s", talosImage) tinkManifests := suite.getTinkManifests(namespace, service, ss, talosImage) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, tinkManifests) }) suite.ApplyManifests(ctx, tinkManifests) // wait for the control-plane pod to be running suite.Require().NoError(suite.WaitForPodToBeRunning(ctx, 2*time.Minute, namespace, ss+"-0")) // read back Service to figure out the ports svc, err := suite.Clientset.CoreV1().Services(namespace).Get(ctx, service, metav1.GetOptions{}) suite.Require().NoError(err) var k8sPort, talosPort int for _, portSpec := range svc.Spec.Ports { switch portSpec.Name { case tinkK8sPort: k8sPort = int(portSpec.NodePort) case tinkTalosPort: talosPort = int(portSpec.NodePort) } } suite.Require().NotZero(k8sPort) suite.Require().NotZero(talosPort) // find pod IP pod, err := suite.Clientset.CoreV1().Pods(namespace).Get(ctx, ss+"-0", metav1.GetOptions{}) suite.Require().NoError(err) suite.Require().NotEmpty(pod.Status.PodIP) podIP := netip.MustParseAddr(pod.Status.PodIP) // grab any random lbNode IP lbNode := suite.RandomDiscoveredNodeInternalIP() talosEndpoint := net.JoinHostPort(lbNode, strconv.Itoa(talosPort)) in, err := generate.NewInput(namespace, fmt.Sprintf("https://%s", net.JoinHostPort(lbNode, strconv.Itoa(k8sPort))), constants.DefaultKubernetesVersion, generate.WithAdditionalSubjectAltNames([]string{lbNode}), generate.WithHostDNSForwardKubeDNSToHost(true), ) suite.Require().NoError(err) // override pod/service subnets, as Talos-in-Talos would use it for "host" addresses in.PodNet = []string{"192.168.0.0/20"} in.ServiceNet = []string{"192.168.128.0/20"} cpCfg, err := in.Config(machine.TypeControlPlane) suite.Require().NoError(err) cpCfgBytes, err := cpCfg.Bytes() suite.Require().NoError(err) readyErr := suite.waitForEndpointReady(talosEndpoint) if readyErr != nil { suite.LogPodLogs(ctx, namespace, ss+"-0") } suite.Require().NoError(readyErr) insecureClient, err := client.New(ctx, client.WithEndpoints(talosEndpoint), client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}), ) suite.Require().NoError(err) suite.T().Log("applying initial configuration") _, err = insecureClient.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{ Data: cpCfgBytes, Mode: machineapi.ApplyConfigurationRequest_AUTO, }) suite.Require().NoError(err) // bootstrap talosconfig, err := in.Talosconfig() suite.Require().NoError(err) talosconfig.Contexts[talosconfig.Context].Endpoints = []string{talosEndpoint} talosconfig.Contexts[talosconfig.Context].Nodes = []string{podIP.String()} suite.T().Logf("talosconfig = %s", string(ensure.Value(talosconfig.Bytes()))) readyErr = suite.waitForEndpointReady(talosEndpoint) if readyErr != nil { suite.LogPodLogs(ctx, namespace, ss+"-0") } suite.Require().NoError(readyErr) talosClient, err := client.New(ctx, client.WithConfigContext(talosconfig.Contexts[talosconfig.Context]), ) suite.Require().NoError(err) suite.T().Log("bootstrapping") if !suite.Assert().EventuallyWithT( func(collect *assert.CollectT) { asrt := assert.New(collect) asrt.NoError(talosClient.Bootstrap(ctx, &machineapi.BootstrapRequest{})) }, time.Minute, 100*time.Millisecond, ) { suite.LogPodLogs(ctx, namespace, ss+"-0") suite.T().Fatalf("failed to bootstrap Talos-in-Kubernetes") } clusterAccess := &tinkClusterAccess{ KubernetesClient: cluster.KubernetesClient{ ClientProvider: &cluster.ConfigClientProvider{ TalosConfig: talosconfig, }, }, nodeIP: podIP, } suite.Require().NoError( check.Wait( ctx, clusterAccess, check.DefaultClusterChecks(), check.StderrReporter(), ), ) } type tinkClusterAccess struct { cluster.KubernetesClient nodeIP netip.Addr } func (access *tinkClusterAccess) Nodes() []cluster.NodeInfo { return []cluster.NodeInfo{ { InternalIP: access.nodeIP, IPs: []netip.Addr{access.nodeIP}, }, } } func (access *tinkClusterAccess) NodesByType(typ machine.Type) []cluster.NodeInfo { switch typ { case machine.TypeControlPlane: return []cluster.NodeInfo{ { InternalIP: access.nodeIP, IPs: []netip.Addr{access.nodeIP}, }, } case machine.TypeWorker, machine.TypeInit: return nil case machine.TypeUnknown: fallthrough default: panic(fmt.Sprintf("unexpected machine type: %s", typ)) } } func (suite *TinkSuite) waitForEndpointReady(endpoint string) error { return retry.Constant(30*time.Second, retry.WithUnits(10*time.Millisecond)).Retry(func() error { c, err := (&tls.Dialer{ Config: &tls.Config{ InsecureSkipVerify: true, }, }).DialContext(suite.T().Context(), "tcp", endpoint) if c != nil { c.Close() //nolint:errcheck } return retry.ExpectedError(err) }) } func (suite *TinkSuite) getTinkManifests(namespace, serviceName, ssName, talosImage string) []unstructured.Unstructured { labels := map[string]string{ "app": "talos-cp", } tinkManifests := []runtime.Object{ //nolint:prealloc // this is a test &corev1.Namespace{ TypeMeta: metav1.TypeMeta{ Kind: "Namespace", APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ Name: namespace, Labels: map[string]string{ podsecurity.EnforceLevelLabel: string(podsecurity.LevelPrivileged), }, }, }, &corev1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ Name: serviceName, Namespace: namespace, }, Spec: corev1.ServiceSpec{ Type: corev1.ServiceTypeNodePort, Selector: labels, Ports: []corev1.ServicePort{ { Name: tinkK8sPort, Protocol: corev1.ProtocolTCP, Port: constants.DefaultControlPlanePort, TargetPort: intstr.FromString(tinkK8sPort), }, { Name: tinkTalosPort, Protocol: corev1.ProtocolTCP, Port: constants.ApidPort, TargetPort: intstr.FromString(tinkTalosPort), }, }, }, }, } statefulSet := &appsv1.StatefulSet{ TypeMeta: metav1.TypeMeta{ Kind: "StatefulSet", APIVersion: "apps/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: ssName, Namespace: namespace, }, Spec: appsv1.StatefulSetSpec{ ServiceName: serviceName, Replicas: new(int32(1)), Selector: &metav1.LabelSelector{ MatchLabels: labels, }, Template: corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: labels, }, Spec: corev1.PodSpec{ Containers: []corev1.Container{ { Name: "talos", Image: talosImage, ImagePullPolicy: corev1.PullAlways, SecurityContext: &corev1.SecurityContext{ Privileged: new(true), ReadOnlyRootFilesystem: new(true), SeccompProfile: &corev1.SeccompProfile{ Type: corev1.SeccompProfileTypeUnconfined, }, }, Env: []corev1.EnvVar{ { Name: "PLATFORM", Value: "container", }, }, Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceMemory: resource.MustParse("1Gi"), corev1.ResourceCPU: resource.MustParse("750m"), }, }, Ports: []corev1.ContainerPort{ { ContainerPort: constants.ApidPort, Name: tinkTalosPort, }, { ContainerPort: constants.DefaultControlPlanePort, Name: tinkK8sPort, }, }, }, }, }, }, }, } for _, ephemeralMount := range []string{"run", "system", "tmp"} { statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts = append( statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ MountPath: "/" + ephemeralMount, Name: ephemeralMount, }, ) statefulSet.Spec.Template.Spec.Volumes = append( statefulSet.Spec.Template.Spec.Volumes, corev1.Volume{ Name: ephemeralMount, VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }, ) } type overlayMountSpec struct { MountPoint string Size string } for _, overlayMount := range append( []overlayMountSpec{ { MountPoint: constants.StateMountPoint, Size: "100Mi", }, { MountPoint: constants.EphemeralMountPoint, Size: "6Gi", }, }, xslices.Map( xslices.Filter(constants.Overlays, func(overlay constants.SELinuxLabeledPath) bool { return overlay.Path != "/opt" }), // /opt/cni/bin contains CNI binaries func(mnt constants.SELinuxLabeledPath) overlayMountSpec { return overlayMountSpec{ MountPoint: mnt.Path, Size: "100Mi", } }, )..., ) { name := strings.ReplaceAll(strings.TrimLeft(overlayMount.MountPoint, "/"), "/", "-") statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts = append( statefulSet.Spec.Template.Spec.Containers[0].VolumeMounts, corev1.VolumeMount{ MountPath: overlayMount.MountPoint, Name: name, }, ) statefulSet.Spec.VolumeClaimTemplates = append( statefulSet.Spec.VolumeClaimTemplates, corev1.PersistentVolumeClaim{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ corev1.ReadWriteOnce, }, Resources: corev1.VolumeResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: resource.MustParse(overlayMount.Size), }, }, }, }) } tinkManifests = append(tinkManifests, statefulSet) return xslices.Map(tinkManifests, suite.ToUnstructured) } func init() { allSuites = append(allSuites, new(TinkSuite)) } ================================================ FILE: internal/integration/k8s/usernamespace.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "bufio" "bytes" "context" _ "embed" "fmt" "strings" "time" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // UserNamespaceSuite verifies that a pod with user namespace works. type UserNamespaceSuite struct { base.K8sSuite } //go:embed testdata/usernamespace.yaml var userNamespacePodSpec []byte // SuiteName returns the name of the suite. func (suite *UserNamespaceSuite) SuiteName() string { return "k8s.UserNamespaceSuite" } // TestUserNamespace verifies that a pod with user namespace works. // //nolint:gocyclo,cyclop func (suite *UserNamespaceSuite) TestUserNamespace() { if suite.Cluster == nil { suite.T().Skip("without full cluster state reaching out to the node IP is not reliable") } if suite.Cluster.Provisioner() != base.ProvisionerQEMU { suite.T().Skip("skipping usernamespace test since provisioner is not qemu") } ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) suite.T().Cleanup(cancel) node := suite.RandomDiscoveredNodeInternalIP(machine.TypeWorker) nodeCtx := client.WithNodes(ctx, node) reader, err := suite.Client.Read(nodeCtx, "/proc/sys/user/max_user_namespaces") suite.Require().NoError(err) var maxUserNamespaces bytes.Buffer _, err = maxUserNamespaces.ReadFrom(reader) suite.Require().NoError(err) if strings.TrimSpace(maxUserNamespaces.String()) == "0" { suite.T().Skip("skipping test since user namespace is disabled") } controlPlaneNode := suite.RandomDiscoveredNodeInternalIP(machine.TypeControlPlane) controlPlaneNodeCtx := client.WithNode(ctx, controlPlaneNode) controlPlaneNodeConfig, err := suite.ReadConfigFromNode(controlPlaneNodeCtx) suite.Require().NoError(err) if controlPlaneNodeConfig.Cluster().APIServer().ExtraArgs() == nil { suite.T().Skip("skipping test since no api server extra args found") } else { if featureGates, ok := controlPlaneNodeConfig.Cluster().APIServer().ExtraArgs()["feature-gates"]; ok { hasUserNamespacesSupport := false for _, featureGate := range featureGates { if strings.Contains(featureGate, "UserNamespacesSupport=true") { hasUserNamespacesSupport = true break } } if !hasUserNamespacesSupport { suite.T().Skip("skipping test since user namespace feature gate is not enabled for kube-apiserver") } } } workerNodeConfig, err := suite.ReadConfigFromNode(client.WithNode(ctx, node)) suite.Require().NoError(err) if workerNodeConfig.Machine().Kubelet().ExtraConfig() == nil { suite.T().Skip("skipping test since no kubelet extra config found") } else { if featureGates, ok := workerNodeConfig.Machine().Kubelet().ExtraConfig()["featureGates"]; ok { if fg, ok := featureGates.(map[string]string); ok { if val, ok := fg["UserNamespacesSupport"]; !ok || val != "true" { suite.T().Skip("skipping test since user namespace feature gate is not enabled for kubelet") } } } } k8sNode, err := suite.GetK8sNodeByInternalIP(ctx, node) suite.Require().NoError(err) suite.T().Logf("testing k8s user namespace on node %q (%q)", node, k8sNode.Name) // bind the pod to the node usernamespacePodManifest := suite.ParseManifests(bytes.ReplaceAll(userNamespacePodSpec, []byte("$NODE$"), []byte(k8sNode.Name))) suite.T().Cleanup(func() { cleanUpCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) defer cleanupCancel() suite.DeleteManifests(cleanUpCtx, usernamespacePodManifest) }) suite.ApplyManifests(ctx, usernamespacePodManifest) suite.Require().NoError(suite.WaitForPodToBeRunning(ctx, time.Minute, "default", "userns")) processResp, err := suite.Client.Processes(nodeCtx) suite.Require().NoError(err) var sleepProcessPID int for _, processInfo := range processResp.Messages { for _, process := range processInfo.Processes { if strings.Contains(process.Args, "sleep 1000") { sleepProcessPID = int(process.Pid) break } } } suite.Require().NotZero(sleepProcessPID, "sleep process not found for user namespace test") reader, err = suite.Client.Read(nodeCtx, fmt.Sprintf("/proc/%d/status", sleepProcessPID)) suite.Require().NoError(err) var processStatus bytes.Buffer _, err = processStatus.ReadFrom(reader) suite.Require().NoError(err) scanner := bufio.NewScanner(&processStatus) var processUsingUserNamespace bool for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "Uid:") { fields := strings.Fields(line) if fields[0] != "0" && fields[1] != "0" && fields[2] != "0" && fields[3] != "0" { processUsingUserNamespace = true } break } } suite.Require().True(processUsingUserNamespace, "sleep process should not have root UID in host namespace\n", processStatus.String()) } func init() { allSuites = append(allSuites, new(UserNamespaceSuite)) } ================================================ FILE: internal/integration/k8s/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_k8s package k8s import ( "context" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/machinery/constants" ) // VersionSuite verifies Talos version. type VersionSuite struct { base.K8sSuite } // SuiteName ... func (suite *VersionSuite) SuiteName() string { return "k8s.VersionSuite" } // TestExpectedVersion verifies that node versions matches expected. func (suite *VersionSuite) TestExpectedVersion() { // verify k8s version (api server) apiServerVersion, err := suite.DiscoveryClient.ServerVersion() suite.Require().NoError(err) expectedAPIServerVersion := fmt.Sprintf("v%s", constants.DefaultKubernetesVersion) suite.Assert().Equal(expectedAPIServerVersion, apiServerVersion.GitVersion) checkKernelVersion := suite.Capabilities().RunsTalosKernel // verify each node (kubelet version, Talos version, etc.) nodes, err := suite.Clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) suite.Require().NoError(err) expectedTalosVersion := fmt.Sprintf("Talos (%s)", suite.Version) expectedContainerRuntimeVersion := fmt.Sprintf("containerd://%s", constants.DefaultContainerdVersion) expectedKubeletVersion := fmt.Sprintf("v%s", constants.DefaultKubernetesVersion) expectedKernelVersion := constants.DefaultKernelVersion for _, node := range nodes.Items { suite.Assert().Equal(expectedTalosVersion, node.Status.NodeInfo.OSImage) suite.Assert().Equal("linux", node.Status.NodeInfo.OperatingSystem) suite.Assert().Equal(expectedContainerRuntimeVersion, node.Status.NodeInfo.ContainerRuntimeVersion) suite.Assert().Equal(expectedKubeletVersion, node.Status.NodeInfo.KubeletVersion) if checkKernelVersion { suite.Assert().Equal(expectedKernelVersion, node.Status.NodeInfo.KernelVersion) } } } func init() { allSuites = append(allSuites, new(VersionSuite)) } ================================================ FILE: internal/integration/provision/k8s_compatibility.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_provision package provision import ( "fmt" "slices" "github.com/blang/semver/v4" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" ) // K8sCompatibilitySuite ... type K8sCompatibilitySuite struct { BaseSuite track int versionsSequence []string } // SuiteName ... func (suite *K8sCompatibilitySuite) SuiteName() string { return fmt.Sprintf("provision.UpgradeSuite.KubernetesCompatibility-TR%d", suite.track) } // SetupSuite ... func (suite *K8sCompatibilitySuite) SetupSuite() { // figure out Kubernetes versions to go through, the calculation is based on: // * DefaultKubernetesVersion, e.g. 1.29.0 // * SupportedKubernetesVersions, e.g. 6 // * available `kubelet` images (tags) // // E.g. with example values above, upgrade will go through: // 1.24 -> 1.25 -> 1.26 -> 1.27 -> 1.28 -> 1.29 (6 versions) // For each past Kubernetes release, latest patch release will be used, // for the latest version (DefaultKubernetesVersion), the exact version will be used kubeletRepository, err := name.NewRepository(constants.KubeletImage) suite.Require().NoError(err) maxVersion, err := semver.Parse(constants.DefaultKubernetesVersion) suite.Require().NoError(err) minVersion := semver.Version{ Major: maxVersion.Major, Minor: maxVersion.Minor - constants.SupportedKubernetesVersions + 1, Patch: 0, } // while Talos is in alpha stage, DefaultKubernetesVersion might be 1 minor behind the latest alpha Kubernetes version, // so we need to ensure that minVersion fits into compatibility range minVersionAdjusted := false currentTalosVersion, err := compatibility.ParseTalosVersion(version.NewVersion()) suite.Require().NoError(err) minKubernetesVersion, err := compatibility.ParseKubernetesVersion(minVersion.String()) suite.Require().NoError(err) if minKubernetesVersion.SupportedWith(currentTalosVersion) != nil { // bump up minVersion to the next minor version minVersion.Minor++ minVersionAdjusted = true } type versionInfo struct { Major uint64 Minor uint64 } versionsToUse := map[versionInfo]semver.Version{ { Major: maxVersion.Major, Minor: maxVersion.Minor, }: maxVersion, } tags, err := remote.List(kubeletRepository) suite.Require().NoError(err) for _, tag := range tags { version, err := semver.ParseTolerant(tag) if err != nil { continue } if version.Pre != nil { continue } if version.LT(minVersion) { continue } if version.GT(maxVersion) { continue } versionKey := versionInfo{ Major: version.Major, Minor: version.Minor, } if curVersion := versionsToUse[versionKey]; version.GT(curVersion) { versionsToUse[versionKey] = version } } k8sVersions := maps.Values(versionsToUse) slices.SortFunc(k8sVersions, func(a, b semver.Version) int { return a.Compare(b) }) suite.versionsSequence = xslices.Map(k8sVersions, semver.Version.String) suite.T().Logf("using following upgrade sequence: %v", suite.versionsSequence) if minVersionAdjusted { suite.T().Logf("min Kubernetes version was adjusted to %s to fit Talos compatibility range", minVersion.String()) suite.Assert().Len(suite.versionsSequence, constants.SupportedKubernetesVersions-1) } else { suite.Assert().Len(suite.versionsSequence, constants.SupportedKubernetesVersions) } suite.BaseSuite.SetupSuite() } // TestAllVersions tries to run cluster on all Kubernetes versions. func (suite *K8sCompatibilitySuite) TestAllVersions() { // start a cluster using latest Talos, and on earliest supported Kubernetes version suite.setupCluster(clusterOptions{ ClusterName: "k8s-compat", ControlplaneNodes: DefaultSettings.ControlplaneNodes, WorkerNodes: DefaultSettings.WorkerNodes, SourceKernelPath: helpers.ArtifactPath(constants.KernelAssetWithArch), SourceInitramfsPath: helpers.ArtifactPath(constants.InitramfsAssetWithArch), SourceInstallerImage: fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ), SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: suite.versionsSequence[0], }) suite.runE2E(suite.versionsSequence[0]) // for each next supported Kubernetes version, upgrade k8s and run e2e tests for i := 1; i < len(suite.versionsSequence); i++ { suite.upgradeKubernetes(suite.versionsSequence[i-1], suite.versionsSequence[i], false) suite.waitForClusterHealth() suite.runE2E(suite.versionsSequence[i]) } } func init() { allSuites = append( allSuites, &K8sCompatibilitySuite{track: 2}, ) } ================================================ FILE: internal/integration/provision/maintenance_basic.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_provision package provision import ( "crypto/tls" "errors" "fmt" "io" "os" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // MaintenanceBasicSuite ... type MaintenanceBasicSuite struct { BaseSuite track int } // SuiteName ... func (suite *MaintenanceBasicSuite) SuiteName() string { return fmt.Sprintf("provision.UpgradeSuite.MaintenanceBasic-TR%d", suite.track) } // TestAPI tests basic maintenance API operations. // //nolint:gocyclo func (suite *MaintenanceBasicSuite) TestAPI() { const ( maintenanceControlplanes = 1 maintenanceWorkers = 1 ) suite.setupCluster(clusterOptions{ ClusterName: "maintenance", ControlplaneNodes: maintenanceControlplanes, WorkerNodes: maintenanceWorkers, SourceKernelPath: helpers.ArtifactPath(constants.KernelAssetWithArch), SourceInitramfsPath: helpers.ArtifactPath(constants.InitramfsAssetWithArch), SourceInstallerImage: fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ), SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: constants.DefaultKubernetesVersion, WithSkipInjectingConfig: true, }) maintenanceClients := make([]*client.Client, len(suite.Cluster.Info().Nodes)) for i, machine := range suite.Cluster.Info().Nodes { var err error maintenanceClients[i], err = client.New( suite.ctx, client.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}), client.WithEndpoints(machine.IPs[0].String()), ) suite.Require().NoError(err) } defer func() { for _, c := range maintenanceClients { suite.Require().NoError(c.Close()) } }() suite.Run("wait for maintenance API", func() { // we should be able to query version API for every machine suite.Require().EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) for _, maintenanceClient := range maintenanceClients { version, err := maintenanceClient.Version(suite.ctx) if !asrt.NoError(err) { return } suite.Assert().Equal(DefaultSettings.CurrentVersion, version.GetMessages()[0].GetVersion().GetTag()) } }, time.Minute, time.Second, "version API should be available") }) suite.Run("testing basic maintenance APIs", func() { // it doesn't matter which machine to use, as they are all same in maintenance mode right now maintenanceClient := maintenanceClients[0] linkStatuses, err := safe.ReaderListAll[*network.LinkStatus](suite.ctx, maintenanceClient.COSI) suite.Require().NoError(err) suite.Assert().NotEmpty(linkStatuses) // link specs should be not available (sensitive) _, err = safe.ReaderListAll[*network.LinkSpec](suite.ctx, maintenanceClient.COSI) suite.Require().Error(err) suite.Require().Equal(codes.PermissionDenied, client.StatusCode(err)) // reboot should be not authorized in maintenance mode err = maintenanceClient.Reboot(suite.ctx) suite.Require().Error(err) suite.Require().Equal(codes.PermissionDenied, client.StatusCode(err)) listClient, err := maintenanceClient.ImageClient.List(suite.ctx, &machine.ImageServiceListRequest{ Containerd: &common.ContainerdInstance{ Driver: common.ContainerDriver_CONTAINERD, Namespace: common.ContainerdNamespace_NS_SYSTEM, }, }) suite.Require().NoError(err) for { _, err := listClient.Recv() if errors.Is(err, io.EOF) { break } suite.Require().NoError(err) } }) suite.Run("apply config and have a cluster", func() { for i := range maintenanceControlplanes { maintenanceClient := maintenanceClients[i] configData, err := suite.configBundle.ControlPlaneCfg.Bytes() suite.Require().NoError(err) _, err = maintenanceClient.ApplyConfiguration(suite.ctx, &machine.ApplyConfigurationRequest{ Data: configData, }) suite.Require().NoError(err) } for i := range maintenanceWorkers { maintenanceClient := maintenanceClients[maintenanceControlplanes+i] configData, err := suite.configBundle.WorkerCfg.Bytes() suite.Require().NoError(err) _, err = maintenanceClient.ApplyConfiguration(suite.ctx, &machine.ApplyConfigurationRequest{ Data: configData, }) suite.Require().NoError(err) } suite.Require().NoError(suite.clusterAccess.Bootstrap(suite.ctx, os.Stdout)) suite.waitForClusterHealth() }) suite.Run("reset STATE and EPHEMERAL", func() { // reset starting from worker nodes for idx := len(suite.Cluster.Info().Nodes) - 1; idx >= 0; idx-- { node := suite.Cluster.Info().Nodes[idx].IPs[0].String() suite.Run(fmt.Sprintf("resetting node %s", node), func() { client, err := suite.clusterAccess.Client(node) suite.Require().NoError(err) defer func() { suite.Require().NoError(client.Close()) }() suite.Require().NoError(client.ResetGeneric(suite.ctx, &machine.ResetRequest{ Graceful: false, Reboot: true, SystemPartitionsToWipe: []*machine.ResetPartitionSpec{ { Label: constants.StatePartitionLabel, Wipe: true, }, { Label: constants.EphemeralPartitionLabel, Wipe: true, }, }, })) }) } }) suite.Run("wait for back to maintenance API", func() { // we should be able to query version API for every machine suite.Require().EventuallyWithT(func(collect *assert.CollectT) { asrt := assert.New(collect) for _, maintenanceClient := range maintenanceClients { version, err := maintenanceClient.Version(suite.ctx) if !asrt.NoError(err) { return } suite.Assert().Equal(DefaultSettings.CurrentVersion, version.GetMessages()[0].GetVersion().GetTag()) } }, 3*time.Minute, time.Second, "version API should be available") }) } func init() { allSuites = append( allSuites, &MaintenanceBasicSuite{track: 3}, ) } ================================================ FILE: internal/integration/provision/provision.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package provision provides integration tests which rely on provisioning cluster per test. package provision import ( "context" "encoding/json" "errors" "fmt" "io" "net/netip" "os" "path/filepath" "regexp" "strings" "sync" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-kubernetes/kubernetes/ssa" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/go-retry/retry" sideronet "github.com/siderolabs/net" "github.com/stretchr/testify/suite" "go.yaml.in/yaml/v4" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" "github.com/siderolabs/talos/internal/integration/base" "github.com/siderolabs/talos/pkg/cluster/check" "github.com/siderolabs/talos/pkg/cluster/hydrophone" "github.com/siderolabs/talos/pkg/cluster/kubernetes" "github.com/siderolabs/talos/pkg/machinery/api/common" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" talosclient "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/access" "github.com/siderolabs/talos/pkg/provision/providers/qemu" ) var allSuites []suite.TestingSuite // GetAllSuites returns all the suites for provision test. // // Depending on build tags, this might return different lists. func GetAllSuites() []suite.TestingSuite { return allSuites } // Settings for provision tests. type Settings struct { // CIDR to use for provisioned clusters CIDR string // Registry mirrors to push to Talos config, in format `host=endpoint` RegistryMirrors base.StringList // MTU for the network. MTU int // VM parameters CPUs int64 MemMB int64 DiskGB uint64 // Node count for the tests ControlplaneNodes int WorkerNodes int // Target installer image registry TargetInstallImageRegistry string // Current version of the cluster (built in the CI pass) CurrentVersion string // Custom CNI URL to use. CustomCNIURL string // CNI bundle for QEMU provisioner. CNIBundleURL string } // DefaultSettings filled in by test runner. var DefaultSettings = Settings{ CIDR: "172.21.0.0/24", MTU: 1500, CPUs: 4, MemMB: 3 * 1024, DiskGB: 12, ControlplaneNodes: 3, WorkerNodes: 1, TargetInstallImageRegistry: "ghcr.io", CNIBundleURL: fmt.Sprintf("https://github.com/siderolabs/talos/releases/download/%s/talosctl-cni-bundle-%s.tar.gz", trimVersion(version.Tag), constants.ArchVariable), } func trimVersion(version string) string { // remove anything extra after semantic version core, `v0.3.2-1-abcd` -> `v0.3.2` return regexp.MustCompile(`(-\d+-g[0-9a-f]+)$`).ReplaceAllString(version, "") } var defaultNameservers = []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("1.1.1.1")} // BaseSuite provides base features for provision tests. type BaseSuite struct { suite.Suite base.TalosSuite provisioner provision.Provisioner configBundle *bundle.Bundle clusterAccess *access.Adapter controlPlaneEndpoint string //nolint:containedctx ctx context.Context ctxCancel context.CancelFunc stateDir string cniDir string } // SetupSuite ... func (suite *BaseSuite) SetupSuite() { // timeout for the whole test suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), time.Hour) var err error suite.provisioner, err = qemu.NewProvisioner(suite.ctx) suite.Require().NoError(err) } // TearDownSuite ... func (suite *BaseSuite) TearDownSuite() { if suite.clusterAccess != nil { suite.Assert().NoError(suite.clusterAccess.Close()) } if suite.Cluster != nil { suite.Assert().NoError(suite.provisioner.Destroy(suite.ctx, suite.Cluster)) } suite.ctxCancel() if suite.stateDir != "" { suite.Assert().NoError(os.RemoveAll(suite.stateDir)) } if suite.provisioner != nil { suite.Assert().NoError(suite.provisioner.Close()) } } // waitForClusterHealth asserts cluster health after any change. func (suite *BaseSuite) waitForClusterHealth() { runs := 1 singleNodeCluster := len(suite.Cluster.Info().Nodes) == 1 if singleNodeCluster { // run health check several times for single node clusters, // as self-hosted control plane is not stable after reboot runs = 3 } for run := range runs { if run > 0 { time.Sleep(15 * time.Second) } checkCtx, checkCtxCancel := context.WithTimeout(suite.ctx, 15*time.Minute) defer checkCtxCancel() suite.Require().NoError( check.Wait( checkCtx, suite.clusterAccess, check.DefaultClusterChecks(), check.StderrReporter(), ), ) } } func (suite *BaseSuite) untaint(name string) { client, err := suite.clusterAccess.K8sClient(suite.ctx) suite.Require().NoError(err) n, err := client.CoreV1().Nodes().Get(suite.ctx, name, metav1.GetOptions{}) suite.Require().NoError(err) oldData, err := json.Marshal(n) suite.Require().NoError(err) k := 0 for _, taint := range n.Spec.Taints { if taint.Key != constants.LabelNodeRoleControlPlane { n.Spec.Taints[k] = taint k++ } } n.Spec.Taints = n.Spec.Taints[:k] newData, err := json.Marshal(n) suite.Require().NoError(err) patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, corev1.Node{}) suite.Require().NoError(err) _, err = client.CoreV1().Nodes().Patch( suite.ctx, n.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, ) suite.Require().NoError(err) } func (suite *BaseSuite) assertSameVersionCluster(client *talosclient.Client, expectedVersion string) { nodes := xslices.Map(suite.Cluster.Info().Nodes, func(node provision.NodeInfo) string { return node.IPs[0].String() }) ctx := talosclient.WithNodes(suite.ctx, nodes...) var v *machineapi.VersionResponse err := retry.Constant( time.Minute, ).Retry( func() error { var e error v, e = client.Version(ctx) return retry.ExpectedError(e) }, ) suite.Require().NoError(err) suite.Require().Len(v.Messages, len(nodes)) for _, version := range v.Messages { suite.Assert().Equal(expectedVersion, version.Version.Tag) } } func (suite *BaseSuite) assertCmdlineContains(client *talosclient.Client, node string, expectedCmdlineContains string) { ctx := talosclient.WithNode(suite.ctx, node) cmdline, err := safe.ReaderGetByID[*runtime.KernelCmdline](ctx, client.COSI, runtime.KernelCmdlineID) suite.Require().NoError(err) suite.Assert().NotEmpty(cmdline, "expected cmdline to be not empty") suite.Assert().Contains(cmdline.TypedSpec().Cmdline, expectedCmdlineContains, "expected cmdline to contain %q", expectedCmdlineContains) } func (suite *BaseSuite) readVersion(nodeCtx context.Context, client *talosclient.Client) ( version string, err error, ) { var v *machineapi.VersionResponse v, err = client.Version(nodeCtx) if err != nil { return version, err } version = v.Messages[0].Version.Tag return version, err } type upgradeOptions struct { TargetInstallerImage string // Deprecated: staged upgrades are not supported by the new LifecycleService API. // Use the legacy MachineService.Upgrade path instead. UpgradeStage bool TargetVersion string } //nolint:gocyclo,cyclop func (suite *BaseSuite) upgradeNode(client *talosclient.Client, node provision.NodeInfo, options upgradeOptions) { suite.T().Logf("upgrading node %s", node.IPs[0]) ctx, cancel := context.WithCancel(suite.ctx) defer cancel() nodeCtx := talosclient.WithNodes(ctx, node.IPs[0].String()) // Staged upgrades are not supported by the new LifecycleService API, // so skip straight to the legacy path. if !options.UpgradeStage { if suite.tryUpgradeViaLifecycleService(nodeCtx, client, node, options) { // LifecycleService.Upgrade succeeded — trigger reboot and wait. suite.T().Logf("upgrade via LifecycleService succeeded, rebooting node %s", node.IPs[0]) suite.Require().NoError(client.Reboot(nodeCtx)) suite.waitForUpgrade(nodeCtx, client, node, options) return } suite.T().Logf("LifecycleService.Upgrade not available, falling back to legacy MachineService.Upgrade") } // Legacy path: MachineService.Upgrade (handles image pull, install, and reboot in one call). suite.upgradeNodeLegacy(nodeCtx, client, options) suite.waitForUpgrade(nodeCtx, client, node, options) } // tryUpgradeViaLifecycleService attempts to upgrade via the new streaming // LifecycleService.Upgrade API. It pre-pulls the installer image, then calls // the streaming RPC. Returns true on success, false if the server returned // codes.Unimplemented (indicating the API is not available). // //nolint:gocyclo func (suite *BaseSuite) tryUpgradeViaLifecycleService( nodeCtx context.Context, c *talosclient.Client, node provision.NodeInfo, options upgradeOptions, ) bool { // Step 1: Pre-pull the installer image into the system containerd namespace. suite.T().Logf("pre-pulling installer image %q on node %s", options.TargetInstallerImage, node.IPs[0]) containerdInstance := &common.ContainerdInstance{ Driver: common.ContainerDriver_CONTAINERD, Namespace: common.ContainerdNamespace_NS_SYSTEM, } nodes := []string{node.IPs[0].String()} if !suite.pullInstallerImageViaLifecycleService(nodeCtx, c, containerdInstance, nodes, options.TargetInstallerImage) { return false } if !suite.upgradeViaLifecycleService(nodeCtx, c, containerdInstance, nodes, options.TargetInstallerImage) { return false } return true } type provisionStreamResponse[ResponseT any] struct { Node string Payload *ResponseT Err error } func streamPerNode[ResponseT any]( ctx context.Context, nodes []string, initiate func(context.Context) (grpc.ServerStreamingClient[ResponseT], error), ) <-chan provisionStreamResponse[ResponseT] { responseCh := make(chan provisionStreamResponse[ResponseT]) var wg sync.WaitGroup wg.Add(len(nodes)) for _, node := range nodes { go func() { defer wg.Done() stream, err := initiate(talosclient.WithNode(ctx, node)) if err != nil { sendProvisionResponse(ctx, responseCh, provisionStreamResponse[ResponseT]{ Node: node, Err: err, }) return } for { payload, err := stream.Recv() if err != nil { if errors.Is(err, io.EOF) { return } sendProvisionResponse(ctx, responseCh, provisionStreamResponse[ResponseT]{ Node: node, Err: err, }) return } if !sendProvisionResponse(ctx, responseCh, provisionStreamResponse[ResponseT]{ Node: node, Payload: payload, }) { return } } }() } go func() { wg.Wait() close(responseCh) }() return responseCh } func sendProvisionResponse[ResponseT any]( ctx context.Context, responseCh chan<- provisionStreamResponse[ResponseT], resp provisionStreamResponse[ResponseT], ) bool { select { case <-ctx.Done(): return false case responseCh <- resp: return true } } func (suite *BaseSuite) pullInstallerImageViaLifecycleService( ctx context.Context, c *talosclient.Client, containerdInstance *common.ContainerdInstance, nodes []string, imageRef string, ) bool { responseChan := streamPerNode(ctx, nodes, func(ctx context.Context) (grpc.ServerStreamingClient[machineapi.ImageServicePullResponse], error) { return c.ImageClient.Pull(ctx, &machineapi.ImageServicePullRequest{ Containerd: containerdInstance, ImageRef: imageRef, }) }, ) var ( finishedPulls = map[string]string{} errs error ) for resp := range responseChan { if resp.Err != nil { if status.Code(resp.Err) == codes.Unimplemented { return false } errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) continue } if resp.Payload == nil { errs = errors.Join(errs, fmt.Errorf("empty image pull response from node %s", resp.Node)) continue } switch payload := resp.Payload.Response.(type) { case *machineapi.ImageServicePullResponse_Name: finishedPulls[resp.Node] = payload.Name case *machineapi.ImageServicePullResponse_PullProgress: default: errs = errors.Join(errs, fmt.Errorf("unexpected image pull response type from node %s: %T", resp.Node, payload)) } } suite.Require().NoError(errs, "error during image pull") for node, imageName := range finishedPulls { suite.T().Logf("node %s pulled image %s", node, imageName) } return true } func (suite *BaseSuite) upgradeViaLifecycleService( ctx context.Context, c *talosclient.Client, containerdInstance *common.ContainerdInstance, nodes []string, imageRef string, ) bool { responseChan := streamPerNode(ctx, nodes, func(ctx context.Context) (grpc.ServerStreamingClient[machineapi.LifecycleServiceUpgradeResponse], error) { return c.LifecycleClient.Upgrade(ctx, &machineapi.LifecycleServiceUpgradeRequest{ Containerd: containerdInstance, Source: &machineapi.InstallArtifactsSource{ ImageName: imageRef, }, }) }, ) var ( exitCodes = map[string]int32{} errs error ) for resp := range responseChan { if resp.Err != nil { if status.Code(resp.Err) == codes.Unimplemented { return false } errs = errors.Join(errs, fmt.Errorf("error from node %s: %w", resp.Node, resp.Err)) continue } if resp.Payload == nil { errs = errors.Join(errs, fmt.Errorf("empty upgrade response from node %s", resp.Node)) continue } switch payload := resp.Payload.GetProgress().GetResponse().(type) { case *machineapi.LifecycleServiceInstallProgress_Message: suite.T().Logf("upgrade log (%s): %s", resp.Node, payload.Message) case *machineapi.LifecycleServiceInstallProgress_ExitCode: exitCodes[resp.Node] = payload.ExitCode default: errs = errors.Join(errs, fmt.Errorf("unexpected upgrade response type from node %s: %T", resp.Node, payload)) } } suite.Require().NoError(errs, "error during LifecycleService.Upgrade") for node, exitCode := range exitCodes { suite.Require().Equalf(int32(0), exitCode, "LifecycleService.Upgrade exited with non-zero code on node %s", node) } return true } // upgradeNodeLegacy performs an upgrade using the legacy (deprecated) MachineService.Upgrade // unary API, which handles image pull, install, and reboot in a single call. // //nolint:gocyclo func (suite *BaseSuite) upgradeNodeLegacy( nodeCtx context.Context, c *talosclient.Client, options upgradeOptions, ) { var ( resp *machineapi.UpgradeResponse err error ) err = retry.Constant(time.Minute, retry.WithUnits(10*time.Second)).Retry( func() error { resp, err = c.Upgrade( //nolint:staticcheck // using deprecated API for testing backward compatibility nodeCtx, options.TargetInstallerImage, options.UpgradeStage, false, ) if err != nil { if strings.Contains(err.Error(), "leader changed") { return retry.ExpectedError(err) } if strings.Contains(err.Error(), "failed to acquire upgrade lock") { return retry.ExpectedError(err) } return err } return nil }, ) suite.Require().NoError(err) suite.Require().Equal("Upgrade request received", resp.Messages[0].Ack) actorID := resp.Messages[0].ActorId eventCh := make(chan talosclient.EventResult) // watch for events suite.Require().NoError(c.EventsWatchV2(nodeCtx, eventCh, talosclient.WithActorID(actorID), talosclient.WithTailEvents(-1))) waitTimer := time.NewTimer(5 * time.Minute) defer waitTimer.Stop() waitLoop: for { select { case ev := <-eventCh: suite.Require().NoError(ev.Error) switch msg := ev.Event.Payload.(type) { case *machineapi.SequenceEvent: if msg.Error != nil { suite.FailNow("upgrade failed", "%s: %s", msg.Error.Message, msg.Error.Code) } case *machineapi.PhaseEvent: if msg.Action == machineapi.PhaseEvent_START && msg.Phase == "kexec" { // about to be rebooted break waitLoop } if msg.Action == machineapi.PhaseEvent_STOP { suite.T().Logf("upgrade phase %q finished", msg.Phase) } } case <-waitTimer.C: suite.FailNow("timeout waiting for upgrade to finish") case <-nodeCtx.Done(): suite.FailNow("context canceled") } } } // waitForUpgrade waits for the node to come back up after a reboot with the // expected target version, then verifies cluster health. This is shared by // both the new LifecycleService and the legacy MachineService upgrade paths. func (suite *BaseSuite) waitForUpgrade( nodeCtx context.Context, c *talosclient.Client, node provision.NodeInfo, options upgradeOptions, ) { // wait for the apid to be shut down time.Sleep(10 * time.Second) // wait for the version to be equal to target version var err error suite.Require().NoError( retry.Constant(10 * time.Minute).Retry( func() error { var version string version, err = suite.readVersion(nodeCtx, c) if err != nil { // API might be unresponsive during upgrade return retry.ExpectedError(err) } if version != options.TargetVersion { // upgrade not finished yet return retry.ExpectedErrorf( "node %q version doesn't match expected: expected %q, got %q", node.IPs[0].String(), options.TargetVersion, version, ) } return nil }, ), ) suite.waitForClusterHealth() } func (suite *BaseSuite) upgradeKubernetes(fromVersion, toVersion string, skipKubeletUpgrade bool) { if fromVersion == toVersion { suite.T().Logf("skipping Kubernetes upgrade, as versions are equal %q -> %q", fromVersion, toVersion) return } suite.T().Logf("upgrading Kubernetes: %q -> %q", fromVersion, toVersion) path, err := upgrade.NewPath(fromVersion, toVersion) suite.Require().NoError(err) options := kubernetes.UpgradeOptions{ Path: path, ControlPlaneEndpoint: suite.controlPlaneEndpoint, UpgradeKubelet: !skipKubeletUpgrade, PrePullImages: true, KubeletImage: constants.KubeletImage, APIServerImage: constants.KubernetesAPIServerImage, ControllerManagerImage: constants.KubernetesControllerManagerImage, SchedulerImage: constants.KubernetesSchedulerImage, ProxyImage: constants.KubeProxyImage, EncoderOpt: encoder.WithComments(encoder.CommentsAll), InventoryPolicy: ssa.InventoryPolicyAdoptIfNoInventory, ReconcileTimeout: 3 * time.Minute, } suite.Require().NoError(kubernetes.Upgrade(suite.ctx, suite.clusterAccess, options)) } type clusterOptions struct { ClusterName string ControlplaneNodes int WorkerNodes int InjectExtraKernelArgs *procfs.Cmdline SourceKernelPath string SourceInitramfsPath string SourceDiskImagePath string SourceISOPath string SourceInstallerImage string SourceVersion string SourceK8sVersion string WithEncryption bool WithBios bool WithApplyConfig bool WithSkipInjectingConfig bool } // setupCluster provisions source clusters and waits for health. // //nolint:gocyclo func (suite *BaseSuite) setupCluster(options clusterOptions) { defaultStateDir, err := clientconfig.GetTalosDirectory() suite.Require().NoError(err) suite.stateDir = filepath.Join(defaultStateDir, "clusters") suite.cniDir = filepath.Join(defaultStateDir, "cni") cidr, err := netip.ParsePrefix(DefaultSettings.CIDR) suite.Require().NoError(err) var gatewayIP netip.Addr gatewayIP, err = sideronet.NthIPInNetwork(cidr, 1) suite.Require().NoError(err) ips := make([]netip.Addr, options.ControlplaneNodes+options.WorkerNodes) for i := range ips { ips[i], err = sideronet.NthIPInNetwork(cidr, i+2) suite.Require().NoError(err) } suite.T().Logf("initializing provisioner with cluster name %q, state directory %q", options.ClusterName, suite.stateDir) request := provision.ClusterRequest{ Name: options.ClusterName, Network: provision.NetworkRequest{ Name: options.ClusterName, CIDRs: []netip.Prefix{cidr}, GatewayAddrs: []netip.Addr{gatewayIP}, MTU: DefaultSettings.MTU, Nameservers: defaultNameservers, CNI: provision.CNIConfig{ BinPath: []string{filepath.Join(suite.cniDir, "bin")}, ConfDir: filepath.Join(suite.cniDir, "conf.d"), CacheDir: filepath.Join(suite.cniDir, "cache"), BundleURL: DefaultSettings.CNIBundleURL, }, }, SelfExecutable: suite.TalosctlPath, StateDirectory: suite.stateDir, } switch { case options.SourceISOPath != "": request.ISOPath = options.SourceISOPath case options.SourceDiskImagePath != "": request.DiskImagePath = options.SourceDiskImagePath default: request.KernelPath = options.SourceKernelPath request.InitramfsPath = options.SourceInitramfsPath } suite.controlPlaneEndpoint = suite.provisioner.GetExternalKubernetesControlPlaneEndpoint(request.Network, constants.DefaultControlPlanePort) versionContract, err := config.ParseContractFromVersion(options.SourceVersion) suite.Require().NoError(err) genOptions, bundleOptions := suite.provisioner.GenOptions(request.Network, versionContract) for _, registryMirror := range DefaultSettings.RegistryMirrors { parts := strings.Split(registryMirror, "=") suite.Require().Len(parts, 2) genOptions = append(genOptions, generate.WithRegistryMirror(parts[0], parts[1])) } controlplaneEndpoints := make([]string, options.ControlplaneNodes) for i := range controlplaneEndpoints { controlplaneEndpoints[i] = ips[i].String() } if DefaultSettings.CustomCNIURL != "" { genOptions = append( genOptions, generate.WithClusterCNIConfig( &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{DefaultSettings.CustomCNIURL}, }, ), ) } var extraPatches []configpatcher.Patch if options.WithEncryption { if versionContract.VolumeConfigEncryptionSupported() { // use modern encryption config stateCfg := block.NewVolumeConfigV1Alpha1() stateCfg.MetaName = constants.StatePartitionLabel stateCfg.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 stateCfg.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyNodeID: &block.EncryptionKeyNodeID{}, }, } ephemeralCfg := block.NewVolumeConfigV1Alpha1() ephemeralCfg.MetaName = constants.EphemeralPartitionLabel ephemeralCfg.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 ephemeralCfg.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyNodeID: &block.EncryptionKeyNodeID{}, KeyLockToSTATE: new(true), }, } ctr, err := container.New(stateCfg, ephemeralCfg) suite.Require().NoError(err) extraPatches = append(extraPatches, configpatcher.NewStrategicMergePatch(ctr)) } else { // use legacy encryption config diskEncryptionConfig := &v1alpha1.SystemDiskEncryptionConfig{ StatePartition: &v1alpha1.EncryptionConfig{ EncryptionProvider: encryption.LUKS2, EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeySlot: 0, KeyNodeID: &v1alpha1.EncryptionKeyNodeID{}, }, }, }, EphemeralPartition: &v1alpha1.EncryptionConfig{ EncryptionProvider: encryption.LUKS2, EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeySlot: 0, KeyNodeID: &v1alpha1.EncryptionKeyNodeID{}, }, }, }, } patchRaw := map[string]any{ "machine": map[string]any{ "systemDiskEncryption": diskEncryptionConfig, }, } patchData, err := yaml.Marshal(patchRaw) suite.Require().NoError(err) patch, err := configpatcher.LoadPatch(patchData) suite.Require().NoError(err) extraPatches = append(extraPatches, patch) } } suite.configBundle, err = bundle.NewBundle( append([]bundle.Option{ bundle.WithInputOptions( &bundle.InputOptions{ ClusterName: options.ClusterName, Endpoint: suite.controlPlaneEndpoint, KubeVersion: options.SourceK8sVersion, GenOptions: append( genOptions, generate.WithEndpointList(controlplaneEndpoints), generate.WithInstallImage(options.SourceInstallerImage), generate.WithDNSDomain("cluster.local"), generate.WithVersionContract(versionContract), ), }, ), bundle.WithPatch(extraPatches), }, bundleOptions..., )..., ) suite.Require().NoError(err) for i := range options.ControlplaneNodes { request.Nodes = append( request.Nodes, provision.NodeRequest{ Name: fmt.Sprintf("control-plane-%d", i+1), Type: machine.TypeControlPlane, IPs: []netip.Addr{ips[i]}, Memory: DefaultSettings.MemMB * 1024 * 1024, NanoCPUs: DefaultSettings.CPUs * 1000 * 1000 * 1000, Disks: []*provision.Disk{ { Size: DefaultSettings.DiskGB * 1024 * 1024 * 1024, }, }, Config: suite.configBundle.ControlPlane(), SDStubKernelArgs: options.InjectExtraKernelArgs, SkipInjectingConfig: options.WithSkipInjectingConfig, }, ) } for i := 1; i <= options.WorkerNodes; i++ { request.Nodes = append( request.Nodes, provision.NodeRequest{ Name: fmt.Sprintf("worker-%d", i), Type: machine.TypeWorker, IPs: []netip.Addr{ips[options.ControlplaneNodes+i-1]}, Memory: DefaultSettings.MemMB * 1024 * 1024, NanoCPUs: DefaultSettings.CPUs * 1000 * 1000 * 1000, Disks: []*provision.Disk{ { Size: DefaultSettings.DiskGB * 1024 * 1024 * 1024, }, }, Config: suite.configBundle.Worker(), SDStubKernelArgs: options.InjectExtraKernelArgs, SkipInjectingConfig: options.WithSkipInjectingConfig, }, ) } provisionerOptions := []provision.Option{ provision.WithBootlader(true), provision.WithUEFI(!options.WithBios), provision.WithTalosConfig(suite.configBundle.TalosConfig()), } suite.Cluster, err = suite.provisioner.Create( suite.ctx, request, provisionerOptions..., ) suite.Require().NoError(err) if options.WithApplyConfig { clusterAccess := access.NewAdapter(suite.Cluster, provisionerOptions...) defer clusterAccess.Close() //nolint:errcheck if err := clusterAccess.ApplyConfig(suite.ctx, request.Nodes, request.SiderolinkRequest, os.Stderr); err != nil { suite.FailNow("failed to apply config", err.Error()) } } c, err := clientconfig.Open("") suite.Require().NoError(err) c.Merge(suite.configBundle.TalosConfig()) suite.Require().NoError(c.Save("")) suite.clusterAccess = access.NewAdapter(suite.Cluster, provision.WithTalosConfig(suite.configBundle.TalosConfig())) if !options.WithSkipInjectingConfig { suite.Require().NoError(suite.clusterAccess.Bootstrap(suite.ctx, os.Stdout)) suite.waitForClusterHealth() } } // runE2E runs e2e test on the cluster. func (suite *BaseSuite) runE2E(k8sVersion string) { options := hydrophone.DefaultOptions() options.KubernetesVersion = k8sVersion suite.Assert().NoError(hydrophone.Run(suite.ctx, suite.clusterAccess, options)) } ================================================ FILE: internal/integration/provision/upgrade.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration_provision package provision import ( "fmt" "path/filepath" "github.com/cosi-project/runtime/pkg/resource/rtestutils" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/cmd/talosctl/pkg/mgmt/helpers" "github.com/siderolabs/talos/pkg/images" talosclient "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) //nolint:maligned type upgradeSpec struct { ShortName string InjectExtraKernelArgs *procfs.Cmdline SourceKernelPath string SourceInitramfsPath string SourceDiskImagePath string SourceISOPath string SourceInstallerImage string SourceVersion string SourceK8sVersion string TargetInstallerImage string TargetVersion string TargetK8sVersion string TargetCmdlineContains string SkipKubeletUpgrade bool ControlplaneNodes int WorkerNodes int // Deprecated: staged upgrades are not supported by the new LifecycleService API. // Use the legacy MachineService.Upgrade path instead. UpgradeStage bool WithEncryption bool WithBios bool WithApplyConfig bool WithEnforcing bool } const ( // These versions should be kept in sync with Makefile variable RELEASES. previousRelease = "v1.11.6" stableRelease = "v1.12.0" // or soon-to-be-stable // The current version (the one being built on CI) is DefaultSettings.CurrentVersion. // Command to find Kubernetes version for past releases: // // git show ${TAG}:pkg/machinery/constants/constants.go | grep KubernetesVersion previousK8sVersion = "1.34.1" // constants.DefaultKubernetesVersion in the previousRelease stableK8sVersion = "1.35.0" // constants.DefaultKubernetesVersion in the stableRelease currentK8sVersion = constants.DefaultKubernetesVersion ) // upgradePreviousToStable upgrades from the previous Talos release to the stable release. func upgradePreviousToStable() upgradeSpec { return upgradeSpec{ ShortName: fmt.Sprintf("%s-%s", previousRelease, stableRelease), SourceKernelPath: helpers.ArtifactPath(filepath.Join(trimVersion(previousRelease), constants.KernelAsset)), SourceInitramfsPath: helpers.ArtifactPath( filepath.Join( trimVersion(previousRelease), constants.InitramfsAsset, ), ), SourceInstallerImage: fmt.Sprintf("%s:%s", "ghcr.io/siderolabs/installer", previousRelease), SourceVersion: previousRelease, SourceK8sVersion: previousK8sVersion, TargetInstallerImage: fmt.Sprintf("%s:%s", "ghcr.io/siderolabs/installer", stableRelease), TargetVersion: stableRelease, TargetK8sVersion: stableK8sVersion, ControlplaneNodes: DefaultSettings.ControlplaneNodes, WorkerNodes: DefaultSettings.WorkerNodes, } } // upgradeStableToCurrent upgrades from the stable Talos release to the current version. func upgradeStableToCurrent() upgradeSpec { return upgradeSpec{ ShortName: fmt.Sprintf("%s-%s", stableRelease, DefaultSettings.CurrentVersion), SourceKernelPath: helpers.ArtifactPath(filepath.Join(trimVersion(stableRelease), constants.KernelAsset)), SourceInitramfsPath: helpers.ArtifactPath(filepath.Join(trimVersion(stableRelease), constants.InitramfsAsset)), SourceInstallerImage: fmt.Sprintf("%s:%s", "ghcr.io/siderolabs/installer", stableRelease), SourceVersion: stableRelease, SourceK8sVersion: stableK8sVersion, TargetInstallerImage: fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ), TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: DefaultSettings.ControlplaneNodes, WorkerNodes: DefaultSettings.WorkerNodes, WithEncryption: true, } } // upgradeCurrentToCurrent upgrades the current version to itself. func upgradeCurrentToCurrent() upgradeSpec { installerImage := fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ) return upgradeSpec{ ShortName: fmt.Sprintf("%s-same-ver", DefaultSettings.CurrentVersion), SourceKernelPath: helpers.ArtifactPath(constants.KernelAssetWithArch), SourceInitramfsPath: helpers.ArtifactPath(constants.InitramfsAssetWithArch), SourceInstallerImage: installerImage, SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: currentK8sVersion, TargetInstallerImage: installerImage, TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: DefaultSettings.ControlplaneNodes, WorkerNodes: DefaultSettings.WorkerNodes, WithEncryption: true, } } // upgradeCurrentToCurrentBios upgrades the current version to itself without UEFI. func upgradeCurrentToCurrentBios() upgradeSpec { installerImage := fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ) return upgradeSpec{ ShortName: fmt.Sprintf("%s-same-ver-bios", DefaultSettings.CurrentVersion), SourceDiskImagePath: helpers.ArtifactPath("metal-amd64.raw.zst"), SourceInstallerImage: installerImage, SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: currentK8sVersion, TargetInstallerImage: installerImage, TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: DefaultSettings.ControlplaneNodes, WorkerNodes: DefaultSettings.WorkerNodes, WithEncryption: true, WithBios: true, WithApplyConfig: true, } } // upgradeStableToCurrentPreserveStage upgrades from the stable Talos release to the current version for single-node cluster with preserve and stage. func upgradeStableToCurrentPreserveStage() upgradeSpec { return upgradeSpec{ ShortName: fmt.Sprintf("prsrv-stg-%s-%s", stableRelease, DefaultSettings.CurrentVersion), SourceKernelPath: helpers.ArtifactPath(filepath.Join(trimVersion(stableRelease), constants.KernelAsset)), SourceInitramfsPath: helpers.ArtifactPath(filepath.Join(trimVersion(stableRelease), constants.InitramfsAsset)), SourceInstallerImage: fmt.Sprintf("%s:%s", "ghcr.io/siderolabs/installer", stableRelease), SourceVersion: stableRelease, SourceK8sVersion: stableK8sVersion, TargetInstallerImage: fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ), TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: 1, WorkerNodes: 0, UpgradeStage: true, } } func upgradeCurrentToCurrentNewCmdline() upgradeSpec { installerImage := fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ) targetInstallerImage := installerImage + "-extra-cmdline" return upgradeSpec{ ShortName: fmt.Sprintf("%s-same-ver-extra-cmdline", DefaultSettings.CurrentVersion), SourceISOPath: helpers.ArtifactPath("metal-amd64.iso"), SourceInstallerImage: installerImage, SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: currentK8sVersion, TargetInstallerImage: targetInstallerImage, TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: 1, WorkerNodes: 0, TargetCmdlineContains: "talos.extra_cmdline=extra-super-cmdline", WithApplyConfig: true, } } func upgradeCurrentToCurrentEnforcing() upgradeSpec { installerImage := fmt.Sprintf( "%s/%s:%s", DefaultSettings.TargetInstallImageRegistry, images.DefaultInstallerImageName, DefaultSettings.CurrentVersion, ) return upgradeSpec{ ShortName: fmt.Sprintf("%s-same-ver-enforcing", DefaultSettings.CurrentVersion), InjectExtraKernelArgs: procfs.NewCmdline("enforcing=1"), SourceISOPath: helpers.ArtifactPath("metal-amd64.iso"), SourceInstallerImage: installerImage, SourceVersion: DefaultSettings.CurrentVersion, SourceK8sVersion: currentK8sVersion, TargetInstallerImage: installerImage, TargetVersion: DefaultSettings.CurrentVersion, TargetK8sVersion: currentK8sVersion, ControlplaneNodes: 1, WorkerNodes: 0, TargetCmdlineContains: "enforcing=1", WithApplyConfig: true, WithEnforcing: true, } } // UpgradeSuite ... type UpgradeSuite struct { BaseSuite specGen func() upgradeSpec spec upgradeSpec track int } // SetupSuite ... func (suite *UpgradeSuite) SetupSuite() { // call generate late in the flow, as it needs to pick up settings overridden by test runner suite.spec = suite.specGen() suite.T().Logf("upgrade spec = %v", suite.spec) suite.BaseSuite.SetupSuite() } // runE2E runs e2e test on the cluster. func (suite *UpgradeSuite) runE2E(k8sVersion string) { if suite.spec.WorkerNodes == 0 { // no worker nodes, should make masters schedulable suite.untaint("control-plane-1") } suite.BaseSuite.runE2E(k8sVersion) } // TestRolling performs rolling upgrade starting with master nodes. func (suite *UpgradeSuite) TestRolling() { suite.setupCluster(clusterOptions{ ClusterName: suite.spec.ShortName, ControlplaneNodes: suite.spec.ControlplaneNodes, WorkerNodes: suite.spec.WorkerNodes, InjectExtraKernelArgs: suite.spec.InjectExtraKernelArgs, SourceKernelPath: suite.spec.SourceKernelPath, SourceInitramfsPath: suite.spec.SourceInitramfsPath, SourceDiskImagePath: suite.spec.SourceDiskImagePath, SourceISOPath: suite.spec.SourceISOPath, SourceInstallerImage: suite.spec.SourceInstallerImage, SourceVersion: suite.spec.SourceVersion, SourceK8sVersion: suite.spec.SourceK8sVersion, WithEncryption: suite.spec.WithEncryption, WithBios: suite.spec.WithBios, WithApplyConfig: suite.spec.WithApplyConfig, }) client, err := suite.clusterAccess.Client() suite.Require().NoError(err) // verify initial cluster version suite.assertSameVersionCluster(client, suite.spec.SourceVersion) // verify enforcing state for _, node := range suite.Cluster.Info().Nodes { rtestutils.AssertResource( talosclient.WithNode(suite.ctx, node.IPs[0].String()), suite.T(), client.COSI, runtime.SecurityStateID, func(r *runtime.SecurityState, asrt *assert.Assertions) { asrt.Equal(suite.spec.WithEnforcing, r.TypedSpec().SELinuxState == runtime.SELinuxStateEnforcing) }, ) } options := upgradeOptions{ TargetInstallerImage: suite.spec.TargetInstallerImage, UpgradeStage: suite.spec.UpgradeStage, TargetVersion: suite.spec.TargetVersion, } // upgrade master nodes for _, node := range suite.Cluster.Info().Nodes { if node.Type == machine.TypeInit || node.Type == machine.TypeControlPlane { suite.upgradeNode(client, node, options) } } // upgrade worker nodes for _, node := range suite.Cluster.Info().Nodes { if node.Type == machine.TypeWorker { suite.upgradeNode(client, node, options) } } // verify final cluster version suite.assertSameVersionCluster(client, suite.spec.TargetVersion) // verify enforcing state for _, node := range suite.Cluster.Info().Nodes { rtestutils.AssertResource( talosclient.WithNode(suite.ctx, node.IPs[0].String()), suite.T(), client.COSI, runtime.SecurityStateID, func(r *runtime.SecurityState, asrt *assert.Assertions) { asrt.Equal(suite.spec.WithEnforcing, r.TypedSpec().SELinuxState == runtime.SELinuxStateEnforcing) }, ) } // upgrade Kubernetes if required suite.upgradeKubernetes(suite.spec.SourceK8sVersion, suite.spec.TargetK8sVersion, suite.spec.SkipKubeletUpgrade) if suite.spec.TargetCmdlineContains != "" { for _, node := range suite.Cluster.Info().Nodes { suite.assertCmdlineContains(client, node.IPs[0].String(), suite.spec.TargetCmdlineContains) } } // run e2e test suite.runE2E(suite.spec.TargetK8sVersion) } // SuiteName ... func (suite *UpgradeSuite) SuiteName() string { if suite.spec.ShortName == "" { suite.spec = suite.specGen() } return fmt.Sprintf("provision.UpgradeSuite.%s-TR%d", suite.spec.ShortName, suite.track) } func init() { allSuites = append( allSuites, &UpgradeSuite{specGen: upgradePreviousToStable, track: 0}, &UpgradeSuite{specGen: upgradeStableToCurrent, track: 1}, &UpgradeSuite{specGen: upgradeCurrentToCurrent, track: 2}, &UpgradeSuite{specGen: upgradeCurrentToCurrentBios, track: 0}, &UpgradeSuite{specGen: upgradeStableToCurrentPreserveStage, track: 1}, &UpgradeSuite{specGen: upgradeCurrentToCurrentNewCmdline, track: 2}, &UpgradeSuite{specGen: upgradeCurrentToCurrentEnforcing, track: 1}, ) } ================================================ FILE: internal/integration/version_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build integration // Package integration_test contains core runners for integration tests package integration_test import ( "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/integration/base" ) // VersionSuite. type VersionSuite struct { suite.Suite base.TalosSuite } func (suite *VersionSuite) SuiteName() string { return "VersionSuite" } func (suite *VersionSuite) TestExpectedVersion() { const versionRegex = `v([0-9]+)\.([0-9]+)\.([0-9]+)(-[0-9]+-[a-z]+\.[0-9]+)?(-.g[a-f0-9]+)?(-dirty)?` suite.Assert().Regexp(versionRegex, suite.Version) } func init() { allSuites = append(allSuites, new(VersionSuite)) } ================================================ FILE: internal/pkg/capability/capability.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package capability provides utility functions to work with capabilities. package capability import ( "strings" "github.com/siderolabs/gen/maps" "kernel.org/pub/linux/libs/security/libcap/cap" "github.com/siderolabs/talos/pkg/machinery/constants" ) // AllCapabilitiesSet returns the set of all available capabilities. // // Returned capabilities are in UPPERCASE. func AllCapabilitiesSet() map[string]struct{} { capabilities := make(map[string]struct{}) for v := cap.Value(0); v < cap.MaxBits(); v++ { if set, _ := cap.GetBound(v); set { //nolint:errcheck capabilities[strings.ToUpper(v.String())] = struct{}{} } } return capabilities } // AllCapabilitiesSetLowercase returns the set of all available capabilities. // // Returned capabilities are in lowercase. func AllCapabilitiesSetLowercase() map[string]struct{} { return maps.Map(AllCapabilitiesSet(), func(capability string, _ struct{}) (string, struct{}) { return strings.ToLower(capability), struct{}{} }) } // AllGrantableCapabilities returns list of capabilities that can be granted to the container based on // process bounding capabilities. // // Returned capabilities are in UPPERCASE. func AllGrantableCapabilities() []string { allCapabilities := AllCapabilitiesSet() for dropped := range constants.DefaultDroppedCapabilities { delete(allCapabilities, strings.ToUpper(dropped)) } return maps.Keys(allCapabilities) } ================================================ FILE: internal/pkg/cgroup/cgroup.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cgroup provides cgroup utilities to handle nested cgroups. // // When Talos runs in a container, it might either share or not share the host cgroup namespace. // If the cgroup namespace is not shared, PID 1 will appear in cgroup '/', otherwise it will be // part of some pre-existing cgroup hierarchy. // // When Talos is running in a non-container mode, it is always at the root of the cgroup hierarchy. // // This package provides a transparent way to handle nested cgroups by providing a Path() function // which returns the correct cgroup path based on the cgroup hierarchy available. package cgroup import ( "fmt" "os" "path/filepath" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-debug" "github.com/siderolabs/talos/internal/pkg/containermode" "github.com/siderolabs/talos/pkg/machinery/constants" ) // CommonCgroup interface presents a cgroup manager, be it v1 or v2 // It can be further extended once new methods are required. type CommonCgroup interface { Delete() error } var root = "/" // InitRoot initializes the root cgroup path. // // This function should be called once at the beginning of the program, after the cgroup // filesystem is mounted. // // This function only supports cgroupv2 nesting. func InitRoot() error { if cgroups.Mode() != cgroups.Unified { return nil } var err error root, err = cgroup2.NestedGroupPath("/") return err } // Root returns the root cgroup path. func Root() string { return root } // Path returns the path to the // // This function handles the case when the cgroups are nested. func Path(cgroupPath string) string { if cgroups.Mode() != cgroups.Unified { return cgroupPath } return filepath.Join(root, cgroupPath) } func zeroIfRace[T any](v T) T { if debug.RaceEnabled { var zeroT T return zeroT } return v } //nolint:gocyclo func getCgroupV2Resources(name string) *cgroup2.Resources { switch name { case constants.CgroupInit: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupInitReservedMemory)), Low: new(int64(constants.CgroupInitReservedMemory * 2)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupInitMillicores))), }, } case constants.CgroupSystem: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupSystemReservedMemory)), Low: new(int64(constants.CgroupSystemReservedMemory * 2)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupSystemMillicores))), }, } case constants.CgroupSystemDebug: return &cgroup2.Resources{} // no limits for debug cgroup case constants.CgroupSystemRuntime: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupSystemRuntimeReservedMemory)), Low: new(int64(constants.CgroupSystemRuntimeReservedMemory * 2)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupSystemRuntimeMillicores))), }, } case constants.CgroupUdevd: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupUdevdReservedMemory)), Low: new(int64(constants.CgroupUdevdReservedMemory * 2)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupUdevdMillicores))), }, } case constants.CgroupPodRuntimeRoot: return &cgroup2.Resources{ CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupPodRuntimeRootMillicores))), }, } case constants.CgroupPodRuntime: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupPodRuntimeReservedMemory)), Low: new(int64(constants.CgroupPodRuntimeReservedMemory * 2)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupPodRuntimeMillicores))), }, } case constants.CgroupKubelet: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupKubeletReservedMemory)), Low: new(int64(constants.CgroupKubeletReservedMemory * 2)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupKubeletMillicores))), }, } case constants.CgroupEtcd: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Low: new(int64(constants.CgroupEtcdReservedMemory)), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupEtcdMillicores))), }, } case constants.CgroupDashboard: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Max: zeroIfRace(new(int64(constants.CgroupDashboardMaxMemory))), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupDashboardMillicores))), }, } case constants.CgroupApid: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupApidReservedMemory)), Low: new(int64(constants.CgroupApidReservedMemory * 2)), Max: zeroIfRace(new(int64(constants.CgroupApidMaxMemory))), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupApidMillicores))), }, } case constants.CgroupTrustd: return &cgroup2.Resources{ Memory: &cgroup2.Memory{ Min: new(int64(constants.CgroupTrustdReservedMemory)), Low: new(int64(constants.CgroupTrustdReservedMemory * 2)), Max: zeroIfRace(new(int64(constants.CgroupTrustdMaxMemory))), Swap: new(int64(0)), }, CPU: &cgroup2.CPU{ Weight: new(MillicoresToCPUWeight(MilliCores(constants.CgroupTrustdMillicores))), }, } } return &cgroup2.Resources{} } // CreateCgroup creates a cgroup, with resources limits if configured and supported. func CreateCgroup(name string) (CommonCgroup, error) { resources := getCgroupV2Resources(name) if containermode.InContainer() { // don't attempt to set resources in container mode, as they might conflict with the parent cgroup tree resources = &cgroup2.Resources{} } if cgroups.Mode() == cgroups.Unified { cg, err := cgroup2.NewManager(constants.CgroupMountPath, Path(name), resources) if err != nil { return nil, fmt.Errorf("failed to create cgroup: %w", err) } if name == constants.CgroupInit { if err := cg.AddProc(uint64(os.Getpid())); err != nil { return nil, fmt.Errorf("failed to move init process to cgroup: %w", err) } } return cg, nil } cg, err := cgroup1.New(cgroup1.StaticPath(name), &specs.LinuxResources{}) if err != nil { return nil, fmt.Errorf("failed to create cgroup: %w", err) } if name == constants.CgroupInit { if err := cg.Add(cgroup1.Process{ Pid: os.Getpid(), }); err != nil { return nil, fmt.Errorf("failed to move init process to cgroup: %w", err) } } return cg, nil } ================================================ FILE: internal/pkg/cgroup/cpu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroup import ( "runtime" "sync" "github.com/google/cadvisor/utils/sysfs" "github.com/google/cadvisor/utils/sysinfo" ) var availableCPUCores = sync.OnceValue(func() int { _, cores, err := sysinfo.GetNodesInfo(sysfs.NewRealSysFs()) if err != nil || cores < 1 { return runtime.NumCPU() } return cores }) // MilliCores represents a CPU value in milli-cores. type MilliCores uint // AvailableMilliCores returns the number of available CPU cores in milli-cores. func AvailableMilliCores() MilliCores { return MilliCores(availableCPUCores()) * 1000 } // CPUShare represents a CPU share value. type CPUShare uint64 // MilliCoresToShares converts milli-cores to CPU shares. func MilliCoresToShares(milliCores MilliCores) CPUShare { return CPUShare(milliCores) * 1024 / 1000 } // SharesToCPUWeight converts CPU shares to CPU weight. func SharesToCPUWeight(shares CPUShare) uint64 { return uint64((((shares - 2) * 9999) / 262142) + 1) } // MillicoresToCPUWeight converts milli-cores to CPU weight. // // It limits millicores to available CPU cores. func MillicoresToCPUWeight(requested MilliCores) uint64 { requested = min(requested, AvailableMilliCores()) return SharesToCPUWeight(MilliCoresToShares(requested)) } ================================================ FILE: internal/pkg/cgroup/cpu_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroup_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/cgroup" ) func TestAvailableMillicores(t *testing.T) { t.Logf("Available CPU milli-cores: %d", cgroup.AvailableMilliCores()) assert.GreaterOrEqual(t, cgroup.AvailableMilliCores(), cgroup.MilliCores(1000)) } func TestMillicoresToShares(t *testing.T) { assert.Equal(t, cgroup.CPUShare(102), cgroup.MilliCoresToShares(100)) assert.Equal(t, cgroup.CPUShare(1024), cgroup.MilliCoresToShares(1000)) assert.Equal(t, cgroup.CPUShare(2560), cgroup.MilliCoresToShares(2500)) } func TestSharesToCPUWeight(t *testing.T) { assert.Equal(t, uint64(4), cgroup.SharesToCPUWeight(102)) assert.Equal(t, uint64(79), cgroup.SharesToCPUWeight(2048)) assert.Equal(t, uint64(313), cgroup.SharesToCPUWeight(8192)) } func TestMillicoresToCPUWeight(t *testing.T) { // depends on number of CPUs available, but for < 1000 millicores it should be same result assert.Equal(t, uint64(4), cgroup.MillicoresToCPUWeight(100)) assert.Equal(t, uint64(20), cgroup.MillicoresToCPUWeight(500)) assert.Equal(t, uint64(39), cgroup.MillicoresToCPUWeight(1000)) } ================================================ FILE: internal/pkg/cgroups/cgroups.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cgroups provides functions to parse cgroup information. package cgroups import ( "io" "slices" "strings" "github.com/siderolabs/gen/maps" ) // Tree represents a cgroup tree. type Tree struct { Root *Node } // Find the node by directory path. func (t *Tree) Find(directoryPath string) *Node { node := t.Root for component := range strings.SplitSeq(directoryPath, "/") { if component == "." || component == "" { return node } if node.Children == nil { node.Children = make(map[string]*Node) } child, ok := node.Children[component] if !ok { child = &Node{} node.Children[component] = child } node = child } return node } // ResolveNames resolves the names of the node and its children. func (t *Tree) ResolveNames(nameMap map[string]string) { t.Root.ResolveNames(nameMap) } // Walk the tree. func (t *Tree) Walk(fn func(*Node)) { t.Root.Walk(fn) } // SortedChildren returns the sorted children of the node. func (n *Node) SortedChildren() []string { children := maps.Keys(n.Children) slices.Sort(children) return children } // ResolveNames resolves the names of the node and its children. func (n *Node) ResolveNames(nameMap map[string]string) { for name, child := range n.Children { if resolvedName, ok := nameMap[name]; ok { delete(n.Children, name) n.Children[resolvedName] = child } child.ResolveNames(nameMap) } } // Walk the node. func (n *Node) Walk(fn func(*Node)) { fn(n) for _, child := range n.Children { child.Walk(fn) } } // Node represents a cgroup node. type Node struct { Children map[string]*Node CgroupEvents FlatMap CgroupFreeze Value CgroupProcs Values CgroupStat FlatMap CgroupThreads Values // Resolved externally into process names. CgroupProcsResolved []RawValue CPUIdle Value CPUMax Values CPUMaxBurst Value CPUPressure NestedKeyed CPUStat FlatMap CPUStatLocal FlatMap CPUWeight Value CPUWeightNice Value CPUSetCPUs RawValue CPUSetCPUsEffective RawValue CPUSetMems RawValue CPUSetMemsEffective RawValue IOBFQWeight FlatMap IOMax NestedKeyed IOPressure NestedKeyed IOStat NestedKeyed MemoryCurrent Value MemoryEvents FlatMap MemoryEventsLocal FlatMap MemoryHigh Value MemoryLow Value MemoryMax Value MemoryMin Value MemoryNUMAStat NestedKeyed MemoryOOMGroup Value MemoryPeak Value MemoryPressure NestedKeyed MemoryStat FlatMap MemorySwapCurrent Value MemorySwapEvents FlatMap MemorySwapHigh Value MemorySwapMax Value MemorySwapPeak Value MemoryZswapCurrent Value MemoryZswapMax Value MemoryZswapWriteback Value PidsCurrent Value PidsEvents FlatMap PidsMax Value PidsPeak Value } func parseSingleValue(parser func(r io.Reader) (Values, error), out *Value, r io.Reader) error { values, err := parser(r) if err != nil { return err } if len(values) > 0 { *out = values[0] } return nil } // Parse the cgroup information by filename from the reader. // //nolint:gocyclo,cyclop func (n *Node) Parse(filename string, r io.Reader) error { var err error switch filename { case "cgroup.events": n.CgroupEvents, err = ParseFlatMapValues(r) return err case "cgroup.freeze": return parseSingleValue(ParseNewlineSeparatedValues, &n.CgroupFreeze, r) case "cgroup.procs": n.CgroupProcs, err = ParseNewlineSeparatedValues(r) return err case "cgroup.stat": n.CgroupStat, err = ParseFlatMapValues(r) return err case "cgroup.threads": n.CgroupThreads, err = ParseNewlineSeparatedValues(r) return err case "cpu.idle": return parseSingleValue(ParseNewlineSeparatedValues, &n.CPUIdle, r) case "cpu.max": n.CPUMax, err = ParseSpaceSeparatedValues(r) return err case "cpu.max.burst": return parseSingleValue(ParseNewlineSeparatedValues, &n.CPUMaxBurst, r) case "cpu.pressure": n.CPUPressure, err = ParseNestedKeyedValues(r) return err case "cpu.stat": n.CPUStat, err = ParseFlatMapValues(r) return err case "cpu.stat.local": n.CPUStatLocal, err = ParseFlatMapValues(r) return err case "cpu.weight": return parseSingleValue(ParseNewlineSeparatedValues, &n.CPUWeight, r) case "cpu.weight.nice": return parseSingleValue(ParseNewlineSeparatedValues, &n.CPUWeightNice, r) case "cpuset.cpus": n.CPUSetCPUs, err = ParseRawValue(r) return err case "cpuset.cpus.effective": n.CPUSetCPUsEffective, err = ParseRawValue(r) return err case "cpuset.mems": n.CPUSetMems, err = ParseRawValue(r) return err case "cpuset.mems.effective": n.CPUSetMemsEffective, err = ParseRawValue(r) return err case "io.bfq.weight": n.IOBFQWeight, err = ParseFlatMapValues(r) return err case "io.max": n.IOMax, err = ParseNestedKeyedValues(r) return err case "io.pressure": n.IOPressure, err = ParseNestedKeyedValues(r) return err case "io.stat": n.IOStat, err = ParseNestedKeyedValues(r) return err case "memory.current": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryCurrent, r) case "memory.events": n.MemoryEvents, err = ParseFlatMapValues(r) return err case "memory.events.local": n.MemoryEventsLocal, err = ParseFlatMapValues(r) return err case "memory.high": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryHigh, r) case "memory.low": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryLow, r) case "memory.max": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryMax, r) case "memory.min": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryMin, r) case "memory.numa_stat": n.MemoryNUMAStat, err = ParseNestedKeyedValues(r) return err case "memory.oom.group": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryOOMGroup, r) case "memory.peak": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryPeak, r) case "memory.pressure": n.MemoryPressure, err = ParseNestedKeyedValues(r) return err case "memory.stat": n.MemoryStat, err = ParseFlatMapValues(r) return err case "memory.swap.current": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapCurrent, r) case "memory.swap.events": n.MemorySwapEvents, err = ParseFlatMapValues(r) return err case "memory.swap.high": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapHigh, r) case "memory.swap.max": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapMax, r) case "memory.swap.peak": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemorySwapPeak, r) case "memory.zswap.current": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapCurrent, r) case "memory.zswap.max": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapMax, r) case "memory.zswap.writeback": return parseSingleValue(ParseNewlineSeparatedValues, &n.MemoryZswapWriteback, r) case "pids.current": return parseSingleValue(ParseNewlineSeparatedValues, &n.PidsCurrent, r) case "pids.events": n.PidsEvents, err = ParseFlatMapValues(r) return err case "pids.max": return parseSingleValue(ParseNewlineSeparatedValues, &n.PidsMax, r) case "pids.peak": return parseSingleValue(ParseNewlineSeparatedValues, &n.PidsPeak, r) } return nil } ================================================ FILE: internal/pkg/cgroups/raw.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups import ( "bufio" "io" ) // RawValue is a raw cgroup value (without any parsing). type RawValue string // ParseRawValue parses the raw cgroup value. func ParseRawValue(r io.Reader) (RawValue, error) { scanner := bufio.NewScanner(r) if !scanner.Scan() { return RawValue(""), nil } line := scanner.Text() if err := scanner.Err(); err != nil { return RawValue(""), err } return RawValue(line), nil } ================================================ FILE: internal/pkg/cgroups/reader.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups import ( "fmt" "os" "path/filepath" ) // ReadCgroupfsProperty reads a property from cgroupfs into an existing Node. func ReadCgroupfsProperty(node *Node, cgroupPath, property string) error { f, err := os.OpenFile(filepath.Join(cgroupPath, property), os.O_RDONLY, 0) if err != nil { return fmt.Errorf("error opening cgroupfs file %w", err) } defer f.Close() //nolint:errcheck err = node.Parse(property, f) if err != nil { return fmt.Errorf("error parsing cgroupfs file %w", err) } return nil } // GetCgroupProperty reads a property from cgroupfs into a new Node. func GetCgroupProperty(cgroupPath, property string) (*Node, error) { node := Node{} err := ReadCgroupfsProperty(&node, cgroupPath, property) return &node, err } ================================================ FILE: internal/pkg/cgroups/tar.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups import ( "archive/tar" "compress/gzip" "fmt" "io" "path/filepath" ) // TreeFromTarGz builds a crgroup tree from the tar.gz reader. // // It is assumed to work with output of `talosctl cp /sys/fs/cgroup -`. func TreeFromTarGz(r io.Reader) (*Tree, error) { gzReader, err := gzip.NewReader(r) if err != nil { return nil, err } defer gzReader.Close() //nolint:errcheck tarReader := tar.NewReader(gzReader) tree := &Tree{ Root: &Node{}, } for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { return nil, err } if header.Typeflag != tar.TypeReg { continue } directory, filename := filepath.Split(header.Name) node := tree.Find(directory) if err = node.Parse(filename, tarReader); err != nil { return nil, fmt.Errorf("failed to parse %q: %w", header.Name, err) } } return tree, nil } ================================================ FILE: internal/pkg/cgroups/tar_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups_test import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/cgroups" ) func TestTreeFromTarGz(t *testing.T) { t.Parallel() tarFile, err := os.Open("testdata/cgroup.tar.gz") require.NoError(t, err) t.Cleanup(func() { assert.NoError(t, tarFile.Close()) }) tree, err := cgroups.TreeFromTarGz(tarFile) require.NoError(t, err) assert.Equal(t, []string{"init", "kubepods", "podruntime", "system"}, tree.Root.SortedChildren()) assert.Equal(t, "114712576", tree.Find("init").MemoryCurrent.String()) } ================================================ FILE: internal/pkg/cgroups/value.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups import ( "bufio" "errors" "io" "math" "strconv" "strings" "time" "github.com/dustin/go-humanize" ) // Value represents a cgroup value. // // Value might represent 'max' value. type Value struct { Val int64 Frac int IsMax bool IsSet bool } // String returns the string representation of the cgroup value. func (v Value) String() string { switch { case !v.IsSet: return "unset" case v.IsMax: return "max" default: s := strconv.FormatInt(v.Val, 10) if v.Frac == 0 { return s } if len(s) < v.Frac+1 { s = strings.Repeat("0", (v.Frac+1)-len(s)) + s } return s[:len(s)-v.Frac] + "." + s[len(s)-v.Frac:] } } // HumanizeIBytes returns the humanized bytes representation of the cgroup value. func (v Value) HumanizeIBytes() string { if !v.IsSet || v.IsMax || v.Frac > 0 { return v.String() } return humanize.IBytes(uint64(v.Val)) } // DivideBy returns the value divided by another value in percentage. // // a.DivideBy(b) = a / b * 100. func (v Value) DivideBy(other Value) Value { switch { case !v.IsSet || !other.IsSet: // if either value is unset, return unset return Value{} case other.IsMax && !v.IsMax: // if other is max and v is not, return 0.00% return Value{IsSet: true, Frac: 2} case v.IsMax && other.IsMax: // if both are max, return 100.00% return Value{Val: 10000, IsSet: true, Frac: 2} case other.Val == 0 || v.IsMax: // if other is 0, return max return Value{IsMax: true, IsSet: true} default: return Value{Val: int64(math.Round(float64(v.Val) / float64(other.Val) * 100 * 100)), IsSet: true, Frac: 2} } } // Float64 returns the float64 representation of the cgroup value. func (v Value) Float64() float64 { switch { case !v.IsSet: return math.NaN() case v.IsMax: return math.Inf(1) case v.Frac == 0: return float64(v.Val) default: return float64(v.Val) / math.Pow10(v.Frac) } } // UsecToDuration returns the duration representation of the cgroup value in microseconds. func (v Value) UsecToDuration() string { if !v.IsSet || v.IsMax { return v.String() } return (time.Duration(v.Val) * time.Microsecond).String() } // Values represents a list of cgroup values. type Values []Value // FlatMap returns the flat map of the cgroup values. type FlatMap map[string]Value // NestedKeyed returns the nested keyed map of the cgroup values. type NestedKeyed map[string]FlatMap // ParseValue parses the cgroup value from the string. func ParseValue(s string) (Value, error) { if s == "max" { return Value{IsMax: true, IsSet: true}, nil } var frac int l, r, ok := strings.Cut(s, ".") if ok { frac = len(r) s = l + r } val, err := strconv.ParseInt(s, 10, 64) if err != nil { return Value{}, err } return Value{Val: val, Frac: frac, IsSet: true}, nil } // ParseNewlineSeparatedValues parses the cgroup values from the newline separated string. // // New-line separated values // (when only one value can be written at once) // // VAL0\n // VAL1\n // ... func ParseNewlineSeparatedValues(r io.Reader) (Values, error) { scanner := bufio.NewScanner(r) var values Values for scanner.Scan() { val, err := ParseValue(scanner.Text()) if err != nil { return nil, err } values = append(values, val) } if err := scanner.Err(); err != nil { return nil, err } return values, nil } // ParseSpaceSeparatedValues parses the cgroup values from the space separated string. // // Space separated values // (when read-only or multiple values can be written at once) // // VAL0 VAL1 ...\n. func ParseSpaceSeparatedValues(r io.Reader) (Values, error) { scanner := bufio.NewScanner(r) if !scanner.Scan() { return nil, nil } line := scanner.Text() parts := strings.Fields(line) values := make(Values, 0, len(parts)) for _, s := range parts { val, err := ParseValue(s) if err != nil { return nil, err } values = append(values, val) } if err := scanner.Err(); err != nil { return nil, err } return values, nil } // ParseFlatMapValues parses the cgroup values from the flat map. // // Flat keyed: // // KEY0 VAL0\n // KEY1 VAL1\n // ... func ParseFlatMapValues(r io.Reader) (FlatMap, error) { scanner := bufio.NewScanner(r) flatMap := FlatMap{} for scanner.Scan() { line := scanner.Text() key, value, ok := strings.Cut(line, " ") if !ok { return nil, errors.New("invalid format") } val, err := ParseValue(value) if err != nil { return nil, err } flatMap[key] = val } if err := scanner.Err(); err != nil { return nil, err } return flatMap, nil } // ParseNestedKeyedValues parses the cgroup values from the nested keyed map. // // Nested keyed: // // KEY0 SUB_KEY0=VAL00 SUB_KEY1=VAL01... // KEY1 SUB_KEY0=VAL10 SUB_KEY1=VAL11... // ... func ParseNestedKeyedValues(r io.Reader) (NestedKeyed, error) { scanner := bufio.NewScanner(r) nestedKeyed := NestedKeyed{} for scanner.Scan() { line := scanner.Text() key, values, ok := strings.Cut(line, " ") if !ok { return nil, errors.New("invalid format") } flatMap := FlatMap{} for pair := range strings.FieldsSeq(values) { subKey, value, ok := strings.Cut(pair, "=") if !ok { return nil, errors.New("invalid format") } val, err := ParseValue(value) if err != nil { return nil, err } flatMap[subKey] = val } nestedKeyed[key] = flatMap } if err := scanner.Err(); err != nil { return nil, err } return nestedKeyed, nil } ================================================ FILE: internal/pkg/cgroups/value_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cgroups_test import ( "math" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/cgroups" ) func TestParseValue(t *testing.T) { t.Parallel() for _, test := range []struct { in string expected cgroups.Value expectedFloat64 float64 }{ { in: "42", expected: cgroups.Value{Val: 42, IsSet: true}, expectedFloat64: 42, }, { in: "max", expected: cgroups.Value{IsMax: true, IsSet: true}, expectedFloat64: math.Inf(1), }, { in: "42.5", expected: cgroups.Value{Val: 425, Frac: 1, IsSet: true}, expectedFloat64: 42.5, }, { in: "0.00", expected: cgroups.Value{Val: 0, Frac: 2, IsSet: true}, }, } { t.Run(test.in, func(t *testing.T) { t.Parallel() v, err := cgroups.ParseValue(test.in) require.NoError(t, err) assert.Equal(t, test.expected, v) assert.Equal(t, test.expectedFloat64, v.Float64()) assert.Equal(t, test.in, v.String()) }) } } func TestParseNewlineSeparatedValues(t *testing.T) { //nolint:dupl t.Parallel() for _, test := range []struct { name string input string expected cgroups.Values }{ { name: "one", input: "42\n", expected: cgroups.Values{ {Val: 42, IsSet: true}, }, }, { name: "two", input: "42\n43\n", expected: cgroups.Values{ {Val: 42, IsSet: true}, {Val: 43, IsSet: true}, }, }, { name: "max", input: "max\n", expected: cgroups.Values{ {IsMax: true, IsSet: true}, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() r := strings.NewReader(test.input) values, err := cgroups.ParseNewlineSeparatedValues(r) require.NoError(t, err) require.Equal(t, test.expected, values) }) } } func TestParseSpaceSeparatedValues(t *testing.T) { //nolint:dupl t.Parallel() for _, test := range []struct { name string input string expected cgroups.Values }{ { name: "one", input: "42\n", expected: cgroups.Values{ {Val: 42, IsSet: true}, }, }, { name: "two", input: "42 43\n", expected: cgroups.Values{ {Val: 42, IsSet: true}, {Val: 43, IsSet: true}, }, }, { name: "max", input: "max\n", expected: cgroups.Values{ {IsMax: true, IsSet: true}, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() r := strings.NewReader(test.input) values, err := cgroups.ParseSpaceSeparatedValues(r) require.NoError(t, err) require.Equal(t, test.expected, values) }) } } func TestParseFlatMapValues(t *testing.T) { t.Parallel() for _, test := range []struct { name string input string expected cgroups.FlatMap }{ { name: "values", input: "anon 3000\nfile 6000\n", expected: cgroups.FlatMap{ "anon": {Val: 3000, IsSet: true}, "file": {Val: 6000, IsSet: true}, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() r := strings.NewReader(test.input) values, err := cgroups.ParseFlatMapValues(r) require.NoError(t, err) require.Equal(t, test.expected, values) }) } } func TestParseNestedKeyedValues(t *testing.T) { t.Parallel() for _, test := range []struct { name string input string expected cgroups.NestedKeyed }{ { name: "values", input: "anon rss=3125 vms=123\nreal rss=1234 vms=5678\n", expected: cgroups.NestedKeyed{ "anon": { "rss": {Val: 3125, IsSet: true}, "vms": {Val: 123, IsSet: true}, }, "real": { "rss": {Val: 1234, IsSet: true}, "vms": {Val: 5678, IsSet: true}, }, }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() r := strings.NewReader(test.input) values, err := cgroups.ParseNestedKeyedValues(r) require.NoError(t, err) require.Equal(t, test.expected, values) }) } } ================================================ FILE: internal/pkg/console/console.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package console contains console-related functionality. package console import ( "fmt" "os" "syscall" "unsafe" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( // vtActivate activates the specified virtual terminal. // See VT_ACTIVATE: // https://man7.org/linux/man-pages/man2/ioctl_console.2.html // https://github.com/torvalds/linux/blob/v6.2/include/uapi/linux/vt.h#L42 vtActivate uintptr = 0x5606 // tioclSetKmsgRedirect redirects kernel messages to the specified tty. // See TIOCL_SETKMSGREDIRECT: // https://github.com/torvalds/linux/blob/v6.2/include/uapi/linux/tiocl.h#L33 // https://github.com/torvalds/linux/blob/v6.2/drivers/tty/vt/vt.c#L3242 tioclSetKmsgRedirect byte = 11 ) // Switch switches the active console to the specified tty. func Switch(ttyNumber int) error { // redirect the kernel logs to their own TTY instead of the currently used one, // so that other TTYs (e.g., dashboard on tty2) do not get flooded with kernel logs if err := redirectKernelLogs(constants.KernelLogsTTY); err != nil { return err } // we need a valid fd to any tty because ioctl requires it tty0, err := os.OpenFile("/dev/tty0", os.O_RDWR, 0) if err != nil { return err } defer tty0.Close() //nolint:errcheck if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tty0.Fd(), vtActivate, uintptr(ttyNumber)); errno != 0 { return fmt.Errorf("failed to activate console: %w", errno) } return nil } // redirectKernelLogs redirects kernel logs to the specified tty. func redirectKernelLogs(ttyNumber int) error { tty, err := os.OpenFile(fmt.Sprintf("/dev/tty%d", ttyNumber), os.O_RDWR, 0) if err != nil { return err } args := [2]byte{tioclSetKmsgRedirect, byte(ttyNumber)} if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, tty.Fd(), syscall.TIOCLINUX, uintptr(unsafe.Pointer(&args))); errno != 0 { return fmt.Errorf("failed to set redirect for kmsg: %w", errno) } return tty.Close() } ================================================ FILE: internal/pkg/containermode/containermode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package containermode contains a utility function to detect if Talos is running in a container. package containermode import ( "os" "github.com/siderolabs/talos/pkg/machinery/constants" ) // InContainer checks whether or not Talos is running in a container. func InContainer() bool { if _, err := os.Stat(constants.ContainerMarkerFilePath); err == nil { return true } return false } ================================================ FILE: internal/pkg/containers/container.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package containers provides the container implementatiom. package containers import ( "context" "errors" "fmt" "io" "os" "path/filepath" "strings" "syscall" "github.com/siderolabs/go-tail" "github.com/siderolabs/talos/pkg/chunker" "github.com/siderolabs/talos/pkg/chunker/file" "github.com/siderolabs/talos/pkg/chunker/stream" ) // Container presents information about a container. type Container struct { Inspector Inspector Display string // Friendly Name Name string // container name ID string // container sha/id UID string // container uid Digest string // Container Digest Image string PodName string Sandbox string Status string // Running state of container RestartCount string LogPath string Metrics *ContainerMetrics Pid uint32 IsPodSandbox bool // real container or just pod sandbox NetworkNamespace string } // ContainerMetrics represents container cgroup stats. type ContainerMetrics struct { MemoryUsage uint64 CPUUsage uint64 } // GetProcessStderr returns process stderr. func (c *Container) GetProcessStderr() (string, error) { return c.Inspector.GetProcessStderr(c.ID) } // GetLogFile returns path to log file, k8s-style. func (c *Container) GetLogFile() string { if c.LogPath != "" { return c.LogPath } if c.Sandbox == "" || !strings.Contains(c.Display, ":") { return "" } return filepath.Join(c.Sandbox, c.Name, c.RestartCount+".log") } // Kill sends signal to container task. func (c *Container) Kill(signal syscall.Signal) error { return c.Inspector.Kill(c.ID, c.IsPodSandbox, signal) } // GetLogChunker returns chunker for container log file. func (c *Container) GetLogChunker(ctx context.Context, follow bool, tailLines int) (chunker.Chunker, io.Closer, error) { logFile := c.GetLogFile() if logFile != "" { f, err := os.OpenFile(logFile, os.O_RDONLY, 0) if err != nil { return nil, nil, err } if tailLines >= 0 { err = tail.SeekLines(f, tailLines) if err != nil { f.Close() //nolint:errcheck return nil, nil, fmt.Errorf("error tailing log: %w", err) } } var chunkerOptions []file.Option if follow { chunkerOptions = append(chunkerOptions, file.WithFollow()) } return file.NewChunker(ctx, f, chunkerOptions...), f, nil } filename, err := c.GetProcessStderr() if err != nil { return nil, nil, err } if filename == "" { return nil, nil, errors.New("no log available") } f, err := os.OpenFile(filename, os.O_RDONLY, 0) if err != nil { return nil, nil, err } return stream.NewChunker(ctx, f), f, nil } ================================================ FILE: internal/pkg/containers/containerd/containerd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package containerd implements containers.Inspector via containerd API package containerd import ( "context" "fmt" "path" "strings" "syscall" "time" v1 "github.com/containerd/cgroups/v3/cgroup1/stats" v2 "github.com/containerd/cgroups/v3/cgroup2/stats" tasks "github.com/containerd/containerd/api/services/tasks/v1" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/errdefs" "github.com/containerd/typeurl/v2" "github.com/hashicorp/go-multierror" ctrs "github.com/siderolabs/talos/internal/pkg/containers" "github.com/siderolabs/talos/pkg/machinery/constants" ) type inspector struct { client *containerd.Client //nolint:containedctx nsctx context.Context } type inspectorOptions struct { containerdAddress string } // Option configures containerd Inspector. type Option func(*inspectorOptions) // WithContainerdAddress configures containerd address to use. func WithContainerdAddress(address string) Option { return func(o *inspectorOptions) { o.containerdAddress = address } } // NewInspector builds new Inspector instance in specified namespace. func NewInspector(ctx context.Context, namespace string, options ...Option) (ctrs.Inspector, error) { var err error opt := inspectorOptions{ containerdAddress: constants.CRIContainerdAddress, } for _, o := range options { o(&opt) } i := inspector{} i.client, err = containerd.New(opt.containerdAddress) if err != nil { return nil, err } i.nsctx = namespaces.WithNamespace(ctx, namespace) return &i, nil } // Close frees associated resources. func (i *inspector) Close() error { return i.client.Close() } // Images returns a hash of image digest -> name. func (i *inspector) Images() (map[string]string, error) { images, err := i.client.ListImages(i.nsctx, "") if err != nil { return nil, err } // create a map[sha]name for easier lookups later imageList := make(map[string]string, len(images)) for _, image := range images { if strings.HasPrefix(image.Name(), "sha256:") { continue } imageList[image.Target().Digest.String()] = image.Name() } return imageList, nil } //nolint:gocyclo,cyclop func (i *inspector) containerInfo( cntr containerd.Container, imageList map[string]string, singleLookup bool, ) (*ctrs.Container, error) { cp := &ctrs.Container{} info, err := cntr.Info(i.nsctx) if err != nil { return nil, fmt.Errorf("error getting container info for %q: %w", cntr.ID(), err) } spec, err := cntr.Spec(i.nsctx) if err != nil { return nil, fmt.Errorf("error getting container spec for %q: %w", cntr.ID(), err) } img, err := cntr.Image(i.nsctx) if err != nil { if !errdefs.IsNotFound(err) { return nil, fmt.Errorf("error getting container image for %q: %w", cntr.ID(), err) } } task, err := cntr.Task(i.nsctx, nil) if err != nil { if errdefs.IsNotFound(err) { // running task not found, skip container return nil, nil } return nil, fmt.Errorf("error getting container task for %q: %w", cntr.ID(), err) } status, err := task.Status(i.nsctx) if err != nil { return nil, fmt.Errorf("error getting task status for %q: %w", cntr.ID(), err) } cp.Inspector = i cp.ID = cntr.ID() cp.Name = cntr.ID() cp.Display = cntr.ID() cp.RestartCount = "0" if img != nil { cp.Digest = img.Target().Digest.String() } cp.Image = cp.Digest if imageList != nil { if resolved, ok := imageList[cp.Image]; ok { cp.Image = resolved } } cp.Pid = task.Pid() cp.Status = strings.ToUpper(string(status.Status)) var ( cname, cns string ok bool ) if cname, ok = info.Labels["io.kubernetes.pod.name"]; ok { if cns, ok = info.Labels["io.kubernetes.pod.namespace"]; ok { cp.Display = path.Join(cns, cname) } } if status.Status == containerd.Running { metrics, err := task.Metrics(i.nsctx) if err != nil { return nil, fmt.Errorf("error pulling metrics for %q: %w", cntr.ID(), err) } anydata, err := typeurl.UnmarshalAny(metrics.Data) if err != nil { return nil, fmt.Errorf("error unmarshalling metrics for %q: %w", cntr.ID(), err) } cp.Metrics = &ctrs.ContainerMetrics{} switch data := anydata.(type) { case *v1.Metrics: mem := data.Memory if mem != nil && mem.Usage != nil { if mem.TotalInactiveFile < mem.Usage.Usage { cp.Metrics.MemoryUsage = mem.Usage.Usage - mem.TotalInactiveFile } } cpu := data.CPU if cpu != nil && cpu.Usage != nil { cp.Metrics.CPUUsage = cpu.Usage.Total } case *v2.Metrics: mem := data.Memory if mem != nil { cp.Metrics.MemoryUsage = mem.Usage } cpu := data.CPU if cpu != nil { cp.Metrics.CPUUsage = cpu.UsageUsec * uint64(time.Microsecond/time.Nanosecond) // convert to nsec } default: return nil, fmt.Errorf("failed to convert metric data to cgroups Metrics: %T", anydata) } } // Save off an identifier for the pod // this is typically the container name (non-k8s namespace) // or will be k8s namespace"/"k8s pod name":"container name cp.PodName = cp.Display // Pull restart count // TODO: this doesn't work as CRI doesn't publish this to containerd annotations if _, ok := spec.Annotations["io.kubernetes.container.restartCount"]; ok { cp.RestartCount = spec.Annotations["io.kubernetes.container.restartCount"] } // Typically on the 'infrastructure' container, aka k8s.gcr.io/pause if _, ok := spec.Annotations["io.kubernetes.cri.sandbox-log-directory"]; ok { cp.Sandbox = spec.Annotations["io.kubernetes.cri.sandbox-log-directory"] cp.IsPodSandbox = true } else if singleLookup && cns != "" && cname != "" { // try to find matching infrastructure container and pull sandbox from it query := fmt.Sprintf( "labels.\"io.kubernetes.pod.namespace\"==%q,labels.\"io.kubernetes.pod.name\"==%q", cns, cname, ) infraContainers, err := i.client.Containers(i.nsctx, query) if err == nil { for j := range infraContainers { if infraSpec, err := infraContainers[j].Spec(i.nsctx); err == nil { if spec.Annotations["io.kubernetes.sandbox-id"] != infraSpec.Annotations["io.kubernetes.sandbox-id"] { continue } if sandbox, found := infraSpec.Annotations["io.kubernetes.cri.sandbox-log-directory"]; found { cp.Sandbox = sandbox break } } } } } // Typically on actual application containers inside the pod/sandbox if _, ok := info.Labels["io.kubernetes.container.name"]; ok { cp.Name = info.Labels["io.kubernetes.container.name"] cp.Display = cp.Display + ":" + info.Labels["io.kubernetes.container.name"] } return cp, nil } // Container returns info about a single container. // // If container is not found, Container returns nil // //nolint:gocyclo func (i *inspector) Container(id string) (*ctrs.Container, error) { var ( query string skipWithK8sName bool ) // if id looks like k8s one, ns/pod:container, parse it and build query slashIdx := strings.Index(id, "/") //nolint:modernize if slashIdx > 0 { name := "" namespace, pod := id[:slashIdx], id[slashIdx+1:] semicolonIdx := strings.LastIndex(pod, ":") if semicolonIdx > 0 { name = pod[semicolonIdx+1:] pod = pod[:semicolonIdx] } query = fmt.Sprintf( "labels.\"io.kubernetes.pod.namespace\"==%q,labels.\"io.kubernetes.pod.name\"==%q", namespace, pod, ) if name != "" { query += fmt.Sprintf(",labels.\"io.kubernetes.container.name\"==%q", name) } else { skipWithK8sName = true } } else { query = fmt.Sprintf("id==%q", id) } containers, err := i.client.Containers(i.nsctx, query) if err != nil { return nil, err } if len(containers) == 0 { return nil, nil } var cntr *ctrs.Container for j := range containers { if skipWithK8sName { var labels map[string]string if labels, err = containers[j].Labels(i.nsctx); err == nil { if _, found := labels["io.kubernetes.container.name"]; found { continue } } } cntr, err = i.containerInfo(containers[j], nil, true) if err == nil && cntr != nil { break } } return cntr, err } // Pods collects information about running pods & containers. // //nolint:gocyclo func (i *inspector) Pods() ([]*ctrs.Pod, error) { imageList, err := i.Images() if err != nil { return nil, err } containers, err := i.client.Containers(i.nsctx) if err != nil { return nil, err } var ( multiErr *multierror.Error pods []*ctrs.Pod ) for _, cntr := range containers { cp, err := i.containerInfo(cntr, imageList, false) if err != nil { multiErr = multierror.Append(multiErr, err) continue } if cp == nil { // not running container continue } // Figure out if we need to create a new pod or append // to an existing pod // Also set pod sandbox ID if defined found := false for _, pod := range pods { if pod.Name != cp.PodName { continue } if cp.Sandbox != "" { pod.Sandbox = cp.Sandbox } pod.Containers = append(pod.Containers, cp) found = true break } if !found { p := &ctrs.Pod{ Name: cp.PodName, Containers: []*ctrs.Container{cp}, Sandbox: cp.Sandbox, } pods = append(pods, p) } } // This seems janky because it is // But we need to loop through everything again to associate // the sandbox with the container name so we can get a proper // filepath to the location of the logfile for _, contents := range pods { for _, cntr := range contents.Containers { if cntr.Sandbox == "" && contents.Sandbox != "" { cntr.Sandbox = contents.Sandbox } } } return pods, multiErr.ErrorOrNil() } // GetProcessStderr returns process stderr. func (i *inspector) GetProcessStderr(id string) (string, error) { task, err := i.client.TaskService().Get(i.nsctx, &tasks.GetRequest{ContainerID: id}) if err != nil { return "", err } return task.Process.Stderr, nil } // Kill sends signal to container task. func (i *inspector) Kill(id string, isPodSandbox bool, signal syscall.Signal) error { _, err := i.client.TaskService().Kill(i.nsctx, &tasks.KillRequest{ContainerID: id, Signal: uint32(signal)}) return err } ================================================ FILE: internal/pkg/containers/containerd/containerd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd_test import ( "context" "encoding/hex" "os" "path/filepath" "sync" "testing" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/google/uuid" "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" containerdrunner "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" ctrd "github.com/siderolabs/talos/internal/pkg/containers/containerd" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( busyboxImage = "docker.io/library/busybox:1.30.1" busyboxImageDigest = "sha256:4b6ad3a68d34da29bf7c8ccb5d355ba8b4babcad1f99798204e7abb43e54ee3d" ) func MockEventSink(state events.ServiceState, message string, args ...any) { } //nolint:maligned type ContainerdSuite struct { suite.Suite tmpDir string loggingManager runtime.LoggingManager containerdNamespace string containerdRunner runner.Runner containerdWg sync.WaitGroup containerdAddress string containerID string client *containerd.Client image containerd.Image containerRunners []runner.Runner containersWg sync.WaitGroup } func (suite *ContainerdSuite) SetupSuite() { if cgroups.Mode() == cgroups.Unified { suite.T().Skip("test doesn't pass under cgroupsv2") } var err error suite.tmpDir = suite.T().TempDir() suite.loggingManager = logging.NewFileLoggingManager(suite.tmpDir) stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root") suite.Require().NoError(os.Mkdir(stateDir, 0o777)) suite.Require().NoError(os.Mkdir(rootDir, 0o777)) suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock") if cgroups.Mode() == cgroups.Unified { var ( groupPath string manager *cgroup2.Manager ) groupPath, err = cgroup2.NestedGroupPath(suite.tmpDir) suite.Require().NoError(err) manager, err = cgroup2.NewManager(constants.CgroupMountPath, groupPath, &cgroup2.Resources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } else { var manager cgroup1.Cgroup manager, err = cgroup1.New(cgroup1.NestedPath(suite.tmpDir), &specs.LinuxResources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } args := &runner.Args{ ID: "containerd", ProcessArgs: []string{ "/bin/containerd", "--address", suite.containerdAddress, "--state", stateDir, "--root", rootDir, "--config", constants.CRIContainerdConfig, }, } suite.containerdRunner = process.NewRunner( false, args, runner.WithLoggingManager(suite.loggingManager), runner.WithEnv([]string{constants.EnvPathWithBin}), runner.WithCgroupPath(suite.tmpDir), ) suite.Require().NoError(suite.containerdRunner.Open()) suite.containerdWg.Go(func() { defer suite.containerdRunner.Close() //nolint:errcheck suite.containerdRunner.Run(MockEventSink) //nolint:errcheck }) suite.client, err = containerd.New(suite.containerdAddress) suite.Require().NoError(err) namespace := ([16]byte)(uuid.New()) suite.containerdNamespace = "talos" + hex.EncodeToString(namespace[:]) ctx := namespaces.WithNamespace(context.Background(), suite.containerdNamespace) suite.image, err = suite.client.Pull(ctx, busyboxImage, containerd.WithPullUnpack) suite.Require().NoError(err) } func (suite *ContainerdSuite) TearDownSuite() { suite.Require().NoError(suite.client.Close()) suite.Require().NoError(suite.containerdRunner.Stop()) suite.containerdWg.Wait() } func (suite *ContainerdSuite) SetupTest() { suite.containerRunners = nil suite.containerID = uuid.New().String() } func (suite *ContainerdSuite) run(runners ...runner.Runner) { runningCh := make(chan bool, 2*len(runners)) for _, r := range runners { suite.Require().NoError(r.Open()) suite.containerRunners = append(suite.containerRunners, r) suite.containersWg.Add(1) go func(r runner.Runner) { runningSink := func(state events.ServiceState, message string, args ...any) { if state == events.StateRunning { runningCh <- true } } defer func() { runningCh <- false }() defer suite.containersWg.Done() suite.Require().NoError(r.Run(runningSink)) }(r) } // wait for the containers to be started actually for range runners { result := <-runningCh suite.Require().True(result, "some containers failed to start") } } func (suite *ContainerdSuite) TearDownTest() { for _, r := range suite.containerRunners { suite.Assert().NoError(r.Stop()) } suite.containersWg.Wait() for _, r := range suite.containerRunners { suite.Assert().NoError(r.Close()) } } func (suite *ContainerdSuite) runK8sContainers() { suite.run(containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID + "1", ProcessArgs: []string{"/bin/sh", "-c", "sleep 3600"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerOpts(containerd.WithContainerLabels(map[string]string{ "io.kubernetes.pod.name": "fun", "io.kubernetes.pod.namespace": "ns1", })), runner.WithOCISpecOpts(oci.WithAnnotations(map[string]string{ "io.kubernetes.cri.sandbox-log-directory": "sandbox", "io.kubernetes.cri.sandbox-id": "c888d69b73b5b444c2b0bd70da28c3da102b0aeb327f3a297626e2558def327f", })), runner.WithContainerdAddress(suite.containerdAddress), ), containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID + "2", ProcessArgs: []string{"/bin/sh", "-c", "sleep 3600"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerOpts(containerd.WithContainerLabels(map[string]string{ "io.kubernetes.pod.name": "fun", "io.kubernetes.pod.namespace": "ns1", "io.kubernetes.container.name": "run", })), runner.WithOCISpecOpts(oci.WithAnnotations(map[string]string{ "io.kubernetes.cri.sandbox-id": "c888d69b73b5b444c2b0bd70da28c3da102b0aeb327f3a297626e2558def327f", })), runner.WithContainerdAddress(suite.containerdAddress), )) } func (suite *ContainerdSuite) TestPodsNonK8s() { suite.run(containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "sleep 3600"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), )) i, err := ctrd.NewInspector(context.Background(), suite.containerdNamespace, ctrd.WithContainerdAddress(suite.containerdAddress)) suite.Assert().NoError(err) pods, err := i.Pods() suite.Require().NoError(err) suite.Require().Len(pods, 1) suite.Assert().Equal(suite.containerID, pods[0].Name) suite.Assert().Equal("", pods[0].Sandbox) suite.Require().Len(pods[0].Containers, 1) suite.Assert().Equal(suite.containerID, pods[0].Containers[0].Display) suite.Assert().Equal(suite.containerID, pods[0].Containers[0].Name) suite.Assert().Equal(suite.containerID, pods[0].Containers[0].ID) suite.Assert().Equal(busyboxImage, pods[0].Containers[0].Image) suite.Assert().Equal("RUNNING", pods[0].Containers[0].Status) suite.Assert().NotNil(pods[0].Containers[0].Metrics) suite.Assert().NoError(i.Close()) } func (suite *ContainerdSuite) TestPodsK8s() { suite.runK8sContainers() i, err := ctrd.NewInspector(context.Background(), suite.containerdNamespace, ctrd.WithContainerdAddress(suite.containerdAddress)) suite.Assert().NoError(err) pods, err := i.Pods() suite.Require().NoError(err) suite.Require().Len(pods, 1) suite.Assert().Equal("ns1/fun", pods[0].Name) suite.Assert().Equal("sandbox", pods[0].Sandbox) suite.Require().Len(pods[0].Containers, 2) suite.Assert().Equal("ns1/fun", pods[0].Containers[0].Display) suite.Assert().Equal(suite.containerID+"1", pods[0].Containers[0].Name) suite.Assert().Equal(suite.containerID+"1", pods[0].Containers[0].ID) suite.Assert().Equal("sandbox", pods[0].Containers[0].Sandbox) suite.Assert().Equal("0", pods[0].Containers[0].RestartCount) suite.Assert().Equal("", pods[0].Containers[0].GetLogFile()) suite.Assert().Equal(busyboxImage, pods[0].Containers[0].Image) suite.Assert().Equal("RUNNING", pods[0].Containers[0].Status) suite.Assert().NotNil(pods[0].Containers[0].Metrics) suite.Assert().Equal("ns1/fun:run", pods[0].Containers[1].Display) suite.Assert().Equal("run", pods[0].Containers[1].Name) suite.Assert().Equal(suite.containerID+"2", pods[0].Containers[1].ID) suite.Assert().Equal("sandbox", pods[0].Containers[1].Sandbox) suite.Assert().Equal("sandbox/run/0.log", pods[0].Containers[1].GetLogFile()) suite.Assert().Equal(busyboxImage, pods[0].Containers[1].Image) suite.Assert().Equal("RUNNING", pods[0].Containers[1].Status) suite.Assert().NotNil(pods[0].Containers[1].Metrics) suite.Assert().NoError(i.Close()) } func (suite *ContainerdSuite) TestContainerNonK8s() { suite.run(containerdrunner.NewRunner(false, &runner.Args{ ID: suite.containerID, ProcessArgs: []string{"/bin/sh", "-c", "sleep 3600"}, }, runner.WithLoggingManager(suite.loggingManager), runner.WithNamespace(suite.containerdNamespace), runner.WithContainerImage(busyboxImage), runner.WithContainerdAddress(suite.containerdAddress), )) i, err := ctrd.NewInspector(context.Background(), suite.containerdNamespace, ctrd.WithContainerdAddress(suite.containerdAddress)) suite.Assert().NoError(err) cntr, err := i.Container(suite.containerID) suite.Require().NoError(err) suite.Require().NotNil(cntr) suite.Assert().Equal(suite.containerID, cntr.Name) suite.Assert().Equal(suite.containerID, cntr.Display) suite.Assert().Equal(suite.containerID, cntr.ID) suite.Assert().Equal(busyboxImageDigest, cntr.Image) // image is not resolved suite.Assert().Equal("RUNNING", cntr.Status) cntr, err = i.Container("nosuchcontainer") suite.Require().NoError(err) suite.Require().Nil(cntr) suite.Assert().NoError(i.Close()) } func (suite *ContainerdSuite) TestContainerK8s() { suite.runK8sContainers() i, err := ctrd.NewInspector(context.Background(), suite.containerdNamespace, ctrd.WithContainerdAddress(suite.containerdAddress)) suite.Assert().NoError(err) cntr, err := i.Container("ns1/fun") suite.Require().NoError(err) suite.Require().NotNil(cntr) suite.Assert().Equal(suite.containerID+"1", cntr.Name) suite.Assert().Equal("ns1/fun", cntr.Display) suite.Assert().Equal(suite.containerID+"1", cntr.ID) suite.Assert().Equal("sandbox", cntr.Sandbox) suite.Assert().Equal("", cntr.GetLogFile()) suite.Assert().Equal(busyboxImageDigest, cntr.Image) // image is not resolved suite.Assert().Equal("RUNNING", cntr.Status) cntr, err = i.Container("ns1/fun:run") suite.Require().NoError(err) suite.Require().NotNil(cntr) suite.Assert().Equal("run", cntr.Name) suite.Assert().Equal("ns1/fun:run", cntr.Display) suite.Assert().Equal(suite.containerID+"2", cntr.ID) suite.Assert().Equal("sandbox", cntr.Sandbox) suite.Assert().Equal("sandbox/run/0.log", cntr.GetLogFile()) suite.Assert().Equal(busyboxImageDigest, cntr.Image) // image is not resolved suite.Assert().Equal("RUNNING", cntr.Status) cntr, err = i.Container("ns2/fun:run") suite.Require().NoError(err) suite.Require().Nil(cntr) cntr, err = i.Container("ns1/run:run") suite.Require().NoError(err) suite.Require().Nil(cntr) cntr, err = i.Container("ns1/fun:go") suite.Require().NoError(err) suite.Require().Nil(cntr) suite.Assert().NoError(i.Close()) } func TestContainerdSuite(t *testing.T) { if os.Getuid() != 0 { t.Skip("can't run the test as non-root") } _, err := os.Stat("/bin/containerd") if err != nil { t.Skip("containerd binary is not available, skipping the test") } suite.Run(t, new(ContainerdSuite)) } ================================================ FILE: internal/pkg/containers/containers_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containers_test import ( "testing" ) func TestEmpty(t *testing.T) { // replace with real test } ================================================ FILE: internal/pkg/containers/cri/containerd/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd // AuthConfig represents the registry auth options. type AuthConfig struct { Username string `toml:"username"` Password string `toml:"password"` Auth string `toml:"auth"` IdentityToken string `toml:"identitytoken"` } // RegistryConfig represents a registry. type RegistryConfig struct { Auth *AuthConfig `toml:"auth"` } // Registry represents the registry configuration. type Registry struct { ConfigPath string `toml:"config_path"` Configs map[string]RegistryConfig `toml:"configs"` } // CRIConfig represents the CRI config. type CRIConfig struct { Registry Registry `toml:"registry"` } // PluginsConfig represents the CRI plugins config. type PluginsConfig struct { CRI CRIConfig `toml:"io.containerd.cri.v1.images"` } // Config represnts the containerd config. type Config struct { Plugins PluginsConfig `toml:"plugins"` } ================================================ FILE: internal/pkg/containers/cri/containerd/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd_test import ( _ "embed" "testing" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/pkg/containers/cri/containerd" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) //go:embed testdata/cri.toml var expectedCRIConfig string type mockConfig struct { mirrors map[string]*cri.RegistryMirrorConfig auths map[string]*cri.RegistryAuthConfig tlses map[string]*cri.RegistryTLSConfig } // Mirrors implements the Registries interface. func (c *mockConfig) Mirrors() map[string]config.RegistryMirrorConfig { mirrors := make(map[string]config.RegistryMirrorConfig, len(c.mirrors)) for k, v := range c.mirrors { mirrors[k] = v } return mirrors } // Auths implements the Registries interface. func (c *mockConfig) Auths() map[string]config.RegistryAuthConfig { auths := make(map[string]config.RegistryAuthConfig, len(c.auths)) for k, v := range c.auths { auths[k] = v } return auths } // TLSs implements the Registries interface. func (c *mockConfig) TLSs() map[string]cri.RegistryTLSConfigExtended { tlses := make(map[string]cri.RegistryTLSConfigExtended, len(c.tlses)) for k, v := range c.tlses { tlses[k] = v } return tlses } type ConfigSuite struct { suite.Suite } func (suite *ConfigSuite) TestGenerateRegistriesConfig() { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://registry-1.docker.io"}, {EndpointEndpoint: "https://registry-2.docker.io"}, }, }, }, auths: map[string]*cri.RegistryAuthConfig{ "some.host:123": { RegistryUsername: "root", RegistryPassword: "secret", RegistryAuth: "auth", RegistryIdentityToken: "token", }, "docker.io": { RegistryUsername: "root", RegistryPassword: "topsecret", }, }, tlses: map[string]*cri.RegistryTLSConfig{ "some.host:123": { TLSInsecureSkipVerify: true, TLSCA: []byte("cacert"), TLSClientIdentity: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("clientcert"), Key: []byte("clientkey"), }, }, }, } criConfig, err := containerd.GenerateCRIConfig(cfg) suite.Require().NoError(err) suite.Assert().Equal(expectedCRIConfig, string(criConfig)) } func TestConfigSuite(t *testing.T) { suite.Run(t, new(ConfigSuite)) } ================================================ FILE: internal/pkg/containers/cri/containerd/containerd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package containerd provides support for containerd CRI plugin package containerd import ( "bytes" "maps" "path/filepath" "slices" "github.com/containerd/containerd/v2/core/remotes/docker" "github.com/pelletier/go-toml/v2" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // GenerateCRIConfig returns a part of CRI config for registry auth. // // Once containerd supports different way of supplying auth info, this should be updated. func GenerateCRIConfig(r cri.Registries) ([]byte, error) { var ctrdCfg Config ctrdCfg.Plugins.CRI.Registry.ConfigPath = filepath.Join(constants.EtcCRIConfdPath, "hosts") ctrdCfg.Plugins.CRI.Registry.Configs = make(map[string]RegistryConfig) for _, registryHost := range slices.Sorted(maps.Keys(r.Auths())) { authConfig := r.Auths()[registryHost] cfg := RegistryConfig{} cfg.Auth = &AuthConfig{ Username: authConfig.Username(), Password: authConfig.Password(), Auth: authConfig.Auth(), IdentityToken: authConfig.IdentityToken(), } configHost, _ := docker.DefaultHost(registryHost) //nolint:errcheck // doesn't return an error ctrdCfg.Plugins.CRI.Registry.Configs[configHost] = cfg } var buf bytes.Buffer if err := toml.NewEncoder(&buf).SetIndentTables(true).Encode(&ctrdCfg); err != nil { return nil, err } return buf.Bytes(), nil } ================================================ FILE: internal/pkg/containers/cri/containerd/hosts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd import ( "bytes" "errors" "fmt" "net/url" "os" "path/filepath" "strings" "github.com/containerd/containerd/v2/core/remotes/docker" "github.com/pelletier/go-toml/v2" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // HostsConfig describes layout of registry configuration in "hosts" format. // // See: https://github.com/containerd/containerd/blob/main/docs/hosts.md type HostsConfig struct { Directories map[string]*HostsDirectory } // HostsDirectory describes a single directory for a specific registry. type HostsDirectory struct { Files []*HostsFile } // HostsFile describes a single file configuring registry. // // This might be `hosts.toml` or a specific certificate. type HostsFile struct { Name string Contents []byte Mode os.FileMode } // GenerateHosts generates a structure describing contents of the containerd hosts configuration. // //nolint:gocyclo func GenerateHosts(cfg cri.Registries, basePath string) (*HostsConfig, error) { config := &HostsConfig{ Directories: map[string]*HostsDirectory{}, } configureEndpoint := func(host string, directoryName string, hostToml *HostToml, directory *HostsDirectory) { tlsConfig, ok := cfg.TLSs()[host] if !ok { return } if tlsConfig.InsecureSkipVerify() { hostToml.SkipVerify = true } if tlsConfig.CA() != nil { relPath := fmt.Sprintf("%s-ca.crt", host) directory.Files = append(directory.Files, &HostsFile{ Name: relPath, Contents: tlsConfig.CA(), Mode: 0o600, }, ) hostToml.CACert = filepath.Join(basePath, directoryName, relPath) } if tlsConfig.ClientIdentity() != nil { relPathCrt := fmt.Sprintf("%s-client.crt", host) relPathKey := fmt.Sprintf("%s-client.key", host) directory.Files = append(directory.Files, &HostsFile{ Name: relPathCrt, Contents: tlsConfig.ClientIdentity().Crt, Mode: 0o600, }, &HostsFile{ Name: relPathKey, Contents: tlsConfig.ClientIdentity().Key, Mode: 0o600, }, ) hostToml.Client = [][2]string{ { filepath.Join(basePath, directoryName, relPathCrt), filepath.Join(basePath, directoryName, relPathKey), }, } } } // process mirrors for registryName, endpoints := range cfg.Mirrors() { directoryName := hostDirectory(registryName) directory := &HostsDirectory{} var hostsConfig HostsConfiguration for _, endpoint := range endpoints.Endpoints() { u, err := url.Parse(endpoint.Endpoint()) if err != nil { return nil, fmt.Errorf("error parsing endpoint %q for host %q: %w", endpoint, registryName, err) } hostEntry := HostEntry{ Host: endpoint.Endpoint(), HostToml: HostToml{ Capabilities: []string{"pull", "resolve"}, // TODO: we should make it configurable eventually OverridePath: endpoint.OverridePath(), }, } configureEndpoint(u.Host, directoryName, &hostEntry.HostToml, directory) hostsConfig.HostEntries = append(hostsConfig.HostEntries, hostEntry) } if endpoints.SkipFallback() { hostsConfig.DisableFallback() } cfgOut, err := hostsConfig.RenderTOML() if err != nil { return nil, err } directory.Files = append(directory.Files, &HostsFile{ Name: "hosts.toml", Mode: 0o600, Contents: cfgOut, }, ) config.Directories[directoryName] = directory } // process TLS config for non-mirrored endpoints (even if they were already processed) for hostname, tlsConfig := range cfg.TLSs() { directoryName := hostDirectory(hostname) if _, ok := config.Directories[directoryName]; ok { // skip, already configured continue } if tlsConfig == nil || (tlsConfig.CA() == nil && tlsConfig.ClientIdentity() == nil && !tlsConfig.InsecureSkipVerify()) { // skip, no specific config continue } if hostname == "*" { // no way to generate TLS config for wildcard host return nil, errors.New("wildcard host TLS configuration is not supported") } directory := &HostsDirectory{} defaultHost, err := docker.DefaultHost(hostname) if err != nil { return nil, err } defaultHost = "https://" + defaultHost rootEntry := HostEntry{ Host: defaultHost, } configureEndpoint(hostname, directoryName, &rootEntry.HostToml, directory) hostsToml := HostsConfiguration{ RootEntry: optional.Some(rootEntry), } cfgOut, err := hostsToml.RenderTOML() if err != nil { return nil, err } directory.Files = append(directory.Files, &HostsFile{ Name: "hosts.toml", Mode: 0o600, Contents: cfgOut, }, ) config.Directories[directoryName] = directory } return config, nil } // hostDirectory converts ":port" to "_port_" in directory names. func hostDirectory(host string) string { if host == "*" { return "_default" } idx := strings.LastIndex(host, ":") if idx > 0 { return host[:idx] + "_" + host[idx+1:] + "_" } return host } // HostEntry describes the configuration for a single host. type HostEntry struct { Host string HostToml //nolint:embeddedstructfieldcheck } // HostsConfiguration describes the configuration of `hosts.toml` file in the format not compatible with TOML. // // The hosts entries should come in order, and go-toml only supports map[string]any, so we need to do some tricks. type HostsConfiguration struct { RootEntry optional.Optional[HostEntry] // might be missing HostEntries []HostEntry } // DisableFallback disables the fallback to the default host. func (hc *HostsConfiguration) DisableFallback() { if len(hc.HostEntries) == 0 { return } // push the last entry as the root entry hc.RootEntry = optional.Some(hc.HostEntries[len(hc.HostEntries)-1]) hc.HostEntries = hc.HostEntries[:len(hc.HostEntries)-1] } // RenderTOML renders the configuration to TOML format. func (hc *HostsConfiguration) RenderTOML() ([]byte, error) { var out bytes.Buffer // toml marshaling doesn't guarantee proper order of map keys, so instead we should marshal // each time and append to the output if rootEntry, ok := hc.RootEntry.Get(); ok { server := HostsTomlServer{ Server: rootEntry.Host, HostToml: rootEntry.HostToml, } if err := toml.NewEncoder(&out).SetIndentTables(true).Encode(server); err != nil { return nil, err } } for i, entry := range hc.HostEntries { hostEntry := HostsTomlHost{ HostConfigs: map[string]HostToml{ entry.Host: entry.HostToml, }, } var tomlBuf bytes.Buffer if err := toml.NewEncoder(&tomlBuf).SetIndentTables(true).Encode(hostEntry); err != nil { return nil, err } tomlBytes := tomlBuf.Bytes() // this is an ugly hack, and neither TOML format nor go-toml library make it easier // // we need to marshal each endpoint in the order they are specified in the config, but go-toml defines // the tree as map[string]interface{} and doesn't guarantee the order of keys // // so we marshal each entry separately and combine the output, which results in something like: // // [host] // [host."foo.bar"] // [host] // [host."bar.foo"] // // but this is invalid TOML, as `[host]' is repeated, so we do an ugly hack and remove it below const hostPrefix = "[host]\n" if i > 0 { if bytes.HasPrefix(tomlBytes, []byte(hostPrefix)) { tomlBytes = tomlBytes[len(hostPrefix):] } } out.Write(tomlBytes) } return out.Bytes(), nil } // HostsTomlServer describes only 'server' part of the `hosts.toml` file. type HostsTomlServer struct { // top-level entry is used as the last one in the fallback chain. Server string `toml:"server,omitempty"` HostToml //nolint:embeddedstructfieldcheck // embedded, matches the server } // HostsTomlHost describes the `hosts.toml` file entry for hosts. // // It is supposed to be marshaled as a single-entry map to keep the order correct. type HostsTomlHost struct { // Note: this doesn't match the TOML format, but allows use to keep endpoints ordered properly. HostConfigs map[string]HostToml `toml:"host"` } // HostToml is a single entry in `hosts.toml`. type HostToml struct { Capabilities []string `toml:"capabilities,omitempty"` OverridePath bool `toml:"override_path,omitempty"` CACert string `toml:"ca,omitempty"` Client [][2]string `toml:"client,omitempty"` SkipVerify bool `toml:"skip_verify,omitempty"` } ================================================ FILE: internal/pkg/containers/cri/containerd/hosts_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containerd_test import ( _ "embed" "testing" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/containers/cri/containerd" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) func TestGenerateHostsWithTLS(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://registry-1.docker.io"}, {EndpointEndpoint: "https://registry-2.docker.io"}, }, }, }, auths: map[string]*cri.RegistryAuthConfig{ "some.host:123": { RegistryUsername: "root", RegistryPassword: "secret", RegistryAuth: "auth", RegistryIdentityToken: "token", }, }, tlses: map[string]*cri.RegistryTLSConfig{ "some.host:123": { TLSInsecureSkipVerify: true, TLSCA: []byte("cacert"), TLSClientIdentity: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("clientcert"), Key: []byte("clientkey"), }, }, "registry-2.docker.io": { TLSInsecureSkipVerify: true, }, }, } result, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") require.NoError(t, err) assert.Equal(t, &containerd.HostsConfig{ Directories: map[string]*containerd.HostsDirectory{ "docker.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://registry-1.docker.io']\n capabilities = ['pull', 'resolve']\n [host.'https://registry-2.docker.io']\n capabilities = ['pull', 'resolve']\n skip_verify = true\n"), //nolint:lll }, }, }, "some.host_123_": { Files: []*containerd.HostsFile{ { Name: "some.host:123-ca.crt", Mode: 0o600, Contents: []byte("cacert"), }, { Name: "some.host:123-client.crt", Mode: 0o600, Contents: []byte("clientcert"), }, { Name: "some.host:123-client.key", Mode: 0o600, Contents: []byte("clientkey"), }, { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'https://some.host:123'\nca = '/etc/cri/conf.d/hosts/some.host_123_/some.host:123-ca.crt'\nclient = [['/etc/cri/conf.d/hosts/some.host_123_/some.host:123-client.crt', '/etc/cri/conf.d/hosts/some.host_123_/some.host:123-client.key']]\nskip_verify = true\n"), //nolint:lll }, }, }, "registry-2.docker.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'https://registry-2.docker.io'\nskip_verify = true\n"), }, }, }, }, }, result) } func TestGenerateHostsWithoutTLS(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://registry-1.docker.io"}, {EndpointEndpoint: "https://registry-2.docker.io"}, }, }, "*": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://my-registry"}, }, }, }, auths: map[string]*cri.RegistryAuthConfig{ "some.host:123": { RegistryUsername: "root", RegistryPassword: "secret", RegistryAuth: "auth", RegistryIdentityToken: "token", }, }, } result, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") require.NoError(t, err) assert.Equal(t, &containerd.HostsConfig{ Directories: map[string]*containerd.HostsDirectory{ "docker.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://registry-1.docker.io']\n capabilities = ['pull', 'resolve']\n [host.'https://registry-2.docker.io']\n capabilities = ['pull', 'resolve']\n"), //nolint:lll }, }, }, "_default": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://my-registry']\n capabilities = ['pull', 'resolve']\n"), }, }, }, }, }, result) } func TestGenerateHostsTLSWildcardWrong(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{}, tlses: map[string]*cri.RegistryTLSConfig{ "*": { TLSCA: []byte("allcert"), }, }, } _, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") assert.EqualError(t, err, "wildcard host TLS configuration is not supported") } func TestGenerateHostsTLSWildcard(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "*": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://my-registry1"}, {EndpointEndpoint: "https://my-registry2"}, }, }, }, tlses: map[string]*cri.RegistryTLSConfig{ "my-registry1": { TLSCA: []byte("allcert"), }, }, } result, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") require.NoError(t, err) assert.Equal(t, &containerd.HostsConfig{ Directories: map[string]*containerd.HostsDirectory{ "_default": { Files: []*containerd.HostsFile{ { Name: "my-registry1-ca.crt", Mode: 0o600, Contents: []byte("allcert"), }, { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://my-registry1']\n capabilities = ['pull', 'resolve']\n ca = '/etc/cri/conf.d/hosts/_default/my-registry1-ca.crt'\n [host.'https://my-registry2']\n capabilities = ['pull', 'resolve']\n"), //nolint:lll }, }, }, "my-registry1": { Files: []*containerd.HostsFile{ { Name: "my-registry1-ca.crt", Mode: 0o600, Contents: []byte("allcert"), }, { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'https://my-registry1'\nca = '/etc/cri/conf.d/hosts/my-registry1/my-registry1-ca.crt'\n"), }, }, }, }, }, result) } func TestGenerateHostsWithHarbor(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ { EndpointEndpoint: "https://harbor/v2/mirrors/proxy.docker.io", EndpointOverridePath: true, }, }, }, "ghcr.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ { EndpointEndpoint: "https://harbor/v2/mirrors/proxy.ghcr.io", EndpointOverridePath: true, }, }, }, }, auths: map[string]*cri.RegistryAuthConfig{ "harbor": { RegistryUsername: "root", RegistryPassword: "secret", RegistryAuth: "auth", RegistryIdentityToken: "token", }, }, tlses: map[string]*cri.RegistryTLSConfig{ "harbor": { TLSInsecureSkipVerify: true, }, }, } result, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") require.NoError(t, err) t.Logf( "config %q", string(result.Directories["harbor"].Files[0].Contents), ) assert.Equal(t, &containerd.HostsConfig{ Directories: map[string]*containerd.HostsDirectory{ "docker.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://harbor/v2/mirrors/proxy.docker.io']\n capabilities = ['pull', 'resolve']\n override_path = true\n skip_verify = true\n"), }, }, }, "ghcr.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("[host]\n [host.'https://harbor/v2/mirrors/proxy.ghcr.io']\n capabilities = ['pull', 'resolve']\n override_path = true\n skip_verify = true\n"), }, }, }, "harbor": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'https://harbor'\nskip_verify = true\n"), }, }, }, }, }, result) } func TestGenerateHostsSkipFallback(t *testing.T) { cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "https://harbor/v2/mirrors/proxy.docker.io", EndpointOverridePath: true}, {EndpointEndpoint: "http://127.0.0.1:5001/v2/", EndpointOverridePath: true}, }, MirrorSkipFallback: true, }, "ghcr.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5002"}, }, MirrorSkipFallback: true, }, }, } result, err := containerd.GenerateHosts(cfg, "/etc/cri/conf.d/hosts") require.NoError(t, err) t.Logf( "config docker.io %q", string(result.Directories["docker.io"].Files[0].Contents), ) t.Logf( "config ghcr.io %q", string(result.Directories["ghcr.io"].Files[0].Contents), ) assert.Equal(t, &containerd.HostsConfig{ Directories: map[string]*containerd.HostsDirectory{ "docker.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'http://127.0.0.1:5001/v2/'\ncapabilities = ['pull', 'resolve']\noverride_path = true\n[host]\n [host.'https://harbor/v2/mirrors/proxy.docker.io']\n capabilities = ['pull', 'resolve']\n override_path = true\n"), //nolint:lll }, }, }, "ghcr.io": { Files: []*containerd.HostsFile{ { Name: "hosts.toml", Mode: 0o600, Contents: []byte("server = 'http://127.0.0.1:5002'\ncapabilities = ['pull', 'resolve']\n"), }, }, }, }, }, result) } ================================================ FILE: internal/pkg/containers/cri/containerd/testdata/cri.toml ================================================ [plugins] [plugins.'io.containerd.cri.v1.images'] [plugins.'io.containerd.cri.v1.images'.registry] config_path = '/etc/cri/conf.d/hosts' [plugins.'io.containerd.cri.v1.images'.registry.configs] [plugins.'io.containerd.cri.v1.images'.registry.configs.'registry-1.docker.io'] [plugins.'io.containerd.cri.v1.images'.registry.configs.'registry-1.docker.io'.auth] username = 'root' password = 'topsecret' auth = '' identitytoken = '' [plugins.'io.containerd.cri.v1.images'.registry.configs.'some.host:123'] [plugins.'io.containerd.cri.v1.images'.registry.configs.'some.host:123'.auth] username = 'root' password = 'secret' auth = 'auth' identitytoken = 'token' ================================================ FILE: internal/pkg/containers/cri/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cri implements containers.Inspector via CRI package cri import ( "context" "encoding/json" "fmt" "path" "strings" "syscall" "time" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" ctrs "github.com/siderolabs/talos/internal/pkg/containers" criclient "github.com/siderolabs/talos/internal/pkg/cri" "github.com/siderolabs/talos/pkg/machinery/constants" ) type inspector struct { client *criclient.Client ctx context.Context //nolint:containedctx } type inspectorOptions struct { criEndpoint string } // Option configures containerd Inspector. type Option func(*inspectorOptions) // WithCRIEndpoint configures CRI endpoint to use. func WithCRIEndpoint(endpoint string) Option { return func(o *inspectorOptions) { o.criEndpoint = endpoint } } // NewInspector builds new Inspector instance for CRI. func NewInspector(ctx context.Context, options ...Option) (ctrs.Inspector, error) { var err error opt := inspectorOptions{ criEndpoint: "unix:" + constants.CRIContainerdAddress, } for _, o := range options { o(&opt) } i := inspector{ ctx: ctx, } i.client, err = criclient.NewClient(opt.criEndpoint, 10*time.Second) if err != nil { return nil, err } return &i, nil } // Close frees associated resources. func (i *inspector) Close() error { return i.client.Close() } // Images returns a hash of image digest -> name. func (i *inspector) Images() (map[string]string, error) { images, err := i.client.ListImages(i.ctx, &runtimeapi.ImageFilter{}) if err != nil { return nil, err } result := make(map[string]string) for _, image := range images { if len(image.RepoTags) > 0 { result[image.Id] = image.RepoTags[0] } } return result, nil } func parseContainerDisplay(id string) (namespace, pod, name, containerID string) { namespace, pod, ok := strings.Cut(id, "/") if !ok { return "", "", id, "" } pod, name, ok = strings.Cut(pod, ":") if !ok { return namespace, pod, "", "" } name, containerID, ok = strings.Cut(name, ":") if !ok { return namespace, pod, name, "" } return namespace, pod, name, containerID } // Container returns info about a single container. // // If container is not found, Container returns nil. func (i *inspector) Container(id string) (*ctrs.Container, error) { namespace, pod, name, cntID := parseContainerDisplay(id) if pod == "" { return nil, nil } if name == "" { // request for a pod sandbox sandboxes, err := i.client.ListPodSandbox( i.ctx, &runtimeapi.PodSandboxFilter{ State: &runtimeapi.PodSandboxStateValue{ State: runtimeapi.PodSandboxState_SANDBOX_READY, }, LabelSelector: map[string]string{ "io.kubernetes.pod.name": pod, "io.kubernetes.pod.namespace": namespace, }, }, ) if err != nil { return nil, err } if len(sandboxes) == 0 { return nil, nil } pod, err := i.buildPod(sandboxes[0]) if err != nil { return nil, err } return pod.Containers[0], nil } // request for a container containers, err := i.client.ListContainers( i.ctx, &runtimeapi.ContainerFilter{ LabelSelector: map[string]string{ "io.kubernetes.pod.name": pod, "io.kubernetes.pod.namespace": namespace, "io.kubernetes.container.name": name, }, }, ) if err != nil { return nil, err } if len(containers) == 0 { return nil, nil } if cntID != "" { cnt, ok := findContainer(cntID, containers) if !ok { return nil, fmt.Errorf("container %q not found", id) } return i.buildContainer(cnt) } return i.buildContainer(containers[0]) } func findContainer(cntID string, containers []*runtimeapi.Container) (*runtimeapi.Container, bool) { // I'm sure that we can proabably find container using CRI labels, but // I'm not sure if it will work with partial IDs. for _, cnt := range containers { if strings.Contains(cnt.Id, cntID) { return cnt, true } } return nil, false } //nolint:gocyclo func (i *inspector) buildPod(sandbox *runtimeapi.PodSandbox) (*ctrs.Pod, error) { sandboxStatus, sandboxInfo, err := i.client.PodSandboxStatus(i.ctx, sandbox.Id) if err != nil { return nil, err } podName := sandbox.Metadata.Namespace + "/" + sandbox.Metadata.Name pod := &ctrs.Pod{ Name: podName, Containers: []*ctrs.Container{ { Inspector: i, Display: podName, Name: podName, ID: sandbox.Id, UID: sandbox.Metadata.Uid, PodName: podName, Status: sandboxStatus.State.String(), IsPodSandbox: true, Metrics: &ctrs.ContainerMetrics{ // assume pod sandbox uses zero }, }, }, } if info, ok := sandboxInfo["info"]; ok { var verboseInfo map[string]any if err := json.Unmarshal([]byte(info), &verboseInfo); err == nil { if pid, ok := verboseInfo["pid"]; ok { if fpid, ok := pid.(float64); ok { pod.Containers[0].Pid = uint32(fpid) } } if image, ok := verboseInfo["image"]; ok { if digest, ok := image.(string); ok { pod.Containers[0].Image = digest pod.Containers[0].Digest = digest } } if runtimeSpec, ok := verboseInfo["runtimeSpec"].(map[string]any); ok { if linuxSpec, ok := runtimeSpec["linux"].(map[string]any); ok { if namespaces, ok := linuxSpec["namespaces"].([]any); ok { for _, n := range namespaces { if nt, ok := n.(map[string]any); ok && nt["type"] == "network" { if netnsPath, ok := nt["path"].(string); ok { pod.Containers[0].NetworkNamespace = path.Base(netnsPath) break } } } } } } } } return pod, nil } func (i *inspector) buildContainer(container *runtimeapi.Container) (*ctrs.Container, error) { containerStatus, containerInfo, err := i.client.ContainerStatus(i.ctx, container.Id, true) if err != nil { return nil, err } podName := container.Labels["io.kubernetes.pod.namespace"] + "/" + container.Labels["io.kubernetes.pod.name"] ctr := &ctrs.Container{ Inspector: i, Display: podName + ":" + container.Metadata.Name + ":" + safeCut(container.Id, 12), Name: container.Metadata.Name, ID: container.Id, Digest: container.ImageRef, Image: container.ImageRef, PodName: podName, RestartCount: container.Annotations["io.kubernetes.container.restartCount"], Status: container.State.String(), LogPath: containerStatus.LogPath, } if info, ok := containerInfo["info"]; ok { var verboseInfo map[string]any if err := json.Unmarshal([]byte(info), &verboseInfo); err == nil { if pid, ok := verboseInfo["pid"]; ok { if fpid, ok := pid.(float64); ok { ctr.Pid = uint32(fpid) } } } } return ctr, nil } func safeCut(id string, i int) string { return id[:min(i, len(id))] } // Pods collects information about running pods & containers. // //nolint:gocyclo func (i *inspector) Pods() ([]*ctrs.Pod, error) { sandboxes, err := i.client.ListPodSandbox( i.ctx, &runtimeapi.PodSandboxFilter{ State: &runtimeapi.PodSandboxStateValue{ State: runtimeapi.PodSandboxState_SANDBOX_READY, }, }, ) if err != nil { return nil, err } containers, err := i.client.ListContainers(i.ctx, &runtimeapi.ContainerFilter{}) if err != nil { return nil, err } metrics, err := i.client.ListContainerStats(i.ctx, &runtimeapi.ContainerStatsFilter{}) if err != nil { return nil, err } metricsPerContainer := map[string]*runtimeapi.ContainerStats{} for _, metric := range metrics { metricsPerContainer[metric.Attributes.Id] = metric } images, err := i.Images() if err != nil { return nil, err } result := []*ctrs.Pod(nil) podMap := make(map[string]*ctrs.Pod) for _, sandbox := range sandboxes { pod, err := i.buildPod(sandbox) if err != nil { return nil, err } if imageName, ok := images[pod.Containers[0].Digest]; ok { pod.Containers[0].Image = imageName } result = append(result, pod) podMap[sandbox.Id] = pod } for _, container := range containers { pod := podMap[container.PodSandboxId] if pod == nil { // should never happen continue } ctr, err := i.buildContainer(container) if err != nil { return nil, err } if imageName, ok := images[ctr.Digest]; ok { ctr.Image = imageName } if metrics := metricsPerContainer[ctr.ID]; metrics != nil { ctr.Metrics = &ctrs.ContainerMetrics{} if metrics.Memory != nil && metrics.Memory.WorkingSetBytes != nil { ctr.Metrics.MemoryUsage = metrics.Memory.WorkingSetBytes.Value } if metrics.Cpu != nil && metrics.Cpu.UsageCoreNanoSeconds != nil { ctr.Metrics.CPUUsage = metrics.Cpu.UsageCoreNanoSeconds.Value } } pod.Containers = append(pod.Containers, ctr) } return result, nil } // GetProcessStderr returns process stderr. func (i *inspector) GetProcessStderr(string) (string, error) { // CRI doesn't seem to have an easy way to do that return "", nil } // Kill sends signal to container task. func (i *inspector) Kill(id string, isPodSandbox bool, _ syscall.Signal) error { if isPodSandbox { return i.client.StopPodSandbox(i.ctx, id) } return i.client.StopContainer(i.ctx, id, 10) } ================================================ FILE: internal/pkg/containers/cri/cri_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "context" "os" "path/filepath" "runtime/debug" "sync" "testing" "time" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/suite" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" ctrs "github.com/siderolabs/talos/internal/pkg/containers" "github.com/siderolabs/talos/internal/pkg/containers/cri" criclient "github.com/siderolabs/talos/internal/pkg/cri" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( busyboxImage = "docker.io/library/busybox:1.30.1" // busyboxImageDigest = "sha256:64f5d945efcc0f39ab11b3cd4ba403cc9fefe1fa3613123ca016cf3708e8cafb". // pauseImage = "k8s.gcr.io/pause:3.1". // pauseImageDigest = "sha256:da86e6ba6ca197bf6bc5e9d900febd906b133eaa4750e6bed647b0fbe50ed43e". ) func MockEventSink(state events.ServiceState, message string, args ...any) { } type CRISuite struct { suite.Suite tmpDir string containerdRunner runner.Runner containerdWg sync.WaitGroup containerdAddress string client *criclient.Client ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc inspector ctrs.Inspector pods []string } func (suite *CRISuite) SetupSuite() { if cgroups.Mode() == cgroups.Unified { suite.T().Skip("test doesn't pass under cgroupsv2") } var err error suite.tmpDir = suite.T().TempDir() stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root") suite.Require().NoError(os.Mkdir(stateDir, 0o777)) suite.Require().NoError(os.Mkdir(rootDir, 0o777)) suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock") if cgroups.Mode() == cgroups.Unified { var ( groupPath string manager *cgroup2.Manager ) groupPath, err = cgroup2.NestedGroupPath(suite.tmpDir) suite.Require().NoError(err) manager, err = cgroup2.NewManager(constants.CgroupMountPath, groupPath, &cgroup2.Resources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } else { var manager cgroup1.Cgroup manager, err = cgroup1.New(cgroup1.NestedPath(suite.tmpDir), &specs.LinuxResources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } args := &runner.Args{ ID: "containerd", ProcessArgs: []string{ "/bin/containerd", "--address", suite.containerdAddress, "--state", stateDir, "--root", rootDir, "--config", constants.CRIContainerdConfig, }, } suite.containerdRunner = process.NewRunner( false, args, runner.WithLoggingManager(logging.NewFileLoggingManager(suite.tmpDir)), runner.WithEnv([]string{constants.EnvPathWithBin}), runner.WithCgroupPath(suite.tmpDir), ) suite.Require().NoError(suite.containerdRunner.Open()) suite.containerdWg.Go(func() { defer suite.containerdRunner.Close() //nolint:errcheck suite.containerdRunner.Run(MockEventSink) //nolint:errcheck }) suite.client, err = criclient.NewClient("unix:"+suite.containerdAddress, 30*time.Second) suite.Require().NoError(err) suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) suite.inspector, err = cri.NewInspector(suite.ctx, cri.WithCRIEndpoint("unix:"+suite.containerdAddress)) suite.Require().NoError(err) } func (suite *CRISuite) TearDownSuite() { suite.ctxCancel() suite.Require().NoError(suite.inspector.Close()) suite.Require().NoError(suite.client.Close()) suite.Require().NoError(suite.containerdRunner.Stop()) suite.containerdWg.Wait() } func (suite *CRISuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) suite.pods = nil podSandboxConfig := &runtimeapi.PodSandboxConfig{ Metadata: &runtimeapi.PodSandboxMetadata{ Name: "etcd-master-1", Uid: "ed1a599a53090941c9b4025c7e3e883d", Namespace: "kube-system", Attempt: 0, }, Labels: map[string]string{ "io.kubernetes.pod.name": "etcd-master-1", "io.kubernetes.pod.namespace": "kube-system", }, LogDirectory: suite.tmpDir, Linux: &runtimeapi.LinuxPodSandboxConfig{ SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{ NamespaceOptions: &runtimeapi.NamespaceOption{ Network: runtimeapi.NamespaceMode_NODE, }, }, }, } podSandboxID, err := suite.client.RunPodSandbox(suite.ctx, podSandboxConfig, "") suite.Require().NoError(err) suite.pods = append(suite.pods, podSandboxID) suite.Require().Len(podSandboxID, 64) imageRef, err := suite.client.PullImage( suite.ctx, &runtimeapi.ImageSpec{ Image: busyboxImage, }, podSandboxConfig, ) suite.Require().NoError(err) ctrID, err := suite.client.CreateContainer( suite.ctx, podSandboxID, &runtimeapi.ContainerConfig{ Metadata: &runtimeapi.ContainerMetadata{ Name: "etcd", }, Labels: map[string]string{ "io.kubernetes.container.name": "etcd", "io.kubernetes.pod.name": "etcd-master-1", "io.kubernetes.pod.namespace": "kube-system", }, Annotations: map[string]string{ "io.kubernetes.container.restartCount": "1", }, Image: &runtimeapi.ImageSpec{ Image: imageRef, }, Command: []string{"/bin/sh", "-c", "sleep 3600"}, }, podSandboxConfig, ) suite.Require().NoError(err) suite.Require().Len(ctrID, 64) err = suite.client.StartContainer(suite.ctx, ctrID) suite.Require().NoError(err) } func (suite *CRISuite) TearDownTest() { for _, pod := range suite.pods { suite.Require().NoError(suite.client.StopPodSandbox(suite.ctx, pod)) suite.Require().NoError(suite.client.RemovePodSandbox(suite.ctx, pod)) } suite.ctxCancel() } func (suite *CRISuite) TestPods() { pods, err := suite.inspector.Pods() suite.Require().NoError(err) suite.Require().Len(pods, 1) suite.Assert().Equal("kube-system/etcd-master-1", pods[0].Name) suite.Require().Len(pods[0].Containers, 2) suite.Assert().Equal(pods[0].Name, pods[0].Containers[0].Display) suite.Assert().Equal(pods[0].Name, pods[0].Containers[0].Name) suite.Assert().Equal("SANDBOX_READY", pods[0].Containers[0].Status) // suite.Assert().Equal(pauseImageDigest, pods[0].Containers[0].Digest) // suite.Assert().Equal(pauseImage, pods[0].Containers[0].Image) suite.Assert().True(pods[0].Containers[0].Pid > 0) suite.Assert().Equal("kube-system/etcd-master-1:etcd", pods[0].Containers[1].Display) suite.Assert().Equal("etcd", pods[0].Containers[1].Name) // suite.Assert().Equal(busyboxImage, pods[0].Containers[1].Image) // suite.Assert().Equal(busyboxImageDigest, pods[0].Containers[1].Digest) suite.Assert().Equal("CONTAINER_RUNNING", pods[0].Containers[1].Status) suite.Assert().Equal("1", pods[0].Containers[1].RestartCount) suite.Assert().True(pods[0].Containers[1].Pid > 0) } func (suite *CRISuite) TestContainer() { defer func() { r := recover() if r != nil { t := suite.T() t.Errorf("test panicked: %v %s", r, debug.Stack()) t.FailNow() } }() container, err := suite.inspector.Container("kube-system/etcd-master-1") suite.Require().NoError(err) suite.Assert().Equal("kube-system/etcd-master-1", container.Display) suite.Assert().Equal(container.Display, container.Name) suite.Assert().Equal("SANDBOX_READY", container.Status) // suite.Assert().Equal(pauseImageDigest, container.Digest) suite.Assert().True(container.Pid > 0) container, err = suite.inspector.Container("kube-system/etcd-master-1:etcd") suite.Require().NoError(err) suite.Assert().Equal("kube-system/etcd-master-1:etcd", container.Display) suite.Assert().Equal("etcd", container.Name) suite.Assert().Equal("CONTAINER_RUNNING", container.Status) // suite.Assert().Equal(busyboxImageDigest, container.Image) // suite.Assert().Equal(busyboxImageDigest, container.Digest) suite.Assert().Equal("1", container.RestartCount) suite.Assert().True(container.Pid > 0) container, err = suite.inspector.Container("kube-system/etcd-master-1:etcd2") suite.Require().NoError(err) suite.Require().Nil(container) container, err = suite.inspector.Container("kube-system/etcd-master-2") suite.Require().NoError(err) suite.Require().Nil(container) container, err = suite.inspector.Container("talos") suite.Require().NoError(err) suite.Require().Nil(container) } func TestCRISuite(t *testing.T) { if os.Getuid() != 0 { t.Skip("can't run the test as non-root") } _, err := os.Stat("/bin/containerd") if err != nil { t.Skip("containerd binary is not available, skipping the test") } suite.Run(t, new(CRISuite)) } ================================================ FILE: internal/pkg/containers/image/console/progress.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package console provides a console-based implementation of image pull progress reporting. package console import ( "log" "sync" "time" "github.com/dustin/go-humanize" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/progress" ) // ReportInterval is the interval between progress reports. const ReportInterval = 15 * time.Second type layerProgress struct { status progress.LayerPullStatus offset int64 total int64 } // ProgressReporter reports image pull progress to the console. type ProgressReporter struct { imageRef string mu sync.Mutex layers map[string]*layerProgress stopCh chan struct{} } // NewProgressReporter creates a new ProgressReporter. func NewProgressReporter(imageRef string) image.ProgressReporter { return &ProgressReporter{ imageRef: imageRef, } } // Update implements UpdateFn interface. func (c *ProgressReporter) Update(upd progress.LayerPullProgress) { c.mu.Lock() defer c.mu.Unlock() if c.layers == nil { c.layers = make(map[string]*layerProgress) } lp, ok := c.layers[upd.LayerID] if !ok { lp = &layerProgress{} c.layers[upd.LayerID] = lp } if upd.Status == progress.LayerPullStatusDownloading { lp.total = upd.Total lp.offset = upd.Offset } else { lp.offset = lp.total } lp.status = upd.Status } // Start implements ProgressReporter interface. func (c *ProgressReporter) Start() { c.stopCh = make(chan struct{}) go func() { ticker := time.NewTicker(ReportInterval) defer ticker.Stop() c.reportProgress() for { select { case <-ticker.C: c.reportProgress() case <-c.stopCh: return } } }() } // Stop implements ProgressReporter interface. func (c *ProgressReporter) Stop() { close(c.stopCh) } func (c *ProgressReporter) reportProgress() { c.mu.Lock() defer c.mu.Unlock() if len(c.layers) == 0 { log.Printf("pulling image %s: starting...", c.imageRef) return } var ( anyDownloading bool overallOffset int64 overallTotal int64 ) for _, l := range c.layers { if l.status == progress.LayerPullStatusDownloading { anyDownloading = true } overallOffset += l.offset overallTotal += l.total } if !anyDownloading { log.Printf("pulling image %s: extracting...", c.imageRef) return } var percentage float64 if overallTotal > 0 { percentage = float64(overallOffset) / float64(overallTotal) * 100.0 } log.Printf("pulling image %s: downloading %d layers (%s/%s) (%.2f%%)...", c.imageRef, len(c.layers), humanize.IBytes(uint64(overallOffset)), humanize.IBytes(uint64(overallTotal)), percentage, ) } ================================================ FILE: internal/pkg/containers/image/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package image import ( "context" "errors" "fmt" stdlog "log" "maps" "time" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/pkg/snapshotters" "github.com/containerd/errdefs" "github.com/containerd/log" "github.com/containerd/platforms" "github.com/cosi-project/runtime/pkg/state" "github.com/distribution/reference" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/siderolabs/go-retry/retry" "github.com/sirupsen/logrus" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/pkg/containers/image/progress" "github.com/siderolabs/talos/internal/pkg/containers/image/verify" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // Image pull retry settings. const ( PullTimeout = 20 * time.Minute PullRetryInterval = 5 * time.Second ) // PullOption is an option for Pull function. type PullOption func(*PullOptions) // PullOptions configure Pull function. type PullOptions struct { SkipIfAlreadyPulled bool MaxNotFoundRetries int NewProgressReporter NewProgressReporter } // DefaultPullOptions returns default options for Pull function. func DefaultPullOptions() PullOptions { return PullOptions{ SkipIfAlreadyPulled: false, MaxNotFoundRetries: 5, } } // WithSkipIfAlreadyPulled skips pulling if image is already pulled and unpacked. func WithSkipIfAlreadyPulled() PullOption { return func(opts *PullOptions) { opts.SkipIfAlreadyPulled = true } } // WithMaxNotFoundRetries sets the maximum number of retries for not found errors. func WithMaxNotFoundRetries(maxRetries int) PullOption { return func(opts *PullOptions) { opts.MaxNotFoundRetries = maxRetries } } // WithProgressReporter enables reporting pull progress. func WithProgressReporter(newReporter NewProgressReporter) PullOption { return func(opts *PullOptions) { opts.NewProgressReporter = newReporter } } // ProgressReporter is an interface for reporting image pull progress. type ProgressReporter interface { Start() Stop() Update(progress.LayerPullProgress) } // NewProgressReporter creates a new progress reporter. type NewProgressReporter func(imageRef string) ProgressReporter // RegistriesBuilder is a function that returns registries configuration. type RegistriesBuilder = func(context.Context) (cri.Registries, error) // NewSimpleProgressReporter creates a simple progress reporter that just needs Update function. func NewSimpleProgressReporter(updateFn func(progress.LayerPullProgress)) NewProgressReporter { return func(imageRef string) ProgressReporter { return &simpleProgressReporter{ updateFn: updateFn, } } } type simpleProgressReporter struct { updateFn func(progress.LayerPullProgress) } func (s *simpleProgressReporter) Start() {} func (s *simpleProgressReporter) Stop() {} func (s *simpleProgressReporter) Update(p progress.LayerPullProgress) { s.updateFn(p) } // Pull is a convenience function that wraps the containerd image pull func with // retry functionality. // //nolint:gocyclo,cyclop func Pull( ctx context.Context, registryBuilder RegistriesBuilder, resources state.State, client *containerd.Client, ref string, opt ...PullOption, ) (img containerd.Image, err error) { opts := DefaultPullOptions() for _, o := range opt { o(&opts) } namedRef, err := reference.ParseDockerRef(ref) if err != nil { return nil, fmt.Errorf("failed to parse image reference %q: %w", ref, err) } // normalize reference ref = namedRef.String() if opts.SkipIfAlreadyPulled { img, err = client.GetImage(ctx, ref) if err == nil { var unpacked bool unpacked, err = img.IsUnpacked(ctx, "") if err == nil && unpacked { if err = manageAliases(ctx, client, namedRef, img); err == nil { return img, nil } } } } containerdLogger := logrus.New() containerdLogger.Out = stdlog.Default().Writer() containerdLogger.Formatter = &logrus.TextFormatter{ DisableColors: true, DisableQuote: true, DisableTimestamp: true, } ctx = log.WithLogger(ctx, containerdLogger.WithField("image", ref)) notFoundErrors := 0 err = retry.Exponential(PullTimeout, retry.WithUnits(PullRetryInterval), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error { registriesConfig, err := registryBuilder(ctx) if err != nil { return fmt.Errorf("failed to get configured registries: %w", err) } resolver := NewResolver(registriesConfig) verifyResult, err := verify.ImageSignature(ctx, zap.NewNop(), resources, resolver, ref) if err != nil { switch status.Code(err) { //nolint:exhaustive case codes.PermissionDenied: // verification denied by matched rule, no need to retry return errors.New(status.Convert(err).Message()) case codes.NotFound: // verification failed because image not found, no need to retry notFoundErrors++ if notFoundErrors > opts.MaxNotFoundRetries { return err } return retry.ExpectedError(err) default: return retry.ExpectedError(err) } } containerdRemoteOpts := []containerd.RemoteOpt{ containerd.WithPullUnpack, containerd.WithChildLabelMap(images.ChildGCLabelsFilterLayers), containerd.WithPlatformMatcher(platforms.Default()), containerd.WithResolver(resolver), } pullRef := ref if verifyResult.Verified { containerdRemoteOpts = append(containerdRemoteOpts, containerd.WithPullLabel(constants.ImageLabelVerified, verifyResult.Message), ) pullRef = verifyResult.DigestedImageRef } if opts.NewProgressReporter != nil { reporter := opts.NewProgressReporter(ref) reporter.Start() defer reporter.Stop() pp := progress.NewPullProgress( client.ContentStore(), client.SnapshotService("overlayfs"), reporter.Update, ) finishProgress := pp.ShowProgress(ctx) defer finishProgress() containerdRemoteOpts = append(containerdRemoteOpts, containerd.WithImageHandler( images.HandlerFunc( func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { if images.IsLayerType(desc.MediaType) { pp.Add(desc) } return nil, nil }, ), ), containerd.WithImageHandlerWrapper(snapshotters.AppendInfoHandlerWrapper(ref)), ) } if img, err = client.Pull( ctx, pullRef, containerdRemoteOpts..., ); err != nil { err = fmt.Errorf("failed to pull image %q: %w", ref, err) if errors.Is(err, errdefs.ErrNotFound) { notFoundErrors++ if notFoundErrors > opts.MaxNotFoundRetries { return err } } return retry.ExpectedError(err) } return nil }) if err != nil { return nil, err } if err = manageAliases(ctx, client, namedRef, img); err != nil { return nil, err } return img, nil } func manageAliases(ctx context.Context, client *containerd.Client, namedRef reference.Named, img containerd.Image) error { // re-tag pulled image imageDigest := img.Target().Digest.String() refs := []string{imageDigest} if _, ok := namedRef.(reference.NamedTagged); ok { refs = append(refs, namedRef.String()) } if _, ok := namedRef.(reference.Canonical); ok { refs = append(refs, namedRef.String()) } else { refs = append(refs, namedRef.Name()+"@"+imageDigest) } for _, newRef := range refs { if err := createAlias(ctx, client, newRef, img.Target(), img.Labels()); err != nil { return err } } return nil } func createAlias(ctx context.Context, client *containerd.Client, name string, desc ocispec.Descriptor, labels map[string]string) error { img := images.Image{ Name: name, Target: desc, Labels: labels, } oldImg, err := client.ImageService().Create(ctx, img) if err == nil || !errdefs.IsAlreadyExists(err) { return err } if oldImg.Target.Digest == img.Target.Digest && maps.Equal(oldImg.Labels, img.Labels) { return nil } _, err = client.ImageService().Update(ctx, img, "target", "labels") return err } ================================================ FILE: internal/pkg/containers/image/progress/pull_progress.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package progress provides functionality to track and report image pull progress. package progress import ( "context" "errors" "log" "slices" "sync" "time" "github.com/containerd/containerd/v2/core/content" "github.com/containerd/containerd/v2/core/images" "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/containerd/v2/core/snapshots" "github.com/containerd/containerd/v2/pkg/snapshotters" "github.com/containerd/errdefs" "github.com/moby/moby/client/pkg/stringid" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) // LayerPullStatus represents the status of a single image layer during pull. type LayerPullStatus int // Possible values for LayerPullStatus. const ( LayerPullStatusDownloading LayerPullStatus = iota LayerPullStatusDownloadComplete LayerPullStatusExtracting LayerPullStatusExtractComplete LayerPullStatusAlreadyExists ) // LayerPullProgress represents the progress of an image pull operation. // // Note: keep this in sync machine/images.proto. type LayerPullProgress struct { LayerID string Status LayerPullStatus // If Status is Extracting, this shows the elapsed time since extraction started. Elapsed time.Duration // If Status is Downloading, these show the current offset and total size of the layer. Offset int64 Total int64 } // UpdateFn is used by PullProgress to report progress updates. type UpdateFn func(LayerPullProgress) // PullProgress tracks and reports the progress of image pulls. type PullProgress struct { store content.Store snapshotter snapshots.Snapshotter updateFn UpdateFn mu sync.Mutex descs map[digest.Digest]ocispec.Descriptor // guarded by mu layers []ocispec.Descriptor unpackStart map[digest.Digest]time.Time } // NewPullProgress creates a new PullProgress instance. func NewPullProgress(store content.Store, snapshotter snapshots.Snapshotter, fn UpdateFn) *PullProgress { return &PullProgress{ updateFn: fn, store: store, snapshotter: snapshotter, descs: make(map[digest.Digest]ocispec.Descriptor), mu: sync.Mutex{}, } } // ShowProgress starts tracking and reporting pull progress. func (p *PullProgress) ShowProgress(ctx context.Context) func() { ctx, cancel := context.WithCancel(ctx) start := time.Now() lastUpdate := make(chan struct{}) go func() { ticker := time.NewTicker(100 * time.Millisecond) defer ticker.Stop() for { select { case <-ctx.Done(): ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), time.Millisecond*500) defer cancel() if err := p.trackProgress(ctx, start); err != nil { log.Printf("failed to write pull progress: %s", err.Error()) } close(lastUpdate) return case <-ticker.C: if err := p.trackProgress(ctx, start); err != nil { if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { log.Printf("updating progress failed %s", err.Error()) } } } } }() // call this when done pulling to stop progress updates return func() { cancel() <-lastUpdate } } func (p *PullProgress) trackProgress(ctx context.Context, start time.Time) error { err := p.trackOngoingPulls(ctx, start) if err != nil { return err } return p.trackPulledLayers(ctx) } func (p *PullProgress) trackOngoingPulls(ctx context.Context, start time.Time) error { //nolint:gocyclo actives, err := p.store.ListStatuses(ctx, "") if err != nil { if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { return err } return nil } pulling := make(map[string]content.Status, len(actives)) for _, status := range actives { pulling[status.Ref] = status } for _, j := range p.jobs() { key := remotes.MakeRefKey(ctx, j) if info, ok := pulling[key]; ok { if info.Offset == 0 { continue } p.updateFn(LayerPullProgress{ LayerID: stringid.TruncateID(j.Digest.Encoded()), Status: LayerPullStatusDownloading, Offset: info.Offset, Total: info.Total, }) continue } info, err := p.store.Info(ctx, j.Digest) switch { case err != nil: if !errdefs.IsNotFound(err) { return err } case info.CreatedAt.After(start): p.updateFn(LayerPullProgress{ LayerID: stringid.TruncateID(j.Digest.Encoded()), Status: LayerPullStatusDownloadComplete, }) if images.IsLayerType(j.MediaType) { p.layers = append(p.layers, j) } p.remove(j) default: p.updateFn(LayerPullProgress{ LayerID: stringid.TruncateID(j.Digest.Encoded()), Status: LayerPullStatusAlreadyExists, }) if images.IsLayerType(j.MediaType) { p.layers = append(p.layers, j) } p.remove(j) } } return nil } //nolint:gocyclo func (p *PullProgress) trackPulledLayers(ctx context.Context) error { var committedIdx []int for idx, desc := range p.layers { walkFilter := "labels.\"" + snapshotters.TargetLayerDigestLabel + "\"==" + p.layers[idx].Digest.String() err := p.snapshotter.Walk(ctx, func(ctx context.Context, sn snapshots.Info) error { if sn.Kind == snapshots.KindActive { if p.unpackStart == nil { p.unpackStart = make(map[digest.Digest]time.Time) } var elapsed time.Duration if began, ok := p.unpackStart[desc.Digest]; !ok { p.unpackStart[desc.Digest] = time.Now() } else { elapsed = time.Since(began) } p.updateFn(LayerPullProgress{ LayerID: stringid.TruncateID(desc.Digest.Encoded()), Status: LayerPullStatusExtracting, Elapsed: elapsed, }) return nil } if sn.Kind == snapshots.KindCommitted { p.updateFn( LayerPullProgress{ LayerID: stringid.TruncateID(desc.Digest.Encoded()), Status: LayerPullStatusExtractComplete, }, ) committedIdx = append(committedIdx, idx) return nil } return nil }, walkFilter) if err != nil { return err } } // remove finished/committed layers from p.layers if len(committedIdx) > 0 { slices.Sort(committedIdx) for i := len(committedIdx) - 1; i >= 0; i-- { if i < len(committedIdx)-1 && committedIdx[i] == committedIdx[i+1] { continue } p.layers = slices.Delete(p.layers, committedIdx[i], committedIdx[i]+1) } } return nil } // Add adds new descriptors to be tracked. func (p *PullProgress) Add(desc ...ocispec.Descriptor) { p.mu.Lock() defer p.mu.Unlock() for _, d := range desc { if _, ok := p.descs[d.Digest]; ok { continue } p.descs[d.Digest] = d } } func (p *PullProgress) remove(desc ocispec.Descriptor) { p.mu.Lock() defer p.mu.Unlock() delete(p.descs, desc.Digest) } func (p *PullProgress) jobs() []ocispec.Descriptor { p.mu.Lock() defer p.mu.Unlock() descs := make([]ocispec.Descriptor, 0, len(p.descs)) for _, d := range p.descs { descs = append(descs, d) } return descs } ================================================ FILE: internal/pkg/containers/image/resolver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package image import ( "encoding/base64" "fmt" "net/http" "net/url" "path" "strings" "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/containerd/v2/core/remotes/docker" "github.com/hashicorp/go-cleanhttp" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) // NewResolver builds registry resolver based on Talos configuration. func NewResolver(reg cri.Registries) remotes.Resolver { return docker.NewResolver(docker.ResolverOptions{ Hosts: RegistryHosts(reg), }) } // RegistryHosts returns host configuration per registry. // //nolint:gocyclo func RegistryHosts(reg cri.Registries) docker.RegistryHosts { return func(host string) ([]docker.RegistryHost, error) { var registries []docker.RegistryHost endpoints, err := RegistryEndpoints(reg, host) if err != nil { return nil, err } for _, endpoint := range endpoints { u, err := url.Parse(endpoint.Endpoint) if err != nil { return nil, fmt.Errorf("error parsing endpoint %q for host %q: %w", endpoint.Endpoint, host, err) } transport := newTransport() client := &http.Client{Transport: transport} registryTLSConfig := reg.TLSs()[u.Host] registryAuthConfig := reg.Auths()[u.Host] if u.Scheme != "https" && registryTLSConfig != nil { return nil, fmt.Errorf("TLS config specified for non-HTTPS registry: %q", u.Host) } if registryTLSConfig != nil { transport.TLSClientConfig, err = registryTLSConfig.GetTLSConfig() if err != nil { return nil, fmt.Errorf("error preparing TLS config for %q: %w", u.Host, err) } // set up refreshing Root CAs if none were provided if transport.TLSClientConfig.RootCAs == nil { transport.TLSClientConfig.RootCAs = httpdefaults.RootCAs() } } if u.Path == "" { if !endpoint.OverridePath { u.Path = "/v2" } } else { u.Path = path.Clean(u.Path) if !strings.HasSuffix(u.Path, "/v2") && !endpoint.OverridePath { u.Path += "/v2" } } uu := u registries = append(registries, docker.RegistryHost{ Client: client, Authorizer: docker.NewDockerAuthorizer( docker.WithAuthClient(client), docker.WithAuthCreds(func(host string) (string, string, error) { if registryAuthConfig == nil { return "", "", nil } return PrepareAuth(registryAuthConfig, uu.Host, host) })), Host: uu.Host, Scheme: uu.Scheme, Path: uu.Path, Capabilities: docker.HostCapabilityResolve | docker.HostCapabilityPull, }) } return registries, nil } } // EndpointEntry represents a registry endpoint. type EndpointEntry struct { Endpoint string OverridePath bool } // RegistryEndpointEntriesFromConfig returns registry endpoints per host. func RegistryEndpointEntriesFromConfig(host string, reg config.RegistryMirrorConfig) ([]EndpointEntry, error) { entries := xslices.Map(reg.Endpoints(), func(endpoint config.RegistryEndpointConfig) EndpointEntry { return EndpointEntry{Endpoint: endpoint.Endpoint(), OverridePath: endpoint.OverridePath()} }) if reg.SkipFallback() { return entries, nil } defaultHost, err := docker.DefaultHost(host) if err != nil { return nil, fmt.Errorf("error getting default host for %q: %w", host, err) } entries = append(entries, EndpointEntry{Endpoint: "https://" + defaultHost, OverridePath: false}) return entries, nil } // RegistryEndpoints returns registry endpoints per host using reg. func RegistryEndpoints(reg cri.Registries, host string) (endpoints []EndpointEntry, err error) { // direct hit by host if hostConfig, ok := reg.Mirrors()[host]; ok { return RegistryEndpointEntriesFromConfig(host, hostConfig) } // '*' if catchAllConfig, ok := reg.Mirrors()["*"]; ok { return RegistryEndpointEntriesFromConfig(host, catchAllConfig) } // still no endpoints, use default defaultHost, err := docker.DefaultHost(host) if err != nil { return nil, fmt.Errorf("error getting default host for %q: %w", host, err) } return []EndpointEntry{ { Endpoint: "https://" + defaultHost, OverridePath: false, }, }, nil } // PrepareAuth returns authentication info in the format expected by containerd. func PrepareAuth(auth config.RegistryAuthConfig, host, expectedHost string) (string, string, error) { if auth == nil { return "", "", nil } if expectedHost != host { // don't pass auth to unknown hosts return "", "", nil } if auth.Username() != "" { return auth.Username(), auth.Password(), nil } if auth.IdentityToken() != "" { return "", auth.IdentityToken(), nil } if auth.Auth() != "" { decLen := base64.StdEncoding.DecodedLen(len(auth.Auth())) decoded := make([]byte, decLen) _, err := base64.StdEncoding.Decode(decoded, []byte(auth.Auth())) if err != nil { return "", "", fmt.Errorf("error parsing auth for %q: %w", host, err) } fields := strings.SplitN(string(decoded), ":", 2) if len(fields) != 2 { return "", "", fmt.Errorf("invalid decoded auth for %q: %q", host, decoded) } user, password := fields[0], fields[1] return user, strings.Trim(password, "\x00"), nil } return "", "", fmt.Errorf("invalid auth config for %q", host) } // newTransport creates HTTP transport with default settings. func newTransport() *http.Transport { return httpdefaults.PatchTransport(cleanhttp.DefaultTransport()) } ================================================ FILE: internal/pkg/containers/image/resolver_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package image_test import ( "context" "net/http" "testing" "github.com/opencontainers/go-digest" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) type mockConfig struct { mirrors map[string]*cri.RegistryMirrorConfig auths map[string]*cri.RegistryAuthConfig tlses map[string]*cri.RegistryTLSConfig } func (c *mockConfig) Mirrors() map[string]config.RegistryMirrorConfig { mirrors := make(map[string]config.RegistryMirrorConfig, len(c.mirrors)) for k, v := range c.mirrors { mirrors[k] = v } return mirrors } func (c *mockConfig) Auths() map[string]config.RegistryAuthConfig { auths := make(map[string]config.RegistryAuthConfig, len(c.auths)) for k, v := range c.auths { auths[k] = v } return auths } func (c *mockConfig) TLSs() map[string]cri.RegistryTLSConfigExtended { registries := make(map[string]cri.RegistryTLSConfigExtended, len(c.tlses)) for k, v := range c.tlses { registries[k] = v } return registries } type ResolverSuite struct { suite.Suite } func (suite *ResolverSuite) TestRegistryEndpoints() { type request struct { host string expectedEndpoints []image.EndpointEntry } for _, tt := range []struct { name string config *mockConfig requests []request }{ { name: "no config", config: &mockConfig{}, requests: []request{ { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://registry-1.docker.io", }, }, }, { host: "quay.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://quay.io", }, }, }, }, }, { name: "config with mirror and no fallback", config: &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5000"}, {EndpointEndpoint: "https://some.host"}, }, MirrorSkipFallback: true, }, }, }, requests: []request{ { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5000", }, { Endpoint: "https://some.host", }, }, }, { host: "quay.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://quay.io", }, }, }, }, }, { name: "config with mirror and fallback", config: &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "ghcr.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5000"}, {EndpointEndpoint: "https://some.host"}, }, }, }, }, requests: []request{ { host: "ghcr.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5000", }, { Endpoint: "https://some.host", }, { Endpoint: "https://ghcr.io", }, }, }, { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://registry-1.docker.io", }, }, }, }, }, { name: "config with catch-all and no fallback", config: &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5000"}, {EndpointEndpoint: "https://some.host"}, }, MirrorSkipFallback: true, }, "*": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5001"}, }, MirrorSkipFallback: true, }, }, }, requests: []request{ { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5000", }, { Endpoint: "https://some.host", }, }, }, { host: "quay.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5001", }, }, }, }, }, { name: "config with catch-all and fallback", config: &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "*": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5001"}, }, }, }, }, requests: []request{ { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5001", }, { Endpoint: "https://registry-1.docker.io", }, }, }, { host: "quay.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "http://127.0.0.1:5001", }, { Endpoint: "https://quay.io", }, }, }, }, }, { name: "config with override path", config: &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ { EndpointEndpoint: "https://harbor/v2/registry.docker.io", EndpointOverridePath: true, }, }, MirrorSkipFallback: true, }, "ghcr.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ { EndpointEndpoint: "https://harbor/v2/registry.ghcr.io", EndpointOverridePath: true, }, }, }, }, }, requests: []request{ { host: "docker.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://harbor/v2/registry.docker.io", OverridePath: true, }, }, }, { host: "ghcr.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://harbor/v2/registry.ghcr.io", OverridePath: true, }, { Endpoint: "https://ghcr.io", }, }, }, { host: "quay.io", expectedEndpoints: []image.EndpointEntry{ { Endpoint: "https://quay.io", }, }, }, }, }, } { suite.Run(tt.name, func() { for _, req := range tt.requests { suite.Run(req.host, func() { endpoints, err := image.RegistryEndpoints(tt.config, req.host) suite.Assert().NoError(err) suite.Assert().Equal(req.expectedEndpoints, endpoints) }) } }) } } func (suite *ResolverSuite) TestPrepareAuth() { user, pass, err := image.PrepareAuth(nil, "docker.io", "docker.io") suite.Assert().NoError(err) suite.Assert().Equal("", user) suite.Assert().Equal("", pass) user, pass, err = image.PrepareAuth(&cri.RegistryAuthConfig{ RegistryUsername: "root", RegistryPassword: "secret", }, "docker.io", "not.docker.io") suite.Assert().NoError(err) suite.Assert().Equal("", user) suite.Assert().Equal("", pass) user, pass, err = image.PrepareAuth(&cri.RegistryAuthConfig{ RegistryUsername: "root", RegistryPassword: "secret", }, "docker.io", "docker.io") suite.Assert().NoError(err) suite.Assert().Equal("root", user) suite.Assert().Equal("secret", pass) user, pass, err = image.PrepareAuth(&cri.RegistryAuthConfig{ RegistryIdentityToken: "xyz", }, "docker.io", "docker.io") suite.Assert().NoError(err) suite.Assert().Equal("", user) suite.Assert().Equal("xyz", pass) user, pass, err = image.PrepareAuth(&cri.RegistryAuthConfig{ RegistryAuth: "dXNlcjE6c2VjcmV0MQ==", }, "docker.io", "docker.io") suite.Assert().NoError(err) suite.Assert().Equal("user1", user) suite.Assert().Equal("secret1", pass) _, _, err = image.PrepareAuth(&cri.RegistryAuthConfig{}, "docker.io", "docker.io") suite.Assert().EqualError(err, "invalid auth config for \"docker.io\"") } func (suite *ResolverSuite) TestRegistryHosts() { registryHosts, err := image.RegistryHosts(&mockConfig{})("docker.io") suite.Require().NoError(err) suite.Assert().Len(registryHosts, 1) suite.Assert().Equal("https", registryHosts[0].Scheme) suite.Assert().Equal("registry-1.docker.io", registryHosts[0].Host) suite.Assert().Equal("/v2", registryHosts[0].Path) suite.Assert().Nil(registryHosts[0].Client.Transport.(*http.Transport).TLSClientConfig.Certificates) cfg := &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ {EndpointEndpoint: "http://127.0.0.1:5000/docker.io"}, {EndpointEndpoint: "https://some.host"}, }, MirrorSkipFallback: true, }, "ghcr.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{ { EndpointEndpoint: "https://harbor/v2/registry.ghcr.io", EndpointOverridePath: true, }, }, MirrorSkipFallback: true, }, }, } registryHosts, err = image.RegistryHosts(cfg)("docker.io") suite.Require().NoError(err) suite.Assert().Len(registryHosts, 2) suite.Assert().Equal("http", registryHosts[0].Scheme) suite.Assert().Equal("127.0.0.1:5000", registryHosts[0].Host) suite.Assert().Equal("/docker.io/v2", registryHosts[0].Path) suite.Assert().Nil(registryHosts[0].Client.Transport.(*http.Transport).TLSClientConfig.Certificates) suite.Assert().Equal("https", registryHosts[1].Scheme) suite.Assert().Equal("some.host", registryHosts[1].Host) suite.Assert().Equal("/v2", registryHosts[1].Path) suite.Assert().Nil(registryHosts[1].Client.Transport.(*http.Transport).TLSClientConfig.Certificates) registryHosts, err = image.RegistryHosts(cfg)("ghcr.io") suite.Require().NoError(err) suite.Assert().Len(registryHosts, 1) suite.Assert().Equal("https", registryHosts[0].Scheme) suite.Assert().Equal("harbor", registryHosts[0].Host) suite.Assert().Equal("/v2/registry.ghcr.io", registryHosts[0].Path) cfg = &mockConfig{ mirrors: map[string]*cri.RegistryMirrorConfig{ "docker.io": { MirrorEndpoints: []cri.RegistryEndpointConfig{{EndpointEndpoint: "https://some.host:123"}}, MirrorSkipFallback: true, }, }, auths: map[string]*cri.RegistryAuthConfig{ "some.host:123": { RegistryUsername: "root", RegistryPassword: "secret", }, }, tlses: map[string]*cri.RegistryTLSConfig{ "some.host:123": { TLSCA: []byte(caCertMock), }, }, } registryHosts, err = image.RegistryHosts(cfg)("docker.io") suite.Require().NoError(err) suite.Assert().Len(registryHosts, 1) suite.Assert().Equal("https", registryHosts[0].Scheme) suite.Assert().Equal("some.host:123", registryHosts[0].Host) suite.Assert().Equal("/v2", registryHosts[0].Path) tlsClientConfig := registryHosts[0].Client.Transport.(*http.Transport).TLSClientConfig suite.Require().NotNil(tlsClientConfig) suite.Require().NotNil(tlsClientConfig.RootCAs) suite.Require().Empty(tlsClientConfig.Certificates) suite.Require().NotNil(registryHosts[0].Authorizer) req, err := http.NewRequest(http.MethodGet, "htts://some.host:123/v2", nil) //nolint:noctx suite.Require().NoError(err) resp := &http.Response{} resp.Request = req resp.Header = http.Header{} resp.Header.Add("Www-Authenticate", "Basic realm=\"Access to the staging site\", charset=\"UTF-8\"") suite.Require().NoError(registryHosts[0].Authorizer.AddResponses(context.Background(), []*http.Response{resp})) suite.Require().NoError(registryHosts[0].Authorizer.Authorize(context.Background(), req)) suite.Assert().Equal("Basic cm9vdDpzZWNyZXQ=", req.Header.Get("Authorization")) } func (suite *ResolverSuite) TestResolveTag() { resolver := image.NewResolver(&mockConfig{}) name, desc, err := resolver.Resolve(suite.T().Context(), "ghcr.io/siderolabs/talos:v1.12.0") suite.Require().NoError(err) suite.Assert().Equal("ghcr.io/siderolabs/talos:v1.12.0", name) suite.Assert().Equal(digest.Digest("sha256:342707af87028596549bb68882654f90122cbbaf29cb2a537dfbc8b1b7770898"), desc.Digest) } func TestResolverSuite(t *testing.T) { t.Parallel() suite.Run(t, new(ResolverSuite)) } const caCertMock = `-----BEGIN CERTIFICATE----- MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI 7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl -----END CERTIFICATE----- ` ================================================ FILE: internal/pkg/containers/image/verify/internal/cosign/cosign.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cosign provides cosign-based image signature verification via Talos pull process. package cosign import ( "context" "encoding/hex" "encoding/json" "errors" "fmt" "io" "strings" "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/errdefs" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sigstore/cosign/v3/pkg/cosign" "github.com/sigstore/cosign/v3/pkg/cosign/bundle" "github.com/sigstore/cosign/v3/pkg/oci" "github.com/sigstore/cosign/v3/pkg/oci/static" sgbundle "github.com/sigstore/sigstore-go/pkg/bundle" "github.com/sigstore/sigstore-go/pkg/verify" "go.uber.org/zap" ) const ( maxManifestSize = 128 * 1024 maxBundleSize = 1024 * 1024 maxPayloadSize = 1024 * 1024 maxLayers = 100 sigstoreBundleMediaTypePrefix = "application/vnd.dev.sigstore.bundle" sigstoreBundleV03ArtifactType = "application/vnd.dev.sigstore.bundle.v0.3+json" ) // VerifyResult represents the result of a signature verification attempt. type VerifyResult struct { Message string } // VerifyImage verifies the given image reference and digest against the provided verification configuration. // // It checks in priority order: // 1. OCI referrers API for new-style sigstore bundles (sha256:xxx referrers). // 2. The bundle tag (sha256-xxx, no .sig suffix) for new-style sigstore bundles stored as a tag. // 3. The legacy .sig tag (sha256-xxx.sig) for legacy cosign signature layers. // // All registry I/O goes through the provided resolver, which handles authentication and mirror routing. // // The verifiers are in opts, if any of the verifiers returns true for bundle verification, the image is considered verified. func VerifyImage(ctx context.Context, logger *zap.Logger, resolver remotes.Resolver, imageRef name.Digest, co cosign.CheckOpts) (*VerifyResult, error) { logger = logger.With(zap.Stringer("image", imageRef)) imageDigest, err := v1.NewHash(imageRef.DigestStr()) if err != nil { return nil, fmt.Errorf("failed to parse image digest: %w", err) } digestBytes, err := hex.DecodeString(imageDigest.Hex) if err != nil { return nil, fmt.Errorf("failed to decode digest hex: %w", err) } artifactPolicyOption := verify.WithArtifactDigest(imageDigest.Algorithm, digestBytes) // Step 1: try OCI referrers for new-style sigstore bundles. fetcher, err := resolver.Fetcher(ctx, imageRef.Name()) if err != nil { return nil, fmt.Errorf("failed to get fetcher: %w", err) } if refFetcher, ok := fetcher.(remotes.ReferrersFetcher); ok { referrers, err := refFetcher.FetchReferrers(ctx, digest.Digest(imageRef.DigestStr()), remotes.WithReferrerArtifactTypes(sigstoreBundleV03ArtifactType)) if err == nil { bundleRefs := filterBundleReferrers(referrers) if len(bundleRefs) > 0 { logger.Debug("verifying via OCI referrer bundles", zap.Int("count", len(bundleRefs))) return verifyBundleReferrers(ctx, logger, fetcher, bundleRefs, artifactPolicyOption, co) } } } // Step 2: try bundle tag (sha256-xxx) for new-style sigstore bundles stored as a tag. logger.Debug("no bundle referrers found, trying bundle tag") verified, result, bundleErr := verifyFromBundleTag(ctx, logger, resolver, imageRef, artifactPolicyOption, co) if verified { return result, bundleErr } // Step 3: fall back to the legacy .sig tag (sha256-xxx.sig). logger.Debug("no bundle tag found, falling back to legacy .sig tag") result, legacyErr := verifyFromLegacySigTag(ctx, logger, resolver, imageRef, imageDigest, co) if legacyErr != nil { return nil, fmt.Errorf("no valid signature found: %w", errors.Join(bundleErr, legacyErr)) } return result, nil } // filterBundleReferrers returns only referrers whose ArtifactType indicates a sigstore bundle. func filterBundleReferrers(referrers []ocispec.Descriptor) []ocispec.Descriptor { var out []ocispec.Descriptor for _, r := range referrers { if strings.HasPrefix(r.ArtifactType, sigstoreBundleMediaTypePrefix) { out = append(out, r) } } return out } // verifyBundleReferrers verifies a set of OCI referrer descriptors as new-style sigstore bundles. // // Each referrer points to an OCI image manifest whose single layer contains the sigstore bundle JSON. func verifyBundleReferrers( ctx context.Context, logger *zap.Logger, fetcher remotes.Fetcher, referrers []ocispec.Descriptor, artifactPolicyOption verify.ArtifactPolicyOption, co cosign.CheckOpts, ) (*VerifyResult, error) { var lastErr error for _, referrer := range referrers { b, err := fetchBundleFromReferrer(ctx, fetcher, referrer) if err != nil { logger.Debug("failed to fetch bundle referrer", zap.String("digest", referrer.Digest.String()), zap.Error(err)) lastErr = err continue } co.NewBundleFormat = true if _, err := cosign.VerifyNewBundle(ctx, &co, artifactPolicyOption, b); err != nil { logger.Debug("bundle referrer verification failed", zap.String("digest", referrer.Digest.String()), zap.Error(err)) lastErr = err continue } logger.Debug("bundle referrer verified", zap.String("digest", referrer.Digest.String())) return &VerifyResult{ Message: fmt.Sprintf("verified via bundle referrer with digest %s", referrer.Digest.String()), }, nil } if lastErr != nil { return nil, fmt.Errorf("no valid bundle referrer: %w", lastErr) } return nil, errors.New("no valid bundle referrers") } // verifyFromBundleTag fetches the bundle tag (sha256-xxx, no .sig suffix) and verifies via // new-style sigstore bundle layers. // // Returns (true, nil) if a bundle was successfully verified, (true, err) if bundle layers were // found but verification failed, and (false, nil) if the tag was not found or had no bundle layers. // //nolint:gocyclo func verifyFromBundleTag( ctx context.Context, logger *zap.Logger, resolver remotes.Resolver, imageRef name.Digest, artifactPolicyOption verify.ArtifactPolicyOption, co cosign.CheckOpts, ) (bool, *VerifyResult, error) { bundleTag := strings.ReplaceAll(imageRef.DigestStr(), ":", "-") logger.Debug("resolving bundle tag", zap.String("bundleTag", bundleTag)) resolvedName, desc, err := resolver.Resolve(ctx, imageRef.Repository.Name()+":"+bundleTag) if err != nil { logger.Debug("bundle tag not found", zap.String("bundleTag", bundleTag), zap.Error(err)) if errdefs.IsNotFound(err) { return false, nil, errors.New("bundle tag not found") } return false, nil, fmt.Errorf("failed to resolve bundle tag %s: %w", bundleTag, err) } logger.Debug("resolved bundle tag", zap.String("name", resolvedName), zap.String("media_type", desc.MediaType)) fetcher, err := resolver.Fetcher(ctx, resolvedName) if err != nil { return false, nil, fmt.Errorf("failed to get fetcher for bundle tag: %w", err) } var bundleLayers []ocispec.Descriptor switch desc.MediaType { case ocispec.MediaTypeImageManifest: manifest, err := fetchManifest(ctx, fetcher, desc) if err != nil { return false, nil, fmt.Errorf("failed to fetch bundle manifest: %w", err) } for _, layer := range manifest.Layers[:min(maxLayers, len(manifest.Layers))] { if strings.HasPrefix(layer.MediaType, sigstoreBundleMediaTypePrefix) { bundleLayers = append(bundleLayers, layer) } } case ocispec.MediaTypeImageIndex: // The bundle tag may be an OCI image index wrapping individual bundle manifests. // Walk each manifest entry and collect bundle layers from all of them. index, err := fetchIndex(ctx, fetcher, desc) if err != nil { return false, nil, fmt.Errorf("failed to fetch bundle index: %w", err) } for _, manifestDesc := range index.Manifests[:min(maxLayers, len(index.Manifests))] { if manifestDesc.MediaType != ocispec.MediaTypeImageManifest { continue } manifest, err := fetchManifest(ctx, fetcher, manifestDesc) if err != nil { logger.Debug("failed to fetch manifest from bundle index", zap.String("digest", manifestDesc.Digest.String()), zap.Error(err)) continue } for _, layer := range manifest.Layers[:min(maxLayers, len(manifest.Layers))] { if strings.HasPrefix(layer.MediaType, sigstoreBundleMediaTypePrefix) { bundleLayers = append(bundleLayers, layer) } } } default: logger.Debug("unexpected media type for bundle tag, skipping", zap.String("media_type", desc.MediaType)) return false, nil, nil } if len(bundleLayers) == 0 { logger.Debug("no bundle layers found in bundle tag") return false, nil, nil } result, err := verifyBundleLayers(ctx, logger, fetcher, bundleLayers, artifactPolicyOption, co) return true, result, err } // verifyFromLegacySigTag fetches the legacy .sig tag (sha256-xxx.sig) and verifies via legacy // cosign signature layers. func verifyFromLegacySigTag(ctx context.Context, logger *zap.Logger, resolver remotes.Resolver, imageRef name.Digest, imageDigest v1.Hash, co cosign.CheckOpts) (*VerifyResult, error) { signatureTag := strings.ReplaceAll(imageRef.DigestStr(), ":", "-") + ".sig" logger.Debug("resolving .sig tag", zap.String("signatureTag", signatureTag)) resolvedName, desc, err := resolver.Resolve(ctx, imageRef.Repository.Name()+":"+signatureTag) if err != nil { if errdefs.IsNotFound(err) { return nil, fmt.Errorf("legacy signature tag not found") } return nil, fmt.Errorf("failed to resolve .sig tag %s: %w", signatureTag, err) } logger.Debug("resolved .sig manifest", zap.String("name", resolvedName), zap.String("media_type", desc.MediaType)) if desc.MediaType != ocispec.MediaTypeImageManifest { return nil, fmt.Errorf("unexpected media type for .sig manifest: %s", desc.MediaType) } fetcher, err := resolver.Fetcher(ctx, resolvedName) if err != nil { return nil, fmt.Errorf("failed to get fetcher for .sig manifest: %w", err) } manifest, err := fetchManifest(ctx, fetcher, desc) if err != nil { return nil, fmt.Errorf("failed to fetch .sig manifest: %w", err) } legacyLayers := manifest.Layers[:min(maxLayers, len(manifest.Layers))] return verifyLegacyLayers(ctx, logger, fetcher, legacyLayers, imageDigest, co) } // verifyBundleLayers verifies layers from a .sig manifest that carry sigstore bundle JSON directly. func verifyBundleLayers( ctx context.Context, logger *zap.Logger, fetcher remotes.Fetcher, layers []ocispec.Descriptor, artifactPolicyOption verify.ArtifactPolicyOption, co cosign.CheckOpts, ) (*VerifyResult, error) { var lastErr error for i, layer := range layers { b, err := fetchBundleFromLayer(ctx, fetcher, layer) if err != nil { logger.Debug("failed to fetch bundle layer", zap.Int("layer", i), zap.Error(err)) lastErr = err continue } co.NewBundleFormat = true if _, err := cosign.VerifyNewBundle(ctx, &co, artifactPolicyOption, b); err != nil { logger.Debug("bundle layer verification failed", zap.Int("layer", i), zap.Error(err)) lastErr = err continue } logger.Debug("bundle layer verified", zap.Int("layer", i)) return &VerifyResult{ Message: "verified via bundle", }, nil } if lastErr != nil { return nil, fmt.Errorf("no valid bundle layer: %w", lastErr) } return nil, errors.New("no valid bundle layers") } // verifyLegacyLayers verifies layers from a .sig manifest as legacy cosign signatures. func verifyLegacyLayers(ctx context.Context, logger *zap.Logger, fetcher remotes.Fetcher, layers []ocispec.Descriptor, imageDigest v1.Hash, co cosign.CheckOpts) (*VerifyResult, error) { var lastErr error for i, layer := range layers { sig, err := buildLegacySignature(ctx, fetcher, layer) if err != nil { logger.Debug("failed to build legacy signature from layer", zap.Int("layer", i), zap.Error(err)) lastErr = err continue } co.NewBundleFormat = false bundleVerified, err := cosign.VerifyImageSignature(ctx, sig, imageDigest, &co) if err != nil { logger.Debug("legacy signature verification failed", zap.Int("layer", i), zap.Error(err)) lastErr = err continue } logger.Debug("legacy signature verified", zap.Int("layer", i), zap.Bool("bundle_verified", bundleVerified)) return &VerifyResult{ Message: fmt.Sprintf("verified via legacy signature (bundle verified %v)", bundleVerified), }, nil } if lastErr != nil { return nil, fmt.Errorf("no valid legacy signature: %w", lastErr) } return nil, errors.New("no legacy signatures found") } // fetchManifest fetches and decodes an OCI image manifest descriptor. // //nolint:dupl // not a duplicate! func fetchManifest(ctx context.Context, fetcher remotes.Fetcher, desc ocispec.Descriptor) (ocispec.Manifest, error) { rc, err := fetcher.Fetch(ctx, desc) if err != nil { return ocispec.Manifest{}, fmt.Errorf("failed to fetch: %w", err) } defer rc.Close() //nolint:errcheck var manifest ocispec.Manifest if err := json.NewDecoder(io.LimitReader(rc, maxManifestSize)).Decode(&manifest); err != nil { return ocispec.Manifest{}, fmt.Errorf("failed to decode manifest: %w", err) } return manifest, nil } // fetchIndex fetches and decodes an OCI image index descriptor. // //nolint:dupl // not a duplicate! func fetchIndex(ctx context.Context, fetcher remotes.Fetcher, desc ocispec.Descriptor) (ocispec.Index, error) { rc, err := fetcher.Fetch(ctx, desc) if err != nil { return ocispec.Index{}, fmt.Errorf("failed to fetch: %w", err) } defer rc.Close() //nolint:errcheck var index ocispec.Index if err := json.NewDecoder(io.LimitReader(rc, maxManifestSize)).Decode(&index); err != nil { return ocispec.Index{}, fmt.Errorf("failed to decode index: %w", err) } return index, nil } // fetchBundleFromReferrer fetches a sigstore bundle stored as an OCI referrer. // // The referrer descriptor points to an OCI image manifest whose single layer contains the // sigstore bundle JSON (media type application/vnd.dev.sigstore.bundle.v0.3+json). func fetchBundleFromReferrer(ctx context.Context, fetcher remotes.Fetcher, referrer ocispec.Descriptor) (*sgbundle.Bundle, error) { manifest, err := fetchManifest(ctx, fetcher, referrer) if err != nil { return nil, fmt.Errorf("failed to fetch bundle manifest: %w", err) } if len(manifest.Layers) != 1 { return nil, fmt.Errorf("expected exactly one layer in bundle manifest, got %d", len(manifest.Layers)) } return fetchBundleFromLayer(ctx, fetcher, manifest.Layers[0]) } // fetchBundleFromLayer fetches a sigstore bundle stored directly as a .sig manifest layer. // // The layer content is the sigstore bundle JSON. func fetchBundleFromLayer(ctx context.Context, fetcher remotes.Fetcher, layer ocispec.Descriptor) (*sgbundle.Bundle, error) { rc, err := fetcher.Fetch(ctx, layer) if err != nil { return nil, fmt.Errorf("failed to fetch bundle layer: %w", err) } defer rc.Close() //nolint:errcheck bundleBytes, err := io.ReadAll(io.LimitReader(rc, maxBundleSize)) if err != nil { return nil, fmt.Errorf("failed to read bundle layer: %w", err) } b := &sgbundle.Bundle{} if err := b.UnmarshalJSON(bundleBytes); err != nil { return nil, fmt.Errorf("failed to parse sigstore bundle: %w", err) } if !b.MinVersion("v0.3") { return nil, errors.New("bundle version too old (requires v0.3+)") } return b, nil } // buildLegacySignature constructs an oci.Signature from a cosign legacy signature layer. // // The layer content is the simple signing payload (the data that was signed). The cryptographic // signature and optional certificate/chain/Rekor bundle are read from the layer annotations. func buildLegacySignature(ctx context.Context, fetcher remotes.Fetcher, layer ocispec.Descriptor) (oci.Signature, error) { // Fetch the layer content: the simple signing payload (what was signed). rc, err := fetcher.Fetch(ctx, layer) if err != nil { return nil, fmt.Errorf("failed to fetch signature layer: %w", err) } defer rc.Close() //nolint:errcheck payload, err := io.ReadAll(io.LimitReader(rc, maxPayloadSize)) if err != nil { return nil, fmt.Errorf("failed to read signature layer: %w", err) } // Base64-encoded cryptographic signature from layer annotation. b64sig, ok := layer.Annotations[static.SignatureAnnotationKey] if !ok { return nil, errors.New("missing signature annotation") } var staticOpts []static.Option // Certificate and intermediate chain for keyless (Fulcio-issued) signing. if certPEM, ok := layer.Annotations[static.CertificateAnnotationKey]; ok { chainPEM := layer.Annotations[static.ChainAnnotationKey] staticOpts = append(staticOpts, static.WithCertChain([]byte(certPEM), []byte(chainPEM))) } // Rekor transparency log bundle, if present. if bundleJSON, ok := layer.Annotations[static.BundleAnnotationKey]; ok { var rb bundle.RekorBundle if err := json.Unmarshal([]byte(bundleJSON), &rb); err != nil { return nil, fmt.Errorf("failed to parse Rekor bundle annotation: %w", err) } staticOpts = append(staticOpts, static.WithBundle(&rb)) } return static.NewSignature(payload, b64sig, staticOpts...) } ================================================ FILE: internal/pkg/containers/image/verify/internal/cosign/cosign_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cosign_test import ( "testing" "github.com/google/go-containerregistry/pkg/name" "github.com/sigstore/cosign/v3/pkg/cosign" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/pkg/containers/image" ourcosign "github.com/siderolabs/talos/internal/pkg/containers/image/verify/internal/cosign" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) type mockRegistriesConfig struct{} func (mockRegistriesConfig) Mirrors() map[string]config.RegistryMirrorConfig { return nil } func (mockRegistriesConfig) Auths() map[string]config.RegistryAuthConfig { return nil } func (mockRegistriesConfig) TLSs() map[string]cri.RegistryTLSConfigExtended { return nil } func TestVerifyImage(t *testing.T) { t.Parallel() resolver := image.NewResolver(mockRegistriesConfig{}) trustedRoot, err := cosign.TrustedRoot() require.NoError(t, err) for _, test := range []struct { imageRef string checkOpts cosign.CheckOpts expectedResultMessage string expectedError string }{ { imageRef: "registry.k8s.io/etcd:v3.6.8@sha256:397189418d1a00e500c0605ad18d1baf3b541a1004d768448c367e48071622e5", checkOpts: cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Issuer: "https://accounts.google.com", Subject: "krel-trust@k8s-releng-prod.iam.gserviceaccount.com", }, }, }, expectedResultMessage: "verified via legacy signature (bundle verified true)", }, { imageRef: "ghcr.io/siderolabs/talos:v1.13.0-alpha.2@sha256:9361de6684b441da62298998ab89166efccca35772afc24fee3ae53c569ec44c", checkOpts: cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Issuer: "https://accounts.google.com", SubjectRegExp: "@siderolabs.com$", }, }, }, expectedResultMessage: "verified via bundle", }, { imageRef: "ghcr.io/siderolabs/extensions:v1.13.0-alpha.2@sha256:033cbce24e681208245797e53386b4bef1c4a995a32feebdfa05c5063f889334", checkOpts: cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Issuer: "https://accounts.google.com", Subject: "releasemgr-svc@talos-production.iam.gserviceaccount.com", }, }, }, expectedResultMessage: "verified via bundle", }, { imageRef: "ghcr.io/siderolabs/extensions:v1.13.0-alpha.1-17-gc538dab@sha256:32ed7bb3845215bfd71bf4284a2a5113ecd49ce45cde0324764fe84b378c8633", checkOpts: cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Issuer: "https://accounts.google.com", Subject: "releasemgr-svc@talos-production.iam.gserviceaccount.com", }, }, }, expectedError: "no valid signature found: bundle tag not found\nlegacy signature tag not found", }, { imageRef: "ghcr.io/siderolabs/extensions:v1.13.0-alpha.1@sha256:5c3abcee03ef7369bb92f1f3d76c1afd27ccc97fa2879145a486b554f3091648", checkOpts: cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Issuer: "https://some.entity", Subject: "releasemgr@world", }, }, }, expectedError: "no valid bundle layer: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN " + "value \"releasemgr@world\", got \"releasemgr-svc@talos-production.iam.gserviceaccount.com\"", }, } { t.Run(test.imageRef, func(t *testing.T) { t.Parallel() logger := zaptest.NewLogger(t) imageRef, err := name.NewDigest(test.imageRef) require.NoError(t, err) result, err := ourcosign.VerifyImage(t.Context(), logger, resolver, imageRef, test.checkOpts) if test.expectedError != "" { require.Error(t, err) assert.EqualError(t, err, test.expectedError) return } require.NoError(t, err) require.NotNil(t, result) assert.Equal(t, test.expectedResultMessage, result.Message) }) } } ================================================ FILE: internal/pkg/containers/image/verify/verify.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package verify provides functionality to verify container images against configured verification policies. package verify import ( "context" "crypto" "fmt" "time" "github.com/containerd/containerd/v2/core/remotes" "github.com/containerd/errdefs" "github.com/cosi-project/runtime/pkg/state" "github.com/google/go-containerregistry/pkg/name" "github.com/sigstore/cosign/v3/pkg/cosign" "github.com/sigstore/sigstore-go/pkg/root" "github.com/sigstore/sigstore/pkg/cryptoutils" sigsig "github.com/sigstore/sigstore/pkg/signature" "go.uber.org/zap" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ourcosign "github.com/siderolabs/talos/internal/pkg/containers/image/verify/internal/cosign" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) // ImageSignature verifies image signature within Talos source code. // //nolint:gocyclo func ImageSignature( ctx context.Context, logger *zap.Logger, resources state.State, resolver remotes.Resolver, imageRef string, ) (*machine.ImageServiceVerifyResponse, error) { logger = logger.With(zap.String("image_ref", imageRef)) inRef, err := name.ParseReference(imageRef) if err != nil { return nil, status.Errorf(codes.InvalidArgument, "image reference is invalid: %s", err) } ruleMatcher, err := security.ImageVerificationRuleMatcher(ctx, resources) if err != nil { return nil, status.Errorf(codes.Internal, "failed to create image verification rule matcher: %s", err) } logger.Debug("finding matching image verification rule for image reference", zap.Stringer("image_ref_context", inRef.Context())) matchedRule := ruleMatcher(inRef.Context().String()) if matchedRule == nil { logger.Info("no matched image verification rule, allowing by default", zap.Stringer("image_ref_context", inRef.Context())) return &machine.ImageServiceVerifyResponse{ Verified: false, Message: "no matched rule", }, nil } if matchedRule.TypedSpec().Deny { logger.Info("verification denied by matched rule", zap.String("rule_id", matchedRule.Metadata().ID())) return nil, status.Errorf(codes.PermissionDenied, "verification denied by matched rule (%s)", matchedRule.Metadata().ID()) } if matchedRule.TypedSpec().Skip { logger.Info("verification skipped by matched rule", zap.String("rule_id", matchedRule.Metadata().ID())) return &machine.ImageServiceVerifyResponse{ Verified: false, Message: fmt.Sprintf("verification skipped by matched rule (%s)", matchedRule.Metadata().ID()), }, nil } // resolve the image reference to a digest reference if needed var ( digestRef name.Digest ok bool ) if digestRef, ok = inRef.(name.Digest); !ok { _, desc, err := resolver.Resolve(ctx, inRef.String()) if err != nil { if errdefs.IsNotFound(err) { logger.Info("image reference not found during resolution", zap.Error(err)) return nil, status.Errorf(codes.NotFound, "image reference not found during resolution: %s", err) } return nil, status.Errorf(codes.Internal, "failed to resolve image reference: %s", err) } digestRef, err = name.NewDigest(inRef.Context().Name() + "@" + desc.Digest.String()) if err != nil { return nil, status.Errorf(codes.Internal, "failed to construct digest reference: %s", err) } } // convert the rule to cosign check opts checkOpts, err := cosignCheckOptsFromRule(ctx, matchedRule.TypedSpec(), resources) if err != nil { return nil, status.Errorf(codes.Internal, "failed to convert verification rule to cosign check options: %s", err) } result, err := ourcosign.VerifyImage(ctx, logger, resolver, digestRef, checkOpts) if err != nil { return nil, status.Errorf(codes.PermissionDenied, "image verification failed: %s", err) } return &machine.ImageServiceVerifyResponse{ Verified: true, Message: result.Message, DigestedImageRef: digestRef.String(), }, nil } // tufTimeout is the timeout for fetching TUF trusted root metadata during keyless verification. const tufTimeout = 15 * time.Second func getTrustedRoot(ctx context.Context, resources state.State) (root.TrustedMaterial, error) { ctx, cancel := context.WithTimeout(ctx, tufTimeout) defer cancel() r, err := resources.WatchFor(ctx, security.NewTUFTrustedRoot(security.TrustedRootID).Metadata(), state.WithEventTypes(state.Created, state.Updated), ) if err != nil { return nil, fmt.Errorf("failed to watch for TUF trusted root: %w", err) } tufData, ok := r.(*security.TUFTrustedRoot) if !ok { return nil, fmt.Errorf("unexpected resource type for TUF trusted root: %T", r) } return root.NewTrustedRootFromJSON([]byte(tufData.TypedSpec().JSONData)) } func cosignCheckOptsFromRule(ctx context.Context, rule *security.ImageVerificationRuleSpec, resources state.State) (cosign.CheckOpts, error) { switch { case rule.KeylessVerifier != nil: trustedRoot, err := getTrustedRoot(ctx, resources) if err != nil { return cosign.CheckOpts{}, fmt.Errorf("failed to get trusted root: %w", err) } return cosign.CheckOpts{ TrustedMaterial: trustedRoot, Identities: []cosign.Identity{ { Subject: rule.KeylessVerifier.Subject, SubjectRegExp: rule.KeylessVerifier.SubjectRegex, Issuer: rule.KeylessVerifier.Issuer, }, }, }, nil case rule.PublicKeyVerifier != nil: pub, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(rule.PublicKeyVerifier.Certificate)) if err != nil { return cosign.CheckOpts{}, fmt.Errorf("failed to unmarshal public key: %w", err) } verifier, err := sigsig.LoadVerifier(pub, crypto.SHA256) if err != nil { return cosign.CheckOpts{}, fmt.Errorf("failed to load public key: %w", err) } return cosign.CheckOpts{ Offline: true, SigVerifier: verifier, }, nil default: return cosign.CheckOpts{}, fmt.Errorf("unsupported verifier type in rule") } } ================================================ FILE: internal/pkg/containers/inspector.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containers import "syscall" // Inspector gather information about pods & containers. type Inspector interface { // Pods collects information about running pods & containers. Pods() ([]*Pod, error) // Container returns info about a single container. Container(id string) (*Container, error) // Close frees associated resources. Close() error // Returns path to the container's stderr pipe GetProcessStderr(ID string) (string, error) // Kill sends signal to container's process Kill(ID string, isPodSandbox bool, signal syscall.Signal) error } ================================================ FILE: internal/pkg/containers/pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package containers // Pod presents information about a pod, including a list of containers. type Pod struct { Name string Sandbox string Containers []*Container } ================================================ FILE: internal/pkg/cri/client.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "fmt" "runtime/pprof" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/siderolabs/talos/pkg/machinery/client/dialer" ) var newClientPprof = pprof.NewProfile("internal/pkg/cri.NewClient") // Client is a lightweight implementation of CRI client. type Client struct { conn *grpc.ClientConn runtimeClient runtimeapi.RuntimeServiceClient imagesClient runtimeapi.ImageServiceClient } // maxMsgSize use 16MB as the default message size limit. // grpc library default is 4MB. const maxMsgSize = 1024 * 1024 * 16 // NewClient builds CRI client. func NewClient(endpoint string, _ time.Duration) (*Client, error) { conn, err := grpc.NewClient(endpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)), grpc.WithContextDialer(dialer.DialUnix()), grpc.WithSharedWriteBuffer(true), ) if err != nil { return nil, fmt.Errorf("error connecting to CRI: %w", err) } res := &Client{ conn: conn, runtimeClient: runtimeapi.NewRuntimeServiceClient(conn), imagesClient: runtimeapi.NewImageServiceClient(conn), } newClientPprof.Add(res, 1) return res, nil } // Close connection. func (c *Client) Close() error { newClientPprof.Remove(c) return c.conn.Close() } ================================================ FILE: internal/pkg/cri/containers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" ) // CreateContainer creates a new container in the specified PodSandbox. func (c *Client) CreateContainer(ctx context.Context, podSandBoxID string, config *runtimeapi.ContainerConfig, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) { resp, err := c.runtimeClient.CreateContainer(ctx, &runtimeapi.CreateContainerRequest{ PodSandboxId: podSandBoxID, Config: config, SandboxConfig: sandboxConfig, }) if err != nil { return "", fmt.Errorf("CreateContainer in sandbox %q from runtime service failed: %w", podSandBoxID, err) } if resp.ContainerId == "" { return "", fmt.Errorf("ContainerId is not set for container %q", config.GetMetadata()) } return resp.ContainerId, nil } // StartContainer starts the container. func (c *Client) StartContainer(ctx context.Context, containerID string) error { _, err := c.runtimeClient.StartContainer(ctx, &runtimeapi.StartContainerRequest{ ContainerId: containerID, }) if err != nil { return fmt.Errorf("StartContainer %q from runtime service failed: %w", containerID, err) } return nil } // StopContainer stops a running container with a grace period (i.e., timeout). func (c *Client) StopContainer(ctx context.Context, containerID string, timeout int64) error { _, err := c.runtimeClient.StopContainer(ctx, &runtimeapi.StopContainerRequest{ ContainerId: containerID, Timeout: timeout, }) if err != nil { return fmt.Errorf("StopContainer %q from runtime service failed: %w", containerID, err) } return nil } // RemoveContainer removes the container. If the container is running, the container // should be forced to removal. func (c *Client) RemoveContainer(ctx context.Context, containerID string) error { _, err := c.runtimeClient.RemoveContainer(ctx, &runtimeapi.RemoveContainerRequest{ ContainerId: containerID, }) if err != nil { return fmt.Errorf("RemoveContainer %q from runtime service failed: %w", containerID, err) } return nil } // ListContainers lists containers by filters. func (c *Client) ListContainers(ctx context.Context, filter *runtimeapi.ContainerFilter) ([]*runtimeapi.Container, error) { resp, err := c.runtimeClient.ListContainers(ctx, &runtimeapi.ListContainersRequest{ Filter: filter, }) if err != nil { return nil, fmt.Errorf("ListContainers with filter %+v from runtime service failed: %w", filter, err) } return resp.Containers, nil } // ContainerStatus returns the container status. func (c *Client) ContainerStatus(ctx context.Context, containerID string, verbose bool) (*runtimeapi.ContainerStatus, map[string]string, error) { resp, err := c.runtimeClient.ContainerStatus(ctx, &runtimeapi.ContainerStatusRequest{ ContainerId: containerID, Verbose: verbose, }) if err != nil { return nil, nil, fmt.Errorf("ContainerStatus %q from runtime service failed: %w", containerID, err) } return resp.Status, resp.Info, nil } // ContainerStats returns the stats of the container. func (c *Client) ContainerStats(ctx context.Context, containerID string) (*runtimeapi.ContainerStats, error) { resp, err := c.runtimeClient.ContainerStats(ctx, &runtimeapi.ContainerStatsRequest{ ContainerId: containerID, }) if err != nil { return nil, fmt.Errorf("ContainerStatus %q from runtime service failed: %w", containerID, err) } return resp.GetStats(), nil } // ListContainerStats returns stats for all the containers matching the filter. func (c *Client) ListContainerStats(ctx context.Context, filter *runtimeapi.ContainerStatsFilter) ([]*runtimeapi.ContainerStats, error) { resp, err := c.runtimeClient.ListContainerStats(ctx, &runtimeapi.ListContainerStatsRequest{ Filter: filter, }) if err != nil { return nil, fmt.Errorf("ListContainerStats with filter %+v from runtime service failed: %w", filter, err) } return resp.GetStats(), nil } ================================================ FILE: internal/pkg/cri/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cri provides minimal CRI client. package cri // This client is based on k8s version in https://github.com/kubernetes/kubernetes/tree/master/pkg/kubelet/remote, // but it doesn't depend on k8s libs. ================================================ FILE: internal/pkg/cri/cri_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "context" "os" "path/filepath" "sync" "testing" "time" "github.com/containerd/cgroups/v3" "github.com/containerd/cgroups/v3/cgroup1" "github.com/containerd/cgroups/v3/cgroup2" "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/suite" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/logging" "github.com/siderolabs/talos/internal/app/machined/pkg/system/events" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner" "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/process" "github.com/siderolabs/talos/internal/pkg/cri" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( busyboxImage = "docker.io/library/busybox:1.30.1" ) func MockEventSink(t *testing.T) func(state events.ServiceState, message string, args ...any) { return func(state events.ServiceState, message string, args ...any) { t.Logf(message, args...) } } type CRISuite struct { suite.Suite tmpDir string containerdRunner runner.Runner containerdWg sync.WaitGroup containerdAddress string client *cri.Client ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc } func (suite *CRISuite) SetupSuite() { if cgroups.Mode() == cgroups.Unified { suite.T().Skip("test doesn't pass under cgroupsv2") } var err error suite.tmpDir = suite.T().TempDir() stateDir, rootDir := filepath.Join(suite.tmpDir, "state"), filepath.Join(suite.tmpDir, "root") suite.Require().NoError(os.Mkdir(stateDir, 0o777)) suite.Require().NoError(os.Mkdir(rootDir, 0o777)) if cgroups.Mode() == cgroups.Unified { var ( groupPath string manager *cgroup2.Manager ) groupPath, err = cgroup2.NestedGroupPath(suite.tmpDir) suite.Require().NoError(err) manager, err = cgroup2.NewManager(constants.CgroupMountPath, groupPath, &cgroup2.Resources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } else { var manager cgroup1.Cgroup manager, err = cgroup1.New(cgroup1.NestedPath(suite.tmpDir), &specs.LinuxResources{}) suite.Require().NoError(err) defer manager.Delete() //nolint:errcheck } suite.containerdAddress = filepath.Join(suite.tmpDir, "run.sock") args := &runner.Args{ ID: "containerd", ProcessArgs: []string{ "/bin/containerd", "--address", suite.containerdAddress, "--state", stateDir, "--root", rootDir, "--config", constants.CRIContainerdConfig, }, } suite.containerdRunner = process.NewRunner( false, args, runner.WithLoggingManager(logging.NewFileLoggingManager(suite.tmpDir)), runner.WithEnv([]string{constants.EnvPathWithBin}), runner.WithCgroupPath(suite.tmpDir), ) suite.Require().NoError(suite.containerdRunner.Open()) suite.containerdWg.Go(func() { defer suite.containerdRunner.Close() //nolint:errcheck suite.containerdRunner.Run(MockEventSink(suite.T())) //nolint:errcheck }) suite.client, err = cri.NewClient("unix:"+suite.containerdAddress, 30*time.Second) suite.Require().NoError(err) } func (suite *CRISuite) TearDownSuite() { suite.ctxCancel() suite.Require().NoError(suite.client.Close()) suite.Require().NoError(suite.containerdRunner.Stop()) suite.containerdWg.Wait() } func (suite *CRISuite) SetupTest() { suite.ctx, suite.ctxCancel = context.WithTimeout(context.Background(), 30*time.Second) } func (suite *CRISuite) TearDownTest() { suite.ctxCancel() } func (suite *CRISuite) TestRunSandboxContainer() { podSandboxConfig := &runtimeapi.PodSandboxConfig{ Metadata: &runtimeapi.PodSandboxMetadata{ Name: "etcd-master-1", Uid: "ed1a599a53090941c9b4025c7e3e883d", Namespace: "kube-system", Attempt: 0, }, Labels: map[string]string{ "io.kubernetes.pod.name": "etcd-master-1", "io.kubernetes.pod.namespace": "kube-system", }, LogDirectory: suite.tmpDir, Linux: &runtimeapi.LinuxPodSandboxConfig{ SecurityContext: &runtimeapi.LinuxSandboxSecurityContext{ NamespaceOptions: &runtimeapi.NamespaceOption{ Network: runtimeapi.NamespaceMode_NODE, }, }, }, } podSandboxID, err := suite.client.RunPodSandbox(suite.ctx, podSandboxConfig, "") suite.Require().NoError(err) suite.Require().Len(podSandboxID, 64) imageRef, err := suite.client.PullImage( suite.ctx, &runtimeapi.ImageSpec{ Image: busyboxImage, }, podSandboxConfig, ) suite.Require().NoError(err) _, err = suite.client.ImageStatus( suite.ctx, &runtimeapi.ImageSpec{ Image: imageRef, }, ) suite.Require().NoError(err) ctrID, err := suite.client.CreateContainer( suite.ctx, podSandboxID, &runtimeapi.ContainerConfig{ Metadata: &runtimeapi.ContainerMetadata{ Name: "etcd", }, Labels: map[string]string{ "io.kubernetes.container.name": "etcd", "io.kubernetes.pod.name": "etcd-master-1", "io.kubernetes.pod.namespace": "kube-system", }, Annotations: map[string]string{ "io.kubernetes.container.restartCount": "1", }, Image: &runtimeapi.ImageSpec{ Image: imageRef, }, Command: []string{"/bin/sh", "-c", "sleep 3600"}, }, podSandboxConfig, ) suite.Require().NoError(err) suite.Require().Len(ctrID, 64) err = suite.client.StartContainer(suite.ctx, ctrID) suite.Require().NoError(err) _, err = suite.client.ContainerStats(suite.ctx, ctrID) suite.Require().NoError(err) _, _, err = suite.client.ContainerStatus(suite.ctx, ctrID, true) suite.Require().NoError(err) err = suite.client.StopContainer(suite.ctx, ctrID, 10) suite.Require().NoError(err) err = suite.client.RemoveContainer(suite.ctx, ctrID) suite.Require().NoError(err) err = suite.client.StopPodSandbox(suite.ctx, podSandboxID) suite.Require().NoError(err) err = suite.client.RemovePodSandbox(suite.ctx, podSandboxID) suite.Require().NoError(err) } func (suite *CRISuite) TestList() { pods, err := suite.client.ListPodSandbox(suite.ctx, &runtimeapi.PodSandboxFilter{}) suite.Require().NoError(err) suite.Require().Len(pods, 0) containers, err := suite.client.ListContainers(suite.ctx, &runtimeapi.ContainerFilter{}) suite.Require().NoError(err) suite.Require().Len(containers, 0) containerStats, err := suite.client.ListContainerStats(suite.ctx, &runtimeapi.ContainerStatsFilter{}) suite.Require().NoError(err) suite.Require().Len(containerStats, 0) _, err = suite.client.ListImages(suite.ctx, &runtimeapi.ImageFilter{}) suite.Require().NoError(err) } func TestCRISuite(t *testing.T) { if os.Getuid() != 0 { t.Skip("can't run the test as non-root") } _, err := os.Stat("/bin/containerd") if err != nil { t.Skip("containerd binary is not available, skipping the test") } suite.Run(t, new(CRISuite)) } ================================================ FILE: internal/pkg/cri/images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" ) // PullImage pulls container image. func (c *Client) PullImage(ctx context.Context, image *runtimeapi.ImageSpec, sandboxConfig *runtimeapi.PodSandboxConfig) (string, error) { resp, err := c.imagesClient.PullImage(ctx, &runtimeapi.PullImageRequest{ Image: image, SandboxConfig: sandboxConfig, }) if err != nil { return "", fmt.Errorf("error pulling image %s: %w", image, err) } return resp.ImageRef, nil } // ListImages lists available images. func (c *Client) ListImages(ctx context.Context, filter *runtimeapi.ImageFilter) ([]*runtimeapi.Image, error) { resp, err := c.imagesClient.ListImages(ctx, &runtimeapi.ListImagesRequest{ Filter: filter, }) if err != nil { return nil, fmt.Errorf("error listing images: %w", err) } return resp.Images, nil } // ImageStatus returns the status of the image. func (c *Client) ImageStatus(ctx context.Context, image *runtimeapi.ImageSpec) (*runtimeapi.Image, error) { resp, err := c.imagesClient.ImageStatus(ctx, &runtimeapi.ImageStatusRequest{ Image: image, }) if err != nil { return nil, fmt.Errorf("ImageStatus %q from image service failed: %w", image.Image, err) } if resp.Image != nil { if resp.Image.Id == "" || resp.Image.Size == 0 { return nil, fmt.Errorf("id or size of image %q is not set", image.Image) } } return resp.Image, nil } ================================================ FILE: internal/pkg/cri/pods.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "context" "fmt" "log" "slices" "time" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1" talosclient "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/constants" ) // RunPodSandbox creates and starts a pod-level sandbox. Runtimes should ensure // the sandbox is in ready state. func (c *Client) RunPodSandbox(ctx context.Context, config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) { resp, err := c.runtimeClient.RunPodSandbox(ctx, &runtimeapi.RunPodSandboxRequest{ Config: config, RuntimeHandler: runtimeHandler, }) if err != nil { return "", err } if resp.PodSandboxId == "" { return "", fmt.Errorf("PodSandboxId is not set for sandbox %q", config.GetMetadata()) } return resp.PodSandboxId, nil } // StopPodSandbox stops the sandbox. If there are any running containers in the // sandbox, they should be forced to termination. func (c *Client) StopPodSandbox(ctx context.Context, podSandBoxID string) error { _, err := c.runtimeClient.StopPodSandbox(ctx, &runtimeapi.StopPodSandboxRequest{ PodSandboxId: podSandBoxID, }) if err != nil { return fmt.Errorf("StopPodSandbox %q from runtime service failed: %w", podSandBoxID, err) } return nil } // RemovePodSandbox removes the sandbox. If there are any containers in the // sandbox, they should be forcibly removed. func (c *Client) RemovePodSandbox(ctx context.Context, podSandBoxID string) error { _, err := c.runtimeClient.RemovePodSandbox(ctx, &runtimeapi.RemovePodSandboxRequest{ PodSandboxId: podSandBoxID, }) if err != nil { return fmt.Errorf("RemovePodSandbox %q from runtime service failed: %w", podSandBoxID, err) } return nil } // ListPodSandbox returns a list of PodSandboxes. func (c *Client) ListPodSandbox(ctx context.Context, filter *runtimeapi.PodSandboxFilter) ([]*runtimeapi.PodSandbox, error) { resp, err := c.runtimeClient.ListPodSandbox(ctx, &runtimeapi.ListPodSandboxRequest{ Filter: filter, }) if err != nil { return nil, fmt.Errorf("ListPodSandbox with filter %+v from runtime service failed: %w", filter, err) } return resp.Items, nil } // PodSandboxStatus returns the status of the PodSandbox. func (c *Client) PodSandboxStatus(ctx context.Context, podSandBoxID string) (*runtimeapi.PodSandboxStatus, map[string]string, error) { resp, err := c.runtimeClient.PodSandboxStatus(ctx, &runtimeapi.PodSandboxStatusRequest{ PodSandboxId: podSandBoxID, Verbose: true, }) if err != nil { return nil, nil, err } return resp.Status, resp.Info, nil } // StopAction for StopAndRemovePodSandboxes. type StopAction int // Stop actions. const ( StopOnly StopAction = iota StopAndRemove ) // StopAndRemovePodSandboxes stops and removes all pods with the specified network mode. If no // network mode is specified, all pods will be removed. func (c *Client) StopAndRemovePodSandboxes(ctx context.Context, stopAction StopAction, modes ...runtimeapi.NamespaceMode) (err error) { pods, err := c.ListPodSandbox(ctx, nil) if err != nil { return err } var g errgroup.Group for _, pod := range pods { g.Go(func() error { status, _, e := c.PodSandboxStatus(ctx, pod.GetId()) if e != nil { if talosclient.StatusCode(e) == codes.NotFound { return nil } return e } networkMode := status.GetLinux().GetNamespaces().GetOptions().GetNetwork() // If any modes are specified, we verify that the current pod is // running any one of the modes. If it doesn't, we skip it. if len(modes) > 0 && !contains(networkMode, modes) { return nil } if e = stopAndRemove(ctx, stopAction, c, pod, networkMode.String()); e != nil { return fmt.Errorf("failed stopping pod %s/%s: %w", pod.Metadata.Namespace, pod.Metadata.Name, e) } return nil }) } return g.Wait() } func contains(mode runtimeapi.NamespaceMode, modes []runtimeapi.NamespaceMode) bool { return slices.Contains(modes, mode) } //nolint:gocyclo func stopAndRemove(ctx context.Context, stopAction StopAction, client *Client, pod *runtimeapi.PodSandbox, mode string) (err error) { action := "stopping" status := "stopped" if stopAction == StopAndRemove { action = "removing" status = "removed" } log.Printf("%s pod %s/%s with network mode %q", action, pod.Metadata.Namespace, pod.Metadata.Name, mode) filter := &runtimeapi.ContainerFilter{ PodSandboxId: pod.Id, } containers, err := client.ListContainers(ctx, filter) if err != nil { if talosclient.StatusCode(err) == codes.NotFound { return nil } return err } var g errgroup.Group for _, container := range containers { g.Go(func() error { // TODO(andrewrynhard): Can we set the timeout dynamically? if container.State == runtimeapi.ContainerState_CONTAINER_RUNNING || container.State == runtimeapi.ContainerState_CONTAINER_UNKNOWN { log.Printf("stopping container %s/%s:%s", pod.Metadata.Namespace, pod.Metadata.Name, container.Metadata.Name) if criErr := client.StopContainer(ctx, container.Id, int64(constants.KubeletShutdownGracePeriod.Seconds())); criErr != nil { if talosclient.StatusCode(criErr) == codes.NotFound { return nil } return criErr } } if stopAction == StopAndRemove { log.Printf("removing container %s/%s:%s", pod.Metadata.Namespace, pod.Metadata.Name, container.Metadata.Name) if removeErr := retry.Constant(constants.KubeletShutdownGracePeriod, retry.WithUnits(time.Second), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error { if criErr := client.RemoveContainer(ctx, container.Id); criErr != nil { if talosclient.StatusCode(criErr) == codes.NotFound { return nil } return retry.ExpectedError(criErr) } return nil }); removeErr != nil { return removeErr } } log.Printf("%s container %s/%s:%s", status, pod.Metadata.Namespace, pod.Metadata.Name, container.Metadata.Name) return nil }) } if err = g.Wait(); err != nil { return err } if pod.State == runtimeapi.PodSandboxState_SANDBOX_READY { if err = client.StopPodSandbox(ctx, pod.Id); err != nil { if talosclient.StatusCode(err) == codes.NotFound { return nil } log.Printf("error stopping pod %s/%s, ignored: %s", pod.Metadata.Namespace, pod.Metadata.Name, err) return nil } } if stopAction == StopAndRemove { if err = client.RemovePodSandbox(ctx, pod.Id); err != nil { if talosclient.StatusCode(err) == codes.NotFound { return nil } log.Printf("error removing pod %s/%s, ignored: %s", pod.Metadata.Namespace, pod.Metadata.Name, err) return nil } } log.Printf("%s pod %s/%s", status, pod.Metadata.Namespace, pod.Metadata.Name) return nil } ================================================ FILE: internal/pkg/ctxutil/ctxutil.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ctxutil provides utilities for working with contexts. package ctxutil import "context" // Cause returns the cause of the context error, or nil if there is no error or the error is a usual context error. func Cause(ctx context.Context) error { if c := context.Cause(ctx); c != ctx.Err() { return c } return nil } ================================================ FILE: internal/pkg/dashboard/apidata/apidata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package apidata implements the types and the data sources for the data sourced from various Talos APIs. package apidata ================================================ FILE: internal/pkg/dashboard/apidata/data.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package apidata implements types to handle monitoring data, calculate values from it, etc. package apidata import ( "time" ) const maxPoints = 1000 // Data represents the monitoring data retrieved via Talos API. // // Data structure is sent over the channel each interval. type Data struct { // Data per each node. Nodes map[string]*Node Timestamp time.Time Interval time.Duration } // CalculateDiff with data from previous iteration. func (data *Data) CalculateDiff(oldData *Data) { data.Interval = data.Timestamp.Sub(oldData.Timestamp) for node, nodeData := range data.Nodes { oldNodeData := oldData.Nodes[node] if oldNodeData == nil { continue } nodeData.UpdateDiff(oldNodeData) nodeData.UpdateSeries(oldNodeData) } } ================================================ FILE: internal/pkg/dashboard/apidata/diff.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package apidata import "github.com/siderolabs/talos/pkg/machinery/api/machine" func cpuInfoDiff(old, next *machine.CPUStat) *machine.CPUStat { if old == nil || next == nil { return &machine.CPUStat{} } // TODO: support wraparound return &machine.CPUStat{ User: next.User - old.User, Nice: next.Nice - old.Nice, System: next.System - old.System, Idle: next.Idle - old.Idle, Iowait: next.Iowait - old.Iowait, Irq: next.Irq - old.Irq, SoftIrq: next.SoftIrq - old.SoftIrq, Steal: next.Steal - old.Steal, Guest: next.Guest - old.Guest, GuestNice: next.GuestNice - old.GuestNice, } } func netDevDiff(old, next *machine.NetDev) *machine.NetDev { if old == nil || next == nil { return &machine.NetDev{} } // TODO: support wraparound return &machine.NetDev{ Name: next.Name, RxBytes: next.RxBytes - old.RxBytes, RxPackets: next.RxPackets - old.RxPackets, RxErrors: next.RxErrors - old.RxErrors, RxDropped: next.RxDropped - old.RxDropped, RxFifo: next.RxFifo - old.RxFifo, RxFrame: next.RxFrame - old.RxFrame, RxCompressed: next.RxCompressed - old.RxCompressed, RxMulticast: next.RxMulticast - old.RxMulticast, TxBytes: next.TxBytes - old.TxBytes, TxPackets: next.TxPackets - old.TxPackets, TxErrors: next.TxErrors - old.TxErrors, TxDropped: next.TxDropped - old.TxDropped, TxFifo: next.TxFifo - old.TxFifo, TxCollisions: next.TxCollisions - old.TxCollisions, TxCarrier: next.TxCarrier - old.TxCarrier, TxCompressed: next.TxCompressed - old.TxCompressed, } } func diskStatDiff(old, next *machine.DiskStat) *machine.DiskStat { if old == nil || next == nil { return &machine.DiskStat{} } // TODO: support wraparound return &machine.DiskStat{ Name: next.Name, ReadCompleted: next.ReadCompleted - old.ReadCompleted, ReadMerged: next.ReadMerged - old.ReadMerged, ReadSectors: next.ReadSectors - old.ReadSectors, WriteCompleted: next.WriteCompleted - old.WriteCompleted, WriteMerged: next.WriteMerged - old.WriteMerged, WriteSectors: next.WriteSectors - old.WriteSectors, DiscardCompleted: next.DiscardCompleted - old.DiscardCompleted, DiscardMerged: next.DiscardMerged - old.DiscardMerged, DiscardSectors: next.DiscardSectors - old.DiscardSectors, } } func procDiff(old, next *machine.ProcessInfo) *machine.ProcessInfo { if old == nil || next == nil { return &machine.ProcessInfo{} } // TODO: support wraparound return &machine.ProcessInfo{ CpuTime: next.CpuTime - old.CpuTime, } } ================================================ FILE: internal/pkg/dashboard/apidata/node.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package apidata import ( "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) // Node represents data gathered from a single node. type Node struct { // These fields are directly API responses. LoadAvg *machine.LoadAvg Version *machine.Version Memory *machine.Memory SystemStat *machine.SystemStat CPUsFreqStats *machine.CPUsFreqStats CPUsInfo *machine.CPUsInfo NetDevStats *machine.NetworkDeviceStats DiskStats *machine.DiskStats Processes *machine.Process ServiceList *machine.ServiceList // These fields are calculated as diff with Node data from previous pol. SystemStatDiff *machine.SystemStat NetDevStatsDiff *machine.NetworkDeviceStats DiskStatsDiff *machine.DiskStats ProcsDiff map[int32]*machine.ProcessInfo // Time-series data. Series map[string][]float64 } // MemUsage as used/total. func (node *Node) MemUsage() float64 { memTotal := node.Memory.GetMeminfo().GetMemtotal() memUsed := node.Memory.GetMeminfo().GetMemtotal() - node.Memory.GetMeminfo().GetMemfree() - node.Memory.GetMeminfo().GetCached() - node.Memory.GetMeminfo().GetBuffers() if memTotal == 0 { return 0 } return float64(memUsed) / float64(memTotal) } // CPUUsageByName returns CPU usage by name. // //nolint:gocyclo func (node *Node) CPUUsageByName(name string) float64 { if node.SystemStatDiff == nil || node.SystemStatDiff.CpuTotal == nil { return 0 } stat := node.SystemStatDiff.CpuTotal idle := stat.Idle + stat.Iowait nonIdle := stat.User + stat.Nice + stat.System + stat.Irq + stat.Steal + stat.SoftIrq total := idle + nonIdle if total == 0 { return 0 } switch name { case "user": return stat.User / total case "system": return stat.System / total case "idle": return stat.Idle / total case "iowait": return stat.Iowait / total case "nice": return stat.Nice / total case "irq": return stat.Irq / total case "steal": return stat.Steal / total case "softirq": return stat.SoftIrq / total case "usage": return (total - idle) / total case "total": return total case "total_weighted": cpuCount := len(node.CPUsInfo.GetCpuInfo()) if cpuCount == 0 { return total } return total / float64(cpuCount) } panic("unknown cpu usage name") } // CtxSwitches returns diff of context switches. func (node *Node) CtxSwitches() uint64 { if node.SystemStatDiff == nil { return 0 } return node.SystemStatDiff.GetContextSwitches() } // ProcsCreated returns diff of processes created. func (node *Node) ProcsCreated() uint64 { if node.SystemStatDiff == nil { return 0 } return node.SystemStatDiff.GetProcessCreated() } // UpdateSeries builds time-series data based on previous iteration data. func (node *Node) UpdateSeries(old *Node) { node.Series = make(map[string][]float64) for _, graphInfo := range []struct { name string f func() float64 }{ { "mem", node.MemUsage, }, { "user", func() float64 { return node.CPUUsageByName("user") }, }, { "system", func() float64 { return node.CPUUsageByName("system") }, }, { "loadavg", func() float64 { return node.LoadAvg.GetLoad1() }, }, { "netrxbytes", func() float64 { return float64(node.NetDevStatsDiff.GetTotal().GetRxBytes()) }, }, { "nettxbytes", func() float64 { return float64(node.NetDevStatsDiff.GetTotal().GetTxBytes()) }, }, { "diskrdsectors", func() float64 { return float64(node.DiskStatsDiff.GetTotal().GetReadSectors()) }, }, { "diskwrsectors", func() float64 { return float64(node.DiskStatsDiff.GetTotal().GetWriteSectors()) }, }, } { oldSeries := old.Series[graphInfo.name] off := 0 if len(oldSeries) > maxPoints { off = len(oldSeries) - maxPoints } node.Series[graphInfo.name] = append(oldSeries[off:], graphInfo.f()) // TODO: bug with plot widget for len(node.Series[graphInfo.name]) < 2 { node.Series[graphInfo.name] = append([]float64{0.0}, node.Series[graphInfo.name]...) } } } // UpdateDiff calculates diff with node data from previous iteration. func (node *Node) UpdateDiff(old *Node) { if old.SystemStat != nil { node.SystemStatDiff = &machine.SystemStat{ // TODO: support other fields CpuTotal: cpuInfoDiff(old.SystemStat.GetCpuTotal(), node.SystemStat.GetCpuTotal()), ContextSwitches: node.SystemStat.GetContextSwitches() - old.SystemStat.GetContextSwitches(), ProcessCreated: node.SystemStat.GetProcessCreated() - old.SystemStat.GetProcessCreated(), } } if old.NetDevStats != nil { node.NetDevStatsDiff = &machine.NetworkDeviceStats{ // TODO: support other fields Total: netDevDiff(old.NetDevStats.GetTotal(), node.NetDevStats.GetTotal()), } } if old.DiskStats != nil { node.DiskStatsDiff = &machine.DiskStats{ // TODO: support other fields Total: diskStatDiff(old.DiskStats.GetTotal(), node.DiskStats.GetTotal()), } } if old.Processes != nil { index := xslices.ToMap(old.Processes.GetProcesses(), func(proc *machine.ProcessInfo) (int32, *machine.ProcessInfo) { return proc.Pid, proc }) node.ProcsDiff = xslices.ToMap(node.Processes.GetProcesses(), func(proc *machine.ProcessInfo) (int32, *machine.ProcessInfo) { return proc.Pid, procDiff(index[proc.Pid], proc) }) } } ================================================ FILE: internal/pkg/dashboard/apidata/source.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package apidata import ( "context" "sync" "time" "golang.org/x/sync/errgroup" "google.golang.org/protobuf/types/known/emptypb" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/pkg/dashboard/resolver" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) // Source is a data source that gathers information about a Talos node using Talos API. type Source struct { *client.Client Resolver resolver.Resolver Interval time.Duration ctx context.Context //nolint:containedctx ctxCancel context.CancelFunc wg sync.WaitGroup } // Run the data poll on interval. func (source *Source) Run(ctx context.Context) <-chan *Data { dataCh := make(chan *Data) source.ctx, source.ctxCancel = context.WithCancel(ctx) source.wg.Add(1) go source.run(dataCh) return dataCh } // Stop the data collection process. func (source *Source) Stop() { source.ctxCancel() source.wg.Wait() } func (source *Source) run(dataCh chan<- *Data) { defer source.wg.Done() defer close(dataCh) ticker := time.NewTicker(source.Interval) defer ticker.Stop() var oldData, currentData *Data for { currentData = source.gather() if oldData == nil { currentData.CalculateDiff(currentData) } else { currentData.CalculateDiff(oldData) } select { case dataCh <- currentData: case <-source.ctx.Done(): return } select { case <-source.ctx.Done(): return case <-ticker.C: } oldData = currentData } } type protoMsg[T any] interface { GetMessages() []T } func unpack[T helpers.Message](source *Source, nodes map[string]*Node, resultLock *sync.Mutex, resp protoMsg[T], setter func(node *Node, value T)) { resultLock.Lock() defer resultLock.Unlock() for _, msg := range resp.GetMessages() { node := source.node(msg) if _, ok := nodes[node]; !ok { nodes[node] = &Node{} } if msg.GetMetadata().GetError() != "" { continue } setter(nodes[node], msg) } } //nolint:gocyclo func (source *Source) gather() *Data { result := &Data{ Timestamp: time.Now(), Nodes: map[string]*Node{}, } var resultLock sync.Mutex gatherFuncs := []func() error{ func() error { resp, err := source.MachineClient.LoadAvg(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.LoadAvg) { node.LoadAvg = value }) return nil }, func() error { resp, err := source.MachineClient.Version(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.Version) { node.Version = value }) return nil }, func() error { resp, err := source.MachineClient.Memory(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.Memory) { node.Memory = value }) return nil }, func() error { resp, err := source.MachineClient.SystemStat(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.SystemStat) { node.SystemStat = value }) return nil }, func() error { resp, err := source.MachineClient.CPUFreqStats(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.CPUsFreqStats) { node.CPUsFreqStats = value }) return nil }, func() error { resp, err := source.MachineClient.CPUInfo(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.CPUsInfo) { node.CPUsInfo = value }) return nil }, func() error { resp, err := source.MachineClient.NetworkDeviceStats(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.NetworkDeviceStats) { node.NetDevStats = value }) return nil }, func() error { resp, err := source.MachineClient.DiskStats(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.DiskStats) { node.DiskStats = value }) return nil }, func() error { resp, err := source.MachineClient.Processes(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.Process) { node.Processes = value }) return nil }, func() error { resp, err := source.MachineClient.ServiceList(source.ctx, &emptypb.Empty{}) if err != nil { return err } unpack(source, result.Nodes, &resultLock, resp, func(node *Node, value *machine.ServiceList) { node.ServiceList = value }) return nil }, } var eg errgroup.Group for _, f := range gatherFuncs { eg.Go(f) } if err := eg.Wait(); err != nil { // TODO: handle error _ = err } return result } func (source *Source) node(msg helpers.Message) string { hostname := msg.GetMetadata().GetHostname() return source.Resolver.Resolve(hostname) } ================================================ FILE: internal/pkg/dashboard/components/components.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package components implements specific widgets for the dashboard. package components import ( "fmt" "strings" "github.com/siderolabs/gen/xslices" ) const ( noData = "..." notAvailable = "n/a" none = "" maxLogLines = 1000 ) // field represents a field in a widget consist of a name and a value, rendered next to each other. type field struct { Name string Value string } func (f *field) render(nameWidth int) string { return fmt.Sprintf("[::b]%s[::-] %s", padRight(f.Name, nameWidth), f.Value) } type fieldGroup struct { fields []field } // String implements the Stringer interface. func (fg *fieldGroup) String() string { width := fg.maxFieldNameLength() return strings.Join( xslices.Map(fg.fields, func(t field) string { return t.render(width) }), "\n", ) } func (fg *fieldGroup) maxFieldNameLength() int { result := 0 for _, f := range fg.fields { result = max(result, len(f.Name)) } return result } // padRight pads a string to the specified width by appending spaces to the end. func padRight(s string, width int) string { return fmt.Sprintf("%-*s", width, s) } func toHealthStatus(healthy bool) string { if healthy { return formatStatus("Healthy") } return formatStatus("Unhealthy") } func formatStatus(status any) string { statusStr := capitalizeFirst(fmt.Sprintf("%v", status)) switch strings.ToLower(statusStr) { case "running", "healthy", "true": return formatText(statusStr, true) case "stopped", "unhealthy", "false": return formatText(statusStr, false) default: return statusStr } } func formatText(text string, ok bool) string { if text == "" { return "" } if ok { return fmt.Sprintf("[green]√ %s[-]", text) } return fmt.Sprintf("[red]× %s[-]", text) } // capitalizeFirst capitalizes the first character of string. func capitalizeFirst(s string) string { if s == "" { return s } return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:]) } ================================================ FILE: internal/pkg/dashboard/components/diagnostics.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "slices" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Diagnostics represents the diagnostics widget. type Diagnostics struct { tview.Grid hline *HorizontalLine info *tview.TextView selectedNode string perNodeWarnings map[string][]*runtime.Diagnostic } // NewDiagnostics initializes Diagnostics. func NewDiagnostics() *Diagnostics { widget := &Diagnostics{ Grid: *tview.NewGrid(), info: tview.NewTextView(), hline: NewHorizontalLine("Diagnostics"), perNodeWarnings: make(map[string][]*runtime.Diagnostic), } widget.info. SetDynamicColors(true). SetBorderPadding(0, 0, 1, 1) widget.SetRows(1, 0).SetColumns(0) widget.AddItem(widget.hline, 0, 0, 1, 1, 0, 0, false) widget.AddItem(widget.info, 1, 0, 1, 1, 0, 0, false) return widget } // GetCurrentHeight returns the height of the widget. func (widget *Diagnostics) GetCurrentHeight() int { numWarnings := len(widget.perNodeWarnings[widget.selectedNode]) if numWarnings == 0 { return 0 } return 1 + numWarnings } // OnNodeSelect implements the NodeSelectListener interface. func (widget *Diagnostics) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *Diagnostics) OnResourceDataChange(data resourcedata.Data) { r, ok := data.Resource.(*runtime.Diagnostic) if !ok { return } idx := slices.IndexFunc(widget.perNodeWarnings[data.Node], func(warning *runtime.Diagnostic) bool { return warning.Metadata().ID() == r.Metadata().ID() }) if data.Deleted { if idx != -1 { widget.perNodeWarnings[data.Node] = slices.Delete(widget.perNodeWarnings[data.Node], idx, idx+1) } } else { if idx == -1 { widget.perNodeWarnings[data.Node] = append(widget.perNodeWarnings[data.Node], r) } else { widget.perNodeWarnings[data.Node][idx] = r } } if data.Node == widget.selectedNode { widget.redraw() } } // WriteLog writes the log line to the widget. func (widget *Diagnostics) redraw() { widget.info.SetWrap(true) widget.info.Clear() for _, warning := range widget.perNodeWarnings[widget.selectedNode] { fmt.Fprintf(widget.info, "■ (%s) [red]%s[-]\n", //nolint:errcheck tview.Escape(warning.TypedSpec().DocumentationURL(warning.Metadata().ID())), tview.Escape(warning.TypedSpec().Message)) } } ================================================ FILE: internal/pkg/dashboard/components/footer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "slices" "strings" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "github.com/siderolabs/gen/maps" ) // hitRegion represents a clickable region in the footer. type hitRegion struct { startX int endX int node string // non-empty if this is a node region screen string // non-empty if this is a screen region } // Footer represents the top bar with host info. type Footer struct { tview.TextView selectedNode string nodes []string screenKeyToName map[string]string selectedScreen string paused bool hitRegions []hitRegion // NodeClick is called when a node label is clicked. May be nil. NodeClick func(node string) // ScreenClick is called when a screen label is clicked. May be nil. ScreenClick func(screen string) } // NewFooter initializes Footer. // //nolint:gocyclo func NewFooter(screenKeyToName map[string]string, nodes []string) *Footer { var initialScreen string for _, name := range screenKeyToName { initialScreen = name break } widget := &Footer{ TextView: *tview.NewTextView(), screenKeyToName: screenKeyToName, selectedScreen: initialScreen, nodes: nodes, } widget.SetDynamicColors(true) // set the background to be a horizontal line widget.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { for i := x; i < x+width; i++ { for j := y; j < y+height; j++ { screen.SetContent( i, j, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite), ) } } return x, y, width, height }) widget.SetMouseCapture(func(action tview.MouseAction, event *tcell.EventMouse) (tview.MouseAction, *tcell.EventMouse) { if action != tview.MouseLeftClick { return action, event } mx, _ := event.Position() wx, _, _, _ := widget.GetRect() clickX := mx - wx for _, region := range widget.hitRegions { if clickX >= region.startX && clickX < region.endX { if region.node != "" && widget.NodeClick != nil { widget.NodeClick(region.node) } else if region.screen != "" && widget.ScreenClick != nil { widget.ScreenClick(region.screen) } break } } return action, event }) widget.refresh() return widget } // OnNodeSelect implements the NodeSelectListener interface. func (widget *Footer) OnNodeSelect(node string) { widget.selectedNode = node widget.refresh() } // SelectScreen refreshes the footer with the tabs and screens data. func (widget *Footer) SelectScreen(screen string) { widget.selectedScreen = screen widget.refresh() } // SetPaused refreshes the footer with the new paused state. func (widget *Footer) SetPaused(paused bool) { widget.paused = paused widget.refresh() } // refresh rebuilds the footer text and updates hit regions for mouse click detection. // // The footer format is: [node1 | node2 | node3] --- [F1: Screen1] --- [Screen2] --- ... // where the selected node/screen is highlighted in red. // Hit regions track the x-offset ranges of each node and screen label. func (widget *Footer) refresh() { var sb strings.Builder x := 0 widget.hitRegions = widget.hitRegions[:0] // write appends s to the text and advances x by the visible character width. write := func(s string, visibleWidth int) { sb.WriteString(s) x += visibleWidth } // Opening bracket wrapping the nodes section. write("[", 1) for i, node := range widget.nodes { if i > 0 { write(" | ", 3) } displayName := node if displayName == "" { displayName = "(local)" } startX := x nameLen := len([]rune(displayName)) if node == widget.selectedNode { write(fmt.Sprintf("[red]%s[-]", displayName), nameLen) } else { write(displayName, nameLen) } widget.hitRegions = append(widget.hitRegions, hitRegion{ startX: startX, endX: x, node: node, }) } // Closing bracket and separator between nodes and screens. write("] --- ", 6) screenKeys := maps.Keys(widget.screenKeyToName) slices.Sort(screenKeys) for i, screenKey := range screenKeys { if i > 0 { write(" --- ", 5) } screenName := widget.screenKeyToName[screenKey] startX := x if screenName == widget.selectedScreen { // [[red]ScreenName[-]] renders as [ScreenName] (the [[ is an escaped [). write(fmt.Sprintf("[[red]%s[-]]", screenName), len([]rune(screenName))+2) } else { // [F1: ScreenName] is not a tview color tag and renders literally. write(fmt.Sprintf("[%s: %s]", screenKey, screenName), len(screenKey)+len([]rune(screenName))+4) } widget.hitRegions = append(widget.hitRegions, hitRegion{ startX: startX, endX: x, screen: screenName, }) } if widget.paused { // [[yellow]PAUSED[-]] renders as [PAUSED] — no click region needed. write(" --- ", 5) write("[[yellow]PAUSED[-]]", 8) } widget.SetText(sb.String()) } ================================================ FILE: internal/pkg/dashboard/components/gauges.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "math" "github.com/gdamore/tcell/v2" "github.com/navidys/tvxwidgets" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" ) // SystemGauges quickly show CPU/mem load. type SystemGauges struct { tview.Primitive cpuGauge *tvxwidgets.PercentageModeGauge memGauge *tvxwidgets.PercentageModeGauge } // NewSystemGauges creates SystemGauges. func NewSystemGauges() *SystemGauges { root := tview.NewGrid().SetRows(0).SetColumns(0) root.SetBorderPadding(1, 2, 1, 1) cpuGauge := tvxwidgets.NewPercentageModeGauge() cpuGauge.SetBorder(false) cpuGauge.SetMaxValue(100) cpuGauge.SetPgBgColor(tview.Styles.ContrastBackgroundColor) cpuFrame := tview.NewFrame(cpuGauge) cpuFrame.SetBorders(0, 0, 0, 0, 0, 0). AddText("[::b]CPU", true, tview.AlignLeft, tcell.ColorDefault) root.AddItem(cpuFrame, 0, 0, 1, 1, 0, 0, false) memGauge := tvxwidgets.NewPercentageModeGauge() memGauge.SetBorder(false) memGauge.SetMaxValue(100) memGauge.SetPgBgColor(tview.Styles.ContrastBackgroundColor) memFrame := tview.NewFrame(memGauge) memFrame.SetBorders(0, 0, 0, 0, 0, 0). AddText("[::b]MEM", true, tview.AlignLeft, tcell.ColorDefault) root.AddItem(memFrame, 1, 0, 1, 1, 0, 0, false) widget := &SystemGauges{ Primitive: root, cpuGauge: cpuGauge, memGauge: memGauge, } return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *SystemGauges) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { widget.cpuGauge.SetValue(0) widget.memGauge.SetValue(0) } else { memUsed := nodeData.MemUsage() widget.memGauge.SetValue(int(math.Round(memUsed * 100.0))) cpuUsed := nodeData.CPUUsageByName("usage") widget.cpuGauge.SetValue(int(math.Round(cpuUsed * 100.0))) } } ================================================ FILE: internal/pkg/dashboard/components/graphs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "slices" "github.com/gdamore/tcell/v2" "github.com/navidys/tvxwidgets" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" ) // BaseGraph represents the widget with some usage graph. type BaseGraph struct { tview.Primitive plot *tvxwidgets.Plot labels []string } // NewBaseGraph initializes BaseGraph. func NewBaseGraph(title string, labels []string) *BaseGraph { widget := &BaseGraph{ plot: tvxwidgets.NewPlot(), labels: labels, } root := tview.NewFrame(widget.plot). SetBorders(0, 0, 0, 0, 0, 0). AddText(title, true, tview.AlignCenter, tcell.ColorDefault) widget.plot.SetBorder(false) widget.plot.SetLineColor([]tcell.Color{ tcell.ColorRed, tcell.ColorGreen, }) widget.plot.SetTitle(title) widget.plot.SetDrawAxes(false) widget.plot.SetMarker(tvxwidgets.PlotMarkerBraille) widget.Primitive = root return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *BaseGraph) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { plotData := make([][]float64, len(widget.labels)) for i := range widget.labels { plotData[i] = []float64{0} } widget.plot.SetData(plotData) return } _, _, width, _ := widget.plot.GetPlotRect() //nolint:dogsled plotData := make([][]float64, len(widget.labels)) for i, name := range widget.labels { series := nodeData.Series[name] maxPoints := min(width, len(series)) plotData[i] = slices.Clone(series[len(series)-maxPoints:]) } widget.plot.SetData(plotData) } // NewCPUGraph creates CPU usage graph. func NewCPUGraph() *BaseGraph { return NewBaseGraph("[::b]CPU USER/SYSTEM", []string{"user", "system"}) } // NewMemGraph creates mem usage graph. func NewMemGraph() *BaseGraph { return NewBaseGraph("[::b]MEM USED", []string{"mem"}) } // NewLoadAvgGraph creates loadavg graph. func NewLoadAvgGraph() *BaseGraph { return NewBaseGraph("[::b]LOAD AVG 60sec", []string{"loadavg"}) } ================================================ FILE: internal/pkg/dashboard/components/header.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "math" "sort" "strconv" "time" "github.com/dustin/go-humanize" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const noHostname = "(no hostname)" type headerData struct { hostname string version string uptime string cpuFreq string totalMem string numProcesses string cpuUsagePercent string memUsagePercent string } // Header represents the top bar with host info. type Header struct { tview.TextView selectedNode string nodeMap map[string]*headerData spinnerPos int } // NewHeader initializes Header. func NewHeader() *Header { header := &Header{ TextView: *tview.NewTextView(), nodeMap: make(map[string]*headerData), } header.SetDynamicColors(true).SetText(noData) return header } // OnNodeSelect implements the NodeSelectListener interface. func (widget *Header) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *Header) OnResourceDataChange(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) switch res := data.Resource.(type) { //nolint:gocritic case *network.HostnameStatus: if data.Deleted { nodeData.hostname = noHostname } else { nodeData.hostname = res.TypedSpec().Hostname } } if data.Node == widget.selectedNode { widget.redraw() } } // OnAPIDataChange implements the APIDataListener interface. func (widget *Header) OnAPIDataChange(node string, data *apidata.Data) { for node, nodeData := range data.Nodes { widget.updateNodeAPIData(node, nodeData) } if node == widget.selectedNode { widget.redraw() } } // OnTick implements the TickerListener interface. func (widget *Header) OnTick() { widget.spinnerPos++ widget.redraw() } func (widget *Header) humanizeCPUFrequency(mhz float64) string { value := math.Round(mhz) unit := "MHz" if mhz >= 1000 { ghz := value / 1000 value = math.Round(ghz*100) / 100 unit = "GHz" } return fmt.Sprintf("%s%s", humanize.Ftoa(value), unit) } // formatUptime returns a duration in a human readable form. // // If d>24h returns format "23d72h3m5s", otherwise "72h3m5s". func formatUptime(d time.Duration) string { const day = 24 * time.Hour d = d.Round(time.Second) if d >= day { uptimeDays := d / day uptimeRest := d % day return fmt.Sprintf("%dd%s", uptimeDays, uptimeRest) } return d.String() } // Spinner is a set of characters compatible with Linux virtual console CP437. var spinner = []string{"\u2510", "\u2518", "\u2514", "\u250C"} func (widget *Header) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) spinnerPos := widget.spinnerPos % len(spinner) text := fmt.Sprintf( "[green]%s [yellow::b]%s[-:-:-] (%s): uptime %s, %s, %s RAM, PROCS %s, CPU %s, RAM %s", spinner[spinnerPos], data.hostname, data.version, data.uptime, data.cpuFreq, data.totalMem, data.numProcesses, data.cpuUsagePercent, data.memUsagePercent, ) widget.SetText(text) } //nolint:gocyclo func (widget *Header) updateNodeAPIData(node string, data *apidata.Node) { nodeData := widget.getOrCreateNodeData(node) if data == nil { return } nodeData.cpuUsagePercent = fmt.Sprintf("%.1f%%", data.CPUUsageByName("usage")*100.0) nodeData.memUsagePercent = fmt.Sprintf("%.1f%%", data.MemUsage()*100.0) if data.Version != nil { nodeData.version = data.Version.GetVersion().GetTag() } else { nodeData.version = notAvailable } if data.SystemStat != nil && data.SystemStat.BootTime != 0 { uptime := time.Since(time.Unix(int64(data.SystemStat.GetBootTime()), 0)) nodeData.uptime = formatUptime(uptime) } else { nodeData.uptime = notAvailable } if data.CPUsInfo != nil { numCPUs := len(data.CPUsInfo.GetCpuInfo()) if numCPUs > 0 { nodeData.cpuFreq = fmt.Sprintf("%dx%s", numCPUs, widget.humanizeCPUFrequency(data.CPUsInfo.GetCpuInfo()[0].GetCpuMhz())) } } else { nodeData.cpuFreq = notAvailable } if data.CPUsFreqStats != nil && data.CPUsFreqStats.CpuFreqStats != nil { numCPUs := len(data.CPUsFreqStats.CpuFreqStats) uniqMhz := make(map[uint64]int, numCPUs) for _, cpuFreqStat := range data.CPUsFreqStats.CpuFreqStats { uniqMhz[cpuFreqStat.CurrentFrequency]++ } keys := make([]uint64, 0, len(uniqMhz)) for mhz := range uniqMhz { if mhz == 0 { continue } keys = append(keys, mhz) } if len(keys) > 0 { sort.Slice(keys, func(i, j int) bool { return keys[i] > keys[j] }) nodeData.cpuFreq = "" } for i, mhz := range keys { if i > 0 { nodeData.cpuFreq += " " } nodeData.cpuFreq += fmt.Sprintf("%dx%s", uniqMhz[mhz], widget.humanizeCPUFrequency(float64(mhz)/1000.0)) } } else { nodeData.cpuFreq = notAvailable } if data.Processes != nil { nodeData.numProcesses = strconv.Itoa(len(data.Processes.GetProcesses())) } else { nodeData.numProcesses = notAvailable } if data.Memory != nil { nodeData.totalMem = humanize.IBytes(data.Memory.GetMeminfo().GetMemtotal() << 10) } else { nodeData.totalMem = notAvailable } } func (widget *Header) getOrCreateNodeData(node string) *headerData { data, ok := widget.nodeMap[node] if !ok { data = &headerData{ hostname: notAvailable, version: notAvailable, uptime: notAvailable, cpuFreq: notAvailable, totalMem: notAvailable, numProcesses: notAvailable, cpuUsagePercent: notAvailable, memUsagePercent: notAvailable, } widget.nodeMap[node] = data } return data } ================================================ FILE: internal/pkg/dashboard/components/horizontalline.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) // HorizontalLine is a widget that draws a horizontal line. type HorizontalLine struct { tview.TextView label []rune } // NewHorizontalLine initializes HorizontalLine. func NewHorizontalLine(label string) *HorizontalLine { widget := &HorizontalLine{ TextView: *tview.NewTextView(), label: []rune(" " + label + " "), } const leftGap = 2 // set the background to be a horizontal line widget.SetDrawFunc(func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { labelLength := len(widget.label) for i := x; i < x+width; i++ { for j := y; j < y+height; j++ { if j == y && i >= leftGap && i-leftGap < labelLength { screen.SetContent(i, j, widget.label[i-leftGap], nil, tcell.StyleDefault.Foreground(tcell.ColorYellow)) } else { screen.SetContent(i, j, tview.BoxDrawingsLightHorizontal, nil, tcell.StyleDefault.Foreground(tcell.ColorWhite)) } } } return x, y, width, height }) return widget } ================================================ FILE: internal/pkg/dashboard/components/info.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "github.com/dustin/go-humanize" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" ) // LoadAvgInfo represents the widget with load average info. type LoadAvgInfo struct { tview.TextView } // NewLoadAvgInfo initializes LoadAvgInfo. func NewLoadAvgInfo() *LoadAvgInfo { widget := &LoadAvgInfo{ TextView: *tview.NewTextView(), } widget.SetBorder(false) widget.SetBorderPadding(1, 0, 1, 0) widget.SetDynamicColors(true) return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *LoadAvgInfo) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { widget.SetText(noData) } else { widget.SetText(fmt.Sprintf( "[::b]LOAD[::-]\n"+ "1 min [::b]%6.2f[::-]\n"+ "5 min [::b]%6.2f[::-]\n"+ "15 min [::b]%6.2f[::-]", nodeData.LoadAvg.GetLoad1(), nodeData.LoadAvg.GetLoad5(), nodeData.LoadAvg.GetLoad15(), )) } } // ProcsInfo represents the widget with processes info. type ProcsInfo struct { tview.TextView } // NewProcsInfo initializes ProcsInfo. func NewProcsInfo() *ProcsInfo { widget := &ProcsInfo{ TextView: *tview.NewTextView(), } widget.SetBorder(false) widget.SetBorderPadding(1, 0, 1, 0) widget.SetDynamicColors(true) return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *ProcsInfo) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { widget.SetText(noData) } else { procsCreated, suffix := humanize.ComputeSI(float64(nodeData.ProcsCreated())) widget.SetText(fmt.Sprintf( "[::b]PROCS[::-]\n"+ "Created [::b]%5.1f%s[::-]\n"+ "Running [::b]%5d[::-]\n"+ "Blocked [::b]%5d[::-]", procsCreated, suffix, nodeData.SystemStat.GetProcessRunning(), nodeData.SystemStat.GetProcessBlocked(), )) } } // MemInfo represents the widget with memory info. type MemInfo struct { tview.TextView } // NewMemInfo initializes LoadAvgInfo. func NewMemInfo() *MemInfo { widget := &MemInfo{ TextView: *tview.NewTextView(), } widget.SetBorder(false) widget.SetBorderPadding(1, 0, 1, 0) widget.SetDynamicColors(true) return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *MemInfo) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { widget.SetText(noData) } else { widget.SetText(fmt.Sprintf( "[::b]MEMORY[::-]\n"+ "Total [::b]%8s[::-] Buffers [::b]%8s[::-]\n"+ "Used [::b]%8s[::-] Cache [::b]%8s[::-]\n"+ "Free [::b]%8s[::-] Avail [::b]%8s[::-]\n"+ "Shared [::b]%8s[::-] Swapped [::b]%8s[::-]\n", humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemtotal()<<10), humanize.Bytes(nodeData.Memory.GetMeminfo().GetBuffers()<<10), humanize.Bytes((nodeData.Memory.GetMeminfo().GetMemtotal()-nodeData.Memory.GetMeminfo().GetMemfree()-nodeData.Memory.GetMeminfo().GetCached()-nodeData.Memory.GetMeminfo().GetBuffers())<<10), humanize.Bytes(nodeData.Memory.GetMeminfo().GetCached()<<10), humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemfree()<<10), humanize.Bytes(nodeData.Memory.GetMeminfo().GetMemavailable()<<10), humanize.Bytes(nodeData.Memory.GetMeminfo().GetShmem()<<10), humanize.Bytes((nodeData.Memory.GetMeminfo().GetSwaptotal()-nodeData.Memory.GetMeminfo().GetSwapfree())<<10), )) } } // CPUInfo represents the widget with CPU info. type CPUInfo struct { tview.TextView } // NewCPUInfo initializes CPUInfo. func NewCPUInfo() *CPUInfo { widget := &CPUInfo{ TextView: *tview.NewTextView(), } widget.SetBorder(false) widget.SetBorderPadding(1, 0, 1, 0) widget.SetDynamicColors(true) return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *CPUInfo) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { widget.SetText(noData) } else { ctxSw, suffix := humanize.ComputeSI(float64(nodeData.CtxSwitches())) widget.SetText(fmt.Sprintf( "[::b]CPU[::-]\n"+ "User [::b]%5.1f%%[::-] Nice [::b]%5.1f%%[::-]\n"+ "System [::b]%5.1f%%[::-] IRQ [::b]%5.1f%%[::-]\n"+ "Idle [::b]%5.1f%%[::-] Iowait [::b]%5.1f%%[::-]\n"+ "Steal [::b]%5.1f%%[::-] CtxSw [::b]%5.1f%s[::-]\n", nodeData.CPUUsageByName("user")*100.0, nodeData.CPUUsageByName("nice")*100.0, nodeData.CPUUsageByName("system")*100.0, nodeData.CPUUsageByName("irq")*100.0, nodeData.CPUUsageByName("idle")*100.0, nodeData.CPUUsageByName("iowait")*100.0, nodeData.CPUUsageByName("steal")*100.0, ctxSw, suffix, )) } } ================================================ FILE: internal/pkg/dashboard/components/kubernetesinfo.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/rivo/tview" "github.com/siderolabs/gen/maps" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) type staticPodStatuses struct { apiServer string controllerManager string scheduler string } type kubernetesInfoData struct { isControlPlane bool typ string kubernetesVersion string kubeletStatus string podStatuses staticPodStatuses staticPodStatusMap map[resource.ID]*k8s.StaticPodStatus } // KubernetesInfo represents the kubernetes info widget. type KubernetesInfo struct { tview.TextView selectedNode string nodeMap map[string]*kubernetesInfoData } // NewKubernetesInfo initializes KubernetesInfo. func NewKubernetesInfo() *KubernetesInfo { kubernetes := &KubernetesInfo{ TextView: *tview.NewTextView(), nodeMap: make(map[string]*kubernetesInfoData), } kubernetes.SetDynamicColors(true). SetText(noData). SetBorderPadding(1, 0, 1, 0) return kubernetes } // OnNodeSelect implements the NodeSelectListener interface. func (widget *KubernetesInfo) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *KubernetesInfo) OnResourceDataChange(data resourcedata.Data) { widget.updateNodeData(data) if data.Node == widget.selectedNode { widget.redraw() } } // OnAPIDataChange implements the APIDataListener interface. func (widget *KubernetesInfo) OnAPIDataChange(node string, data *apidata.Data) { nodeAPIData := data.Nodes[node] widget.updateNodeAPIData(node, nodeAPIData) if node == widget.selectedNode { widget.redraw() } } func (widget *KubernetesInfo) updateNodeData(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) switch res := data.Resource.(type) { case *k8s.KubeletSpec: if data.Deleted { nodeData.kubernetesVersion = notAvailable } else { imageParts := strings.Split(res.TypedSpec().Image, ":") if len(imageParts) > 0 { nodeData.kubernetesVersion = imageParts[len(imageParts)-1] } } case *k8s.StaticPodStatus: if data.Deleted { delete(nodeData.staticPodStatusMap, res.Metadata().ID()) } else { nodeData.staticPodStatusMap[res.Metadata().ID()] = res } nodeData.podStatuses = widget.staticPodStatuses(maps.Values(nodeData.staticPodStatusMap)) case *config.MachineType: if data.Deleted { nodeData.isControlPlane = false nodeData.typ = notAvailable } else { nodeData.isControlPlane = res.MachineType() == machine.TypeControlPlane nodeData.typ = res.MachineType().String() } } } func (widget *KubernetesInfo) updateNodeAPIData(node string, data *apidata.Node) { nodeData := widget.getOrCreateNodeData(node) if data != nil && data.ServiceList != nil { for _, info := range data.ServiceList.GetServices() { if info.Id == "kubelet" { nodeData.kubeletStatus = toHealthStatus(info.GetHealth().Healthy) break } } } } func (widget *KubernetesInfo) getOrCreateNodeData(node string) *kubernetesInfoData { nodeData, ok := widget.nodeMap[node] if !ok { nodeData = &kubernetesInfoData{ typ: notAvailable, kubernetesVersion: notAvailable, kubeletStatus: notAvailable, podStatuses: staticPodStatuses{ apiServer: notAvailable, controllerManager: notAvailable, scheduler: notAvailable, }, staticPodStatusMap: make(map[resource.ID]*k8s.StaticPodStatus), } widget.nodeMap[node] = nodeData } return nodeData } func (widget *KubernetesInfo) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) fieldList := make([]field, 0, 5) fieldList = append(fieldList, field{ Name: "TYPE", Value: data.typ, }, field{ Name: "KUBERNETES", Value: data.kubernetesVersion, }, field{ Name: "KUBELET", Value: data.kubeletStatus, }) if data.isControlPlane { fieldList = append(fieldList, field{ Name: "APISERVER", Value: data.podStatuses.apiServer, }, field{ Name: "CONTROLLER-MANAGER", Value: data.podStatuses.controllerManager, }, field{ Name: "SCHEDULER", Value: data.podStatuses.scheduler, }) } fields := fieldGroup{ fields: fieldList, } widget.SetText(fields.String()) } func (widget *KubernetesInfo) staticPodStatuses(statuses []*k8s.StaticPodStatus) staticPodStatuses { result := staticPodStatuses{ apiServer: notAvailable, controllerManager: notAvailable, scheduler: notAvailable, } isReady := func(podStatus map[string]any) string { conditions, conditionsOk := podStatus["conditions"] if !conditionsOk { return notAvailable } conditionsSlc, conditionsSlcOk := conditions.([]any) if !conditionsSlcOk { return notAvailable } for _, condition := range conditionsSlc { conditionObj, conditionObjOk := condition.(map[string]any) if !conditionObjOk { return notAvailable } if conditionObj["type"] == "Ready" { return toHealthStatus(conditionObj["status"] == "True") } } return notAvailable } for _, status := range statuses { podStatus := status.TypedSpec().PodStatus switch { case strings.Contains(status.Metadata().ID(), "kube-apiserver"): result.apiServer = isReady(podStatus) case strings.Contains(status.Metadata().ID(), "kube-controller-manager"): result.controllerManager = isReady(podStatus) case strings.Contains(status.Metadata().ID(), "kube-scheduler"): result.scheduler = isReady(podStatus) } } return result } ================================================ FILE: internal/pkg/dashboard/components/logviewer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) // LogViewer represents the logs widget. type LogViewer struct { tview.Grid logs tview.TextView } // NewLogViewer initializes LogViewer. func NewLogViewer() *LogViewer { widget := &LogViewer{ Grid: *tview.NewGrid(), logs: *tview.NewTextView(), } widget.logs.ScrollToEnd(). SetDynamicColors(true). SetMaxLines(maxLogLines). SetText(noData). SetBorderPadding(0, 0, 1, 1). SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { _, _, _, pageSize := widget.logs.GetInnerRect() lineOffset, columnOffset := widget.logs.GetScrollOffset() //nolint:exhaustive switch event.Key() { case tcell.KeyCtrlD: widget.logs.ScrollTo(lineOffset+(pageSize/2), columnOffset) return nil case tcell.KeyCtrlU: widget.logs.ScrollTo(lineOffset-(pageSize/2), columnOffset) return nil } return event }) widget.SetRows(1, 0).SetColumns(0) widget.AddItem(NewHorizontalLine("Logs"), 0, 0, 1, 1, 0, 0, false) widget.AddItem(&widget.logs, 1, 0, 1, 1, 0, 0, true) return widget } // WriteLog writes the log line to the widget. func (widget *LogViewer) WriteLog(logLine, logError string) { if logError != "" { logLine = "[red]" + tview.Escape(logError) + "[-]\n" } else { logLine = tview.Escape(logLine) + "\n" } widget.logs.Write([]byte(logLine)) //nolint:errcheck } ================================================ FILE: internal/pkg/dashboard/components/networkinfo.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "net/netip" "slices" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/rivo/tview" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) var ( zeroPrefix = netip.Prefix{} routedNoK8sID = network.FilteredNodeAddressID(network.NodeAddressRoutedID, k8s.NodeAddressFilterNoK8s) ) type networkInfoData struct { hostname string gateway string connectivity string resolvers string timeservers string addresses string nodeAddressRouted *network.NodeAddress nodeAddressRoutedNoK8s *network.NodeAddress routeStatusMap map[resource.ID]*network.RouteStatus } // NetworkInfo represents the network info widget. type NetworkInfo struct { tview.TextView selectedNode string nodeMap map[string]*networkInfoData } // NewNetworkInfo initializes NetworkInfo. func NewNetworkInfo() *NetworkInfo { component := &NetworkInfo{ TextView: *tview.NewTextView(), nodeMap: make(map[string]*networkInfoData), } component.SetDynamicColors(true). SetText(noData). SetBorderPadding(1, 0, 1, 0) return component } // OnNodeSelect implements the NodeSelectListener interface. func (widget *NetworkInfo) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *NetworkInfo) OnResourceDataChange(data resourcedata.Data) { widget.updateNodeData(data) if data.Node == widget.selectedNode { widget.redraw() } } //nolint:gocyclo func (widget *NetworkInfo) updateNodeData(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) switch res := data.Resource.(type) { case *network.ResolverStatus: if data.Deleted { nodeData.resolvers = notAvailable } else { nodeData.resolvers = widget.resolvers(res) } case *network.TimeServerStatus: if data.Deleted { nodeData.timeservers = notAvailable } else { nodeData.timeservers = widget.timeservers(res) } case *network.Status: if data.Deleted { nodeData.connectivity = notAvailable } else { nodeData.connectivity = widget.connectivity(res) } case *network.HostnameStatus: if data.Deleted { nodeData.hostname = notAvailable } else { nodeData.hostname = res.TypedSpec().Hostname } case *network.RouteStatus: if data.Deleted { delete(nodeData.routeStatusMap, res.Metadata().ID()) } else { nodeData.routeStatusMap[res.Metadata().ID()] = res } nodeData.gateway = widget.gateway(maps.Values(nodeData.routeStatusMap)) case *network.NodeAddress: widget.setAddresses(data, res) } } func (widget *NetworkInfo) getOrCreateNodeData(node string) *networkInfoData { data, ok := widget.nodeMap[node] if !ok { data = &networkInfoData{ hostname: notAvailable, addresses: notAvailable, gateway: notAvailable, connectivity: notAvailable, resolvers: notAvailable, timeservers: notAvailable, routeStatusMap: make(map[resource.ID]*network.RouteStatus), } widget.nodeMap[node] = data } return data } func (widget *NetworkInfo) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) fields := fieldGroup{ fields: []field{ { Name: "HOST", Value: data.hostname, }, { Name: "IP", Value: data.addresses, }, { Name: "GW", Value: data.gateway, }, { Name: "CONNECTIVITY", Value: data.connectivity, }, { Name: "DNS", Value: data.resolvers, }, { Name: "NTP", Value: data.timeservers, }, }, } widget.SetText(fields.String()) } func (widget *NetworkInfo) setAddresses(data resourcedata.Data, nodeAddress *network.NodeAddress) { nodeData := widget.getOrCreateNodeData(data.Node) switch nodeAddress.Metadata().ID() { case network.NodeAddressRoutedID: if data.Deleted { nodeData.nodeAddressRouted = nil } else { nodeData.nodeAddressRouted = nodeAddress } case routedNoK8sID: if data.Deleted { nodeData.nodeAddressRoutedNoK8s = nil } else { nodeData.nodeAddressRoutedNoK8s = nodeAddress } } formatIPs := func(res *network.NodeAddress) string { if res == nil { return notAvailable } strs := xslices.Map(res.TypedSpec().Addresses, netip.Prefix.String) slices.Sort(strs) return strings.Join(strs, ", ") } // if "routed-no-k8s" is available, use it if nodeData.nodeAddressRoutedNoK8s != nil { nodeData.addresses = formatIPs(nodeData.nodeAddressRoutedNoK8s) return } // fallback to "routed" nodeData.addresses = formatIPs(nodeData.nodeAddressRouted) } func (widget *NetworkInfo) gateway(statuses []*network.RouteStatus) string { var gatewaysV4, gatewaysV6 []string for _, status := range statuses { gateway := status.TypedSpec().Gateway if !gateway.IsValid() || status.TypedSpec().Destination != zeroPrefix { continue } if gateway.Is4() { gatewaysV4 = append(gatewaysV4, gateway.String()) } else { gatewaysV6 = append(gatewaysV6, gateway.String()) } } if len(gatewaysV4) == 0 && len(gatewaysV6) == 0 { return notAvailable } slices.Sort(gatewaysV4) slices.Sort(gatewaysV6) return strings.Join(append(gatewaysV4, gatewaysV6...), ", ") } func (widget *NetworkInfo) resolvers(status *network.ResolverStatus) string { strs := xslices.Map(status.TypedSpec().DNSServers, netip.Addr.String) if len(strs) == 0 { return none } return strings.Join(strs, ", ") } func (widget *NetworkInfo) timeservers(status *network.TimeServerStatus) string { if len(status.TypedSpec().NTPServers) == 0 { return none } return strings.Join(status.TypedSpec().NTPServers, ", ") } func (widget *NetworkInfo) connectivity(status *network.Status) string { if status.TypedSpec().ConnectivityReady { return "[green]√ OK[-]" } return "[red]× FAILED[-]" } ================================================ FILE: internal/pkg/dashboard/components/sparklines.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "github.com/gdamore/tcell/v2" "github.com/navidys/tvxwidgets" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" ) // BaseSparklineGroup represents the widget with some sparklines. type BaseSparklineGroup struct { tview.Primitive sparklines []*tvxwidgets.Sparkline dataLabels []string } // NewBaseSparklineGroup initializes BaseSparklineGroup. func NewBaseSparklineGroup(title string, labels, dataLabels []string) *BaseSparklineGroup { flex := tview.NewFlex().SetDirection(tview.FlexRow) root := tview.NewFrame(flex). SetBorders(0, 0, 0, 0, 0, 0). AddText(title, true, tview.AlignLeft, tcell.ColorDefault) colors := []tcell.Color{tcell.ColorRed, tcell.ColorGreen} sparklines := make([]*tvxwidgets.Sparkline, len(labels)) for i := range labels { sparklines[i] = tvxwidgets.NewSparkline() sparklines[i].SetBorder(false) sparklines[i].SetDataTitle(labels[i]) sparklines[i].SetTitleColor(tcell.ColorDefault) sparklines[i].SetLineColor(colors[i%len(colors)]) flex.AddItem(sparklines[i], 0, 1, false) } return &BaseSparklineGroup{ Primitive: root, sparklines: sparklines, dataLabels: dataLabels, } } // OnAPIDataChange implements the APIDataListener interface. func (widget *BaseSparklineGroup) OnAPIDataChange(node string, data *apidata.Data) { nodeData := data.Nodes[node] if nodeData == nil { for i := range widget.sparklines { widget.sparklines[i].SetData([]float64{0}) } return } _, _, width, _ := widget.GetRect() //nolint:dogsled for i, name := range widget.dataLabels { series := nodeData.Series[name] if len(series) < width { width = len(series) } widget.sparklines[i].SetData(series[len(series)-width:]) } } // NewNetSparkline creates network sparkline. func NewNetSparkline() *BaseSparklineGroup { return NewBaseSparklineGroup(" [::b]NET", []string{"RX", "TX"}, []string{"netrxbytes", "nettxbytes"}) } // NewDiskSparkline creates disk sparkline. func NewDiskSparkline() *BaseSparklineGroup { return NewBaseSparklineGroup(" [::b]DISK", []string{"READ", "WRITE"}, []string{"diskrdsectors", "diskwrsectors"}) } ================================================ FILE: internal/pkg/dashboard/components/tables.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "path/filepath" "sort" "strconv" "strings" "time" "github.com/dustin/go-humanize" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" ) // ProcessTable represents the widget with process info. type ProcessTable struct { *tview.Table lastNode string } // NewProcessTable initializes ProcessTable. func NewProcessTable() *ProcessTable { widget := &ProcessTable{ Table: tview.NewTable(), } widget.SetFixed(1, 0) widget.SetBorders(false) widget.SetSelectable(true, false) widget.SetBorderPadding(0, 0, 1, 0) widget.SetSelectedStyle(tcell.StyleDefault.Attributes(tcell.AttrReverse)) widget.clear() return widget } // width constants for ProcessTable columns. const ( pidWidth = 7 stateWidth = 1 cpuWidth = 6 memWidth = 6 virtWidth = 8 resWidth = 8 timeWidth = 10 threadsWidth = 4 ) func (widget *ProcessTable) clear() { widget.Clear() widget.SetCell(0, 0, &tview.TableCell{ Text: "[::b]PID", Align: tview.AlignRight, MaxWidth: pidWidth, NotSelectable: true, }) widget.SetCell(0, 1, &tview.TableCell{ Text: "[::b]S", Align: tview.AlignCenter, MaxWidth: stateWidth, NotSelectable: true, }) widget.SetCell(0, 2, &tview.TableCell{ Text: "[::b]CPU%", Align: tview.AlignRight, MaxWidth: cpuWidth, NotSelectable: true, }) widget.SetCell(0, 3, &tview.TableCell{ Text: "[::b]MEM%", Align: tview.AlignRight, MaxWidth: memWidth, NotSelectable: true, }) widget.SetCell(0, 4, &tview.TableCell{ Text: "[::b]VIRT", Align: tview.AlignRight, MaxWidth: virtWidth, NotSelectable: true, }) widget.SetCell(0, 5, &tview.TableCell{ Text: "[::b]RES", Align: tview.AlignRight, MaxWidth: resWidth, NotSelectable: true, }) widget.SetCell(0, 6, &tview.TableCell{ Text: "[::b]TIME+", Align: tview.AlignRight, MaxWidth: timeWidth, NotSelectable: true, }) widget.SetCell(0, 7, &tview.TableCell{ Text: "[::b]THR", Align: tview.AlignRight, MaxWidth: threadsWidth, NotSelectable: true, }) widget.SetCell(0, 8, &tview.TableCell{ Text: "[::b]COMMAND", Align: tview.AlignLeft, NotSelectable: true, Expansion: 1, }) widget.SetCell(1, 0, &tview.TableCell{ Text: noData, Align: tview.AlignCenter, NotSelectable: true, }) } // OnAPIDataChange implements the APIDataListener interface. // //nolint:gocyclo func (widget *ProcessTable) OnAPIDataChange(node string, data *apidata.Data) { widget.clear() nodeData := data.Nodes[node] if nodeData == nil { widget.Select(1, 0) return } totalMem := nodeData.Memory.GetMeminfo().GetMemtotal() * 1024 if totalMem == 0 { totalMem = 1 } totalWeightedCPU := nodeData.CPUUsageByName("total_weighted") if totalWeightedCPU == 0 { totalWeightedCPU = 1 } // All downstream logic relies on nodeData.Processes to be not nil // Putting a check here to reduce cyclomatic complexity if nodeData.Processes == nil { return } if nodeData.ProcsDiff != nil { sort.Slice(nodeData.Processes.Processes, func(i, j int) bool { proc1 := nodeData.Processes.Processes[i] proc2 := nodeData.Processes.Processes[j] return nodeData.ProcsDiff[proc1.Pid].CpuTime > nodeData.ProcsDiff[proc2.Pid].CpuTime }) } for idx, proc := range nodeData.Processes.Processes { var args string switch { case proc.Executable == "": args = proc.Command case proc.Args != "" && strings.Fields(proc.Args)[0] == filepath.Base(strings.Fields(proc.Executable)[0]): args = strings.Replace(proc.Args, strings.Fields(proc.Args)[0], proc.Executable, 1) default: args = proc.Args } // filter out non-printable characters args = strings.Map(func(r rune) rune { if r < 32 || r > 126 { return ' ' } return r }, args) widget.SetCell(idx+1, 0, &tview.TableCell{ Text: strconv.FormatInt(int64(proc.GetPid()), 10), Align: tview.AlignRight, MaxWidth: pidWidth, }) widget.SetCell(idx+1, 1, &tview.TableCell{ Text: proc.State, Align: tview.AlignCenter, MaxWidth: stateWidth, }) widget.SetCell(idx+1, 2, &tview.TableCell{ Text: fmt.Sprintf("%.1f", nodeData.ProcsDiff[proc.Pid].GetCpuTime()/totalWeightedCPU*100.0), Align: tview.AlignRight, MaxWidth: cpuWidth, }) widget.SetCell(idx+1, 3, &tview.TableCell{ Text: fmt.Sprintf("%.1f", float64(proc.ResidentMemory)/float64(totalMem)*100.0), Align: tview.AlignRight, MaxWidth: memWidth, }) widget.SetCell(idx+1, 4, &tview.TableCell{ Text: humanize.Bytes(proc.VirtualMemory), Align: tview.AlignRight, MaxWidth: virtWidth, }) widget.SetCell(idx+1, 5, &tview.TableCell{ Text: humanize.Bytes(proc.ResidentMemory), Align: tview.AlignRight, MaxWidth: resWidth, }) widget.SetCell(idx+1, 6, &tview.TableCell{ Text: (time.Duration(proc.CpuTime) * time.Second).String(), Align: tview.AlignRight, MaxWidth: timeWidth, }) widget.SetCell(idx+1, 7, &tview.TableCell{ Text: strconv.FormatInt(int64(proc.Threads), 10), Align: tview.AlignRight, MaxWidth: threadsWidth, }) widget.SetCell(idx+1, 8, &tview.TableCell{ Text: args, Align: tview.AlignLeft, }) } selectedRow, _ := widget.GetSelection() if selectedRow > len(nodeData.Processes.Processes)+1 || widget.lastNode != node { widget.Select(1, 0) } widget.lastNode = node } ================================================ FILE: internal/pkg/dashboard/components/tables_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components_test import ( "testing" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/components" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) func TestUpdate(t *testing.T) { testProcessTable := components.NewProcessTable() testData := &apidata.Data{ Nodes: map[string]*apidata.Node{ "node1": { Processes: &machine.Process{ Processes: []*machine.ProcessInfo{}, }, ProcsDiff: map[int32]*machine.ProcessInfo{ 1: {}, }, Series: map[string][]float64{}, }, "node2": { ProcsDiff: map[int32]*machine.ProcessInfo{ 1: {}, }, }, }, } testProcessTable.OnAPIDataChange("node1", testData) // Node2 does not have processes, without the check it panics testProcessTable.OnAPIDataChange("node2", testData) } ================================================ FILE: internal/pkg/dashboard/components/talosinfo.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package components import ( "fmt" "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) type talosInfoData struct { uuid string clusterName string siderolink string stage string ready string numMachinesText string secureBootState string machineIDSet map[string]struct{} } // TalosInfo represents the Talos info widget. type TalosInfo struct { tview.TextView selectedNode string nodeMap map[string]*talosInfoData } // NewTalosInfo initializes TalosInfo. func NewTalosInfo() *TalosInfo { widget := &TalosInfo{ TextView: *tview.NewTextView(), nodeMap: make(map[string]*talosInfoData), } widget.SetDynamicColors(true). SetText(noData). SetBorderPadding(1, 0, 1, 0) return widget } // OnNodeSelect implements the NodeSelectListener interface. func (widget *TalosInfo) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *TalosInfo) OnResourceDataChange(data resourcedata.Data) { widget.updateNodeData(data) if data.Node == widget.selectedNode { widget.redraw() } } //nolint:gocyclo func (widget *TalosInfo) updateNodeData(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) switch res := data.Resource.(type) { case *hardware.SystemInformation: if data.Deleted { nodeData.uuid = notAvailable } else { nodeData.uuid = res.TypedSpec().UUID } case *cluster.Info: clusterName := res.TypedSpec().ClusterName if data.Deleted || clusterName == "" { nodeData.clusterName = notAvailable } else { nodeData.clusterName = clusterName } case *siderolink.Status: if data.Deleted { nodeData.siderolink = notAvailable } else { nodeData.siderolink = formatText(res.TypedSpec().Host, res.TypedSpec().Connected) } case *runtime.MachineStatus: if data.Deleted { nodeData.stage = notAvailable nodeData.ready = notAvailable } else { nodeData.stage = formatStatus(res.TypedSpec().Stage.String()) nodeData.ready = formatStatus(res.TypedSpec().Status.Ready) } case *runtime.SecurityState: if data.Deleted { nodeData.secureBootState = notAvailable } else { nodeData.secureBootState = formatStatus(res.TypedSpec().SecureBoot) } case *cluster.Member: if data.Deleted { delete(nodeData.machineIDSet, res.Metadata().ID()) } else { nodeData.machineIDSet[res.Metadata().ID()] = struct{}{} } suffix := "" if len(nodeData.machineIDSet) != 1 { suffix = "s" } nodeData.numMachinesText = fmt.Sprintf("(%d machine%s)", len(nodeData.machineIDSet), suffix) } } func (widget *TalosInfo) getOrCreateNodeData(node string) *talosInfoData { nodeData, ok := widget.nodeMap[node] if !ok { nodeData = &talosInfoData{ uuid: notAvailable, clusterName: notAvailable, siderolink: notAvailable, stage: notAvailable, ready: notAvailable, numMachinesText: notAvailable, secureBootState: notAvailable, machineIDSet: make(map[string]struct{}), } widget.nodeMap[node] = nodeData } return nodeData } func (widget *TalosInfo) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) fields := fieldGroup{ fields: []field{ { Name: "UUID", Value: data.uuid, }, { Name: "CLUSTER", Value: data.clusterName + " " + data.numMachinesText, }, { Name: "SIDEROLINK", Value: data.siderolink, }, { Name: "STAGE", Value: data.stage, }, { Name: "READY", Value: data.ready, }, { Name: "SECUREBOOT", Value: data.secureBootState, }, }, } widget.SetText(fields.String()) } ================================================ FILE: internal/pkg/dashboard/configurl.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "context" "fmt" "strings" "github.com/rivo/tview" "github.com/siderolabs/go-procfs/procfs" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/meta" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) const unset = "[gray](unset)[-]" type configURLData struct { existingCode string } // ConfigURLGrid represents the config URL grid. type ConfigURLGrid struct { tview.Grid dashboard *Dashboard form *tview.Form template *tview.TextView existingCode *tview.TextView newCodeField *tview.InputField infoView *tview.TextView selectedNode string nodeMap map[string]*configURLData } // NewConfigURLGrid returns a new config URL grid. func NewConfigURLGrid(ctx context.Context, dashboard *Dashboard) *ConfigURLGrid { grid := &ConfigURLGrid{ Grid: *tview.NewGrid(), dashboard: dashboard, form: tview.NewForm(), template: tview.NewTextView().SetDynamicColors(true).SetLabel("Template").SetScrollable(false), existingCode: tview.NewTextView().SetDynamicColors(true).SetLabel("Existing Code").SetText(unset).SetSize(1, 0).SetScrollable(false), newCodeField: tview.NewInputField().SetLabel("New Code"), infoView: tview.NewTextView().SetDynamicColors(true).SetSize(2, 0).SetScrollable(false), nodeMap: make(map[string]*configURLData), } grid.template.SetText(grid.readTemplateFromKernelArgs()) grid.SetRows(-1, 18, -1).SetColumns(-1, 72, -1) grid.form.SetBorder(true) grid.form.AddFormItem(grid.template) grid.form.AddFormItem(grid.existingCode) grid.form.AddFormItem(grid.newCodeField) grid.form.AddButton("Save", func() { ctx = nodeContext(ctx, grid.selectedNode) //nolint:fatcontext value := grid.newCodeField.GetText() if value == "" { grid.infoView.SetText("[red]Error: No code entered[-]") return } err := dashboard.cli.MetaWrite(ctx, meta.DownloadURLCode, []byte(value)) if err != nil { grid.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err)) return } grid.clearForm() grid.dashboard.selectScreen(ScreenSummary) }) grid.form.AddButton("Delete", func() { ctx = nodeContext(ctx, grid.selectedNode) //nolint:fatcontext err := dashboard.cli.MetaDelete(ctx, meta.DownloadURLCode) if err != nil { if status.Code(err) == codes.NotFound { grid.clearForm() grid.infoView.SetText("[green]Already deleted[-]") return } grid.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err)) return } grid.clearForm() grid.infoView.SetText("[green]Deleted successfully[-]") }) grid.form.AddFormItem(grid.infoView) grid.AddItem(tview.NewBox(), 0, 0, 1, 3, 0, 0, false) grid.AddItem(tview.NewBox(), 1, 0, 1, 1, 0, 0, false) grid.AddItem(grid.form, 1, 1, 1, 1, 0, 0, false) grid.AddItem(tview.NewBox(), 1, 2, 1, 1, 0, 0, false) grid.AddItem(tview.NewBox(), 2, 0, 1, 3, 0, 0, false) return grid } func (widget *ConfigURLGrid) readTemplateFromKernelArgs() (val string) { defer func() { // catch potential panic from procfs.ProcCmdline() if r := recover(); r != nil { val = "error reading kernel args" } }() option := procfs.ProcCmdline().Get(constants.KernelParamConfig).First() if option == nil { return unset } codeVar := fmt.Sprintf("${%s}", constants.CodeKey) return strings.ReplaceAll(tview.Escape(*option), codeVar, fmt.Sprintf("[green]%s[-]", codeVar)) } // OnScreenSelect implements the screenSelectListener interface. func (widget *ConfigURLGrid) onScreenSelect(active bool) { if active { widget.dashboard.app.SetFocus(widget.form) } else { widget.clearForm() } } // OnNodeSelect implements the NodeSelectListener interface. func (widget *ConfigURLGrid) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.clearForm() widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *ConfigURLGrid) OnResourceDataChange(data resourcedata.Data) { widget.updateNodeData(data) if data.Node == widget.selectedNode { widget.redraw() } } func (widget *ConfigURLGrid) updateNodeData(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) //nolint:gocritic switch res := data.Resource.(type) { case *runtimeres.MetaKey: if res.Metadata().ID() == runtimeres.MetaKeyTagToID(meta.DownloadURLCode) { if data.Deleted { nodeData.existingCode = unset } else { val := res.TypedSpec().Value if val == "" { val = "(empty)" } nodeData.existingCode = fmt.Sprintf("[blue]%s[-]", val) } } } } func (widget *ConfigURLGrid) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) widget.existingCode.SetText(data.existingCode) } func (widget *ConfigURLGrid) getOrCreateNodeData(node string) *configURLData { nodeData, ok := widget.nodeMap[node] if !ok { nodeData = &configURLData{ existingCode: unset, } widget.nodeMap[node] = nodeData } return nodeData } func (widget *ConfigURLGrid) clearForm() { widget.form.SetFocus(0) widget.infoView.SetText("") widget.newCodeField.SetText("") } ================================================ FILE: internal/pkg/dashboard/context.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "context" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/client" ) func nodeContext(ctx context.Context, selectedNode string) context.Context { md, mdOk := metadata.FromOutgoingContext(ctx) if mdOk { md.Delete("nodes") ctx = metadata.NewOutgoingContext(ctx, md) } if selectedNode != "" { ctx = client.WithNode(ctx, selectedNode) } return ctx } ================================================ FILE: internal/pkg/dashboard/dashboard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package dashboard implements a text-based UI dashboard. package dashboard import ( "context" "errors" "fmt" "net/netip" "slices" "strings" "time" "github.com/gdamore/tcell/v2" _ "github.com/gdamore/tcell/v2/terminfo/l/linux" // linux terminal is used when running on the machine, but not included with tcell_minimal "github.com/rivo/tview" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-api-signature/pkg/message" "golang.org/x/sync/errgroup" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/components" "github.com/siderolabs/talos/internal/pkg/dashboard/logdata" "github.com/siderolabs/talos/internal/pkg/dashboard/resolver" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/client" ) func init() { // set background to be left as the default color of the terminal tview.Styles.PrimitiveBackgroundColor = tcell.ColorDefault } // Screen is a dashboard screen. type Screen string const ( pageMain = "main" // ScreenSummary is the summary screen. ScreenSummary Screen = "Summary" // ScreenMonitor is the monitor (metrics) screen. ScreenMonitor Screen = "Monitor" // ScreenNetworkConfig is the network configuration screen. ScreenNetworkConfig Screen = "Network Config" // ScreenConfigURL is the config URL screen. ScreenConfigURL Screen = "Config URL" ) // APIDataListener is a listener which is notified when API-sourced data is updated. type APIDataListener interface { OnAPIDataChange(node string, data *apidata.Data) } // ResourceDataListener is a listener which is notified when a resource is updated. type ResourceDataListener interface { OnResourceDataChange(data resourcedata.Data) } // LogDataListener is a listener which is notified when a log line is received. type LogDataListener interface { OnLogDataChange(node, logLine, logError string) } // NodeSelectListener is a listener which is notified when a node is selected. type NodeSelectListener interface { OnNodeSelect(node string) } // TickerListener is a listener which is notified on every tick. type TickerListener interface { OnTick() } type screenConfig struct { screenKey string screen Screen keyCode tcell.Key primitive screenSelectListener allowNodeNavigation bool } // screenSelectListener is a listener which is notified when a screen is selected. type screenSelectListener interface { tview.Primitive onScreenSelect(active bool) } // Dashboard implements the summary dashboard. type Dashboard struct { cli *client.Client interval time.Duration apiDataSource *apidata.Source resourceDataSource *resourcedata.Source logDataSource *logdata.Source apiDataListeners []APIDataListener resourceDataListeners []ResourceDataListener logDataListeners []LogDataListener nodeSelectListeners []NodeSelectListener tickerListeners []TickerListener app *tview.Application mainGrid *tview.Grid pages *tview.Pages selectedScreenConfig *screenConfig screenConfigs []screenConfig footer *components.Footer data *apidata.Data selectedNodeIndex int selectedNode string paused bool nodeSet map[string]struct{} ipsToNodeAliases map[string]string nodes []string } // buildDashboard initializes the summary dashboard. // //nolint:gocyclo,cyclop func buildDashboard(ctx context.Context, cli *client.Client, opts ...Option) (*Dashboard, error) { defOptions := defaultOptions() for _, opt := range opts { opt(defOptions) } // map node IPs to their aliases (names/IPs - as specified "nodes" in context). // this will also trigger the interactive API authentication if needed - e.g., when the API is used through Omni. ipsToNodeAliases, err := collectNodeIPsToNodeAliases(ctx, cli) if err != nil { return nil, err } nodes := getSortedNodeAliases(ipsToNodeAliases) dashboard := &Dashboard{ cli: cli, interval: defOptions.interval, app: tview.NewApplication(), nodeSet: make(map[string]struct{}), nodes: nodes, ipsToNodeAliases: ipsToNodeAliases, } dashboard.mainGrid = tview.NewGrid(). SetRows(1, 0, 1). SetColumns(0) dashboard.pages = tview.NewPages().AddPage(pageMain, dashboard.mainGrid, true, true) dashboard.app.EnableMouse(true) dashboard.app.SetRoot(dashboard.pages, true).SetFocus(dashboard.pages) header := components.NewHeader() dashboard.mainGrid.AddItem(header, 0, 0, 1, 1, 0, 0, false) if err = dashboard.initScreenConfigs(ctx, defOptions.screens); err != nil { return nil, err } screenKeyToName := xslices.ToMap(dashboard.screenConfigs, func(t screenConfig) (string, string) { return t.screenKey, string(t.screen) }) screenConfigByKeyCode := xslices.ToMap(dashboard.screenConfigs, func(config screenConfig) (tcell.Key, screenConfig) { return config.keyCode, config }) dashboard.footer = components.NewFooter(screenKeyToName, nodes) dashboard.footer.NodeClick = func(node string) { allowNodeNavigation := dashboard.selectedScreenConfig != nil && dashboard.selectedScreenConfig.allowNodeNavigation if !allowNodeNavigation { return } for i, n := range dashboard.nodes { if n == node { dashboard.selectNodeByIndex(i) break } } } dashboard.footer.ScreenClick = func(screenName string) { dashboard.selectScreen(Screen(screenName)) } dashboard.app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { config, screenOk := screenConfigByKeyCode[event.Key()] allowNodeNavigation := dashboard.selectedScreenConfig != nil && dashboard.selectedScreenConfig.allowNodeNavigation switch { case screenOk: dashboard.selectScreen(config.screen) return nil case allowNodeNavigation && (event.Key() == tcell.KeyLeft || event.Rune() == 'h'): dashboard.selectNodeByIndex(dashboard.selectedNodeIndex - 1) return nil case allowNodeNavigation && (event.Key() == tcell.KeyRight || event.Rune() == 'l'): dashboard.selectNodeByIndex(dashboard.selectedNodeIndex + 1) return nil case defOptions.allowExitKeys && (event.Key() == tcell.KeyCtrlC || event.Rune() == 'q'): dashboard.app.Stop() return nil case event.Key() == tcell.KeyCtrlZ: dashboard.paused = !dashboard.paused dashboard.footer.SetPaused(dashboard.paused) return nil } return event }) dashboard.mainGrid.AddItem(dashboard.footer, 2, 0, 1, 1, 0, 0, false) dashboard.apiDataListeners = []APIDataListener{ header, } dashboard.resourceDataListeners = []ResourceDataListener{ header, } dashboard.logDataListeners = []LogDataListener{} dashboard.nodeSelectListeners = []NodeSelectListener{ header, dashboard.footer, } dashboard.tickerListeners = []TickerListener{ header, } for _, config := range dashboard.screenConfigs { screenPrimitive := config.primitive apiDataListener, ok := screenPrimitive.(APIDataListener) if ok { dashboard.apiDataListeners = append(dashboard.apiDataListeners, apiDataListener) } resourceDataListener, ok := screenPrimitive.(ResourceDataListener) if ok { dashboard.resourceDataListeners = append(dashboard.resourceDataListeners, resourceDataListener) } logDataListener, ok := screenPrimitive.(LogDataListener) if ok { dashboard.logDataListeners = append(dashboard.logDataListeners, logDataListener) } nodeSelectListener, ok := screenPrimitive.(NodeSelectListener) if ok { dashboard.nodeSelectListeners = append(dashboard.nodeSelectListeners, nodeSelectListener) } } nodeResolver := resolver.New(ipsToNodeAliases) dashboard.apiDataSource = &apidata.Source{ Client: cli, Interval: defOptions.interval, Resolver: nodeResolver, } dashboard.resourceDataSource = &resourcedata.Source{ COSI: cli.COSI, } dashboard.logDataSource = logdata.NewSource(cli, nodeResolver) return dashboard, nil } func (d *Dashboard) initScreenConfigs(ctx context.Context, screens []Screen) error { primitiveForScreen := func(screen Screen) screenSelectListener { switch screen { case ScreenSummary: return NewSummaryGrid(d.app) case ScreenMonitor: return NewMonitorGrid(d.app) case ScreenNetworkConfig: return NewNetworkConfigGrid(ctx, d) case ScreenConfigURL: return NewConfigURLGrid(ctx, d) default: return nil } } d.screenConfigs = make([]screenConfig, 0, len(screens)) for i, screen := range screens { primitive := primitiveForScreen(screen) if primitive == nil { return fmt.Errorf("unknown screen %s", screen) } config := screenConfig{ screenKey: fmt.Sprintf("F%d", i+1), screen: screen, keyCode: tcell.KeyF1 + tcell.Key(i), primitive: primitive, allowNodeNavigation: true, } if screen == ScreenNetworkConfig || screen == ScreenConfigURL { config.allowNodeNavigation = false } d.screenConfigs = append(d.screenConfigs, config) } return nil } // Run starts the dashboard. func Run(ctx context.Context, cli *client.Client, opts ...Option) (runErr error) { ctx, cancel := context.WithCancel(ctx) defer cancel() dashboard, err := buildDashboard(ctx, cli, opts...) if err != nil { return err } dashboard.selectNodeByIndex(0) // handle panic & stop dashboard gracefully on exit defer func() { if r := recover(); r != nil { runErr = fmt.Errorf("dashboard panic: %v", r) } dashboard.app.Stop() }() dashboard.selectScreen(ScreenSummary) eg, ctx := errgroup.WithContext(ctx) stopFunc := dashboard.startDataHandler(ctx) defer stopFunc() //nolint:errcheck eg.Go(func() error { defer cancel() return dashboard.app.Run() }) // stop dashboard when the context is canceled eg.Go(func() error { <-ctx.Done() dashboard.app.Stop() return nil }) return eg.Wait() } // startDataHandler starts the data and log update handler and returns a function to stop it. // //nolint:gocyclo func (d *Dashboard) startDataHandler(ctx context.Context) func() error { var eg errgroup.Group ctx, cancel := context.WithCancel(ctx) stopFunc := func() error { cancel() err := eg.Wait() if errors.Is(err, context.Canceled) { return nil } return err } eg.Go(func() error { // start API data source dataCh := d.apiDataSource.Run(ctx) defer d.apiDataSource.Stop() // start resources data source d.resourceDataSource.Run(ctx) defer d.resourceDataSource.Stop() //nolint:errcheck // start logs data source d.logDataSource.Start(ctx) defer d.logDataSource.Stop() //nolint:errcheck lastLogTime := time.Now() ticker := time.NewTicker(500 * time.Millisecond) defer ticker.Stop() for { select { case <-ctx.Done(): return ctx.Err() case nodeLog := <-d.logDataSource.LogCh: if time.Since(lastLogTime) < 50*time.Millisecond { d.app.QueueUpdate(func() { d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error) }) } else { d.app.QueueUpdateDraw(func() { d.processLog(nodeLog.Node, nodeLog.Log, nodeLog.Error) }) } lastLogTime = time.Now() case d.data = <-dataCh: d.app.QueueUpdateDraw(func() { if !d.paused { d.processAPIData() } }) case nodeResource := <-d.resourceDataSource.NodeResourceCh: d.app.QueueUpdateDraw(func() { d.processNodeResource(nodeResource) }) case <-ticker.C: d.app.QueueUpdateDraw(func() { if !d.paused { d.processTick() } }) } } }) return stopFunc } func (d *Dashboard) selectNodeByIndex(index int) { if len(d.nodes) == 0 { return } if index < 0 { index = 0 } else if index >= len(d.nodes) { index = len(d.nodes) - 1 } d.selectedNode = d.nodes[index] d.selectedNodeIndex = index d.processAPIData() for _, listener := range d.nodeSelectListeners { listener.OnNodeSelect(d.selectedNode) } } // processAPIData re-renders the components with new API-sourced data. func (d *Dashboard) processAPIData() { if d.data == nil { return } for _, component := range d.apiDataListeners { component.OnAPIDataChange(d.selectedNode, d.data) } } // processNodeResource re-renders the components with new resource data. func (d *Dashboard) processNodeResource(nodeResource resourcedata.Data) { for _, component := range d.resourceDataListeners { component.OnResourceDataChange(nodeResource) } } // processLog re-renders the log components with new log data. func (d *Dashboard) processLog(node, logLine, logError string) { for _, component := range d.logDataListeners { component.OnLogDataChange(node, logLine, logError) } } // processTick re-renders the components with ticker. func (d *Dashboard) processTick() { for _, component := range d.tickerListeners { component.OnTick() } } func (d *Dashboard) selectScreen(screen Screen) { for _, info := range d.screenConfigs { if info.screen == screen { d.selectedScreenConfig = &info //nolint:exportloopref d.mainGrid.AddItem(info.primitive, 1, 0, 1, 1, 0, 0, false) info.primitive.onScreenSelect(true) continue } d.mainGrid.RemoveItem(info.primitive) info.primitive.onScreenSelect(false) } d.footer.SelectScreen(string(screen)) } // collectNodeIPsToNodeAliases probes all nodes in the context for their IP addresses by calling their .Version endpoint and maps them to the node aliases in the context. // // Sample output: // // 172.20.0.6 -> node-1 // // 10.42.0.1 -> node-1 // // 172.20.0.7 -> node-2 // // 10.42.0.2 -> node-2. func collectNodeIPsToNodeAliases(ctx context.Context, c *client.Client) (map[string]string, error) { ipsToNodeAliases := make(map[string]string) nodes := nodeAliasesInContext(ctx) for _, node := range nodes { ctx = client.WithNodes(ctx, node) //nolint:fatcontext // do not replace this with "WithNode" - it would not return the IP in the response metadata resp, err := c.Version(ctx) if err != nil { return nil, fmt.Errorf("failed to get node %q version: %w", node, err) } if len(resp.GetMessages()) == 0 { return nil, fmt.Errorf("node %q returned no messages in version response", node) } nodeIP := resp.GetMessages()[0].GetMetadata().GetHostname() if nodeIP == "" { return nil, fmt.Errorf("node %q returned no IP in version response", node) } ipsToNodeAliases[nodeIP] = node } return ipsToNodeAliases, nil } // nodeAliasesInContext extracts the node aliases (IP, name etc.) from the given context which are stored in the "node" or "nodes" GRPC metadata. func nodeAliasesInContext(ctx context.Context) []string { md, mdOk := metadata.FromOutgoingContext(ctx) if !mdOk { return nil } nodeVal := md.Get("node") if len(nodeVal) > 0 { return []string{nodeVal[0]} } nodesVal := md.Get(message.NodesHeaderKey) return xslices.FlatMap(nodesVal, func(node string) []string { return strings.Split(node, ",") }) } // getSortedNodeAliases returns the unique node aliases sorted by their IP address. func getSortedNodeAliases(ipToNodeAliases map[string]string) []string { if len(ipToNodeAliases) == 0 { // assume that it is the local node (running on TTY) return []string{""} } nodeAliases := maps.Keys(xslices.ToSet(maps.Values(ipToNodeAliases))) // eliminate duplicates // if the aliases are IP addresses, compare them as IPs // otherwise, compare them as strings // all IPs come before non-IPs slices.SortFunc(nodeAliases, func(a, b string) int { addrA, aErr := netip.ParseAddr(a) addrB, bErr := netip.ParseAddr(b) if aErr != nil && bErr != nil { return strings.Compare(a, b) } if aErr != nil { return 1 } if bErr != nil { return -1 } return addrA.Compare(addrB) }) return nodeAliases } ================================================ FILE: internal/pkg/dashboard/formdata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "errors" "fmt" "net/netip" "strings" "unicode" "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) const ( interfaceNone = "(none)" // ModeDHCP is the DHCP mode for the link. ModeDHCP = "DHCP" // ModeStatic is the static IP mode for the link. ModeStatic = "Static" ) // NetworkConfigFormData is the form data for the network config. type NetworkConfigFormData struct { Base runtime.PlatformNetworkConfig Hostname string DNSServers string TimeServers string Iface string Mode string Addresses string Gateway string } // ToPlatformNetworkConfig converts the form data to a PlatformNetworkConfig. // //nolint:gocyclo func (formData *NetworkConfigFormData) ToPlatformNetworkConfig() (*runtime.PlatformNetworkConfig, error) { var errs error config := &formData.Base // zero-out the fields managed by the form config.Hostnames = nil config.Resolvers = nil config.TimeServers = nil config.Links = nil config.Operators = nil config.Addresses = nil config.Routes = nil if formData.Hostname != "" { config.Hostnames = []network.HostnameSpecSpec{ { Hostname: formData.Hostname, ConfigLayer: network.ConfigPlatform, }, } } dnsServers, err := formData.parseAddresses(formData.DNSServers) if err != nil { errs = multierror.Append(errs, fmt.Errorf("failed to parse DNS servers: %w", err)) } if len(dnsServers) > 0 { config.Resolvers = []network.ResolverSpecSpec{ { DNSServers: dnsServers, ConfigLayer: network.ConfigPlatform, }, } } timeServers := formData.splitInputList(formData.TimeServers) if len(timeServers) > 0 { config.TimeServers = []network.TimeServerSpecSpec{ { NTPServers: timeServers, ConfigLayer: network.ConfigPlatform, }, } } ifaceSelected := formData.Iface != "" && formData.Iface != interfaceNone if ifaceSelected { config.Links = []network.LinkSpecSpec{ { Name: formData.Iface, Logical: false, Up: true, Type: nethelpers.LinkEther, ConfigLayer: network.ConfigPlatform, }, } switch formData.Mode { case ModeDHCP: config.Operators = []network.OperatorSpecSpec{ { Operator: network.OperatorDHCP4, LinkName: formData.Iface, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: 1024, }, ConfigLayer: network.ConfigPlatform, }, } case ModeStatic: config.Addresses, err = formData.buildAddresses(formData.Iface) if err != nil { errs = multierror.Append(errs, err) } if len(config.Addresses) == 0 { errs = multierror.Append(errs, errors.New("no addresses specified")) } config.Routes, err = formData.buildRoutes(formData.Iface) if err != nil { errs = multierror.Append(errs, err) } } } if errs != nil { return nil, errs } return config, nil } func (formData *NetworkConfigFormData) parseAddresses(text string) ([]netip.Addr, error) { var errs error split := formData.splitInputList(text) addresses := make([]netip.Addr, 0, len(split)) for _, address := range split { addr, err := netip.ParseAddr(address) if err != nil { errs = multierror.Append(errs, fmt.Errorf("address: %w", err)) continue } addresses = append(addresses, addr) } if errs != nil { return nil, errs } return addresses, nil } func (formData *NetworkConfigFormData) buildAddresses(linkName string) ([]network.AddressSpecSpec, error) { var errs error addressesSplit := formData.splitInputList(formData.Addresses) addresses := make([]network.AddressSpecSpec, 0, len(addressesSplit)) for _, address := range addressesSplit { prefix, err := netip.ParsePrefix(address) if err != nil { errs = multierror.Append(errs, err) continue } ipFamily := nethelpers.FamilyInet4 if prefix.Addr().Is6() { ipFamily = nethelpers.FamilyInet6 } addresses = append(addresses, network.AddressSpecSpec{ Address: prefix, LinkName: linkName, Family: ipFamily, Scope: nethelpers.ScopeGlobal, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }) } if errs != nil { return nil, errs } return addresses, nil } func (formData *NetworkConfigFormData) buildRoutes(linkName string) ([]network.RouteSpecSpec, error) { gateway := strings.TrimSpace(formData.Gateway) gatewayAddr, err := netip.ParseAddr(gateway) if err != nil { return nil, fmt.Errorf("gateway: %w", err) } family := nethelpers.FamilyInet4 if gatewayAddr.Is6() { family = nethelpers.FamilyInet6 } return []network.RouteSpecSpec{ { Family: family, Gateway: gatewayAddr, OutLinkName: linkName, Table: nethelpers.TableMain, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolStatic, ConfigLayer: network.ConfigPlatform, }, }, nil } func (formData *NetworkConfigFormData) splitInputList(text string) []string { parts := strings.FieldsFunc(text, func(r rune) bool { return r == ',' || unicode.IsSpace(r) }) result := make([]string, 0, len(parts)) for _, part := range parts { trimmed := strings.TrimSpace(part) if trimmed != "" { result = append(result, part) } } return result } ================================================ FILE: internal/pkg/dashboard/formdata_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/dashboard" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestEmptyFormData(t *testing.T) { formData := dashboard.NetworkConfigFormData{} config, err := formData.ToPlatformNetworkConfig() assert.NoError(t, err) assert.Equal(t, runtime.PlatformNetworkConfig{}, *config) } func TestBaseDataZeroOut(t *testing.T) { base := runtime.PlatformNetworkConfig{ Addresses: []network.AddressSpecSpec{ { LinkName: "foobar", }, }, Links: []network.LinkSpecSpec{ { Name: "foobar", }, }, Routes: []network.RouteSpecSpec{ { OutLinkName: "foobar", }, }, Hostnames: []network.HostnameSpecSpec{ { Hostname: "foobar", }, }, Resolvers: []network.ResolverSpecSpec{ { DNSServers: []netip.Addr{ netip.MustParseAddr("1.2.3.4"), }, }, }, TimeServers: []network.TimeServerSpecSpec{ { NTPServers: []string{"foobar"}, }, }, Operators: []network.OperatorSpecSpec{ { LinkName: "foobar", }, }, ExternalIPs: []netip.Addr{ netip.MustParseAddr("2.3.4.5"), }, Metadata: &runtimeres.PlatformMetadataSpec{ Platform: "foobar", Spot: true, }, } formData := dashboard.NetworkConfigFormData{ Base: base, } config, err := formData.ToPlatformNetworkConfig() assert.NoError(t, err) // assert that the fields managed by the form are zeroed out assert.Empty(t, config.Addresses) assert.Empty(t, config.Links) assert.Empty(t, config.Routes) assert.Empty(t, config.Hostnames) assert.Empty(t, config.Resolvers) assert.Empty(t, config.TimeServers) assert.Empty(t, config.Operators) // assert that the fields not managed by the form are untouched assert.Equal(t, base.ExternalIPs, config.ExternalIPs) assert.Equal(t, base.Metadata, config.Metadata) } func TestFilledFormNoIface(t *testing.T) { formData := dashboard.NetworkConfigFormData{ Base: runtime.PlatformNetworkConfig{ Metadata: &runtimeres.PlatformMetadataSpec{ Platform: "foobar", }, }, Hostname: "foobar", DNSServers: "1.2.3.4 5.6.7.8", TimeServers: "a.example.com , b.example.com", } config, err := formData.ToPlatformNetworkConfig() assert.NoError(t, err) assert.Equal( t, runtime.PlatformNetworkConfig{ Hostnames: []network.HostnameSpecSpec{{ Hostname: "foobar", ConfigLayer: network.ConfigPlatform, }}, Resolvers: []network.ResolverSpecSpec{{ DNSServers: []netip.Addr{ netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("5.6.7.8"), }, ConfigLayer: network.ConfigPlatform, }}, TimeServers: []network.TimeServerSpecSpec{ { NTPServers: []string{"a.example.com", "b.example.com"}, ConfigLayer: network.ConfigPlatform, }, }, Metadata: &runtimeres.PlatformMetadataSpec{ Platform: "foobar", }, }, *config, ) } func TestFilledFormModeDHCP(t *testing.T) { formData := dashboard.NetworkConfigFormData{ Iface: "eth0", Mode: dashboard.ModeDHCP, } config, err := formData.ToPlatformNetworkConfig() assert.NoError(t, err) assert.Equal(t, runtime.PlatformNetworkConfig{ Links: []network.LinkSpecSpec{ { Name: formData.Iface, Logical: false, Up: true, Type: nethelpers.LinkEther, ConfigLayer: network.ConfigPlatform, }, }, Operators: []network.OperatorSpecSpec{ { Operator: network.OperatorDHCP4, LinkName: formData.Iface, RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: 1024, }, ConfigLayer: network.ConfigPlatform, }, }, }, *config) } func TestFilledFormModeStaticNoAddresses(t *testing.T) { formData := dashboard.NetworkConfigFormData{ Iface: "eth0", Mode: dashboard.ModeStatic, } _, err := formData.ToPlatformNetworkConfig() assert.ErrorContains(t, err, "no addresses specified") } func TestFilledFormModeStaticNoGateway(t *testing.T) { formData := dashboard.NetworkConfigFormData{ Iface: "eth0", Mode: dashboard.ModeStatic, Addresses: "1.2.3.4/24", } _, err := formData.ToPlatformNetworkConfig() assert.ErrorContains(t, err, "unable to parse") } func TestFilledFormModeStatic(t *testing.T) { formData := dashboard.NetworkConfigFormData{ Iface: "eth42", Mode: dashboard.ModeStatic, Addresses: "1.2.3.4/24 2.3.4.5/32", Gateway: "3.4.5.6", } config, err := formData.ToPlatformNetworkConfig() assert.NoError(t, err) assert.Equal(t, runtime.PlatformNetworkConfig{ Links: []network.LinkSpecSpec{ { Name: formData.Iface, Logical: false, Up: true, Type: nethelpers.LinkEther, ConfigLayer: network.ConfigPlatform, }, }, Addresses: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("1.2.3.4/24"), LinkName: formData.Iface, Family: nethelpers.FamilyInet4, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }, { Address: netip.MustParsePrefix("2.3.4.5/32"), LinkName: formData.Iface, Family: nethelpers.FamilyInet4, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigPlatform, }, }, Routes: []network.RouteSpecSpec{ { Family: nethelpers.FamilyInet4, Gateway: netip.MustParseAddr("3.4.5.6"), OutLinkName: "eth42", Table: nethelpers.TableMain, Scope: nethelpers.ScopeGlobal, Type: nethelpers.TypeUnicast, Protocol: nethelpers.ProtocolStatic, ConfigLayer: network.ConfigPlatform, }, }, }, *config) } ================================================ FILE: internal/pkg/dashboard/logdata/logdata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package logdata implements the types and the data sources for the data sourced from the Talos dmesg API. package logdata import ( "context" "errors" "fmt" "strings" "sync" "time" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/cmd/talosctl/pkg/talos/helpers" "github.com/siderolabs/talos/internal/pkg/dashboard/resolver" "github.com/siderolabs/talos/internal/pkg/dashboard/util" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" ) // Data is a log line from a node. type Data struct { Node string Log string Error string } // Source is a data source for Kernel (dmesg) logs. type Source struct { client *client.Client resolver resolver.Resolver logCtxCancel context.CancelFunc eg errgroup.Group once sync.Once LogCh chan Data } // NewSource initializes and returns Source data source. func NewSource(client *client.Client, resolver resolver.Resolver) *Source { return &Source{ client: client, resolver: resolver, LogCh: make(chan Data), } } // Start starts the data source. func (source *Source) Start(ctx context.Context) { source.once.Do(func() { source.start(ctx) }) } // Stop stops the data source. func (source *Source) Stop() error { source.logCtxCancel() return source.eg.Wait() } func (source *Source) start(ctx context.Context) { ctx, source.logCtxCancel = context.WithCancel(ctx) for _, nodeContext := range util.NodeContexts(ctx) { source.eg.Go(func() error { return source.tailNodeWithRetries(nodeContext.Ctx, nodeContext.Node) }) } } func (source *Source) tailNodeWithRetries(ctx context.Context, node string) error { for { readErr := source.readDmesg(ctx, node) if errors.Is(readErr, context.Canceled) || status.Code(readErr) == codes.Canceled { return nil } if readErr != nil { resolved := source.resolver.Resolve(node) source.LogCh <- Data{Node: resolved, Error: readErr.Error()} } // back off a bit before retrying sleepWithContext(ctx, 30*time.Second) } } func (source *Source) readDmesg(ctx context.Context, node string) error { dmesgStream, err := source.client.Dmesg(ctx, true, false) if err != nil { return fmt.Errorf("dashboard: error opening dmesg stream: %w", err) } readErr := helpers.ReadGRPCStream(dmesgStream, func(data *common.Data, _ string, _ bool) error { if len(data.Bytes) == 0 { return nil } line := strings.TrimSpace(string(data.Bytes)) if line == "" { return nil } select { case <-ctx.Done(): return ctx.Err() case source.LogCh <- Data{Node: node, Log: line}: } return nil }) if readErr != nil { return fmt.Errorf("error reading dmesg stream: %w", readErr) } return nil } func sleepWithContext(ctx context.Context, d time.Duration) { timer := time.NewTimer(d) select { case <-ctx.Done(): if !timer.Stop() { <-timer.C } case <-timer.C: } } ================================================ FILE: internal/pkg/dashboard/monitor.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/components" ) // MonitorGrid represents the monitoring grid with a process table and various metrics. type MonitorGrid struct { tview.Grid app *tview.Application apiDataListeners []APIDataListener processTable *components.ProcessTable } // NewMonitorGrid initializes MonitorGrid. func NewMonitorGrid(app *tview.Application) *MonitorGrid { widget := &MonitorGrid{ app: app, Grid: *tview.NewGrid(), } widget.SetRows(7, -1, -2).SetColumns(0) infoGrid := tview.NewGrid().SetRows(0).SetColumns(-1, -2, -1, -1, -2) sysGauges := components.NewSystemGauges() cpuInfo := components.NewCPUInfo() loadAvgInfo := components.NewLoadAvgInfo() procsInfo := components.NewProcsInfo() memInfo := components.NewMemInfo() infoGrid.AddItem(sysGauges, 0, 0, 1, 1, 0, 0, false) infoGrid.AddItem(cpuInfo, 0, 1, 1, 1, 0, 0, false) infoGrid.AddItem(loadAvgInfo, 0, 2, 1, 1, 0, 0, false) infoGrid.AddItem(procsInfo, 0, 3, 1, 1, 0, 0, false) infoGrid.AddItem(memInfo, 0, 4, 1, 1, 0, 0, false) graphGrid := tview.NewGrid().SetRows(0).SetColumns(0, 0, 0) cpuGraph := components.NewCPUGraph() memGraph := components.NewMemGraph() loadAvgGraph := components.NewLoadAvgGraph() graphGrid.AddItem(cpuGraph, 0, 0, 1, 1, 0, 0, false) graphGrid.AddItem(memGraph, 0, 1, 1, 1, 0, 0, false) graphGrid.AddItem(loadAvgGraph, 0, 2, 1, 1, 0, 0, false) bottomGrid := tview.NewGrid().SetRows(0, 0).SetColumns(-1, -3) netSparkline := components.NewNetSparkline() diskSparkline := components.NewDiskSparkline() widget.processTable = components.NewProcessTable() bottomGrid.AddItem(netSparkline, 0, 0, 1, 1, 0, 0, false) bottomGrid.AddItem(diskSparkline, 1, 0, 1, 1, 0, 0, false) bottomGrid.AddItem(widget.processTable, 0, 1, 2, 1, 0, 0, false) widget.AddItem(infoGrid, 0, 0, 1, 1, 0, 0, false) widget.AddItem(graphGrid, 1, 0, 1, 1, 0, 0, false) widget.AddItem(bottomGrid, 2, 0, 1, 1, 0, 0, false) widget.apiDataListeners = []APIDataListener{ sysGauges, cpuInfo, loadAvgInfo, procsInfo, memInfo, cpuGraph, memGraph, loadAvgGraph, netSparkline, diskSparkline, widget.processTable, } return widget } // OnAPIDataChange implements the APIDataListener interface. func (widget *MonitorGrid) OnAPIDataChange(node string, data *apidata.Data) { for _, dataWidget := range widget.apiDataListeners { dataWidget.OnAPIDataChange(node, data) } } // OnScreenSelect implements the screenSelectListener interface. func (widget *MonitorGrid) onScreenSelect(active bool) { if active { widget.processTable.ScrollToBeginning() widget.processTable.Select(1, 0) widget.app.SetFocus(widget.processTable) } } ================================================ FILE: internal/pkg/dashboard/networkconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "context" "fmt" "slices" "strings" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-pointer" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/network" runtimeres "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) const ( formItemHostname = "Hostname" formItemDNSServers = "DNS Servers" formItemTimeServers = "Time Servers" formItemInterface = "Interface" formItemMode = "Mode" formItemAddresses = "Addresses" formItemGateway = "Gateway" ) type networkConfigData struct { existingConfig *runtime.PlatformNetworkConfig newConfig *runtime.PlatformNetworkConfig newConfigError error linkSet map[string]struct{} } // NetworkConfigGrid represents the network configuration widget. type NetworkConfigGrid struct { tview.Grid dashboard *Dashboard configForm *tview.Form hostnameField *tview.InputField dnsServersField *tview.InputField timeServersField *tview.InputField interfaceDropdown *tview.DropDown modeDropdown *tview.DropDown addressesField *tview.InputField gatewayField *tview.InputField infoView *tview.TextView existingConfigView *tview.TextView newConfigView *tview.TextView selectedNode string nodeMap map[string]*networkConfigData } // NewNetworkConfigGrid initializes NetworkConfigGrid. func NewNetworkConfigGrid(ctx context.Context, dashboard *Dashboard) *NetworkConfigGrid { widget := &NetworkConfigGrid{ Grid: *tview.NewGrid(), configForm: tview.NewForm(), infoView: tview.NewTextView(), existingConfigView: tview.NewTextView(), newConfigView: tview.NewTextView(), nodeMap: make(map[string]*networkConfigData), dashboard: dashboard, } widget.configForm.SetBorder(true).SetTitle("Configure (Ctrl+Q)") widget.SetRows(0, 3).SetColumns(0, 0, 0) widget.infoView. SetDynamicColors(true). SetScrollable(true). SetWrap(true) widget.existingConfigView. SetDynamicColors(true). SetScrollable(true). SetBorderPadding(0, 0, 1, 0). SetBorder(true). SetTitle("Existing Config (Ctrl+W)") widget.newConfigView. SetDynamicColors(true). SetScrollable(true). SetBorderPadding(0, 0, 1, 0). SetBorder(true). SetTitle("New Config (Ctrl+E)") widget.AddItem(widget.configForm, 0, 0, 1, 1, 0, 0, false) widget.AddItem(widget.infoView, 1, 0, 1, 1, 0, 0, false) widget.AddItem(widget.existingConfigView, 0, 1, 2, 1, 0, 0, false) widget.AddItem(widget.newConfigView, 0, 2, 2, 1, 0, 0, false) widget.hostnameField = tview.NewInputField().SetLabel(formItemHostname) widget.hostnameField.SetBlurFunc(widget.formEdited) widget.dnsServersField = tview.NewInputField().SetLabel(formItemDNSServers) widget.dnsServersField.SetBlurFunc(widget.formEdited) widget.timeServersField = tview.NewInputField().SetLabel(formItemTimeServers) widget.timeServersField.SetBlurFunc(widget.formEdited) widget.interfaceDropdown = tview.NewDropDown().SetLabel(formItemInterface) widget.interfaceDropdown.SetBlurFunc(widget.formEdited) widget.interfaceDropdown.SetOptions([]string{interfaceNone}, func(_ string, _ int) { widget.formEdited() }) widget.interfaceDropdown.SetListStyles( tcell.StyleDefault.Foreground(tview.Styles.PrimitiveBackgroundColor).Background(tview.Styles.MoreContrastBackgroundColor), tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tview.Styles.PrimaryTextColor), ) widget.modeDropdown = tview.NewDropDown().SetLabel(formItemMode) widget.modeDropdown.SetBlurFunc(widget.formEdited) widget.modeDropdown.SetOptions([]string{ModeDHCP, ModeStatic}, func(_ string, _ int) { widget.formEdited() }) widget.modeDropdown.SetListStyles( tcell.StyleDefault.Foreground(tview.Styles.PrimitiveBackgroundColor).Background(tview.Styles.MoreContrastBackgroundColor), tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tview.Styles.PrimaryTextColor), ) widget.addressesField = tview.NewInputField().SetLabel(formItemAddresses) widget.addressesField.SetBlurFunc(widget.formEdited) widget.gatewayField = tview.NewInputField().SetLabel(formItemGateway) widget.gatewayField.SetBlurFunc(widget.formEdited) widget.configForm.AddFormItem(widget.hostnameField) widget.configForm.AddFormItem(widget.dnsServersField) widget.configForm.AddFormItem(widget.timeServersField) widget.configForm.AddFormItem(widget.interfaceDropdown) widget.configForm.AddFormItem(widget.modeDropdown) widget.configForm.AddFormItem(widget.addressesField) widget.configForm.AddFormItem(widget.gatewayField) widget.configForm.AddButton("Save", func() { widget.save(ctx) }) saveButton := widget.configForm.GetButton(0) saveButton.SetBlurFunc(widget.formEdited) inputCapture := func(event *tcell.EventKey) *tcell.EventKey { if widget.handleFocusSwitch(event) { return nil } return event } widget.configForm.SetInputCapture(inputCapture) widget.existingConfigView.SetInputCapture(inputCapture) widget.newConfigView.SetInputCapture(inputCapture) widget.interfaceDropdown.SetCurrentOption(0) widget.modeDropdown.SetCurrentOption(0) return widget } // OnNodeSelect implements the NodeSelectListener interface. func (widget *NetworkConfigGrid) OnNodeSelect(node string) { if node != widget.selectedNode { widget.selectedNode = node widget.clearForm() widget.formEdited() widget.redraw() } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *NetworkConfigGrid) OnResourceDataChange(data resourcedata.Data) { widget.updateNodeData(data) if data.Node == widget.selectedNode { widget.redraw() } } //nolint:gocyclo func (widget *NetworkConfigGrid) formEdited() { widget.infoView.SetText("") resetInputField := func(field *tview.InputField) { // avoid triggering another form edit if there is nothing to change if field.GetText() != "" { field.SetText("") } } resetDropdown := func(dropdown *tview.DropDown) { // avoid triggering another form edit if there is nothing to change if currentIndex, _ := dropdown.GetCurrentOption(); currentIndex != 0 { dropdown.SetCurrentOption(0) } } _, currentInterface := widget.interfaceDropdown.GetCurrentOption() _, currentMode := widget.modeDropdown.GetCurrentOption() ifaceSelected := currentInterface != "" && currentInterface != interfaceNone if ifaceSelected { if itemIndex := widget.configForm.GetFormItemIndex(formItemMode); itemIndex == -1 { widget.configForm.AddFormItem(widget.modeDropdown) } switch currentMode { case ModeDHCP: resetInputField(widget.addressesField) resetInputField(widget.gatewayField) if itemIndex := widget.configForm.GetFormItemIndex(formItemAddresses); itemIndex != -1 { widget.configForm.RemoveFormItem(itemIndex) } if itemIndex := widget.configForm.GetFormItemIndex(formItemGateway); itemIndex != -1 { widget.configForm.RemoveFormItem(itemIndex) } case ModeStatic: if itemIndex := widget.configForm.GetFormItemIndex(formItemAddresses); itemIndex == -1 { widget.configForm.AddFormItem(widget.addressesField) } if itemIndex := widget.configForm.GetFormItemIndex(formItemGateway); itemIndex == -1 { widget.configForm.AddFormItem(widget.gatewayField) } } } else { resetDropdown(widget.modeDropdown) resetInputField(widget.addressesField) resetInputField(widget.gatewayField) if itemIndex := widget.configForm.GetFormItemIndex(formItemMode); itemIndex != -1 { widget.configForm.RemoveFormItem(itemIndex) } if itemIndex := widget.configForm.GetFormItemIndex(formItemAddresses); itemIndex != -1 { widget.configForm.RemoveFormItem(itemIndex) } if itemIndex := widget.configForm.GetFormItemIndex(formItemGateway); itemIndex != -1 { widget.configForm.RemoveFormItem(itemIndex) } } data := widget.getOrCreateNodeData(widget.selectedNode) formData := NetworkConfigFormData{ Base: pointer.SafeDeref(data.existingConfig), Hostname: widget.hostnameField.GetText(), DNSServers: widget.dnsServersField.GetText(), TimeServers: widget.timeServersField.GetText(), Iface: currentInterface, Mode: currentMode, Addresses: widget.addressesField.GetText(), Gateway: widget.gatewayField.GetText(), } config, err := formData.ToPlatformNetworkConfig() if err != nil { data.newConfig = nil data.newConfigError = err } else { data.newConfig = config data.newConfigError = nil } widget.redraw() } func (widget *NetworkConfigGrid) redraw() { data := widget.getOrCreateNodeData(widget.selectedNode) if data.existingConfig != nil { var buf strings.Builder encoder := yaml.NewEncoder(&buf) encoder.SetIndent(2) err := encoder.Encode(data.existingConfig) if err != nil { widget.existingConfigView.SetText(fmt.Sprintf("[red]error: %v[-]", err)) } widget.existingConfigView.SetText(fmt.Sprintf("[lightblue]%s[-]", tview.Escape(buf.String()))) } else { widget.existingConfigView.SetText("[gray]No Config[-]") } if data.newConfigError != nil { widget.newConfigView.SetText(fmt.Sprintf("[red]error: %v[-]", data.newConfigError)) } else if data.newConfig != nil { var buf strings.Builder encoder := yaml.NewEncoder(&buf) encoder.SetIndent(2) err := encoder.Encode(data.newConfig) if err != nil { widget.newConfigView.SetText(fmt.Sprintf("[red]error: %v[-]", err)) } widget.newConfigView.SetText(fmt.Sprintf("[green]%s[-]", tview.Escape(buf.String()))) } } func (widget *NetworkConfigGrid) clearForm() { widget.hostnameField.SetText("") widget.dnsServersField.SetText("") widget.timeServersField.SetText("") widget.interfaceDropdown.SetCurrentOption(0) widget.modeDropdown.SetCurrentOption(0) widget.addressesField.SetText("") widget.gatewayField.SetText("") widget.infoView.SetText("") widget.configForm.SetFocus(0) widget.formEdited() } func (widget *NetworkConfigGrid) updateNodeData(data resourcedata.Data) { nodeData := widget.getOrCreateNodeData(data.Node) switch res := data.Resource.(type) { case *network.LinkStatus: if data.Deleted { delete(nodeData.linkSet, res.Metadata().ID()) } else { if !res.TypedSpec().Physical() { return } nodeData.linkSet[res.Metadata().ID()] = struct{}{} } links := maps.Keys(nodeData.linkSet) slices.Sort(links) allLinks := append([]string{interfaceNone}, links...) widget.interfaceDropdown.SetOptions(allLinks, func(_ string, _ int) { widget.formEdited() }) case *runtimeres.MetaKey: if res.Metadata().ID() == runtimeres.MetaKeyTagToID(meta.MetalNetworkPlatformConfig) { if data.Deleted { nodeData.existingConfig = nil } else { cfg := runtime.PlatformNetworkConfig{} if err := yaml.Unmarshal([]byte(res.TypedSpec().Value), &cfg); err != nil { widget.existingConfigView.SetText(fmt.Sprintf("[red]error: %v[-]", err)) return } nodeData.existingConfig = &cfg widget.formEdited() } } } } func (widget *NetworkConfigGrid) getOrCreateNodeData(node string) *networkConfigData { nodeData, ok := widget.nodeMap[node] if !ok { nodeData = &networkConfigData{ linkSet: make(map[string]struct{}), } widget.nodeMap[node] = nodeData } return nodeData } // OnScreenSelect implements the screenSelectListener interface. func (widget *NetworkConfigGrid) onScreenSelect(active bool) { if active { widget.dashboard.app.SetFocus(widget.configForm) } } func (widget *NetworkConfigGrid) save(ctx context.Context) { nodeData := widget.getOrCreateNodeData(widget.selectedNode) if nodeData.newConfig == nil { widget.infoView.SetText("[red]Error: nothing to save[-]") return } if nodeData.newConfigError != nil { widget.infoView.SetText("[red]Error: cannot save, fix the errors and try again[-]") return } configBytes, err := yaml.Marshal(nodeData.newConfig) if err != nil { widget.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err)) return } ctx = nodeContext(ctx, widget.selectedNode) if err = widget.dashboard.cli.MetaWrite(ctx, meta.MetalNetworkPlatformConfig, configBytes); err != nil { widget.infoView.SetText(fmt.Sprintf("[red]Error: %v[-]", err)) return } widget.infoView.SetText("[green]Network config saved successfully[-]") widget.clearForm() widget.dashboard.selectScreen(ScreenSummary) } func (widget *NetworkConfigGrid) handleFocusSwitch(event *tcell.EventKey) bool { switch event.Key() { //nolint:exhaustive case tcell.KeyCtrlQ: widget.dashboard.app.SetFocus(widget.configForm) return true case tcell.KeyCtrlW: widget.dashboard.app.SetFocus(widget.existingConfigView) return true case tcell.KeyCtrlE: widget.dashboard.app.SetFocus(widget.newConfigView) return true default: return false } } ================================================ FILE: internal/pkg/dashboard/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "time" ) type options struct { interval time.Duration allowExitKeys bool screens []Screen } func defaultOptions() *options { return &options{ interval: 5 * time.Second, allowExitKeys: true, screens: []Screen{ ScreenSummary, ScreenMonitor, ScreenNetworkConfig, }, } } // Option is a functional option for Dashboard. type Option func(*options) // WithInterval sets the interval for the dashboard. func WithInterval(interval time.Duration) Option { return func(o *options) { o.interval = interval } } // WithAllowExitKeys sets whether the dashboard should allow exit keys (Ctrl + C). func WithAllowExitKeys(allowExitKeys bool) Option { return func(o *options) { o.allowExitKeys = allowExitKeys } } // WithScreens sets the screens to display. // The order is preserved. func WithScreens(screens ...Screen) Option { return func(o *options) { o.screens = screens } } ================================================ FILE: internal/pkg/dashboard/resolver/resolver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package resolver resolves the node names. package resolver // Resolver resolves the node names. type Resolver struct { db map[string]string } // New creates a new Resolver. func New(db map[string]string) Resolver { return Resolver{ db: db, } } // Resolve attempts to resolve the node name. func (n *Resolver) Resolve(node string) string { if resolved, ok := n.db[node]; ok { return resolved } return node } ================================================ FILE: internal/pkg/dashboard/resourcedata/resourcedata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package resourcedata implements the types and the data sources for the data sourced from the Talos resource API (COSI). package resourcedata import ( "context" "errors" "fmt" "sync" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/channel" "golang.org/x/sync/errgroup" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/internal/pkg/dashboard/util" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/config" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // Data contains a resource, whether it is deleted and the node it came from. type Data struct { Node string Resource resource.Resource Deleted bool } // Source is the data source for the Talos resources. type Source struct { ctxCancel context.CancelFunc eg errgroup.Group once sync.Once COSI state.State ch chan Data NodeResourceCh <-chan Data } // Run starts the data source. func (source *Source) Run(ctx context.Context) { source.once.Do(func() { source.run(ctx) }) } // Stop stops the data source. func (source *Source) Stop() error { source.ctxCancel() return source.eg.Wait() } func (source *Source) run(ctx context.Context) { ctx, source.ctxCancel = context.WithCancel(ctx) source.ch = make(chan Data) source.NodeResourceCh = source.ch for _, nodeContext := range util.NodeContexts(ctx) { source.eg.Go(func() error { source.runResourceWatchWithRetries(nodeContext.Ctx, nodeContext.Node) return nil }) } } func (source *Source) runResourceWatchWithRetries(ctx context.Context, node string) { for { if err := source.runResourceWatch(ctx, node); errors.Is(err, context.Canceled) { return } // wait for a second before the next retry timer := time.NewTimer(1 * time.Second) select { case <-ctx.Done(): timer.Stop() return case <-timer.C: } } } //nolint:gocyclo func (source *Source) runResourceWatch(ctx context.Context, node string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() eventCh := make(chan state.Event) watchResources := []resource.Pointer{ runtime.NewMachineStatus().Metadata(), runtime.NewSecurityStateSpec(v1alpha1.NamespaceName).Metadata(), config.NewMachineType().Metadata(), k8s.NewKubeletSpec(k8s.NamespaceName, k8s.KubeletID).Metadata(), network.NewResolverStatus(network.NamespaceName, network.ResolverID).Metadata(), network.NewTimeServerStatus(network.NamespaceName, network.TimeServerID).Metadata(), hardware.NewSystemInformation(hardware.SystemInformationID).Metadata(), cluster.NewInfo().Metadata(), network.NewStatus(network.NamespaceName, network.StatusID).Metadata(), network.NewHostnameStatus(network.NamespaceName, network.HostnameID).Metadata(), } for _, ptr := range watchResources { err := source.COSI.Watch(ctx, ptr, eventCh) if err != nil && client.StatusCode(err) != codes.PermissionDenied { return err } } watchKindResources := []resource.Pointer{ runtime.NewMetaKey(runtime.NamespaceName, "").Metadata(), k8s.NewStaticPodStatus(k8s.NamespaceName, "").Metadata(), network.NewRouteStatus(network.NamespaceName, "").Metadata(), network.NewLinkStatus(network.NamespaceName, "").Metadata(), cluster.NewMember(cluster.NamespaceName, "").Metadata(), network.NewNodeAddress(network.NamespaceName, "").Metadata(), siderolink.NewStatus().Metadata(), runtime.NewDiagnostic(runtime.NamespaceName, "").Metadata(), } for _, ptr := range watchKindResources { err := source.COSI.WatchKind(ctx, ptr, eventCh, state.WithBootstrapContents(true)) if err != nil && client.StatusCode(err) != codes.PermissionDenied { return err } } for { select { case <-ctx.Done(): return ctx.Err() case event := <-eventCh: switch event.Type { case state.Errored: return fmt.Errorf("watch failed: %w", event.Error) case state.Bootstrapped, state.Noop: // ignored case state.Created, state.Updated: if !channel.SendWithContext(ctx, source.ch, Data{ Node: node, Resource: event.Resource, }) { return ctx.Err() } case state.Destroyed: if !channel.SendWithContext(ctx, source.ch, Data{ Node: node, Resource: event.Resource, Deleted: true, }) { return ctx.Err() } } } } } ================================================ FILE: internal/pkg/dashboard/summary.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dashboard import ( "github.com/rivo/tview" "github.com/siderolabs/talos/internal/pkg/dashboard/apidata" "github.com/siderolabs/talos/internal/pkg/dashboard/components" "github.com/siderolabs/talos/internal/pkg/dashboard/resourcedata" ) // SummaryGrid represents the summary grid with the basic node information and the logs. type SummaryGrid struct { tview.Grid app *tview.Application apiDataListeners []APIDataListener resourceListeners []ResourceDataListener nodeSelectListeners []NodeSelectListener active bool node string logViewers map[string]*components.LogViewer diagnostics *components.Diagnostics diagnosticsVisible bool } const summaryTopFixedRows = 7 // NewSummaryGrid initializes SummaryGrid. func NewSummaryGrid(app *tview.Application) *SummaryGrid { widget := &SummaryGrid{ app: app, Grid: *tview.NewGrid(), logViewers: make(map[string]*components.LogViewer), } widget.SetRows(summaryTopFixedRows, 0).SetColumns(-3, -2, -3) talosInfo := components.NewTalosInfo() widget.AddItem(talosInfo, 0, 0, 1, 1, 0, 0, false) kubernetesInfo := components.NewKubernetesInfo() widget.AddItem(kubernetesInfo, 0, 1, 1, 1, 0, 0, false) networkInfo := components.NewNetworkInfo() widget.AddItem(networkInfo, 0, 2, 1, 1, 0, 0, false) widget.diagnostics = components.NewDiagnostics() widget.apiDataListeners = []APIDataListener{ kubernetesInfo, } widget.resourceListeners = []ResourceDataListener{ talosInfo, kubernetesInfo, networkInfo, widget.diagnostics, } widget.nodeSelectListeners = []NodeSelectListener{ talosInfo, kubernetesInfo, networkInfo, widget.diagnostics, } return widget } // OnNodeSelect implements the NodeSelectListener interface. func (widget *SummaryGrid) OnNodeSelect(node string) { widget.node = node widget.updateLogViewer() for _, nodeSelectListener := range widget.nodeSelectListeners { nodeSelectListener.OnNodeSelect(node) } widget.updateDiagnostics() } // OnAPIDataChange implements the APIDataListener interface. func (widget *SummaryGrid) OnAPIDataChange(node string, data *apidata.Data) { for _, dataWidget := range widget.apiDataListeners { dataWidget.OnAPIDataChange(node, data) } } // OnResourceDataChange implements the ResourceDataListener interface. func (widget *SummaryGrid) OnResourceDataChange(nodeResource resourcedata.Data) { for _, resourceListener := range widget.resourceListeners { resourceListener.OnResourceDataChange(nodeResource) } widget.updateDiagnostics() } // OnLogDataChange implements the LogDataListener interface. func (widget *SummaryGrid) OnLogDataChange(node, logLine, logError string) { widget.logViewer(node).WriteLog(logLine, logError) } func (widget *SummaryGrid) updateDiagnostics() { height := widget.diagnostics.GetCurrentHeight() switch { case height == 0 && widget.diagnosticsVisible: widget.RemoveItem(widget.diagnostics) widget.SetRows(summaryTopFixedRows, 0) widget.diagnosticsVisible = false case height > 0 && !widget.diagnosticsVisible: widget.SetRows(summaryTopFixedRows, 0, height) widget.AddItem(widget.diagnostics, 2, 0, 1, 3, 0, 0, false) widget.diagnosticsVisible = true case height > 0: widget.SetRows(summaryTopFixedRows, 0, height) } } func (widget *SummaryGrid) updateLogViewer() { if !widget.active { return } widget.logViewer(widget.node) for currNode, logViewer := range widget.logViewers { if currNode == widget.node { widget.AddItem(logViewer, 1, 0, 1, 3, 0, 0, false) widget.app.SetFocus(logViewer) return } widget.RemoveItem(logViewer) } } func (widget *SummaryGrid) logViewer(node string) *components.LogViewer { logViewer, ok := widget.logViewers[node] if !ok { logViewer = components.NewLogViewer() widget.logViewers[node] = logViewer } return logViewer } // OnScreenSelect implements the screenSelectListener interface. func (widget *SummaryGrid) onScreenSelect(active bool) { widget.active = active widget.updateLogViewer() } ================================================ FILE: internal/pkg/dashboard/util/util.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package util provides utility functions for the dashboard. // //nolint:revive package util import ( "context" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/client" ) // NodeContext contains the context.Context for a single node and the node name. type NodeContext struct { Ctx context.Context //nolint:containedctx Node string } // NodeContexts returns a list of NodeContexts from the given context. // // It extracts the node names from the outgoing GRPC context metadata. // If the node name is not present in the metadata, context will be returned as-is with an empty node name. func NodeContexts(ctx context.Context) []NodeContext { md, mdOk := metadata.FromOutgoingContext(ctx) if !mdOk { return []NodeContext{{Ctx: ctx}} } nodeVal := md.Get("node") if len(nodeVal) > 0 { return []NodeContext{{Ctx: ctx, Node: nodeVal[0]}} } nodesVal := md.Get("nodes") if len(nodesVal) == 0 { return []NodeContext{{Ctx: ctx}} } nodeContexts := make([]NodeContext, 0, len(nodesVal)) for _, node := range nodesVal { nodeContexts = append(nodeContexts, NodeContext{Ctx: client.WithNode(ctx, node), Node: node}) } return nodeContexts } ================================================ FILE: internal/pkg/discovery/registry/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry import ( "context" "encoding/json" "errors" "fmt" "net/netip" "strconv" "strings" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/client-go/informers" informersv1 "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/tools/cache" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // Kubernetes defines a Kubernetes-based node discoverer. type Kubernetes struct { client *kubernetes.Client nodes informersv1.NodeInformer } // NewKubernetes creates new Kubernetes registry. func NewKubernetes(client *kubernetes.Client) *Kubernetes { return &Kubernetes{ client: client, } } // AnnotationsFromAffiliate generates Kubernetes Node annotations from the Affiliate spec. func AnnotationsFromAffiliate(affiliate *cluster.Affiliate) map[string]string { var kubeSpanAddress string if !value.IsZero(affiliate.TypedSpec().KubeSpan.Address) { kubeSpanAddress = affiliate.TypedSpec().KubeSpan.Address.String() } var apiServerPort string if affiliate.TypedSpec().ControlPlane != nil { apiServerPort = strconv.Itoa(affiliate.TypedSpec().ControlPlane.APIServerPort) } return map[string]string{ constants.ClusterNodeIDAnnotation: affiliate.Metadata().ID(), constants.NetworkSelfIPsAnnotation: ipsToString(affiliate.TypedSpec().Addresses), constants.NetworkAPIServerPortAnnotation: apiServerPort, constants.KubeSpanIPAnnotation: kubeSpanAddress, constants.KubeSpanPublicKeyAnnotation: affiliate.TypedSpec().KubeSpan.PublicKey, constants.KubeSpanAssignedPrefixesAnnotation: ipPrefixesToString(affiliate.TypedSpec().KubeSpan.AdditionalAddresses), constants.KubeSpanKnownEndpointsAnnotation: ipPortsToString(affiliate.TypedSpec().KubeSpan.Endpoints), constants.KubeSpanExcludeAdvertisedNetworksAnnotation: ipPrefixesToString(affiliate.TypedSpec().KubeSpan.ExcludeAdvertisedNetworks), } } // AffiliateFromNode converts Kubernetes Node resource to Affiliate. // // If the Node resource doesn't have cluster discovery annotations, nil is returned. // //nolint:gocyclo func AffiliateFromNode(node *v1.Node) *cluster.AffiliateSpec { nodeID, ok := node.Annotations[constants.ClusterNodeIDAnnotation] if !ok { // skip the node, not part of the cluster discovery process return nil } affiliate := &cluster.AffiliateSpec{ NodeID: nodeID, } if selfIPs, ok := node.Annotations[constants.NetworkSelfIPsAnnotation]; ok { affiliate.Addresses = parseIPs(selfIPs) } // Nodename and hostname are pulled from native Kubernetes fields. affiliate.Nodename = node.Name for _, addr := range node.Status.Addresses { if addr.Type == v1.NodeHostName { affiliate.Hostname = addr.Address break } } // Machine type is derived from node roles. _, labelControlPlane := node.Labels[constants.LabelNodeRoleControlPlane] affiliate.MachineType = machine.TypeWorker if labelControlPlane { affiliate.MachineType = machine.TypeControlPlane } affiliate.OperatingSystem = node.Status.NodeInfo.OSImage // Every other field is pulled from node annotations. if publicKey, ok := node.Annotations[constants.KubeSpanPublicKeyAnnotation]; ok { affiliate.KubeSpan.PublicKey = publicKey } if ksIP, ok := node.Annotations[constants.KubeSpanIPAnnotation]; ok { affiliate.KubeSpan.Address, _ = netip.ParseAddr(ksIP) //nolint:errcheck } if additionalAddresses, ok := node.Annotations[constants.KubeSpanAssignedPrefixesAnnotation]; ok { affiliate.KubeSpan.AdditionalAddresses = parseIPPrefixes(additionalAddresses) } if endpoints, ok := node.Annotations[constants.KubeSpanKnownEndpointsAnnotation]; ok { affiliate.KubeSpan.Endpoints = parseIPPorts(endpoints) } if apiServerPort, ok := node.Annotations[constants.NetworkAPIServerPortAnnotation]; ok { if port, err := strconv.Atoi(apiServerPort); err == nil { affiliate.ControlPlane = &cluster.ControlPlane{ APIServerPort: port, } } } if advertisedFilters, ok := node.Annotations[constants.KubeSpanExcludeAdvertisedNetworksAnnotation]; ok { affiliate.KubeSpan.ExcludeAdvertisedNetworks = parseIPPrefixes(advertisedFilters) } return affiliate } func ipsToString(in []netip.Addr) string { return strings.Join(xslices.Map(in, netip.Addr.String), ",") } func ipPrefixesToString(in []netip.Prefix) string { return strings.Join(xslices.Map(in, netip.Prefix.String), ",") } func ipPortsToString(in []netip.AddrPort) string { return strings.Join(xslices.Map(in, netip.AddrPort.String), ",") } func parseIPs(in string) []netip.Addr { var result []netip.Addr for item := range strings.SplitSeq(in, ",") { if ip, err := netip.ParseAddr(item); err == nil { result = append(result, ip) } } return result } func parseIPPrefixes(in string) []netip.Prefix { var result []netip.Prefix for item := range strings.SplitSeq(in, ",") { if ip, err := netip.ParsePrefix(item); err == nil { result = append(result, ip) } } return result } func parseIPPorts(in string) []netip.AddrPort { var result []netip.AddrPort for item := range strings.SplitSeq(in, ",") { if ip, err := netip.ParseAddrPort(item); err == nil { result = append(result, ip) } } return result } // Push updates Kubernetes Node resource to track Affiliate state. func (r *Kubernetes) Push(ctx context.Context, affiliate *cluster.Affiliate) error { node, err := r.client.CoreV1().Nodes().Get(ctx, affiliate.TypedSpec().Nodename, metav1.GetOptions{}) if err != nil { return fmt.Errorf("failed to get node %q: %w", affiliate.TypedSpec().Nodename, err) } oldData, err := json.Marshal(node) if err != nil { return fmt.Errorf("failed to marshal existing node data: %w", err) } for key, value := range AnnotationsFromAffiliate(affiliate) { if value == "" { delete(node.Annotations, key) } else { node.Annotations[key] = value } } newData, err := json.Marshal(node) if err != nil { return fmt.Errorf("failed to marshal new data for node %q: %w", node.Name, err) } patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Node{}) if err != nil { return fmt.Errorf("failed to create two way merge patch: %w", err) } if _, err := r.client.CoreV1().Nodes().Patch(ctx, affiliate.TypedSpec().Nodename, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil { if apierrors.IsConflict(err) { return fmt.Errorf("unable to update node %q due to conflict: %w", affiliate.TypedSpec().Nodename, err) } return fmt.Errorf("error patching node %q: %w", affiliate.TypedSpec().Nodename, err) } return nil } // List returns list of Affiliates coming from the registry. // // Watch should be called first for the List to return data. func (r *Kubernetes) List(localNodeName string) ([]*cluster.AffiliateSpec, error) { if r.nodes == nil { return nil, errors.New("List() called without Watch() first") } nodes, err := r.nodes.Lister().List(labels.Everything()) if err != nil { return nil, err } result := make([]*cluster.AffiliateSpec, 0, len(nodes)) for _, node := range nodes { // skip this node, no need to pull itself if node.Name == localNodeName { continue } affiliate := AffiliateFromNode(node) if affiliate == nil { continue } result = append(result, affiliate) } return result, nil } // Watch starts watching Node state and notifies on updates via notify channel. func (r *Kubernetes) Watch(ctx context.Context, logger *zap.Logger) (<-chan struct{}, func(), error) { informerFactory := informers.NewSharedInformerFactory(r.client.Clientset, constants.KubernetesInformerDefaultResyncPeriod) notifyCh := make(chan struct{}, 1) notify := func(_ any) { select { case notifyCh <- struct{}{}: default: } } r.nodes = informerFactory.Core().V1().Nodes() if err := r.nodes.Informer().SetWatchErrorHandler(func(r *cache.Reflector, err error) { logger.Error("kubernetes registry node watch error", zap.Error(err)) }); err != nil { return nil, nil, fmt.Errorf("failed to set watch error handler: %w", err) } if _, err := r.nodes.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: notify, DeleteFunc: notify, UpdateFunc: func(_, _ any) { notify(nil) }, }); err != nil { return nil, nil, fmt.Errorf("failed to add event handler: %w", err) } informerFactory.Start(ctx.Done()) return notifyCh, informerFactory.Shutdown, nil } ================================================ FILE: internal/pkg/discovery/registry/kubernetes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/internal/pkg/discovery/registry" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func TestAnnotationsFromAffiliate(t *testing.T) { for _, tt := range []struct { name string affiliate cluster.AffiliateSpec expected map[string]string }{ { name: "zero", expected: map[string]string{ "cluster.talos.dev/node-id": "", "networking.talos.dev/api-server-port": "", "networking.talos.dev/assigned-prefixes": "", "networking.talos.dev/kubespan-endpoints": "", "networking.talos.dev/kubespan-ip": "", "networking.talos.dev/kubespan-public-key": "", "networking.talos.dev/kubespan-exclude-advertised-networks": "", "networking.talos.dev/self-ips": "", }, }, { name: "mixed", affiliate: cluster.AffiliateSpec{ NodeID: "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}, }, }, expected: map[string]string{ "cluster.talos.dev/node-id": "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", "networking.talos.dev/api-server-port": "", "networking.talos.dev/assigned-prefixes": "10.244.3.1/24", "networking.talos.dev/kubespan-endpoints": "10.0.0.2:51820,192.168.3.4:51820", "networking.talos.dev/kubespan-ip": "fd50:8d60:4238:6302:f857:23ff:fe21:d1e0", "networking.talos.dev/kubespan-public-key": "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", "networking.talos.dev/kubespan-exclude-advertised-networks": "0.0.0.0/0,::/0", "networking.talos.dev/self-ips": "10.0.0.2,192.168.3.4", }, }, { name: "controlplane", affiliate: cluster.AffiliateSpec{ NodeID: "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, ControlPlane: &cluster.ControlPlane{ APIServerPort: 443, }, }, expected: map[string]string{ "cluster.talos.dev/node-id": "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", "networking.talos.dev/api-server-port": "443", "networking.talos.dev/assigned-prefixes": "", "networking.talos.dev/kubespan-endpoints": "", "networking.talos.dev/kubespan-ip": "", "networking.talos.dev/kubespan-public-key": "", "networking.talos.dev/kubespan-exclude-advertised-networks": "", "networking.talos.dev/self-ips": "10.0.0.2,192.168.3.4", }, }, } { t.Run(tt.name, func(t *testing.T) { affiliate := cluster.NewAffiliate(cluster.NamespaceName, tt.affiliate.NodeID) *affiliate.TypedSpec() = tt.affiliate assert.Equal(t, tt.expected, registry.AnnotationsFromAffiliate(affiliate)) }) } } func TestAffiliateFromNode(t *testing.T) { for _, tt := range []struct { name string node v1.Node expected *cluster.AffiliateSpec }{ { name: "no annotations", node: v1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "worker-1", Annotations: map[string]string{}, }, Spec: v1.NodeSpec{}, }, expected: nil, }, { name: "discovered", node: v1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", Annotations: map[string]string{ "cluster.talos.dev/node-id": "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", "networking.talos.dev/assigned-prefixes": "10.244.3.1/24", "networking.talos.dev/kubespan-endpoints": "10.0.0.2:51820,192.168.3.4:51820", "networking.talos.dev/kubespan-ip": "fd50:8d60:4238:6302:f857:23ff:fe21:d1e0", "networking.talos.dev/kubespan-public-key": "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", "networking.talos.dev/kubespan-exclude-advertised-networks": "0.0.0.0/0,::/0", "networking.talos.dev/self-ips": "10.0.0.2,192.168.3.4", }, Labels: map[string]string{ constants.LabelNodeRoleControlPlane: "", }, }, Spec: v1.NodeSpec{}, Status: v1.NodeStatus{ Addresses: []v1.NodeAddress{ { Type: v1.NodeHostName, Address: "foo.com", }, }, NodeInfo: v1.NodeSystemInfo{ OSImage: "Talos (v1.0.0)", }, }, }, expected: &cluster.AffiliateSpec{ NodeID: "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, OperatingSystem: "Talos (v1.0.0)", KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}, }, }, }, { name: "controlplane", node: v1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "bar", Annotations: map[string]string{ "cluster.talos.dev/node-id": "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", "networking.talos.dev/api-server-port": "6443", "networking.talos.dev/self-ips": "10.0.0.2,192.168.3.4", }, Labels: map[string]string{ constants.LabelNodeRoleControlPlane: "", }, }, Spec: v1.NodeSpec{}, Status: v1.NodeStatus{ Addresses: []v1.NodeAddress{ { Type: v1.NodeHostName, Address: "foo.com", }, }, NodeInfo: v1.NodeSystemInfo{ OSImage: "Talos (v1.0.0)", }, }, }, expected: &cluster.AffiliateSpec{ NodeID: "29QQTc97U5ZyFTIX33Dp9NqtwxqQI8QI13scCLzffrZ", Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, OperatingSystem: "Talos (v1.0.0)", ControlPlane: &cluster.ControlPlane{ APIServerPort: 6443, }, }, }, } { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.expected, registry.AffiliateFromNode(&tt.node)) }) } } ================================================ FILE: internal/pkg/discovery/registry/registry.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package registry provides code to push and pull Affiliates to different registries. package registry ================================================ FILE: internal/pkg/dns/dns.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package dns provides dns server implementation. package dns import ( "context" "errors" "fmt" "iter" "net" "net/netip" "slices" "sync" "sync/atomic" "syscall" "time" "github.com/coredns/coredns/plugin" "github.com/coredns/coredns/plugin/cache" "github.com/coredns/coredns/plugin/pkg/dnsutil" "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/coredns/coredns/request" "github.com/miekg/dns" "github.com/siderolabs/gen/xiter" "go.uber.org/zap" "golang.org/x/sys/unix" ) // Cache is a [dns.Handler] to [plugin.Handler] adapter. type Cache struct { cache *cache.Cache logger *zap.Logger } // NewCache creates a new Cache. func NewCache(next plugin.Handler, l *zap.Logger) *Cache { c := cache.NewCache( "zones", "view", cache.WithNegativeTTL(10*time.Second, dnsutil.MinimalDefaultTTL), ) c.Next = next return &Cache{cache: c, logger: l} } // ServeDNS implements [dns.Handler]. func (c *Cache) ServeDNS(wr dns.ResponseWriter, msg *dns.Msg) { wr = request.NewScrubWriter(msg, wr) ctx, cancel := context.WithTimeout(context.Background(), 4500*time.Millisecond) defer cancel() code, err := c.cache.ServeDNS(ctx, wr, msg) if err != nil { // we should probably call newProxy.Healthcheck() if there are too many errors c.logger.Warn("error serving dns request", zap.Error(err)) } if clientWrite(code) { return } // Something went wrong state := request.Request{W: wr, Req: msg} answer := new(dns.Msg) answer.SetRcode(msg, code) state.SizeAndDo(answer) err = wr.WriteMsg(answer) if err != nil { c.logger.Warn("error writing dns response", zap.Error(err)) } } // Clear clears the cache. func (c *Cache) Clear() { c.cache.Clear() } // clientWrite returns true if the response has been written to the client. func clientWrite(rcode int) bool { switch rcode { case dns.RcodeServerFailure, dns.RcodeRefused, dns.RcodeFormatError, dns.RcodeNotImplemented: return false default: return true } } // Handler is a dns proxy selector. type Handler struct { mx sync.RWMutex dests iter.Seq[*proxy.Proxy] logger *zap.Logger } // NewHandler creates a new Handler. func NewHandler(logger *zap.Logger) *Handler { return &Handler{ logger: logger, dests: xiter.Empty[*proxy.Proxy], } } // Name implements plugin.Handler. func (h *Handler) Name() string { return "Handler" } // ServeDNS implements plugin.Handler. // //nolint:gocyclo func (h *Handler) ServeDNS(ctx context.Context, wrt dns.ResponseWriter, msg *dns.Msg) (int, error) { h.mx.RLock() defer h.mx.RUnlock() req := request.Request{W: wrt, Req: msg} logger := h.logger.With( zap.Stringer("data", msg), zap.String("proto", req.Proto()), zap.String("question", req.QName()), zap.Stringer("local_addr", wrt.LocalAddr()), zap.Stringer("remote_addr", wrt.RemoteAddr()), ) var ( called bool resp *dns.Msg err error ) for ups := range h.dests { called = true opts := proxy.Options{} logger.Debug("making dns request", zap.String("upstream", ups.Addr())) for { resp, err = ups.Connect(ctx, req, opts) switch { case errors.Is(err, proxy.ErrCachedClosed): // Remote side closed conn, can only happen with TCP. continue case resp != nil && resp.Truncated && !opts.ForceTCP: // Retry with TCP if truncated opts.ForceTCP = true continue } break } if resp != nil && (resp.Rcode == dns.RcodeServerFailure || resp.Rcode == dns.RcodeRefused) { continue } if ctx.Err() != nil || err == nil { break } continue } if !called { return dns.RcodeServerFailure, errors.New("no destination available") } if ctx.Err() != nil { return dns.RcodeServerFailure, ctx.Err() } else if err != nil { return dns.RcodeServerFailure, err } if !req.Match(resp) { logger.Warn("dns response didn't match", zap.Stringer("data", resp)) return dns.RcodeFormatError, nil } err = wrt.WriteMsg(resp) if err != nil { // We can't do much here, but at least log the error. logger.Warn("error writing dns response", zap.Error(err)) } logger.Debug("dns response", zap.Stringer("data", resp)) return dns.RcodeSuccess, nil } // SetProxy sets destination dns proxy servers. func (h *Handler) SetProxy(prxs iter.Seq[*proxy.Proxy]) bool { h.mx.Lock() defer h.mx.Unlock() if xiter.Equal(h.dests, prxs) { return false } h.dests = prxs return true } // Stop stops and clears dns proxy selector. func (h *Handler) Stop() { h.SetProxy(xiter.Empty) } // NewNodeHandler creates a new NodeHandler. func NewNodeHandler(next plugin.Handler, hostMapper HostMapper, logger *zap.Logger) *NodeHandler { return &NodeHandler{next: next, mapper: hostMapper, logger: logger} } // HostMapper is a name to node mapper. type HostMapper interface { ResolveAddr(ctx context.Context, qType uint16, name string) (iter.Seq[netip.Addr], bool) } // NodeHandler try to resolve dns request to a node. If required node is not found, it will move to the next handler. type NodeHandler struct { next plugin.Handler mapper HostMapper logger *zap.Logger enabled atomic.Bool } // Name implements plugin.Handler. func (h *NodeHandler) Name() string { return "NodeHandler" } // ServeDNS implements plugin.Handler. func (h *NodeHandler) ServeDNS(ctx context.Context, wrt dns.ResponseWriter, msg *dns.Msg) (int, error) { if !h.enabled.Load() { return h.next.ServeDNS(ctx, wrt, msg) } idx := slices.IndexFunc(msg.Question, func(q dns.Question) bool { return q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA }) if idx == -1 { return h.next.ServeDNS(ctx, wrt, msg) } req := request.Request{W: wrt, Req: msg} // Check if the request is for a node. result, ok := h.mapper.ResolveAddr(ctx, req.QType(), req.Name()) if !ok { return h.next.ServeDNS(ctx, wrt, msg) } answers := mapAnswers(result, req.Name()) if len(answers) == 0 { return h.next.ServeDNS(ctx, wrt, msg) } resp := new(dns.Msg).SetReply(req.Req) resp.Authoritative = true resp.Answer = answers err := wrt.WriteMsg(resp) if err != nil { // We can't do much here, but at least log the error. h.logger.Warn("error writing dns response in node handler", zap.Error(err)) } return dns.RcodeSuccess, nil } func mapAnswers(addrs iter.Seq[netip.Addr], name string) []dns.RR { var result []dns.RR for addr := range addrs { switch { case addr.Is4(): result = append(result, &dns.A{ Hdr: dns.RR_Header{ Name: name, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: nodeDNSResponseTTL, }, A: addr.AsSlice(), }) case addr.Is6(): result = append(result, &dns.AAAA{ Hdr: dns.RR_Header{ Name: name, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: nodeDNSResponseTTL, }, AAAA: addr.AsSlice(), }) } } return result } const nodeDNSResponseTTL = 10 // SetEnabled sets the handler enabled state. func (h *NodeHandler) SetEnabled(enabled bool) { h.enabled.Store(enabled) } // NewTCPListener creates a new TCP listener. func NewTCPListener(network, addr string, control ControlFn) (net.Listener, error) { network, ok := networkNames[network] if !ok { return nil, fmt.Errorf("unsupported network: %s", network) } lc := net.ListenConfig{Control: control} return lc.Listen(context.Background(), network, addr) } // NewUDPPacketConn creates a new UDP packet connection. func NewUDPPacketConn(network, addr string, control ControlFn) (net.PacketConn, error) { network, ok := networkNames[network] if !ok { return nil, fmt.Errorf("unsupported network: %s", network) } lc := net.ListenConfig{Control: control} return lc.ListenPacket(context.Background(), network, addr) } // ControlFn is an alias to [net.ListenConfig.Control] function. type ControlFn = func(string, string, syscall.RawConn) error // MakeControl creates a control function for setting socket options. func MakeControl(network string, forwardEnabled bool) (ControlFn, error) { maxHops := 1 if forwardEnabled { maxHops = 2 } var options []controlOptions switch network { case "tcp", "tcp4": options = []controlOptions{ {unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "failed to set IP_RECVTTL"}, {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd {unix.IPPROTO_IP, unix.IP_TTL, maxHops, "failed to set IP_TTL"}, } case "tcp6": options = []controlOptions{ {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"}, {unix.IPPROTO_TCP, unix.TCP_FASTOPEN, 5, "failed to set TCP_FASTOPEN"}, // tcp specific stuff from systemd {unix.IPPROTO_TCP, unix.TCP_NODELAY, 1, "failed to set TCP_NODELAY"}, // tcp specific stuff from systemd {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"}, } case "udp", "udp4": options = []controlOptions{ {unix.IPPROTO_IP, unix.IP_RECVTTL, maxHops, "failed to set IP_RECVTTL"}, {unix.IPPROTO_IP, unix.IP_TTL, maxHops, "failed to set IP_TTL"}, } case "udp6": options = []controlOptions{ {unix.IPPROTO_IPV6, unix.IPV6_RECVHOPLIMIT, maxHops, "failed to set IPV6_RECVHOPLIMIT"}, {unix.IPPROTO_IPV6, unix.IPV6_UNICAST_HOPS, maxHops, "failed to set IPV6_UNICAST_HOPS"}, } default: return nil, fmt.Errorf("unsupported network: %s", network) } return func(_ string, _ string, c syscall.RawConn) error { var resErr error err := c.Control(func(fd uintptr) { for _, opt := range options { opErr := unix.SetsockoptInt(int(fd), opt.level, opt.opt, opt.val) if opErr != nil { resErr = fmt.Errorf(opt.errorMessage+": %w", opErr) return } } }) if err != nil { return fmt.Errorf("failed in control call: %w", err) } if resErr != nil { return fmt.Errorf("failed to set socket options: %w", resErr) } return nil }, nil } type controlOptions struct { level int opt int val int errorMessage string } var networkNames = map[string]string{ "tcp": "tcp4", "tcp4": "tcp4", "tcp6": "tcp6", "udp": "udp4", "udp4": "udp4", "udp6": "udp6", } ================================================ FILE: internal/pkg/dns/dns_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dns_test import ( "context" "iter" "net" "net/netip" "slices" "strings" "testing" "time" "github.com/coredns/coredns/plugin/pkg/proxy" dnssrv "github.com/miekg/dns" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/gen/xtesting/check" "github.com/stretchr/testify/require" "github.com/thejerf/suture/v4" "go.uber.org/goleak" "go.uber.org/zap/zaptest" "github.com/siderolabs/talos/internal/pkg/dns" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func TestDNS(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t) }) tests := []struct { name string hostname string nameservers []string expectedCode int errCheck check.Check }{ { name: "success", hostname: "google.com", nameservers: []string{"8.8.8.8"}, expectedCode: dnssrv.RcodeSuccess, errCheck: check.NoError(), }, { name: "failure", hostname: "google-fail.com", nameservers: []string{"242.242.242.242"}, errCheck: check.ErrorContains("i/o timeout"), }, { name: "empty destinations", hostname: "google.com", nameservers: nil, expectedCode: dnssrv.RcodeServerFailure, errCheck: check.NoError(), }, { name: "empty destinations but node exists", hostname: "talos-default-worker-1", nameservers: nil, expectedCode: dnssrv.RcodeSuccess, errCheck: check.NoError(), }, { name: "empty destinations but node doesn't exists", hostname: "talos-default-worker-2", nameservers: []string{"8.8.8.8"}, expectedCode: dnssrv.RcodeNameError, errCheck: check.NoError(), }, { // The first one will return SERVFAIL and the second will return REFUSED. We should try both. name: `should return "refused"`, hostname: "dnssec-failed.org", nameservers: []string{"1.1.1.1", "ns-1098.awsdns-09.org."}, expectedCode: dnssrv.RcodeRefused, errCheck: check.NoError(), }, } for _, dnsAddr := range []string{"127.0.0.1:10700"} { for _, test := range tests { t.Run(dnsAddr+"/"+test.name, func(t *testing.T) { stop := newManager(t, test.nameservers...) t.Cleanup(stop) time.Sleep(10 * time.Millisecond) c := dnssrv.Client{ Timeout: time.Second, } r, _, err := c.Exchange(createQuery(test.hostname), dnsAddr) test.errCheck(t, err) if r != nil { require.Equal(t, test.expectedCode, r.Rcode, r) } t.Logf("r: %s", r) }) } } } func TestDNSEmptyDestinations(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t) }) stop := newManager(t) defer stop() time.Sleep(10 * time.Millisecond) r, err := dnssrv.Exchange(createQuery("google.com"), "127.0.0.1:10700") require.NoError(t, err) require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r) r, err = dnssrv.Exchange(createQuery("google.com"), "127.0.0.1:10700") require.NoError(t, err) require.Equal(t, dnssrv.RcodeServerFailure, r.Rcode, r) stop() } func Test_ServeBackground(t *testing.T) { t.Cleanup(func() { goleak.VerifyNone(t) }) m := dns.NewManager(&testReader{}, func(e suture.Event) { t.Log("dns-runners event:", e) }, zaptest.NewLogger(t)) m.ServeBackground(t.Context()) // should not panic since ServeBackground is called with the same context m.ServeBackground(t.Context()) // should panic since ServeBackground is called with a different context require.Panics(t, func() { m.ServeBackground(context.TODO()) }) //nolint:usetesting for _, err := range m.RunAll(slices.Values([]dns.AddressPair{ {Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")}, {Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10701")}, }), false) { require.NoError(t, err) } require.NoError(t, m.ClearAll(false)) } func newManager(t *testing.T, nameservers ...string) func() { m := dns.NewManager( &testReader{}, func(e suture.Event) { t.Log("dns-runners event:", e) }, zaptest.NewLogger(t), ) m.AllowNodeResolving(true) t.Cleanup(func() { if err := m.ClearAll(false); err != nil { t.Logf("error stopping dns runners: %v", err) } }) pxs := xslices.Map(nameservers, func(ns string) *proxy.Proxy { p := proxy.NewProxy(ns, net.JoinHostPort(ns, "53"), "dns") p.Start(500 * time.Millisecond) return p }) t.Cleanup(func() { for _, p := range pxs { p.Close() // We had to manually add this method to the coredns Proxy type. } }) ctx, cancel := context.WithCancel(context.Background()) //nolint:usetesting t.Cleanup(cancel) m.SetUpstreams(slices.Values(pxs)) m.ServeBackground(ctx) m.ServeBackground(ctx) for _, err := range m.RunAll(slices.Values([]dns.AddressPair{ {Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")}, {Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10701")}, {Network: "tcp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")}, }), false) { if err != nil && strings.Contains(err.Error(), "failed to set TCP_FASTOPEN") { continue } require.NoError(t, err) } for _, err := range m.RunAll(slices.Values([]dns.AddressPair{ {Network: "udp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")}, {Network: "tcp", Addr: netip.MustParseAddrPort("127.0.0.1:10700")}, }), false) { if err != nil && strings.Contains(err.Error(), "failed to set TCP_FASTOPEN") { continue } require.NoError(t, err) } return func() { if err := m.ClearAll(false); err != nil { t.Logf("error stopping dns runners: %v", err) } } } func createQuery(name string) *dnssrv.Msg { return &dnssrv.Msg{ MsgHdr: dnssrv.MsgHdr{ Id: dnssrv.Id(), RecursionDesired: true, }, Question: []dnssrv.Question{ { Name: dnssrv.Fqdn(name), Qtype: dnssrv.TypeA, Qclass: dnssrv.ClassINET, }, }, } } type testReader struct{} func (r *testReader) ReadMembers(context.Context) (iter.Seq[*cluster.Member], error) { namesToAddresses := map[string][]netip.Addr{ "talos-default-controlplane-1": {netip.MustParseAddr("172.20.0.2")}, "talos-default-worker-1": {netip.MustParseAddr("172.20.0.3")}, } result := maps.ToSlice(namesToAddresses, func(k string, v []netip.Addr) *cluster.Member { result := cluster.NewMember(cluster.NamespaceName, k) result.TypedSpec().Addresses = v return result }) return slices.Values(result), nil } ================================================ FILE: internal/pkg/dns/manager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dns import ( "context" "errors" "fmt" "iter" "net/netip" "slices" "strings" "time" "github.com/coredns/coredns/plugin/pkg/proxy" "github.com/hashicorp/go-multierror" dnssrv "github.com/miekg/dns" "github.com/siderolabs/gen/xiter" "github.com/thejerf/suture/v4" "go.uber.org/zap" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // ErrCreatingRunner is an error that occurs when creating a runner. var ErrCreatingRunner = errors.New("error creating runner") // Manager manages DNS runners. type Manager struct { originalCtx context.Context //nolint:containedctx handler *Handler nodeHandler *NodeHandler rootHandler *Cache s *suture.Supervisor supervisorCh <-chan error logger *zap.Logger runners map[AddressPair]suture.ServiceToken } // NewManager creates a new manager. func NewManager(mr MemberReader, hook suture.EventHook, logger *zap.Logger) *Manager { handler := NewHandler(logger) nodeHandler := NewNodeHandler(handler, &addrResolver{mr: mr}, logger) rootHandler := NewCache(nodeHandler, logger) m := &Manager{ handler: handler, nodeHandler: nodeHandler, rootHandler: rootHandler, s: suture.New("dns-resolve-cache-runners", suture.Spec{EventHook: hook}), logger: logger, runners: map[AddressPair]suture.ServiceToken{}, } return m } // ServeBackground starts the manager in the background. It panics if the manager is not initialized or if it's called // more than once. func (m *Manager) ServeBackground(ctx context.Context) { switch { case m.originalCtx == nil: m.originalCtx = ctx case m.originalCtx != ctx: panic("Manager.ServeBackground is called with a different context") case m.originalCtx == ctx: return } m.supervisorCh = m.s.ServeBackground(ctx) } // AddressPair represents a network and address with port. type AddressPair struct { Network string Addr netip.AddrPort } // String returns a string representation of the address pair. func (a AddressPair) String() string { return "Network: " + a.Network + ", Addr: " + a.Addr.String() } // RunAll updates and run the runners managed by the manager. It returns an iterator which yields the address pairs for // all running and attempted ro run configurations. It's mandatory to range over the iterator to ensure all runners are updated. func (m *Manager) RunAll(pairs iter.Seq[AddressPair], forwardEnabled bool) iter.Seq2[RunResult, error] { return func(yield func(RunResult, error) bool) { preserve := make(map[AddressPair]struct{}, len(m.runners)) for cfg := range pairs { preserve[cfg] = struct{}{} if _, ok := m.runners[cfg]; ok { if !yield(makeResult(cfg, StatusRunning), nil) { return } continue } opts, err := newDNSRunnerOpts(cfg, m.rootHandler, forwardEnabled) if err != nil { err = fmt.Errorf("%w: %w", ErrCreatingRunner, err) } else { m.runners[cfg] = m.s.Add(NewRunner(opts, m.logger)) } if !yield(makeResult(cfg, StatusNew), err) { return } } for cfg, token := range m.runners { if _, ok := preserve[cfg]; ok { continue } err := m.s.RemoveAndWait(token, 0) if err != nil { err = fmt.Errorf("error removing runner: %w", err) } if !yield(makeResult(cfg, StatusRemoved), err) { return } delete(m.runners, cfg) } } } func makeResult(cfg AddressPair, s Status) RunResult { return RunResult{AddressPair: cfg, Status: s} } // AllowNodeResolving enables or disables the node resolving feature. func (m *Manager) AllowNodeResolving(enabled bool) { m.nodeHandler.SetEnabled(enabled) } // SetUpstreams sets the upstreams for the DNS handler. It returns true if the upstreams were updated, false otherwise. func (m *Manager) SetUpstreams(prxs iter.Seq[*proxy.Proxy]) bool { if !m.handler.SetProxy(prxs) { return false } // Upstreams updated, clear cache to prevent DNS poisoning. m.rootHandler.Clear() return true } // ClearAll stops and removes all runners. Returns all errors if any runner failed to properly stop. func (m *Manager) ClearAll(dry bool) error { if dry { return nil } var multiErr *multierror.Error for _, err := range m.clearAll() { if err != nil { multiErr = multierror.Append(multiErr, err) } } return multiErr.ErrorOrNil() } func (m *Manager) clearAll() iter.Seq2[AddressPair, error] { return func(yield func(AddressPair, error) bool) { if len(m.runners) == 0 { return } defer m.handler.Stop() for runData, token := range m.runners { err := m.s.RemoveAndWait(token, 0) if err != nil && !errors.Is(err, suture.ErrSupervisorNotRunning) && !errors.Is(err, suture.ErrTimeout) { err = fmt.Errorf("error removing runner: %w", err) } else { err = nil } if !yield(runData, err) { return } delete(m.runners, runData) } } } // Done reports if superwisor finished execution. func (m *Manager) Done() <-chan error { return m.supervisorCh } type addrResolver struct { mr MemberReader } func (s *addrResolver) ResolveAddr(ctx context.Context, qType uint16, name string) (iter.Seq[netip.Addr], bool) { name = strings.TrimRight(name, ".") items, err := s.mr.ReadMembers(ctx) if err != nil { return nil, false } found, ok := xiter.Find(func(res *cluster.Member) bool { return fqdnMatch(name, res.TypedSpec().Hostname) || fqdnMatch(name, res.Metadata().ID()) }, items) if !ok { return nil, false } return xiter.Filter( func(addr netip.Addr) bool { return (qType == dnssrv.TypeA && addr.Is4()) || (qType == dnssrv.TypeAAAA && addr.Is6()) }, slices.Values(found.TypedSpec().Addresses), ), true } func fqdnMatch(what, where string) bool { what = strings.TrimRight(what, ".") where = strings.TrimRight(where, ".") if what == where { return true } first, _, found := strings.Cut(where, ".") if !found { return false } return what == first } // MemberReader is an interface to read members. type MemberReader interface { ReadMembers(ctx context.Context) (iter.Seq[*cluster.Member], error) } func newDNSRunnerOpts(cfg AddressPair, rootHandler dnssrv.Handler, forwardEnabled bool) (RunnerOptions, error) { if cfg.Addr.Addr().Is6() && !strings.HasSuffix(cfg.Network, "6") { cfg.Network += "6" } var serverOpts RunnerOptions controlFn, ctrlErr := MakeControl(cfg.Network, forwardEnabled) if ctrlErr != nil { return serverOpts, fmt.Errorf("error creating %q control function: %w", cfg.Network, ctrlErr) } switch cfg.Network { case "udp", "udp6": packetConn, err := NewUDPPacketConn(cfg.Network, cfg.Addr.String(), controlFn) if err != nil { return serverOpts, fmt.Errorf("error creating %q packet conn: %w", cfg.Network, err) } serverOpts = RunnerOptions{ PacketConn: packetConn, Handler: rootHandler, } case "tcp", "tcp6": listener, err := NewTCPListener(cfg.Network, cfg.Addr.String(), controlFn) if err != nil { return serverOpts, fmt.Errorf("error creating %q listener: %w", cfg.Network, err) } serverOpts = RunnerOptions{ Listener: listener, Handler: rootHandler, ReadTimeout: 3 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: func() time.Duration { return 10 * time.Second }, MaxTCPQueries: -1, } } return serverOpts, nil } // RunResult represents the result of a RunAll iteration. type RunResult struct { AddressPair Status Status } // Status represents the status of a runner. type Status int const ( // StatusNew represents a new runner. StatusNew Status = iota // StatusRunning represents a already running runner. StatusRunning // StatusRemoved represents a removed runner. StatusRemoved ) ================================================ FILE: internal/pkg/dns/runnner.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dns import ( "context" "errors" "io" "net" "time" "github.com/miekg/dns" "go.uber.org/zap" ) // RunnerOptions is a [Runner] options. type RunnerOptions struct { Listener net.Listener PacketConn net.PacketConn Handler dns.Handler ReadTimeout time.Duration WriteTimeout time.Duration IdleTimeout func() time.Duration MaxTCPQueries int } // NewRunner creates a new [Runner]. func NewRunner(opts RunnerOptions, l *zap.Logger) *Runner { return &Runner{ srv: &dns.Server{ Listener: opts.Listener, PacketConn: opts.PacketConn, Handler: opts.Handler, UDPSize: dns.DefaultMsgSize, // 4096 since default is [dns.MinMsgSize] = 512 bytes, which is too small. ReadTimeout: opts.ReadTimeout, WriteTimeout: opts.WriteTimeout, IdleTimeout: opts.IdleTimeout, MaxTCPQueries: opts.MaxTCPQueries, }, logger: l, } } // Runner is a DNS server runner. type Runner struct { srv *dns.Server logger *zap.Logger } // Serve starts the DNS server. Implements [suture.Service] interface. func (r *Runner) Serve(ctx context.Context) error { errCh := make(chan error) go func() { errCh <- r.srv.ActivateAndServe() }() select { case err := <-errCh: return err case <-ctx.Done(): } r.close() select { case err := <-errCh: return err case <-time.After(time.Second): return errors.New("timeout waiting for server to close") } } func (r *Runner) close() { l := r.logger if r.srv.Listener != nil { l = l.With(zap.String("net", "tcp"), zap.String("local_addr", r.srv.Listener.Addr().String())) } else if r.srv.PacketConn != nil { l = l.With(zap.String("net", "udp"), zap.String("local_addr", r.srv.PacketConn.LocalAddr().String())) } closer := io.Closer(r.srv.Listener) if closer == nil { closer = r.srv.PacketConn } if closer != nil { if err := closer.Close(); err != nil { l.Error("error closing dns server listener", zap.Error(err)) } else { l.Debug("dns server listener closed") } } sCtx, sCancel := context.WithTimeout(context.Background(), 5*time.Second) defer sCancel() err := r.srv.ShutdownContext(sCtx) if err != nil && !errors.Is(err, net.ErrClosed) { l.Error("error shutting down dns server", zap.Error(err)) } } ================================================ FILE: internal/pkg/efivarfs/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: internal/pkg/efivarfs/README.md ================================================ # efivarfs This package was copied from the upstream repository at [efivarfs](https://github.com/monogon/monogon/tree/main/osbase/efivarfs) There were minimal changes done to comply with the linter and gofmt rules. The major change was to remove the EFI variable file immutable attribute before writing to it. ================================================ FILE: internal/pkg/efivarfs/boot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package efivarfs import ( "bytes" "encoding/binary" "errors" "fmt" "math" "strings" ) // LoadOptionCategory defines the category of a load option. This is used to // differentiate between different types of boot options. type LoadOptionCategory uint8 const ( // LoadOptionCategoryBoot is the default category for boot entries. LoadOptionCategoryBoot LoadOptionCategory = 0x0 // LoadOptionCategoryApp is the category for boot entries that are // not booted as part of the normal boot order, but are only launched via menu or hotkey. // This category is optional for bootloaders to support, before creating // new boot entries of this category firmware support needs to be // confirmed. LoadOptionCategoryApp LoadOptionCategory = 0x1 ) // LoadOption contains information on a payload to be loaded by EFI. type LoadOption struct { // Human-readable description of what this load option loads. // This is what's being shown by the firmware when selecting a boot option. Description string // If set, firmware will skip this load option when it is in BootOrder. // It is unspecificed whether this prevents the user from booting the entry // manually. Inactive bool // If set, this load option will not be shown in any menu for load option // selection. This does not affect other functionality. Hidden bool // Category contains the category of the load entry. The selected category // affects various firmware behaviors, see the individual value // descriptions for more information. Category LoadOptionCategory // Path to the UEFI PE executable to execute when this load option is being // loaded. FilePath DevicePath // ExtraPaths contains additional device paths with vendor-specific // behavior. Can generally be left empty. ExtraPaths []DevicePath // OptionalData gets passed as an argument to the executed PE executable. // If zero-length a NULL value is passed to the executable. OptionalData []byte } // Marshal encodes a LoadOption into a binary EFI_LOAD_OPTION. func (e *LoadOption) Marshal() ([]byte, error) { var ( data []byte attrs uint32 ) attrs |= (uint32(e.Category) & 0x1f) << 8 if e.Hidden { attrs |= 0x08 } if !e.Inactive { attrs |= 0x01 } data = binary.LittleEndian.AppendUint32(data, attrs) filePathRaw, err := e.FilePath.Marshal() if err != nil { return nil, fmt.Errorf("failed marshaling FilePath: %w", err) } for _, ep := range e.ExtraPaths { epRaw, err := ep.Marshal() if err != nil { return nil, fmt.Errorf("failed marshaling ExtraPath: %w", err) } filePathRaw = append(filePathRaw, epRaw...) } if len(filePathRaw) > math.MaxUint16 { return nil, fmt.Errorf("failed marshaling FilePath/ExtraPath: value too big (%d)", len(filePathRaw)) } data = binary.LittleEndian.AppendUint16(data, uint16(len(filePathRaw))) if strings.IndexByte(e.Description, 0x00) != -1 { return nil, fmt.Errorf("failed to encode Description: contains invalid null bytes") } encodedDesc, err := Encoding.NewEncoder().Bytes([]byte(e.Description)) if err != nil { return nil, fmt.Errorf("failed to encode Description: %w", err) } data = append(data, encodedDesc...) data = append(data, 0x00, 0x00) // Final UTF-16/UCS-2 null code data = append(data, filePathRaw...) data = append(data, e.OptionalData...) return data, nil } // UnmarshalLoadOption decodes a binary EFI_LOAD_OPTION into a LoadOption. func UnmarshalLoadOption(data []byte) (*LoadOption, error) { if len(data) < 6 { return nil, fmt.Errorf("invalid load option: minimum 6 bytes are required, got %d", len(data)) } var opt LoadOption attrs := binary.LittleEndian.Uint32(data[:4]) opt.Category = LoadOptionCategory((attrs >> 8) & 0x1f) opt.Hidden = attrs&0x08 != 0 opt.Inactive = attrs&0x01 == 0 lenPath := binary.LittleEndian.Uint16(data[4:6]) // Search for UTF-16 null code nullIdx := bytes.Index(data[6:], []byte{0x00, 0x00}) if nullIdx == -1 { return nil, errors.New("no null code point marking end of Description found") } descriptionEnd := 6 + nullIdx + 1 descriptionRaw := data[6:descriptionEnd] description, err := Encoding.NewDecoder().Bytes(descriptionRaw) if err != nil { return nil, fmt.Errorf("error decoding UTF-16 in Description: %w", err) } descriptionEnd += 2 // 2 null bytes terminating UTF-16 string opt.Description = string(description) if descriptionEnd+int(lenPath) > len(data) { return nil, fmt.Errorf("declared length of FilePath (%d) overruns available data (%d)", lenPath, len(data)-descriptionEnd) } filePathData := data[descriptionEnd : descriptionEnd+int(lenPath)] opt.FilePath, filePathData, err = UnmarshalDevicePath(filePathData) if err != nil { return nil, fmt.Errorf("failed unmarshaling FilePath: %w", err) } for len(filePathData) > 0 { var extraPath DevicePath extraPath, filePathData, err = UnmarshalDevicePath(filePathData) if err != nil { return nil, fmt.Errorf("failed unmarshaling ExtraPath: %w", err) } opt.ExtraPaths = append(opt.ExtraPaths, extraPath) } if descriptionEnd+int(lenPath) < len(data) { opt.OptionalData = data[descriptionEnd+int(lenPath):] } return &opt, nil } // BootOrder represents the contents of the BootOrder EFI variable. type BootOrder []uint16 // Marshal generates the binary representation of a BootOrder. func (t *BootOrder) Marshal() []byte { var out []byte for _, v := range *t { out = binary.LittleEndian.AppendUint16(out, v) } return out } // UnmarshalBootOrder loads a BootOrder from its binary representation. func UnmarshalBootOrder(d []byte) (BootOrder, error) { if len(d)%2 != 0 { return nil, fmt.Errorf("invalid length: %v bytes", len(d)) } l := len(d) / 2 out := make(BootOrder, l) for i := range l { out[i] = binary.LittleEndian.Uint16(d[i*2:]) } return out, nil } ================================================ FILE: internal/pkg/efivarfs/boot_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package efivarfs_test import ( "encoding/hex" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/efivarfs" ) // Generated with old working marshaler and manually double-checked // // nolint:errcheck var ref, _ = hex.DecodeString( "010000004a004500780061006d0070006c006500000004012a00010000000" + "500000000000000080000000000000014b8a76bad9dd11180b400c04fd430" + "c8020204041c005c0074006500730074005c0061002e00650066006900000" + "07fff0400", ) func TestEncoding(t *testing.T) { opt := efivarfs.LoadOption{ Description: "Example", FilePath: efivarfs.DevicePath{ &efivarfs.HardDrivePath{ PartitionNumber: 1, PartitionStartBlock: 5, PartitionSizeBlocks: 8, PartitionMatch: efivarfs.PartitionGPT{ PartitionUUID: uuid.NameSpaceX500, }, }, efivarfs.FilePath("/test/a.efi"), }, } got, err := opt.Marshal() require.NoError(t, err, "failed to marshal LoadOption") require.Equal(t, ref, got) got2, err := efivarfs.UnmarshalLoadOption(got) require.NoError(t, err, "failed to unmarshal LoadOption") require.Equal(t, &opt, got2, "unmarshaled LoadOption does not match original") } func FuzzDecode(f *testing.F) { f.Add(ref) f.Fuzz(func(t *testing.T, a []byte) { // Just try to see if it crashes _, _ = efivarfs.UnmarshalLoadOption(a) //nolint:errcheck }) } ================================================ FILE: internal/pkg/efivarfs/devicepath.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package efivarfs import ( "bytes" "encoding/binary" "errors" "fmt" "math" "strings" "github.com/google/uuid" "github.com/siderolabs/talos/internal/pkg/msguid" ) // DevicePath represents a path consisting of one or more elements to an // entity implementing an EFI protocol. It's very broadly used inside EFI // for representing all sorts of abstract paths. In the context of this // package it is used to represent paths to EFI loaders. // See https://uefi.org/specs/UEFI/2.10/10_Protocols_Device_Path_Protocol.html // for more information. type DevicePath []DevicePathElem // DevicePathElem is a common interface for all UEFI device path elements. type DevicePathElem interface { typ() uint8 subType() uint8 data() ([]byte, error) } type pathElemUnmarshalFunc func([]byte) (DevicePathElem, error) // PartitionMBR matches a drive or partition formatted with legacy MBR // (Master Boot Record). type PartitionMBR struct { // DiskSignature contains a 4-byte signature identifying the drive, located // just after the 440 bytes of boot sector loading code. // Note that since MBR does not have per-partition signatures, this is // combined with PartitionNumber to select a partition. DiskSignature [4]byte } func (p PartitionMBR) partitionSignature() (sig [16]byte) { copy(sig[:4], p.DiskSignature[:]) return sig } func (p PartitionMBR) partitionFormat() uint8 { return 0x01 } func (p PartitionMBR) signatureType() uint8 { return 0x01 } // PartitionGPT matches a partition on a drive formatted with GPT. type PartitionGPT struct { // UUID of the partition to be matched. Conversion into mixed-endian format // is taken care of, a standard big-endian UUID can be put in here. PartitionUUID uuid.UUID } func (p PartitionGPT) partitionSignature() [16]byte { return msguid.From(p.PartitionUUID) } func (p PartitionGPT) partitionFormat() uint8 { return 0x02 } func (p PartitionGPT) signatureType() uint8 { return 0x02 } // PartitionUnknown is being used to represent unknown partitioning schemas or // combinations of PartitionFormat/SignatureType. It contains raw uninterpreted // data. type PartitionUnknown struct { PartitionSignature [16]byte PartitionFormat uint8 SignatureType uint8 } func (p PartitionUnknown) partitionSignature() [16]byte { return p.PartitionSignature } func (p PartitionUnknown) partitionFormat() uint8 { return p.PartitionFormat } func (p PartitionUnknown) signatureType() uint8 { return p.SignatureType } // PartitionMatch is an interface that defines methods for matching // partitions on drives or filepaths. type PartitionMatch interface { partitionSignature() [16]byte partitionFormat() uint8 signatureType() uint8 } // HardDrivePath matches whole drives or partitions on GPT/MBR formatted // drives. type HardDrivePath struct { // Partition number, starting at 1. If zero or unset, the whole drive is // selected. PartitionNumber uint32 // Block address at which the partition starts. Not used for matching // partitions in EDK2. PartitionStartBlock uint64 // Number of blocks occupied by the partition starting from the // PartitionStartBlock. Not used for matching partitions in EDK2. PartitionSizeBlocks uint64 // PartitionMatch is used to match drive or partition signatures. // Use PartitionMBR and PartitionGPT types here. PartitionMatch PartitionMatch } func (h *HardDrivePath) typ() uint8 { return 4 } func (h *HardDrivePath) subType() uint8 { return 1 } func (h *HardDrivePath) data() ([]byte, error) { out := make([]byte, 38) le := binary.LittleEndian le.PutUint32(out[0:4], h.PartitionNumber) le.PutUint64(out[4:12], h.PartitionStartBlock) le.PutUint64(out[12:20], h.PartitionSizeBlocks) if h.PartitionMatch == nil { return nil, errors.New("PartitionMatch needs to be set") } sig := h.PartitionMatch.partitionSignature() copy(out[20:36], sig[:]) out[36] = h.PartitionMatch.partitionFormat() out[37] = h.PartitionMatch.signatureType() return out, nil } func unmarshalHardDrivePath(data []byte) (DevicePathElem, error) { var h HardDrivePath if len(data) != 38 { return nil, fmt.Errorf("invalid HardDrivePath element, expected 38 bytes, got %d", len(data)) } le := binary.LittleEndian h.PartitionNumber = le.Uint32(data[0:4]) h.PartitionStartBlock = le.Uint64(data[4:12]) h.PartitionSizeBlocks = le.Uint64(data[12:20]) partitionFormat := data[36] signatureType := data[37] var rawSig [16]byte copy(rawSig[:], data[20:36]) switch { case partitionFormat == 1 && signatureType == 1: // MBR var mbr PartitionMBR copy(mbr.DiskSignature[:], rawSig[:4]) h.PartitionMatch = mbr case partitionFormat == 2 && signatureType == 2: // GPT h.PartitionMatch = PartitionGPT{ PartitionUUID: msguid.To(rawSig), } default: // Unknown h.PartitionMatch = PartitionUnknown{ PartitionSignature: rawSig, PartitionFormat: partitionFormat, SignatureType: signatureType, } } return &h, nil } // FilePath contains a backslash-separated path or part of a path to a file on // a filesystem. type FilePath string func (f FilePath) typ() uint8 { return 4 } func (f FilePath) subType() uint8 { return 4 } func (f FilePath) data() ([]byte, error) { if strings.IndexByte(string(f), 0x00) != -1 { return nil, fmt.Errorf("contains invalid null bytes") } withBackslashes := bytes.ReplaceAll([]byte(f), []byte(`/`), []byte(`\`)) out, err := Encoding.NewEncoder().Bytes(withBackslashes) if err != nil { return nil, fmt.Errorf("failed to encode FilePath to UTF-16: %w", err) } return append(out, 0x00, 0x00), nil } func unmarshalFilePath(data []byte) (DevicePathElem, error) { if len(data) < 2 { return nil, fmt.Errorf("FilePath must be at least 2 bytes because of UTF-16 null terminator") } out, err := Encoding.NewDecoder().Bytes(data) if err != nil { return nil, fmt.Errorf("error decoding FilePath UTF-16 string: %w", err) } nullIdx := bytes.IndexByte(out, 0x00) if nullIdx != len(out)-1 { return nil, fmt.Errorf("FilePath not properly null-terminated") } withoutBackslashes := strings.ReplaceAll(string(out[:len(out)-1]), `\`, `/`) return FilePath(withoutBackslashes), nil } // Map key contains type and subtype. var pathElementUnmarshalMap = map[[2]byte]pathElemUnmarshalFunc{ {4, 1}: unmarshalHardDrivePath, {4, 4}: unmarshalFilePath, } // UnknownPath is a generic structure for all types of path elements not // understood by this library. The UEFI-specified set of path element // types is vast and mostly unused, this generic type allows for parsing as // well as pass-through of not-understood path elements. type UnknownPath struct { TypeVal uint8 SubTypeVal uint8 DataVal []byte } func (u UnknownPath) typ() uint8 { return u.TypeVal } func (u UnknownPath) subType() uint8 { return u.SubTypeVal } func (u UnknownPath) data() ([]byte, error) { return u.DataVal, nil } // Marshal encodes the device path in binary form. func (d DevicePath) Marshal() ([]byte, error) { var buf []byte //nolint:prealloc for _, p := range d { buf = append(buf, p.typ(), p.subType()) elemBuf, err := p.data() if err != nil { return nil, fmt.Errorf("failed marshaling path element: %w", err) } // 4 is size of header which is included in length field if len(elemBuf)+4 > math.MaxUint16 { return nil, fmt.Errorf("path element payload over maximum size") } buf = binary.LittleEndian.AppendUint16(buf, uint16(len(elemBuf)+4)) buf = append(buf, elemBuf...) } // End of device path (Type 0x7f, SubType 0xFF) buf = append(buf, 0x7f, 0xff, 0x04, 0x00) return buf, nil } // UnmarshalDevicePath parses a binary device path until it encounters an end // device path structure. It returns that device path (excluding the final end // device path marker) as well as all all data following the end marker. // //nolint:gocyclo func UnmarshalDevicePath(data []byte) (DevicePath, []byte, error) { rest := data var p DevicePath for { if len(rest) < 4 { if len(rest) != 0 { return nil, nil, fmt.Errorf("dangling bytes at the end of device path: %x", rest) } break } t := rest[0] subT := rest[1] dataLen := binary.LittleEndian.Uint16(rest[2:4]) if int(dataLen) > len(rest) { return nil, nil, fmt.Errorf("path element larger than rest of buffer: %d > %d", dataLen, len(rest)) } if dataLen < 4 { return nil, nil, fmt.Errorf("path element must be at least 4 bytes (header), length indicates %d", dataLen) } elemData := rest[4:dataLen] rest = rest[dataLen:] // End of Device Path if t == 0x7f && subT == 0xff { return p, rest, nil } unmarshal, ok := pathElementUnmarshalMap[[2]byte{t, subT}] if !ok { p = append(p, &UnknownPath{ TypeVal: t, SubTypeVal: subT, DataVal: elemData, }) continue } elem, err := unmarshal(elemData) if err != nil { return nil, nil, fmt.Errorf("failed decoding path element %d: %w", len(p), err) } p = append(p, elem) } if len(p) == 0 { return nil, nil, errors.New("empty DevicePath without End Of Path element") } return nil, nil, fmt.Errorf("got DevicePath with %d elements, but without End Of Path element", len(p)) } ================================================ FILE: internal/pkg/efivarfs/devicepath_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package efivarfs_test import ( "bytes" "testing" "github.com/google/uuid" "github.com/siderolabs/talos/internal/pkg/efivarfs" ) // nolint:gocyclo func TestMarshalExamples(t *testing.T) { cases := []struct { name string path efivarfs.DevicePath expected []byte expectError bool }{ { name: "TestNone", path: efivarfs.DevicePath{}, expected: []byte{ 0x7f, 0xff, // End of HW device path 0x04, 0x00, // Length: 4 bytes }, }, { // From UEFI Device Path Examples, extracted single entry name: "TestHD", path: efivarfs.DevicePath{ &efivarfs.HardDrivePath{ PartitionNumber: 1, PartitionStartBlock: 0x22, PartitionSizeBlocks: 0x2710000, PartitionMatch: efivarfs.PartitionGPT{ PartitionUUID: uuid.MustParse("15E39A00-1DD2-1000-8D7F-00A0C92408FC"), }, }, }, expected: []byte{ 0x04, 0x01, // Hard Disk type 0x2a, 0x00, // Length 0x01, 0x00, 0x00, 0x00, // Partition Number 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Part Start 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x00, 0x00, // Part Size 0x00, 0x9a, 0xe3, 0x15, 0xd2, 0x1d, 0x00, 0x10, 0x8d, 0x7f, 0x00, 0xa0, 0xc9, 0x24, 0x08, 0xfc, // Signature 0x02, // Part Format GPT 0x02, // Signature GPT 0x7f, 0xff, // End of HW device path 0x04, 0x00, // Length: 4 bytes }, }, { name: "TestFilePath", path: efivarfs.DevicePath{ efivarfs.FilePath("asdf"), }, expected: []byte{ 0x04, 0x04, // File Path type 0x0e, 0x00, // Length 'a', 0x00, 's', 0x00, 'd', 0x00, 'f', 0x00, 0x00, 0x00, 0x7f, 0xff, // End of HW device path 0x04, 0x00, // Length: 4 bytes }, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { got, err := c.path.Marshal() if err != nil && !c.expectError { t.Fatalf("unexpected error: %v", err) } if err == nil && c.expectError { t.Fatalf("expected error, got %x", got) } if err != nil && c.expectError { // Do not compare result in case error is expected return } if !bytes.Equal(got, c.expected) { t.Fatalf("expected %x, got %x", c.expected, got) } _, rest, err := efivarfs.UnmarshalDevicePath(got) if err != nil { t.Errorf("failed to unmarshal value again: %v", err) } if len(rest) != 0 { t.Errorf("rest is non-zero after single valid device path: %x", rest) } }) } } ================================================ FILE: internal/pkg/efivarfs/efivarfs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 // Package efivarfs provides functions to read and manipulate UEFI runtime // variables. It uses Linux's efivarfs [1] to access the variables and all // functions generally require that this is mounted at // "/sys/firmware/efi/efivars". // // [1] https://www.kernel.org/doc/html/latest/filesystems/efivarfs.html package efivarfs import ( "encoding/binary" "errors" "fmt" "io/fs" "os" "strings" "github.com/g0rbe/go-chattr" "github.com/google/uuid" "golang.org/x/sys/unix" "golang.org/x/text/encoding/unicode" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( // Path is the path to the efivarfs mount point. Path = "/sys/firmware/efi/efivars" ) var ( // ScopeGlobal is the scope of variables defined by the EFI specification // itself. ScopeGlobal = uuid.MustParse("8be4df61-93ca-11d2-aa0d-00e098032b8c") // ScopeSystemd is the scope of variables defined by Systemd/bootspec. ScopeSystemd = uuid.MustParse("4a67b082-0a4c-41cf-b6c7-440b29bb8c4f") ) // Encoding defines the Unicode encoding used by UEFI, which is UCS-2 Little // Endian. For BMP characters UTF-16 is equivalent to UCS-2. See the UEFI // Spec 2.9, Sections 33.2.6 and 1.8.1. var Encoding = unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) // Attribute contains a bitset of EFI variable attributes. type Attribute uint32 const ( // AttrNonVolatile is the attribute for non-volatile variables. // If set the value of the variable is is persistent across resets and // power cycles. Variables without this set cannot be created or modified // after UEFI boot services are terminated. AttrNonVolatile Attribute = 1 << iota // AttrBootserviceAccess is the attribute for variables that can be // accessed from UEFI boot services. AttrBootserviceAccess // AttrRuntimeAccess is the attribute for variables that can be accessed from // an operating system after UEFI boot services are terminated. Variables // setting this must also set AttrBootserviceAccess. This is automatically // taken care of by Write in this package. AttrRuntimeAccess // AttrHardwareErrorRecord is the attribute for variables that are used to // mark a variable as being a hardware error record. See UEFI 2.10 section // 8.2.8 for more information about this. AttrHardwareErrorRecord // AttrAuthenticatedWriteAccess is the attribute for variables that require // authenticated access to write. // // Deprecated: should not be used for new variables. AttrAuthenticatedWriteAccess // AttrTimeBasedAuthenticatedWriteAccess is the attribute for variables // that require special authentication to write. These variables // cannot be written with this package. AttrTimeBasedAuthenticatedWriteAccess // AttrAppendWrite is the attribute for variables that can be appended to. // If set in a Write() call, tries to append the data instead of replacing // it completely. AttrAppendWrite // AttrEnhancedAuthenticatedAccess is the attribute for variables that // require special authentication to access and write. These variables // cannot be accessed with this package. AttrEnhancedAuthenticatedAccess ) func varPath(scope uuid.UUID, varName string) string { return fmt.Sprintf("/sys/firmware/efi/efivars/%s-%s", varName, scope.String()) } // ReadWriter is an interface for reading and writing EFI variables. type ReadWriter interface { Write(scope uuid.UUID, varName string, attrs Attribute, value []byte) error Delete(scope uuid.UUID, varName string) error Read(scope uuid.UUID, varName string) ([]byte, Attribute, error) List(scope uuid.UUID) ([]string, error) } // FilesystemReaderWriter implements ReaderWriter using the efivars Linux filesystem. type FilesystemReaderWriter struct { write bool } // NewFilesystemReaderWriter creates a new FilesystemReaderWriter. func NewFilesystemReaderWriter(write bool) (*FilesystemReaderWriter, error) { if write { if err := unix.Mount("efivarfs", constants.EFIVarsMountPoint, "efivarfs", unix.MS_REMOUNT, ""); err != nil { return nil, err } } return &FilesystemReaderWriter{ write: write, }, nil } // Close unmounts efivarfs if the FilesystemReaderWriter was created with write // access. func (rw *FilesystemReaderWriter) Close() error { if rw.write { return unix.Mount("efivarfs", constants.EFIVarsMountPoint, "efivarfs", unix.MS_REMOUNT|unix.MS_RDONLY, "") } return nil } // Write writes the value of the named variable in the given scope. func (rw *FilesystemReaderWriter) Write(scope uuid.UUID, varName string, attrs Attribute, value []byte) error { if !rw.write { return errors.New("efivarfs was opened read-only") } // Ref: https://docs.kernel.org/filesystems/efivarfs.html // Remove immutable attribute from the efivarfs file if it exists if _, err := os.Stat(varPath(scope, varName)); err == nil { f, err := os.Open(varPath(scope, varName)) if err != nil { return fmt.Errorf("failed to open efivarfs file %q: %w", varPath(scope, varName), err) } defer f.Close() //nolint:errcheck if err := chattr.UnsetAttr(f, chattr.FS_IMMUTABLE_FL); err != nil { return fmt.Errorf("failed to clear immutable attribute from efivarfs file %q: %w", varPath(scope, varName), err) } defer chattr.SetAttr(f, chattr.FS_IMMUTABLE_FL) //nolint:errcheck } // Write attributes, see @linux//Documentation/filesystems:efivarfs.rst for format f, err := os.OpenFile(varPath(scope, varName), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { e := err // Unwrap PathError here as we wrap our own parameter message around it var perr *fs.PathError if errors.As(err, &perr) { e = perr.Err } return fmt.Errorf("writing %q in scope %s: %w", varName, scope, e) } // Required by UEFI 2.10 Section 8.2.3: // Runtime access to a data variable implies boot service access. Attributes // that have EFI_VARIABLE_RUNTIME_ACCESS set must also have // EFI_VARIABLE_BOOTSERVICE_ACCESS set. The caller is responsible for // following this rule. if attrs&AttrRuntimeAccess != 0 { attrs |= AttrBootserviceAccess } // Linux wants everything in on write, so assemble an intermediate buffer buf := make([]byte, len(value)+4) binary.LittleEndian.PutUint32(buf[:4], uint32(attrs)) copy(buf[4:], value) _, err = f.Write(buf) if err1 := f.Close(); err1 != nil && err == nil { err = err1 } return err } // Read reads the value of the named variable in the given scope. func (rw *FilesystemReaderWriter) Read(scope uuid.UUID, varName string) ([]byte, Attribute, error) { val, err := os.ReadFile(varPath(scope, varName)) if err != nil { e := err // Unwrap PathError here as we wrap our own parameter message around it var perr *fs.PathError if errors.As(err, &perr) { e = perr.Err } return nil, Attribute(0), fmt.Errorf("reading %q in scope %s: %w", varName, scope, e) } if len(val) < 4 { return nil, Attribute(0), fmt.Errorf("reading %q in scope %s: malformed, less than 4 bytes long", varName, scope) } return val[4:], Attribute(binary.LittleEndian.Uint32(val[:4])), nil } // List lists all variable names present for a given scope sorted by their names // in Go's "native" string sort order. func (rw *FilesystemReaderWriter) List(scope uuid.UUID) ([]string, error) { vars, err := os.ReadDir(Path) if err != nil { return nil, fmt.Errorf("failed to list variable directory: %w", err) } var outVarNames []string //nolint:prealloc suffix := fmt.Sprintf("-%v", scope) for _, v := range vars { if v.IsDir() { continue } if !strings.HasSuffix(v.Name(), suffix) { continue } outVarNames = append(outVarNames, strings.TrimSuffix(v.Name(), suffix)) } return outVarNames, nil } // Delete deletes the given variable name in the given scope. Use with care, // some firmware fails to boot if variables it uses are deleted. func (rw *FilesystemReaderWriter) Delete(scope uuid.UUID, varName string) error { if !rw.write { return errors.New("efivarfs was opened read-only") } return os.Remove(varPath(scope, varName)) } // UniqueBootOrder returns a copy of the given BootOrder with duplicate entries // removed, preserving the order of first appearance. func UniqueBootOrder(bootOrder BootOrder) BootOrder { seen := make(map[uint16]struct{}, len(bootOrder)) j := 0 for _, v := range bootOrder { if _, ok := seen[v]; ok { continue } seen[v] = struct{}{} bootOrder[j] = v j++ } return bootOrder[:j] } ================================================ FILE: internal/pkg/efivarfs/efivarfs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package efivarfs_test import ( "encoding/binary" "io/fs" "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/efivarfs" ) func TestBootOrder(t *testing.T) { t.Parallel() var bootOrderEntries []byte for _, entry := range []int{1, 0, 2, 3} { bootOrderEntries = binary.LittleEndian.AppendUint16(bootOrderEntries, uint16(entry)) } efiRW := efivarfs.Mock{ Variables: map[uuid.UUID]map[string]efivarfs.MockVariable{ efivarfs.ScopeGlobal: { "BootOrder": { Attrs: 0, Data: bootOrderEntries, }, }, }, } vars, err := efiRW.List(efivarfs.ScopeGlobal) require.NoError(t, err) require.Contains(t, vars, "BootOrder", "variable BootOrder not found") bootOrder, err := efivarfs.GetBootOrder(&efiRW) require.NoError(t, err) require.Equal(t, efivarfs.BootOrder([]uint16{1, 0, 2, 3}), bootOrder, "BootOrder does not match expected value") require.NoError(t, efivarfs.SetBootOrder(&efiRW, efivarfs.BootOrder([]uint16{1, 0, 3}))) bootOrder, err = efivarfs.GetBootOrder(&efiRW) require.NoError(t, err) require.Equal(t, efivarfs.BootOrder([]uint16{1, 0, 3}), bootOrder, "BootOrder does not match expected value after SetBootOrder") } func TestBootEntries(t *testing.T) { t.Parallel() efiRW := efivarfs.Mock{} // no entries yet entries, err := efivarfs.ListBootEntries(&efiRW) require.NoError(t, err) require.Empty(t, len(entries), "expected no boot entries in empty mock") // create first entry idx, err := efivarfs.AddBootEntry(&efiRW, &efivarfs.LoadOption{ Description: "First Entry", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/first.efi"), }, }) require.NoError(t, err) require.Equal(t, 0, idx, "first boot entry index should be 0") // verify first entry entry, err := efivarfs.GetBootEntry(&efiRW, idx) require.NoError(t, err) require.Equal(t, "First Entry", entry.Description, "first boot entry description does not match") require.Equal(t, efivarfs.DevicePath{efivarfs.FilePath("/first.efi")}, entry.FilePath, "first boot entry file path does not match") // create second entry require.NoError(t, efivarfs.SetBootEntry(&efiRW, 1, &efivarfs.LoadOption{ Description: "Second Entry", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/second.efi"), }, }), "failed to set second boot entry") // verify second entry entry, err = efivarfs.GetBootEntry(&efiRW, 1) require.NoError(t, err) require.Equal(t, "Second Entry", entry.Description, "second boot entry description does not match") require.Equal(t, efivarfs.DevicePath{efivarfs.FilePath("/second.efi")}, entry.FilePath, "second boot entry file path does not match") // list all entries entries, err = efivarfs.ListBootEntries(&efiRW) require.NoError(t, err) require.Len(t, entries, 2, "expected exactly two boot entries after adding two") // try overwrite first entry require.NoError(t, efivarfs.SetBootEntry(&efiRW, idx, &efivarfs.LoadOption{ Description: "First Entry Overwritten", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/first_overwritten.efi"), }, }), "failed to overwrite first boot entry") // verify first entry after overwrite entry, err = efivarfs.GetBootEntry(&efiRW, idx) require.NoError(t, err) require.Equal(t, "First Entry Overwritten", entry.Description, "first boot entry description does not match after overwrite") require.Equal(t, efivarfs.DevicePath{efivarfs.FilePath("/first_overwritten.efi")}, entry.FilePath, "first boot entry file path does not match after overwrite") // verify delete non-existing entry require.ErrorIs(t, efivarfs.DeleteBootEntry(&efiRW, 42), fs.ErrNotExist, "expected ErrNoSuchEntry when deleting non-existing entry") // delete second entry require.NoError(t, efivarfs.DeleteBootEntry(&efiRW, 1), "failed to delete second boot entry") // verify second entry is gone _, err = efivarfs.GetBootEntry(&efiRW, 1) require.ErrorIs(t, err, fs.ErrNotExist, "expected ErrNoSuchEntry when getting deleted entry") // list entries entries, err = efivarfs.ListBootEntries(&efiRW) require.NoError(t, err) require.Len(t, entries, 1, "expected exactly one boot entry after deleting one of two") // set entry with a high index require.NoError(t, efivarfs.SetBootEntry(&efiRW, 42, &efivarfs.LoadOption{ Description: "High Index Entry", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/high_index.efi"), }, }), "failed to set high index boot entry") // make sure adding a new entry uses the lowest available index (which is 1 now) newIdx, err := efivarfs.AddBootEntry(&efiRW, &efivarfs.LoadOption{ Description: "New Entry", FilePath: efivarfs.DevicePath{ efivarfs.FilePath("/new.efi"), }, }) require.NoError(t, err) require.Equal(t, 1, newIdx, "expected new boot entry index to be 1, the lowest available index") } func TestUniqueBootOrder(t *testing.T) { t.Parallel() require.Equal(t, efivarfs.BootOrder{}, efivarfs.UniqueBootOrder(efivarfs.BootOrder{}), "empty BootOrder should remain empty") require.Equal(t, efivarfs.BootOrder{1, 2, 3}, efivarfs.UniqueBootOrder(efivarfs.BootOrder{1, 2, 3}), "BootOrder with unique entries should remain unchanged") require.Equal(t, efivarfs.BootOrder{1, 2, 3}, efivarfs.UniqueBootOrder(efivarfs.BootOrder{1, 2, 3, 2, 1, 3}), "BootOrder with duplicates should have duplicates removed preserving order of first appearance") //nolint:lll require.Equal(t, efivarfs.BootOrder{0, 1, 3, 2}, efivarfs.UniqueBootOrder(efivarfs.BootOrder{0, 1, 0, 1, 0, 1, 1, 3, 2, 2, 3, 3, 1}), "BootOrder with all entries duplicated should have duplicates removed preserving order of first appearance") //nolint:lll } ================================================ FILE: internal/pkg/efivarfs/mock.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package efivarfs import ( "io/fs" "maps" "slices" "github.com/google/uuid" ) // Mock is a mock implementation of ReaderWriter interface for testing purposes. type Mock struct { Variables map[uuid.UUID]map[string]MockVariable } // MockVariable represents a mock EFI variable with its attributes and data. type MockVariable struct { Attrs Attribute Data []byte } // Write writes a variable to the given scope. func (mock *Mock) Write(scope uuid.UUID, varName string, attrs Attribute, value []byte) error { if mock.Variables == nil { mock.Variables = make(map[uuid.UUID]map[string]MockVariable) } if mock.Variables[scope] == nil { mock.Variables[scope] = make(map[string]MockVariable) } mock.Variables[scope][varName] = MockVariable{ Attrs: attrs, Data: value, } return nil } // Delete deletes a variable from the given scope. func (mock *Mock) Delete(scope uuid.UUID, varName string) error { if mock.Variables == nil || mock.Variables[scope] == nil { return fs.ErrNotExist } if _, exists := mock.Variables[scope][varName]; !exists { return fs.ErrNotExist } delete(mock.Variables[scope], varName) return nil } // Read reads a variable from the given scope. func (mock *Mock) Read(scope uuid.UUID, varName string) ([]byte, Attribute, error) { if mock.Variables == nil || mock.Variables[scope] == nil { return nil, 0, fs.ErrNotExist } variable, exists := mock.Variables[scope][varName] if !exists { return nil, 0, fs.ErrNotExist } return variable.Data, variable.Attrs, nil } // List lists all variable names in the given scope. func (mock *Mock) List(scope uuid.UUID) ([]string, error) { if mock.Variables == nil || mock.Variables[scope] == nil { return nil, nil } return slices.Collect(maps.Keys(mock.Variables[scope])), nil } ================================================ FILE: internal/pkg/efivarfs/variables.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package efivarfs import ( "bytes" "encoding/binary" "errors" "fmt" "io/fs" "math" "regexp" "strconv" "github.com/google/uuid" ) func decodeString(varData []byte) (string, error) { efiStringRaw, err := Encoding.NewDecoder().Bytes(varData) if err != nil { // Pass the decoding error unwrapped. return "", err } // Remove the null suffix. return string(bytes.TrimSuffix(efiStringRaw, []byte{0})), nil } // ReadLoaderDevicePartUUID reads the ESP UUID from an EFI variable. func ReadLoaderDevicePartUUID(rw ReadWriter) (uuid.UUID, error) { efiVar, _, err := rw.Read(ScopeSystemd, "LoaderDevicePartUUID") if err != nil { return uuid.Nil, err } strContent, err := decodeString(efiVar) if err != nil { return uuid.Nil, fmt.Errorf("decoding string failed: %w", err) } out, err := uuid.Parse(strContent) if err != nil { return uuid.Nil, fmt.Errorf("value in LoaderDevicePartUUID could not be parsed as UUID: %w", err) } return out, nil } // Technically UEFI mandates that only upper-case hex indices are valid, but in // practice even vendors themselves ship firmware with lowercase hex indices, // thus accept these here as well. var bootVarRegexp = regexp.MustCompile(`^Boot([0-9A-Fa-f]{4})$`) // ListBootEntries lists all EFI boot entries present in the system by their index. func ListBootEntries(rw ReadWriter) (map[int]*LoadOption, error) { bootEntries := make(map[int]*LoadOption) varNames, err := rw.List(ScopeGlobal) if err != nil { return nil, fmt.Errorf("failed to list EFI variables at scope %s: %w", ScopeGlobal, err) } for _, varName := range varNames { s := bootVarRegexp.FindStringSubmatch(varName) if s == nil { continue } idx, err := strconv.ParseUint(s[1], 16, 16) if err != nil { // This cannot be hit as all regexp matches are parseable. // A quick fuzz run agrees. panic(err) } entry, err := GetBootEntry(rw, int(idx)) if err != nil { return nil, fmt.Errorf("failed to get boot entry %s: %w", varName, err) } bootEntries[int(idx)] = entry } return bootEntries, nil } // AddBootEntry creates an new EFI boot entry variable and returns its // non-negative index on success. func AddBootEntry(rw ReadWriter, be *LoadOption) (int, error) { bootEntries, err := ListBootEntries(rw) if err != nil { return -1, fmt.Errorf("failed to list boot entries: %w", err) } idx := -1 for i := range math.MaxUint16 { if _, ok := bootEntries[i]; !ok { idx = i break } } if idx == -1 { return -1, errors.New("all 2^16 boot entry variables are occupied") } err = SetBootEntry(rw, idx, be) if err != nil { return -1, fmt.Errorf("failed to set new boot entry: %w", err) } return idx, nil } // GetBootEntry returns the boot entry at the given index. func GetBootEntry(rw ReadWriter, idx int) (*LoadOption, error) { raw, _, err := rw.Read(ScopeGlobal, fmt.Sprintf("Boot%04X", idx)) if errors.Is(err, fs.ErrNotExist) { // Try non-spec-conforming lowercase entry raw, _, err = rw.Read(ScopeGlobal, fmt.Sprintf("Boot%04x", idx)) } if err != nil { return nil, err } return UnmarshalLoadOption(raw) } // SetBootEntry writes the given boot entry to the given index. func SetBootEntry(rw ReadWriter, idx int, be *LoadOption) error { bem, err := be.Marshal() if err != nil { return fmt.Errorf("while marshaling the EFI boot entry: %w", err) } return rw.Write(ScopeGlobal, fmt.Sprintf("Boot%04X", idx), AttrNonVolatile|AttrRuntimeAccess, bem) } // DeleteBootEntry deletes the boot entry at the given index. func DeleteBootEntry(rw ReadWriter, idx int) error { err := rw.Delete(ScopeGlobal, fmt.Sprintf("Boot%04X", idx)) if errors.Is(err, fs.ErrNotExist) { // Try non-spec-conforming lowercase entry err = rw.Delete(ScopeGlobal, fmt.Sprintf("Boot%04x", idx)) } return err } // SetBootOrder replaces contents of the boot order variable with the order // specified in ord. func SetBootOrder(rw ReadWriter, ord BootOrder) error { return rw.Write(ScopeGlobal, "BootOrder", AttrNonVolatile|AttrRuntimeAccess, ord.Marshal()) } // GetBootOrder returns the current boot order of the system. func GetBootOrder(rw ReadWriter) (BootOrder, error) { raw, _, err := rw.Read(ScopeGlobal, "BootOrder") if err != nil { return nil, err } ord, err := UnmarshalBootOrder(raw) if err != nil { return nil, fmt.Errorf("invalid boot order structure: %w", err) } return ord, nil } // SetBootNext sets the boot entry used for the next boot only. It automatically // resets after the next boot. func SetBootNext(rw ReadWriter, entryIdx uint16) error { data := make([]byte, 2) binary.LittleEndian.PutUint16(data, entryIdx) return rw.Write(ScopeGlobal, "BootNext", AttrNonVolatile|AttrRuntimeAccess, data) } ================================================ FILE: internal/pkg/encryption/encryption.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package encryption provides modules for the partition encryption handling. package encryption import ( "cmp" "context" "encoding/json" "errors" "fmt" "slices" "strconv" "time" "github.com/hashicorp/go-multierror" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/luks" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "go.uber.org/zap" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" "github.com/siderolabs/talos/internal/pkg/encryption/keys" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) const keyHandlerTimeout = time.Second * 10 // Helpers provides helper methods for encryption handling. type Helpers struct { GetSystemInformation helpers.SystemInformationGetter TPMLocker helpers.TPMLockFunc SaltGetter helpers.SaltGetter } // NewHandler creates new Handler. func NewHandler(encryptionConfig block.EncryptionSpec, volumeID string, helpers Helpers) (*Handler, error) { cipher, err := luks.ParseCipherKind(encryptionConfig.Cipher) if err != nil { return nil, fmt.Errorf("failed to parse cipher kind: %w", err) } var opts []luks.Option if encryptionConfig.KeySize != 0 { opts = append(opts, luks.WithKeySize(encryptionConfig.KeySize)) } if encryptionConfig.BlockSize != 0 { opts = append(opts, luks.WithBlockSize(encryptionConfig.BlockSize)) } if encryptionConfig.PerfOptions != nil { for _, opt := range encryptionConfig.PerfOptions { if err = luks.ValidatePerfOption(opt); err != nil { return nil, fmt.Errorf("invalid luks performance options: %w", err) } } opts = append(opts, luks.WithPerfOptions(encryptionConfig.PerfOptions...)) } keyHandlers := make([]keys.Handler, 0, len(encryptionConfig.Keys)) for _, cfg := range encryptionConfig.Keys { handler, err := keys.NewHandler(cfg, keys.WithVolumeID(volumeID), keys.WithSystemInformationGetter(helpers.GetSystemInformation), keys.WithTPMLocker(helpers.TPMLocker), keys.WithSaltGetter(helpers.SaltGetter), ) if err != nil { return nil, err } keyHandlers = append(keyHandlers, handler) } //nolint:scopelint slices.SortFunc(keyHandlers, func(a, b keys.Handler) int { return cmp.Compare(a.Slot(), b.Slot()) }) provider := luks.New( cipher, opts..., ) return &Handler{ encryptionProvider: provider, keyHandlers: keyHandlers, saltGetter: helpers.SaltGetter, }, nil } // Handler reads encryption config, creates appropriate // encryption provider, handles encrypted partition open and close. type Handler struct { encryptionProvider encryption.Provider keyHandlers []keys.Handler saltGetter helpers.SaltGetter } // Name returns the name of the handler. func (h *Handler) Name() string { return block.EncryptionProviderLUKS2.String() } // Open encrypted partition. func (h *Handler) Open(ctx context.Context, logger *zap.Logger, devicePath, mappedName string) (string, int, []string, error) { isOpen, path, err := h.encryptionProvider.IsOpen(ctx, devicePath, mappedName) if err != nil { return "", -1, nil, err } var usedKey *encryption.Key if !isOpen { handler, key, _, err := h.tryHandlers(ctx, logger, func(ctx context.Context, handler keys.Handler) (*encryption.Key, token.Token, error) { slotToken, err := h.readToken(ctx, devicePath, handler.Slot()) if err != nil { return nil, nil, err } slotKey, err := handler.GetKey(ctx, slotToken) if err != nil { return nil, nil, err } // try to open with the key, if it fails, tryHandlers will try the next handler path, err = h.encryptionProvider.Open(ctx, devicePath, mappedName, slotKey) if err != nil { return nil, nil, err } return slotKey, slotToken, nil }) if err != nil { return "", -1, nil, err } logger.Info("opened encrypted device", zap.Int("slot", handler.Slot()), zap.String("type", fmt.Sprintf("%T", handler))) usedKey = key } failedSyncs, err := h.syncKeys(ctx, logger, devicePath, usedKey) if err != nil { return "", -1, nil, err } return path, usedKey.Slot, failedSyncs, nil } // Close encrypted partition. func (h *Handler) Close(ctx context.Context, encryptedPath string) error { if err := h.encryptionProvider.Close(ctx, encryptedPath); err != nil { if errors.Is(err, encryption.ErrDeviceNotReady) { return nil } return fmt.Errorf("error closing %s: %w", encryptedPath, err) } return nil } // FormatAndEncrypt formats and encrypts the volume. func (h *Handler) FormatAndEncrypt(ctx context.Context, logger *zap.Logger, path string) error { _, key, token, err := h.tryHandlers(ctx, logger, func(ctx context.Context, h keys.Handler) (*encryption.Key, token.Token, error) { return h.NewKey(ctx) }) if err != nil { return err } if err = h.encryptionProvider.Encrypt(ctx, path, key); err != nil { return err } if token != nil { if err = h.encryptionProvider.SetToken(ctx, path, key.Slot, token); err != nil { return err } } for _, handler := range h.keyHandlers { if handler.Slot() == key.Slot { continue } if err := h.addKey(ctx, path, key, handler); err != nil { return err } } return nil } //nolint:gocyclo func (h *Handler) syncKeys(ctx context.Context, logger *zap.Logger, path string, k *encryption.Key) ([]string, error) { keyslots, err := h.encryptionProvider.ReadKeyslots(path) if err != nil { return nil, err } var failedSyncs []string visited := map[string]bool{} for _, handler := range h.keyHandlers { slot := strconv.Itoa(handler.Slot()) visited[slot] = true // no need to update the key which we already detected as unchanged if k != nil && k.Slot == handler.Slot() { continue } // keyslot exists if _, ok := keyslots.Keyslots[slot]; ok { if err = h.updateKey(ctx, path, k, handler); err != nil { logger.Error("failed to update key", zap.Int("slot", handler.Slot()), zap.String("handler", fmt.Sprintf("%T", handler)), zap.Error(err)) failedSyncs = append(failedSyncs, fmt.Sprintf("error updating key slot %s %T: %s", slot, handler, err)) } else { logger.Info("updated encryption key", zap.Int("slot", handler.Slot()), zap.String("handler", fmt.Sprintf("%T", handler))) } } else { // keyslot does not exist so just add the key if err = h.addKey(ctx, path, k, handler); err != nil { logger.Error("failed to add key", zap.Int("slot", handler.Slot()), zap.String("handler", fmt.Sprintf("%T", handler)), zap.Error(err)) failedSyncs = append(failedSyncs, fmt.Sprintf("error adding key slot %s %T: %s", slot, handler, err)) } else { logger.Info("added encryption key", zap.Int("slot", handler.Slot()), zap.String("handler", fmt.Sprintf("%T", handler))) } } } // cleanup deleted key slots for slot := range keyslots.Keyslots { if !visited[slot] { s, err := strconv.ParseInt(slot, 10, 64) if err != nil { return nil, err } if err = h.encryptionProvider.RemoveKey(ctx, path, int(s), k); err != nil { logger.Error("failed to remove key", zap.Int("slot", int(s)), zap.Error(err)) failedSyncs = append(failedSyncs, fmt.Sprintf("error removing key slot %s: %s", slot, err)) } else { logger.Info("removed encryption key", zap.Int("slot", k.Slot)) } } } return failedSyncs, nil } func (h *Handler) updateKey(ctx context.Context, path string, existingKey *encryption.Key, handler keys.Handler) error { valid, err := h.checkKey(ctx, path, handler) if err != nil { return err } if valid { return nil } // re-add the key to the slot err = h.encryptionProvider.RemoveKey(ctx, path, handler.Slot(), existingKey) if err != nil { return fmt.Errorf("failed to drop old key during key update %w", err) } err = h.addKey(ctx, path, existingKey, handler) if err != nil { return fmt.Errorf("failed to add new key during key update %w", err) } return err } func (h *Handler) checkKey(ctx context.Context, path string, handler keys.Handler) (bool, error) { token, err := h.readToken(ctx, path, handler.Slot()) if err != nil { return false, err } key, err := handler.GetKey(ctx, token) if err != nil { if errors.Is(err, keys.ErrTokenInvalid) { return false, nil } return false, err } return h.encryptionProvider.CheckKey(ctx, path, key) } func (h *Handler) addKey(ctx context.Context, path string, existingKey *encryption.Key, handler keys.Handler) error { key, token, err := handler.NewKey(ctx) if err != nil { return err } if token != nil { if err = h.encryptionProvider.SetToken(ctx, path, key.Slot, token); err != nil { return err } } err = h.encryptionProvider.AddKey(ctx, path, existingKey, key) if err != nil { return fmt.Errorf("failed to add new key during key update %w", err) } return nil } // tryHandlers tries to get encryption keys from all available handlers. // // It returns the first handler that successfully returns a key. func (h *Handler) tryHandlers( ctx context.Context, logger *zap.Logger, cb func(ctx context.Context, h keys.Handler) (*encryption.Key, token.Token, error), ) ( keys.Handler, *encryption.Key, token.Token, error, ) { if len(h.keyHandlers) == 0 { return nil, nil, nil, errors.New("no encryption keys found") } callback := func(ctx context.Context, h keys.Handler) (*encryption.Key, token.Token, error) { ctx, cancel := context.WithTimeout(ctx, keyHandlerTimeout) defer cancel() return cb(ctx, h) } var errs error for _, h := range h.keyHandlers { key, token, err := callback(ctx, h) if err != nil { errs = multierror.Append(errs, err) logger.Warn("failed to call key handler", zap.Int("slot", h.Slot()), zap.String("handler", fmt.Sprintf("%T", h)), zap.Error(err)) continue } return h, key, token, nil } return nil, nil, nil, fmt.Errorf("no handlers available to get encryption keys from: %w", errs) } func (h *Handler) readToken(ctx context.Context, path string, id int) (token.Token, error) { token := luks.Token[json.RawMessage]{} err := h.encryptionProvider.ReadToken(ctx, path, id, &token) if err != nil { if errors.Is(err, encryption.ErrTokenNotFound) { return nil, nil //nolint:nilnil } return nil, err } switch token.Type { case keys.TokenTypeKMS: kmsData := &keys.KMSToken{} if err = json.Unmarshal(token.UserData, &kmsData); err != nil { return nil, err } return &luks.Token[*keys.KMSToken]{ Type: token.Type, UserData: kmsData, }, nil case keys.TokenTypeTPM: tpmData := &keys.TPMToken{} if err = json.Unmarshal(token.UserData, &tpmData); err != nil { return nil, err } return &luks.Token[*keys.TPMToken]{ Type: token.Type, UserData: tpmData, }, nil default: return nil, fmt.Errorf("unknown token type %s", token.Type) } } ================================================ FILE: internal/pkg/encryption/encryption_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encryption_test import ( "testing" ) func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: internal/pkg/encryption/helpers/helpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package helpers defines encryption handlers. package helpers import ( "context" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) // SystemInformationGetter defines the closure which can be used in key handlers to get the node UUID. type SystemInformationGetter func(context.Context) (*hardware.SystemInformation, error) // TPMLockFunc is a function that ensures that the TPM is locked and PCR state is as expected. type TPMLockFunc func(context.Context, func() error) error // SaltGetter defines the closure which can be used in key handlers to get the encryption salt. type SaltGetter func(context.Context) ([]byte, error) ================================================ FILE: internal/pkg/encryption/keys/keys.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package keys contains various encryption KeyHandler implementations. package keys import ( "context" "errors" "fmt" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) var errNoSystemInfoGetter = errors.New("the UUID getter is not set") // NewHandler key using provided config. // //nolint:gocyclo func NewHandler(cfg block.EncryptionKey, options ...KeyOption) (Handler, error) { opts, err := NewDefaultOptions(options) if err != nil { return nil, err } key := KeyHandler{slot: cfg.Slot} var handler Handler switch cfg.Type { case block.EncryptionKeyStatic: k := cfg.StaticPassphrase if k == nil { return nil, errors.New("static key must have key data defined") } handler = NewStaticKeyHandler(key, k) case block.EncryptionKeyNodeID: if opts.GetSystemInformation == nil { return nil, fmt.Errorf("failed to create nodeUUID key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) } handler = NewNodeIDKeyHandler(key, opts.VolumeID, opts.GetSystemInformation) case block.EncryptionKeyKMS: if opts.GetSystemInformation == nil { return nil, fmt.Errorf("failed to create KMS key handler at slot %d: %w", cfg.Slot, errNoSystemInfoGetter) } handler, err = NewKMSKeyHandler(key, cfg.KMSEndpoint, opts.GetSystemInformation) if err != nil { return nil, err } case block.EncryptionKeyTPM: if opts.TPMLocker == nil { return nil, fmt.Errorf("failed to create TPM key handler at slot %d: no TPM lock function", cfg.Slot) } handler, err = NewTPMKeyHandler( key, cfg.TPMCheckSecurebootStatusOnEnroll, cfg.TPMPCRs, opts.TPMLocker) if err != nil { return nil, err } default: return nil, fmt.Errorf("unsupported key type: %s", cfg.Type) } if cfg.LockToSTATE { if opts.SaltGetter == nil { return nil, fmt.Errorf("failed to create state-locked key handler at slot %d: no salt getter", cfg.Slot) } handler = NewSaltedHandler(handler, opts.SaltGetter) } return handler, nil } // Handler manages key lifecycle. type Handler interface { NewKey(context.Context) (*encryption.Key, token.Token, error) GetKey(context.Context, token.Token) (*encryption.Key, error) Slot() int } // KeyHandler is the base class for all key handlers. type KeyHandler struct { slot int } // Slot implements Handler interface. func (k *KeyHandler) Slot() int { return k.slot } // ErrTokenInvalid is returned by the keys handler if the supplied token is not valid. var ErrTokenInvalid = errors.New("invalid token") ================================================ FILE: internal/pkg/encryption/keys/kms.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import ( "context" "crypto/rand" "crypto/tls" "encoding/base64" "fmt" "io" "time" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/luks" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/kms-client/api/kms" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" "github.com/siderolabs/talos/internal/pkg/endpoint" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/client/dialer" ) // KMSToken is the userdata stored in the partition token metadata. type KMSToken struct { SealedData []byte `json:"sealedData"` } // KMSKeyHandler seals token using KMS service. type KMSKeyHandler struct { KeyHandler kmsEndpoint string getSystemInfo helpers.SystemInformationGetter } // NewKMSKeyHandler creates new KMSKeyHandler. func NewKMSKeyHandler(key KeyHandler, kmsEndpoint string, getSystemInfo helpers.SystemInformationGetter) (*KMSKeyHandler, error) { return &KMSKeyHandler{ KeyHandler: key, kmsEndpoint: kmsEndpoint, getSystemInfo: getSystemInfo, }, nil } // NewKey implements Handler interface. func (h *KMSKeyHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() conn, err := h.getConn() if err != nil { return nil, nil, fmt.Errorf("error dialing KMS endpoint %q: %w", h.kmsEndpoint, err) } defer conn.Close() //nolint:errcheck client := kms.NewKMSServiceClient(conn) key := make([]byte, 32) if _, err = io.ReadFull(rand.Reader, key); err != nil { return nil, nil, err } systemInformation, err := h.getSystemInfo(ctx) if err != nil { return nil, nil, err } resp, err := client.Seal(ctx, &kms.Request{ NodeUuid: systemInformation.TypedSpec().UUID, Data: key, }) if err != nil { return nil, nil, fmt.Errorf("failed to seal KMS passphrase, slot %d: %w", h.Slot(), err) } token := &luks.Token[*KMSToken]{ Type: TokenTypeKMS, UserData: &KMSToken{ SealedData: resp.Data, }, } return encryption.NewKey(h.slot, []byte(base64.StdEncoding.EncodeToString(key))), token, nil } // GetKey implements Handler interface. func (h *KMSKeyHandler) GetKey(ctx context.Context, t token.Token) (*encryption.Key, error) { token, ok := t.(*luks.Token[*KMSToken]) if !ok { return nil, ErrTokenInvalid } ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() conn, err := h.getConn() if err != nil { return nil, fmt.Errorf("error dialing KMS endpoint %q: %w", h.kmsEndpoint, err) } defer conn.Close() //nolint:errcheck client := kms.NewKMSServiceClient(conn) systemInformation, err := h.getSystemInfo(ctx) if err != nil { return nil, err } resp, err := client.Unseal(ctx, &kms.Request{ NodeUuid: systemInformation.TypedSpec().UUID, Data: token.UserData.SealedData, }) if err != nil { return nil, fmt.Errorf("failed to unseal KMS passphrase, slot %d: %w", h.Slot(), err) } return encryption.NewKey(h.slot, []byte(base64.StdEncoding.EncodeToString(resp.Data))), nil } func (h *KMSKeyHandler) getConn() (*grpc.ClientConn, error) { var transportCredentials credentials.TransportCredentials endpoint, err := endpoint.Parse(h.kmsEndpoint) if err != nil { return nil, err } if endpoint.Insecure { transportCredentials = insecure.NewCredentials() } else { transportCredentials = credentials.NewTLS(&tls.Config{ RootCAs: httpdefaults.RootCAs(), }) } return grpc.NewClient( endpoint.Host, grpc.WithTransportCredentials(transportCredentials), grpc.WithSharedWriteBuffer(true), grpc.WithContextDialer(dialer.DynamicProxyDialerWithTLSConfig(httpdefaults.RootCAsTLSConfig)), ) } ================================================ FILE: internal/pkg/encryption/keys/nodeid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import ( "context" "fmt" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" ) // NodeIDKeyHandler generates the key based on current node information // and provided template string. type NodeIDKeyHandler struct { KeyHandler partitionLabel string getSystemInfo helpers.SystemInformationGetter } // NewNodeIDKeyHandler creates new NodeIDKeyHandler. func NewNodeIDKeyHandler(key KeyHandler, partitionLabel string, systemInfoGetter helpers.SystemInformationGetter) *NodeIDKeyHandler { return &NodeIDKeyHandler{ KeyHandler: key, partitionLabel: partitionLabel, getSystemInfo: systemInfoGetter, } } // NewKey implements Handler interface. func (h *NodeIDKeyHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) { k, err := h.GetKey(ctx, nil) return k, nil, err } // GetKey implements Handler interface. func (h *NodeIDKeyHandler) GetKey(ctx context.Context, _ token.Token) (*encryption.Key, error) { systemInformation, err := h.getSystemInfo(ctx) if err != nil { return nil, fmt.Errorf("failed to get UUID: %w", err) } nodeUUID := systemInformation.TypedSpec().UUID if nodeUUID == "" { return nil, fmt.Errorf("machine UUID is not populated %s", nodeUUID) } // primitive entropy check counts := map[rune]int{} for _, s := range nodeUUID { counts[s]++ if counts[s] > len(nodeUUID)/2 { return nil, fmt.Errorf("machine UUID %s entropy check failed", nodeUUID) } } return encryption.NewKey(h.slot, []byte(nodeUUID+h.partitionLabel)), nil } ================================================ FILE: internal/pkg/encryption/keys/nodeid_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys_test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/encryption/keys" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) func TestNodeID(t *testing.T) { t.Parallel() handler := keys.NewNodeIDKeyHandler(keys.KeyHandler{}, "test", func(context.Context) (*hardware.SystemInformation, error) { res := hardware.NewSystemInformation(hardware.SystemInformationID) res.TypedSpec().UUID = "12345678-1234-5678-1234-567812345678" return res, nil }) key, token, err := handler.NewKey(t.Context()) require.NoError(t, err) require.Nil(t, token) assert.Equal(t, "12345678-1234-5678-1234-567812345678test", string(key.Value)) key, err = handler.GetKey(t.Context(), nil) require.NoError(t, err) assert.Equal(t, "12345678-1234-5678-1234-567812345678test", string(key.Value)) } func TestNodeIDBadEntropy(t *testing.T) { t.Parallel() handler := keys.NewNodeIDKeyHandler(keys.KeyHandler{}, "test", func(context.Context) (*hardware.SystemInformation, error) { res := hardware.NewSystemInformation(hardware.SystemInformationID) res.TypedSpec().UUID = "11111111-0000-1111-1111-111111111111" // bad entropy return res, nil }) _, _, err := handler.NewKey(t.Context()) require.Error(t, err) assert.EqualError(t, err, "machine UUID 11111111-0000-1111-1111-111111111111 entropy check failed") } ================================================ FILE: internal/pkg/encryption/keys/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import "github.com/siderolabs/talos/internal/pkg/encryption/helpers" // KeyOption represents key option callback used in KeyHandler.GetKey func. type KeyOption func(o *KeyOptions) error // KeyOptions set of options to be used in KeyHandler.GetKey func. type KeyOptions struct { VolumeID string GetSystemInformation helpers.SystemInformationGetter TPMLocker helpers.TPMLockFunc SaltGetter helpers.SaltGetter } // WithVolumeID passes the partition label to the key handler. func WithVolumeID(label string) KeyOption { return func(o *KeyOptions) error { o.VolumeID = label return nil } } // WithSystemInformationGetter passes the node UUID to the key handler. func WithSystemInformationGetter(getter helpers.SystemInformationGetter) KeyOption { return func(o *KeyOptions) error { o.GetSystemInformation = getter return nil } } // WithTPMLocker passes the TPM locker to the key handler. func WithTPMLocker(locker helpers.TPMLockFunc) KeyOption { return func(o *KeyOptions) error { o.TPMLocker = locker return nil } } // WithSaltGetter passes the salt getter to the key handler. func WithSaltGetter(getter helpers.SaltGetter) KeyOption { return func(o *KeyOptions) error { o.SaltGetter = getter return nil } } // NewDefaultOptions creates new KeyOptions. func NewDefaultOptions(options []KeyOption) (*KeyOptions, error) { var opts KeyOptions for _, o := range options { err := o(&opts) if err != nil { return nil, err } } return &opts, nil } ================================================ FILE: internal/pkg/encryption/keys/salted.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import ( "context" "fmt" "slices" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" ) // SaltedHandler is a key handler wrapper that salts the key with a provided random salt. type SaltedHandler struct { wrapped Handler saltGetter helpers.SaltGetter } // NewSaltedHandler creates a new handler that wraps the provided key handler and uses the provided salt getter. func NewSaltedHandler(wrapped Handler, saltGetter helpers.SaltGetter) Handler { return &SaltedHandler{ wrapped: wrapped, saltGetter: saltGetter, } } // NewKey implements the keys.Handler interface. func (k *SaltedHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) { key, token, err := k.wrapped.NewKey(ctx) if err != nil { return key, token, err } salt, err := k.saltGetter(ctx) if err != nil { return nil, nil, fmt.Errorf("failed to get disk encryption key salt: %w", err) } key.Value = slices.Concat(key.Value, salt) return key, token, nil } // GetKey implements the keys.Handler interface. func (k *SaltedHandler) GetKey(ctx context.Context, token token.Token) (*encryption.Key, error) { key, err := k.wrapped.GetKey(ctx, token) if err != nil { return key, err } salt, err := k.saltGetter(ctx) if err != nil { return nil, fmt.Errorf("failed to get disk encryption key salt: %w", err) } key.Value = slices.Concat(key.Value, salt) return key, nil } // Slot implements the keys.Handler interface. func (k *SaltedHandler) Slot() int { return k.wrapped.Slot() } ================================================ FILE: internal/pkg/encryption/keys/salted_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys_test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/encryption/keys" ) func TestSalted(t *testing.T) { t.Parallel() const ( secret = "topsecret" salt = "salted" ) inner := keys.NewStaticKeyHandler(keys.KeyHandler{}, []byte(secret)) handler := keys.NewSaltedHandler(inner, func(context.Context) ([]byte, error) { return []byte(salt), nil }) key, token, err := handler.NewKey(t.Context()) require.NoError(t, err) require.Nil(t, token) assert.Equal(t, secret+salt, string(key.Value)) key, err = handler.GetKey(t.Context(), nil) require.NoError(t, err) assert.Equal(t, secret+salt, string(key.Value)) } ================================================ FILE: internal/pkg/encryption/keys/static.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import ( "context" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/token" ) // StaticKeyHandler just handles the static key value all the time. type StaticKeyHandler struct { KeyHandler data []byte } // NewStaticKeyHandler creates new EphemeralKeyHandler. func NewStaticKeyHandler(key KeyHandler, data []byte) *StaticKeyHandler { return &StaticKeyHandler{ KeyHandler: key, data: data, } } // NewKey implements Handler interface. func (h *StaticKeyHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) { k, err := h.GetKey(ctx, nil) return k, nil, err } // GetKey implements Handler interface. func (h *StaticKeyHandler) GetKey(context.Context, token.Token) (*encryption.Key, error) { return encryption.NewKey(h.slot, h.data), nil } ================================================ FILE: internal/pkg/encryption/keys/static_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/encryption/keys" ) func TestStatic(t *testing.T) { t.Parallel() const secret = "topsecret" handler := keys.NewStaticKeyHandler(keys.KeyHandler{}, []byte(secret)) key, token, err := handler.NewKey(t.Context()) require.NoError(t, err) require.Nil(t, token) assert.Equal(t, secret, string(key.Value)) key1, err := handler.GetKey(t.Context(), nil) require.NoError(t, err) assert.Equal(t, key, key1) } ================================================ FILE: internal/pkg/encryption/keys/token.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys const ( // TokenTypeKMS is KMS assisted encryption token. TokenTypeKMS = "sideroKMS" // TokenTypeTPM is TPM assisted encryption token. TokenTypeTPM = "talos-tpm2" ) ================================================ FILE: internal/pkg/encryption/keys/tpm2.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package keys import ( "context" "crypto/rand" "encoding/base64" "fmt" "io" "github.com/foxboron/go-uefi/efi" "github.com/siderolabs/go-blockdevice/v2/encryption" "github.com/siderolabs/go-blockdevice/v2/encryption/luks" "github.com/siderolabs/go-blockdevice/v2/encryption/token" "github.com/siderolabs/talos/internal/pkg/encryption/helpers" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" ) // TPMToken is the userdata stored in the partition token metadata. type TPMToken struct { KeySlots []int `json:"keyslots"` SealedBlobPrivate []byte `json:"sealed_blob_private"` SealedBlobPublic []byte `json:"sealed_blob_public"` PCRs []int `json:"pcrs,omitempty"` PubKeyPCRs []int `json:"pubkey_pcrs"` Alg string `json:"alg"` EncryptionVersion string `json:"encryption_version,omitempty"` PolicyHash []byte `json:"policy_hash"` KeyName []byte `json:"key_name"` } // TPMKeyHandler seals token using TPM. type TPMKeyHandler struct { KeyHandler tpmLocker helpers.TPMLockFunc checkSecurebootOnEnroll bool tpmPCRs []int } // NewTPMKeyHandler creates new TPMKeyHandler. func NewTPMKeyHandler(key KeyHandler, checkSecurebootOnEnroll bool, tpmPCRs []int, tpmLocker helpers.TPMLockFunc) (*TPMKeyHandler, error) { return &TPMKeyHandler{ KeyHandler: key, tpmLocker: tpmLocker, checkSecurebootOnEnroll: checkSecurebootOnEnroll, tpmPCRs: tpmPCRs, }, nil } // NewKey implements Handler interface. func (h *TPMKeyHandler) NewKey(ctx context.Context) (*encryption.Key, token.Token, error) { if h.checkSecurebootOnEnroll { if !efi.GetSecureBoot() { return nil, nil, fmt.Errorf("failed to enroll the TPM2 key, as SecureBoot is disabled (and checkSecurebootOnEnroll is enabled)") } if efi.GetSetupMode() { return nil, nil, fmt.Errorf("failed to enroll the TPM2 key, as the system is in SecureBoot setup mode (and checkSecurebootOnEnroll is enabled)") } } key := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, key); err != nil { return nil, nil, err } var resp *tpm2.SealedResponse if err := h.tpmLocker(ctx, func() error { var err error resp, err = tpm2.Seal(key, h.tpmPCRs) return err }); err != nil { return nil, nil, err } token := &luks.Token[*TPMToken]{ Type: TokenTypeTPM, UserData: &TPMToken{ KeySlots: []int{h.slot}, SealedBlobPrivate: resp.SealedBlobPrivate, SealedBlobPublic: resp.SealedBlobPublic, Alg: resp.Alg, EncryptionVersion: resp.EncryptionVersion, PubKeyPCRs: resp.PubKeyPCRs, PCRs: resp.PCRs, PolicyHash: resp.PolicyDigest, KeyName: resp.KeyName, }, } return encryption.NewKey(h.slot, []byte(base64.StdEncoding.EncodeToString(key))), token, nil } // GetKey implements Handler interface. func (h *TPMKeyHandler) GetKey(ctx context.Context, t token.Token) (*encryption.Key, error) { token, ok := t.(*luks.Token[*TPMToken]) if !ok { return nil, ErrTokenInvalid } sealed := tpm2.SealedResponse{ SealedBlobPrivate: token.UserData.SealedBlobPrivate, SealedBlobPublic: token.UserData.SealedBlobPublic, PolicyDigest: token.UserData.PolicyHash, KeyName: token.UserData.KeyName, PCRs: token.UserData.PCRs, PubKeyPCRs: token.UserData.PubKeyPCRs, EncryptionVersion: token.UserData.EncryptionVersion, } var key []byte if err := h.tpmLocker(ctx, func() error { var err error key, err = tpm2.Unseal(sealed) return err }); err != nil { return nil, err } return encryption.NewKey(h.slot, []byte(base64.StdEncoding.EncodeToString(key))), nil } ================================================ FILE: internal/pkg/encryption/node_params.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encryption // NodeParams contains node information relevant for the encryption handler. type NodeParams struct { UUID string } ================================================ FILE: internal/pkg/endpoint/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package endpoint has common tools for parsing http API endpoints. package endpoint import ( "net/url" "regexp" ) var urlSchemeMatcher = regexp.MustCompile(`[a-zA-z]+://`) // Endpoint defines all params parsed from the API endpoint. type Endpoint struct { Host string Insecure bool params url.Values } // Parse parses the endpoint from string. func Parse(sideroLinkParam string) (Endpoint, error) { if !urlSchemeMatcher.MatchString(sideroLinkParam) { sideroLinkParam = "grpc://" + sideroLinkParam } u, err := url.Parse(sideroLinkParam) if err != nil { return Endpoint{}, err } result := Endpoint{ Host: u.Host, Insecure: u.Scheme == "grpc", params: u.Query(), } if u.Port() == "" && u.Scheme == "https" { result.Host += ":443" } return result, nil } // GetParam reads param from the query. func (e *Endpoint) GetParam(name string) string { return e.params.Get(name) } ================================================ FILE: internal/pkg/endpoint/endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package endpoint_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/endpoint" ) func TestParseEndpoint(t *testing.T) { t.Run("parses a join token from a complete URL without error", func(t *testing.T) { // when endpoint, err := endpoint.Parse("grpc://10.5.0.2:3445?jointoken=ttt") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:3445", endpoint.Host) assert.True(t, endpoint.Insecure) assert.Equal(t, "ttt", endpoint.GetParam("jointoken")) }) t.Run("parses a join token from a secure URL without error", func(t *testing.T) { // when endpoint, err := endpoint.Parse("https://10.5.0.2:3445?jointoken=ttt&jointoken=xxx") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:3445", endpoint.Host) assert.False(t, endpoint.Insecure) assert.Equal(t, "ttt", endpoint.GetParam("jointoken")) }) t.Run("parses a join token from a secure URL without port", func(t *testing.T) { // when endpoint, err := endpoint.Parse("https://10.5.0.2?jointoken=ttt&jointoken=xxx") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:443", endpoint.Host) assert.False(t, endpoint.Insecure) assert.Equal(t, "ttt", endpoint.GetParam("jointoken")) }) t.Run("parses a join token from an URL without a scheme", func(t *testing.T) { // when endpoint, err := endpoint.Parse("10.5.0.2:3445?jointoken=ttt") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:3445", endpoint.Host) assert.True(t, endpoint.Insecure) assert.Equal(t, "ttt", endpoint.GetParam("jointoken")) }) t.Run("does not error if there is no join token in a complete URL", func(t *testing.T) { // when endpoint, err := endpoint.Parse("grpc://10.5.0.2:3445") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:3445", endpoint.Host) assert.True(t, endpoint.Insecure) assert.Equal(t, "", endpoint.GetParam("jointoken")) }) t.Run("does not error if there is no join token in an URL without a scheme", func(t *testing.T) { // when endpoint, err := endpoint.Parse("10.5.0.2:3445") // then assert.NoError(t, err) assert.Equal(t, "10.5.0.2:3445", endpoint.Host) assert.True(t, endpoint.Insecure) assert.Equal(t, "", endpoint.GetParam("jointoken")) }) } ================================================ FILE: internal/pkg/environment/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package environment provides a set of functions to get environment variables. package environment import ( "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Get the desired set of the environment variables based on kernel cmdline and machine config. // // The returned value is a list of strings in the form of "key=value". func Get(cfg config.Config) []string { return GetCmdline(procfs.ProcCmdline(), cfg) } // GetCmdline the desired set of the environment variables based on kernel cmdline. func GetCmdline(cmdline *procfs.Cmdline, cfg config.Config) []string { var result []string param := cmdline.Get(constants.KernelParamEnvironment) for idx := 0; ; idx++ { val := param.Get(idx) if val == nil { break } result = append(result, *val) } if cfg != nil { if cfg.Machine() != nil { for k, v := range cfg.Machine().Env() { result = append(result, k+"="+v) } } if cfg.Environment() != nil { for k, v := range cfg.Environment().Variables() { result = append(result, k+"="+v) } } } return result } ================================================ FILE: internal/pkg/environment/environment_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package environment_test import ( "testing" "github.com/siderolabs/go-procfs/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestGet(t *testing.T) { t.Parallel() for _, test := range []struct { name string cmdline string cfg map[string]string expected []string }{ { name: "empty", }, { name: "machine config only", cfg: map[string]string{ "http_proxy": "http://proxy.example.com:8080", }, expected: []string{ "http_proxy=http://proxy.example.com:8080", }, }, { name: "cmdline only", cmdline: "talos.environment=foo=bar talos.environment=bar=baz", expected: []string{ "foo=bar", "bar=baz", }, }, { name: "cmdline and machine config", cmdline: "talos.environment=foo=bar", cfg: map[string]string{ "http_proxy": "http://proxy.example.com:8080", }, expected: []string{ "foo=bar", "http_proxy=http://proxy.example.com:8080", }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cmdline := procfs.NewCmdline(test.cmdline) var cfg config.Config if test.cfg != nil { var err error cfg, err = container.New(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineEnv: test.cfg, }, }) require.NoError(t, err) } result := environment.GetCmdline(cmdline, cfg) assert.Equal(t, test.expected, result) }) } } ================================================ FILE: internal/pkg/etcd/certs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( stdlibx509 "crypto/x509" "fmt" "net" "net/netip" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) // CertificateGenerator contains etcd certificate options. type CertificateGenerator struct { CA *x509.PEMEncodedCertificateAndKey NodeAddresses *network.NodeAddress HostnameStatus *network.HostnameStatus } // buildOptions set common certificate options. func (gen *CertificateGenerator) buildOptions(autoSANs, includeLocalhost bool) []x509.Option { addresses := gen.NodeAddresses.TypedSpec().IPs() if includeLocalhost { addresses = append(addresses, netip.MustParseAddr("127.0.0.1"), netip.MustParseAddr("::1"), ) } hostname := gen.HostnameStatus.TypedSpec().Hostname dnsNames := gen.HostnameStatus.TypedSpec().DNSNames() if includeLocalhost { dnsNames = append(dnsNames, "localhost") } result := []x509.Option{ x509.NotAfter(time.Now().Add(87600 * time.Hour)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature | stdlibx509.KeyUsageKeyEncipherment), } if autoSANs { result = append(result, x509.CommonName(hostname), x509.DNSNames(dnsNames), x509.IPAddresses(xslices.Map(addresses, func(addr netip.Addr) net.IP { return addr.AsSlice() })), ) } return result } // GeneratePeerCert generates etcd peer certificate and key from etcd CA. func (gen *CertificateGenerator) GeneratePeerCert() (*x509.PEMEncodedCertificateAndKey, error) { opts := gen.buildOptions(true, false) opts = append(opts, x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, stdlibx509.ExtKeyUsageClientAuth, }), ) ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(gen.CA) if err != nil { return nil, fmt.Errorf("failed loading CA from config: %w", err) } keyPair, err := x509.NewKeyPair(ca, opts...) if err != nil { return nil, fmt.Errorf("failed generating peer key pair: %w", err) } return x509.NewCertificateAndKeyFromKeyPair(keyPair), nil } // GenerateServerCert generates server etcd certificate and key from etcd CA. func (gen *CertificateGenerator) GenerateServerCert() (*x509.PEMEncodedCertificateAndKey, error) { opts := gen.buildOptions(true, true) opts = append(opts, x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageServerAuth, stdlibx509.ExtKeyUsageClientAuth, }), ) ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(gen.CA) if err != nil { return nil, fmt.Errorf("failed loading CA from config: %w", err) } keyPair, err := x509.NewKeyPair(ca, opts...) if err != nil { return nil, fmt.Errorf("failed generating client key pair: %w", err) } return x509.NewCertificateAndKeyFromKeyPair(keyPair), nil } // GenerateClientCert generates client certificate and key from etcd CA. func (gen *CertificateGenerator) GenerateClientCert(commonName string) (*x509.PEMEncodedCertificateAndKey, error) { opts := gen.buildOptions(false, false) opts = append(opts, x509.CommonName(commonName)) opts = append(opts, x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageClientAuth, }), ) ca, err := x509.NewCertificateAuthorityFromCertificateAndKey(gen.CA) if err != nil { return nil, fmt.Errorf("failed loading CA from config: %w", err) } keyPair, err := x509.NewKeyPair(ca, opts...) if err != nil { return nil, fmt.Errorf("failed generating client key pair: %w", err) } return x509.NewCertificateAndKeyFromKeyPair(keyPair), nil } ================================================ FILE: internal/pkg/etcd/endpoints.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "errors" "fmt" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // GetEndpoints returns expected endpoints of etcd cluster members. // // It is not guaranteed that etcd is running on each listed endpoint. func GetEndpoints(ctx context.Context, resources state.State) ([]string, error) { endpointResources, err := safe.StateListAll[*k8s.Endpoint](ctx, resources) if err != nil { return nil, fmt.Errorf("error getting endpoints resources: %w", err) } var endpointAddrs k8s.EndpointList // merge all endpoints into a single list for res := range endpointResources.All() { endpointAddrs = endpointAddrs.Merge(res) } if endpointAddrs.IsEmpty() { return nil, errors.New("no controlplane endpoints discovered yet") } endpoints := endpointAddrs.Strings() // Etcd expects host:port format. for i := range endpoints { endpoints[i] = nethelpers.JoinHostPort(endpoints[i], constants.EtcdClientPort) } return endpoints, nil } ================================================ FILE: internal/pkg/etcd/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "errors" "fmt" "log" "math/rand/v2" "os" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/go-retry/retry" "go.etcd.io/etcd/api/v3/etcdserverpb" "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" "go.etcd.io/etcd/client/pkg/v3/transport" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" "google.golang.org/grpc" "github.com/siderolabs/talos/internal/app/machined/pkg/system" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" etcdresource "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) // QuorumCheckTimeout is the amount of time to allow for KV operations before quorum is declared invalid. const QuorumCheckTimeout = 15 * time.Second // Client is a wrapper around the official etcd client. type Client struct { *clientv3.Client } // NewClient initializes and returns an etcd client configured to talk to // a list of endpoints. func NewClient(ctx context.Context, endpoints []string, dialOpts ...grpc.DialOption) (client *Client, err error) { tlsInfo := transport.TLSInfo{ CertFile: constants.EtcdAdminCert, KeyFile: constants.EtcdAdminKey, TrustedCAFile: constants.EtcdCACert, } tlsConfig, err := tlsInfo.ClientConfig() if err != nil { return nil, fmt.Errorf("error building etcd client TLS config: %w", err) } c, err := clientv3.New(clientv3.Config{ Endpoints: endpoints, DialTimeout: 5 * time.Second, Context: ctx, DialOptions: append(dialOpts, grpc.WithSharedWriteBuffer(true)), TLS: tlsConfig, Logger: zap.NewNop(), }) if err != nil { return nil, fmt.Errorf("error building etcd client: %w", err) } return &Client{Client: c}, nil } // NewLocalClient initializes and returns etcd client configured to talk to localhost endpoint. func NewLocalClient(ctx context.Context, dialOpts ...grpc.DialOption) (client *Client, err error) { return NewClient( ctx, []string{nethelpers.JoinHostPort("localhost", constants.EtcdClientPort)}, dialOpts..., ) } // NewClientFromControlPlaneIPs initializes and returns an etcd client // configured to talk to all members. func NewClientFromControlPlaneIPs(ctx context.Context, resources state.State, dialOpts ...grpc.DialOption) (client *Client, err error) { endpoints, err := GetEndpoints(ctx, resources) if err != nil { return nil, err } // Shuffle endpoints to establish random order on each call to avoid patterns based on sorted IP list. rand.Shuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) return NewClient(ctx, endpoints, dialOpts...) } // ValidateForUpgrade validates the etcd cluster state to ensure that performing // an upgrade is safe. func (c *Client) ValidateForUpgrade(ctx context.Context, config config.Config) error { if config.Machine().Type() == machine.TypeWorker { return nil } resp, err := c.MemberList(ctx) if err != nil { return err } if len(resp.Members) == 2 { return fmt.Errorf("etcd member count(%d) is insufficient to maintain quorum if upgrade commences", len(resp.Members)) } for _, member := range resp.Members { // If the member is not started, the name will be an empty string. if len(member.Name) == 0 { return fmt.Errorf("etcd member %016x is not started, all members must be running to perform an upgrade", member.ID) } if err = validateMemberHealth(ctx, member.GetClientURLs()); err != nil { return fmt.Errorf("etcd member %016x is not healthy; all members must be healthy to perform an upgrade: %w", member.ID, err) } } return nil } // ValidateQuorum performs a KV operation to make certain that quorum is good. func (c *Client) ValidateQuorum(ctx context.Context) (err error) { // Get a random key. As long as we can get the response without an error, quorum is good. checkCtx, cancel := context.WithTimeout(ctx, QuorumCheckTimeout) defer cancel() _, err = c.Get(checkCtx, "health") if err == rpctypes.ErrPermissionDenied { // Permission denied is OK since proposal goes through consensus to get this error. err = nil } if err != nil { return err } return nil } func validateMemberHealth(ctx context.Context, memberURIs []string) (err error) { c, err := NewClient(ctx, memberURIs) if err != nil { return fmt.Errorf("failed to create client to member: %w", err) } return c.ValidateQuorum(ctx) } // LeaveCluster removes the current member from the etcd cluster and nukes etcd data directory. func (c *Client) LeaveCluster(ctx context.Context, st state.State) error { memberID, err := GetLocalMemberID(ctx, st) if err != nil { return err } if err := retry.Constant(5*time.Minute, retry.WithUnits(10*time.Second)).RetryWithContext(ctx, func(ctx context.Context) error { err := c.RemoveMemberByMemberID(ctx, memberID) if err == nil { return nil } if errors.Is(err, rpctypes.ErrUnhealthy) { // unhealthy is returned when the member hasn't established connections with quorum other members return retry.ExpectedError(err) } if errors.Is(err, rpctypes.ErrStopped) { // retry the stopped errors as the member might be in the process of shutting down return retry.ExpectedError(err) } if errors.Is(err, rpctypes.ErrMemberNotFound) { // already removed, nothing to do return nil } return err }); err != nil { return err } if err := system.Services(nil).Stop(ctx, "etcd"); err != nil { return fmt.Errorf("failed to stop etcd: %w", err) } // Once the member is removed, the data is no longer valid. if err := os.RemoveAll(constants.EtcdDataPath); err != nil { return fmt.Errorf("failed to remove %s: %w", constants.EtcdDataPath, err) } return nil } // GetMemberID returns the member ID of the node client is connected to. func (c *Client) GetMemberID(ctx context.Context) (uint64, error) { resp, err := c.Client.Maintenance.AlarmList(ctx) if err != nil { return 0, err } return resp.Header.MemberId, nil } // RemoveMemberByMemberID removes the member from the etcd cluster. func (c *Client) RemoveMemberByMemberID(ctx context.Context, memberID uint64) error { _, err := c.MemberRemove(ctx, memberID) if err != nil { return fmt.Errorf("failed to remove member %d: %w", memberID, err) } return nil } // ForfeitLeadership transfers leadership from the current member to another // member. // //nolint:gocyclo func (c *Client) ForfeitLeadership(ctx context.Context, memberID string) (string, error) { resp, err := c.MemberList(ctx) if err != nil { return "", fmt.Errorf("failed to list etcd members: %w", err) } if len(resp.Members) == 1 { return "", errors.New("cannot forfeit leadership, only one member") } var member *etcdserverpb.Member memberIDUint64, err := etcdresource.ParseMemberID(memberID) if err != nil { return "", err } for _, m := range resp.Members { if m.ID == memberIDUint64 { member = m break } } if member == nil { return "", fmt.Errorf("failed to find %q in list of etcd members", memberID) } for _, ep := range member.GetClientURLs() { var status *clientv3.StatusResponse status, err = c.Status(ctx, ep) if err != nil { return "", err } if status.Leader != member.GetID() { return "", nil } for _, m := range resp.Members { if m.GetID() != member.GetID() { log.Printf("moving leadership from %q to %q", member.GetName(), m.GetName()) conn, err := c.Dial(ep) if err != nil { return "", err } maintenance := clientv3.NewMaintenanceFromMaintenanceClient(clientv3.RetryMaintenanceClient(c.Client, conn), c.Client) _, err = maintenance.MoveLeader(ctx, m.GetID()) if err != nil { if errors.Is(err, rpctypes.ErrNotLeader) { // this member is not a leader anymore, so nothing to be done for the forfeit leadership return "", nil } return "", err } return m.GetName(), nil } } } return "", nil } ================================================ FILE: internal/pkg/etcd/local.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) // GetLocalMemberID gets the etcd member id of the local node via resources. func GetLocalMemberID(ctx context.Context, s state.State) (uint64, error) { ctx, cancel := context.WithTimeout(ctx, 3*time.Minute) defer cancel() member, err := safe.StateWatchFor[*etcd.Member]( ctx, s, etcd.NewMember(etcd.NamespaceName, etcd.LocalMemberID).Metadata(), state.WithEventTypes(state.Created), ) if err != nil { return 0, fmt.Errorf("failed to get local etcd member ID: %w", err) } return etcd.ParseMemberID(member.TypedSpec().MemberID) } ================================================ FILE: internal/pkg/etcd/lock.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "fmt" "time" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/zap" ) // WithLock executes the given function exclusively by acquiring an Etcd lock with the given key. func WithLock(ctx context.Context, key string, logger *zap.Logger, f func() error) error { etcdClient, err := NewLocalClient(ctx) if err != nil { return fmt.Errorf("error creating etcd client: %w", err) } defer etcdClient.Close() //nolint:errcheck session, err := concurrency.NewSession(etcdClient.Client) if err != nil { return fmt.Errorf("error creating etcd session: %w", err) } defer session.Close() //nolint:errcheck mutex := concurrency.NewMutex(session, key) logger.Debug("waiting for mutex", zap.String("key", key)) if err = mutex.Lock(ctx); err != nil { return fmt.Errorf("error acquiring mutex for key %s: %w", key, err) } logger.Debug("mutex acquired", zap.String("key", key)) defer func() { logger.Debug("releasing mutex", zap.String("key", key)) unlockCtx, unlockCancel := context.WithTimeout(context.Background(), 30*time.Second) defer unlockCancel() if err = mutex.Unlock(unlockCtx); err != nil { logger.Error("error releasing mutex", zap.String("key", key), zap.Error(err)) } }() return f() } ================================================ FILE: internal/pkg/extensions/compress.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "context" "fmt" "io" "io/fs" "os" "os/exec" "path/filepath" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // List of globs and destinations for early CPU ucode. // // See https://www.kernel.org/doc/html/v6.1/x86/microcode.html#early-load-microcode. // // We need to repackage the ucode blobs matching the glob into the destination concatenating // them all together. // The resulting blobs should be placed into uncompressed cpio archive prepended to the normal (compressed) initramfs. func earlyCPUUcode(quirks quirks.Quirks) []struct { glob, dst string } { fwPath := quirks.FirmwarePath() return []struct { glob, dst string }{ {fwPath + "/intel-ucode/*", "kernel/x86/microcode/GenuineIntel.bin"}, {fwPath + "/amd-ucode/microcode_amd*.bin", "kernel/x86/microcode/AuthenticAMD.bin"}, } } // List of paths to be moved to the future initramfs. func initramfsPaths(quirks quirks.Quirks) []string { return []string{ quirks.FirmwarePath(), } } // Compress builds the squashfs image in the specified destination folder. // // Components which should be placed to the initramfs are moved to the initramfsPath. // Ucode components are moved into a separate designated location. func (ext *Extension) Compress(ctx context.Context, squashPath, initramfsPath string, quirks quirks.Quirks) (string, error) { if err := ext.handleUcode(initramfsPath, quirks); err != nil { return "", err } for _, path := range initramfsPaths(quirks) { if _, err := os.Stat(filepath.Join(ext.RootfsPath(), path)); err == nil { if err = moveFiles(filepath.Join(ext.RootfsPath(), path), filepath.Join(initramfsPath, path)); err != nil { return "", err } } } squashPath = filepath.Join(squashPath, fmt.Sprintf("%s.sqsh", ext.Directory())) var compressArgs []string if quirks.UseZSTDCompression() { compressArgs = []string{"-comp", "zstd", "-Xcompression-level", "18"} } else { compressArgs = []string{"-comp", "xz", "-Xdict-size", "100%"} } cmd := exec.CommandContext(ctx, "mksquashfs", append([]string{ext.RootfsPath(), squashPath, "-all-root", "-noappend", "-no-progress"}, compressArgs...)...) cmd.Stderr = os.Stderr return squashPath, cmd.Run() } func appendBlob(dst io.Writer, srcPath string) error { src, err := os.Open(srcPath) if err != nil { return err } defer src.Close() //nolint:errcheck if _, err = io.Copy(dst, src); err != nil { return err } if err = src.Close(); err != nil { return err } return os.Remove(srcPath) } func (ext *Extension) handleUcode(initramfsPath string, quirks quirks.Quirks) error { for _, ucode := range earlyCPUUcode(quirks) { matches, err := filepath.Glob(filepath.Join(ext.RootfsPath(), ucode.glob)) if err != nil { return err } if len(matches) == 0 { continue } // if some ucode is found, append it to the blob in the initramfs if err = os.MkdirAll(filepath.Dir(filepath.Join(initramfsPath, ucode.dst)), 0o755); err != nil { return err } dst, err := os.OpenFile(filepath.Join(initramfsPath, ucode.dst), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o644) if err != nil { return err } defer dst.Close() //nolint:errcheck for _, match := range matches { if err = appendBlob(dst, match); err != nil { return err } } if err = dst.Close(); err != nil { return err } } return nil } func moveFiles(srcPath, dstPath string) error { return handleFilesOp(srcPath, dstPath, os.Remove) } func copyFiles(srcPath, dstPath string) error { return handleFilesOp(srcPath, dstPath, nil) } func handleFilesOp(srcPath, dstPath string, op func(string) error) error { st, err := os.Stat(srcPath) if err != nil { return err } if st.IsDir() { return handleDirectoryOp(st, srcPath, dstPath, op) } return handleFileOp(st, srcPath, dstPath, op) } func moveFile(st fs.FileInfo, srcPath, dstPath string) error { return handleFileOp(st, srcPath, dstPath, os.Remove) } func handleFileOp(st fs.FileInfo, srcPath, dstPath string, op func(string) error) error { src, err := os.Open(srcPath) if err != nil { return err } defer src.Close() //nolint:errcheck dst, err := os.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY, st.Mode().Perm()) if err != nil { return err } defer dst.Close() //nolint:errcheck _, err = io.Copy(dst, src) if err != nil { return err } if op != nil { return op(srcPath) } return nil } func handleDirectoryOp(st fs.FileInfo, srcPath, dstPath string, op func(string) error) error { if err := os.MkdirAll(dstPath, st.Mode().Perm()); err != nil { return err } contents, err := os.ReadDir(srcPath) if err != nil { return err } for _, item := range contents { if err = handleFilesOp(filepath.Join(srcPath, item.Name()), filepath.Join(dstPath, item.Name()), op); err != nil { return err } } if op != nil { return op(srcPath) } return nil } ================================================ FILE: internal/pkg/extensions/discarder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "errors" "io" ) // discarder is used to implement ReadAt from a Reader // by reading, and discarding, data until the offset // is reached. It can only go forward. It is designed // for pipe-like files. type discarder struct { r io.Reader pos int64 } // ReadAt implements ReadAt for a discarder. // It is an error for the offset to be negative. func (r *discarder) ReadAt(p []byte, off int64) (int, error) { if off-r.pos < 0 { return 0, errors.New("negative seek on discarder not allowed") } if off != r.pos { i, err := io.Copy(io.Discard, io.LimitReader(r.r, off-r.pos)) if err != nil || i != off-r.pos { return 0, err } r.pos += i } n, err := io.ReadFull(r.r, p) if err != nil { return n, err } r.pos += int64(n) return n, err } var _ io.ReaderAt = &discarder{} ================================================ FILE: internal/pkg/extensions/extensions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package extensions provides function to manage system extensions. package extensions import ( "github.com/siderolabs/talos/pkg/machinery/extensions" ) // Extension wraps the extensions.Extension type with additional methods. type Extension struct { *extensions.Extension } ================================================ FILE: internal/pkg/extensions/extensions_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions_test import ( "os/exec" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/extensions" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) func TestCompress(t *testing.T) { // Compress is going to change contents of the extension, copy to some temporary directory extDir := t.TempDir() require.NoError(t, exec.CommandContext(t.Context(), "cp", "-r", "testdata/good/extension1", extDir).Run()) exts, err := extensions.List(extDir) require.NoError(t, err) require.Len(t, exts, 1) ext := exts[0] squashDest, initramfsDest := t.TempDir(), t.TempDir() squashFile, err := ext.Compress(t.Context(), squashDest, initramfsDest, quirks.New("")) assert.NoError(t, err) assert.FileExists(t, squashFile) assert.FileExists(t, filepath.Join(initramfsDest, "usr", "lib", "firmware", "amd", "cpu")) } ================================================ FILE: internal/pkg/extensions/kernel_modules.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package extensions provides function to manage system extensions. package extensions import ( "bytes" "context" "errors" "fmt" "io" "io/fs" "log" "os" "os/exec" "path/filepath" "strings" "github.com/klauspost/compress/zstd" "github.com/u-root/u-root/pkg/cpio" "github.com/ulikunitz/xz" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // ProvidesKernelModules returns true if the extension provides kernel modules. func (ext *Extension) ProvidesKernelModules(quirks quirks.Quirks) bool { if _, err := os.Stat(ext.KernelModuleDirectory(quirks)); errors.Is(err, fs.ErrNotExist) { return false } return true } // KernelModuleDirectory returns the path to the kernel modules directory. func (ext *Extension) KernelModuleDirectory(quirks quirks.Quirks) string { return filepath.Join(ext.RootfsPath(), quirks.KernelModulesPath()) } func autoDecompress(r io.Reader) (io.Reader, error) { var magic [4]byte if _, err := r.Read(magic[:]); err != nil { return nil, err } src := io.MultiReader(bytes.NewReader(magic[:]), r) // xz magic if bytes.Equal(magic[:], []byte{0xfd, '7', 'z', 'X'}) { return xz.NewReader(src) } return zstd.NewReader(src) } // GenerateKernelModuleDependencyTreeExtension generates a kernel module dependency tree extension. // //nolint:gocyclo func GenerateKernelModuleDependencyTreeExtension( ctx context.Context, extensionPathsWithKernelModules []string, initramfsPath, scratchPath string, quirks quirks.Quirks, printFunc func(format string, v ...any), ) (*Extension, error) { printFunc("preparing to run depmod to generate kernel modules dependency tree") tempDir, err := os.MkdirTemp("", "ext-modules") if err != nil { return nil, err } defer logErr("removing temporary directory", func() error { return os.RemoveAll(tempDir) }) initramfsxz, err := os.Open(initramfsPath) if err != nil { return nil, err } defer logErr("closing initramfs", func() error { return initramfsxz.Close() }) r, err := autoDecompress(initramfsxz) if err != nil { return nil, err } tempRootfsFile := filepath.Join(tempDir, constants.RootfsAsset) if err = extractRootfsFromInitramfs(r, tempRootfsFile); err != nil { return nil, fmt.Errorf("error extacting cpio: %w", err) } kernelModulesPath := quirks.KernelModulesPath() // extract /usr/lib/modules from the squashfs under a temporary root to run depmod on it tempLibModules := filepath.Join(tempDir, "modules") if err = unsquash(ctx, tempRootfsFile, tempLibModules, kernelModulesPath); err != nil { return nil, fmt.Errorf("error running unsquashfs: %w", err) } rootfsKernelModulesPath := filepath.Join(tempLibModules, kernelModulesPath) // under the /usr/lib/modules there should be the only path which is the kernel version contents, err := os.ReadDir(rootfsKernelModulesPath) if err != nil { return nil, err } if len(contents) != 1 || !contents[0].IsDir() { return nil, fmt.Errorf("invalid kernel modules path: %s", rootfsKernelModulesPath) } kernelVersionPath := contents[0].Name() // copy to the same location modules from all extensions for _, path := range extensionPathsWithKernelModules { if err = copyFiles(filepath.Join(path, kernelVersionPath), filepath.Join(rootfsKernelModulesPath, kernelVersionPath)); err != nil { return nil, fmt.Errorf("copying kernel modules from %s failed: %w", path, err) } } printFunc("running depmod to generate kernel modules dependency tree") if err = depmod(ctx, rootfsKernelModulesPath, kernelVersionPath); err != nil { return nil, fmt.Errorf("error running depmod: %w", err) } // we want the extension to survive this function, so not storing in a temporary directory kernelModulesDependencyTreeStagingDir := filepath.Join(scratchPath, "modules.dep") // we want to make sure the root directory has the right permissions. if err := os.MkdirAll(kernelModulesDependencyTreeStagingDir, 0o755); err != nil { return nil, err } kernelModulesDepenencyTreeDirectory := filepath.Join(kernelModulesDependencyTreeStagingDir, kernelModulesPath, kernelVersionPath) if err := os.MkdirAll(kernelModulesDepenencyTreeDirectory, 0o755); err != nil { return nil, err } if err := findAndMoveKernelModulesDepFiles(kernelModulesDepenencyTreeDirectory, filepath.Join(rootfsKernelModulesPath, kernelVersionPath)); err != nil { return nil, err } kernelModulesDepTreeExtension := extensions.New( kernelModulesDependencyTreeStagingDir, "modules.dep", extensions.Manifest{ Version: kernelVersionPath, Metadata: extensions.Metadata{ Name: "modules.dep", Version: kernelVersionPath, Author: "Talos Machinery", Description: "Combined modules.dep for all extensions", }, }, ) return &Extension{kernelModulesDepTreeExtension}, nil } func logErr(msg string, f func() error) { // if file is already closed, ignore the error if err := f(); err != nil && !errors.Is(err, os.ErrClosed) { log.Println(msg, err) } } func extractRootfsFromInitramfs(r io.Reader, rootfsFilePath string) error { recReader := cpio.Newc.Reader(&discarder{r: r}) return cpio.ForEachRecord(recReader, func(r cpio.Record) error { if r.Name != constants.RootfsAsset { return nil } reader := io.NewSectionReader(r.ReaderAt, 0, int64(r.FileSize)) f, err := os.Create(rootfsFilePath) if err != nil { return err } defer logErr("closing rootfs", func() error { return f.Close() }) _, err = io.Copy(f, reader) if err != nil { return err } return f.Close() }) } func unsquash(ctx context.Context, squashfsPath, dest, path string) error { cmd := exec.CommandContext(ctx, "unsquashfs", "-no-xattrs", "-d", dest, "-f", "-n", squashfsPath, path) cmd.Stderr = os.Stderr return cmd.Run() } func depmod(ctx context.Context, baseDir, kernelVersionPath string) error { // Do not trim /usr, because it is needed for depmod baseDir = strings.TrimSuffix(baseDir, "/lib/modules") cmd := exec.CommandContext(ctx, "depmod", "--all", "--basedir", baseDir, "--config", "/etc/modules.d/10-extra-modules.conf", kernelVersionPath) cmd.Stderr = os.Stderr return cmd.Run() } func findAndMoveKernelModulesDepFiles(dest, kernelModulesPath string) error { fs, err := os.ReadDir(kernelModulesPath) if err != nil { return err } for _, f := range fs { if f.IsDir() { continue } if strings.HasPrefix(f.Name(), "modules.") { fs, err := f.Info() if err != nil { return err } if err := moveFile(fs, filepath.Join(kernelModulesPath, f.Name()), filepath.Join(dest, f.Name())); err != nil { return err } } } return nil } ================================================ FILE: internal/pkg/extensions/list.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "cmp" "errors" "fmt" "io/fs" "os" "path/filepath" "slices" "github.com/siderolabs/talos/pkg/machinery/extensions" ) // List prepared unpacked extensions under rootPath. func List(rootPath string) ([]*Extension, error) { items, err := os.ReadDir(rootPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, err } if len(items) == 0 { return nil, nil } slices.SortFunc(items, func(a, b os.DirEntry) int { return cmp.Compare(a.Name(), b.Name()) }) result := make([]*Extension, 0, len(items)) for _, item := range items { if !item.IsDir() { return nil, fmt.Errorf("unexpected non-directory entry: %q", item.Name()) } ext, err := extensions.Load(filepath.Join(rootPath, item.Name())) if err != nil { return nil, fmt.Errorf("error loading extension %s: %w", item.Name(), err) } result = append(result, &Extension{ext}) } return result, nil } ================================================ FILE: internal/pkg/extensions/list_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/extensions" ) func TestList(t *testing.T) { extensions, err := extensions.List("testdata/good/") require.NoError(t, err) require.Len(t, extensions, 1) assert.Equal(t, "gvisor", extensions[0].Manifest.Metadata.Name) } ================================================ FILE: internal/pkg/extensions/testdata/good/extension1/manifest.yaml ================================================ version: v1alpha1 metadata: name: gvisor version: 20220117.0-v1.0.0 author: Andrew Rynhard description: > This system extension provides gVisor using containerd's runtime handler. compatibility: talos: version: ">= v1.0.0" ================================================ FILE: internal/pkg/extensions/testdata/good/extension1/rootfs/usr/lib/firmware/amd/cpu ================================================ ================================================ FILE: internal/pkg/extensions/testdata/good/extension1/rootfs/usr/lib/ld-linux-x86-64.so.2 ================================================ ================================================ FILE: internal/pkg/install/install.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package install provides functionality to run the installer container for Talos. package install import ( "bytes" "context" "fmt" "io" "log" "os" "strconv" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/cio" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/errdefs" "github.com/cosi-project/runtime/pkg/state" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/go-kmsg" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" containerdrunner "github.com/siderolabs/talos/internal/app/machined/pkg/system/runner/containerd" "github.com/siderolabs/talos/internal/pkg/capability" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/console" "github.com/siderolabs/talos/internal/pkg/environment" "github.com/siderolabs/talos/internal/pkg/selinux" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" configcore "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // RunInstallerContainer performs an installation via the installer container. // //nolint:gocyclo,cyclop func RunInstallerContainer( disk, platform, ref string, cfg configcore.Config, cfgContainer configcore.Container, resources state.State, registryBuilder image.RegistriesBuilder, opts ...Option, ) error { const containerID = "upgrade" options := Options{Pull: true} //nolint:staticcheck for _, opt := range opts { if err := opt(&options); err != nil { return err } } ctx, cancel := context.WithCancel(context.Background()) defer cancel() ctx = namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace) client, err := containerd.New(constants.SystemContainerdAddress) if err != nil { return err } defer client.Close() //nolint:errcheck var done func(context.Context) error ctx, done, err = client.WithLease(ctx) defer done(ctx) //nolint:errcheck var img containerd.Image if !options.Pull { img, err = client.GetImage(ctx, ref) } if img == nil || err != nil && errdefs.IsNotFound(err) { log.Printf("pulling %q", ref) img, err = image.Pull(ctx, registryBuilder, resources, client, ref, image.WithProgressReporter(console.NewProgressReporter)) } if err != nil { return err } // See if there's previous container/snapshot to clean up var oldcontainer containerd.Container if oldcontainer, err = client.LoadContainer(ctx, containerID); err == nil { if err = oldcontainer.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { return fmt.Errorf("error deleting old container instance: %w", err) } } if err = client.SnapshotService("").Remove(ctx, containerID); err != nil && !errdefs.IsNotFound(err) { return fmt.Errorf("error cleaning up stale snapshot: %w", err) } mounts := []specs.Mount{ {Type: "bind", Destination: "/dev", Source: "/dev", Options: []string{"rbind", "rshared", "rw"}}, } // mount the machined socket into the container for upgrade pre-checks if the socket exists if _, err = os.Stat(constants.MachineSocketPath); err == nil { mounts = append(mounts, specs.Mount{Type: "bind", Destination: constants.MachineSocketPath, Source: constants.MachineSocketPath, Options: []string{"rbind", "rshared", "ro"}}, ) } // mount the efivars into the container if the efivars directory exists if _, err = os.Stat(constants.EFIVarsMountPoint); err == nil { mounts = append(mounts, specs.Mount{Type: "efivarfs", Source: "efivarfs", Destination: constants.EFIVarsMountPoint, Options: []string{"rw", "nosuid", "nodev", "noexec", "relatime"}}, ) } // mount the /.extra directory into the container if the directory exists if _, err = os.Stat(constants.SDStubDynamicInitrdPath); err == nil { mounts = append(mounts, specs.Mount{Type: "bind", Destination: constants.SDStubDynamicInitrdPath, Source: constants.SDStubDynamicInitrdPath, Options: []string{"rbind", "rshared", "ro"}}, ) } // TODO(andrewrynhard): To handle cases when the newer version changes the // platform name, this should be determined in the installer container. config := constants.ConfigNone if c := procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); c != nil { config = *c } upgrade := strconv.FormatBool(options.Upgrade) force := strconv.FormatBool(options.Force) zero := strconv.FormatBool(options.Zero) args := []string{ "/bin/installer", "install", "--disk=" + disk, "--platform=" + platform, "--config=" + config, "--upgrade=" + upgrade, "--force=" + force, "--zero=" + zero, } for _, arg := range options.ExtraKernelArgs { args = append(args, "--extra-kernel-arg", arg) } for _, preservedArg := range []string{ constants.KernelParamSideroLink, constants.KernelParamEventsSink, constants.KernelParamLoggingKernel, constants.KernelParamEquinixMetalEvents, constants.KernelParamAuditdDisabled, constants.KernelParamDashboardDisabled, constants.KernelParamNetIfnames, constants.KernelParamEnforceModuleSigVerify, } { if c := procfs.ProcCmdline().Get(preservedArg).First(); c != nil { args = append(args, "--extra-kernel-arg", fmt.Sprintf("%s=%s", preservedArg, *c)) } } specOpts := []oci.SpecOpts{ oci.WithImageConfig(img), oci.WithProcessArgs(args...), oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostNamespace(specs.PIDNamespace), oci.WithMounts(mounts), oci.WithHostHostsFile, oci.WithHostResolvconf, oci.WithParentCgroupDevices, oci.WithCapabilities(capability.AllGrantableCapabilities()), oci.WithMaskedPaths(nil), oci.WithReadonlyPaths(nil), oci.WithWriteableSysfs, oci.WithWriteableCgroupfs, oci.WithApparmorProfile(""), oci.WithSeccompUnconfined, oci.WithAllDevicesAllowed, oci.WithEnv(environment.Get(cfg)), } if selinux.IsEnabled() { specOpts = append(specOpts, oci.WithSelinuxLabel(constants.SelinuxLabelInstaller)) } containerOpts := []containerd.NewContainerOpts{ containerd.WithImage(img), containerd.WithNewSnapshot(containerID, img), containerd.WithNewSpec(specOpts...), } container, err := client.NewContainer(ctx, containerID, containerOpts...) if err != nil { return err } defer container.Delete(ctx, containerd.WithSnapshotCleanup) //nolint:errcheck f, err := os.OpenFile("/dev/kmsg", os.O_RDWR|unix.O_CLOEXEC|unix.O_NONBLOCK|unix.O_NOCTTY, 0o666) if err != nil { return fmt.Errorf("failed to open /dev/kmsg: %w", err) } //nolint:errcheck defer f.Close() w := &kmsg.Writer{KmsgWriter: f} var r interface { io.Reader WaitAndClose(context.Context, containerd.Task) } if cfgContainer != nil { var configBytes []byte configBytes, err = cfgContainer.Bytes() if err != nil { return err } r = &containerdrunner.StdinCloser{ Stdin: bytes.NewReader(configBytes), Closer: make(chan struct{}), } } creator := cio.NewCreator(cio.WithStreams(r, w, w)) t, err := container.NewTask(ctx, creator) if err != nil { return err } if r != nil { go r.WaitAndClose(ctx, t) } defer t.Delete(ctx) //nolint:errcheck if err = t.Start(ctx); err != nil { return fmt.Errorf("failed to start %q task: %w", "upgrade", err) } statusC, err := t.Wait(ctx) if err != nil { return fmt.Errorf("failed waiting for %q task: %w", "upgrade", err) } status := <-statusC code := status.ExitCode() if code != 0 { return fmt.Errorf("task %q failed: exit code %d", "upgrade", code) } return nil } // OptionsFromUpgradeRequest builds installer options from upgrade request. func OptionsFromUpgradeRequest(r runtime.Runtime, in *machineapi.UpgradeRequest) []Option { opts := []Option{ WithUpgrade(true), WithForce(!in.GetPreserve()), } if r.Config() != nil && r.Config().Machine() != nil { opts = append(opts, WithExtraKernelArgs(r.Config().Machine().Install().ExtraKernelArgs())) } return opts } ================================================ FILE: internal/pkg/install/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install // Option is a functional option. type Option func(o *Options) error // Options describes the install options. type Options struct { // Deprecated: Pull is not used in new Lifecycle API. Pull bool Force bool Upgrade bool Zero bool ExtraKernelArgs []string } // DefaultInstallOptions returns default options. func DefaultInstallOptions() Options { return Options{} } // Apply list of Option. func (o *Options) Apply(opts ...Option) error { for _, opt := range opts { if err := opt(o); err != nil { return err } } return nil } // WithOptions sets Options as a whole. func WithOptions(opts Options) Option { return func(o *Options) error { *o = opts return nil } } // WithPull sets the pull option. // // Deprecated: Pull is not used in new Lifecycle API. func WithPull(b bool) Option { return func(o *Options) error { o.Pull = b return nil } } // WithForce sets the force option. func WithForce(b bool) Option { return func(o *Options) error { o.Force = b return nil } } // WithUpgrade sets the upgrade option. func WithUpgrade(b bool) Option { return func(o *Options) error { o.Upgrade = b return nil } } // WithZero sets the zero option. func WithZero(b bool) Option { return func(o *Options) error { o.Zero = b return nil } } // WithExtraKernelArgs sets the extra args. func WithExtraKernelArgs(s []string) Option { return func(o *Options) error { o.ExtraKernelArgs = s return nil } } ================================================ FILE: internal/pkg/install/pull.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package install import ( "context" "errors" "fmt" containerd "github.com/containerd/containerd/v2/client" "github.com/containerd/containerd/v2/pkg/cio" "github.com/containerd/containerd/v2/pkg/namespaces" "github.com/containerd/containerd/v2/pkg/oci" "github.com/containerd/errdefs" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/internal/pkg/containers/image" "github.com/siderolabs/talos/internal/pkg/containers/image/console" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" ) // PullAndValidateInstallerImage pulls down the installer and validates that it can run. // //nolint:gocyclo func PullAndValidateInstallerImage(ctx context.Context, resources state.State, registryBuilder image.RegistriesBuilder, ref string) error { // Pull down specified installer image early so we can bail if it doesn't exist in the upstream registry containerdctx := namespaces.WithNamespace(ctx, constants.SystemContainerdNamespace) const containerID = "validate" client, err := containerd.New(constants.SystemContainerdAddress) if err != nil { return err } defer client.Close() //nolint:errcheck img, err := image.Pull(containerdctx, registryBuilder, resources, client, ref, image.WithSkipIfAlreadyPulled(), image.WithMaxNotFoundRetries(1), image.WithProgressReporter(console.NewProgressReporter), ) if err != nil { return err } // See if there's previous container/snapshot to clean up var oldcontainer containerd.Container if oldcontainer, err = client.LoadContainer(containerdctx, containerID); err == nil { if err = oldcontainer.Delete(containerdctx, containerd.WithSnapshotCleanup); err != nil { return fmt.Errorf("error deleting old container instance: %w", err) } } if err = client.SnapshotService("").Remove(containerdctx, containerID); err != nil && !errdefs.IsNotFound(err) { return fmt.Errorf("error cleaning up stale snapshot: %w", err) } // Launch the container with a known help command for a simple check to make sure the image is valid args := []string{ "/bin/installer", "--help", } specOpts := []oci.SpecOpts{ oci.WithImageConfig(img), oci.WithProcessArgs(args...), } if selinux.IsEnabled() { specOpts = append(specOpts, oci.WithSelinuxLabel(constants.SelinuxLabelInstaller)) } containerOpts := []containerd.NewContainerOpts{ containerd.WithImage(img), containerd.WithNewSnapshot(containerID, img), containerd.WithNewSpec(specOpts...), } container, err := client.NewContainer(containerdctx, containerID, containerOpts...) if err != nil { return err } //nolint:errcheck defer container.Delete(containerdctx, containerd.WithSnapshotCleanup) task, err := container.NewTask(containerdctx, cio.NullIO) if err != nil { return err } //nolint:errcheck defer task.Delete(containerdctx) exitStatusC, err := task.Wait(containerdctx) if err != nil { return err } if err = task.Start(containerdctx); err != nil { return err } status := <-exitStatusC code, _, err := status.Result() if err != nil { return err } if code != 0 { return errors.New("installer help returned non-zero exit. assuming invalid installer") } return nil } ================================================ FILE: internal/pkg/logind/broker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logind import ( "bufio" "context" "errors" "fmt" "io" "log" "net" "os" "strings" "sync" "syscall" "time" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" ) // DBusBroker implements simplified D-Bus broker which allows to connect // kubelet D-Bus connection with Talos logind mock. // // Broker doesn't actually implement auth, and it is supposed that service // connects on one socket, while a client connects on another socket. type DBusBroker struct { listenService, listenClient net.Listener } // NewBroker initializes new broker. func NewBroker(ctx context.Context, serviceSocketPath, clientSocketPath string) (*DBusBroker, error) { // remove socket paths as with Docker mode paths might persist across container restarts for _, socketPath := range []string{serviceSocketPath, clientSocketPath} { if err := os.RemoveAll(socketPath); err != nil { return nil, fmt.Errorf("error cleaning up D-Bus socket paths: %w", err) } } broker := &DBusBroker{} var err error broker.listenService, err = (&net.ListenConfig{}).Listen(ctx, "unix", serviceSocketPath) if err != nil { return nil, err } if err = selinux.SetLabel(serviceSocketPath, constants.DBusServiceSocketLabel); err != nil { return nil, err } broker.listenClient, err = (&net.ListenConfig{}).Listen(ctx, "unix", clientSocketPath) if err != nil { return nil, err } if err = selinux.SetLabel(clientSocketPath, constants.DBusClientSocketLabel); err != nil { return nil, err } return broker, nil } // Close the listen sockets. func (broker *DBusBroker) Close() error { if err := broker.listenClient.Close(); err != nil { return err } return broker.listenService.Close() } // Run the broker. func (broker *DBusBroker) Run(ctx context.Context) error { eg, ctx := errgroup.WithContext(ctx) var ( connClient, connService net.Conn mu sync.Mutex ) eg.Go(func() error { return broker.run(ctx, broker.listenService, &mu, &connService, &connClient) }) eg.Go(func() error { return broker.run(ctx, broker.listenClient, &mu, &connClient, &connService) }) return eg.Wait() } func (broker *DBusBroker) run(ctx context.Context, l net.Listener, mu *sync.Mutex, ours, theirs *net.Conn) error { for ctx.Err() == nil { conn, err := l.Accept() if err != nil { if errors.Is(err, net.ErrClosed) { return nil } return err } handleConn(ctx, conn.(*net.UnixConn), mu, ours, theirs) } return nil } func extractFiles(oob []byte) []int { if len(oob) == 0 { return nil } var fds []int scms, err := syscall.ParseSocketControlMessage(oob) if err == nil { for _, scm := range scms { var files []int files, err = syscall.ParseUnixRights(&scm) if err == nil { fds = append(fds, files...) } } } return fds } //nolint:gocyclo func handleConn(ctx context.Context, conn *net.UnixConn, mu *sync.Mutex, ours, theirs *net.Conn) { defer conn.Close() //nolint: errcheck r := bufio.NewReader(conn) if err := handleAuth(r, conn); err != nil { log.Printf("auth failed: %s", err) return } mu.Lock() *ours = conn mu.Unlock() defer func() { mu.Lock() *ours = nil mu.Unlock() }() buf := make([]byte, 4096) oob := make([]byte, 4096) for ctx.Err() == nil { var ( n, oobn int err error ) if r.Buffered() > 0 { // read remaining buffered data n, err = r.Read(buf[:r.Buffered()]) } else { // read the message and OOB data from the UNIX socket n, oobn, _, _, err = conn.ReadMsgUnix(buf, oob) } if err != nil { return } // capture all file descriptors in the OOB message // broker needs to close the file descriptors as they get passed to the other peer fds := extractFiles(oob[:oobn]) // find the other side of the connection var w net.Conn for range 10 { mu.Lock() w = *theirs mu.Unlock() if w != nil { break } select { case <-time.After(time.Second): case <-ctx.Done(): return } } if w == nil { // drop data, as there's no other connection continue } // send the message and OOB date // this will pass the file descriptors if they are in the OOB date if _, _, err = w.(*net.UnixConn).WriteMsgUnix(buf[:n], oob[:oobn], nil); err != nil { return } // close fds to make sure broker doesn't hold the fds on its side for _, fd := range fds { syscall.Close(fd) //nolint:errcheck } } } //nolint:gocyclo func handleAuth(r *bufio.Reader, w io.Writer) error { readLine := func() (string, error) { l, err := r.ReadString('\n') if err != nil { return l, err } l = strings.TrimRight(l, "\r\n") return l, nil } // first, should receive AUTH command preceded by zero byte line, err := readLine() if err != nil { return err } if line != "\x00AUTH" { return fmt.Errorf("unexpected line, expected AUTH: %q", line) } if _, err = w.Write([]byte("REJECTED EXTERNAL\r\n")); err != nil { return err } // now real auth command line, err = readLine() if err != nil { return err } if !strings.HasPrefix(line, "AUTH EXTERNAL") { return fmt.Errorf("unexpected line, expected AUTH EXTERNAL: %q", line) } if _, err = w.Write([]byte("OK 1234deadbeef\r\n")); err != nil { return err } // negotiate unix FDs line, err = readLine() if err != nil { return err } if line != "NEGOTIATE_UNIX_FD" { return fmt.Errorf("unexpected line, expected NEGOTIATE_UNIX_FD: %q", line) } if _, err = w.Write([]byte("AGREE_UNIX_FD\r\n")); err != nil { return err } // BEGIN line, err = readLine() if err != nil { return err } if line != "BEGIN" { return fmt.Errorf("unexpected line, expected BEGIN: %q", line) } return nil } ================================================ FILE: internal/pkg/logind/dbus.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logind import "github.com/godbus/dbus/v5" const ( dbusPath = dbus.ObjectPath("/org/freedesktop/DBus") dbusInterface = "org.freedesktop.DBus" propsInterface = "org.freedesktop.DBus.Properties" ) type dbusMock struct{} func (dbusMock) Hello() (string, *dbus.Error) { return "id", nil } func (dbusMock) AddMatch(_ string) *dbus.Error { return nil } ================================================ FILE: internal/pkg/logind/kubelet_mock_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logind_test import ( "errors" "fmt" "log" "syscall" "time" "github.com/godbus/dbus/v5" ) const ( logindService = "org.freedesktop.login1" logindObject = dbus.ObjectPath("/org/freedesktop/login1") logindInterface = "org.freedesktop.login1.Manager" ) type dBusConnector interface { Object(dest string, path dbus.ObjectPath) dbus.BusObject AddMatchSignal(options ...dbus.MatchOption) error Signal(ch chan<- *dbus.Signal) Close() error } // DBusCon has functions that can be used to interact with systemd and logind over dbus. type DBusCon struct { SystemBus dBusConnector } func NewDBusCon(path string) (*DBusCon, error) { conn, err := dbus.Connect(path) if err != nil { return nil, err } return &DBusCon{ SystemBus: conn, }, nil } func (bus *DBusCon) Close() error { return bus.SystemBus.Close() } // InhibitLock is a lock obtained after creating an systemd inhibitor by calling InhibitShutdown(). type InhibitLock uint32 // CurrentInhibitDelay returns the current delay inhibitor timeout value as configured in logind.conf(5). // see https://www.freedesktop.org/software/systemd/man/logind.conf.html for more details. func (bus *DBusCon) CurrentInhibitDelay() (time.Duration, error) { obj := bus.SystemBus.Object(logindService, logindObject) res, err := obj.GetProperty(logindInterface + ".InhibitDelayMaxUSec") if err != nil { return 0, fmt.Errorf("failed reading InhibitDelayMaxUSec property from logind: %w", err) } delay, ok := res.Value().(uint64) if !ok { return 0, errors.New("InhibitDelayMaxUSec from logind is not a uint64 as expected") } // InhibitDelayMaxUSec is in microseconds duration := time.Duration(delay) * time.Microsecond return duration, nil } // InhibitShutdown creates an systemd inhibitor by calling logind's Inhibt() and returns the inhibitor lock // see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details. func (bus *DBusCon) InhibitShutdown() (InhibitLock, error) { obj := bus.SystemBus.Object(logindService, logindObject) what := "shutdown" who := "kubelet" why := "Kubelet needs time to handle node shutdown" mode := "delay" call := obj.Call("org.freedesktop.login1.Manager.Inhibit", 0, what, who, why, mode) if call.Err != nil { return InhibitLock(0), fmt.Errorf("failed creating systemd inhibitor: %w", call.Err) } var fd uint32 err := call.Store(&fd) if err != nil { return InhibitLock(0), fmt.Errorf("failed storing inhibit lock file descriptor: %w", err) } return InhibitLock(fd), nil } // ReleaseInhibitLock will release the underlying inhibit lock which will cause the shutdown to start. func (bus *DBusCon) ReleaseInhibitLock(lock InhibitLock) error { err := syscall.Close(int(lock)) if err != nil { return fmt.Errorf("unable to close systemd inhibitor lock: %w", err) } return nil } // MonitorShutdown detects the a node shutdown by watching for "PrepareForShutdown" logind events. // see https://www.freedesktop.org/wiki/Software/systemd/inhibit/ for more details. func (bus *DBusCon) MonitorShutdown() (<-chan bool, error) { err := bus.SystemBus.AddMatchSignal(dbus.WithMatchInterface(logindInterface), dbus.WithMatchMember("PrepareForShutdown"), dbus.WithMatchObjectPath("/org/freedesktop/login1")) if err != nil { return nil, err } busChan := make(chan *dbus.Signal, 1) bus.SystemBus.Signal(busChan) shutdownChan := make(chan bool, 1) go func() { for { event, ok := <-busChan if !ok { close(shutdownChan) return } if event == nil || len(event.Body) == 0 { log.Printf("failed obtaining shutdown event, PrepareForShutdown event was empty") continue } shutdownActive, ok := event.Body[0].(bool) if !ok { log.Printf("Failed obtaining shutdown event, PrepareForShutdown event was not bool type as expected") continue } shutdownChan <- shutdownActive } }() return shutdownChan, nil } ================================================ FILE: internal/pkg/logind/logind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package logind provides D-Bus logind mock to facilitate graceful kubelet shutdown. package logind import ( "slices" "sync" "syscall" "time" "github.com/godbus/dbus/v5" "github.com/godbus/dbus/v5/prop" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( logindService = "org.freedesktop.login1" logindObject = dbus.ObjectPath("/org/freedesktop/login1") logindInterface = "org.freedesktop.login1.Manager" ) // InhibitMaxDelay is the maximum delay for graceful shutdown. const InhibitMaxDelay = 40 * constants.KubeletShutdownGracePeriod type logindMock struct { mu sync.Mutex inhibitPipe []int } func (mock *logindMock) Inhibit(what, who, why, mode string) (dbus.UnixFD, *dbus.Error) { mock.mu.Lock() defer mock.mu.Unlock() for _, fd := range mock.inhibitPipe { syscall.Close(fd) //nolint:errcheck } mock.inhibitPipe = make([]int, 2) if err := syscall.Pipe2(mock.inhibitPipe, syscall.O_CLOEXEC); err != nil { return dbus.UnixFD(0), dbus.MakeFailedError(err) } return dbus.UnixFD(mock.inhibitPipe[1]), nil } func (mock *logindMock) Get(iface, property string) (dbus.Variant, *dbus.Error) { if iface != logindInterface { return dbus.Variant{}, prop.ErrIfaceNotFound } if property != "InhibitDelayMaxUSec" { return dbus.Variant{}, prop.ErrPropNotFound } return dbus.MakeVariant(uint64(InhibitMaxDelay / time.Microsecond)), nil } func (mock *logindMock) getPipe() []int { mock.mu.Lock() defer mock.mu.Unlock() return slices.Clone(mock.inhibitPipe) } ================================================ FILE: internal/pkg/logind/logind_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logind_test import ( "context" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/logind" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestIntegration(t *testing.T) { dir := t.TempDir() socketPathService := filepath.Join(dir, "system_bus_service") socketPathClient := filepath.Join(dir, "system_bus_client") broker, err := logind.NewBroker(t.Context(), socketPathService, socketPathClient) require.NoError(t, err) ctx, cancel := context.WithCancel(t.Context()) defer cancel() errCh := make(chan error, 1) go func() { errCh <- broker.Run(ctx) }() serviceConn, err := logind.NewServiceMock(socketPathService) require.NoError(t, err) defer serviceConn.Close() //nolint:errcheck kubeletConn, err := NewDBusCon("unix:path=" + socketPathClient) require.NoError(t, err) defer kubeletConn.Close() //nolint:errcheck t.Log("ready to go") d, err := kubeletConn.CurrentInhibitDelay() require.NoError(t, err) assert.Equal(t, 40*constants.KubeletShutdownGracePeriod, d) t.Log("acquiring inhibit lock") l, err := kubeletConn.InhibitShutdown() require.NoError(t, err) t.Log("monitoring shutdown signal") ch, err := kubeletConn.MonitorShutdown() require.NoError(t, err) t.Log("emitting shutdown signal") require.NoError(t, serviceConn.EmitShutdown()) assert.True(t, <-ch) t.Log("releasing inhibit lock") require.NoError(t, kubeletConn.ReleaseInhibitLock(l)) t.Log("waiting for inhibit lock release") assert.NoError(t, serviceConn.WaitLockRelease(ctx)) assert.NoError(t, serviceConn.Close()) assert.NoError(t, kubeletConn.Close()) assert.NoError(t, broker.Close()) cancel() assert.NoError(t, <-errCh) } ================================================ FILE: internal/pkg/logind/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logind import ( "context" "syscall" "github.com/godbus/dbus/v5" ) // ServiceMock connects to the broker and mocks the D-Bus and logind. type ServiceMock struct { conn *dbus.Conn logind logindMock } // NewServiceMock initializes the D-Bus and logind mock. func NewServiceMock(socketPath string) (*ServiceMock, error) { var mock ServiceMock conn, err := dbus.Dial("unix:path=" + socketPath) if err != nil { return nil, err } if err = conn.Auth(nil); err != nil { return nil, err } var dbusMock dbusMock if err = conn.ExportMethodTable( map[string]any{ "AddMatch": dbusMock.AddMatch, "Hello": dbusMock.Hello, }, dbusPath, dbusInterface, ); err != nil { return nil, err } if err = conn.ExportMethodTable( map[string]any{ "Inhibit": mock.logind.Inhibit, }, logindObject, logindService, ); err != nil { return nil, err } if err = conn.ExportMethodTable( map[string]any{ "Inhibit": mock.logind.Inhibit, }, logindObject, logindInterface, ); err != nil { return nil, err } if err = conn.ExportMethodTable( map[string]any{ "Get": mock.logind.Get, }, logindObject, propsInterface, ); err != nil { return nil, err } mock.conn = conn return &mock, nil } // Close the connection. func (mock *ServiceMock) Close() error { return mock.conn.Close() } // EmitShutdown notifies about the shutdown. func (mock *ServiceMock) EmitShutdown() error { return mock.conn.Emit(logindObject, logindService+".PrepareForShutdown", true) } // WaitLockRelease waits for the inhibit lock to be released. func (mock *ServiceMock) WaitLockRelease(ctx context.Context) error { pipe := mock.logind.getPipe() // no inhibit lock if len(pipe) == 0 { return nil } // close the write side of the pipe, other fd to the write pipe is in the kubelet if err := syscall.Close(pipe[1]); err != nil { return err } errCh := make(chan error, 1) go func() { // attempt to read from the pipe, as soon as kubelet closes its end, read should return buf := make([]byte, 1) _, err := syscall.Read(pipe[0], buf) errCh <- err }() select { case err := <-errCh: return err case <-ctx.Done(): return ctx.Err() } } ================================================ FILE: internal/pkg/measure/internal/pcr/bank_data.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr import ( "crypto" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/hex" "fmt" "os" "github.com/google/go-tpm/tpm2" "github.com/siderolabs/talos/internal/pkg/secureboot" tpm2internal "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" "github.com/siderolabs/talos/pkg/machinery/constants" ) // RSAKey is the input for the CalculateBankData function. type RSAKey interface { crypto.Signer PublicRSAKey() *rsa.PublicKey } // CalculateBankData calculates the PCR bank data for a given set of UKI file sections. // // This mimics the process happening happening in the TPM when the UKI is being loaded. // //nolint:gocyclo func CalculateBankData(pcrNumber int, alg tpm2.TPMAlgID, sectionData map[string]string, rsaKey RSAKey) ([]tpm2internal.BankData, error) { // get fingerprint of public key pubKeyFingerprint := sha256.Sum256(x509.MarshalPKCS1PublicKey(rsaKey.PublicRSAKey())) hashAlg, err := alg.Hash() if err != nil { return nil, err } pcrSelector, err := tpm2internal.CreateSelector([]int{constants.UKIPCR}) if err != nil { return nil, fmt.Errorf("failed to create PCR selection: %v", err) } pcrSelection := tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: alg, PCRSelect: pcrSelector, }, }, } hashData := NewDigest(hashAlg) for _, section := range OrderedSections() { if file := sectionData[section]; file != "" { hashData.Extend(append([]byte(section), 0)) if err = func() error { f, err := os.Open(file) if err != nil { return err } defer f.Close() //nolint:errcheck return hashData.ExtendFrom(f) }(); err != nil { return nil, fmt.Errorf("failed to hash section %q: %v", section, err) } } } banks := make([]tpm2internal.BankData, 0) for _, phaseInfo := range secureboot.OrderedPhases() { // extend always, but only calculate signature if requested hashData.Extend([]byte(phaseInfo.Phase)) if !phaseInfo.CalculateSignature { continue } hash := hashData.Hash() policyPCR, err := tpm2internal.CalculatePolicy(hash, pcrSelection) if err != nil { return nil, err } sigData, err := Sign(policyPCR, hashAlg, rsaKey) if err != nil { return nil, err } banks = append(banks, tpm2internal.BankData{ PCRs: []int{pcrNumber}, PKFP: hex.EncodeToString(pubKeyFingerprint[:]), Sig: sigData.SignatureBase64, Pol: sigData.Digest, }) } return banks, nil } ================================================ FILE: internal/pkg/measure/internal/pcr/bank_data_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr_test import ( "crypto/rsa" "crypto/x509" "encoding/pem" "os" "testing" "github.com/google/go-tpm/tpm2" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/measure/internal/pcr" tpm2internal "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" ) type keyWrapper struct { *rsa.PrivateKey } func (k keyWrapper) PublicRSAKey() *rsa.PublicKey { return &k.PrivateKey.PublicKey } func TestCalculateBankData(t *testing.T) { t.Parallel() pemKey, err := os.ReadFile("../../testdata/pcr-signing-key.pem") require.NoError(t, err) block, _ := pem.Decode(pemKey) require.NotNil(t, block) key, err := x509.ParsePKCS1PrivateKey(block.Bytes) require.NoError(t, err) bankData, err := pcr.CalculateBankData(15, tpm2.TPMAlgSHA256, map[string]string{ ".initrd": "testdata/a", ".linux": "testdata/b", ".dtb": "testdata/c", }, keyWrapper{key}) require.NoError(t, err) require.Equal(t, []tpm2internal.BankData{ { PCRs: []int{15}, PKFP: "58f58f625bd8a8b6681e4b40688cf99b26419b6b2c5f6e14a2c7c67a3b0b1620", Pol: "a1c9d366451c82969238eb82a5282f84b6a3d499e540430ecf792083155225dd", Sig: "bO4F/T6bio7jLJFpi4GsJHjZDj+H5Pq4stjKA5WhkzBNCmE1gQECeOALUfNJ1RW/HKhwSp7KhGFqqnjyg/eXR3c0pVuYUKuAjZz9NMXS4dAQlSLxtNWMmlX3XDst/UKfxB6Z+m2KluJpF3KeAw03tP9lru6nfzaickOs1UL83IO5QgLkCHpUcSloZcya0xS9ETCNBd5gm8K+c9gc7+CmpFLTo1uTxbBK0Mea+3fn7GAZROHPMBLosvTM5D9vplsWIXXAXSaHr/sj5bxOIR+orCQZOdYY+/8ra4oFVXzHc9kkPP3A6mWzoADKryWWIVKPx/DGLi0ExT2fpCNdUoMOacvD+dqDqjVBhcOwoAZkNqve/W/poqaLlKyFTqlmGmv+08WavtShYmCURa4Mn3UFf49BTVkktxoQ+jTMroyit1uK/ppMSjaPwQp2Dd1pRCY4hcFfLwqryy1zRMT/XmZ2e91MYe40Pr9Tom2ZH0YAigDosBPuP6RHt7IypFIgary3louW1dqNLXW8p38Y91nYDKBWI9x0tVn5ufqtk5wkHnExTjUYkTWU98+p5J7urDIhLuX1mSi57Ekq02f+lVLMs85SHfmMfzZl7l7Xi4npYbW+5xHKiAxLnaXVJCHdW0xiAD0VTLer5Oe5nf7FrjSzS39rXoryKfcHFOIxRT1XQOA=", //nolint:lll }, }, bankData) } ================================================ FILE: internal/pkg/measure/internal/pcr/extend.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr import ( "bytes" "crypto" "fmt" "io" ) // Digest implements the PCR extension algorithm. // // Each time `Extend` is called, the hash of the previous data is // prepended to the hash of new data and hashed together. // // The initial hash value is all zeroes. type Digest struct { alg crypto.Hash hash []byte } // NewDigest creates a new Digest with the speified hash algorithm. func NewDigest(alg crypto.Hash) *Digest { return &Digest{ alg: alg, hash: make([]byte, alg.Size()), } } // Hash returns the current hash value. func (d *Digest) Hash() []byte { return d.hash } // Extend extends the current hash with the specified data. func (d *Digest) Extend(data []byte) { d.ExtendFrom(bytes.NewReader(data)) //nolint:errcheck } // ExtendFrom extends the current hash with the specified io,WriterTo. func (d *Digest) ExtendFrom(rdr io.WriterTo) error { // create hash of incoming data hash := d.alg.New() _, err := rdr.WriteTo(hash) if err != nil { return fmt.Errorf("failed to hash data: %v", err) } hashSum := hash.Sum(nil) // extend hash with previous data and hashed incoming data hash.Reset() hash.Write(d.hash) hash.Write(hashSum) // set sum as new hash d.hash = hash.Sum(nil) return nil } ================================================ FILE: internal/pkg/measure/internal/pcr/extend_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr_test import ( "crypto" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/measure/internal/pcr" ) func TestExtend(t *testing.T) { t.Parallel() hash := pcr.NewDigest(crypto.SHA256) assert.Equal(t, make([]byte, 32), hash.Hash()) hash.Extend([]byte("foo")) assert.Equal(t, []byte{0x42, 0x48, 0x16, 0xd0, 0x20, 0xcf, 0x3d, 0x79, 0x3a, 0xc0, 0x21, 0xda, 0x47, 0x37, 0x9b, 0xdf, 0x60, 0x80, 0x80, 0xa8, 0x3e, 0xb9, 0x36, 0x4a, 0x7f, 0xbe, 0xb, 0xdf, 0xa8, 0x71, 0x11, 0xd7}, hash.Hash(), ) hash.Extend([]byte("bar")) assert.Equal(t, []byte{0x63, 0x5c, 0x18, 0xb1, 0x5e, 0xf5, 0xc5, 0xd6, 0xc0, 0x20, 0xe7, 0x23, 0x39, 0xdd, 0xef, 0xd8, 0xb0, 0x5c, 0x4c, 0x4a, 0x44, 0xb3, 0x4e, 0xff, 0x8c, 0xef, 0x22, 0x6f, 0x89, 0x2, 0x77, 0x2}, hash.Hash(), ) } ================================================ FILE: internal/pkg/measure/internal/pcr/sections.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr // OrderedSections returns the sections that are measured into PCR. // // Derived from https://github.com/systemd/systemd/blob/v257.1/src/fundamental/uki.h#L6 // .pcrsig section is omitted here since that's what we are calulating here. func OrderedSections() []string { // DO NOT REARRANGE return []string{ ".linux", ".osrel", ".cmdline", ".initrd", ".ucode", ".splash", ".dtb", ".uname", ".sbat", ".pcrpkey", ".profile", ".dtbauto", ".hwids", } } ================================================ FILE: internal/pkg/measure/internal/pcr/sign.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pcr contains code that handles PCR operations. package pcr import ( "crypto" "encoding/base64" "encoding/hex" "fmt" ) // Signature returns the hashed signature digest and base64 encoded signature. type Signature struct { Digest string SignatureBase64 string } // Sign the digest using specified hash and key. func Sign(digest []byte, hash crypto.Hash, key crypto.Signer) (*Signature, error) { digestToHash := hash.New() digestToHash.Write(digest) digestHashed := digestToHash.Sum(nil) // sign policy digest signedData, err := key.Sign(nil, digestHashed, hash) if err != nil { return nil, fmt.Errorf("signing failed: %v", err) } return &Signature{ Digest: hex.EncodeToString(digest), SignatureBase64: base64.StdEncoding.EncodeToString(signedData), }, nil } ================================================ FILE: internal/pkg/measure/internal/pcr/sign_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pcr_test import ( "crypto" "crypto/x509" "encoding/hex" "encoding/pem" "os" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/measure/internal/pcr" ) func TestSign(t *testing.T) { t.Parallel() pemKey, err := os.ReadFile("../../testdata/pcr-signing-key.pem") require.NoError(t, err) block, _ := pem.Decode(pemKey) require.NotNil(t, block) key, err := x509.ParsePKCS1PrivateKey(block.Bytes) require.NoError(t, err) digest, err := hex.DecodeString("e9590019f04a00029bb5ac512c3d3dfff0ec0e66418cfb5035e22313af891d81") require.NoError(t, err) signature, err := pcr.Sign(digest, crypto.SHA256, key) require.NoError(t, err) require.Equal(t, &pcr.Signature{ Digest: "e9590019f04a00029bb5ac512c3d3dfff0ec0e66418cfb5035e22313af891d81", SignatureBase64: "Ylam12MOrPQs2m0AsHzRYBjZwYB1B5W0N4Qq62bNjiV4KgQVpwGTnIA0Rgmdaa1bTL+9+7oZ84H1xR0Q248Yd+2P1ZU5KaSysdoi3nlvotRYUq93HQeVjSLe1WUnoZ56EovP47tPuvLqIHmjPYq3V/EVLS6fD3+mXKZr/Q7sdlUjmGtYO5H0rV39C6Oq4Pwk9WJ4oRRKWwCp4KbxOujJ2ANqJl2QdJJA4WSle8+OML+SomelSDCjwt+s+T+0ZUhCY11Els1PtKO55ySU9N67m7wMIAy7aMwF6vbqyRajFDZN8ad7huhXDpwBGBMaEX5ajm2FseUj+h0EYbAm030FwduqZ9WlTMwp9KUx6dK2uOjckKgItBQfVXFoOo8dl4Al9PDktcmuytogI7o1OdzmJAcrb8BiPLLppmNsEgKR+5+poAsSA3Z0dcREiLbvKm10m7mXHGwRg84knZGSrsbHkD9I3ngeOM3JiPLGGCp4nYjBNzKP4jiygTEgEuZ2ueV9PikwlnM5qaDdByIH+0u3LAJubzN2XyI6TGugNRzdvKRIxtl5dSwRoIptiXInN81q6pw2i27YmzvR16tCTxXFRIcHjxpq5Q4KpVohbYhh4kHiWexbqJMpUPoLVEaw+m+Kh7gMvZlud67I6ldRIjDoy/LSdnsXcjpQFkNoF0ZKhX8=", //nolint:lll }, signature, ) } ================================================ FILE: internal/pkg/measure/internal/pcr/testdata/a ================================================ aaaa ================================================ FILE: internal/pkg/measure/internal/pcr/testdata/b ================================================ bbbb ================================================ FILE: internal/pkg/measure/internal/pcr/testdata/c ================================================ cccc ================================================ FILE: internal/pkg/measure/measure.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package measure contains Go implementation of 'systemd-measure' command. // // This implements TPM PCR emulation, UKI signature measurement, signing the measured values. package measure import ( "crypto" "crypto/rsa" "github.com/google/go-tpm/tpm2" "github.com/siderolabs/talos/internal/pkg/measure/internal/pcr" tpm2internal "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" "github.com/siderolabs/talos/pkg/machinery/constants" ) // SectionsData holds a map of Section to file path to the corresponding section. type SectionsData map[string]string // RSAKey is the input for the CalculateBankData function. type RSAKey interface { crypto.Signer PublicRSAKey() *rsa.PublicKey } // GenerateSignedPCR generates the PCR signed data for a given set of UKI file sections. func GenerateSignedPCR(sectionsData SectionsData, rsaKey RSAKey) (*tpm2internal.PCRData, error) { data := &tpm2internal.PCRData{} for _, algo := range []struct { alg tpm2.TPMAlgID bankDataSetter *[]tpm2internal.BankData }{ { alg: tpm2.TPMAlgSHA256, bankDataSetter: &data.SHA256, }, { alg: tpm2.TPMAlgSHA384, bankDataSetter: &data.SHA384, }, { alg: tpm2.TPMAlgSHA512, bankDataSetter: &data.SHA512, }, } { bankData, err := pcr.CalculateBankData(constants.UKIPCR, algo.alg, sectionsData, rsaKey) if err != nil { return nil, err } *algo.bankDataSetter = bankData } return data, nil } ================================================ FILE: internal/pkg/measure/measure_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package measure_test import ( "bytes" "crypto/rsa" "crypto/x509" "encoding/json" "encoding/pem" "fmt" "os" "os/exec" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/measure" "github.com/siderolabs/talos/internal/pkg/measure/internal/pcr" ) const ( // ExpectedSignatureJSON is pre-calculated signature. //nolint:lll ExpectedSignatureJSON = `{"sha256":[{"pcrs":[11],"pkfp":"58f58f625bd8a8b6681e4b40688cf99b26419b6b2c5f6e14a2c7c67a3b0b1620","pol":"c0c4f61f8ac39267a7638cde8029b82a0ccd378b2acbfdffb77ee1f0f9d464ec","sig":"zyyb7nNjQP6GyM1Y2TtCo2FbSDSLMYzDIw5sgsm3vWDgWK6bZnItxixA1J9pF8ccqW09VH5kQU3a5xFl1ZsNmZwSUtK1wr9jwITSW4V+G2508gt1X3t0Yq9SXfXo8JNhKhayjtcfLKEIj3NvaCEgEMwcJJWUM/fooWeoXOdlx3JfTLcL1Pog6Hy7o4nDGKGMHsxUf1RNzf2Ro+Z4lXQ1334fqLeoC2ZbQbFyjRuGMin0/QvWZQ0k8FQ6ZooZR/SrJQrUu/ouzrzyEFIOBLfituBjw6fnT40ieZUh9N/bPqrX9jtKT3eBICYoEuY5V67bpF1Ygm+sDarBvR9MYVBY4DSm1iIOtABBUKVaACXxG4dFrEAUydXv62Yd9Kl86DqoBXrQRu+dcBKa1Stw/eGhzzoaXQo4XeutnIj3QOwtOHN1Z/L02gcZdlCcRboGAiTCWs5m642oSJa8jiAKWzpgwsJpSDkHRsmjWewQMBccgDy1j6DiHp4HakgeK2pePWdD/c8pu8unThKigKy/wsH+QMXpzNufkqAq2aWyuempFDn+OdVHGE5htZuSNIa66Vj0sd1guH13K9zxNUN8JguWnk21zInAN1IpbmyxlgRYCEpuE++E5Te4tqskCHUAU4D1iLS8a73SK5wXS7AnMXK7XhQ4FFvJCaOaQiJjC30LnEk="}],"sha384":[{"pcrs":[11],"pkfp":"58f58f625bd8a8b6681e4b40688cf99b26419b6b2c5f6e14a2c7c67a3b0b1620","pol":"e8f6311e36f9ee038df65255ae7c471d1d1f91ce742b98a152be72337f6c937d","sig":"SH9VntNIwbApIoTM9GksYp6fScBkjQ156rEscm9cR6hFcIy8oZeFCzaCxfl122UgNVzXMZDst4S3xF/BjwG1TiUE3SvzlvCwzvAkLVitHzD3Jj/oyy5vIfe7lhrPvSSb5ta3I5DsBE4693OlXDw/hXzoj93i/Cbztqf/N7G354tO2COx8CXu63Yz3DBijdvDN2lIA6AVv5AV+IC0koU8wuKsdOmbHoysDI/JtFi5f2qQP667qjOUJj2FgsLoH//ZvlKTZjIrGsQo9q6iQsxAEdBKdWub8mrexgalH4jzHfFyVxDDeV6+D77xFCAuaT1fVOcGKTncxAvxN2M+wg7XDEVnZm6Itmh+Qh8DKejogHTBVr1ALBzE12hSpvIlYHQ6bNrzORPX/+7eJ3nbnTfYEp9psyFbxV+21U7A7ArwDQLnhosbVFY94YSyQd13bfgWQll013HzcpiTuzg3+qU4DjPc0x764z614+K9kyJQLqhiE0Y82Qhelhu+dJa/DrmOPEXQ6Lt5YG5TPQSHkNq79vPF1JUC1OkMvnFwO10eU2zPbuVKN7mf9S2ERSGEB9oX7pYLf99gNDh+VCII1iP+Jzj7jcAdvt6VK1Cme89kzXWem66eLaDNYIibb18KJ0uArqBt8VBzsX5wUb9sQTo/f2DOPI2AvHrzWziXjUG8cWc="}],"sha512":[{"pcrs":[11],"pkfp":"58f58f625bd8a8b6681e4b40688cf99b26419b6b2c5f6e14a2c7c67a3b0b1620","pol":"207a8217158f9cc521dca50358a593dc61ed9308ef27da9df4ef64d634309160","sig":"NY659iroM9POX2a75p26i8JPXd+/Tsb45Wd/FEmjb1gopY4ygRumFNn/YuOebbhxHq67SMB30Fz5R1ktLJHgiCZotg5BqiUZWKF1zVlze928LTZqS4xxU5eUlZuzFkDyokwuxzvmc1+qFotkP1FMq8LSyDpBpUKby26EOP8cLYuE6PKc3LmhvjTmOUglICyv0JAoPRARDjZEN/f1O2T9HnACYjXZIVuKkBhMyYkuIgP+kGR1ChnHZTvcOkFByZcOUWtX6ChAP+OvDYQ1lbpomTyyKLmBvA935kJCCL1cDv3u5LCCaPfLm7zAVIyOrfELUpt8I3Oe/qIRX7CiP/TzN3IaNPOzU4+rm0vLcM56T5bEfUw4ikQILykQ202hUeH9Zv5Fw/qZI2nU4ToRamtLQd28xKfod9Uq2fbfORybUU5Ab2SbXpcwMu9PUsapFpjf9hrC1jf/G26TJ1mEzhhLPgJ1UlKyFX6ba0urhudR9dr3xLHlh3gw+T1NnZVram1aYBJnOU6lxHcoD+wmop3pqu1S0VOJX9YIGKzZ5jrzJ102cAI58nihASIkaWD7JqisFBCp2jX0ETu6bJLcufLv7ekT+asw2K1C8BxH5INiRkXu1HEPLYhYBceAgEFGROI26Awy08o8axsCt2zSAi2EafnCEt8mIo5E5RDGvRbRsWk="}]}` ) type rsaWrapper struct { *rsa.PrivateKey } func (w rsaWrapper) PublicRSAKey() *rsa.PublicKey { return &w.PrivateKey.PublicKey } func loadRSAKey(path string) (measure.RSAKey, error) { keyData, err := os.ReadFile(path) if err != nil { return nil, err } // convert private key to rsa.PrivateKey rsaPrivateKeyBlock, _ := pem.Decode(keyData) if rsaPrivateKeyBlock == nil { return nil, err } rsaKey, err := x509.ParsePKCS1PrivateKey(rsaPrivateKeyBlock.Bytes) if err != nil { return nil, fmt.Errorf("parse private key failed: %v", err) } return rsaWrapper{rsaKey}, nil } func TestMeasureMatchesExpectedOutput(t *testing.T) { expectedSignatureHex := ExpectedSignatureJSON if _, err := exec.LookPath("systemd-measure"); err == nil { t.Log("systemd-measure binary found, using it to get expected signature") expectedSignatureHex = getSignatureUsingSDMeasure(t) } tmpDir := t.TempDir() sectionsData := measure.SectionsData{} // create temporary files with the ordered section name and data as the section name for _, section := range pcr.OrderedSections() { sectionFile := filepath.Join(tmpDir, section) if err := os.WriteFile(sectionFile, []byte(section), 0o644); err != nil { t.Fatal(err) } sectionsData[section] = sectionFile } rsaKey, err := loadRSAKey("testdata/pcr-signing-key.pem") if err != nil { t.Fatal(err) } pcrData, err := measure.GenerateSignedPCR(sectionsData, rsaKey) if err != nil { t.Fatal(err) } pcrDataJSON, err := json.Marshal(&pcrData) if err != nil { t.Fatal(err) } assert.Equal(t, expectedSignatureHex, string(pcrDataJSON)) } func getSignatureUsingSDMeasure(t *testing.T) string { tmpDir := t.TempDir() sdMeasureArgs := make([]string, len(pcr.OrderedSections())) // create temporary files with the ordered section name and data as the section name for i, section := range pcr.OrderedSections() { sectionFile := filepath.Join(tmpDir, section) if err := os.WriteFile(sectionFile, []byte(section), 0o644); err != nil { t.Error(err) } sdMeasureArgs[i] = fmt.Sprintf("--%s=%s", strings.TrimPrefix(section, "."), sectionFile) } var ( signature bytes.Buffer stderr bytes.Buffer ) sdCmd := exec.CommandContext( t.Context(), "systemd-measure", append([]string{ "sign", "--private-key", "testdata/pcr-signing-key.pem", "--bank=sha256", "--bank=sha384", "--bank=sha512", "--phase=enter-initrd:leave-initrd:enter-machined", "--json=short", }, sdMeasureArgs..., )...) sdCmd.Stdout = &signature sdCmd.Stderr = &stderr t.Log("Running systemd-measure command:", sdCmd.String()) if err := sdCmd.Run(); err != nil { t.Log("stderr:", stderr.String()) t.Fatalf("systemd-measure failed: %v", err) } s := bytes.TrimSpace(signature.Bytes()) return string(s) } ================================================ FILE: internal/pkg/measure/testdata/pcr-signing-key.pem ================================================ -----BEGIN RSA PRIVATE KEY----- MIIJKQIBAAKCAgEA7qhAkdtZxqkIP79DDGin9eaJBeNlJsClJTcbaXbNfk2QJGT3 lqo9ErXQQftwYWLGo+kVd8puhnHGPkLW9apT1/ZmUJEFwxV5xws0RllGVPhUga+1 oubHUqhEiy707S4RrUEMk/o9wqmtnl2hY5FxMeQn2o7xrpcNhm8FtHpvQrT0MsbC 1cS1ytZH/hwPy/QIB9bx+ugOha6wtQBnpgix1BhHC/NwDIYPg+ONpQSCu9gkXVtL GlKfmjscUANQtBuVKa5NflrjkHw7NAdKYdKpMnmzr0yu6Tn/2oNmUiJAwHz0BXpf b4Yn8n/IoKJQ5Tv1g6d30wxxpBd0lbwSe9MLRchIDJ5aFRybyRxaPGT17U3yEVzb V78kIFtocaqkc1ise8remZ0wxHzuolbTZD6oswt7C9jMLvfMAQ7JtENXrpDM//Xz dRLzyTWKOjhG0YmKKRY6cIrPkugM0PHGCE3RMSH1FmPMrWWBNAMwS0Zba0Wm1b7v dw5fKeE8txH+IpA3IaE9AytYk0ig98ZgmXmBV0sgxmJ/94scEF+sDg65LIkSEJMz f6q30UghbJJoP7eKOoDX9KBrR+POEsWm/EcU5jTEQTHMU+qKtj5KD6TUn8R8yi4w CnyZ7uJLUqm8Ou8MzEZWbrsrbMvrewPDAHn0QQvb2tDtBgn6oH192jpkzckCAwEA AQKCAgEAkiWcrPU7i+lVMNxqLb4lJPOQ83cmKU4Nk7WkZrgm7PKIk5D1AWGs1rla GB3m2uxHIncI+3uOpWwk71m1E2nDwFuWmj3E3otXMKnO0Em5RS1xap10SJa0dwyu NOGDgX8Vuhg8oJ28lmmb9X/25edZ/yhts2yX2ceMs8dnIfdcDOiNJk8LXycAAH+q RJVgoxAEnvBk7LaQthKdCap+znFCnNRlJY9lDXZHKAgAZI5XlLquwjC21B7GuAb8 to7hK/o8JPMlZ3w3IPLCuoDAbxk3Hb7jZzU5Y39uC50t2pw5NOcP9A7VRJFOAzV3 Yc8kZMyL85xpR2e2a7slXNB4LTW3D0zy/fSO63R9cLNrlp+I9p7xDgz1mylo8FoW T1TyNAWo/gIa7r43Ufp/C0lrWSd5gkz2nMVWiFl8M1lpx7zDSZk8U1sKzFZswmFQ h5On7kxo14gUdzogb1hrJuEI9Ke52kRb4YMm094LFI/BWQ89QF0NXJ6CSkb2MyWc f0kyfEMRUbmHi/EfpQlKK2uhOsxXdhZN2VP4nl1Yg0xxv0cLSMcP7DdJPso5VSC/ +wF8ni7+GMEDtntMEGjuXjq/zypyjptaRKpw4iRqxydUqa0C1PovzDoUDn7eKJBv 2p9evDG8zWenZ7g/VYWy4ZtpwVa3SAXeeuLmdllphPp6n6uweFECggEBAPiSb2IY O33JRmxQeqrc6cmcv1l4AbedSr3F7X4DC+HZCkG27bZlzMBqbAQichC/pPhx544S CBxB7e1Qsqjw8LLNea0sQsRbVXMSQpwBqjJ2g4mCN4S/hPQHSeESODPz/OaZrQq5 iVSckrAlbgFs8HepOLxTQQVVp97m+vrnwlh7SZj/CXXi5QcZ4tHPNTFLLtwCgBUA X0Ausn9hpeHrud8imzQuPXJRgBMaF0BYRUxj0fjp3sty2ZfjiPjhTrdSCWOiJUSb onDb6kyYwN/hBEt7JNbSbC7viOM72/TwJQqDn4Bw8C7E0kiy3ZpQiCdyOYWnyDZP SyDhAM4nHLNYss0CggEBAPXJ+DMmLCPkE5OOElxGU+JIPM28+5jhKn+HcswAS5VV 6tp1m8gIrmgwTpXA9aGi9BjPHzo6y3s1sNYHp3lGr5nSdgQaYuLvBR56FE0RXVP7 bUHiN//R7a8QTkyYCqdrQNxLAzEARBNZaMIhMPfPXDDevFZWunAxIDXxC9IVRpjS VjmKwqLuk66uInko4qEn34NiU25x+VpDOjz0fia58VTlL9WrYy0w/QANVlg1JKRq wjjx4kWnmCQ1qQeagB+xqJtrEc/GK4z6qY/OC0pKZA26t6sXhkmn0zQshmwSAZem /QtEW3lDmsfAlvEibJUSshXz3ygWz4v21nWocF4gXu0CggEBAPgwXf43686gVSx4 /sHzaYrgcz5F0JEhACuToJmdORP7vX33xEnGQzYsDEXkjreiYnmeYXE9F9P/EC1P 0dNVHz+oYcFC3DdqaltG9DMIhoN0ScnWttBY2cs+K8oKgwt8phspfdmjfzd4Tg6K kNfjigYwdHG1Psqwx7iMMDStiyMFlmqo2y1Vqw/4DL0ogxgA1XzfEjvl7zUKazc8 rIBy+VeOGiFzue6W6aYo+uZIPIkVceVyvf2tYw2BJpY5gHsR8kYE8+kY7Ix7R+nK 62meJsem4RWNbG9AxBD/B5P84z8oRO3d1jMcWko0LYeSuR+JsV1+NS3k5kKh5kfw TXvVKFECggEACMp4fhvXaFE4AgcK0RIS3f0Hb7Raq1UiV/1YNcOs8GJqS/X45Gar Fj7kEKceIfHaGSkPTN3deUKqWH1dmBDXJwFIB02KS+OQo05qe3crh11uwvR8XEH9 5k0G/+ZQOzyyzS5BpvcDeE2yWX8maTaZbYYJ5myjrm+TX1qHubPZGo4rV1OHMpyl 25GO2haERI9Qhzp1EXYyHPBanOOBv5DW+NpZo6LFoVAnPGE9vVnpPZgz6iV8mlEs N99TdFoqSvfnt+dUc8H6vMgaWHJeJQIUIgmTmCL3QpsmCq+s/yCFvg7S7hw7yVKJ rqtMusMobwyEIhTe3mgydCcX9I1Zt4Qg4QKCAQAnnNjOMflzEz+TvGNPUyVyzXef xRs2XBDipX/vPK7L3VvgiXGQuOAq4PYj7NdYHfIiVmFeqz7eY9gMmQyoscMwVPCm X+FuHepHXfcxUjQwnpFa/lAqpNxspgU09CQXW5hUkIdWblZePtRiGKvlo51CdzU5 KlOylrIXl6opsApdrgSCduBtuR9uz2Cn3NiaG0Xe2x67VphclNaW+RATU4bUJEMn 9aO8k+wp8CjlJ1xjSrhqIIBHGMmouyK5J0r3S3vRlTCLEjGpGq8+Z9shVkfuDk6N HOB2KZ1LN68eOYb4eZFKE+l2nGylFsHlOtkagX1IxtVoW+a5vDenGBNd+gC9 -----END RSA PRIVATE KEY----- ================================================ FILE: internal/pkg/meta/internal/adv/adv.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package adv provides common interfaces to access ADV data. package adv // ADV describes implementation which stores tag-value data. type ADV interface { ReadTag(t uint8) (val string, ok bool) ReadTagBytes(t uint8) (val []byte, ok bool) SetTag(t uint8, val string) (ok bool) SetTagBytes(t uint8, val []byte) (ok bool) DeleteTag(t uint8) (ok bool) ListTags() (tags []uint8) Bytes() ([]byte, error) } const ( // End is the noop tag. End = iota _ _ // Reserved1 is a reserved tag. Reserved1 // Reserved2 is a reserved tag. Reserved2 // Reserved3 is a reserved tag. Reserved3 ) ================================================ FILE: internal/pkg/meta/internal/adv/syslinux/syslinux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package syslinux provides syslinux-compatible ADV data. package syslinux import ( "encoding/binary" "fmt" "io" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv" ) const ( // AdvSize is the total size. AdvSize = 512 // AdvLen is the usable data size. AdvLen = AdvSize - 3*4 // AdvMagic1 is the head signature. AdvMagic1 = uint32(0x5a2d2fa5) // AdvMagic2 is the total checksum. AdvMagic2 = uint32(0xa3041767) // AdvMagic3 is the tail signature. AdvMagic3 = uint32(0xdd28bf64) ) // ADV represents the Syslinux Auxiliary Data Vector. type ADV []byte // NewADV returns the Auxiliary Data Vector. func NewADV(r io.ReadSeeker) (adv ADV, err error) { b := make([]byte, 2*AdvSize) if r == nil { return b, nil } _, err = r.Seek(-2*AdvSize, io.SeekEnd) if err != nil { return nil, fmt.Errorf("failed to seek for syslinux adv: %w", err) } _, err = io.ReadFull(r, b) if err != nil { return nil, fmt.Errorf("failed to read syslinux adv: %w", err) } adv = b return adv, nil } // ReadTag reads a tag in the ADV. func (a ADV) ReadTag(t uint8) (val string, ok bool) { var b []byte b, ok = a.ReadTagBytes(t) val = string(b) return val, ok } // ReadTagBytes reads a tag in the ADV. func (a ADV) ReadTagBytes(t uint8) (val []byte, ok bool) { // Header is in first 8 bytes. i := 8 // End at tail plus two bytes required for successful next tag. for i < AdvSize-4-2 { tag := a[i] size := int(a[i+1]) if tag == adv.End { break } if tag != t { // Jump to the next tag. i += 2 + size continue } length := int(a[i+1]) + i val = a[i+2 : length+2] ok = true break } return val, ok } // ListTags returns a list of tags in the ADV. func (a ADV) ListTags() []uint8 { // Header is in first 8 bytes. i := 8 var tags []uint8 // End at tail plus two bytes required for successful next tag. for i < AdvSize-4-2 { tag := a[i] size := int(a[i+1]) if tag == adv.End { break } tags = append(tags, tag) // Jump to the next tag. i += 2 + size } return tags } // SetTag sets a tag in the ADV. func (a ADV) SetTag(t uint8, val string) bool { return a.SetTagBytes(t, []byte(val)) } // SetTagBytes sets a tag in the ADV. func (a ADV) SetTagBytes(t uint8, val []byte) (ok bool) { if len(val) > 255 { return false } // delete the tag if it exists a.DeleteTag(t) // Header is in first 8 bytes. i := 8 // End at tail plus two bytes required for successful next tag. for i < AdvSize-4-2 { tag := a[i] size := int(a[i+1]) if tag != adv.End { // Jump to the next tag. i += 2 + size continue } // overflow check if i+2+len(val) > AdvSize-4-2 { return false } length := uint8(len(val)) a[i] = t a[i+1] = length copy(a[i+2:i+2+int(length)], val) ok = true break } if ok { a.cleanup() } return ok } // DeleteTag deletes a tag in the ADV. func (a ADV) DeleteTag(t uint8) (ok bool) { // Header is in first 8 bytes. i := 8 // End at tail plus two bytes required for successful next tag. for i < AdvSize-4-2 { tag := a[i] size := int(a[i+1]) if tag == adv.End { break } if tag != t { // Jump to the next tag. i += 2 + size continue } // Save the data after the tag that we will shift to the left by 2 + length // of the tag data. start := i + 2 + size end := a[AdvSize-4] data := make([]byte, len(a[start:end])) copy(data, a[start:end]) // The total size we want to zero out is the length of all the remaining // data we saved above. length := 2 + len(data) // Zero each element to the right. for j := i; j < length; j++ { a[j] = 0 } // Shift the data. copy(a[i:], data) ok = true break } if ok { a.cleanup() } return ok } // Bytes returns serialized contents of ADV. func (a ADV) Bytes() ([]byte, error) { return a, nil } func (a ADV) cleanup() { a.head() a.total() a.tail() copy(a[AdvSize:], a[:AdvSize]) } func (a ADV) head() { binary.LittleEndian.PutUint32(a[0:4], AdvMagic1) } func (a ADV) total() { csum := AdvMagic2 for i := 8; i < AdvSize-4; i += 4 { csum -= binary.LittleEndian.Uint32(a[i : i+4]) } binary.LittleEndian.PutUint32(a[4:8], csum) } func (a ADV) tail() { binary.LittleEndian.PutUint32(a[AdvSize-4:AdvSize], AdvMagic3) } ================================================ FILE: internal/pkg/meta/internal/adv/syslinux/syslinux_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,lll,maligned,scopelint,testpackage package syslinux import ( "bytes" "io" "os" "slices" "testing" ) func TestNewADV(t *testing.T) { f, err := os.Open("testdata/adv.sys") if err != nil { t.Errorf("failed to open test adv.sys: %v", err) } //nolint:errcheck defer f.Close() type args struct { r io.ReadSeeker } tests := []struct { name string args args wantAdv ADV wantErr bool }{ { name: "valid tags", args: args{ r: f, }, wantAdv: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { //nolint:errcheck defer f.Seek(0, 0) gotAdv, err := NewADV(tt.args.r) if (err != nil) != tt.wantErr { t.Errorf("NewADV() error = %v, wantErr %v", err, tt.wantErr) return } if !bytes.Equal(gotAdv, tt.wantAdv) { t.Errorf("NewADV() = %v, want %v", gotAdv, tt.wantAdv) } }) } } func TestADV_ReadTag(t *testing.T) { type args struct { t uint8 } tests := []struct { name string a ADV args args wantVal string wantOk bool }{ { name: "bootonce", a: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 1, }, wantVal: "test me", wantOk: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if len(tt.a) != 2*AdvSize { t.Errorf("Test data is invalid, ADV size = %v, want %v", len(tt.a), 2*AdvSize) } gotVal, gotOk := tt.a.ReadTag(tt.args.t) if gotVal != tt.wantVal { t.Errorf("ADV.ReadTag() gotVal = %v, want %v", gotVal, tt.wantVal) } if gotOk != tt.wantOk { t.Errorf("ADV.ReadTag() gotOk = %v, want %v", gotOk, tt.wantOk) } tags := tt.a.ListTags() if !slices.Equal(tags, []uint8{tt.args.t}) { t.Errorf("ADV.ListTags() got = %v, want %v", tags, []uint8{tt.args.t}) } }) } } func TestADV_SetTag(t *testing.T) { type args struct { t uint8 val string } tests := []struct { name string a ADV args args wantADV ADV wantOk bool }{ { name: "set test me", a: ADV{165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 1, val: "test me", }, wantADV: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantOk: true, }, { name: "set test", a: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 6, val: "add test", }, wantADV: ADV{165, 47, 45, 90, 197, 189, 210, 250, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 197, 189, 210, 250, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantOk: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if len(tt.a) != 2*AdvSize { t.Errorf("Test data is invalid, source ADV size = %v, want %v", len(tt.a), 2*AdvSize) } if len(tt.wantADV) != 2*AdvSize { t.Errorf("Test data is invalid, target ADV size = %v, want %v", len(tt.wantADV), 2*AdvSize) } if gotOk := tt.a.SetTag(tt.args.t, tt.args.val); gotOk != tt.wantOk { t.Errorf("ADV.SetTag() = %v, want %v", gotOk, tt.wantOk) } if !bytes.Equal(tt.a, tt.wantADV) { t.Errorf("ADV = %v, want %v", tt.a, tt.wantADV) } }) } } func TestADV_DeleteTag(t *testing.T) { type args struct { t uint8 } tests := []struct { name string a ADV args args wantADV ADV wantOk bool }{ { name: "delete test me", a: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 1, }, wantADV: ADV{165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantOk: true, }, { name: "delete test", a: ADV{165, 47, 45, 90, 185, 209, 145, 51, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 185, 209, 145, 51, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 6, }, wantADV: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantOk: true, }, { name: "delete test again", a: ADV{165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 142, 155, 111, 208, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, args: args{ t: 6, }, wantADV: ADV{165, 47, 45, 90, 197, 189, 210, 250, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 197, 189, 210, 250, 1, 7, 116, 101, 115, 116, 32, 109, 101, 6, 8, 97, 100, 100, 32, 116, 101, 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, wantOk: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if len(tt.a) != 2*AdvSize { t.Errorf("Test data is invalid, source ADV size = %v, want %v", len(tt.a), 2*AdvSize) } if len(tt.wantADV) != 2*AdvSize { t.Errorf("Test data is invalid, target ADV size = %v, want %v", len(tt.wantADV), 2*AdvSize) } if gotOk := tt.a.DeleteTag(tt.args.t); gotOk != tt.wantOk { t.Errorf("ADV.DeleteTag() = %v, want %v", gotOk, tt.wantOk) } if !bytes.Equal(tt.a, tt.wantADV) { t.Errorf("ADV = %v, want %v", tt.a, tt.wantADV) } }) } } func TestADV_cleanup(t *testing.T) { tests := []struct { name string a ADV want ADV }{ { a: make(ADV, 1024), want: ADV{165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.a.cleanup() if !bytes.Equal(tt.a, tt.want) { t.Errorf("ADV.cleanup() = %v, want %v", tt.a, tt.want) } }) } } func TestADV_head(t *testing.T) { tests := []struct { name string a ADV }{ { a: ADV{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.a.head() // Expecting 165, 47, 45, 90. if tt.a[0] != 165 { t.Errorf("head() = %v, want %v", tt.a[0], 165) } if tt.a[1] != 47 { t.Errorf("head() = %v, want %v", tt.a[0], 47) } if tt.a[2] != 45 { t.Errorf("head() = %v, want %v", tt.a[0], 45) } if tt.a[3] != 90 { t.Errorf("head() = %v, want %v", tt.a[0], 90) } }) } } func TestADV_total(t *testing.T) { tests := []struct { name string a ADV }{ { a: ADV{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.a.total() // Expecting 103, 23, 4, 163. if tt.a[4] != 103 { t.Errorf("head() = %v, want %v", tt.a[4], 103) } if tt.a[5] != 23 { t.Errorf("head() = %v, want %v", tt.a[5], 23) } if tt.a[6] != 4 { t.Errorf("head() = %v, want %v", tt.a[6], 4) } if tt.a[7] != 163 { t.Errorf("head() = %v, want %v", tt.a[7], 163) } }) } } func TestADV_tail(t *testing.T) { tests := []struct { name string a ADV }{ { a: ADV{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221, 165, 47, 45, 90, 103, 23, 4, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 191, 40, 221}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.a.tail() // Expecting 100, 191, 40, 221. if tt.a[len(tt.a)-4] != 100 { t.Errorf("head() = %v, want %v", tt.a[len(tt.a)-4], 100) } if tt.a[len(tt.a)-3] != 191 { t.Errorf("head() = %v, want %v", tt.a[len(tt.a)-3], 191) } if tt.a[len(tt.a)-2] != 40 { t.Errorf("head() = %v, want %v", tt.a[len(tt.a)-2], 40) } if tt.a[len(tt.a)-1] != 221 { t.Errorf("head() = %v, want %v", tt.a[len(tt.a)-1], 221) } }) } } func TestADV_overwrite(t *testing.T) { buf := make([]byte, 2*AdvSize) a, err := NewADV(bytes.NewReader(buf)) if err != nil { t.Errorf("NewADV() failed: %s", err) } for range 1024 { if !a.SetTag(1, "yes") { t.Errorf("SetTag() failed") } } } func TestADV_many_tags(t *testing.T) { buf := make([]byte, 2*AdvSize) a, err := NewADV(bytes.NewReader(buf)) if err != nil { t.Errorf("NewADV() failed: %s", err) } for i := uint8(1); i < 255; i++ { a.SetTag(i, "xa") } } ================================================ FILE: internal/pkg/meta/internal/adv/talos/talos.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos implements modern ADV which supports large size for the values and tags. package talos import ( "bytes" "crypto/sha256" "encoding/binary" "fmt" "io" "slices" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv" ) // Basic constants configuring the ADV. const ( Length = 256 * 1024 // 256KiB DataLength = Length - 40 Size = 2 * Length // Redundancy ) // Magic constants. const ( Magic1 uint32 = 0x5a4b3c2d Magic2 uint32 = 0xa5b4c3d2 ) // Tag is the key. // // We use a byte here for compatibility with syslinux, but format has space for uint32. type Tag uint8 // Value stored for the tag. type Value []byte // ADV implements the Talos extended ADV. // // Layout (all in big-endian): // // 0x0000 4 bytes magic1 // 0x0004 4 bytes tag // 0x0008 4 bytes size // 0x000c (size) bytes value // ... more tags // -0x0024 32 bytes sha256 of the whole block with checksum set to zero // -0x0004 4 bytes magic2 // // Whole data structure is written twice for redundancy. type ADV struct { Tags map[Tag]Value } // NewADV loads ADV from the block device. func NewADV(r io.Reader) (*ADV, error) { a := &ADV{ Tags: make(map[Tag]Value), } if r == nil { return a, nil } buf := make([]byte, Length) _, err := io.ReadFull(r, buf) if err != nil { return nil, fmt.Errorf("failed to load first block: %w", err) } if err = a.Unmarshal(buf); err == nil { return a, nil } // try 2nd copy _, err = io.ReadFull(r, buf) if err != nil { return nil, fmt.Errorf("failed to load second block: %w", err) } if err = a.Unmarshal(buf); err != nil { return a, fmt.Errorf("failed to unmarshal second block: %w", err) } return a, nil } // Unmarshal single copy from the serialized representation. func (a *ADV) Unmarshal(buf []byte) error { magic1 := binary.BigEndian.Uint32(buf[:4]) if magic1 != Magic1 { return fmt.Errorf("adv: unexpected magic %x, expecting %x", magic1, Magic1) } magic2 := binary.BigEndian.Uint32(buf[len(buf)-4:]) if magic2 != Magic2 { return fmt.Errorf("adv: unexpected magic %x, expecting %x", magic2, Magic2) } checksum := slices.Clone(buf[len(buf)-36 : len(buf)-4]) copy(buf[len(buf)-36:len(buf)-4], make([]byte, 32)) hash := sha256.New() hash.Write(buf) actualChecksum := hash.Sum(nil) if !bytes.Equal(checksum, actualChecksum) { return fmt.Errorf("adv: checksum mismatch: %x, expecting %x", checksum, actualChecksum) } data := buf[4 : len(buf)-36] for len(data) >= 8 { tag := binary.BigEndian.Uint32(data[:4]) if tag == adv.End { break } size := binary.BigEndian.Uint32(data[4:8]) if uint32(len(data)) < size+8 { return fmt.Errorf("adv: value goes beyond the end of the buffer: tag %d, size %d", tag, size) } value := data[8 : 8+size] a.Tags[Tag(tag)] = Value(value) data = data[8+size:] } return nil } // Marshal single copy of ADV. func (a *ADV) Marshal() ([]byte, error) { buf := make([]byte, Length) binary.BigEndian.PutUint32(buf[0:4], Magic1) binary.BigEndian.PutUint32(buf[len(buf)-4:], Magic2) data := buf[4 : len(buf)-36] for tag, value := range a.Tags { if len(value)+8 > len(data) { return nil, fmt.Errorf("adv: overflow %d bytes", len(value)+8-len(data)) } binary.BigEndian.PutUint32(data[0:4], uint32(tag)) binary.BigEndian.PutUint32(data[4:8], uint32(len(value))) copy(data[8:8+len(value)], value) data = data[8+len(value):] } hash := sha256.New() hash.Write(buf) copy(buf[len(buf)-36:len(buf)-4], hash.Sum(nil)) return buf, nil } // Bytes marshal full representation. func (a *ADV) Bytes() ([]byte, error) { marshaled, err := a.Marshal() if err != nil { return nil, err } return append(marshaled, marshaled...), nil } // ReadTag to get tag value. func (a *ADV) ReadTag(t uint8) (val string, ok bool) { b, ok := a.ReadTagBytes(t) val = string(b) return val, ok } // ReadTagBytes to get tag value. func (a *ADV) ReadTagBytes(t uint8) (val []byte, ok bool) { val, ok = a.Tags[Tag(t)] return val, ok } // ListTags to get list of tags. func (a *ADV) ListTags() (tags []uint8) { return xslices.Map(maps.Keys(a.Tags), func(t Tag) uint8 { return uint8(t) }) } // SetTag to set tag value. func (a *ADV) SetTag(t uint8, val string) (ok bool) { return a.SetTagBytes(t, []byte(val)) } // SetTagBytes to set tag value. func (a *ADV) SetTagBytes(t uint8, val []byte) (ok bool) { size := 20 // magic + checksum for _, v := range a.Tags { size += len(v) + 8 } oldVal := a.Tags[Tag(t)] size += len(Value(val)) - len(oldVal) if size > DataLength { return false } a.Tags[Tag(t)] = Value(val) return true } // DeleteTag to delete tag value. func (a *ADV) DeleteTag(t uint8) (ok bool) { _, ok = a.Tags[Tag(t)] delete(a.Tags, Tag(t)) return ok } ================================================ FILE: internal/pkg/meta/internal/adv/talos/talos_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package talos_test import ( "bytes" "slices" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv/talos" ) func TestMarshalUnmarshal(t *testing.T) { a, err := talos.NewADV(bytes.NewReader(make([]byte, talos.Size))) assert.Error(t, err) require.NotNil(t, a) const ( val1 = "value1" val2 = "value2" val3 = "value3" ) assert.True(t, a.SetTag(adv.Reserved1, val1)) assert.True(t, a.SetTag(adv.Reserved2, val2)) assert.True(t, a.SetTag(adv.Reserved3, val3)) b, err := a.Bytes() require.NoError(t, err) assert.Len(t, b, talos.Size) // test recoverable corruption for _, c := range []struct { zeroOut [][2]int }{ {}, { zeroOut: [][2]int{ {0, 2}, }, }, { zeroOut: [][2]int{ {30, 1000}, }, }, { zeroOut: [][2]int{ {8, 4}, {40, 2}, }, }, { zeroOut: [][2]int{ {0, talos.Length}, }, }, } { corrupted := slices.Clone(b) for _, z := range c.zeroOut { copy(corrupted[z[0]:z[0]+z[1]], make([]byte, z[1])) } a, err = talos.NewADV(bytes.NewReader(b)) require.NoError(t, err) require.NotNil(t, a) val, ok := a.ReadTag(adv.Reserved1) assert.True(t, ok) assert.Equal(t, val1, val) val, ok = a.ReadTag(adv.Reserved2) assert.True(t, ok) assert.Equal(t, val2, val) val, ok = a.ReadTag(adv.Reserved3) assert.True(t, ok) assert.Equal(t, val3, val) tags := a.ListTags() slices.Sort(tags) assert.Equal(t, []uint8{adv.Reserved1, adv.Reserved2, adv.Reserved3}, tags) } } ================================================ FILE: internal/pkg/meta/meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package meta provides access to META partition: key-value partition persisted across reboots. package meta import ( "context" "errors" "fmt" "io" "log" "os" goruntime "runtime" "sync" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" blockdev "github.com/siderolabs/go-blockdevice/v2/block" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv/syslinux" "github.com/siderolabs/talos/internal/pkg/meta/internal/adv/talos" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Meta represents the META reader/writer. // // Meta abstracts away all details about loading/storing the metadata providing an easy to use interface. type Meta struct { mu sync.Mutex legacy adv.ADV talos adv.ADV state state.State opts Options } // Options configures the META. type Options struct { fixedPath string printer func(string, ...any) } // Option is a functional option. type Option func(*Options) // WithFixedPath sets the fixed path to META partition. func WithFixedPath(path string) Option { return func(o *Options) { o.fixedPath = path } } // WithPrinter sets the function to print the logs, default is log.Printf. func WithPrinter(printer func(string, ...any)) Option { return func(o *Options) { o.printer = printer } } // New initializes empty META, trying to probe the existing META first. func New(ctx context.Context, st state.State, opts ...Option) (*Meta, error) { meta := &Meta{ state: st, opts: Options{ printer: log.Printf, }, } for _, opt := range opts { opt(&meta.opts) } var err error meta.legacy, err = syslinux.NewADV(nil) if err != nil { return nil, err } meta.talos, err = talos.NewADV(nil) if err != nil { return nil, err } err = meta.Reload(ctx) return meta, err } func (meta *Meta) getPath(ctx context.Context) (string, string, error) { if meta.opts.fixedPath != "" { return meta.opts.fixedPath, "", nil } if meta.state == nil { return "", "", os.ErrNotExist } metaStatus, err := block.WaitForVolumePhase(ctx, meta.state, constants.MetaPartitionLabel, block.VolumePhaseReady, block.VolumePhaseMissing, block.VolumePhaseClosed) if err != nil { return "", "", err } // add our own finalizer for the META volume to ensure it never gets removed, even in the late stages of the reboot if err = meta.state.AddFinalizer(ctx, metaStatus.Metadata(), constants.MetaPartitionLabel); err != nil { return "", "", err } if metaStatus.TypedSpec().Phase == block.VolumePhaseMissing { return "", "", os.ErrNotExist } if metaStatus.TypedSpec().Phase == block.VolumePhaseClosed && metaStatus.TypedSpec().MountLocation == "" { return "", "", os.ErrNotExist } return metaStatus.TypedSpec().MountLocation, metaStatus.TypedSpec().ParentLocation, nil } // Reload refreshes the META from the disk. // //nolint:gocyclo func (meta *Meta) Reload(ctx context.Context) error { meta.mu.Lock() defer meta.mu.Unlock() path, parentPath, err := meta.getPath(ctx) if err != nil { return err } if parentPath != "" { parentDev, err := blockdev.NewFromPath(parentPath) if err != nil { return err } defer parentDev.Close() //nolint:errcheck if err = parentDev.RetryLock(ctx, true); err != nil { return err } defer parentDev.Unlock() //nolint:errcheck } meta.opts.printer("META: loading from %s", path) f, err := os.Open(path) if err != nil { return err } defer f.Close() //nolint:errcheck if err := flock(f, unix.LOCK_SH); err != nil { return err } adv, err := talos.NewADV(f) if adv == nil && err != nil { // if adv is not nil, but err is nil, it might be missing ADV, ignore it return fmt.Errorf("failed to load Talos adv: %w", err) } legacyAdv, err := syslinux.NewADV(f) if err != nil { return fmt.Errorf("failed to load syslinux adv: %w", err) } // copy values from in-memory to on-disk version for _, t := range meta.talos.ListTags() { val, _ := meta.talos.ReadTagBytes(t) adv.SetTagBytes(t, val) } meta.opts.printer("META: loaded %d keys", len(adv.ListTags())) meta.talos = adv meta.legacy = legacyAdv return meta.syncState(ctx) } // syncState sync resources with adv contents. func (meta *Meta) syncState(ctx context.Context) error { if meta.state == nil { return nil } existingTags := make(map[resource.ID]struct{}) for _, t := range meta.talos.ListTags() { existingTags[runtime.MetaKeyTagToID(t)] = struct{}{} val, _ := meta.talos.ReadTag(t) if err := updateTagResource(ctx, meta.state, t, val); err != nil { return err } } items, err := meta.state.List(ctx, runtime.NewMetaKey(runtime.NamespaceName, "").Metadata()) if err != nil { return err } for _, item := range items.Items { if _, exists := existingTags[item.Metadata().ID()]; exists { continue } if err = meta.state.Destroy(ctx, item.Metadata()); err != nil { return err } } return nil } // Flush writes the META to the disk. // //nolint:gocyclo func (meta *Meta) Flush() error { meta.mu.Lock() defer meta.mu.Unlock() path, parentPath, err := meta.getPath(context.TODO()) if err != nil { return err } if parentPath != "" { parentDev, err := blockdev.NewFromPath(parentPath) if err != nil { return err } defer parentDev.Close() //nolint:errcheck if err = parentDev.RetryLock(context.Background(), true); err != nil { return err } defer parentDev.Unlock() //nolint:errcheck } meta.opts.printer("META: saving to %s", path) f, err := os.OpenFile(path, os.O_RDWR, 0) if err != nil { return err } defer f.Close() //nolint:errcheck if err := flock(f, unix.LOCK_EX); err != nil { return err } serialized, err := meta.talos.Bytes() if err != nil { return err } n, err := f.WriteAt(serialized, 0) if err != nil { return err } if n != len(serialized) { return fmt.Errorf("expected to write %d bytes, wrote %d", len(serialized), n) } serialized, err = meta.legacy.Bytes() if err != nil { return err } offset, err := f.Seek(-int64(len(serialized)), io.SeekEnd) if err != nil { return err } n, err = f.WriteAt(serialized, offset) if err != nil { return err } if n != len(serialized) { return fmt.Errorf("expected to write %d bytes, wrote %d", len(serialized), n) } meta.opts.printer("META: saved %d keys", len(meta.talos.ListTags())) return f.Sync() } // ReadTag reads a tag from the META. func (meta *Meta) ReadTag(t uint8) (val string, ok bool) { meta.mu.Lock() defer meta.mu.Unlock() val, ok = meta.talos.ReadTag(t) if !ok { val, ok = meta.legacy.ReadTag(t) } return val, ok } // ReadTagBytes reads a tag from the META. func (meta *Meta) ReadTagBytes(t uint8) (val []byte, ok bool) { meta.mu.Lock() defer meta.mu.Unlock() val, ok = meta.talos.ReadTagBytes(t) if !ok { val, ok = meta.legacy.ReadTagBytes(t) } return val, ok } // SetTag writes a tag to the META. func (meta *Meta) SetTag(ctx context.Context, t uint8, val string) (bool, error) { meta.mu.Lock() defer meta.mu.Unlock() ok := meta.talos.SetTag(t, val) if ok { err := updateTagResource(ctx, meta.state, t, val) if err != nil { return false, err } } return ok, nil } // SetTagBytes writes a tag to the META. func (meta *Meta) SetTagBytes(ctx context.Context, t uint8, val []byte) (bool, error) { meta.mu.Lock() defer meta.mu.Unlock() ok := meta.talos.SetTagBytes(t, val) if ok { err := updateTagResource(ctx, meta.state, t, string(val)) if err != nil { return false, err } } return ok, nil } // DeleteTag deletes a tag from the META. func (meta *Meta) DeleteTag(ctx context.Context, t uint8) (bool, error) { meta.mu.Lock() defer meta.mu.Unlock() ok := meta.talos.DeleteTag(t) if !ok { ok = meta.legacy.DeleteTag(t) } if meta.state == nil { return ok, nil } err := meta.state.Destroy(ctx, runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(t)).Metadata()) if state.IsNotFoundError(err) { err = nil } return ok, err } func updateTagResource(ctx context.Context, st state.State, t uint8, val string) error { if st == nil { return nil } _, err := safe.StateUpdateWithConflicts(ctx, st, runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(t)).Metadata(), func(r *runtime.MetaKey) error { r.TypedSpec().Value = val return nil }) if err == nil { return nil } if state.IsNotFoundError(err) { r := runtime.NewMetaKey(runtime.NamespaceName, runtime.MetaKeyTagToID(t)) r.TypedSpec().Value = val return st.Create(ctx, r) } return err } func flock(f *os.File, flag int) error { for { if err := unix.Flock(int(f.Fd()), flag); !errors.Is(err, unix.EINTR) { return err } goruntime.KeepAlive(f) } } ================================================ FILE: internal/pkg/meta/meta_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package meta provides access to META partition: key-value partition persisted across reboots. package meta_test import ( "os" "path/filepath" "testing" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/meta" metaconsts "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func setupTest(t *testing.T) (*meta.Meta, string, state.State) { t.Helper() tmpDir := t.TempDir() path := filepath.Join(tmpDir, "meta") f, err := os.Create(path) require.NoError(t, err) require.NoError(t, f.Truncate(1024*1024)) require.NoError(t, f.Close()) st := state.WrapCore(namespaced.NewState(inmem.Build)) m, err := meta.New(t.Context(), st, meta.WithFixedPath(path)) require.NoError(t, err) return m, path, st } func TestFlow(t *testing.T) { t.Parallel() m, path, st := setupTest(t) ctx := t.Context() ok, err := m.SetTag(ctx, metaconsts.Upgrade, "1.2.3") require.NoError(t, err) assert.True(t, ok) val, ok := m.ReadTag(metaconsts.Upgrade) assert.True(t, ok) assert.Equal(t, "1.2.3", val) _, ok = m.ReadTag(metaconsts.StagedUpgradeImageRef) assert.False(t, ok) ok, err = m.DeleteTag(ctx, metaconsts.Upgrade) require.NoError(t, err) assert.True(t, ok) ok, err = m.SetTag(ctx, metaconsts.StagedUpgradeInstallOptions, "install-fast") require.NoError(t, err) assert.True(t, ok) assert.NoError(t, m.Flush()) assert.NoError(t, m.Reload(ctx)) val, ok = m.ReadTag(metaconsts.StagedUpgradeInstallOptions) assert.True(t, ok) assert.Equal(t, "install-fast", val) m2, err := meta.New(ctx, st, meta.WithFixedPath(path)) require.NoError(t, err) _, ok = m2.ReadTag(metaconsts.Upgrade) assert.False(t, ok) val, ok = m2.ReadTag(metaconsts.StagedUpgradeInstallOptions) assert.True(t, ok) assert.Equal(t, "install-fast", val) list, err := safe.StateList[*runtime.MetaKey](ctx, st, runtime.NewMetaKey(runtime.NamespaceName, "").Metadata()) require.NoError(t, err) for res := range list.All() { assert.Equal(t, "0x08", res.Metadata().ID()) assert.Equal(t, "install-fast", res.TypedSpec().Value) } } ================================================ FILE: internal/pkg/miniprocfs/miniprocfs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package miniprocfs contains optimized small interface to access /proc filesystem. package miniprocfs ================================================ FILE: internal/pkg/miniprocfs/processes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package miniprocfs import ( "bytes" "errors" "io" "os" "strconv" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) const ( procsPageSize = 256 procsBufSize = 16 * 1024 userHz = 100 ) // Processes wraps iterative walker over processes under /proc. type Processes struct { fd *os.File dirnames []string idx int buf []byte pagesize int RootPath string } // NewProcesses initializes process info iterator with path /proc. func NewProcesses() (*Processes, error) { return NewProcessesWithPath("/proc") } // NewProcessesWithPath initializes process info iterator with non-default path. func NewProcessesWithPath(rootPath string) (*Processes, error) { procs := &Processes{ RootPath: rootPath, buf: make([]byte, procsBufSize), } var err error procs.fd, err = os.Open(rootPath) if err != nil { return nil, err } procs.pagesize = os.Getpagesize() return procs, nil } // Close the iterator. func (procs *Processes) Close() error { return procs.fd.Close() } // Next returns process info until the list of processes is exhausted. // // Next returns nil, nil when all processes were processed. // Next skips processes which can't be analyzed. func (procs *Processes) Next() (*machine.ProcessInfo, error) { for { if procs.idx >= len(procs.dirnames) { var err error procs.dirnames, err = procs.fd.Readdirnames(procsPageSize) if err == io.EOF { return nil, nil } if err != nil { return nil, err } procs.idx = 0 } info, err := procs.readProc(procs.dirnames[procs.idx]) procs.idx++ // if err != nil, this process was killed before we were able to read /proc data if err == nil { return info, nil } } } //nolint:gocyclo func (procs *Processes) readProc(pidString string) (*machine.ProcessInfo, error) { pid, err := strconv.ParseInt(pidString, 10, 32) if err != nil { return nil, err } path := procs.RootPath + "/" + pidString + "/" executable, err := os.Readlink(path + "exe") if err != nil { return nil, err } if err = procs.readFileIntoBuf(path + "comm"); err != nil { return nil, err } command := string(bytes.TrimSpace(procs.buf)) if err = procs.readFileIntoBuf(path + "cmdline"); err != nil { return nil, err } args := string(bytes.ReplaceAll(bytes.TrimRight(procs.buf, "\x00"), []byte{0}, []byte{' '})) if err = procs.readFileIntoBuf(path + "stat"); err != nil { return nil, err } rbracket := bytes.LastIndexByte(procs.buf, ')') if rbracket == -1 { return nil, errors.New("unexpected format") } fields := bytes.Fields(procs.buf[rbracket+2:]) state := string(fields[0]) ppid, err := strconv.ParseInt(string(fields[1]), 10, 32) if err != nil { return nil, err } numThreads, err := strconv.ParseInt(string(fields[17]), 10, 32) if err != nil { return nil, err } uTime, err := strconv.ParseUint(string(fields[11]), 10, 64) if err != nil { return nil, err } sTime, err := strconv.ParseUint(string(fields[12]), 10, 64) if err != nil { return nil, err } vSize, err := strconv.ParseUint(string(fields[20]), 10, 64) if err != nil { return nil, err } rss, err := strconv.ParseUint(string(fields[21]), 10, 64) if err != nil { return nil, err } var label string if err = procs.readFileIntoBuf(path + "attr/current"); err == nil { label = string(bytes.Trim(procs.buf, "\x00\n")) } return &machine.ProcessInfo{ Pid: int32(pid), Ppid: int32(ppid), State: state, Threads: int32(numThreads), CpuTime: float64(uTime+sTime) / userHz, VirtualMemory: vSize, ResidentMemory: rss * uint64(procs.pagesize), Command: command, Executable: executable, Args: args, Label: label, }, nil } func (procs *Processes) readFileIntoBuf(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() //nolint:errcheck procs.buf = procs.buf[:cap(procs.buf)] n, err := f.Read(procs.buf) if err != nil { return err } procs.buf = procs.buf[:n] return f.Close() } ================================================ FILE: internal/pkg/miniprocfs/processes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package miniprocfs_test import ( "strings" "testing" "github.com/prometheus/procfs" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/miniprocfs" "github.com/siderolabs/talos/pkg/machinery/api/machine" ) func TestLive(t *testing.T) { processes, err := miniprocfs.NewProcesses() require.NoError(t, err) count := 0 for { proc, err := processes.Next() require.NoError(t, err) if proc == nil { break } count++ } assert.Greater(t, count, 0) assert.NoError(t, processes.Close()) } func TestMock(t *testing.T) { processes, err := miniprocfs.NewProcessesWithPath("testdata/") require.NoError(t, err) gold, err := procfs.NewFS("testdata/") require.NoError(t, err) for { proc, err := processes.Next() require.NoError(t, err) if proc == nil { break } goldInfo, err := gold.Proc(int(proc.Pid)) require.NoError(t, err) goldExecutable, err := goldInfo.Executable() require.NoError(t, err) assert.Equal(t, goldExecutable, proc.Executable) goldCommand, err := goldInfo.Comm() require.NoError(t, err) assert.Equal(t, goldCommand, proc.Command) goldCmdline, err := goldInfo.CmdLine() require.NoError(t, err) assert.Equal(t, strings.Join(goldCmdline, " "), proc.Args) goldStat, err := goldInfo.Stat() require.NoError(t, err) assert.EqualValues(t, goldStat.PPID, proc.Ppid) assert.EqualValues(t, goldStat.NumThreads, proc.Threads) assert.EqualValues(t, goldStat.CPUTime(), proc.CpuTime) assert.EqualValues(t, goldStat.VirtualMemory(), proc.VirtualMemory) assert.EqualValues(t, goldStat.ResidentMemory(), proc.ResidentMemory) } assert.NoError(t, processes.Close()) } func BenchmarkPrometheusProcfs(b *testing.B) { b.ReportAllocs() for b.Loop() { var resp []*machine.ProcessInfo procs, err := procfs.AllProcs() require.NoError(b, err) for _, proc := range procs { executable, err := proc.Executable() if err != nil { continue } command, err := proc.Comm() if err != nil { continue } args, err := proc.CmdLine() if err != nil { continue } stats, err := proc.Stat() if err != nil { continue } resp = append(resp, &machine.ProcessInfo{ Pid: int32(proc.PID), Ppid: int32(stats.PPID), State: stats.State, Threads: int32(stats.NumThreads), CpuTime: stats.CPUTime(), VirtualMemory: uint64(stats.VirtualMemory()), ResidentMemory: uint64(stats.ResidentMemory()), Command: command, Executable: executable, Args: strings.Join(args, " "), }) } _ = resp } } func BenchmarkProcesses(b *testing.B) { b.ReportAllocs() for b.Loop() { var resp []*machine.ProcessInfo processes, err := miniprocfs.NewProcesses() require.NoError(b, err) for { proc, err := processes.Next() require.NoError(b, err) if proc == nil { break } resp = append(resp, proc) } _ = resp assert.NoError(b, processes.Close()) } } ================================================ FILE: internal/pkg/miniprocfs/testdata/1920080/comm ================================================ fish ================================================ FILE: internal/pkg/miniprocfs/testdata/1920080/stat ================================================ 1920080 (fish) S 1920079 1920080 1920080 34817 497247 4194304 290636 24169180 15 32793 1888 426 1313442 30669 20 0 2 0 127957791 465002496 5805 18446744073709551615 93935540826112 93935542596848 140726024906784 0 0 0 0 2625540 135356435 0 0 0 17 15 0 0 7 0 0 93935542601152 93935542624320 93935548821504 140726024912137 140726024912143 140726024912143 140726024912874 0 ================================================ FILE: internal/pkg/miniprocfs/testdata/3731034/comm ================================================ tail ================================================ FILE: internal/pkg/miniprocfs/testdata/3731034/stat ================================================ 3731034 (tail) S 1919986 3731034 1919986 34816 3731034 4194304 106 0 0 0 44 243 0 0 20 0 1 0 139625799 8593408 230 18446744073709551615 94174076493824 94174076538145 140729409375152 0 0 0 0 0 0 1 0 0 17 1 0 0 0 0 0 94174076561648 94174076563648 94174079950848 140729409379084 140729409379437 140729409379437 140729409380330 0 ================================================ FILE: internal/pkg/miniprocfs/testdata/keys ================================================ ================================================ FILE: internal/pkg/miniprocfs/testdata/kmsg ================================================ ================================================ FILE: internal/pkg/mount/switchroot/switchroot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package switchroot provides the switching root filesystem functionality. package switchroot import ( "fmt" "log" "os" "path/filepath" "github.com/siderolabs/go-debug" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/mount/v3" "github.com/siderolabs/talos/internal/pkg/secureboot" "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/fipsmode" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Paths preserved in the initramfs. var preservedPaths = map[string]struct{}{ constants.ExtensionsConfigFile: {}, quirks.New("").FirmwarePath(): {}, constants.SDStubDynamicInitrdPath: {}, } // Switch moves the rootfs to a specified directory. See // https://github.com/karelzak/util-linux/blob/master/sys-utils/switch_root.c. // //nolint:gocyclo func Switch(prefix string, mountpoints mount.Managers) (err error) { log.Println("moving mounts to the new rootfs") if err = mountpoints.Move(prefix); err != nil { return err } log.Printf("changing working directory into %s", prefix) if err = unix.Chdir(prefix); err != nil { return fmt.Errorf("error changing working directory to %s: %w", prefix, err) } var old *os.File if old, err = os.Open("/"); err != nil { return fmt.Errorf("error opening /: %w", err) } //nolint:errcheck defer old.Close() log.Printf("moving %s to /", prefix) if err = unix.Mount(prefix, "/", "", unix.MS_MOVE, ""); err != nil { return fmt.Errorf("error moving /: %w", err) } log.Println("changing root directory") if err = unix.Chroot("."); err != nil { return fmt.Errorf("error chroot: %w", err) } log.Println("cleaning up initramfs") if _, err = recursiveDelete(int(old.Fd()), "/"); err != nil { return fmt.Errorf("error deleting initramfs: %w", err) } if err := selinux.Init(); err != nil { return err } // extend PCR 11 with leave-initrd if err = tpm2.PCRExtend(constants.UKIPCR, []byte(secureboot.LeaveInitrd)); err != nil { return fmt.Errorf("failed to extend PCR %d with leave-initrd: %v", constants.UKIPCR, err) } // Note that /sbin/init is machined. We call it init since this is the // convention. log.Println("executing /sbin/init") envv := []string{ constants.EnvGRPCEnforccceALPNEnabled, constants.EnvTcellMinimizeEnvironment, } if debug.RaceEnabled { envv = append(envv, constants.EnvGoraceHaltOnError) log.Printf("race detection enabled with halt_on_error=1") } if val := procfs.ProcCmdline().Get("talos.fips140"); val != nil && fipsmode.Enabled() { if pointer.SafeDeref(val.First()) == "strict" { envv = append(envv, constants.EnvFIPS140ModeStrict) } } if val := procfs.ProcCmdline().Get("talos.debugshell"); val != nil { if err = unix.Exec("/bin/bash", []string{"/bin/bash"}, envv); err != nil { return fmt.Errorf("error executing /bin/bash: %w", err) } return nil } if err = unix.Exec("/sbin/init", []string{"/sbin/init"}, envv); err != nil { return fmt.Errorf("error executing /sbin/init: %w", err) } return nil } func recursiveDelete(fd int, path string) (preserved bool, err error) { parentDev, err := getDev(fd) if err != nil { return false, err } dir := os.NewFile(uintptr(fd), "__ignored__") //nolint:errcheck defer dir.Close() names, err := dir.Readdirnames(-1) if err != nil { return false, err } preserved = false for _, name := range names { p, err := recusiveDeleteInner(fd, parentDev, name, filepath.Join(path, name)) if err != nil { return false, err } preserved = preserved || p } return preserved, nil } func recusiveDeleteInner(parentFd int, parentDev uint64, childName, path string) (preserved bool, err error) { if _, preserved = preservedPaths[path]; preserved { return preserved, nil } childFd, err := unix.Openat(parentFd, childName, unix.O_DIRECTORY|unix.O_NOFOLLOW|unix.O_CLOEXEC, unix.O_RDWR) if err != nil { return false, unix.Unlinkat(parentFd, childName, 0) } //nolint:errcheck defer unix.Close(childFd) var childFdDev uint64 if childFdDev, err = getDev(childFd); err != nil { return false, err } else if childFdDev != parentDev { return false, nil } preserved, err = recursiveDelete(childFd, path) if err != nil { return false, err } if preserved { // some child paths got preserved, skip unlinking the parent return preserved, nil } err = unix.Unlinkat(parentFd, childName, unix.AT_REMOVEDIR) return false, err } func getDev(fd int) (dev uint64, err error) { var stat unix.Stat_t if err := unix.Fstat(fd, &stat); err != nil { return 0, err } return stat.Dev, nil } ================================================ FILE: internal/pkg/mount/switchroot/switchroot_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package switchroot_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: internal/pkg/mount/v3/all.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "bufio" "fmt" "log" "os" "sort" "strings" "time" "golang.org/x/sys/unix" ) func unmountWithTimeout(target string, flags int, timeout time.Duration) error { errCh := make(chan error, 1) go func() { errCh <- unix.Unmount(target, flags) }() timer := time.NewTimer(timeout) defer timer.Stop() select { case <-timer.C: return fmt.Errorf("unmounting %s timed out after %s", target, timeout) case err := <-errCh: return err } } // UnmountAll attempts to unmount all the mounted filesystems via "self" mountinfo. func UnmountAll() error { // timeout in seconds const timeout = 10 ticker := time.NewTicker(time.Second) defer ticker.Stop() for range timeout { mounts, err := readMountInfo() if err != nil { return err } failedUnmounts := 0 for _, mountInfo := range mounts { if mountInfo.MountPoint == "" { continue } if strings.HasPrefix(mountInfo.MountSource, "/dev/") { err = unmountWithTimeout(mountInfo.MountPoint, 0, time.Second) if err == nil { log.Printf("unmounted %s (%s)", mountInfo.MountPoint, mountInfo.MountSource) } else { log.Printf("failed unmounting %s: %s", mountInfo.MountPoint, err) failedUnmounts++ } } } if failedUnmounts == 0 { break } log.Printf("retrying %d unmount operations...", failedUnmounts) <-ticker.C } return nil } type mountInfo struct { MountPoint string MountSource string } func readMountInfo() ([]mountInfo, error) { f, err := os.Open("/proc/self/mountinfo") if err != nil { return nil, err } defer f.Close() //nolint:errcheck var mounts []mountInfo scanner := bufio.NewScanner(f) for scanner.Scan() { line := scanner.Text() parts := strings.SplitN(line, " - ", 2) if len(parts) < 2 { continue } var mntInfo mountInfo pre := strings.Fields(parts[0]) post := strings.Fields(parts[1]) if len(pre) >= 5 { mntInfo.MountPoint = pre[4] } if len(post) >= 2 { mntInfo.MountSource = post[1] } mounts = append(mounts, mntInfo) } return mounts, scanner.Err() } func getSubmounts(target string) ([]string, error) { mounts, err := readMountInfo() if err != nil { return nil, err } var submounts []string for _, mnt := range mounts { if mnt.MountPoint == target { continue } if strings.HasPrefix(mnt.MountPoint, target+"/") { submounts = append(submounts, mnt.MountPoint) } } sort.Slice(submounts, func(i, j int) bool { return len(submounts[i]) > len(submounts[j]) }) return submounts, nil } ================================================ FILE: internal/pkg/mount/v3/helpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "errors" "fmt" "os" "path/filepath" "strings" "github.com/freddierice/go-losetup/v2" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) // // NOTE: This file is a rewrite of various helpers from mount/v2. // func discard(string, ...any) {} // NewCgroup2 creates a new cgroup2 filesystem. func NewCgroup2() *Manager { return NewManager( WithTarget(constants.CgroupMountPath), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_RELATIME), WithFsopen( "cgroup2", fsopen.WithBoolParameter("nsdelegate"), fsopen.WithBoolParameter("memory_recursiveprot"), ), ) } // NewReadOnlyOverlay creates a new read-only overlay filesystem. func NewReadOnlyOverlay(sources []string, target string, printer func(string, ...any), options ...ManagerOption) *Manager { fsOptions := []fsopen.Option{} if printer != nil { printer("mounting %d overlays: %v", len(sources), sources) } if len(sources) > 1 { for _, source := range sources { fsOptions = append(fsOptions, fsopen.WithStringParameter("lowerdir+", source)) } } else if len(sources) == 1 { fsOptions = append(fsOptions, fsopen.WithStringParameter("lowerdir", sources[0])) } options = append(options, WithPrinter(printer), WithTarget(target), WithReadOnly(), WithFsopen("overlay", fsOptions...), ) return NewManager(options...) } // NewOverlayWithBasePath creates a new overlay filesystem with a base path. func NewOverlayWithBasePath(sources []string, target, basePath string, printer func(string, ...any), options ...ManagerOption) *Manager { _, overlayPrefix, _ := strings.Cut(target, "/") overlayPrefix = strings.ReplaceAll(overlayPrefix, "/", "-") diff := fmt.Sprintf(filepath.Join(basePath, "%s-diff"), overlayPrefix) workdir := fmt.Sprintf(filepath.Join(basePath, "%s-workdir"), overlayPrefix) fsOptions := []fsopen.Option{ fsopen.WithStringParameter("upperdir", diff), fsopen.WithStringParameter("workdir", workdir), } if len(sources) > 1 { for _, source := range sources { fsOptions = append(fsOptions, fsopen.WithStringParameter("lowerdir+", source)) } } else if len(sources) == 1 { fsOptions = append(fsOptions, fsopen.WithStringParameter("lowerdir", sources[0])) } options = append(options, WithTarget(target), WithExtraDirs(diff, workdir), WithFsopen("overlay", fsOptions...), WithPrinter(printer), ) return NewManager(options...) } // NewVarOverlay creates a new /var overlay filesystem. func NewVarOverlay(sources []string, target string, printer func(string, ...any), options ...ManagerOption) *Manager { return NewOverlayWithBasePath(sources, target, constants.VarSystemOverlaysPath, printer, options...) } // NewSystemOverlay creates a new /system overlay filesystem. func NewSystemOverlay(sources []string, target string, printer func(string, ...any), options ...ManagerOption) *Manager { return NewOverlayWithBasePath(sources, target, constants.SystemOverlaysPath, printer, options...) } // Squashfs binds the squashfs to the loop device and returns the mountpoint for it to the specified target. func Squashfs(target, squashfsFile string, printer func(string, ...any)) (*Manager, error) { dev, err := losetup.Attach(squashfsFile, 0, true) if err != nil { return nil, err } return NewManager( WithTarget(target), WithPrinter(printer), WithReadOnly(), WithShared(), WithExtraUnmountCallbacks(func(m *Manager) { dev.Detach() //nolint:errcheck }), WithFsopen( "squashfs", fsopen.WithSource(dev.Path()), fsopen.WithBoolParameter("ro"), ), ), nil } func gather[T comparable](c ...func() T) []T { var ( zero T vals []T ) for _, f := range c { val := f() if val != zero { vals = append(vals, val) } } return vals } func newManager(condition func() bool, opts ...ManagerOption) func() *Manager { return func() *Manager { if !condition() { return nil } return NewManager(opts...) } } func always() bool { return true } func hasEFIVars() bool { _, err := os.Stat(constants.EFIVarsMountPoint) if err == nil || errors.Is(err, os.ErrNotExist) { return err == nil } // this means something else is wrong, let's panic // as this should never happen panic(err) } // Pseudo creates basic filesystem mountpoint managers. func Pseudo(printer func(string, ...any)) Managers { return gather( newManager( always, WithPrinter(printer), WithTarget("/dev"), WithKeepOpenAfterMount(), WithMountAttributes(unix.MOUNT_ATTR_NOSUID), WithFsopen( "devtmpfs", fsopen.WithStringParameter("mode", "0755"), ), ), newManager( always, WithPrinter(printer), WithTarget("/proc"), WithKeepOpenAfterMount(), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV), WithFsopen("proc"), ), newManager( always, WithPrinter(printer), WithTarget("/sys"), WithKeepOpenAfterMount(), WithFsopen("sysfs"), ), ) } // PseudoLate creates late pseudo filesystem mountpoint managers. func PseudoLate(printer func(string, ...any)) Managers { return gather( newManager( always, WithPrinter(printer), WithTarget("/run"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_RELATIME), WithSelinuxLabel(constants.RunSelinuxLabel), WithFsopen( "tmpfs", fsopen.WithStringParameter("mode", "0755"), ), ), newManager( always, WithPrinter(printer), WithTarget("/system"), WithSelinuxLabel(constants.SystemSelinuxLabel), WithRecursiveUnmount(), WithFsopen( "tmpfs", fsopen.WithStringParameter("mode", "0755"), ), ), newManager( always, WithPrinter(printer), WithTarget("/tmp"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV), WithFsopen( "tmpfs", fsopen.WithStringParameter("mode", "0755"), fsopen.WithStringParameter("size", "64M"), ), ), ) } // PseudoSub creates additional pseudo filesystem mountpoint managers. func PseudoSub(printer func(string, ...any)) Managers { return gather( newManager( always, WithPrinter(printer), WithTarget("/dev/shm"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME), WithFsopen("tmpfs"), ), newManager( always, WithPrinter(printer), WithTarget("/dev/pts"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC), WithFsopen( "devpts", fsopen.WithStringParameter("ptmxmode", "000"), fsopen.WithStringParameter("mode", "620"), fsopen.WithStringParameter("gid", "5"), ), ), newManager( always, WithPrinter(printer), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NODEV), WithTarget("/dev/hugepages"), WithFsopen("hugetlbfs"), ), newManager( always, WithPrinter(printer), WithTarget("/sys/fs/bpf"), WithFsopen("bpf"), ), newManager( always, WithPrinter(printer), WithTarget("/sys/kernel/security"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME), WithFsopen("securityfs"), ), newManager( always, WithPrinter(printer), WithTarget("/sys/kernel/tracing"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV), WithFsopen("tracefs"), ), newManager( always, WithPrinter(printer), WithTarget("/sys/kernel/config"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME), WithFsopen("configfs"), ), newManager( always, WithPrinter(printer), WithTarget("/sys/kernel/debug"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME), WithFsopen("debugfs"), ), newManager( selinux.IsEnabled, WithPrinter(printer), WithTarget("/sys/fs/selinux"), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_RELATIME), WithFsopen("selinuxfs"), ), newManager( hasEFIVars, WithPrinter(printer), WithTarget(constants.EFIVarsMountPoint), WithMountAttributes(unix.MOUNT_ATTR_NOSUID|unix.MOUNT_ATTR_NOEXEC|unix.MOUNT_ATTR_NODEV|unix.MOUNT_ATTR_RELATIME|unix.MOUNT_ATTR_RDONLY), WithFsopen("efivarfs"), ), ) } ================================================ FILE: internal/pkg/mount/v3/manager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "fmt" "os" "path/filepath" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" "github.com/siderolabs/talos/pkg/xfs/opentree" ) // Manager is the filesystem manager for mounting and unmounting filesystems. type Manager struct { fs xfs.FS target string printer func(string, ...any) selinuxLabel string shared bool skipIfMounted bool keepOpen bool detached bool mountattr int extraDirs []string extraUnmountCallbacks []func(m *Manager) recursiveUnmount bool point *Point } // NewManager creates a new Manager with the given options. func NewManager(opts ...ManagerOption) *Manager { m := &Manager{} for _, opt := range opts { opt.set(m) } return m } // Mount mounts a filesystem with the given options. func (m *Manager) Mount() (*Point, error) { printer := discard if m.printer != nil { printer = m.printer } for _, dir := range m.extraDirs { printer("creating directory tree %q", dir) if !filepath.IsAbs(dir) { return nil, fmt.Errorf("dir %q is not absolute", dir) } if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("error creating mount point directory %q: %w", dir, err) } } root := &xfs.UnixRoot{FS: m.fs} if err := root.OpenFS(); err != nil { return nil, fmt.Errorf("openfs failed: %w", err) } m.point = &Point{ root: root, detached: m.detached, keepOpen: m.keepOpen, target: m.target, selinuxLabel: m.selinuxLabel, } opts := Options{ Printer: printer, Shared: m.shared, SkipIfMounted: m.skipIfMounted, MountAttributes: m.mountattr, } if !m.detached { if err := os.MkdirAll(m.target, 0o755); err != nil { return nil, fmt.Errorf("failed to create mount target %s: %w", m.target, err) } printer("mounting %q to %q", m.point.Source(), m.target) } if err := m.point.Mount(opts); err != nil { return nil, fmt.Errorf("failed to mount: %w", err) } return m.point, nil } // Unmount the mount point. func (m *Manager) Unmount() error { printer := discard if m.printer != nil { printer = m.printer } opts := UnmountOptions{ Printer: printer, Recursive: m.recursiveUnmount, } for _, cb := range m.extraUnmountCallbacks { defer cb(m) } return m.point.Unmount(opts) } // Move the mount point to a new target. func (m *Manager) Move(newTarget string) error { return m.point.Move(newTarget) } // ManagerOption is an option for configuring the Manager. type ManagerOption struct { set func(*Manager) } // WithTarget sets the target mount point. func WithTarget(target string) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.target = target }, } } // WithKeepOpenAfterMount assesses if the mountpoint fd should be kept open after the Mount. func WithKeepOpenAfterMount() ManagerOption { return ManagerOption{ set: func(m *Manager) { m.keepOpen = true }, } } // WithSelinuxLabel sets the mount SELinux label. func WithSelinuxLabel(label string) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.selinuxLabel = label }, } } // WithShared sets the mount as shared. func WithShared() ManagerOption { return ManagerOption{ set: func(m *Manager) { m.shared = true }, } } // WithPrinter sets the printer function for logging. func WithPrinter(printer func(string, ...any)) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.printer = printer }, } } // WithSkipIfMounted sets the option to skip mounting if already mounted. func WithSkipIfMounted() ManagerOption { return ManagerOption{ set: func(m *Manager) { m.skipIfMounted = true }, } } // WithFsopen sets the filesystem opener with the given type and options. func WithFsopen(fstype string, opts ...fsopen.Option) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.fs = fsopen.New(fstype, opts...) }, } } // WithOpentreeFromPath sets the opentree opener with the path. func WithOpentreeFromPath(path string) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.fs = opentree.NewFromPath(path) }, } } // WithMountAttributes sets the mount attributes. func WithMountAttributes(flags int) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.mountattr |= flags }, } } // WithDisableAccessTime sets MOUNT_ATTR_NOATIME. func WithDisableAccessTime() ManagerOption { return WithMountAttributes(unix.MOUNT_ATTR_NOATIME) } // WithSecure sets MOUNT_ATTR_NOSUID and MOUNT_ATTR_NODEV. func WithSecure() ManagerOption { return WithMountAttributes(unix.MOUNT_ATTR_NOSUID | unix.MOUNT_ATTR_NODEV) } // WithReadOnly sets the mount as read only. func WithReadOnly() ManagerOption { return WithMountAttributes(unix.MOUNT_ATTR_RDONLY) } // WithDetached sets the mount as detached. func WithDetached() ManagerOption { return ManagerOption{ set: func(m *Manager) { m.detached = true m.keepOpen = true }, } } // WithExtraDirs adds extra dirs to the manager that should be created prior to mounting the filesystem. func WithExtraDirs(dirs ...string) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.extraDirs = append(m.extraDirs, dirs...) }, } } // WithExtraUnmountCallbacks adds extra callbacks to the unmount operation. // Those need to handle errors by themselves. func WithExtraUnmountCallbacks(callbacks ...func(m *Manager)) ManagerOption { return ManagerOption{ set: func(m *Manager) { m.extraUnmountCallbacks = append(m.extraUnmountCallbacks, callbacks...) }, } } // WithRecursiveUnmount enables recursive unmounting of all child mounts before unmounting the target. func WithRecursiveUnmount() ManagerOption { return ManagerOption{ set: func(m *Manager) { m.recursiveUnmount = true }, } } ================================================ FILE: internal/pkg/mount/v3/managers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "errors" "path/filepath" "slices" ) // Managers is a list of mount managers. type Managers []*Manager // Mount all mount points managed by managers. func (managers Managers) Mount() (func() error, error) { unmounters := make([]func() error, 0, len(managers)) for _, manager := range managers { if _, err := manager.Mount(); err != nil { // unmount what got already mounted slices.Reverse(unmounters) for _, unmounter := range unmounters { if unmountErr := unmounter(); unmountErr != nil { err = errors.Join(err, unmountErr) } } return nil, err } unmounters = append(unmounters, manager.Unmount) } // unmount last to first slices.Reverse(unmounters) return func() error { var unmountErr error for _, unmounter := range unmounters { if err := unmounter(); err != nil { unmountErr = errors.Join(unmountErr, err) } } return unmountErr }, nil } // Unmount all mount points managed by managers. func (managers Managers) Unmount() error { for i := len(managers) - 1; i >= 0; i-- { if err := managers[i].Unmount(); err != nil { return err } } return nil } // Move all mount managers to a new prefix. func (managers Managers) Move(prefix string) error { for _, manager := range managers { if err := manager.Move(filepath.Join(prefix, manager.target)); err != nil { return err } } return nil } ================================================ FILE: internal/pkg/mount/v3/mount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package mount handles filesystem mount operations. package mount import ( "fmt" "golang.org/x/sys/unix" ) // BindReadonly creates a common way to create a readonly bind mounted destination. func BindReadonly(src, dst string) error { sourceFD, err := unix.OpenTree(unix.AT_FDCWD, src, unix.OPEN_TREE_CLONE|unix.OPEN_TREE_CLOEXEC) if err != nil { return fmt.Errorf("failed to opentree source %s: %w", src, err) } defer unix.Close(sourceFD) //nolint:errcheck if err := unix.MountSetattr(sourceFD, "", unix.AT_EMPTY_PATH, &unix.MountAttr{ Attr_set: unix.MOUNT_ATTR_RDONLY, }); err != nil { return fmt.Errorf("failed to set mount attribute: %w", err) } if err := unix.MoveMount(sourceFD, "", unix.AT_FDCWD, dst, unix.MOVE_MOUNT_F_EMPTY_PATH); err != nil { return fmt.Errorf("failed to move mount from %s to %s: %w", src, dst, err) } return nil } // BindReadonlyFd creates a common way to create a readonly bind mounted destination. func BindReadonlyFd(dfd int, dst string) error { sourceFD, err := unix.OpenTree(dfd, "", unix.OPEN_TREE_CLONE|unix.OPEN_TREE_CLOEXEC|unix.AT_EMPTY_PATH) if err != nil { return fmt.Errorf("failed to opentree: %w", err) } defer unix.Close(sourceFD) //nolint:errcheck if err := unix.MountSetattr(sourceFD, "", unix.AT_EMPTY_PATH, &unix.MountAttr{ Attr_set: unix.MOUNT_ATTR_RDONLY, }); err != nil { return fmt.Errorf("failed to set mount attribute: %w", err) } if err := unix.MoveMount(sourceFD, "", unix.AT_FDCWD, dst, unix.MOVE_MOUNT_F_EMPTY_PATH); err != nil { return fmt.Errorf("failed to move mount to %s: %w", dst, err) } return nil } ================================================ FILE: internal/pkg/mount/v3/point.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "bufio" "context" "errors" "fmt" "os" "strings" "syscall" "time" "github.com/siderolabs/go-retry/retry" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/selinux" "github.com/siderolabs/talos/pkg/xfs" ) // Point represents a mount point in the filesystem. type Point struct { root xfs.Root keepOpen bool detached bool fstype string source string target string selinuxLabel string } // NewPoint creates a new mount point. func NewPoint(source string, srcfd int, target string, targetfd int, fstype string) *Point { return &Point{ source: source, target: target, fstype: fstype, } } // Options represents options for mounting a mount point. type Options struct { SkipIfMounted bool MountAttributes int Shared bool Printer func(string, ...any) } // Mount the mount point. // //nolint:gocyclo func (p *Point) Mount(opts Options) error { defer p.Release(false) //nolint:errcheck if p.detached { return nil } if opts.SkipIfMounted { isMounted, err := p.IsMounted() if err != nil { return err } if isMounted { return nil } } return p.retry(func() error { if err := p.moveMount(p.target); err != nil { return fmt.Errorf("error mounting %q to %q: %w", p.Source(), p.target, err) } if err := p.setattr(&unix.MountAttr{ Attr_set: uint64(opts.MountAttributes), }, 0); err != nil { return fmt.Errorf("error setting mountattributes on %s: %w", p.target, err) } if opts.Shared { if err := p.Share(); err != nil { return fmt.Errorf("error making %q shared: %w", p.target, err) } } return FilterSelinuxLabelErrors(p.Target(), p.FSType(), selinux.SetLabel(p.target, p.selinuxLabel)) }, false) } // FilterSelinuxLabelErrors filters out certain errors when setting the SELinux label on the mount point. // - ENOTSUP is ignored for all filesystems, as it indicates that the filesystem does not support extended attributes. // - EROFS is ignored for virtiofs, as it indicates that the underlying filesystem is read-only and does not support setting labels. func FilterSelinuxLabelErrors(target, fstype string, err error) error { if err == nil { return nil } if errors.Is(err, unix.ENOTSUP) { return nil } if fstype == "virtiofs" && errors.Is(err, unix.EROFS) { return nil } return fmt.Errorf("error setting selinux label on %q: %w", target, err) } // Share makes the mount point shared. func (p *Point) Share() error { if p.detached { return syscall.EINVAL } return p.setattr(&unix.MountAttr{ Propagation: unix.MS_SHARED, }, unix.AT_RECURSIVE) } // UnmountOptions represents options for unmounting a mount point. type UnmountOptions struct { Printer func(string, ...any) Recursive bool } // Release closes the file descriptor of the underlying mount point. func (p *Point) Release(force bool) error { if p.keepOpen && !force { return nil } return p.root.Close() } // Unmount unmounts the mount point, retrying on certain errors. func (p *Point) Unmount(opts UnmountOptions) error { if err := p.Release(true); err != nil { return err } if p.detached { return nil } return p.retry(func() error { return SafeUnmount(context.Background(), opts.Printer, p.target, opts.Recursive) }, true) } // IsMounted checks if the mount point is mounted by checking the mount on the target. func (p *Point) IsMounted() (bool, error) { f, err := os.Open("/proc/mounts") if err != nil { return false, err } defer f.Close() //nolint:errcheck scanner := bufio.NewScanner(f) for scanner.Scan() { fields := strings.Fields(scanner.Text()) if len(fields) < 2 { continue } mountpoint := fields[1] if mountpoint == p.target { return true, nil } } return false, scanner.Err() } // Move the mount point to a new target. func (p *Point) Move(newTarget string) error { return p.moveMount(newTarget) } //nolint:gocyclo func (p *Point) retry(f func() error, isUnmount bool) error { return retry.Constant(5*time.Second, retry.WithUnits(50*time.Millisecond)).Retry(func() error { if err := f(); err != nil { switch err { case unix.EBUSY: return retry.ExpectedError(err) case unix.ENOENT, unix.ENXIO: // if udevd triggers BLKRRPART ioctl, partition device entry might disappear temporarily return retry.ExpectedError(err) case unix.EUCLEAN, unix.EIO: if !isUnmount { if errRepair := p.root.RepairFS(context.Background()); errRepair != nil { return fmt.Errorf("error repairing: %w", errRepair) } } return retry.ExpectedError(err) case unix.EINVAL: isMounted, checkErr := p.IsMounted() if checkErr != nil { return retry.ExpectedError(checkErr) } if !isMounted && !isUnmount { if errRepair := p.root.RepairFS(context.Background()); errRepair != nil { return fmt.Errorf("error repairing: %w", errRepair) } return retry.ExpectedError(err) } if !isMounted && isUnmount { // if partition is already unmounted, ignore EINVAL return nil } return err default: return err } } return nil }) } func (p *Point) moveMount(target string) error { fd, err := p.root.Fd() if err != nil { if p.Source() != "" { if err := unix.MoveMount(unix.AT_FDCWD, p.Source(), unix.AT_FDCWD, target, 0); err != nil { return fmt.Errorf("error moving mount from %q to %q: %w", p.Source(), target, err) } return nil } return fmt.Errorf("error getting root fd: %w", err) } if err := unix.MoveMount(fd, "", unix.AT_FDCWD, target, unix.MOVE_MOUNT_F_EMPTY_PATH); err != nil { return fmt.Errorf("error moving detached mount fd=%d to %q: %w", fd, target, err) } return nil } // Target returns the target of the mount point. func (p *Point) Target() string { return p.target } // Source returns the source of the mount point. func (p *Point) Source() string { if p.source == "" { return p.root.Source() } return p.source } // FSType returns the filesystem type of the mount point. func (p *Point) FSType() string { if p.fstype == "" { return p.root.FSType() } return p.fstype } // Root returns the underlying xfs.Root of the mount point. func (p *Point) Root() xfs.Root { return p.root } // RemountReadOnly remounts the mount point as read-only. func (p *Point) RemountReadOnly() error { if p.detached { return nil } return p.setattr(&unix.MountAttr{ Attr_set: unix.MOUNT_ATTR_RDONLY, }, 0) } // RemountReadWrite remounts the mount point as read-write. func (p *Point) RemountReadWrite() error { if p.detached { return nil } return p.setattr(&unix.MountAttr{ Attr_clr: unix.MOUNT_ATTR_RDONLY, }, 0) } // SetDisableAccessTime sets or clears the noatime mount attribute. func (p *Point) SetDisableAccessTime(disable bool) error { if p.detached { return nil } if disable { return p.setattr(&unix.MountAttr{ Attr_set: unix.MOUNT_ATTR_NOATIME, }, 0) } return p.setattr(&unix.MountAttr{ Attr_clr: unix.MOUNT_ATTR_NOATIME, }, 0) } // SetSecure sets or clears the nosuid and nodev mount attributes. func (p *Point) SetSecure(secure bool) error { if p.detached { return nil } if secure { return p.setattr(&unix.MountAttr{ Attr_set: unix.MOUNT_ATTR_NOSUID | unix.MOUNT_ATTR_NODEV, }, 0) } return p.setattr(&unix.MountAttr{ Attr_clr: unix.MOUNT_ATTR_NOSUID | unix.MOUNT_ATTR_NODEV, }, 0) } func (p *Point) setattr(attr *unix.MountAttr, flags int) error { if (attr.Attr_set&unix.MOUNT_ATTR_NOATIME) != 0 || (attr.Attr_clr&unix.MOUNT_ATTR_NOATIME) != 0 { attr.Attr_clr |= unix.MOUNT_ATTR__ATIME } fd, err := p.root.Fd() if err != nil && !errors.Is(err, os.ErrClosed) { return err } if p.target != "" { fd = unix.AT_FDCWD } else { flags |= unix.AT_EMPTY_PATH } if err := unix.MountSetattr(fd, p.target, uint(flags), attr); err != nil { return fmt.Errorf("setattr failed for fd=%d target=%q flags=%d attr=%#+v: %w", fd, p.target, flags, *attr, err) } return nil } ================================================ FILE: internal/pkg/mount/v3/unmount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package mount import ( "context" "fmt" "time" "golang.org/x/sys/unix" ) func trySyncMount(target string, printer func(string, ...any)) error { // open the mountpoint directory to get an fd on the fs fd, err := unix.Open(target, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_CLOEXEC, 0) if err != nil { return fmt.Errorf("open %q: %w", target, err) } defer unix.Close(fd) //nolint:errcheck // sync the filesystem backing this fd if err := unix.Syncfs(fd); err != nil { return fmt.Errorf("SYS_SYNCFS %q: %w", target, err) } printer("syncfs(%s) ok", target) return nil } func unmountLoop(ctx context.Context, printer func(string, ...any), target string, flags int, timeout time.Duration, extraMessage string) (bool, error) { errCh := make(chan error, 1) // we need to try to sync fs before if err := trySyncMount(target, printer); err != nil { printer("sync failed: %s", err) } go func() { errCh <- unix.Unmount(target, flags) }() start := time.Now() progressTicker := time.NewTicker(timeout / 5) defer progressTicker.Stop() unmountLoop: for { select { case <-ctx.Done(): return true, ctx.Err() case err := <-errCh: return true, err case <-progressTicker.C: timeLeft := timeout - time.Since(start) if timeLeft <= 0 { break unmountLoop } printer("unmounting %s%s is taking longer than expected, still waiting for %s", target, extraMessage, timeLeft) } } return false, nil } // SafeUnmount unmounts the target path, first without force, then with force if the first attempt fails. // // It makes sure that unmounting has a finite operation timeout. // If recursive is true, it will first unmount all child mounts under target. func SafeUnmount(ctx context.Context, printer func(string, ...any), target string, recursive bool) error { const ( unmountTimeout = 90 * time.Second unmountForceTimeout = 10 * time.Second ) if printer == nil { printer = discard } if recursive { submounts, err := getSubmounts(target) if err != nil { printer("failed to get submounts for %s: %v", target, err) } else { for _, submount := range submounts { printer("recursively unmounting submount %s", submount) if err := safeUnmountSingle(ctx, printer, submount, unmountTimeout); err != nil { printer("failed to unmount submount %s: %v", submount, err) } } } } ok, err := unmountLoop(ctx, printer, target, 0, unmountTimeout, "") if ok { return err } logSubmounts(printer, target) printer("unmounting %s with force", target) ok, err = unmountLoop(ctx, printer, target, unix.MNT_FORCE, unmountForceTimeout, " with force flag") if ok { return err } logSubmounts(printer, target) return fmt.Errorf("unmounting %s with force flag timed out", target) } func safeUnmountSingle(ctx context.Context, printer func(string, ...any), target string, timeout time.Duration) error { ok, err := unmountLoop(ctx, printer, target, 0, timeout, "") if ok { return err } return nil } func logSubmounts(printer func(string, ...any), target string) { submounts, err := getSubmounts(target) if err != nil { printer("failed to get submounts for %s: %v", target, err) return } if len(submounts) > 0 { printer("submounts on %s: %v", target, submounts) } } ================================================ FILE: internal/pkg/msguid/LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: internal/pkg/msguid/README.md ================================================ # msguid This package was copied from the upstream repository at [msguid](https://github.com/monogon/monogon/tree/main/osbase/msguid) There were minimal changes done to comply with the linter and gofmt rules. ================================================ FILE: internal/pkg/msguid/msguid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 // Package msguid provides functions to convert UUIDs/GUIDs to and from // Microsoft's idiosyncratic "mixed-endian" format. // See https://uefi.org/specs/UEFI/2.10/Apx_A_GUID_and_Time_Formats.html#text-representation-relationships-apxa-guid-and-time-formats // for an explanation of the format. package msguid import "github.com/google/uuid" var mixedEndianTranspose = []int{3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15} // From converts from a standard UUID into its mixed-endian encoding. func From(u uuid.UUID) (o [16]byte) { for dest, from := range mixedEndianTranspose { o[dest] = u[from] } return o } // To converts a mixed-endian-encoded UUID to its standard format. func To(i [16]byte) (o uuid.UUID) { for from, dest := range mixedEndianTranspose { o[dest] = i[from] } return o } ================================================ FILE: internal/pkg/msguid/msguid_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright The Monogon Project Authors. // SPDX-License-Identifier: Apache-2.0 package msguid_test import ( "testing" "github.com/google/uuid" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/msguid" ) func TestRoundTrip(t *testing.T) { cases := []struct { name string uuid string expected [16]byte }{ { "WikipediaExample1", "00112233-4455-6677-8899-AABBCCDDEEFF", [16]byte{ 0x33, 0x22, 0x11, 0x00, 0x55, 0x44, 0x77, 0x66, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, }, }, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { origUUID := uuid.MustParse(c.uuid) got := msguid.From(origUUID) require.Equal(t, c.expected, got, "From(%q) returned unexpected result", origUUID) back := msguid.To(got) require.Equal(t, origUUID, back, "From(To(%q)) did not return original value", origUUID) }) } } ================================================ FILE: internal/pkg/ntp/consts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package ntp import "time" const ( // MinAllowablePoll is the minimum time allowed for a client to query a time server. MinAllowablePoll = 32 * time.Second // MaxAllowablePoll is the maximum allowed interval for querying a time server. MaxAllowablePoll = 2048 * time.Second // RetryPoll is the interval between retries if the error is not Kiss-o-Death. RetryPoll = time.Second // AdjustTimeLimit is a maximum time drift to compensate via adjtimex(). // // Deltas smaller than AdjustTimeLimit are gradually adjusted (slewed) to approach the network time. // Deltas larger than AdjustTimeLimit are set by letting the system time jump. AdjustTimeLimit = 400 * time.Millisecond // EpochLimit is a minimum time difference to signal that change as epoch change. EpochLimit = 15 * time.Minute // ExpectedAccuracy is the expected time sync accuracy, used to adjust poll interval. ExpectedAccuracy = 200 * time.Millisecond ) ================================================ FILE: internal/pkg/ntp/interfaces.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package ntp import ( "syscall" "time" "github.com/beevik/ntp" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/timex" ) // CurrentTimeFunc provides a function which returns current time. type CurrentTimeFunc func() time.Time // QueryFunc provides a function which performs NTP query. type QueryFunc func(server string) (*ntp.Response, error) // SetTimeFunc provides a function to set system time. type SetTimeFunc func(tv *syscall.Timeval) error // AdjustTimeFunc provides a function to adjust time. type AdjustTimeFunc func(buf *unix.Timex) (state timex.State, err error) ================================================ FILE: internal/pkg/ntp/internal/spike/spike.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package spike provides a spike detector for NTP responses. package spike import ( "math" "github.com/beevik/ntp" ) const defaultCapacity = 8 // Sample is a single NTP response sample. type Sample struct { Offset, RTT float64 // in seconds } // SampleFromNTPResponse converts an NTP response to a Sample. func SampleFromNTPResponse(resp *ntp.Response) Sample { return Sample{ Offset: resp.ClockOffset.Seconds(), RTT: resp.RTT.Seconds(), } } // Detector detects spikes in NTP response samples. // // Zero value is ready to use. type Detector struct { packetCount int64 samples []Sample samplesIdx int samplesJitter float64 } // IsSpike returns true if the given sample is a spike. // // The sample is added to the detector's internal state. func (d *Detector) IsSpike(sample Sample) bool { if d.samples == nil { d.samples = make([]Sample, defaultCapacity) } d.packetCount++ if d.packetCount == 1 { // ignore first packet return false } var currentIndex int currentIndex, d.samplesIdx = d.samplesIdx, (d.samplesIdx+1)%len(d.samples) d.samples[d.samplesIdx] = sample jitter := d.samplesJitter indexMin := currentIndex for i := range d.samples { if d.samples[i].RTT == 0 { continue } if d.samples[i].RTT < d.samples[indexMin].RTT { indexMin = i } } var j float64 for i := range d.samples { offsetDiff := d.samples[i].Offset - d.samples[indexMin].Offset j += offsetDiff * offsetDiff } d.samplesJitter = math.Sqrt(j / (float64(len(d.samples)) - 1)) if math.Abs(sample.Offset) > sample.RTT { // always accept clock offset if that is larger than rtt return false } if d.packetCount < 4 { // need more samples to make a decision return false } // This check was specifically removed (while it exists in systemd-timesync), // as I don't understand why it's needed (@smira). // It seems to give false positives when the RTT and Offset are close to each other, // e.g. when NTP server is on the same LAN. // // if math.Abs(sample.Offset) > d.samples[indexMin].RTT { // // do not accept anything worse than the maximum possible error of the best sample // return true // } // check that diff to the last offset is not more than 3*(observed jitter) return math.Abs(sample.Offset-d.samples[currentIndex].Offset) > 3*jitter } // Jitter returns the current jitter. func (d *Detector) Jitter() float64 { return d.samplesJitter } ================================================ FILE: internal/pkg/ntp/internal/spike/spike_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package spike_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/ntp/internal/spike" ) func TestSpikeDetector(t *testing.T) { for _, test := range []struct { name string samples []spike.Sample expectedSpikes []bool }{ { name: "no spikes", samples: []spike.Sample{ {Offset: 0.01, RTT: 0.01}, {Offset: 0.05, RTT: 0.01}, {Offset: 0.03, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: -0.01, RTT: 0.01}, {Offset: -0.02, RTT: 0.03}, // not a spike, just a large RTT }, expectedSpikes: []bool{ false, false, false, false, false, false, }, }, { name: "offset spike", samples: []spike.Sample{ {Offset: 0.01, RTT: 0.01}, {Offset: 0.05, RTT: 0.01}, {Offset: 0.03, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: -0.01, RTT: 0.01}, {Offset: -0.5, RTT: 0.7}, // spike }, expectedSpikes: []bool{ false, false, false, false, false, false, false, true, }, }, { name: "adjusting to higher RTT", samples: []spike.Sample{ {Offset: 0.01, RTT: 0.01}, {Offset: 0.05, RTT: 0.01}, {Offset: 0.03, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: -0.01, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, {Offset: -0.01, RTT: 0.01}, {Offset: -0.5, RTT: 0.7}, // spike {Offset: 0.5, RTT: 0.7}, // spike {Offset: -0.5, RTT: 0.7}, // spike {Offset: 0.5, RTT: 0.7}, // not a spike anymore, filter adjusted itself {Offset: -0.5, RTT: 0.7}, {Offset: 0.01, RTT: 0.01}, }, expectedSpikes: []bool{ false, false, false, false, false, false, false, true, true, true, false, false, false, }, }, { name: "initial ignore", samples: []spike.Sample{ {Offset: 5, RTT: 0.01}, // initial packet is ignored completely {Offset: 0.05, RTT: 0.05}, {Offset: 0.5, RTT: 0.5}, // spike detection kicks in after 4 packets {Offset: 0.01, RTT: 0.01}, {Offset: -0.01, RTT: 0.01}, {Offset: 0.01, RTT: 0.01}, }, expectedSpikes: []bool{ false, false, false, false, false, false, }, }, } { t.Run(test.name, func(t *testing.T) { var detector spike.Detector for i, sample := range test.samples { isSpike := detector.IsSpike(sample) assert.Equal(t, test.expectedSpikes[i], isSpike, "unexpected spike: %v (position %d)", test.expectedSpikes[i], i) } }) } } ================================================ FILE: internal/pkg/ntp/ntp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ntp provides a time sync client via SNTP protocol. package ntp import ( "bytes" "context" "fmt" "math/bits" "net" "os" "slices" "strings" "sync" "time" "github.com/beevik/ntp" "github.com/u-root/u-root/pkg/rtc" "go.uber.org/zap" "go.uber.org/zap/zapcore" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/ntp/internal/spike" "github.com/siderolabs/talos/internal/pkg/timex" ) // Syncer performs time sync via NTP on schedule. type Syncer struct { logger *zap.Logger timeServersMu sync.Mutex timeServers []string lastSyncServer string timeSyncNotified bool timeSynced chan struct{} restartSyncCh chan struct{} epochChangeCh chan struct{} firstSync bool spikeDetector spike.Detector MinPoll, MaxPoll, RetryPoll time.Duration // these functions are overridden in tests for mocking support CurrentTime CurrentTimeFunc NTPQuery QueryFunc AdjustTime AdjustTimeFunc DisableRTC bool } // Measurement is a struct containing correction data based on a time request. type Measurement struct { ClockOffset time.Duration Leap ntp.LeapIndicator Spike bool } // NewSyncer creates new Syncer with default configuration. func NewSyncer(logger *zap.Logger, timeServers []string) *Syncer { syncer := &Syncer{ logger: logger, timeServers: slices.Clone(timeServers), timeSynced: make(chan struct{}), restartSyncCh: make(chan struct{}, 1), epochChangeCh: make(chan struct{}, 1), firstSync: true, spikeDetector: spike.Detector{}, MinPoll: MinAllowablePoll, MaxPoll: MaxAllowablePoll, RetryPoll: RetryPoll, CurrentTime: time.Now, NTPQuery: ntp.Query, AdjustTime: timex.Adjtimex, } return syncer } // Synced returns a channel which is closed when time is in sync. func (syncer *Syncer) Synced() <-chan struct{} { return syncer.timeSynced } // EpochChange returns a channel which receives a value each time jumps more than EpochLimit. func (syncer *Syncer) EpochChange() <-chan struct{} { return syncer.epochChangeCh } func (syncer *Syncer) getTimeServers() []string { syncer.timeServersMu.Lock() defer syncer.timeServersMu.Unlock() return syncer.timeServers } func (syncer *Syncer) getLastSyncServer() string { syncer.timeServersMu.Lock() defer syncer.timeServersMu.Unlock() return syncer.lastSyncServer } func (syncer *Syncer) setLastSyncServer(lastSyncServer string) { syncer.timeServersMu.Lock() defer syncer.timeServersMu.Unlock() syncer.lastSyncServer = lastSyncServer } // SetTimeServers sets the list of time servers to use. func (syncer *Syncer) SetTimeServers(timeServers []string) { syncer.timeServersMu.Lock() defer syncer.timeServersMu.Unlock() if slices.Equal(timeServers, syncer.timeServers) { return } syncer.timeServers = slices.Clone(timeServers) syncer.lastSyncServer = "" syncer.restartSync() } func (syncer *Syncer) restartSync() { select { case syncer.restartSyncCh <- struct{}{}: default: } } func absDuration(d time.Duration) time.Duration { if d < 0 { return -d } return d } func (syncer *Syncer) isSpike(resp *ntp.Response) bool { return syncer.spikeDetector.IsSpike(spike.SampleFromNTPResponse(resp)) } // Run runs the sync process. // // Run is usually run in a goroutine. // When context is canceled, sync process aborts. // //nolint:gocyclo,cyclop func (syncer *Syncer) Run(ctx context.Context) { var ( rtcClock *rtc.RTC err error ) if !syncer.DisableRTC { rtcClock, err = rtc.OpenRTC() if err != nil { syncer.logger.Error("failure opening RTC, ignored", zap.Error(err)) } else { defer rtcClock.Close() //nolint:errcheck } } pollInterval := time.Duration(0) for { lastSyncServer, resp, err := syncer.query(ctx) if err != nil { return } spike := false if resp != nil { spike = resp.Spike } switch { case resp == nil: // if no response was ever received, consider doing short sleep to retry sooner as it's not Kiss-o-Death response pollInterval = syncer.RetryPoll case pollInterval == 0: // first sync pollInterval = syncer.MinPoll case !spike && absDuration(resp.ClockOffset) > ExpectedAccuracy: // huge offset, retry sync with minimum interval pollInterval = syncer.MinPoll case absDuration(resp.ClockOffset) < ExpectedAccuracy*25/100: // *0.25 // clock offset is within 25% of expected accuracy, increase poll interval if pollInterval < syncer.MaxPoll { pollInterval *= 2 } case spike || absDuration(resp.ClockOffset) > ExpectedAccuracy*75/100: // *0.75 // spike was detected or clock offset is too large, decrease poll interval if pollInterval > syncer.MinPoll { pollInterval /= 2 } } if resp != nil && pollInterval < syncer.MinPoll { // set poll interval to at least min poll if there was any response pollInterval = syncer.MinPoll } syncer.logger.Debug("sample stats", zap.Duration("jitter", time.Duration(syncer.spikeDetector.Jitter()*float64(time.Second))), zap.Duration("poll_interval", pollInterval), zap.Bool("spike", spike), zap.Bool("resp_exists", resp != nil), ) if resp != nil && !spike { err = syncer.adjustTime(resp.ClockOffset, resp.Leap, lastSyncServer, pollInterval, rtcClock) if err == nil { if !syncer.timeSyncNotified { // successful first time sync, notify about it close(syncer.timeSynced) syncer.timeSyncNotified = true } } else { syncer.logger.Error("error adjusting time", zap.Error(err)) } } select { case <-ctx.Done(): return case <-syncer.restartSyncCh: // time servers got changed, restart the loop immediately case <-time.After(pollInterval): } } } func (syncer *Syncer) query(ctx context.Context) (lastSyncServer string, measurement *Measurement, err error) { lastSyncServer = syncer.getLastSyncServer() failedServer := "" if lastSyncServer != "" { measurement, err = syncer.queryServer(lastSyncServer) if err != nil { syncer.logger.Error(fmt.Sprintf("time query error with server %q", lastSyncServer), zap.Error(err)) failedServer = lastSyncServer lastSyncServer = "" err = nil } } if lastSyncServer == "" { var serverList []string serverList, err = syncer.resolveServers(ctx) if err != nil { return lastSyncServer, measurement, err } for _, server := range serverList { if server == failedServer { // skip server which failed in previous sync to avoid sending requests with short interval continue } select { case <-ctx.Done(): return lastSyncServer, measurement, ctx.Err() case <-syncer.restartSyncCh: syncer.restartSync() // re-queue restart for outer loop return lastSyncServer, measurement, nil default: } measurement, err = syncer.queryServer(server) if err != nil { syncer.logger.Error(fmt.Sprintf("time query error with server %q", server), zap.Error(err)) err = nil } else { syncer.setLastSyncServer(server) lastSyncServer = server break } } } return lastSyncServer, measurement, err } // IsPTPDevice checks if a given server string represents a PTP device. func IsPTPDevice(server string) bool { return strings.HasPrefix(server, "/dev/") } func (syncer *Syncer) resolveServers(ctx context.Context) ([]string, error) { var serverList []string for _, server := range syncer.getTimeServers() { if IsPTPDevice(server) { serverList = append(serverList, server) } else { ips, err := syncer.lookupIPAddrWithTimeout(ctx, server, 5*time.Second) if err != nil { syncer.logger.Error(fmt.Sprintf("failed looking up %q, ignored", server), zap.Error(err)) } for _, ip := range ips { serverList = append(serverList, ip.String()) } } select { case <-ctx.Done(): return nil, ctx.Err() default: } } return serverList, nil } func (syncer *Syncer) lookupIPAddrWithTimeout(ctx context.Context, host string, timeout time.Duration) ([]net.IPAddr, error) { ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() return (&net.Resolver{}).LookupIPAddr(ctx, host) } func (syncer *Syncer) queryServer(server string) (*Measurement, error) { if IsPTPDevice(server) { return syncer.queryPTP(server) } return syncer.queryNTP(server) } func (syncer *Syncer) queryPTP(device string) (*Measurement, error) { ts, err := QueryPTPDevice(device) if err != nil { return nil, err } offset := time.Until(time.Unix(ts.Sec, ts.Nsec)) syncer.logger.Debug("PTP clock", zap.Duration("clock_offset", offset), zap.Int64("sec", ts.Sec), zap.Int64("nsec", ts.Nsec), zap.String("device", device), ) meas := &Measurement{ ClockOffset: offset, Leap: 0, Spike: false, } return meas, err } // QueryPTPDevice queries PTP device for current time. func QueryPTPDevice(device string) (unix.Timespec, error) { phc, err := os.Open(device) if err != nil { return unix.Timespec{}, err } defer phc.Close() //nolint:errcheck // From clock_gettime(2): // // Using the appropriate macros, open file descriptors may be converted into clock IDs and passed to clock_gettime(), clock_settime(), and clock_adjtime(2). The // following example shows how to convert a file descriptor into a dynamic clock ID. // // #define CLOCKFD 3 // #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) clockid := int32(3 | (^phc.Fd() << 3)) var ts unix.Timespec err = unix.ClockGettime(clockid, &ts) if err != nil { return unix.Timespec{}, err } return ts, err } func (syncer *Syncer) queryNTP(server string) (*Measurement, error) { resp, err := syncer.NTPQuery(server) if err != nil { return nil, err } syncer.logger.Debug("NTP response", zap.Duration("clock_offset", resp.ClockOffset), zap.Duration("rtt", resp.RTT), zap.Uint8("leap", uint8(resp.Leap)), zap.Uint8("stratum", resp.Stratum), zap.Duration("precision", resp.Precision), zap.Duration("root_delay", resp.RootDelay), zap.Duration("root_dispersion", resp.RootDispersion), zap.Duration("root_distance", resp.RootDistance), ) validationError := resp.Validate() if validationError != nil { return nil, validationError } return &Measurement{ ClockOffset: resp.ClockOffset, Leap: resp.Leap, Spike: syncer.isSpike(resp), }, nil } // log2i returns 0 for v == 0 and v == 1. func log2i(v uint64) int { if v == 0 { return 0 } return 63 - bits.LeadingZeros64(v) } // adjustTime adds an offset to the current time. // //nolint:gocyclo func (syncer *Syncer) adjustTime(offset time.Duration, leapSecond ntp.LeapIndicator, server string, nextPollInterval time.Duration, rtcClock *rtc.RTC) error { var ( buf bytes.Buffer req unix.Timex jump bool ) if offset < -AdjustTimeLimit || offset > AdjustTimeLimit { jump = true fmt.Fprintf(&buf, "adjusting time (jump) by %s via %s", offset, server) req = unix.Timex{ Modes: unix.ADJ_SETOFFSET | unix.ADJ_NANO | unix.ADJ_STATUS | unix.ADJ_MAXERROR | unix.ADJ_ESTERROR, Time: unix.Timeval{ Sec: int64(offset / time.Second), Usec: int64(offset / time.Nanosecond % time.Second), }, Maxerror: 0, Esterror: 0, } // kernel wants tv_usec to be positive if req.Time.Usec < 0 { req.Time.Sec-- req.Time.Usec += int64(time.Second / time.Nanosecond) } } else { fmt.Fprintf(&buf, "adjusting time (slew) by %s via %s", offset, server) pollSeconds := uint64(nextPollInterval / time.Second) log2iPollSeconds := log2i(pollSeconds) req = unix.Timex{ Modes: unix.ADJ_OFFSET | unix.ADJ_NANO | unix.ADJ_STATUS | unix.ADJ_TIMECONST | unix.ADJ_MAXERROR | unix.ADJ_ESTERROR, Offset: int64(offset / time.Nanosecond), Status: unix.STA_PLL, Maxerror: 0, Esterror: 0, Constant: int64(log2iPollSeconds) - 4, } } switch leapSecond { //nolint:exhaustive case ntp.LeapAddSecond: req.Status |= unix.STA_INS case ntp.LeapDelSecond: req.Status |= unix.STA_DEL } logLevel := zapcore.DebugLevel if jump { logLevel = zapcore.InfoLevel } state, err := syncer.AdjustTime(&req) fmt.Fprintf(&buf, ", state %s, status %s", state, timex.Status(req.Status)) if err != nil { logLevel = zapcore.WarnLevel fmt.Fprintf(&buf, ", error was %s", err) } if syncer.firstSync && logLevel == zapcore.DebugLevel { // promote first sync to info level syncer.firstSync = false logLevel = zapcore.InfoLevel } if ce := syncer.logger.Check(logLevel, buf.String()); ce != nil { ce.Write() } syncer.logger.Debug("adjtime state", zap.Int64("constant", req.Constant), zap.Duration("offset", time.Duration(req.Offset)), zap.Int64("freq_offset", req.Freq), zap.Int64("freq_offset_ppm", req.Freq/65536), ) if err == nil { if offset < -EpochLimit || offset > EpochLimit { // notify about epoch change select { case syncer.epochChangeCh <- struct{}{}: default: } } if jump { if rtcClock != nil { if rtcErr := rtcClock.Set(time.Now().Add(offset)); rtcErr != nil { syncer.logger.Error("error syncing RTC", zap.Error(rtcErr)) } else { syncer.logger.Info("synchronized RTC with system clock") } } } } return err } ================================================ FILE: internal/pkg/ntp/ntp_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package ntp_test import ( "context" "errors" "fmt" "sync" "testing" "time" beevikntp "github.com/beevik/ntp" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/suite" "go.uber.org/zap" "go.uber.org/zap/zaptest" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/ntp" "github.com/siderolabs/talos/internal/pkg/timex" "github.com/siderolabs/talos/pkg/machinery/constants" ) type NTPSuite struct { suite.Suite clockLock sync.Mutex systemClock time.Time clockAdjustments []time.Duration failingServer int spikyServer int kissOfDeathServer int } func TestNTPSuite(t *testing.T) { suite.Run(t, new(NTPSuite)) } func (suite *NTPSuite) SetupTest() { suite.systemClock = time.Now().UTC() suite.clockAdjustments = nil suite.failingServer = 0 suite.spikyServer = 0 suite.kissOfDeathServer = 0 } func (suite *NTPSuite) getSystemClock() time.Time { suite.clockLock.Lock() defer suite.clockLock.Unlock() return suite.systemClock } func (suite *NTPSuite) adjustSystemClock(val *unix.Timex) (status timex.State, err error) { suite.clockLock.Lock() defer suite.clockLock.Unlock() if val.Modes&unix.ADJ_OFFSET == unix.ADJ_OFFSET { suite.T().Logf("adjustment by %s", time.Duration(val.Offset)*time.Nanosecond) suite.clockAdjustments = append(suite.clockAdjustments, time.Duration(val.Offset)*time.Nanosecond) } else { suite.T().Logf("set clock by %s", time.Duration(val.Time.Sec)*time.Second+time.Duration(val.Time.Usec)*time.Nanosecond) suite.systemClock = suite.systemClock.Add(time.Duration(val.Time.Sec)*time.Second + time.Duration(val.Time.Usec)*time.Nanosecond) } return status, err } //nolint:gocyclo func (suite *NTPSuite) fakeQuery(host string) (resp *beevikntp.Response, err error) { switch host { case "127.0.0.1": // error return nil, errors.New("no response") case "127.0.0.2": // invalid response resp = &beevikntp.Response{} suite.Require().Error(resp.Validate()) return resp, nil case "127.0.0.3": // adjust +1ms resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: time.Millisecond, RTT: time.Millisecond / 2, } suite.Require().NoError(resp.Validate()) return resp, nil case "127.0.0.4": // adjust +2ms resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: 2 * time.Millisecond, RTT: time.Millisecond / 2, } suite.Require().NoError(resp.Validate()) return resp, nil case "127.0.0.5": // adjust +2*epoch resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: ntp.EpochLimit * 2, RTT: time.Millisecond, } suite.Require().NoError(resp.Validate()) return resp, nil case "127.0.0.6": // adjust 1ms/fail alternating suite.failingServer++ if suite.failingServer%2 == 0 { return nil, errors.New("failed this time") } resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: time.Millisecond, RTT: time.Millisecond / 2, } suite.Require().NoError(resp.Validate()) return resp, nil case "127.0.0.7": // server with spikes suite.spikyServer++ if suite.spikyServer%5 == 4 { resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: time.Second, RTT: 2 * time.Second, } } else { resp = &beevikntp.Response{ Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: time.Millisecond, RTT: time.Millisecond / 2, } } suite.Require().NoError(resp.Validate()) return resp, nil case "127.0.0.8": // kiss of death alternating suite.kissOfDeathServer++ if suite.kissOfDeathServer%2 == 1 { return &beevikntp.Response{ // kiss of death Stratum: 0, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: 2 * time.Millisecond, RTT: time.Millisecond / 2, }, nil } return &beevikntp.Response{ // normal response Stratum: 1, Time: suite.systemClock, ReferenceTime: suite.systemClock, ClockOffset: time.Millisecond, RTT: time.Millisecond / 2, }, nil default: return nil, fmt.Errorf("unknown host %q", host) } } func (suite *NTPSuite) TestSync() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{constants.DefaultNTPServer}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.DisableRTC = true ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } cancel() wg.Wait() } func (suite *NTPSuite) TestSyncContinuous() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.3"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true syncer.MinPoll = time.Second syncer.MaxPoll = time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { suite.clockLock.Lock() defer suite.clockLock.Unlock() if len(suite.clockAdjustments) < 3 { return retry.ExpectedErrorf("not enough syncs") } return nil }), ) cancel() wg.Wait() } //nolint:dupl func (suite *NTPSuite) TestSyncKissOfDeath() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.8"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true syncer.MinPoll = time.Second syncer.MaxPoll = time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { suite.clockLock.Lock() defer suite.clockLock.Unlock() if len(suite.clockAdjustments) < 2 { return retry.ExpectedErrorf("not enough syncs") } for _, adj := range suite.clockAdjustments { // kiss of death syncs should be ignored suite.Assert().Equal(time.Millisecond, adj) } return nil }), ) cancel() wg.Wait() } //nolint:dupl func (suite *NTPSuite) TestSyncWithSpikes() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.7"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true syncer.MinPoll = time.Second syncer.MaxPoll = time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } suite.Assert().NoError( retry.Constant(12*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { suite.clockLock.Lock() defer suite.clockLock.Unlock() if len(suite.clockAdjustments) < 6 { return retry.ExpectedErrorf("not enough syncs") } for _, adj := range suite.clockAdjustments { // 1s spike should be filtered out suite.Assert().Equal(time.Millisecond, adj) } return nil }), ) cancel() wg.Wait() } func (suite *NTPSuite) TestSyncChangeTimeservers() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.1"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.DisableRTC = true ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): suite.Assert().Fail("unexpected sync") case <-time.After(3 * time.Second): } syncer.SetTimeServers([]string{constants.DefaultNTPServer}) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } cancel() wg.Wait() } func (suite *NTPSuite) TestSyncIterateTimeservers() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.1", "127.0.0.2", "127.0.0.3", "127.0.0.4"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true syncer.MinPoll = time.Second syncer.MaxPoll = time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { suite.clockLock.Lock() defer suite.clockLock.Unlock() if len(suite.clockAdjustments) < 3 { return retry.ExpectedErrorf("not enough syncs") } return nil }), ) cancel() wg.Wait() // should always sync with 127.0.0.3, and never switch to 127.0.0.4 for i := range 3 { suite.Assert().Equal(time.Millisecond, suite.clockAdjustments[i]) } } func (suite *NTPSuite) TestSyncEpochChange() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.5"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } select { case <-syncer.EpochChange(): case <-time.After(10 * time.Second): suite.Assert().Fail("epoch change timeout") } cancel() wg.Wait() suite.Assert().Greater(-time.Since(suite.systemClock), ntp.EpochLimit) } func (suite *NTPSuite) TestSyncSwitchTimeservers() { syncer := ntp.NewSyncer(zaptest.NewLogger(suite.T()).With(zap.String("controller", "ntp")), []string{"127.0.0.6", "127.0.0.4"}) syncer.AdjustTime = suite.adjustSystemClock syncer.CurrentTime = suite.getSystemClock syncer.NTPQuery = suite.fakeQuery syncer.DisableRTC = true syncer.MinPoll = time.Second syncer.MaxPoll = time.Second ctx, cancel := context.WithCancel(context.Background()) defer cancel() var wg sync.WaitGroup wg.Go(func() { syncer.Run(ctx) }) select { case <-syncer.Synced(): case <-time.After(10 * time.Second): suite.Assert().Fail("time sync timeout") } suite.Assert().NoError( retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { suite.clockLock.Lock() defer suite.clockLock.Unlock() if len(suite.clockAdjustments) < 3 { return retry.ExpectedErrorf("not enough syncs") } return nil }), ) cancel() wg.Wait() // should start sync with 127.0.0.6, then switch to 127.0.0.4 suite.Assert().Equal(time.Millisecond, suite.clockAdjustments[0]) for i := 1; i < 3; i++ { suite.Assert().Equal(2*time.Millisecond, suite.clockAdjustments[i]) } } ================================================ FILE: internal/pkg/partition/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package partition // Type in partition table. type Type = string // GPT partition types. // // TODO: should be moved into the blockdevice library. const ( EFISystemPartition Type = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" BIOSBootPartition Type = "21686148-6449-6E6F-744E-656564454649" LinuxFilesystemData Type = "0FC63DAF-8483-4772-8E79-3D69D8477DE4" LinkSwap Type = "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F" ) // FileSystemType is used to format partitions. type FileSystemType = string // Filesystem types. const ( FilesystemTypeNone FileSystemType = "none" FilesystemTypeZeroes FileSystemType = "zeroes" FilesystemTypeXFS FileSystemType = "xfs" FilesystemTypeVFAT FileSystemType = "vfat" FileSystemTypeExt4 FileSystemType = "ext4" ) ================================================ FILE: internal/pkg/partition/format.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package partition provides common utils for system partition format. package partition import ( "context" "fmt" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/makefs" ) // FormatOptions contains format parameters. type FormatOptions struct { Label string SourceDirectory string FileSystemType FileSystemType Force bool UnsupportedFSOption bool Reproducible bool } // FormatOption to control options. type FormatOption func(*FormatOptions) // WithSourceDirectory sets the source directory for populating the filesystem. func WithSourceDirectory(dir string) FormatOption { return func(o *FormatOptions) { o.SourceDirectory = dir } } // WithUnsupportedFSOption sets the unsupported filesystem option. func WithUnsupportedFSOption() FormatOption { return func(o *FormatOptions) { o.UnsupportedFSOption = true } } // WithForce sets the force option. func WithForce() FormatOption { return func(o *FormatOptions) { o.Force = true } } // WithLabel sets the label for the filesystem. func WithLabel(label string) FormatOption { return func(o *FormatOptions) { o.Label = label } } // WithFileSystemType sets the filesystem type. func WithFileSystemType(fsType FileSystemType) FormatOption { return func(o *FormatOptions) { o.FileSystemType = fsType } } // WithReproducible sets the reproducible option. func WithReproducible() FormatOption { return func(o *FormatOptions) { o.Reproducible = true } } // NewFormatOptions creates a new format options. func NewFormatOptions(opts ...FormatOption) *FormatOptions { o := &FormatOptions{} for _, opt := range opts { opt(o) } return systemPartitionsFormatOptions(*o) } // Format zeroes the device and formats it using filesystem type provided. func Format(ctx context.Context, devname string, t *FormatOptions, talosVersion string, printf func(string, ...any)) error { opts := []makefs.Option{ makefs.WithForce(t.Force), makefs.WithLabel(t.Label), makefs.WithPrintf(printf), } if t.UnsupportedFSOption { opts = append(opts, makefs.WithUnsupportedFSOption(t.UnsupportedFSOption)) } if t.SourceDirectory != "" { opts = append(opts, makefs.WithSourceDirectory(t.SourceDirectory)) } if t.Reproducible { opts = append(opts, makefs.WithReproducible(true)) } printf("formatting the partition %q as %q with label %q\n", devname, t.FileSystemType, t.Label) switch t.FileSystemType { case FilesystemTypeNone: return nil case FilesystemTypeZeroes: return zeroPartition(devname) case FilesystemTypeVFAT: return makefs.VFAT(ctx, devname, opts...) case FilesystemTypeXFS: opts = append(opts, makefs.WithConfigFile(quirks.New(talosVersion).XFSMkfsConfig())) return makefs.XFS(ctx, devname, opts...) case FileSystemTypeExt4: return makefs.Ext4(ctx, devname, opts...) default: return fmt.Errorf("unsupported filesystem type: %q", t.FileSystemType) } } // zeroPartition fills the partition with zeroes. func zeroPartition(devname string) (err error) { part, err := block.NewFromPath(devname, block.OpenForWrite()) if err != nil { return err } defer part.Close() //nolint:errcheck return part.FastWipe() } // systemPartitionsFormatOptions returns format options for system partitions. func systemPartitionsFormatOptions(opts FormatOptions) *FormatOptions { switch opts.Label { case constants.EFIPartitionLabel: return &FormatOptions{ Label: constants.EFIPartitionLabel, SourceDirectory: opts.SourceDirectory, Reproducible: opts.Reproducible, FileSystemType: FilesystemTypeVFAT, Force: true, } case constants.BIOSGrubPartitionLabel: return &FormatOptions{ Label: constants.BIOSGrubPartitionLabel, FileSystemType: FilesystemTypeZeroes, Force: true, } case constants.BootPartitionLabel: return &FormatOptions{ Label: constants.BootPartitionLabel, SourceDirectory: opts.SourceDirectory, Reproducible: opts.Reproducible, FileSystemType: FilesystemTypeXFS, Force: true, } case constants.MetaPartitionLabel: return &FormatOptions{ Label: constants.MetaPartitionLabel, FileSystemType: FilesystemTypeZeroes, Force: true, } case constants.StatePartitionLabel: return &FormatOptions{ Label: constants.StatePartitionLabel, FileSystemType: FilesystemTypeNone, } case constants.EphemeralPartitionLabel: return &FormatOptions{ Label: constants.EphemeralPartitionLabel, FileSystemType: FilesystemTypeNone, } case constants.ImageCachePartitionLabel: return &FormatOptions{ Label: constants.ImageCachePartitionLabel, SourceDirectory: opts.SourceDirectory, FileSystemType: FileSystemTypeExt4, Reproducible: opts.Reproducible, } default: return nil } } ================================================ FILE: internal/pkg/partition/helpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package partition import ( "fmt" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/go-blockdevice/v2/block" ) // WipeWithSignatures wipes the given block device by its signatures (if available) // and falls back to fast wipe otherwise. // // The function assumes that the caller locked properly the block device (or the parent // device in case of partitions) before calling it. // // If non-nil log function is passed, it will be used to log the wipe process. func WipeWithSignatures(bd *block.Device, deviceName string, log func(string, ...any)) error { info, err := blkid.Probe(bd.File(), blkid.WithSkipLocking(true)) if err == nil && info != nil && len(info.SignatureRanges) > 0 { // probe successful, wipe by signatures if err = bd.FastWipe(xslices.Map(info.SignatureRanges, func(r blkid.SignatureRange) block.Range { return block.Range(r) })...); err != nil { return fmt.Errorf("failed to wipe block device %q: %v", deviceName, err) } if log != nil { log("block device %q wiped by ranges: %s", deviceName, strings.Join( xslices.Map(info.SignatureRanges, func(r blkid.SignatureRange) string { return fmt.Sprintf("%d-%d", r.Offset, r.Offset+r.Size) }, ), ", ", ), ) } } // [TODO]: wipe the first/last 1MiB after wiping by signatures to cover somewhat unknown edge cases // What has been observed so far is that wiping VFAT signature still makes `mkfs.xfs` believe there is // a VFAT filesystem on the partition, refusing to create XFS over it without `-f` flag. // // probe failed or no signatures found, fast wipe if err = bd.FastWipe(); err != nil { return fmt.Errorf("failed to wipe block device %q: %v", deviceName, err) } if log != nil { log("block device %q wiped with fast method", deviceName) } return nil } ================================================ FILE: internal/pkg/partition/partition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package partition provides common utils for system partition format. package partition import ( "fmt" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Options contains the options for creating a partition. type Options struct { FormatOptions PartitionLabel string PartitionType Type Size uint64 PartitionOpts []gpt.PartitionOption } // NewPartitionOptions returns a new PartitionOptions. // //nolint:gocyclo func NewPartitionOptions(uki bool, quirk quirks.Quirks, formatOpts ...FormatOption) Options { formatOptions := NewFormatOptions(formatOpts...) if formatOptions == nil { panic("empty format options, expected at least label") } switch formatOptions.Label { case constants.EFIPartitionLabel: partitionOptions := Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: EFISystemPartition, Size: quirk.PartitionSizes().GrubEFISize(), } if uki { partitionOptions.Size = quirk.PartitionSizes().UKIEFISize() } return partitionOptions case constants.BIOSGrubPartitionLabel: if uki { panic("BIOS partition is not supported with UKI") } return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: BIOSBootPartition, Size: quirk.PartitionSizes().GrubBIOSSize(), PartitionOpts: []gpt.PartitionOption{gpt.WithLegacyBIOSBootableAttribute(true)}, } case constants.BootPartitionLabel: if uki { panic("BOOT partition is not supported with UKI") } return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: LinuxFilesystemData, Size: quirk.PartitionSizes().GrubBootSize(), } case constants.MetaPartitionLabel: return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: LinuxFilesystemData, Size: quirk.PartitionSizes().METASize(), } case constants.StatePartitionLabel: return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: LinuxFilesystemData, Size: quirk.PartitionSizes().StateSize(), } case constants.EphemeralPartitionLabel: return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: LinuxFilesystemData, Size: 0, } case constants.ImageCachePartitionLabel: return Options{ FormatOptions: *formatOptions, PartitionLabel: formatOptions.Label, PartitionType: LinuxFilesystemData, Size: 0, } default: panic(fmt.Sprintf("unknown partition label %q", formatOptions.Label)) } } ================================================ FILE: internal/pkg/partition/wipe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package partition import ( "context" "fmt" "time" "github.com/siderolabs/go-blockdevice/v2/block" "github.com/siderolabs/go-blockdevice/v2/partitioning/gpt" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumeWipeTarget is a target for wiping a volume. type VolumeWipeTarget struct { label string parentDevName, devName string partitionIndex int // partitionIndex is 1-based, but decrement before using } // VolumeWipeTargetFromVolumeStatus creates a new VolumeWipeTarget from a VolumeStatus. func VolumeWipeTargetFromVolumeStatus(vs *blockres.VolumeStatus) *VolumeWipeTarget { parentDevName := vs.TypedSpec().ParentLocation if parentDevName == "" { parentDevName = vs.TypedSpec().Location } return &VolumeWipeTarget{ label: vs.Metadata().ID(), parentDevName: parentDevName, devName: vs.TypedSpec().Location, partitionIndex: vs.TypedSpec().PartitionIndex, } } // VolumeWipeTargetFromDiscoveredVolume creates a new VolumeWipeTarget from a DiscoveredVolume. func VolumeWipeTargetFromDiscoveredVolume(dv *blockres.DiscoveredVolume) *VolumeWipeTarget { parentDevName := dv.TypedSpec().ParentDevPath if parentDevName == "" { parentDevName = dv.TypedSpec().DevPath } return &VolumeWipeTarget{ label: dv.TypedSpec().PartitionLabel, parentDevName: parentDevName, devName: dv.TypedSpec().DevPath, partitionIndex: int(dv.TypedSpec().PartitionIndex), } } // GetLabel implements runtime.PartitionTarget. func (v *VolumeWipeTarget) GetLabel() string { return v.label } // String implements runtime.PartitionTarget. func (v *VolumeWipeTarget) String() string { return fmt.Sprintf("%s:%s", v.label, v.devName) } // Wipe implements runtime.PartitionTarget. // Asides from wiping the device, Wipe() also drops the partition. func (v *VolumeWipeTarget) Wipe(ctx context.Context, log func(string, ...any)) error { parentBd, err := block.NewFromPath(v.parentDevName, block.OpenForWrite()) if err != nil { return fmt.Errorf("error opening block device %q: %w", v.parentDevName, err) } defer parentBd.Close() //nolint:errcheck if err = parentBd.RetryLockWithTimeout(ctx, true, time.Minute); err != nil { return fmt.Errorf("error locking block device %q: %w", v.parentDevName, err) } defer parentBd.Unlock() //nolint:errcheck if err := v.wipeWithParentLocked(log); err != nil { return fmt.Errorf("error wiping device %q: %w", v.devName, err) } if parentBd == nil || v.partitionIndex == 0 { return fmt.Errorf("missing parent block device or partition index") } if err := v.dropWithParentLocked(parentBd, log); err != nil { return fmt.Errorf("error dropping partition: %w", err) } return nil } func (v *VolumeWipeTarget) wipeWithParentLocked(log func(string, ...any)) error { bd, err := block.NewFromPath(v.devName, block.OpenForWrite()) if err != nil { return fmt.Errorf("error opening block device %q: %w", v.devName, err) } defer bd.Close() //nolint:errcheck log("wiping volume %q (%s)", v.GetLabel(), v.devName) return WipeWithSignatures(bd, v.devName, log) } func (v *VolumeWipeTarget) dropWithParentLocked(parentBd *block.Device, log func(string, ...any)) error { log("dropping partition %d from device %q", v.partitionIndex, v.parentDevName) gptdev, err := gpt.DeviceFromBlockDevice(parentBd) if err != nil { return fmt.Errorf("failed to get GPT device: %w", err) } pt, err := gpt.Read(gptdev) if err != nil { return fmt.Errorf("failed to read GPT table: %w", err) } if err = pt.DeletePartition(v.partitionIndex - 1); err != nil { return fmt.Errorf("failed to delete partition: %w", err) } if err = pt.Write(); err != nil { return fmt.Errorf("failed to write GPT table: %w", err) } return nil } ================================================ FILE: internal/pkg/pcap/pcap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pcap implements writing packet data to pcap files. package pcap import ( "encoding/binary" "fmt" "io" "time" "github.com/gopacket/gopacket" ) // Writer wraps an underlying io.Writer to write packet data in PCAP // format. See http://wiki.wireshark.org/Development/LibpcapFileFormat // for information on the file format. // // For those that care, we currently write v2.4 files with nanosecond // or microsecond timestamp resolution and little-endian encoding. type Writer struct { w io.Writer buf [16]byte } const ( magicNanoseconds = 0xA1B23C4D versionMajor = 2 versionMinor = 4 ) // LinkType is the link type for the pcap file. type LinkType uint32 // LinkType values. const ( LinkTypeEthernet LinkType = 1 LinkTypeRaw LinkType = 101 ) // NewWriter returns a new writer object. // // If this is a new empty writer (as opposed to // an append), you must call WriteFileHeader before WritePacket. Packet // timestamps are written with nanosecond precision. func NewWriter(w io.Writer) *Writer { return &Writer{w: w} } // WriteFileHeader writes a file header out to the writer. // This must be called exactly once per output. func (w *Writer) WriteFileHeader(snaplen uint32, linktype LinkType) error { var buf [24]byte binary.LittleEndian.PutUint32(buf[0:4], magicNanoseconds) binary.LittleEndian.PutUint16(buf[4:6], versionMajor) binary.LittleEndian.PutUint16(buf[6:8], versionMinor) // bytes 8:12 stay 0 (timezone = UTC) // bytes 12:16 stay 0 (sigfigs is always set to zero, according to // http://wiki.wireshark.org/Development/LibpcapFileFormat binary.LittleEndian.PutUint32(buf[16:20], snaplen) binary.LittleEndian.PutUint32(buf[20:24], uint32(linktype)) _, err := w.w.Write(buf[:]) return err } func (w *Writer) writePacketHeader(ci gopacket.CaptureInfo) error { t := ci.Timestamp if t.IsZero() { t = time.Now() } secs := t.Unix() binary.LittleEndian.PutUint32(w.buf[0:4], uint32(secs)) usecs := t.Nanosecond() binary.LittleEndian.PutUint32(w.buf[4:8], uint32(usecs)) binary.LittleEndian.PutUint32(w.buf[8:12], uint32(ci.CaptureLength)) binary.LittleEndian.PutUint32(w.buf[12:16], uint32(ci.Length)) _, err := w.w.Write(w.buf[:]) return err } // WritePacket writes the given packet data out to the file. func (w *Writer) WritePacket(ci gopacket.CaptureInfo, data []byte) error { if ci.CaptureLength != len(data) { return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data)) } if ci.CaptureLength > ci.Length { return fmt.Errorf("invalid capture info %+v: capture length > length", ci) } if err := w.writePacketHeader(ci); err != nil { return fmt.Errorf("error writing packet header: %v", err) } _, err := w.w.Write(data) return err } ================================================ FILE: internal/pkg/pci/pci.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pci provides methods to access PCI-related data. package pci import ( "github.com/siderolabs/go-pcidb/pkg/pcidb" ) // Device describes PCI device. type Device struct { VendorID uint16 ProductID uint16 Vendor string Product string } // LookupDB looks up device info in the PCI database. func (d *Device) LookupDB() { d.Vendor, _ = pcidb.LookupVendor(d.VendorID) d.Product, _ = pcidb.LookupProduct(d.VendorID, d.ProductID) } ================================================ FILE: internal/pkg/pci/sysfs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pci import ( "bytes" "errors" "fmt" "io/fs" "os" "strconv" ) const sysfsPath = "/sys/bus/pci/devices/%s/%s" func readID(busPath, name string) (uint16, error) { contents, err := os.ReadFile(fmt.Sprintf(sysfsPath, busPath, name)) if err != nil { return 0, err } v, err := strconv.ParseUint(string(bytes.TrimSpace(contents)), 0, 16) return uint16(v), err } // SysfsDeviceInfo looks up vendor and product ID from sysfs. func SysfsDeviceInfo(busPath string) (*Device, error) { var ( d Device err error ) d.ProductID, err = readID(busPath, "device") if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, err } d.VendorID, err = readID(busPath, "vendor") if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, err } return &d, err } ================================================ FILE: internal/pkg/rng/pool.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package rng import ( "bytes" "fmt" "os" "strconv" "golang.org/x/sys/unix" ) // GetPoolSize returns kernel random pool size. func GetPoolSize() (int, error) { contents, err := os.ReadFile("/proc/sys/kernel/random/poolsize") if err != nil { return 0, err } return strconv.Atoi(string(bytes.TrimSpace(contents))) } // WriteEntropy writes entropy data to the pool. func WriteEntropy(data []byte) error { fd, err := os.OpenFile("/dev/urandom", os.O_WRONLY|unix.O_CLOEXEC|unix.O_NOCTTY, 0) if err != nil { return err } defer fd.Close() //nolint:errcheck _, err = fd.Write(data) if err != nil { return fmt.Errorf("error writing entropy: %w", err) } return fd.Close() } ================================================ FILE: internal/pkg/rng/rng.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package rng handles interaction with kernel random number generator. package rng ================================================ FILE: internal/pkg/rng/tpm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package rng import ( "errors" "fmt" "io/fs" "log" "time" "github.com/google/go-tpm/tpm2" "github.com/siderolabs/talos/internal/pkg/tpm" ) // TPMSeed seeds the random entropy pool from the TPM. // //nolint:gocyclo func TPMSeed() error { t, err := tpm.Open() if err != nil { // if the TPM is not available we can skip seeding random pool if errors.Is(err, fs.ErrNotExist) { log.Printf("TPM device is not available") return nil } return fmt.Errorf("error opening TPM device: %w", err) } defer t.Close() //nolint:errcheck // now we need to check if the TPM is a 2.0 device // we can do this by checking the manufacturer, // if it fails, we can skip trying to seed the pool caps, err := tpm2.GetCapability{ Capability: tpm2.TPMCapTPMProperties, Property: uint32(tpm2.TPMPTManufacturer), PropertyCount: 1, }.Execute(t) if err != nil { log.Printf("TPM device is not a TPM 2.0, skipping seeding entropy pool from TPM") return nil } props, err := caps.CapabilityData.Data.TPMProperties() if err != nil { return fmt.Errorf("error getting properties: %w", err) } log.Printf("TPM manufacturer ID: %08x", props.TPMProperty[0].Value) poolSize, err := GetPoolSize() if err != nil { return fmt.Errorf("error getting pool size: %w", err) } remaining := poolSize start := time.Now() for remaining > 0 { chunk := min(remaining, 32) // default to small chunk (size of AES key) cmd := tpm2.GetRandom{ BytesRequested: uint16(chunk), } resp, err := cmd.Execute(t) if err != nil { return fmt.Errorf("error getting random data from the TPM: %w", err) } if len(resp.RandomBytes.Buffer) == 0 { return fmt.Errorf("received zero random bytes from the TPM: %w", err) } if err = WriteEntropy(resp.RandomBytes.Buffer); err != nil { return fmt.Errorf("error writing random pool entropy: %w", err) } remaining -= len(resp.RandomBytes.Buffer) } log.Printf("seeded random pool with %d bytes from the TPM in %s", poolSize, time.Since(start)) return nil } ================================================ FILE: internal/pkg/rootfs/rootfs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package rootfs_test import ( "debug/buildinfo" "os" "runtime" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestPkgsGoVersionMatchesTalos(t *testing.T) { t.Parallel() if hostname, _ := os.Hostname(); hostname != "buildkitsandbox" { //nolint:errcheck t.Skipf("skipping test; only run on buildkitsandbox, got %s", hostname) } const sampleBinaryPath = "/usr/bin/containerd" info, err := buildinfo.ReadFile(sampleBinaryPath) if err != nil { t.Fatalf("failed to read build info from %s: %v", sampleBinaryPath, err) } binaryGoVersion := info.GoVersion runtimeGoVersion := runtime.Version() assert.Equal(t, runtimeGoVersion, binaryGoVersion) assert.Equal(t, runtimeGoVersion, constants.GoVersion) } ================================================ FILE: internal/pkg/secureboot/database/database.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package database generates SecureBoot auto-enrollment database. package database import ( "crypto/sha256" "crypto/x509" "embed" "path/filepath" "sync" "github.com/foxboron/go-uefi/efi/signature" "github.com/foxboron/go-uefi/efi/util" "github.com/foxboron/go-uefi/efivar" "github.com/google/uuid" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Entry is a UEFI database entry. type Entry struct { Name string Contents []byte } const ( microsoftSignatureOwnerGUID = "77fa9abd-0359-4d32-bd60-28f4e78f784b" ) // Well-known UEFI DB certificates (DER data). // //go:embed certs/db/*.der var wellKnownDB embed.FS // Well-known UEFI KEK certificates (PEM data). // //go:embed certs/kek/*.der var wellKnownKEK embed.FS func loadWellKnownCertificates(fs embed.FS, path string) ([]*x509.Certificate, error) { certs := []*x509.Certificate{} files, err := fs.ReadDir(path) if err != nil { return nil, err } for _, file := range files { data, err := fs.ReadFile(filepath.Join(path, file.Name())) if err != nil { return nil, err } cert, err := x509.ParseCertificate(data) if err != nil { return nil, err } certs = append(certs, cert) } return certs, nil } var wellKnownDBCertificates = sync.OnceValue(func() []*x509.Certificate { certs, err := loadWellKnownCertificates(wellKnownDB, "certs/db") if err != nil { panic(err) } return certs }) var wellKnownKEKCertificates = sync.OnceValue(func() []*x509.Certificate { certs, err := loadWellKnownCertificates(wellKnownKEK, "certs/kek") if err != nil { panic(err) } return certs }) // Options for Generate. type Options struct { IncludeWellKnownCertificates bool } // Option is a functional option for Generate. type Option func(*Options) // IncludeWellKnownCertificates is an option to include well-known certificates. func IncludeWellKnownCertificates(v bool) Option { return func(o *Options) { o.IncludeWellKnownCertificates = v } } // Generate generates a UEFI database to enroll the signing certificate. // // ref: https://blog.hansenpartnership.com/the-meaning-of-all-the-uefi-keys/ // //nolint:gocyclo func Generate(enrolledCertificate []byte, signer pesign.CertificateSigner, opts ...Option) ([]Entry, error) { var options Options for _, opt := range opts { opt(&options) } // derive UUID from enrolled certificate uuid := uuid.NewHash(sha256.New(), uuid.NameSpaceX500, enrolledCertificate, 4) efiGUID := util.StringToGUID(uuid.String()) // Create PK ESL pk := signature.NewSignatureDatabase() if err := pk.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil { return nil, err } _, signedPK, err := signature.SignEFIVariable(efivar.PK, pk, signer.Signer(), signer.Certificate()) if err != nil { return nil, err } // Create KEK ESL kek := signature.NewSignatureDatabase() if err := kek.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil { return nil, err } if options.IncludeWellKnownCertificates { owner := util.StringToGUID(microsoftSignatureOwnerGUID) for _, cert := range wellKnownKEKCertificates() { if err := kek.Append(signature.CERT_X509_GUID, *owner, cert.Raw); err != nil { return nil, err } } } _, signedKEK, err := signature.SignEFIVariable(efivar.KEK, kek, signer.Signer(), signer.Certificate()) if err != nil { return nil, err } // Create db ESL db := signature.NewSignatureDatabase() if err := db.Append(signature.CERT_X509_GUID, *efiGUID, enrolledCertificate); err != nil { return nil, err } if options.IncludeWellKnownCertificates { owner := util.StringToGUID(microsoftSignatureOwnerGUID) for _, cert := range wellKnownDBCertificates() { if err := db.Append(signature.CERT_X509_GUID, *owner, cert.Raw); err != nil { return nil, err } } } _, signedDB, err := signature.SignEFIVariable(efivar.Db, db, signer.Signer(), signer.Certificate()) if err != nil { return nil, err } return []Entry{ {Name: constants.SignatureKeyAsset, Contents: signedDB.Bytes()}, {Name: constants.KeyExchangeKeyAsset, Contents: signedKEK.Bytes()}, {Name: constants.PlatformKeyAsset, Contents: signedPK.Bytes()}, }, nil } ================================================ FILE: internal/pkg/secureboot/pesign/pesign.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pesign implements the PE (portable executable) signing. package pesign import ( "cmp" "crypto" "crypto/x509" "fmt" "io" "os" "github.com/foxboron/go-uefi/authenticode" ) // Signer sigs PE (portable executable) files. type Signer struct { provider CertificateSigner } // CertificateSigner is a provider of the certificate and the signer. type CertificateSigner interface { Signer() crypto.Signer Certificate() *x509.Certificate } // NewSigner creates a new Signer. func NewSigner(provider CertificateSigner) (*Signer, error) { return &Signer{ provider: provider, }, nil } // Sign signs the input file and writes the output to the output file. func (s *Signer) Sign(input, output string) error { in, err := os.Open(input) if err != nil { return err } defer in.Close() //nolint:errcheck pecoff, err := authenticode.Parse(in) if err != nil { return fmt.Errorf("error parsing binary: %w", err) } _, err = pecoff.Sign(s.provider.Signer(), s.provider.Certificate()) if err != nil { return fmt.Errorf("error signing binary: %w", err) } f, err := os.OpenFile(output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) if err != nil { return fmt.Errorf("error opening output file: %w", err) } closer := f.Close defer closer() //nolint:errcheck _, err = io.Copy(f, pecoff.Open()) return cmp.Or(err, closer()) } ================================================ FILE: internal/pkg/secureboot/pesign/pesign_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pesign_test import ( "crypto" stdx509 "crypto/x509" "os" "path/filepath" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" ) type certificateProvider struct { *x509.CertificateAuthority } func (c *certificateProvider) Signer() crypto.Signer { return c.CertificateAuthority.Key.(crypto.Signer) } func (c *certificateProvider) Certificate() *stdx509.Certificate { return c.CertificateAuthority.Crt } func TestSign(t *testing.T) { currentTime := time.Now() opts := []x509.Option{ x509.RSA(true), x509.Bits(2048), x509.CommonName("test-sign"), x509.NotAfter(currentTime.Add(secrets.CAValidityTime)), x509.NotBefore(currentTime), x509.Organization("test-sign"), } tmpDir := t.TempDir() signingKey, err := x509.NewSelfSignedCertificateAuthority(opts...) require.NoError(t, err) signer, err := pesign.NewSigner(&certificateProvider{signingKey}) require.NoError(t, err) require.NoError(t, signer.Sign("testdata/systemd-bootx64.efi", filepath.Join(tmpDir, "boot.efi"))) unsigned, err := os.Stat("testdata/systemd-bootx64.efi") require.NoError(t, err) signed, err := os.Stat(filepath.Join(tmpDir, "boot.efi")) require.NoError(t, err) require.Greater(t, signed.Size(), unsigned.Size()) } ================================================ FILE: internal/pkg/secureboot/secureboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package secureboot contains base definitions for the Secure Boot process. package secureboot // Phase is the phase value extended to the PCR. type Phase string const ( // EnterInitrd is the phase value extended to the PCR during the initrd. EnterInitrd Phase = "enter-initrd" // LeaveInitrd is the phase value extended to the PCR just before switching to machined. LeaveInitrd Phase = "leave-initrd" // EnterMachined is the phase value extended to the PCR before starting machined. // There should be only a signed signature for the enter-machined phase. EnterMachined Phase = "enter-machined" // StartTheWorld is the phase value extended to the PCR before starting all services. StartTheWorld Phase = "start-the-world" ) // PhaseInfo describes which phase extensions are signed/measured. type PhaseInfo struct { Phase Phase CalculateSignature bool } // OrderedPhases returns the phases that are measured, in order. // // Derived from https://github.com/systemd/systemd/blob/v253/src/boot/measure.c#L295-L308 // ref: https://www.freedesktop.org/software/systemd/man/systemd-pcrphase.service.html#Description // // In the case of Talos disk decryption, happens in machined, so we need to only sign EnterMachined // so that machined can only decrypt the disk if the system booted with the correct kernel/initrd/cmdline // OrderedPhases returns the phases that are measured. func OrderedPhases() []PhaseInfo { // DO NOT REARRANGE return []PhaseInfo{ { Phase: EnterInitrd, CalculateSignature: false, }, { Phase: LeaveInitrd, CalculateSignature: false, }, { Phase: EnterMachined, CalculateSignature: true, }, } } ================================================ FILE: internal/pkg/secureboot/tpm2/keys.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "os" "github.com/google/go-tpm/tpm2" ) // ParsePCRSigningPubKey parses a PEM encoded RSA public key. func ParsePCRSigningPubKey(file string) (*rsa.PublicKey, error) { pcrSigningPubKey, err := os.ReadFile(file) if err != nil { return nil, fmt.Errorf("failed to read pcr signing public key: %v", err) } block, _ := pem.Decode(pcrSigningPubKey) if block == nil { return nil, errors.New("failed to decode pcr signing public key") } // parse rsa public key tpm2PubKeyAny, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { return nil, err } tpm2PubKey, ok := tpm2PubKeyAny.(*rsa.PublicKey) if !ok { return nil, errors.New("failed to cast pcr signing public key to rsa") } return tpm2PubKey, nil } // RSAPubKeyTemplate returns a TPM2.0 public key template for RSA keys. func RSAPubKeyTemplate(bitlen, exponent int, modulus []byte) tpm2.TPMTPublic { return tpm2.TPMTPublic{ Type: tpm2.TPMAlgRSA, NameAlg: tpm2.TPMAlgSHA256, ObjectAttributes: tpm2.TPMAObject{ Decrypt: true, SignEncrypt: true, UserWithAuth: true, }, Parameters: tpm2.NewTPMUPublicParms(tpm2.TPMAlgRSA, &tpm2.TPMSRSAParms{ Symmetric: tpm2.TPMTSymDefObject{ Algorithm: tpm2.TPMAlgNull, Mode: tpm2.NewTPMUSymMode(tpm2.TPMAlgRSA, tpm2.TPMAlgNull), }, Scheme: tpm2.TPMTRSAScheme{ Scheme: tpm2.TPMAlgNull, Details: tpm2.NewTPMUAsymScheme(tpm2.TPMAlgRSA, &tpm2.TPMSSigSchemeRSASSA{ HashAlg: tpm2.TPMAlgNull, }), }, KeyBits: tpm2.TPMKeyBits(bitlen), Exponent: uint32(exponent), }), Unique: tpm2.NewTPMUPublicID(tpm2.TPMAlgRSA, &tpm2.TPM2BPublicKeyRSA{ Buffer: modulus, }), } } ================================================ FILE: internal/pkg/secureboot/tpm2/pcr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "bytes" "crypto/sha256" "errors" "fmt" "io/fs" "log" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/siderolabs/talos/internal/pkg/tpm" "github.com/siderolabs/talos/pkg/machinery/constants" ) // CreateSelector converts PCR numbers into a bitmask. func CreateSelector(pcrs []int) ([]byte, error) { const sizeOfPCRSelect = 3 mask := make([]byte, sizeOfPCRSelect) for _, n := range pcrs { if n >= 8*sizeOfPCRSelect { return nil, fmt.Errorf("PCR index %d is out of range (exceeds maximum value %d)", n, 8*sizeOfPCRSelect-1) } mask[n>>3] |= 1 << (n & 0x7) } return mask, nil } // ReadPCR reads the value of a single PCR. func ReadPCR(t transport.TPM, pcr int) ([]byte, error) { pcrSelector, err := CreateSelector([]int{pcr}) if err != nil { return nil, fmt.Errorf("failed to create PCR selection: %w", err) } pcrRead := tpm2.PCRRead{ PCRSelectionIn: tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: tpm2.TPMAlgSHA256, PCRSelect: pcrSelector, }, }, }, } pcrValue, err := pcrRead.Execute(t) if err != nil { return nil, fmt.Errorf("failed to read PCR: %w", err) } return pcrValue.PCRValues.Digests[0].Buffer, nil } // PCRExtend hashes the input and extends the PCR with the hash. func PCRExtend(pcr int, data []byte) error { t, err := tpm.Open() if err != nil { // if the TPM is not available we can skip the PCR extension if errors.Is(err, fs.ErrNotExist) { log.Printf("TPM device is not available, skipping PCR extension") return nil } return err } defer t.Close() //nolint:errcheck // now we need to check if the TPM is a 2.0 device // we can do this by checking the manufacturer, // if it fails, we can skip the PCR extension _, err = tpm2.GetCapability{ Capability: tpm2.TPMCapTPMProperties, Property: uint32(tpm2.TPMPTManufacturer), PropertyCount: 1, }.Execute(t) if err != nil { log.Printf("TPM device is not a TPM 2.0, skipping PCR extension") return nil } // since we are using SHA256, we can assume that the PCR bank is SHA256 digest := sha256.Sum256(data) pcrHandle := tpm2.PCRExtend{ PCRHandle: tpm2.AuthHandle{ Handle: tpm2.TPMHandle(pcr), Auth: tpm2.PasswordAuth(nil), }, Digests: tpm2.TPMLDigestValues{ Digests: []tpm2.TPMTHA{ { HashAlg: tpm2.TPMAlgSHA256, Digest: digest[:], }, }, }, } if _, err = pcrHandle.Execute(t); err != nil { return err } return nil } // PolicyPCRDigest executes policyPCR and returns the digest. func PolicyPCRDigest(t transport.TPM, policyHandle tpm2.TPMHandle, pcrSelection tpm2.TPMLPCRSelection) (*tpm2.TPM2BDigest, error) { policyPCR := tpm2.PolicyPCR{ PolicySession: policyHandle, Pcrs: pcrSelection, } if _, err := policyPCR.Execute(t); err != nil { return nil, fmt.Errorf("failed to execute policyPCR: %w", err) } policyGetDigest := tpm2.PolicyGetDigest{ PolicySession: policyHandle, } policyGetDigestResponse, err := policyGetDigest.Execute(t) if err != nil { return nil, fmt.Errorf("failed to get policy digest: %w", err) } return &policyGetDigestResponse.PolicyDigest, nil } //nolint:gocyclo func validatePCRBanks(t transport.TPM, tpmPCRs []int) error { pcrValue, err := ReadPCR(t, constants.UKIPCR) if err != nil { return fmt.Errorf("failed to read PCR: %w", err) } if err = validatePCRNotZeroAndNotFilled(pcrValue, constants.UKIPCR); err != nil { return err } // here we need to read individual PCRs and not the overall PCR state to make sure each is not empty for _, pcr := range tpmPCRs { pcrValue, err = ReadPCR(t, pcr) if err != nil { return fmt.Errorf("failed to read PCR %d: %w", pcr, err) } if err = validatePCRNotZeroAndNotFilled(pcrValue, pcr); err != nil { return err } } caps := tpm2.GetCapability{ Capability: tpm2.TPMCapPCRs, Property: 0, PropertyCount: 1, } capsResp, err := caps.Execute(t) if err != nil { return fmt.Errorf("failed to get PCR capabilities: %w", err) } assignedPCRs, err := capsResp.CapabilityData.Data.AssignedPCR() if err != nil { return fmt.Errorf("failed to parse assigned PCRs: %w", err) } for _, s := range assignedPCRs.PCRSelections { if s.Hash != tpm2.TPMAlgSHA256 { continue } // check if 24 banks are available if len(s.PCRSelect) != 24/8 { return fmt.Errorf("unexpected number of PCR banks: %d", len(s.PCRSelect)) } // check if all banks are available if s.PCRSelect[0] != 0xff || s.PCRSelect[1] != 0xff || s.PCRSelect[2] != 0xff { return fmt.Errorf("unexpected PCR banks: %v", s.PCRSelect) } } return nil } func validatePCRNotZeroAndNotFilled(pcrValue []byte, pcr int) error { if bytes.Equal(pcrValue, bytes.Repeat([]byte{0x00}, sha256.Size)) { return fmt.Errorf("PCR bank %d is populated with all zeroes", pcr) } if bytes.Equal(pcrValue, bytes.Repeat([]byte{0xFF}, sha256.Size)) { return fmt.Errorf("PCR bank %d is populated with all 0xFF", pcr) } return nil } ================================================ FILE: internal/pkg/secureboot/tpm2/pcr_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2_test import ( "testing" "github.com/stretchr/testify/require" tpm2internal "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" ) func TestGetSelection(t *testing.T) { t.Parallel() for _, tt := range []struct { name string pcrs []int expected []byte }{ { name: "empty", expected: []byte{0, 0, 0}, }, { name: "1, 3, 5", pcrs: []int{1, 3, 5}, expected: []byte{42, 0, 0}, }, { name: "21, 22, 23", pcrs: []int{21, 22, 23}, expected: []byte{0, 0, 0xe0}, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() actual, err := tpm2internal.CreateSelector(tt.pcrs) require.NoError(t, err) require.Equal(t, tt.expected, actual) }) } } ================================================ FILE: internal/pkg/secureboot/tpm2/policy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "crypto/sha256" "fmt" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" ) // CalculatePolicy calculates the policy hash for a given PCR value and PCR selection. func CalculatePolicy(pcrValue []byte, pcrSelection tpm2.TPMLPCRSelection) ([]byte, error) { calculator, err := tpm2.NewPolicyCalculator(tpm2.TPMAlgSHA256) if err != nil { return nil, err } pcrHash := sha256.Sum256(pcrValue) policy := tpm2.PolicyPCR{ PcrDigest: tpm2.TPM2BDigest{ Buffer: pcrHash[:], }, Pcrs: pcrSelection, } if err := policy.Update(calculator); err != nil { return nil, err } return calculator.Hash().Digest, nil } // calculatePolicyAuthorize creates and updates a PolicyAuthorize for the given public key. func calculatePolicyAuthorize(calculator *tpm2.PolicyCalculator, pubKey string) error { pubKeyData, err := ParsePCRSigningPubKey(pubKey) if err != nil { return err } publicKeyTemplate := RSAPubKeyTemplate(pubKeyData.N.BitLen(), pubKeyData.E, pubKeyData.N.Bytes()) name, err := tpm2.ObjectName(&publicKeyTemplate) if err != nil { return fmt.Errorf("failed to calculate name: %v", err) } policyAuthorize := tpm2.PolicyAuthorize{ KeySign: *name, } return policyAuthorize.Update(calculator) } // SealingPolicyDigestInfo holds the information needed to calculate a sealing policy digest. type SealingPolicyDigestInfo struct { PublicKey string PCRs []int ReadPCRFunc func(t transport.TPM, pcr int) ([]byte, error) } // CalculateSealingPolicyDigest calculates the sealing policy digest for a given public key and PCRs. func CalculateSealingPolicyDigest(t transport.TPM, spInfo SealingPolicyDigestInfo) ([]byte, error) { calculator, err := tpm2.NewPolicyCalculator(tpm2.TPMAlgSHA256) if err != nil { return nil, fmt.Errorf("failed to create policy calculator: %v", err) } if err := calculatePolicyAuthorize(calculator, spInfo.PublicKey); err != nil { return nil, fmt.Errorf("failed to calculate policy authorize: %v", err) } if len(spInfo.PCRs) == 0 { return calculator.Hash().Digest, nil } pcrSelector, err := CreateSelector(spInfo.PCRs) if err != nil { return nil, fmt.Errorf("failed to create PCR selection for PCRs %v: %v", spInfo.PCRs, err) } pcrSelection := tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: tpm2.TPMAlgSHA256, PCRSelect: pcrSelector, }, }, } hash := sha256.New() for _, p := range spInfo.PCRs { pcrValue, err := spInfo.ReadPCRFunc(t, p) if err != nil { return nil, fmt.Errorf("failed to read PCR %d: %v", p, err) } if _, err := hash.Write(pcrValue); err != nil { return nil, fmt.Errorf("failed to hash PCR value for PCR %d: %v", p, err) } } policy := tpm2.PolicyPCR{ PcrDigest: tpm2.TPM2BDigest{ Buffer: hash.Sum(nil), }, Pcrs: pcrSelection, } if err := policy.Update(calculator); err != nil { return nil, fmt.Errorf("failed to update policy digest for PCRs %v: %v", spInfo.PCRs, err) } return calculator.Hash().Digest, nil } ================================================ FILE: internal/pkg/secureboot/tpm2/policy_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2_test import ( "testing" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/stretchr/testify/require" tpm2internal "github.com/siderolabs/talos/internal/pkg/secureboot/tpm2" ) func TestCalculatePolicy(t *testing.T) { t.Parallel() policy, err := tpm2internal.CalculatePolicy([]byte{1, 3, 5}, tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: tpm2.TPMAlgSHA256, PCRSelect: []byte{10, 11, 12}, }, }, }) require.NoError(t, err) require.Equal(t, []byte{0x84, 0xd6, 0x51, 0x47, 0xb0, 0x53, 0x94, 0xd0, 0xfa, 0xc4, 0x5e, 0x36, 0x0, 0x20, 0x3e, 0x3a, 0x11, 0x1, 0x27, 0xfb, 0xe2, 0x6f, 0xc1, 0xe3, 0x3, 0x3, 0x10, 0x21, 0x33, 0xf9, 0x15, 0xe3}, policy, ) } func TestCalculateSealingPolicyDigestWithNOPCRs(t *testing.T) { t.Parallel() calculated, err := tpm2internal.CalculateSealingPolicyDigest(nil, tpm2internal.SealingPolicyDigestInfo{ PublicKey: "testdata/pcr-signing-crt.pem", ReadPCRFunc: func(t transport.TPM, pcr int) ([]byte, error) { return nil, nil }, }) require.NoError(t, err) require.Equal(t, []byte{0x86, 0xdc, 0x2b, 0x7f, 0x5a, 0xeb, 0xde, 0x57, 0xd4, 0x72, 0xdd, 0xbc, 0x3d, 0x5b, 0xd5, 0xb5, 0xb, 0x15, 0x6f, 0x4d, 0x6b, 0xa6, 0x62, 0xc2, 0x1a, 0xff, 0xbf, 0xb1, 0xb2, 0xd4, 0xb9, 0x84}, calculated, ) } func TestCalculateSealingPolicyDigestWithPCRs(t *testing.T) { t.Parallel() calculated, err := tpm2internal.CalculateSealingPolicyDigest(nil, tpm2internal.SealingPolicyDigestInfo{ PublicKey: "testdata/pcr-signing-crt.pem", PCRs: []int{0, 11}, ReadPCRFunc: func(t transport.TPM, pcr int) ([]byte, error) { return []byte{0x9c, 0x9c, 0x10, 0x58, 0x77, 0x9d, 0x2b, 0xf6, 0x30, 0x1b, 0x56, 0x8, 0x5b, 0x26, 0xe9, 0xae, 0x98, 0x62, 0x2e, 0x1f, 0xa7, 0x3e, 0xad, 0xd9, 0x8b, 0x9c, 0xa3, 0xa1, 0x8, 0x29, 0xc1, 0x9c}, nil //nolint:lll }, }) require.NoError(t, err) require.Equal(t, []byte{0x14, 0xff, 0x86, 0xf8, 0x6, 0x9c, 0xe4, 0xf2, 0xc9, 0x18, 0x32, 0x5e, 0x2e, 0x1b, 0x78, 0x11, 0xcf, 0xc5, 0xe6, 0x27, 0x8d, 0xd1, 0xb, 0x86, 0xa9, 0x4, 0x16, 0xc2, 0x8f, 0xe6, 0x47, 0x6a}, calculated) //nolint:lll } ================================================ FILE: internal/pkg/secureboot/tpm2/seal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "fmt" "github.com/google/go-tpm/tpm2" "github.com/siderolabs/talos/internal/pkg/tpm" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Seal seals the key using TPM2.0. func Seal(key []byte, tpmPCRs []int) (*SealedResponse, error) { t, err := tpm.Open() if err != nil { return nil, err } defer t.Close() //nolint:errcheck // fail early if PCR banks are not present or filled with all zeroes or 0xff if err = validatePCRBanks(t, tpmPCRs); err != nil { return nil, err } sealingPolicyDigest, err := CalculateSealingPolicyDigest(t, SealingPolicyDigestInfo{ PublicKey: constants.PCRPublicKey, PCRs: tpmPCRs, ReadPCRFunc: ReadPCR, }) if err != nil { return nil, fmt.Errorf("failed to calculate sealing policy digest: %v", err) } primary := tpm2.CreatePrimary{ PrimaryHandle: tpm2.TPMRHOwner, InPublic: tpm2.New2B(tpm2.ECCSRKTemplate), } createPrimaryResponse, err := primary.Execute(t) if err != nil { return nil, err } // TODO: persist SRK as per https://github.com/systemd/systemd/blob/main/src/shared/tpm2-util.c#L1039 defer func() { flush := tpm2.FlushContext{ FlushHandle: createPrimaryResponse.ObjectHandle, } _, flushErr := flush.Execute(t) if flushErr != nil { err = flushErr } }() outPub, err := createPrimaryResponse.OutPublic.Contents() if err != nil { return nil, err } create := tpm2.Create{ ParentHandle: tpm2.AuthHandle{ Handle: createPrimaryResponse.ObjectHandle, Name: createPrimaryResponse.Name, Auth: tpm2.HMAC( tpm2.TPMAlgSHA256, 20, tpm2.Salted(createPrimaryResponse.ObjectHandle, *outPub), tpm2.AESEncryption(128, tpm2.EncryptInOut), ), }, InSensitive: tpm2.TPM2BSensitiveCreate{ Sensitive: &tpm2.TPMSSensitiveCreate{ Data: tpm2.NewTPMUSensitiveCreate(&tpm2.TPM2BSensitiveData{ Buffer: key, }), }, }, InPublic: tpm2.New2B(tpm2.TPMTPublic{ Type: tpm2.TPMAlgKeyedHash, NameAlg: tpm2.TPMAlgSHA256, ObjectAttributes: tpm2.TPMAObject{ FixedTPM: true, FixedParent: true, NoDA: true, // see https://github.com/systemd/systemd/pull/30728 }, Parameters: tpm2.NewTPMUPublicParms(tpm2.TPMAlgKeyedHash, &tpm2.TPMSKeyedHashParms{ Scheme: tpm2.TPMTKeyedHashScheme{ Scheme: tpm2.TPMAlgNull, }, }), AuthPolicy: tpm2.TPM2BDigest{ Buffer: sealingPolicyDigest, }, }), } createResp, err := create.Execute(t) if err != nil { return nil, err } resp := SealedResponse{ SealedBlobPrivate: tpm2.Marshal(createResp.OutPrivate), SealedBlobPublic: tpm2.Marshal(createResp.OutPublic), KeyName: tpm2.Marshal(createPrimaryResponse.Name), PolicyDigest: sealingPolicyDigest, PCRs: tpmPCRs, PubKeyPCRs: []int{constants.UKIPCR}, EncryptionVersion: EncryptionSchemaVersionErrata, Alg: "sha256", } return &resp, nil } ================================================ FILE: internal/pkg/secureboot/tpm2/signature.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "encoding/json" "fmt" "os" "github.com/siderolabs/talos/pkg/machinery/constants" ) // PCRData is the data structure for PCR signature json. type PCRData struct { SHA1 []BankData `json:"sha1,omitempty"` SHA256 []BankData `json:"sha256,omitempty"` SHA384 []BankData `json:"sha384,omitempty"` SHA512 []BankData `json:"sha512,omitempty"` } // BankData constains data for a specific PCR bank. type BankData struct { // list of PCR banks PCRs []int `json:"pcrs"` // Public key of the TPM PKFP string `json:"pkfp"` // Policy digest Pol string `json:"pol"` // Signature of the policy digest in base64 Sig string `json:"sig"` } // ParsePCRSignature parses the PCR signature json file. func ParsePCRSignature() (*PCRData, error) { pcrSignature, err := os.ReadFile(constants.PCRSignatureJSON) if err != nil { return nil, fmt.Errorf("failed to read pcr signature: %v", err) } pcrData := &PCRData{} if err = json.Unmarshal(pcrSignature, pcrData); err != nil { return nil, fmt.Errorf("failed to unmarshal pcr signature: %v", err) } return pcrData, nil } ================================================ FILE: internal/pkg/secureboot/tpm2/testdata/pcr-signing-crt.pem ================================================ -----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7qhAkdtZxqkIP79DDGin 9eaJBeNlJsClJTcbaXbNfk2QJGT3lqo9ErXQQftwYWLGo+kVd8puhnHGPkLW9apT 1/ZmUJEFwxV5xws0RllGVPhUga+1oubHUqhEiy707S4RrUEMk/o9wqmtnl2hY5Fx MeQn2o7xrpcNhm8FtHpvQrT0MsbC1cS1ytZH/hwPy/QIB9bx+ugOha6wtQBnpgix 1BhHC/NwDIYPg+ONpQSCu9gkXVtLGlKfmjscUANQtBuVKa5NflrjkHw7NAdKYdKp Mnmzr0yu6Tn/2oNmUiJAwHz0BXpfb4Yn8n/IoKJQ5Tv1g6d30wxxpBd0lbwSe9ML RchIDJ5aFRybyRxaPGT17U3yEVzbV78kIFtocaqkc1ise8remZ0wxHzuolbTZD6o swt7C9jMLvfMAQ7JtENXrpDM//XzdRLzyTWKOjhG0YmKKRY6cIrPkugM0PHGCE3R MSH1FmPMrWWBNAMwS0Zba0Wm1b7vdw5fKeE8txH+IpA3IaE9AytYk0ig98ZgmXmB V0sgxmJ/94scEF+sDg65LIkSEJMzf6q30UghbJJoP7eKOoDX9KBrR+POEsWm/EcU 5jTEQTHMU+qKtj5KD6TUn8R8yi4wCnyZ7uJLUqm8Ou8MzEZWbrsrbMvrewPDAHn0 QQvb2tDtBgn6oH192jpkzckCAwEAAQ== -----END PUBLIC KEY----- ================================================ FILE: internal/pkg/secureboot/tpm2/tpm2.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 const ( // EncryptionSchemaVersionErrata is the errata for the encryption schema version. // Talos versions older than 1.12 locked to PCR 7 and PCR 11 but the luks json header only // saved the PCR 11 value, so if the version is not set or empty we can assume that the keys // are sealed to both PCR 7 and PCR 11. If the version is `1` we can be sure that the keys // are locked to PCR 11 only. EncryptionSchemaVersionErrata = "1" ) // SealedResponse is the response from the TPM2.0 Seal operation. type SealedResponse struct { SealedBlobPrivate []byte SealedBlobPublic []byte KeyName []byte PolicyDigest []byte PCRs []int PubKeyPCRs []int EncryptionVersion string Alg string } ================================================ FILE: internal/pkg/secureboot/tpm2/unseal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package tpm2 provides TPM2.0 related functionality helpers. package tpm2 import ( "bytes" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/hex" "errors" "fmt" "github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2/transport" "github.com/siderolabs/talos/internal/pkg/tpm" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Unseal unseals a sealed blob using the TPM // //nolint:gocyclo,cyclop func Unseal(sealed SealedResponse) ([]byte, error) { t, err := tpm.Open() if err != nil { return nil, err } defer t.Close() //nolint:errcheck // fail early if PCR banks are not present or filled with all zeroes or 0xff if err = validatePCRBanks(t, sealed.PCRs); err != nil { return nil, err } tpmPub, err := tpm2.Unmarshal[tpm2.TPM2BPublic](sealed.SealedBlobPublic) if err != nil { return nil, err } tpmPriv, err := tpm2.Unmarshal[tpm2.TPM2BPrivate](sealed.SealedBlobPrivate) if err != nil { return nil, err } srk, err := tpm2.Unmarshal[tpm2.TPM2BName](sealed.KeyName) if err != nil { return nil, err } // we need to create a primary since we don't persist the SRK primary := tpm2.CreatePrimary{ PrimaryHandle: tpm2.TPMRHOwner, InPublic: tpm2.New2B(tpm2.ECCSRKTemplate), } createPrimaryResponse, err := primary.Execute(t) if err != nil { return nil, err } defer func() { flush := tpm2.FlushContext{ FlushHandle: createPrimaryResponse.ObjectHandle, } _, flushErr := flush.Execute(t) if flushErr != nil { err = flushErr } }() outPub, err := createPrimaryResponse.OutPublic.Contents() if err != nil { return nil, err } if !bytes.Equal(createPrimaryResponse.Name.Buffer, srk.Buffer) { // this means the srk name does not match, possibly due to a different TPM or tpm was reset // could also mean the disk was used on a different machine return nil, fmt.Errorf("srk name does not match, expected %x, got %x", srk.Buffer, createPrimaryResponse.Name.Buffer) } load := tpm2.Load{ ParentHandle: tpm2.NamedHandle{ Handle: createPrimaryResponse.ObjectHandle, Name: createPrimaryResponse.Name, }, InPrivate: *tpmPriv, InPublic: *tpmPub, } loadResponse, err := load.Execute(t) if err != nil { return nil, err } policySess, policyCloseFunc, err := tpm2.PolicySession( t, tpm2.TPMAlgSHA256, 20, tpm2.Salted(createPrimaryResponse.ObjectHandle, *outPub), ) if err != nil { return nil, fmt.Errorf("failed to create policy session: %w", err) } defer policyCloseFunc() //nolint:errcheck pubKey, err := ParsePCRSigningPubKey(constants.PCRPublicKey) if err != nil { return nil, err } loadExternal := tpm2.LoadExternal{ Hierarchy: tpm2.TPMRHOwner, InPublic: tpm2.New2B(RSAPubKeyTemplate(pubKey.N.BitLen(), pubKey.E, pubKey.N.Bytes())), } loadExternalResponse, err := loadExternal.Execute(t) if err != nil { return nil, fmt.Errorf("failed to load external key: %w", err) } defer func() { flush := tpm2.FlushContext{ FlushHandle: loadExternalResponse.ObjectHandle, } _, flushErr := flush.Execute(t) if flushErr != nil { err = flushErr } }() pcrSelector, err := CreateSelector([]int{constants.UKIPCR}) if err != nil { return nil, err } policyDigest, err := PolicyPCRDigest(t, policySess.Handle(), tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: tpm2.TPMAlgSHA256, PCRSelect: pcrSelector, }, }, }) if err != nil { return nil, fmt.Errorf("failed to retrieve policy digest: %w", err) } sigJSON, err := ParsePCRSignature() if err != nil { return nil, err } pubKeyFingerprint := sha256.Sum256(x509.MarshalPKCS1PublicKey(pubKey)) var signature string // TODO: maybe we should use the highest supported algorithm of the TPM // fallback to the next one if the signature is not found for _, bank := range sigJSON.SHA256 { digest, decodeErr := hex.DecodeString(bank.Pol) if decodeErr != nil { return nil, decodeErr } if bytes.Equal(digest, policyDigest.Buffer) { signature = bank.Sig if hex.EncodeToString(pubKeyFingerprint[:]) != bank.PKFP { return nil, errors.New("certificate fingerprint does not match") } break } } if signature == "" { return nil, errors.New("no signatures matching PCR SHA256 digest found in signature JSON") } signatureDecoded, err := base64.StdEncoding.DecodeString(signature) if err != nil { return nil, err } // Verify will only verify the RSA part of the RSA+SHA256 signature, // hence we need to do the SHA256 part ourselves policyDigestHash := sha256.Sum256(policyDigest.Buffer) verifySignature := tpm2.VerifySignature{ KeyHandle: loadExternalResponse.ObjectHandle, Digest: tpm2.TPM2BDigest{ Buffer: policyDigestHash[:], }, Signature: tpm2.TPMTSignature{ SigAlg: tpm2.TPMAlgRSASSA, Signature: tpm2.NewTPMUSignature(tpm2.TPMAlgRSASSA, &tpm2.TPMSSignatureRSA{ Hash: tpm2.TPMAlgSHA256, Sig: tpm2.TPM2BPublicKeyRSA{ Buffer: signatureDecoded, }, }), }, } verifySignatureResponse, err := verifySignature.Execute(t) if err != nil { return nil, fmt.Errorf("failed to verify signature: %w", err) } policyAuthorize := tpm2.PolicyAuthorize{ PolicySession: policySess.Handle(), ApprovedPolicy: *policyDigest, KeySign: loadExternalResponse.Name, CheckTicket: verifySignatureResponse.Validation, } if _, err = policyAuthorize.Execute(t); err != nil { return nil, fmt.Errorf("failed to execute policy authorize: %w", err) } // this handles the case when talos is upgraded from pre Talos 1.12 and we had PCRs field // set to just PCR 11 but locked to PCR 7 // in this case the EncryptionVersion is empty and the PubKeyPCRs field is also empty // since both EncryptionVersion and PubKeyPCRs are introduced for Talos 1.12+ only if sealed.EncryptionVersion == "" && len(sealed.PubKeyPCRs) == 0 { if len(sealed.PCRs) == 1 && sealed.PCRs[0] == constants.UKIPCR { if err := validatePCRPolicyDigest(t, policySess.Handle(), []int{constants.SecureBootStatePCR}, sealed.PolicyDigest); err != nil { return nil, fmt.Errorf("failed to validate PCR policy digest for PCRs %v: %w", []int{constants.SecureBootStatePCR}, err) } } } // Talos 1.12+ sets the EncryptionVersion and PubKeyPCRs to PCR 11 and any other PCR used to PCRs field if sealed.EncryptionVersion != "" && len(sealed.PCRs) > 0 { if err := validatePCRPolicyDigest(t, policySess.Handle(), sealed.PCRs, sealed.PolicyDigest); err != nil { return nil, fmt.Errorf("failed to validate PCR policy digest for PCRs %v: %w", sealed.PCRs, err) } } unsealOp := tpm2.Unseal{ ItemHandle: tpm2.AuthHandle{ Handle: loadResponse.ObjectHandle, Name: loadResponse.Name, Auth: policySess, }, } unsealResponse, err := unsealOp.Execute(t, tpm2.HMAC( tpm2.TPMAlgSHA256, 20, tpm2.Salted(createPrimaryResponse.ObjectHandle, *outPub), tpm2.AESEncryption(128, tpm2.EncryptOut), tpm2.Bound(loadResponse.ObjectHandle, loadResponse.Name, nil), )) if err != nil { return nil, fmt.Errorf("failed to unseal op: %w", err) } return unsealResponse.OutData.Buffer, nil } func validatePCRPolicyDigest(t transport.TPM, handle tpm2.TPMHandle, pcrs []int, digest []byte) error { pcrSelector, err := CreateSelector(pcrs) if err != nil { return fmt.Errorf("failed to create PCR selector for PCRs %v: %w", pcrs, err) } pcrPolicyDigest, err := PolicyPCRDigest(t, handle, tpm2.TPMLPCRSelection{ PCRSelections: []tpm2.TPMSPCRSelection{ { Hash: tpm2.TPMAlgSHA256, PCRSelect: pcrSelector, }, }, }) if err != nil { return fmt.Errorf("failed to calculate policy PCR digest: %w", err) } if !bytes.Equal(pcrPolicyDigest.Buffer, digest) { return fmt.Errorf("sealing policy digest does not match, expected %x, got %x", digest, pcrPolicyDigest.Buffer) } return nil } ================================================ FILE: internal/pkg/selinux/policy/file_contexts ================================================ /etc(/.*)? system_u:object_r:etc_t:s0 /opt(/.*)? system_u:object_r:opt_t:s0 /usr(/.*)? system_u:object_r:usr_t:s0 /etc/cni(/.*)? system_u:object_r:cni_conf_t:s0 /opt/cni(/.*)? system_u:object_r:cni_plugin_t:s0 /usr/bin(/.*)? system_u:object_r:bin_exec_t:s0 /usr/lib(/.*)? system_u:object_r:lib_t:s0 /usr/lib/udev(/.*)? system_u:object_r:udev_exec_t:s0 /etc/kubernetes(/.*)? system_u:object_r:k8s_conf_t:s0 /opt/containerd(/.*)? system_u:object_r:containerd_plugin_t:s0 /usr/lib/modules(/.*)? system_u:object_r:module_t:s0 /usr/share/zoneinfo(/.*)? system_u:object_r:etc_t:s0 /usr/lib/udev/rules.d(/.*)? system_u:object_r:udev_rules_t:s0 /usr/libexec/kubernetes(/.*)? system_u:object_r:k8s_plugin_t:s0 /usr/local/lib/kubelet/credentialproviders(/.*)? system_u:object_r:k8s_credentialproviders_t:s0 / system_u:object_r:rootfs_t:s0 /bin system_u:object_r:bin_exec_t:s0 /lib system_u:object_r:lib_t:s0 /sbin system_u:object_r:bin_exec_t:s0 /lib64 system_u:object_r:lib_t:s0 /usr/sbin system_u:object_r:bin_exec_t:s0 /usr/lib64 system_u:object_r:lib_t:s0 /lib/modules system_u:object_r:module_t:s0 /usr/bin/runc system_u:object_r:containerd_exec_t:s0 /usr/bin/init -- system_u:object_r:init_exec_t:s0 /usr/bin/udevadm -- system_u:object_r:udev_exec_t:s0 /usr/bin/poweroff system_u:object_r:init_exec_t:s0 /usr/bin/shutdown system_u:object_r:init_exec_t:s0 /usr/bin/modprobe -- system_u:object_r:modprobe_exec_t:s0 /usr/bin/dashboard system_u:object_r:init_exec_t:s0 /usr/bin/containerd system_u:object_r:containerd_exec_t:s0 /usr/bin/systemd-udevd -- system_u:object_r:udev_exec_t:s0 /usr/bin/containerd-shim-runc-v2 system_u:object_r:containerd_exec_t:s0 ================================================ FILE: internal/pkg/selinux/policy/selinux/common/classmaps.cil ================================================ ; Access to all file classes (classmap fs_classes (getattr ioctl relabelfrom relabelto mounton rw ro)) ; getattr (classmapping fs_classes relabelfrom (filesystem (getattr))) (classmapping fs_classes getattr (file (getattr))) (classmapping fs_classes getattr (dir (getattr))) (classmapping fs_classes getattr (lnk_file (getattr))) (classmapping fs_classes getattr (chr_file (getattr))) (classmapping fs_classes getattr (blk_file (getattr))) (classmapping fs_classes getattr (sock_file (getattr))) (classmapping fs_classes getattr (fifo_file (getattr))) ; ioctl (classmapping fs_classes ioctl (file (ioctl))) (classmapping fs_classes ioctl (dir (ioctl))) (classmapping fs_classes ioctl (lnk_file (ioctl))) (classmapping fs_classes ioctl (chr_file (ioctl))) (classmapping fs_classes ioctl (blk_file (ioctl))) (classmapping fs_classes ioctl (sock_file (ioctl))) (classmapping fs_classes ioctl (fifo_file (ioctl))) ; relabelfrom (classmapping fs_classes relabelfrom (filesystem (relabelfrom))) (classmapping fs_classes relabelfrom (file (relabelfrom))) (classmapping fs_classes relabelfrom (dir (relabelfrom))) (classmapping fs_classes relabelfrom (lnk_file (relabelfrom))) (classmapping fs_classes relabelfrom (chr_file (relabelfrom))) (classmapping fs_classes relabelfrom (blk_file (relabelfrom))) (classmapping fs_classes relabelfrom (sock_file (relabelfrom))) (classmapping fs_classes relabelfrom (fifo_file (relabelfrom))) ; relabelto (classmapping fs_classes relabelto (filesystem (relabelto))) (classmapping fs_classes relabelto (file (relabelto))) (classmapping fs_classes relabelto (dir (relabelto))) (classmapping fs_classes relabelto (lnk_file (relabelto))) (classmapping fs_classes relabelto (chr_file (relabelto))) (classmapping fs_classes relabelto (blk_file (relabelto))) (classmapping fs_classes relabelto (sock_file (relabelto))) (classmapping fs_classes relabelto (fifo_file (relabelto))) ; mounton (classmapping fs_classes mounton (file (mounton))) (classmapping fs_classes mounton (dir (mounton))) (classmapping fs_classes mounton (lnk_file (mounton))) (classmapping fs_classes mounton (chr_file (mounton))) (classmapping fs_classes mounton (blk_file (mounton))) (classmapping fs_classes mounton (sock_file (mounton))) (classmapping fs_classes mounton (fifo_file (mounton))) ; rw is full without SELinux management (classmapping fs_classes rw (filesystem ( associate getattr mount quotaget quotamod remount unmount watch ))) (classmapping fs_classes rw (file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) (classmapping fs_classes rw (dir ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write add_name remove_name reparent rmdir search ))) (classmapping fs_classes rw (lnk_file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) (classmapping fs_classes rw (chr_file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) (classmapping fs_classes rw (blk_file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) (classmapping fs_classes rw (sock_file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) (classmapping fs_classes rw (fifo_file ( append create execmod getattr ioctl link lock map mounton open quotaon read rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write ))) ; ro is rw without write and configure (classmapping fs_classes ro (filesystem ( associate getattr quotaget watch ))) (classmapping fs_classes ro (file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) (classmapping fs_classes ro (dir ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm search ))) (classmapping fs_classes ro (lnk_file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) (classmapping fs_classes ro (chr_file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) (classmapping fs_classes ro (blk_file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) (classmapping fs_classes ro (sock_file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) (classmapping fs_classes ro (fifo_file ( execmod getattr lock map open read watch watch_mount watch_reads watch_sb watch_with_perm ))) ; Netlink socket access (classmap netlink_classes (full)) ; Full means any access except relabeling (classmapping netlink_classes full (netlink_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_route_socket ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write nlmsg_read nlmsg_write ))) (classmapping netlink_classes full (netlink_tcpdiag_socket ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write nlmsg_read nlmsg_write ))) (classmapping netlink_classes full (netlink_nflog_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_xfrm_socket ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write nlmsg_read nlmsg_write ))) (classmapping netlink_classes full (netlink_selinux_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_audit_socket ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write nlmsg_read nlmsg_write nlmsg_relay nlmsg_readpriv nlmsg_tty_audit ))) (classmapping netlink_classes full (netlink_dnrt_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) ; Used by eBPF performance utilities (classmapping netlink_classes full (netlink_kobject_uevent_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_iscsi_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_fib_lookup_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_connector_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_netfilter_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_generic_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_scsitransport_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_rdma_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (classmapping netlink_classes full (netlink_crypto_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) ; Everything except ptrace (classmap process_classes (full signal)) (classmapping process_classes full (process ( dyntransition execheap execmem execstack fork getattr getcap getpgid getsched getsession getrlimit noatsecure rlimitinh setcap setcurrent setexec setfscreate setkeycreate setpgid setrlimit setsched setsockcreate share sigchld siginh sigkill signal signull sigstop transition ))) (classmapping process_classes signal (process ( sigchld siginh sigkill signal signull sigstop ))) ================================================ FILE: internal/pkg/selinux/policy/selinux/common/files.cil ================================================ ; Runtime and mounted filesystems (type system_t) (call filesystem_f (system_t)) (allow system_t tmpfs_t (filesystem (associate))) (type system_run_t) (call system_f (system_run_t)) (allow system_run_t tmpfs_t (filesystem (associate))) (type system_run_containerd_t) (call system_f (system_run_containerd_t)) (allow system_run_containerd_t tmpfs_t (filesystem (associate))) (type run_containerd_t) (call system_f (run_containerd_t)) (allow run_containerd_t tmpfs_t (filesystem (associate))) (type etc_t) (call system_f (etc_t)) (allow etc_t fs_t (filesystem (associate))) (allow etc_t tmpfs_t (filesystem (associate))) (context etc_t (system_u object_r etc_t (systemLow systemLow))) (filecon "/etc(/.*)?" any etc_t) (filecon "/usr/share/zoneinfo(/.*)?" any etc_t) (type system_var_t) (call system_f (system_var_t)) (allow system_var_t fs_t (filesystem (associate))) (allow system_var_t tmpfs_t (filesystem (associate))) (type ephemeral_t) (call filesystem_f (ephemeral_t)) (type system_state_t) (call filesystem_f (system_state_t)) (type run_t) (call filesystem_f (run_t)) (allow run_t tmpfs_t (filesystem (associate))) (type opt_t) (call filesystem_f (opt_t)) (filecon "/opt(/.*)?" any (system_u object_r opt_t (systemLow systemLow))) (type usr_t) (call system_f (usr_t)) (filecon "/usr(/.*)?" any (system_u object_r usr_t (systemLow systemLow))) ================================================ FILE: internal/pkg/selinux/policy/selinux/common/network.cil ================================================ (allow any_p port_t (dccp_socket (name_connect))) (allow any_p port_t (sctp_socket (name_bind name_connect))) (allow any_p port_t (tcp_socket (name_bind name_connect))) (allow any_p port_t (udp_socket (name_bind))) (allow any_p port_t (rawip_socket (name_bind))) (allow any_p node_t (dccp_socket (node_bind))) (allow any_p node_t (tcp_socket (node_bind))) (allow any_p node_t (udp_socket (node_bind))) (allow any_p node_t (rawip_socket (node_bind))) (allow any_p node_t (sctp_socket (node_bind))) (allow any_p node_t (icmp_socket (node_bind))) (allow any_p netif_t (netif (egress ingress))) ; Network sockets, except relabeling (allow any_p any_p (socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (dccp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (tcp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (udp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (rawip_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (packet_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (key_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (appletalk_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (tun_socket ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write attach_queue ))) (allow any_p any_p (sctp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (icmp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (ax25_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (ipx_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (netrom_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (atmpvc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (x25_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (rose_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (decnet_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (atmsvc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (rds_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (irda_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (pppox_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (llc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (can_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (tipc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (bluetooth_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (iucv_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (rxrpc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (isdn_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (phonet_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (ieee802154_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (caif_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (alg_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (nfc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (vsock_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (kcm_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (qipcrtr_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (smc_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p any_p (xdp_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) ; UDS within any domain is always allowed (allow any_p self (unix_stream_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) (allow any_p self (unix_dgram_socket (accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom sendto setattr setopt shutdown write))) ; Netlink (allow any_p self (netlink_classes (full))) (allow system_p any_p (netlink_classes (full))) ; IPsec (allow any_p self (association (sendto recvfrom setcontext polmatch))) (allow any_p unlabeled_t (association (sendto recvfrom setcontext polmatch))) ; InfiniBand (allow any_p unlabeled_t (infiniband_pkey (access))) (allow any_p unlabeled_t (infiniband_endport (manage_subnet))) ================================================ FILE: internal/pkg/selinux/policy/selinux/common/processes.cil ================================================ ; Query procfs about self, plus OOM adj and similar writes (controlled by other access control and caps) ; Also FIFO/socket writes for own stuff (allow any_p self (fs_classes (rw))) ; Read process info (allow any_p procfs_t (fs_classes (ro))) ; Allow all process actions but ptrace, set* will be guarded by transitions (allow any_p self (process_classes (full))) ; Read various parameters like hugepages (allow any_p sysfs_t (fs_classes (ro))) (allow any_p proc_cmdline_t (fs_classes (ro))) ; Pseudo devices (allow any_p null_device_t (fs_classes (rw))) ; All caps, except sys_boot and sys_modules (allow any_p self (capability ( audit_control audit_write chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill lease linux_immutable mknod net_admin net_bind_service net_broadcast net_raw setfcap setgid setpcap setuid sys_admin sys_chroot sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config ))) (allow any_p self (cap_userns ( audit_control audit_write chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill lease linux_immutable mknod net_admin net_bind_service net_broadcast net_raw setfcap setgid setpcap setuid sys_admin sys_chroot sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config ))) ; All but mac_admin, mac_override and syslog (allow any_p self (capability2 ( audit_read block_suspend bpf checkpoint_restore perfmon wake_alarm ))) (allow any_p self (cap2_userns ( audit_read block_suspend bpf checkpoint_restore perfmon wake_alarm ))) ; Enable (e)BPF for all processes (allow any_p self (bpf (map_create map_read map_write prog_load prog_run))) ; Allow init to manage processes (allow init_t service_p (fs_classes (rw))) (allow init_t service_p (process_classes (full))) ; kernel cmdline (allow system_p proc_cmdline_t (fs_classes (ro))) (allow system_container_p proc_cmdline_t (fs_classes (ro))) ; These only run binaries from the squashfs so this shouldn't do harm. ; TODO: eliminate such common permissions (allow system_p any_f (fs_classes (ro))) (allow system_container_p any_f (fs_classes (ro))) ; By default, allow any process to access any device except special ones (allow any_p common_device_f (fs_classes (rw))) ; CNI, containerd, many different services read and write sysctl parameters (allow any_p proc_sysctl_t (fs_classes (rw))) ; Unconfined FS and files (allow any_p unconfined_f (fs_classes (rw))) ; Kernel threads can access anything (allow kernel_t any_f (fs_classes (rw))) ; Own sockets (allow any_p self (unix_stream_socket (connectto))) ; allow reading cgroup info (allow any_p cgroup_t (fs_classes (ro))) ; ; cilium-agent ; (allow any_p self (perf_event (all))) ; (allow any_p bpf_t (fs_classes (rw))) ; fio (allow any_p self (shm (all))) ; /run/flannel, created by containerd (allow any_p pod_containerd_run_t (fs_classes (rw))) ; Mayastor (allow any_p hugetlbfs_t (fs_classes (rw))) ; ceph-osd accesses io_uring (allow any_p self (anon_inode (map read write create))) ; ; cilium lock, socket and misc runtime files ; (allow any_p run_t (fs_classes (rw))) ; time data, LVM config (allow any_p etc_t (fs_classes (ro))) ; ; OpenEBS manages volumes, ceph ; (allow any_p sysfs_t (fs_classes (rw))) ; (allow any_p udev_run_t (fs_classes (ro ioctl))) ; ; pods also run CNI, LVM and other host binaries ; (allow any_p bin_exec_t (fs_classes (ro))) ; (allow pod_p bin_exec_t (file (execute execute_no_trans))) ; pf-2-proto-262-type-1 ; refpolicy allows it for spc_t (allow any_p kernel_t (system (module_request))) ; debug transitions: ; (auditallow any_f_any_p any_f_any_p (process (dyntransition fork ptrace setcurrent setexec setfscreate setkeycreate transition))) ; (auditallow any_f_any_p any_f_any_p (process2 (nnp_transition nosuid_transition))) ; (auditallow any_f_any_p any_f_any_p (file (entrypoint execute execute_no_trans))) ; HACK: this happens in a process cloned from containerd for running CNI: ; [ 77.042515] audit: type=1400 audit(1741862615.446:4227): avc: granted { execute } for pid=3136 comm="containerd" name="loopback" dev="loop0" ino=68 scontext=system_u:system_r:init_t:s0 tcontext=system_u:object_r:cni_plugin_t:s0 tclass=file ; [ 77.043500] audit: type=1400 audit(1741862615.446:4227): avc: granted { execute } for pid=3136 comm="containerd" name="loopback" dev="overlay" ino=68 scontext=system_u:system_r:pod_containerd_t:s0 tcontext=system_u:object_r:cni_plugin_t:s0 tclass=file ; [ 77.044528] audit: type=1400 audit(1741862615.446:4227): avc: granted { execute_no_trans } for pid=3136 comm="containerd" path="/opt/cni/bin/loopback" dev="overlay" ino=68 scontext=system_u:system_r:pod_containerd_t:s0 tcontext=system_u:object_r:cni_plugin_t:s0 tclass=file ; [ 77.045739] audit: type=1400 audit(1741862615.446:4227): avc: granted { execute } for pid=3136 comm="loopback" path="/opt/cni/bin/loopback" dev="overlay" ino=68 scontext=system_u:system_r:pod_containerd_t:s0 tcontext=system_u:object_r:cni_plugin_t:s0 tclass=file ; This is being researched as a kernel bug since a process gains init_t context without the policy allowing it. (allow init_t cni_plugin_t (file (execute))) ; Let ls and other path walking work, esp. for tests (allow any_p any_f (dir (search))) (allow any_p any_f (fs_classes (getattr))) ================================================ FILE: internal/pkg/selinux/policy/selinux/common/typeattributes.cil ================================================ (typeattribute common_f) (macro common_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset common_f ARG1) ) (typeattribute protected_f) (macro protected_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset protected_f ARG1) ) (typeattribute system_f) (macro system_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset system_f ARG1) ) (typeattribute system_socket_f) (macro system_socket_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset system_socket_f ARG1) ) (allow system_socket_f tmpfs_t (filesystem (associate))) (typeattribute common_device_f) (macro common_device_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset common_device_f ARG1) ) (typeattribute protected_device_f) (macro protected_device_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset protected_device_f ARG1) ) (typeattributeset common_device_f device_t) (typeattribute device_f) (typeattributeset device_f common_device_f) (typeattributeset device_f protected_device_f) (typeattribute any_f) (typeattributeset any_f filesystem_f) (typeattributeset any_f common_f) (typeattributeset any_f protected_f) (typeattributeset any_f system_f) (typeattributeset any_f system_socket_f) (typeattributeset any_f device_f) (typeattribute unconfined_f) (typeattributeset unconfined_f tmpfs_t) (typeattributeset unconfined_f common_f) (typeattributeset unconfined_f common_device_f) (typeattribute filesystem_f) (macro filesystem_f ((type ARG1)) (roletype object_r ARG1) (typeattributeset filesystem_f ARG1) ) (allow filesystem_f self (filesystem (associate))) (allow any_f fs_t (filesystem (associate))) (allow filesystem_f fs_t (filesystem (associate))) (typeattribute service_exec_f) (typeattribute service_p) ; (process_t, exec_t) (macro service_p ((type process_label) (type executable_label)) (roletype system_r process_label) (typeattributeset service_p process_label) (typeattributeset service_exec_f executable_label) (allow process_label executable_label (fs_classes (ro))) (allow process_label executable_label (file (entrypoint execute execute_no_trans))) ) (typeattribute system_container_p) (macro system_container_p ((type ARG1)) (roletype system_r ARG1) (typeattributeset system_container_p ARG1) ) (typeattribute pod_p) (macro pod_p ((type ARG1)) (roletype system_r ARG1) (typeattributeset pod_p ARG1) ) (typeattribute system_p) (typeattributeset system_p kernel_t) (typeattributeset system_p initramfs_t) (typeattributeset system_p init_t) (typeattributeset system_p service_p) (typeattribute any_p) (typeattributeset any_p system_p) (typeattributeset any_p system_container_p) (typeattributeset any_p pod_p) (typeattribute any_f_any_p) (typeattributeset any_f_any_p any_f) (typeattributeset any_f_any_p any_p) ================================================ FILE: internal/pkg/selinux/policy/selinux/immutable/classes.cil ================================================ (class security ( check_context compute_av compute_create compute_member compute_relabel compute_user load_policy read_policy setbool setcheckreqprot setenforce setsecparam validate_trans )) (class filesystem ( associate getattr mount quotaget quotamod relabelfrom relabelto remount unmount watch )) (common file_common ( append audit_access create execmod execute getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto rename setattr unlink watch watch_mount watch_reads watch_sb watch_with_perm write )) (class file (entrypoint execute_no_trans)) (classcommon file file_common) (class dir (add_name remove_name reparent rmdir search)) (classcommon dir file_common) (class lnk_file ()) (classcommon lnk_file file_common) (class chr_file ()) (classcommon chr_file file_common) (class blk_file ()) (classcommon blk_file file_common) (class sock_file ()) (classcommon sock_file file_common) (class fifo_file ()) (classcommon fifo_file file_common) (common socket_common ( accept append bind connect create getattr getopt ioctl listen lock map name_bind read recvfrom relabelfrom relabelto sendto setattr setopt shutdown write )) (class socket ()) (classcommon socket socket_common) (class tcp_socket (name_connect node_bind)) (classcommon tcp_socket socket_common) (class udp_socket (node_bind)) (classcommon udp_socket socket_common) (class rawip_socket (node_bind)) (classcommon rawip_socket socket_common) (class packet_socket ()) (classcommon packet_socket socket_common) (class key_socket ()) (classcommon key_socket socket_common) (class unix_stream_socket (connectto)) (classcommon unix_stream_socket socket_common) (class unix_dgram_socket ()) (classcommon unix_dgram_socket socket_common) (class netif (egress ingress)) (class process ( dyntransition execheap execmem execstack fork getattr getcap getpgid getrlimit getsched getsession noatsecure ptrace rlimitinh setcap setcurrent setexec setfscreate setkeycreate setpgid setrlimit setsched setsockcreate share sigchld siginh sigkill signal signull sigstop transition )) (class system ( ipc_info module_load module_request syslog_console syslog_mod syslog_read firmware_load kexec_image_load kexec_initramfs_load )) (class process2 (nnp_transition nosuid_transition)) (class fd (use)) (class node (recvfrom sendto)) (class bpf (map_create map_read map_write prog_load prog_run)) (common capability_common ( audit_control audit_write chown dac_override dac_read_search fowner fsetid ipc_lock ipc_owner kill lease linux_immutable mknod net_admin net_bind_service net_broadcast net_raw setfcap setgid setpcap setuid sys_admin sys_boot sys_chroot sys_module sys_nice sys_pacct sys_ptrace sys_rawio sys_resource sys_time sys_tty_config )) (class capability ()) (class cap_userns ()) (classcommon capability capability_common) (classcommon cap_userns capability_common) (common capability2_common ( audit_read block_suspend bpf checkpoint_restore mac_admin mac_override perfmon syslog wake_alarm )) (class capability2 ()) (class cap2_userns ()) (classcommon capability2 capability2_common) (classcommon cap2_userns capability2_common) (class netlink_socket ()) (classcommon netlink_socket socket_common) (class netlink_route_socket (nlmsg_read nlmsg_write)) (classcommon netlink_route_socket socket_common) (class netlink_tcpdiag_socket (nlmsg_read nlmsg_write)) (classcommon netlink_tcpdiag_socket socket_common) (class netlink_nflog_socket ()) (classcommon netlink_nflog_socket socket_common) (class netlink_selinux_socket ()) (classcommon netlink_selinux_socket socket_common) (class netlink_audit_socket ( nlmsg_read nlmsg_readpriv nlmsg_relay nlmsg_tty_audit nlmsg_write )) (classcommon netlink_audit_socket socket_common) (class netlink_dnrt_socket ()) (classcommon netlink_dnrt_socket socket_common) (class netlink_kobject_uevent_socket ()) (classcommon netlink_kobject_uevent_socket socket_common) (class netlink_iscsi_socket ()) (classcommon netlink_iscsi_socket socket_common) (class netlink_fib_lookup_socket ()) (classcommon netlink_fib_lookup_socket socket_common) (class netlink_connector_socket ()) (classcommon netlink_connector_socket socket_common) (class netlink_netfilter_socket ()) (classcommon netlink_netfilter_socket socket_common) (class netlink_generic_socket ()) (classcommon netlink_generic_socket socket_common) (class netlink_scsitransport_socket ()) (classcommon netlink_scsitransport_socket socket_common) (class netlink_rdma_socket ()) (classcommon netlink_rdma_socket socket_common) (class netlink_crypto_socket ()) (classcommon netlink_crypto_socket socket_common) (class netlink_xfrm_socket (nlmsg_read nlmsg_write)) (classcommon netlink_xfrm_socket socket_common) (class obsolete_netlink_firewall_socket (nlmsg_read nlmsg_write)) (classcommon obsolete_netlink_firewall_socket socket_common) (class obsolete_netlink_ip6fw_socket (nlmsg_read nlmsg_write)) (classcommon obsolete_netlink_ip6fw_socket socket_common) (class key (view read write search link setattr create)) (common ipc_common ( associate create destroy getattr read setattr unix_read unix_write write )) ; Deprecated (class ipc ()) (classcommon ipc ipc_common) (class sem ()) (classcommon sem ipc_common) (class msgq (enqueue)) (classcommon msgq ipc_common) (class msg (send receive)) (classcommon msg ipc_common) (class shm (lock)) (classcommon shm ipc_common) (class appletalk_socket ()) (classcommon appletalk_socket socket_common) (class packet (send recv relabelto forward_in forward_out)) (class association (sendto recvfrom setcontext polmatch)) (class dccp_socket (node_bind name_connect)) (classcommon dccp_socket socket_common) (class memprotect (mmap_zero)) (class peer (recv)) (class kernel_service (use_as_override create_files_as)) (class tun_socket (attach_queue)) (classcommon tun_socket socket_common) (class binder (impersonate call set_context_mgr transfer)) (class infiniband_pkey (access)) (class infiniband_endport (manage_subnet)) (class sctp_socket (node_bind name_connect association)) (classcommon sctp_socket socket_common) (class icmp_socket (node_bind)) (classcommon icmp_socket socket_common) (class ax25_socket ()) (classcommon ax25_socket socket_common) (class ipx_socket ()) (classcommon ipx_socket socket_common) (class netrom_socket ()) (classcommon netrom_socket socket_common) (class atmpvc_socket ()) (classcommon atmpvc_socket socket_common) (class x25_socket ()) (classcommon x25_socket socket_common) (class rose_socket ()) (classcommon rose_socket socket_common) (class decnet_socket ()) (classcommon decnet_socket socket_common) (class atmsvc_socket ()) (classcommon atmsvc_socket socket_common) (class rds_socket ()) (classcommon rds_socket socket_common) (class irda_socket ()) (classcommon irda_socket socket_common) (class pppox_socket ()) (classcommon pppox_socket socket_common) (class llc_socket ()) (classcommon llc_socket socket_common) (class can_socket ()) (classcommon can_socket socket_common) (class tipc_socket ()) (classcommon tipc_socket socket_common) (class bluetooth_socket ()) (classcommon bluetooth_socket socket_common) (class iucv_socket ()) (classcommon iucv_socket socket_common) (class rxrpc_socket ()) (classcommon rxrpc_socket socket_common) (class isdn_socket ()) (classcommon isdn_socket socket_common) (class phonet_socket ()) (classcommon phonet_socket socket_common) (class ieee802154_socket ()) (classcommon ieee802154_socket socket_common) (class caif_socket ()) (classcommon caif_socket socket_common) (class alg_socket ()) (classcommon alg_socket socket_common) (class nfc_socket ()) (classcommon nfc_socket socket_common) (class vsock_socket ()) (classcommon vsock_socket socket_common) (class kcm_socket ()) (classcommon kcm_socket socket_common) (class qipcrtr_socket ()) (classcommon qipcrtr_socket socket_common) (class smc_socket ()) (classcommon smc_socket socket_common) (class xdp_socket ()) (classcommon xdp_socket socket_common) (class mctp_socket ()) (classcommon mctp_socket socket_common) (class perf_event (open cpu kernel tracepoint read write)) ; Deprecated in 5.16, no longer checked by kernel (class lockdown (integrity confidentiality)) (class anon_inode ()) (classcommon anon_inode file_common) (class io_uring (override_creds sqpoll cmd)) (class user_namespace (create)) (classorder ( security process system capability filesystem file dir fd lnk_file chr_file blk_file sock_file fifo_file socket tcp_socket udp_socket rawip_socket node netif netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket sem msg msgq shm ipc netlink_route_socket obsolete_netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket obsolete_netlink_ip6fw_socket netlink_dnrt_socket association netlink_kobject_uevent_socket appletalk_socket packet key dccp_socket memprotect peer capability2 kernel_service tun_socket binder netlink_iscsi_socket netlink_fib_lookup_socket netlink_connector_socket netlink_netfilter_socket netlink_generic_socket netlink_scsitransport_socket netlink_rdma_socket netlink_crypto_socket infiniband_pkey infiniband_endport cap_userns cap2_userns sctp_socket icmp_socket ax25_socket ipx_socket netrom_socket atmpvc_socket x25_socket rose_socket decnet_socket atmsvc_socket rds_socket irda_socket pppox_socket llc_socket can_socket tipc_socket bluetooth_socket iucv_socket rxrpc_socket isdn_socket phonet_socket ieee802154_socket caif_socket alg_socket nfc_socket vsock_socket kcm_socket qipcrtr_socket smc_socket process2 bpf xdp_socket mctp_socket perf_event lockdown anon_inode io_uring user_namespace )) ================================================ FILE: internal/pkg/selinux/policy/selinux/immutable/fs.cil ================================================ (type devpts_t) (call filesystem_f (devpts_t)) (fsuse trans devpts (system_u object_r devpts_t (systemLow systemLow))) (type tmpfs_t) (call filesystem_f (tmpfs_t)) (fsuse trans tmpfs (system_u object_r tmpfs_t (systemLow systemLow))) (fsuse trans shm (system_u object_r tmpfs_t (systemLow systemLow))) (fsuse trans mqueue (system_u object_r tmpfs_t (systemLow systemLow))) (type ramfs_t) (call filesystem_f (ramfs_t)) (fsuse trans ramfs (system_u object_r ramfs_t (systemLow systemLow))) (type hugetlbfs_t) (call filesystem_f (hugetlbfs_t)) (fsuse trans hugetlbfs (system_u object_r hugetlbfs_t (systemLow systemLow))) (type rootfs_t) (call filesystem_f (rootfs_t)) (genfscon rootfs "/" (system_u object_r rootfs_t (systemLow systemLow))) (filecon "/" any (system_u object_r rootfs_t (systemLow systemLow))) (type sysfs_t) (call filesystem_f (sysfs_t)) (type sys_module_t) (call filesystem_f (sys_module_t)) (allow sys_module_t sysfs_t (filesystem (associate))) (genfscon sysfs "/" (system_u object_r sysfs_t (systemLow systemLow))) (genfscon sysfs "/module" (system_u object_r sys_module_t (systemLow systemLow))) (type bpf_t) (call filesystem_f (bpf_t)) (genfscon bpf "/" (system_u object_r bpf_t (systemLow systemLow))) (type debugfs_t) (call filesystem_f (debugfs_t)) (genfscon debugfs "/" (system_u object_r debugfs_t (systemLow systemLow))) (type cgroup_t) (call filesystem_f (cgroup_t)) (genfscon cgroup "/" (system_u object_r cgroup_t (systemLow systemLow))) (genfscon cgroup2 "/" (system_u object_r cgroup_t (systemLow systemLow))) (type selinuxfs_t) (call filesystem_f (selinuxfs_t)) (genfscon selinuxfs "/" (system_u object_r selinuxfs_t (systemLow systemLow))) (type procfs_t) (call filesystem_f (procfs_t)) (context procfs_t (system_u object_r procfs_t (systemLow systemLow))) (genfscon proc "/" procfs_t) (genfscon proc "/sysvipc" procfs_t) (type proc_cmdline_t) (call filesystem_f (proc_cmdline_t)) (genfscon proc "/cmdline" (system_u object_r proc_cmdline_t (systemLow systemLow))) (type proc_sysctl_t) (call filesystem_f (proc_sysctl_t)) (genfscon proc "/sys" (system_u object_r proc_sysctl_t (systemLow systemLow))) ; It matches /sys, yet should not have the same context (genfscon proc "/sysrq-trigger" procfs_t) (type securityfs_t) (call filesystem_f (securityfs_t)) (genfscon securityfs "/" (system_u object_r securityfs_t (systemLow systemLow))) (type tracefs_t) (call filesystem_f (tracefs_t)) (genfscon tracefs "/" (system_u object_r tracefs_t (systemLow systemLow))) (type efivarfs_t) (call filesystem_f (efivarfs_t)) (genfscon efivarfs "/" (system_u object_r efivarfs_t (systemLow systemLow))) (type anon_inodefs_t) (call filesystem_f (anon_inodefs_t)) (genfscon anon_inodefs "/" (system_u object_r anon_inodefs_t (systemLow systemLow))) (type binfmt_misc_t) (call filesystem_f (binfmt_misc_t)) (genfscon binfmt_misc "/" (system_u object_r binfmt_misc_t (systemLow systemLow))) (type bdev_t) (call filesystem_f (bdev_t)) (genfscon bdev "/" (system_u object_r bdev_t (systemLow systemLow))) (type autofs_t) (call filesystem_f (autofs_t)) (context autofs_t (system_u object_r autofs_t (systemLow systemLow))) (genfscon autofs "/" autofs_t) (genfscon automount "/" autofs_t) (type fuse_fs_t) (call filesystem_f (fuse_fs_t)) (context fuse_fs_t (system_u object_r fuse_fs_t (systemLow systemLow))) (genfscon fuse "/" fuse_fs_t) (genfscon fuseblk "/" fuse_fs_t) (genfscon fusectl "/" fuse_fs_t) (type foreign_fs_t) (call filesystem_f (foreign_fs_t)) (context foreign_fs_t (system_u object_r foreign_fs_t (systemLow systemLow))) (genfscon fat "/" foreign_fs_t) (genfscon hfs "/" foreign_fs_t) (genfscon hfsplus "/" foreign_fs_t) (genfscon msdos "/" foreign_fs_t) (genfscon ntfs "/" foreign_fs_t) (genfscon ntfs-3g "/" foreign_fs_t) (genfscon vfat "/" foreign_fs_t) (type iso_fs_t) (call filesystem_f (iso_fs_t)) (context iso_fs_t (system_u object_r iso_fs_t (systemLow systemLow))) (genfscon iso9660 "/" iso_fs_t) (genfscon udf "/" iso_fs_t) (type sysv_fs_t) (call filesystem_f (sysv_fs_t)) (context sysv_fs_t (system_u object_r sysv_fs_t (systemLow systemLow))) (genfscon sysv "/" sysv_fs_t) (genfscon v7 "/" sysv_fs_t) (type network_fs_t) (call filesystem_f (network_fs_t)) (context network_fs_t (system_u object_r network_fs_t (systemLow systemLow))) (genfscon afs "/" network_fs_t) (genfscon cifs "/" network_fs_t) (genfscon coda "/" network_fs_t) (genfscon dazukofs "/" network_fs_t) (genfscon lustre "/" network_fs_t) (genfscon ncpfs "/" network_fs_t) (genfscon nfs "/" network_fs_t) (genfscon nfs4 "/" network_fs_t) (genfscon panfs "/" network_fs_t) (genfscon smbfs "/" network_fs_t) (type vmblock_fs_t) (call filesystem_f (vmblock_fs_t)) (context vmblock_fs_t (system_u object_r vmblock_fs_t (systemLow systemLow))) (genfscon vboxsf "/" vmblock_fs_t) (genfscon virtiofs "/" vmblock_fs_t) (genfscon vmblock "/" vmblock_fs_t) (genfscon vmhgfs "/" vmblock_fs_t) (genfscon xenfs "/" vmblock_fs_t) (type configfs_t) (call filesystem_f (configfs_t)) (genfscon configfs "/" (system_u object_r configfs_t (systemLow systemLow))) (type futexfs_t) (call filesystem_f (futexfs_t)) (genfscon futexfs "/" (system_u object_r futexfs_t (systemLow systemLow))) (type infinibandeventfs_t) (call filesystem_f (infinibandeventfs_t)) (genfscon infinibandeventfs "/" (system_u object_r infinibandeventfs_t (systemLow systemLow))) (type inotifyfs_t) (call filesystem_f (inotifyfs_t)) (genfscon inotifyfs "/" (system_u object_r inotifyfs_t (systemLow systemLow))) (type nfsd_t) (call filesystem_f (nfsd_t)) (genfscon nfsd "/" (system_u object_r nfsd_t (systemLow systemLow))) (type nsfs_t) (call filesystem_f (nsfs_t)) (genfscon nsfs "/" (system_u object_r nsfs_t (systemLow systemLow))) (type pstore_t) (call filesystem_f (pstore_t)) (genfscon pstore "/" (system_u object_r pstore_t (systemLow systemLow))) (type rpc_pipefs_t) (call filesystem_f (rpc_pipefs_t)) (genfscon rpc_pipefs "/" (system_u object_r rpc_pipefs_t (systemLow systemLow))) (type usbfs_t) (call filesystem_f (usbfs_t)) (context usbfs_t (system_u object_r usbfs_t (systemLow systemLow))) (genfscon functionfs "/" usbfs_t) (genfscon gadgetfs "/" usbfs_t) (genfscon usbdevfs "/" usbfs_t) (genfscon usbfs "/" usbfs_t) (fsuse task sockfs (system_u object_r fs_t (systemLow systemLow))) (fsuse task pipefs (system_u object_r fs_t (systemLow systemLow))) (fsuse task eventpollfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr zfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr xfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr virtiofs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr ubifs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr squashfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr overlay (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr lustre (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr jfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr jffs2 (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr gpfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr gfs2 (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr gfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr f2fs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr ext4dev (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr ext4 (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr ext3 (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr ext2 (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr erofs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr encfs (system_u object_r fs_t (systemLow systemLow))) (fsuse xattr btrfs (system_u object_r fs_t (systemLow systemLow))) (type device_t) (call filesystem_f (device_t)) (fsuse trans devtmpfs (system_u object_r device_t (systemLow systemLow))) ; files on pseudo-FS's (allow any_p procfs_t (filesystem (associate))) (allow device_f device_t (filesystem (associate))) ================================================ FILE: internal/pkg/selinux/policy/selinux/immutable/preamble.cil ================================================ (sensitivity s0) (sensitivityorder (s0)) (level systemLow (s0)) (handleunknown deny) (mls true) (policycap open_perms) (policycap extended_socket_class) (policycap cgroup_seclabel) (policycap nnp_nosuid_transition) (policycap ioctl_skip_cloexec) (policycap userspace_initial_context) (policycap genfs_seclabel_symlinks) ================================================ FILE: internal/pkg/selinux/policy/selinux/immutable/roles.cil ================================================ (role object_r) (type object_r) (roletype object_r object_r) (role system_r) (type system_r) (roletype system_r system_r) (user system_u) (userrange system_u (systemLow systemLow)) (userlevel system_u systemLow) (userrole system_u object_r) (userrole system_u system_r) ================================================ FILE: internal/pkg/selinux/policy/selinux/immutable/sids.cil ================================================ (type null_device_t) (call common_device_f (null_device_t)) (sid devnull) (sidcontext devnull (system_u object_r null_device_t (systemLow systemLow))) (sid igmp_packet) (sidcontext igmp_packet (system_u object_r unlabeled_t (systemLow systemLow))) (sid icmp_socket) (sidcontext icmp_socket (system_u object_r unlabeled_t (systemLow systemLow))) (sid tcp_socket) (sidcontext tcp_socket (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_modprobe) (sidcontext sysctl_modprobe (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl) (sidcontext sysctl (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_fs) (sidcontext sysctl_fs (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_kernel) (sidcontext sysctl_kernel (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_net) (sidcontext sysctl_net (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_net_unix) (sidcontext sysctl_net_unix (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_vm) (sidcontext sysctl_vm (system_u object_r unlabeled_t (systemLow systemLow))) (sid sysctl_dev) (sidcontext sysctl_dev (system_u object_r unlabeled_t (systemLow systemLow))) (sid kmod) (sidcontext kmod (system_u object_r unlabeled_t (systemLow systemLow))) (sid policy) (sidcontext policy (system_u object_r unlabeled_t (systemLow systemLow))) (sid scmp_packet) (sidcontext scmp_packet (system_u object_r unlabeled_t (systemLow systemLow))) (type unlabeled_t) (call system_f (unlabeled_t)) (type fs_t) (call filesystem_f (fs_t)) (allow unlabeled_t fs_t (filesystem (associate))) (type node_t) (roletype object_r node_t) (sid node) (sidcontext node (system_u object_r node_t (systemLow systemLow))) (type netlabel_peer_t) (roletype object_r netlabel_peer_t) (sid netmsg) (sidcontext netmsg (system_u object_r netlabel_peer_t (systemLow systemLow))) (type netif_t) (roletype object_r netif_t) (sid netif) (sidcontext netif (system_u object_r netif_t (systemLow systemLow))) (type port_t) (roletype object_r port_t) (sid port) (sidcontext port (system_u object_r port_t (systemLow systemLow))) (sid any_socket) (sidcontext any_socket (system_u object_r unlabeled_t (systemLow systemLow))) (type initramfs_t) (roletype system_r initramfs_t) (sid init) (sidcontext init (system_u system_r initramfs_t (systemLow systemLow))) (sid file_labels) (sidcontext file_labels (system_u object_r unlabeled_t (systemLow systemLow))) (sid file) (sidcontext file (system_u object_r unlabeled_t (systemLow systemLow))) (sid fs) (sidcontext fs (system_u object_r fs_t (systemLow systemLow))) (sid unlabeled) (sidcontext unlabeled (system_u object_r unlabeled_t (systemLow systemLow))) (type security_t) (roletype object_r security_t) (sid security) (sidcontext security (system_u object_r security_t (systemLow systemLow))) (type kernel_t) (roletype system_r kernel_t) (sid kernel) (sidcontext kernel (system_u system_r kernel_t (systemLow systemLow))) (sidorder ( kernel security unlabeled fs file file_labels init any_socket port netif netmsg node igmp_packet icmp_socket tcp_socket sysctl_modprobe sysctl sysctl_fs sysctl_kernel sysctl_net sysctl_net_unix sysctl_vm sysctl_dev kmod policy scmp_packet devnull )) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/cri.cil ================================================ ; Pod (CRI) containerd (type pod_containerd_t) (call service_p (pod_containerd_t containerd_exec_t)) (type pod_containerd_socket_t) (call system_socket_f (pod_containerd_socket_t)) ; Shim and client sockets (allow pod_containerd_t run_t (fs_classes (rw))) (typeattribute not_pod_containerd_socket_t) (typeattributeset not_pod_containerd_socket_t (not pod_containerd_socket_t)) (typetransition pod_containerd_t not_pod_containerd_socket_t sock_file pod_containerd_socket_t) (type pod_containerd_run_t) (call system_f (pod_containerd_run_t)) (allow pod_containerd_run_t tmpfs_t (filesystem (associate))) (typetransition pod_containerd_t run_t file pod_containerd_run_t) (typetransition pod_containerd_t run_t dir pod_containerd_run_t) (typetransition pod_containerd_t run_t lnk_file pod_containerd_run_t) (typetransition pod_containerd_t run_t chr_file pod_containerd_run_t) (typetransition pod_containerd_t run_t blk_file pod_containerd_run_t) (typetransition pod_containerd_t run_t fifo_file pod_containerd_run_t) (allow pod_containerd_t pod_containerd_run_t (fs_classes (rw))) ; Transition to pod contexts (allow pod_containerd_t pod_p (process2 (nnp_transition nosuid_transition))) (allow pod_containerd_t pod_p (process (transition))) ; Containerd storage (allow pod_containerd_t containerd_state_t (fs_classes (rw))) (allow pod_containerd_t containerd_state_t (file (entrypoint execute execute_no_trans))) (allow pod_containerd_t containerd_state_t (fs_classes (relabelto relabelfrom))) ; Manage procfs & processes (allow pod_containerd_t pod_p (fs_classes (rw))) (allow pod_containerd_t pod_p (process_classes (full))) (allow pod_p pod_containerd_t (fd (use))) (allow pod_p pod_containerd_t (fifo_file (append getattr ioctl map open read rename setattr watch write))) ; CNI (allow pod_containerd_t cni_plugin_t (file (execute_no_trans execute))) (allow pod_containerd_t cni_plugin_t (fs_classes (ro))) ; iptables used by CNI (allow pod_containerd_t bin_exec_t (file (execute_no_trans execute))) (allow pod_containerd_t bin_exec_t (fs_classes (ro))) ; (allow pod_p cni_conf_t (fs_classes (rw))) (allow pod_containerd_t cni_state_t (fs_classes (rw))) ; Logs (allow pod_containerd_t pods_log_t (fs_classes (rw))) (allow pod_containerd_t pod_p (key (view read write search link setattr create))) ; Shim socket (allow pod_containerd_t pod_containerd_socket_t (fs_classes (rw))) ; System containers, not k8s pods (type etcd_t) (call pod_p (etcd_t)) ; FIXME: insecure as anyone with access to the pod containerd may obtain this domain (allow etcd_t containerd_state_t (file (entrypoint))) (type etcd_pki_t) (call protected_f (etcd_pki_t)) (allow etcd_pki_t tmpfs_t (filesystem (associate))) (allow etcd_t etcd_pki_t (fs_classes (ro))) (type etcd_data_t) (call protected_f (etcd_data_t)) (allow etcd_t etcd_data_t (fs_classes (rw))) (type cni_conf_t) (call filesystem_f (cni_conf_t)) (filecon "/etc/cni(/.*)?" any (system_u object_r cni_conf_t (systemLow systemLow))) (type cni_plugin_t) (call filesystem_f (cni_plugin_t)) (filecon "/opt/cni(/.*)?" any (system_u object_r cni_plugin_t (systemLow systemLow))) (type containerd_plugin_t) (call filesystem_f (containerd_plugin_t)) (filecon "/opt/containerd(/.*)?" any (system_u object_r containerd_plugin_t (systemLow systemLow))) (type containerd_state_t) (call common_f (containerd_state_t)) (type kube_apiserver_config_t) (call protected_f (kube_apiserver_config_t)) (allow kube_apiserver_config_t tmpfs_t (filesystem (associate))) (type kube_scheduler_config_t) (call protected_f (kube_scheduler_config_t)) (allow kube_scheduler_config_t tmpfs_t (filesystem (associate))) (type kube_apiserver_secret_t) (call protected_f (kube_apiserver_secret_t)) (allow kube_apiserver_secret_t tmpfs_t (filesystem (associate))) (type kube_controller_manager_secret_t) (call protected_f (kube_controller_manager_secret_t)) (allow kube_controller_manager_secret_t tmpfs_t (filesystem (associate))) (type kube_scheduler_secret_t) (call protected_f (kube_scheduler_secret_t)) (allow kube_scheduler_secret_t tmpfs_t (filesystem (associate))) (typeattribute kube_secret_f) (typeattributeset kube_secret_f kube_apiserver_config_t) (typeattributeset kube_secret_f kube_scheduler_config_t) (typeattributeset kube_secret_f kube_apiserver_secret_t) (typeattributeset kube_secret_f kube_controller_manager_secret_t) (typeattributeset kube_secret_f kube_scheduler_secret_t) ; Allow kubernetes pods to access their configuration and secrets ; FIXME: insecure as anyone with access to the pod containerd may obtain these domains ; TODO: label static pods with the correct domains (allow pod_p kube_secret_f (fs_classes (ro))) (type var_log_t) (call protected_f (var_log_t)) (type audit_log_t) (call protected_f (audit_log_t)) (type containers_log_t) (call protected_f (containers_log_t)) (type pods_log_t) (call protected_f (pods_log_t)) (type var_lock_t) (call protected_f (var_lock_t)) (allow var_lock_t tmpfs_t (filesystem (associate))) (type seccomp_profile_t) (call protected_f (seccomp_profile_t)) (type kube_log_t) (call protected_f (kube_log_t)) (typeattribute log_f) (typeattributeset log_f var_log_t) (typeattributeset log_f audit_log_t) (typeattributeset log_f containers_log_t) (typeattributeset log_f pods_log_t) (typeattributeset log_f var_lock_t) (typeattributeset log_f seccomp_profile_t) (typeattributeset log_f kube_log_t) ; FIXME: insecure as anyone with access to the pod containerd may obtain these domains ; TODO: label static pods with the correct domains (allow pod_p log_f (fs_classes (rw))) (type cni_state_t) (call protected_f (cni_state_t)) ; All pods ; local-path-provisioner (allow pod_containerd_t ephemeral_t (fs_classes (rw))) (allow pod_p ephemeral_t (fs_classes (rw))) ; userns mode ; https://github.com/containerd/containerd/blob/9b552d441582d71b7fd322c0b9f2c7c23b26b729/pkg/sys/unshare_linux.go#L59 ; https://github.com/containerd/containerd/blob/9b552d441582d71b7fd322c0b9f2c7c23b26b729/core/mount/mount_idmapped_utils_linux.go#L51 (allow pod_containerd_t self (process (ptrace))) (allow pod_containerd_t self (user_namespace (create))) ; Manage pseudo-terminals for containers, Kubernetes Conformance needs this (allow pod_containerd_t devpts_t (fs_classes (rw))) (allow pod_p devpts_t (fs_classes (rw))) ; ; cilium ; (allow pod_containerd_t pod_p (unix_stream_socket (connectto))) ; pkg/flannel (allow pod_p cni_conf_t (fs_classes (rw))) ; Common rules for containerd accessing system (typeattributeset containerd_p pod_containerd_t) (type pod_t) (call pod_p (pod_t)) (typeattribute not_containerd_exec_t) (typeattributeset not_containerd_exec_t (not containerd_exec_t)) (typetransition pod_containerd_t containerd_state_t process pod_t) (allow pod_p containerd_state_t (fs_classes (rw))) (allow pod_p containerd_state_t (file (entrypoint execute execute_no_trans))) ; ; allow configuring CNI ; (allow pod_p cni_conf_t (fs_classes (rw))) ; ; CNI plugins access netns ; (allow pod_p nsfs_t (fs_classes (rw))) ; Flannel calls modprobe (allow pod_p module_t (fs_classes (ro))) ; emptyDir volumes, used e.g. by hydrophone cts ; TODO: label (allow pod_p kubelet_state_t (fs_classes (rw))) ; getattr, unmount called by containers and should be guarded by their mount namespaces (allow pod_p fs_t (filesystem ( ; associate getattr mount quotaget quotamod ; relabelfrom ; relabelto remount unmount watch ))) ; Calico and basically any CNI installing plugins (allow pod_t cni_plugin_t (fs_classes (rw))) ; `install` also seems to test the plugin (allow pod_t cni_plugin_t (file (execute execute_no_trans))) ; TinK is an example of what needs this. Docker in a pod will also require this. (allow pod_p cgroup_t (fs_classes (rw))) (allow pod_p nsfs_t (fs_classes (ro))) (allow pod_p self (key (all))) (allow pod_p self (capability2 (syslog))) (allow pod_p procfs_t (filesystem (mount remount unmount))) (allow pod_p kernel_t (system (syslog_read))) ; Mainly ephemeral, but basically run anything without transitioning (allow pod_p any_f (file (execute execute_no_trans))) ; /sys/firmware (allow pod_p sysfs_t (filesystem (mount remount unmount))) (allow pod_p sysfs_t (fs_classes (mounton))) ; TinK (allow pod_p sysfs_t (filesystem (mount remount unmount))) (allow pod_p nsfs_t (filesystem (mount remount unmount))) (allow pod_p devpts_t (filesystem (mount remount unmount))) ; /proc/bus (allow pod_p procfs_t (filesystem (mount remount unmount))) (allow pod_p procfs_t (fs_classes (mounton))) ; module params (used e.g. by containerd) (allow pod_p sys_module_t (fs_classes (ro))) ; TinK ; [ 110.873621] audit: type=1400 audit(1742647858.928:213): avc: denied { relabelfrom } for pid=4732 comm="containerd" name="etc" dev="vda4" ino=122484 scontext=system_u:system_r:pod_t:s0 tcontext=system_u:object_r:ephemeral_t:s0 tclass=dir permissive=1 ; [ 110.875762] audit: type=1400 audit(1742647858.928:213): avc: denied { relabelto } for pid=4732 comm="containerd" name="etc" dev="vda4" ino=122484 scontext=system_u:system_r:pod_t:s0 tcontext=system_u:object_r:ephemeral_t:s0 tclass=dir permissive=1 (allow pod_p ephemeral_t (fs_classes (relabelfrom relabelto))) ; CRI containerd should talk machined API to call machine.ImageService/Verify API (allow pod_containerd_t machine_socket_t (fs_classes (rw))) (allow pod_containerd_t init_t (unix_stream_socket (connectto))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/dashboard.cil ================================================ (type dashboard_t) (call service_p (dashboard_t init_exec_t)) ; TTY (allow dashboard_t device_t (fs_classes (rw))) ; machine ID and similar (allow dashboard_t etc_t (fs_classes (ro))) ; socket (allow dashboard_t machine_socket_t (fs_classes (rw))) (allow dashboard_t init_t (unix_stream_socket (connectto))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/kubelet.cil ================================================ (type kubelet_t) (call pod_p (kubelet_t)) ; FIXME: insecure as anyone with access to the pod containerd may obtain this domain (allow kubelet_t containerd_state_t (file (entrypoint execute execute_no_trans))) (type k8s_conf_t) (call system_f (k8s_conf_t)) (filecon "/etc/kubernetes(/.*)?" any (system_u object_r k8s_conf_t (systemLow systemLow))) (type k8s_plugin_t) (call system_f (k8s_plugin_t)) (filecon "/usr/libexec/kubernetes(/.*)?" any (system_u object_r k8s_plugin_t (systemLow systemLow))) (type k8s_credentialproviders_t) (call system_f (k8s_credentialproviders_t)) (filecon "/usr/local/lib/kubelet/credentialproviders(/.*)?" any (system_u object_r k8s_credentialproviders_t (systemLow systemLow))) (type kubelet_state_t) (call system_f (kubelet_state_t)) (allow init_t kubelet_state_t (fs_classes (rw))) (allow pod_containerd_t kubelet_state_t (fs_classes (rw))) ; FIXME: insecure as anyone with access to the pod containerd may obtain this domain ; TODO: Secure kubelet launch labeling ; Manage processes & cgroups (allow kubelet_t cgroup_t (fs_classes (rw))) (allow kubelet_t any_p (fs_classes (ro))) ; D-Bus for notifying kubelet of system power events (allow kubelet_t dbus_client_socket_t (fs_classes (rw))) ; Kubelet reads many attrs, handles mounts (allow kubelet_t fs_t (filesystem (getattr remount))) (allow kubelet_t any_f (fs_classes (getattr))) ; Config and state access (allow kubelet_t etc_t (fs_classes (ro))) (allow kubelet_t k8s_conf_t (fs_classes (rw))) (allow kubelet_t kubelet_state_t (fs_classes (rw))) ; Some mount/umount calls, xtables lock ; TODO: label more precisely? (allow kubelet_t run_t (fs_classes (rw))) ; Communication with init via pipes and sockets (allow kubelet_t init_t (fd (use))) (allow kubelet_t init_t (fifo_file (write))) (allow kubelet_t init_t (unix_stream_socket (connectto))) ; Run pods (allow kubelet_t pod_containerd_socket_t (fs_classes (rw))) (allow kubelet_t pod_containerd_t (unix_stream_socket (connectto))) ; Miscelaneous admin permissions (allow kubelet_t securityfs_t (fs_classes (ro))) (allow kubelet_t self (capability2 (syslog))) (allow kubelet_t kernel_t (system (syslog_read))) ; Manage system configuration (allow kubelet_t sysfs_t (fs_classes (rw))) ; CNI config, /etc/hosts written by kubelet (allow pod_p kubelet_state_t (fs_classes (ro))) (allow kubelet_t k8s_plugin_t (fs_classes (rw))) (allow kubelet_t k8s_plugin_t (file (entrypoint execute execute_no_trans))) ; kubelet sends sigkill to pod processes (allow kubelet_t pod_p (process_classes (signal))) ; ; Used when Mayastor/OpenEBS is enabled ; (allow kubelet_t hugetlbfs_t (fs_classes (rw))) ; ; /registration/rook-ceph.cephfs.csi.ceph.com-reg.sock and similar when using Rook-Ceph ; (allow kubelet_t pod_p (unix_stream_socket (connectto))) ; local-path-provisioner (allow kubelet_t ephemeral_t (fs_classes (rw))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/machined.cil ================================================ (type init_exec_t) (call system_f (init_exec_t)) (context init_exec_t (system_u object_r init_exec_t (systemLow systemLow))) (filecon "/usr/bin/init" file init_exec_t) (filecon "/usr/bin/poweroff" any init_exec_t) (filecon "/usr/bin/shutdown" any init_exec_t) (filecon "/usr/bin/dashboard" any init_exec_t) (type init_t) (roletype system_r init_t) (allow init_t init_exec_t (file (execute entrypoint))) ; System daemon sockets (type machine_socket_t) (call system_socket_f (machine_socket_t)) (type dbus_service_socket_t) (call system_socket_f (dbus_service_socket_t)) (type dbus_client_socket_t) (call system_socket_f (dbus_client_socket_t)) (allow init_t service_p (process (transition))) ; Manage processes (allow init_t any_p (fs_classes (rw))) (allow init_t any_p (process_classes (full))) (allow init_t service_exec_f (file (execute))) (allow init_t service_exec_f (fs_classes (ro))) (allow init_t service_p (key (view read write search link setattr create))) ; Libraries must also pass the exceutable check to be used ; Allow their use by all host services (type lib_t) (call system_f (lib_t)) (context lib_t (system_u object_r lib_t (systemLow systemLow))) (filecon "/usr/lib(/.*)?" any lib_t) (filecon "/usr/lib64" any lib_t) (filecon "/lib" any lib_t) (filecon "/lib64" any lib_t) (allow system_p lib_t (file (execute))) (allow system_p lib_t (fs_classes (ro))) ; Should not occur unless misconfigured by machined (type unconfined_service_t) (roletype system_r unconfined_service_t) (typeattributeset service_p unconfined_service_t) (type bin_exec_t) (call system_f (bin_exec_t)) (context bin_exec_t (system_u object_r bin_exec_t (systemLow systemLow))) (filecon "/usr/bin(/.*)?" any bin_exec_t) (filecon "/usr/sbin" any bin_exec_t) (filecon "/bin" any bin_exec_t) (filecon "/sbin" any bin_exec_t) ; Typically machined executes LVM, cryptsetup and similar utilities ; They are short-running, come from the rootfs and do not accept user input, so can be started in init_t domain (allow init_t bin_exec_t (file (execute execute_no_trans))) (allow init_t bin_exec_t (fs_classes (ro))) ; Access kernel module parameters (allow init_t sys_module_t (fs_classes (rw))) ; Configure kubelet (allow init_t kubelet_state_t (fs_classes (rw))) (allow udev_t device_f (fs_classes (rw))) ; Access containerd sockets for running containers (allow init_t sys_containerd_socket_t (sock_file (write))) (allow init_t sys_containerd_t (unix_stream_socket (connectto))) (allow init_t pod_containerd_socket_t (sock_file (write))) (allow init_t pod_containerd_t (unix_stream_socket (connectto))) ; Allow access to any file for machined, for ls (allow init_t any_f (fs_classes (rw))) ; /dev/console (allow init_t kernel_t (system (syslog_console syslog_mod syslog_read))) (allow init_t self (capability2 (syslog))) (allow init_t kernel_t (fd (use))) ; reboot & kexec (allow init_t self (capability (sys_boot))) (allow init_t tmpfs_t (system (kexec_image_load kexec_initramfs_load))) ; labeling FS (allow init_t any_f (fs_classes (relabelfrom relabelto))) ; rootfs.sqsh (allow kernel_t rootfs_t (file (read))) ; initramfs init before switching root (allow initramfs_t device_t (fs_classes (rw))) (allow initramfs_t rootfs_t (fs_classes (ro))) (allow initramfs_t self (process_classes (full))) (allow kernel_t initramfs_t (fd (use))) ; Make machined go into proper context (allow initramfs_t bin_exec_t (fs_classes (ro))) ; Find init (allow initramfs_t init_exec_t (fs_classes (ro))) (allow initramfs_t init_exec_t (file (execute))) (typetransition initramfs_t init_exec_t process init_t) (allow initramfs_t init_t (process_classes (full))) (allow init_t initramfs_t (fd (use))) ; Direct child processes (allow system_p init_t (fd (use))) (allow system_p init_t (fifo_file (append getattr ioctl map open read rename setattr watch write))) ; cryptsetup (allow init_t self (sem (associate create destroy getattr read setattr unix_read unix_write write))) (allow init_t kernel_t (key (search))) (allow init_t self (key (all))) (allow init_t kernel_t (system (ipc_info))) ; Modules loaded via machined (allow init_t module_t (system (module_load))) (allow init_t self (capability (sys_module))) (allow init_t apid_t (unix_stream_socket (connectto))) ; CNI (allow init_t pod_containerd_t (fd (use))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/selinux.cil ================================================ (allow initramfs_t security_t (security (setbool setsecparam))) (allow init_t security_t (security (setbool setsecparam))) ; Policy is loaded by initramfs init and mustn't be modified ; The only way to set mode to permissive is setting enforcing=0 in kernel cmdline (neverallow any_p security_t (security (load_policy setenforce))) (allow any_p security_t (security ( check_context compute_av compute_create compute_member compute_relabel compute_user read_policy setcheckreqprot validate_trans ))) (allow any_p selinuxfs_t (fs_classes (ro))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/system-containerd.cil ================================================ (type containerd_exec_t) (call system_f (containerd_exec_t)) (context containerd_exec_t (system_u object_r containerd_exec_t (systemLow systemLow))) (filecon "/usr/bin/containerd" any containerd_exec_t) (filecon "/usr/bin/containerd-shim-runc-v2" any containerd_exec_t) (filecon "/usr/bin/runc" any containerd_exec_t) ; System containerd (type sys_containerd_t) (call service_p (sys_containerd_t containerd_exec_t)) (type sys_containerd_socket_t) (call system_socket_f (sys_containerd_socket_t)) ; Shim sockets (typetransition sys_containerd_t run_t sock_file sys_containerd_socket_t) ; Client socket (typetransition sys_containerd_t system_t sock_file sys_containerd_socket_t) (type sys_containerd_run_t) (call system_f (sys_containerd_run_t)) (allow sys_containerd_run_t tmpfs_t (filesystem (associate))) (typetransition sys_containerd_t run_t file sys_containerd_run_t) (typetransition sys_containerd_t run_t dir sys_containerd_run_t) (typetransition sys_containerd_t run_t lnk_file sys_containerd_run_t) (typetransition sys_containerd_t run_t chr_file sys_containerd_run_t) (typetransition sys_containerd_t run_t blk_file sys_containerd_run_t) (typetransition sys_containerd_t run_t fifo_file sys_containerd_run_t) (allow sys_containerd_t system_t (fs_classes (rw))) (allow sys_containerd_t system_var_t (fs_classes (rw))) ; failed to create task: \"apid\": failed to create shim task: OCI runtime create failed: unable to retrieve ; OCI runtime error (open /system/run/containerd/io.containerd.runtime.v2.task/system/apid/log.json: ; no such file or directory): runc did not terminate successfully (allow sys_containerd_t system_run_t (fs_classes (rw))) (allow sys_containerd_t run_t (fs_classes (rw))) (allow sys_containerd_t system_var_t (fs_classes (relabelto relabelfrom))) (allow sys_containerd_t sys_containerd_run_t (fs_classes (rw))) (allow sys_containerd_t pod_containerd_run_t (fs_classes (rw))) ; shim sockets ; Transition to system container contexts (allow sys_containerd_t system_container_p (process2 (nnp_transition nosuid_transition))) (allow sys_containerd_t system_container_p (process (transition))) ; Manage procfs & processes (allow sys_containerd_t system_container_p (fs_classes (rw))) (allow sys_containerd_t system_container_p (process_classes (full))) (allow system_container_p sys_containerd_t (fd (use))) (allow system_container_p sys_containerd_t (fifo_file (append getattr ioctl map open read rename setattr watch write))) (allow sys_containerd_t system_container_p (key (view read write search link setattr create))) ; Shim sockets (allow sys_containerd_t sys_containerd_socket_t (fs_classes (rw))) (typeattribute containerd_p) (typeattributeset containerd_p sys_containerd_t) ; Access overlayfs parameters (allow containerd_p sys_module_t (fs_classes (ro))) ; manage cgroups & namespaces (allow containerd_p cgroup_t (fs_classes (rw))) (allow containerd_p nsfs_t (fs_classes (rw))) ; mounts (allow containerd_p filesystem_f (filesystem (unmount remount mount))) ; mounton for masking (allow containerd_p any_f (fs_classes (mounton))) ; Shim sockets (allow containerd_p self (unix_stream_socket (connectto))) ; logs (allow sys_containerd_t system_t (fs_classes (rw))) ; Typically a system extension ; Possibly a service misconfigured by machined (type unconfined_container_t) (call system_container_p (unconfined_container_t)) ; logs (allow sys_containerd_t system_t (fs_classes (rw))) ; Manage keys (allow containerd_p self (key (view read write search link setattr create))) ; Containerd uses igzip on amd64 and pigz on arm64 when pulling images (allow sys_containerd_t bin_exec_t (file (execute_no_trans execute))) (allow sys_containerd_t bin_exec_t (fs_classes (ro))) ; PTYs are allocated for talosctl debug (allow sys_containerd_t devpts_t (fs_classes (rw))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/system-containers.cil ================================================ (allow sys_containerd_t init_exec_t (file (execute))) (allow sys_containerd_t init_exec_t (fs_classes (ro))) ; apid (type apid_t) (call system_container_p (apid_t)) (allow apid_t init_exec_t (file (entrypoint execute))) (type apid_socket_t) (call system_socket_f (apid_socket_t)) (type apid_runtime_socket_t) (call system_socket_f (apid_runtime_socket_t)) ; create the socket (allow apid_t system_t (fs_classes (rw))) (allow apid_t system_t (sock_file (relabelfrom))) (allow apid_t system_run_t (fs_classes (rw))) (allow apid_t system_run_t (sock_file (relabelfrom))) (allow apid_t apid_socket_t (sock_file (relabelto))) (allow apid_t apid_socket_t (fs_classes (rw))) (allow apid_t apid_runtime_socket_t (sock_file (relabelto))) (allow apid_t apid_runtime_socket_t (fs_classes (rw))) ; Talk to machined (allow apid_t machine_socket_t (fs_classes (rw))) (allow apid_t init_t (unix_stream_socket (connectto))) ; trustd (type trustd_t) (call system_container_p (trustd_t)) (allow trustd_t init_exec_t (file (entrypoint execute))) (type trustd_runtime_socket_t) (call system_socket_f (trustd_runtime_socket_t)) (allow trustd_t trustd_runtime_socket_t (sock_file (write))) ; Trustd socket is created and owned by machined (allow trustd_t init_t (unix_stream_socket (connectto))) ; Talos installer (type installer_t) (call system_container_p (installer_t)) (allow sys_containerd_t system_var_t (file (execute))) (allow installer_t system_var_t (file (entrypoint execute execute_no_trans))) (allow installer_t system_var_t (fs_classes (rw))) ; /boot(/EFI) (allow installer_t foreign_fs_t (fs_classes (rw))) (allow installer_t unlabeled_t (fs_classes (rw))) (allow installer_t fs_t (filesystem (unmount mount))) ; efibootmgr used for installing in UEFI mode (allow installer_t efivarfs_t (fs_classes (rw))) ; Talk to machined (allow installer_t machine_socket_t (sock_file (write))) (allow installer_t init_t (unix_stream_socket (connectto))) ; talosctl debug container, automatically transition to it (type debug_t) (call system_container_p (debug_t)) (typetransition sys_containerd_t system_var_t process debug_t) (allow debug_t system_var_t (file (entrypoint execute execute_no_trans))) (allow debug_t system_var_t (fs_classes (rw))) (allow debug_t log_f (fs_classes (rw))) (allow debug_t ephemeral_t (fs_classes (rw))) (allow debug_t devpts_t (fs_classes (rw))) (allow debug_t cni_conf_t (fs_classes (rw))) (allow debug_t module_t (fs_classes (ro))) (allow debug_t kubelet_state_t (fs_classes (rw))) (allow debug_t fs_t (filesystem ( getattr mount quotaget quotamod remount unmount watch ))) (allow debug_t cgroup_t (fs_classes (rw))) (allow debug_t nsfs_t (fs_classes (ro))) (allow debug_t self (key (all))) (allow debug_t self (capability2 (syslog))) (allow debug_t kernel_t (system (syslog_read))) (allow debug_t sys_module_t (fs_classes (ro))) ; Navigate procfs and monitor processes (allow debug_t any_p (fs_classes (ro))) (allow debug_t kernel_t (fs_classes (ro))) ================================================ FILE: internal/pkg/selinux/policy/selinux/services/udev.cil ================================================ (type udev_exec_t) (call system_f (udev_exec_t)) (context udev_exec_t (system_u object_r udev_exec_t (systemLow systemLow))) (filecon "/usr/bin/systemd-udevd" file udev_exec_t) (filecon "/usr/bin/udevadm" file udev_exec_t) ; Do not reorder: label non-executable rules files as executable (type udev_rules_t) (call system_f (udev_rules_t)) (filecon "/usr/lib/udev/rules.d(/.*)?" any (system_u object_r udev_rules_t (systemLow systemLow))) (filecon "/usr/lib/udev(/.*)?" any udev_exec_t) ; Some rules are on tmpfs (allow udev_rules_t tmpfs_t (filesystem (associate))) (allow udev_t lib_t (fs_classes (ioctl))) (type module_t) (call system_f (module_t)) (filecon "/usr/lib/modules(/.*)?" any (system_u object_r module_t (systemLow systemLow))) (filecon "/lib/modules" any (system_u object_r module_t (systemLow systemLow))) (type udev_t) (call service_p (udev_t udev_exec_t)) (type udev_socket_t) (call system_socket_f (udev_socket_t)) (typeattribute not_udev_socket_t) (typeattributeset not_udev_socket_t (not udev_socket_t)) (typetransition udev_t not_udev_socket_t sock_file udev_socket_t) ; udevadm called by machined in its context (allow init_t udev_exec_t (file (execute_no_trans))) (allow init_t udev_t (unix_stream_socket (connectto))) ; Device subsystems, labeled by udev rules ; RTC is important for security verification (type rtc_device_t) (call protected_device_f (rtc_device_t)) ; Could be exposed firmware storage (type mtd_device_t) (call protected_device_f (mtd_device_t)) ; Could reset the system (type wdt_device_t) (call protected_device_f (wdt_device_t)) ; Typically client pods must not access TPM (type tpm_device_t) (call protected_device_f (tpm_device_t)) ; TODO: label and restrict block devices (type modprobe_exec_t) (call system_f (modprobe_exec_t)) (filecon "/usr/bin/modprobe" file (system_u object_r modprobe_exec_t (systemLow systemLow))) ; If modprobe is called by machined or kernel do a transition to udev context which has module permissions (allow udev_t modprobe_exec_t (file (execute execute_no_trans))) (allow udev_t modprobe_exec_t (fs_classes (ro))) (typetransition kernel_t modprobe_exec_t process udev_t) (typetransition init_t modprobe_exec_t process udev_t) (allow udev_t kernel_t (fd (use))) (allow udev_t modprobe_exec_t (file (entrypoint))) (allow kernel_t modprobe_exec_t (file (execute))) (allow kernel_t udev_t (process (all))) ; including transition (allow init_t modprobe_exec_t (file (execute))) (allow init_t udev_t (process (all))) ; including transition (allow kernel_t module_t (system (module_load module_request))) (allow udev_t module_t (fs_classes (ro))) (allow udev_t module_t (system (module_load module_request))) (allow udev_t self (capability (sys_module))) (allow udev_t self (cap_userns (sys_module))) (allow udev_t kernel_t (system (module_load module_request))) (allow kernel_t self (system (module_request))) ; Allow udev to read rules files (allow udev_t udev_rules_t (fs_classes (ro ioctl))) ; udev rules can set module parameters (allow udev_t sys_module_t (fs_classes (rw))) (allow udev_t device_t (fs_classes (relabelfrom))) (allow udev_t device_f (fs_classes (rw relabelto))) ; socket and runtime files (type udev_run_t) (call system_f (udev_run_t)) (allow udev_run_t tmpfs_t (filesystem (associate))) (typetransition udev_t run_t file udev_run_t) (typetransition udev_t run_t dir udev_run_t) (typetransition udev_t run_t lnk_file udev_run_t) (typetransition udev_t run_t chr_file udev_run_t) (typetransition udev_t run_t blk_file udev_run_t) (typetransition udev_t run_t fifo_file udev_run_t) (allow udev_t run_t (dir (add_name write))) ; before transition (allow udev_t udev_run_t (fs_classes (rw))) (allow udev_t udev_socket_t (fs_classes (rw))) ; manage properties from rules (allow udev_t sysfs_t (fs_classes (rw))) ; Container detection (allow udev_t init_t (fs_classes (ro ioctl))) ; exec-invoke managed stuff (allow udev_t cgroup_t (fs_classes (rw ioctl))) (allow udev_t kernel_t (key (search))) ; dmsetup ; TODO: label more specifically? (allow udev_t bin_exec_t (file (execute execute_no_trans))) (allow udev_t kernel_t (system (ipc_info))) (allow udev_t any_p (sem (all))) ================================================ FILE: internal/pkg/selinux/selinux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package selinux provides generic code for managing SELinux. package selinux import ( "bytes" _ "embed" "log" "os" "path/filepath" "slices" "sync" "github.com/pkg/xattr" "github.com/siderolabs/go-procfs/procfs" "golang.org/x/sys/unix" "github.com/siderolabs/talos/internal/pkg/containermode" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/xfs" ) //go:embed policy/policy.33 var policy []byte // IsEnabled checks if SELinux is enabled on the system by reading // the kernel command line. It returns true if SELinux is enabled, // otherwise it returns false. It also ensures we're not in a container. // By default SELinux is disabled. var IsEnabled = sync.OnceValue(func() bool { if containermode.InContainer() { return false } val := procfs.ProcCmdline().Get(constants.KernelParamSELinux).First() var selinuxFSPresent bool if _, err := os.Stat("/sys/fs/selinux"); err == nil { selinuxFSPresent = true } return val != nil && *val == "1" && selinuxFSPresent }) // IsEnforcing checks if SELinux is enabled and the mode should be enforcing. // By default if SELinux is enabled we consider it to be permissive. var IsEnforcing = sync.OnceValue(func() bool { if !IsEnabled() { return false } val := procfs.ProcCmdline().Get(constants.KernelParamSELinuxEnforcing).First() return val != nil && *val == "1" }) // GetLabel gets label for file, directory or symlink (not following symlinks) // It does not perform the operation in case SELinux is disabled. func GetLabel(filename string) (string, error) { if !IsEnabled() { return "", nil } label, err := xattr.LGet(filename, "security.selinux") if err != nil { return "", err } if label == nil { return "", nil } return string(bytes.Trim(label, "\x00\n")), nil } // SetLabel sets label for file, directory or symlink (not following symlinks) // It does not perform the operation in case SELinux is disabled, provided label is empty or already set. func SetLabel(filename string, label string, excludeLabels ...string) error { if label == "" || !IsEnabled() { return nil } currentLabel, err := GetLabel(filename) if err != nil { return err } // Skip extra FS transactions when labels are okay. if currentLabel == label { return nil } // Skip setting label if it's in excludeLabels. if currentLabel != "" && slices.Contains(excludeLabels, currentLabel) { return nil } // We use LGet/LSet so that we manipulate label on the exact path, not the symlink target. if err := xattr.LSet(filename, "security.selinux", []byte(label)); err != nil { return err } return nil } // FGetLabel gets label for file, directory or symlink (not following symlinks) using provided root. // It does not perform the operation in case SELinux is disabled. func FGetLabel(root xfs.Root, filename string) (string, error) { if !IsEnabled() { return "", nil } f, err := xfs.OpenFile(root, filename, unix.O_RDONLY|unix.O_NOFOLLOW, 0) if err != nil { return "", err } defer f.Close() //nolint:errcheck osf, err := xfs.AsOSFile(f, filename) if err != nil { return "", err } defer osf.Close() //nolint:errcheck label, err := xattr.FGet(osf, "security.selinux") if err != nil { return "", err } if label == nil { return "", nil } return string(bytes.Trim(label, "\x00\n")), nil } // FSetLabel sets label for file, directory or symlink (not following symlinks) using provided root. // It does not perform the operation in case SELinux is disabled, provided label is empty or already set. func FSetLabel(root xfs.Root, filename string, label string, excludeLabels ...string) error { if label == "" || !IsEnabled() { return nil } currentLabel, err := FGetLabel(root, filename) if err != nil { return err } // Skip extra FS transactions when labels are okay. if currentLabel == label { return nil } // Skip setting label if it's in excludeLabels. if currentLabel != "" && slices.Contains(excludeLabels, currentLabel) { return nil } f, err := xfs.Open(root, filename) if err != nil { return err } defer f.Close() //nolint:errcheck osf, err := xfs.AsOSFile(f, filename) if err != nil { return err } defer osf.Close() //nolint:errcheck // We use FGet/FSet so that we manipulate label on the exact path, not the symlink target. if err := xattr.FSet(osf, "security.selinux", []byte(label)); err != nil { return err } return nil } // SetLabelRecursive sets label for directory and its content recursively. // It does not perform the operation in case SELinux is disabled, provided label is empty or already set. func SetLabelRecursive(dir string, label string, excludeLabels ...string) error { if label == "" || !IsEnabled() { return nil } return filepath.Walk(dir, func(path string, _ os.FileInfo, err error) error { return SetLabel(path, label, excludeLabels...) }) } // Init initializes SELinux based on the configured mode. // It loads the policy and enforces it if necessary. func Init() error { if !IsEnabled() { log.Println("selinux: disabled, not loading policy") return nil } if IsEnforcing() { log.Println("selinux: running in enforcing mode, policy will be applied as soon as it's loaded") } log.Println("selinux: loading policy") if err := os.WriteFile("/sys/fs/selinux/load", policy, 0o777); err != nil { return err } log.Println("selinux: policy loaded") return nil } ================================================ FILE: internal/pkg/smbios/oem.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package smbios import ( "strings" ) // ReadOEMVariable reads the OEM variable from SMBIOS and returns its value. func ReadOEMVariable(name string) ([]string, error) { smbiosInfo, err := GetSMBIOSInfo() if err != nil { return nil, err } var result []string for _, str := range smbiosInfo.OEMStrings.Strings { key, val, ok := strings.Cut(str, "=") if !ok { continue } if key == name { result = append(result, val) } } return result, nil } ================================================ FILE: internal/pkg/smbios/smbios.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package smbios provides access to SMBIOS information. package smbios import ( "sync" "github.com/siderolabs/go-smbios/smbios" ) var ( syncSMBIOS sync.Once connSMBIOS *smbios.SMBIOS errSMBIOS error ) // GetSMBIOSInfo returns the SMBIOS info. func GetSMBIOSInfo() (*smbios.SMBIOS, error) { syncSMBIOS.Do(func() { connSMBIOS, errSMBIOS = smbios.New() }) return connSMBIOS, errSMBIOS } ================================================ FILE: internal/pkg/timex/timex.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package timex provides a simple wrapper around adjtimex syscall. package timex import ( "strings" "golang.org/x/sys/unix" ) // Status is bitmask field of statuses. type Status int32 func (status Status) String() string { var labels []string for _, item := range []struct { bit Status label string }{ {unix.STA_PLL, "STA_PLL"}, /* enable PLL updates (rw) */ {unix.STA_PPSFREQ, "STA_PPSFREQ"}, /* enable PPS freq discipline (rw) */ {unix.STA_PPSTIME, "STA_PPSTIME"}, /* enable PPS time discipline (rw) */ {unix.STA_FLL, "STA_FLL"}, /* select frequency-lock mode (rw) */ {unix.STA_INS, "STA_INS"}, /* insert leap (rw) */ {unix.STA_DEL, "STA_DEL"}, /* delete leap (rw) */ {unix.STA_UNSYNC, "STA_UNSYNC"}, /* clock unsynchronized (rw) */ {unix.STA_FREQHOLD, "STA_FREQHOLD"}, /* hold frequency (rw) */ {unix.STA_PPSSIGNAL, "STA_PPSSIGNAL"}, /* PPS signal present (ro) */ {unix.STA_PPSJITTER, "STA_PPSJITTER"}, /* PPS signal jitter exceeded (ro) */ {unix.STA_PPSWANDER, "STA_PPSWANDER"}, /* PPS signal wander exceeded (ro) */ {unix.STA_PPSERROR, "STA_PPSERROR"}, /* PPS signal calibration error (ro) */ {unix.STA_CLOCKERR, "STA_CLOCKERR"}, /* clock hardware fault (ro) */ {unix.STA_NANO, "STA_NANO"}, /* resolution (0 = us, 1 = ns) (ro) */ {unix.STA_MODE, "STA_MODE"}, /* mode (0 = PLL, 1 = FLL) (ro) */ {unix.STA_CLK, "STA_CLK"}, /* clock source (0 = A, 1 = B) (ro) */ } { if (status & item.bit) == item.bit { labels = append(labels, item.label) } } return strings.Join(labels, " | ") } // State is clock state. type State int func (state State) String() string { switch state { case unix.TIME_OK: return "TIME_OK" case unix.TIME_INS: return "TIME_INS" case unix.TIME_DEL: return "TIME_DEL" case unix.TIME_OOP: return "TIME_OOP" case unix.TIME_WAIT: return "TIME_WAIT" case unix.TIME_ERROR: return "TIME_ERROR" default: return "TIME_UNKNOWN" } } // Adjtimex provides a wrapper around syscall.Adjtimex. func Adjtimex(buf *unix.Timex) (state State, err error) { st, err := unix.ClockAdjtime(unix.CLOCK_REALTIME, buf) return State(st), err } ================================================ FILE: internal/pkg/toml/merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package toml import ( "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" "os" "github.com/pelletier/go-toml/v2" "github.com/siderolabs/talos/pkg/machinery/config/merge" ) // tomlDecodeFile decodes a TOML file into the provided destination, and returns a sha256 hash of the file content. func tomlDecodeFile(path string, dest any) ([]byte, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() //nolint:errcheck hash := sha256.New() err = toml.NewDecoder(io.TeeReader(f, hash)).Decode(dest) return hash.Sum(nil), err } // Merge several TOML documents in files into one. // // Merge process relies on generic map[string]any merge which might not always be correct. // // Merge returns a sha256 checksum of each file merged. func Merge(parts []string) ([]byte, map[string][]byte, error) { merged := map[string]any{} checksums := make(map[string][]byte, len(parts)) var header []byte for _, part := range parts { partial := map[string]any{} hash, err := tomlDecodeFile(part, &partial) if err != nil { return nil, nil, fmt.Errorf("error decoding %q: %w", part, err) } if err := merge.Merge(merged, partial); err != nil { return nil, nil, fmt.Errorf("error merging %q: %w", part, err) } header = fmt.Appendf(header, "## %s (sha256:%s)\n", part, hex.EncodeToString(hash)) checksums[part] = hash } var out bytes.Buffer _, _ = out.Write(header) _ = out.WriteByte('\n') if err := toml.NewEncoder(&out).SetIndentTables(true).Encode(merged); err != nil { return nil, nil, fmt.Errorf("error encoding merged config: %w", err) } return out.Bytes(), checksums, nil } ================================================ FILE: internal/pkg/toml/merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package toml_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/toml" ) //go:embed testdata/expected.toml var expected []byte func TestMerge(t *testing.T) { out, checksums, err := toml.Merge([]string{ "testdata/1.toml", "testdata/2.toml", "testdata/3.toml", }) require.NoError(t, err) assert.Equal(t, string(expected), string(out)) assert.Contains(t, checksums, "testdata/1.toml") assert.Contains(t, checksums, "testdata/2.toml") assert.Contains(t, checksums, "testdata/3.toml") } ================================================ FILE: internal/pkg/toml/testdata/1.toml ================================================ version = 2 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2" discard_unpacked_layers = true ================================================ FILE: internal/pkg/toml/testdata/2.toml ================================================ [plugins] [plugins."io.containerd.grpc.v1.cri"] [plugins."io.containerd.grpc.v1.cri".registry] config_path = "/etc/cri/conf.d/hosts" [plugins."io.containerd.grpc.v1.cri".registry.configs] ================================================ FILE: internal/pkg/toml/testdata/3.toml ================================================ [metrics] address = "0.0.0.0:11234" [plugins] [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.k8s.io/pause:3.8" ================================================ FILE: internal/pkg/toml/testdata/expected.toml ================================================ ## testdata/1.toml (sha256:2eac71621235f8666c54ad3f29a77b2e8483bbd7f0717f8613af591fb5609b44) ## testdata/2.toml (sha256:47ae85a638a291b04518413a12b19c51883a17c8f5064193462d3527b4495e36) ## testdata/3.toml (sha256:159608dffd674e5fe351d47166eab59ee93f6523ff336602364edfd7be25c796) version = 2 [metrics] address = '0.0.0.0:11234' [plugins] [plugins.'io.containerd.grpc.v1.cri'] sandbox_image = 'registry.k8s.io/pause:3.8' [plugins.'io.containerd.grpc.v1.cri'.containerd] [plugins.'io.containerd.grpc.v1.cri'.containerd.runtimes] [plugins.'io.containerd.grpc.v1.cri'.containerd.runtimes.runc] discard_unpacked_layers = true runtime_type = 'io.containerd.runc.v2' [plugins.'io.containerd.grpc.v1.cri'.registry] config_path = '/etc/cri/conf.d/hosts' [plugins.'io.containerd.grpc.v1.cri'.registry.configs] ================================================ FILE: internal/pkg/toml/toml.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package toml provides utility functions for TOML handling. package toml ================================================ FILE: internal/pkg/tpm/tpm_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux // Package tpm provides TPM 2.0 support. package tpm import ( "errors" "os" "github.com/google/go-tpm/tpm2/transport" "github.com/google/go-tpm/tpm2/transport/linuxtpm" ) // Open a TPM device. // // Tries first /dev/tpmrm0 and then /dev/tpm0. func Open() (transport.TPMCloser, error) { tpm, err := linuxtpm.Open("/dev/tpmrm0") if errors.Is(err, os.ErrNotExist) { tpm, err = linuxtpm.Open("/dev/tpm0") } return tpm, err } ================================================ FILE: internal/pkg/tpm/tpm_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux // Package tpm provides TPM 2.0 support. package tpm import ( "errors" "github.com/google/go-tpm/tpm2/transport" ) // Open a TPM device. func Open() (transport.TPMCloser, error) { return nil, errors.New("TPM device is not available") } ================================================ FILE: internal/pkg/uki/assemble.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki import ( "path/filepath" "github.com/siderolabs/talos/internal/pkg/uki/internal/pe" ) // assemble the UKI file out of sections. func (builder *Builder) assemble() error { builder.unsignedUKIPath = filepath.Join(builder.scratchDir, "unsigned.uki") return pe.AssembleNative(builder.SdStubPath, builder.unsignedUKIPath, builder.sections) } ================================================ FILE: internal/pkg/uki/generate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki import ( "crypto/x509" "encoding/json" "encoding/pem" "fmt" "os" "path/filepath" "slices" talosx509 "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/internal/pkg/measure" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/splash" ) func (builder *Builder) generateOSRel() error { osRelease, err := version.OSReleaseFor(version.Name, builder.Version) if err != nil { return err } path := filepath.Join(builder.scratchDir, "os-release") if err = os.WriteFile(path, osRelease, 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionOSRel.String(), Path: path, Measure: true, Append: true, }, ) return nil } func (builder *Builder) generateCmdline() error { path := filepath.Join(builder.scratchDir, "cmdline") if err := os.WriteFile(path, []byte(builder.Cmdline), 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionCmdline.String(), Path: path, Measure: true, Append: true, }, ) return nil } func (builder *Builder) generateInitrd() error { builder.sections = append(builder.sections, section{ Name: SectionInitrd.String(), Path: builder.InitrdPath, Measure: true, Append: true, }, ) return nil } func (builder *Builder) generateSplash() error { path := filepath.Join(builder.scratchDir, "splash.bmp") if err := os.WriteFile(path, splash.GetBootImage(), 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionSplash.String(), Path: path, Measure: true, Append: true, }, ) return nil } func (builder *Builder) generateUname() error { // it is not always possible to get the kernel version from the kernel image, so we // do a bit of pre-checks var kernelVersion string if builder.Version == version.Tag { // if building from the same version of Talos, use default kernel version kernelVersion = constants.DefaultKernelVersion } else { // otherwise, try to get the kernel version from the kernel image kernelVersion, _ = DiscoverKernelVersion(builder.KernelPath) //nolint:errcheck } if kernelVersion == "" { // we haven't got the kernel version, skip the uname section return nil } path := filepath.Join(builder.scratchDir, "uname") if err := os.WriteFile(path, []byte(kernelVersion), 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionUname.String(), Path: path, Measure: true, Append: true, }, ) return nil } func (builder *Builder) generateSBAT() error { sbat, err := GetSBAT(builder.SdStubPath) if err != nil { return err } path := filepath.Join(builder.scratchDir, "sbat") if err = os.WriteFile(path, sbat, 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionSBAT.String(), Path: path, Measure: true, }, ) return nil } func (builder *Builder) generatePCRPublicKey() error { publicKeyBytes, err := x509.MarshalPKIXPublicKey(builder.PCRSigner.PublicRSAKey()) if err != nil { return err } publicKeyPEM := pem.EncodeToMemory(&pem.Block{ Type: talosx509.PEMTypeRSAPublic, Bytes: publicKeyBytes, }) path := filepath.Join(builder.scratchDir, "pcr-public.pem") if err = os.WriteFile(path, publicKeyPEM, 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionPCRPKey.String(), Path: path, Append: true, Measure: true, }, ) return nil } func defaultProfiles() []Profile { return []Profile{ { ID: "main", }, } } func (builder *Builder) generateProfiles() error { if !quirks.New(builder.Version).SupportsUKIProfiles() { return nil } if slices.ContainsFunc(builder.Profiles, func(p Profile) bool { return p.ID == "main" }) { return fmt.Errorf("profile with ID 'main' is reserved") } for _, profile := range slices.Concat(defaultProfiles(), builder.Profiles) { path := filepath.Join(builder.scratchDir, fmt.Sprintf("profile-%s", profile.ID)) if err := os.WriteFile(path, []byte(profile.String()), 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionProfile.String(), Path: path, Append: true, Measure: true, }, ) if profile.Cmdline != "" { path = filepath.Join(builder.scratchDir, fmt.Sprintf("profile-%s-cmdline", profile.ID)) if err := os.WriteFile(path, []byte(profile.Cmdline), 0o600); err != nil { return err } builder.sections = append(builder.sections, section{ Name: SectionCmdline.String(), Path: path, Append: true, Measure: true, }, ) } } return nil } func (builder *Builder) generateKernel() error { path := builder.KernelPath if builder.peSigner != nil { path := filepath.Join(builder.scratchDir, "kernel") if err := builder.peSigner.Sign(builder.KernelPath, path); err != nil { return err } } builder.sections = append(builder.sections, section{ Name: SectionLinux.String(), Path: path, Append: true, Measure: true, }, ) return nil } type profileIndex struct { Start int End int } func (builder *Builder) generatePCRSig() error { toMeasure := xslices.Filter(builder.sections, func(s section) bool { return s.Measure }) if quirks.New(builder.Version).SupportsUKIProfiles() { profileIndexes := []profileIndex{} var previousProfileIndex int for i, s := range toMeasure { if s.Name == SectionProfile.String() { if previousProfileIndex != 0 { profileIndexes[len(profileIndexes)-1].End = i } profileIndexes = append(profileIndexes, profileIndex{Start: i}) previousProfileIndex = i } } if previousProfileIndex != 0 { profileIndexes[len(profileIndexes)-1].End = len(toMeasure) } for i, profileIndex := range profileIndexes { profileData := xslices.ToMap( slices.Concat(toMeasure[:profileIndexes[0].Start], toMeasure[profileIndex.Start:profileIndex.End]), func(s section) (string, string) { return s.Name, s.Path }, ) if err := builder.writePCRSignature(profileData, fmt.Sprintf("pcrpsig-%d", i), profileIndex.End+i); err != nil { return err } } return nil } sectionData := xslices.ToMap(toMeasure, func(s section) (string, string) { return s.Name, s.Path }) return builder.writePCRSignature(sectionData, "pcrpsig", len(builder.sections)) } func (builder *Builder) writePCRSignature(data map[string]string, filename string, insertIndex int) error { pcrData, err := measure.GenerateSignedPCR(data, builder.PCRSigner) if err != nil { return err } pcrSignatureData, err := json.Marshal(pcrData) if err != nil { return err } path := filepath.Join(builder.scratchDir, filename) if err = os.WriteFile(path, pcrSignatureData, 0o600); err != nil { return err } builder.sections = slices.Insert(builder.sections, insertIndex, section{ Name: SectionPCRSig.String(), Path: path, Append: true, }) return nil } ================================================ FILE: internal/pkg/uki/internal/pe/extract.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pe import ( "debug/pe" "fmt" "io" ) // AssetInfo contains the kernel, initrd, and cmdline from a PE file. type AssetInfo struct { io.Closer Kernel io.Reader Initrd io.Reader Cmdline io.Reader } // Extract extracts the kernel, initrd, and cmdline from a PE file. func Extract(ukiPath string) (assetInfo AssetInfo, err error) { peFile, err := pe.Open(ukiPath) if err != nil { return assetInfo, fmt.Errorf("failed to open PE file: %w", err) } assetInfo.Closer = peFile sectionMap := map[string]*io.Reader{ ".initrd": &assetInfo.Initrd, ".cmdline": &assetInfo.Cmdline, ".linux": &assetInfo.Kernel, } for _, section := range peFile.Sections { if reader, exists := sectionMap[section.Name]; exists && *reader == nil { *reader = io.LimitReader(section.Open(), int64(section.VirtualSize)) } } for name, reader := range sectionMap { if *reader == nil { return assetInfo, fmt.Errorf("%s not found in PE file", name) } } return assetInfo, nil } ================================================ FILE: internal/pkg/uki/internal/pe/extract_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pe_test import ( "io" "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/internal/pkg/uki/internal/pe" ) func TestUKIExtract(t *testing.T) { srcFile := "testdata/linuxx64.efi.stub" destDir := t.TempDir() destFile := filepath.Join(destDir, "vmlinuz.efi") for _, section := range []string{"linux", "initrd", "cmdline", "profile-default", "profile-reset", "cmdline-reset"} { assert.NoError(t, os.WriteFile(filepath.Join(destDir, section), []byte(section), 0o644)) } assert.NoError(t, pe.AssembleNative(srcFile, destFile, []pe.Section{ { Name: ".linux", Path: filepath.Join(destDir, "linux"), Measure: false, Append: true, }, { Name: ".initrd", Path: filepath.Join(destDir, "initrd"), Measure: false, Append: true, }, { Name: ".cmdline", Path: filepath.Join(destDir, "cmdline"), Measure: false, Append: true, }, { Name: ".profile", Path: filepath.Join(destDir, "profile-default"), Measure: false, Append: true, }, { Name: ".profile", Path: filepath.Join(destDir, "profile-reset"), Measure: false, Append: true, }, { Name: ".cmdline", Path: filepath.Join(destDir, "cmdline-reset"), Measure: false, Append: true, }, })) ukiData, err := pe.Extract(destFile) assert.NoError(t, err) t.Cleanup(func() { assert.NoError(t, ukiData.Close()) }) var kernel, initrd, cmdline strings.Builder _, err = io.Copy(&kernel, ukiData.Kernel) assert.NoError(t, err) assert.Equal(t, "linux", kernel.String()) _, err = io.Copy(&initrd, ukiData.Initrd) assert.NoError(t, err) assert.Equal(t, "initrd", initrd.String()) _, err = io.Copy(&cmdline, ukiData.Cmdline) assert.NoError(t, err) assert.Equal(t, "cmdline", cmdline.String()) } ================================================ FILE: internal/pkg/uki/internal/pe/native.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pe import ( "debug/pe" "encoding/binary" "errors" "fmt" "io" "os" "time" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/imager/utils" ) const ( dosHeaderLength = 0x40 dosHeaderPadding = 0x40 ) // AssembleNative is a helper function to assemble the PE file without external programs. // //nolint:gocyclo,cyclop func AssembleNative(srcPath, dstPath string, sections []Section) error { in, err := os.Open(srcPath) if err != nil { return err } peFile, err := pe.NewFile(in) if err != nil { return err } defer in.Close() //nolint: errcheck if peFile.COFFSymbols != nil { return errors.New("COFF symbols are not supported") } if peFile.StringTable != nil { return errors.New("COFF string table is not supported") } if peFile.Symbols != nil { return errors.New("symbols are not supported") } _, err = in.Seek(0, io.SeekStart) if err != nil { return err } out, err := os.Create(dstPath) if err != nil { return fmt.Errorf("failed to create output: %w", err) } defer out.Close() //nolint: errcheck // 1. DOS header var dosheader [dosHeaderLength]byte _, err = in.ReadAt(dosheader[:], 0) if err != nil { return fmt.Errorf("failed to read DOS header: %w", err) } binary.LittleEndian.PutUint32(dosheader[dosHeaderLength-4:], dosHeaderLength+dosHeaderPadding) _, err = out.Write(append(append(dosheader[:], make([]byte, dosHeaderPadding)...), []byte("PE\x00\x00")...)) if err != nil { return fmt.Errorf("failed to write DOS header: %w", err) } // 2. PE header and optional header newFileHeader := peFile.FileHeader timestamp, ok, err := utils.SourceDateEpoch() if err != nil { return fmt.Errorf("failed to get SOURCE_DATE_EPOCH: %w", err) } if !ok { timestamp = time.Now().Unix() } newFileHeader.TimeDateStamp = uint32(timestamp) // find the first VMA address lastSection := peFile.Sections[len(peFile.Sections)-1] header, ok := peFile.OptionalHeader.(*pe.OptionalHeader64) if !ok { return errors.New("failed to get optional header") } sectionAlignment := uint64(header.SectionAlignment - 1) fileAlignment := uint64(header.FileAlignment - 1) baseVirtualAddress := uint64(lastSection.VirtualAddress) + uint64(lastSection.VirtualSize) baseVirtualAddress = (baseVirtualAddress + sectionAlignment) &^ sectionAlignment newHeader := *header newHeader.MajorLinkerVersion = 0 newHeader.MinorLinkerVersion = 0 newHeader.CheckSum = 0 type peSectionWithPath struct { pe.Section SourcePath string } newSections := xslices.Map(peFile.Sections, func(section *pe.Section) *peSectionWithPath { return &peSectionWithPath{ Section: *section, } }) // calculate sections size and VMA for i := range sections { if !sections[i].Append { continue } st, err := os.Stat(sections[i].Path) if err != nil { return err } sections[i].virtualSize = uint64(st.Size()) sections[i].virtualAddress = baseVirtualAddress baseVirtualAddress += sections[i].virtualSize baseVirtualAddress = (baseVirtualAddress + sectionAlignment) &^ sectionAlignment newFileHeader.NumberOfSections++ newSections = append(newSections, &peSectionWithPath{ Section: pe.Section{ SectionHeader: pe.SectionHeader{ Name: sections[i].Name, VirtualSize: uint32(sections[i].virtualSize), VirtualAddress: uint32(sections[i].virtualAddress), Size: uint32((sections[i].virtualSize + fileAlignment) &^ fileAlignment), Characteristics: pe.IMAGE_SCN_CNT_INITIALIZED_DATA | pe.IMAGE_SCN_MEM_READ, }, }, SourcePath: sections[i].Path, }) } newHeader.SizeOfInitializedData = 0 newHeader.SizeOfCode = 0 newHeader.SizeOfHeaders = 0x80 /* DOS header */ + uint32(binary.Size(pe.FileHeader{})+binary.Size(pe.OptionalHeader64{})+binary.Size(pe.SectionHeader32{})*len(newSections)) newHeader.SizeOfHeaders = (newHeader.SizeOfHeaders + uint32(fileAlignment)) &^ uint32(fileAlignment) lastNewSection := newSections[len(newSections)-1] lastSectionPointer := uint64(lastNewSection.VirtualAddress) + uint64(lastNewSection.VirtualSize) + newHeader.ImageBase lastSectionPointer = (lastSectionPointer + sectionAlignment) &^ sectionAlignment newHeader.SizeOfImage = uint32(lastSectionPointer - newHeader.ImageBase) for _, section := range newSections { if section.Characteristics&pe.IMAGE_SCN_CNT_INITIALIZED_DATA != 0 { newHeader.SizeOfInitializedData += section.Size } else { newHeader.SizeOfCode += section.Size } } // write the new file header if err = binary.Write(out, binary.LittleEndian, newFileHeader); err != nil { return fmt.Errorf("failed to write file header: %w", err) } if err = binary.Write(out, binary.LittleEndian, newHeader); err != nil { return fmt.Errorf("failed to write optional header: %w", err) } // 3. Section headers rawSections := xslices.Map(newSections, func(section *peSectionWithPath) pe.SectionHeader32 { var rawName [8]byte copy(rawName[:], section.Name) return pe.SectionHeader32{ Name: rawName, VirtualSize: section.VirtualSize, VirtualAddress: section.VirtualAddress, SizeOfRawData: section.Size, Characteristics: section.Characteristics, } }, ) sectionOffset := newHeader.SizeOfHeaders for i := range rawSections { rawSections[i].PointerToRawData = sectionOffset sectionOffset += rawSections[i].SizeOfRawData } for _, rawSection := range rawSections { if err = binary.Write(out, binary.LittleEndian, rawSection); err != nil { return fmt.Errorf("failed to write section header: %w", err) } } // 4. Section data for i, rawSection := range rawSections { name := newSections[i].Name sourcePath := newSections[i].SourcePath if err := func(rawSection pe.SectionHeader32, name, sourcePath string) error { // the section might come either from the input PE file or from a separate file var sectionData io.ReadCloser if sourcePath != "" { sectionData, err = os.Open(sourcePath) if err != nil { return fmt.Errorf("failed to open section data: %w", err) } defer sectionData.Close() //nolint: errcheck } if sectionData == nil { for _, section := range peFile.Sections { if section.Name == name { sectionData = io.NopCloser(section.Open()) break } } } if sectionData == nil { return fmt.Errorf("failed to find section data for %q", name) } _, err = out.Seek(int64(rawSection.PointerToRawData), io.SeekStart) if err != nil { return fmt.Errorf("failed to seek to section data: %w", err) } n, err := io.Copy(out, sectionData) if err != nil { return fmt.Errorf("failed to copy section data: %w", err) } if n > int64(rawSection.SizeOfRawData) { return fmt.Errorf("section data is too large: %d > %d", n, rawSection.SizeOfRawData) } if n < int64(rawSection.SizeOfRawData) { _, err = io.CopyN(out, zeroReader{}, int64(rawSection.SizeOfRawData)-n) if err != nil { return fmt.Errorf("failed to zero-fill section data: %w", err) } } return nil }(rawSection, name, sourcePath); err != nil { return fmt.Errorf("failed to write section data %s: %w", name, err) } } return nil } type zeroReader struct{} func (zeroReader) Read(p []byte) (int, error) { for i := range p { p[i] = 0 } return len(p), nil } ================================================ FILE: internal/pkg/uki/internal/pe/objcopy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pe import ( "context" "debug/pe" "errors" "fmt" "os" "os/exec" ) // AssembleObjcopy is a helper function to assemble the PE file using objcopy. func AssembleObjcopy(ctx context.Context, srcPath, dstPath string, sections []Section) error { peFile, err := pe.Open(srcPath) if err != nil { return err } defer peFile.Close() //nolint: errcheck // find the first VMA address lastSection := peFile.Sections[len(peFile.Sections)-1] header, ok := peFile.OptionalHeader.(*pe.OptionalHeader64) if !ok { return errors.New("failed to get optional header") } sectionAlignment := uint64(header.SectionAlignment - 1) baseVMA := header.ImageBase + uint64(lastSection.VirtualAddress) + uint64(lastSection.VirtualSize) baseVMA = (baseVMA + sectionAlignment) &^ sectionAlignment // calculate sections size and VMA for i := range sections { if !sections[i].Append { continue } st, err := os.Stat(sections[i].Path) if err != nil { return err } sections[i].virtualSize = uint64(st.Size()) sections[i].virtualAddress = baseVMA baseVMA += sections[i].virtualSize baseVMA = (baseVMA + sectionAlignment) &^ sectionAlignment } // create the output file args := make([]string, 0, len(sections)+2) for _, section := range sections { if !section.Append { continue } args = append(args, "--add-section", fmt.Sprintf("%s=%s", section.Name, section.Path), "--change-section-vma", fmt.Sprintf("%s=0x%x", section.Name, section.virtualAddress)) } args = append(args, srcPath, dstPath) cmd := exec.CommandContext(ctx, "objcopy", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() } ================================================ FILE: internal/pkg/uki/internal/pe/pe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package pe handles appending sections to PE files. package pe // Section is a UKI file section. type Section struct { // Section name. Name string // Path to the contents of the section. Path string // Should the section be measured to the TPM? Measure bool // Should the section be appended, or is it already in the PE file. Append bool // Virtual virtualSize & VMA of the section. virtualSize uint64 virtualAddress uint64 } ================================================ FILE: internal/pkg/uki/internal/pe/pe_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package pe_test import ( "bytes" "os" "os/exec" "path/filepath" "regexp" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/uki/internal/pe" ) func assertToolsPresent(t *testing.T) { t.Helper() for _, tool := range []string{ "objcopy", "objdump", "xxd", } { _, err := exec.LookPath(tool) if err != nil { t.Skipf("missing tool: %s", tool) } } } func TestAssembleNative(t *testing.T) { assertToolsPresent(t) t.Setenv("SOURCE_DATE_EPOCH", "1609459200") tmpDir := t.TempDir() outNative := filepath.Join(tmpDir, "uki-native.bin") outObjcopy := filepath.Join(tmpDir, "uki-objcopy.bin") unamePath := filepath.Join(tmpDir, "uname") require.NoError(t, os.WriteFile(unamePath, []byte("Talos"), 0o644)) linuxPath := filepath.Join(tmpDir, "linux") require.NoError(t, os.WriteFile(linuxPath, bytes.Repeat([]byte{0xde, 0xad, 0xbe, 0xef}, 1048576), 0o644)) sections := func() []pe.Section { return []pe.Section{ { Name: ".text", }, { Name: ".uname", Append: true, Path: unamePath, }, { Name: ".linux", Append: true, Path: linuxPath, }, } } require.NoError(t, pe.AssembleNative("testdata/linuxx64.efi.stub", outNative, sections())) require.NoError(t, pe.AssembleObjcopy(t.Context(), "testdata/linuxx64.efi.stub", outObjcopy, sections())) headersNative := dumpHeaders(t, outNative) headersObjcopy := dumpHeaders(t, outObjcopy) // we don't compute the checksums, so ignore these fields headersObjcopy = regexp.MustCompile(`(CheckSum\s+)[0-9a-fA-F]+`).ReplaceAllString(headersObjcopy, "${1}00000000") // we don't set linker version headersObjcopy = regexp.MustCompile(`((Major|Minor)LinkerVersion\s+)[0-9.]+`).ReplaceAllString(headersObjcopy, "${1}0") assert.Equal(t, headersObjcopy, headersNative) for _, sectionName := range []string{ ".text", ".rodata", ".data", ".sbat", ".sdmagic", ".reloc", ".uname", ".linux", } { sectionObjcopy := extractSection(t, outObjcopy, sectionName) sectionNative := extractSection(t, outNative, sectionName) assert.Equal(t, sectionObjcopy, sectionNative) } if false { // deep binary comparison, disabled by default, as there will be some difference always binaryObjcopy := binaryDump(t, outObjcopy) binaryNative := binaryDump(t, outNative) assert.Equal(t, binaryObjcopy, binaryNative) } } func dumpHeaders(t *testing.T, path string) string { t.Helper() output, err := exec.CommandContext(t.Context(), "objdump", "-x", path).CombinedOutput() require.NoError(t, err, string(output)) output = bytes.ReplaceAll(output, []byte(path), []byte("uki.bin")) return string(output) } func binaryDump(t *testing.T, path string) string { t.Helper() output, err := exec.CommandContext(t.Context(), "xxd", path).CombinedOutput() require.NoError(t, err, string(output)) return string(output) } func extractSection(t *testing.T, path, section string) string { t.Helper() output, err := exec.CommandContext(t.Context(), "objdump", "-s", "--section", section, path).CombinedOutput() require.NoError(t, err, string(output)) output = bytes.ReplaceAll(output, []byte(path), []byte("uki.bin")) return string(output) } func TestMultipleSections(t *testing.T) { assertToolsPresent(t) tmpDir := t.TempDir() unamePath := filepath.Join(tmpDir, "uname") require.NoError(t, os.WriteFile(unamePath, []byte("Talos"), 0o644)) unameNewPath := filepath.Join(tmpDir, "uname-new") require.NoError(t, os.WriteFile(unameNewPath, []byte("Talos-new"), 0o644)) outNative := filepath.Join(tmpDir, "uki-native.bin") sections := func() []pe.Section { return []pe.Section{ { Name: ".text", }, { Name: ".uname", Append: true, Path: unamePath, }, { Name: ".uname", Append: true, Path: unameNewPath, }, } } require.NoError(t, pe.AssembleNative("testdata/linuxx64.efi.stub", outNative, sections())) sectionContents := extractSection(t, outNative, ".uname") assert.Contains(t, sectionContents, "Talos") assert.Contains(t, sectionContents, "Talos-new") } ================================================ FILE: internal/pkg/uki/kernel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki import ( "bytes" "encoding/binary" "errors" "os" "strings" ) // DiscoverKernelVersion reads kernel version from the kernel image. // // This only works for x86 kernel images. // // Based on https://www.kernel.org/doc/html/v5.6/x86/boot.html. func DiscoverKernelVersion(kernelPath string) (string, error) { f, err := os.Open(kernelPath) if err != nil { return "", err } defer f.Close() //nolint:errcheck header := make([]byte, 1024) _, err = f.Read(header) if err != nil { return "", err } // check header magic if string(header[0x202:0x206]) != "HdrS" { return "", errors.New("invalid kernel image") } setupSects := header[0x1f1] versionOffset := binary.LittleEndian.Uint16(header[0x20e:0x210]) if versionOffset == 0 { return "", errors.New("no kernel version") } if versionOffset > uint16(setupSects)*0x200 { return "", errors.New("invalid kernel version offset") } versionOffset += 0x200 version := make([]byte, 256) _, err = f.ReadAt(version, int64(versionOffset)) if err != nil { return "", err } idx := bytes.IndexByte(version, 0) //nolint:modernize // this way is better if idx == -1 { return "", errors.New("invalid kernel version") } versionString := string(version[:idx]) versionString, _, _ = strings.Cut(versionString, " ") return versionString, nil } ================================================ FILE: internal/pkg/uki/kernel_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/uki" ) func TestKernelVersion(t *testing.T) { version, err := uki.DiscoverKernelVersion("testdata/kernel") require.NoError(t, err) assert.Equal(t, "6.1.58-talos", version) } ================================================ FILE: internal/pkg/uki/sbat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki import ( "debug/pe" "errors" ) // GetSBAT returns the SBAT section from the PE file. func GetSBAT(path string) ([]byte, error) { pefile, err := pe.Open(path) if err != nil { return nil, err } defer pefile.Close() //nolint:errcheck if sectionData := pefile.Section(SectionSBAT.String()); sectionData != nil { data, err := sectionData.Data() if err != nil { return nil, err } return data[:sectionData.VirtualSize], nil } return nil, errors.New("could not find SBAT section") } ================================================ FILE: internal/pkg/uki/sbat_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/uki" ) func TestGetSBAT(t *testing.T) { t.Parallel() data, err := uki.GetSBAT("internal/pe/testdata/linuxx64.efi.stub") require.NoError(t, err) require.Equal(t, "sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\nsystemd-stub,1,The systemd Developers,systemd,257,https://systemd.io/\nsystemd-stub.talos,1,Talos Linux,systemd,257.2257.2,https://github.com/siderolabs/tools/issues\n", //nolint:lll string(data), ) } ================================================ FILE: internal/pkg/uki/uki.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package uki creates the UKI file out of the sd-stub and other sections. package uki import ( "fmt" "log" "os" "github.com/siderolabs/talos/internal/pkg/measure" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" "github.com/siderolabs/talos/internal/pkg/uki/internal/pe" "github.com/siderolabs/talos/pkg/imager/utils" ) // Section is a name of a PE file section (UEFI binary). type Section string // List of well-known section names. const ( SectionLinux Section = ".linux" SectionOSRel Section = ".osrel" SectionCmdline Section = ".cmdline" SectionInitrd Section = ".initrd" SectionUcode Section = ".ucode" SectionSplash Section = ".splash" SectionDTB Section = ".dtb" SectionUname Section = ".uname" SectionSBAT Section = ".sbat" SectionPCRSig Section = ".pcrsig" SectionPCRPKey Section = ".pcrpkey" SectionProfile Section = ".profile" SectionDTBAuto Section = ".dtbauto" SectionHWIDS Section = ".hwids" ) // String returns the string representation of the section. func (s Section) String() string { return string(s) } type section = pe.Section // Builder is a UKI file builder. type Builder struct { // Source options. // // Arch of the UKI file. Arch string // Version of Talos. Version string // Path to the sd-stub. SdStubPath string // Path to the sd-boot. SdBootPath string // Path to the kernel image. KernelPath string // Path to the initrd image. InitrdPath string // Kernel cmdline. Cmdline string // SecureBoot certificate and signer. SecureBootSigner pesign.CertificateSigner // PCR signer. PCRSigner measure.RSAKey // Profiles to include in the UKI. Profiles []Profile // Output options: // // Path to the signed sd-boot. OutSdBootPath string // Path to the output UKI file. OutUKIPath string // fields initialized during build sections []section scratchDir string peSigner *pesign.Signer unsignedUKIPath string } // Profile is a UKI Profile. // For now only cmdline is supported. type Profile struct { ID string Title string Cmdline string } // String returns the string representation of the profile that gets adds to the `.profile` section. func (p Profile) String() string { s := fmt.Sprintf("ID=%s", p.ID) if p.Title != "" { s += fmt.Sprintf("\nTITLE=%s", p.Title) } return s } // Build the unsigned UKI file. // // Build process is as follows: // - build ephemeral sections (uname, os-release), and other proposed sections // - assemble the final UKI file starting from sd-stub and appending generated section. func (builder *Builder) Build(printf func(string, ...any)) error { var err error builder.scratchDir, err = os.MkdirTemp("", "talos-uki") if err != nil { return err } defer func() { if err = os.RemoveAll(builder.scratchDir); err != nil { log.Printf("failed to remove scratch dir: %v", err) } }() if err := utils.CopyFiles(printf, utils.SourceDestination(builder.SdBootPath, builder.OutSdBootPath)); err != nil { return err } printf("generating UKI sections") // generate and build list of all sections for _, generateSection := range []func() error{ builder.generateSBAT, builder.generateOSRel, builder.generateCmdline, builder.generateUname, builder.generateSplash, builder.generateKernel, builder.generateInitrd, builder.generateProfiles, } { if err = generateSection(); err != nil { return fmt.Errorf("error generating sections: %w", err) } } printf("assembling UKI") // assemble the final UKI file if err = builder.assemble(); err != nil { return fmt.Errorf("error assembling UKI: %w", err) } return utils.CopyFiles(printf, utils.SourceDestination(builder.unsignedUKIPath, builder.OutUKIPath)) } // BuildSigned the UKI file. // // BuildSigned process is as follows: // - sign the sd-boot EFI binary, and write it to the OutSdBootPath // - build ephemeral sections (uname, os-release), and other proposed sections // - measure sections, generate signature, and append to the list of sections // - assemble the final UKI file starting from sd-stub and appending generated section. func (builder *Builder) BuildSigned(printf func(string, ...any)) error { var err error builder.scratchDir, err = os.MkdirTemp("", "talos-uki") if err != nil { return err } defer func() { if err = os.RemoveAll(builder.scratchDir); err != nil { log.Printf("failed to remove scratch dir: %v", err) } }() printf("signing systemd-boot") builder.peSigner, err = pesign.NewSigner(builder.SecureBootSigner) if err != nil { return fmt.Errorf("error initializing signer: %w", err) } // sign sd-boot if err = builder.peSigner.Sign(builder.SdBootPath, builder.OutSdBootPath); err != nil { return fmt.Errorf("error signing sd-boot: %w", err) } printf("generating UKI sections") // generate and build list of all sections for _, generateSection := range []func() error{ builder.generateSBAT, builder.generateOSRel, builder.generateCmdline, builder.generateUname, builder.generateSplash, builder.generatePCRPublicKey, builder.generateKernel, builder.generateInitrd, builder.generateProfiles, builder.generatePCRSig, } { if err = generateSection(); err != nil { return fmt.Errorf("error generating sections: %w", err) } } printf("assembling UKI") // assemble the final UKI file if err = builder.assemble(); err != nil { return fmt.Errorf("error assembling UKI: %w", err) } printf("signing UKI") // sign the UKI file return builder.peSigner.Sign(builder.unsignedUKIPath, builder.OutUKIPath) } // Extract extracts the kernel, initrd, and cmdline from the UKI file. func Extract(ukiPath string) (asset pe.AssetInfo, err error) { return pe.Extract(ukiPath) } ================================================ FILE: internal/pkg/uki/uki_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package uki_test import ( "crypto" "crypto/rsa" stdx509 "crypto/x509" "debug/pe" "encoding/pem" "fmt" "io" "os" "os/exec" "path/filepath" "strings" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/pkg/measure" "github.com/siderolabs/talos/internal/pkg/uki" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/splash" ) type certificateProvider struct { *x509.CertificateAuthority } func (c *certificateProvider) Signer() crypto.Signer { return c.CertificateAuthority.Key.(crypto.Signer) } func (c *certificateProvider) Certificate() *stdx509.Certificate { return c.CertificateAuthority.Crt } type rsaWrapper struct { *rsa.PrivateKey } func (w rsaWrapper) PublicRSAKey() *rsa.PublicKey { return &w.PrivateKey.PublicKey } func loadRSAKey(path string) (measure.RSAKey, error) { keyData, err := os.ReadFile(path) if err != nil { return nil, err } // convert private key to rsa.PrivateKey rsaPrivateKeyBlock, _ := pem.Decode(keyData) if rsaPrivateKeyBlock == nil { return nil, err } rsaKey, err := stdx509.ParsePKCS1PrivateKey(rsaPrivateKeyBlock.Bytes) if err != nil { return nil, fmt.Errorf("parse private key failed: %v", err) } return rsaWrapper{rsaKey}, nil } // TestBuildUKI tests the UKI build process for both normal and signed UKI with and without multi profile ukis. // It uses the UKI build process from the uki package and the ukify tool to // generate a UKI file and then compares the generated UKI file with the UKI // file generated by the ukify tool. // This test requires a patched version of ukify that allows `enter-machined` as a phase and drop the trailing null character from sbat // since Talos UKI generation doesn't update sbat // diff --git src/ukify/ukify.py src/ukify/ukify.py // index 639301bdb6..0ab9394c2b 100755 // --- src/ukify/ukify.py // +++ src/ukify/ukify.py // @@ -622,6 +622,7 @@ def parse_banks(s: str) -> list[str]: // // KNOWN_PHASES = ( // 'enter-initrd', // 'leave-initrd', // - 'enter-machined', // 'sysinit', // 'ready', // 'shutdown', // // @@ -1046,7 +1047,7 @@ def merge_sbat(input_pe: list[Path], input_text: list[str]) -> str: // // return ( // 'sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n' // + '\n'.join(sbat) // - + '\n\x00' // - + '\n' // ) func TestBuildUKI(t *testing.T) { requiredTools := []string{ "ukify", "sbverify", "sbsign", "systemd-measure", } allToolsFound := true for _, tool := range requiredTools { if _, err := exec.LookPath(tool); err != nil { t.Logf("tool %s not found", tool) allToolsFound = false } } if !allToolsFound { t.Skipf("skipping test as some required tools %s not found", strings.Join(requiredTools, ", ")) } currentTime := time.Now() opts := []x509.Option{ x509.RSA(true), x509.Bits(2048), x509.CommonName("test-sign"), x509.NotAfter(currentTime.Add(secrets.CAValidityTime)), x509.NotBefore(currentTime), x509.Organization("test-sign"), } signingKey, err := x509.NewSelfSignedCertificateAuthority(opts...) require.NoError(t, err) pcrSigningKey := "../measure/testdata/pcr-signing-key.pem" rsaKey, err := loadRSAKey(pcrSigningKey) if err != nil { t.Fatal(err) } tempDir := t.TempDir() publicKeyBytes, err := stdx509.MarshalPKIXPublicKey(rsaKey.PublicRSAKey()) require.NoError(t, err) publicKeyPEM := pem.EncodeToMemory(&pem.Block{ Type: x509.PEMTypeRSAPublic, Bytes: publicKeyBytes, }) pcrPubKey := filepath.Join(tempDir, "pcr-public.pem") require.NoError(t, os.WriteFile(pcrPubKey, publicKeyPEM, 0o600)) sdBootSigned := filepath.Join(tempDir, "sd-boot-signed.efi") kernel := "testdata/kernel" initrd := "testdata/kernel" // we don't have a real initrd file, so we use the kernel file cmdline := "console=ttyS0" splashImage := filepath.Join(tempDir, "splash") osReleaseFile := filepath.Join(tempDir, "os-release") secureBootSingingKey := filepath.Join(tempDir, "sb-key.pem") secureBootCertificate := filepath.Join(tempDir, "sb-cert.pem") sdStubPath := "internal/pe/testdata/linuxx64.efi.stub" addonStubPath := "internal/pe/testdata/addonx64.efi.stub" ukiProfiles := []uki.Profile{ { ID: "reset-maintenance", Title: "Reset to maintenance mode", Cmdline: cmdline + fmt.Sprintf(" %s=system:EPHEMERAL,STATE", constants.KernelParamWipe), }, { ID: "reset", Title: "Reset system disk", Cmdline: cmdline + fmt.Sprintf(" %s=system", constants.KernelParamWipe), }, } for _, talosVersion := range []string{"1.9.0", "1.10.0"} { ukiUnsigned := filepath.Join(tempDir, fmt.Sprintf("uki-%s.efi", talosVersion)) ukiSigned := filepath.Join(tempDir, fmt.Sprintf("uki-%s-signed.efi", talosVersion)) ukifyUKIUnsigned := filepath.Join(tempDir, fmt.Sprintf("uki-ukify-%s.efi", talosVersion)) ukifyUKISigned := filepath.Join(tempDir, fmt.Sprintf("uki-ukify-signed-%s.efi", talosVersion)) builder := &uki.Builder{ Arch: "amd64", Version: talosVersion, SdStubPath: sdStubPath, SdBootPath: sdStubPath, // this doesn't matter for the test KernelPath: kernel, InitrdPath: initrd, Cmdline: cmdline, Profiles: ukiProfiles, OutSdBootPath: sdBootSigned, OutUKIPath: ukiUnsigned, } require.NoError(t, builder.Build(t.Logf)) signedBuilder := &uki.Builder{ Arch: "amd64", Version: talosVersion, SdStubPath: sdStubPath, SdBootPath: sdStubPath, // this doesn't matter for the test KernelPath: kernel, InitrdPath: initrd, Cmdline: cmdline, SecureBootSigner: &certificateProvider{signingKey}, PCRSigner: rsaKey, Profiles: ukiProfiles, OutSdBootPath: sdBootSigned, OutUKIPath: ukiSigned, } require.NoError(t, signedBuilder.BuildSigned(t.Logf)) require.NoError(t, os.WriteFile(splashImage, splash.GetBootImage(), 0o600)) osRelease, err := version.OSReleaseFor(version.Name, builder.Version) require.NoError(t, err) require.NoError(t, os.WriteFile(osReleaseFile, osRelease, 0o600)) kernelVersion, err := uki.DiscoverKernelVersion(builder.KernelPath) require.NoError(t, err) require.NoError(t, os.WriteFile(secureBootCertificate, signingKey.CrtPEM, 0o600)) require.NoError(t, os.WriteFile(secureBootSingingKey, signingKey.KeyPEM, 0o600)) ukifyCmdArgs := []string{ "build", "--stub", builder.SdStubPath, "--sbat", "sbat,", // this is a hack so that ukify doesn't add extra sbat info "--splash", splashImage, "--linux", kernel, "--initrd", initrd, "--os-release", "@" + osReleaseFile, "--uname", kernelVersion, "--cmdline", cmdline, "--output", ukifyUKIUnsigned, } ukifySignedCmdArgs := []string{ "build", "--stub", signedBuilder.SdStubPath, "--sbat", "sbat,", // this is a hack so that ukify doesn't add extra sbat info "--splash", splashImage, "--linux", kernel, "--initrd", initrd, "--os-release", "@" + osReleaseFile, "--uname", kernelVersion, "--cmdline", cmdline, "--pcr-private-key", pcrSigningKey, "--pcrpkey", pcrPubKey, "--secureboot-private-key", secureBootSingingKey, "--secureboot-certificate", secureBootCertificate, "--pcr-banks", "sha256,sha384,sha512", "--phases", "enter-initrd:leave-initrd:enter-machined", "--output", ukifyUKISigned, } if quirks.New(talosVersion).SupportsUKIProfiles() { resetMaintenanceProfile := filepath.Join(tempDir, "profile0.efi") resetProfile := filepath.Join(tempDir, "profile1.efi") ukifyResetMaintenanceProfileCmd := exec.CommandContext( t.Context(), "ukify", []string{ "build", "--stub", addonStubPath, "--profile", "ID=reset-maintenance\nTITLE=Reset to maintenance mode", "--cmdline", cmdline + fmt.Sprintf(" %s=system:EPHEMERAL,STATE", constants.KernelParamWipe), "--output", resetMaintenanceProfile, }..., ) ukifyResetMaintenanceProfileCmd.Stderr = os.Stderr ukifyResetMaintenanceProfileCmd.Stdout = os.Stdout t.Log("Running ukify command:", ukifyResetMaintenanceProfileCmd.String()) require.NoError(t, ukifyResetMaintenanceProfileCmd.Run()) ukifyResetProfileCmd := exec.CommandContext( t.Context(), "ukify", []string{ "build", "--stub", addonStubPath, "--profile", "ID=reset\nTITLE=Reset system disk", "--cmdline", cmdline + fmt.Sprintf(" %s=system", constants.KernelParamWipe), "--output", resetProfile, }..., ) ukifyResetProfileCmd.Stderr = os.Stderr ukifyResetProfileCmd.Stdout = os.Stdout t.Log("Running ukify command:", ukifyResetProfileCmd.String()) require.NoError(t, ukifyResetProfileCmd.Run()) ukifyCmdArgs = append(ukifyCmdArgs, "--join-profile", resetMaintenanceProfile, "--join-profile", resetProfile, ) ukifySignedCmdArgs = append(ukifySignedCmdArgs, "--join-profile", resetMaintenanceProfile, "--join-profile", resetProfile, ) } ukifyCmd := exec.CommandContext( t.Context(), "ukify", ukifyCmdArgs..., ) ukifyCmd.Stderr = os.Stderr ukifyCmd.Stdout = os.Stdout t.Log("Running ukify command:", ukifyCmd.String()) require.NoError(t, ukifyCmd.Run()) compareUKIFiles(t, ukiUnsigned, ukifyUKIUnsigned) ukifySignedCmd := exec.CommandContext( t.Context(), "ukify", ukifySignedCmdArgs..., ) ukifySignedCmd.Stderr = os.Stderr ukifySignedCmd.Stdout = os.Stdout t.Log("Running ukify command:", ukifySignedCmd.String()) require.NoError(t, ukifySignedCmd.Run()) compareUKIFiles(t, ukiSigned, ukifyUKISigned) } } func compareUKIFiles(t *testing.T, ukiNative, ukiUKIFY string) { ukiData, err := pe.Open(ukiNative) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, ukiData.Close()) }) ukiDataUKIFY, err := pe.Open(ukiUKIFY) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, ukiDataUKIFY.Close()) }) if len(ukiData.Sections) != len(ukiDataUKIFY.Sections) { t.Fatalf("sections count mismatch: %d != %d", len(ukiData.Sections), len(ukiDataUKIFY.Sections)) } for i, section := range ukiData.Sections { sectionUKIFY := ukiDataUKIFY.Sections[i] if section.Name != sectionUKIFY.Name { t.Fatalf("section name mismatch: %s != %s", section.Name, sectionUKIFY.Name) } sectionReader := io.LimitReader(section.Open(), int64(section.VirtualSize)) sectionReaderUKIFY := io.LimitReader(sectionUKIFY.Open(), int64(sectionUKIFY.VirtualSize)) var sectionData, sectionDataUKIFY strings.Builder _, err := io.Copy(§ionData, sectionReader) require.NoError(t, err) _, err = io.Copy(§ionDataUKIFY, sectionReaderUKIFY) require.NoError(t, err) expected := sectionData.String() actual := sectionDataUKIFY.String() if section.Name == ".pcrsig" { expected = strings.ReplaceAll(expected, " ", "") actual = strings.ReplaceAll(actual, " ", "") } require.Equal(t, expected, actual, "section %s at index %d differs", section.Name, i) } } ================================================ FILE: internal/pkg/zboot/zboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package zboot provides a function to extract the kernel from a Zboot image. package zboot import ( "bytes" "encoding/binary" "fmt" "io" "os" "github.com/klauspost/compress/zstd" "golang.org/x/sys/unix" ) // Extract extracts the kernel from a Zboot image and returns a file descriptor of the extracted kernel. func Extract(kernel *os.File) (int, io.Closer, error) { // https://git.kernel.org/pub/scm/utils/kernel/kexec/kexec-tools.git/tree/include/kexec-pe-zboot.h var peZbootheaderData [28]byte if _, err := io.ReadFull(kernel, peZbootheaderData[:]); err != nil { return 0, nil, err } // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S // https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/pe.h#n42 if !bytes.Equal(peZbootheaderData[:2], []byte("MZ")) { return 0, nil, fmt.Errorf("invalid PE Zboot header") } // not a Zboot image, return if !bytes.Equal(peZbootheaderData[4:8], []byte("zimg")) { return int(kernel.Fd()), nil, nil } payloadOffset := binary.LittleEndian.Uint32(peZbootheaderData[8:12]) payloadSize := binary.LittleEndian.Uint32(peZbootheaderData[12:16]) if _, err := kernel.Seek(int64(payloadOffset), io.SeekStart); err != nil { return 0, nil, fmt.Errorf("failed to seek to kernel zstd data from vmlinuz.efi: %w", err) } z, err := zstd.NewReader(io.LimitReader(kernel, int64(payloadSize))) if err != nil { return 0, nil, fmt.Errorf("failed to create zstd reader: %w", err) } defer z.Close() fd, err := unix.MemfdCreate("vmlinux", 0) if err != nil { return 0, nil, fmt.Errorf("memfdCreate: %v", err) } kernelMemfd := os.NewFile(uintptr(fd), "vmlinux") if _, err := io.Copy(kernelMemfd, z); err != nil { kernelMemfd.Close() //nolint:errcheck return 0, nil, fmt.Errorf("failed to copy zstd data to memfd: %w", err) } if _, err := kernelMemfd.Seek(0, io.SeekStart); err != nil { kernelMemfd.Close() //nolint:errcheck return 0, nil, fmt.Errorf("failed to seek to start of memfd: %w", err) } return fd, kernelMemfd, nil } ================================================ FILE: pkg/archiver/archiver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package archiver provides a service to archive part of the filesystem into tar archive. package archiver import ( "compress/gzip" "context" "io" ) // TarGz produces .tar.gz archive of filesystem starting at rootPath. func TarGz(ctx context.Context, rootPath string, output io.Writer, walkerOptions ...WalkerOption) error { paths, err := Walker(ctx, rootPath, append(walkerOptions, WithSkipRoot())...) if err != nil { return err } zw := gzip.NewWriter(output) //nolint:errcheck defer zw.Close() err = Tar(ctx, paths, zw) if err != nil { return err } return zw.Close() } // UntarGz extracts .tar.gz archive to the rootPath. func UntarGz(ctx context.Context, input io.Reader, rootPath string) error { zr, err := gzip.NewReader(input) if err != nil { return err } //nolint:errcheck defer zr.Close() err = Untar(ctx, zr, rootPath) if err != nil { return err } return zr.Close() } ================================================ FILE: pkg/archiver/archiver_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver_test import ( "bytes" "os" "path/filepath" "slices" "github.com/stretchr/testify/suite" ) type CommonSuite struct { suite.Suite tmpDir string } var filesFixture = []struct { Path string Mode os.FileMode Contents []byte Size int }{ { Path: "/etc/hostname", Mode: 0o644, Contents: []byte("localhost"), }, { Path: "/etc/certs/ca.crt", Mode: 0o600, Contents: []byte("-- CA PEM CERT -- VERY SECRET"), }, { Path: "/dev/random", Mode: 0o600 | os.ModeDevice | os.ModeCharDevice, }, { Path: "/usr/bin/cp", Mode: 0o755, Contents: []byte("ELF EXECUTABLE IIRC"), }, { Path: "/usr/bin/mv", Mode: 0o644 | os.ModeSymlink, Contents: []byte("/usr/bin/cp"), }, { Path: "/lib/dynalib.so", Mode: 0o644, Contents: []byte("SOME LIBRARY OUT THERE"), Size: 20 * 1024, }, } func (suite *CommonSuite) SetupSuite() { suite.tmpDir = suite.T().TempDir() for _, file := range filesFixture { suite.Require().NoError(os.MkdirAll(filepath.Join(suite.tmpDir, filepath.Dir(file.Path)), 0o777)) if file.Mode&os.ModeSymlink != 0 { suite.Require().NoError(os.Symlink(string(file.Contents), filepath.Join(suite.tmpDir, file.Path))) continue } f, err := os.OpenFile(filepath.Join(suite.tmpDir, file.Path), os.O_CREATE|os.O_WRONLY, file.Mode) suite.Require().NoError(err) var contents []byte if file.Size > 0 { contents = slices.Concat( bytes.Repeat(file.Contents, file.Size/len(file.Contents)), file.Contents[:file.Size-file.Size/len(file.Contents)*len(file.Contents)], ) } else { contents = file.Contents } _, err = f.Write(contents) suite.Require().NoError(err) suite.Require().NoError(f.Close()) } } ================================================ FILE: pkg/archiver/tar.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver import ( "archive/tar" "bytes" "context" "errors" "fmt" "io" "log" "os" "slices" "syscall" multierror "github.com/hashicorp/go-multierror" ) // Tar creates .tar archive and writes it to output for every item in paths channel. func Tar(ctx context.Context, paths <-chan FileItem, output io.Writer) error { tw := tar.NewWriter(output) //nolint:errcheck defer tw.Close() var multiErr *multierror.Error buf := make([]byte, 4096) for fi := range paths { if fi.Error != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("skipping %q: %s", fi.FullPath, fi.Error)) continue } err := processFile(ctx, tw, fi, buf) if err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("skipping %q: %s", fi.FullPath, err)) } } if err := tw.Close(); err != nil { multiErr = multierror.Append(multiErr, err) } return multiErr.ErrorOrNil() } //nolint:gocyclo func processFile(ctx context.Context, tw *tar.Writer, fi FileItem, buf []byte) error { header, err := tar.FileInfoHeader(fi.FileInfo, fi.Link) if err != nil { // not supported by tar return err } header.Name = fi.RelPath if fi.FileInfo.IsDir() { header.Name += string(os.PathSeparator) } skipData := false switch header.Typeflag { case tar.TypeLink, tar.TypeSymlink, tar.TypeChar, tar.TypeBlock, tar.TypeDir, tar.TypeFifo: // no data for these types, move on skipData = true } var r io.Reader if !skipData { var fp *os.File fp, err = os.Open(fi.FullPath) if err != nil { return err } defer fp.Close() //nolint:errcheck r = fp } if !skipData && header.Size == 0 { // Linux reports /proc files as zero length, but they might have data, // so we try to read limited amount of data from it to determine the size var n int n, err = r.Read(buf) switch { case err == io.EOF: // file is empty for real skipData = true case err != nil: // error reading from the file if errors.Is(err, syscall.EINVAL) { // some files are not supported by os.Open, e.g. /proc/sys/net/ipv4/conf/all/accept_local skipData = true } else { return err } case n < len(buf): header.Size = int64(n) r = bytes.NewReader(slices.Clone(buf[:n])) default: // none matched so the file is bigger than we expected, ignore it and copy as zero size skipData = true } } err = tw.WriteHeader(header) if err != nil { return err } if skipData { return nil } return archiveFile(ctx, tw, fi, r, buf) } func archiveFile(ctx context.Context, tw io.Writer, fi FileItem, r io.Reader, buf []byte) error { for { n, err := r.Read(buf) if err != nil { if err == io.EOF { return nil } return err } select { case <-ctx.Done(): return ctx.Err() default: } _, err = tw.Write(buf[:n]) if err != nil { if err == tar.ErrWriteTooLong { log.Printf("ignoring long write for %q", fi.FullPath) return nil } return err } } } ================================================ FILE: pkg/archiver/tar_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver_test import ( "archive/tar" "bytes" "context" "io" "os" "path/filepath" "testing" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/archiver" ) type TarSuite struct { CommonSuite } func (suite *TarSuite) TestArchiveDir() { ch, err := archiver.Walker(context.Background(), suite.tmpDir) suite.Require().NoError(err) var buf bytes.Buffer err = archiver.Tar(context.Background(), ch, &buf) suite.Require().NoError(err) pathsSeen := map[string]struct{}{} tr := tar.NewReader(&buf) for { hdr, err := tr.Next() if err == io.EOF { break } suite.Require().NoError(err) if hdr.Typeflag == tar.TypeDir { continue } contents, err := io.ReadAll(tr) suite.Require().NoError(err) found := false for _, fi := range filesFixture { if fi.Path[1:] == hdr.Name { found = true pathsSeen[fi.Path] = struct{}{} switch { case fi.Mode&os.ModeSymlink != 0: suite.Require().Equal(string(fi.Contents), hdr.Linkname) case fi.Size > 0: suite.Require().Len(contents, fi.Size) case fi.Contents != nil: suite.Require().EqualValues(fi.Contents, contents) default: suite.Require().Len(contents, 0) } } } suite.Require().True(found, "file %q", hdr.Name) } for _, fi := range filesFixture { _, ok := pathsSeen[fi.Path] suite.Require().True(ok, "path %q", fi.Path) } } func (suite *TarSuite) TestArchiveFile() { ch, err := archiver.Walker(context.Background(), filepath.Join(suite.tmpDir, "/usr/bin/cp")) suite.Require().NoError(err) var buf bytes.Buffer err = archiver.Tar(context.Background(), ch, &buf) suite.Require().NoError(err) tr := tar.NewReader(&buf) for { hdr, err := tr.Next() if err == io.EOF { break } suite.Require().NoError(err) expectedContents := []byte("ELF EXECUTABLE IIRC") suite.Require().EqualValues(hdr.Typeflag, tar.TypeReg) suite.Require().EqualValues(hdr.Name, "cp") suite.Require().EqualValues(hdr.Size, len(expectedContents)) contents, err := io.ReadAll(tr) suite.Require().NoError(err) suite.Require().Equal(expectedContents, contents) } } func (suite *TarSuite) TestArchiveProcfs() { ch, err := archiver.Walker(context.Background(), "/proc/self/", archiver.WithMaxRecurseDepth(0)) suite.Require().NoError(err) var buf bytes.Buffer // it's okay to have some errors here, as some files are not readable archiver.Tar(context.Background(), ch, &buf) //nolint:errcheck tr := tar.NewReader(&buf) expectedNonZeroFiles := map[string]struct{}{ "cmdline": {}, "environ": {}, "limits": {}, "io": {}, "stat": {}, } for { hdr, err := tr.Next() if err == io.EOF { break } suite.Require().NoError(err) if _, expected := expectedNonZeroFiles[hdr.Name]; !expected { continue } suite.Assert().EqualValues(hdr.Typeflag, tar.TypeReg) suite.Assert().NotZero(hdr.Size) delete(expectedNonZeroFiles, hdr.Name) } suite.Assert().Empty(expectedNonZeroFiles) } func TestTarSuite(t *testing.T) { suite.Run(t, new(TarSuite)) } ================================================ FILE: pkg/archiver/untar.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver import ( "archive/tar" "context" "errors" "fmt" "io" "os" "path/filepath" "github.com/pkg/xattr" "github.com/siderolabs/talos/pkg/safepath" ) // Untar extracts .tar archive from r into filesystem under rootPath. // //nolint:gocyclo func Untar(ctx context.Context, r io.Reader, rootPath string) error { tr := tar.NewReader(r) for { select { case <-ctx.Done(): return ctx.Err() default: } hdr, err := tr.Next() if err != nil { if err == io.EOF { break } return fmt.Errorf("error reading tar header: %s", err) } hdrPath := safepath.CleanPath(hdr.Name) if hdrPath == "" { return errors.New("empty tar header path") } path := filepath.Join(rootPath, hdrPath) switch hdr.Typeflag { case tar.TypeDir: mode := hdr.FileInfo().Mode() & os.ModePerm mode |= 0o700 // make rwx for the owner if err = os.Mkdir(path, mode); err != nil && !os.IsExist(err) { return fmt.Errorf("error creating directory %q mode %s: %w", path, mode, err) } if err = os.Chmod(path, mode); err != nil { return fmt.Errorf("error updating mode %s for %q: %w", mode, path, err) } case tar.TypeSymlink: if err = os.Symlink(hdr.Linkname, path); err != nil { return fmt.Errorf("error creating symlink %q -> %q: %w", path, hdr.Linkname, err) } default: mode := hdr.FileInfo().Mode() fp, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_EXCL, mode) if err != nil { return fmt.Errorf("error creating file %q mode %s: %w", path, mode, err) } _, err = io.Copy(fp, tr) if err != nil { return fmt.Errorf("error copying data to %q: %w", path, err) } if err = fp.Close(); err != nil { return fmt.Errorf("error closing %q: %w", path, err) } if err = os.Chmod(path, mode); err != nil { return fmt.Errorf("error updating mode %s for %q: %w", mode, path, err) } } if hdr.PAXRecords["SCHILY.xattr.security.selinux"] != "" { if err = xattr.LSet(path, "security.selinux", []byte(hdr.PAXRecords["SCHILY.xattr.security.selinux"])); err != nil { return fmt.Errorf("error setting selinux xattr for %q: %w", path, err) } } } return nil } ================================================ FILE: pkg/archiver/walker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver import ( "context" "os" "path/filepath" "strings" ) // FileItem is unit of work for archive. type FileItem struct { FullPath string RelPath string FileInfo os.FileInfo Link string Error error } // FileType is a file type. type FileType int // File types. const ( RegularFileType FileType = iota DirectoryFileType SymlinkFileType ) type walkerOptions struct { skipRoot bool maxRecurseDepth int fnmatchPatterns []string types map[FileType]struct{} } // WalkerOption configures Walker. type WalkerOption func(*walkerOptions) // WithSkipRoot skips root path if it's a directory. func WithSkipRoot() WalkerOption { return func(o *walkerOptions) { o.skipRoot = true } } // WithMaxRecurseDepth controls maximum recursion depth while walking file tree. // // Value of -1 disables depth control. func WithMaxRecurseDepth(maxDepth int) WalkerOption { return func(o *walkerOptions) { o.maxRecurseDepth = maxDepth } } // WithFnmatchPatterns filters results to match the patterns. // // Default is not to do any filtering. func WithFnmatchPatterns(patterns ...string) WalkerOption { return func(o *walkerOptions) { o.fnmatchPatterns = append(o.fnmatchPatterns, patterns...) } } // WithFileTypes filters results by file types. // // Default is not to do any filtering. func WithFileTypes(fileType ...FileType) WalkerOption { return func(o *walkerOptions) { o.types = make(map[FileType]struct{}, len(fileType)) for _, t := range fileType { o.types[t] = struct{}{} } } } // Walker provides a channel of file info/paths for archival. // //nolint:gocyclo,cyclop func Walker(ctx context.Context, rootPath string, options ...WalkerOption) (<-chan FileItem, error) { var opts walkerOptions opts.maxRecurseDepth = -1 for _, o := range options { o(&opts) } info, err := os.Lstat(rootPath) if err != nil { return nil, err } if info.Mode()&os.ModeSymlink == os.ModeSymlink { rootPath, err = filepath.EvalSymlinks(rootPath) if err != nil { return nil, err } } ch := make(chan FileItem) go func() { defer close(ch) err := filepath.Walk(rootPath, func(path string, fileInfo os.FileInfo, walkErr error) error { item := FileItem{ FullPath: path, FileInfo: fileInfo, Error: walkErr, } if path == rootPath && !fileInfo.IsDir() { // only one file item.RelPath = filepath.Base(path) } else if item.Error == nil { item.RelPath, item.Error = filepath.Rel(rootPath, path) } // TODO: refactor all those `if item.Error == nil &&` conditions if item.Error == nil && len(opts.types) > 0 { var matches bool for t := range opts.types { switch t { case RegularFileType: matches = fileInfo.Mode()&os.ModeType == 0 case DirectoryFileType: matches = fileInfo.Mode()&os.ModeDir != 0 case SymlinkFileType: matches = fileInfo.Mode()&os.ModeSymlink != 0 } if matches { break } } if !matches { return nil } } if item.Error == nil && path == rootPath && opts.skipRoot && fileInfo.IsDir() { // skip containing directory return nil } if item.Error == nil && fileInfo.Mode()&os.ModeSymlink == os.ModeSymlink { item.Link, item.Error = os.Readlink(path) } if item.Error == nil && len(opts.fnmatchPatterns) > 0 { var matches bool for _, pattern := range opts.fnmatchPatterns { if matches, _ = filepath.Match(pattern, item.RelPath); matches { //nolint:errcheck break } } if !matches { return nil } } select { case <-ctx.Done(): return ctx.Err() case ch <- item: } if item.Error == nil && fileInfo.IsDir() && atMaxDepth(opts.maxRecurseDepth, rootPath, path) { return filepath.SkipDir } return nil }) if err != nil { select { case <-ctx.Done(): case ch <- FileItem{Error: err}: } } }() return ch, nil } // OSPathSeparator is the string version of the os.PathSeparator. const OSPathSeparator = string(os.PathSeparator) func atMaxDepth(maximum int, root, cur string) bool { if maximum < 0 { return false } if root == cur { // always recurse the root directory return false } return (strings.Count(cur, OSPathSeparator) - strings.Count(root, OSPathSeparator)) >= maximum } ================================================ FILE: pkg/archiver/walker_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package archiver_test import ( "context" "os" "path/filepath" "strconv" "testing" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/archiver" ) type WalkerSuite struct { CommonSuite } func (suite *WalkerSuite) TestIterationDir() { ch, err := archiver.Walker(context.Background(), suite.tmpDir, archiver.WithSkipRoot()) suite.Require().NoError(err) relPaths := []string(nil) //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) relPaths = append(relPaths, fi.RelPath) if fi.RelPath == "usr/bin/mv" { suite.Assert().Equal("/usr/bin/cp", fi.Link) } } suite.Assert().Equal([]string{ "dev", "dev/random", "etc", "etc/certs", "etc/certs/ca.crt", "etc/hostname", "lib", "lib/dynalib.so", "usr", "usr/bin", "usr/bin/cp", "usr/bin/mv", }, relPaths) } func (suite *WalkerSuite) TestIterationFilter() { ch, err := archiver.Walker(context.Background(), suite.tmpDir, archiver.WithSkipRoot(), archiver.WithFnmatchPatterns("dev/*", "lib")) suite.Require().NoError(err) relPaths := []string(nil) //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) relPaths = append(relPaths, fi.RelPath) if fi.RelPath == "usr/bin/mv" { suite.Assert().Equal("/usr/bin/cp", fi.Link) } } suite.Assert().Equal([]string{ "dev/random", "lib", }, relPaths) } func (suite *WalkerSuite) TestIterationMaxRecurseDepth() { for _, test := range []struct { maxDepth int result []string }{ { maxDepth: -1, result: []string{".", "dev", "dev/random", "etc", "etc/certs", "etc/certs/ca.crt", "etc/hostname", "lib", "lib/dynalib.so", "usr", "usr/bin", "usr/bin/cp", "usr/bin/mv"}, }, { // confusing case maxDepth: 0, result: []string{".", "dev", "etc", "lib", "usr"}, }, { maxDepth: 1, result: []string{".", "dev", "etc", "lib", "usr"}, }, { maxDepth: 2, result: []string{".", "dev", "dev/random", "etc", "etc/certs", "etc/hostname", "lib", "lib/dynalib.so", "usr", "usr/bin"}, }, { maxDepth: 3, result: []string{".", "dev", "dev/random", "etc", "etc/certs", "etc/certs/ca.crt", "etc/hostname", "lib", "lib/dynalib.so", "usr", "usr/bin", "usr/bin/cp", "usr/bin/mv"}, }, { maxDepth: 4, result: []string{".", "dev", "dev/random", "etc", "etc/certs", "etc/certs/ca.crt", "etc/hostname", "lib", "lib/dynalib.so", "usr", "usr/bin", "usr/bin/cp", "usr/bin/mv"}, }, } { suite.Run(strconv.Itoa(test.maxDepth), func() { suite.T().Parallel() ch, err := archiver.Walker(context.Background(), suite.tmpDir, archiver.WithMaxRecurseDepth(test.maxDepth)) suite.Require().NoError(err) var result []string //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) result = append(result, fi.RelPath) } suite.Equal(test.result, result) }) } } func (suite *WalkerSuite) TestIterationFile() { ch, err := archiver.Walker(context.Background(), filepath.Join(suite.tmpDir, "usr/bin/cp")) suite.Require().NoError(err) relPaths := []string(nil) //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) relPaths = append(relPaths, fi.RelPath) } suite.Assert().Equal([]string{"cp"}, relPaths) } func (suite *WalkerSuite) TestIterationSymlink() { original := filepath.Join(suite.tmpDir, "original") err := os.Mkdir(original, 0o755) suite.Require().NoError(err) defer func() { err = os.RemoveAll(original) suite.Require().NoError(err) }() // NB: We make this a relative symlink to make the test more complete. newname := filepath.Join(suite.tmpDir, "new") err = os.Symlink("original", newname) suite.Require().NoError(err) defer func() { err = os.Remove(newname) suite.Require().NoError(err) }() err = os.WriteFile(filepath.Join(original, "original.txt"), []byte{}, 0o666) suite.Require().NoError(err) ch, err := archiver.Walker(context.Background(), newname) suite.Require().NoError(err) relPaths := []string(nil) //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) relPaths = append(relPaths, fi.RelPath) } suite.Assert().Equal([]string{".", "original.txt"}, relPaths) } func (suite *WalkerSuite) TestIterationNotFound() { _, err := archiver.Walker(context.Background(), filepath.Join(suite.tmpDir, "doesntlivehere")) suite.Require().Error(err) } func (suite *WalkerSuite) TestIterationTypes() { ch, err := archiver.Walker(context.Background(), suite.tmpDir, archiver.WithFileTypes(archiver.DirectoryFileType)) suite.Require().NoError(err) relPaths := []string(nil) //nolint:prealloc // this is a test for fi := range ch { suite.Require().NoError(fi.Error) relPaths = append(relPaths, fi.RelPath) } suite.Assert().Equal([]string{ ".", "dev", "etc", "etc/certs", "lib", "usr", "usr/bin", }, relPaths) } func TestWalkerSuite(t *testing.T) { suite.Run(t, new(WalkerSuite)) } ================================================ FILE: pkg/argsbuilder/argsbuilder_args.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package argsbuilder import ( "fmt" "slices" "strings" "github.com/siderolabs/gen/maps" ) // Key represents an arg key. type Key = string // Value represents an arg value. type Value = []string // Args represents a set of args. type Args map[Key]Value // MustMerge implements the ArgsBuilder interface. func (a Args) MustMerge(args Args, setters ...MergeOption) { if err := a.Merge(args, setters...); err != nil { panic(err) } } // Merge implements the ArgsBuilder interface. // //nolint:gocyclo func (a Args) Merge(args Args, setters ...MergeOption) error { var opts MergeOptions for _, s := range setters { s(&opts) } policies := opts.Policies if policies == nil { policies = MergePolicies{} } for key, val := range args { policy := policies[key] switch policy { case MergeDenied: return NewDenylistError(key) case MergeOverwrite: a[key] = slices.Clone(val) case MergeAppend: existing := make([]string, 0, len(val)+len(a[key])) existing = append(existing, a[key]...) existing = append(existing, val...) a[key] = existing case MergePrepend: existing := make([]string, 0, len(val)+len(a[key])) existing = append(existing, val...) existing = append(existing, a[key]...) a[key] = existing case MergeAdditive: // 1. Join the existing []string slice into one string so we can Split it. // This handles cases where a[key] might be ["a", "b"] or ["a,b"]. rawExisting := strings.Join(a[key], ",") values := strings.Split(rawExisting, ",") definedValues := map[string]struct{}{} i := 0 for _, v := range values { cleanV := strings.TrimSpace(v) if cleanV != "" { // Only keep if unique if _, seen := definedValues[cleanV]; !seen { definedValues[cleanV] = struct{}{} values[i] = cleanV i++ } } } values = values[:i] // 2. Join the incoming 'val' slice so we can SplitSeq over it. rawIncoming := strings.Join(val, ",") for v := range strings.SplitSeq(rawIncoming, ",") { v = strings.TrimSpace(v) if v != "" { if _, defined := definedValues[v]; !defined { values = append(values, v) // Mark as defined to prevent duplicates within the incoming values too definedValues[v] = struct{}{} } } } // 3. Join the results and wrap in a []string to satisfy the type. a[key] = []string{strings.Join(values, ",")} } } return nil } // Set implements the ArgsBuilder interface. func (a Args) Set(k Key, v Value) ArgsBuilder { a[k] = v return a } // Args implements the ArgsBuilder interface. func (a Args) Args() []string { keys := maps.Keys(a) slices.Sort(keys) args := make([]string, 0, len(a)) for _, key := range keys { vals := a[key] for _, val := range vals { args = append( args, fmt.Sprintf("--%s=%s", key, val), ) } } return args } // Get returns an args value. func (a Args) Get(k Key) Value { return a[k] } // Contains checks if an arg key exists. func (a Args) Contains(k Key) bool { _, ok := a[k] return ok } // DenyListError represents an error indicating that an argument was supplied // that is not allowed. type DenyListError struct { s string } // NewDenylistError returns a DenyListError. func NewDenylistError(s string) error { return &DenyListError{s} } // Error implements the Error interface. func (b *DenyListError) Error() string { return fmt.Sprintf("extra arg %q is not allowed", b.s) } ================================================ FILE: pkg/argsbuilder/argsbuilder_interface.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package argsbuilder // MergePolicy defines args builder args merging policy. type MergePolicy int const ( // MergeOverwrite overwrite arg when merging. MergeOverwrite = iota // MergeAdditive concat argument lists. MergeAdditive // MergeDenied fail merge if another object has the arg defined. MergeDenied // MergePrepend prepends new values before existing ones. MergePrepend // MergeAppend appends new values after existing ones. MergeAppend ) // MergePolicies merge policy map. type MergePolicies map[string]MergePolicy // MergeOptions provides optional arguments for merge. type MergeOptions struct { Policies MergePolicies } // MergeOption optional merge argument setter. type MergeOption func(*MergeOptions) // WithMergePolicies set merge policies during merge. func WithMergePolicies(policies MergePolicies) MergeOption { return func(o *MergeOptions) { o.Policies = policies } } // WithDenyList disable merge for all keys in map. func WithDenyList(denyList Args) MergeOption { return func(o *MergeOptions) { if o.Policies == nil { o.Policies = MergePolicies{} } for k := range denyList { o.Policies[k] = MergeDenied } } } // ArgsBuilder defines the requirements to build and manage a set of args. type ArgsBuilder interface { MustMerge(Args, ...MergeOption) Merge(Args, ...MergeOption) error Set(string, []string) ArgsBuilder Args() []string } ================================================ FILE: pkg/argsbuilder/argsbuilder_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package argsbuilder_test import ( "testing" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/argsbuilder" ) type ArgsbuilderSuite struct { suite.Suite } func (suite *ArgsbuilderSuite) TestMergeAdditive() { args := argsbuilder.Args{ "param": {"value1,value2,value3"}, "param2": {""}, } suite.Require().NoError( args.Merge( argsbuilder.Args{ "param": {"value2, value10"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergeAdditive, }), ), ) suite.Require().Equal([]string{"value1,value2,value3,value10"}, args["param"]) suite.Assert().Equal([]string{"--param=value1,value2,value3,value10", "--param2="}, args.Args()) suite.Require().NoError( args.Merge(argsbuilder.Args{ "param2": {"value1, value5"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param2": argsbuilder.MergeAdditive, }), ), ) suite.Require().Equal([]string{"value1,value5"}, args["param2"]) suite.Assert().Equal([]string{"--param=value1,value2,value3,value10", "--param2=value1,value5"}, args.Args()) } func (suite *ArgsbuilderSuite) TestMergeOverwrite() { args := argsbuilder.Args{ "param": {"value1,value2"}, } suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value10"}, }), ) suite.Require().Equal([]string{"value10"}, args["param"]) suite.Assert().Equal([]string{"--param=value10"}, args.Args()) suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value10", "value11"}, }), ) suite.Require().Equal([]string{"value10", "value11"}, args["param"]) suite.Assert().Equal([]string{"--param=value10", "--param=value11"}, args.Args()) } //nolint:dupl func (suite *ArgsbuilderSuite) TestMergePrepend() { args := argsbuilder.Args{ "param": {"value1"}, } suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value2", "value3"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergePrepend, }), ), ) suite.Require().Equal([]string{"value2", "value3", "value1"}, args["param"]) suite.Assert().Equal([]string{"--param=value2", "--param=value3", "--param=value1"}, args.Args()) suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value4"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergePrepend, }), ), ) suite.Require().Equal([]string{"value4", "value2", "value3", "value1"}, args["param"]) suite.Assert().Equal([]string{"--param=value4", "--param=value2", "--param=value3", "--param=value1"}, args.Args()) } //nolint:dupl func (suite *ArgsbuilderSuite) TestMergeAppend() { args := argsbuilder.Args{ "param": {"value1"}, } suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value2", "value3"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergeAppend, }), ), ) suite.Require().Equal([]string{"value1", "value2", "value3"}, args["param"]) suite.Assert().Equal([]string{"--param=value1", "--param=value2", "--param=value3"}, args.Args()) suite.Require().NoError( args.Merge(argsbuilder.Args{ "param": {"value4"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergeAppend, }), ), ) suite.Require().Equal([]string{"value1", "value2", "value3", "value4"}, args["param"]) suite.Assert().Equal([]string{"--param=value1", "--param=value2", "--param=value3", "--param=value4"}, args.Args()) } func (suite *ArgsbuilderSuite) TestMergeDenied() { args := argsbuilder.Args{ "param": {"value1,value2"}, } suite.Require().Error( args.Merge(argsbuilder.Args{ "param": {"value10"}, }, argsbuilder.WithMergePolicies(argsbuilder.MergePolicies{ "param": argsbuilder.MergeDenied, }), ), ) } func (suite *ArgsbuilderSuite) TestMergeDenyList() { args := argsbuilder.Args{ "param": {"value1,value2"}, } denyList := argsbuilder.Args{ "param1": {""}, "param2": {""}, "param3": {""}, } suite.Require().Error( args.Merge(argsbuilder.Args{ "param2": {"value10"}, }, argsbuilder.WithDenyList(denyList), ), ) } func TestArgsbuilderSuite(t *testing.T) { suite.Run(t, &ArgsbuilderSuite{}) } ================================================ FILE: pkg/bytesize/bytesize.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package bytesize adds logic to help parse byte sizes in various forms such as gb, mb, GiB, etc. package bytesize import ( "fmt" "unicode" "github.com/dustin/go-humanize" ) // ByteSize implements pflag.Value interface and is meant to be used with flags specifying a size in bytes. // A value can be set before assigning to a flag to function as a default value. type ByteSize struct { valBytes uint64 val string defaultUnit string } // WithDefaultUnit creates a New ByteSize with a default unit. func WithDefaultUnit(unit string) *ByteSize { return &ByteSize{defaultUnit: unit} } // New creates a New ByteSize without a default unit. func New() *ByteSize { return &ByteSize{} } // Bytes returns the value in bytes. func (bs *ByteSize) Bytes() uint64 { return bs.valBytes } // Megabytes returns the value in megabytes. func (bs *ByteSize) Megabytes() uint64 { return bs.valBytes / (1000 * 1000) } // Gigabytes returns the value in gigabytes. func (bs *ByteSize) Gigabytes() uint64 { return bs.valBytes / (1000 * 1000 * 1000) } // Mebibytetes returns the value in binary megabytes. func (bs *ByteSize) Mebibytetes() uint64 { return bs.valBytes / (1024 * 1024) } // Gibibytes returns the value in binary gigabytes. func (bs *ByteSize) Gibibytes() uint64 { return bs.valBytes / (1024 * 1024 * 1024) } // String returns the string representation of the value with the default unit if set. func (bs *ByteSize) String() string { return bs.val } // SetDefaultUnit sets the default unit to use in case one wasn't specifies. func (bs *ByteSize) SetDefaultUnit(unit string) { bs.defaultUnit = unit } // Set implements pflag.Value interface. func (bs *ByteSize) Set(in string) error { if in == "" || in == "0" { bs.val = "0" bs.valBytes = 0 return nil } // if no unit is specified if unicode.IsDigit(rune(in[len(in)-1])) { if bs.defaultUnit == "" { return fmt.Errorf("no unit specified for %q", in) } in += bs.defaultUnit } valBytes, err := humanize.ParseBytes(in) if err != nil { return err } bs.val = in bs.valBytes = valBytes return nil } // Type implements pflag.Value interface (this will show up as the type next to the flag description). func (bs *ByteSize) Type() string { return "string(mb,gb)" } ================================================ FILE: pkg/bytesize/bytesize_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package bytesize_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/bytesize" ) func TestBytesizeNoDefaultUnit(t *testing.T) { t.Run("empty input", func(t *testing.T) { bs := bytesize.New() assert.NoError(t, bs.Set("")) assert.EqualValues(t, 0, bs.Bytes()) assert.Equal(t, "0", bs.String()) assert.NoError(t, bs.Set("0")) assert.EqualValues(t, 0, bs.Bytes()) assert.Equal(t, "0", bs.String()) }) t.Run("no unit specified", func(t *testing.T) { bs := bytesize.New() assert.ErrorContains(t, bs.Set("10"), "no unit specified") }) t.Run("explicit unit provided", func(t *testing.T) { bs := bytesize.New() assert.NoError(t, bs.Set("0.5mb")) assert.Equal(t, "0.5mb", bs.String()) assert.Equal(t, uint64(500000), bs.Bytes()) }) } func TestBytesizeWithDefaultUnit(t *testing.T) { t.Run("empty input", func(t *testing.T) { bs := bytesize.WithDefaultUnit("mb") assert.NoError(t, bs.Set("")) assert.EqualValues(t, 0, bs.Bytes()) assert.Equal(t, "0", bs.String()) assert.NoError(t, bs.Set("0")) assert.EqualValues(t, 0, bs.Bytes()) assert.Equal(t, "0", bs.String()) }) t.Run("no unit specified", func(t *testing.T) { bs := bytesize.WithDefaultUnit("mb") assert.NoError(t, bs.Set("10")) assert.Equal(t, "10mb", bs.String()) assert.EqualValues(t, 10*1000*1000, bs.Bytes()) }) t.Run("explicit unit provided", func(t *testing.T) { bs := bytesize.WithDefaultUnit("mb") assert.NoError(t, bs.Set("0.5gb")) assert.Equal(t, "0.5gb", bs.String()) assert.EqualValues(t, 500000000, bs.Bytes()) }) } func TestByteSizeUnits(t *testing.T) { bs := bytesize.New() assert.NoError(t, bs.Set("3000000000b")) assert.EqualValues(t, 3000, bs.Megabytes()) assert.EqualValues(t, 3, bs.Gigabytes()) assert.EqualValues(t, 2861, bs.Mebibytetes()) // 3,000,000,000 / 1024^2 = 2861 MiB assert.EqualValues(t, 2, bs.Gibibytes()) // 3,000,000,000 / 1024^3 = 2 GiB } ================================================ FILE: pkg/chunker/chunker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package chunker // Chunker is an interface for embedding all chunking interfaces under one name. type Chunker = ChunkReader // ChunkReader is an interface describing a reader that streams data in []byte // chunks. type ChunkReader interface { Read() <-chan []byte } ================================================ FILE: pkg/chunker/file/file.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package file import ( "context" "io" "os" "github.com/siderolabs/talos/pkg/chunker" "github.com/siderolabs/talos/pkg/chunker/stream" "github.com/siderolabs/talos/pkg/follow" ) // Options is the functional options struct. type Options struct { Size int Follow bool } // Option is the functional option func. type Option func(*Options) // WithSize sets the chunk size of the Chunker. func WithSize(s int) Option { return func(args *Options) { args.Size = s } } // WithFollow file updates using inotify(). func WithFollow() Option { return func(args *Options) { args.Follow = true } } // Source is an interface describing the source of a File. type Source = *os.File // NewChunker initializes a Chunker with default values. func NewChunker(ctx context.Context, source Source, setters ...Option) chunker.Chunker { opts := &Options{ Size: 1024, } for _, setter := range setters { setter(opts) } var r io.ReadCloser = source if opts.Follow { r = follow.NewReader(ctx, source) } return stream.NewChunker(ctx, r, stream.Size(opts.Size)) } ================================================ FILE: pkg/chunker/file/file_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package file_test import ( "context" "fmt" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/chunker/file" ) type FileChunkerSuite struct { suite.Suite tmpDir string no int reader, writer *os.File } func (suite *FileChunkerSuite) SetupSuite() { suite.tmpDir = suite.T().TempDir() } func (suite *FileChunkerSuite) SetupTest() { suite.no++ var err error suite.writer, err = os.Create(filepath.Join(suite.tmpDir, fmt.Sprintf("%d.log", suite.no))) suite.Require().NoError(err) suite.reader, err = os.Open(suite.writer.Name()) suite.Require().NoError(err) } func (suite *FileChunkerSuite) TearDownTest() { suite.Require().NoError(suite.writer.Close()) suite.reader.Close() //nolint:errcheck } func collectChunks(chunksCh <-chan []byte) <-chan []byte { combinedCh := make(chan []byte) go func() { res := []byte(nil) for chunk := range chunksCh { res = append(res, chunk...) } combinedCh <- res }() return combinedCh } func (suite *FileChunkerSuite) TestStreaming() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := file.NewChunker(ctx, suite.reader, file.WithFollow()) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") time.Sleep(50 * time.Millisecond) ctxCancel() suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FileChunkerSuite) TestStreamingWithSomeHead() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := file.NewChunker(ctx, suite.reader, file.WithFollow()) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("mno") time.Sleep(50 * time.Millisecond) ctxCancel() suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FileChunkerSuite) TestStreamingSmallBuffer() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := file.NewChunker(ctx, suite.reader, file.WithSize(1), file.WithFollow()) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") // create extra file to try to confuse watch _, err := os.Create(filepath.Join(suite.tmpDir, "x.log")) suite.Require().NoError(err) time.Sleep(50 * time.Millisecond) ctxCancel() suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FileChunkerSuite) TestStreamingDeleted() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := file.NewChunker(ctx, suite.reader, file.WithFollow()) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") time.Sleep(50 * time.Millisecond) // chunker should terminate when file is removed suite.Require().NoError(os.Remove(suite.writer.Name())) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FileChunkerSuite) TestNoFollow() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := file.NewChunker(ctx, suite.reader) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) suite.Require().Equal([]byte("abcdefghi"), <-combinedCh) } func TestFileChunkerSuite(t *testing.T) { suite.Run(t, new(FileChunkerSuite)) } ================================================ FILE: pkg/chunker/stream/stream.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package stream import ( "context" "errors" "fmt" "io" "os" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-circular" "github.com/siderolabs/talos/pkg/chunker" ) // Options is the functional options struct. type Options struct { Size int } // Option is the functional option func. type Option func(*Options) // Size sets the chunk size of the Chunker. func Size(s int) Option { return func(args *Options) { args.Size = s } } // Stream is a conecrete type that implements the chunker.Chunker interface. type Stream struct { source Source options *Options ctx context.Context //nolint:containedctx } // Source is an interface describing the source of a Stream. type Source interface { io.ReadCloser } // NewChunker initializes a Chunker with default values. func NewChunker(ctx context.Context, source Source, setters ...Option) chunker.Chunker { opts := &Options{ Size: 1024, } for _, setter := range setters { setter(opts) } return &Stream{ source, opts, ctx, } } // Read implements ChunkReader. // //nolint:gocyclo func (c *Stream) Read() <-chan []byte { // Create a buffered channel of length 1. ch := make(chan []byte, 1) go func(ch chan []byte) { defer close(ch) ctx, cancel := context.WithCancel(c.ctx) defer cancel() go func() { <-ctx.Done() c.source.Close() //nolint:errcheck }() buf := make([]byte, c.options.Size) for { select { case <-ctx.Done(): return default: } n, err := c.source.Read(buf) if err != nil { if !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, os.ErrClosed) || errors.Is(err, circular.ErrClosed)) { fmt.Printf("read error: %s\n", err.Error()) } break } if n != 0 { // Copy the buffer since we will modify it in the next loop. b := xslices.CopyN(buf, n) select { case <-ctx.Done(): return case ch <- b: } } } }(ch) return ch } ================================================ FILE: pkg/chunker/stream/stream_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package stream_test import ( "context" "io" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/chunker/stream" ) type StreamChunkerSuite struct { suite.Suite reader *io.PipeReader writer *io.PipeWriter } func (suite *StreamChunkerSuite) SetupTest() { suite.reader, suite.writer = io.Pipe() } func (suite *StreamChunkerSuite) TearDownTest() { suite.Require().NoError(suite.writer.Close()) suite.Require().NoError(suite.reader.Close()) } func collectChunks(chunksCh <-chan []byte) <-chan []byte { combinedCh := make(chan []byte) go func() { res := []byte(nil) for chunk := range chunksCh { res = append(res, chunk...) } combinedCh <- res }() return combinedCh } func (suite *StreamChunkerSuite) TestStreaming() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := stream.NewChunker(ctx, suite.reader) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.Write([]byte("abc")) //nolint:errcheck suite.writer.Write([]byte("def")) //nolint:errcheck suite.writer.Write([]byte("ghi")) time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.Write([]byte("jkl")) //nolint:errcheck suite.writer.Write([]byte("mno")) suite.Require().NoError(suite.writer.Close()) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *StreamChunkerSuite) TestStreamingSmallBuf() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := stream.NewChunker(ctx, suite.reader, stream.Size(1)) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.Write([]byte("abc")) //nolint:errcheck suite.writer.Write([]byte("def")) //nolint:errcheck suite.writer.Write([]byte("ghi")) time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.Write([]byte("jkl")) //nolint:errcheck suite.writer.Write([]byte("mno")) suite.Require().NoError(suite.writer.Close()) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *StreamChunkerSuite) TestStreamingCancel() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() chunker := stream.NewChunker(ctx, suite.reader) chunksCh := chunker.Read() combinedCh := collectChunks(chunksCh) //nolint:errcheck suite.writer.Write([]byte("abc")) //nolint:errcheck suite.writer.Write([]byte("def")) //nolint:errcheck suite.writer.Write([]byte("ghi")) time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.Write([]byte("jkl")) //nolint:errcheck suite.writer.Write([]byte("mno")) time.Sleep(50 * time.Millisecond) ctxCancel() suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func TestStreamChunkerSuite(t *testing.T) { suite.Run(t, new(StreamChunkerSuite)) } ================================================ FILE: pkg/cli/cli.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cli provides utilities for CLI tools. package cli ================================================ FILE: pkg/cli/context.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cli import ( "context" "fmt" "os" "os/signal" "syscall" ) // WithContext wraps function call to provide a context cancellable with ^C. func WithContext(ctx context.Context, f func(context.Context) error) error { wrappedCtx, wrappedCtxCancel := context.WithCancel(ctx) defer wrappedCtxCancel() // listen for ^C and SIGTERM and abort context sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) exited := make(chan struct{}) defer close(exited) go func() { select { case <-sigCh: wrappedCtxCancel() signal.Stop(sigCh) fmt.Fprintln(os.Stderr, "Signal received, aborting, press Ctrl+C once again to abort immediately...") case <-wrappedCtx.Done(): return case <-exited: } }() return f(wrappedCtx) } ================================================ FILE: pkg/cli/output.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cli import ( "fmt" "os" "strings" ) // Fatalf prints formatted message to stderr and aborts execution. func Fatalf(message string, args ...any) { if !strings.HasSuffix(message, "\n") { message += "\n" } fmt.Fprintf(os.Stderr, message, args...) os.Exit(1) } // Warning prints formatted message to stderr. func Warning(message string, args ...any) { if !strings.HasSuffix(message, "\n") { message += "\n" } fmt.Fprintf(os.Stderr, "WARNING: "+message, args...) } ================================================ FILE: pkg/cli/should.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cli // Should panics if err != nil // // Should is useful when error should never happen in customer environment, // it can only be development error. func Should(err error) { if err != nil { panic(err) } } ================================================ FILE: pkg/cluster/apply-config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "crypto/tls" "fmt" "io" "time" "github.com/siderolabs/go-retry/retry" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/provision" ) // ApplyConfigClient client to apply config. type ApplyConfigClient struct { ClientProvider Info } // ApplyConfig on the node via the API using insecure mode. func (s *APIBootstrapper) ApplyConfig(ctx context.Context, nodes []provision.NodeRequest, sl provision.SiderolinkRequest, out io.Writer) error { for _, node := range nodes { configureNode := func(ctx context.Context) error { ep := node.IPs[0].String() if addr, ok := sl.GetAddr(node.UUID); ok { fmt.Fprintln(out, "using SideroLink node address for 'with-apply-config'", node.UUID, "=", addr.String()) ep = addr.String() } c, err := client.New(ctx, client.WithTLSConfig(&tls.Config{ InsecureSkipVerify: true, }), client.WithEndpoints(ep)) if err != nil { return err } cfgBytes, err := node.Config.Bytes() if err != nil { return err } _, err = c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{ Data: cfgBytes, }) if err != nil { return retry.ExpectedError(err) } return nil } if err := retry.Constant(2*time.Minute, retry.WithUnits(250*time.Millisecond), retry.WithJitter(50*time.Millisecond)).RetryWithContext(ctx, configureNode); err != nil { return err } } return nil } ================================================ FILE: pkg/cluster/bootstrap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "errors" "fmt" "io" "slices" "strings" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/go-retry/retry" "google.golang.org/grpc/backoff" "google.golang.org/grpc/codes" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // APIBootstrapper bootstraps cluster via Talos API. type APIBootstrapper struct { ClientProvider Info } // Bootstrap the cluster via the API. // // Bootstrap implements Bootstrapper interface. // //nolint:gocyclo func (s *APIBootstrapper) Bootstrap(ctx context.Context, out io.Writer) error { cli, err := s.Client() if err != nil { return err } controlPlaneNodes := s.NodesByType(machine.TypeControlPlane) if len(controlPlaneNodes) == 0 { return errors.New("no control plane nodes to bootstrap") } slices.SortFunc(controlPlaneNodes, func(a, b NodeInfo) int { return strings.Compare(a.IPs[0].String(), b.IPs[0].String()) }) nodeIP := controlPlaneNodes[0].IPs[0] nodeCtx := client.WithNode(ctx, nodeIP.String()) fmt.Fprintln(out, "waiting for Talos API (to bootstrap the cluster)") err = retry.Constant(10*time.Minute, retry.WithUnits(500*time.Millisecond)).RetryWithContext(nodeCtx, func(nodeCtx context.Context) error { retryCtx, cancel := context.WithTimeout(nodeCtx, 2*time.Second) defer cancel() if _, err = cli.Version(retryCtx); err != nil { return retry.ExpectedError(err) } machineStatus, err := safe.ReaderGetByID[*runtime.MachineStatus](retryCtx, cli.COSI, runtime.MachineStatusID) if err != nil { return retry.ExpectedError(err) } switch machineStatus.TypedSpec().Stage { case runtime.MachineStageBooting, runtime.MachineStageRunning: return nil case runtime.MachineStageUnknown, runtime.MachineStageMaintenance, runtime.MachineStageInstalling, runtime.MachineStageRebooting, runtime.MachineStageShuttingDown, runtime.MachineStageResetting, runtime.MachineStageUpgrading: return retry.ExpectedError(fmt.Errorf("machine in unexpected stage %s", machineStatus.TypedSpec().Stage)) } return nil }) if err != nil { return err } fmt.Fprintln(out, "bootstrapping cluster") return retry.Constant(backoff.DefaultConfig.MaxDelay, retry.WithUnits(100*time.Millisecond)).RetryWithContext(nodeCtx, func(nodeCtx context.Context) error { retryCtx, cancel := context.WithTimeout(nodeCtx, 2*time.Second) defer cancel() if err = cli.Bootstrap(retryCtx, &machineapi.BootstrapRequest{}); err != nil { switch { // deadline exceeded in case it's verbatim context error case errors.Is(err, context.DeadlineExceeded): return retry.ExpectedError(err) // FailedPrecondition when time is not in sync yet on the server // DeadlineExceeded when the call fails in the gRPC stack either on the server or client side // Canceled is when apid restarts on transitioning maintenance -> ready case client.StatusCode(err) == codes.FailedPrecondition || client.StatusCode(err) == codes.DeadlineExceeded || client.StatusCode(err) == codes.Canceled: return retry.ExpectedError(err) // connection refused, including proxied connection refused via the endpoint to the node case strings.Contains(err.Error(), "connection refused"): return retry.ExpectedError(err) // connection timeout case strings.Contains(err.Error(), "error reading from server: EOF"): return retry.ExpectedError(err) } return err } return nil }) } ================================================ FILE: pkg/cluster/check/apid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package check provides set of checks to verify cluster readiness. package check import ( "context" "github.com/siderolabs/talos/pkg/machinery/client" ) // ApidReadyAssertion checks whether apid is responsive on all the nodes. func ApidReadyAssertion(ctx context.Context, cluster ClusterInfo) error { cli, err := cluster.Client() if err != nil { return err } nodes := cluster.Nodes() nodeIPs := mapIPsToStrings(mapNodeInfosToInternalIPs(nodes)) nodesCtx := client.WithNodes(ctx, nodeIPs...) _, err = cli.Version(nodesCtx) return err } ================================================ FILE: pkg/cluster/check/check.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package check provides set of checks to verify cluster readiness. package check import ( "context" "net/netip" "time" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/conditions" ) const updateInterval = 100 * time.Millisecond // ClusterInfo is interface requires by checks. type ClusterInfo interface { cluster.ClientProvider cluster.K8sProvider cluster.Info } // ClusterCheck implements a function which returns condition based on ClusterAccess. type ClusterCheck func(ClusterInfo) conditions.Condition // Reporter presents wait progress. // // It is supposed that reporter drops duplicate messages. type Reporter interface { Update(condition conditions.Condition) } // Wait run the checks against the cluster and waits for the full set to succeed. // // Context ctx might have a timeout set to limit overall wait time. // Each check might define its own timeout. func Wait(ctx context.Context, cluster ClusterInfo, checks []ClusterCheck, reporter Reporter) error { for _, check := range checks { select { case <-ctx.Done(): return ctx.Err() default: } condition := check(cluster) errCh := make(chan error, 1) go func(condition conditions.Condition) { errCh <- condition.Wait(ctx) }(condition) var err error func() { ticker := time.NewTicker(updateInterval) defer ticker.Stop() // report initial state reporter.Update(condition) // report last state defer reporter.Update(condition) for { select { case err = <-errCh: return case <-ticker.C: reporter.Update(condition) } } }() if err != nil { return err } } return nil } func flatMapNodeInfosToIPs(nodes []cluster.NodeInfo) []netip.Addr { return xslices.FlatMap(nodes, func(node cluster.NodeInfo) []netip.Addr { return node.IPs }) } func mapNodeInfosToInternalIPs(nodes []cluster.NodeInfo) []netip.Addr { return xslices.Map(nodes, func(node cluster.NodeInfo) netip.Addr { return node.InternalIP }) } func mapIPsToStrings(input []netip.Addr) []string { return xslices.Map(input, func(ip netip.Addr) string { return ip.String() }) } ================================================ FILE: pkg/cluster/check/check_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/cluster/check/default.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "context" "slices" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // DefaultClusterChecks returns a set of default Talos cluster readiness checks. func DefaultClusterChecks() []ClusterCheck { return slices.Concat( PreBootSequenceChecks(), K8sComponentsReadinessChecks(), []ClusterCheck{ // wait for all the nodes to report ready at k8s level func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all k8s nodes to report ready", func(ctx context.Context) error { if cniDisabled, err := cniDisabledStatus(ctx, cluster); err != nil { return err } else if cniDisabled { return conditions.ErrSkipAssertion } return K8sAllNodesReadyAssertion(ctx, cluster) }, 5*time.Second) }, // wait for kube-proxy to report ready func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("kube-proxy to report ready", func(ctx context.Context) error { present, replicas, err := DaemonSetPresent(ctx, cluster, "kube-system", "k8s-app=kube-proxy") if err != nil { return err } if !present { return conditions.ErrSkipAssertion } return K8sPodReadyAssertion(ctx, cluster, replicas, "kube-system", "k8s-app=kube-proxy") }, 5*time.Second) }, // wait for coredns to report ready func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("coredns to report ready", func(ctx context.Context) error { if cniDisabled, err := cniDisabledStatus(ctx, cluster); err != nil { return err } else if cniDisabled { return conditions.ErrSkipAssertion } present, replicas, err := DeploymentPresent(ctx, cluster, "kube-system", "k8s-app=kube-dns") if err != nil { return err } if !present { return conditions.ErrSkipAssertion } return K8sPodReadyAssertion(ctx, cluster, replicas, "kube-system", "k8s-app=kube-dns") }, 5*time.Second) }, // wait for all the nodes to be schedulable func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all k8s nodes to report schedulable", func(ctx context.Context) error { return K8sAllNodesSchedulableAssertion(ctx, cluster) }, 5*time.Second) }, }, ) } func cniDisabledStatus(ctx context.Context, cluster ClusterInfo) (bool, error) { cli, err := cluster.Client() if err != nil { return false, err } bmc, err := safe.ReaderGetByID[*k8s.BootstrapManifestsConfig](ctx, cli.COSI, k8s.BootstrapManifestsConfigID) if err != nil { return false, err } return bmc.TypedSpec().CNIName == "none", nil } // K8sComponentsReadinessChecks returns a set of K8s cluster readiness checks which are specific to the k8s components // being up and running. This test can be skipped if the cluster is set to use a custom CNI, as the checks won't be healthy // until the CNI is up and running. func K8sComponentsReadinessChecks() []ClusterCheck { return []ClusterCheck{ // wait for all the nodes to report in at k8s level func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all k8s nodes to report", func(ctx context.Context) error { return K8sAllNodesReportedAssertion(ctx, cluster) }, 30*time.Second) // give more time per each attempt, as this check is going to build and cache kubeconfig }, // wait for k8s control plane static pods func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all control plane static pods to be running", func(ctx context.Context) error { return K8sControlPlaneStaticPods(ctx, cluster) }, 5*time.Second) }, // wait for HA k8s control plane func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all control plane components to be ready", func(ctx context.Context) error { return K8sFullControlPlaneAssertion(ctx, cluster) }, 5*time.Second) }, } } // ExtraClusterChecks returns a set of additional Talos cluster readiness checks which work only for newer versions of Talos. // // ExtraClusterChecks can't be used reliably in upgrade tests, as older versions might not pass the checks. func ExtraClusterChecks() []ClusterCheck { return []ClusterCheck{} } // PreBootSequenceChecks returns a set of Talos cluster readiness checks which are run before boot sequence. func PreBootSequenceChecks() []ClusterCheck { return []ClusterCheck{ // wait for etcd to be healthy on all control plane nodes func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("etcd to be healthy", func(ctx context.Context) error { return ServiceHealthAssertion(ctx, cluster, "etcd", WithNodeTypes(machine.TypeInit, machine.TypeControlPlane)) }, 5*time.Second) }, // wait for etcd members to be consistent across nodes func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("etcd members to be consistent across nodes", func(ctx context.Context) error { return EtcdConsistentAssertion(ctx, cluster) }, 5*time.Second) }, // wait for etcd members to be the control plane nodes func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("etcd members to be control plane nodes", func(ctx context.Context) error { return EtcdControlPlaneNodesAssertion(ctx, cluster) }, 5*time.Second) }, // wait for apid to be ready on all the nodes func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("apid to be ready", func(ctx context.Context) error { return ApidReadyAssertion(ctx, cluster) }, 5*time.Second) }, // wait for all nodes to report their memory size func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all nodes memory sizes", func(ctx context.Context) error { return AllNodesMemorySizes(ctx, cluster) }, 5*time.Second) }, // wait for all nodes to report their disk size func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all nodes disk sizes", func(ctx context.Context) error { return AllNodesDiskSizes(ctx, cluster) }, 5*time.Second) }, // check diagnostics func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("no diagnostics", func(ctx context.Context) error { return NoDiagnostics(ctx, cluster) }, 5*time.Second) }, // wait for kubelet to be healthy on all func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("kubelet to be healthy", func(ctx context.Context) error { return ServiceHealthAssertion(ctx, cluster, "kubelet", WithNodeTypes(machine.TypeInit, machine.TypeControlPlane)) }, 5*time.Second) }, // wait for all nodes to finish booting func(cluster ClusterInfo) conditions.Condition { return conditions.PollingCondition("all nodes to finish boot sequence", func(ctx context.Context) error { return AllNodesBootedAssertion(ctx, cluster) }, 5*time.Second) }, } } ================================================ FILE: pkg/cluster/check/diagnostics.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "context" "fmt" "slices" "strings" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // NoDiagnostics checks whether there are no diagnostic warnings. func NoDiagnostics(ctx context.Context, cluster ClusterInfo) error { cli, err := cluster.Client() if err != nil { return err } nodes := cluster.Nodes() nodeInternalIPs := mapIPsToStrings(mapNodeInfosToInternalIPs(nodes)) warningsByNode := map[string][]*runtime.Diagnostic{} for _, nodeIP := range nodeInternalIPs { warnings, err := safe.StateListAll[*runtime.Diagnostic](client.WithNode(ctx, nodeIP), cli.COSI) if err != nil { if client.StatusCode(err) == codes.PermissionDenied { // not supported, skip return conditions.ErrSkipAssertion } return err } for res := range warnings.All() { warningsByNode[nodeIP] = append(warningsByNode[nodeIP], res) } } if len(warningsByNode) == 0 { return nil } nodesWithWarnings := maps.Keys(warningsByNode) slices.Sort(nodesWithWarnings) return fmt.Errorf("active diagnostics: %s", strings.Join(xslices.Map(nodesWithWarnings, func(node string) string { return node + ": " + strings.Join(xslices.Map(warningsByNode[node], func(warning *runtime.Diagnostic) string { return warning.TypedSpec().Message }), ", ") }), "; ")) } ================================================ FILE: pkg/cluster/check/discovery.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "fmt" "net/netip" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" clussterres "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) // DiscoveredClusterInfo represents a cluster.Info populated using the discovery service. type DiscoveredClusterInfo struct { nodes []cluster.NodeInfo nodesByType map[machine.Type][]cluster.NodeInfo } // Nodes returns list of all node infos. func (d *DiscoveredClusterInfo) Nodes() []cluster.NodeInfo { return d.nodes } // NodesByType return list of node endpoints by type. func (d *DiscoveredClusterInfo) NodesByType(m machine.Type) []cluster.NodeInfo { return d.nodesByType[m] } // NewDiscoveredClusterInfo returns a new cluster.Info populated from the discovery service. func NewDiscoveredClusterInfo(members []*clussterres.Member) (cluster.Info, error) { m, err := membersToNodeInfoMap(members) if err != nil { return nil, err } nodes := xslices.FlatMap(maps.Values(m), func(t []cluster.NodeInfo) []cluster.NodeInfo { return t }) return &DiscoveredClusterInfo{ nodes: nodes, nodesByType: m, }, nil } func membersToNodeInfoMap(members []*clussterres.Member) (map[machine.Type][]cluster.NodeInfo, error) { result := make(map[machine.Type][]cluster.NodeInfo) for _, member := range members { spec := member.TypedSpec() machineType := spec.MachineType nodeInfo, err := memberToNodeInfo(member) if err != nil { return nil, err } result[machineType] = append(result[machineType], nodeInfo) } return result, nil } func memberToNodeInfo(member *clussterres.Member) (cluster.NodeInfo, error) { ips, err := stringsToNetipAddrs(xslices.Map(member.TypedSpec().Addresses, func(ip netip.Addr) string { return ip.String() })) if err != nil { return cluster.NodeInfo{}, err } if len(ips) == 0 { return cluster.NodeInfo{}, fmt.Errorf("no IP address found for member: %s", member.Metadata().ID()) } return cluster.NodeInfo{ InternalIP: ips[0], IPs: ips, }, nil } func stringsToNetipAddrs(ips []string) ([]netip.Addr, error) { result := make([]netip.Addr, 0, len(ips)) for _, ip := range ips { parsed, err := netip.ParseAddr(ip) if err != nil { return nil, err } result = append(result, parsed) } return result, nil } ================================================ FILE: pkg/cluster/check/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "cmp" "context" "errors" "fmt" "net/url" "slices" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/cluster" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // EtcdConsistentAssertion checks that etcd membership is consistent across nodes. func EtcdConsistentAssertion(ctx context.Context, cl ClusterInfo) error { cli, err := cl.Client() if err != nil { return err } var nodes []cluster.NodeInfo //nolint:prealloc // dynamic initNodes := cl.NodesByType(machine.TypeInit) nodes = append(nodes, initNodes...) controlPlaneNodes := cl.NodesByType(machine.TypeControlPlane) nodes = append(nodes, controlPlaneNodes...) nodesCtx := client.WithNodes(ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(nodes))...) resp, err := cli.EtcdMemberList(nodesCtx, &machineapi.EtcdMemberListRequest{}) if err != nil { return err } type data struct { hostname string id uint64 isLearner bool } knownMembers := map[data]struct{}{} messages := resp.GetMessages() if len(messages) == 0 { return errors.New("no messages returned") } slices.SortFunc(messages, func(a, b *machineapi.EtcdMembers) int { return cmp.Compare(a.GetMetadata().GetHostname(), b.GetMetadata().GetHostname()) }) for i, message := range messages { if i == 0 { // Fill data using first message for _, member := range message.Members { knownMembers[data{member.Hostname, member.Id, member.IsLearner}] = struct{}{} } continue } node := message.Metadata.GetHostname() if len(message.Members) != len(knownMembers) { expected := maps.ToSlice(knownMembers, func(k data, v struct{}) string { return k.hostname }) actual := xslices.Map(message.Members, (*machineapi.EtcdMember).GetHostname) return fmt.Errorf("%s: expected to have %v members, got %v", node, expected, actual) } // check that member list is the same on all nodes for _, member := range message.Members { if _, found := knownMembers[data{member.Hostname, member.Id, member.IsLearner}]; !found { return fmt.Errorf("%s: found unexpected etcd member %s", node, member.Hostname) } } } return nil } // EtcdControlPlaneNodesAssertion checks that etcd nodes are control plane nodes. func EtcdControlPlaneNodesAssertion(ctx context.Context, cl ClusterInfo) error { cli, err := cl.Client() if err != nil { return err } nodes := append(cl.NodesByType(machine.TypeInit), cl.NodesByType(machine.TypeControlPlane)...) resp, err := cli.EtcdMemberList(ctx, &machineapi.EtcdMemberListRequest{}) if err != nil { return err } members := resp.GetMessages()[0].GetMembers() var memberIPs []string for _, member := range members { for _, peerURL := range member.GetPeerUrls() { parsed, err2 := url.Parse(peerURL) if err2 != nil { return err2 } memberIP := parsed.Hostname() memberIPs = append(memberIPs, memberIP) } } controlPlaneNodeIPs := mapIPsToStrings(flatMapNodeInfosToIPs(nodes)) if !maps.Contains(xslices.ToSet(controlPlaneNodeIPs), memberIPs) { return fmt.Errorf("etcd member ips %q are not subset of control plane node ips %q", memberIPs, controlPlaneNodeIPs) } return nil } ================================================ FILE: pkg/cluster/check/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "context" "fmt" "slices" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // AllNodesBootedAssertion checks whether nodes reached end of 'Boot' sequence. // //nolint:gocyclo func AllNodesBootedAssertion(ctx context.Context, cluster ClusterInfo) error { cli, err := cluster.Client() if err != nil { return err } nodes := cluster.Nodes() nodeInternalIPs := mapIPsToStrings(mapNodeInfosToInternalIPs(nodes)) ctx, cancel := context.WithCancel(ctx) defer cancel() type eventWithNode struct { node string event state.Event } eventCh := make(chan eventWithNode) for _, nodeIP := range nodeInternalIPs { nodeEventCh := make(chan state.Event) if err = cli.COSI.Watch(client.WithNode(ctx, nodeIP), runtime.NewMachineStatus().Metadata(), nodeEventCh); err != nil { return err } go func(nodeIP string) { for { select { case <-ctx.Done(): return case ev := <-nodeEventCh: channel.SendWithContext(ctx, eventCh, eventWithNode{node: nodeIP, event: ev}) } } }(nodeIP) } nodeStages := make(map[string]runtime.MachineStage, len(nodeInternalIPs)) for _, nodeIP := range nodeInternalIPs { nodeStages[nodeIP] = runtime.MachineStageUnknown } for { select { case <-ctx.Done(): return ctx.Err() case ev := <-eventCh: switch ev.event.Type { case state.Created, state.Updated: machineStatus, ok := ev.event.Resource.(*runtime.MachineStatus) if !ok { return fmt.Errorf("unexpected resource type: %T", ev.event.Resource) } nodeStages[ev.node] = machineStatus.TypedSpec().Stage case state.Destroyed, state.Bootstrapped, state.Noop: // nothing case state.Errored: return fmt.Errorf("error watching machine %s status: %w", ev.node, ev.event.Error) } } allNodesRunning := true allNodesReported := true stageWithNodes := map[runtime.MachineStage][]string{} for nodeIP, stage := range nodeStages { if stage != runtime.MachineStageRunning { allNodesRunning = false } if stage == runtime.MachineStageUnknown { allNodesReported = false } stageWithNodes[stage] = append(stageWithNodes[stage], nodeIP) } if !allNodesReported { // keep waiting for data from all nodes continue } if allNodesRunning { return nil } // if we're here, not all nodes are running delete(stageWithNodes, runtime.MachineStageRunning) stages := maps.Keys(stageWithNodes) slices.Sort(stages) message := xslices.Map(stages, func(stage runtime.MachineStage) string { nodeIPs := stageWithNodes[stage] slices.Sort(nodeIPs) return fmt.Sprintf("%s: %v", stage, nodeIPs) }) return fmt.Errorf("nodes are not running: %s", strings.Join(message, ", ")) } } ================================================ FILE: pkg/cluster/check/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package check provides set of checks to verify cluster readiness. package check import ( "context" "fmt" "net/netip" "slices" "strings" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/maps" "github.com/siderolabs/go-pointer" "google.golang.org/grpc/codes" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // K8sAllNodesReportedAssertion checks whether all the nodes show up in node list. func K8sAllNodesReportedAssertion(ctx context.Context, cl ClusterInfo) error { clientset, err := cl.K8sClient(ctx) if err != nil { return err } expectedNodeInfos := cl.Nodes() nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return err } actualNodeInfos := make([]cluster.NodeInfo, 0, len(nodes.Items)) for _, node := range nodes.Items { var internalIP netip.Addr var ips []netip.Addr for _, nodeAddress := range node.Status.Addresses { switch nodeAddress.Type { //nolint:exhaustive case v1.NodeInternalIP: internalIP, err = netip.ParseAddr(nodeAddress.Address) if err != nil { return err } ips = append(ips, internalIP) case v1.NodeExternalIP: externalIP, err := netip.ParseAddr(nodeAddress.Address) if err != nil { return err } ips = append(ips, externalIP) } } actualNodeInfo := cluster.NodeInfo{ InternalIP: internalIP, IPs: ips, } actualNodeInfos = append(actualNodeInfos, actualNodeInfo) } return cluster.NodesMatch(expectedNodeInfos, actualNodeInfos) } // K8sFullControlPlaneAssertion checks whether all the controlplane nodes are k8s controlplane nodes. // //nolint:gocyclo,cyclop func K8sFullControlPlaneAssertion(ctx context.Context, cl ClusterInfo) error { clientset, err := cl.K8sClient(ctx) if err != nil { return err } expectedNodes := append(cl.NodesByType(machine.TypeInit), cl.NodesByType(machine.TypeControlPlane)...) nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return err } actualNodes := make([]cluster.NodeInfo, 0, len(nodes.Items)) for _, node := range nodes.Items { for label := range node.Labels { if label == constants.LabelNodeRoleControlPlane { var internalIP netip.Addr var ips []netip.Addr for _, nodeAddress := range node.Status.Addresses { switch nodeAddress.Type { //nolint:exhaustive case v1.NodeInternalIP: internalIP, err = netip.ParseAddr(nodeAddress.Address) if err != nil { return err } ips = append(ips, internalIP) case v1.NodeExternalIP: externalIP, err2 := netip.ParseAddr(nodeAddress.Address) if err2 != nil { return err2 } ips = append(ips, externalIP) } } actualNodeInfo := cluster.NodeInfo{ InternalIP: internalIP, IPs: ips, } actualNodes = append(actualNodes, actualNodeInfo) break } } } err = cluster.NodesMatch(expectedNodes, actualNodes) if err != nil { return err } // NB: We run the control plane check after node readiness check in order to // ensure that all control plane nodes have been labeled with the controlplane // label. // daemonset check only there for pre-0.9 clusters with self-hosted control plane daemonsets, err := clientset.AppsV1().DaemonSets("kube-system").List(ctx, metav1.ListOptions{ LabelSelector: "k8s-app in (kube-apiserver,kube-scheduler,kube-controller-manager)", }) if err != nil { return err } for _, ds := range daemonsets.Items { if ds.Status.CurrentNumberScheduled != ds.Status.DesiredNumberScheduled { return fmt.Errorf("expected current number scheduled for %s to be %d, got %d", ds.GetName(), ds.Status.DesiredNumberScheduled, ds.Status.CurrentNumberScheduled) } if ds.Status.NumberAvailable != ds.Status.DesiredNumberScheduled { return fmt.Errorf("expected number available for %s to be %d, got %d", ds.GetName(), ds.Status.DesiredNumberScheduled, ds.Status.NumberAvailable) } if ds.Status.NumberReady != ds.Status.DesiredNumberScheduled { return fmt.Errorf("expected number ready for %s to be %d, got %d", ds.GetName(), ds.Status.DesiredNumberScheduled, ds.Status.NumberReady) } } for _, k8sApp := range []string{"kube-apiserver", "kube-scheduler", "kube-controller-manager"} { // list pods to verify that daemonset status is updated properly pods, err := clientset.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{ LabelSelector: fmt.Sprintf("k8s-app = %s", k8sApp), }) if err != nil { return fmt.Errorf("error listing pods for app %s: %w", k8sApp, err) } // filter out pod checkpoints n := 0 for _, pod := range pods.Items { if _, exists := pod.Annotations["checkpointer.alpha.coreos.com/checkpoint-of"]; !exists { pods.Items[n] = pod n++ } } pods.Items = pods.Items[:n] if len(pods.Items) != len(actualNodes) { return fmt.Errorf("expected number of pods for %s to be %d, got %d", k8sApp, len(actualNodes), len(pods.Items)) } var notReadyPods []string for _, pod := range pods.Items { for _, cond := range pod.Status.Conditions { if cond.Type == v1.PodReady { if cond.Status != v1.ConditionTrue { notReadyPods = append(notReadyPods, pod.Name) break } } } } if len(notReadyPods) > 0 { return fmt.Errorf("some pods are not ready for %s: %v", k8sApp, notReadyPods) } } return nil } // K8sAllNodesReadyAssertion checks whether all the nodes are Ready. func K8sAllNodesReadyAssertion(ctx context.Context, cluster cluster.K8sProvider) error { clientset, err := cluster.K8sClient(ctx) if err != nil { return err } nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return err } var notReadyNodes []string for _, node := range nodes.Items { for _, cond := range node.Status.Conditions { if cond.Type == v1.NodeReady { if cond.Status != v1.ConditionTrue { notReadyNodes = append(notReadyNodes, node.Name) break } } } } if len(notReadyNodes) == 0 { return nil } return fmt.Errorf("some nodes are not ready: %v", notReadyNodes) } // K8sAllNodesSchedulableAssertion checks whether all the nodes are schedulable (not cordoned). func K8sAllNodesSchedulableAssertion(ctx context.Context, cluster cluster.K8sProvider) error { clientset, err := cluster.K8sClient(ctx) if err != nil { return err } nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return err } var notSchedulableNodes []string for _, node := range nodes.Items { if node.Spec.Unschedulable { notSchedulableNodes = append(notSchedulableNodes, node.Name) break } } if len(notSchedulableNodes) == 0 { return nil } return fmt.Errorf("some nodes are not schedulable: %v", notSchedulableNodes) } // K8sPodReadyAssertion checks whether all the pods matching label selector are Ready, and there is at least one. // //nolint:gocyclo func K8sPodReadyAssertion(ctx context.Context, cluster cluster.K8sProvider, replicas int, namespace, labelSelector string) error { clientset, err := cluster.K8sClient(ctx) if err != nil { return err } pods, err := clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) if err != nil { return err } if len(pods.Items) == 0 { return fmt.Errorf("no pods found for namespace %q and label selector %q", namespace, labelSelector) } var notReadyPods, readyPods []string for _, pod := range pods.Items { // skip deleted pods if pod.DeletionTimestamp != nil { continue } // skip failed pods if pod.Status.Phase == v1.PodFailed { continue } // skip pods which `kubectl get pods` marks as 'Completed': // * these pods have a phase 'Running', but all containers are terminated // * such pods appear after a graceful kubelet shutdown allContainersTerminated := true for _, containerStatus := range pod.Status.ContainerStatuses { if containerStatus.State.Terminated == nil { allContainersTerminated = false break } } if allContainersTerminated { continue } ready := false for _, cond := range pod.Status.Conditions { if cond.Type == v1.PodReady { if cond.Status == v1.ConditionTrue { ready = true break } } } if !ready { notReadyPods = append(notReadyPods, pod.Name) } else { readyPods = append(readyPods, pod.Name) } } if len(readyPods) == 0 { return fmt.Errorf("no ready pods found for namespace %q and label selector %q", namespace, labelSelector) } if len(notReadyPods) == 0 { if len(readyPods) != replicas { return fmt.Errorf("expected %d ready pods, got %d", replicas, len(readyPods)) } return nil } return fmt.Errorf("some pods are not ready: %v", notReadyPods) } // DaemonSetPresent returns true if there is at least one DaemonSet matching given label selector. func DaemonSetPresent(ctx context.Context, cluster cluster.K8sProvider, namespace, labelSelector string) (bool, int, error) { clientset, err := cluster.K8sClient(ctx) if err != nil { return false, 0, err } dss, err := clientset.AppsV1().DaemonSets(namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) if err != nil { return false, 0, err } if len(dss.Items) == 0 { return false, 0, nil } return true, int(dss.Items[0].Status.DesiredNumberScheduled), nil } // DeploymentPresent returns true if there is at least one ReplicaSet matching given label selector. func DeploymentPresent(ctx context.Context, cluster cluster.K8sProvider, namespace, labelSelector string) (bool, int, error) { clientset, err := cluster.K8sClient(ctx) if err != nil { return false, 0, err } deployments, err := clientset.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{ LabelSelector: labelSelector, }) if err != nil { return false, 0, err } if len(deployments.Items) == 0 { return false, 0, nil } deployment := deployments.Items[0] return true, int(pointer.SafeDeref(deployment.Spec.Replicas)), nil } // K8sControlPlaneStaticPods checks whether all the controlplane nodes are running required Kubernetes static pods. func K8sControlPlaneStaticPods(ctx context.Context, cl ClusterInfo) error { expectedNodes := append(cl.NodesByType(machine.TypeInit), cl.NodesByType(machine.TypeControlPlane)...) // using here new Talos COSI API, Talos 1.2+ required c, err := cl.Client() if err != nil { return err } for _, node := range expectedNodes { expectedStaticPods := map[string]struct{}{ "kube-system/kube-apiserver": {}, "kube-system/kube-controller-manager": {}, "kube-system/kube-scheduler": {}, } items, err := safe.StateListAll[*k8s.StaticPodStatus](client.WithNode(ctx, node.InternalIP.String()), c.COSI) if err != nil { if client.StatusCode(err) == codes.Unimplemented { // old version of Talos without COSI API return nil } return fmt.Errorf("error listing static pods on node %s: %w", node.InternalIP, err) } for res := range items.All() { for expectedStaticPod := range expectedStaticPods { if strings.HasPrefix(res.Metadata().ID(), expectedStaticPod) { delete(expectedStaticPods, expectedStaticPod) } } } if len(expectedStaticPods) > 0 { missingStaticPods := maps.Keys(expectedStaticPods) slices.Sort(missingStaticPods) return fmt.Errorf("missing static pods on node %s: %v", node.InternalIP, missingStaticPods) } } return nil } ================================================ FILE: pkg/cluster/check/nodes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "context" "errors" fmt "fmt" "slices" "github.com/cosi-project/runtime/pkg/safe" "github.com/dustin/go-humanize" "github.com/hashicorp/go-multierror" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/resources/block" "github.com/siderolabs/talos/pkg/minimal" ) // AllNodesMemorySizes checks that all nodes have enough memory. func AllNodesMemorySizes(ctx context.Context, cluster ClusterInfo) error { cl, err := cluster.Client() if err != nil { return fmt.Errorf("error getting client: %w", err) } nodesIP, err := getNonContainerNodes( client.WithNodes( ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(cluster.Nodes()))..., ), cl, ) if err != nil { return err } if len(nodesIP) == 0 { return nil } resp, err := cl.Memory(client.WithNodes(ctx, nodesIP...)) if err != nil { return fmt.Errorf("error getting nodes memory: %w", err) } var resultErr error nodeToType := getNodesTypes(cluster, machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker) for _, msg := range resp.Messages { if msg.Metadata == nil { return errors.New("no metadata in the response") } hostname := msg.Metadata.Hostname typ, ok := nodeToType[hostname] if !ok { return fmt.Errorf("unexpected node %q in response", hostname) } minimum, _, err := minimal.Memory(typ) if err != nil { resultErr = multierror.Append(resultErr, err) continue } if totalMemory := msg.Meminfo.Memtotal * humanize.KiByte; totalMemory < minimum { resultErr = multierror.Append( resultErr, fmt.Errorf( "node %q does not meet memory requirements: expected at least %d MiB, actual %d MiB", hostname, minimum/humanize.MiByte, totalMemory/humanize.MiByte, ), ) } } return resultErr } func getNodesTypes(cluster ClusterInfo, nodeTypes ...machine.Type) map[string]machine.Type { result := map[string]machine.Type{} for _, typ := range nodeTypes { for _, node := range cluster.NodesByType(typ) { result[node.InternalIP.String()] = typ } } return result } // AllNodesDiskSizes checks that all nodes have enough disk space. // //nolint:gocyclo func AllNodesDiskSizes(ctx context.Context, cluster ClusterInfo) error { cl, err := cluster.Client() if err != nil { return fmt.Errorf("error getting client: %w", err) } nodesIP, err := getNonContainerNodes( client.WithNodes( ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(cluster.Nodes()))..., ), cl, ) if err != nil { return err } if len(nodesIP) == 0 { return nil } var resultErr error slices.Sort(nodesIP) for _, nodeIP := range nodesIP { vs, err := safe.StateGetByID[*block.VolumeStatus](client.WithNode(ctx, nodeIP), cl.COSI, constants.EphemeralPartitionLabel) if err != nil { if client.StatusCode(err) == codes.PermissionDenied { // old Talos versions don't support this resource return conditions.ErrSkipAssertion } resultErr = multierror.Append(resultErr, fmt.Errorf("error getting volume status for node %q: %w", nodeIP, err)) continue } actualSize := vs.TypedSpec().Size // calculate EPHEMERAL by subtracting the size of all other partitions and GPT overhead ps := quirks.New("").PartitionSizes() minimalEphemeralSize := minimal.DiskSize() - (ps.UKIEFISize() + ps.StateSize() + ps.METASize() + 10*1048576 /* GPT overhead, including alignment */) if actualSize < minimalEphemeralSize { resultErr = multierror.Append(resultErr, fmt.Errorf( "ephemeral partition %q for node %q is too small, expected at least %s, actual %s", vs.TypedSpec().Location, nodeIP, humanize.IBytes(minimalEphemeralSize), humanize.IBytes(actualSize), )) } } return resultErr } func getNonContainerNodes(ctx context.Context, cl *client.Client) ([]string, error) { resp, err := cl.Version(ctx) if err != nil { return nil, fmt.Errorf("error getting version: %w", err) } result := make([]string, 0, len(resp.Messages)) for _, msg := range resp.Messages { if msg.Metadata == nil { return nil, errors.New("got empty metadata") } if msg.Platform.Mode == "container" { continue } result = append(result, msg.Metadata.Hostname) } return result, nil } ================================================ FILE: pkg/cluster/check/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import "github.com/siderolabs/talos/pkg/machinery/config/machine" // Option represents functional option. type Option func(o *Options) error // WithNodeTypes sets the node types for a check. func WithNodeTypes(t ...machine.Type) Option { return func(o *Options) error { o.Types = t return nil } } // Options describes ClusterCheck parameters. type Options struct { Types []machine.Type } // DefaultOptions returns the default options. func DefaultOptions() *Options { return &Options{} } ================================================ FILE: pkg/cluster/check/reporter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package check import ( "fmt" "strings" "github.com/siderolabs/talos/pkg/conditions" "github.com/siderolabs/talos/pkg/reporter" ) // ConditionReporter is a reporter that reports conditions to a reporter.Reporter. type ConditionReporter struct { w *reporter.Reporter } // Update reports a condition to the reporter. func (r *ConditionReporter) Update(condition conditions.Condition) { r.w.Report(conditionToUpdate(condition)) } // StderrReporter returns console reporter with stderr output. func StderrReporter() *ConditionReporter { return &ConditionReporter{ w: reporter.New(), } } func conditionToUpdate(condition conditions.Condition) reporter.Update { line := strings.TrimSpace(fmt.Sprintf("waiting for %s", condition.String())) switch { case strings.HasSuffix(line, "..."): return reporter.Update{ Message: line, Status: reporter.StatusRunning, } case strings.HasSuffix(line, conditions.OK): return reporter.Update{ Message: line, Status: reporter.StatusSucceeded, } case strings.HasSuffix(line, conditions.ErrSkipAssertion.Error()): return reporter.Update{ Message: line, Status: reporter.StatusSkip, } default: return reporter.Update{ Message: line, Status: reporter.StatusError, } } } ================================================ FILE: pkg/cluster/check/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package check provides set of checks to verify cluster readiness. package check import ( "cmp" "context" "errors" "fmt" "slices" "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // ErrServiceNotFound is an error that indicates that a service was not found. var ErrServiceNotFound = errors.New("service not found") // ServiceStateAssertion checks whether service reached some specified state. func ServiceStateAssertion(ctx context.Context, cl ClusterInfo, service string, states ...string) error { cli, err := cl.Client() if err != nil { return err } // by default, we check all control plane nodes. if some nodes don't have that service running, // it won't be returned in the response nodes := append(cl.NodesByType(machine.TypeInit), cl.NodesByType(machine.TypeControlPlane)...) nodesCtx := client.WithNodes(ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(nodes))...) servicesInfo, err := cli.ServiceInfo(nodesCtx, service) if err != nil { return err } if len(servicesInfo) == 0 { return ErrServiceNotFound } acceptedStates := map[string]struct{}{} for _, state := range states { acceptedStates[state] = struct{}{} } var multiErr *multierror.Error for _, serviceInfo := range servicesInfo { node := serviceInfo.Metadata.GetHostname() if len(serviceInfo.Service.Events.Events) == 0 { multiErr = multierror.Append(multiErr, fmt.Errorf("%s: no events recorded yet for service %q", node, service)) continue } lastEvent := serviceInfo.Service.Events.Events[len(serviceInfo.Service.Events.Events)-1] if _, ok := acceptedStates[lastEvent.State]; !ok { multiErr = multierror.Append(multiErr, fmt.Errorf("%s: service %q not in expected state %q: current state [%s] %s", node, service, states, lastEvent.State, lastEvent.Msg)) } } return multiErr.ErrorOrNil() } // ServiceHealthAssertion checks whether service reached some specified state. // //nolint:gocyclo func ServiceHealthAssertion(ctx context.Context, cl ClusterInfo, service string, setters ...Option) error { opts := DefaultOptions() for _, setter := range setters { if err := setter(opts); err != nil { return err } } cli, err := cl.Client() if err != nil { return err } var nodes []cluster.NodeInfo if len(opts.Types) > 0 { for _, t := range opts.Types { nodes = append(nodes, cl.NodesByType(t)...) } } else { nodes = cl.Nodes() } count := len(nodes) nodesCtx := client.WithNodes(ctx, mapIPsToStrings(mapNodeInfosToInternalIPs(nodes))...) servicesInfo, err := cli.ServiceInfo(nodesCtx, service) if err != nil { return err } if len(servicesInfo) != count { return fmt.Errorf("expected a response with %d node(s), got %d", count, len(servicesInfo)) } var multiErr *multierror.Error // sort service info list so that errors returned are consistent slices.SortFunc(servicesInfo, func(a, b client.ServiceInfo) int { return cmp.Compare(a.Metadata.GetHostname(), b.Metadata.GetHostname()) }) for _, serviceInfo := range servicesInfo { node := serviceInfo.Metadata.GetHostname() if len(serviceInfo.Service.Events.Events) == 0 { multiErr = multierror.Append(multiErr, fmt.Errorf("%s: no events recorded yet for service %q", node, service)) continue } lastEvent := serviceInfo.Service.Events.Events[len(serviceInfo.Service.Events.Events)-1] if lastEvent.State != "Running" { multiErr = multierror.Append(multiErr, fmt.Errorf("%s: service %q not in expected state %q: current state [%s] %s", node, service, "Running", lastEvent.State, lastEvent.Msg)) continue } if !serviceInfo.Service.GetHealth().GetHealthy() { multiErr = multierror.Append(multiErr, fmt.Errorf("%s: service is not healthy: %s", node, service)) continue } } return multiErr.ErrorOrNil() } ================================================ FILE: pkg/cluster/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cluster provides functions to access, check and inspect Talos clusters. package cluster import ( "context" "fmt" "io" "net/netip" "slices" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" k8s "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // ClientProvider builds Talos client by endpoint. // // Client instance should be cached and closed when Close() is called. type ClientProvider interface { // Client returns Talos client instance for default (if no endpoints are given) or // specific endpoint. Client(endpoints ...string) (*client.Client, error) // Close client connections. Close() error } // K8sProvider builds Kubernetes client to access Talos cluster. type K8sProvider interface { Kubeconfig(ctx context.Context) ([]byte, error) K8sRestConfig(ctx context.Context) (*rest.Config, error) K8sClient(ctx context.Context) (*kubernetes.Clientset, error) K8sHelper(ctx context.Context) (*k8s.Client, error) K8sClose() error } // CrashDumper captures Talos cluster state to the specified writer for debugging. type CrashDumper interface { CrashDump(ctx context.Context, out io.Writer) } // NodeInfo describes a Talos node. type NodeInfo struct { InternalIP netip.Addr IPs []netip.Addr } // Info describes the Talos cluster. type Info interface { // Nodes returns list of all node infos. Nodes() []NodeInfo // NodesByType return list of node endpoints by type. NodesByType(machine.Type) []NodeInfo } // Bootstrapper performs Talos cluster bootstrap. type Bootstrapper interface { Bootstrap(ctx context.Context, out io.Writer) error } // IPsToNodeInfos converts list of IPs to a list of NodeInfos. func IPsToNodeInfos(ips []string) ([]NodeInfo, error) { result := make([]NodeInfo, len(ips)) for i, ip := range ips { info, err := IPToNodeInfo(ip) if err != nil { return nil, err } result[i] = *info } return result, nil } // IPToNodeInfo converts a node internal IP to a NodeInfo. func IPToNodeInfo(ip string) (*NodeInfo, error) { parsed, err := netip.ParseAddr(ip) if err != nil { return nil, err } return &NodeInfo{ InternalIP: parsed, IPs: []netip.Addr{parsed}, }, nil } // NodesMatch asserts that the provided expected set of nodes match the actual set of nodes. // // Each expectedNode IPs should have a non-empty intersection with actualNode IPs. func NodesMatch(expected, actual []NodeInfo) error { actualNodes := xslices.ToMap(actual, func(n NodeInfo) (*NodeInfo, struct{}) { return &n, struct{}{} }) for _, expectedNodeInfo := range expected { found := false for actualNodeInfo := range actualNodes { // expectedNodeInfo.IPs intersection with actualNodeInfo.IPs is not empty if len(maps.Intersect(xslices.ToSet(actualNodeInfo.IPs), xslices.ToSet(expectedNodeInfo.IPs))) > 0 { delete(actualNodes, actualNodeInfo) found = true break } } if !found { return fmt.Errorf("can't find expected node with IPs %q", expectedNodeInfo.IPs) } } if len(actualNodes) > 0 { unexpectedIPs := xslices.FlatMap(maps.Keys(actualNodes), func(n *NodeInfo) []netip.Addr { return n.IPs }) slices.SortFunc(unexpectedIPs, func(a, b netip.Addr) int { return a.Compare(b) }) return fmt.Errorf("unexpected nodes with IPs %q", unexpectedIPs) } return nil } ================================================ FILE: pkg/cluster/cluster_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/cluster" ) func TestAssertNodes(t *testing.T) { for _, tt := range []struct { name string expectedNodes []cluster.NodeInfo actualNodes []cluster.NodeInfo expectedError string }{ { name: "aws+discovery", expectedNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("172.23.1.3")}, }, }, actualNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("172.23.1.3")}, }, { IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("172.23.1.2")}, }, }, }, { name: "aws+private", expectedNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.3")}, }, }, actualNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("172.23.1.3")}, }, { IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("172.23.1.2")}, }, }, }, { name: "more internal IPs", expectedNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("ff::1"), netip.MustParseAddr("172.23.1.3")}, }, { IPs: []netip.Addr{netip.MustParseAddr("ff::2"), netip.MustParseAddr("172.23.1.2")}, }, }, actualNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.3")}, }, }, }, { name: "extra node expected", expectedNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.3")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.4")}, }, }, actualNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("172.23.1.3")}, }, }, expectedError: `can't find expected node with IPs ["172.23.1.4"]`, }, { name: "extra node actual", expectedNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.3")}, }, }, actualNodes: []cluster.NodeInfo{ { IPs: []netip.Addr{netip.MustParseAddr("1.2.3.4"), netip.MustParseAddr("172.23.1.2")}, }, { IPs: []netip.Addr{netip.MustParseAddr("5.6.7.8"), netip.MustParseAddr("172.23.1.3")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.4")}, }, { IPs: []netip.Addr{netip.MustParseAddr("172.23.1.5"), netip.MustParseAddr("9.10.11.12")}, }, }, expectedError: "unexpected nodes with IPs [\"9.10.11.12\" \"172.23.1.4\" \"172.23.1.5\"]", }, } { t.Run(tt.name, func(t *testing.T) { err := cluster.NodesMatch(tt.expectedNodes, tt.actualNodes) if tt.expectedError == "" { assert.NoError(t, err) } else { assert.EqualError(t, err, tt.expectedError) } }) } } ================================================ FILE: pkg/cluster/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "strings" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // ConfigClientProvider builds Talos client from client config. type ConfigClientProvider struct { // DefaultClient to be used when using default endpoints. // // Not required, if missing client will be constructed from the config. DefaultClient *client.Client // TalosConfig is a client Talos configuration. TalosConfig *clientconfig.Config clients map[string]*client.Client } // Client returns Talos client instance for default (if no endpoints are given) or // specific endpoints. // // Client implements ClientProvider interface. func (c *ConfigClientProvider) Client(endpoints ...string) (*client.Client, error) { key := strings.Join(endpoints, ",") if c.clients == nil { c.clients = make(map[string]*client.Client) } if cli := c.clients[key]; cli != nil { return cli, nil } if len(endpoints) == 0 && c.DefaultClient != nil { return c.DefaultClient, nil } opts := []client.OptionFunc{ client.WithConfig(c.TalosConfig), } if len(endpoints) > 0 { opts = append(opts, client.WithEndpoints(endpoints...)) } return client.New(context.TODO(), opts...) } // Close all the client connections. func (c *ConfigClientProvider) Close() error { for _, cli := range c.clients { if err := cli.Close(); err != nil { return err } } c.clients = nil return nil } ================================================ FILE: pkg/cluster/crashdump.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "io" "os" "time" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-talos-support/support" "github.com/siderolabs/go-talos-support/support/bundle" "github.com/siderolabs/go-talos-support/support/collectors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8s "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/provision" ) // Crashdump creates a support.zip for the cluster. func Crashdump(ctx context.Context, cluster provision.Cluster, logWriter io.Writer, zipFilePath string) { supportFile, err := os.Create(zipFilePath) if err != nil { fmt.Fprintf(logWriter, "error creating crashdump file: %s\n", err) return } defer supportFile.Close() //nolint:errcheck // limit support bundle generation time ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() c, err := client.New(ctx, client.WithDefaultConfig()) if err != nil { fmt.Fprintf(logWriter, "error creating crashdump: %s\n", err) return } nodes := xslices.Map(cluster.Info().Nodes, func(nodeInfo provision.NodeInfo) string { return nodeInfo.IPs[0].String() }) controlplane := nodes[0] opts := []bundle.Option{ bundle.WithArchiveOutput(supportFile), bundle.WithTalosClient(c), bundle.WithNodes(nodes...), bundle.WithNumWorkers(4), bundle.WithLogOutput(io.Discard), } kubeclient, err := getKubernetesClient(ctx, c, controlplane) // ignore error if we can't get a k8s client if err == nil { opts = append(opts, bundle.WithKubernetesClient(kubeclient)) } options := bundle.NewOptions(opts...) collectors, err := collectors.GetForOptions(ctx, options) if err != nil { fmt.Fprintf(logWriter, "error creating crashdump collector options: %s\n", err) return } if err := support.CreateSupportBundle(ctx, options, collectors...); err != nil { fmt.Fprintf(logWriter, "error creating crashdump: %s\n", err) return } } func getKubernetesClient(ctx context.Context, c *client.Client, endpoint string) (*k8s.Clientset, error) { kubeconfig, err := c.Kubeconfig(client.WithNodes(ctx, endpoint)) if err != nil { return nil, err } config, err := clientcmd.NewClientConfigFromBytes(kubeconfig) if err != nil { return nil, err } restconfig, err := config.ClientConfig() if err != nil { return nil, err } clientset, err := k8s.NewForConfig(restconfig) if err != nil { return nil, err } // just checking that k8s responds _, err = clientset.CoreV1().Namespaces().Get(ctx, "kube-system", v1.GetOptions{}) if err != nil { return nil, err } return clientset, nil } ================================================ FILE: pkg/cluster/hydrophone/hydrophone.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package hydrophone provides functions to run Kubernetes e2e tests. package hydrophone import ( "context" "fmt" "os" "path/filepath" "strings" "time" "github.com/blang/semver/v4" yaml "go.yaml.in/yaml/v4" "sigs.k8s.io/hydrophone/pkg/common" "sigs.k8s.io/hydrophone/pkg/conformance" "sigs.k8s.io/hydrophone/pkg/conformance/client" "sigs.k8s.io/hydrophone/pkg/types" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Options for the tests. type Options struct { RunTests []string Skip string Parallel int RunTimeout time.Duration DeleteTimeout time.Duration KubernetesVersion string UseSpinner bool RetrieveResults bool ResultsPath string } // DefaultOptions with hand-picked tests, timeouts, etc. func DefaultOptions() *Options { return &Options{ RunTests: []string{ // list of tests to cover basic kubernetes operations "Pods should be submitted and removed", "Services should serve a basic endpoint from pods", "Services should be able to change the type from ExternalName to ClusterIP", }, Parallel: 2, RunTimeout: 10 * time.Minute, DeleteTimeout: 3 * time.Minute, KubernetesVersion: constants.DefaultKubernetesVersion, } } // NetworkPolicies runs only network policy related tests. func NetworkPolicies(ctx context.Context, cluster cluster.K8sProvider) error { options := Options{ RunTests: []string{ `\[Feature:NetworkPolicy\]`, }, Parallel: 4, RunTimeout: 15 * time.Minute, DeleteTimeout: 3 * time.Minute, KubernetesVersion: constants.DefaultKubernetesVersion, } return Run(ctx, cluster, &options) } // FastConformance runs conformance suite in two passes: parallel + serial for non parallel-safe tests. func FastConformance(ctx context.Context, cluster cluster.K8sProvider) error { optionsList := []Options{ { RunTests: []string{`\[Conformance\]`}, Skip: `\[Serial\]`, Parallel: 16, RunTimeout: time.Hour, DeleteTimeout: 5 * time.Minute, KubernetesVersion: constants.DefaultKubernetesVersion, UseSpinner: true, }, { RunTests: []string{`\[Serial\].*\[Conformance\]`}, RunTimeout: time.Hour, DeleteTimeout: 5 * time.Minute, KubernetesVersion: constants.DefaultKubernetesVersion, UseSpinner: true, }, } for _, options := range optionsList { if err := Run(ctx, cluster, &options); err != nil { return err } } return nil } // CertifiedConformance runs conformance suite in certified mode collecting all the results. func CertifiedConformance(ctx context.Context, cluster cluster.K8sProvider) error { options := Options{ RunTests: []string{`\[Conformance\]`}, RunTimeout: 2 * time.Hour, DeleteTimeout: 5 * time.Minute, KubernetesVersion: constants.DefaultKubernetesVersion, RetrieveResults: true, UseSpinner: true, } k8sVersion, err := semver.ParseTolerant(options.KubernetesVersion) if err != nil { return err } options.ResultsPath = fmt.Sprintf("v%d.%d/talos", k8sVersion.Major, k8sVersion.Minor) if err = os.MkdirAll(options.ResultsPath, 0o755); err != nil { return err } return Run(ctx, cluster, &options) } // Run the e2e test against cluster with provided options. // //nolint:gocyclo func Run(ctx context.Context, cluster cluster.K8sProvider, options *Options) error { cfg, err := cluster.K8sRestConfig(ctx) if err != nil { return fmt.Errorf("error getting kubernetes config: %w", err) } // reset timeout to prevent log streaming from timing out cfg.Timeout = 0 config := types.NewDefaultConfiguration() config.ConformanceImage = fmt.Sprintf("registry.k8s.io/conformance:v%s", options.KubernetesVersion) config.OutputDir = options.ResultsPath config.Parallel = options.Parallel config.Skip = options.Skip clientset, err := cluster.K8sClient(ctx) if err != nil { return fmt.Errorf("error getting kubernetes client: %w", err) } testRunner := conformance.NewTestRunner(config, clientset) testClient := client.NewClient(cfg, clientset, config.Namespace) cleanup := func() error { if err := testRunner.Cleanup(ctx); err != nil { return fmt.Errorf("failed to cleanup: %w", err) } return nil } defer cleanup() //nolint:errcheck if err = cleanup(); err != nil { return err } verboseGinkgo := config.Verbosity >= 6 showSpinner := !verboseGinkgo && config.Verbosity > 2 && options.UseSpinner && os.Getenv("CI") == "" fmt.Printf("running conformance tests version %s\n", options.KubernetesVersion) fmt.Printf("running tests: %s\n", strings.Join(options.RunTests, "|")) if err := testRunner.Deploy(ctx, strings.Join(options.RunTests, "|"), "", verboseGinkgo, config.StartupTimeout); err != nil { return fmt.Errorf("failed to deploy tests: %w", err) } before := time.Now() var spinner *common.Spinner if showSpinner { spinner = common.NewSpinner(os.Stdout) spinner.Start() } // PrintE2ELogs is a long running method if err := testClient.PrintE2ELogs(ctx); err != nil { return fmt.Errorf("failed to get test logs: %w", err) } if showSpinner { spinner.Stop() } fmt.Printf("tests finished after %v.\n", time.Since(before).Round(time.Second)) exitCode, err := testClient.FetchExitCode(ctx) if err != nil { return fmt.Errorf("failed to determine exit code: %w", err) } if exitCode == 0 { fmt.Println("tests completed successfully") } else { return fmt.Errorf("tests failed: code %d", exitCode) } if options.RetrieveResults { if err := testClient.FetchFiles(ctx, config.OutputDir); err != nil { return fmt.Errorf("failed to download results: %w", err) } productInfo, err := yaml.Marshal(talos) if err != nil { return fmt.Errorf("error marshaling product info: %w", err) } if err = os.WriteFile(filepath.Join(options.ResultsPath, "PRODUCT.yaml"), productInfo, 0o644); err != nil { return err } } return cleanup() } ================================================ FILE: pkg/cluster/hydrophone/product.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hydrophone import "github.com/siderolabs/talos/pkg/machinery/version" type product struct { Vendor string `yaml:"vendor"` Name string `yaml:"name"` Version string `yaml:"version"` WebsiteURL string `yaml:"website_url"` RepoURL string `yaml:"repo_url"` DocumentationURL string `yaml:"documentation_url"` ProductLogoURL string `yaml:"product_logo_url"` Type string `yaml:"type"` Description string `yaml:"description"` ContactEmail string `yaml:"contact_email_address"` } var talos = product{ Vendor: "Sidero Labs", Name: "Talos Linux", Version: version.Tag, WebsiteURL: "https://www.siderolabs.com/", RepoURL: "https://github.com/siderolabs/talos", DocumentationURL: "https://www.talos.dev", ProductLogoURL: "https://www.talos.dev/images/Sidero_stacked_darkbkgd_RGB.svg", Type: "installer", Description: "Talos Linux is Linux designed for Kubernetes - secure, immutable, and minimal.", ContactEmail: "developers@siderolabs.com", } ================================================ FILE: pkg/cluster/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" k8s "k8s.io/client-go/kubernetes" "github.com/siderolabs/talos/pkg/kubernetes" ) // KubernetesFromKubeletClient provides Kubernetes client built from local kubelet config. type KubernetesFromKubeletClient struct { KubeHelper *kubernetes.Client clientset *k8s.Clientset } // K8sClient builds Kubernetes client from local kubelet config. // // Kubernetes client instance is cached. func (k *KubernetesFromKubeletClient) K8sClient(ctx context.Context) (*k8s.Clientset, error) { if k.clientset != nil { return k.clientset, nil } var err error if k.KubeHelper, err = kubernetes.NewClientFromKubeletKubeconfig(); err != nil { return nil, err } k.clientset = k.KubeHelper.Clientset return k.clientset, nil } // K8sHelper returns wrapper around K8sClient. func (k *KubernetesFromKubeletClient) K8sHelper(ctx context.Context) (*kubernetes.Client, error) { if k.KubeHelper != nil { return k.KubeHelper, nil } _, err := k.K8sClient(ctx) if err != nil { return nil, err } return k.KubeHelper, nil } // K8sClose closes Kubernetes client. func (k *KubernetesFromKubeletClient) K8sClose() error { if k.KubeHelper == nil { return nil } return k.KubeHelper.Close() } ================================================ FILE: pkg/cluster/kubernetes/compat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "fmt" "sort" "github.com/blang/semver/v4" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/compatibility" ) // NodeVersion holds the node identifier along with its Talos version. type NodeVersion struct { Node string Version *compatibility.TalosVersion } // GetNodesTalosVersions retrieves the Talos versions for the specified nodes. func GetNodesTalosVersions(ctx context.Context, talosClient *client.Client, nodes []string) ([]NodeVersion, error) { eg, ctx := errgroup.WithContext(ctx) versions := make([]NodeVersion, len(nodes)) for i, node := range nodes { eg.Go(func() error { nodeCtx := client.WithNode(ctx, node) resp, err := talosClient.Version(nodeCtx) if err != nil { return fmt.Errorf("node %q: %w", node, err) } v, err := compatibility.ParseTalosVersion(resp.Messages[0].GetVersion()) if err != nil { return fmt.Errorf("node %q: %w", node, err) } versions[i] = NodeVersion{Node: node, Version: v} return nil }) } if err := eg.Wait(); err != nil { return nil, err } return versions, nil } // GetMinimumTalosVersion returns the minimum Talos version from the provided list of NodeVersion. func GetMinimumTalosVersion(versions []NodeVersion) (*compatibility.TalosVersion, error) { if len(versions) == 0 { return nil, nil } semvers := make([]struct { Index int Version semver.Version }, len(versions)) for i, nv := range versions { semverV, err := semver.ParseTolerant(nv.Version.String()) if err != nil { return nil, fmt.Errorf("failed to parse talos version %q for node %q: %w", nv.Version.String(), nv.Node, err) } semvers[i] = struct { Index int Version semver.Version }{ Index: i, Version: semverV, } } sort.Slice(semvers, func(i, j int) bool { return semvers[i].Version.Compare(semvers[j].Version) < 0 }) return versions[semvers[0].Index].Version, nil } // CheckCompatibility checks if all provided Talos versions are compatible with the given Kubernetes version. func CheckCompatibility(k8sVersion string, versions []NodeVersion) error { parsedK8s, err := compatibility.ParseKubernetesVersion(k8sVersion) if err != nil { return err } for _, nv := range versions { if err := parsedK8s.SupportedWith(nv.Version); err != nil { return fmt.Errorf("node %q: %w", nv.Node, err) } } return nil } // VerifyVersionCompatibility retrieves Talos versions for the specified nodes and checks their compatibility with the given Kubernetes version. func VerifyVersionCompatibility(ctx context.Context, talosClient *client.Client, nodes []string, k8sVersion string, logger func(string, ...any)) ( *compatibility.TalosVersion, error, ) { versions, err := GetNodesTalosVersions(ctx, talosClient, nodes) if err != nil { return nil, err } if err := CheckCompatibility(k8sVersion, versions); err != nil { return nil, err } minV, err := GetMinimumTalosVersion(versions) if err != nil { return nil, err } for _, nv := range versions { logger("> %q: Talos version %s is compatible with Kubernetes version %s", nv.Node, nv.Version, k8sVersion) } return minV, nil } ================================================ FILE: pkg/cluster/kubernetes/compat_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/cluster/kubernetes" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/compatibility" ) func TestGetMinimumTalosVersion(t *testing.T) { v100, err := compatibility.ParseTalosVersion(&machine.VersionInfo{Tag: "v1.0.0"}) require.NoError(t, err) v110, err := compatibility.ParseTalosVersion(&machine.VersionInfo{Tag: "v1.1.0"}) require.NoError(t, err) v120, err := compatibility.ParseTalosVersion(&machine.VersionInfo{Tag: "v1.2.0"}) require.NoError(t, err) v130Dirty, err := compatibility.ParseTalosVersion(&machine.VersionInfo{Tag: "1.3.0-alpha.2-93-gcd04c3dde-dirty"}) require.NoError(t, err) tests := []struct { name string versions []kubernetes.NodeVersion want *compatibility.TalosVersion wantErr bool }{ { name: "empty", versions: []kubernetes.NodeVersion{}, want: nil, wantErr: false, }, { name: "single version", versions: []kubernetes.NodeVersion{ { Node: "node1", Version: v100, }, }, want: v100, wantErr: false, }, { name: "single dirty version", versions: []kubernetes.NodeVersion{ { Node: "node1", Version: v130Dirty, }, }, want: v130Dirty, wantErr: false, }, { name: "multiple versions, sorted", versions: []kubernetes.NodeVersion{ { Node: "node1", Version: v100, }, { Node: "node2", Version: v110, }, { Node: "node3", Version: v120, }, }, want: v100, wantErr: false, }, { name: "multiple versions, unsorted", versions: []kubernetes.NodeVersion{ { Node: "node2", Version: v110, }, { Node: "node1", Version: v100, }, { Node: "node3", Version: v120, }, { Node: "node4dirty", Version: v130Dirty, }, }, want: v100, wantErr: false, }, { name: "multiple versions, same version", versions: []kubernetes.NodeVersion{ { Node: "node1", Version: v110, }, { Node: "node2", Version: v110, }, }, want: v110, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := kubernetes.GetMinimumTalosVersion(tt.versions) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) assert.Equal(t, tt.want, got) } }) } } ================================================ FILE: pkg/cluster/kubernetes/detect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "errors" "fmt" "strings" "github.com/blang/semver/v4" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // DetectLowestVersion returns lowest Kubernetes components versions in the cluster. // //nolint:gocyclo func DetectLowestVersion(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions) (string, error) { k8sClient, err := cluster.K8sHelper(ctx) if err != nil { return "", fmt.Errorf("error building kubernetes client: %w", err) } apps := map[string]struct{}{ "kube-apiserver": {}, "kube-controller-manager": {}, "kube-proxy": {}, "kube-scheduler": {}, } pods, err := k8sClient.CoreV1().Pods("kube-system").List(ctx, metav1.ListOptions{}) if err != nil { return "", err } var version *semver.Version for _, pod := range pods.Items { app := pod.GetObjectMeta().GetLabels()["k8s-app"] if _, ok := apps[app]; !ok { continue } for _, container := range pod.Spec.Containers { if container.Name != app { continue } image, _, _ := strings.Cut(container.Image, "@") _, imageTag, ok := strings.Cut(image, ":") if !ok { continue } v, err := semver.ParseTolerant(strings.TrimLeft(imageTag, "v")) if err != nil { options.Log("failed to parse %s container version %s", app, err) continue } if version == nil || v.LT(*version) { version = &v } } } if version == nil { return "", errors.New("failed to detect lowest Kubernetes version") } return version.String(), nil } ================================================ FILE: pkg/cluster/kubernetes/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "errors" "fmt" "slices" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xiter" "github.com/siderolabs/go-retry/retry" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/client" v1alpha1config "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) const kubelet = "kubelet" func upgradeKubelet(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions) error { if !options.UpgradeKubelet { options.Log("skipped updating kubelet") return nil } options.Log("updating kubelet to version %q", options.Path.ToVersion()) for node := range xiter.Concat(slices.Values(options.controlPlaneNodes), slices.Values(options.workerNodes)) { if err := upgradeKubeletOnNode(ctx, cluster, options, node); err != nil { return fmt.Errorf("error updating node %q: %w", node, err) } } return nil } //nolint:gocyclo,cyclop func upgradeKubeletOnNode(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions, node string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() c, err := cluster.Client() if err != nil { return fmt.Errorf("error building Talos API client: %w", err) } ctx = client.WithNode(ctx, node) options.Log(" > %q: starting update", node) watchCh := make(chan safe.WrappedStateEvent[*v1alpha1.Service]) if err = safe.StateWatch(ctx, c.COSI, resource.NewMetadata(v1alpha1.NamespaceName, v1alpha1.ServiceType, kubelet, resource.VersionUndefined), watchCh); err != nil { return fmt.Errorf("error watching service: %w", err) } var ev safe.WrappedStateEvent[*v1alpha1.Service] select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() != state.Created { return fmt.Errorf("unexpected event type: %s", ev.Type()) } initialService, err := ev.Resource() if err != nil { return fmt.Errorf("error inspecting service: %w", err) } if !initialService.TypedSpec().Running || !initialService.TypedSpec().Healthy { return errors.New("kubelet is not healthy") } // find out current kubelet version, as the machine config might have a missing image field, // look it up from the kubelet spec kubeletSpec, err := safe.StateGet[*k8s.KubeletSpec](ctx, c.COSI, resource.NewMetadata(k8s.NamespaceName, k8s.KubeletSpecType, kubelet, resource.VersionUndefined)) if err != nil { return fmt.Errorf("error fetching kubelet spec: %w", err) } skipWait := false err = patchNodeConfig(ctx, cluster, node, options.EncoderOpt, upgradeKubeletPatcher(options, kubeletSpec)) if err != nil { if errors.Is(err, errUpdateSkipped) { skipWait = true } else { return fmt.Errorf("error patching node config: %w", err) } } if options.DryRun { return nil } options.Log(" > %q: machine configuration patched", node) if !skipWait { options.Log(" > %q: waiting for kubelet restart", node) // first, wait for kubelet to go down for { select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() == state.Destroyed { break } } // now wait for kubelet to go up & healthy for { select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() == state.Created || ev.Type() == state.Updated { var service *v1alpha1.Service service, err = ev.Resource() if err != nil { return fmt.Errorf("error inspecting service: %w", err) } if service.TypedSpec().Running && service.TypedSpec().Healthy { break } } } } options.Log(" > %q: waiting for node update", node) if err = retry.Constant(3*time.Minute, retry.WithUnits(10*time.Second)).Retry( func() error { return checkNodeKubeletVersion(ctx, cluster, node, "v"+options.Path.ToVersion()) }, ); err != nil { return err } options.Log(" < %q: successfully updated", node) return nil } func extractKubeletVersionSuffix(imageRef string) string { for _, suffix := range []string{"-fat", "-slim"} { if strings.HasSuffix(imageRef, suffix) { return suffix } } return "" } func upgradeKubeletPatcher( options UpgradeOptions, kubeletSpec *k8s.KubeletSpec, ) func(config *v1alpha1config.Config) error { return func(config *v1alpha1config.Config) error { if config.MachineConfig == nil { config.MachineConfig = &v1alpha1config.MachineConfig{} } if config.MachineConfig.MachineKubelet == nil { config.MachineConfig.MachineKubelet = &v1alpha1config.KubeletConfig{} } oldImage := kubeletSpec.TypedSpec().Image oldImage, _, _ = strings.Cut(oldImage, "@") // ignore digest if present oldSuffix := extractKubeletVersionSuffix(oldImage) newVersion := options.Path.ToVersion() + oldSuffix logUpdate := func(oldImage string) { _, version, _ := strings.Cut(oldImage, ":") if version == "" { version = options.Path.FromVersion() } version = strings.TrimLeft(version, "v") options.Log(" > update %s: %s -> %s", kubelet, version, newVersion) if options.DryRun { options.Log(" > skipped in dry-run") } } image := fmt.Sprintf("%s:v%s", options.KubeletImage, newVersion) if oldImage == image { return errUpdateSkipped } logUpdate(oldImage) if options.DryRun { return errUpdateSkipped } config.MachineConfig.MachineKubelet.KubeletImage = image return nil } } //nolint:gocyclo func checkNodeKubeletVersion(ctx context.Context, cluster UpgradeProvider, nodeToCheck, version string) error { k8sClient, err := cluster.K8sHelper(ctx) if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } nodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { if kubernetes.IsRetryableError(err) { return retry.ExpectedError(err) } return err } nodeFound := false for _, node := range nodes.Items { matchingNode := false for _, address := range node.Status.Addresses { if address.Address == nodeToCheck { matchingNode = true break } } if !matchingNode { continue } nodeFound = true if node.Status.NodeInfo.KubeletVersion != version { return retry.ExpectedErrorf( "node version mismatch: got %q, expected %q", node.Status.NodeInfo.KubeletVersion, version, ) } ready := false for _, condition := range node.Status.Conditions { if condition.Type != v1.NodeReady { continue } if condition.Status == v1.ConditionTrue { ready = true break } } if !ready { return retry.ExpectedErrorf("node is not ready") } break } if !nodeFound { return retry.ExpectedErrorf("node %q not found", nodeToCheck) } return nil } ================================================ FILE: pkg/cluster/kubernetes/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubernetes provides cluster-wide kubernetes utilities. package kubernetes ================================================ FILE: pkg/cluster/kubernetes/kubernetes_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes_test import ( "fmt" "testing" "github.com/blang/semver/v4" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestUpgradePath(t *testing.T) { // ensure that upgrade path is available for n-5 supported Kubernetes versions latestVersion, err := semver.ParseTolerant(constants.DefaultKubernetesVersion) require.NoError(t, err) for minorVersion := latestVersion.Minor - constants.SupportedKubernetesVersions + 1; minorVersion <= latestVersion.Minor; minorVersion++ { thisVersion := fmt.Sprintf("%d.%d", latestVersion.Major, minorVersion) path, err := upgrade.NewPath(thisVersion, thisVersion) require.NoError(t, err) assert.True(t, path.IsSupported(), "upgrade path %s is not supported", path.String()) if minorVersion != latestVersion.Minor { nextVersion := fmt.Sprintf("%d.%d", latestVersion.Major, minorVersion+1) path, err = upgrade.NewPath(thisVersion, nextVersion) require.NoError(t, err) assert.True(t, path.IsSupported(), "upgrade path %s is not supported", path.String()) } } } ================================================ FILE: pkg/cluster/kubernetes/patch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/encoder" v1alpha1config "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // patchNodeConfig updates node configuration by means of patch function. func patchNodeConfig(ctx context.Context, cluster UpgradeProvider, node string, encoderOpt encoder.Option, patchFunc func(config *v1alpha1config.Config) error) error { c, err := cluster.Client() if err != nil { return fmt.Errorf("error building Talos API client: %w", err) } ctx = client.WithNode(ctx, node) mc, err := safe.StateGetByID[*config.MachineConfig](ctx, c.COSI, config.ActiveID) if err != nil { return fmt.Errorf("error fetching config resource: %w", err) } provider := mc.Provider() newProvider, err := provider.PatchV1Alpha1(patchFunc) if err != nil { return fmt.Errorf("error patching config: %w", err) } cfgBytes, err := newProvider.EncodeBytes(encoderOpt) if err != nil { return fmt.Errorf("error serializing config: %w", err) } _, err = c.ApplyConfiguration(ctx, &machine.ApplyConfigurationRequest{ Data: cfgBytes, Mode: machine.ApplyConfigurationRequest_NO_REBOOT, }) if err != nil { return fmt.Errorf("error applying config: %w", err) } return nil } ================================================ FILE: pkg/cluster/kubernetes/talos_managed.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "errors" "fmt" "slices" "strings" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/google/go-containerregistry/pkg/name" "github.com/siderolabs/gen/channel" "github.com/siderolabs/gen/xiter" "github.com/siderolabs/go-kubernetes/kubernetes/manifests" "github.com/siderolabs/go-kubernetes/kubernetes/ssa" ssacli "github.com/siderolabs/go-kubernetes/kubernetes/ssa/cli" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/client-go/informers" "k8s.io/client-go/tools/cache" "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" machinetype "github.com/siderolabs/talos/pkg/machinery/config/machine" v1alpha1config "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // UpgradeProvider are the cluster interfaces required by upgrade process. type UpgradeProvider interface { cluster.ClientProvider cluster.K8sProvider } // ValidateImageReference validates if the provided string is a valid Docker image reference. func ValidateImageReference(ref string) error { _, err := name.ParseReference(ref) if err != nil { return fmt.Errorf("invalid image reference: %w", err) } return nil } // Validate checks all image references in the upgrade options. func (options *UpgradeOptions) Validate() error { images := map[string]string{ "kubelet": options.KubeletImage, "apiserver": options.APIServerImage, "controller-manager": options.ControllerManagerImage, "scheduler": options.SchedulerImage, "proxy": options.ProxyImage, } for name, image := range images { if err := ValidateImageReference(image); err != nil { return fmt.Errorf("%s: %w", name, err) } } return nil } // Upgrade the Kubernetes control plane components, manifests, kubelets. // //nolint:gocyclo,cyclop func Upgrade(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions) error { if err := options.Validate(); err != nil { return fmt.Errorf("invalid upgrade options: %w", err) } if !options.Path.IsSupported() { return fmt.Errorf("unsupported upgrade path %s (from %q to %q)", options.Path, options.Path.FromVersion(), options.Path.ToVersion()) } k8sClient, err := cluster.K8sHelper(ctx) if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } defer k8sClient.Close() //nolint:errcheck options.controlPlaneNodes, err = k8sClient.NodeIPs(ctx, machinetype.TypeControlPlane) if err != nil { return fmt.Errorf("error fetching controlplane nodes: %w", err) } if len(options.controlPlaneNodes) == 0 { return errors.New("no controlplane nodes discovered") } options.Log("discovered controlplane nodes %q", options.controlPlaneNodes) if options.UpgradeKubelet { options.workerNodes, err = k8sClient.NodeIPs(ctx, machinetype.TypeWorker) if err != nil { return fmt.Errorf("error fetching worker nodes: %w", err) } options.Log("discovered worker nodes %q", options.workerNodes) } talosClient, err := cluster.Client() if err != nil { return err } k8sConfig, err := cluster.K8sRestConfig(ctx) if err != nil { return err } minTalosVersion, err := VerifyVersionCompatibility(ctx, talosClient, slices.Concat(options.controlPlaneNodes, options.workerNodes), options.Path.ToVersion(), options.Log) if err != nil { return err } upgradeChecks, err := upgrade.NewChecks(options.Path, talosClient.COSI, k8sConfig, options.controlPlaneNodes, options.workerNodes, options.Log) if err != nil { return err } if err = upgradeChecks.Run(ctx); err != nil { return err } if options.PrePullImages { if err = prePullImages(ctx, talosClient, options); err != nil { return fmt.Errorf("failed pre-pulling images: %w", err) } } for _, service := range []string{kubeAPIServer, kubeControllerManager, kubeScheduler} { if err = upgradeStaticPod(ctx, cluster, options, service); err != nil { return fmt.Errorf("failed updating service %q: %w", service, err) } } if err = upgradeKubeProxy(ctx, cluster, options); err != nil { return fmt.Errorf("failed updating kube-proxy: %w", err) } if options.UpgradeKubelet { if err = upgradeKubelet(ctx, cluster, options); err != nil { return fmt.Errorf("failed upgrading kubelet: %w", err) } } useSSA := minTalosVersion.SupportsSSAManifestSync() return PerformManifestsSync(ctx, cluster, useSSA, options) } func prePullImages(ctx context.Context, talosClient *client.Client, options UpgradeOptions) error { for _, imageRef := range []string{ fmt.Sprintf("%s:v%s", options.APIServerImage, options.Path.ToVersion()), fmt.Sprintf("%s:v%s", options.ControllerManagerImage, options.Path.ToVersion()), fmt.Sprintf("%s:v%s", options.SchedulerImage, options.Path.ToVersion()), } { for _, node := range options.controlPlaneNodes { options.Log(" > %q: pre-pulling %s", node, imageRef) //nolint:staticcheck // using legacy method, but should be refactored err := talosClient.ImagePull(client.WithNode(ctx, node), common.ContainerdNamespace_NS_CRI, imageRef) if err != nil { if status.Code(err) == codes.Unimplemented { options.Log(" < %q: not implemented, skipping", node) } else { return fmt.Errorf("error pre-pulling %s on %s: %w", imageRef, node, err) } } } } if !options.UpgradeKubelet { return nil } for node := range xiter.Concat(slices.Values(options.controlPlaneNodes), slices.Values(options.workerNodes)) { kubeletSpec, err := safe.StateGet[*k8s.KubeletSpec](client.WithNode(ctx, node), talosClient.COSI, resource.NewMetadata(k8s.NamespaceName, k8s.KubeletSpecType, kubelet, resource.VersionUndefined)) if err != nil { return fmt.Errorf("error fetching kubelet spec on node %s: %w", node, err) } imageSuffix := extractKubeletVersionSuffix(kubeletSpec.TypedSpec().Image) imageRef := fmt.Sprintf("%s:v%s%s", options.KubeletImage, options.Path.ToVersion(), imageSuffix) options.Log(" > %q: pre-pulling %s", node, imageRef) //nolint:staticcheck // using legacy method, but should be refactored err = talosClient.ImagePull(client.WithNode(ctx, node), common.ContainerdNamespace_NS_SYSTEM, imageRef) if err != nil { if status.Code(err) == codes.Unimplemented { options.Log(" < %q: not implemented, skipping", node) } else { return fmt.Errorf("error pre-pulling %s on %s: %w", imageRef, node, err) } } } return nil } func upgradeStaticPod(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions, service string) error { options.Log("updating %q to version %q", service, options.Path.ToVersion()) for _, node := range options.controlPlaneNodes { if err := upgradeStaticPodOnNode(ctx, cluster, options, service, node); err != nil { return fmt.Errorf("error updating node %q: %w", node, err) } } return nil } func upgradeKubeProxy(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions) error { options.Log("updating kube-proxy to version %q", options.Path.ToVersion()) for _, node := range options.controlPlaneNodes { options.Log(" > %q: starting update", node) if err := patchNodeConfig(ctx, cluster, node, options.EncoderOpt, patchKubeProxy(options)); err != nil { return fmt.Errorf("error updating node %q: %w", node, err) } } return nil } func patchKubeProxy(options UpgradeOptions) func(config *v1alpha1config.Config) error { return func(config *v1alpha1config.Config) error { if options.DryRun { options.Log(" > skipped in dry-run") return nil } if config.ClusterConfig == nil { config.ClusterConfig = &v1alpha1config.ClusterConfig{} } if config.ClusterConfig.ProxyConfig == nil { config.ClusterConfig.ProxyConfig = &v1alpha1config.ProxyConfig{} } config.ClusterConfig.ProxyConfig.ContainerImage = fmt.Sprintf("%s:v%s", options.ProxyImage, options.Path.ToVersion()) return nil } } func controlplaneConfigResourceType(service string) resource.Type { switch service { case kubeAPIServer: return k8s.APIServerConfigType case kubeControllerManager: return k8s.ControllerManagerConfigType case kubeScheduler: return k8s.SchedulerConfigType } panic(fmt.Sprintf("unknown service ID %q", service)) } //nolint:gocyclo func upgradeStaticPodOnNode(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions, service, node string) error { ctx, cancel := context.WithCancel(ctx) defer cancel() c, err := cluster.Client() if err != nil { return fmt.Errorf("error building Talos API client: %w", err) } ctx = client.WithNode(ctx, node) options.Log(" > %q: starting update", node) watchCh := make(chan state.Event) if err = c.COSI.Watch(ctx, resource.NewMetadata(k8s.ControlPlaneNamespaceName, controlplaneConfigResourceType(service), service, resource.VersionUndefined), watchCh); err != nil { return fmt.Errorf("error watching service configuration: %w", err) } var ( expectedConfigVersion string initialConfig resource.Resource ) select { case ev := <-watchCh: if ev.Type != state.Created { return fmt.Errorf("unexpected event type: %d", ev.Type) } expectedConfigVersion = ev.Resource.Metadata().Version().String() initialConfig = ev.Resource case <-ctx.Done(): return ctx.Err() } skipConfigWait := false err = patchNodeConfig(ctx, cluster, node, options.EncoderOpt, upgradeStaticPodPatcher(options, service, initialConfig)) if err != nil { if errors.Is(err, errUpdateSkipped) { skipConfigWait = true } else { return fmt.Errorf("error patching node config: %w", err) } } if options.DryRun { return nil } options.Log(" > %q: machine configuration patched", node) options.Log(" > %q: waiting for %s pod update", node, service) if !skipConfigWait { select { case ev := <-watchCh: if ev.Type != state.Updated { return fmt.Errorf("unexpected event type: %d", ev.Type) } expectedConfigVersion = ev.Resource.Metadata().Version().String() case <-ctx.Done(): return ctx.Err() } } if err = checkPodStatus(ctx, cluster, options, service, node, expectedConfigVersion); err != nil { return err } options.Log(" < %q: successfully updated", node) return nil } var errUpdateSkipped = errors.New("update skipped") func staticPodImage(logUpdate func(oldImage string), imageName, containerImage, configImage string, options UpgradeOptions) (string, error) { image := fmt.Sprintf("%s:v%s", imageName, options.Path.ToVersion()) if containerImage == image || configImage == image { return "", errUpdateSkipped } logUpdate(containerImage) if options.DryRun { return "", errUpdateSkipped } return image, nil } //nolint:gocyclo func upgradeStaticPodPatcher(options UpgradeOptions, service string, configResource resource.Resource) func(config *v1alpha1config.Config) error { return func(config *v1alpha1config.Config) error { if config.ClusterConfig == nil { config.ClusterConfig = &v1alpha1config.ClusterConfig{} } var configImage string switch r := configResource.(type) { case *k8s.APIServerConfig: configImage = r.TypedSpec().Image case *k8s.ControllerManagerConfig: configImage = r.TypedSpec().Image case *k8s.SchedulerConfig: configImage = r.TypedSpec().Image default: return fmt.Errorf("unsupported service config %T", configResource) } logUpdate := func(oldImage string) { oldImage, _, _ = strings.Cut(oldImage, "@") // ignore digest if present _, version, _ := strings.Cut(oldImage, ":") if version == "" { version = options.Path.FromVersion() } options.Log(" > update %s: %s -> %s", service, version, options.Path.ToVersion()) if options.DryRun { options.Log(" > skipped in dry-run") } } switch service { case kubeAPIServer: if config.ClusterConfig.APIServerConfig == nil { config.ClusterConfig.APIServerConfig = &v1alpha1config.APIServerConfig{} } image, err := staticPodImage(logUpdate, options.APIServerImage, config.ClusterConfig.APIServerConfig.ContainerImage, configImage, options) if err != nil { return err } config.ClusterConfig.APIServerConfig.ContainerImage = image case kubeControllerManager: if config.ClusterConfig.ControllerManagerConfig == nil { config.ClusterConfig.ControllerManagerConfig = &v1alpha1config.ControllerManagerConfig{} } image, err := staticPodImage(logUpdate, options.ControllerManagerImage, config.ClusterConfig.ControllerManagerConfig.ContainerImage, configImage, options) if err != nil { return err } config.ClusterConfig.ControllerManagerConfig.ContainerImage = image case kubeScheduler: if config.ClusterConfig.SchedulerConfig == nil { config.ClusterConfig.SchedulerConfig = &v1alpha1config.SchedulerConfig{} } image, err := staticPodImage(logUpdate, options.SchedulerImage, config.ClusterConfig.SchedulerConfig.ContainerImage, configImage, options) if err != nil { return err } config.ClusterConfig.SchedulerConfig.ContainerImage = image default: return fmt.Errorf("unsupported service %q", service) } return nil } } // PerformManifestsSync performs manifests sync from Talos manifest list to Kubernetes. func PerformManifestsSync( ctx context.Context, cluster UpgradeProvider, useSSA bool, options UpgradeOptions, ) error { objects, err := getManifests(ctx, cluster) if err != nil { return err } // Use server-side apply for Talos versions >= v1.13.0 if useSSA { return syncManifestsSSA(ctx, objects, cluster, options) } return syncManifests(ctx, objects, cluster, options) } func getManifests(ctx context.Context, cluster UpgradeProvider) ([]*unstructured.Unstructured, error) { talosclient, err := cluster.Client() if err != nil { return nil, err } defer cluster.Close() //nolint:errcheck md, _ := metadata.FromOutgoingContext(ctx) if nodes := md["nodes"]; len(nodes) > 0 { ctx = client.WithNode(ctx, nodes[0]) } return manifests.GetBootstrapManifests(ctx, talosclient.COSI, nil) } // syncManifests is the legacy non SSA manifests sync function. // It should be removed once Talos v1.12 is no longer supported. func syncManifests(ctx context.Context, objects []*unstructured.Unstructured, cluster UpgradeProvider, options UpgradeOptions) error { config, err := cluster.K8sRestConfig(ctx) if err != nil { return err } return manifests.SyncWithLog(ctx, objects, config, options.DryRun, options.Log) } //nolint:gocyclo func syncManifestsSSA(ctx context.Context, objects []*unstructured.Unstructured, cluster UpgradeProvider, options UpgradeOptions) error { config, err := cluster.K8sRestConfig(ctx) if err != nil { return err } updatingManifestsLogline := "updating manifests" if options.DryRun { updatingManifestsLogline += " (dry run)" } options.Log("%s", updatingManifestsLogline) manager, err := ssa.NewManager(ctx, config, constants.KubernetesFieldManagerName, constants.KubernetesInventoryNamespace, constants.KubernetesBootstrapManifestsInventoryName, ) if err != nil { return fmt.Errorf("error creating SSA manager: %w", err) } defer manager.Close() if options.DryRun { // only do the diff in dry-run mode changes, err := manager.Diff(ctx, objects, ssa.DiffOptions{ NoPrune: options.NoPrune, InventoryPolicy: options.InventoryPolicy, }) if err != nil { return fmt.Errorf("error diffing manifests: %w", err) } for _, change := range changes { options.Log(" < %s %s", change.Action, change.Subject) if change.Diff != "" { options.Log("%s", change.Diff) } } return nil } changes, err := manager.Apply(ctx, objects, ssa.ApplyOptions{ InventoryPolicy: options.InventoryPolicy, WaitTimeout: options.ReconcileTimeout, NoPrune: options.NoPrune, Force: options.ForceManifests, }) if err != nil { return fmt.Errorf("error applying manifests: %w", err) } ssacli.LogApplyResults(ctx, changes, manager, options.Log) if options.SkipManifestWait { options.Log("skipping waiting for manifest reconciliation") return nil } waitOptions := ssa.WaitOptions{ Interval: 2 * time.Second, Timeout: options.ReconcileTimeout, FailFast: true, } return ssacli.Wait(ctx, changes, options.Log, manager, waitOptions) } //nolint:gocyclo func checkPodStatus(ctx context.Context, cluster UpgradeProvider, options UpgradeOptions, service, node, configVersion string) error { ctx, cancel := context.WithTimeout(ctx, 5*time.Minute) defer cancel() k8sClient, err := cluster.K8sHelper(ctx) if err != nil { return fmt.Errorf("error building kubernetes client: %w", err) } defer k8sClient.Close() //nolint:errcheck informerFactory := informers.NewSharedInformerFactoryWithOptions( k8sClient, 10*time.Second, informers.WithNamespace(namespace), informers.WithTweakListOptions(func(options *metav1.ListOptions) { options.LabelSelector = fmt.Sprintf("k8s-app = %s", service) }), ) notifyCh := make(chan *v1.Pod) informer := informerFactory.Core().V1().Pods().Informer() if err := informer.SetWatchErrorHandler(func(r *cache.Reflector, err error) { options.Log("kubernetes endpoint watch error: %s", err) }); err != nil { return fmt.Errorf("error setting watch error handler: %w", err) } if _, err := informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj any) { channel.SendWithContext(ctx, notifyCh, obj.(*v1.Pod)) }, DeleteFunc: func(_ any) {}, UpdateFunc: func(_, obj any) { channel.SendWithContext(ctx, notifyCh, obj.(*v1.Pod)) }, }); err != nil { return fmt.Errorf("error adding watch event handler: %w", err) } informerFactory.Start(ctx.Done()) defer func() { cancel() informerFactory.Shutdown() }() for { select { case <-ctx.Done(): return ctx.Err() case pod := <-notifyCh: if pod.Status.HostIP != node { continue } if pod.Annotations[constants.AnnotationStaticPodConfigVersion] != configVersion { options.Log(" > %q: %s: waiting, config version mismatch: got %q, expected %q", node, service, pod.Annotations[constants.AnnotationStaticPodConfigVersion], configVersion) continue } ready := false for _, condition := range pod.Status.Conditions { if condition.Type != v1.PodReady { continue } if condition.Status == v1.ConditionTrue { ready = true break } } if !ready { options.Log(" > %q: %s: pod is not ready, waiting", node, service) continue } return nil } } } ================================================ FILE: pkg/cluster/kubernetes/upgrade.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "fmt" "io" "time" "github.com/siderolabs/go-kubernetes/kubernetes/ssa" "github.com/siderolabs/go-kubernetes/kubernetes/upgrade" "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) const ( namespace = "kube-system" kubeAPIServer = "kube-apiserver" kubeControllerManager = "kube-controller-manager" kubeScheduler = "kube-scheduler" ) // UpgradeOptions represents Kubernetes control plane upgrade settings. type UpgradeOptions struct { Path *upgrade.Path DryRun bool ControlPlaneEndpoint string LogOutput io.Writer PrePullImages bool UpgradeKubelet bool EncoderOpt encoder.Option KubeletImage string APIServerImage string ControllerManagerImage string SchedulerImage string ProxyImage string NoPrune bool ForceManifests bool ReconcileTimeout time.Duration InventoryPolicy ssa.InventoryPolicy SkipManifestWait bool controlPlaneNodes []string workerNodes []string } // Log writes the line to logger or to stdout if no logger was provided. func (options *UpgradeOptions) Log(line string, args ...any) { if options.LogOutput != nil { fmt.Fprintf(options.LogOutput, line, args...) //nolint:errcheck return } fmt.Printf(line+"\n", args...) } ================================================ FILE: pkg/cluster/kubernetes/upgrade_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/cluster/kubernetes" ) func TestValidateImageReference(t *testing.T) { tests := []struct { name string ref string wantErr bool errMsg string }{ { name: "valid simple image", ref: "k8s.gcr.io/kube-apiserver:v1.23.0", wantErr: false, }, { name: "valid image with digest", ref: "k8s.gcr.io/kube-apiserver@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", wantErr: false, }, { name: "valid image with port", ref: "localhost:5000/kube-apiserver:latest", wantErr: false, }, { name: "invalid image reference", ref: "invalid/image@sha256:invalid", wantErr: true, errMsg: "invalid image reference", }, { name: "invalid image reference v2", ref: ":v1.32.1", wantErr: true, errMsg: "invalid image reference", }, { name: "empty image reference", ref: "", wantErr: true, errMsg: "invalid image reference", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := kubernetes.ValidateImageReference(tt.ref) if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } func TestUpgradeOptions_validate(t *testing.T) { tests := []struct { name string options kubernetes.UpgradeOptions wantErr bool errMsg string }{ { name: "valid images", options: kubernetes.UpgradeOptions{ KubeletImage: "k8s.gcr.io/kubelet:v1.23.0", APIServerImage: "k8s.gcr.io/kube-apiserver:v1.23.0", ControllerManagerImage: "k8s.gcr.io/kube-controller-manager:v1.23.0", SchedulerImage: "k8s.gcr.io/kube-scheduler:v1.23.0", ProxyImage: "k8s.gcr.io/kube-proxy:v1.23.0", }, wantErr: false, }, { name: "invalid kubelet image", options: kubernetes.UpgradeOptions{ KubeletImage: "invalid/image@sha256:invalid", APIServerImage: "k8s.gcr.io/kube-apiserver:v1.23.0", ControllerManagerImage: "k8s.gcr.io/kube-controller-manager:v1.23.0", SchedulerImage: "k8s.gcr.io/kube-scheduler:v1.23.0", ProxyImage: "k8s.gcr.io/kube-proxy:v1.23.0", }, wantErr: true, errMsg: "kubelet: invalid image reference", }, { name: "invalid apiserver image", options: kubernetes.UpgradeOptions{ KubeletImage: "k8s.gcr.io/kubelet:v1.23.0", APIServerImage: ":v1.23.0", ControllerManagerImage: "k8s.gcr.io/kube-controller-manager:v1.23.0", SchedulerImage: "k8s.gcr.io/kube-scheduler:v1.23.0", ProxyImage: "k8s.gcr.io/kube-proxy:v1.23.0", }, wantErr: true, errMsg: "apiserver: invalid image reference", }, { name: "image with digest", options: kubernetes.UpgradeOptions{ KubeletImage: "k8s.gcr.io/kubelet@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", APIServerImage: "k8s.gcr.io/kube-apiserver:v1.23.0@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ControllerManagerImage: "k8s.gcr.io/kube-controller-manager:v1.23.0", SchedulerImage: "k8s.gcr.io/kube-scheduler:v1.23.0", ProxyImage: "k8s.gcr.io/kube-proxy:v1.23.0", }, wantErr: false, }, { name: "image with port number", options: kubernetes.UpgradeOptions{ KubeletImage: "localhost:5000/kubelet:latest", APIServerImage: "k8s.gcr.io/kube-apiserver:v1.23.0", ControllerManagerImage: "k8s.gcr.io/kube-controller-manager:v1.23.0", SchedulerImage: "k8s.gcr.io/kube-scheduler:v1.23.0", ProxyImage: "k8s.gcr.io/kube-proxy:v1.23.0", }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.options.Validate() if tt.wantErr { assert.Error(t, err) if tt.errMsg != "" { assert.Contains(t, err.Error(), tt.errMsg) } } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/cluster/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "net" "strconv" "strings" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" k8s "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/constants" ) // KubernetesClient provides Kubernetes client built via Talos API Kubeconfig. type KubernetesClient struct { // Base Talos client provider. ClientProvider // ForceEndpoint overrides default Kubernetes API endpoint. ForceEndpoint string KubeHelper *k8s.Client kubeconfig []byte clientset *kubernetes.Clientset } // Kubeconfig returns raw kubeconfig. // // Kubeconfig is cached. func (k *KubernetesClient) Kubeconfig(ctx context.Context) ([]byte, error) { if k.kubeconfig != nil { return k.kubeconfig, nil } client, err := k.Client() if err != nil { return nil, err } k.kubeconfig, err = client.Kubeconfig(ctx) return k.kubeconfig, err } // K8sRestConfig returns *rest.Config (parsed kubeconfig). func (k *KubernetesClient) K8sRestConfig(ctx context.Context) (*rest.Config, error) { kubeconfig, err := k.Kubeconfig(ctx) if err != nil { return nil, err } config, err := clientcmd.BuildConfigFromKubeconfigGetter("", func() (*clientcmdapi.Config, error) { return clientcmd.Load(kubeconfig) }) if err != nil { return nil, err } if k.ForceEndpoint != "" { forceEndpoint, _ := strings.CutPrefix(k.ForceEndpoint, "https://") host, port, err := net.SplitHostPort(forceEndpoint) if err != nil { host = forceEndpoint port = strconv.Itoa(constants.DefaultControlPlanePort) } config.Host = net.JoinHostPort(host, port) } return config, nil } // K8sClient builds Kubernetes client via Talos Kubeconfig API. // // Kubernetes client instance is cached. func (k *KubernetesClient) K8sClient(ctx context.Context) (*kubernetes.Clientset, error) { if k.clientset != nil { return k.clientset, nil } config, err := k.K8sRestConfig(ctx) if err != nil { return nil, err } if k.KubeHelper, err = k8s.NewForConfig(config); err != nil { return nil, err } k.clientset = k.KubeHelper.Clientset return k.clientset, nil } // K8sHelper returns wrapper around K8sClient. func (k *KubernetesClient) K8sHelper(ctx context.Context) (*k8s.Client, error) { if k.KubeHelper != nil { return k.KubeHelper, nil } _, err := k.K8sClient(ctx) if err != nil { return nil, err } return k.KubeHelper, nil } // K8sClose closes Kubernetes client. func (k *KubernetesClient) K8sClose() error { if k.KubeHelper == nil { return nil } return k.KubeHelper.Close() } ================================================ FILE: pkg/cluster/local.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "context" "fmt" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" secretsgen "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/role" ) // LocalClientProvider builds Talos client to connect to same-node apid instance over file socket. type LocalClientProvider struct { client *client.Client resources state.State roles role.Set } // NewLocalClientProvider creates a new LocalClientProvider instance. // // This provider only works on controlplane nodes, as it relies on the // root Talos API certificate being available. func NewLocalClientProvider(resources state.State, roles role.Set) *LocalClientProvider { return &LocalClientProvider{ resources: resources, roles: roles, } } // Client returns Talos client instance for default (if no endpoints are given) or // specific endpoints. // // Client implements ClientProvider interface. func (c *LocalClientProvider) Client(endpoints ...string) (*client.Client, error) { if c.client != nil { return c.client, nil } ctx := context.TODO() rootSecrets, err := safe.StateGetByID[*secrets.OSRoot](ctx, c.resources, secrets.OSRootID) if err != nil { return nil, fmt.Errorf("failed to get OS root secrets: %w", err) } nodeAddress, err := safe.StateGetByID[*network.NodeAddress](ctx, c.resources, network.NodeAddressDefaultID) if err != nil { return nil, fmt.Errorf("failed to get node address: %w", err) } if len(nodeAddress.TypedSpec().IPs()) == 0 { return nil, fmt.Errorf("no node IPs found in node address") } if len(endpoints) == 0 { endpoints = []string{nodeAddress.TypedSpec().IPs()[0].String()} } // use a short-lived certificate, as we need to connect once const certificateTTL = 10 * time.Minute cert, err := secretsgen.NewAdminCertificateAndKey( time.Now(), rootSecrets.TypedSpec().IssuingCA, c.roles, certificateTTL, ) if err != nil { return nil, fmt.Errorf("failed to generate client certificate: %w", err) } talosconfig := clientconfig.NewConfig("local", endpoints, rootSecrets.TypedSpec().IssuingCA.Crt, cert) c.client, err = client.New( ctx, client.WithConfig(talosconfig), ) return c.client, err } // Close all the client connections. func (c *LocalClientProvider) Close() error { if c.client != nil { if err := c.client.Close(); err != nil { return err } c.client = nil } return nil } ================================================ FILE: pkg/cluster/logsaarchive.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "compress/gzip" "fmt" "io" "os" "path/filepath" "strings" "github.com/siderolabs/talos/pkg/imager/filemap" ) // SaveClusterLogsArchive saves all logs from the cluster state directory to a gzip archive. func SaveClusterLogsArchive(statePath, archivePath string) { if err := saveClusterLogsArchive(statePath, archivePath); err != nil { fmt.Fprintf(os.Stderr, "error saving cluster logs archive: %v\n", err) } } func saveClusterLogsArchive(statePath, archivePath string) error { var logFileMap []filemap.File if err := filepath.WalkDir(statePath, func(path string, d os.DirEntry, err error) error { if err != nil { return err } if !strings.HasSuffix(path, ".log") { return nil } rel, err := filepath.Rel(statePath, path) if err != nil { return err } if d.IsDir() && rel == "." { return nil } statInfo, err := d.Info() if err != nil { return err } logFileMap = append(logFileMap, filemap.File{ ImagePath: rel, SourcePath: path, ImageMode: int64(statInfo.Mode().Perm()), }) return nil }); err != nil { return fmt.Errorf("error building filemap: %w", err) } logFile, err := os.Create(archivePath) if err != nil { return fmt.Errorf("error creating log archive: %w", err) } defer logFile.Close() //nolint:errcheck gzipWriter := gzip.NewWriter(logFile) defer gzipWriter.Close() //nolint:errcheck r := filemap.Build(logFileMap) if _, err := io.Copy(gzipWriter, r); err != nil { return fmt.Errorf("error writing log archive: %w", err) } return nil } ================================================ FILE: pkg/cluster/provision.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "net/netip" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/provision" ) // MapProvisionNodeInfosToClusterNodeInfos maps provision.NodeInfos to cluster.NodeInfos. func MapProvisionNodeInfosToClusterNodeInfos(nodes []provision.NodeInfo) ([]NodeInfo, error) { result := make([]NodeInfo, len(nodes)) for i, info := range nodes { clusterNodeInfo, err := toClusterNodeInfo(info) if err != nil { return nil, err } result[i] = *clusterNodeInfo } return result, nil } // MapProvisionNodeInfosToNodeInfosByType maps provision.NodeInfos // to cluster.NodeInfos, grouping them by machine type. func MapProvisionNodeInfosToNodeInfosByType(nodes []provision.NodeInfo) (map[machine.Type][]NodeInfo, error) { result := make(map[machine.Type][]NodeInfo) for _, info := range nodes { clusterNodeInfo, err := toClusterNodeInfo(info) if err != nil { return nil, err } result[info.Type] = append(result[info.Type], *clusterNodeInfo) } return result, nil } func toClusterNodeInfo(info provision.NodeInfo) (*NodeInfo, error) { ips := make([]netip.Addr, len(info.IPs)) for i, ip := range info.IPs { parsed, err := netip.ParseAddr(ip.String()) if err != nil { return nil, err } ips[i] = parsed } return &NodeInfo{ InternalIP: ips[0], IPs: ips, }, nil } ================================================ FILE: pkg/conditions/all.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package conditions provides a set of conditions which can be used to wait for some condition to be met. package conditions import ( "context" "strings" "sync" "github.com/hashicorp/go-multierror" ) type all struct { mu sync.Mutex conditions []Condition } type waitResult struct { i int err error } func (a *all) Wait(ctx context.Context) error { errCh := make(chan waitResult) a.mu.Lock() for i := range a.conditions { go func(i int) { errCh <- waitResult{ err: a.conditions[i].Wait(ctx), i: i, } }(i) } a.mu.Unlock() err := (*multierror.Error)(nil) for range a.conditions { res := <-errCh a.mu.Lock() a.conditions[res.i] = nil a.mu.Unlock() err = multierror.Append(err, res.err) } // collapse errors if any of them is context canceled if err != nil { for _, e := range err.Errors { if e == context.Canceled { return e } } } return err.ErrorOrNil() } func (a *all) String() string { descriptions := []string(nil) a.mu.Lock() for _, c := range a.conditions { if c != nil { descriptions = append(descriptions, c.String()) } } a.mu.Unlock() return strings.Join(descriptions, ", ") } // WaitForAll creates a condition which waits for all the conditions to be successful. // // If the condition is nil, it is ignored. // WaitForAll(nil) return nil. func WaitForAll(conditions ...Condition) Condition { res := &all{} for _, c := range conditions { if c == nil { continue } if multi, ok := c.(*all); ok { // flatten lists res.conditions = append(res.conditions, multi.conditions...) } else { res.conditions = append(res.conditions, c) } } return res } ================================================ FILE: pkg/conditions/all_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions_test import ( "context" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/conditions" ) type AllSuite struct { suite.Suite } type MockCondition struct { description string errCh chan error } func (mc *MockCondition) String() string { return mc.description } func (mc *MockCondition) Wait(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() case err := <-mc.errCh: return err } } func (suite *AllSuite) TestString() { suite.Require().Equal("A, B", conditions.WaitForAll( &MockCondition{description: "A"}, &MockCondition{description: "B"}, ).String()) suite.Require().Equal("A", conditions.WaitForAll( &MockCondition{description: "A"}, ).String()) conds := []conditions.Condition{ &MockCondition{description: "A", errCh: make(chan error)}, &MockCondition{description: "B", errCh: make(chan error)}, } waiter := conditions.WaitForAll(conds...) done := make(chan error) go func() { done <- waiter.Wait(context.Background()) }() suite.Require().Equal("A, B", waiter.String()) conds[0].(*MockCondition).errCh <- nil time.Sleep(50 * time.Millisecond) // done waiting for 'A', so description should now be shorter suite.Require().Equal("B", waiter.String()) conds[1].(*MockCondition).errCh <- nil <-done } func (suite *AllSuite) TestFlatten() { conds1 := []conditions.Condition{ &MockCondition{description: "A", errCh: make(chan error)}, &MockCondition{description: "B", errCh: make(chan error)}, } conds2 := []conditions.Condition{ &MockCondition{description: "C", errCh: make(chan error)}, &MockCondition{description: "D", errCh: make(chan error)}, } waiter := conditions.WaitForAll(conditions.WaitForAll(conds1...), conditions.WaitForAll(conds2...)) suite.Require().Equal("A, B, C, D", waiter.String()) ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() done := make(chan error) go func() { done <- waiter.Wait(ctx) }() conds1[0].(*MockCondition).errCh <- nil conds2[1].(*MockCondition).errCh <- nil time.Sleep(50 * time.Millisecond) suite.Require().Equal("B, C", waiter.String()) ctxCancel() suite.Require().Equal(context.Canceled, <-done) } func TestAllSuite(t *testing.T) { suite.Run(t, new(AllSuite)) } ================================================ FILE: pkg/conditions/conditions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions import ( "context" "fmt" ) // OK is returned by the String method of the passed Condition. const OK = "OK" // Condition is a object which Wait()s for some condition to become true. // // Condition can describe itself via String() method. type Condition interface { fmt.Stringer Wait(ctx context.Context) error } ================================================ FILE: pkg/conditions/files.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions import ( "context" "errors" "fmt" "io/fs" "os" "time" "github.com/siderolabs/gen/xslices" ) type file string func (filename file) Wait(ctx context.Context) error { ticker := time.NewTicker(time.Second) defer ticker.Stop() for { _, err := os.Stat(string(filename)) if err == nil { return nil } if !errors.Is(err, fs.ErrNotExist) { return err } select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } } } func (filename file) String() string { return fmt.Sprintf("file %q to exist", string(filename)) } // WaitForFileToExist is a service condition that will wait for the existence of // a file. func WaitForFileToExist(filename string) Condition { return file(filename) } // WaitForFilesToExist is a service condition that will wait for the existence of all the files. func WaitForFilesToExist(filenames ...string) Condition { conditions := xslices.Map(filenames, WaitForFileToExist) return WaitForAll(conditions...) } ================================================ FILE: pkg/conditions/files_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions_test import ( "context" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/conditions" ) type FilesSuite struct { suite.Suite tempDir string } func (suite *FilesSuite) SetupSuite() { suite.tempDir = suite.T().TempDir() } func (suite *FilesSuite) createFile(name string) (path string) { path = filepath.Join(suite.tempDir, name) f, err := os.Create(path) suite.Require().NoError(err) suite.Require().NoError(f.Close()) return path } func (suite *FilesSuite) TestString() { suite.Require().Equal("file \"abc.txt\" to exist", conditions.WaitForFileToExist("abc.txt").String()) } func (suite *FilesSuite) TestWaitForFileToExist() { path := suite.createFile("w.txt") err := conditions.WaitForFileToExist(path).Wait(context.Background()) suite.Require().NoError(err) suite.Require().NoError(os.Remove(path)) errCh := make(chan error) go func() { errCh <- conditions.WaitForFileToExist(path).Wait(context.Background()) }() time.Sleep(50 * time.Millisecond) select { case <-errCh: suite.Fail("unexpected return") default: } suite.createFile("w.txt") suite.Require().NoError(<-errCh) suite.Require().NoError(os.Remove(path)) ctx, ctxCancel := context.WithCancel(context.Background()) go func() { errCh <- conditions.WaitForFileToExist(path).Wait(ctx) }() time.Sleep(50 * time.Millisecond) select { case <-errCh: suite.Fail("unexpected return") default: } ctxCancel() suite.Require().EqualError(<-errCh, context.Canceled.Error()) } func (suite *FilesSuite) TestWaitForAllFilesToExist() { pathA := suite.createFile("wA.txt") pathB := suite.createFile("wB.txt") err := conditions.WaitForFilesToExist(pathA, pathB).Wait(context.Background()) suite.Require().NoError(err) suite.Require().NoError(os.Remove(pathB)) errCh := make(chan error) go func() { errCh <- conditions.WaitForFilesToExist(pathA, pathB).Wait(context.Background()) }() time.Sleep(50 * time.Millisecond) select { case <-errCh: suite.Fail("unexpected return") default: } suite.createFile("wB.txt") suite.Require().NoError(<-errCh) suite.Require().NoError(os.Remove(pathA)) suite.Require().NoError(os.Remove(pathB)) ctx, ctxCancel := context.WithCancel(context.Background()) go func() { errCh <- conditions.WaitForFilesToExist(pathA, pathB).Wait(ctx) }() time.Sleep(50 * time.Millisecond) select { case <-errCh: suite.Fail("unexpected return") default: } ctxCancel() suite.Require().EqualError(<-errCh, context.Canceled.Error()) } func TestFilesSuite(t *testing.T) { suite.Run(t, new(FilesSuite)) } ================================================ FILE: pkg/conditions/kubeconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions import ( "context" "errors" "fmt" "io/fs" "os" "time" "k8s.io/client-go/tools/clientcmd" ) type kubeconfig string func (filename kubeconfig) Wait(ctx context.Context) error { ticker := time.NewTicker(time.Second) defer ticker.Stop() for { _, err := os.Stat(string(filename)) if err != nil && !errors.Is(err, fs.ErrNotExist) { return err } _, err = clientcmd.BuildConfigFromFlags("", string(filename)) if err == nil { return nil } // TODO: we can't check for specific error here (looking for file not found for client key/cert): // https://github.com/kubernetes/kubernetes/pull/105080 select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } } } func (filename kubeconfig) String() string { return fmt.Sprintf("kubeconfig %q to be ready", string(filename)) } // WaitForKubeconfigReady is a condition that will wait for the kubeconfig to be ready. func WaitForKubeconfigReady(filename string) Condition { return kubeconfig(filename) } ================================================ FILE: pkg/conditions/none.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions import "context" type condition struct{} func (condition) Wait(ctx context.Context) error { return nil } func (condition) String() string { return "nothing" } // None is a service condition that has no conditions. func None() Condition { return condition{} } ================================================ FILE: pkg/conditions/poll.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions import ( "context" "errors" "fmt" "sync" "time" ) // ErrSkipAssertion is used as a return value from AssertionFunc to indicate that this assertion // (and, by extension, condition, and check) is to be skipped. // It is not returned as an error by any Condition's Wait method // but recorded as description and returned by String method. var ErrSkipAssertion = errors.New("SKIP") // AssertionFunc is called every poll interval until it returns nil. type AssertionFunc func(ctx context.Context) error type pollingCondition struct { lastErrMu sync.Mutex lastErr error lastErrSet bool assertion AssertionFunc description string interval time.Duration } func (p *pollingCondition) String() string { lastErr := "..." p.lastErrMu.Lock() if p.lastErrSet { if p.lastErr != nil { lastErr = p.lastErr.Error() } else { lastErr = OK } } p.lastErrMu.Unlock() return fmt.Sprintf("%s: %s", p.description, lastErr) } func (p *pollingCondition) Wait(ctx context.Context) error { ticker := time.NewTicker(p.interval) defer ticker.Stop() for { err := func() error { runCtx, runCtxCancel := context.WithTimeout(ctx, p.interval) defer runCtxCancel() err := p.assertion(runCtx) p.lastErrMu.Lock() p.lastErr = err p.lastErrSet = true p.lastErrMu.Unlock() return err }() if err == nil || err == ErrSkipAssertion { return nil } select { case <-ctx.Done(): return ctx.Err() case <-ticker.C: } } } // PollingCondition converts AssertionFunc into Condition by calling it every interval until // it completes or the context is canceled. func PollingCondition(description string, assertion AssertionFunc, interval time.Duration) Condition { return &pollingCondition{ assertion: assertion, description: description, interval: interval, } } ================================================ FILE: pkg/conditions/poll_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package conditions_test import ( "context" "errors" "testing" "time" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/conditions" ) func TestPollingCondition(t *testing.T) { t.Parallel() t.Run("OK", func(t *testing.T) { t.Parallel() var calls int cond := conditions.PollingCondition("Test condition", func(ctx context.Context) error { calls++ if calls < 2 { return errors.New("failed") } return nil }, time.Millisecond) err := cond.Wait(t.Context()) assert.NoError(t, err) assert.Equal(t, "Test condition: OK", cond.String()) assert.Equal(t, 2, calls) }) t.Run("Skip", func(t *testing.T) { t.Parallel() var calls int cond := conditions.PollingCondition("Test condition", func(ctx context.Context) error { calls++ if calls < 2 { return errors.New("failed") } return conditions.ErrSkipAssertion }, time.Millisecond) err := cond.Wait(t.Context()) assert.NoError(t, err) assert.Equal(t, "Test condition: SKIP", cond.String()) assert.Equal(t, 2, calls) }) t.Run("Fatal", func(t *testing.T) { t.Parallel() var calls int cond := conditions.PollingCondition("Test condition", func(ctx context.Context) error { calls++ return errors.New("failed") }, 750*time.Millisecond) ctx, cancel := context.WithTimeout(t.Context(), 1400*time.Millisecond) defer cancel() err := cond.Wait(ctx) assert.Equal(t, context.DeadlineExceeded, err) assert.Equal(t, "Test condition: failed", cond.String()) assert.Equal(t, 2, calls) }) } ================================================ FILE: pkg/download/download.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package download provides a download with retries for machine configuration and userdata. package download import ( "bytes" "context" "encoding/base64" "fmt" "io" "maps" "math/rand/v2" "net" "net/http" "net/url" "os" "strconv" "time" "github.com/hashicorp/go-cleanhttp" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/pkg/httpdefaults" ) const b64 = "base64" type downloadOptions struct { Headers map[string]string Format string LowSrcPort bool EndpointFunc func(context.Context) (string, error) ErrorOnNotFound error ErrorOnBadRequest error ErrorOnEmptyResponse error Timeout time.Duration RetryOptions []retry.Option } // Option configures the download options. type Option func(*downloadOptions) func downloadDefaults(endpoint string) *downloadOptions { return &downloadOptions{ EndpointFunc: func(context.Context) (string, error) { return endpoint, nil }, Headers: make(map[string]string), Timeout: 3 * time.Minute, } } // WithFormat specifies the source format. This ultimately will be a yaml // but may be represented in different formats. For example, the config // may be base64 encoded. func WithFormat(format string) Option { return func(d *downloadOptions) { switch format { case b64: d.Format = b64 default: d.Format = "yaml" } } } // WithHeaders specifies any http headers that are needed for downloading // the config. func WithHeaders(headers map[string]string) Option { return func(d *downloadOptions) { if headers == nil { return } if d.Headers == nil { d.Headers = map[string]string{} } maps.Copy(d.Headers, headers) } } // WithLowSrcPort sets low source port to download // the config. func WithLowSrcPort() Option { return func(d *downloadOptions) { d.LowSrcPort = true } } // WithErrorOnNotFound provides specific error to return when response has HTTP 404 error. func WithErrorOnNotFound(e error) Option { return func(d *downloadOptions) { d.ErrorOnNotFound = e } } // WithErrorOnEmptyResponse provides specific error to return when response is empty. func WithErrorOnEmptyResponse(e error) Option { return func(d *downloadOptions) { d.ErrorOnEmptyResponse = e } } // WithErrorOnBadRequest provides specific error to return when response has HTTP 400 error. func WithErrorOnBadRequest(e error) Option { return func(d *downloadOptions) { d.ErrorOnBadRequest = e } } // WithEndpointFunc provides a function that sets the endpoint of the download options. func WithEndpointFunc(endpointFunc func(context.Context) (string, error)) Option { return func(d *downloadOptions) { d.EndpointFunc = endpointFunc } } // WithTimeout sets the timeout for the download. func WithTimeout(timeout time.Duration) Option { return func(d *downloadOptions) { d.Timeout = timeout } } // WithRetryOptions sets the retry options for the download. func WithRetryOptions(opts ...retry.Option) Option { return func(d *downloadOptions) { d.RetryOptions = append(d.RetryOptions, opts...) } } // Download downloads a config. // //nolint:gocyclo func Download(ctx context.Context, endpoint string, opts ...Option) (b []byte, err error) { options := downloadDefaults(endpoint) for _, opt := range opts { opt(options) } err = retry.Exponential( options.Timeout, append([]retry.Option{ retry.WithUnits(time.Second), retry.WithJitter(time.Second), retry.WithErrorLogging(true), }, options.RetryOptions..., )..., ).RetryWithContext(ctx, func(ctx context.Context) error { var attemptEndpoint string attemptEndpoint, err = options.EndpointFunc(ctx) if err != nil { return err } if err = func() error { var u *url.URL u, err = url.Parse(attemptEndpoint) if err != nil { return err } if u.Scheme == "file" { var fileContent []byte fileContent, err = os.ReadFile(u.Path) if err != nil { return err } b = fileContent return nil } var req *http.Request if req, err = http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil); err != nil { return err } for k, v := range options.Headers { req.Header.Set(k, v) } b, err = download(req, options) return err }(); err != nil { return fmt.Errorf("failed to download config from %q: %w", endpoint, err) } return nil }) if err != nil { return nil, err } if options.Format == b64 { var b64 []byte b64, err = base64.StdEncoding.DecodeString(string(b)) if err != nil { return nil, err } b = b64 } return b, nil } //nolint:gocyclo func download(req *http.Request, options *downloadOptions) (data []byte, err error) { transport := httpdefaults.PatchTransport(cleanhttp.DefaultTransport()) transport.RegisterProtocol("tftp", NewTFTPTransport()) if options.LowSrcPort { port := 100 + rand.IntN(512) localTCPAddr, tcperr := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(port)) if tcperr != nil { return nil, retry.ExpectedErrorf("resolving source tcp address: %s", tcperr.Error()) } d := (&net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, DualStack: true, LocalAddr: localTCPAddr, }).DialContext transport.DialContext = d } client := &http.Client{ Transport: transport, } resp, err := client.Do(req) if err != nil { return data, retry.ExpectedError(err) } //nolint:errcheck defer resp.Body.Close() if resp.StatusCode == http.StatusNotFound && options.ErrorOnNotFound != nil { return data, options.ErrorOnNotFound } if resp.StatusCode == http.StatusBadRequest && options.ErrorOnBadRequest != nil { return data, options.ErrorOnBadRequest } // 204 - StatusNoContent is also a successful response, signaling that there is no body if resp.StatusCode == http.StatusNoContent { return data, options.ErrorOnEmptyResponse } if resp.StatusCode != http.StatusOK { // try to read first 32 bytes of the response body // to provide more context in case of error data, _ = io.ReadAll(io.LimitReader(resp.Body, 32)) //nolint:errcheck // as error already happened, we don't care much about this one data = bytes.ToValidUTF8(data, nil) return data, retry.ExpectedErrorf("failed to download config, status code %d, body %q", resp.StatusCode, string(data)) } data, err = io.ReadAll(resp.Body) if err != nil { return data, retry.ExpectedErrorf("read config: %s", err.Error()) } if len(data) == 0 && options.ErrorOnEmptyResponse != nil { return data, options.ErrorOnEmptyResponse } return data, nil } ================================================ FILE: pkg/download/download_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package download_test import ( "context" "errors" "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/siderolabs/go-retry/retry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/download" ) type flipper struct { srv *httptest.Server val int sleep time.Duration } func (f *flipper) EndpointFunc() func(context.Context) (string, error) { return func(context.Context) (string, error) { time.Sleep(f.sleep) f.val++ if f.val%2 == 1 { return f.srv.URL + "/404", nil } return f.srv.URL + "/data", nil } } func TestDownload(t *testing.T) { t.Parallel() srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/empty": w.WriteHeader(http.StatusOK) case "/data": w.WriteHeader(http.StatusOK) w.Write([]byte("data")) //nolint:errcheck case "/base64": w.WriteHeader(http.StatusOK) w.Write([]byte("ZGF0YQ==")) //nolint:errcheck case "/400": w.WriteHeader(http.StatusBadRequest) fmt.Fprintln(w, "bad request") case "/404": w.WriteHeader(http.StatusNotFound) fmt.Fprintln(w, "not found") case "/204": w.WriteHeader(http.StatusNoContent) default: w.WriteHeader(http.StatusInternalServerError) } })) t.Cleanup(srv.Close) flip := flipper{ srv: srv, } sleepingFlip := flipper{ srv: srv, sleep: 100 * time.Millisecond, } for _, test := range []struct { name string path string opts []download.Option expected string expectedError string }{ { name: "empty download", path: "/empty", expected: "", }, { name: "empty download with 204", path: "/204", expected: "", }, { name: "some data", path: "/data", expected: "data", }, { name: "base64", path: "/base64", opts: []download.Option{download.WithFormat("base64")}, expected: "data", }, { name: "empty error", path: "/empty", opts: []download.Option{download.WithErrorOnEmptyResponse(errors.New("empty response"))}, expectedError: "empty response", }, { name: "empty error by 204", path: "/204", opts: []download.Option{download.WithErrorOnEmptyResponse(errors.New("empty response"))}, expectedError: "empty response", }, { name: "not found error", path: "/404", opts: []download.Option{download.WithErrorOnNotFound(errors.New("gone forever"))}, expectedError: "gone forever", }, { name: "bad request error", path: "/400", opts: []download.Option{download.WithErrorOnBadRequest(errors.New("bad req"))}, expectedError: "bad req", }, { name: "failure 404", path: "/404", opts: []download.Option{download.WithTimeout(2 * time.Second)}, expectedError: "failed to download config, status code 404, body \"not found\\n\"", }, { name: "failure 400", path: "/400", opts: []download.Option{download.WithTimeout(2 * time.Second)}, expectedError: "failed to download config, status code 400, body \"bad request\\n\"", }, { name: "retry endpoint change", opts: []download.Option{ download.WithTimeout(2 * time.Second), download.WithEndpointFunc(flip.EndpointFunc()), }, expected: "data", }, { name: "retry with attempt timeout", opts: []download.Option{ download.WithTimeout(2 * time.Second), download.WithEndpointFunc(sleepingFlip.EndpointFunc()), download.WithRetryOptions(retry.WithAttemptTimeout(200 * time.Millisecond)), }, expected: "data", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) defer cancel() b, err := download.Download(ctx, srv.URL+test.path, test.opts...) if test.expectedError != "" { assert.ErrorContains(t, err, test.expectedError) } else { require.NoError(t, err) assert.Equal(t, test.expected, string(b)) } }) } } ================================================ FILE: pkg/download/tftp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package download import ( "bytes" "fmt" "io" "net/http" "github.com/pin/tftp/v3" ) // NewTFTPTransport returns an http.RoundTripper capable of handling the TFTP // protocol. func NewTFTPTransport() http.RoundTripper { return tftpRoundTripper{} } type tftpRoundTripper struct{} func (t tftpRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { addr := req.URL.Host if req.URL.Port() == "" { addr += ":69" } c, err := tftp.NewClient(addr) if err != nil { return nil, err } w, err := c.Receive(req.URL.Path, "octet") if err != nil { return nil, err } buf := &bytes.Buffer{} written, err := w.WriteTo(buf) if err != nil { return nil, err } if expected, ok := w.(tftp.IncomingTransfer).Size(); ok { if written != expected { return nil, fmt.Errorf("expected %d bytes, got %d", expected, written) } } return &http.Response{ Status: "200 OK", StatusCode: http.StatusOK, Proto: "TFTP/1.0", ProtoMajor: 1, ProtoMinor: 0, Body: io.NopCloser(buf), ContentLength: -1, Request: req, }, nil } ================================================ FILE: pkg/filetree/chown.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package filetree import ( "io/fs" "os" "path/filepath" "syscall" ) // ChownRecursive changes file ownership recursively from the specified root. func ChownRecursive(root string, uid, gid uint32) error { return filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } if info.Sys().(*syscall.Stat_t).Uid != uid || info.Sys().(*syscall.Stat_t).Gid != gid { return os.Chown(path, int(uid), int(gid)) } return nil }) } ================================================ FILE: pkg/filetree/filetree.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package filetree provides file tree operations. package filetree ================================================ FILE: pkg/flags/flags.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package flags provides custom pflag.Value implementations for common use cases. package flags import ( "fmt" "slices" "strings" "github.com/blang/semver/v4" "github.com/siderolabs/gen/maps" "github.com/siderolabs/gen/xslices" "github.com/spf13/pflag" ) type choiceValue struct { value string validate func(string) error } // Set implements pflag.Value interface. func (v *choiceValue) Set(s string) error { err := v.validate(s) if err != nil { return err } v.value = s return nil } // Type implements pflag.Value interface. func (v *choiceValue) Type() string { return "string" } // String implements pflag.Value interface. func (v *choiceValue) String() string { return v.value } // StringChoice returns a [choiceValue] that validates the value against a set // of choices. Only the last value will be used if multiple values are set. func StringChoice(defaultValue string, otherChoices ...string) pflag.Value { return &choiceValue{ value: defaultValue, validate: func(s string) error { choices := slices.Concat(otherChoices, []string{defaultValue}) if slices.Contains(choices, s) { return nil } return fmt.Errorf("must be one of %v", choices) }, } } type semverValue struct { value semver.Version validators []SemverValidateFunc } // SemverValidateFunc allows setting restrictions on the version. type SemverValidateFunc func(v semver.Version) error // Set implements pflag.Value interface. func (v *semverValue) Set(s string) error { vers, err := semver.ParseTolerant(s) if err != nil { return err } for _, validator := range v.validators { if err := validator(vers); err != nil { return err } } v.value = vers return nil } // Type implements pflag.Value interface. func (v *semverValue) Type() string { return "semver" } // String implements pflag.Value interface. func (v *semverValue) String() string { return "v" + v.value.String() } // Semver returns a pflag.Value that parses and stores a semantic version. // // Parsing is performed using semver.ParseTolerant. After parsing, any provided // SemverValidateFunc validators are applied in order and may reject the version. // // The returned value is initialized with defaultValue, which is used until Set // is called successfully. func Semver(defaultValue string, validators ...SemverValidateFunc) pflag.Value { v, err := semver.ParseTolerant(defaultValue) if err != nil { panic(err) } return &semverValue{ value: v, validators: validators, } } type comparableStringer interface { ~int32 comparable fmt.Stringer } // PflagExtended extends pflag.Value with additional methods for retrieving the value as type T and getting valid string values. type PflagExtended[T any] interface { pflag.Value Value() T Options() []string } type protoEnumValue[T comparableStringer] struct { value T values map[string]int32 names map[int32]string } // Set implements pflag.Value interface. func (v *protoEnumValue[T]) Set(s string) error { value, ok := v.values[strings.ToUpper(s)] if !ok { return fmt.Errorf("must be one of %v", v.Options()) } v.value = T(value) return nil } // Type implements pflag.Value interface. func (v *protoEnumValue[T]) Type() string { return "string" } // String implements pflag.Value interface. func (v *protoEnumValue[T]) String() string { return strings.ToLower(v.value.String()) } // Value returns the enum value as the type T. func (v *protoEnumValue[T]) Value() T { return v.value } // Options returns the valid string values for the enum. func (v *protoEnumValue[T]) Options() []string { opts := xslices.Map(maps.Keys(v.values), strings.ToLower) slices.Sort(opts) return opts } // ProtoEnum returns a [protoEnumValue] that validates the value is correct. // Only the last value will be used if multiple values are set. func ProtoEnum[T comparableStringer](defaultValue T, values map[string]int32, names map[int32]string) PflagExtended[T] { return &protoEnumValue[T]{ value: defaultValue, values: values, names: names, } } ================================================ FILE: pkg/follow/follow.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package follow provides Reader which follows file updates and turns it into a stream. package follow import ( "context" "errors" "fmt" "io" "os" "path/filepath" "sync" "github.com/fsnotify/fsnotify" ) // Reader implements io.ReadCloser over regular file following file contents. // // This makes file similar to the stream in semantics. type Reader struct { source *os.File //nolint:containedctx ctx context.Context ctxCancel context.CancelFunc notifyCh chan error mu sync.Mutex closed bool notifyStarted bool } // NewReader wraps io.File as follow.Reader. func NewReader(readCtx context.Context, source *os.File) *Reader { ctx, ctxCancel := context.WithCancel(readCtx) return &Reader{ source: source, notifyCh: make(chan error, 1), ctx: ctx, ctxCancel: ctxCancel, } } // Read implements io.Reader interface. func (r *Reader) Read(p []byte) (n int, err error) { r.mu.Lock() if r.closed { err = io.ErrClosedPipe r.mu.Unlock() return n, err } if !r.notifyStarted { r.startNotify() } r.mu.Unlock() select { case <-r.ctx.Done(): err = io.EOF return n, err default: } for { n, err = r.source.Read(p) if err == nil || err != io.EOF { return n, err } select { case <-r.ctx.Done(): err = io.EOF return n, err case err = <-r.notifyCh: if err != nil { return n, err } } } } // Close implements io.Closer interface. func (r *Reader) Close() error { r.mu.Lock() if r.closed { r.mu.Unlock() return nil } r.closed = true r.mu.Unlock() r.ctxCancel() return r.source.Close() } func (r *Reader) startNotify() { r.notifyStarted = true go r.notify() } //nolint:gocyclo func (r *Reader) notify() { watcher, err := fsnotify.NewWatcher() if err != nil { select { case r.notifyCh <- fmt.Errorf("failed to watch: %w", err): case <-r.ctx.Done(): } return } //nolint:errcheck defer watcher.Close() filename := r.source.Name() if err = watcher.Add(filepath.Dir(filename)); err != nil { select { case r.notifyCh <- fmt.Errorf("failed to add dir watch: %w", err): case <-r.ctx.Done(): } return } for { select { case <-r.ctx.Done(): return case event := <-watcher.Events: if event.Name != filename { // ignore events for other files continue } switch event.Op { //nolint:exhaustive case fsnotify.Write: // non-blocking send, we need to keep processing fsnotify events // at least signal message is in r.notifyCh which will allow Read to wake up select { case r.notifyCh <- nil: default: } case fsnotify.Remove: select { case r.notifyCh <- errors.New("file was removed while watching"): case <-r.ctx.Done(): } return } case err := <-watcher.Errors: select { case r.notifyCh <- fmt.Errorf("failed to watch: %w", err): case <-r.ctx.Done(): } return } } } ================================================ FILE: pkg/follow/follow_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package follow_test import ( "bytes" "context" "fmt" "io" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/follow" ) type FollowSuite struct { suite.Suite tmpDir string no int reader, writer *os.File r *follow.Reader } func (suite *FollowSuite) SetupSuite() { suite.tmpDir = suite.T().TempDir() } func (suite *FollowSuite) SetupTest() { suite.no++ var err error suite.writer, err = os.Create(filepath.Join(suite.tmpDir, fmt.Sprintf("%d.log", suite.no))) suite.Require().NoError(err) suite.reader, err = os.Open(suite.writer.Name()) suite.Require().NoError(err) } func (suite *FollowSuite) TearDownTest() { suite.Require().NoError(suite.writer.Close()) suite.reader.Close() //nolint:errcheck } //nolint:unparam func (suite *FollowSuite) readAll(ctx context.Context, expectedError string, sizeHint int, timeout time.Duration) <-chan []byte { combinedCh := make(chan []byte) ctx, ctxCancel := context.WithTimeout(ctx, timeout) suite.r = follow.NewReader(ctx, suite.reader) go func() { defer ctxCancel() defer suite.r.Close() //nolint:errcheck contents := make([]byte, sizeHint) n, err := io.ReadFull(suite.r, contents) if expectedError == "" { suite.Assert().NoError(err) } else { suite.Assert().EqualError(err, expectedError) } combinedCh <- contents[:n] }() return combinedCh } func (suite *FollowSuite) smallReadAll(ctx context.Context, sizeHint int, timeout time.Duration) <-chan []byte { combinedCh := make(chan []byte) ctx, ctxCancel := context.WithTimeout(ctx, timeout) suite.r = follow.NewReader(ctx, suite.reader) go func() { defer ctxCancel() defer suite.r.Close() //nolint:errcheck buf := make([]byte, 1) var output bytes.Buffer _, err := io.CopyBuffer(&output, io.LimitReader(suite.r, int64(sizeHint)), buf) suite.Assert().NoError(err) combinedCh <- output.Bytes() }() return combinedCh } func (suite *FollowSuite) TestStreaming() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() combinedCh := suite.readAll(ctx, "", 15, time.Second) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FollowSuite) TestStreamingClose() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() combinedCh := suite.readAll(ctx, "", 15, time.Second) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") time.Sleep(150 * time.Millisecond) suite.Require().NoError(suite.r.Close()) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FollowSuite) TestStreamingWithSomeHead() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") combinedCh := suite.readAll(ctx, "", 15, time.Second) //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("mno") suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FollowSuite) TestStreamingSmallBuffer() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() combinedCh := suite.smallReadAll(ctx, 15, time.Second) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") time.Sleep(50 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") // create extra file to try to confuse watch _, err := os.Create(filepath.Join(suite.tmpDir, "x.log")) suite.Require().NoError(err) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FollowSuite) TestDeleted() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() // pass sizeHint as 15+1 to make code read beyond the end and encounter file removed combinedCh := suite.readAll(ctx, "file was removed while watching", 16, time.Second) time.Sleep(150 * time.Millisecond) //nolint:errcheck suite.writer.WriteString("abc") //nolint:errcheck suite.writer.WriteString("def") //nolint:errcheck suite.writer.WriteString("ghi") //nolint:errcheck suite.writer.WriteString("jkl") //nolint:errcheck suite.writer.WriteString("mno") time.Sleep(150 * time.Millisecond) // chunker should terminate when file is removed suite.Require().NoError(os.Remove(suite.writer.Name())) suite.Require().Equal([]byte("abcdefghijklmno"), <-combinedCh) } func (suite *FollowSuite) TestReadWrite() { ctx, ctxCancel := context.WithCancel(context.Background()) defer ctxCancel() r := follow.NewReader(ctx, suite.reader) buf := make([]byte, 256) //nolint:errcheck suite.writer.WriteString("abc") n, err := r.Read(buf) suite.Require().NoError(err) suite.Require().Equal(3, n) suite.Require().Equal([]byte("abc"), buf[:n]) //nolint:errcheck suite.writer.WriteString("def") n, err = r.Read(buf) suite.Require().NoError(err) suite.Require().Equal(3, n) suite.Require().Equal([]byte("def"), buf[:n]) ch := make(chan []byte) go func() { n, err = r.Read(buf) suite.Require().NoError(err) suite.Require().Equal(3, n) ch <- buf[:n] }() // Read should block on no new data select { case <-ch: suite.Require().Fail("should block on read") case <-time.After(50 * time.Millisecond): } //nolint:errcheck suite.writer.WriteString("ghi") suite.Require().Equal([]byte("ghi"), <-ch) } func TestFollowSuite(t *testing.T) { suite.Run(t, new(FollowSuite)) } ================================================ FILE: pkg/grpc/factory/factory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package factory import ( "context" "errors" "fmt" "io" "log" "net" "os" "path/filepath" "runtime/debug" "strconv" grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" _ "github.com/siderolabs/proto-codec/codec" // register codec v2 "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/reflection" "google.golang.org/grpc/status" grpclog "github.com/siderolabs/talos/pkg/grpc/middleware/log" ) // Registrator describes the set of methods required in order for a concrete // type to be used with the Listen function. type Registrator interface { Register(*grpc.Server) } // Options is the functional options struct. type Options struct { Address string Port int SocketPath string Network string ServerOptions []grpc.ServerOption UnaryInterceptors []grpc.UnaryServerInterceptor StreamInterceptors []grpc.StreamServerInterceptor Reflection bool logPrefix string logDestination io.Writer } // Option is the functional option func. type Option func(*Options) // Address sets the listen address of the server. func Address(a string) Option { return func(args *Options) { args.Address = a } } // Port sets the listen port of the server. func Port(o int) Option { return func(args *Options) { args.Port = o } } // SocketPath sets the listen unix file socket path of the server. func SocketPath(o string) Option { return func(args *Options) { args.SocketPath = o } } // Network sets the network type of the listener. func Network(o string) Option { return func(args *Options) { args.Network = o } } // ServerOptions appends to the gRPC server options of the server. func ServerOptions(o ...grpc.ServerOption) Option { return func(args *Options) { args.ServerOptions = append(args.ServerOptions, o...) } } // WithUnaryInterceptor appends to the list of gRPC server unary interceptors. func WithUnaryInterceptor(i grpc.UnaryServerInterceptor) Option { return func(args *Options) { args.UnaryInterceptors = append(args.UnaryInterceptors, i) } } // WithStreamInterceptor appends to the list of gRPC server stream interceptors. func WithStreamInterceptor(i grpc.StreamServerInterceptor) Option { return func(args *Options) { args.StreamInterceptors = append(args.StreamInterceptors, i) } } // WithLog sets up request logging to specified destination. func WithLog(prefix string, w io.Writer) Option { return func(args *Options) { args.logPrefix = prefix args.logDestination = w } } // WithDefaultLog sets up request logging to default destination. func WithDefaultLog() Option { return func(args *Options) { args.logDestination = log.Writer() } } // WithReflection enables gRPC reflection APIs: https://github.com/grpc/grpc/blob/master/doc/server-reflection.md func WithReflection() Option { return func(args *Options) { args.Reflection = true } } func recoveryHandler(logger *log.Logger) grpc_recovery.RecoveryHandlerFunc { return func(p any) error { if logger != nil { logger.Printf("panic: %v\n%s", p, string(debug.Stack())) } return status.Errorf(codes.Internal, "%v", p) } } // NewDefaultOptions initializes the Options struct with default values. func NewDefaultOptions(setters ...Option) *Options { opts := &Options{ Network: "tcp", SocketPath: "/run/factory/factory.sock", } for _, setter := range setters { setter(opts) } var logger *log.Logger if opts.logDestination != nil { logger = log.New(opts.logDestination, opts.logPrefix, log.Flags()) } // Recovery is installed as the first middleware in the chain to handle panics (via defer and recover()) in all subsequent middlewares. recoveryOpt := grpc_recovery.WithRecoveryHandler(recoveryHandler(logger)) opts.UnaryInterceptors = append( []grpc.UnaryServerInterceptor{grpc_recovery.UnaryServerInterceptor(recoveryOpt)}, opts.UnaryInterceptors..., ) opts.StreamInterceptors = append( []grpc.StreamServerInterceptor{grpc_recovery.StreamServerInterceptor(recoveryOpt)}, opts.StreamInterceptors..., ) if logger != nil { // Logging is installed as the first middleware (even before recovery middleware) in the chain // so that request in the form it was received and status sent on the wire is logged (error/success). // It also tracks the whole duration of the request, including other middleware overhead. logMiddleware := grpclog.NewMiddleware(logger) opts.UnaryInterceptors = append( []grpc.UnaryServerInterceptor{logMiddleware.UnaryInterceptor()}, opts.UnaryInterceptors..., ) opts.StreamInterceptors = append( []grpc.StreamServerInterceptor{logMiddleware.StreamInterceptor()}, opts.StreamInterceptors..., ) } opts.ServerOptions = append( opts.ServerOptions, grpc.InitialWindowSize(65535*32), grpc.InitialConnWindowSize(65535*16), grpc.ChainUnaryInterceptor(opts.UnaryInterceptors...), grpc.ChainStreamInterceptor(opts.StreamInterceptors...), grpc.SharedWriteBuffer(true), ) return opts } // NewServer builds grpc server and binds it to the Registrator. func NewServer(r Registrator, setters ...Option) *grpc.Server { opts := NewDefaultOptions(setters...) server := grpc.NewServer(opts.ServerOptions...) r.Register(server) if opts.Reflection { reflection.Register(server) } return server } // NewListener builds listener for grpc server. func NewListener(ctx context.Context, setters ...Option) (net.Listener, error) { opts := NewDefaultOptions(setters...) if opts.Network == "tcp" && opts.Port == 0 { return nil, errors.New("a port is required for TCP listener") } var address string switch opts.Network { case "unix": address = opts.SocketPath // Unlink the address or we will get the error: // bind: address already in use. if _, err := os.Stat(address); err == nil { if err := os.Remove(address); err != nil { return nil, err } } // Make any dirs on the path to the listening socket. if err := os.MkdirAll(filepath.Dir(address), 0o700); err != nil { return nil, fmt.Errorf("error creating containing directory for the file socket; %w", err) } case "tcp": address = net.JoinHostPort(opts.Address, strconv.Itoa(opts.Port)) default: return nil, fmt.Errorf("unknown network: %s", opts.Network) } return (&net.ListenConfig{}).Listen(ctx, opts.Network, address) } // ListenAndServe configures TLS for mutual authentication by loading the CA into a // CertPool and configuring the server's policy for TLS Client Authentication. // Once TLS is configured, the gRPC options are built to make use of the TLS // configuration and the receiver (Server) is registered to the gRPC server. // Finally the gRPC server is started. func ListenAndServe(ctx context.Context, r Registrator, setters ...Option) (err error) { server := NewServer(r, setters...) listener, err := NewListener(ctx, setters...) if err != nil { return err } return server.Serve(listener) } // ServerGracefulStop the server with a timeout. // // Core gRPC doesn't support timeouts. func ServerGracefulStop(server *grpc.Server, shutdownCtx context.Context) { //nolint:revive stopped := make(chan struct{}) go func() { defer close(stopped) server.GracefulStop() }() select { case <-shutdownCtx.Done(): server.Stop() case <-stopped: server.Stop() } } ================================================ FILE: pkg/grpc/factory/factory_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package factory_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/grpc/gen/gen_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/grpc/gen/local.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "github.com/siderolabs/crypto/x509" ) // LocalGenerator represents the OS identity generator. type LocalGenerator struct { caKey []byte caCrt []byte } // NewLocalGenerator initializes a LocalGenerator. func NewLocalGenerator(caKey, caCrt []byte) (g *LocalGenerator, err error) { g = &LocalGenerator{caKey, caCrt} return g, nil } // Identity creates an identity certificate using a local root CA. func (g *LocalGenerator) Identity(csr *x509.CertificateSigningRequest) (ca, crt []byte, err error) { var c *x509.Certificate c, err = x509.NewCertificateFromCSRBytes(g.caCrt, g.caKey, csr.X509CertificateRequestPEM) if err != nil { return ca, crt, err } crt = c.X509CertificatePEM return g.caCrt, crt, nil } ================================================ FILE: pkg/grpc/gen/remote.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package gen import ( "context" "errors" "fmt" "runtime/pprof" "strings" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-retry/retry" "google.golang.org/grpc" "github.com/siderolabs/talos/pkg/grpc/middleware/auth/basic" securityapi "github.com/siderolabs/talos/pkg/machinery/api/security" "github.com/siderolabs/talos/pkg/machinery/client/resolver" "github.com/siderolabs/talos/pkg/machinery/constants" ) var remoteGeneratorPprof = pprof.NewProfile("pkg/grpc/gen.RemoteGenerator") // RemoteGenerator represents the OS identity generator. type RemoteGenerator struct { conn *grpc.ClientConn client securityapi.SecurityServiceClient } // NewRemoteGenerator initializes a RemoteGenerator with a preconfigured grpc.ClientConn. func NewRemoteGenerator(token string, endpoints []string, acceptedCAs []*x509.PEMEncodedCertificate) (g *RemoteGenerator, err error) { if len(endpoints) == 0 { return nil, errors.New("at least one root of trust endpoint is required") } endpoints = resolver.EnsureEndpointsHavePorts(endpoints, constants.TrustdPort) g = &RemoteGenerator{} remoteGeneratorPprof.Add(g, 1) conn, err := basic.NewConnection(fmt.Sprintf("%s:///%s", resolver.RoundRobinResolverScheme, strings.Join(endpoints, ",")), basic.NewTokenCredentials(token), acceptedCAs) if err != nil { return nil, err } g.conn = conn g.client = securityapi.NewSecurityServiceClient(g.conn) return g, nil } // Identity creates an identity certificate via the security API. func (g *RemoteGenerator) Identity(csr *x509.CertificateSigningRequest) (ca, crt []byte, err error) { return g.IdentityContext(context.Background(), csr) } // IdentityContext creates an identity certificate via the security API. func (g *RemoteGenerator) IdentityContext(ctx context.Context, csr *x509.CertificateSigningRequest) (ca, crt []byte, err error) { req := &securityapi.CertificateRequest{ Csr: csr.X509CertificateRequestPEM, } ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() if err = retry.Exponential(time.Minute, retry.WithAttemptTimeout(10*time.Second), retry.WithUnits(time.Second), retry.WithJitter(100*time.Millisecond), ).RetryWithContext(ctx, func(ctx context.Context) error { var resp *securityapi.CertificateResponse resp, err = g.client.Certificate(ctx, req) if err != nil { return retry.ExpectedError(err) } ca = resp.Ca crt = resp.Crt return nil }); err != nil { return nil, nil, err } return ca, crt, nil } // Close closes the gRPC client connection. func (g *RemoteGenerator) Close() error { remoteGeneratorPprof.Remove(g) return g.conn.Close() } ================================================ FILE: pkg/grpc/middleware/auth/basic/basic.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package basic import ( "bytes" "crypto/tls" stdx509 "crypto/x509" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "github.com/siderolabs/talos/pkg/httpdefaults" "github.com/siderolabs/talos/pkg/machinery/client/dialer" ) // Credentials describes an authorization method. type Credentials interface { credentials.PerRPCCredentials UnaryInterceptor() grpc.UnaryServerInterceptor } // NewConnection initializes a grpc.ClientConn configured for basic // authentication. func NewConnection(address string, creds credentials.PerRPCCredentials, acceptedCAs []*x509.PEMEncodedCertificate) (conn *grpc.ClientConn, err error) { tlsConfig := &tls.Config{} tlsConfig.RootCAs = stdx509.NewCertPool() tlsConfig.RootCAs.AppendCertsFromPEM(bytes.Join( xslices.Map( acceptedCAs, func(cert *x509.PEMEncodedCertificate) []byte { return cert.Crt }, ), nil, )) grpcOpts := []grpc.DialOption{ grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), grpc.WithPerRPCCredentials(creds), grpc.WithSharedWriteBuffer(true), grpc.WithContextDialer(dialer.DynamicProxyDialerWithTLSConfig(httpdefaults.RootCAsTLSConfig)), } conn, err = grpc.NewClient(address, grpcOpts...) if err != nil { return conn, err } return conn, nil } ================================================ FILE: pkg/grpc/middleware/auth/basic/token.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package basic import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" ) // TokenGetterFunc is the function to dynamically retrieve the token. type TokenGetterFunc func(context.Context) (string, error) // TokenCredentials implements credentials.PerRPCCredentials. It uses a basic // token lookup to authenticate users. type TokenCredentials struct { tokenGetter TokenGetterFunc } // NewTokenCredentials initializes ClientCredentials with the token. func NewTokenCredentials(token string) (creds Credentials) { creds = &TokenCredentials{ tokenGetter: func(context.Context) (string, error) { return token, nil }, } return creds } // NewTokenCredentialsDynamic initializes ClientCredentials with the dynamic token token. func NewTokenCredentialsDynamic(f TokenGetterFunc) (creds Credentials) { creds = &TokenCredentials{ tokenGetter: f, } return creds } // GetRequestMetadata sets the value for the "token" key. func (b *TokenCredentials) GetRequestMetadata(ctx context.Context, s ...string) (map[string]string, error) { token, err := b.tokenGetter(ctx) if err != nil { return nil, err } return map[string]string{ "token": token, }, nil } // RequireTransportSecurity is set to true in order to encrypt the // communication. func (b *TokenCredentials) RequireTransportSecurity() bool { return true } func (b *TokenCredentials) authenticate(ctx context.Context) error { token, err := b.tokenGetter(ctx) if err != nil { return err } if md, ok := metadata.FromIncomingContext(ctx); ok { if len(md["token"]) > 0 && md["token"][0] == token { return nil } } return fmt.Errorf("%s", codes.Unauthenticated.String()) } // UnaryInterceptor sets the UnaryServerInterceptor for the server and enforces // basic authentication. func (b *TokenCredentials) UnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { if err := b.authenticate(ctx); err != nil { return nil, err } return handler(ctx, req) } } ================================================ FILE: pkg/grpc/middleware/auth/basic/username_and_password.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package basic import ( "context" "fmt" "log" "time" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/metadata" ) // UsernameAndPasswordCredentials implements credentials.PerRPCCredentials. It uses a basic // username and password lookup to authenticate users. type UsernameAndPasswordCredentials struct { Username string Password string } // NewUsernameAndPasswordCredentials initializes username and password // Credentials. func NewUsernameAndPasswordCredentials(username, password string) (creds Credentials) { creds = &UsernameAndPasswordCredentials{ Username: username, Password: password, } return creds } // GetRequestMetadata sets the value for the username and password. func (b *UsernameAndPasswordCredentials) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { return map[string]string{ "username": b.Username, "password": b.Password, }, nil } // RequireTransportSecurity is set to true in order to encrypt the // communication. func (b *UsernameAndPasswordCredentials) RequireTransportSecurity() bool { return true } func (b *UsernameAndPasswordCredentials) authorize(ctx context.Context) error { if md, ok := metadata.FromIncomingContext(ctx); ok { if len(md["username"]) > 0 && md["username"][0] == b.Username && len(md["password"]) > 0 && md["password"][0] == b.Password { return nil } return fmt.Errorf("%s", codes.Unauthenticated.String()) } return nil } // UnaryInterceptor sets the UnaryServerInterceptor for the server and enforces // basic authentication. func (b *UsernameAndPasswordCredentials) UnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { start := time.Now() if err := b.authorize(ctx); err != nil { return nil, err } h, err := handler(ctx, req) log.Printf("request - Method:%s\tDuration:%s\tError:%v\n", info.FullMethod, time.Since(start), err, ) return h, err } } ================================================ FILE: pkg/grpc/middleware/auth/basic/username_and_password_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package basic_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/grpc/middleware/authz/authorizer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package authz import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/role" ) // ErrNotAuthorized should be returned to the client when they are not authorized. var ErrNotAuthorized = status.Error(codes.PermissionDenied, "not authorized") // Authorizer checks that the user is authorized (has a valid role) to call intercepted gRPC method. // User roles should be set the Injector interceptor. type Authorizer struct { // Maps full gRPC method names to roles. The user should have at least one of them. Rules map[string]role.Set // Defines roles for gRPC methods not present in Rules. FallbackRoles role.Set // Logger. Logger func(format string, v ...any) } func (a *Authorizer) logf(format string, v ...any) { if a.Logger != nil { a.Logger(format, v...) } } // authorize returns error if the user is not authorized (doesn't have a valid role) to call the given gRPC method. // User roles should be previously set the Injector interceptor. func (a *Authorizer) authorize(ctx context.Context, method string) error { allowedRoles, found := a.Rules[method] if !found { a.logf("no explicit rule found for %q, falling back to %v", method, a.FallbackRoles.Strings()) allowedRoles = a.FallbackRoles } clientRoles := GetRoles(ctx) if allowedRoles.IncludesAny(clientRoles) { a.logf("authorized (%v includes %v)", allowedRoles.Strings(), clientRoles.Strings()) return nil } a.logf("not authorized (%v doesn't include %v)", allowedRoles.Strings(), clientRoles.Strings()) return ErrNotAuthorized } // UnaryInterceptor returns grpc UnaryServerInterceptor. func (a *Authorizer) UnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { if err := a.authorize(ctx, info.FullMethod); err != nil { return nil, err } return handler(ctx, req) } } // StreamInterceptor returns grpc StreamServerInterceptor. func (a *Authorizer) StreamInterceptor() grpc.StreamServerInterceptor { return func(srv any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { if err := a.authorize(stream.Context(), info.FullMethod); err != nil { return err } return handler(srv, stream) } } ================================================ FILE: pkg/grpc/middleware/authz/authorizer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package authz_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/grpc/middleware/authz/context.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package authz import ( "context" "github.com/siderolabs/talos/pkg/machinery/role" ) // ctxKey is used to store parsed roles in the context. // Should be used only in this file. type ctxKey struct{} // GetRoles returns roles stored in the context by the Injector interceptor. // May be used for additional checks in the API method handler. func GetRoles(ctx context.Context) role.Set { set, ok := getFromContext(ctx) if !ok { panic("no roles in the context") } return set } // HasRole returns true if the context includes the given role. func HasRole(ctx context.Context, r role.Role) bool { return GetRoles(ctx).Includes(r) } // getFromContext returns roles stored in the context. func getFromContext(ctx context.Context) (role.Set, bool) { set, ok := ctx.Value(ctxKey{}).(role.Set) return set, ok } // ContextWithRoles returns derived context with roles set. func ContextWithRoles(ctx context.Context, roles role.Set) context.Context { // sanity check if ctx.Value(ctxKey{}) != nil { panic("roles already stored in the context") } return context.WithValue(ctx, ctxKey{}, roles) } ================================================ FILE: pkg/grpc/middleware/authz/injector.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package authz import ( "context" "fmt" "net" "net/netip" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware/v2" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/role" ) // InjectorMode specifies how roles are extracted. type InjectorMode int const ( // Disabled is used when RBAC is disabled in the machine configuration. All roles are assumed. Disabled InjectorMode = iota // ReadOnly is used to inject only the Reader role. ReadOnly // ReadOnlyWithAdminOnSiderolink is used to inject the Admin role if the peer is a SideroLink peer. // Otherwise, the Reader role is injected. ReadOnlyWithAdminOnSiderolink // MetadataOnly is used internally. Checks only metadata. MetadataOnly // Enabled is used when RBAC is enabled in the machine configuration. Roles are extracted normally. Enabled ) var ( adminRoleSet = role.MakeSet(role.Admin) readerRoleSet = role.MakeSet(role.Reader) ) // SideroLinkPeerCheckFunc checks if the peer is a SideroLink peer. type SideroLinkPeerCheckFunc func(ctx context.Context) (netip.Addr, bool) // Injector sets roles to the context. type Injector struct { // Mode. Mode InjectorMode // SideroLinkPeerCheckFunc checks if the peer is a SideroLink peer. // When not specified, it defaults to isSideroLinkPeer. SideroLinkPeerCheckFunc SideroLinkPeerCheckFunc // Logger. Logger func(format string, v ...any) } func (i *Injector) logf(format string, v ...any) { if i.Logger != nil { i.Logger(format, v...) } } // extractRoles returns roles extracted from the user's certificate (in case of the first apid instance), // or from gRPC metadata (in case of subsequent apid instances, machined, or user with impersonator role). // //nolint:gocyclo func (i *Injector) extractRoles(ctx context.Context) role.Set { // sanity check if _, ok := getFromContext(ctx); ok { panic("roles should not be present in the context at this point") } switch i.Mode { case Disabled: i.logf("RBAC is disabled, injecting all roles") return role.All case ReadOnly: return readerRoleSet case ReadOnlyWithAdminOnSiderolink: check := i.SideroLinkPeerCheckFunc if check == nil { check = isSideroLinkPeer } if siderolinkPeerAddr, siderolinkPeer := check(ctx); siderolinkPeer { i.logf("inject admin role for SideroLink peer %q", siderolinkPeerAddr) return adminRoleSet } return readerRoleSet case MetadataOnly: roles, _ := getFromMetadata(ctx, i.logf) return roles case Enabled: p, ok := peer.FromContext(ctx) if !ok { panic("can't get peer information") } tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo) if !ok { panic(fmt.Sprintf("expected credentials.TLSInfo, got %T", p.AuthInfo)) } if len(tlsInfo.State.PeerCertificates) == 0 { panic("expected at least one certificate") } // PeerCertificates[0] is the leaf certificate the connection was verified against, so this // is the client cert. Other certificates in the chain might be CAs or intermediates. strings := tlsInfo.State.PeerCertificates[0].Subject.Organization // TODO validate cert.KeyUsage, cert.ExtKeyUsage, cert.Issuer.Organization, other fields there? roles, unknownRoles := role.Parse(strings) i.logf("parsed peer's certificate orgs %v as %v (unknownRoles = %v)", strings, roles.Strings(), unknownRoles) // trust gRPC metadata from clients with impersonator role if present // (including requests proxied from other apid instances) if roles.Includes(role.Impersonator) { metadataRoles, ok := getFromMetadata(ctx, i.logf) if ok { return metadataRoles } // that's a real user with impersonator role then i.logf("no roles in metadadata, returning parsed roles") } return roles } panic("unreachable") } // UnaryInterceptor returns grpc UnaryServerInterceptor. func (i *Injector) UnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { ctx = ContextWithRoles(ctx, i.extractRoles(ctx)) return handler(ctx, req) } } // StreamInterceptor returns grpc StreamServerInterceptor. func (i *Injector) StreamInterceptor() grpc.StreamServerInterceptor { return func(srv any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { ctx := stream.Context() ctx = ContextWithRoles(ctx, i.extractRoles(ctx)) wrapped := grpc_middleware.WrapServerStream(stream) wrapped.WrappedContext = ctx return handler(srv, wrapped) } } func isSideroLinkPeer(ctx context.Context) (netip.Addr, bool) { addr, ok := peerAddress(ctx) if !ok { return netip.Addr{}, false } return addr, network.IsULA(addr, network.ULASideroLink) } func peerAddress(ctx context.Context) (netip.Addr, bool) { remotePeer, ok := peer.FromContext(ctx) if !ok { return netip.Addr{}, false } ip, _, err := net.SplitHostPort(remotePeer.Addr.String()) if err != nil { return netip.Addr{}, false } addr, err := netip.ParseAddr(ip) if err != nil { return netip.Addr{}, false } return addr, true } ================================================ FILE: pkg/grpc/middleware/authz/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package authz import ( "context" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) // mdKey is used to store roles in gRPC metadata. // Should be used only in this file. const mdKey = constants.APIAuthzRoleMetadataKey // SetMetadata sets given roles in gRPC metadata. func SetMetadata(md metadata.MD, roles role.Set) { md.Set(mdKey, roles.Strings()...) } // getFromMetadata returns roles extracted from gRPC metadata. func getFromMetadata(ctx context.Context, logf func(format string, v ...any)) (role.Set, bool) { md, ok := metadata.FromIncomingContext(ctx) if !ok { panic("no request metadata") } strings := md.Get(mdKey) if len(strings) == 0 { if logf != nil { logf("no roles in metadata") } return role.Zero, false } roles, unknownRoles := role.Parse(strings) if logf != nil { logf("parsed metadata %v as %v (unknownRoles = %v)", strings, roles.Strings(), unknownRoles) } return roles, true } ================================================ FILE: pkg/grpc/middleware/log/log.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package log provides simple grpc logging middleware package log import ( "context" "slices" "strings" "time" "github.com/siderolabs/gen/maps" "google.golang.org/grpc" "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" ) // Middleware provides grpc logging middleware. type Middleware struct { logger Logger } // Logger is the interface that the Middleware expects for logging. type Logger interface { Printf(format string, v ...any) } // NewMiddleware creates new logging middleware. func NewMiddleware(logger Logger) *Middleware { return &Middleware{ logger: logger, } } var sensitiveFields = map[string]struct{}{ "token": {}, } // ExtractMetadata formats metadata from incoming grpc context as string for the log. func ExtractMetadata(ctx context.Context) string { md, _ := metadata.FromIncomingContext(ctx) keys := maps.Keys(md) slices.Sort(keys) pairs := make([]string, 0, len(keys)) for _, key := range keys { value := strings.Join(md[key], ",") if _, sensitive := sensitiveFields[key]; sensitive { value = "" } pairs = append(pairs, key+"="+value) } return strings.Join(pairs, ";") } // UnaryInterceptor returns grpc UnaryServerInterceptor. func (m *Middleware) UnaryInterceptor() grpc.UnaryServerInterceptor { return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { startTime := time.Now() resp, err := handler(ctx, req) duration := time.Since(startTime) code := status.Code(err) msg := "Success" if err != nil { msg = err.Error() } m.logger.Printf("%s [%s] %s unary %s (%s)", code, info.FullMethod, duration, msg, ExtractMetadata(ctx)) return resp, err } } // StreamInterceptor returns grpc StreamServerInterceptor. func (m *Middleware) StreamInterceptor() grpc.StreamServerInterceptor { return func(srv any, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { startTime := time.Now() err := handler(srv, stream) duration := time.Since(startTime) code := status.Code(err) msg := "Success" if err != nil { msg = err.Error() } m.logger.Printf("%s [%s] %s stream %s (%s)", code, info.FullMethod, duration, msg, ExtractMetadata(stream.Context())) return err } } ================================================ FILE: pkg/grpc/middleware/log/log_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package log_test import ( "testing" "github.com/stretchr/testify/assert" metadata "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/grpc/middleware/log" ) func TestExtractMetadata(t *testing.T) { for _, test := range []struct { name string md metadata.MD expected string }{ { name: "empty", md: metadata.MD{}, expected: "", }, { name: "regular", md: metadata.Pairs("foo", "bar", "one", "two", "a", "b"), expected: "a=b;foo=bar;one=two", }, { name: "sensitive", md: metadata.Pairs("foo", "bar", "token", "secret"), expected: "foo=bar;token=", }, } { ctx := t.Context() ctx = metadata.NewIncomingContext(ctx, test.md) assert.Equal(t, test.expected, log.ExtractMetadata(ctx), test.name) } } ================================================ FILE: pkg/grpc/proxy/backend/backend.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package backend implements common proxy backends satisfying proxy.Backend interface package backend ================================================ FILE: pkg/grpc/proxy/backend/local.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend import ( "context" "sync" "github.com/siderolabs/grpc-proxy/proxy" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/machinery/constants" ) var _ proxy.Backend = (*Local)(nil) // Local implements local backend (proxying one2one to local service). type Local struct { name string socketPath string mu sync.Mutex conn *grpc.ClientConn } // NewLocal builds new Local backend. func NewLocal(name, socketPath string) *Local { return &Local{ name: name, socketPath: socketPath, } } func (l *Local) String() string { return l.name } // GetConnection returns a grpc connection to the backend. func (l *Local) GetConnection(ctx context.Context, _ string) (context.Context, *grpc.ClientConn, error) { md, _ := metadata.FromIncomingContext(ctx) md = md.Copy() authz.SetMetadata(md, authz.GetRoles(ctx)) outCtx := metadata.NewOutgoingContext(ctx, md) l.mu.Lock() defer l.mu.Unlock() if l.conn != nil { return outCtx, l.conn, nil } var err error l.conn, err = grpc.NewClient( "unix:"+l.socketPath, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultCallOptions( grpc.MaxCallRecvMsgSize(constants.GRPCMaxMessageSize), grpc.ForceCodecV2(proxy.Codec()), ), grpc.WithSharedWriteBuffer(true), grpc.WithNoProxy(), ) return outCtx, l.conn, err } // AppendInfo is called to enhance response from the backend with additional data. func (l *Local) AppendInfo(_ bool, resp []byte) ([]byte, error) { return resp, nil } // BuildError is called to convert error from upstream into response field. func (l *Local) BuildError(bool, error) ([]byte, error) { return nil, nil } // Close the backend connection. func (l *Local) Close() error { l.mu.Lock() defer l.mu.Unlock() if l.conn == nil { return nil } err := l.conn.Close() l.conn = nil return err } ================================================ FILE: pkg/grpc/proxy/backend/local_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package backend_test import ( "testing" "github.com/stretchr/testify/assert" "google.golang.org/grpc/metadata" "github.com/siderolabs/talos/pkg/grpc/middleware/authz" "github.com/siderolabs/talos/pkg/grpc/proxy/backend" "github.com/siderolabs/talos/pkg/machinery/role" ) func TestLocalGetConnection(t *testing.T) { t.Parallel() l := backend.NewLocal("test", "/tmp/test.sock") md1 := metadata.New(nil) md1.Set("key", "value1", "value2") ctx1 := metadata.NewIncomingContext(authz.ContextWithRoles(t.Context(), role.MakeSet(role.Admin)), md1) outCtx1, conn1, err1 := l.GetConnection(ctx1, "") assert.NoError(t, err1) assert.NotNil(t, conn1) assert.Equal(t, role.MakeSet(role.Admin), authz.GetRoles(outCtx1)) mdOut1, ok1 := metadata.FromOutgoingContext(outCtx1) assert.True(t, ok1) assert.Equal(t, []string{"value1", "value2"}, mdOut1.Get("key")) assert.Equal(t, []string{"os:admin"}, mdOut1.Get("talos-role")) t.Run("Same context", func(t *testing.T) { t.Parallel() ctx2 := ctx1 outCtx2, conn2, err2 := l.GetConnection(ctx2, "") assert.NoError(t, err2) assert.Equal(t, conn1, conn2) // connection is cached assert.Equal(t, role.MakeSet(role.Admin), authz.GetRoles(outCtx2)) mdOut2, ok2 := metadata.FromOutgoingContext(outCtx2) assert.True(t, ok2) assert.Equal(t, []string{"value1", "value2"}, mdOut2.Get("key")) assert.Equal(t, []string{"os:admin"}, mdOut2.Get("talos-role")) }) t.Run("Other context", func(t *testing.T) { t.Parallel() md3 := metadata.New(nil) md3.Set("key", "value3", "value4") ctx3 := metadata.NewIncomingContext(authz.ContextWithRoles(t.Context(), role.MakeSet(role.Reader)), md3) outCtx3, conn3, err3 := l.GetConnection(ctx3, "") assert.NoError(t, err3) assert.Equal(t, conn1, conn3) // connection is cached assert.Equal(t, role.MakeSet(role.Reader), authz.GetRoles(outCtx3)) mdOut3, ok3 := metadata.FromOutgoingContext(outCtx3) assert.True(t, ok3) assert.Equal(t, []string{"value3", "value4"}, mdOut3.Get("key")) assert.Equal(t, []string{"os:reader"}, mdOut3.Get("talos-role")) }) } ================================================ FILE: pkg/httpdefaults/httpdefaults.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package httpdefaults provides default HTTP client settings for Talos. package httpdefaults import ( "crypto/tls" "net/http" "net/url" "golang.org/x/net/http/httpproxy" ) // PatchTransport updates *http.Transport with Talos-specific settings. // // Settings applied here only make sense when running in Talos root filesystem. func PatchTransport(transport *http.Transport) *http.Transport { // Explicitly set the Proxy function to work around proxy.Do // once: the environment variables will be reread/initialized each time the // http call is made. transport.Proxy = func(req *http.Request) (*url.URL, error) { return httpproxy.FromEnvironment().ProxyFunc()(req.URL) } // Override the TLS config to allow refreshing CA list which might be updated // via the machine config on the fly. transport.TLSClientConfig = &tls.Config{ RootCAs: RootCAs(), } return transport } ================================================ FILE: pkg/httpdefaults/tls.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package httpdefaults import ( "crypto/tls" "crypto/x509" "io/fs" "os" "sync" "github.com/siderolabs/talos/pkg/machinery/constants" ) var ( cachedPool *x509.CertPool cachedSt fs.FileInfo cacheMu sync.Mutex ) // RootCAs provides a cached, but refreshed, list of root CAs. // // If loading certificates fails for any reason, function returns nil. func RootCAs() *x509.CertPool { st, err := os.Stat(constants.DefaultTrustedCAFile) if err != nil { return nil } // check if the file hasn't changed cacheMu.Lock() defer cacheMu.Unlock() if cachedPool != nil && cachedSt != nil { if cachedSt.ModTime().Equal(st.ModTime()) && cachedSt.Size() == st.Size() { return cachedPool } } pool := x509.NewCertPool() contents, err := os.ReadFile(constants.DefaultTrustedCAFile) if err == nil { if pool.AppendCertsFromPEM(contents) { cachedPool = pool cachedSt = st } } if cachedPool == nil { return nil } return cachedPool.Clone() } // RootCAsTLSConfig provides a TLS configuration with the root CAs. func RootCAsTLSConfig() *tls.Config { return &tls.Config{ RootCAs: RootCAs(), } } ================================================ FILE: pkg/httpdefaults/useragent.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package httpdefaults import "github.com/siderolabs/talos/pkg/machinery/version" // UserAgent is the default User-Agent header value for HTTP requests made by Talos. func UserAgent() string { return version.Name + "/" + version.Tag } ================================================ FILE: pkg/imager/cache/cache.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cache provides facilities for generating a cache tarball from images. package cache import ( "cmp" "crypto/sha256" "errors" "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "strings" "sync" "syscall" "time" "github.com/dustin/go-humanize" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/authn/github" "github.com/google/go-containerregistry/pkg/crane" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/cache" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/google" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/google/go-containerregistry/pkg/v1/remote/transport" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/pkg/imager/filemap" ) const ( blobsDir = "blob" manifestsDir = "manifests" ) // rewriteRegistry name back to workaround https://github.com/google/go-containerregistry/pull/69. func rewriteRegistry(registryName, origRef string) string { if registryName == name.DefaultRegistry && !strings.HasPrefix(origRef, name.DefaultRegistry+"/") { return "docker.io" } // convert :port to _port_ to support copying image-cache to vfat filesystems idx := strings.LastIndex(registryName, ":") if idx > 0 { return registryName[:idx] + "_" + registryName[idx+1:] + "_" } return registryName } // Generate generates a cache tarball from the given images. // //nolint:gocyclo,cyclop func Generate(images []string, platforms []string, insecure bool, imageLayerCachePath, dest string, flat bool, withCosignSignatures bool) error { tmpDir, err := os.MkdirTemp("", "talos-image-cache-gen") if err != nil { return fmt.Errorf("creating temporary directory: %w", err) } if imageLayerCachePath != "" { if err := os.MkdirAll(imageLayerCachePath, 0o755); err != nil { return fmt.Errorf("creating image layer cache directory: %w", err) } } removeAll := sync.OnceValue(func() error { return os.RemoveAll(tmpDir) }) defer removeAll() //nolint:errcheck if len(platforms) < 1 { return fmt.Errorf("must specify at least one platform") } for _, platform := range platforms { v1Platform, err := v1.ParsePlatform(platform) if err != nil { return fmt.Errorf("parsing platform: %w", err) } if err := os.MkdirAll(filepath.Join(tmpDir, blobsDir), 0o755); err != nil { return err } var nameOptions []name.Option keychain := authn.NewMultiKeychain( authn.DefaultKeychain, github.Keychain, google.Keychain, ) craneOpts := []crane.Option{ crane.WithAuthFromKeychain(keychain), } remoteOpts := []remote.Option{ remote.WithAuthFromKeychain(keychain), remote.WithPlatform(*v1Platform), } // sigRemoteOpts is used for fetching cosign signatures - no platform resolution // since signatures are platform-independent and may be stored as OCI image indexes. sigRemoteOpts := []remote.Option{ remote.WithAuthFromKeychain(keychain), } if insecure { craneOpts = append(craneOpts, crane.Insecure) nameOptions = append(nameOptions, name.Insecure) } for _, src := range images { r := retry.Exponential( 30*time.Minute, retry.WithUnits(time.Second), retry.WithJitter(time.Second), retry.WithErrorLogging(true), ) err := r.Retry(func() error { if err := processImage(src, tmpDir, imageLayerCachePath, nameOptions, craneOpts, remoteOpts, sigRemoteOpts, withCosignSignatures); err != nil { switch { case errors.Is(err, new(name.ErrBadName)): return err default: return retry.ExpectedError(err) } } return nil }) if err != nil { return fmt.Errorf("failed to prcess image: %w", err) } } } if flat { return move(tmpDir, dest) } newImg := mutate.MediaType(empty.Image, types.OCIManifestSchema1) newImg = mutate.ConfigMediaType(newImg, types.OCIConfigJSON) newImg, err = mutate.CreatedAt(newImg, v1.Time{Time: time.Now()}) if err != nil { return fmt.Errorf("setting created at: %w", err) } artifacts, err := filemap.Walk(tmpDir, "") if err != nil { return fmt.Errorf("walking filesystem: %w", err) } artifactsLayer, err := filemap.Layer(artifacts) if err != nil { return fmt.Errorf("creating artifacts layer: %w", err) } newImg, err = mutate.AppendLayers(newImg, artifactsLayer) if err != nil { return fmt.Errorf("appending artifacts layer: %w", err) } // we can always write an empty index, since dest is always a new empty directory ociLayout, err := layout.Write(dest, empty.Index) if err != nil { return fmt.Errorf("creating layout: %w", err) } imagePlatform, err := v1.ParsePlatform(platforms[0]) if err != nil { return fmt.Errorf("parsing platform: %w", err) } if err := ociLayout.AppendImage(newImg, layout.WithPlatform(*imagePlatform)); err != nil { return fmt.Errorf("appending image: %w", err) } return removeAll() } func move(src, dest string) error { if err := os.Rename(src, dest); err == nil { return nil } else if !errors.Is(err, syscall.EXDEV) { // not a cross-device error - return it return err } // cross-device: must copy+remove info, err := os.Stat(src) if err != nil { return err } if info.IsDir() { if err := copyDir(src, dest); err != nil { return err } } else { if err := copyFile(src, dest, info.Mode()); err != nil { return err } } return os.RemoveAll(src) } func copyFile(src, dest string, perm fs.FileMode) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() //nolint:errcheck // Ensure destination directory exists if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { return err } out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm) if err != nil { return err } defer out.Close() //nolint:errcheck if _, err = io.Copy(out, in); err != nil { return err } return nil } func copyDir(src, dest string) error { return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error { if err != nil { return err } rel, err := filepath.Rel(src, path) if err != nil { return err } target := filepath.Join(dest, rel) if info.IsDir() { return os.MkdirAll(target, info.Mode()) } return copyFile(path, target, info.Mode()) }) } //nolint:gocyclo,cyclop func processImage( src, tmpDir, imageLayerCachePath string, nameOptions []name.Option, craneOpts []crane.Option, remoteOpts []remote.Option, sigRemoteOpts []remote.Option, withCosignSignatures bool, ) error { fmt.Fprintf(os.Stderr, "fetching image %q\n", src) ref, err := name.ParseReference(src, nameOptions...) if err != nil { return fmt.Errorf("parsing reference %q: %w", src, err) } referenceDir := filepath.Join(tmpDir, manifestsDir, rewriteRegistry(ref.Context().RegistryStr(), src), filepath.FromSlash(ref.Context().RepositoryStr()), "reference") digestDir := filepath.Join(tmpDir, manifestsDir, rewriteRegistry(ref.Context().RegistryStr(), src), filepath.FromSlash(ref.Context().RepositoryStr()), "digest") // if the reference was parsed as a tag, use it tag, ok := ref.(name.Tag) if !ok { if base, _, ok := strings.Cut(src, "@"); ok { // if the reference was a digest, but contained a tag, re-parse it tag, _ = name.NewTag(base, nameOptions...) //nolint:errcheck } } if err = os.MkdirAll(referenceDir, 0o755); err != nil { return err } if err = os.MkdirAll(digestDir, 0o755); err != nil { return err } manifest, err := crane.Manifest( ref.String(), craneOpts..., ) if err != nil { return fmt.Errorf("fetching manifest %q: %w", ref.String(), err) } rmt, err := remote.Get( ref, remoteOpts..., ) if err != nil { return fmt.Errorf("fetching image %q: %w", ref.String(), err) } if tag.TagStr() != "" { if err := os.WriteFile(filepath.Join(referenceDir, tag.TagStr()), manifest, 0o644); err != nil { return err } } if err := os.WriteFile(filepath.Join(digestDir, strings.ReplaceAll(rmt.Digest.String(), "sha256:", "sha256-")), manifest, 0o644); err != nil { return err } if withCosignSignatures { if err := processCosignSignature(src, rmt.Digest, tmpDir, nameOptions, craneOpts, sigRemoteOpts); err != nil { return err } } img, err := rmt.Image() if err != nil { return fmt.Errorf("converting image to index: %w", err) } if imageLayerCachePath != "" { img = cache.Image(img, cache.NewFilesystemCache(imageLayerCachePath)) } layers, err := img.Layers() if err != nil { return fmt.Errorf("getting image layers: %w", err) } config, err := img.RawConfigFile() if err != nil { return fmt.Errorf("getting image config: %w", err) } platformManifest, err := img.RawManifest() if err != nil { return fmt.Errorf("getting image platform manifest: %w", err) } h := sha256.New() if _, err := h.Write(platformManifest); err != nil { return fmt.Errorf("platform manifest hash: %w", err) } if err := os.WriteFile(filepath.Join(digestDir, fmt.Sprintf("sha256-%x", h.Sum(nil))), platformManifest, 0o644); err != nil { return err } configHash, err := img.ConfigName() if err != nil { return fmt.Errorf("getting image config hash: %w", err) } if err := os.WriteFile(filepath.Join(tmpDir, blobsDir, strings.ReplaceAll(configHash.String(), "sha256:", "sha256-")), config, 0o644); err != nil { return err } for _, layer := range layers { if err = processLayer(layer, tmpDir); err != nil { return err } } return nil } //nolint:gocyclo,cyclop func processCosignSignature( src string, digest v1.Hash, tmpDir string, nameOptions []name.Option, craneOpts []crane.Option, remoteOpts []remote.Option, ) error { ref, err := name.ParseReference(src, nameOptions...) if err != nil { return fmt.Errorf("parsing reference for cosign %q: %w", src, err) } baseTag := strings.ReplaceAll(digest.String(), "sha256:", "sha256-") for _, sigTagStr := range []string{baseTag + ".sig", baseTag} { sigRef, err := name.NewTag(ref.Context().String()+":"+sigTagStr, nameOptions...) if err != nil { continue } fmt.Fprintf(os.Stderr, "fetching cosign signature %q\n", sigRef.String()) sigManifest, err := crane.Manifest(sigRef.String(), craneOpts...) if err != nil { var transportErr *transport.Error if errors.As(err, &transportErr) && transportErr.StatusCode == http.StatusNotFound { continue } return fmt.Errorf("fetching cosign manifest %q: %w", sigRef.String(), err) } sigRmt, err := remote.Get(sigRef, remoteOpts...) if err != nil { var transportErr *transport.Error if errors.As(err, &transportErr) && transportErr.StatusCode == http.StatusNotFound { continue } return fmt.Errorf("fetching cosign image %q: %w", sigRef.String(), err) } referenceDir := filepath.Join(tmpDir, manifestsDir, rewriteRegistry(sigRef.Context().RegistryStr(), src), filepath.FromSlash(sigRef.Context().RepositoryStr()), "reference") digestDir := filepath.Join(tmpDir, manifestsDir, rewriteRegistry(sigRef.Context().RegistryStr(), src), filepath.FromSlash(sigRef.Context().RepositoryStr()), "digest") if err = os.MkdirAll(referenceDir, 0o755); err != nil { return err } if err = os.MkdirAll(digestDir, 0o755); err != nil { return err } if err := os.WriteFile(filepath.Join(referenceDir, sigTagStr), sigManifest, 0o644); err != nil { return err } if err := os.WriteFile(filepath.Join(digestDir, strings.ReplaceAll(sigRmt.Digest.String(), "sha256:", "sha256-")), sigManifest, 0o644); err != nil { return err } switch sigRmt.MediaType { //nolint:exhaustive case types.OCIImageIndex, types.DockerManifestList: // Signature is an OCI image index - process each child manifest. idx, err := sigRmt.ImageIndex() if err != nil { return fmt.Errorf("getting cosign image index: %w", err) } idxManifest, err := idx.IndexManifest() if err != nil { return fmt.Errorf("getting cosign index manifest: %w", err) } for _, descriptor := range idxManifest.Manifests { childImg, err := idx.Image(descriptor.Digest) if err != nil { return fmt.Errorf("getting cosign child image %s: %w", descriptor.Digest, err) } childManifest, err := childImg.RawManifest() if err != nil { return fmt.Errorf("getting cosign child manifest: %w", err) } childDigest, err := childImg.Digest() if err != nil { return fmt.Errorf("getting cosign child digest: %w", err) } if err := os.WriteFile(filepath.Join(digestDir, strings.ReplaceAll(childDigest.String(), "sha256:", "sha256-")), childManifest, 0o644); err != nil { return err } config, err := childImg.RawConfigFile() if err != nil { return fmt.Errorf("getting cosign child config: %w", err) } configHash, err := childImg.ConfigName() if err != nil { return fmt.Errorf("getting cosign child config hash: %w", err) } if err := os.WriteFile(filepath.Join(tmpDir, blobsDir, strings.ReplaceAll(configHash.String(), "sha256:", "sha256-")), config, 0o644); err != nil { return err } layers, err := childImg.Layers() if err != nil { return fmt.Errorf("getting cosign child layers: %w", err) } for _, layer := range layers { if err = processLayer(layer, tmpDir); err != nil { return err } } } default: // Signature is a single image manifest. img, err := sigRmt.Image() if err != nil { return fmt.Errorf("getting cosign image: %w", err) } config, err := img.RawConfigFile() if err != nil { return fmt.Errorf("getting cosign config: %w", err) } configHash, err := img.ConfigName() if err != nil { return fmt.Errorf("getting cosign config hash: %w", err) } if err := os.WriteFile(filepath.Join(tmpDir, blobsDir, strings.ReplaceAll(configHash.String(), "sha256:", "sha256-")), config, 0o644); err != nil { return err } layers, err := img.Layers() if err != nil { return fmt.Errorf("getting cosign layers: %w", err) } for _, layer := range layers { if err = processLayer(layer, tmpDir); err != nil { return err } } } return nil } return nil } func processLayer(layer v1.Layer, dstDir string) error { digest, err := layer.Digest() if err != nil { return fmt.Errorf("getting layer digest: %w", err) } blobPath := filepath.Join(dstDir, blobsDir, strings.ReplaceAll(digest.String(), "sha256:", "sha256-")) if _, err := os.Stat(blobPath); err == nil { // we already have this blob, skip it return nil } size, err := layer.Size() if err != nil { return fmt.Errorf("getting layer size: %w", err) } fmt.Fprintf(os.Stderr, "> layer %q (size %s)...\n", digest, humanize.Bytes(uint64(size))) reader, err := layer.Compressed() if err != nil { return fmt.Errorf("getting layer reader: %w", err) } rdrCloser := sync.OnceValue(reader.Close) defer rdrCloser() //nolint:errcheck file, err := os.Create(blobPath) if err != nil { return err } fileCloser := sync.OnceValue(file.Close) defer fileCloser() //nolint:errcheck if _, err := io.Copy(file, reader); err != nil { return err } return cmp.Or(rdrCloser(), fileCloser()) } ================================================ FILE: pkg/imager/embed.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager import ( "archive/tar" "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" "os" "path/filepath" "strings" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/extensions" ) func (i *Imager) handleEmbeddedConfig() error { if len(i.prof.Customization.EmbeddedMachineConfiguration) == 0 { return nil } contents, err := BuildEmbeddedConfigExtension([]byte(i.prof.Customization.EmbeddedMachineConfiguration)) if err != nil { return fmt.Errorf("failed to build embedded config extension: %w", err) } tmpPath := filepath.Join(i.tempDir, "embedded-config.tar") f, err := os.Create(tmpPath) if err != nil { return fmt.Errorf("failed to create temporary file: %w", err) } defer f.Close() //nolint:errcheck if _, err := io.Copy(f, contents); err != nil { return fmt.Errorf("failed to write embedded config to temporary file: %w", err) } i.prof.Input.SystemExtensions = append(i.prof.Input.SystemExtensions, profile.ContainerAsset{ TarballPath: tmpPath, }, ) return nil } // BuildEmbeddedConfigExtension builds a tarball containing the embedded machine configuration as a virtual extension. func BuildEmbeddedConfigExtension(machineConfig []byte) (io.Reader, error) { sha256sum := sha256.Sum256(machineConfig) extensionVersion := hex.EncodeToString(sha256sum[:]) manifest := extensions.Manifest{ Version: "v1alpha1", Metadata: extensions.Metadata{ Name: "embedded-config", Version: extensionVersion, Author: "Imager", Description: "Virtual extension which embeds the machine configuration.", Compatibility: extensions.Compatibility{ Talos: extensions.Constraint{ Version: ">= 1.0.0", }, }, }, } manifestBytes, err := yaml.Marshal(manifest) if err != nil { return nil, fmt.Errorf("failed to marshal manifest: %w", err) } var buf bytes.Buffer tw := tar.NewWriter(&buf) if err = tw.WriteHeader(&tar.Header{ Name: "manifest.yaml", Typeflag: tar.TypeReg, Mode: 0o644, Size: int64(len(manifestBytes)), }); err != nil { return nil, fmt.Errorf("failed to write manifest header: %w", err) } if _, err = tw.Write(manifestBytes); err != nil { return nil, fmt.Errorf("failed to write manifest: %w", err) } path := strings.Split(strings.TrimRight(constants.EmbeddedConfigDirectory, "/"), "/") for i := range path { dir := filepath.Join("rootfs", strings.Join(path[:i+1], "/")) if err = tw.WriteHeader(&tar.Header{ Name: dir, Typeflag: tar.TypeDir, Mode: 0o755, }); err != nil { return nil, fmt.Errorf("failed to write rootfs header: %w", err) } } if err = tw.WriteHeader(&tar.Header{ Name: filepath.Join("rootfs", constants.EmbeddedConfigDirectory, constants.ConfigFilename), Typeflag: tar.TypeReg, Mode: 0o000, Size: int64(len(machineConfig)), PAXRecords: map[string]string{ "SCHILY.xattr.security.selinux": constants.StateSelinuxLabel, }, }); err != nil { return nil, fmt.Errorf("failed to write embedded header: %w", err) } if _, err = tw.Write(machineConfig); err != nil { return nil, fmt.Errorf("failed to write embedded config: %w", err) } if err = tw.Close(); err != nil { return nil, fmt.Errorf("failed to close tar writer: %w", err) } return &buf, nil } ================================================ FILE: pkg/imager/embed_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager_test import ( "archive/tar" "io" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager" ) func TestBuildEmbeddedConfigExtension(t *testing.T) { t.Parallel() out, err := imager.BuildEmbeddedConfigExtension([]byte("test")) require.NoError(t, err) tr := tar.NewReader(out) var paths []string for { hdr, err := tr.Next() if err == io.EOF { break } require.NoError(t, err) paths = append(paths, hdr.Name) } assert.Equal(t, []string{ "manifest.yaml", "rootfs", "rootfs/usr", "rootfs/usr/local", "rootfs/usr/local/etc", "rootfs/usr/local/etc/talos", "rootfs/usr/local/etc/talos/config.yaml", }, paths) } ================================================ FILE: pkg/imager/extensions/contents.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "bytes" "fmt" "io" "os" "path/filepath" "strings" "github.com/siderolabs/talos/internal/pkg/extensions" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) func findExtensionsWithKernelModules(extensions []*extensions.Extension, quirks quirks.Quirks) []string { var modulesPath []string for _, ext := range extensions { if ext.ProvidesKernelModules(quirks) { modulesPath = append(modulesPath, ext.KernelModuleDirectory(quirks)) } } return modulesPath } // buildInitramfsContents builds a list of files to be included into initramfs directly, bypassing extensions squashfs. // // Two listings are returned: // - uncompressedListing is a list of files that should be included into initramfs uncompressed prepended as a first section // - compressedListing is a list of files that should be included into initramfs compressed. func buildInitramfsContents(path string) (compressedListing, uncompressedListing []byte, err error) { var compressedBuffer, uncompressedBuffer bytes.Buffer if err := buildInitramfsContentsRecursive(path, "", &compressedBuffer, &uncompressedBuffer); err != nil { return nil, nil, err } return compressedBuffer.Bytes(), uncompressedBuffer.Bytes(), nil } func buildInitramfsContentsRecursive(basePath, path string, compressedW, uncompressedW io.Writer) error { if path != "" { if path == "kernel" || strings.HasPrefix(path, "kernel/") { fmt.Fprintf(uncompressedW, "%s\n", path) } else { fmt.Fprintf(compressedW, "%s\n", path) } } st, err := os.Stat(filepath.Join(basePath, path)) if err != nil { return err } if !st.IsDir() { return nil } contents, err := os.ReadDir(filepath.Join(basePath, path)) if err != nil { return err } for _, item := range contents { if err = buildInitramfsContentsRecursive(basePath, filepath.Join(path, item.Name()), compressedW, uncompressedW); err != nil { return err } } return nil } ================================================ FILE: pkg/imager/extensions/extensions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package extensions provides facilities for building initramfs.xz with extensions. package extensions import ( "context" "fmt" "os" "path/filepath" "github.com/siderolabs/talos/internal/pkg/extensions" "github.com/siderolabs/talos/pkg/machinery/constants" extinterface "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Builder rebuilds initramfs.xz with extensions. type Builder struct { // The initramfs will be rebuilt in-place. InitramfsPath string // Architecture of the initramfs. Arch string // ExtensionTreePath is a path to the extracted extension tree. ExtensionTreePath string // ExtensionValidateContents enables validation of the extension contents. ExtensionValidateContents bool // Printf is used for logging. Printf func(format string, v ...any) // Quirks for the Talos version being used. Quirks quirks.Quirks } // Build rebuilds the initramfs.xz with extensions. // //nolint:gocyclo func (builder *Builder) Build(ctx context.Context) error { extensionsList, err := extensions.List(builder.ExtensionTreePath) if err != nil { return fmt.Errorf("error listing extensions: %w", err) } if len(extensionsList) == 0 { return nil } if err = builder.printExtensions(extensionsList); err != nil { return err } if err = builder.validateExtensions(extensionsList); err != nil { return err } extensionPathsWithKernelModules := findExtensionsWithKernelModules(extensionsList, builder.Quirks) if len(extensionPathsWithKernelModules) > 0 { var scratchPath string // create a temporary directory to store 'modules.dep' extension scratchPath, err = os.MkdirTemp("", "ext-modules") if err != nil { return err } defer os.RemoveAll(scratchPath) //nolint:errcheck kernelModuleDepExtension, genErr := extensions.GenerateKernelModuleDependencyTreeExtension(ctx, extensionPathsWithKernelModules, builder.InitramfsPath, scratchPath, builder.Quirks, builder.Printf) if genErr != nil { return genErr } extensionsList = append(extensionsList, kernelModuleDepExtension) } tempDir, err := os.MkdirTemp("", "ext") if err != nil { return err } defer os.RemoveAll(tempDir) //nolint:errcheck var cfg *extinterface.Config if cfg, err = builder.compressExtensions(ctx, extensionsList, tempDir); err != nil { return err } if err = cfg.Write(filepath.Join(tempDir, constants.ExtensionsConfigFile)); err != nil { return err } return builder.rebuildInitramfs(ctx, tempDir, builder.Quirks) } func (builder *Builder) validateExtensions(extensions []*extensions.Extension) error { builder.Printf("validating system extensions") opts := []extinterface.ValidationOption{ extinterface.WithValidateConstraints(), extinterface.WithTalosVersion(builder.Quirks.Version()), } if builder.ExtensionValidateContents { opts = append(opts, extinterface.WithValidateContents()) } for _, ext := range extensions { if err := ext.Validate(opts...); err != nil { return fmt.Errorf("error validating extension %q: %w", ext.Manifest.Metadata.Name, err) } } return nil } func (builder *Builder) compressExtensions(ctx context.Context, extensions []*extensions.Extension, tempDir string) (*extinterface.Config, error) { cfg := &extinterface.Config{} builder.Printf("compressing system extensions") for _, ext := range extensions { path, err := ext.Compress(ctx, tempDir, tempDir, builder.Quirks) if err != nil { return nil, fmt.Errorf("error compressing extension %q: %w", ext.Manifest.Metadata.Name, err) } cfg.Layers = append(cfg.Layers, &extinterface.Layer{ Image: filepath.Base(path), Metadata: ext.Manifest.Metadata, }) } return cfg, nil } ================================================ FILE: pkg/imager/extensions/printer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "bytes" "fmt" "text/tabwriter" "github.com/siderolabs/talos/internal/pkg/extensions" ) func (builder *Builder) printExtensions(extensions []*extensions.Extension) error { builder.Printf("discovered system extensions:") var b bytes.Buffer w := tabwriter.NewWriter(&b, 0, 0, 3, ' ', 0) fmt.Fprint(w, "NAME\tVERSION\tAUTHOR\n") for _, ext := range extensions { fmt.Fprintf(w, "%s\t%s\t%s\n", ext.Manifest.Metadata.Name, ext.Manifest.Metadata.Version, ext.Manifest.Metadata.Author) } if err := w.Flush(); err != nil { return err } for { line, err := b.ReadString('\n') if err != nil { break } builder.Printf("%s", line) } return nil //nolint:nilerr } ================================================ FILE: pkg/imager/extensions/rebuild.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "bytes" "context" "fmt" "io" "os" "os/exec" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // rebuildInitramfs rebuilds finalized initramfs with extensions. // // If uncompressedListing is not empty, contents will be prepended to the initramfs uncompressed. // Contents from compressedListing will be appended to the initramfs compressed (xz/zstd) as a second block. // Original initramfs.xz contents will stay without changes. func (builder *Builder) rebuildInitramfs(ctx context.Context, tempDir string, quirks quirks.Quirks) error { compressedListing, uncompressedListing, err := buildInitramfsContents(tempDir) if err != nil { return err } if len(uncompressedListing) > 0 { if err = builder.prependUncompressedInitramfs(ctx, tempDir, uncompressedListing); err != nil { return fmt.Errorf("error prepending uncompressed initramfs: %w", err) } } if err = builder.appendCompressedInitramfs(ctx, tempDir, compressedListing, quirks); err != nil { return fmt.Errorf("error appending compressed initramfs: %w", err) } return nil } func (builder *Builder) appendCompressedInitramfs(ctx context.Context, tempDir string, compressedListing []byte, quirks quirks.Quirks) error { builder.Printf("creating system extensions initramfs archive and compressing it") // the code below runs the equivalent of: // find $tempDir -print | cpio -H newc --create --reproducible | { xz -v -C crc32 -0 -e -T 0 -z || zstd -T0 -18 -c --quiet } pipeR, pipeW, err := os.Pipe() if err != nil { return err } defer pipeR.Close() //nolint:errcheck defer pipeW.Close() //nolint:errcheck // build cpio image which contains .sqsh images and extensions.yaml cmd1 := exec.CommandContext(ctx, "cpio", "-H", "newc", "--create", "--reproducible", "--quiet", "-R", "+0:+0") cmd1.Dir = tempDir cmd1.Stdin = bytes.NewReader(compressedListing) cmd1.Stdout = pipeW cmd1.Stderr = os.Stderr if err = cmd1.Start(); err != nil { return err } if err = pipeW.Close(); err != nil { return err } destination, err := os.OpenFile(builder.InitramfsPath, os.O_APPEND|os.O_WRONLY, 0) if err != nil { return err } defer destination.Close() //nolint:errcheck // append compressed initramfs.sysext to the original initramfs.xz, kernel can read such format var cmd2 *exec.Cmd if quirks.UseZSTDCompression() { cmd2 = exec.CommandContext(ctx, "zstd", "-T0", "-18", "-c", "--quiet") } else { cmd2 = exec.CommandContext(ctx, "xz", "-v", "-C", "crc32", "-0", "-e", "-T", "0", "-z", "--quiet") } cmd2.Dir = tempDir cmd2.Stdin = pipeR cmd2.Stdout = destination cmd2.Stderr = os.Stderr if err = cmd2.Start(); err != nil { return err } if err = pipeR.Close(); err != nil { return err } errCh := make(chan error, 1) go func() { errCh <- cmd1.Wait() }() go func() { errCh <- cmd2.Wait() }() for range 2 { if err = <-errCh; err != nil { return err } } return destination.Sync() } func (builder *Builder) prependUncompressedInitramfs(ctx context.Context, tempDir string, uncompressedListing []byte) error { builder.Printf("creating uncompressed initramfs archive") // the code below runs the equivalent of: // mv initramfs.xz initramfs.xz-old // find $tempDir -print | cpio -H newc --create --reproducible > initramfs.xz // cat initramfs.xz-old >> initramfs.xz // rm initramfs.xz-old initramfsOld := builder.InitramfsPath + "-old" if err := os.Rename(builder.InitramfsPath, initramfsOld); err != nil { return err } destination, err := os.OpenFile(builder.InitramfsPath, os.O_CREATE|os.O_WRONLY, 0o644) if err != nil { return err } defer destination.Close() //nolint:errcheck cmd := exec.CommandContext(ctx, "cpio", "-H", "newc", "--create", "--reproducible", "--quiet", "-R", "+0:+0") cmd.Dir = tempDir cmd.Stdin = bytes.NewReader(uncompressedListing) cmd.Stdout = destination cmd.Stderr = os.Stderr if err = cmd.Run(); err != nil { return err } old, err := os.Open(initramfsOld) if err != nil { return err } defer old.Close() //nolint:errcheck if _, err = io.Copy(destination, old); err != nil { return err } if err = destination.Close(); err != nil { return err } if err = old.Close(); err != nil { return err } return os.Remove(initramfsOld) } ================================================ FILE: pkg/imager/filemap/filemap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package filemap provides a way to create reproducible layers from a file system. package filemap import ( "archive/tar" "cmp" "fmt" "io" "os" stdpath "path" "path/filepath" "slices" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" ) // File is a path -> file content map representing a file system. type File struct { ImagePath string SourcePath string ImageMode int64 } // Walk the filesystem generating a filemap. func Walk(sourceBasePath, imageBasePath string) ([]File, error) { var filemap []File err := filepath.WalkDir(sourceBasePath, func(path string, d os.DirEntry, err error) error { if err != nil { return err } rel, err := filepath.Rel(sourceBasePath, path) if err != nil { return err } if d.IsDir() && rel == "." { return nil } statInfo, err := d.Info() if err != nil { return err } filemap = append(filemap, File{ ImagePath: stdpath.Join(imageBasePath, filepath.ToSlash(rel)), SourcePath: path, ImageMode: int64(statInfo.Mode().Perm()), }) return nil }) return filemap, err } // Build a tarball from a filemap. func Build(filemap []File) io.ReadCloser { pr, pw := io.Pipe() go func() { pw.CloseWithError(func() error { w := tar.NewWriter(pw) for _, entry := range filemap { if err := func(entry File) error { in, err := os.Open(entry.SourcePath) if err != nil { return fmt.Errorf("error opening %q: %w", entry.SourcePath, err) } defer in.Close() //nolint:errcheck st, err := in.Stat() if err != nil { return fmt.Errorf("error stating file %s: %w", entry.SourcePath, err) } if st.IsDir() { if err := handleDir(w, entry.ImagePath, entry.ImageMode); err != nil { return fmt.Errorf("error handling directory %s: %w", entry.SourcePath, err) } return in.Close() } if err := handleFile(w, in, entry.ImagePath, entry.ImageMode, st.Size()); err != nil { return fmt.Errorf("error handling file %s: %w", entry.SourcePath, err) } return in.Close() }(entry); err != nil { return fmt.Errorf("error processing %s: %w", entry.SourcePath, err) } } return w.Close() }()) }() return pr } func handleFile(w *tar.Writer, r io.Reader, path string, mode, size int64) error { header := &tar.Header{ Name: path, Mode: mode, Size: size, } if err := w.WriteHeader(header); err != nil { return fmt.Errorf("error writing tar header for %s: %w", path, err) } if _, err := io.Copy(w, r); err != nil { return fmt.Errorf("error writing tar data for %s: %w", path, err) } return nil } func handleDir(w *tar.Writer, path string, mode int64) error { header := &tar.Header{ Name: path, Mode: mode, Typeflag: tar.TypeDir, } if err := w.WriteHeader(header); err != nil { return fmt.Errorf("error writing tar header for %s: %w", path, err) } return nil } // Layer creates a layer from a single file map. // // These layers are reproducible and consistent. // // A filemap is a path -> file content map representing a file system. func Layer(filemap []File) (v1.Layer, error) { slices.SortFunc(filemap, func(a, b File) int { return cmp.Compare(a.ImagePath, b.ImagePath) }) // Return a new copy of the buffer each time it's opened. return tarball.LayerFromOpener(func() (io.ReadCloser, error) { return Build(filemap), nil }, tarball.WithMediaType(types.OCILayer)) } ================================================ FILE: pkg/imager/filemap/filemap_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package filemap_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/imager/filemap" ) func TestFileMap(t *testing.T) { tempDir := t.TempDir() assert.NoError(t, os.MkdirAll(filepath.Join(tempDir, "foo/a/b"), 0o755)) assert.NoError(t, os.MkdirAll(filepath.Join(tempDir, "foo/c"), 0o755)) assert.NoError(t, os.MkdirAll(filepath.Join(tempDir, "foo/d"), 0o750)) assert.NoError(t, os.WriteFile(filepath.Join(tempDir, "foo/a/b/normal"), nil, 0o644)) assert.NoError(t, os.WriteFile(filepath.Join(tempDir, "foo/c/executable"), []byte("world"), 0o755)) artifacts, err := filemap.Walk(tempDir, "") assert.NoError(t, err) assert.Equal( t, []filemap.File{ { ImagePath: "foo", SourcePath: filepath.Join(tempDir, "foo"), ImageMode: 0o755, }, { ImagePath: "foo/a", SourcePath: filepath.Join(tempDir, "foo/a"), ImageMode: 0o755, }, { ImagePath: "foo/a/b", SourcePath: filepath.Join(tempDir, "foo/a/b"), ImageMode: 0o755, }, { ImagePath: "foo/a/b/normal", SourcePath: filepath.Join(tempDir, "foo/a/b/normal"), ImageMode: 0o644, }, { ImagePath: "foo/c", SourcePath: filepath.Join(tempDir, "foo/c"), ImageMode: 0o755, }, { ImagePath: "foo/c/executable", SourcePath: filepath.Join(tempDir, "foo/c/executable"), ImageMode: 0o755, }, { ImagePath: "foo/d", SourcePath: filepath.Join(tempDir, "foo/d"), ImageMode: 0o750, }, }, artifacts, ) } ================================================ FILE: pkg/imager/imager.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package imager contains code related to generation of different boot assets for Talos Linux. package imager import ( "context" "fmt" "os" "path/filepath" "runtime" "strconv" "strings" "github.com/siderolabs/go-procfs/procfs" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform" "github.com/siderolabs/talos/internal/pkg/uki" "github.com/siderolabs/talos/pkg/imager/extensions" "github.com/siderolabs/talos/pkg/imager/overlay/executor" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/machinery/overlay" "github.com/siderolabs/talos/pkg/machinery/version" "github.com/siderolabs/talos/pkg/reporter" ) // Imager is an interface for image generation. type Imager struct { prof profile.Profile overlayInstaller overlay.Installer[overlay.ExtraOptions] extraProfiles map[string]profile.Profile tempDir string // boot assets initramfsPath string cmdline string sdBootPath string ukiPath string } // New creates a new Imager. func New(prof profile.Profile) (*Imager, error) { return &Imager{ prof: prof, }, nil } // Execute image generation. // //nolint:gocyclo,cyclop func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporter.Reporter) (outputAssetPath string, err error) { i.tempDir, err = os.MkdirTemp("", "imager") if err != nil { return "", fmt.Errorf("failed to create temporary directory: %w", err) } defer os.RemoveAll(i.tempDir) //nolint:errcheck // 0. Handle overlays first if err = i.handleOverlay(ctx, report); err != nil { return "", err } if err = i.handleProf(); err != nil { return "", err } if err = i.handleEmbeddedConfig(); err != nil { return "", err } report.Report(reporter.Update{ Message: "profile ready:", Status: reporter.StatusSucceeded, }) // 1. Dump the profile. if err = i.prof.Dump(os.Stderr); err != nil { return "", err } // 2. Transform `initramfs.xz` with system extensions if err = i.buildInitramfs(ctx, report); err != nil { return "", err } // 3. Prepare kernel arguments. if err = i.buildCmdline(ctx); err != nil { return "", err } report.Report(reporter.Update{ Message: fmt.Sprintf("kernel command line: %s", i.cmdline), Status: reporter.StatusSucceeded, }) // 4. Build UKI if needed needBuildUKI := quirks.New(i.prof.Version).SupportsUKI() switch i.prof.Output.Kind { case profile.OutKindUKI: if !needBuildUKI { return "", fmt.Errorf("UKI output is not supported in this Talos version") } case profile.OutKindISO, profile.OutKindInstaller, profile.OutKindImage: needBuildUKI = needBuildUKI || quirks.New(i.prof.Version).UseSDBootForUEFI() case profile.OutKindCmdline, profile.OutKindKernel, profile.OutKindInitramfs: needBuildUKI = false case profile.OutKindUnknown: fallthrough default: return "", fmt.Errorf("unknown output kind: %s", i.prof.Output.Kind) } if needBuildUKI { if err = i.buildUKI(ctx, report); err != nil { return "", err } } // 5. Build the output. outputAssetPath = filepath.Join(outputPath, i.prof.OutputPath()) switch i.prof.Output.Kind { case profile.OutKindISO: err = i.outISO(ctx, outputAssetPath, report) case profile.OutKindKernel: err = i.outKernel(outputAssetPath, report) case profile.OutKindUKI: err = i.outUKI(outputAssetPath, report) case profile.OutKindInitramfs: err = i.outInitramfs(outputAssetPath, report) case profile.OutKindCmdline: err = i.outCmdline(outputAssetPath) case profile.OutKindImage: err = i.outImage(ctx, outputAssetPath, report) case profile.OutKindInstaller: err = i.outInstaller(ctx, outputAssetPath, report) case profile.OutKindUnknown: fallthrough default: return "", fmt.Errorf("unknown output kind: %s", i.prof.Output.Kind) } if err != nil { return "", err } report.Report(reporter.Update{ Message: fmt.Sprintf("output asset path: %s", outputAssetPath), Status: reporter.StatusSucceeded, }) // 6. Post-process the output. switch i.prof.Output.OutFormat { case profile.OutFormatRaw: // do nothing return outputAssetPath, nil case profile.OutFormatXZ: return i.postProcessXz(ctx, outputAssetPath, report) case profile.OutFormatGZ: return i.postProcessGz(ctx, outputAssetPath, report) case profile.OutFormatZSTD: return i.postProcessZstd(ctx, outputAssetPath, report) case profile.OutFormatTar: return i.postProcessTar(ctx, outputAssetPath, report) case profile.OutFormatUnknown: fallthrough default: return "", fmt.Errorf("unknown output format: %s", i.prof.Output.OutFormat) } } func (i *Imager) handleOverlay(ctx context.Context, report *reporter.Reporter) error { if i.prof.Overlay == nil { report.Report(reporter.Update{ Message: "skipped pulling overlay (no overlay)", Status: reporter.StatusSkip, }) return nil } tempOverlayPath := filepath.Join(i.tempDir, constants.ImagerOverlayBasePath) if err := os.MkdirAll(tempOverlayPath, 0o755); err != nil { return fmt.Errorf("failed to create overlay directory: %w", err) } if err := i.prof.Overlay.Image.Extract(ctx, tempOverlayPath, runtime.GOARCH, progressPrintf(report, reporter.Update{Message: "pulling overlay...", Status: reporter.StatusRunning})); err != nil { return err } // find all *.yaml files in the overlay/profiles/ directory profileYAMLs, err := filepath.Glob(filepath.Join(i.tempDir, constants.ImagerOverlayProfilesPath, "*.yaml")) if err != nil { return fmt.Errorf("failed to find profiles: %w", err) } if i.prof.Overlay.Name == "" { i.prof.Overlay.Name = constants.ImagerOverlayInstallerDefault } i.overlayInstaller = executor.New(filepath.Join(i.tempDir, constants.ImagerOverlayInstallersPath, i.prof.Overlay.Name)) if i.extraProfiles == nil { i.extraProfiles = make(map[string]profile.Profile) } for _, profilePath := range profileYAMLs { profileName := strings.TrimSuffix(filepath.Base(profilePath), ".yaml") var overlayProfile profile.Profile profileDataBytes, err := os.ReadFile(profilePath) if err != nil { return fmt.Errorf("failed to read profile: %w", err) } if err := yaml.Unmarshal(profileDataBytes, &overlayProfile); err != nil { return fmt.Errorf("failed to unmarshal profile: %w", err) } i.extraProfiles[profileName] = overlayProfile } return nil } func (i *Imager) handleProf() error { // resolve the profile if it contains a base name if i.prof.BaseProfileName != "" { baseProfile, ok := i.extraProfiles[i.prof.BaseProfileName] if !ok { baseProfile, ok = profile.Default[i.prof.BaseProfileName] } if !ok { return fmt.Errorf("unknown base profile: %s", i.prof.BaseProfileName) } baseProfile = baseProfile.DeepCopy() // merge the profiles if err := merge.Merge(&baseProfile, &i.prof); err != nil { return err } i.prof = baseProfile i.prof.BaseProfileName = "" } if i.prof.Version == "" { i.prof.Version = version.Tag } i.prof.Input.FillDefaults(i.prof.Arch, i.prof.Version, i.prof.SecureBootEnabled()) i.prof.Output.FillDefaults(i.prof.Arch, i.prof.Version, i.prof.SecureBootEnabled()) if err := i.prof.Validate(); err != nil { return fmt.Errorf("profile is invalid: %w", err) } return nil } // buildInitramfs transforms `initramfs.xz` with system extensions. func (i *Imager) buildInitramfs(ctx context.Context, report *reporter.Reporter) error { if len(i.prof.Input.SystemExtensions) == 0 { report.Report(reporter.Update{ Message: "skipped initramfs rebuild (no system extensions)", Status: reporter.StatusSkip, }) // no system extensions, happy path i.initramfsPath = i.prof.Input.Initramfs.Path return nil } if i.prof.Output.Kind == profile.OutKindCmdline || i.prof.Output.Kind == profile.OutKindKernel { // these outputs don't use initramfs image return nil } printf := progressPrintf(report, reporter.Update{Message: "rebuilding initramfs with system extensions...", Status: reporter.StatusRunning}) // copy the initramfs to a temporary location, as it's going to be modified during the extension build process tempInitramfsPath := filepath.Join(i.tempDir, "initramfs.xz") if err := utils.CopyFiles(printf, utils.SourceDestination(i.prof.Input.Initramfs.Path, tempInitramfsPath)); err != nil { return fmt.Errorf("failed to copy initramfs: %w", err) } i.initramfsPath = tempInitramfsPath extensionsCheckoutDir := filepath.Join(i.tempDir, "extensions") // pull every extension to a temporary location for j, ext := range i.prof.Input.SystemExtensions { extensionDir := filepath.Join(extensionsCheckoutDir, strconv.Itoa(j)) if err := os.MkdirAll(extensionDir, 0o755); err != nil { return fmt.Errorf("failed to create extension directory: %w", err) } if err := ext.Extract(ctx, extensionDir, i.prof.Arch, printf); err != nil { return err } } // rebuild initramfs builder := extensions.Builder{ InitramfsPath: i.initramfsPath, Arch: i.prof.Arch, ExtensionTreePath: extensionsCheckoutDir, Printf: printf, Quirks: quirks.New(i.prof.Version), } if err := builder.Build(ctx); err != nil { return err } report.Report(reporter.Update{ Message: "initramfs ready", Status: reporter.StatusSucceeded, }) return nil } // buildCmdline builds the kernel command line. // //nolint:gocyclo func (i *Imager) buildCmdline(ctx context.Context) error { p, err := platform.NewPlatform(i.prof.Platform) if err != nil { return err } q := quirks.New(i.prof.Version) cmdline := procfs.NewCmdline("") // platform kernel args cmdline.Append(constants.KernelParamPlatform, p.Name()) cmdline.SetAll(p.KernelArgs(i.prof.Arch, q).Strings()) if q.SupportsHaltIfInstalled() && i.prof.Output.Kind == profile.OutKindISO { cmdline.Append(constants.KernelParamHaltIfInstalled, "1") } // overlay kernel args if i.overlayInstaller != nil { options, optsErr := i.overlayInstaller.GetOptions(ctx, i.prof.Overlay.ExtraOptions) if optsErr != nil { return optsErr } cmdline.SetAll(options.KernelArgs) } // first defaults, then extra kernel args to allow extra kernel args to override defaults if err = cmdline.AppendAll(kernel.DefaultArgs(q)); err != nil { return err } if i.prof.SecureBootEnabled() { if err = cmdline.AppendAll(kernel.SecureBootArgs(q)); err != nil { return err } } // meta values can be written only to the "image" output if len(i.prof.Customization.MetaContents) > 0 && i.prof.Output.Kind != profile.OutKindImage { // pass META values as kernel talos.environment args which will be passed via the environment to the installer cmdline.Append( constants.KernelParamEnvironment, constants.MetaValuesEnvVar+"="+i.prof.Customization.MetaContents.Encode(quirks.New(i.prof.Version).SupportsCompressedEncodedMETA()), ) } // apply customization if err = cmdline.AppendAll( i.prof.Customization.ExtraKernelArgs, procfs.WithOverwriteArgs("console"), procfs.WithOverwriteArgs(constants.KernelParamPlatform), procfs.WithDeleteNegatedArgs(), ); err != nil { return err } i.cmdline = cmdline.String() return nil } // buildUKI assembles the UKI and signs it. func (i *Imager) buildUKI(ctx context.Context, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "building UKI...", Status: reporter.StatusRunning}) i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi") i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi") builder := uki.Builder{ Arch: i.prof.Arch, Version: i.prof.Version, SdStubPath: i.prof.Input.SDStub.Path, SdBootPath: i.prof.Input.SDBoot.Path, KernelPath: i.prof.Input.Kernel.Path, InitrdPath: i.initramfsPath, Cmdline: i.cmdline, OutSdBootPath: i.sdBootPath, OutUKIPath: i.ukiPath, } switch i.prof.Output.Kind { //nolint:exhaustive case profile.OutKindISO: builder.Profiles = append(builder.Profiles, uki.Profile{ ID: "reset", Title: "Reset system disk", Cmdline: builder.Cmdline + fmt.Sprintf(" %s=system", constants.KernelParamWipe), }) case profile.OutKindImage, profile.OutKindInstaller: builder.Profiles = append(builder.Profiles, uki.Profile{ ID: "reset-maintenance", Title: "Reset to maintenance mode", Cmdline: builder.Cmdline + fmt.Sprintf(" %s=system:EPHEMERAL,STATE", constants.KernelParamWipe), }) } buildFunc := builder.Build if i.prof.SecureBootEnabled() { i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi.signed") i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi.signed") builder.OutSdBootPath = i.sdBootPath builder.OutUKIPath = i.ukiPath pcrSigner, err := i.prof.Input.SecureBoot.PCRSigner.GetSigner(ctx) if err != nil { return fmt.Errorf("failed to get PCR signer: %w", err) } securebootSigner, err := i.prof.Input.SecureBoot.SecureBootSigner.GetSigner(ctx) if err != nil { return fmt.Errorf("failed to get SecureBoot signer: %w", err) } builder.SecureBootSigner = securebootSigner builder.PCRSigner = pcrSigner buildFunc = builder.BuildSigned } if err := buildFunc(printf); err != nil { return err } report.Report(reporter.Update{ Message: "UKI ready", Status: reporter.StatusSucceeded, }) return nil } ================================================ FILE: pkg/imager/imager_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager_test import ( "context" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/reporter" ) func TestImager(t *testing.T) { t.Parallel() for _, test := range []struct { name string prof profile.Profile expected string }{ { name: "cmdline-pre1.8-amd64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "amd64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.7.0", }, expected: "talos.platform=metal console=ttyS0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512", //nolint:lll }, { name: "cmdline-pre1.8-arm64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "arm64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.7.0", }, expected: "talos.platform=metal console=ttyAMA0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512", //nolint:lll }, { name: "cmdline-1.8-amd64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "amd64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.8.0", }, expected: "talos.platform=metal console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512", //nolint:lll }, { name: "cmdline-1.8-arm64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "arm64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.8.0", }, expected: "talos.platform=metal console=ttyAMA0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512", //nolint:lll }, { name: "cmdline-1.10-amd64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "amd64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.10.1", }, expected: "talos.platform=metal console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512 selinux=1", //nolint:lll }, { name: "cmdline-1.10-arm64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "arm64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.10.1", }, expected: "talos.platform=metal console=ttyAMA0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512 selinux=1", //nolint:lll }, { name: "cmdline-1.11-amd64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "amd64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.11.0", }, expected: "talos.platform=metal console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on selinux=1", //nolint:lll }, { name: "cmdline-1.11-arm64", prof: profile.Profile{ BaseProfileName: "metal", Arch: "arm64", Output: profile.Output{ Kind: profile.OutKindCmdline, OutFormat: profile.OutFormatRaw, }, Version: "1.11.0", }, expected: "talos.platform=metal console=ttyAMA0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on selinux=1", //nolint:lll }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) t.Cleanup(cancel) imgr, err := imager.New(test.prof) require.NoError(t, err) outPath := t.TempDir() outputPath, err := imgr.Execute(ctx, outPath, reporter.New()) require.NoError(t, err) out, err := os.ReadFile(outputPath) require.NoError(t, err) assert.Equal(t, test.expected, string(out)) }) } } ================================================ FILE: pkg/imager/iso/grub.cfg ================================================ set default=0 set timeout=3 insmod all_video terminal_input console terminal_output console menuentry "Talos ISO" { set gfxmode=auto set gfxpayload=text linux /boot/vmlinuz {{ quote .Cmdline }} initrd /boot/initramfs.xz } {{ if .AddResetOption -}} menuentry "Reset Talos installation" { set gfxmode=auto set gfxpayload=text linux /boot/vmlinuz {{ quote .Cmdline }} talos.experimental.wipe=system initrd /boot/initramfs.xz } {{ end -}} ================================================ FILE: pkg/imager/iso/grub.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package iso import ( "bytes" _ "embed" "os" "path/filepath" "text/template" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) //go:embed grub.cfg var grubCfgTemplate string // CreateGRUB creates a GRUB-based ISO image. // // This iso supports both BIOS and UEFI booting. func (options Options) CreateGRUB(printf func(string, ...any)) (Generator, error) { if err := utils.CopyFiles( printf, utils.SourceDestination(options.KernelPath, filepath.Join(options.ScratchDir, "boot", "vmlinuz")), utils.SourceDestination(options.InitramfsPath, filepath.Join(options.ScratchDir, "boot", "initramfs.xz")), ); err != nil { return nil, err } printf("creating grub.cfg") var grubCfg bytes.Buffer tmpl, err := template.New("grub.cfg"). Funcs(template.FuncMap{ "quote": grub.Quote, }). Parse(grubCfgTemplate) if err != nil { return nil, err } if err = tmpl.Execute(&grubCfg, struct { Cmdline string AddResetOption bool }{ Cmdline: options.Cmdline, AddResetOption: quirks.New(options.Version).SupportsResetGRUBOption(), }); err != nil { return nil, err } cfgPath := filepath.Join(options.ScratchDir, "boot/grub/grub.cfg") if err = os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil { return nil, err } if err = os.WriteFile(cfgPath, grubCfg.Bytes(), 0o666); err != nil { return nil, err } if err = utils.TouchFiles(printf, options.ScratchDir); err != nil { return nil, err } printf("creating ISO image") return &ExecutorOptions{ Command: "grub-mkrescue", Version: options.Version, Arguments: []string{ "--compress=xz", "--output=" + options.OutPath, "--verbose", options.ScratchDir, "-iso-level", "3", "--", }, }, nil } ================================================ FILE: pkg/imager/iso/hybrid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package iso import ( "context" "path/filepath" ) // CreateHybrid creates an ISO image that supports both BIOS and UEFI booting. func (options Options) CreateHybrid(ctx context.Context, printf func(string, ...any)) (Generator, error) { if _, err := options.CreateGRUB(printf); err != nil { return nil, err } if _, err := options.CreateUEFI(ctx, printf); err != nil { return nil, err } efiBootImg := filepath.Join(options.ScratchDir, "efiboot.img") return &ExecutorOptions{ Command: "grub-mkrescue", Version: options.Version, Arguments: []string{ "--compress=xz", "--output=" + options.OutPath, "--verbose", "--directory=/usr/lib/grub/i386-pc", // only for BIOS boot "-m", "efiboot.img", // exclude the EFI boot image from the ISO "-iso-level", "3", options.ScratchDir, "-eltorito-alt-boot", "-e", "--interval:appended_partition_2:all::", // use appended partition 2 for EFI "-append_partition", "2", "0xef", efiBootImg, "-appended_part_as_gpt", "-partition_cyl_align", // pad partition to cylinder boundary "all", "-partition_offset", "16", // support booting from USB "-iso_mbr_part_type", "0x83", // just to have more clear info when doing a fdisk -l "-no-emul-boot", "--", }, }, nil } ================================================ FILE: pkg/imager/iso/iso.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package iso contains functions for creating ISO images. package iso import ( "context" "fmt" "os" "strings" "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // VolumeID returns a valid volume ID for the given label. func VolumeID(label string) string { // builds a valid volume ID: 32 chars out of [A-Z0-9_] label = strings.ToUpper(label) label = strings.Map(func(r rune) rune { switch { case r >= 'A' && r <= 'Z': return r case r >= '0' && r <= '9': return r case r == '_' || r == '-' || r == '.': return '_' default: return -1 } }, label) if len(label) > 32 { label = label[:32] } return label } // Label returns an ISO full label for a given version. func Label(version string, secureboot bool) string { label := "Talos-" if secureboot { label += "SB-" } return label + version } // ExecutorOptions defines the iso generation options. type ExecutorOptions struct { Command string Version string Arguments []string } // Generator is an interface for executing the iso generation. type Generator interface { Generate(ctx context.Context) error } // Options describe the input generating different types of ISOs. type Options struct { KernelPath string InitramfsPath string Cmdline string UKIPath string SDBootPath string Arch string Version string // A value in loader.conf secure-boot-enroll: off, manual, if-safe, force. SDBootSecureBootEnrollKeys string // UKISigningCertDer is the DER encoded UKI signing certificate. UKISigningCertDerPath string // optional, for auto-enrolling secureboot keys PlatformKeyPath string KeyExchangeKeyPath string SignatureKeyPath string ScratchDir string OutPath string } // Generate creates an ISO image. func (e *ExecutorOptions) Generate(ctx context.Context) error { if epoch, ok, err := utils.SourceDateEpoch(); err != nil { return err } else if ok { // set EFI FAT image serial number if err := os.Setenv("GRUB_FAT_SERIAL_NUMBER", fmt.Sprintf("%x", uint32(epoch))); err != nil { return err } e.Arguments = append(e.Arguments, "-volume_date", "all_file_dates", fmt.Sprintf("=%d", epoch), "-volume_date", "uuid", time.Unix(epoch, 0).Format("2006010215040500"), ) } if quirks.New(e.Version).SupportsISOLabel() { label := Label(e.Version, false) e.Arguments = append(e.Arguments, "-volid", VolumeID(label), "-volset-id", label, ) } _, err := cmd.RunWithOptions(ctx, e.Command, e.Arguments) if err != nil { return fmt.Errorf("failed to create ISO: %w", err) } return nil } ================================================ FILE: pkg/imager/iso/iso_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package iso_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/imager/iso" ) func TestVolumeID(t *testing.T) { t.Parallel() for _, test := range []struct { in string out string }{ { in: "Talos-v1.7.6", out: "TALOS_V1_7_6", }, { in: "Talos-v1.7.6-beta.0", out: "TALOS_V1_7_6_BETA_0", }, } { t.Run(test.in, func(t *testing.T) { t.Parallel() assert.Equal(t, test.out, iso.VolumeID(test.in)) }) } } ================================================ FILE: pkg/imager/iso/loader.conf.tmpl ================================================ # systemd-boot configuration timeout 10 secure-boot-enroll {{ .SecureBootEnroll }} ================================================ FILE: pkg/imager/iso/uefi.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package iso import ( "bytes" "context" _ "embed" "fmt" "os" "path/filepath" "text/template" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/go-copy/copy" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/makefs" ) const ( // mib is the size of a megabyte. mib = 1024 * 1024 ) //go:embed loader.conf.tmpl var loaderConfigTemplate string // CreateUEFI creates an iso using a UKI, systemd-boot. // // The ISO created supports only booting in UEFI mode, and supports SecureBoot. // //nolint:gocyclo,cyclop func (options Options) CreateUEFI(ctx context.Context, printf func(string, ...any)) (Generator, error) { if err := os.MkdirAll(options.ScratchDir, 0o755); err != nil { return nil, err } printf("preparing raw image") efiBootImg := filepath.Join(options.ScratchDir, "efiboot.img") // initial size isoSize := int64(10 * mib) for _, path := range []string{ options.SDBootPath, options.UKIPath, } { st, err := os.Stat(path) if err != nil { return nil, err } isoSize += (st.Size() + mib - 1) / mib * mib } if err := utils.CreateRawDisk(printf, efiBootImg, isoSize, true); err != nil { return nil, err } printf("preparing loader.conf") var loaderConfigOut bytes.Buffer if err := template.Must(template.New("loader.conf").Parse(loaderConfigTemplate)).Execute(&loaderConfigOut, struct { SecureBootEnroll string }{ SecureBootEnroll: options.SDBootSecureBootEnrollKeys, }); err != nil { return nil, fmt.Errorf("error rendering loader.conf: %w", err) } printf("creating vFAT EFI image") fopts := []makefs.Option{ makefs.WithLabel(constants.EFIPartitionLabel), makefs.WithReproducible(true), } if err := makefs.VFAT(ctx, efiBootImg, fopts...); err != nil { return nil, err } if err := os.MkdirAll(filepath.Join(options.ScratchDir, "EFI/Linux"), 0o755); err != nil { return nil, err } if err := os.MkdirAll(filepath.Join(options.ScratchDir, "EFI/BOOT"), 0o755); err != nil { return nil, err } if err := os.MkdirAll(filepath.Join(options.ScratchDir, "loader"), 0o755); err != nil { return nil, err } efiBootPath := "EFI/BOOT/BOOTX64.EFI" if options.Arch == "arm64" { efiBootPath = "EFI/BOOT/BOOTAA64.EFI" } if err := copy.File(options.SDBootPath, filepath.Join(options.ScratchDir, efiBootPath)); err != nil { return nil, err } if err := copy.File(options.UKIPath, filepath.Join(options.ScratchDir, fmt.Sprintf("EFI/Linux/Talos-%s.efi", options.Version))); err != nil { return nil, err } if err := os.WriteFile(filepath.Join(options.ScratchDir, "loader/loader.conf"), loaderConfigOut.Bytes(), 0o644); err != nil { return nil, err } if options.UKISigningCertDerPath != "" { if err := os.MkdirAll(filepath.Join(options.ScratchDir, "EFI/keys"), 0o755); err != nil { return nil, err } if err := copy.File(options.UKISigningCertDerPath, filepath.Join(options.ScratchDir, "EFI/keys/uki-signing-cert.der")); err != nil { return nil, err } } if options.PlatformKeyPath != "" || options.KeyExchangeKeyPath != "" || options.SignatureKeyPath != "" { if err := os.MkdirAll(filepath.Join(options.ScratchDir, "loader/keys/auto"), 0o755); err != nil { return nil, err } } if options.PlatformKeyPath != "" { if err := copy.File(options.PlatformKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.PlatformKeyAsset)); err != nil { return nil, err } } if options.KeyExchangeKeyPath != "" { if err := copy.File(options.KeyExchangeKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.KeyExchangeKeyAsset)); err != nil { return nil, err } } if options.SignatureKeyPath != "" { if err := copy.File(options.SignatureKeyPath, filepath.Join(options.ScratchDir, "loader/keys/auto", constants.SignatureKeyAsset)); err != nil { return nil, err } } // fixup directory timestamps recursively if err := utils.TouchFiles(printf, options.ScratchDir); err != nil { return nil, err } if _, err := cmd.RunWithOptions( ctx, "mcopy", []string{ "-s", // recursive "-p", // preserve attributes "-Q", // quit on error "-m", // preserve modification time "-i", efiBootImg, filepath.Join(options.ScratchDir, "EFI"), filepath.Join(options.ScratchDir, "loader"), "::", }, ); err != nil { return nil, err } printf("creating ISO image") return &ExecutorOptions{ Command: "xorrisofs", Version: options.Version, Arguments: []string{ "-e", "--interval:appended_partition_2:all::", // use appended partition 2 for EFI "-append_partition", "2", "0xef", efiBootImg, "-partition_cyl_align", // pad partition to cylinder boundary "all", "-partition_offset", "16", // support booting from USB "-iso_mbr_part_type", "0x83", // just to have more clear info when doing a fdisk -l "-no-emul-boot", "-m", "efiboot.img", // exclude the EFI boot image from the ISO "-iso-level", "3", "-o", options.OutPath, options.ScratchDir, "--", }, }, nil } ================================================ FILE: pkg/imager/out.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager import ( "context" "encoding/pem" "fmt" "os" "path/filepath" "slices" "strings" "time" "github.com/dustin/go-humanize" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/empty" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/tarball" "github.com/google/go-containerregistry/pkg/v1/types" "github.com/siderolabs/go-pointer" "github.com/siderolabs/go-procfs/procfs" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/cmd/installer/pkg/install" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options" "github.com/siderolabs/talos/internal/pkg/secureboot/database" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" "github.com/siderolabs/talos/pkg/imager/filemap" "github.com/siderolabs/talos/pkg/imager/iso" "github.com/siderolabs/talos/pkg/imager/ova" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/imager/qemuimg" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/meta" "github.com/siderolabs/talos/pkg/reporter" ) func (i *Imager) outInitramfs(path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "copying initramfs...", Status: reporter.StatusRunning}) if err := utils.CopyFiles(printf, utils.SourceDestination(i.initramfsPath, path)); err != nil { return err } report.Report(reporter.Update{Message: "initramfs output ready", Status: reporter.StatusSucceeded}) return nil } func (i *Imager) outKernel(path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "copying kernel...", Status: reporter.StatusRunning}) if err := utils.CopyFiles(printf, utils.SourceDestination(i.prof.Input.Kernel.Path, path)); err != nil { return err } report.Report(reporter.Update{Message: "kernel output ready", Status: reporter.StatusSucceeded}) return nil } func (i *Imager) outUKI(path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "copying kernel...", Status: reporter.StatusRunning}) if err := utils.CopyFiles(printf, utils.SourceDestination(i.ukiPath, path)); err != nil { return err } report.Report(reporter.Update{Message: "UKI output ready", Status: reporter.StatusSucceeded}) return nil } func (i *Imager) outCmdline(path string) error { return os.WriteFile(path, []byte(i.cmdline), 0o644) } //nolint:gocyclo,cyclop func (i *Imager) outISO(ctx context.Context, path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "building ISO...", Status: reporter.StatusRunning}) scratchSpace := filepath.Join(i.tempDir, "iso") var ( err error zeroContainerAsset profile.ContainerAsset ) if i.prof.Input.ImageCache != zeroContainerAsset { if err := os.MkdirAll(filepath.Join(scratchSpace, "imagecache"), 0o755); err != nil { return err } if err := i.prof.Input.ImageCache.Extract(ctx, filepath.Join(scratchSpace, "imagecache"), i.prof.Arch, printf); err != nil { return err } } var generator iso.Generator switch { case i.prof.SecureBootEnabled(): isoOptions := pointer.SafeDeref(i.prof.Output.ISOOptions) var signer pesign.CertificateSigner signer, err = i.prof.Input.SecureBoot.SecureBootSigner.GetSigner(ctx) if err != nil { return fmt.Errorf("failed to get SecureBoot signer: %w", err) } derCrtPath := filepath.Join(i.tempDir, "uki.der") if err = os.WriteFile(derCrtPath, signer.Certificate().Raw, 0o600); err != nil { return fmt.Errorf("failed to write uki.der: %w", err) } options := iso.Options{ UKIPath: i.ukiPath, SDBootPath: i.sdBootPath, SDBootSecureBootEnrollKeys: isoOptions.SDBootEnrollKeys.String(), UKISigningCertDerPath: derCrtPath, PlatformKeyPath: i.prof.Input.SecureBoot.PlatformKeyPath, KeyExchangeKeyPath: i.prof.Input.SecureBoot.KeyExchangeKeyPath, SignatureKeyPath: i.prof.Input.SecureBoot.SignatureKeyPath, Arch: i.prof.Arch, Version: i.prof.Version, ScratchDir: scratchSpace, OutPath: path, } if i.prof.Input.SecureBoot.PlatformKeyPath == "" { report.Report(reporter.Update{Message: "generating SecureBoot database...", Status: reporter.StatusRunning}) // generate the database automatically from provided values enrolledPEM := pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: signer.Certificate().Raw, }) var entries []database.Entry entries, err = database.Generate(enrolledPEM, signer, database.IncludeWellKnownCertificates(i.prof.Input.SecureBoot.IncludeWellKnownCerts)) if err != nil { return fmt.Errorf("failed to generate database: %w", err) } for _, entry := range entries { entryPath := filepath.Join(i.tempDir, entry.Name) if err = os.WriteFile(entryPath, entry.Contents, 0o600); err != nil { return err } switch entry.Name { case constants.PlatformKeyAsset: options.PlatformKeyPath = entryPath case constants.KeyExchangeKeyAsset: options.KeyExchangeKeyPath = entryPath case constants.SignatureKeyAsset: options.SignatureKeyPath = entryPath default: return fmt.Errorf("unknown database entry: %s", entry.Name) } } } else { options.PlatformKeyPath = i.prof.Input.SecureBoot.PlatformKeyPath options.KeyExchangeKeyPath = i.prof.Input.SecureBoot.KeyExchangeKeyPath options.SignatureKeyPath = i.prof.Input.SecureBoot.SignatureKeyPath } generator, err = options.CreateUEFI(ctx, printf) if err != nil { return err } case quirks.New(i.prof.Version).ISOSupportsSettingBootloader(): options := iso.Options{ KernelPath: i.prof.Input.Kernel.Path, InitramfsPath: i.initramfsPath, Cmdline: i.cmdline, UKIPath: i.ukiPath, SDBootPath: i.sdBootPath, SDBootSecureBootEnrollKeys: "off", Arch: i.prof.Arch, Version: i.prof.Version, ScratchDir: scratchSpace, OutPath: path, } switch i.prof.Output.ISOOptions.Bootloader { case profile.BootLoaderKindDualBoot: generator, err = options.CreateHybrid(ctx, printf) if err != nil { return err } case profile.BootLoaderKindSDBoot: generator, err = options.CreateUEFI(ctx, printf) if err != nil { return err } case profile.BootLoaderKindGrub: generator, err = options.CreateGRUB(printf) if err != nil { return err } case profile.BootLoaderKindNone: return fmt.Errorf("cannot create ISO with no bootloader") } case quirks.New(i.prof.Version).UseSDBootForUEFI(): options := iso.Options{ KernelPath: i.prof.Input.Kernel.Path, InitramfsPath: i.initramfsPath, Cmdline: i.cmdline, UKIPath: i.ukiPath, SDBootPath: i.sdBootPath, SDBootSecureBootEnrollKeys: "off", Arch: i.prof.Arch, Version: i.prof.Version, ScratchDir: scratchSpace, OutPath: path, } generator, err = options.CreateHybrid(ctx, printf) if err != nil { return err } default: options := iso.Options{ KernelPath: i.prof.Input.Kernel.Path, InitramfsPath: i.initramfsPath, Cmdline: i.cmdline, Arch: i.prof.Arch, Version: i.prof.Version, ScratchDir: scratchSpace, OutPath: path, } generator, err = options.CreateGRUB(printf) if err != nil { return err } } if err := generator.Generate(ctx); err != nil { return err } report.Report(reporter.Update{Message: "ISO ready", Status: reporter.StatusSucceeded}) return nil } func (i *Imager) outImage(ctx context.Context, path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "creating disk image...", Status: reporter.StatusRunning}) if err := i.buildImage(ctx, path, printf); err != nil { return err } switch i.prof.Output.ImageOptions.DiskFormat { case profile.DiskFormatRaw: // nothing to do case profile.DiskFormatQCOW2: if err := qemuimg.Convert(ctx, "raw", "qcow2", i.prof.Output.ImageOptions.DiskFormatOptions, path, printf); err != nil { return err } case profile.DiskFormatVPC: if err := qemuimg.Convert(ctx, "raw", "vpc", i.prof.Output.ImageOptions.DiskFormatOptions, path, printf); err != nil { return err } case profile.DiskFormatOVA: scratchPath := filepath.Join(i.tempDir, "ova") if err := ova.CreateOVAFromRAW(ctx, path, i.prof.Arch, scratchPath, i.prof.Output.ImageOptions.DiskSize, printf); err != nil { return err } case profile.DiskFormatUnknown: fallthrough default: return fmt.Errorf("unsupported disk format: %s", i.prof.Output.ImageOptions.DiskFormat) } report.Report(reporter.Update{Message: "disk image ready", Status: reporter.StatusSucceeded}) return nil } //nolint:gocyclo func (i *Imager) buildImage(ctx context.Context, path string, printf func(string, ...any)) error { var zeroContainerAsset profile.ContainerAsset cmdline := procfs.NewCmdline(i.cmdline) scratchSpace := filepath.Join(i.tempDir, "image") metaContents := slices.Clone(i.prof.Customization.MetaContents) if i.prof.Arch == "amd64" && !i.prof.SecureBootEnabled() && quirks.New(i.prof.Version).UseSDBootForUEFI() { // allow overriding the bootloader if provided if i.prof.Output.ImageOptions.Bootloader == profile.BootLoaderKindDualBoot { metaContents = append(metaContents, meta.Value{ Key: meta.DiskImageBootloader, Value: profile.BootLoaderKindDualBoot.String(), }) } } opts := &install.Options{ DiskPath: path, Platform: i.prof.Platform, Arch: i.prof.Arch, MetaValues: install.FromMeta(metaContents), ImageSecureboot: i.prof.SecureBootEnabled(), DiskImageBootloader: i.prof.Output.ImageOptions.Bootloader.String(), Version: i.prof.Version, BootAssets: options.BootAssets{ KernelPath: i.prof.Input.Kernel.Path, InitramfsPath: i.initramfsPath, UKIPath: i.ukiPath, SDBootPath: i.sdBootPath, }, MountPrefix: scratchSpace, Printf: printf, } if i.overlayInstaller != nil { opts.OverlayInstaller = i.overlayInstaller opts.ExtraOptions = i.prof.Overlay.ExtraOptions opts.OverlayExtractedDir = i.tempDir } if i.prof.Input.ImageCache != zeroContainerAsset { imageCacheDir := filepath.Join(i.tempDir, "imagecache") if err := os.MkdirAll(imageCacheDir, 0o755); err != nil { return err } if err := i.prof.Input.ImageCache.Extract(ctx, imageCacheDir, i.prof.Arch, printf); err != nil { return err } opts.ImageCachePath = imageCacheDir imageCacheSize, err := calculateDirectorySizeWithOverhead(imageCacheDir, 20) if err != nil { return fmt.Errorf("failed to calculate image cache size: %w", err) } // Align to a 1MiB boundary so both 512 and 4K sector sizes are covered. // miBMinusOne is a bit mask with all low bits set for a 1MiB boundary // (for 1MiB this is 0xFFFFF). Adding this mask and then clearing those // low bits with &^ effectively rounds imageCacheSize up to the next // multiple of 1MiB, while leaving already aligned sizes unchanged. miBMinusOne := int64(1024*1024) - 1 imageCacheSize = (imageCacheSize + miBMinusOne) &^ miBMinusOne opts.ImageCacheSize = imageCacheSize printf("updating image size to accommodate image cache: +%s", humanize.Bytes(uint64(imageCacheSize))) i.prof.Output.ImageOptions.DiskSize += imageCacheSize } if err := utils.CreateRawDisk(printf, path, i.prof.Output.ImageOptions.DiskSize, false); err != nil { return err } installer, err := install.NewInstaller(ctx, cmdline, install.ModeImage, opts) if err != nil { return fmt.Errorf("failed to create installer: %w", err) } if err := installer.Install(ctx, install.ModeImage); err != nil { return fmt.Errorf("failed to install: %w", err) } return nil } func calculateDirectorySizeWithOverhead(dir string, overheadPercentage int) (int64, error) { var totalSize int64 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !info.IsDir() { totalSize += info.Size() } return nil }) if err != nil { return 0, err } overhead := (totalSize * int64(overheadPercentage)) / 100 totalSize += overhead return totalSize, nil } //nolint:gocyclo,cyclop func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "building installer...", Status: reporter.StatusRunning}) baseInstallerImg, err := i.prof.Input.BaseInstaller.Pull(ctx, i.prof.Arch, printf) if err != nil { return err } baseLayers, err := baseInstallerImg.Layers() if err != nil { return fmt.Errorf("failed to get layers: %w", err) } configFile, err := baseInstallerImg.ConfigFile() if err != nil { return fmt.Errorf("failed to get config file: %w", err) } config := *configFile.Config.DeepCopy() printf("creating empty image") newInstallerImg := mutate.MediaType(empty.Image, types.OCIManifestSchema1) newInstallerImg = mutate.ConfigMediaType(newInstallerImg, types.OCIConfigJSON) // `empty.Image` won't error, so no need to check newCfgFile, _ := empty.Image.ConfigFile() //nolint:errcheck newCfgFile.Architecture = i.prof.Arch newCfgFile.OS = "linux" newInstallerImg, err = mutate.ConfigFile(newInstallerImg, newCfgFile) if err != nil { return fmt.Errorf("failed to set image architecture: %w", err) } newInstallerImg, err = mutate.Config(newInstallerImg, config) if err != nil { return fmt.Errorf("failed to set config: %w", err) } newInstallerImg, err = mutate.CreatedAt(newInstallerImg, v1.Time{Time: time.Now()}) if err != nil { return fmt.Errorf("failed to set created at: %w", err) } // Talos v1.5+ optimizes the install layers to be easily replaceable with new artifacts // other Talos versions will have an overhead of artifacts being stored twice if len(baseLayers) == 2 { // optimized for installer image for artifacts replacements baseLayers = baseLayers[:1] } newInstallerImg, err = mutate.AppendLayers(newInstallerImg, baseLayers...) if err != nil { return fmt.Errorf("failed to append layers: %w", err) } var artifacts []filemap.File printf("generating artifacts layer") ukiPath := strings.TrimLeft(fmt.Sprintf(constants.UKIAssetPath, i.prof.Arch), "/") quirks := quirks.New(i.prof.Version) if i.prof.SecureBootEnabled() && !quirks.UseSDBootForUEFI() { ukiPath += ".signed" // support for older secureboot installers } if quirks.UseSDBootForUEFI() || i.prof.SecureBootEnabled() { artifacts = append(artifacts, filemap.File{ ImagePath: strings.TrimLeft(fmt.Sprintf(constants.SDBootAssetPath, i.prof.Arch), "/"), SourcePath: i.sdBootPath, }, filemap.File{ ImagePath: ukiPath, SourcePath: i.ukiPath, }, ) } else { artifacts = append(artifacts, filemap.File{ ImagePath: strings.TrimLeft(fmt.Sprintf(constants.KernelAssetPath, i.prof.Arch), "/"), SourcePath: i.prof.Input.Kernel.Path, }, filemap.File{ ImagePath: strings.TrimLeft(fmt.Sprintf(constants.InitramfsAssetPath, i.prof.Arch), "/"), SourcePath: i.initramfsPath, }, ) } artifactsLayer, err := filemap.Layer(artifacts) if err != nil { return fmt.Errorf("failed to create artifacts layer: %w", err) } newInstallerImg, err = mutate.AppendLayers(newInstallerImg, artifactsLayer) if err != nil { return fmt.Errorf("failed to append artifacts layer: %w", err) } if i.overlayInstaller != nil { tempOverlayPath := filepath.Join(i.tempDir, "overlay-installer", constants.ImagerOverlayBasePath) if err := os.MkdirAll(tempOverlayPath, 0o755); err != nil { return fmt.Errorf("failed to create overlay directory: %w", err) } if err := i.prof.Input.OverlayInstaller.Extract( ctx, tempOverlayPath, i.prof.Arch, progressPrintf(report, reporter.Update{Message: "pulling overlay for installer...", Status: reporter.StatusRunning}), ); err != nil { return err } extraOpts, internalErr := yaml.Marshal(i.prof.Overlay.ExtraOptions) if internalErr != nil { return fmt.Errorf("failed to marshal extra options: %w", internalErr) } if internalErr = os.WriteFile(filepath.Join(i.tempDir, constants.ImagerOverlayExtraOptionsPath), extraOpts, 0o644); internalErr != nil { return fmt.Errorf("failed to write extra options yaml: %w", internalErr) } printf("generating overlay installer layer") var overlayArtifacts []filemap.File for _, extraArtifact := range []struct { sourcePath string imagePath string }{ { sourcePath: filepath.Join(i.tempDir, "overlay-installer", constants.ImagerOverlayArtifactsPath), imagePath: strings.TrimLeft(constants.ImagerOverlayArtifactsPath, "/"), }, { sourcePath: filepath.Join(i.tempDir, "overlay-installer", constants.ImagerOverlayInstallersPath, i.prof.Overlay.Name), imagePath: strings.TrimLeft(constants.ImagerOverlayInstallerDefaultPath, "/"), }, { sourcePath: filepath.Join(i.tempDir, constants.ImagerOverlayExtraOptionsPath), imagePath: strings.TrimLeft(constants.ImagerOverlayExtraOptionsPath, "/"), }, } { var extraFiles []filemap.File extraFiles, err = filemap.Walk(extraArtifact.sourcePath, extraArtifact.imagePath) if err != nil { return fmt.Errorf("failed to walk extra artifact %s: %w", extraArtifact.sourcePath, err) } overlayArtifacts = append(overlayArtifacts, extraFiles...) } overlayArtifactsLayer, internalErr := filemap.Layer(overlayArtifacts) if internalErr != nil { return fmt.Errorf("failed to create overlay artifacts layer: %w", internalErr) } newInstallerImg, internalErr = mutate.AppendLayers(newInstallerImg, overlayArtifactsLayer) if internalErr != nil { return fmt.Errorf("failed to append overlay artifacts layer: %w", internalErr) } } ref, err := name.ParseReference(i.prof.Input.BaseInstaller.ImageRef) if err != nil { return fmt.Errorf("failed to parse image reference: %w", err) } printf("writing image tarball") if err := tarball.WriteToFile(path, ref, newInstallerImg); err != nil { return fmt.Errorf("failed to write image tarball: %w", err) } report.Report(reporter.Update{Message: "installer container image ready", Status: reporter.StatusSucceeded}) return nil } ================================================ FILE: pkg/imager/ova/ova.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ova implements OVA creation. package ova import ( "bytes" "context" "crypto/sha256" "encoding/hex" "io" "os" "path/filepath" "strings" "text/template" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/imager/vmdkconvert" ) const mfTpl = `SHA256({{ .VMDK }})= {{ .VMDKSHA }} SHA256({{ .OVF }})= {{ .OVFSHA }} ` // OVF format reference: https://www.dmtf.org/standards/ovf. // //nolint:lll const ovfTpl = ` Virtual disk information The list of logical networks The VM Network network A virtual machine talos Talos Virtual Appliance Inline Talos config The kind of installed guest operating system Virtual hardware requirements Virtual Hardware Family 0 talos vmx-15 hertz * 10^6 Number of Virtual CPUs 2 virtual CPU(s) 1 3 2 byte * 2^20 Memory Size 2048MB of memory 2 4 2048 0 SCSI Controller scsiController0 3 VirtualSCSI 6 0 disk0 ovf:/disk/vmdisk1 4 3 17 2 true VM Network VmxNet3 ethernet adapter on "VM Network" ethernet0 5 VmxNet3 10 false video 6 24 false vmci 7 vmware.vmci 1 ` // CreateOVAFromRAW creates an OVA from a RAW disk. // //nolint:gocyclo func CreateOVAFromRAW(ctx context.Context, outPath, arch, scratchPath string, diskSize int64, printf func(string, ...any)) error { if err := os.MkdirAll(scratchPath, 0o755); err != nil { return err } vmdkPath := filepath.Join(scratchPath, "disk.vmdk") if err := utils.CopyFiles(printf, utils.SourceDestination(outPath, vmdkPath)); err != nil { return err } if err := vmdkconvert.ConvertToStreamOptimizedVMDK(ctx, vmdkPath, printf); err != nil { return err } f, err := os.Stat(vmdkPath) if err != nil { return err } imageSize := f.Size() ovf, err := renderOVF(imageSize, diskSize) if err != nil { return err } input, err := os.Open(vmdkPath) if err != nil { return err } defer input.Close() //nolint:errcheck vmdkSHA25Sum, err := sha256sum(input) if err != nil { return err } ovfSHA25Sum, err := sha256sum(strings.NewReader(ovf)) if err != nil { return err } mf, err := renderMF(vmdkSHA25Sum, ovfSHA25Sum) if err != nil { return err } if err = os.WriteFile(filepath.Join(scratchPath, "disk.mf"), []byte(mf), 0o666); err != nil { return err } if err = os.WriteFile(filepath.Join(scratchPath, "disk.ovf"), []byte(ovf), 0o666); err != nil { return err } if _, err = cmd.RunWithOptions(ctx, "tar", []string{"-cvf", outPath, "-C", scratchPath, "disk.ovf", "disk.mf", "disk.vmdk"}); err != nil { return err } return nil } func sha256sum(input io.Reader) (string, error) { hash := sha256.New() if _, err := io.Copy(hash, input); err != nil { return "", err } sum := hash.Sum(nil) return hex.EncodeToString(sum), nil } func renderMF(vmdkSHA25Sum, ovfSHA25Sum string) (string, error) { cfg := struct { VMDK string VMDKSHA string OVF string OVFSHA string }{ VMDK: "disk.vmdk", VMDKSHA: vmdkSHA25Sum, OVF: "disk.ovf", OVFSHA: ovfSHA25Sum, } templ := template.Must(template.New("mf").Parse(mfTpl)) var buf bytes.Buffer if err := templ.Execute(&buf, cfg); err != nil { return "", err } return buf.String(), nil } func renderOVF(imageSize, diskSize int64) (string, error) { cfg := struct { VMDK string Size int64 Capacity int64 }{ VMDK: "disk.vmdk", Size: imageSize, Capacity: diskSize / (1 << 20), } templ := template.Must(template.New("ovf").Parse(ovfTpl)) var buf bytes.Buffer if err := templ.Execute(&buf, cfg); err != nil { return "", err } return buf.String(), nil } ================================================ FILE: pkg/imager/overlay/executor/executor.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package executor implements overlay.Installer package executor import ( "bytes" "context" "fmt" "io" "os/exec" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/overlay" ) var _ overlay.Installer[overlay.ExtraOptions] = (*Options)(nil) // Options executor options. type Options struct { commandPath string } // New returns a new overlay installer executor. func New(commandPath string) *Options { return &Options{ commandPath: commandPath, } } // GetOptions returns the options for the overlay installer. func (o *Options) GetOptions(ctx context.Context, extra overlay.ExtraOptions) (overlay.Options, error) { // parse extra as yaml extraYAML, err := yaml.Marshal(extra) if err != nil { return overlay.Options{}, fmt.Errorf("failed to marshal extra: %w", err) } out, err := o.execute(ctx, bytes.NewReader(extraYAML), "get-options") if err != nil { return overlay.Options{}, err } var options overlay.Options if err := yaml.Unmarshal(out, &options); err != nil { return overlay.Options{}, fmt.Errorf("failed to unmarshal overlay options: %w", err) } return options, nil } // Install installs the overlay. func (o *Options) Install(ctx context.Context, options overlay.InstallOptions[overlay.ExtraOptions]) error { optionsBytes, err := yaml.Marshal(&options) if err != nil { return fmt.Errorf("failed to marshal options: %w", err) } if _, err := o.execute(ctx, bytes.NewReader(optionsBytes), "install"); err != nil { return err } return nil } func (o *Options) execute(ctx context.Context, stdin io.Reader, args ...string) ([]byte, error) { cmd := exec.CommandContext(ctx, o.commandPath, args...) cmd.Stdin = stdin var stdOut, stdErr bytes.Buffer cmd.Stdout = &stdOut cmd.Stderr = &stdErr if err := cmd.Run(); err != nil { return nil, fmt.Errorf("failed to run overlay installer: %w, stdErr: %s", err, stdErr.Bytes()) } return stdOut.Bytes(), nil } ================================================ FILE: pkg/imager/post.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager import ( "context" "fmt" "os" "os/exec" "path/filepath" "strconv" "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/reporter" ) //nolint:gocyclo func (i *Imager) postProcessTar(ctx context.Context, filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "processing .tar.gz", Status: reporter.StatusRunning}) dir := filepath.Dir(filename) src := "disk.raw" if err := os.Rename(filename, filepath.Join(dir, src)); err != nil { return "", err } outPath := filename + ".tar.gz" pipeR, pipeW, err := os.Pipe() if err != nil { return "", err } timestamp, ok, err := utils.SourceDateEpoch() if err != nil { return "", fmt.Errorf("failed to get SOURCE_DATE_EPOCH: %w", err) } if !ok { timestamp = time.Now().Unix() } cmd1 := exec.CommandContext( ctx, "tar", "-cvf", "-", "-C", dir, "--sparse", "--sort=name", "--owner=0", "--group=0", "--numeric-owner", "--mtime=@"+strconv.FormatInt(timestamp, 10), src, ) cmd1.Stdout = pipeW cmd1.Stderr = os.Stderr if err := cmd1.Start(); err != nil { return "", err } if err = pipeW.Close(); err != nil { return "", err } destination, err := os.OpenFile(outPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) if err != nil { return "", err } defer destination.Close() //nolint:errcheck cmd2 := exec.CommandContext(ctx, "pigz", "-6", "-f", "--no-time", "-") cmd2.Stdin = pipeR cmd2.Stdout = destination cmd2.Stderr = os.Stderr if err := cmd2.Start(); err != nil { return "", err } if err = pipeR.Close(); err != nil { return "", err } errCh := make(chan error, 1) go func() { errCh <- cmd1.Wait() }() go func() { errCh <- cmd2.Wait() }() for range 2 { if err = <-errCh; err != nil { return "", err } } if err := destination.Sync(); err != nil { return "", err } if err := os.Remove(filepath.Join(dir, src)); err != nil { return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("archive is ready: %s", outPath), Status: reporter.StatusSucceeded}) return outPath, nil } func (i *Imager) postProcessGz(ctx context.Context, filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "compressing .gz", Status: reporter.StatusRunning}) if _, err := cmd.RunWithOptions(ctx, "pigz", []string{"-6", "--no-time", "-f", filename}); err != nil { return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.gz", filename), Status: reporter.StatusSucceeded}) return filename + ".gz", nil } func (i *Imager) postProcessXz(ctx context.Context, filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "compressing .xz", Status: reporter.StatusRunning}) if _, err := cmd.RunWithOptions(ctx, "xz", []string{"-0", "-f", "-T", "0", filename}); err != nil { return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.xz", filename), Status: reporter.StatusSucceeded}) return filename + ".xz", nil } func (i *Imager) postProcessZstd(ctx context.Context, filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "compressing .zst", Status: reporter.StatusRunning}) out := filename + ".zst" if _, err := cmd.RunWithOptions(ctx, "zstd", []string{"-T0", "--rm", "-18", "--quiet", "--force", "-o", out, filename}); err != nil { return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s", out), Status: reporter.StatusSucceeded}) return filename + ".zst", nil } ================================================ FILE: pkg/imager/profile/customization.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile import ( "github.com/siderolabs/talos/pkg/machinery/meta" ) // CustomizationProfile describes customizations that can be applied to the image. type CustomizationProfile struct { // ExtraKernelArgs is a list of extra kernel arguments. ExtraKernelArgs []string `yaml:"extraKernelArgs,omitempty"` // MetaContents is a list of META partition contents. MetaContents meta.Values `yaml:"metaContents,omitempty"` // EmbeddedMachineConfiguration is the machine configuration to embed into the image. EmbeddedMachineConfiguration string `yaml:"embeddedMachineConfiguration,omitempty"` } ================================================ FILE: pkg/imager/profile/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type Profile -type SecureBootAssets -header-file ../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package profile import ( "github.com/siderolabs/talos/pkg/machinery/meta" ) // DeepCopy generates a deep copy of Profile. func (o Profile) DeepCopy() Profile { var cp Profile = o if o.SecureBoot != nil { cp.SecureBoot = new(bool) *cp.SecureBoot = *o.SecureBoot } if o.Customization.ExtraKernelArgs != nil { cp.Customization.ExtraKernelArgs = make([]string, len(o.Customization.ExtraKernelArgs)) copy(cp.Customization.ExtraKernelArgs, o.Customization.ExtraKernelArgs) } if o.Customization.MetaContents != nil { cp.Customization.MetaContents = make([]meta.Value, len(o.Customization.MetaContents)) copy(cp.Customization.MetaContents, o.Customization.MetaContents) } if o.Input.SecureBoot != nil { retV := o.Input.SecureBoot.DeepCopy() cp.Input.SecureBoot = &retV } if o.Input.SystemExtensions != nil { cp.Input.SystemExtensions = make([]ContainerAsset, len(o.Input.SystemExtensions)) copy(cp.Input.SystemExtensions, o.Input.SystemExtensions) } if o.Overlay != nil { cp.Overlay = new(OverlayOptions) *cp.Overlay = *o.Overlay if o.Overlay.ExtraOptions != nil { cp.Overlay.ExtraOptions = make(map[string]any, len(o.Overlay.ExtraOptions)) for k4, v4 := range o.Overlay.ExtraOptions { cp.Overlay.ExtraOptions[k4] = v4 } } } if o.Output.ImageOptions != nil { cp.Output.ImageOptions = new(ImageOptions) *cp.Output.ImageOptions = *o.Output.ImageOptions } if o.Output.ISOOptions != nil { cp.Output.ISOOptions = new(ISOOptions) *cp.Output.ISOOptions = *o.Output.ISOOptions } return cp } // DeepCopy generates a deep copy of SecureBootAssets. func (o SecureBootAssets) DeepCopy() SecureBootAssets { var cp SecureBootAssets = o return cp } ================================================ FILE: pkg/imager/profile/default.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile import ( "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( mib = 1024 * 1024 // MinRAWDiskSize is the minimum size disk we can create. Used for metal images. MinRAWDiskSize = 1246 * mib // DefaultRAWDiskSize is the value we use for any non-metal images by default. DefaultRAWDiskSize = 8192 * mib ) // Default describes built-in profiles. var Default = map[string]Profile{ // ISO "iso": { Platform: constants.PlatformMetal, SecureBoot: new(false), Output: Output{ Kind: OutKindISO, OutFormat: OutFormatRaw, }, }, "secureboot-iso": { Platform: constants.PlatformMetal, SecureBoot: new(true), Output: Output{ Kind: OutKindISO, OutFormat: OutFormatRaw, ISOOptions: &ISOOptions{ SDBootEnrollKeys: SDBootEnrollKeysIfSafe, }, }, }, // Metal images "metal": { Platform: constants.PlatformMetal, SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "metal-uki": { Platform: constants.PlatformMetal, SecureBoot: new(false), Output: Output{ Kind: OutKindUKI, OutFormat: OutFormatRaw, }, }, "secureboot-metal-uki": { Platform: constants.PlatformMetal, SecureBoot: new(true), Output: Output{ Kind: OutKindUKI, OutFormat: OutFormatRaw, }, }, "secureboot-metal": { Platform: constants.PlatformMetal, SecureBoot: new(true), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "installer": { Platform: "metal", SecureBoot: new(false), Output: Output{ Kind: OutKindInstaller, OutFormat: OutFormatRaw, }, }, "secureboot-installer": { Platform: "metal", SecureBoot: new(true), Output: Output{ Kind: OutKindInstaller, OutFormat: OutFormatRaw, }, }, // Clouds "akamai": { Platform: "akamai", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatGZ, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "aws": { Platform: "aws", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "azure": { Platform: "azure", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatVPC, DiskFormatOptions: "subformat=fixed,force_size", }, }, }, "cloudstack": { Platform: "cloudstack", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "digital-ocean": { Platform: "digital-ocean", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatGZ, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "exoscale": { Platform: "exoscale", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: 10 * 1024 * mib, DiskFormat: DiskFormatQCOW2, DiskFormatOptions: "cluster_size=8k", }, }, }, "gcp": { Platform: "gcp", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatTar, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "hcloud": { Platform: "hcloud", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "nocloud": { Platform: "nocloud", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "opennebula": { Platform: "opennebula", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "openstack": { Platform: "openstack", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "oracle": { Platform: "oracle", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatQCOW2, DiskFormatOptions: "cluster_size=8k", }, }, }, "scaleway": { Platform: "scaleway", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: MinRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "upcloud": { Platform: "upcloud", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, "vmware": { Platform: "vmware", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatRaw, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatOVA, }, }, }, "vultr": { Platform: "vultr", SecureBoot: new(false), Output: Output{ Kind: OutKindImage, OutFormat: OutFormatZSTD, ImageOptions: &ImageOptions{ DiskSize: DefaultRAWDiskSize, DiskFormat: DiskFormatRaw, }, }, }, } ================================================ FILE: pkg/imager/profile/input.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile import ( "context" "errors" "fmt" "io" "os" "path/filepath" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/authn/github" "github.com/google/go-containerregistry/pkg/crane" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/google" "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/siderolabs/gen/value" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/internal/pkg/measure" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" "github.com/siderolabs/talos/pkg/archiver" "github.com/siderolabs/talos/pkg/imager/profile/internal/signer/aws" "github.com/siderolabs/talos/pkg/imager/profile/internal/signer/azure" "github.com/siderolabs/talos/pkg/imager/profile/internal/signer/file" "github.com/siderolabs/talos/pkg/images" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) const ( arm64 = "arm64" amd64 = "amd64" ) // Input describes inputs for image generation. type Input struct { // Kernel is a vmlinuz file. Kernel FileAsset `yaml:"kernel"` // Initramfs is a initramfs file (without system extensions). Initramfs FileAsset `yaml:"initramfs"` // SDStub is a sd-stub file (only for SecureBoot). SDStub FileAsset `yaml:"sdStub,omitempty"` // SDBoot is a sd-boot file (only for SecureBoot). SDBoot FileAsset `yaml:"sdBoot,omitempty"` // Base installer image to mutate. BaseInstaller ContainerAsset `yaml:"baseInstaller,omitempty"` // ImageCache is an image cache to inject into the asset. ImageCache ContainerAsset `yaml:"imageCache,omitempty"` // OverlayInstaller is an overlay image to inject into the installer. // // OverlayInstaller architecture should match the output installer architecture. OverlayInstaller ContainerAsset `yaml:"overlayInstaller,omitempty"` // SecureBoot is a section with secureboot keys, only for SecureBoot enabled builds. SecureBoot *SecureBootAssets `yaml:"secureboot,omitempty"` // SystemExtensions is a list of system extensions to install. SystemExtensions []ContainerAsset `yaml:"systemExtensions,omitempty"` } // FileAsset describes a file asset. type FileAsset struct { // Path to the file. Path string `yaml:"path"` } // ContainerAsset describes a container asset. type ContainerAsset struct { // ImageRef is a reference to the container image. ImageRef string `yaml:"imageRef"` // ForceInsecure forces insecure registry communication. ForceInsecure bool `yaml:"forceInsecure,omitempty"` // TarballPath is a path to the .tar format container image contents. // // If TarballPath is set, ImageRef is ignored. TarballPath string `yaml:"tarballPath,omitempty"` // OCIPath is a path to the OCI format container image contents. // // If OCIPath is set, ImageRef is ignored. OCIPath string `yaml:"ociPath,omitempty"` } // SecureBootAssets describes secureboot assets. type SecureBootAssets struct { // SecureBoot signing key & cert. SecureBootSigner SigningKeyAndCertificate `yaml:"secureBootSigner"` // PCR signing key. PCRSigner SigningKey `yaml:"pcrSigner"` // Optional, auto-enrollment paths. PlatformKeyPath string `yaml:"platformKeyPath,omitempty"` KeyExchangeKeyPath string `yaml:"keyExchangeKeyPath,omitempty"` SignatureKeyPath string `yaml:"signatureKeyPath,omitempty"` // Optional, auto-enrollment include well-known UEFI (Microsoft) certs. IncludeWellKnownCerts bool `yaml:"includeWellKnownCerts,omitempty"` } // SigningKeyAndCertificate describes a signing key & certificate. type SigningKeyAndCertificate struct { // File-based. // // Static key and certificate paths. KeyPath string `yaml:"keyPath,omitempty"` CertPath string `yaml:"certPath,omitempty"` // Azure. // // Azure Vault URL and certificate ID, key will be found from the certificate. AzureVaultURL string `yaml:"azureVaultURL,omitempty"` AzureCertificateID string `yaml:"azureCertificateID,omitempty"` // AWS. // // AWS KMS Key ID, ACM certificate ARN, and region. // Support local cert file for legacy use cases. AwsKMSKeyID string `yaml:"awsKMSKeyID,omitempty"` AwsRegion string `yaml:"awsRegion,omitempty"` AwsCertPath string `yaml:"awsCertPath,omitempty"` AwsCertARN string `yaml:"awsCertARN,omitempty"` } // SigningKey describes a signing key. type SigningKey struct { // File-based. // // Static key path. KeyPath string `yaml:"keyPath,omitempty"` // Azure. // // Azure Vault URL and key ID. // AzureKeyVersion might be left empty to use the latest key version. AzureVaultURL string `yaml:"azureVaultURL,omitempty"` AzureKeyID string `yaml:"azureKeyID,omitempty"` AzureKeyVersion string `yaml:"azureKeyVersion,omitempty"` // AWS. // // AWS KMS Key ID and region. AwsKMSKeyID string `yaml:"awsKMSKeyID,omitempty"` AwsRegion string `yaml:"awsRegion,omitempty"` } // GetSigner returns the signer. func (key SigningKey) GetSigner(ctx context.Context) (measure.RSAKey, error) { switch { case key.KeyPath != "": return file.NewPCRSigner(key.KeyPath) case key.AzureVaultURL != "" && key.AzureKeyID != "": return azure.NewPCRSigner(ctx, key.AzureVaultURL, key.AzureKeyID, key.AzureKeyVersion) case key.AwsKMSKeyID != "": return aws.NewPCRSigner(ctx, key.AwsKMSKeyID, key.AwsRegion) default: return nil, errors.New("unsupported PCR signer") } } // GetSigner returns the signer. func (keyAndCert SigningKeyAndCertificate) GetSigner(ctx context.Context) (pesign.CertificateSigner, error) { switch { case keyAndCert.KeyPath != "" && keyAndCert.CertPath != "": return file.NewSecureBootSigner(keyAndCert.CertPath, keyAndCert.KeyPath) case keyAndCert.AzureVaultURL != "" && keyAndCert.AzureCertificateID != "": return azure.NewSecureBootSigner(ctx, keyAndCert.AzureVaultURL, keyAndCert.AzureCertificateID, keyAndCert.AzureCertificateID) case keyAndCert.AwsKMSKeyID != "" && keyAndCert.AwsCertARN != "": return aws.NewSecureBootACMSigner(ctx, keyAndCert.AwsKMSKeyID, keyAndCert.AwsRegion, keyAndCert.AwsCertARN) case keyAndCert.AwsKMSKeyID != "" && keyAndCert.AwsCertPath != "": return aws.NewSecureBootSigner(ctx, keyAndCert.AwsKMSKeyID, keyAndCert.AwsRegion, keyAndCert.AwsCertPath) default: return nil, errors.New("unsupported PCR signer") } } const defaultSecureBootPrefix = "/secureboot" // FillDefaults fills default values for the input. // //nolint:gocyclo,cyclop func (i *Input) FillDefaults(arch, version string, secureboot bool) { var ( zeroFileAsset FileAsset zeroContainerAsset ContainerAsset ) if i.Kernel == zeroFileAsset { i.Kernel.Path = fmt.Sprintf(constants.KernelAssetPath, arch) } if i.Initramfs == zeroFileAsset { i.Initramfs.Path = fmt.Sprintf(constants.InitramfsAssetPath, arch) } if i.BaseInstaller == zeroContainerAsset { i.BaseInstaller.ImageRef = fmt.Sprintf("%s:%s", images.DefaultInstallerImageRepository, version) if quirks.New(version).SupportsUnifiedInstaller() { i.BaseInstaller.ImageRef = fmt.Sprintf("%s-base:%s", images.DefaultInstallerImageRepository, version) } } if i.SDStub == zeroFileAsset { i.SDStub.Path = fmt.Sprintf(constants.SDStubAssetPath, arch) } if i.SDBoot == zeroFileAsset { i.SDBoot.Path = fmt.Sprintf(constants.SDBootAssetPath, arch) } if secureboot { if i.SecureBoot == nil { i.SecureBoot = &SecureBootAssets{} } if value.IsZero(i.SecureBoot.SecureBootSigner) { i.SecureBoot.SecureBootSigner.KeyPath = filepath.Join(defaultSecureBootPrefix, constants.SecureBootSigningKeyAsset) i.SecureBoot.SecureBootSigner.CertPath = filepath.Join(defaultSecureBootPrefix, constants.SecureBootSigningCertAsset) } if value.IsZero(i.SecureBoot.PCRSigner) { i.SecureBoot.PCRSigner.KeyPath = filepath.Join(defaultSecureBootPrefix, constants.PCRSigningKeyAsset) } if i.SecureBoot.PlatformKeyPath == "" { if platformKeyPath := filepath.Join(defaultSecureBootPrefix, constants.PlatformKeyAsset); fileExists(platformKeyPath) { i.SecureBoot.PlatformKeyPath = platformKeyPath } } if i.SecureBoot.KeyExchangeKeyPath == "" { if keyExchangeKeyPath := filepath.Join(defaultSecureBootPrefix, constants.KeyExchangeKeyAsset); fileExists(keyExchangeKeyPath) { i.SecureBoot.KeyExchangeKeyPath = keyExchangeKeyPath } } if i.SecureBoot.SignatureKeyPath == "" { if signatureKeyPath := filepath.Join(defaultSecureBootPrefix, constants.SignatureKeyAsset); fileExists(signatureKeyPath) { i.SecureBoot.SignatureKeyPath = signatureKeyPath } } } } func fileExists(path string) bool { _, err := os.Stat(path) return err == nil } // Pull the container asset to the path. func (c *ContainerAsset) Pull(ctx context.Context, arch string, printf func(string, ...any)) (v1.Image, error) { if c.TarballPath != "" { return nil, errors.New("pulling tarball container image is not supported") } if c.OCIPath != "" { printf("using OCI image from %s...", c.OCIPath) return c.pullFromOCI(arch) } printf("pulling %s...", c.ImageRef) opts := []crane.Option{ crane.WithPlatform(&v1.Platform{ Architecture: arch, OS: "linux", }), crane.WithContext(ctx), crane.WithAuthFromKeychain( authn.NewMultiKeychain( authn.DefaultKeychain, github.Keychain, google.Keychain, ), ), } if c.ForceInsecure { opts = append(opts, crane.Insecure) } img, err := crane.Pull(c.ImageRef, opts...) if err != nil { return nil, fmt.Errorf("error pulling image %s: %w", c.ImageRef, err) } return img, nil } func (c *ContainerAsset) pullFromOCI(arch string) (v1.Image, error) { ociLayout, err := layout.FromPath(c.OCIPath) if err != nil { return nil, fmt.Errorf("error opening OCI layout: %w", err) } ociIndex, err := ociLayout.ImageIndex() if err != nil { return nil, fmt.Errorf("error opening OCI index: %w", err) } ociManifest, err := ociIndex.IndexManifest() if err != nil { return nil, fmt.Errorf("error opening OCI manifest: %w", err) } for _, manifest := range ociManifest.Manifests { if manifest.Platform == nil { continue } if manifest.Platform.OS == "linux" && manifest.Platform.Architecture == arch { img, err := ociLayout.Image(manifest.Digest) if err != nil { return nil, fmt.Errorf("error opening OCI image: %w", err) } return img, nil } } return nil, fmt.Errorf("no OCI image found for %s", arch) } // Extract the container asset to the path. func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string, printf func(string, ...any)) error { if c.TarballPath != "" { in, err := os.Open(c.TarballPath) if err != nil { return err } defer in.Close() //nolint:errcheck printf("extracting %s...", c.TarballPath) return archiver.Untar(ctx, in, destination) } img, err := c.Pull(ctx, arch, printf) if err != nil { return err } r, w := io.Pipe() eg, ctx := errgroup.WithContext(ctx) eg.Go(func() error { if exportErr := crane.Export(img, w); exportErr != nil { w.CloseWithError(exportErr) return exportErr } w.Close() //nolint:errcheck return nil }) eg.Go(func() error { if untarErr := archiver.Untar(ctx, r, destination); untarErr != nil { r.CloseWithError(untarErr) return untarErr } return nil }) return eg.Wait() } ================================================ FILE: pkg/imager/profile/internal/signer/aws/aws.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package aws implements SecureBoot/PCR signers via AWS Key Management Service. package aws import ( "context" "fmt" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/acm" "github.com/aws/aws-sdk-go-v2/service/kms" ) func getKmsClient(ctx context.Context, awsRegion string) (*kms.Client, error) { awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(awsRegion)) if err != nil { return nil, fmt.Errorf("error initializing AWS default config: %w", err) } return kms.NewFromConfig(awsCfg), nil } func getAcmClient(ctx context.Context, awsRegion string) (*acm.Client, error) { awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(awsRegion)) if err != nil { return nil, fmt.Errorf("error initializing AWS default config: %w", err) } return acm.NewFromConfig(awsCfg), nil } ================================================ FILE: pkg/imager/profile/internal/signer/aws/aws_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package aws_test import ( "crypto/sha256" "os" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager/profile/internal/signer/aws" ) func TestIntegration(t *testing.T) { for _, envVar := range []string{"AWS_KMS_KEY_ID", "AWS_REGION", "AWS_CERT_PATH", "AWS_CERT_ARN"} { if os.Getenv(envVar) == "" { t.Skipf("%s not set", envVar) } } signer, err := aws.NewPCRSigner(t.Context(), os.Getenv("AWS_KMS_KEY_ID"), os.Getenv("AWS_REGION")) require.NoError(t, err) digest := sha256.Sum256(nil) _, err = signer.Sign(nil, digest[:], nil) require.NoError(t, err) sbSigner, err := aws.NewSecureBootSigner(t.Context(), os.Getenv("AWS_KMS_KEY_ID"), os.Getenv("AWS_REGION"), os.Getenv("AWS_CERT_PATH")) require.NoError(t, err) _, err = sbSigner.Signer().Sign(nil, digest[:], nil) require.NoError(t, err) sbAcmSigner, err := aws.NewSecureBootACMSigner(t.Context(), os.Getenv("AWS_KMS_KEY_ID"), os.Getenv("AWS_REGION"), os.Getenv("AWS_CERT_ARN")) require.NoError(t, err) _, err = sbAcmSigner.Signer().Sign(nil, digest[:], nil) require.NoError(t, err) } ================================================ FILE: pkg/imager/profile/internal/signer/aws/pcr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package aws import ( "context" "crypto" "crypto/rsa" "crypto/x509" "fmt" "io" "math/big" "github.com/aws/aws-sdk-go-v2/service/kms" "github.com/aws/aws-sdk-go-v2/service/kms/types" "github.com/siderolabs/talos/internal/pkg/measure" ) // KeySigner implements measure.RSAKey interface. // // KeySigner wraps Azure APIs to provide public key and crypto.Signer interface out of Azure Key Vault RSA key. type KeySigner struct { keyName string mode mode client *kms.Client publicKey *rsa.PublicKey } var algMap = map[mode]map[crypto.Hash]types.SigningAlgorithmSpec{ rsaPKCS1v15: { crypto.SHA256: types.SigningAlgorithmSpecRsassaPkcs1V15Sha256, crypto.SHA384: types.SigningAlgorithmSpecRsassaPkcs1V15Sha384, crypto.SHA512: types.SigningAlgorithmSpecRsassaPkcs1V15Sha512, }, rsaPSS: { crypto.SHA256: types.SigningAlgorithmSpecRsassaPssSha256, crypto.SHA384: types.SigningAlgorithmSpecRsassaPssSha384, crypto.SHA512: types.SigningAlgorithmSpecRsassaPssSha512, }, ecdsa: { crypto.SHA256: types.SigningAlgorithmSpecEcdsaSha256, crypto.SHA384: types.SigningAlgorithmSpecEcdsaSha384, crypto.SHA512: types.SigningAlgorithmSpecEcdsaSha512, }, } type mode string const ( rsaPKCS1v15 mode = "pkcs1v15" rsaPSS mode = "pss" ecdsa mode = "ecdsa" ) // PublicRSAKey returns the public key. func (s *KeySigner) PublicRSAKey() *rsa.PublicKey { return s.publicKey } // Public returns the public key. func (s *KeySigner) Public() crypto.PublicKey { return s.PublicRSAKey() } // Sign implements the crypto.Signer interface. func (s *KeySigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { mode := s.mode inner := algMap[mode] if inner == nil { return nil, fmt.Errorf("mode not supported") } hf := crypto.SHA256 if opts != nil { hf = opts.HashFunc() } algorithm := inner[hf] if algorithm == "" { return nil, fmt.Errorf("algorithm not supported") } resp, err := s.client.Sign(context.Background(), &kms.SignInput{ KeyId: &s.keyName, Message: digest, MessageType: types.MessageTypeDigest, SigningAlgorithm: algorithm, }) if err != nil { return nil, err } return resp.Signature, nil } // Verify interface. var _ measure.RSAKey = (*KeySigner)(nil) // NewPCRSigner creates a new PCR signer from AWS settings. func NewPCRSigner(ctx context.Context, kmsKeyID, awsRegion string) (*KeySigner, error) { client, err := getKmsClient(ctx, awsRegion) if err != nil { return nil, fmt.Errorf("failed to build AWS kms client: %w", err) } keyResponse, err := client.GetPublicKey(ctx, &kms.GetPublicKeyInput{ KeyId: &kmsKeyID, }) if err != nil { return nil, fmt.Errorf("failed to get key: %w", err) } if keyResponse.KeyUsage != "SIGN_VERIFY" { return nil, fmt.Errorf("key usage is not SIGN_VERIFY") } switch keyResponse.KeySpec { //nolint:exhaustive case types.KeySpecRsa2048, types.KeySpecRsa3072, types.KeySpecRsa4096: // expected, continue default: return nil, fmt.Errorf("key type is not RSA") } parsedKey, err := x509.ParsePKIXPublicKey(keyResponse.PublicKey) if err != nil { return nil, fmt.Errorf("Public key is not valid: %w", err) } rsaKey := parsedKey.(*rsa.PublicKey) if rsaKey.E == 0 { return nil, fmt.Errorf("property e is empty") } if rsaKey.N.Cmp(big.NewInt(0)) == 0 { return nil, fmt.Errorf("property N is empty") } return &KeySigner{ keyName: kmsKeyID, mode: rsaPKCS1v15, // TODO: make this configurable publicKey: rsaKey, client: client, }, nil } ================================================ FILE: pkg/imager/profile/internal/signer/aws/secureboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package aws import ( "context" "crypto" "crypto/x509" "encoding/pem" "fmt" "os" "github.com/aws/aws-sdk-go-v2/service/acm" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" ) // SecureBootSigner implements pesign.CertificateSigner interface. type SecureBootSigner struct { keySigner *KeySigner cert *x509.Certificate } // Verify interface. var _ pesign.CertificateSigner = (*SecureBootSigner)(nil) // Signer returns the signer. func (s *SecureBootSigner) Signer() crypto.Signer { return s.keySigner } // Certificate returns the certificate. func (s *SecureBootSigner) Certificate() *x509.Certificate { return s.cert } // NewSecureBootSigner creates a new SecureBootSigner. func NewSecureBootSigner(ctx context.Context, kmsKeyID, awsRegion, certPath string) (*SecureBootSigner, error) { keySigner, err := NewPCRSigner(ctx, kmsKeyID, awsRegion) if err != nil { return nil, fmt.Errorf("failed to initialize certificate key signer (kms): %w", err) } certData, err := os.ReadFile(certPath) if err != nil { return nil, err } certBlock, _ := pem.Decode(certData) if certBlock == nil { return nil, fmt.Errorf("failed to decode certificate") } cert, err := x509.ParseCertificate(certBlock.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse certificate: %w", err) } return &SecureBootSigner{ keySigner: keySigner, cert: cert, }, nil } // NewSecureBootACMSigner creates a new SecureBootSigner using an ACM certificate. func NewSecureBootACMSigner(ctx context.Context, kmsKeyID, awsRegion, acmCertificateARN string) (*SecureBootSigner, error) { keySigner, err := NewPCRSigner(ctx, kmsKeyID, awsRegion) if err != nil { return nil, fmt.Errorf("failed to initialize certificate key signer (kms): %w", err) } acmClient, err := getAcmClient(ctx, awsRegion) if err != nil { return nil, fmt.Errorf("failed to build ACM client: %w", err) } resp, err := acmClient.GetCertificate(ctx, &acm.GetCertificateInput{ CertificateArn: &acmCertificateARN, }) if err != nil { return nil, fmt.Errorf("failed to get certificate: %w", err) } certBlock, _ := pem.Decode([]byte(pointer.SafeDeref(resp.Certificate))) if certBlock == nil { return nil, fmt.Errorf("failed to decode certificate") } cert, err := x509.ParseCertificate(certBlock.Bytes) if err != nil { return nil, fmt.Errorf("failed to decode certificate: %w", err) } return &SecureBootSigner{ keySigner: keySigner, cert: cert, }, nil } ================================================ FILE: pkg/imager/profile/internal/signer/azure/azure.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package azure implements SecureBoot/PCR signers via Azure Key Vault. package azure import ( "context" "errors" "fmt" "os" "strings" "sync" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" ) type authenticationMethod string const ( unknownAuthenticationMethod = "unknown" environmentAuthenticationMethod = "environment" cliAuthenticationMethod = "cli" ) const azureClientID = "AZURE_CLIENT_ID" // getAuthenticationMethod returns an authenticationMethod to use to get an Azure Authorizer. // If no environment variables are set, unknownAuthMethod will be used. // If the environment variable 'AZURE_AUTH_METHOD' is set to either environment or cli, use it. // If the environment variables 'AZURE_TENANT_ID', 'AZURE_CLIENT_ID' and 'AZURE_CLIENT_SECRET' are set, use environment. func getAuthenticationMethod() authenticationMethod { tenantID := os.Getenv("AZURE_TENANT_ID") clientID := os.Getenv("AZURE_CLIENT_ID") clientSecret := os.Getenv("AZURE_CLIENT_SECRET") authMethod := os.Getenv("AZURE_AUTH_METHOD") if authMethod != "" { switch strings.ToLower(authMethod) { case "environment": return environmentAuthenticationMethod case "cli": return cliAuthenticationMethod } } if tenantID != "" && clientID != "" && clientSecret != "" { return environmentAuthenticationMethod } return unknownAuthenticationMethod } type azureCredential interface { GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) } func getAzClientOpts() azcore.ClientOptions { envName := os.Getenv("AZURE_ENVIRONMENT") switch envName { case "AZUREUSGOVERNMENT", "AZUREUSGOVERNMENTCLOUD": return azcore.ClientOptions{Cloud: cloud.AzureGovernment} case "AZURECHINACLOUD": return azcore.ClientOptions{Cloud: cloud.AzureChina} case "AZURECLOUD", "AZUREPUBLICCLOUD": return azcore.ClientOptions{Cloud: cloud.AzurePublic} default: return azcore.ClientOptions{Cloud: cloud.AzurePublic} } } // getAzureCredential takes an authenticationMethod and returns an Azure credential or an error. // // If the method is unknown, Environment will be tested and if it returns an error CLI will be tested. // If the method is specified, the specified method will be used and no other will be tested. // This means the following default order of methods will be used if nothing else is defined: // 1. Client credentials (FromEnvironment) // 2. Client certificate (FromEnvironment) // 3. Username password (FromEnvironment) // 4. MSI (FromEnvironment) // 5. CLI (FromCLI). func getAzureCredential(method authenticationMethod) (azureCredential, error) { clientOpts := getAzClientOpts() switch method { case environmentAuthenticationMethod: envCred, err := azidentity.NewEnvironmentCredential(&azidentity.EnvironmentCredentialOptions{ClientOptions: clientOpts}) if err == nil { return envCred, nil } o := &azidentity.ManagedIdentityCredentialOptions{ClientOptions: clientOpts} if ID, ok := os.LookupEnv(azureClientID); ok { o.ID = azidentity.ClientID(ID) } msiCred, err := azidentity.NewManagedIdentityCredential(o) if err == nil { return msiCred, nil } return nil, fmt.Errorf("failed to create default azure credential from env auth method: %w", err) case cliAuthenticationMethod: cred, err := azidentity.NewAzureCLICredential(nil) if err != nil { return nil, fmt.Errorf("failed to create default Azure credential from env auth method: %w", err) } return cred, nil case unknownAuthenticationMethod: break default: return nil, errors.New("you should never reach this") } envCreds, err := azidentity.NewEnvironmentCredential(&azidentity.EnvironmentCredentialOptions{ClientOptions: clientOpts}) if err == nil { return envCreds, nil } cliCreds, err := azidentity.NewAzureCLICredential(nil) if err != nil { return nil, fmt.Errorf("failed to create default Azure credential from env auth method: %w", err) } return cliCreds, nil } type azureCredentialAndError struct { cred azureCredential err error } var azureCredentialsOnce = sync.OnceValue(func() azureCredentialAndError { authMethod := getAuthenticationMethod() cred, err := getAzureCredential(authMethod) return azureCredentialAndError{cred, err} }) func getKeysClient(vaultURL string) (*azkeys.Client, error) { credAndError := azureCredentialsOnce() if credAndError.err != nil { return nil, credAndError.err } return azkeys.NewClient(vaultURL, credAndError.cred, nil) } func getCertsClient(vaultURL string) (*azcertificates.Client, error) { credAndError := azureCredentialsOnce() if credAndError.err != nil { return nil, credAndError.err } return azcertificates.NewClient(vaultURL, credAndError.cred, nil) } ================================================ FILE: pkg/imager/profile/internal/signer/azure/azure_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure_test import ( "crypto/sha256" "os" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager/profile/internal/signer/azure" ) func TestIntegration(t *testing.T) { for _, envVar := range []string{"AZURE_VAULT_URL", "AZURE_KEY_ID", "AZURE_CERT_ID", "AZURE_TENANT_ID", "AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET"} { if os.Getenv(envVar) == "" { t.Skipf("%s not set", envVar) } } signer, err := azure.NewPCRSigner(t.Context(), os.Getenv("AZURE_VAULT_URL"), os.Getenv("AZURE_KEY_ID"), "") require.NoError(t, err) digest := sha256.Sum256(nil) _, err = signer.Sign(nil, digest[:], nil) require.NoError(t, err) sbSigner, err := azure.NewSecureBootSigner(t.Context(), os.Getenv("AZURE_VAULT_URL"), os.Getenv("AZURE_CERT_ID"), "") require.NoError(t, err) _, err = sbSigner.Signer().Sign(nil, digest[:], nil) require.NoError(t, err) } ================================================ FILE: pkg/imager/profile/internal/signer/azure/pcr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure import ( "context" "crypto" "crypto/rsa" "errors" "fmt" "io" "math/big" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys" "github.com/siderolabs/talos/internal/pkg/measure" ) // KeySigner implements measure.RSAKey interface. // // KeySigner wraps Azure APIs to provide public key and crypto.Signer interface out of Azure Key Vault RSA key. type KeySigner struct { keyName, keyVersion string client *azkeys.Client publicKey *rsa.PublicKey } // PublicRSAKey returns the public key. func (s *KeySigner) PublicRSAKey() *rsa.PublicKey { return s.publicKey } // Public returns the public key. func (s *KeySigner) Public() crypto.PublicKey { return s.PublicRSAKey() } // Sign implements the crypto.Signer interface. func (s *KeySigner) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { params := azkeys.SignParameters{ Value: digest, } hf := crypto.SHA256 if opts != nil { hf = opts.HashFunc() } switch hf { //nolint:exhaustive case crypto.SHA256: params.Algorithm = new(azkeys.SignatureAlgorithmRS256) case crypto.SHA384: params.Algorithm = new(azkeys.SignatureAlgorithmRS384) case crypto.SHA512: params.Algorithm = new(azkeys.SignatureAlgorithmRS512) default: return nil, errors.New("unsupported hashing function") } resp, err := s.client.Sign(context.Background(), s.keyName, s.keyVersion, params, &azkeys.SignOptions{}) if err != nil { return nil, fmt.Errorf("failed to sign: %w", err) } return resp.Result, nil } // Verify interface. var _ measure.RSAKey = (*KeySigner)(nil) // NewPCRSigner creates a new PCR signer from Azure settings. func NewPCRSigner(ctx context.Context, vaultURL, keyID, keyVersion string) (*KeySigner, error) { client, err := getKeysClient(vaultURL) if err != nil { return nil, fmt.Errorf("failed to build Azure client: %w", err) } keyResponse, err := client.GetKey(ctx, keyID, keyVersion, &azkeys.GetKeyOptions{}) if err != nil { return nil, fmt.Errorf("failed to get key: %w", err) } if keyResponse.Key.Kty == nil { return nil, errors.New("key type is nil") } switch *keyResponse.Key.Kty { //nolint:exhaustive case azkeys.KeyTypeRSA, azkeys.KeyTypeRSAHSM: // expected, continue default: return nil, errors.New("key type is not RSA") } var publicKey rsa.PublicKey // N = modulus if len(keyResponse.Key.N) == 0 { return nil, errors.New("property N is empty") } publicKey.N = &big.Int{} publicKey.N.SetBytes(keyResponse.Key.N) // e = public exponent if len(keyResponse.Key.E) == 0 { return nil, errors.New("property e is empty") } publicKey.E = int(big.NewInt(0).SetBytes(keyResponse.Key.E).Uint64()) return &KeySigner{ keyName: keyID, keyVersion: keyVersion, publicKey: &publicKey, client: client, }, nil } ================================================ FILE: pkg/imager/profile/internal/signer/azure/secureboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package azure import ( "context" "crypto" "crypto/x509" "fmt" "github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" ) // SecureBootSigner implements pesign.CertificateSigner interface. type SecureBootSigner struct { keySigner *KeySigner cert *x509.Certificate } // Verify interface. var _ pesign.CertificateSigner = (*SecureBootSigner)(nil) // Signer returns the signer. func (s *SecureBootSigner) Signer() crypto.Signer { return s.keySigner } // Certificate returns the certificate. func (s *SecureBootSigner) Certificate() *x509.Certificate { return s.cert } // NewSecureBootSigner creates a new SecureBootSigner. func NewSecureBootSigner(ctx context.Context, vaultURL, certificateID, certificateVersion string) (*SecureBootSigner, error) { certsClient, err := getCertsClient(vaultURL) if err != nil { return nil, fmt.Errorf("failed to build Azure certificates client: %w", err) } resp, err := certsClient.GetCertificate(ctx, certificateID, certificateVersion, &azcertificates.GetCertificateOptions{}) if err != nil { return nil, fmt.Errorf("failed to get certificate: %w", err) } cert, err := x509.ParseCertificate(resp.CER) if err != nil { return nil, fmt.Errorf("failed to decode certificate: %w", err) } // initialize key signer via existing implementation KID := pointer.SafeDeref(resp.KID) keySigner, err := NewPCRSigner(ctx, vaultURL, KID.Name(), KID.Version()) if err != nil { return nil, fmt.Errorf("failed to initialize certificate key signer: %w", err) } return &SecureBootSigner{ cert: cert, keySigner: keySigner, }, nil } ================================================ FILE: pkg/imager/profile/internal/signer/file/pcr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package file implements SecureBoot/PCR signers via plain filesystem files. package file import ( "crypto" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "io" "os" "github.com/siderolabs/talos/internal/pkg/measure" ) // PCRSigner implements measure.RSAKey interface. type PCRSigner struct { key *rsa.PrivateKey } // Verify interface. var _ measure.RSAKey = (*PCRSigner)(nil) // PublicRSAKey returns the public key. func (s *PCRSigner) PublicRSAKey() *rsa.PublicKey { return &s.key.PublicKey } // Public returns the public key. func (s *PCRSigner) Public() crypto.PublicKey { return s.PublicRSAKey() } // Sign implements the crypto.Signer interface. func (s *PCRSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { return s.key.Sign(rand, digest, opts) } // NewPCRSigner creates a new PCR signer from the private key file. func NewPCRSigner(keyPath string) (*PCRSigner, error) { keyData, err := os.ReadFile(keyPath) if err != nil { return nil, err } // convert private key to rsa.PrivateKey rsaPrivateKeyBlock, _ := pem.Decode(keyData) if rsaPrivateKeyBlock == nil { return nil, errors.New("failed to decode private key") } rsaKey, err := x509.ParsePKCS1PrivateKey(rsaPrivateKeyBlock.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse private RSA key: %v", err) } return &PCRSigner{rsaKey}, nil } ================================================ FILE: pkg/imager/profile/internal/signer/file/secureboot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package file import ( "crypto" "crypto/rsa" "crypto/x509" "encoding/pem" "errors" "fmt" "os" "github.com/siderolabs/talos/internal/pkg/secureboot/pesign" ) // SecureBootSigner implements pesign.CertificateSigner interface. type SecureBootSigner struct { key *rsa.PrivateKey cert *x509.Certificate } // Verify interface. var _ pesign.CertificateSigner = (*SecureBootSigner)(nil) // Signer returns the signer. func (s *SecureBootSigner) Signer() crypto.Signer { return s.key } // Certificate returns the certificate. func (s *SecureBootSigner) Certificate() *x509.Certificate { return s.cert } // NewSecureBootSigner creates a new SecureBootSigner. func NewSecureBootSigner(certPath, keyPath string) (*SecureBootSigner, error) { keyData, err := os.ReadFile(keyPath) if err != nil { return nil, err } // convert private key to rsa.PrivateKey rsaPrivateKeyBlock, _ := pem.Decode(keyData) if rsaPrivateKeyBlock == nil { return nil, errors.New("failed to decode private key") } rsaKey, err := x509.ParsePKCS1PrivateKey(rsaPrivateKeyBlock.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse private RSA key: %w", err) } certData, err := os.ReadFile(certPath) if err != nil { return nil, err } certBlock, _ := pem.Decode(certData) if certBlock == nil { return nil, errors.New("failed to decode certificate") } cert, err := x509.ParseCertificate(certBlock.Bytes) if err != nil { return nil, fmt.Errorf("failed to parse certificate: %w", err) } return &SecureBootSigner{ key: rsaKey, cert: cert, }, nil } ================================================ FILE: pkg/imager/profile/output.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile import ( "github.com/siderolabs/talos/pkg/machinery/imager/imageropts" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // Output describes image generation result. type Output struct { // Kind of the output: // * iso - ISO image // * image - disk image (Talos pre-installed) // * installer - installer container // * kernel - Linux kernel // * initramfs - initramfs image Kind OutputKind `yaml:"kind"` // Options for the 'image' output. ImageOptions *ImageOptions `yaml:"imageOptions,omitempty"` // Options for the 'iso' output. ISOOptions *ISOOptions `yaml:"isoOptions,omitempty"` // OutFormat is the format for the output: // * raw - output raw file // * .tar.gz - output tar.gz archive // * .xz - output xz archive // * .gz - output gz archive OutFormat OutFormat `yaml:"outFormat"` } // ImageOptions describes options for the 'image' output. type ImageOptions struct { // DiskSize is the size of the disk image (bytes). DiskSize int64 `yaml:"diskSize"` // DiskFormat is the format of the disk image: // * raw - raw disk image // * qcow2 - qcow2 disk image // * vhd - VPC disk image // * ova - VMWare disk image DiskFormat DiskFormat `yaml:"diskFormat,omitempty"` // DiskFormatOptions are additional options for the disk format DiskFormatOptions string `yaml:"diskFormatOptions,omitempty"` // Bootloader is the bootloader to use for the disk image. // If not set, it defaults to dual-boot. Bootloader BootloaderKind `yaml:"bootloader,omitempty"` } // ISOOptions describes options for the 'iso' output. type ISOOptions struct { // SDBootEnrollKeys is a value in loader.conf secure-boot-enroll: off, manual, if-safe, force. // // If not set, it defaults to if-safe. SDBootEnrollKeys SDBootEnrollKeys `yaml:"sdBootEnrollKeys"` // Bootloader is the bootloader to use for the iso image. // If not set, it defaults to dual-boot. Bootloader BootloaderKind `yaml:"bootloader,omitempty"` } // OutputKind is output specification. type OutputKind int // OutputKind values. const ( OutKindUnknown OutputKind = iota // unknown OutKindISO // iso OutKindImage // image OutKindInstaller // installer OutKindKernel // kernel OutKindInitramfs // initramfs OutKindUKI // uki OutKindCmdline // cmdline ) // OutFormat is output format specification. type OutFormat int // OutFormat values. const ( OutFormatUnknown OutFormat = iota // unknown OutFormatRaw // raw OutFormatTar // .tar.gz OutFormatXZ // .xz OutFormatGZ // .gz OutFormatZSTD // .zst ) // DiskFormat is disk format specification. type DiskFormat int // DiskFormat values. const ( DiskFormatUnknown DiskFormat = iota // unknown DiskFormatRaw // raw DiskFormatQCOW2 // qcow2 DiskFormatVPC // vhd DiskFormatOVA // ova ) // SDBootEnrollKeys is a value in loader.conf secure-boot-enroll: off, manual, if-safe, force. type SDBootEnrollKeys int // SDBootEnrollKeys values. const ( SDBootEnrollKeysIfSafe SDBootEnrollKeys = iota // if-safe SDBootEnrollKeysManual // manual SDBootEnrollKeysForce // force SDBootEnrollKeysOff // off ) // BootloaderKind is a bootloader for the disk image. type BootloaderKind = imageropts.BootloaderKind // BootloaderKind values re-exported from imageropts. const ( BootLoaderKindNone = imageropts.BootLoaderKindNone BootLoaderKindDualBoot = imageropts.BootLoaderKindDualBoot BootLoaderKindSDBoot = imageropts.BootLoaderKindSDBoot BootLoaderKindGrub = imageropts.BootLoaderKindGrub ) // FillDefaults fills default values for the output. func (o *Output) FillDefaults(arch, version string, secureboot bool) { switch o.Kind { //nolint:exhaustive case OutKindImage: if o.ImageOptions == nil { o.ImageOptions = &ImageOptions{} } o.ImageOptions.Bootloader = o.selectBootloader(o.ImageOptions.Bootloader, arch, version, secureboot) ps := quirks.New(version).PartitionSizes() // bump default image size for expanded boot o.ImageOptions.DiskSize += int64(ps.GrubBootSize()) - 1000*1024*1024 // 1000 MiB if o.ImageOptions.Bootloader == BootLoaderKindDualBoot { // add extra space for BIOS and BOOT partitions o.ImageOptions.DiskSize += int64(ps.GrubBIOSSize()) + int64(ps.GrubBootSize()) } case OutKindISO: if !quirks.New(version).ISOSupportsSettingBootloader() { return } if o.ISOOptions == nil { o.ISOOptions = &ISOOptions{} } o.ISOOptions.Bootloader = o.selectBootloader(o.ISOOptions.Bootloader, arch, version, secureboot) } } func (o *Output) selectBootloader(current BootloaderKind, arch, version string, secureboot bool) BootloaderKind { // If already set, return the current value, secureboot shouldn't allow changing it. if current != BootLoaderKindNone && !secureboot { return current } useSDBoot := quirks.New(version).UseSDBootForUEFI() switch { case secureboot: // secureboot is always using sd-boot return BootLoaderKindSDBoot case arch == "arm64" && useSDBoot: // arm64 always uses sd-boot for Talos >= 1.10 return BootLoaderKindSDBoot case !useSDBoot: // legacy versions of Talos use GRUB for BIOS/UEFI return BootLoaderKindGrub default: // Default to dual-boot if not overridden. return BootLoaderKindDualBoot } } ================================================ FILE: pkg/imager/profile/output_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) func createOutputWithDefaults(kind profile.OutputKind, arch, version string, secureBoot bool) profile.Output { out := profile.Output{ Kind: kind, } out.FillDefaults(arch, version, secureBoot) return out } func createOutputWithOverride(kind profile.OutputKind, bootloader profile.BootloaderKind, secureBoot bool) profile.Output { out := profile.Output{ Kind: kind, } switch kind { //nolint:exhaustive case profile.OutKindImage: out.ImageOptions = &profile.ImageOptions{ Bootloader: bootloader, } case profile.OutKindISO: out.ISOOptions = &profile.ISOOptions{ Bootloader: bootloader, } } out.FillDefaults("amd64", "v1.12.0", secureBoot) return out } func TestBootloaderSetting(t *testing.T) { t.Parallel() tests := []struct { arch string version string secureBoot bool wantImage profile.BootloaderKind }{ // Talos < 1.10: GRUB for both amd64/arm64, ISO options not supported {"amd64", "1.9.0", false, profile.BootLoaderKindGrub}, {"amd64", "1.9.0", true, profile.BootLoaderKindSDBoot}, {"arm64", "1.9.0", false, profile.BootLoaderKindGrub}, {"arm64", "1.9.0", true, profile.BootLoaderKindSDBoot}, // Talos 1.10-1.11: amd64=dual-boot, arm64=sd-boot, ISO options not supported {"amd64", "1.10.0", false, profile.BootLoaderKindDualBoot}, {"amd64", "1.10.0", true, profile.BootLoaderKindSDBoot}, {"arm64", "1.10.0", false, profile.BootLoaderKindSDBoot}, {"arm64", "1.10.0", true, profile.BootLoaderKindSDBoot}, {"amd64", "1.11.0", false, profile.BootLoaderKindDualBoot}, {"amd64", "1.11.0", true, profile.BootLoaderKindSDBoot}, {"arm64", "1.11.0", false, profile.BootLoaderKindSDBoot}, {"arm64", "1.11.0", true, profile.BootLoaderKindSDBoot}, // Talos >= 1.12: amd64=dual-boot, arm64=sd-boot, ISO options supported {"amd64", "1.12.0", false, profile.BootLoaderKindDualBoot}, {"amd64", "1.12.0", true, profile.BootLoaderKindSDBoot}, {"arm64", "1.12.0", false, profile.BootLoaderKindSDBoot}, {"arm64", "1.12.0", true, profile.BootLoaderKindSDBoot}, } for _, tt := range tests { name := tt.arch + "-" + tt.version if tt.secureBoot { name += "-secureboot" } t.Run(name, func(t *testing.T) { t.Parallel() // Test Image output img := createOutputWithDefaults(profile.OutKindImage, tt.arch, tt.version, tt.secureBoot) require.NotNil(t, img.ImageOptions) require.Equal(t, tt.wantImage, img.ImageOptions.Bootloader) // Test ISO output iso := createOutputWithDefaults(profile.OutKindISO, tt.arch, tt.version, tt.secureBoot) if quirks.New(tt.version).ISOSupportsSettingBootloader() { require.NotNil(t, iso.ISOOptions) require.Equal(t, tt.wantImage, iso.ISOOptions.Bootloader) } else { require.Nil(t, iso.ISOOptions) } }) } } func TestBootloaderOverride(t *testing.T) { t.Parallel() tests := []struct { name string secureBoot bool override profile.BootloaderKind expectedBootloader profile.BootloaderKind }{ { "non-secureboot override to sd-boot", false, profile.BootLoaderKindSDBoot, profile.BootLoaderKindSDBoot, }, { "secureboot override to grub", true, profile.BootLoaderKindGrub, profile.BootLoaderKindSDBoot, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Test Image output with override img := createOutputWithOverride(profile.OutKindImage, tt.override, tt.secureBoot) require.NotNil(t, img.ImageOptions) require.Equal(t, tt.expectedBootloader, img.ImageOptions.Bootloader) // Test ISO output with override iso := createOutputWithOverride(profile.OutKindISO, tt.override, tt.secureBoot) require.Equal(t, tt.expectedBootloader, iso.ISOOptions.Bootloader) }) } } ================================================ FILE: pkg/imager/profile/outputkind_enumer.go ================================================ // Code generated by "enumer -type OutputKind,OutFormat,DiskFormat,SDBootEnrollKeys -linecomment -text"; DO NOT EDIT. package profile import ( "fmt" "strings" ) const _OutputKindName = "unknownisoimageinstallerkernelinitramfsukicmdline" var _OutputKindIndex = [...]uint8{0, 7, 10, 15, 24, 30, 39, 42, 49} const _OutputKindLowerName = "unknownisoimageinstallerkernelinitramfsukicmdline" func (i OutputKind) String() string { if i < 0 || i >= OutputKind(len(_OutputKindIndex)-1) { return fmt.Sprintf("OutputKind(%d)", i) } return _OutputKindName[_OutputKindIndex[i]:_OutputKindIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _OutputKindNoOp() { var x [1]struct{} _ = x[OutKindUnknown-(0)] _ = x[OutKindISO-(1)] _ = x[OutKindImage-(2)] _ = x[OutKindInstaller-(3)] _ = x[OutKindKernel-(4)] _ = x[OutKindInitramfs-(5)] _ = x[OutKindUKI-(6)] _ = x[OutKindCmdline-(7)] } var _OutputKindValues = []OutputKind{OutKindUnknown, OutKindISO, OutKindImage, OutKindInstaller, OutKindKernel, OutKindInitramfs, OutKindUKI, OutKindCmdline} var _OutputKindNameToValueMap = map[string]OutputKind{ _OutputKindName[0:7]: OutKindUnknown, _OutputKindLowerName[0:7]: OutKindUnknown, _OutputKindName[7:10]: OutKindISO, _OutputKindLowerName[7:10]: OutKindISO, _OutputKindName[10:15]: OutKindImage, _OutputKindLowerName[10:15]: OutKindImage, _OutputKindName[15:24]: OutKindInstaller, _OutputKindLowerName[15:24]: OutKindInstaller, _OutputKindName[24:30]: OutKindKernel, _OutputKindLowerName[24:30]: OutKindKernel, _OutputKindName[30:39]: OutKindInitramfs, _OutputKindLowerName[30:39]: OutKindInitramfs, _OutputKindName[39:42]: OutKindUKI, _OutputKindLowerName[39:42]: OutKindUKI, _OutputKindName[42:49]: OutKindCmdline, _OutputKindLowerName[42:49]: OutKindCmdline, } var _OutputKindNames = []string{ _OutputKindName[0:7], _OutputKindName[7:10], _OutputKindName[10:15], _OutputKindName[15:24], _OutputKindName[24:30], _OutputKindName[30:39], _OutputKindName[39:42], _OutputKindName[42:49], } // OutputKindString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func OutputKindString(s string) (OutputKind, error) { if val, ok := _OutputKindNameToValueMap[s]; ok { return val, nil } if val, ok := _OutputKindNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to OutputKind values", s) } // OutputKindValues returns all values of the enum func OutputKindValues() []OutputKind { return _OutputKindValues } // OutputKindStrings returns a slice of all String values of the enum func OutputKindStrings() []string { strs := make([]string, len(_OutputKindNames)) copy(strs, _OutputKindNames) return strs } // IsAOutputKind returns "true" if the value is listed in the enum definition. "false" otherwise func (i OutputKind) IsAOutputKind() bool { for _, v := range _OutputKindValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for OutputKind func (i OutputKind) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for OutputKind func (i *OutputKind) UnmarshalText(text []byte) error { var err error *i, err = OutputKindString(string(text)) return err } const _OutFormatName = "unknownraw.tar.gz.xz.gz.zst" var _OutFormatIndex = [...]uint8{0, 7, 10, 17, 20, 23, 27} const _OutFormatLowerName = "unknownraw.tar.gz.xz.gz.zst" func (i OutFormat) String() string { if i < 0 || i >= OutFormat(len(_OutFormatIndex)-1) { return fmt.Sprintf("OutFormat(%d)", i) } return _OutFormatName[_OutFormatIndex[i]:_OutFormatIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _OutFormatNoOp() { var x [1]struct{} _ = x[OutFormatUnknown-(0)] _ = x[OutFormatRaw-(1)] _ = x[OutFormatTar-(2)] _ = x[OutFormatXZ-(3)] _ = x[OutFormatGZ-(4)] _ = x[OutFormatZSTD-(5)] } var _OutFormatValues = []OutFormat{OutFormatUnknown, OutFormatRaw, OutFormatTar, OutFormatXZ, OutFormatGZ, OutFormatZSTD} var _OutFormatNameToValueMap = map[string]OutFormat{ _OutFormatName[0:7]: OutFormatUnknown, _OutFormatLowerName[0:7]: OutFormatUnknown, _OutFormatName[7:10]: OutFormatRaw, _OutFormatLowerName[7:10]: OutFormatRaw, _OutFormatName[10:17]: OutFormatTar, _OutFormatLowerName[10:17]: OutFormatTar, _OutFormatName[17:20]: OutFormatXZ, _OutFormatLowerName[17:20]: OutFormatXZ, _OutFormatName[20:23]: OutFormatGZ, _OutFormatLowerName[20:23]: OutFormatGZ, _OutFormatName[23:27]: OutFormatZSTD, _OutFormatLowerName[23:27]: OutFormatZSTD, } var _OutFormatNames = []string{ _OutFormatName[0:7], _OutFormatName[7:10], _OutFormatName[10:17], _OutFormatName[17:20], _OutFormatName[20:23], _OutFormatName[23:27], } // OutFormatString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func OutFormatString(s string) (OutFormat, error) { if val, ok := _OutFormatNameToValueMap[s]; ok { return val, nil } if val, ok := _OutFormatNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to OutFormat values", s) } // OutFormatValues returns all values of the enum func OutFormatValues() []OutFormat { return _OutFormatValues } // OutFormatStrings returns a slice of all String values of the enum func OutFormatStrings() []string { strs := make([]string, len(_OutFormatNames)) copy(strs, _OutFormatNames) return strs } // IsAOutFormat returns "true" if the value is listed in the enum definition. "false" otherwise func (i OutFormat) IsAOutFormat() bool { for _, v := range _OutFormatValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for OutFormat func (i OutFormat) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for OutFormat func (i *OutFormat) UnmarshalText(text []byte) error { var err error *i, err = OutFormatString(string(text)) return err } const _DiskFormatName = "unknownrawqcow2vhdova" var _DiskFormatIndex = [...]uint8{0, 7, 10, 15, 18, 21} const _DiskFormatLowerName = "unknownrawqcow2vhdova" func (i DiskFormat) String() string { if i < 0 || i >= DiskFormat(len(_DiskFormatIndex)-1) { return fmt.Sprintf("DiskFormat(%d)", i) } return _DiskFormatName[_DiskFormatIndex[i]:_DiskFormatIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _DiskFormatNoOp() { var x [1]struct{} _ = x[DiskFormatUnknown-(0)] _ = x[DiskFormatRaw-(1)] _ = x[DiskFormatQCOW2-(2)] _ = x[DiskFormatVPC-(3)] _ = x[DiskFormatOVA-(4)] } var _DiskFormatValues = []DiskFormat{DiskFormatUnknown, DiskFormatRaw, DiskFormatQCOW2, DiskFormatVPC, DiskFormatOVA} var _DiskFormatNameToValueMap = map[string]DiskFormat{ _DiskFormatName[0:7]: DiskFormatUnknown, _DiskFormatLowerName[0:7]: DiskFormatUnknown, _DiskFormatName[7:10]: DiskFormatRaw, _DiskFormatLowerName[7:10]: DiskFormatRaw, _DiskFormatName[10:15]: DiskFormatQCOW2, _DiskFormatLowerName[10:15]: DiskFormatQCOW2, _DiskFormatName[15:18]: DiskFormatVPC, _DiskFormatLowerName[15:18]: DiskFormatVPC, _DiskFormatName[18:21]: DiskFormatOVA, _DiskFormatLowerName[18:21]: DiskFormatOVA, } var _DiskFormatNames = []string{ _DiskFormatName[0:7], _DiskFormatName[7:10], _DiskFormatName[10:15], _DiskFormatName[15:18], _DiskFormatName[18:21], } // DiskFormatString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func DiskFormatString(s string) (DiskFormat, error) { if val, ok := _DiskFormatNameToValueMap[s]; ok { return val, nil } if val, ok := _DiskFormatNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to DiskFormat values", s) } // DiskFormatValues returns all values of the enum func DiskFormatValues() []DiskFormat { return _DiskFormatValues } // DiskFormatStrings returns a slice of all String values of the enum func DiskFormatStrings() []string { strs := make([]string, len(_DiskFormatNames)) copy(strs, _DiskFormatNames) return strs } // IsADiskFormat returns "true" if the value is listed in the enum definition. "false" otherwise func (i DiskFormat) IsADiskFormat() bool { for _, v := range _DiskFormatValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for DiskFormat func (i DiskFormat) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for DiskFormat func (i *DiskFormat) UnmarshalText(text []byte) error { var err error *i, err = DiskFormatString(string(text)) return err } const _SDBootEnrollKeysName = "if-safemanualforceoff" var _SDBootEnrollKeysIndex = [...]uint8{0, 7, 13, 18, 21} const _SDBootEnrollKeysLowerName = "if-safemanualforceoff" func (i SDBootEnrollKeys) String() string { if i < 0 || i >= SDBootEnrollKeys(len(_SDBootEnrollKeysIndex)-1) { return fmt.Sprintf("SDBootEnrollKeys(%d)", i) } return _SDBootEnrollKeysName[_SDBootEnrollKeysIndex[i]:_SDBootEnrollKeysIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _SDBootEnrollKeysNoOp() { var x [1]struct{} _ = x[SDBootEnrollKeysIfSafe-(0)] _ = x[SDBootEnrollKeysManual-(1)] _ = x[SDBootEnrollKeysForce-(2)] _ = x[SDBootEnrollKeysOff-(3)] } var _SDBootEnrollKeysValues = []SDBootEnrollKeys{SDBootEnrollKeysIfSafe, SDBootEnrollKeysManual, SDBootEnrollKeysForce, SDBootEnrollKeysOff} var _SDBootEnrollKeysNameToValueMap = map[string]SDBootEnrollKeys{ _SDBootEnrollKeysName[0:7]: SDBootEnrollKeysIfSafe, _SDBootEnrollKeysLowerName[0:7]: SDBootEnrollKeysIfSafe, _SDBootEnrollKeysName[7:13]: SDBootEnrollKeysManual, _SDBootEnrollKeysLowerName[7:13]: SDBootEnrollKeysManual, _SDBootEnrollKeysName[13:18]: SDBootEnrollKeysForce, _SDBootEnrollKeysLowerName[13:18]: SDBootEnrollKeysForce, _SDBootEnrollKeysName[18:21]: SDBootEnrollKeysOff, _SDBootEnrollKeysLowerName[18:21]: SDBootEnrollKeysOff, } var _SDBootEnrollKeysNames = []string{ _SDBootEnrollKeysName[0:7], _SDBootEnrollKeysName[7:13], _SDBootEnrollKeysName[13:18], _SDBootEnrollKeysName[18:21], } // SDBootEnrollKeysString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func SDBootEnrollKeysString(s string) (SDBootEnrollKeys, error) { if val, ok := _SDBootEnrollKeysNameToValueMap[s]; ok { return val, nil } if val, ok := _SDBootEnrollKeysNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to SDBootEnrollKeys values", s) } // SDBootEnrollKeysValues returns all values of the enum func SDBootEnrollKeysValues() []SDBootEnrollKeys { return _SDBootEnrollKeysValues } // SDBootEnrollKeysStrings returns a slice of all String values of the enum func SDBootEnrollKeysStrings() []string { strs := make([]string, len(_SDBootEnrollKeysNames)) copy(strs, _SDBootEnrollKeysNames) return strs } // IsASDBootEnrollKeys returns "true" if the value is listed in the enum definition. "false" otherwise func (i SDBootEnrollKeys) IsASDBootEnrollKeys() bool { for _, v := range _SDBootEnrollKeysValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for SDBootEnrollKeys func (i SDBootEnrollKeys) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for SDBootEnrollKeys func (i *SDBootEnrollKeys) UnmarshalText(text []byte) error { var err error *i, err = SDBootEnrollKeysString(string(text)) return err } ================================================ FILE: pkg/imager/profile/profile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package profile contains definition of the image generation profile. package profile import ( "errors" "fmt" "io" "github.com/siderolabs/go-pointer" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/overlay" ) //go:generate go tool github.com/siderolabs/deep-copy -type Profile -type SecureBootAssets -header-file ../../../hack/boilerplate.txt -o deep_copy.generated.go . //go:generate go tool github.com/dmarkham/enumer -type OutputKind,OutFormat,DiskFormat,SDBootEnrollKeys -linecomment -text // Profile describes image generation result. type Profile struct { // BaseProfileName is the profile name to inherit from. BaseProfileName string `yaml:"baseProfileName,omitempty"` // Architecture of the image: amd64 or arm64. Arch string `yaml:"arch"` // Platform name of the image: qemu, aws, gcp, etc. Platform string `yaml:"platform"` // SecureBoot enables SecureBoot (only for UEFI build). SecureBoot *bool `yaml:"secureboot"` // Version is Talos version. Version string `yaml:"version"` // Various customizations than can be applied to the image. Customization CustomizationProfile `yaml:"customization,omitempty"` // Input describes inputs for image generation. Input Input `yaml:"input"` // Overlay describes overlay options for image generation. Overlay *OverlayOptions `yaml:"overlay,omitempty"` // Output describes image generation result. Output Output `yaml:"output"` } // OverlayOptions describes overlay options for image generation. type OverlayOptions struct { // Name of the overlay installer, defaults to `default` if not set. Name string `yaml:"name"` // Image to use for the overlay. Image ContainerAsset `yaml:"image"` // Options for the overlay. overlay.ExtraOptions `yaml:"options,omitempty"` //nolint:embeddedstructfieldcheck } // SecureBootEnabled derefences SecureBoot. func (p *Profile) SecureBootEnabled() bool { return pointer.SafeDeref(p.SecureBoot) } // Validate the profile. // //nolint:gocyclo,cyclop func (p *Profile) Validate() error { if p.Arch != amd64 && p.Arch != arm64 { return fmt.Errorf("invalid arch %q", p.Arch) } if p.Platform == "" { return errors.New("platform is required") } if p.SecureBootEnabled() && !quirks.New(p.Version).SupportsUKI() { return fmt.Errorf("secureboot is not supported for Talos version %q", p.Version) } switch p.Output.Kind { case OutKindUnknown: return errors.New("unknown output kind") case OutKindISO: // ISO supports all kinds of customization if quirks.New(p.Version).ISOSupportsSettingBootloader() { if p.Output.ISOOptions != nil && p.Output.ISOOptions.Bootloader == BootLoaderKindNone { return errors.New("bootloader cannot be 'none' for ISO output") } } case OutKindCmdline: // cmdline supports all kinds of customization case OutKindImage: // Image supports all kinds of customization if p.Output.ImageOptions == nil { return errors.New("image options are required for image output") } if p.Output.ImageOptions.DiskSize == 0 { return errors.New("disk size is required for image output") } if p.Output.ImageOptions.Bootloader == BootLoaderKindNone { return errors.New("bootloader cannot be 'none' for disk image output") } case OutKindInstaller: if len(p.Customization.MetaContents) > 0 { return fmt.Errorf("customization of meta partition is not supported for %s output", p.Output.Kind) } case OutKindKernel, OutKindInitramfs: if p.SecureBootEnabled() { return fmt.Errorf("secureboot is not supported for %s output", p.Output.Kind) } if len(p.Customization.ExtraKernelArgs) > 0 { return fmt.Errorf("customization of kernel args is not supported for %s output", p.Output.Kind) } if len(p.Customization.MetaContents) > 0 { return fmt.Errorf("customization of meta partition is not supported for %s output", p.Output.Kind) } case OutKindUKI: } return nil } // OutputPath generates the output path for the profile. // //nolint:gocyclo func (p *Profile) OutputPath() string { path := p.Platform path += "-" + p.Arch if p.SecureBootEnabled() { path += "-secureboot" } switch p.Output.Kind { case OutKindUnknown: panic("unknown output kind") case OutKindISO: path += ".iso" case OutKindImage: path += "." + p.Output.ImageOptions.DiskFormat.String() case OutKindInstaller: path = "installer-" + p.Arch if p.SecureBootEnabled() { path += "-secureboot" } path += ".tar" case OutKindKernel: path = "kernel-" + p.Arch case OutKindInitramfs: path = "initramfs-" + path + ".xz" case OutKindUKI: path += "-uki.efi" case OutKindCmdline: path = "cmdline-" + path } return path } // Dump the profile as YAML. func (p *Profile) Dump(w io.Writer) error { encoder := yaml.NewEncoder(w) encoder.SetIndent(2) return encoder.Encode(p) } ================================================ FILE: pkg/imager/profile/profile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package profile_test import ( "errors" "io/fs" "os" "sort" "strings" "testing" "github.com/blang/semver/v4" "github.com/siderolabs/gen/maps" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/imager/profile" "github.com/siderolabs/talos/pkg/machinery/version" ) func TestFillDefaults(t *testing.T) { t.Parallel() arches := []string{"amd64", "arm64"} versions := []string{"1.9.0", "1.10.0", "1.11.0", "1.12.0", "1.13.0"} lastVersion := semver.MustParse(versions[len(versions)-1]) currentVersion, err := semver.ParseTolerant(version.Tag) require.NoError(t, err) currentVersion.Patch = 0 currentVersion.Pre = nil require.True(t, lastVersion.GTE(currentVersion), "last version %s should be greater or equal to current version %s", lastVersion, currentVersion) profiles := maps.Keys(profile.Default) sort.Strings(profiles) // flip this to true to generate missing testdata files const recordMissing = false if recordMissing { t.Logf("recording missing testdata files, failing the test") t.Fail() } for _, prof := range profiles { t.Run(prof, func(t *testing.T) { t.Parallel() var secureBoot bool if strings.HasPrefix(prof, "secureboot") { secureBoot = true } for _, arch := range arches { t.Run(arch, func(t *testing.T) { t.Parallel() for _, version := range versions { t.Run(version, func(t *testing.T) { t.Parallel() p := profile.Default[prof].DeepCopy() p.Arch = arch p.Version = version p.Input.FillDefaults(arch, version, secureBoot) p.Output.FillDefaults(arch, version, secureBoot) require.NoError(t, p.Validate()) var profileData strings.Builder require.NoError(t, p.Dump(&profileData)) expectedData, err := os.ReadFile("testdata/" + prof + "-" + arch + "-" + version + ".yaml") if errors.Is(err, fs.ErrNotExist) && recordMissing { require.NoError(t, os.WriteFile("testdata/"+prof+"-"+arch+"-"+version+".yaml", []byte(profileData.String()), 0o644)) } else { require.NoError(t, err) require.Equal(t, string(expectedData), profileData.String(), "profile: %s, platform: %s, version: %s", prof, arch, version) } }) } }) } }) } } ================================================ FILE: pkg/imager/profile/testdata/akamai-amd64-1.10.0.yaml ================================================ arch: amd64 platform: akamai secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-amd64-1.11.0.yaml ================================================ arch: amd64 platform: akamai secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-amd64-1.12.0.yaml ================================================ arch: amd64 platform: akamai secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-amd64-1.13.0.yaml ================================================ arch: amd64 platform: akamai secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-amd64-1.9.0.yaml ================================================ arch: amd64 platform: akamai secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-arm64-1.10.0.yaml ================================================ arch: arm64 platform: akamai secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-arm64-1.11.0.yaml ================================================ arch: arm64 platform: akamai secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-arm64-1.12.0.yaml ================================================ arch: arm64 platform: akamai secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-arm64-1.13.0.yaml ================================================ arch: arm64 platform: akamai secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/akamai-arm64-1.9.0.yaml ================================================ arch: arm64 platform: akamai secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/aws-amd64-1.10.0.yaml ================================================ arch: amd64 platform: aws secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-amd64-1.11.0.yaml ================================================ arch: amd64 platform: aws secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-amd64-1.12.0.yaml ================================================ arch: amd64 platform: aws secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-amd64-1.13.0.yaml ================================================ arch: amd64 platform: aws secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-amd64-1.9.0.yaml ================================================ arch: amd64 platform: aws secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-arm64-1.10.0.yaml ================================================ arch: arm64 platform: aws secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-arm64-1.11.0.yaml ================================================ arch: arm64 platform: aws secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-arm64-1.12.0.yaml ================================================ arch: arm64 platform: aws secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-arm64-1.13.0.yaml ================================================ arch: arm64 platform: aws secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/aws-arm64-1.9.0.yaml ================================================ arch: arm64 platform: aws secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-amd64-1.10.0.yaml ================================================ arch: amd64 platform: azure secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-amd64-1.11.0.yaml ================================================ arch: amd64 platform: azure secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-amd64-1.12.0.yaml ================================================ arch: amd64 platform: azure secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-amd64-1.13.0.yaml ================================================ arch: amd64 platform: azure secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-amd64-1.9.0.yaml ================================================ arch: amd64 platform: azure secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-arm64-1.10.0.yaml ================================================ arch: arm64 platform: azure secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-arm64-1.11.0.yaml ================================================ arch: arm64 platform: azure secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-arm64-1.12.0.yaml ================================================ arch: arm64 platform: azure secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-arm64-1.13.0.yaml ================================================ arch: arm64 platform: azure secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/azure-arm64-1.9.0.yaml ================================================ arch: arm64 platform: azure secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: vhd diskFormatOptions: subformat=fixed,force_size bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-amd64-1.10.0.yaml ================================================ arch: amd64 platform: cloudstack secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-amd64-1.11.0.yaml ================================================ arch: amd64 platform: cloudstack secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-amd64-1.12.0.yaml ================================================ arch: amd64 platform: cloudstack secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-amd64-1.13.0.yaml ================================================ arch: amd64 platform: cloudstack secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-amd64-1.9.0.yaml ================================================ arch: amd64 platform: cloudstack secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-arm64-1.10.0.yaml ================================================ arch: arm64 platform: cloudstack secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-arm64-1.11.0.yaml ================================================ arch: arm64 platform: cloudstack secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-arm64-1.12.0.yaml ================================================ arch: arm64 platform: cloudstack secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-arm64-1.13.0.yaml ================================================ arch: arm64 platform: cloudstack secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/cloudstack-arm64-1.9.0.yaml ================================================ arch: arm64 platform: cloudstack secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-amd64-1.10.0.yaml ================================================ arch: amd64 platform: digital-ocean secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-amd64-1.11.0.yaml ================================================ arch: amd64 platform: digital-ocean secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-amd64-1.12.0.yaml ================================================ arch: amd64 platform: digital-ocean secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-amd64-1.13.0.yaml ================================================ arch: amd64 platform: digital-ocean secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-amd64-1.9.0.yaml ================================================ arch: amd64 platform: digital-ocean secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-arm64-1.10.0.yaml ================================================ arch: arm64 platform: digital-ocean secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-arm64-1.11.0.yaml ================================================ arch: arm64 platform: digital-ocean secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-arm64-1.12.0.yaml ================================================ arch: arm64 platform: digital-ocean secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-arm64-1.13.0.yaml ================================================ arch: arm64 platform: digital-ocean secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/digital-ocean-arm64-1.9.0.yaml ================================================ arch: arm64 platform: digital-ocean secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .gz ================================================ FILE: pkg/imager/profile/testdata/exoscale-amd64-1.10.0.yaml ================================================ arch: amd64 platform: exoscale secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 11787042816 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-amd64-1.11.0.yaml ================================================ arch: amd64 platform: exoscale secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 13884194816 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-amd64-1.12.0.yaml ================================================ arch: amd64 platform: exoscale secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 13884194816 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-amd64-1.13.0.yaml ================================================ arch: amd64 platform: exoscale secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 13884194816 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-amd64-1.9.0.yaml ================================================ arch: amd64 platform: exoscale secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 10737418240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-arm64-1.10.0.yaml ================================================ arch: arm64 platform: exoscale secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 10737418240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-arm64-1.11.0.yaml ================================================ arch: arm64 platform: exoscale secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11785994240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-arm64-1.12.0.yaml ================================================ arch: arm64 platform: exoscale secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11785994240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-arm64-1.13.0.yaml ================================================ arch: arm64 platform: exoscale secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11785994240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/exoscale-arm64-1.9.0.yaml ================================================ arch: arm64 platform: exoscale secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 10737418240 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/gcp-amd64-1.10.0.yaml ================================================ arch: amd64 platform: gcp secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-amd64-1.11.0.yaml ================================================ arch: amd64 platform: gcp secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-amd64-1.12.0.yaml ================================================ arch: amd64 platform: gcp secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-amd64-1.13.0.yaml ================================================ arch: amd64 platform: gcp secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-amd64-1.9.0.yaml ================================================ arch: amd64 platform: gcp secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-arm64-1.10.0.yaml ================================================ arch: arm64 platform: gcp secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-arm64-1.11.0.yaml ================================================ arch: arm64 platform: gcp secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-arm64-1.12.0.yaml ================================================ arch: arm64 platform: gcp secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-arm64-1.13.0.yaml ================================================ arch: arm64 platform: gcp secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/gcp-arm64-1.9.0.yaml ================================================ arch: arm64 platform: gcp secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .tar.gz ================================================ FILE: pkg/imager/profile/testdata/hcloud-amd64-1.10.0.yaml ================================================ arch: amd64 platform: hcloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-amd64-1.11.0.yaml ================================================ arch: amd64 platform: hcloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-amd64-1.12.0.yaml ================================================ arch: amd64 platform: hcloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-amd64-1.13.0.yaml ================================================ arch: amd64 platform: hcloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-amd64-1.9.0.yaml ================================================ arch: amd64 platform: hcloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-arm64-1.10.0.yaml ================================================ arch: arm64 platform: hcloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-arm64-1.11.0.yaml ================================================ arch: arm64 platform: hcloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-arm64-1.12.0.yaml ================================================ arch: arm64 platform: hcloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-arm64-1.13.0.yaml ================================================ arch: arm64 platform: hcloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/hcloud-arm64-1.9.0.yaml ================================================ arch: arm64 platform: hcloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/installer-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/installer-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/iso-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: iso outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/metal-uki-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/metal-uki-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/nocloud-amd64-1.10.0.yaml ================================================ arch: amd64 platform: nocloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-amd64-1.11.0.yaml ================================================ arch: amd64 platform: nocloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-amd64-1.12.0.yaml ================================================ arch: amd64 platform: nocloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-amd64-1.13.0.yaml ================================================ arch: amd64 platform: nocloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-amd64-1.9.0.yaml ================================================ arch: amd64 platform: nocloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-arm64-1.10.0.yaml ================================================ arch: arm64 platform: nocloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-arm64-1.11.0.yaml ================================================ arch: arm64 platform: nocloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-arm64-1.12.0.yaml ================================================ arch: arm64 platform: nocloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-arm64-1.13.0.yaml ================================================ arch: arm64 platform: nocloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/nocloud-arm64-1.9.0.yaml ================================================ arch: arm64 platform: nocloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-amd64-1.10.0.yaml ================================================ arch: amd64 platform: opennebula secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-amd64-1.11.0.yaml ================================================ arch: amd64 platform: opennebula secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-amd64-1.12.0.yaml ================================================ arch: amd64 platform: opennebula secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-amd64-1.13.0.yaml ================================================ arch: amd64 platform: opennebula secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-amd64-1.9.0.yaml ================================================ arch: amd64 platform: opennebula secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-arm64-1.10.0.yaml ================================================ arch: arm64 platform: opennebula secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-arm64-1.11.0.yaml ================================================ arch: arm64 platform: opennebula secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-arm64-1.12.0.yaml ================================================ arch: arm64 platform: opennebula secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-arm64-1.13.0.yaml ================================================ arch: arm64 platform: opennebula secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/opennebula-arm64-1.9.0.yaml ================================================ arch: arm64 platform: opennebula secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-amd64-1.10.0.yaml ================================================ arch: amd64 platform: openstack secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-amd64-1.11.0.yaml ================================================ arch: amd64 platform: openstack secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-amd64-1.12.0.yaml ================================================ arch: amd64 platform: openstack secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-amd64-1.13.0.yaml ================================================ arch: amd64 platform: openstack secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-amd64-1.9.0.yaml ================================================ arch: amd64 platform: openstack secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-arm64-1.10.0.yaml ================================================ arch: arm64 platform: openstack secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-arm64-1.11.0.yaml ================================================ arch: arm64 platform: openstack secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-arm64-1.12.0.yaml ================================================ arch: arm64 platform: openstack secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-arm64-1.13.0.yaml ================================================ arch: arm64 platform: openstack secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/openstack-arm64-1.9.0.yaml ================================================ arch: arm64 platform: openstack secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-amd64-1.10.0.yaml ================================================ arch: amd64 platform: oracle secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-amd64-1.11.0.yaml ================================================ arch: amd64 platform: oracle secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-amd64-1.12.0.yaml ================================================ arch: amd64 platform: oracle secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-amd64-1.13.0.yaml ================================================ arch: amd64 platform: oracle secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-amd64-1.9.0.yaml ================================================ arch: amd64 platform: oracle secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-arm64-1.10.0.yaml ================================================ arch: arm64 platform: oracle secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-arm64-1.11.0.yaml ================================================ arch: arm64 platform: oracle secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-arm64-1.12.0.yaml ================================================ arch: arm64 platform: oracle secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-arm64-1.13.0.yaml ================================================ arch: arm64 platform: oracle secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/oracle-arm64-1.9.0.yaml ================================================ arch: arm64 platform: oracle secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: qcow2 diskFormatOptions: cluster_size=8k bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-amd64-1.10.0.yaml ================================================ arch: amd64 platform: scaleway secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 2356150272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-amd64-1.11.0.yaml ================================================ arch: amd64 platform: scaleway secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-amd64-1.12.0.yaml ================================================ arch: amd64 platform: scaleway secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-amd64-1.13.0.yaml ================================================ arch: amd64 platform: scaleway secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 4453302272 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-amd64-1.9.0.yaml ================================================ arch: amd64 platform: scaleway secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-arm64-1.10.0.yaml ================================================ arch: arm64 platform: scaleway secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-arm64-1.11.0.yaml ================================================ arch: arm64 platform: scaleway secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-arm64-1.12.0.yaml ================================================ arch: arm64 platform: scaleway secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-arm64-1.13.0.yaml ================================================ arch: arm64 platform: scaleway secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/scaleway-arm64-1.9.0.yaml ================================================ arch: arm64 platform: scaleway secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-installer-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: installer outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-iso-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: iso isoOptions: sdBootEnrollKeys: if-safe outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 2355101696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: image imageOptions: diskSize: 1306525696 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-amd64-1.10.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-amd64-1.11.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-amd64-1.12.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-amd64-1.13.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-amd64-1.9.0.yaml ================================================ arch: amd64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-arm64-1.10.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-arm64-1.11.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-arm64-1.12.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-arm64-1.13.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/secureboot-metal-uki-arm64-1.9.0.yaml ================================================ arch: arm64 platform: metal secureboot: true version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 secureboot: secureBootSigner: keyPath: /secureboot/uki-signing-key.pem certPath: /secureboot/uki-signing-cert.pem pcrSigner: keyPath: /secureboot/pcr-signing-key.pem output: kind: uki outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/upcloud-amd64-1.10.0.yaml ================================================ arch: amd64 platform: upcloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-amd64-1.11.0.yaml ================================================ arch: amd64 platform: upcloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-amd64-1.12.0.yaml ================================================ arch: amd64 platform: upcloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-amd64-1.13.0.yaml ================================================ arch: amd64 platform: upcloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-amd64-1.9.0.yaml ================================================ arch: amd64 platform: upcloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-arm64-1.10.0.yaml ================================================ arch: arm64 platform: upcloud secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-arm64-1.11.0.yaml ================================================ arch: arm64 platform: upcloud secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-arm64-1.12.0.yaml ================================================ arch: arm64 platform: upcloud secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-arm64-1.13.0.yaml ================================================ arch: arm64 platform: upcloud secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/upcloud-arm64-1.9.0.yaml ================================================ arch: arm64 platform: upcloud secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vmware-amd64-1.10.0.yaml ================================================ arch: amd64 platform: vmware secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: ova bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-amd64-1.11.0.yaml ================================================ arch: amd64 platform: vmware secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: ova bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-amd64-1.12.0.yaml ================================================ arch: amd64 platform: vmware secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: ova bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-amd64-1.13.0.yaml ================================================ arch: amd64 platform: vmware secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: ova bootloader: dual-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-amd64-1.9.0.yaml ================================================ arch: amd64 platform: vmware secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: ova bootloader: grub outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-arm64-1.10.0.yaml ================================================ arch: arm64 platform: vmware secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: ova bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-arm64-1.11.0.yaml ================================================ arch: arm64 platform: vmware secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: ova bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-arm64-1.12.0.yaml ================================================ arch: arm64 platform: vmware secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: ova bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-arm64-1.13.0.yaml ================================================ arch: arm64 platform: vmware secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: ova bootloader: sd-boot outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vmware-arm64-1.9.0.yaml ================================================ arch: arm64 platform: vmware secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: ova bootloader: grub outFormat: raw ================================================ FILE: pkg/imager/profile/testdata/vultr-amd64-1.10.0.yaml ================================================ arch: amd64 platform: vultr secureboot: false version: 1.10.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 9639559168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-amd64-1.11.0.yaml ================================================ arch: amd64 platform: vultr secureboot: false version: 1.11.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-amd64-1.12.0.yaml ================================================ arch: amd64 platform: vultr secureboot: false version: 1.12.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-amd64-1.13.0.yaml ================================================ arch: amd64 platform: vultr secureboot: false version: 1.13.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 11736711168 diskFormat: raw bootloader: dual-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-amd64-1.9.0.yaml ================================================ arch: amd64 platform: vultr secureboot: false version: 1.9.0 input: kernel: path: /usr/install/amd64/vmlinuz initramfs: path: /usr/install/amd64/initramfs.xz sdStub: path: /usr/install/amd64/systemd-stub.efi sdBoot: path: /usr/install/amd64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-arm64-1.10.0.yaml ================================================ arch: arm64 platform: vultr secureboot: false version: 1.10.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.10.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-arm64-1.11.0.yaml ================================================ arch: arm64 platform: vultr secureboot: false version: 1.11.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.11.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-arm64-1.12.0.yaml ================================================ arch: arm64 platform: vultr secureboot: false version: 1.12.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.12.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-arm64-1.13.0.yaml ================================================ arch: arm64 platform: vultr secureboot: false version: 1.13.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer-base:1.13.0 output: kind: image imageOptions: diskSize: 9638510592 diskFormat: raw bootloader: sd-boot outFormat: .zst ================================================ FILE: pkg/imager/profile/testdata/vultr-arm64-1.9.0.yaml ================================================ arch: arm64 platform: vultr secureboot: false version: 1.9.0 input: kernel: path: /usr/install/arm64/vmlinuz initramfs: path: /usr/install/arm64/initramfs.xz sdStub: path: /usr/install/arm64/systemd-stub.efi sdBoot: path: /usr/install/arm64/systemd-boot.efi baseInstaller: imageRef: ghcr.io/siderolabs/installer:1.9.0 output: kind: image imageOptions: diskSize: 8589934592 diskFormat: raw bootloader: grub outFormat: .zst ================================================ FILE: pkg/imager/progress.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package imager import ( "fmt" "github.com/siderolabs/talos/pkg/reporter" ) // progressPrintf wraps a reporter.Reporter to report progress via Printf logging. func progressPrintf(report *reporter.Reporter, status reporter.Update) func(format string, args ...any) { return func(format string, args ...any) { msg := status.Message extra := fmt.Sprintf(format, args...) if extra != "" { msg += "\n\t" + extra } report.Report(reporter.Update{ Message: msg, Status: status.Status, }) } } ================================================ FILE: pkg/imager/qemuimg/qemuimg.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package qemuimg provides a wrapper around qemu-img. package qemuimg import ( "context" "os" "github.com/siderolabs/go-cmd/pkg/cmd" ) // Convert converts an image from one format to another. func Convert(ctx context.Context, inputFmt, outputFmt, options, path string, printf func(string, ...any)) error { src := path + ".in" dest := path printf("converting %s to %s", inputFmt, outputFmt) if err := os.Rename(path, src); err != nil { return err } if _, err := cmd.RunWithOptions(ctx, "qemu-img", []string{"convert", "-f", inputFmt, "-O", outputFmt, "-o", options, src, dest}); err != nil { return err } return os.Remove(src) } // Resize an image. func Resize(ctx context.Context, file, size string) error { if _, err := cmd.RunWithOptions(ctx, "qemu-img", []string{"resize", file, size}); err != nil { return err } return nil } ================================================ FILE: pkg/imager/utils/copy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package utils import ( "fmt" "io" "os" "path/filepath" "github.com/siderolabs/gen/pair/ordered" ) // CopyInstruction describes a file copy operation. type CopyInstruction = ordered.Pair[string, string] // SourceDestination returns a CopyInstruction that copies src to dest. func SourceDestination(src, dest string) CopyInstruction { return ordered.MakePair(src, dest) } // CopyFiles copies files according to the given instructions. func CopyFiles(printf func(string, ...any), instructions ...CopyInstruction) error { for _, instruction := range instructions { if err := func(instruction CopyInstruction) error { src, dest := instruction.F1, instruction.F2 if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { return err } printf("copying %s to %s", src, dest) from, err := os.Open(src) if err != nil { return err } //nolint:errcheck defer from.Close() to, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } //nolint:errcheck defer to.Close() _, err = io.Copy(to, from) return err }(instruction); err != nil { return fmt.Errorf("error copying %s -> %s: %w", instruction.F1, instruction.F2, err) } } return nil } // CopyReaderInstruction describes a reader copy operation. type CopyReaderInstruction struct { Reader io.Reader Dest string } // ReaderDestination returns a CopyReaderInstruction that copies reader to dest. func ReaderDestination(reader io.Reader, dest string) CopyReaderInstruction { return CopyReaderInstruction{Reader: reader, Dest: dest} } // CopyReader copies readers according to the given instructions. func CopyReader(printf func(string, ...any), instructions ...CopyReaderInstruction) error { for _, instruction := range instructions { if err := func(instruction CopyReaderInstruction) error { dest := instruction.Dest if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { return err } printf("copying from io reader to %s", dest) to, err := os.OpenFile(dest, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o666) if err != nil { return err } //nolint:errcheck defer to.Close() _, err = io.Copy(to, instruction.Reader) return err }(instruction); err != nil { return fmt.Errorf("error copying reader -> %s: %w", instruction.Dest, err) } } return nil } ================================================ FILE: pkg/imager/utils/epoch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package utils import ( "os" "strconv" ) // SourceDateEpoch returns parsed value of SOURCE_DATE_EPOCH. func SourceDateEpoch() (int64, bool, error) { epoch, ok := os.LookupEnv("SOURCE_DATE_EPOCH") if !ok { return 0, false, nil } epochInt, err := strconv.ParseInt(epoch, 10, 64) if err != nil { return 0, false, err } return epochInt, true, nil } ================================================ FILE: pkg/imager/utils/raw.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package utils import ( "fmt" "os" "syscall" "github.com/dustin/go-humanize" ) // CreateRawDisk creates a raw disk image of the specified size. func CreateRawDisk(printf func(string, ...any), path string, diskSize int64, allocate bool) error { printf("creating raw disk of size %s", humanize.Bytes(uint64(diskSize))) f, err := os.Create(path) if err != nil { return fmt.Errorf("failed to create raw disk: %w", err) } defer f.Close() //nolint:errcheck if err = f.Truncate(diskSize); err != nil { return fmt.Errorf("failed to create raw disk: %w", err) } if allocate { if err = syscall.Fallocate(int(f.Fd()), 0, 0, diskSize); err != nil { fmt.Fprintf(os.Stderr, "WARNING: failed to preallocate disk space for %q (size %d): %s", path, diskSize, err) } } return f.Close() } ================================================ FILE: pkg/imager/utils/touch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package utils import ( "io/fs" "os" "path/filepath" "time" ) // TouchFiles updates mtime for all the files under root if SOURCE_DATE_EPOCH is set. func TouchFiles(printf func(string, ...any), root string) error { epochInt, ok, err := SourceDateEpoch() if err != nil { return err } if !ok { return nil } timestamp := time.Unix(epochInt, 0) printf("changing timestamps under %q to %s", root, timestamp) return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } return os.Chtimes(path, timestamp, timestamp) }) } ================================================ FILE: pkg/imager/utils/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package utils contains small utilities used by the imager. // //nolint:revive package utils ================================================ FILE: pkg/imager/vmdkconvert/vmdkconvert.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vmdkconvert provides a wrapper around the vmdk-convert tool from open-vmdk. package vmdkconvert import ( "context" "os" "github.com/siderolabs/go-cmd/pkg/cmd" ) // ConvertToStreamOptimizedVMDK converts a raw / flat / sparse disk image to a stream optimized VMDK. func ConvertToStreamOptimizedVMDK(ctx context.Context, path string, printf func(string, ...any)) error { src := path + ".in" dest := path printf("converting disk image to stream optimized vmdk") if err := os.Rename(path, src); err != nil { return err } if _, err := cmd.RunWithOptions(ctx, "vmdk-convert", []string{src, dest}); err != nil { return err } return os.Remove(src) } ================================================ FILE: pkg/images/images.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package images provides some default images. package images import ( "github.com/siderolabs/talos/pkg/machinery/gendata" "github.com/siderolabs/talos/pkg/machinery/version" ) var ( // Username is the default registry username. Username = gendata.ImagesUsername // Registry is the default registry. Registry = gendata.ImagesRegistry // DefaultInstallerImageName is the default container image name for // the installer. DefaultInstallerImageName = Username + "/installer" // DefaultInstallerImageRepository is the default container repository for // the installer. DefaultInstallerImageRepository = Registry + "/" + DefaultInstallerImageName // DefaultInstallerImage is the default installer image. DefaultInstallerImage = DefaultInstallerImageRepository + ":" + version.Tag // DefaultTalosImageName is the default container image name for // the talos image. DefaultTalosImageName = Username + "/talos" // DefaultTalosImageRepository is the default container repository for // the talos image. DefaultTalosImageRepository = Registry + "/" + DefaultTalosImageName // DefaultTalosImage is the default talos image. DefaultTalosImage = DefaultTalosImageRepository + ":" + version.Tag // DefaultInstallerBaseImageRepository is the default container repository for // installer-base image. DefaultInstallerBaseImageRepository = Registry + "/" + Username + "/installer-base" // DefaultImagerImageRepository is the default container repository for // imager image. DefaultImagerImageRepository = Registry + "/" + Username + "/imager" // DefaultTalosctlAllImageRepository is the default container repository for // talosctl-all image. DefaultTalosctlAllImageRepository = Registry + "/" + Username + "/talosctl-all" // DefaultOverlaysManifestName is the default container manifest name for // the overlays. DefaultOverlaysManifestName = Username + "/overlays" // DefaultOverlaysManifestRepository is the default container repository for // overlays manifest. DefaultOverlaysManifestRepository = Registry + "/" + DefaultOverlaysManifestName // DefaultExtensionsManifestName is the default container manifest name for // the extensions. DefaultExtensionsManifestName = Username + "/extensions" // DefaultExtensionsManifestRepository is the default container repository for // extensions manifest. DefaultExtensionsManifestRepository = Registry + "/" + DefaultExtensionsManifestName ) ================================================ FILE: pkg/images/list.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package images import ( "fmt" "strings" "github.com/google/go-containerregistry/pkg/name" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Versions holds all the images (and their versions) that are used in Talos. type Versions struct { Etcd name.Tag Flannel name.Tag CoreDNS name.Tag Kubelet name.Tag KubeAPIServer name.Tag KubeControllerManager name.Tag KubeNetworkPolicies name.Tag KubeProxy name.Tag KubeScheduler name.Tag Pause name.Tag } // DefaultSandboxImage is defined as a constant in cri package of containerd, and it's not exported. // // The integration test verifies that our constant is accurate. const DefaultSandboxImage = "registry.k8s.io/pause:3.10.1" // List returns default image versions. func List(config config.Config) Versions { var images Versions images.Etcd = mustParseTag(config.Cluster().Etcd().Image()) images.CoreDNS = mustParseTag(config.Cluster().CoreDNS().Image()) images.Flannel = mustParseTag(fmt.Sprintf("ghcr.io/siderolabs/flannel:%s", constants.FlannelVersion)) // mirrored from docker.io/flannelcni/flannel images.Kubelet = mustParseTag(config.Machine().Kubelet().Image()) images.KubeAPIServer = mustParseTag(config.Cluster().APIServer().Image()) images.KubeControllerManager = mustParseTag(config.Cluster().ControllerManager().Image()) images.KubeNetworkPolicies = mustParseTag(fmt.Sprintf("registry.k8s.io/networking/kube-network-policies:%s", constants.KubeNetworkPoliciesVersion)) images.KubeProxy = mustParseTag(config.Cluster().Proxy().Image()) images.KubeScheduler = mustParseTag(config.Cluster().Scheduler().Image()) images.Pause = mustParseTag(DefaultSandboxImage) return images } // VersionsListOptions allows overriding the default component versions // displayed to the user. // // Any non-empty field value replaces the corresponding default version // when presenting available or selected versions. Fields left empty // will fall back to their built-in defaults. type VersionsListOptions struct { // KubernetesVersion overrides the default Kubernetes version. KubernetesVersion string // CoreDNSVersion overrides the default CoreDNS version. CoreDNSVersion string // EtcdVersion overrides the default etcd version. EtcdVersion string // FlannelVersion overrides the default Flannel version. FlannelVersion string // PauseVersion overrides the default pause container image version. PauseVersion string // KubeNetworkPoliciesVersion overrides the default kube-network-policies version. KubeNetworkPoliciesVersion string } // ListWithOptions returns image versions with overrides. func ListWithOptions(config config.Config, opts VersionsListOptions) Versions { images := List(config) if opts.CoreDNSVersion != "" { images.CoreDNS = images.CoreDNS.Tag(opts.CoreDNSVersion) } if opts.EtcdVersion != "" { images.Etcd = images.Etcd.Tag(opts.EtcdVersion) } if opts.FlannelVersion != "" { images.Flannel = images.Flannel.Tag(opts.FlannelVersion) } if opts.PauseVersion != "" { images.Pause = images.Pause.Tag(opts.PauseVersion) } if opts.KubernetesVersion != "" { images.Kubelet = images.Kubelet.Tag(opts.KubernetesVersion) images.KubeAPIServer = images.KubeAPIServer.Tag(opts.KubernetesVersion) images.KubeControllerManager = images.KubeControllerManager.Tag(opts.KubernetesVersion) images.KubeProxy = images.KubeProxy.Tag(opts.KubernetesVersion) images.KubeScheduler = images.KubeScheduler.Tag(opts.KubernetesVersion) } if opts.KubeNetworkPoliciesVersion != "" { images.KubeNetworkPolicies = images.KubeNetworkPolicies.Tag(opts.KubeNetworkPoliciesVersion) } return images } func mustParseTag(s string) name.Tag { s, _, _ = strings.Cut(s, "@") // ignore digest if present r, err := name.ParseReference(s) if err != nil { panic(err) } t, ok := r.(name.Tag) if !ok { panic(fmt.Sprintf("%T is not name.Tag: %#+v", r, r)) } return t } func mustParseReferenceWithTag(ref, tag string) name.Tag { r, err := name.ParseReference(ref) if err != nil { panic(err) } return r.Context().Tag(tag) } // TalosBundle holds the core images (and their versions) that are used to build Talos. type TalosBundle struct { Installer name.Tag InstallerBase name.Tag Imager name.Tag Talos name.Tag TalosctlAll name.Tag Overlays name.Tag Extensions name.Tag } // ListSourcesFor returns source bundle for specific version. func ListSourcesFor(tag string) TalosBundle { var bundle TalosBundle bundle.Installer = mustParseReferenceWithTag(DefaultInstallerImageRepository, tag) bundle.InstallerBase = mustParseReferenceWithTag(DefaultInstallerBaseImageRepository, tag) bundle.Imager = mustParseReferenceWithTag(DefaultImagerImageRepository, tag) bundle.Talos = mustParseReferenceWithTag(DefaultTalosImageRepository, tag) bundle.TalosctlAll = mustParseReferenceWithTag(DefaultTalosctlAllImageRepository, tag) bundle.Overlays = mustParseReferenceWithTag(DefaultOverlaysManifestRepository, tag) bundle.Extensions = mustParseReferenceWithTag(DefaultExtensionsManifestRepository, tag) return bundle } ================================================ FILE: pkg/kernel/kernel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kernel provides the utilities to interact with the kernel. package kernel import ( "os" "github.com/siderolabs/talos/pkg/machinery/kernel" ) // WriteParam writes a value to a key under /proc/sys. func WriteParam(prop *kernel.Param) error { return os.WriteFile(prop.Path(), []byte(prop.Value), 0o644) } // ReadParam reads a value from a key under /proc/sys. func ReadParam(prop *kernel.Param) ([]byte, error) { return os.ReadFile(prop.Path()) } // DeleteParam deletes a value from a key under /proc/sys. func DeleteParam(prop *kernel.Param) error { return os.Remove(prop.Path()) } ================================================ FILE: pkg/kernel/kspp/kspp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kspp implements KSPP kernel parameters enforcement. package kspp import ( "fmt" "github.com/hashicorp/go-multierror" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/kernel" ) // RequiredKSPPKernelParameters is the set of kernel parameters required to // satisfy the KSPP. var RequiredKSPPKernelParameters = procfs.Parameters{ // init_on_alloc and init_on_free are not enforced, as they default to '1' in kernel config // this way they can be overridden via installer extra args in case of severe performance issues // procfs.NewParameter("init_on_alloc").Append("1"), // procfs.NewParameter("init_on_free").Append("1"), procfs.NewParameter("slab_nomerge").Append(""), procfs.NewParameter("pti").Append("on"), } // EnforceKSPPKernelParameters verifies that all required KSPP kernel // parameters are present with the right value. func EnforceKSPPKernelParameters() error { var result *multierror.Error for _, values := range RequiredKSPPKernelParameters { var val *string if val = procfs.ProcCmdline().Get(values.Key()).First(); val == nil { result = multierror.Append(result, fmt.Errorf("KSPP kernel parameter %s is required", values.Key())) continue } expected := values.First() if *val != *expected { result = multierror.Append(result, fmt.Errorf("KSPP kernel parameter %s was found with value %s, expected %s", values.Key(), *val, *expected)) } } return result.ErrorOrNil() } // GetKernelParams returns the list of KSPP kernel parameters. func GetKernelParams() []*kernel.Param { return []*kernel.Param{ { Key: "proc.sys.dev.tty.ldisc_autoload", Value: "0", }, { Key: "proc.sys.dev.tty.legacy_tiocsti", Value: "0", }, { Key: "proc.sys.fs.protected_symlinks", Value: "1", }, { Key: "proc.sys.fs.protected_hardlinks", Value: "1", }, { Key: "proc.sys.fs.protected_fifos", Value: "2", }, { Key: "proc.sys.fs.protected_regular", Value: "2", }, { Key: "proc.sys.fs.suid_dumpable", Value: "0", }, { Key: "proc.sys.kernel.kptr_restrict", Value: "2", }, { Key: "proc.sys.kernel.dmesg_restrict", Value: "1", }, { Key: "proc.sys.kernel.perf_event_paranoid", Value: "3", }, { Key: "proc.sys.kernel.randomize_va_space", Value: "2", }, { // Bumping this to 3 (https://www.kernel.org/doc/Documentation/security/Yama.txt) // breaks Kubernetes pods with user namespaces, which are not enabled by default, but still supported. Key: "proc.sys.kernel.yama.ptrace_scope", Value: "2", }, { Key: "proc.sys.user.max_user_namespaces", Value: "0", }, { Key: "proc.sys.kernel.unprivileged_bpf_disabled", Value: "1", }, { Key: "proc.sys.net.core.bpf_jit_harden", Value: "2", }, { // Disable dangerous userfaultfd usage. Key: "proc.sys.vm.unprivileged_userfaultfd", Value: "0", }, } } ================================================ FILE: pkg/kubeconfig/generate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeconfig import ( "bytes" stdlibx509 "crypto/x509" "fmt" "io" "net/url" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "github.com/siderolabs/talos/pkg/machinery/config/config" ) // GenerateAdminInput is the interface for the GenerateAdmin function. // // This interface is implemented by config.Cluster(). type GenerateAdminInput interface { Name() string Endpoint() *url.URL IssuingCA() *x509.PEMEncodedCertificateAndKey AcceptedCAs() []*x509.PEMEncodedCertificate AdminKubeconfig() config.AdminKubeconfig } // GenerateAdmin generates admin kubeconfig for the cluster. func GenerateAdmin(config GenerateAdminInput, out io.Writer) error { acceptedCAs := config.AcceptedCAs() if config.IssuingCA() != nil { acceptedCAs = append(acceptedCAs, &x509.PEMEncodedCertificate{Crt: config.IssuingCA().Crt}) } return Generate( &GenerateInput{ ClusterName: config.Name(), IssuingCA: config.IssuingCA(), AcceptedCAs: acceptedCAs, CertificateLifetime: config.AdminKubeconfig().CertLifetime(), CommonName: config.AdminKubeconfig().CommonName(), Organization: config.AdminKubeconfig().CertOrganization(), Endpoint: config.Endpoint().String(), Username: "admin", ContextName: "admin", }, out, ) } // GenerateInput are input parameters for Generate. type GenerateInput struct { ClusterName string IssuingCA *x509.PEMEncodedCertificateAndKey AcceptedCAs []*x509.PEMEncodedCertificate CertificateLifetime time.Duration CommonName string Organization string Endpoint string Username string ContextName string } const allowedTimeSkew = 10 * time.Second // Generate a kubeconfig for the cluster from the given Input. func Generate(in *GenerateInput, out io.Writer) error { k8sCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(in.IssuingCA) if err != nil { return fmt.Errorf("error getting Kubernetes CA: %w", err) } clientCert, err := x509.NewKeyPair(k8sCA, x509.CommonName(in.CommonName), x509.Organization(in.Organization), x509.NotBefore(time.Now().Add(-allowedTimeSkew)), x509.NotAfter(time.Now().Add(in.CertificateLifetime)), x509.KeyUsage(stdlibx509.KeyUsageDigitalSignature|stdlibx509.KeyUsageKeyEncipherment), x509.ExtKeyUsage([]stdlibx509.ExtKeyUsage{ stdlibx509.ExtKeyUsageClientAuth, }), ) if err != nil { return fmt.Errorf("error generating Kubernetes client certificate: %w", err) } clientCertPEM := x509.NewCertificateAndKeyFromKeyPair(clientCert) serverCAs := bytes.Join(xslices.Map(in.AcceptedCAs, func(ca *x509.PEMEncodedCertificate) []byte { return ca.Crt }), nil) cfg := clientcmdapi.Config{ APIVersion: "v1", Kind: "Config", Clusters: map[string]*clientcmdapi.Cluster{ in.ClusterName: { Server: in.Endpoint, CertificateAuthorityData: serverCAs, }, }, AuthInfos: map[string]*clientcmdapi.AuthInfo{ in.Username + "@" + in.ClusterName: { ClientCertificateData: clientCertPEM.Crt, ClientKeyData: clientCertPEM.Key, }, }, Contexts: map[string]*clientcmdapi.Context{ in.ContextName + "@" + in.ClusterName: { Cluster: in.ClusterName, Namespace: "default", AuthInfo: in.Username + "@" + in.ClusterName, }, }, CurrentContext: in.ContextName + "@" + in.ClusterName, } marshaled, err := clientcmd.Write(cfg) if err != nil { return err } _, err = out.Write(marshaled) return err } ================================================ FILE: pkg/kubeconfig/generate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeconfig_test import ( "bytes" "fmt" "net/url" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/suite" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/pkg/kubeconfig" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) type GenerateSuite struct { suite.Suite } func (suite *GenerateSuite) TestGenerateAdmin() { for _, rsa := range []bool{true, false} { suite.Run(fmt.Sprintf("RSA=%v", rsa), func() { ca, err := x509.NewSelfSignedCertificateAuthority(x509.RSA(rsa)) suite.Require().NoError(err) u, err := url.Parse("http://localhost:3333/api") suite.Require().NoError(err) cfg := &v1alpha1.ClusterConfig{ ClusterName: "talos1", ClusterCA: &x509.PEMEncodedCertificateAndKey{ Crt: ca.CrtPEM, Key: ca.KeyPEM, }, ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: u, }, }, AdminKubeconfigConfig: &v1alpha1.AdminKubeconfigConfig{ AdminKubeconfigCertLifetime: time.Hour, }, } var buf bytes.Buffer suite.Require().NoError(kubeconfig.GenerateAdmin(cfg, &buf)) // verify config via k8s client config, err := clientcmd.Load(buf.Bytes()) suite.Require().NoError(err) suite.Assert().NoError(clientcmd.ConfirmUsable(*config, fmt.Sprintf("admin@%s", cfg.ClusterName))) }) } } func (suite *GenerateSuite) TestGenerate() { ca, err := x509.NewSelfSignedCertificateAuthority(x509.RSA(false)) suite.Require().NoError(err) k8sCA := x509.NewCertificateAndKeyFromCertificateAuthority(ca) input := kubeconfig.GenerateInput{ ClusterName: "foo", IssuingCA: k8sCA, AcceptedCAs: []*x509.PEMEncodedCertificate{{Crt: k8sCA.Crt}}, CertificateLifetime: time.Hour, CommonName: "system:kube-controller-manager", Organization: "system:kube-controller-manager", Endpoint: "https://localhost:6443/", Username: "kube-controller-manager", ContextName: "kube-controller-manager", } var buf bytes.Buffer suite.Require().NoError(kubeconfig.Generate(&input, &buf)) suite.T().Logf("Generated kubeconfig:\n%s", buf.String()) // verify config via k8s client config, err := clientcmd.Load(buf.Bytes()) suite.Require().NoError(err) suite.Assert().NoError(clientcmd.ConfirmUsable(*config, "kube-controller-manager@foo")) } func TestGenerateSuite(t *testing.T) { suite.Run(t, new(GenerateSuite)) } ================================================ FILE: pkg/kubeconfig/kubeconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubeconfig provides Kubernetes config file generation. package kubeconfig ================================================ FILE: pkg/kubernetes/errors.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "github.com/siderolabs/go-kubernetes/kubernetes" ) // IsRetryableError returns true if this Kubernetes API should be retried. func IsRetryableError(err error) bool { return kubernetes.IsRetryableError(err) } ================================================ FILE: pkg/kubernetes/inject/serviceaccount.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package inject import ( "bytes" "errors" "fmt" "io" "path/filepath" "strings" "go.yaml.in/yaml/v4" appsv1 "k8s.io/api/apps/v1" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer/json" "github.com/siderolabs/talos/pkg/machinery/constants" ) const ( injectToEnv = false volumeName = "talos-secrets" nameSuffix = "-talos-secrets" apiVersionField = "apiVersion" kindField = "kind" metadataField = "metadata" namespaceField = "namespace" nameField = "name" yamlSeparator = "---\n" ) // ServiceAccount takes a YAML with Kubernetes manifests and requested Talos roles as input // and injects Talos service accounts into them. // //nolint:gocyclo func ServiceAccount(reader io.Reader, roles []string) ([]byte, error) { var err error objectSerializer := json.NewSerializerWithOptions( json.DefaultMetaFactory, nil, nil, json.SerializerOptions{ Yaml: true, Pretty: true, Strict: true, }, ) seenResourceIDs := make(map[string]struct{}) var buf bytes.Buffer decoder := yaml.NewDecoder(reader) // loop over all documents in a possibly YAML with multiple documents separated by --- for { var raw map[string]any err = decoder.Decode(&raw) if errors.Is(err, io.EOF) { break } if err != nil { return nil, err } if raw == nil { continue } var injected metav1.Object injected, err = injectToObject(raw) if err != nil { // not a known resource with a PodSpec // if this is already a Talos ServiceAccount resource we have seen, // we keep it only if we have not seen it yet (means it belongs to the user, not injected by us) id := readResourceIDFromServiceAccount(raw) if id != "" { if _, ok := seenResourceIDs[id]; ok { continue } seenResourceIDs[id] = struct{}{} } err = yaml.NewEncoder(&buf).Encode(raw) if err != nil { return nil, err } buf.WriteString(yamlSeparator) continue } // injectable resource type which contains a PodSpec runtimeObject, ok := injected.(runtime.Object) if !ok { return nil, errors.New("injected object is not a runtime.Object") } err = objectSerializer.Encode(runtimeObject, &buf) if err != nil { return nil, err } buf.WriteString(yamlSeparator) id := readResourceIDFromObject(injected) // inject service account for the resource if _, ok = seenResourceIDs[id]; !ok { sa := buildServiceAccount(injected.GetNamespace(), fmt.Sprintf("%s%s", injected.GetName(), nameSuffix), roles) err = yaml.NewEncoder(&buf).Encode(sa) if err != nil { return nil, err } buf.WriteString(yamlSeparator) // mark resource as seen seenResourceIDs[id] = struct{}{} } } return buf.Bytes(), nil } func buildServiceAccount(namespace string, name string, roles []string) map[string]any { metadata := map[string]any{ nameField: name, } if namespace != "" { metadata[namespaceField] = namespace } return map[string]any{ apiVersionField: fmt.Sprintf( "%s/%s", constants.ServiceAccountResourceGroup, constants.ServiceAccountResourceVersion, ), kindField: constants.ServiceAccountResourceKind, metadataField: metadata, "spec": map[string]any{ "roles": roles, }, } } func isServiceAccount(raw map[string]any) bool { apiVersionKind, err := readResourceAPIVersionKind(raw) if err != nil { return false } return apiVersionKind == fmt.Sprintf( "%s/%s/%s", constants.ServiceAccountResourceGroup, constants.ServiceAccountResourceVersion, constants.ServiceAccountResourceKind, ) } // injectToDocument takes a single YAML document and attempts to inject a ServiceAccount // into it if it is a known Kubernetes resource type which contains a corev1.PodSpec. func injectToObject(raw map[string]any) (metav1.Object, error) { var err error apiVersionKind, err := readResourceAPIVersionKind(raw) if err != nil { return nil, err } switch apiVersionKind { case "v1/Pod": return injectToPodSpecObject[corev1.Pod](raw, func(obj *corev1.Pod) *corev1.PodSpec { return &obj.Spec }) case "apps/v1/Deployment": return injectToPodSpecObject[appsv1.Deployment](raw, func(obj *appsv1.Deployment) *corev1.PodSpec { return &obj.Spec.Template.Spec }) case "apps/v1/StatefulSet": return injectToPodSpecObject[appsv1.StatefulSet](raw, func(obj *appsv1.StatefulSet) *corev1.PodSpec { return &obj.Spec.Template.Spec }) case "apps/v1/DaemonSet": return injectToPodSpecObject[appsv1.DaemonSet](raw, func(obj *appsv1.DaemonSet) *corev1.PodSpec { return &obj.Spec.Template.Spec }) case "batch/v1/Job": return injectToPodSpecObject[batchv1.Job](raw, func(obj *batchv1.Job) *corev1.PodSpec { return &obj.Spec.Template.Spec }) case "batch/v1/CronJob": return injectToPodSpecObject[batchv1.CronJob](raw, func(obj *batchv1.CronJob) *corev1.PodSpec { return &obj.Spec.JobTemplate.Spec.Template.Spec }) } return nil, fmt.Errorf("unsupported object type: %s", apiVersionKind) } func injectToPodSpecObject[T any](raw map[string]any, podSpecFunc func(*T) *corev1.PodSpec) (*T, error) { objectName, nameFound, err := unstructured.NestedString(raw, metadataField, nameField) if err != nil { return nil, err } if !nameFound { return nil, errors.New("object has no name") } var obj T err = runtime.DefaultUnstructuredConverter.FromUnstructuredWithValidation(raw, &obj, false) if err != nil { return nil, err } injectToPodSpec(fmt.Sprintf("%s%s", objectName, nameSuffix), podSpecFunc(&obj)) return &obj, nil } func readResourceAPIVersionKind(raw map[string]any) (string, error) { apiVersion, found, err := unstructured.NestedString(raw, apiVersionField) if err != nil { return "", err } if !found { return "", fmt.Errorf("%s not found", apiVersionField) } kind, found, err := unstructured.NestedString(raw, kindField) if err != nil { return "", err } if !found { return "", fmt.Errorf("%s not found", kindField) } return fmt.Sprintf("%s/%s", apiVersion, kind), nil } func readResourceIDFromObject(obj metav1.Object) string { if obj.GetNamespace() == "" { return obj.GetName() } return fmt.Sprintf("%s/%s", obj.GetNamespace(), obj.GetName()) } func readResourceIDFromServiceAccount(raw map[string]any) string { if !isServiceAccount(raw) { return "" } name, nameFound, err := unstructured.NestedString(raw, metadataField, nameField) if err != nil || !nameFound { return "" } nameTrimmed := strings.TrimSuffix(name, nameSuffix) ns, nsFound, err := unstructured.NestedString(raw, metadataField, namespaceField) if err != nil { return "" } if nsFound { return fmt.Sprintf("%s/%s", ns, nameTrimmed) } return nameTrimmed } func injectToPodSpec(secretName string, podSpec *corev1.PodSpec) { podSpec.Volumes = injectToVolumes(secretName, podSpec.Volumes) podSpec.InitContainers = injectToContainers(podSpec.InitContainers) podSpec.Containers = injectToContainers(podSpec.Containers) } func injectToVolumes(name string, volumes []corev1.Volume) []corev1.Volume { result := make([]corev1.Volume, 0, len(volumes)) for _, volume := range volumes { if volume.Name != volumeName { result = append(result, volume) } } result = append(result, corev1.Volume{ Name: volumeName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ SecretName: name, }, }, }) return result } func injectToContainers(containers []corev1.Container) []corev1.Container { result := make([]corev1.Container, 0, len(containers)) for _, container := range containers { injectToContainer(&container) result = append(result, container) } return result } func injectToContainer(container *corev1.Container) { volumeMounts := make([]corev1.VolumeMount, 0, len(container.VolumeMounts)) for _, mount := range container.VolumeMounts { if mount.Name != volumeName { volumeMounts = append(volumeMounts, mount) } } volumeMounts = append(volumeMounts, corev1.VolumeMount{ Name: volumeName, MountPath: constants.ServiceAccountMountPath, }) container.VolumeMounts = volumeMounts if injectToEnv { container.Env = injectToContainerEnv(container.Env) } } func injectToContainerEnv(env []corev1.EnvVar) []corev1.EnvVar { result := make([]corev1.EnvVar, 0, len(env)) for _, envVar := range env { if envVar.Name != constants.TalosConfigEnvVar { result = append(result, envVar) } } result = append(result, corev1.EnvVar{ Name: constants.TalosConfigEnvVar, Value: filepath.Join(constants.ServiceAccountMountPath, constants.TalosconfigFilename), }) return result } ================================================ FILE: pkg/kubernetes/klog.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "io" "k8s.io/klog/v2" ) func init() { // Kubernetes client likes to do calls to `klog` in random places which are not configurable. // For Talos this means those logs are going to the console which doesn't look good. klog.EnableContextualLogging(false) klog.SetOutput(io.Discard) klog.LogToStderr(false) } ================================================ FILE: pkg/kubernetes/kubelet/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubelet provides minimal client for the kubelet API. package kubelet import ( "context" "encoding/json" "errors" "fmt" "io/fs" "os" "path/filepath" "time" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Client is a kubelet API client. // // Client can only talk to the local kubelet on the same node. type Client struct { client *rest.RESTClient } // NewClient creates new kubelet API client. func NewClient(nodename string, clientCert, clientKey, caPEM []byte) (*Client, error) { config := &rest.Config{ Host: fmt.Sprintf("https://127.0.0.1:%d/", constants.KubeletPort), ContentConfig: rest.ContentConfig{ NegotiatedSerializer: serializer.WithoutConversionCodecFactory{CodecFactory: scheme.Codecs}, }, TLSClientConfig: rest.TLSClientConfig{ CertData: clientCert, KeyData: clientKey, CAData: caPEM, ServerName: nodename, }, } kubeletCert, err := os.ReadFile(filepath.Join(constants.KubeletPKIDir, "kubelet.crt")) if err == nil { config.CAData = append(config.CAData, kubeletCert...) } else if err != nil { // ignore if file doesn't exist, assume cert isn't self-signed if !errors.Is(err, fs.ErrNotExist) { return nil, fmt.Errorf("error reading kubelet certificate: %w", err) } } client := &Client{} client.client, err = rest.UnversionedRESTClientFor(config) if err != nil { return nil, fmt.Errorf("error building REST client: %w", err) } return client, nil } // Pods returns list of pods running on the kubelet. func (c *Client) Pods(ctx context.Context) (*PodList, error) { var podList PodList bytes, err := c.client.Get().AbsPath("/pods/").Timeout(30 * time.Second).Do(ctx).Raw() if err != nil { return nil, err } err = json.Unmarshal(bytes, &podList) if err != nil { return nil, err } return &podList, nil } // PodList is a list of pods. type PodList struct { Items []Pod `json:"items"` } // Pod returns pod details. type Pod struct { Metadata Metadata `json:"metadata"` Status v1.PodStatus `json:"status"` } // Metadata is a pod metadata. type Metadata struct { Name string `json:"name"` Namespace string `json:"namespace"` Annotations Annotations `json:"annotations"` } // Annotations are the annotations on a pod. type Annotations struct { // ConfigMapSource indicates where the resource is coming from. // Its value is "file"/"http" for static pods and "api" for resources came from kube-apiserver. ConfigSource string `json:"kubernetes.io/config.source"` } ================================================ FILE: pkg/kubernetes/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "context" "fmt" "log" "net/url" "os" "time" "github.com/cosi-project/runtime/pkg/controller" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/crypto/x509" taloskubernetes "github.com/siderolabs/go-kubernetes/kubernetes" "github.com/siderolabs/go-retry/retry" "golang.org/x/sync/errgroup" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" policy "k8s.io/api/policy/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) const ( // DrainTimeout is maximum time to wait for the node to be drained. DrainTimeout = 5 * time.Minute ) // Client represents a set of helper methods for interacting with the // Kubernetes API. type Client struct { *taloskubernetes.Client } // NewClientFromKubeletKubeconfig initializes and returns a Client. func NewClientFromKubeletKubeconfig() (*Client, error) { config, err := clientcmd.BuildConfigFromFlags("", constants.KubeletKubeconfig) if err != nil { return nil, err } return NewForConfig(config) } func loadPKIIntoVariable(data *[]byte, path *string) error { if len(*data) > 0 { return nil } if *path == "" { return fmt.Errorf("no certificate data or file provided") } pkiData, err := os.ReadFile(*path) if err != nil { return fmt.Errorf("failed to read certificate file: %w", err) } *data = pkiData *path = "" return nil } // NewForConfig initializes and returns a client using the provided config. func NewForConfig(config *restclient.Config) (*Client, error) { // read the certificates into byte slices to prevent the client from launching automatic // certificate reload if err := loadPKIIntoVariable(&config.TLSClientConfig.CAData, &config.TLSClientConfig.CAFile); err != nil { return nil, fmt.Errorf("failed to load CA certificate: %w", err) } if err := loadPKIIntoVariable(&config.TLSClientConfig.CertData, &config.TLSClientConfig.CertFile); err != nil { return nil, fmt.Errorf("failed to load client certificate: %w", err) } if err := loadPKIIntoVariable(&config.TLSClientConfig.KeyData, &config.TLSClientConfig.KeyFile); err != nil { return nil, fmt.Errorf("failed to load client key: %w", err) } // now, initialize the client using the standard method client, err := taloskubernetes.NewForConfig(config) if err != nil { return nil, err } return &Client{ Client: client, }, nil } // NewClientFromPKI initializes and returns a Client. // //nolint:interfacer func NewClientFromPKI(ca, crt, key []byte, endpoint *url.URL) (*Client, error) { tlsClientConfig := restclient.TLSClientConfig{ CAData: ca, CertData: crt, KeyData: key, } config := &restclient.Config{ Host: endpoint.String(), TLSClientConfig: tlsClientConfig, Timeout: 30 * time.Second, } return NewForConfig(config) } // NewTemporaryClientControlPlane initializes a Kubernetes client for a controlplane node // using PKI information. // // The client uses "localhost" endpoint, so it doesn't depend on the loadbalancer to be ready. func NewTemporaryClientControlPlane(ctx context.Context, r controller.Reader) (client *Client, err error) { k8sRoot, err := safe.ReaderGet[*secrets.KubernetesRoot](ctx, r, resource.NewMetadata(secrets.NamespaceName, secrets.KubernetesRootType, secrets.KubernetesRootID, resource.VersionUndefined)) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("failed to get kubernetes config: %w", err) } k8sRootSpec := k8sRoot.TypedSpec() return NewTemporaryClientFromPKI(k8sRootSpec.IssuingCA, k8sRootSpec.LocalEndpoint) } // NewTemporaryClientFromPKI initializes a Kubernetes client using a certificate // with a TTL of 10 minutes. func NewTemporaryClientFromPKI(ca *x509.PEMEncodedCertificateAndKey, endpoint *url.URL) (client *Client, err error) { opts := []x509.Option{ x509.CommonName(constants.KubernetesAdminCertCommonName), x509.Organization(constants.KubernetesAdminCertOrganization), x509.NotBefore(time.Now().Add(-time.Minute)), // allow for a minute for the time to be not in sync across nodes x509.NotAfter(time.Now().Add(10 * time.Minute)), } k8sCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(ca) if err != nil { return nil, fmt.Errorf("failed decoding Kubernetes CA: %w", err) } keyPair, err := x509.NewKeyPair(k8sCA, opts...) if err != nil { return nil, fmt.Errorf("failed generating temporary cert: %w", err) } h, err := NewClientFromPKI(ca.Crt, keyPair.CrtPEM, keyPair.KeyPEM, endpoint) if err != nil { return nil, fmt.Errorf("failed to create client: %w", err) } return h, nil } // NodeIPs returns list of node IP addresses by machine type. // //nolint:gocyclo func (h *Client) NodeIPs(ctx context.Context, machineType machine.Type) (addrs []string, err error) { resp, err := h.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return nil, err } addrs = []string{} for _, node := range resp.Items { _, labelControlPlane := node.Labels[constants.LabelNodeRoleControlPlane] var skip, foundInternalIP bool switch machineType { case machine.TypeInit, machine.TypeControlPlane: skip = !labelControlPlane case machine.TypeWorker: skip = labelControlPlane case machine.TypeUnknown: fallthrough default: panic(fmt.Sprintf("unexpected machine type %v", machineType)) } if skip { continue } // try to get the internal IP address for _, nodeAddress := range node.Status.Addresses { if nodeAddress.Type == corev1.NodeInternalIP { addrs = append(addrs, nodeAddress.Address) foundInternalIP = true break } } if !foundInternalIP { // no internal IP, fallback to external IP for _, nodeAddress := range node.Status.Addresses { if nodeAddress.Type == corev1.NodeExternalIP { addrs = append(addrs, nodeAddress.Address) break } } } } return addrs, nil } // Drain evicts all pods on a given node. func (h *Client) Drain(ctx context.Context, node string) error { ctx, cancel := context.WithTimeout(ctx, DrainTimeout) defer cancel() opts := metav1.ListOptions{ FieldSelector: fields.SelectorFromSet(fields.Set{"spec.nodeName": node}).String(), } pods, err := h.CoreV1().Pods(metav1.NamespaceAll).List(ctx, opts) if err != nil { return fmt.Errorf("cannot get pods for node %s: %w", node, err) } var eg errgroup.Group // Evict each pod. for _, pod := range pods.Items { eg.Go(func() error { if _, ok := pod.ObjectMeta.Annotations[corev1.MirrorPodAnnotationKey]; ok { log.Printf("skipping mirror pod %s/%s\n", pod.GetNamespace(), pod.GetName()) return nil } controllerRef := metav1.GetControllerOf(&pod) if controllerRef == nil { log.Printf("skipping unmanaged pod %s/%s\n", pod.GetNamespace(), pod.GetName()) return nil } if controllerRef.Kind == appsv1.SchemeGroupVersion.WithKind("DaemonSet").Kind { log.Printf("skipping DaemonSet pod %s/%s\n", pod.GetNamespace(), pod.GetName()) return nil } if !pod.DeletionTimestamp.IsZero() { log.Printf("skipping deleted pod %s/%s\n", pod.GetNamespace(), pod.GetName()) } if err := h.evict(ctx, pod, int64(60)); err != nil { log.Printf("WARNING: failed to evict pod: %v", err) } return nil }) } return eg.Wait() } func (h *Client) evict(ctx context.Context, p corev1.Pod, gracePeriod int64) error { for { pol := &policy.Eviction{ ObjectMeta: metav1.ObjectMeta{Namespace: p.GetNamespace(), Name: p.GetName()}, DeleteOptions: &metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}, } err := h.CoreV1().Pods(p.GetNamespace()).Evict(ctx, pol) switch { case apierrors.IsTooManyRequests(err): time.Sleep(5 * time.Second) case apierrors.IsNotFound(err): return nil case err != nil: return fmt.Errorf("failed to evict pod %s/%s: %w", p.GetNamespace(), p.GetName(), err) default: if err = h.waitForPodDeleted(ctx, &p); err != nil { return fmt.Errorf("failed waiting on pod %s/%s to be deleted: %w", p.GetNamespace(), p.GetName(), err) } return nil } } } func (h *Client) waitForPodDeleted(ctx context.Context, p *corev1.Pod) error { return retry.Constant(time.Minute, retry.WithUnits(3*time.Second)).RetryWithContext(ctx, func(ctx context.Context) error { pod, err := h.CoreV1().Pods(p.GetNamespace()).Get(ctx, p.GetName(), metav1.GetOptions{}) switch { case apierrors.IsNotFound(err): return nil case apierrors.IsForbidden(err): // in Kubernetes 1.32+, NodeRestriction plugin won't let us list a pod which is not on our node, including deleted ones return nil case err != nil: if IsRetryableError(err) { return retry.ExpectedError(err) } return fmt.Errorf("failed to get pod %s/%s: %w", p.GetNamespace(), p.GetName(), err) } if pod.GetUID() != p.GetUID() { return nil } return retry.ExpectedErrorf("pod is still running on the node") }) } ================================================ FILE: pkg/kubernetes/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes import ( "github.com/blang/semver/v4" "github.com/distribution/reference" ) // VersionGTE returns true if the version of the image is greater than or equal to the provided version. // // It supports any kind of image reference, but requires the tag to be present. func VersionGTE(image string, version semver.Version) bool { imageRef, err := reference.ParseNormalizedNamed(image) if err != nil { // couldn't parse the reference, so we can't compare return false } taggedRef, ok := imageRef.(reference.Tagged) if !ok { // tag is missing return false } vers, err := semver.ParseTolerant(taggedRef.Tag()) if err != nil { // invalid version return false } vers.Pre = nil // reset the pre-release version to compare only the version return vers.GTE(version) } ================================================ FILE: pkg/kubernetes/version_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubernetes_test import ( "testing" "github.com/blang/semver/v4" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/kubernetes" ) func TestVersionGTE(t *testing.T) { for _, test := range []struct { name string image string version semver.Version expected bool }{ { name: "tagged image", image: "registry.k8s.io/kube-apiserver:v1.30.0", version: semver.MustParse("1.30.0"), expected: true, }, { name: "tagged image, not less", image: "registry.k8s.io/kube-apiserver:v1.29.8", version: semver.MustParse("1.30.0"), expected: false, }, { name: "tagged image, alpha", image: "registry.k8s.io/kube-apiserver:v1.30.0-alpha.3", version: semver.MustParse("1.30.0"), expected: true, }, { name: "tagged and digested image", image: "registry.k8s.io/kube-apiserver:v1.30.0@sha256:9efd51eb47ecdd66b9426d9361edca2cbed38d57c4fe9d81213867310a1fdd99", version: semver.MustParse("1.30.0"), expected: true, }, { name: "invalid tag", image: "registry.k8s.io/kube-apiserver:latest", version: semver.MustParse("1.30.0"), expected: false, }, { name: "only digest", image: "registry.k8s.io/kube-apiserver@sha256:9efd51eb47ecdd66b9426d9361edca2cbed38d57c4fe9d81213867310a1fdd99", version: semver.MustParse("1.30.0"), expected: false, }, } { t.Run(test.name, func(t *testing.T) { require.Equal(t, test.expected, kubernetes.VersionGTE(test.image, test.version)) }) } } ================================================ FILE: pkg/logging/error_suppress.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "sync/atomic" "go.uber.org/zap/zapcore" ) // NewControllerErrorSuppressor creates a new controller error suppressor. // // It suppresses error logs for a given controller unless it logs >= threshold errors. // The idea is that all controllers reconcile errors, so if the error is not transient, // it will be reported enough time to hit the threshold, but transient errors will be // suppressed. // // The suppressor records the controller name by inspecting a log field named "controller" // passed via `logger.With()` call. func NewControllerErrorSuppressor(core zapcore.Core, threshold int64) zapcore.Core { return &consoleSampler{ Core: core, threshold: threshold, } } type consoleSampler struct { zapcore.Core hits *atomic.Int64 threshold int64 controller string } var _ zapcore.Core = (*consoleSampler)(nil) func (s *consoleSampler) Level() zapcore.Level { return zapcore.LevelOf(s.Core) } func (s *consoleSampler) With(fields []zapcore.Field) zapcore.Core { controller := s.controller num := s.hits for _, field := range fields { if field.Key == "controller" { if field.String != controller { controller = field.String num = new(atomic.Int64) } break } } return &consoleSampler{ threshold: s.threshold, controller: controller, hits: num, Core: s.Core.With(fields), } } func (s *consoleSampler) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { if !s.Enabled(ent.Level) { return ce } if ent.Level == zapcore.ErrorLevel && s.controller != "" { if s.hits.Add(1) <= s.threshold { // suppress the log return ce } } return s.Core.Check(ent, ce) } ================================================ FILE: pkg/logging/error_suppress_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging_test import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/zap/zapcore" "go.uber.org/zap/zaptest/observer" "github.com/siderolabs/talos/pkg/logging" ) func assertLogged(t *testing.T, core zapcore.Core, logs *observer.ObservedLogs, entries []zapcore.Entry, expectedCount int) { t.Helper() for _, entry := range entries { if ce := core.Check(entry, nil); ce != nil { ce.Write() } } assert.Len(t, logs.TakeAll(), expectedCount) } func TestErrorSuppressor(t *testing.T) { t.Parallel() core, logs := observer.New(zapcore.InfoLevel) const threshold = 2 core = logging.NewControllerErrorSuppressor(core, threshold) // warn/info messages are not affected assertLogged(t, core, logs, []zapcore.Entry{ {Level: zapcore.InfoLevel, Message: "abc"}, {Level: zapcore.WarnLevel, Message: "def"}, {Level: zapcore.DebugLevel, Message: "message"}, // below level }, 2) // different controllers, suppress counters are independent controllerCore1 := core.With([]zapcore.Field{{Key: "controller", String: "c1"}}) controllerCore2 := core.With([]zapcore.Field{{Key: "controller", String: "c2"}}) assertLogged(t, controllerCore1, logs, []zapcore.Entry{ {Level: zapcore.ErrorLevel, Message: "controller failed"}, // suppressed {Level: zapcore.ErrorLevel, Message: "controller failed"}, // suppressed {Level: zapcore.ErrorLevel, Message: "controller failed"}, }, 1) assertLogged(t, controllerCore2, logs, []zapcore.Entry{ {Level: zapcore.ErrorLevel, Message: "controller failed"}, // suppressed {Level: zapcore.ErrorLevel, Message: "controller failed"}, // suppressed }, 0) assertLogged(t, controllerCore1, logs, []zapcore.Entry{ {Level: zapcore.ErrorLevel, Message: "controller failed"}, // not suppressed, over threshold }, 1) assertLogged(t, controllerCore1.With([]zapcore.Field{{Key: "foo", String: "bar"}}), logs, []zapcore.Entry{ {Level: zapcore.ErrorLevel, Message: "controller failed"}, // .With() without 'controller' field keeps the counter value from the parent }, 1) } ================================================ FILE: pkg/logging/logging.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package logging provides logging primitives. package logging ================================================ FILE: pkg/logging/zap.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "io" "log" "strings" "github.com/siderolabs/gen/xslices" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // LogWriter is a wrapper around zap.Logger that implements io.Writer interface. type LogWriter struct { dest *zap.Logger level zapcore.Level } // NewWriter creates new log zap log writer. func NewWriter(l *zap.Logger, level zapcore.Level) io.Writer { return &LogWriter{ dest: l, level: level, } } // Write implements io.Writer interface. func (lw *LogWriter) Write(line []byte) (int, error) { checked := lw.dest.Check(lw.level, strings.TrimSpace(string(line))) if checked == nil { return 0, nil } checked.Write() return len(line), nil } // logWrapper wraps around standard logger. type logWrapper struct { log *log.Logger } // Write implements io.Writer interface. func (lw *logWrapper) Write(line []byte) (int, error) { if lw.log == nil { log.Print(string(line)) } else { lw.log.Print(string(line)) } return len(line), nil } // StdWriter creates a sync writer that writes all logs to the std logger. var StdWriter = &logWrapper{nil} // LogDestination defines logging destination Config. type LogDestination struct { // Level log level. level zapcore.LevelEnabler writer io.Writer config zapcore.EncoderConfig suppressThreshold int64 } // LogDestinationOption defines a log destination encoder config setter. type LogDestinationOption func(dest *LogDestination) // WithoutTimestamp disables timestamp. func WithoutTimestamp() LogDestinationOption { return func(dest *LogDestination) { dest.config.EncodeTime = nil } } // WithoutLogLevels disable log level. func WithoutLogLevels() LogDestinationOption { return func(dest *LogDestination) { dest.config.EncodeLevel = nil } } // WithColoredLevels enables log level colored output. func WithColoredLevels() LogDestinationOption { return func(dest *LogDestination) { dest.config.EncodeLevel = zapcore.CapitalColorLevelEncoder } } // WithControllerErrorSuppressor creates a new console log controller error suppressor. func WithControllerErrorSuppressor(threshold int64) LogDestinationOption { return func(dest *LogDestination) { dest.suppressThreshold = threshold } } // NewLogDestination creates new log destination. func NewLogDestination(writer io.Writer, logLevel zapcore.LevelEnabler, options ...LogDestinationOption) *LogDestination { config := zap.NewDevelopmentEncoderConfig() config.ConsoleSeparator = " " config.StacktraceKey = "error" dest := &LogDestination{ level: logLevel, config: config, writer: writer, } for _, option := range options { option(dest) } return dest } // Wrap is a simple helper to wrap io.Writer with default arguments. func Wrap(writer io.Writer) *zap.Logger { return ZapLogger( NewLogDestination(writer, zapcore.DebugLevel), ) } // ZapLogger creates new default Zap Logger. func ZapLogger(dests ...*LogDestination) *zap.Logger { if len(dests) == 0 { panic("at least one writer must be defined") } cores := xslices.Map(dests, func(dest *LogDestination) zapcore.Core { core := zapcore.NewCore( zapcore.NewConsoleEncoder(dest.config), zapcore.AddSync(dest.writer), dest.level, ) if dest.suppressThreshold > 0 { core = NewControllerErrorSuppressor(core, dest.suppressThreshold) } return core }) return zap.New(zapcore.NewTee(cores...)) } // Component helper for creating zap.Field. func Component(name string) zapcore.Field { return zap.String("component", name) } ================================================ FILE: pkg/machinery/api/api.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package api contains API definitions for Talos Linux. // //nolint:revive package api import ( cosi "github.com/cosi-project/runtime/api/v1alpha1" "google.golang.org/protobuf/reflect/protoreflect" "github.com/siderolabs/talos/pkg/machinery/api/cluster" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/api/inspect" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/api/security" "github.com/siderolabs/talos/pkg/machinery/api/storage" "github.com/siderolabs/talos/pkg/machinery/api/time" ) // TalosAPIdOne2ManyAPIs returns a list of API services that support one-to-many // communication pattern served by apid. // // Note: we are moving to one-to-one APIs, so this list should not grow. func TalosAPIdOne2ManyAPIs() []protoreflect.FileDescriptor { return []protoreflect.FileDescriptor{ common.File_common_common_proto, cluster.File_cluster_cluster_proto, inspect.File_inspect_inspect_proto, machine.File_machine_machine_proto, storage.File_storage_storage_proto, time.File_time_time_proto, } } // TalosAPIdAllAPIs returns a list of all API services served by apid. // // This includes legacy one-to-many APIs as well as newer one-to-one APIs. func TalosAPIdAllAPIs() []protoreflect.FileDescriptor { return append(TalosAPIdOne2ManyAPIs(), cosi.File_v1alpha1_state_proto, machine.File_machine_debug_proto, machine.File_machine_image_proto, machine.File_machine_lifecycle_proto, ) } // AllAPIs returns a list of all API services served by Talos components. // // This includes Talos apid and trustd APIs. func AllAPIs() []protoreflect.FileDescriptor { return append(TalosAPIdAllAPIs(), security.File_security_security_proto, ) } ================================================ FILE: pkg/machinery/api/cluster/cluster.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: cluster/cluster.proto package cluster import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type HealthCheckRequest struct { state protoimpl.MessageState `protogen:"open.v1"` WaitTimeout *durationpb.Duration `protobuf:"bytes,1,opt,name=wait_timeout,json=waitTimeout,proto3" json:"wait_timeout,omitempty"` ClusterInfo *ClusterInfo `protobuf:"bytes,2,opt,name=cluster_info,json=clusterInfo,proto3" json:"cluster_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HealthCheckRequest) Reset() { *x = HealthCheckRequest{} mi := &file_cluster_cluster_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HealthCheckRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*HealthCheckRequest) ProtoMessage() {} func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message { mi := &file_cluster_cluster_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead. func (*HealthCheckRequest) Descriptor() ([]byte, []int) { return file_cluster_cluster_proto_rawDescGZIP(), []int{0} } func (x *HealthCheckRequest) GetWaitTimeout() *durationpb.Duration { if x != nil { return x.WaitTimeout } return nil } func (x *HealthCheckRequest) GetClusterInfo() *ClusterInfo { if x != nil { return x.ClusterInfo } return nil } type ClusterInfo struct { state protoimpl.MessageState `protogen:"open.v1"` ControlPlaneNodes []string `protobuf:"bytes,1,rep,name=control_plane_nodes,json=controlPlaneNodes,proto3" json:"control_plane_nodes,omitempty"` WorkerNodes []string `protobuf:"bytes,2,rep,name=worker_nodes,json=workerNodes,proto3" json:"worker_nodes,omitempty"` ForceEndpoint string `protobuf:"bytes,3,opt,name=force_endpoint,json=forceEndpoint,proto3" json:"force_endpoint,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClusterInfo) Reset() { *x = ClusterInfo{} mi := &file_cluster_cluster_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClusterInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClusterInfo) ProtoMessage() {} func (x *ClusterInfo) ProtoReflect() protoreflect.Message { mi := &file_cluster_cluster_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClusterInfo.ProtoReflect.Descriptor instead. func (*ClusterInfo) Descriptor() ([]byte, []int) { return file_cluster_cluster_proto_rawDescGZIP(), []int{1} } func (x *ClusterInfo) GetControlPlaneNodes() []string { if x != nil { return x.ControlPlaneNodes } return nil } func (x *ClusterInfo) GetWorkerNodes() []string { if x != nil { return x.WorkerNodes } return nil } func (x *ClusterInfo) GetForceEndpoint() string { if x != nil { return x.ForceEndpoint } return "" } type HealthCheckProgress struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HealthCheckProgress) Reset() { *x = HealthCheckProgress{} mi := &file_cluster_cluster_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HealthCheckProgress) String() string { return protoimpl.X.MessageStringOf(x) } func (*HealthCheckProgress) ProtoMessage() {} func (x *HealthCheckProgress) ProtoReflect() protoreflect.Message { mi := &file_cluster_cluster_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HealthCheckProgress.ProtoReflect.Descriptor instead. func (*HealthCheckProgress) Descriptor() ([]byte, []int) { return file_cluster_cluster_proto_rawDescGZIP(), []int{2} } func (x *HealthCheckProgress) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *HealthCheckProgress) GetMessage() string { if x != nil { return x.Message } return "" } var File_cluster_cluster_proto protoreflect.FileDescriptor const file_cluster_cluster_proto_rawDesc = "" + "\n" + "\x15cluster/cluster.proto\x12\acluster\x1a\x13common/common.proto\x1a\x1egoogle/protobuf/duration.proto\"\x8b\x01\n" + "\x12HealthCheckRequest\x12<\n" + "\fwait_timeout\x18\x01 \x01(\v2\x19.google.protobuf.DurationR\vwaitTimeout\x127\n" + "\fcluster_info\x18\x02 \x01(\v2\x14.cluster.ClusterInfoR\vclusterInfo\"\x87\x01\n" + "\vClusterInfo\x12.\n" + "\x13control_plane_nodes\x18\x01 \x03(\tR\x11controlPlaneNodes\x12!\n" + "\fworker_nodes\x18\x02 \x03(\tR\vworkerNodes\x12%\n" + "\x0eforce_endpoint\x18\x03 \x01(\tR\rforceEndpoint\"]\n" + "\x13HealthCheckProgress\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x18\n" + "\amessage\x18\x02 \x01(\tR\amessage2\\\n" + "\x0eClusterService\x12J\n" + "\vHealthCheck\x12\x1b.cluster.HealthCheckRequest\x1a\x1c.cluster.HealthCheckProgress0\x01BN\n" + "\x15dev.talos.api.clusterZ5github.com/siderolabs/talos/pkg/machinery/api/clusterb\x06proto3" var ( file_cluster_cluster_proto_rawDescOnce sync.Once file_cluster_cluster_proto_rawDescData []byte ) func file_cluster_cluster_proto_rawDescGZIP() []byte { file_cluster_cluster_proto_rawDescOnce.Do(func() { file_cluster_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_cluster_cluster_proto_rawDesc), len(file_cluster_cluster_proto_rawDesc))) }) return file_cluster_cluster_proto_rawDescData } var file_cluster_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_cluster_cluster_proto_goTypes = []any{ (*HealthCheckRequest)(nil), // 0: cluster.HealthCheckRequest (*ClusterInfo)(nil), // 1: cluster.ClusterInfo (*HealthCheckProgress)(nil), // 2: cluster.HealthCheckProgress (*durationpb.Duration)(nil), // 3: google.protobuf.Duration (*common.Metadata)(nil), // 4: common.Metadata } var file_cluster_cluster_proto_depIdxs = []int32{ 3, // 0: cluster.HealthCheckRequest.wait_timeout:type_name -> google.protobuf.Duration 1, // 1: cluster.HealthCheckRequest.cluster_info:type_name -> cluster.ClusterInfo 4, // 2: cluster.HealthCheckProgress.metadata:type_name -> common.Metadata 0, // 3: cluster.ClusterService.HealthCheck:input_type -> cluster.HealthCheckRequest 2, // 4: cluster.ClusterService.HealthCheck:output_type -> cluster.HealthCheckProgress 4, // [4:5] is the sub-list for method output_type 3, // [3:4] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_cluster_cluster_proto_init() } func file_cluster_cluster_proto_init() { if File_cluster_cluster_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_cluster_cluster_proto_rawDesc), len(file_cluster_cluster_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 1, }, GoTypes: file_cluster_cluster_proto_goTypes, DependencyIndexes: file_cluster_cluster_proto_depIdxs, MessageInfos: file_cluster_cluster_proto_msgTypes, }.Build() File_cluster_cluster_proto = out.File file_cluster_cluster_proto_goTypes = nil file_cluster_cluster_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/cluster/cluster_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: cluster/cluster.proto package cluster import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( ClusterService_HealthCheck_FullMethodName = "/cluster.ClusterService/HealthCheck" ) // ClusterServiceClient is the client API for ClusterService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The cluster service definition. type ClusterServiceClient interface { HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckProgress], error) } type clusterServiceClient struct { cc grpc.ClientConnInterface } func NewClusterServiceClient(cc grpc.ClientConnInterface) ClusterServiceClient { return &clusterServiceClient{cc} } func (c *clusterServiceClient) HealthCheck(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[HealthCheckProgress], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ClusterService_ServiceDesc.Streams[0], ClusterService_HealthCheck_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[HealthCheckRequest, HealthCheckProgress]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ClusterService_HealthCheckClient = grpc.ServerStreamingClient[HealthCheckProgress] // ClusterServiceServer is the server API for ClusterService service. // All implementations must embed UnimplementedClusterServiceServer // for forward compatibility. // // The cluster service definition. type ClusterServiceServer interface { HealthCheck(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckProgress]) error mustEmbedUnimplementedClusterServiceServer() } // UnimplementedClusterServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedClusterServiceServer struct{} func (UnimplementedClusterServiceServer) HealthCheck(*HealthCheckRequest, grpc.ServerStreamingServer[HealthCheckProgress]) error { return status.Error(codes.Unimplemented, "method HealthCheck not implemented") } func (UnimplementedClusterServiceServer) mustEmbedUnimplementedClusterServiceServer() {} func (UnimplementedClusterServiceServer) testEmbeddedByValue() {} // UnsafeClusterServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ClusterServiceServer will // result in compilation errors. type UnsafeClusterServiceServer interface { mustEmbedUnimplementedClusterServiceServer() } func RegisterClusterServiceServer(s grpc.ServiceRegistrar, srv ClusterServiceServer) { // If the following call panics, it indicates UnimplementedClusterServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&ClusterService_ServiceDesc, srv) } func _ClusterService_HealthCheck_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(HealthCheckRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ClusterServiceServer).HealthCheck(m, &grpc.GenericServerStream[HealthCheckRequest, HealthCheckProgress]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ClusterService_HealthCheckServer = grpc.ServerStreamingServer[HealthCheckProgress] // ClusterService_ServiceDesc is the grpc.ServiceDesc for ClusterService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ClusterService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "cluster.ClusterService", HandlerType: (*ClusterServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "HealthCheck", Handler: _ClusterService_HealthCheck_Handler, ServerStreams: true, }, }, Metadata: "cluster/cluster.proto", } ================================================ FILE: pkg/machinery/api/cluster/cluster_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: cluster/cluster.proto package cluster import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb1 "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *HealthCheckRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HealthCheckRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HealthCheckRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClusterInfo != nil { size, err := m.ClusterInfo.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.WaitTimeout != nil { size, err := (*durationpb.Duration)(m.WaitTimeout).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ClusterInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ClusterInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ClusterInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ForceEndpoint) > 0 { i -= len(m.ForceEndpoint) copy(dAtA[i:], m.ForceEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ForceEndpoint))) i-- dAtA[i] = 0x1a } if len(m.WorkerNodes) > 0 { for iNdEx := len(m.WorkerNodes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.WorkerNodes[iNdEx]) copy(dAtA[i:], m.WorkerNodes[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.WorkerNodes[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.ControlPlaneNodes) > 0 { for iNdEx := len(m.ControlPlaneNodes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ControlPlaneNodes[iNdEx]) copy(dAtA[i:], m.ControlPlaneNodes[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ControlPlaneNodes[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *HealthCheckProgress) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HealthCheckProgress) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HealthCheckProgress) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Message) > 0 { i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *HealthCheckRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.WaitTimeout != nil { l = (*durationpb.Duration)(m.WaitTimeout).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClusterInfo != nil { l = m.ClusterInfo.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ClusterInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ControlPlaneNodes) > 0 { for _, s := range m.ControlPlaneNodes { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.WorkerNodes) > 0 { for _, s := range m.WorkerNodes { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.ForceEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *HealthCheckProgress) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Message) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *HealthCheckRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HealthCheckRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HealthCheckRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WaitTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.WaitTimeout == nil { m.WaitTimeout = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.WaitTimeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClusterInfo == nil { m.ClusterInfo = &ClusterInfo{} } if err := m.ClusterInfo.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ClusterInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ClusterInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ClusterInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControlPlaneNodes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ControlPlaneNodes = append(m.ControlPlaneNodes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WorkerNodes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.WorkerNodes = append(m.WorkerNodes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ForceEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ForceEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HealthCheckProgress) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HealthCheckProgress: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HealthCheckProgress: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/common/common.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: common/common.proto package common import ( reflect "reflect" sync "sync" unsafe "unsafe" status "google.golang.org/genproto/googleapis/rpc/status" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" descriptorpb "google.golang.org/protobuf/types/descriptorpb" anypb "google.golang.org/protobuf/types/known/anypb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Code int32 const ( Code_FATAL Code = 0 Code_LOCKED Code = 1 Code_CANCELED Code = 2 ) // Enum value maps for Code. var ( Code_name = map[int32]string{ 0: "FATAL", 1: "LOCKED", 2: "CANCELED", } Code_value = map[string]int32{ "FATAL": 0, "LOCKED": 1, "CANCELED": 2, } ) func (x Code) Enum() *Code { p := new(Code) *p = x return p } func (x Code) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Code) Descriptor() protoreflect.EnumDescriptor { return file_common_common_proto_enumTypes[0].Descriptor() } func (Code) Type() protoreflect.EnumType { return &file_common_common_proto_enumTypes[0] } func (x Code) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Code.Descriptor instead. func (Code) EnumDescriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{0} } type ContainerDriver int32 const ( ContainerDriver_CONTAINERD ContainerDriver = 0 ContainerDriver_CRI ContainerDriver = 1 ) // Enum value maps for ContainerDriver. var ( ContainerDriver_name = map[int32]string{ 0: "CONTAINERD", 1: "CRI", } ContainerDriver_value = map[string]int32{ "CONTAINERD": 0, "CRI": 1, } ) func (x ContainerDriver) Enum() *ContainerDriver { p := new(ContainerDriver) *p = x return p } func (x ContainerDriver) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ContainerDriver) Descriptor() protoreflect.EnumDescriptor { return file_common_common_proto_enumTypes[1].Descriptor() } func (ContainerDriver) Type() protoreflect.EnumType { return &file_common_common_proto_enumTypes[1] } func (x ContainerDriver) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ContainerDriver.Descriptor instead. func (ContainerDriver) EnumDescriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{1} } type ContainerdNamespace int32 const ( ContainerdNamespace_NS_UNKNOWN ContainerdNamespace = 0 ContainerdNamespace_NS_SYSTEM ContainerdNamespace = 1 ContainerdNamespace_NS_CRI ContainerdNamespace = 2 ) // Enum value maps for ContainerdNamespace. var ( ContainerdNamespace_name = map[int32]string{ 0: "NS_UNKNOWN", 1: "NS_SYSTEM", 2: "NS_CRI", } ContainerdNamespace_value = map[string]int32{ "NS_UNKNOWN": 0, "NS_SYSTEM": 1, "NS_CRI": 2, } ) func (x ContainerdNamespace) Enum() *ContainerdNamespace { p := new(ContainerdNamespace) *p = x return p } func (x ContainerdNamespace) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ContainerdNamespace) Descriptor() protoreflect.EnumDescriptor { return file_common_common_proto_enumTypes[2].Descriptor() } func (ContainerdNamespace) Type() protoreflect.EnumType { return &file_common_common_proto_enumTypes[2] } func (x ContainerdNamespace) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ContainerdNamespace.Descriptor instead. func (ContainerdNamespace) EnumDescriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{2} } type Error struct { state protoimpl.MessageState `protogen:"open.v1"` Code Code `protobuf:"varint,1,opt,name=code,proto3,enum=common.Code" json:"code,omitempty"` Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` Details []*anypb.Any `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Error) Reset() { *x = Error{} mi := &file_common_common_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Error) String() string { return protoimpl.X.MessageStringOf(x) } func (*Error) ProtoMessage() {} func (x *Error) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Error.ProtoReflect.Descriptor instead. func (*Error) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{0} } func (x *Error) GetCode() Code { if x != nil { return x.Code } return Code_FATAL } func (x *Error) GetMessage() string { if x != nil { return x.Message } return "" } func (x *Error) GetDetails() []*anypb.Any { if x != nil { return x.Details } return nil } // Common metadata message nested in all reply message types type Metadata struct { state protoimpl.MessageState `protogen:"open.v1"` // hostname of the server response comes from (injected by proxy) Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` // error is set if request failed to the upstream (rest of response is // undefined) Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` // error as gRPC Status Status *status.Status `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Metadata) Reset() { *x = Metadata{} mi := &file_common_common_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Metadata) String() string { return protoimpl.X.MessageStringOf(x) } func (*Metadata) ProtoMessage() {} func (x *Metadata) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Metadata.ProtoReflect.Descriptor instead. func (*Metadata) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{1} } func (x *Metadata) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *Metadata) GetError() string { if x != nil { return x.Error } return "" } func (x *Metadata) GetStatus() *status.Status { if x != nil { return x.Status } return nil } type Data struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Bytes []byte `protobuf:"bytes,2,opt,name=bytes,proto3" json:"bytes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Data) Reset() { *x = Data{} mi := &file_common_common_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Data) String() string { return protoimpl.X.MessageStringOf(x) } func (*Data) ProtoMessage() {} func (x *Data) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Data.ProtoReflect.Descriptor instead. func (*Data) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{2} } func (x *Data) GetMetadata() *Metadata { if x != nil { return x.Metadata } return nil } func (x *Data) GetBytes() []byte { if x != nil { return x.Bytes } return nil } type DataResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Data `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DataResponse) Reset() { *x = DataResponse{} mi := &file_common_common_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DataResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*DataResponse) ProtoMessage() {} func (x *DataResponse) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DataResponse.ProtoReflect.Descriptor instead. func (*DataResponse) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{3} } func (x *DataResponse) GetMessages() []*Data { if x != nil { return x.Messages } return nil } type Empty struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Empty) Reset() { *x = Empty{} mi := &file_common_common_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Empty) String() string { return protoimpl.X.MessageStringOf(x) } func (*Empty) ProtoMessage() {} func (x *Empty) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Empty.ProtoReflect.Descriptor instead. func (*Empty) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{4} } func (x *Empty) GetMetadata() *Metadata { if x != nil { return x.Metadata } return nil } type EmptyResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Empty `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EmptyResponse) Reset() { *x = EmptyResponse{} mi := &file_common_common_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EmptyResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EmptyResponse) ProtoMessage() {} func (x *EmptyResponse) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EmptyResponse.ProtoReflect.Descriptor instead. func (*EmptyResponse) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{5} } func (x *EmptyResponse) GetMessages() []*Empty { if x != nil { return x.Messages } return nil } type ContainerdInstance struct { state protoimpl.MessageState `protogen:"open.v1"` // Containerd instance to use. Driver ContainerDriver `protobuf:"varint,1,opt,name=driver,proto3,enum=common.ContainerDriver" json:"driver,omitempty"` // Containerd namespace to use. Namespace ContainerdNamespace `protobuf:"varint,2,opt,name=namespace,proto3,enum=common.ContainerdNamespace" json:"namespace,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ContainerdInstance) Reset() { *x = ContainerdInstance{} mi := &file_common_common_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ContainerdInstance) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContainerdInstance) ProtoMessage() {} func (x *ContainerdInstance) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContainerdInstance.ProtoReflect.Descriptor instead. func (*ContainerdInstance) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{6} } func (x *ContainerdInstance) GetDriver() ContainerDriver { if x != nil { return x.Driver } return ContainerDriver_CONTAINERD } func (x *ContainerdInstance) GetNamespace() ContainerdNamespace { if x != nil { return x.Namespace } return ContainerdNamespace_NS_UNKNOWN } type URL struct { state protoimpl.MessageState `protogen:"open.v1"` FullPath string `protobuf:"bytes,1,opt,name=full_path,json=fullPath,proto3" json:"full_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *URL) Reset() { *x = URL{} mi := &file_common_common_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *URL) String() string { return protoimpl.X.MessageStringOf(x) } func (*URL) ProtoMessage() {} func (x *URL) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use URL.ProtoReflect.Descriptor instead. func (*URL) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{7} } func (x *URL) GetFullPath() string { if x != nil { return x.FullPath } return "" } type PEMEncodedCertificateAndKey struct { state protoimpl.MessageState `protogen:"open.v1"` Crt []byte `protobuf:"bytes,1,opt,name=crt,proto3" json:"crt,omitempty"` Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PEMEncodedCertificateAndKey) Reset() { *x = PEMEncodedCertificateAndKey{} mi := &file_common_common_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PEMEncodedCertificateAndKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*PEMEncodedCertificateAndKey) ProtoMessage() {} func (x *PEMEncodedCertificateAndKey) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PEMEncodedCertificateAndKey.ProtoReflect.Descriptor instead. func (*PEMEncodedCertificateAndKey) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{8} } func (x *PEMEncodedCertificateAndKey) GetCrt() []byte { if x != nil { return x.Crt } return nil } func (x *PEMEncodedCertificateAndKey) GetKey() []byte { if x != nil { return x.Key } return nil } type PEMEncodedKey struct { state protoimpl.MessageState `protogen:"open.v1"` Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PEMEncodedKey) Reset() { *x = PEMEncodedKey{} mi := &file_common_common_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PEMEncodedKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*PEMEncodedKey) ProtoMessage() {} func (x *PEMEncodedKey) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PEMEncodedKey.ProtoReflect.Descriptor instead. func (*PEMEncodedKey) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{9} } func (x *PEMEncodedKey) GetKey() []byte { if x != nil { return x.Key } return nil } type PEMEncodedCertificate struct { state protoimpl.MessageState `protogen:"open.v1"` Crt []byte `protobuf:"bytes,1,opt,name=crt,proto3" json:"crt,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PEMEncodedCertificate) Reset() { *x = PEMEncodedCertificate{} mi := &file_common_common_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PEMEncodedCertificate) String() string { return protoimpl.X.MessageStringOf(x) } func (*PEMEncodedCertificate) ProtoMessage() {} func (x *PEMEncodedCertificate) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PEMEncodedCertificate.ProtoReflect.Descriptor instead. func (*PEMEncodedCertificate) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{10} } func (x *PEMEncodedCertificate) GetCrt() []byte { if x != nil { return x.Crt } return nil } type NetIP struct { state protoimpl.MessageState `protogen:"open.v1"` Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetIP) Reset() { *x = NetIP{} mi := &file_common_common_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetIP) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetIP) ProtoMessage() {} func (x *NetIP) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetIP.ProtoReflect.Descriptor instead. func (*NetIP) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{11} } func (x *NetIP) GetIp() []byte { if x != nil { return x.Ip } return nil } type NetIPPort struct { state protoimpl.MessageState `protogen:"open.v1"` Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetIPPort) Reset() { *x = NetIPPort{} mi := &file_common_common_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetIPPort) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetIPPort) ProtoMessage() {} func (x *NetIPPort) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetIPPort.ProtoReflect.Descriptor instead. func (*NetIPPort) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{12} } func (x *NetIPPort) GetIp() []byte { if x != nil { return x.Ip } return nil } func (x *NetIPPort) GetPort() int32 { if x != nil { return x.Port } return 0 } type NetIPPrefix struct { state protoimpl.MessageState `protogen:"open.v1"` Ip []byte `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` PrefixLength int32 `protobuf:"varint,2,opt,name=prefix_length,json=prefixLength,proto3" json:"prefix_length,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetIPPrefix) Reset() { *x = NetIPPrefix{} mi := &file_common_common_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetIPPrefix) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetIPPrefix) ProtoMessage() {} func (x *NetIPPrefix) ProtoReflect() protoreflect.Message { mi := &file_common_common_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetIPPrefix.ProtoReflect.Descriptor instead. func (*NetIPPrefix) Descriptor() ([]byte, []int) { return file_common_common_proto_rawDescGZIP(), []int{13} } func (x *NetIPPrefix) GetIp() []byte { if x != nil { return x.Ip } return nil } func (x *NetIPPrefix) GetPrefixLength() int32 { if x != nil { return x.PrefixLength } return 0 } var file_common_common_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.MessageOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_message", Tag: "bytes,93117,opt,name=remove_deprecated_message", Filename: "common/common.proto", }, { ExtendedType: (*descriptorpb.FieldOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_field", Tag: "bytes,93117,opt,name=remove_deprecated_field", Filename: "common/common.proto", }, { ExtendedType: (*descriptorpb.EnumOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_enum", Tag: "bytes,93117,opt,name=remove_deprecated_enum", Filename: "common/common.proto", }, { ExtendedType: (*descriptorpb.EnumValueOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_enum_value", Tag: "bytes,93117,opt,name=remove_deprecated_enum_value", Filename: "common/common.proto", }, { ExtendedType: (*descriptorpb.MethodOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_method", Tag: "bytes,93117,opt,name=remove_deprecated_method", Filename: "common/common.proto", }, { ExtendedType: (*descriptorpb.ServiceOptions)(nil), ExtensionType: (*string)(nil), Field: 93117, Name: "common.remove_deprecated_service", Tag: "bytes,93117,opt,name=remove_deprecated_service", Filename: "common/common.proto", }, } // Extension fields to descriptorpb.MessageOptions. var ( // Indicates the Talos version when this deprecated message will be removed from API. // // optional string remove_deprecated_message = 93117; E_RemoveDeprecatedMessage = &file_common_common_proto_extTypes[0] ) // Extension fields to descriptorpb.FieldOptions. var ( // Indicates the Talos version when this deprecated filed will be removed from API. // // optional string remove_deprecated_field = 93117; E_RemoveDeprecatedField = &file_common_common_proto_extTypes[1] ) // Extension fields to descriptorpb.EnumOptions. var ( // Indicates the Talos version when this deprecated enum will be removed from API. // // optional string remove_deprecated_enum = 93117; E_RemoveDeprecatedEnum = &file_common_common_proto_extTypes[2] ) // Extension fields to descriptorpb.EnumValueOptions. var ( // Indicates the Talos version when this deprecated enum value will be removed from API. // // optional string remove_deprecated_enum_value = 93117; E_RemoveDeprecatedEnumValue = &file_common_common_proto_extTypes[3] ) // Extension fields to descriptorpb.MethodOptions. var ( // Indicates the Talos version when this deprecated method will be removed from API. // // optional string remove_deprecated_method = 93117; E_RemoveDeprecatedMethod = &file_common_common_proto_extTypes[4] ) // Extension fields to descriptorpb.ServiceOptions. var ( // Indicates the Talos version when this deprecated service will be removed from API. // // optional string remove_deprecated_service = 93117; E_RemoveDeprecatedService = &file_common_common_proto_extTypes[5] ) var File_common_common_proto protoreflect.FileDescriptor const file_common_common_proto_rawDesc = "" + "\n" + "\x13common/common.proto\x12\x06common\x1a\x19google/protobuf/any.proto\x1a google/protobuf/descriptor.proto\x1a\x17google/rpc/status.proto\"s\n" + "\x05Error\x12 \n" + "\x04code\x18\x01 \x01(\x0e2\f.common.CodeR\x04code\x12\x18\n" + "\amessage\x18\x02 \x01(\tR\amessage\x12.\n" + "\adetails\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\adetails\"h\n" + "\bMetadata\x12\x1a\n" + "\bhostname\x18\x01 \x01(\tR\bhostname\x12\x14\n" + "\x05error\x18\x02 \x01(\tR\x05error\x12*\n" + "\x06status\x18\x03 \x01(\v2\x12.google.rpc.StatusR\x06status\"J\n" + "\x04Data\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x14\n" + "\x05bytes\x18\x02 \x01(\fR\x05bytes\"8\n" + "\fDataResponse\x12(\n" + "\bmessages\x18\x01 \x03(\v2\f.common.DataR\bmessages\"5\n" + "\x05Empty\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\":\n" + "\rEmptyResponse\x12)\n" + "\bmessages\x18\x01 \x03(\v2\r.common.EmptyR\bmessages\"\x80\x01\n" + "\x12ContainerdInstance\x12/\n" + "\x06driver\x18\x01 \x01(\x0e2\x17.common.ContainerDriverR\x06driver\x129\n" + "\tnamespace\x18\x02 \x01(\x0e2\x1b.common.ContainerdNamespaceR\tnamespace\"\"\n" + "\x03URL\x12\x1b\n" + "\tfull_path\x18\x01 \x01(\tR\bfullPath\"A\n" + "\x1bPEMEncodedCertificateAndKey\x12\x10\n" + "\x03crt\x18\x01 \x01(\fR\x03crt\x12\x10\n" + "\x03key\x18\x02 \x01(\fR\x03key\"!\n" + "\rPEMEncodedKey\x12\x10\n" + "\x03key\x18\x01 \x01(\fR\x03key\")\n" + "\x15PEMEncodedCertificate\x12\x10\n" + "\x03crt\x18\x01 \x01(\fR\x03crt\"\x17\n" + "\x05NetIP\x12\x0e\n" + "\x02ip\x18\x01 \x01(\fR\x02ip\"/\n" + "\tNetIPPort\x12\x0e\n" + "\x02ip\x18\x01 \x01(\fR\x02ip\x12\x12\n" + "\x04port\x18\x02 \x01(\x05R\x04port\"B\n" + "\vNetIPPrefix\x12\x0e\n" + "\x02ip\x18\x01 \x01(\fR\x02ip\x12#\n" + "\rprefix_length\x18\x02 \x01(\x05R\fprefixLength*+\n" + "\x04Code\x12\t\n" + "\x05FATAL\x10\x00\x12\n" + "\n" + "\x06LOCKED\x10\x01\x12\f\n" + "\bCANCELED\x10\x02**\n" + "\x0fContainerDriver\x12\x0e\n" + "\n" + "CONTAINERD\x10\x00\x12\a\n" + "\x03CRI\x10\x01*@\n" + "\x13ContainerdNamespace\x12\x0e\n" + "\n" + "NS_UNKNOWN\x10\x00\x12\r\n" + "\tNS_SYSTEM\x10\x01\x12\n" + "\n" + "\x06NS_CRI\x10\x02:]\n" + "\x19remove_deprecated_message\x12\x1f.google.protobuf.MessageOptions\x18\xbd\xd7\x05 \x01(\tR\x17removeDeprecatedMessage:W\n" + "\x17remove_deprecated_field\x12\x1d.google.protobuf.FieldOptions\x18\xbd\xd7\x05 \x01(\tR\x15removeDeprecatedField:T\n" + "\x16remove_deprecated_enum\x12\x1c.google.protobuf.EnumOptions\x18\xbd\xd7\x05 \x01(\tR\x14removeDeprecatedEnum:d\n" + "\x1cremove_deprecated_enum_value\x12!.google.protobuf.EnumValueOptions\x18\xbd\xd7\x05 \x01(\tR\x19removeDeprecatedEnumValue:Z\n" + "\x18remove_deprecated_method\x12\x1e.google.protobuf.MethodOptions\x18\xbd\xd7\x05 \x01(\tR\x16removeDeprecatedMethod:]\n" + "\x19remove_deprecated_service\x12\x1f.google.protobuf.ServiceOptions\x18\xbd\xd7\x05 \x01(\tR\x17removeDeprecatedServiceBL\n" + "\x14dev.talos.api.commonZ4github.com/siderolabs/talos/pkg/machinery/api/commonb\x06proto3" var ( file_common_common_proto_rawDescOnce sync.Once file_common_common_proto_rawDescData []byte ) func file_common_common_proto_rawDescGZIP() []byte { file_common_common_proto_rawDescOnce.Do(func() { file_common_common_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_common_common_proto_rawDesc), len(file_common_common_proto_rawDesc))) }) return file_common_common_proto_rawDescData } var file_common_common_proto_enumTypes = make([]protoimpl.EnumInfo, 3) var file_common_common_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_common_common_proto_goTypes = []any{ (Code)(0), // 0: common.Code (ContainerDriver)(0), // 1: common.ContainerDriver (ContainerdNamespace)(0), // 2: common.ContainerdNamespace (*Error)(nil), // 3: common.Error (*Metadata)(nil), // 4: common.Metadata (*Data)(nil), // 5: common.Data (*DataResponse)(nil), // 6: common.DataResponse (*Empty)(nil), // 7: common.Empty (*EmptyResponse)(nil), // 8: common.EmptyResponse (*ContainerdInstance)(nil), // 9: common.ContainerdInstance (*URL)(nil), // 10: common.URL (*PEMEncodedCertificateAndKey)(nil), // 11: common.PEMEncodedCertificateAndKey (*PEMEncodedKey)(nil), // 12: common.PEMEncodedKey (*PEMEncodedCertificate)(nil), // 13: common.PEMEncodedCertificate (*NetIP)(nil), // 14: common.NetIP (*NetIPPort)(nil), // 15: common.NetIPPort (*NetIPPrefix)(nil), // 16: common.NetIPPrefix (*anypb.Any)(nil), // 17: google.protobuf.Any (*status.Status)(nil), // 18: google.rpc.Status (*descriptorpb.MessageOptions)(nil), // 19: google.protobuf.MessageOptions (*descriptorpb.FieldOptions)(nil), // 20: google.protobuf.FieldOptions (*descriptorpb.EnumOptions)(nil), // 21: google.protobuf.EnumOptions (*descriptorpb.EnumValueOptions)(nil), // 22: google.protobuf.EnumValueOptions (*descriptorpb.MethodOptions)(nil), // 23: google.protobuf.MethodOptions (*descriptorpb.ServiceOptions)(nil), // 24: google.protobuf.ServiceOptions } var file_common_common_proto_depIdxs = []int32{ 0, // 0: common.Error.code:type_name -> common.Code 17, // 1: common.Error.details:type_name -> google.protobuf.Any 18, // 2: common.Metadata.status:type_name -> google.rpc.Status 4, // 3: common.Data.metadata:type_name -> common.Metadata 5, // 4: common.DataResponse.messages:type_name -> common.Data 4, // 5: common.Empty.metadata:type_name -> common.Metadata 7, // 6: common.EmptyResponse.messages:type_name -> common.Empty 1, // 7: common.ContainerdInstance.driver:type_name -> common.ContainerDriver 2, // 8: common.ContainerdInstance.namespace:type_name -> common.ContainerdNamespace 19, // 9: common.remove_deprecated_message:extendee -> google.protobuf.MessageOptions 20, // 10: common.remove_deprecated_field:extendee -> google.protobuf.FieldOptions 21, // 11: common.remove_deprecated_enum:extendee -> google.protobuf.EnumOptions 22, // 12: common.remove_deprecated_enum_value:extendee -> google.protobuf.EnumValueOptions 23, // 13: common.remove_deprecated_method:extendee -> google.protobuf.MethodOptions 24, // 14: common.remove_deprecated_service:extendee -> google.protobuf.ServiceOptions 15, // [15:15] is the sub-list for method output_type 15, // [15:15] is the sub-list for method input_type 15, // [15:15] is the sub-list for extension type_name 9, // [9:15] is the sub-list for extension extendee 0, // [0:9] is the sub-list for field type_name } func init() { file_common_common_proto_init() } func file_common_common_proto_init() { if File_common_common_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_common_common_proto_rawDesc), len(file_common_common_proto_rawDesc)), NumEnums: 3, NumMessages: 14, NumExtensions: 6, NumServices: 0, }, GoTypes: file_common_common_proto_goTypes, DependencyIndexes: file_common_common_proto_depIdxs, EnumInfos: file_common_common_proto_enumTypes, MessageInfos: file_common_common_proto_msgTypes, ExtensionInfos: file_common_common_proto_extTypes, }.Build() File_common_common_proto = out.File file_common_common_proto_goTypes = nil file_common_common_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/common/common_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: common/common.proto package common import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" anypb "github.com/planetscale/vtprotobuf/types/known/anypb" status "google.golang.org/genproto/googleapis/rpc/status" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb1 "google.golang.org/protobuf/types/known/anypb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *Error) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Error) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Error) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Details) > 0 { for iNdEx := len(m.Details) - 1; iNdEx >= 0; iNdEx-- { size, err := (*anypb.Any)(m.Details[iNdEx]).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if len(m.Message) > 0 { i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0x12 } if m.Code != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Code)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Metadata) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Metadata) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Metadata) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Status != nil { if vtmsg, ok := interface{}(m.Status).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Status) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Error))) i-- dAtA[i] = 0x12 } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Data) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Data) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Data) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Bytes) > 0 { i -= len(m.Bytes) copy(dAtA[i:], m.Bytes) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Bytes))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DataResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DataResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DataResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *Empty) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Empty) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Empty) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EmptyResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EmptyResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EmptyResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ContainerdInstance) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ContainerdInstance) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ContainerdInstance) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Namespace != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Namespace)) i-- dAtA[i] = 0x10 } if m.Driver != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Driver)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *URL) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *URL) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *URL) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.FullPath) > 0 { i -= len(m.FullPath) copy(dAtA[i:], m.FullPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FullPath))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PEMEncodedCertificateAndKey) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PEMEncodedCertificateAndKey) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PEMEncodedCertificateAndKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0x12 } if len(m.Crt) > 0 { i -= len(m.Crt) copy(dAtA[i:], m.Crt) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Crt))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PEMEncodedKey) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PEMEncodedKey) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PEMEncodedKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PEMEncodedCertificate) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PEMEncodedCertificate) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PEMEncodedCertificate) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Crt) > 0 { i -= len(m.Crt) copy(dAtA[i:], m.Crt) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Crt))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetIP) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetIP) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetIP) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Ip) > 0 { i -= len(m.Ip) copy(dAtA[i:], m.Ip) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ip))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetIPPort) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetIPPort) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetIPPort) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Port != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Port)) i-- dAtA[i] = 0x10 } if len(m.Ip) > 0 { i -= len(m.Ip) copy(dAtA[i:], m.Ip) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ip))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetIPPrefix) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetIPPrefix) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetIPPrefix) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.PrefixLength != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PrefixLength)) i-- dAtA[i] = 0x10 } if len(m.Ip) > 0 { i -= len(m.Ip) copy(dAtA[i:], m.Ip) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ip))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Error) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Code != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Code)) } l = len(m.Message) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Details) > 0 { for _, e := range m.Details { l = (*anypb.Any)(e).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Metadata) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Error) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Status != nil { if size, ok := interface{}(m.Status).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Status) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Data) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { l = m.Metadata.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Bytes) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DataResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Empty) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { l = m.Metadata.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EmptyResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ContainerdInstance) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Driver != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Driver)) } if m.Namespace != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Namespace)) } n += len(m.unknownFields) return n } func (m *URL) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.FullPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PEMEncodedCertificateAndKey) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Crt) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PEMEncodedKey) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PEMEncodedCertificate) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Crt) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NetIP) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Ip) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NetIPPort) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Ip) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Port != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Port)) } n += len(m.unknownFields) return n } func (m *NetIPPrefix) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Ip) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PrefixLength != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PrefixLength)) } n += len(m.unknownFields) return n } func (m *Error) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Error: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Error: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Code", wireType) } m.Code = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Code |= Code(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Details", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Details = append(m.Details, &anypb1.Any{}) if err := (*anypb.Any)(m.Details[len(m.Details)-1]).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Metadata) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Metadata: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Status == nil { m.Status = &status.Status{} } if unmarshal, ok := interface{}(m.Status).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Status); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Data) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Data: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Data: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &Metadata{} } if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Bytes", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Bytes = append(m.Bytes[:0], dAtA[iNdEx:postIndex]...) if m.Bytes == nil { m.Bytes = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DataResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DataResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DataResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Data{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Empty) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Empty: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Empty: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &Metadata{} } if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EmptyResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EmptyResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EmptyResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Empty{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ContainerdInstance) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ContainerdInstance: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ContainerdInstance: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } m.Driver = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Driver |= ContainerDriver(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } m.Namespace = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Namespace |= ContainerdNamespace(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *URL) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: URL: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: URL: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FullPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FullPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PEMEncodedCertificateAndKey) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PEMEncodedCertificateAndKey: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PEMEncodedCertificateAndKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Crt", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Crt = append(m.Crt[:0], dAtA[iNdEx:postIndex]...) if m.Crt == nil { m.Crt = []byte{} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) if m.Key == nil { m.Key = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PEMEncodedKey) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PEMEncodedKey: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PEMEncodedKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) if m.Key == nil { m.Key = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PEMEncodedCertificate) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PEMEncodedCertificate: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PEMEncodedCertificate: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Crt", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Crt = append(m.Crt[:0], dAtA[iNdEx:postIndex]...) if m.Crt == nil { m.Crt = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetIP) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetIP: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetIP: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ip", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ip = append(m.Ip[:0], dAtA[iNdEx:postIndex]...) if m.Ip == nil { m.Ip = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetIPPort) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetIPPort: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetIPPort: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ip", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ip = append(m.Ip[:0], dAtA[iNdEx:postIndex]...) if m.Ip == nil { m.Ip = []byte{} } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType) } m.Port = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Port |= int32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetIPPrefix) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetIPPrefix: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetIPPrefix: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ip", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ip = append(m.Ip[:0], dAtA[iNdEx:postIndex]...) if m.Ip == nil { m.Ip = []byte{} } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PrefixLength", wireType) } m.PrefixLength = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PrefixLength |= int32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/inspect/inspect.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: inspect/inspect.proto package inspect import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DependencyEdgeType int32 const ( DependencyEdgeType_OUTPUT_EXCLUSIVE DependencyEdgeType = 0 DependencyEdgeType_OUTPUT_SHARED DependencyEdgeType = 3 DependencyEdgeType_INPUT_STRONG DependencyEdgeType = 1 DependencyEdgeType_INPUT_WEAK DependencyEdgeType = 2 DependencyEdgeType_INPUT_DESTROY_READY DependencyEdgeType = 4 ) // Enum value maps for DependencyEdgeType. var ( DependencyEdgeType_name = map[int32]string{ 0: "OUTPUT_EXCLUSIVE", 3: "OUTPUT_SHARED", 1: "INPUT_STRONG", 2: "INPUT_WEAK", 4: "INPUT_DESTROY_READY", } DependencyEdgeType_value = map[string]int32{ "OUTPUT_EXCLUSIVE": 0, "OUTPUT_SHARED": 3, "INPUT_STRONG": 1, "INPUT_WEAK": 2, "INPUT_DESTROY_READY": 4, } ) func (x DependencyEdgeType) Enum() *DependencyEdgeType { p := new(DependencyEdgeType) *p = x return p } func (x DependencyEdgeType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DependencyEdgeType) Descriptor() protoreflect.EnumDescriptor { return file_inspect_inspect_proto_enumTypes[0].Descriptor() } func (DependencyEdgeType) Type() protoreflect.EnumType { return &file_inspect_inspect_proto_enumTypes[0] } func (x DependencyEdgeType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DependencyEdgeType.Descriptor instead. func (DependencyEdgeType) EnumDescriptor() ([]byte, []int) { return file_inspect_inspect_proto_rawDescGZIP(), []int{0} } // The ControllerRuntimeDependency message contains the graph of controller-resource dependencies. type ControllerRuntimeDependency struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Edges []*ControllerDependencyEdge `protobuf:"bytes,2,rep,name=edges,proto3" json:"edges,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControllerRuntimeDependency) Reset() { *x = ControllerRuntimeDependency{} mi := &file_inspect_inspect_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControllerRuntimeDependency) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControllerRuntimeDependency) ProtoMessage() {} func (x *ControllerRuntimeDependency) ProtoReflect() protoreflect.Message { mi := &file_inspect_inspect_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControllerRuntimeDependency.ProtoReflect.Descriptor instead. func (*ControllerRuntimeDependency) Descriptor() ([]byte, []int) { return file_inspect_inspect_proto_rawDescGZIP(), []int{0} } func (x *ControllerRuntimeDependency) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ControllerRuntimeDependency) GetEdges() []*ControllerDependencyEdge { if x != nil { return x.Edges } return nil } type ControllerRuntimeDependenciesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ControllerRuntimeDependency `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControllerRuntimeDependenciesResponse) Reset() { *x = ControllerRuntimeDependenciesResponse{} mi := &file_inspect_inspect_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControllerRuntimeDependenciesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControllerRuntimeDependenciesResponse) ProtoMessage() {} func (x *ControllerRuntimeDependenciesResponse) ProtoReflect() protoreflect.Message { mi := &file_inspect_inspect_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControllerRuntimeDependenciesResponse.ProtoReflect.Descriptor instead. func (*ControllerRuntimeDependenciesResponse) Descriptor() ([]byte, []int) { return file_inspect_inspect_proto_rawDescGZIP(), []int{1} } func (x *ControllerRuntimeDependenciesResponse) GetMessages() []*ControllerRuntimeDependency { if x != nil { return x.Messages } return nil } type ControllerDependencyEdge struct { state protoimpl.MessageState `protogen:"open.v1"` ControllerName string `protobuf:"bytes,1,opt,name=controller_name,json=controllerName,proto3" json:"controller_name,omitempty"` EdgeType DependencyEdgeType `protobuf:"varint,2,opt,name=edge_type,json=edgeType,proto3,enum=inspect.DependencyEdgeType" json:"edge_type,omitempty"` ResourceNamespace string `protobuf:"bytes,3,opt,name=resource_namespace,json=resourceNamespace,proto3" json:"resource_namespace,omitempty"` ResourceType string `protobuf:"bytes,4,opt,name=resource_type,json=resourceType,proto3" json:"resource_type,omitempty"` ResourceId string `protobuf:"bytes,5,opt,name=resource_id,json=resourceId,proto3" json:"resource_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControllerDependencyEdge) Reset() { *x = ControllerDependencyEdge{} mi := &file_inspect_inspect_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControllerDependencyEdge) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControllerDependencyEdge) ProtoMessage() {} func (x *ControllerDependencyEdge) ProtoReflect() protoreflect.Message { mi := &file_inspect_inspect_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControllerDependencyEdge.ProtoReflect.Descriptor instead. func (*ControllerDependencyEdge) Descriptor() ([]byte, []int) { return file_inspect_inspect_proto_rawDescGZIP(), []int{2} } func (x *ControllerDependencyEdge) GetControllerName() string { if x != nil { return x.ControllerName } return "" } func (x *ControllerDependencyEdge) GetEdgeType() DependencyEdgeType { if x != nil { return x.EdgeType } return DependencyEdgeType_OUTPUT_EXCLUSIVE } func (x *ControllerDependencyEdge) GetResourceNamespace() string { if x != nil { return x.ResourceNamespace } return "" } func (x *ControllerDependencyEdge) GetResourceType() string { if x != nil { return x.ResourceType } return "" } func (x *ControllerDependencyEdge) GetResourceId() string { if x != nil { return x.ResourceId } return "" } var File_inspect_inspect_proto protoreflect.FileDescriptor const file_inspect_inspect_proto_rawDesc = "" + "\n" + "\x15inspect/inspect.proto\x12\ainspect\x1a\x13common/common.proto\x1a\x1bgoogle/protobuf/empty.proto\"\x84\x01\n" + "\x1bControllerRuntimeDependency\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x127\n" + "\x05edges\x18\x02 \x03(\v2!.inspect.ControllerDependencyEdgeR\x05edges\"i\n" + "%ControllerRuntimeDependenciesResponse\x12@\n" + "\bmessages\x18\x01 \x03(\v2$.inspect.ControllerRuntimeDependencyR\bmessages\"\xf2\x01\n" + "\x18ControllerDependencyEdge\x12'\n" + "\x0fcontroller_name\x18\x01 \x01(\tR\x0econtrollerName\x128\n" + "\tedge_type\x18\x02 \x01(\x0e2\x1b.inspect.DependencyEdgeTypeR\bedgeType\x12-\n" + "\x12resource_namespace\x18\x03 \x01(\tR\x11resourceNamespace\x12#\n" + "\rresource_type\x18\x04 \x01(\tR\fresourceType\x12\x1f\n" + "\vresource_id\x18\x05 \x01(\tR\n" + "resourceId*x\n" + "\x12DependencyEdgeType\x12\x14\n" + "\x10OUTPUT_EXCLUSIVE\x10\x00\x12\x11\n" + "\rOUTPUT_SHARED\x10\x03\x12\x10\n" + "\fINPUT_STRONG\x10\x01\x12\x0e\n" + "\n" + "INPUT_WEAK\x10\x02\x12\x17\n" + "\x13INPUT_DESTROY_READY\x10\x042y\n" + "\x0eInspectService\x12g\n" + "\x1dControllerRuntimeDependencies\x12\x16.google.protobuf.Empty\x1a..inspect.ControllerRuntimeDependenciesResponseBN\n" + "\x15dev.talos.api.inspectZ5github.com/siderolabs/talos/pkg/machinery/api/inspectb\x06proto3" var ( file_inspect_inspect_proto_rawDescOnce sync.Once file_inspect_inspect_proto_rawDescData []byte ) func file_inspect_inspect_proto_rawDescGZIP() []byte { file_inspect_inspect_proto_rawDescOnce.Do(func() { file_inspect_inspect_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_inspect_inspect_proto_rawDesc), len(file_inspect_inspect_proto_rawDesc))) }) return file_inspect_inspect_proto_rawDescData } var file_inspect_inspect_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_inspect_inspect_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_inspect_inspect_proto_goTypes = []any{ (DependencyEdgeType)(0), // 0: inspect.DependencyEdgeType (*ControllerRuntimeDependency)(nil), // 1: inspect.ControllerRuntimeDependency (*ControllerRuntimeDependenciesResponse)(nil), // 2: inspect.ControllerRuntimeDependenciesResponse (*ControllerDependencyEdge)(nil), // 3: inspect.ControllerDependencyEdge (*common.Metadata)(nil), // 4: common.Metadata (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_inspect_inspect_proto_depIdxs = []int32{ 4, // 0: inspect.ControllerRuntimeDependency.metadata:type_name -> common.Metadata 3, // 1: inspect.ControllerRuntimeDependency.edges:type_name -> inspect.ControllerDependencyEdge 1, // 2: inspect.ControllerRuntimeDependenciesResponse.messages:type_name -> inspect.ControllerRuntimeDependency 0, // 3: inspect.ControllerDependencyEdge.edge_type:type_name -> inspect.DependencyEdgeType 5, // 4: inspect.InspectService.ControllerRuntimeDependencies:input_type -> google.protobuf.Empty 2, // 5: inspect.InspectService.ControllerRuntimeDependencies:output_type -> inspect.ControllerRuntimeDependenciesResponse 5, // [5:6] is the sub-list for method output_type 4, // [4:5] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_inspect_inspect_proto_init() } func file_inspect_inspect_proto_init() { if File_inspect_inspect_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_inspect_inspect_proto_rawDesc), len(file_inspect_inspect_proto_rawDesc)), NumEnums: 1, NumMessages: 3, NumExtensions: 0, NumServices: 1, }, GoTypes: file_inspect_inspect_proto_goTypes, DependencyIndexes: file_inspect_inspect_proto_depIdxs, EnumInfos: file_inspect_inspect_proto_enumTypes, MessageInfos: file_inspect_inspect_proto_msgTypes, }.Build() File_inspect_inspect_proto = out.File file_inspect_inspect_proto_goTypes = nil file_inspect_inspect_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/inspect/inspect_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: inspect/inspect.proto package inspect import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( InspectService_ControllerRuntimeDependencies_FullMethodName = "/inspect.InspectService/ControllerRuntimeDependencies" ) // InspectServiceClient is the client API for InspectService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The inspect service definition. // // InspectService provides auxiliary API to inspect OS internals. type InspectServiceClient interface { ControllerRuntimeDependencies(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ControllerRuntimeDependenciesResponse, error) } type inspectServiceClient struct { cc grpc.ClientConnInterface } func NewInspectServiceClient(cc grpc.ClientConnInterface) InspectServiceClient { return &inspectServiceClient{cc} } func (c *inspectServiceClient) ControllerRuntimeDependencies(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ControllerRuntimeDependenciesResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ControllerRuntimeDependenciesResponse) err := c.cc.Invoke(ctx, InspectService_ControllerRuntimeDependencies_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // InspectServiceServer is the server API for InspectService service. // All implementations must embed UnimplementedInspectServiceServer // for forward compatibility. // // The inspect service definition. // // InspectService provides auxiliary API to inspect OS internals. type InspectServiceServer interface { ControllerRuntimeDependencies(context.Context, *emptypb.Empty) (*ControllerRuntimeDependenciesResponse, error) mustEmbedUnimplementedInspectServiceServer() } // UnimplementedInspectServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedInspectServiceServer struct{} func (UnimplementedInspectServiceServer) ControllerRuntimeDependencies(context.Context, *emptypb.Empty) (*ControllerRuntimeDependenciesResponse, error) { return nil, status.Error(codes.Unimplemented, "method ControllerRuntimeDependencies not implemented") } func (UnimplementedInspectServiceServer) mustEmbedUnimplementedInspectServiceServer() {} func (UnimplementedInspectServiceServer) testEmbeddedByValue() {} // UnsafeInspectServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to InspectServiceServer will // result in compilation errors. type UnsafeInspectServiceServer interface { mustEmbedUnimplementedInspectServiceServer() } func RegisterInspectServiceServer(s grpc.ServiceRegistrar, srv InspectServiceServer) { // If the following call panics, it indicates UnimplementedInspectServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&InspectService_ServiceDesc, srv) } func _InspectService_ControllerRuntimeDependencies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(InspectServiceServer).ControllerRuntimeDependencies(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: InspectService_ControllerRuntimeDependencies_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(InspectServiceServer).ControllerRuntimeDependencies(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } // InspectService_ServiceDesc is the grpc.ServiceDesc for InspectService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var InspectService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "inspect.InspectService", HandlerType: (*InspectServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ControllerRuntimeDependencies", Handler: _InspectService_ControllerRuntimeDependencies_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "inspect/inspect.proto", } ================================================ FILE: pkg/machinery/api/inspect/inspect_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: inspect/inspect.proto package inspect import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ControllerRuntimeDependency) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControllerRuntimeDependency) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControllerRuntimeDependency) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Edges) > 0 { for iNdEx := len(m.Edges) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Edges[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ControllerRuntimeDependenciesResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControllerRuntimeDependenciesResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControllerRuntimeDependenciesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ControllerDependencyEdge) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControllerDependencyEdge) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControllerDependencyEdge) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ResourceId) > 0 { i -= len(m.ResourceId) copy(dAtA[i:], m.ResourceId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceId))) i-- dAtA[i] = 0x2a } if len(m.ResourceType) > 0 { i -= len(m.ResourceType) copy(dAtA[i:], m.ResourceType) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceType))) i-- dAtA[i] = 0x22 } if len(m.ResourceNamespace) > 0 { i -= len(m.ResourceNamespace) copy(dAtA[i:], m.ResourceNamespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ResourceNamespace))) i-- dAtA[i] = 0x1a } if m.EdgeType != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EdgeType)) i-- dAtA[i] = 0x10 } if len(m.ControllerName) > 0 { i -= len(m.ControllerName) copy(dAtA[i:], m.ControllerName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ControllerName))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ControllerRuntimeDependency) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Edges) > 0 { for _, e := range m.Edges { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ControllerRuntimeDependenciesResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ControllerDependencyEdge) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ControllerName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EdgeType != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.EdgeType)) } l = len(m.ResourceNamespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ResourceType) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ResourceId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ControllerRuntimeDependency) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControllerRuntimeDependency: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControllerRuntimeDependency: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Edges", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Edges = append(m.Edges, &ControllerDependencyEdge{}) if err := m.Edges[len(m.Edges)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ControllerRuntimeDependenciesResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControllerRuntimeDependenciesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControllerRuntimeDependenciesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ControllerRuntimeDependency{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ControllerDependencyEdge) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControllerDependencyEdge: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControllerDependencyEdge: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControllerName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ControllerName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EdgeType", wireType) } m.EdgeType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EdgeType |= DependencyEdgeType(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ResourceNamespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ResourceNamespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ResourceType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ResourceType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ResourceId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ResourceId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/machine/debug.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: machine/debug.proto package machine import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type DebugContainerRunRequestSpec_Profile int32 const ( DebugContainerRunRequestSpec_PROFILE_UNSPECIFIED DebugContainerRunRequestSpec_Profile = 0 DebugContainerRunRequestSpec_PROFILE_PRIVILEGED DebugContainerRunRequestSpec_Profile = 1 ) // Enum value maps for DebugContainerRunRequestSpec_Profile. var ( DebugContainerRunRequestSpec_Profile_name = map[int32]string{ 0: "PROFILE_UNSPECIFIED", 1: "PROFILE_PRIVILEGED", } DebugContainerRunRequestSpec_Profile_value = map[string]int32{ "PROFILE_UNSPECIFIED": 0, "PROFILE_PRIVILEGED": 1, } ) func (x DebugContainerRunRequestSpec_Profile) Enum() *DebugContainerRunRequestSpec_Profile { p := new(DebugContainerRunRequestSpec_Profile) *p = x return p } func (x DebugContainerRunRequestSpec_Profile) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (DebugContainerRunRequestSpec_Profile) Descriptor() protoreflect.EnumDescriptor { return file_machine_debug_proto_enumTypes[0].Descriptor() } func (DebugContainerRunRequestSpec_Profile) Type() protoreflect.EnumType { return &file_machine_debug_proto_enumTypes[0] } func (x DebugContainerRunRequestSpec_Profile) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use DebugContainerRunRequestSpec_Profile.Descriptor instead. func (DebugContainerRunRequestSpec_Profile) EnumDescriptor() ([]byte, []int) { return file_machine_debug_proto_rawDescGZIP(), []int{1, 0} } type DebugContainerRunRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Request: // // *DebugContainerRunRequest_Spec // *DebugContainerRunRequest_StdinData // *DebugContainerRunRequest_Signal // *DebugContainerRunRequest_TermResize Request isDebugContainerRunRequest_Request `protobuf_oneof:"request"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DebugContainerRunRequest) Reset() { *x = DebugContainerRunRequest{} mi := &file_machine_debug_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DebugContainerRunRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugContainerRunRequest) ProtoMessage() {} func (x *DebugContainerRunRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_debug_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugContainerRunRequest.ProtoReflect.Descriptor instead. func (*DebugContainerRunRequest) Descriptor() ([]byte, []int) { return file_machine_debug_proto_rawDescGZIP(), []int{0} } func (x *DebugContainerRunRequest) GetRequest() isDebugContainerRunRequest_Request { if x != nil { return x.Request } return nil } func (x *DebugContainerRunRequest) GetSpec() *DebugContainerRunRequestSpec { if x != nil { if x, ok := x.Request.(*DebugContainerRunRequest_Spec); ok { return x.Spec } } return nil } func (x *DebugContainerRunRequest) GetStdinData() []byte { if x != nil { if x, ok := x.Request.(*DebugContainerRunRequest_StdinData); ok { return x.StdinData } } return nil } func (x *DebugContainerRunRequest) GetSignal() int32 { if x != nil { if x, ok := x.Request.(*DebugContainerRunRequest_Signal); ok { return x.Signal } } return 0 } func (x *DebugContainerRunRequest) GetTermResize() *DebugContainerTerminalResize { if x != nil { if x, ok := x.Request.(*DebugContainerRunRequest_TermResize); ok { return x.TermResize } } return nil } type isDebugContainerRunRequest_Request interface { isDebugContainerRunRequest_Request() } type DebugContainerRunRequest_Spec struct { // 1. send the container spec Spec *DebugContainerRunRequestSpec `protobuf:"bytes,1,opt,name=spec,proto3,oneof"` } type DebugContainerRunRequest_StdinData struct { // 2. send either of the three below to interact with the running container StdinData []byte `protobuf:"bytes,2,opt,name=stdin_data,json=stdinData,proto3,oneof"` } type DebugContainerRunRequest_Signal struct { Signal int32 `protobuf:"varint,3,opt,name=signal,proto3,oneof"` } type DebugContainerRunRequest_TermResize struct { TermResize *DebugContainerTerminalResize `protobuf:"bytes,4,opt,name=term_resize,json=termResize,proto3,oneof"` } func (*DebugContainerRunRequest_Spec) isDebugContainerRunRequest_Request() {} func (*DebugContainerRunRequest_StdinData) isDebugContainerRunRequest_Request() {} func (*DebugContainerRunRequest_Signal) isDebugContainerRunRequest_Request() {} func (*DebugContainerRunRequest_TermResize) isDebugContainerRunRequest_Request() {} type DebugContainerRunRequestSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` ImageName string `protobuf:"bytes,2,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"` Args []string `protobuf:"bytes,3,rep,name=args,proto3" json:"args,omitempty"` Env map[string]string `protobuf:"bytes,4,rep,name=env,proto3" json:"env,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Profile DebugContainerRunRequestSpec_Profile `protobuf:"varint,5,opt,name=profile,proto3,enum=machine.DebugContainerRunRequestSpec_Profile" json:"profile,omitempty"` Tty bool `protobuf:"varint,6,opt,name=tty,proto3" json:"tty,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DebugContainerRunRequestSpec) Reset() { *x = DebugContainerRunRequestSpec{} mi := &file_machine_debug_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DebugContainerRunRequestSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugContainerRunRequestSpec) ProtoMessage() {} func (x *DebugContainerRunRequestSpec) ProtoReflect() protoreflect.Message { mi := &file_machine_debug_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugContainerRunRequestSpec.ProtoReflect.Descriptor instead. func (*DebugContainerRunRequestSpec) Descriptor() ([]byte, []int) { return file_machine_debug_proto_rawDescGZIP(), []int{1} } func (x *DebugContainerRunRequestSpec) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } func (x *DebugContainerRunRequestSpec) GetImageName() string { if x != nil { return x.ImageName } return "" } func (x *DebugContainerRunRequestSpec) GetArgs() []string { if x != nil { return x.Args } return nil } func (x *DebugContainerRunRequestSpec) GetEnv() map[string]string { if x != nil { return x.Env } return nil } func (x *DebugContainerRunRequestSpec) GetProfile() DebugContainerRunRequestSpec_Profile { if x != nil { return x.Profile } return DebugContainerRunRequestSpec_PROFILE_UNSPECIFIED } func (x *DebugContainerRunRequestSpec) GetTty() bool { if x != nil { return x.Tty } return false } type DebugContainerTerminalResize struct { state protoimpl.MessageState `protogen:"open.v1"` Width int32 `protobuf:"varint,1,opt,name=width,proto3" json:"width,omitempty"` Height int32 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DebugContainerTerminalResize) Reset() { *x = DebugContainerTerminalResize{} mi := &file_machine_debug_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DebugContainerTerminalResize) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugContainerTerminalResize) ProtoMessage() {} func (x *DebugContainerTerminalResize) ProtoReflect() protoreflect.Message { mi := &file_machine_debug_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugContainerTerminalResize.ProtoReflect.Descriptor instead. func (*DebugContainerTerminalResize) Descriptor() ([]byte, []int) { return file_machine_debug_proto_rawDescGZIP(), []int{2} } func (x *DebugContainerTerminalResize) GetWidth() int32 { if x != nil { return x.Width } return 0 } func (x *DebugContainerTerminalResize) GetHeight() int32 { if x != nil { return x.Height } return 0 } type DebugContainerRunResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Resp: // // *DebugContainerRunResponse_StdoutData // *DebugContainerRunResponse_ExitCode Resp isDebugContainerRunResponse_Resp `protobuf_oneof:"resp"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DebugContainerRunResponse) Reset() { *x = DebugContainerRunResponse{} mi := &file_machine_debug_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DebugContainerRunResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*DebugContainerRunResponse) ProtoMessage() {} func (x *DebugContainerRunResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_debug_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DebugContainerRunResponse.ProtoReflect.Descriptor instead. func (*DebugContainerRunResponse) Descriptor() ([]byte, []int) { return file_machine_debug_proto_rawDescGZIP(), []int{3} } func (x *DebugContainerRunResponse) GetResp() isDebugContainerRunResponse_Resp { if x != nil { return x.Resp } return nil } func (x *DebugContainerRunResponse) GetStdoutData() []byte { if x != nil { if x, ok := x.Resp.(*DebugContainerRunResponse_StdoutData); ok { return x.StdoutData } } return nil } func (x *DebugContainerRunResponse) GetExitCode() int32 { if x != nil { if x, ok := x.Resp.(*DebugContainerRunResponse_ExitCode); ok { return x.ExitCode } } return 0 } type isDebugContainerRunResponse_Resp interface { isDebugContainerRunResponse_Resp() } type DebugContainerRunResponse_StdoutData struct { StdoutData []byte `protobuf:"bytes,2,opt,name=stdout_data,json=stdoutData,proto3,oneof"` } type DebugContainerRunResponse_ExitCode struct { ExitCode int32 `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3,oneof"` } func (*DebugContainerRunResponse_StdoutData) isDebugContainerRunResponse_Resp() {} func (*DebugContainerRunResponse_ExitCode) isDebugContainerRunResponse_Resp() {} var File_machine_debug_proto protoreflect.FileDescriptor const file_machine_debug_proto_rawDesc = "" + "\n" + "\x13machine/debug.proto\x12\amachine\x1a\x13common/common.proto\"\xe7\x01\n" + "\x18DebugContainerRunRequest\x12;\n" + "\x04spec\x18\x01 \x01(\v2%.machine.DebugContainerRunRequestSpecH\x00R\x04spec\x12\x1f\n" + "\n" + "stdin_data\x18\x02 \x01(\fH\x00R\tstdinData\x12\x18\n" + "\x06signal\x18\x03 \x01(\x05H\x00R\x06signal\x12H\n" + "\vterm_resize\x18\x04 \x01(\v2%.machine.DebugContainerTerminalResizeH\x00R\n" + "termResizeB\t\n" + "\arequest\"\x9e\x03\n" + "\x1cDebugContainerRunRequestSpec\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\x12\x1d\n" + "\n" + "image_name\x18\x02 \x01(\tR\timageName\x12\x12\n" + "\x04args\x18\x03 \x03(\tR\x04args\x12@\n" + "\x03env\x18\x04 \x03(\v2..machine.DebugContainerRunRequestSpec.EnvEntryR\x03env\x12G\n" + "\aprofile\x18\x05 \x01(\x0e2-.machine.DebugContainerRunRequestSpec.ProfileR\aprofile\x12\x10\n" + "\x03tty\x18\x06 \x01(\bR\x03tty\x1a6\n" + "\bEnvEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\":\n" + "\aProfile\x12\x17\n" + "\x13PROFILE_UNSPECIFIED\x10\x00\x12\x16\n" + "\x12PROFILE_PRIVILEGED\x10\x01\"L\n" + "\x1cDebugContainerTerminalResize\x12\x14\n" + "\x05width\x18\x01 \x01(\x05R\x05width\x12\x16\n" + "\x06height\x18\x02 \x01(\x05R\x06height\"e\n" + "\x19DebugContainerRunResponse\x12!\n" + "\vstdout_data\x18\x02 \x01(\fH\x00R\n" + "stdoutData\x12\x1d\n" + "\texit_code\x18\x03 \x01(\x05H\x00R\bexitCodeB\x06\n" + "\x04resp2i\n" + "\fDebugService\x12Y\n" + "\fContainerRun\x12!.machine.DebugContainerRunRequest\x1a\".machine.DebugContainerRunResponse(\x010\x01BN\n" + "\x15dev.talos.api.machineZ5github.com/siderolabs/talos/pkg/machinery/api/machineb\x06proto3" var ( file_machine_debug_proto_rawDescOnce sync.Once file_machine_debug_proto_rawDescData []byte ) func file_machine_debug_proto_rawDescGZIP() []byte { file_machine_debug_proto_rawDescOnce.Do(func() { file_machine_debug_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_machine_debug_proto_rawDesc), len(file_machine_debug_proto_rawDesc))) }) return file_machine_debug_proto_rawDescData } var file_machine_debug_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_machine_debug_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_machine_debug_proto_goTypes = []any{ (DebugContainerRunRequestSpec_Profile)(0), // 0: machine.DebugContainerRunRequestSpec.Profile (*DebugContainerRunRequest)(nil), // 1: machine.DebugContainerRunRequest (*DebugContainerRunRequestSpec)(nil), // 2: machine.DebugContainerRunRequestSpec (*DebugContainerTerminalResize)(nil), // 3: machine.DebugContainerTerminalResize (*DebugContainerRunResponse)(nil), // 4: machine.DebugContainerRunResponse nil, // 5: machine.DebugContainerRunRequestSpec.EnvEntry (*common.ContainerdInstance)(nil), // 6: common.ContainerdInstance } var file_machine_debug_proto_depIdxs = []int32{ 2, // 0: machine.DebugContainerRunRequest.spec:type_name -> machine.DebugContainerRunRequestSpec 3, // 1: machine.DebugContainerRunRequest.term_resize:type_name -> machine.DebugContainerTerminalResize 6, // 2: machine.DebugContainerRunRequestSpec.containerd:type_name -> common.ContainerdInstance 5, // 3: machine.DebugContainerRunRequestSpec.env:type_name -> machine.DebugContainerRunRequestSpec.EnvEntry 0, // 4: machine.DebugContainerRunRequestSpec.profile:type_name -> machine.DebugContainerRunRequestSpec.Profile 1, // 5: machine.DebugService.ContainerRun:input_type -> machine.DebugContainerRunRequest 4, // 6: machine.DebugService.ContainerRun:output_type -> machine.DebugContainerRunResponse 6, // [6:7] is the sub-list for method output_type 5, // [5:6] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name } func init() { file_machine_debug_proto_init() } func file_machine_debug_proto_init() { if File_machine_debug_proto != nil { return } file_machine_debug_proto_msgTypes[0].OneofWrappers = []any{ (*DebugContainerRunRequest_Spec)(nil), (*DebugContainerRunRequest_StdinData)(nil), (*DebugContainerRunRequest_Signal)(nil), (*DebugContainerRunRequest_TermResize)(nil), } file_machine_debug_proto_msgTypes[3].OneofWrappers = []any{ (*DebugContainerRunResponse_StdoutData)(nil), (*DebugContainerRunResponse_ExitCode)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_machine_debug_proto_rawDesc), len(file_machine_debug_proto_rawDesc)), NumEnums: 1, NumMessages: 5, NumExtensions: 0, NumServices: 1, }, GoTypes: file_machine_debug_proto_goTypes, DependencyIndexes: file_machine_debug_proto_depIdxs, EnumInfos: file_machine_debug_proto_enumTypes, MessageInfos: file_machine_debug_proto_msgTypes, }.Build() File_machine_debug_proto = out.File file_machine_debug_proto_goTypes = nil file_machine_debug_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/machine/debug_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: machine/debug.proto package machine import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( DebugService_ContainerRun_FullMethodName = "/machine.DebugService/ContainerRun" ) // DebugServiceClient is the client API for DebugService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // DebugService provides debugging and inspection capabilities for a Talos node. type DebugServiceClient interface { // ContainerRun runs a debug container, attaches to it, and streams I/O. ContainerRun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[DebugContainerRunRequest, DebugContainerRunResponse], error) } type debugServiceClient struct { cc grpc.ClientConnInterface } func NewDebugServiceClient(cc grpc.ClientConnInterface) DebugServiceClient { return &debugServiceClient{cc} } func (c *debugServiceClient) ContainerRun(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[DebugContainerRunRequest, DebugContainerRunResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &DebugService_ServiceDesc.Streams[0], DebugService_ContainerRun_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[DebugContainerRunRequest, DebugContainerRunResponse]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type DebugService_ContainerRunClient = grpc.BidiStreamingClient[DebugContainerRunRequest, DebugContainerRunResponse] // DebugServiceServer is the server API for DebugService service. // All implementations must embed UnimplementedDebugServiceServer // for forward compatibility. // // DebugService provides debugging and inspection capabilities for a Talos node. type DebugServiceServer interface { // ContainerRun runs a debug container, attaches to it, and streams I/O. ContainerRun(grpc.BidiStreamingServer[DebugContainerRunRequest, DebugContainerRunResponse]) error mustEmbedUnimplementedDebugServiceServer() } // UnimplementedDebugServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedDebugServiceServer struct{} func (UnimplementedDebugServiceServer) ContainerRun(grpc.BidiStreamingServer[DebugContainerRunRequest, DebugContainerRunResponse]) error { return status.Error(codes.Unimplemented, "method ContainerRun not implemented") } func (UnimplementedDebugServiceServer) mustEmbedUnimplementedDebugServiceServer() {} func (UnimplementedDebugServiceServer) testEmbeddedByValue() {} // UnsafeDebugServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to DebugServiceServer will // result in compilation errors. type UnsafeDebugServiceServer interface { mustEmbedUnimplementedDebugServiceServer() } func RegisterDebugServiceServer(s grpc.ServiceRegistrar, srv DebugServiceServer) { // If the following call panics, it indicates UnimplementedDebugServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&DebugService_ServiceDesc, srv) } func _DebugService_ContainerRun_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(DebugServiceServer).ContainerRun(&grpc.GenericServerStream[DebugContainerRunRequest, DebugContainerRunResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type DebugService_ContainerRunServer = grpc.BidiStreamingServer[DebugContainerRunRequest, DebugContainerRunResponse] // DebugService_ServiceDesc is the grpc.ServiceDesc for DebugService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var DebugService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "machine.DebugService", HandlerType: (*DebugServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "ContainerRun", Handler: _DebugService_ContainerRun_Handler, ServerStreams: true, ClientStreams: true, }, }, Metadata: "machine/debug.proto", } ================================================ FILE: pkg/machinery/api/machine/debug_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: machine/debug.proto package machine import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *DebugContainerRunRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DebugContainerRunRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if vtmsg, ok := m.Request.(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size } return len(dAtA) - i, nil } func (m *DebugContainerRunRequest_Spec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequest_Spec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) if m.Spec != nil { size, err := m.Spec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } else { i = protohelpers.EncodeVarint(dAtA, i, 0) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DebugContainerRunRequest_StdinData) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequest_StdinData) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i -= len(m.StdinData) copy(dAtA[i:], m.StdinData) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.StdinData))) i-- dAtA[i] = 0x12 return len(dAtA) - i, nil } func (m *DebugContainerRunRequest_Signal) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequest_Signal) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Signal)) i-- dAtA[i] = 0x18 return len(dAtA) - i, nil } func (m *DebugContainerRunRequest_TermResize) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequest_TermResize) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) if m.TermResize != nil { size, err := m.TermResize.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } else { i = protohelpers.EncodeVarint(dAtA, i, 0) i-- dAtA[i] = 0x22 } return len(dAtA) - i, nil } func (m *DebugContainerRunRequestSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DebugContainerRunRequestSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Tty { i-- if m.Tty { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.Profile != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Profile)) i-- dAtA[i] = 0x28 } if len(m.Env) > 0 { for k := range m.Env { v := m.Env[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if len(m.Args) > 0 { for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Args[iNdEx]) copy(dAtA[i:], m.Args[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) i-- dAtA[i] = 0x1a } } if len(m.ImageName) > 0 { i -= len(m.ImageName) copy(dAtA[i:], m.ImageName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImageName))) i-- dAtA[i] = 0x12 } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DebugContainerTerminalResize) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DebugContainerTerminalResize) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerTerminalResize) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Height != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Height)) i-- dAtA[i] = 0x10 } if m.Width != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Width)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DebugContainerRunResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DebugContainerRunResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if vtmsg, ok := m.Resp.(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size } return len(dAtA) - i, nil } func (m *DebugContainerRunResponse_StdoutData) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunResponse_StdoutData) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i -= len(m.StdoutData) copy(dAtA[i:], m.StdoutData) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.StdoutData))) i-- dAtA[i] = 0x12 return len(dAtA) - i, nil } func (m *DebugContainerRunResponse_ExitCode) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DebugContainerRunResponse_ExitCode) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ExitCode)) i-- dAtA[i] = 0x18 return len(dAtA) - i, nil } func (m *DebugContainerRunRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if vtmsg, ok := m.Request.(interface{ SizeVT() int }); ok { n += vtmsg.SizeVT() } n += len(m.unknownFields) return n } func (m *DebugContainerRunRequest_Spec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Spec != nil { l = m.Spec.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } else { n += 2 } return n } func (m *DebugContainerRunRequest_StdinData) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.StdinData) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) return n } func (m *DebugContainerRunRequest_Signal) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += 1 + protohelpers.SizeOfVarint(uint64(m.Signal)) return n } func (m *DebugContainerRunRequest_TermResize) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.TermResize != nil { l = m.TermResize.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } else { n += 2 } return n } func (m *DebugContainerRunRequestSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ImageName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Args) > 0 { for _, s := range m.Args { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Env) > 0 { for k, v := range m.Env { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if m.Profile != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Profile)) } if m.Tty { n += 2 } n += len(m.unknownFields) return n } func (m *DebugContainerTerminalResize) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Width != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Width)) } if m.Height != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Height)) } n += len(m.unknownFields) return n } func (m *DebugContainerRunResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if vtmsg, ok := m.Resp.(interface{ SizeVT() int }); ok { n += vtmsg.SizeVT() } n += len(m.unknownFields) return n } func (m *DebugContainerRunResponse_StdoutData) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.StdoutData) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) return n } func (m *DebugContainerRunResponse_ExitCode) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += 1 + protohelpers.SizeOfVarint(uint64(m.ExitCode)) return n } func (m *DebugContainerRunRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DebugContainerRunRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DebugContainerRunRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if oneof, ok := m.Request.(*DebugContainerRunRequest_Spec); ok { if err := oneof.Spec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { v := &DebugContainerRunRequestSpec{} if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } m.Request = &DebugContainerRunRequest_Spec{Spec: v} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StdinData", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } v := make([]byte, postIndex-iNdEx) copy(v, dAtA[iNdEx:postIndex]) m.Request = &DebugContainerRunRequest_StdinData{StdinData: v} iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Signal", wireType) } var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.Request = &DebugContainerRunRequest_Signal{Signal: v} case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TermResize", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if oneof, ok := m.Request.(*DebugContainerRunRequest_TermResize); ok { if err := oneof.TermResize.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { v := &DebugContainerTerminalResize{} if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } m.Request = &DebugContainerRunRequest_TermResize{TermResize: v} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DebugContainerRunRequestSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DebugContainerRunRequestSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DebugContainerRunRequestSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImageName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Env", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Env == nil { m.Env = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Env[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Profile", wireType) } m.Profile = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Profile |= DebugContainerRunRequestSpec_Profile(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tty", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Tty = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DebugContainerTerminalResize) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DebugContainerTerminalResize: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DebugContainerTerminalResize: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Width", wireType) } m.Width = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Width |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) } m.Height = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Height |= int32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DebugContainerRunResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DebugContainerRunResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DebugContainerRunResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StdoutData", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } v := make([]byte, postIndex-iNdEx) copy(v, dAtA[iNdEx:postIndex]) m.Resp = &DebugContainerRunResponse_StdoutData{StdoutData: v} iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) } var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.Resp = &DebugContainerRunResponse_ExitCode{ExitCode: v} default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/machine/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package machine contains the machine service API definitions. package machine import ( fmt "fmt" "github.com/dustin/go-humanize" ) // Fmt formats the pull progress status into a human-readable string. func (s *ImageServicePullLayerProgress) Fmt() string { switch s.GetStatus() { case ImageServicePullLayerProgress_DOWNLOADING: return fmt.Sprintf("downloading layer %s/%s (%.1f%%)", humanize.IBytes(uint64(s.GetOffset())), humanize.IBytes(uint64(s.GetTotal())), float64(s.GetOffset())/float64(s.GetTotal())*100.0, ) case ImageServicePullLayerProgress_DOWNLOAD_COMPLETE: return "layer download complete" case ImageServicePullLayerProgress_EXTRACTING: return fmt.Sprintf("extracting layer (%s)", s.Elapsed.AsDuration()) case ImageServicePullLayerProgress_EXTRACT_COMPLETE: return "layer pull complete" case ImageServicePullLayerProgress_ALREADY_EXISTS: return "layer already exists" } return "" } ================================================ FILE: pkg/machinery/api/machine/image.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: machine/image.proto package machine import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" emptypb "google.golang.org/protobuf/types/known/emptypb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ImageServicePullLayerProgress_Status int32 const ( // Keep this in sync with ImagePullLayerProgress.Status. ImageServicePullLayerProgress_DOWNLOADING ImageServicePullLayerProgress_Status = 0 ImageServicePullLayerProgress_DOWNLOAD_COMPLETE ImageServicePullLayerProgress_Status = 1 ImageServicePullLayerProgress_EXTRACTING ImageServicePullLayerProgress_Status = 2 ImageServicePullLayerProgress_EXTRACT_COMPLETE ImageServicePullLayerProgress_Status = 3 ImageServicePullLayerProgress_ALREADY_EXISTS ImageServicePullLayerProgress_Status = 4 ) // Enum value maps for ImageServicePullLayerProgress_Status. var ( ImageServicePullLayerProgress_Status_name = map[int32]string{ 0: "DOWNLOADING", 1: "DOWNLOAD_COMPLETE", 2: "EXTRACTING", 3: "EXTRACT_COMPLETE", 4: "ALREADY_EXISTS", } ImageServicePullLayerProgress_Status_value = map[string]int32{ "DOWNLOADING": 0, "DOWNLOAD_COMPLETE": 1, "EXTRACTING": 2, "EXTRACT_COMPLETE": 3, "ALREADY_EXISTS": 4, } ) func (x ImageServicePullLayerProgress_Status) Enum() *ImageServicePullLayerProgress_Status { p := new(ImageServicePullLayerProgress_Status) *p = x return p } func (x ImageServicePullLayerProgress_Status) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ImageServicePullLayerProgress_Status) Descriptor() protoreflect.EnumDescriptor { return file_machine_image_proto_enumTypes[0].Descriptor() } func (ImageServicePullLayerProgress_Status) Type() protoreflect.EnumType { return &file_machine_image_proto_enumTypes[0] } func (x ImageServicePullLayerProgress_Status) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ImageServicePullLayerProgress_Status.Descriptor instead. func (ImageServicePullLayerProgress_Status) EnumDescriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{6, 0} } type ImageServiceListRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceListRequest) Reset() { *x = ImageServiceListRequest{} mi := &file_machine_image_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceListRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceListRequest) ProtoMessage() {} func (x *ImageServiceListRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceListRequest.ProtoReflect.Descriptor instead. func (*ImageServiceListRequest) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{0} } func (x *ImageServiceListRequest) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } type ImageServiceListResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Digest string `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` Labels map[string]string `protobuf:"bytes,5,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceListResponse) Reset() { *x = ImageServiceListResponse{} mi := &file_machine_image_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceListResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceListResponse) ProtoMessage() {} func (x *ImageServiceListResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceListResponse.ProtoReflect.Descriptor instead. func (*ImageServiceListResponse) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{1} } func (x *ImageServiceListResponse) GetName() string { if x != nil { return x.Name } return "" } func (x *ImageServiceListResponse) GetDigest() string { if x != nil { return x.Digest } return "" } func (x *ImageServiceListResponse) GetSize() int64 { if x != nil { return x.Size } return 0 } func (x *ImageServiceListResponse) GetCreatedAt() *timestamppb.Timestamp { if x != nil { return x.CreatedAt } return nil } func (x *ImageServiceListResponse) GetLabels() map[string]string { if x != nil { return x.Labels } return nil } type ImageServicePullRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` // Image reference to pull. ImageRef string `protobuf:"bytes,3,opt,name=image_ref,json=imageRef,proto3" json:"image_ref,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServicePullRequest) Reset() { *x = ImageServicePullRequest{} mi := &file_machine_image_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServicePullRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServicePullRequest) ProtoMessage() {} func (x *ImageServicePullRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServicePullRequest.ProtoReflect.Descriptor instead. func (*ImageServicePullRequest) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{2} } func (x *ImageServicePullRequest) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } func (x *ImageServicePullRequest) GetImageRef() string { if x != nil { return x.ImageRef } return "" } type ImageServicePullResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Response: // // *ImageServicePullResponse_Name // *ImageServicePullResponse_PullProgress Response isImageServicePullResponse_Response `protobuf_oneof:"response"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServicePullResponse) Reset() { *x = ImageServicePullResponse{} mi := &file_machine_image_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServicePullResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServicePullResponse) ProtoMessage() {} func (x *ImageServicePullResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServicePullResponse.ProtoReflect.Descriptor instead. func (*ImageServicePullResponse) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{3} } func (x *ImageServicePullResponse) GetResponse() isImageServicePullResponse_Response { if x != nil { return x.Response } return nil } func (x *ImageServicePullResponse) GetName() string { if x != nil { if x, ok := x.Response.(*ImageServicePullResponse_Name); ok { return x.Name } } return "" } func (x *ImageServicePullResponse) GetPullProgress() *ImageServicePullProgress { if x != nil { if x, ok := x.Response.(*ImageServicePullResponse_PullProgress); ok { return x.PullProgress } } return nil } type isImageServicePullResponse_Response interface { isImageServicePullResponse_Response() } type ImageServicePullResponse_Name struct { // Name of the pulled image (when done). Name string `protobuf:"bytes,1,opt,name=name,proto3,oneof"` } type ImageServicePullResponse_PullProgress struct { // Progress of the image pull (intermediate updates). PullProgress *ImageServicePullProgress `protobuf:"bytes,2,opt,name=pull_progress,json=pullProgress,proto3,oneof"` } func (*ImageServicePullResponse_Name) isImageServicePullResponse_Response() {} func (*ImageServicePullResponse_PullProgress) isImageServicePullResponse_Response() {} type ImageServiceImportRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Request: // // *ImageServiceImportRequest_Containerd // *ImageServiceImportRequest_ImageChunk Request isImageServiceImportRequest_Request `protobuf_oneof:"request"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceImportRequest) Reset() { *x = ImageServiceImportRequest{} mi := &file_machine_image_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceImportRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceImportRequest) ProtoMessage() {} func (x *ImageServiceImportRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceImportRequest.ProtoReflect.Descriptor instead. func (*ImageServiceImportRequest) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{4} } func (x *ImageServiceImportRequest) GetRequest() isImageServiceImportRequest_Request { if x != nil { return x.Request } return nil } func (x *ImageServiceImportRequest) GetContainerd() *common.ContainerdInstance { if x != nil { if x, ok := x.Request.(*ImageServiceImportRequest_Containerd); ok { return x.Containerd } } return nil } func (x *ImageServiceImportRequest) GetImageChunk() *common.Data { if x != nil { if x, ok := x.Request.(*ImageServiceImportRequest_ImageChunk); ok { return x.ImageChunk } } return nil } type isImageServiceImportRequest_Request interface { isImageServiceImportRequest_Request() } type ImageServiceImportRequest_Containerd struct { // Containerd instance to use. Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3,oneof"` } type ImageServiceImportRequest_ImageChunk struct { // Chunk of the image tarball. ImageChunk *common.Data `protobuf:"bytes,2,opt,name=image_chunk,json=imageChunk,proto3,oneof"` } func (*ImageServiceImportRequest_Containerd) isImageServiceImportRequest_Request() {} func (*ImageServiceImportRequest_ImageChunk) isImageServiceImportRequest_Request() {} type ImageServiceImportResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Name of the imported image. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceImportResponse) Reset() { *x = ImageServiceImportResponse{} mi := &file_machine_image_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceImportResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceImportResponse) ProtoMessage() {} func (x *ImageServiceImportResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceImportResponse.ProtoReflect.Descriptor instead. func (*ImageServiceImportResponse) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{5} } func (x *ImageServiceImportResponse) GetName() string { if x != nil { return x.Name } return "" } type ImageServicePullLayerProgress struct { state protoimpl.MessageState `protogen:"open.v1"` Status ImageServicePullLayerProgress_Status `protobuf:"varint,1,opt,name=status,proto3,enum=machine.ImageServicePullLayerProgress_Status" json:"status,omitempty"` Elapsed *durationpb.Duration `protobuf:"bytes,2,opt,name=elapsed,proto3" json:"elapsed,omitempty"` Offset int64 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` Total int64 `protobuf:"varint,4,opt,name=total,proto3" json:"total,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServicePullLayerProgress) Reset() { *x = ImageServicePullLayerProgress{} mi := &file_machine_image_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServicePullLayerProgress) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServicePullLayerProgress) ProtoMessage() {} func (x *ImageServicePullLayerProgress) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServicePullLayerProgress.ProtoReflect.Descriptor instead. func (*ImageServicePullLayerProgress) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{6} } func (x *ImageServicePullLayerProgress) GetStatus() ImageServicePullLayerProgress_Status { if x != nil { return x.Status } return ImageServicePullLayerProgress_DOWNLOADING } func (x *ImageServicePullLayerProgress) GetElapsed() *durationpb.Duration { if x != nil { return x.Elapsed } return nil } func (x *ImageServicePullLayerProgress) GetOffset() int64 { if x != nil { return x.Offset } return 0 } func (x *ImageServicePullLayerProgress) GetTotal() int64 { if x != nil { return x.Total } return 0 } type ImageServicePullProgress struct { state protoimpl.MessageState `protogen:"open.v1"` LayerId string `protobuf:"bytes,1,opt,name=layer_id,json=layerId,proto3" json:"layer_id,omitempty"` Progress *ImageServicePullLayerProgress `protobuf:"bytes,2,opt,name=progress,proto3" json:"progress,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServicePullProgress) Reset() { *x = ImageServicePullProgress{} mi := &file_machine_image_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServicePullProgress) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServicePullProgress) ProtoMessage() {} func (x *ImageServicePullProgress) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServicePullProgress.ProtoReflect.Descriptor instead. func (*ImageServicePullProgress) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{7} } func (x *ImageServicePullProgress) GetLayerId() string { if x != nil { return x.LayerId } return "" } func (x *ImageServicePullProgress) GetProgress() *ImageServicePullLayerProgress { if x != nil { return x.Progress } return nil } type ImageServiceRemoveRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` // Image reference to remove. ImageRef string `protobuf:"bytes,2,opt,name=image_ref,json=imageRef,proto3" json:"image_ref,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceRemoveRequest) Reset() { *x = ImageServiceRemoveRequest{} mi := &file_machine_image_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceRemoveRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceRemoveRequest) ProtoMessage() {} func (x *ImageServiceRemoveRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceRemoveRequest.ProtoReflect.Descriptor instead. func (*ImageServiceRemoveRequest) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{8} } func (x *ImageServiceRemoveRequest) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } func (x *ImageServiceRemoveRequest) GetImageRef() string { if x != nil { return x.ImageRef } return "" } type ImageServiceVerifyRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Image reference to verify. // // The image reference could be either in: // - the digest form (e.g. "docker.io/library/nginx@sha256:abc123...") to ensure that the exact image is verified. // - the tag form (e.g. "docker.io/library/nginx:latest") to verify the image currently pointed by the tag, and the resolved // digested will be returned in the response. // // Any other format will cause the error. ImageRef string `protobuf:"bytes,1,opt,name=image_ref,json=imageRef,proto3" json:"image_ref,omitempty"` // Authentication credentials for the registry (if needed). // // By default Talos will use configured auth, but additional // image pull secret can be submitted here. Credentials *ImageServiceCredentials `protobuf:"bytes,2,opt,name=credentials,proto3" json:"credentials,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceVerifyRequest) Reset() { *x = ImageServiceVerifyRequest{} mi := &file_machine_image_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceVerifyRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceVerifyRequest) ProtoMessage() {} func (x *ImageServiceVerifyRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceVerifyRequest.ProtoReflect.Descriptor instead. func (*ImageServiceVerifyRequest) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{9} } func (x *ImageServiceVerifyRequest) GetImageRef() string { if x != nil { return x.ImageRef } return "" } func (x *ImageServiceVerifyRequest) GetCredentials() *ImageServiceCredentials { if x != nil { return x.Credentials } return nil } type ImageServiceCredentials struct { state protoimpl.MessageState `protogen:"open.v1"` // Host of the registry (e.g. "docker.io"). Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` // Username for the registry. Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` // Password (token) for the registry. Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceCredentials) Reset() { *x = ImageServiceCredentials{} mi := &file_machine_image_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceCredentials) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceCredentials) ProtoMessage() {} func (x *ImageServiceCredentials) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceCredentials.ProtoReflect.Descriptor instead. func (*ImageServiceCredentials) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{10} } func (x *ImageServiceCredentials) GetHost() string { if x != nil { return x.Host } return "" } func (x *ImageServiceCredentials) GetUsername() string { if x != nil { return x.Username } return "" } func (x *ImageServiceCredentials) GetPassword() string { if x != nil { return x.Password } return "" } type ImageServiceVerifyResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Was the image verified: if it didn't match any verify rule, false will be returned. // If the image matched the rule, but the verification failed, an error will be returned. Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"` // Free-form verification result message, e.g. with details about the matched rule and how the image was verified. Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` // The pinned image reference with resolved digest that was verified (e.g. "docker.io/library/nginx@sha256:abc123..."). // // This is only set if verified=true. DigestedImageRef string `protobuf:"bytes,3,opt,name=digested_image_ref,json=digestedImageRef,proto3" json:"digested_image_ref,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageServiceVerifyResponse) Reset() { *x = ImageServiceVerifyResponse{} mi := &file_machine_image_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageServiceVerifyResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageServiceVerifyResponse) ProtoMessage() {} func (x *ImageServiceVerifyResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_image_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageServiceVerifyResponse.ProtoReflect.Descriptor instead. func (*ImageServiceVerifyResponse) Descriptor() ([]byte, []int) { return file_machine_image_proto_rawDescGZIP(), []int{11} } func (x *ImageServiceVerifyResponse) GetVerified() bool { if x != nil { return x.Verified } return false } func (x *ImageServiceVerifyResponse) GetMessage() string { if x != nil { return x.Message } return "" } func (x *ImageServiceVerifyResponse) GetDigestedImageRef() string { if x != nil { return x.DigestedImageRef } return "" } var File_machine_image_proto protoreflect.FileDescriptor const file_machine_image_proto_rawDesc = "" + "\n" + "\x13machine/image.proto\x12\amachine\x1a\x13common/common.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"U\n" + "\x17ImageServiceListRequest\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\"\x97\x02\n" + "\x18ImageServiceListResponse\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06digest\x18\x02 \x01(\tR\x06digest\x12\x12\n" + "\x04size\x18\x03 \x01(\x03R\x04size\x129\n" + "\n" + "created_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\x12E\n" + "\x06labels\x18\x05 \x03(\v2-.machine.ImageServiceListResponse.LabelsEntryR\x06labels\x1a9\n" + "\vLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"r\n" + "\x17ImageServicePullRequest\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\x12\x1b\n" + "\timage_ref\x18\x03 \x01(\tR\bimageRef\"\x86\x01\n" + "\x18ImageServicePullResponse\x12\x14\n" + "\x04name\x18\x01 \x01(\tH\x00R\x04name\x12H\n" + "\rpull_progress\x18\x02 \x01(\v2!.machine.ImageServicePullProgressH\x00R\fpullProgressB\n" + "\n" + "\bresponse\"\x95\x01\n" + "\x19ImageServiceImportRequest\x12<\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceH\x00R\n" + "containerd\x12/\n" + "\vimage_chunk\x18\x02 \x01(\v2\f.common.DataH\x00R\n" + "imageChunkB\t\n" + "\arequest\"0\n" + "\x1aImageServiceImportResponse\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\"\xb5\x02\n" + "\x1dImageServicePullLayerProgress\x12E\n" + "\x06status\x18\x01 \x01(\x0e2-.machine.ImageServicePullLayerProgress.StatusR\x06status\x123\n" + "\aelapsed\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\aelapsed\x12\x16\n" + "\x06offset\x18\x03 \x01(\x03R\x06offset\x12\x14\n" + "\x05total\x18\x04 \x01(\x03R\x05total\"j\n" + "\x06Status\x12\x0f\n" + "\vDOWNLOADING\x10\x00\x12\x15\n" + "\x11DOWNLOAD_COMPLETE\x10\x01\x12\x0e\n" + "\n" + "EXTRACTING\x10\x02\x12\x14\n" + "\x10EXTRACT_COMPLETE\x10\x03\x12\x12\n" + "\x0eALREADY_EXISTS\x10\x04\"y\n" + "\x18ImageServicePullProgress\x12\x19\n" + "\blayer_id\x18\x01 \x01(\tR\alayerId\x12B\n" + "\bprogress\x18\x02 \x01(\v2&.machine.ImageServicePullLayerProgressR\bprogress\"t\n" + "\x19ImageServiceRemoveRequest\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\x12\x1b\n" + "\timage_ref\x18\x02 \x01(\tR\bimageRef\"|\n" + "\x19ImageServiceVerifyRequest\x12\x1b\n" + "\timage_ref\x18\x01 \x01(\tR\bimageRef\x12B\n" + "\vcredentials\x18\x02 \x01(\v2 .machine.ImageServiceCredentialsR\vcredentials\"e\n" + "\x17ImageServiceCredentials\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x1a\n" + "\busername\x18\x02 \x01(\tR\busername\x12\x1a\n" + "\bpassword\x18\x03 \x01(\tR\bpassword\"\x80\x01\n" + "\x1aImageServiceVerifyResponse\x12\x1a\n" + "\bverified\x18\x01 \x01(\bR\bverified\x12\x18\n" + "\amessage\x18\x02 \x01(\tR\amessage\x12,\n" + "\x12digested_image_ref\x18\x03 \x01(\tR\x10digestedImageRef2\x9a\x03\n" + "\fImageService\x12M\n" + "\x04List\x12 .machine.ImageServiceListRequest\x1a!.machine.ImageServiceListResponse0\x01\x12M\n" + "\x04Pull\x12 .machine.ImageServicePullRequest\x1a!.machine.ImageServicePullResponse0\x01\x12S\n" + "\x06Import\x12\".machine.ImageServiceImportRequest\x1a#.machine.ImageServiceImportResponse(\x01\x12D\n" + "\x06Remove\x12\".machine.ImageServiceRemoveRequest\x1a\x16.google.protobuf.Empty\x12Q\n" + "\x06Verify\x12\".machine.ImageServiceVerifyRequest\x1a#.machine.ImageServiceVerifyResponseBN\n" + "\x15dev.talos.api.machineZ5github.com/siderolabs/talos/pkg/machinery/api/machineb\x06proto3" var ( file_machine_image_proto_rawDescOnce sync.Once file_machine_image_proto_rawDescData []byte ) func file_machine_image_proto_rawDescGZIP() []byte { file_machine_image_proto_rawDescOnce.Do(func() { file_machine_image_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_machine_image_proto_rawDesc), len(file_machine_image_proto_rawDesc))) }) return file_machine_image_proto_rawDescData } var file_machine_image_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_machine_image_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_machine_image_proto_goTypes = []any{ (ImageServicePullLayerProgress_Status)(0), // 0: machine.ImageServicePullLayerProgress.Status (*ImageServiceListRequest)(nil), // 1: machine.ImageServiceListRequest (*ImageServiceListResponse)(nil), // 2: machine.ImageServiceListResponse (*ImageServicePullRequest)(nil), // 3: machine.ImageServicePullRequest (*ImageServicePullResponse)(nil), // 4: machine.ImageServicePullResponse (*ImageServiceImportRequest)(nil), // 5: machine.ImageServiceImportRequest (*ImageServiceImportResponse)(nil), // 6: machine.ImageServiceImportResponse (*ImageServicePullLayerProgress)(nil), // 7: machine.ImageServicePullLayerProgress (*ImageServicePullProgress)(nil), // 8: machine.ImageServicePullProgress (*ImageServiceRemoveRequest)(nil), // 9: machine.ImageServiceRemoveRequest (*ImageServiceVerifyRequest)(nil), // 10: machine.ImageServiceVerifyRequest (*ImageServiceCredentials)(nil), // 11: machine.ImageServiceCredentials (*ImageServiceVerifyResponse)(nil), // 12: machine.ImageServiceVerifyResponse nil, // 13: machine.ImageServiceListResponse.LabelsEntry (*common.ContainerdInstance)(nil), // 14: common.ContainerdInstance (*timestamppb.Timestamp)(nil), // 15: google.protobuf.Timestamp (*common.Data)(nil), // 16: common.Data (*durationpb.Duration)(nil), // 17: google.protobuf.Duration (*emptypb.Empty)(nil), // 18: google.protobuf.Empty } var file_machine_image_proto_depIdxs = []int32{ 14, // 0: machine.ImageServiceListRequest.containerd:type_name -> common.ContainerdInstance 15, // 1: machine.ImageServiceListResponse.created_at:type_name -> google.protobuf.Timestamp 13, // 2: machine.ImageServiceListResponse.labels:type_name -> machine.ImageServiceListResponse.LabelsEntry 14, // 3: machine.ImageServicePullRequest.containerd:type_name -> common.ContainerdInstance 8, // 4: machine.ImageServicePullResponse.pull_progress:type_name -> machine.ImageServicePullProgress 14, // 5: machine.ImageServiceImportRequest.containerd:type_name -> common.ContainerdInstance 16, // 6: machine.ImageServiceImportRequest.image_chunk:type_name -> common.Data 0, // 7: machine.ImageServicePullLayerProgress.status:type_name -> machine.ImageServicePullLayerProgress.Status 17, // 8: machine.ImageServicePullLayerProgress.elapsed:type_name -> google.protobuf.Duration 7, // 9: machine.ImageServicePullProgress.progress:type_name -> machine.ImageServicePullLayerProgress 14, // 10: machine.ImageServiceRemoveRequest.containerd:type_name -> common.ContainerdInstance 11, // 11: machine.ImageServiceVerifyRequest.credentials:type_name -> machine.ImageServiceCredentials 1, // 12: machine.ImageService.List:input_type -> machine.ImageServiceListRequest 3, // 13: machine.ImageService.Pull:input_type -> machine.ImageServicePullRequest 5, // 14: machine.ImageService.Import:input_type -> machine.ImageServiceImportRequest 9, // 15: machine.ImageService.Remove:input_type -> machine.ImageServiceRemoveRequest 10, // 16: machine.ImageService.Verify:input_type -> machine.ImageServiceVerifyRequest 2, // 17: machine.ImageService.List:output_type -> machine.ImageServiceListResponse 4, // 18: machine.ImageService.Pull:output_type -> machine.ImageServicePullResponse 6, // 19: machine.ImageService.Import:output_type -> machine.ImageServiceImportResponse 18, // 20: machine.ImageService.Remove:output_type -> google.protobuf.Empty 12, // 21: machine.ImageService.Verify:output_type -> machine.ImageServiceVerifyResponse 17, // [17:22] is the sub-list for method output_type 12, // [12:17] is the sub-list for method input_type 12, // [12:12] is the sub-list for extension type_name 12, // [12:12] is the sub-list for extension extendee 0, // [0:12] is the sub-list for field type_name } func init() { file_machine_image_proto_init() } func file_machine_image_proto_init() { if File_machine_image_proto != nil { return } file_machine_image_proto_msgTypes[3].OneofWrappers = []any{ (*ImageServicePullResponse_Name)(nil), (*ImageServicePullResponse_PullProgress)(nil), } file_machine_image_proto_msgTypes[4].OneofWrappers = []any{ (*ImageServiceImportRequest_Containerd)(nil), (*ImageServiceImportRequest_ImageChunk)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_machine_image_proto_rawDesc), len(file_machine_image_proto_rawDesc)), NumEnums: 1, NumMessages: 13, NumExtensions: 0, NumServices: 1, }, GoTypes: file_machine_image_proto_goTypes, DependencyIndexes: file_machine_image_proto_depIdxs, EnumInfos: file_machine_image_proto_enumTypes, MessageInfos: file_machine_image_proto_msgTypes, }.Build() File_machine_image_proto = out.File file_machine_image_proto_goTypes = nil file_machine_image_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/machine/image_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: machine/image.proto package machine import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( ImageService_List_FullMethodName = "/machine.ImageService/List" ImageService_Pull_FullMethodName = "/machine.ImageService/Pull" ImageService_Import_FullMethodName = "/machine.ImageService/Import" ImageService_Remove_FullMethodName = "/machine.ImageService/Remove" ImageService_Verify_FullMethodName = "/machine.ImageService/Verify" ) // ImageServiceClient is the client API for ImageService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The machine service definition. type ImageServiceClient interface { // List images in the containerd. List(ctx context.Context, in *ImageServiceListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageServiceListResponse], error) // Pull an image into the containerd. Pull(ctx context.Context, in *ImageServicePullRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageServicePullResponse], error) // Import an image from a stream (tarball). Import(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[ImageServiceImportRequest, ImageServiceImportResponse], error) // Remove an image from the containerd. Remove(ctx context.Context, in *ImageServiceRemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // Verify an image signature. Verify(ctx context.Context, in *ImageServiceVerifyRequest, opts ...grpc.CallOption) (*ImageServiceVerifyResponse, error) } type imageServiceClient struct { cc grpc.ClientConnInterface } func NewImageServiceClient(cc grpc.ClientConnInterface) ImageServiceClient { return &imageServiceClient{cc} } func (c *imageServiceClient) List(ctx context.Context, in *ImageServiceListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageServiceListResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ImageService_ServiceDesc.Streams[0], ImageService_List_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ImageServiceListRequest, ImageServiceListResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_ListClient = grpc.ServerStreamingClient[ImageServiceListResponse] func (c *imageServiceClient) Pull(ctx context.Context, in *ImageServicePullRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageServicePullResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ImageService_ServiceDesc.Streams[1], ImageService_Pull_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ImageServicePullRequest, ImageServicePullResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_PullClient = grpc.ServerStreamingClient[ImageServicePullResponse] func (c *imageServiceClient) Import(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[ImageServiceImportRequest, ImageServiceImportResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &ImageService_ServiceDesc.Streams[2], ImageService_Import_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ImageServiceImportRequest, ImageServiceImportResponse]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_ImportClient = grpc.ClientStreamingClient[ImageServiceImportRequest, ImageServiceImportResponse] func (c *imageServiceClient) Remove(ctx context.Context, in *ImageServiceRemoveRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) err := c.cc.Invoke(ctx, ImageService_Remove_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *imageServiceClient) Verify(ctx context.Context, in *ImageServiceVerifyRequest, opts ...grpc.CallOption) (*ImageServiceVerifyResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ImageServiceVerifyResponse) err := c.cc.Invoke(ctx, ImageService_Verify_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // ImageServiceServer is the server API for ImageService service. // All implementations must embed UnimplementedImageServiceServer // for forward compatibility. // // The machine service definition. type ImageServiceServer interface { // List images in the containerd. List(*ImageServiceListRequest, grpc.ServerStreamingServer[ImageServiceListResponse]) error // Pull an image into the containerd. Pull(*ImageServicePullRequest, grpc.ServerStreamingServer[ImageServicePullResponse]) error // Import an image from a stream (tarball). Import(grpc.ClientStreamingServer[ImageServiceImportRequest, ImageServiceImportResponse]) error // Remove an image from the containerd. Remove(context.Context, *ImageServiceRemoveRequest) (*emptypb.Empty, error) // Verify an image signature. Verify(context.Context, *ImageServiceVerifyRequest) (*ImageServiceVerifyResponse, error) mustEmbedUnimplementedImageServiceServer() } // UnimplementedImageServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedImageServiceServer struct{} func (UnimplementedImageServiceServer) List(*ImageServiceListRequest, grpc.ServerStreamingServer[ImageServiceListResponse]) error { return status.Error(codes.Unimplemented, "method List not implemented") } func (UnimplementedImageServiceServer) Pull(*ImageServicePullRequest, grpc.ServerStreamingServer[ImageServicePullResponse]) error { return status.Error(codes.Unimplemented, "method Pull not implemented") } func (UnimplementedImageServiceServer) Import(grpc.ClientStreamingServer[ImageServiceImportRequest, ImageServiceImportResponse]) error { return status.Error(codes.Unimplemented, "method Import not implemented") } func (UnimplementedImageServiceServer) Remove(context.Context, *ImageServiceRemoveRequest) (*emptypb.Empty, error) { return nil, status.Error(codes.Unimplemented, "method Remove not implemented") } func (UnimplementedImageServiceServer) Verify(context.Context, *ImageServiceVerifyRequest) (*ImageServiceVerifyResponse, error) { return nil, status.Error(codes.Unimplemented, "method Verify not implemented") } func (UnimplementedImageServiceServer) mustEmbedUnimplementedImageServiceServer() {} func (UnimplementedImageServiceServer) testEmbeddedByValue() {} // UnsafeImageServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to ImageServiceServer will // result in compilation errors. type UnsafeImageServiceServer interface { mustEmbedUnimplementedImageServiceServer() } func RegisterImageServiceServer(s grpc.ServiceRegistrar, srv ImageServiceServer) { // If the following call panics, it indicates UnimplementedImageServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&ImageService_ServiceDesc, srv) } func _ImageService_List_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ImageServiceListRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ImageServiceServer).List(m, &grpc.GenericServerStream[ImageServiceListRequest, ImageServiceListResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_ListServer = grpc.ServerStreamingServer[ImageServiceListResponse] func _ImageService_Pull_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ImageServicePullRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(ImageServiceServer).Pull(m, &grpc.GenericServerStream[ImageServicePullRequest, ImageServicePullResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_PullServer = grpc.ServerStreamingServer[ImageServicePullResponse] func _ImageService_Import_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(ImageServiceServer).Import(&grpc.GenericServerStream[ImageServiceImportRequest, ImageServiceImportResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type ImageService_ImportServer = grpc.ClientStreamingServer[ImageServiceImportRequest, ImageServiceImportResponse] func _ImageService_Remove_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ImageServiceRemoveRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ImageServiceServer).Remove(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ImageService_Remove_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ImageServiceServer).Remove(ctx, req.(*ImageServiceRemoveRequest)) } return interceptor(ctx, in, info, handler) } func _ImageService_Verify_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ImageServiceVerifyRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(ImageServiceServer).Verify(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: ImageService_Verify_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(ImageServiceServer).Verify(ctx, req.(*ImageServiceVerifyRequest)) } return interceptor(ctx, in, info, handler) } // ImageService_ServiceDesc is the grpc.ServiceDesc for ImageService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var ImageService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "machine.ImageService", HandlerType: (*ImageServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Remove", Handler: _ImageService_Remove_Handler, }, { MethodName: "Verify", Handler: _ImageService_Verify_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "List", Handler: _ImageService_List_Handler, ServerStreams: true, }, { StreamName: "Pull", Handler: _ImageService_Pull_Handler, ServerStreams: true, }, { StreamName: "Import", Handler: _ImageService_Import_Handler, ClientStreams: true, }, }, Metadata: "machine/image.proto", } ================================================ FILE: pkg/machinery/api/machine/image_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: machine/image.proto package machine import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" timestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb1 "google.golang.org/protobuf/types/known/durationpb" timestamppb1 "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ImageServiceListRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceListRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceListRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceListResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceListResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Labels) > 0 { for k := range m.Labels { v := m.Labels[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if m.CreatedAt != nil { size, err := (*timestamppb.Timestamp)(m.CreatedAt).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x18 } if len(m.Digest) > 0 { i -= len(m.Digest) copy(dAtA[i:], m.Digest) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Digest))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServicePullRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServicePullRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ImageRef) > 0 { i -= len(m.ImageRef) copy(dAtA[i:], m.ImageRef) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImageRef))) i-- dAtA[i] = 0x1a } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServicePullResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServicePullResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if vtmsg, ok := m.Response.(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size } return len(dAtA) - i, nil } func (m *ImageServicePullResponse_Name) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullResponse_Name) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa return len(dAtA) - i, nil } func (m *ImageServicePullResponse_PullProgress) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullResponse_PullProgress) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) if m.PullProgress != nil { size, err := m.PullProgress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } else { i = protohelpers.EncodeVarint(dAtA, i, 0) i-- dAtA[i] = 0x12 } return len(dAtA) - i, nil } func (m *ImageServiceImportRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceImportRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceImportRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if vtmsg, ok := m.Request.(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size } return len(dAtA) - i, nil } func (m *ImageServiceImportRequest_Containerd) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceImportRequest_Containerd) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } else { i = protohelpers.EncodeVarint(dAtA, i, 0) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceImportRequest_ImageChunk) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceImportRequest_ImageChunk) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) if m.ImageChunk != nil { if vtmsg, ok := interface{}(m.ImageChunk).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ImageChunk) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } else { i = protohelpers.EncodeVarint(dAtA, i, 0) i-- dAtA[i] = 0x12 } return len(dAtA) - i, nil } func (m *ImageServiceImportResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceImportResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceImportResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServicePullLayerProgress) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServicePullLayerProgress) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullLayerProgress) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Total != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Total)) i-- dAtA[i] = 0x20 } if m.Offset != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) i-- dAtA[i] = 0x18 } if m.Elapsed != nil { size, err := (*durationpb.Duration)(m.Elapsed).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Status != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ImageServicePullProgress) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServicePullProgress) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServicePullProgress) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Progress != nil { size, err := m.Progress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.LayerId) > 0 { i -= len(m.LayerId) copy(dAtA[i:], m.LayerId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LayerId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceRemoveRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceRemoveRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceRemoveRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ImageRef) > 0 { i -= len(m.ImageRef) copy(dAtA[i:], m.ImageRef) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImageRef))) i-- dAtA[i] = 0x12 } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceVerifyRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceVerifyRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceVerifyRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Credentials != nil { size, err := m.Credentials.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.ImageRef) > 0 { i -= len(m.ImageRef) copy(dAtA[i:], m.ImageRef) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImageRef))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceCredentials) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceCredentials) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceCredentials) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Password) > 0 { i -= len(m.Password) copy(dAtA[i:], m.Password) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Password))) i-- dAtA[i] = 0x1a } if len(m.Username) > 0 { i -= len(m.Username) copy(dAtA[i:], m.Username) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Username))) i-- dAtA[i] = 0x12 } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageServiceVerifyResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageServiceVerifyResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageServiceVerifyResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.DigestedImageRef) > 0 { i -= len(m.DigestedImageRef) copy(dAtA[i:], m.DigestedImageRef) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DigestedImageRef))) i-- dAtA[i] = 0x1a } if len(m.Message) > 0 { i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0x12 } if m.Verified { i-- if m.Verified { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ImageServiceListRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceListResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Digest) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.CreatedAt != nil { l = (*timestamppb.Timestamp)(m.CreatedAt).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Labels) > 0 { for k, v := range m.Labels { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } n += len(m.unknownFields) return n } func (m *ImageServicePullRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ImageRef) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServicePullResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if vtmsg, ok := m.Response.(interface{ SizeVT() int }); ok { n += vtmsg.SizeVT() } n += len(m.unknownFields) return n } func (m *ImageServicePullResponse_Name) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) return n } func (m *ImageServicePullResponse_PullProgress) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.PullProgress != nil { l = m.PullProgress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } else { n += 2 } return n } func (m *ImageServiceImportRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if vtmsg, ok := m.Request.(interface{ SizeVT() int }); ok { n += vtmsg.SizeVT() } n += len(m.unknownFields) return n } func (m *ImageServiceImportRequest_Containerd) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } else { n += 2 } return n } func (m *ImageServiceImportRequest_ImageChunk) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.ImageChunk != nil { if size, ok := interface{}(m.ImageChunk).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.ImageChunk) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } else { n += 2 } return n } func (m *ImageServiceImportResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServicePullLayerProgress) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Status != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Status)) } if m.Elapsed != nil { l = (*durationpb.Duration)(m.Elapsed).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Offset != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Offset)) } if m.Total != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Total)) } n += len(m.unknownFields) return n } func (m *ImageServicePullProgress) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.LayerId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Progress != nil { l = m.Progress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceRemoveRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ImageRef) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceVerifyRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ImageRef) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Credentials != nil { l = m.Credentials.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceCredentials) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Username) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Password) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceVerifyResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Verified { n += 2 } l = len(m.Message) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DigestedImageRef) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageServiceListRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceListResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Digest = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CreatedAt == nil { m.CreatedAt = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.CreatedAt).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Labels == nil { m.Labels = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Labels[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServicePullRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServicePullRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServicePullRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageRef", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImageRef = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServicePullResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServicePullResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServicePullResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Response = &ImageServicePullResponse_Name{Name: string(dAtA[iNdEx:postIndex])} iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PullProgress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if oneof, ok := m.Response.(*ImageServicePullResponse_PullProgress); ok { if err := oneof.PullProgress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { v := &ImageServicePullProgress{} if err := v.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } m.Response = &ImageServicePullResponse_PullProgress{PullProgress: v} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceImportRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceImportRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceImportRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if oneof, ok := m.Request.(*ImageServiceImportRequest_Containerd); ok { if unmarshal, ok := interface{}(oneof.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], oneof.Containerd); err != nil { return err } } } else { v := &common.ContainerdInstance{} if unmarshal, ok := interface{}(v).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], v); err != nil { return err } } m.Request = &ImageServiceImportRequest_Containerd{Containerd: v} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageChunk", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if oneof, ok := m.Request.(*ImageServiceImportRequest_ImageChunk); ok { if unmarshal, ok := interface{}(oneof.ImageChunk).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], oneof.ImageChunk); err != nil { return err } } } else { v := &common.Data{} if unmarshal, ok := interface{}(v).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], v); err != nil { return err } } m.Request = &ImageServiceImportRequest_ImageChunk{ImageChunk: v} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceImportResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceImportResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceImportResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServicePullLayerProgress) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServicePullLayerProgress: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServicePullLayerProgress: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= ImageServicePullLayerProgress_Status(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Elapsed", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Elapsed == nil { m.Elapsed = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Elapsed).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) } m.Offset = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Offset |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) } m.Total = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Total |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServicePullProgress) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServicePullProgress: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServicePullProgress: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LayerId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LayerId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Progress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Progress == nil { m.Progress = &ImageServicePullLayerProgress{} } if err := m.Progress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceRemoveRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceRemoveRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceRemoveRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageRef", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImageRef = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceVerifyRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceVerifyRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceVerifyRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageRef", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImageRef = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Credentials", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Credentials == nil { m.Credentials = &ImageServiceCredentials{} } if err := m.Credentials.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceCredentials) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceCredentials: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceCredentials: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Username = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Password = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageServiceVerifyResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageServiceVerifyResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageServiceVerifyResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Verified", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Verified = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DigestedImageRef", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DigestedImageRef = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/machine/lifecycle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package machine contains the machine service API definitions. package machine import "fmt" // Fmt formats the pull progress status into a human-readable string. func (s *LifecycleServiceInstallProgress) Fmt() string { switch msg := s.GetResponse().(type) { case *LifecycleServiceInstallProgress_Message: return msg.Message case *LifecycleServiceInstallProgress_ExitCode: return fmt.Sprintf("Exit code: %d", msg.ExitCode) } return "" } ================================================ FILE: pkg/machinery/api/machine/lifecycle.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: machine/lifecycle.proto package machine import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // InstallArtifactsSource specifies the source of the installation artifacts. type InstallArtifactsSource struct { state protoimpl.MessageState `protogen:"open.v1"` // The reference name of the image, as returned by `talosctl image pull`. ImageName string `protobuf:"bytes,1,opt,name=image_name,json=imageName,proto3" json:"image_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InstallArtifactsSource) Reset() { *x = InstallArtifactsSource{} mi := &file_machine_lifecycle_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InstallArtifactsSource) String() string { return protoimpl.X.MessageStringOf(x) } func (*InstallArtifactsSource) ProtoMessage() {} func (x *InstallArtifactsSource) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InstallArtifactsSource.ProtoReflect.Descriptor instead. func (*InstallArtifactsSource) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{0} } func (x *InstallArtifactsSource) GetImageName() string { if x != nil { return x.ImageName } return "" } // InstallDestination specifies the target for installation. type InstallDestination struct { state protoimpl.MessageState `protogen:"open.v1"` // The disk to which Talos should be installed, e.g. "/dev/sda". Disk string `protobuf:"bytes,1,opt,name=disk,proto3" json:"disk,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InstallDestination) Reset() { *x = InstallDestination{} mi := &file_machine_lifecycle_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InstallDestination) String() string { return protoimpl.X.MessageStringOf(x) } func (*InstallDestination) ProtoMessage() {} func (x *InstallDestination) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InstallDestination.ProtoReflect.Descriptor instead. func (*InstallDestination) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{1} } func (x *InstallDestination) GetDisk() string { if x != nil { return x.Disk } return "" } // LifecycleServiceInstallRequest contains the necessary information to perform an installation. type LifecycleServiceInstallRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The containerd instance to use for pulling the installation artifacts. Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` // The source of the installation artifacts. Source *InstallArtifactsSource `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` // The destination for the installation. Destination *InstallDestination `protobuf:"bytes,3,opt,name=destination,proto3" json:"destination,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LifecycleServiceInstallRequest) Reset() { *x = LifecycleServiceInstallRequest{} mi := &file_machine_lifecycle_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LifecycleServiceInstallRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*LifecycleServiceInstallRequest) ProtoMessage() {} func (x *LifecycleServiceInstallRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LifecycleServiceInstallRequest.ProtoReflect.Descriptor instead. func (*LifecycleServiceInstallRequest) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{2} } func (x *LifecycleServiceInstallRequest) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } func (x *LifecycleServiceInstallRequest) GetSource() *InstallArtifactsSource { if x != nil { return x.Source } return nil } func (x *LifecycleServiceInstallRequest) GetDestination() *InstallDestination { if x != nil { return x.Destination } return nil } // LifecycleServiceInstallProgress represents the progress of the installation or upgrade process. type LifecycleServiceInstallProgress struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Response: // // *LifecycleServiceInstallProgress_Message // *LifecycleServiceInstallProgress_ExitCode Response isLifecycleServiceInstallProgress_Response `protobuf_oneof:"response"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LifecycleServiceInstallProgress) Reset() { *x = LifecycleServiceInstallProgress{} mi := &file_machine_lifecycle_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LifecycleServiceInstallProgress) String() string { return protoimpl.X.MessageStringOf(x) } func (*LifecycleServiceInstallProgress) ProtoMessage() {} func (x *LifecycleServiceInstallProgress) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LifecycleServiceInstallProgress.ProtoReflect.Descriptor instead. func (*LifecycleServiceInstallProgress) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{3} } func (x *LifecycleServiceInstallProgress) GetResponse() isLifecycleServiceInstallProgress_Response { if x != nil { return x.Response } return nil } func (x *LifecycleServiceInstallProgress) GetMessage() string { if x != nil { if x, ok := x.Response.(*LifecycleServiceInstallProgress_Message); ok { return x.Message } } return "" } func (x *LifecycleServiceInstallProgress) GetExitCode() int32 { if x != nil { if x, ok := x.Response.(*LifecycleServiceInstallProgress_ExitCode); ok { return x.ExitCode } } return 0 } type isLifecycleServiceInstallProgress_Response interface { isLifecycleServiceInstallProgress_Response() } type LifecycleServiceInstallProgress_Message struct { // A message indicating the current progress of the installation or upgrade. Message string `protobuf:"bytes,1,opt,name=message,proto3,oneof"` } type LifecycleServiceInstallProgress_ExitCode struct { // An exit code indicating the result of the installation or upgrade process. // A non-zero value indicates an error. // Server SHOULD NOT respond with error, even if the value is non-zero. // It's responsibility of the client to handle the exit code appropriately. ExitCode int32 `protobuf:"varint,2,opt,name=exit_code,json=exitCode,proto3,oneof"` } func (*LifecycleServiceInstallProgress_Message) isLifecycleServiceInstallProgress_Response() {} func (*LifecycleServiceInstallProgress_ExitCode) isLifecycleServiceInstallProgress_Response() {} // LifecycleServiceInstallResponse is the response message for the Install RPC, containing progress updates. type LifecycleServiceInstallResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The progress of the installation process. Progress *LifecycleServiceInstallProgress `protobuf:"bytes,1,opt,name=progress,proto3" json:"progress,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LifecycleServiceInstallResponse) Reset() { *x = LifecycleServiceInstallResponse{} mi := &file_machine_lifecycle_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LifecycleServiceInstallResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*LifecycleServiceInstallResponse) ProtoMessage() {} func (x *LifecycleServiceInstallResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LifecycleServiceInstallResponse.ProtoReflect.Descriptor instead. func (*LifecycleServiceInstallResponse) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{4} } func (x *LifecycleServiceInstallResponse) GetProgress() *LifecycleServiceInstallProgress { if x != nil { return x.Progress } return nil } // LifecycleServiceUpgradeRequest contains the necessary information to perform an upgrade. type LifecycleServiceUpgradeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The containerd instance to use for pulling the installation artifacts. Containerd *common.ContainerdInstance `protobuf:"bytes,1,opt,name=containerd,proto3" json:"containerd,omitempty"` // The source of the installation artifacts for the upgrade. Source *InstallArtifactsSource `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LifecycleServiceUpgradeRequest) Reset() { *x = LifecycleServiceUpgradeRequest{} mi := &file_machine_lifecycle_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LifecycleServiceUpgradeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*LifecycleServiceUpgradeRequest) ProtoMessage() {} func (x *LifecycleServiceUpgradeRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LifecycleServiceUpgradeRequest.ProtoReflect.Descriptor instead. func (*LifecycleServiceUpgradeRequest) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{5} } func (x *LifecycleServiceUpgradeRequest) GetContainerd() *common.ContainerdInstance { if x != nil { return x.Containerd } return nil } func (x *LifecycleServiceUpgradeRequest) GetSource() *InstallArtifactsSource { if x != nil { return x.Source } return nil } // LifecycleServiceUpgradeResponse is the response message for the Upgrade RPC, containing progress updates. type LifecycleServiceUpgradeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // The progress of the upgrade process. Progress *LifecycleServiceInstallProgress `protobuf:"bytes,1,opt,name=progress,proto3" json:"progress,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LifecycleServiceUpgradeResponse) Reset() { *x = LifecycleServiceUpgradeResponse{} mi := &file_machine_lifecycle_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LifecycleServiceUpgradeResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*LifecycleServiceUpgradeResponse) ProtoMessage() {} func (x *LifecycleServiceUpgradeResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_lifecycle_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LifecycleServiceUpgradeResponse.ProtoReflect.Descriptor instead. func (*LifecycleServiceUpgradeResponse) Descriptor() ([]byte, []int) { return file_machine_lifecycle_proto_rawDescGZIP(), []int{6} } func (x *LifecycleServiceUpgradeResponse) GetProgress() *LifecycleServiceInstallProgress { if x != nil { return x.Progress } return nil } var File_machine_lifecycle_proto protoreflect.FileDescriptor const file_machine_lifecycle_proto_rawDesc = "" + "\n" + "\x17machine/lifecycle.proto\x12\amachine\x1a\x13common/common.proto\"7\n" + "\x16InstallArtifactsSource\x12\x1d\n" + "\n" + "image_name\x18\x01 \x01(\tR\timageName\"(\n" + "\x12InstallDestination\x12\x12\n" + "\x04disk\x18\x01 \x01(\tR\x04disk\"\xd4\x01\n" + "\x1eLifecycleServiceInstallRequest\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\x127\n" + "\x06source\x18\x02 \x01(\v2\x1f.machine.InstallArtifactsSourceR\x06source\x12=\n" + "\vdestination\x18\x03 \x01(\v2\x1b.machine.InstallDestinationR\vdestination\"h\n" + "\x1fLifecycleServiceInstallProgress\x12\x1a\n" + "\amessage\x18\x01 \x01(\tH\x00R\amessage\x12\x1d\n" + "\texit_code\x18\x02 \x01(\x05H\x00R\bexitCodeB\n" + "\n" + "\bresponse\"g\n" + "\x1fLifecycleServiceInstallResponse\x12D\n" + "\bprogress\x18\x01 \x01(\v2(.machine.LifecycleServiceInstallProgressR\bprogress\"\x95\x01\n" + "\x1eLifecycleServiceUpgradeRequest\x12:\n" + "\n" + "containerd\x18\x01 \x01(\v2\x1a.common.ContainerdInstanceR\n" + "containerd\x127\n" + "\x06source\x18\x02 \x01(\v2\x1f.machine.InstallArtifactsSourceR\x06source\"g\n" + "\x1fLifecycleServiceUpgradeResponse\x12D\n" + "\bprogress\x18\x01 \x01(\v2(.machine.LifecycleServiceInstallProgressR\bprogress2\xd2\x01\n" + "\x10LifecycleService\x12^\n" + "\aInstall\x12'.machine.LifecycleServiceInstallRequest\x1a(.machine.LifecycleServiceInstallResponse0\x01\x12^\n" + "\aUpgrade\x12'.machine.LifecycleServiceUpgradeRequest\x1a(.machine.LifecycleServiceUpgradeResponse0\x01BN\n" + "\x15dev.talos.api.machineZ5github.com/siderolabs/talos/pkg/machinery/api/machineb\x06proto3" var ( file_machine_lifecycle_proto_rawDescOnce sync.Once file_machine_lifecycle_proto_rawDescData []byte ) func file_machine_lifecycle_proto_rawDescGZIP() []byte { file_machine_lifecycle_proto_rawDescOnce.Do(func() { file_machine_lifecycle_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_machine_lifecycle_proto_rawDesc), len(file_machine_lifecycle_proto_rawDesc))) }) return file_machine_lifecycle_proto_rawDescData } var file_machine_lifecycle_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_machine_lifecycle_proto_goTypes = []any{ (*InstallArtifactsSource)(nil), // 0: machine.InstallArtifactsSource (*InstallDestination)(nil), // 1: machine.InstallDestination (*LifecycleServiceInstallRequest)(nil), // 2: machine.LifecycleServiceInstallRequest (*LifecycleServiceInstallProgress)(nil), // 3: machine.LifecycleServiceInstallProgress (*LifecycleServiceInstallResponse)(nil), // 4: machine.LifecycleServiceInstallResponse (*LifecycleServiceUpgradeRequest)(nil), // 5: machine.LifecycleServiceUpgradeRequest (*LifecycleServiceUpgradeResponse)(nil), // 6: machine.LifecycleServiceUpgradeResponse (*common.ContainerdInstance)(nil), // 7: common.ContainerdInstance } var file_machine_lifecycle_proto_depIdxs = []int32{ 7, // 0: machine.LifecycleServiceInstallRequest.containerd:type_name -> common.ContainerdInstance 0, // 1: machine.LifecycleServiceInstallRequest.source:type_name -> machine.InstallArtifactsSource 1, // 2: machine.LifecycleServiceInstallRequest.destination:type_name -> machine.InstallDestination 3, // 3: machine.LifecycleServiceInstallResponse.progress:type_name -> machine.LifecycleServiceInstallProgress 7, // 4: machine.LifecycleServiceUpgradeRequest.containerd:type_name -> common.ContainerdInstance 0, // 5: machine.LifecycleServiceUpgradeRequest.source:type_name -> machine.InstallArtifactsSource 3, // 6: machine.LifecycleServiceUpgradeResponse.progress:type_name -> machine.LifecycleServiceInstallProgress 2, // 7: machine.LifecycleService.Install:input_type -> machine.LifecycleServiceInstallRequest 5, // 8: machine.LifecycleService.Upgrade:input_type -> machine.LifecycleServiceUpgradeRequest 4, // 9: machine.LifecycleService.Install:output_type -> machine.LifecycleServiceInstallResponse 6, // 10: machine.LifecycleService.Upgrade:output_type -> machine.LifecycleServiceUpgradeResponse 9, // [9:11] is the sub-list for method output_type 7, // [7:9] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name } func init() { file_machine_lifecycle_proto_init() } func file_machine_lifecycle_proto_init() { if File_machine_lifecycle_proto != nil { return } file_machine_lifecycle_proto_msgTypes[3].OneofWrappers = []any{ (*LifecycleServiceInstallProgress_Message)(nil), (*LifecycleServiceInstallProgress_ExitCode)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_machine_lifecycle_proto_rawDesc), len(file_machine_lifecycle_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 1, }, GoTypes: file_machine_lifecycle_proto_goTypes, DependencyIndexes: file_machine_lifecycle_proto_depIdxs, MessageInfos: file_machine_lifecycle_proto_msgTypes, }.Build() File_machine_lifecycle_proto = out.File file_machine_lifecycle_proto_goTypes = nil file_machine_lifecycle_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/machine/lifecycle_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: machine/lifecycle.proto package machine import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( LifecycleService_Install_FullMethodName = "/machine.LifecycleService/Install" LifecycleService_Upgrade_FullMethodName = "/machine.LifecycleService/Upgrade" ) // LifecycleServiceClient is the client API for LifecycleService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The LifecycleService handles installation and upgrade operations. type LifecycleServiceClient interface { // Install Talos to disk. // The RPC should fail if the Talos is already installed on the target disk. Install(ctx context.Context, in *LifecycleServiceInstallRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LifecycleServiceInstallResponse], error) // Upgrade Talos to a new version. // The RPC should fail if Talos is not already installed on the target disk. Upgrade(ctx context.Context, in *LifecycleServiceUpgradeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LifecycleServiceUpgradeResponse], error) } type lifecycleServiceClient struct { cc grpc.ClientConnInterface } func NewLifecycleServiceClient(cc grpc.ClientConnInterface) LifecycleServiceClient { return &lifecycleServiceClient{cc} } func (c *lifecycleServiceClient) Install(ctx context.Context, in *LifecycleServiceInstallRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LifecycleServiceInstallResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &LifecycleService_ServiceDesc.Streams[0], LifecycleService_Install_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[LifecycleServiceInstallRequest, LifecycleServiceInstallResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LifecycleService_InstallClient = grpc.ServerStreamingClient[LifecycleServiceInstallResponse] func (c *lifecycleServiceClient) Upgrade(ctx context.Context, in *LifecycleServiceUpgradeRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[LifecycleServiceUpgradeResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &LifecycleService_ServiceDesc.Streams[1], LifecycleService_Upgrade_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[LifecycleServiceUpgradeRequest, LifecycleServiceUpgradeResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LifecycleService_UpgradeClient = grpc.ServerStreamingClient[LifecycleServiceUpgradeResponse] // LifecycleServiceServer is the server API for LifecycleService service. // All implementations must embed UnimplementedLifecycleServiceServer // for forward compatibility. // // The LifecycleService handles installation and upgrade operations. type LifecycleServiceServer interface { // Install Talos to disk. // The RPC should fail if the Talos is already installed on the target disk. Install(*LifecycleServiceInstallRequest, grpc.ServerStreamingServer[LifecycleServiceInstallResponse]) error // Upgrade Talos to a new version. // The RPC should fail if Talos is not already installed on the target disk. Upgrade(*LifecycleServiceUpgradeRequest, grpc.ServerStreamingServer[LifecycleServiceUpgradeResponse]) error mustEmbedUnimplementedLifecycleServiceServer() } // UnimplementedLifecycleServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedLifecycleServiceServer struct{} func (UnimplementedLifecycleServiceServer) Install(*LifecycleServiceInstallRequest, grpc.ServerStreamingServer[LifecycleServiceInstallResponse]) error { return status.Error(codes.Unimplemented, "method Install not implemented") } func (UnimplementedLifecycleServiceServer) Upgrade(*LifecycleServiceUpgradeRequest, grpc.ServerStreamingServer[LifecycleServiceUpgradeResponse]) error { return status.Error(codes.Unimplemented, "method Upgrade not implemented") } func (UnimplementedLifecycleServiceServer) mustEmbedUnimplementedLifecycleServiceServer() {} func (UnimplementedLifecycleServiceServer) testEmbeddedByValue() {} // UnsafeLifecycleServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to LifecycleServiceServer will // result in compilation errors. type UnsafeLifecycleServiceServer interface { mustEmbedUnimplementedLifecycleServiceServer() } func RegisterLifecycleServiceServer(s grpc.ServiceRegistrar, srv LifecycleServiceServer) { // If the following call panics, it indicates UnimplementedLifecycleServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&LifecycleService_ServiceDesc, srv) } func _LifecycleService_Install_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(LifecycleServiceInstallRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(LifecycleServiceServer).Install(m, &grpc.GenericServerStream[LifecycleServiceInstallRequest, LifecycleServiceInstallResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LifecycleService_InstallServer = grpc.ServerStreamingServer[LifecycleServiceInstallResponse] func _LifecycleService_Upgrade_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(LifecycleServiceUpgradeRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(LifecycleServiceServer).Upgrade(m, &grpc.GenericServerStream[LifecycleServiceUpgradeRequest, LifecycleServiceUpgradeResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type LifecycleService_UpgradeServer = grpc.ServerStreamingServer[LifecycleServiceUpgradeResponse] // LifecycleService_ServiceDesc is the grpc.ServiceDesc for LifecycleService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var LifecycleService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "machine.LifecycleService", HandlerType: (*LifecycleServiceServer)(nil), Methods: []grpc.MethodDesc{}, Streams: []grpc.StreamDesc{ { StreamName: "Install", Handler: _LifecycleService_Install_Handler, ServerStreams: true, }, { StreamName: "Upgrade", Handler: _LifecycleService_Upgrade_Handler, ServerStreams: true, }, }, Metadata: "machine/lifecycle.proto", } ================================================ FILE: pkg/machinery/api/machine/lifecycle_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: machine/lifecycle.proto package machine import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *InstallArtifactsSource) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *InstallArtifactsSource) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *InstallArtifactsSource) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ImageName) > 0 { i -= len(m.ImageName) copy(dAtA[i:], m.ImageName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImageName))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *InstallDestination) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *InstallDestination) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *InstallDestination) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Disk) > 0 { i -= len(m.Disk) copy(dAtA[i:], m.Disk) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Disk))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LifecycleServiceInstallRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LifecycleServiceInstallRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceInstallRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Destination != nil { size, err := m.Destination.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Source != nil { size, err := m.Source.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LifecycleServiceInstallProgress) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LifecycleServiceInstallProgress) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceInstallProgress) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if vtmsg, ok := m.Response.(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size } return len(dAtA) - i, nil } func (m *LifecycleServiceInstallProgress_Message) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceInstallProgress_Message) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0xa return len(dAtA) - i, nil } func (m *LifecycleServiceInstallProgress_ExitCode) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceInstallProgress_ExitCode) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i := len(dAtA) i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ExitCode)) i-- dAtA[i] = 0x10 return len(dAtA) - i, nil } func (m *LifecycleServiceInstallResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LifecycleServiceInstallResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceInstallResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Progress != nil { size, err := m.Progress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LifecycleServiceUpgradeRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LifecycleServiceUpgradeRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceUpgradeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Source != nil { size, err := m.Source.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Containerd != nil { if vtmsg, ok := interface{}(m.Containerd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Containerd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LifecycleServiceUpgradeResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LifecycleServiceUpgradeResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LifecycleServiceUpgradeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Progress != nil { size, err := m.Progress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *InstallArtifactsSource) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ImageName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *InstallDestination) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Disk) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LifecycleServiceInstallRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Source != nil { l = m.Source.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Destination != nil { l = m.Destination.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LifecycleServiceInstallProgress) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if vtmsg, ok := m.Response.(interface{ SizeVT() int }); ok { n += vtmsg.SizeVT() } n += len(m.unknownFields) return n } func (m *LifecycleServiceInstallProgress_Message) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Message) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) return n } func (m *LifecycleServiceInstallProgress_ExitCode) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += 1 + protohelpers.SizeOfVarint(uint64(m.ExitCode)) return n } func (m *LifecycleServiceInstallResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Progress != nil { l = m.Progress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LifecycleServiceUpgradeRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Containerd != nil { if size, ok := interface{}(m.Containerd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Containerd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Source != nil { l = m.Source.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LifecycleServiceUpgradeResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Progress != nil { l = m.Progress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *InstallArtifactsSource) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: InstallArtifactsSource: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: InstallArtifactsSource: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImageName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImageName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *InstallDestination) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: InstallDestination: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: InstallDestination: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Disk", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Disk = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LifecycleServiceInstallRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LifecycleServiceInstallRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LifecycleServiceInstallRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Source == nil { m.Source = &InstallArtifactsSource{} } if err := m.Source.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Destination == nil { m.Destination = &InstallDestination{} } if err := m.Destination.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LifecycleServiceInstallProgress) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LifecycleServiceInstallProgress: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LifecycleServiceInstallProgress: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Response = &LifecycleServiceInstallProgress_Message{Message: string(dAtA[iNdEx:postIndex])} iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ExitCode", wireType) } var v int32 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int32(b&0x7F) << shift if b < 0x80 { break } } m.Response = &LifecycleServiceInstallProgress_ExitCode{ExitCode: v} default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LifecycleServiceInstallResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LifecycleServiceInstallResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LifecycleServiceInstallResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Progress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Progress == nil { m.Progress = &LifecycleServiceInstallProgress{} } if err := m.Progress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LifecycleServiceUpgradeRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LifecycleServiceUpgradeRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LifecycleServiceUpgradeRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containerd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Containerd == nil { m.Containerd = &common.ContainerdInstance{} } if unmarshal, ok := interface{}(m.Containerd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Containerd); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Source == nil { m.Source = &InstallArtifactsSource{} } if err := m.Source.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LifecycleServiceUpgradeResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LifecycleServiceUpgradeResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LifecycleServiceUpgradeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Progress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Progress == nil { m.Progress = &LifecycleServiceInstallProgress{} } if err := m.Progress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/machine/machine.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: machine/machine.proto package machine import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb "google.golang.org/protobuf/types/known/anypb" durationpb "google.golang.org/protobuf/types/known/durationpb" emptypb "google.golang.org/protobuf/types/known/emptypb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type ApplyConfigurationRequest_Mode int32 const ( ApplyConfigurationRequest_REBOOT ApplyConfigurationRequest_Mode = 0 ApplyConfigurationRequest_AUTO ApplyConfigurationRequest_Mode = 1 ApplyConfigurationRequest_NO_REBOOT ApplyConfigurationRequest_Mode = 2 ApplyConfigurationRequest_STAGED ApplyConfigurationRequest_Mode = 3 ApplyConfigurationRequest_TRY ApplyConfigurationRequest_Mode = 4 ) // Enum value maps for ApplyConfigurationRequest_Mode. var ( ApplyConfigurationRequest_Mode_name = map[int32]string{ 0: "REBOOT", 1: "AUTO", 2: "NO_REBOOT", 3: "STAGED", 4: "TRY", } ApplyConfigurationRequest_Mode_value = map[string]int32{ "REBOOT": 0, "AUTO": 1, "NO_REBOOT": 2, "STAGED": 3, "TRY": 4, } ) func (x ApplyConfigurationRequest_Mode) Enum() *ApplyConfigurationRequest_Mode { p := new(ApplyConfigurationRequest_Mode) *p = x return p } func (x ApplyConfigurationRequest_Mode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ApplyConfigurationRequest_Mode) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[0].Descriptor() } func (ApplyConfigurationRequest_Mode) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[0] } func (x ApplyConfigurationRequest_Mode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ApplyConfigurationRequest_Mode.Descriptor instead. func (ApplyConfigurationRequest_Mode) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{0, 0} } type RebootRequest_Mode int32 const ( RebootRequest_DEFAULT RebootRequest_Mode = 0 RebootRequest_POWERCYCLE RebootRequest_Mode = 1 RebootRequest_FORCE RebootRequest_Mode = 2 ) // Enum value maps for RebootRequest_Mode. var ( RebootRequest_Mode_name = map[int32]string{ 0: "DEFAULT", 1: "POWERCYCLE", 2: "FORCE", } RebootRequest_Mode_value = map[string]int32{ "DEFAULT": 0, "POWERCYCLE": 1, "FORCE": 2, } ) func (x RebootRequest_Mode) Enum() *RebootRequest_Mode { p := new(RebootRequest_Mode) *p = x return p } func (x RebootRequest_Mode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (RebootRequest_Mode) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[1].Descriptor() } func (RebootRequest_Mode) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[1] } func (x RebootRequest_Mode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use RebootRequest_Mode.Descriptor instead. func (RebootRequest_Mode) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{3, 0} } type SequenceEvent_Action int32 const ( SequenceEvent_NOOP SequenceEvent_Action = 0 SequenceEvent_START SequenceEvent_Action = 1 SequenceEvent_STOP SequenceEvent_Action = 2 ) // Enum value maps for SequenceEvent_Action. var ( SequenceEvent_Action_name = map[int32]string{ 0: "NOOP", 1: "START", 2: "STOP", } SequenceEvent_Action_value = map[string]int32{ "NOOP": 0, "START": 1, "STOP": 2, } ) func (x SequenceEvent_Action) Enum() *SequenceEvent_Action { p := new(SequenceEvent_Action) *p = x return p } func (x SequenceEvent_Action) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (SequenceEvent_Action) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[2].Descriptor() } func (SequenceEvent_Action) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[2] } func (x SequenceEvent_Action) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use SequenceEvent_Action.Descriptor instead. func (SequenceEvent_Action) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{9, 0} } type PhaseEvent_Action int32 const ( PhaseEvent_START PhaseEvent_Action = 0 PhaseEvent_STOP PhaseEvent_Action = 1 ) // Enum value maps for PhaseEvent_Action. var ( PhaseEvent_Action_name = map[int32]string{ 0: "START", 1: "STOP", } PhaseEvent_Action_value = map[string]int32{ "START": 0, "STOP": 1, } ) func (x PhaseEvent_Action) Enum() *PhaseEvent_Action { p := new(PhaseEvent_Action) *p = x return p } func (x PhaseEvent_Action) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (PhaseEvent_Action) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[3].Descriptor() } func (PhaseEvent_Action) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[3] } func (x PhaseEvent_Action) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use PhaseEvent_Action.Descriptor instead. func (PhaseEvent_Action) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{10, 0} } type TaskEvent_Action int32 const ( TaskEvent_START TaskEvent_Action = 0 TaskEvent_STOP TaskEvent_Action = 1 ) // Enum value maps for TaskEvent_Action. var ( TaskEvent_Action_name = map[int32]string{ 0: "START", 1: "STOP", } TaskEvent_Action_value = map[string]int32{ "START": 0, "STOP": 1, } ) func (x TaskEvent_Action) Enum() *TaskEvent_Action { p := new(TaskEvent_Action) *p = x return p } func (x TaskEvent_Action) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (TaskEvent_Action) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[4].Descriptor() } func (TaskEvent_Action) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[4] } func (x TaskEvent_Action) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use TaskEvent_Action.Descriptor instead. func (TaskEvent_Action) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{11, 0} } type ServiceStateEvent_Action int32 const ( ServiceStateEvent_INITIALIZED ServiceStateEvent_Action = 0 ServiceStateEvent_PREPARING ServiceStateEvent_Action = 1 ServiceStateEvent_WAITING ServiceStateEvent_Action = 2 ServiceStateEvent_RUNNING ServiceStateEvent_Action = 3 ServiceStateEvent_STOPPING ServiceStateEvent_Action = 4 ServiceStateEvent_FINISHED ServiceStateEvent_Action = 5 ServiceStateEvent_FAILED ServiceStateEvent_Action = 6 ServiceStateEvent_SKIPPED ServiceStateEvent_Action = 7 ServiceStateEvent_STARTING ServiceStateEvent_Action = 8 ) // Enum value maps for ServiceStateEvent_Action. var ( ServiceStateEvent_Action_name = map[int32]string{ 0: "INITIALIZED", 1: "PREPARING", 2: "WAITING", 3: "RUNNING", 4: "STOPPING", 5: "FINISHED", 6: "FAILED", 7: "SKIPPED", 8: "STARTING", } ServiceStateEvent_Action_value = map[string]int32{ "INITIALIZED": 0, "PREPARING": 1, "WAITING": 2, "RUNNING": 3, "STOPPING": 4, "FINISHED": 5, "FAILED": 6, "SKIPPED": 7, "STARTING": 8, } ) func (x ServiceStateEvent_Action) Enum() *ServiceStateEvent_Action { p := new(ServiceStateEvent_Action) *p = x return p } func (x ServiceStateEvent_Action) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ServiceStateEvent_Action) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[5].Descriptor() } func (ServiceStateEvent_Action) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[5] } func (x ServiceStateEvent_Action) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ServiceStateEvent_Action.Descriptor instead. func (ServiceStateEvent_Action) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{12, 0} } type MachineStatusEvent_MachineStage int32 const ( MachineStatusEvent_UNKNOWN MachineStatusEvent_MachineStage = 0 MachineStatusEvent_BOOTING MachineStatusEvent_MachineStage = 1 MachineStatusEvent_INSTALLING MachineStatusEvent_MachineStage = 2 MachineStatusEvent_MAINTENANCE MachineStatusEvent_MachineStage = 3 MachineStatusEvent_RUNNING MachineStatusEvent_MachineStage = 4 MachineStatusEvent_REBOOTING MachineStatusEvent_MachineStage = 5 MachineStatusEvent_SHUTTING_DOWN MachineStatusEvent_MachineStage = 6 MachineStatusEvent_RESETTING MachineStatusEvent_MachineStage = 7 MachineStatusEvent_UPGRADING MachineStatusEvent_MachineStage = 8 ) // Enum value maps for MachineStatusEvent_MachineStage. var ( MachineStatusEvent_MachineStage_name = map[int32]string{ 0: "UNKNOWN", 1: "BOOTING", 2: "INSTALLING", 3: "MAINTENANCE", 4: "RUNNING", 5: "REBOOTING", 6: "SHUTTING_DOWN", 7: "RESETTING", 8: "UPGRADING", } MachineStatusEvent_MachineStage_value = map[string]int32{ "UNKNOWN": 0, "BOOTING": 1, "INSTALLING": 2, "MAINTENANCE": 3, "RUNNING": 4, "REBOOTING": 5, "SHUTTING_DOWN": 6, "RESETTING": 7, "UPGRADING": 8, } ) func (x MachineStatusEvent_MachineStage) Enum() *MachineStatusEvent_MachineStage { p := new(MachineStatusEvent_MachineStage) *p = x return p } func (x MachineStatusEvent_MachineStage) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (MachineStatusEvent_MachineStage) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[6].Descriptor() } func (MachineStatusEvent_MachineStage) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[6] } func (x MachineStatusEvent_MachineStage) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use MachineStatusEvent_MachineStage.Descriptor instead. func (MachineStatusEvent_MachineStage) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{17, 0} } type ResetRequest_WipeMode int32 const ( ResetRequest_ALL ResetRequest_WipeMode = 0 ResetRequest_SYSTEM_DISK ResetRequest_WipeMode = 1 ResetRequest_USER_DISKS ResetRequest_WipeMode = 2 ) // Enum value maps for ResetRequest_WipeMode. var ( ResetRequest_WipeMode_name = map[int32]string{ 0: "ALL", 1: "SYSTEM_DISK", 2: "USER_DISKS", } ResetRequest_WipeMode_value = map[string]int32{ "ALL": 0, "SYSTEM_DISK": 1, "USER_DISKS": 2, } ) func (x ResetRequest_WipeMode) Enum() *ResetRequest_WipeMode { p := new(ResetRequest_WipeMode) *p = x return p } func (x ResetRequest_WipeMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ResetRequest_WipeMode) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[7].Descriptor() } func (ResetRequest_WipeMode) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[7] } func (x ResetRequest_WipeMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ResetRequest_WipeMode.Descriptor instead. func (ResetRequest_WipeMode) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{21, 0} } type UpgradeRequest_RebootMode int32 const ( UpgradeRequest_DEFAULT UpgradeRequest_RebootMode = 0 UpgradeRequest_POWERCYCLE UpgradeRequest_RebootMode = 1 ) // Enum value maps for UpgradeRequest_RebootMode. var ( UpgradeRequest_RebootMode_name = map[int32]string{ 0: "DEFAULT", 1: "POWERCYCLE", } UpgradeRequest_RebootMode_value = map[string]int32{ "DEFAULT": 0, "POWERCYCLE": 1, } ) func (x UpgradeRequest_RebootMode) Enum() *UpgradeRequest_RebootMode { p := new(UpgradeRequest_RebootMode) *p = x return p } func (x UpgradeRequest_RebootMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (UpgradeRequest_RebootMode) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[8].Descriptor() } func (UpgradeRequest_RebootMode) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[8] } func (x UpgradeRequest_RebootMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use UpgradeRequest_RebootMode.Descriptor instead. func (UpgradeRequest_RebootMode) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{27, 0} } // File type. type ListRequest_Type int32 const ( // Regular file (not directory, symlink, etc). ListRequest_REGULAR ListRequest_Type = 0 // Directory. ListRequest_DIRECTORY ListRequest_Type = 1 // Symbolic link. ListRequest_SYMLINK ListRequest_Type = 2 ) // Enum value maps for ListRequest_Type. var ( ListRequest_Type_name = map[int32]string{ 0: "REGULAR", 1: "DIRECTORY", 2: "SYMLINK", } ListRequest_Type_value = map[string]int32{ "REGULAR": 0, "DIRECTORY": 1, "SYMLINK": 2, } ) func (x ListRequest_Type) Enum() *ListRequest_Type { p := new(ListRequest_Type) *p = x return p } func (x ListRequest_Type) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ListRequest_Type) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[9].Descriptor() } func (ListRequest_Type) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[9] } func (x ListRequest_Type) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ListRequest_Type.Descriptor instead. func (ListRequest_Type) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{46, 0} } type EtcdMemberAlarm_AlarmType int32 const ( EtcdMemberAlarm_NONE EtcdMemberAlarm_AlarmType = 0 EtcdMemberAlarm_NOSPACE EtcdMemberAlarm_AlarmType = 1 EtcdMemberAlarm_CORRUPT EtcdMemberAlarm_AlarmType = 2 ) // Enum value maps for EtcdMemberAlarm_AlarmType. var ( EtcdMemberAlarm_AlarmType_name = map[int32]string{ 0: "NONE", 1: "NOSPACE", 2: "CORRUPT", } EtcdMemberAlarm_AlarmType_value = map[string]int32{ "NONE": 0, "NOSPACE": 1, "CORRUPT": 2, } ) func (x EtcdMemberAlarm_AlarmType) Enum() *EtcdMemberAlarm_AlarmType { p := new(EtcdMemberAlarm_AlarmType) *p = x return p } func (x EtcdMemberAlarm_AlarmType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (EtcdMemberAlarm_AlarmType) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[10].Descriptor() } func (EtcdMemberAlarm_AlarmType) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[10] } func (x EtcdMemberAlarm_AlarmType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use EtcdMemberAlarm_AlarmType.Descriptor instead. func (EtcdMemberAlarm_AlarmType) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{125, 0} } type MachineConfig_MachineType int32 const ( MachineConfig_TYPE_UNKNOWN MachineConfig_MachineType = 0 MachineConfig_TYPE_INIT MachineConfig_MachineType = 1 MachineConfig_TYPE_CONTROL_PLANE MachineConfig_MachineType = 2 MachineConfig_TYPE_WORKER MachineConfig_MachineType = 3 ) // Enum value maps for MachineConfig_MachineType. var ( MachineConfig_MachineType_name = map[int32]string{ 0: "TYPE_UNKNOWN", 1: "TYPE_INIT", 2: "TYPE_CONTROL_PLANE", 3: "TYPE_WORKER", } MachineConfig_MachineType_value = map[string]int32{ "TYPE_UNKNOWN": 0, "TYPE_INIT": 1, "TYPE_CONTROL_PLANE": 2, "TYPE_WORKER": 3, } ) func (x MachineConfig_MachineType) Enum() *MachineConfig_MachineType { p := new(MachineConfig_MachineType) *p = x return p } func (x MachineConfig_MachineType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (MachineConfig_MachineType) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[11].Descriptor() } func (MachineConfig_MachineType) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[11] } func (x MachineConfig_MachineType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use MachineConfig_MachineType.Descriptor instead. func (MachineConfig_MachineType) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{147, 0} } type NetstatRequest_Filter int32 const ( NetstatRequest_ALL NetstatRequest_Filter = 0 NetstatRequest_CONNECTED NetstatRequest_Filter = 1 NetstatRequest_LISTENING NetstatRequest_Filter = 2 ) // Enum value maps for NetstatRequest_Filter. var ( NetstatRequest_Filter_name = map[int32]string{ 0: "ALL", 1: "CONNECTED", 2: "LISTENING", } NetstatRequest_Filter_value = map[string]int32{ "ALL": 0, "CONNECTED": 1, "LISTENING": 2, } ) func (x NetstatRequest_Filter) Enum() *NetstatRequest_Filter { p := new(NetstatRequest_Filter) *p = x return p } func (x NetstatRequest_Filter) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NetstatRequest_Filter) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[12].Descriptor() } func (NetstatRequest_Filter) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[12] } func (x NetstatRequest_Filter) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NetstatRequest_Filter.Descriptor instead. func (NetstatRequest_Filter) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{157, 0} } type ConnectRecord_State int32 const ( ConnectRecord_RESERVED ConnectRecord_State = 0 ConnectRecord_ESTABLISHED ConnectRecord_State = 1 ConnectRecord_SYN_SENT ConnectRecord_State = 2 ConnectRecord_SYN_RECV ConnectRecord_State = 3 ConnectRecord_FIN_WAIT1 ConnectRecord_State = 4 ConnectRecord_FIN_WAIT2 ConnectRecord_State = 5 ConnectRecord_TIME_WAIT ConnectRecord_State = 6 ConnectRecord_CLOSE ConnectRecord_State = 7 ConnectRecord_CLOSEWAIT ConnectRecord_State = 8 ConnectRecord_LASTACK ConnectRecord_State = 9 ConnectRecord_LISTEN ConnectRecord_State = 10 ConnectRecord_CLOSING ConnectRecord_State = 11 ) // Enum value maps for ConnectRecord_State. var ( ConnectRecord_State_name = map[int32]string{ 0: "RESERVED", 1: "ESTABLISHED", 2: "SYN_SENT", 3: "SYN_RECV", 4: "FIN_WAIT1", 5: "FIN_WAIT2", 6: "TIME_WAIT", 7: "CLOSE", 8: "CLOSEWAIT", 9: "LASTACK", 10: "LISTEN", 11: "CLOSING", } ConnectRecord_State_value = map[string]int32{ "RESERVED": 0, "ESTABLISHED": 1, "SYN_SENT": 2, "SYN_RECV": 3, "FIN_WAIT1": 4, "FIN_WAIT2": 5, "TIME_WAIT": 6, "CLOSE": 7, "CLOSEWAIT": 8, "LASTACK": 9, "LISTEN": 10, "CLOSING": 11, } ) func (x ConnectRecord_State) Enum() *ConnectRecord_State { p := new(ConnectRecord_State) *p = x return p } func (x ConnectRecord_State) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ConnectRecord_State) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[13].Descriptor() } func (ConnectRecord_State) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[13] } func (x ConnectRecord_State) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ConnectRecord_State.Descriptor instead. func (ConnectRecord_State) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{158, 0} } type ConnectRecord_TimerActive int32 const ( ConnectRecord_OFF ConnectRecord_TimerActive = 0 ConnectRecord_ON ConnectRecord_TimerActive = 1 ConnectRecord_KEEPALIVE ConnectRecord_TimerActive = 2 ConnectRecord_TIMEWAIT ConnectRecord_TimerActive = 3 ConnectRecord_PROBE ConnectRecord_TimerActive = 4 ) // Enum value maps for ConnectRecord_TimerActive. var ( ConnectRecord_TimerActive_name = map[int32]string{ 0: "OFF", 1: "ON", 2: "KEEPALIVE", 3: "TIMEWAIT", 4: "PROBE", } ConnectRecord_TimerActive_value = map[string]int32{ "OFF": 0, "ON": 1, "KEEPALIVE": 2, "TIMEWAIT": 3, "PROBE": 4, } ) func (x ConnectRecord_TimerActive) Enum() *ConnectRecord_TimerActive { p := new(ConnectRecord_TimerActive) *p = x return p } func (x ConnectRecord_TimerActive) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (ConnectRecord_TimerActive) Descriptor() protoreflect.EnumDescriptor { return file_machine_machine_proto_enumTypes[14].Descriptor() } func (ConnectRecord_TimerActive) Type() protoreflect.EnumType { return &file_machine_machine_proto_enumTypes[14] } func (x ConnectRecord_TimerActive) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use ConnectRecord_TimerActive.Descriptor instead. func (ConnectRecord_TimerActive) EnumDescriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{158, 1} } // rpc applyConfiguration // ApplyConfiguration describes a request to assert a new configuration upon a // node. type ApplyConfigurationRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` Mode ApplyConfigurationRequest_Mode `protobuf:"varint,4,opt,name=mode,proto3,enum=machine.ApplyConfigurationRequest_Mode" json:"mode,omitempty"` DryRun bool `protobuf:"varint,5,opt,name=dry_run,json=dryRun,proto3" json:"dry_run,omitempty"` TryModeTimeout *durationpb.Duration `protobuf:"bytes,6,opt,name=try_mode_timeout,json=tryModeTimeout,proto3" json:"try_mode_timeout,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ApplyConfigurationRequest) Reset() { *x = ApplyConfigurationRequest{} mi := &file_machine_machine_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ApplyConfigurationRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ApplyConfigurationRequest) ProtoMessage() {} func (x *ApplyConfigurationRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ApplyConfigurationRequest.ProtoReflect.Descriptor instead. func (*ApplyConfigurationRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{0} } func (x *ApplyConfigurationRequest) GetData() []byte { if x != nil { return x.Data } return nil } func (x *ApplyConfigurationRequest) GetMode() ApplyConfigurationRequest_Mode { if x != nil { return x.Mode } return ApplyConfigurationRequest_REBOOT } func (x *ApplyConfigurationRequest) GetDryRun() bool { if x != nil { return x.DryRun } return false } func (x *ApplyConfigurationRequest) GetTryModeTimeout() *durationpb.Duration { if x != nil { return x.TryModeTimeout } return nil } // ApplyConfigurationResponse describes the response to a configuration request. type ApplyConfiguration struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // Configuration validation warnings. Warnings []string `protobuf:"bytes,2,rep,name=warnings,proto3" json:"warnings,omitempty"` // States which mode was actually chosen. Mode ApplyConfigurationRequest_Mode `protobuf:"varint,3,opt,name=mode,proto3,enum=machine.ApplyConfigurationRequest_Mode" json:"mode,omitempty"` // Human-readable message explaining the result of the apply configuration call. ModeDetails string `protobuf:"bytes,4,opt,name=mode_details,json=modeDetails,proto3" json:"mode_details,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ApplyConfiguration) Reset() { *x = ApplyConfiguration{} mi := &file_machine_machine_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ApplyConfiguration) String() string { return protoimpl.X.MessageStringOf(x) } func (*ApplyConfiguration) ProtoMessage() {} func (x *ApplyConfiguration) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ApplyConfiguration.ProtoReflect.Descriptor instead. func (*ApplyConfiguration) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{1} } func (x *ApplyConfiguration) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ApplyConfiguration) GetWarnings() []string { if x != nil { return x.Warnings } return nil } func (x *ApplyConfiguration) GetMode() ApplyConfigurationRequest_Mode { if x != nil { return x.Mode } return ApplyConfigurationRequest_REBOOT } func (x *ApplyConfiguration) GetModeDetails() string { if x != nil { return x.ModeDetails } return "" } type ApplyConfigurationResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ApplyConfiguration `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ApplyConfigurationResponse) Reset() { *x = ApplyConfigurationResponse{} mi := &file_machine_machine_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ApplyConfigurationResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ApplyConfigurationResponse) ProtoMessage() {} func (x *ApplyConfigurationResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ApplyConfigurationResponse.ProtoReflect.Descriptor instead. func (*ApplyConfigurationResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{2} } func (x *ApplyConfigurationResponse) GetMessages() []*ApplyConfiguration { if x != nil { return x.Messages } return nil } // rpc reboot type RebootRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Mode RebootRequest_Mode `protobuf:"varint,1,opt,name=mode,proto3,enum=machine.RebootRequest_Mode" json:"mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RebootRequest) Reset() { *x = RebootRequest{} mi := &file_machine_machine_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RebootRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RebootRequest) ProtoMessage() {} func (x *RebootRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RebootRequest.ProtoReflect.Descriptor instead. func (*RebootRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{3} } func (x *RebootRequest) GetMode() RebootRequest_Mode { if x != nil { return x.Mode } return RebootRequest_DEFAULT } // The reboot message containing the reboot status. type Reboot struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ActorId string `protobuf:"bytes,2,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Reboot) Reset() { *x = Reboot{} mi := &file_machine_machine_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Reboot) String() string { return protoimpl.X.MessageStringOf(x) } func (*Reboot) ProtoMessage() {} func (x *Reboot) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Reboot.ProtoReflect.Descriptor instead. func (*Reboot) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{4} } func (x *Reboot) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Reboot) GetActorId() string { if x != nil { return x.ActorId } return "" } type RebootResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Reboot `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RebootResponse) Reset() { *x = RebootResponse{} mi := &file_machine_machine_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RebootResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RebootResponse) ProtoMessage() {} func (x *RebootResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RebootResponse.ProtoReflect.Descriptor instead. func (*RebootResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{5} } func (x *RebootResponse) GetMessages() []*Reboot { if x != nil { return x.Messages } return nil } // rpc Bootstrap type BootstrapRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Enable etcd recovery from the snapshot. // Snapshot should be uploaded before this call via EtcdRecover RPC. RecoverEtcd bool `protobuf:"varint,1,opt,name=recover_etcd,json=recoverEtcd,proto3" json:"recover_etcd,omitempty"` // Skip hash check on the snapshot (etcd). // Enable this when recovering from data directory copy to skip integrity check. RecoverSkipHashCheck bool `protobuf:"varint,2,opt,name=recover_skip_hash_check,json=recoverSkipHashCheck,proto3" json:"recover_skip_hash_check,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BootstrapRequest) Reset() { *x = BootstrapRequest{} mi := &file_machine_machine_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BootstrapRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*BootstrapRequest) ProtoMessage() {} func (x *BootstrapRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BootstrapRequest.ProtoReflect.Descriptor instead. func (*BootstrapRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{6} } func (x *BootstrapRequest) GetRecoverEtcd() bool { if x != nil { return x.RecoverEtcd } return false } func (x *BootstrapRequest) GetRecoverSkipHashCheck() bool { if x != nil { return x.RecoverSkipHashCheck } return false } // The bootstrap message containing the bootstrap status. type Bootstrap struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Bootstrap) Reset() { *x = Bootstrap{} mi := &file_machine_machine_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Bootstrap) String() string { return protoimpl.X.MessageStringOf(x) } func (*Bootstrap) ProtoMessage() {} func (x *Bootstrap) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Bootstrap.ProtoReflect.Descriptor instead. func (*Bootstrap) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{7} } func (x *Bootstrap) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type BootstrapResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Bootstrap `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BootstrapResponse) Reset() { *x = BootstrapResponse{} mi := &file_machine_machine_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BootstrapResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*BootstrapResponse) ProtoMessage() {} func (x *BootstrapResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BootstrapResponse.ProtoReflect.Descriptor instead. func (*BootstrapResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{8} } func (x *BootstrapResponse) GetMessages() []*Bootstrap { if x != nil { return x.Messages } return nil } // rpc events type SequenceEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Sequence string `protobuf:"bytes,1,opt,name=sequence,proto3" json:"sequence,omitempty"` Action SequenceEvent_Action `protobuf:"varint,2,opt,name=action,proto3,enum=machine.SequenceEvent_Action" json:"action,omitempty"` Error *common.Error `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SequenceEvent) Reset() { *x = SequenceEvent{} mi := &file_machine_machine_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SequenceEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*SequenceEvent) ProtoMessage() {} func (x *SequenceEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SequenceEvent.ProtoReflect.Descriptor instead. func (*SequenceEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{9} } func (x *SequenceEvent) GetSequence() string { if x != nil { return x.Sequence } return "" } func (x *SequenceEvent) GetAction() SequenceEvent_Action { if x != nil { return x.Action } return SequenceEvent_NOOP } func (x *SequenceEvent) GetError() *common.Error { if x != nil { return x.Error } return nil } type PhaseEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Phase string `protobuf:"bytes,1,opt,name=phase,proto3" json:"phase,omitempty"` Action PhaseEvent_Action `protobuf:"varint,2,opt,name=action,proto3,enum=machine.PhaseEvent_Action" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PhaseEvent) Reset() { *x = PhaseEvent{} mi := &file_machine_machine_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PhaseEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*PhaseEvent) ProtoMessage() {} func (x *PhaseEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PhaseEvent.ProtoReflect.Descriptor instead. func (*PhaseEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{10} } func (x *PhaseEvent) GetPhase() string { if x != nil { return x.Phase } return "" } func (x *PhaseEvent) GetAction() PhaseEvent_Action { if x != nil { return x.Action } return PhaseEvent_START } type TaskEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Task string `protobuf:"bytes,1,opt,name=task,proto3" json:"task,omitempty"` Action TaskEvent_Action `protobuf:"varint,2,opt,name=action,proto3,enum=machine.TaskEvent_Action" json:"action,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TaskEvent) Reset() { *x = TaskEvent{} mi := &file_machine_machine_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TaskEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*TaskEvent) ProtoMessage() {} func (x *TaskEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TaskEvent.ProtoReflect.Descriptor instead. func (*TaskEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{11} } func (x *TaskEvent) GetTask() string { if x != nil { return x.Task } return "" } func (x *TaskEvent) GetAction() TaskEvent_Action { if x != nil { return x.Action } return TaskEvent_START } type ServiceStateEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` Action ServiceStateEvent_Action `protobuf:"varint,2,opt,name=action,proto3,enum=machine.ServiceStateEvent_Action" json:"action,omitempty"` Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"` Health *ServiceHealth `protobuf:"bytes,4,opt,name=health,proto3" json:"health,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStateEvent) Reset() { *x = ServiceStateEvent{} mi := &file_machine_machine_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStateEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStateEvent) ProtoMessage() {} func (x *ServiceStateEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStateEvent.ProtoReflect.Descriptor instead. func (*ServiceStateEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{12} } func (x *ServiceStateEvent) GetService() string { if x != nil { return x.Service } return "" } func (x *ServiceStateEvent) GetAction() ServiceStateEvent_Action { if x != nil { return x.Action } return ServiceStateEvent_INITIALIZED } func (x *ServiceStateEvent) GetMessage() string { if x != nil { return x.Message } return "" } func (x *ServiceStateEvent) GetHealth() *ServiceHealth { if x != nil { return x.Health } return nil } type RestartEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Cmd int64 `protobuf:"varint,1,opt,name=cmd,proto3" json:"cmd,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestartEvent) Reset() { *x = RestartEvent{} mi := &file_machine_machine_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RestartEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*RestartEvent) ProtoMessage() {} func (x *RestartEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RestartEvent.ProtoReflect.Descriptor instead. func (*RestartEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{13} } func (x *RestartEvent) GetCmd() int64 { if x != nil { return x.Cmd } return 0 } // ConfigLoadErrorEvent is reported when the config loading has failed. type ConfigLoadErrorEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigLoadErrorEvent) Reset() { *x = ConfigLoadErrorEvent{} mi := &file_machine_machine_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigLoadErrorEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigLoadErrorEvent) ProtoMessage() {} func (x *ConfigLoadErrorEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigLoadErrorEvent.ProtoReflect.Descriptor instead. func (*ConfigLoadErrorEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{14} } func (x *ConfigLoadErrorEvent) GetError() string { if x != nil { return x.Error } return "" } // ConfigValidationErrorEvent is reported when config validation has failed. type ConfigValidationErrorEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigValidationErrorEvent) Reset() { *x = ConfigValidationErrorEvent{} mi := &file_machine_machine_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigValidationErrorEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigValidationErrorEvent) ProtoMessage() {} func (x *ConfigValidationErrorEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigValidationErrorEvent.ProtoReflect.Descriptor instead. func (*ConfigValidationErrorEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{15} } func (x *ConfigValidationErrorEvent) GetError() string { if x != nil { return x.Error } return "" } // AddressEvent reports node endpoints aggregated from k8s.Endpoints and network.Hostname. type AddressEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` Addresses []string `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddressEvent) Reset() { *x = AddressEvent{} mi := &file_machine_machine_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddressEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddressEvent) ProtoMessage() {} func (x *AddressEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddressEvent.ProtoReflect.Descriptor instead. func (*AddressEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{16} } func (x *AddressEvent) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *AddressEvent) GetAddresses() []string { if x != nil { return x.Addresses } return nil } // MachineStatusEvent reports changes to the MachineStatus resource. type MachineStatusEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Stage MachineStatusEvent_MachineStage `protobuf:"varint,1,opt,name=stage,proto3,enum=machine.MachineStatusEvent_MachineStage" json:"stage,omitempty"` Status *MachineStatusEvent_MachineStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineStatusEvent) Reset() { *x = MachineStatusEvent{} mi := &file_machine_machine_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineStatusEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineStatusEvent) ProtoMessage() {} func (x *MachineStatusEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineStatusEvent.ProtoReflect.Descriptor instead. func (*MachineStatusEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{17} } func (x *MachineStatusEvent) GetStage() MachineStatusEvent_MachineStage { if x != nil { return x.Stage } return MachineStatusEvent_UNKNOWN } func (x *MachineStatusEvent) GetStatus() *MachineStatusEvent_MachineStatus { if x != nil { return x.Status } return nil } type EventsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` TailEvents int32 `protobuf:"varint,1,opt,name=tail_events,json=tailEvents,proto3" json:"tail_events,omitempty"` TailId string `protobuf:"bytes,2,opt,name=tail_id,json=tailId,proto3" json:"tail_id,omitempty"` TailSeconds int32 `protobuf:"varint,3,opt,name=tail_seconds,json=tailSeconds,proto3" json:"tail_seconds,omitempty"` WithActorId string `protobuf:"bytes,4,opt,name=with_actor_id,json=withActorId,proto3" json:"with_actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EventsRequest) Reset() { *x = EventsRequest{} mi := &file_machine_machine_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EventsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EventsRequest) ProtoMessage() {} func (x *EventsRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EventsRequest.ProtoReflect.Descriptor instead. func (*EventsRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{18} } func (x *EventsRequest) GetTailEvents() int32 { if x != nil { return x.TailEvents } return 0 } func (x *EventsRequest) GetTailId() string { if x != nil { return x.TailId } return "" } func (x *EventsRequest) GetTailSeconds() int32 { if x != nil { return x.TailSeconds } return 0 } func (x *EventsRequest) GetWithActorId() string { if x != nil { return x.WithActorId } return "" } type Event struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Data *anypb.Any `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` ActorId string `protobuf:"bytes,4,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Event) Reset() { *x = Event{} mi := &file_machine_machine_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Event) String() string { return protoimpl.X.MessageStringOf(x) } func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Event.ProtoReflect.Descriptor instead. func (*Event) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{19} } func (x *Event) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Event) GetData() *anypb.Any { if x != nil { return x.Data } return nil } func (x *Event) GetId() string { if x != nil { return x.Id } return "" } func (x *Event) GetActorId() string { if x != nil { return x.ActorId } return "" } // rpc reset type ResetPartitionSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Label string `protobuf:"bytes,1,opt,name=label,proto3" json:"label,omitempty"` Wipe bool `protobuf:"varint,2,opt,name=wipe,proto3" json:"wipe,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResetPartitionSpec) Reset() { *x = ResetPartitionSpec{} mi := &file_machine_machine_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResetPartitionSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResetPartitionSpec) ProtoMessage() {} func (x *ResetPartitionSpec) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResetPartitionSpec.ProtoReflect.Descriptor instead. func (*ResetPartitionSpec) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{20} } func (x *ResetPartitionSpec) GetLabel() string { if x != nil { return x.Label } return "" } func (x *ResetPartitionSpec) GetWipe() bool { if x != nil { return x.Wipe } return false } type ResetRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Graceful indicates whether node should leave etcd before the upgrade, it also // enforces etcd checks before leaving. Graceful bool `protobuf:"varint,1,opt,name=graceful,proto3" json:"graceful,omitempty"` // Reboot indicates whether node should reboot or halt after resetting. Reboot bool `protobuf:"varint,2,opt,name=reboot,proto3" json:"reboot,omitempty"` // System_partitions_to_wipe lists specific system disk partitions to be reset (wiped). // If system_partitions_to_wipe is empty, all the partitions are erased. SystemPartitionsToWipe []*ResetPartitionSpec `protobuf:"bytes,3,rep,name=system_partitions_to_wipe,json=systemPartitionsToWipe,proto3" json:"system_partitions_to_wipe,omitempty"` // UserDisksToWipe lists specific connected block devices to be reset (wiped). UserDisksToWipe []string `protobuf:"bytes,4,rep,name=user_disks_to_wipe,json=userDisksToWipe,proto3" json:"user_disks_to_wipe,omitempty"` // WipeMode defines which devices should be wiped. Mode ResetRequest_WipeMode `protobuf:"varint,5,opt,name=mode,proto3,enum=machine.ResetRequest_WipeMode" json:"mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResetRequest) Reset() { *x = ResetRequest{} mi := &file_machine_machine_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResetRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResetRequest) ProtoMessage() {} func (x *ResetRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResetRequest.ProtoReflect.Descriptor instead. func (*ResetRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{21} } func (x *ResetRequest) GetGraceful() bool { if x != nil { return x.Graceful } return false } func (x *ResetRequest) GetReboot() bool { if x != nil { return x.Reboot } return false } func (x *ResetRequest) GetSystemPartitionsToWipe() []*ResetPartitionSpec { if x != nil { return x.SystemPartitionsToWipe } return nil } func (x *ResetRequest) GetUserDisksToWipe() []string { if x != nil { return x.UserDisksToWipe } return nil } func (x *ResetRequest) GetMode() ResetRequest_WipeMode { if x != nil { return x.Mode } return ResetRequest_ALL } // The reset message containing the restart status. type Reset struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ActorId string `protobuf:"bytes,2,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Reset) Reset() { *x = Reset{} mi := &file_machine_machine_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Reset) String() string { return protoimpl.X.MessageStringOf(x) } func (*Reset) ProtoMessage() {} func (x *Reset) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Reset.ProtoReflect.Descriptor instead. func (*Reset) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{22} } func (x *Reset) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Reset) GetActorId() string { if x != nil { return x.ActorId } return "" } type ResetResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Reset `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResetResponse) Reset() { *x = ResetResponse{} mi := &file_machine_machine_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResetResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResetResponse) ProtoMessage() {} func (x *ResetResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResetResponse.ProtoReflect.Descriptor instead. func (*ResetResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{23} } func (x *ResetResponse) GetMessages() []*Reset { if x != nil { return x.Messages } return nil } // rpc shutdown // The messages message containing the shutdown status. type Shutdown struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ActorId string `protobuf:"bytes,2,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Shutdown) Reset() { *x = Shutdown{} mi := &file_machine_machine_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Shutdown) String() string { return protoimpl.X.MessageStringOf(x) } func (*Shutdown) ProtoMessage() {} func (x *Shutdown) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Shutdown.ProtoReflect.Descriptor instead. func (*Shutdown) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{24} } func (x *Shutdown) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Shutdown) GetActorId() string { if x != nil { return x.ActorId } return "" } type ShutdownRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Force indicates whether node should shutdown without first cordening and draining Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ShutdownRequest) Reset() { *x = ShutdownRequest{} mi := &file_machine_machine_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ShutdownRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShutdownRequest) ProtoMessage() {} func (x *ShutdownRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShutdownRequest.ProtoReflect.Descriptor instead. func (*ShutdownRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{25} } func (x *ShutdownRequest) GetForce() bool { if x != nil { return x.Force } return false } type ShutdownResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Shutdown `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ShutdownResponse) Reset() { *x = ShutdownResponse{} mi := &file_machine_machine_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ShutdownResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ShutdownResponse) ProtoMessage() {} func (x *ShutdownResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ShutdownResponse.ProtoReflect.Descriptor instead. func (*ShutdownResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{26} } func (x *ShutdownResponse) GetMessages() []*Shutdown { if x != nil { return x.Messages } return nil } // rpc upgrade type UpgradeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` Preserve bool `protobuf:"varint,2,opt,name=preserve,proto3" json:"preserve,omitempty"` Stage bool `protobuf:"varint,3,opt,name=stage,proto3" json:"stage,omitempty"` Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` RebootMode UpgradeRequest_RebootMode `protobuf:"varint,5,opt,name=reboot_mode,json=rebootMode,proto3,enum=machine.UpgradeRequest_RebootMode" json:"reboot_mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpgradeRequest) Reset() { *x = UpgradeRequest{} mi := &file_machine_machine_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpgradeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpgradeRequest) ProtoMessage() {} func (x *UpgradeRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpgradeRequest.ProtoReflect.Descriptor instead. func (*UpgradeRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{27} } func (x *UpgradeRequest) GetImage() string { if x != nil { return x.Image } return "" } func (x *UpgradeRequest) GetPreserve() bool { if x != nil { return x.Preserve } return false } func (x *UpgradeRequest) GetStage() bool { if x != nil { return x.Stage } return false } func (x *UpgradeRequest) GetForce() bool { if x != nil { return x.Force } return false } func (x *UpgradeRequest) GetRebootMode() UpgradeRequest_RebootMode { if x != nil { return x.RebootMode } return UpgradeRequest_DEFAULT } type Upgrade struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Ack string `protobuf:"bytes,2,opt,name=ack,proto3" json:"ack,omitempty"` ActorId string `protobuf:"bytes,3,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Upgrade) Reset() { *x = Upgrade{} mi := &file_machine_machine_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Upgrade) String() string { return protoimpl.X.MessageStringOf(x) } func (*Upgrade) ProtoMessage() {} func (x *Upgrade) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Upgrade.ProtoReflect.Descriptor instead. func (*Upgrade) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{28} } func (x *Upgrade) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Upgrade) GetAck() string { if x != nil { return x.Ack } return "" } func (x *Upgrade) GetActorId() string { if x != nil { return x.ActorId } return "" } type UpgradeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Upgrade `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UpgradeResponse) Reset() { *x = UpgradeResponse{} mi := &file_machine_machine_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UpgradeResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*UpgradeResponse) ProtoMessage() {} func (x *UpgradeResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UpgradeResponse.ProtoReflect.Descriptor instead. func (*UpgradeResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{29} } func (x *UpgradeResponse) GetMessages() []*Upgrade { if x != nil { return x.Messages } return nil } // rpc servicelist type ServiceList struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Services []*ServiceInfo `protobuf:"bytes,2,rep,name=services,proto3" json:"services,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceList) Reset() { *x = ServiceList{} mi := &file_machine_machine_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceList) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceList) ProtoMessage() {} func (x *ServiceList) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceList.ProtoReflect.Descriptor instead. func (*ServiceList) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{30} } func (x *ServiceList) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ServiceList) GetServices() []*ServiceInfo { if x != nil { return x.Services } return nil } type ServiceListResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ServiceList `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceListResponse) Reset() { *x = ServiceListResponse{} mi := &file_machine_machine_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceListResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceListResponse) ProtoMessage() {} func (x *ServiceListResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceListResponse.ProtoReflect.Descriptor instead. func (*ServiceListResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{31} } func (x *ServiceListResponse) GetMessages() []*ServiceList { if x != nil { return x.Messages } return nil } type ServiceInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` Events *ServiceEvents `protobuf:"bytes,3,opt,name=events,proto3" json:"events,omitempty"` Health *ServiceHealth `protobuf:"bytes,4,opt,name=health,proto3" json:"health,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceInfo) Reset() { *x = ServiceInfo{} mi := &file_machine_machine_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceInfo) ProtoMessage() {} func (x *ServiceInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceInfo.ProtoReflect.Descriptor instead. func (*ServiceInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{32} } func (x *ServiceInfo) GetId() string { if x != nil { return x.Id } return "" } func (x *ServiceInfo) GetState() string { if x != nil { return x.State } return "" } func (x *ServiceInfo) GetEvents() *ServiceEvents { if x != nil { return x.Events } return nil } func (x *ServiceInfo) GetHealth() *ServiceHealth { if x != nil { return x.Health } return nil } type ServiceEvents struct { state protoimpl.MessageState `protogen:"open.v1"` Events []*ServiceEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceEvents) Reset() { *x = ServiceEvents{} mi := &file_machine_machine_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceEvents) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceEvents) ProtoMessage() {} func (x *ServiceEvents) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceEvents.ProtoReflect.Descriptor instead. func (*ServiceEvents) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{33} } func (x *ServiceEvents) GetEvents() []*ServiceEvent { if x != nil { return x.Events } return nil } type ServiceEvent struct { state protoimpl.MessageState `protogen:"open.v1"` Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` State string `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` Ts *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=ts,proto3" json:"ts,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceEvent) Reset() { *x = ServiceEvent{} mi := &file_machine_machine_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceEvent) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceEvent) ProtoMessage() {} func (x *ServiceEvent) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceEvent.ProtoReflect.Descriptor instead. func (*ServiceEvent) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{34} } func (x *ServiceEvent) GetMsg() string { if x != nil { return x.Msg } return "" } func (x *ServiceEvent) GetState() string { if x != nil { return x.State } return "" } func (x *ServiceEvent) GetTs() *timestamppb.Timestamp { if x != nil { return x.Ts } return nil } type ServiceHealth struct { state protoimpl.MessageState `protogen:"open.v1"` Unknown bool `protobuf:"varint,1,opt,name=unknown,proto3" json:"unknown,omitempty"` Healthy bool `protobuf:"varint,2,opt,name=healthy,proto3" json:"healthy,omitempty"` LastMessage string `protobuf:"bytes,3,opt,name=last_message,json=lastMessage,proto3" json:"last_message,omitempty"` LastChange *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=last_change,json=lastChange,proto3" json:"last_change,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceHealth) Reset() { *x = ServiceHealth{} mi := &file_machine_machine_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceHealth) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceHealth) ProtoMessage() {} func (x *ServiceHealth) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceHealth.ProtoReflect.Descriptor instead. func (*ServiceHealth) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{35} } func (x *ServiceHealth) GetUnknown() bool { if x != nil { return x.Unknown } return false } func (x *ServiceHealth) GetHealthy() bool { if x != nil { return x.Healthy } return false } func (x *ServiceHealth) GetLastMessage() string { if x != nil { return x.LastMessage } return "" } func (x *ServiceHealth) GetLastChange() *timestamppb.Timestamp { if x != nil { return x.LastChange } return nil } // rpc servicestart type ServiceStartRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStartRequest) Reset() { *x = ServiceStartRequest{} mi := &file_machine_machine_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStartRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStartRequest) ProtoMessage() {} func (x *ServiceStartRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStartRequest.ProtoReflect.Descriptor instead. func (*ServiceStartRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{36} } func (x *ServiceStartRequest) GetId() string { if x != nil { return x.Id } return "" } type ServiceStart struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Resp string `protobuf:"bytes,2,opt,name=resp,proto3" json:"resp,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStart) Reset() { *x = ServiceStart{} mi := &file_machine_machine_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStart) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStart) ProtoMessage() {} func (x *ServiceStart) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStart.ProtoReflect.Descriptor instead. func (*ServiceStart) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{37} } func (x *ServiceStart) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ServiceStart) GetResp() string { if x != nil { return x.Resp } return "" } type ServiceStartResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ServiceStart `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStartResponse) Reset() { *x = ServiceStartResponse{} mi := &file_machine_machine_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStartResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStartResponse) ProtoMessage() {} func (x *ServiceStartResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStartResponse.ProtoReflect.Descriptor instead. func (*ServiceStartResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{38} } func (x *ServiceStartResponse) GetMessages() []*ServiceStart { if x != nil { return x.Messages } return nil } type ServiceStopRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStopRequest) Reset() { *x = ServiceStopRequest{} mi := &file_machine_machine_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStopRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStopRequest) ProtoMessage() {} func (x *ServiceStopRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStopRequest.ProtoReflect.Descriptor instead. func (*ServiceStopRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{39} } func (x *ServiceStopRequest) GetId() string { if x != nil { return x.Id } return "" } type ServiceStop struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Resp string `protobuf:"bytes,2,opt,name=resp,proto3" json:"resp,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStop) Reset() { *x = ServiceStop{} mi := &file_machine_machine_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStop) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStop) ProtoMessage() {} func (x *ServiceStop) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStop.ProtoReflect.Descriptor instead. func (*ServiceStop) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{40} } func (x *ServiceStop) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ServiceStop) GetResp() string { if x != nil { return x.Resp } return "" } type ServiceStopResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ServiceStop `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceStopResponse) Reset() { *x = ServiceStopResponse{} mi := &file_machine_machine_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceStopResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceStopResponse) ProtoMessage() {} func (x *ServiceStopResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceStopResponse.ProtoReflect.Descriptor instead. func (*ServiceStopResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{41} } func (x *ServiceStopResponse) GetMessages() []*ServiceStop { if x != nil { return x.Messages } return nil } type ServiceRestartRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceRestartRequest) Reset() { *x = ServiceRestartRequest{} mi := &file_machine_machine_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceRestartRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceRestartRequest) ProtoMessage() {} func (x *ServiceRestartRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceRestartRequest.ProtoReflect.Descriptor instead. func (*ServiceRestartRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{42} } func (x *ServiceRestartRequest) GetId() string { if x != nil { return x.Id } return "" } type ServiceRestart struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Resp string `protobuf:"bytes,2,opt,name=resp,proto3" json:"resp,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceRestart) Reset() { *x = ServiceRestart{} mi := &file_machine_machine_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceRestart) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceRestart) ProtoMessage() {} func (x *ServiceRestart) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceRestart.ProtoReflect.Descriptor instead. func (*ServiceRestart) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{43} } func (x *ServiceRestart) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ServiceRestart) GetResp() string { if x != nil { return x.Resp } return "" } type ServiceRestartResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ServiceRestart `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceRestartResponse) Reset() { *x = ServiceRestartResponse{} mi := &file_machine_machine_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceRestartResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceRestartResponse) ProtoMessage() {} func (x *ServiceRestartResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceRestartResponse.ProtoReflect.Descriptor instead. func (*ServiceRestartResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{44} } func (x *ServiceRestartResponse) GetMessages() []*ServiceRestart { if x != nil { return x.Messages } return nil } // CopyRequest describes a request to copy data out of Talos node // // Copy produces .tar.gz archive which is streamed back to the caller type CopyRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Root path to start copying data out, it might be either a file or directory RootPath string `protobuf:"bytes,1,opt,name=root_path,json=rootPath,proto3" json:"root_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CopyRequest) Reset() { *x = CopyRequest{} mi := &file_machine_machine_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CopyRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CopyRequest) ProtoMessage() {} func (x *CopyRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CopyRequest.ProtoReflect.Descriptor instead. func (*CopyRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{45} } func (x *CopyRequest) GetRootPath() string { if x != nil { return x.RootPath } return "" } // ListRequest describes a request to list the contents of a directory. type ListRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Root indicates the root directory for the list. If not indicated, '/' is // presumed. Root string `protobuf:"bytes,1,opt,name=root,proto3" json:"root,omitempty"` // Recurse indicates that subdirectories should be recursed. Recurse bool `protobuf:"varint,2,opt,name=recurse,proto3" json:"recurse,omitempty"` // RecursionDepth indicates how many levels of subdirectories should be // recursed. The default (0) indicates that no limit should be enforced. RecursionDepth int32 `protobuf:"varint,3,opt,name=recursion_depth,json=recursionDepth,proto3" json:"recursion_depth,omitempty"` // Types indicates what file type should be returned. If not indicated, // all files will be returned. Types []ListRequest_Type `protobuf:"varint,4,rep,packed,name=types,proto3,enum=machine.ListRequest_Type" json:"types,omitempty"` // Report xattrs ReportXattrs bool `protobuf:"varint,5,opt,name=report_xattrs,json=reportXattrs,proto3" json:"report_xattrs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ListRequest) Reset() { *x = ListRequest{} mi := &file_machine_machine_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ListRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ListRequest) ProtoMessage() {} func (x *ListRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. func (*ListRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{46} } func (x *ListRequest) GetRoot() string { if x != nil { return x.Root } return "" } func (x *ListRequest) GetRecurse() bool { if x != nil { return x.Recurse } return false } func (x *ListRequest) GetRecursionDepth() int32 { if x != nil { return x.RecursionDepth } return 0 } func (x *ListRequest) GetTypes() []ListRequest_Type { if x != nil { return x.Types } return nil } func (x *ListRequest) GetReportXattrs() bool { if x != nil { return x.ReportXattrs } return false } // DiskUsageRequest describes a request to list disk usage of directories and regular files type DiskUsageRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // RecursionDepth indicates how many levels of subdirectories should be // recursed. The default (0) indicates that no limit should be enforced. RecursionDepth int32 `protobuf:"varint,1,opt,name=recursion_depth,json=recursionDepth,proto3" json:"recursion_depth,omitempty"` // All write sizes for all files, not just directories. All bool `protobuf:"varint,2,opt,name=all,proto3" json:"all,omitempty"` // Threshold exclude entries smaller than SIZE if positive, // or entries greater than SIZE if negative. Threshold int64 `protobuf:"varint,3,opt,name=threshold,proto3" json:"threshold,omitempty"` // DiskUsagePaths is the list of directories to calculate disk usage for. Paths []string `protobuf:"bytes,4,rep,name=paths,proto3" json:"paths,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskUsageRequest) Reset() { *x = DiskUsageRequest{} mi := &file_machine_machine_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskUsageRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskUsageRequest) ProtoMessage() {} func (x *DiskUsageRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskUsageRequest.ProtoReflect.Descriptor instead. func (*DiskUsageRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{47} } func (x *DiskUsageRequest) GetRecursionDepth() int32 { if x != nil { return x.RecursionDepth } return 0 } func (x *DiskUsageRequest) GetAll() bool { if x != nil { return x.All } return false } func (x *DiskUsageRequest) GetThreshold() int64 { if x != nil { return x.Threshold } return 0 } func (x *DiskUsageRequest) GetPaths() []string { if x != nil { return x.Paths } return nil } // FileInfo describes a file or directory's information type FileInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // Name is the name (including prefixed path) of the file or directory Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Size indicates the number of bytes contained within the file Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` // Mode is the bitmap of UNIX mode/permission flags of the file Mode uint32 `protobuf:"varint,4,opt,name=mode,proto3" json:"mode,omitempty"` // Modified indicates the UNIX timestamp at which the file was last modified Modified int64 `protobuf:"varint,5,opt,name=modified,proto3" json:"modified,omitempty"` // IsDir indicates that the file is a directory IsDir bool `protobuf:"varint,6,opt,name=is_dir,json=isDir,proto3" json:"is_dir,omitempty"` // Error describes any error encountered while trying to read the file // information. Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` // Link is filled with symlink target Link string `protobuf:"bytes,8,opt,name=link,proto3" json:"link,omitempty"` // RelativeName is the name of the file or directory relative to the RootPath RelativeName string `protobuf:"bytes,9,opt,name=relative_name,json=relativeName,proto3" json:"relative_name,omitempty"` // Owner uid Uid uint32 `protobuf:"varint,10,opt,name=uid,proto3" json:"uid,omitempty"` // Owner gid Gid uint32 `protobuf:"varint,11,opt,name=gid,proto3" json:"gid,omitempty"` // Extended attributes (if present and requested) Xattrs []*Xattr `protobuf:"bytes,12,rep,name=xattrs,proto3" json:"xattrs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FileInfo) Reset() { *x = FileInfo{} mi := &file_machine_machine_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FileInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*FileInfo) ProtoMessage() {} func (x *FileInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FileInfo.ProtoReflect.Descriptor instead. func (*FileInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{48} } func (x *FileInfo) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *FileInfo) GetName() string { if x != nil { return x.Name } return "" } func (x *FileInfo) GetSize() int64 { if x != nil { return x.Size } return 0 } func (x *FileInfo) GetMode() uint32 { if x != nil { return x.Mode } return 0 } func (x *FileInfo) GetModified() int64 { if x != nil { return x.Modified } return 0 } func (x *FileInfo) GetIsDir() bool { if x != nil { return x.IsDir } return false } func (x *FileInfo) GetError() string { if x != nil { return x.Error } return "" } func (x *FileInfo) GetLink() string { if x != nil { return x.Link } return "" } func (x *FileInfo) GetRelativeName() string { if x != nil { return x.RelativeName } return "" } func (x *FileInfo) GetUid() uint32 { if x != nil { return x.Uid } return 0 } func (x *FileInfo) GetGid() uint32 { if x != nil { return x.Gid } return 0 } func (x *FileInfo) GetXattrs() []*Xattr { if x != nil { return x.Xattrs } return nil } type Xattr struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Xattr) Reset() { *x = Xattr{} mi := &file_machine_machine_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Xattr) String() string { return protoimpl.X.MessageStringOf(x) } func (*Xattr) ProtoMessage() {} func (x *Xattr) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Xattr.ProtoReflect.Descriptor instead. func (*Xattr) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{49} } func (x *Xattr) GetName() string { if x != nil { return x.Name } return "" } func (x *Xattr) GetData() []byte { if x != nil { return x.Data } return nil } // DiskUsageInfo describes a file or directory's information for du command type DiskUsageInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // Name is the name (including prefixed path) of the file or directory Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Size indicates the number of bytes contained within the file Size int64 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` // Error describes any error encountered while trying to read the file // information. Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` // RelativeName is the name of the file or directory relative to the RootPath RelativeName string `protobuf:"bytes,5,opt,name=relative_name,json=relativeName,proto3" json:"relative_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskUsageInfo) Reset() { *x = DiskUsageInfo{} mi := &file_machine_machine_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskUsageInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskUsageInfo) ProtoMessage() {} func (x *DiskUsageInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskUsageInfo.ProtoReflect.Descriptor instead. func (*DiskUsageInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{50} } func (x *DiskUsageInfo) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *DiskUsageInfo) GetName() string { if x != nil { return x.Name } return "" } func (x *DiskUsageInfo) GetSize() int64 { if x != nil { return x.Size } return 0 } func (x *DiskUsageInfo) GetError() string { if x != nil { return x.Error } return "" } func (x *DiskUsageInfo) GetRelativeName() string { if x != nil { return x.RelativeName } return "" } // The messages message containing the requested df stats. type Mounts struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Stats []*MountStat `protobuf:"bytes,2,rep,name=stats,proto3" json:"stats,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Mounts) Reset() { *x = Mounts{} mi := &file_machine_machine_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Mounts) String() string { return protoimpl.X.MessageStringOf(x) } func (*Mounts) ProtoMessage() {} func (x *Mounts) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Mounts.ProtoReflect.Descriptor instead. func (*Mounts) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{51} } func (x *Mounts) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Mounts) GetStats() []*MountStat { if x != nil { return x.Stats } return nil } type MountsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Mounts `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountsResponse) Reset() { *x = MountsResponse{} mi := &file_machine_machine_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountsResponse) ProtoMessage() {} func (x *MountsResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountsResponse.ProtoReflect.Descriptor instead. func (*MountsResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{52} } func (x *MountsResponse) GetMessages() []*Mounts { if x != nil { return x.Messages } return nil } // The messages message containing the requested processes. type MountStat struct { state protoimpl.MessageState `protogen:"open.v1"` Filesystem string `protobuf:"bytes,1,opt,name=filesystem,proto3" json:"filesystem,omitempty"` Size uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` Available uint64 `protobuf:"varint,3,opt,name=available,proto3" json:"available,omitempty"` MountedOn string `protobuf:"bytes,4,opt,name=mounted_on,json=mountedOn,proto3" json:"mounted_on,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountStat) Reset() { *x = MountStat{} mi := &file_machine_machine_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountStat) ProtoMessage() {} func (x *MountStat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountStat.ProtoReflect.Descriptor instead. func (*MountStat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{53} } func (x *MountStat) GetFilesystem() string { if x != nil { return x.Filesystem } return "" } func (x *MountStat) GetSize() uint64 { if x != nil { return x.Size } return 0 } func (x *MountStat) GetAvailable() uint64 { if x != nil { return x.Available } return 0 } func (x *MountStat) GetMountedOn() string { if x != nil { return x.MountedOn } return "" } type Version struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Version *VersionInfo `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` Platform *PlatformInfo `protobuf:"bytes,3,opt,name=platform,proto3" json:"platform,omitempty"` // Features describe individual Talos features that can be switched on or off. Features *FeaturesInfo `protobuf:"bytes,4,opt,name=features,proto3" json:"features,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Version) Reset() { *x = Version{} mi := &file_machine_machine_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Version) String() string { return protoimpl.X.MessageStringOf(x) } func (*Version) ProtoMessage() {} func (x *Version) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Version.ProtoReflect.Descriptor instead. func (*Version) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{54} } func (x *Version) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Version) GetVersion() *VersionInfo { if x != nil { return x.Version } return nil } func (x *Version) GetPlatform() *PlatformInfo { if x != nil { return x.Platform } return nil } func (x *Version) GetFeatures() *FeaturesInfo { if x != nil { return x.Features } return nil } type VersionResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Version `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VersionResponse) Reset() { *x = VersionResponse{} mi := &file_machine_machine_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VersionResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*VersionResponse) ProtoMessage() {} func (x *VersionResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VersionResponse.ProtoReflect.Descriptor instead. func (*VersionResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{55} } func (x *VersionResponse) GetMessages() []*Version { if x != nil { return x.Messages } return nil } type VersionInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` Sha string `protobuf:"bytes,2,opt,name=sha,proto3" json:"sha,omitempty"` Built string `protobuf:"bytes,3,opt,name=built,proto3" json:"built,omitempty"` GoVersion string `protobuf:"bytes,4,opt,name=go_version,json=goVersion,proto3" json:"go_version,omitempty"` Os string `protobuf:"bytes,5,opt,name=os,proto3" json:"os,omitempty"` Arch string `protobuf:"bytes,6,opt,name=arch,proto3" json:"arch,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VersionInfo) Reset() { *x = VersionInfo{} mi := &file_machine_machine_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VersionInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*VersionInfo) ProtoMessage() {} func (x *VersionInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VersionInfo.ProtoReflect.Descriptor instead. func (*VersionInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{56} } func (x *VersionInfo) GetTag() string { if x != nil { return x.Tag } return "" } func (x *VersionInfo) GetSha() string { if x != nil { return x.Sha } return "" } func (x *VersionInfo) GetBuilt() string { if x != nil { return x.Built } return "" } func (x *VersionInfo) GetGoVersion() string { if x != nil { return x.GoVersion } return "" } func (x *VersionInfo) GetOs() string { if x != nil { return x.Os } return "" } func (x *VersionInfo) GetArch() string { if x != nil { return x.Arch } return "" } type PlatformInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Mode string `protobuf:"bytes,2,opt,name=mode,proto3" json:"mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PlatformInfo) Reset() { *x = PlatformInfo{} mi := &file_machine_machine_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PlatformInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*PlatformInfo) ProtoMessage() {} func (x *PlatformInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PlatformInfo.ProtoReflect.Descriptor instead. func (*PlatformInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{57} } func (x *PlatformInfo) GetName() string { if x != nil { return x.Name } return "" } func (x *PlatformInfo) GetMode() string { if x != nil { return x.Mode } return "" } // FeaturesInfo describes individual Talos features that can be switched on or off. type FeaturesInfo struct { state protoimpl.MessageState `protogen:"open.v1"` // RBAC is true if role-based access control is enabled. Rbac bool `protobuf:"varint,1,opt,name=rbac,proto3" json:"rbac,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FeaturesInfo) Reset() { *x = FeaturesInfo{} mi := &file_machine_machine_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FeaturesInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*FeaturesInfo) ProtoMessage() {} func (x *FeaturesInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FeaturesInfo.ProtoReflect.Descriptor instead. func (*FeaturesInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{58} } func (x *FeaturesInfo) GetRbac() bool { if x != nil { return x.Rbac } return false } // rpc logs // The request message containing the process name. type LogsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` // driver might be default "containerd" or "cri" Driver common.ContainerDriver `protobuf:"varint,3,opt,name=driver,proto3,enum=common.ContainerDriver" json:"driver,omitempty"` Follow bool `protobuf:"varint,4,opt,name=follow,proto3" json:"follow,omitempty"` TailLines int32 `protobuf:"varint,5,opt,name=tail_lines,json=tailLines,proto3" json:"tail_lines,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LogsRequest) Reset() { *x = LogsRequest{} mi := &file_machine_machine_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LogsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogsRequest) ProtoMessage() {} func (x *LogsRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogsRequest.ProtoReflect.Descriptor instead. func (*LogsRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{59} } func (x *LogsRequest) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *LogsRequest) GetId() string { if x != nil { return x.Id } return "" } func (x *LogsRequest) GetDriver() common.ContainerDriver { if x != nil { return x.Driver } return common.ContainerDriver(0) } func (x *LogsRequest) GetFollow() bool { if x != nil { return x.Follow } return false } func (x *LogsRequest) GetTailLines() int32 { if x != nil { return x.TailLines } return 0 } type ReadRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ReadRequest) Reset() { *x = ReadRequest{} mi := &file_machine_machine_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ReadRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ReadRequest) ProtoMessage() {} func (x *ReadRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ReadRequest.ProtoReflect.Descriptor instead. func (*ReadRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{60} } func (x *ReadRequest) GetPath() string { if x != nil { return x.Path } return "" } // LogsContainer describes all available registered log containers. type LogsContainer struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Ids []string `protobuf:"bytes,2,rep,name=ids,proto3" json:"ids,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LogsContainer) Reset() { *x = LogsContainer{} mi := &file_machine_machine_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LogsContainer) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogsContainer) ProtoMessage() {} func (x *LogsContainer) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogsContainer.ProtoReflect.Descriptor instead. func (*LogsContainer) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{61} } func (x *LogsContainer) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *LogsContainer) GetIds() []string { if x != nil { return x.Ids } return nil } type LogsContainersResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*LogsContainer `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LogsContainersResponse) Reset() { *x = LogsContainersResponse{} mi := &file_machine_machine_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LogsContainersResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*LogsContainersResponse) ProtoMessage() {} func (x *LogsContainersResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LogsContainersResponse.ProtoReflect.Descriptor instead. func (*LogsContainersResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{62} } func (x *LogsContainersResponse) GetMessages() []*LogsContainer { if x != nil { return x.Messages } return nil } // rpc rollback type RollbackRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RollbackRequest) Reset() { *x = RollbackRequest{} mi := &file_machine_machine_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RollbackRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RollbackRequest) ProtoMessage() {} func (x *RollbackRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RollbackRequest.ProtoReflect.Descriptor instead. func (*RollbackRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{63} } type Rollback struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Rollback) Reset() { *x = Rollback{} mi := &file_machine_machine_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Rollback) String() string { return protoimpl.X.MessageStringOf(x) } func (*Rollback) ProtoMessage() {} func (x *Rollback) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Rollback.ProtoReflect.Descriptor instead. func (*Rollback) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{64} } func (x *Rollback) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type RollbackResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Rollback `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RollbackResponse) Reset() { *x = RollbackResponse{} mi := &file_machine_machine_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RollbackResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RollbackResponse) ProtoMessage() {} func (x *RollbackResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RollbackResponse.ProtoReflect.Descriptor instead. func (*RollbackResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{65} } func (x *RollbackResponse) GetMessages() []*Rollback { if x != nil { return x.Messages } return nil } type ContainersRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` // driver might be default "containerd" or "cri" Driver common.ContainerDriver `protobuf:"varint,2,opt,name=driver,proto3,enum=common.ContainerDriver" json:"driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ContainersRequest) Reset() { *x = ContainersRequest{} mi := &file_machine_machine_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ContainersRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContainersRequest) ProtoMessage() {} func (x *ContainersRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContainersRequest.ProtoReflect.Descriptor instead. func (*ContainersRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{66} } func (x *ContainersRequest) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *ContainersRequest) GetDriver() common.ContainerDriver { if x != nil { return x.Driver } return common.ContainerDriver(0) } // The messages message containing the requested containers. type ContainerInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` Uid string `protobuf:"bytes,10,opt,name=uid,proto3" json:"uid,omitempty"` InternalId string `protobuf:"bytes,9,opt,name=internal_id,json=internalId,proto3" json:"internal_id,omitempty"` Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` Pid uint32 `protobuf:"varint,4,opt,name=pid,proto3" json:"pid,omitempty"` Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` PodId string `protobuf:"bytes,6,opt,name=pod_id,json=podId,proto3" json:"pod_id,omitempty"` Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` NetworkNamespace string `protobuf:"bytes,8,opt,name=network_namespace,json=networkNamespace,proto3" json:"network_namespace,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ContainerInfo) Reset() { *x = ContainerInfo{} mi := &file_machine_machine_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ContainerInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContainerInfo) ProtoMessage() {} func (x *ContainerInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContainerInfo.ProtoReflect.Descriptor instead. func (*ContainerInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{67} } func (x *ContainerInfo) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *ContainerInfo) GetId() string { if x != nil { return x.Id } return "" } func (x *ContainerInfo) GetUid() string { if x != nil { return x.Uid } return "" } func (x *ContainerInfo) GetInternalId() string { if x != nil { return x.InternalId } return "" } func (x *ContainerInfo) GetImage() string { if x != nil { return x.Image } return "" } func (x *ContainerInfo) GetPid() uint32 { if x != nil { return x.Pid } return 0 } func (x *ContainerInfo) GetStatus() string { if x != nil { return x.Status } return "" } func (x *ContainerInfo) GetPodId() string { if x != nil { return x.PodId } return "" } func (x *ContainerInfo) GetName() string { if x != nil { return x.Name } return "" } func (x *ContainerInfo) GetNetworkNamespace() string { if x != nil { return x.NetworkNamespace } return "" } // The messages message containing the requested containers. type Container struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Containers []*ContainerInfo `protobuf:"bytes,2,rep,name=containers,proto3" json:"containers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Container) Reset() { *x = Container{} mi := &file_machine_machine_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Container) String() string { return protoimpl.X.MessageStringOf(x) } func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{68} } func (x *Container) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Container) GetContainers() []*ContainerInfo { if x != nil { return x.Containers } return nil } type ContainersResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Container `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ContainersResponse) Reset() { *x = ContainersResponse{} mi := &file_machine_machine_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ContainersResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ContainersResponse) ProtoMessage() {} func (x *ContainersResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ContainersResponse.ProtoReflect.Descriptor instead. func (*ContainersResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{69} } func (x *ContainersResponse) GetMessages() []*Container { if x != nil { return x.Messages } return nil } // dmesg type DmesgRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Follow bool `protobuf:"varint,1,opt,name=follow,proto3" json:"follow,omitempty"` Tail bool `protobuf:"varint,2,opt,name=tail,proto3" json:"tail,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DmesgRequest) Reset() { *x = DmesgRequest{} mi := &file_machine_machine_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DmesgRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*DmesgRequest) ProtoMessage() {} func (x *DmesgRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DmesgRequest.ProtoReflect.Descriptor instead. func (*DmesgRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{70} } func (x *DmesgRequest) GetFollow() bool { if x != nil { return x.Follow } return false } func (x *DmesgRequest) GetTail() bool { if x != nil { return x.Tail } return false } // rpc processes type ProcessesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Process `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProcessesResponse) Reset() { *x = ProcessesResponse{} mi := &file_machine_machine_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProcessesResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProcessesResponse) ProtoMessage() {} func (x *ProcessesResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProcessesResponse.ProtoReflect.Descriptor instead. func (*ProcessesResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{71} } func (x *ProcessesResponse) GetMessages() []*Process { if x != nil { return x.Messages } return nil } type Process struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Processes []*ProcessInfo `protobuf:"bytes,2,rep,name=processes,proto3" json:"processes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Process) Reset() { *x = Process{} mi := &file_machine_machine_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Process) String() string { return protoimpl.X.MessageStringOf(x) } func (*Process) ProtoMessage() {} func (x *Process) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Process.ProtoReflect.Descriptor instead. func (*Process) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{72} } func (x *Process) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Process) GetProcesses() []*ProcessInfo { if x != nil { return x.Processes } return nil } type ProcessInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Pid int32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` Ppid int32 `protobuf:"varint,2,opt,name=ppid,proto3" json:"ppid,omitempty"` State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` Threads int32 `protobuf:"varint,4,opt,name=threads,proto3" json:"threads,omitempty"` CpuTime float64 `protobuf:"fixed64,5,opt,name=cpu_time,json=cpuTime,proto3" json:"cpu_time,omitempty"` VirtualMemory uint64 `protobuf:"varint,6,opt,name=virtual_memory,json=virtualMemory,proto3" json:"virtual_memory,omitempty"` ResidentMemory uint64 `protobuf:"varint,7,opt,name=resident_memory,json=residentMemory,proto3" json:"resident_memory,omitempty"` Command string `protobuf:"bytes,8,opt,name=command,proto3" json:"command,omitempty"` Executable string `protobuf:"bytes,9,opt,name=executable,proto3" json:"executable,omitempty"` Args string `protobuf:"bytes,10,opt,name=args,proto3" json:"args,omitempty"` Label string `protobuf:"bytes,11,opt,name=label,proto3" json:"label,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProcessInfo) Reset() { *x = ProcessInfo{} mi := &file_machine_machine_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProcessInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProcessInfo) ProtoMessage() {} func (x *ProcessInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead. func (*ProcessInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{73} } func (x *ProcessInfo) GetPid() int32 { if x != nil { return x.Pid } return 0 } func (x *ProcessInfo) GetPpid() int32 { if x != nil { return x.Ppid } return 0 } func (x *ProcessInfo) GetState() string { if x != nil { return x.State } return "" } func (x *ProcessInfo) GetThreads() int32 { if x != nil { return x.Threads } return 0 } func (x *ProcessInfo) GetCpuTime() float64 { if x != nil { return x.CpuTime } return 0 } func (x *ProcessInfo) GetVirtualMemory() uint64 { if x != nil { return x.VirtualMemory } return 0 } func (x *ProcessInfo) GetResidentMemory() uint64 { if x != nil { return x.ResidentMemory } return 0 } func (x *ProcessInfo) GetCommand() string { if x != nil { return x.Command } return "" } func (x *ProcessInfo) GetExecutable() string { if x != nil { return x.Executable } return "" } func (x *ProcessInfo) GetArgs() string { if x != nil { return x.Args } return "" } func (x *ProcessInfo) GetLabel() string { if x != nil { return x.Label } return "" } // rpc restart // The request message containing the process to restart. type RestartRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` // driver might be default "containerd" or "cri" Driver common.ContainerDriver `protobuf:"varint,3,opt,name=driver,proto3,enum=common.ContainerDriver" json:"driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestartRequest) Reset() { *x = RestartRequest{} mi := &file_machine_machine_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RestartRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*RestartRequest) ProtoMessage() {} func (x *RestartRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RestartRequest.ProtoReflect.Descriptor instead. func (*RestartRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{74} } func (x *RestartRequest) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *RestartRequest) GetId() string { if x != nil { return x.Id } return "" } func (x *RestartRequest) GetDriver() common.ContainerDriver { if x != nil { return x.Driver } return common.ContainerDriver(0) } type Restart struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Restart) Reset() { *x = Restart{} mi := &file_machine_machine_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Restart) String() string { return protoimpl.X.MessageStringOf(x) } func (*Restart) ProtoMessage() {} func (x *Restart) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Restart.ProtoReflect.Descriptor instead. func (*Restart) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{75} } func (x *Restart) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } // The messages message containing the restart status. type RestartResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Restart `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RestartResponse) Reset() { *x = RestartResponse{} mi := &file_machine_machine_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RestartResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*RestartResponse) ProtoMessage() {} func (x *RestartResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RestartResponse.ProtoReflect.Descriptor instead. func (*RestartResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{76} } func (x *RestartResponse) GetMessages() []*Restart { if x != nil { return x.Messages } return nil } // The request message containing the containerd namespace. type StatsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` // driver might be default "containerd" or "cri" Driver common.ContainerDriver `protobuf:"varint,2,opt,name=driver,proto3,enum=common.ContainerDriver" json:"driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StatsRequest) Reset() { *x = StatsRequest{} mi := &file_machine_machine_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StatsRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*StatsRequest) ProtoMessage() {} func (x *StatsRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StatsRequest.ProtoReflect.Descriptor instead. func (*StatsRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{77} } func (x *StatsRequest) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *StatsRequest) GetDriver() common.ContainerDriver { if x != nil { return x.Driver } return common.ContainerDriver(0) } // The messages message containing the requested stats. type Stats struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Stats []*Stat `protobuf:"bytes,2,rep,name=stats,proto3" json:"stats,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Stats) Reset() { *x = Stats{} mi := &file_machine_machine_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Stats) String() string { return protoimpl.X.MessageStringOf(x) } func (*Stats) ProtoMessage() {} func (x *Stats) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Stats.ProtoReflect.Descriptor instead. func (*Stats) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{78} } func (x *Stats) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Stats) GetStats() []*Stat { if x != nil { return x.Stats } return nil } type StatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Stats `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StatsResponse) Reset() { *x = StatsResponse{} mi := &file_machine_machine_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*StatsResponse) ProtoMessage() {} func (x *StatsResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StatsResponse.ProtoReflect.Descriptor instead. func (*StatsResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{79} } func (x *StatsResponse) GetMessages() []*Stats { if x != nil { return x.Messages } return nil } // The messages message containing the requested stat. type Stat struct { state protoimpl.MessageState `protogen:"open.v1"` Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` MemoryUsage uint64 `protobuf:"varint,4,opt,name=memory_usage,json=memoryUsage,proto3" json:"memory_usage,omitempty"` CpuUsage uint64 `protobuf:"varint,5,opt,name=cpu_usage,json=cpuUsage,proto3" json:"cpu_usage,omitempty"` PodId string `protobuf:"bytes,6,opt,name=pod_id,json=podId,proto3" json:"pod_id,omitempty"` Name string `protobuf:"bytes,7,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Stat) Reset() { *x = Stat{} mi := &file_machine_machine_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Stat) String() string { return protoimpl.X.MessageStringOf(x) } func (*Stat) ProtoMessage() {} func (x *Stat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Stat.ProtoReflect.Descriptor instead. func (*Stat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{80} } func (x *Stat) GetNamespace() string { if x != nil { return x.Namespace } return "" } func (x *Stat) GetId() string { if x != nil { return x.Id } return "" } func (x *Stat) GetMemoryUsage() uint64 { if x != nil { return x.MemoryUsage } return 0 } func (x *Stat) GetCpuUsage() uint64 { if x != nil { return x.CpuUsage } return 0 } func (x *Stat) GetPodId() string { if x != nil { return x.PodId } return "" } func (x *Stat) GetName() string { if x != nil { return x.Name } return "" } type Memory struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Meminfo *MemInfo `protobuf:"bytes,2,opt,name=meminfo,proto3" json:"meminfo,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Memory) Reset() { *x = Memory{} mi := &file_machine_machine_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Memory) String() string { return protoimpl.X.MessageStringOf(x) } func (*Memory) ProtoMessage() {} func (x *Memory) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Memory.ProtoReflect.Descriptor instead. func (*Memory) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{81} } func (x *Memory) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Memory) GetMeminfo() *MemInfo { if x != nil { return x.Meminfo } return nil } type MemoryResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Memory `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemoryResponse) Reset() { *x = MemoryResponse{} mi := &file_machine_machine_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemoryResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemoryResponse) ProtoMessage() {} func (x *MemoryResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemoryResponse.ProtoReflect.Descriptor instead. func (*MemoryResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{82} } func (x *MemoryResponse) GetMessages() []*Memory { if x != nil { return x.Messages } return nil } type MemInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Memtotal uint64 `protobuf:"varint,1,opt,name=memtotal,proto3" json:"memtotal,omitempty"` Memfree uint64 `protobuf:"varint,2,opt,name=memfree,proto3" json:"memfree,omitempty"` Memavailable uint64 `protobuf:"varint,3,opt,name=memavailable,proto3" json:"memavailable,omitempty"` Buffers uint64 `protobuf:"varint,4,opt,name=buffers,proto3" json:"buffers,omitempty"` Cached uint64 `protobuf:"varint,5,opt,name=cached,proto3" json:"cached,omitempty"` Swapcached uint64 `protobuf:"varint,6,opt,name=swapcached,proto3" json:"swapcached,omitempty"` Active uint64 `protobuf:"varint,7,opt,name=active,proto3" json:"active,omitempty"` Inactive uint64 `protobuf:"varint,8,opt,name=inactive,proto3" json:"inactive,omitempty"` Activeanon uint64 `protobuf:"varint,9,opt,name=activeanon,proto3" json:"activeanon,omitempty"` Inactiveanon uint64 `protobuf:"varint,10,opt,name=inactiveanon,proto3" json:"inactiveanon,omitempty"` Activefile uint64 `protobuf:"varint,11,opt,name=activefile,proto3" json:"activefile,omitempty"` Inactivefile uint64 `protobuf:"varint,12,opt,name=inactivefile,proto3" json:"inactivefile,omitempty"` Unevictable uint64 `protobuf:"varint,13,opt,name=unevictable,proto3" json:"unevictable,omitempty"` Mlocked uint64 `protobuf:"varint,14,opt,name=mlocked,proto3" json:"mlocked,omitempty"` Swaptotal uint64 `protobuf:"varint,15,opt,name=swaptotal,proto3" json:"swaptotal,omitempty"` Swapfree uint64 `protobuf:"varint,16,opt,name=swapfree,proto3" json:"swapfree,omitempty"` Dirty uint64 `protobuf:"varint,17,opt,name=dirty,proto3" json:"dirty,omitempty"` Writeback uint64 `protobuf:"varint,18,opt,name=writeback,proto3" json:"writeback,omitempty"` Anonpages uint64 `protobuf:"varint,19,opt,name=anonpages,proto3" json:"anonpages,omitempty"` Mapped uint64 `protobuf:"varint,20,opt,name=mapped,proto3" json:"mapped,omitempty"` Shmem uint64 `protobuf:"varint,21,opt,name=shmem,proto3" json:"shmem,omitempty"` Slab uint64 `protobuf:"varint,22,opt,name=slab,proto3" json:"slab,omitempty"` Sreclaimable uint64 `protobuf:"varint,23,opt,name=sreclaimable,proto3" json:"sreclaimable,omitempty"` Sunreclaim uint64 `protobuf:"varint,24,opt,name=sunreclaim,proto3" json:"sunreclaim,omitempty"` Kernelstack uint64 `protobuf:"varint,25,opt,name=kernelstack,proto3" json:"kernelstack,omitempty"` Pagetables uint64 `protobuf:"varint,26,opt,name=pagetables,proto3" json:"pagetables,omitempty"` Nfsunstable uint64 `protobuf:"varint,27,opt,name=nfsunstable,proto3" json:"nfsunstable,omitempty"` Bounce uint64 `protobuf:"varint,28,opt,name=bounce,proto3" json:"bounce,omitempty"` Writebacktmp uint64 `protobuf:"varint,29,opt,name=writebacktmp,proto3" json:"writebacktmp,omitempty"` Commitlimit uint64 `protobuf:"varint,30,opt,name=commitlimit,proto3" json:"commitlimit,omitempty"` Committedas uint64 `protobuf:"varint,31,opt,name=committedas,proto3" json:"committedas,omitempty"` Vmalloctotal uint64 `protobuf:"varint,32,opt,name=vmalloctotal,proto3" json:"vmalloctotal,omitempty"` Vmallocused uint64 `protobuf:"varint,33,opt,name=vmallocused,proto3" json:"vmallocused,omitempty"` Vmallocchunk uint64 `protobuf:"varint,34,opt,name=vmallocchunk,proto3" json:"vmallocchunk,omitempty"` Hardwarecorrupted uint64 `protobuf:"varint,35,opt,name=hardwarecorrupted,proto3" json:"hardwarecorrupted,omitempty"` Anonhugepages uint64 `protobuf:"varint,36,opt,name=anonhugepages,proto3" json:"anonhugepages,omitempty"` Shmemhugepages uint64 `protobuf:"varint,37,opt,name=shmemhugepages,proto3" json:"shmemhugepages,omitempty"` Shmempmdmapped uint64 `protobuf:"varint,38,opt,name=shmempmdmapped,proto3" json:"shmempmdmapped,omitempty"` Cmatotal uint64 `protobuf:"varint,39,opt,name=cmatotal,proto3" json:"cmatotal,omitempty"` Cmafree uint64 `protobuf:"varint,40,opt,name=cmafree,proto3" json:"cmafree,omitempty"` Hugepagestotal uint64 `protobuf:"varint,41,opt,name=hugepagestotal,proto3" json:"hugepagestotal,omitempty"` Hugepagesfree uint64 `protobuf:"varint,42,opt,name=hugepagesfree,proto3" json:"hugepagesfree,omitempty"` Hugepagesrsvd uint64 `protobuf:"varint,43,opt,name=hugepagesrsvd,proto3" json:"hugepagesrsvd,omitempty"` Hugepagessurp uint64 `protobuf:"varint,44,opt,name=hugepagessurp,proto3" json:"hugepagessurp,omitempty"` Hugepagesize uint64 `protobuf:"varint,45,opt,name=hugepagesize,proto3" json:"hugepagesize,omitempty"` Directmap4K uint64 `protobuf:"varint,46,opt,name=directmap4k,proto3" json:"directmap4k,omitempty"` Directmap2M uint64 `protobuf:"varint,47,opt,name=directmap2m,proto3" json:"directmap2m,omitempty"` Directmap1G uint64 `protobuf:"varint,48,opt,name=directmap1g,proto3" json:"directmap1g,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemInfo) Reset() { *x = MemInfo{} mi := &file_machine_machine_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemInfo) ProtoMessage() {} func (x *MemInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemInfo.ProtoReflect.Descriptor instead. func (*MemInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{83} } func (x *MemInfo) GetMemtotal() uint64 { if x != nil { return x.Memtotal } return 0 } func (x *MemInfo) GetMemfree() uint64 { if x != nil { return x.Memfree } return 0 } func (x *MemInfo) GetMemavailable() uint64 { if x != nil { return x.Memavailable } return 0 } func (x *MemInfo) GetBuffers() uint64 { if x != nil { return x.Buffers } return 0 } func (x *MemInfo) GetCached() uint64 { if x != nil { return x.Cached } return 0 } func (x *MemInfo) GetSwapcached() uint64 { if x != nil { return x.Swapcached } return 0 } func (x *MemInfo) GetActive() uint64 { if x != nil { return x.Active } return 0 } func (x *MemInfo) GetInactive() uint64 { if x != nil { return x.Inactive } return 0 } func (x *MemInfo) GetActiveanon() uint64 { if x != nil { return x.Activeanon } return 0 } func (x *MemInfo) GetInactiveanon() uint64 { if x != nil { return x.Inactiveanon } return 0 } func (x *MemInfo) GetActivefile() uint64 { if x != nil { return x.Activefile } return 0 } func (x *MemInfo) GetInactivefile() uint64 { if x != nil { return x.Inactivefile } return 0 } func (x *MemInfo) GetUnevictable() uint64 { if x != nil { return x.Unevictable } return 0 } func (x *MemInfo) GetMlocked() uint64 { if x != nil { return x.Mlocked } return 0 } func (x *MemInfo) GetSwaptotal() uint64 { if x != nil { return x.Swaptotal } return 0 } func (x *MemInfo) GetSwapfree() uint64 { if x != nil { return x.Swapfree } return 0 } func (x *MemInfo) GetDirty() uint64 { if x != nil { return x.Dirty } return 0 } func (x *MemInfo) GetWriteback() uint64 { if x != nil { return x.Writeback } return 0 } func (x *MemInfo) GetAnonpages() uint64 { if x != nil { return x.Anonpages } return 0 } func (x *MemInfo) GetMapped() uint64 { if x != nil { return x.Mapped } return 0 } func (x *MemInfo) GetShmem() uint64 { if x != nil { return x.Shmem } return 0 } func (x *MemInfo) GetSlab() uint64 { if x != nil { return x.Slab } return 0 } func (x *MemInfo) GetSreclaimable() uint64 { if x != nil { return x.Sreclaimable } return 0 } func (x *MemInfo) GetSunreclaim() uint64 { if x != nil { return x.Sunreclaim } return 0 } func (x *MemInfo) GetKernelstack() uint64 { if x != nil { return x.Kernelstack } return 0 } func (x *MemInfo) GetPagetables() uint64 { if x != nil { return x.Pagetables } return 0 } func (x *MemInfo) GetNfsunstable() uint64 { if x != nil { return x.Nfsunstable } return 0 } func (x *MemInfo) GetBounce() uint64 { if x != nil { return x.Bounce } return 0 } func (x *MemInfo) GetWritebacktmp() uint64 { if x != nil { return x.Writebacktmp } return 0 } func (x *MemInfo) GetCommitlimit() uint64 { if x != nil { return x.Commitlimit } return 0 } func (x *MemInfo) GetCommittedas() uint64 { if x != nil { return x.Committedas } return 0 } func (x *MemInfo) GetVmalloctotal() uint64 { if x != nil { return x.Vmalloctotal } return 0 } func (x *MemInfo) GetVmallocused() uint64 { if x != nil { return x.Vmallocused } return 0 } func (x *MemInfo) GetVmallocchunk() uint64 { if x != nil { return x.Vmallocchunk } return 0 } func (x *MemInfo) GetHardwarecorrupted() uint64 { if x != nil { return x.Hardwarecorrupted } return 0 } func (x *MemInfo) GetAnonhugepages() uint64 { if x != nil { return x.Anonhugepages } return 0 } func (x *MemInfo) GetShmemhugepages() uint64 { if x != nil { return x.Shmemhugepages } return 0 } func (x *MemInfo) GetShmempmdmapped() uint64 { if x != nil { return x.Shmempmdmapped } return 0 } func (x *MemInfo) GetCmatotal() uint64 { if x != nil { return x.Cmatotal } return 0 } func (x *MemInfo) GetCmafree() uint64 { if x != nil { return x.Cmafree } return 0 } func (x *MemInfo) GetHugepagestotal() uint64 { if x != nil { return x.Hugepagestotal } return 0 } func (x *MemInfo) GetHugepagesfree() uint64 { if x != nil { return x.Hugepagesfree } return 0 } func (x *MemInfo) GetHugepagesrsvd() uint64 { if x != nil { return x.Hugepagesrsvd } return 0 } func (x *MemInfo) GetHugepagessurp() uint64 { if x != nil { return x.Hugepagessurp } return 0 } func (x *MemInfo) GetHugepagesize() uint64 { if x != nil { return x.Hugepagesize } return 0 } func (x *MemInfo) GetDirectmap4K() uint64 { if x != nil { return x.Directmap4K } return 0 } func (x *MemInfo) GetDirectmap2M() uint64 { if x != nil { return x.Directmap2M } return 0 } func (x *MemInfo) GetDirectmap1G() uint64 { if x != nil { return x.Directmap1G } return 0 } type HostnameResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Hostname `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HostnameResponse) Reset() { *x = HostnameResponse{} mi := &file_machine_machine_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HostnameResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*HostnameResponse) ProtoMessage() {} func (x *HostnameResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HostnameResponse.ProtoReflect.Descriptor instead. func (*HostnameResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{84} } func (x *HostnameResponse) GetMessages() []*Hostname { if x != nil { return x.Messages } return nil } type Hostname struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Hostname) Reset() { *x = Hostname{} mi := &file_machine_machine_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Hostname) String() string { return protoimpl.X.MessageStringOf(x) } func (*Hostname) ProtoMessage() {} func (x *Hostname) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Hostname.ProtoReflect.Descriptor instead. func (*Hostname) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{85} } func (x *Hostname) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Hostname) GetHostname() string { if x != nil { return x.Hostname } return "" } type LoadAvgResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*LoadAvg `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LoadAvgResponse) Reset() { *x = LoadAvgResponse{} mi := &file_machine_machine_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LoadAvgResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*LoadAvgResponse) ProtoMessage() {} func (x *LoadAvgResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LoadAvgResponse.ProtoReflect.Descriptor instead. func (*LoadAvgResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{86} } func (x *LoadAvgResponse) GetMessages() []*LoadAvg { if x != nil { return x.Messages } return nil } type LoadAvg struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Load1 float64 `protobuf:"fixed64,2,opt,name=load1,proto3" json:"load1,omitempty"` Load5 float64 `protobuf:"fixed64,3,opt,name=load5,proto3" json:"load5,omitempty"` Load15 float64 `protobuf:"fixed64,4,opt,name=load15,proto3" json:"load15,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LoadAvg) Reset() { *x = LoadAvg{} mi := &file_machine_machine_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LoadAvg) String() string { return protoimpl.X.MessageStringOf(x) } func (*LoadAvg) ProtoMessage() {} func (x *LoadAvg) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LoadAvg.ProtoReflect.Descriptor instead. func (*LoadAvg) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{87} } func (x *LoadAvg) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *LoadAvg) GetLoad1() float64 { if x != nil { return x.Load1 } return 0 } func (x *LoadAvg) GetLoad5() float64 { if x != nil { return x.Load5 } return 0 } func (x *LoadAvg) GetLoad15() float64 { if x != nil { return x.Load15 } return 0 } type SystemStatResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*SystemStat `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemStatResponse) Reset() { *x = SystemStatResponse{} mi := &file_machine_machine_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemStatResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemStatResponse) ProtoMessage() {} func (x *SystemStatResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemStatResponse.ProtoReflect.Descriptor instead. func (*SystemStatResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{88} } func (x *SystemStatResponse) GetMessages() []*SystemStat { if x != nil { return x.Messages } return nil } type SystemStat struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` BootTime uint64 `protobuf:"varint,2,opt,name=boot_time,json=bootTime,proto3" json:"boot_time,omitempty"` CpuTotal *CPUStat `protobuf:"bytes,3,opt,name=cpu_total,json=cpuTotal,proto3" json:"cpu_total,omitempty"` Cpu []*CPUStat `protobuf:"bytes,4,rep,name=cpu,proto3" json:"cpu,omitempty"` IrqTotal uint64 `protobuf:"varint,5,opt,name=irq_total,json=irqTotal,proto3" json:"irq_total,omitempty"` Irq []uint64 `protobuf:"varint,6,rep,packed,name=irq,proto3" json:"irq,omitempty"` ContextSwitches uint64 `protobuf:"varint,7,opt,name=context_switches,json=contextSwitches,proto3" json:"context_switches,omitempty"` ProcessCreated uint64 `protobuf:"varint,8,opt,name=process_created,json=processCreated,proto3" json:"process_created,omitempty"` ProcessRunning uint64 `protobuf:"varint,9,opt,name=process_running,json=processRunning,proto3" json:"process_running,omitempty"` ProcessBlocked uint64 `protobuf:"varint,10,opt,name=process_blocked,json=processBlocked,proto3" json:"process_blocked,omitempty"` SoftIrqTotal uint64 `protobuf:"varint,11,opt,name=soft_irq_total,json=softIrqTotal,proto3" json:"soft_irq_total,omitempty"` SoftIrq *SoftIRQStat `protobuf:"bytes,12,opt,name=soft_irq,json=softIrq,proto3" json:"soft_irq,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemStat) Reset() { *x = SystemStat{} mi := &file_machine_machine_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemStat) ProtoMessage() {} func (x *SystemStat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemStat.ProtoReflect.Descriptor instead. func (*SystemStat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{89} } func (x *SystemStat) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *SystemStat) GetBootTime() uint64 { if x != nil { return x.BootTime } return 0 } func (x *SystemStat) GetCpuTotal() *CPUStat { if x != nil { return x.CpuTotal } return nil } func (x *SystemStat) GetCpu() []*CPUStat { if x != nil { return x.Cpu } return nil } func (x *SystemStat) GetIrqTotal() uint64 { if x != nil { return x.IrqTotal } return 0 } func (x *SystemStat) GetIrq() []uint64 { if x != nil { return x.Irq } return nil } func (x *SystemStat) GetContextSwitches() uint64 { if x != nil { return x.ContextSwitches } return 0 } func (x *SystemStat) GetProcessCreated() uint64 { if x != nil { return x.ProcessCreated } return 0 } func (x *SystemStat) GetProcessRunning() uint64 { if x != nil { return x.ProcessRunning } return 0 } func (x *SystemStat) GetProcessBlocked() uint64 { if x != nil { return x.ProcessBlocked } return 0 } func (x *SystemStat) GetSoftIrqTotal() uint64 { if x != nil { return x.SoftIrqTotal } return 0 } func (x *SystemStat) GetSoftIrq() *SoftIRQStat { if x != nil { return x.SoftIrq } return nil } type CPUStat struct { state protoimpl.MessageState `protogen:"open.v1"` User float64 `protobuf:"fixed64,1,opt,name=user,proto3" json:"user,omitempty"` Nice float64 `protobuf:"fixed64,2,opt,name=nice,proto3" json:"nice,omitempty"` System float64 `protobuf:"fixed64,3,opt,name=system,proto3" json:"system,omitempty"` Idle float64 `protobuf:"fixed64,4,opt,name=idle,proto3" json:"idle,omitempty"` Iowait float64 `protobuf:"fixed64,5,opt,name=iowait,proto3" json:"iowait,omitempty"` Irq float64 `protobuf:"fixed64,6,opt,name=irq,proto3" json:"irq,omitempty"` SoftIrq float64 `protobuf:"fixed64,7,opt,name=soft_irq,json=softIrq,proto3" json:"soft_irq,omitempty"` Steal float64 `protobuf:"fixed64,8,opt,name=steal,proto3" json:"steal,omitempty"` Guest float64 `protobuf:"fixed64,9,opt,name=guest,proto3" json:"guest,omitempty"` GuestNice float64 `protobuf:"fixed64,10,opt,name=guest_nice,json=guestNice,proto3" json:"guest_nice,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUStat) Reset() { *x = CPUStat{} mi := &file_machine_machine_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUStat) ProtoMessage() {} func (x *CPUStat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUStat.ProtoReflect.Descriptor instead. func (*CPUStat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{90} } func (x *CPUStat) GetUser() float64 { if x != nil { return x.User } return 0 } func (x *CPUStat) GetNice() float64 { if x != nil { return x.Nice } return 0 } func (x *CPUStat) GetSystem() float64 { if x != nil { return x.System } return 0 } func (x *CPUStat) GetIdle() float64 { if x != nil { return x.Idle } return 0 } func (x *CPUStat) GetIowait() float64 { if x != nil { return x.Iowait } return 0 } func (x *CPUStat) GetIrq() float64 { if x != nil { return x.Irq } return 0 } func (x *CPUStat) GetSoftIrq() float64 { if x != nil { return x.SoftIrq } return 0 } func (x *CPUStat) GetSteal() float64 { if x != nil { return x.Steal } return 0 } func (x *CPUStat) GetGuest() float64 { if x != nil { return x.Guest } return 0 } func (x *CPUStat) GetGuestNice() float64 { if x != nil { return x.GuestNice } return 0 } type SoftIRQStat struct { state protoimpl.MessageState `protogen:"open.v1"` Hi uint64 `protobuf:"varint,1,opt,name=hi,proto3" json:"hi,omitempty"` Timer uint64 `protobuf:"varint,2,opt,name=timer,proto3" json:"timer,omitempty"` NetTx uint64 `protobuf:"varint,3,opt,name=net_tx,json=netTx,proto3" json:"net_tx,omitempty"` NetRx uint64 `protobuf:"varint,4,opt,name=net_rx,json=netRx,proto3" json:"net_rx,omitempty"` Block uint64 `protobuf:"varint,5,opt,name=block,proto3" json:"block,omitempty"` BlockIoPoll uint64 `protobuf:"varint,6,opt,name=block_io_poll,json=blockIoPoll,proto3" json:"block_io_poll,omitempty"` Tasklet uint64 `protobuf:"varint,7,opt,name=tasklet,proto3" json:"tasklet,omitempty"` Sched uint64 `protobuf:"varint,8,opt,name=sched,proto3" json:"sched,omitempty"` Hrtimer uint64 `protobuf:"varint,9,opt,name=hrtimer,proto3" json:"hrtimer,omitempty"` Rcu uint64 `protobuf:"varint,10,opt,name=rcu,proto3" json:"rcu,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SoftIRQStat) Reset() { *x = SoftIRQStat{} mi := &file_machine_machine_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SoftIRQStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*SoftIRQStat) ProtoMessage() {} func (x *SoftIRQStat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SoftIRQStat.ProtoReflect.Descriptor instead. func (*SoftIRQStat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{91} } func (x *SoftIRQStat) GetHi() uint64 { if x != nil { return x.Hi } return 0 } func (x *SoftIRQStat) GetTimer() uint64 { if x != nil { return x.Timer } return 0 } func (x *SoftIRQStat) GetNetTx() uint64 { if x != nil { return x.NetTx } return 0 } func (x *SoftIRQStat) GetNetRx() uint64 { if x != nil { return x.NetRx } return 0 } func (x *SoftIRQStat) GetBlock() uint64 { if x != nil { return x.Block } return 0 } func (x *SoftIRQStat) GetBlockIoPoll() uint64 { if x != nil { return x.BlockIoPoll } return 0 } func (x *SoftIRQStat) GetTasklet() uint64 { if x != nil { return x.Tasklet } return 0 } func (x *SoftIRQStat) GetSched() uint64 { if x != nil { return x.Sched } return 0 } func (x *SoftIRQStat) GetHrtimer() uint64 { if x != nil { return x.Hrtimer } return 0 } func (x *SoftIRQStat) GetRcu() uint64 { if x != nil { return x.Rcu } return 0 } type CPUFreqStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*CPUsFreqStats `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUFreqStatsResponse) Reset() { *x = CPUFreqStatsResponse{} mi := &file_machine_machine_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUFreqStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUFreqStatsResponse) ProtoMessage() {} func (x *CPUFreqStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUFreqStatsResponse.ProtoReflect.Descriptor instead. func (*CPUFreqStatsResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{92} } func (x *CPUFreqStatsResponse) GetMessages() []*CPUsFreqStats { if x != nil { return x.Messages } return nil } type CPUsFreqStats struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` CpuFreqStats []*CPUFreqStats `protobuf:"bytes,2,rep,name=cpu_freq_stats,json=cpuFreqStats,proto3" json:"cpu_freq_stats,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUsFreqStats) Reset() { *x = CPUsFreqStats{} mi := &file_machine_machine_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUsFreqStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUsFreqStats) ProtoMessage() {} func (x *CPUsFreqStats) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUsFreqStats.ProtoReflect.Descriptor instead. func (*CPUsFreqStats) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{93} } func (x *CPUsFreqStats) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *CPUsFreqStats) GetCpuFreqStats() []*CPUFreqStats { if x != nil { return x.CpuFreqStats } return nil } type CPUFreqStats struct { state protoimpl.MessageState `protogen:"open.v1"` CurrentFrequency uint64 `protobuf:"varint,1,opt,name=current_frequency,json=currentFrequency,proto3" json:"current_frequency,omitempty"` MinimumFrequency uint64 `protobuf:"varint,2,opt,name=minimum_frequency,json=minimumFrequency,proto3" json:"minimum_frequency,omitempty"` MaximumFrequency uint64 `protobuf:"varint,3,opt,name=maximum_frequency,json=maximumFrequency,proto3" json:"maximum_frequency,omitempty"` Governor string `protobuf:"bytes,4,opt,name=governor,proto3" json:"governor,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUFreqStats) Reset() { *x = CPUFreqStats{} mi := &file_machine_machine_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUFreqStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUFreqStats) ProtoMessage() {} func (x *CPUFreqStats) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUFreqStats.ProtoReflect.Descriptor instead. func (*CPUFreqStats) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{94} } func (x *CPUFreqStats) GetCurrentFrequency() uint64 { if x != nil { return x.CurrentFrequency } return 0 } func (x *CPUFreqStats) GetMinimumFrequency() uint64 { if x != nil { return x.MinimumFrequency } return 0 } func (x *CPUFreqStats) GetMaximumFrequency() uint64 { if x != nil { return x.MaximumFrequency } return 0 } func (x *CPUFreqStats) GetGovernor() string { if x != nil { return x.Governor } return "" } type CPUInfoResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*CPUsInfo `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUInfoResponse) Reset() { *x = CPUInfoResponse{} mi := &file_machine_machine_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUInfoResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUInfoResponse) ProtoMessage() {} func (x *CPUInfoResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUInfoResponse.ProtoReflect.Descriptor instead. func (*CPUInfoResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{95} } func (x *CPUInfoResponse) GetMessages() []*CPUsInfo { if x != nil { return x.Messages } return nil } type CPUsInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` CpuInfo []*CPUInfo `protobuf:"bytes,2,rep,name=cpu_info,json=cpuInfo,proto3" json:"cpu_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUsInfo) Reset() { *x = CPUsInfo{} mi := &file_machine_machine_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUsInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUsInfo) ProtoMessage() {} func (x *CPUsInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUsInfo.ProtoReflect.Descriptor instead. func (*CPUsInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{96} } func (x *CPUsInfo) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *CPUsInfo) GetCpuInfo() []*CPUInfo { if x != nil { return x.CpuInfo } return nil } type CPUInfo struct { state protoimpl.MessageState `protogen:"open.v1"` Processor uint32 `protobuf:"varint,1,opt,name=processor,proto3" json:"processor,omitempty"` VendorId string `protobuf:"bytes,2,opt,name=vendor_id,json=vendorId,proto3" json:"vendor_id,omitempty"` CpuFamily string `protobuf:"bytes,3,opt,name=cpu_family,json=cpuFamily,proto3" json:"cpu_family,omitempty"` Model string `protobuf:"bytes,4,opt,name=model,proto3" json:"model,omitempty"` ModelName string `protobuf:"bytes,5,opt,name=model_name,json=modelName,proto3" json:"model_name,omitempty"` Stepping string `protobuf:"bytes,6,opt,name=stepping,proto3" json:"stepping,omitempty"` Microcode string `protobuf:"bytes,7,opt,name=microcode,proto3" json:"microcode,omitempty"` CpuMhz float64 `protobuf:"fixed64,8,opt,name=cpu_mhz,json=cpuMhz,proto3" json:"cpu_mhz,omitempty"` CacheSize string `protobuf:"bytes,9,opt,name=cache_size,json=cacheSize,proto3" json:"cache_size,omitempty"` PhysicalId string `protobuf:"bytes,10,opt,name=physical_id,json=physicalId,proto3" json:"physical_id,omitempty"` Siblings uint32 `protobuf:"varint,11,opt,name=siblings,proto3" json:"siblings,omitempty"` CoreId string `protobuf:"bytes,12,opt,name=core_id,json=coreId,proto3" json:"core_id,omitempty"` CpuCores uint32 `protobuf:"varint,13,opt,name=cpu_cores,json=cpuCores,proto3" json:"cpu_cores,omitempty"` ApicId string `protobuf:"bytes,14,opt,name=apic_id,json=apicId,proto3" json:"apic_id,omitempty"` InitialApicId string `protobuf:"bytes,15,opt,name=initial_apic_id,json=initialApicId,proto3" json:"initial_apic_id,omitempty"` Fpu string `protobuf:"bytes,16,opt,name=fpu,proto3" json:"fpu,omitempty"` FpuException string `protobuf:"bytes,17,opt,name=fpu_exception,json=fpuException,proto3" json:"fpu_exception,omitempty"` CpuIdLevel uint32 `protobuf:"varint,18,opt,name=cpu_id_level,json=cpuIdLevel,proto3" json:"cpu_id_level,omitempty"` Wp string `protobuf:"bytes,19,opt,name=wp,proto3" json:"wp,omitempty"` Flags []string `protobuf:"bytes,20,rep,name=flags,proto3" json:"flags,omitempty"` Bugs []string `protobuf:"bytes,21,rep,name=bugs,proto3" json:"bugs,omitempty"` BogoMips float64 `protobuf:"fixed64,22,opt,name=bogo_mips,json=bogoMips,proto3" json:"bogo_mips,omitempty"` ClFlushSize uint32 `protobuf:"varint,23,opt,name=cl_flush_size,json=clFlushSize,proto3" json:"cl_flush_size,omitempty"` CacheAlignment uint32 `protobuf:"varint,24,opt,name=cache_alignment,json=cacheAlignment,proto3" json:"cache_alignment,omitempty"` AddressSizes string `protobuf:"bytes,25,opt,name=address_sizes,json=addressSizes,proto3" json:"address_sizes,omitempty"` PowerManagement string `protobuf:"bytes,26,opt,name=power_management,json=powerManagement,proto3" json:"power_management,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUInfo) Reset() { *x = CPUInfo{} mi := &file_machine_machine_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUInfo) ProtoMessage() {} func (x *CPUInfo) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUInfo.ProtoReflect.Descriptor instead. func (*CPUInfo) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{97} } func (x *CPUInfo) GetProcessor() uint32 { if x != nil { return x.Processor } return 0 } func (x *CPUInfo) GetVendorId() string { if x != nil { return x.VendorId } return "" } func (x *CPUInfo) GetCpuFamily() string { if x != nil { return x.CpuFamily } return "" } func (x *CPUInfo) GetModel() string { if x != nil { return x.Model } return "" } func (x *CPUInfo) GetModelName() string { if x != nil { return x.ModelName } return "" } func (x *CPUInfo) GetStepping() string { if x != nil { return x.Stepping } return "" } func (x *CPUInfo) GetMicrocode() string { if x != nil { return x.Microcode } return "" } func (x *CPUInfo) GetCpuMhz() float64 { if x != nil { return x.CpuMhz } return 0 } func (x *CPUInfo) GetCacheSize() string { if x != nil { return x.CacheSize } return "" } func (x *CPUInfo) GetPhysicalId() string { if x != nil { return x.PhysicalId } return "" } func (x *CPUInfo) GetSiblings() uint32 { if x != nil { return x.Siblings } return 0 } func (x *CPUInfo) GetCoreId() string { if x != nil { return x.CoreId } return "" } func (x *CPUInfo) GetCpuCores() uint32 { if x != nil { return x.CpuCores } return 0 } func (x *CPUInfo) GetApicId() string { if x != nil { return x.ApicId } return "" } func (x *CPUInfo) GetInitialApicId() string { if x != nil { return x.InitialApicId } return "" } func (x *CPUInfo) GetFpu() string { if x != nil { return x.Fpu } return "" } func (x *CPUInfo) GetFpuException() string { if x != nil { return x.FpuException } return "" } func (x *CPUInfo) GetCpuIdLevel() uint32 { if x != nil { return x.CpuIdLevel } return 0 } func (x *CPUInfo) GetWp() string { if x != nil { return x.Wp } return "" } func (x *CPUInfo) GetFlags() []string { if x != nil { return x.Flags } return nil } func (x *CPUInfo) GetBugs() []string { if x != nil { return x.Bugs } return nil } func (x *CPUInfo) GetBogoMips() float64 { if x != nil { return x.BogoMips } return 0 } func (x *CPUInfo) GetClFlushSize() uint32 { if x != nil { return x.ClFlushSize } return 0 } func (x *CPUInfo) GetCacheAlignment() uint32 { if x != nil { return x.CacheAlignment } return 0 } func (x *CPUInfo) GetAddressSizes() string { if x != nil { return x.AddressSizes } return "" } func (x *CPUInfo) GetPowerManagement() string { if x != nil { return x.PowerManagement } return "" } type NetworkDeviceStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*NetworkDeviceStats `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetworkDeviceStatsResponse) Reset() { *x = NetworkDeviceStatsResponse{} mi := &file_machine_machine_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetworkDeviceStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetworkDeviceStatsResponse) ProtoMessage() {} func (x *NetworkDeviceStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetworkDeviceStatsResponse.ProtoReflect.Descriptor instead. func (*NetworkDeviceStatsResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{98} } func (x *NetworkDeviceStatsResponse) GetMessages() []*NetworkDeviceStats { if x != nil { return x.Messages } return nil } type NetworkDeviceStats struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Total *NetDev `protobuf:"bytes,2,opt,name=total,proto3" json:"total,omitempty"` Devices []*NetDev `protobuf:"bytes,3,rep,name=devices,proto3" json:"devices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetworkDeviceStats) Reset() { *x = NetworkDeviceStats{} mi := &file_machine_machine_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetworkDeviceStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetworkDeviceStats) ProtoMessage() {} func (x *NetworkDeviceStats) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetworkDeviceStats.ProtoReflect.Descriptor instead. func (*NetworkDeviceStats) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{99} } func (x *NetworkDeviceStats) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *NetworkDeviceStats) GetTotal() *NetDev { if x != nil { return x.Total } return nil } func (x *NetworkDeviceStats) GetDevices() []*NetDev { if x != nil { return x.Devices } return nil } type NetDev struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` RxBytes uint64 `protobuf:"varint,2,opt,name=rx_bytes,json=rxBytes,proto3" json:"rx_bytes,omitempty"` RxPackets uint64 `protobuf:"varint,3,opt,name=rx_packets,json=rxPackets,proto3" json:"rx_packets,omitempty"` RxErrors uint64 `protobuf:"varint,4,opt,name=rx_errors,json=rxErrors,proto3" json:"rx_errors,omitempty"` RxDropped uint64 `protobuf:"varint,5,opt,name=rx_dropped,json=rxDropped,proto3" json:"rx_dropped,omitempty"` RxFifo uint64 `protobuf:"varint,6,opt,name=rx_fifo,json=rxFifo,proto3" json:"rx_fifo,omitempty"` RxFrame uint64 `protobuf:"varint,7,opt,name=rx_frame,json=rxFrame,proto3" json:"rx_frame,omitempty"` RxCompressed uint64 `protobuf:"varint,8,opt,name=rx_compressed,json=rxCompressed,proto3" json:"rx_compressed,omitempty"` RxMulticast uint64 `protobuf:"varint,9,opt,name=rx_multicast,json=rxMulticast,proto3" json:"rx_multicast,omitempty"` TxBytes uint64 `protobuf:"varint,10,opt,name=tx_bytes,json=txBytes,proto3" json:"tx_bytes,omitempty"` TxPackets uint64 `protobuf:"varint,11,opt,name=tx_packets,json=txPackets,proto3" json:"tx_packets,omitempty"` TxErrors uint64 `protobuf:"varint,12,opt,name=tx_errors,json=txErrors,proto3" json:"tx_errors,omitempty"` TxDropped uint64 `protobuf:"varint,13,opt,name=tx_dropped,json=txDropped,proto3" json:"tx_dropped,omitempty"` TxFifo uint64 `protobuf:"varint,14,opt,name=tx_fifo,json=txFifo,proto3" json:"tx_fifo,omitempty"` TxCollisions uint64 `protobuf:"varint,15,opt,name=tx_collisions,json=txCollisions,proto3" json:"tx_collisions,omitempty"` TxCarrier uint64 `protobuf:"varint,16,opt,name=tx_carrier,json=txCarrier,proto3" json:"tx_carrier,omitempty"` TxCompressed uint64 `protobuf:"varint,17,opt,name=tx_compressed,json=txCompressed,proto3" json:"tx_compressed,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetDev) Reset() { *x = NetDev{} mi := &file_machine_machine_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetDev) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetDev) ProtoMessage() {} func (x *NetDev) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetDev.ProtoReflect.Descriptor instead. func (*NetDev) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{100} } func (x *NetDev) GetName() string { if x != nil { return x.Name } return "" } func (x *NetDev) GetRxBytes() uint64 { if x != nil { return x.RxBytes } return 0 } func (x *NetDev) GetRxPackets() uint64 { if x != nil { return x.RxPackets } return 0 } func (x *NetDev) GetRxErrors() uint64 { if x != nil { return x.RxErrors } return 0 } func (x *NetDev) GetRxDropped() uint64 { if x != nil { return x.RxDropped } return 0 } func (x *NetDev) GetRxFifo() uint64 { if x != nil { return x.RxFifo } return 0 } func (x *NetDev) GetRxFrame() uint64 { if x != nil { return x.RxFrame } return 0 } func (x *NetDev) GetRxCompressed() uint64 { if x != nil { return x.RxCompressed } return 0 } func (x *NetDev) GetRxMulticast() uint64 { if x != nil { return x.RxMulticast } return 0 } func (x *NetDev) GetTxBytes() uint64 { if x != nil { return x.TxBytes } return 0 } func (x *NetDev) GetTxPackets() uint64 { if x != nil { return x.TxPackets } return 0 } func (x *NetDev) GetTxErrors() uint64 { if x != nil { return x.TxErrors } return 0 } func (x *NetDev) GetTxDropped() uint64 { if x != nil { return x.TxDropped } return 0 } func (x *NetDev) GetTxFifo() uint64 { if x != nil { return x.TxFifo } return 0 } func (x *NetDev) GetTxCollisions() uint64 { if x != nil { return x.TxCollisions } return 0 } func (x *NetDev) GetTxCarrier() uint64 { if x != nil { return x.TxCarrier } return 0 } func (x *NetDev) GetTxCompressed() uint64 { if x != nil { return x.TxCompressed } return 0 } type DiskStatsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*DiskStats `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskStatsResponse) Reset() { *x = DiskStatsResponse{} mi := &file_machine_machine_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskStatsResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskStatsResponse) ProtoMessage() {} func (x *DiskStatsResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskStatsResponse.ProtoReflect.Descriptor instead. func (*DiskStatsResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{101} } func (x *DiskStatsResponse) GetMessages() []*DiskStats { if x != nil { return x.Messages } return nil } type DiskStats struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Total *DiskStat `protobuf:"bytes,2,opt,name=total,proto3" json:"total,omitempty"` Devices []*DiskStat `protobuf:"bytes,3,rep,name=devices,proto3" json:"devices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskStats) Reset() { *x = DiskStats{} mi := &file_machine_machine_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskStats) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskStats) ProtoMessage() {} func (x *DiskStats) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskStats.ProtoReflect.Descriptor instead. func (*DiskStats) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{102} } func (x *DiskStats) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *DiskStats) GetTotal() *DiskStat { if x != nil { return x.Total } return nil } func (x *DiskStats) GetDevices() []*DiskStat { if x != nil { return x.Devices } return nil } type DiskStat struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` ReadCompleted uint64 `protobuf:"varint,2,opt,name=read_completed,json=readCompleted,proto3" json:"read_completed,omitempty"` ReadMerged uint64 `protobuf:"varint,3,opt,name=read_merged,json=readMerged,proto3" json:"read_merged,omitempty"` ReadSectors uint64 `protobuf:"varint,4,opt,name=read_sectors,json=readSectors,proto3" json:"read_sectors,omitempty"` ReadTimeMs uint64 `protobuf:"varint,5,opt,name=read_time_ms,json=readTimeMs,proto3" json:"read_time_ms,omitempty"` WriteCompleted uint64 `protobuf:"varint,6,opt,name=write_completed,json=writeCompleted,proto3" json:"write_completed,omitempty"` WriteMerged uint64 `protobuf:"varint,7,opt,name=write_merged,json=writeMerged,proto3" json:"write_merged,omitempty"` WriteSectors uint64 `protobuf:"varint,8,opt,name=write_sectors,json=writeSectors,proto3" json:"write_sectors,omitempty"` WriteTimeMs uint64 `protobuf:"varint,9,opt,name=write_time_ms,json=writeTimeMs,proto3" json:"write_time_ms,omitempty"` IoInProgress uint64 `protobuf:"varint,10,opt,name=io_in_progress,json=ioInProgress,proto3" json:"io_in_progress,omitempty"` IoTimeMs uint64 `protobuf:"varint,11,opt,name=io_time_ms,json=ioTimeMs,proto3" json:"io_time_ms,omitempty"` IoTimeWeightedMs uint64 `protobuf:"varint,12,opt,name=io_time_weighted_ms,json=ioTimeWeightedMs,proto3" json:"io_time_weighted_ms,omitempty"` DiscardCompleted uint64 `protobuf:"varint,13,opt,name=discard_completed,json=discardCompleted,proto3" json:"discard_completed,omitempty"` DiscardMerged uint64 `protobuf:"varint,14,opt,name=discard_merged,json=discardMerged,proto3" json:"discard_merged,omitempty"` DiscardSectors uint64 `protobuf:"varint,15,opt,name=discard_sectors,json=discardSectors,proto3" json:"discard_sectors,omitempty"` DiscardTimeMs uint64 `protobuf:"varint,16,opt,name=discard_time_ms,json=discardTimeMs,proto3" json:"discard_time_ms,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskStat) Reset() { *x = DiskStat{} mi := &file_machine_machine_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskStat) ProtoMessage() {} func (x *DiskStat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskStat.ProtoReflect.Descriptor instead. func (*DiskStat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{103} } func (x *DiskStat) GetName() string { if x != nil { return x.Name } return "" } func (x *DiskStat) GetReadCompleted() uint64 { if x != nil { return x.ReadCompleted } return 0 } func (x *DiskStat) GetReadMerged() uint64 { if x != nil { return x.ReadMerged } return 0 } func (x *DiskStat) GetReadSectors() uint64 { if x != nil { return x.ReadSectors } return 0 } func (x *DiskStat) GetReadTimeMs() uint64 { if x != nil { return x.ReadTimeMs } return 0 } func (x *DiskStat) GetWriteCompleted() uint64 { if x != nil { return x.WriteCompleted } return 0 } func (x *DiskStat) GetWriteMerged() uint64 { if x != nil { return x.WriteMerged } return 0 } func (x *DiskStat) GetWriteSectors() uint64 { if x != nil { return x.WriteSectors } return 0 } func (x *DiskStat) GetWriteTimeMs() uint64 { if x != nil { return x.WriteTimeMs } return 0 } func (x *DiskStat) GetIoInProgress() uint64 { if x != nil { return x.IoInProgress } return 0 } func (x *DiskStat) GetIoTimeMs() uint64 { if x != nil { return x.IoTimeMs } return 0 } func (x *DiskStat) GetIoTimeWeightedMs() uint64 { if x != nil { return x.IoTimeWeightedMs } return 0 } func (x *DiskStat) GetDiscardCompleted() uint64 { if x != nil { return x.DiscardCompleted } return 0 } func (x *DiskStat) GetDiscardMerged() uint64 { if x != nil { return x.DiscardMerged } return 0 } func (x *DiskStat) GetDiscardSectors() uint64 { if x != nil { return x.DiscardSectors } return 0 } func (x *DiskStat) GetDiscardTimeMs() uint64 { if x != nil { return x.DiscardTimeMs } return 0 } type EtcdLeaveClusterRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdLeaveClusterRequest) Reset() { *x = EtcdLeaveClusterRequest{} mi := &file_machine_machine_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdLeaveClusterRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdLeaveClusterRequest) ProtoMessage() {} func (x *EtcdLeaveClusterRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdLeaveClusterRequest.ProtoReflect.Descriptor instead. func (*EtcdLeaveClusterRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{104} } type EtcdLeaveCluster struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdLeaveCluster) Reset() { *x = EtcdLeaveCluster{} mi := &file_machine_machine_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdLeaveCluster) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdLeaveCluster) ProtoMessage() {} func (x *EtcdLeaveCluster) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdLeaveCluster.ProtoReflect.Descriptor instead. func (*EtcdLeaveCluster) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{105} } func (x *EtcdLeaveCluster) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type EtcdLeaveClusterResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdLeaveCluster `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdLeaveClusterResponse) Reset() { *x = EtcdLeaveClusterResponse{} mi := &file_machine_machine_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdLeaveClusterResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdLeaveClusterResponse) ProtoMessage() {} func (x *EtcdLeaveClusterResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdLeaveClusterResponse.ProtoReflect.Descriptor instead. func (*EtcdLeaveClusterResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{106} } func (x *EtcdLeaveClusterResponse) GetMessages() []*EtcdLeaveCluster { if x != nil { return x.Messages } return nil } type EtcdRemoveMemberRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Member string `protobuf:"bytes,1,opt,name=member,proto3" json:"member,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMemberRequest) Reset() { *x = EtcdRemoveMemberRequest{} mi := &file_machine_machine_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMemberRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMemberRequest) ProtoMessage() {} func (x *EtcdRemoveMemberRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMemberRequest.ProtoReflect.Descriptor instead. func (*EtcdRemoveMemberRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{107} } func (x *EtcdRemoveMemberRequest) GetMember() string { if x != nil { return x.Member } return "" } type EtcdRemoveMember struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMember) Reset() { *x = EtcdRemoveMember{} mi := &file_machine_machine_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMember) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMember) ProtoMessage() {} func (x *EtcdRemoveMember) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMember.ProtoReflect.Descriptor instead. func (*EtcdRemoveMember) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{108} } func (x *EtcdRemoveMember) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type EtcdRemoveMemberResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdRemoveMember `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMemberResponse) Reset() { *x = EtcdRemoveMemberResponse{} mi := &file_machine_machine_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMemberResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMemberResponse) ProtoMessage() {} func (x *EtcdRemoveMemberResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMemberResponse.ProtoReflect.Descriptor instead. func (*EtcdRemoveMemberResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{109} } func (x *EtcdRemoveMemberResponse) GetMessages() []*EtcdRemoveMember { if x != nil { return x.Messages } return nil } type EtcdRemoveMemberByIDRequest struct { state protoimpl.MessageState `protogen:"open.v1"` MemberId uint64 `protobuf:"varint,1,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMemberByIDRequest) Reset() { *x = EtcdRemoveMemberByIDRequest{} mi := &file_machine_machine_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMemberByIDRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMemberByIDRequest) ProtoMessage() {} func (x *EtcdRemoveMemberByIDRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMemberByIDRequest.ProtoReflect.Descriptor instead. func (*EtcdRemoveMemberByIDRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{110} } func (x *EtcdRemoveMemberByIDRequest) GetMemberId() uint64 { if x != nil { return x.MemberId } return 0 } type EtcdRemoveMemberByID struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMemberByID) Reset() { *x = EtcdRemoveMemberByID{} mi := &file_machine_machine_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMemberByID) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMemberByID) ProtoMessage() {} func (x *EtcdRemoveMemberByID) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMemberByID.ProtoReflect.Descriptor instead. func (*EtcdRemoveMemberByID) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{111} } func (x *EtcdRemoveMemberByID) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type EtcdRemoveMemberByIDResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdRemoveMemberByID `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRemoveMemberByIDResponse) Reset() { *x = EtcdRemoveMemberByIDResponse{} mi := &file_machine_machine_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRemoveMemberByIDResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRemoveMemberByIDResponse) ProtoMessage() {} func (x *EtcdRemoveMemberByIDResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRemoveMemberByIDResponse.ProtoReflect.Descriptor instead. func (*EtcdRemoveMemberByIDResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{112} } func (x *EtcdRemoveMemberByIDResponse) GetMessages() []*EtcdRemoveMemberByID { if x != nil { return x.Messages } return nil } type EtcdForfeitLeadershipRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdForfeitLeadershipRequest) Reset() { *x = EtcdForfeitLeadershipRequest{} mi := &file_machine_machine_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdForfeitLeadershipRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdForfeitLeadershipRequest) ProtoMessage() {} func (x *EtcdForfeitLeadershipRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdForfeitLeadershipRequest.ProtoReflect.Descriptor instead. func (*EtcdForfeitLeadershipRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{113} } type EtcdForfeitLeadership struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Member string `protobuf:"bytes,2,opt,name=member,proto3" json:"member,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdForfeitLeadership) Reset() { *x = EtcdForfeitLeadership{} mi := &file_machine_machine_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdForfeitLeadership) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdForfeitLeadership) ProtoMessage() {} func (x *EtcdForfeitLeadership) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdForfeitLeadership.ProtoReflect.Descriptor instead. func (*EtcdForfeitLeadership) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{114} } func (x *EtcdForfeitLeadership) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdForfeitLeadership) GetMember() string { if x != nil { return x.Member } return "" } type EtcdForfeitLeadershipResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdForfeitLeadership `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdForfeitLeadershipResponse) Reset() { *x = EtcdForfeitLeadershipResponse{} mi := &file_machine_machine_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdForfeitLeadershipResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdForfeitLeadershipResponse) ProtoMessage() {} func (x *EtcdForfeitLeadershipResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdForfeitLeadershipResponse.ProtoReflect.Descriptor instead. func (*EtcdForfeitLeadershipResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{115} } func (x *EtcdForfeitLeadershipResponse) GetMessages() []*EtcdForfeitLeadership { if x != nil { return x.Messages } return nil } type EtcdMemberListRequest struct { state protoimpl.MessageState `protogen:"open.v1"` QueryLocal bool `protobuf:"varint,1,opt,name=query_local,json=queryLocal,proto3" json:"query_local,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMemberListRequest) Reset() { *x = EtcdMemberListRequest{} mi := &file_machine_machine_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMemberListRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMemberListRequest) ProtoMessage() {} func (x *EtcdMemberListRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMemberListRequest.ProtoReflect.Descriptor instead. func (*EtcdMemberListRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{116} } func (x *EtcdMemberListRequest) GetQueryLocal() bool { if x != nil { return x.QueryLocal } return false } // EtcdMember describes a single etcd member. type EtcdMember struct { state protoimpl.MessageState `protogen:"open.v1"` // member ID. Id uint64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // human-readable name of the member. Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` // the list of URLs the member exposes to clients for communication. PeerUrls []string `protobuf:"bytes,4,rep,name=peer_urls,json=peerUrls,proto3" json:"peer_urls,omitempty"` // the list of URLs the member exposes to the cluster for communication. ClientUrls []string `protobuf:"bytes,5,rep,name=client_urls,json=clientUrls,proto3" json:"client_urls,omitempty"` // learner flag IsLearner bool `protobuf:"varint,6,opt,name=is_learner,json=isLearner,proto3" json:"is_learner,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMember) Reset() { *x = EtcdMember{} mi := &file_machine_machine_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMember) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMember) ProtoMessage() {} func (x *EtcdMember) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMember.ProtoReflect.Descriptor instead. func (*EtcdMember) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{117} } func (x *EtcdMember) GetId() uint64 { if x != nil { return x.Id } return 0 } func (x *EtcdMember) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *EtcdMember) GetPeerUrls() []string { if x != nil { return x.PeerUrls } return nil } func (x *EtcdMember) GetClientUrls() []string { if x != nil { return x.ClientUrls } return nil } func (x *EtcdMember) GetIsLearner() bool { if x != nil { return x.IsLearner } return false } // EtcdMembers contains the list of members registered on the host. type EtcdMembers struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // list of member hostnames. LegacyMembers []string `protobuf:"bytes,2,rep,name=legacy_members,json=legacyMembers,proto3" json:"legacy_members,omitempty"` // the list of etcd members registered on the node. Members []*EtcdMember `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMembers) Reset() { *x = EtcdMembers{} mi := &file_machine_machine_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMembers) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMembers) ProtoMessage() {} func (x *EtcdMembers) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMembers.ProtoReflect.Descriptor instead. func (*EtcdMembers) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{118} } func (x *EtcdMembers) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdMembers) GetLegacyMembers() []string { if x != nil { return x.LegacyMembers } return nil } func (x *EtcdMembers) GetMembers() []*EtcdMember { if x != nil { return x.Members } return nil } type EtcdMemberListResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdMembers `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMemberListResponse) Reset() { *x = EtcdMemberListResponse{} mi := &file_machine_machine_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMemberListResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMemberListResponse) ProtoMessage() {} func (x *EtcdMemberListResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMemberListResponse.ProtoReflect.Descriptor instead. func (*EtcdMemberListResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{119} } func (x *EtcdMemberListResponse) GetMessages() []*EtcdMembers { if x != nil { return x.Messages } return nil } type EtcdSnapshotRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdSnapshotRequest) Reset() { *x = EtcdSnapshotRequest{} mi := &file_machine_machine_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdSnapshotRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdSnapshotRequest) ProtoMessage() {} func (x *EtcdSnapshotRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdSnapshotRequest.ProtoReflect.Descriptor instead. func (*EtcdSnapshotRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{120} } type EtcdRecover struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRecover) Reset() { *x = EtcdRecover{} mi := &file_machine_machine_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRecover) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRecover) ProtoMessage() {} func (x *EtcdRecover) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRecover.ProtoReflect.Descriptor instead. func (*EtcdRecover) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{121} } func (x *EtcdRecover) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type EtcdRecoverResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdRecover `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRecoverResponse) Reset() { *x = EtcdRecoverResponse{} mi := &file_machine_machine_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRecoverResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRecoverResponse) ProtoMessage() {} func (x *EtcdRecoverResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRecoverResponse.ProtoReflect.Descriptor instead. func (*EtcdRecoverResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{122} } func (x *EtcdRecoverResponse) GetMessages() []*EtcdRecover { if x != nil { return x.Messages } return nil } type EtcdAlarmListResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdAlarm `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdAlarmListResponse) Reset() { *x = EtcdAlarmListResponse{} mi := &file_machine_machine_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdAlarmListResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdAlarmListResponse) ProtoMessage() {} func (x *EtcdAlarmListResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdAlarmListResponse.ProtoReflect.Descriptor instead. func (*EtcdAlarmListResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{123} } func (x *EtcdAlarmListResponse) GetMessages() []*EtcdAlarm { if x != nil { return x.Messages } return nil } type EtcdAlarm struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` MemberAlarms []*EtcdMemberAlarm `protobuf:"bytes,2,rep,name=member_alarms,json=memberAlarms,proto3" json:"member_alarms,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdAlarm) Reset() { *x = EtcdAlarm{} mi := &file_machine_machine_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdAlarm) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdAlarm) ProtoMessage() {} func (x *EtcdAlarm) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdAlarm.ProtoReflect.Descriptor instead. func (*EtcdAlarm) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{124} } func (x *EtcdAlarm) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdAlarm) GetMemberAlarms() []*EtcdMemberAlarm { if x != nil { return x.MemberAlarms } return nil } type EtcdMemberAlarm struct { state protoimpl.MessageState `protogen:"open.v1"` MemberId uint64 `protobuf:"varint,1,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` Alarm EtcdMemberAlarm_AlarmType `protobuf:"varint,2,opt,name=alarm,proto3,enum=machine.EtcdMemberAlarm_AlarmType" json:"alarm,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMemberAlarm) Reset() { *x = EtcdMemberAlarm{} mi := &file_machine_machine_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMemberAlarm) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMemberAlarm) ProtoMessage() {} func (x *EtcdMemberAlarm) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMemberAlarm.ProtoReflect.Descriptor instead. func (*EtcdMemberAlarm) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{125} } func (x *EtcdMemberAlarm) GetMemberId() uint64 { if x != nil { return x.MemberId } return 0 } func (x *EtcdMemberAlarm) GetAlarm() EtcdMemberAlarm_AlarmType { if x != nil { return x.Alarm } return EtcdMemberAlarm_NONE } type EtcdAlarmDisarmResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdAlarmDisarm `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdAlarmDisarmResponse) Reset() { *x = EtcdAlarmDisarmResponse{} mi := &file_machine_machine_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdAlarmDisarmResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdAlarmDisarmResponse) ProtoMessage() {} func (x *EtcdAlarmDisarmResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdAlarmDisarmResponse.ProtoReflect.Descriptor instead. func (*EtcdAlarmDisarmResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{126} } func (x *EtcdAlarmDisarmResponse) GetMessages() []*EtcdAlarmDisarm { if x != nil { return x.Messages } return nil } type EtcdAlarmDisarm struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` MemberAlarms []*EtcdMemberAlarm `protobuf:"bytes,2,rep,name=member_alarms,json=memberAlarms,proto3" json:"member_alarms,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdAlarmDisarm) Reset() { *x = EtcdAlarmDisarm{} mi := &file_machine_machine_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdAlarmDisarm) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdAlarmDisarm) ProtoMessage() {} func (x *EtcdAlarmDisarm) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdAlarmDisarm.ProtoReflect.Descriptor instead. func (*EtcdAlarmDisarm) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{127} } func (x *EtcdAlarmDisarm) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdAlarmDisarm) GetMemberAlarms() []*EtcdMemberAlarm { if x != nil { return x.MemberAlarms } return nil } type EtcdDefragmentResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdDefragment `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDefragmentResponse) Reset() { *x = EtcdDefragmentResponse{} mi := &file_machine_machine_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDefragmentResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDefragmentResponse) ProtoMessage() {} func (x *EtcdDefragmentResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDefragmentResponse.ProtoReflect.Descriptor instead. func (*EtcdDefragmentResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{128} } func (x *EtcdDefragmentResponse) GetMessages() []*EtcdDefragment { if x != nil { return x.Messages } return nil } type EtcdDefragment struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDefragment) Reset() { *x = EtcdDefragment{} mi := &file_machine_machine_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDefragment) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDefragment) ProtoMessage() {} func (x *EtcdDefragment) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDefragment.ProtoReflect.Descriptor instead. func (*EtcdDefragment) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{129} } func (x *EtcdDefragment) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type EtcdStatusResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdStatus `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdStatusResponse) Reset() { *x = EtcdStatusResponse{} mi := &file_machine_machine_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdStatusResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdStatusResponse) ProtoMessage() {} func (x *EtcdStatusResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdStatusResponse.ProtoReflect.Descriptor instead. func (*EtcdStatusResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{130} } func (x *EtcdStatusResponse) GetMessages() []*EtcdStatus { if x != nil { return x.Messages } return nil } type EtcdStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` MemberStatus *EtcdMemberStatus `protobuf:"bytes,2,opt,name=member_status,json=memberStatus,proto3" json:"member_status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdStatus) Reset() { *x = EtcdStatus{} mi := &file_machine_machine_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdStatus) ProtoMessage() {} func (x *EtcdStatus) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdStatus.ProtoReflect.Descriptor instead. func (*EtcdStatus) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{131} } func (x *EtcdStatus) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdStatus) GetMemberStatus() *EtcdMemberStatus { if x != nil { return x.MemberStatus } return nil } type EtcdMemberStatus struct { state protoimpl.MessageState `protogen:"open.v1"` StorageVersion string `protobuf:"bytes,11,opt,name=storage_version,json=storageVersion,proto3" json:"storage_version,omitempty"` MemberId uint64 `protobuf:"varint,10,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` ProtocolVersion string `protobuf:"bytes,1,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` DbSize int64 `protobuf:"varint,2,opt,name=db_size,json=dbSize,proto3" json:"db_size,omitempty"` DbSizeInUse int64 `protobuf:"varint,3,opt,name=db_size_in_use,json=dbSizeInUse,proto3" json:"db_size_in_use,omitempty"` Leader uint64 `protobuf:"varint,4,opt,name=leader,proto3" json:"leader,omitempty"` RaftIndex uint64 `protobuf:"varint,5,opt,name=raft_index,json=raftIndex,proto3" json:"raft_index,omitempty"` RaftTerm uint64 `protobuf:"varint,6,opt,name=raft_term,json=raftTerm,proto3" json:"raft_term,omitempty"` RaftAppliedIndex uint64 `protobuf:"varint,7,opt,name=raft_applied_index,json=raftAppliedIndex,proto3" json:"raft_applied_index,omitempty"` Errors []string `protobuf:"bytes,8,rep,name=errors,proto3" json:"errors,omitempty"` IsLearner bool `protobuf:"varint,9,opt,name=is_learner,json=isLearner,proto3" json:"is_learner,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdMemberStatus) Reset() { *x = EtcdMemberStatus{} mi := &file_machine_machine_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdMemberStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdMemberStatus) ProtoMessage() {} func (x *EtcdMemberStatus) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdMemberStatus.ProtoReflect.Descriptor instead. func (*EtcdMemberStatus) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{132} } func (x *EtcdMemberStatus) GetStorageVersion() string { if x != nil { return x.StorageVersion } return "" } func (x *EtcdMemberStatus) GetMemberId() uint64 { if x != nil { return x.MemberId } return 0 } func (x *EtcdMemberStatus) GetProtocolVersion() string { if x != nil { return x.ProtocolVersion } return "" } func (x *EtcdMemberStatus) GetDbSize() int64 { if x != nil { return x.DbSize } return 0 } func (x *EtcdMemberStatus) GetDbSizeInUse() int64 { if x != nil { return x.DbSizeInUse } return 0 } func (x *EtcdMemberStatus) GetLeader() uint64 { if x != nil { return x.Leader } return 0 } func (x *EtcdMemberStatus) GetRaftIndex() uint64 { if x != nil { return x.RaftIndex } return 0 } func (x *EtcdMemberStatus) GetRaftTerm() uint64 { if x != nil { return x.RaftTerm } return 0 } func (x *EtcdMemberStatus) GetRaftAppliedIndex() uint64 { if x != nil { return x.RaftAppliedIndex } return 0 } func (x *EtcdMemberStatus) GetErrors() []string { if x != nil { return x.Errors } return nil } func (x *EtcdMemberStatus) GetIsLearner() bool { if x != nil { return x.IsLearner } return false } type EtcdDowngradeValidateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeValidateRequest) Reset() { *x = EtcdDowngradeValidateRequest{} mi := &file_machine_machine_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeValidateRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeValidateRequest) ProtoMessage() {} func (x *EtcdDowngradeValidateRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeValidateRequest.ProtoReflect.Descriptor instead. func (*EtcdDowngradeValidateRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{133} } func (x *EtcdDowngradeValidateRequest) GetVersion() string { if x != nil { return x.Version } return "" } type EtcdDowngradeValidateResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdDowngradeValidate `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeValidateResponse) Reset() { *x = EtcdDowngradeValidateResponse{} mi := &file_machine_machine_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeValidateResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeValidateResponse) ProtoMessage() {} func (x *EtcdDowngradeValidateResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeValidateResponse.ProtoReflect.Descriptor instead. func (*EtcdDowngradeValidateResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{134} } func (x *EtcdDowngradeValidateResponse) GetMessages() []*EtcdDowngradeValidate { if x != nil { return x.Messages } return nil } type EtcdDowngradeValidate struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ClusterDowngrade *EtcdClusterDowngrade `protobuf:"bytes,2,opt,name=cluster_downgrade,json=clusterDowngrade,proto3" json:"cluster_downgrade,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeValidate) Reset() { *x = EtcdDowngradeValidate{} mi := &file_machine_machine_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeValidate) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeValidate) ProtoMessage() {} func (x *EtcdDowngradeValidate) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeValidate.ProtoReflect.Descriptor instead. func (*EtcdDowngradeValidate) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{135} } func (x *EtcdDowngradeValidate) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdDowngradeValidate) GetClusterDowngrade() *EtcdClusterDowngrade { if x != nil { return x.ClusterDowngrade } return nil } type EtcdDowngradeEnableRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeEnableRequest) Reset() { *x = EtcdDowngradeEnableRequest{} mi := &file_machine_machine_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeEnableRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeEnableRequest) ProtoMessage() {} func (x *EtcdDowngradeEnableRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeEnableRequest.ProtoReflect.Descriptor instead. func (*EtcdDowngradeEnableRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{136} } func (x *EtcdDowngradeEnableRequest) GetVersion() string { if x != nil { return x.Version } return "" } type EtcdDowngradeEnableResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdDowngradeEnable `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeEnableResponse) Reset() { *x = EtcdDowngradeEnableResponse{} mi := &file_machine_machine_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeEnableResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeEnableResponse) ProtoMessage() {} func (x *EtcdDowngradeEnableResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeEnableResponse.ProtoReflect.Descriptor instead. func (*EtcdDowngradeEnableResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{137} } func (x *EtcdDowngradeEnableResponse) GetMessages() []*EtcdDowngradeEnable { if x != nil { return x.Messages } return nil } type EtcdDowngradeEnable struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ClusterDowngrade *EtcdClusterDowngrade `protobuf:"bytes,2,opt,name=cluster_downgrade,json=clusterDowngrade,proto3" json:"cluster_downgrade,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeEnable) Reset() { *x = EtcdDowngradeEnable{} mi := &file_machine_machine_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeEnable) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeEnable) ProtoMessage() {} func (x *EtcdDowngradeEnable) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeEnable.ProtoReflect.Descriptor instead. func (*EtcdDowngradeEnable) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{138} } func (x *EtcdDowngradeEnable) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdDowngradeEnable) GetClusterDowngrade() *EtcdClusterDowngrade { if x != nil { return x.ClusterDowngrade } return nil } type EtcdDowngradeCancelResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*EtcdDowngradeCancel `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeCancelResponse) Reset() { *x = EtcdDowngradeCancelResponse{} mi := &file_machine_machine_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeCancelResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeCancelResponse) ProtoMessage() {} func (x *EtcdDowngradeCancelResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeCancelResponse.ProtoReflect.Descriptor instead. func (*EtcdDowngradeCancelResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{139} } func (x *EtcdDowngradeCancelResponse) GetMessages() []*EtcdDowngradeCancel { if x != nil { return x.Messages } return nil } type EtcdDowngradeCancel struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` ClusterDowngrade *EtcdClusterDowngrade `protobuf:"bytes,2,opt,name=cluster_downgrade,json=clusterDowngrade,proto3" json:"cluster_downgrade,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdDowngradeCancel) Reset() { *x = EtcdDowngradeCancel{} mi := &file_machine_machine_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdDowngradeCancel) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdDowngradeCancel) ProtoMessage() {} func (x *EtcdDowngradeCancel) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdDowngradeCancel.ProtoReflect.Descriptor instead. func (*EtcdDowngradeCancel) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{140} } func (x *EtcdDowngradeCancel) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *EtcdDowngradeCancel) GetClusterDowngrade() *EtcdClusterDowngrade { if x != nil { return x.ClusterDowngrade } return nil } type EtcdClusterDowngrade struct { state protoimpl.MessageState `protogen:"open.v1"` ClusterVersion string `protobuf:"bytes,1,opt,name=cluster_version,json=clusterVersion,proto3" json:"cluster_version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdClusterDowngrade) Reset() { *x = EtcdClusterDowngrade{} mi := &file_machine_machine_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdClusterDowngrade) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdClusterDowngrade) ProtoMessage() {} func (x *EtcdClusterDowngrade) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdClusterDowngrade.ProtoReflect.Descriptor instead. func (*EtcdClusterDowngrade) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{141} } func (x *EtcdClusterDowngrade) GetClusterVersion() string { if x != nil { return x.ClusterVersion } return "" } type RouteConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` Gateway string `protobuf:"bytes,2,opt,name=gateway,proto3" json:"gateway,omitempty"` Metric uint32 `protobuf:"varint,3,opt,name=metric,proto3" json:"metric,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RouteConfig) Reset() { *x = RouteConfig{} mi := &file_machine_machine_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RouteConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RouteConfig) ProtoMessage() {} func (x *RouteConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RouteConfig.ProtoReflect.Descriptor instead. func (*RouteConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{142} } func (x *RouteConfig) GetNetwork() string { if x != nil { return x.Network } return "" } func (x *RouteConfig) GetGateway() string { if x != nil { return x.Gateway } return "" } func (x *RouteConfig) GetMetric() uint32 { if x != nil { return x.Metric } return 0 } type DHCPOptionsConfig struct { state protoimpl.MessageState `protogen:"open.v1"` RouteMetric uint32 `protobuf:"varint,1,opt,name=route_metric,json=routeMetric,proto3" json:"route_metric,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DHCPOptionsConfig) Reset() { *x = DHCPOptionsConfig{} mi := &file_machine_machine_proto_msgTypes[143] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DHCPOptionsConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*DHCPOptionsConfig) ProtoMessage() {} func (x *DHCPOptionsConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[143] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DHCPOptionsConfig.ProtoReflect.Descriptor instead. func (*DHCPOptionsConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{143} } func (x *DHCPOptionsConfig) GetRouteMetric() uint32 { if x != nil { return x.RouteMetric } return 0 } type NetworkDeviceConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Interface string `protobuf:"bytes,1,opt,name=interface,proto3" json:"interface,omitempty"` Cidr string `protobuf:"bytes,2,opt,name=cidr,proto3" json:"cidr,omitempty"` Mtu int32 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"` Dhcp bool `protobuf:"varint,4,opt,name=dhcp,proto3" json:"dhcp,omitempty"` Ignore bool `protobuf:"varint,5,opt,name=ignore,proto3" json:"ignore,omitempty"` DhcpOptions *DHCPOptionsConfig `protobuf:"bytes,6,opt,name=dhcp_options,json=dhcpOptions,proto3" json:"dhcp_options,omitempty"` Routes []*RouteConfig `protobuf:"bytes,7,rep,name=routes,proto3" json:"routes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetworkDeviceConfig) Reset() { *x = NetworkDeviceConfig{} mi := &file_machine_machine_proto_msgTypes[144] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetworkDeviceConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetworkDeviceConfig) ProtoMessage() {} func (x *NetworkDeviceConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[144] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetworkDeviceConfig.ProtoReflect.Descriptor instead. func (*NetworkDeviceConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{144} } func (x *NetworkDeviceConfig) GetInterface() string { if x != nil { return x.Interface } return "" } func (x *NetworkDeviceConfig) GetCidr() string { if x != nil { return x.Cidr } return "" } func (x *NetworkDeviceConfig) GetMtu() int32 { if x != nil { return x.Mtu } return 0 } func (x *NetworkDeviceConfig) GetDhcp() bool { if x != nil { return x.Dhcp } return false } func (x *NetworkDeviceConfig) GetIgnore() bool { if x != nil { return x.Ignore } return false } func (x *NetworkDeviceConfig) GetDhcpOptions() *DHCPOptionsConfig { if x != nil { return x.DhcpOptions } return nil } func (x *NetworkDeviceConfig) GetRoutes() []*RouteConfig { if x != nil { return x.Routes } return nil } type NetworkConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` Interfaces []*NetworkDeviceConfig `protobuf:"bytes,2,rep,name=interfaces,proto3" json:"interfaces,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetworkConfig) Reset() { *x = NetworkConfig{} mi := &file_machine_machine_proto_msgTypes[145] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetworkConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetworkConfig) ProtoMessage() {} func (x *NetworkConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[145] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetworkConfig.ProtoReflect.Descriptor instead. func (*NetworkConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{145} } func (x *NetworkConfig) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *NetworkConfig) GetInterfaces() []*NetworkDeviceConfig { if x != nil { return x.Interfaces } return nil } type InstallConfig struct { state protoimpl.MessageState `protogen:"open.v1"` InstallDisk string `protobuf:"bytes,1,opt,name=install_disk,json=installDisk,proto3" json:"install_disk,omitempty"` InstallImage string `protobuf:"bytes,2,opt,name=install_image,json=installImage,proto3" json:"install_image,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InstallConfig) Reset() { *x = InstallConfig{} mi := &file_machine_machine_proto_msgTypes[146] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InstallConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*InstallConfig) ProtoMessage() {} func (x *InstallConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[146] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InstallConfig.ProtoReflect.Descriptor instead. func (*InstallConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{146} } func (x *InstallConfig) GetInstallDisk() string { if x != nil { return x.InstallDisk } return "" } func (x *InstallConfig) GetInstallImage() string { if x != nil { return x.InstallImage } return "" } type MachineConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Type MachineConfig_MachineType `protobuf:"varint,1,opt,name=type,proto3,enum=machine.MachineConfig_MachineType" json:"type,omitempty"` InstallConfig *InstallConfig `protobuf:"bytes,2,opt,name=install_config,json=installConfig,proto3" json:"install_config,omitempty"` NetworkConfig *NetworkConfig `protobuf:"bytes,3,opt,name=network_config,json=networkConfig,proto3" json:"network_config,omitempty"` KubernetesVersion string `protobuf:"bytes,4,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineConfig) Reset() { *x = MachineConfig{} mi := &file_machine_machine_proto_msgTypes[147] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineConfig) ProtoMessage() {} func (x *MachineConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[147] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineConfig.ProtoReflect.Descriptor instead. func (*MachineConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{147} } func (x *MachineConfig) GetType() MachineConfig_MachineType { if x != nil { return x.Type } return MachineConfig_TYPE_UNKNOWN } func (x *MachineConfig) GetInstallConfig() *InstallConfig { if x != nil { return x.InstallConfig } return nil } func (x *MachineConfig) GetNetworkConfig() *NetworkConfig { if x != nil { return x.NetworkConfig } return nil } func (x *MachineConfig) GetKubernetesVersion() string { if x != nil { return x.KubernetesVersion } return "" } type ControlPlaneConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControlPlaneConfig) Reset() { *x = ControlPlaneConfig{} mi := &file_machine_machine_proto_msgTypes[148] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControlPlaneConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControlPlaneConfig) ProtoMessage() {} func (x *ControlPlaneConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[148] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControlPlaneConfig.ProtoReflect.Descriptor instead. func (*ControlPlaneConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{148} } func (x *ControlPlaneConfig) GetEndpoint() string { if x != nil { return x.Endpoint } return "" } type CNIConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Urls []string `protobuf:"bytes,2,rep,name=urls,proto3" json:"urls,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CNIConfig) Reset() { *x = CNIConfig{} mi := &file_machine_machine_proto_msgTypes[149] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CNIConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*CNIConfig) ProtoMessage() {} func (x *CNIConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[149] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CNIConfig.ProtoReflect.Descriptor instead. func (*CNIConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{149} } func (x *CNIConfig) GetName() string { if x != nil { return x.Name } return "" } func (x *CNIConfig) GetUrls() []string { if x != nil { return x.Urls } return nil } type ClusterNetworkConfig struct { state protoimpl.MessageState `protogen:"open.v1"` DnsDomain string `protobuf:"bytes,1,opt,name=dns_domain,json=dnsDomain,proto3" json:"dns_domain,omitempty"` CniConfig *CNIConfig `protobuf:"bytes,2,opt,name=cni_config,json=cniConfig,proto3" json:"cni_config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClusterNetworkConfig) Reset() { *x = ClusterNetworkConfig{} mi := &file_machine_machine_proto_msgTypes[150] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClusterNetworkConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClusterNetworkConfig) ProtoMessage() {} func (x *ClusterNetworkConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[150] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClusterNetworkConfig.ProtoReflect.Descriptor instead. func (*ClusterNetworkConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{150} } func (x *ClusterNetworkConfig) GetDnsDomain() string { if x != nil { return x.DnsDomain } return "" } func (x *ClusterNetworkConfig) GetCniConfig() *CNIConfig { if x != nil { return x.CniConfig } return nil } type ClusterConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` ControlPlane *ControlPlaneConfig `protobuf:"bytes,2,opt,name=control_plane,json=controlPlane,proto3" json:"control_plane,omitempty"` ClusterNetwork *ClusterNetworkConfig `protobuf:"bytes,3,opt,name=cluster_network,json=clusterNetwork,proto3" json:"cluster_network,omitempty"` AllowSchedulingOnControlPlanes bool `protobuf:"varint,4,opt,name=allow_scheduling_on_control_planes,json=allowSchedulingOnControlPlanes,proto3" json:"allow_scheduling_on_control_planes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClusterConfig) Reset() { *x = ClusterConfig{} mi := &file_machine_machine_proto_msgTypes[151] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClusterConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClusterConfig) ProtoMessage() {} func (x *ClusterConfig) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[151] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClusterConfig.ProtoReflect.Descriptor instead. func (*ClusterConfig) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{151} } func (x *ClusterConfig) GetName() string { if x != nil { return x.Name } return "" } func (x *ClusterConfig) GetControlPlane() *ControlPlaneConfig { if x != nil { return x.ControlPlane } return nil } func (x *ClusterConfig) GetClusterNetwork() *ClusterNetworkConfig { if x != nil { return x.ClusterNetwork } return nil } func (x *ClusterConfig) GetAllowSchedulingOnControlPlanes() bool { if x != nil { return x.AllowSchedulingOnControlPlanes } return false } type GenerateClientConfigurationRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Roles in the generated client certificate. Roles []string `protobuf:"bytes,1,rep,name=roles,proto3" json:"roles,omitempty"` // Client certificate TTL. CrtTtl *durationpb.Duration `protobuf:"bytes,2,opt,name=crt_ttl,json=crtTtl,proto3" json:"crt_ttl,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GenerateClientConfigurationRequest) Reset() { *x = GenerateClientConfigurationRequest{} mi := &file_machine_machine_proto_msgTypes[152] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GenerateClientConfigurationRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*GenerateClientConfigurationRequest) ProtoMessage() {} func (x *GenerateClientConfigurationRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[152] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GenerateClientConfigurationRequest.ProtoReflect.Descriptor instead. func (*GenerateClientConfigurationRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{152} } func (x *GenerateClientConfigurationRequest) GetRoles() []string { if x != nil { return x.Roles } return nil } func (x *GenerateClientConfigurationRequest) GetCrtTtl() *durationpb.Duration { if x != nil { return x.CrtTtl } return nil } type GenerateClientConfiguration struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // PEM-encoded CA certificate. Ca []byte `protobuf:"bytes,2,opt,name=ca,proto3" json:"ca,omitempty"` // PEM-encoded generated client certificate. Crt []byte `protobuf:"bytes,3,opt,name=crt,proto3" json:"crt,omitempty"` // PEM-encoded generated client key. Key []byte `protobuf:"bytes,4,opt,name=key,proto3" json:"key,omitempty"` // Client configuration (talosconfig) file content. Talosconfig []byte `protobuf:"bytes,5,opt,name=talosconfig,proto3" json:"talosconfig,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GenerateClientConfiguration) Reset() { *x = GenerateClientConfiguration{} mi := &file_machine_machine_proto_msgTypes[153] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GenerateClientConfiguration) String() string { return protoimpl.X.MessageStringOf(x) } func (*GenerateClientConfiguration) ProtoMessage() {} func (x *GenerateClientConfiguration) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[153] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GenerateClientConfiguration.ProtoReflect.Descriptor instead. func (*GenerateClientConfiguration) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{153} } func (x *GenerateClientConfiguration) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *GenerateClientConfiguration) GetCa() []byte { if x != nil { return x.Ca } return nil } func (x *GenerateClientConfiguration) GetCrt() []byte { if x != nil { return x.Crt } return nil } func (x *GenerateClientConfiguration) GetKey() []byte { if x != nil { return x.Key } return nil } func (x *GenerateClientConfiguration) GetTalosconfig() []byte { if x != nil { return x.Talosconfig } return nil } type GenerateClientConfigurationResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*GenerateClientConfiguration `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *GenerateClientConfigurationResponse) Reset() { *x = GenerateClientConfigurationResponse{} mi := &file_machine_machine_proto_msgTypes[154] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *GenerateClientConfigurationResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*GenerateClientConfigurationResponse) ProtoMessage() {} func (x *GenerateClientConfigurationResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[154] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use GenerateClientConfigurationResponse.ProtoReflect.Descriptor instead. func (*GenerateClientConfigurationResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{154} } func (x *GenerateClientConfigurationResponse) GetMessages() []*GenerateClientConfiguration { if x != nil { return x.Messages } return nil } type PacketCaptureRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Interface name to perform packet capture on. Interface string `protobuf:"bytes,1,opt,name=interface,proto3" json:"interface,omitempty"` // Enable promiscuous mode. Promiscuous bool `protobuf:"varint,2,opt,name=promiscuous,proto3" json:"promiscuous,omitempty"` // Snap length in bytes. SnapLen uint32 `protobuf:"varint,3,opt,name=snap_len,json=snapLen,proto3" json:"snap_len,omitempty"` // BPF filter. BpfFilter []*BPFInstruction `protobuf:"bytes,4,rep,name=bpf_filter,json=bpfFilter,proto3" json:"bpf_filter,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PacketCaptureRequest) Reset() { *x = PacketCaptureRequest{} mi := &file_machine_machine_proto_msgTypes[155] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PacketCaptureRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*PacketCaptureRequest) ProtoMessage() {} func (x *PacketCaptureRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[155] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PacketCaptureRequest.ProtoReflect.Descriptor instead. func (*PacketCaptureRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{155} } func (x *PacketCaptureRequest) GetInterface() string { if x != nil { return x.Interface } return "" } func (x *PacketCaptureRequest) GetPromiscuous() bool { if x != nil { return x.Promiscuous } return false } func (x *PacketCaptureRequest) GetSnapLen() uint32 { if x != nil { return x.SnapLen } return 0 } func (x *PacketCaptureRequest) GetBpfFilter() []*BPFInstruction { if x != nil { return x.BpfFilter } return nil } type BPFInstruction struct { state protoimpl.MessageState `protogen:"open.v1"` Op uint32 `protobuf:"varint,1,opt,name=op,proto3" json:"op,omitempty"` Jt uint32 `protobuf:"varint,2,opt,name=jt,proto3" json:"jt,omitempty"` Jf uint32 `protobuf:"varint,3,opt,name=jf,proto3" json:"jf,omitempty"` K uint32 `protobuf:"varint,4,opt,name=k,proto3" json:"k,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BPFInstruction) Reset() { *x = BPFInstruction{} mi := &file_machine_machine_proto_msgTypes[156] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BPFInstruction) String() string { return protoimpl.X.MessageStringOf(x) } func (*BPFInstruction) ProtoMessage() {} func (x *BPFInstruction) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[156] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BPFInstruction.ProtoReflect.Descriptor instead. func (*BPFInstruction) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{156} } func (x *BPFInstruction) GetOp() uint32 { if x != nil { return x.Op } return 0 } func (x *BPFInstruction) GetJt() uint32 { if x != nil { return x.Jt } return 0 } func (x *BPFInstruction) GetJf() uint32 { if x != nil { return x.Jf } return 0 } func (x *BPFInstruction) GetK() uint32 { if x != nil { return x.K } return 0 } type NetstatRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Filter NetstatRequest_Filter `protobuf:"varint,1,opt,name=filter,proto3,enum=machine.NetstatRequest_Filter" json:"filter,omitempty"` Feature *NetstatRequest_Feature `protobuf:"bytes,2,opt,name=feature,proto3" json:"feature,omitempty"` L4Proto *NetstatRequest_L4Proto `protobuf:"bytes,3,opt,name=l4proto,proto3" json:"l4proto,omitempty"` Netns *NetstatRequest_NetNS `protobuf:"bytes,4,opt,name=netns,proto3" json:"netns,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetstatRequest) Reset() { *x = NetstatRequest{} mi := &file_machine_machine_proto_msgTypes[157] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetstatRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetstatRequest) ProtoMessage() {} func (x *NetstatRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[157] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetstatRequest.ProtoReflect.Descriptor instead. func (*NetstatRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{157} } func (x *NetstatRequest) GetFilter() NetstatRequest_Filter { if x != nil { return x.Filter } return NetstatRequest_ALL } func (x *NetstatRequest) GetFeature() *NetstatRequest_Feature { if x != nil { return x.Feature } return nil } func (x *NetstatRequest) GetL4Proto() *NetstatRequest_L4Proto { if x != nil { return x.L4Proto } return nil } func (x *NetstatRequest) GetNetns() *NetstatRequest_NetNS { if x != nil { return x.Netns } return nil } type ConnectRecord struct { state protoimpl.MessageState `protogen:"open.v1"` L4Proto string `protobuf:"bytes,1,opt,name=l4proto,proto3" json:"l4proto,omitempty"` Localip string `protobuf:"bytes,2,opt,name=localip,proto3" json:"localip,omitempty"` Localport uint32 `protobuf:"varint,3,opt,name=localport,proto3" json:"localport,omitempty"` Remoteip string `protobuf:"bytes,4,opt,name=remoteip,proto3" json:"remoteip,omitempty"` Remoteport uint32 `protobuf:"varint,5,opt,name=remoteport,proto3" json:"remoteport,omitempty"` State ConnectRecord_State `protobuf:"varint,6,opt,name=state,proto3,enum=machine.ConnectRecord_State" json:"state,omitempty"` Txqueue uint64 `protobuf:"varint,7,opt,name=txqueue,proto3" json:"txqueue,omitempty"` Rxqueue uint64 `protobuf:"varint,8,opt,name=rxqueue,proto3" json:"rxqueue,omitempty"` Tr ConnectRecord_TimerActive `protobuf:"varint,9,opt,name=tr,proto3,enum=machine.ConnectRecord_TimerActive" json:"tr,omitempty"` Timerwhen uint64 `protobuf:"varint,10,opt,name=timerwhen,proto3" json:"timerwhen,omitempty"` Retrnsmt uint64 `protobuf:"varint,11,opt,name=retrnsmt,proto3" json:"retrnsmt,omitempty"` Uid uint32 `protobuf:"varint,12,opt,name=uid,proto3" json:"uid,omitempty"` Timeout uint64 `protobuf:"varint,13,opt,name=timeout,proto3" json:"timeout,omitempty"` Inode uint64 `protobuf:"varint,14,opt,name=inode,proto3" json:"inode,omitempty"` Ref uint64 `protobuf:"varint,15,opt,name=ref,proto3" json:"ref,omitempty"` Pointer uint64 `protobuf:"varint,16,opt,name=pointer,proto3" json:"pointer,omitempty"` Process *ConnectRecord_Process `protobuf:"bytes,17,opt,name=process,proto3" json:"process,omitempty"` Netns string `protobuf:"bytes,18,opt,name=netns,proto3" json:"netns,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConnectRecord) Reset() { *x = ConnectRecord{} mi := &file_machine_machine_proto_msgTypes[158] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConnectRecord) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConnectRecord) ProtoMessage() {} func (x *ConnectRecord) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[158] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConnectRecord.ProtoReflect.Descriptor instead. func (*ConnectRecord) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{158} } func (x *ConnectRecord) GetL4Proto() string { if x != nil { return x.L4Proto } return "" } func (x *ConnectRecord) GetLocalip() string { if x != nil { return x.Localip } return "" } func (x *ConnectRecord) GetLocalport() uint32 { if x != nil { return x.Localport } return 0 } func (x *ConnectRecord) GetRemoteip() string { if x != nil { return x.Remoteip } return "" } func (x *ConnectRecord) GetRemoteport() uint32 { if x != nil { return x.Remoteport } return 0 } func (x *ConnectRecord) GetState() ConnectRecord_State { if x != nil { return x.State } return ConnectRecord_RESERVED } func (x *ConnectRecord) GetTxqueue() uint64 { if x != nil { return x.Txqueue } return 0 } func (x *ConnectRecord) GetRxqueue() uint64 { if x != nil { return x.Rxqueue } return 0 } func (x *ConnectRecord) GetTr() ConnectRecord_TimerActive { if x != nil { return x.Tr } return ConnectRecord_OFF } func (x *ConnectRecord) GetTimerwhen() uint64 { if x != nil { return x.Timerwhen } return 0 } func (x *ConnectRecord) GetRetrnsmt() uint64 { if x != nil { return x.Retrnsmt } return 0 } func (x *ConnectRecord) GetUid() uint32 { if x != nil { return x.Uid } return 0 } func (x *ConnectRecord) GetTimeout() uint64 { if x != nil { return x.Timeout } return 0 } func (x *ConnectRecord) GetInode() uint64 { if x != nil { return x.Inode } return 0 } func (x *ConnectRecord) GetRef() uint64 { if x != nil { return x.Ref } return 0 } func (x *ConnectRecord) GetPointer() uint64 { if x != nil { return x.Pointer } return 0 } func (x *ConnectRecord) GetProcess() *ConnectRecord_Process { if x != nil { return x.Process } return nil } func (x *ConnectRecord) GetNetns() string { if x != nil { return x.Netns } return "" } type Netstat struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Connectrecord []*ConnectRecord `protobuf:"bytes,2,rep,name=connectrecord,proto3" json:"connectrecord,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Netstat) Reset() { *x = Netstat{} mi := &file_machine_machine_proto_msgTypes[159] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Netstat) String() string { return protoimpl.X.MessageStringOf(x) } func (*Netstat) ProtoMessage() {} func (x *Netstat) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[159] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Netstat.ProtoReflect.Descriptor instead. func (*Netstat) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{159} } func (x *Netstat) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Netstat) GetConnectrecord() []*ConnectRecord { if x != nil { return x.Connectrecord } return nil } type NetstatResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Netstat `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetstatResponse) Reset() { *x = NetstatResponse{} mi := &file_machine_machine_proto_msgTypes[160] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetstatResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetstatResponse) ProtoMessage() {} func (x *NetstatResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[160] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetstatResponse.ProtoReflect.Descriptor instead. func (*NetstatResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{160} } func (x *NetstatResponse) GetMessages() []*Netstat { if x != nil { return x.Messages } return nil } type MetaWriteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Key uint32 `protobuf:"varint,1,opt,name=key,proto3" json:"key,omitempty"` Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaWriteRequest) Reset() { *x = MetaWriteRequest{} mi := &file_machine_machine_proto_msgTypes[161] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaWriteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaWriteRequest) ProtoMessage() {} func (x *MetaWriteRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[161] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaWriteRequest.ProtoReflect.Descriptor instead. func (*MetaWriteRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{161} } func (x *MetaWriteRequest) GetKey() uint32 { if x != nil { return x.Key } return 0 } func (x *MetaWriteRequest) GetValue() []byte { if x != nil { return x.Value } return nil } type MetaWrite struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaWrite) Reset() { *x = MetaWrite{} mi := &file_machine_machine_proto_msgTypes[162] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaWrite) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaWrite) ProtoMessage() {} func (x *MetaWrite) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[162] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaWrite.ProtoReflect.Descriptor instead. func (*MetaWrite) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{162} } func (x *MetaWrite) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type MetaWriteResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*MetaWrite `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaWriteResponse) Reset() { *x = MetaWriteResponse{} mi := &file_machine_machine_proto_msgTypes[163] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaWriteResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaWriteResponse) ProtoMessage() {} func (x *MetaWriteResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[163] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaWriteResponse.ProtoReflect.Descriptor instead. func (*MetaWriteResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{163} } func (x *MetaWriteResponse) GetMessages() []*MetaWrite { if x != nil { return x.Messages } return nil } type MetaDeleteRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Key uint32 `protobuf:"varint,1,opt,name=key,proto3" json:"key,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaDeleteRequest) Reset() { *x = MetaDeleteRequest{} mi := &file_machine_machine_proto_msgTypes[164] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaDeleteRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaDeleteRequest) ProtoMessage() {} func (x *MetaDeleteRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[164] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaDeleteRequest.ProtoReflect.Descriptor instead. func (*MetaDeleteRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{164} } func (x *MetaDeleteRequest) GetKey() uint32 { if x != nil { return x.Key } return 0 } type MetaDelete struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaDelete) Reset() { *x = MetaDelete{} mi := &file_machine_machine_proto_msgTypes[165] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaDelete) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaDelete) ProtoMessage() {} func (x *MetaDelete) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[165] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaDelete.ProtoReflect.Descriptor instead. func (*MetaDelete) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{165} } func (x *MetaDelete) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type MetaDeleteResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*MetaDelete `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaDeleteResponse) Reset() { *x = MetaDeleteResponse{} mi := &file_machine_machine_proto_msgTypes[166] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaDeleteResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaDeleteResponse) ProtoMessage() {} func (x *MetaDeleteResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[166] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaDeleteResponse.ProtoReflect.Descriptor instead. func (*MetaDeleteResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{166} } func (x *MetaDeleteResponse) GetMessages() []*MetaDelete { if x != nil { return x.Messages } return nil } type ImageListRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Containerd namespace to use. Namespace common.ContainerdNamespace `protobuf:"varint,1,opt,name=namespace,proto3,enum=common.ContainerdNamespace" json:"namespace,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageListRequest) Reset() { *x = ImageListRequest{} mi := &file_machine_machine_proto_msgTypes[167] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageListRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageListRequest) ProtoMessage() {} func (x *ImageListRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[167] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageListRequest.ProtoReflect.Descriptor instead. func (*ImageListRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{167} } func (x *ImageListRequest) GetNamespace() common.ContainerdNamespace { if x != nil { return x.Namespace } return common.ContainerdNamespace(0) } type ImageListResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Digest string `protobuf:"bytes,3,opt,name=digest,proto3" json:"digest,omitempty"` Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageListResponse) Reset() { *x = ImageListResponse{} mi := &file_machine_machine_proto_msgTypes[168] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageListResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageListResponse) ProtoMessage() {} func (x *ImageListResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[168] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageListResponse.ProtoReflect.Descriptor instead. func (*ImageListResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{168} } func (x *ImageListResponse) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *ImageListResponse) GetName() string { if x != nil { return x.Name } return "" } func (x *ImageListResponse) GetDigest() string { if x != nil { return x.Digest } return "" } func (x *ImageListResponse) GetSize() int64 { if x != nil { return x.Size } return 0 } func (x *ImageListResponse) GetCreatedAt() *timestamppb.Timestamp { if x != nil { return x.CreatedAt } return nil } type ImagePullRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Containerd namespace to use. Namespace common.ContainerdNamespace `protobuf:"varint,1,opt,name=namespace,proto3,enum=common.ContainerdNamespace" json:"namespace,omitempty"` // Image reference to pull. Reference string `protobuf:"bytes,2,opt,name=reference,proto3" json:"reference,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImagePullRequest) Reset() { *x = ImagePullRequest{} mi := &file_machine_machine_proto_msgTypes[169] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImagePullRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImagePullRequest) ProtoMessage() {} func (x *ImagePullRequest) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[169] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImagePullRequest.ProtoReflect.Descriptor instead. func (*ImagePullRequest) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{169} } func (x *ImagePullRequest) GetNamespace() common.ContainerdNamespace { if x != nil { return x.Namespace } return common.ContainerdNamespace(0) } func (x *ImagePullRequest) GetReference() string { if x != nil { return x.Reference } return "" } type ImagePull struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImagePull) Reset() { *x = ImagePull{} mi := &file_machine_machine_proto_msgTypes[170] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImagePull) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImagePull) ProtoMessage() {} func (x *ImagePull) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[170] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImagePull.ProtoReflect.Descriptor instead. func (*ImagePull) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{170} } func (x *ImagePull) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } type ImagePullResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*ImagePull `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImagePullResponse) Reset() { *x = ImagePullResponse{} mi := &file_machine_machine_proto_msgTypes[171] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImagePullResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImagePullResponse) ProtoMessage() {} func (x *ImagePullResponse) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[171] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImagePullResponse.ProtoReflect.Descriptor instead. func (*ImagePullResponse) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{171} } func (x *ImagePullResponse) GetMessages() []*ImagePull { if x != nil { return x.Messages } return nil } type MachineStatusEvent_MachineStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` UnmetConditions []*MachineStatusEvent_MachineStatus_UnmetCondition `protobuf:"bytes,2,rep,name=unmet_conditions,json=unmetConditions,proto3" json:"unmet_conditions,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineStatusEvent_MachineStatus) Reset() { *x = MachineStatusEvent_MachineStatus{} mi := &file_machine_machine_proto_msgTypes[172] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineStatusEvent_MachineStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineStatusEvent_MachineStatus) ProtoMessage() {} func (x *MachineStatusEvent_MachineStatus) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[172] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineStatusEvent_MachineStatus.ProtoReflect.Descriptor instead. func (*MachineStatusEvent_MachineStatus) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{17, 0} } func (x *MachineStatusEvent_MachineStatus) GetReady() bool { if x != nil { return x.Ready } return false } func (x *MachineStatusEvent_MachineStatus) GetUnmetConditions() []*MachineStatusEvent_MachineStatus_UnmetCondition { if x != nil { return x.UnmetConditions } return nil } type MachineStatusEvent_MachineStatus_UnmetCondition struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineStatusEvent_MachineStatus_UnmetCondition) Reset() { *x = MachineStatusEvent_MachineStatus_UnmetCondition{} mi := &file_machine_machine_proto_msgTypes[173] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineStatusEvent_MachineStatus_UnmetCondition) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineStatusEvent_MachineStatus_UnmetCondition) ProtoMessage() {} func (x *MachineStatusEvent_MachineStatus_UnmetCondition) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[173] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineStatusEvent_MachineStatus_UnmetCondition.ProtoReflect.Descriptor instead. func (*MachineStatusEvent_MachineStatus_UnmetCondition) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{17, 0, 0} } func (x *MachineStatusEvent_MachineStatus_UnmetCondition) GetName() string { if x != nil { return x.Name } return "" } func (x *MachineStatusEvent_MachineStatus_UnmetCondition) GetReason() string { if x != nil { return x.Reason } return "" } type NetstatRequest_Feature struct { state protoimpl.MessageState `protogen:"open.v1"` Pid bool `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetstatRequest_Feature) Reset() { *x = NetstatRequest_Feature{} mi := &file_machine_machine_proto_msgTypes[174] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetstatRequest_Feature) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetstatRequest_Feature) ProtoMessage() {} func (x *NetstatRequest_Feature) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[174] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetstatRequest_Feature.ProtoReflect.Descriptor instead. func (*NetstatRequest_Feature) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{157, 0} } func (x *NetstatRequest_Feature) GetPid() bool { if x != nil { return x.Pid } return false } type NetstatRequest_L4Proto struct { state protoimpl.MessageState `protogen:"open.v1"` Tcp bool `protobuf:"varint,1,opt,name=tcp,proto3" json:"tcp,omitempty"` Tcp6 bool `protobuf:"varint,2,opt,name=tcp6,proto3" json:"tcp6,omitempty"` Udp bool `protobuf:"varint,3,opt,name=udp,proto3" json:"udp,omitempty"` Udp6 bool `protobuf:"varint,4,opt,name=udp6,proto3" json:"udp6,omitempty"` Udplite bool `protobuf:"varint,5,opt,name=udplite,proto3" json:"udplite,omitempty"` Udplite6 bool `protobuf:"varint,6,opt,name=udplite6,proto3" json:"udplite6,omitempty"` Raw bool `protobuf:"varint,7,opt,name=raw,proto3" json:"raw,omitempty"` Raw6 bool `protobuf:"varint,8,opt,name=raw6,proto3" json:"raw6,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetstatRequest_L4Proto) Reset() { *x = NetstatRequest_L4Proto{} mi := &file_machine_machine_proto_msgTypes[175] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetstatRequest_L4Proto) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetstatRequest_L4Proto) ProtoMessage() {} func (x *NetstatRequest_L4Proto) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[175] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetstatRequest_L4Proto.ProtoReflect.Descriptor instead. func (*NetstatRequest_L4Proto) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{157, 1} } func (x *NetstatRequest_L4Proto) GetTcp() bool { if x != nil { return x.Tcp } return false } func (x *NetstatRequest_L4Proto) GetTcp6() bool { if x != nil { return x.Tcp6 } return false } func (x *NetstatRequest_L4Proto) GetUdp() bool { if x != nil { return x.Udp } return false } func (x *NetstatRequest_L4Proto) GetUdp6() bool { if x != nil { return x.Udp6 } return false } func (x *NetstatRequest_L4Proto) GetUdplite() bool { if x != nil { return x.Udplite } return false } func (x *NetstatRequest_L4Proto) GetUdplite6() bool { if x != nil { return x.Udplite6 } return false } func (x *NetstatRequest_L4Proto) GetRaw() bool { if x != nil { return x.Raw } return false } func (x *NetstatRequest_L4Proto) GetRaw6() bool { if x != nil { return x.Raw6 } return false } type NetstatRequest_NetNS struct { state protoimpl.MessageState `protogen:"open.v1"` Hostnetwork bool `protobuf:"varint,1,opt,name=hostnetwork,proto3" json:"hostnetwork,omitempty"` Netns []string `protobuf:"bytes,2,rep,name=netns,proto3" json:"netns,omitempty"` Allnetns bool `protobuf:"varint,3,opt,name=allnetns,proto3" json:"allnetns,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NetstatRequest_NetNS) Reset() { *x = NetstatRequest_NetNS{} mi := &file_machine_machine_proto_msgTypes[176] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NetstatRequest_NetNS) String() string { return protoimpl.X.MessageStringOf(x) } func (*NetstatRequest_NetNS) ProtoMessage() {} func (x *NetstatRequest_NetNS) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[176] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NetstatRequest_NetNS.ProtoReflect.Descriptor instead. func (*NetstatRequest_NetNS) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{157, 2} } func (x *NetstatRequest_NetNS) GetHostnetwork() bool { if x != nil { return x.Hostnetwork } return false } func (x *NetstatRequest_NetNS) GetNetns() []string { if x != nil { return x.Netns } return nil } func (x *NetstatRequest_NetNS) GetAllnetns() bool { if x != nil { return x.Allnetns } return false } type ConnectRecord_Process struct { state protoimpl.MessageState `protogen:"open.v1"` Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConnectRecord_Process) Reset() { *x = ConnectRecord_Process{} mi := &file_machine_machine_proto_msgTypes[177] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConnectRecord_Process) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConnectRecord_Process) ProtoMessage() {} func (x *ConnectRecord_Process) ProtoReflect() protoreflect.Message { mi := &file_machine_machine_proto_msgTypes[177] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConnectRecord_Process.ProtoReflect.Descriptor instead. func (*ConnectRecord_Process) Descriptor() ([]byte, []int) { return file_machine_machine_proto_rawDescGZIP(), []int{158, 0} } func (x *ConnectRecord_Process) GetPid() uint32 { if x != nil { return x.Pid } return 0 } func (x *ConnectRecord_Process) GetName() string { if x != nil { return x.Name } return "" } var File_machine_machine_proto protoreflect.FileDescriptor const file_machine_machine_proto_rawDesc = "" + "\n" + "\x15machine/machine.proto\x12\amachine\x1a\x13common/common.proto\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x8c\x02\n" + "\x19ApplyConfigurationRequest\x12\x12\n" + "\x04data\x18\x01 \x01(\fR\x04data\x12;\n" + "\x04mode\x18\x04 \x01(\x0e2'.machine.ApplyConfigurationRequest.ModeR\x04mode\x12\x17\n" + "\adry_run\x18\x05 \x01(\bR\x06dryRun\x12C\n" + "\x10try_mode_timeout\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\x0etryModeTimeout\"@\n" + "\x04Mode\x12\n" + "\n" + "\x06REBOOT\x10\x00\x12\b\n" + "\x04AUTO\x10\x01\x12\r\n" + "\tNO_REBOOT\x10\x02\x12\n" + "\n" + "\x06STAGED\x10\x03\x12\a\n" + "\x03TRY\x10\x04\"\xbe\x01\n" + "\x12ApplyConfiguration\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x1a\n" + "\bwarnings\x18\x02 \x03(\tR\bwarnings\x12;\n" + "\x04mode\x18\x03 \x01(\x0e2'.machine.ApplyConfigurationRequest.ModeR\x04mode\x12!\n" + "\fmode_details\x18\x04 \x01(\tR\vmodeDetails\"U\n" + "\x1aApplyConfigurationResponse\x127\n" + "\bmessages\x18\x01 \x03(\v2\x1b.machine.ApplyConfigurationR\bmessages\"p\n" + "\rRebootRequest\x12/\n" + "\x04mode\x18\x01 \x01(\x0e2\x1b.machine.RebootRequest.ModeR\x04mode\".\n" + "\x04Mode\x12\v\n" + "\aDEFAULT\x10\x00\x12\x0e\n" + "\n" + "POWERCYCLE\x10\x01\x12\t\n" + "\x05FORCE\x10\x02\"Q\n" + "\x06Reboot\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x19\n" + "\bactor_id\x18\x02 \x01(\tR\aactorId\"=\n" + "\x0eRebootResponse\x12+\n" + "\bmessages\x18\x01 \x03(\v2\x0f.machine.RebootR\bmessages\"l\n" + "\x10BootstrapRequest\x12!\n" + "\frecover_etcd\x18\x01 \x01(\bR\vrecoverEtcd\x125\n" + "\x17recover_skip_hash_check\x18\x02 \x01(\bR\x14recoverSkipHashCheck\"9\n" + "\tBootstrap\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"C\n" + "\x11BootstrapResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.BootstrapR\bmessages\"\xb0\x01\n" + "\rSequenceEvent\x12\x1a\n" + "\bsequence\x18\x01 \x01(\tR\bsequence\x125\n" + "\x06action\x18\x02 \x01(\x0e2\x1d.machine.SequenceEvent.ActionR\x06action\x12#\n" + "\x05error\x18\x03 \x01(\v2\r.common.ErrorR\x05error\"'\n" + "\x06Action\x12\b\n" + "\x04NOOP\x10\x00\x12\t\n" + "\x05START\x10\x01\x12\b\n" + "\x04STOP\x10\x02\"u\n" + "\n" + "PhaseEvent\x12\x14\n" + "\x05phase\x18\x01 \x01(\tR\x05phase\x122\n" + "\x06action\x18\x02 \x01(\x0e2\x1a.machine.PhaseEvent.ActionR\x06action\"\x1d\n" + "\x06Action\x12\t\n" + "\x05START\x10\x00\x12\b\n" + "\x04STOP\x10\x01\"q\n" + "\tTaskEvent\x12\x12\n" + "\x04task\x18\x01 \x01(\tR\x04task\x121\n" + "\x06action\x18\x02 \x01(\x0e2\x19.machine.TaskEvent.ActionR\x06action\"\x1d\n" + "\x06Action\x12\t\n" + "\x05START\x10\x00\x12\b\n" + "\x04STOP\x10\x01\"\xba\x02\n" + "\x11ServiceStateEvent\x12\x18\n" + "\aservice\x18\x01 \x01(\tR\aservice\x129\n" + "\x06action\x18\x02 \x01(\x0e2!.machine.ServiceStateEvent.ActionR\x06action\x12\x18\n" + "\amessage\x18\x03 \x01(\tR\amessage\x12.\n" + "\x06health\x18\x04 \x01(\v2\x16.machine.ServiceHealthR\x06health\"\x85\x01\n" + "\x06Action\x12\x0f\n" + "\vINITIALIZED\x10\x00\x12\r\n" + "\tPREPARING\x10\x01\x12\v\n" + "\aWAITING\x10\x02\x12\v\n" + "\aRUNNING\x10\x03\x12\f\n" + "\bSTOPPING\x10\x04\x12\f\n" + "\bFINISHED\x10\x05\x12\n" + "\n" + "\x06FAILED\x10\x06\x12\v\n" + "\aSKIPPED\x10\a\x12\f\n" + "\bSTARTING\x10\b\" \n" + "\fRestartEvent\x12\x10\n" + "\x03cmd\x18\x01 \x01(\x03R\x03cmd\",\n" + "\x14ConfigLoadErrorEvent\x12\x14\n" + "\x05error\x18\x01 \x01(\tR\x05error\"2\n" + "\x1aConfigValidationErrorEvent\x12\x14\n" + "\x05error\x18\x01 \x01(\tR\x05error\"H\n" + "\fAddressEvent\x12\x1a\n" + "\bhostname\x18\x01 \x01(\tR\bhostname\x12\x1c\n" + "\taddresses\x18\x02 \x03(\tR\taddresses\"\xfb\x03\n" + "\x12MachineStatusEvent\x12>\n" + "\x05stage\x18\x01 \x01(\x0e2(.machine.MachineStatusEvent.MachineStageR\x05stage\x12A\n" + "\x06status\x18\x02 \x01(\v2).machine.MachineStatusEvent.MachineStatusR\x06status\x1a\xc8\x01\n" + "\rMachineStatus\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12c\n" + "\x10unmet_conditions\x18\x02 \x03(\v28.machine.MachineStatusEvent.MachineStatus.UnmetConditionR\x0funmetConditions\x1a<\n" + "\x0eUnmetCondition\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06reason\x18\x02 \x01(\tR\x06reason\"\x96\x01\n" + "\fMachineStage\x12\v\n" + "\aUNKNOWN\x10\x00\x12\v\n" + "\aBOOTING\x10\x01\x12\x0e\n" + "\n" + "INSTALLING\x10\x02\x12\x0f\n" + "\vMAINTENANCE\x10\x03\x12\v\n" + "\aRUNNING\x10\x04\x12\r\n" + "\tREBOOTING\x10\x05\x12\x11\n" + "\rSHUTTING_DOWN\x10\x06\x12\r\n" + "\tRESETTING\x10\a\x12\r\n" + "\tUPGRADING\x10\b\"\x90\x01\n" + "\rEventsRequest\x12\x1f\n" + "\vtail_events\x18\x01 \x01(\x05R\n" + "tailEvents\x12\x17\n" + "\atail_id\x18\x02 \x01(\tR\x06tailId\x12!\n" + "\ftail_seconds\x18\x03 \x01(\x05R\vtailSeconds\x12\"\n" + "\rwith_actor_id\x18\x04 \x01(\tR\vwithActorId\"\x8a\x01\n" + "\x05Event\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12(\n" + "\x04data\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x04data\x12\x0e\n" + "\x02id\x18\x03 \x01(\tR\x02id\x12\x19\n" + "\bactor_id\x18\x04 \x01(\tR\aactorId\">\n" + "\x12ResetPartitionSpec\x12\x14\n" + "\x05label\x18\x01 \x01(\tR\x05label\x12\x12\n" + "\x04wipe\x18\x02 \x01(\bR\x04wipe\"\xb1\x02\n" + "\fResetRequest\x12\x1a\n" + "\bgraceful\x18\x01 \x01(\bR\bgraceful\x12\x16\n" + "\x06reboot\x18\x02 \x01(\bR\x06reboot\x12V\n" + "\x19system_partitions_to_wipe\x18\x03 \x03(\v2\x1b.machine.ResetPartitionSpecR\x16systemPartitionsToWipe\x12+\n" + "\x12user_disks_to_wipe\x18\x04 \x03(\tR\x0fuserDisksToWipe\x122\n" + "\x04mode\x18\x05 \x01(\x0e2\x1e.machine.ResetRequest.WipeModeR\x04mode\"4\n" + "\bWipeMode\x12\a\n" + "\x03ALL\x10\x00\x12\x0f\n" + "\vSYSTEM_DISK\x10\x01\x12\x0e\n" + "\n" + "USER_DISKS\x10\x02\"P\n" + "\x05Reset\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x19\n" + "\bactor_id\x18\x02 \x01(\tR\aactorId\";\n" + "\rResetResponse\x12*\n" + "\bmessages\x18\x01 \x03(\v2\x0e.machine.ResetR\bmessages\"S\n" + "\bShutdown\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x19\n" + "\bactor_id\x18\x02 \x01(\tR\aactorId\"'\n" + "\x0fShutdownRequest\x12\x14\n" + "\x05force\x18\x01 \x01(\bR\x05force\"A\n" + "\x10ShutdownResponse\x12-\n" + "\bmessages\x18\x01 \x03(\v2\x11.machine.ShutdownR\bmessages\"\xde\x01\n" + "\x0eUpgradeRequest\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12\x1a\n" + "\bpreserve\x18\x02 \x01(\bR\bpreserve\x12\x14\n" + "\x05stage\x18\x03 \x01(\bR\x05stage\x12\x14\n" + "\x05force\x18\x04 \x01(\bR\x05force\x12C\n" + "\vreboot_mode\x18\x05 \x01(\x0e2\".machine.UpgradeRequest.RebootModeR\n" + "rebootMode\")\n" + "\n" + "RebootMode\x12\v\n" + "\aDEFAULT\x10\x00\x12\x0e\n" + "\n" + "POWERCYCLE\x10\x01\"d\n" + "\aUpgrade\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x10\n" + "\x03ack\x18\x02 \x01(\tR\x03ack\x12\x19\n" + "\bactor_id\x18\x03 \x01(\tR\aactorId\"?\n" + "\x0fUpgradeResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.UpgradeR\bmessages\"m\n" + "\vServiceList\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x120\n" + "\bservices\x18\x02 \x03(\v2\x14.machine.ServiceInfoR\bservices\"G\n" + "\x13ServiceListResponse\x120\n" + "\bmessages\x18\x01 \x03(\v2\x14.machine.ServiceListR\bmessages\"\x93\x01\n" + "\vServiceInfo\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n" + "\x05state\x18\x02 \x01(\tR\x05state\x12.\n" + "\x06events\x18\x03 \x01(\v2\x16.machine.ServiceEventsR\x06events\x12.\n" + "\x06health\x18\x04 \x01(\v2\x16.machine.ServiceHealthR\x06health\">\n" + "\rServiceEvents\x12-\n" + "\x06events\x18\x01 \x03(\v2\x15.machine.ServiceEventR\x06events\"b\n" + "\fServiceEvent\x12\x10\n" + "\x03msg\x18\x01 \x01(\tR\x03msg\x12\x14\n" + "\x05state\x18\x02 \x01(\tR\x05state\x12*\n" + "\x02ts\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x02ts\"\xa3\x01\n" + "\rServiceHealth\x12\x18\n" + "\aunknown\x18\x01 \x01(\bR\aunknown\x12\x18\n" + "\ahealthy\x18\x02 \x01(\bR\ahealthy\x12!\n" + "\flast_message\x18\x03 \x01(\tR\vlastMessage\x12;\n" + "\vlast_change\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "lastChange\"%\n" + "\x13ServiceStartRequest\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\"P\n" + "\fServiceStart\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04resp\x18\x02 \x01(\tR\x04resp\"I\n" + "\x14ServiceStartResponse\x121\n" + "\bmessages\x18\x01 \x03(\v2\x15.machine.ServiceStartR\bmessages\"$\n" + "\x12ServiceStopRequest\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\"O\n" + "\vServiceStop\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04resp\x18\x02 \x01(\tR\x04resp\"G\n" + "\x13ServiceStopResponse\x120\n" + "\bmessages\x18\x01 \x03(\v2\x14.machine.ServiceStopR\bmessages\"'\n" + "\x15ServiceRestartRequest\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\"R\n" + "\x0eServiceRestart\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04resp\x18\x02 \x01(\tR\x04resp\"M\n" + "\x16ServiceRestartResponse\x123\n" + "\bmessages\x18\x01 \x03(\v2\x17.machine.ServiceRestartR\bmessages\"*\n" + "\vCopyRequest\x12\x1b\n" + "\troot_path\x18\x01 \x01(\tR\brootPath\"\xeb\x01\n" + "\vListRequest\x12\x12\n" + "\x04root\x18\x01 \x01(\tR\x04root\x12\x18\n" + "\arecurse\x18\x02 \x01(\bR\arecurse\x12'\n" + "\x0frecursion_depth\x18\x03 \x01(\x05R\x0erecursionDepth\x12/\n" + "\x05types\x18\x04 \x03(\x0e2\x19.machine.ListRequest.TypeR\x05types\x12#\n" + "\rreport_xattrs\x18\x05 \x01(\bR\freportXattrs\"/\n" + "\x04Type\x12\v\n" + "\aREGULAR\x10\x00\x12\r\n" + "\tDIRECTORY\x10\x01\x12\v\n" + "\aSYMLINK\x10\x02\"\x81\x01\n" + "\x10DiskUsageRequest\x12'\n" + "\x0frecursion_depth\x18\x01 \x01(\x05R\x0erecursionDepth\x12\x10\n" + "\x03all\x18\x02 \x01(\bR\x03all\x12\x1c\n" + "\tthreshold\x18\x03 \x01(\x03R\tthreshold\x12\x14\n" + "\x05paths\x18\x04 \x03(\tR\x05paths\"\xc2\x02\n" + "\bFileInfo\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" + "\x04size\x18\x03 \x01(\x03R\x04size\x12\x12\n" + "\x04mode\x18\x04 \x01(\rR\x04mode\x12\x1a\n" + "\bmodified\x18\x05 \x01(\x03R\bmodified\x12\x15\n" + "\x06is_dir\x18\x06 \x01(\bR\x05isDir\x12\x14\n" + "\x05error\x18\a \x01(\tR\x05error\x12\x12\n" + "\x04link\x18\b \x01(\tR\x04link\x12#\n" + "\rrelative_name\x18\t \x01(\tR\frelativeName\x12\x10\n" + "\x03uid\x18\n" + " \x01(\rR\x03uid\x12\x10\n" + "\x03gid\x18\v \x01(\rR\x03gid\x12&\n" + "\x06xattrs\x18\f \x03(\v2\x0e.machine.XattrR\x06xattrs\"/\n" + "\x05Xattr\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04data\x18\x02 \x01(\fR\x04data\"\xa0\x01\n" + "\rDiskUsageInfo\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n" + "\x04size\x18\x03 \x01(\x03R\x04size\x12\x14\n" + "\x05error\x18\x04 \x01(\tR\x05error\x12#\n" + "\rrelative_name\x18\x05 \x01(\tR\frelativeName\"`\n" + "\x06Mounts\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12(\n" + "\x05stats\x18\x02 \x03(\v2\x12.machine.MountStatR\x05stats\"=\n" + "\x0eMountsResponse\x12+\n" + "\bmessages\x18\x01 \x03(\v2\x0f.machine.MountsR\bmessages\"|\n" + "\tMountStat\x12\x1e\n" + "\n" + "filesystem\x18\x01 \x01(\tR\n" + "filesystem\x12\x12\n" + "\x04size\x18\x02 \x01(\x04R\x04size\x12\x1c\n" + "\tavailable\x18\x03 \x01(\x04R\tavailable\x12\x1d\n" + "\n" + "mounted_on\x18\x04 \x01(\tR\tmountedOn\"\xcd\x01\n" + "\aVersion\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12.\n" + "\aversion\x18\x02 \x01(\v2\x14.machine.VersionInfoR\aversion\x121\n" + "\bplatform\x18\x03 \x01(\v2\x15.machine.PlatformInfoR\bplatform\x121\n" + "\bfeatures\x18\x04 \x01(\v2\x15.machine.FeaturesInfoR\bfeatures\"?\n" + "\x0fVersionResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.VersionR\bmessages\"\x8a\x01\n" + "\vVersionInfo\x12\x10\n" + "\x03tag\x18\x01 \x01(\tR\x03tag\x12\x10\n" + "\x03sha\x18\x02 \x01(\tR\x03sha\x12\x14\n" + "\x05built\x18\x03 \x01(\tR\x05built\x12\x1d\n" + "\n" + "go_version\x18\x04 \x01(\tR\tgoVersion\x12\x0e\n" + "\x02os\x18\x05 \x01(\tR\x02os\x12\x12\n" + "\x04arch\x18\x06 \x01(\tR\x04arch\"6\n" + "\fPlatformInfo\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04mode\x18\x02 \x01(\tR\x04mode\"\"\n" + "\fFeaturesInfo\x12\x12\n" + "\x04rbac\x18\x01 \x01(\bR\x04rbac\"\xa3\x01\n" + "\vLogsRequest\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12/\n" + "\x06driver\x18\x03 \x01(\x0e2\x17.common.ContainerDriverR\x06driver\x12\x16\n" + "\x06follow\x18\x04 \x01(\bR\x06follow\x12\x1d\n" + "\n" + "tail_lines\x18\x05 \x01(\x05R\ttailLines\"!\n" + "\vReadRequest\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\"O\n" + "\rLogsContainer\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x10\n" + "\x03ids\x18\x02 \x03(\tR\x03ids\"L\n" + "\x16LogsContainersResponse\x122\n" + "\bmessages\x18\x01 \x03(\v2\x16.machine.LogsContainerR\bmessages\"\x11\n" + "\x0fRollbackRequest\"8\n" + "\bRollback\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"A\n" + "\x10RollbackResponse\x12-\n" + "\bmessages\x18\x01 \x03(\v2\x11.machine.RollbackR\bmessages\"b\n" + "\x11ContainersRequest\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12/\n" + "\x06driver\x18\x02 \x01(\x0e2\x17.common.ContainerDriverR\x06driver\"\x88\x02\n" + "\rContainerInfo\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12\x10\n" + "\x03uid\x18\n" + " \x01(\tR\x03uid\x12\x1f\n" + "\vinternal_id\x18\t \x01(\tR\n" + "internalId\x12\x14\n" + "\x05image\x18\x03 \x01(\tR\x05image\x12\x10\n" + "\x03pid\x18\x04 \x01(\rR\x03pid\x12\x16\n" + "\x06status\x18\x05 \x01(\tR\x06status\x12\x15\n" + "\x06pod_id\x18\x06 \x01(\tR\x05podId\x12\x12\n" + "\x04name\x18\a \x01(\tR\x04name\x12+\n" + "\x11network_namespace\x18\b \x01(\tR\x10networkNamespace\"q\n" + "\tContainer\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x126\n" + "\n" + "containers\x18\x02 \x03(\v2\x16.machine.ContainerInfoR\n" + "containers\"D\n" + "\x12ContainersResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.ContainerR\bmessages\":\n" + "\fDmesgRequest\x12\x16\n" + "\x06follow\x18\x01 \x01(\bR\x06follow\x12\x12\n" + "\x04tail\x18\x02 \x01(\bR\x04tail\"A\n" + "\x11ProcessesResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.ProcessR\bmessages\"k\n" + "\aProcess\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x122\n" + "\tprocesses\x18\x02 \x03(\v2\x14.machine.ProcessInfoR\tprocesses\"\xb2\x02\n" + "\vProcessInfo\x12\x10\n" + "\x03pid\x18\x01 \x01(\x05R\x03pid\x12\x12\n" + "\x04ppid\x18\x02 \x01(\x05R\x04ppid\x12\x14\n" + "\x05state\x18\x03 \x01(\tR\x05state\x12\x18\n" + "\athreads\x18\x04 \x01(\x05R\athreads\x12\x19\n" + "\bcpu_time\x18\x05 \x01(\x01R\acpuTime\x12%\n" + "\x0evirtual_memory\x18\x06 \x01(\x04R\rvirtualMemory\x12'\n" + "\x0fresident_memory\x18\a \x01(\x04R\x0eresidentMemory\x12\x18\n" + "\acommand\x18\b \x01(\tR\acommand\x12\x1e\n" + "\n" + "executable\x18\t \x01(\tR\n" + "executable\x12\x12\n" + "\x04args\x18\n" + " \x01(\tR\x04args\x12\x14\n" + "\x05label\x18\v \x01(\tR\x05label\"o\n" + "\x0eRestartRequest\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12/\n" + "\x06driver\x18\x03 \x01(\x0e2\x17.common.ContainerDriverR\x06driver\"7\n" + "\aRestart\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"?\n" + "\x0fRestartResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.RestartR\bmessages\"]\n" + "\fStatsRequest\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12/\n" + "\x06driver\x18\x02 \x01(\x0e2\x17.common.ContainerDriverR\x06driver\"Z\n" + "\x05Stats\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12#\n" + "\x05stats\x18\x02 \x03(\v2\r.machine.StatR\x05stats\";\n" + "\rStatsResponse\x12*\n" + "\bmessages\x18\x01 \x03(\v2\x0e.machine.StatsR\bmessages\"\x9f\x01\n" + "\x04Stat\x12\x1c\n" + "\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x0e\n" + "\x02id\x18\x02 \x01(\tR\x02id\x12!\n" + "\fmemory_usage\x18\x04 \x01(\x04R\vmemoryUsage\x12\x1b\n" + "\tcpu_usage\x18\x05 \x01(\x04R\bcpuUsage\x12\x15\n" + "\x06pod_id\x18\x06 \x01(\tR\x05podId\x12\x12\n" + "\x04name\x18\a \x01(\tR\x04name\"b\n" + "\x06Memory\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12*\n" + "\ameminfo\x18\x02 \x01(\v2\x10.machine.MemInfoR\ameminfo\"=\n" + "\x0eMemoryResponse\x12+\n" + "\bmessages\x18\x01 \x03(\v2\x0f.machine.MemoryR\bmessages\"\x8b\f\n" + "\aMemInfo\x12\x1a\n" + "\bmemtotal\x18\x01 \x01(\x04R\bmemtotal\x12\x18\n" + "\amemfree\x18\x02 \x01(\x04R\amemfree\x12\"\n" + "\fmemavailable\x18\x03 \x01(\x04R\fmemavailable\x12\x18\n" + "\abuffers\x18\x04 \x01(\x04R\abuffers\x12\x16\n" + "\x06cached\x18\x05 \x01(\x04R\x06cached\x12\x1e\n" + "\n" + "swapcached\x18\x06 \x01(\x04R\n" + "swapcached\x12\x16\n" + "\x06active\x18\a \x01(\x04R\x06active\x12\x1a\n" + "\binactive\x18\b \x01(\x04R\binactive\x12\x1e\n" + "\n" + "activeanon\x18\t \x01(\x04R\n" + "activeanon\x12\"\n" + "\finactiveanon\x18\n" + " \x01(\x04R\finactiveanon\x12\x1e\n" + "\n" + "activefile\x18\v \x01(\x04R\n" + "activefile\x12\"\n" + "\finactivefile\x18\f \x01(\x04R\finactivefile\x12 \n" + "\vunevictable\x18\r \x01(\x04R\vunevictable\x12\x18\n" + "\amlocked\x18\x0e \x01(\x04R\amlocked\x12\x1c\n" + "\tswaptotal\x18\x0f \x01(\x04R\tswaptotal\x12\x1a\n" + "\bswapfree\x18\x10 \x01(\x04R\bswapfree\x12\x14\n" + "\x05dirty\x18\x11 \x01(\x04R\x05dirty\x12\x1c\n" + "\twriteback\x18\x12 \x01(\x04R\twriteback\x12\x1c\n" + "\tanonpages\x18\x13 \x01(\x04R\tanonpages\x12\x16\n" + "\x06mapped\x18\x14 \x01(\x04R\x06mapped\x12\x14\n" + "\x05shmem\x18\x15 \x01(\x04R\x05shmem\x12\x12\n" + "\x04slab\x18\x16 \x01(\x04R\x04slab\x12\"\n" + "\fsreclaimable\x18\x17 \x01(\x04R\fsreclaimable\x12\x1e\n" + "\n" + "sunreclaim\x18\x18 \x01(\x04R\n" + "sunreclaim\x12 \n" + "\vkernelstack\x18\x19 \x01(\x04R\vkernelstack\x12\x1e\n" + "\n" + "pagetables\x18\x1a \x01(\x04R\n" + "pagetables\x12 \n" + "\vnfsunstable\x18\x1b \x01(\x04R\vnfsunstable\x12\x16\n" + "\x06bounce\x18\x1c \x01(\x04R\x06bounce\x12\"\n" + "\fwritebacktmp\x18\x1d \x01(\x04R\fwritebacktmp\x12 \n" + "\vcommitlimit\x18\x1e \x01(\x04R\vcommitlimit\x12 \n" + "\vcommittedas\x18\x1f \x01(\x04R\vcommittedas\x12\"\n" + "\fvmalloctotal\x18 \x01(\x04R\fvmalloctotal\x12 \n" + "\vvmallocused\x18! \x01(\x04R\vvmallocused\x12\"\n" + "\fvmallocchunk\x18\" \x01(\x04R\fvmallocchunk\x12,\n" + "\x11hardwarecorrupted\x18# \x01(\x04R\x11hardwarecorrupted\x12$\n" + "\ranonhugepages\x18$ \x01(\x04R\ranonhugepages\x12&\n" + "\x0eshmemhugepages\x18% \x01(\x04R\x0eshmemhugepages\x12&\n" + "\x0eshmempmdmapped\x18& \x01(\x04R\x0eshmempmdmapped\x12\x1a\n" + "\bcmatotal\x18' \x01(\x04R\bcmatotal\x12\x18\n" + "\acmafree\x18( \x01(\x04R\acmafree\x12&\n" + "\x0ehugepagestotal\x18) \x01(\x04R\x0ehugepagestotal\x12$\n" + "\rhugepagesfree\x18* \x01(\x04R\rhugepagesfree\x12$\n" + "\rhugepagesrsvd\x18+ \x01(\x04R\rhugepagesrsvd\x12$\n" + "\rhugepagessurp\x18, \x01(\x04R\rhugepagessurp\x12\"\n" + "\fhugepagesize\x18- \x01(\x04R\fhugepagesize\x12 \n" + "\vdirectmap4k\x18. \x01(\x04R\vdirectmap4k\x12 \n" + "\vdirectmap2m\x18/ \x01(\x04R\vdirectmap2m\x12 \n" + "\vdirectmap1g\x180 \x01(\x04R\vdirectmap1g\"A\n" + "\x10HostnameResponse\x12-\n" + "\bmessages\x18\x01 \x03(\v2\x11.machine.HostnameR\bmessages\"T\n" + "\bHostname\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x1a\n" + "\bhostname\x18\x02 \x01(\tR\bhostname\"?\n" + "\x0fLoadAvgResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.LoadAvgR\bmessages\"{\n" + "\aLoadAvg\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x14\n" + "\x05load1\x18\x02 \x01(\x01R\x05load1\x12\x14\n" + "\x05load5\x18\x03 \x01(\x01R\x05load5\x12\x16\n" + "\x06load15\x18\x04 \x01(\x01R\x06load15\"E\n" + "\x12SystemStatResponse\x12/\n" + "\bmessages\x18\x01 \x03(\v2\x13.machine.SystemStatR\bmessages\"\xd6\x03\n" + "\n" + "SystemStat\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x1b\n" + "\tboot_time\x18\x02 \x01(\x04R\bbootTime\x12-\n" + "\tcpu_total\x18\x03 \x01(\v2\x10.machine.CPUStatR\bcpuTotal\x12\"\n" + "\x03cpu\x18\x04 \x03(\v2\x10.machine.CPUStatR\x03cpu\x12\x1b\n" + "\tirq_total\x18\x05 \x01(\x04R\birqTotal\x12\x10\n" + "\x03irq\x18\x06 \x03(\x04R\x03irq\x12)\n" + "\x10context_switches\x18\a \x01(\x04R\x0fcontextSwitches\x12'\n" + "\x0fprocess_created\x18\b \x01(\x04R\x0eprocessCreated\x12'\n" + "\x0fprocess_running\x18\t \x01(\x04R\x0eprocessRunning\x12'\n" + "\x0fprocess_blocked\x18\n" + " \x01(\x04R\x0eprocessBlocked\x12$\n" + "\x0esoft_irq_total\x18\v \x01(\x04R\fsoftIrqTotal\x12/\n" + "\bsoft_irq\x18\f \x01(\v2\x14.machine.SoftIRQStatR\asoftIrq\"\xed\x01\n" + "\aCPUStat\x12\x12\n" + "\x04user\x18\x01 \x01(\x01R\x04user\x12\x12\n" + "\x04nice\x18\x02 \x01(\x01R\x04nice\x12\x16\n" + "\x06system\x18\x03 \x01(\x01R\x06system\x12\x12\n" + "\x04idle\x18\x04 \x01(\x01R\x04idle\x12\x16\n" + "\x06iowait\x18\x05 \x01(\x01R\x06iowait\x12\x10\n" + "\x03irq\x18\x06 \x01(\x01R\x03irq\x12\x19\n" + "\bsoft_irq\x18\a \x01(\x01R\asoftIrq\x12\x14\n" + "\x05steal\x18\b \x01(\x01R\x05steal\x12\x14\n" + "\x05guest\x18\t \x01(\x01R\x05guest\x12\x1d\n" + "\n" + "guest_nice\x18\n" + " \x01(\x01R\tguestNice\"\xf7\x01\n" + "\vSoftIRQStat\x12\x0e\n" + "\x02hi\x18\x01 \x01(\x04R\x02hi\x12\x14\n" + "\x05timer\x18\x02 \x01(\x04R\x05timer\x12\x15\n" + "\x06net_tx\x18\x03 \x01(\x04R\x05netTx\x12\x15\n" + "\x06net_rx\x18\x04 \x01(\x04R\x05netRx\x12\x14\n" + "\x05block\x18\x05 \x01(\x04R\x05block\x12\"\n" + "\rblock_io_poll\x18\x06 \x01(\x04R\vblockIoPoll\x12\x18\n" + "\atasklet\x18\a \x01(\x04R\atasklet\x12\x14\n" + "\x05sched\x18\b \x01(\x04R\x05sched\x12\x18\n" + "\ahrtimer\x18\t \x01(\x04R\ahrtimer\x12\x10\n" + "\x03rcu\x18\n" + " \x01(\x04R\x03rcu\"J\n" + "\x14CPUFreqStatsResponse\x122\n" + "\bmessages\x18\x01 \x03(\v2\x16.machine.CPUsFreqStatsR\bmessages\"z\n" + "\rCPUsFreqStats\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12;\n" + "\x0ecpu_freq_stats\x18\x02 \x03(\v2\x15.machine.CPUFreqStatsR\fcpuFreqStats\"\xb1\x01\n" + "\fCPUFreqStats\x12+\n" + "\x11current_frequency\x18\x01 \x01(\x04R\x10currentFrequency\x12+\n" + "\x11minimum_frequency\x18\x02 \x01(\x04R\x10minimumFrequency\x12+\n" + "\x11maximum_frequency\x18\x03 \x01(\x04R\x10maximumFrequency\x12\x1a\n" + "\bgovernor\x18\x04 \x01(\tR\bgovernor\"@\n" + "\x0fCPUInfoResponse\x12-\n" + "\bmessages\x18\x01 \x03(\v2\x11.machine.CPUsInfoR\bmessages\"e\n" + "\bCPUsInfo\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12+\n" + "\bcpu_info\x18\x02 \x03(\v2\x10.machine.CPUInfoR\acpuInfo\"\x8b\x06\n" + "\aCPUInfo\x12\x1c\n" + "\tprocessor\x18\x01 \x01(\rR\tprocessor\x12\x1b\n" + "\tvendor_id\x18\x02 \x01(\tR\bvendorId\x12\x1d\n" + "\n" + "cpu_family\x18\x03 \x01(\tR\tcpuFamily\x12\x14\n" + "\x05model\x18\x04 \x01(\tR\x05model\x12\x1d\n" + "\n" + "model_name\x18\x05 \x01(\tR\tmodelName\x12\x1a\n" + "\bstepping\x18\x06 \x01(\tR\bstepping\x12\x1c\n" + "\tmicrocode\x18\a \x01(\tR\tmicrocode\x12\x17\n" + "\acpu_mhz\x18\b \x01(\x01R\x06cpuMhz\x12\x1d\n" + "\n" + "cache_size\x18\t \x01(\tR\tcacheSize\x12\x1f\n" + "\vphysical_id\x18\n" + " \x01(\tR\n" + "physicalId\x12\x1a\n" + "\bsiblings\x18\v \x01(\rR\bsiblings\x12\x17\n" + "\acore_id\x18\f \x01(\tR\x06coreId\x12\x1b\n" + "\tcpu_cores\x18\r \x01(\rR\bcpuCores\x12\x17\n" + "\aapic_id\x18\x0e \x01(\tR\x06apicId\x12&\n" + "\x0finitial_apic_id\x18\x0f \x01(\tR\rinitialApicId\x12\x10\n" + "\x03fpu\x18\x10 \x01(\tR\x03fpu\x12#\n" + "\rfpu_exception\x18\x11 \x01(\tR\ffpuException\x12 \n" + "\fcpu_id_level\x18\x12 \x01(\rR\n" + "cpuIdLevel\x12\x0e\n" + "\x02wp\x18\x13 \x01(\tR\x02wp\x12\x14\n" + "\x05flags\x18\x14 \x03(\tR\x05flags\x12\x12\n" + "\x04bugs\x18\x15 \x03(\tR\x04bugs\x12\x1b\n" + "\tbogo_mips\x18\x16 \x01(\x01R\bbogoMips\x12\"\n" + "\rcl_flush_size\x18\x17 \x01(\rR\vclFlushSize\x12'\n" + "\x0fcache_alignment\x18\x18 \x01(\rR\x0ecacheAlignment\x12#\n" + "\raddress_sizes\x18\x19 \x01(\tR\faddressSizes\x12)\n" + "\x10power_management\x18\x1a \x01(\tR\x0fpowerManagement\"U\n" + "\x1aNetworkDeviceStatsResponse\x127\n" + "\bmessages\x18\x01 \x03(\v2\x1b.machine.NetworkDeviceStatsR\bmessages\"\x94\x01\n" + "\x12NetworkDeviceStats\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12%\n" + "\x05total\x18\x02 \x01(\v2\x0f.machine.NetDevR\x05total\x12)\n" + "\adevices\x18\x03 \x03(\v2\x0f.machine.NetDevR\adevices\"\x86\x04\n" + "\x06NetDev\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + "\brx_bytes\x18\x02 \x01(\x04R\arxBytes\x12\x1d\n" + "\n" + "rx_packets\x18\x03 \x01(\x04R\trxPackets\x12\x1b\n" + "\trx_errors\x18\x04 \x01(\x04R\brxErrors\x12\x1d\n" + "\n" + "rx_dropped\x18\x05 \x01(\x04R\trxDropped\x12\x17\n" + "\arx_fifo\x18\x06 \x01(\x04R\x06rxFifo\x12\x19\n" + "\brx_frame\x18\a \x01(\x04R\arxFrame\x12#\n" + "\rrx_compressed\x18\b \x01(\x04R\frxCompressed\x12!\n" + "\frx_multicast\x18\t \x01(\x04R\vrxMulticast\x12\x19\n" + "\btx_bytes\x18\n" + " \x01(\x04R\atxBytes\x12\x1d\n" + "\n" + "tx_packets\x18\v \x01(\x04R\ttxPackets\x12\x1b\n" + "\ttx_errors\x18\f \x01(\x04R\btxErrors\x12\x1d\n" + "\n" + "tx_dropped\x18\r \x01(\x04R\ttxDropped\x12\x17\n" + "\atx_fifo\x18\x0e \x01(\x04R\x06txFifo\x12#\n" + "\rtx_collisions\x18\x0f \x01(\x04R\ftxCollisions\x12\x1d\n" + "\n" + "tx_carrier\x18\x10 \x01(\x04R\ttxCarrier\x12#\n" + "\rtx_compressed\x18\x11 \x01(\x04R\ftxCompressed\"C\n" + "\x11DiskStatsResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.DiskStatsR\bmessages\"\x8f\x01\n" + "\tDiskStats\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12'\n" + "\x05total\x18\x02 \x01(\v2\x11.machine.DiskStatR\x05total\x12+\n" + "\adevices\x18\x03 \x03(\v2\x11.machine.DiskStatR\adevices\"\xd8\x04\n" + "\bDiskStat\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12%\n" + "\x0eread_completed\x18\x02 \x01(\x04R\rreadCompleted\x12\x1f\n" + "\vread_merged\x18\x03 \x01(\x04R\n" + "readMerged\x12!\n" + "\fread_sectors\x18\x04 \x01(\x04R\vreadSectors\x12 \n" + "\fread_time_ms\x18\x05 \x01(\x04R\n" + "readTimeMs\x12'\n" + "\x0fwrite_completed\x18\x06 \x01(\x04R\x0ewriteCompleted\x12!\n" + "\fwrite_merged\x18\a \x01(\x04R\vwriteMerged\x12#\n" + "\rwrite_sectors\x18\b \x01(\x04R\fwriteSectors\x12\"\n" + "\rwrite_time_ms\x18\t \x01(\x04R\vwriteTimeMs\x12$\n" + "\x0eio_in_progress\x18\n" + " \x01(\x04R\fioInProgress\x12\x1c\n" + "\n" + "io_time_ms\x18\v \x01(\x04R\bioTimeMs\x12-\n" + "\x13io_time_weighted_ms\x18\f \x01(\x04R\x10ioTimeWeightedMs\x12+\n" + "\x11discard_completed\x18\r \x01(\x04R\x10discardCompleted\x12%\n" + "\x0ediscard_merged\x18\x0e \x01(\x04R\rdiscardMerged\x12'\n" + "\x0fdiscard_sectors\x18\x0f \x01(\x04R\x0ediscardSectors\x12&\n" + "\x0fdiscard_time_ms\x18\x10 \x01(\x04R\rdiscardTimeMs\"\x19\n" + "\x17EtcdLeaveClusterRequest\"@\n" + "\x10EtcdLeaveCluster\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"Q\n" + "\x18EtcdLeaveClusterResponse\x125\n" + "\bmessages\x18\x01 \x03(\v2\x19.machine.EtcdLeaveClusterR\bmessages\"1\n" + "\x17EtcdRemoveMemberRequest\x12\x16\n" + "\x06member\x18\x01 \x01(\tR\x06member\"@\n" + "\x10EtcdRemoveMember\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"Q\n" + "\x18EtcdRemoveMemberResponse\x125\n" + "\bmessages\x18\x01 \x03(\v2\x19.machine.EtcdRemoveMemberR\bmessages\":\n" + "\x1bEtcdRemoveMemberByIDRequest\x12\x1b\n" + "\tmember_id\x18\x01 \x01(\x04R\bmemberId\"D\n" + "\x14EtcdRemoveMemberByID\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"Y\n" + "\x1cEtcdRemoveMemberByIDResponse\x129\n" + "\bmessages\x18\x01 \x03(\v2\x1d.machine.EtcdRemoveMemberByIDR\bmessages\"\x1e\n" + "\x1cEtcdForfeitLeadershipRequest\"]\n" + "\x15EtcdForfeitLeadership\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x16\n" + "\x06member\x18\x02 \x01(\tR\x06member\"[\n" + "\x1dEtcdForfeitLeadershipResponse\x12:\n" + "\bmessages\x18\x01 \x03(\v2\x1e.machine.EtcdForfeitLeadershipR\bmessages\"8\n" + "\x15EtcdMemberListRequest\x12\x1f\n" + "\vquery_local\x18\x01 \x01(\bR\n" + "queryLocal\"\x95\x01\n" + "\n" + "EtcdMember\x12\x0e\n" + "\x02id\x18\x02 \x01(\x04R\x02id\x12\x1a\n" + "\bhostname\x18\x03 \x01(\tR\bhostname\x12\x1b\n" + "\tpeer_urls\x18\x04 \x03(\tR\bpeerUrls\x12\x1f\n" + "\vclient_urls\x18\x05 \x03(\tR\n" + "clientUrls\x12\x1d\n" + "\n" + "is_learner\x18\x06 \x01(\bR\tisLearner\"\x91\x01\n" + "\vEtcdMembers\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12%\n" + "\x0elegacy_members\x18\x02 \x03(\tR\rlegacyMembers\x12-\n" + "\amembers\x18\x03 \x03(\v2\x13.machine.EtcdMemberR\amembers\"J\n" + "\x16EtcdMemberListResponse\x120\n" + "\bmessages\x18\x01 \x03(\v2\x14.machine.EtcdMembersR\bmessages\"\x15\n" + "\x13EtcdSnapshotRequest\";\n" + "\vEtcdRecover\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"G\n" + "\x13EtcdRecoverResponse\x120\n" + "\bmessages\x18\x01 \x03(\v2\x14.machine.EtcdRecoverR\bmessages\"G\n" + "\x15EtcdAlarmListResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.EtcdAlarmR\bmessages\"x\n" + "\tEtcdAlarm\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12=\n" + "\rmember_alarms\x18\x02 \x03(\v2\x18.machine.EtcdMemberAlarmR\fmemberAlarms\"\x99\x01\n" + "\x0fEtcdMemberAlarm\x12\x1b\n" + "\tmember_id\x18\x01 \x01(\x04R\bmemberId\x128\n" + "\x05alarm\x18\x02 \x01(\x0e2\".machine.EtcdMemberAlarm.AlarmTypeR\x05alarm\"/\n" + "\tAlarmType\x12\b\n" + "\x04NONE\x10\x00\x12\v\n" + "\aNOSPACE\x10\x01\x12\v\n" + "\aCORRUPT\x10\x02\"O\n" + "\x17EtcdAlarmDisarmResponse\x124\n" + "\bmessages\x18\x01 \x03(\v2\x18.machine.EtcdAlarmDisarmR\bmessages\"~\n" + "\x0fEtcdAlarmDisarm\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12=\n" + "\rmember_alarms\x18\x02 \x03(\v2\x18.machine.EtcdMemberAlarmR\fmemberAlarms\"M\n" + "\x16EtcdDefragmentResponse\x123\n" + "\bmessages\x18\x01 \x03(\v2\x17.machine.EtcdDefragmentR\bmessages\">\n" + "\x0eEtcdDefragment\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"E\n" + "\x12EtcdStatusResponse\x12/\n" + "\bmessages\x18\x01 \x03(\v2\x13.machine.EtcdStatusR\bmessages\"z\n" + "\n" + "EtcdStatus\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12>\n" + "\rmember_status\x18\x02 \x01(\v2\x19.machine.EtcdMemberStatusR\fmemberStatus\"\xfa\x02\n" + "\x10EtcdMemberStatus\x12'\n" + "\x0fstorage_version\x18\v \x01(\tR\x0estorageVersion\x12\x1b\n" + "\tmember_id\x18\n" + " \x01(\x04R\bmemberId\x12)\n" + "\x10protocol_version\x18\x01 \x01(\tR\x0fprotocolVersion\x12\x17\n" + "\adb_size\x18\x02 \x01(\x03R\x06dbSize\x12#\n" + "\x0edb_size_in_use\x18\x03 \x01(\x03R\vdbSizeInUse\x12\x16\n" + "\x06leader\x18\x04 \x01(\x04R\x06leader\x12\x1d\n" + "\n" + "raft_index\x18\x05 \x01(\x04R\traftIndex\x12\x1b\n" + "\traft_term\x18\x06 \x01(\x04R\braftTerm\x12,\n" + "\x12raft_applied_index\x18\a \x01(\x04R\x10raftAppliedIndex\x12\x16\n" + "\x06errors\x18\b \x03(\tR\x06errors\x12\x1d\n" + "\n" + "is_learner\x18\t \x01(\bR\tisLearner\"8\n" + "\x1cEtcdDowngradeValidateRequest\x12\x18\n" + "\aversion\x18\x01 \x01(\tR\aversion\"[\n" + "\x1dEtcdDowngradeValidateResponse\x12:\n" + "\bmessages\x18\x01 \x03(\v2\x1e.machine.EtcdDowngradeValidateR\bmessages\"\x91\x01\n" + "\x15EtcdDowngradeValidate\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12J\n" + "\x11cluster_downgrade\x18\x02 \x01(\v2\x1d.machine.EtcdClusterDowngradeR\x10clusterDowngrade\"6\n" + "\x1aEtcdDowngradeEnableRequest\x12\x18\n" + "\aversion\x18\x01 \x01(\tR\aversion\"W\n" + "\x1bEtcdDowngradeEnableResponse\x128\n" + "\bmessages\x18\x01 \x03(\v2\x1c.machine.EtcdDowngradeEnableR\bmessages\"\x8f\x01\n" + "\x13EtcdDowngradeEnable\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12J\n" + "\x11cluster_downgrade\x18\x02 \x01(\v2\x1d.machine.EtcdClusterDowngradeR\x10clusterDowngrade\"W\n" + "\x1bEtcdDowngradeCancelResponse\x128\n" + "\bmessages\x18\x01 \x03(\v2\x1c.machine.EtcdDowngradeCancelR\bmessages\"\x8f\x01\n" + "\x13EtcdDowngradeCancel\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12J\n" + "\x11cluster_downgrade\x18\x02 \x01(\v2\x1d.machine.EtcdClusterDowngradeR\x10clusterDowngrade\"?\n" + "\x14EtcdClusterDowngrade\x12'\n" + "\x0fcluster_version\x18\x01 \x01(\tR\x0eclusterVersion\"Y\n" + "\vRouteConfig\x12\x18\n" + "\anetwork\x18\x01 \x01(\tR\anetwork\x12\x18\n" + "\agateway\x18\x02 \x01(\tR\agateway\x12\x16\n" + "\x06metric\x18\x03 \x01(\rR\x06metric\"6\n" + "\x11DHCPOptionsConfig\x12!\n" + "\froute_metric\x18\x01 \x01(\rR\vrouteMetric\"\xf2\x01\n" + "\x13NetworkDeviceConfig\x12\x1c\n" + "\tinterface\x18\x01 \x01(\tR\tinterface\x12\x12\n" + "\x04cidr\x18\x02 \x01(\tR\x04cidr\x12\x10\n" + "\x03mtu\x18\x03 \x01(\x05R\x03mtu\x12\x12\n" + "\x04dhcp\x18\x04 \x01(\bR\x04dhcp\x12\x16\n" + "\x06ignore\x18\x05 \x01(\bR\x06ignore\x12=\n" + "\fdhcp_options\x18\x06 \x01(\v2\x1a.machine.DHCPOptionsConfigR\vdhcpOptions\x12,\n" + "\x06routes\x18\a \x03(\v2\x14.machine.RouteConfigR\x06routes\"i\n" + "\rNetworkConfig\x12\x1a\n" + "\bhostname\x18\x01 \x01(\tR\bhostname\x12<\n" + "\n" + "interfaces\x18\x02 \x03(\v2\x1c.machine.NetworkDeviceConfigR\n" + "interfaces\"W\n" + "\rInstallConfig\x12!\n" + "\finstall_disk\x18\x01 \x01(\tR\vinstallDisk\x12#\n" + "\rinstall_image\x18\x02 \x01(\tR\finstallImage\"\xcd\x02\n" + "\rMachineConfig\x126\n" + "\x04type\x18\x01 \x01(\x0e2\".machine.MachineConfig.MachineTypeR\x04type\x12=\n" + "\x0einstall_config\x18\x02 \x01(\v2\x16.machine.InstallConfigR\rinstallConfig\x12=\n" + "\x0enetwork_config\x18\x03 \x01(\v2\x16.machine.NetworkConfigR\rnetworkConfig\x12-\n" + "\x12kubernetes_version\x18\x04 \x01(\tR\x11kubernetesVersion\"W\n" + "\vMachineType\x12\x10\n" + "\fTYPE_UNKNOWN\x10\x00\x12\r\n" + "\tTYPE_INIT\x10\x01\x12\x16\n" + "\x12TYPE_CONTROL_PLANE\x10\x02\x12\x0f\n" + "\vTYPE_WORKER\x10\x03\"0\n" + "\x12ControlPlaneConfig\x12\x1a\n" + "\bendpoint\x18\x01 \x01(\tR\bendpoint\"3\n" + "\tCNIConfig\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n" + "\x04urls\x18\x02 \x03(\tR\x04urls\"h\n" + "\x14ClusterNetworkConfig\x12\x1d\n" + "\n" + "dns_domain\x18\x01 \x01(\tR\tdnsDomain\x121\n" + "\n" + "cni_config\x18\x02 \x01(\v2\x12.machine.CNIConfigR\tcniConfig\"\xf9\x01\n" + "\rClusterConfig\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12@\n" + "\rcontrol_plane\x18\x02 \x01(\v2\x1b.machine.ControlPlaneConfigR\fcontrolPlane\x12F\n" + "\x0fcluster_network\x18\x03 \x01(\v2\x1d.machine.ClusterNetworkConfigR\x0eclusterNetwork\x12J\n" + "\"allow_scheduling_on_control_planes\x18\x04 \x01(\bR\x1eallowSchedulingOnControlPlanes\"n\n" + "\"GenerateClientConfigurationRequest\x12\x14\n" + "\x05roles\x18\x01 \x03(\tR\x05roles\x122\n" + "\acrt_ttl\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\x06crtTtl\"\xa1\x01\n" + "\x1bGenerateClientConfiguration\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x0e\n" + "\x02ca\x18\x02 \x01(\fR\x02ca\x12\x10\n" + "\x03crt\x18\x03 \x01(\fR\x03crt\x12\x10\n" + "\x03key\x18\x04 \x01(\fR\x03key\x12 \n" + "\vtalosconfig\x18\x05 \x01(\fR\vtalosconfig\"g\n" + "#GenerateClientConfigurationResponse\x12@\n" + "\bmessages\x18\x01 \x03(\v2$.machine.GenerateClientConfigurationR\bmessages\"\xa9\x01\n" + "\x14PacketCaptureRequest\x12\x1c\n" + "\tinterface\x18\x01 \x01(\tR\tinterface\x12 \n" + "\vpromiscuous\x18\x02 \x01(\bR\vpromiscuous\x12\x19\n" + "\bsnap_len\x18\x03 \x01(\rR\asnapLen\x126\n" + "\n" + "bpf_filter\x18\x04 \x03(\v2\x17.machine.BPFInstructionR\tbpfFilter\"N\n" + "\x0eBPFInstruction\x12\x0e\n" + "\x02op\x18\x01 \x01(\rR\x02op\x12\x0e\n" + "\x02jt\x18\x02 \x01(\rR\x02jt\x12\x0e\n" + "\x02jf\x18\x03 \x01(\rR\x02jf\x12\f\n" + "\x01k\x18\x04 \x01(\rR\x01k\"\xd2\x04\n" + "\x0eNetstatRequest\x126\n" + "\x06filter\x18\x01 \x01(\x0e2\x1e.machine.NetstatRequest.FilterR\x06filter\x129\n" + "\afeature\x18\x02 \x01(\v2\x1f.machine.NetstatRequest.FeatureR\afeature\x129\n" + "\al4proto\x18\x03 \x01(\v2\x1f.machine.NetstatRequest.L4protoR\al4proto\x123\n" + "\x05netns\x18\x04 \x01(\v2\x1d.machine.NetstatRequest.NetNSR\x05netns\x1a\x1b\n" + "\aFeature\x12\x10\n" + "\x03pid\x18\x01 \x01(\bR\x03pid\x1a\xb1\x01\n" + "\aL4proto\x12\x10\n" + "\x03tcp\x18\x01 \x01(\bR\x03tcp\x12\x12\n" + "\x04tcp6\x18\x02 \x01(\bR\x04tcp6\x12\x10\n" + "\x03udp\x18\x03 \x01(\bR\x03udp\x12\x12\n" + "\x04udp6\x18\x04 \x01(\bR\x04udp6\x12\x18\n" + "\audplite\x18\x05 \x01(\bR\audplite\x12\x1a\n" + "\budplite6\x18\x06 \x01(\bR\budplite6\x12\x10\n" + "\x03raw\x18\a \x01(\bR\x03raw\x12\x12\n" + "\x04raw6\x18\b \x01(\bR\x04raw6\x1a[\n" + "\x05NetNS\x12 \n" + "\vhostnetwork\x18\x01 \x01(\bR\vhostnetwork\x12\x14\n" + "\x05netns\x18\x02 \x03(\tR\x05netns\x12\x1a\n" + "\ballnetns\x18\x03 \x01(\bR\ballnetns\"/\n" + "\x06Filter\x12\a\n" + "\x03ALL\x10\x00\x12\r\n" + "\tCONNECTED\x10\x01\x12\r\n" + "\tLISTENING\x10\x02\"\xdc\x06\n" + "\rConnectRecord\x12\x18\n" + "\al4proto\x18\x01 \x01(\tR\al4proto\x12\x18\n" + "\alocalip\x18\x02 \x01(\tR\alocalip\x12\x1c\n" + "\tlocalport\x18\x03 \x01(\rR\tlocalport\x12\x1a\n" + "\bremoteip\x18\x04 \x01(\tR\bremoteip\x12\x1e\n" + "\n" + "remoteport\x18\x05 \x01(\rR\n" + "remoteport\x122\n" + "\x05state\x18\x06 \x01(\x0e2\x1c.machine.ConnectRecord.StateR\x05state\x12\x18\n" + "\atxqueue\x18\a \x01(\x04R\atxqueue\x12\x18\n" + "\arxqueue\x18\b \x01(\x04R\arxqueue\x122\n" + "\x02tr\x18\t \x01(\x0e2\".machine.ConnectRecord.TimerActiveR\x02tr\x12\x1c\n" + "\ttimerwhen\x18\n" + " \x01(\x04R\ttimerwhen\x12\x1a\n" + "\bretrnsmt\x18\v \x01(\x04R\bretrnsmt\x12\x10\n" + "\x03uid\x18\f \x01(\rR\x03uid\x12\x18\n" + "\atimeout\x18\r \x01(\x04R\atimeout\x12\x14\n" + "\x05inode\x18\x0e \x01(\x04R\x05inode\x12\x10\n" + "\x03ref\x18\x0f \x01(\x04R\x03ref\x12\x18\n" + "\apointer\x18\x10 \x01(\x04R\apointer\x128\n" + "\aprocess\x18\x11 \x01(\v2\x1e.machine.ConnectRecord.ProcessR\aprocess\x12\x14\n" + "\x05netns\x18\x12 \x01(\tR\x05netns\x1a/\n" + "\aProcess\x12\x10\n" + "\x03pid\x18\x01 \x01(\rR\x03pid\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\"\xaf\x01\n" + "\x05State\x12\f\n" + "\bRESERVED\x10\x00\x12\x0f\n" + "\vESTABLISHED\x10\x01\x12\f\n" + "\bSYN_SENT\x10\x02\x12\f\n" + "\bSYN_RECV\x10\x03\x12\r\n" + "\tFIN_WAIT1\x10\x04\x12\r\n" + "\tFIN_WAIT2\x10\x05\x12\r\n" + "\tTIME_WAIT\x10\x06\x12\t\n" + "\x05CLOSE\x10\a\x12\r\n" + "\tCLOSEWAIT\x10\b\x12\v\n" + "\aLASTACK\x10\t\x12\n" + "\n" + "\x06LISTEN\x10\n" + "\x12\v\n" + "\aCLOSING\x10\v\"F\n" + "\vTimerActive\x12\a\n" + "\x03OFF\x10\x00\x12\x06\n" + "\x02ON\x10\x01\x12\r\n" + "\tKEEPALIVE\x10\x02\x12\f\n" + "\bTIMEWAIT\x10\x03\x12\t\n" + "\x05PROBE\x10\x04\"u\n" + "\aNetstat\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12<\n" + "\rconnectrecord\x18\x02 \x03(\v2\x16.machine.ConnectRecordR\rconnectrecord\"?\n" + "\x0fNetstatResponse\x12,\n" + "\bmessages\x18\x01 \x03(\v2\x10.machine.NetstatR\bmessages\":\n" + "\x10MetaWriteRequest\x12\x10\n" + "\x03key\x18\x01 \x01(\rR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\fR\x05value\"9\n" + "\tMetaWrite\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"C\n" + "\x11MetaWriteResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.MetaWriteR\bmessages\"%\n" + "\x11MetaDeleteRequest\x12\x10\n" + "\x03key\x18\x01 \x01(\rR\x03key\":\n" + "\n" + "MetaDelete\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"E\n" + "\x12MetaDeleteResponse\x12/\n" + "\bmessages\x18\x01 \x03(\v2\x13.machine.MetaDeleteR\bmessages\"M\n" + "\x10ImageListRequest\x129\n" + "\tnamespace\x18\x01 \x01(\x0e2\x1b.common.ContainerdNamespaceR\tnamespace\"\xbc\x01\n" + "\x11ImageListResponse\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" + "\x06digest\x18\x03 \x01(\tR\x06digest\x12\x12\n" + "\x04size\x18\x04 \x01(\x03R\x04size\x129\n" + "\n" + "created_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"k\n" + "\x10ImagePullRequest\x129\n" + "\tnamespace\x18\x01 \x01(\x0e2\x1b.common.ContainerdNamespaceR\tnamespace\x12\x1c\n" + "\treference\x18\x02 \x01(\tR\treference\"9\n" + "\tImagePull\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\"C\n" + "\x11ImagePullResponse\x12.\n" + "\bmessages\x18\x01 \x03(\v2\x12.machine.ImagePullR\bmessages2\xef\x1d\n" + "\x0eMachineService\x12]\n" + "\x12ApplyConfiguration\x12\".machine.ApplyConfigurationRequest\x1a#.machine.ApplyConfigurationResponse\x12B\n" + "\tBootstrap\x12\x19.machine.BootstrapRequest\x1a\x1a.machine.BootstrapResponse\x12E\n" + "\n" + "Containers\x12\x1a.machine.ContainersRequest\x1a\x1b.machine.ContainersResponse\x12,\n" + "\x04Copy\x12\x14.machine.CopyRequest\x1a\f.common.Data0\x01\x12E\n" + "\fCPUFreqStats\x12\x16.google.protobuf.Empty\x1a\x1d.machine.CPUFreqStatsResponse\x12;\n" + "\aCPUInfo\x12\x16.google.protobuf.Empty\x1a\x18.machine.CPUInfoResponse\x12?\n" + "\tDiskStats\x12\x16.google.protobuf.Empty\x1a\x1a.machine.DiskStatsResponse\x12.\n" + "\x05Dmesg\x12\x15.machine.DmesgRequest\x1a\f.common.Data0\x01\x122\n" + "\x06Events\x12\x16.machine.EventsRequest\x1a\x0e.machine.Event0\x01\x12Q\n" + "\x0eEtcdMemberList\x12\x1e.machine.EtcdMemberListRequest\x1a\x1f.machine.EtcdMemberListResponse\x12c\n" + "\x14EtcdRemoveMemberByID\x12$.machine.EtcdRemoveMemberByIDRequest\x1a%.machine.EtcdRemoveMemberByIDResponse\x12W\n" + "\x10EtcdLeaveCluster\x12 .machine.EtcdLeaveClusterRequest\x1a!.machine.EtcdLeaveClusterResponse\x12f\n" + "\x15EtcdForfeitLeadership\x12%.machine.EtcdForfeitLeadershipRequest\x1a&.machine.EtcdForfeitLeadershipResponse\x12;\n" + "\vEtcdRecover\x12\f.common.Data\x1a\x1c.machine.EtcdRecoverResponse(\x01\x12<\n" + "\fEtcdSnapshot\x12\x1c.machine.EtcdSnapshotRequest\x1a\f.common.Data0\x01\x12G\n" + "\rEtcdAlarmList\x12\x16.google.protobuf.Empty\x1a\x1e.machine.EtcdAlarmListResponse\x12K\n" + "\x0fEtcdAlarmDisarm\x12\x16.google.protobuf.Empty\x1a .machine.EtcdAlarmDisarmResponse\x12I\n" + "\x0eEtcdDefragment\x12\x16.google.protobuf.Empty\x1a\x1f.machine.EtcdDefragmentResponse\x12A\n" + "\n" + "EtcdStatus\x12\x16.google.protobuf.Empty\x1a\x1b.machine.EtcdStatusResponse\x12f\n" + "\x15EtcdDowngradeValidate\x12%.machine.EtcdDowngradeValidateRequest\x1a&.machine.EtcdDowngradeValidateResponse\x12`\n" + "\x13EtcdDowngradeEnable\x12#.machine.EtcdDowngradeEnableRequest\x1a$.machine.EtcdDowngradeEnableResponse\x12S\n" + "\x13EtcdDowngradeCancel\x12\x16.google.protobuf.Empty\x1a$.machine.EtcdDowngradeCancelResponse\x12=\n" + "\bHostname\x12\x16.google.protobuf.Empty\x1a\x19.machine.HostnameResponse\x124\n" + "\n" + "Kubeconfig\x12\x16.google.protobuf.Empty\x1a\f.common.Data0\x01\x121\n" + "\x04List\x12\x14.machine.ListRequest\x1a\x11.machine.FileInfo0\x01\x12@\n" + "\tDiskUsage\x12\x19.machine.DiskUsageRequest\x1a\x16.machine.DiskUsageInfo0\x01\x12;\n" + "\aLoadAvg\x12\x16.google.protobuf.Empty\x1a\x18.machine.LoadAvgResponse\x12,\n" + "\x04Logs\x12\x14.machine.LogsRequest\x1a\f.common.Data0\x01\x12I\n" + "\x0eLogsContainers\x12\x16.google.protobuf.Empty\x1a\x1f.machine.LogsContainersResponse\x129\n" + "\x06Memory\x12\x16.google.protobuf.Empty\x1a\x17.machine.MemoryResponse\x129\n" + "\x06Mounts\x12\x16.google.protobuf.Empty\x1a\x17.machine.MountsResponse\x12Q\n" + "\x12NetworkDeviceStats\x12\x16.google.protobuf.Empty\x1a#.machine.NetworkDeviceStatsResponse\x12?\n" + "\tProcesses\x12\x16.google.protobuf.Empty\x1a\x1a.machine.ProcessesResponse\x12,\n" + "\x04Read\x12\x14.machine.ReadRequest\x1a\f.common.Data0\x01\x129\n" + "\x06Reboot\x12\x16.machine.RebootRequest\x1a\x17.machine.RebootResponse\x12<\n" + "\aRestart\x12\x17.machine.RestartRequest\x1a\x18.machine.RestartResponse\x12?\n" + "\bRollback\x12\x18.machine.RollbackRequest\x1a\x19.machine.RollbackResponse\x126\n" + "\x05Reset\x12\x15.machine.ResetRequest\x1a\x16.machine.ResetResponse\x12C\n" + "\vServiceList\x12\x16.google.protobuf.Empty\x1a\x1c.machine.ServiceListResponse\x12Q\n" + "\x0eServiceRestart\x12\x1e.machine.ServiceRestartRequest\x1a\x1f.machine.ServiceRestartResponse\x12K\n" + "\fServiceStart\x12\x1c.machine.ServiceStartRequest\x1a\x1d.machine.ServiceStartResponse\x12H\n" + "\vServiceStop\x12\x1b.machine.ServiceStopRequest\x1a\x1c.machine.ServiceStopResponse\x12?\n" + "\bShutdown\x12\x18.machine.ShutdownRequest\x1a\x19.machine.ShutdownResponse\x126\n" + "\x05Stats\x12\x15.machine.StatsRequest\x1a\x16.machine.StatsResponse\x12A\n" + "\n" + "SystemStat\x12\x16.google.protobuf.Empty\x1a\x1b.machine.SystemStatResponse\x12J\n" + "\aUpgrade\x12\x17.machine.UpgradeRequest\x1a\x18.machine.UpgradeResponse\"\f\xea\xbb-\x05v1.18\x88\x02\x01\x12;\n" + "\aVersion\x12\x16.google.protobuf.Empty\x1a\x18.machine.VersionResponse\x12x\n" + "\x1bGenerateClientConfiguration\x12+.machine.GenerateClientConfigurationRequest\x1a,.machine.GenerateClientConfigurationResponse\x12>\n" + "\rPacketCapture\x12\x1d.machine.PacketCaptureRequest\x1a\f.common.Data0\x01\x12<\n" + "\aNetstat\x12\x17.machine.NetstatRequest\x1a\x18.machine.NetstatResponse\x12B\n" + "\tMetaWrite\x12\x19.machine.MetaWriteRequest\x1a\x1a.machine.MetaWriteResponse\x12E\n" + "\n" + "MetaDelete\x12\x1a.machine.MetaDeleteRequest\x1a\x1b.machine.MetaDeleteResponse\x12R\n" + "\tImageList\x12\x19.machine.ImageListRequest\x1a\x1a.machine.ImageListResponse\"\f\xea\xbb-\x05v1.18\x88\x02\x010\x01\x12P\n" + "\tImagePull\x12\x19.machine.ImagePullRequest\x1a\x1a.machine.ImagePullResponse\"\f\xea\xbb-\x05v1.18\x88\x02\x01BN\n" + "\x15dev.talos.api.machineZ5github.com/siderolabs/talos/pkg/machinery/api/machineb\x06proto3" var ( file_machine_machine_proto_rawDescOnce sync.Once file_machine_machine_proto_rawDescData []byte ) func file_machine_machine_proto_rawDescGZIP() []byte { file_machine_machine_proto_rawDescOnce.Do(func() { file_machine_machine_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_machine_machine_proto_rawDesc), len(file_machine_machine_proto_rawDesc))) }) return file_machine_machine_proto_rawDescData } var file_machine_machine_proto_enumTypes = make([]protoimpl.EnumInfo, 15) var file_machine_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 178) var file_machine_machine_proto_goTypes = []any{ (ApplyConfigurationRequest_Mode)(0), // 0: machine.ApplyConfigurationRequest.Mode (RebootRequest_Mode)(0), // 1: machine.RebootRequest.Mode (SequenceEvent_Action)(0), // 2: machine.SequenceEvent.Action (PhaseEvent_Action)(0), // 3: machine.PhaseEvent.Action (TaskEvent_Action)(0), // 4: machine.TaskEvent.Action (ServiceStateEvent_Action)(0), // 5: machine.ServiceStateEvent.Action (MachineStatusEvent_MachineStage)(0), // 6: machine.MachineStatusEvent.MachineStage (ResetRequest_WipeMode)(0), // 7: machine.ResetRequest.WipeMode (UpgradeRequest_RebootMode)(0), // 8: machine.UpgradeRequest.RebootMode (ListRequest_Type)(0), // 9: machine.ListRequest.Type (EtcdMemberAlarm_AlarmType)(0), // 10: machine.EtcdMemberAlarm.AlarmType (MachineConfig_MachineType)(0), // 11: machine.MachineConfig.MachineType (NetstatRequest_Filter)(0), // 12: machine.NetstatRequest.Filter (ConnectRecord_State)(0), // 13: machine.ConnectRecord.State (ConnectRecord_TimerActive)(0), // 14: machine.ConnectRecord.TimerActive (*ApplyConfigurationRequest)(nil), // 15: machine.ApplyConfigurationRequest (*ApplyConfiguration)(nil), // 16: machine.ApplyConfiguration (*ApplyConfigurationResponse)(nil), // 17: machine.ApplyConfigurationResponse (*RebootRequest)(nil), // 18: machine.RebootRequest (*Reboot)(nil), // 19: machine.Reboot (*RebootResponse)(nil), // 20: machine.RebootResponse (*BootstrapRequest)(nil), // 21: machine.BootstrapRequest (*Bootstrap)(nil), // 22: machine.Bootstrap (*BootstrapResponse)(nil), // 23: machine.BootstrapResponse (*SequenceEvent)(nil), // 24: machine.SequenceEvent (*PhaseEvent)(nil), // 25: machine.PhaseEvent (*TaskEvent)(nil), // 26: machine.TaskEvent (*ServiceStateEvent)(nil), // 27: machine.ServiceStateEvent (*RestartEvent)(nil), // 28: machine.RestartEvent (*ConfigLoadErrorEvent)(nil), // 29: machine.ConfigLoadErrorEvent (*ConfigValidationErrorEvent)(nil), // 30: machine.ConfigValidationErrorEvent (*AddressEvent)(nil), // 31: machine.AddressEvent (*MachineStatusEvent)(nil), // 32: machine.MachineStatusEvent (*EventsRequest)(nil), // 33: machine.EventsRequest (*Event)(nil), // 34: machine.Event (*ResetPartitionSpec)(nil), // 35: machine.ResetPartitionSpec (*ResetRequest)(nil), // 36: machine.ResetRequest (*Reset)(nil), // 37: machine.Reset (*ResetResponse)(nil), // 38: machine.ResetResponse (*Shutdown)(nil), // 39: machine.Shutdown (*ShutdownRequest)(nil), // 40: machine.ShutdownRequest (*ShutdownResponse)(nil), // 41: machine.ShutdownResponse (*UpgradeRequest)(nil), // 42: machine.UpgradeRequest (*Upgrade)(nil), // 43: machine.Upgrade (*UpgradeResponse)(nil), // 44: machine.UpgradeResponse (*ServiceList)(nil), // 45: machine.ServiceList (*ServiceListResponse)(nil), // 46: machine.ServiceListResponse (*ServiceInfo)(nil), // 47: machine.ServiceInfo (*ServiceEvents)(nil), // 48: machine.ServiceEvents (*ServiceEvent)(nil), // 49: machine.ServiceEvent (*ServiceHealth)(nil), // 50: machine.ServiceHealth (*ServiceStartRequest)(nil), // 51: machine.ServiceStartRequest (*ServiceStart)(nil), // 52: machine.ServiceStart (*ServiceStartResponse)(nil), // 53: machine.ServiceStartResponse (*ServiceStopRequest)(nil), // 54: machine.ServiceStopRequest (*ServiceStop)(nil), // 55: machine.ServiceStop (*ServiceStopResponse)(nil), // 56: machine.ServiceStopResponse (*ServiceRestartRequest)(nil), // 57: machine.ServiceRestartRequest (*ServiceRestart)(nil), // 58: machine.ServiceRestart (*ServiceRestartResponse)(nil), // 59: machine.ServiceRestartResponse (*CopyRequest)(nil), // 60: machine.CopyRequest (*ListRequest)(nil), // 61: machine.ListRequest (*DiskUsageRequest)(nil), // 62: machine.DiskUsageRequest (*FileInfo)(nil), // 63: machine.FileInfo (*Xattr)(nil), // 64: machine.Xattr (*DiskUsageInfo)(nil), // 65: machine.DiskUsageInfo (*Mounts)(nil), // 66: machine.Mounts (*MountsResponse)(nil), // 67: machine.MountsResponse (*MountStat)(nil), // 68: machine.MountStat (*Version)(nil), // 69: machine.Version (*VersionResponse)(nil), // 70: machine.VersionResponse (*VersionInfo)(nil), // 71: machine.VersionInfo (*PlatformInfo)(nil), // 72: machine.PlatformInfo (*FeaturesInfo)(nil), // 73: machine.FeaturesInfo (*LogsRequest)(nil), // 74: machine.LogsRequest (*ReadRequest)(nil), // 75: machine.ReadRequest (*LogsContainer)(nil), // 76: machine.LogsContainer (*LogsContainersResponse)(nil), // 77: machine.LogsContainersResponse (*RollbackRequest)(nil), // 78: machine.RollbackRequest (*Rollback)(nil), // 79: machine.Rollback (*RollbackResponse)(nil), // 80: machine.RollbackResponse (*ContainersRequest)(nil), // 81: machine.ContainersRequest (*ContainerInfo)(nil), // 82: machine.ContainerInfo (*Container)(nil), // 83: machine.Container (*ContainersResponse)(nil), // 84: machine.ContainersResponse (*DmesgRequest)(nil), // 85: machine.DmesgRequest (*ProcessesResponse)(nil), // 86: machine.ProcessesResponse (*Process)(nil), // 87: machine.Process (*ProcessInfo)(nil), // 88: machine.ProcessInfo (*RestartRequest)(nil), // 89: machine.RestartRequest (*Restart)(nil), // 90: machine.Restart (*RestartResponse)(nil), // 91: machine.RestartResponse (*StatsRequest)(nil), // 92: machine.StatsRequest (*Stats)(nil), // 93: machine.Stats (*StatsResponse)(nil), // 94: machine.StatsResponse (*Stat)(nil), // 95: machine.Stat (*Memory)(nil), // 96: machine.Memory (*MemoryResponse)(nil), // 97: machine.MemoryResponse (*MemInfo)(nil), // 98: machine.MemInfo (*HostnameResponse)(nil), // 99: machine.HostnameResponse (*Hostname)(nil), // 100: machine.Hostname (*LoadAvgResponse)(nil), // 101: machine.LoadAvgResponse (*LoadAvg)(nil), // 102: machine.LoadAvg (*SystemStatResponse)(nil), // 103: machine.SystemStatResponse (*SystemStat)(nil), // 104: machine.SystemStat (*CPUStat)(nil), // 105: machine.CPUStat (*SoftIRQStat)(nil), // 106: machine.SoftIRQStat (*CPUFreqStatsResponse)(nil), // 107: machine.CPUFreqStatsResponse (*CPUsFreqStats)(nil), // 108: machine.CPUsFreqStats (*CPUFreqStats)(nil), // 109: machine.CPUFreqStats (*CPUInfoResponse)(nil), // 110: machine.CPUInfoResponse (*CPUsInfo)(nil), // 111: machine.CPUsInfo (*CPUInfo)(nil), // 112: machine.CPUInfo (*NetworkDeviceStatsResponse)(nil), // 113: machine.NetworkDeviceStatsResponse (*NetworkDeviceStats)(nil), // 114: machine.NetworkDeviceStats (*NetDev)(nil), // 115: machine.NetDev (*DiskStatsResponse)(nil), // 116: machine.DiskStatsResponse (*DiskStats)(nil), // 117: machine.DiskStats (*DiskStat)(nil), // 118: machine.DiskStat (*EtcdLeaveClusterRequest)(nil), // 119: machine.EtcdLeaveClusterRequest (*EtcdLeaveCluster)(nil), // 120: machine.EtcdLeaveCluster (*EtcdLeaveClusterResponse)(nil), // 121: machine.EtcdLeaveClusterResponse (*EtcdRemoveMemberRequest)(nil), // 122: machine.EtcdRemoveMemberRequest (*EtcdRemoveMember)(nil), // 123: machine.EtcdRemoveMember (*EtcdRemoveMemberResponse)(nil), // 124: machine.EtcdRemoveMemberResponse (*EtcdRemoveMemberByIDRequest)(nil), // 125: machine.EtcdRemoveMemberByIDRequest (*EtcdRemoveMemberByID)(nil), // 126: machine.EtcdRemoveMemberByID (*EtcdRemoveMemberByIDResponse)(nil), // 127: machine.EtcdRemoveMemberByIDResponse (*EtcdForfeitLeadershipRequest)(nil), // 128: machine.EtcdForfeitLeadershipRequest (*EtcdForfeitLeadership)(nil), // 129: machine.EtcdForfeitLeadership (*EtcdForfeitLeadershipResponse)(nil), // 130: machine.EtcdForfeitLeadershipResponse (*EtcdMemberListRequest)(nil), // 131: machine.EtcdMemberListRequest (*EtcdMember)(nil), // 132: machine.EtcdMember (*EtcdMembers)(nil), // 133: machine.EtcdMembers (*EtcdMemberListResponse)(nil), // 134: machine.EtcdMemberListResponse (*EtcdSnapshotRequest)(nil), // 135: machine.EtcdSnapshotRequest (*EtcdRecover)(nil), // 136: machine.EtcdRecover (*EtcdRecoverResponse)(nil), // 137: machine.EtcdRecoverResponse (*EtcdAlarmListResponse)(nil), // 138: machine.EtcdAlarmListResponse (*EtcdAlarm)(nil), // 139: machine.EtcdAlarm (*EtcdMemberAlarm)(nil), // 140: machine.EtcdMemberAlarm (*EtcdAlarmDisarmResponse)(nil), // 141: machine.EtcdAlarmDisarmResponse (*EtcdAlarmDisarm)(nil), // 142: machine.EtcdAlarmDisarm (*EtcdDefragmentResponse)(nil), // 143: machine.EtcdDefragmentResponse (*EtcdDefragment)(nil), // 144: machine.EtcdDefragment (*EtcdStatusResponse)(nil), // 145: machine.EtcdStatusResponse (*EtcdStatus)(nil), // 146: machine.EtcdStatus (*EtcdMemberStatus)(nil), // 147: machine.EtcdMemberStatus (*EtcdDowngradeValidateRequest)(nil), // 148: machine.EtcdDowngradeValidateRequest (*EtcdDowngradeValidateResponse)(nil), // 149: machine.EtcdDowngradeValidateResponse (*EtcdDowngradeValidate)(nil), // 150: machine.EtcdDowngradeValidate (*EtcdDowngradeEnableRequest)(nil), // 151: machine.EtcdDowngradeEnableRequest (*EtcdDowngradeEnableResponse)(nil), // 152: machine.EtcdDowngradeEnableResponse (*EtcdDowngradeEnable)(nil), // 153: machine.EtcdDowngradeEnable (*EtcdDowngradeCancelResponse)(nil), // 154: machine.EtcdDowngradeCancelResponse (*EtcdDowngradeCancel)(nil), // 155: machine.EtcdDowngradeCancel (*EtcdClusterDowngrade)(nil), // 156: machine.EtcdClusterDowngrade (*RouteConfig)(nil), // 157: machine.RouteConfig (*DHCPOptionsConfig)(nil), // 158: machine.DHCPOptionsConfig (*NetworkDeviceConfig)(nil), // 159: machine.NetworkDeviceConfig (*NetworkConfig)(nil), // 160: machine.NetworkConfig (*InstallConfig)(nil), // 161: machine.InstallConfig (*MachineConfig)(nil), // 162: machine.MachineConfig (*ControlPlaneConfig)(nil), // 163: machine.ControlPlaneConfig (*CNIConfig)(nil), // 164: machine.CNIConfig (*ClusterNetworkConfig)(nil), // 165: machine.ClusterNetworkConfig (*ClusterConfig)(nil), // 166: machine.ClusterConfig (*GenerateClientConfigurationRequest)(nil), // 167: machine.GenerateClientConfigurationRequest (*GenerateClientConfiguration)(nil), // 168: machine.GenerateClientConfiguration (*GenerateClientConfigurationResponse)(nil), // 169: machine.GenerateClientConfigurationResponse (*PacketCaptureRequest)(nil), // 170: machine.PacketCaptureRequest (*BPFInstruction)(nil), // 171: machine.BPFInstruction (*NetstatRequest)(nil), // 172: machine.NetstatRequest (*ConnectRecord)(nil), // 173: machine.ConnectRecord (*Netstat)(nil), // 174: machine.Netstat (*NetstatResponse)(nil), // 175: machine.NetstatResponse (*MetaWriteRequest)(nil), // 176: machine.MetaWriteRequest (*MetaWrite)(nil), // 177: machine.MetaWrite (*MetaWriteResponse)(nil), // 178: machine.MetaWriteResponse (*MetaDeleteRequest)(nil), // 179: machine.MetaDeleteRequest (*MetaDelete)(nil), // 180: machine.MetaDelete (*MetaDeleteResponse)(nil), // 181: machine.MetaDeleteResponse (*ImageListRequest)(nil), // 182: machine.ImageListRequest (*ImageListResponse)(nil), // 183: machine.ImageListResponse (*ImagePullRequest)(nil), // 184: machine.ImagePullRequest (*ImagePull)(nil), // 185: machine.ImagePull (*ImagePullResponse)(nil), // 186: machine.ImagePullResponse (*MachineStatusEvent_MachineStatus)(nil), // 187: machine.MachineStatusEvent.MachineStatus (*MachineStatusEvent_MachineStatus_UnmetCondition)(nil), // 188: machine.MachineStatusEvent.MachineStatus.UnmetCondition (*NetstatRequest_Feature)(nil), // 189: machine.NetstatRequest.Feature (*NetstatRequest_L4Proto)(nil), // 190: machine.NetstatRequest.L4proto (*NetstatRequest_NetNS)(nil), // 191: machine.NetstatRequest.NetNS (*ConnectRecord_Process)(nil), // 192: machine.ConnectRecord.Process (*durationpb.Duration)(nil), // 193: google.protobuf.Duration (*common.Metadata)(nil), // 194: common.Metadata (*common.Error)(nil), // 195: common.Error (*anypb.Any)(nil), // 196: google.protobuf.Any (*timestamppb.Timestamp)(nil), // 197: google.protobuf.Timestamp (common.ContainerDriver)(0), // 198: common.ContainerDriver (common.ContainerdNamespace)(0), // 199: common.ContainerdNamespace (*emptypb.Empty)(nil), // 200: google.protobuf.Empty (*common.Data)(nil), // 201: common.Data } var file_machine_machine_proto_depIdxs = []int32{ 0, // 0: machine.ApplyConfigurationRequest.mode:type_name -> machine.ApplyConfigurationRequest.Mode 193, // 1: machine.ApplyConfigurationRequest.try_mode_timeout:type_name -> google.protobuf.Duration 194, // 2: machine.ApplyConfiguration.metadata:type_name -> common.Metadata 0, // 3: machine.ApplyConfiguration.mode:type_name -> machine.ApplyConfigurationRequest.Mode 16, // 4: machine.ApplyConfigurationResponse.messages:type_name -> machine.ApplyConfiguration 1, // 5: machine.RebootRequest.mode:type_name -> machine.RebootRequest.Mode 194, // 6: machine.Reboot.metadata:type_name -> common.Metadata 19, // 7: machine.RebootResponse.messages:type_name -> machine.Reboot 194, // 8: machine.Bootstrap.metadata:type_name -> common.Metadata 22, // 9: machine.BootstrapResponse.messages:type_name -> machine.Bootstrap 2, // 10: machine.SequenceEvent.action:type_name -> machine.SequenceEvent.Action 195, // 11: machine.SequenceEvent.error:type_name -> common.Error 3, // 12: machine.PhaseEvent.action:type_name -> machine.PhaseEvent.Action 4, // 13: machine.TaskEvent.action:type_name -> machine.TaskEvent.Action 5, // 14: machine.ServiceStateEvent.action:type_name -> machine.ServiceStateEvent.Action 50, // 15: machine.ServiceStateEvent.health:type_name -> machine.ServiceHealth 6, // 16: machine.MachineStatusEvent.stage:type_name -> machine.MachineStatusEvent.MachineStage 187, // 17: machine.MachineStatusEvent.status:type_name -> machine.MachineStatusEvent.MachineStatus 194, // 18: machine.Event.metadata:type_name -> common.Metadata 196, // 19: machine.Event.data:type_name -> google.protobuf.Any 35, // 20: machine.ResetRequest.system_partitions_to_wipe:type_name -> machine.ResetPartitionSpec 7, // 21: machine.ResetRequest.mode:type_name -> machine.ResetRequest.WipeMode 194, // 22: machine.Reset.metadata:type_name -> common.Metadata 37, // 23: machine.ResetResponse.messages:type_name -> machine.Reset 194, // 24: machine.Shutdown.metadata:type_name -> common.Metadata 39, // 25: machine.ShutdownResponse.messages:type_name -> machine.Shutdown 8, // 26: machine.UpgradeRequest.reboot_mode:type_name -> machine.UpgradeRequest.RebootMode 194, // 27: machine.Upgrade.metadata:type_name -> common.Metadata 43, // 28: machine.UpgradeResponse.messages:type_name -> machine.Upgrade 194, // 29: machine.ServiceList.metadata:type_name -> common.Metadata 47, // 30: machine.ServiceList.services:type_name -> machine.ServiceInfo 45, // 31: machine.ServiceListResponse.messages:type_name -> machine.ServiceList 48, // 32: machine.ServiceInfo.events:type_name -> machine.ServiceEvents 50, // 33: machine.ServiceInfo.health:type_name -> machine.ServiceHealth 49, // 34: machine.ServiceEvents.events:type_name -> machine.ServiceEvent 197, // 35: machine.ServiceEvent.ts:type_name -> google.protobuf.Timestamp 197, // 36: machine.ServiceHealth.last_change:type_name -> google.protobuf.Timestamp 194, // 37: machine.ServiceStart.metadata:type_name -> common.Metadata 52, // 38: machine.ServiceStartResponse.messages:type_name -> machine.ServiceStart 194, // 39: machine.ServiceStop.metadata:type_name -> common.Metadata 55, // 40: machine.ServiceStopResponse.messages:type_name -> machine.ServiceStop 194, // 41: machine.ServiceRestart.metadata:type_name -> common.Metadata 58, // 42: machine.ServiceRestartResponse.messages:type_name -> machine.ServiceRestart 9, // 43: machine.ListRequest.types:type_name -> machine.ListRequest.Type 194, // 44: machine.FileInfo.metadata:type_name -> common.Metadata 64, // 45: machine.FileInfo.xattrs:type_name -> machine.Xattr 194, // 46: machine.DiskUsageInfo.metadata:type_name -> common.Metadata 194, // 47: machine.Mounts.metadata:type_name -> common.Metadata 68, // 48: machine.Mounts.stats:type_name -> machine.MountStat 66, // 49: machine.MountsResponse.messages:type_name -> machine.Mounts 194, // 50: machine.Version.metadata:type_name -> common.Metadata 71, // 51: machine.Version.version:type_name -> machine.VersionInfo 72, // 52: machine.Version.platform:type_name -> machine.PlatformInfo 73, // 53: machine.Version.features:type_name -> machine.FeaturesInfo 69, // 54: machine.VersionResponse.messages:type_name -> machine.Version 198, // 55: machine.LogsRequest.driver:type_name -> common.ContainerDriver 194, // 56: machine.LogsContainer.metadata:type_name -> common.Metadata 76, // 57: machine.LogsContainersResponse.messages:type_name -> machine.LogsContainer 194, // 58: machine.Rollback.metadata:type_name -> common.Metadata 79, // 59: machine.RollbackResponse.messages:type_name -> machine.Rollback 198, // 60: machine.ContainersRequest.driver:type_name -> common.ContainerDriver 194, // 61: machine.Container.metadata:type_name -> common.Metadata 82, // 62: machine.Container.containers:type_name -> machine.ContainerInfo 83, // 63: machine.ContainersResponse.messages:type_name -> machine.Container 87, // 64: machine.ProcessesResponse.messages:type_name -> machine.Process 194, // 65: machine.Process.metadata:type_name -> common.Metadata 88, // 66: machine.Process.processes:type_name -> machine.ProcessInfo 198, // 67: machine.RestartRequest.driver:type_name -> common.ContainerDriver 194, // 68: machine.Restart.metadata:type_name -> common.Metadata 90, // 69: machine.RestartResponse.messages:type_name -> machine.Restart 198, // 70: machine.StatsRequest.driver:type_name -> common.ContainerDriver 194, // 71: machine.Stats.metadata:type_name -> common.Metadata 95, // 72: machine.Stats.stats:type_name -> machine.Stat 93, // 73: machine.StatsResponse.messages:type_name -> machine.Stats 194, // 74: machine.Memory.metadata:type_name -> common.Metadata 98, // 75: machine.Memory.meminfo:type_name -> machine.MemInfo 96, // 76: machine.MemoryResponse.messages:type_name -> machine.Memory 100, // 77: machine.HostnameResponse.messages:type_name -> machine.Hostname 194, // 78: machine.Hostname.metadata:type_name -> common.Metadata 102, // 79: machine.LoadAvgResponse.messages:type_name -> machine.LoadAvg 194, // 80: machine.LoadAvg.metadata:type_name -> common.Metadata 104, // 81: machine.SystemStatResponse.messages:type_name -> machine.SystemStat 194, // 82: machine.SystemStat.metadata:type_name -> common.Metadata 105, // 83: machine.SystemStat.cpu_total:type_name -> machine.CPUStat 105, // 84: machine.SystemStat.cpu:type_name -> machine.CPUStat 106, // 85: machine.SystemStat.soft_irq:type_name -> machine.SoftIRQStat 108, // 86: machine.CPUFreqStatsResponse.messages:type_name -> machine.CPUsFreqStats 194, // 87: machine.CPUsFreqStats.metadata:type_name -> common.Metadata 109, // 88: machine.CPUsFreqStats.cpu_freq_stats:type_name -> machine.CPUFreqStats 111, // 89: machine.CPUInfoResponse.messages:type_name -> machine.CPUsInfo 194, // 90: machine.CPUsInfo.metadata:type_name -> common.Metadata 112, // 91: machine.CPUsInfo.cpu_info:type_name -> machine.CPUInfo 114, // 92: machine.NetworkDeviceStatsResponse.messages:type_name -> machine.NetworkDeviceStats 194, // 93: machine.NetworkDeviceStats.metadata:type_name -> common.Metadata 115, // 94: machine.NetworkDeviceStats.total:type_name -> machine.NetDev 115, // 95: machine.NetworkDeviceStats.devices:type_name -> machine.NetDev 117, // 96: machine.DiskStatsResponse.messages:type_name -> machine.DiskStats 194, // 97: machine.DiskStats.metadata:type_name -> common.Metadata 118, // 98: machine.DiskStats.total:type_name -> machine.DiskStat 118, // 99: machine.DiskStats.devices:type_name -> machine.DiskStat 194, // 100: machine.EtcdLeaveCluster.metadata:type_name -> common.Metadata 120, // 101: machine.EtcdLeaveClusterResponse.messages:type_name -> machine.EtcdLeaveCluster 194, // 102: machine.EtcdRemoveMember.metadata:type_name -> common.Metadata 123, // 103: machine.EtcdRemoveMemberResponse.messages:type_name -> machine.EtcdRemoveMember 194, // 104: machine.EtcdRemoveMemberByID.metadata:type_name -> common.Metadata 126, // 105: machine.EtcdRemoveMemberByIDResponse.messages:type_name -> machine.EtcdRemoveMemberByID 194, // 106: machine.EtcdForfeitLeadership.metadata:type_name -> common.Metadata 129, // 107: machine.EtcdForfeitLeadershipResponse.messages:type_name -> machine.EtcdForfeitLeadership 194, // 108: machine.EtcdMembers.metadata:type_name -> common.Metadata 132, // 109: machine.EtcdMembers.members:type_name -> machine.EtcdMember 133, // 110: machine.EtcdMemberListResponse.messages:type_name -> machine.EtcdMembers 194, // 111: machine.EtcdRecover.metadata:type_name -> common.Metadata 136, // 112: machine.EtcdRecoverResponse.messages:type_name -> machine.EtcdRecover 139, // 113: machine.EtcdAlarmListResponse.messages:type_name -> machine.EtcdAlarm 194, // 114: machine.EtcdAlarm.metadata:type_name -> common.Metadata 140, // 115: machine.EtcdAlarm.member_alarms:type_name -> machine.EtcdMemberAlarm 10, // 116: machine.EtcdMemberAlarm.alarm:type_name -> machine.EtcdMemberAlarm.AlarmType 142, // 117: machine.EtcdAlarmDisarmResponse.messages:type_name -> machine.EtcdAlarmDisarm 194, // 118: machine.EtcdAlarmDisarm.metadata:type_name -> common.Metadata 140, // 119: machine.EtcdAlarmDisarm.member_alarms:type_name -> machine.EtcdMemberAlarm 144, // 120: machine.EtcdDefragmentResponse.messages:type_name -> machine.EtcdDefragment 194, // 121: machine.EtcdDefragment.metadata:type_name -> common.Metadata 146, // 122: machine.EtcdStatusResponse.messages:type_name -> machine.EtcdStatus 194, // 123: machine.EtcdStatus.metadata:type_name -> common.Metadata 147, // 124: machine.EtcdStatus.member_status:type_name -> machine.EtcdMemberStatus 150, // 125: machine.EtcdDowngradeValidateResponse.messages:type_name -> machine.EtcdDowngradeValidate 194, // 126: machine.EtcdDowngradeValidate.metadata:type_name -> common.Metadata 156, // 127: machine.EtcdDowngradeValidate.cluster_downgrade:type_name -> machine.EtcdClusterDowngrade 153, // 128: machine.EtcdDowngradeEnableResponse.messages:type_name -> machine.EtcdDowngradeEnable 194, // 129: machine.EtcdDowngradeEnable.metadata:type_name -> common.Metadata 156, // 130: machine.EtcdDowngradeEnable.cluster_downgrade:type_name -> machine.EtcdClusterDowngrade 155, // 131: machine.EtcdDowngradeCancelResponse.messages:type_name -> machine.EtcdDowngradeCancel 194, // 132: machine.EtcdDowngradeCancel.metadata:type_name -> common.Metadata 156, // 133: machine.EtcdDowngradeCancel.cluster_downgrade:type_name -> machine.EtcdClusterDowngrade 158, // 134: machine.NetworkDeviceConfig.dhcp_options:type_name -> machine.DHCPOptionsConfig 157, // 135: machine.NetworkDeviceConfig.routes:type_name -> machine.RouteConfig 159, // 136: machine.NetworkConfig.interfaces:type_name -> machine.NetworkDeviceConfig 11, // 137: machine.MachineConfig.type:type_name -> machine.MachineConfig.MachineType 161, // 138: machine.MachineConfig.install_config:type_name -> machine.InstallConfig 160, // 139: machine.MachineConfig.network_config:type_name -> machine.NetworkConfig 164, // 140: machine.ClusterNetworkConfig.cni_config:type_name -> machine.CNIConfig 163, // 141: machine.ClusterConfig.control_plane:type_name -> machine.ControlPlaneConfig 165, // 142: machine.ClusterConfig.cluster_network:type_name -> machine.ClusterNetworkConfig 193, // 143: machine.GenerateClientConfigurationRequest.crt_ttl:type_name -> google.protobuf.Duration 194, // 144: machine.GenerateClientConfiguration.metadata:type_name -> common.Metadata 168, // 145: machine.GenerateClientConfigurationResponse.messages:type_name -> machine.GenerateClientConfiguration 171, // 146: machine.PacketCaptureRequest.bpf_filter:type_name -> machine.BPFInstruction 12, // 147: machine.NetstatRequest.filter:type_name -> machine.NetstatRequest.Filter 189, // 148: machine.NetstatRequest.feature:type_name -> machine.NetstatRequest.Feature 190, // 149: machine.NetstatRequest.l4proto:type_name -> machine.NetstatRequest.L4proto 191, // 150: machine.NetstatRequest.netns:type_name -> machine.NetstatRequest.NetNS 13, // 151: machine.ConnectRecord.state:type_name -> machine.ConnectRecord.State 14, // 152: machine.ConnectRecord.tr:type_name -> machine.ConnectRecord.TimerActive 192, // 153: machine.ConnectRecord.process:type_name -> machine.ConnectRecord.Process 194, // 154: machine.Netstat.metadata:type_name -> common.Metadata 173, // 155: machine.Netstat.connectrecord:type_name -> machine.ConnectRecord 174, // 156: machine.NetstatResponse.messages:type_name -> machine.Netstat 194, // 157: machine.MetaWrite.metadata:type_name -> common.Metadata 177, // 158: machine.MetaWriteResponse.messages:type_name -> machine.MetaWrite 194, // 159: machine.MetaDelete.metadata:type_name -> common.Metadata 180, // 160: machine.MetaDeleteResponse.messages:type_name -> machine.MetaDelete 199, // 161: machine.ImageListRequest.namespace:type_name -> common.ContainerdNamespace 194, // 162: machine.ImageListResponse.metadata:type_name -> common.Metadata 197, // 163: machine.ImageListResponse.created_at:type_name -> google.protobuf.Timestamp 199, // 164: machine.ImagePullRequest.namespace:type_name -> common.ContainerdNamespace 194, // 165: machine.ImagePull.metadata:type_name -> common.Metadata 185, // 166: machine.ImagePullResponse.messages:type_name -> machine.ImagePull 188, // 167: machine.MachineStatusEvent.MachineStatus.unmet_conditions:type_name -> machine.MachineStatusEvent.MachineStatus.UnmetCondition 15, // 168: machine.MachineService.ApplyConfiguration:input_type -> machine.ApplyConfigurationRequest 21, // 169: machine.MachineService.Bootstrap:input_type -> machine.BootstrapRequest 81, // 170: machine.MachineService.Containers:input_type -> machine.ContainersRequest 60, // 171: machine.MachineService.Copy:input_type -> machine.CopyRequest 200, // 172: machine.MachineService.CPUFreqStats:input_type -> google.protobuf.Empty 200, // 173: machine.MachineService.CPUInfo:input_type -> google.protobuf.Empty 200, // 174: machine.MachineService.DiskStats:input_type -> google.protobuf.Empty 85, // 175: machine.MachineService.Dmesg:input_type -> machine.DmesgRequest 33, // 176: machine.MachineService.Events:input_type -> machine.EventsRequest 131, // 177: machine.MachineService.EtcdMemberList:input_type -> machine.EtcdMemberListRequest 125, // 178: machine.MachineService.EtcdRemoveMemberByID:input_type -> machine.EtcdRemoveMemberByIDRequest 119, // 179: machine.MachineService.EtcdLeaveCluster:input_type -> machine.EtcdLeaveClusterRequest 128, // 180: machine.MachineService.EtcdForfeitLeadership:input_type -> machine.EtcdForfeitLeadershipRequest 201, // 181: machine.MachineService.EtcdRecover:input_type -> common.Data 135, // 182: machine.MachineService.EtcdSnapshot:input_type -> machine.EtcdSnapshotRequest 200, // 183: machine.MachineService.EtcdAlarmList:input_type -> google.protobuf.Empty 200, // 184: machine.MachineService.EtcdAlarmDisarm:input_type -> google.protobuf.Empty 200, // 185: machine.MachineService.EtcdDefragment:input_type -> google.protobuf.Empty 200, // 186: machine.MachineService.EtcdStatus:input_type -> google.protobuf.Empty 148, // 187: machine.MachineService.EtcdDowngradeValidate:input_type -> machine.EtcdDowngradeValidateRequest 151, // 188: machine.MachineService.EtcdDowngradeEnable:input_type -> machine.EtcdDowngradeEnableRequest 200, // 189: machine.MachineService.EtcdDowngradeCancel:input_type -> google.protobuf.Empty 200, // 190: machine.MachineService.Hostname:input_type -> google.protobuf.Empty 200, // 191: machine.MachineService.Kubeconfig:input_type -> google.protobuf.Empty 61, // 192: machine.MachineService.List:input_type -> machine.ListRequest 62, // 193: machine.MachineService.DiskUsage:input_type -> machine.DiskUsageRequest 200, // 194: machine.MachineService.LoadAvg:input_type -> google.protobuf.Empty 74, // 195: machine.MachineService.Logs:input_type -> machine.LogsRequest 200, // 196: machine.MachineService.LogsContainers:input_type -> google.protobuf.Empty 200, // 197: machine.MachineService.Memory:input_type -> google.protobuf.Empty 200, // 198: machine.MachineService.Mounts:input_type -> google.protobuf.Empty 200, // 199: machine.MachineService.NetworkDeviceStats:input_type -> google.protobuf.Empty 200, // 200: machine.MachineService.Processes:input_type -> google.protobuf.Empty 75, // 201: machine.MachineService.Read:input_type -> machine.ReadRequest 18, // 202: machine.MachineService.Reboot:input_type -> machine.RebootRequest 89, // 203: machine.MachineService.Restart:input_type -> machine.RestartRequest 78, // 204: machine.MachineService.Rollback:input_type -> machine.RollbackRequest 36, // 205: machine.MachineService.Reset:input_type -> machine.ResetRequest 200, // 206: machine.MachineService.ServiceList:input_type -> google.protobuf.Empty 57, // 207: machine.MachineService.ServiceRestart:input_type -> machine.ServiceRestartRequest 51, // 208: machine.MachineService.ServiceStart:input_type -> machine.ServiceStartRequest 54, // 209: machine.MachineService.ServiceStop:input_type -> machine.ServiceStopRequest 40, // 210: machine.MachineService.Shutdown:input_type -> machine.ShutdownRequest 92, // 211: machine.MachineService.Stats:input_type -> machine.StatsRequest 200, // 212: machine.MachineService.SystemStat:input_type -> google.protobuf.Empty 42, // 213: machine.MachineService.Upgrade:input_type -> machine.UpgradeRequest 200, // 214: machine.MachineService.Version:input_type -> google.protobuf.Empty 167, // 215: machine.MachineService.GenerateClientConfiguration:input_type -> machine.GenerateClientConfigurationRequest 170, // 216: machine.MachineService.PacketCapture:input_type -> machine.PacketCaptureRequest 172, // 217: machine.MachineService.Netstat:input_type -> machine.NetstatRequest 176, // 218: machine.MachineService.MetaWrite:input_type -> machine.MetaWriteRequest 179, // 219: machine.MachineService.MetaDelete:input_type -> machine.MetaDeleteRequest 182, // 220: machine.MachineService.ImageList:input_type -> machine.ImageListRequest 184, // 221: machine.MachineService.ImagePull:input_type -> machine.ImagePullRequest 17, // 222: machine.MachineService.ApplyConfiguration:output_type -> machine.ApplyConfigurationResponse 23, // 223: machine.MachineService.Bootstrap:output_type -> machine.BootstrapResponse 84, // 224: machine.MachineService.Containers:output_type -> machine.ContainersResponse 201, // 225: machine.MachineService.Copy:output_type -> common.Data 107, // 226: machine.MachineService.CPUFreqStats:output_type -> machine.CPUFreqStatsResponse 110, // 227: machine.MachineService.CPUInfo:output_type -> machine.CPUInfoResponse 116, // 228: machine.MachineService.DiskStats:output_type -> machine.DiskStatsResponse 201, // 229: machine.MachineService.Dmesg:output_type -> common.Data 34, // 230: machine.MachineService.Events:output_type -> machine.Event 134, // 231: machine.MachineService.EtcdMemberList:output_type -> machine.EtcdMemberListResponse 127, // 232: machine.MachineService.EtcdRemoveMemberByID:output_type -> machine.EtcdRemoveMemberByIDResponse 121, // 233: machine.MachineService.EtcdLeaveCluster:output_type -> machine.EtcdLeaveClusterResponse 130, // 234: machine.MachineService.EtcdForfeitLeadership:output_type -> machine.EtcdForfeitLeadershipResponse 137, // 235: machine.MachineService.EtcdRecover:output_type -> machine.EtcdRecoverResponse 201, // 236: machine.MachineService.EtcdSnapshot:output_type -> common.Data 138, // 237: machine.MachineService.EtcdAlarmList:output_type -> machine.EtcdAlarmListResponse 141, // 238: machine.MachineService.EtcdAlarmDisarm:output_type -> machine.EtcdAlarmDisarmResponse 143, // 239: machine.MachineService.EtcdDefragment:output_type -> machine.EtcdDefragmentResponse 145, // 240: machine.MachineService.EtcdStatus:output_type -> machine.EtcdStatusResponse 149, // 241: machine.MachineService.EtcdDowngradeValidate:output_type -> machine.EtcdDowngradeValidateResponse 152, // 242: machine.MachineService.EtcdDowngradeEnable:output_type -> machine.EtcdDowngradeEnableResponse 154, // 243: machine.MachineService.EtcdDowngradeCancel:output_type -> machine.EtcdDowngradeCancelResponse 99, // 244: machine.MachineService.Hostname:output_type -> machine.HostnameResponse 201, // 245: machine.MachineService.Kubeconfig:output_type -> common.Data 63, // 246: machine.MachineService.List:output_type -> machine.FileInfo 65, // 247: machine.MachineService.DiskUsage:output_type -> machine.DiskUsageInfo 101, // 248: machine.MachineService.LoadAvg:output_type -> machine.LoadAvgResponse 201, // 249: machine.MachineService.Logs:output_type -> common.Data 77, // 250: machine.MachineService.LogsContainers:output_type -> machine.LogsContainersResponse 97, // 251: machine.MachineService.Memory:output_type -> machine.MemoryResponse 67, // 252: machine.MachineService.Mounts:output_type -> machine.MountsResponse 113, // 253: machine.MachineService.NetworkDeviceStats:output_type -> machine.NetworkDeviceStatsResponse 86, // 254: machine.MachineService.Processes:output_type -> machine.ProcessesResponse 201, // 255: machine.MachineService.Read:output_type -> common.Data 20, // 256: machine.MachineService.Reboot:output_type -> machine.RebootResponse 91, // 257: machine.MachineService.Restart:output_type -> machine.RestartResponse 80, // 258: machine.MachineService.Rollback:output_type -> machine.RollbackResponse 38, // 259: machine.MachineService.Reset:output_type -> machine.ResetResponse 46, // 260: machine.MachineService.ServiceList:output_type -> machine.ServiceListResponse 59, // 261: machine.MachineService.ServiceRestart:output_type -> machine.ServiceRestartResponse 53, // 262: machine.MachineService.ServiceStart:output_type -> machine.ServiceStartResponse 56, // 263: machine.MachineService.ServiceStop:output_type -> machine.ServiceStopResponse 41, // 264: machine.MachineService.Shutdown:output_type -> machine.ShutdownResponse 94, // 265: machine.MachineService.Stats:output_type -> machine.StatsResponse 103, // 266: machine.MachineService.SystemStat:output_type -> machine.SystemStatResponse 44, // 267: machine.MachineService.Upgrade:output_type -> machine.UpgradeResponse 70, // 268: machine.MachineService.Version:output_type -> machine.VersionResponse 169, // 269: machine.MachineService.GenerateClientConfiguration:output_type -> machine.GenerateClientConfigurationResponse 201, // 270: machine.MachineService.PacketCapture:output_type -> common.Data 175, // 271: machine.MachineService.Netstat:output_type -> machine.NetstatResponse 178, // 272: machine.MachineService.MetaWrite:output_type -> machine.MetaWriteResponse 181, // 273: machine.MachineService.MetaDelete:output_type -> machine.MetaDeleteResponse 183, // 274: machine.MachineService.ImageList:output_type -> machine.ImageListResponse 186, // 275: machine.MachineService.ImagePull:output_type -> machine.ImagePullResponse 222, // [222:276] is the sub-list for method output_type 168, // [168:222] is the sub-list for method input_type 168, // [168:168] is the sub-list for extension type_name 168, // [168:168] is the sub-list for extension extendee 0, // [0:168] is the sub-list for field type_name } func init() { file_machine_machine_proto_init() } func file_machine_machine_proto_init() { if File_machine_machine_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_machine_machine_proto_rawDesc), len(file_machine_machine_proto_rawDesc)), NumEnums: 15, NumMessages: 178, NumExtensions: 0, NumServices: 1, }, GoTypes: file_machine_machine_proto_goTypes, DependencyIndexes: file_machine_machine_proto_depIdxs, EnumInfos: file_machine_machine_proto_enumTypes, MessageInfos: file_machine_machine_proto_msgTypes, }.Build() File_machine_machine_proto = out.File file_machine_machine_proto_goTypes = nil file_machine_machine_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/machine/machine_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: machine/machine.proto package machine import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( MachineService_ApplyConfiguration_FullMethodName = "/machine.MachineService/ApplyConfiguration" MachineService_Bootstrap_FullMethodName = "/machine.MachineService/Bootstrap" MachineService_Containers_FullMethodName = "/machine.MachineService/Containers" MachineService_Copy_FullMethodName = "/machine.MachineService/Copy" MachineService_CPUFreqStats_FullMethodName = "/machine.MachineService/CPUFreqStats" MachineService_CPUInfo_FullMethodName = "/machine.MachineService/CPUInfo" MachineService_DiskStats_FullMethodName = "/machine.MachineService/DiskStats" MachineService_Dmesg_FullMethodName = "/machine.MachineService/Dmesg" MachineService_Events_FullMethodName = "/machine.MachineService/Events" MachineService_EtcdMemberList_FullMethodName = "/machine.MachineService/EtcdMemberList" MachineService_EtcdRemoveMemberByID_FullMethodName = "/machine.MachineService/EtcdRemoveMemberByID" MachineService_EtcdLeaveCluster_FullMethodName = "/machine.MachineService/EtcdLeaveCluster" MachineService_EtcdForfeitLeadership_FullMethodName = "/machine.MachineService/EtcdForfeitLeadership" MachineService_EtcdRecover_FullMethodName = "/machine.MachineService/EtcdRecover" MachineService_EtcdSnapshot_FullMethodName = "/machine.MachineService/EtcdSnapshot" MachineService_EtcdAlarmList_FullMethodName = "/machine.MachineService/EtcdAlarmList" MachineService_EtcdAlarmDisarm_FullMethodName = "/machine.MachineService/EtcdAlarmDisarm" MachineService_EtcdDefragment_FullMethodName = "/machine.MachineService/EtcdDefragment" MachineService_EtcdStatus_FullMethodName = "/machine.MachineService/EtcdStatus" MachineService_EtcdDowngradeValidate_FullMethodName = "/machine.MachineService/EtcdDowngradeValidate" MachineService_EtcdDowngradeEnable_FullMethodName = "/machine.MachineService/EtcdDowngradeEnable" MachineService_EtcdDowngradeCancel_FullMethodName = "/machine.MachineService/EtcdDowngradeCancel" MachineService_Hostname_FullMethodName = "/machine.MachineService/Hostname" MachineService_Kubeconfig_FullMethodName = "/machine.MachineService/Kubeconfig" MachineService_List_FullMethodName = "/machine.MachineService/List" MachineService_DiskUsage_FullMethodName = "/machine.MachineService/DiskUsage" MachineService_LoadAvg_FullMethodName = "/machine.MachineService/LoadAvg" MachineService_Logs_FullMethodName = "/machine.MachineService/Logs" MachineService_LogsContainers_FullMethodName = "/machine.MachineService/LogsContainers" MachineService_Memory_FullMethodName = "/machine.MachineService/Memory" MachineService_Mounts_FullMethodName = "/machine.MachineService/Mounts" MachineService_NetworkDeviceStats_FullMethodName = "/machine.MachineService/NetworkDeviceStats" MachineService_Processes_FullMethodName = "/machine.MachineService/Processes" MachineService_Read_FullMethodName = "/machine.MachineService/Read" MachineService_Reboot_FullMethodName = "/machine.MachineService/Reboot" MachineService_Restart_FullMethodName = "/machine.MachineService/Restart" MachineService_Rollback_FullMethodName = "/machine.MachineService/Rollback" MachineService_Reset_FullMethodName = "/machine.MachineService/Reset" MachineService_ServiceList_FullMethodName = "/machine.MachineService/ServiceList" MachineService_ServiceRestart_FullMethodName = "/machine.MachineService/ServiceRestart" MachineService_ServiceStart_FullMethodName = "/machine.MachineService/ServiceStart" MachineService_ServiceStop_FullMethodName = "/machine.MachineService/ServiceStop" MachineService_Shutdown_FullMethodName = "/machine.MachineService/Shutdown" MachineService_Stats_FullMethodName = "/machine.MachineService/Stats" MachineService_SystemStat_FullMethodName = "/machine.MachineService/SystemStat" MachineService_Upgrade_FullMethodName = "/machine.MachineService/Upgrade" MachineService_Version_FullMethodName = "/machine.MachineService/Version" MachineService_GenerateClientConfiguration_FullMethodName = "/machine.MachineService/GenerateClientConfiguration" MachineService_PacketCapture_FullMethodName = "/machine.MachineService/PacketCapture" MachineService_Netstat_FullMethodName = "/machine.MachineService/Netstat" MachineService_MetaWrite_FullMethodName = "/machine.MachineService/MetaWrite" MachineService_MetaDelete_FullMethodName = "/machine.MachineService/MetaDelete" MachineService_ImageList_FullMethodName = "/machine.MachineService/ImageList" MachineService_ImagePull_FullMethodName = "/machine.MachineService/ImagePull" ) // MachineServiceClient is the client API for MachineService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The machine service definition. type MachineServiceClient interface { ApplyConfiguration(ctx context.Context, in *ApplyConfigurationRequest, opts ...grpc.CallOption) (*ApplyConfigurationResponse, error) // Bootstrap method makes control plane node enter etcd bootstrap mode. // Node aborts etcd join sequence and creates single-node etcd cluster. // If recover_etcd argument is specified, etcd is recovered from a snapshot // uploaded with EtcdRecover. Bootstrap(ctx context.Context, in *BootstrapRequest, opts ...grpc.CallOption) (*BootstrapResponse, error) Containers(ctx context.Context, in *ContainersRequest, opts ...grpc.CallOption) (*ContainersResponse, error) Copy(ctx context.Context, in *CopyRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) CPUFreqStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CPUFreqStatsResponse, error) CPUInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CPUInfoResponse, error) DiskStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DiskStatsResponse, error) Dmesg(ctx context.Context, in *DmesgRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) Events(ctx context.Context, in *EventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error) EtcdMemberList(ctx context.Context, in *EtcdMemberListRequest, opts ...grpc.CallOption) (*EtcdMemberListResponse, error) // EtcdRemoveMemberByID removes a member from the etcd cluster identified by member ID. // This API should be used to remove members which don't have an associated Talos node anymore. // To remove a member with a running Talos node, use EtcdLeaveCluster API on the node to be removed. EtcdRemoveMemberByID(ctx context.Context, in *EtcdRemoveMemberByIDRequest, opts ...grpc.CallOption) (*EtcdRemoveMemberByIDResponse, error) EtcdLeaveCluster(ctx context.Context, in *EtcdLeaveClusterRequest, opts ...grpc.CallOption) (*EtcdLeaveClusterResponse, error) EtcdForfeitLeadership(ctx context.Context, in *EtcdForfeitLeadershipRequest, opts ...grpc.CallOption) (*EtcdForfeitLeadershipResponse, error) // EtcdRecover method uploads etcd data snapshot created with EtcdSnapshot // to the node. // Snapshot can be later used to recover the cluster via Bootstrap method. EtcdRecover(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[common.Data, EtcdRecoverResponse], error) // EtcdSnapshot method creates etcd data snapshot (backup) from the local etcd instance // and streams it back to the client. // This method is available only on control plane nodes (which run etcd). EtcdSnapshot(ctx context.Context, in *EtcdSnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) // EtcdAlarmList lists etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). EtcdAlarmList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdAlarmListResponse, error) // EtcdAlarmDisarm disarms etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). EtcdAlarmDisarm(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdAlarmDisarmResponse, error) // EtcdDefragment defragments etcd data directory for the current node. // Defragmentation is a resource-heavy operation, so it should only run on a specific // node. // This method is available only on control plane nodes (which run etcd). EtcdDefragment(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdDefragmentResponse, error) // EtcdStatus returns etcd status for the current member. // This method is available only on control plane nodes (which run etcd). EtcdStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdStatusResponse, error) // EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeValidate(ctx context.Context, in *EtcdDowngradeValidateRequest, opts ...grpc.CallOption) (*EtcdDowngradeValidateResponse, error) // EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeEnable(ctx context.Context, in *EtcdDowngradeEnableRequest, opts ...grpc.CallOption) (*EtcdDowngradeEnableResponse, error) // EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeCancel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdDowngradeCancelResponse, error) Hostname(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HostnameResponse, error) Kubeconfig(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileInfo], error) DiskUsage(ctx context.Context, in *DiskUsageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DiskUsageInfo], error) LoadAvg(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*LoadAvgResponse, error) Logs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) LogsContainers(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*LogsContainersResponse, error) Memory(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MemoryResponse, error) Mounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MountsResponse, error) NetworkDeviceStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*NetworkDeviceStatsResponse, error) Processes(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ProcessesResponse, error) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) Reboot(ctx context.Context, in *RebootRequest, opts ...grpc.CallOption) (*RebootResponse, error) Restart(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) Rollback(ctx context.Context, in *RollbackRequest, opts ...grpc.CallOption) (*RollbackResponse, error) Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*ResetResponse, error) ServiceList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ServiceListResponse, error) ServiceRestart(ctx context.Context, in *ServiceRestartRequest, opts ...grpc.CallOption) (*ServiceRestartResponse, error) ServiceStart(ctx context.Context, in *ServiceStartRequest, opts ...grpc.CallOption) (*ServiceStartResponse, error) ServiceStop(ctx context.Context, in *ServiceStopRequest, opts ...grpc.CallOption) (*ServiceStopResponse, error) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) SystemStat(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemStatResponse, error) // Deprecated: Do not use. // Upgrade initiates the upgrade of the node to a new version of Talos. // // Use LifecycleService Upgrade RPC instead. Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (*UpgradeResponse, error) Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) // GenerateClientConfiguration generates talosctl client configuration (talosconfig). GenerateClientConfiguration(ctx context.Context, in *GenerateClientConfigurationRequest, opts ...grpc.CallOption) (*GenerateClientConfigurationResponse, error) // PacketCapture performs packet capture and streams back pcap file. PacketCapture(ctx context.Context, in *PacketCaptureRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) // Netstat provides information about network connections. Netstat(ctx context.Context, in *NetstatRequest, opts ...grpc.CallOption) (*NetstatResponse, error) // MetaWrite writes a META key-value pair. MetaWrite(ctx context.Context, in *MetaWriteRequest, opts ...grpc.CallOption) (*MetaWriteResponse, error) // MetaDelete deletes a META key. MetaDelete(ctx context.Context, in *MetaDeleteRequest, opts ...grpc.CallOption) (*MetaDeleteResponse, error) // Deprecated: Do not use. // ImageList lists images in the CRI. // // Use ImageService List RPC instead. ImageList(ctx context.Context, in *ImageListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageListResponse], error) // Deprecated: Do not use. // ImagePull pulls an image into the CRI. // // Use ImageService Pull RPC instead. ImagePull(ctx context.Context, in *ImagePullRequest, opts ...grpc.CallOption) (*ImagePullResponse, error) } type machineServiceClient struct { cc grpc.ClientConnInterface } func NewMachineServiceClient(cc grpc.ClientConnInterface) MachineServiceClient { return &machineServiceClient{cc} } func (c *machineServiceClient) ApplyConfiguration(ctx context.Context, in *ApplyConfigurationRequest, opts ...grpc.CallOption) (*ApplyConfigurationResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ApplyConfigurationResponse) err := c.cc.Invoke(ctx, MachineService_ApplyConfiguration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Bootstrap(ctx context.Context, in *BootstrapRequest, opts ...grpc.CallOption) (*BootstrapResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(BootstrapResponse) err := c.cc.Invoke(ctx, MachineService_Bootstrap_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Containers(ctx context.Context, in *ContainersRequest, opts ...grpc.CallOption) (*ContainersResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ContainersResponse) err := c.cc.Invoke(ctx, MachineService_Containers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Copy(ctx context.Context, in *CopyRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[0], MachineService_Copy_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[CopyRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_CopyClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) CPUFreqStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CPUFreqStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CPUFreqStatsResponse) err := c.cc.Invoke(ctx, MachineService_CPUFreqStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) CPUInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CPUInfoResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CPUInfoResponse) err := c.cc.Invoke(ctx, MachineService_CPUInfo_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) DiskStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DiskStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DiskStatsResponse) err := c.cc.Invoke(ctx, MachineService_DiskStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Dmesg(ctx context.Context, in *DmesgRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[1], MachineService_Dmesg_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[DmesgRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_DmesgClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) Events(ctx context.Context, in *EventsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[Event], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[2], MachineService_Events_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[EventsRequest, Event]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EventsClient = grpc.ServerStreamingClient[Event] func (c *machineServiceClient) EtcdMemberList(ctx context.Context, in *EtcdMemberListRequest, opts ...grpc.CallOption) (*EtcdMemberListResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdMemberListResponse) err := c.cc.Invoke(ctx, MachineService_EtcdMemberList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdRemoveMemberByID(ctx context.Context, in *EtcdRemoveMemberByIDRequest, opts ...grpc.CallOption) (*EtcdRemoveMemberByIDResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdRemoveMemberByIDResponse) err := c.cc.Invoke(ctx, MachineService_EtcdRemoveMemberByID_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdLeaveCluster(ctx context.Context, in *EtcdLeaveClusterRequest, opts ...grpc.CallOption) (*EtcdLeaveClusterResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdLeaveClusterResponse) err := c.cc.Invoke(ctx, MachineService_EtcdLeaveCluster_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdForfeitLeadership(ctx context.Context, in *EtcdForfeitLeadershipRequest, opts ...grpc.CallOption) (*EtcdForfeitLeadershipResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdForfeitLeadershipResponse) err := c.cc.Invoke(ctx, MachineService_EtcdForfeitLeadership_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdRecover(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[common.Data, EtcdRecoverResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[3], MachineService_EtcdRecover_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[common.Data, EtcdRecoverResponse]{ClientStream: stream} return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EtcdRecoverClient = grpc.ClientStreamingClient[common.Data, EtcdRecoverResponse] func (c *machineServiceClient) EtcdSnapshot(ctx context.Context, in *EtcdSnapshotRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[4], MachineService_EtcdSnapshot_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[EtcdSnapshotRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EtcdSnapshotClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) EtcdAlarmList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdAlarmListResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdAlarmListResponse) err := c.cc.Invoke(ctx, MachineService_EtcdAlarmList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdAlarmDisarm(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdAlarmDisarmResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdAlarmDisarmResponse) err := c.cc.Invoke(ctx, MachineService_EtcdAlarmDisarm_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdDefragment(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdDefragmentResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdDefragmentResponse) err := c.cc.Invoke(ctx, MachineService_EtcdDefragment_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdStatus(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdStatusResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdStatusResponse) err := c.cc.Invoke(ctx, MachineService_EtcdStatus_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdDowngradeValidate(ctx context.Context, in *EtcdDowngradeValidateRequest, opts ...grpc.CallOption) (*EtcdDowngradeValidateResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdDowngradeValidateResponse) err := c.cc.Invoke(ctx, MachineService_EtcdDowngradeValidate_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdDowngradeEnable(ctx context.Context, in *EtcdDowngradeEnableRequest, opts ...grpc.CallOption) (*EtcdDowngradeEnableResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdDowngradeEnableResponse) err := c.cc.Invoke(ctx, MachineService_EtcdDowngradeEnable_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) EtcdDowngradeCancel(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*EtcdDowngradeCancelResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(EtcdDowngradeCancelResponse) err := c.cc.Invoke(ctx, MachineService_EtcdDowngradeCancel_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Hostname(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*HostnameResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(HostnameResponse) err := c.cc.Invoke(ctx, MachineService_Hostname_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Kubeconfig(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[5], MachineService_Kubeconfig_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[emptypb.Empty, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_KubeconfigClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) List(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[FileInfo], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[6], MachineService_List_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ListRequest, FileInfo]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ListClient = grpc.ServerStreamingClient[FileInfo] func (c *machineServiceClient) DiskUsage(ctx context.Context, in *DiskUsageRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[DiskUsageInfo], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[7], MachineService_DiskUsage_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[DiskUsageRequest, DiskUsageInfo]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_DiskUsageClient = grpc.ServerStreamingClient[DiskUsageInfo] func (c *machineServiceClient) LoadAvg(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*LoadAvgResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LoadAvgResponse) err := c.cc.Invoke(ctx, MachineService_LoadAvg_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Logs(ctx context.Context, in *LogsRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[8], MachineService_Logs_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[LogsRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_LogsClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) LogsContainers(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*LogsContainersResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(LogsContainersResponse) err := c.cc.Invoke(ctx, MachineService_LogsContainers_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Memory(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MemoryResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MemoryResponse) err := c.cc.Invoke(ctx, MachineService_Memory_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Mounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MountsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MountsResponse) err := c.cc.Invoke(ctx, MachineService_Mounts_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) NetworkDeviceStats(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*NetworkDeviceStatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(NetworkDeviceStatsResponse) err := c.cc.Invoke(ctx, MachineService_NetworkDeviceStats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Processes(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ProcessesResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ProcessesResponse) err := c.cc.Invoke(ctx, MachineService_Processes_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Read(ctx context.Context, in *ReadRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[9], MachineService_Read_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ReadRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ReadClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) Reboot(ctx context.Context, in *RebootRequest, opts ...grpc.CallOption) (*RebootResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RebootResponse) err := c.cc.Invoke(ctx, MachineService_Reboot_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Restart(ctx context.Context, in *RestartRequest, opts ...grpc.CallOption) (*RestartResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RestartResponse) err := c.cc.Invoke(ctx, MachineService_Restart_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Rollback(ctx context.Context, in *RollbackRequest, opts ...grpc.CallOption) (*RollbackResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RollbackResponse) err := c.cc.Invoke(ctx, MachineService_Rollback_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Reset(ctx context.Context, in *ResetRequest, opts ...grpc.CallOption) (*ResetResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ResetResponse) err := c.cc.Invoke(ctx, MachineService_Reset_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) ServiceList(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ServiceListResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ServiceListResponse) err := c.cc.Invoke(ctx, MachineService_ServiceList_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) ServiceRestart(ctx context.Context, in *ServiceRestartRequest, opts ...grpc.CallOption) (*ServiceRestartResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ServiceRestartResponse) err := c.cc.Invoke(ctx, MachineService_ServiceRestart_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) ServiceStart(ctx context.Context, in *ServiceStartRequest, opts ...grpc.CallOption) (*ServiceStartResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ServiceStartResponse) err := c.cc.Invoke(ctx, MachineService_ServiceStart_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) ServiceStop(ctx context.Context, in *ServiceStopRequest, opts ...grpc.CallOption) (*ServiceStopResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ServiceStopResponse) err := c.cc.Invoke(ctx, MachineService_ServiceStop_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Shutdown(ctx context.Context, in *ShutdownRequest, opts ...grpc.CallOption) (*ShutdownResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ShutdownResponse) err := c.cc.Invoke(ctx, MachineService_Shutdown_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Stats(ctx context.Context, in *StatsRequest, opts ...grpc.CallOption) (*StatsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(StatsResponse) err := c.cc.Invoke(ctx, MachineService_Stats_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) SystemStat(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*SystemStatResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SystemStatResponse) err := c.cc.Invoke(ctx, MachineService_SystemStat_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // Deprecated: Do not use. func (c *machineServiceClient) Upgrade(ctx context.Context, in *UpgradeRequest, opts ...grpc.CallOption) (*UpgradeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(UpgradeResponse) err := c.cc.Invoke(ctx, MachineService_Upgrade_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) Version(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*VersionResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(VersionResponse) err := c.cc.Invoke(ctx, MachineService_Version_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) GenerateClientConfiguration(ctx context.Context, in *GenerateClientConfigurationRequest, opts ...grpc.CallOption) (*GenerateClientConfigurationResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GenerateClientConfigurationResponse) err := c.cc.Invoke(ctx, MachineService_GenerateClientConfiguration_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) PacketCapture(ctx context.Context, in *PacketCaptureRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[common.Data], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[10], MachineService_PacketCapture_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[PacketCaptureRequest, common.Data]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_PacketCaptureClient = grpc.ServerStreamingClient[common.Data] func (c *machineServiceClient) Netstat(ctx context.Context, in *NetstatRequest, opts ...grpc.CallOption) (*NetstatResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(NetstatResponse) err := c.cc.Invoke(ctx, MachineService_Netstat_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) MetaWrite(ctx context.Context, in *MetaWriteRequest, opts ...grpc.CallOption) (*MetaWriteResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MetaWriteResponse) err := c.cc.Invoke(ctx, MachineService_MetaWrite_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *machineServiceClient) MetaDelete(ctx context.Context, in *MetaDeleteRequest, opts ...grpc.CallOption) (*MetaDeleteResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(MetaDeleteResponse) err := c.cc.Invoke(ctx, MachineService_MetaDelete_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // Deprecated: Do not use. func (c *machineServiceClient) ImageList(ctx context.Context, in *ImageListRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ImageListResponse], error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) stream, err := c.cc.NewStream(ctx, &MachineService_ServiceDesc.Streams[11], MachineService_ImageList_FullMethodName, cOpts...) if err != nil { return nil, err } x := &grpc.GenericClientStream[ImageListRequest, ImageListResponse]{ClientStream: stream} if err := x.ClientStream.SendMsg(in); err != nil { return nil, err } if err := x.ClientStream.CloseSend(); err != nil { return nil, err } return x, nil } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ImageListClient = grpc.ServerStreamingClient[ImageListResponse] // Deprecated: Do not use. func (c *machineServiceClient) ImagePull(ctx context.Context, in *ImagePullRequest, opts ...grpc.CallOption) (*ImagePullResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ImagePullResponse) err := c.cc.Invoke(ctx, MachineService_ImagePull_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // MachineServiceServer is the server API for MachineService service. // All implementations must embed UnimplementedMachineServiceServer // for forward compatibility. // // The machine service definition. type MachineServiceServer interface { ApplyConfiguration(context.Context, *ApplyConfigurationRequest) (*ApplyConfigurationResponse, error) // Bootstrap method makes control plane node enter etcd bootstrap mode. // Node aborts etcd join sequence and creates single-node etcd cluster. // If recover_etcd argument is specified, etcd is recovered from a snapshot // uploaded with EtcdRecover. Bootstrap(context.Context, *BootstrapRequest) (*BootstrapResponse, error) Containers(context.Context, *ContainersRequest) (*ContainersResponse, error) Copy(*CopyRequest, grpc.ServerStreamingServer[common.Data]) error CPUFreqStats(context.Context, *emptypb.Empty) (*CPUFreqStatsResponse, error) CPUInfo(context.Context, *emptypb.Empty) (*CPUInfoResponse, error) DiskStats(context.Context, *emptypb.Empty) (*DiskStatsResponse, error) Dmesg(*DmesgRequest, grpc.ServerStreamingServer[common.Data]) error Events(*EventsRequest, grpc.ServerStreamingServer[Event]) error EtcdMemberList(context.Context, *EtcdMemberListRequest) (*EtcdMemberListResponse, error) // EtcdRemoveMemberByID removes a member from the etcd cluster identified by member ID. // This API should be used to remove members which don't have an associated Talos node anymore. // To remove a member with a running Talos node, use EtcdLeaveCluster API on the node to be removed. EtcdRemoveMemberByID(context.Context, *EtcdRemoveMemberByIDRequest) (*EtcdRemoveMemberByIDResponse, error) EtcdLeaveCluster(context.Context, *EtcdLeaveClusterRequest) (*EtcdLeaveClusterResponse, error) EtcdForfeitLeadership(context.Context, *EtcdForfeitLeadershipRequest) (*EtcdForfeitLeadershipResponse, error) // EtcdRecover method uploads etcd data snapshot created with EtcdSnapshot // to the node. // Snapshot can be later used to recover the cluster via Bootstrap method. EtcdRecover(grpc.ClientStreamingServer[common.Data, EtcdRecoverResponse]) error // EtcdSnapshot method creates etcd data snapshot (backup) from the local etcd instance // and streams it back to the client. // This method is available only on control plane nodes (which run etcd). EtcdSnapshot(*EtcdSnapshotRequest, grpc.ServerStreamingServer[common.Data]) error // EtcdAlarmList lists etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). EtcdAlarmList(context.Context, *emptypb.Empty) (*EtcdAlarmListResponse, error) // EtcdAlarmDisarm disarms etcd alarms for the current node. // This method is available only on control plane nodes (which run etcd). EtcdAlarmDisarm(context.Context, *emptypb.Empty) (*EtcdAlarmDisarmResponse, error) // EtcdDefragment defragments etcd data directory for the current node. // Defragmentation is a resource-heavy operation, so it should only run on a specific // node. // This method is available only on control plane nodes (which run etcd). EtcdDefragment(context.Context, *emptypb.Empty) (*EtcdDefragmentResponse, error) // EtcdStatus returns etcd status for the current member. // This method is available only on control plane nodes (which run etcd). EtcdStatus(context.Context, *emptypb.Empty) (*EtcdStatusResponse, error) // EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeValidate(context.Context, *EtcdDowngradeValidateRequest) (*EtcdDowngradeValidateResponse, error) // EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeEnable(context.Context, *EtcdDowngradeEnableRequest) (*EtcdDowngradeEnableResponse, error) // EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. // This method is available only on control plane nodes (which run etcd). EtcdDowngradeCancel(context.Context, *emptypb.Empty) (*EtcdDowngradeCancelResponse, error) Hostname(context.Context, *emptypb.Empty) (*HostnameResponse, error) Kubeconfig(*emptypb.Empty, grpc.ServerStreamingServer[common.Data]) error List(*ListRequest, grpc.ServerStreamingServer[FileInfo]) error DiskUsage(*DiskUsageRequest, grpc.ServerStreamingServer[DiskUsageInfo]) error LoadAvg(context.Context, *emptypb.Empty) (*LoadAvgResponse, error) Logs(*LogsRequest, grpc.ServerStreamingServer[common.Data]) error LogsContainers(context.Context, *emptypb.Empty) (*LogsContainersResponse, error) Memory(context.Context, *emptypb.Empty) (*MemoryResponse, error) Mounts(context.Context, *emptypb.Empty) (*MountsResponse, error) NetworkDeviceStats(context.Context, *emptypb.Empty) (*NetworkDeviceStatsResponse, error) Processes(context.Context, *emptypb.Empty) (*ProcessesResponse, error) Read(*ReadRequest, grpc.ServerStreamingServer[common.Data]) error Reboot(context.Context, *RebootRequest) (*RebootResponse, error) Restart(context.Context, *RestartRequest) (*RestartResponse, error) Rollback(context.Context, *RollbackRequest) (*RollbackResponse, error) Reset(context.Context, *ResetRequest) (*ResetResponse, error) ServiceList(context.Context, *emptypb.Empty) (*ServiceListResponse, error) ServiceRestart(context.Context, *ServiceRestartRequest) (*ServiceRestartResponse, error) ServiceStart(context.Context, *ServiceStartRequest) (*ServiceStartResponse, error) ServiceStop(context.Context, *ServiceStopRequest) (*ServiceStopResponse, error) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) Stats(context.Context, *StatsRequest) (*StatsResponse, error) SystemStat(context.Context, *emptypb.Empty) (*SystemStatResponse, error) // Deprecated: Do not use. // Upgrade initiates the upgrade of the node to a new version of Talos. // // Use LifecycleService Upgrade RPC instead. Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) Version(context.Context, *emptypb.Empty) (*VersionResponse, error) // GenerateClientConfiguration generates talosctl client configuration (talosconfig). GenerateClientConfiguration(context.Context, *GenerateClientConfigurationRequest) (*GenerateClientConfigurationResponse, error) // PacketCapture performs packet capture and streams back pcap file. PacketCapture(*PacketCaptureRequest, grpc.ServerStreamingServer[common.Data]) error // Netstat provides information about network connections. Netstat(context.Context, *NetstatRequest) (*NetstatResponse, error) // MetaWrite writes a META key-value pair. MetaWrite(context.Context, *MetaWriteRequest) (*MetaWriteResponse, error) // MetaDelete deletes a META key. MetaDelete(context.Context, *MetaDeleteRequest) (*MetaDeleteResponse, error) // Deprecated: Do not use. // ImageList lists images in the CRI. // // Use ImageService List RPC instead. ImageList(*ImageListRequest, grpc.ServerStreamingServer[ImageListResponse]) error // Deprecated: Do not use. // ImagePull pulls an image into the CRI. // // Use ImageService Pull RPC instead. ImagePull(context.Context, *ImagePullRequest) (*ImagePullResponse, error) mustEmbedUnimplementedMachineServiceServer() } // UnimplementedMachineServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedMachineServiceServer struct{} func (UnimplementedMachineServiceServer) ApplyConfiguration(context.Context, *ApplyConfigurationRequest) (*ApplyConfigurationResponse, error) { return nil, status.Error(codes.Unimplemented, "method ApplyConfiguration not implemented") } func (UnimplementedMachineServiceServer) Bootstrap(context.Context, *BootstrapRequest) (*BootstrapResponse, error) { return nil, status.Error(codes.Unimplemented, "method Bootstrap not implemented") } func (UnimplementedMachineServiceServer) Containers(context.Context, *ContainersRequest) (*ContainersResponse, error) { return nil, status.Error(codes.Unimplemented, "method Containers not implemented") } func (UnimplementedMachineServiceServer) Copy(*CopyRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method Copy not implemented") } func (UnimplementedMachineServiceServer) CPUFreqStats(context.Context, *emptypb.Empty) (*CPUFreqStatsResponse, error) { return nil, status.Error(codes.Unimplemented, "method CPUFreqStats not implemented") } func (UnimplementedMachineServiceServer) CPUInfo(context.Context, *emptypb.Empty) (*CPUInfoResponse, error) { return nil, status.Error(codes.Unimplemented, "method CPUInfo not implemented") } func (UnimplementedMachineServiceServer) DiskStats(context.Context, *emptypb.Empty) (*DiskStatsResponse, error) { return nil, status.Error(codes.Unimplemented, "method DiskStats not implemented") } func (UnimplementedMachineServiceServer) Dmesg(*DmesgRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method Dmesg not implemented") } func (UnimplementedMachineServiceServer) Events(*EventsRequest, grpc.ServerStreamingServer[Event]) error { return status.Error(codes.Unimplemented, "method Events not implemented") } func (UnimplementedMachineServiceServer) EtcdMemberList(context.Context, *EtcdMemberListRequest) (*EtcdMemberListResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdMemberList not implemented") } func (UnimplementedMachineServiceServer) EtcdRemoveMemberByID(context.Context, *EtcdRemoveMemberByIDRequest) (*EtcdRemoveMemberByIDResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdRemoveMemberByID not implemented") } func (UnimplementedMachineServiceServer) EtcdLeaveCluster(context.Context, *EtcdLeaveClusterRequest) (*EtcdLeaveClusterResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdLeaveCluster not implemented") } func (UnimplementedMachineServiceServer) EtcdForfeitLeadership(context.Context, *EtcdForfeitLeadershipRequest) (*EtcdForfeitLeadershipResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdForfeitLeadership not implemented") } func (UnimplementedMachineServiceServer) EtcdRecover(grpc.ClientStreamingServer[common.Data, EtcdRecoverResponse]) error { return status.Error(codes.Unimplemented, "method EtcdRecover not implemented") } func (UnimplementedMachineServiceServer) EtcdSnapshot(*EtcdSnapshotRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method EtcdSnapshot not implemented") } func (UnimplementedMachineServiceServer) EtcdAlarmList(context.Context, *emptypb.Empty) (*EtcdAlarmListResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdAlarmList not implemented") } func (UnimplementedMachineServiceServer) EtcdAlarmDisarm(context.Context, *emptypb.Empty) (*EtcdAlarmDisarmResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdAlarmDisarm not implemented") } func (UnimplementedMachineServiceServer) EtcdDefragment(context.Context, *emptypb.Empty) (*EtcdDefragmentResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdDefragment not implemented") } func (UnimplementedMachineServiceServer) EtcdStatus(context.Context, *emptypb.Empty) (*EtcdStatusResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdStatus not implemented") } func (UnimplementedMachineServiceServer) EtcdDowngradeValidate(context.Context, *EtcdDowngradeValidateRequest) (*EtcdDowngradeValidateResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdDowngradeValidate not implemented") } func (UnimplementedMachineServiceServer) EtcdDowngradeEnable(context.Context, *EtcdDowngradeEnableRequest) (*EtcdDowngradeEnableResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdDowngradeEnable not implemented") } func (UnimplementedMachineServiceServer) EtcdDowngradeCancel(context.Context, *emptypb.Empty) (*EtcdDowngradeCancelResponse, error) { return nil, status.Error(codes.Unimplemented, "method EtcdDowngradeCancel not implemented") } func (UnimplementedMachineServiceServer) Hostname(context.Context, *emptypb.Empty) (*HostnameResponse, error) { return nil, status.Error(codes.Unimplemented, "method Hostname not implemented") } func (UnimplementedMachineServiceServer) Kubeconfig(*emptypb.Empty, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method Kubeconfig not implemented") } func (UnimplementedMachineServiceServer) List(*ListRequest, grpc.ServerStreamingServer[FileInfo]) error { return status.Error(codes.Unimplemented, "method List not implemented") } func (UnimplementedMachineServiceServer) DiskUsage(*DiskUsageRequest, grpc.ServerStreamingServer[DiskUsageInfo]) error { return status.Error(codes.Unimplemented, "method DiskUsage not implemented") } func (UnimplementedMachineServiceServer) LoadAvg(context.Context, *emptypb.Empty) (*LoadAvgResponse, error) { return nil, status.Error(codes.Unimplemented, "method LoadAvg not implemented") } func (UnimplementedMachineServiceServer) Logs(*LogsRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method Logs not implemented") } func (UnimplementedMachineServiceServer) LogsContainers(context.Context, *emptypb.Empty) (*LogsContainersResponse, error) { return nil, status.Error(codes.Unimplemented, "method LogsContainers not implemented") } func (UnimplementedMachineServiceServer) Memory(context.Context, *emptypb.Empty) (*MemoryResponse, error) { return nil, status.Error(codes.Unimplemented, "method Memory not implemented") } func (UnimplementedMachineServiceServer) Mounts(context.Context, *emptypb.Empty) (*MountsResponse, error) { return nil, status.Error(codes.Unimplemented, "method Mounts not implemented") } func (UnimplementedMachineServiceServer) NetworkDeviceStats(context.Context, *emptypb.Empty) (*NetworkDeviceStatsResponse, error) { return nil, status.Error(codes.Unimplemented, "method NetworkDeviceStats not implemented") } func (UnimplementedMachineServiceServer) Processes(context.Context, *emptypb.Empty) (*ProcessesResponse, error) { return nil, status.Error(codes.Unimplemented, "method Processes not implemented") } func (UnimplementedMachineServiceServer) Read(*ReadRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method Read not implemented") } func (UnimplementedMachineServiceServer) Reboot(context.Context, *RebootRequest) (*RebootResponse, error) { return nil, status.Error(codes.Unimplemented, "method Reboot not implemented") } func (UnimplementedMachineServiceServer) Restart(context.Context, *RestartRequest) (*RestartResponse, error) { return nil, status.Error(codes.Unimplemented, "method Restart not implemented") } func (UnimplementedMachineServiceServer) Rollback(context.Context, *RollbackRequest) (*RollbackResponse, error) { return nil, status.Error(codes.Unimplemented, "method Rollback not implemented") } func (UnimplementedMachineServiceServer) Reset(context.Context, *ResetRequest) (*ResetResponse, error) { return nil, status.Error(codes.Unimplemented, "method Reset not implemented") } func (UnimplementedMachineServiceServer) ServiceList(context.Context, *emptypb.Empty) (*ServiceListResponse, error) { return nil, status.Error(codes.Unimplemented, "method ServiceList not implemented") } func (UnimplementedMachineServiceServer) ServiceRestart(context.Context, *ServiceRestartRequest) (*ServiceRestartResponse, error) { return nil, status.Error(codes.Unimplemented, "method ServiceRestart not implemented") } func (UnimplementedMachineServiceServer) ServiceStart(context.Context, *ServiceStartRequest) (*ServiceStartResponse, error) { return nil, status.Error(codes.Unimplemented, "method ServiceStart not implemented") } func (UnimplementedMachineServiceServer) ServiceStop(context.Context, *ServiceStopRequest) (*ServiceStopResponse, error) { return nil, status.Error(codes.Unimplemented, "method ServiceStop not implemented") } func (UnimplementedMachineServiceServer) Shutdown(context.Context, *ShutdownRequest) (*ShutdownResponse, error) { return nil, status.Error(codes.Unimplemented, "method Shutdown not implemented") } func (UnimplementedMachineServiceServer) Stats(context.Context, *StatsRequest) (*StatsResponse, error) { return nil, status.Error(codes.Unimplemented, "method Stats not implemented") } func (UnimplementedMachineServiceServer) SystemStat(context.Context, *emptypb.Empty) (*SystemStatResponse, error) { return nil, status.Error(codes.Unimplemented, "method SystemStat not implemented") } func (UnimplementedMachineServiceServer) Upgrade(context.Context, *UpgradeRequest) (*UpgradeResponse, error) { return nil, status.Error(codes.Unimplemented, "method Upgrade not implemented") } func (UnimplementedMachineServiceServer) Version(context.Context, *emptypb.Empty) (*VersionResponse, error) { return nil, status.Error(codes.Unimplemented, "method Version not implemented") } func (UnimplementedMachineServiceServer) GenerateClientConfiguration(context.Context, *GenerateClientConfigurationRequest) (*GenerateClientConfigurationResponse, error) { return nil, status.Error(codes.Unimplemented, "method GenerateClientConfiguration not implemented") } func (UnimplementedMachineServiceServer) PacketCapture(*PacketCaptureRequest, grpc.ServerStreamingServer[common.Data]) error { return status.Error(codes.Unimplemented, "method PacketCapture not implemented") } func (UnimplementedMachineServiceServer) Netstat(context.Context, *NetstatRequest) (*NetstatResponse, error) { return nil, status.Error(codes.Unimplemented, "method Netstat not implemented") } func (UnimplementedMachineServiceServer) MetaWrite(context.Context, *MetaWriteRequest) (*MetaWriteResponse, error) { return nil, status.Error(codes.Unimplemented, "method MetaWrite not implemented") } func (UnimplementedMachineServiceServer) MetaDelete(context.Context, *MetaDeleteRequest) (*MetaDeleteResponse, error) { return nil, status.Error(codes.Unimplemented, "method MetaDelete not implemented") } func (UnimplementedMachineServiceServer) ImageList(*ImageListRequest, grpc.ServerStreamingServer[ImageListResponse]) error { return status.Error(codes.Unimplemented, "method ImageList not implemented") } func (UnimplementedMachineServiceServer) ImagePull(context.Context, *ImagePullRequest) (*ImagePullResponse, error) { return nil, status.Error(codes.Unimplemented, "method ImagePull not implemented") } func (UnimplementedMachineServiceServer) mustEmbedUnimplementedMachineServiceServer() {} func (UnimplementedMachineServiceServer) testEmbeddedByValue() {} // UnsafeMachineServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to MachineServiceServer will // result in compilation errors. type UnsafeMachineServiceServer interface { mustEmbedUnimplementedMachineServiceServer() } func RegisterMachineServiceServer(s grpc.ServiceRegistrar, srv MachineServiceServer) { // If the following call panics, it indicates UnimplementedMachineServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&MachineService_ServiceDesc, srv) } func _MachineService_ApplyConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ApplyConfigurationRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ApplyConfiguration(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ApplyConfiguration_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ApplyConfiguration(ctx, req.(*ApplyConfigurationRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Bootstrap_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(BootstrapRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Bootstrap(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Bootstrap_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Bootstrap(ctx, req.(*BootstrapRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Containers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ContainersRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Containers(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Containers_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Containers(ctx, req.(*ContainersRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Copy_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(CopyRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Copy(m, &grpc.GenericServerStream[CopyRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_CopyServer = grpc.ServerStreamingServer[common.Data] func _MachineService_CPUFreqStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).CPUFreqStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_CPUFreqStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).CPUFreqStats(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_CPUInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).CPUInfo(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_CPUInfo_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).CPUInfo(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_DiskStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).DiskStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_DiskStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).DiskStats(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Dmesg_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(DmesgRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Dmesg(m, &grpc.GenericServerStream[DmesgRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_DmesgServer = grpc.ServerStreamingServer[common.Data] func _MachineService_Events_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(EventsRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Events(m, &grpc.GenericServerStream[EventsRequest, Event]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EventsServer = grpc.ServerStreamingServer[Event] func _MachineService_EtcdMemberList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdMemberListRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdMemberList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdMemberList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdMemberList(ctx, req.(*EtcdMemberListRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdRemoveMemberByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdRemoveMemberByIDRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdRemoveMemberByID(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdRemoveMemberByID_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdRemoveMemberByID(ctx, req.(*EtcdRemoveMemberByIDRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdLeaveCluster_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdLeaveClusterRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdLeaveCluster(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdLeaveCluster_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdLeaveCluster(ctx, req.(*EtcdLeaveClusterRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdForfeitLeadership_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdForfeitLeadershipRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdForfeitLeadership(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdForfeitLeadership_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdForfeitLeadership(ctx, req.(*EtcdForfeitLeadershipRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdRecover_Handler(srv interface{}, stream grpc.ServerStream) error { return srv.(MachineServiceServer).EtcdRecover(&grpc.GenericServerStream[common.Data, EtcdRecoverResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EtcdRecoverServer = grpc.ClientStreamingServer[common.Data, EtcdRecoverResponse] func _MachineService_EtcdSnapshot_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(EtcdSnapshotRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).EtcdSnapshot(m, &grpc.GenericServerStream[EtcdSnapshotRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_EtcdSnapshotServer = grpc.ServerStreamingServer[common.Data] func _MachineService_EtcdAlarmList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdAlarmList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdAlarmList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdAlarmList(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdAlarmDisarm_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdAlarmDisarm(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdAlarmDisarm_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdAlarmDisarm(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdDefragment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdDefragment(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdDefragment_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdDefragment(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdStatus(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdStatus_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdStatus(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdDowngradeValidate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdDowngradeValidateRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdDowngradeValidate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdDowngradeValidate_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdDowngradeValidate(ctx, req.(*EtcdDowngradeValidateRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdDowngradeEnable_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(EtcdDowngradeEnableRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdDowngradeEnable(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdDowngradeEnable_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdDowngradeEnable(ctx, req.(*EtcdDowngradeEnableRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_EtcdDowngradeCancel_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).EtcdDowngradeCancel(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_EtcdDowngradeCancel_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).EtcdDowngradeCancel(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Hostname_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Hostname(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Hostname_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Hostname(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Kubeconfig_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(emptypb.Empty) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Kubeconfig(m, &grpc.GenericServerStream[emptypb.Empty, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_KubeconfigServer = grpc.ServerStreamingServer[common.Data] func _MachineService_List_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ListRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).List(m, &grpc.GenericServerStream[ListRequest, FileInfo]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ListServer = grpc.ServerStreamingServer[FileInfo] func _MachineService_DiskUsage_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(DiskUsageRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).DiskUsage(m, &grpc.GenericServerStream[DiskUsageRequest, DiskUsageInfo]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_DiskUsageServer = grpc.ServerStreamingServer[DiskUsageInfo] func _MachineService_LoadAvg_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).LoadAvg(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_LoadAvg_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).LoadAvg(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Logs_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(LogsRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Logs(m, &grpc.GenericServerStream[LogsRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_LogsServer = grpc.ServerStreamingServer[common.Data] func _MachineService_LogsContainers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).LogsContainers(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_LogsContainers_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).LogsContainers(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Memory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Memory(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Memory_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Memory(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Mounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Mounts(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Mounts_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Mounts(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_NetworkDeviceStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).NetworkDeviceStats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_NetworkDeviceStats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).NetworkDeviceStats(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Processes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Processes(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Processes_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Processes(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Read_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ReadRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).Read(m, &grpc.GenericServerStream[ReadRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ReadServer = grpc.ServerStreamingServer[common.Data] func _MachineService_Reboot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RebootRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Reboot(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Reboot_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Reboot(ctx, req.(*RebootRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Restart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RestartRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Restart(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Restart_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Restart(ctx, req.(*RestartRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Rollback_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RollbackRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Rollback(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Rollback_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Rollback(ctx, req.(*RollbackRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Reset_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ResetRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Reset(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Reset_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Reset(ctx, req.(*ResetRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_ServiceList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ServiceList(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ServiceList_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ServiceList(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_ServiceRestart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ServiceRestartRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ServiceRestart(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ServiceRestart_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ServiceRestart(ctx, req.(*ServiceRestartRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_ServiceStart_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ServiceStartRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ServiceStart(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ServiceStart_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ServiceStart(ctx, req.(*ServiceStartRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_ServiceStop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ServiceStopRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ServiceStop(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ServiceStop_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ServiceStop(ctx, req.(*ServiceStopRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ShutdownRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Shutdown(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Shutdown_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Shutdown(ctx, req.(*ShutdownRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Stats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(StatsRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Stats(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Stats_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Stats(ctx, req.(*StatsRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_SystemStat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).SystemStat(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_SystemStat_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).SystemStat(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_Upgrade_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(UpgradeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Upgrade(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Upgrade_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Upgrade(ctx, req.(*UpgradeRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Version(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Version_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Version(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _MachineService_GenerateClientConfiguration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GenerateClientConfigurationRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).GenerateClientConfiguration(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_GenerateClientConfiguration_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).GenerateClientConfiguration(ctx, req.(*GenerateClientConfigurationRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_PacketCapture_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(PacketCaptureRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).PacketCapture(m, &grpc.GenericServerStream[PacketCaptureRequest, common.Data]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_PacketCaptureServer = grpc.ServerStreamingServer[common.Data] func _MachineService_Netstat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(NetstatRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).Netstat(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_Netstat_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).Netstat(ctx, req.(*NetstatRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_MetaWrite_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MetaWriteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).MetaWrite(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_MetaWrite_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).MetaWrite(ctx, req.(*MetaWriteRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_MetaDelete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MetaDeleteRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).MetaDelete(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_MetaDelete_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).MetaDelete(ctx, req.(*MetaDeleteRequest)) } return interceptor(ctx, in, info, handler) } func _MachineService_ImageList_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(ImageListRequest) if err := stream.RecvMsg(m); err != nil { return err } return srv.(MachineServiceServer).ImageList(m, &grpc.GenericServerStream[ImageListRequest, ImageListResponse]{ServerStream: stream}) } // This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. type MachineService_ImageListServer = grpc.ServerStreamingServer[ImageListResponse] func _MachineService_ImagePull_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ImagePullRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(MachineServiceServer).ImagePull(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: MachineService_ImagePull_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(MachineServiceServer).ImagePull(ctx, req.(*ImagePullRequest)) } return interceptor(ctx, in, info, handler) } // MachineService_ServiceDesc is the grpc.ServiceDesc for MachineService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var MachineService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "machine.MachineService", HandlerType: (*MachineServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "ApplyConfiguration", Handler: _MachineService_ApplyConfiguration_Handler, }, { MethodName: "Bootstrap", Handler: _MachineService_Bootstrap_Handler, }, { MethodName: "Containers", Handler: _MachineService_Containers_Handler, }, { MethodName: "CPUFreqStats", Handler: _MachineService_CPUFreqStats_Handler, }, { MethodName: "CPUInfo", Handler: _MachineService_CPUInfo_Handler, }, { MethodName: "DiskStats", Handler: _MachineService_DiskStats_Handler, }, { MethodName: "EtcdMemberList", Handler: _MachineService_EtcdMemberList_Handler, }, { MethodName: "EtcdRemoveMemberByID", Handler: _MachineService_EtcdRemoveMemberByID_Handler, }, { MethodName: "EtcdLeaveCluster", Handler: _MachineService_EtcdLeaveCluster_Handler, }, { MethodName: "EtcdForfeitLeadership", Handler: _MachineService_EtcdForfeitLeadership_Handler, }, { MethodName: "EtcdAlarmList", Handler: _MachineService_EtcdAlarmList_Handler, }, { MethodName: "EtcdAlarmDisarm", Handler: _MachineService_EtcdAlarmDisarm_Handler, }, { MethodName: "EtcdDefragment", Handler: _MachineService_EtcdDefragment_Handler, }, { MethodName: "EtcdStatus", Handler: _MachineService_EtcdStatus_Handler, }, { MethodName: "EtcdDowngradeValidate", Handler: _MachineService_EtcdDowngradeValidate_Handler, }, { MethodName: "EtcdDowngradeEnable", Handler: _MachineService_EtcdDowngradeEnable_Handler, }, { MethodName: "EtcdDowngradeCancel", Handler: _MachineService_EtcdDowngradeCancel_Handler, }, { MethodName: "Hostname", Handler: _MachineService_Hostname_Handler, }, { MethodName: "LoadAvg", Handler: _MachineService_LoadAvg_Handler, }, { MethodName: "LogsContainers", Handler: _MachineService_LogsContainers_Handler, }, { MethodName: "Memory", Handler: _MachineService_Memory_Handler, }, { MethodName: "Mounts", Handler: _MachineService_Mounts_Handler, }, { MethodName: "NetworkDeviceStats", Handler: _MachineService_NetworkDeviceStats_Handler, }, { MethodName: "Processes", Handler: _MachineService_Processes_Handler, }, { MethodName: "Reboot", Handler: _MachineService_Reboot_Handler, }, { MethodName: "Restart", Handler: _MachineService_Restart_Handler, }, { MethodName: "Rollback", Handler: _MachineService_Rollback_Handler, }, { MethodName: "Reset", Handler: _MachineService_Reset_Handler, }, { MethodName: "ServiceList", Handler: _MachineService_ServiceList_Handler, }, { MethodName: "ServiceRestart", Handler: _MachineService_ServiceRestart_Handler, }, { MethodName: "ServiceStart", Handler: _MachineService_ServiceStart_Handler, }, { MethodName: "ServiceStop", Handler: _MachineService_ServiceStop_Handler, }, { MethodName: "Shutdown", Handler: _MachineService_Shutdown_Handler, }, { MethodName: "Stats", Handler: _MachineService_Stats_Handler, }, { MethodName: "SystemStat", Handler: _MachineService_SystemStat_Handler, }, { MethodName: "Upgrade", Handler: _MachineService_Upgrade_Handler, }, { MethodName: "Version", Handler: _MachineService_Version_Handler, }, { MethodName: "GenerateClientConfiguration", Handler: _MachineService_GenerateClientConfiguration_Handler, }, { MethodName: "Netstat", Handler: _MachineService_Netstat_Handler, }, { MethodName: "MetaWrite", Handler: _MachineService_MetaWrite_Handler, }, { MethodName: "MetaDelete", Handler: _MachineService_MetaDelete_Handler, }, { MethodName: "ImagePull", Handler: _MachineService_ImagePull_Handler, }, }, Streams: []grpc.StreamDesc{ { StreamName: "Copy", Handler: _MachineService_Copy_Handler, ServerStreams: true, }, { StreamName: "Dmesg", Handler: _MachineService_Dmesg_Handler, ServerStreams: true, }, { StreamName: "Events", Handler: _MachineService_Events_Handler, ServerStreams: true, }, { StreamName: "EtcdRecover", Handler: _MachineService_EtcdRecover_Handler, ClientStreams: true, }, { StreamName: "EtcdSnapshot", Handler: _MachineService_EtcdSnapshot_Handler, ServerStreams: true, }, { StreamName: "Kubeconfig", Handler: _MachineService_Kubeconfig_Handler, ServerStreams: true, }, { StreamName: "List", Handler: _MachineService_List_Handler, ServerStreams: true, }, { StreamName: "DiskUsage", Handler: _MachineService_DiskUsage_Handler, ServerStreams: true, }, { StreamName: "Logs", Handler: _MachineService_Logs_Handler, ServerStreams: true, }, { StreamName: "Read", Handler: _MachineService_Read_Handler, ServerStreams: true, }, { StreamName: "PacketCapture", Handler: _MachineService_PacketCapture_Handler, ServerStreams: true, }, { StreamName: "ImageList", Handler: _MachineService_ImageList_Handler, ServerStreams: true, }, }, Metadata: "machine/machine.proto", } ================================================ FILE: pkg/machinery/api/machine/machine_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: machine/machine.proto package machine import ( binary "encoding/binary" fmt "fmt" io "io" math "math" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" anypb "github.com/planetscale/vtprotobuf/types/known/anypb" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" timestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" anypb1 "google.golang.org/protobuf/types/known/anypb" durationpb1 "google.golang.org/protobuf/types/known/durationpb" timestamppb1 "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ApplyConfigurationRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ApplyConfigurationRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ApplyConfigurationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TryModeTimeout != nil { size, err := (*durationpb.Duration)(m.TryModeTimeout).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.DryRun { i-- if m.DryRun { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x20 } if len(m.Data) > 0 { i -= len(m.Data) copy(dAtA[i:], m.Data) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ApplyConfiguration) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ApplyConfiguration) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ApplyConfiguration) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ModeDetails) > 0 { i -= len(m.ModeDetails) copy(dAtA[i:], m.ModeDetails) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ModeDetails))) i-- dAtA[i] = 0x22 } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x18 } if len(m.Warnings) > 0 { for iNdEx := len(m.Warnings) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Warnings[iNdEx]) copy(dAtA[i:], m.Warnings[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Warnings[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ApplyConfigurationResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ApplyConfigurationResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ApplyConfigurationResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RebootRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RebootRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RebootRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Reboot) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Reboot) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Reboot) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ActorId) > 0 { i -= len(m.ActorId) copy(dAtA[i:], m.ActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ActorId))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RebootResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RebootResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RebootResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *BootstrapRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BootstrapRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BootstrapRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.RecoverSkipHashCheck { i-- if m.RecoverSkipHashCheck { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.RecoverEtcd { i-- if m.RecoverEtcd { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Bootstrap) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Bootstrap) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Bootstrap) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BootstrapResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BootstrapResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BootstrapResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *SequenceEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SequenceEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SequenceEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Error != nil { if vtmsg, ok := interface{}(m.Error).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Error) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x10 } if len(m.Sequence) > 0 { i -= len(m.Sequence) copy(dAtA[i:], m.Sequence) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Sequence))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PhaseEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PhaseEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PhaseEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x10 } if len(m.Phase) > 0 { i -= len(m.Phase) copy(dAtA[i:], m.Phase) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Phase))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TaskEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TaskEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TaskEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x10 } if len(m.Task) > 0 { i -= len(m.Task) copy(dAtA[i:], m.Task) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Task))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceStateEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStateEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStateEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Health != nil { size, err := m.Health.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if len(m.Message) > 0 { i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0x1a } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x10 } if len(m.Service) > 0 { i -= len(m.Service) copy(dAtA[i:], m.Service) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Service))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RestartEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RestartEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RestartEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Cmd != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Cmd)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ConfigLoadErrorEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigLoadErrorEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigLoadErrorEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Error))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ConfigValidationErrorEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigValidationErrorEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigValidationErrorEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Error))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddressEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddressEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AddressEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Addresses[iNdEx]) copy(dAtA[i:], m.Addresses[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Addresses[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MachineStatusEvent_MachineStatus_UnmetCondition) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineStatusEvent_MachineStatus_UnmetCondition) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineStatusEvent_MachineStatus_UnmetCondition) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Reason) > 0 { i -= len(m.Reason) copy(dAtA[i:], m.Reason) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Reason))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MachineStatusEvent_MachineStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineStatusEvent_MachineStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineStatusEvent_MachineStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.UnmetConditions) > 0 { for iNdEx := len(m.UnmetConditions) - 1; iNdEx >= 0; iNdEx-- { size, err := m.UnmetConditions[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MachineStatusEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineStatusEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineStatusEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Status != nil { size, err := m.Status.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Stage != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Stage)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EventsRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EventsRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EventsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.WithActorId) > 0 { i -= len(m.WithActorId) copy(dAtA[i:], m.WithActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.WithActorId))) i-- dAtA[i] = 0x22 } if m.TailSeconds != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TailSeconds)) i-- dAtA[i] = 0x18 } if len(m.TailId) > 0 { i -= len(m.TailId) copy(dAtA[i:], m.TailId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TailId))) i-- dAtA[i] = 0x12 } if m.TailEvents != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TailEvents)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Event) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Event) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Event) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ActorId) > 0 { i -= len(m.ActorId) copy(dAtA[i:], m.ActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ActorId))) i-- dAtA[i] = 0x22 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0x1a } if m.Data != nil { size, err := (*anypb.Any)(m.Data).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetPartitionSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetPartitionSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ResetPartitionSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Wipe { i-- if m.Wipe { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ResetRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x28 } if len(m.UserDisksToWipe) > 0 { for iNdEx := len(m.UserDisksToWipe) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.UserDisksToWipe[iNdEx]) copy(dAtA[i:], m.UserDisksToWipe[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.UserDisksToWipe[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.SystemPartitionsToWipe) > 0 { for iNdEx := len(m.SystemPartitionsToWipe) - 1; iNdEx >= 0; iNdEx-- { size, err := m.SystemPartitionsToWipe[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if m.Reboot { i-- if m.Reboot { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Graceful { i-- if m.Graceful { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Reset) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Reset) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Reset) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ActorId) > 0 { i -= len(m.ActorId) copy(dAtA[i:], m.ActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ActorId))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ResetResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResetResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ResetResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *Shutdown) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Shutdown) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Shutdown) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ActorId) > 0 { i -= len(m.ActorId) copy(dAtA[i:], m.ActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ActorId))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ShutdownRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShutdownRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ShutdownRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Force { i-- if m.Force { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ShutdownResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ShutdownResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ShutdownResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *UpgradeRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UpgradeRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *UpgradeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.RebootMode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RebootMode)) i-- dAtA[i] = 0x28 } if m.Force { i-- if m.Force { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.Stage { i-- if m.Stage { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Preserve { i-- if m.Preserve { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Upgrade) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Upgrade) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Upgrade) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ActorId) > 0 { i -= len(m.ActorId) copy(dAtA[i:], m.ActorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ActorId))) i-- dAtA[i] = 0x1a } if len(m.Ack) > 0 { i -= len(m.Ack) copy(dAtA[i:], m.Ack) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ack))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *UpgradeResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UpgradeResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *UpgradeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ServiceList) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceList) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceList) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Services) > 0 { for iNdEx := len(m.Services) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Services[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceListResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceListResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ServiceInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Health != nil { size, err := m.Health.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Events != nil { size, err := m.Events.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if len(m.State) > 0 { i -= len(m.State) copy(dAtA[i:], m.State) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.State))) i-- dAtA[i] = 0x12 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceEvents) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceEvents) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceEvents) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Events) > 0 { for iNdEx := len(m.Events) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Events[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ServiceEvent) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceEvent) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceEvent) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Ts != nil { size, err := (*timestamppb.Timestamp)(m.Ts).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if len(m.State) > 0 { i -= len(m.State) copy(dAtA[i:], m.State) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.State))) i-- dAtA[i] = 0x12 } if len(m.Msg) > 0 { i -= len(m.Msg) copy(dAtA[i:], m.Msg) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Msg))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceHealth) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceHealth) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceHealth) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.LastChange != nil { size, err := (*timestamppb.Timestamp)(m.LastChange).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if len(m.LastMessage) > 0 { i -= len(m.LastMessage) copy(dAtA[i:], m.LastMessage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LastMessage))) i-- dAtA[i] = 0x1a } if m.Healthy { i-- if m.Healthy { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Unknown { i-- if m.Unknown { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ServiceStartRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStartRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStartRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceStart) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStart) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStart) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Resp) > 0 { i -= len(m.Resp) copy(dAtA[i:], m.Resp) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Resp))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceStartResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStartResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStartResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ServiceStopRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStopRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStopRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceStop) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStop) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStop) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Resp) > 0 { i -= len(m.Resp) copy(dAtA[i:], m.Resp) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Resp))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceStopResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceStopResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceStopResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ServiceRestartRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceRestartRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceRestartRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceRestart) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceRestart) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceRestart) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Resp) > 0 { i -= len(m.Resp) copy(dAtA[i:], m.Resp) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Resp))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ServiceRestartResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceRestartResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceRestartResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *CopyRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CopyRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CopyRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.RootPath) > 0 { i -= len(m.RootPath) copy(dAtA[i:], m.RootPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RootPath))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ListRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ListRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ListRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ReportXattrs { i-- if m.ReportXattrs { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.Types) > 0 { var pksize2 int for _, num := range m.Types { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.Types { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x22 } if m.RecursionDepth != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RecursionDepth)) i-- dAtA[i] = 0x18 } if m.Recurse { i-- if m.Recurse { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Root) > 0 { i -= len(m.Root) copy(dAtA[i:], m.Root) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Root))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiskUsageRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskUsageRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskUsageRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Paths) > 0 { for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Paths[iNdEx]) copy(dAtA[i:], m.Paths[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Paths[iNdEx]))) i-- dAtA[i] = 0x22 } } if m.Threshold != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Threshold)) i-- dAtA[i] = 0x18 } if m.All { i-- if m.All { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.RecursionDepth != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RecursionDepth)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *FileInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *FileInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *FileInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Xattrs) > 0 { for iNdEx := len(m.Xattrs) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Xattrs[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x62 } } if m.Gid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Gid)) i-- dAtA[i] = 0x58 } if m.Uid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Uid)) i-- dAtA[i] = 0x50 } if len(m.RelativeName) > 0 { i -= len(m.RelativeName) copy(dAtA[i:], m.RelativeName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RelativeName))) i-- dAtA[i] = 0x4a } if len(m.Link) > 0 { i -= len(m.Link) copy(dAtA[i:], m.Link) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Link))) i-- dAtA[i] = 0x42 } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Error))) i-- dAtA[i] = 0x3a } if m.IsDir { i-- if m.IsDir { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.Modified != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Modified)) i-- dAtA[i] = 0x28 } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x20 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x18 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Xattr) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Xattr) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Xattr) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Data) > 0 { i -= len(m.Data) copy(dAtA[i:], m.Data) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Data))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiskUsageInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskUsageInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskUsageInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.RelativeName) > 0 { i -= len(m.RelativeName) copy(dAtA[i:], m.RelativeName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RelativeName))) i-- dAtA[i] = 0x2a } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Error))) i-- dAtA[i] = 0x22 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x18 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Mounts) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Mounts) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Mounts) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Stats) > 0 { for iNdEx := len(m.Stats) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Stats[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MountsResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountsResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *MountStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MountedOn) > 0 { i -= len(m.MountedOn) copy(dAtA[i:], m.MountedOn) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MountedOn))) i-- dAtA[i] = 0x22 } if m.Available != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Available)) i-- dAtA[i] = 0x18 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x10 } if len(m.Filesystem) > 0 { i -= len(m.Filesystem) copy(dAtA[i:], m.Filesystem) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Filesystem))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Version) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Version) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Version) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Features != nil { size, err := m.Features.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Platform != nil { size, err := m.Platform.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Version != nil { size, err := m.Version.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VersionResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VersionResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VersionResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *VersionInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VersionInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VersionInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Arch) > 0 { i -= len(m.Arch) copy(dAtA[i:], m.Arch) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Arch))) i-- dAtA[i] = 0x32 } if len(m.Os) > 0 { i -= len(m.Os) copy(dAtA[i:], m.Os) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Os))) i-- dAtA[i] = 0x2a } if len(m.GoVersion) > 0 { i -= len(m.GoVersion) copy(dAtA[i:], m.GoVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.GoVersion))) i-- dAtA[i] = 0x22 } if len(m.Built) > 0 { i -= len(m.Built) copy(dAtA[i:], m.Built) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Built))) i-- dAtA[i] = 0x1a } if len(m.Sha) > 0 { i -= len(m.Sha) copy(dAtA[i:], m.Sha) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Sha))) i-- dAtA[i] = 0x12 } if len(m.Tag) > 0 { i -= len(m.Tag) copy(dAtA[i:], m.Tag) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Tag))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PlatformInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PlatformInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PlatformInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Mode) > 0 { i -= len(m.Mode) copy(dAtA[i:], m.Mode) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Mode))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *FeaturesInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *FeaturesInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *FeaturesInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Rbac { i-- if m.Rbac { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *LogsRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LogsRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LogsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TailLines != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TailLines)) i-- dAtA[i] = 0x28 } if m.Follow { i-- if m.Follow { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.Driver != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Driver)) i-- dAtA[i] = 0x18 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ReadRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ReadRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ReadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Path) > 0 { i -= len(m.Path) copy(dAtA[i:], m.Path) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Path))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LogsContainer) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LogsContainer) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LogsContainer) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Ids) > 0 { for iNdEx := len(m.Ids) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Ids[iNdEx]) copy(dAtA[i:], m.Ids[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ids[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LogsContainersResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LogsContainersResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LogsContainersResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RollbackRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RollbackRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RollbackRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } return len(dAtA) - i, nil } func (m *Rollback) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Rollback) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Rollback) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RollbackResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RollbackResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RollbackResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ContainersRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ContainersRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ContainersRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Driver != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Driver)) i-- dAtA[i] = 0x10 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ContainerInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ContainerInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ContainerInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Uid) > 0 { i -= len(m.Uid) copy(dAtA[i:], m.Uid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uid))) i-- dAtA[i] = 0x52 } if len(m.InternalId) > 0 { i -= len(m.InternalId) copy(dAtA[i:], m.InternalId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InternalId))) i-- dAtA[i] = 0x4a } if len(m.NetworkNamespace) > 0 { i -= len(m.NetworkNamespace) copy(dAtA[i:], m.NetworkNamespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NetworkNamespace))) i-- dAtA[i] = 0x42 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x3a } if len(m.PodId) > 0 { i -= len(m.PodId) copy(dAtA[i:], m.PodId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PodId))) i-- dAtA[i] = 0x32 } if len(m.Status) > 0 { i -= len(m.Status) copy(dAtA[i:], m.Status) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Status))) i-- dAtA[i] = 0x2a } if m.Pid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Pid)) i-- dAtA[i] = 0x20 } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0x1a } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Container) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Container) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Container) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Containers) > 0 { for iNdEx := len(m.Containers) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Containers[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ContainersResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ContainersResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ContainersResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *DmesgRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DmesgRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DmesgRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Tail { i-- if m.Tail { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Follow { i-- if m.Follow { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ProcessesResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProcessesResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProcessesResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *Process) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Process) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Process) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Processes) > 0 { for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Processes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ProcessInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProcessInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProcessInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x5a } if len(m.Args) > 0 { i -= len(m.Args) copy(dAtA[i:], m.Args) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Args))) i-- dAtA[i] = 0x52 } if len(m.Executable) > 0 { i -= len(m.Executable) copy(dAtA[i:], m.Executable) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Executable))) i-- dAtA[i] = 0x4a } if len(m.Command) > 0 { i -= len(m.Command) copy(dAtA[i:], m.Command) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Command))) i-- dAtA[i] = 0x42 } if m.ResidentMemory != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResidentMemory)) i-- dAtA[i] = 0x38 } if m.VirtualMemory != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.VirtualMemory)) i-- dAtA[i] = 0x30 } if m.CpuTime != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.CpuTime)))) i-- dAtA[i] = 0x29 } if m.Threads != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Threads)) i-- dAtA[i] = 0x20 } if len(m.State) > 0 { i -= len(m.State) copy(dAtA[i:], m.State) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.State))) i-- dAtA[i] = 0x1a } if m.Ppid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ppid)) i-- dAtA[i] = 0x10 } if m.Pid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Pid)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RestartRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RestartRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RestartRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Driver != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Driver)) i-- dAtA[i] = 0x18 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Restart) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Restart) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Restart) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RestartResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RestartResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RestartResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *StatsRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StatsRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StatsRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Driver != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Driver)) i-- dAtA[i] = 0x10 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Stats) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Stats) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Stats) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Stats) > 0 { for iNdEx := len(m.Stats) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Stats[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StatsResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StatsResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StatsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *Stat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Stat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Stat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x3a } if len(m.PodId) > 0 { i -= len(m.PodId) copy(dAtA[i:], m.PodId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PodId))) i-- dAtA[i] = 0x32 } if m.CpuUsage != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuUsage)) i-- dAtA[i] = 0x28 } if m.MemoryUsage != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemoryUsage)) i-- dAtA[i] = 0x20 } if len(m.Id) > 0 { i -= len(m.Id) copy(dAtA[i:], m.Id) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Id))) i-- dAtA[i] = 0x12 } if len(m.Namespace) > 0 { i -= len(m.Namespace) copy(dAtA[i:], m.Namespace) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Namespace))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Memory) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Memory) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Memory) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Meminfo != nil { size, err := m.Meminfo.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MemoryResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemoryResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemoryResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *MemInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Directmap1G != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Directmap1G)) i-- dAtA[i] = 0x3 i-- dAtA[i] = 0x80 } if m.Directmap2M != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Directmap2M)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xf8 } if m.Directmap4K != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Directmap4K)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xf0 } if m.Hugepagesize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagesize)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xe8 } if m.Hugepagessurp != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagessurp)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xe0 } if m.Hugepagesrsvd != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagesrsvd)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xd8 } if m.Hugepagesfree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagesfree)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xd0 } if m.Hugepagestotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagestotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xc8 } if m.Cmafree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Cmafree)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xc0 } if m.Cmatotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Cmatotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xb8 } if m.Shmempmdmapped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Shmempmdmapped)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xb0 } if m.Shmemhugepages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Shmemhugepages)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xa8 } if m.Anonhugepages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Anonhugepages)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xa0 } if m.Hardwarecorrupted != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hardwarecorrupted)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x98 } if m.Vmallocchunk != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Vmallocchunk)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x90 } if m.Vmallocused != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Vmallocused)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x88 } if m.Vmalloctotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Vmalloctotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x80 } if m.Committedas != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Committedas)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xf8 } if m.Commitlimit != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Commitlimit)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xf0 } if m.Writebacktmp != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Writebacktmp)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe8 } if m.Bounce != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Bounce)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe0 } if m.Nfsunstable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Nfsunstable)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd8 } if m.Pagetables != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Pagetables)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd0 } if m.Kernelstack != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Kernelstack)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc8 } if m.Sunreclaim != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Sunreclaim)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc0 } if m.Sreclaimable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Sreclaimable)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb8 } if m.Slab != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Slab)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb0 } if m.Shmem != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Shmem)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa8 } if m.Mapped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mapped)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa0 } if m.Anonpages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Anonpages)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x98 } if m.Writeback != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Writeback)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if m.Dirty != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Dirty)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x88 } if m.Swapfree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Swapfree)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.Swaptotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Swaptotal)) i-- dAtA[i] = 0x78 } if m.Mlocked != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mlocked)) i-- dAtA[i] = 0x70 } if m.Unevictable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Unevictable)) i-- dAtA[i] = 0x68 } if m.Inactivefile != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Inactivefile)) i-- dAtA[i] = 0x60 } if m.Activefile != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Activefile)) i-- dAtA[i] = 0x58 } if m.Inactiveanon != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Inactiveanon)) i-- dAtA[i] = 0x50 } if m.Activeanon != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Activeanon)) i-- dAtA[i] = 0x48 } if m.Inactive != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Inactive)) i-- dAtA[i] = 0x40 } if m.Active != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Active)) i-- dAtA[i] = 0x38 } if m.Swapcached != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Swapcached)) i-- dAtA[i] = 0x30 } if m.Cached != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Cached)) i-- dAtA[i] = 0x28 } if m.Buffers != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Buffers)) i-- dAtA[i] = 0x20 } if m.Memavailable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Memavailable)) i-- dAtA[i] = 0x18 } if m.Memfree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Memfree)) i-- dAtA[i] = 0x10 } if m.Memtotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Memtotal)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *HostnameResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HostnameResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HostnameResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *Hostname) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Hostname) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Hostname) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LoadAvgResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LoadAvgResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LoadAvgResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *LoadAvg) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LoadAvg) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LoadAvg) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Load15 != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Load15)))) i-- dAtA[i] = 0x21 } if m.Load5 != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Load5)))) i-- dAtA[i] = 0x19 } if m.Load1 != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Load1)))) i-- dAtA[i] = 0x11 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SystemStatResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SystemStatResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SystemStatResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *SystemStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SystemStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SystemStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SoftIrq != nil { size, err := m.SoftIrq.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x62 } if m.SoftIrqTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SoftIrqTotal)) i-- dAtA[i] = 0x58 } if m.ProcessBlocked != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessBlocked)) i-- dAtA[i] = 0x50 } if m.ProcessRunning != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessRunning)) i-- dAtA[i] = 0x48 } if m.ProcessCreated != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessCreated)) i-- dAtA[i] = 0x40 } if m.ContextSwitches != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ContextSwitches)) i-- dAtA[i] = 0x38 } if len(m.Irq) > 0 { var pksize2 int for _, num := range m.Irq { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num := range m.Irq { for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x32 } if m.IrqTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IrqTotal)) i-- dAtA[i] = 0x28 } if len(m.Cpu) > 0 { for iNdEx := len(m.Cpu) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Cpu[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } } if m.CpuTotal != nil { size, err := m.CpuTotal.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.BootTime != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BootTime)) i-- dAtA[i] = 0x10 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CPUStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.GuestNice != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.GuestNice)))) i-- dAtA[i] = 0x51 } if m.Guest != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Guest)))) i-- dAtA[i] = 0x49 } if m.Steal != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Steal)))) i-- dAtA[i] = 0x41 } if m.SoftIrq != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.SoftIrq)))) i-- dAtA[i] = 0x39 } if m.Irq != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Irq)))) i-- dAtA[i] = 0x31 } if m.Iowait != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Iowait)))) i-- dAtA[i] = 0x29 } if m.Idle != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Idle)))) i-- dAtA[i] = 0x21 } if m.System != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.System)))) i-- dAtA[i] = 0x19 } if m.Nice != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Nice)))) i-- dAtA[i] = 0x11 } if m.User != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.User)))) i-- dAtA[i] = 0x9 } return len(dAtA) - i, nil } func (m *SoftIRQStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SoftIRQStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SoftIRQStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Rcu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rcu)) i-- dAtA[i] = 0x50 } if m.Hrtimer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hrtimer)) i-- dAtA[i] = 0x48 } if m.Sched != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Sched)) i-- dAtA[i] = 0x40 } if m.Tasklet != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tasklet)) i-- dAtA[i] = 0x38 } if m.BlockIoPoll != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BlockIoPoll)) i-- dAtA[i] = 0x30 } if m.Block != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Block)) i-- dAtA[i] = 0x28 } if m.NetRx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NetRx)) i-- dAtA[i] = 0x20 } if m.NetTx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NetTx)) i-- dAtA[i] = 0x18 } if m.Timer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Timer)) i-- dAtA[i] = 0x10 } if m.Hi != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hi)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CPUFreqStatsResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUFreqStatsResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUFreqStatsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *CPUsFreqStats) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUsFreqStats) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUsFreqStats) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.CpuFreqStats) > 0 { for iNdEx := len(m.CpuFreqStats) - 1; iNdEx >= 0; iNdEx-- { size, err := m.CpuFreqStats[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CPUFreqStats) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUFreqStats) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUFreqStats) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Governor) > 0 { i -= len(m.Governor) copy(dAtA[i:], m.Governor) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Governor))) i-- dAtA[i] = 0x22 } if m.MaximumFrequency != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MaximumFrequency)) i-- dAtA[i] = 0x18 } if m.MinimumFrequency != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MinimumFrequency)) i-- dAtA[i] = 0x10 } if m.CurrentFrequency != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CurrentFrequency)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CPUInfoResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUInfoResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUInfoResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *CPUsInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUsInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUsInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.CpuInfo) > 0 { for iNdEx := len(m.CpuInfo) - 1; iNdEx >= 0; iNdEx-- { size, err := m.CpuInfo[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CPUInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.PowerManagement) > 0 { i -= len(m.PowerManagement) copy(dAtA[i:], m.PowerManagement) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PowerManagement))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd2 } if len(m.AddressSizes) > 0 { i -= len(m.AddressSizes) copy(dAtA[i:], m.AddressSizes) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AddressSizes))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xca } if m.CacheAlignment != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CacheAlignment)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc0 } if m.ClFlushSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ClFlushSize)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb8 } if m.BogoMips != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.BogoMips)))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb1 } if len(m.Bugs) > 0 { for iNdEx := len(m.Bugs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Bugs[iNdEx]) copy(dAtA[i:], m.Bugs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Bugs[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xaa } } if len(m.Flags) > 0 { for iNdEx := len(m.Flags) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Flags[iNdEx]) copy(dAtA[i:], m.Flags[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Flags[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa2 } } if len(m.Wp) > 0 { i -= len(m.Wp) copy(dAtA[i:], m.Wp) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Wp))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if m.CpuIdLevel != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuIdLevel)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if len(m.FpuException) > 0 { i -= len(m.FpuException) copy(dAtA[i:], m.FpuException) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FpuException))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if len(m.Fpu) > 0 { i -= len(m.Fpu) copy(dAtA[i:], m.Fpu) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Fpu))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if len(m.InitialApicId) > 0 { i -= len(m.InitialApicId) copy(dAtA[i:], m.InitialApicId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InitialApicId))) i-- dAtA[i] = 0x7a } if len(m.ApicId) > 0 { i -= len(m.ApicId) copy(dAtA[i:], m.ApicId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ApicId))) i-- dAtA[i] = 0x72 } if m.CpuCores != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CpuCores)) i-- dAtA[i] = 0x68 } if len(m.CoreId) > 0 { i -= len(m.CoreId) copy(dAtA[i:], m.CoreId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CoreId))) i-- dAtA[i] = 0x62 } if m.Siblings != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Siblings)) i-- dAtA[i] = 0x58 } if len(m.PhysicalId) > 0 { i -= len(m.PhysicalId) copy(dAtA[i:], m.PhysicalId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PhysicalId))) i-- dAtA[i] = 0x52 } if len(m.CacheSize) > 0 { i -= len(m.CacheSize) copy(dAtA[i:], m.CacheSize) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CacheSize))) i-- dAtA[i] = 0x4a } if m.CpuMhz != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.CpuMhz)))) i-- dAtA[i] = 0x41 } if len(m.Microcode) > 0 { i -= len(m.Microcode) copy(dAtA[i:], m.Microcode) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Microcode))) i-- dAtA[i] = 0x3a } if len(m.Stepping) > 0 { i -= len(m.Stepping) copy(dAtA[i:], m.Stepping) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Stepping))) i-- dAtA[i] = 0x32 } if len(m.ModelName) > 0 { i -= len(m.ModelName) copy(dAtA[i:], m.ModelName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ModelName))) i-- dAtA[i] = 0x2a } if len(m.Model) > 0 { i -= len(m.Model) copy(dAtA[i:], m.Model) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Model))) i-- dAtA[i] = 0x22 } if len(m.CpuFamily) > 0 { i -= len(m.CpuFamily) copy(dAtA[i:], m.CpuFamily) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CpuFamily))) i-- dAtA[i] = 0x1a } if len(m.VendorId) > 0 { i -= len(m.VendorId) copy(dAtA[i:], m.VendorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VendorId))) i-- dAtA[i] = 0x12 } if m.Processor != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Processor)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetworkDeviceStatsResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetworkDeviceStatsResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetworkDeviceStatsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NetworkDeviceStats) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetworkDeviceStats) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetworkDeviceStats) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Devices) > 0 { for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Devices[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if m.Total != nil { size, err := m.Total.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetDev) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetDev) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetDev) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TxCompressed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxCompressed)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x88 } if m.TxCarrier != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxCarrier)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.TxCollisions != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxCollisions)) i-- dAtA[i] = 0x78 } if m.TxFifo != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxFifo)) i-- dAtA[i] = 0x70 } if m.TxDropped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxDropped)) i-- dAtA[i] = 0x68 } if m.TxErrors != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxErrors)) i-- dAtA[i] = 0x60 } if m.TxPackets != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxPackets)) i-- dAtA[i] = 0x58 } if m.TxBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxBytes)) i-- dAtA[i] = 0x50 } if m.RxMulticast != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMulticast)) i-- dAtA[i] = 0x48 } if m.RxCompressed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxCompressed)) i-- dAtA[i] = 0x40 } if m.RxFrame != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxFrame)) i-- dAtA[i] = 0x38 } if m.RxFifo != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxFifo)) i-- dAtA[i] = 0x30 } if m.RxDropped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxDropped)) i-- dAtA[i] = 0x28 } if m.RxErrors != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxErrors)) i-- dAtA[i] = 0x20 } if m.RxPackets != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxPackets)) i-- dAtA[i] = 0x18 } if m.RxBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxBytes)) i-- dAtA[i] = 0x10 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiskStatsResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskStatsResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskStatsResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *DiskStats) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskStats) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskStats) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Devices) > 0 { for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Devices[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if m.Total != nil { size, err := m.Total.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiskStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.DiscardTimeMs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DiscardTimeMs)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.DiscardSectors != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DiscardSectors)) i-- dAtA[i] = 0x78 } if m.DiscardMerged != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DiscardMerged)) i-- dAtA[i] = 0x70 } if m.DiscardCompleted != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DiscardCompleted)) i-- dAtA[i] = 0x68 } if m.IoTimeWeightedMs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IoTimeWeightedMs)) i-- dAtA[i] = 0x60 } if m.IoTimeMs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IoTimeMs)) i-- dAtA[i] = 0x58 } if m.IoInProgress != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IoInProgress)) i-- dAtA[i] = 0x50 } if m.WriteTimeMs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WriteTimeMs)) i-- dAtA[i] = 0x48 } if m.WriteSectors != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WriteSectors)) i-- dAtA[i] = 0x40 } if m.WriteMerged != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WriteMerged)) i-- dAtA[i] = 0x38 } if m.WriteCompleted != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WriteCompleted)) i-- dAtA[i] = 0x30 } if m.ReadTimeMs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReadTimeMs)) i-- dAtA[i] = 0x28 } if m.ReadSectors != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReadSectors)) i-- dAtA[i] = 0x20 } if m.ReadMerged != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReadMerged)) i-- dAtA[i] = 0x18 } if m.ReadCompleted != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReadCompleted)) i-- dAtA[i] = 0x10 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdLeaveClusterRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdLeaveClusterRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdLeaveClusterRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } return len(dAtA) - i, nil } func (m *EtcdLeaveCluster) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdLeaveCluster) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdLeaveCluster) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdLeaveClusterResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdLeaveClusterResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdLeaveClusterResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdRemoveMemberRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMemberRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMemberRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Member) > 0 { i -= len(m.Member) copy(dAtA[i:], m.Member) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Member))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdRemoveMember) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMember) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMember) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdRemoveMemberResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMemberResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMemberResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdRemoveMemberByIDRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMemberByIDRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMemberByIDRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MemberId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemberId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EtcdRemoveMemberByID) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMemberByID) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMemberByID) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdRemoveMemberByIDResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRemoveMemberByIDResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRemoveMemberByIDResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdForfeitLeadershipRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdForfeitLeadershipRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdForfeitLeadershipRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } return len(dAtA) - i, nil } func (m *EtcdForfeitLeadership) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdForfeitLeadership) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdForfeitLeadership) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Member) > 0 { i -= len(m.Member) copy(dAtA[i:], m.Member) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Member))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdForfeitLeadershipResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdForfeitLeadershipResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdForfeitLeadershipResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdMemberListRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMemberListRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMemberListRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.QueryLocal { i-- if m.QueryLocal { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EtcdMember) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMember) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMember) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.IsLearner { i-- if m.IsLearner { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if len(m.ClientUrls) > 0 { for iNdEx := len(m.ClientUrls) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ClientUrls[iNdEx]) copy(dAtA[i:], m.ClientUrls[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClientUrls[iNdEx]))) i-- dAtA[i] = 0x2a } } if len(m.PeerUrls) > 0 { for iNdEx := len(m.PeerUrls) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PeerUrls[iNdEx]) copy(dAtA[i:], m.PeerUrls[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PeerUrls[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0x1a } if m.Id != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Id)) i-- dAtA[i] = 0x10 } return len(dAtA) - i, nil } func (m *EtcdMembers) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMembers) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMembers) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Members) > 0 { for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Members[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if len(m.LegacyMembers) > 0 { for iNdEx := len(m.LegacyMembers) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.LegacyMembers[iNdEx]) copy(dAtA[i:], m.LegacyMembers[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LegacyMembers[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdMemberListResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMemberListResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMemberListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdSnapshotRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdSnapshotRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdSnapshotRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } return len(dAtA) - i, nil } func (m *EtcdRecover) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRecover) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRecover) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdRecoverResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRecoverResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRecoverResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdAlarmListResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdAlarmListResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdAlarmListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdAlarm) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdAlarm) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdAlarm) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MemberAlarms) > 0 { for iNdEx := len(m.MemberAlarms) - 1; iNdEx >= 0; iNdEx-- { size, err := m.MemberAlarms[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdMemberAlarm) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMemberAlarm) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMemberAlarm) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Alarm != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Alarm)) i-- dAtA[i] = 0x10 } if m.MemberId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemberId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EtcdAlarmDisarmResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdAlarmDisarmResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdAlarmDisarmResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdAlarmDisarm) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdAlarmDisarm) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdAlarmDisarm) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MemberAlarms) > 0 { for iNdEx := len(m.MemberAlarms) - 1; iNdEx >= 0; iNdEx-- { size, err := m.MemberAlarms[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDefragmentResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDefragmentResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDefragmentResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdDefragment) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDefragment) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDefragment) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdStatusResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdStatusResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdStatusResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MemberStatus != nil { size, err := m.MemberStatus.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdMemberStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdMemberStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdMemberStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.StorageVersion) > 0 { i -= len(m.StorageVersion) copy(dAtA[i:], m.StorageVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.StorageVersion))) i-- dAtA[i] = 0x5a } if m.MemberId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemberId)) i-- dAtA[i] = 0x50 } if m.IsLearner { i-- if m.IsLearner { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x48 } if len(m.Errors) > 0 { for iNdEx := len(m.Errors) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Errors[iNdEx]) copy(dAtA[i:], m.Errors[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Errors[iNdEx]))) i-- dAtA[i] = 0x42 } } if m.RaftAppliedIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RaftAppliedIndex)) i-- dAtA[i] = 0x38 } if m.RaftTerm != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RaftTerm)) i-- dAtA[i] = 0x30 } if m.RaftIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RaftIndex)) i-- dAtA[i] = 0x28 } if m.Leader != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Leader)) i-- dAtA[i] = 0x20 } if m.DbSizeInUse != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DbSizeInUse)) i-- dAtA[i] = 0x18 } if m.DbSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DbSize)) i-- dAtA[i] = 0x10 } if len(m.ProtocolVersion) > 0 { i -= len(m.ProtocolVersion) copy(dAtA[i:], m.ProtocolVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProtocolVersion))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDowngradeValidateRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeValidateRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeValidateRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDowngradeValidateResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeValidateResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeValidateResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdDowngradeValidate) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeValidate) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeValidate) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClusterDowngrade != nil { size, err := m.ClusterDowngrade.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDowngradeEnableRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeEnableRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeEnableRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDowngradeEnableResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeEnableResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeEnableResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdDowngradeEnable) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeEnable) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeEnable) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClusterDowngrade != nil { size, err := m.ClusterDowngrade.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdDowngradeCancelResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeCancelResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeCancelResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EtcdDowngradeCancel) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdDowngradeCancel) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdDowngradeCancel) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClusterDowngrade != nil { size, err := m.ClusterDowngrade.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdClusterDowngrade) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdClusterDowngrade) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdClusterDowngrade) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ClusterVersion) > 0 { i -= len(m.ClusterVersion) copy(dAtA[i:], m.ClusterVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterVersion))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RouteConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RouteConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RouteConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metric != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Metric)) i-- dAtA[i] = 0x18 } if len(m.Gateway) > 0 { i -= len(m.Gateway) copy(dAtA[i:], m.Gateway) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Gateway))) i-- dAtA[i] = 0x12 } if len(m.Network) > 0 { i -= len(m.Network) copy(dAtA[i:], m.Network) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Network))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DHCPOptionsConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DHCPOptionsConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DHCPOptionsConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.RouteMetric != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RouteMetric)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetworkDeviceConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetworkDeviceConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetworkDeviceConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Routes) > 0 { for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Routes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } } if m.DhcpOptions != nil { size, err := m.DhcpOptions.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.Ignore { i-- if m.Ignore { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Dhcp { i-- if m.Dhcp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x18 } if len(m.Cidr) > 0 { i -= len(m.Cidr) copy(dAtA[i:], m.Cidr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Cidr))) i-- dAtA[i] = 0x12 } if len(m.Interface) > 0 { i -= len(m.Interface) copy(dAtA[i:], m.Interface) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Interface))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetworkConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetworkConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetworkConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Interfaces) > 0 { for iNdEx := len(m.Interfaces) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Interfaces[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *InstallConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *InstallConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *InstallConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.InstallImage) > 0 { i -= len(m.InstallImage) copy(dAtA[i:], m.InstallImage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InstallImage))) i-- dAtA[i] = 0x12 } if len(m.InstallDisk) > 0 { i -= len(m.InstallDisk) copy(dAtA[i:], m.InstallDisk) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InstallDisk))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MachineConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.KubernetesVersion) > 0 { i -= len(m.KubernetesVersion) copy(dAtA[i:], m.KubernetesVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.KubernetesVersion))) i-- dAtA[i] = 0x22 } if m.NetworkConfig != nil { size, err := m.NetworkConfig.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.InstallConfig != nil { size, err := m.InstallConfig.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ControlPlaneConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControlPlaneConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControlPlaneConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Endpoint) > 0 { i -= len(m.Endpoint) copy(dAtA[i:], m.Endpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Endpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CNIConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CNIConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CNIConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Urls) > 0 { for iNdEx := len(m.Urls) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Urls[iNdEx]) copy(dAtA[i:], m.Urls[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Urls[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ClusterNetworkConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ClusterNetworkConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ClusterNetworkConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.CniConfig != nil { size, err := m.CniConfig.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.DnsDomain) > 0 { i -= len(m.DnsDomain) copy(dAtA[i:], m.DnsDomain) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DnsDomain))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ClusterConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ClusterConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ClusterConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.AllowSchedulingOnControlPlanes { i-- if m.AllowSchedulingOnControlPlanes { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ClusterNetwork != nil { size, err := m.ClusterNetwork.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.ControlPlane != nil { size, err := m.ControlPlane.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GenerateClientConfigurationRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GenerateClientConfigurationRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *GenerateClientConfigurationRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.CrtTtl != nil { size, err := (*durationpb.Duration)(m.CrtTtl).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Roles) > 0 { for iNdEx := len(m.Roles) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Roles[iNdEx]) copy(dAtA[i:], m.Roles[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Roles[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *GenerateClientConfiguration) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GenerateClientConfiguration) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *GenerateClientConfiguration) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Talosconfig) > 0 { i -= len(m.Talosconfig) copy(dAtA[i:], m.Talosconfig) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Talosconfig))) i-- dAtA[i] = 0x2a } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0x22 } if len(m.Crt) > 0 { i -= len(m.Crt) copy(dAtA[i:], m.Crt) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Crt))) i-- dAtA[i] = 0x1a } if len(m.Ca) > 0 { i -= len(m.Ca) copy(dAtA[i:], m.Ca) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ca))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *GenerateClientConfigurationResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *GenerateClientConfigurationResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *GenerateClientConfigurationResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *PacketCaptureRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PacketCaptureRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PacketCaptureRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.BpfFilter) > 0 { for iNdEx := len(m.BpfFilter) - 1; iNdEx >= 0; iNdEx-- { size, err := m.BpfFilter[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } } if m.SnapLen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SnapLen)) i-- dAtA[i] = 0x18 } if m.Promiscuous { i-- if m.Promiscuous { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Interface) > 0 { i -= len(m.Interface) copy(dAtA[i:], m.Interface) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Interface))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BPFInstruction) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BPFInstruction) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BPFInstruction) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.K != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.K)) i-- dAtA[i] = 0x20 } if m.Jf != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Jf)) i-- dAtA[i] = 0x18 } if m.Jt != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Jt)) i-- dAtA[i] = 0x10 } if m.Op != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Op)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetstatRequest_Feature) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetstatRequest_Feature) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetstatRequest_Feature) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Pid { i-- if m.Pid { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetstatRequest_L4Proto) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetstatRequest_L4Proto) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetstatRequest_L4Proto) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Raw6 { i-- if m.Raw6 { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.Raw { i-- if m.Raw { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.Udplite6 { i-- if m.Udplite6 { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.Udplite { i-- if m.Udplite { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Udp6 { i-- if m.Udp6 { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.Udp { i-- if m.Udp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Tcp6 { i-- if m.Tcp6 { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Tcp { i-- if m.Tcp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetstatRequest_NetNS) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetstatRequest_NetNS) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetstatRequest_NetNS) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Allnetns { i-- if m.Allnetns { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.Netns) > 0 { for iNdEx := len(m.Netns) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Netns[iNdEx]) copy(dAtA[i:], m.Netns[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Netns[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Hostnetwork { i-- if m.Hostnetwork { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NetstatRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetstatRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetstatRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Netns != nil { size, err := m.Netns.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.L4Proto != nil { size, err := m.L4Proto.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Feature != nil { size, err := m.Feature.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Filter != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Filter)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ConnectRecord_Process) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConnectRecord_Process) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConnectRecord_Process) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if m.Pid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Pid)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ConnectRecord) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConnectRecord) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConnectRecord) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Netns) > 0 { i -= len(m.Netns) copy(dAtA[i:], m.Netns) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Netns))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if m.Process != nil { size, err := m.Process.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if m.Pointer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Pointer)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.Ref != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Ref)) i-- dAtA[i] = 0x78 } if m.Inode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Inode)) i-- dAtA[i] = 0x70 } if m.Timeout != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Timeout)) i-- dAtA[i] = 0x68 } if m.Uid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Uid)) i-- dAtA[i] = 0x60 } if m.Retrnsmt != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Retrnsmt)) i-- dAtA[i] = 0x58 } if m.Timerwhen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Timerwhen)) i-- dAtA[i] = 0x50 } if m.Tr != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tr)) i-- dAtA[i] = 0x48 } if m.Rxqueue != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rxqueue)) i-- dAtA[i] = 0x40 } if m.Txqueue != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Txqueue)) i-- dAtA[i] = 0x38 } if m.State != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.State)) i-- dAtA[i] = 0x30 } if m.Remoteport != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Remoteport)) i-- dAtA[i] = 0x28 } if len(m.Remoteip) > 0 { i -= len(m.Remoteip) copy(dAtA[i:], m.Remoteip) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Remoteip))) i-- dAtA[i] = 0x22 } if m.Localport != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Localport)) i-- dAtA[i] = 0x18 } if len(m.Localip) > 0 { i -= len(m.Localip) copy(dAtA[i:], m.Localip) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Localip))) i-- dAtA[i] = 0x12 } if len(m.L4Proto) > 0 { i -= len(m.L4Proto) copy(dAtA[i:], m.L4Proto) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.L4Proto))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Netstat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Netstat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Netstat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Connectrecord) > 0 { for iNdEx := len(m.Connectrecord) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Connectrecord[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NetstatResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NetstatResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NetstatResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *MetaWriteRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaWriteRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaWriteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0x12 } if m.Key != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Key)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MetaWrite) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaWrite) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaWrite) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MetaWriteResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaWriteResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaWriteResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *MetaDeleteRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaDeleteRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaDeleteRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Key != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Key)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MetaDelete) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaDelete) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaDelete) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MetaDeleteResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaDeleteResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaDeleteResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ImageListRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageListRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageListRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Namespace != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Namespace)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ImageListResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageListResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.CreatedAt != nil { size, err := (*timestamppb.Timestamp)(m.CreatedAt).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x20 } if len(m.Digest) > 0 { i -= len(m.Digest) copy(dAtA[i:], m.Digest) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Digest))) i-- dAtA[i] = 0x1a } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImagePullRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImagePullRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImagePullRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Reference) > 0 { i -= len(m.Reference) copy(dAtA[i:], m.Reference) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Reference))) i-- dAtA[i] = 0x12 } if m.Namespace != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Namespace)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ImagePull) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImagePull) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImagePull) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImagePullResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImagePullResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImagePullResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ApplyConfigurationRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Data) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } if m.DryRun { n += 2 } if m.TryModeTimeout != nil { l = (*durationpb.Duration)(m.TryModeTimeout).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ApplyConfiguration) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Warnings) > 0 { for _, s := range m.Warnings { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } l = len(m.ModeDetails) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ApplyConfigurationResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *RebootRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } n += len(m.unknownFields) return n } func (m *Reboot) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RebootResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BootstrapRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RecoverEtcd { n += 2 } if m.RecoverSkipHashCheck { n += 2 } n += len(m.unknownFields) return n } func (m *Bootstrap) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *BootstrapResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *SequenceEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Sequence) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } if m.Error != nil { if size, ok := interface{}(m.Error).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Error) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PhaseEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Phase) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } n += len(m.unknownFields) return n } func (m *TaskEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Task) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } n += len(m.unknownFields) return n } func (m *ServiceStateEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Service) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } l = len(m.Message) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Health != nil { l = m.Health.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RestartEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Cmd != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Cmd)) } n += len(m.unknownFields) return n } func (m *ConfigLoadErrorEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Error) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConfigValidationErrorEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Error) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *AddressEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Addresses) > 0 { for _, s := range m.Addresses { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MachineStatusEvent_MachineStatus_UnmetCondition) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Reason) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MachineStatusEvent_MachineStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } if len(m.UnmetConditions) > 0 { for _, e := range m.UnmetConditions { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MachineStatusEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Stage != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Stage)) } if m.Status != nil { l = m.Status.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EventsRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.TailEvents != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TailEvents)) } l = len(m.TailId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.TailSeconds != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TailSeconds)) } l = len(m.WithActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Event) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Data != nil { l = (*anypb.Any)(m.Data).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ResetPartitionSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Wipe { n += 2 } n += len(m.unknownFields) return n } func (m *ResetRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Graceful { n += 2 } if m.Reboot { n += 2 } if len(m.SystemPartitionsToWipe) > 0 { for _, e := range m.SystemPartitionsToWipe { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.UserDisksToWipe) > 0 { for _, s := range m.UserDisksToWipe { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } n += len(m.unknownFields) return n } func (m *Reset) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ResetResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Shutdown) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ShutdownRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Force { n += 2 } n += len(m.unknownFields) return n } func (m *ShutdownResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *UpgradeRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Preserve { n += 2 } if m.Stage { n += 2 } if m.Force { n += 2 } if m.RebootMode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RebootMode)) } n += len(m.unknownFields) return n } func (m *Upgrade) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Ack) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ActorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *UpgradeResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceList) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Services) > 0 { for _, e := range m.Services { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceListResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.State) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Events != nil { l = m.Events.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Health != nil { l = m.Health.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceEvents) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Events) > 0 { for _, e := range m.Events { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceEvent) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Msg) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.State) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Ts != nil { l = (*timestamppb.Timestamp)(m.Ts).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceHealth) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Unknown { n += 2 } if m.Healthy { n += 2 } l = len(m.LastMessage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LastChange != nil { l = (*timestamppb.Timestamp)(m.LastChange).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceStartRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceStart) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Resp) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceStartResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceStopRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceStop) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Resp) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceStopResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ServiceRestartRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceRestart) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Resp) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ServiceRestartResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *CopyRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.RootPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ListRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Root) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Recurse { n += 2 } if m.RecursionDepth != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RecursionDepth)) } if len(m.Types) > 0 { l = 0 for _, e := range m.Types { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } if m.ReportXattrs { n += 2 } n += len(m.unknownFields) return n } func (m *DiskUsageRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RecursionDepth != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RecursionDepth)) } if m.All { n += 2 } if m.Threshold != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Threshold)) } if len(m.Paths) > 0 { for _, s := range m.Paths { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *FileInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } if m.Modified != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Modified)) } if m.IsDir { n += 2 } l = len(m.Error) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Link) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.RelativeName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Uid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Uid)) } if m.Gid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Gid)) } if len(m.Xattrs) > 0 { for _, e := range m.Xattrs { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Xattr) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Data) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DiskUsageInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } l = len(m.Error) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.RelativeName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Mounts) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Stats) > 0 { for _, e := range m.Stats { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MountsResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MountStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Filesystem) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.Available != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Available)) } l = len(m.MountedOn) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Version) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Version != nil { l = m.Version.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Platform != nil { l = m.Platform.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Features != nil { l = m.Features.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *VersionResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *VersionInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Tag) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Sha) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Built) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.GoVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Os) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Arch) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PlatformInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Mode) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *FeaturesInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Rbac { n += 2 } n += len(m.unknownFields) return n } func (m *LogsRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Driver != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Driver)) } if m.Follow { n += 2 } if m.TailLines != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TailLines)) } n += len(m.unknownFields) return n } func (m *ReadRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Path) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LogsContainer) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Ids) > 0 { for _, s := range m.Ids { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *LogsContainersResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *RollbackRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += len(m.unknownFields) return n } func (m *Rollback) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RollbackResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ContainersRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Driver != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Driver)) } n += len(m.unknownFields) return n } func (m *ContainerInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Pid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Pid)) } l = len(m.Status) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PodId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.NetworkNamespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.InternalId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Uid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Container) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Containers) > 0 { for _, e := range m.Containers { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ContainersResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *DmesgRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Follow { n += 2 } if m.Tail { n += 2 } n += len(m.unknownFields) return n } func (m *ProcessesResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Process) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Processes) > 0 { for _, e := range m.Processes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ProcessInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Pid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Pid)) } if m.Ppid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Ppid)) } l = len(m.State) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Threads != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Threads)) } if m.CpuTime != 0 { n += 9 } if m.VirtualMemory != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.VirtualMemory)) } if m.ResidentMemory != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ResidentMemory)) } l = len(m.Command) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Executable) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Args) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RestartRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Driver != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Driver)) } n += len(m.unknownFields) return n } func (m *Restart) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RestartResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *StatsRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Driver != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Driver)) } n += len(m.unknownFields) return n } func (m *Stats) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Stats) > 0 { for _, e := range m.Stats { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *StatsResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Stat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Namespace) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Id) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MemoryUsage != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemoryUsage)) } if m.CpuUsage != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CpuUsage)) } l = len(m.PodId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Memory) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Meminfo != nil { l = m.Meminfo.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MemoryResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MemInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Memtotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Memtotal)) } if m.Memfree != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Memfree)) } if m.Memavailable != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Memavailable)) } if m.Buffers != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Buffers)) } if m.Cached != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Cached)) } if m.Swapcached != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Swapcached)) } if m.Active != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Active)) } if m.Inactive != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Inactive)) } if m.Activeanon != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Activeanon)) } if m.Inactiveanon != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Inactiveanon)) } if m.Activefile != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Activefile)) } if m.Inactivefile != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Inactivefile)) } if m.Unevictable != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Unevictable)) } if m.Mlocked != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mlocked)) } if m.Swaptotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Swaptotal)) } if m.Swapfree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Swapfree)) } if m.Dirty != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Dirty)) } if m.Writeback != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Writeback)) } if m.Anonpages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Anonpages)) } if m.Mapped != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Mapped)) } if m.Shmem != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Shmem)) } if m.Slab != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Slab)) } if m.Sreclaimable != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Sreclaimable)) } if m.Sunreclaim != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Sunreclaim)) } if m.Kernelstack != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Kernelstack)) } if m.Pagetables != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Pagetables)) } if m.Nfsunstable != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Nfsunstable)) } if m.Bounce != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Bounce)) } if m.Writebacktmp != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Writebacktmp)) } if m.Commitlimit != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Commitlimit)) } if m.Committedas != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Committedas)) } if m.Vmalloctotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Vmalloctotal)) } if m.Vmallocused != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Vmallocused)) } if m.Vmallocchunk != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Vmallocchunk)) } if m.Hardwarecorrupted != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hardwarecorrupted)) } if m.Anonhugepages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Anonhugepages)) } if m.Shmemhugepages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Shmemhugepages)) } if m.Shmempmdmapped != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Shmempmdmapped)) } if m.Cmatotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Cmatotal)) } if m.Cmafree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Cmafree)) } if m.Hugepagestotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagestotal)) } if m.Hugepagesfree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagesfree)) } if m.Hugepagesrsvd != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagesrsvd)) } if m.Hugepagessurp != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagessurp)) } if m.Hugepagesize != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagesize)) } if m.Directmap4K != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Directmap4K)) } if m.Directmap2M != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Directmap2M)) } if m.Directmap1G != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Directmap1G)) } n += len(m.unknownFields) return n } func (m *HostnameResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *Hostname) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LoadAvgResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *LoadAvg) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Load1 != 0 { n += 9 } if m.Load5 != 0 { n += 9 } if m.Load15 != 0 { n += 9 } n += len(m.unknownFields) return n } func (m *SystemStatResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *SystemStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BootTime != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.BootTime)) } if m.CpuTotal != nil { l = m.CpuTotal.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Cpu) > 0 { for _, e := range m.Cpu { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.IrqTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IrqTotal)) } if len(m.Irq) > 0 { l = 0 for _, e := range m.Irq { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } if m.ContextSwitches != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ContextSwitches)) } if m.ProcessCreated != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessCreated)) } if m.ProcessRunning != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessRunning)) } if m.ProcessBlocked != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessBlocked)) } if m.SoftIrqTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SoftIrqTotal)) } if m.SoftIrq != nil { l = m.SoftIrq.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *CPUStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.User != 0 { n += 9 } if m.Nice != 0 { n += 9 } if m.System != 0 { n += 9 } if m.Idle != 0 { n += 9 } if m.Iowait != 0 { n += 9 } if m.Irq != 0 { n += 9 } if m.SoftIrq != 0 { n += 9 } if m.Steal != 0 { n += 9 } if m.Guest != 0 { n += 9 } if m.GuestNice != 0 { n += 9 } n += len(m.unknownFields) return n } func (m *SoftIRQStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Hi != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Hi)) } if m.Timer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Timer)) } if m.NetTx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.NetTx)) } if m.NetRx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.NetRx)) } if m.Block != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Block)) } if m.BlockIoPoll != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.BlockIoPoll)) } if m.Tasklet != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tasklet)) } if m.Sched != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Sched)) } if m.Hrtimer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Hrtimer)) } if m.Rcu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rcu)) } n += len(m.unknownFields) return n } func (m *CPUFreqStatsResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *CPUsFreqStats) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.CpuFreqStats) > 0 { for _, e := range m.CpuFreqStats { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *CPUFreqStats) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.CurrentFrequency != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CurrentFrequency)) } if m.MinimumFrequency != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MinimumFrequency)) } if m.MaximumFrequency != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MaximumFrequency)) } l = len(m.Governor) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *CPUInfoResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *CPUsInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.CpuInfo) > 0 { for _, e := range m.CpuInfo { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *CPUInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Processor != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Processor)) } l = len(m.VendorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.CpuFamily) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Model) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ModelName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Stepping) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Microcode) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CpuMhz != 0 { n += 9 } l = len(m.CacheSize) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PhysicalId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Siblings != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Siblings)) } l = len(m.CoreId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CpuCores != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CpuCores)) } l = len(m.ApicId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.InitialApicId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Fpu) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.FpuException) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CpuIdLevel != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CpuIdLevel)) } l = len(m.Wp) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Flags) > 0 { for _, s := range m.Flags { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Bugs) > 0 { for _, s := range m.Bugs { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.BogoMips != 0 { n += 10 } if m.ClFlushSize != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.ClFlushSize)) } if m.CacheAlignment != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CacheAlignment)) } l = len(m.AddressSizes) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PowerManagement) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NetworkDeviceStatsResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NetworkDeviceStats) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Total != nil { l = m.Total.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Devices) > 0 { for _, e := range m.Devices { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NetDev) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.RxBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxBytes)) } if m.RxPackets != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxPackets)) } if m.RxErrors != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxErrors)) } if m.RxDropped != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxDropped)) } if m.RxFifo != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxFifo)) } if m.RxFrame != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxFrame)) } if m.RxCompressed != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxCompressed)) } if m.RxMulticast != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMulticast)) } if m.TxBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxBytes)) } if m.TxPackets != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxPackets)) } if m.TxErrors != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxErrors)) } if m.TxDropped != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxDropped)) } if m.TxFifo != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxFifo)) } if m.TxCollisions != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxCollisions)) } if m.TxCarrier != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.TxCarrier)) } if m.TxCompressed != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.TxCompressed)) } n += len(m.unknownFields) return n } func (m *DiskStatsResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *DiskStats) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Total != nil { l = m.Total.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Devices) > 0 { for _, e := range m.Devices { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *DiskStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ReadCompleted != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReadCompleted)) } if m.ReadMerged != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReadMerged)) } if m.ReadSectors != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReadSectors)) } if m.ReadTimeMs != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReadTimeMs)) } if m.WriteCompleted != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.WriteCompleted)) } if m.WriteMerged != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.WriteMerged)) } if m.WriteSectors != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.WriteSectors)) } if m.WriteTimeMs != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.WriteTimeMs)) } if m.IoInProgress != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IoInProgress)) } if m.IoTimeMs != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IoTimeMs)) } if m.IoTimeWeightedMs != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IoTimeWeightedMs)) } if m.DiscardCompleted != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DiscardCompleted)) } if m.DiscardMerged != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DiscardMerged)) } if m.DiscardSectors != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DiscardSectors)) } if m.DiscardTimeMs != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.DiscardTimeMs)) } n += len(m.unknownFields) return n } func (m *EtcdLeaveClusterRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += len(m.unknownFields) return n } func (m *EtcdLeaveCluster) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdLeaveClusterResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdRemoveMemberRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Member) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdRemoveMember) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdRemoveMemberResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdRemoveMemberByIDRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MemberId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemberId)) } n += len(m.unknownFields) return n } func (m *EtcdRemoveMemberByID) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdRemoveMemberByIDResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdForfeitLeadershipRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += len(m.unknownFields) return n } func (m *EtcdForfeitLeadership) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Member) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdForfeitLeadershipResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdMemberListRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.QueryLocal { n += 2 } n += len(m.unknownFields) return n } func (m *EtcdMember) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Id != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Id)) } l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.PeerUrls) > 0 { for _, s := range m.PeerUrls { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ClientUrls) > 0 { for _, s := range m.ClientUrls { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.IsLearner { n += 2 } n += len(m.unknownFields) return n } func (m *EtcdMembers) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.LegacyMembers) > 0 { for _, s := range m.LegacyMembers { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Members) > 0 { for _, e := range m.Members { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdMemberListResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdSnapshotRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l n += len(m.unknownFields) return n } func (m *EtcdRecover) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdRecoverResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdAlarmListResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdAlarm) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.MemberAlarms) > 0 { for _, e := range m.MemberAlarms { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdMemberAlarm) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MemberId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemberId)) } if m.Alarm != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Alarm)) } n += len(m.unknownFields) return n } func (m *EtcdAlarmDisarmResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdAlarmDisarm) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.MemberAlarms) > 0 { for _, e := range m.MemberAlarms { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdDefragmentResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdDefragment) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdStatusResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MemberStatus != nil { l = m.MemberStatus.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdMemberStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ProtocolVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.DbSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DbSize)) } if m.DbSizeInUse != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DbSizeInUse)) } if m.Leader != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Leader)) } if m.RaftIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RaftIndex)) } if m.RaftTerm != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RaftTerm)) } if m.RaftAppliedIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RaftAppliedIndex)) } if len(m.Errors) > 0 { for _, s := range m.Errors { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.IsLearner { n += 2 } if m.MemberId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemberId)) } l = len(m.StorageVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdDowngradeValidateRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdDowngradeValidateResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdDowngradeValidate) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClusterDowngrade != nil { l = m.ClusterDowngrade.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdDowngradeEnableRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdDowngradeEnableResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdDowngradeEnable) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClusterDowngrade != nil { l = m.ClusterDowngrade.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdDowngradeCancelResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EtcdDowngradeCancel) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClusterDowngrade != nil { l = m.ClusterDowngrade.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdClusterDowngrade) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ClusterVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RouteConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Network) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Gateway) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Metric != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Metric)) } n += len(m.unknownFields) return n } func (m *DHCPOptionsConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RouteMetric != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RouteMetric)) } n += len(m.unknownFields) return n } func (m *NetworkDeviceConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Interface) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Cidr) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } if m.Dhcp { n += 2 } if m.Ignore { n += 2 } if m.DhcpOptions != nil { l = m.DhcpOptions.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Routes) > 0 { for _, e := range m.Routes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NetworkConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Interfaces) > 0 { for _, e := range m.Interfaces { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *InstallConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.InstallDisk) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.InstallImage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MachineConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } if m.InstallConfig != nil { l = m.InstallConfig.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.NetworkConfig != nil { l = m.NetworkConfig.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.KubernetesVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ControlPlaneConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Endpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *CNIConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Urls) > 0 { for _, s := range m.Urls { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ClusterNetworkConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DnsDomain) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CniConfig != nil { l = m.CniConfig.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ClusterConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ControlPlane != nil { l = m.ControlPlane.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClusterNetwork != nil { l = m.ClusterNetwork.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.AllowSchedulingOnControlPlanes { n += 2 } n += len(m.unknownFields) return n } func (m *GenerateClientConfigurationRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Roles) > 0 { for _, s := range m.Roles { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.CrtTtl != nil { l = (*durationpb.Duration)(m.CrtTtl).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *GenerateClientConfiguration) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Ca) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Crt) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Talosconfig) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *GenerateClientConfigurationResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *PacketCaptureRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Interface) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Promiscuous { n += 2 } if m.SnapLen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SnapLen)) } if len(m.BpfFilter) > 0 { for _, e := range m.BpfFilter { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BPFInstruction) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Op != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Op)) } if m.Jt != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Jt)) } if m.Jf != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Jf)) } if m.K != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.K)) } n += len(m.unknownFields) return n } func (m *NetstatRequest_Feature) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Pid { n += 2 } n += len(m.unknownFields) return n } func (m *NetstatRequest_L4Proto) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Tcp { n += 2 } if m.Tcp6 { n += 2 } if m.Udp { n += 2 } if m.Udp6 { n += 2 } if m.Udplite { n += 2 } if m.Udplite6 { n += 2 } if m.Raw { n += 2 } if m.Raw6 { n += 2 } n += len(m.unknownFields) return n } func (m *NetstatRequest_NetNS) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Hostnetwork { n += 2 } if len(m.Netns) > 0 { for _, s := range m.Netns { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Allnetns { n += 2 } n += len(m.unknownFields) return n } func (m *NetstatRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Filter != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Filter)) } if m.Feature != nil { l = m.Feature.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.L4Proto != nil { l = m.L4Proto.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Netns != nil { l = m.Netns.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConnectRecord_Process) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Pid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Pid)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConnectRecord) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.L4Proto) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Localip) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Localport != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Localport)) } l = len(m.Remoteip) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Remoteport != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Remoteport)) } if m.State != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.State)) } if m.Txqueue != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Txqueue)) } if m.Rxqueue != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rxqueue)) } if m.Tr != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tr)) } if m.Timerwhen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Timerwhen)) } if m.Retrnsmt != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Retrnsmt)) } if m.Uid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Uid)) } if m.Timeout != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Timeout)) } if m.Inode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Inode)) } if m.Ref != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Ref)) } if m.Pointer != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Pointer)) } if m.Process != nil { l = m.Process.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Netns) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Netstat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Connectrecord) > 0 { for _, e := range m.Connectrecord { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NetstatResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MetaWriteRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Key != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Key)) } l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MetaWrite) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MetaWriteResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MetaDeleteRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Key != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Key)) } n += len(m.unknownFields) return n } func (m *MetaDelete) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MetaDeleteResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ImageListRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Namespace != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Namespace)) } n += len(m.unknownFields) return n } func (m *ImageListResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Digest) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.CreatedAt != nil { l = (*timestamppb.Timestamp)(m.CreatedAt).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImagePullRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Namespace != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Namespace)) } l = len(m.Reference) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImagePull) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImagePullResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ApplyConfigurationRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ApplyConfigurationRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ApplyConfigurationRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) if m.Data == nil { m.Data = []byte{} } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= ApplyConfigurationRequest_Mode(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DryRun", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DryRun = bool(v != 0) case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TryModeTimeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.TryModeTimeout == nil { m.TryModeTimeout = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.TryModeTimeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ApplyConfiguration) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ApplyConfiguration: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ApplyConfiguration: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Warnings", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Warnings = append(m.Warnings, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= ApplyConfigurationRequest_Mode(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ModeDetails", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ModeDetails = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ApplyConfigurationResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ApplyConfigurationResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ApplyConfigurationResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ApplyConfiguration{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RebootRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RebootRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RebootRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= RebootRequest_Mode(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Reboot) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Reboot: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Reboot: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RebootResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RebootResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RebootResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Reboot{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BootstrapRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BootstrapRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BootstrapRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RecoverEtcd", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RecoverEtcd = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RecoverSkipHashCheck", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RecoverSkipHashCheck = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Bootstrap) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Bootstrap: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Bootstrap: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BootstrapResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BootstrapResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BootstrapResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Bootstrap{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SequenceEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SequenceEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SequenceEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Sequence", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Sequence = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= SequenceEvent_Action(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Error == nil { m.Error = &common.Error{} } if unmarshal, ok := interface{}(m.Error).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Error); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PhaseEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PhaseEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PhaseEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Phase = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= PhaseEvent_Action(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TaskEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TaskEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TaskEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Task", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Task = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= TaskEvent_Action(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStateEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStateEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStateEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Service", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Service = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= ServiceStateEvent_Action(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Health", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Health == nil { m.Health = &ServiceHealth{} } if err := m.Health.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RestartEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RestartEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RestartEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cmd", wireType) } m.Cmd = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Cmd |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConfigLoadErrorEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigLoadErrorEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigLoadErrorEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConfigValidationErrorEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigValidationErrorEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigValidationErrorEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddressEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddressEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddressEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineStatusEvent_MachineStatus_UnmetCondition) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineStatusEvent_MachineStatus_UnmetCondition: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineStatusEvent_MachineStatus_UnmetCondition: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Reason = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineStatusEvent_MachineStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineStatusEvent_MachineStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineStatusEvent_MachineStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UnmetConditions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UnmetConditions = append(m.UnmetConditions, &MachineStatusEvent_MachineStatus_UnmetCondition{}) if err := m.UnmetConditions[len(m.UnmetConditions)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineStatusEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineStatusEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineStatusEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Stage", wireType) } m.Stage = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Stage |= MachineStatusEvent_MachineStage(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Status == nil { m.Status = &MachineStatusEvent_MachineStatus{} } if err := m.Status.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EventsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EventsRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EventsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TailEvents", wireType) } m.TailEvents = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TailEvents |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TailId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TailId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TailSeconds", wireType) } m.TailSeconds = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TailSeconds |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WithActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.WithActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Event) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Event: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Data == nil { m.Data = &anypb1.Any{} } if err := (*anypb.Any)(m.Data).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetPartitionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetPartitionSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetPartitionSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Wipe", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Wipe = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Graceful", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Graceful = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Reboot", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Reboot = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SystemPartitionsToWipe", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SystemPartitionsToWipe = append(m.SystemPartitionsToWipe, &ResetPartitionSpec{}) if err := m.SystemPartitionsToWipe[len(m.SystemPartitionsToWipe)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UserDisksToWipe", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UserDisksToWipe = append(m.UserDisksToWipe, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= ResetRequest_WipeMode(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Reset) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Reset: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Reset: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResetResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResetResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResetResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Reset{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Shutdown) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Shutdown: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Shutdown: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShutdownRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShutdownRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShutdownRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Force = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ShutdownResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ShutdownResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ShutdownResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Shutdown{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UpgradeRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UpgradeRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UpgradeRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Preserve", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Preserve = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Stage", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Stage = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Force = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RebootMode", wireType) } m.RebootMode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RebootMode |= UpgradeRequest_RebootMode(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Upgrade) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Upgrade: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Upgrade: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ack", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ack = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ActorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ActorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UpgradeResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UpgradeResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UpgradeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Upgrade{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceList) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceList: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceList: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Services", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Services = append(m.Services, &ServiceInfo{}) if err := m.Services[len(m.Services)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceListResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ServiceList{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.State = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Events == nil { m.Events = &ServiceEvents{} } if err := m.Events.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Health", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Health == nil { m.Health = &ServiceHealth{} } if err := m.Health.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceEvents) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceEvents: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceEvents: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Events = append(m.Events, &ServiceEvent{}) if err := m.Events[len(m.Events)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceEvent) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceEvent: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceEvent: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Msg", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Msg = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.State = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ts", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Ts == nil { m.Ts = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.Ts).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceHealth) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceHealth: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceHealth: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unknown", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Unknown = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Healthy", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Healthy = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastMessage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LastMessage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastChange", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastChange == nil { m.LastChange = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.LastChange).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStartRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStartRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStartRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStart) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStart: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStart: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resp", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Resp = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStartResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStartResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStartResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ServiceStart{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStopRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStopRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStopRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStop) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStop: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStop: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resp", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Resp = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceStopResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceStopResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceStopResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ServiceStop{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceRestartRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceRestartRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceRestartRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceRestart) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceRestart: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceRestart: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resp", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Resp = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ServiceRestartResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceRestartResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceRestartResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ServiceRestart{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CopyRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CopyRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CopyRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RootPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RootPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ListRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Root", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Root = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Recurse", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Recurse = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RecursionDepth", wireType) } m.RecursionDepth = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RecursionDepth |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType == 0 { var v ListRequest_Type for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= ListRequest_Type(b&0x7F) << shift if b < 0x80 { break } } m.Types = append(m.Types, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int if elementCount != 0 && len(m.Types) == 0 { m.Types = make([]ListRequest_Type, 0, elementCount) } for iNdEx < postIndex { var v ListRequest_Type for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= ListRequest_Type(b&0x7F) << shift if b < 0x80 { break } } m.Types = append(m.Types, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReportXattrs", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReportXattrs = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskUsageRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskUsageRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskUsageRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RecursionDepth", wireType) } m.RecursionDepth = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RecursionDepth |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field All", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.All = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Threshold", wireType) } m.Threshold = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Threshold |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *FileInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: FileInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: FileInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Modified", wireType) } m.Modified = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Modified |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IsDir", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IsDir = bool(v != 0) case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Link", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Link = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RelativeName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RelativeName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) } m.Uid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Uid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) } m.Gid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Gid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Xattrs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Xattrs = append(m.Xattrs, &Xattr{}) if err := m.Xattrs[len(m.Xattrs)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Xattr) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Xattr: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Xattr: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) if m.Data == nil { m.Data = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskUsageInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskUsageInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskUsageInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Error", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RelativeName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RelativeName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Mounts) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Mounts: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Mounts: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stats", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Stats = append(m.Stats, &MountStat{}) if err := m.Stats[len(m.Stats)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Mounts{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Filesystem", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Filesystem = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Available", wireType) } m.Available = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Available |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MountedOn", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MountedOn = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Version) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Version: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Version: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Version == nil { m.Version = &VersionInfo{} } if err := m.Version.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Platform == nil { m.Platform = &PlatformInfo{} } if err := m.Platform.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Features", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Features == nil { m.Features = &FeaturesInfo{} } if err := m.Features.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VersionResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VersionResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VersionResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Version{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VersionInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VersionInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VersionInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tag", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Tag = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Sha", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Sha = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Built", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Built = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field GoVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.GoVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Os", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Os = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Arch", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Arch = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PlatformInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PlatformInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PlatformInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Mode = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *FeaturesInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: FeaturesInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: FeaturesInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rbac", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Rbac = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LogsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LogsRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LogsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } m.Driver = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Driver |= common.ContainerDriver(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Follow", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Follow = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TailLines", wireType) } m.TailLines = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TailLines |= int32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ReadRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ReadRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ReadRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Path", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Path = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LogsContainer) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LogsContainer: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LogsContainer: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ids", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ids = append(m.Ids, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LogsContainersResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LogsContainersResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LogsContainersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &LogsContainer{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RollbackRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RollbackRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RollbackRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Rollback) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Rollback: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Rollback: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RollbackResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RollbackResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RollbackResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Rollback{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ContainersRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ContainersRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ContainersRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } m.Driver = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Driver |= common.ContainerDriver(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ContainerInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ContainerInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ContainerInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) } m.Pid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PodId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NetworkNamespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NetworkNamespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InternalId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InternalId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Container) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Container: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Container: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Containers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Containers = append(m.Containers, &ContainerInfo{}) if err := m.Containers[len(m.Containers)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ContainersResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ContainersResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ContainersResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Container{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DmesgRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DmesgRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DmesgRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Follow", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Follow = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tail", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Tail = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProcessesResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProcessesResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProcessesResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Process{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Process) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Process: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Process: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Processes = append(m.Processes, &ProcessInfo{}) if err := m.Processes[len(m.Processes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProcessInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProcessInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProcessInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) } m.Pid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Pid |= int32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ppid", wireType) } m.Ppid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Ppid |= int32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.State = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Threads", wireType) } m.Threads = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Threads |= int32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field CpuTime", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.CpuTime = float64(math.Float64frombits(v)) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field VirtualMemory", wireType) } m.VirtualMemory = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.VirtualMemory |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ResidentMemory", wireType) } m.ResidentMemory = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ResidentMemory |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Command = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Executable", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Executable = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Args = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RestartRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RestartRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RestartRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } m.Driver = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Driver |= common.ContainerDriver(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Restart) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Restart: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Restart: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RestartResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RestartResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RestartResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Restart{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StatsRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StatsRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StatsRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } m.Driver = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Driver |= common.ContainerDriver(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Stats) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Stats: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Stats: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stats", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Stats = append(m.Stats, &Stat{}) if err := m.Stats[len(m.Stats)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StatsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StatsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Stats{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Stat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Stat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Stat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Namespace = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Id = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemoryUsage", wireType) } m.MemoryUsage = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemoryUsage |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CpuUsage", wireType) } m.CpuUsage = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CpuUsage |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PodId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Memory) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Memory: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Memory: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Meminfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Meminfo == nil { m.Meminfo = &MemInfo{} } if err := m.Meminfo.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MemoryResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemoryResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemoryResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Memory{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MemInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Memtotal", wireType) } m.Memtotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Memtotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Memfree", wireType) } m.Memfree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Memfree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Memavailable", wireType) } m.Memavailable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Memavailable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Buffers", wireType) } m.Buffers = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Buffers |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cached", wireType) } m.Cached = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Cached |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Swapcached", wireType) } m.Swapcached = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Swapcached |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Active", wireType) } m.Active = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Active |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Inactive", wireType) } m.Inactive = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Inactive |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Activeanon", wireType) } m.Activeanon = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Activeanon |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Inactiveanon", wireType) } m.Inactiveanon = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Inactiveanon |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Activefile", wireType) } m.Activefile = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Activefile |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Inactivefile", wireType) } m.Inactivefile = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Inactivefile |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) } m.Unevictable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Unevictable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mlocked", wireType) } m.Mlocked = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mlocked |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Swaptotal", wireType) } m.Swaptotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Swaptotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Swapfree", wireType) } m.Swapfree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Swapfree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) } m.Dirty = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Dirty |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) } m.Writeback = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Writeback |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 19: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Anonpages", wireType) } m.Anonpages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Anonpages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 20: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mapped", wireType) } m.Mapped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mapped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 21: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Shmem", wireType) } m.Shmem = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Shmem |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 22: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Slab", wireType) } m.Slab = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Slab |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 23: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Sreclaimable", wireType) } m.Sreclaimable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Sreclaimable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 24: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Sunreclaim", wireType) } m.Sunreclaim = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Sunreclaim |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 25: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Kernelstack", wireType) } m.Kernelstack = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Kernelstack |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 26: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pagetables", wireType) } m.Pagetables = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Pagetables |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 27: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Nfsunstable", wireType) } m.Nfsunstable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Nfsunstable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 28: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Bounce", wireType) } m.Bounce = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Bounce |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 29: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Writebacktmp", wireType) } m.Writebacktmp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Writebacktmp |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 30: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Commitlimit", wireType) } m.Commitlimit = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Commitlimit |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 31: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Committedas", wireType) } m.Committedas = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Committedas |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 32: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Vmalloctotal", wireType) } m.Vmalloctotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Vmalloctotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 33: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Vmallocused", wireType) } m.Vmallocused = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Vmallocused |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 34: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Vmallocchunk", wireType) } m.Vmallocchunk = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Vmallocchunk |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 35: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hardwarecorrupted", wireType) } m.Hardwarecorrupted = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hardwarecorrupted |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 36: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Anonhugepages", wireType) } m.Anonhugepages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Anonhugepages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 37: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Shmemhugepages", wireType) } m.Shmemhugepages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Shmemhugepages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 38: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Shmempmdmapped", wireType) } m.Shmempmdmapped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Shmempmdmapped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 39: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cmatotal", wireType) } m.Cmatotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Cmatotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 40: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cmafree", wireType) } m.Cmafree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Cmafree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 41: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagestotal", wireType) } m.Hugepagestotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagestotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 42: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagesfree", wireType) } m.Hugepagesfree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagesfree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 43: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagesrsvd", wireType) } m.Hugepagesrsvd = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagesrsvd |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 44: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagessurp", wireType) } m.Hugepagessurp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagessurp |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 45: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagesize", wireType) } m.Hugepagesize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagesize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 46: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Directmap4K", wireType) } m.Directmap4K = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Directmap4K |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 47: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Directmap2M", wireType) } m.Directmap2M = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Directmap2M |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 48: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Directmap1G", wireType) } m.Directmap1G = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Directmap1G |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HostnameResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HostnameResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HostnameResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Hostname{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Hostname) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Hostname: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Hostname: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LoadAvgResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LoadAvgResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LoadAvgResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &LoadAvg{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LoadAvg) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LoadAvg: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LoadAvg: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Load1", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Load1 = float64(math.Float64frombits(v)) case 3: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Load5", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Load5 = float64(math.Float64frombits(v)) case 4: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Load15", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Load15 = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SystemStatResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SystemStatResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SystemStatResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &SystemStat{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SystemStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SystemStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SystemStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BootTime", wireType) } m.BootTime = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BootTime |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpuTotal", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CpuTotal == nil { m.CpuTotal = &CPUStat{} } if err := m.CpuTotal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Cpu", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Cpu = append(m.Cpu, &CPUStat{}) if err := m.Cpu[len(m.Cpu)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IrqTotal", wireType) } m.IrqTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IrqTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType == 0 { var v uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= uint64(b&0x7F) << shift if b < 0x80 { break } } m.Irq = append(m.Irq, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.Irq) == 0 { m.Irq = make([]uint64, 0, elementCount) } for iNdEx < postIndex { var v uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= uint64(b&0x7F) << shift if b < 0x80 { break } } m.Irq = append(m.Irq, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field Irq", wireType) } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ContextSwitches", wireType) } m.ContextSwitches = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ContextSwitches |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessCreated", wireType) } m.ProcessCreated = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessCreated |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessRunning", wireType) } m.ProcessRunning = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessRunning |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessBlocked", wireType) } m.ProcessBlocked = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessBlocked |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SoftIrqTotal", wireType) } m.SoftIrqTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SoftIrqTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SoftIrq", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.SoftIrq == nil { m.SoftIrq = &SoftIRQStat{} } if err := m.SoftIrq.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.User = float64(math.Float64frombits(v)) case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Nice", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Nice = float64(math.Float64frombits(v)) case 3: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field System", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.System = float64(math.Float64frombits(v)) case 4: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Idle", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Idle = float64(math.Float64frombits(v)) case 5: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Iowait", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Iowait = float64(math.Float64frombits(v)) case 6: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Irq", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Irq = float64(math.Float64frombits(v)) case 7: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field SoftIrq", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.SoftIrq = float64(math.Float64frombits(v)) case 8: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Steal", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Steal = float64(math.Float64frombits(v)) case 9: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Guest", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Guest = float64(math.Float64frombits(v)) case 10: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field GuestNice", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.GuestNice = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SoftIRQStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SoftIRQStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SoftIRQStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hi", wireType) } m.Hi = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hi |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timer", wireType) } m.Timer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Timer |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NetTx", wireType) } m.NetTx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NetTx |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NetRx", wireType) } m.NetRx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NetRx |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Block", wireType) } m.Block = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Block |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BlockIoPoll", wireType) } m.BlockIoPoll = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BlockIoPoll |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tasklet", wireType) } m.Tasklet = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tasklet |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Sched", wireType) } m.Sched = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Sched |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hrtimer", wireType) } m.Hrtimer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hrtimer |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rcu", wireType) } m.Rcu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rcu |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUFreqStatsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUFreqStatsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUFreqStatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &CPUsFreqStats{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUsFreqStats) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUsFreqStats: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUsFreqStats: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpuFreqStats", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CpuFreqStats = append(m.CpuFreqStats, &CPUFreqStats{}) if err := m.CpuFreqStats[len(m.CpuFreqStats)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUFreqStats) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUFreqStats: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUFreqStats: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentFrequency", wireType) } m.CurrentFrequency = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CurrentFrequency |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MinimumFrequency", wireType) } m.MinimumFrequency = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MinimumFrequency |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MaximumFrequency", wireType) } m.MaximumFrequency = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MaximumFrequency |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Governor", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Governor = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUInfoResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUInfoResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUInfoResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &CPUsInfo{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUsInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUsInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUsInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpuInfo", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CpuInfo = append(m.CpuInfo, &CPUInfo{}) if err := m.CpuInfo[len(m.CpuInfo)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Processor", wireType) } m.Processor = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Processor |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VendorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VendorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpuFamily", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CpuFamily = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Model", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Model = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ModelName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ModelName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stepping", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Stepping = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Microcode", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Microcode = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field CpuMhz", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.CpuMhz = float64(math.Float64frombits(v)) case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CacheSize", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CacheSize = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PhysicalId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PhysicalId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Siblings", wireType) } m.Siblings = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Siblings |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CoreId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CoreId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CpuCores", wireType) } m.CpuCores = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CpuCores |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApicId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApicId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InitialApicId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InitialApicId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Fpu", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Fpu = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FpuException", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FpuException = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CpuIdLevel", wireType) } m.CpuIdLevel = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CpuIdLevel |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Wp", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Wp = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Flags = append(m.Flags, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Bugs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Bugs = append(m.Bugs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 22: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field BogoMips", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.BogoMips = float64(math.Float64frombits(v)) case 23: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ClFlushSize", wireType) } m.ClFlushSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ClFlushSize |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 24: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CacheAlignment", wireType) } m.CacheAlignment = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CacheAlignment |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 25: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AddressSizes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AddressSizes = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 26: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PowerManagement", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PowerManagement = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetworkDeviceStatsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetworkDeviceStatsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetworkDeviceStatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &NetworkDeviceStats{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetworkDeviceStats) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetworkDeviceStats: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetworkDeviceStats: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Total == nil { m.Total = &NetDev{} } if err := m.Total.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Devices = append(m.Devices, &NetDev{}) if err := m.Devices[len(m.Devices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetDev) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetDev: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetDev: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxBytes", wireType) } m.RxBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxBytes |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxPackets", wireType) } m.RxPackets = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxPackets |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxErrors", wireType) } m.RxErrors = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxErrors |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxDropped", wireType) } m.RxDropped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxDropped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxFifo", wireType) } m.RxFifo = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxFifo |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxFrame", wireType) } m.RxFrame = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxFrame |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxCompressed", wireType) } m.RxCompressed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxCompressed |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMulticast", wireType) } m.RxMulticast = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMulticast |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxBytes", wireType) } m.TxBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxBytes |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPackets", wireType) } m.TxPackets = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxPackets |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxErrors", wireType) } m.TxErrors = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxErrors |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxDropped", wireType) } m.TxDropped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxDropped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxFifo", wireType) } m.TxFifo = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxFifo |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxCollisions", wireType) } m.TxCollisions = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxCollisions |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxCarrier", wireType) } m.TxCarrier = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxCarrier |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxCompressed", wireType) } m.TxCompressed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxCompressed |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskStatsResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskStatsResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskStatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &DiskStats{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskStats) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskStats: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskStats: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Total", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Total == nil { m.Total = &DiskStat{} } if err := m.Total.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Devices = append(m.Devices, &DiskStat{}) if err := m.Devices[len(m.Devices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadCompleted", wireType) } m.ReadCompleted = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReadCompleted |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadMerged", wireType) } m.ReadMerged = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReadMerged |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadSectors", wireType) } m.ReadSectors = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReadSectors |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadTimeMs", wireType) } m.ReadTimeMs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReadTimeMs |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WriteCompleted", wireType) } m.WriteCompleted = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WriteCompleted |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WriteMerged", wireType) } m.WriteMerged = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WriteMerged |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WriteSectors", wireType) } m.WriteSectors = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WriteSectors |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WriteTimeMs", wireType) } m.WriteTimeMs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WriteTimeMs |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IoInProgress", wireType) } m.IoInProgress = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IoInProgress |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IoTimeMs", wireType) } m.IoTimeMs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IoTimeMs |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IoTimeWeightedMs", wireType) } m.IoTimeWeightedMs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IoTimeWeightedMs |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DiscardCompleted", wireType) } m.DiscardCompleted = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DiscardCompleted |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DiscardMerged", wireType) } m.DiscardMerged = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DiscardMerged |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DiscardSectors", wireType) } m.DiscardSectors = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DiscardSectors |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DiscardTimeMs", wireType) } m.DiscardTimeMs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DiscardTimeMs |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdLeaveClusterRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdLeaveClusterRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdLeaveClusterRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdLeaveCluster) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdLeaveCluster: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdLeaveCluster: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdLeaveClusterResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdLeaveClusterResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdLeaveClusterResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdLeaveCluster{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMemberRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMemberRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMemberRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Member", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Member = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMember) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMember: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMember: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMemberResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMemberResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMemberResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdRemoveMember{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMemberByIDRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMemberByIDRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMemberByIDRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType) } m.MemberId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemberId |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMemberByID) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMemberByID: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMemberByID: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRemoveMemberByIDResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRemoveMemberByIDResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRemoveMemberByIDResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdRemoveMemberByID{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdForfeitLeadershipRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdForfeitLeadershipRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdForfeitLeadershipRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdForfeitLeadership) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdForfeitLeadership: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdForfeitLeadership: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Member", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Member = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdForfeitLeadershipResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdForfeitLeadershipResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdForfeitLeadershipResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdForfeitLeadership{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMemberListRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMemberListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMemberListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field QueryLocal", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.QueryLocal = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMember) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMember: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMember: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } m.Id = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Id |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PeerUrls", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PeerUrls = append(m.PeerUrls, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientUrls", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClientUrls = append(m.ClientUrls, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IsLearner = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMembers) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMembers: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMembers: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LegacyMembers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LegacyMembers = append(m.LegacyMembers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Members = append(m.Members, &EtcdMember{}) if err := m.Members[len(m.Members)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMemberListResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMemberListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMemberListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdMembers{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdSnapshotRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdSnapshotRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdSnapshotRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRecover) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRecover: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRecover: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRecoverResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRecoverResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRecoverResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdRecover{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdAlarmListResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdAlarmListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdAlarmListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdAlarm{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdAlarm) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdAlarm: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdAlarm: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MemberAlarms", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MemberAlarms = append(m.MemberAlarms, &EtcdMemberAlarm{}) if err := m.MemberAlarms[len(m.MemberAlarms)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMemberAlarm) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMemberAlarm: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMemberAlarm: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType) } m.MemberId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemberId |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Alarm", wireType) } m.Alarm = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Alarm |= EtcdMemberAlarm_AlarmType(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdAlarmDisarmResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdAlarmDisarmResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdAlarmDisarmResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdAlarmDisarm{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdAlarmDisarm) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdAlarmDisarm: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdAlarmDisarm: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MemberAlarms", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MemberAlarms = append(m.MemberAlarms, &EtcdMemberAlarm{}) if err := m.MemberAlarms[len(m.MemberAlarms)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDefragmentResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDefragmentResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDefragmentResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdDefragment{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDefragment) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDefragment: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDefragment: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdStatusResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdStatusResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdStatusResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdStatus{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MemberStatus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MemberStatus == nil { m.MemberStatus = &EtcdMemberStatus{} } if err := m.MemberStatus.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdMemberStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdMemberStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdMemberStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProtocolVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProtocolVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DbSize", wireType) } m.DbSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DbSize |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DbSizeInUse", wireType) } m.DbSizeInUse = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DbSizeInUse |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Leader", wireType) } m.Leader = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Leader |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RaftIndex", wireType) } m.RaftIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RaftIndex |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RaftTerm", wireType) } m.RaftTerm = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RaftTerm |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RaftAppliedIndex", wireType) } m.RaftAppliedIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RaftAppliedIndex |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Errors", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Errors = append(m.Errors, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IsLearner", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IsLearner = bool(v != 0) case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType) } m.MemberId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemberId |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StorageVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.StorageVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeValidateRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeValidateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeValidateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeValidateResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeValidateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeValidateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdDowngradeValidate{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeValidate) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeValidate: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeValidate: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDowngrade", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClusterDowngrade == nil { m.ClusterDowngrade = &EtcdClusterDowngrade{} } if err := m.ClusterDowngrade.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeEnableRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeEnableRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeEnableRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeEnableResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeEnableResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeEnableResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdDowngradeEnable{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeEnable) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeEnable: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeEnable: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDowngrade", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClusterDowngrade == nil { m.ClusterDowngrade = &EtcdClusterDowngrade{} } if err := m.ClusterDowngrade.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeCancelResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeCancelResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeCancelResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &EtcdDowngradeCancel{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdDowngradeCancel) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdDowngradeCancel: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdDowngradeCancel: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDowngrade", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClusterDowngrade == nil { m.ClusterDowngrade = &EtcdClusterDowngrade{} } if err := m.ClusterDowngrade.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdClusterDowngrade) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdClusterDowngrade: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdClusterDowngrade: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RouteConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RouteConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RouteConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Network", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Network = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Gateway", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Gateway = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Metric", wireType) } m.Metric = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Metric |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DHCPOptionsConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DHCPOptionsConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DHCPOptionsConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RouteMetric", wireType) } m.RouteMetric = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RouteMetric |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetworkDeviceConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetworkDeviceConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetworkDeviceConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Interface", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Interface = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Cidr", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Cidr = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= int32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Dhcp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Dhcp = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ignore", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ignore = bool(v != 0) case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DhcpOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.DhcpOptions == nil { m.DhcpOptions = &DHCPOptionsConfig{} } if err := m.DhcpOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Routes = append(m.Routes, &RouteConfig{}) if err := m.Routes[len(m.Routes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetworkConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetworkConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetworkConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Interfaces", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Interfaces = append(m.Interfaces, &NetworkDeviceConfig{}) if err := m.Interfaces[len(m.Interfaces)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *InstallConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: InstallConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: InstallConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InstallDisk", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InstallDisk = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InstallImage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InstallImage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= MachineConfig_MachineType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InstallConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.InstallConfig == nil { m.InstallConfig = &InstallConfig{} } if err := m.InstallConfig.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NetworkConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.NetworkConfig == nil { m.NetworkConfig = &NetworkConfig{} } if err := m.NetworkConfig.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field KubernetesVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.KubernetesVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ControlPlaneConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControlPlaneConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControlPlaneConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CNIConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CNIConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CNIConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Urls", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Urls = append(m.Urls, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ClusterNetworkConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ClusterNetworkConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ClusterNetworkConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CniConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CniConfig == nil { m.CniConfig = &CNIConfig{} } if err := m.CniConfig.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ClusterConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ClusterConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ClusterConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControlPlane", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ControlPlane == nil { m.ControlPlane = &ControlPlaneConfig{} } if err := m.ControlPlane.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterNetwork", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClusterNetwork == nil { m.ClusterNetwork = &ClusterNetworkConfig{} } if err := m.ClusterNetwork.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AllowSchedulingOnControlPlanes", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AllowSchedulingOnControlPlanes = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GenerateClientConfigurationRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GenerateClientConfigurationRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GenerateClientConfigurationRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Roles", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Roles = append(m.Roles, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CrtTtl", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CrtTtl == nil { m.CrtTtl = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.CrtTtl).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GenerateClientConfiguration) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GenerateClientConfiguration: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GenerateClientConfiguration: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ca", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ca = append(m.Ca[:0], dAtA[iNdEx:postIndex]...) if m.Ca == nil { m.Ca = []byte{} } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Crt", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Crt = append(m.Crt[:0], dAtA[iNdEx:postIndex]...) if m.Crt == nil { m.Crt = []byte{} } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...) if m.Key == nil { m.Key = []byte{} } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Talosconfig", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Talosconfig = append(m.Talosconfig[:0], dAtA[iNdEx:postIndex]...) if m.Talosconfig == nil { m.Talosconfig = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *GenerateClientConfigurationResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: GenerateClientConfigurationResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: GenerateClientConfigurationResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &GenerateClientConfiguration{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PacketCaptureRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PacketCaptureRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PacketCaptureRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Interface", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Interface = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Promiscuous", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Promiscuous = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SnapLen", wireType) } m.SnapLen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SnapLen |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BpfFilter", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BpfFilter = append(m.BpfFilter, &BPFInstruction{}) if err := m.BpfFilter[len(m.BpfFilter)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BPFInstruction) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BPFInstruction: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BPFInstruction: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Op", wireType) } m.Op = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Op |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Jt", wireType) } m.Jt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Jt |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Jf", wireType) } m.Jf = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Jf |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field K", wireType) } m.K = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.K |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetstatRequest_Feature) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetstatRequest_Feature: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetstatRequest_Feature: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Pid = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetstatRequest_L4Proto) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetstatRequest_L4Proto: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetstatRequest_L4Proto: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tcp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Tcp = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tcp6", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Tcp6 = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Udp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Udp = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Udp6", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Udp6 = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Udplite", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Udplite = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Udplite6", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Udplite6 = bool(v != 0) case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Raw", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Raw = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Raw6", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Raw6 = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetstatRequest_NetNS) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetstatRequest_NetNS: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetstatRequest_NetNS: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hostnetwork", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Hostnetwork = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Netns = append(m.Netns, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Allnetns", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Allnetns = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetstatRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetstatRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetstatRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Filter", wireType) } m.Filter = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Filter |= NetstatRequest_Filter(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Feature", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Feature == nil { m.Feature = &NetstatRequest_Feature{} } if err := m.Feature.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field L4Proto", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.L4Proto == nil { m.L4Proto = &NetstatRequest_L4Proto{} } if err := m.L4Proto.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Netns == nil { m.Netns = &NetstatRequest_NetNS{} } if err := m.Netns.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConnectRecord_Process) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConnectRecord_Process: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConnectRecord_Process: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pid", wireType) } m.Pid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Pid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConnectRecord) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConnectRecord: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConnectRecord: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field L4Proto", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.L4Proto = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Localip", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Localip = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Localport", wireType) } m.Localport = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Localport |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Remoteip", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Remoteip = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Remoteport", wireType) } m.Remoteport = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Remoteport |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } m.State = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.State |= ConnectRecord_State(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Txqueue", wireType) } m.Txqueue = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Txqueue |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rxqueue", wireType) } m.Rxqueue = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rxqueue |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tr", wireType) } m.Tr = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tr |= ConnectRecord_TimerActive(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timerwhen", wireType) } m.Timerwhen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Timerwhen |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Retrnsmt", wireType) } m.Retrnsmt = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Retrnsmt |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) } m.Uid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Uid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) } m.Timeout = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Timeout |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Inode", wireType) } m.Inode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Inode |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ref", wireType) } m.Ref = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Ref |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Pointer", wireType) } m.Pointer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Pointer |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Process", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Process == nil { m.Process = &ConnectRecord_Process{} } if err := m.Process.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Netns", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Netns = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Netstat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Netstat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Netstat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Connectrecord", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Connectrecord = append(m.Connectrecord, &ConnectRecord{}) if err := m.Connectrecord[len(m.Connectrecord)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NetstatResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NetstatResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NetstatResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Netstat{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaWriteRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaWriteRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaWriteRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } m.Key = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Key |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = append(m.Value[:0], dAtA[iNdEx:postIndex]...) if m.Value == nil { m.Value = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaWrite) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaWrite: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaWrite: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaWriteResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaWriteResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaWriteResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &MetaWrite{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaDeleteRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaDeleteRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaDeleteRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } m.Key = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Key |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaDelete) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaDelete: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaDelete: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaDeleteResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaDeleteResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaDeleteResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &MetaDelete{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageListRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageListRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageListRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } m.Namespace = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Namespace |= common.ContainerdNamespace(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageListResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageListResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageListResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Digest", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Digest = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CreatedAt", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CreatedAt == nil { m.CreatedAt = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.CreatedAt).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImagePullRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImagePullRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImagePullRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) } m.Namespace = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Namespace |= common.ContainerdNamespace(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Reference", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Reference = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImagePull) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImagePull: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImagePull: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImagePullResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImagePullResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImagePullResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &ImagePull{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/config/config.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/config/config.proto package config import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // MachineType matches machine.Type constants. type MachineType int32 const ( MachineType_UNKNOWN MachineType = 0 MachineType_INIT MachineType = 1 MachineType_CONTROL_PLANE MachineType = 2 MachineType_WORKER MachineType = 3 ) // Enum value maps for MachineType. var ( MachineType_name = map[int32]string{ 0: "UNKNOWN", 1: "INIT", 2: "CONTROL_PLANE", 3: "WORKER", } MachineType_value = map[string]int32{ "UNKNOWN": 0, "INIT": 1, "CONTROL_PLANE": 2, "WORKER": 3, } ) func (x MachineType) Enum() *MachineType { p := new(MachineType) *p = x return p } func (x MachineType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (MachineType) Descriptor() protoreflect.EnumDescriptor { return file_resource_config_config_proto_enumTypes[0].Descriptor() } func (MachineType) Type() protoreflect.EnumType { return &file_resource_config_config_proto_enumTypes[0] } func (x MachineType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use MachineType.Descriptor instead. func (MachineType) EnumDescriptor() ([]byte, []int) { return file_resource_config_config_proto_rawDescGZIP(), []int{0} } // MessageConfigSpec is the spec for the config.MachineConfig resource. type MachineConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` // Contains YAML marshalled machine configuration. // // Byte representation is preserved as the machine configuration was submitted to the node. YamlMarshalled []byte `protobuf:"bytes,1,opt,name=yaml_marshalled,json=yamlMarshalled,proto3" json:"yaml_marshalled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineConfigSpec) Reset() { *x = MachineConfigSpec{} mi := &file_resource_config_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineConfigSpec) ProtoMessage() {} func (x *MachineConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_config_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineConfigSpec.ProtoReflect.Descriptor instead. func (*MachineConfigSpec) Descriptor() ([]byte, []int) { return file_resource_config_config_proto_rawDescGZIP(), []int{0} } func (x *MachineConfigSpec) GetYamlMarshalled() []byte { if x != nil { return x.YamlMarshalled } return nil } // MachineTypeSpec is the spec for the config.MachineType resource. type MachineTypeSpec struct { state protoimpl.MessageState `protogen:"open.v1"` MachineType MachineType `protobuf:"varint,1,opt,name=machine_type,json=machineType,proto3,enum=resource.config.MachineType" json:"machine_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineTypeSpec) Reset() { *x = MachineTypeSpec{} mi := &file_resource_config_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineTypeSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineTypeSpec) ProtoMessage() {} func (x *MachineTypeSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_config_config_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineTypeSpec.ProtoReflect.Descriptor instead. func (*MachineTypeSpec) Descriptor() ([]byte, []int) { return file_resource_config_config_proto_rawDescGZIP(), []int{1} } func (x *MachineTypeSpec) GetMachineType() MachineType { if x != nil { return x.MachineType } return MachineType_UNKNOWN } var File_resource_config_config_proto protoreflect.FileDescriptor const file_resource_config_config_proto_rawDesc = "" + "\n" + "\x1cresource/config/config.proto\x12\x0fresource.config\"<\n" + "\x11MachineConfigSpec\x12'\n" + "\x0fyaml_marshalled\x18\x01 \x01(\fR\x0eyamlMarshalled\"R\n" + "\x0fMachineTypeSpec\x12?\n" + "\fmachine_type\x18\x01 \x01(\x0e2\x1c.resource.config.MachineTypeR\vmachineType*C\n" + "\vMachineType\x12\v\n" + "\aUNKNOWN\x10\x00\x12\b\n" + "\x04INIT\x10\x01\x12\x11\n" + "\rCONTROL_PLANE\x10\x02\x12\n" + "\n" + "\x06WORKER\x10\x03B^\n" + "\x1ddev.talos.api.resource.configZ=github.com/siderolabs/talos/pkg/machinery/api/resource/configb\x06proto3" var ( file_resource_config_config_proto_rawDescOnce sync.Once file_resource_config_config_proto_rawDescData []byte ) func file_resource_config_config_proto_rawDescGZIP() []byte { file_resource_config_config_proto_rawDescOnce.Do(func() { file_resource_config_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_config_config_proto_rawDesc), len(file_resource_config_config_proto_rawDesc))) }) return file_resource_config_config_proto_rawDescData } var file_resource_config_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_resource_config_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_resource_config_config_proto_goTypes = []any{ (MachineType)(0), // 0: resource.config.MachineType (*MachineConfigSpec)(nil), // 1: resource.config.MachineConfigSpec (*MachineTypeSpec)(nil), // 2: resource.config.MachineTypeSpec } var file_resource_config_config_proto_depIdxs = []int32{ 0, // 0: resource.config.MachineTypeSpec.machine_type:type_name -> resource.config.MachineType 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_resource_config_config_proto_init() } func file_resource_config_config_proto_init() { if File_resource_config_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_config_config_proto_rawDesc), len(file_resource_config_config_proto_rawDesc)), NumEnums: 1, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_config_config_proto_goTypes, DependencyIndexes: file_resource_config_config_proto_depIdxs, EnumInfos: file_resource_config_config_proto_enumTypes, MessageInfos: file_resource_config_config_proto_msgTypes, }.Build() File_resource_config_config_proto = out.File file_resource_config_config_proto_goTypes = nil file_resource_config_config_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/config/config_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/config/config.proto package config import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *MachineConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.YamlMarshalled) > 0 { i -= len(m.YamlMarshalled) copy(dAtA[i:], m.YamlMarshalled) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.YamlMarshalled))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MachineTypeSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineTypeSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineTypeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MachineType != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MachineType)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MachineConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.YamlMarshalled) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MachineTypeSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MachineType != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MachineType)) } n += len(m.unknownFields) return n } func (m *MachineConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field YamlMarshalled", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.YamlMarshalled = append(m.YamlMarshalled[:0], dAtA[iNdEx:postIndex]...) if m.YamlMarshalled == nil { m.YamlMarshalled = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineTypeSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineTypeSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineTypeSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MachineType", wireType) } m.MachineType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MachineType |= MachineType(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/block/block.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/block/block.proto package block import ( reflect "reflect" sync "sync" unsafe "unsafe" v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // DeviceSpec is the spec for devices status. type DeviceSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Major int64 `protobuf:"varint,2,opt,name=major,proto3" json:"major,omitempty"` Minor int64 `protobuf:"varint,3,opt,name=minor,proto3" json:"minor,omitempty"` PartitionName string `protobuf:"bytes,4,opt,name=partition_name,json=partitionName,proto3" json:"partition_name,omitempty"` PartitionNumber int64 `protobuf:"varint,5,opt,name=partition_number,json=partitionNumber,proto3" json:"partition_number,omitempty"` Generation int64 `protobuf:"varint,6,opt,name=generation,proto3" json:"generation,omitempty"` DevicePath string `protobuf:"bytes,7,opt,name=device_path,json=devicePath,proto3" json:"device_path,omitempty"` Parent string `protobuf:"bytes,8,opt,name=parent,proto3" json:"parent,omitempty"` Secondaries []string `protobuf:"bytes,9,rep,name=secondaries,proto3" json:"secondaries,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeviceSpec) Reset() { *x = DeviceSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeviceSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeviceSpec) ProtoMessage() {} func (x *DeviceSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeviceSpec.ProtoReflect.Descriptor instead. func (*DeviceSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{0} } func (x *DeviceSpec) GetType() string { if x != nil { return x.Type } return "" } func (x *DeviceSpec) GetMajor() int64 { if x != nil { return x.Major } return 0 } func (x *DeviceSpec) GetMinor() int64 { if x != nil { return x.Minor } return 0 } func (x *DeviceSpec) GetPartitionName() string { if x != nil { return x.PartitionName } return "" } func (x *DeviceSpec) GetPartitionNumber() int64 { if x != nil { return x.PartitionNumber } return 0 } func (x *DeviceSpec) GetGeneration() int64 { if x != nil { return x.Generation } return 0 } func (x *DeviceSpec) GetDevicePath() string { if x != nil { return x.DevicePath } return "" } func (x *DeviceSpec) GetParent() string { if x != nil { return x.Parent } return "" } func (x *DeviceSpec) GetSecondaries() []string { if x != nil { return x.Secondaries } return nil } // DiscoveredVolumeSpec is the spec for DiscoveredVolumes resource. type DiscoveredVolumeSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Size uint64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` SectorSize uint64 `protobuf:"varint,2,opt,name=sector_size,json=sectorSize,proto3" json:"sector_size,omitempty"` IoSize uint64 `protobuf:"varint,3,opt,name=io_size,json=ioSize,proto3" json:"io_size,omitempty"` Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` Uuid string `protobuf:"bytes,5,opt,name=uuid,proto3" json:"uuid,omitempty"` Label string `protobuf:"bytes,6,opt,name=label,proto3" json:"label,omitempty"` BlockSize uint32 `protobuf:"varint,7,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` FilesystemBlockSize uint32 `protobuf:"varint,8,opt,name=filesystem_block_size,json=filesystemBlockSize,proto3" json:"filesystem_block_size,omitempty"` ProbedSize uint64 `protobuf:"varint,9,opt,name=probed_size,json=probedSize,proto3" json:"probed_size,omitempty"` PartitionUuid string `protobuf:"bytes,10,opt,name=partition_uuid,json=partitionUuid,proto3" json:"partition_uuid,omitempty"` PartitionType string `protobuf:"bytes,11,opt,name=partition_type,json=partitionType,proto3" json:"partition_type,omitempty"` PartitionLabel string `protobuf:"bytes,12,opt,name=partition_label,json=partitionLabel,proto3" json:"partition_label,omitempty"` PartitionIndex uint64 `protobuf:"varint,13,opt,name=partition_index,json=partitionIndex,proto3" json:"partition_index,omitempty"` Type string `protobuf:"bytes,14,opt,name=type,proto3" json:"type,omitempty"` DevicePath string `protobuf:"bytes,15,opt,name=device_path,json=devicePath,proto3" json:"device_path,omitempty"` Parent string `protobuf:"bytes,16,opt,name=parent,proto3" json:"parent,omitempty"` DevPath string `protobuf:"bytes,17,opt,name=dev_path,json=devPath,proto3" json:"dev_path,omitempty"` ParentDevPath string `protobuf:"bytes,18,opt,name=parent_dev_path,json=parentDevPath,proto3" json:"parent_dev_path,omitempty"` PrettySize string `protobuf:"bytes,19,opt,name=pretty_size,json=prettySize,proto3" json:"pretty_size,omitempty"` Offset uint64 `protobuf:"varint,20,opt,name=offset,proto3" json:"offset,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiscoveredVolumeSpec) Reset() { *x = DiscoveredVolumeSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiscoveredVolumeSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiscoveredVolumeSpec) ProtoMessage() {} func (x *DiscoveredVolumeSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiscoveredVolumeSpec.ProtoReflect.Descriptor instead. func (*DiscoveredVolumeSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{1} } func (x *DiscoveredVolumeSpec) GetSize() uint64 { if x != nil { return x.Size } return 0 } func (x *DiscoveredVolumeSpec) GetSectorSize() uint64 { if x != nil { return x.SectorSize } return 0 } func (x *DiscoveredVolumeSpec) GetIoSize() uint64 { if x != nil { return x.IoSize } return 0 } func (x *DiscoveredVolumeSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *DiscoveredVolumeSpec) GetUuid() string { if x != nil { return x.Uuid } return "" } func (x *DiscoveredVolumeSpec) GetLabel() string { if x != nil { return x.Label } return "" } func (x *DiscoveredVolumeSpec) GetBlockSize() uint32 { if x != nil { return x.BlockSize } return 0 } func (x *DiscoveredVolumeSpec) GetFilesystemBlockSize() uint32 { if x != nil { return x.FilesystemBlockSize } return 0 } func (x *DiscoveredVolumeSpec) GetProbedSize() uint64 { if x != nil { return x.ProbedSize } return 0 } func (x *DiscoveredVolumeSpec) GetPartitionUuid() string { if x != nil { return x.PartitionUuid } return "" } func (x *DiscoveredVolumeSpec) GetPartitionType() string { if x != nil { return x.PartitionType } return "" } func (x *DiscoveredVolumeSpec) GetPartitionLabel() string { if x != nil { return x.PartitionLabel } return "" } func (x *DiscoveredVolumeSpec) GetPartitionIndex() uint64 { if x != nil { return x.PartitionIndex } return 0 } func (x *DiscoveredVolumeSpec) GetType() string { if x != nil { return x.Type } return "" } func (x *DiscoveredVolumeSpec) GetDevicePath() string { if x != nil { return x.DevicePath } return "" } func (x *DiscoveredVolumeSpec) GetParent() string { if x != nil { return x.Parent } return "" } func (x *DiscoveredVolumeSpec) GetDevPath() string { if x != nil { return x.DevPath } return "" } func (x *DiscoveredVolumeSpec) GetParentDevPath() string { if x != nil { return x.ParentDevPath } return "" } func (x *DiscoveredVolumeSpec) GetPrettySize() string { if x != nil { return x.PrettySize } return "" } func (x *DiscoveredVolumeSpec) GetOffset() uint64 { if x != nil { return x.Offset } return 0 } // DiscoveryRefreshRequestSpec is the spec for DiscoveryRefreshRequest. type DiscoveryRefreshRequestSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Request int64 `protobuf:"varint,1,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiscoveryRefreshRequestSpec) Reset() { *x = DiscoveryRefreshRequestSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiscoveryRefreshRequestSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiscoveryRefreshRequestSpec) ProtoMessage() {} func (x *DiscoveryRefreshRequestSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiscoveryRefreshRequestSpec.ProtoReflect.Descriptor instead. func (*DiscoveryRefreshRequestSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{2} } func (x *DiscoveryRefreshRequestSpec) GetRequest() int64 { if x != nil { return x.Request } return 0 } // DiscoveryRefreshStatusSpec is the spec for DiscoveryRefreshStatus status. type DiscoveryRefreshStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Request int64 `protobuf:"varint,1,opt,name=request,proto3" json:"request,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiscoveryRefreshStatusSpec) Reset() { *x = DiscoveryRefreshStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiscoveryRefreshStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiscoveryRefreshStatusSpec) ProtoMessage() {} func (x *DiscoveryRefreshStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiscoveryRefreshStatusSpec.ProtoReflect.Descriptor instead. func (*DiscoveryRefreshStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{3} } func (x *DiscoveryRefreshStatusSpec) GetRequest() int64 { if x != nil { return x.Request } return 0 } // DiskSelector selects a disk for the volume. type DiskSelector struct { state protoimpl.MessageState `protogen:"open.v1"` Match *v1alpha1.CheckedExpr `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` External string `protobuf:"bytes,2,opt,name=external,proto3" json:"external,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskSelector) Reset() { *x = DiskSelector{} mi := &file_resource_definitions_block_block_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskSelector) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskSelector) ProtoMessage() {} func (x *DiskSelector) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskSelector.ProtoReflect.Descriptor instead. func (*DiskSelector) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{4} } func (x *DiskSelector) GetMatch() *v1alpha1.CheckedExpr { if x != nil { return x.Match } return nil } func (x *DiskSelector) GetExternal() string { if x != nil { return x.External } return "" } // DiskSpec is the spec for Disks status. type DiskSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Size uint64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` IoSize uint64 `protobuf:"varint,2,opt,name=io_size,json=ioSize,proto3" json:"io_size,omitempty"` SectorSize uint64 `protobuf:"varint,3,opt,name=sector_size,json=sectorSize,proto3" json:"sector_size,omitempty"` Readonly bool `protobuf:"varint,4,opt,name=readonly,proto3" json:"readonly,omitempty"` Model string `protobuf:"bytes,5,opt,name=model,proto3" json:"model,omitempty"` Serial string `protobuf:"bytes,6,opt,name=serial,proto3" json:"serial,omitempty"` Modalias string `protobuf:"bytes,7,opt,name=modalias,proto3" json:"modalias,omitempty"` Wwid string `protobuf:"bytes,8,opt,name=wwid,proto3" json:"wwid,omitempty"` BusPath string `protobuf:"bytes,9,opt,name=bus_path,json=busPath,proto3" json:"bus_path,omitempty"` SubSystem string `protobuf:"bytes,10,opt,name=sub_system,json=subSystem,proto3" json:"sub_system,omitempty"` Transport string `protobuf:"bytes,11,opt,name=transport,proto3" json:"transport,omitempty"` Rotational bool `protobuf:"varint,12,opt,name=rotational,proto3" json:"rotational,omitempty"` Cdrom bool `protobuf:"varint,13,opt,name=cdrom,proto3" json:"cdrom,omitempty"` DevPath string `protobuf:"bytes,14,opt,name=dev_path,json=devPath,proto3" json:"dev_path,omitempty"` PrettySize string `protobuf:"bytes,15,opt,name=pretty_size,json=prettySize,proto3" json:"pretty_size,omitempty"` SecondaryDisks []string `protobuf:"bytes,16,rep,name=secondary_disks,json=secondaryDisks,proto3" json:"secondary_disks,omitempty"` Uuid string `protobuf:"bytes,17,opt,name=uuid,proto3" json:"uuid,omitempty"` Symlinks []string `protobuf:"bytes,18,rep,name=symlinks,proto3" json:"symlinks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiskSpec) Reset() { *x = DiskSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiskSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiskSpec) ProtoMessage() {} func (x *DiskSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiskSpec.ProtoReflect.Descriptor instead. func (*DiskSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{5} } func (x *DiskSpec) GetSize() uint64 { if x != nil { return x.Size } return 0 } func (x *DiskSpec) GetIoSize() uint64 { if x != nil { return x.IoSize } return 0 } func (x *DiskSpec) GetSectorSize() uint64 { if x != nil { return x.SectorSize } return 0 } func (x *DiskSpec) GetReadonly() bool { if x != nil { return x.Readonly } return false } func (x *DiskSpec) GetModel() string { if x != nil { return x.Model } return "" } func (x *DiskSpec) GetSerial() string { if x != nil { return x.Serial } return "" } func (x *DiskSpec) GetModalias() string { if x != nil { return x.Modalias } return "" } func (x *DiskSpec) GetWwid() string { if x != nil { return x.Wwid } return "" } func (x *DiskSpec) GetBusPath() string { if x != nil { return x.BusPath } return "" } func (x *DiskSpec) GetSubSystem() string { if x != nil { return x.SubSystem } return "" } func (x *DiskSpec) GetTransport() string { if x != nil { return x.Transport } return "" } func (x *DiskSpec) GetRotational() bool { if x != nil { return x.Rotational } return false } func (x *DiskSpec) GetCdrom() bool { if x != nil { return x.Cdrom } return false } func (x *DiskSpec) GetDevPath() string { if x != nil { return x.DevPath } return "" } func (x *DiskSpec) GetPrettySize() string { if x != nil { return x.PrettySize } return "" } func (x *DiskSpec) GetSecondaryDisks() []string { if x != nil { return x.SecondaryDisks } return nil } func (x *DiskSpec) GetUuid() string { if x != nil { return x.Uuid } return "" } func (x *DiskSpec) GetSymlinks() []string { if x != nil { return x.Symlinks } return nil } // EncryptionKey is the spec for volume encryption key. type EncryptionKey struct { state protoimpl.MessageState `protogen:"open.v1"` Slot int64 `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty"` Type enums.BlockEncryptionKeyType `protobuf:"varint,2,opt,name=type,proto3,enum=talos.resource.definitions.enums.BlockEncryptionKeyType" json:"type,omitempty"` StaticPassphrase []byte `protobuf:"bytes,3,opt,name=static_passphrase,json=staticPassphrase,proto3" json:"static_passphrase,omitempty"` KmsEndpoint string `protobuf:"bytes,4,opt,name=kms_endpoint,json=kmsEndpoint,proto3" json:"kms_endpoint,omitempty"` TpmCheckSecurebootStatusOnEnroll bool `protobuf:"varint,5,opt,name=tpm_check_secureboot_status_on_enroll,json=tpmCheckSecurebootStatusOnEnroll,proto3" json:"tpm_check_secureboot_status_on_enroll,omitempty"` LockToState bool `protobuf:"varint,6,opt,name=lock_to_state,json=lockToState,proto3" json:"lock_to_state,omitempty"` TpmpcRs []int64 `protobuf:"varint,7,rep,packed,name=tpmpc_rs,json=tpmpcRs,proto3" json:"tpmpc_rs,omitempty"` TpmPubKeyPcRs []int64 `protobuf:"varint,8,rep,packed,name=tpm_pub_key_pc_rs,json=tpmPubKeyPcRs,proto3" json:"tpm_pub_key_pc_rs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EncryptionKey) Reset() { *x = EncryptionKey{} mi := &file_resource_definitions_block_block_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EncryptionKey) String() string { return protoimpl.X.MessageStringOf(x) } func (*EncryptionKey) ProtoMessage() {} func (x *EncryptionKey) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EncryptionKey.ProtoReflect.Descriptor instead. func (*EncryptionKey) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{6} } func (x *EncryptionKey) GetSlot() int64 { if x != nil { return x.Slot } return 0 } func (x *EncryptionKey) GetType() enums.BlockEncryptionKeyType { if x != nil { return x.Type } return enums.BlockEncryptionKeyType(0) } func (x *EncryptionKey) GetStaticPassphrase() []byte { if x != nil { return x.StaticPassphrase } return nil } func (x *EncryptionKey) GetKmsEndpoint() string { if x != nil { return x.KmsEndpoint } return "" } func (x *EncryptionKey) GetTpmCheckSecurebootStatusOnEnroll() bool { if x != nil { return x.TpmCheckSecurebootStatusOnEnroll } return false } func (x *EncryptionKey) GetLockToState() bool { if x != nil { return x.LockToState } return false } func (x *EncryptionKey) GetTpmpcRs() []int64 { if x != nil { return x.TpmpcRs } return nil } func (x *EncryptionKey) GetTpmPubKeyPcRs() []int64 { if x != nil { return x.TpmPubKeyPcRs } return nil } // EncryptionSpec is the spec for volume encryption. type EncryptionSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Provider enums.BlockEncryptionProviderType `protobuf:"varint,1,opt,name=provider,proto3,enum=talos.resource.definitions.enums.BlockEncryptionProviderType" json:"provider,omitempty"` Keys []*EncryptionKey `protobuf:"bytes,2,rep,name=keys,proto3" json:"keys,omitempty"` Cipher string `protobuf:"bytes,3,opt,name=cipher,proto3" json:"cipher,omitempty"` KeySize uint64 `protobuf:"varint,4,opt,name=key_size,json=keySize,proto3" json:"key_size,omitempty"` BlockSize uint64 `protobuf:"varint,5,opt,name=block_size,json=blockSize,proto3" json:"block_size,omitempty"` PerfOptions []string `protobuf:"bytes,6,rep,name=perf_options,json=perfOptions,proto3" json:"perf_options,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EncryptionSpec) Reset() { *x = EncryptionSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EncryptionSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EncryptionSpec) ProtoMessage() {} func (x *EncryptionSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EncryptionSpec.ProtoReflect.Descriptor instead. func (*EncryptionSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{7} } func (x *EncryptionSpec) GetProvider() enums.BlockEncryptionProviderType { if x != nil { return x.Provider } return enums.BlockEncryptionProviderType(0) } func (x *EncryptionSpec) GetKeys() []*EncryptionKey { if x != nil { return x.Keys } return nil } func (x *EncryptionSpec) GetCipher() string { if x != nil { return x.Cipher } return "" } func (x *EncryptionSpec) GetKeySize() uint64 { if x != nil { return x.KeySize } return 0 } func (x *EncryptionSpec) GetBlockSize() uint64 { if x != nil { return x.BlockSize } return 0 } func (x *EncryptionSpec) GetPerfOptions() []string { if x != nil { return x.PerfOptions } return nil } // FilesystemSpec is the spec for volume filesystem. type FilesystemSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Type enums.BlockFilesystemType `protobuf:"varint,1,opt,name=type,proto3,enum=talos.resource.definitions.enums.BlockFilesystemType" json:"type,omitempty"` Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *FilesystemSpec) Reset() { *x = FilesystemSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *FilesystemSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*FilesystemSpec) ProtoMessage() {} func (x *FilesystemSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use FilesystemSpec.ProtoReflect.Descriptor instead. func (*FilesystemSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{8} } func (x *FilesystemSpec) GetType() enums.BlockFilesystemType { if x != nil { return x.Type } return enums.BlockFilesystemType(0) } func (x *FilesystemSpec) GetLabel() string { if x != nil { return x.Label } return "" } // LocatorSpec is the spec for volume locator. type LocatorSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Match *v1alpha1.CheckedExpr `protobuf:"bytes,1,opt,name=match,proto3" json:"match,omitempty"` DiskMatch *v1alpha1.CheckedExpr `protobuf:"bytes,2,opt,name=disk_match,json=diskMatch,proto3" json:"disk_match,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LocatorSpec) Reset() { *x = LocatorSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LocatorSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LocatorSpec) ProtoMessage() {} func (x *LocatorSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LocatorSpec.ProtoReflect.Descriptor instead. func (*LocatorSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{9} } func (x *LocatorSpec) GetMatch() *v1alpha1.CheckedExpr { if x != nil { return x.Match } return nil } func (x *LocatorSpec) GetDiskMatch() *v1alpha1.CheckedExpr { if x != nil { return x.DiskMatch } return nil } // MountRequestSpec is the spec for MountRequest. type MountRequestSpec struct { state protoimpl.MessageState `protogen:"open.v1"` VolumeId string `protobuf:"bytes,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` ParentMountId string `protobuf:"bytes,2,opt,name=parent_mount_id,json=parentMountId,proto3" json:"parent_mount_id,omitempty"` Requesters []string `protobuf:"bytes,3,rep,name=requesters,proto3" json:"requesters,omitempty"` RequesterIDs []string `protobuf:"bytes,4,rep,name=requester_i_ds,json=requesterIDs,proto3" json:"requester_i_ds,omitempty"` ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` Detached bool `protobuf:"varint,6,opt,name=detached,proto3" json:"detached,omitempty"` DisableAccessTime bool `protobuf:"varint,7,opt,name=disable_access_time,json=disableAccessTime,proto3" json:"disable_access_time,omitempty"` Secure bool `protobuf:"varint,8,opt,name=secure,proto3" json:"secure,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountRequestSpec) Reset() { *x = MountRequestSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountRequestSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountRequestSpec) ProtoMessage() {} func (x *MountRequestSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountRequestSpec.ProtoReflect.Descriptor instead. func (*MountRequestSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{10} } func (x *MountRequestSpec) GetVolumeId() string { if x != nil { return x.VolumeId } return "" } func (x *MountRequestSpec) GetParentMountId() string { if x != nil { return x.ParentMountId } return "" } func (x *MountRequestSpec) GetRequesters() []string { if x != nil { return x.Requesters } return nil } func (x *MountRequestSpec) GetRequesterIDs() []string { if x != nil { return x.RequesterIDs } return nil } func (x *MountRequestSpec) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *MountRequestSpec) GetDetached() bool { if x != nil { return x.Detached } return false } func (x *MountRequestSpec) GetDisableAccessTime() bool { if x != nil { return x.DisableAccessTime } return false } func (x *MountRequestSpec) GetSecure() bool { if x != nil { return x.Secure } return false } // MountSpec is the spec for volume mount. type MountSpec struct { state protoimpl.MessageState `protogen:"open.v1"` TargetPath string `protobuf:"bytes,1,opt,name=target_path,json=targetPath,proto3" json:"target_path,omitempty"` SelinuxLabel string `protobuf:"bytes,2,opt,name=selinux_label,json=selinuxLabel,proto3" json:"selinux_label,omitempty"` ProjectQuotaSupport bool `protobuf:"varint,3,opt,name=project_quota_support,json=projectQuotaSupport,proto3" json:"project_quota_support,omitempty"` ParentId string `protobuf:"bytes,4,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` FileMode uint32 `protobuf:"varint,5,opt,name=file_mode,json=fileMode,proto3" json:"file_mode,omitempty"` Uid int64 `protobuf:"varint,6,opt,name=uid,proto3" json:"uid,omitempty"` Gid int64 `protobuf:"varint,7,opt,name=gid,proto3" json:"gid,omitempty"` RecursiveRelabel bool `protobuf:"varint,8,opt,name=recursive_relabel,json=recursiveRelabel,proto3" json:"recursive_relabel,omitempty"` BindTarget string `protobuf:"bytes,9,opt,name=bind_target,json=bindTarget,proto3" json:"bind_target,omitempty"` Parameters []*ParameterSpec `protobuf:"bytes,10,rep,name=parameters,proto3" json:"parameters,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountSpec) Reset() { *x = MountSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountSpec) ProtoMessage() {} func (x *MountSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountSpec.ProtoReflect.Descriptor instead. func (*MountSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{11} } func (x *MountSpec) GetTargetPath() string { if x != nil { return x.TargetPath } return "" } func (x *MountSpec) GetSelinuxLabel() string { if x != nil { return x.SelinuxLabel } return "" } func (x *MountSpec) GetProjectQuotaSupport() bool { if x != nil { return x.ProjectQuotaSupport } return false } func (x *MountSpec) GetParentId() string { if x != nil { return x.ParentId } return "" } func (x *MountSpec) GetFileMode() uint32 { if x != nil { return x.FileMode } return 0 } func (x *MountSpec) GetUid() int64 { if x != nil { return x.Uid } return 0 } func (x *MountSpec) GetGid() int64 { if x != nil { return x.Gid } return 0 } func (x *MountSpec) GetRecursiveRelabel() bool { if x != nil { return x.RecursiveRelabel } return false } func (x *MountSpec) GetBindTarget() string { if x != nil { return x.BindTarget } return "" } func (x *MountSpec) GetParameters() []*ParameterSpec { if x != nil { return x.Parameters } return nil } // MountStatusSpec is the spec for MountStatus. type MountStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Spec *MountRequestSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` Source string `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` Filesystem enums.BlockFilesystemType `protobuf:"varint,4,opt,name=filesystem,proto3,enum=talos.resource.definitions.enums.BlockFilesystemType" json:"filesystem,omitempty"` ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` ProjectQuotaSupport bool `protobuf:"varint,6,opt,name=project_quota_support,json=projectQuotaSupport,proto3" json:"project_quota_support,omitempty"` EncryptionProvider enums.BlockEncryptionProviderType `protobuf:"varint,7,opt,name=encryption_provider,json=encryptionProvider,proto3,enum=talos.resource.definitions.enums.BlockEncryptionProviderType" json:"encryption_provider,omitempty"` Detached bool `protobuf:"varint,8,opt,name=detached,proto3" json:"detached,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountStatusSpec) Reset() { *x = MountStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountStatusSpec) ProtoMessage() {} func (x *MountStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountStatusSpec.ProtoReflect.Descriptor instead. func (*MountStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{12} } func (x *MountStatusSpec) GetSpec() *MountRequestSpec { if x != nil { return x.Spec } return nil } func (x *MountStatusSpec) GetTarget() string { if x != nil { return x.Target } return "" } func (x *MountStatusSpec) GetSource() string { if x != nil { return x.Source } return "" } func (x *MountStatusSpec) GetFilesystem() enums.BlockFilesystemType { if x != nil { return x.Filesystem } return enums.BlockFilesystemType(0) } func (x *MountStatusSpec) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *MountStatusSpec) GetProjectQuotaSupport() bool { if x != nil { return x.ProjectQuotaSupport } return false } func (x *MountStatusSpec) GetEncryptionProvider() enums.BlockEncryptionProviderType { if x != nil { return x.EncryptionProvider } return enums.BlockEncryptionProviderType(0) } func (x *MountStatusSpec) GetDetached() bool { if x != nil { return x.Detached } return false } // ParameterSpec is a mount parameter. type ParameterSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Type enums.BlockFSParameterType `protobuf:"varint,1,opt,name=type,proto3,enum=talos.resource.definitions.enums.BlockFSParameterType" json:"type,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` String_ string `protobuf:"bytes,3,opt,name=string,proto3" json:"string,omitempty"` Binary []byte `protobuf:"bytes,5,opt,name=binary,proto3" json:"binary,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ParameterSpec) Reset() { *x = ParameterSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ParameterSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ParameterSpec) ProtoMessage() {} func (x *ParameterSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ParameterSpec.ProtoReflect.Descriptor instead. func (*ParameterSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{13} } func (x *ParameterSpec) GetType() enums.BlockFSParameterType { if x != nil { return x.Type } return enums.BlockFSParameterType(0) } func (x *ParameterSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *ParameterSpec) GetString_() string { if x != nil { return x.String_ } return "" } func (x *ParameterSpec) GetBinary() []byte { if x != nil { return x.Binary } return nil } // PartitionSpec is the spec for volume partitioning. type PartitionSpec struct { state protoimpl.MessageState `protogen:"open.v1"` MinSize uint64 `protobuf:"varint,1,opt,name=min_size,json=minSize,proto3" json:"min_size,omitempty"` MaxSize uint64 `protobuf:"varint,2,opt,name=max_size,json=maxSize,proto3" json:"max_size,omitempty"` Grow bool `protobuf:"varint,3,opt,name=grow,proto3" json:"grow,omitempty"` Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"` TypeUuid string `protobuf:"bytes,5,opt,name=type_uuid,json=typeUuid,proto3" json:"type_uuid,omitempty"` RelativeMaxSize uint64 `protobuf:"varint,6,opt,name=relative_max_size,json=relativeMaxSize,proto3" json:"relative_max_size,omitempty"` NegativeMaxSize bool `protobuf:"varint,7,opt,name=negative_max_size,json=negativeMaxSize,proto3" json:"negative_max_size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PartitionSpec) Reset() { *x = PartitionSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PartitionSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PartitionSpec) ProtoMessage() {} func (x *PartitionSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PartitionSpec.ProtoReflect.Descriptor instead. func (*PartitionSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{14} } func (x *PartitionSpec) GetMinSize() uint64 { if x != nil { return x.MinSize } return 0 } func (x *PartitionSpec) GetMaxSize() uint64 { if x != nil { return x.MaxSize } return 0 } func (x *PartitionSpec) GetGrow() bool { if x != nil { return x.Grow } return false } func (x *PartitionSpec) GetLabel() string { if x != nil { return x.Label } return "" } func (x *PartitionSpec) GetTypeUuid() string { if x != nil { return x.TypeUuid } return "" } func (x *PartitionSpec) GetRelativeMaxSize() uint64 { if x != nil { return x.RelativeMaxSize } return 0 } func (x *PartitionSpec) GetNegativeMaxSize() bool { if x != nil { return x.NegativeMaxSize } return false } // ProvisioningSpec is the spec for volume provisioning. type ProvisioningSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DiskSelector *DiskSelector `protobuf:"bytes,1,opt,name=disk_selector,json=diskSelector,proto3" json:"disk_selector,omitempty"` PartitionSpec *PartitionSpec `protobuf:"bytes,2,opt,name=partition_spec,json=partitionSpec,proto3" json:"partition_spec,omitempty"` Wave int64 `protobuf:"varint,3,opt,name=wave,proto3" json:"wave,omitempty"` FilesystemSpec *FilesystemSpec `protobuf:"bytes,4,opt,name=filesystem_spec,json=filesystemSpec,proto3" json:"filesystem_spec,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProvisioningSpec) Reset() { *x = ProvisioningSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProvisioningSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProvisioningSpec) ProtoMessage() {} func (x *ProvisioningSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProvisioningSpec.ProtoReflect.Descriptor instead. func (*ProvisioningSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{15} } func (x *ProvisioningSpec) GetDiskSelector() *DiskSelector { if x != nil { return x.DiskSelector } return nil } func (x *ProvisioningSpec) GetPartitionSpec() *PartitionSpec { if x != nil { return x.PartitionSpec } return nil } func (x *ProvisioningSpec) GetWave() int64 { if x != nil { return x.Wave } return 0 } func (x *ProvisioningSpec) GetFilesystemSpec() *FilesystemSpec { if x != nil { return x.FilesystemSpec } return nil } // SwapStatusSpec is the spec for SwapStatuss resource. type SwapStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` SizeBytes uint64 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` SizeHuman string `protobuf:"bytes,3,opt,name=size_human,json=sizeHuman,proto3" json:"size_human,omitempty"` UsedBytes uint64 `protobuf:"varint,4,opt,name=used_bytes,json=usedBytes,proto3" json:"used_bytes,omitempty"` UsedHuman string `protobuf:"bytes,5,opt,name=used_human,json=usedHuman,proto3" json:"used_human,omitempty"` Priority int32 `protobuf:"varint,6,opt,name=priority,proto3" json:"priority,omitempty"` Type string `protobuf:"bytes,7,opt,name=type,proto3" json:"type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SwapStatusSpec) Reset() { *x = SwapStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SwapStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SwapStatusSpec) ProtoMessage() {} func (x *SwapStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SwapStatusSpec.ProtoReflect.Descriptor instead. func (*SwapStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{16} } func (x *SwapStatusSpec) GetDevice() string { if x != nil { return x.Device } return "" } func (x *SwapStatusSpec) GetSizeBytes() uint64 { if x != nil { return x.SizeBytes } return 0 } func (x *SwapStatusSpec) GetSizeHuman() string { if x != nil { return x.SizeHuman } return "" } func (x *SwapStatusSpec) GetUsedBytes() uint64 { if x != nil { return x.UsedBytes } return 0 } func (x *SwapStatusSpec) GetUsedHuman() string { if x != nil { return x.UsedHuman } return "" } func (x *SwapStatusSpec) GetPriority() int32 { if x != nil { return x.Priority } return 0 } func (x *SwapStatusSpec) GetType() string { if x != nil { return x.Type } return "" } // SymlinkProvisioningSpec is the spec for volume symlink. type SymlinkProvisioningSpec struct { state protoimpl.MessageState `protogen:"open.v1"` SymlinkTargetPath string `protobuf:"bytes,1,opt,name=symlink_target_path,json=symlinkTargetPath,proto3" json:"symlink_target_path,omitempty"` Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SymlinkProvisioningSpec) Reset() { *x = SymlinkProvisioningSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SymlinkProvisioningSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SymlinkProvisioningSpec) ProtoMessage() {} func (x *SymlinkProvisioningSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SymlinkProvisioningSpec.ProtoReflect.Descriptor instead. func (*SymlinkProvisioningSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{17} } func (x *SymlinkProvisioningSpec) GetSymlinkTargetPath() string { if x != nil { return x.SymlinkTargetPath } return "" } func (x *SymlinkProvisioningSpec) GetForce() bool { if x != nil { return x.Force } return false } // SymlinkSpec is the spec for Symlinks resource. type SymlinkSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Paths []string `protobuf:"bytes,1,rep,name=paths,proto3" json:"paths,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SymlinkSpec) Reset() { *x = SymlinkSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SymlinkSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SymlinkSpec) ProtoMessage() {} func (x *SymlinkSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SymlinkSpec.ProtoReflect.Descriptor instead. func (*SymlinkSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{18} } func (x *SymlinkSpec) GetPaths() []string { if x != nil { return x.Paths } return nil } // SystemDiskSpec is the spec for SystemDisks resource. type SystemDiskSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DiskId string `protobuf:"bytes,1,opt,name=disk_id,json=diskId,proto3" json:"disk_id,omitempty"` DevPath string `protobuf:"bytes,2,opt,name=dev_path,json=devPath,proto3" json:"dev_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemDiskSpec) Reset() { *x = SystemDiskSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemDiskSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemDiskSpec) ProtoMessage() {} func (x *SystemDiskSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemDiskSpec.ProtoReflect.Descriptor instead. func (*SystemDiskSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{19} } func (x *SystemDiskSpec) GetDiskId() string { if x != nil { return x.DiskId } return "" } func (x *SystemDiskSpec) GetDevPath() string { if x != nil { return x.DevPath } return "" } // TPMEncryptionOptionsInfo is the options for TPM-based encryption. type TPMEncryptionOptionsInfo struct { state protoimpl.MessageState `protogen:"open.v1"` PcRs []int64 `protobuf:"varint,1,rep,packed,name=pc_rs,json=pcRs,proto3" json:"pc_rs,omitempty"` PubKeyPcRs []int64 `protobuf:"varint,2,rep,packed,name=pub_key_pc_rs,json=pubKeyPcRs,proto3" json:"pub_key_pc_rs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TPMEncryptionOptionsInfo) Reset() { *x = TPMEncryptionOptionsInfo{} mi := &file_resource_definitions_block_block_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TPMEncryptionOptionsInfo) String() string { return protoimpl.X.MessageStringOf(x) } func (*TPMEncryptionOptionsInfo) ProtoMessage() {} func (x *TPMEncryptionOptionsInfo) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TPMEncryptionOptionsInfo.ProtoReflect.Descriptor instead. func (*TPMEncryptionOptionsInfo) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{20} } func (x *TPMEncryptionOptionsInfo) GetPcRs() []int64 { if x != nil { return x.PcRs } return nil } func (x *TPMEncryptionOptionsInfo) GetPubKeyPcRs() []int64 { if x != nil { return x.PubKeyPcRs } return nil } // UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus resource. type UserDiskConfigStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` TornDown bool `protobuf:"varint,2,opt,name=torn_down,json=tornDown,proto3" json:"torn_down,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UserDiskConfigStatusSpec) Reset() { *x = UserDiskConfigStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UserDiskConfigStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*UserDiskConfigStatusSpec) ProtoMessage() {} func (x *UserDiskConfigStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UserDiskConfigStatusSpec.ProtoReflect.Descriptor instead. func (*UserDiskConfigStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{21} } func (x *UserDiskConfigStatusSpec) GetReady() bool { if x != nil { return x.Ready } return false } func (x *UserDiskConfigStatusSpec) GetTornDown() bool { if x != nil { return x.TornDown } return false } // VolumeConfigSpec is the spec for VolumeConfig resource. type VolumeConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ParentId string `protobuf:"bytes,1,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` Type enums.BlockVolumeType `protobuf:"varint,2,opt,name=type,proto3,enum=talos.resource.definitions.enums.BlockVolumeType" json:"type,omitempty"` Provisioning *ProvisioningSpec `protobuf:"bytes,3,opt,name=provisioning,proto3" json:"provisioning,omitempty"` Locator *LocatorSpec `protobuf:"bytes,4,opt,name=locator,proto3" json:"locator,omitempty"` Mount *MountSpec `protobuf:"bytes,5,opt,name=mount,proto3" json:"mount,omitempty"` Encryption *EncryptionSpec `protobuf:"bytes,6,opt,name=encryption,proto3" json:"encryption,omitempty"` Symlink *SymlinkProvisioningSpec `protobuf:"bytes,7,opt,name=symlink,proto3" json:"symlink,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VolumeConfigSpec) Reset() { *x = VolumeConfigSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VolumeConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VolumeConfigSpec) ProtoMessage() {} func (x *VolumeConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VolumeConfigSpec.ProtoReflect.Descriptor instead. func (*VolumeConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{22} } func (x *VolumeConfigSpec) GetParentId() string { if x != nil { return x.ParentId } return "" } func (x *VolumeConfigSpec) GetType() enums.BlockVolumeType { if x != nil { return x.Type } return enums.BlockVolumeType(0) } func (x *VolumeConfigSpec) GetProvisioning() *ProvisioningSpec { if x != nil { return x.Provisioning } return nil } func (x *VolumeConfigSpec) GetLocator() *LocatorSpec { if x != nil { return x.Locator } return nil } func (x *VolumeConfigSpec) GetMount() *MountSpec { if x != nil { return x.Mount } return nil } func (x *VolumeConfigSpec) GetEncryption() *EncryptionSpec { if x != nil { return x.Encryption } return nil } func (x *VolumeConfigSpec) GetSymlink() *SymlinkProvisioningSpec { if x != nil { return x.Symlink } return nil } // VolumeMountRequestSpec is the spec for VolumeMountRequest. type VolumeMountRequestSpec struct { state protoimpl.MessageState `protogen:"open.v1"` VolumeId string `protobuf:"bytes,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` Requester string `protobuf:"bytes,2,opt,name=requester,proto3" json:"requester,omitempty"` ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` Detached bool `protobuf:"varint,4,opt,name=detached,proto3" json:"detached,omitempty"` DisableAccessTime bool `protobuf:"varint,5,opt,name=disable_access_time,json=disableAccessTime,proto3" json:"disable_access_time,omitempty"` Secure bool `protobuf:"varint,6,opt,name=secure,proto3" json:"secure,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VolumeMountRequestSpec) Reset() { *x = VolumeMountRequestSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VolumeMountRequestSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VolumeMountRequestSpec) ProtoMessage() {} func (x *VolumeMountRequestSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VolumeMountRequestSpec.ProtoReflect.Descriptor instead. func (*VolumeMountRequestSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{23} } func (x *VolumeMountRequestSpec) GetVolumeId() string { if x != nil { return x.VolumeId } return "" } func (x *VolumeMountRequestSpec) GetRequester() string { if x != nil { return x.Requester } return "" } func (x *VolumeMountRequestSpec) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *VolumeMountRequestSpec) GetDetached() bool { if x != nil { return x.Detached } return false } func (x *VolumeMountRequestSpec) GetDisableAccessTime() bool { if x != nil { return x.DisableAccessTime } return false } func (x *VolumeMountRequestSpec) GetSecure() bool { if x != nil { return x.Secure } return false } // VolumeMountStatusSpec is the spec for VolumeMountStatus. type VolumeMountStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` VolumeId string `protobuf:"bytes,1,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` Requester string `protobuf:"bytes,2,opt,name=requester,proto3" json:"requester,omitempty"` Target string `protobuf:"bytes,3,opt,name=target,proto3" json:"target,omitempty"` ReadOnly bool `protobuf:"varint,4,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` Detached bool `protobuf:"varint,5,opt,name=detached,proto3" json:"detached,omitempty"` DisableAccessTime bool `protobuf:"varint,6,opt,name=disable_access_time,json=disableAccessTime,proto3" json:"disable_access_time,omitempty"` Secure bool `protobuf:"varint,7,opt,name=secure,proto3" json:"secure,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VolumeMountStatusSpec) Reset() { *x = VolumeMountStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VolumeMountStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VolumeMountStatusSpec) ProtoMessage() {} func (x *VolumeMountStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VolumeMountStatusSpec.ProtoReflect.Descriptor instead. func (*VolumeMountStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{24} } func (x *VolumeMountStatusSpec) GetVolumeId() string { if x != nil { return x.VolumeId } return "" } func (x *VolumeMountStatusSpec) GetRequester() string { if x != nil { return x.Requester } return "" } func (x *VolumeMountStatusSpec) GetTarget() string { if x != nil { return x.Target } return "" } func (x *VolumeMountStatusSpec) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } func (x *VolumeMountStatusSpec) GetDetached() bool { if x != nil { return x.Detached } return false } func (x *VolumeMountStatusSpec) GetDisableAccessTime() bool { if x != nil { return x.DisableAccessTime } return false } func (x *VolumeMountStatusSpec) GetSecure() bool { if x != nil { return x.Secure } return false } // VolumeStatusSpec is the spec for VolumeStatus resource. type VolumeStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Phase enums.BlockVolumePhase `protobuf:"varint,1,opt,name=phase,proto3,enum=talos.resource.definitions.enums.BlockVolumePhase" json:"phase,omitempty"` Location string `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"` ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` Uuid string `protobuf:"bytes,4,opt,name=uuid,proto3" json:"uuid,omitempty"` PartitionUuid string `protobuf:"bytes,5,opt,name=partition_uuid,json=partitionUuid,proto3" json:"partition_uuid,omitempty"` PreFailPhase enums.BlockVolumePhase `protobuf:"varint,6,opt,name=pre_fail_phase,json=preFailPhase,proto3,enum=talos.resource.definitions.enums.BlockVolumePhase" json:"pre_fail_phase,omitempty"` ParentLocation string `protobuf:"bytes,7,opt,name=parent_location,json=parentLocation,proto3" json:"parent_location,omitempty"` PartitionIndex int64 `protobuf:"varint,8,opt,name=partition_index,json=partitionIndex,proto3" json:"partition_index,omitempty"` Size uint64 `protobuf:"varint,9,opt,name=size,proto3" json:"size,omitempty"` Filesystem enums.BlockFilesystemType `protobuf:"varint,10,opt,name=filesystem,proto3,enum=talos.resource.definitions.enums.BlockFilesystemType" json:"filesystem,omitempty"` MountLocation string `protobuf:"bytes,11,opt,name=mount_location,json=mountLocation,proto3" json:"mount_location,omitempty"` EncryptionProvider enums.BlockEncryptionProviderType `protobuf:"varint,12,opt,name=encryption_provider,json=encryptionProvider,proto3,enum=talos.resource.definitions.enums.BlockEncryptionProviderType" json:"encryption_provider,omitempty"` PrettySize string `protobuf:"bytes,13,opt,name=pretty_size,json=prettySize,proto3" json:"pretty_size,omitempty"` EncryptionFailedSyncs []string `protobuf:"bytes,14,rep,name=encryption_failed_syncs,json=encryptionFailedSyncs,proto3" json:"encryption_failed_syncs,omitempty"` MountSpec *MountSpec `protobuf:"bytes,15,opt,name=mount_spec,json=mountSpec,proto3" json:"mount_spec,omitempty"` Type enums.BlockVolumeType `protobuf:"varint,16,opt,name=type,proto3,enum=talos.resource.definitions.enums.BlockVolumeType" json:"type,omitempty"` ConfiguredEncryptionKeys []string `protobuf:"bytes,17,rep,name=configured_encryption_keys,json=configuredEncryptionKeys,proto3" json:"configured_encryption_keys,omitempty"` SymlinkSpec *SymlinkProvisioningSpec `protobuf:"bytes,18,opt,name=symlink_spec,json=symlinkSpec,proto3" json:"symlink_spec,omitempty"` ParentId string `protobuf:"bytes,19,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` EncryptionLockedToState bool `protobuf:"varint,20,opt,name=encryption_locked_to_state,json=encryptionLockedToState,proto3" json:"encryption_locked_to_state,omitempty"` EncryptionSlot int64 `protobuf:"varint,21,opt,name=encryption_slot,json=encryptionSlot,proto3" json:"encryption_slot,omitempty"` TpmEncryptionOptions *TPMEncryptionOptionsInfo `protobuf:"bytes,22,opt,name=tpm_encryption_options,json=tpmEncryptionOptions,proto3" json:"tpm_encryption_options,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VolumeStatusSpec) Reset() { *x = VolumeStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VolumeStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VolumeStatusSpec) ProtoMessage() {} func (x *VolumeStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VolumeStatusSpec.ProtoReflect.Descriptor instead. func (*VolumeStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{25} } func (x *VolumeStatusSpec) GetPhase() enums.BlockVolumePhase { if x != nil { return x.Phase } return enums.BlockVolumePhase(0) } func (x *VolumeStatusSpec) GetLocation() string { if x != nil { return x.Location } return "" } func (x *VolumeStatusSpec) GetErrorMessage() string { if x != nil { return x.ErrorMessage } return "" } func (x *VolumeStatusSpec) GetUuid() string { if x != nil { return x.Uuid } return "" } func (x *VolumeStatusSpec) GetPartitionUuid() string { if x != nil { return x.PartitionUuid } return "" } func (x *VolumeStatusSpec) GetPreFailPhase() enums.BlockVolumePhase { if x != nil { return x.PreFailPhase } return enums.BlockVolumePhase(0) } func (x *VolumeStatusSpec) GetParentLocation() string { if x != nil { return x.ParentLocation } return "" } func (x *VolumeStatusSpec) GetPartitionIndex() int64 { if x != nil { return x.PartitionIndex } return 0 } func (x *VolumeStatusSpec) GetSize() uint64 { if x != nil { return x.Size } return 0 } func (x *VolumeStatusSpec) GetFilesystem() enums.BlockFilesystemType { if x != nil { return x.Filesystem } return enums.BlockFilesystemType(0) } func (x *VolumeStatusSpec) GetMountLocation() string { if x != nil { return x.MountLocation } return "" } func (x *VolumeStatusSpec) GetEncryptionProvider() enums.BlockEncryptionProviderType { if x != nil { return x.EncryptionProvider } return enums.BlockEncryptionProviderType(0) } func (x *VolumeStatusSpec) GetPrettySize() string { if x != nil { return x.PrettySize } return "" } func (x *VolumeStatusSpec) GetEncryptionFailedSyncs() []string { if x != nil { return x.EncryptionFailedSyncs } return nil } func (x *VolumeStatusSpec) GetMountSpec() *MountSpec { if x != nil { return x.MountSpec } return nil } func (x *VolumeStatusSpec) GetType() enums.BlockVolumeType { if x != nil { return x.Type } return enums.BlockVolumeType(0) } func (x *VolumeStatusSpec) GetConfiguredEncryptionKeys() []string { if x != nil { return x.ConfiguredEncryptionKeys } return nil } func (x *VolumeStatusSpec) GetSymlinkSpec() *SymlinkProvisioningSpec { if x != nil { return x.SymlinkSpec } return nil } func (x *VolumeStatusSpec) GetParentId() string { if x != nil { return x.ParentId } return "" } func (x *VolumeStatusSpec) GetEncryptionLockedToState() bool { if x != nil { return x.EncryptionLockedToState } return false } func (x *VolumeStatusSpec) GetEncryptionSlot() int64 { if x != nil { return x.EncryptionSlot } return 0 } func (x *VolumeStatusSpec) GetTpmEncryptionOptions() *TPMEncryptionOptionsInfo { if x != nil { return x.TpmEncryptionOptions } return nil } // ZswapStatusSpec is the spec for ZswapStatus resource. type ZswapStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` TotalSizeBytes uint64 `protobuf:"varint,1,opt,name=total_size_bytes,json=totalSizeBytes,proto3" json:"total_size_bytes,omitempty"` TotalSizeHuman string `protobuf:"bytes,2,opt,name=total_size_human,json=totalSizeHuman,proto3" json:"total_size_human,omitempty"` StoredPages uint64 `protobuf:"varint,3,opt,name=stored_pages,json=storedPages,proto3" json:"stored_pages,omitempty"` PoolLimitHit uint64 `protobuf:"varint,4,opt,name=pool_limit_hit,json=poolLimitHit,proto3" json:"pool_limit_hit,omitempty"` RejectReclaimFail uint64 `protobuf:"varint,5,opt,name=reject_reclaim_fail,json=rejectReclaimFail,proto3" json:"reject_reclaim_fail,omitempty"` RejectAllocFail uint64 `protobuf:"varint,6,opt,name=reject_alloc_fail,json=rejectAllocFail,proto3" json:"reject_alloc_fail,omitempty"` RejectKmemcacheFail uint64 `protobuf:"varint,7,opt,name=reject_kmemcache_fail,json=rejectKmemcacheFail,proto3" json:"reject_kmemcache_fail,omitempty"` RejectCompressFail uint64 `protobuf:"varint,8,opt,name=reject_compress_fail,json=rejectCompressFail,proto3" json:"reject_compress_fail,omitempty"` RejectCompressPoor uint64 `protobuf:"varint,9,opt,name=reject_compress_poor,json=rejectCompressPoor,proto3" json:"reject_compress_poor,omitempty"` WrittenBackPages uint64 `protobuf:"varint,10,opt,name=written_back_pages,json=writtenBackPages,proto3" json:"written_back_pages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ZswapStatusSpec) Reset() { *x = ZswapStatusSpec{} mi := &file_resource_definitions_block_block_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ZswapStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ZswapStatusSpec) ProtoMessage() {} func (x *ZswapStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_block_block_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ZswapStatusSpec.ProtoReflect.Descriptor instead. func (*ZswapStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_block_block_proto_rawDescGZIP(), []int{26} } func (x *ZswapStatusSpec) GetTotalSizeBytes() uint64 { if x != nil { return x.TotalSizeBytes } return 0 } func (x *ZswapStatusSpec) GetTotalSizeHuman() string { if x != nil { return x.TotalSizeHuman } return "" } func (x *ZswapStatusSpec) GetStoredPages() uint64 { if x != nil { return x.StoredPages } return 0 } func (x *ZswapStatusSpec) GetPoolLimitHit() uint64 { if x != nil { return x.PoolLimitHit } return 0 } func (x *ZswapStatusSpec) GetRejectReclaimFail() uint64 { if x != nil { return x.RejectReclaimFail } return 0 } func (x *ZswapStatusSpec) GetRejectAllocFail() uint64 { if x != nil { return x.RejectAllocFail } return 0 } func (x *ZswapStatusSpec) GetRejectKmemcacheFail() uint64 { if x != nil { return x.RejectKmemcacheFail } return 0 } func (x *ZswapStatusSpec) GetRejectCompressFail() uint64 { if x != nil { return x.RejectCompressFail } return 0 } func (x *ZswapStatusSpec) GetRejectCompressPoor() uint64 { if x != nil { return x.RejectCompressPoor } return 0 } func (x *ZswapStatusSpec) GetWrittenBackPages() uint64 { if x != nil { return x.WrittenBackPages } return 0 } var File_resource_definitions_block_block_proto protoreflect.FileDescriptor const file_resource_definitions_block_block_proto_rawDesc = "" + "\n" + "&resource/definitions/block/block.proto\x12 talos.resource.definitions.block\x1a&google/api/expr/v1alpha1/checked.proto\x1a&resource/definitions/enums/enums.proto\"\x99\x02\n" + "\n" + "DeviceSpec\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12\x14\n" + "\x05major\x18\x02 \x01(\x03R\x05major\x12\x14\n" + "\x05minor\x18\x03 \x01(\x03R\x05minor\x12%\n" + "\x0epartition_name\x18\x04 \x01(\tR\rpartitionName\x12)\n" + "\x10partition_number\x18\x05 \x01(\x03R\x0fpartitionNumber\x12\x1e\n" + "\n" + "generation\x18\x06 \x01(\x03R\n" + "generation\x12\x1f\n" + "\vdevice_path\x18\a \x01(\tR\n" + "devicePath\x12\x16\n" + "\x06parent\x18\b \x01(\tR\x06parent\x12 \n" + "\vsecondaries\x18\t \x03(\tR\vsecondaries\"\xff\x04\n" + "\x14DiscoveredVolumeSpec\x12\x12\n" + "\x04size\x18\x01 \x01(\x04R\x04size\x12\x1f\n" + "\vsector_size\x18\x02 \x01(\x04R\n" + "sectorSize\x12\x17\n" + "\aio_size\x18\x03 \x01(\x04R\x06ioSize\x12\x12\n" + "\x04name\x18\x04 \x01(\tR\x04name\x12\x12\n" + "\x04uuid\x18\x05 \x01(\tR\x04uuid\x12\x14\n" + "\x05label\x18\x06 \x01(\tR\x05label\x12\x1d\n" + "\n" + "block_size\x18\a \x01(\rR\tblockSize\x122\n" + "\x15filesystem_block_size\x18\b \x01(\rR\x13filesystemBlockSize\x12\x1f\n" + "\vprobed_size\x18\t \x01(\x04R\n" + "probedSize\x12%\n" + "\x0epartition_uuid\x18\n" + " \x01(\tR\rpartitionUuid\x12%\n" + "\x0epartition_type\x18\v \x01(\tR\rpartitionType\x12'\n" + "\x0fpartition_label\x18\f \x01(\tR\x0epartitionLabel\x12'\n" + "\x0fpartition_index\x18\r \x01(\x04R\x0epartitionIndex\x12\x12\n" + "\x04type\x18\x0e \x01(\tR\x04type\x12\x1f\n" + "\vdevice_path\x18\x0f \x01(\tR\n" + "devicePath\x12\x16\n" + "\x06parent\x18\x10 \x01(\tR\x06parent\x12\x19\n" + "\bdev_path\x18\x11 \x01(\tR\adevPath\x12&\n" + "\x0fparent_dev_path\x18\x12 \x01(\tR\rparentDevPath\x12\x1f\n" + "\vpretty_size\x18\x13 \x01(\tR\n" + "prettySize\x12\x16\n" + "\x06offset\x18\x14 \x01(\x04R\x06offset\"7\n" + "\x1bDiscoveryRefreshRequestSpec\x12\x18\n" + "\arequest\x18\x01 \x01(\x03R\arequest\"6\n" + "\x1aDiscoveryRefreshStatusSpec\x12\x18\n" + "\arequest\x18\x01 \x01(\x03R\arequest\"g\n" + "\fDiskSelector\x12;\n" + "\x05match\x18\x01 \x01(\v2%.google.api.expr.v1alpha1.CheckedExprR\x05match\x12\x1a\n" + "\bexternal\x18\x02 \x01(\tR\bexternal\"\xf5\x03\n" + "\bDiskSpec\x12\x12\n" + "\x04size\x18\x01 \x01(\x04R\x04size\x12\x17\n" + "\aio_size\x18\x02 \x01(\x04R\x06ioSize\x12\x1f\n" + "\vsector_size\x18\x03 \x01(\x04R\n" + "sectorSize\x12\x1a\n" + "\breadonly\x18\x04 \x01(\bR\breadonly\x12\x14\n" + "\x05model\x18\x05 \x01(\tR\x05model\x12\x16\n" + "\x06serial\x18\x06 \x01(\tR\x06serial\x12\x1a\n" + "\bmodalias\x18\a \x01(\tR\bmodalias\x12\x12\n" + "\x04wwid\x18\b \x01(\tR\x04wwid\x12\x19\n" + "\bbus_path\x18\t \x01(\tR\abusPath\x12\x1d\n" + "\n" + "sub_system\x18\n" + " \x01(\tR\tsubSystem\x12\x1c\n" + "\ttransport\x18\v \x01(\tR\ttransport\x12\x1e\n" + "\n" + "rotational\x18\f \x01(\bR\n" + "rotational\x12\x14\n" + "\x05cdrom\x18\r \x01(\bR\x05cdrom\x12\x19\n" + "\bdev_path\x18\x0e \x01(\tR\adevPath\x12\x1f\n" + "\vpretty_size\x18\x0f \x01(\tR\n" + "prettySize\x12'\n" + "\x0fsecondary_disks\x18\x10 \x03(\tR\x0esecondaryDisks\x12\x12\n" + "\x04uuid\x18\x11 \x01(\tR\x04uuid\x12\x1a\n" + "\bsymlinks\x18\x12 \x03(\tR\bsymlinks\"\xfb\x02\n" + "\rEncryptionKey\x12\x12\n" + "\x04slot\x18\x01 \x01(\x03R\x04slot\x12L\n" + "\x04type\x18\x02 \x01(\x0e28.talos.resource.definitions.enums.BlockEncryptionKeyTypeR\x04type\x12+\n" + "\x11static_passphrase\x18\x03 \x01(\fR\x10staticPassphrase\x12!\n" + "\fkms_endpoint\x18\x04 \x01(\tR\vkmsEndpoint\x12O\n" + "%tpm_check_secureboot_status_on_enroll\x18\x05 \x01(\bR tpmCheckSecurebootStatusOnEnroll\x12\"\n" + "\rlock_to_state\x18\x06 \x01(\bR\vlockToState\x12\x19\n" + "\btpmpc_rs\x18\a \x03(\x03R\atpmpcRs\x12(\n" + "\x11tpm_pub_key_pc_rs\x18\b \x03(\x03R\rtpmPubKeyPcRs\"\xa5\x02\n" + "\x0eEncryptionSpec\x12Y\n" + "\bprovider\x18\x01 \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\bprovider\x12C\n" + "\x04keys\x18\x02 \x03(\v2/.talos.resource.definitions.block.EncryptionKeyR\x04keys\x12\x16\n" + "\x06cipher\x18\x03 \x01(\tR\x06cipher\x12\x19\n" + "\bkey_size\x18\x04 \x01(\x04R\akeySize\x12\x1d\n" + "\n" + "block_size\x18\x05 \x01(\x04R\tblockSize\x12!\n" + "\fperf_options\x18\x06 \x03(\tR\vperfOptions\"q\n" + "\x0eFilesystemSpec\x12I\n" + "\x04type\x18\x01 \x01(\x0e25.talos.resource.definitions.enums.BlockFilesystemTypeR\x04type\x12\x14\n" + "\x05label\x18\x02 \x01(\tR\x05label\"\x90\x01\n" + "\vLocatorSpec\x12;\n" + "\x05match\x18\x01 \x01(\v2%.google.api.expr.v1alpha1.CheckedExprR\x05match\x12D\n" + "\n" + "disk_match\x18\x02 \x01(\v2%.google.api.expr.v1alpha1.CheckedExprR\tdiskMatch\"\x9e\x02\n" + "\x10MountRequestSpec\x12\x1b\n" + "\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12&\n" + "\x0fparent_mount_id\x18\x02 \x01(\tR\rparentMountId\x12\x1e\n" + "\n" + "requesters\x18\x03 \x03(\tR\n" + "requesters\x12$\n" + "\x0erequester_i_ds\x18\x04 \x03(\tR\frequesterIDs\x12\x1b\n" + "\tread_only\x18\x05 \x01(\bR\breadOnly\x12\x1a\n" + "\bdetached\x18\x06 \x01(\bR\bdetached\x12.\n" + "\x13disable_access_time\x18\a \x01(\bR\x11disableAccessTime\x12\x16\n" + "\x06secure\x18\b \x01(\bR\x06secure\"\x82\x03\n" + "\tMountSpec\x12\x1f\n" + "\vtarget_path\x18\x01 \x01(\tR\n" + "targetPath\x12#\n" + "\rselinux_label\x18\x02 \x01(\tR\fselinuxLabel\x122\n" + "\x15project_quota_support\x18\x03 \x01(\bR\x13projectQuotaSupport\x12\x1b\n" + "\tparent_id\x18\x04 \x01(\tR\bparentId\x12\x1b\n" + "\tfile_mode\x18\x05 \x01(\rR\bfileMode\x12\x10\n" + "\x03uid\x18\x06 \x01(\x03R\x03uid\x12\x10\n" + "\x03gid\x18\a \x01(\x03R\x03gid\x12+\n" + "\x11recursive_relabel\x18\b \x01(\bR\x10recursiveRelabel\x12\x1f\n" + "\vbind_target\x18\t \x01(\tR\n" + "bindTarget\x12O\n" + "\n" + "parameters\x18\n" + " \x03(\v2/.talos.resource.definitions.block.ParameterSpecR\n" + "parameters\"\xbd\x03\n" + "\x0fMountStatusSpec\x12F\n" + "\x04spec\x18\x01 \x01(\v22.talos.resource.definitions.block.MountRequestSpecR\x04spec\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\x12\x16\n" + "\x06source\x18\x03 \x01(\tR\x06source\x12U\n" + "\n" + "filesystem\x18\x04 \x01(\x0e25.talos.resource.definitions.enums.BlockFilesystemTypeR\n" + "filesystem\x12\x1b\n" + "\tread_only\x18\x05 \x01(\bR\breadOnly\x122\n" + "\x15project_quota_support\x18\x06 \x01(\bR\x13projectQuotaSupport\x12n\n" + "\x13encryption_provider\x18\a \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\x12encryptionProvider\x12\x1a\n" + "\bdetached\x18\b \x01(\bR\bdetached\"\x9f\x01\n" + "\rParameterSpec\x12J\n" + "\x04type\x18\x01 \x01(\x0e26.talos.resource.definitions.enums.BlockFSParameterTypeR\x04type\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x16\n" + "\x06string\x18\x03 \x01(\tR\x06string\x12\x16\n" + "\x06binary\x18\x05 \x01(\fR\x06binary\"\xe4\x01\n" + "\rPartitionSpec\x12\x19\n" + "\bmin_size\x18\x01 \x01(\x04R\aminSize\x12\x19\n" + "\bmax_size\x18\x02 \x01(\x04R\amaxSize\x12\x12\n" + "\x04grow\x18\x03 \x01(\bR\x04grow\x12\x14\n" + "\x05label\x18\x04 \x01(\tR\x05label\x12\x1b\n" + "\ttype_uuid\x18\x05 \x01(\tR\btypeUuid\x12*\n" + "\x11relative_max_size\x18\x06 \x01(\x04R\x0frelativeMaxSize\x12*\n" + "\x11negative_max_size\x18\a \x01(\bR\x0fnegativeMaxSize\"\xae\x02\n" + "\x10ProvisioningSpec\x12S\n" + "\rdisk_selector\x18\x01 \x01(\v2..talos.resource.definitions.block.DiskSelectorR\fdiskSelector\x12V\n" + "\x0epartition_spec\x18\x02 \x01(\v2/.talos.resource.definitions.block.PartitionSpecR\rpartitionSpec\x12\x12\n" + "\x04wave\x18\x03 \x01(\x03R\x04wave\x12Y\n" + "\x0ffilesystem_spec\x18\x04 \x01(\v20.talos.resource.definitions.block.FilesystemSpecR\x0efilesystemSpec\"\xd4\x01\n" + "\x0eSwapStatusSpec\x12\x16\n" + "\x06device\x18\x01 \x01(\tR\x06device\x12\x1d\n" + "\n" + "size_bytes\x18\x02 \x01(\x04R\tsizeBytes\x12\x1d\n" + "\n" + "size_human\x18\x03 \x01(\tR\tsizeHuman\x12\x1d\n" + "\n" + "used_bytes\x18\x04 \x01(\x04R\tusedBytes\x12\x1d\n" + "\n" + "used_human\x18\x05 \x01(\tR\tusedHuman\x12\x1a\n" + "\bpriority\x18\x06 \x01(\x05R\bpriority\x12\x12\n" + "\x04type\x18\a \x01(\tR\x04type\"_\n" + "\x17SymlinkProvisioningSpec\x12.\n" + "\x13symlink_target_path\x18\x01 \x01(\tR\x11symlinkTargetPath\x12\x14\n" + "\x05force\x18\x02 \x01(\bR\x05force\"#\n" + "\vSymlinkSpec\x12\x14\n" + "\x05paths\x18\x01 \x03(\tR\x05paths\"D\n" + "\x0eSystemDiskSpec\x12\x17\n" + "\adisk_id\x18\x01 \x01(\tR\x06diskId\x12\x19\n" + "\bdev_path\x18\x02 \x01(\tR\adevPath\"R\n" + "\x18TPMEncryptionOptionsInfo\x12\x13\n" + "\x05pc_rs\x18\x01 \x03(\x03R\x04pcRs\x12!\n" + "\rpub_key_pc_rs\x18\x02 \x03(\x03R\n" + "pubKeyPcRs\"M\n" + "\x18UserDiskConfigStatusSpec\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12\x1b\n" + "\ttorn_down\x18\x02 \x01(\bR\btornDown\"\x81\x04\n" + "\x10VolumeConfigSpec\x12\x1b\n" + "\tparent_id\x18\x01 \x01(\tR\bparentId\x12E\n" + "\x04type\x18\x02 \x01(\x0e21.talos.resource.definitions.enums.BlockVolumeTypeR\x04type\x12V\n" + "\fprovisioning\x18\x03 \x01(\v22.talos.resource.definitions.block.ProvisioningSpecR\fprovisioning\x12G\n" + "\alocator\x18\x04 \x01(\v2-.talos.resource.definitions.block.LocatorSpecR\alocator\x12A\n" + "\x05mount\x18\x05 \x01(\v2+.talos.resource.definitions.block.MountSpecR\x05mount\x12P\n" + "\n" + "encryption\x18\x06 \x01(\v20.talos.resource.definitions.block.EncryptionSpecR\n" + "encryption\x12S\n" + "\asymlink\x18\a \x01(\v29.talos.resource.definitions.block.SymlinkProvisioningSpecR\asymlink\"\xd4\x01\n" + "\x16VolumeMountRequestSpec\x12\x1b\n" + "\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12\x1c\n" + "\trequester\x18\x02 \x01(\tR\trequester\x12\x1b\n" + "\tread_only\x18\x03 \x01(\bR\breadOnly\x12\x1a\n" + "\bdetached\x18\x04 \x01(\bR\bdetached\x12.\n" + "\x13disable_access_time\x18\x05 \x01(\bR\x11disableAccessTime\x12\x16\n" + "\x06secure\x18\x06 \x01(\bR\x06secure\"\xeb\x01\n" + "\x15VolumeMountStatusSpec\x12\x1b\n" + "\tvolume_id\x18\x01 \x01(\tR\bvolumeId\x12\x1c\n" + "\trequester\x18\x02 \x01(\tR\trequester\x12\x16\n" + "\x06target\x18\x03 \x01(\tR\x06target\x12\x1b\n" + "\tread_only\x18\x04 \x01(\bR\breadOnly\x12\x1a\n" + "\bdetached\x18\x05 \x01(\bR\bdetached\x12.\n" + "\x13disable_access_time\x18\x06 \x01(\bR\x11disableAccessTime\x12\x16\n" + "\x06secure\x18\a \x01(\bR\x06secure\"\x83\n" + "\n" + "\x10VolumeStatusSpec\x12H\n" + "\x05phase\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.BlockVolumePhaseR\x05phase\x12\x1a\n" + "\blocation\x18\x02 \x01(\tR\blocation\x12#\n" + "\rerror_message\x18\x03 \x01(\tR\ferrorMessage\x12\x12\n" + "\x04uuid\x18\x04 \x01(\tR\x04uuid\x12%\n" + "\x0epartition_uuid\x18\x05 \x01(\tR\rpartitionUuid\x12X\n" + "\x0epre_fail_phase\x18\x06 \x01(\x0e22.talos.resource.definitions.enums.BlockVolumePhaseR\fpreFailPhase\x12'\n" + "\x0fparent_location\x18\a \x01(\tR\x0eparentLocation\x12'\n" + "\x0fpartition_index\x18\b \x01(\x03R\x0epartitionIndex\x12\x12\n" + "\x04size\x18\t \x01(\x04R\x04size\x12U\n" + "\n" + "filesystem\x18\n" + " \x01(\x0e25.talos.resource.definitions.enums.BlockFilesystemTypeR\n" + "filesystem\x12%\n" + "\x0emount_location\x18\v \x01(\tR\rmountLocation\x12n\n" + "\x13encryption_provider\x18\f \x01(\x0e2=.talos.resource.definitions.enums.BlockEncryptionProviderTypeR\x12encryptionProvider\x12\x1f\n" + "\vpretty_size\x18\r \x01(\tR\n" + "prettySize\x126\n" + "\x17encryption_failed_syncs\x18\x0e \x03(\tR\x15encryptionFailedSyncs\x12J\n" + "\n" + "mount_spec\x18\x0f \x01(\v2+.talos.resource.definitions.block.MountSpecR\tmountSpec\x12E\n" + "\x04type\x18\x10 \x01(\x0e21.talos.resource.definitions.enums.BlockVolumeTypeR\x04type\x12<\n" + "\x1aconfigured_encryption_keys\x18\x11 \x03(\tR\x18configuredEncryptionKeys\x12\\\n" + "\fsymlink_spec\x18\x12 \x01(\v29.talos.resource.definitions.block.SymlinkProvisioningSpecR\vsymlinkSpec\x12\x1b\n" + "\tparent_id\x18\x13 \x01(\tR\bparentId\x12;\n" + "\x1aencryption_locked_to_state\x18\x14 \x01(\bR\x17encryptionLockedToState\x12'\n" + "\x0fencryption_slot\x18\x15 \x01(\x03R\x0eencryptionSlot\x12p\n" + "\x16tpm_encryption_options\x18\x16 \x01(\v2:.talos.resource.definitions.block.TPMEncryptionOptionsInfoR\x14tpmEncryptionOptions\"\xd0\x03\n" + "\x0fZswapStatusSpec\x12(\n" + "\x10total_size_bytes\x18\x01 \x01(\x04R\x0etotalSizeBytes\x12(\n" + "\x10total_size_human\x18\x02 \x01(\tR\x0etotalSizeHuman\x12!\n" + "\fstored_pages\x18\x03 \x01(\x04R\vstoredPages\x12$\n" + "\x0epool_limit_hit\x18\x04 \x01(\x04R\fpoolLimitHit\x12.\n" + "\x13reject_reclaim_fail\x18\x05 \x01(\x04R\x11rejectReclaimFail\x12*\n" + "\x11reject_alloc_fail\x18\x06 \x01(\x04R\x0frejectAllocFail\x122\n" + "\x15reject_kmemcache_fail\x18\a \x01(\x04R\x13rejectKmemcacheFail\x120\n" + "\x14reject_compress_fail\x18\b \x01(\x04R\x12rejectCompressFail\x120\n" + "\x14reject_compress_poor\x18\t \x01(\x04R\x12rejectCompressPoor\x12,\n" + "\x12written_back_pages\x18\n" + " \x01(\x04R\x10writtenBackPagesBt\n" + "(dev.talos.api.resource.definitions.blockZHgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/blockb\x06proto3" var ( file_resource_definitions_block_block_proto_rawDescOnce sync.Once file_resource_definitions_block_block_proto_rawDescData []byte ) func file_resource_definitions_block_block_proto_rawDescGZIP() []byte { file_resource_definitions_block_block_proto_rawDescOnce.Do(func() { file_resource_definitions_block_block_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_block_block_proto_rawDesc), len(file_resource_definitions_block_block_proto_rawDesc))) }) return file_resource_definitions_block_block_proto_rawDescData } var file_resource_definitions_block_block_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_resource_definitions_block_block_proto_goTypes = []any{ (*DeviceSpec)(nil), // 0: talos.resource.definitions.block.DeviceSpec (*DiscoveredVolumeSpec)(nil), // 1: talos.resource.definitions.block.DiscoveredVolumeSpec (*DiscoveryRefreshRequestSpec)(nil), // 2: talos.resource.definitions.block.DiscoveryRefreshRequestSpec (*DiscoveryRefreshStatusSpec)(nil), // 3: talos.resource.definitions.block.DiscoveryRefreshStatusSpec (*DiskSelector)(nil), // 4: talos.resource.definitions.block.DiskSelector (*DiskSpec)(nil), // 5: talos.resource.definitions.block.DiskSpec (*EncryptionKey)(nil), // 6: talos.resource.definitions.block.EncryptionKey (*EncryptionSpec)(nil), // 7: talos.resource.definitions.block.EncryptionSpec (*FilesystemSpec)(nil), // 8: talos.resource.definitions.block.FilesystemSpec (*LocatorSpec)(nil), // 9: talos.resource.definitions.block.LocatorSpec (*MountRequestSpec)(nil), // 10: talos.resource.definitions.block.MountRequestSpec (*MountSpec)(nil), // 11: talos.resource.definitions.block.MountSpec (*MountStatusSpec)(nil), // 12: talos.resource.definitions.block.MountStatusSpec (*ParameterSpec)(nil), // 13: talos.resource.definitions.block.ParameterSpec (*PartitionSpec)(nil), // 14: talos.resource.definitions.block.PartitionSpec (*ProvisioningSpec)(nil), // 15: talos.resource.definitions.block.ProvisioningSpec (*SwapStatusSpec)(nil), // 16: talos.resource.definitions.block.SwapStatusSpec (*SymlinkProvisioningSpec)(nil), // 17: talos.resource.definitions.block.SymlinkProvisioningSpec (*SymlinkSpec)(nil), // 18: talos.resource.definitions.block.SymlinkSpec (*SystemDiskSpec)(nil), // 19: talos.resource.definitions.block.SystemDiskSpec (*TPMEncryptionOptionsInfo)(nil), // 20: talos.resource.definitions.block.TPMEncryptionOptionsInfo (*UserDiskConfigStatusSpec)(nil), // 21: talos.resource.definitions.block.UserDiskConfigStatusSpec (*VolumeConfigSpec)(nil), // 22: talos.resource.definitions.block.VolumeConfigSpec (*VolumeMountRequestSpec)(nil), // 23: talos.resource.definitions.block.VolumeMountRequestSpec (*VolumeMountStatusSpec)(nil), // 24: talos.resource.definitions.block.VolumeMountStatusSpec (*VolumeStatusSpec)(nil), // 25: talos.resource.definitions.block.VolumeStatusSpec (*ZswapStatusSpec)(nil), // 26: talos.resource.definitions.block.ZswapStatusSpec (*v1alpha1.CheckedExpr)(nil), // 27: google.api.expr.v1alpha1.CheckedExpr (enums.BlockEncryptionKeyType)(0), // 28: talos.resource.definitions.enums.BlockEncryptionKeyType (enums.BlockEncryptionProviderType)(0), // 29: talos.resource.definitions.enums.BlockEncryptionProviderType (enums.BlockFilesystemType)(0), // 30: talos.resource.definitions.enums.BlockFilesystemType (enums.BlockFSParameterType)(0), // 31: talos.resource.definitions.enums.BlockFSParameterType (enums.BlockVolumeType)(0), // 32: talos.resource.definitions.enums.BlockVolumeType (enums.BlockVolumePhase)(0), // 33: talos.resource.definitions.enums.BlockVolumePhase } var file_resource_definitions_block_block_proto_depIdxs = []int32{ 27, // 0: talos.resource.definitions.block.DiskSelector.match:type_name -> google.api.expr.v1alpha1.CheckedExpr 28, // 1: talos.resource.definitions.block.EncryptionKey.type:type_name -> talos.resource.definitions.enums.BlockEncryptionKeyType 29, // 2: talos.resource.definitions.block.EncryptionSpec.provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType 6, // 3: talos.resource.definitions.block.EncryptionSpec.keys:type_name -> talos.resource.definitions.block.EncryptionKey 30, // 4: talos.resource.definitions.block.FilesystemSpec.type:type_name -> talos.resource.definitions.enums.BlockFilesystemType 27, // 5: talos.resource.definitions.block.LocatorSpec.match:type_name -> google.api.expr.v1alpha1.CheckedExpr 27, // 6: talos.resource.definitions.block.LocatorSpec.disk_match:type_name -> google.api.expr.v1alpha1.CheckedExpr 13, // 7: talos.resource.definitions.block.MountSpec.parameters:type_name -> talos.resource.definitions.block.ParameterSpec 10, // 8: talos.resource.definitions.block.MountStatusSpec.spec:type_name -> talos.resource.definitions.block.MountRequestSpec 30, // 9: talos.resource.definitions.block.MountStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType 29, // 10: talos.resource.definitions.block.MountStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType 31, // 11: talos.resource.definitions.block.ParameterSpec.type:type_name -> talos.resource.definitions.enums.BlockFSParameterType 4, // 12: talos.resource.definitions.block.ProvisioningSpec.disk_selector:type_name -> talos.resource.definitions.block.DiskSelector 14, // 13: talos.resource.definitions.block.ProvisioningSpec.partition_spec:type_name -> talos.resource.definitions.block.PartitionSpec 8, // 14: talos.resource.definitions.block.ProvisioningSpec.filesystem_spec:type_name -> talos.resource.definitions.block.FilesystemSpec 32, // 15: talos.resource.definitions.block.VolumeConfigSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType 15, // 16: talos.resource.definitions.block.VolumeConfigSpec.provisioning:type_name -> talos.resource.definitions.block.ProvisioningSpec 9, // 17: talos.resource.definitions.block.VolumeConfigSpec.locator:type_name -> talos.resource.definitions.block.LocatorSpec 11, // 18: talos.resource.definitions.block.VolumeConfigSpec.mount:type_name -> talos.resource.definitions.block.MountSpec 7, // 19: talos.resource.definitions.block.VolumeConfigSpec.encryption:type_name -> talos.resource.definitions.block.EncryptionSpec 17, // 20: talos.resource.definitions.block.VolumeConfigSpec.symlink:type_name -> talos.resource.definitions.block.SymlinkProvisioningSpec 33, // 21: talos.resource.definitions.block.VolumeStatusSpec.phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase 33, // 22: talos.resource.definitions.block.VolumeStatusSpec.pre_fail_phase:type_name -> talos.resource.definitions.enums.BlockVolumePhase 30, // 23: talos.resource.definitions.block.VolumeStatusSpec.filesystem:type_name -> talos.resource.definitions.enums.BlockFilesystemType 29, // 24: talos.resource.definitions.block.VolumeStatusSpec.encryption_provider:type_name -> talos.resource.definitions.enums.BlockEncryptionProviderType 11, // 25: talos.resource.definitions.block.VolumeStatusSpec.mount_spec:type_name -> talos.resource.definitions.block.MountSpec 32, // 26: talos.resource.definitions.block.VolumeStatusSpec.type:type_name -> talos.resource.definitions.enums.BlockVolumeType 17, // 27: talos.resource.definitions.block.VolumeStatusSpec.symlink_spec:type_name -> talos.resource.definitions.block.SymlinkProvisioningSpec 20, // 28: talos.resource.definitions.block.VolumeStatusSpec.tpm_encryption_options:type_name -> talos.resource.definitions.block.TPMEncryptionOptionsInfo 29, // [29:29] is the sub-list for method output_type 29, // [29:29] is the sub-list for method input_type 29, // [29:29] is the sub-list for extension type_name 29, // [29:29] is the sub-list for extension extendee 0, // [0:29] is the sub-list for field type_name } func init() { file_resource_definitions_block_block_proto_init() } func file_resource_definitions_block_block_proto_init() { if File_resource_definitions_block_block_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_block_block_proto_rawDesc), len(file_resource_definitions_block_block_proto_rawDesc)), NumEnums: 0, NumMessages: 27, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_block_block_proto_goTypes, DependencyIndexes: file_resource_definitions_block_block_proto_depIdxs, MessageInfos: file_resource_definitions_block_block_proto_msgTypes, }.Build() File_resource_definitions_block_block_proto = out.File file_resource_definitions_block_block_proto_goTypes = nil file_resource_definitions_block_block_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/block/block_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/block/block.proto package block import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" v1alpha1 "google.golang.org/genproto/googleapis/api/expr/v1alpha1" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *DeviceSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DeviceSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DeviceSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Secondaries) > 0 { for iNdEx := len(m.Secondaries) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Secondaries[iNdEx]) copy(dAtA[i:], m.Secondaries[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Secondaries[iNdEx]))) i-- dAtA[i] = 0x4a } } if len(m.Parent) > 0 { i -= len(m.Parent) copy(dAtA[i:], m.Parent) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Parent))) i-- dAtA[i] = 0x42 } if len(m.DevicePath) > 0 { i -= len(m.DevicePath) copy(dAtA[i:], m.DevicePath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DevicePath))) i-- dAtA[i] = 0x3a } if m.Generation != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Generation)) i-- dAtA[i] = 0x30 } if m.PartitionNumber != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PartitionNumber)) i-- dAtA[i] = 0x28 } if len(m.PartitionName) > 0 { i -= len(m.PartitionName) copy(dAtA[i:], m.PartitionName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartitionName))) i-- dAtA[i] = 0x22 } if m.Minor != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Minor)) i-- dAtA[i] = 0x18 } if m.Major != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Major)) i-- dAtA[i] = 0x10 } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiscoveredVolumeSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiscoveredVolumeSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiscoveredVolumeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Offset != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Offset)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa0 } if len(m.PrettySize) > 0 { i -= len(m.PrettySize) copy(dAtA[i:], m.PrettySize) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PrettySize))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if len(m.ParentDevPath) > 0 { i -= len(m.ParentDevPath) copy(dAtA[i:], m.ParentDevPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentDevPath))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if len(m.DevPath) > 0 { i -= len(m.DevPath) copy(dAtA[i:], m.DevPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DevPath))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if len(m.Parent) > 0 { i -= len(m.Parent) copy(dAtA[i:], m.Parent) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Parent))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if len(m.DevicePath) > 0 { i -= len(m.DevicePath) copy(dAtA[i:], m.DevicePath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DevicePath))) i-- dAtA[i] = 0x7a } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0x72 } if m.PartitionIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PartitionIndex)) i-- dAtA[i] = 0x68 } if len(m.PartitionLabel) > 0 { i -= len(m.PartitionLabel) copy(dAtA[i:], m.PartitionLabel) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartitionLabel))) i-- dAtA[i] = 0x62 } if len(m.PartitionType) > 0 { i -= len(m.PartitionType) copy(dAtA[i:], m.PartitionType) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartitionType))) i-- dAtA[i] = 0x5a } if len(m.PartitionUuid) > 0 { i -= len(m.PartitionUuid) copy(dAtA[i:], m.PartitionUuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartitionUuid))) i-- dAtA[i] = 0x52 } if m.ProbedSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProbedSize)) i-- dAtA[i] = 0x48 } if m.FilesystemBlockSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FilesystemBlockSize)) i-- dAtA[i] = 0x40 } if m.BlockSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BlockSize)) i-- dAtA[i] = 0x38 } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x32 } if len(m.Uuid) > 0 { i -= len(m.Uuid) copy(dAtA[i:], m.Uuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uuid))) i-- dAtA[i] = 0x2a } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x22 } if m.IoSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IoSize)) i-- dAtA[i] = 0x18 } if m.SectorSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SectorSize)) i-- dAtA[i] = 0x10 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DiscoveryRefreshRequestSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiscoveryRefreshRequestSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiscoveryRefreshRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Request != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Request)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DiscoveryRefreshStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiscoveryRefreshStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiscoveryRefreshStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Request != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Request)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DiskSelector) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskSelector) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskSelector) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.External) > 0 { i -= len(m.External) copy(dAtA[i:], m.External) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.External))) i-- dAtA[i] = 0x12 } if m.Match != nil { if vtmsg, ok := interface{}(m.Match).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Match) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DiskSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiskSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Symlinks) > 0 { for iNdEx := len(m.Symlinks) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Symlinks[iNdEx]) copy(dAtA[i:], m.Symlinks[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Symlinks[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } } if len(m.Uuid) > 0 { i -= len(m.Uuid) copy(dAtA[i:], m.Uuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uuid))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if len(m.SecondaryDisks) > 0 { for iNdEx := len(m.SecondaryDisks) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SecondaryDisks[iNdEx]) copy(dAtA[i:], m.SecondaryDisks[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SecondaryDisks[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } } if len(m.PrettySize) > 0 { i -= len(m.PrettySize) copy(dAtA[i:], m.PrettySize) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PrettySize))) i-- dAtA[i] = 0x7a } if len(m.DevPath) > 0 { i -= len(m.DevPath) copy(dAtA[i:], m.DevPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DevPath))) i-- dAtA[i] = 0x72 } if m.Cdrom { i-- if m.Cdrom { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x68 } if m.Rotational { i-- if m.Rotational { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x60 } if len(m.Transport) > 0 { i -= len(m.Transport) copy(dAtA[i:], m.Transport) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Transport))) i-- dAtA[i] = 0x5a } if len(m.SubSystem) > 0 { i -= len(m.SubSystem) copy(dAtA[i:], m.SubSystem) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubSystem))) i-- dAtA[i] = 0x52 } if len(m.BusPath) > 0 { i -= len(m.BusPath) copy(dAtA[i:], m.BusPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BusPath))) i-- dAtA[i] = 0x4a } if len(m.Wwid) > 0 { i -= len(m.Wwid) copy(dAtA[i:], m.Wwid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Wwid))) i-- dAtA[i] = 0x42 } if len(m.Modalias) > 0 { i -= len(m.Modalias) copy(dAtA[i:], m.Modalias) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Modalias))) i-- dAtA[i] = 0x3a } if len(m.Serial) > 0 { i -= len(m.Serial) copy(dAtA[i:], m.Serial) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Serial))) i-- dAtA[i] = 0x32 } if len(m.Model) > 0 { i -= len(m.Model) copy(dAtA[i:], m.Model) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Model))) i-- dAtA[i] = 0x2a } if m.Readonly { i-- if m.Readonly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.SectorSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SectorSize)) i-- dAtA[i] = 0x18 } if m.IoSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IoSize)) i-- dAtA[i] = 0x10 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EncryptionKey) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EncryptionKey) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EncryptionKey) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.TpmPubKeyPcRs) > 0 { var pksize2 int for _, num := range m.TpmPubKeyPcRs { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.TpmPubKeyPcRs { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x42 } if len(m.TpmpcRs) > 0 { var pksize4 int for _, num := range m.TpmpcRs { pksize4 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize4 j3 := i for _, num1 := range m.TpmpcRs { num := uint64(num1) for num >= 1<<7 { dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j3++ } dAtA[j3] = uint8(num) j3++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize4)) i-- dAtA[i] = 0x3a } if m.LockToState { i-- if m.LockToState { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.TpmCheckSecurebootStatusOnEnroll { i-- if m.TpmCheckSecurebootStatusOnEnroll { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.KmsEndpoint) > 0 { i -= len(m.KmsEndpoint) copy(dAtA[i:], m.KmsEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.KmsEndpoint))) i-- dAtA[i] = 0x22 } if len(m.StaticPassphrase) > 0 { i -= len(m.StaticPassphrase) copy(dAtA[i:], m.StaticPassphrase) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.StaticPassphrase))) i-- dAtA[i] = 0x1a } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x10 } if m.Slot != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Slot)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EncryptionSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EncryptionSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EncryptionSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.PerfOptions) > 0 { for iNdEx := len(m.PerfOptions) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PerfOptions[iNdEx]) copy(dAtA[i:], m.PerfOptions[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PerfOptions[iNdEx]))) i-- dAtA[i] = 0x32 } } if m.BlockSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BlockSize)) i-- dAtA[i] = 0x28 } if m.KeySize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.KeySize)) i-- dAtA[i] = 0x20 } if len(m.Cipher) > 0 { i -= len(m.Cipher) copy(dAtA[i:], m.Cipher) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Cipher))) i-- dAtA[i] = 0x1a } if len(m.Keys) > 0 { for iNdEx := len(m.Keys) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Keys[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Provider != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Provider)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *FilesystemSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *FilesystemSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *FilesystemSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x12 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *LocatorSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LocatorSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LocatorSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.DiskMatch != nil { if vtmsg, ok := interface{}(m.DiskMatch).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.DiskMatch) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Match != nil { if vtmsg, ok := interface{}(m.Match).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Match) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MountRequestSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountRequestSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Secure { i-- if m.Secure { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.DisableAccessTime { i-- if m.DisableAccessTime { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.Detached { i-- if m.Detached { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.ReadOnly { i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.RequesterIDs) > 0 { for iNdEx := len(m.RequesterIDs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.RequesterIDs[iNdEx]) copy(dAtA[i:], m.RequesterIDs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RequesterIDs[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.Requesters) > 0 { for iNdEx := len(m.Requesters) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Requesters[iNdEx]) copy(dAtA[i:], m.Requesters[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Requesters[iNdEx]))) i-- dAtA[i] = 0x1a } } if len(m.ParentMountId) > 0 { i -= len(m.ParentMountId) copy(dAtA[i:], m.ParentMountId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentMountId))) i-- dAtA[i] = 0x12 } if len(m.VolumeId) > 0 { i -= len(m.VolumeId) copy(dAtA[i:], m.VolumeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VolumeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MountSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Parameters) > 0 { for iNdEx := len(m.Parameters) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Parameters[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x52 } } if len(m.BindTarget) > 0 { i -= len(m.BindTarget) copy(dAtA[i:], m.BindTarget) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BindTarget))) i-- dAtA[i] = 0x4a } if m.RecursiveRelabel { i-- if m.RecursiveRelabel { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.Gid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Gid)) i-- dAtA[i] = 0x38 } if m.Uid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Uid)) i-- dAtA[i] = 0x30 } if m.FileMode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FileMode)) i-- dAtA[i] = 0x28 } if len(m.ParentId) > 0 { i -= len(m.ParentId) copy(dAtA[i:], m.ParentId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentId))) i-- dAtA[i] = 0x22 } if m.ProjectQuotaSupport { i-- if m.ProjectQuotaSupport { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.SelinuxLabel) > 0 { i -= len(m.SelinuxLabel) copy(dAtA[i:], m.SelinuxLabel) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SelinuxLabel))) i-- dAtA[i] = 0x12 } if len(m.TargetPath) > 0 { i -= len(m.TargetPath) copy(dAtA[i:], m.TargetPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TargetPath))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MountStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Detached { i-- if m.Detached { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.EncryptionProvider != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EncryptionProvider)) i-- dAtA[i] = 0x38 } if m.ProjectQuotaSupport { i-- if m.ProjectQuotaSupport { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.ReadOnly { i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Filesystem != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Filesystem)) i-- dAtA[i] = 0x20 } if len(m.Source) > 0 { i -= len(m.Source) copy(dAtA[i:], m.Source) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Source))) i-- dAtA[i] = 0x1a } if len(m.Target) > 0 { i -= len(m.Target) copy(dAtA[i:], m.Target) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Target))) i-- dAtA[i] = 0x12 } if m.Spec != nil { size, err := m.Spec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ParameterSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ParameterSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ParameterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Binary) > 0 { i -= len(m.Binary) copy(dAtA[i:], m.Binary) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Binary))) i-- dAtA[i] = 0x2a } if len(m.String_) > 0 { i -= len(m.String_) copy(dAtA[i:], m.String_) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.String_))) i-- dAtA[i] = 0x1a } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PartitionSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PartitionSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PartitionSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.NegativeMaxSize { i-- if m.NegativeMaxSize { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.RelativeMaxSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RelativeMaxSize)) i-- dAtA[i] = 0x30 } if len(m.TypeUuid) > 0 { i -= len(m.TypeUuid) copy(dAtA[i:], m.TypeUuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TypeUuid))) i-- dAtA[i] = 0x2a } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x22 } if m.Grow { i-- if m.Grow { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.MaxSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MaxSize)) i-- dAtA[i] = 0x10 } if m.MinSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MinSize)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ProvisioningSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProvisioningSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProvisioningSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.FilesystemSpec != nil { size, err := m.FilesystemSpec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Wave != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Wave)) i-- dAtA[i] = 0x18 } if m.PartitionSpec != nil { size, err := m.PartitionSpec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.DiskSelector != nil { size, err := m.DiskSelector.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SwapStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SwapStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SwapStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0x3a } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x30 } if len(m.UsedHuman) > 0 { i -= len(m.UsedHuman) copy(dAtA[i:], m.UsedHuman) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.UsedHuman))) i-- dAtA[i] = 0x2a } if m.UsedBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.UsedBytes)) i-- dAtA[i] = 0x20 } if len(m.SizeHuman) > 0 { i -= len(m.SizeHuman) copy(dAtA[i:], m.SizeHuman) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SizeHuman))) i-- dAtA[i] = 0x1a } if m.SizeBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SizeBytes)) i-- dAtA[i] = 0x10 } if len(m.Device) > 0 { i -= len(m.Device) copy(dAtA[i:], m.Device) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SymlinkProvisioningSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SymlinkProvisioningSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SymlinkProvisioningSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Force { i-- if m.Force { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.SymlinkTargetPath) > 0 { i -= len(m.SymlinkTargetPath) copy(dAtA[i:], m.SymlinkTargetPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SymlinkTargetPath))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SymlinkSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SymlinkSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SymlinkSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Paths) > 0 { for iNdEx := len(m.Paths) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Paths[iNdEx]) copy(dAtA[i:], m.Paths[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Paths[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *SystemDiskSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SystemDiskSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SystemDiskSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.DevPath) > 0 { i -= len(m.DevPath) copy(dAtA[i:], m.DevPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DevPath))) i-- dAtA[i] = 0x12 } if len(m.DiskId) > 0 { i -= len(m.DiskId) copy(dAtA[i:], m.DiskId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DiskId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TPMEncryptionOptionsInfo) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TPMEncryptionOptionsInfo) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TPMEncryptionOptionsInfo) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.PubKeyPcRs) > 0 { var pksize2 int for _, num := range m.PubKeyPcRs { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.PubKeyPcRs { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x12 } if len(m.PcRs) > 0 { var pksize4 int for _, num := range m.PcRs { pksize4 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize4 j3 := i for _, num1 := range m.PcRs { num := uint64(num1) for num >= 1<<7 { dAtA[j3] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j3++ } dAtA[j3] = uint8(num) j3++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize4)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *UserDiskConfigStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UserDiskConfigStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *UserDiskConfigStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TornDown { i-- if m.TornDown { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *VolumeConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VolumeConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VolumeConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Symlink != nil { size, err := m.Symlink.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } if m.Encryption != nil { size, err := m.Encryption.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.Mount != nil { size, err := m.Mount.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if m.Locator != nil { size, err := m.Locator.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Provisioning != nil { size, err := m.Provisioning.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x10 } if len(m.ParentId) > 0 { i -= len(m.ParentId) copy(dAtA[i:], m.ParentId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VolumeMountRequestSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VolumeMountRequestSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VolumeMountRequestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Secure { i-- if m.Secure { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.DisableAccessTime { i-- if m.DisableAccessTime { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Detached { i-- if m.Detached { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ReadOnly { i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.Requester) > 0 { i -= len(m.Requester) copy(dAtA[i:], m.Requester) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Requester))) i-- dAtA[i] = 0x12 } if len(m.VolumeId) > 0 { i -= len(m.VolumeId) copy(dAtA[i:], m.VolumeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VolumeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VolumeMountStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VolumeMountStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VolumeMountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Secure { i-- if m.Secure { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.DisableAccessTime { i-- if m.DisableAccessTime { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.Detached { i-- if m.Detached { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.ReadOnly { i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.Target) > 0 { i -= len(m.Target) copy(dAtA[i:], m.Target) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Target))) i-- dAtA[i] = 0x1a } if len(m.Requester) > 0 { i -= len(m.Requester) copy(dAtA[i:], m.Requester) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Requester))) i-- dAtA[i] = 0x12 } if len(m.VolumeId) > 0 { i -= len(m.VolumeId) copy(dAtA[i:], m.VolumeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VolumeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VolumeStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VolumeStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VolumeStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TpmEncryptionOptions != nil { size, err := m.TpmEncryptionOptions.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb2 } if m.EncryptionSlot != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EncryptionSlot)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa8 } if m.EncryptionLockedToState { i-- if m.EncryptionLockedToState { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa0 } if len(m.ParentId) > 0 { i -= len(m.ParentId) copy(dAtA[i:], m.ParentId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentId))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if m.SymlinkSpec != nil { size, err := m.SymlinkSpec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if len(m.ConfiguredEncryptionKeys) > 0 { for iNdEx := len(m.ConfiguredEncryptionKeys) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ConfiguredEncryptionKeys[iNdEx]) copy(dAtA[i:], m.ConfiguredEncryptionKeys[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ConfiguredEncryptionKeys[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.MountSpec != nil { size, err := m.MountSpec.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x7a } if len(m.EncryptionFailedSyncs) > 0 { for iNdEx := len(m.EncryptionFailedSyncs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.EncryptionFailedSyncs[iNdEx]) copy(dAtA[i:], m.EncryptionFailedSyncs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.EncryptionFailedSyncs[iNdEx]))) i-- dAtA[i] = 0x72 } } if len(m.PrettySize) > 0 { i -= len(m.PrettySize) copy(dAtA[i:], m.PrettySize) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PrettySize))) i-- dAtA[i] = 0x6a } if m.EncryptionProvider != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.EncryptionProvider)) i-- dAtA[i] = 0x60 } if len(m.MountLocation) > 0 { i -= len(m.MountLocation) copy(dAtA[i:], m.MountLocation) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MountLocation))) i-- dAtA[i] = 0x5a } if m.Filesystem != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Filesystem)) i-- dAtA[i] = 0x50 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x48 } if m.PartitionIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PartitionIndex)) i-- dAtA[i] = 0x40 } if len(m.ParentLocation) > 0 { i -= len(m.ParentLocation) copy(dAtA[i:], m.ParentLocation) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentLocation))) i-- dAtA[i] = 0x3a } if m.PreFailPhase != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PreFailPhase)) i-- dAtA[i] = 0x30 } if len(m.PartitionUuid) > 0 { i -= len(m.PartitionUuid) copy(dAtA[i:], m.PartitionUuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartitionUuid))) i-- dAtA[i] = 0x2a } if len(m.Uuid) > 0 { i -= len(m.Uuid) copy(dAtA[i:], m.Uuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uuid))) i-- dAtA[i] = 0x22 } if len(m.ErrorMessage) > 0 { i -= len(m.ErrorMessage) copy(dAtA[i:], m.ErrorMessage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ErrorMessage))) i-- dAtA[i] = 0x1a } if len(m.Location) > 0 { i -= len(m.Location) copy(dAtA[i:], m.Location) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Location))) i-- dAtA[i] = 0x12 } if m.Phase != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Phase)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ZswapStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ZswapStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ZswapStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.WrittenBackPages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WrittenBackPages)) i-- dAtA[i] = 0x50 } if m.RejectCompressPoor != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectCompressPoor)) i-- dAtA[i] = 0x48 } if m.RejectCompressFail != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectCompressFail)) i-- dAtA[i] = 0x40 } if m.RejectKmemcacheFail != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectKmemcacheFail)) i-- dAtA[i] = 0x38 } if m.RejectAllocFail != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectAllocFail)) i-- dAtA[i] = 0x30 } if m.RejectReclaimFail != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RejectReclaimFail)) i-- dAtA[i] = 0x28 } if m.PoolLimitHit != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PoolLimitHit)) i-- dAtA[i] = 0x20 } if m.StoredPages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.StoredPages)) i-- dAtA[i] = 0x18 } if len(m.TotalSizeHuman) > 0 { i -= len(m.TotalSizeHuman) copy(dAtA[i:], m.TotalSizeHuman) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TotalSizeHuman))) i-- dAtA[i] = 0x12 } if m.TotalSizeBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TotalSizeBytes)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DeviceSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Major != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Major)) } if m.Minor != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Minor)) } l = len(m.PartitionName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PartitionNumber != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PartitionNumber)) } if m.Generation != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Generation)) } l = len(m.DevicePath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Parent) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Secondaries) > 0 { for _, s := range m.Secondaries { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *DiscoveredVolumeSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.SectorSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SectorSize)) } if m.IoSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IoSize)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Uuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BlockSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.BlockSize)) } if m.FilesystemBlockSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FilesystemBlockSize)) } if m.ProbedSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProbedSize)) } l = len(m.PartitionUuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PartitionType) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PartitionLabel) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PartitionIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PartitionIndex)) } l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DevicePath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Parent) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DevPath) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ParentDevPath) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PrettySize) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Offset != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Offset)) } n += len(m.unknownFields) return n } func (m *DiscoveryRefreshRequestSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Request)) } n += len(m.unknownFields) return n } func (m *DiscoveryRefreshStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Request != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Request)) } n += len(m.unknownFields) return n } func (m *DiskSelector) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Match != nil { if size, ok := interface{}(m.Match).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Match) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.External) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DiskSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.IoSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IoSize)) } if m.SectorSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SectorSize)) } if m.Readonly { n += 2 } l = len(m.Model) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Serial) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Modalias) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Wwid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BusPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SubSystem) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Transport) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Rotational { n += 2 } if m.Cdrom { n += 2 } l = len(m.DevPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PrettySize) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.SecondaryDisks) > 0 { for _, s := range m.SecondaryDisks { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Uuid) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Symlinks) > 0 { for _, s := range m.Symlinks { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EncryptionKey) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Slot != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Slot)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } l = len(m.StaticPassphrase) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.KmsEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.TpmCheckSecurebootStatusOnEnroll { n += 2 } if m.LockToState { n += 2 } if len(m.TpmpcRs) > 0 { l = 0 for _, e := range m.TpmpcRs { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } if len(m.TpmPubKeyPcRs) > 0 { l = 0 for _, e := range m.TpmPubKeyPcRs { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *EncryptionSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Provider != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Provider)) } if len(m.Keys) > 0 { for _, e := range m.Keys { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Cipher) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.KeySize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.KeySize)) } if m.BlockSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.BlockSize)) } if len(m.PerfOptions) > 0 { for _, s := range m.PerfOptions { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *FilesystemSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LocatorSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Match != nil { if size, ok := interface{}(m.Match).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Match) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.DiskMatch != nil { if size, ok := interface{}(m.DiskMatch).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.DiskMatch) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MountRequestSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.VolumeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ParentMountId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Requesters) > 0 { for _, s := range m.Requesters { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.RequesterIDs) > 0 { for _, s := range m.RequesterIDs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ReadOnly { n += 2 } if m.Detached { n += 2 } if m.DisableAccessTime { n += 2 } if m.Secure { n += 2 } n += len(m.unknownFields) return n } func (m *MountSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.TargetPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SelinuxLabel) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ProjectQuotaSupport { n += 2 } l = len(m.ParentId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FileMode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FileMode)) } if m.Uid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Uid)) } if m.Gid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Gid)) } if m.RecursiveRelabel { n += 2 } l = len(m.BindTarget) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Parameters) > 0 { for _, e := range m.Parameters { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MountStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Spec != nil { l = m.Spec.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Target) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Source) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Filesystem != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Filesystem)) } if m.ReadOnly { n += 2 } if m.ProjectQuotaSupport { n += 2 } if m.EncryptionProvider != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.EncryptionProvider)) } if m.Detached { n += 2 } n += len(m.unknownFields) return n } func (m *ParameterSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.String_) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Binary) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PartitionSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MinSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MinSize)) } if m.MaxSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MaxSize)) } if m.Grow { n += 2 } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.TypeUuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.RelativeMaxSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RelativeMaxSize)) } if m.NegativeMaxSize { n += 2 } n += len(m.unknownFields) return n } func (m *ProvisioningSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.DiskSelector != nil { l = m.DiskSelector.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PartitionSpec != nil { l = m.PartitionSpec.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Wave != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Wave)) } if m.FilesystemSpec != nil { l = m.FilesystemSpec.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *SwapStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Device) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SizeBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SizeBytes)) } l = len(m.SizeHuman) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.UsedBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.UsedBytes)) } l = len(m.UsedHuman) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *SymlinkProvisioningSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.SymlinkTargetPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Force { n += 2 } n += len(m.unknownFields) return n } func (m *SymlinkSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Paths) > 0 { for _, s := range m.Paths { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *SystemDiskSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DiskId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DevPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *TPMEncryptionOptionsInfo) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.PcRs) > 0 { l = 0 for _, e := range m.PcRs { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } if len(m.PubKeyPcRs) > 0 { l = 0 for _, e := range m.PubKeyPcRs { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *UserDiskConfigStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } if m.TornDown { n += 2 } n += len(m.unknownFields) return n } func (m *VolumeConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ParentId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } if m.Provisioning != nil { l = m.Provisioning.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Locator != nil { l = m.Locator.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mount != nil { l = m.Mount.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Encryption != nil { l = m.Encryption.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Symlink != nil { l = m.Symlink.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *VolumeMountRequestSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.VolumeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Requester) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ReadOnly { n += 2 } if m.Detached { n += 2 } if m.DisableAccessTime { n += 2 } if m.Secure { n += 2 } n += len(m.unknownFields) return n } func (m *VolumeMountStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.VolumeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Requester) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Target) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ReadOnly { n += 2 } if m.Detached { n += 2 } if m.DisableAccessTime { n += 2 } if m.Secure { n += 2 } n += len(m.unknownFields) return n } func (m *VolumeStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Phase != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Phase)) } l = len(m.Location) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ErrorMessage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Uuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PartitionUuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PreFailPhase != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PreFailPhase)) } l = len(m.ParentLocation) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PartitionIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PartitionIndex)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.Filesystem != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Filesystem)) } l = len(m.MountLocation) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EncryptionProvider != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.EncryptionProvider)) } l = len(m.PrettySize) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.EncryptionFailedSyncs) > 0 { for _, s := range m.EncryptionFailedSyncs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.MountSpec != nil { l = m.MountSpec.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Type != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Type)) } if len(m.ConfiguredEncryptionKeys) > 0 { for _, s := range m.ConfiguredEncryptionKeys { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.SymlinkSpec != nil { l = m.SymlinkSpec.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ParentId) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EncryptionLockedToState { n += 3 } if m.EncryptionSlot != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.EncryptionSlot)) } if m.TpmEncryptionOptions != nil { l = m.TpmEncryptionOptions.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ZswapStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.TotalSizeBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TotalSizeBytes)) } l = len(m.TotalSizeHuman) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.StoredPages != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.StoredPages)) } if m.PoolLimitHit != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PoolLimitHit)) } if m.RejectReclaimFail != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectReclaimFail)) } if m.RejectAllocFail != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectAllocFail)) } if m.RejectKmemcacheFail != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectKmemcacheFail)) } if m.RejectCompressFail != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectCompressFail)) } if m.RejectCompressPoor != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RejectCompressPoor)) } if m.WrittenBackPages != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.WrittenBackPages)) } n += len(m.unknownFields) return n } func (m *DeviceSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DeviceSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DeviceSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Major", wireType) } m.Major = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Major |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Minor", wireType) } m.Minor = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Minor |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartitionName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionNumber", wireType) } m.PartitionNumber = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PartitionNumber |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType) } m.Generation = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Generation |= int64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DevicePath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DevicePath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Parent", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Parent = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Secondaries", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Secondaries = append(m.Secondaries, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiscoveredVolumeSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiscoveredVolumeSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiscoveredVolumeSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SectorSize", wireType) } m.SectorSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SectorSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IoSize", wireType) } m.IoSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IoSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BlockSize", wireType) } m.BlockSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BlockSize |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FilesystemBlockSize", wireType) } m.FilesystemBlockSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FilesystemBlockSize |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProbedSize", wireType) } m.ProbedSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProbedSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionUuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartitionUuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartitionType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionLabel", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartitionLabel = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionIndex", wireType) } m.PartitionIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PartitionIndex |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DevicePath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DevicePath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Parent", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Parent = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DevPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DevPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentDevPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentDevPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PrettySize", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PrettySize = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 20: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) } m.Offset = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Offset |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiscoveryRefreshRequestSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiscoveryRefreshRequestSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiscoveryRefreshRequestSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } m.Request = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Request |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiscoveryRefreshStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiscoveryRefreshStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiscoveryRefreshStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Request", wireType) } m.Request = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Request |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskSelector) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskSelector: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskSelector: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Match", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Match == nil { m.Match = &v1alpha1.CheckedExpr{} } if unmarshal, ok := interface{}(m.Match).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Match); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field External", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.External = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiskSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiskSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiskSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IoSize", wireType) } m.IoSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IoSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SectorSize", wireType) } m.SectorSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SectorSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Readonly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Readonly = bool(v != 0) case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Model", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Model = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Serial", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Serial = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Modalias", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Modalias = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Wwid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Wwid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BusPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BusPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SubSystem", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SubSystem = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Transport", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Transport = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rotational", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Rotational = bool(v != 0) case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cdrom", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Cdrom = bool(v != 0) case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DevPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DevPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PrettySize", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PrettySize = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SecondaryDisks", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SecondaryDisks = append(m.SecondaryDisks, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Symlinks", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Symlinks = append(m.Symlinks, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EncryptionKey) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EncryptionKey: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EncryptionKey: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Slot", wireType) } m.Slot = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Slot |= int64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.BlockEncryptionKeyType(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StaticPassphrase", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.StaticPassphrase = append(m.StaticPassphrase[:0], dAtA[iNdEx:postIndex]...) if m.StaticPassphrase == nil { m.StaticPassphrase = []byte{} } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field KmsEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.KmsEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TpmCheckSecurebootStatusOnEnroll", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TpmCheckSecurebootStatusOnEnroll = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LockToState", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.LockToState = bool(v != 0) case 7: if wireType == 0 { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.TpmpcRs = append(m.TpmpcRs, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.TpmpcRs) == 0 { m.TpmpcRs = make([]int64, 0, elementCount) } for iNdEx < postIndex { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.TpmpcRs = append(m.TpmpcRs, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field TpmpcRs", wireType) } case 8: if wireType == 0 { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.TpmPubKeyPcRs = append(m.TpmPubKeyPcRs, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.TpmPubKeyPcRs) == 0 { m.TpmPubKeyPcRs = make([]int64, 0, elementCount) } for iNdEx < postIndex { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.TpmPubKeyPcRs = append(m.TpmPubKeyPcRs, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field TpmPubKeyPcRs", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EncryptionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EncryptionSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EncryptionSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Provider", wireType) } m.Provider = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Provider |= enums.BlockEncryptionProviderType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Keys", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Keys = append(m.Keys, &EncryptionKey{}) if err := m.Keys[len(m.Keys)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Cipher", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Cipher = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field KeySize", wireType) } m.KeySize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.KeySize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BlockSize", wireType) } m.BlockSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BlockSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PerfOptions", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PerfOptions = append(m.PerfOptions, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *FilesystemSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: FilesystemSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: FilesystemSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.BlockFilesystemType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LocatorSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LocatorSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LocatorSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Match", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Match == nil { m.Match = &v1alpha1.CheckedExpr{} } if unmarshal, ok := interface{}(m.Match).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Match); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DiskMatch", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.DiskMatch == nil { m.DiskMatch = &v1alpha1.CheckedExpr{} } if unmarshal, ok := interface{}(m.DiskMatch).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.DiskMatch); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountRequestSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountRequestSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountRequestSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VolumeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VolumeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentMountId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentMountId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Requesters", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Requesters = append(m.Requesters, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RequesterIDs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RequesterIDs = append(m.RequesterIDs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadOnly = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Detached = bool(v != 0) case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DisableAccessTime", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DisableAccessTime = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Secure = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TargetPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SelinuxLabel", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SelinuxLabel = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProjectQuotaSupport", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ProjectQuotaSupport = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FileMode", wireType) } m.FileMode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FileMode |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Uid", wireType) } m.Uid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Uid |= int64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Gid", wireType) } m.Gid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Gid |= int64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RecursiveRelabel", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RecursiveRelabel = bool(v != 0) case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BindTarget", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BindTarget = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Parameters = append(m.Parameters, &ParameterSpec{}) if err := m.Parameters[len(m.Parameters)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Spec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Spec == nil { m.Spec = &MountRequestSpec{} } if err := m.Spec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Target = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Source = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Filesystem", wireType) } m.Filesystem = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Filesystem |= enums.BlockFilesystemType(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadOnly = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProjectQuotaSupport", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ProjectQuotaSupport = bool(v != 0) case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionProvider", wireType) } m.EncryptionProvider = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EncryptionProvider |= enums.BlockEncryptionProviderType(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Detached = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ParameterSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ParameterSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ParameterSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.BlockFSParameterType(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.String_ = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Binary", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Binary = append(m.Binary[:0], dAtA[iNdEx:postIndex]...) if m.Binary == nil { m.Binary = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PartitionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PartitionSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PartitionSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MinSize", wireType) } m.MinSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MinSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MaxSize", wireType) } m.MaxSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MaxSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Grow", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Grow = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TypeUuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TypeUuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RelativeMaxSize", wireType) } m.RelativeMaxSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RelativeMaxSize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NegativeMaxSize", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.NegativeMaxSize = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProvisioningSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProvisioningSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProvisioningSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DiskSelector", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.DiskSelector == nil { m.DiskSelector = &DiskSelector{} } if err := m.DiskSelector.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionSpec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.PartitionSpec == nil { m.PartitionSpec = &PartitionSpec{} } if err := m.PartitionSpec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Wave", wireType) } m.Wave = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Wave |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FilesystemSpec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.FilesystemSpec == nil { m.FilesystemSpec = &FilesystemSpec{} } if err := m.FilesystemSpec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SwapStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SwapStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SwapStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Device = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SizeBytes", wireType) } m.SizeBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SizeBytes |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SizeHuman", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SizeHuman = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field UsedBytes", wireType) } m.UsedBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.UsedBytes |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UsedHuman", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UsedHuman = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= int32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SymlinkProvisioningSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SymlinkProvisioningSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SymlinkProvisioningSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SymlinkTargetPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SymlinkTargetPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Force", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Force = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SymlinkSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SymlinkSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SymlinkSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Paths", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Paths = append(m.Paths, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SystemDiskSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SystemDiskSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SystemDiskSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DiskId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DiskId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DevPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DevPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TPMEncryptionOptionsInfo) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TPMEncryptionOptionsInfo: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TPMEncryptionOptionsInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType == 0 { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.PcRs = append(m.PcRs, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.PcRs) == 0 { m.PcRs = make([]int64, 0, elementCount) } for iNdEx < postIndex { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.PcRs = append(m.PcRs, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field PcRs", wireType) } case 2: if wireType == 0 { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.PubKeyPcRs = append(m.PubKeyPcRs, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int var count int for _, integer := range dAtA[iNdEx:postIndex] { if integer < 128 { count++ } } elementCount = count if elementCount != 0 && len(m.PubKeyPcRs) == 0 { m.PubKeyPcRs = make([]int64, 0, elementCount) } for iNdEx < postIndex { var v int64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int64(b&0x7F) << shift if b < 0x80 { break } } m.PubKeyPcRs = append(m.PubKeyPcRs, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field PubKeyPcRs", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UserDiskConfigStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UserDiskConfigStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UserDiskConfigStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TornDown", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TornDown = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VolumeConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VolumeConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VolumeConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.BlockVolumeType(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Provisioning", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Provisioning == nil { m.Provisioning = &ProvisioningSpec{} } if err := m.Provisioning.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Locator", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Locator == nil { m.Locator = &LocatorSpec{} } if err := m.Locator.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Mount", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Mount == nil { m.Mount = &MountSpec{} } if err := m.Mount.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Encryption", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Encryption == nil { m.Encryption = &EncryptionSpec{} } if err := m.Encryption.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Symlink", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Symlink == nil { m.Symlink = &SymlinkProvisioningSpec{} } if err := m.Symlink.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VolumeMountRequestSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VolumeMountRequestSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VolumeMountRequestSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VolumeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VolumeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Requester", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Requester = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadOnly = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Detached = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DisableAccessTime", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DisableAccessTime = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Secure = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VolumeMountStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VolumeMountStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VolumeMountStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VolumeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VolumeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Requester", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Requester = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Target = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadOnly = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Detached", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Detached = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DisableAccessTime", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DisableAccessTime = bool(v != 0) case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Secure", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Secure = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VolumeStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VolumeStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VolumeStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Phase", wireType) } m.Phase = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Phase |= enums.BlockVolumePhase(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Location", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Location = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ErrorMessage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ErrorMessage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionUuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartitionUuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PreFailPhase", wireType) } m.PreFailPhase = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PreFailPhase |= enums.BlockVolumePhase(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentLocation", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentLocation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PartitionIndex", wireType) } m.PartitionIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PartitionIndex |= int64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Filesystem", wireType) } m.Filesystem = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Filesystem |= enums.BlockFilesystemType(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MountLocation", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MountLocation = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionProvider", wireType) } m.EncryptionProvider = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EncryptionProvider |= enums.BlockEncryptionProviderType(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PrettySize", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PrettySize = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionFailedSyncs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.EncryptionFailedSyncs = append(m.EncryptionFailedSyncs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MountSpec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MountSpec == nil { m.MountSpec = &MountSpec{} } if err := m.MountSpec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.BlockVolumeType(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ConfiguredEncryptionKeys", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ConfiguredEncryptionKeys = append(m.ConfiguredEncryptionKeys, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SymlinkSpec", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.SymlinkSpec == nil { m.SymlinkSpec = &SymlinkProvisioningSpec{} } if err := m.SymlinkSpec.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 20: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionLockedToState", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.EncryptionLockedToState = bool(v != 0) case 21: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionSlot", wireType) } m.EncryptionSlot = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.EncryptionSlot |= int64(b&0x7F) << shift if b < 0x80 { break } } case 22: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TpmEncryptionOptions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.TpmEncryptionOptions == nil { m.TpmEncryptionOptions = &TPMEncryptionOptionsInfo{} } if err := m.TpmEncryptionOptions.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ZswapStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ZswapStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ZswapStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TotalSizeBytes", wireType) } m.TotalSizeBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TotalSizeBytes |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TotalSizeHuman", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TotalSizeHuman = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field StoredPages", wireType) } m.StoredPages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.StoredPages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PoolLimitHit", wireType) } m.PoolLimitHit = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PoolLimitHit |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RejectReclaimFail", wireType) } m.RejectReclaimFail = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RejectReclaimFail |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RejectAllocFail", wireType) } m.RejectAllocFail = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RejectAllocFail |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RejectKmemcacheFail", wireType) } m.RejectKmemcacheFail = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RejectKmemcacheFail |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RejectCompressFail", wireType) } m.RejectCompressFail = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RejectCompressFail |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RejectCompressPoor", wireType) } m.RejectCompressPoor = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RejectCompressPoor |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WrittenBackPages", wireType) } m.WrittenBackPages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WrittenBackPages |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/cluster/cluster.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/cluster/cluster.proto package cluster import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // AffiliateSpec describes Affiliate state. type AffiliateSpec struct { state protoimpl.MessageState `protogen:"open.v1"` NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` Addresses []*common.NetIP `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` Nodename string `protobuf:"bytes,4,opt,name=nodename,proto3" json:"nodename,omitempty"` OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"` MachineType enums.MachineType `protobuf:"varint,6,opt,name=machine_type,json=machineType,proto3,enum=talos.resource.definitions.enums.MachineType" json:"machine_type,omitempty"` KubeSpan *KubeSpanAffiliateSpec `protobuf:"bytes,7,opt,name=kube_span,json=kubeSpan,proto3" json:"kube_span,omitempty"` ControlPlane *ControlPlane `protobuf:"bytes,8,opt,name=control_plane,json=controlPlane,proto3" json:"control_plane,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AffiliateSpec) Reset() { *x = AffiliateSpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AffiliateSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AffiliateSpec) ProtoMessage() {} func (x *AffiliateSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AffiliateSpec.ProtoReflect.Descriptor instead. func (*AffiliateSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{0} } func (x *AffiliateSpec) GetNodeId() string { if x != nil { return x.NodeId } return "" } func (x *AffiliateSpec) GetAddresses() []*common.NetIP { if x != nil { return x.Addresses } return nil } func (x *AffiliateSpec) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *AffiliateSpec) GetNodename() string { if x != nil { return x.Nodename } return "" } func (x *AffiliateSpec) GetOperatingSystem() string { if x != nil { return x.OperatingSystem } return "" } func (x *AffiliateSpec) GetMachineType() enums.MachineType { if x != nil { return x.MachineType } return enums.MachineType(0) } func (x *AffiliateSpec) GetKubeSpan() *KubeSpanAffiliateSpec { if x != nil { return x.KubeSpan } return nil } func (x *AffiliateSpec) GetControlPlane() *ControlPlane { if x != nil { return x.ControlPlane } return nil } // ConfigSpec describes KubeSpan configuration. type ConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DiscoveryEnabled bool `protobuf:"varint,1,opt,name=discovery_enabled,json=discoveryEnabled,proto3" json:"discovery_enabled,omitempty"` RegistryKubernetesEnabled bool `protobuf:"varint,2,opt,name=registry_kubernetes_enabled,json=registryKubernetesEnabled,proto3" json:"registry_kubernetes_enabled,omitempty"` RegistryServiceEnabled bool `protobuf:"varint,3,opt,name=registry_service_enabled,json=registryServiceEnabled,proto3" json:"registry_service_enabled,omitempty"` ServiceEndpoint string `protobuf:"bytes,4,opt,name=service_endpoint,json=serviceEndpoint,proto3" json:"service_endpoint,omitempty"` ServiceEndpointInsecure bool `protobuf:"varint,5,opt,name=service_endpoint_insecure,json=serviceEndpointInsecure,proto3" json:"service_endpoint_insecure,omitempty"` ServiceEncryptionKey []byte `protobuf:"bytes,6,opt,name=service_encryption_key,json=serviceEncryptionKey,proto3" json:"service_encryption_key,omitempty"` ServiceClusterId string `protobuf:"bytes,7,opt,name=service_cluster_id,json=serviceClusterId,proto3" json:"service_cluster_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigSpec) Reset() { *x = ConfigSpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigSpec) ProtoMessage() {} func (x *ConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigSpec.ProtoReflect.Descriptor instead. func (*ConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{1} } func (x *ConfigSpec) GetDiscoveryEnabled() bool { if x != nil { return x.DiscoveryEnabled } return false } func (x *ConfigSpec) GetRegistryKubernetesEnabled() bool { if x != nil { return x.RegistryKubernetesEnabled } return false } func (x *ConfigSpec) GetRegistryServiceEnabled() bool { if x != nil { return x.RegistryServiceEnabled } return false } func (x *ConfigSpec) GetServiceEndpoint() string { if x != nil { return x.ServiceEndpoint } return "" } func (x *ConfigSpec) GetServiceEndpointInsecure() bool { if x != nil { return x.ServiceEndpointInsecure } return false } func (x *ConfigSpec) GetServiceEncryptionKey() []byte { if x != nil { return x.ServiceEncryptionKey } return nil } func (x *ConfigSpec) GetServiceClusterId() string { if x != nil { return x.ServiceClusterId } return "" } // ControlPlane describes ControlPlane data if any. type ControlPlane struct { state protoimpl.MessageState `protogen:"open.v1"` ApiServerPort int64 `protobuf:"varint,1,opt,name=api_server_port,json=apiServerPort,proto3" json:"api_server_port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControlPlane) Reset() { *x = ControlPlane{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControlPlane) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControlPlane) ProtoMessage() {} func (x *ControlPlane) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControlPlane.ProtoReflect.Descriptor instead. func (*ControlPlane) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{2} } func (x *ControlPlane) GetApiServerPort() int64 { if x != nil { return x.ApiServerPort } return 0 } // IdentitySpec describes status of rendered secrets. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. type IdentitySpec struct { state protoimpl.MessageState `protogen:"open.v1"` NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *IdentitySpec) Reset() { *x = IdentitySpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *IdentitySpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*IdentitySpec) ProtoMessage() {} func (x *IdentitySpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use IdentitySpec.ProtoReflect.Descriptor instead. func (*IdentitySpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{3} } func (x *IdentitySpec) GetNodeId() string { if x != nil { return x.NodeId } return "" } // InfoSpec describes cluster information. type InfoSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ClusterId string `protobuf:"bytes,1,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` ClusterName string `protobuf:"bytes,2,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *InfoSpec) Reset() { *x = InfoSpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *InfoSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*InfoSpec) ProtoMessage() {} func (x *InfoSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use InfoSpec.ProtoReflect.Descriptor instead. func (*InfoSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{4} } func (x *InfoSpec) GetClusterId() string { if x != nil { return x.ClusterId } return "" } func (x *InfoSpec) GetClusterName() string { if x != nil { return x.ClusterName } return "" } // KubeSpanAffiliateSpec describes additional information specific for the KubeSpan. type KubeSpanAffiliateSpec struct { state protoimpl.MessageState `protogen:"open.v1"` PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` Address *common.NetIP `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` AdditionalAddresses []*common.NetIPPrefix `protobuf:"bytes,3,rep,name=additional_addresses,json=additionalAddresses,proto3" json:"additional_addresses,omitempty"` Endpoints []*common.NetIPPort `protobuf:"bytes,4,rep,name=endpoints,proto3" json:"endpoints,omitempty"` ExcludeAdvertisedNetworks []*common.NetIPPrefix `protobuf:"bytes,5,rep,name=exclude_advertised_networks,json=excludeAdvertisedNetworks,proto3" json:"exclude_advertised_networks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubeSpanAffiliateSpec) Reset() { *x = KubeSpanAffiliateSpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubeSpanAffiliateSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubeSpanAffiliateSpec) ProtoMessage() {} func (x *KubeSpanAffiliateSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubeSpanAffiliateSpec.ProtoReflect.Descriptor instead. func (*KubeSpanAffiliateSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{5} } func (x *KubeSpanAffiliateSpec) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } func (x *KubeSpanAffiliateSpec) GetAddress() *common.NetIP { if x != nil { return x.Address } return nil } func (x *KubeSpanAffiliateSpec) GetAdditionalAddresses() []*common.NetIPPrefix { if x != nil { return x.AdditionalAddresses } return nil } func (x *KubeSpanAffiliateSpec) GetEndpoints() []*common.NetIPPort { if x != nil { return x.Endpoints } return nil } func (x *KubeSpanAffiliateSpec) GetExcludeAdvertisedNetworks() []*common.NetIPPrefix { if x != nil { return x.ExcludeAdvertisedNetworks } return nil } // MemberSpec describes Member state. type MemberSpec struct { state protoimpl.MessageState `protogen:"open.v1"` NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` Addresses []*common.NetIP `protobuf:"bytes,2,rep,name=addresses,proto3" json:"addresses,omitempty"` Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` MachineType enums.MachineType `protobuf:"varint,4,opt,name=machine_type,json=machineType,proto3,enum=talos.resource.definitions.enums.MachineType" json:"machine_type,omitempty"` OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"` ControlPlane *ControlPlane `protobuf:"bytes,6,opt,name=control_plane,json=controlPlane,proto3" json:"control_plane,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemberSpec) Reset() { *x = MemberSpec{} mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemberSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemberSpec) ProtoMessage() {} func (x *MemberSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cluster_cluster_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemberSpec.ProtoReflect.Descriptor instead. func (*MemberSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cluster_cluster_proto_rawDescGZIP(), []int{6} } func (x *MemberSpec) GetNodeId() string { if x != nil { return x.NodeId } return "" } func (x *MemberSpec) GetAddresses() []*common.NetIP { if x != nil { return x.Addresses } return nil } func (x *MemberSpec) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *MemberSpec) GetMachineType() enums.MachineType { if x != nil { return x.MachineType } return enums.MachineType(0) } func (x *MemberSpec) GetOperatingSystem() string { if x != nil { return x.OperatingSystem } return "" } func (x *MemberSpec) GetControlPlane() *ControlPlane { if x != nil { return x.ControlPlane } return nil } var File_resource_definitions_cluster_cluster_proto protoreflect.FileDescriptor const file_resource_definitions_cluster_cluster_proto_rawDesc = "" + "\n" + "*resource/definitions/cluster/cluster.proto\x12\"talos.resource.definitions.cluster\x1a\x13common/common.proto\x1a&resource/definitions/enums/enums.proto\"\xb9\x03\n" + "\rAffiliateSpec\x12\x17\n" + "\anode_id\x18\x01 \x01(\tR\x06nodeId\x12+\n" + "\taddresses\x18\x02 \x03(\v2\r.common.NetIPR\taddresses\x12\x1a\n" + "\bhostname\x18\x03 \x01(\tR\bhostname\x12\x1a\n" + "\bnodename\x18\x04 \x01(\tR\bnodename\x12)\n" + "\x10operating_system\x18\x05 \x01(\tR\x0foperatingSystem\x12P\n" + "\fmachine_type\x18\x06 \x01(\x0e2-.talos.resource.definitions.enums.MachineTypeR\vmachineType\x12V\n" + "\tkube_span\x18\a \x01(\v29.talos.resource.definitions.cluster.KubeSpanAffiliateSpecR\bkubeSpan\x12U\n" + "\rcontrol_plane\x18\b \x01(\v20.talos.resource.definitions.cluster.ControlPlaneR\fcontrolPlane\"\xfe\x02\n" + "\n" + "ConfigSpec\x12+\n" + "\x11discovery_enabled\x18\x01 \x01(\bR\x10discoveryEnabled\x12>\n" + "\x1bregistry_kubernetes_enabled\x18\x02 \x01(\bR\x19registryKubernetesEnabled\x128\n" + "\x18registry_service_enabled\x18\x03 \x01(\bR\x16registryServiceEnabled\x12)\n" + "\x10service_endpoint\x18\x04 \x01(\tR\x0fserviceEndpoint\x12:\n" + "\x19service_endpoint_insecure\x18\x05 \x01(\bR\x17serviceEndpointInsecure\x124\n" + "\x16service_encryption_key\x18\x06 \x01(\fR\x14serviceEncryptionKey\x12,\n" + "\x12service_cluster_id\x18\a \x01(\tR\x10serviceClusterId\"6\n" + "\fControlPlane\x12&\n" + "\x0fapi_server_port\x18\x01 \x01(\x03R\rapiServerPort\"'\n" + "\fIdentitySpec\x12\x17\n" + "\anode_id\x18\x01 \x01(\tR\x06nodeId\"L\n" + "\bInfoSpec\x12\x1d\n" + "\n" + "cluster_id\x18\x01 \x01(\tR\tclusterId\x12!\n" + "\fcluster_name\x18\x02 \x01(\tR\vclusterName\"\xad\x02\n" + "\x15KubeSpanAffiliateSpec\x12\x1d\n" + "\n" + "public_key\x18\x01 \x01(\tR\tpublicKey\x12'\n" + "\aaddress\x18\x02 \x01(\v2\r.common.NetIPR\aaddress\x12F\n" + "\x14additional_addresses\x18\x03 \x03(\v2\x13.common.NetIPPrefixR\x13additionalAddresses\x12/\n" + "\tendpoints\x18\x04 \x03(\v2\x11.common.NetIPPortR\tendpoints\x12S\n" + "\x1bexclude_advertised_networks\x18\x05 \x03(\v2\x13.common.NetIPPrefixR\x19excludeAdvertisedNetworks\"\xc2\x02\n" + "\n" + "MemberSpec\x12\x17\n" + "\anode_id\x18\x01 \x01(\tR\x06nodeId\x12+\n" + "\taddresses\x18\x02 \x03(\v2\r.common.NetIPR\taddresses\x12\x1a\n" + "\bhostname\x18\x03 \x01(\tR\bhostname\x12P\n" + "\fmachine_type\x18\x04 \x01(\x0e2-.talos.resource.definitions.enums.MachineTypeR\vmachineType\x12)\n" + "\x10operating_system\x18\x05 \x01(\tR\x0foperatingSystem\x12U\n" + "\rcontrol_plane\x18\x06 \x01(\v20.talos.resource.definitions.cluster.ControlPlaneR\fcontrolPlaneBx\n" + "*dev.talos.api.resource.definitions.clusterZJgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/clusterb\x06proto3" var ( file_resource_definitions_cluster_cluster_proto_rawDescOnce sync.Once file_resource_definitions_cluster_cluster_proto_rawDescData []byte ) func file_resource_definitions_cluster_cluster_proto_rawDescGZIP() []byte { file_resource_definitions_cluster_cluster_proto_rawDescOnce.Do(func() { file_resource_definitions_cluster_cluster_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_cluster_cluster_proto_rawDesc), len(file_resource_definitions_cluster_cluster_proto_rawDesc))) }) return file_resource_definitions_cluster_cluster_proto_rawDescData } var file_resource_definitions_cluster_cluster_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_resource_definitions_cluster_cluster_proto_goTypes = []any{ (*AffiliateSpec)(nil), // 0: talos.resource.definitions.cluster.AffiliateSpec (*ConfigSpec)(nil), // 1: talos.resource.definitions.cluster.ConfigSpec (*ControlPlane)(nil), // 2: talos.resource.definitions.cluster.ControlPlane (*IdentitySpec)(nil), // 3: talos.resource.definitions.cluster.IdentitySpec (*InfoSpec)(nil), // 4: talos.resource.definitions.cluster.InfoSpec (*KubeSpanAffiliateSpec)(nil), // 5: talos.resource.definitions.cluster.KubeSpanAffiliateSpec (*MemberSpec)(nil), // 6: talos.resource.definitions.cluster.MemberSpec (*common.NetIP)(nil), // 7: common.NetIP (enums.MachineType)(0), // 8: talos.resource.definitions.enums.MachineType (*common.NetIPPrefix)(nil), // 9: common.NetIPPrefix (*common.NetIPPort)(nil), // 10: common.NetIPPort } var file_resource_definitions_cluster_cluster_proto_depIdxs = []int32{ 7, // 0: talos.resource.definitions.cluster.AffiliateSpec.addresses:type_name -> common.NetIP 8, // 1: talos.resource.definitions.cluster.AffiliateSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType 5, // 2: talos.resource.definitions.cluster.AffiliateSpec.kube_span:type_name -> talos.resource.definitions.cluster.KubeSpanAffiliateSpec 2, // 3: talos.resource.definitions.cluster.AffiliateSpec.control_plane:type_name -> talos.resource.definitions.cluster.ControlPlane 7, // 4: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.address:type_name -> common.NetIP 9, // 5: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.additional_addresses:type_name -> common.NetIPPrefix 10, // 6: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.endpoints:type_name -> common.NetIPPort 9, // 7: talos.resource.definitions.cluster.KubeSpanAffiliateSpec.exclude_advertised_networks:type_name -> common.NetIPPrefix 7, // 8: talos.resource.definitions.cluster.MemberSpec.addresses:type_name -> common.NetIP 8, // 9: talos.resource.definitions.cluster.MemberSpec.machine_type:type_name -> talos.resource.definitions.enums.MachineType 2, // 10: talos.resource.definitions.cluster.MemberSpec.control_plane:type_name -> talos.resource.definitions.cluster.ControlPlane 11, // [11:11] is the sub-list for method output_type 11, // [11:11] is the sub-list for method input_type 11, // [11:11] is the sub-list for extension type_name 11, // [11:11] is the sub-list for extension extendee 0, // [0:11] is the sub-list for field type_name } func init() { file_resource_definitions_cluster_cluster_proto_init() } func file_resource_definitions_cluster_cluster_proto_init() { if File_resource_definitions_cluster_cluster_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_cluster_cluster_proto_rawDesc), len(file_resource_definitions_cluster_cluster_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_cluster_cluster_proto_goTypes, DependencyIndexes: file_resource_definitions_cluster_cluster_proto_depIdxs, MessageInfos: file_resource_definitions_cluster_cluster_proto_msgTypes, }.Build() File_resource_definitions_cluster_cluster_proto = out.File file_resource_definitions_cluster_cluster_proto_goTypes = nil file_resource_definitions_cluster_cluster_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/cluster/cluster_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/cluster/cluster.proto package cluster import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *AffiliateSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AffiliateSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AffiliateSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ControlPlane != nil { size, err := m.ControlPlane.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } if m.KubeSpan != nil { size, err := m.KubeSpan.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } if m.MachineType != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MachineType)) i-- dAtA[i] = 0x30 } if len(m.OperatingSystem) > 0 { i -= len(m.OperatingSystem) copy(dAtA[i:], m.OperatingSystem) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OperatingSystem))) i-- dAtA[i] = 0x2a } if len(m.Nodename) > 0 { i -= len(m.Nodename) copy(dAtA[i:], m.Nodename) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Nodename))) i-- dAtA[i] = 0x22 } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0x1a } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Addresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Addresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.NodeId) > 0 { i -= len(m.NodeId) copy(dAtA[i:], m.NodeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NodeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ServiceClusterId) > 0 { i -= len(m.ServiceClusterId) copy(dAtA[i:], m.ServiceClusterId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ServiceClusterId))) i-- dAtA[i] = 0x3a } if len(m.ServiceEncryptionKey) > 0 { i -= len(m.ServiceEncryptionKey) copy(dAtA[i:], m.ServiceEncryptionKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ServiceEncryptionKey))) i-- dAtA[i] = 0x32 } if m.ServiceEndpointInsecure { i-- if m.ServiceEndpointInsecure { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.ServiceEndpoint) > 0 { i -= len(m.ServiceEndpoint) copy(dAtA[i:], m.ServiceEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ServiceEndpoint))) i-- dAtA[i] = 0x22 } if m.RegistryServiceEnabled { i-- if m.RegistryServiceEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.RegistryKubernetesEnabled { i-- if m.RegistryKubernetesEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.DiscoveryEnabled { i-- if m.DiscoveryEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ControlPlane) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControlPlane) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControlPlane) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ApiServerPort != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ApiServerPort)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *IdentitySpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *IdentitySpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *IdentitySpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.NodeId) > 0 { i -= len(m.NodeId) copy(dAtA[i:], m.NodeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NodeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *InfoSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *InfoSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *InfoSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ClusterName) > 0 { i -= len(m.ClusterName) copy(dAtA[i:], m.ClusterName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterName))) i-- dAtA[i] = 0x12 } if len(m.ClusterId) > 0 { i -= len(m.ClusterId) copy(dAtA[i:], m.ClusterId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubeSpanAffiliateSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubeSpanAffiliateSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubeSpanAffiliateSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExcludeAdvertisedNetworks) > 0 { for iNdEx := len(m.ExcludeAdvertisedNetworks) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExcludeAdvertisedNetworks[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExcludeAdvertisedNetworks[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if len(m.Endpoints) > 0 { for iNdEx := len(m.Endpoints) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Endpoints[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoints[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } } if len(m.AdditionalAddresses) > 0 { for iNdEx := len(m.AdditionalAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AdditionalAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AdditionalAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } } if m.Address != nil { if vtmsg, ok := interface{}(m.Address).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Address) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if len(m.PublicKey) > 0 { i -= len(m.PublicKey) copy(dAtA[i:], m.PublicKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MemberSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemberSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemberSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ControlPlane != nil { size, err := m.ControlPlane.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if len(m.OperatingSystem) > 0 { i -= len(m.OperatingSystem) copy(dAtA[i:], m.OperatingSystem) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OperatingSystem))) i-- dAtA[i] = 0x2a } if m.MachineType != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MachineType)) i-- dAtA[i] = 0x20 } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0x1a } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Addresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Addresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.NodeId) > 0 { i -= len(m.NodeId) copy(dAtA[i:], m.NodeId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NodeId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AffiliateSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.NodeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Addresses) > 0 { for _, e := range m.Addresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Nodename) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.OperatingSystem) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MachineType != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MachineType)) } if m.KubeSpan != nil { l = m.KubeSpan.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ControlPlane != nil { l = m.ControlPlane.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.DiscoveryEnabled { n += 2 } if m.RegistryKubernetesEnabled { n += 2 } if m.RegistryServiceEnabled { n += 2 } l = len(m.ServiceEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ServiceEndpointInsecure { n += 2 } l = len(m.ServiceEncryptionKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ServiceClusterId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ControlPlane) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.ApiServerPort != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ApiServerPort)) } n += len(m.unknownFields) return n } func (m *IdentitySpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.NodeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *InfoSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ClusterId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ClusterName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KubeSpanAffiliateSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.PublicKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Address != nil { if size, ok := interface{}(m.Address).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Address) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AdditionalAddresses) > 0 { for _, e := range m.AdditionalAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Endpoints) > 0 { for _, e := range m.Endpoints { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExcludeAdvertisedNetworks) > 0 { for _, e := range m.ExcludeAdvertisedNetworks { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MemberSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.NodeId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Addresses) > 0 { for _, e := range m.Addresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MachineType != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MachineType)) } l = len(m.OperatingSystem) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ControlPlane != nil { l = m.ControlPlane.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *AffiliateSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AffiliateSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AffiliateSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NodeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NodeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.Addresses[len(m.Addresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Addresses[len(m.Addresses)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Nodename", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Nodename = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OperatingSystem", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OperatingSystem = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MachineType", wireType) } m.MachineType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MachineType |= enums.MachineType(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field KubeSpan", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.KubeSpan == nil { m.KubeSpan = &KubeSpanAffiliateSpec{} } if err := m.KubeSpan.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControlPlane", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ControlPlane == nil { m.ControlPlane = &ControlPlane{} } if err := m.ControlPlane.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DiscoveryEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DiscoveryEnabled = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryKubernetesEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RegistryKubernetesEnabled = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryServiceEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RegistryServiceEnabled = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ServiceEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceEndpointInsecure", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ServiceEndpointInsecure = bool(v != 0) case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceEncryptionKey", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ServiceEncryptionKey = append(m.ServiceEncryptionKey[:0], dAtA[iNdEx:postIndex]...) if m.ServiceEncryptionKey == nil { m.ServiceEncryptionKey = []byte{} } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ServiceClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ControlPlane) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControlPlane: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControlPlane: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ApiServerPort", wireType) } m.ApiServerPort = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ApiServerPort |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *IdentitySpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: IdentitySpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: IdentitySpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NodeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NodeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *InfoSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: InfoSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: InfoSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubeSpanAffiliateSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubeSpanAffiliateSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubeSpanAffiliateSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PublicKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Address == nil { m.Address = &common.NetIP{} } if unmarshal, ok := interface{}(m.Address).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Address); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdditionalAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdditionalAddresses = append(m.AdditionalAddresses, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.AdditionalAddresses[len(m.AdditionalAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AdditionalAddresses[len(m.AdditionalAddresses)-1]); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoints = append(m.Endpoints, &common.NetIPPort{}) if unmarshal, ok := interface{}(m.Endpoints[len(m.Endpoints)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoints[len(m.Endpoints)-1]); err != nil { return err } } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExcludeAdvertisedNetworks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExcludeAdvertisedNetworks = append(m.ExcludeAdvertisedNetworks, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.ExcludeAdvertisedNetworks[len(m.ExcludeAdvertisedNetworks)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExcludeAdvertisedNetworks[len(m.ExcludeAdvertisedNetworks)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MemberSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemberSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemberSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NodeId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NodeId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.Addresses[len(m.Addresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Addresses[len(m.Addresses)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MachineType", wireType) } m.MachineType = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MachineType |= enums.MachineType(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OperatingSystem", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OperatingSystem = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControlPlane", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ControlPlane == nil { m.ControlPlane = &ControlPlane{} } if err := m.ControlPlane.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/cri/cri.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/cri/cri.proto package cri import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ImageCacheConfigSpec represents the ImageCacheConfig. type ImageCacheConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Status enums.CriImageCacheStatus `protobuf:"varint,1,opt,name=status,proto3,enum=talos.resource.definitions.enums.CriImageCacheStatus" json:"status,omitempty"` Roots []string `protobuf:"bytes,2,rep,name=roots,proto3" json:"roots,omitempty"` CopyStatus enums.CriImageCacheCopyStatus `protobuf:"varint,3,opt,name=copy_status,json=copyStatus,proto3,enum=talos.resource.definitions.enums.CriImageCacheCopyStatus" json:"copy_status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageCacheConfigSpec) Reset() { *x = ImageCacheConfigSpec{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageCacheConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageCacheConfigSpec) ProtoMessage() {} func (x *ImageCacheConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageCacheConfigSpec.ProtoReflect.Descriptor instead. func (*ImageCacheConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{0} } func (x *ImageCacheConfigSpec) GetStatus() enums.CriImageCacheStatus { if x != nil { return x.Status } return enums.CriImageCacheStatus(0) } func (x *ImageCacheConfigSpec) GetRoots() []string { if x != nil { return x.Roots } return nil } func (x *ImageCacheConfigSpec) GetCopyStatus() enums.CriImageCacheCopyStatus { if x != nil { return x.CopyStatus } return enums.CriImageCacheCopyStatus(0) } // RegistriesConfigSpec describes status of rendered secrets. type RegistriesConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` RegistryMirrors map[string]*RegistryMirrorConfig `protobuf:"bytes,1,rep,name=registry_mirrors,json=registryMirrors,proto3" json:"registry_mirrors,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` RegistryAuths map[string]*RegistryAuthConfig `protobuf:"bytes,2,rep,name=registry_auths,json=registryAuths,proto3" json:"registry_auths,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` RegistryTlSs map[string]*RegistryTLSConfig `protobuf:"bytes,3,rep,name=registry_tl_ss,json=registryTlSs,proto3" json:"registry_tl_ss,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RegistriesConfigSpec) Reset() { *x = RegistriesConfigSpec{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RegistriesConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*RegistriesConfigSpec) ProtoMessage() {} func (x *RegistriesConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RegistriesConfigSpec.ProtoReflect.Descriptor instead. func (*RegistriesConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{1} } func (x *RegistriesConfigSpec) GetRegistryMirrors() map[string]*RegistryMirrorConfig { if x != nil { return x.RegistryMirrors } return nil } func (x *RegistriesConfigSpec) GetRegistryAuths() map[string]*RegistryAuthConfig { if x != nil { return x.RegistryAuths } return nil } func (x *RegistriesConfigSpec) GetRegistryTlSs() map[string]*RegistryTLSConfig { if x != nil { return x.RegistryTlSs } return nil } // RegistryAuthConfig specifies authentication configuration for a registry. type RegistryAuthConfig struct { state protoimpl.MessageState `protogen:"open.v1"` RegistryUsername string `protobuf:"bytes,1,opt,name=registry_username,json=registryUsername,proto3" json:"registry_username,omitempty"` RegistryPassword string `protobuf:"bytes,2,opt,name=registry_password,json=registryPassword,proto3" json:"registry_password,omitempty"` RegistryAuth string `protobuf:"bytes,3,opt,name=registry_auth,json=registryAuth,proto3" json:"registry_auth,omitempty"` RegistryIdentityToken string `protobuf:"bytes,4,opt,name=registry_identity_token,json=registryIdentityToken,proto3" json:"registry_identity_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RegistryAuthConfig) Reset() { *x = RegistryAuthConfig{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RegistryAuthConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RegistryAuthConfig) ProtoMessage() {} func (x *RegistryAuthConfig) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RegistryAuthConfig.ProtoReflect.Descriptor instead. func (*RegistryAuthConfig) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{2} } func (x *RegistryAuthConfig) GetRegistryUsername() string { if x != nil { return x.RegistryUsername } return "" } func (x *RegistryAuthConfig) GetRegistryPassword() string { if x != nil { return x.RegistryPassword } return "" } func (x *RegistryAuthConfig) GetRegistryAuth() string { if x != nil { return x.RegistryAuth } return "" } func (x *RegistryAuthConfig) GetRegistryIdentityToken() string { if x != nil { return x.RegistryIdentityToken } return "" } // RegistryEndpointConfig represents a single registry endpoint. type RegistryEndpointConfig struct { state protoimpl.MessageState `protogen:"open.v1"` EndpointEndpoint string `protobuf:"bytes,1,opt,name=endpoint_endpoint,json=endpointEndpoint,proto3" json:"endpoint_endpoint,omitempty"` EndpointOverridePath bool `protobuf:"varint,2,opt,name=endpoint_override_path,json=endpointOverridePath,proto3" json:"endpoint_override_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RegistryEndpointConfig) Reset() { *x = RegistryEndpointConfig{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RegistryEndpointConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RegistryEndpointConfig) ProtoMessage() {} func (x *RegistryEndpointConfig) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RegistryEndpointConfig.ProtoReflect.Descriptor instead. func (*RegistryEndpointConfig) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{3} } func (x *RegistryEndpointConfig) GetEndpointEndpoint() string { if x != nil { return x.EndpointEndpoint } return "" } func (x *RegistryEndpointConfig) GetEndpointOverridePath() bool { if x != nil { return x.EndpointOverridePath } return false } // RegistryMirrorConfig represents mirror configuration for a registry. type RegistryMirrorConfig struct { state protoimpl.MessageState `protogen:"open.v1"` MirrorEndpoints []*RegistryEndpointConfig `protobuf:"bytes,1,rep,name=mirror_endpoints,json=mirrorEndpoints,proto3" json:"mirror_endpoints,omitempty"` MirrorSkipFallback bool `protobuf:"varint,3,opt,name=mirror_skip_fallback,json=mirrorSkipFallback,proto3" json:"mirror_skip_fallback,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RegistryMirrorConfig) Reset() { *x = RegistryMirrorConfig{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RegistryMirrorConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RegistryMirrorConfig) ProtoMessage() {} func (x *RegistryMirrorConfig) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RegistryMirrorConfig.ProtoReflect.Descriptor instead. func (*RegistryMirrorConfig) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{4} } func (x *RegistryMirrorConfig) GetMirrorEndpoints() []*RegistryEndpointConfig { if x != nil { return x.MirrorEndpoints } return nil } func (x *RegistryMirrorConfig) GetMirrorSkipFallback() bool { if x != nil { return x.MirrorSkipFallback } return false } // RegistryTLSConfig specifies TLS config for HTTPS registries. type RegistryTLSConfig struct { state protoimpl.MessageState `protogen:"open.v1"` TlsClientIdentity *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=tls_client_identity,json=tlsClientIdentity,proto3" json:"tls_client_identity,omitempty"` Tlsca []byte `protobuf:"bytes,2,opt,name=tlsca,proto3" json:"tlsca,omitempty"` TlsInsecureSkipVerify bool `protobuf:"varint,3,opt,name=tls_insecure_skip_verify,json=tlsInsecureSkipVerify,proto3" json:"tls_insecure_skip_verify,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RegistryTLSConfig) Reset() { *x = RegistryTLSConfig{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RegistryTLSConfig) String() string { return protoimpl.X.MessageStringOf(x) } func (*RegistryTLSConfig) ProtoMessage() {} func (x *RegistryTLSConfig) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RegistryTLSConfig.ProtoReflect.Descriptor instead. func (*RegistryTLSConfig) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{5} } func (x *RegistryTLSConfig) GetTlsClientIdentity() *common.PEMEncodedCertificateAndKey { if x != nil { return x.TlsClientIdentity } return nil } func (x *RegistryTLSConfig) GetTlsca() []byte { if x != nil { return x.Tlsca } return nil } func (x *RegistryTLSConfig) GetTlsInsecureSkipVerify() bool { if x != nil { return x.TlsInsecureSkipVerify } return false } // SeccompProfileSpec represents the SeccompProfile. type SeccompProfileSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Value *structpb.Struct `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SeccompProfileSpec) Reset() { *x = SeccompProfileSpec{} mi := &file_resource_definitions_cri_cri_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SeccompProfileSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SeccompProfileSpec) ProtoMessage() {} func (x *SeccompProfileSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_cri_cri_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SeccompProfileSpec.ProtoReflect.Descriptor instead. func (*SeccompProfileSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_cri_cri_proto_rawDescGZIP(), []int{6} } func (x *SeccompProfileSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *SeccompProfileSpec) GetValue() *structpb.Struct { if x != nil { return x.Value } return nil } var File_resource_definitions_cri_cri_proto protoreflect.FileDescriptor const file_resource_definitions_cri_cri_proto_rawDesc = "" + "\n" + "\"resource/definitions/cri/cri.proto\x12\x1etalos.resource.definitions.cri\x1a\x13common/common.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a&resource/definitions/enums/enums.proto\"\xd7\x01\n" + "\x14ImageCacheConfigSpec\x12M\n" + "\x06status\x18\x01 \x01(\x0e25.talos.resource.definitions.enums.CriImageCacheStatusR\x06status\x12\x14\n" + "\x05roots\x18\x02 \x03(\tR\x05roots\x12Z\n" + "\vcopy_status\x18\x03 \x01(\x0e29.talos.resource.definitions.enums.CriImageCacheCopyStatusR\n" + "copyStatus\"\xce\x05\n" + "\x14RegistriesConfigSpec\x12t\n" + "\x10registry_mirrors\x18\x01 \x03(\v2I.talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntryR\x0fregistryMirrors\x12n\n" + "\x0eregistry_auths\x18\x02 \x03(\v2G.talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntryR\rregistryAuths\x12l\n" + "\x0eregistry_tl_ss\x18\x03 \x03(\v2F.talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntryR\fregistryTlSs\x1ax\n" + "\x14RegistryMirrorsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12J\n" + "\x05value\x18\x02 \x01(\v24.talos.resource.definitions.cri.RegistryMirrorConfigR\x05value:\x028\x01\x1at\n" + "\x12RegistryAuthsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12H\n" + "\x05value\x18\x02 \x01(\v22.talos.resource.definitions.cri.RegistryAuthConfigR\x05value:\x028\x01\x1ar\n" + "\x11RegistryTlSsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12G\n" + "\x05value\x18\x02 \x01(\v21.talos.resource.definitions.cri.RegistryTLSConfigR\x05value:\x028\x01\"\xcb\x01\n" + "\x12RegistryAuthConfig\x12+\n" + "\x11registry_username\x18\x01 \x01(\tR\x10registryUsername\x12+\n" + "\x11registry_password\x18\x02 \x01(\tR\x10registryPassword\x12#\n" + "\rregistry_auth\x18\x03 \x01(\tR\fregistryAuth\x126\n" + "\x17registry_identity_token\x18\x04 \x01(\tR\x15registryIdentityToken\"{\n" + "\x16RegistryEndpointConfig\x12+\n" + "\x11endpoint_endpoint\x18\x01 \x01(\tR\x10endpointEndpoint\x124\n" + "\x16endpoint_override_path\x18\x02 \x01(\bR\x14endpointOverridePath\"\xab\x01\n" + "\x14RegistryMirrorConfig\x12a\n" + "\x10mirror_endpoints\x18\x01 \x03(\v26.talos.resource.definitions.cri.RegistryEndpointConfigR\x0fmirrorEndpoints\x120\n" + "\x14mirror_skip_fallback\x18\x03 \x01(\bR\x12mirrorSkipFallback\"\xb7\x01\n" + "\x11RegistryTLSConfig\x12S\n" + "\x13tls_client_identity\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x11tlsClientIdentity\x12\x14\n" + "\x05tlsca\x18\x02 \x01(\fR\x05tlsca\x127\n" + "\x18tls_insecure_skip_verify\x18\x03 \x01(\bR\x15tlsInsecureSkipVerify\"W\n" + "\x12SeccompProfileSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12-\n" + "\x05value\x18\x02 \x01(\v2\x17.google.protobuf.StructR\x05valueBp\n" + "&dev.talos.api.resource.definitions.criZFgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/crib\x06proto3" var ( file_resource_definitions_cri_cri_proto_rawDescOnce sync.Once file_resource_definitions_cri_cri_proto_rawDescData []byte ) func file_resource_definitions_cri_cri_proto_rawDescGZIP() []byte { file_resource_definitions_cri_cri_proto_rawDescOnce.Do(func() { file_resource_definitions_cri_cri_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_cri_cri_proto_rawDesc), len(file_resource_definitions_cri_cri_proto_rawDesc))) }) return file_resource_definitions_cri_cri_proto_rawDescData } var file_resource_definitions_cri_cri_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_resource_definitions_cri_cri_proto_goTypes = []any{ (*ImageCacheConfigSpec)(nil), // 0: talos.resource.definitions.cri.ImageCacheConfigSpec (*RegistriesConfigSpec)(nil), // 1: talos.resource.definitions.cri.RegistriesConfigSpec (*RegistryAuthConfig)(nil), // 2: talos.resource.definitions.cri.RegistryAuthConfig (*RegistryEndpointConfig)(nil), // 3: talos.resource.definitions.cri.RegistryEndpointConfig (*RegistryMirrorConfig)(nil), // 4: talos.resource.definitions.cri.RegistryMirrorConfig (*RegistryTLSConfig)(nil), // 5: talos.resource.definitions.cri.RegistryTLSConfig (*SeccompProfileSpec)(nil), // 6: talos.resource.definitions.cri.SeccompProfileSpec nil, // 7: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntry nil, // 8: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntry nil, // 9: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntry (enums.CriImageCacheStatus)(0), // 10: talos.resource.definitions.enums.CriImageCacheStatus (enums.CriImageCacheCopyStatus)(0), // 11: talos.resource.definitions.enums.CriImageCacheCopyStatus (*common.PEMEncodedCertificateAndKey)(nil), // 12: common.PEMEncodedCertificateAndKey (*structpb.Struct)(nil), // 13: google.protobuf.Struct } var file_resource_definitions_cri_cri_proto_depIdxs = []int32{ 10, // 0: talos.resource.definitions.cri.ImageCacheConfigSpec.status:type_name -> talos.resource.definitions.enums.CriImageCacheStatus 11, // 1: talos.resource.definitions.cri.ImageCacheConfigSpec.copy_status:type_name -> talos.resource.definitions.enums.CriImageCacheCopyStatus 7, // 2: talos.resource.definitions.cri.RegistriesConfigSpec.registry_mirrors:type_name -> talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntry 8, // 3: talos.resource.definitions.cri.RegistriesConfigSpec.registry_auths:type_name -> talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntry 9, // 4: talos.resource.definitions.cri.RegistriesConfigSpec.registry_tl_ss:type_name -> talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntry 3, // 5: talos.resource.definitions.cri.RegistryMirrorConfig.mirror_endpoints:type_name -> talos.resource.definitions.cri.RegistryEndpointConfig 12, // 6: talos.resource.definitions.cri.RegistryTLSConfig.tls_client_identity:type_name -> common.PEMEncodedCertificateAndKey 13, // 7: talos.resource.definitions.cri.SeccompProfileSpec.value:type_name -> google.protobuf.Struct 4, // 8: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntry.value:type_name -> talos.resource.definitions.cri.RegistryMirrorConfig 2, // 9: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntry.value:type_name -> talos.resource.definitions.cri.RegistryAuthConfig 5, // 10: talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntry.value:type_name -> talos.resource.definitions.cri.RegistryTLSConfig 11, // [11:11] is the sub-list for method output_type 11, // [11:11] is the sub-list for method input_type 11, // [11:11] is the sub-list for extension type_name 11, // [11:11] is the sub-list for extension extendee 0, // [0:11] is the sub-list for field type_name } func init() { file_resource_definitions_cri_cri_proto_init() } func file_resource_definitions_cri_cri_proto_init() { if File_resource_definitions_cri_cri_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_cri_cri_proto_rawDesc), len(file_resource_definitions_cri_cri_proto_rawDesc)), NumEnums: 0, NumMessages: 10, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_cri_cri_proto_goTypes, DependencyIndexes: file_resource_definitions_cri_cri_proto_depIdxs, MessageInfos: file_resource_definitions_cri_cri_proto_msgTypes, }.Build() File_resource_definitions_cri_cri_proto = out.File file_resource_definitions_cri_cri_proto_goTypes = nil file_resource_definitions_cri_cri_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/cri/cri_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/cri/cri.proto package cri import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" structpb "github.com/planetscale/vtprotobuf/types/known/structpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb1 "google.golang.org/protobuf/types/known/structpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ImageCacheConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageCacheConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageCacheConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.CopyStatus != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CopyStatus)) i-- dAtA[i] = 0x18 } if len(m.Roots) > 0 { for iNdEx := len(m.Roots) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Roots[iNdEx]) copy(dAtA[i:], m.Roots[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Roots[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Status != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RegistriesConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RegistriesConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RegistriesConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.RegistryTlSs) > 0 { for k := range m.RegistryTlSs { v := m.RegistryTlSs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1a } } if len(m.RegistryAuths) > 0 { for k := range m.RegistryAuths { v := m.RegistryAuths[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if len(m.RegistryMirrors) > 0 { for k := range m.RegistryMirrors { v := m.RegistryMirrors[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RegistryAuthConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RegistryAuthConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RegistryAuthConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.RegistryIdentityToken) > 0 { i -= len(m.RegistryIdentityToken) copy(dAtA[i:], m.RegistryIdentityToken) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RegistryIdentityToken))) i-- dAtA[i] = 0x22 } if len(m.RegistryAuth) > 0 { i -= len(m.RegistryAuth) copy(dAtA[i:], m.RegistryAuth) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RegistryAuth))) i-- dAtA[i] = 0x1a } if len(m.RegistryPassword) > 0 { i -= len(m.RegistryPassword) copy(dAtA[i:], m.RegistryPassword) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RegistryPassword))) i-- dAtA[i] = 0x12 } if len(m.RegistryUsername) > 0 { i -= len(m.RegistryUsername) copy(dAtA[i:], m.RegistryUsername) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.RegistryUsername))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RegistryEndpointConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RegistryEndpointConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RegistryEndpointConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.EndpointOverridePath { i-- if m.EndpointOverridePath { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.EndpointEndpoint) > 0 { i -= len(m.EndpointEndpoint) copy(dAtA[i:], m.EndpointEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.EndpointEndpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *RegistryMirrorConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RegistryMirrorConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RegistryMirrorConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MirrorSkipFallback { i-- if m.MirrorSkipFallback { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.MirrorEndpoints) > 0 { for iNdEx := len(m.MirrorEndpoints) - 1; iNdEx >= 0; iNdEx-- { size, err := m.MirrorEndpoints[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RegistryTLSConfig) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RegistryTLSConfig) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RegistryTLSConfig) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TlsInsecureSkipVerify { i-- if m.TlsInsecureSkipVerify { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.Tlsca) > 0 { i -= len(m.Tlsca) copy(dAtA[i:], m.Tlsca) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Tlsca))) i-- dAtA[i] = 0x12 } if m.TlsClientIdentity != nil { if vtmsg, ok := interface{}(m.TlsClientIdentity).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.TlsClientIdentity) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SeccompProfileSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SeccompProfileSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SeccompProfileSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Value != nil { size, err := (*structpb.Struct)(m.Value).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageCacheConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Status != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Status)) } if len(m.Roots) > 0 { for _, s := range m.Roots { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.CopyStatus != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CopyStatus)) } n += len(m.unknownFields) return n } func (m *RegistriesConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.RegistryMirrors) > 0 { for k, v := range m.RegistryMirrors { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.RegistryAuths) > 0 { for k, v := range m.RegistryAuths { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.RegistryTlSs) > 0 { for k, v := range m.RegistryTlSs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } n += len(m.unknownFields) return n } func (m *RegistryAuthConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.RegistryUsername) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.RegistryPassword) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.RegistryAuth) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.RegistryIdentityToken) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *RegistryEndpointConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.EndpointEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EndpointOverridePath { n += 2 } n += len(m.unknownFields) return n } func (m *RegistryMirrorConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.MirrorEndpoints) > 0 { for _, e := range m.MirrorEndpoints { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.MirrorSkipFallback { n += 2 } n += len(m.unknownFields) return n } func (m *RegistryTLSConfig) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.TlsClientIdentity != nil { if size, ok := interface{}(m.TlsClientIdentity).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.TlsClientIdentity) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Tlsca) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.TlsInsecureSkipVerify { n += 2 } n += len(m.unknownFields) return n } func (m *SeccompProfileSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Value != nil { l = (*structpb.Struct)(m.Value).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageCacheConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageCacheConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageCacheConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= enums.CriImageCacheStatus(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Roots", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Roots = append(m.Roots, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CopyStatus", wireType) } m.CopyStatus = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CopyStatus |= enums.CriImageCacheCopyStatus(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RegistriesConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RegistriesConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RegistriesConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryMirrors", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.RegistryMirrors == nil { m.RegistryMirrors = make(map[string]*RegistryMirrorConfig) } var mapkey string var mapvalue *RegistryMirrorConfig for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &RegistryMirrorConfig{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.RegistryMirrors[mapkey] = mapvalue iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryAuths", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.RegistryAuths == nil { m.RegistryAuths = make(map[string]*RegistryAuthConfig) } var mapkey string var mapvalue *RegistryAuthConfig for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &RegistryAuthConfig{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.RegistryAuths[mapkey] = mapvalue iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryTlSs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.RegistryTlSs == nil { m.RegistryTlSs = make(map[string]*RegistryTLSConfig) } var mapkey string var mapvalue *RegistryTLSConfig for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &RegistryTLSConfig{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.RegistryTlSs[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RegistryAuthConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RegistryAuthConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RegistryAuthConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryUsername", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RegistryUsername = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryPassword", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RegistryPassword = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryAuth", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RegistryAuth = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field RegistryIdentityToken", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.RegistryIdentityToken = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RegistryEndpointConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RegistryEndpointConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RegistryEndpointConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EndpointEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.EndpointEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EndpointOverridePath", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.EndpointOverridePath = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RegistryMirrorConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RegistryMirrorConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RegistryMirrorConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MirrorEndpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MirrorEndpoints = append(m.MirrorEndpoints, &RegistryEndpointConfig{}) if err := m.MirrorEndpoints[len(m.MirrorEndpoints)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MirrorSkipFallback", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.MirrorSkipFallback = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RegistryTLSConfig) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RegistryTLSConfig: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RegistryTLSConfig: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TlsClientIdentity", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.TlsClientIdentity == nil { m.TlsClientIdentity = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.TlsClientIdentity).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.TlsClientIdentity); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tlsca", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Tlsca = append(m.Tlsca[:0], dAtA[iNdEx:postIndex]...) if m.Tlsca == nil { m.Tlsca = []byte{} } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TlsInsecureSkipVerify", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TlsInsecureSkipVerify = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SeccompProfileSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SeccompProfileSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SeccompProfileSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Value == nil { m.Value = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Value).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/enums/enums.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/enums/enums.proto package enums import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // RuntimeMachineStage describes the stage of the machine boot/run process. type RuntimeMachineStage int32 const ( RuntimeMachineStage_MACHINE_STAGE_UNKNOWN RuntimeMachineStage = 0 RuntimeMachineStage_MACHINE_STAGE_BOOTING RuntimeMachineStage = 1 RuntimeMachineStage_MACHINE_STAGE_INSTALLING RuntimeMachineStage = 2 RuntimeMachineStage_MACHINE_STAGE_MAINTENANCE RuntimeMachineStage = 3 RuntimeMachineStage_MACHINE_STAGE_RUNNING RuntimeMachineStage = 4 RuntimeMachineStage_MACHINE_STAGE_REBOOTING RuntimeMachineStage = 5 RuntimeMachineStage_MACHINE_STAGE_SHUTTING_DOWN RuntimeMachineStage = 6 RuntimeMachineStage_MACHINE_STAGE_RESETTING RuntimeMachineStage = 7 RuntimeMachineStage_MACHINE_STAGE_UPGRADING RuntimeMachineStage = 8 ) // Enum value maps for RuntimeMachineStage. var ( RuntimeMachineStage_name = map[int32]string{ 0: "MACHINE_STAGE_UNKNOWN", 1: "MACHINE_STAGE_BOOTING", 2: "MACHINE_STAGE_INSTALLING", 3: "MACHINE_STAGE_MAINTENANCE", 4: "MACHINE_STAGE_RUNNING", 5: "MACHINE_STAGE_REBOOTING", 6: "MACHINE_STAGE_SHUTTING_DOWN", 7: "MACHINE_STAGE_RESETTING", 8: "MACHINE_STAGE_UPGRADING", } RuntimeMachineStage_value = map[string]int32{ "MACHINE_STAGE_UNKNOWN": 0, "MACHINE_STAGE_BOOTING": 1, "MACHINE_STAGE_INSTALLING": 2, "MACHINE_STAGE_MAINTENANCE": 3, "MACHINE_STAGE_RUNNING": 4, "MACHINE_STAGE_REBOOTING": 5, "MACHINE_STAGE_SHUTTING_DOWN": 6, "MACHINE_STAGE_RESETTING": 7, "MACHINE_STAGE_UPGRADING": 8, } ) func (x RuntimeMachineStage) Enum() *RuntimeMachineStage { p := new(RuntimeMachineStage) *p = x return p } func (x RuntimeMachineStage) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (RuntimeMachineStage) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[0].Descriptor() } func (RuntimeMachineStage) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[0] } func (x RuntimeMachineStage) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use RuntimeMachineStage.Descriptor instead. func (RuntimeMachineStage) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{0} } // RuntimeSELinuxState describes the current SELinux status. type RuntimeSELinuxState int32 const ( RuntimeSELinuxState_SE_LINUX_STATE_DISABLED RuntimeSELinuxState = 0 RuntimeSELinuxState_SE_LINUX_STATE_PERMISSIVE RuntimeSELinuxState = 1 RuntimeSELinuxState_SE_LINUX_STATE_ENFORCING RuntimeSELinuxState = 2 ) // Enum value maps for RuntimeSELinuxState. var ( RuntimeSELinuxState_name = map[int32]string{ 0: "SE_LINUX_STATE_DISABLED", 1: "SE_LINUX_STATE_PERMISSIVE", 2: "SE_LINUX_STATE_ENFORCING", } RuntimeSELinuxState_value = map[string]int32{ "SE_LINUX_STATE_DISABLED": 0, "SE_LINUX_STATE_PERMISSIVE": 1, "SE_LINUX_STATE_ENFORCING": 2, } ) func (x RuntimeSELinuxState) Enum() *RuntimeSELinuxState { p := new(RuntimeSELinuxState) *p = x return p } func (x RuntimeSELinuxState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (RuntimeSELinuxState) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[1].Descriptor() } func (RuntimeSELinuxState) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[1] } func (x RuntimeSELinuxState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use RuntimeSELinuxState.Descriptor instead. func (RuntimeSELinuxState) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{1} } // RuntimeFIPSState describes the current FIPS status. type RuntimeFIPSState int32 const ( RuntimeFIPSState_FIPS_STATE_DISABLED RuntimeFIPSState = 0 RuntimeFIPSState_FIPS_STATE_ENABLED RuntimeFIPSState = 1 RuntimeFIPSState_FIPS_STATE_STRICT RuntimeFIPSState = 2 ) // Enum value maps for RuntimeFIPSState. var ( RuntimeFIPSState_name = map[int32]string{ 0: "FIPS_STATE_DISABLED", 1: "FIPS_STATE_ENABLED", 2: "FIPS_STATE_STRICT", } RuntimeFIPSState_value = map[string]int32{ "FIPS_STATE_DISABLED": 0, "FIPS_STATE_ENABLED": 1, "FIPS_STATE_STRICT": 2, } ) func (x RuntimeFIPSState) Enum() *RuntimeFIPSState { p := new(RuntimeFIPSState) *p = x return p } func (x RuntimeFIPSState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (RuntimeFIPSState) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[2].Descriptor() } func (RuntimeFIPSState) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[2] } func (x RuntimeFIPSState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use RuntimeFIPSState.Descriptor instead. func (RuntimeFIPSState) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{2} } // MachineType represents a machine type. type MachineType int32 const ( // TypeUnknown represents undefined node type, when there is no machine configuration yet. MachineType_TYPE_UNKNOWN MachineType = 0 // TypeInit type designates the first control plane node to come up. You can think of it like a bootstrap node. // This node will perform the initial steps to bootstrap the cluster -- generation of TLS assets, starting of the control plane, etc. MachineType_TYPE_INIT MachineType = 1 // TypeControlPlane designates the node as a control plane member. // This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler. MachineType_TYPE_CONTROL_PLANE MachineType = 2 // TypeWorker designates the node as a worker node. // This means it will be an available compute node for scheduling workloads. MachineType_TYPE_WORKER MachineType = 3 ) // Enum value maps for MachineType. var ( MachineType_name = map[int32]string{ 0: "TYPE_UNKNOWN", 1: "TYPE_INIT", 2: "TYPE_CONTROL_PLANE", 3: "TYPE_WORKER", } MachineType_value = map[string]int32{ "TYPE_UNKNOWN": 0, "TYPE_INIT": 1, "TYPE_CONTROL_PLANE": 2, "TYPE_WORKER": 3, } ) func (x MachineType) Enum() *MachineType { p := new(MachineType) *p = x return p } func (x MachineType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (MachineType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[3].Descriptor() } func (MachineType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[3] } func (x MachineType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use MachineType.Descriptor instead. func (MachineType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{3} } // NethelpersAddressFlag wraps IFF_* constants. type NethelpersAddressFlag int32 const ( NethelpersAddressFlag_NETHELPERS_ADDRESSFLAG_UNSPECIFIED NethelpersAddressFlag = 0 NethelpersAddressFlag_ADDRESS_TEMPORARY NethelpersAddressFlag = 1 NethelpersAddressFlag_ADDRESS_NO_DAD NethelpersAddressFlag = 2 NethelpersAddressFlag_ADDRESS_OPTIMISTIC NethelpersAddressFlag = 4 NethelpersAddressFlag_ADDRESS_DAD_FAILED NethelpersAddressFlag = 8 NethelpersAddressFlag_ADDRESS_HOME NethelpersAddressFlag = 16 NethelpersAddressFlag_ADDRESS_DEPRECATED NethelpersAddressFlag = 32 NethelpersAddressFlag_ADDRESS_TENTATIVE NethelpersAddressFlag = 64 NethelpersAddressFlag_ADDRESS_PERMANENT NethelpersAddressFlag = 128 NethelpersAddressFlag_ADDRESS_MANAGEMENT_TEMP NethelpersAddressFlag = 256 NethelpersAddressFlag_ADDRESS_NO_PREFIX_ROUTE NethelpersAddressFlag = 512 NethelpersAddressFlag_ADDRESS_MC_AUTO_JOIN NethelpersAddressFlag = 1024 NethelpersAddressFlag_ADDRESS_STABLE_PRIVACY NethelpersAddressFlag = 2048 ) // Enum value maps for NethelpersAddressFlag. var ( NethelpersAddressFlag_name = map[int32]string{ 0: "NETHELPERS_ADDRESSFLAG_UNSPECIFIED", 1: "ADDRESS_TEMPORARY", 2: "ADDRESS_NO_DAD", 4: "ADDRESS_OPTIMISTIC", 8: "ADDRESS_DAD_FAILED", 16: "ADDRESS_HOME", 32: "ADDRESS_DEPRECATED", 64: "ADDRESS_TENTATIVE", 128: "ADDRESS_PERMANENT", 256: "ADDRESS_MANAGEMENT_TEMP", 512: "ADDRESS_NO_PREFIX_ROUTE", 1024: "ADDRESS_MC_AUTO_JOIN", 2048: "ADDRESS_STABLE_PRIVACY", } NethelpersAddressFlag_value = map[string]int32{ "NETHELPERS_ADDRESSFLAG_UNSPECIFIED": 0, "ADDRESS_TEMPORARY": 1, "ADDRESS_NO_DAD": 2, "ADDRESS_OPTIMISTIC": 4, "ADDRESS_DAD_FAILED": 8, "ADDRESS_HOME": 16, "ADDRESS_DEPRECATED": 32, "ADDRESS_TENTATIVE": 64, "ADDRESS_PERMANENT": 128, "ADDRESS_MANAGEMENT_TEMP": 256, "ADDRESS_NO_PREFIX_ROUTE": 512, "ADDRESS_MC_AUTO_JOIN": 1024, "ADDRESS_STABLE_PRIVACY": 2048, } ) func (x NethelpersAddressFlag) Enum() *NethelpersAddressFlag { p := new(NethelpersAddressFlag) *p = x return p } func (x NethelpersAddressFlag) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersAddressFlag) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[4].Descriptor() } func (NethelpersAddressFlag) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[4] } func (x NethelpersAddressFlag) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersAddressFlag.Descriptor instead. func (NethelpersAddressFlag) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{4} } // NethelpersAddressSortAlgorithm is an internal address sorting algorithm. type NethelpersAddressSortAlgorithm int32 const ( NethelpersAddressSortAlgorithm_ADDRESS_SORT_ALGORITHM_V1 NethelpersAddressSortAlgorithm = 0 NethelpersAddressSortAlgorithm_ADDRESS_SORT_ALGORITHM_V2 NethelpersAddressSortAlgorithm = 1 ) // Enum value maps for NethelpersAddressSortAlgorithm. var ( NethelpersAddressSortAlgorithm_name = map[int32]string{ 0: "ADDRESS_SORT_ALGORITHM_V1", 1: "ADDRESS_SORT_ALGORITHM_V2", } NethelpersAddressSortAlgorithm_value = map[string]int32{ "ADDRESS_SORT_ALGORITHM_V1": 0, "ADDRESS_SORT_ALGORITHM_V2": 1, } ) func (x NethelpersAddressSortAlgorithm) Enum() *NethelpersAddressSortAlgorithm { p := new(NethelpersAddressSortAlgorithm) *p = x return p } func (x NethelpersAddressSortAlgorithm) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersAddressSortAlgorithm) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[5].Descriptor() } func (NethelpersAddressSortAlgorithm) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[5] } func (x NethelpersAddressSortAlgorithm) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersAddressSortAlgorithm.Descriptor instead. func (NethelpersAddressSortAlgorithm) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{5} } // NethelpersADLACPActive is ADLACPActive. type NethelpersADLACPActive int32 const ( NethelpersADLACPActive_ADLACP_ACTIVE_OFF NethelpersADLACPActive = 0 NethelpersADLACPActive_ADLACP_ACTIVE_ON NethelpersADLACPActive = 1 ) // Enum value maps for NethelpersADLACPActive. var ( NethelpersADLACPActive_name = map[int32]string{ 0: "ADLACP_ACTIVE_OFF", 1: "ADLACP_ACTIVE_ON", } NethelpersADLACPActive_value = map[string]int32{ "ADLACP_ACTIVE_OFF": 0, "ADLACP_ACTIVE_ON": 1, } ) func (x NethelpersADLACPActive) Enum() *NethelpersADLACPActive { p := new(NethelpersADLACPActive) *p = x return p } func (x NethelpersADLACPActive) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersADLACPActive) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[6].Descriptor() } func (NethelpersADLACPActive) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[6] } func (x NethelpersADLACPActive) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersADLACPActive.Descriptor instead. func (NethelpersADLACPActive) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{6} } // NethelpersADSelect is ADSelect. type NethelpersADSelect int32 const ( NethelpersADSelect_AD_SELECT_STABLE NethelpersADSelect = 0 NethelpersADSelect_AD_SELECT_BANDWIDTH NethelpersADSelect = 1 NethelpersADSelect_AD_SELECT_COUNT NethelpersADSelect = 2 ) // Enum value maps for NethelpersADSelect. var ( NethelpersADSelect_name = map[int32]string{ 0: "AD_SELECT_STABLE", 1: "AD_SELECT_BANDWIDTH", 2: "AD_SELECT_COUNT", } NethelpersADSelect_value = map[string]int32{ "AD_SELECT_STABLE": 0, "AD_SELECT_BANDWIDTH": 1, "AD_SELECT_COUNT": 2, } ) func (x NethelpersADSelect) Enum() *NethelpersADSelect { p := new(NethelpersADSelect) *p = x return p } func (x NethelpersADSelect) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersADSelect) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[7].Descriptor() } func (NethelpersADSelect) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[7] } func (x NethelpersADSelect) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersADSelect.Descriptor instead. func (NethelpersADSelect) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{7} } // NethelpersARPAllTargets is an ARP targets mode. type NethelpersARPAllTargets int32 const ( NethelpersARPAllTargets_ARP_ALL_TARGETS_ANY NethelpersARPAllTargets = 0 NethelpersARPAllTargets_ARP_ALL_TARGETS_ALL NethelpersARPAllTargets = 1 ) // Enum value maps for NethelpersARPAllTargets. var ( NethelpersARPAllTargets_name = map[int32]string{ 0: "ARP_ALL_TARGETS_ANY", 1: "ARP_ALL_TARGETS_ALL", } NethelpersARPAllTargets_value = map[string]int32{ "ARP_ALL_TARGETS_ANY": 0, "ARP_ALL_TARGETS_ALL": 1, } ) func (x NethelpersARPAllTargets) Enum() *NethelpersARPAllTargets { p := new(NethelpersARPAllTargets) *p = x return p } func (x NethelpersARPAllTargets) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersARPAllTargets) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[8].Descriptor() } func (NethelpersARPAllTargets) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[8] } func (x NethelpersARPAllTargets) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersARPAllTargets.Descriptor instead. func (NethelpersARPAllTargets) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{8} } // NethelpersARPValidate is an ARP Validation mode. type NethelpersARPValidate int32 const ( NethelpersARPValidate_ARP_VALIDATE_NONE NethelpersARPValidate = 0 NethelpersARPValidate_ARP_VALIDATE_ACTIVE NethelpersARPValidate = 1 NethelpersARPValidate_ARP_VALIDATE_BACKUP NethelpersARPValidate = 2 NethelpersARPValidate_ARP_VALIDATE_ALL NethelpersARPValidate = 3 NethelpersARPValidate_ARP_VALIDATE_FILTER NethelpersARPValidate = 4 NethelpersARPValidate_ARP_VALIDATE_FILTER_ACTIVE NethelpersARPValidate = 5 NethelpersARPValidate_ARP_VALIDATE_FILTER_BACKUP NethelpersARPValidate = 6 ) // Enum value maps for NethelpersARPValidate. var ( NethelpersARPValidate_name = map[int32]string{ 0: "ARP_VALIDATE_NONE", 1: "ARP_VALIDATE_ACTIVE", 2: "ARP_VALIDATE_BACKUP", 3: "ARP_VALIDATE_ALL", 4: "ARP_VALIDATE_FILTER", 5: "ARP_VALIDATE_FILTER_ACTIVE", 6: "ARP_VALIDATE_FILTER_BACKUP", } NethelpersARPValidate_value = map[string]int32{ "ARP_VALIDATE_NONE": 0, "ARP_VALIDATE_ACTIVE": 1, "ARP_VALIDATE_BACKUP": 2, "ARP_VALIDATE_ALL": 3, "ARP_VALIDATE_FILTER": 4, "ARP_VALIDATE_FILTER_ACTIVE": 5, "ARP_VALIDATE_FILTER_BACKUP": 6, } ) func (x NethelpersARPValidate) Enum() *NethelpersARPValidate { p := new(NethelpersARPValidate) *p = x return p } func (x NethelpersARPValidate) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersARPValidate) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[9].Descriptor() } func (NethelpersARPValidate) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[9] } func (x NethelpersARPValidate) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersARPValidate.Descriptor instead. func (NethelpersARPValidate) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{9} } // NethelpersAutoHostnameKind is a kind of automatically generated hostname. type NethelpersAutoHostnameKind int32 const ( NethelpersAutoHostnameKind_AUTO_HOSTNAME_KIND_OFF NethelpersAutoHostnameKind = 0 NethelpersAutoHostnameKind_AUTO_HOSTNAME_KIND_ADDR NethelpersAutoHostnameKind = 1 NethelpersAutoHostnameKind_AUTO_HOSTNAME_KIND_STABLE NethelpersAutoHostnameKind = 2 ) // Enum value maps for NethelpersAutoHostnameKind. var ( NethelpersAutoHostnameKind_name = map[int32]string{ 0: "AUTO_HOSTNAME_KIND_OFF", 1: "AUTO_HOSTNAME_KIND_ADDR", 2: "AUTO_HOSTNAME_KIND_STABLE", } NethelpersAutoHostnameKind_value = map[string]int32{ "AUTO_HOSTNAME_KIND_OFF": 0, "AUTO_HOSTNAME_KIND_ADDR": 1, "AUTO_HOSTNAME_KIND_STABLE": 2, } ) func (x NethelpersAutoHostnameKind) Enum() *NethelpersAutoHostnameKind { p := new(NethelpersAutoHostnameKind) *p = x return p } func (x NethelpersAutoHostnameKind) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersAutoHostnameKind) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[10].Descriptor() } func (NethelpersAutoHostnameKind) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[10] } func (x NethelpersAutoHostnameKind) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersAutoHostnameKind.Descriptor instead. func (NethelpersAutoHostnameKind) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{10} } // NethelpersBondMode is a bond mode. type NethelpersBondMode int32 const ( NethelpersBondMode_BOND_MODE_ROUNDROBIN NethelpersBondMode = 0 NethelpersBondMode_BOND_MODE_ACTIVE_BACKUP NethelpersBondMode = 1 NethelpersBondMode_BOND_MODE_XOR NethelpersBondMode = 2 NethelpersBondMode_BOND_MODE_BROADCAST NethelpersBondMode = 3 NethelpersBondMode_BOND_MODE8023_AD NethelpersBondMode = 4 NethelpersBondMode_BOND_MODE_TLB NethelpersBondMode = 5 NethelpersBondMode_BOND_MODE_ALB NethelpersBondMode = 6 ) // Enum value maps for NethelpersBondMode. var ( NethelpersBondMode_name = map[int32]string{ 0: "BOND_MODE_ROUNDROBIN", 1: "BOND_MODE_ACTIVE_BACKUP", 2: "BOND_MODE_XOR", 3: "BOND_MODE_BROADCAST", 4: "BOND_MODE8023_AD", 5: "BOND_MODE_TLB", 6: "BOND_MODE_ALB", } NethelpersBondMode_value = map[string]int32{ "BOND_MODE_ROUNDROBIN": 0, "BOND_MODE_ACTIVE_BACKUP": 1, "BOND_MODE_XOR": 2, "BOND_MODE_BROADCAST": 3, "BOND_MODE8023_AD": 4, "BOND_MODE_TLB": 5, "BOND_MODE_ALB": 6, } ) func (x NethelpersBondMode) Enum() *NethelpersBondMode { p := new(NethelpersBondMode) *p = x return p } func (x NethelpersBondMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersBondMode) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[11].Descriptor() } func (NethelpersBondMode) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[11] } func (x NethelpersBondMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersBondMode.Descriptor instead. func (NethelpersBondMode) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{11} } // NethelpersBondXmitHashPolicy is a bond hash policy. type NethelpersBondXmitHashPolicy int32 const ( NethelpersBondXmitHashPolicy_BOND_XMIT_POLICY_LAYER2 NethelpersBondXmitHashPolicy = 0 NethelpersBondXmitHashPolicy_BOND_XMIT_POLICY_LAYER34 NethelpersBondXmitHashPolicy = 1 NethelpersBondXmitHashPolicy_BOND_XMIT_POLICY_LAYER23 NethelpersBondXmitHashPolicy = 2 NethelpersBondXmitHashPolicy_BOND_XMIT_POLICY_ENCAP23 NethelpersBondXmitHashPolicy = 3 NethelpersBondXmitHashPolicy_BOND_XMIT_POLICY_ENCAP34 NethelpersBondXmitHashPolicy = 4 ) // Enum value maps for NethelpersBondXmitHashPolicy. var ( NethelpersBondXmitHashPolicy_name = map[int32]string{ 0: "BOND_XMIT_POLICY_LAYER2", 1: "BOND_XMIT_POLICY_LAYER34", 2: "BOND_XMIT_POLICY_LAYER23", 3: "BOND_XMIT_POLICY_ENCAP23", 4: "BOND_XMIT_POLICY_ENCAP34", } NethelpersBondXmitHashPolicy_value = map[string]int32{ "BOND_XMIT_POLICY_LAYER2": 0, "BOND_XMIT_POLICY_LAYER34": 1, "BOND_XMIT_POLICY_LAYER23": 2, "BOND_XMIT_POLICY_ENCAP23": 3, "BOND_XMIT_POLICY_ENCAP34": 4, } ) func (x NethelpersBondXmitHashPolicy) Enum() *NethelpersBondXmitHashPolicy { p := new(NethelpersBondXmitHashPolicy) *p = x return p } func (x NethelpersBondXmitHashPolicy) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersBondXmitHashPolicy) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[12].Descriptor() } func (NethelpersBondXmitHashPolicy) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[12] } func (x NethelpersBondXmitHashPolicy) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersBondXmitHashPolicy.Descriptor instead. func (NethelpersBondXmitHashPolicy) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{12} } // NethelpersClientIdentifier is a DHCP client identifier. type NethelpersClientIdentifier int32 const ( NethelpersClientIdentifier_CLIENT_IDENTIFIER_NONE NethelpersClientIdentifier = 0 NethelpersClientIdentifier_CLIENT_IDENTIFIER_MAC NethelpersClientIdentifier = 1 NethelpersClientIdentifier_CLIENT_IDENTIFIER_DUID NethelpersClientIdentifier = 2 ) // Enum value maps for NethelpersClientIdentifier. var ( NethelpersClientIdentifier_name = map[int32]string{ 0: "CLIENT_IDENTIFIER_NONE", 1: "CLIENT_IDENTIFIER_MAC", 2: "CLIENT_IDENTIFIER_DUID", } NethelpersClientIdentifier_value = map[string]int32{ "CLIENT_IDENTIFIER_NONE": 0, "CLIENT_IDENTIFIER_MAC": 1, "CLIENT_IDENTIFIER_DUID": 2, } ) func (x NethelpersClientIdentifier) Enum() *NethelpersClientIdentifier { p := new(NethelpersClientIdentifier) *p = x return p } func (x NethelpersClientIdentifier) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersClientIdentifier) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[13].Descriptor() } func (NethelpersClientIdentifier) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[13] } func (x NethelpersClientIdentifier) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersClientIdentifier.Descriptor instead. func (NethelpersClientIdentifier) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{13} } // NethelpersConntrackState is a conntrack state. type NethelpersConntrackState int32 const ( NethelpersConntrackState_NETHELPERS_CONNTRACKSTATE_UNSPECIFIED NethelpersConntrackState = 0 NethelpersConntrackState_CONNTRACK_STATE_NEW NethelpersConntrackState = 8 NethelpersConntrackState_CONNTRACK_STATE_RELATED NethelpersConntrackState = 4 NethelpersConntrackState_CONNTRACK_STATE_ESTABLISHED NethelpersConntrackState = 2 NethelpersConntrackState_CONNTRACK_STATE_INVALID NethelpersConntrackState = 1 ) // Enum value maps for NethelpersConntrackState. var ( NethelpersConntrackState_name = map[int32]string{ 0: "NETHELPERS_CONNTRACKSTATE_UNSPECIFIED", 8: "CONNTRACK_STATE_NEW", 4: "CONNTRACK_STATE_RELATED", 2: "CONNTRACK_STATE_ESTABLISHED", 1: "CONNTRACK_STATE_INVALID", } NethelpersConntrackState_value = map[string]int32{ "NETHELPERS_CONNTRACKSTATE_UNSPECIFIED": 0, "CONNTRACK_STATE_NEW": 8, "CONNTRACK_STATE_RELATED": 4, "CONNTRACK_STATE_ESTABLISHED": 2, "CONNTRACK_STATE_INVALID": 1, } ) func (x NethelpersConntrackState) Enum() *NethelpersConntrackState { p := new(NethelpersConntrackState) *p = x return p } func (x NethelpersConntrackState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersConntrackState) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[14].Descriptor() } func (NethelpersConntrackState) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[14] } func (x NethelpersConntrackState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersConntrackState.Descriptor instead. func (NethelpersConntrackState) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{14} } // NethelpersDuplex wraps ethtool.Duplex for YAML marshaling. type NethelpersDuplex int32 const ( NethelpersDuplex_HALF NethelpersDuplex = 0 NethelpersDuplex_FULL NethelpersDuplex = 1 NethelpersDuplex_UNKNOWN NethelpersDuplex = 255 ) // Enum value maps for NethelpersDuplex. var ( NethelpersDuplex_name = map[int32]string{ 0: "HALF", 1: "FULL", 255: "UNKNOWN", } NethelpersDuplex_value = map[string]int32{ "HALF": 0, "FULL": 1, "UNKNOWN": 255, } ) func (x NethelpersDuplex) Enum() *NethelpersDuplex { p := new(NethelpersDuplex) *p = x return p } func (x NethelpersDuplex) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersDuplex) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[15].Descriptor() } func (NethelpersDuplex) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[15] } func (x NethelpersDuplex) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersDuplex.Descriptor instead. func (NethelpersDuplex) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{15} } // NethelpersFailOverMAC is a MAC failover mode. type NethelpersFailOverMAC int32 const ( NethelpersFailOverMAC_FAIL_OVER_MAC_NONE NethelpersFailOverMAC = 0 NethelpersFailOverMAC_FAIL_OVER_MAC_ACTIVE NethelpersFailOverMAC = 1 NethelpersFailOverMAC_FAIL_OVER_MAC_FOLLOW NethelpersFailOverMAC = 2 ) // Enum value maps for NethelpersFailOverMAC. var ( NethelpersFailOverMAC_name = map[int32]string{ 0: "FAIL_OVER_MAC_NONE", 1: "FAIL_OVER_MAC_ACTIVE", 2: "FAIL_OVER_MAC_FOLLOW", } NethelpersFailOverMAC_value = map[string]int32{ "FAIL_OVER_MAC_NONE": 0, "FAIL_OVER_MAC_ACTIVE": 1, "FAIL_OVER_MAC_FOLLOW": 2, } ) func (x NethelpersFailOverMAC) Enum() *NethelpersFailOverMAC { p := new(NethelpersFailOverMAC) *p = x return p } func (x NethelpersFailOverMAC) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersFailOverMAC) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[16].Descriptor() } func (NethelpersFailOverMAC) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[16] } func (x NethelpersFailOverMAC) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersFailOverMAC.Descriptor instead. func (NethelpersFailOverMAC) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{16} } // NethelpersFamily is a network family. type NethelpersFamily int32 const ( NethelpersFamily_NETHELPERS_FAMILY_UNSPECIFIED NethelpersFamily = 0 NethelpersFamily_FAMILY_INET4 NethelpersFamily = 2 NethelpersFamily_FAMILY_INET6 NethelpersFamily = 10 ) // Enum value maps for NethelpersFamily. var ( NethelpersFamily_name = map[int32]string{ 0: "NETHELPERS_FAMILY_UNSPECIFIED", 2: "FAMILY_INET4", 10: "FAMILY_INET6", } NethelpersFamily_value = map[string]int32{ "NETHELPERS_FAMILY_UNSPECIFIED": 0, "FAMILY_INET4": 2, "FAMILY_INET6": 10, } ) func (x NethelpersFamily) Enum() *NethelpersFamily { p := new(NethelpersFamily) *p = x return p } func (x NethelpersFamily) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersFamily) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[17].Descriptor() } func (NethelpersFamily) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[17] } func (x NethelpersFamily) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersFamily.Descriptor instead. func (NethelpersFamily) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{17} } // NethelpersICMPType is a ICMP packet type. type NethelpersICMPType int32 const ( NethelpersICMPType_NETHELPERS_ICMPTYPE_UNSPECIFIED NethelpersICMPType = 0 NethelpersICMPType_ICMP_TYPE_TIMESTAMP_REQUEST NethelpersICMPType = 13 NethelpersICMPType_ICMP_TYPE_TIMESTAMP_REPLY NethelpersICMPType = 14 NethelpersICMPType_ICMP_TYPE_ADDRESS_MASK_REQUEST NethelpersICMPType = 17 NethelpersICMPType_ICMP_TYPE_ADDRESS_MASK_REPLY NethelpersICMPType = 18 ) // Enum value maps for NethelpersICMPType. var ( NethelpersICMPType_name = map[int32]string{ 0: "NETHELPERS_ICMPTYPE_UNSPECIFIED", 13: "ICMP_TYPE_TIMESTAMP_REQUEST", 14: "ICMP_TYPE_TIMESTAMP_REPLY", 17: "ICMP_TYPE_ADDRESS_MASK_REQUEST", 18: "ICMP_TYPE_ADDRESS_MASK_REPLY", } NethelpersICMPType_value = map[string]int32{ "NETHELPERS_ICMPTYPE_UNSPECIFIED": 0, "ICMP_TYPE_TIMESTAMP_REQUEST": 13, "ICMP_TYPE_TIMESTAMP_REPLY": 14, "ICMP_TYPE_ADDRESS_MASK_REQUEST": 17, "ICMP_TYPE_ADDRESS_MASK_REPLY": 18, } ) func (x NethelpersICMPType) Enum() *NethelpersICMPType { p := new(NethelpersICMPType) *p = x return p } func (x NethelpersICMPType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersICMPType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[18].Descriptor() } func (NethelpersICMPType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[18] } func (x NethelpersICMPType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersICMPType.Descriptor instead. func (NethelpersICMPType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{18} } // NethelpersLACPRate is a LACP rate. type NethelpersLACPRate int32 const ( NethelpersLACPRate_LACP_RATE_SLOW NethelpersLACPRate = 0 NethelpersLACPRate_LACP_RATE_FAST NethelpersLACPRate = 1 ) // Enum value maps for NethelpersLACPRate. var ( NethelpersLACPRate_name = map[int32]string{ 0: "LACP_RATE_SLOW", 1: "LACP_RATE_FAST", } NethelpersLACPRate_value = map[string]int32{ "LACP_RATE_SLOW": 0, "LACP_RATE_FAST": 1, } ) func (x NethelpersLACPRate) Enum() *NethelpersLACPRate { p := new(NethelpersLACPRate) *p = x return p } func (x NethelpersLACPRate) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersLACPRate) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[19].Descriptor() } func (NethelpersLACPRate) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[19] } func (x NethelpersLACPRate) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersLACPRate.Descriptor instead. func (NethelpersLACPRate) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{19} } // NethelpersLinkType is a link type. type NethelpersLinkType int32 const ( NethelpersLinkType_LINK_NETROM NethelpersLinkType = 0 NethelpersLinkType_LINK_ETHER NethelpersLinkType = 1 NethelpersLinkType_LINK_EETHER NethelpersLinkType = 2 NethelpersLinkType_LINK_AX25 NethelpersLinkType = 3 NethelpersLinkType_LINK_PRONET NethelpersLinkType = 4 NethelpersLinkType_LINK_CHAOS NethelpersLinkType = 5 NethelpersLinkType_LINK_IEE802 NethelpersLinkType = 6 NethelpersLinkType_LINK_ARCNET NethelpersLinkType = 7 NethelpersLinkType_LINK_ATALK NethelpersLinkType = 8 NethelpersLinkType_LINK_DLCI NethelpersLinkType = 15 NethelpersLinkType_LINK_ATM NethelpersLinkType = 19 NethelpersLinkType_LINK_METRICOM NethelpersLinkType = 23 NethelpersLinkType_LINK_IEEE1394 NethelpersLinkType = 24 NethelpersLinkType_LINK_EUI64 NethelpersLinkType = 27 NethelpersLinkType_LINK_INFINIBAND NethelpersLinkType = 32 NethelpersLinkType_LINK_SLIP NethelpersLinkType = 256 NethelpersLinkType_LINK_CSLIP NethelpersLinkType = 257 NethelpersLinkType_LINK_SLIP6 NethelpersLinkType = 258 NethelpersLinkType_LINK_CSLIP6 NethelpersLinkType = 259 NethelpersLinkType_LINK_RSRVD NethelpersLinkType = 260 NethelpersLinkType_LINK_ADAPT NethelpersLinkType = 264 NethelpersLinkType_LINK_ROSE NethelpersLinkType = 270 NethelpersLinkType_LINK_X25 NethelpersLinkType = 271 NethelpersLinkType_LINK_HWX25 NethelpersLinkType = 272 NethelpersLinkType_LINK_CAN NethelpersLinkType = 280 NethelpersLinkType_LINK_PPP NethelpersLinkType = 512 NethelpersLinkType_LINK_CISCO NethelpersLinkType = 513 NethelpersLinkType_LINK_HDLC NethelpersLinkType = 513 NethelpersLinkType_LINK_LAPB NethelpersLinkType = 516 NethelpersLinkType_LINK_DDCMP NethelpersLinkType = 517 NethelpersLinkType_LINK_RAWHDLC NethelpersLinkType = 518 NethelpersLinkType_LINK_TUNNEL NethelpersLinkType = 768 NethelpersLinkType_LINK_TUNNEL6 NethelpersLinkType = 769 NethelpersLinkType_LINK_FRAD NethelpersLinkType = 770 NethelpersLinkType_LINK_SKIP NethelpersLinkType = 771 NethelpersLinkType_LINK_LOOPBCK NethelpersLinkType = 772 NethelpersLinkType_LINK_LOCALTLK NethelpersLinkType = 773 NethelpersLinkType_LINK_FDDI NethelpersLinkType = 774 NethelpersLinkType_LINK_BIF NethelpersLinkType = 775 NethelpersLinkType_LINK_SIT NethelpersLinkType = 776 NethelpersLinkType_LINK_IPDDP NethelpersLinkType = 777 NethelpersLinkType_LINK_IPGRE NethelpersLinkType = 778 NethelpersLinkType_LINK_PIMREG NethelpersLinkType = 779 NethelpersLinkType_LINK_HIPPI NethelpersLinkType = 780 NethelpersLinkType_LINK_ASH NethelpersLinkType = 781 NethelpersLinkType_LINK_ECONET NethelpersLinkType = 782 NethelpersLinkType_LINK_IRDA NethelpersLinkType = 783 NethelpersLinkType_LINK_FCPP NethelpersLinkType = 784 NethelpersLinkType_LINK_FCAL NethelpersLinkType = 785 NethelpersLinkType_LINK_FCPL NethelpersLinkType = 786 NethelpersLinkType_LINK_FCFABRIC NethelpersLinkType = 787 NethelpersLinkType_LINK_FCFABRIC1 NethelpersLinkType = 788 NethelpersLinkType_LINK_FCFABRIC2 NethelpersLinkType = 789 NethelpersLinkType_LINK_FCFABRIC3 NethelpersLinkType = 790 NethelpersLinkType_LINK_FCFABRIC4 NethelpersLinkType = 791 NethelpersLinkType_LINK_FCFABRIC5 NethelpersLinkType = 792 NethelpersLinkType_LINK_FCFABRIC6 NethelpersLinkType = 793 NethelpersLinkType_LINK_FCFABRIC7 NethelpersLinkType = 794 NethelpersLinkType_LINK_FCFABRIC8 NethelpersLinkType = 795 NethelpersLinkType_LINK_FCFABRIC9 NethelpersLinkType = 796 NethelpersLinkType_LINK_FCFABRIC10 NethelpersLinkType = 797 NethelpersLinkType_LINK_FCFABRIC11 NethelpersLinkType = 798 NethelpersLinkType_LINK_FCFABRIC12 NethelpersLinkType = 799 NethelpersLinkType_LINK_IEE802TR NethelpersLinkType = 800 NethelpersLinkType_LINK_IEE80211 NethelpersLinkType = 801 NethelpersLinkType_LINK_IEE80211PRISM NethelpersLinkType = 802 NethelpersLinkType_LINK_IEE80211_RADIOTAP NethelpersLinkType = 803 NethelpersLinkType_LINK_IEE8021154 NethelpersLinkType = 804 NethelpersLinkType_LINK_IEE8021154MONITOR NethelpersLinkType = 805 NethelpersLinkType_LINK_PHONET NethelpersLinkType = 820 NethelpersLinkType_LINK_PHONETPIPE NethelpersLinkType = 821 NethelpersLinkType_LINK_CAIF NethelpersLinkType = 822 NethelpersLinkType_LINK_IP6GRE NethelpersLinkType = 823 NethelpersLinkType_LINK_NETLINK NethelpersLinkType = 824 NethelpersLinkType_LINK6_LOWPAN NethelpersLinkType = 825 NethelpersLinkType_LINK_VOID NethelpersLinkType = 65535 NethelpersLinkType_LINK_NONE NethelpersLinkType = 65534 ) // Enum value maps for NethelpersLinkType. var ( NethelpersLinkType_name = map[int32]string{ 0: "LINK_NETROM", 1: "LINK_ETHER", 2: "LINK_EETHER", 3: "LINK_AX25", 4: "LINK_PRONET", 5: "LINK_CHAOS", 6: "LINK_IEE802", 7: "LINK_ARCNET", 8: "LINK_ATALK", 15: "LINK_DLCI", 19: "LINK_ATM", 23: "LINK_METRICOM", 24: "LINK_IEEE1394", 27: "LINK_EUI64", 32: "LINK_INFINIBAND", 256: "LINK_SLIP", 257: "LINK_CSLIP", 258: "LINK_SLIP6", 259: "LINK_CSLIP6", 260: "LINK_RSRVD", 264: "LINK_ADAPT", 270: "LINK_ROSE", 271: "LINK_X25", 272: "LINK_HWX25", 280: "LINK_CAN", 512: "LINK_PPP", 513: "LINK_CISCO", // Duplicate value: 513: "LINK_HDLC", 516: "LINK_LAPB", 517: "LINK_DDCMP", 518: "LINK_RAWHDLC", 768: "LINK_TUNNEL", 769: "LINK_TUNNEL6", 770: "LINK_FRAD", 771: "LINK_SKIP", 772: "LINK_LOOPBCK", 773: "LINK_LOCALTLK", 774: "LINK_FDDI", 775: "LINK_BIF", 776: "LINK_SIT", 777: "LINK_IPDDP", 778: "LINK_IPGRE", 779: "LINK_PIMREG", 780: "LINK_HIPPI", 781: "LINK_ASH", 782: "LINK_ECONET", 783: "LINK_IRDA", 784: "LINK_FCPP", 785: "LINK_FCAL", 786: "LINK_FCPL", 787: "LINK_FCFABRIC", 788: "LINK_FCFABRIC1", 789: "LINK_FCFABRIC2", 790: "LINK_FCFABRIC3", 791: "LINK_FCFABRIC4", 792: "LINK_FCFABRIC5", 793: "LINK_FCFABRIC6", 794: "LINK_FCFABRIC7", 795: "LINK_FCFABRIC8", 796: "LINK_FCFABRIC9", 797: "LINK_FCFABRIC10", 798: "LINK_FCFABRIC11", 799: "LINK_FCFABRIC12", 800: "LINK_IEE802TR", 801: "LINK_IEE80211", 802: "LINK_IEE80211PRISM", 803: "LINK_IEE80211_RADIOTAP", 804: "LINK_IEE8021154", 805: "LINK_IEE8021154MONITOR", 820: "LINK_PHONET", 821: "LINK_PHONETPIPE", 822: "LINK_CAIF", 823: "LINK_IP6GRE", 824: "LINK_NETLINK", 825: "LINK6_LOWPAN", 65535: "LINK_VOID", 65534: "LINK_NONE", } NethelpersLinkType_value = map[string]int32{ "LINK_NETROM": 0, "LINK_ETHER": 1, "LINK_EETHER": 2, "LINK_AX25": 3, "LINK_PRONET": 4, "LINK_CHAOS": 5, "LINK_IEE802": 6, "LINK_ARCNET": 7, "LINK_ATALK": 8, "LINK_DLCI": 15, "LINK_ATM": 19, "LINK_METRICOM": 23, "LINK_IEEE1394": 24, "LINK_EUI64": 27, "LINK_INFINIBAND": 32, "LINK_SLIP": 256, "LINK_CSLIP": 257, "LINK_SLIP6": 258, "LINK_CSLIP6": 259, "LINK_RSRVD": 260, "LINK_ADAPT": 264, "LINK_ROSE": 270, "LINK_X25": 271, "LINK_HWX25": 272, "LINK_CAN": 280, "LINK_PPP": 512, "LINK_CISCO": 513, "LINK_HDLC": 513, "LINK_LAPB": 516, "LINK_DDCMP": 517, "LINK_RAWHDLC": 518, "LINK_TUNNEL": 768, "LINK_TUNNEL6": 769, "LINK_FRAD": 770, "LINK_SKIP": 771, "LINK_LOOPBCK": 772, "LINK_LOCALTLK": 773, "LINK_FDDI": 774, "LINK_BIF": 775, "LINK_SIT": 776, "LINK_IPDDP": 777, "LINK_IPGRE": 778, "LINK_PIMREG": 779, "LINK_HIPPI": 780, "LINK_ASH": 781, "LINK_ECONET": 782, "LINK_IRDA": 783, "LINK_FCPP": 784, "LINK_FCAL": 785, "LINK_FCPL": 786, "LINK_FCFABRIC": 787, "LINK_FCFABRIC1": 788, "LINK_FCFABRIC2": 789, "LINK_FCFABRIC3": 790, "LINK_FCFABRIC4": 791, "LINK_FCFABRIC5": 792, "LINK_FCFABRIC6": 793, "LINK_FCFABRIC7": 794, "LINK_FCFABRIC8": 795, "LINK_FCFABRIC9": 796, "LINK_FCFABRIC10": 797, "LINK_FCFABRIC11": 798, "LINK_FCFABRIC12": 799, "LINK_IEE802TR": 800, "LINK_IEE80211": 801, "LINK_IEE80211PRISM": 802, "LINK_IEE80211_RADIOTAP": 803, "LINK_IEE8021154": 804, "LINK_IEE8021154MONITOR": 805, "LINK_PHONET": 820, "LINK_PHONETPIPE": 821, "LINK_CAIF": 822, "LINK_IP6GRE": 823, "LINK_NETLINK": 824, "LINK6_LOWPAN": 825, "LINK_VOID": 65535, "LINK_NONE": 65534, } ) func (x NethelpersLinkType) Enum() *NethelpersLinkType { p := new(NethelpersLinkType) *p = x return p } func (x NethelpersLinkType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersLinkType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[20].Descriptor() } func (NethelpersLinkType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[20] } func (x NethelpersLinkType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersLinkType.Descriptor instead. func (NethelpersLinkType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{20} } // NethelpersMatchOperator is a netfilter match operator. type NethelpersMatchOperator int32 const ( NethelpersMatchOperator_OPERATOR_EQUAL NethelpersMatchOperator = 0 NethelpersMatchOperator_OPERATOR_NOT_EQUAL NethelpersMatchOperator = 1 ) // Enum value maps for NethelpersMatchOperator. var ( NethelpersMatchOperator_name = map[int32]string{ 0: "OPERATOR_EQUAL", 1: "OPERATOR_NOT_EQUAL", } NethelpersMatchOperator_value = map[string]int32{ "OPERATOR_EQUAL": 0, "OPERATOR_NOT_EQUAL": 1, } ) func (x NethelpersMatchOperator) Enum() *NethelpersMatchOperator { p := new(NethelpersMatchOperator) *p = x return p } func (x NethelpersMatchOperator) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersMatchOperator) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[21].Descriptor() } func (NethelpersMatchOperator) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[21] } func (x NethelpersMatchOperator) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersMatchOperator.Descriptor instead. func (NethelpersMatchOperator) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{21} } // NethelpersNfTablesChainHook wraps nftables.ChainHook for YAML marshaling. type NethelpersNfTablesChainHook int32 const ( NethelpersNfTablesChainHook_CHAIN_HOOK_PREROUTING NethelpersNfTablesChainHook = 0 NethelpersNfTablesChainHook_CHAIN_HOOK_INPUT NethelpersNfTablesChainHook = 1 NethelpersNfTablesChainHook_CHAIN_HOOK_FORWARD NethelpersNfTablesChainHook = 2 NethelpersNfTablesChainHook_CHAIN_HOOK_OUTPUT NethelpersNfTablesChainHook = 3 NethelpersNfTablesChainHook_CHAIN_HOOK_POSTROUTING NethelpersNfTablesChainHook = 4 ) // Enum value maps for NethelpersNfTablesChainHook. var ( NethelpersNfTablesChainHook_name = map[int32]string{ 0: "CHAIN_HOOK_PREROUTING", 1: "CHAIN_HOOK_INPUT", 2: "CHAIN_HOOK_FORWARD", 3: "CHAIN_HOOK_OUTPUT", 4: "CHAIN_HOOK_POSTROUTING", } NethelpersNfTablesChainHook_value = map[string]int32{ "CHAIN_HOOK_PREROUTING": 0, "CHAIN_HOOK_INPUT": 1, "CHAIN_HOOK_FORWARD": 2, "CHAIN_HOOK_OUTPUT": 3, "CHAIN_HOOK_POSTROUTING": 4, } ) func (x NethelpersNfTablesChainHook) Enum() *NethelpersNfTablesChainHook { p := new(NethelpersNfTablesChainHook) *p = x return p } func (x NethelpersNfTablesChainHook) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersNfTablesChainHook) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[22].Descriptor() } func (NethelpersNfTablesChainHook) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[22] } func (x NethelpersNfTablesChainHook) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersNfTablesChainHook.Descriptor instead. func (NethelpersNfTablesChainHook) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{22} } // NethelpersNfTablesChainPriority wraps nftables.ChainPriority for YAML marshaling. type NethelpersNfTablesChainPriority int32 const ( NethelpersNfTablesChainPriority_NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED NethelpersNfTablesChainPriority = 0 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_FIRST NethelpersNfTablesChainPriority = -2147483648 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_CONNTRACK_DEFRAG NethelpersNfTablesChainPriority = -400 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_RAW NethelpersNfTablesChainPriority = -300 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_SE_LINUX_FIRST NethelpersNfTablesChainPriority = -225 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_CONNTRACK NethelpersNfTablesChainPriority = -200 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_MANGLE NethelpersNfTablesChainPriority = -150 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_NAT_DEST NethelpersNfTablesChainPriority = -100 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_FILTER NethelpersNfTablesChainPriority = 0 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_SECURITY NethelpersNfTablesChainPriority = 50 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_NAT_SOURCE NethelpersNfTablesChainPriority = 100 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_SE_LINUX_LAST NethelpersNfTablesChainPriority = 225 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_CONNTRACK_HELPER NethelpersNfTablesChainPriority = 300 NethelpersNfTablesChainPriority_CHAIN_PRIORITY_LAST NethelpersNfTablesChainPriority = 2147483647 ) // Enum value maps for NethelpersNfTablesChainPriority. var ( NethelpersNfTablesChainPriority_name = map[int32]string{ 0: "NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED", -2147483648: "CHAIN_PRIORITY_FIRST", -400: "CHAIN_PRIORITY_CONNTRACK_DEFRAG", -300: "CHAIN_PRIORITY_RAW", -225: "CHAIN_PRIORITY_SE_LINUX_FIRST", -200: "CHAIN_PRIORITY_CONNTRACK", -150: "CHAIN_PRIORITY_MANGLE", -100: "CHAIN_PRIORITY_NAT_DEST", // Duplicate value: 0: "CHAIN_PRIORITY_FILTER", 50: "CHAIN_PRIORITY_SECURITY", 100: "CHAIN_PRIORITY_NAT_SOURCE", 225: "CHAIN_PRIORITY_SE_LINUX_LAST", 300: "CHAIN_PRIORITY_CONNTRACK_HELPER", 2147483647: "CHAIN_PRIORITY_LAST", } NethelpersNfTablesChainPriority_value = map[string]int32{ "NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED": 0, "CHAIN_PRIORITY_FIRST": -2147483648, "CHAIN_PRIORITY_CONNTRACK_DEFRAG": -400, "CHAIN_PRIORITY_RAW": -300, "CHAIN_PRIORITY_SE_LINUX_FIRST": -225, "CHAIN_PRIORITY_CONNTRACK": -200, "CHAIN_PRIORITY_MANGLE": -150, "CHAIN_PRIORITY_NAT_DEST": -100, "CHAIN_PRIORITY_FILTER": 0, "CHAIN_PRIORITY_SECURITY": 50, "CHAIN_PRIORITY_NAT_SOURCE": 100, "CHAIN_PRIORITY_SE_LINUX_LAST": 225, "CHAIN_PRIORITY_CONNTRACK_HELPER": 300, "CHAIN_PRIORITY_LAST": 2147483647, } ) func (x NethelpersNfTablesChainPriority) Enum() *NethelpersNfTablesChainPriority { p := new(NethelpersNfTablesChainPriority) *p = x return p } func (x NethelpersNfTablesChainPriority) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersNfTablesChainPriority) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[23].Descriptor() } func (NethelpersNfTablesChainPriority) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[23] } func (x NethelpersNfTablesChainPriority) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersNfTablesChainPriority.Descriptor instead. func (NethelpersNfTablesChainPriority) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{23} } // NethelpersNfTablesVerdict wraps nftables.Verdict for YAML marshaling. type NethelpersNfTablesVerdict int32 const ( NethelpersNfTablesVerdict_VERDICT_DROP NethelpersNfTablesVerdict = 0 NethelpersNfTablesVerdict_VERDICT_ACCEPT NethelpersNfTablesVerdict = 1 ) // Enum value maps for NethelpersNfTablesVerdict. var ( NethelpersNfTablesVerdict_name = map[int32]string{ 0: "VERDICT_DROP", 1: "VERDICT_ACCEPT", } NethelpersNfTablesVerdict_value = map[string]int32{ "VERDICT_DROP": 0, "VERDICT_ACCEPT": 1, } ) func (x NethelpersNfTablesVerdict) Enum() *NethelpersNfTablesVerdict { p := new(NethelpersNfTablesVerdict) *p = x return p } func (x NethelpersNfTablesVerdict) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersNfTablesVerdict) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[24].Descriptor() } func (NethelpersNfTablesVerdict) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[24] } func (x NethelpersNfTablesVerdict) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersNfTablesVerdict.Descriptor instead. func (NethelpersNfTablesVerdict) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{24} } // NethelpersOperationalState wraps rtnetlink.OperationalState for YAML marshaling. type NethelpersOperationalState int32 const ( NethelpersOperationalState_OPER_STATE_UNKNOWN NethelpersOperationalState = 0 NethelpersOperationalState_OPER_STATE_NOT_PRESENT NethelpersOperationalState = 1 NethelpersOperationalState_OPER_STATE_DOWN NethelpersOperationalState = 2 NethelpersOperationalState_OPER_STATE_LOWER_LAYER_DOWN NethelpersOperationalState = 3 NethelpersOperationalState_OPER_STATE_TESTING NethelpersOperationalState = 4 NethelpersOperationalState_OPER_STATE_DORMANT NethelpersOperationalState = 5 NethelpersOperationalState_OPER_STATE_UP NethelpersOperationalState = 6 ) // Enum value maps for NethelpersOperationalState. var ( NethelpersOperationalState_name = map[int32]string{ 0: "OPER_STATE_UNKNOWN", 1: "OPER_STATE_NOT_PRESENT", 2: "OPER_STATE_DOWN", 3: "OPER_STATE_LOWER_LAYER_DOWN", 4: "OPER_STATE_TESTING", 5: "OPER_STATE_DORMANT", 6: "OPER_STATE_UP", } NethelpersOperationalState_value = map[string]int32{ "OPER_STATE_UNKNOWN": 0, "OPER_STATE_NOT_PRESENT": 1, "OPER_STATE_DOWN": 2, "OPER_STATE_LOWER_LAYER_DOWN": 3, "OPER_STATE_TESTING": 4, "OPER_STATE_DORMANT": 5, "OPER_STATE_UP": 6, } ) func (x NethelpersOperationalState) Enum() *NethelpersOperationalState { p := new(NethelpersOperationalState) *p = x return p } func (x NethelpersOperationalState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersOperationalState) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[25].Descriptor() } func (NethelpersOperationalState) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[25] } func (x NethelpersOperationalState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersOperationalState.Descriptor instead. func (NethelpersOperationalState) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{25} } // NethelpersPort wraps ethtool.Port for YAML marshaling. type NethelpersPort int32 const ( NethelpersPort_TWISTED_PAIR NethelpersPort = 0 NethelpersPort_AUI NethelpersPort = 1 NethelpersPort_MII NethelpersPort = 2 NethelpersPort_FIBRE NethelpersPort = 3 NethelpersPort_BNC NethelpersPort = 4 NethelpersPort_DIRECT_ATTACH NethelpersPort = 5 NethelpersPort_NONE NethelpersPort = 239 NethelpersPort_OTHER NethelpersPort = 255 ) // Enum value maps for NethelpersPort. var ( NethelpersPort_name = map[int32]string{ 0: "TWISTED_PAIR", 1: "AUI", 2: "MII", 3: "FIBRE", 4: "BNC", 5: "DIRECT_ATTACH", 239: "NONE", 255: "OTHER", } NethelpersPort_value = map[string]int32{ "TWISTED_PAIR": 0, "AUI": 1, "MII": 2, "FIBRE": 3, "BNC": 4, "DIRECT_ATTACH": 5, "NONE": 239, "OTHER": 255, } ) func (x NethelpersPort) Enum() *NethelpersPort { p := new(NethelpersPort) *p = x return p } func (x NethelpersPort) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersPort) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[26].Descriptor() } func (NethelpersPort) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[26] } func (x NethelpersPort) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersPort.Descriptor instead. func (NethelpersPort) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{26} } // NethelpersPrimaryReselect is an ARP targets mode. type NethelpersPrimaryReselect int32 const ( NethelpersPrimaryReselect_PRIMARY_RESELECT_ALWAYS NethelpersPrimaryReselect = 0 NethelpersPrimaryReselect_PRIMARY_RESELECT_BETTER NethelpersPrimaryReselect = 1 NethelpersPrimaryReselect_PRIMARY_RESELECT_FAILURE NethelpersPrimaryReselect = 2 ) // Enum value maps for NethelpersPrimaryReselect. var ( NethelpersPrimaryReselect_name = map[int32]string{ 0: "PRIMARY_RESELECT_ALWAYS", 1: "PRIMARY_RESELECT_BETTER", 2: "PRIMARY_RESELECT_FAILURE", } NethelpersPrimaryReselect_value = map[string]int32{ "PRIMARY_RESELECT_ALWAYS": 0, "PRIMARY_RESELECT_BETTER": 1, "PRIMARY_RESELECT_FAILURE": 2, } ) func (x NethelpersPrimaryReselect) Enum() *NethelpersPrimaryReselect { p := new(NethelpersPrimaryReselect) *p = x return p } func (x NethelpersPrimaryReselect) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersPrimaryReselect) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[27].Descriptor() } func (NethelpersPrimaryReselect) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[27] } func (x NethelpersPrimaryReselect) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersPrimaryReselect.Descriptor instead. func (NethelpersPrimaryReselect) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{27} } // NethelpersProtocol is a inet protocol. type NethelpersProtocol int32 const ( NethelpersProtocol_NETHELPERS_PROTOCOL_UNSPECIFIED NethelpersProtocol = 0 NethelpersProtocol_PROTOCOL_ICMP NethelpersProtocol = 1 NethelpersProtocol_PROTOCOL_TCP NethelpersProtocol = 6 NethelpersProtocol_PROTOCOL_UDP NethelpersProtocol = 17 NethelpersProtocol_PROTOCOL_ICM_PV6 NethelpersProtocol = 58 ) // Enum value maps for NethelpersProtocol. var ( NethelpersProtocol_name = map[int32]string{ 0: "NETHELPERS_PROTOCOL_UNSPECIFIED", 1: "PROTOCOL_ICMP", 6: "PROTOCOL_TCP", 17: "PROTOCOL_UDP", 58: "PROTOCOL_ICM_PV6", } NethelpersProtocol_value = map[string]int32{ "NETHELPERS_PROTOCOL_UNSPECIFIED": 0, "PROTOCOL_ICMP": 1, "PROTOCOL_TCP": 6, "PROTOCOL_UDP": 17, "PROTOCOL_ICM_PV6": 58, } ) func (x NethelpersProtocol) Enum() *NethelpersProtocol { p := new(NethelpersProtocol) *p = x return p } func (x NethelpersProtocol) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersProtocol) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[28].Descriptor() } func (NethelpersProtocol) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[28] } func (x NethelpersProtocol) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersProtocol.Descriptor instead. func (NethelpersProtocol) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{28} } // NethelpersRouteFlag wraps RTM_F_* constants. type NethelpersRouteFlag int32 const ( NethelpersRouteFlag_NETHELPERS_ROUTEFLAG_UNSPECIFIED NethelpersRouteFlag = 0 NethelpersRouteFlag_ROUTE_NOTIFY NethelpersRouteFlag = 256 NethelpersRouteFlag_ROUTE_CLONED NethelpersRouteFlag = 512 NethelpersRouteFlag_ROUTE_EQUALIZE NethelpersRouteFlag = 1024 NethelpersRouteFlag_ROUTE_PREFIX NethelpersRouteFlag = 2048 NethelpersRouteFlag_ROUTE_LOOKUP_TABLE NethelpersRouteFlag = 4096 NethelpersRouteFlag_ROUTE_FIB_MATCH NethelpersRouteFlag = 8192 NethelpersRouteFlag_ROUTE_OFFLOAD NethelpersRouteFlag = 16384 NethelpersRouteFlag_ROUTE_TRAP NethelpersRouteFlag = 32768 ) // Enum value maps for NethelpersRouteFlag. var ( NethelpersRouteFlag_name = map[int32]string{ 0: "NETHELPERS_ROUTEFLAG_UNSPECIFIED", 256: "ROUTE_NOTIFY", 512: "ROUTE_CLONED", 1024: "ROUTE_EQUALIZE", 2048: "ROUTE_PREFIX", 4096: "ROUTE_LOOKUP_TABLE", 8192: "ROUTE_FIB_MATCH", 16384: "ROUTE_OFFLOAD", 32768: "ROUTE_TRAP", } NethelpersRouteFlag_value = map[string]int32{ "NETHELPERS_ROUTEFLAG_UNSPECIFIED": 0, "ROUTE_NOTIFY": 256, "ROUTE_CLONED": 512, "ROUTE_EQUALIZE": 1024, "ROUTE_PREFIX": 2048, "ROUTE_LOOKUP_TABLE": 4096, "ROUTE_FIB_MATCH": 8192, "ROUTE_OFFLOAD": 16384, "ROUTE_TRAP": 32768, } ) func (x NethelpersRouteFlag) Enum() *NethelpersRouteFlag { p := new(NethelpersRouteFlag) *p = x return p } func (x NethelpersRouteFlag) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersRouteFlag) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[29].Descriptor() } func (NethelpersRouteFlag) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[29] } func (x NethelpersRouteFlag) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersRouteFlag.Descriptor instead. func (NethelpersRouteFlag) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{29} } // NethelpersRouteProtocol is a routing protocol. type NethelpersRouteProtocol int32 const ( NethelpersRouteProtocol_PROTOCOL_UNSPEC NethelpersRouteProtocol = 0 NethelpersRouteProtocol_PROTOCOL_REDIRECT NethelpersRouteProtocol = 1 NethelpersRouteProtocol_PROTOCOL_KERNEL NethelpersRouteProtocol = 2 NethelpersRouteProtocol_PROTOCOL_BOOT NethelpersRouteProtocol = 3 NethelpersRouteProtocol_PROTOCOL_STATIC NethelpersRouteProtocol = 4 NethelpersRouteProtocol_PROTOCOL_RA NethelpersRouteProtocol = 9 NethelpersRouteProtocol_PROTOCOL_MRT NethelpersRouteProtocol = 10 NethelpersRouteProtocol_PROTOCOL_ZEBRA NethelpersRouteProtocol = 11 NethelpersRouteProtocol_PROTOCOL_BIRD NethelpersRouteProtocol = 12 NethelpersRouteProtocol_PROTOCOL_DNROUTED NethelpersRouteProtocol = 13 NethelpersRouteProtocol_PROTOCOL_XORP NethelpersRouteProtocol = 14 NethelpersRouteProtocol_PROTOCOL_NTK NethelpersRouteProtocol = 15 NethelpersRouteProtocol_PROTOCOL_DHCP NethelpersRouteProtocol = 16 NethelpersRouteProtocol_PROTOCOL_MRTD NethelpersRouteProtocol = 17 NethelpersRouteProtocol_PROTOCOL_KEEPALIVED NethelpersRouteProtocol = 18 NethelpersRouteProtocol_PROTOCOL_BABEL NethelpersRouteProtocol = 42 NethelpersRouteProtocol_PROTOCOL_OPENR NethelpersRouteProtocol = 99 NethelpersRouteProtocol_PROTOCOL_BGP NethelpersRouteProtocol = 186 NethelpersRouteProtocol_PROTOCOL_ISIS NethelpersRouteProtocol = 187 NethelpersRouteProtocol_PROTOCOL_OSPF NethelpersRouteProtocol = 188 NethelpersRouteProtocol_PROTOCOL_RIP NethelpersRouteProtocol = 189 NethelpersRouteProtocol_PROTOCOL_EIGRP NethelpersRouteProtocol = 192 ) // Enum value maps for NethelpersRouteProtocol. var ( NethelpersRouteProtocol_name = map[int32]string{ 0: "PROTOCOL_UNSPEC", 1: "PROTOCOL_REDIRECT", 2: "PROTOCOL_KERNEL", 3: "PROTOCOL_BOOT", 4: "PROTOCOL_STATIC", 9: "PROTOCOL_RA", 10: "PROTOCOL_MRT", 11: "PROTOCOL_ZEBRA", 12: "PROTOCOL_BIRD", 13: "PROTOCOL_DNROUTED", 14: "PROTOCOL_XORP", 15: "PROTOCOL_NTK", 16: "PROTOCOL_DHCP", 17: "PROTOCOL_MRTD", 18: "PROTOCOL_KEEPALIVED", 42: "PROTOCOL_BABEL", 99: "PROTOCOL_OPENR", 186: "PROTOCOL_BGP", 187: "PROTOCOL_ISIS", 188: "PROTOCOL_OSPF", 189: "PROTOCOL_RIP", 192: "PROTOCOL_EIGRP", } NethelpersRouteProtocol_value = map[string]int32{ "PROTOCOL_UNSPEC": 0, "PROTOCOL_REDIRECT": 1, "PROTOCOL_KERNEL": 2, "PROTOCOL_BOOT": 3, "PROTOCOL_STATIC": 4, "PROTOCOL_RA": 9, "PROTOCOL_MRT": 10, "PROTOCOL_ZEBRA": 11, "PROTOCOL_BIRD": 12, "PROTOCOL_DNROUTED": 13, "PROTOCOL_XORP": 14, "PROTOCOL_NTK": 15, "PROTOCOL_DHCP": 16, "PROTOCOL_MRTD": 17, "PROTOCOL_KEEPALIVED": 18, "PROTOCOL_BABEL": 42, "PROTOCOL_OPENR": 99, "PROTOCOL_BGP": 186, "PROTOCOL_ISIS": 187, "PROTOCOL_OSPF": 188, "PROTOCOL_RIP": 189, "PROTOCOL_EIGRP": 192, } ) func (x NethelpersRouteProtocol) Enum() *NethelpersRouteProtocol { p := new(NethelpersRouteProtocol) *p = x return p } func (x NethelpersRouteProtocol) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersRouteProtocol) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[30].Descriptor() } func (NethelpersRouteProtocol) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[30] } func (x NethelpersRouteProtocol) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersRouteProtocol.Descriptor instead. func (NethelpersRouteProtocol) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{30} } // NethelpersRouteType is a route type. type NethelpersRouteType int32 const ( NethelpersRouteType_TYPE_UNSPEC NethelpersRouteType = 0 NethelpersRouteType_TYPE_UNICAST NethelpersRouteType = 1 NethelpersRouteType_TYPE_LOCAL NethelpersRouteType = 2 NethelpersRouteType_TYPE_BROADCAST NethelpersRouteType = 3 NethelpersRouteType_TYPE_ANYCAST NethelpersRouteType = 4 NethelpersRouteType_TYPE_MULTICAST NethelpersRouteType = 5 NethelpersRouteType_TYPE_BLACKHOLE NethelpersRouteType = 6 NethelpersRouteType_TYPE_UNREACHABLE NethelpersRouteType = 7 NethelpersRouteType_TYPE_PROHIBIT NethelpersRouteType = 8 NethelpersRouteType_TYPE_THROW NethelpersRouteType = 9 NethelpersRouteType_TYPE_NAT NethelpersRouteType = 10 NethelpersRouteType_TYPE_X_RESOLVE NethelpersRouteType = 11 ) // Enum value maps for NethelpersRouteType. var ( NethelpersRouteType_name = map[int32]string{ 0: "TYPE_UNSPEC", 1: "TYPE_UNICAST", 2: "TYPE_LOCAL", 3: "TYPE_BROADCAST", 4: "TYPE_ANYCAST", 5: "TYPE_MULTICAST", 6: "TYPE_BLACKHOLE", 7: "TYPE_UNREACHABLE", 8: "TYPE_PROHIBIT", 9: "TYPE_THROW", 10: "TYPE_NAT", 11: "TYPE_X_RESOLVE", } NethelpersRouteType_value = map[string]int32{ "TYPE_UNSPEC": 0, "TYPE_UNICAST": 1, "TYPE_LOCAL": 2, "TYPE_BROADCAST": 3, "TYPE_ANYCAST": 4, "TYPE_MULTICAST": 5, "TYPE_BLACKHOLE": 6, "TYPE_UNREACHABLE": 7, "TYPE_PROHIBIT": 8, "TYPE_THROW": 9, "TYPE_NAT": 10, "TYPE_X_RESOLVE": 11, } ) func (x NethelpersRouteType) Enum() *NethelpersRouteType { p := new(NethelpersRouteType) *p = x return p } func (x NethelpersRouteType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersRouteType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[31].Descriptor() } func (NethelpersRouteType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[31] } func (x NethelpersRouteType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersRouteType.Descriptor instead. func (NethelpersRouteType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{31} } // NethelpersRoutingRuleAction is a routing rule action. type NethelpersRoutingRuleAction int32 const ( NethelpersRoutingRuleAction_ROUTING_RULE_ACTION_UNSPEC NethelpersRoutingRuleAction = 0 NethelpersRoutingRuleAction_ROUTING_RULE_ACTION_UNICAST NethelpersRoutingRuleAction = 1 NethelpersRoutingRuleAction_ROUTING_RULE_ACTION_BLACKHOLE NethelpersRoutingRuleAction = 6 NethelpersRoutingRuleAction_ROUTING_RULE_ACTION_UNREACHABLE NethelpersRoutingRuleAction = 7 NethelpersRoutingRuleAction_ROUTING_RULE_ACTION_PROHIBIT NethelpersRoutingRuleAction = 8 ) // Enum value maps for NethelpersRoutingRuleAction. var ( NethelpersRoutingRuleAction_name = map[int32]string{ 0: "ROUTING_RULE_ACTION_UNSPEC", 1: "ROUTING_RULE_ACTION_UNICAST", 6: "ROUTING_RULE_ACTION_BLACKHOLE", 7: "ROUTING_RULE_ACTION_UNREACHABLE", 8: "ROUTING_RULE_ACTION_PROHIBIT", } NethelpersRoutingRuleAction_value = map[string]int32{ "ROUTING_RULE_ACTION_UNSPEC": 0, "ROUTING_RULE_ACTION_UNICAST": 1, "ROUTING_RULE_ACTION_BLACKHOLE": 6, "ROUTING_RULE_ACTION_UNREACHABLE": 7, "ROUTING_RULE_ACTION_PROHIBIT": 8, } ) func (x NethelpersRoutingRuleAction) Enum() *NethelpersRoutingRuleAction { p := new(NethelpersRoutingRuleAction) *p = x return p } func (x NethelpersRoutingRuleAction) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersRoutingRuleAction) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[32].Descriptor() } func (NethelpersRoutingRuleAction) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[32] } func (x NethelpersRoutingRuleAction) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersRoutingRuleAction.Descriptor instead. func (NethelpersRoutingRuleAction) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{32} } // NethelpersRoutingTable is a routing table ID. type NethelpersRoutingTable int32 const ( NethelpersRoutingTable_TABLE_UNSPEC NethelpersRoutingTable = 0 NethelpersRoutingTable_TABLE1 NethelpersRoutingTable = 1 NethelpersRoutingTable_TABLE2 NethelpersRoutingTable = 2 NethelpersRoutingTable_TABLE3 NethelpersRoutingTable = 3 NethelpersRoutingTable_TABLE4 NethelpersRoutingTable = 4 NethelpersRoutingTable_TABLE5 NethelpersRoutingTable = 5 NethelpersRoutingTable_TABLE6 NethelpersRoutingTable = 6 NethelpersRoutingTable_TABLE7 NethelpersRoutingTable = 7 NethelpersRoutingTable_TABLE8 NethelpersRoutingTable = 8 NethelpersRoutingTable_TABLE9 NethelpersRoutingTable = 9 NethelpersRoutingTable_TABLE10 NethelpersRoutingTable = 10 NethelpersRoutingTable_TABLE11 NethelpersRoutingTable = 11 NethelpersRoutingTable_TABLE12 NethelpersRoutingTable = 12 NethelpersRoutingTable_TABLE13 NethelpersRoutingTable = 13 NethelpersRoutingTable_TABLE14 NethelpersRoutingTable = 14 NethelpersRoutingTable_TABLE15 NethelpersRoutingTable = 15 NethelpersRoutingTable_TABLE16 NethelpersRoutingTable = 16 NethelpersRoutingTable_TABLE17 NethelpersRoutingTable = 17 NethelpersRoutingTable_TABLE18 NethelpersRoutingTable = 18 NethelpersRoutingTable_TABLE19 NethelpersRoutingTable = 19 NethelpersRoutingTable_TABLE20 NethelpersRoutingTable = 20 NethelpersRoutingTable_TABLE21 NethelpersRoutingTable = 21 NethelpersRoutingTable_TABLE22 NethelpersRoutingTable = 22 NethelpersRoutingTable_TABLE23 NethelpersRoutingTable = 23 NethelpersRoutingTable_TABLE24 NethelpersRoutingTable = 24 NethelpersRoutingTable_TABLE25 NethelpersRoutingTable = 25 NethelpersRoutingTable_TABLE26 NethelpersRoutingTable = 26 NethelpersRoutingTable_TABLE27 NethelpersRoutingTable = 27 NethelpersRoutingTable_TABLE28 NethelpersRoutingTable = 28 NethelpersRoutingTable_TABLE29 NethelpersRoutingTable = 29 NethelpersRoutingTable_TABLE30 NethelpersRoutingTable = 30 NethelpersRoutingTable_TABLE31 NethelpersRoutingTable = 31 NethelpersRoutingTable_TABLE32 NethelpersRoutingTable = 32 NethelpersRoutingTable_TABLE33 NethelpersRoutingTable = 33 NethelpersRoutingTable_TABLE34 NethelpersRoutingTable = 34 NethelpersRoutingTable_TABLE35 NethelpersRoutingTable = 35 NethelpersRoutingTable_TABLE36 NethelpersRoutingTable = 36 NethelpersRoutingTable_TABLE37 NethelpersRoutingTable = 37 NethelpersRoutingTable_TABLE38 NethelpersRoutingTable = 38 NethelpersRoutingTable_TABLE39 NethelpersRoutingTable = 39 NethelpersRoutingTable_TABLE40 NethelpersRoutingTable = 40 NethelpersRoutingTable_TABLE41 NethelpersRoutingTable = 41 NethelpersRoutingTable_TABLE42 NethelpersRoutingTable = 42 NethelpersRoutingTable_TABLE43 NethelpersRoutingTable = 43 NethelpersRoutingTable_TABLE44 NethelpersRoutingTable = 44 NethelpersRoutingTable_TABLE45 NethelpersRoutingTable = 45 NethelpersRoutingTable_TABLE46 NethelpersRoutingTable = 46 NethelpersRoutingTable_TABLE47 NethelpersRoutingTable = 47 NethelpersRoutingTable_TABLE48 NethelpersRoutingTable = 48 NethelpersRoutingTable_TABLE49 NethelpersRoutingTable = 49 NethelpersRoutingTable_TABLE50 NethelpersRoutingTable = 50 NethelpersRoutingTable_TABLE51 NethelpersRoutingTable = 51 NethelpersRoutingTable_TABLE52 NethelpersRoutingTable = 52 NethelpersRoutingTable_TABLE53 NethelpersRoutingTable = 53 NethelpersRoutingTable_TABLE54 NethelpersRoutingTable = 54 NethelpersRoutingTable_TABLE55 NethelpersRoutingTable = 55 NethelpersRoutingTable_TABLE56 NethelpersRoutingTable = 56 NethelpersRoutingTable_TABLE57 NethelpersRoutingTable = 57 NethelpersRoutingTable_TABLE58 NethelpersRoutingTable = 58 NethelpersRoutingTable_TABLE59 NethelpersRoutingTable = 59 NethelpersRoutingTable_TABLE60 NethelpersRoutingTable = 60 NethelpersRoutingTable_TABLE61 NethelpersRoutingTable = 61 NethelpersRoutingTable_TABLE62 NethelpersRoutingTable = 62 NethelpersRoutingTable_TABLE63 NethelpersRoutingTable = 63 NethelpersRoutingTable_TABLE64 NethelpersRoutingTable = 64 NethelpersRoutingTable_TABLE65 NethelpersRoutingTable = 65 NethelpersRoutingTable_TABLE66 NethelpersRoutingTable = 66 NethelpersRoutingTable_TABLE67 NethelpersRoutingTable = 67 NethelpersRoutingTable_TABLE68 NethelpersRoutingTable = 68 NethelpersRoutingTable_TABLE69 NethelpersRoutingTable = 69 NethelpersRoutingTable_TABLE70 NethelpersRoutingTable = 70 NethelpersRoutingTable_TABLE71 NethelpersRoutingTable = 71 NethelpersRoutingTable_TABLE72 NethelpersRoutingTable = 72 NethelpersRoutingTable_TABLE73 NethelpersRoutingTable = 73 NethelpersRoutingTable_TABLE74 NethelpersRoutingTable = 74 NethelpersRoutingTable_TABLE75 NethelpersRoutingTable = 75 NethelpersRoutingTable_TABLE76 NethelpersRoutingTable = 76 NethelpersRoutingTable_TABLE77 NethelpersRoutingTable = 77 NethelpersRoutingTable_TABLE78 NethelpersRoutingTable = 78 NethelpersRoutingTable_TABLE79 NethelpersRoutingTable = 79 NethelpersRoutingTable_TABLE80 NethelpersRoutingTable = 80 NethelpersRoutingTable_TABLE81 NethelpersRoutingTable = 81 NethelpersRoutingTable_TABLE82 NethelpersRoutingTable = 82 NethelpersRoutingTable_TABLE83 NethelpersRoutingTable = 83 NethelpersRoutingTable_TABLE84 NethelpersRoutingTable = 84 NethelpersRoutingTable_TABLE85 NethelpersRoutingTable = 85 NethelpersRoutingTable_TABLE86 NethelpersRoutingTable = 86 NethelpersRoutingTable_TABLE87 NethelpersRoutingTable = 87 NethelpersRoutingTable_TABLE88 NethelpersRoutingTable = 88 NethelpersRoutingTable_TABLE89 NethelpersRoutingTable = 89 NethelpersRoutingTable_TABLE90 NethelpersRoutingTable = 90 NethelpersRoutingTable_TABLE91 NethelpersRoutingTable = 91 NethelpersRoutingTable_TABLE92 NethelpersRoutingTable = 92 NethelpersRoutingTable_TABLE93 NethelpersRoutingTable = 93 NethelpersRoutingTable_TABLE94 NethelpersRoutingTable = 94 NethelpersRoutingTable_TABLE95 NethelpersRoutingTable = 95 NethelpersRoutingTable_TABLE96 NethelpersRoutingTable = 96 NethelpersRoutingTable_TABLE97 NethelpersRoutingTable = 97 NethelpersRoutingTable_TABLE98 NethelpersRoutingTable = 98 NethelpersRoutingTable_TABLE99 NethelpersRoutingTable = 99 NethelpersRoutingTable_TABLE100 NethelpersRoutingTable = 100 NethelpersRoutingTable_TABLE101 NethelpersRoutingTable = 101 NethelpersRoutingTable_TABLE102 NethelpersRoutingTable = 102 NethelpersRoutingTable_TABLE103 NethelpersRoutingTable = 103 NethelpersRoutingTable_TABLE104 NethelpersRoutingTable = 104 NethelpersRoutingTable_TABLE105 NethelpersRoutingTable = 105 NethelpersRoutingTable_TABLE106 NethelpersRoutingTable = 106 NethelpersRoutingTable_TABLE107 NethelpersRoutingTable = 107 NethelpersRoutingTable_TABLE108 NethelpersRoutingTable = 108 NethelpersRoutingTable_TABLE109 NethelpersRoutingTable = 109 NethelpersRoutingTable_TABLE110 NethelpersRoutingTable = 110 NethelpersRoutingTable_TABLE111 NethelpersRoutingTable = 111 NethelpersRoutingTable_TABLE112 NethelpersRoutingTable = 112 NethelpersRoutingTable_TABLE113 NethelpersRoutingTable = 113 NethelpersRoutingTable_TABLE114 NethelpersRoutingTable = 114 NethelpersRoutingTable_TABLE115 NethelpersRoutingTable = 115 NethelpersRoutingTable_TABLE116 NethelpersRoutingTable = 116 NethelpersRoutingTable_TABLE117 NethelpersRoutingTable = 117 NethelpersRoutingTable_TABLE118 NethelpersRoutingTable = 118 NethelpersRoutingTable_TABLE119 NethelpersRoutingTable = 119 NethelpersRoutingTable_TABLE120 NethelpersRoutingTable = 120 NethelpersRoutingTable_TABLE121 NethelpersRoutingTable = 121 NethelpersRoutingTable_TABLE122 NethelpersRoutingTable = 122 NethelpersRoutingTable_TABLE123 NethelpersRoutingTable = 123 NethelpersRoutingTable_TABLE124 NethelpersRoutingTable = 124 NethelpersRoutingTable_TABLE125 NethelpersRoutingTable = 125 NethelpersRoutingTable_TABLE126 NethelpersRoutingTable = 126 NethelpersRoutingTable_TABLE127 NethelpersRoutingTable = 127 NethelpersRoutingTable_TABLE128 NethelpersRoutingTable = 128 NethelpersRoutingTable_TABLE129 NethelpersRoutingTable = 129 NethelpersRoutingTable_TABLE130 NethelpersRoutingTable = 130 NethelpersRoutingTable_TABLE131 NethelpersRoutingTable = 131 NethelpersRoutingTable_TABLE132 NethelpersRoutingTable = 132 NethelpersRoutingTable_TABLE133 NethelpersRoutingTable = 133 NethelpersRoutingTable_TABLE134 NethelpersRoutingTable = 134 NethelpersRoutingTable_TABLE135 NethelpersRoutingTable = 135 NethelpersRoutingTable_TABLE136 NethelpersRoutingTable = 136 NethelpersRoutingTable_TABLE137 NethelpersRoutingTable = 137 NethelpersRoutingTable_TABLE138 NethelpersRoutingTable = 138 NethelpersRoutingTable_TABLE139 NethelpersRoutingTable = 139 NethelpersRoutingTable_TABLE140 NethelpersRoutingTable = 140 NethelpersRoutingTable_TABLE141 NethelpersRoutingTable = 141 NethelpersRoutingTable_TABLE142 NethelpersRoutingTable = 142 NethelpersRoutingTable_TABLE143 NethelpersRoutingTable = 143 NethelpersRoutingTable_TABLE144 NethelpersRoutingTable = 144 NethelpersRoutingTable_TABLE145 NethelpersRoutingTable = 145 NethelpersRoutingTable_TABLE146 NethelpersRoutingTable = 146 NethelpersRoutingTable_TABLE147 NethelpersRoutingTable = 147 NethelpersRoutingTable_TABLE148 NethelpersRoutingTable = 148 NethelpersRoutingTable_TABLE149 NethelpersRoutingTable = 149 NethelpersRoutingTable_TABLE150 NethelpersRoutingTable = 150 NethelpersRoutingTable_TABLE151 NethelpersRoutingTable = 151 NethelpersRoutingTable_TABLE152 NethelpersRoutingTable = 152 NethelpersRoutingTable_TABLE153 NethelpersRoutingTable = 153 NethelpersRoutingTable_TABLE154 NethelpersRoutingTable = 154 NethelpersRoutingTable_TABLE155 NethelpersRoutingTable = 155 NethelpersRoutingTable_TABLE156 NethelpersRoutingTable = 156 NethelpersRoutingTable_TABLE157 NethelpersRoutingTable = 157 NethelpersRoutingTable_TABLE158 NethelpersRoutingTable = 158 NethelpersRoutingTable_TABLE159 NethelpersRoutingTable = 159 NethelpersRoutingTable_TABLE160 NethelpersRoutingTable = 160 NethelpersRoutingTable_TABLE161 NethelpersRoutingTable = 161 NethelpersRoutingTable_TABLE162 NethelpersRoutingTable = 162 NethelpersRoutingTable_TABLE163 NethelpersRoutingTable = 163 NethelpersRoutingTable_TABLE164 NethelpersRoutingTable = 164 NethelpersRoutingTable_TABLE165 NethelpersRoutingTable = 165 NethelpersRoutingTable_TABLE166 NethelpersRoutingTable = 166 NethelpersRoutingTable_TABLE167 NethelpersRoutingTable = 167 NethelpersRoutingTable_TABLE168 NethelpersRoutingTable = 168 NethelpersRoutingTable_TABLE169 NethelpersRoutingTable = 169 NethelpersRoutingTable_TABLE170 NethelpersRoutingTable = 170 NethelpersRoutingTable_TABLE171 NethelpersRoutingTable = 171 NethelpersRoutingTable_TABLE172 NethelpersRoutingTable = 172 NethelpersRoutingTable_TABLE173 NethelpersRoutingTable = 173 NethelpersRoutingTable_TABLE174 NethelpersRoutingTable = 174 NethelpersRoutingTable_TABLE175 NethelpersRoutingTable = 175 NethelpersRoutingTable_TABLE176 NethelpersRoutingTable = 176 NethelpersRoutingTable_TABLE177 NethelpersRoutingTable = 177 NethelpersRoutingTable_TABLE178 NethelpersRoutingTable = 178 NethelpersRoutingTable_TABLE179 NethelpersRoutingTable = 179 NethelpersRoutingTable_TABLE180 NethelpersRoutingTable = 180 NethelpersRoutingTable_TABLE181 NethelpersRoutingTable = 181 NethelpersRoutingTable_TABLE182 NethelpersRoutingTable = 182 NethelpersRoutingTable_TABLE183 NethelpersRoutingTable = 183 NethelpersRoutingTable_TABLE184 NethelpersRoutingTable = 184 NethelpersRoutingTable_TABLE185 NethelpersRoutingTable = 185 NethelpersRoutingTable_TABLE186 NethelpersRoutingTable = 186 NethelpersRoutingTable_TABLE187 NethelpersRoutingTable = 187 NethelpersRoutingTable_TABLE188 NethelpersRoutingTable = 188 NethelpersRoutingTable_TABLE189 NethelpersRoutingTable = 189 NethelpersRoutingTable_TABLE190 NethelpersRoutingTable = 190 NethelpersRoutingTable_TABLE191 NethelpersRoutingTable = 191 NethelpersRoutingTable_TABLE192 NethelpersRoutingTable = 192 NethelpersRoutingTable_TABLE193 NethelpersRoutingTable = 193 NethelpersRoutingTable_TABLE194 NethelpersRoutingTable = 194 NethelpersRoutingTable_TABLE195 NethelpersRoutingTable = 195 NethelpersRoutingTable_TABLE196 NethelpersRoutingTable = 196 NethelpersRoutingTable_TABLE197 NethelpersRoutingTable = 197 NethelpersRoutingTable_TABLE198 NethelpersRoutingTable = 198 NethelpersRoutingTable_TABLE199 NethelpersRoutingTable = 199 NethelpersRoutingTable_TABLE200 NethelpersRoutingTable = 200 NethelpersRoutingTable_TABLE201 NethelpersRoutingTable = 201 NethelpersRoutingTable_TABLE202 NethelpersRoutingTable = 202 NethelpersRoutingTable_TABLE203 NethelpersRoutingTable = 203 NethelpersRoutingTable_TABLE204 NethelpersRoutingTable = 204 NethelpersRoutingTable_TABLE205 NethelpersRoutingTable = 205 NethelpersRoutingTable_TABLE206 NethelpersRoutingTable = 206 NethelpersRoutingTable_TABLE207 NethelpersRoutingTable = 207 NethelpersRoutingTable_TABLE208 NethelpersRoutingTable = 208 NethelpersRoutingTable_TABLE209 NethelpersRoutingTable = 209 NethelpersRoutingTable_TABLE210 NethelpersRoutingTable = 210 NethelpersRoutingTable_TABLE211 NethelpersRoutingTable = 211 NethelpersRoutingTable_TABLE212 NethelpersRoutingTable = 212 NethelpersRoutingTable_TABLE213 NethelpersRoutingTable = 213 NethelpersRoutingTable_TABLE214 NethelpersRoutingTable = 214 NethelpersRoutingTable_TABLE215 NethelpersRoutingTable = 215 NethelpersRoutingTable_TABLE216 NethelpersRoutingTable = 216 NethelpersRoutingTable_TABLE217 NethelpersRoutingTable = 217 NethelpersRoutingTable_TABLE218 NethelpersRoutingTable = 218 NethelpersRoutingTable_TABLE219 NethelpersRoutingTable = 219 NethelpersRoutingTable_TABLE220 NethelpersRoutingTable = 220 NethelpersRoutingTable_TABLE221 NethelpersRoutingTable = 221 NethelpersRoutingTable_TABLE222 NethelpersRoutingTable = 222 NethelpersRoutingTable_TABLE223 NethelpersRoutingTable = 223 NethelpersRoutingTable_TABLE224 NethelpersRoutingTable = 224 NethelpersRoutingTable_TABLE225 NethelpersRoutingTable = 225 NethelpersRoutingTable_TABLE226 NethelpersRoutingTable = 226 NethelpersRoutingTable_TABLE227 NethelpersRoutingTable = 227 NethelpersRoutingTable_TABLE228 NethelpersRoutingTable = 228 NethelpersRoutingTable_TABLE229 NethelpersRoutingTable = 229 NethelpersRoutingTable_TABLE230 NethelpersRoutingTable = 230 NethelpersRoutingTable_TABLE231 NethelpersRoutingTable = 231 NethelpersRoutingTable_TABLE232 NethelpersRoutingTable = 232 NethelpersRoutingTable_TABLE233 NethelpersRoutingTable = 233 NethelpersRoutingTable_TABLE234 NethelpersRoutingTable = 234 NethelpersRoutingTable_TABLE235 NethelpersRoutingTable = 235 NethelpersRoutingTable_TABLE236 NethelpersRoutingTable = 236 NethelpersRoutingTable_TABLE237 NethelpersRoutingTable = 237 NethelpersRoutingTable_TABLE238 NethelpersRoutingTable = 238 NethelpersRoutingTable_TABLE239 NethelpersRoutingTable = 239 NethelpersRoutingTable_TABLE240 NethelpersRoutingTable = 240 NethelpersRoutingTable_TABLE241 NethelpersRoutingTable = 241 NethelpersRoutingTable_TABLE242 NethelpersRoutingTable = 242 NethelpersRoutingTable_TABLE243 NethelpersRoutingTable = 243 NethelpersRoutingTable_TABLE244 NethelpersRoutingTable = 244 NethelpersRoutingTable_TABLE245 NethelpersRoutingTable = 245 NethelpersRoutingTable_TABLE246 NethelpersRoutingTable = 246 NethelpersRoutingTable_TABLE247 NethelpersRoutingTable = 247 NethelpersRoutingTable_TABLE248 NethelpersRoutingTable = 248 NethelpersRoutingTable_TABLE249 NethelpersRoutingTable = 249 NethelpersRoutingTable_TABLE250 NethelpersRoutingTable = 250 NethelpersRoutingTable_TABLE251 NethelpersRoutingTable = 251 NethelpersRoutingTable_TABLE252 NethelpersRoutingTable = 252 NethelpersRoutingTable_TABLE_DEFAULT NethelpersRoutingTable = 253 NethelpersRoutingTable_TABLE_MAIN NethelpersRoutingTable = 254 NethelpersRoutingTable_TABLE_LOCAL NethelpersRoutingTable = 255 ) // Enum value maps for NethelpersRoutingTable. var ( NethelpersRoutingTable_name = map[int32]string{ 0: "TABLE_UNSPEC", 1: "TABLE1", 2: "TABLE2", 3: "TABLE3", 4: "TABLE4", 5: "TABLE5", 6: "TABLE6", 7: "TABLE7", 8: "TABLE8", 9: "TABLE9", 10: "TABLE10", 11: "TABLE11", 12: "TABLE12", 13: "TABLE13", 14: "TABLE14", 15: "TABLE15", 16: "TABLE16", 17: "TABLE17", 18: "TABLE18", 19: "TABLE19", 20: "TABLE20", 21: "TABLE21", 22: "TABLE22", 23: "TABLE23", 24: "TABLE24", 25: "TABLE25", 26: "TABLE26", 27: "TABLE27", 28: "TABLE28", 29: "TABLE29", 30: "TABLE30", 31: "TABLE31", 32: "TABLE32", 33: "TABLE33", 34: "TABLE34", 35: "TABLE35", 36: "TABLE36", 37: "TABLE37", 38: "TABLE38", 39: "TABLE39", 40: "TABLE40", 41: "TABLE41", 42: "TABLE42", 43: "TABLE43", 44: "TABLE44", 45: "TABLE45", 46: "TABLE46", 47: "TABLE47", 48: "TABLE48", 49: "TABLE49", 50: "TABLE50", 51: "TABLE51", 52: "TABLE52", 53: "TABLE53", 54: "TABLE54", 55: "TABLE55", 56: "TABLE56", 57: "TABLE57", 58: "TABLE58", 59: "TABLE59", 60: "TABLE60", 61: "TABLE61", 62: "TABLE62", 63: "TABLE63", 64: "TABLE64", 65: "TABLE65", 66: "TABLE66", 67: "TABLE67", 68: "TABLE68", 69: "TABLE69", 70: "TABLE70", 71: "TABLE71", 72: "TABLE72", 73: "TABLE73", 74: "TABLE74", 75: "TABLE75", 76: "TABLE76", 77: "TABLE77", 78: "TABLE78", 79: "TABLE79", 80: "TABLE80", 81: "TABLE81", 82: "TABLE82", 83: "TABLE83", 84: "TABLE84", 85: "TABLE85", 86: "TABLE86", 87: "TABLE87", 88: "TABLE88", 89: "TABLE89", 90: "TABLE90", 91: "TABLE91", 92: "TABLE92", 93: "TABLE93", 94: "TABLE94", 95: "TABLE95", 96: "TABLE96", 97: "TABLE97", 98: "TABLE98", 99: "TABLE99", 100: "TABLE100", 101: "TABLE101", 102: "TABLE102", 103: "TABLE103", 104: "TABLE104", 105: "TABLE105", 106: "TABLE106", 107: "TABLE107", 108: "TABLE108", 109: "TABLE109", 110: "TABLE110", 111: "TABLE111", 112: "TABLE112", 113: "TABLE113", 114: "TABLE114", 115: "TABLE115", 116: "TABLE116", 117: "TABLE117", 118: "TABLE118", 119: "TABLE119", 120: "TABLE120", 121: "TABLE121", 122: "TABLE122", 123: "TABLE123", 124: "TABLE124", 125: "TABLE125", 126: "TABLE126", 127: "TABLE127", 128: "TABLE128", 129: "TABLE129", 130: "TABLE130", 131: "TABLE131", 132: "TABLE132", 133: "TABLE133", 134: "TABLE134", 135: "TABLE135", 136: "TABLE136", 137: "TABLE137", 138: "TABLE138", 139: "TABLE139", 140: "TABLE140", 141: "TABLE141", 142: "TABLE142", 143: "TABLE143", 144: "TABLE144", 145: "TABLE145", 146: "TABLE146", 147: "TABLE147", 148: "TABLE148", 149: "TABLE149", 150: "TABLE150", 151: "TABLE151", 152: "TABLE152", 153: "TABLE153", 154: "TABLE154", 155: "TABLE155", 156: "TABLE156", 157: "TABLE157", 158: "TABLE158", 159: "TABLE159", 160: "TABLE160", 161: "TABLE161", 162: "TABLE162", 163: "TABLE163", 164: "TABLE164", 165: "TABLE165", 166: "TABLE166", 167: "TABLE167", 168: "TABLE168", 169: "TABLE169", 170: "TABLE170", 171: "TABLE171", 172: "TABLE172", 173: "TABLE173", 174: "TABLE174", 175: "TABLE175", 176: "TABLE176", 177: "TABLE177", 178: "TABLE178", 179: "TABLE179", 180: "TABLE180", 181: "TABLE181", 182: "TABLE182", 183: "TABLE183", 184: "TABLE184", 185: "TABLE185", 186: "TABLE186", 187: "TABLE187", 188: "TABLE188", 189: "TABLE189", 190: "TABLE190", 191: "TABLE191", 192: "TABLE192", 193: "TABLE193", 194: "TABLE194", 195: "TABLE195", 196: "TABLE196", 197: "TABLE197", 198: "TABLE198", 199: "TABLE199", 200: "TABLE200", 201: "TABLE201", 202: "TABLE202", 203: "TABLE203", 204: "TABLE204", 205: "TABLE205", 206: "TABLE206", 207: "TABLE207", 208: "TABLE208", 209: "TABLE209", 210: "TABLE210", 211: "TABLE211", 212: "TABLE212", 213: "TABLE213", 214: "TABLE214", 215: "TABLE215", 216: "TABLE216", 217: "TABLE217", 218: "TABLE218", 219: "TABLE219", 220: "TABLE220", 221: "TABLE221", 222: "TABLE222", 223: "TABLE223", 224: "TABLE224", 225: "TABLE225", 226: "TABLE226", 227: "TABLE227", 228: "TABLE228", 229: "TABLE229", 230: "TABLE230", 231: "TABLE231", 232: "TABLE232", 233: "TABLE233", 234: "TABLE234", 235: "TABLE235", 236: "TABLE236", 237: "TABLE237", 238: "TABLE238", 239: "TABLE239", 240: "TABLE240", 241: "TABLE241", 242: "TABLE242", 243: "TABLE243", 244: "TABLE244", 245: "TABLE245", 246: "TABLE246", 247: "TABLE247", 248: "TABLE248", 249: "TABLE249", 250: "TABLE250", 251: "TABLE251", 252: "TABLE252", 253: "TABLE_DEFAULT", 254: "TABLE_MAIN", 255: "TABLE_LOCAL", } NethelpersRoutingTable_value = map[string]int32{ "TABLE_UNSPEC": 0, "TABLE1": 1, "TABLE2": 2, "TABLE3": 3, "TABLE4": 4, "TABLE5": 5, "TABLE6": 6, "TABLE7": 7, "TABLE8": 8, "TABLE9": 9, "TABLE10": 10, "TABLE11": 11, "TABLE12": 12, "TABLE13": 13, "TABLE14": 14, "TABLE15": 15, "TABLE16": 16, "TABLE17": 17, "TABLE18": 18, "TABLE19": 19, "TABLE20": 20, "TABLE21": 21, "TABLE22": 22, "TABLE23": 23, "TABLE24": 24, "TABLE25": 25, "TABLE26": 26, "TABLE27": 27, "TABLE28": 28, "TABLE29": 29, "TABLE30": 30, "TABLE31": 31, "TABLE32": 32, "TABLE33": 33, "TABLE34": 34, "TABLE35": 35, "TABLE36": 36, "TABLE37": 37, "TABLE38": 38, "TABLE39": 39, "TABLE40": 40, "TABLE41": 41, "TABLE42": 42, "TABLE43": 43, "TABLE44": 44, "TABLE45": 45, "TABLE46": 46, "TABLE47": 47, "TABLE48": 48, "TABLE49": 49, "TABLE50": 50, "TABLE51": 51, "TABLE52": 52, "TABLE53": 53, "TABLE54": 54, "TABLE55": 55, "TABLE56": 56, "TABLE57": 57, "TABLE58": 58, "TABLE59": 59, "TABLE60": 60, "TABLE61": 61, "TABLE62": 62, "TABLE63": 63, "TABLE64": 64, "TABLE65": 65, "TABLE66": 66, "TABLE67": 67, "TABLE68": 68, "TABLE69": 69, "TABLE70": 70, "TABLE71": 71, "TABLE72": 72, "TABLE73": 73, "TABLE74": 74, "TABLE75": 75, "TABLE76": 76, "TABLE77": 77, "TABLE78": 78, "TABLE79": 79, "TABLE80": 80, "TABLE81": 81, "TABLE82": 82, "TABLE83": 83, "TABLE84": 84, "TABLE85": 85, "TABLE86": 86, "TABLE87": 87, "TABLE88": 88, "TABLE89": 89, "TABLE90": 90, "TABLE91": 91, "TABLE92": 92, "TABLE93": 93, "TABLE94": 94, "TABLE95": 95, "TABLE96": 96, "TABLE97": 97, "TABLE98": 98, "TABLE99": 99, "TABLE100": 100, "TABLE101": 101, "TABLE102": 102, "TABLE103": 103, "TABLE104": 104, "TABLE105": 105, "TABLE106": 106, "TABLE107": 107, "TABLE108": 108, "TABLE109": 109, "TABLE110": 110, "TABLE111": 111, "TABLE112": 112, "TABLE113": 113, "TABLE114": 114, "TABLE115": 115, "TABLE116": 116, "TABLE117": 117, "TABLE118": 118, "TABLE119": 119, "TABLE120": 120, "TABLE121": 121, "TABLE122": 122, "TABLE123": 123, "TABLE124": 124, "TABLE125": 125, "TABLE126": 126, "TABLE127": 127, "TABLE128": 128, "TABLE129": 129, "TABLE130": 130, "TABLE131": 131, "TABLE132": 132, "TABLE133": 133, "TABLE134": 134, "TABLE135": 135, "TABLE136": 136, "TABLE137": 137, "TABLE138": 138, "TABLE139": 139, "TABLE140": 140, "TABLE141": 141, "TABLE142": 142, "TABLE143": 143, "TABLE144": 144, "TABLE145": 145, "TABLE146": 146, "TABLE147": 147, "TABLE148": 148, "TABLE149": 149, "TABLE150": 150, "TABLE151": 151, "TABLE152": 152, "TABLE153": 153, "TABLE154": 154, "TABLE155": 155, "TABLE156": 156, "TABLE157": 157, "TABLE158": 158, "TABLE159": 159, "TABLE160": 160, "TABLE161": 161, "TABLE162": 162, "TABLE163": 163, "TABLE164": 164, "TABLE165": 165, "TABLE166": 166, "TABLE167": 167, "TABLE168": 168, "TABLE169": 169, "TABLE170": 170, "TABLE171": 171, "TABLE172": 172, "TABLE173": 173, "TABLE174": 174, "TABLE175": 175, "TABLE176": 176, "TABLE177": 177, "TABLE178": 178, "TABLE179": 179, "TABLE180": 180, "TABLE181": 181, "TABLE182": 182, "TABLE183": 183, "TABLE184": 184, "TABLE185": 185, "TABLE186": 186, "TABLE187": 187, "TABLE188": 188, "TABLE189": 189, "TABLE190": 190, "TABLE191": 191, "TABLE192": 192, "TABLE193": 193, "TABLE194": 194, "TABLE195": 195, "TABLE196": 196, "TABLE197": 197, "TABLE198": 198, "TABLE199": 199, "TABLE200": 200, "TABLE201": 201, "TABLE202": 202, "TABLE203": 203, "TABLE204": 204, "TABLE205": 205, "TABLE206": 206, "TABLE207": 207, "TABLE208": 208, "TABLE209": 209, "TABLE210": 210, "TABLE211": 211, "TABLE212": 212, "TABLE213": 213, "TABLE214": 214, "TABLE215": 215, "TABLE216": 216, "TABLE217": 217, "TABLE218": 218, "TABLE219": 219, "TABLE220": 220, "TABLE221": 221, "TABLE222": 222, "TABLE223": 223, "TABLE224": 224, "TABLE225": 225, "TABLE226": 226, "TABLE227": 227, "TABLE228": 228, "TABLE229": 229, "TABLE230": 230, "TABLE231": 231, "TABLE232": 232, "TABLE233": 233, "TABLE234": 234, "TABLE235": 235, "TABLE236": 236, "TABLE237": 237, "TABLE238": 238, "TABLE239": 239, "TABLE240": 240, "TABLE241": 241, "TABLE242": 242, "TABLE243": 243, "TABLE244": 244, "TABLE245": 245, "TABLE246": 246, "TABLE247": 247, "TABLE248": 248, "TABLE249": 249, "TABLE250": 250, "TABLE251": 251, "TABLE252": 252, "TABLE_DEFAULT": 253, "TABLE_MAIN": 254, "TABLE_LOCAL": 255, } ) func (x NethelpersRoutingTable) Enum() *NethelpersRoutingTable { p := new(NethelpersRoutingTable) *p = x return p } func (x NethelpersRoutingTable) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersRoutingTable) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[33].Descriptor() } func (NethelpersRoutingTable) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[33] } func (x NethelpersRoutingTable) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersRoutingTable.Descriptor instead. func (NethelpersRoutingTable) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{33} } // NethelpersScope is an address scope. type NethelpersScope int32 const ( NethelpersScope_SCOPE_GLOBAL NethelpersScope = 0 NethelpersScope_SCOPE_SITE NethelpersScope = 200 NethelpersScope_SCOPE_LINK NethelpersScope = 253 NethelpersScope_SCOPE_HOST NethelpersScope = 254 NethelpersScope_SCOPE_NOWHERE NethelpersScope = 255 ) // Enum value maps for NethelpersScope. var ( NethelpersScope_name = map[int32]string{ 0: "SCOPE_GLOBAL", 200: "SCOPE_SITE", 253: "SCOPE_LINK", 254: "SCOPE_HOST", 255: "SCOPE_NOWHERE", } NethelpersScope_value = map[string]int32{ "SCOPE_GLOBAL": 0, "SCOPE_SITE": 200, "SCOPE_LINK": 253, "SCOPE_HOST": 254, "SCOPE_NOWHERE": 255, } ) func (x NethelpersScope) Enum() *NethelpersScope { p := new(NethelpersScope) *p = x return p } func (x NethelpersScope) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersScope) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[34].Descriptor() } func (NethelpersScope) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[34] } func (x NethelpersScope) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersScope.Descriptor instead. func (NethelpersScope) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{34} } // NethelpersVLANProtocol is a VLAN protocol. type NethelpersVLANProtocol int32 const ( NethelpersVLANProtocol_NETHELPERS_VLANPROTOCOL_UNSPECIFIED NethelpersVLANProtocol = 0 NethelpersVLANProtocol_VLAN_PROTOCOL8021_Q NethelpersVLANProtocol = 33024 NethelpersVLANProtocol_VLAN_PROTOCOL8021_AD NethelpersVLANProtocol = 34984 ) // Enum value maps for NethelpersVLANProtocol. var ( NethelpersVLANProtocol_name = map[int32]string{ 0: "NETHELPERS_VLANPROTOCOL_UNSPECIFIED", 33024: "VLAN_PROTOCOL8021_Q", 34984: "VLAN_PROTOCOL8021_AD", } NethelpersVLANProtocol_value = map[string]int32{ "NETHELPERS_VLANPROTOCOL_UNSPECIFIED": 0, "VLAN_PROTOCOL8021_Q": 33024, "VLAN_PROTOCOL8021_AD": 34984, } ) func (x NethelpersVLANProtocol) Enum() *NethelpersVLANProtocol { p := new(NethelpersVLANProtocol) *p = x return p } func (x NethelpersVLANProtocol) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersVLANProtocol) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[35].Descriptor() } func (NethelpersVLANProtocol) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[35] } func (x NethelpersVLANProtocol) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersVLANProtocol.Descriptor instead. func (NethelpersVLANProtocol) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{35} } // NethelpersWOLMode wraps ethtool.WOLMode for YAML marshaling. type NethelpersWOLMode int32 const ( NethelpersWOLMode_NETHELPERS_WOLMODE_UNSPECIFIED NethelpersWOLMode = 0 NethelpersWOLMode_WOL_MODE_PHY NethelpersWOLMode = 1 NethelpersWOLMode_WOL_MODE_UNICAST NethelpersWOLMode = 2 NethelpersWOLMode_WOL_MODE_MULTICAST NethelpersWOLMode = 4 NethelpersWOLMode_WOL_MODE_BROADCAST NethelpersWOLMode = 8 NethelpersWOLMode_WOL_MODE_MAGIC NethelpersWOLMode = 32 NethelpersWOLMode_WOL_MODE_MAGIC_SECURE NethelpersWOLMode = 64 NethelpersWOLMode_WOL_MODE_FILTER NethelpersWOLMode = 128 ) // Enum value maps for NethelpersWOLMode. var ( NethelpersWOLMode_name = map[int32]string{ 0: "NETHELPERS_WOLMODE_UNSPECIFIED", 1: "WOL_MODE_PHY", 2: "WOL_MODE_UNICAST", 4: "WOL_MODE_MULTICAST", 8: "WOL_MODE_BROADCAST", 32: "WOL_MODE_MAGIC", 64: "WOL_MODE_MAGIC_SECURE", 128: "WOL_MODE_FILTER", } NethelpersWOLMode_value = map[string]int32{ "NETHELPERS_WOLMODE_UNSPECIFIED": 0, "WOL_MODE_PHY": 1, "WOL_MODE_UNICAST": 2, "WOL_MODE_MULTICAST": 4, "WOL_MODE_BROADCAST": 8, "WOL_MODE_MAGIC": 32, "WOL_MODE_MAGIC_SECURE": 64, "WOL_MODE_FILTER": 128, } ) func (x NethelpersWOLMode) Enum() *NethelpersWOLMode { p := new(NethelpersWOLMode) *p = x return p } func (x NethelpersWOLMode) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NethelpersWOLMode) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[36].Descriptor() } func (NethelpersWOLMode) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[36] } func (x NethelpersWOLMode) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NethelpersWOLMode.Descriptor instead. func (NethelpersWOLMode) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{36} } // BlockEncryptionKeyType describes encryption key type. type BlockEncryptionKeyType int32 const ( BlockEncryptionKeyType_ENCRYPTION_KEY_STATIC BlockEncryptionKeyType = 0 BlockEncryptionKeyType_ENCRYPTION_KEY_NODE_ID BlockEncryptionKeyType = 1 BlockEncryptionKeyType_ENCRYPTION_KEY_KMS BlockEncryptionKeyType = 2 BlockEncryptionKeyType_ENCRYPTION_KEY_TPM BlockEncryptionKeyType = 3 ) // Enum value maps for BlockEncryptionKeyType. var ( BlockEncryptionKeyType_name = map[int32]string{ 0: "ENCRYPTION_KEY_STATIC", 1: "ENCRYPTION_KEY_NODE_ID", 2: "ENCRYPTION_KEY_KMS", 3: "ENCRYPTION_KEY_TPM", } BlockEncryptionKeyType_value = map[string]int32{ "ENCRYPTION_KEY_STATIC": 0, "ENCRYPTION_KEY_NODE_ID": 1, "ENCRYPTION_KEY_KMS": 2, "ENCRYPTION_KEY_TPM": 3, } ) func (x BlockEncryptionKeyType) Enum() *BlockEncryptionKeyType { p := new(BlockEncryptionKeyType) *p = x return p } func (x BlockEncryptionKeyType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockEncryptionKeyType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[37].Descriptor() } func (BlockEncryptionKeyType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[37] } func (x BlockEncryptionKeyType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockEncryptionKeyType.Descriptor instead. func (BlockEncryptionKeyType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{37} } // BlockEncryptionProviderType describes encryption provider type. type BlockEncryptionProviderType int32 const ( BlockEncryptionProviderType_ENCRYPTION_PROVIDER_NONE BlockEncryptionProviderType = 0 BlockEncryptionProviderType_ENCRYPTION_PROVIDER_LUKS2 BlockEncryptionProviderType = 1 ) // Enum value maps for BlockEncryptionProviderType. var ( BlockEncryptionProviderType_name = map[int32]string{ 0: "ENCRYPTION_PROVIDER_NONE", 1: "ENCRYPTION_PROVIDER_LUKS2", } BlockEncryptionProviderType_value = map[string]int32{ "ENCRYPTION_PROVIDER_NONE": 0, "ENCRYPTION_PROVIDER_LUKS2": 1, } ) func (x BlockEncryptionProviderType) Enum() *BlockEncryptionProviderType { p := new(BlockEncryptionProviderType) *p = x return p } func (x BlockEncryptionProviderType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockEncryptionProviderType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[38].Descriptor() } func (BlockEncryptionProviderType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[38] } func (x BlockEncryptionProviderType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockEncryptionProviderType.Descriptor instead. func (BlockEncryptionProviderType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{38} } // BlockFilesystemType describes filesystem type. type BlockFilesystemType int32 const ( BlockFilesystemType_FILESYSTEM_TYPE_NONE BlockFilesystemType = 0 BlockFilesystemType_FILESYSTEM_TYPE_XFS BlockFilesystemType = 1 BlockFilesystemType_FILESYSTEM_TYPE_VFAT BlockFilesystemType = 2 BlockFilesystemType_FILESYSTEM_TYPE_EXT4 BlockFilesystemType = 3 BlockFilesystemType_FILESYSTEM_TYPE_ISO9660 BlockFilesystemType = 4 BlockFilesystemType_FILESYSTEM_TYPE_SWAP BlockFilesystemType = 5 BlockFilesystemType_FILESYSTEM_TYPE_VIRTIOFS BlockFilesystemType = 6 ) // Enum value maps for BlockFilesystemType. var ( BlockFilesystemType_name = map[int32]string{ 0: "FILESYSTEM_TYPE_NONE", 1: "FILESYSTEM_TYPE_XFS", 2: "FILESYSTEM_TYPE_VFAT", 3: "FILESYSTEM_TYPE_EXT4", 4: "FILESYSTEM_TYPE_ISO9660", 5: "FILESYSTEM_TYPE_SWAP", 6: "FILESYSTEM_TYPE_VIRTIOFS", } BlockFilesystemType_value = map[string]int32{ "FILESYSTEM_TYPE_NONE": 0, "FILESYSTEM_TYPE_XFS": 1, "FILESYSTEM_TYPE_VFAT": 2, "FILESYSTEM_TYPE_EXT4": 3, "FILESYSTEM_TYPE_ISO9660": 4, "FILESYSTEM_TYPE_SWAP": 5, "FILESYSTEM_TYPE_VIRTIOFS": 6, } ) func (x BlockFilesystemType) Enum() *BlockFilesystemType { p := new(BlockFilesystemType) *p = x return p } func (x BlockFilesystemType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockFilesystemType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[39].Descriptor() } func (BlockFilesystemType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[39] } func (x BlockFilesystemType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockFilesystemType.Descriptor instead. func (BlockFilesystemType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{39} } // BlockFSParameterType describes Filesystem Parameter type. type BlockFSParameterType int32 const ( BlockFSParameterType_FS_PARAMETER_TYPE_STRING_VALUE BlockFSParameterType = 0 BlockFSParameterType_FS_PARAMETER_TYPE_BOOLEAN_VALUE BlockFSParameterType = 1 BlockFSParameterType_FS_PARAMETER_TYPE_BINARY_VALUE BlockFSParameterType = 2 ) // Enum value maps for BlockFSParameterType. var ( BlockFSParameterType_name = map[int32]string{ 0: "FS_PARAMETER_TYPE_STRING_VALUE", 1: "FS_PARAMETER_TYPE_BOOLEAN_VALUE", 2: "FS_PARAMETER_TYPE_BINARY_VALUE", } BlockFSParameterType_value = map[string]int32{ "FS_PARAMETER_TYPE_STRING_VALUE": 0, "FS_PARAMETER_TYPE_BOOLEAN_VALUE": 1, "FS_PARAMETER_TYPE_BINARY_VALUE": 2, } ) func (x BlockFSParameterType) Enum() *BlockFSParameterType { p := new(BlockFSParameterType) *p = x return p } func (x BlockFSParameterType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockFSParameterType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[40].Descriptor() } func (BlockFSParameterType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[40] } func (x BlockFSParameterType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockFSParameterType.Descriptor instead. func (BlockFSParameterType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{40} } // BlockVolumePhase describes volume phase. type BlockVolumePhase int32 const ( BlockVolumePhase_VOLUME_PHASE_WAITING BlockVolumePhase = 0 BlockVolumePhase_VOLUME_PHASE_FAILED BlockVolumePhase = 1 BlockVolumePhase_VOLUME_PHASE_MISSING BlockVolumePhase = 2 BlockVolumePhase_VOLUME_PHASE_LOCATED BlockVolumePhase = 3 BlockVolumePhase_VOLUME_PHASE_PROVISIONED BlockVolumePhase = 4 BlockVolumePhase_VOLUME_PHASE_PREPARED BlockVolumePhase = 5 BlockVolumePhase_VOLUME_PHASE_READY BlockVolumePhase = 6 BlockVolumePhase_VOLUME_PHASE_CLOSED BlockVolumePhase = 7 ) // Enum value maps for BlockVolumePhase. var ( BlockVolumePhase_name = map[int32]string{ 0: "VOLUME_PHASE_WAITING", 1: "VOLUME_PHASE_FAILED", 2: "VOLUME_PHASE_MISSING", 3: "VOLUME_PHASE_LOCATED", 4: "VOLUME_PHASE_PROVISIONED", 5: "VOLUME_PHASE_PREPARED", 6: "VOLUME_PHASE_READY", 7: "VOLUME_PHASE_CLOSED", } BlockVolumePhase_value = map[string]int32{ "VOLUME_PHASE_WAITING": 0, "VOLUME_PHASE_FAILED": 1, "VOLUME_PHASE_MISSING": 2, "VOLUME_PHASE_LOCATED": 3, "VOLUME_PHASE_PROVISIONED": 4, "VOLUME_PHASE_PREPARED": 5, "VOLUME_PHASE_READY": 6, "VOLUME_PHASE_CLOSED": 7, } ) func (x BlockVolumePhase) Enum() *BlockVolumePhase { p := new(BlockVolumePhase) *p = x return p } func (x BlockVolumePhase) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockVolumePhase) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[41].Descriptor() } func (BlockVolumePhase) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[41] } func (x BlockVolumePhase) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockVolumePhase.Descriptor instead. func (BlockVolumePhase) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{41} } // BlockVolumeType describes volume type. type BlockVolumeType int32 const ( BlockVolumeType_VOLUME_TYPE_PARTITION BlockVolumeType = 0 BlockVolumeType_VOLUME_TYPE_DISK BlockVolumeType = 1 BlockVolumeType_VOLUME_TYPE_TMPFS BlockVolumeType = 2 BlockVolumeType_VOLUME_TYPE_DIRECTORY BlockVolumeType = 3 BlockVolumeType_VOLUME_TYPE_SYMLINK BlockVolumeType = 4 BlockVolumeType_VOLUME_TYPE_OVERLAY BlockVolumeType = 5 BlockVolumeType_VOLUME_TYPE_EXTERNAL BlockVolumeType = 6 ) // Enum value maps for BlockVolumeType. var ( BlockVolumeType_name = map[int32]string{ 0: "VOLUME_TYPE_PARTITION", 1: "VOLUME_TYPE_DISK", 2: "VOLUME_TYPE_TMPFS", 3: "VOLUME_TYPE_DIRECTORY", 4: "VOLUME_TYPE_SYMLINK", 5: "VOLUME_TYPE_OVERLAY", 6: "VOLUME_TYPE_EXTERNAL", } BlockVolumeType_value = map[string]int32{ "VOLUME_TYPE_PARTITION": 0, "VOLUME_TYPE_DISK": 1, "VOLUME_TYPE_TMPFS": 2, "VOLUME_TYPE_DIRECTORY": 3, "VOLUME_TYPE_SYMLINK": 4, "VOLUME_TYPE_OVERLAY": 5, "VOLUME_TYPE_EXTERNAL": 6, } ) func (x BlockVolumeType) Enum() *BlockVolumeType { p := new(BlockVolumeType) *p = x return p } func (x BlockVolumeType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockVolumeType) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[42].Descriptor() } func (BlockVolumeType) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[42] } func (x BlockVolumeType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockVolumeType.Descriptor instead. func (BlockVolumeType) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{42} } // CriImageCacheStatus describes image cache status type. type CriImageCacheStatus int32 const ( CriImageCacheStatus_IMAGE_CACHE_STATUS_UNKNOWN CriImageCacheStatus = 0 CriImageCacheStatus_IMAGE_CACHE_STATUS_DISABLED CriImageCacheStatus = 1 CriImageCacheStatus_IMAGE_CACHE_STATUS_PREPARING CriImageCacheStatus = 2 CriImageCacheStatus_IMAGE_CACHE_STATUS_READY CriImageCacheStatus = 3 ) // Enum value maps for CriImageCacheStatus. var ( CriImageCacheStatus_name = map[int32]string{ 0: "IMAGE_CACHE_STATUS_UNKNOWN", 1: "IMAGE_CACHE_STATUS_DISABLED", 2: "IMAGE_CACHE_STATUS_PREPARING", 3: "IMAGE_CACHE_STATUS_READY", } CriImageCacheStatus_value = map[string]int32{ "IMAGE_CACHE_STATUS_UNKNOWN": 0, "IMAGE_CACHE_STATUS_DISABLED": 1, "IMAGE_CACHE_STATUS_PREPARING": 2, "IMAGE_CACHE_STATUS_READY": 3, } ) func (x CriImageCacheStatus) Enum() *CriImageCacheStatus { p := new(CriImageCacheStatus) *p = x return p } func (x CriImageCacheStatus) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (CriImageCacheStatus) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[43].Descriptor() } func (CriImageCacheStatus) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[43] } func (x CriImageCacheStatus) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use CriImageCacheStatus.Descriptor instead. func (CriImageCacheStatus) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{43} } // CriImageCacheCopyStatus describes image cache copy status type. type CriImageCacheCopyStatus int32 const ( CriImageCacheCopyStatus_IMAGE_CACHE_COPY_STATUS_UNKNOWN CriImageCacheCopyStatus = 0 CriImageCacheCopyStatus_IMAGE_CACHE_COPY_STATUS_SKIPPED CriImageCacheCopyStatus = 1 CriImageCacheCopyStatus_IMAGE_CACHE_COPY_STATUS_PENDING CriImageCacheCopyStatus = 2 CriImageCacheCopyStatus_IMAGE_CACHE_COPY_STATUS_READY CriImageCacheCopyStatus = 3 ) // Enum value maps for CriImageCacheCopyStatus. var ( CriImageCacheCopyStatus_name = map[int32]string{ 0: "IMAGE_CACHE_COPY_STATUS_UNKNOWN", 1: "IMAGE_CACHE_COPY_STATUS_SKIPPED", 2: "IMAGE_CACHE_COPY_STATUS_PENDING", 3: "IMAGE_CACHE_COPY_STATUS_READY", } CriImageCacheCopyStatus_value = map[string]int32{ "IMAGE_CACHE_COPY_STATUS_UNKNOWN": 0, "IMAGE_CACHE_COPY_STATUS_SKIPPED": 1, "IMAGE_CACHE_COPY_STATUS_PENDING": 2, "IMAGE_CACHE_COPY_STATUS_READY": 3, } ) func (x CriImageCacheCopyStatus) Enum() *CriImageCacheCopyStatus { p := new(CriImageCacheCopyStatus) *p = x return p } func (x CriImageCacheCopyStatus) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (CriImageCacheCopyStatus) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[44].Descriptor() } func (CriImageCacheCopyStatus) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[44] } func (x CriImageCacheCopyStatus) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use CriImageCacheCopyStatus.Descriptor instead. func (CriImageCacheCopyStatus) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{44} } // KubespanPeerState is KubeSpan peer current state. type KubespanPeerState int32 const ( KubespanPeerState_PEER_STATE_UNKNOWN KubespanPeerState = 0 KubespanPeerState_PEER_STATE_UP KubespanPeerState = 1 KubespanPeerState_PEER_STATE_DOWN KubespanPeerState = 2 ) // Enum value maps for KubespanPeerState. var ( KubespanPeerState_name = map[int32]string{ 0: "PEER_STATE_UNKNOWN", 1: "PEER_STATE_UP", 2: "PEER_STATE_DOWN", } KubespanPeerState_value = map[string]int32{ "PEER_STATE_UNKNOWN": 0, "PEER_STATE_UP": 1, "PEER_STATE_DOWN": 2, } ) func (x KubespanPeerState) Enum() *KubespanPeerState { p := new(KubespanPeerState) *p = x return p } func (x KubespanPeerState) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (KubespanPeerState) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[45].Descriptor() } func (KubespanPeerState) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[45] } func (x KubespanPeerState) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use KubespanPeerState.Descriptor instead. func (KubespanPeerState) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{45} } // NetworkConfigLayer describes network configuration layers, with lowest priority first. type NetworkConfigLayer int32 const ( NetworkConfigLayer_CONFIG_DEFAULT NetworkConfigLayer = 0 NetworkConfigLayer_CONFIG_CMDLINE NetworkConfigLayer = 1 NetworkConfigLayer_CONFIG_PLATFORM NetworkConfigLayer = 2 NetworkConfigLayer_CONFIG_OPERATOR NetworkConfigLayer = 3 NetworkConfigLayer_CONFIG_MACHINE_CONFIGURATION NetworkConfigLayer = 4 ) // Enum value maps for NetworkConfigLayer. var ( NetworkConfigLayer_name = map[int32]string{ 0: "CONFIG_DEFAULT", 1: "CONFIG_CMDLINE", 2: "CONFIG_PLATFORM", 3: "CONFIG_OPERATOR", 4: "CONFIG_MACHINE_CONFIGURATION", } NetworkConfigLayer_value = map[string]int32{ "CONFIG_DEFAULT": 0, "CONFIG_CMDLINE": 1, "CONFIG_PLATFORM": 2, "CONFIG_OPERATOR": 3, "CONFIG_MACHINE_CONFIGURATION": 4, } ) func (x NetworkConfigLayer) Enum() *NetworkConfigLayer { p := new(NetworkConfigLayer) *p = x return p } func (x NetworkConfigLayer) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NetworkConfigLayer) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[46].Descriptor() } func (NetworkConfigLayer) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[46] } func (x NetworkConfigLayer) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NetworkConfigLayer.Descriptor instead. func (NetworkConfigLayer) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{46} } // NetworkOperator enumerates Talos network operators. type NetworkOperator int32 const ( NetworkOperator_OPERATOR_DHCP4 NetworkOperator = 0 NetworkOperator_OPERATOR_DHCP6 NetworkOperator = 1 NetworkOperator_OPERATOR_VIP NetworkOperator = 2 ) // Enum value maps for NetworkOperator. var ( NetworkOperator_name = map[int32]string{ 0: "OPERATOR_DHCP4", 1: "OPERATOR_DHCP6", 2: "OPERATOR_VIP", } NetworkOperator_value = map[string]int32{ "OPERATOR_DHCP4": 0, "OPERATOR_DHCP6": 1, "OPERATOR_VIP": 2, } ) func (x NetworkOperator) Enum() *NetworkOperator { p := new(NetworkOperator) *p = x return p } func (x NetworkOperator) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (NetworkOperator) Descriptor() protoreflect.EnumDescriptor { return file_resource_definitions_enums_enums_proto_enumTypes[47].Descriptor() } func (NetworkOperator) Type() protoreflect.EnumType { return &file_resource_definitions_enums_enums_proto_enumTypes[47] } func (x NetworkOperator) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use NetworkOperator.Descriptor instead. func (NetworkOperator) EnumDescriptor() ([]byte, []int) { return file_resource_definitions_enums_enums_proto_rawDescGZIP(), []int{47} } var File_resource_definitions_enums_enums_proto protoreflect.FileDescriptor const file_resource_definitions_enums_enums_proto_rawDesc = "" + "\n" + "&resource/definitions/enums/enums.proto\x12 talos.resource.definitions.enums*\x9b\x02\n" + "\x13RuntimeMachineStage\x12\x19\n" + "\x15MACHINE_STAGE_UNKNOWN\x10\x00\x12\x19\n" + "\x15MACHINE_STAGE_BOOTING\x10\x01\x12\x1c\n" + "\x18MACHINE_STAGE_INSTALLING\x10\x02\x12\x1d\n" + "\x19MACHINE_STAGE_MAINTENANCE\x10\x03\x12\x19\n" + "\x15MACHINE_STAGE_RUNNING\x10\x04\x12\x1b\n" + "\x17MACHINE_STAGE_REBOOTING\x10\x05\x12\x1f\n" + "\x1bMACHINE_STAGE_SHUTTING_DOWN\x10\x06\x12\x1b\n" + "\x17MACHINE_STAGE_RESETTING\x10\a\x12\x1b\n" + "\x17MACHINE_STAGE_UPGRADING\x10\b*o\n" + "\x13RuntimeSELinuxState\x12\x1b\n" + "\x17SE_LINUX_STATE_DISABLED\x10\x00\x12\x1d\n" + "\x19SE_LINUX_STATE_PERMISSIVE\x10\x01\x12\x1c\n" + "\x18SE_LINUX_STATE_ENFORCING\x10\x02*Z\n" + "\x10RuntimeFIPSState\x12\x17\n" + "\x13FIPS_STATE_DISABLED\x10\x00\x12\x16\n" + "\x12FIPS_STATE_ENABLED\x10\x01\x12\x15\n" + "\x11FIPS_STATE_STRICT\x10\x02*W\n" + "\vMachineType\x12\x10\n" + "\fTYPE_UNKNOWN\x10\x00\x12\r\n" + "\tTYPE_INIT\x10\x01\x12\x16\n" + "\x12TYPE_CONTROL_PLANE\x10\x02\x12\x0f\n" + "\vTYPE_WORKER\x10\x03*\xe7\x02\n" + "\x15NethelpersAddressFlag\x12&\n" + "\"NETHELPERS_ADDRESSFLAG_UNSPECIFIED\x10\x00\x12\x15\n" + "\x11ADDRESS_TEMPORARY\x10\x01\x12\x12\n" + "\x0eADDRESS_NO_DAD\x10\x02\x12\x16\n" + "\x12ADDRESS_OPTIMISTIC\x10\x04\x12\x16\n" + "\x12ADDRESS_DAD_FAILED\x10\b\x12\x10\n" + "\fADDRESS_HOME\x10\x10\x12\x16\n" + "\x12ADDRESS_DEPRECATED\x10 \x12\x15\n" + "\x11ADDRESS_TENTATIVE\x10@\x12\x16\n" + "\x11ADDRESS_PERMANENT\x10\x80\x01\x12\x1c\n" + "\x17ADDRESS_MANAGEMENT_TEMP\x10\x80\x02\x12\x1c\n" + "\x17ADDRESS_NO_PREFIX_ROUTE\x10\x80\x04\x12\x19\n" + "\x14ADDRESS_MC_AUTO_JOIN\x10\x80\b\x12\x1b\n" + "\x16ADDRESS_STABLE_PRIVACY\x10\x80\x10*^\n" + "\x1eNethelpersAddressSortAlgorithm\x12\x1d\n" + "\x19ADDRESS_SORT_ALGORITHM_V1\x10\x00\x12\x1d\n" + "\x19ADDRESS_SORT_ALGORITHM_V2\x10\x01*E\n" + "\x16NethelpersADLACPActive\x12\x15\n" + "\x11ADLACP_ACTIVE_OFF\x10\x00\x12\x14\n" + "\x10ADLACP_ACTIVE_ON\x10\x01*X\n" + "\x12NethelpersADSelect\x12\x14\n" + "\x10AD_SELECT_STABLE\x10\x00\x12\x17\n" + "\x13AD_SELECT_BANDWIDTH\x10\x01\x12\x13\n" + "\x0fAD_SELECT_COUNT\x10\x02*K\n" + "\x17NethelpersARPAllTargets\x12\x17\n" + "\x13ARP_ALL_TARGETS_ANY\x10\x00\x12\x17\n" + "\x13ARP_ALL_TARGETS_ALL\x10\x01*\xcf\x01\n" + "\x15NethelpersARPValidate\x12\x15\n" + "\x11ARP_VALIDATE_NONE\x10\x00\x12\x17\n" + "\x13ARP_VALIDATE_ACTIVE\x10\x01\x12\x17\n" + "\x13ARP_VALIDATE_BACKUP\x10\x02\x12\x14\n" + "\x10ARP_VALIDATE_ALL\x10\x03\x12\x17\n" + "\x13ARP_VALIDATE_FILTER\x10\x04\x12\x1e\n" + "\x1aARP_VALIDATE_FILTER_ACTIVE\x10\x05\x12\x1e\n" + "\x1aARP_VALIDATE_FILTER_BACKUP\x10\x06*t\n" + "\x1aNethelpersAutoHostnameKind\x12\x1a\n" + "\x16AUTO_HOSTNAME_KIND_OFF\x10\x00\x12\x1b\n" + "\x17AUTO_HOSTNAME_KIND_ADDR\x10\x01\x12\x1d\n" + "\x19AUTO_HOSTNAME_KIND_STABLE\x10\x02*\xb3\x01\n" + "\x12NethelpersBondMode\x12\x18\n" + "\x14BOND_MODE_ROUNDROBIN\x10\x00\x12\x1b\n" + "\x17BOND_MODE_ACTIVE_BACKUP\x10\x01\x12\x11\n" + "\rBOND_MODE_XOR\x10\x02\x12\x17\n" + "\x13BOND_MODE_BROADCAST\x10\x03\x12\x14\n" + "\x10BOND_MODE8023_AD\x10\x04\x12\x11\n" + "\rBOND_MODE_TLB\x10\x05\x12\x11\n" + "\rBOND_MODE_ALB\x10\x06*\xb3\x01\n" + "\x1cNethelpersBondXmitHashPolicy\x12\x1b\n" + "\x17BOND_XMIT_POLICY_LAYER2\x10\x00\x12\x1c\n" + "\x18BOND_XMIT_POLICY_LAYER34\x10\x01\x12\x1c\n" + "\x18BOND_XMIT_POLICY_LAYER23\x10\x02\x12\x1c\n" + "\x18BOND_XMIT_POLICY_ENCAP23\x10\x03\x12\x1c\n" + "\x18BOND_XMIT_POLICY_ENCAP34\x10\x04*o\n" + "\x1aNethelpersClientIdentifier\x12\x1a\n" + "\x16CLIENT_IDENTIFIER_NONE\x10\x00\x12\x19\n" + "\x15CLIENT_IDENTIFIER_MAC\x10\x01\x12\x1a\n" + "\x16CLIENT_IDENTIFIER_DUID\x10\x02*\xb9\x01\n" + "\x18NethelpersConntrackState\x12)\n" + "%NETHELPERS_CONNTRACKSTATE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13CONNTRACK_STATE_NEW\x10\b\x12\x1b\n" + "\x17CONNTRACK_STATE_RELATED\x10\x04\x12\x1f\n" + "\x1bCONNTRACK_STATE_ESTABLISHED\x10\x02\x12\x1b\n" + "\x17CONNTRACK_STATE_INVALID\x10\x01*4\n" + "\x10NethelpersDuplex\x12\b\n" + "\x04HALF\x10\x00\x12\b\n" + "\x04FULL\x10\x01\x12\f\n" + "\aUNKNOWN\x10\xff\x01*c\n" + "\x15NethelpersFailOverMAC\x12\x16\n" + "\x12FAIL_OVER_MAC_NONE\x10\x00\x12\x18\n" + "\x14FAIL_OVER_MAC_ACTIVE\x10\x01\x12\x18\n" + "\x14FAIL_OVER_MAC_FOLLOW\x10\x02*Y\n" + "\x10NethelpersFamily\x12!\n" + "\x1dNETHELPERS_FAMILY_UNSPECIFIED\x10\x00\x12\x10\n" + "\fFAMILY_INET4\x10\x02\x12\x10\n" + "\fFAMILY_INET6\x10\n" + "*\xbf\x01\n" + "\x12NethelpersICMPType\x12#\n" + "\x1fNETHELPERS_ICMPTYPE_UNSPECIFIED\x10\x00\x12\x1f\n" + "\x1bICMP_TYPE_TIMESTAMP_REQUEST\x10\r\x12\x1d\n" + "\x19ICMP_TYPE_TIMESTAMP_REPLY\x10\x0e\x12\"\n" + "\x1eICMP_TYPE_ADDRESS_MASK_REQUEST\x10\x11\x12 \n" + "\x1cICMP_TYPE_ADDRESS_MASK_REPLY\x10\x12*<\n" + "\x12NethelpersLACPRate\x12\x12\n" + "\x0eLACP_RATE_SLOW\x10\x00\x12\x12\n" + "\x0eLACP_RATE_FAST\x10\x01*\x93\v\n" + "\x12NethelpersLinkType\x12\x0f\n" + "\vLINK_NETROM\x10\x00\x12\x0e\n" + "\n" + "LINK_ETHER\x10\x01\x12\x0f\n" + "\vLINK_EETHER\x10\x02\x12\r\n" + "\tLINK_AX25\x10\x03\x12\x0f\n" + "\vLINK_PRONET\x10\x04\x12\x0e\n" + "\n" + "LINK_CHAOS\x10\x05\x12\x0f\n" + "\vLINK_IEE802\x10\x06\x12\x0f\n" + "\vLINK_ARCNET\x10\a\x12\x0e\n" + "\n" + "LINK_ATALK\x10\b\x12\r\n" + "\tLINK_DLCI\x10\x0f\x12\f\n" + "\bLINK_ATM\x10\x13\x12\x11\n" + "\rLINK_METRICOM\x10\x17\x12\x11\n" + "\rLINK_IEEE1394\x10\x18\x12\x0e\n" + "\n" + "LINK_EUI64\x10\x1b\x12\x13\n" + "\x0fLINK_INFINIBAND\x10 \x12\x0e\n" + "\tLINK_SLIP\x10\x80\x02\x12\x0f\n" + "\n" + "LINK_CSLIP\x10\x81\x02\x12\x0f\n" + "\n" + "LINK_SLIP6\x10\x82\x02\x12\x10\n" + "\vLINK_CSLIP6\x10\x83\x02\x12\x0f\n" + "\n" + "LINK_RSRVD\x10\x84\x02\x12\x0f\n" + "\n" + "LINK_ADAPT\x10\x88\x02\x12\x0e\n" + "\tLINK_ROSE\x10\x8e\x02\x12\r\n" + "\bLINK_X25\x10\x8f\x02\x12\x0f\n" + "\n" + "LINK_HWX25\x10\x90\x02\x12\r\n" + "\bLINK_CAN\x10\x98\x02\x12\r\n" + "\bLINK_PPP\x10\x80\x04\x12\x0f\n" + "\n" + "LINK_CISCO\x10\x81\x04\x12\x0e\n" + "\tLINK_HDLC\x10\x81\x04\x12\x0e\n" + "\tLINK_LAPB\x10\x84\x04\x12\x0f\n" + "\n" + "LINK_DDCMP\x10\x85\x04\x12\x11\n" + "\fLINK_RAWHDLC\x10\x86\x04\x12\x10\n" + "\vLINK_TUNNEL\x10\x80\x06\x12\x11\n" + "\fLINK_TUNNEL6\x10\x81\x06\x12\x0e\n" + "\tLINK_FRAD\x10\x82\x06\x12\x0e\n" + "\tLINK_SKIP\x10\x83\x06\x12\x11\n" + "\fLINK_LOOPBCK\x10\x84\x06\x12\x12\n" + "\rLINK_LOCALTLK\x10\x85\x06\x12\x0e\n" + "\tLINK_FDDI\x10\x86\x06\x12\r\n" + "\bLINK_BIF\x10\x87\x06\x12\r\n" + "\bLINK_SIT\x10\x88\x06\x12\x0f\n" + "\n" + "LINK_IPDDP\x10\x89\x06\x12\x0f\n" + "\n" + "LINK_IPGRE\x10\x8a\x06\x12\x10\n" + "\vLINK_PIMREG\x10\x8b\x06\x12\x0f\n" + "\n" + "LINK_HIPPI\x10\x8c\x06\x12\r\n" + "\bLINK_ASH\x10\x8d\x06\x12\x10\n" + "\vLINK_ECONET\x10\x8e\x06\x12\x0e\n" + "\tLINK_IRDA\x10\x8f\x06\x12\x0e\n" + "\tLINK_FCPP\x10\x90\x06\x12\x0e\n" + "\tLINK_FCAL\x10\x91\x06\x12\x0e\n" + "\tLINK_FCPL\x10\x92\x06\x12\x12\n" + "\rLINK_FCFABRIC\x10\x93\x06\x12\x13\n" + "\x0eLINK_FCFABRIC1\x10\x94\x06\x12\x13\n" + "\x0eLINK_FCFABRIC2\x10\x95\x06\x12\x13\n" + "\x0eLINK_FCFABRIC3\x10\x96\x06\x12\x13\n" + "\x0eLINK_FCFABRIC4\x10\x97\x06\x12\x13\n" + "\x0eLINK_FCFABRIC5\x10\x98\x06\x12\x13\n" + "\x0eLINK_FCFABRIC6\x10\x99\x06\x12\x13\n" + "\x0eLINK_FCFABRIC7\x10\x9a\x06\x12\x13\n" + "\x0eLINK_FCFABRIC8\x10\x9b\x06\x12\x13\n" + "\x0eLINK_FCFABRIC9\x10\x9c\x06\x12\x14\n" + "\x0fLINK_FCFABRIC10\x10\x9d\x06\x12\x14\n" + "\x0fLINK_FCFABRIC11\x10\x9e\x06\x12\x14\n" + "\x0fLINK_FCFABRIC12\x10\x9f\x06\x12\x12\n" + "\rLINK_IEE802TR\x10\xa0\x06\x12\x12\n" + "\rLINK_IEE80211\x10\xa1\x06\x12\x17\n" + "\x12LINK_IEE80211PRISM\x10\xa2\x06\x12\x1b\n" + "\x16LINK_IEE80211_RADIOTAP\x10\xa3\x06\x12\x14\n" + "\x0fLINK_IEE8021154\x10\xa4\x06\x12\x1b\n" + "\x16LINK_IEE8021154MONITOR\x10\xa5\x06\x12\x10\n" + "\vLINK_PHONET\x10\xb4\x06\x12\x14\n" + "\x0fLINK_PHONETPIPE\x10\xb5\x06\x12\x0e\n" + "\tLINK_CAIF\x10\xb6\x06\x12\x10\n" + "\vLINK_IP6GRE\x10\xb7\x06\x12\x11\n" + "\fLINK_NETLINK\x10\xb8\x06\x12\x11\n" + "\fLINK6_LOWPAN\x10\xb9\x06\x12\x0f\n" + "\tLINK_VOID\x10\xff\xff\x03\x12\x0f\n" + "\tLINK_NONE\x10\xfe\xff\x03\x1a\x02\x10\x01*E\n" + "\x17NethelpersMatchOperator\x12\x12\n" + "\x0eOPERATOR_EQUAL\x10\x00\x12\x16\n" + "\x12OPERATOR_NOT_EQUAL\x10\x01*\x99\x01\n" + "\x1bNethelpersNfTablesChainHook\x12\x19\n" + "\x15CHAIN_HOOK_PREROUTING\x10\x00\x12\x14\n" + "\x10CHAIN_HOOK_INPUT\x10\x01\x12\x16\n" + "\x12CHAIN_HOOK_FORWARD\x10\x02\x12\x15\n" + "\x11CHAIN_HOOK_OUTPUT\x10\x03\x12\x1a\n" + "\x16CHAIN_HOOK_POSTROUTING\x10\x04*\xa3\x04\n" + "\x1fNethelpersNfTablesChainPriority\x120\n" + ",NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED\x10\x00\x12!\n" + "\x14CHAIN_PRIORITY_FIRST\x10\x80\x80\x80\x80\xf8\xff\xff\xff\xff\x01\x12,\n" + "\x1fCHAIN_PRIORITY_CONNTRACK_DEFRAG\x10\xf0\xfc\xff\xff\xff\xff\xff\xff\xff\x01\x12\x1f\n" + "\x12CHAIN_PRIORITY_RAW\x10\xd4\xfd\xff\xff\xff\xff\xff\xff\xff\x01\x12*\n" + "\x1dCHAIN_PRIORITY_SE_LINUX_FIRST\x10\x9f\xfe\xff\xff\xff\xff\xff\xff\xff\x01\x12%\n" + "\x18CHAIN_PRIORITY_CONNTRACK\x10\xb8\xfe\xff\xff\xff\xff\xff\xff\xff\x01\x12\"\n" + "\x15CHAIN_PRIORITY_MANGLE\x10\xea\xfe\xff\xff\xff\xff\xff\xff\xff\x01\x12$\n" + "\x17CHAIN_PRIORITY_NAT_DEST\x10\x9c\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x19\n" + "\x15CHAIN_PRIORITY_FILTER\x10\x00\x12\x1b\n" + "\x17CHAIN_PRIORITY_SECURITY\x102\x12\x1d\n" + "\x19CHAIN_PRIORITY_NAT_SOURCE\x10d\x12!\n" + "\x1cCHAIN_PRIORITY_SE_LINUX_LAST\x10\xe1\x01\x12$\n" + "\x1fCHAIN_PRIORITY_CONNTRACK_HELPER\x10\xac\x02\x12\x1b\n" + "\x13CHAIN_PRIORITY_LAST\x10\xff\xff\xff\xff\a\x1a\x02\x10\x01*A\n" + "\x19NethelpersNfTablesVerdict\x12\x10\n" + "\fVERDICT_DROP\x10\x00\x12\x12\n" + "\x0eVERDICT_ACCEPT\x10\x01*\xc9\x01\n" + "\x1aNethelpersOperationalState\x12\x16\n" + "\x12OPER_STATE_UNKNOWN\x10\x00\x12\x1a\n" + "\x16OPER_STATE_NOT_PRESENT\x10\x01\x12\x13\n" + "\x0fOPER_STATE_DOWN\x10\x02\x12\x1f\n" + "\x1bOPER_STATE_LOWER_LAYER_DOWN\x10\x03\x12\x16\n" + "\x12OPER_STATE_TESTING\x10\x04\x12\x16\n" + "\x12OPER_STATE_DORMANT\x10\x05\x12\x11\n" + "\rOPER_STATE_UP\x10\x06*r\n" + "\x0eNethelpersPort\x12\x10\n" + "\fTWISTED_PAIR\x10\x00\x12\a\n" + "\x03AUI\x10\x01\x12\a\n" + "\x03MII\x10\x02\x12\t\n" + "\x05FIBRE\x10\x03\x12\a\n" + "\x03BNC\x10\x04\x12\x11\n" + "\rDIRECT_ATTACH\x10\x05\x12\t\n" + "\x04NONE\x10\xef\x01\x12\n" + "\n" + "\x05OTHER\x10\xff\x01*s\n" + "\x19NethelpersPrimaryReselect\x12\x1b\n" + "\x17PRIMARY_RESELECT_ALWAYS\x10\x00\x12\x1b\n" + "\x17PRIMARY_RESELECT_BETTER\x10\x01\x12\x1c\n" + "\x18PRIMARY_RESELECT_FAILURE\x10\x02*\x86\x01\n" + "\x12NethelpersProtocol\x12#\n" + "\x1fNETHELPERS_PROTOCOL_UNSPECIFIED\x10\x00\x12\x11\n" + "\rPROTOCOL_ICMP\x10\x01\x12\x10\n" + "\fPROTOCOL_TCP\x10\x06\x12\x10\n" + "\fPROTOCOL_UDP\x10\x11\x12\x14\n" + "\x10PROTOCOL_ICM_PV6\x10:*\xdf\x01\n" + "\x13NethelpersRouteFlag\x12$\n" + " NETHELPERS_ROUTEFLAG_UNSPECIFIED\x10\x00\x12\x11\n" + "\fROUTE_NOTIFY\x10\x80\x02\x12\x11\n" + "\fROUTE_CLONED\x10\x80\x04\x12\x13\n" + "\x0eROUTE_EQUALIZE\x10\x80\b\x12\x11\n" + "\fROUTE_PREFIX\x10\x80\x10\x12\x17\n" + "\x12ROUTE_LOOKUP_TABLE\x10\x80 \x12\x14\n" + "\x0fROUTE_FIB_MATCH\x10\x80@\x12\x13\n" + "\rROUTE_OFFLOAD\x10\x80\x80\x01\x12\x10\n" + "\n" + "ROUTE_TRAP\x10\x80\x80\x02*\xd2\x03\n" + "\x17NethelpersRouteProtocol\x12\x13\n" + "\x0fPROTOCOL_UNSPEC\x10\x00\x12\x15\n" + "\x11PROTOCOL_REDIRECT\x10\x01\x12\x13\n" + "\x0fPROTOCOL_KERNEL\x10\x02\x12\x11\n" + "\rPROTOCOL_BOOT\x10\x03\x12\x13\n" + "\x0fPROTOCOL_STATIC\x10\x04\x12\x0f\n" + "\vPROTOCOL_RA\x10\t\x12\x10\n" + "\fPROTOCOL_MRT\x10\n" + "\x12\x12\n" + "\x0ePROTOCOL_ZEBRA\x10\v\x12\x11\n" + "\rPROTOCOL_BIRD\x10\f\x12\x15\n" + "\x11PROTOCOL_DNROUTED\x10\r\x12\x11\n" + "\rPROTOCOL_XORP\x10\x0e\x12\x10\n" + "\fPROTOCOL_NTK\x10\x0f\x12\x11\n" + "\rPROTOCOL_DHCP\x10\x10\x12\x11\n" + "\rPROTOCOL_MRTD\x10\x11\x12\x17\n" + "\x13PROTOCOL_KEEPALIVED\x10\x12\x12\x12\n" + "\x0ePROTOCOL_BABEL\x10*\x12\x12\n" + "\x0ePROTOCOL_OPENR\x10c\x12\x11\n" + "\fPROTOCOL_BGP\x10\xba\x01\x12\x12\n" + "\rPROTOCOL_ISIS\x10\xbb\x01\x12\x12\n" + "\rPROTOCOL_OSPF\x10\xbc\x01\x12\x11\n" + "\fPROTOCOL_RIP\x10\xbd\x01\x12\x13\n" + "\x0ePROTOCOL_EIGRP\x10\xc0\x01*\xf1\x01\n" + "\x13NethelpersRouteType\x12\x0f\n" + "\vTYPE_UNSPEC\x10\x00\x12\x10\n" + "\fTYPE_UNICAST\x10\x01\x12\x0e\n" + "\n" + "TYPE_LOCAL\x10\x02\x12\x12\n" + "\x0eTYPE_BROADCAST\x10\x03\x12\x10\n" + "\fTYPE_ANYCAST\x10\x04\x12\x12\n" + "\x0eTYPE_MULTICAST\x10\x05\x12\x12\n" + "\x0eTYPE_BLACKHOLE\x10\x06\x12\x14\n" + "\x10TYPE_UNREACHABLE\x10\a\x12\x11\n" + "\rTYPE_PROHIBIT\x10\b\x12\x0e\n" + "\n" + "TYPE_THROW\x10\t\x12\f\n" + "\bTYPE_NAT\x10\n" + "\x12\x12\n" + "\x0eTYPE_X_RESOLVE\x10\v*\xc8\x01\n" + "\x1bNethelpersRoutingRuleAction\x12\x1e\n" + "\x1aROUTING_RULE_ACTION_UNSPEC\x10\x00\x12\x1f\n" + "\x1bROUTING_RULE_ACTION_UNICAST\x10\x01\x12!\n" + "\x1dROUTING_RULE_ACTION_BLACKHOLE\x10\x06\x12#\n" + "\x1fROUTING_RULE_ACTION_UNREACHABLE\x10\a\x12 \n" + "\x1cROUTING_RULE_ACTION_PROHIBIT\x10\b*\xba\x1c\n" + "\x16NethelpersRoutingTable\x12\x10\n" + "\fTABLE_UNSPEC\x10\x00\x12\n" + "\n" + "\x06TABLE1\x10\x01\x12\n" + "\n" + "\x06TABLE2\x10\x02\x12\n" + "\n" + "\x06TABLE3\x10\x03\x12\n" + "\n" + "\x06TABLE4\x10\x04\x12\n" + "\n" + "\x06TABLE5\x10\x05\x12\n" + "\n" + "\x06TABLE6\x10\x06\x12\n" + "\n" + "\x06TABLE7\x10\a\x12\n" + "\n" + "\x06TABLE8\x10\b\x12\n" + "\n" + "\x06TABLE9\x10\t\x12\v\n" + "\aTABLE10\x10\n" + "\x12\v\n" + "\aTABLE11\x10\v\x12\v\n" + "\aTABLE12\x10\f\x12\v\n" + "\aTABLE13\x10\r\x12\v\n" + "\aTABLE14\x10\x0e\x12\v\n" + "\aTABLE15\x10\x0f\x12\v\n" + "\aTABLE16\x10\x10\x12\v\n" + "\aTABLE17\x10\x11\x12\v\n" + "\aTABLE18\x10\x12\x12\v\n" + "\aTABLE19\x10\x13\x12\v\n" + "\aTABLE20\x10\x14\x12\v\n" + "\aTABLE21\x10\x15\x12\v\n" + "\aTABLE22\x10\x16\x12\v\n" + "\aTABLE23\x10\x17\x12\v\n" + "\aTABLE24\x10\x18\x12\v\n" + "\aTABLE25\x10\x19\x12\v\n" + "\aTABLE26\x10\x1a\x12\v\n" + "\aTABLE27\x10\x1b\x12\v\n" + "\aTABLE28\x10\x1c\x12\v\n" + "\aTABLE29\x10\x1d\x12\v\n" + "\aTABLE30\x10\x1e\x12\v\n" + "\aTABLE31\x10\x1f\x12\v\n" + "\aTABLE32\x10 \x12\v\n" + "\aTABLE33\x10!\x12\v\n" + "\aTABLE34\x10\"\x12\v\n" + "\aTABLE35\x10#\x12\v\n" + "\aTABLE36\x10$\x12\v\n" + "\aTABLE37\x10%\x12\v\n" + "\aTABLE38\x10&\x12\v\n" + "\aTABLE39\x10'\x12\v\n" + "\aTABLE40\x10(\x12\v\n" + "\aTABLE41\x10)\x12\v\n" + "\aTABLE42\x10*\x12\v\n" + "\aTABLE43\x10+\x12\v\n" + "\aTABLE44\x10,\x12\v\n" + "\aTABLE45\x10-\x12\v\n" + "\aTABLE46\x10.\x12\v\n" + "\aTABLE47\x10/\x12\v\n" + "\aTABLE48\x100\x12\v\n" + "\aTABLE49\x101\x12\v\n" + "\aTABLE50\x102\x12\v\n" + "\aTABLE51\x103\x12\v\n" + "\aTABLE52\x104\x12\v\n" + "\aTABLE53\x105\x12\v\n" + "\aTABLE54\x106\x12\v\n" + "\aTABLE55\x107\x12\v\n" + "\aTABLE56\x108\x12\v\n" + "\aTABLE57\x109\x12\v\n" + "\aTABLE58\x10:\x12\v\n" + "\aTABLE59\x10;\x12\v\n" + "\aTABLE60\x10<\x12\v\n" + "\aTABLE61\x10=\x12\v\n" + "\aTABLE62\x10>\x12\v\n" + "\aTABLE63\x10?\x12\v\n" + "\aTABLE64\x10@\x12\v\n" + "\aTABLE65\x10A\x12\v\n" + "\aTABLE66\x10B\x12\v\n" + "\aTABLE67\x10C\x12\v\n" + "\aTABLE68\x10D\x12\v\n" + "\aTABLE69\x10E\x12\v\n" + "\aTABLE70\x10F\x12\v\n" + "\aTABLE71\x10G\x12\v\n" + "\aTABLE72\x10H\x12\v\n" + "\aTABLE73\x10I\x12\v\n" + "\aTABLE74\x10J\x12\v\n" + "\aTABLE75\x10K\x12\v\n" + "\aTABLE76\x10L\x12\v\n" + "\aTABLE77\x10M\x12\v\n" + "\aTABLE78\x10N\x12\v\n" + "\aTABLE79\x10O\x12\v\n" + "\aTABLE80\x10P\x12\v\n" + "\aTABLE81\x10Q\x12\v\n" + "\aTABLE82\x10R\x12\v\n" + "\aTABLE83\x10S\x12\v\n" + "\aTABLE84\x10T\x12\v\n" + "\aTABLE85\x10U\x12\v\n" + "\aTABLE86\x10V\x12\v\n" + "\aTABLE87\x10W\x12\v\n" + "\aTABLE88\x10X\x12\v\n" + "\aTABLE89\x10Y\x12\v\n" + "\aTABLE90\x10Z\x12\v\n" + "\aTABLE91\x10[\x12\v\n" + "\aTABLE92\x10\\\x12\v\n" + "\aTABLE93\x10]\x12\v\n" + "\aTABLE94\x10^\x12\v\n" + "\aTABLE95\x10_\x12\v\n" + "\aTABLE96\x10`\x12\v\n" + "\aTABLE97\x10a\x12\v\n" + "\aTABLE98\x10b\x12\v\n" + "\aTABLE99\x10c\x12\f\n" + "\bTABLE100\x10d\x12\f\n" + "\bTABLE101\x10e\x12\f\n" + "\bTABLE102\x10f\x12\f\n" + "\bTABLE103\x10g\x12\f\n" + "\bTABLE104\x10h\x12\f\n" + "\bTABLE105\x10i\x12\f\n" + "\bTABLE106\x10j\x12\f\n" + "\bTABLE107\x10k\x12\f\n" + "\bTABLE108\x10l\x12\f\n" + "\bTABLE109\x10m\x12\f\n" + "\bTABLE110\x10n\x12\f\n" + "\bTABLE111\x10o\x12\f\n" + "\bTABLE112\x10p\x12\f\n" + "\bTABLE113\x10q\x12\f\n" + "\bTABLE114\x10r\x12\f\n" + "\bTABLE115\x10s\x12\f\n" + "\bTABLE116\x10t\x12\f\n" + "\bTABLE117\x10u\x12\f\n" + "\bTABLE118\x10v\x12\f\n" + "\bTABLE119\x10w\x12\f\n" + "\bTABLE120\x10x\x12\f\n" + "\bTABLE121\x10y\x12\f\n" + "\bTABLE122\x10z\x12\f\n" + "\bTABLE123\x10{\x12\f\n" + "\bTABLE124\x10|\x12\f\n" + "\bTABLE125\x10}\x12\f\n" + "\bTABLE126\x10~\x12\f\n" + "\bTABLE127\x10\x7f\x12\r\n" + "\bTABLE128\x10\x80\x01\x12\r\n" + "\bTABLE129\x10\x81\x01\x12\r\n" + "\bTABLE130\x10\x82\x01\x12\r\n" + "\bTABLE131\x10\x83\x01\x12\r\n" + "\bTABLE132\x10\x84\x01\x12\r\n" + "\bTABLE133\x10\x85\x01\x12\r\n" + "\bTABLE134\x10\x86\x01\x12\r\n" + "\bTABLE135\x10\x87\x01\x12\r\n" + "\bTABLE136\x10\x88\x01\x12\r\n" + "\bTABLE137\x10\x89\x01\x12\r\n" + "\bTABLE138\x10\x8a\x01\x12\r\n" + "\bTABLE139\x10\x8b\x01\x12\r\n" + "\bTABLE140\x10\x8c\x01\x12\r\n" + "\bTABLE141\x10\x8d\x01\x12\r\n" + "\bTABLE142\x10\x8e\x01\x12\r\n" + "\bTABLE143\x10\x8f\x01\x12\r\n" + "\bTABLE144\x10\x90\x01\x12\r\n" + "\bTABLE145\x10\x91\x01\x12\r\n" + "\bTABLE146\x10\x92\x01\x12\r\n" + "\bTABLE147\x10\x93\x01\x12\r\n" + "\bTABLE148\x10\x94\x01\x12\r\n" + "\bTABLE149\x10\x95\x01\x12\r\n" + "\bTABLE150\x10\x96\x01\x12\r\n" + "\bTABLE151\x10\x97\x01\x12\r\n" + "\bTABLE152\x10\x98\x01\x12\r\n" + "\bTABLE153\x10\x99\x01\x12\r\n" + "\bTABLE154\x10\x9a\x01\x12\r\n" + "\bTABLE155\x10\x9b\x01\x12\r\n" + "\bTABLE156\x10\x9c\x01\x12\r\n" + "\bTABLE157\x10\x9d\x01\x12\r\n" + "\bTABLE158\x10\x9e\x01\x12\r\n" + "\bTABLE159\x10\x9f\x01\x12\r\n" + "\bTABLE160\x10\xa0\x01\x12\r\n" + "\bTABLE161\x10\xa1\x01\x12\r\n" + "\bTABLE162\x10\xa2\x01\x12\r\n" + "\bTABLE163\x10\xa3\x01\x12\r\n" + "\bTABLE164\x10\xa4\x01\x12\r\n" + "\bTABLE165\x10\xa5\x01\x12\r\n" + "\bTABLE166\x10\xa6\x01\x12\r\n" + "\bTABLE167\x10\xa7\x01\x12\r\n" + "\bTABLE168\x10\xa8\x01\x12\r\n" + "\bTABLE169\x10\xa9\x01\x12\r\n" + "\bTABLE170\x10\xaa\x01\x12\r\n" + "\bTABLE171\x10\xab\x01\x12\r\n" + "\bTABLE172\x10\xac\x01\x12\r\n" + "\bTABLE173\x10\xad\x01\x12\r\n" + "\bTABLE174\x10\xae\x01\x12\r\n" + "\bTABLE175\x10\xaf\x01\x12\r\n" + "\bTABLE176\x10\xb0\x01\x12\r\n" + "\bTABLE177\x10\xb1\x01\x12\r\n" + "\bTABLE178\x10\xb2\x01\x12\r\n" + "\bTABLE179\x10\xb3\x01\x12\r\n" + "\bTABLE180\x10\xb4\x01\x12\r\n" + "\bTABLE181\x10\xb5\x01\x12\r\n" + "\bTABLE182\x10\xb6\x01\x12\r\n" + "\bTABLE183\x10\xb7\x01\x12\r\n" + "\bTABLE184\x10\xb8\x01\x12\r\n" + "\bTABLE185\x10\xb9\x01\x12\r\n" + "\bTABLE186\x10\xba\x01\x12\r\n" + "\bTABLE187\x10\xbb\x01\x12\r\n" + "\bTABLE188\x10\xbc\x01\x12\r\n" + "\bTABLE189\x10\xbd\x01\x12\r\n" + "\bTABLE190\x10\xbe\x01\x12\r\n" + "\bTABLE191\x10\xbf\x01\x12\r\n" + "\bTABLE192\x10\xc0\x01\x12\r\n" + "\bTABLE193\x10\xc1\x01\x12\r\n" + "\bTABLE194\x10\xc2\x01\x12\r\n" + "\bTABLE195\x10\xc3\x01\x12\r\n" + "\bTABLE196\x10\xc4\x01\x12\r\n" + "\bTABLE197\x10\xc5\x01\x12\r\n" + "\bTABLE198\x10\xc6\x01\x12\r\n" + "\bTABLE199\x10\xc7\x01\x12\r\n" + "\bTABLE200\x10\xc8\x01\x12\r\n" + "\bTABLE201\x10\xc9\x01\x12\r\n" + "\bTABLE202\x10\xca\x01\x12\r\n" + "\bTABLE203\x10\xcb\x01\x12\r\n" + "\bTABLE204\x10\xcc\x01\x12\r\n" + "\bTABLE205\x10\xcd\x01\x12\r\n" + "\bTABLE206\x10\xce\x01\x12\r\n" + "\bTABLE207\x10\xcf\x01\x12\r\n" + "\bTABLE208\x10\xd0\x01\x12\r\n" + "\bTABLE209\x10\xd1\x01\x12\r\n" + "\bTABLE210\x10\xd2\x01\x12\r\n" + "\bTABLE211\x10\xd3\x01\x12\r\n" + "\bTABLE212\x10\xd4\x01\x12\r\n" + "\bTABLE213\x10\xd5\x01\x12\r\n" + "\bTABLE214\x10\xd6\x01\x12\r\n" + "\bTABLE215\x10\xd7\x01\x12\r\n" + "\bTABLE216\x10\xd8\x01\x12\r\n" + "\bTABLE217\x10\xd9\x01\x12\r\n" + "\bTABLE218\x10\xda\x01\x12\r\n" + "\bTABLE219\x10\xdb\x01\x12\r\n" + "\bTABLE220\x10\xdc\x01\x12\r\n" + "\bTABLE221\x10\xdd\x01\x12\r\n" + "\bTABLE222\x10\xde\x01\x12\r\n" + "\bTABLE223\x10\xdf\x01\x12\r\n" + "\bTABLE224\x10\xe0\x01\x12\r\n" + "\bTABLE225\x10\xe1\x01\x12\r\n" + "\bTABLE226\x10\xe2\x01\x12\r\n" + "\bTABLE227\x10\xe3\x01\x12\r\n" + "\bTABLE228\x10\xe4\x01\x12\r\n" + "\bTABLE229\x10\xe5\x01\x12\r\n" + "\bTABLE230\x10\xe6\x01\x12\r\n" + "\bTABLE231\x10\xe7\x01\x12\r\n" + "\bTABLE232\x10\xe8\x01\x12\r\n" + "\bTABLE233\x10\xe9\x01\x12\r\n" + "\bTABLE234\x10\xea\x01\x12\r\n" + "\bTABLE235\x10\xeb\x01\x12\r\n" + "\bTABLE236\x10\xec\x01\x12\r\n" + "\bTABLE237\x10\xed\x01\x12\r\n" + "\bTABLE238\x10\xee\x01\x12\r\n" + "\bTABLE239\x10\xef\x01\x12\r\n" + "\bTABLE240\x10\xf0\x01\x12\r\n" + "\bTABLE241\x10\xf1\x01\x12\r\n" + "\bTABLE242\x10\xf2\x01\x12\r\n" + "\bTABLE243\x10\xf3\x01\x12\r\n" + "\bTABLE244\x10\xf4\x01\x12\r\n" + "\bTABLE245\x10\xf5\x01\x12\r\n" + "\bTABLE246\x10\xf6\x01\x12\r\n" + "\bTABLE247\x10\xf7\x01\x12\r\n" + "\bTABLE248\x10\xf8\x01\x12\r\n" + "\bTABLE249\x10\xf9\x01\x12\r\n" + "\bTABLE250\x10\xfa\x01\x12\r\n" + "\bTABLE251\x10\xfb\x01\x12\r\n" + "\bTABLE252\x10\xfc\x01\x12\x12\n" + "\rTABLE_DEFAULT\x10\xfd\x01\x12\x0f\n" + "\n" + "TABLE_MAIN\x10\xfe\x01\x12\x10\n" + "\vTABLE_LOCAL\x10\xff\x01*j\n" + "\x0fNethelpersScope\x12\x10\n" + "\fSCOPE_GLOBAL\x10\x00\x12\x0f\n" + "\n" + "SCOPE_SITE\x10\xc8\x01\x12\x0f\n" + "\n" + "SCOPE_LINK\x10\xfd\x01\x12\x0f\n" + "\n" + "SCOPE_HOST\x10\xfe\x01\x12\x12\n" + "\rSCOPE_NOWHERE\x10\xff\x01*x\n" + "\x16NethelpersVLANProtocol\x12'\n" + "#NETHELPERS_VLANPROTOCOL_UNSPECIFIED\x10\x00\x12\x19\n" + "\x13VLAN_PROTOCOL8021_Q\x10\x80\x82\x02\x12\x1a\n" + "\x14VLAN_PROTOCOL8021_AD\x10\xa8\x91\x02*\xd4\x01\n" + "\x11NethelpersWOLMode\x12\"\n" + "\x1eNETHELPERS_WOLMODE_UNSPECIFIED\x10\x00\x12\x10\n" + "\fWOL_MODE_PHY\x10\x01\x12\x14\n" + "\x10WOL_MODE_UNICAST\x10\x02\x12\x16\n" + "\x12WOL_MODE_MULTICAST\x10\x04\x12\x16\n" + "\x12WOL_MODE_BROADCAST\x10\b\x12\x12\n" + "\x0eWOL_MODE_MAGIC\x10 \x12\x19\n" + "\x15WOL_MODE_MAGIC_SECURE\x10@\x12\x14\n" + "\x0fWOL_MODE_FILTER\x10\x80\x01*\x7f\n" + "\x16BlockEncryptionKeyType\x12\x19\n" + "\x15ENCRYPTION_KEY_STATIC\x10\x00\x12\x1a\n" + "\x16ENCRYPTION_KEY_NODE_ID\x10\x01\x12\x16\n" + "\x12ENCRYPTION_KEY_KMS\x10\x02\x12\x16\n" + "\x12ENCRYPTION_KEY_TPM\x10\x03*Z\n" + "\x1bBlockEncryptionProviderType\x12\x1c\n" + "\x18ENCRYPTION_PROVIDER_NONE\x10\x00\x12\x1d\n" + "\x19ENCRYPTION_PROVIDER_LUKS2\x10\x01*\xd1\x01\n" + "\x13BlockFilesystemType\x12\x18\n" + "\x14FILESYSTEM_TYPE_NONE\x10\x00\x12\x17\n" + "\x13FILESYSTEM_TYPE_XFS\x10\x01\x12\x18\n" + "\x14FILESYSTEM_TYPE_VFAT\x10\x02\x12\x18\n" + "\x14FILESYSTEM_TYPE_EXT4\x10\x03\x12\x1b\n" + "\x17FILESYSTEM_TYPE_ISO9660\x10\x04\x12\x18\n" + "\x14FILESYSTEM_TYPE_SWAP\x10\x05\x12\x1c\n" + "\x18FILESYSTEM_TYPE_VIRTIOFS\x10\x06*\x83\x01\n" + "\x14BlockFSParameterType\x12\"\n" + "\x1eFS_PARAMETER_TYPE_STRING_VALUE\x10\x00\x12#\n" + "\x1fFS_PARAMETER_TYPE_BOOLEAN_VALUE\x10\x01\x12\"\n" + "\x1eFS_PARAMETER_TYPE_BINARY_VALUE\x10\x02*\xe3\x01\n" + "\x10BlockVolumePhase\x12\x18\n" + "\x14VOLUME_PHASE_WAITING\x10\x00\x12\x17\n" + "\x13VOLUME_PHASE_FAILED\x10\x01\x12\x18\n" + "\x14VOLUME_PHASE_MISSING\x10\x02\x12\x18\n" + "\x14VOLUME_PHASE_LOCATED\x10\x03\x12\x1c\n" + "\x18VOLUME_PHASE_PROVISIONED\x10\x04\x12\x19\n" + "\x15VOLUME_PHASE_PREPARED\x10\x05\x12\x16\n" + "\x12VOLUME_PHASE_READY\x10\x06\x12\x17\n" + "\x13VOLUME_PHASE_CLOSED\x10\a*\xc0\x01\n" + "\x0fBlockVolumeType\x12\x19\n" + "\x15VOLUME_TYPE_PARTITION\x10\x00\x12\x14\n" + "\x10VOLUME_TYPE_DISK\x10\x01\x12\x15\n" + "\x11VOLUME_TYPE_TMPFS\x10\x02\x12\x19\n" + "\x15VOLUME_TYPE_DIRECTORY\x10\x03\x12\x17\n" + "\x13VOLUME_TYPE_SYMLINK\x10\x04\x12\x17\n" + "\x13VOLUME_TYPE_OVERLAY\x10\x05\x12\x18\n" + "\x14VOLUME_TYPE_EXTERNAL\x10\x06*\x96\x01\n" + "\x13CriImageCacheStatus\x12\x1e\n" + "\x1aIMAGE_CACHE_STATUS_UNKNOWN\x10\x00\x12\x1f\n" + "\x1bIMAGE_CACHE_STATUS_DISABLED\x10\x01\x12 \n" + "\x1cIMAGE_CACHE_STATUS_PREPARING\x10\x02\x12\x1c\n" + "\x18IMAGE_CACHE_STATUS_READY\x10\x03*\xab\x01\n" + "\x17CriImageCacheCopyStatus\x12#\n" + "\x1fIMAGE_CACHE_COPY_STATUS_UNKNOWN\x10\x00\x12#\n" + "\x1fIMAGE_CACHE_COPY_STATUS_SKIPPED\x10\x01\x12#\n" + "\x1fIMAGE_CACHE_COPY_STATUS_PENDING\x10\x02\x12!\n" + "\x1dIMAGE_CACHE_COPY_STATUS_READY\x10\x03*S\n" + "\x11KubespanPeerState\x12\x16\n" + "\x12PEER_STATE_UNKNOWN\x10\x00\x12\x11\n" + "\rPEER_STATE_UP\x10\x01\x12\x13\n" + "\x0fPEER_STATE_DOWN\x10\x02*\x88\x01\n" + "\x12NetworkConfigLayer\x12\x12\n" + "\x0eCONFIG_DEFAULT\x10\x00\x12\x12\n" + "\x0eCONFIG_CMDLINE\x10\x01\x12\x13\n" + "\x0fCONFIG_PLATFORM\x10\x02\x12\x13\n" + "\x0fCONFIG_OPERATOR\x10\x03\x12 \n" + "\x1cCONFIG_MACHINE_CONFIGURATION\x10\x04*K\n" + "\x0fNetworkOperator\x12\x12\n" + "\x0eOPERATOR_DHCP4\x10\x00\x12\x12\n" + "\x0eOPERATOR_DHCP6\x10\x01\x12\x10\n" + "\fOPERATOR_VIP\x10\x02Bt\n" + "(dev.talos.api.resource.definitions.enumsZHgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enumsb\x06proto3" var ( file_resource_definitions_enums_enums_proto_rawDescOnce sync.Once file_resource_definitions_enums_enums_proto_rawDescData []byte ) func file_resource_definitions_enums_enums_proto_rawDescGZIP() []byte { file_resource_definitions_enums_enums_proto_rawDescOnce.Do(func() { file_resource_definitions_enums_enums_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_enums_enums_proto_rawDesc), len(file_resource_definitions_enums_enums_proto_rawDesc))) }) return file_resource_definitions_enums_enums_proto_rawDescData } var file_resource_definitions_enums_enums_proto_enumTypes = make([]protoimpl.EnumInfo, 48) var file_resource_definitions_enums_enums_proto_goTypes = []any{ (RuntimeMachineStage)(0), // 0: talos.resource.definitions.enums.RuntimeMachineStage (RuntimeSELinuxState)(0), // 1: talos.resource.definitions.enums.RuntimeSELinuxState (RuntimeFIPSState)(0), // 2: talos.resource.definitions.enums.RuntimeFIPSState (MachineType)(0), // 3: talos.resource.definitions.enums.MachineType (NethelpersAddressFlag)(0), // 4: talos.resource.definitions.enums.NethelpersAddressFlag (NethelpersAddressSortAlgorithm)(0), // 5: talos.resource.definitions.enums.NethelpersAddressSortAlgorithm (NethelpersADLACPActive)(0), // 6: talos.resource.definitions.enums.NethelpersADLACPActive (NethelpersADSelect)(0), // 7: talos.resource.definitions.enums.NethelpersADSelect (NethelpersARPAllTargets)(0), // 8: talos.resource.definitions.enums.NethelpersARPAllTargets (NethelpersARPValidate)(0), // 9: talos.resource.definitions.enums.NethelpersARPValidate (NethelpersAutoHostnameKind)(0), // 10: talos.resource.definitions.enums.NethelpersAutoHostnameKind (NethelpersBondMode)(0), // 11: talos.resource.definitions.enums.NethelpersBondMode (NethelpersBondXmitHashPolicy)(0), // 12: talos.resource.definitions.enums.NethelpersBondXmitHashPolicy (NethelpersClientIdentifier)(0), // 13: talos.resource.definitions.enums.NethelpersClientIdentifier (NethelpersConntrackState)(0), // 14: talos.resource.definitions.enums.NethelpersConntrackState (NethelpersDuplex)(0), // 15: talos.resource.definitions.enums.NethelpersDuplex (NethelpersFailOverMAC)(0), // 16: talos.resource.definitions.enums.NethelpersFailOverMAC (NethelpersFamily)(0), // 17: talos.resource.definitions.enums.NethelpersFamily (NethelpersICMPType)(0), // 18: talos.resource.definitions.enums.NethelpersICMPType (NethelpersLACPRate)(0), // 19: talos.resource.definitions.enums.NethelpersLACPRate (NethelpersLinkType)(0), // 20: talos.resource.definitions.enums.NethelpersLinkType (NethelpersMatchOperator)(0), // 21: talos.resource.definitions.enums.NethelpersMatchOperator (NethelpersNfTablesChainHook)(0), // 22: talos.resource.definitions.enums.NethelpersNfTablesChainHook (NethelpersNfTablesChainPriority)(0), // 23: talos.resource.definitions.enums.NethelpersNfTablesChainPriority (NethelpersNfTablesVerdict)(0), // 24: talos.resource.definitions.enums.NethelpersNfTablesVerdict (NethelpersOperationalState)(0), // 25: talos.resource.definitions.enums.NethelpersOperationalState (NethelpersPort)(0), // 26: talos.resource.definitions.enums.NethelpersPort (NethelpersPrimaryReselect)(0), // 27: talos.resource.definitions.enums.NethelpersPrimaryReselect (NethelpersProtocol)(0), // 28: talos.resource.definitions.enums.NethelpersProtocol (NethelpersRouteFlag)(0), // 29: talos.resource.definitions.enums.NethelpersRouteFlag (NethelpersRouteProtocol)(0), // 30: talos.resource.definitions.enums.NethelpersRouteProtocol (NethelpersRouteType)(0), // 31: talos.resource.definitions.enums.NethelpersRouteType (NethelpersRoutingRuleAction)(0), // 32: talos.resource.definitions.enums.NethelpersRoutingRuleAction (NethelpersRoutingTable)(0), // 33: talos.resource.definitions.enums.NethelpersRoutingTable (NethelpersScope)(0), // 34: talos.resource.definitions.enums.NethelpersScope (NethelpersVLANProtocol)(0), // 35: talos.resource.definitions.enums.NethelpersVLANProtocol (NethelpersWOLMode)(0), // 36: talos.resource.definitions.enums.NethelpersWOLMode (BlockEncryptionKeyType)(0), // 37: talos.resource.definitions.enums.BlockEncryptionKeyType (BlockEncryptionProviderType)(0), // 38: talos.resource.definitions.enums.BlockEncryptionProviderType (BlockFilesystemType)(0), // 39: talos.resource.definitions.enums.BlockFilesystemType (BlockFSParameterType)(0), // 40: talos.resource.definitions.enums.BlockFSParameterType (BlockVolumePhase)(0), // 41: talos.resource.definitions.enums.BlockVolumePhase (BlockVolumeType)(0), // 42: talos.resource.definitions.enums.BlockVolumeType (CriImageCacheStatus)(0), // 43: talos.resource.definitions.enums.CriImageCacheStatus (CriImageCacheCopyStatus)(0), // 44: talos.resource.definitions.enums.CriImageCacheCopyStatus (KubespanPeerState)(0), // 45: talos.resource.definitions.enums.KubespanPeerState (NetworkConfigLayer)(0), // 46: talos.resource.definitions.enums.NetworkConfigLayer (NetworkOperator)(0), // 47: talos.resource.definitions.enums.NetworkOperator } var file_resource_definitions_enums_enums_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_definitions_enums_enums_proto_init() } func file_resource_definitions_enums_enums_proto_init() { if File_resource_definitions_enums_enums_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_enums_enums_proto_rawDesc), len(file_resource_definitions_enums_enums_proto_rawDesc)), NumEnums: 48, NumMessages: 0, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_enums_enums_proto_goTypes, DependencyIndexes: file_resource_definitions_enums_enums_proto_depIdxs, EnumInfos: file_resource_definitions_enums_enums_proto_enumTypes, }.Build() File_resource_definitions_enums_enums_proto = out.File file_resource_definitions_enums_enums_proto_goTypes = nil file_resource_definitions_enums_enums_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/etcd/etcd.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/etcd/etcd.proto package etcd import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ArgValues represents values for a command line argument which can be specified multiple times. type ArgValues struct { state protoimpl.MessageState `protogen:"open.v1"` Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ArgValues) Reset() { *x = ArgValues{} mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ArgValues) String() string { return protoimpl.X.MessageStringOf(x) } func (*ArgValues) ProtoMessage() {} func (x *ArgValues) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ArgValues.ProtoReflect.Descriptor instead. func (*ArgValues) Descriptor() ([]byte, []int) { return file_resource_definitions_etcd_etcd_proto_rawDescGZIP(), []int{0} } func (x *ArgValues) GetValues() []string { if x != nil { return x.Values } return nil } // ConfigSpec describes (some) configuration settings of etcd. type ConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` AdvertiseValidSubnets []string `protobuf:"bytes,1,rep,name=advertise_valid_subnets,json=advertiseValidSubnets,proto3" json:"advertise_valid_subnets,omitempty"` AdvertiseExcludeSubnets []string `protobuf:"bytes,2,rep,name=advertise_exclude_subnets,json=advertiseExcludeSubnets,proto3" json:"advertise_exclude_subnets,omitempty"` Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,4,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ListenValidSubnets []string `protobuf:"bytes,5,rep,name=listen_valid_subnets,json=listenValidSubnets,proto3" json:"listen_valid_subnets,omitempty"` ListenExcludeSubnets []string `protobuf:"bytes,6,rep,name=listen_exclude_subnets,json=listenExcludeSubnets,proto3" json:"listen_exclude_subnets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigSpec) Reset() { *x = ConfigSpec{} mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigSpec) ProtoMessage() {} func (x *ConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigSpec.ProtoReflect.Descriptor instead. func (*ConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_etcd_etcd_proto_rawDescGZIP(), []int{1} } func (x *ConfigSpec) GetAdvertiseValidSubnets() []string { if x != nil { return x.AdvertiseValidSubnets } return nil } func (x *ConfigSpec) GetAdvertiseExcludeSubnets() []string { if x != nil { return x.AdvertiseExcludeSubnets } return nil } func (x *ConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *ConfigSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *ConfigSpec) GetListenValidSubnets() []string { if x != nil { return x.ListenValidSubnets } return nil } func (x *ConfigSpec) GetListenExcludeSubnets() []string { if x != nil { return x.ListenExcludeSubnets } return nil } // MemberSpec holds information about an etcd member. type MemberSpec struct { state protoimpl.MessageState `protogen:"open.v1"` MemberId string `protobuf:"bytes,1,opt,name=member_id,json=memberId,proto3" json:"member_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemberSpec) Reset() { *x = MemberSpec{} mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemberSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemberSpec) ProtoMessage() {} func (x *MemberSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemberSpec.ProtoReflect.Descriptor instead. func (*MemberSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_etcd_etcd_proto_rawDescGZIP(), []int{2} } func (x *MemberSpec) GetMemberId() string { if x != nil { return x.MemberId } return "" } // PKIStatusSpec describes status of rendered secrets. type PKIStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PKIStatusSpec) Reset() { *x = PKIStatusSpec{} mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PKIStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PKIStatusSpec) ProtoMessage() {} func (x *PKIStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PKIStatusSpec.ProtoReflect.Descriptor instead. func (*PKIStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_etcd_etcd_proto_rawDescGZIP(), []int{3} } func (x *PKIStatusSpec) GetReady() bool { if x != nil { return x.Ready } return false } func (x *PKIStatusSpec) GetVersion() string { if x != nil { return x.Version } return "" } // SpecSpec describes (some) Specuration settings of etcd. type SpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` AdvertisedAddresses []*common.NetIP `protobuf:"bytes,2,rep,name=advertised_addresses,json=advertisedAddresses,proto3" json:"advertised_addresses,omitempty"` Image string `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,4,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ListenPeerAddresses []*common.NetIP `protobuf:"bytes,5,rep,name=listen_peer_addresses,json=listenPeerAddresses,proto3" json:"listen_peer_addresses,omitempty"` ListenClientAddresses []*common.NetIP `protobuf:"bytes,6,rep,name=listen_client_addresses,json=listenClientAddresses,proto3" json:"listen_client_addresses,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SpecSpec) Reset() { *x = SpecSpec{} mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SpecSpec) ProtoMessage() {} func (x *SpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_etcd_etcd_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SpecSpec.ProtoReflect.Descriptor instead. func (*SpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_etcd_etcd_proto_rawDescGZIP(), []int{4} } func (x *SpecSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *SpecSpec) GetAdvertisedAddresses() []*common.NetIP { if x != nil { return x.AdvertisedAddresses } return nil } func (x *SpecSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *SpecSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *SpecSpec) GetListenPeerAddresses() []*common.NetIP { if x != nil { return x.ListenPeerAddresses } return nil } func (x *SpecSpec) GetListenClientAddresses() []*common.NetIP { if x != nil { return x.ListenClientAddresses } return nil } var File_resource_definitions_etcd_etcd_proto protoreflect.FileDescriptor const file_resource_definitions_etcd_etcd_proto_rawDesc = "" + "\n" + "$resource/definitions/etcd/etcd.proto\x12\x1ftalos.resource.definitions.etcd\x1a\x13common/common.proto\"#\n" + "\tArgValues\x12\x16\n" + "\x06values\x18\x01 \x03(\tR\x06values\"\xc3\x03\n" + "\n" + "ConfigSpec\x126\n" + "\x17advertise_valid_subnets\x18\x01 \x03(\tR\x15advertiseValidSubnets\x12:\n" + "\x19advertise_exclude_subnets\x18\x02 \x03(\tR\x17advertiseExcludeSubnets\x12\x14\n" + "\x05image\x18\x03 \x01(\tR\x05image\x12Y\n" + "\n" + "extra_args\x18\x04 \x03(\v2:.talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntryR\textraArgs\x120\n" + "\x14listen_valid_subnets\x18\x05 \x03(\tR\x12listenValidSubnets\x124\n" + "\x16listen_exclude_subnets\x18\x06 \x03(\tR\x14listenExcludeSubnets\x1ah\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12@\n" + "\x05value\x18\x02 \x01(\v2*.talos.resource.definitions.etcd.ArgValuesR\x05value:\x028\x01\")\n" + "\n" + "MemberSpec\x12\x1b\n" + "\tmember_id\x18\x01 \x01(\tR\bmemberId\"?\n" + "\rPKIStatusSpec\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12\x18\n" + "\aversion\x18\x02 \x01(\tR\aversion\"\xc3\x03\n" + "\bSpecSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12@\n" + "\x14advertised_addresses\x18\x02 \x03(\v2\r.common.NetIPR\x13advertisedAddresses\x12\x14\n" + "\x05image\x18\x03 \x01(\tR\x05image\x12W\n" + "\n" + "extra_args\x18\x04 \x03(\v28.talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntryR\textraArgs\x12A\n" + "\x15listen_peer_addresses\x18\x05 \x03(\v2\r.common.NetIPR\x13listenPeerAddresses\x12E\n" + "\x17listen_client_addresses\x18\x06 \x03(\v2\r.common.NetIPR\x15listenClientAddresses\x1ah\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12@\n" + "\x05value\x18\x02 \x01(\v2*.talos.resource.definitions.etcd.ArgValuesR\x05value:\x028\x01Br\n" + "'dev.talos.api.resource.definitions.etcdZGgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/etcdb\x06proto3" var ( file_resource_definitions_etcd_etcd_proto_rawDescOnce sync.Once file_resource_definitions_etcd_etcd_proto_rawDescData []byte ) func file_resource_definitions_etcd_etcd_proto_rawDescGZIP() []byte { file_resource_definitions_etcd_etcd_proto_rawDescOnce.Do(func() { file_resource_definitions_etcd_etcd_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_etcd_etcd_proto_rawDesc), len(file_resource_definitions_etcd_etcd_proto_rawDesc))) }) return file_resource_definitions_etcd_etcd_proto_rawDescData } var file_resource_definitions_etcd_etcd_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_resource_definitions_etcd_etcd_proto_goTypes = []any{ (*ArgValues)(nil), // 0: talos.resource.definitions.etcd.ArgValues (*ConfigSpec)(nil), // 1: talos.resource.definitions.etcd.ConfigSpec (*MemberSpec)(nil), // 2: talos.resource.definitions.etcd.MemberSpec (*PKIStatusSpec)(nil), // 3: talos.resource.definitions.etcd.PKIStatusSpec (*SpecSpec)(nil), // 4: talos.resource.definitions.etcd.SpecSpec nil, // 5: talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntry nil, // 6: talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntry (*common.NetIP)(nil), // 7: common.NetIP } var file_resource_definitions_etcd_etcd_proto_depIdxs = []int32{ 5, // 0: talos.resource.definitions.etcd.ConfigSpec.extra_args:type_name -> talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntry 7, // 1: talos.resource.definitions.etcd.SpecSpec.advertised_addresses:type_name -> common.NetIP 6, // 2: talos.resource.definitions.etcd.SpecSpec.extra_args:type_name -> talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntry 7, // 3: talos.resource.definitions.etcd.SpecSpec.listen_peer_addresses:type_name -> common.NetIP 7, // 4: talos.resource.definitions.etcd.SpecSpec.listen_client_addresses:type_name -> common.NetIP 0, // 5: talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.etcd.ArgValues 0, // 6: talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.etcd.ArgValues 7, // [7:7] is the sub-list for method output_type 7, // [7:7] is the sub-list for method input_type 7, // [7:7] is the sub-list for extension type_name 7, // [7:7] is the sub-list for extension extendee 0, // [0:7] is the sub-list for field type_name } func init() { file_resource_definitions_etcd_etcd_proto_init() } func file_resource_definitions_etcd_etcd_proto_init() { if File_resource_definitions_etcd_etcd_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_etcd_etcd_proto_rawDesc), len(file_resource_definitions_etcd_etcd_proto_rawDesc)), NumEnums: 0, NumMessages: 7, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_etcd_etcd_proto_goTypes, DependencyIndexes: file_resource_definitions_etcd_etcd_proto_depIdxs, MessageInfos: file_resource_definitions_etcd_etcd_proto_msgTypes, }.Build() File_resource_definitions_etcd_etcd_proto = out.File file_resource_definitions_etcd_etcd_proto_goTypes = nil file_resource_definitions_etcd_etcd_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/etcd/etcd_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/etcd/etcd.proto package etcd import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ArgValues) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ArgValues) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ArgValues) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Values) > 0 { for iNdEx := len(m.Values) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Values[iNdEx]) copy(dAtA[i:], m.Values[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Values[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ListenExcludeSubnets) > 0 { for iNdEx := len(m.ListenExcludeSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ListenExcludeSubnets[iNdEx]) copy(dAtA[i:], m.ListenExcludeSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ListenExcludeSubnets[iNdEx]))) i-- dAtA[i] = 0x32 } } if len(m.ListenValidSubnets) > 0 { for iNdEx := len(m.ListenValidSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ListenValidSubnets[iNdEx]) copy(dAtA[i:], m.ListenValidSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ListenValidSubnets[iNdEx]))) i-- dAtA[i] = 0x2a } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0x1a } if len(m.AdvertiseExcludeSubnets) > 0 { for iNdEx := len(m.AdvertiseExcludeSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AdvertiseExcludeSubnets[iNdEx]) copy(dAtA[i:], m.AdvertiseExcludeSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AdvertiseExcludeSubnets[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.AdvertiseValidSubnets) > 0 { for iNdEx := len(m.AdvertiseValidSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AdvertiseValidSubnets[iNdEx]) copy(dAtA[i:], m.AdvertiseValidSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AdvertiseValidSubnets[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *MemberSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemberSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemberSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MemberId) > 0 { i -= len(m.MemberId) copy(dAtA[i:], m.MemberId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MemberId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PKIStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PKIStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PKIStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x12 } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *SpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ListenClientAddresses) > 0 { for iNdEx := len(m.ListenClientAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ListenClientAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ListenClientAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x32 } } if len(m.ListenPeerAddresses) > 0 { for iNdEx := len(m.ListenPeerAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ListenPeerAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ListenPeerAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0x1a } if len(m.AdvertisedAddresses) > 0 { for iNdEx := len(m.AdvertisedAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AdvertisedAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AdvertisedAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ArgValues) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Values) > 0 { for _, s := range m.Values { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.AdvertiseValidSubnets) > 0 { for _, s := range m.AdvertiseValidSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.AdvertiseExcludeSubnets) > 0 { for _, s := range m.AdvertiseExcludeSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ListenValidSubnets) > 0 { for _, s := range m.ListenValidSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ListenExcludeSubnets) > 0 { for _, s := range m.ListenExcludeSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MemberSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.MemberId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PKIStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *SpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AdvertisedAddresses) > 0 { for _, e := range m.AdvertisedAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ListenPeerAddresses) > 0 { for _, e := range m.ListenPeerAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ListenClientAddresses) > 0 { for _, e := range m.ListenClientAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ArgValues) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ArgValues: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ArgValues: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Values = append(m.Values, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdvertiseValidSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdvertiseValidSubnets = append(m.AdvertiseValidSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdvertiseExcludeSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdvertiseExcludeSubnets = append(m.AdvertiseExcludeSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenValidSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenValidSubnets = append(m.ListenValidSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenExcludeSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenExcludeSubnets = append(m.ListenExcludeSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MemberSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemberSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemberSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MemberId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MemberId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PKIStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PKIStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PKIStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdvertisedAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdvertisedAddresses = append(m.AdvertisedAddresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.AdvertisedAddresses[len(m.AdvertisedAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AdvertisedAddresses[len(m.AdvertisedAddresses)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenPeerAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenPeerAddresses = append(m.ListenPeerAddresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.ListenPeerAddresses[len(m.ListenPeerAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ListenPeerAddresses[len(m.ListenPeerAddresses)-1]); err != nil { return err } } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenClientAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenClientAddresses = append(m.ListenClientAddresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.ListenClientAddresses[len(m.ListenClientAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ListenClientAddresses[len(m.ListenClientAddresses)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/extensions/extensions.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/extensions/extensions.proto package extensions import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // Compatibility describes extension compatibility. type Compatibility struct { state protoimpl.MessageState `protogen:"open.v1"` Talos *Constraint `protobuf:"bytes,1,opt,name=talos,proto3" json:"talos,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Compatibility) Reset() { *x = Compatibility{} mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Compatibility) String() string { return protoimpl.X.MessageStringOf(x) } func (*Compatibility) ProtoMessage() {} func (x *Compatibility) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Compatibility.ProtoReflect.Descriptor instead. func (*Compatibility) Descriptor() ([]byte, []int) { return file_resource_definitions_extensions_extensions_proto_rawDescGZIP(), []int{0} } func (x *Compatibility) GetTalos() *Constraint { if x != nil { return x.Talos } return nil } // Constraint describes compatibility constraint. type Constraint struct { state protoimpl.MessageState `protogen:"open.v1"` Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Constraint) Reset() { *x = Constraint{} mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Constraint) String() string { return protoimpl.X.MessageStringOf(x) } func (*Constraint) ProtoMessage() {} func (x *Constraint) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Constraint.ProtoReflect.Descriptor instead. func (*Constraint) Descriptor() ([]byte, []int) { return file_resource_definitions_extensions_extensions_proto_rawDescGZIP(), []int{1} } func (x *Constraint) GetVersion() string { if x != nil { return x.Version } return "" } // Layer defines overlay mount layer. type Layer struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` Metadata *Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Layer) Reset() { *x = Layer{} mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Layer) String() string { return protoimpl.X.MessageStringOf(x) } func (*Layer) ProtoMessage() {} func (x *Layer) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Layer.ProtoReflect.Descriptor instead. func (*Layer) Descriptor() ([]byte, []int) { return file_resource_definitions_extensions_extensions_proto_rawDescGZIP(), []int{2} } func (x *Layer) GetImage() string { if x != nil { return x.Image } return "" } func (x *Layer) GetMetadata() *Metadata { if x != nil { return x.Metadata } return nil } // Metadata describes base extension metadata. type Metadata struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` Author string `protobuf:"bytes,3,opt,name=author,proto3" json:"author,omitempty"` Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` Compatibility *Compatibility `protobuf:"bytes,5,opt,name=compatibility,proto3" json:"compatibility,omitempty"` ExtraInfo string `protobuf:"bytes,6,opt,name=extra_info,json=extraInfo,proto3" json:"extra_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Metadata) Reset() { *x = Metadata{} mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Metadata) String() string { return protoimpl.X.MessageStringOf(x) } func (*Metadata) ProtoMessage() {} func (x *Metadata) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_extensions_extensions_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Metadata.ProtoReflect.Descriptor instead. func (*Metadata) Descriptor() ([]byte, []int) { return file_resource_definitions_extensions_extensions_proto_rawDescGZIP(), []int{3} } func (x *Metadata) GetName() string { if x != nil { return x.Name } return "" } func (x *Metadata) GetVersion() string { if x != nil { return x.Version } return "" } func (x *Metadata) GetAuthor() string { if x != nil { return x.Author } return "" } func (x *Metadata) GetDescription() string { if x != nil { return x.Description } return "" } func (x *Metadata) GetCompatibility() *Compatibility { if x != nil { return x.Compatibility } return nil } func (x *Metadata) GetExtraInfo() string { if x != nil { return x.ExtraInfo } return "" } var File_resource_definitions_extensions_extensions_proto protoreflect.FileDescriptor const file_resource_definitions_extensions_extensions_proto_rawDesc = "" + "\n" + "0resource/definitions/extensions/extensions.proto\x12%talos.resource.definitions.extensions\"X\n" + "\rCompatibility\x12G\n" + "\x05talos\x18\x01 \x01(\v21.talos.resource.definitions.extensions.ConstraintR\x05talos\"&\n" + "\n" + "Constraint\x12\x18\n" + "\aversion\x18\x01 \x01(\tR\aversion\"j\n" + "\x05Layer\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12K\n" + "\bmetadata\x18\x02 \x01(\v2/.talos.resource.definitions.extensions.MetadataR\bmetadata\"\xed\x01\n" + "\bMetadata\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + "\aversion\x18\x02 \x01(\tR\aversion\x12\x16\n" + "\x06author\x18\x03 \x01(\tR\x06author\x12 \n" + "\vdescription\x18\x04 \x01(\tR\vdescription\x12Z\n" + "\rcompatibility\x18\x05 \x01(\v24.talos.resource.definitions.extensions.CompatibilityR\rcompatibility\x12\x1d\n" + "\n" + "extra_info\x18\x06 \x01(\tR\textraInfoB~\n" + "-dev.talos.api.resource.definitions.extensionsZMgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/extensionsb\x06proto3" var ( file_resource_definitions_extensions_extensions_proto_rawDescOnce sync.Once file_resource_definitions_extensions_extensions_proto_rawDescData []byte ) func file_resource_definitions_extensions_extensions_proto_rawDescGZIP() []byte { file_resource_definitions_extensions_extensions_proto_rawDescOnce.Do(func() { file_resource_definitions_extensions_extensions_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_extensions_extensions_proto_rawDesc), len(file_resource_definitions_extensions_extensions_proto_rawDesc))) }) return file_resource_definitions_extensions_extensions_proto_rawDescData } var file_resource_definitions_extensions_extensions_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_resource_definitions_extensions_extensions_proto_goTypes = []any{ (*Compatibility)(nil), // 0: talos.resource.definitions.extensions.Compatibility (*Constraint)(nil), // 1: talos.resource.definitions.extensions.Constraint (*Layer)(nil), // 2: talos.resource.definitions.extensions.Layer (*Metadata)(nil), // 3: talos.resource.definitions.extensions.Metadata } var file_resource_definitions_extensions_extensions_proto_depIdxs = []int32{ 1, // 0: talos.resource.definitions.extensions.Compatibility.talos:type_name -> talos.resource.definitions.extensions.Constraint 3, // 1: talos.resource.definitions.extensions.Layer.metadata:type_name -> talos.resource.definitions.extensions.Metadata 0, // 2: talos.resource.definitions.extensions.Metadata.compatibility:type_name -> talos.resource.definitions.extensions.Compatibility 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_resource_definitions_extensions_extensions_proto_init() } func file_resource_definitions_extensions_extensions_proto_init() { if File_resource_definitions_extensions_extensions_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_extensions_extensions_proto_rawDesc), len(file_resource_definitions_extensions_extensions_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_extensions_extensions_proto_goTypes, DependencyIndexes: file_resource_definitions_extensions_extensions_proto_depIdxs, MessageInfos: file_resource_definitions_extensions_extensions_proto_msgTypes, }.Build() File_resource_definitions_extensions_extensions_proto = out.File file_resource_definitions_extensions_extensions_proto_goTypes = nil file_resource_definitions_extensions_extensions_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/extensions/extensions_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/extensions/extensions.proto package extensions import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *Compatibility) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Compatibility) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Compatibility) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Talos != nil { size, err := m.Talos.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Constraint) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Constraint) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Constraint) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Layer) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Layer) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Layer) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { size, err := m.Metadata.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Metadata) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Metadata) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Metadata) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExtraInfo) > 0 { i -= len(m.ExtraInfo) copy(dAtA[i:], m.ExtraInfo) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ExtraInfo))) i-- dAtA[i] = 0x32 } if m.Compatibility != nil { size, err := m.Compatibility.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if len(m.Description) > 0 { i -= len(m.Description) copy(dAtA[i:], m.Description) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Description))) i-- dAtA[i] = 0x22 } if len(m.Author) > 0 { i -= len(m.Author) copy(dAtA[i:], m.Author) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Author))) i-- dAtA[i] = 0x1a } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Compatibility) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Talos != nil { l = m.Talos.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Constraint) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Layer) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Metadata != nil { l = m.Metadata.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Metadata) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Author) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Description) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Compatibility != nil { l = m.Compatibility.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ExtraInfo) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Compatibility) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Compatibility: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Compatibility: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Talos", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Talos == nil { m.Talos = &Constraint{} } if err := m.Talos.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Constraint) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Constraint: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Constraint: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Layer) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Layer: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Layer: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &Metadata{} } if err := m.Metadata.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Metadata) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Metadata: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Metadata: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Author", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Author = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Description", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Description = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Compatibility", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Compatibility == nil { m.Compatibility = &Compatibility{} } if err := m.Compatibility.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraInfo", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraInfo = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/files/files.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/files/files.proto package files import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // EtcFileSpecSpec describes status of rendered secrets. type EtcFileSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Contents []byte `protobuf:"bytes,1,opt,name=contents,proto3" json:"contents,omitempty"` Mode uint32 `protobuf:"varint,2,opt,name=mode,proto3" json:"mode,omitempty"` SelinuxLabel string `protobuf:"bytes,3,opt,name=selinux_label,json=selinuxLabel,proto3" json:"selinux_label,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcFileSpecSpec) Reset() { *x = EtcFileSpecSpec{} mi := &file_resource_definitions_files_files_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcFileSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcFileSpecSpec) ProtoMessage() {} func (x *EtcFileSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_files_files_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcFileSpecSpec.ProtoReflect.Descriptor instead. func (*EtcFileSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_files_files_proto_rawDescGZIP(), []int{0} } func (x *EtcFileSpecSpec) GetContents() []byte { if x != nil { return x.Contents } return nil } func (x *EtcFileSpecSpec) GetMode() uint32 { if x != nil { return x.Mode } return 0 } func (x *EtcFileSpecSpec) GetSelinuxLabel() string { if x != nil { return x.SelinuxLabel } return "" } // EtcFileStatusSpec describes status of rendered secrets. type EtcFileStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` SpecVersion string `protobuf:"bytes,1,opt,name=spec_version,json=specVersion,proto3" json:"spec_version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcFileStatusSpec) Reset() { *x = EtcFileStatusSpec{} mi := &file_resource_definitions_files_files_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcFileStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcFileStatusSpec) ProtoMessage() {} func (x *EtcFileStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_files_files_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcFileStatusSpec.ProtoReflect.Descriptor instead. func (*EtcFileStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_files_files_proto_rawDescGZIP(), []int{1} } func (x *EtcFileStatusSpec) GetSpecVersion() string { if x != nil { return x.SpecVersion } return "" } var File_resource_definitions_files_files_proto protoreflect.FileDescriptor const file_resource_definitions_files_files_proto_rawDesc = "" + "\n" + "&resource/definitions/files/files.proto\x12 talos.resource.definitions.files\"f\n" + "\x0fEtcFileSpecSpec\x12\x1a\n" + "\bcontents\x18\x01 \x01(\fR\bcontents\x12\x12\n" + "\x04mode\x18\x02 \x01(\rR\x04mode\x12#\n" + "\rselinux_label\x18\x03 \x01(\tR\fselinuxLabel\"6\n" + "\x11EtcFileStatusSpec\x12!\n" + "\fspec_version\x18\x01 \x01(\tR\vspecVersionBt\n" + "(dev.talos.api.resource.definitions.filesZHgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/filesb\x06proto3" var ( file_resource_definitions_files_files_proto_rawDescOnce sync.Once file_resource_definitions_files_files_proto_rawDescData []byte ) func file_resource_definitions_files_files_proto_rawDescGZIP() []byte { file_resource_definitions_files_files_proto_rawDescOnce.Do(func() { file_resource_definitions_files_files_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_files_files_proto_rawDesc), len(file_resource_definitions_files_files_proto_rawDesc))) }) return file_resource_definitions_files_files_proto_rawDescData } var file_resource_definitions_files_files_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_resource_definitions_files_files_proto_goTypes = []any{ (*EtcFileSpecSpec)(nil), // 0: talos.resource.definitions.files.EtcFileSpecSpec (*EtcFileStatusSpec)(nil), // 1: talos.resource.definitions.files.EtcFileStatusSpec } var file_resource_definitions_files_files_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_definitions_files_files_proto_init() } func file_resource_definitions_files_files_proto_init() { if File_resource_definitions_files_files_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_files_files_proto_rawDesc), len(file_resource_definitions_files_files_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_files_files_proto_goTypes, DependencyIndexes: file_resource_definitions_files_files_proto_depIdxs, MessageInfos: file_resource_definitions_files_files_proto_msgTypes, }.Build() File_resource_definitions_files_files_proto = out.File file_resource_definitions_files_files_proto_goTypes = nil file_resource_definitions_files_files_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/files/files_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/files/files.proto package files import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *EtcFileSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcFileSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcFileSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SelinuxLabel) > 0 { i -= len(m.SelinuxLabel) copy(dAtA[i:], m.SelinuxLabel) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SelinuxLabel))) i-- dAtA[i] = 0x1a } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x10 } if len(m.Contents) > 0 { i -= len(m.Contents) copy(dAtA[i:], m.Contents) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Contents))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcFileStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcFileStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcFileStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SpecVersion) > 0 { i -= len(m.SpecVersion) copy(dAtA[i:], m.SpecVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SpecVersion))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcFileSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Contents) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } l = len(m.SelinuxLabel) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcFileStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.SpecVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcFileSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcFileSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcFileSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Contents", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Contents = append(m.Contents[:0], dAtA[iNdEx:postIndex]...) if m.Contents == nil { m.Contents = []byte{} } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SelinuxLabel", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SelinuxLabel = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcFileStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcFileStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcFileStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SpecVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SpecVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/hardware/hardware.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/hardware/hardware.proto package hardware import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // MemoryModuleSpec represents a single Memory. type MemoryModuleSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Size uint32 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` DeviceLocator string `protobuf:"bytes,2,opt,name=device_locator,json=deviceLocator,proto3" json:"device_locator,omitempty"` BankLocator string `protobuf:"bytes,3,opt,name=bank_locator,json=bankLocator,proto3" json:"bank_locator,omitempty"` Speed uint32 `protobuf:"varint,4,opt,name=speed,proto3" json:"speed,omitempty"` Manufacturer string `protobuf:"bytes,5,opt,name=manufacturer,proto3" json:"manufacturer,omitempty"` SerialNumber string `protobuf:"bytes,6,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty"` AssetTag string `protobuf:"bytes,7,opt,name=asset_tag,json=assetTag,proto3" json:"asset_tag,omitempty"` ProductName string `protobuf:"bytes,8,opt,name=product_name,json=productName,proto3" json:"product_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemoryModuleSpec) Reset() { *x = MemoryModuleSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemoryModuleSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemoryModuleSpec) ProtoMessage() {} func (x *MemoryModuleSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemoryModuleSpec.ProtoReflect.Descriptor instead. func (*MemoryModuleSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{0} } func (x *MemoryModuleSpec) GetSize() uint32 { if x != nil { return x.Size } return 0 } func (x *MemoryModuleSpec) GetDeviceLocator() string { if x != nil { return x.DeviceLocator } return "" } func (x *MemoryModuleSpec) GetBankLocator() string { if x != nil { return x.BankLocator } return "" } func (x *MemoryModuleSpec) GetSpeed() uint32 { if x != nil { return x.Speed } return 0 } func (x *MemoryModuleSpec) GetManufacturer() string { if x != nil { return x.Manufacturer } return "" } func (x *MemoryModuleSpec) GetSerialNumber() string { if x != nil { return x.SerialNumber } return "" } func (x *MemoryModuleSpec) GetAssetTag() string { if x != nil { return x.AssetTag } return "" } func (x *MemoryModuleSpec) GetProductName() string { if x != nil { return x.ProductName } return "" } // PCIDeviceSpec represents a single processor. type PCIDeviceSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Class string `protobuf:"bytes,1,opt,name=class,proto3" json:"class,omitempty"` Subclass string `protobuf:"bytes,2,opt,name=subclass,proto3" json:"subclass,omitempty"` Vendor string `protobuf:"bytes,3,opt,name=vendor,proto3" json:"vendor,omitempty"` Product string `protobuf:"bytes,4,opt,name=product,proto3" json:"product,omitempty"` ClassId string `protobuf:"bytes,5,opt,name=class_id,json=classId,proto3" json:"class_id,omitempty"` SubclassId string `protobuf:"bytes,6,opt,name=subclass_id,json=subclassId,proto3" json:"subclass_id,omitempty"` VendorId string `protobuf:"bytes,7,opt,name=vendor_id,json=vendorId,proto3" json:"vendor_id,omitempty"` ProductId string `protobuf:"bytes,8,opt,name=product_id,json=productId,proto3" json:"product_id,omitempty"` Driver string `protobuf:"bytes,9,opt,name=driver,proto3" json:"driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PCIDeviceSpec) Reset() { *x = PCIDeviceSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PCIDeviceSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PCIDeviceSpec) ProtoMessage() {} func (x *PCIDeviceSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PCIDeviceSpec.ProtoReflect.Descriptor instead. func (*PCIDeviceSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{1} } func (x *PCIDeviceSpec) GetClass() string { if x != nil { return x.Class } return "" } func (x *PCIDeviceSpec) GetSubclass() string { if x != nil { return x.Subclass } return "" } func (x *PCIDeviceSpec) GetVendor() string { if x != nil { return x.Vendor } return "" } func (x *PCIDeviceSpec) GetProduct() string { if x != nil { return x.Product } return "" } func (x *PCIDeviceSpec) GetClassId() string { if x != nil { return x.ClassId } return "" } func (x *PCIDeviceSpec) GetSubclassId() string { if x != nil { return x.SubclassId } return "" } func (x *PCIDeviceSpec) GetVendorId() string { if x != nil { return x.VendorId } return "" } func (x *PCIDeviceSpec) GetProductId() string { if x != nil { return x.ProductId } return "" } func (x *PCIDeviceSpec) GetDriver() string { if x != nil { return x.Driver } return "" } // PCIDriverRebindConfigSpec describes PCI rebind configuration. type PCIDriverRebindConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Pciid string `protobuf:"bytes,1,opt,name=pciid,proto3" json:"pciid,omitempty"` TargetDriver string `protobuf:"bytes,2,opt,name=target_driver,json=targetDriver,proto3" json:"target_driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PCIDriverRebindConfigSpec) Reset() { *x = PCIDriverRebindConfigSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PCIDriverRebindConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PCIDriverRebindConfigSpec) ProtoMessage() {} func (x *PCIDriverRebindConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PCIDriverRebindConfigSpec.ProtoReflect.Descriptor instead. func (*PCIDriverRebindConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{2} } func (x *PCIDriverRebindConfigSpec) GetPciid() string { if x != nil { return x.Pciid } return "" } func (x *PCIDriverRebindConfigSpec) GetTargetDriver() string { if x != nil { return x.TargetDriver } return "" } // PCIDriverRebindStatusSpec describes status of rebinded drivers. type PCIDriverRebindStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Pciid string `protobuf:"bytes,1,opt,name=pciid,proto3" json:"pciid,omitempty"` TargetDriver string `protobuf:"bytes,2,opt,name=target_driver,json=targetDriver,proto3" json:"target_driver,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PCIDriverRebindStatusSpec) Reset() { *x = PCIDriverRebindStatusSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PCIDriverRebindStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PCIDriverRebindStatusSpec) ProtoMessage() {} func (x *PCIDriverRebindStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PCIDriverRebindStatusSpec.ProtoReflect.Descriptor instead. func (*PCIDriverRebindStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{3} } func (x *PCIDriverRebindStatusSpec) GetPciid() string { if x != nil { return x.Pciid } return "" } func (x *PCIDriverRebindStatusSpec) GetTargetDriver() string { if x != nil { return x.TargetDriver } return "" } // ProcessorSpec represents a single processor. type ProcessorSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Socket string `protobuf:"bytes,1,opt,name=socket,proto3" json:"socket,omitempty"` Manufacturer string `protobuf:"bytes,2,opt,name=manufacturer,proto3" json:"manufacturer,omitempty"` ProductName string `protobuf:"bytes,3,opt,name=product_name,json=productName,proto3" json:"product_name,omitempty"` MaxSpeed uint32 `protobuf:"varint,4,opt,name=max_speed,json=maxSpeed,proto3" json:"max_speed,omitempty"` BootSpeed uint32 `protobuf:"varint,5,opt,name=boot_speed,json=bootSpeed,proto3" json:"boot_speed,omitempty"` Status uint32 `protobuf:"varint,6,opt,name=status,proto3" json:"status,omitempty"` SerialNumber string `protobuf:"bytes,7,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty"` AssetTag string `protobuf:"bytes,8,opt,name=asset_tag,json=assetTag,proto3" json:"asset_tag,omitempty"` PartNumber string `protobuf:"bytes,9,opt,name=part_number,json=partNumber,proto3" json:"part_number,omitempty"` CoreCount uint32 `protobuf:"varint,10,opt,name=core_count,json=coreCount,proto3" json:"core_count,omitempty"` CoreEnabled uint32 `protobuf:"varint,11,opt,name=core_enabled,json=coreEnabled,proto3" json:"core_enabled,omitempty"` ThreadCount uint32 `protobuf:"varint,12,opt,name=thread_count,json=threadCount,proto3" json:"thread_count,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProcessorSpec) Reset() { *x = ProcessorSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProcessorSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProcessorSpec) ProtoMessage() {} func (x *ProcessorSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProcessorSpec.ProtoReflect.Descriptor instead. func (*ProcessorSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{4} } func (x *ProcessorSpec) GetSocket() string { if x != nil { return x.Socket } return "" } func (x *ProcessorSpec) GetManufacturer() string { if x != nil { return x.Manufacturer } return "" } func (x *ProcessorSpec) GetProductName() string { if x != nil { return x.ProductName } return "" } func (x *ProcessorSpec) GetMaxSpeed() uint32 { if x != nil { return x.MaxSpeed } return 0 } func (x *ProcessorSpec) GetBootSpeed() uint32 { if x != nil { return x.BootSpeed } return 0 } func (x *ProcessorSpec) GetStatus() uint32 { if x != nil { return x.Status } return 0 } func (x *ProcessorSpec) GetSerialNumber() string { if x != nil { return x.SerialNumber } return "" } func (x *ProcessorSpec) GetAssetTag() string { if x != nil { return x.AssetTag } return "" } func (x *ProcessorSpec) GetPartNumber() string { if x != nil { return x.PartNumber } return "" } func (x *ProcessorSpec) GetCoreCount() uint32 { if x != nil { return x.CoreCount } return 0 } func (x *ProcessorSpec) GetCoreEnabled() uint32 { if x != nil { return x.CoreEnabled } return 0 } func (x *ProcessorSpec) GetThreadCount() uint32 { if x != nil { return x.ThreadCount } return 0 } // SystemInformationSpec represents the system information obtained from smbios. type SystemInformationSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Manufacturer string `protobuf:"bytes,1,opt,name=manufacturer,proto3" json:"manufacturer,omitempty"` ProductName string `protobuf:"bytes,2,opt,name=product_name,json=productName,proto3" json:"product_name,omitempty"` Version string `protobuf:"bytes,3,opt,name=version,proto3" json:"version,omitempty"` SerialNumber string `protobuf:"bytes,4,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty"` Uuid string `protobuf:"bytes,5,opt,name=uuid,proto3" json:"uuid,omitempty"` WakeUpType string `protobuf:"bytes,6,opt,name=wake_up_type,json=wakeUpType,proto3" json:"wake_up_type,omitempty"` SkuNumber string `protobuf:"bytes,7,opt,name=sku_number,json=skuNumber,proto3" json:"sku_number,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SystemInformationSpec) Reset() { *x = SystemInformationSpec{} mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SystemInformationSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SystemInformationSpec) ProtoMessage() {} func (x *SystemInformationSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_hardware_hardware_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SystemInformationSpec.ProtoReflect.Descriptor instead. func (*SystemInformationSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_hardware_hardware_proto_rawDescGZIP(), []int{5} } func (x *SystemInformationSpec) GetManufacturer() string { if x != nil { return x.Manufacturer } return "" } func (x *SystemInformationSpec) GetProductName() string { if x != nil { return x.ProductName } return "" } func (x *SystemInformationSpec) GetVersion() string { if x != nil { return x.Version } return "" } func (x *SystemInformationSpec) GetSerialNumber() string { if x != nil { return x.SerialNumber } return "" } func (x *SystemInformationSpec) GetUuid() string { if x != nil { return x.Uuid } return "" } func (x *SystemInformationSpec) GetWakeUpType() string { if x != nil { return x.WakeUpType } return "" } func (x *SystemInformationSpec) GetSkuNumber() string { if x != nil { return x.SkuNumber } return "" } var File_resource_definitions_hardware_hardware_proto protoreflect.FileDescriptor const file_resource_definitions_hardware_hardware_proto_rawDesc = "" + "\n" + ",resource/definitions/hardware/hardware.proto\x12#talos.resource.definitions.hardware\"\x8f\x02\n" + "\x10MemoryModuleSpec\x12\x12\n" + "\x04size\x18\x01 \x01(\rR\x04size\x12%\n" + "\x0edevice_locator\x18\x02 \x01(\tR\rdeviceLocator\x12!\n" + "\fbank_locator\x18\x03 \x01(\tR\vbankLocator\x12\x14\n" + "\x05speed\x18\x04 \x01(\rR\x05speed\x12\"\n" + "\fmanufacturer\x18\x05 \x01(\tR\fmanufacturer\x12#\n" + "\rserial_number\x18\x06 \x01(\tR\fserialNumber\x12\x1b\n" + "\tasset_tag\x18\a \x01(\tR\bassetTag\x12!\n" + "\fproduct_name\x18\b \x01(\tR\vproductName\"\x83\x02\n" + "\rPCIDeviceSpec\x12\x14\n" + "\x05class\x18\x01 \x01(\tR\x05class\x12\x1a\n" + "\bsubclass\x18\x02 \x01(\tR\bsubclass\x12\x16\n" + "\x06vendor\x18\x03 \x01(\tR\x06vendor\x12\x18\n" + "\aproduct\x18\x04 \x01(\tR\aproduct\x12\x19\n" + "\bclass_id\x18\x05 \x01(\tR\aclassId\x12\x1f\n" + "\vsubclass_id\x18\x06 \x01(\tR\n" + "subclassId\x12\x1b\n" + "\tvendor_id\x18\a \x01(\tR\bvendorId\x12\x1d\n" + "\n" + "product_id\x18\b \x01(\tR\tproductId\x12\x16\n" + "\x06driver\x18\t \x01(\tR\x06driver\"V\n" + "\x19PCIDriverRebindConfigSpec\x12\x14\n" + "\x05pciid\x18\x01 \x01(\tR\x05pciid\x12#\n" + "\rtarget_driver\x18\x02 \x01(\tR\ftargetDriver\"V\n" + "\x19PCIDriverRebindStatusSpec\x12\x14\n" + "\x05pciid\x18\x01 \x01(\tR\x05pciid\x12#\n" + "\rtarget_driver\x18\x02 \x01(\tR\ftargetDriver\"\x8a\x03\n" + "\rProcessorSpec\x12\x16\n" + "\x06socket\x18\x01 \x01(\tR\x06socket\x12\"\n" + "\fmanufacturer\x18\x02 \x01(\tR\fmanufacturer\x12!\n" + "\fproduct_name\x18\x03 \x01(\tR\vproductName\x12\x1b\n" + "\tmax_speed\x18\x04 \x01(\rR\bmaxSpeed\x12\x1d\n" + "\n" + "boot_speed\x18\x05 \x01(\rR\tbootSpeed\x12\x16\n" + "\x06status\x18\x06 \x01(\rR\x06status\x12#\n" + "\rserial_number\x18\a \x01(\tR\fserialNumber\x12\x1b\n" + "\tasset_tag\x18\b \x01(\tR\bassetTag\x12\x1f\n" + "\vpart_number\x18\t \x01(\tR\n" + "partNumber\x12\x1d\n" + "\n" + "core_count\x18\n" + " \x01(\rR\tcoreCount\x12!\n" + "\fcore_enabled\x18\v \x01(\rR\vcoreEnabled\x12!\n" + "\fthread_count\x18\f \x01(\rR\vthreadCount\"\xf2\x01\n" + "\x15SystemInformationSpec\x12\"\n" + "\fmanufacturer\x18\x01 \x01(\tR\fmanufacturer\x12!\n" + "\fproduct_name\x18\x02 \x01(\tR\vproductName\x12\x18\n" + "\aversion\x18\x03 \x01(\tR\aversion\x12#\n" + "\rserial_number\x18\x04 \x01(\tR\fserialNumber\x12\x12\n" + "\x04uuid\x18\x05 \x01(\tR\x04uuid\x12 \n" + "\fwake_up_type\x18\x06 \x01(\tR\n" + "wakeUpType\x12\x1d\n" + "\n" + "sku_number\x18\a \x01(\tR\tskuNumberBz\n" + "+dev.talos.api.resource.definitions.hardwareZKgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/hardwareb\x06proto3" var ( file_resource_definitions_hardware_hardware_proto_rawDescOnce sync.Once file_resource_definitions_hardware_hardware_proto_rawDescData []byte ) func file_resource_definitions_hardware_hardware_proto_rawDescGZIP() []byte { file_resource_definitions_hardware_hardware_proto_rawDescOnce.Do(func() { file_resource_definitions_hardware_hardware_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_hardware_hardware_proto_rawDesc), len(file_resource_definitions_hardware_hardware_proto_rawDesc))) }) return file_resource_definitions_hardware_hardware_proto_rawDescData } var file_resource_definitions_hardware_hardware_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_resource_definitions_hardware_hardware_proto_goTypes = []any{ (*MemoryModuleSpec)(nil), // 0: talos.resource.definitions.hardware.MemoryModuleSpec (*PCIDeviceSpec)(nil), // 1: talos.resource.definitions.hardware.PCIDeviceSpec (*PCIDriverRebindConfigSpec)(nil), // 2: talos.resource.definitions.hardware.PCIDriverRebindConfigSpec (*PCIDriverRebindStatusSpec)(nil), // 3: talos.resource.definitions.hardware.PCIDriverRebindStatusSpec (*ProcessorSpec)(nil), // 4: talos.resource.definitions.hardware.ProcessorSpec (*SystemInformationSpec)(nil), // 5: talos.resource.definitions.hardware.SystemInformationSpec } var file_resource_definitions_hardware_hardware_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_definitions_hardware_hardware_proto_init() } func file_resource_definitions_hardware_hardware_proto_init() { if File_resource_definitions_hardware_hardware_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_hardware_hardware_proto_rawDesc), len(file_resource_definitions_hardware_hardware_proto_rawDesc)), NumEnums: 0, NumMessages: 6, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_hardware_hardware_proto_goTypes, DependencyIndexes: file_resource_definitions_hardware_hardware_proto_depIdxs, MessageInfos: file_resource_definitions_hardware_hardware_proto_msgTypes, }.Build() File_resource_definitions_hardware_hardware_proto = out.File file_resource_definitions_hardware_hardware_proto_goTypes = nil file_resource_definitions_hardware_hardware_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/hardware/hardware_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/hardware/hardware.proto package hardware import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *MemoryModuleSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemoryModuleSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemoryModuleSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ProductName) > 0 { i -= len(m.ProductName) copy(dAtA[i:], m.ProductName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProductName))) i-- dAtA[i] = 0x42 } if len(m.AssetTag) > 0 { i -= len(m.AssetTag) copy(dAtA[i:], m.AssetTag) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AssetTag))) i-- dAtA[i] = 0x3a } if len(m.SerialNumber) > 0 { i -= len(m.SerialNumber) copy(dAtA[i:], m.SerialNumber) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SerialNumber))) i-- dAtA[i] = 0x32 } if len(m.Manufacturer) > 0 { i -= len(m.Manufacturer) copy(dAtA[i:], m.Manufacturer) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Manufacturer))) i-- dAtA[i] = 0x2a } if m.Speed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Speed)) i-- dAtA[i] = 0x20 } if len(m.BankLocator) > 0 { i -= len(m.BankLocator) copy(dAtA[i:], m.BankLocator) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BankLocator))) i-- dAtA[i] = 0x1a } if len(m.DeviceLocator) > 0 { i -= len(m.DeviceLocator) copy(dAtA[i:], m.DeviceLocator) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DeviceLocator))) i-- dAtA[i] = 0x12 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PCIDeviceSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PCIDeviceSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PCIDeviceSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Driver) > 0 { i -= len(m.Driver) copy(dAtA[i:], m.Driver) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Driver))) i-- dAtA[i] = 0x4a } if len(m.ProductId) > 0 { i -= len(m.ProductId) copy(dAtA[i:], m.ProductId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProductId))) i-- dAtA[i] = 0x42 } if len(m.VendorId) > 0 { i -= len(m.VendorId) copy(dAtA[i:], m.VendorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VendorId))) i-- dAtA[i] = 0x3a } if len(m.SubclassId) > 0 { i -= len(m.SubclassId) copy(dAtA[i:], m.SubclassId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubclassId))) i-- dAtA[i] = 0x32 } if len(m.ClassId) > 0 { i -= len(m.ClassId) copy(dAtA[i:], m.ClassId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClassId))) i-- dAtA[i] = 0x2a } if len(m.Product) > 0 { i -= len(m.Product) copy(dAtA[i:], m.Product) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Product))) i-- dAtA[i] = 0x22 } if len(m.Vendor) > 0 { i -= len(m.Vendor) copy(dAtA[i:], m.Vendor) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Vendor))) i-- dAtA[i] = 0x1a } if len(m.Subclass) > 0 { i -= len(m.Subclass) copy(dAtA[i:], m.Subclass) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Subclass))) i-- dAtA[i] = 0x12 } if len(m.Class) > 0 { i -= len(m.Class) copy(dAtA[i:], m.Class) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Class))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PCIDriverRebindConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PCIDriverRebindConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PCIDriverRebindConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.TargetDriver) > 0 { i -= len(m.TargetDriver) copy(dAtA[i:], m.TargetDriver) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TargetDriver))) i-- dAtA[i] = 0x12 } if len(m.Pciid) > 0 { i -= len(m.Pciid) copy(dAtA[i:], m.Pciid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Pciid))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PCIDriverRebindStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PCIDriverRebindStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PCIDriverRebindStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.TargetDriver) > 0 { i -= len(m.TargetDriver) copy(dAtA[i:], m.TargetDriver) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TargetDriver))) i-- dAtA[i] = 0x12 } if len(m.Pciid) > 0 { i -= len(m.Pciid) copy(dAtA[i:], m.Pciid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Pciid))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ProcessorSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProcessorSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProcessorSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ThreadCount != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ThreadCount)) i-- dAtA[i] = 0x60 } if m.CoreEnabled != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CoreEnabled)) i-- dAtA[i] = 0x58 } if m.CoreCount != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CoreCount)) i-- dAtA[i] = 0x50 } if len(m.PartNumber) > 0 { i -= len(m.PartNumber) copy(dAtA[i:], m.PartNumber) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PartNumber))) i-- dAtA[i] = 0x4a } if len(m.AssetTag) > 0 { i -= len(m.AssetTag) copy(dAtA[i:], m.AssetTag) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AssetTag))) i-- dAtA[i] = 0x42 } if len(m.SerialNumber) > 0 { i -= len(m.SerialNumber) copy(dAtA[i:], m.SerialNumber) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SerialNumber))) i-- dAtA[i] = 0x3a } if m.Status != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Status)) i-- dAtA[i] = 0x30 } if m.BootSpeed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.BootSpeed)) i-- dAtA[i] = 0x28 } if m.MaxSpeed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MaxSpeed)) i-- dAtA[i] = 0x20 } if len(m.ProductName) > 0 { i -= len(m.ProductName) copy(dAtA[i:], m.ProductName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProductName))) i-- dAtA[i] = 0x1a } if len(m.Manufacturer) > 0 { i -= len(m.Manufacturer) copy(dAtA[i:], m.Manufacturer) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Manufacturer))) i-- dAtA[i] = 0x12 } if len(m.Socket) > 0 { i -= len(m.Socket) copy(dAtA[i:], m.Socket) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Socket))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SystemInformationSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SystemInformationSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SystemInformationSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SkuNumber) > 0 { i -= len(m.SkuNumber) copy(dAtA[i:], m.SkuNumber) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SkuNumber))) i-- dAtA[i] = 0x3a } if len(m.WakeUpType) > 0 { i -= len(m.WakeUpType) copy(dAtA[i:], m.WakeUpType) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.WakeUpType))) i-- dAtA[i] = 0x32 } if len(m.Uuid) > 0 { i -= len(m.Uuid) copy(dAtA[i:], m.Uuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uuid))) i-- dAtA[i] = 0x2a } if len(m.SerialNumber) > 0 { i -= len(m.SerialNumber) copy(dAtA[i:], m.SerialNumber) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SerialNumber))) i-- dAtA[i] = 0x22 } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x1a } if len(m.ProductName) > 0 { i -= len(m.ProductName) copy(dAtA[i:], m.ProductName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProductName))) i-- dAtA[i] = 0x12 } if len(m.Manufacturer) > 0 { i -= len(m.Manufacturer) copy(dAtA[i:], m.Manufacturer) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Manufacturer))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MemoryModuleSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } l = len(m.DeviceLocator) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BankLocator) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Speed != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Speed)) } l = len(m.Manufacturer) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SerialNumber) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.AssetTag) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProductName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PCIDeviceSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Class) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Subclass) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Vendor) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Product) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ClassId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SubclassId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.VendorId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProductId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Driver) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PCIDriverRebindConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Pciid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.TargetDriver) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PCIDriverRebindStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Pciid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.TargetDriver) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ProcessorSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Socket) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Manufacturer) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProductName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MaxSpeed != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MaxSpeed)) } if m.BootSpeed != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.BootSpeed)) } if m.Status != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Status)) } l = len(m.SerialNumber) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.AssetTag) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PartNumber) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CoreCount != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CoreCount)) } if m.CoreEnabled != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CoreEnabled)) } if m.ThreadCount != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ThreadCount)) } n += len(m.unknownFields) return n } func (m *SystemInformationSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Manufacturer) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProductName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SerialNumber) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Uuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.WakeUpType) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SkuNumber) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MemoryModuleSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemoryModuleSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemoryModuleSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DeviceLocator", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DeviceLocator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BankLocator", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BankLocator = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Speed", wireType) } m.Speed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Speed |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Manufacturer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Manufacturer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SerialNumber", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SerialNumber = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AssetTag", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AssetTag = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProductName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProductName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PCIDeviceSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PCIDeviceSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PCIDeviceSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Class", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Class = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Subclass", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Subclass = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vendor", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Vendor = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Product", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Product = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClassId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClassId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SubclassId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SubclassId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VendorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VendorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProductId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProductId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Driver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PCIDriverRebindConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PCIDriverRebindConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PCIDriverRebindConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pciid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Pciid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetDriver", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TargetDriver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PCIDriverRebindStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PCIDriverRebindStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PCIDriverRebindStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pciid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Pciid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TargetDriver", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TargetDriver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProcessorSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProcessorSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProcessorSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Socket", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Socket = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Manufacturer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Manufacturer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProductName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProductName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MaxSpeed", wireType) } m.MaxSpeed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MaxSpeed |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BootSpeed", wireType) } m.BootSpeed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.BootSpeed |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } m.Status = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Status |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SerialNumber", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SerialNumber = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AssetTag", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AssetTag = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PartNumber", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PartNumber = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CoreCount", wireType) } m.CoreCount = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CoreCount |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CoreEnabled", wireType) } m.CoreEnabled = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CoreEnabled |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ThreadCount", wireType) } m.ThreadCount = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ThreadCount |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SystemInformationSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SystemInformationSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SystemInformationSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Manufacturer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Manufacturer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProductName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProductName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SerialNumber", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SerialNumber = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field WakeUpType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.WakeUpType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SkuNumber", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SkuNumber = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/k8s/k8s.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/k8s/k8s.proto package k8s import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb "google.golang.org/protobuf/types/known/structpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" proto "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/proto" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // APIServerConfigSpec is configuration for kube-apiserver. type APIServerConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` CloudProvider string `protobuf:"bytes,2,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` ControlPlaneEndpoint string `protobuf:"bytes,3,opt,name=control_plane_endpoint,json=controlPlaneEndpoint,proto3" json:"control_plane_endpoint,omitempty"` EtcdServers []string `protobuf:"bytes,4,rep,name=etcd_servers,json=etcdServers,proto3" json:"etcd_servers,omitempty"` LocalPort int64 `protobuf:"varint,5,opt,name=local_port,json=localPort,proto3" json:"local_port,omitempty"` ServiceCidRs []string `protobuf:"bytes,6,rep,name=service_cid_rs,json=serviceCidRs,proto3" json:"service_cid_rs,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,7,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ExtraVolumes []*ExtraVolume `protobuf:"bytes,8,rep,name=extra_volumes,json=extraVolumes,proto3" json:"extra_volumes,omitempty"` EnvironmentVariables map[string]string `protobuf:"bytes,9,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` AdvertisedAddress string `protobuf:"bytes,11,opt,name=advertised_address,json=advertisedAddress,proto3" json:"advertised_address,omitempty"` Resources *Resources `protobuf:"bytes,12,opt,name=resources,proto3" json:"resources,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *APIServerConfigSpec) Reset() { *x = APIServerConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *APIServerConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*APIServerConfigSpec) ProtoMessage() {} func (x *APIServerConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use APIServerConfigSpec.ProtoReflect.Descriptor instead. func (*APIServerConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{0} } func (x *APIServerConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *APIServerConfigSpec) GetCloudProvider() string { if x != nil { return x.CloudProvider } return "" } func (x *APIServerConfigSpec) GetControlPlaneEndpoint() string { if x != nil { return x.ControlPlaneEndpoint } return "" } func (x *APIServerConfigSpec) GetEtcdServers() []string { if x != nil { return x.EtcdServers } return nil } func (x *APIServerConfigSpec) GetLocalPort() int64 { if x != nil { return x.LocalPort } return 0 } func (x *APIServerConfigSpec) GetServiceCidRs() []string { if x != nil { return x.ServiceCidRs } return nil } func (x *APIServerConfigSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *APIServerConfigSpec) GetExtraVolumes() []*ExtraVolume { if x != nil { return x.ExtraVolumes } return nil } func (x *APIServerConfigSpec) GetEnvironmentVariables() map[string]string { if x != nil { return x.EnvironmentVariables } return nil } func (x *APIServerConfigSpec) GetAdvertisedAddress() string { if x != nil { return x.AdvertisedAddress } return "" } func (x *APIServerConfigSpec) GetResources() *Resources { if x != nil { return x.Resources } return nil } // AdmissionControlConfigSpec is configuration for kube-apiserver. type AdmissionControlConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Config []*AdmissionPluginSpec `protobuf:"bytes,1,rep,name=config,proto3" json:"config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AdmissionControlConfigSpec) Reset() { *x = AdmissionControlConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AdmissionControlConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AdmissionControlConfigSpec) ProtoMessage() {} func (x *AdmissionControlConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AdmissionControlConfigSpec.ProtoReflect.Descriptor instead. func (*AdmissionControlConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{1} } func (x *AdmissionControlConfigSpec) GetConfig() []*AdmissionPluginSpec { if x != nil { return x.Config } return nil } // AdmissionPluginSpec is a single admission plugin configuration Admission Control plugins. type AdmissionPluginSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Configuration *structpb.Struct `protobuf:"bytes,2,opt,name=configuration,proto3" json:"configuration,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AdmissionPluginSpec) Reset() { *x = AdmissionPluginSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AdmissionPluginSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AdmissionPluginSpec) ProtoMessage() {} func (x *AdmissionPluginSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AdmissionPluginSpec.ProtoReflect.Descriptor instead. func (*AdmissionPluginSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{2} } func (x *AdmissionPluginSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *AdmissionPluginSpec) GetConfiguration() *structpb.Struct { if x != nil { return x.Configuration } return nil } // ArgValues represents values for a command line argument which can be specified multiple times. type ArgValues struct { state protoimpl.MessageState `protogen:"open.v1"` Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ArgValues) Reset() { *x = ArgValues{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ArgValues) String() string { return protoimpl.X.MessageStringOf(x) } func (*ArgValues) ProtoMessage() {} func (x *ArgValues) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ArgValues.ProtoReflect.Descriptor instead. func (*ArgValues) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{3} } func (x *ArgValues) GetValues() []string { if x != nil { return x.Values } return nil } // AuditPolicyConfigSpec is audit policy configuration for kube-apiserver. type AuditPolicyConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Config *structpb.Struct `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AuditPolicyConfigSpec) Reset() { *x = AuditPolicyConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AuditPolicyConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AuditPolicyConfigSpec) ProtoMessage() {} func (x *AuditPolicyConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AuditPolicyConfigSpec.ProtoReflect.Descriptor instead. func (*AuditPolicyConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{4} } func (x *AuditPolicyConfigSpec) GetConfig() *structpb.Struct { if x != nil { return x.Config } return nil } // AuthorizationAuthorizersSpec is a configuration of authorization authorizers. type AuthorizationAuthorizersSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` Webhook *structpb.Struct `protobuf:"bytes,3,opt,name=webhook,proto3" json:"webhook,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AuthorizationAuthorizersSpec) Reset() { *x = AuthorizationAuthorizersSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AuthorizationAuthorizersSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AuthorizationAuthorizersSpec) ProtoMessage() {} func (x *AuthorizationAuthorizersSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AuthorizationAuthorizersSpec.ProtoReflect.Descriptor instead. func (*AuthorizationAuthorizersSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{5} } func (x *AuthorizationAuthorizersSpec) GetType() string { if x != nil { return x.Type } return "" } func (x *AuthorizationAuthorizersSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *AuthorizationAuthorizersSpec) GetWebhook() *structpb.Struct { if x != nil { return x.Webhook } return nil } // AuthorizationConfigSpec is authorization configuration for kube-apiserver. type AuthorizationConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` Config []*AuthorizationAuthorizersSpec `protobuf:"bytes,2,rep,name=config,proto3" json:"config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AuthorizationConfigSpec) Reset() { *x = AuthorizationConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AuthorizationConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AuthorizationConfigSpec) ProtoMessage() {} func (x *AuthorizationConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AuthorizationConfigSpec.ProtoReflect.Descriptor instead. func (*AuthorizationConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{6} } func (x *AuthorizationConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *AuthorizationConfigSpec) GetConfig() []*AuthorizationAuthorizersSpec { if x != nil { return x.Config } return nil } // BootstrapManifestsConfigSpec is configuration for bootstrap manifests. type BootstrapManifestsConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` ClusterDomain string `protobuf:"bytes,2,opt,name=cluster_domain,json=clusterDomain,proto3" json:"cluster_domain,omitempty"` PodCidRs []string `protobuf:"bytes,3,rep,name=pod_cid_rs,json=podCidRs,proto3" json:"pod_cid_rs,omitempty"` ProxyEnabled bool `protobuf:"varint,4,opt,name=proxy_enabled,json=proxyEnabled,proto3" json:"proxy_enabled,omitempty"` ProxyImage string `protobuf:"bytes,5,opt,name=proxy_image,json=proxyImage,proto3" json:"proxy_image,omitempty"` ProxyArgs []string `protobuf:"bytes,6,rep,name=proxy_args,json=proxyArgs,proto3" json:"proxy_args,omitempty"` CoreDnsEnabled bool `protobuf:"varint,7,opt,name=core_dns_enabled,json=coreDnsEnabled,proto3" json:"core_dns_enabled,omitempty"` CoreDnsImage string `protobuf:"bytes,8,opt,name=core_dns_image,json=coreDnsImage,proto3" json:"core_dns_image,omitempty"` DnsServiceIp string `protobuf:"bytes,9,opt,name=dns_service_ip,json=dnsServiceIp,proto3" json:"dns_service_ip,omitempty"` DnsServiceIPv6 string `protobuf:"bytes,10,opt,name=dns_service_i_pv6,json=dnsServiceIPv6,proto3" json:"dns_service_i_pv6,omitempty"` FlannelEnabled bool `protobuf:"varint,11,opt,name=flannel_enabled,json=flannelEnabled,proto3" json:"flannel_enabled,omitempty"` FlannelImage string `protobuf:"bytes,12,opt,name=flannel_image,json=flannelImage,proto3" json:"flannel_image,omitempty"` PodSecurityPolicyEnabled bool `protobuf:"varint,14,opt,name=pod_security_policy_enabled,json=podSecurityPolicyEnabled,proto3" json:"pod_security_policy_enabled,omitempty"` TalosApiServiceEnabled bool `protobuf:"varint,15,opt,name=talos_api_service_enabled,json=talosApiServiceEnabled,proto3" json:"talos_api_service_enabled,omitempty"` FlannelExtraArgs []string `protobuf:"bytes,16,rep,name=flannel_extra_args,json=flannelExtraArgs,proto3" json:"flannel_extra_args,omitempty"` FlannelKubeServiceHost string `protobuf:"bytes,17,opt,name=flannel_kube_service_host,json=flannelKubeServiceHost,proto3" json:"flannel_kube_service_host,omitempty"` FlannelKubeServicePort string `protobuf:"bytes,18,opt,name=flannel_kube_service_port,json=flannelKubeServicePort,proto3" json:"flannel_kube_service_port,omitempty"` FlannelKubeNetworkPoliciesEnabled bool `protobuf:"varint,19,opt,name=flannel_kube_network_policies_enabled,json=flannelKubeNetworkPoliciesEnabled,proto3" json:"flannel_kube_network_policies_enabled,omitempty"` FlannelKubeNetworkPoliciesImage string `protobuf:"bytes,20,opt,name=flannel_kube_network_policies_image,json=flannelKubeNetworkPoliciesImage,proto3" json:"flannel_kube_network_policies_image,omitempty"` CniName string `protobuf:"bytes,21,opt,name=cni_name,json=cniName,proto3" json:"cni_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BootstrapManifestsConfigSpec) Reset() { *x = BootstrapManifestsConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BootstrapManifestsConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*BootstrapManifestsConfigSpec) ProtoMessage() {} func (x *BootstrapManifestsConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BootstrapManifestsConfigSpec.ProtoReflect.Descriptor instead. func (*BootstrapManifestsConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{7} } func (x *BootstrapManifestsConfigSpec) GetServer() string { if x != nil { return x.Server } return "" } func (x *BootstrapManifestsConfigSpec) GetClusterDomain() string { if x != nil { return x.ClusterDomain } return "" } func (x *BootstrapManifestsConfigSpec) GetPodCidRs() []string { if x != nil { return x.PodCidRs } return nil } func (x *BootstrapManifestsConfigSpec) GetProxyEnabled() bool { if x != nil { return x.ProxyEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetProxyImage() string { if x != nil { return x.ProxyImage } return "" } func (x *BootstrapManifestsConfigSpec) GetProxyArgs() []string { if x != nil { return x.ProxyArgs } return nil } func (x *BootstrapManifestsConfigSpec) GetCoreDnsEnabled() bool { if x != nil { return x.CoreDnsEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetCoreDnsImage() string { if x != nil { return x.CoreDnsImage } return "" } func (x *BootstrapManifestsConfigSpec) GetDnsServiceIp() string { if x != nil { return x.DnsServiceIp } return "" } func (x *BootstrapManifestsConfigSpec) GetDnsServiceIPv6() string { if x != nil { return x.DnsServiceIPv6 } return "" } func (x *BootstrapManifestsConfigSpec) GetFlannelEnabled() bool { if x != nil { return x.FlannelEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetFlannelImage() string { if x != nil { return x.FlannelImage } return "" } func (x *BootstrapManifestsConfigSpec) GetPodSecurityPolicyEnabled() bool { if x != nil { return x.PodSecurityPolicyEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetTalosApiServiceEnabled() bool { if x != nil { return x.TalosApiServiceEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetFlannelExtraArgs() []string { if x != nil { return x.FlannelExtraArgs } return nil } func (x *BootstrapManifestsConfigSpec) GetFlannelKubeServiceHost() string { if x != nil { return x.FlannelKubeServiceHost } return "" } func (x *BootstrapManifestsConfigSpec) GetFlannelKubeServicePort() string { if x != nil { return x.FlannelKubeServicePort } return "" } func (x *BootstrapManifestsConfigSpec) GetFlannelKubeNetworkPoliciesEnabled() bool { if x != nil { return x.FlannelKubeNetworkPoliciesEnabled } return false } func (x *BootstrapManifestsConfigSpec) GetFlannelKubeNetworkPoliciesImage() string { if x != nil { return x.FlannelKubeNetworkPoliciesImage } return "" } func (x *BootstrapManifestsConfigSpec) GetCniName() string { if x != nil { return x.CniName } return "" } // ConfigStatusSpec describes status of rendered secrets. type ConfigStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigStatusSpec) Reset() { *x = ConfigStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigStatusSpec) ProtoMessage() {} func (x *ConfigStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigStatusSpec.ProtoReflect.Descriptor instead. func (*ConfigStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{8} } func (x *ConfigStatusSpec) GetReady() bool { if x != nil { return x.Ready } return false } func (x *ConfigStatusSpec) GetVersion() string { if x != nil { return x.Version } return "" } // ControllerManagerConfigSpec is configuration for kube-controller-manager. type ControllerManagerConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` CloudProvider string `protobuf:"bytes,3,opt,name=cloud_provider,json=cloudProvider,proto3" json:"cloud_provider,omitempty"` PodCidRs []string `protobuf:"bytes,4,rep,name=pod_cid_rs,json=podCidRs,proto3" json:"pod_cid_rs,omitempty"` ServiceCidRs []string `protobuf:"bytes,5,rep,name=service_cid_rs,json=serviceCidRs,proto3" json:"service_cid_rs,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,6,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ExtraVolumes []*ExtraVolume `protobuf:"bytes,7,rep,name=extra_volumes,json=extraVolumes,proto3" json:"extra_volumes,omitempty"` EnvironmentVariables map[string]string `protobuf:"bytes,8,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Resources *Resources `protobuf:"bytes,9,opt,name=resources,proto3" json:"resources,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ControllerManagerConfigSpec) Reset() { *x = ControllerManagerConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ControllerManagerConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ControllerManagerConfigSpec) ProtoMessage() {} func (x *ControllerManagerConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ControllerManagerConfigSpec.ProtoReflect.Descriptor instead. func (*ControllerManagerConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{9} } func (x *ControllerManagerConfigSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *ControllerManagerConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *ControllerManagerConfigSpec) GetCloudProvider() string { if x != nil { return x.CloudProvider } return "" } func (x *ControllerManagerConfigSpec) GetPodCidRs() []string { if x != nil { return x.PodCidRs } return nil } func (x *ControllerManagerConfigSpec) GetServiceCidRs() []string { if x != nil { return x.ServiceCidRs } return nil } func (x *ControllerManagerConfigSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *ControllerManagerConfigSpec) GetExtraVolumes() []*ExtraVolume { if x != nil { return x.ExtraVolumes } return nil } func (x *ControllerManagerConfigSpec) GetEnvironmentVariables() map[string]string { if x != nil { return x.EnvironmentVariables } return nil } func (x *ControllerManagerConfigSpec) GetResources() *Resources { if x != nil { return x.Resources } return nil } // EndpointSpec describes a list of endpoints to connect to. type EndpointSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Addresses []*common.NetIP `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` Hosts []string `protobuf:"bytes,2,rep,name=hosts,proto3" json:"hosts,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EndpointSpec) Reset() { *x = EndpointSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EndpointSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EndpointSpec) ProtoMessage() {} func (x *EndpointSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EndpointSpec.ProtoReflect.Descriptor instead. func (*EndpointSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{10} } func (x *EndpointSpec) GetAddresses() []*common.NetIP { if x != nil { return x.Addresses } return nil } func (x *EndpointSpec) GetHosts() []string { if x != nil { return x.Hosts } return nil } // ExtraManifest defines a single extra manifest to download. type ExtraManifest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` Priority string `protobuf:"bytes,3,opt,name=priority,proto3" json:"priority,omitempty"` ExtraHeaders map[string]string `protobuf:"bytes,4,rep,name=extra_headers,json=extraHeaders,proto3" json:"extra_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` InlineManifest string `protobuf:"bytes,5,opt,name=inline_manifest,json=inlineManifest,proto3" json:"inline_manifest,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraManifest) Reset() { *x = ExtraManifest{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraManifest) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraManifest) ProtoMessage() {} func (x *ExtraManifest) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraManifest.ProtoReflect.Descriptor instead. func (*ExtraManifest) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{11} } func (x *ExtraManifest) GetName() string { if x != nil { return x.Name } return "" } func (x *ExtraManifest) GetUrl() string { if x != nil { return x.Url } return "" } func (x *ExtraManifest) GetPriority() string { if x != nil { return x.Priority } return "" } func (x *ExtraManifest) GetExtraHeaders() map[string]string { if x != nil { return x.ExtraHeaders } return nil } func (x *ExtraManifest) GetInlineManifest() string { if x != nil { return x.InlineManifest } return "" } // ExtraManifestsConfigSpec is configuration for extra bootstrap manifests. type ExtraManifestsConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ExtraManifests []*ExtraManifest `protobuf:"bytes,1,rep,name=extra_manifests,json=extraManifests,proto3" json:"extra_manifests,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraManifestsConfigSpec) Reset() { *x = ExtraManifestsConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraManifestsConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraManifestsConfigSpec) ProtoMessage() {} func (x *ExtraManifestsConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraManifestsConfigSpec.ProtoReflect.Descriptor instead. func (*ExtraManifestsConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{12} } func (x *ExtraManifestsConfigSpec) GetExtraManifests() []*ExtraManifest { if x != nil { return x.ExtraManifests } return nil } // ExtraVolume is a configuration of extra volume. type ExtraVolume struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` HostPath string `protobuf:"bytes,2,opt,name=host_path,json=hostPath,proto3" json:"host_path,omitempty"` MountPath string `protobuf:"bytes,3,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"` ReadOnly bool `protobuf:"varint,4,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtraVolume) Reset() { *x = ExtraVolume{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtraVolume) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtraVolume) ProtoMessage() {} func (x *ExtraVolume) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtraVolume.ProtoReflect.Descriptor instead. func (*ExtraVolume) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{13} } func (x *ExtraVolume) GetName() string { if x != nil { return x.Name } return "" } func (x *ExtraVolume) GetHostPath() string { if x != nil { return x.HostPath } return "" } func (x *ExtraVolume) GetMountPath() string { if x != nil { return x.MountPath } return "" } func (x *ExtraVolume) GetReadOnly() bool { if x != nil { return x.ReadOnly } return false } // KubePrismConfigSpec describes KubePrismConfig data. type KubePrismConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` Port int64 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` Endpoints []*KubePrismEndpoint `protobuf:"bytes,3,rep,name=endpoints,proto3" json:"endpoints,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubePrismConfigSpec) Reset() { *x = KubePrismConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubePrismConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubePrismConfigSpec) ProtoMessage() {} func (x *KubePrismConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubePrismConfigSpec.ProtoReflect.Descriptor instead. func (*KubePrismConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{14} } func (x *KubePrismConfigSpec) GetHost() string { if x != nil { return x.Host } return "" } func (x *KubePrismConfigSpec) GetPort() int64 { if x != nil { return x.Port } return 0 } func (x *KubePrismConfigSpec) GetEndpoints() []*KubePrismEndpoint { if x != nil { return x.Endpoints } return nil } // KubePrismEndpoint holds data for control plane endpoint. type KubePrismEndpoint struct { state protoimpl.MessageState `protogen:"open.v1"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubePrismEndpoint) Reset() { *x = KubePrismEndpoint{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubePrismEndpoint) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubePrismEndpoint) ProtoMessage() {} func (x *KubePrismEndpoint) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubePrismEndpoint.ProtoReflect.Descriptor instead. func (*KubePrismEndpoint) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{15} } func (x *KubePrismEndpoint) GetHost() string { if x != nil { return x.Host } return "" } func (x *KubePrismEndpoint) GetPort() uint32 { if x != nil { return x.Port } return 0 } // KubePrismEndpointsSpec describes KubePrismEndpoints configuration. type KubePrismEndpointsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoints []*KubePrismEndpoint `protobuf:"bytes,1,rep,name=endpoints,proto3" json:"endpoints,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubePrismEndpointsSpec) Reset() { *x = KubePrismEndpointsSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubePrismEndpointsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubePrismEndpointsSpec) ProtoMessage() {} func (x *KubePrismEndpointsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubePrismEndpointsSpec.ProtoReflect.Descriptor instead. func (*KubePrismEndpointsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{16} } func (x *KubePrismEndpointsSpec) GetEndpoints() []*KubePrismEndpoint { if x != nil { return x.Endpoints } return nil } // KubePrismStatusesSpec describes KubePrismStatuses data. type KubePrismStatusesSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` Healthy bool `protobuf:"varint,2,opt,name=healthy,proto3" json:"healthy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubePrismStatusesSpec) Reset() { *x = KubePrismStatusesSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubePrismStatusesSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubePrismStatusesSpec) ProtoMessage() {} func (x *KubePrismStatusesSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubePrismStatusesSpec.ProtoReflect.Descriptor instead. func (*KubePrismStatusesSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{17} } func (x *KubePrismStatusesSpec) GetHost() string { if x != nil { return x.Host } return "" } func (x *KubePrismStatusesSpec) GetHealthy() bool { if x != nil { return x.Healthy } return false } // KubeletConfigSpec holds the source of kubelet configuration. type KubeletConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` ClusterDns []string `protobuf:"bytes,2,rep,name=cluster_dns,json=clusterDns,proto3" json:"cluster_dns,omitempty"` ClusterDomain string `protobuf:"bytes,3,opt,name=cluster_domain,json=clusterDomain,proto3" json:"cluster_domain,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,4,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ExtraMounts []*proto.Mount `protobuf:"bytes,5,rep,name=extra_mounts,json=extraMounts,proto3" json:"extra_mounts,omitempty"` ExtraConfig *structpb.Struct `protobuf:"bytes,6,opt,name=extra_config,json=extraConfig,proto3" json:"extra_config,omitempty"` CloudProviderExternal bool `protobuf:"varint,7,opt,name=cloud_provider_external,json=cloudProviderExternal,proto3" json:"cloud_provider_external,omitempty"` DefaultRuntimeSeccompEnabled bool `protobuf:"varint,8,opt,name=default_runtime_seccomp_enabled,json=defaultRuntimeSeccompEnabled,proto3" json:"default_runtime_seccomp_enabled,omitempty"` SkipNodeRegistration bool `protobuf:"varint,9,opt,name=skip_node_registration,json=skipNodeRegistration,proto3" json:"skip_node_registration,omitempty"` StaticPodListUrl string `protobuf:"bytes,10,opt,name=static_pod_list_url,json=staticPodListUrl,proto3" json:"static_pod_list_url,omitempty"` DisableManifestsDirectory bool `protobuf:"varint,11,opt,name=disable_manifests_directory,json=disableManifestsDirectory,proto3" json:"disable_manifests_directory,omitempty"` EnableFsQuotaMonitoring bool `protobuf:"varint,12,opt,name=enable_fs_quota_monitoring,json=enableFsQuotaMonitoring,proto3" json:"enable_fs_quota_monitoring,omitempty"` CredentialProviderConfig *structpb.Struct `protobuf:"bytes,13,opt,name=credential_provider_config,json=credentialProviderConfig,proto3" json:"credential_provider_config,omitempty"` AllowSchedulingOnControlPlane bool `protobuf:"varint,14,opt,name=allow_scheduling_on_control_plane,json=allowSchedulingOnControlPlane,proto3" json:"allow_scheduling_on_control_plane,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubeletConfigSpec) Reset() { *x = KubeletConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubeletConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubeletConfigSpec) ProtoMessage() {} func (x *KubeletConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubeletConfigSpec.ProtoReflect.Descriptor instead. func (*KubeletConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{18} } func (x *KubeletConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *KubeletConfigSpec) GetClusterDns() []string { if x != nil { return x.ClusterDns } return nil } func (x *KubeletConfigSpec) GetClusterDomain() string { if x != nil { return x.ClusterDomain } return "" } func (x *KubeletConfigSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *KubeletConfigSpec) GetExtraMounts() []*proto.Mount { if x != nil { return x.ExtraMounts } return nil } func (x *KubeletConfigSpec) GetExtraConfig() *structpb.Struct { if x != nil { return x.ExtraConfig } return nil } func (x *KubeletConfigSpec) GetCloudProviderExternal() bool { if x != nil { return x.CloudProviderExternal } return false } func (x *KubeletConfigSpec) GetDefaultRuntimeSeccompEnabled() bool { if x != nil { return x.DefaultRuntimeSeccompEnabled } return false } func (x *KubeletConfigSpec) GetSkipNodeRegistration() bool { if x != nil { return x.SkipNodeRegistration } return false } func (x *KubeletConfigSpec) GetStaticPodListUrl() string { if x != nil { return x.StaticPodListUrl } return "" } func (x *KubeletConfigSpec) GetDisableManifestsDirectory() bool { if x != nil { return x.DisableManifestsDirectory } return false } func (x *KubeletConfigSpec) GetEnableFsQuotaMonitoring() bool { if x != nil { return x.EnableFsQuotaMonitoring } return false } func (x *KubeletConfigSpec) GetCredentialProviderConfig() *structpb.Struct { if x != nil { return x.CredentialProviderConfig } return nil } func (x *KubeletConfigSpec) GetAllowSchedulingOnControlPlane() bool { if x != nil { return x.AllowSchedulingOnControlPlane } return false } // KubeletSpecSpec holds the source of kubelet configuration. type KubeletSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` ExtraMounts []*proto.Mount `protobuf:"bytes,3,rep,name=extra_mounts,json=extraMounts,proto3" json:"extra_mounts,omitempty"` ExpectedNodename string `protobuf:"bytes,4,opt,name=expected_nodename,json=expectedNodename,proto3" json:"expected_nodename,omitempty"` Config *structpb.Struct `protobuf:"bytes,5,opt,name=config,proto3" json:"config,omitempty"` CredentialProviderConfig *structpb.Struct `protobuf:"bytes,6,opt,name=credential_provider_config,json=credentialProviderConfig,proto3" json:"credential_provider_config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubeletSpecSpec) Reset() { *x = KubeletSpecSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubeletSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubeletSpecSpec) ProtoMessage() {} func (x *KubeletSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubeletSpecSpec.ProtoReflect.Descriptor instead. func (*KubeletSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{19} } func (x *KubeletSpecSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *KubeletSpecSpec) GetArgs() []string { if x != nil { return x.Args } return nil } func (x *KubeletSpecSpec) GetExtraMounts() []*proto.Mount { if x != nil { return x.ExtraMounts } return nil } func (x *KubeletSpecSpec) GetExpectedNodename() string { if x != nil { return x.ExpectedNodename } return "" } func (x *KubeletSpecSpec) GetConfig() *structpb.Struct { if x != nil { return x.Config } return nil } func (x *KubeletSpecSpec) GetCredentialProviderConfig() *structpb.Struct { if x != nil { return x.CredentialProviderConfig } return nil } // ManifestSpec holds the Kubernetes resources spec. type ManifestSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Items []*SingleManifest `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ManifestSpec) Reset() { *x = ManifestSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ManifestSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ManifestSpec) ProtoMessage() {} func (x *ManifestSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ManifestSpec.ProtoReflect.Descriptor instead. func (*ManifestSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{20} } func (x *ManifestSpec) GetItems() []*SingleManifest { if x != nil { return x.Items } return nil } // ManifestStatusSpec describes manifest application status. type ManifestStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ManifestsApplied []string `protobuf:"bytes,1,rep,name=manifests_applied,json=manifestsApplied,proto3" json:"manifests_applied,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ManifestStatusSpec) Reset() { *x = ManifestStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ManifestStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ManifestStatusSpec) ProtoMessage() {} func (x *ManifestStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ManifestStatusSpec.ProtoReflect.Descriptor instead. func (*ManifestStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{21} } func (x *ManifestStatusSpec) GetManifestsApplied() []string { if x != nil { return x.ManifestsApplied } return nil } // NodeAnnotationSpecSpec represents an annoation that's attached to a Talos node. type NodeAnnotationSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeAnnotationSpecSpec) Reset() { *x = NodeAnnotationSpecSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeAnnotationSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeAnnotationSpecSpec) ProtoMessage() {} func (x *NodeAnnotationSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeAnnotationSpecSpec.ProtoReflect.Descriptor instead. func (*NodeAnnotationSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{22} } func (x *NodeAnnotationSpecSpec) GetKey() string { if x != nil { return x.Key } return "" } func (x *NodeAnnotationSpecSpec) GetValue() string { if x != nil { return x.Value } return "" } // NodeIPConfigSpec holds the Node IP specification. type NodeIPConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ValidSubnets []string `protobuf:"bytes,1,rep,name=valid_subnets,json=validSubnets,proto3" json:"valid_subnets,omitempty"` ExcludeSubnets []string `protobuf:"bytes,2,rep,name=exclude_subnets,json=excludeSubnets,proto3" json:"exclude_subnets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeIPConfigSpec) Reset() { *x = NodeIPConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeIPConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeIPConfigSpec) ProtoMessage() {} func (x *NodeIPConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeIPConfigSpec.ProtoReflect.Descriptor instead. func (*NodeIPConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{23} } func (x *NodeIPConfigSpec) GetValidSubnets() []string { if x != nil { return x.ValidSubnets } return nil } func (x *NodeIPConfigSpec) GetExcludeSubnets() []string { if x != nil { return x.ExcludeSubnets } return nil } // NodeIPSpec holds the Node IP specification. type NodeIPSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Addresses []*common.NetIP `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeIPSpec) Reset() { *x = NodeIPSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeIPSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeIPSpec) ProtoMessage() {} func (x *NodeIPSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeIPSpec.ProtoReflect.Descriptor instead. func (*NodeIPSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{24} } func (x *NodeIPSpec) GetAddresses() []*common.NetIP { if x != nil { return x.Addresses } return nil } // NodeLabelSpecSpec represents a label that's attached to a Talos node. type NodeLabelSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeLabelSpecSpec) Reset() { *x = NodeLabelSpecSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeLabelSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeLabelSpecSpec) ProtoMessage() {} func (x *NodeLabelSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeLabelSpecSpec.ProtoReflect.Descriptor instead. func (*NodeLabelSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{25} } func (x *NodeLabelSpecSpec) GetKey() string { if x != nil { return x.Key } return "" } func (x *NodeLabelSpecSpec) GetValue() string { if x != nil { return x.Value } return "" } // NodeStatusSpec describes Kubernetes NodeStatus. type NodeStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Nodename string `protobuf:"bytes,1,opt,name=nodename,proto3" json:"nodename,omitempty"` NodeReady bool `protobuf:"varint,2,opt,name=node_ready,json=nodeReady,proto3" json:"node_ready,omitempty"` Unschedulable bool `protobuf:"varint,3,opt,name=unschedulable,proto3" json:"unschedulable,omitempty"` Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` PodCidRs []*common.NetIPPrefix `protobuf:"bytes,6,rep,name=pod_cid_rs,json=podCidRs,proto3" json:"pod_cid_rs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeStatusSpec) Reset() { *x = NodeStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeStatusSpec) ProtoMessage() {} func (x *NodeStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeStatusSpec.ProtoReflect.Descriptor instead. func (*NodeStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{26} } func (x *NodeStatusSpec) GetNodename() string { if x != nil { return x.Nodename } return "" } func (x *NodeStatusSpec) GetNodeReady() bool { if x != nil { return x.NodeReady } return false } func (x *NodeStatusSpec) GetUnschedulable() bool { if x != nil { return x.Unschedulable } return false } func (x *NodeStatusSpec) GetLabels() map[string]string { if x != nil { return x.Labels } return nil } func (x *NodeStatusSpec) GetAnnotations() map[string]string { if x != nil { return x.Annotations } return nil } func (x *NodeStatusSpec) GetPodCidRs() []*common.NetIPPrefix { if x != nil { return x.PodCidRs } return nil } // NodeTaintSpecSpec represents a label that's attached to a Talos node. type NodeTaintSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Effect string `protobuf:"bytes,2,opt,name=effect,proto3" json:"effect,omitempty"` Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeTaintSpecSpec) Reset() { *x = NodeTaintSpecSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeTaintSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeTaintSpecSpec) ProtoMessage() {} func (x *NodeTaintSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeTaintSpecSpec.ProtoReflect.Descriptor instead. func (*NodeTaintSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{27} } func (x *NodeTaintSpecSpec) GetKey() string { if x != nil { return x.Key } return "" } func (x *NodeTaintSpecSpec) GetEffect() string { if x != nil { return x.Effect } return "" } func (x *NodeTaintSpecSpec) GetValue() string { if x != nil { return x.Value } return "" } // NodenameSpec describes Kubernetes nodename. type NodenameSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Nodename string `protobuf:"bytes,1,opt,name=nodename,proto3" json:"nodename,omitempty"` HostnameVersion string `protobuf:"bytes,2,opt,name=hostname_version,json=hostnameVersion,proto3" json:"hostname_version,omitempty"` SkipNodeRegistration bool `protobuf:"varint,3,opt,name=skip_node_registration,json=skipNodeRegistration,proto3" json:"skip_node_registration,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodenameSpec) Reset() { *x = NodenameSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodenameSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodenameSpec) ProtoMessage() {} func (x *NodenameSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodenameSpec.ProtoReflect.Descriptor instead. func (*NodenameSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{28} } func (x *NodenameSpec) GetNodename() string { if x != nil { return x.Nodename } return "" } func (x *NodenameSpec) GetHostnameVersion() string { if x != nil { return x.HostnameVersion } return "" } func (x *NodenameSpec) GetSkipNodeRegistration() bool { if x != nil { return x.SkipNodeRegistration } return false } // Resources is a configuration of cpu and memory resources. type Resources struct { state protoimpl.MessageState `protogen:"open.v1"` Requests map[string]string `protobuf:"bytes,1,rep,name=requests,proto3" json:"requests,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Limits map[string]string `protobuf:"bytes,2,rep,name=limits,proto3" json:"limits,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Resources) Reset() { *x = Resources{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Resources) String() string { return protoimpl.X.MessageStringOf(x) } func (*Resources) ProtoMessage() {} func (x *Resources) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Resources.ProtoReflect.Descriptor instead. func (*Resources) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{29} } func (x *Resources) GetRequests() map[string]string { if x != nil { return x.Requests } return nil } func (x *Resources) GetLimits() map[string]string { if x != nil { return x.Limits } return nil } // SchedulerConfigSpec is configuration for kube-scheduler. type SchedulerConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` Image string `protobuf:"bytes,2,opt,name=image,proto3" json:"image,omitempty"` ExtraArgs map[string]*ArgValues `protobuf:"bytes,3,rep,name=extra_args,json=extraArgs,proto3" json:"extra_args,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` ExtraVolumes []*ExtraVolume `protobuf:"bytes,4,rep,name=extra_volumes,json=extraVolumes,proto3" json:"extra_volumes,omitempty"` EnvironmentVariables map[string]string `protobuf:"bytes,5,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` Resources *Resources `protobuf:"bytes,6,opt,name=resources,proto3" json:"resources,omitempty"` Config *structpb.Struct `protobuf:"bytes,7,opt,name=config,proto3" json:"config,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SchedulerConfigSpec) Reset() { *x = SchedulerConfigSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SchedulerConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SchedulerConfigSpec) ProtoMessage() {} func (x *SchedulerConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SchedulerConfigSpec.ProtoReflect.Descriptor instead. func (*SchedulerConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{30} } func (x *SchedulerConfigSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *SchedulerConfigSpec) GetImage() string { if x != nil { return x.Image } return "" } func (x *SchedulerConfigSpec) GetExtraArgs() map[string]*ArgValues { if x != nil { return x.ExtraArgs } return nil } func (x *SchedulerConfigSpec) GetExtraVolumes() []*ExtraVolume { if x != nil { return x.ExtraVolumes } return nil } func (x *SchedulerConfigSpec) GetEnvironmentVariables() map[string]string { if x != nil { return x.EnvironmentVariables } return nil } func (x *SchedulerConfigSpec) GetResources() *Resources { if x != nil { return x.Resources } return nil } func (x *SchedulerConfigSpec) GetConfig() *structpb.Struct { if x != nil { return x.Config } return nil } // SecretsStatusSpec describes status of rendered secrets. type SecretsStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SecretsStatusSpec) Reset() { *x = SecretsStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SecretsStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecretsStatusSpec) ProtoMessage() {} func (x *SecretsStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecretsStatusSpec.ProtoReflect.Descriptor instead. func (*SecretsStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{31} } func (x *SecretsStatusSpec) GetReady() bool { if x != nil { return x.Ready } return false } func (x *SecretsStatusSpec) GetVersion() string { if x != nil { return x.Version } return "" } // SingleManifest is a single manifest. type SingleManifest struct { state protoimpl.MessageState `protogen:"open.v1"` Object *structpb.Struct `protobuf:"bytes,1,opt,name=object,proto3" json:"object,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SingleManifest) Reset() { *x = SingleManifest{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SingleManifest) String() string { return protoimpl.X.MessageStringOf(x) } func (*SingleManifest) ProtoMessage() {} func (x *SingleManifest) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SingleManifest.ProtoReflect.Descriptor instead. func (*SingleManifest) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{32} } func (x *SingleManifest) GetObject() *structpb.Struct { if x != nil { return x.Object } return nil } // StaticPodServerStatusSpec describes static pod spec, it contains marshaled *v1.Pod spec. type StaticPodServerStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StaticPodServerStatusSpec) Reset() { *x = StaticPodServerStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StaticPodServerStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StaticPodServerStatusSpec) ProtoMessage() {} func (x *StaticPodServerStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StaticPodServerStatusSpec.ProtoReflect.Descriptor instead. func (*StaticPodServerStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{33} } func (x *StaticPodServerStatusSpec) GetUrl() string { if x != nil { return x.Url } return "" } // StaticPodSpec describes static pod spec, it contains marshaled *v1.Pod spec. type StaticPodSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Pod *structpb.Struct `protobuf:"bytes,1,opt,name=pod,proto3" json:"pod,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StaticPodSpec) Reset() { *x = StaticPodSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StaticPodSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StaticPodSpec) ProtoMessage() {} func (x *StaticPodSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StaticPodSpec.ProtoReflect.Descriptor instead. func (*StaticPodSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{34} } func (x *StaticPodSpec) GetPod() *structpb.Struct { if x != nil { return x.Pod } return nil } // StaticPodStatusSpec describes kubelet static pod status. type StaticPodStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` PodStatus *structpb.Struct `protobuf:"bytes,1,opt,name=pod_status,json=podStatus,proto3" json:"pod_status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StaticPodStatusSpec) Reset() { *x = StaticPodStatusSpec{} mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StaticPodStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StaticPodStatusSpec) ProtoMessage() {} func (x *StaticPodStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_k8s_k8s_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StaticPodStatusSpec.ProtoReflect.Descriptor instead. func (*StaticPodStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_k8s_k8s_proto_rawDescGZIP(), []int{35} } func (x *StaticPodStatusSpec) GetPodStatus() *structpb.Struct { if x != nil { return x.PodStatus } return nil } var File_resource_definitions_k8s_k8s_proto protoreflect.FileDescriptor const file_resource_definitions_k8s_k8s_proto_rawDesc = "" + "\n" + "\"resource/definitions/k8s/k8s.proto\x12\x1etalos.resource.definitions.k8s\x1a\x13common/common.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a&resource/definitions/proto/proto.proto\"\xd4\x06\n" + "\x13APIServerConfigSpec\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12%\n" + "\x0ecloud_provider\x18\x02 \x01(\tR\rcloudProvider\x124\n" + "\x16control_plane_endpoint\x18\x03 \x01(\tR\x14controlPlaneEndpoint\x12!\n" + "\fetcd_servers\x18\x04 \x03(\tR\vetcdServers\x12\x1d\n" + "\n" + "local_port\x18\x05 \x01(\x03R\tlocalPort\x12$\n" + "\x0eservice_cid_rs\x18\x06 \x03(\tR\fserviceCidRs\x12a\n" + "\n" + "extra_args\x18\a \x03(\v2B.talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntryR\textraArgs\x12P\n" + "\rextra_volumes\x18\b \x03(\v2+.talos.resource.definitions.k8s.ExtraVolumeR\fextraVolumes\x12\x82\x01\n" + "\x15environment_variables\x18\t \x03(\v2M.talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntryR\x14environmentVariables\x12-\n" + "\x12advertised_address\x18\v \x01(\tR\x11advertisedAddress\x12G\n" + "\tresources\x18\f \x01(\v2).talos.resource.definitions.k8s.ResourcesR\tresources\x1ag\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12?\n" + "\x05value\x18\x02 \x01(\v2).talos.resource.definitions.k8s.ArgValuesR\x05value:\x028\x01\x1aG\n" + "\x19EnvironmentVariablesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"i\n" + "\x1aAdmissionControlConfigSpec\x12K\n" + "\x06config\x18\x01 \x03(\v23.talos.resource.definitions.k8s.AdmissionPluginSpecR\x06config\"h\n" + "\x13AdmissionPluginSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12=\n" + "\rconfiguration\x18\x02 \x01(\v2\x17.google.protobuf.StructR\rconfiguration\"#\n" + "\tArgValues\x12\x16\n" + "\x06values\x18\x01 \x03(\tR\x06values\"H\n" + "\x15AuditPolicyConfigSpec\x12/\n" + "\x06config\x18\x01 \x01(\v2\x17.google.protobuf.StructR\x06config\"y\n" + "\x1cAuthorizationAuthorizersSpec\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x121\n" + "\awebhook\x18\x03 \x01(\v2\x17.google.protobuf.StructR\awebhook\"\x85\x01\n" + "\x17AuthorizationConfigSpec\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12T\n" + "\x06config\x18\x02 \x03(\v2<.talos.resource.definitions.k8s.AuthorizationAuthorizersSpecR\x06config\"\xa8\a\n" + "\x1cBootstrapManifestsConfigSpec\x12\x16\n" + "\x06server\x18\x01 \x01(\tR\x06server\x12%\n" + "\x0ecluster_domain\x18\x02 \x01(\tR\rclusterDomain\x12\x1c\n" + "\n" + "pod_cid_rs\x18\x03 \x03(\tR\bpodCidRs\x12#\n" + "\rproxy_enabled\x18\x04 \x01(\bR\fproxyEnabled\x12\x1f\n" + "\vproxy_image\x18\x05 \x01(\tR\n" + "proxyImage\x12\x1d\n" + "\n" + "proxy_args\x18\x06 \x03(\tR\tproxyArgs\x12(\n" + "\x10core_dns_enabled\x18\a \x01(\bR\x0ecoreDnsEnabled\x12$\n" + "\x0ecore_dns_image\x18\b \x01(\tR\fcoreDnsImage\x12$\n" + "\x0edns_service_ip\x18\t \x01(\tR\fdnsServiceIp\x12)\n" + "\x11dns_service_i_pv6\x18\n" + " \x01(\tR\x0ednsServiceIPv6\x12'\n" + "\x0fflannel_enabled\x18\v \x01(\bR\x0eflannelEnabled\x12#\n" + "\rflannel_image\x18\f \x01(\tR\fflannelImage\x12=\n" + "\x1bpod_security_policy_enabled\x18\x0e \x01(\bR\x18podSecurityPolicyEnabled\x129\n" + "\x19talos_api_service_enabled\x18\x0f \x01(\bR\x16talosApiServiceEnabled\x12,\n" + "\x12flannel_extra_args\x18\x10 \x03(\tR\x10flannelExtraArgs\x129\n" + "\x19flannel_kube_service_host\x18\x11 \x01(\tR\x16flannelKubeServiceHost\x129\n" + "\x19flannel_kube_service_port\x18\x12 \x01(\tR\x16flannelKubeServicePort\x12P\n" + "%flannel_kube_network_policies_enabled\x18\x13 \x01(\bR!flannelKubeNetworkPoliciesEnabled\x12L\n" + "#flannel_kube_network_policies_image\x18\x14 \x01(\tR\x1fflannelKubeNetworkPoliciesImage\x12\x19\n" + "\bcni_name\x18\x15 \x01(\tR\acniName\"B\n" + "\x10ConfigStatusSpec\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12\x18\n" + "\aversion\x18\x02 \x01(\tR\aversion\"\xfd\x05\n" + "\x1bControllerManagerConfigSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12\x14\n" + "\x05image\x18\x02 \x01(\tR\x05image\x12%\n" + "\x0ecloud_provider\x18\x03 \x01(\tR\rcloudProvider\x12\x1c\n" + "\n" + "pod_cid_rs\x18\x04 \x03(\tR\bpodCidRs\x12$\n" + "\x0eservice_cid_rs\x18\x05 \x03(\tR\fserviceCidRs\x12i\n" + "\n" + "extra_args\x18\x06 \x03(\v2J.talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntryR\textraArgs\x12P\n" + "\rextra_volumes\x18\a \x03(\v2+.talos.resource.definitions.k8s.ExtraVolumeR\fextraVolumes\x12\x8a\x01\n" + "\x15environment_variables\x18\b \x03(\v2U.talos.resource.definitions.k8s.ControllerManagerConfigSpec.EnvironmentVariablesEntryR\x14environmentVariables\x12G\n" + "\tresources\x18\t \x01(\v2).talos.resource.definitions.k8s.ResourcesR\tresources\x1ag\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12?\n" + "\x05value\x18\x02 \x01(\v2).talos.resource.definitions.k8s.ArgValuesR\x05value:\x028\x01\x1aG\n" + "\x19EnvironmentVariablesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"Q\n" + "\fEndpointSpec\x12+\n" + "\taddresses\x18\x01 \x03(\v2\r.common.NetIPR\taddresses\x12\x14\n" + "\x05hosts\x18\x02 \x03(\tR\x05hosts\"\xa1\x02\n" + "\rExtraManifest\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n" + "\x03url\x18\x02 \x01(\tR\x03url\x12\x1a\n" + "\bpriority\x18\x03 \x01(\tR\bpriority\x12d\n" + "\rextra_headers\x18\x04 \x03(\v2?.talos.resource.definitions.k8s.ExtraManifest.ExtraHeadersEntryR\fextraHeaders\x12'\n" + "\x0finline_manifest\x18\x05 \x01(\tR\x0einlineManifest\x1a?\n" + "\x11ExtraHeadersEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"r\n" + "\x18ExtraManifestsConfigSpec\x12V\n" + "\x0fextra_manifests\x18\x01 \x03(\v2-.talos.resource.definitions.k8s.ExtraManifestR\x0eextraManifests\"z\n" + "\vExtraVolume\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n" + "\thost_path\x18\x02 \x01(\tR\bhostPath\x12\x1d\n" + "\n" + "mount_path\x18\x03 \x01(\tR\tmountPath\x12\x1b\n" + "\tread_only\x18\x04 \x01(\bR\breadOnly\"\x8e\x01\n" + "\x13KubePrismConfigSpec\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + "\x04port\x18\x02 \x01(\x03R\x04port\x12O\n" + "\tendpoints\x18\x03 \x03(\v21.talos.resource.definitions.k8s.KubePrismEndpointR\tendpoints\";\n" + "\x11KubePrismEndpoint\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + "\x04port\x18\x02 \x01(\rR\x04port\"i\n" + "\x16KubePrismEndpointsSpec\x12O\n" + "\tendpoints\x18\x01 \x03(\v21.talos.resource.definitions.k8s.KubePrismEndpointR\tendpoints\"E\n" + "\x15KubePrismStatusesSpec\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x18\n" + "\ahealthy\x18\x02 \x01(\bR\ahealthy\"\xc5\a\n" + "\x11KubeletConfigSpec\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12\x1f\n" + "\vcluster_dns\x18\x02 \x03(\tR\n" + "clusterDns\x12%\n" + "\x0ecluster_domain\x18\x03 \x01(\tR\rclusterDomain\x12_\n" + "\n" + "extra_args\x18\x04 \x03(\v2@.talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntryR\textraArgs\x12J\n" + "\fextra_mounts\x18\x05 \x03(\v2'.talos.resource.definitions.proto.MountR\vextraMounts\x12:\n" + "\fextra_config\x18\x06 \x01(\v2\x17.google.protobuf.StructR\vextraConfig\x126\n" + "\x17cloud_provider_external\x18\a \x01(\bR\x15cloudProviderExternal\x12E\n" + "\x1fdefault_runtime_seccomp_enabled\x18\b \x01(\bR\x1cdefaultRuntimeSeccompEnabled\x124\n" + "\x16skip_node_registration\x18\t \x01(\bR\x14skipNodeRegistration\x12-\n" + "\x13static_pod_list_url\x18\n" + " \x01(\tR\x10staticPodListUrl\x12>\n" + "\x1bdisable_manifests_directory\x18\v \x01(\bR\x19disableManifestsDirectory\x12;\n" + "\x1aenable_fs_quota_monitoring\x18\f \x01(\bR\x17enableFsQuotaMonitoring\x12U\n" + "\x1acredential_provider_config\x18\r \x01(\v2\x17.google.protobuf.StructR\x18credentialProviderConfig\x12H\n" + "!allow_scheduling_on_control_plane\x18\x0e \x01(\bR\x1dallowSchedulingOnControlPlane\x1ag\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12?\n" + "\x05value\x18\x02 \x01(\v2).talos.resource.definitions.k8s.ArgValuesR\x05value:\x028\x01\"\xbc\x02\n" + "\x0fKubeletSpecSpec\x12\x14\n" + "\x05image\x18\x01 \x01(\tR\x05image\x12\x12\n" + "\x04args\x18\x02 \x03(\tR\x04args\x12J\n" + "\fextra_mounts\x18\x03 \x03(\v2'.talos.resource.definitions.proto.MountR\vextraMounts\x12+\n" + "\x11expected_nodename\x18\x04 \x01(\tR\x10expectedNodename\x12/\n" + "\x06config\x18\x05 \x01(\v2\x17.google.protobuf.StructR\x06config\x12U\n" + "\x1acredential_provider_config\x18\x06 \x01(\v2\x17.google.protobuf.StructR\x18credentialProviderConfig\"T\n" + "\fManifestSpec\x12D\n" + "\x05items\x18\x01 \x03(\v2..talos.resource.definitions.k8s.SingleManifestR\x05items\"A\n" + "\x12ManifestStatusSpec\x12+\n" + "\x11manifests_applied\x18\x01 \x03(\tR\x10manifestsApplied\"@\n" + "\x16NodeAnnotationSpecSpec\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"`\n" + "\x10NodeIPConfigSpec\x12#\n" + "\rvalid_subnets\x18\x01 \x03(\tR\fvalidSubnets\x12'\n" + "\x0fexclude_subnets\x18\x02 \x03(\tR\x0eexcludeSubnets\"9\n" + "\n" + "NodeIPSpec\x12+\n" + "\taddresses\x18\x01 \x03(\v2\r.common.NetIPR\taddresses\";\n" + "\x11NodeLabelSpecSpec\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"\xd6\x03\n" + "\x0eNodeStatusSpec\x12\x1a\n" + "\bnodename\x18\x01 \x01(\tR\bnodename\x12\x1d\n" + "\n" + "node_ready\x18\x02 \x01(\bR\tnodeReady\x12$\n" + "\runschedulable\x18\x03 \x01(\bR\runschedulable\x12R\n" + "\x06labels\x18\x04 \x03(\v2:.talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntryR\x06labels\x12a\n" + "\vannotations\x18\x05 \x03(\v2?.talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntryR\vannotations\x121\n" + "\n" + "pod_cid_rs\x18\x06 \x03(\v2\x13.common.NetIPPrefixR\bpodCidRs\x1a9\n" + "\vLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + "\x10AnnotationsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"S\n" + "\x11NodeTaintSpecSpec\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x16\n" + "\x06effect\x18\x02 \x01(\tR\x06effect\x12\x14\n" + "\x05value\x18\x03 \x01(\tR\x05value\"\x8b\x01\n" + "\fNodenameSpec\x12\x1a\n" + "\bnodename\x18\x01 \x01(\tR\bnodename\x12)\n" + "\x10hostname_version\x18\x02 \x01(\tR\x0fhostnameVersion\x124\n" + "\x16skip_node_registration\x18\x03 \x01(\bR\x14skipNodeRegistration\"\xa7\x02\n" + "\tResources\x12S\n" + "\brequests\x18\x01 \x03(\v27.talos.resource.definitions.k8s.Resources.RequestsEntryR\brequests\x12M\n" + "\x06limits\x18\x02 \x03(\v25.talos.resource.definitions.k8s.Resources.LimitsEntryR\x06limits\x1a;\n" + "\rRequestsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a9\n" + "\vLimitsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xab\x05\n" + "\x13SchedulerConfigSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12\x14\n" + "\x05image\x18\x02 \x01(\tR\x05image\x12a\n" + "\n" + "extra_args\x18\x03 \x03(\v2B.talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntryR\textraArgs\x12P\n" + "\rextra_volumes\x18\x04 \x03(\v2+.talos.resource.definitions.k8s.ExtraVolumeR\fextraVolumes\x12\x82\x01\n" + "\x15environment_variables\x18\x05 \x03(\v2M.talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntryR\x14environmentVariables\x12G\n" + "\tresources\x18\x06 \x01(\v2).talos.resource.definitions.k8s.ResourcesR\tresources\x12/\n" + "\x06config\x18\a \x01(\v2\x17.google.protobuf.StructR\x06config\x1ag\n" + "\x0eExtraArgsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12?\n" + "\x05value\x18\x02 \x01(\v2).talos.resource.definitions.k8s.ArgValuesR\x05value:\x028\x01\x1aG\n" + "\x19EnvironmentVariablesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"C\n" + "\x11SecretsStatusSpec\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12\x18\n" + "\aversion\x18\x02 \x01(\tR\aversion\"A\n" + "\x0eSingleManifest\x12/\n" + "\x06object\x18\x01 \x01(\v2\x17.google.protobuf.StructR\x06object\"-\n" + "\x19StaticPodServerStatusSpec\x12\x10\n" + "\x03url\x18\x01 \x01(\tR\x03url\":\n" + "\rStaticPodSpec\x12)\n" + "\x03pod\x18\x01 \x01(\v2\x17.google.protobuf.StructR\x03pod\"M\n" + "\x13StaticPodStatusSpec\x126\n" + "\n" + "pod_status\x18\x01 \x01(\v2\x17.google.protobuf.StructR\tpodStatusBp\n" + "&dev.talos.api.resource.definitions.k8sZFgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/k8sb\x06proto3" var ( file_resource_definitions_k8s_k8s_proto_rawDescOnce sync.Once file_resource_definitions_k8s_k8s_proto_rawDescData []byte ) func file_resource_definitions_k8s_k8s_proto_rawDescGZIP() []byte { file_resource_definitions_k8s_k8s_proto_rawDescOnce.Do(func() { file_resource_definitions_k8s_k8s_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_k8s_k8s_proto_rawDesc), len(file_resource_definitions_k8s_k8s_proto_rawDesc))) }) return file_resource_definitions_k8s_k8s_proto_rawDescData } var file_resource_definitions_k8s_k8s_proto_msgTypes = make([]protoimpl.MessageInfo, 48) var file_resource_definitions_k8s_k8s_proto_goTypes = []any{ (*APIServerConfigSpec)(nil), // 0: talos.resource.definitions.k8s.APIServerConfigSpec (*AdmissionControlConfigSpec)(nil), // 1: talos.resource.definitions.k8s.AdmissionControlConfigSpec (*AdmissionPluginSpec)(nil), // 2: talos.resource.definitions.k8s.AdmissionPluginSpec (*ArgValues)(nil), // 3: talos.resource.definitions.k8s.ArgValues (*AuditPolicyConfigSpec)(nil), // 4: talos.resource.definitions.k8s.AuditPolicyConfigSpec (*AuthorizationAuthorizersSpec)(nil), // 5: talos.resource.definitions.k8s.AuthorizationAuthorizersSpec (*AuthorizationConfigSpec)(nil), // 6: talos.resource.definitions.k8s.AuthorizationConfigSpec (*BootstrapManifestsConfigSpec)(nil), // 7: talos.resource.definitions.k8s.BootstrapManifestsConfigSpec (*ConfigStatusSpec)(nil), // 8: talos.resource.definitions.k8s.ConfigStatusSpec (*ControllerManagerConfigSpec)(nil), // 9: talos.resource.definitions.k8s.ControllerManagerConfigSpec (*EndpointSpec)(nil), // 10: talos.resource.definitions.k8s.EndpointSpec (*ExtraManifest)(nil), // 11: talos.resource.definitions.k8s.ExtraManifest (*ExtraManifestsConfigSpec)(nil), // 12: talos.resource.definitions.k8s.ExtraManifestsConfigSpec (*ExtraVolume)(nil), // 13: talos.resource.definitions.k8s.ExtraVolume (*KubePrismConfigSpec)(nil), // 14: talos.resource.definitions.k8s.KubePrismConfigSpec (*KubePrismEndpoint)(nil), // 15: talos.resource.definitions.k8s.KubePrismEndpoint (*KubePrismEndpointsSpec)(nil), // 16: talos.resource.definitions.k8s.KubePrismEndpointsSpec (*KubePrismStatusesSpec)(nil), // 17: talos.resource.definitions.k8s.KubePrismStatusesSpec (*KubeletConfigSpec)(nil), // 18: talos.resource.definitions.k8s.KubeletConfigSpec (*KubeletSpecSpec)(nil), // 19: talos.resource.definitions.k8s.KubeletSpecSpec (*ManifestSpec)(nil), // 20: talos.resource.definitions.k8s.ManifestSpec (*ManifestStatusSpec)(nil), // 21: talos.resource.definitions.k8s.ManifestStatusSpec (*NodeAnnotationSpecSpec)(nil), // 22: talos.resource.definitions.k8s.NodeAnnotationSpecSpec (*NodeIPConfigSpec)(nil), // 23: talos.resource.definitions.k8s.NodeIPConfigSpec (*NodeIPSpec)(nil), // 24: talos.resource.definitions.k8s.NodeIPSpec (*NodeLabelSpecSpec)(nil), // 25: talos.resource.definitions.k8s.NodeLabelSpecSpec (*NodeStatusSpec)(nil), // 26: talos.resource.definitions.k8s.NodeStatusSpec (*NodeTaintSpecSpec)(nil), // 27: talos.resource.definitions.k8s.NodeTaintSpecSpec (*NodenameSpec)(nil), // 28: talos.resource.definitions.k8s.NodenameSpec (*Resources)(nil), // 29: talos.resource.definitions.k8s.Resources (*SchedulerConfigSpec)(nil), // 30: talos.resource.definitions.k8s.SchedulerConfigSpec (*SecretsStatusSpec)(nil), // 31: talos.resource.definitions.k8s.SecretsStatusSpec (*SingleManifest)(nil), // 32: talos.resource.definitions.k8s.SingleManifest (*StaticPodServerStatusSpec)(nil), // 33: talos.resource.definitions.k8s.StaticPodServerStatusSpec (*StaticPodSpec)(nil), // 34: talos.resource.definitions.k8s.StaticPodSpec (*StaticPodStatusSpec)(nil), // 35: talos.resource.definitions.k8s.StaticPodStatusSpec nil, // 36: talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry nil, // 37: talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntry nil, // 38: talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntry nil, // 39: talos.resource.definitions.k8s.ControllerManagerConfigSpec.EnvironmentVariablesEntry nil, // 40: talos.resource.definitions.k8s.ExtraManifest.ExtraHeadersEntry nil, // 41: talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry nil, // 42: talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry nil, // 43: talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry nil, // 44: talos.resource.definitions.k8s.Resources.RequestsEntry nil, // 45: talos.resource.definitions.k8s.Resources.LimitsEntry nil, // 46: talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry nil, // 47: talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry (*structpb.Struct)(nil), // 48: google.protobuf.Struct (*common.NetIP)(nil), // 49: common.NetIP (*proto.Mount)(nil), // 50: talos.resource.definitions.proto.Mount (*common.NetIPPrefix)(nil), // 51: common.NetIPPrefix } var file_resource_definitions_k8s_k8s_proto_depIdxs = []int32{ 36, // 0: talos.resource.definitions.k8s.APIServerConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry 13, // 1: talos.resource.definitions.k8s.APIServerConfigSpec.extra_volumes:type_name -> talos.resource.definitions.k8s.ExtraVolume 37, // 2: talos.resource.definitions.k8s.APIServerConfigSpec.environment_variables:type_name -> talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntry 29, // 3: talos.resource.definitions.k8s.APIServerConfigSpec.resources:type_name -> talos.resource.definitions.k8s.Resources 2, // 4: talos.resource.definitions.k8s.AdmissionControlConfigSpec.config:type_name -> talos.resource.definitions.k8s.AdmissionPluginSpec 48, // 5: talos.resource.definitions.k8s.AdmissionPluginSpec.configuration:type_name -> google.protobuf.Struct 48, // 6: talos.resource.definitions.k8s.AuditPolicyConfigSpec.config:type_name -> google.protobuf.Struct 48, // 7: talos.resource.definitions.k8s.AuthorizationAuthorizersSpec.webhook:type_name -> google.protobuf.Struct 5, // 8: talos.resource.definitions.k8s.AuthorizationConfigSpec.config:type_name -> talos.resource.definitions.k8s.AuthorizationAuthorizersSpec 38, // 9: talos.resource.definitions.k8s.ControllerManagerConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntry 13, // 10: talos.resource.definitions.k8s.ControllerManagerConfigSpec.extra_volumes:type_name -> talos.resource.definitions.k8s.ExtraVolume 39, // 11: talos.resource.definitions.k8s.ControllerManagerConfigSpec.environment_variables:type_name -> talos.resource.definitions.k8s.ControllerManagerConfigSpec.EnvironmentVariablesEntry 29, // 12: talos.resource.definitions.k8s.ControllerManagerConfigSpec.resources:type_name -> talos.resource.definitions.k8s.Resources 49, // 13: talos.resource.definitions.k8s.EndpointSpec.addresses:type_name -> common.NetIP 40, // 14: talos.resource.definitions.k8s.ExtraManifest.extra_headers:type_name -> talos.resource.definitions.k8s.ExtraManifest.ExtraHeadersEntry 11, // 15: talos.resource.definitions.k8s.ExtraManifestsConfigSpec.extra_manifests:type_name -> talos.resource.definitions.k8s.ExtraManifest 15, // 16: talos.resource.definitions.k8s.KubePrismConfigSpec.endpoints:type_name -> talos.resource.definitions.k8s.KubePrismEndpoint 15, // 17: talos.resource.definitions.k8s.KubePrismEndpointsSpec.endpoints:type_name -> talos.resource.definitions.k8s.KubePrismEndpoint 41, // 18: talos.resource.definitions.k8s.KubeletConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry 50, // 19: talos.resource.definitions.k8s.KubeletConfigSpec.extra_mounts:type_name -> talos.resource.definitions.proto.Mount 48, // 20: talos.resource.definitions.k8s.KubeletConfigSpec.extra_config:type_name -> google.protobuf.Struct 48, // 21: talos.resource.definitions.k8s.KubeletConfigSpec.credential_provider_config:type_name -> google.protobuf.Struct 50, // 22: talos.resource.definitions.k8s.KubeletSpecSpec.extra_mounts:type_name -> talos.resource.definitions.proto.Mount 48, // 23: talos.resource.definitions.k8s.KubeletSpecSpec.config:type_name -> google.protobuf.Struct 48, // 24: talos.resource.definitions.k8s.KubeletSpecSpec.credential_provider_config:type_name -> google.protobuf.Struct 32, // 25: talos.resource.definitions.k8s.ManifestSpec.items:type_name -> talos.resource.definitions.k8s.SingleManifest 49, // 26: talos.resource.definitions.k8s.NodeIPSpec.addresses:type_name -> common.NetIP 42, // 27: talos.resource.definitions.k8s.NodeStatusSpec.labels:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry 43, // 28: talos.resource.definitions.k8s.NodeStatusSpec.annotations:type_name -> talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry 51, // 29: talos.resource.definitions.k8s.NodeStatusSpec.pod_cid_rs:type_name -> common.NetIPPrefix 44, // 30: talos.resource.definitions.k8s.Resources.requests:type_name -> talos.resource.definitions.k8s.Resources.RequestsEntry 45, // 31: talos.resource.definitions.k8s.Resources.limits:type_name -> talos.resource.definitions.k8s.Resources.LimitsEntry 46, // 32: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_args:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry 13, // 33: talos.resource.definitions.k8s.SchedulerConfigSpec.extra_volumes:type_name -> talos.resource.definitions.k8s.ExtraVolume 47, // 34: talos.resource.definitions.k8s.SchedulerConfigSpec.environment_variables:type_name -> talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry 29, // 35: talos.resource.definitions.k8s.SchedulerConfigSpec.resources:type_name -> talos.resource.definitions.k8s.Resources 48, // 36: talos.resource.definitions.k8s.SchedulerConfigSpec.config:type_name -> google.protobuf.Struct 48, // 37: talos.resource.definitions.k8s.SingleManifest.object:type_name -> google.protobuf.Struct 48, // 38: talos.resource.definitions.k8s.StaticPodSpec.pod:type_name -> google.protobuf.Struct 48, // 39: talos.resource.definitions.k8s.StaticPodStatusSpec.pod_status:type_name -> google.protobuf.Struct 3, // 40: talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.k8s.ArgValues 3, // 41: talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.k8s.ArgValues 3, // 42: talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.k8s.ArgValues 3, // 43: talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry.value:type_name -> talos.resource.definitions.k8s.ArgValues 44, // [44:44] is the sub-list for method output_type 44, // [44:44] is the sub-list for method input_type 44, // [44:44] is the sub-list for extension type_name 44, // [44:44] is the sub-list for extension extendee 0, // [0:44] is the sub-list for field type_name } func init() { file_resource_definitions_k8s_k8s_proto_init() } func file_resource_definitions_k8s_k8s_proto_init() { if File_resource_definitions_k8s_k8s_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_k8s_k8s_proto_rawDesc), len(file_resource_definitions_k8s_k8s_proto_rawDesc)), NumEnums: 0, NumMessages: 48, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_k8s_k8s_proto_goTypes, DependencyIndexes: file_resource_definitions_k8s_k8s_proto_depIdxs, MessageInfos: file_resource_definitions_k8s_k8s_proto_msgTypes, }.Build() File_resource_definitions_k8s_k8s_proto = out.File file_resource_definitions_k8s_k8s_proto_goTypes = nil file_resource_definitions_k8s_k8s_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/k8s/k8s_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/k8s/k8s.proto package k8s import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" structpb "github.com/planetscale/vtprotobuf/types/known/structpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" structpb1 "google.golang.org/protobuf/types/known/structpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" proto1 "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/proto" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *APIServerConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *APIServerConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *APIServerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Resources != nil { size, err := m.Resources.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x62 } if len(m.AdvertisedAddress) > 0 { i -= len(m.AdvertisedAddress) copy(dAtA[i:], m.AdvertisedAddress) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AdvertisedAddress))) i-- dAtA[i] = 0x5a } if len(m.EnvironmentVariables) > 0 { for k := range m.EnvironmentVariables { v := m.EnvironmentVariables[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x4a } } if len(m.ExtraVolumes) > 0 { for iNdEx := len(m.ExtraVolumes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.ExtraVolumes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x3a } } if len(m.ServiceCidRs) > 0 { for iNdEx := len(m.ServiceCidRs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ServiceCidRs[iNdEx]) copy(dAtA[i:], m.ServiceCidRs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ServiceCidRs[iNdEx]))) i-- dAtA[i] = 0x32 } } if m.LocalPort != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LocalPort)) i-- dAtA[i] = 0x28 } if len(m.EtcdServers) > 0 { for iNdEx := len(m.EtcdServers) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.EtcdServers[iNdEx]) copy(dAtA[i:], m.EtcdServers[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.EtcdServers[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.ControlPlaneEndpoint) > 0 { i -= len(m.ControlPlaneEndpoint) copy(dAtA[i:], m.ControlPlaneEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ControlPlaneEndpoint))) i-- dAtA[i] = 0x1a } if len(m.CloudProvider) > 0 { i -= len(m.CloudProvider) copy(dAtA[i:], m.CloudProvider) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CloudProvider))) i-- dAtA[i] = 0x12 } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AdmissionControlConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AdmissionControlConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AdmissionControlConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Config) > 0 { for iNdEx := len(m.Config) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Config[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *AdmissionPluginSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AdmissionPluginSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AdmissionPluginSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Configuration != nil { size, err := (*structpb.Struct)(m.Configuration).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ArgValues) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ArgValues) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ArgValues) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Values) > 0 { for iNdEx := len(m.Values) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Values[iNdEx]) copy(dAtA[i:], m.Values[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Values[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *AuditPolicyConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AuditPolicyConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AuditPolicyConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Config != nil { size, err := (*structpb.Struct)(m.Config).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AuthorizationAuthorizersSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AuthorizationAuthorizersSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AuthorizationAuthorizersSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Webhook != nil { size, err := (*structpb.Struct)(m.Webhook).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x12 } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AuthorizationConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AuthorizationConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AuthorizationConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Config) > 0 { for iNdEx := len(m.Config) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Config[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BootstrapManifestsConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BootstrapManifestsConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BootstrapManifestsConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.CniName) > 0 { i -= len(m.CniName) copy(dAtA[i:], m.CniName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CniName))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xaa } if len(m.FlannelKubeNetworkPoliciesImage) > 0 { i -= len(m.FlannelKubeNetworkPoliciesImage) copy(dAtA[i:], m.FlannelKubeNetworkPoliciesImage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FlannelKubeNetworkPoliciesImage))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa2 } if m.FlannelKubeNetworkPoliciesEnabled { i-- if m.FlannelKubeNetworkPoliciesEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x98 } if len(m.FlannelKubeServicePort) > 0 { i -= len(m.FlannelKubeServicePort) copy(dAtA[i:], m.FlannelKubeServicePort) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FlannelKubeServicePort))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if len(m.FlannelKubeServiceHost) > 0 { i -= len(m.FlannelKubeServiceHost) copy(dAtA[i:], m.FlannelKubeServiceHost) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FlannelKubeServiceHost))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if len(m.FlannelExtraArgs) > 0 { for iNdEx := len(m.FlannelExtraArgs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.FlannelExtraArgs[iNdEx]) copy(dAtA[i:], m.FlannelExtraArgs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FlannelExtraArgs[iNdEx]))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } } if m.TalosApiServiceEnabled { i-- if m.TalosApiServiceEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x78 } if m.PodSecurityPolicyEnabled { i-- if m.PodSecurityPolicyEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x70 } if len(m.FlannelImage) > 0 { i -= len(m.FlannelImage) copy(dAtA[i:], m.FlannelImage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FlannelImage))) i-- dAtA[i] = 0x62 } if m.FlannelEnabled { i-- if m.FlannelEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x58 } if len(m.DnsServiceIPv6) > 0 { i -= len(m.DnsServiceIPv6) copy(dAtA[i:], m.DnsServiceIPv6) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DnsServiceIPv6))) i-- dAtA[i] = 0x52 } if len(m.DnsServiceIp) > 0 { i -= len(m.DnsServiceIp) copy(dAtA[i:], m.DnsServiceIp) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DnsServiceIp))) i-- dAtA[i] = 0x4a } if len(m.CoreDnsImage) > 0 { i -= len(m.CoreDnsImage) copy(dAtA[i:], m.CoreDnsImage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CoreDnsImage))) i-- dAtA[i] = 0x42 } if m.CoreDnsEnabled { i-- if m.CoreDnsEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if len(m.ProxyArgs) > 0 { for iNdEx := len(m.ProxyArgs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ProxyArgs[iNdEx]) copy(dAtA[i:], m.ProxyArgs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProxyArgs[iNdEx]))) i-- dAtA[i] = 0x32 } } if len(m.ProxyImage) > 0 { i -= len(m.ProxyImage) copy(dAtA[i:], m.ProxyImage) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProxyImage))) i-- dAtA[i] = 0x2a } if m.ProxyEnabled { i-- if m.ProxyEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.PodCidRs) > 0 { for iNdEx := len(m.PodCidRs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PodCidRs[iNdEx]) copy(dAtA[i:], m.PodCidRs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PodCidRs[iNdEx]))) i-- dAtA[i] = 0x1a } } if len(m.ClusterDomain) > 0 { i -= len(m.ClusterDomain) copy(dAtA[i:], m.ClusterDomain) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterDomain))) i-- dAtA[i] = 0x12 } if len(m.Server) > 0 { i -= len(m.Server) copy(dAtA[i:], m.Server) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Server))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ConfigStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x12 } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ControllerManagerConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ControllerManagerConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ControllerManagerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Resources != nil { size, err := m.Resources.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x4a } if len(m.EnvironmentVariables) > 0 { for k := range m.EnvironmentVariables { v := m.EnvironmentVariables[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x42 } } if len(m.ExtraVolumes) > 0 { for iNdEx := len(m.ExtraVolumes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.ExtraVolumes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x32 } } if len(m.ServiceCidRs) > 0 { for iNdEx := len(m.ServiceCidRs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ServiceCidRs[iNdEx]) copy(dAtA[i:], m.ServiceCidRs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ServiceCidRs[iNdEx]))) i-- dAtA[i] = 0x2a } } if len(m.PodCidRs) > 0 { for iNdEx := len(m.PodCidRs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PodCidRs[iNdEx]) copy(dAtA[i:], m.PodCidRs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PodCidRs[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.CloudProvider) > 0 { i -= len(m.CloudProvider) copy(dAtA[i:], m.CloudProvider) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CloudProvider))) i-- dAtA[i] = 0x1a } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0x12 } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EndpointSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EndpointSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EndpointSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Hosts) > 0 { for iNdEx := len(m.Hosts) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Hosts[iNdEx]) copy(dAtA[i:], m.Hosts[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hosts[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Addresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Addresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ExtraManifest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtraManifest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtraManifest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.InlineManifest) > 0 { i -= len(m.InlineManifest) copy(dAtA[i:], m.InlineManifest) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InlineManifest))) i-- dAtA[i] = 0x2a } if len(m.ExtraHeaders) > 0 { for k := range m.ExtraHeaders { v := m.ExtraHeaders[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if len(m.Priority) > 0 { i -= len(m.Priority) copy(dAtA[i:], m.Priority) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Priority))) i-- dAtA[i] = 0x1a } if len(m.Url) > 0 { i -= len(m.Url) copy(dAtA[i:], m.Url) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Url))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ExtraManifestsConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtraManifestsConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtraManifestsConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExtraManifests) > 0 { for iNdEx := len(m.ExtraManifests) - 1; iNdEx >= 0; iNdEx-- { size, err := m.ExtraManifests[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ExtraVolume) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtraVolume) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtraVolume) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ReadOnly { i-- if m.ReadOnly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.MountPath) > 0 { i -= len(m.MountPath) copy(dAtA[i:], m.MountPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MountPath))) i-- dAtA[i] = 0x1a } if len(m.HostPath) > 0 { i -= len(m.HostPath) copy(dAtA[i:], m.HostPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HostPath))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubePrismConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubePrismConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubePrismConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Endpoints) > 0 { for iNdEx := len(m.Endpoints) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Endpoints[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if m.Port != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Port)) i-- dAtA[i] = 0x10 } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubePrismEndpoint) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubePrismEndpoint) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubePrismEndpoint) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Port != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Port)) i-- dAtA[i] = 0x10 } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubePrismEndpointsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubePrismEndpointsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubePrismEndpointsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Endpoints) > 0 { for iNdEx := len(m.Endpoints) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Endpoints[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *KubePrismStatusesSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubePrismStatusesSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubePrismStatusesSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Healthy { i-- if m.Healthy { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubeletConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubeletConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubeletConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.AllowSchedulingOnControlPlane { i-- if m.AllowSchedulingOnControlPlane { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x70 } if m.CredentialProviderConfig != nil { size, err := (*structpb.Struct)(m.CredentialProviderConfig).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x6a } if m.EnableFsQuotaMonitoring { i-- if m.EnableFsQuotaMonitoring { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x60 } if m.DisableManifestsDirectory { i-- if m.DisableManifestsDirectory { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x58 } if len(m.StaticPodListUrl) > 0 { i -= len(m.StaticPodListUrl) copy(dAtA[i:], m.StaticPodListUrl) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.StaticPodListUrl))) i-- dAtA[i] = 0x52 } if m.SkipNodeRegistration { i-- if m.SkipNodeRegistration { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x48 } if m.DefaultRuntimeSeccompEnabled { i-- if m.DefaultRuntimeSeccompEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.CloudProviderExternal { i-- if m.CloudProviderExternal { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.ExtraConfig != nil { size, err := (*structpb.Struct)(m.ExtraConfig).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if len(m.ExtraMounts) > 0 { for iNdEx := len(m.ExtraMounts) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExtraMounts[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExtraMounts[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if len(m.ClusterDomain) > 0 { i -= len(m.ClusterDomain) copy(dAtA[i:], m.ClusterDomain) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterDomain))) i-- dAtA[i] = 0x1a } if len(m.ClusterDns) > 0 { for iNdEx := len(m.ClusterDns) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ClusterDns[iNdEx]) copy(dAtA[i:], m.ClusterDns[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterDns[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubeletSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubeletSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubeletSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.CredentialProviderConfig != nil { size, err := (*structpb.Struct)(m.CredentialProviderConfig).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.Config != nil { size, err := (*structpb.Struct)(m.Config).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if len(m.ExpectedNodename) > 0 { i -= len(m.ExpectedNodename) copy(dAtA[i:], m.ExpectedNodename) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ExpectedNodename))) i-- dAtA[i] = 0x22 } if len(m.ExtraMounts) > 0 { for iNdEx := len(m.ExtraMounts) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExtraMounts[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExtraMounts[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } } if len(m.Args) > 0 { for iNdEx := len(m.Args) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Args[iNdEx]) copy(dAtA[i:], m.Args[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Args[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ManifestSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ManifestSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ManifestSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Items) > 0 { for iNdEx := len(m.Items) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Items[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ManifestStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ManifestStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ManifestStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ManifestsApplied) > 0 { for iNdEx := len(m.ManifestsApplied) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ManifestsApplied[iNdEx]) copy(dAtA[i:], m.ManifestsApplied[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ManifestsApplied[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NodeAnnotationSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeAnnotationSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeAnnotationSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0x12 } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NodeIPConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeIPConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeIPConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExcludeSubnets) > 0 { for iNdEx := len(m.ExcludeSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ExcludeSubnets[iNdEx]) copy(dAtA[i:], m.ExcludeSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ExcludeSubnets[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.ValidSubnets) > 0 { for iNdEx := len(m.ValidSubnets) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.ValidSubnets[iNdEx]) copy(dAtA[i:], m.ValidSubnets[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ValidSubnets[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NodeIPSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeIPSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeIPSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Addresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Addresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NodeLabelSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeLabelSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeLabelSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0x12 } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NodeStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.PodCidRs) > 0 { for iNdEx := len(m.PodCidRs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.PodCidRs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.PodCidRs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x32 } } if len(m.Annotations) > 0 { for k := range m.Annotations { v := m.Annotations[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if len(m.Labels) > 0 { for k := range m.Labels { v := m.Labels[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x22 } } if m.Unschedulable { i-- if m.Unschedulable { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.NodeReady { i-- if m.NodeReady { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Nodename) > 0 { i -= len(m.Nodename) copy(dAtA[i:], m.Nodename) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Nodename))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NodeTaintSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeTaintSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeTaintSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0x1a } if len(m.Effect) > 0 { i -= len(m.Effect) copy(dAtA[i:], m.Effect) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Effect))) i-- dAtA[i] = 0x12 } if len(m.Key) > 0 { i -= len(m.Key) copy(dAtA[i:], m.Key) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Key))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NodenameSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodenameSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodenameSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SkipNodeRegistration { i-- if m.SkipNodeRegistration { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.HostnameVersion) > 0 { i -= len(m.HostnameVersion) copy(dAtA[i:], m.HostnameVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HostnameVersion))) i-- dAtA[i] = 0x12 } if len(m.Nodename) > 0 { i -= len(m.Nodename) copy(dAtA[i:], m.Nodename) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Nodename))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Resources) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Resources) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Resources) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Limits) > 0 { for k := range m.Limits { v := m.Limits[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if len(m.Requests) > 0 { for k := range m.Requests { v := m.Requests[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *SchedulerConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SchedulerConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SchedulerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Config != nil { size, err := (*structpb.Struct)(m.Config).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } if m.Resources != nil { size, err := m.Resources.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if len(m.EnvironmentVariables) > 0 { for k := range m.EnvironmentVariables { v := m.EnvironmentVariables[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x2a } } if len(m.ExtraVolumes) > 0 { for iNdEx := len(m.ExtraVolumes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.ExtraVolumes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } } if len(m.ExtraArgs) > 0 { for k := range m.ExtraArgs { v := m.ExtraArgs[k] baseI := i size, err := v.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1a } } if len(m.Image) > 0 { i -= len(m.Image) copy(dAtA[i:], m.Image) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Image))) i-- dAtA[i] = 0x12 } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *SecretsStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SecretsStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SecretsStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x12 } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *SingleManifest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SingleManifest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SingleManifest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Object != nil { size, err := (*structpb.Struct)(m.Object).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StaticPodServerStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StaticPodServerStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StaticPodServerStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Url) > 0 { i -= len(m.Url) copy(dAtA[i:], m.Url) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Url))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StaticPodSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StaticPodSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StaticPodSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Pod != nil { size, err := (*structpb.Struct)(m.Pod).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StaticPodStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StaticPodStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StaticPodStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.PodStatus != nil { size, err := (*structpb.Struct)(m.PodStatus).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *APIServerConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.CloudProvider) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ControlPlaneEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.EtcdServers) > 0 { for _, s := range m.EtcdServers { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.LocalPort != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.LocalPort)) } if len(m.ServiceCidRs) > 0 { for _, s := range m.ServiceCidRs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ExtraVolumes) > 0 { for _, e := range m.ExtraVolumes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.EnvironmentVariables) > 0 { for k, v := range m.EnvironmentVariables { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } l = len(m.AdvertisedAddress) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Resources != nil { l = m.Resources.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *AdmissionControlConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Config) > 0 { for _, e := range m.Config { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *AdmissionPluginSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Configuration != nil { l = (*structpb.Struct)(m.Configuration).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ArgValues) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Values) > 0 { for _, s := range m.Values { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *AuditPolicyConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Config != nil { l = (*structpb.Struct)(m.Config).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *AuthorizationAuthorizersSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Webhook != nil { l = (*structpb.Struct)(m.Webhook).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *AuthorizationConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Config) > 0 { for _, e := range m.Config { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BootstrapManifestsConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Server) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ClusterDomain) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.PodCidRs) > 0 { for _, s := range m.PodCidRs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ProxyEnabled { n += 2 } l = len(m.ProxyImage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ProxyArgs) > 0 { for _, s := range m.ProxyArgs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.CoreDnsEnabled { n += 2 } l = len(m.CoreDnsImage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DnsServiceIp) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DnsServiceIPv6) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FlannelEnabled { n += 2 } l = len(m.FlannelImage) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PodSecurityPolicyEnabled { n += 2 } if m.TalosApiServiceEnabled { n += 2 } if len(m.FlannelExtraArgs) > 0 { for _, s := range m.FlannelExtraArgs { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.FlannelKubeServiceHost) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.FlannelKubeServicePort) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FlannelKubeNetworkPoliciesEnabled { n += 3 } l = len(m.FlannelKubeNetworkPoliciesImage) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.CniName) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConfigStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ControllerManagerConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.CloudProvider) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.PodCidRs) > 0 { for _, s := range m.PodCidRs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ServiceCidRs) > 0 { for _, s := range m.ServiceCidRs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ExtraVolumes) > 0 { for _, e := range m.ExtraVolumes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.EnvironmentVariables) > 0 { for k, v := range m.EnvironmentVariables { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if m.Resources != nil { l = m.Resources.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EndpointSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Addresses) > 0 { for _, e := range m.Addresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Hosts) > 0 { for _, s := range m.Hosts { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ExtraManifest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Url) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Priority) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ExtraHeaders) > 0 { for k, v := range m.ExtraHeaders { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } l = len(m.InlineManifest) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ExtraManifestsConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ExtraManifests) > 0 { for _, e := range m.ExtraManifests { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ExtraVolume) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.HostPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.MountPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ReadOnly { n += 2 } n += len(m.unknownFields) return n } func (m *KubePrismConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Port != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Port)) } if len(m.Endpoints) > 0 { for _, e := range m.Endpoints { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *KubePrismEndpoint) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Port != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Port)) } n += len(m.unknownFields) return n } func (m *KubePrismEndpointsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Endpoints) > 0 { for _, e := range m.Endpoints { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *KubePrismStatusesSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Healthy { n += 2 } n += len(m.unknownFields) return n } func (m *KubeletConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ClusterDns) > 0 { for _, s := range m.ClusterDns { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.ClusterDomain) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ExtraMounts) > 0 { for _, e := range m.ExtraMounts { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ExtraConfig != nil { l = (*structpb.Struct)(m.ExtraConfig).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CloudProviderExternal { n += 2 } if m.DefaultRuntimeSeccompEnabled { n += 2 } if m.SkipNodeRegistration { n += 2 } l = len(m.StaticPodListUrl) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.DisableManifestsDirectory { n += 2 } if m.EnableFsQuotaMonitoring { n += 2 } if m.CredentialProviderConfig != nil { l = (*structpb.Struct)(m.CredentialProviderConfig).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.AllowSchedulingOnControlPlane { n += 2 } n += len(m.unknownFields) return n } func (m *KubeletSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Args) > 0 { for _, s := range m.Args { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExtraMounts) > 0 { for _, e := range m.ExtraMounts { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.ExpectedNodename) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Config != nil { l = (*structpb.Struct)(m.Config).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.CredentialProviderConfig != nil { l = (*structpb.Struct)(m.CredentialProviderConfig).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ManifestSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Items) > 0 { for _, e := range m.Items { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ManifestStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ManifestsApplied) > 0 { for _, s := range m.ManifestsApplied { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NodeAnnotationSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NodeIPConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.ValidSubnets) > 0 { for _, s := range m.ValidSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExcludeSubnets) > 0 { for _, s := range m.ExcludeSubnets { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NodeIPSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Addresses) > 0 { for _, e := range m.Addresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NodeLabelSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NodeStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Nodename) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.NodeReady { n += 2 } if m.Unschedulable { n += 2 } if len(m.Labels) > 0 { for k, v := range m.Labels { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.Annotations) > 0 { for k, v := range m.Annotations { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.PodCidRs) > 0 { for _, e := range m.PodCidRs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NodeTaintSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Key) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Effect) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NodenameSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Nodename) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.HostnameVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SkipNodeRegistration { n += 2 } n += len(m.unknownFields) return n } func (m *Resources) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Requests) > 0 { for k, v := range m.Requests { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.Limits) > 0 { for k, v := range m.Limits { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } n += len(m.unknownFields) return n } func (m *SchedulerConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } l = len(m.Image) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ExtraArgs) > 0 { for k, v := range m.ExtraArgs { _ = k _ = v l = 0 if v != nil { l = v.SizeVT() } l += 1 + protohelpers.SizeOfVarint(uint64(l)) mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + l n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if len(m.ExtraVolumes) > 0 { for _, e := range m.ExtraVolumes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.EnvironmentVariables) > 0 { for k, v := range m.EnvironmentVariables { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if m.Resources != nil { l = m.Resources.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Config != nil { l = (*structpb.Struct)(m.Config).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *SecretsStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *SingleManifest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Object != nil { l = (*structpb.Struct)(m.Object).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *StaticPodServerStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Url) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *StaticPodSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Pod != nil { l = (*structpb.Struct)(m.Pod).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *StaticPodStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.PodStatus != nil { l = (*structpb.Struct)(m.PodStatus).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *APIServerConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: APIServerConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: APIServerConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CloudProvider", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CloudProvider = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControlPlaneEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ControlPlaneEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EtcdServers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.EtcdServers = append(m.EtcdServers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LocalPort", wireType) } m.LocalPort = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LocalPort |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceCidRs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ServiceCidRs = append(m.ServiceCidRs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraVolumes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraVolumes = append(m.ExtraVolumes, &ExtraVolume{}) if err := m.ExtraVolumes[len(m.ExtraVolumes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentVariables", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EnvironmentVariables == nil { m.EnvironmentVariables = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.EnvironmentVariables[mapkey] = mapvalue iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdvertisedAddress", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdvertisedAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Resources == nil { m.Resources = &Resources{} } if err := m.Resources.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AdmissionControlConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AdmissionControlConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AdmissionControlConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Config = append(m.Config, &AdmissionPluginSpec{}) if err := m.Config[len(m.Config)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AdmissionPluginSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AdmissionPluginSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AdmissionPluginSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Configuration", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Configuration == nil { m.Configuration = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Configuration).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ArgValues) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ArgValues: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ArgValues: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Values", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Values = append(m.Values, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AuditPolicyConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AuditPolicyConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AuditPolicyConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Config == nil { m.Config = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Config).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AuthorizationAuthorizersSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AuthorizationAuthorizersSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AuthorizationAuthorizersSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Webhook", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Webhook == nil { m.Webhook = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Webhook).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AuthorizationConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AuthorizationConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AuthorizationConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Config = append(m.Config, &AuthorizationAuthorizersSpec{}) if err := m.Config[len(m.Config)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BootstrapManifestsConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BootstrapManifestsConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BootstrapManifestsConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Server = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodCidRs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PodCidRs = append(m.PodCidRs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProxyEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ProxyEnabled = bool(v != 0) case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProxyImage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProxyImage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProxyArgs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProxyArgs = append(m.ProxyArgs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CoreDnsEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.CoreDnsEnabled = bool(v != 0) case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CoreDnsImage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CoreDnsImage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsServiceIp", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsServiceIp = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsServiceIPv6", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsServiceIPv6 = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.FlannelEnabled = bool(v != 0) case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelImage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FlannelImage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PodSecurityPolicyEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.PodSecurityPolicyEnabled = bool(v != 0) case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TalosApiServiceEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TalosApiServiceEnabled = bool(v != 0) case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelExtraArgs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FlannelExtraArgs = append(m.FlannelExtraArgs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelKubeServiceHost", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FlannelKubeServiceHost = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelKubeServicePort", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FlannelKubeServicePort = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 19: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelKubeNetworkPoliciesEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.FlannelKubeNetworkPoliciesEnabled = bool(v != 0) case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FlannelKubeNetworkPoliciesImage", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FlannelKubeNetworkPoliciesImage = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CniName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CniName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ConfigStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ControllerManagerConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ControllerManagerConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ControllerManagerConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CloudProvider", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CloudProvider = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodCidRs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PodCidRs = append(m.PodCidRs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceCidRs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ServiceCidRs = append(m.ServiceCidRs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraVolumes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraVolumes = append(m.ExtraVolumes, &ExtraVolume{}) if err := m.ExtraVolumes[len(m.ExtraVolumes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentVariables", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EnvironmentVariables == nil { m.EnvironmentVariables = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.EnvironmentVariables[mapkey] = mapvalue iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Resources == nil { m.Resources = &Resources{} } if err := m.Resources.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EndpointSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EndpointSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EndpointSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.Addresses[len(m.Addresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Addresses[len(m.Addresses)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hosts", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hosts = append(m.Hosts, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtraManifest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtraManifest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtraManifest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Url = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Priority = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraHeaders", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraHeaders == nil { m.ExtraHeaders = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraHeaders[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InlineManifest", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InlineManifest = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtraManifestsConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtraManifestsConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtraManifestsConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraManifests", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraManifests = append(m.ExtraManifests, &ExtraManifest{}) if err := m.ExtraManifests[len(m.ExtraManifests)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtraVolume) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtraVolume: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtraVolume: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HostPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.HostPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MountPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MountPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadOnly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadOnly = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubePrismConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubePrismConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubePrismConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType) } m.Port = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Port |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoints = append(m.Endpoints, &KubePrismEndpoint{}) if err := m.Endpoints[len(m.Endpoints)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubePrismEndpoint) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubePrismEndpoint: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubePrismEndpoint: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType) } m.Port = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Port |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubePrismEndpointsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubePrismEndpointsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubePrismEndpointsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoints = append(m.Endpoints, &KubePrismEndpoint{}) if err := m.Endpoints[len(m.Endpoints)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubePrismStatusesSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubePrismStatusesSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubePrismStatusesSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Healthy", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Healthy = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubeletConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubeletConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubeletConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDns", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterDns = append(m.ClusterDns, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraMounts", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraMounts = append(m.ExtraMounts, &proto1.Mount{}) if unmarshal, ok := interface{}(m.ExtraMounts[len(m.ExtraMounts)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExtraMounts[len(m.ExtraMounts)-1]); err != nil { return err } } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraConfig == nil { m.ExtraConfig = &structpb1.Struct{} } if err := (*structpb.Struct)(m.ExtraConfig).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CloudProviderExternal", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.CloudProviderExternal = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DefaultRuntimeSeccompEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DefaultRuntimeSeccompEnabled = bool(v != 0) case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipNodeRegistration", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipNodeRegistration = bool(v != 0) case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field StaticPodListUrl", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.StaticPodListUrl = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DisableManifestsDirectory", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DisableManifestsDirectory = bool(v != 0) case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EnableFsQuotaMonitoring", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.EnableFsQuotaMonitoring = bool(v != 0) case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CredentialProviderConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CredentialProviderConfig == nil { m.CredentialProviderConfig = &structpb1.Struct{} } if err := (*structpb.Struct)(m.CredentialProviderConfig).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AllowSchedulingOnControlPlane", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AllowSchedulingOnControlPlane = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubeletSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubeletSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubeletSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Args", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Args = append(m.Args, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraMounts", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraMounts = append(m.ExtraMounts, &proto1.Mount{}) if unmarshal, ok := interface{}(m.ExtraMounts[len(m.ExtraMounts)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExtraMounts[len(m.ExtraMounts)-1]); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExpectedNodename", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExpectedNodename = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Config == nil { m.Config = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Config).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CredentialProviderConfig", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CredentialProviderConfig == nil { m.CredentialProviderConfig = &structpb1.Struct{} } if err := (*structpb.Struct)(m.CredentialProviderConfig).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ManifestSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ManifestSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ManifestSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Items = append(m.Items, &SingleManifest{}) if err := m.Items[len(m.Items)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ManifestStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ManifestStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ManifestStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ManifestsApplied", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ManifestsApplied = append(m.ManifestsApplied, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeAnnotationSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeAnnotationSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeAnnotationSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeIPConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeIPConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeIPConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ValidSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ValidSubnets = append(m.ValidSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExcludeSubnets", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExcludeSubnets = append(m.ExcludeSubnets, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeIPSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeIPSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeIPSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.Addresses[len(m.Addresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Addresses[len(m.Addresses)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeLabelSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeLabelSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeLabelSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Nodename", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Nodename = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NodeReady", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.NodeReady = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unschedulable", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Unschedulable = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Labels == nil { m.Labels = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Labels[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Annotations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Annotations == nil { m.Annotations = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Annotations[mapkey] = mapvalue iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodCidRs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PodCidRs = append(m.PodCidRs, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.PodCidRs[len(m.PodCidRs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.PodCidRs[len(m.PodCidRs)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeTaintSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeTaintSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeTaintSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Key = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Effect", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Effect = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodenameSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodenameSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodenameSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Nodename", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Nodename = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HostnameVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.HostnameVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipNodeRegistration", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipNodeRegistration = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Resources) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Resources: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Resources: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Requests", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Requests == nil { m.Requests = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Requests[mapkey] = mapvalue iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Limits", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Limits == nil { m.Limits = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Limits[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SchedulerConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SchedulerConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SchedulerConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Image", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Image = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraArgs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ExtraArgs == nil { m.ExtraArgs = make(map[string]*ArgValues) } var mapkey string var mapvalue *ArgValues for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapmsglen |= int(b&0x7F) << shift if b < 0x80 { break } } if mapmsglen < 0 { return protohelpers.ErrInvalidLength } postmsgIndex := iNdEx + mapmsglen if postmsgIndex < 0 { return protohelpers.ErrInvalidLength } if postmsgIndex > l { return io.ErrUnexpectedEOF } mapvalue = &ArgValues{} if err := mapvalue.UnmarshalVT(dAtA[iNdEx:postmsgIndex]); err != nil { return err } iNdEx = postmsgIndex } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.ExtraArgs[mapkey] = mapvalue iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraVolumes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraVolumes = append(m.ExtraVolumes, &ExtraVolume{}) if err := m.ExtraVolumes[len(m.ExtraVolumes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EnvironmentVariables", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EnvironmentVariables == nil { m.EnvironmentVariables = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.EnvironmentVariables[mapkey] = mapvalue iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resources", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Resources == nil { m.Resources = &Resources{} } if err := m.Resources.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Config", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Config == nil { m.Config = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Config).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SecretsStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SecretsStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SecretsStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SingleManifest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SingleManifest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SingleManifest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Object", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Object == nil { m.Object = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Object).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StaticPodServerStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StaticPodServerStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StaticPodServerStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Url", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Url = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StaticPodSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StaticPodSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StaticPodSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pod", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Pod == nil { m.Pod = &structpb1.Struct{} } if err := (*structpb.Struct)(m.Pod).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StaticPodStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StaticPodStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StaticPodStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PodStatus", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.PodStatus == nil { m.PodStatus = &structpb1.Struct{} } if err := (*structpb.Struct)(m.PodStatus).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/kubeaccess/kubeaccess.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/kubeaccess/kubeaccess.proto package kubeaccess import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ConfigSpec describes KubeSpan configuration.. type ConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` AllowedApiRoles []string `protobuf:"bytes,2,rep,name=allowed_api_roles,json=allowedApiRoles,proto3" json:"allowed_api_roles,omitempty"` AllowedKubernetesNamespaces []string `protobuf:"bytes,3,rep,name=allowed_kubernetes_namespaces,json=allowedKubernetesNamespaces,proto3" json:"allowed_kubernetes_namespaces,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigSpec) Reset() { *x = ConfigSpec{} mi := &file_resource_definitions_kubeaccess_kubeaccess_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigSpec) ProtoMessage() {} func (x *ConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubeaccess_kubeaccess_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigSpec.ProtoReflect.Descriptor instead. func (*ConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescGZIP(), []int{0} } func (x *ConfigSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *ConfigSpec) GetAllowedApiRoles() []string { if x != nil { return x.AllowedApiRoles } return nil } func (x *ConfigSpec) GetAllowedKubernetesNamespaces() []string { if x != nil { return x.AllowedKubernetesNamespaces } return nil } var File_resource_definitions_kubeaccess_kubeaccess_proto protoreflect.FileDescriptor const file_resource_definitions_kubeaccess_kubeaccess_proto_rawDesc = "" + "\n" + "0resource/definitions/kubeaccess/kubeaccess.proto\x12%talos.resource.definitions.kubeaccess\"\x96\x01\n" + "\n" + "ConfigSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12*\n" + "\x11allowed_api_roles\x18\x02 \x03(\tR\x0fallowedApiRoles\x12B\n" + "\x1dallowed_kubernetes_namespaces\x18\x03 \x03(\tR\x1ballowedKubernetesNamespacesB~\n" + "-dev.talos.api.resource.definitions.kubeaccessZMgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/kubeaccessb\x06proto3" var ( file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescOnce sync.Once file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescData []byte ) func file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescGZIP() []byte { file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescOnce.Do(func() { file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_kubeaccess_kubeaccess_proto_rawDesc), len(file_resource_definitions_kubeaccess_kubeaccess_proto_rawDesc))) }) return file_resource_definitions_kubeaccess_kubeaccess_proto_rawDescData } var file_resource_definitions_kubeaccess_kubeaccess_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_resource_definitions_kubeaccess_kubeaccess_proto_goTypes = []any{ (*ConfigSpec)(nil), // 0: talos.resource.definitions.kubeaccess.ConfigSpec } var file_resource_definitions_kubeaccess_kubeaccess_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_definitions_kubeaccess_kubeaccess_proto_init() } func file_resource_definitions_kubeaccess_kubeaccess_proto_init() { if File_resource_definitions_kubeaccess_kubeaccess_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_kubeaccess_kubeaccess_proto_rawDesc), len(file_resource_definitions_kubeaccess_kubeaccess_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_kubeaccess_kubeaccess_proto_goTypes, DependencyIndexes: file_resource_definitions_kubeaccess_kubeaccess_proto_depIdxs, MessageInfos: file_resource_definitions_kubeaccess_kubeaccess_proto_msgTypes, }.Build() File_resource_definitions_kubeaccess_kubeaccess_proto = out.File file_resource_definitions_kubeaccess_kubeaccess_proto_goTypes = nil file_resource_definitions_kubeaccess_kubeaccess_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/kubeaccess/kubeaccess_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/kubeaccess/kubeaccess.proto package kubeaccess import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AllowedKubernetesNamespaces) > 0 { for iNdEx := len(m.AllowedKubernetesNamespaces) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AllowedKubernetesNamespaces[iNdEx]) copy(dAtA[i:], m.AllowedKubernetesNamespaces[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AllowedKubernetesNamespaces[iNdEx]))) i-- dAtA[i] = 0x1a } } if len(m.AllowedApiRoles) > 0 { for iNdEx := len(m.AllowedApiRoles) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AllowedApiRoles[iNdEx]) copy(dAtA[i:], m.AllowedApiRoles[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AllowedApiRoles[iNdEx]))) i-- dAtA[i] = 0x12 } } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } if len(m.AllowedApiRoles) > 0 { for _, s := range m.AllowedApiRoles { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.AllowedKubernetesNamespaces) > 0 { for _, s := range m.AllowedKubernetesNamespaces { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AllowedApiRoles", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AllowedApiRoles = append(m.AllowedApiRoles, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AllowedKubernetesNamespaces", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AllowedKubernetesNamespaces = append(m.AllowedKubernetesNamespaces, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/kubespan/kubespan.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/kubespan/kubespan.proto package kubespan import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ConfigSpec describes KubeSpan configuration.. type ConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` ClusterId string `protobuf:"bytes,2,opt,name=cluster_id,json=clusterId,proto3" json:"cluster_id,omitempty"` SharedSecret string `protobuf:"bytes,3,opt,name=shared_secret,json=sharedSecret,proto3" json:"shared_secret,omitempty"` ForceRouting bool `protobuf:"varint,4,opt,name=force_routing,json=forceRouting,proto3" json:"force_routing,omitempty"` AdvertiseKubernetesNetworks bool `protobuf:"varint,5,opt,name=advertise_kubernetes_networks,json=advertiseKubernetesNetworks,proto3" json:"advertise_kubernetes_networks,omitempty"` Mtu uint32 `protobuf:"varint,6,opt,name=mtu,proto3" json:"mtu,omitempty"` EndpointFilters []string `protobuf:"bytes,7,rep,name=endpoint_filters,json=endpointFilters,proto3" json:"endpoint_filters,omitempty"` HarvestExtraEndpoints bool `protobuf:"varint,8,opt,name=harvest_extra_endpoints,json=harvestExtraEndpoints,proto3" json:"harvest_extra_endpoints,omitempty"` ExtraEndpoints []*common.NetIPPort `protobuf:"bytes,9,rep,name=extra_endpoints,json=extraEndpoints,proto3" json:"extra_endpoints,omitempty"` ExcludeAdvertisedNetworks []*common.NetIPPrefix `protobuf:"bytes,10,rep,name=exclude_advertised_networks,json=excludeAdvertisedNetworks,proto3" json:"exclude_advertised_networks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigSpec) Reset() { *x = ConfigSpec{} mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigSpec) ProtoMessage() {} func (x *ConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigSpec.ProtoReflect.Descriptor instead. func (*ConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP(), []int{0} } func (x *ConfigSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *ConfigSpec) GetClusterId() string { if x != nil { return x.ClusterId } return "" } func (x *ConfigSpec) GetSharedSecret() string { if x != nil { return x.SharedSecret } return "" } func (x *ConfigSpec) GetForceRouting() bool { if x != nil { return x.ForceRouting } return false } func (x *ConfigSpec) GetAdvertiseKubernetesNetworks() bool { if x != nil { return x.AdvertiseKubernetesNetworks } return false } func (x *ConfigSpec) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *ConfigSpec) GetEndpointFilters() []string { if x != nil { return x.EndpointFilters } return nil } func (x *ConfigSpec) GetHarvestExtraEndpoints() bool { if x != nil { return x.HarvestExtraEndpoints } return false } func (x *ConfigSpec) GetExtraEndpoints() []*common.NetIPPort { if x != nil { return x.ExtraEndpoints } return nil } func (x *ConfigSpec) GetExcludeAdvertisedNetworks() []*common.NetIPPrefix { if x != nil { return x.ExcludeAdvertisedNetworks } return nil } // EndpointSpec describes Endpoint state. type EndpointSpec struct { state protoimpl.MessageState `protogen:"open.v1"` AffiliateId string `protobuf:"bytes,1,opt,name=affiliate_id,json=affiliateId,proto3" json:"affiliate_id,omitempty"` Endpoint *common.NetIPPort `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EndpointSpec) Reset() { *x = EndpointSpec{} mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EndpointSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EndpointSpec) ProtoMessage() {} func (x *EndpointSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EndpointSpec.ProtoReflect.Descriptor instead. func (*EndpointSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP(), []int{1} } func (x *EndpointSpec) GetAffiliateId() string { if x != nil { return x.AffiliateId } return "" } func (x *EndpointSpec) GetEndpoint() *common.NetIPPort { if x != nil { return x.Endpoint } return nil } // IdentitySpec describes KubeSpan keys and address. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. type IdentitySpec struct { state protoimpl.MessageState `protogen:"open.v1"` Address *common.NetIPPrefix `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Subnet *common.NetIPPrefix `protobuf:"bytes,2,opt,name=subnet,proto3" json:"subnet,omitempty"` PrivateKey string `protobuf:"bytes,3,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` PublicKey string `protobuf:"bytes,4,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *IdentitySpec) Reset() { *x = IdentitySpec{} mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *IdentitySpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*IdentitySpec) ProtoMessage() {} func (x *IdentitySpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use IdentitySpec.ProtoReflect.Descriptor instead. func (*IdentitySpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP(), []int{2} } func (x *IdentitySpec) GetAddress() *common.NetIPPrefix { if x != nil { return x.Address } return nil } func (x *IdentitySpec) GetSubnet() *common.NetIPPrefix { if x != nil { return x.Subnet } return nil } func (x *IdentitySpec) GetPrivateKey() string { if x != nil { return x.PrivateKey } return "" } func (x *IdentitySpec) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } // PeerSpecSpec describes PeerSpec state. type PeerSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Address *common.NetIP `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` AllowedIps []*common.NetIPPrefix `protobuf:"bytes,2,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` Endpoints []*common.NetIPPort `protobuf:"bytes,3,rep,name=endpoints,proto3" json:"endpoints,omitempty"` Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PeerSpecSpec) Reset() { *x = PeerSpecSpec{} mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PeerSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerSpecSpec) ProtoMessage() {} func (x *PeerSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerSpecSpec.ProtoReflect.Descriptor instead. func (*PeerSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP(), []int{3} } func (x *PeerSpecSpec) GetAddress() *common.NetIP { if x != nil { return x.Address } return nil } func (x *PeerSpecSpec) GetAllowedIps() []*common.NetIPPrefix { if x != nil { return x.AllowedIps } return nil } func (x *PeerSpecSpec) GetEndpoints() []*common.NetIPPort { if x != nil { return x.Endpoints } return nil } func (x *PeerSpecSpec) GetLabel() string { if x != nil { return x.Label } return "" } // PeerStatusSpec describes PeerStatus state. type PeerStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint *common.NetIPPort `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` Label string `protobuf:"bytes,2,opt,name=label,proto3" json:"label,omitempty"` State enums.KubespanPeerState `protobuf:"varint,3,opt,name=state,proto3,enum=talos.resource.definitions.enums.KubespanPeerState" json:"state,omitempty"` ReceiveBytes int64 `protobuf:"varint,4,opt,name=receive_bytes,json=receiveBytes,proto3" json:"receive_bytes,omitempty"` TransmitBytes int64 `protobuf:"varint,5,opt,name=transmit_bytes,json=transmitBytes,proto3" json:"transmit_bytes,omitempty"` LastHandshakeTime *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_handshake_time,json=lastHandshakeTime,proto3" json:"last_handshake_time,omitempty"` LastUsedEndpoint *common.NetIPPort `protobuf:"bytes,7,opt,name=last_used_endpoint,json=lastUsedEndpoint,proto3" json:"last_used_endpoint,omitempty"` LastEndpointChange *timestamppb.Timestamp `protobuf:"bytes,8,opt,name=last_endpoint_change,json=lastEndpointChange,proto3" json:"last_endpoint_change,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PeerStatusSpec) Reset() { *x = PeerStatusSpec{} mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PeerStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PeerStatusSpec) ProtoMessage() {} func (x *PeerStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_kubespan_kubespan_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PeerStatusSpec.ProtoReflect.Descriptor instead. func (*PeerStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP(), []int{4} } func (x *PeerStatusSpec) GetEndpoint() *common.NetIPPort { if x != nil { return x.Endpoint } return nil } func (x *PeerStatusSpec) GetLabel() string { if x != nil { return x.Label } return "" } func (x *PeerStatusSpec) GetState() enums.KubespanPeerState { if x != nil { return x.State } return enums.KubespanPeerState(0) } func (x *PeerStatusSpec) GetReceiveBytes() int64 { if x != nil { return x.ReceiveBytes } return 0 } func (x *PeerStatusSpec) GetTransmitBytes() int64 { if x != nil { return x.TransmitBytes } return 0 } func (x *PeerStatusSpec) GetLastHandshakeTime() *timestamppb.Timestamp { if x != nil { return x.LastHandshakeTime } return nil } func (x *PeerStatusSpec) GetLastUsedEndpoint() *common.NetIPPort { if x != nil { return x.LastUsedEndpoint } return nil } func (x *PeerStatusSpec) GetLastEndpointChange() *timestamppb.Timestamp { if x != nil { return x.LastEndpointChange } return nil } var File_resource_definitions_kubespan_kubespan_proto protoreflect.FileDescriptor const file_resource_definitions_kubespan_kubespan_proto_rawDesc = "" + "\n" + ",resource/definitions/kubespan/kubespan.proto\x12#talos.resource.definitions.kubespan\x1a\x13common/common.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a&resource/definitions/enums/enums.proto\"\xd9\x03\n" + "\n" + "ConfigSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12\x1d\n" + "\n" + "cluster_id\x18\x02 \x01(\tR\tclusterId\x12#\n" + "\rshared_secret\x18\x03 \x01(\tR\fsharedSecret\x12#\n" + "\rforce_routing\x18\x04 \x01(\bR\fforceRouting\x12B\n" + "\x1dadvertise_kubernetes_networks\x18\x05 \x01(\bR\x1badvertiseKubernetesNetworks\x12\x10\n" + "\x03mtu\x18\x06 \x01(\rR\x03mtu\x12)\n" + "\x10endpoint_filters\x18\a \x03(\tR\x0fendpointFilters\x126\n" + "\x17harvest_extra_endpoints\x18\b \x01(\bR\x15harvestExtraEndpoints\x12:\n" + "\x0fextra_endpoints\x18\t \x03(\v2\x11.common.NetIPPortR\x0eextraEndpoints\x12S\n" + "\x1bexclude_advertised_networks\x18\n" + " \x03(\v2\x13.common.NetIPPrefixR\x19excludeAdvertisedNetworks\"`\n" + "\fEndpointSpec\x12!\n" + "\faffiliate_id\x18\x01 \x01(\tR\vaffiliateId\x12-\n" + "\bendpoint\x18\x02 \x01(\v2\x11.common.NetIPPortR\bendpoint\"\xaa\x01\n" + "\fIdentitySpec\x12-\n" + "\aaddress\x18\x01 \x01(\v2\x13.common.NetIPPrefixR\aaddress\x12+\n" + "\x06subnet\x18\x02 \x01(\v2\x13.common.NetIPPrefixR\x06subnet\x12\x1f\n" + "\vprivate_key\x18\x03 \x01(\tR\n" + "privateKey\x12\x1d\n" + "\n" + "public_key\x18\x04 \x01(\tR\tpublicKey\"\xb4\x01\n" + "\fPeerSpecSpec\x12'\n" + "\aaddress\x18\x01 \x01(\v2\r.common.NetIPR\aaddress\x124\n" + "\vallowed_ips\x18\x02 \x03(\v2\x13.common.NetIPPrefixR\n" + "allowedIps\x12/\n" + "\tendpoints\x18\x03 \x03(\v2\x11.common.NetIPPortR\tendpoints\x12\x14\n" + "\x05label\x18\x04 \x01(\tR\x05label\"\xc7\x03\n" + "\x0ePeerStatusSpec\x12-\n" + "\bendpoint\x18\x01 \x01(\v2\x11.common.NetIPPortR\bendpoint\x12\x14\n" + "\x05label\x18\x02 \x01(\tR\x05label\x12I\n" + "\x05state\x18\x03 \x01(\x0e23.talos.resource.definitions.enums.KubespanPeerStateR\x05state\x12#\n" + "\rreceive_bytes\x18\x04 \x01(\x03R\freceiveBytes\x12%\n" + "\x0etransmit_bytes\x18\x05 \x01(\x03R\rtransmitBytes\x12J\n" + "\x13last_handshake_time\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\x11lastHandshakeTime\x12?\n" + "\x12last_used_endpoint\x18\a \x01(\v2\x11.common.NetIPPortR\x10lastUsedEndpoint\x12L\n" + "\x14last_endpoint_change\x18\b \x01(\v2\x1a.google.protobuf.TimestampR\x12lastEndpointChangeBz\n" + "+dev.talos.api.resource.definitions.kubespanZKgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/kubespanb\x06proto3" var ( file_resource_definitions_kubespan_kubespan_proto_rawDescOnce sync.Once file_resource_definitions_kubespan_kubespan_proto_rawDescData []byte ) func file_resource_definitions_kubespan_kubespan_proto_rawDescGZIP() []byte { file_resource_definitions_kubespan_kubespan_proto_rawDescOnce.Do(func() { file_resource_definitions_kubespan_kubespan_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_kubespan_kubespan_proto_rawDesc), len(file_resource_definitions_kubespan_kubespan_proto_rawDesc))) }) return file_resource_definitions_kubespan_kubespan_proto_rawDescData } var file_resource_definitions_kubespan_kubespan_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_resource_definitions_kubespan_kubespan_proto_goTypes = []any{ (*ConfigSpec)(nil), // 0: talos.resource.definitions.kubespan.ConfigSpec (*EndpointSpec)(nil), // 1: talos.resource.definitions.kubespan.EndpointSpec (*IdentitySpec)(nil), // 2: talos.resource.definitions.kubespan.IdentitySpec (*PeerSpecSpec)(nil), // 3: talos.resource.definitions.kubespan.PeerSpecSpec (*PeerStatusSpec)(nil), // 4: talos.resource.definitions.kubespan.PeerStatusSpec (*common.NetIPPort)(nil), // 5: common.NetIPPort (*common.NetIPPrefix)(nil), // 6: common.NetIPPrefix (*common.NetIP)(nil), // 7: common.NetIP (enums.KubespanPeerState)(0), // 8: talos.resource.definitions.enums.KubespanPeerState (*timestamppb.Timestamp)(nil), // 9: google.protobuf.Timestamp } var file_resource_definitions_kubespan_kubespan_proto_depIdxs = []int32{ 5, // 0: talos.resource.definitions.kubespan.ConfigSpec.extra_endpoints:type_name -> common.NetIPPort 6, // 1: talos.resource.definitions.kubespan.ConfigSpec.exclude_advertised_networks:type_name -> common.NetIPPrefix 5, // 2: talos.resource.definitions.kubespan.EndpointSpec.endpoint:type_name -> common.NetIPPort 6, // 3: talos.resource.definitions.kubespan.IdentitySpec.address:type_name -> common.NetIPPrefix 6, // 4: talos.resource.definitions.kubespan.IdentitySpec.subnet:type_name -> common.NetIPPrefix 7, // 5: talos.resource.definitions.kubespan.PeerSpecSpec.address:type_name -> common.NetIP 6, // 6: talos.resource.definitions.kubespan.PeerSpecSpec.allowed_ips:type_name -> common.NetIPPrefix 5, // 7: talos.resource.definitions.kubespan.PeerSpecSpec.endpoints:type_name -> common.NetIPPort 5, // 8: talos.resource.definitions.kubespan.PeerStatusSpec.endpoint:type_name -> common.NetIPPort 8, // 9: talos.resource.definitions.kubespan.PeerStatusSpec.state:type_name -> talos.resource.definitions.enums.KubespanPeerState 9, // 10: talos.resource.definitions.kubespan.PeerStatusSpec.last_handshake_time:type_name -> google.protobuf.Timestamp 5, // 11: talos.resource.definitions.kubespan.PeerStatusSpec.last_used_endpoint:type_name -> common.NetIPPort 9, // 12: talos.resource.definitions.kubespan.PeerStatusSpec.last_endpoint_change:type_name -> google.protobuf.Timestamp 13, // [13:13] is the sub-list for method output_type 13, // [13:13] is the sub-list for method input_type 13, // [13:13] is the sub-list for extension type_name 13, // [13:13] is the sub-list for extension extendee 0, // [0:13] is the sub-list for field type_name } func init() { file_resource_definitions_kubespan_kubespan_proto_init() } func file_resource_definitions_kubespan_kubespan_proto_init() { if File_resource_definitions_kubespan_kubespan_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_kubespan_kubespan_proto_rawDesc), len(file_resource_definitions_kubespan_kubespan_proto_rawDesc)), NumEnums: 0, NumMessages: 5, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_kubespan_kubespan_proto_goTypes, DependencyIndexes: file_resource_definitions_kubespan_kubespan_proto_depIdxs, MessageInfos: file_resource_definitions_kubespan_kubespan_proto_msgTypes, }.Build() File_resource_definitions_kubespan_kubespan_proto = out.File file_resource_definitions_kubespan_kubespan_proto_goTypes = nil file_resource_definitions_kubespan_kubespan_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/kubespan/kubespan_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/kubespan/kubespan.proto package kubespan import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" timestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb1 "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExcludeAdvertisedNetworks) > 0 { for iNdEx := len(m.ExcludeAdvertisedNetworks) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExcludeAdvertisedNetworks[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExcludeAdvertisedNetworks[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x52 } } if len(m.ExtraEndpoints) > 0 { for iNdEx := len(m.ExtraEndpoints) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExtraEndpoints[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExtraEndpoints[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x4a } } if m.HarvestExtraEndpoints { i-- if m.HarvestExtraEndpoints { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if len(m.EndpointFilters) > 0 { for iNdEx := len(m.EndpointFilters) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.EndpointFilters[iNdEx]) copy(dAtA[i:], m.EndpointFilters[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.EndpointFilters[iNdEx]))) i-- dAtA[i] = 0x3a } } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x30 } if m.AdvertiseKubernetesNetworks { i-- if m.AdvertiseKubernetesNetworks { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.ForceRouting { i-- if m.ForceRouting { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.SharedSecret) > 0 { i -= len(m.SharedSecret) copy(dAtA[i:], m.SharedSecret) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SharedSecret))) i-- dAtA[i] = 0x1a } if len(m.ClusterId) > 0 { i -= len(m.ClusterId) copy(dAtA[i:], m.ClusterId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ClusterId))) i-- dAtA[i] = 0x12 } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EndpointSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EndpointSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EndpointSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Endpoint != nil { if vtmsg, ok := interface{}(m.Endpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if len(m.AffiliateId) > 0 { i -= len(m.AffiliateId) copy(dAtA[i:], m.AffiliateId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AffiliateId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *IdentitySpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *IdentitySpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *IdentitySpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.PublicKey) > 0 { i -= len(m.PublicKey) copy(dAtA[i:], m.PublicKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) i-- dAtA[i] = 0x22 } if len(m.PrivateKey) > 0 { i -= len(m.PrivateKey) copy(dAtA[i:], m.PrivateKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PrivateKey))) i-- dAtA[i] = 0x1a } if m.Subnet != nil { if vtmsg, ok := interface{}(m.Subnet).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Subnet) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Address != nil { if vtmsg, ok := interface{}(m.Address).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Address) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PeerSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PeerSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PeerSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x22 } if len(m.Endpoints) > 0 { for iNdEx := len(m.Endpoints) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Endpoints[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoints[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } } if len(m.AllowedIps) > 0 { for iNdEx := len(m.AllowedIps) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AllowedIps[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AllowedIps[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if m.Address != nil { if vtmsg, ok := interface{}(m.Address).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Address) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PeerStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PeerStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PeerStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.LastEndpointChange != nil { size, err := (*timestamppb.Timestamp)(m.LastEndpointChange).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } if m.LastUsedEndpoint != nil { if vtmsg, ok := interface{}(m.LastUsedEndpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.LastUsedEndpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x3a } if m.LastHandshakeTime != nil { size, err := (*timestamppb.Timestamp)(m.LastHandshakeTime).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.TransmitBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TransmitBytes)) i-- dAtA[i] = 0x28 } if m.ReceiveBytes != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReceiveBytes)) i-- dAtA[i] = 0x20 } if m.State != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.State)) i-- dAtA[i] = 0x18 } if len(m.Label) > 0 { i -= len(m.Label) copy(dAtA[i:], m.Label) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Label))) i-- dAtA[i] = 0x12 } if m.Endpoint != nil { if vtmsg, ok := interface{}(m.Endpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } l = len(m.ClusterId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SharedSecret) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ForceRouting { n += 2 } if m.AdvertiseKubernetesNetworks { n += 2 } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } if len(m.EndpointFilters) > 0 { for _, s := range m.EndpointFilters { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.HarvestExtraEndpoints { n += 2 } if len(m.ExtraEndpoints) > 0 { for _, e := range m.ExtraEndpoints { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExcludeAdvertisedNetworks) > 0 { for _, e := range m.ExcludeAdvertisedNetworks { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EndpointSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.AffiliateId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Endpoint != nil { if size, ok := interface{}(m.Endpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Endpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *IdentitySpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Address != nil { if size, ok := interface{}(m.Address).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Address) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Subnet != nil { if size, ok := interface{}(m.Subnet).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Subnet) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PrivateKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PublicKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PeerSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Address != nil { if size, ok := interface{}(m.Address).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Address) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AllowedIps) > 0 { for _, e := range m.AllowedIps { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Endpoints) > 0 { for _, e := range m.Endpoints { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PeerStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Endpoint != nil { if size, ok := interface{}(m.Endpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Endpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Label) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.State != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.State)) } if m.ReceiveBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReceiveBytes)) } if m.TransmitBytes != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TransmitBytes)) } if m.LastHandshakeTime != nil { l = (*timestamppb.Timestamp)(m.LastHandshakeTime).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LastUsedEndpoint != nil { if size, ok := interface{}(m.LastUsedEndpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.LastUsedEndpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LastEndpointChange != nil { l = (*timestamppb.Timestamp)(m.LastEndpointChange).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClusterId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ClusterId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SharedSecret", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SharedSecret = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ForceRouting", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ForceRouting = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AdvertiseKubernetesNetworks", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AdvertiseKubernetesNetworks = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EndpointFilters", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.EndpointFilters = append(m.EndpointFilters, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HarvestExtraEndpoints", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.HarvestExtraEndpoints = bool(v != 0) case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExtraEndpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExtraEndpoints = append(m.ExtraEndpoints, &common.NetIPPort{}) if unmarshal, ok := interface{}(m.ExtraEndpoints[len(m.ExtraEndpoints)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExtraEndpoints[len(m.ExtraEndpoints)-1]); err != nil { return err } } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExcludeAdvertisedNetworks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExcludeAdvertisedNetworks = append(m.ExcludeAdvertisedNetworks, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.ExcludeAdvertisedNetworks[len(m.ExcludeAdvertisedNetworks)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExcludeAdvertisedNetworks[len(m.ExcludeAdvertisedNetworks)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EndpointSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EndpointSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EndpointSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AffiliateId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AffiliateId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Endpoint == nil { m.Endpoint = &common.NetIPPort{} } if unmarshal, ok := interface{}(m.Endpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoint); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *IdentitySpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: IdentitySpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: IdentitySpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Address == nil { m.Address = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Address).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Address); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Subnet", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Subnet == nil { m.Subnet = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Subnet).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Subnet); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PrivateKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PrivateKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PublicKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PeerSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PeerSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PeerSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Address == nil { m.Address = &common.NetIP{} } if unmarshal, ok := interface{}(m.Address).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Address); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AllowedIps", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AllowedIps = append(m.AllowedIps, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.AllowedIps[len(m.AllowedIps)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AllowedIps[len(m.AllowedIps)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoints", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoints = append(m.Endpoints, &common.NetIPPort{}) if unmarshal, ok := interface{}(m.Endpoints[len(m.Endpoints)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoints[len(m.Endpoints)-1]); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PeerStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PeerStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PeerStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Endpoint == nil { m.Endpoint = &common.NetIPPort{} } if unmarshal, ok := interface{}(m.Endpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoint); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Label", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Label = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } m.State = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.State |= enums.KubespanPeerState(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReceiveBytes", wireType) } m.ReceiveBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReceiveBytes |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TransmitBytes", wireType) } m.TransmitBytes = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TransmitBytes |= int64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastHandshakeTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastHandshakeTime == nil { m.LastHandshakeTime = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.LastHandshakeTime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastUsedEndpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastUsedEndpoint == nil { m.LastUsedEndpoint = &common.NetIPPort{} } if unmarshal, ok := interface{}(m.LastUsedEndpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.LastUsedEndpoint); err != nil { return err } } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastEndpointChange", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastEndpointChange == nil { m.LastEndpointChange = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.LastEndpointChange).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/network/network.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/network/network.proto package network import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" runtime "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/runtime" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // AddressSpecSpec describes status of rendered secrets. type AddressSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Address *common.NetIPPrefix `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` LinkName string `protobuf:"bytes,2,opt,name=link_name,json=linkName,proto3" json:"link_name,omitempty"` Family enums.NethelpersFamily `protobuf:"varint,3,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Scope enums.NethelpersScope `protobuf:"varint,4,opt,name=scope,proto3,enum=talos.resource.definitions.enums.NethelpersScope" json:"scope,omitempty"` Flags uint32 `protobuf:"varint,5,opt,name=flags,proto3" json:"flags,omitempty"` AnnounceWithArp bool `protobuf:"varint,6,opt,name=announce_with_arp,json=announceWithArp,proto3" json:"announce_with_arp,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,7,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` Priority uint32 `protobuf:"varint,8,opt,name=priority,proto3" json:"priority,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddressSpecSpec) Reset() { *x = AddressSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddressSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddressSpecSpec) ProtoMessage() {} func (x *AddressSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddressSpecSpec.ProtoReflect.Descriptor instead. func (*AddressSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{0} } func (x *AddressSpecSpec) GetAddress() *common.NetIPPrefix { if x != nil { return x.Address } return nil } func (x *AddressSpecSpec) GetLinkName() string { if x != nil { return x.LinkName } return "" } func (x *AddressSpecSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *AddressSpecSpec) GetScope() enums.NethelpersScope { if x != nil { return x.Scope } return enums.NethelpersScope(0) } func (x *AddressSpecSpec) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *AddressSpecSpec) GetAnnounceWithArp() bool { if x != nil { return x.AnnounceWithArp } return false } func (x *AddressSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } func (x *AddressSpecSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } // AddressStatusSpec describes status of rendered secrets. type AddressStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Address *common.NetIPPrefix `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` Local *common.NetIP `protobuf:"bytes,2,opt,name=local,proto3" json:"local,omitempty"` Broadcast *common.NetIP `protobuf:"bytes,3,opt,name=broadcast,proto3" json:"broadcast,omitempty"` Anycast *common.NetIP `protobuf:"bytes,4,opt,name=anycast,proto3" json:"anycast,omitempty"` Multicast *common.NetIP `protobuf:"bytes,5,opt,name=multicast,proto3" json:"multicast,omitempty"` LinkIndex uint32 `protobuf:"varint,6,opt,name=link_index,json=linkIndex,proto3" json:"link_index,omitempty"` LinkName string `protobuf:"bytes,7,opt,name=link_name,json=linkName,proto3" json:"link_name,omitempty"` Family enums.NethelpersFamily `protobuf:"varint,8,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Scope enums.NethelpersScope `protobuf:"varint,9,opt,name=scope,proto3,enum=talos.resource.definitions.enums.NethelpersScope" json:"scope,omitempty"` Flags uint32 `protobuf:"varint,10,opt,name=flags,proto3" json:"flags,omitempty"` Priority uint32 `protobuf:"varint,11,opt,name=priority,proto3" json:"priority,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AddressStatusSpec) Reset() { *x = AddressStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AddressStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AddressStatusSpec) ProtoMessage() {} func (x *AddressStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AddressStatusSpec.ProtoReflect.Descriptor instead. func (*AddressStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{1} } func (x *AddressStatusSpec) GetAddress() *common.NetIPPrefix { if x != nil { return x.Address } return nil } func (x *AddressStatusSpec) GetLocal() *common.NetIP { if x != nil { return x.Local } return nil } func (x *AddressStatusSpec) GetBroadcast() *common.NetIP { if x != nil { return x.Broadcast } return nil } func (x *AddressStatusSpec) GetAnycast() *common.NetIP { if x != nil { return x.Anycast } return nil } func (x *AddressStatusSpec) GetMulticast() *common.NetIP { if x != nil { return x.Multicast } return nil } func (x *AddressStatusSpec) GetLinkIndex() uint32 { if x != nil { return x.LinkIndex } return 0 } func (x *AddressStatusSpec) GetLinkName() string { if x != nil { return x.LinkName } return "" } func (x *AddressStatusSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *AddressStatusSpec) GetScope() enums.NethelpersScope { if x != nil { return x.Scope } return enums.NethelpersScope(0) } func (x *AddressStatusSpec) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *AddressStatusSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } // BondMasterSpec describes bond settings if Kind == "bond". type BondMasterSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Mode enums.NethelpersBondMode `protobuf:"varint,1,opt,name=mode,proto3,enum=talos.resource.definitions.enums.NethelpersBondMode" json:"mode,omitempty"` HashPolicy enums.NethelpersBondXmitHashPolicy `protobuf:"varint,2,opt,name=hash_policy,json=hashPolicy,proto3,enum=talos.resource.definitions.enums.NethelpersBondXmitHashPolicy" json:"hash_policy,omitempty"` LacpRate enums.NethelpersLACPRate `protobuf:"varint,3,opt,name=lacp_rate,json=lacpRate,proto3,enum=talos.resource.definitions.enums.NethelpersLACPRate" json:"lacp_rate,omitempty"` ArpValidate enums.NethelpersARPValidate `protobuf:"varint,4,opt,name=arp_validate,json=arpValidate,proto3,enum=talos.resource.definitions.enums.NethelpersARPValidate" json:"arp_validate,omitempty"` ArpAllTargets enums.NethelpersARPAllTargets `protobuf:"varint,5,opt,name=arp_all_targets,json=arpAllTargets,proto3,enum=talos.resource.definitions.enums.NethelpersARPAllTargets" json:"arp_all_targets,omitempty"` PrimaryIndex uint32 `protobuf:"varint,6,opt,name=primary_index,json=primaryIndex,proto3" json:"primary_index,omitempty"` PrimaryReselect enums.NethelpersPrimaryReselect `protobuf:"varint,7,opt,name=primary_reselect,json=primaryReselect,proto3,enum=talos.resource.definitions.enums.NethelpersPrimaryReselect" json:"primary_reselect,omitempty"` FailOverMac enums.NethelpersFailOverMAC `protobuf:"varint,8,opt,name=fail_over_mac,json=failOverMac,proto3,enum=talos.resource.definitions.enums.NethelpersFailOverMAC" json:"fail_over_mac,omitempty"` AdSelect enums.NethelpersADSelect `protobuf:"varint,9,opt,name=ad_select,json=adSelect,proto3,enum=talos.resource.definitions.enums.NethelpersADSelect" json:"ad_select,omitempty"` MiiMon uint32 `protobuf:"varint,10,opt,name=mii_mon,json=miiMon,proto3" json:"mii_mon,omitempty"` UpDelay uint32 `protobuf:"varint,11,opt,name=up_delay,json=upDelay,proto3" json:"up_delay,omitempty"` DownDelay uint32 `protobuf:"varint,12,opt,name=down_delay,json=downDelay,proto3" json:"down_delay,omitempty"` ArpInterval uint32 `protobuf:"varint,13,opt,name=arp_interval,json=arpInterval,proto3" json:"arp_interval,omitempty"` ResendIgmp uint32 `protobuf:"varint,14,opt,name=resend_igmp,json=resendIgmp,proto3" json:"resend_igmp,omitempty"` MinLinks uint32 `protobuf:"varint,15,opt,name=min_links,json=minLinks,proto3" json:"min_links,omitempty"` LpInterval uint32 `protobuf:"varint,16,opt,name=lp_interval,json=lpInterval,proto3" json:"lp_interval,omitempty"` PacketsPerSlave uint32 `protobuf:"varint,17,opt,name=packets_per_slave,json=packetsPerSlave,proto3" json:"packets_per_slave,omitempty"` NumPeerNotif uint32 `protobuf:"varint,18,opt,name=num_peer_notif,json=numPeerNotif,proto3" json:"num_peer_notif,omitempty"` TlbDynamicLb uint32 `protobuf:"varint,19,opt,name=tlb_dynamic_lb,json=tlbDynamicLb,proto3" json:"tlb_dynamic_lb,omitempty"` AllSlavesActive uint32 `protobuf:"varint,20,opt,name=all_slaves_active,json=allSlavesActive,proto3" json:"all_slaves_active,omitempty"` UseCarrier bool `protobuf:"varint,21,opt,name=use_carrier,json=useCarrier,proto3" json:"use_carrier,omitempty"` AdActorSysPrio uint32 `protobuf:"varint,22,opt,name=ad_actor_sys_prio,json=adActorSysPrio,proto3" json:"ad_actor_sys_prio,omitempty"` AdUserPortKey uint32 `protobuf:"varint,23,opt,name=ad_user_port_key,json=adUserPortKey,proto3" json:"ad_user_port_key,omitempty"` PeerNotifyDelay uint32 `protobuf:"varint,24,opt,name=peer_notify_delay,json=peerNotifyDelay,proto3" json:"peer_notify_delay,omitempty"` ArpipTargets []*common.NetIP `protobuf:"bytes,25,rep,name=arpip_targets,json=arpipTargets,proto3" json:"arpip_targets,omitempty"` Nsip6Targets []*common.NetIP `protobuf:"bytes,26,rep,name=nsip6_targets,json=nsip6Targets,proto3" json:"nsip6_targets,omitempty"` AdlacpActive enums.NethelpersADLACPActive `protobuf:"varint,27,opt,name=adlacp_active,json=adlacpActive,proto3,enum=talos.resource.definitions.enums.NethelpersADLACPActive" json:"adlacp_active,omitempty"` MissedMax uint32 `protobuf:"varint,28,opt,name=missed_max,json=missedMax,proto3" json:"missed_max,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BondMasterSpec) Reset() { *x = BondMasterSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BondMasterSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*BondMasterSpec) ProtoMessage() {} func (x *BondMasterSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BondMasterSpec.ProtoReflect.Descriptor instead. func (*BondMasterSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{2} } func (x *BondMasterSpec) GetMode() enums.NethelpersBondMode { if x != nil { return x.Mode } return enums.NethelpersBondMode(0) } func (x *BondMasterSpec) GetHashPolicy() enums.NethelpersBondXmitHashPolicy { if x != nil { return x.HashPolicy } return enums.NethelpersBondXmitHashPolicy(0) } func (x *BondMasterSpec) GetLacpRate() enums.NethelpersLACPRate { if x != nil { return x.LacpRate } return enums.NethelpersLACPRate(0) } func (x *BondMasterSpec) GetArpValidate() enums.NethelpersARPValidate { if x != nil { return x.ArpValidate } return enums.NethelpersARPValidate(0) } func (x *BondMasterSpec) GetArpAllTargets() enums.NethelpersARPAllTargets { if x != nil { return x.ArpAllTargets } return enums.NethelpersARPAllTargets(0) } func (x *BondMasterSpec) GetPrimaryIndex() uint32 { if x != nil { return x.PrimaryIndex } return 0 } func (x *BondMasterSpec) GetPrimaryReselect() enums.NethelpersPrimaryReselect { if x != nil { return x.PrimaryReselect } return enums.NethelpersPrimaryReselect(0) } func (x *BondMasterSpec) GetFailOverMac() enums.NethelpersFailOverMAC { if x != nil { return x.FailOverMac } return enums.NethelpersFailOverMAC(0) } func (x *BondMasterSpec) GetAdSelect() enums.NethelpersADSelect { if x != nil { return x.AdSelect } return enums.NethelpersADSelect(0) } func (x *BondMasterSpec) GetMiiMon() uint32 { if x != nil { return x.MiiMon } return 0 } func (x *BondMasterSpec) GetUpDelay() uint32 { if x != nil { return x.UpDelay } return 0 } func (x *BondMasterSpec) GetDownDelay() uint32 { if x != nil { return x.DownDelay } return 0 } func (x *BondMasterSpec) GetArpInterval() uint32 { if x != nil { return x.ArpInterval } return 0 } func (x *BondMasterSpec) GetResendIgmp() uint32 { if x != nil { return x.ResendIgmp } return 0 } func (x *BondMasterSpec) GetMinLinks() uint32 { if x != nil { return x.MinLinks } return 0 } func (x *BondMasterSpec) GetLpInterval() uint32 { if x != nil { return x.LpInterval } return 0 } func (x *BondMasterSpec) GetPacketsPerSlave() uint32 { if x != nil { return x.PacketsPerSlave } return 0 } func (x *BondMasterSpec) GetNumPeerNotif() uint32 { if x != nil { return x.NumPeerNotif } return 0 } func (x *BondMasterSpec) GetTlbDynamicLb() uint32 { if x != nil { return x.TlbDynamicLb } return 0 } func (x *BondMasterSpec) GetAllSlavesActive() uint32 { if x != nil { return x.AllSlavesActive } return 0 } func (x *BondMasterSpec) GetUseCarrier() bool { if x != nil { return x.UseCarrier } return false } func (x *BondMasterSpec) GetAdActorSysPrio() uint32 { if x != nil { return x.AdActorSysPrio } return 0 } func (x *BondMasterSpec) GetAdUserPortKey() uint32 { if x != nil { return x.AdUserPortKey } return 0 } func (x *BondMasterSpec) GetPeerNotifyDelay() uint32 { if x != nil { return x.PeerNotifyDelay } return 0 } func (x *BondMasterSpec) GetArpipTargets() []*common.NetIP { if x != nil { return x.ArpipTargets } return nil } func (x *BondMasterSpec) GetNsip6Targets() []*common.NetIP { if x != nil { return x.Nsip6Targets } return nil } func (x *BondMasterSpec) GetAdlacpActive() enums.NethelpersADLACPActive { if x != nil { return x.AdlacpActive } return enums.NethelpersADLACPActive(0) } func (x *BondMasterSpec) GetMissedMax() uint32 { if x != nil { return x.MissedMax } return 0 } // BondSlave contains a bond's master name and slave index. type BondSlave struct { state protoimpl.MessageState `protogen:"open.v1"` MasterName string `protobuf:"bytes,1,opt,name=master_name,json=masterName,proto3" json:"master_name,omitempty"` SlaveIndex int64 `protobuf:"varint,2,opt,name=slave_index,json=slaveIndex,proto3" json:"slave_index,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BondSlave) Reset() { *x = BondSlave{} mi := &file_resource_definitions_network_network_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BondSlave) String() string { return protoimpl.X.MessageStringOf(x) } func (*BondSlave) ProtoMessage() {} func (x *BondSlave) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BondSlave.ProtoReflect.Descriptor instead. func (*BondSlave) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{3} } func (x *BondSlave) GetMasterName() string { if x != nil { return x.MasterName } return "" } func (x *BondSlave) GetSlaveIndex() int64 { if x != nil { return x.SlaveIndex } return 0 } // BridgeMasterSpec describes bridge settings if Kind == "bridge". type BridgeMasterSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Stp *STPSpec `protobuf:"bytes,1,opt,name=stp,proto3" json:"stp,omitempty"` Vlan *BridgeVLANSpec `protobuf:"bytes,2,opt,name=vlan,proto3" json:"vlan,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BridgeMasterSpec) Reset() { *x = BridgeMasterSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BridgeMasterSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*BridgeMasterSpec) ProtoMessage() {} func (x *BridgeMasterSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BridgeMasterSpec.ProtoReflect.Descriptor instead. func (*BridgeMasterSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{4} } func (x *BridgeMasterSpec) GetStp() *STPSpec { if x != nil { return x.Stp } return nil } func (x *BridgeMasterSpec) GetVlan() *BridgeVLANSpec { if x != nil { return x.Vlan } return nil } // BridgeSlave contains the name of the master bridge of a bridged interface type BridgeSlave struct { state protoimpl.MessageState `protogen:"open.v1"` MasterName string `protobuf:"bytes,1,opt,name=master_name,json=masterName,proto3" json:"master_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BridgeSlave) Reset() { *x = BridgeSlave{} mi := &file_resource_definitions_network_network_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BridgeSlave) String() string { return protoimpl.X.MessageStringOf(x) } func (*BridgeSlave) ProtoMessage() {} func (x *BridgeSlave) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BridgeSlave.ProtoReflect.Descriptor instead. func (*BridgeSlave) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{5} } func (x *BridgeSlave) GetMasterName() string { if x != nil { return x.MasterName } return "" } // BridgeVLANSpec describes VLAN settings of a bridge. type BridgeVLANSpec struct { state protoimpl.MessageState `protogen:"open.v1"` FilteringEnabled bool `protobuf:"varint,1,opt,name=filtering_enabled,json=filteringEnabled,proto3" json:"filtering_enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BridgeVLANSpec) Reset() { *x = BridgeVLANSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BridgeVLANSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*BridgeVLANSpec) ProtoMessage() {} func (x *BridgeVLANSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BridgeVLANSpec.ProtoReflect.Descriptor instead. func (*BridgeVLANSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{6} } func (x *BridgeVLANSpec) GetFilteringEnabled() bool { if x != nil { return x.FilteringEnabled } return false } // ClientIdentifierSpec is a shared DHCP4/DHCP6 client identifier spec. type ClientIdentifierSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ClientIdentifier enums.NethelpersClientIdentifier `protobuf:"varint,1,opt,name=client_identifier,json=clientIdentifier,proto3,enum=talos.resource.definitions.enums.NethelpersClientIdentifier" json:"client_identifier,omitempty"` DuidRawHex string `protobuf:"bytes,2,opt,name=duid_raw_hex,json=duidRawHex,proto3" json:"duid_raw_hex,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ClientIdentifierSpec) Reset() { *x = ClientIdentifierSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ClientIdentifierSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ClientIdentifierSpec) ProtoMessage() {} func (x *ClientIdentifierSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ClientIdentifierSpec.ProtoReflect.Descriptor instead. func (*ClientIdentifierSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{7} } func (x *ClientIdentifierSpec) GetClientIdentifier() enums.NethelpersClientIdentifier { if x != nil { return x.ClientIdentifier } return enums.NethelpersClientIdentifier(0) } func (x *ClientIdentifierSpec) GetDuidRawHex() string { if x != nil { return x.DuidRawHex } return "" } // DHCP4OperatorSpec describes DHCP4 operator options. type DHCP4OperatorSpec struct { state protoimpl.MessageState `protogen:"open.v1"` RouteMetric uint32 `protobuf:"varint,1,opt,name=route_metric,json=routeMetric,proto3" json:"route_metric,omitempty"` SkipHostnameRequest bool `protobuf:"varint,2,opt,name=skip_hostname_request,json=skipHostnameRequest,proto3" json:"skip_hostname_request,omitempty"` ClientIdentifier *ClientIdentifierSpec `protobuf:"bytes,3,opt,name=client_identifier,json=clientIdentifier,proto3" json:"client_identifier,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DHCP4OperatorSpec) Reset() { *x = DHCP4OperatorSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DHCP4OperatorSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DHCP4OperatorSpec) ProtoMessage() {} func (x *DHCP4OperatorSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DHCP4OperatorSpec.ProtoReflect.Descriptor instead. func (*DHCP4OperatorSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{8} } func (x *DHCP4OperatorSpec) GetRouteMetric() uint32 { if x != nil { return x.RouteMetric } return 0 } func (x *DHCP4OperatorSpec) GetSkipHostnameRequest() bool { if x != nil { return x.SkipHostnameRequest } return false } func (x *DHCP4OperatorSpec) GetClientIdentifier() *ClientIdentifierSpec { if x != nil { return x.ClientIdentifier } return nil } // DHCP6OperatorSpec describes DHCP6 operator options. type DHCP6OperatorSpec struct { state protoimpl.MessageState `protogen:"open.v1"` RouteMetric uint32 `protobuf:"varint,2,opt,name=route_metric,json=routeMetric,proto3" json:"route_metric,omitempty"` SkipHostnameRequest bool `protobuf:"varint,3,opt,name=skip_hostname_request,json=skipHostnameRequest,proto3" json:"skip_hostname_request,omitempty"` ClientIdentifier *ClientIdentifierSpec `protobuf:"bytes,4,opt,name=client_identifier,json=clientIdentifier,proto3" json:"client_identifier,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DHCP6OperatorSpec) Reset() { *x = DHCP6OperatorSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DHCP6OperatorSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DHCP6OperatorSpec) ProtoMessage() {} func (x *DHCP6OperatorSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DHCP6OperatorSpec.ProtoReflect.Descriptor instead. func (*DHCP6OperatorSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{9} } func (x *DHCP6OperatorSpec) GetRouteMetric() uint32 { if x != nil { return x.RouteMetric } return 0 } func (x *DHCP6OperatorSpec) GetSkipHostnameRequest() bool { if x != nil { return x.SkipHostnameRequest } return false } func (x *DHCP6OperatorSpec) GetClientIdentifier() *ClientIdentifierSpec { if x != nil { return x.ClientIdentifier } return nil } // DNSResolveCacheSpec describes DNS servers status. type DNSResolveCacheSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DNSResolveCacheSpec) Reset() { *x = DNSResolveCacheSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DNSResolveCacheSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DNSResolveCacheSpec) ProtoMessage() {} func (x *DNSResolveCacheSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DNSResolveCacheSpec.ProtoReflect.Descriptor instead. func (*DNSResolveCacheSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{10} } func (x *DNSResolveCacheSpec) GetStatus() string { if x != nil { return x.Status } return "" } // EthernetChannelsSpec describes config of Ethernet channels. type EthernetChannelsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Rx uint32 `protobuf:"varint,1,opt,name=rx,proto3" json:"rx,omitempty"` Tx uint32 `protobuf:"varint,2,opt,name=tx,proto3" json:"tx,omitempty"` Other uint32 `protobuf:"varint,3,opt,name=other,proto3" json:"other,omitempty"` Combined uint32 `protobuf:"varint,4,opt,name=combined,proto3" json:"combined,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetChannelsSpec) Reset() { *x = EthernetChannelsSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetChannelsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetChannelsSpec) ProtoMessage() {} func (x *EthernetChannelsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetChannelsSpec.ProtoReflect.Descriptor instead. func (*EthernetChannelsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{11} } func (x *EthernetChannelsSpec) GetRx() uint32 { if x != nil { return x.Rx } return 0 } func (x *EthernetChannelsSpec) GetTx() uint32 { if x != nil { return x.Tx } return 0 } func (x *EthernetChannelsSpec) GetOther() uint32 { if x != nil { return x.Other } return 0 } func (x *EthernetChannelsSpec) GetCombined() uint32 { if x != nil { return x.Combined } return 0 } // EthernetChannelsStatus describes status of Ethernet channels. type EthernetChannelsStatus struct { state protoimpl.MessageState `protogen:"open.v1"` RxMax uint32 `protobuf:"varint,1,opt,name=rx_max,json=rxMax,proto3" json:"rx_max,omitempty"` TxMax uint32 `protobuf:"varint,2,opt,name=tx_max,json=txMax,proto3" json:"tx_max,omitempty"` OtherMax uint32 `protobuf:"varint,3,opt,name=other_max,json=otherMax,proto3" json:"other_max,omitempty"` CombinedMax uint32 `protobuf:"varint,4,opt,name=combined_max,json=combinedMax,proto3" json:"combined_max,omitempty"` Rx uint32 `protobuf:"varint,5,opt,name=rx,proto3" json:"rx,omitempty"` Tx uint32 `protobuf:"varint,6,opt,name=tx,proto3" json:"tx,omitempty"` Other uint32 `protobuf:"varint,7,opt,name=other,proto3" json:"other,omitempty"` Combined uint32 `protobuf:"varint,8,opt,name=combined,proto3" json:"combined,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetChannelsStatus) Reset() { *x = EthernetChannelsStatus{} mi := &file_resource_definitions_network_network_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetChannelsStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetChannelsStatus) ProtoMessage() {} func (x *EthernetChannelsStatus) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetChannelsStatus.ProtoReflect.Descriptor instead. func (*EthernetChannelsStatus) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{12} } func (x *EthernetChannelsStatus) GetRxMax() uint32 { if x != nil { return x.RxMax } return 0 } func (x *EthernetChannelsStatus) GetTxMax() uint32 { if x != nil { return x.TxMax } return 0 } func (x *EthernetChannelsStatus) GetOtherMax() uint32 { if x != nil { return x.OtherMax } return 0 } func (x *EthernetChannelsStatus) GetCombinedMax() uint32 { if x != nil { return x.CombinedMax } return 0 } func (x *EthernetChannelsStatus) GetRx() uint32 { if x != nil { return x.Rx } return 0 } func (x *EthernetChannelsStatus) GetTx() uint32 { if x != nil { return x.Tx } return 0 } func (x *EthernetChannelsStatus) GetOther() uint32 { if x != nil { return x.Other } return 0 } func (x *EthernetChannelsStatus) GetCombined() uint32 { if x != nil { return x.Combined } return 0 } // EthernetFeatureStatus describes status of Ethernet features. type EthernetFeatureStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetFeatureStatus) Reset() { *x = EthernetFeatureStatus{} mi := &file_resource_definitions_network_network_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetFeatureStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetFeatureStatus) ProtoMessage() {} func (x *EthernetFeatureStatus) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetFeatureStatus.ProtoReflect.Descriptor instead. func (*EthernetFeatureStatus) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{13} } func (x *EthernetFeatureStatus) GetName() string { if x != nil { return x.Name } return "" } func (x *EthernetFeatureStatus) GetStatus() string { if x != nil { return x.Status } return "" } // EthernetRingsSpec describes config of Ethernet rings. type EthernetRingsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Rx uint32 `protobuf:"varint,1,opt,name=rx,proto3" json:"rx,omitempty"` RxMini uint32 `protobuf:"varint,2,opt,name=rx_mini,json=rxMini,proto3" json:"rx_mini,omitempty"` RxJumbo uint32 `protobuf:"varint,3,opt,name=rx_jumbo,json=rxJumbo,proto3" json:"rx_jumbo,omitempty"` Tx uint32 `protobuf:"varint,4,opt,name=tx,proto3" json:"tx,omitempty"` RxBufLen uint32 `protobuf:"varint,5,opt,name=rx_buf_len,json=rxBufLen,proto3" json:"rx_buf_len,omitempty"` CqeSize uint32 `protobuf:"varint,6,opt,name=cqe_size,json=cqeSize,proto3" json:"cqe_size,omitempty"` TxPush bool `protobuf:"varint,7,opt,name=tx_push,json=txPush,proto3" json:"tx_push,omitempty"` RxPush bool `protobuf:"varint,8,opt,name=rx_push,json=rxPush,proto3" json:"rx_push,omitempty"` TxPushBufLen uint32 `protobuf:"varint,9,opt,name=tx_push_buf_len,json=txPushBufLen,proto3" json:"tx_push_buf_len,omitempty"` TcpDataSplit bool `protobuf:"varint,10,opt,name=tcp_data_split,json=tcpDataSplit,proto3" json:"tcp_data_split,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetRingsSpec) Reset() { *x = EthernetRingsSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetRingsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetRingsSpec) ProtoMessage() {} func (x *EthernetRingsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetRingsSpec.ProtoReflect.Descriptor instead. func (*EthernetRingsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{14} } func (x *EthernetRingsSpec) GetRx() uint32 { if x != nil { return x.Rx } return 0 } func (x *EthernetRingsSpec) GetRxMini() uint32 { if x != nil { return x.RxMini } return 0 } func (x *EthernetRingsSpec) GetRxJumbo() uint32 { if x != nil { return x.RxJumbo } return 0 } func (x *EthernetRingsSpec) GetTx() uint32 { if x != nil { return x.Tx } return 0 } func (x *EthernetRingsSpec) GetRxBufLen() uint32 { if x != nil { return x.RxBufLen } return 0 } func (x *EthernetRingsSpec) GetCqeSize() uint32 { if x != nil { return x.CqeSize } return 0 } func (x *EthernetRingsSpec) GetTxPush() bool { if x != nil { return x.TxPush } return false } func (x *EthernetRingsSpec) GetRxPush() bool { if x != nil { return x.RxPush } return false } func (x *EthernetRingsSpec) GetTxPushBufLen() uint32 { if x != nil { return x.TxPushBufLen } return 0 } func (x *EthernetRingsSpec) GetTcpDataSplit() bool { if x != nil { return x.TcpDataSplit } return false } // EthernetRingsStatus describes status of Ethernet rings. type EthernetRingsStatus struct { state protoimpl.MessageState `protogen:"open.v1"` RxMax uint32 `protobuf:"varint,1,opt,name=rx_max,json=rxMax,proto3" json:"rx_max,omitempty"` RxMiniMax uint32 `protobuf:"varint,2,opt,name=rx_mini_max,json=rxMiniMax,proto3" json:"rx_mini_max,omitempty"` RxJumboMax uint32 `protobuf:"varint,3,opt,name=rx_jumbo_max,json=rxJumboMax,proto3" json:"rx_jumbo_max,omitempty"` TxMax uint32 `protobuf:"varint,4,opt,name=tx_max,json=txMax,proto3" json:"tx_max,omitempty"` TxPushBufLenMax uint32 `protobuf:"varint,5,opt,name=tx_push_buf_len_max,json=txPushBufLenMax,proto3" json:"tx_push_buf_len_max,omitempty"` Rx uint32 `protobuf:"varint,6,opt,name=rx,proto3" json:"rx,omitempty"` RxMini uint32 `protobuf:"varint,7,opt,name=rx_mini,json=rxMini,proto3" json:"rx_mini,omitempty"` RxJumbo uint32 `protobuf:"varint,8,opt,name=rx_jumbo,json=rxJumbo,proto3" json:"rx_jumbo,omitempty"` Tx uint32 `protobuf:"varint,9,opt,name=tx,proto3" json:"tx,omitempty"` RxBufLen uint32 `protobuf:"varint,10,opt,name=rx_buf_len,json=rxBufLen,proto3" json:"rx_buf_len,omitempty"` CqeSize uint32 `protobuf:"varint,11,opt,name=cqe_size,json=cqeSize,proto3" json:"cqe_size,omitempty"` TxPush bool `protobuf:"varint,12,opt,name=tx_push,json=txPush,proto3" json:"tx_push,omitempty"` RxPush bool `protobuf:"varint,13,opt,name=rx_push,json=rxPush,proto3" json:"rx_push,omitempty"` TxPushBufLen uint32 `protobuf:"varint,14,opt,name=tx_push_buf_len,json=txPushBufLen,proto3" json:"tx_push_buf_len,omitempty"` TcpDataSplit bool `protobuf:"varint,15,opt,name=tcp_data_split,json=tcpDataSplit,proto3" json:"tcp_data_split,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetRingsStatus) Reset() { *x = EthernetRingsStatus{} mi := &file_resource_definitions_network_network_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetRingsStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetRingsStatus) ProtoMessage() {} func (x *EthernetRingsStatus) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetRingsStatus.ProtoReflect.Descriptor instead. func (*EthernetRingsStatus) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{15} } func (x *EthernetRingsStatus) GetRxMax() uint32 { if x != nil { return x.RxMax } return 0 } func (x *EthernetRingsStatus) GetRxMiniMax() uint32 { if x != nil { return x.RxMiniMax } return 0 } func (x *EthernetRingsStatus) GetRxJumboMax() uint32 { if x != nil { return x.RxJumboMax } return 0 } func (x *EthernetRingsStatus) GetTxMax() uint32 { if x != nil { return x.TxMax } return 0 } func (x *EthernetRingsStatus) GetTxPushBufLenMax() uint32 { if x != nil { return x.TxPushBufLenMax } return 0 } func (x *EthernetRingsStatus) GetRx() uint32 { if x != nil { return x.Rx } return 0 } func (x *EthernetRingsStatus) GetRxMini() uint32 { if x != nil { return x.RxMini } return 0 } func (x *EthernetRingsStatus) GetRxJumbo() uint32 { if x != nil { return x.RxJumbo } return 0 } func (x *EthernetRingsStatus) GetTx() uint32 { if x != nil { return x.Tx } return 0 } func (x *EthernetRingsStatus) GetRxBufLen() uint32 { if x != nil { return x.RxBufLen } return 0 } func (x *EthernetRingsStatus) GetCqeSize() uint32 { if x != nil { return x.CqeSize } return 0 } func (x *EthernetRingsStatus) GetTxPush() bool { if x != nil { return x.TxPush } return false } func (x *EthernetRingsStatus) GetRxPush() bool { if x != nil { return x.RxPush } return false } func (x *EthernetRingsStatus) GetTxPushBufLen() uint32 { if x != nil { return x.TxPushBufLen } return 0 } func (x *EthernetRingsStatus) GetTcpDataSplit() bool { if x != nil { return x.TcpDataSplit } return false } // EthernetSpecSpec describes config of Ethernet link. type EthernetSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Rings *EthernetRingsSpec `protobuf:"bytes,1,opt,name=rings,proto3" json:"rings,omitempty"` Features map[string]bool `protobuf:"bytes,2,rep,name=features,proto3" json:"features,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"varint,2,opt,name=value"` Channels *EthernetChannelsSpec `protobuf:"bytes,3,opt,name=channels,proto3" json:"channels,omitempty"` WakeOnLan []enums.NethelpersWOLMode `protobuf:"varint,4,rep,packed,name=wake_on_lan,json=wakeOnLan,proto3,enum=talos.resource.definitions.enums.NethelpersWOLMode" json:"wake_on_lan,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetSpecSpec) Reset() { *x = EthernetSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetSpecSpec) ProtoMessage() {} func (x *EthernetSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetSpecSpec.ProtoReflect.Descriptor instead. func (*EthernetSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{16} } func (x *EthernetSpecSpec) GetRings() *EthernetRingsSpec { if x != nil { return x.Rings } return nil } func (x *EthernetSpecSpec) GetFeatures() map[string]bool { if x != nil { return x.Features } return nil } func (x *EthernetSpecSpec) GetChannels() *EthernetChannelsSpec { if x != nil { return x.Channels } return nil } func (x *EthernetSpecSpec) GetWakeOnLan() []enums.NethelpersWOLMode { if x != nil { return x.WakeOnLan } return nil } // EthernetStatusSpec describes status of rendered secrets. type EthernetStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` LinkState bool `protobuf:"varint,1,opt,name=link_state,json=linkState,proto3" json:"link_state,omitempty"` SpeedMegabits int64 `protobuf:"varint,2,opt,name=speed_megabits,json=speedMegabits,proto3" json:"speed_megabits,omitempty"` Port enums.NethelpersPort `protobuf:"varint,3,opt,name=port,proto3,enum=talos.resource.definitions.enums.NethelpersPort" json:"port,omitempty"` Duplex enums.NethelpersDuplex `protobuf:"varint,4,opt,name=duplex,proto3,enum=talos.resource.definitions.enums.NethelpersDuplex" json:"duplex,omitempty"` OurModes []string `protobuf:"bytes,5,rep,name=our_modes,json=ourModes,proto3" json:"our_modes,omitempty"` PeerModes []string `protobuf:"bytes,6,rep,name=peer_modes,json=peerModes,proto3" json:"peer_modes,omitempty"` Rings *EthernetRingsStatus `protobuf:"bytes,7,opt,name=rings,proto3" json:"rings,omitempty"` Features []*EthernetFeatureStatus `protobuf:"bytes,8,rep,name=features,proto3" json:"features,omitempty"` Channels *EthernetChannelsStatus `protobuf:"bytes,9,opt,name=channels,proto3" json:"channels,omitempty"` WakeOnLan []enums.NethelpersWOLMode `protobuf:"varint,10,rep,packed,name=wake_on_lan,json=wakeOnLan,proto3,enum=talos.resource.definitions.enums.NethelpersWOLMode" json:"wake_on_lan,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EthernetStatusSpec) Reset() { *x = EthernetStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EthernetStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EthernetStatusSpec) ProtoMessage() {} func (x *EthernetStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EthernetStatusSpec.ProtoReflect.Descriptor instead. func (*EthernetStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{17} } func (x *EthernetStatusSpec) GetLinkState() bool { if x != nil { return x.LinkState } return false } func (x *EthernetStatusSpec) GetSpeedMegabits() int64 { if x != nil { return x.SpeedMegabits } return 0 } func (x *EthernetStatusSpec) GetPort() enums.NethelpersPort { if x != nil { return x.Port } return enums.NethelpersPort(0) } func (x *EthernetStatusSpec) GetDuplex() enums.NethelpersDuplex { if x != nil { return x.Duplex } return enums.NethelpersDuplex(0) } func (x *EthernetStatusSpec) GetOurModes() []string { if x != nil { return x.OurModes } return nil } func (x *EthernetStatusSpec) GetPeerModes() []string { if x != nil { return x.PeerModes } return nil } func (x *EthernetStatusSpec) GetRings() *EthernetRingsStatus { if x != nil { return x.Rings } return nil } func (x *EthernetStatusSpec) GetFeatures() []*EthernetFeatureStatus { if x != nil { return x.Features } return nil } func (x *EthernetStatusSpec) GetChannels() *EthernetChannelsStatus { if x != nil { return x.Channels } return nil } func (x *EthernetStatusSpec) GetWakeOnLan() []enums.NethelpersWOLMode { if x != nil { return x.WakeOnLan } return nil } // HardwareAddrSpec describes spec for the link. type HardwareAddrSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` HardwareAddr []byte `protobuf:"bytes,2,opt,name=hardware_addr,json=hardwareAddr,proto3" json:"hardware_addr,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HardwareAddrSpec) Reset() { *x = HardwareAddrSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HardwareAddrSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*HardwareAddrSpec) ProtoMessage() {} func (x *HardwareAddrSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HardwareAddrSpec.ProtoReflect.Descriptor instead. func (*HardwareAddrSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{18} } func (x *HardwareAddrSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *HardwareAddrSpec) GetHardwareAddr() []byte { if x != nil { return x.HardwareAddr } return nil } // HostDNSConfigSpec describes host DNS config. type HostDNSConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` ListenAddresses []*common.NetIPPort `protobuf:"bytes,2,rep,name=listen_addresses,json=listenAddresses,proto3" json:"listen_addresses,omitempty"` ServiceHostDnsAddress *common.NetIP `protobuf:"bytes,3,opt,name=service_host_dns_address,json=serviceHostDnsAddress,proto3" json:"service_host_dns_address,omitempty"` ResolveMemberNames bool `protobuf:"varint,4,opt,name=resolve_member_names,json=resolveMemberNames,proto3" json:"resolve_member_names,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HostDNSConfigSpec) Reset() { *x = HostDNSConfigSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HostDNSConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*HostDNSConfigSpec) ProtoMessage() {} func (x *HostDNSConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HostDNSConfigSpec.ProtoReflect.Descriptor instead. func (*HostDNSConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{19} } func (x *HostDNSConfigSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } func (x *HostDNSConfigSpec) GetListenAddresses() []*common.NetIPPort { if x != nil { return x.ListenAddresses } return nil } func (x *HostDNSConfigSpec) GetServiceHostDnsAddress() *common.NetIP { if x != nil { return x.ServiceHostDnsAddress } return nil } func (x *HostDNSConfigSpec) GetResolveMemberNames() bool { if x != nil { return x.ResolveMemberNames } return false } // HostnameSpecSpec describes node hostname. type HostnameSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` Domainname string `protobuf:"bytes,2,opt,name=domainname,proto3" json:"domainname,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,3,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HostnameSpecSpec) Reset() { *x = HostnameSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HostnameSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*HostnameSpecSpec) ProtoMessage() {} func (x *HostnameSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HostnameSpecSpec.ProtoReflect.Descriptor instead. func (*HostnameSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{20} } func (x *HostnameSpecSpec) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *HostnameSpecSpec) GetDomainname() string { if x != nil { return x.Domainname } return "" } func (x *HostnameSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } // HostnameStatusSpec describes node hostname. type HostnameStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` Domainname string `protobuf:"bytes,2,opt,name=domainname,proto3" json:"domainname,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *HostnameStatusSpec) Reset() { *x = HostnameStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *HostnameStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*HostnameStatusSpec) ProtoMessage() {} func (x *HostnameStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use HostnameStatusSpec.ProtoReflect.Descriptor instead. func (*HostnameStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{21} } func (x *HostnameStatusSpec) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *HostnameStatusSpec) GetDomainname() string { if x != nil { return x.Domainname } return "" } // LinkAliasSpecSpec describes status of rendered secrets. type LinkAliasSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Alias string `protobuf:"bytes,1,opt,name=alias,proto3" json:"alias,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LinkAliasSpecSpec) Reset() { *x = LinkAliasSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LinkAliasSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinkAliasSpecSpec) ProtoMessage() {} func (x *LinkAliasSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinkAliasSpecSpec.ProtoReflect.Descriptor instead. func (*LinkAliasSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{22} } func (x *LinkAliasSpecSpec) GetAlias() string { if x != nil { return x.Alias } return "" } // LinkRefreshSpec describes status of rendered secrets. type LinkRefreshSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Generation int64 `protobuf:"varint,1,opt,name=generation,proto3" json:"generation,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LinkRefreshSpec) Reset() { *x = LinkRefreshSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LinkRefreshSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinkRefreshSpec) ProtoMessage() {} func (x *LinkRefreshSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinkRefreshSpec.ProtoReflect.Descriptor instead. func (*LinkRefreshSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{23} } func (x *LinkRefreshSpec) GetGeneration() int64 { if x != nil { return x.Generation } return 0 } // LinkSpecSpec describes spec for the link. type LinkSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Logical bool `protobuf:"varint,2,opt,name=logical,proto3" json:"logical,omitempty"` Up bool `protobuf:"varint,3,opt,name=up,proto3" json:"up,omitempty"` Mtu uint32 `protobuf:"varint,4,opt,name=mtu,proto3" json:"mtu,omitempty"` Kind string `protobuf:"bytes,5,opt,name=kind,proto3" json:"kind,omitempty"` Type enums.NethelpersLinkType `protobuf:"varint,6,opt,name=type,proto3,enum=talos.resource.definitions.enums.NethelpersLinkType" json:"type,omitempty"` ParentName string `protobuf:"bytes,7,opt,name=parent_name,json=parentName,proto3" json:"parent_name,omitempty"` BondSlave *BondSlave `protobuf:"bytes,8,opt,name=bond_slave,json=bondSlave,proto3" json:"bond_slave,omitempty"` BridgeSlave *BridgeSlave `protobuf:"bytes,9,opt,name=bridge_slave,json=bridgeSlave,proto3" json:"bridge_slave,omitempty"` Vlan *VLANSpec `protobuf:"bytes,10,opt,name=vlan,proto3" json:"vlan,omitempty"` BondMaster *BondMasterSpec `protobuf:"bytes,11,opt,name=bond_master,json=bondMaster,proto3" json:"bond_master,omitempty"` BridgeMaster *BridgeMasterSpec `protobuf:"bytes,12,opt,name=bridge_master,json=bridgeMaster,proto3" json:"bridge_master,omitempty"` Wireguard *WireguardSpec `protobuf:"bytes,13,opt,name=wireguard,proto3" json:"wireguard,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,14,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` HardwareAddress []byte `protobuf:"bytes,15,opt,name=hardware_address,json=hardwareAddress,proto3" json:"hardware_address,omitempty"` Multicast bool `protobuf:"varint,16,opt,name=multicast,proto3" json:"multicast,omitempty"` VrfMaster *VRFMasterSpec `protobuf:"bytes,17,opt,name=vrf_master,json=vrfMaster,proto3" json:"vrf_master,omitempty"` VrfSlave *VRFSlave `protobuf:"bytes,18,opt,name=vrf_slave,json=vrfSlave,proto3" json:"vrf_slave,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LinkSpecSpec) Reset() { *x = LinkSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LinkSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinkSpecSpec) ProtoMessage() {} func (x *LinkSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinkSpecSpec.ProtoReflect.Descriptor instead. func (*LinkSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{24} } func (x *LinkSpecSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *LinkSpecSpec) GetLogical() bool { if x != nil { return x.Logical } return false } func (x *LinkSpecSpec) GetUp() bool { if x != nil { return x.Up } return false } func (x *LinkSpecSpec) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *LinkSpecSpec) GetKind() string { if x != nil { return x.Kind } return "" } func (x *LinkSpecSpec) GetType() enums.NethelpersLinkType { if x != nil { return x.Type } return enums.NethelpersLinkType(0) } func (x *LinkSpecSpec) GetParentName() string { if x != nil { return x.ParentName } return "" } func (x *LinkSpecSpec) GetBondSlave() *BondSlave { if x != nil { return x.BondSlave } return nil } func (x *LinkSpecSpec) GetBridgeSlave() *BridgeSlave { if x != nil { return x.BridgeSlave } return nil } func (x *LinkSpecSpec) GetVlan() *VLANSpec { if x != nil { return x.Vlan } return nil } func (x *LinkSpecSpec) GetBondMaster() *BondMasterSpec { if x != nil { return x.BondMaster } return nil } func (x *LinkSpecSpec) GetBridgeMaster() *BridgeMasterSpec { if x != nil { return x.BridgeMaster } return nil } func (x *LinkSpecSpec) GetWireguard() *WireguardSpec { if x != nil { return x.Wireguard } return nil } func (x *LinkSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } func (x *LinkSpecSpec) GetHardwareAddress() []byte { if x != nil { return x.HardwareAddress } return nil } func (x *LinkSpecSpec) GetMulticast() bool { if x != nil { return x.Multicast } return false } func (x *LinkSpecSpec) GetVrfMaster() *VRFMasterSpec { if x != nil { return x.VrfMaster } return nil } func (x *LinkSpecSpec) GetVrfSlave() *VRFSlave { if x != nil { return x.VrfSlave } return nil } // LinkStatusSpec describes status of rendered secrets. type LinkStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Index uint32 `protobuf:"varint,1,opt,name=index,proto3" json:"index,omitempty"` Type enums.NethelpersLinkType `protobuf:"varint,2,opt,name=type,proto3,enum=talos.resource.definitions.enums.NethelpersLinkType" json:"type,omitempty"` LinkIndex uint32 `protobuf:"varint,3,opt,name=link_index,json=linkIndex,proto3" json:"link_index,omitempty"` Flags uint32 `protobuf:"varint,4,opt,name=flags,proto3" json:"flags,omitempty"` HardwareAddr []byte `protobuf:"bytes,5,opt,name=hardware_addr,json=hardwareAddr,proto3" json:"hardware_addr,omitempty"` BroadcastAddr []byte `protobuf:"bytes,6,opt,name=broadcast_addr,json=broadcastAddr,proto3" json:"broadcast_addr,omitempty"` Mtu uint32 `protobuf:"varint,7,opt,name=mtu,proto3" json:"mtu,omitempty"` QueueDisc string `protobuf:"bytes,8,opt,name=queue_disc,json=queueDisc,proto3" json:"queue_disc,omitempty"` MasterIndex uint32 `protobuf:"varint,9,opt,name=master_index,json=masterIndex,proto3" json:"master_index,omitempty"` OperationalState enums.NethelpersOperationalState `protobuf:"varint,10,opt,name=operational_state,json=operationalState,proto3,enum=talos.resource.definitions.enums.NethelpersOperationalState" json:"operational_state,omitempty"` Kind string `protobuf:"bytes,11,opt,name=kind,proto3" json:"kind,omitempty"` SlaveKind string `protobuf:"bytes,12,opt,name=slave_kind,json=slaveKind,proto3" json:"slave_kind,omitempty"` BusPath string `protobuf:"bytes,13,opt,name=bus_path,json=busPath,proto3" json:"bus_path,omitempty"` Pciid string `protobuf:"bytes,14,opt,name=pciid,proto3" json:"pciid,omitempty"` Driver string `protobuf:"bytes,15,opt,name=driver,proto3" json:"driver,omitempty"` DriverVersion string `protobuf:"bytes,16,opt,name=driver_version,json=driverVersion,proto3" json:"driver_version,omitempty"` FirmwareVersion string `protobuf:"bytes,17,opt,name=firmware_version,json=firmwareVersion,proto3" json:"firmware_version,omitempty"` ProductId string `protobuf:"bytes,18,opt,name=product_id,json=productId,proto3" json:"product_id,omitempty"` VendorId string `protobuf:"bytes,19,opt,name=vendor_id,json=vendorId,proto3" json:"vendor_id,omitempty"` Product string `protobuf:"bytes,20,opt,name=product,proto3" json:"product,omitempty"` Vendor string `protobuf:"bytes,21,opt,name=vendor,proto3" json:"vendor,omitempty"` LinkState bool `protobuf:"varint,22,opt,name=link_state,json=linkState,proto3" json:"link_state,omitempty"` SpeedMegabits int64 `protobuf:"varint,23,opt,name=speed_megabits,json=speedMegabits,proto3" json:"speed_megabits,omitempty"` Port enums.NethelpersPort `protobuf:"varint,24,opt,name=port,proto3,enum=talos.resource.definitions.enums.NethelpersPort" json:"port,omitempty"` Duplex enums.NethelpersDuplex `protobuf:"varint,25,opt,name=duplex,proto3,enum=talos.resource.definitions.enums.NethelpersDuplex" json:"duplex,omitempty"` Vlan *VLANSpec `protobuf:"bytes,26,opt,name=vlan,proto3" json:"vlan,omitempty"` BridgeMaster *BridgeMasterSpec `protobuf:"bytes,27,opt,name=bridge_master,json=bridgeMaster,proto3" json:"bridge_master,omitempty"` BondMaster *BondMasterSpec `protobuf:"bytes,28,opt,name=bond_master,json=bondMaster,proto3" json:"bond_master,omitempty"` Wireguard *WireguardSpec `protobuf:"bytes,29,opt,name=wireguard,proto3" json:"wireguard,omitempty"` PermanentAddr []byte `protobuf:"bytes,30,opt,name=permanent_addr,json=permanentAddr,proto3" json:"permanent_addr,omitempty"` Alias string `protobuf:"bytes,31,opt,name=alias,proto3" json:"alias,omitempty"` AltNames []string `protobuf:"bytes,32,rep,name=alt_names,json=altNames,proto3" json:"alt_names,omitempty"` VrfMaster *VRFMasterSpec `protobuf:"bytes,33,opt,name=vrf_master,json=vrfMaster,proto3" json:"vrf_master,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LinkStatusSpec) Reset() { *x = LinkStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LinkStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinkStatusSpec) ProtoMessage() {} func (x *LinkStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinkStatusSpec.ProtoReflect.Descriptor instead. func (*LinkStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{25} } func (x *LinkStatusSpec) GetIndex() uint32 { if x != nil { return x.Index } return 0 } func (x *LinkStatusSpec) GetType() enums.NethelpersLinkType { if x != nil { return x.Type } return enums.NethelpersLinkType(0) } func (x *LinkStatusSpec) GetLinkIndex() uint32 { if x != nil { return x.LinkIndex } return 0 } func (x *LinkStatusSpec) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *LinkStatusSpec) GetHardwareAddr() []byte { if x != nil { return x.HardwareAddr } return nil } func (x *LinkStatusSpec) GetBroadcastAddr() []byte { if x != nil { return x.BroadcastAddr } return nil } func (x *LinkStatusSpec) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } func (x *LinkStatusSpec) GetQueueDisc() string { if x != nil { return x.QueueDisc } return "" } func (x *LinkStatusSpec) GetMasterIndex() uint32 { if x != nil { return x.MasterIndex } return 0 } func (x *LinkStatusSpec) GetOperationalState() enums.NethelpersOperationalState { if x != nil { return x.OperationalState } return enums.NethelpersOperationalState(0) } func (x *LinkStatusSpec) GetKind() string { if x != nil { return x.Kind } return "" } func (x *LinkStatusSpec) GetSlaveKind() string { if x != nil { return x.SlaveKind } return "" } func (x *LinkStatusSpec) GetBusPath() string { if x != nil { return x.BusPath } return "" } func (x *LinkStatusSpec) GetPciid() string { if x != nil { return x.Pciid } return "" } func (x *LinkStatusSpec) GetDriver() string { if x != nil { return x.Driver } return "" } func (x *LinkStatusSpec) GetDriverVersion() string { if x != nil { return x.DriverVersion } return "" } func (x *LinkStatusSpec) GetFirmwareVersion() string { if x != nil { return x.FirmwareVersion } return "" } func (x *LinkStatusSpec) GetProductId() string { if x != nil { return x.ProductId } return "" } func (x *LinkStatusSpec) GetVendorId() string { if x != nil { return x.VendorId } return "" } func (x *LinkStatusSpec) GetProduct() string { if x != nil { return x.Product } return "" } func (x *LinkStatusSpec) GetVendor() string { if x != nil { return x.Vendor } return "" } func (x *LinkStatusSpec) GetLinkState() bool { if x != nil { return x.LinkState } return false } func (x *LinkStatusSpec) GetSpeedMegabits() int64 { if x != nil { return x.SpeedMegabits } return 0 } func (x *LinkStatusSpec) GetPort() enums.NethelpersPort { if x != nil { return x.Port } return enums.NethelpersPort(0) } func (x *LinkStatusSpec) GetDuplex() enums.NethelpersDuplex { if x != nil { return x.Duplex } return enums.NethelpersDuplex(0) } func (x *LinkStatusSpec) GetVlan() *VLANSpec { if x != nil { return x.Vlan } return nil } func (x *LinkStatusSpec) GetBridgeMaster() *BridgeMasterSpec { if x != nil { return x.BridgeMaster } return nil } func (x *LinkStatusSpec) GetBondMaster() *BondMasterSpec { if x != nil { return x.BondMaster } return nil } func (x *LinkStatusSpec) GetWireguard() *WireguardSpec { if x != nil { return x.Wireguard } return nil } func (x *LinkStatusSpec) GetPermanentAddr() []byte { if x != nil { return x.PermanentAddr } return nil } func (x *LinkStatusSpec) GetAlias() string { if x != nil { return x.Alias } return "" } func (x *LinkStatusSpec) GetAltNames() []string { if x != nil { return x.AltNames } return nil } func (x *LinkStatusSpec) GetVrfMaster() *VRFMasterSpec { if x != nil { return x.VrfMaster } return nil } // NfTablesAddressMatch describes the match on the IP address. type NfTablesAddressMatch struct { state protoimpl.MessageState `protogen:"open.v1"` IncludeSubnets []*common.NetIPPrefix `protobuf:"bytes,1,rep,name=include_subnets,json=includeSubnets,proto3" json:"include_subnets,omitempty"` ExcludeSubnets []*common.NetIPPrefix `protobuf:"bytes,2,rep,name=exclude_subnets,json=excludeSubnets,proto3" json:"exclude_subnets,omitempty"` Invert bool `protobuf:"varint,3,opt,name=invert,proto3" json:"invert,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesAddressMatch) Reset() { *x = NfTablesAddressMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesAddressMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesAddressMatch) ProtoMessage() {} func (x *NfTablesAddressMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesAddressMatch.ProtoReflect.Descriptor instead. func (*NfTablesAddressMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{26} } func (x *NfTablesAddressMatch) GetIncludeSubnets() []*common.NetIPPrefix { if x != nil { return x.IncludeSubnets } return nil } func (x *NfTablesAddressMatch) GetExcludeSubnets() []*common.NetIPPrefix { if x != nil { return x.ExcludeSubnets } return nil } func (x *NfTablesAddressMatch) GetInvert() bool { if x != nil { return x.Invert } return false } // NfTablesChainSpec describes status of rendered secrets. type NfTablesChainSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` Hook enums.NethelpersNfTablesChainHook `protobuf:"varint,2,opt,name=hook,proto3,enum=talos.resource.definitions.enums.NethelpersNfTablesChainHook" json:"hook,omitempty"` Priority enums.NethelpersNfTablesChainPriority `protobuf:"varint,3,opt,name=priority,proto3,enum=talos.resource.definitions.enums.NethelpersNfTablesChainPriority" json:"priority,omitempty"` Rules []*NfTablesRule `protobuf:"bytes,4,rep,name=rules,proto3" json:"rules,omitempty"` Policy enums.NethelpersNfTablesVerdict `protobuf:"varint,5,opt,name=policy,proto3,enum=talos.resource.definitions.enums.NethelpersNfTablesVerdict" json:"policy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesChainSpec) Reset() { *x = NfTablesChainSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesChainSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesChainSpec) ProtoMessage() {} func (x *NfTablesChainSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesChainSpec.ProtoReflect.Descriptor instead. func (*NfTablesChainSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{27} } func (x *NfTablesChainSpec) GetType() string { if x != nil { return x.Type } return "" } func (x *NfTablesChainSpec) GetHook() enums.NethelpersNfTablesChainHook { if x != nil { return x.Hook } return enums.NethelpersNfTablesChainHook(0) } func (x *NfTablesChainSpec) GetPriority() enums.NethelpersNfTablesChainPriority { if x != nil { return x.Priority } return enums.NethelpersNfTablesChainPriority(0) } func (x *NfTablesChainSpec) GetRules() []*NfTablesRule { if x != nil { return x.Rules } return nil } func (x *NfTablesChainSpec) GetPolicy() enums.NethelpersNfTablesVerdict { if x != nil { return x.Policy } return enums.NethelpersNfTablesVerdict(0) } // NfTablesClampMSS describes the TCP MSS clamping operation. // // MSS is limited by the `MaxMTU` so that: // - IPv4: MSS = MaxMTU - 40 // - IPv6: MSS = MaxMTU - 60. type NfTablesClampMSS struct { state protoimpl.MessageState `protogen:"open.v1"` Mtu uint32 `protobuf:"varint,1,opt,name=mtu,proto3" json:"mtu,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesClampMSS) Reset() { *x = NfTablesClampMSS{} mi := &file_resource_definitions_network_network_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesClampMSS) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesClampMSS) ProtoMessage() {} func (x *NfTablesClampMSS) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesClampMSS.ProtoReflect.Descriptor instead. func (*NfTablesClampMSS) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{28} } func (x *NfTablesClampMSS) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } // NfTablesConntrackStateMatch describes the match on the connection tracking state. type NfTablesConntrackStateMatch struct { state protoimpl.MessageState `protogen:"open.v1"` States []enums.NethelpersConntrackState `protobuf:"varint,1,rep,packed,name=states,proto3,enum=talos.resource.definitions.enums.NethelpersConntrackState" json:"states,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesConntrackStateMatch) Reset() { *x = NfTablesConntrackStateMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesConntrackStateMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesConntrackStateMatch) ProtoMessage() {} func (x *NfTablesConntrackStateMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesConntrackStateMatch.ProtoReflect.Descriptor instead. func (*NfTablesConntrackStateMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{29} } func (x *NfTablesConntrackStateMatch) GetStates() []enums.NethelpersConntrackState { if x != nil { return x.States } return nil } // NfTablesICMPTypeMatch describes the match on the ICMP type. type NfTablesICMPTypeMatch struct { state protoimpl.MessageState `protogen:"open.v1"` Types []enums.NethelpersICMPType `protobuf:"varint,1,rep,packed,name=types,proto3,enum=talos.resource.definitions.enums.NethelpersICMPType" json:"types,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesICMPTypeMatch) Reset() { *x = NfTablesICMPTypeMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesICMPTypeMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesICMPTypeMatch) ProtoMessage() {} func (x *NfTablesICMPTypeMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesICMPTypeMatch.ProtoReflect.Descriptor instead. func (*NfTablesICMPTypeMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{30} } func (x *NfTablesICMPTypeMatch) GetTypes() []enums.NethelpersICMPType { if x != nil { return x.Types } return nil } // NfTablesIfNameMatch describes the match on the interface name. type NfTablesIfNameMatch struct { state protoimpl.MessageState `protogen:"open.v1"` Operator enums.NethelpersMatchOperator `protobuf:"varint,2,opt,name=operator,proto3,enum=talos.resource.definitions.enums.NethelpersMatchOperator" json:"operator,omitempty"` InterfaceNames []string `protobuf:"bytes,3,rep,name=interface_names,json=interfaceNames,proto3" json:"interface_names,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesIfNameMatch) Reset() { *x = NfTablesIfNameMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesIfNameMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesIfNameMatch) ProtoMessage() {} func (x *NfTablesIfNameMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesIfNameMatch.ProtoReflect.Descriptor instead. func (*NfTablesIfNameMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{31} } func (x *NfTablesIfNameMatch) GetOperator() enums.NethelpersMatchOperator { if x != nil { return x.Operator } return enums.NethelpersMatchOperator(0) } func (x *NfTablesIfNameMatch) GetInterfaceNames() []string { if x != nil { return x.InterfaceNames } return nil } // NfTablesLayer4Match describes the match on the transport layer protocol. type NfTablesLayer4Match struct { state protoimpl.MessageState `protogen:"open.v1"` Protocol enums.NethelpersProtocol `protobuf:"varint,1,opt,name=protocol,proto3,enum=talos.resource.definitions.enums.NethelpersProtocol" json:"protocol,omitempty"` MatchSourcePort *NfTablesPortMatch `protobuf:"bytes,2,opt,name=match_source_port,json=matchSourcePort,proto3" json:"match_source_port,omitempty"` MatchDestinationPort *NfTablesPortMatch `protobuf:"bytes,3,opt,name=match_destination_port,json=matchDestinationPort,proto3" json:"match_destination_port,omitempty"` MatchIcmpType *NfTablesICMPTypeMatch `protobuf:"bytes,4,opt,name=match_icmp_type,json=matchIcmpType,proto3" json:"match_icmp_type,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesLayer4Match) Reset() { *x = NfTablesLayer4Match{} mi := &file_resource_definitions_network_network_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesLayer4Match) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesLayer4Match) ProtoMessage() {} func (x *NfTablesLayer4Match) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesLayer4Match.ProtoReflect.Descriptor instead. func (*NfTablesLayer4Match) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{32} } func (x *NfTablesLayer4Match) GetProtocol() enums.NethelpersProtocol { if x != nil { return x.Protocol } return enums.NethelpersProtocol(0) } func (x *NfTablesLayer4Match) GetMatchSourcePort() *NfTablesPortMatch { if x != nil { return x.MatchSourcePort } return nil } func (x *NfTablesLayer4Match) GetMatchDestinationPort() *NfTablesPortMatch { if x != nil { return x.MatchDestinationPort } return nil } func (x *NfTablesLayer4Match) GetMatchIcmpType() *NfTablesICMPTypeMatch { if x != nil { return x.MatchIcmpType } return nil } // NfTablesLimitMatch describes the match on the packet rate. type NfTablesLimitMatch struct { state protoimpl.MessageState `protogen:"open.v1"` PacketRatePerSecond uint64 `protobuf:"varint,1,opt,name=packet_rate_per_second,json=packetRatePerSecond,proto3" json:"packet_rate_per_second,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesLimitMatch) Reset() { *x = NfTablesLimitMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesLimitMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesLimitMatch) ProtoMessage() {} func (x *NfTablesLimitMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesLimitMatch.ProtoReflect.Descriptor instead. func (*NfTablesLimitMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{33} } func (x *NfTablesLimitMatch) GetPacketRatePerSecond() uint64 { if x != nil { return x.PacketRatePerSecond } return 0 } // NfTablesMark encodes packet mark match/update operation. // // When used as a match computes the following condition: // (mark & mask) ^ xor == value // // When used as an update computes the following operation: // mark = (mark & mask) ^ xor. type NfTablesMark struct { state protoimpl.MessageState `protogen:"open.v1"` Mask uint32 `protobuf:"varint,1,opt,name=mask,proto3" json:"mask,omitempty"` Xor uint32 `protobuf:"varint,2,opt,name=xor,proto3" json:"xor,omitempty"` Value uint32 `protobuf:"varint,3,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesMark) Reset() { *x = NfTablesMark{} mi := &file_resource_definitions_network_network_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesMark) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesMark) ProtoMessage() {} func (x *NfTablesMark) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesMark.ProtoReflect.Descriptor instead. func (*NfTablesMark) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{34} } func (x *NfTablesMark) GetMask() uint32 { if x != nil { return x.Mask } return 0 } func (x *NfTablesMark) GetXor() uint32 { if x != nil { return x.Xor } return 0 } func (x *NfTablesMark) GetValue() uint32 { if x != nil { return x.Value } return 0 } // NfTablesPortMatch describes the match on the transport layer port. type NfTablesPortMatch struct { state protoimpl.MessageState `protogen:"open.v1"` Ranges []*PortRange `protobuf:"bytes,1,rep,name=ranges,proto3" json:"ranges,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesPortMatch) Reset() { *x = NfTablesPortMatch{} mi := &file_resource_definitions_network_network_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesPortMatch) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesPortMatch) ProtoMessage() {} func (x *NfTablesPortMatch) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesPortMatch.ProtoReflect.Descriptor instead. func (*NfTablesPortMatch) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{35} } func (x *NfTablesPortMatch) GetRanges() []*PortRange { if x != nil { return x.Ranges } return nil } // NfTablesRule describes a single rule in the nftables chain. type NfTablesRule struct { state protoimpl.MessageState `protogen:"open.v1"` MatchOIfName *NfTablesIfNameMatch `protobuf:"bytes,1,opt,name=match_o_if_name,json=matchOIfName,proto3" json:"match_o_if_name,omitempty"` Verdict enums.NethelpersNfTablesVerdict `protobuf:"varint,2,opt,name=verdict,proto3,enum=talos.resource.definitions.enums.NethelpersNfTablesVerdict" json:"verdict,omitempty"` MatchMark *NfTablesMark `protobuf:"bytes,3,opt,name=match_mark,json=matchMark,proto3" json:"match_mark,omitempty"` SetMark *NfTablesMark `protobuf:"bytes,4,opt,name=set_mark,json=setMark,proto3" json:"set_mark,omitempty"` MatchSourceAddress *NfTablesAddressMatch `protobuf:"bytes,5,opt,name=match_source_address,json=matchSourceAddress,proto3" json:"match_source_address,omitempty"` MatchDestinationAddress *NfTablesAddressMatch `protobuf:"bytes,6,opt,name=match_destination_address,json=matchDestinationAddress,proto3" json:"match_destination_address,omitempty"` MatchLayer4 *NfTablesLayer4Match `protobuf:"bytes,7,opt,name=match_layer4,json=matchLayer4,proto3" json:"match_layer4,omitempty"` MatchIIfName *NfTablesIfNameMatch `protobuf:"bytes,8,opt,name=match_i_if_name,json=matchIIfName,proto3" json:"match_i_if_name,omitempty"` ClampMss *NfTablesClampMSS `protobuf:"bytes,9,opt,name=clamp_mss,json=clampMss,proto3" json:"clamp_mss,omitempty"` MatchLimit *NfTablesLimitMatch `protobuf:"bytes,10,opt,name=match_limit,json=matchLimit,proto3" json:"match_limit,omitempty"` MatchConntrackState *NfTablesConntrackStateMatch `protobuf:"bytes,11,opt,name=match_conntrack_state,json=matchConntrackState,proto3" json:"match_conntrack_state,omitempty"` AnonCounter bool `protobuf:"varint,12,opt,name=anon_counter,json=anonCounter,proto3" json:"anon_counter,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NfTablesRule) Reset() { *x = NfTablesRule{} mi := &file_resource_definitions_network_network_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NfTablesRule) String() string { return protoimpl.X.MessageStringOf(x) } func (*NfTablesRule) ProtoMessage() {} func (x *NfTablesRule) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NfTablesRule.ProtoReflect.Descriptor instead. func (*NfTablesRule) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{36} } func (x *NfTablesRule) GetMatchOIfName() *NfTablesIfNameMatch { if x != nil { return x.MatchOIfName } return nil } func (x *NfTablesRule) GetVerdict() enums.NethelpersNfTablesVerdict { if x != nil { return x.Verdict } return enums.NethelpersNfTablesVerdict(0) } func (x *NfTablesRule) GetMatchMark() *NfTablesMark { if x != nil { return x.MatchMark } return nil } func (x *NfTablesRule) GetSetMark() *NfTablesMark { if x != nil { return x.SetMark } return nil } func (x *NfTablesRule) GetMatchSourceAddress() *NfTablesAddressMatch { if x != nil { return x.MatchSourceAddress } return nil } func (x *NfTablesRule) GetMatchDestinationAddress() *NfTablesAddressMatch { if x != nil { return x.MatchDestinationAddress } return nil } func (x *NfTablesRule) GetMatchLayer4() *NfTablesLayer4Match { if x != nil { return x.MatchLayer4 } return nil } func (x *NfTablesRule) GetMatchIIfName() *NfTablesIfNameMatch { if x != nil { return x.MatchIIfName } return nil } func (x *NfTablesRule) GetClampMss() *NfTablesClampMSS { if x != nil { return x.ClampMss } return nil } func (x *NfTablesRule) GetMatchLimit() *NfTablesLimitMatch { if x != nil { return x.MatchLimit } return nil } func (x *NfTablesRule) GetMatchConntrackState() *NfTablesConntrackStateMatch { if x != nil { return x.MatchConntrackState } return nil } func (x *NfTablesRule) GetAnonCounter() bool { if x != nil { return x.AnonCounter } return false } // NodeAddressFilterSpec describes a filter for NodeAddresses. type NodeAddressFilterSpec struct { state protoimpl.MessageState `protogen:"open.v1"` IncludeSubnets []*common.NetIPPrefix `protobuf:"bytes,1,rep,name=include_subnets,json=includeSubnets,proto3" json:"include_subnets,omitempty"` ExcludeSubnets []*common.NetIPPrefix `protobuf:"bytes,2,rep,name=exclude_subnets,json=excludeSubnets,proto3" json:"exclude_subnets,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeAddressFilterSpec) Reset() { *x = NodeAddressFilterSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeAddressFilterSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeAddressFilterSpec) ProtoMessage() {} func (x *NodeAddressFilterSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeAddressFilterSpec.ProtoReflect.Descriptor instead. func (*NodeAddressFilterSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{37} } func (x *NodeAddressFilterSpec) GetIncludeSubnets() []*common.NetIPPrefix { if x != nil { return x.IncludeSubnets } return nil } func (x *NodeAddressFilterSpec) GetExcludeSubnets() []*common.NetIPPrefix { if x != nil { return x.ExcludeSubnets } return nil } // NodeAddressSortAlgorithmSpec describes a filter for NodeAddresses. type NodeAddressSortAlgorithmSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Algorithm enums.NethelpersAddressSortAlgorithm `protobuf:"varint,1,opt,name=algorithm,proto3,enum=talos.resource.definitions.enums.NethelpersAddressSortAlgorithm" json:"algorithm,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeAddressSortAlgorithmSpec) Reset() { *x = NodeAddressSortAlgorithmSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeAddressSortAlgorithmSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeAddressSortAlgorithmSpec) ProtoMessage() {} func (x *NodeAddressSortAlgorithmSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeAddressSortAlgorithmSpec.ProtoReflect.Descriptor instead. func (*NodeAddressSortAlgorithmSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{38} } func (x *NodeAddressSortAlgorithmSpec) GetAlgorithm() enums.NethelpersAddressSortAlgorithm { if x != nil { return x.Algorithm } return enums.NethelpersAddressSortAlgorithm(0) } // NodeAddressSpec describes a set of node addresses. type NodeAddressSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Addresses []*common.NetIPPrefix `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` SortAlgorithm enums.NethelpersAddressSortAlgorithm `protobuf:"varint,2,opt,name=sort_algorithm,json=sortAlgorithm,proto3,enum=talos.resource.definitions.enums.NethelpersAddressSortAlgorithm" json:"sort_algorithm,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *NodeAddressSpec) Reset() { *x = NodeAddressSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *NodeAddressSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*NodeAddressSpec) ProtoMessage() {} func (x *NodeAddressSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use NodeAddressSpec.ProtoReflect.Descriptor instead. func (*NodeAddressSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{39} } func (x *NodeAddressSpec) GetAddresses() []*common.NetIPPrefix { if x != nil { return x.Addresses } return nil } func (x *NodeAddressSpec) GetSortAlgorithm() enums.NethelpersAddressSortAlgorithm { if x != nil { return x.SortAlgorithm } return enums.NethelpersAddressSortAlgorithm(0) } // OperatorSpecSpec describes operator specification. type OperatorSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Operator enums.NetworkOperator `protobuf:"varint,1,opt,name=operator,proto3,enum=talos.resource.definitions.enums.NetworkOperator" json:"operator,omitempty"` LinkName string `protobuf:"bytes,2,opt,name=link_name,json=linkName,proto3" json:"link_name,omitempty"` RequireUp bool `protobuf:"varint,3,opt,name=require_up,json=requireUp,proto3" json:"require_up,omitempty"` Dhcp4 *DHCP4OperatorSpec `protobuf:"bytes,4,opt,name=dhcp4,proto3" json:"dhcp4,omitempty"` Dhcp6 *DHCP6OperatorSpec `protobuf:"bytes,5,opt,name=dhcp6,proto3" json:"dhcp6,omitempty"` Vip *VIPOperatorSpec `protobuf:"bytes,6,opt,name=vip,proto3" json:"vip,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,7,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OperatorSpecSpec) Reset() { *x = OperatorSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OperatorSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*OperatorSpecSpec) ProtoMessage() {} func (x *OperatorSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OperatorSpecSpec.ProtoReflect.Descriptor instead. func (*OperatorSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{40} } func (x *OperatorSpecSpec) GetOperator() enums.NetworkOperator { if x != nil { return x.Operator } return enums.NetworkOperator(0) } func (x *OperatorSpecSpec) GetLinkName() string { if x != nil { return x.LinkName } return "" } func (x *OperatorSpecSpec) GetRequireUp() bool { if x != nil { return x.RequireUp } return false } func (x *OperatorSpecSpec) GetDhcp4() *DHCP4OperatorSpec { if x != nil { return x.Dhcp4 } return nil } func (x *OperatorSpecSpec) GetDhcp6() *DHCP6OperatorSpec { if x != nil { return x.Dhcp6 } return nil } func (x *OperatorSpecSpec) GetVip() *VIPOperatorSpec { if x != nil { return x.Vip } return nil } func (x *OperatorSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } // PlatformConfigSpec describes platform network configuration. // // This structure is marshaled to STATE partition to persist cached network configuration across // reboots. type PlatformConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Addresses []*AddressSpecSpec `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` Links []*LinkSpecSpec `protobuf:"bytes,2,rep,name=links,proto3" json:"links,omitempty"` Routes []*RouteSpecSpec `protobuf:"bytes,3,rep,name=routes,proto3" json:"routes,omitempty"` Hostnames []*HostnameSpecSpec `protobuf:"bytes,4,rep,name=hostnames,proto3" json:"hostnames,omitempty"` Resolvers []*ResolverSpecSpec `protobuf:"bytes,5,rep,name=resolvers,proto3" json:"resolvers,omitempty"` TimeServers []*TimeServerSpecSpec `protobuf:"bytes,6,rep,name=time_servers,json=timeServers,proto3" json:"time_servers,omitempty"` Operators []*OperatorSpecSpec `protobuf:"bytes,7,rep,name=operators,proto3" json:"operators,omitempty"` ExternalIps []*common.NetIP `protobuf:"bytes,8,rep,name=external_ips,json=externalIps,proto3" json:"external_ips,omitempty"` Probes []*ProbeSpecSpec `protobuf:"bytes,9,rep,name=probes,proto3" json:"probes,omitempty"` Metadata *runtime.PlatformMetadataSpec `protobuf:"bytes,10,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PlatformConfigSpec) Reset() { *x = PlatformConfigSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PlatformConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PlatformConfigSpec) ProtoMessage() {} func (x *PlatformConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PlatformConfigSpec.ProtoReflect.Descriptor instead. func (*PlatformConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{41} } func (x *PlatformConfigSpec) GetAddresses() []*AddressSpecSpec { if x != nil { return x.Addresses } return nil } func (x *PlatformConfigSpec) GetLinks() []*LinkSpecSpec { if x != nil { return x.Links } return nil } func (x *PlatformConfigSpec) GetRoutes() []*RouteSpecSpec { if x != nil { return x.Routes } return nil } func (x *PlatformConfigSpec) GetHostnames() []*HostnameSpecSpec { if x != nil { return x.Hostnames } return nil } func (x *PlatformConfigSpec) GetResolvers() []*ResolverSpecSpec { if x != nil { return x.Resolvers } return nil } func (x *PlatformConfigSpec) GetTimeServers() []*TimeServerSpecSpec { if x != nil { return x.TimeServers } return nil } func (x *PlatformConfigSpec) GetOperators() []*OperatorSpecSpec { if x != nil { return x.Operators } return nil } func (x *PlatformConfigSpec) GetExternalIps() []*common.NetIP { if x != nil { return x.ExternalIps } return nil } func (x *PlatformConfigSpec) GetProbes() []*ProbeSpecSpec { if x != nil { return x.Probes } return nil } func (x *PlatformConfigSpec) GetMetadata() *runtime.PlatformMetadataSpec { if x != nil { return x.Metadata } return nil } // PortRange describes a range of ports. // // Range is [lo, hi]. type PortRange struct { state protoimpl.MessageState `protogen:"open.v1"` Lo uint32 `protobuf:"varint,1,opt,name=lo,proto3" json:"lo,omitempty"` Hi uint32 `protobuf:"varint,2,opt,name=hi,proto3" json:"hi,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PortRange) Reset() { *x = PortRange{} mi := &file_resource_definitions_network_network_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PortRange) String() string { return protoimpl.X.MessageStringOf(x) } func (*PortRange) ProtoMessage() {} func (x *PortRange) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PortRange.ProtoReflect.Descriptor instead. func (*PortRange) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{42} } func (x *PortRange) GetLo() uint32 { if x != nil { return x.Lo } return 0 } func (x *PortRange) GetHi() uint32 { if x != nil { return x.Hi } return 0 } // ProbeSpecSpec describes the Probe. type ProbeSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Interval *durationpb.Duration `protobuf:"bytes,1,opt,name=interval,proto3" json:"interval,omitempty"` FailureThreshold int64 `protobuf:"varint,2,opt,name=failure_threshold,json=failureThreshold,proto3" json:"failure_threshold,omitempty"` Tcp *TCPProbeSpec `protobuf:"bytes,3,opt,name=tcp,proto3" json:"tcp,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,4,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProbeSpecSpec) Reset() { *x = ProbeSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProbeSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProbeSpecSpec) ProtoMessage() {} func (x *ProbeSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProbeSpecSpec.ProtoReflect.Descriptor instead. func (*ProbeSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{43} } func (x *ProbeSpecSpec) GetInterval() *durationpb.Duration { if x != nil { return x.Interval } return nil } func (x *ProbeSpecSpec) GetFailureThreshold() int64 { if x != nil { return x.FailureThreshold } return 0 } func (x *ProbeSpecSpec) GetTcp() *TCPProbeSpec { if x != nil { return x.Tcp } return nil } func (x *ProbeSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } // ProbeStatusSpec describes the Probe. type ProbeStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` LastError string `protobuf:"bytes,2,opt,name=last_error,json=lastError,proto3" json:"last_error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ProbeStatusSpec) Reset() { *x = ProbeStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ProbeStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ProbeStatusSpec) ProtoMessage() {} func (x *ProbeStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ProbeStatusSpec.ProtoReflect.Descriptor instead. func (*ProbeStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{44} } func (x *ProbeStatusSpec) GetSuccess() bool { if x != nil { return x.Success } return false } func (x *ProbeStatusSpec) GetLastError() string { if x != nil { return x.LastError } return "" } // ResolverSpecSpec describes DNS resolvers. type ResolverSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DnsServers []*common.NetIP `protobuf:"bytes,1,rep,name=dns_servers,json=dnsServers,proto3" json:"dns_servers,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,2,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` SearchDomains []string `protobuf:"bytes,3,rep,name=search_domains,json=searchDomains,proto3" json:"search_domains,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResolverSpecSpec) Reset() { *x = ResolverSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResolverSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResolverSpecSpec) ProtoMessage() {} func (x *ResolverSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResolverSpecSpec.ProtoReflect.Descriptor instead. func (*ResolverSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{45} } func (x *ResolverSpecSpec) GetDnsServers() []*common.NetIP { if x != nil { return x.DnsServers } return nil } func (x *ResolverSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } func (x *ResolverSpecSpec) GetSearchDomains() []string { if x != nil { return x.SearchDomains } return nil } // ResolverStatusSpec describes DNS resolvers. type ResolverStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DnsServers []*common.NetIP `protobuf:"bytes,1,rep,name=dns_servers,json=dnsServers,proto3" json:"dns_servers,omitempty"` SearchDomains []string `protobuf:"bytes,2,rep,name=search_domains,json=searchDomains,proto3" json:"search_domains,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ResolverStatusSpec) Reset() { *x = ResolverStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ResolverStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ResolverStatusSpec) ProtoMessage() {} func (x *ResolverStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ResolverStatusSpec.ProtoReflect.Descriptor instead. func (*ResolverStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{46} } func (x *ResolverStatusSpec) GetDnsServers() []*common.NetIP { if x != nil { return x.DnsServers } return nil } func (x *ResolverStatusSpec) GetSearchDomains() []string { if x != nil { return x.SearchDomains } return nil } // RouteSpecSpec describes the route. type RouteSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Family enums.NethelpersFamily `protobuf:"varint,1,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Destination *common.NetIPPrefix `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` Source *common.NetIP `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` Gateway *common.NetIP `protobuf:"bytes,4,opt,name=gateway,proto3" json:"gateway,omitempty"` OutLinkName string `protobuf:"bytes,5,opt,name=out_link_name,json=outLinkName,proto3" json:"out_link_name,omitempty"` Table enums.NethelpersRoutingTable `protobuf:"varint,6,opt,name=table,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingTable" json:"table,omitempty"` Priority uint32 `protobuf:"varint,7,opt,name=priority,proto3" json:"priority,omitempty"` Scope enums.NethelpersScope `protobuf:"varint,8,opt,name=scope,proto3,enum=talos.resource.definitions.enums.NethelpersScope" json:"scope,omitempty"` Type enums.NethelpersRouteType `protobuf:"varint,9,opt,name=type,proto3,enum=talos.resource.definitions.enums.NethelpersRouteType" json:"type,omitempty"` Flags uint32 `protobuf:"varint,10,opt,name=flags,proto3" json:"flags,omitempty"` Protocol enums.NethelpersRouteProtocol `protobuf:"varint,11,opt,name=protocol,proto3,enum=talos.resource.definitions.enums.NethelpersRouteProtocol" json:"protocol,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,12,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` Mtu uint32 `protobuf:"varint,13,opt,name=mtu,proto3" json:"mtu,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RouteSpecSpec) Reset() { *x = RouteSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RouteSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*RouteSpecSpec) ProtoMessage() {} func (x *RouteSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RouteSpecSpec.ProtoReflect.Descriptor instead. func (*RouteSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{47} } func (x *RouteSpecSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *RouteSpecSpec) GetDestination() *common.NetIPPrefix { if x != nil { return x.Destination } return nil } func (x *RouteSpecSpec) GetSource() *common.NetIP { if x != nil { return x.Source } return nil } func (x *RouteSpecSpec) GetGateway() *common.NetIP { if x != nil { return x.Gateway } return nil } func (x *RouteSpecSpec) GetOutLinkName() string { if x != nil { return x.OutLinkName } return "" } func (x *RouteSpecSpec) GetTable() enums.NethelpersRoutingTable { if x != nil { return x.Table } return enums.NethelpersRoutingTable(0) } func (x *RouteSpecSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } func (x *RouteSpecSpec) GetScope() enums.NethelpersScope { if x != nil { return x.Scope } return enums.NethelpersScope(0) } func (x *RouteSpecSpec) GetType() enums.NethelpersRouteType { if x != nil { return x.Type } return enums.NethelpersRouteType(0) } func (x *RouteSpecSpec) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *RouteSpecSpec) GetProtocol() enums.NethelpersRouteProtocol { if x != nil { return x.Protocol } return enums.NethelpersRouteProtocol(0) } func (x *RouteSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } func (x *RouteSpecSpec) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } // RouteStatusSpec describes status of rendered secrets. type RouteStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Family enums.NethelpersFamily `protobuf:"varint,1,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Destination *common.NetIPPrefix `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` Source *common.NetIP `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` Gateway *common.NetIP `protobuf:"bytes,4,opt,name=gateway,proto3" json:"gateway,omitempty"` OutLinkIndex uint32 `protobuf:"varint,5,opt,name=out_link_index,json=outLinkIndex,proto3" json:"out_link_index,omitempty"` OutLinkName string `protobuf:"bytes,6,opt,name=out_link_name,json=outLinkName,proto3" json:"out_link_name,omitempty"` Table enums.NethelpersRoutingTable `protobuf:"varint,7,opt,name=table,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingTable" json:"table,omitempty"` Priority uint32 `protobuf:"varint,8,opt,name=priority,proto3" json:"priority,omitempty"` Scope enums.NethelpersScope `protobuf:"varint,9,opt,name=scope,proto3,enum=talos.resource.definitions.enums.NethelpersScope" json:"scope,omitempty"` Type enums.NethelpersRouteType `protobuf:"varint,10,opt,name=type,proto3,enum=talos.resource.definitions.enums.NethelpersRouteType" json:"type,omitempty"` Flags uint32 `protobuf:"varint,11,opt,name=flags,proto3" json:"flags,omitempty"` Protocol enums.NethelpersRouteProtocol `protobuf:"varint,12,opt,name=protocol,proto3,enum=talos.resource.definitions.enums.NethelpersRouteProtocol" json:"protocol,omitempty"` Mtu uint32 `protobuf:"varint,13,opt,name=mtu,proto3" json:"mtu,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RouteStatusSpec) Reset() { *x = RouteStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RouteStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*RouteStatusSpec) ProtoMessage() {} func (x *RouteStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RouteStatusSpec.ProtoReflect.Descriptor instead. func (*RouteStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{48} } func (x *RouteStatusSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *RouteStatusSpec) GetDestination() *common.NetIPPrefix { if x != nil { return x.Destination } return nil } func (x *RouteStatusSpec) GetSource() *common.NetIP { if x != nil { return x.Source } return nil } func (x *RouteStatusSpec) GetGateway() *common.NetIP { if x != nil { return x.Gateway } return nil } func (x *RouteStatusSpec) GetOutLinkIndex() uint32 { if x != nil { return x.OutLinkIndex } return 0 } func (x *RouteStatusSpec) GetOutLinkName() string { if x != nil { return x.OutLinkName } return "" } func (x *RouteStatusSpec) GetTable() enums.NethelpersRoutingTable { if x != nil { return x.Table } return enums.NethelpersRoutingTable(0) } func (x *RouteStatusSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } func (x *RouteStatusSpec) GetScope() enums.NethelpersScope { if x != nil { return x.Scope } return enums.NethelpersScope(0) } func (x *RouteStatusSpec) GetType() enums.NethelpersRouteType { if x != nil { return x.Type } return enums.NethelpersRouteType(0) } func (x *RouteStatusSpec) GetFlags() uint32 { if x != nil { return x.Flags } return 0 } func (x *RouteStatusSpec) GetProtocol() enums.NethelpersRouteProtocol { if x != nil { return x.Protocol } return enums.NethelpersRouteProtocol(0) } func (x *RouteStatusSpec) GetMtu() uint32 { if x != nil { return x.Mtu } return 0 } // RoutingRuleSpecSpec describes the routing rule. type RoutingRuleSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Family enums.NethelpersFamily `protobuf:"varint,1,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Src *common.NetIPPrefix `protobuf:"bytes,2,opt,name=src,proto3" json:"src,omitempty"` Dst *common.NetIPPrefix `protobuf:"bytes,3,opt,name=dst,proto3" json:"dst,omitempty"` Table enums.NethelpersRoutingTable `protobuf:"varint,4,opt,name=table,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingTable" json:"table,omitempty"` Priority uint32 `protobuf:"varint,5,opt,name=priority,proto3" json:"priority,omitempty"` Action enums.NethelpersRoutingRuleAction `protobuf:"varint,6,opt,name=action,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingRuleAction" json:"action,omitempty"` IifName string `protobuf:"bytes,7,opt,name=iif_name,json=iifName,proto3" json:"iif_name,omitempty"` OifName string `protobuf:"bytes,8,opt,name=oif_name,json=oifName,proto3" json:"oif_name,omitempty"` FwMark uint32 `protobuf:"varint,9,opt,name=fw_mark,json=fwMark,proto3" json:"fw_mark,omitempty"` FwMask uint32 `protobuf:"varint,10,opt,name=fw_mask,json=fwMask,proto3" json:"fw_mask,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,11,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RoutingRuleSpecSpec) Reset() { *x = RoutingRuleSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RoutingRuleSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*RoutingRuleSpecSpec) ProtoMessage() {} func (x *RoutingRuleSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RoutingRuleSpecSpec.ProtoReflect.Descriptor instead. func (*RoutingRuleSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{49} } func (x *RoutingRuleSpecSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *RoutingRuleSpecSpec) GetSrc() *common.NetIPPrefix { if x != nil { return x.Src } return nil } func (x *RoutingRuleSpecSpec) GetDst() *common.NetIPPrefix { if x != nil { return x.Dst } return nil } func (x *RoutingRuleSpecSpec) GetTable() enums.NethelpersRoutingTable { if x != nil { return x.Table } return enums.NethelpersRoutingTable(0) } func (x *RoutingRuleSpecSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } func (x *RoutingRuleSpecSpec) GetAction() enums.NethelpersRoutingRuleAction { if x != nil { return x.Action } return enums.NethelpersRoutingRuleAction(0) } func (x *RoutingRuleSpecSpec) GetIifName() string { if x != nil { return x.IifName } return "" } func (x *RoutingRuleSpecSpec) GetOifName() string { if x != nil { return x.OifName } return "" } func (x *RoutingRuleSpecSpec) GetFwMark() uint32 { if x != nil { return x.FwMark } return 0 } func (x *RoutingRuleSpecSpec) GetFwMask() uint32 { if x != nil { return x.FwMask } return 0 } func (x *RoutingRuleSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } // RoutingRuleStatusSpec describes the observed routing rule state. type RoutingRuleStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Family enums.NethelpersFamily `protobuf:"varint,1,opt,name=family,proto3,enum=talos.resource.definitions.enums.NethelpersFamily" json:"family,omitempty"` Src *common.NetIPPrefix `protobuf:"bytes,2,opt,name=src,proto3" json:"src,omitempty"` Dst *common.NetIPPrefix `protobuf:"bytes,3,opt,name=dst,proto3" json:"dst,omitempty"` Table enums.NethelpersRoutingTable `protobuf:"varint,4,opt,name=table,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingTable" json:"table,omitempty"` Priority uint32 `protobuf:"varint,5,opt,name=priority,proto3" json:"priority,omitempty"` Action enums.NethelpersRoutingRuleAction `protobuf:"varint,6,opt,name=action,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingRuleAction" json:"action,omitempty"` IifName string `protobuf:"bytes,7,opt,name=iif_name,json=iifName,proto3" json:"iif_name,omitempty"` OifName string `protobuf:"bytes,8,opt,name=oif_name,json=oifName,proto3" json:"oif_name,omitempty"` FwMark uint32 `protobuf:"varint,9,opt,name=fw_mark,json=fwMark,proto3" json:"fw_mark,omitempty"` FwMask uint32 `protobuf:"varint,10,opt,name=fw_mask,json=fwMask,proto3" json:"fw_mask,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *RoutingRuleStatusSpec) Reset() { *x = RoutingRuleStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *RoutingRuleStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*RoutingRuleStatusSpec) ProtoMessage() {} func (x *RoutingRuleStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use RoutingRuleStatusSpec.ProtoReflect.Descriptor instead. func (*RoutingRuleStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{50} } func (x *RoutingRuleStatusSpec) GetFamily() enums.NethelpersFamily { if x != nil { return x.Family } return enums.NethelpersFamily(0) } func (x *RoutingRuleStatusSpec) GetSrc() *common.NetIPPrefix { if x != nil { return x.Src } return nil } func (x *RoutingRuleStatusSpec) GetDst() *common.NetIPPrefix { if x != nil { return x.Dst } return nil } func (x *RoutingRuleStatusSpec) GetTable() enums.NethelpersRoutingTable { if x != nil { return x.Table } return enums.NethelpersRoutingTable(0) } func (x *RoutingRuleStatusSpec) GetPriority() uint32 { if x != nil { return x.Priority } return 0 } func (x *RoutingRuleStatusSpec) GetAction() enums.NethelpersRoutingRuleAction { if x != nil { return x.Action } return enums.NethelpersRoutingRuleAction(0) } func (x *RoutingRuleStatusSpec) GetIifName() string { if x != nil { return x.IifName } return "" } func (x *RoutingRuleStatusSpec) GetOifName() string { if x != nil { return x.OifName } return "" } func (x *RoutingRuleStatusSpec) GetFwMark() uint32 { if x != nil { return x.FwMark } return 0 } func (x *RoutingRuleStatusSpec) GetFwMask() uint32 { if x != nil { return x.FwMask } return 0 } // STPSpec describes Spanning Tree Protocol (STP) settings of a bridge. type STPSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *STPSpec) Reset() { *x = STPSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *STPSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*STPSpec) ProtoMessage() {} func (x *STPSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use STPSpec.ProtoReflect.Descriptor instead. func (*STPSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{51} } func (x *STPSpec) GetEnabled() bool { if x != nil { return x.Enabled } return false } // StatusSpec describes network state. type StatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` AddressReady bool `protobuf:"varint,1,opt,name=address_ready,json=addressReady,proto3" json:"address_ready,omitempty"` ConnectivityReady bool `protobuf:"varint,2,opt,name=connectivity_ready,json=connectivityReady,proto3" json:"connectivity_ready,omitempty"` HostnameReady bool `protobuf:"varint,3,opt,name=hostname_ready,json=hostnameReady,proto3" json:"hostname_ready,omitempty"` EtcFilesReady bool `protobuf:"varint,4,opt,name=etc_files_ready,json=etcFilesReady,proto3" json:"etc_files_ready,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StatusSpec) Reset() { *x = StatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StatusSpec) ProtoMessage() {} func (x *StatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StatusSpec.ProtoReflect.Descriptor instead. func (*StatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{52} } func (x *StatusSpec) GetAddressReady() bool { if x != nil { return x.AddressReady } return false } func (x *StatusSpec) GetConnectivityReady() bool { if x != nil { return x.ConnectivityReady } return false } func (x *StatusSpec) GetHostnameReady() bool { if x != nil { return x.HostnameReady } return false } func (x *StatusSpec) GetEtcFilesReady() bool { if x != nil { return x.EtcFilesReady } return false } // TCPProbeSpec describes the TCP Probe. type TCPProbeSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` Timeout *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TCPProbeSpec) Reset() { *x = TCPProbeSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TCPProbeSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TCPProbeSpec) ProtoMessage() {} func (x *TCPProbeSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TCPProbeSpec.ProtoReflect.Descriptor instead. func (*TCPProbeSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{53} } func (x *TCPProbeSpec) GetEndpoint() string { if x != nil { return x.Endpoint } return "" } func (x *TCPProbeSpec) GetTimeout() *durationpb.Duration { if x != nil { return x.Timeout } return nil } // TimeServerSpecSpec describes NTP servers. type TimeServerSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` NtpServers []string `protobuf:"bytes,1,rep,name=ntp_servers,json=ntpServers,proto3" json:"ntp_servers,omitempty"` ConfigLayer enums.NetworkConfigLayer `protobuf:"varint,2,opt,name=config_layer,json=configLayer,proto3,enum=talos.resource.definitions.enums.NetworkConfigLayer" json:"config_layer,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeServerSpecSpec) Reset() { *x = TimeServerSpecSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeServerSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeServerSpecSpec) ProtoMessage() {} func (x *TimeServerSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeServerSpecSpec.ProtoReflect.Descriptor instead. func (*TimeServerSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{54} } func (x *TimeServerSpecSpec) GetNtpServers() []string { if x != nil { return x.NtpServers } return nil } func (x *TimeServerSpecSpec) GetConfigLayer() enums.NetworkConfigLayer { if x != nil { return x.ConfigLayer } return enums.NetworkConfigLayer(0) } // TimeServerStatusSpec describes NTP servers. type TimeServerStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` NtpServers []string `protobuf:"bytes,1,rep,name=ntp_servers,json=ntpServers,proto3" json:"ntp_servers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeServerStatusSpec) Reset() { *x = TimeServerStatusSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeServerStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeServerStatusSpec) ProtoMessage() {} func (x *TimeServerStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeServerStatusSpec.ProtoReflect.Descriptor instead. func (*TimeServerStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{55} } func (x *TimeServerStatusSpec) GetNtpServers() []string { if x != nil { return x.NtpServers } return nil } // VIPEquinixMetalSpec describes virtual (elastic) IP settings for Equinix Metal. type VIPEquinixMetalSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ProjectId string `protobuf:"bytes,1,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` DeviceId string `protobuf:"bytes,2,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` ApiToken string `protobuf:"bytes,3,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VIPEquinixMetalSpec) Reset() { *x = VIPEquinixMetalSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VIPEquinixMetalSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VIPEquinixMetalSpec) ProtoMessage() {} func (x *VIPEquinixMetalSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VIPEquinixMetalSpec.ProtoReflect.Descriptor instead. func (*VIPEquinixMetalSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{56} } func (x *VIPEquinixMetalSpec) GetProjectId() string { if x != nil { return x.ProjectId } return "" } func (x *VIPEquinixMetalSpec) GetDeviceId() string { if x != nil { return x.DeviceId } return "" } func (x *VIPEquinixMetalSpec) GetApiToken() string { if x != nil { return x.ApiToken } return "" } // VIPHCloudSpec describes virtual (elastic) IP settings for Hetzner Cloud. type VIPHCloudSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DeviceId int64 `protobuf:"varint,1,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` NetworkId int64 `protobuf:"varint,2,opt,name=network_id,json=networkId,proto3" json:"network_id,omitempty"` ApiToken string `protobuf:"bytes,3,opt,name=api_token,json=apiToken,proto3" json:"api_token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VIPHCloudSpec) Reset() { *x = VIPHCloudSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VIPHCloudSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VIPHCloudSpec) ProtoMessage() {} func (x *VIPHCloudSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VIPHCloudSpec.ProtoReflect.Descriptor instead. func (*VIPHCloudSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{57} } func (x *VIPHCloudSpec) GetDeviceId() int64 { if x != nil { return x.DeviceId } return 0 } func (x *VIPHCloudSpec) GetNetworkId() int64 { if x != nil { return x.NetworkId } return 0 } func (x *VIPHCloudSpec) GetApiToken() string { if x != nil { return x.ApiToken } return "" } // VIPOperatorSpec describes virtual IP operator options. type VIPOperatorSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ip *common.NetIP `protobuf:"bytes,1,opt,name=ip,proto3" json:"ip,omitempty"` GratuitousArp bool `protobuf:"varint,2,opt,name=gratuitous_arp,json=gratuitousArp,proto3" json:"gratuitous_arp,omitempty"` EquinixMetal *VIPEquinixMetalSpec `protobuf:"bytes,3,opt,name=equinix_metal,json=equinixMetal,proto3" json:"equinix_metal,omitempty"` HCloud *VIPHCloudSpec `protobuf:"bytes,4,opt,name=h_cloud,json=hCloud,proto3" json:"h_cloud,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VIPOperatorSpec) Reset() { *x = VIPOperatorSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VIPOperatorSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VIPOperatorSpec) ProtoMessage() {} func (x *VIPOperatorSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VIPOperatorSpec.ProtoReflect.Descriptor instead. func (*VIPOperatorSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{58} } func (x *VIPOperatorSpec) GetIp() *common.NetIP { if x != nil { return x.Ip } return nil } func (x *VIPOperatorSpec) GetGratuitousArp() bool { if x != nil { return x.GratuitousArp } return false } func (x *VIPOperatorSpec) GetEquinixMetal() *VIPEquinixMetalSpec { if x != nil { return x.EquinixMetal } return nil } func (x *VIPOperatorSpec) GetHCloud() *VIPHCloudSpec { if x != nil { return x.HCloud } return nil } // VLANSpec describes VLAN settings if Kind == "vlan". type VLANSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Vid uint32 `protobuf:"varint,1,opt,name=vid,proto3" json:"vid,omitempty"` Protocol enums.NethelpersVLANProtocol `protobuf:"varint,2,opt,name=protocol,proto3,enum=talos.resource.definitions.enums.NethelpersVLANProtocol" json:"protocol,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VLANSpec) Reset() { *x = VLANSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VLANSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VLANSpec) ProtoMessage() {} func (x *VLANSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VLANSpec.ProtoReflect.Descriptor instead. func (*VLANSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{59} } func (x *VLANSpec) GetVid() uint32 { if x != nil { return x.Vid } return 0 } func (x *VLANSpec) GetProtocol() enums.NethelpersVLANProtocol { if x != nil { return x.Protocol } return enums.NethelpersVLANProtocol(0) } // VRFMasterSpec describes vrf settings if Kind == "vrf". type VRFMasterSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Table enums.NethelpersRoutingTable `protobuf:"varint,1,opt,name=table,proto3,enum=talos.resource.definitions.enums.NethelpersRoutingTable" json:"table,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VRFMasterSpec) Reset() { *x = VRFMasterSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VRFMasterSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*VRFMasterSpec) ProtoMessage() {} func (x *VRFMasterSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VRFMasterSpec.ProtoReflect.Descriptor instead. func (*VRFMasterSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{60} } func (x *VRFMasterSpec) GetTable() enums.NethelpersRoutingTable { if x != nil { return x.Table } return enums.NethelpersRoutingTable(0) } // VRFSlave contains the name of the master vrf for an interface type VRFSlave struct { state protoimpl.MessageState `protogen:"open.v1"` MasterName string `protobuf:"bytes,1,opt,name=master_name,json=masterName,proto3" json:"master_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *VRFSlave) Reset() { *x = VRFSlave{} mi := &file_resource_definitions_network_network_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *VRFSlave) String() string { return protoimpl.X.MessageStringOf(x) } func (*VRFSlave) ProtoMessage() {} func (x *VRFSlave) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use VRFSlave.ProtoReflect.Descriptor instead. func (*VRFSlave) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{61} } func (x *VRFSlave) GetMasterName() string { if x != nil { return x.MasterName } return "" } // WireguardPeer describes a single peer. type WireguardPeer struct { state protoimpl.MessageState `protogen:"open.v1"` PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` PresharedKey string `protobuf:"bytes,2,opt,name=preshared_key,json=presharedKey,proto3" json:"preshared_key,omitempty"` Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"` PersistentKeepaliveInterval *durationpb.Duration `protobuf:"bytes,4,opt,name=persistent_keepalive_interval,json=persistentKeepaliveInterval,proto3" json:"persistent_keepalive_interval,omitempty"` AllowedIps []*common.NetIPPrefix `protobuf:"bytes,5,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WireguardPeer) Reset() { *x = WireguardPeer{} mi := &file_resource_definitions_network_network_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WireguardPeer) String() string { return protoimpl.X.MessageStringOf(x) } func (*WireguardPeer) ProtoMessage() {} func (x *WireguardPeer) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WireguardPeer.ProtoReflect.Descriptor instead. func (*WireguardPeer) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{62} } func (x *WireguardPeer) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } func (x *WireguardPeer) GetPresharedKey() string { if x != nil { return x.PresharedKey } return "" } func (x *WireguardPeer) GetEndpoint() string { if x != nil { return x.Endpoint } return "" } func (x *WireguardPeer) GetPersistentKeepaliveInterval() *durationpb.Duration { if x != nil { return x.PersistentKeepaliveInterval } return nil } func (x *WireguardPeer) GetAllowedIps() []*common.NetIPPrefix { if x != nil { return x.AllowedIps } return nil } // WireguardSpec describes Wireguard settings if Kind == "wireguard". type WireguardSpec struct { state protoimpl.MessageState `protogen:"open.v1"` PrivateKey string `protobuf:"bytes,1,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` PublicKey string `protobuf:"bytes,2,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` ListenPort int64 `protobuf:"varint,3,opt,name=listen_port,json=listenPort,proto3" json:"listen_port,omitempty"` FirewallMark int64 `protobuf:"varint,4,opt,name=firewall_mark,json=firewallMark,proto3" json:"firewall_mark,omitempty"` Peers []*WireguardPeer `protobuf:"bytes,5,rep,name=peers,proto3" json:"peers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WireguardSpec) Reset() { *x = WireguardSpec{} mi := &file_resource_definitions_network_network_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WireguardSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*WireguardSpec) ProtoMessage() {} func (x *WireguardSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_network_network_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WireguardSpec.ProtoReflect.Descriptor instead. func (*WireguardSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_network_network_proto_rawDescGZIP(), []int{63} } func (x *WireguardSpec) GetPrivateKey() string { if x != nil { return x.PrivateKey } return "" } func (x *WireguardSpec) GetPublicKey() string { if x != nil { return x.PublicKey } return "" } func (x *WireguardSpec) GetListenPort() int64 { if x != nil { return x.ListenPort } return 0 } func (x *WireguardSpec) GetFirewallMark() int64 { if x != nil { return x.FirewallMark } return 0 } func (x *WireguardSpec) GetPeers() []*WireguardPeer { if x != nil { return x.Peers } return nil } var File_resource_definitions_network_network_proto protoreflect.FileDescriptor const file_resource_definitions_network_network_proto_rawDesc = "" + "\n" + "*resource/definitions/network/network.proto\x12\"talos.resource.definitions.network\x1a\x13common/common.proto\x1a\x1egoogle/protobuf/duration.proto\x1a&resource/definitions/enums/enums.proto\x1a*resource/definitions/runtime/runtime.proto\"\xa9\x03\n" + "\x0fAddressSpecSpec\x12-\n" + "\aaddress\x18\x01 \x01(\v2\x13.common.NetIPPrefixR\aaddress\x12\x1b\n" + "\tlink_name\x18\x02 \x01(\tR\blinkName\x12J\n" + "\x06family\x18\x03 \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x12G\n" + "\x05scope\x18\x04 \x01(\x0e21.talos.resource.definitions.enums.NethelpersScopeR\x05scope\x12\x14\n" + "\x05flags\x18\x05 \x01(\rR\x05flags\x12*\n" + "\x11announce_with_arp\x18\x06 \x01(\bR\x0fannounceWithArp\x12W\n" + "\fconfig_layer\x18\a \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\x12\x1a\n" + "\bpriority\x18\b \x01(\rR\bpriority\"\xed\x03\n" + "\x11AddressStatusSpec\x12-\n" + "\aaddress\x18\x01 \x01(\v2\x13.common.NetIPPrefixR\aaddress\x12#\n" + "\x05local\x18\x02 \x01(\v2\r.common.NetIPR\x05local\x12+\n" + "\tbroadcast\x18\x03 \x01(\v2\r.common.NetIPR\tbroadcast\x12'\n" + "\aanycast\x18\x04 \x01(\v2\r.common.NetIPR\aanycast\x12+\n" + "\tmulticast\x18\x05 \x01(\v2\r.common.NetIPR\tmulticast\x12\x1d\n" + "\n" + "link_index\x18\x06 \x01(\rR\tlinkIndex\x12\x1b\n" + "\tlink_name\x18\a \x01(\tR\blinkName\x12J\n" + "\x06family\x18\b \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x12G\n" + "\x05scope\x18\t \x01(\x0e21.talos.resource.definitions.enums.NethelpersScopeR\x05scope\x12\x14\n" + "\x05flags\x18\n" + " \x01(\rR\x05flags\x12\x1a\n" + "\bpriority\x18\v \x01(\rR\bpriority\"\x8a\f\n" + "\x0eBondMasterSpec\x12H\n" + "\x04mode\x18\x01 \x01(\x0e24.talos.resource.definitions.enums.NethelpersBondModeR\x04mode\x12_\n" + "\vhash_policy\x18\x02 \x01(\x0e2>.talos.resource.definitions.enums.NethelpersBondXmitHashPolicyR\n" + "hashPolicy\x12Q\n" + "\tlacp_rate\x18\x03 \x01(\x0e24.talos.resource.definitions.enums.NethelpersLACPRateR\blacpRate\x12Z\n" + "\farp_validate\x18\x04 \x01(\x0e27.talos.resource.definitions.enums.NethelpersARPValidateR\varpValidate\x12a\n" + "\x0farp_all_targets\x18\x05 \x01(\x0e29.talos.resource.definitions.enums.NethelpersARPAllTargetsR\rarpAllTargets\x12#\n" + "\rprimary_index\x18\x06 \x01(\rR\fprimaryIndex\x12f\n" + "\x10primary_reselect\x18\a \x01(\x0e2;.talos.resource.definitions.enums.NethelpersPrimaryReselectR\x0fprimaryReselect\x12[\n" + "\rfail_over_mac\x18\b \x01(\x0e27.talos.resource.definitions.enums.NethelpersFailOverMACR\vfailOverMac\x12Q\n" + "\tad_select\x18\t \x01(\x0e24.talos.resource.definitions.enums.NethelpersADSelectR\badSelect\x12\x17\n" + "\amii_mon\x18\n" + " \x01(\rR\x06miiMon\x12\x19\n" + "\bup_delay\x18\v \x01(\rR\aupDelay\x12\x1d\n" + "\n" + "down_delay\x18\f \x01(\rR\tdownDelay\x12!\n" + "\farp_interval\x18\r \x01(\rR\varpInterval\x12\x1f\n" + "\vresend_igmp\x18\x0e \x01(\rR\n" + "resendIgmp\x12\x1b\n" + "\tmin_links\x18\x0f \x01(\rR\bminLinks\x12\x1f\n" + "\vlp_interval\x18\x10 \x01(\rR\n" + "lpInterval\x12*\n" + "\x11packets_per_slave\x18\x11 \x01(\rR\x0fpacketsPerSlave\x12$\n" + "\x0enum_peer_notif\x18\x12 \x01(\rR\fnumPeerNotif\x12$\n" + "\x0etlb_dynamic_lb\x18\x13 \x01(\rR\ftlbDynamicLb\x12*\n" + "\x11all_slaves_active\x18\x14 \x01(\rR\x0fallSlavesActive\x12\x1f\n" + "\vuse_carrier\x18\x15 \x01(\bR\n" + "useCarrier\x12)\n" + "\x11ad_actor_sys_prio\x18\x16 \x01(\rR\x0eadActorSysPrio\x12'\n" + "\x10ad_user_port_key\x18\x17 \x01(\rR\radUserPortKey\x12*\n" + "\x11peer_notify_delay\x18\x18 \x01(\rR\x0fpeerNotifyDelay\x122\n" + "\rarpip_targets\x18\x19 \x03(\v2\r.common.NetIPR\farpipTargets\x122\n" + "\rnsip6_targets\x18\x1a \x03(\v2\r.common.NetIPR\fnsip6Targets\x12]\n" + "\radlacp_active\x18\x1b \x01(\x0e28.talos.resource.definitions.enums.NethelpersADLACPActiveR\fadlacpActive\x12\x1d\n" + "\n" + "missed_max\x18\x1c \x01(\rR\tmissedMax\"M\n" + "\tBondSlave\x12\x1f\n" + "\vmaster_name\x18\x01 \x01(\tR\n" + "masterName\x12\x1f\n" + "\vslave_index\x18\x02 \x01(\x03R\n" + "slaveIndex\"\x99\x01\n" + "\x10BridgeMasterSpec\x12=\n" + "\x03stp\x18\x01 \x01(\v2+.talos.resource.definitions.network.STPSpecR\x03stp\x12F\n" + "\x04vlan\x18\x02 \x01(\v22.talos.resource.definitions.network.BridgeVLANSpecR\x04vlan\".\n" + "\vBridgeSlave\x12\x1f\n" + "\vmaster_name\x18\x01 \x01(\tR\n" + "masterName\"=\n" + "\x0eBridgeVLANSpec\x12+\n" + "\x11filtering_enabled\x18\x01 \x01(\bR\x10filteringEnabled\"\xa3\x01\n" + "\x14ClientIdentifierSpec\x12i\n" + "\x11client_identifier\x18\x01 \x01(\x0e2<.talos.resource.definitions.enums.NethelpersClientIdentifierR\x10clientIdentifier\x12 \n" + "\fduid_raw_hex\x18\x02 \x01(\tR\n" + "duidRawHex\"\xd1\x01\n" + "\x11DHCP4OperatorSpec\x12!\n" + "\froute_metric\x18\x01 \x01(\rR\vrouteMetric\x122\n" + "\x15skip_hostname_request\x18\x02 \x01(\bR\x13skipHostnameRequest\x12e\n" + "\x11client_identifier\x18\x03 \x01(\v28.talos.resource.definitions.network.ClientIdentifierSpecR\x10clientIdentifier\"\xd1\x01\n" + "\x11DHCP6OperatorSpec\x12!\n" + "\froute_metric\x18\x02 \x01(\rR\vrouteMetric\x122\n" + "\x15skip_hostname_request\x18\x03 \x01(\bR\x13skipHostnameRequest\x12e\n" + "\x11client_identifier\x18\x04 \x01(\v28.talos.resource.definitions.network.ClientIdentifierSpecR\x10clientIdentifier\"-\n" + "\x13DNSResolveCacheSpec\x12\x16\n" + "\x06status\x18\x01 \x01(\tR\x06status\"h\n" + "\x14EthernetChannelsSpec\x12\x0e\n" + "\x02rx\x18\x01 \x01(\rR\x02rx\x12\x0e\n" + "\x02tx\x18\x02 \x01(\rR\x02tx\x12\x14\n" + "\x05other\x18\x03 \x01(\rR\x05other\x12\x1a\n" + "\bcombined\x18\x04 \x01(\rR\bcombined\"\xd8\x01\n" + "\x16EthernetChannelsStatus\x12\x15\n" + "\x06rx_max\x18\x01 \x01(\rR\x05rxMax\x12\x15\n" + "\x06tx_max\x18\x02 \x01(\rR\x05txMax\x12\x1b\n" + "\tother_max\x18\x03 \x01(\rR\botherMax\x12!\n" + "\fcombined_max\x18\x04 \x01(\rR\vcombinedMax\x12\x0e\n" + "\x02rx\x18\x05 \x01(\rR\x02rx\x12\x0e\n" + "\x02tx\x18\x06 \x01(\rR\x02tx\x12\x14\n" + "\x05other\x18\a \x01(\rR\x05other\x12\x1a\n" + "\bcombined\x18\b \x01(\rR\bcombined\"C\n" + "\x15EthernetFeatureStatus\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06status\x18\x02 \x01(\tR\x06status\"\x9f\x02\n" + "\x11EthernetRingsSpec\x12\x0e\n" + "\x02rx\x18\x01 \x01(\rR\x02rx\x12\x17\n" + "\arx_mini\x18\x02 \x01(\rR\x06rxMini\x12\x19\n" + "\brx_jumbo\x18\x03 \x01(\rR\arxJumbo\x12\x0e\n" + "\x02tx\x18\x04 \x01(\rR\x02tx\x12\x1c\n" + "\n" + "rx_buf_len\x18\x05 \x01(\rR\brxBufLen\x12\x19\n" + "\bcqe_size\x18\x06 \x01(\rR\acqeSize\x12\x17\n" + "\atx_push\x18\a \x01(\bR\x06txPush\x12\x17\n" + "\arx_push\x18\b \x01(\bR\x06rxPush\x12%\n" + "\x0ftx_push_buf_len\x18\t \x01(\rR\ftxPushBufLen\x12$\n" + "\x0etcp_data_split\x18\n" + " \x01(\bR\ftcpDataSplit\"\xbf\x03\n" + "\x13EthernetRingsStatus\x12\x15\n" + "\x06rx_max\x18\x01 \x01(\rR\x05rxMax\x12\x1e\n" + "\vrx_mini_max\x18\x02 \x01(\rR\trxMiniMax\x12 \n" + "\frx_jumbo_max\x18\x03 \x01(\rR\n" + "rxJumboMax\x12\x15\n" + "\x06tx_max\x18\x04 \x01(\rR\x05txMax\x12,\n" + "\x13tx_push_buf_len_max\x18\x05 \x01(\rR\x0ftxPushBufLenMax\x12\x0e\n" + "\x02rx\x18\x06 \x01(\rR\x02rx\x12\x17\n" + "\arx_mini\x18\a \x01(\rR\x06rxMini\x12\x19\n" + "\brx_jumbo\x18\b \x01(\rR\arxJumbo\x12\x0e\n" + "\x02tx\x18\t \x01(\rR\x02tx\x12\x1c\n" + "\n" + "rx_buf_len\x18\n" + " \x01(\rR\brxBufLen\x12\x19\n" + "\bcqe_size\x18\v \x01(\rR\acqeSize\x12\x17\n" + "\atx_push\x18\f \x01(\bR\x06txPush\x12\x17\n" + "\arx_push\x18\r \x01(\bR\x06rxPush\x12%\n" + "\x0ftx_push_buf_len\x18\x0e \x01(\rR\ftxPushBufLen\x12$\n" + "\x0etcp_data_split\x18\x0f \x01(\bR\ftcpDataSplit\"\xa7\x03\n" + "\x10EthernetSpecSpec\x12K\n" + "\x05rings\x18\x01 \x01(\v25.talos.resource.definitions.network.EthernetRingsSpecR\x05rings\x12^\n" + "\bfeatures\x18\x02 \x03(\v2B.talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntryR\bfeatures\x12T\n" + "\bchannels\x18\x03 \x01(\v28.talos.resource.definitions.network.EthernetChannelsSpecR\bchannels\x12S\n" + "\vwake_on_lan\x18\x04 \x03(\x0e23.talos.resource.definitions.enums.NethelpersWOLModeR\twakeOnLan\x1a;\n" + "\rFeaturesEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\bR\x05value:\x028\x01\"\xfb\x04\n" + "\x12EthernetStatusSpec\x12\x1d\n" + "\n" + "link_state\x18\x01 \x01(\bR\tlinkState\x12%\n" + "\x0espeed_megabits\x18\x02 \x01(\x03R\rspeedMegabits\x12D\n" + "\x04port\x18\x03 \x01(\x0e20.talos.resource.definitions.enums.NethelpersPortR\x04port\x12J\n" + "\x06duplex\x18\x04 \x01(\x0e22.talos.resource.definitions.enums.NethelpersDuplexR\x06duplex\x12\x1b\n" + "\tour_modes\x18\x05 \x03(\tR\bourModes\x12\x1d\n" + "\n" + "peer_modes\x18\x06 \x03(\tR\tpeerModes\x12M\n" + "\x05rings\x18\a \x01(\v27.talos.resource.definitions.network.EthernetRingsStatusR\x05rings\x12U\n" + "\bfeatures\x18\b \x03(\v29.talos.resource.definitions.network.EthernetFeatureStatusR\bfeatures\x12V\n" + "\bchannels\x18\t \x01(\v2:.talos.resource.definitions.network.EthernetChannelsStatusR\bchannels\x12S\n" + "\vwake_on_lan\x18\n" + " \x03(\x0e23.talos.resource.definitions.enums.NethelpersWOLModeR\twakeOnLan\"K\n" + "\x10HardwareAddrSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + "\rhardware_addr\x18\x02 \x01(\fR\fhardwareAddr\"\xe5\x01\n" + "\x11HostDNSConfigSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x12<\n" + "\x10listen_addresses\x18\x02 \x03(\v2\x11.common.NetIPPortR\x0flistenAddresses\x12F\n" + "\x18service_host_dns_address\x18\x03 \x01(\v2\r.common.NetIPR\x15serviceHostDnsAddress\x120\n" + "\x14resolve_member_names\x18\x04 \x01(\bR\x12resolveMemberNames\"\xa7\x01\n" + "\x10HostnameSpecSpec\x12\x1a\n" + "\bhostname\x18\x01 \x01(\tR\bhostname\x12\x1e\n" + "\n" + "domainname\x18\x02 \x01(\tR\n" + "domainname\x12W\n" + "\fconfig_layer\x18\x03 \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\"P\n" + "\x12HostnameStatusSpec\x12\x1a\n" + "\bhostname\x18\x01 \x01(\tR\bhostname\x12\x1e\n" + "\n" + "domainname\x18\x02 \x01(\tR\n" + "domainname\")\n" + "\x11LinkAliasSpecSpec\x12\x14\n" + "\x05alias\x18\x01 \x01(\tR\x05alias\"1\n" + "\x0fLinkRefreshSpec\x12\x1e\n" + "\n" + "generation\x18\x01 \x01(\x03R\n" + "generation\"\x81\b\n" + "\fLinkSpecSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + "\alogical\x18\x02 \x01(\bR\alogical\x12\x0e\n" + "\x02up\x18\x03 \x01(\bR\x02up\x12\x10\n" + "\x03mtu\x18\x04 \x01(\rR\x03mtu\x12\x12\n" + "\x04kind\x18\x05 \x01(\tR\x04kind\x12H\n" + "\x04type\x18\x06 \x01(\x0e24.talos.resource.definitions.enums.NethelpersLinkTypeR\x04type\x12\x1f\n" + "\vparent_name\x18\a \x01(\tR\n" + "parentName\x12L\n" + "\n" + "bond_slave\x18\b \x01(\v2-.talos.resource.definitions.network.BondSlaveR\tbondSlave\x12R\n" + "\fbridge_slave\x18\t \x01(\v2/.talos.resource.definitions.network.BridgeSlaveR\vbridgeSlave\x12@\n" + "\x04vlan\x18\n" + " \x01(\v2,.talos.resource.definitions.network.VLANSpecR\x04vlan\x12S\n" + "\vbond_master\x18\v \x01(\v22.talos.resource.definitions.network.BondMasterSpecR\n" + "bondMaster\x12Y\n" + "\rbridge_master\x18\f \x01(\v24.talos.resource.definitions.network.BridgeMasterSpecR\fbridgeMaster\x12O\n" + "\twireguard\x18\r \x01(\v21.talos.resource.definitions.network.WireguardSpecR\twireguard\x12W\n" + "\fconfig_layer\x18\x0e \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\x12)\n" + "\x10hardware_address\x18\x0f \x01(\fR\x0fhardwareAddress\x12\x1c\n" + "\tmulticast\x18\x10 \x01(\bR\tmulticast\x12P\n" + "\n" + "vrf_master\x18\x11 \x01(\v21.talos.resource.definitions.network.VRFMasterSpecR\tvrfMaster\x12I\n" + "\tvrf_slave\x18\x12 \x01(\v2,.talos.resource.definitions.network.VRFSlaveR\bvrfSlave\"\xb3\v\n" + "\x0eLinkStatusSpec\x12\x14\n" + "\x05index\x18\x01 \x01(\rR\x05index\x12H\n" + "\x04type\x18\x02 \x01(\x0e24.talos.resource.definitions.enums.NethelpersLinkTypeR\x04type\x12\x1d\n" + "\n" + "link_index\x18\x03 \x01(\rR\tlinkIndex\x12\x14\n" + "\x05flags\x18\x04 \x01(\rR\x05flags\x12#\n" + "\rhardware_addr\x18\x05 \x01(\fR\fhardwareAddr\x12%\n" + "\x0ebroadcast_addr\x18\x06 \x01(\fR\rbroadcastAddr\x12\x10\n" + "\x03mtu\x18\a \x01(\rR\x03mtu\x12\x1d\n" + "\n" + "queue_disc\x18\b \x01(\tR\tqueueDisc\x12!\n" + "\fmaster_index\x18\t \x01(\rR\vmasterIndex\x12i\n" + "\x11operational_state\x18\n" + " \x01(\x0e2<.talos.resource.definitions.enums.NethelpersOperationalStateR\x10operationalState\x12\x12\n" + "\x04kind\x18\v \x01(\tR\x04kind\x12\x1d\n" + "\n" + "slave_kind\x18\f \x01(\tR\tslaveKind\x12\x19\n" + "\bbus_path\x18\r \x01(\tR\abusPath\x12\x14\n" + "\x05pciid\x18\x0e \x01(\tR\x05pciid\x12\x16\n" + "\x06driver\x18\x0f \x01(\tR\x06driver\x12%\n" + "\x0edriver_version\x18\x10 \x01(\tR\rdriverVersion\x12)\n" + "\x10firmware_version\x18\x11 \x01(\tR\x0ffirmwareVersion\x12\x1d\n" + "\n" + "product_id\x18\x12 \x01(\tR\tproductId\x12\x1b\n" + "\tvendor_id\x18\x13 \x01(\tR\bvendorId\x12\x18\n" + "\aproduct\x18\x14 \x01(\tR\aproduct\x12\x16\n" + "\x06vendor\x18\x15 \x01(\tR\x06vendor\x12\x1d\n" + "\n" + "link_state\x18\x16 \x01(\bR\tlinkState\x12%\n" + "\x0espeed_megabits\x18\x17 \x01(\x03R\rspeedMegabits\x12D\n" + "\x04port\x18\x18 \x01(\x0e20.talos.resource.definitions.enums.NethelpersPortR\x04port\x12J\n" + "\x06duplex\x18\x19 \x01(\x0e22.talos.resource.definitions.enums.NethelpersDuplexR\x06duplex\x12@\n" + "\x04vlan\x18\x1a \x01(\v2,.talos.resource.definitions.network.VLANSpecR\x04vlan\x12Y\n" + "\rbridge_master\x18\x1b \x01(\v24.talos.resource.definitions.network.BridgeMasterSpecR\fbridgeMaster\x12S\n" + "\vbond_master\x18\x1c \x01(\v22.talos.resource.definitions.network.BondMasterSpecR\n" + "bondMaster\x12O\n" + "\twireguard\x18\x1d \x01(\v21.talos.resource.definitions.network.WireguardSpecR\twireguard\x12%\n" + "\x0epermanent_addr\x18\x1e \x01(\fR\rpermanentAddr\x12\x14\n" + "\x05alias\x18\x1f \x01(\tR\x05alias\x12\x1b\n" + "\talt_names\x18 \x03(\tR\baltNames\x12P\n" + "\n" + "vrf_master\x18! \x01(\v21.talos.resource.definitions.network.VRFMasterSpecR\tvrfMaster\"\xaa\x01\n" + "\x14NfTablesAddressMatch\x12<\n" + "\x0finclude_subnets\x18\x01 \x03(\v2\x13.common.NetIPPrefixR\x0eincludeSubnets\x12<\n" + "\x0fexclude_subnets\x18\x02 \x03(\v2\x13.common.NetIPPrefixR\x0eexcludeSubnets\x12\x16\n" + "\x06invert\x18\x03 \x01(\bR\x06invert\"\xf6\x02\n" + "\x11NfTablesChainSpec\x12\x12\n" + "\x04type\x18\x01 \x01(\tR\x04type\x12Q\n" + "\x04hook\x18\x02 \x01(\x0e2=.talos.resource.definitions.enums.NethelpersNfTablesChainHookR\x04hook\x12]\n" + "\bpriority\x18\x03 \x01(\x0e2A.talos.resource.definitions.enums.NethelpersNfTablesChainPriorityR\bpriority\x12F\n" + "\x05rules\x18\x04 \x03(\v20.talos.resource.definitions.network.NfTablesRuleR\x05rules\x12S\n" + "\x06policy\x18\x05 \x01(\x0e2;.talos.resource.definitions.enums.NethelpersNfTablesVerdictR\x06policy\"$\n" + "\x10NfTablesClampMSS\x12\x10\n" + "\x03mtu\x18\x01 \x01(\rR\x03mtu\"q\n" + "\x1bNfTablesConntrackStateMatch\x12R\n" + "\x06states\x18\x01 \x03(\x0e2:.talos.resource.definitions.enums.NethelpersConntrackStateR\x06states\"c\n" + "\x15NfTablesICMPTypeMatch\x12J\n" + "\x05types\x18\x01 \x03(\x0e24.talos.resource.definitions.enums.NethelpersICMPTypeR\x05types\"\x95\x01\n" + "\x13NfTablesIfNameMatch\x12U\n" + "\boperator\x18\x02 \x01(\x0e29.talos.resource.definitions.enums.NethelpersMatchOperatorR\boperator\x12'\n" + "\x0finterface_names\x18\x03 \x03(\tR\x0einterfaceNames\"\x9a\x03\n" + "\x13NfTablesLayer4Match\x12P\n" + "\bprotocol\x18\x01 \x01(\x0e24.talos.resource.definitions.enums.NethelpersProtocolR\bprotocol\x12a\n" + "\x11match_source_port\x18\x02 \x01(\v25.talos.resource.definitions.network.NfTablesPortMatchR\x0fmatchSourcePort\x12k\n" + "\x16match_destination_port\x18\x03 \x01(\v25.talos.resource.definitions.network.NfTablesPortMatchR\x14matchDestinationPort\x12a\n" + "\x0fmatch_icmp_type\x18\x04 \x01(\v29.talos.resource.definitions.network.NfTablesICMPTypeMatchR\rmatchIcmpType\"I\n" + "\x12NfTablesLimitMatch\x123\n" + "\x16packet_rate_per_second\x18\x01 \x01(\x04R\x13packetRatePerSecond\"J\n" + "\fNfTablesMark\x12\x12\n" + "\x04mask\x18\x01 \x01(\rR\x04mask\x12\x10\n" + "\x03xor\x18\x02 \x01(\rR\x03xor\x12\x14\n" + "\x05value\x18\x03 \x01(\rR\x05value\"Z\n" + "\x11NfTablesPortMatch\x12E\n" + "\x06ranges\x18\x01 \x03(\v2-.talos.resource.definitions.network.PortRangeR\x06ranges\"\xc5\b\n" + "\fNfTablesRule\x12^\n" + "\x0fmatch_o_if_name\x18\x01 \x01(\v27.talos.resource.definitions.network.NfTablesIfNameMatchR\fmatchOIfName\x12U\n" + "\averdict\x18\x02 \x01(\x0e2;.talos.resource.definitions.enums.NethelpersNfTablesVerdictR\averdict\x12O\n" + "\n" + "match_mark\x18\x03 \x01(\v20.talos.resource.definitions.network.NfTablesMarkR\tmatchMark\x12K\n" + "\bset_mark\x18\x04 \x01(\v20.talos.resource.definitions.network.NfTablesMarkR\asetMark\x12j\n" + "\x14match_source_address\x18\x05 \x01(\v28.talos.resource.definitions.network.NfTablesAddressMatchR\x12matchSourceAddress\x12t\n" + "\x19match_destination_address\x18\x06 \x01(\v28.talos.resource.definitions.network.NfTablesAddressMatchR\x17matchDestinationAddress\x12Z\n" + "\fmatch_layer4\x18\a \x01(\v27.talos.resource.definitions.network.NfTablesLayer4MatchR\vmatchLayer4\x12^\n" + "\x0fmatch_i_if_name\x18\b \x01(\v27.talos.resource.definitions.network.NfTablesIfNameMatchR\fmatchIIfName\x12Q\n" + "\tclamp_mss\x18\t \x01(\v24.talos.resource.definitions.network.NfTablesClampMSSR\bclampMss\x12W\n" + "\vmatch_limit\x18\n" + " \x01(\v26.talos.resource.definitions.network.NfTablesLimitMatchR\n" + "matchLimit\x12s\n" + "\x15match_conntrack_state\x18\v \x01(\v2?.talos.resource.definitions.network.NfTablesConntrackStateMatchR\x13matchConntrackState\x12!\n" + "\fanon_counter\x18\f \x01(\bR\vanonCounter\"\x93\x01\n" + "\x15NodeAddressFilterSpec\x12<\n" + "\x0finclude_subnets\x18\x01 \x03(\v2\x13.common.NetIPPrefixR\x0eincludeSubnets\x12<\n" + "\x0fexclude_subnets\x18\x02 \x03(\v2\x13.common.NetIPPrefixR\x0eexcludeSubnets\"~\n" + "\x1cNodeAddressSortAlgorithmSpec\x12^\n" + "\talgorithm\x18\x01 \x01(\x0e2@.talos.resource.definitions.enums.NethelpersAddressSortAlgorithmR\talgorithm\"\xad\x01\n" + "\x0fNodeAddressSpec\x121\n" + "\taddresses\x18\x01 \x03(\v2\x13.common.NetIPPrefixR\taddresses\x12g\n" + "\x0esort_algorithm\x18\x02 \x01(\x0e2@.talos.resource.definitions.enums.NethelpersAddressSortAlgorithmR\rsortAlgorithm\"\xd7\x03\n" + "\x10OperatorSpecSpec\x12M\n" + "\boperator\x18\x01 \x01(\x0e21.talos.resource.definitions.enums.NetworkOperatorR\boperator\x12\x1b\n" + "\tlink_name\x18\x02 \x01(\tR\blinkName\x12\x1d\n" + "\n" + "require_up\x18\x03 \x01(\bR\trequireUp\x12K\n" + "\x05dhcp4\x18\x04 \x01(\v25.talos.resource.definitions.network.DHCP4OperatorSpecR\x05dhcp4\x12K\n" + "\x05dhcp6\x18\x05 \x01(\v25.talos.resource.definitions.network.DHCP6OperatorSpecR\x05dhcp6\x12E\n" + "\x03vip\x18\x06 \x01(\v23.talos.resource.definitions.network.VIPOperatorSpecR\x03vip\x12W\n" + "\fconfig_layer\x18\a \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\"\xa4\x06\n" + "\x12PlatformConfigSpec\x12Q\n" + "\taddresses\x18\x01 \x03(\v23.talos.resource.definitions.network.AddressSpecSpecR\taddresses\x12F\n" + "\x05links\x18\x02 \x03(\v20.talos.resource.definitions.network.LinkSpecSpecR\x05links\x12I\n" + "\x06routes\x18\x03 \x03(\v21.talos.resource.definitions.network.RouteSpecSpecR\x06routes\x12R\n" + "\thostnames\x18\x04 \x03(\v24.talos.resource.definitions.network.HostnameSpecSpecR\thostnames\x12R\n" + "\tresolvers\x18\x05 \x03(\v24.talos.resource.definitions.network.ResolverSpecSpecR\tresolvers\x12Y\n" + "\ftime_servers\x18\x06 \x03(\v26.talos.resource.definitions.network.TimeServerSpecSpecR\vtimeServers\x12R\n" + "\toperators\x18\a \x03(\v24.talos.resource.definitions.network.OperatorSpecSpecR\toperators\x120\n" + "\fexternal_ips\x18\b \x03(\v2\r.common.NetIPR\vexternalIps\x12I\n" + "\x06probes\x18\t \x03(\v21.talos.resource.definitions.network.ProbeSpecSpecR\x06probes\x12T\n" + "\bmetadata\x18\n" + " \x01(\v28.talos.resource.definitions.runtime.PlatformMetadataSpecR\bmetadata\"+\n" + "\tPortRange\x12\x0e\n" + "\x02lo\x18\x01 \x01(\rR\x02lo\x12\x0e\n" + "\x02hi\x18\x02 \x01(\rR\x02hi\"\x90\x02\n" + "\rProbeSpecSpec\x125\n" + "\binterval\x18\x01 \x01(\v2\x19.google.protobuf.DurationR\binterval\x12+\n" + "\x11failure_threshold\x18\x02 \x01(\x03R\x10failureThreshold\x12B\n" + "\x03tcp\x18\x03 \x01(\v20.talos.resource.definitions.network.TCPProbeSpecR\x03tcp\x12W\n" + "\fconfig_layer\x18\x04 \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\"J\n" + "\x0fProbeStatusSpec\x12\x18\n" + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x1d\n" + "\n" + "last_error\x18\x02 \x01(\tR\tlastError\"\xc2\x01\n" + "\x10ResolverSpecSpec\x12.\n" + "\vdns_servers\x18\x01 \x03(\v2\r.common.NetIPR\n" + "dnsServers\x12W\n" + "\fconfig_layer\x18\x02 \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\x12%\n" + "\x0esearch_domains\x18\x03 \x03(\tR\rsearchDomains\"k\n" + "\x12ResolverStatusSpec\x12.\n" + "\vdns_servers\x18\x01 \x03(\v2\r.common.NetIPR\n" + "dnsServers\x12%\n" + "\x0esearch_domains\x18\x02 \x03(\tR\rsearchDomains\"\xde\x05\n" + "\rRouteSpecSpec\x12J\n" + "\x06family\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x125\n" + "\vdestination\x18\x02 \x01(\v2\x13.common.NetIPPrefixR\vdestination\x12%\n" + "\x06source\x18\x03 \x01(\v2\r.common.NetIPR\x06source\x12'\n" + "\agateway\x18\x04 \x01(\v2\r.common.NetIPR\agateway\x12\"\n" + "\rout_link_name\x18\x05 \x01(\tR\voutLinkName\x12N\n" + "\x05table\x18\x06 \x01(\x0e28.talos.resource.definitions.enums.NethelpersRoutingTableR\x05table\x12\x1a\n" + "\bpriority\x18\a \x01(\rR\bpriority\x12G\n" + "\x05scope\x18\b \x01(\x0e21.talos.resource.definitions.enums.NethelpersScopeR\x05scope\x12I\n" + "\x04type\x18\t \x01(\x0e25.talos.resource.definitions.enums.NethelpersRouteTypeR\x04type\x12\x14\n" + "\x05flags\x18\n" + " \x01(\rR\x05flags\x12U\n" + "\bprotocol\x18\v \x01(\x0e29.talos.resource.definitions.enums.NethelpersRouteProtocolR\bprotocol\x12W\n" + "\fconfig_layer\x18\f \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\x12\x10\n" + "\x03mtu\x18\r \x01(\rR\x03mtu\"\xad\x05\n" + "\x0fRouteStatusSpec\x12J\n" + "\x06family\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x125\n" + "\vdestination\x18\x02 \x01(\v2\x13.common.NetIPPrefixR\vdestination\x12%\n" + "\x06source\x18\x03 \x01(\v2\r.common.NetIPR\x06source\x12'\n" + "\agateway\x18\x04 \x01(\v2\r.common.NetIPR\agateway\x12$\n" + "\x0eout_link_index\x18\x05 \x01(\rR\foutLinkIndex\x12\"\n" + "\rout_link_name\x18\x06 \x01(\tR\voutLinkName\x12N\n" + "\x05table\x18\a \x01(\x0e28.talos.resource.definitions.enums.NethelpersRoutingTableR\x05table\x12\x1a\n" + "\bpriority\x18\b \x01(\rR\bpriority\x12G\n" + "\x05scope\x18\t \x01(\x0e21.talos.resource.definitions.enums.NethelpersScopeR\x05scope\x12I\n" + "\x04type\x18\n" + " \x01(\x0e25.talos.resource.definitions.enums.NethelpersRouteTypeR\x04type\x12\x14\n" + "\x05flags\x18\v \x01(\rR\x05flags\x12U\n" + "\bprotocol\x18\f \x01(\x0e29.talos.resource.definitions.enums.NethelpersRouteProtocolR\bprotocol\x12\x10\n" + "\x03mtu\x18\r \x01(\rR\x03mtu\"\xb3\x04\n" + "\x13RoutingRuleSpecSpec\x12J\n" + "\x06family\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x12%\n" + "\x03src\x18\x02 \x01(\v2\x13.common.NetIPPrefixR\x03src\x12%\n" + "\x03dst\x18\x03 \x01(\v2\x13.common.NetIPPrefixR\x03dst\x12N\n" + "\x05table\x18\x04 \x01(\x0e28.talos.resource.definitions.enums.NethelpersRoutingTableR\x05table\x12\x1a\n" + "\bpriority\x18\x05 \x01(\rR\bpriority\x12U\n" + "\x06action\x18\x06 \x01(\x0e2=.talos.resource.definitions.enums.NethelpersRoutingRuleActionR\x06action\x12\x19\n" + "\biif_name\x18\a \x01(\tR\aiifName\x12\x19\n" + "\boif_name\x18\b \x01(\tR\aoifName\x12\x17\n" + "\afw_mark\x18\t \x01(\rR\x06fwMark\x12\x17\n" + "\afw_mask\x18\n" + " \x01(\rR\x06fwMask\x12W\n" + "\fconfig_layer\x18\v \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\"\xdc\x03\n" + "\x15RoutingRuleStatusSpec\x12J\n" + "\x06family\x18\x01 \x01(\x0e22.talos.resource.definitions.enums.NethelpersFamilyR\x06family\x12%\n" + "\x03src\x18\x02 \x01(\v2\x13.common.NetIPPrefixR\x03src\x12%\n" + "\x03dst\x18\x03 \x01(\v2\x13.common.NetIPPrefixR\x03dst\x12N\n" + "\x05table\x18\x04 \x01(\x0e28.talos.resource.definitions.enums.NethelpersRoutingTableR\x05table\x12\x1a\n" + "\bpriority\x18\x05 \x01(\rR\bpriority\x12U\n" + "\x06action\x18\x06 \x01(\x0e2=.talos.resource.definitions.enums.NethelpersRoutingRuleActionR\x06action\x12\x19\n" + "\biif_name\x18\a \x01(\tR\aiifName\x12\x19\n" + "\boif_name\x18\b \x01(\tR\aoifName\x12\x17\n" + "\afw_mark\x18\t \x01(\rR\x06fwMark\x12\x17\n" + "\afw_mask\x18\n" + " \x01(\rR\x06fwMask\"#\n" + "\aSTPSpec\x12\x18\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\"\xaf\x01\n" + "\n" + "StatusSpec\x12#\n" + "\raddress_ready\x18\x01 \x01(\bR\faddressReady\x12-\n" + "\x12connectivity_ready\x18\x02 \x01(\bR\x11connectivityReady\x12%\n" + "\x0ehostname_ready\x18\x03 \x01(\bR\rhostnameReady\x12&\n" + "\x0fetc_files_ready\x18\x04 \x01(\bR\retcFilesReady\"_\n" + "\fTCPProbeSpec\x12\x1a\n" + "\bendpoint\x18\x01 \x01(\tR\bendpoint\x123\n" + "\atimeout\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\atimeout\"\x8e\x01\n" + "\x12TimeServerSpecSpec\x12\x1f\n" + "\vntp_servers\x18\x01 \x03(\tR\n" + "ntpServers\x12W\n" + "\fconfig_layer\x18\x02 \x01(\x0e24.talos.resource.definitions.enums.NetworkConfigLayerR\vconfigLayer\"7\n" + "\x14TimeServerStatusSpec\x12\x1f\n" + "\vntp_servers\x18\x01 \x03(\tR\n" + "ntpServers\"n\n" + "\x13VIPEquinixMetalSpec\x12\x1d\n" + "\n" + "project_id\x18\x01 \x01(\tR\tprojectId\x12\x1b\n" + "\tdevice_id\x18\x02 \x01(\tR\bdeviceId\x12\x1b\n" + "\tapi_token\x18\x03 \x01(\tR\bapiToken\"h\n" + "\rVIPHCloudSpec\x12\x1b\n" + "\tdevice_id\x18\x01 \x01(\x03R\bdeviceId\x12\x1d\n" + "\n" + "network_id\x18\x02 \x01(\x03R\tnetworkId\x12\x1b\n" + "\tapi_token\x18\x03 \x01(\tR\bapiToken\"\x81\x02\n" + "\x0fVIPOperatorSpec\x12\x1d\n" + "\x02ip\x18\x01 \x01(\v2\r.common.NetIPR\x02ip\x12%\n" + "\x0egratuitous_arp\x18\x02 \x01(\bR\rgratuitousArp\x12\\\n" + "\requinix_metal\x18\x03 \x01(\v27.talos.resource.definitions.network.VIPEquinixMetalSpecR\fequinixMetal\x12J\n" + "\ah_cloud\x18\x04 \x01(\v21.talos.resource.definitions.network.VIPHCloudSpecR\x06hCloud\"r\n" + "\bVLANSpec\x12\x10\n" + "\x03vid\x18\x01 \x01(\rR\x03vid\x12T\n" + "\bprotocol\x18\x02 \x01(\x0e28.talos.resource.definitions.enums.NethelpersVLANProtocolR\bprotocol\"_\n" + "\rVRFMasterSpec\x12N\n" + "\x05table\x18\x01 \x01(\x0e28.talos.resource.definitions.enums.NethelpersRoutingTableR\x05table\"+\n" + "\bVRFSlave\x12\x1f\n" + "\vmaster_name\x18\x01 \x01(\tR\n" + "masterName\"\x84\x02\n" + "\rWireguardPeer\x12\x1d\n" + "\n" + "public_key\x18\x01 \x01(\tR\tpublicKey\x12#\n" + "\rpreshared_key\x18\x02 \x01(\tR\fpresharedKey\x12\x1a\n" + "\bendpoint\x18\x03 \x01(\tR\bendpoint\x12]\n" + "\x1dpersistent_keepalive_interval\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\x1bpersistentKeepaliveInterval\x124\n" + "\vallowed_ips\x18\x05 \x03(\v2\x13.common.NetIPPrefixR\n" + "allowedIps\"\xde\x01\n" + "\rWireguardSpec\x12\x1f\n" + "\vprivate_key\x18\x01 \x01(\tR\n" + "privateKey\x12\x1d\n" + "\n" + "public_key\x18\x02 \x01(\tR\tpublicKey\x12\x1f\n" + "\vlisten_port\x18\x03 \x01(\x03R\n" + "listenPort\x12#\n" + "\rfirewall_mark\x18\x04 \x01(\x03R\ffirewallMark\x12G\n" + "\x05peers\x18\x05 \x03(\v21.talos.resource.definitions.network.WireguardPeerR\x05peersBx\n" + "*dev.talos.api.resource.definitions.networkZJgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/networkb\x06proto3" var ( file_resource_definitions_network_network_proto_rawDescOnce sync.Once file_resource_definitions_network_network_proto_rawDescData []byte ) func file_resource_definitions_network_network_proto_rawDescGZIP() []byte { file_resource_definitions_network_network_proto_rawDescOnce.Do(func() { file_resource_definitions_network_network_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_network_network_proto_rawDesc), len(file_resource_definitions_network_network_proto_rawDesc))) }) return file_resource_definitions_network_network_proto_rawDescData } var file_resource_definitions_network_network_proto_msgTypes = make([]protoimpl.MessageInfo, 65) var file_resource_definitions_network_network_proto_goTypes = []any{ (*AddressSpecSpec)(nil), // 0: talos.resource.definitions.network.AddressSpecSpec (*AddressStatusSpec)(nil), // 1: talos.resource.definitions.network.AddressStatusSpec (*BondMasterSpec)(nil), // 2: talos.resource.definitions.network.BondMasterSpec (*BondSlave)(nil), // 3: talos.resource.definitions.network.BondSlave (*BridgeMasterSpec)(nil), // 4: talos.resource.definitions.network.BridgeMasterSpec (*BridgeSlave)(nil), // 5: talos.resource.definitions.network.BridgeSlave (*BridgeVLANSpec)(nil), // 6: talos.resource.definitions.network.BridgeVLANSpec (*ClientIdentifierSpec)(nil), // 7: talos.resource.definitions.network.ClientIdentifierSpec (*DHCP4OperatorSpec)(nil), // 8: talos.resource.definitions.network.DHCP4OperatorSpec (*DHCP6OperatorSpec)(nil), // 9: talos.resource.definitions.network.DHCP6OperatorSpec (*DNSResolveCacheSpec)(nil), // 10: talos.resource.definitions.network.DNSResolveCacheSpec (*EthernetChannelsSpec)(nil), // 11: talos.resource.definitions.network.EthernetChannelsSpec (*EthernetChannelsStatus)(nil), // 12: talos.resource.definitions.network.EthernetChannelsStatus (*EthernetFeatureStatus)(nil), // 13: talos.resource.definitions.network.EthernetFeatureStatus (*EthernetRingsSpec)(nil), // 14: talos.resource.definitions.network.EthernetRingsSpec (*EthernetRingsStatus)(nil), // 15: talos.resource.definitions.network.EthernetRingsStatus (*EthernetSpecSpec)(nil), // 16: talos.resource.definitions.network.EthernetSpecSpec (*EthernetStatusSpec)(nil), // 17: talos.resource.definitions.network.EthernetStatusSpec (*HardwareAddrSpec)(nil), // 18: talos.resource.definitions.network.HardwareAddrSpec (*HostDNSConfigSpec)(nil), // 19: talos.resource.definitions.network.HostDNSConfigSpec (*HostnameSpecSpec)(nil), // 20: talos.resource.definitions.network.HostnameSpecSpec (*HostnameStatusSpec)(nil), // 21: talos.resource.definitions.network.HostnameStatusSpec (*LinkAliasSpecSpec)(nil), // 22: talos.resource.definitions.network.LinkAliasSpecSpec (*LinkRefreshSpec)(nil), // 23: talos.resource.definitions.network.LinkRefreshSpec (*LinkSpecSpec)(nil), // 24: talos.resource.definitions.network.LinkSpecSpec (*LinkStatusSpec)(nil), // 25: talos.resource.definitions.network.LinkStatusSpec (*NfTablesAddressMatch)(nil), // 26: talos.resource.definitions.network.NfTablesAddressMatch (*NfTablesChainSpec)(nil), // 27: talos.resource.definitions.network.NfTablesChainSpec (*NfTablesClampMSS)(nil), // 28: talos.resource.definitions.network.NfTablesClampMSS (*NfTablesConntrackStateMatch)(nil), // 29: talos.resource.definitions.network.NfTablesConntrackStateMatch (*NfTablesICMPTypeMatch)(nil), // 30: talos.resource.definitions.network.NfTablesICMPTypeMatch (*NfTablesIfNameMatch)(nil), // 31: talos.resource.definitions.network.NfTablesIfNameMatch (*NfTablesLayer4Match)(nil), // 32: talos.resource.definitions.network.NfTablesLayer4Match (*NfTablesLimitMatch)(nil), // 33: talos.resource.definitions.network.NfTablesLimitMatch (*NfTablesMark)(nil), // 34: talos.resource.definitions.network.NfTablesMark (*NfTablesPortMatch)(nil), // 35: talos.resource.definitions.network.NfTablesPortMatch (*NfTablesRule)(nil), // 36: talos.resource.definitions.network.NfTablesRule (*NodeAddressFilterSpec)(nil), // 37: talos.resource.definitions.network.NodeAddressFilterSpec (*NodeAddressSortAlgorithmSpec)(nil), // 38: talos.resource.definitions.network.NodeAddressSortAlgorithmSpec (*NodeAddressSpec)(nil), // 39: talos.resource.definitions.network.NodeAddressSpec (*OperatorSpecSpec)(nil), // 40: talos.resource.definitions.network.OperatorSpecSpec (*PlatformConfigSpec)(nil), // 41: talos.resource.definitions.network.PlatformConfigSpec (*PortRange)(nil), // 42: talos.resource.definitions.network.PortRange (*ProbeSpecSpec)(nil), // 43: talos.resource.definitions.network.ProbeSpecSpec (*ProbeStatusSpec)(nil), // 44: talos.resource.definitions.network.ProbeStatusSpec (*ResolverSpecSpec)(nil), // 45: talos.resource.definitions.network.ResolverSpecSpec (*ResolverStatusSpec)(nil), // 46: talos.resource.definitions.network.ResolverStatusSpec (*RouteSpecSpec)(nil), // 47: talos.resource.definitions.network.RouteSpecSpec (*RouteStatusSpec)(nil), // 48: talos.resource.definitions.network.RouteStatusSpec (*RoutingRuleSpecSpec)(nil), // 49: talos.resource.definitions.network.RoutingRuleSpecSpec (*RoutingRuleStatusSpec)(nil), // 50: talos.resource.definitions.network.RoutingRuleStatusSpec (*STPSpec)(nil), // 51: talos.resource.definitions.network.STPSpec (*StatusSpec)(nil), // 52: talos.resource.definitions.network.StatusSpec (*TCPProbeSpec)(nil), // 53: talos.resource.definitions.network.TCPProbeSpec (*TimeServerSpecSpec)(nil), // 54: talos.resource.definitions.network.TimeServerSpecSpec (*TimeServerStatusSpec)(nil), // 55: talos.resource.definitions.network.TimeServerStatusSpec (*VIPEquinixMetalSpec)(nil), // 56: talos.resource.definitions.network.VIPEquinixMetalSpec (*VIPHCloudSpec)(nil), // 57: talos.resource.definitions.network.VIPHCloudSpec (*VIPOperatorSpec)(nil), // 58: talos.resource.definitions.network.VIPOperatorSpec (*VLANSpec)(nil), // 59: talos.resource.definitions.network.VLANSpec (*VRFMasterSpec)(nil), // 60: talos.resource.definitions.network.VRFMasterSpec (*VRFSlave)(nil), // 61: talos.resource.definitions.network.VRFSlave (*WireguardPeer)(nil), // 62: talos.resource.definitions.network.WireguardPeer (*WireguardSpec)(nil), // 63: talos.resource.definitions.network.WireguardSpec nil, // 64: talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntry (*common.NetIPPrefix)(nil), // 65: common.NetIPPrefix (enums.NethelpersFamily)(0), // 66: talos.resource.definitions.enums.NethelpersFamily (enums.NethelpersScope)(0), // 67: talos.resource.definitions.enums.NethelpersScope (enums.NetworkConfigLayer)(0), // 68: talos.resource.definitions.enums.NetworkConfigLayer (*common.NetIP)(nil), // 69: common.NetIP (enums.NethelpersBondMode)(0), // 70: talos.resource.definitions.enums.NethelpersBondMode (enums.NethelpersBondXmitHashPolicy)(0), // 71: talos.resource.definitions.enums.NethelpersBondXmitHashPolicy (enums.NethelpersLACPRate)(0), // 72: talos.resource.definitions.enums.NethelpersLACPRate (enums.NethelpersARPValidate)(0), // 73: talos.resource.definitions.enums.NethelpersARPValidate (enums.NethelpersARPAllTargets)(0), // 74: talos.resource.definitions.enums.NethelpersARPAllTargets (enums.NethelpersPrimaryReselect)(0), // 75: talos.resource.definitions.enums.NethelpersPrimaryReselect (enums.NethelpersFailOverMAC)(0), // 76: talos.resource.definitions.enums.NethelpersFailOverMAC (enums.NethelpersADSelect)(0), // 77: talos.resource.definitions.enums.NethelpersADSelect (enums.NethelpersADLACPActive)(0), // 78: talos.resource.definitions.enums.NethelpersADLACPActive (enums.NethelpersClientIdentifier)(0), // 79: talos.resource.definitions.enums.NethelpersClientIdentifier (enums.NethelpersWOLMode)(0), // 80: talos.resource.definitions.enums.NethelpersWOLMode (enums.NethelpersPort)(0), // 81: talos.resource.definitions.enums.NethelpersPort (enums.NethelpersDuplex)(0), // 82: talos.resource.definitions.enums.NethelpersDuplex (*common.NetIPPort)(nil), // 83: common.NetIPPort (enums.NethelpersLinkType)(0), // 84: talos.resource.definitions.enums.NethelpersLinkType (enums.NethelpersOperationalState)(0), // 85: talos.resource.definitions.enums.NethelpersOperationalState (enums.NethelpersNfTablesChainHook)(0), // 86: talos.resource.definitions.enums.NethelpersNfTablesChainHook (enums.NethelpersNfTablesChainPriority)(0), // 87: talos.resource.definitions.enums.NethelpersNfTablesChainPriority (enums.NethelpersNfTablesVerdict)(0), // 88: talos.resource.definitions.enums.NethelpersNfTablesVerdict (enums.NethelpersConntrackState)(0), // 89: talos.resource.definitions.enums.NethelpersConntrackState (enums.NethelpersICMPType)(0), // 90: talos.resource.definitions.enums.NethelpersICMPType (enums.NethelpersMatchOperator)(0), // 91: talos.resource.definitions.enums.NethelpersMatchOperator (enums.NethelpersProtocol)(0), // 92: talos.resource.definitions.enums.NethelpersProtocol (enums.NethelpersAddressSortAlgorithm)(0), // 93: talos.resource.definitions.enums.NethelpersAddressSortAlgorithm (enums.NetworkOperator)(0), // 94: talos.resource.definitions.enums.NetworkOperator (*runtime.PlatformMetadataSpec)(nil), // 95: talos.resource.definitions.runtime.PlatformMetadataSpec (*durationpb.Duration)(nil), // 96: google.protobuf.Duration (enums.NethelpersRoutingTable)(0), // 97: talos.resource.definitions.enums.NethelpersRoutingTable (enums.NethelpersRouteType)(0), // 98: talos.resource.definitions.enums.NethelpersRouteType (enums.NethelpersRouteProtocol)(0), // 99: talos.resource.definitions.enums.NethelpersRouteProtocol (enums.NethelpersRoutingRuleAction)(0), // 100: talos.resource.definitions.enums.NethelpersRoutingRuleAction (enums.NethelpersVLANProtocol)(0), // 101: talos.resource.definitions.enums.NethelpersVLANProtocol } var file_resource_definitions_network_network_proto_depIdxs = []int32{ 65, // 0: talos.resource.definitions.network.AddressSpecSpec.address:type_name -> common.NetIPPrefix 66, // 1: talos.resource.definitions.network.AddressSpecSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 67, // 2: talos.resource.definitions.network.AddressSpecSpec.scope:type_name -> talos.resource.definitions.enums.NethelpersScope 68, // 3: talos.resource.definitions.network.AddressSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 65, // 4: talos.resource.definitions.network.AddressStatusSpec.address:type_name -> common.NetIPPrefix 69, // 5: talos.resource.definitions.network.AddressStatusSpec.local:type_name -> common.NetIP 69, // 6: talos.resource.definitions.network.AddressStatusSpec.broadcast:type_name -> common.NetIP 69, // 7: talos.resource.definitions.network.AddressStatusSpec.anycast:type_name -> common.NetIP 69, // 8: talos.resource.definitions.network.AddressStatusSpec.multicast:type_name -> common.NetIP 66, // 9: talos.resource.definitions.network.AddressStatusSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 67, // 10: talos.resource.definitions.network.AddressStatusSpec.scope:type_name -> talos.resource.definitions.enums.NethelpersScope 70, // 11: talos.resource.definitions.network.BondMasterSpec.mode:type_name -> talos.resource.definitions.enums.NethelpersBondMode 71, // 12: talos.resource.definitions.network.BondMasterSpec.hash_policy:type_name -> talos.resource.definitions.enums.NethelpersBondXmitHashPolicy 72, // 13: talos.resource.definitions.network.BondMasterSpec.lacp_rate:type_name -> talos.resource.definitions.enums.NethelpersLACPRate 73, // 14: talos.resource.definitions.network.BondMasterSpec.arp_validate:type_name -> talos.resource.definitions.enums.NethelpersARPValidate 74, // 15: talos.resource.definitions.network.BondMasterSpec.arp_all_targets:type_name -> talos.resource.definitions.enums.NethelpersARPAllTargets 75, // 16: talos.resource.definitions.network.BondMasterSpec.primary_reselect:type_name -> talos.resource.definitions.enums.NethelpersPrimaryReselect 76, // 17: talos.resource.definitions.network.BondMasterSpec.fail_over_mac:type_name -> talos.resource.definitions.enums.NethelpersFailOverMAC 77, // 18: talos.resource.definitions.network.BondMasterSpec.ad_select:type_name -> talos.resource.definitions.enums.NethelpersADSelect 69, // 19: talos.resource.definitions.network.BondMasterSpec.arpip_targets:type_name -> common.NetIP 69, // 20: talos.resource.definitions.network.BondMasterSpec.nsip6_targets:type_name -> common.NetIP 78, // 21: talos.resource.definitions.network.BondMasterSpec.adlacp_active:type_name -> talos.resource.definitions.enums.NethelpersADLACPActive 51, // 22: talos.resource.definitions.network.BridgeMasterSpec.stp:type_name -> talos.resource.definitions.network.STPSpec 6, // 23: talos.resource.definitions.network.BridgeMasterSpec.vlan:type_name -> talos.resource.definitions.network.BridgeVLANSpec 79, // 24: talos.resource.definitions.network.ClientIdentifierSpec.client_identifier:type_name -> talos.resource.definitions.enums.NethelpersClientIdentifier 7, // 25: talos.resource.definitions.network.DHCP4OperatorSpec.client_identifier:type_name -> talos.resource.definitions.network.ClientIdentifierSpec 7, // 26: talos.resource.definitions.network.DHCP6OperatorSpec.client_identifier:type_name -> talos.resource.definitions.network.ClientIdentifierSpec 14, // 27: talos.resource.definitions.network.EthernetSpecSpec.rings:type_name -> talos.resource.definitions.network.EthernetRingsSpec 64, // 28: talos.resource.definitions.network.EthernetSpecSpec.features:type_name -> talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntry 11, // 29: talos.resource.definitions.network.EthernetSpecSpec.channels:type_name -> talos.resource.definitions.network.EthernetChannelsSpec 80, // 30: talos.resource.definitions.network.EthernetSpecSpec.wake_on_lan:type_name -> talos.resource.definitions.enums.NethelpersWOLMode 81, // 31: talos.resource.definitions.network.EthernetStatusSpec.port:type_name -> talos.resource.definitions.enums.NethelpersPort 82, // 32: talos.resource.definitions.network.EthernetStatusSpec.duplex:type_name -> talos.resource.definitions.enums.NethelpersDuplex 15, // 33: talos.resource.definitions.network.EthernetStatusSpec.rings:type_name -> talos.resource.definitions.network.EthernetRingsStatus 13, // 34: talos.resource.definitions.network.EthernetStatusSpec.features:type_name -> talos.resource.definitions.network.EthernetFeatureStatus 12, // 35: talos.resource.definitions.network.EthernetStatusSpec.channels:type_name -> talos.resource.definitions.network.EthernetChannelsStatus 80, // 36: talos.resource.definitions.network.EthernetStatusSpec.wake_on_lan:type_name -> talos.resource.definitions.enums.NethelpersWOLMode 83, // 37: talos.resource.definitions.network.HostDNSConfigSpec.listen_addresses:type_name -> common.NetIPPort 69, // 38: talos.resource.definitions.network.HostDNSConfigSpec.service_host_dns_address:type_name -> common.NetIP 68, // 39: talos.resource.definitions.network.HostnameSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 84, // 40: talos.resource.definitions.network.LinkSpecSpec.type:type_name -> talos.resource.definitions.enums.NethelpersLinkType 3, // 41: talos.resource.definitions.network.LinkSpecSpec.bond_slave:type_name -> talos.resource.definitions.network.BondSlave 5, // 42: talos.resource.definitions.network.LinkSpecSpec.bridge_slave:type_name -> talos.resource.definitions.network.BridgeSlave 59, // 43: talos.resource.definitions.network.LinkSpecSpec.vlan:type_name -> talos.resource.definitions.network.VLANSpec 2, // 44: talos.resource.definitions.network.LinkSpecSpec.bond_master:type_name -> talos.resource.definitions.network.BondMasterSpec 4, // 45: talos.resource.definitions.network.LinkSpecSpec.bridge_master:type_name -> talos.resource.definitions.network.BridgeMasterSpec 63, // 46: talos.resource.definitions.network.LinkSpecSpec.wireguard:type_name -> talos.resource.definitions.network.WireguardSpec 68, // 47: talos.resource.definitions.network.LinkSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 60, // 48: talos.resource.definitions.network.LinkSpecSpec.vrf_master:type_name -> talos.resource.definitions.network.VRFMasterSpec 61, // 49: talos.resource.definitions.network.LinkSpecSpec.vrf_slave:type_name -> talos.resource.definitions.network.VRFSlave 84, // 50: talos.resource.definitions.network.LinkStatusSpec.type:type_name -> talos.resource.definitions.enums.NethelpersLinkType 85, // 51: talos.resource.definitions.network.LinkStatusSpec.operational_state:type_name -> talos.resource.definitions.enums.NethelpersOperationalState 81, // 52: talos.resource.definitions.network.LinkStatusSpec.port:type_name -> talos.resource.definitions.enums.NethelpersPort 82, // 53: talos.resource.definitions.network.LinkStatusSpec.duplex:type_name -> talos.resource.definitions.enums.NethelpersDuplex 59, // 54: talos.resource.definitions.network.LinkStatusSpec.vlan:type_name -> talos.resource.definitions.network.VLANSpec 4, // 55: talos.resource.definitions.network.LinkStatusSpec.bridge_master:type_name -> talos.resource.definitions.network.BridgeMasterSpec 2, // 56: talos.resource.definitions.network.LinkStatusSpec.bond_master:type_name -> talos.resource.definitions.network.BondMasterSpec 63, // 57: talos.resource.definitions.network.LinkStatusSpec.wireguard:type_name -> talos.resource.definitions.network.WireguardSpec 60, // 58: talos.resource.definitions.network.LinkStatusSpec.vrf_master:type_name -> talos.resource.definitions.network.VRFMasterSpec 65, // 59: talos.resource.definitions.network.NfTablesAddressMatch.include_subnets:type_name -> common.NetIPPrefix 65, // 60: talos.resource.definitions.network.NfTablesAddressMatch.exclude_subnets:type_name -> common.NetIPPrefix 86, // 61: talos.resource.definitions.network.NfTablesChainSpec.hook:type_name -> talos.resource.definitions.enums.NethelpersNfTablesChainHook 87, // 62: talos.resource.definitions.network.NfTablesChainSpec.priority:type_name -> talos.resource.definitions.enums.NethelpersNfTablesChainPriority 36, // 63: talos.resource.definitions.network.NfTablesChainSpec.rules:type_name -> talos.resource.definitions.network.NfTablesRule 88, // 64: talos.resource.definitions.network.NfTablesChainSpec.policy:type_name -> talos.resource.definitions.enums.NethelpersNfTablesVerdict 89, // 65: talos.resource.definitions.network.NfTablesConntrackStateMatch.states:type_name -> talos.resource.definitions.enums.NethelpersConntrackState 90, // 66: talos.resource.definitions.network.NfTablesICMPTypeMatch.types:type_name -> talos.resource.definitions.enums.NethelpersICMPType 91, // 67: talos.resource.definitions.network.NfTablesIfNameMatch.operator:type_name -> talos.resource.definitions.enums.NethelpersMatchOperator 92, // 68: talos.resource.definitions.network.NfTablesLayer4Match.protocol:type_name -> talos.resource.definitions.enums.NethelpersProtocol 35, // 69: talos.resource.definitions.network.NfTablesLayer4Match.match_source_port:type_name -> talos.resource.definitions.network.NfTablesPortMatch 35, // 70: talos.resource.definitions.network.NfTablesLayer4Match.match_destination_port:type_name -> talos.resource.definitions.network.NfTablesPortMatch 30, // 71: talos.resource.definitions.network.NfTablesLayer4Match.match_icmp_type:type_name -> talos.resource.definitions.network.NfTablesICMPTypeMatch 42, // 72: talos.resource.definitions.network.NfTablesPortMatch.ranges:type_name -> talos.resource.definitions.network.PortRange 31, // 73: talos.resource.definitions.network.NfTablesRule.match_o_if_name:type_name -> talos.resource.definitions.network.NfTablesIfNameMatch 88, // 74: talos.resource.definitions.network.NfTablesRule.verdict:type_name -> talos.resource.definitions.enums.NethelpersNfTablesVerdict 34, // 75: talos.resource.definitions.network.NfTablesRule.match_mark:type_name -> talos.resource.definitions.network.NfTablesMark 34, // 76: talos.resource.definitions.network.NfTablesRule.set_mark:type_name -> talos.resource.definitions.network.NfTablesMark 26, // 77: talos.resource.definitions.network.NfTablesRule.match_source_address:type_name -> talos.resource.definitions.network.NfTablesAddressMatch 26, // 78: talos.resource.definitions.network.NfTablesRule.match_destination_address:type_name -> talos.resource.definitions.network.NfTablesAddressMatch 32, // 79: talos.resource.definitions.network.NfTablesRule.match_layer4:type_name -> talos.resource.definitions.network.NfTablesLayer4Match 31, // 80: talos.resource.definitions.network.NfTablesRule.match_i_if_name:type_name -> talos.resource.definitions.network.NfTablesIfNameMatch 28, // 81: talos.resource.definitions.network.NfTablesRule.clamp_mss:type_name -> talos.resource.definitions.network.NfTablesClampMSS 33, // 82: talos.resource.definitions.network.NfTablesRule.match_limit:type_name -> talos.resource.definitions.network.NfTablesLimitMatch 29, // 83: talos.resource.definitions.network.NfTablesRule.match_conntrack_state:type_name -> talos.resource.definitions.network.NfTablesConntrackStateMatch 65, // 84: talos.resource.definitions.network.NodeAddressFilterSpec.include_subnets:type_name -> common.NetIPPrefix 65, // 85: talos.resource.definitions.network.NodeAddressFilterSpec.exclude_subnets:type_name -> common.NetIPPrefix 93, // 86: talos.resource.definitions.network.NodeAddressSortAlgorithmSpec.algorithm:type_name -> talos.resource.definitions.enums.NethelpersAddressSortAlgorithm 65, // 87: talos.resource.definitions.network.NodeAddressSpec.addresses:type_name -> common.NetIPPrefix 93, // 88: talos.resource.definitions.network.NodeAddressSpec.sort_algorithm:type_name -> talos.resource.definitions.enums.NethelpersAddressSortAlgorithm 94, // 89: talos.resource.definitions.network.OperatorSpecSpec.operator:type_name -> talos.resource.definitions.enums.NetworkOperator 8, // 90: talos.resource.definitions.network.OperatorSpecSpec.dhcp4:type_name -> talos.resource.definitions.network.DHCP4OperatorSpec 9, // 91: talos.resource.definitions.network.OperatorSpecSpec.dhcp6:type_name -> talos.resource.definitions.network.DHCP6OperatorSpec 58, // 92: talos.resource.definitions.network.OperatorSpecSpec.vip:type_name -> talos.resource.definitions.network.VIPOperatorSpec 68, // 93: talos.resource.definitions.network.OperatorSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 0, // 94: talos.resource.definitions.network.PlatformConfigSpec.addresses:type_name -> talos.resource.definitions.network.AddressSpecSpec 24, // 95: talos.resource.definitions.network.PlatformConfigSpec.links:type_name -> talos.resource.definitions.network.LinkSpecSpec 47, // 96: talos.resource.definitions.network.PlatformConfigSpec.routes:type_name -> talos.resource.definitions.network.RouteSpecSpec 20, // 97: talos.resource.definitions.network.PlatformConfigSpec.hostnames:type_name -> talos.resource.definitions.network.HostnameSpecSpec 45, // 98: talos.resource.definitions.network.PlatformConfigSpec.resolvers:type_name -> talos.resource.definitions.network.ResolverSpecSpec 54, // 99: talos.resource.definitions.network.PlatformConfigSpec.time_servers:type_name -> talos.resource.definitions.network.TimeServerSpecSpec 40, // 100: talos.resource.definitions.network.PlatformConfigSpec.operators:type_name -> talos.resource.definitions.network.OperatorSpecSpec 69, // 101: talos.resource.definitions.network.PlatformConfigSpec.external_ips:type_name -> common.NetIP 43, // 102: talos.resource.definitions.network.PlatformConfigSpec.probes:type_name -> talos.resource.definitions.network.ProbeSpecSpec 95, // 103: talos.resource.definitions.network.PlatformConfigSpec.metadata:type_name -> talos.resource.definitions.runtime.PlatformMetadataSpec 96, // 104: talos.resource.definitions.network.ProbeSpecSpec.interval:type_name -> google.protobuf.Duration 53, // 105: talos.resource.definitions.network.ProbeSpecSpec.tcp:type_name -> talos.resource.definitions.network.TCPProbeSpec 68, // 106: talos.resource.definitions.network.ProbeSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 69, // 107: talos.resource.definitions.network.ResolverSpecSpec.dns_servers:type_name -> common.NetIP 68, // 108: talos.resource.definitions.network.ResolverSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 69, // 109: talos.resource.definitions.network.ResolverStatusSpec.dns_servers:type_name -> common.NetIP 66, // 110: talos.resource.definitions.network.RouteSpecSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 65, // 111: talos.resource.definitions.network.RouteSpecSpec.destination:type_name -> common.NetIPPrefix 69, // 112: talos.resource.definitions.network.RouteSpecSpec.source:type_name -> common.NetIP 69, // 113: talos.resource.definitions.network.RouteSpecSpec.gateway:type_name -> common.NetIP 97, // 114: talos.resource.definitions.network.RouteSpecSpec.table:type_name -> talos.resource.definitions.enums.NethelpersRoutingTable 67, // 115: talos.resource.definitions.network.RouteSpecSpec.scope:type_name -> talos.resource.definitions.enums.NethelpersScope 98, // 116: talos.resource.definitions.network.RouteSpecSpec.type:type_name -> talos.resource.definitions.enums.NethelpersRouteType 99, // 117: talos.resource.definitions.network.RouteSpecSpec.protocol:type_name -> talos.resource.definitions.enums.NethelpersRouteProtocol 68, // 118: talos.resource.definitions.network.RouteSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 66, // 119: talos.resource.definitions.network.RouteStatusSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 65, // 120: talos.resource.definitions.network.RouteStatusSpec.destination:type_name -> common.NetIPPrefix 69, // 121: talos.resource.definitions.network.RouteStatusSpec.source:type_name -> common.NetIP 69, // 122: talos.resource.definitions.network.RouteStatusSpec.gateway:type_name -> common.NetIP 97, // 123: talos.resource.definitions.network.RouteStatusSpec.table:type_name -> talos.resource.definitions.enums.NethelpersRoutingTable 67, // 124: talos.resource.definitions.network.RouteStatusSpec.scope:type_name -> talos.resource.definitions.enums.NethelpersScope 98, // 125: talos.resource.definitions.network.RouteStatusSpec.type:type_name -> talos.resource.definitions.enums.NethelpersRouteType 99, // 126: talos.resource.definitions.network.RouteStatusSpec.protocol:type_name -> talos.resource.definitions.enums.NethelpersRouteProtocol 66, // 127: talos.resource.definitions.network.RoutingRuleSpecSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 65, // 128: talos.resource.definitions.network.RoutingRuleSpecSpec.src:type_name -> common.NetIPPrefix 65, // 129: talos.resource.definitions.network.RoutingRuleSpecSpec.dst:type_name -> common.NetIPPrefix 97, // 130: talos.resource.definitions.network.RoutingRuleSpecSpec.table:type_name -> talos.resource.definitions.enums.NethelpersRoutingTable 100, // 131: talos.resource.definitions.network.RoutingRuleSpecSpec.action:type_name -> talos.resource.definitions.enums.NethelpersRoutingRuleAction 68, // 132: talos.resource.definitions.network.RoutingRuleSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 66, // 133: talos.resource.definitions.network.RoutingRuleStatusSpec.family:type_name -> talos.resource.definitions.enums.NethelpersFamily 65, // 134: talos.resource.definitions.network.RoutingRuleStatusSpec.src:type_name -> common.NetIPPrefix 65, // 135: talos.resource.definitions.network.RoutingRuleStatusSpec.dst:type_name -> common.NetIPPrefix 97, // 136: talos.resource.definitions.network.RoutingRuleStatusSpec.table:type_name -> talos.resource.definitions.enums.NethelpersRoutingTable 100, // 137: talos.resource.definitions.network.RoutingRuleStatusSpec.action:type_name -> talos.resource.definitions.enums.NethelpersRoutingRuleAction 96, // 138: talos.resource.definitions.network.TCPProbeSpec.timeout:type_name -> google.protobuf.Duration 68, // 139: talos.resource.definitions.network.TimeServerSpecSpec.config_layer:type_name -> talos.resource.definitions.enums.NetworkConfigLayer 69, // 140: talos.resource.definitions.network.VIPOperatorSpec.ip:type_name -> common.NetIP 56, // 141: talos.resource.definitions.network.VIPOperatorSpec.equinix_metal:type_name -> talos.resource.definitions.network.VIPEquinixMetalSpec 57, // 142: talos.resource.definitions.network.VIPOperatorSpec.h_cloud:type_name -> talos.resource.definitions.network.VIPHCloudSpec 101, // 143: talos.resource.definitions.network.VLANSpec.protocol:type_name -> talos.resource.definitions.enums.NethelpersVLANProtocol 97, // 144: talos.resource.definitions.network.VRFMasterSpec.table:type_name -> talos.resource.definitions.enums.NethelpersRoutingTable 96, // 145: talos.resource.definitions.network.WireguardPeer.persistent_keepalive_interval:type_name -> google.protobuf.Duration 65, // 146: talos.resource.definitions.network.WireguardPeer.allowed_ips:type_name -> common.NetIPPrefix 62, // 147: talos.resource.definitions.network.WireguardSpec.peers:type_name -> talos.resource.definitions.network.WireguardPeer 148, // [148:148] is the sub-list for method output_type 148, // [148:148] is the sub-list for method input_type 148, // [148:148] is the sub-list for extension type_name 148, // [148:148] is the sub-list for extension extendee 0, // [0:148] is the sub-list for field type_name } func init() { file_resource_definitions_network_network_proto_init() } func file_resource_definitions_network_network_proto_init() { if File_resource_definitions_network_network_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_network_network_proto_rawDesc), len(file_resource_definitions_network_network_proto_rawDesc)), NumEnums: 0, NumMessages: 65, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_network_network_proto_goTypes, DependencyIndexes: file_resource_definitions_network_network_proto_depIdxs, MessageInfos: file_resource_definitions_network_network_proto_msgTypes, }.Build() File_resource_definitions_network_network_proto = out.File file_resource_definitions_network_network_proto_goTypes = nil file_resource_definitions_network_network_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/network/network_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/network/network.proto package network import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb1 "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" runtime "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/runtime" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *AddressSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddressSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AddressSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x40 } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x38 } if m.AnnounceWithArp { i-- if m.AnnounceWithArp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if m.Flags != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Flags)) i-- dAtA[i] = 0x28 } if m.Scope != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Scope)) i-- dAtA[i] = 0x20 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x18 } if len(m.LinkName) > 0 { i -= len(m.LinkName) copy(dAtA[i:], m.LinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LinkName))) i-- dAtA[i] = 0x12 } if m.Address != nil { if vtmsg, ok := interface{}(m.Address).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Address) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddressStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AddressStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AddressStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x58 } if m.Flags != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Flags)) i-- dAtA[i] = 0x50 } if m.Scope != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Scope)) i-- dAtA[i] = 0x48 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x40 } if len(m.LinkName) > 0 { i -= len(m.LinkName) copy(dAtA[i:], m.LinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LinkName))) i-- dAtA[i] = 0x3a } if m.LinkIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LinkIndex)) i-- dAtA[i] = 0x30 } if m.Multicast != nil { if vtmsg, ok := interface{}(m.Multicast).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Multicast) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } if m.Anycast != nil { if vtmsg, ok := interface{}(m.Anycast).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Anycast) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } if m.Broadcast != nil { if vtmsg, ok := interface{}(m.Broadcast).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Broadcast) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Local != nil { if vtmsg, ok := interface{}(m.Local).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Local) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Address != nil { if vtmsg, ok := interface{}(m.Address).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Address) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BondMasterSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BondMasterSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BondMasterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MissedMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MissedMax)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe0 } if m.AdlacpActive != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AdlacpActive)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd8 } if len(m.Nsip6Targets) > 0 { for iNdEx := len(m.Nsip6Targets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Nsip6Targets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Nsip6Targets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd2 } } if len(m.ArpipTargets) > 0 { for iNdEx := len(m.ArpipTargets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ArpipTargets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ArpipTargets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xca } } if m.PeerNotifyDelay != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PeerNotifyDelay)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc0 } if m.AdUserPortKey != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AdUserPortKey)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb8 } if m.AdActorSysPrio != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AdActorSysPrio)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb0 } if m.UseCarrier { i-- if m.UseCarrier { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa8 } if m.AllSlavesActive != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AllSlavesActive)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa0 } if m.TlbDynamicLb != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TlbDynamicLb)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x98 } if m.NumPeerNotif != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NumPeerNotif)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if m.PacketsPerSlave != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PacketsPerSlave)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x88 } if m.LpInterval != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LpInterval)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.MinLinks != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MinLinks)) i-- dAtA[i] = 0x78 } if m.ResendIgmp != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ResendIgmp)) i-- dAtA[i] = 0x70 } if m.ArpInterval != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ArpInterval)) i-- dAtA[i] = 0x68 } if m.DownDelay != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DownDelay)) i-- dAtA[i] = 0x60 } if m.UpDelay != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.UpDelay)) i-- dAtA[i] = 0x58 } if m.MiiMon != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MiiMon)) i-- dAtA[i] = 0x50 } if m.AdSelect != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AdSelect)) i-- dAtA[i] = 0x48 } if m.FailOverMac != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FailOverMac)) i-- dAtA[i] = 0x40 } if m.PrimaryReselect != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PrimaryReselect)) i-- dAtA[i] = 0x38 } if m.PrimaryIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PrimaryIndex)) i-- dAtA[i] = 0x30 } if m.ArpAllTargets != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ArpAllTargets)) i-- dAtA[i] = 0x28 } if m.ArpValidate != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ArpValidate)) i-- dAtA[i] = 0x20 } if m.LacpRate != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LacpRate)) i-- dAtA[i] = 0x18 } if m.HashPolicy != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HashPolicy)) i-- dAtA[i] = 0x10 } if m.Mode != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mode)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *BondSlave) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BondSlave) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BondSlave) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SlaveIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SlaveIndex)) i-- dAtA[i] = 0x10 } if len(m.MasterName) > 0 { i -= len(m.MasterName) copy(dAtA[i:], m.MasterName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MasterName))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BridgeMasterSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BridgeMasterSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BridgeMasterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Vlan != nil { size, err := m.Vlan.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Stp != nil { size, err := m.Stp.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BridgeSlave) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BridgeSlave) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BridgeSlave) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MasterName) > 0 { i -= len(m.MasterName) copy(dAtA[i:], m.MasterName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MasterName))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BridgeVLANSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BridgeVLANSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BridgeVLANSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.FilteringEnabled { i-- if m.FilteringEnabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ClientIdentifierSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ClientIdentifierSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ClientIdentifierSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.DuidRawHex) > 0 { i -= len(m.DuidRawHex) copy(dAtA[i:], m.DuidRawHex) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DuidRawHex))) i-- dAtA[i] = 0x12 } if m.ClientIdentifier != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ClientIdentifier)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DHCP4OperatorSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DHCP4OperatorSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DHCP4OperatorSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClientIdentifier != nil { size, err := m.ClientIdentifier.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.SkipHostnameRequest { i-- if m.SkipHostnameRequest { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.RouteMetric != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RouteMetric)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DHCP6OperatorSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DHCP6OperatorSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DHCP6OperatorSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ClientIdentifier != nil { size, err := m.ClientIdentifier.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.SkipHostnameRequest { i-- if m.SkipHostnameRequest { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.RouteMetric != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RouteMetric)) i-- dAtA[i] = 0x10 } return len(dAtA) - i, nil } func (m *DNSResolveCacheSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DNSResolveCacheSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DNSResolveCacheSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Status) > 0 { i -= len(m.Status) copy(dAtA[i:], m.Status) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Status))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EthernetChannelsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetChannelsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetChannelsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Combined != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Combined)) i-- dAtA[i] = 0x20 } if m.Other != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Other)) i-- dAtA[i] = 0x18 } if m.Tx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tx)) i-- dAtA[i] = 0x10 } if m.Rx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rx)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EthernetChannelsStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetChannelsStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetChannelsStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Combined != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Combined)) i-- dAtA[i] = 0x40 } if m.Other != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Other)) i-- dAtA[i] = 0x38 } if m.Tx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tx)) i-- dAtA[i] = 0x30 } if m.Rx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rx)) i-- dAtA[i] = 0x28 } if m.CombinedMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CombinedMax)) i-- dAtA[i] = 0x20 } if m.OtherMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OtherMax)) i-- dAtA[i] = 0x18 } if m.TxMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxMax)) i-- dAtA[i] = 0x10 } if m.RxMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMax)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EthernetFeatureStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetFeatureStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetFeatureStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Status) > 0 { i -= len(m.Status) copy(dAtA[i:], m.Status) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Status))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EthernetRingsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetRingsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetRingsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TcpDataSplit { i-- if m.TcpDataSplit { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x50 } if m.TxPushBufLen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxPushBufLen)) i-- dAtA[i] = 0x48 } if m.RxPush { i-- if m.RxPush { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if m.TxPush { i-- if m.TxPush { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.CqeSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CqeSize)) i-- dAtA[i] = 0x30 } if m.RxBufLen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxBufLen)) i-- dAtA[i] = 0x28 } if m.Tx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tx)) i-- dAtA[i] = 0x20 } if m.RxJumbo != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxJumbo)) i-- dAtA[i] = 0x18 } if m.RxMini != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMini)) i-- dAtA[i] = 0x10 } if m.Rx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rx)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EthernetRingsStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetRingsStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetRingsStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.TcpDataSplit { i-- if m.TcpDataSplit { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x78 } if m.TxPushBufLen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxPushBufLen)) i-- dAtA[i] = 0x70 } if m.RxPush { i-- if m.RxPush { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x68 } if m.TxPush { i-- if m.TxPush { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x60 } if m.CqeSize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CqeSize)) i-- dAtA[i] = 0x58 } if m.RxBufLen != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxBufLen)) i-- dAtA[i] = 0x50 } if m.Tx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Tx)) i-- dAtA[i] = 0x48 } if m.RxJumbo != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxJumbo)) i-- dAtA[i] = 0x40 } if m.RxMini != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMini)) i-- dAtA[i] = 0x38 } if m.Rx != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Rx)) i-- dAtA[i] = 0x30 } if m.TxPushBufLenMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxPushBufLenMax)) i-- dAtA[i] = 0x28 } if m.TxMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.TxMax)) i-- dAtA[i] = 0x20 } if m.RxJumboMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxJumboMax)) i-- dAtA[i] = 0x18 } if m.RxMiniMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMiniMax)) i-- dAtA[i] = 0x10 } if m.RxMax != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.RxMax)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *EthernetSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.WakeOnLan) > 0 { var pksize2 int for _, num := range m.WakeOnLan { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.WakeOnLan { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x22 } if m.Channels != nil { size, err := m.Channels.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if len(m.Features) > 0 { for k := range m.Features { v := m.Features[k] baseI := i i-- if v { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x12 } } if m.Rings != nil { size, err := m.Rings.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EthernetStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EthernetStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EthernetStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.WakeOnLan) > 0 { var pksize2 int for _, num := range m.WakeOnLan { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.WakeOnLan { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0x52 } if m.Channels != nil { size, err := m.Channels.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x4a } if len(m.Features) > 0 { for iNdEx := len(m.Features) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Features[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } } if m.Rings != nil { size, err := m.Rings.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } if len(m.PeerModes) > 0 { for iNdEx := len(m.PeerModes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PeerModes[iNdEx]) copy(dAtA[i:], m.PeerModes[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PeerModes[iNdEx]))) i-- dAtA[i] = 0x32 } } if len(m.OurModes) > 0 { for iNdEx := len(m.OurModes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.OurModes[iNdEx]) copy(dAtA[i:], m.OurModes[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OurModes[iNdEx]))) i-- dAtA[i] = 0x2a } } if m.Duplex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Duplex)) i-- dAtA[i] = 0x20 } if m.Port != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Port)) i-- dAtA[i] = 0x18 } if m.SpeedMegabits != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SpeedMegabits)) i-- dAtA[i] = 0x10 } if m.LinkState { i-- if m.LinkState { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *HardwareAddrSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HardwareAddrSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HardwareAddrSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.HardwareAddr) > 0 { i -= len(m.HardwareAddr) copy(dAtA[i:], m.HardwareAddr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HardwareAddr))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *HostDNSConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HostDNSConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HostDNSConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ResolveMemberNames { i-- if m.ResolveMemberNames { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ServiceHostDnsAddress != nil { if vtmsg, ok := interface{}(m.ServiceHostDnsAddress).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ServiceHostDnsAddress) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if len(m.ListenAddresses) > 0 { for iNdEx := len(m.ListenAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ListenAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ListenAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *HostnameSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HostnameSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HostnameSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x18 } if len(m.Domainname) > 0 { i -= len(m.Domainname) copy(dAtA[i:], m.Domainname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Domainname))) i-- dAtA[i] = 0x12 } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *HostnameStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *HostnameStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *HostnameStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Domainname) > 0 { i -= len(m.Domainname) copy(dAtA[i:], m.Domainname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Domainname))) i-- dAtA[i] = 0x12 } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LinkAliasSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LinkAliasSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LinkAliasSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Alias) > 0 { i -= len(m.Alias) copy(dAtA[i:], m.Alias) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Alias))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LinkRefreshSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LinkRefreshSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LinkRefreshSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Generation != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Generation)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *LinkSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LinkSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LinkSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.VrfSlave != nil { size, err := m.VrfSlave.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if m.VrfMaster != nil { size, err := m.VrfMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if m.Multicast { i-- if m.Multicast { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if len(m.HardwareAddress) > 0 { i -= len(m.HardwareAddress) copy(dAtA[i:], m.HardwareAddress) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HardwareAddress))) i-- dAtA[i] = 0x7a } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x70 } if m.Wireguard != nil { size, err := m.Wireguard.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x6a } if m.BridgeMaster != nil { size, err := m.BridgeMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x62 } if m.BondMaster != nil { size, err := m.BondMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x5a } if m.Vlan != nil { size, err := m.Vlan.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x52 } if m.BridgeSlave != nil { size, err := m.BridgeSlave.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x4a } if m.BondSlave != nil { size, err := m.BondSlave.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } if len(m.ParentName) > 0 { i -= len(m.ParentName) copy(dAtA[i:], m.ParentName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ParentName))) i-- dAtA[i] = 0x3a } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x30 } if len(m.Kind) > 0 { i -= len(m.Kind) copy(dAtA[i:], m.Kind) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Kind))) i-- dAtA[i] = 0x2a } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x20 } if m.Up { i-- if m.Up { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Logical { i-- if m.Logical { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LinkStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LinkStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LinkStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.VrfMaster != nil { size, err := m.VrfMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x8a } if len(m.AltNames) > 0 { for iNdEx := len(m.AltNames) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.AltNames[iNdEx]) copy(dAtA[i:], m.AltNames[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AltNames[iNdEx]))) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x82 } } if len(m.Alias) > 0 { i -= len(m.Alias) copy(dAtA[i:], m.Alias) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Alias))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xfa } if len(m.PermanentAddr) > 0 { i -= len(m.PermanentAddr) copy(dAtA[i:], m.PermanentAddr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PermanentAddr))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xf2 } if m.Wireguard != nil { size, err := m.Wireguard.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xea } if m.BondMaster != nil { size, err := m.BondMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe2 } if m.BridgeMaster != nil { size, err := m.BridgeMaster.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xda } if m.Vlan != nil { size, err := m.Vlan.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd2 } if m.Duplex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Duplex)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc8 } if m.Port != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Port)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc0 } if m.SpeedMegabits != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SpeedMegabits)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb8 } if m.LinkState { i-- if m.LinkState { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb0 } if len(m.Vendor) > 0 { i -= len(m.Vendor) copy(dAtA[i:], m.Vendor) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Vendor))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xaa } if len(m.Product) > 0 { i -= len(m.Product) copy(dAtA[i:], m.Product) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Product))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa2 } if len(m.VendorId) > 0 { i -= len(m.VendorId) copy(dAtA[i:], m.VendorId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.VendorId))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x9a } if len(m.ProductId) > 0 { i -= len(m.ProductId) copy(dAtA[i:], m.ProductId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProductId))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x92 } if len(m.FirmwareVersion) > 0 { i -= len(m.FirmwareVersion) copy(dAtA[i:], m.FirmwareVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FirmwareVersion))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x8a } if len(m.DriverVersion) > 0 { i -= len(m.DriverVersion) copy(dAtA[i:], m.DriverVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DriverVersion))) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x82 } if len(m.Driver) > 0 { i -= len(m.Driver) copy(dAtA[i:], m.Driver) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Driver))) i-- dAtA[i] = 0x7a } if len(m.Pciid) > 0 { i -= len(m.Pciid) copy(dAtA[i:], m.Pciid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Pciid))) i-- dAtA[i] = 0x72 } if len(m.BusPath) > 0 { i -= len(m.BusPath) copy(dAtA[i:], m.BusPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BusPath))) i-- dAtA[i] = 0x6a } if len(m.SlaveKind) > 0 { i -= len(m.SlaveKind) copy(dAtA[i:], m.SlaveKind) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SlaveKind))) i-- dAtA[i] = 0x62 } if len(m.Kind) > 0 { i -= len(m.Kind) copy(dAtA[i:], m.Kind) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Kind))) i-- dAtA[i] = 0x5a } if m.OperationalState != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OperationalState)) i-- dAtA[i] = 0x50 } if m.MasterIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MasterIndex)) i-- dAtA[i] = 0x48 } if len(m.QueueDisc) > 0 { i -= len(m.QueueDisc) copy(dAtA[i:], m.QueueDisc) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.QueueDisc))) i-- dAtA[i] = 0x42 } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x38 } if len(m.BroadcastAddr) > 0 { i -= len(m.BroadcastAddr) copy(dAtA[i:], m.BroadcastAddr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BroadcastAddr))) i-- dAtA[i] = 0x32 } if len(m.HardwareAddr) > 0 { i -= len(m.HardwareAddr) copy(dAtA[i:], m.HardwareAddr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.HardwareAddr))) i-- dAtA[i] = 0x2a } if m.Flags != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Flags)) i-- dAtA[i] = 0x20 } if m.LinkIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.LinkIndex)) i-- dAtA[i] = 0x18 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x10 } if m.Index != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Index)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NfTablesAddressMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesAddressMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesAddressMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Invert { i-- if m.Invert { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.ExcludeSubnets) > 0 { for iNdEx := len(m.ExcludeSubnets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExcludeSubnets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExcludeSubnets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.IncludeSubnets) > 0 { for iNdEx := len(m.IncludeSubnets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.IncludeSubnets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.IncludeSubnets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NfTablesChainSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesChainSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesChainSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Policy != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Policy)) i-- dAtA[i] = 0x28 } if len(m.Rules) > 0 { for iNdEx := len(m.Rules) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Rules[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x18 } if m.Hook != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hook)) i-- dAtA[i] = 0x10 } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NfTablesClampMSS) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesClampMSS) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesClampMSS) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NfTablesConntrackStateMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesConntrackStateMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesConntrackStateMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.States) > 0 { var pksize2 int for _, num := range m.States { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.States { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NfTablesICMPTypeMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesICMPTypeMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesICMPTypeMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Types) > 0 { var pksize2 int for _, num := range m.Types { pksize2 += protohelpers.SizeOfVarint(uint64(num)) } i -= pksize2 j1 := i for _, num1 := range m.Types { num := uint64(num1) for num >= 1<<7 { dAtA[j1] = uint8(uint64(num)&0x7f | 0x80) num >>= 7 j1++ } dAtA[j1] = uint8(num) j1++ } i = protohelpers.EncodeVarint(dAtA, i, uint64(pksize2)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NfTablesIfNameMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesIfNameMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesIfNameMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.InterfaceNames) > 0 { for iNdEx := len(m.InterfaceNames) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.InterfaceNames[iNdEx]) copy(dAtA[i:], m.InterfaceNames[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InterfaceNames[iNdEx]))) i-- dAtA[i] = 0x1a } } if m.Operator != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Operator)) i-- dAtA[i] = 0x10 } return len(dAtA) - i, nil } func (m *NfTablesLayer4Match) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesLayer4Match) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesLayer4Match) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.MatchIcmpType != nil { size, err := m.MatchIcmpType.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.MatchDestinationPort != nil { size, err := m.MatchDestinationPort.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.MatchSourcePort != nil { size, err := m.MatchSourcePort.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Protocol != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Protocol)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NfTablesLimitMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesLimitMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesLimitMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.PacketRatePerSecond != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PacketRatePerSecond)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NfTablesMark) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesMark) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesMark) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Value != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Value)) i-- dAtA[i] = 0x18 } if m.Xor != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Xor)) i-- dAtA[i] = 0x10 } if m.Mask != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mask)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NfTablesPortMatch) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesPortMatch) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesPortMatch) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Ranges) > 0 { for iNdEx := len(m.Ranges) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Ranges[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NfTablesRule) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NfTablesRule) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NfTablesRule) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.AnonCounter { i-- if m.AnonCounter { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x60 } if m.MatchConntrackState != nil { size, err := m.MatchConntrackState.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x5a } if m.MatchLimit != nil { size, err := m.MatchLimit.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x52 } if m.ClampMss != nil { size, err := m.ClampMss.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x4a } if m.MatchIIfName != nil { size, err := m.MatchIIfName.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x42 } if m.MatchLayer4 != nil { size, err := m.MatchLayer4.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } if m.MatchDestinationAddress != nil { size, err := m.MatchDestinationAddress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.MatchSourceAddress != nil { size, err := m.MatchSourceAddress.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if m.SetMark != nil { size, err := m.SetMark.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.MatchMark != nil { size, err := m.MatchMark.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Verdict != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Verdict)) i-- dAtA[i] = 0x10 } if m.MatchOIfName != nil { size, err := m.MatchOIfName.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *NodeAddressFilterSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeAddressFilterSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeAddressFilterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ExcludeSubnets) > 0 { for iNdEx := len(m.ExcludeSubnets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExcludeSubnets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExcludeSubnets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.IncludeSubnets) > 0 { for iNdEx := len(m.IncludeSubnets) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.IncludeSubnets[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.IncludeSubnets[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *NodeAddressSortAlgorithmSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeAddressSortAlgorithmSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeAddressSortAlgorithmSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Algorithm != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Algorithm)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *NodeAddressSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *NodeAddressSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *NodeAddressSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SortAlgorithm != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SortAlgorithm)) i-- dAtA[i] = 0x10 } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Addresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Addresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *OperatorSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *OperatorSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *OperatorSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x38 } if m.Vip != nil { size, err := m.Vip.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.Dhcp6 != nil { size, err := m.Dhcp6.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if m.Dhcp4 != nil { size, err := m.Dhcp4.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.RequireUp { i-- if m.RequireUp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.LinkName) > 0 { i -= len(m.LinkName) copy(dAtA[i:], m.LinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LinkName))) i-- dAtA[i] = 0x12 } if m.Operator != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Operator)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *PlatformConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PlatformConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PlatformConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x52 } if len(m.Probes) > 0 { for iNdEx := len(m.Probes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Probes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x4a } } if len(m.ExternalIps) > 0 { for iNdEx := len(m.ExternalIps) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ExternalIps[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ExternalIps[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x42 } } if len(m.Operators) > 0 { for iNdEx := len(m.Operators) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Operators[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x3a } } if len(m.TimeServers) > 0 { for iNdEx := len(m.TimeServers) - 1; iNdEx >= 0; iNdEx-- { size, err := m.TimeServers[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } } if len(m.Resolvers) > 0 { for iNdEx := len(m.Resolvers) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Resolvers[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } } if len(m.Hostnames) > 0 { for iNdEx := len(m.Hostnames) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Hostnames[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } } if len(m.Routes) > 0 { for iNdEx := len(m.Routes) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Routes[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } } if len(m.Links) > 0 { for iNdEx := len(m.Links) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Links[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if len(m.Addresses) > 0 { for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Addresses[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *PortRange) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PortRange) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PortRange) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Hi != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hi)) i-- dAtA[i] = 0x10 } if m.Lo != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Lo)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ProbeSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProbeSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProbeSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x20 } if m.Tcp != nil { size, err := m.Tcp.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.FailureThreshold != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FailureThreshold)) i-- dAtA[i] = 0x10 } if m.Interval != nil { size, err := (*durationpb.Duration)(m.Interval).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ProbeStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ProbeStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ProbeStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.LastError) > 0 { i -= len(m.LastError) copy(dAtA[i:], m.LastError) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LastError))) i-- dAtA[i] = 0x12 } if m.Success { i-- if m.Success { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ResolverSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResolverSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ResolverSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SearchDomains) > 0 { for iNdEx := len(m.SearchDomains) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SearchDomains[iNdEx]) copy(dAtA[i:], m.SearchDomains[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SearchDomains[iNdEx]))) i-- dAtA[i] = 0x1a } } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x10 } if len(m.DnsServers) > 0 { for iNdEx := len(m.DnsServers) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.DnsServers[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.DnsServers[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ResolverStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ResolverStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ResolverStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SearchDomains) > 0 { for iNdEx := len(m.SearchDomains) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.SearchDomains[iNdEx]) copy(dAtA[i:], m.SearchDomains[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SearchDomains[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.DnsServers) > 0 { for iNdEx := len(m.DnsServers) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.DnsServers[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.DnsServers[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *RouteSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RouteSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RouteSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x68 } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x60 } if m.Protocol != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Protocol)) i-- dAtA[i] = 0x58 } if m.Flags != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Flags)) i-- dAtA[i] = 0x50 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x48 } if m.Scope != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Scope)) i-- dAtA[i] = 0x40 } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x38 } if m.Table != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Table)) i-- dAtA[i] = 0x30 } if len(m.OutLinkName) > 0 { i -= len(m.OutLinkName) copy(dAtA[i:], m.OutLinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OutLinkName))) i-- dAtA[i] = 0x2a } if m.Gateway != nil { if vtmsg, ok := interface{}(m.Gateway).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Gateway) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } if m.Source != nil { if vtmsg, ok := interface{}(m.Source).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Source) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Destination != nil { if vtmsg, ok := interface{}(m.Destination).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Destination) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RouteStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RouteStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RouteStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x68 } if m.Protocol != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Protocol)) i-- dAtA[i] = 0x60 } if m.Flags != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Flags)) i-- dAtA[i] = 0x58 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x50 } if m.Scope != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Scope)) i-- dAtA[i] = 0x48 } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x40 } if m.Table != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Table)) i-- dAtA[i] = 0x38 } if len(m.OutLinkName) > 0 { i -= len(m.OutLinkName) copy(dAtA[i:], m.OutLinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OutLinkName))) i-- dAtA[i] = 0x32 } if m.OutLinkIndex != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.OutLinkIndex)) i-- dAtA[i] = 0x28 } if m.Gateway != nil { if vtmsg, ok := interface{}(m.Gateway).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Gateway) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } if m.Source != nil { if vtmsg, ok := interface{}(m.Source).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Source) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Destination != nil { if vtmsg, ok := interface{}(m.Destination).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Destination) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RoutingRuleSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RoutingRuleSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RoutingRuleSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x58 } if m.FwMask != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FwMask)) i-- dAtA[i] = 0x50 } if m.FwMark != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FwMark)) i-- dAtA[i] = 0x48 } if len(m.OifName) > 0 { i -= len(m.OifName) copy(dAtA[i:], m.OifName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OifName))) i-- dAtA[i] = 0x42 } if len(m.IifName) > 0 { i -= len(m.IifName) copy(dAtA[i:], m.IifName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.IifName))) i-- dAtA[i] = 0x3a } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x30 } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x28 } if m.Table != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Table)) i-- dAtA[i] = 0x20 } if m.Dst != nil { if vtmsg, ok := interface{}(m.Dst).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Dst) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Src != nil { if vtmsg, ok := interface{}(m.Src).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Src) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *RoutingRuleStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *RoutingRuleStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *RoutingRuleStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.FwMask != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FwMask)) i-- dAtA[i] = 0x50 } if m.FwMark != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FwMark)) i-- dAtA[i] = 0x48 } if len(m.OifName) > 0 { i -= len(m.OifName) copy(dAtA[i:], m.OifName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.OifName))) i-- dAtA[i] = 0x42 } if len(m.IifName) > 0 { i -= len(m.IifName) copy(dAtA[i:], m.IifName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.IifName))) i-- dAtA[i] = 0x3a } if m.Action != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Action)) i-- dAtA[i] = 0x30 } if m.Priority != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Priority)) i-- dAtA[i] = 0x28 } if m.Table != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Table)) i-- dAtA[i] = 0x20 } if m.Dst != nil { if vtmsg, ok := interface{}(m.Dst).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Dst) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Src != nil { if vtmsg, ok := interface{}(m.Src).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Src) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Family != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Family)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *STPSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *STPSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *STPSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Enabled { i-- if m.Enabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *StatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.EtcFilesReady { i-- if m.EtcFilesReady { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.HostnameReady { i-- if m.HostnameReady { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.ConnectivityReady { i-- if m.ConnectivityReady { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.AddressReady { i-- if m.AddressReady { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *TCPProbeSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TCPProbeSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TCPProbeSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Timeout != nil { size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Endpoint) > 0 { i -= len(m.Endpoint) copy(dAtA[i:], m.Endpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Endpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TimeServerSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TimeServerSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TimeServerSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ConfigLayer != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ConfigLayer)) i-- dAtA[i] = 0x10 } if len(m.NtpServers) > 0 { for iNdEx := len(m.NtpServers) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.NtpServers[iNdEx]) copy(dAtA[i:], m.NtpServers[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NtpServers[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *TimeServerStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TimeServerStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TimeServerStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.NtpServers) > 0 { for iNdEx := len(m.NtpServers) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.NtpServers[iNdEx]) copy(dAtA[i:], m.NtpServers[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.NtpServers[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *VIPEquinixMetalSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VIPEquinixMetalSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VIPEquinixMetalSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ApiToken) > 0 { i -= len(m.ApiToken) copy(dAtA[i:], m.ApiToken) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ApiToken))) i-- dAtA[i] = 0x1a } if len(m.DeviceId) > 0 { i -= len(m.DeviceId) copy(dAtA[i:], m.DeviceId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DeviceId))) i-- dAtA[i] = 0x12 } if len(m.ProjectId) > 0 { i -= len(m.ProjectId) copy(dAtA[i:], m.ProjectId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProjectId))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VIPHCloudSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VIPHCloudSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VIPHCloudSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ApiToken) > 0 { i -= len(m.ApiToken) copy(dAtA[i:], m.ApiToken) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ApiToken))) i-- dAtA[i] = 0x1a } if m.NetworkId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NetworkId)) i-- dAtA[i] = 0x10 } if m.DeviceId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DeviceId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *VIPOperatorSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VIPOperatorSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VIPOperatorSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.HCloud != nil { size, err := m.HCloud.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.EquinixMetal != nil { size, err := m.EquinixMetal.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.GratuitousArp { i-- if m.GratuitousArp { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Ip != nil { if vtmsg, ok := interface{}(m.Ip).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Ip) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *VLANSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VLANSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VLANSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Protocol != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Protocol)) i-- dAtA[i] = 0x10 } if m.Vid != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Vid)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *VRFMasterSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VRFMasterSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VRFMasterSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Table != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Table)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *VRFSlave) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *VRFSlave) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *VRFSlave) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MasterName) > 0 { i -= len(m.MasterName) copy(dAtA[i:], m.MasterName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MasterName))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WireguardPeer) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WireguardPeer) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *WireguardPeer) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AllowedIps) > 0 { for iNdEx := len(m.AllowedIps) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AllowedIps[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AllowedIps[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if m.PersistentKeepaliveInterval != nil { size, err := (*durationpb.Duration)(m.PersistentKeepaliveInterval).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if len(m.Endpoint) > 0 { i -= len(m.Endpoint) copy(dAtA[i:], m.Endpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Endpoint))) i-- dAtA[i] = 0x1a } if len(m.PresharedKey) > 0 { i -= len(m.PresharedKey) copy(dAtA[i:], m.PresharedKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PresharedKey))) i-- dAtA[i] = 0x12 } if len(m.PublicKey) > 0 { i -= len(m.PublicKey) copy(dAtA[i:], m.PublicKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WireguardSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WireguardSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *WireguardSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Peers) > 0 { for iNdEx := len(m.Peers) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Peers[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } } if m.FirewallMark != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FirewallMark)) i-- dAtA[i] = 0x20 } if m.ListenPort != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ListenPort)) i-- dAtA[i] = 0x18 } if len(m.PublicKey) > 0 { i -= len(m.PublicKey) copy(dAtA[i:], m.PublicKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PublicKey))) i-- dAtA[i] = 0x12 } if len(m.PrivateKey) > 0 { i -= len(m.PrivateKey) copy(dAtA[i:], m.PrivateKey) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PrivateKey))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *AddressSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Address != nil { if size, ok := interface{}(m.Address).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Address) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.LinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Scope != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Scope)) } if m.Flags != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Flags)) } if m.AnnounceWithArp { n += 2 } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } n += len(m.unknownFields) return n } func (m *AddressStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Address != nil { if size, ok := interface{}(m.Address).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Address) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Local != nil { if size, ok := interface{}(m.Local).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Local) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Broadcast != nil { if size, ok := interface{}(m.Broadcast).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Broadcast) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Anycast != nil { if size, ok := interface{}(m.Anycast).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Anycast) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Multicast != nil { if size, ok := interface{}(m.Multicast).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Multicast) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LinkIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.LinkIndex)) } l = len(m.LinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Scope != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Scope)) } if m.Flags != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Flags)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } n += len(m.unknownFields) return n } func (m *BondMasterSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Mode != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mode)) } if m.HashPolicy != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.HashPolicy)) } if m.LacpRate != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.LacpRate)) } if m.ArpValidate != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ArpValidate)) } if m.ArpAllTargets != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ArpAllTargets)) } if m.PrimaryIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PrimaryIndex)) } if m.PrimaryReselect != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PrimaryReselect)) } if m.FailOverMac != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FailOverMac)) } if m.AdSelect != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.AdSelect)) } if m.MiiMon != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MiiMon)) } if m.UpDelay != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.UpDelay)) } if m.DownDelay != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DownDelay)) } if m.ArpInterval != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ArpInterval)) } if m.ResendIgmp != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ResendIgmp)) } if m.MinLinks != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MinLinks)) } if m.LpInterval != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.LpInterval)) } if m.PacketsPerSlave != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.PacketsPerSlave)) } if m.NumPeerNotif != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.NumPeerNotif)) } if m.TlbDynamicLb != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.TlbDynamicLb)) } if m.AllSlavesActive != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AllSlavesActive)) } if m.UseCarrier { n += 3 } if m.AdActorSysPrio != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AdActorSysPrio)) } if m.AdUserPortKey != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AdUserPortKey)) } if m.PeerNotifyDelay != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.PeerNotifyDelay)) } if len(m.ArpipTargets) > 0 { for _, e := range m.ArpipTargets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Nsip6Targets) > 0 { for _, e := range m.Nsip6Targets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.AdlacpActive != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AdlacpActive)) } if m.MissedMax != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.MissedMax)) } n += len(m.unknownFields) return n } func (m *BondSlave) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.MasterName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SlaveIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SlaveIndex)) } n += len(m.unknownFields) return n } func (m *BridgeMasterSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Stp != nil { l = m.Stp.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Vlan != nil { l = m.Vlan.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *BridgeSlave) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.MasterName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *BridgeVLANSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.FilteringEnabled { n += 2 } n += len(m.unknownFields) return n } func (m *ClientIdentifierSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.ClientIdentifier != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ClientIdentifier)) } l = len(m.DuidRawHex) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DHCP4OperatorSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RouteMetric != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RouteMetric)) } if m.SkipHostnameRequest { n += 2 } if m.ClientIdentifier != nil { l = m.ClientIdentifier.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DHCP6OperatorSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RouteMetric != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RouteMetric)) } if m.SkipHostnameRequest { n += 2 } if m.ClientIdentifier != nil { l = m.ClientIdentifier.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DNSResolveCacheSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Status) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EthernetChannelsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Rx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rx)) } if m.Tx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tx)) } if m.Other != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Other)) } if m.Combined != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Combined)) } n += len(m.unknownFields) return n } func (m *EthernetChannelsStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RxMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMax)) } if m.TxMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxMax)) } if m.OtherMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.OtherMax)) } if m.CombinedMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CombinedMax)) } if m.Rx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rx)) } if m.Tx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tx)) } if m.Other != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Other)) } if m.Combined != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Combined)) } n += len(m.unknownFields) return n } func (m *EthernetFeatureStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Status) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EthernetRingsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Rx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rx)) } if m.RxMini != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMini)) } if m.RxJumbo != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxJumbo)) } if m.Tx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tx)) } if m.RxBufLen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxBufLen)) } if m.CqeSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CqeSize)) } if m.TxPush { n += 2 } if m.RxPush { n += 2 } if m.TxPushBufLen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxPushBufLen)) } if m.TcpDataSplit { n += 2 } n += len(m.unknownFields) return n } func (m *EthernetRingsStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.RxMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMax)) } if m.RxMiniMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMiniMax)) } if m.RxJumboMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxJumboMax)) } if m.TxMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxMax)) } if m.TxPushBufLenMax != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxPushBufLenMax)) } if m.Rx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Rx)) } if m.RxMini != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxMini)) } if m.RxJumbo != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxJumbo)) } if m.Tx != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Tx)) } if m.RxBufLen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.RxBufLen)) } if m.CqeSize != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.CqeSize)) } if m.TxPush { n += 2 } if m.RxPush { n += 2 } if m.TxPushBufLen != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.TxPushBufLen)) } if m.TcpDataSplit { n += 2 } n += len(m.unknownFields) return n } func (m *EthernetSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Rings != nil { l = m.Rings.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Features) > 0 { for k, v := range m.Features { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + 1 n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } if m.Channels != nil { l = m.Channels.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.WakeOnLan) > 0 { l = 0 for _, e := range m.WakeOnLan { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *EthernetStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.LinkState { n += 2 } if m.SpeedMegabits != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SpeedMegabits)) } if m.Port != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Port)) } if m.Duplex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Duplex)) } if len(m.OurModes) > 0 { for _, s := range m.OurModes { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.PeerModes) > 0 { for _, s := range m.PeerModes { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Rings != nil { l = m.Rings.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Features) > 0 { for _, e := range m.Features { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Channels != nil { l = m.Channels.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.WakeOnLan) > 0 { l = 0 for _, e := range m.WakeOnLan { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *HardwareAddrSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.HardwareAddr) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *HostDNSConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } if len(m.ListenAddresses) > 0 { for _, e := range m.ListenAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ServiceHostDnsAddress != nil { if size, ok := interface{}(m.ServiceHostDnsAddress).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.ServiceHostDnsAddress) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ResolveMemberNames { n += 2 } n += len(m.unknownFields) return n } func (m *HostnameSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Domainname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } n += len(m.unknownFields) return n } func (m *HostnameStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Domainname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LinkAliasSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Alias) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LinkRefreshSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Generation != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Generation)) } n += len(m.unknownFields) return n } func (m *LinkSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Logical { n += 2 } if m.Up { n += 2 } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } l = len(m.Kind) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } l = len(m.ParentName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BondSlave != nil { l = m.BondSlave.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BridgeSlave != nil { l = m.BridgeSlave.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Vlan != nil { l = m.Vlan.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BondMaster != nil { l = m.BondMaster.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BridgeMaster != nil { l = m.BridgeMaster.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Wireguard != nil { l = m.Wireguard.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } l = len(m.HardwareAddress) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Multicast { n += 3 } if m.VrfMaster != nil { l = m.VrfMaster.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.VrfSlave != nil { l = m.VrfSlave.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *LinkStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Index != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Index)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } if m.LinkIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.LinkIndex)) } if m.Flags != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Flags)) } l = len(m.HardwareAddr) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BroadcastAddr) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } l = len(m.QueueDisc) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MasterIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MasterIndex)) } if m.OperationalState != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.OperationalState)) } l = len(m.Kind) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SlaveKind) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BusPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Pciid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Driver) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DriverVersion) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.FirmwareVersion) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProductId) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.VendorId) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Product) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Vendor) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LinkState { n += 3 } if m.SpeedMegabits != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.SpeedMegabits)) } if m.Port != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Port)) } if m.Duplex != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Duplex)) } if m.Vlan != nil { l = m.Vlan.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BridgeMaster != nil { l = m.BridgeMaster.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.BondMaster != nil { l = m.BondMaster.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Wireguard != nil { l = m.Wireguard.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PermanentAddr) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Alias) if l > 0 { n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AltNames) > 0 { for _, s := range m.AltNames { l = len(s) n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.VrfMaster != nil { l = m.VrfMaster.SizeVT() n += 2 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NfTablesAddressMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.IncludeSubnets) > 0 { for _, e := range m.IncludeSubnets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExcludeSubnets) > 0 { for _, e := range m.ExcludeSubnets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Invert { n += 2 } n += len(m.unknownFields) return n } func (m *NfTablesChainSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Hook != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Hook)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } if len(m.Rules) > 0 { for _, e := range m.Rules { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Policy != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Policy)) } n += len(m.unknownFields) return n } func (m *NfTablesClampMSS) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } n += len(m.unknownFields) return n } func (m *NfTablesConntrackStateMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.States) > 0 { l = 0 for _, e := range m.States { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *NfTablesICMPTypeMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Types) > 0 { l = 0 for _, e := range m.Types { l += protohelpers.SizeOfVarint(uint64(e)) } n += 1 + protohelpers.SizeOfVarint(uint64(l)) + l } n += len(m.unknownFields) return n } func (m *NfTablesIfNameMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Operator != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Operator)) } if len(m.InterfaceNames) > 0 { for _, s := range m.InterfaceNames { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NfTablesLayer4Match) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Protocol != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Protocol)) } if m.MatchSourcePort != nil { l = m.MatchSourcePort.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchDestinationPort != nil { l = m.MatchDestinationPort.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchIcmpType != nil { l = m.MatchIcmpType.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *NfTablesLimitMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.PacketRatePerSecond != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.PacketRatePerSecond)) } n += len(m.unknownFields) return n } func (m *NfTablesMark) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Mask != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mask)) } if m.Xor != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Xor)) } if m.Value != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Value)) } n += len(m.unknownFields) return n } func (m *NfTablesPortMatch) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Ranges) > 0 { for _, e := range m.Ranges { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NfTablesRule) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MatchOIfName != nil { l = m.MatchOIfName.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Verdict != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Verdict)) } if m.MatchMark != nil { l = m.MatchMark.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SetMark != nil { l = m.SetMark.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchSourceAddress != nil { l = m.MatchSourceAddress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchDestinationAddress != nil { l = m.MatchDestinationAddress.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchLayer4 != nil { l = m.MatchLayer4.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchIIfName != nil { l = m.MatchIIfName.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ClampMss != nil { l = m.ClampMss.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchLimit != nil { l = m.MatchLimit.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.MatchConntrackState != nil { l = m.MatchConntrackState.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.AnonCounter { n += 2 } n += len(m.unknownFields) return n } func (m *NodeAddressFilterSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.IncludeSubnets) > 0 { for _, e := range m.IncludeSubnets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExcludeSubnets) > 0 { for _, e := range m.ExcludeSubnets { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *NodeAddressSortAlgorithmSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Algorithm != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Algorithm)) } n += len(m.unknownFields) return n } func (m *NodeAddressSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Addresses) > 0 { for _, e := range m.Addresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.SortAlgorithm != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SortAlgorithm)) } n += len(m.unknownFields) return n } func (m *OperatorSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Operator != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Operator)) } l = len(m.LinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.RequireUp { n += 2 } if m.Dhcp4 != nil { l = m.Dhcp4.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Dhcp6 != nil { l = m.Dhcp6.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Vip != nil { l = m.Vip.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } n += len(m.unknownFields) return n } func (m *PlatformConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Addresses) > 0 { for _, e := range m.Addresses { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Links) > 0 { for _, e := range m.Links { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Routes) > 0 { for _, e := range m.Routes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Hostnames) > 0 { for _, e := range m.Hostnames { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Resolvers) > 0 { for _, e := range m.Resolvers { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.TimeServers) > 0 { for _, e := range m.TimeServers { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Operators) > 0 { for _, e := range m.Operators { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.ExternalIps) > 0 { for _, e := range m.ExternalIps { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Probes) > 0 { for _, e := range m.Probes { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *PortRange) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Lo != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Lo)) } if m.Hi != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Hi)) } n += len(m.unknownFields) return n } func (m *ProbeSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Interval != nil { l = (*durationpb.Duration)(m.Interval).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FailureThreshold != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FailureThreshold)) } if m.Tcp != nil { l = m.Tcp.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } n += len(m.unknownFields) return n } func (m *ProbeStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Success { n += 2 } l = len(m.LastError) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ResolverSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.DnsServers) > 0 { for _, e := range m.DnsServers { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } if len(m.SearchDomains) > 0 { for _, s := range m.SearchDomains { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ResolverStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.DnsServers) > 0 { for _, e := range m.DnsServers { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.SearchDomains) > 0 { for _, s := range m.SearchDomains { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *RouteSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Destination != nil { if size, ok := interface{}(m.Destination).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Destination) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Source != nil { if size, ok := interface{}(m.Source).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Source) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Gateway != nil { if size, ok := interface{}(m.Gateway).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Gateway) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.OutLinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Table != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Table)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } if m.Scope != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Scope)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } if m.Flags != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Flags)) } if m.Protocol != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Protocol)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } n += len(m.unknownFields) return n } func (m *RouteStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Destination != nil { if size, ok := interface{}(m.Destination).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Destination) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Source != nil { if size, ok := interface{}(m.Source).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Source) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Gateway != nil { if size, ok := interface{}(m.Gateway).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Gateway) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.OutLinkIndex != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.OutLinkIndex)) } l = len(m.OutLinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Table != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Table)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } if m.Scope != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Scope)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } if m.Flags != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Flags)) } if m.Protocol != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Protocol)) } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } n += len(m.unknownFields) return n } func (m *RoutingRuleSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Src != nil { if size, ok := interface{}(m.Src).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Src) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Dst != nil { if size, ok := interface{}(m.Dst).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Dst) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Table != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Table)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } l = len(m.IifName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.OifName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FwMark != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FwMark)) } if m.FwMask != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FwMask)) } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } n += len(m.unknownFields) return n } func (m *RoutingRuleStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Family != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Family)) } if m.Src != nil { if size, ok := interface{}(m.Src).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Src) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Dst != nil { if size, ok := interface{}(m.Dst).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Dst) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Table != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Table)) } if m.Priority != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Priority)) } if m.Action != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Action)) } l = len(m.IifName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.OifName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FwMark != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FwMark)) } if m.FwMask != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FwMask)) } n += len(m.unknownFields) return n } func (m *STPSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Enabled { n += 2 } n += len(m.unknownFields) return n } func (m *StatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.AddressReady { n += 2 } if m.ConnectivityReady { n += 2 } if m.HostnameReady { n += 2 } if m.EtcFilesReady { n += 2 } n += len(m.unknownFields) return n } func (m *TCPProbeSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Endpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Timeout != nil { l = (*durationpb.Duration)(m.Timeout).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *TimeServerSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.NtpServers) > 0 { for _, s := range m.NtpServers { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.ConfigLayer != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ConfigLayer)) } n += len(m.unknownFields) return n } func (m *TimeServerStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.NtpServers) > 0 { for _, s := range m.NtpServers { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *VIPEquinixMetalSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ProjectId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DeviceId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ApiToken) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *VIPHCloudSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.DeviceId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.DeviceId)) } if m.NetworkId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.NetworkId)) } l = len(m.ApiToken) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *VIPOperatorSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ip != nil { if size, ok := interface{}(m.Ip).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Ip) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.GratuitousArp { n += 2 } if m.EquinixMetal != nil { l = m.EquinixMetal.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.HCloud != nil { l = m.HCloud.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *VLANSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Vid != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Vid)) } if m.Protocol != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Protocol)) } n += len(m.unknownFields) return n } func (m *VRFMasterSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Table != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Table)) } n += len(m.unknownFields) return n } func (m *VRFSlave) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.MasterName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *WireguardPeer) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.PublicKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PresharedKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Endpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PersistentKeepaliveInterval != nil { l = (*durationpb.Duration)(m.PersistentKeepaliveInterval).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AllowedIps) > 0 { for _, e := range m.AllowedIps { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *WireguardSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.PrivateKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PublicKey) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ListenPort != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ListenPort)) } if m.FirewallMark != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FirewallMark)) } if len(m.Peers) > 0 { for _, e := range m.Peers { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *AddressSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddressSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddressSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Address == nil { m.Address = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Address).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Address); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Scope", wireType) } m.Scope = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Scope |= enums.NethelpersScope(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } m.Flags = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Flags |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AnnounceWithArp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AnnounceWithArp = bool(v != 0) case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *AddressStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AddressStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AddressStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Address == nil { m.Address = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Address).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Address); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Local", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Local == nil { m.Local = &common.NetIP{} } if unmarshal, ok := interface{}(m.Local).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Local); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Broadcast", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Broadcast == nil { m.Broadcast = &common.NetIP{} } if unmarshal, ok := interface{}(m.Broadcast).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Broadcast); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Anycast", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Anycast == nil { m.Anycast = &common.NetIP{} } if unmarshal, ok := interface{}(m.Anycast).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Anycast); err != nil { return err } } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Multicast", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Multicast == nil { m.Multicast = &common.NetIP{} } if unmarshal, ok := interface{}(m.Multicast).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Multicast); err != nil { return err } } iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LinkIndex", wireType) } m.LinkIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LinkIndex |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Scope", wireType) } m.Scope = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Scope |= enums.NethelpersScope(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } m.Flags = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Flags |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BondMasterSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BondMasterSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BondMasterSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mode", wireType) } m.Mode = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mode |= enums.NethelpersBondMode(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HashPolicy", wireType) } m.HashPolicy = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HashPolicy |= enums.NethelpersBondXmitHashPolicy(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LacpRate", wireType) } m.LacpRate = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LacpRate |= enums.NethelpersLACPRate(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ArpValidate", wireType) } m.ArpValidate = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ArpValidate |= enums.NethelpersARPValidate(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ArpAllTargets", wireType) } m.ArpAllTargets = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ArpAllTargets |= enums.NethelpersARPAllTargets(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PrimaryIndex", wireType) } m.PrimaryIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PrimaryIndex |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PrimaryReselect", wireType) } m.PrimaryReselect = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PrimaryReselect |= enums.NethelpersPrimaryReselect(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FailOverMac", wireType) } m.FailOverMac = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FailOverMac |= enums.NethelpersFailOverMAC(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AdSelect", wireType) } m.AdSelect = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AdSelect |= enums.NethelpersADSelect(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MiiMon", wireType) } m.MiiMon = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MiiMon |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field UpDelay", wireType) } m.UpDelay = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.UpDelay |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DownDelay", wireType) } m.DownDelay = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DownDelay |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ArpInterval", wireType) } m.ArpInterval = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ArpInterval |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ResendIgmp", wireType) } m.ResendIgmp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ResendIgmp |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MinLinks", wireType) } m.MinLinks = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MinLinks |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LpInterval", wireType) } m.LpInterval = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LpInterval |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PacketsPerSlave", wireType) } m.PacketsPerSlave = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PacketsPerSlave |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NumPeerNotif", wireType) } m.NumPeerNotif = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NumPeerNotif |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 19: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TlbDynamicLb", wireType) } m.TlbDynamicLb = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TlbDynamicLb |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 20: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AllSlavesActive", wireType) } m.AllSlavesActive = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AllSlavesActive |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 21: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field UseCarrier", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.UseCarrier = bool(v != 0) case 22: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AdActorSysPrio", wireType) } m.AdActorSysPrio = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AdActorSysPrio |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 23: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AdUserPortKey", wireType) } m.AdUserPortKey = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AdUserPortKey |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 24: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PeerNotifyDelay", wireType) } m.PeerNotifyDelay = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PeerNotifyDelay |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 25: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ArpipTargets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ArpipTargets = append(m.ArpipTargets, &common.NetIP{}) if unmarshal, ok := interface{}(m.ArpipTargets[len(m.ArpipTargets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ArpipTargets[len(m.ArpipTargets)-1]); err != nil { return err } } iNdEx = postIndex case 26: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Nsip6Targets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Nsip6Targets = append(m.Nsip6Targets, &common.NetIP{}) if unmarshal, ok := interface{}(m.Nsip6Targets[len(m.Nsip6Targets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Nsip6Targets[len(m.Nsip6Targets)-1]); err != nil { return err } } iNdEx = postIndex case 27: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AdlacpActive", wireType) } m.AdlacpActive = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AdlacpActive |= enums.NethelpersADLACPActive(b&0x7F) << shift if b < 0x80 { break } } case 28: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MissedMax", wireType) } m.MissedMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MissedMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BondSlave) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BondSlave: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BondSlave: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MasterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MasterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SlaveIndex", wireType) } m.SlaveIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SlaveIndex |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BridgeMasterSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BridgeMasterSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BridgeMasterSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stp", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Stp == nil { m.Stp = &STPSpec{} } if err := m.Stp.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vlan", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Vlan == nil { m.Vlan = &BridgeVLANSpec{} } if err := m.Vlan.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BridgeSlave) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BridgeSlave: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BridgeSlave: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MasterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MasterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BridgeVLANSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BridgeVLANSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BridgeVLANSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FilteringEnabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.FilteringEnabled = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ClientIdentifierSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ClientIdentifierSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ClientIdentifierSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ClientIdentifier", wireType) } m.ClientIdentifier = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ClientIdentifier |= enums.NethelpersClientIdentifier(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DuidRawHex", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DuidRawHex = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DHCP4OperatorSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DHCP4OperatorSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DHCP4OperatorSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RouteMetric", wireType) } m.RouteMetric = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RouteMetric |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipHostnameRequest", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipHostnameRequest = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientIdentifier", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClientIdentifier == nil { m.ClientIdentifier = &ClientIdentifierSpec{} } if err := m.ClientIdentifier.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DHCP6OperatorSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DHCP6OperatorSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DHCP6OperatorSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RouteMetric", wireType) } m.RouteMetric = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RouteMetric |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipHostnameRequest", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipHostnameRequest = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClientIdentifier", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClientIdentifier == nil { m.ClientIdentifier = &ClientIdentifierSpec{} } if err := m.ClientIdentifier.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DNSResolveCacheSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DNSResolveCacheSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DNSResolveCacheSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetChannelsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetChannelsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetChannelsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rx", wireType) } m.Rx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) } m.Tx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Other", wireType) } m.Other = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Other |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Combined", wireType) } m.Combined = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Combined |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetChannelsStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetChannelsStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetChannelsStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMax", wireType) } m.RxMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxMax", wireType) } m.TxMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field OtherMax", wireType) } m.OtherMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.OtherMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CombinedMax", wireType) } m.CombinedMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CombinedMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rx", wireType) } m.Rx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) } m.Tx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Other", wireType) } m.Other = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Other |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Combined", wireType) } m.Combined = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Combined |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetFeatureStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetFeatureStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetFeatureStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetRingsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetRingsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetRingsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rx", wireType) } m.Rx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMini", wireType) } m.RxMini = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMini |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxJumbo", wireType) } m.RxJumbo = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxJumbo |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) } m.Tx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxBufLen", wireType) } m.RxBufLen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxBufLen |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CqeSize", wireType) } m.CqeSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CqeSize |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPush", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TxPush = bool(v != 0) case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxPush", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RxPush = bool(v != 0) case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPushBufLen", wireType) } m.TxPushBufLen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxPushBufLen |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TcpDataSplit", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TcpDataSplit = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetRingsStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetRingsStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetRingsStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMax", wireType) } m.RxMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMiniMax", wireType) } m.RxMiniMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMiniMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxJumboMax", wireType) } m.RxJumboMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxJumboMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxMax", wireType) } m.TxMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPushBufLenMax", wireType) } m.TxPushBufLenMax = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxPushBufLenMax |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Rx", wireType) } m.Rx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Rx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxMini", wireType) } m.RxMini = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxMini |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxJumbo", wireType) } m.RxJumbo = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxJumbo |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType) } m.Tx = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Tx |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxBufLen", wireType) } m.RxBufLen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.RxBufLen |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CqeSize", wireType) } m.CqeSize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CqeSize |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPush", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TxPush = bool(v != 0) case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RxPush", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RxPush = bool(v != 0) case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TxPushBufLen", wireType) } m.TxPushBufLen = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.TxPushBufLen |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TcpDataSplit", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.TcpDataSplit = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Rings", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Rings == nil { m.Rings = &EthernetRingsSpec{} } if err := m.Rings.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Features", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Features == nil { m.Features = make(map[string]bool) } var mapkey string var mapvalue bool for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapvaluetemp int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ mapvaluetemp |= int(b&0x7F) << shift if b < 0x80 { break } } mapvalue = bool(mapvaluetemp != 0) } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Features[mapkey] = mapvalue iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Channels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Channels == nil { m.Channels = &EthernetChannelsSpec{} } if err := m.Channels.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType == 0 { var v enums.NethelpersWOLMode for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersWOLMode(b&0x7F) << shift if b < 0x80 { break } } m.WakeOnLan = append(m.WakeOnLan, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int if elementCount != 0 && len(m.WakeOnLan) == 0 { m.WakeOnLan = make([]enums.NethelpersWOLMode, 0, elementCount) } for iNdEx < postIndex { var v enums.NethelpersWOLMode for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersWOLMode(b&0x7F) << shift if b < 0x80 { break } } m.WakeOnLan = append(m.WakeOnLan, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field WakeOnLan", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EthernetStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EthernetStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EthernetStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LinkState", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.LinkState = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SpeedMegabits", wireType) } m.SpeedMegabits = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SpeedMegabits |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType) } m.Port = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Port |= enums.NethelpersPort(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Duplex", wireType) } m.Duplex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Duplex |= enums.NethelpersDuplex(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OurModes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OurModes = append(m.OurModes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PeerModes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PeerModes = append(m.PeerModes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Rings", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Rings == nil { m.Rings = &EthernetRingsStatus{} } if err := m.Rings.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Features", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Features = append(m.Features, &EthernetFeatureStatus{}) if err := m.Features[len(m.Features)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Channels", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Channels == nil { m.Channels = &EthernetChannelsStatus{} } if err := m.Channels.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType == 0 { var v enums.NethelpersWOLMode for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersWOLMode(b&0x7F) << shift if b < 0x80 { break } } m.WakeOnLan = append(m.WakeOnLan, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int if elementCount != 0 && len(m.WakeOnLan) == 0 { m.WakeOnLan = make([]enums.NethelpersWOLMode, 0, elementCount) } for iNdEx < postIndex { var v enums.NethelpersWOLMode for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersWOLMode(b&0x7F) << shift if b < 0x80 { break } } m.WakeOnLan = append(m.WakeOnLan, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field WakeOnLan", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HardwareAddrSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HardwareAddrSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HardwareAddrSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HardwareAddr", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.HardwareAddr = append(m.HardwareAddr[:0], dAtA[iNdEx:postIndex]...) if m.HardwareAddr == nil { m.HardwareAddr = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HostDNSConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HostDNSConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HostDNSConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenAddresses = append(m.ListenAddresses, &common.NetIPPort{}) if unmarshal, ok := interface{}(m.ListenAddresses[len(m.ListenAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ListenAddresses[len(m.ListenAddresses)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceHostDnsAddress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ServiceHostDnsAddress == nil { m.ServiceHostDnsAddress = &common.NetIP{} } if unmarshal, ok := interface{}(m.ServiceHostDnsAddress).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ServiceHostDnsAddress); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ResolveMemberNames", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ResolveMemberNames = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HostnameSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HostnameSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HostnameSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domainname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Domainname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *HostnameStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: HostnameStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: HostnameStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Domainname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Domainname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LinkAliasSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LinkAliasSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LinkAliasSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Alias = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LinkRefreshSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LinkRefreshSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LinkRefreshSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Generation", wireType) } m.Generation = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Generation |= int64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LinkSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LinkSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LinkSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Logical", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Logical = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Up", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Up = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Kind = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.NethelpersLinkType(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ParentName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ParentName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BondSlave", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BondSlave == nil { m.BondSlave = &BondSlave{} } if err := m.BondSlave.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BridgeSlave", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BridgeSlave == nil { m.BridgeSlave = &BridgeSlave{} } if err := m.BridgeSlave.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vlan", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Vlan == nil { m.Vlan = &VLANSpec{} } if err := m.Vlan.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BondMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BondMaster == nil { m.BondMaster = &BondMasterSpec{} } if err := m.BondMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BridgeMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BridgeMaster == nil { m.BridgeMaster = &BridgeMasterSpec{} } if err := m.BridgeMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Wireguard", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Wireguard == nil { m.Wireguard = &WireguardSpec{} } if err := m.Wireguard.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HardwareAddress", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.HardwareAddress = append(m.HardwareAddress[:0], dAtA[iNdEx:postIndex]...) if m.HardwareAddress == nil { m.HardwareAddress = []byte{} } iNdEx = postIndex case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Multicast", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Multicast = bool(v != 0) case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VrfMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.VrfMaster == nil { m.VrfMaster = &VRFMasterSpec{} } if err := m.VrfMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VrfSlave", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.VrfSlave == nil { m.VrfSlave = &VRFSlave{} } if err := m.VrfSlave.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LinkStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LinkStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LinkStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Index", wireType) } m.Index = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Index |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.NethelpersLinkType(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LinkIndex", wireType) } m.LinkIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.LinkIndex |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } m.Flags = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Flags |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HardwareAddr", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.HardwareAddr = append(m.HardwareAddr[:0], dAtA[iNdEx:postIndex]...) if m.HardwareAddr == nil { m.HardwareAddr = []byte{} } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BroadcastAddr", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BroadcastAddr = append(m.BroadcastAddr[:0], dAtA[iNdEx:postIndex]...) if m.BroadcastAddr == nil { m.BroadcastAddr = []byte{} } iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field QueueDisc", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.QueueDisc = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MasterIndex", wireType) } m.MasterIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MasterIndex |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field OperationalState", wireType) } m.OperationalState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.OperationalState |= enums.NethelpersOperationalState(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Kind", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Kind = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SlaveKind", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SlaveKind = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BusPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BusPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Pciid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Pciid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Driver = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 16: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DriverVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DriverVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FirmwareVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FirmwareVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 18: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProductId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProductId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 19: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VendorId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.VendorId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 20: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Product", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Product = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 21: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vendor", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Vendor = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 22: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field LinkState", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.LinkState = bool(v != 0) case 23: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SpeedMegabits", wireType) } m.SpeedMegabits = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SpeedMegabits |= int64(b&0x7F) << shift if b < 0x80 { break } } case 24: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Port", wireType) } m.Port = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Port |= enums.NethelpersPort(b&0x7F) << shift if b < 0x80 { break } } case 25: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Duplex", wireType) } m.Duplex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Duplex |= enums.NethelpersDuplex(b&0x7F) << shift if b < 0x80 { break } } case 26: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vlan", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Vlan == nil { m.Vlan = &VLANSpec{} } if err := m.Vlan.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 27: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BridgeMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BridgeMaster == nil { m.BridgeMaster = &BridgeMasterSpec{} } if err := m.BridgeMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 28: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BondMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.BondMaster == nil { m.BondMaster = &BondMasterSpec{} } if err := m.BondMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 29: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Wireguard", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Wireguard == nil { m.Wireguard = &WireguardSpec{} } if err := m.Wireguard.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 30: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PermanentAddr", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PermanentAddr = append(m.PermanentAddr[:0], dAtA[iNdEx:postIndex]...) if m.PermanentAddr == nil { m.PermanentAddr = []byte{} } iNdEx = postIndex case 31: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Alias", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Alias = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 32: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AltNames", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AltNames = append(m.AltNames, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 33: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field VrfMaster", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.VrfMaster == nil { m.VrfMaster = &VRFMasterSpec{} } if err := m.VrfMaster.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesAddressMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesAddressMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesAddressMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IncludeSubnets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.IncludeSubnets = append(m.IncludeSubnets, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.IncludeSubnets[len(m.IncludeSubnets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.IncludeSubnets[len(m.IncludeSubnets)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExcludeSubnets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExcludeSubnets = append(m.ExcludeSubnets, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.ExcludeSubnets[len(m.ExcludeSubnets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExcludeSubnets[len(m.ExcludeSubnets)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Invert", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Invert = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesChainSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesChainSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesChainSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hook", wireType) } m.Hook = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hook |= enums.NethelpersNfTablesChainHook(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= enums.NethelpersNfTablesChainPriority(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Rules", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Rules = append(m.Rules, &NfTablesRule{}) if err := m.Rules[len(m.Rules)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Policy", wireType) } m.Policy = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Policy |= enums.NethelpersNfTablesVerdict(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesClampMSS) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesClampMSS: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesClampMSS: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesConntrackStateMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesConntrackStateMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesConntrackStateMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType == 0 { var v enums.NethelpersConntrackState for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersConntrackState(b&0x7F) << shift if b < 0x80 { break } } m.States = append(m.States, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int if elementCount != 0 && len(m.States) == 0 { m.States = make([]enums.NethelpersConntrackState, 0, elementCount) } for iNdEx < postIndex { var v enums.NethelpersConntrackState for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersConntrackState(b&0x7F) << shift if b < 0x80 { break } } m.States = append(m.States, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field States", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesICMPTypeMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesICMPTypeMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesICMPTypeMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType == 0 { var v enums.NethelpersICMPType for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersICMPType(b&0x7F) << shift if b < 0x80 { break } } m.Types = append(m.Types, v) } else if wireType == 2 { var packedLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ packedLen |= int(b&0x7F) << shift if b < 0x80 { break } } if packedLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + packedLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } var elementCount int if elementCount != 0 && len(m.Types) == 0 { m.Types = make([]enums.NethelpersICMPType, 0, elementCount) } for iNdEx < postIndex { var v enums.NethelpersICMPType for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= enums.NethelpersICMPType(b&0x7F) << shift if b < 0x80 { break } } m.Types = append(m.Types, v) } } else { return fmt.Errorf("proto: wrong wireType = %d for field Types", wireType) } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesIfNameMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesIfNameMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesIfNameMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType) } m.Operator = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Operator |= enums.NethelpersMatchOperator(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InterfaceNames", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InterfaceNames = append(m.InterfaceNames, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesLayer4Match) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesLayer4Match: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesLayer4Match: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) } m.Protocol = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Protocol |= enums.NethelpersProtocol(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchSourcePort", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchSourcePort == nil { m.MatchSourcePort = &NfTablesPortMatch{} } if err := m.MatchSourcePort.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchDestinationPort", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchDestinationPort == nil { m.MatchDestinationPort = &NfTablesPortMatch{} } if err := m.MatchDestinationPort.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchIcmpType", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchIcmpType == nil { m.MatchIcmpType = &NfTablesICMPTypeMatch{} } if err := m.MatchIcmpType.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesLimitMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesLimitMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesLimitMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PacketRatePerSecond", wireType) } m.PacketRatePerSecond = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PacketRatePerSecond |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesMark) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesMark: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesMark: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mask", wireType) } m.Mask = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mask |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Xor", wireType) } m.Xor = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Xor |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } m.Value = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Value |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesPortMatch) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesPortMatch: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesPortMatch: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ranges", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ranges = append(m.Ranges, &PortRange{}) if err := m.Ranges[len(m.Ranges)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NfTablesRule) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NfTablesRule: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NfTablesRule: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchOIfName", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchOIfName == nil { m.MatchOIfName = &NfTablesIfNameMatch{} } if err := m.MatchOIfName.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Verdict", wireType) } m.Verdict = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Verdict |= enums.NethelpersNfTablesVerdict(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchMark", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchMark == nil { m.MatchMark = &NfTablesMark{} } if err := m.MatchMark.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SetMark", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.SetMark == nil { m.SetMark = &NfTablesMark{} } if err := m.SetMark.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchSourceAddress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchSourceAddress == nil { m.MatchSourceAddress = &NfTablesAddressMatch{} } if err := m.MatchSourceAddress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchDestinationAddress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchDestinationAddress == nil { m.MatchDestinationAddress = &NfTablesAddressMatch{} } if err := m.MatchDestinationAddress.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchLayer4", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchLayer4 == nil { m.MatchLayer4 = &NfTablesLayer4Match{} } if err := m.MatchLayer4.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchIIfName", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchIIfName == nil { m.MatchIIfName = &NfTablesIfNameMatch{} } if err := m.MatchIIfName.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ClampMss", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ClampMss == nil { m.ClampMss = &NfTablesClampMSS{} } if err := m.ClampMss.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchLimit", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchLimit == nil { m.MatchLimit = &NfTablesLimitMatch{} } if err := m.MatchLimit.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MatchConntrackState", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MatchConntrackState == nil { m.MatchConntrackState = &NfTablesConntrackStateMatch{} } if err := m.MatchConntrackState.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AnonCounter", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AnonCounter = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeAddressFilterSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeAddressFilterSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeAddressFilterSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IncludeSubnets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.IncludeSubnets = append(m.IncludeSubnets, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.IncludeSubnets[len(m.IncludeSubnets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.IncludeSubnets[len(m.IncludeSubnets)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExcludeSubnets", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExcludeSubnets = append(m.ExcludeSubnets, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.ExcludeSubnets[len(m.ExcludeSubnets)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExcludeSubnets[len(m.ExcludeSubnets)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeAddressSortAlgorithmSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeAddressSortAlgorithmSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeAddressSortAlgorithmSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Algorithm", wireType) } m.Algorithm = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Algorithm |= enums.NethelpersAddressSortAlgorithm(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *NodeAddressSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: NodeAddressSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: NodeAddressSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.Addresses[len(m.Addresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Addresses[len(m.Addresses)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SortAlgorithm", wireType) } m.SortAlgorithm = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SortAlgorithm |= enums.NethelpersAddressSortAlgorithm(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *OperatorSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: OperatorSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: OperatorSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Operator", wireType) } m.Operator = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Operator |= enums.NetworkOperator(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RequireUp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.RequireUp = bool(v != 0) case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Dhcp4", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Dhcp4 == nil { m.Dhcp4 = &DHCP4OperatorSpec{} } if err := m.Dhcp4.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Dhcp6", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Dhcp6 == nil { m.Dhcp6 = &DHCP6OperatorSpec{} } if err := m.Dhcp6.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Vip", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Vip == nil { m.Vip = &VIPOperatorSpec{} } if err := m.Vip.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PlatformConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PlatformConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PlatformConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Addresses = append(m.Addresses, &AddressSpecSpec{}) if err := m.Addresses[len(m.Addresses)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Links", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Links = append(m.Links, &LinkSpecSpec{}) if err := m.Links[len(m.Links)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Routes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Routes = append(m.Routes, &RouteSpecSpec{}) if err := m.Routes[len(m.Routes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostnames", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostnames = append(m.Hostnames, &HostnameSpecSpec{}) if err := m.Hostnames[len(m.Hostnames)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Resolvers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Resolvers = append(m.Resolvers, &ResolverSpecSpec{}) if err := m.Resolvers[len(m.Resolvers)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TimeServers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TimeServers = append(m.TimeServers, &TimeServerSpecSpec{}) if err := m.TimeServers[len(m.TimeServers)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Operators", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Operators = append(m.Operators, &OperatorSpecSpec{}) if err := m.Operators[len(m.Operators)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalIps", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExternalIps = append(m.ExternalIps, &common.NetIP{}) if unmarshal, ok := interface{}(m.ExternalIps[len(m.ExternalIps)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ExternalIps[len(m.ExternalIps)-1]); err != nil { return err } } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Probes", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Probes = append(m.Probes, &ProbeSpecSpec{}) if err := m.Probes[len(m.Probes)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &runtime.PlatformMetadataSpec{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PortRange) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PortRange: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PortRange: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Lo", wireType) } m.Lo = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Lo |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hi", wireType) } m.Hi = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hi |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProbeSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProbeSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProbeSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Interval", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Interval == nil { m.Interval = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Interval).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FailureThreshold", wireType) } m.FailureThreshold = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FailureThreshold |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tcp", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Tcp == nil { m.Tcp = &TCPProbeSpec{} } if err := m.Tcp.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ProbeStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ProbeStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ProbeStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Success", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Success = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastError", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LastError = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResolverSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResolverSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResolverSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsServers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsServers = append(m.DnsServers, &common.NetIP{}) if unmarshal, ok := interface{}(m.DnsServers[len(m.DnsServers)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.DnsServers[len(m.DnsServers)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SearchDomains", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SearchDomains = append(m.SearchDomains, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ResolverStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ResolverStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ResolverStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsServers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsServers = append(m.DnsServers, &common.NetIP{}) if unmarshal, ok := interface{}(m.DnsServers[len(m.DnsServers)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.DnsServers[len(m.DnsServers)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SearchDomains", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SearchDomains = append(m.SearchDomains, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RouteSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RouteSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RouteSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Destination == nil { m.Destination = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Destination).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Destination); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Source == nil { m.Source = &common.NetIP{} } if unmarshal, ok := interface{}(m.Source).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Source); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Gateway", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Gateway == nil { m.Gateway = &common.NetIP{} } if unmarshal, ok := interface{}(m.Gateway).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Gateway); err != nil { return err } } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OutLinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OutLinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } m.Table = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Table |= enums.NethelpersRoutingTable(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Scope", wireType) } m.Scope = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Scope |= enums.NethelpersScope(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.NethelpersRouteType(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } m.Flags = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Flags |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) } m.Protocol = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Protocol |= enums.NethelpersRouteProtocol(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RouteStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RouteStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RouteStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Destination == nil { m.Destination = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Destination).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Destination); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Source == nil { m.Source = &common.NetIP{} } if unmarshal, ok := interface{}(m.Source).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Source); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Gateway", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Gateway == nil { m.Gateway = &common.NetIP{} } if unmarshal, ok := interface{}(m.Gateway).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Gateway); err != nil { return err } } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field OutLinkIndex", wireType) } m.OutLinkIndex = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.OutLinkIndex |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OutLinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OutLinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } m.Table = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Table |= enums.NethelpersRoutingTable(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Scope", wireType) } m.Scope = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Scope |= enums.NethelpersScope(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= enums.NethelpersRouteType(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Flags", wireType) } m.Flags = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Flags |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) } m.Protocol = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Protocol |= enums.NethelpersRouteProtocol(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RoutingRuleSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RoutingRuleSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RoutingRuleSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Src", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Src == nil { m.Src = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Src).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Src); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Dst", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Dst == nil { m.Dst = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Dst).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Dst); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } m.Table = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Table |= enums.NethelpersRoutingTable(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= enums.NethelpersRoutingRuleAction(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IifName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.IifName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OifName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OifName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FwMark", wireType) } m.FwMark = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FwMark |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FwMask", wireType) } m.FwMask = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FwMask |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *RoutingRuleStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: RoutingRuleStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: RoutingRuleStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Family", wireType) } m.Family = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Family |= enums.NethelpersFamily(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Src", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Src == nil { m.Src = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Src).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Src); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Dst", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Dst == nil { m.Dst = &common.NetIPPrefix{} } if unmarshal, ok := interface{}(m.Dst).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Dst); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } m.Table = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Table |= enums.NethelpersRoutingTable(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Priority", wireType) } m.Priority = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Priority |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType) } m.Action = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Action |= enums.NethelpersRoutingRuleAction(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IifName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.IifName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field OifName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.OifName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FwMark", wireType) } m.FwMark = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FwMark |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FwMask", wireType) } m.FwMask = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FwMask |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *STPSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: STPSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: STPSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Enabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Enabled = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AddressReady", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.AddressReady = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConnectivityReady", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ConnectivityReady = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HostnameReady", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.HostnameReady = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field EtcFilesReady", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.EtcFilesReady = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TCPProbeSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TCPProbeSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TCPProbeSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timeout == nil { m.Timeout = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TimeServerSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TimeServerSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TimeServerSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NtpServers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NtpServers = append(m.NtpServers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ConfigLayer", wireType) } m.ConfigLayer = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ConfigLayer |= enums.NetworkConfigLayer(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TimeServerStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TimeServerStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TimeServerStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NtpServers", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.NtpServers = append(m.NtpServers, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VIPEquinixMetalSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VIPEquinixMetalSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VIPEquinixMetalSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProjectId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProjectId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DeviceId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiToken", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApiToken = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VIPHCloudSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VIPHCloudSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VIPHCloudSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DeviceId", wireType) } m.DeviceId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DeviceId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NetworkId", wireType) } m.NetworkId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NetworkId |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiToken", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApiToken = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VIPOperatorSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VIPOperatorSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VIPOperatorSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ip", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Ip == nil { m.Ip = &common.NetIP{} } if unmarshal, ok := interface{}(m.Ip).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Ip); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field GratuitousArp", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.GratuitousArp = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EquinixMetal", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EquinixMetal == nil { m.EquinixMetal = &VIPEquinixMetalSpec{} } if err := m.EquinixMetal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field HCloud", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.HCloud == nil { m.HCloud = &VIPHCloudSpec{} } if err := m.HCloud.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VLANSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VLANSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VLANSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Vid", wireType) } m.Vid = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Vid |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Protocol", wireType) } m.Protocol = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Protocol |= enums.NethelpersVLANProtocol(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VRFMasterSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VRFMasterSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VRFMasterSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Table", wireType) } m.Table = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Table |= enums.NethelpersRoutingTable(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *VRFSlave) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: VRFSlave: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: VRFSlave: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MasterName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MasterName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WireguardPeer) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WireguardPeer: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WireguardPeer: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PublicKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PresharedKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PresharedKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PersistentKeepaliveInterval", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.PersistentKeepaliveInterval == nil { m.PersistentKeepaliveInterval = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.PersistentKeepaliveInterval).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AllowedIps", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AllowedIps = append(m.AllowedIps, &common.NetIPPrefix{}) if unmarshal, ok := interface{}(m.AllowedIps[len(m.AllowedIps)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AllowedIps[len(m.AllowedIps)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WireguardSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WireguardSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WireguardSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PrivateKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PrivateKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PublicKey", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PublicKey = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ListenPort", wireType) } m.ListenPort = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ListenPort |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FirewallMark", wireType) } m.FirewallMark = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FirewallMark |= int64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Peers", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Peers = append(m.Peers, &WireguardPeer{}) if err := m.Peers[len(m.Peers)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/perf/perf.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/perf/perf.proto package perf import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // CPUSpec represents the last CPU stats snapshot. type CPUSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Cpu []*CPUStat `protobuf:"bytes,1,rep,name=cpu,proto3" json:"cpu,omitempty"` CpuTotal *CPUStat `protobuf:"bytes,2,opt,name=cpu_total,json=cpuTotal,proto3" json:"cpu_total,omitempty"` IrqTotal uint64 `protobuf:"varint,3,opt,name=irq_total,json=irqTotal,proto3" json:"irq_total,omitempty"` ContextSwitches uint64 `protobuf:"varint,4,opt,name=context_switches,json=contextSwitches,proto3" json:"context_switches,omitempty"` ProcessCreated uint64 `protobuf:"varint,5,opt,name=process_created,json=processCreated,proto3" json:"process_created,omitempty"` ProcessRunning uint64 `protobuf:"varint,6,opt,name=process_running,json=processRunning,proto3" json:"process_running,omitempty"` ProcessBlocked uint64 `protobuf:"varint,7,opt,name=process_blocked,json=processBlocked,proto3" json:"process_blocked,omitempty"` SoftIrqTotal uint64 `protobuf:"varint,8,opt,name=soft_irq_total,json=softIrqTotal,proto3" json:"soft_irq_total,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUSpec) Reset() { *x = CPUSpec{} mi := &file_resource_definitions_perf_perf_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUSpec) ProtoMessage() {} func (x *CPUSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_perf_perf_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUSpec.ProtoReflect.Descriptor instead. func (*CPUSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_perf_perf_proto_rawDescGZIP(), []int{0} } func (x *CPUSpec) GetCpu() []*CPUStat { if x != nil { return x.Cpu } return nil } func (x *CPUSpec) GetCpuTotal() *CPUStat { if x != nil { return x.CpuTotal } return nil } func (x *CPUSpec) GetIrqTotal() uint64 { if x != nil { return x.IrqTotal } return 0 } func (x *CPUSpec) GetContextSwitches() uint64 { if x != nil { return x.ContextSwitches } return 0 } func (x *CPUSpec) GetProcessCreated() uint64 { if x != nil { return x.ProcessCreated } return 0 } func (x *CPUSpec) GetProcessRunning() uint64 { if x != nil { return x.ProcessRunning } return 0 } func (x *CPUSpec) GetProcessBlocked() uint64 { if x != nil { return x.ProcessBlocked } return 0 } func (x *CPUSpec) GetSoftIrqTotal() uint64 { if x != nil { return x.SoftIrqTotal } return 0 } // CPUStat represents a single cpu stat. type CPUStat struct { state protoimpl.MessageState `protogen:"open.v1"` User float64 `protobuf:"fixed64,1,opt,name=user,proto3" json:"user,omitempty"` Nice float64 `protobuf:"fixed64,2,opt,name=nice,proto3" json:"nice,omitempty"` System float64 `protobuf:"fixed64,3,opt,name=system,proto3" json:"system,omitempty"` Idle float64 `protobuf:"fixed64,4,opt,name=idle,proto3" json:"idle,omitempty"` Iowait float64 `protobuf:"fixed64,5,opt,name=iowait,proto3" json:"iowait,omitempty"` Irq float64 `protobuf:"fixed64,6,opt,name=irq,proto3" json:"irq,omitempty"` SoftIrq float64 `protobuf:"fixed64,7,opt,name=soft_irq,json=softIrq,proto3" json:"soft_irq,omitempty"` Steal float64 `protobuf:"fixed64,8,opt,name=steal,proto3" json:"steal,omitempty"` Guest float64 `protobuf:"fixed64,9,opt,name=guest,proto3" json:"guest,omitempty"` GuestNice float64 `protobuf:"fixed64,10,opt,name=guest_nice,json=guestNice,proto3" json:"guest_nice,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CPUStat) Reset() { *x = CPUStat{} mi := &file_resource_definitions_perf_perf_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CPUStat) String() string { return protoimpl.X.MessageStringOf(x) } func (*CPUStat) ProtoMessage() {} func (x *CPUStat) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_perf_perf_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CPUStat.ProtoReflect.Descriptor instead. func (*CPUStat) Descriptor() ([]byte, []int) { return file_resource_definitions_perf_perf_proto_rawDescGZIP(), []int{1} } func (x *CPUStat) GetUser() float64 { if x != nil { return x.User } return 0 } func (x *CPUStat) GetNice() float64 { if x != nil { return x.Nice } return 0 } func (x *CPUStat) GetSystem() float64 { if x != nil { return x.System } return 0 } func (x *CPUStat) GetIdle() float64 { if x != nil { return x.Idle } return 0 } func (x *CPUStat) GetIowait() float64 { if x != nil { return x.Iowait } return 0 } func (x *CPUStat) GetIrq() float64 { if x != nil { return x.Irq } return 0 } func (x *CPUStat) GetSoftIrq() float64 { if x != nil { return x.SoftIrq } return 0 } func (x *CPUStat) GetSteal() float64 { if x != nil { return x.Steal } return 0 } func (x *CPUStat) GetGuest() float64 { if x != nil { return x.Guest } return 0 } func (x *CPUStat) GetGuestNice() float64 { if x != nil { return x.GuestNice } return 0 } // MemorySpec represents the last Memory stats snapshot. type MemorySpec struct { state protoimpl.MessageState `protogen:"open.v1"` MemTotal uint64 `protobuf:"varint,1,opt,name=mem_total,json=memTotal,proto3" json:"mem_total,omitempty"` MemUsed uint64 `protobuf:"varint,2,opt,name=mem_used,json=memUsed,proto3" json:"mem_used,omitempty"` MemAvailable uint64 `protobuf:"varint,3,opt,name=mem_available,json=memAvailable,proto3" json:"mem_available,omitempty"` Buffers uint64 `protobuf:"varint,4,opt,name=buffers,proto3" json:"buffers,omitempty"` Cached uint64 `protobuf:"varint,5,opt,name=cached,proto3" json:"cached,omitempty"` SwapCached uint64 `protobuf:"varint,6,opt,name=swap_cached,json=swapCached,proto3" json:"swap_cached,omitempty"` Active uint64 `protobuf:"varint,7,opt,name=active,proto3" json:"active,omitempty"` Inactive uint64 `protobuf:"varint,8,opt,name=inactive,proto3" json:"inactive,omitempty"` ActiveAnon uint64 `protobuf:"varint,9,opt,name=active_anon,json=activeAnon,proto3" json:"active_anon,omitempty"` InactiveAnon uint64 `protobuf:"varint,10,opt,name=inactive_anon,json=inactiveAnon,proto3" json:"inactive_anon,omitempty"` ActiveFile uint64 `protobuf:"varint,11,opt,name=active_file,json=activeFile,proto3" json:"active_file,omitempty"` InactiveFile uint64 `protobuf:"varint,12,opt,name=inactive_file,json=inactiveFile,proto3" json:"inactive_file,omitempty"` Unevictable uint64 `protobuf:"varint,13,opt,name=unevictable,proto3" json:"unevictable,omitempty"` Mlocked uint64 `protobuf:"varint,14,opt,name=mlocked,proto3" json:"mlocked,omitempty"` SwapTotal uint64 `protobuf:"varint,15,opt,name=swap_total,json=swapTotal,proto3" json:"swap_total,omitempty"` SwapFree uint64 `protobuf:"varint,16,opt,name=swap_free,json=swapFree,proto3" json:"swap_free,omitempty"` Dirty uint64 `protobuf:"varint,17,opt,name=dirty,proto3" json:"dirty,omitempty"` Writeback uint64 `protobuf:"varint,18,opt,name=writeback,proto3" json:"writeback,omitempty"` AnonPages uint64 `protobuf:"varint,19,opt,name=anon_pages,json=anonPages,proto3" json:"anon_pages,omitempty"` Mapped uint64 `protobuf:"varint,20,opt,name=mapped,proto3" json:"mapped,omitempty"` Shmem uint64 `protobuf:"varint,21,opt,name=shmem,proto3" json:"shmem,omitempty"` Slab uint64 `protobuf:"varint,22,opt,name=slab,proto3" json:"slab,omitempty"` SReclaimable uint64 `protobuf:"varint,23,opt,name=s_reclaimable,json=sReclaimable,proto3" json:"s_reclaimable,omitempty"` SUnreclaim uint64 `protobuf:"varint,24,opt,name=s_unreclaim,json=sUnreclaim,proto3" json:"s_unreclaim,omitempty"` KernelStack uint64 `protobuf:"varint,25,opt,name=kernel_stack,json=kernelStack,proto3" json:"kernel_stack,omitempty"` PageTables uint64 `protobuf:"varint,26,opt,name=page_tables,json=pageTables,proto3" json:"page_tables,omitempty"` NfSunstable uint64 `protobuf:"varint,27,opt,name=nf_sunstable,json=nfSunstable,proto3" json:"nf_sunstable,omitempty"` Bounce uint64 `protobuf:"varint,28,opt,name=bounce,proto3" json:"bounce,omitempty"` WritebackTmp uint64 `protobuf:"varint,29,opt,name=writeback_tmp,json=writebackTmp,proto3" json:"writeback_tmp,omitempty"` CommitLimit uint64 `protobuf:"varint,30,opt,name=commit_limit,json=commitLimit,proto3" json:"commit_limit,omitempty"` CommittedAs uint64 `protobuf:"varint,31,opt,name=committed_as,json=committedAs,proto3" json:"committed_as,omitempty"` VmallocTotal uint64 `protobuf:"varint,32,opt,name=vmalloc_total,json=vmallocTotal,proto3" json:"vmalloc_total,omitempty"` VmallocUsed uint64 `protobuf:"varint,33,opt,name=vmalloc_used,json=vmallocUsed,proto3" json:"vmalloc_used,omitempty"` VmallocChunk uint64 `protobuf:"varint,34,opt,name=vmalloc_chunk,json=vmallocChunk,proto3" json:"vmalloc_chunk,omitempty"` HardwareCorrupted uint64 `protobuf:"varint,35,opt,name=hardware_corrupted,json=hardwareCorrupted,proto3" json:"hardware_corrupted,omitempty"` AnonHugePages uint64 `protobuf:"varint,36,opt,name=anon_huge_pages,json=anonHugePages,proto3" json:"anon_huge_pages,omitempty"` ShmemHugePages uint64 `protobuf:"varint,37,opt,name=shmem_huge_pages,json=shmemHugePages,proto3" json:"shmem_huge_pages,omitempty"` ShmemPmdMapped uint64 `protobuf:"varint,38,opt,name=shmem_pmd_mapped,json=shmemPmdMapped,proto3" json:"shmem_pmd_mapped,omitempty"` CmaTotal uint64 `protobuf:"varint,39,opt,name=cma_total,json=cmaTotal,proto3" json:"cma_total,omitempty"` CmaFree uint64 `protobuf:"varint,40,opt,name=cma_free,json=cmaFree,proto3" json:"cma_free,omitempty"` HugePagesTotal uint64 `protobuf:"varint,41,opt,name=huge_pages_total,json=hugePagesTotal,proto3" json:"huge_pages_total,omitempty"` HugePagesFree uint64 `protobuf:"varint,42,opt,name=huge_pages_free,json=hugePagesFree,proto3" json:"huge_pages_free,omitempty"` HugePagesRsvd uint64 `protobuf:"varint,43,opt,name=huge_pages_rsvd,json=hugePagesRsvd,proto3" json:"huge_pages_rsvd,omitempty"` HugePagesSurp uint64 `protobuf:"varint,44,opt,name=huge_pages_surp,json=hugePagesSurp,proto3" json:"huge_pages_surp,omitempty"` Hugepagesize uint64 `protobuf:"varint,45,opt,name=hugepagesize,proto3" json:"hugepagesize,omitempty"` DirectMap4K uint64 `protobuf:"varint,46,opt,name=direct_map4k,json=directMap4k,proto3" json:"direct_map4k,omitempty"` DirectMap2M uint64 `protobuf:"varint,47,opt,name=direct_map2m,json=directMap2m,proto3" json:"direct_map2m,omitempty"` DirectMap1G uint64 `protobuf:"varint,48,opt,name=direct_map1g,json=directMap1g,proto3" json:"direct_map1g,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MemorySpec) Reset() { *x = MemorySpec{} mi := &file_resource_definitions_perf_perf_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MemorySpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MemorySpec) ProtoMessage() {} func (x *MemorySpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_perf_perf_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MemorySpec.ProtoReflect.Descriptor instead. func (*MemorySpec) Descriptor() ([]byte, []int) { return file_resource_definitions_perf_perf_proto_rawDescGZIP(), []int{2} } func (x *MemorySpec) GetMemTotal() uint64 { if x != nil { return x.MemTotal } return 0 } func (x *MemorySpec) GetMemUsed() uint64 { if x != nil { return x.MemUsed } return 0 } func (x *MemorySpec) GetMemAvailable() uint64 { if x != nil { return x.MemAvailable } return 0 } func (x *MemorySpec) GetBuffers() uint64 { if x != nil { return x.Buffers } return 0 } func (x *MemorySpec) GetCached() uint64 { if x != nil { return x.Cached } return 0 } func (x *MemorySpec) GetSwapCached() uint64 { if x != nil { return x.SwapCached } return 0 } func (x *MemorySpec) GetActive() uint64 { if x != nil { return x.Active } return 0 } func (x *MemorySpec) GetInactive() uint64 { if x != nil { return x.Inactive } return 0 } func (x *MemorySpec) GetActiveAnon() uint64 { if x != nil { return x.ActiveAnon } return 0 } func (x *MemorySpec) GetInactiveAnon() uint64 { if x != nil { return x.InactiveAnon } return 0 } func (x *MemorySpec) GetActiveFile() uint64 { if x != nil { return x.ActiveFile } return 0 } func (x *MemorySpec) GetInactiveFile() uint64 { if x != nil { return x.InactiveFile } return 0 } func (x *MemorySpec) GetUnevictable() uint64 { if x != nil { return x.Unevictable } return 0 } func (x *MemorySpec) GetMlocked() uint64 { if x != nil { return x.Mlocked } return 0 } func (x *MemorySpec) GetSwapTotal() uint64 { if x != nil { return x.SwapTotal } return 0 } func (x *MemorySpec) GetSwapFree() uint64 { if x != nil { return x.SwapFree } return 0 } func (x *MemorySpec) GetDirty() uint64 { if x != nil { return x.Dirty } return 0 } func (x *MemorySpec) GetWriteback() uint64 { if x != nil { return x.Writeback } return 0 } func (x *MemorySpec) GetAnonPages() uint64 { if x != nil { return x.AnonPages } return 0 } func (x *MemorySpec) GetMapped() uint64 { if x != nil { return x.Mapped } return 0 } func (x *MemorySpec) GetShmem() uint64 { if x != nil { return x.Shmem } return 0 } func (x *MemorySpec) GetSlab() uint64 { if x != nil { return x.Slab } return 0 } func (x *MemorySpec) GetSReclaimable() uint64 { if x != nil { return x.SReclaimable } return 0 } func (x *MemorySpec) GetSUnreclaim() uint64 { if x != nil { return x.SUnreclaim } return 0 } func (x *MemorySpec) GetKernelStack() uint64 { if x != nil { return x.KernelStack } return 0 } func (x *MemorySpec) GetPageTables() uint64 { if x != nil { return x.PageTables } return 0 } func (x *MemorySpec) GetNfSunstable() uint64 { if x != nil { return x.NfSunstable } return 0 } func (x *MemorySpec) GetBounce() uint64 { if x != nil { return x.Bounce } return 0 } func (x *MemorySpec) GetWritebackTmp() uint64 { if x != nil { return x.WritebackTmp } return 0 } func (x *MemorySpec) GetCommitLimit() uint64 { if x != nil { return x.CommitLimit } return 0 } func (x *MemorySpec) GetCommittedAs() uint64 { if x != nil { return x.CommittedAs } return 0 } func (x *MemorySpec) GetVmallocTotal() uint64 { if x != nil { return x.VmallocTotal } return 0 } func (x *MemorySpec) GetVmallocUsed() uint64 { if x != nil { return x.VmallocUsed } return 0 } func (x *MemorySpec) GetVmallocChunk() uint64 { if x != nil { return x.VmallocChunk } return 0 } func (x *MemorySpec) GetHardwareCorrupted() uint64 { if x != nil { return x.HardwareCorrupted } return 0 } func (x *MemorySpec) GetAnonHugePages() uint64 { if x != nil { return x.AnonHugePages } return 0 } func (x *MemorySpec) GetShmemHugePages() uint64 { if x != nil { return x.ShmemHugePages } return 0 } func (x *MemorySpec) GetShmemPmdMapped() uint64 { if x != nil { return x.ShmemPmdMapped } return 0 } func (x *MemorySpec) GetCmaTotal() uint64 { if x != nil { return x.CmaTotal } return 0 } func (x *MemorySpec) GetCmaFree() uint64 { if x != nil { return x.CmaFree } return 0 } func (x *MemorySpec) GetHugePagesTotal() uint64 { if x != nil { return x.HugePagesTotal } return 0 } func (x *MemorySpec) GetHugePagesFree() uint64 { if x != nil { return x.HugePagesFree } return 0 } func (x *MemorySpec) GetHugePagesRsvd() uint64 { if x != nil { return x.HugePagesRsvd } return 0 } func (x *MemorySpec) GetHugePagesSurp() uint64 { if x != nil { return x.HugePagesSurp } return 0 } func (x *MemorySpec) GetHugepagesize() uint64 { if x != nil { return x.Hugepagesize } return 0 } func (x *MemorySpec) GetDirectMap4K() uint64 { if x != nil { return x.DirectMap4K } return 0 } func (x *MemorySpec) GetDirectMap2M() uint64 { if x != nil { return x.DirectMap2M } return 0 } func (x *MemorySpec) GetDirectMap1G() uint64 { if x != nil { return x.DirectMap1G } return 0 } var File_resource_definitions_perf_perf_proto protoreflect.FileDescriptor const file_resource_definitions_perf_perf_proto_rawDesc = "" + "\n" + "$resource/definitions/perf/perf.proto\x12\x1ftalos.resource.definitions.perf\"\xf5\x02\n" + "\aCPUSpec\x12:\n" + "\x03cpu\x18\x01 \x03(\v2(.talos.resource.definitions.perf.CPUStatR\x03cpu\x12E\n" + "\tcpu_total\x18\x02 \x01(\v2(.talos.resource.definitions.perf.CPUStatR\bcpuTotal\x12\x1b\n" + "\tirq_total\x18\x03 \x01(\x04R\birqTotal\x12)\n" + "\x10context_switches\x18\x04 \x01(\x04R\x0fcontextSwitches\x12'\n" + "\x0fprocess_created\x18\x05 \x01(\x04R\x0eprocessCreated\x12'\n" + "\x0fprocess_running\x18\x06 \x01(\x04R\x0eprocessRunning\x12'\n" + "\x0fprocess_blocked\x18\a \x01(\x04R\x0eprocessBlocked\x12$\n" + "\x0esoft_irq_total\x18\b \x01(\x04R\fsoftIrqTotal\"\xed\x01\n" + "\aCPUStat\x12\x12\n" + "\x04user\x18\x01 \x01(\x01R\x04user\x12\x12\n" + "\x04nice\x18\x02 \x01(\x01R\x04nice\x12\x16\n" + "\x06system\x18\x03 \x01(\x01R\x06system\x12\x12\n" + "\x04idle\x18\x04 \x01(\x01R\x04idle\x12\x16\n" + "\x06iowait\x18\x05 \x01(\x01R\x06iowait\x12\x10\n" + "\x03irq\x18\x06 \x01(\x01R\x03irq\x12\x19\n" + "\bsoft_irq\x18\a \x01(\x01R\asoftIrq\x12\x14\n" + "\x05steal\x18\b \x01(\x01R\x05steal\x12\x14\n" + "\x05guest\x18\t \x01(\x01R\x05guest\x12\x1d\n" + "\n" + "guest_nice\x18\n" + " \x01(\x01R\tguestNice\"\xb8\f\n" + "\n" + "MemorySpec\x12\x1b\n" + "\tmem_total\x18\x01 \x01(\x04R\bmemTotal\x12\x19\n" + "\bmem_used\x18\x02 \x01(\x04R\amemUsed\x12#\n" + "\rmem_available\x18\x03 \x01(\x04R\fmemAvailable\x12\x18\n" + "\abuffers\x18\x04 \x01(\x04R\abuffers\x12\x16\n" + "\x06cached\x18\x05 \x01(\x04R\x06cached\x12\x1f\n" + "\vswap_cached\x18\x06 \x01(\x04R\n" + "swapCached\x12\x16\n" + "\x06active\x18\a \x01(\x04R\x06active\x12\x1a\n" + "\binactive\x18\b \x01(\x04R\binactive\x12\x1f\n" + "\vactive_anon\x18\t \x01(\x04R\n" + "activeAnon\x12#\n" + "\rinactive_anon\x18\n" + " \x01(\x04R\finactiveAnon\x12\x1f\n" + "\vactive_file\x18\v \x01(\x04R\n" + "activeFile\x12#\n" + "\rinactive_file\x18\f \x01(\x04R\finactiveFile\x12 \n" + "\vunevictable\x18\r \x01(\x04R\vunevictable\x12\x18\n" + "\amlocked\x18\x0e \x01(\x04R\amlocked\x12\x1d\n" + "\n" + "swap_total\x18\x0f \x01(\x04R\tswapTotal\x12\x1b\n" + "\tswap_free\x18\x10 \x01(\x04R\bswapFree\x12\x14\n" + "\x05dirty\x18\x11 \x01(\x04R\x05dirty\x12\x1c\n" + "\twriteback\x18\x12 \x01(\x04R\twriteback\x12\x1d\n" + "\n" + "anon_pages\x18\x13 \x01(\x04R\tanonPages\x12\x16\n" + "\x06mapped\x18\x14 \x01(\x04R\x06mapped\x12\x14\n" + "\x05shmem\x18\x15 \x01(\x04R\x05shmem\x12\x12\n" + "\x04slab\x18\x16 \x01(\x04R\x04slab\x12#\n" + "\rs_reclaimable\x18\x17 \x01(\x04R\fsReclaimable\x12\x1f\n" + "\vs_unreclaim\x18\x18 \x01(\x04R\n" + "sUnreclaim\x12!\n" + "\fkernel_stack\x18\x19 \x01(\x04R\vkernelStack\x12\x1f\n" + "\vpage_tables\x18\x1a \x01(\x04R\n" + "pageTables\x12!\n" + "\fnf_sunstable\x18\x1b \x01(\x04R\vnfSunstable\x12\x16\n" + "\x06bounce\x18\x1c \x01(\x04R\x06bounce\x12#\n" + "\rwriteback_tmp\x18\x1d \x01(\x04R\fwritebackTmp\x12!\n" + "\fcommit_limit\x18\x1e \x01(\x04R\vcommitLimit\x12!\n" + "\fcommitted_as\x18\x1f \x01(\x04R\vcommittedAs\x12#\n" + "\rvmalloc_total\x18 \x01(\x04R\fvmallocTotal\x12!\n" + "\fvmalloc_used\x18! \x01(\x04R\vvmallocUsed\x12#\n" + "\rvmalloc_chunk\x18\" \x01(\x04R\fvmallocChunk\x12-\n" + "\x12hardware_corrupted\x18# \x01(\x04R\x11hardwareCorrupted\x12&\n" + "\x0fanon_huge_pages\x18$ \x01(\x04R\ranonHugePages\x12(\n" + "\x10shmem_huge_pages\x18% \x01(\x04R\x0eshmemHugePages\x12(\n" + "\x10shmem_pmd_mapped\x18& \x01(\x04R\x0eshmemPmdMapped\x12\x1b\n" + "\tcma_total\x18' \x01(\x04R\bcmaTotal\x12\x19\n" + "\bcma_free\x18( \x01(\x04R\acmaFree\x12(\n" + "\x10huge_pages_total\x18) \x01(\x04R\x0ehugePagesTotal\x12&\n" + "\x0fhuge_pages_free\x18* \x01(\x04R\rhugePagesFree\x12&\n" + "\x0fhuge_pages_rsvd\x18+ \x01(\x04R\rhugePagesRsvd\x12&\n" + "\x0fhuge_pages_surp\x18, \x01(\x04R\rhugePagesSurp\x12\"\n" + "\fhugepagesize\x18- \x01(\x04R\fhugepagesize\x12!\n" + "\fdirect_map4k\x18. \x01(\x04R\vdirectMap4k\x12!\n" + "\fdirect_map2m\x18/ \x01(\x04R\vdirectMap2m\x12!\n" + "\fdirect_map1g\x180 \x01(\x04R\vdirectMap1gBr\n" + "'dev.talos.api.resource.definitions.perfZGgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/perfb\x06proto3" var ( file_resource_definitions_perf_perf_proto_rawDescOnce sync.Once file_resource_definitions_perf_perf_proto_rawDescData []byte ) func file_resource_definitions_perf_perf_proto_rawDescGZIP() []byte { file_resource_definitions_perf_perf_proto_rawDescOnce.Do(func() { file_resource_definitions_perf_perf_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_perf_perf_proto_rawDesc), len(file_resource_definitions_perf_perf_proto_rawDesc))) }) return file_resource_definitions_perf_perf_proto_rawDescData } var file_resource_definitions_perf_perf_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_resource_definitions_perf_perf_proto_goTypes = []any{ (*CPUSpec)(nil), // 0: talos.resource.definitions.perf.CPUSpec (*CPUStat)(nil), // 1: talos.resource.definitions.perf.CPUStat (*MemorySpec)(nil), // 2: talos.resource.definitions.perf.MemorySpec } var file_resource_definitions_perf_perf_proto_depIdxs = []int32{ 1, // 0: talos.resource.definitions.perf.CPUSpec.cpu:type_name -> talos.resource.definitions.perf.CPUStat 1, // 1: talos.resource.definitions.perf.CPUSpec.cpu_total:type_name -> talos.resource.definitions.perf.CPUStat 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_resource_definitions_perf_perf_proto_init() } func file_resource_definitions_perf_perf_proto_init() { if File_resource_definitions_perf_perf_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_perf_perf_proto_rawDesc), len(file_resource_definitions_perf_perf_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_perf_perf_proto_goTypes, DependencyIndexes: file_resource_definitions_perf_perf_proto_depIdxs, MessageInfos: file_resource_definitions_perf_perf_proto_msgTypes, }.Build() File_resource_definitions_perf_perf_proto = out.File file_resource_definitions_perf_perf_proto_goTypes = nil file_resource_definitions_perf_perf_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/perf/perf_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/perf/perf.proto package perf import ( binary "encoding/binary" fmt "fmt" io "io" math "math" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *CPUSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SoftIrqTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SoftIrqTotal)) i-- dAtA[i] = 0x40 } if m.ProcessBlocked != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessBlocked)) i-- dAtA[i] = 0x38 } if m.ProcessRunning != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessRunning)) i-- dAtA[i] = 0x30 } if m.ProcessCreated != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ProcessCreated)) i-- dAtA[i] = 0x28 } if m.ContextSwitches != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ContextSwitches)) i-- dAtA[i] = 0x20 } if m.IrqTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.IrqTotal)) i-- dAtA[i] = 0x18 } if m.CpuTotal != nil { size, err := m.CpuTotal.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Cpu) > 0 { for iNdEx := len(m.Cpu) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Cpu[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *CPUStat) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CPUStat) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CPUStat) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.GuestNice != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.GuestNice)))) i-- dAtA[i] = 0x51 } if m.Guest != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Guest)))) i-- dAtA[i] = 0x49 } if m.Steal != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Steal)))) i-- dAtA[i] = 0x41 } if m.SoftIrq != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.SoftIrq)))) i-- dAtA[i] = 0x39 } if m.Irq != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Irq)))) i-- dAtA[i] = 0x31 } if m.Iowait != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Iowait)))) i-- dAtA[i] = 0x29 } if m.Idle != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Idle)))) i-- dAtA[i] = 0x21 } if m.System != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.System)))) i-- dAtA[i] = 0x19 } if m.Nice != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Nice)))) i-- dAtA[i] = 0x11 } if m.User != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.User)))) i-- dAtA[i] = 0x9 } return len(dAtA) - i, nil } func (m *MemorySpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MemorySpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MemorySpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.DirectMap1G != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DirectMap1G)) i-- dAtA[i] = 0x3 i-- dAtA[i] = 0x80 } if m.DirectMap2M != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DirectMap2M)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xf8 } if m.DirectMap4K != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.DirectMap4K)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xf0 } if m.Hugepagesize != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Hugepagesize)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xe8 } if m.HugePagesSurp != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HugePagesSurp)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xe0 } if m.HugePagesRsvd != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HugePagesRsvd)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xd8 } if m.HugePagesFree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HugePagesFree)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xd0 } if m.HugePagesTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HugePagesTotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xc8 } if m.CmaFree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CmaFree)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xc0 } if m.CmaTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CmaTotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xb8 } if m.ShmemPmdMapped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ShmemPmdMapped)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xb0 } if m.ShmemHugePages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ShmemHugePages)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xa8 } if m.AnonHugePages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AnonHugePages)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0xa0 } if m.HardwareCorrupted != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HardwareCorrupted)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x98 } if m.VmallocChunk != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.VmallocChunk)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x90 } if m.VmallocUsed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.VmallocUsed)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x88 } if m.VmallocTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.VmallocTotal)) i-- dAtA[i] = 0x2 i-- dAtA[i] = 0x80 } if m.CommittedAs != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CommittedAs)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xf8 } if m.CommitLimit != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.CommitLimit)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xf0 } if m.WritebackTmp != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.WritebackTmp)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe8 } if m.Bounce != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Bounce)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xe0 } if m.NfSunstable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.NfSunstable)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd8 } if m.PageTables != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.PageTables)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xd0 } if m.KernelStack != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.KernelStack)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc8 } if m.SUnreclaim != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SUnreclaim)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xc0 } if m.SReclaimable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SReclaimable)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb8 } if m.Slab != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Slab)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xb0 } if m.Shmem != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Shmem)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa8 } if m.Mapped != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mapped)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0xa0 } if m.AnonPages != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.AnonPages)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x98 } if m.Writeback != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Writeback)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x90 } if m.Dirty != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Dirty)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x88 } if m.SwapFree != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SwapFree)) i-- dAtA[i] = 0x1 i-- dAtA[i] = 0x80 } if m.SwapTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SwapTotal)) i-- dAtA[i] = 0x78 } if m.Mlocked != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mlocked)) i-- dAtA[i] = 0x70 } if m.Unevictable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Unevictable)) i-- dAtA[i] = 0x68 } if m.InactiveFile != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.InactiveFile)) i-- dAtA[i] = 0x60 } if m.ActiveFile != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ActiveFile)) i-- dAtA[i] = 0x58 } if m.InactiveAnon != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.InactiveAnon)) i-- dAtA[i] = 0x50 } if m.ActiveAnon != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ActiveAnon)) i-- dAtA[i] = 0x48 } if m.Inactive != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Inactive)) i-- dAtA[i] = 0x40 } if m.Active != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Active)) i-- dAtA[i] = 0x38 } if m.SwapCached != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SwapCached)) i-- dAtA[i] = 0x30 } if m.Cached != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Cached)) i-- dAtA[i] = 0x28 } if m.Buffers != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Buffers)) i-- dAtA[i] = 0x20 } if m.MemAvailable != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemAvailable)) i-- dAtA[i] = 0x18 } if m.MemUsed != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemUsed)) i-- dAtA[i] = 0x10 } if m.MemTotal != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.MemTotal)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *CPUSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Cpu) > 0 { for _, e := range m.Cpu { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.CpuTotal != nil { l = m.CpuTotal.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.IrqTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.IrqTotal)) } if m.ContextSwitches != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ContextSwitches)) } if m.ProcessCreated != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessCreated)) } if m.ProcessRunning != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessRunning)) } if m.ProcessBlocked != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ProcessBlocked)) } if m.SoftIrqTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SoftIrqTotal)) } n += len(m.unknownFields) return n } func (m *CPUStat) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.User != 0 { n += 9 } if m.Nice != 0 { n += 9 } if m.System != 0 { n += 9 } if m.Idle != 0 { n += 9 } if m.Iowait != 0 { n += 9 } if m.Irq != 0 { n += 9 } if m.SoftIrq != 0 { n += 9 } if m.Steal != 0 { n += 9 } if m.Guest != 0 { n += 9 } if m.GuestNice != 0 { n += 9 } n += len(m.unknownFields) return n } func (m *MemorySpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.MemTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemTotal)) } if m.MemUsed != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemUsed)) } if m.MemAvailable != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.MemAvailable)) } if m.Buffers != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Buffers)) } if m.Cached != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Cached)) } if m.SwapCached != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SwapCached)) } if m.Active != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Active)) } if m.Inactive != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Inactive)) } if m.ActiveAnon != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ActiveAnon)) } if m.InactiveAnon != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.InactiveAnon)) } if m.ActiveFile != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ActiveFile)) } if m.InactiveFile != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.InactiveFile)) } if m.Unevictable != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Unevictable)) } if m.Mlocked != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mlocked)) } if m.SwapTotal != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SwapTotal)) } if m.SwapFree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.SwapFree)) } if m.Dirty != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Dirty)) } if m.Writeback != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Writeback)) } if m.AnonPages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AnonPages)) } if m.Mapped != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Mapped)) } if m.Shmem != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Shmem)) } if m.Slab != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Slab)) } if m.SReclaimable != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.SReclaimable)) } if m.SUnreclaim != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.SUnreclaim)) } if m.KernelStack != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.KernelStack)) } if m.PageTables != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.PageTables)) } if m.NfSunstable != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.NfSunstable)) } if m.Bounce != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Bounce)) } if m.WritebackTmp != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.WritebackTmp)) } if m.CommitLimit != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CommitLimit)) } if m.CommittedAs != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CommittedAs)) } if m.VmallocTotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.VmallocTotal)) } if m.VmallocUsed != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.VmallocUsed)) } if m.VmallocChunk != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.VmallocChunk)) } if m.HardwareCorrupted != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.HardwareCorrupted)) } if m.AnonHugePages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.AnonHugePages)) } if m.ShmemHugePages != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.ShmemHugePages)) } if m.ShmemPmdMapped != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.ShmemPmdMapped)) } if m.CmaTotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CmaTotal)) } if m.CmaFree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.CmaFree)) } if m.HugePagesTotal != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.HugePagesTotal)) } if m.HugePagesFree != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.HugePagesFree)) } if m.HugePagesRsvd != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.HugePagesRsvd)) } if m.HugePagesSurp != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.HugePagesSurp)) } if m.Hugepagesize != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.Hugepagesize)) } if m.DirectMap4K != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.DirectMap4K)) } if m.DirectMap2M != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.DirectMap2M)) } if m.DirectMap1G != 0 { n += 2 + protohelpers.SizeOfVarint(uint64(m.DirectMap1G)) } n += len(m.unknownFields) return n } func (m *CPUSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Cpu", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Cpu = append(m.Cpu, &CPUStat{}) if err := m.Cpu[len(m.Cpu)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpuTotal", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.CpuTotal == nil { m.CpuTotal = &CPUStat{} } if err := m.CpuTotal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IrqTotal", wireType) } m.IrqTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.IrqTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ContextSwitches", wireType) } m.ContextSwitches = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ContextSwitches |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessCreated", wireType) } m.ProcessCreated = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessCreated |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessRunning", wireType) } m.ProcessRunning = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessRunning |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ProcessBlocked", wireType) } m.ProcessBlocked = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ProcessBlocked |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SoftIrqTotal", wireType) } m.SoftIrqTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SoftIrqTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CPUStat) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CPUStat: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CPUStat: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field User", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.User = float64(math.Float64frombits(v)) case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Nice", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Nice = float64(math.Float64frombits(v)) case 3: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field System", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.System = float64(math.Float64frombits(v)) case 4: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Idle", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Idle = float64(math.Float64frombits(v)) case 5: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Iowait", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Iowait = float64(math.Float64frombits(v)) case 6: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Irq", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Irq = float64(math.Float64frombits(v)) case 7: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field SoftIrq", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.SoftIrq = float64(math.Float64frombits(v)) case 8: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Steal", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Steal = float64(math.Float64frombits(v)) case 9: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Guest", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Guest = float64(math.Float64frombits(v)) case 10: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field GuestNice", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.GuestNice = float64(math.Float64frombits(v)) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MemorySpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MemorySpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MemorySpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemTotal", wireType) } m.MemTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemUsed", wireType) } m.MemUsed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemUsed |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field MemAvailable", wireType) } m.MemAvailable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.MemAvailable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Buffers", wireType) } m.Buffers = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Buffers |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Cached", wireType) } m.Cached = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Cached |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SwapCached", wireType) } m.SwapCached = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SwapCached |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Active", wireType) } m.Active = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Active |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Inactive", wireType) } m.Inactive = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Inactive |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ActiveAnon", wireType) } m.ActiveAnon = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ActiveAnon |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field InactiveAnon", wireType) } m.InactiveAnon = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.InactiveAnon |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ActiveFile", wireType) } m.ActiveFile = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ActiveFile |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 12: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field InactiveFile", wireType) } m.InactiveFile = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.InactiveFile |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unevictable", wireType) } m.Unevictable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Unevictable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 14: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mlocked", wireType) } m.Mlocked = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mlocked |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 15: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SwapTotal", wireType) } m.SwapTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SwapTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 16: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SwapFree", wireType) } m.SwapFree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SwapFree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 17: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Dirty", wireType) } m.Dirty = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Dirty |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 18: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Writeback", wireType) } m.Writeback = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Writeback |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 19: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AnonPages", wireType) } m.AnonPages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AnonPages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 20: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mapped", wireType) } m.Mapped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mapped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 21: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Shmem", wireType) } m.Shmem = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Shmem |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 22: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Slab", wireType) } m.Slab = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Slab |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 23: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SReclaimable", wireType) } m.SReclaimable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SReclaimable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 24: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SUnreclaim", wireType) } m.SUnreclaim = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SUnreclaim |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 25: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field KernelStack", wireType) } m.KernelStack = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.KernelStack |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 26: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field PageTables", wireType) } m.PageTables = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.PageTables |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 27: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NfSunstable", wireType) } m.NfSunstable = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.NfSunstable |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 28: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Bounce", wireType) } m.Bounce = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Bounce |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 29: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field WritebackTmp", wireType) } m.WritebackTmp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.WritebackTmp |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 30: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CommitLimit", wireType) } m.CommitLimit = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CommitLimit |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 31: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CommittedAs", wireType) } m.CommittedAs = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CommittedAs |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 32: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field VmallocTotal", wireType) } m.VmallocTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.VmallocTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 33: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field VmallocUsed", wireType) } m.VmallocUsed = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.VmallocUsed |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 34: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field VmallocChunk", wireType) } m.VmallocChunk = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.VmallocChunk |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 35: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HardwareCorrupted", wireType) } m.HardwareCorrupted = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HardwareCorrupted |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 36: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field AnonHugePages", wireType) } m.AnonHugePages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.AnonHugePages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 37: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShmemHugePages", wireType) } m.ShmemHugePages = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShmemHugePages |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 38: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ShmemPmdMapped", wireType) } m.ShmemPmdMapped = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ShmemPmdMapped |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 39: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CmaTotal", wireType) } m.CmaTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CmaTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 40: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CmaFree", wireType) } m.CmaFree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.CmaFree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 41: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HugePagesTotal", wireType) } m.HugePagesTotal = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HugePagesTotal |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 42: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HugePagesFree", wireType) } m.HugePagesFree = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HugePagesFree |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 43: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HugePagesRsvd", wireType) } m.HugePagesRsvd = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HugePagesRsvd |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 44: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HugePagesSurp", wireType) } m.HugePagesSurp = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HugePagesSurp |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 45: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Hugepagesize", wireType) } m.Hugepagesize = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Hugepagesize |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 46: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DirectMap4K", wireType) } m.DirectMap4K = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DirectMap4K |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 47: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DirectMap2M", wireType) } m.DirectMap2M = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DirectMap2M |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 48: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DirectMap1G", wireType) } m.DirectMap1G = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.DirectMap1G |= uint64(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/proto/proto.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/proto/proto.proto package proto import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // LinuxIDMapping specifies UID/GID mappings. type LinuxIDMapping struct { state protoimpl.MessageState `protogen:"open.v1"` ContainerId uint32 `protobuf:"varint,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` HostId uint32 `protobuf:"varint,2,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` Size uint32 `protobuf:"varint,3,opt,name=size,proto3" json:"size,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LinuxIDMapping) Reset() { *x = LinuxIDMapping{} mi := &file_resource_definitions_proto_proto_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LinuxIDMapping) String() string { return protoimpl.X.MessageStringOf(x) } func (*LinuxIDMapping) ProtoMessage() {} func (x *LinuxIDMapping) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_proto_proto_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LinuxIDMapping.ProtoReflect.Descriptor instead. func (*LinuxIDMapping) Descriptor() ([]byte, []int) { return file_resource_definitions_proto_proto_proto_rawDescGZIP(), []int{0} } func (x *LinuxIDMapping) GetContainerId() uint32 { if x != nil { return x.ContainerId } return 0 } func (x *LinuxIDMapping) GetHostId() uint32 { if x != nil { return x.HostId } return 0 } func (x *LinuxIDMapping) GetSize() uint32 { if x != nil { return x.Size } return 0 } // Mount specifies a mount for a container. type Mount struct { state protoimpl.MessageState `protogen:"open.v1"` Destination string `protobuf:"bytes,1,opt,name=destination,proto3" json:"destination,omitempty"` Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` Source string `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` Options []string `protobuf:"bytes,4,rep,name=options,proto3" json:"options,omitempty"` UidMappings []*LinuxIDMapping `protobuf:"bytes,5,rep,name=uid_mappings,json=uidMappings,proto3" json:"uid_mappings,omitempty"` GidMappings []*LinuxIDMapping `protobuf:"bytes,6,rep,name=gid_mappings,json=gidMappings,proto3" json:"gid_mappings,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Mount) Reset() { *x = Mount{} mi := &file_resource_definitions_proto_proto_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Mount) String() string { return protoimpl.X.MessageStringOf(x) } func (*Mount) ProtoMessage() {} func (x *Mount) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_proto_proto_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Mount.ProtoReflect.Descriptor instead. func (*Mount) Descriptor() ([]byte, []int) { return file_resource_definitions_proto_proto_proto_rawDescGZIP(), []int{1} } func (x *Mount) GetDestination() string { if x != nil { return x.Destination } return "" } func (x *Mount) GetType() string { if x != nil { return x.Type } return "" } func (x *Mount) GetSource() string { if x != nil { return x.Source } return "" } func (x *Mount) GetOptions() []string { if x != nil { return x.Options } return nil } func (x *Mount) GetUidMappings() []*LinuxIDMapping { if x != nil { return x.UidMappings } return nil } func (x *Mount) GetGidMappings() []*LinuxIDMapping { if x != nil { return x.GidMappings } return nil } var File_resource_definitions_proto_proto_proto protoreflect.FileDescriptor const file_resource_definitions_proto_proto_proto_rawDesc = "" + "\n" + "&resource/definitions/proto/proto.proto\x12 talos.resource.definitions.proto\"`\n" + "\x0eLinuxIDMapping\x12!\n" + "\fcontainer_id\x18\x01 \x01(\rR\vcontainerId\x12\x17\n" + "\ahost_id\x18\x02 \x01(\rR\x06hostId\x12\x12\n" + "\x04size\x18\x03 \x01(\rR\x04size\"\x99\x02\n" + "\x05Mount\x12 \n" + "\vdestination\x18\x01 \x01(\tR\vdestination\x12\x12\n" + "\x04type\x18\x02 \x01(\tR\x04type\x12\x16\n" + "\x06source\x18\x03 \x01(\tR\x06source\x12\x18\n" + "\aoptions\x18\x04 \x03(\tR\aoptions\x12S\n" + "\fuid_mappings\x18\x05 \x03(\v20.talos.resource.definitions.proto.LinuxIDMappingR\vuidMappings\x12S\n" + "\fgid_mappings\x18\x06 \x03(\v20.talos.resource.definitions.proto.LinuxIDMappingR\vgidMappingsBt\n" + "(dev.talos.api.resource.definitions.protoZHgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/protob\x06proto3" var ( file_resource_definitions_proto_proto_proto_rawDescOnce sync.Once file_resource_definitions_proto_proto_proto_rawDescData []byte ) func file_resource_definitions_proto_proto_proto_rawDescGZIP() []byte { file_resource_definitions_proto_proto_proto_rawDescOnce.Do(func() { file_resource_definitions_proto_proto_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_proto_proto_proto_rawDesc), len(file_resource_definitions_proto_proto_proto_rawDesc))) }) return file_resource_definitions_proto_proto_proto_rawDescData } var file_resource_definitions_proto_proto_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_resource_definitions_proto_proto_proto_goTypes = []any{ (*LinuxIDMapping)(nil), // 0: talos.resource.definitions.proto.LinuxIDMapping (*Mount)(nil), // 1: talos.resource.definitions.proto.Mount } var file_resource_definitions_proto_proto_proto_depIdxs = []int32{ 0, // 0: talos.resource.definitions.proto.Mount.uid_mappings:type_name -> talos.resource.definitions.proto.LinuxIDMapping 0, // 1: talos.resource.definitions.proto.Mount.gid_mappings:type_name -> talos.resource.definitions.proto.LinuxIDMapping 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name } func init() { file_resource_definitions_proto_proto_proto_init() } func file_resource_definitions_proto_proto_proto_init() { if File_resource_definitions_proto_proto_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_proto_proto_proto_rawDesc), len(file_resource_definitions_proto_proto_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_proto_proto_proto_goTypes, DependencyIndexes: file_resource_definitions_proto_proto_proto_depIdxs, MessageInfos: file_resource_definitions_proto_proto_proto_msgTypes, }.Build() File_resource_definitions_proto_proto_proto = out.File file_resource_definitions_proto_proto_proto_goTypes = nil file_resource_definitions_proto_proto_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/proto/proto_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/proto/proto.proto package proto import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *LinuxIDMapping) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LinuxIDMapping) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LinuxIDMapping) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x18 } if m.HostId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.HostId)) i-- dAtA[i] = 0x10 } if m.ContainerId != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ContainerId)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Mount) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Mount) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Mount) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.GidMappings) > 0 { for iNdEx := len(m.GidMappings) - 1; iNdEx >= 0; iNdEx-- { size, err := m.GidMappings[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } } if len(m.UidMappings) > 0 { for iNdEx := len(m.UidMappings) - 1; iNdEx >= 0; iNdEx-- { size, err := m.UidMappings[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } } if len(m.Options) > 0 { for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Options[iNdEx]) copy(dAtA[i:], m.Options[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Options[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.Source) > 0 { i -= len(m.Source) copy(dAtA[i:], m.Source) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Source))) i-- dAtA[i] = 0x1a } if len(m.Type) > 0 { i -= len(m.Type) copy(dAtA[i:], m.Type) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Type))) i-- dAtA[i] = 0x12 } if len(m.Destination) > 0 { i -= len(m.Destination) copy(dAtA[i:], m.Destination) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Destination))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *LinuxIDMapping) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.ContainerId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ContainerId)) } if m.HostId != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.HostId)) } if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } n += len(m.unknownFields) return n } func (m *Mount) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Destination) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Type) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Source) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Options) > 0 { for _, s := range m.Options { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.UidMappings) > 0 { for _, e := range m.UidMappings { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.GidMappings) > 0 { for _, e := range m.GidMappings { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *LinuxIDMapping) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LinuxIDMapping: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LinuxIDMapping: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ContainerId", wireType) } m.ContainerId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ContainerId |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field HostId", wireType) } m.HostId = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.HostId |= uint32(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint32(b&0x7F) << shift if b < 0x80 { break } } default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Mount) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Mount: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Mount: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Destination", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Destination = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Type = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Source = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Options = append(m.Options, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UidMappings", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UidMappings = append(m.UidMappings, &LinuxIDMapping{}) if err := m.UidMappings[len(m.UidMappings)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field GidMappings", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.GidMappings = append(m.GidMappings, &LinuxIDMapping{}) if err := m.GidMappings[len(m.GidMappings)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/runtime/runtime.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/runtime/runtime.proto package runtime import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // APIServiceConfigSpec describes configuration for Talos API service (apid). type APIServiceConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ListenAddress string `protobuf:"bytes,1,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` NodeRoutingDisabled bool `protobuf:"varint,2,opt,name=node_routing_disabled,json=nodeRoutingDisabled,proto3" json:"node_routing_disabled,omitempty"` ReadonlyRoleMode bool `protobuf:"varint,3,opt,name=readonly_role_mode,json=readonlyRoleMode,proto3" json:"readonly_role_mode,omitempty"` SkipVerifyingClientCert bool `protobuf:"varint,4,opt,name=skip_verifying_client_cert,json=skipVerifyingClientCert,proto3" json:"skip_verifying_client_cert,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *APIServiceConfigSpec) Reset() { *x = APIServiceConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *APIServiceConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*APIServiceConfigSpec) ProtoMessage() {} func (x *APIServiceConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use APIServiceConfigSpec.ProtoReflect.Descriptor instead. func (*APIServiceConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{0} } func (x *APIServiceConfigSpec) GetListenAddress() string { if x != nil { return x.ListenAddress } return "" } func (x *APIServiceConfigSpec) GetNodeRoutingDisabled() bool { if x != nil { return x.NodeRoutingDisabled } return false } func (x *APIServiceConfigSpec) GetReadonlyRoleMode() bool { if x != nil { return x.ReadonlyRoleMode } return false } func (x *APIServiceConfigSpec) GetSkipVerifyingClientCert() bool { if x != nil { return x.SkipVerifyingClientCert } return false } // BootedEntrySpec describes the booted entry resource properties. type BootedEntrySpec struct { state protoimpl.MessageState `protogen:"open.v1"` BootedEntry string `protobuf:"bytes,1,opt,name=booted_entry,json=bootedEntry,proto3" json:"booted_entry,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BootedEntrySpec) Reset() { *x = BootedEntrySpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BootedEntrySpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*BootedEntrySpec) ProtoMessage() {} func (x *BootedEntrySpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BootedEntrySpec.ProtoReflect.Descriptor instead. func (*BootedEntrySpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{1} } func (x *BootedEntrySpec) GetBootedEntry() string { if x != nil { return x.BootedEntry } return "" } // DevicesStatusSpec is the spec for devices status. type DevicesStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DevicesStatusSpec) Reset() { *x = DevicesStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DevicesStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DevicesStatusSpec) ProtoMessage() {} func (x *DevicesStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DevicesStatusSpec.ProtoReflect.Descriptor instead. func (*DevicesStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{2} } func (x *DevicesStatusSpec) GetReady() bool { if x != nil { return x.Ready } return false } // DiagnosticSpec is the spec for devices status. type DiagnosticSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` Details []string `protobuf:"bytes,2,rep,name=details,proto3" json:"details,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DiagnosticSpec) Reset() { *x = DiagnosticSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DiagnosticSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DiagnosticSpec) ProtoMessage() {} func (x *DiagnosticSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DiagnosticSpec.ProtoReflect.Descriptor instead. func (*DiagnosticSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{3} } func (x *DiagnosticSpec) GetMessage() string { if x != nil { return x.Message } return "" } func (x *DiagnosticSpec) GetDetails() []string { if x != nil { return x.Details } return nil } // EnvironmentSpec describes the specification of Environment resource. type EnvironmentSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Variables []string `protobuf:"bytes,1,rep,name=variables,proto3" json:"variables,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EnvironmentSpec) Reset() { *x = EnvironmentSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EnvironmentSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EnvironmentSpec) ProtoMessage() {} func (x *EnvironmentSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EnvironmentSpec.ProtoReflect.Descriptor instead. func (*EnvironmentSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{4} } func (x *EnvironmentSpec) GetVariables() []string { if x != nil { return x.Variables } return nil } // EventSinkConfigSpec describes configuration of Talos event log streaming. type EventSinkConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint string `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EventSinkConfigSpec) Reset() { *x = EventSinkConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EventSinkConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EventSinkConfigSpec) ProtoMessage() {} func (x *EventSinkConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EventSinkConfigSpec.ProtoReflect.Descriptor instead. func (*EventSinkConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{5} } func (x *EventSinkConfigSpec) GetEndpoint() string { if x != nil { return x.Endpoint } return "" } // ExtensionServiceConfigFile describes extensions service config files. type ExtensionServiceConfigFile struct { state protoimpl.MessageState `protogen:"open.v1"` Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"` MountPath string `protobuf:"bytes,2,opt,name=mount_path,json=mountPath,proto3" json:"mount_path,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtensionServiceConfigFile) Reset() { *x = ExtensionServiceConfigFile{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtensionServiceConfigFile) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtensionServiceConfigFile) ProtoMessage() {} func (x *ExtensionServiceConfigFile) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtensionServiceConfigFile.ProtoReflect.Descriptor instead. func (*ExtensionServiceConfigFile) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{6} } func (x *ExtensionServiceConfigFile) GetContent() string { if x != nil { return x.Content } return "" } func (x *ExtensionServiceConfigFile) GetMountPath() string { if x != nil { return x.MountPath } return "" } // ExtensionServiceConfigSpec describes status of rendered extensions service config files. type ExtensionServiceConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Files []*ExtensionServiceConfigFile `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"` Environment []string `protobuf:"bytes,2,rep,name=environment,proto3" json:"environment,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtensionServiceConfigSpec) Reset() { *x = ExtensionServiceConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtensionServiceConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtensionServiceConfigSpec) ProtoMessage() {} func (x *ExtensionServiceConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtensionServiceConfigSpec.ProtoReflect.Descriptor instead. func (*ExtensionServiceConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{7} } func (x *ExtensionServiceConfigSpec) GetFiles() []*ExtensionServiceConfigFile { if x != nil { return x.Files } return nil } func (x *ExtensionServiceConfigSpec) GetEnvironment() []string { if x != nil { return x.Environment } return nil } // ExtensionServiceConfigStatusSpec describes status of rendered extensions service config files. type ExtensionServiceConfigStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` SpecVersion string `protobuf:"bytes,1,opt,name=spec_version,json=specVersion,proto3" json:"spec_version,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ExtensionServiceConfigStatusSpec) Reset() { *x = ExtensionServiceConfigStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ExtensionServiceConfigStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ExtensionServiceConfigStatusSpec) ProtoMessage() {} func (x *ExtensionServiceConfigStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ExtensionServiceConfigStatusSpec.ProtoReflect.Descriptor instead. func (*ExtensionServiceConfigStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{8} } func (x *ExtensionServiceConfigStatusSpec) GetSpecVersion() string { if x != nil { return x.SpecVersion } return "" } // KernelCmdlineSpec presents kernel command line (contents of /proc/cmdline). type KernelCmdlineSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Cmdline string `protobuf:"bytes,1,opt,name=cmdline,proto3" json:"cmdline,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KernelCmdlineSpec) Reset() { *x = KernelCmdlineSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KernelCmdlineSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KernelCmdlineSpec) ProtoMessage() {} func (x *KernelCmdlineSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KernelCmdlineSpec.ProtoReflect.Descriptor instead. func (*KernelCmdlineSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{9} } func (x *KernelCmdlineSpec) GetCmdline() string { if x != nil { return x.Cmdline } return "" } // KernelModuleSpecSpec describes Linux kernel module to load. type KernelModuleSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Parameters []string `protobuf:"bytes,2,rep,name=parameters,proto3" json:"parameters,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KernelModuleSpecSpec) Reset() { *x = KernelModuleSpecSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KernelModuleSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KernelModuleSpecSpec) ProtoMessage() {} func (x *KernelModuleSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KernelModuleSpecSpec.ProtoReflect.Descriptor instead. func (*KernelModuleSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{10} } func (x *KernelModuleSpecSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *KernelModuleSpecSpec) GetParameters() []string { if x != nil { return x.Parameters } return nil } // KernelParamSpecSpec describes status of the defined sysctls. type KernelParamSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` IgnoreErrors bool `protobuf:"varint,2,opt,name=ignore_errors,json=ignoreErrors,proto3" json:"ignore_errors,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KernelParamSpecSpec) Reset() { *x = KernelParamSpecSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KernelParamSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KernelParamSpecSpec) ProtoMessage() {} func (x *KernelParamSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KernelParamSpecSpec.ProtoReflect.Descriptor instead. func (*KernelParamSpecSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{11} } func (x *KernelParamSpecSpec) GetValue() string { if x != nil { return x.Value } return "" } func (x *KernelParamSpecSpec) GetIgnoreErrors() bool { if x != nil { return x.IgnoreErrors } return false } // KernelParamStatusSpec describes status of the defined sysctls. type KernelParamStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Current string `protobuf:"bytes,1,opt,name=current,proto3" json:"current,omitempty"` Default string `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"` Unsupported bool `protobuf:"varint,3,opt,name=unsupported,proto3" json:"unsupported,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KernelParamStatusSpec) Reset() { *x = KernelParamStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KernelParamStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KernelParamStatusSpec) ProtoMessage() {} func (x *KernelParamStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KernelParamStatusSpec.ProtoReflect.Descriptor instead. func (*KernelParamStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{12} } func (x *KernelParamStatusSpec) GetCurrent() string { if x != nil { return x.Current } return "" } func (x *KernelParamStatusSpec) GetDefault() string { if x != nil { return x.Default } return "" } func (x *KernelParamStatusSpec) GetUnsupported() bool { if x != nil { return x.Unsupported } return false } // KmsgLogConfigSpec describes configuration for kmsg log streaming. type KmsgLogConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Destinations []*common.URL `protobuf:"bytes,1,rep,name=destinations,proto3" json:"destinations,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KmsgLogConfigSpec) Reset() { *x = KmsgLogConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KmsgLogConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KmsgLogConfigSpec) ProtoMessage() {} func (x *KmsgLogConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KmsgLogConfigSpec.ProtoReflect.Descriptor instead. func (*KmsgLogConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{13} } func (x *KmsgLogConfigSpec) GetDestinations() []*common.URL { if x != nil { return x.Destinations } return nil } // LoadedKernelModuleSpec describes Linux kernel module to load. type LoadedKernelModuleSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Size int64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` ReferenceCount int64 `protobuf:"varint,2,opt,name=reference_count,json=referenceCount,proto3" json:"reference_count,omitempty"` Dependencies []string `protobuf:"bytes,3,rep,name=dependencies,proto3" json:"dependencies,omitempty"` State string `protobuf:"bytes,4,opt,name=state,proto3" json:"state,omitempty"` Address string `protobuf:"bytes,5,opt,name=address,proto3" json:"address,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *LoadedKernelModuleSpec) Reset() { *x = LoadedKernelModuleSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *LoadedKernelModuleSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*LoadedKernelModuleSpec) ProtoMessage() {} func (x *LoadedKernelModuleSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use LoadedKernelModuleSpec.ProtoReflect.Descriptor instead. func (*LoadedKernelModuleSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{14} } func (x *LoadedKernelModuleSpec) GetSize() int64 { if x != nil { return x.Size } return 0 } func (x *LoadedKernelModuleSpec) GetReferenceCount() int64 { if x != nil { return x.ReferenceCount } return 0 } func (x *LoadedKernelModuleSpec) GetDependencies() []string { if x != nil { return x.Dependencies } return nil } func (x *LoadedKernelModuleSpec) GetState() string { if x != nil { return x.State } return "" } func (x *LoadedKernelModuleSpec) GetAddress() string { if x != nil { return x.Address } return "" } // MachineStatusSpec describes status of the defined sysctls. type MachineStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Stage enums.RuntimeMachineStage `protobuf:"varint,1,opt,name=stage,proto3,enum=talos.resource.definitions.enums.RuntimeMachineStage" json:"stage,omitempty"` Status *MachineStatusStatus `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineStatusSpec) Reset() { *x = MachineStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineStatusSpec) ProtoMessage() {} func (x *MachineStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineStatusSpec.ProtoReflect.Descriptor instead. func (*MachineStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{15} } func (x *MachineStatusSpec) GetStage() enums.RuntimeMachineStage { if x != nil { return x.Stage } return enums.RuntimeMachineStage(0) } func (x *MachineStatusSpec) GetStatus() *MachineStatusStatus { if x != nil { return x.Status } return nil } // MachineStatusStatus describes machine current status at the stage. type MachineStatusStatus struct { state protoimpl.MessageState `protogen:"open.v1"` Ready bool `protobuf:"varint,1,opt,name=ready,proto3" json:"ready,omitempty"` UnmetConditions []*UnmetCondition `protobuf:"bytes,2,rep,name=unmet_conditions,json=unmetConditions,proto3" json:"unmet_conditions,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MachineStatusStatus) Reset() { *x = MachineStatusStatus{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MachineStatusStatus) String() string { return protoimpl.X.MessageStringOf(x) } func (*MachineStatusStatus) ProtoMessage() {} func (x *MachineStatusStatus) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MachineStatusStatus.ProtoReflect.Descriptor instead. func (*MachineStatusStatus) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{16} } func (x *MachineStatusStatus) GetReady() bool { if x != nil { return x.Ready } return false } func (x *MachineStatusStatus) GetUnmetConditions() []*UnmetCondition { if x != nil { return x.UnmetConditions } return nil } // MaintenanceServiceConfigSpec describes configuration for maintenance service API. type MaintenanceServiceConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ListenAddress string `protobuf:"bytes,1,opt,name=listen_address,json=listenAddress,proto3" json:"listen_address,omitempty"` ReachableAddresses []*common.NetIP `protobuf:"bytes,2,rep,name=reachable_addresses,json=reachableAddresses,proto3" json:"reachable_addresses,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MaintenanceServiceConfigSpec) Reset() { *x = MaintenanceServiceConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MaintenanceServiceConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MaintenanceServiceConfigSpec) ProtoMessage() {} func (x *MaintenanceServiceConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MaintenanceServiceConfigSpec.ProtoReflect.Descriptor instead. func (*MaintenanceServiceConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{17} } func (x *MaintenanceServiceConfigSpec) GetListenAddress() string { if x != nil { return x.ListenAddress } return "" } func (x *MaintenanceServiceConfigSpec) GetReachableAddresses() []*common.NetIP { if x != nil { return x.ReachableAddresses } return nil } // MetaKeySpec describes status of the defined sysctls. type MetaKeySpec struct { state protoimpl.MessageState `protogen:"open.v1"` Value string `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaKeySpec) Reset() { *x = MetaKeySpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaKeySpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaKeySpec) ProtoMessage() {} func (x *MetaKeySpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaKeySpec.ProtoReflect.Descriptor instead. func (*MetaKeySpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{18} } func (x *MetaKeySpec) GetValue() string { if x != nil { return x.Value } return "" } // MetaLoadedSpec is the spec for meta loaded. The Done field is always true when resource exists. type MetaLoadedSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Done bool `protobuf:"varint,1,opt,name=done,proto3" json:"done,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MetaLoadedSpec) Reset() { *x = MetaLoadedSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MetaLoadedSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MetaLoadedSpec) ProtoMessage() {} func (x *MetaLoadedSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MetaLoadedSpec.ProtoReflect.Descriptor instead. func (*MetaLoadedSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{19} } func (x *MetaLoadedSpec) GetDone() bool { if x != nil { return x.Done } return false } // MountStatusSpec describes status of the defined sysctls. type MountStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` FilesystemType string `protobuf:"bytes,3,opt,name=filesystem_type,json=filesystemType,proto3" json:"filesystem_type,omitempty"` Options []string `protobuf:"bytes,4,rep,name=options,proto3" json:"options,omitempty"` Encrypted bool `protobuf:"varint,5,opt,name=encrypted,proto3" json:"encrypted,omitempty"` EncryptionProviders []string `protobuf:"bytes,6,rep,name=encryption_providers,json=encryptionProviders,proto3" json:"encryption_providers,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MountStatusSpec) Reset() { *x = MountStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MountStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MountStatusSpec) ProtoMessage() {} func (x *MountStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MountStatusSpec.ProtoReflect.Descriptor instead. func (*MountStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{20} } func (x *MountStatusSpec) GetSource() string { if x != nil { return x.Source } return "" } func (x *MountStatusSpec) GetTarget() string { if x != nil { return x.Target } return "" } func (x *MountStatusSpec) GetFilesystemType() string { if x != nil { return x.FilesystemType } return "" } func (x *MountStatusSpec) GetOptions() []string { if x != nil { return x.Options } return nil } func (x *MountStatusSpec) GetEncrypted() bool { if x != nil { return x.Encrypted } return false } func (x *MountStatusSpec) GetEncryptionProviders() []string { if x != nil { return x.EncryptionProviders } return nil } // OOMActionSpec describes the OOM action record resource properties. type OOMActionSpec struct { state protoimpl.MessageState `protogen:"open.v1"` TriggerContext string `protobuf:"bytes,1,opt,name=trigger_context,json=triggerContext,proto3" json:"trigger_context,omitempty"` Score float64 `protobuf:"fixed64,2,opt,name=score,proto3" json:"score,omitempty"` Processes []string `protobuf:"bytes,3,rep,name=processes,proto3" json:"processes,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OOMActionSpec) Reset() { *x = OOMActionSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OOMActionSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*OOMActionSpec) ProtoMessage() {} func (x *OOMActionSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OOMActionSpec.ProtoReflect.Descriptor instead. func (*OOMActionSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{21} } func (x *OOMActionSpec) GetTriggerContext() string { if x != nil { return x.TriggerContext } return "" } func (x *OOMActionSpec) GetScore() float64 { if x != nil { return x.Score } return 0 } func (x *OOMActionSpec) GetProcesses() []string { if x != nil { return x.Processes } return nil } // PlatformMetadataSpec describes platform metadata properties. type PlatformMetadataSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Platform string `protobuf:"bytes,1,opt,name=platform,proto3" json:"platform,omitempty"` Hostname string `protobuf:"bytes,2,opt,name=hostname,proto3" json:"hostname,omitempty"` Region string `protobuf:"bytes,3,opt,name=region,proto3" json:"region,omitempty"` Zone string `protobuf:"bytes,4,opt,name=zone,proto3" json:"zone,omitempty"` InstanceType string `protobuf:"bytes,5,opt,name=instance_type,json=instanceType,proto3" json:"instance_type,omitempty"` InstanceId string `protobuf:"bytes,6,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` ProviderId string `protobuf:"bytes,7,opt,name=provider_id,json=providerId,proto3" json:"provider_id,omitempty"` Spot bool `protobuf:"varint,8,opt,name=spot,proto3" json:"spot,omitempty"` InternalDns string `protobuf:"bytes,9,opt,name=internal_dns,json=internalDns,proto3" json:"internal_dns,omitempty"` ExternalDns string `protobuf:"bytes,10,opt,name=external_dns,json=externalDns,proto3" json:"external_dns,omitempty"` Tags map[string]string `protobuf:"bytes,11,rep,name=tags,proto3" json:"tags,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *PlatformMetadataSpec) Reset() { *x = PlatformMetadataSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *PlatformMetadataSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*PlatformMetadataSpec) ProtoMessage() {} func (x *PlatformMetadataSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use PlatformMetadataSpec.ProtoReflect.Descriptor instead. func (*PlatformMetadataSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{22} } func (x *PlatformMetadataSpec) GetPlatform() string { if x != nil { return x.Platform } return "" } func (x *PlatformMetadataSpec) GetHostname() string { if x != nil { return x.Hostname } return "" } func (x *PlatformMetadataSpec) GetRegion() string { if x != nil { return x.Region } return "" } func (x *PlatformMetadataSpec) GetZone() string { if x != nil { return x.Zone } return "" } func (x *PlatformMetadataSpec) GetInstanceType() string { if x != nil { return x.InstanceType } return "" } func (x *PlatformMetadataSpec) GetInstanceId() string { if x != nil { return x.InstanceId } return "" } func (x *PlatformMetadataSpec) GetProviderId() string { if x != nil { return x.ProviderId } return "" } func (x *PlatformMetadataSpec) GetSpot() bool { if x != nil { return x.Spot } return false } func (x *PlatformMetadataSpec) GetInternalDns() string { if x != nil { return x.InternalDns } return "" } func (x *PlatformMetadataSpec) GetExternalDns() string { if x != nil { return x.ExternalDns } return "" } func (x *PlatformMetadataSpec) GetTags() map[string]string { if x != nil { return x.Tags } return nil } // SBOMItemSpec describes the SBOM item resource properties. type SBOMItemSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` License string `protobuf:"bytes,3,opt,name=license,proto3" json:"license,omitempty"` CpEs []string `protobuf:"bytes,4,rep,name=cp_es,json=cpEs,proto3" json:"cp_es,omitempty"` PurLs []string `protobuf:"bytes,5,rep,name=pur_ls,json=purLs,proto3" json:"pur_ls,omitempty"` Extension bool `protobuf:"varint,6,opt,name=extension,proto3" json:"extension,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SBOMItemSpec) Reset() { *x = SBOMItemSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SBOMItemSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SBOMItemSpec) ProtoMessage() {} func (x *SBOMItemSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SBOMItemSpec.ProtoReflect.Descriptor instead. func (*SBOMItemSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{23} } func (x *SBOMItemSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *SBOMItemSpec) GetVersion() string { if x != nil { return x.Version } return "" } func (x *SBOMItemSpec) GetLicense() string { if x != nil { return x.License } return "" } func (x *SBOMItemSpec) GetCpEs() []string { if x != nil { return x.CpEs } return nil } func (x *SBOMItemSpec) GetPurLs() []string { if x != nil { return x.PurLs } return nil } func (x *SBOMItemSpec) GetExtension() bool { if x != nil { return x.Extension } return false } // SecurityStateSpec describes the security state resource properties. type SecurityStateSpec struct { state protoimpl.MessageState `protogen:"open.v1"` SecureBoot bool `protobuf:"varint,1,opt,name=secure_boot,json=secureBoot,proto3" json:"secure_boot,omitempty"` UkiSigningKeyFingerprint string `protobuf:"bytes,2,opt,name=uki_signing_key_fingerprint,json=ukiSigningKeyFingerprint,proto3" json:"uki_signing_key_fingerprint,omitempty"` PcrSigningKeyFingerprint string `protobuf:"bytes,3,opt,name=pcr_signing_key_fingerprint,json=pcrSigningKeyFingerprint,proto3" json:"pcr_signing_key_fingerprint,omitempty"` SeLinuxState enums.RuntimeSELinuxState `protobuf:"varint,4,opt,name=se_linux_state,json=seLinuxState,proto3,enum=talos.resource.definitions.enums.RuntimeSELinuxState" json:"se_linux_state,omitempty"` BootedWithUki bool `protobuf:"varint,5,opt,name=booted_with_uki,json=bootedWithUki,proto3" json:"booted_with_uki,omitempty"` FipsState enums.RuntimeFIPSState `protobuf:"varint,6,opt,name=fips_state,json=fipsState,proto3,enum=talos.resource.definitions.enums.RuntimeFIPSState" json:"fips_state,omitempty"` ModuleSignatureEnforced bool `protobuf:"varint,7,opt,name=module_signature_enforced,json=moduleSignatureEnforced,proto3" json:"module_signature_enforced,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SecurityStateSpec) Reset() { *x = SecurityStateSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *SecurityStateSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*SecurityStateSpec) ProtoMessage() {} func (x *SecurityStateSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use SecurityStateSpec.ProtoReflect.Descriptor instead. func (*SecurityStateSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{24} } func (x *SecurityStateSpec) GetSecureBoot() bool { if x != nil { return x.SecureBoot } return false } func (x *SecurityStateSpec) GetUkiSigningKeyFingerprint() string { if x != nil { return x.UkiSigningKeyFingerprint } return "" } func (x *SecurityStateSpec) GetPcrSigningKeyFingerprint() string { if x != nil { return x.PcrSigningKeyFingerprint } return "" } func (x *SecurityStateSpec) GetSeLinuxState() enums.RuntimeSELinuxState { if x != nil { return x.SeLinuxState } return enums.RuntimeSELinuxState(0) } func (x *SecurityStateSpec) GetBootedWithUki() bool { if x != nil { return x.BootedWithUki } return false } func (x *SecurityStateSpec) GetFipsState() enums.RuntimeFIPSState { if x != nil { return x.FipsState } return enums.RuntimeFIPSState(0) } func (x *SecurityStateSpec) GetModuleSignatureEnforced() bool { if x != nil { return x.ModuleSignatureEnforced } return false } // UniqueMachineTokenSpec is the spec for the machine unique token. Token can be empty if machine wasn't assigned any. type UniqueMachineTokenSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UniqueMachineTokenSpec) Reset() { *x = UniqueMachineTokenSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UniqueMachineTokenSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*UniqueMachineTokenSpec) ProtoMessage() {} func (x *UniqueMachineTokenSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UniqueMachineTokenSpec.ProtoReflect.Descriptor instead. func (*UniqueMachineTokenSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{25} } func (x *UniqueMachineTokenSpec) GetToken() string { if x != nil { return x.Token } return "" } // UnmetCondition is a failure which prevents machine from being ready at the stage. type UnmetCondition struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *UnmetCondition) Reset() { *x = UnmetCondition{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *UnmetCondition) String() string { return protoimpl.X.MessageStringOf(x) } func (*UnmetCondition) ProtoMessage() {} func (x *UnmetCondition) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use UnmetCondition.ProtoReflect.Descriptor instead. func (*UnmetCondition) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{26} } func (x *UnmetCondition) GetName() string { if x != nil { return x.Name } return "" } func (x *UnmetCondition) GetReason() string { if x != nil { return x.Reason } return "" } // WatchdogTimerConfigSpec describes configuration of watchdog timer. type WatchdogTimerConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` Timeout *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WatchdogTimerConfigSpec) Reset() { *x = WatchdogTimerConfigSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WatchdogTimerConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*WatchdogTimerConfigSpec) ProtoMessage() {} func (x *WatchdogTimerConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WatchdogTimerConfigSpec.ProtoReflect.Descriptor instead. func (*WatchdogTimerConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{27} } func (x *WatchdogTimerConfigSpec) GetDevice() string { if x != nil { return x.Device } return "" } func (x *WatchdogTimerConfigSpec) GetTimeout() *durationpb.Duration { if x != nil { return x.Timeout } return nil } // WatchdogTimerStatusSpec describes configuration of watchdog timer. type WatchdogTimerStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` Timeout *durationpb.Duration `protobuf:"bytes,2,opt,name=timeout,proto3" json:"timeout,omitempty"` FeedInterval *durationpb.Duration `protobuf:"bytes,3,opt,name=feed_interval,json=feedInterval,proto3" json:"feed_interval,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *WatchdogTimerStatusSpec) Reset() { *x = WatchdogTimerStatusSpec{} mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *WatchdogTimerStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*WatchdogTimerStatusSpec) ProtoMessage() {} func (x *WatchdogTimerStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_runtime_runtime_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use WatchdogTimerStatusSpec.ProtoReflect.Descriptor instead. func (*WatchdogTimerStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_runtime_runtime_proto_rawDescGZIP(), []int{28} } func (x *WatchdogTimerStatusSpec) GetDevice() string { if x != nil { return x.Device } return "" } func (x *WatchdogTimerStatusSpec) GetTimeout() *durationpb.Duration { if x != nil { return x.Timeout } return nil } func (x *WatchdogTimerStatusSpec) GetFeedInterval() *durationpb.Duration { if x != nil { return x.FeedInterval } return nil } var File_resource_definitions_runtime_runtime_proto protoreflect.FileDescriptor const file_resource_definitions_runtime_runtime_proto_rawDesc = "" + "\n" + "*resource/definitions/runtime/runtime.proto\x12\"talos.resource.definitions.runtime\x1a\x13common/common.proto\x1a\x1egoogle/protobuf/duration.proto\x1a&resource/definitions/enums/enums.proto\"\xdc\x01\n" + "\x14APIServiceConfigSpec\x12%\n" + "\x0elisten_address\x18\x01 \x01(\tR\rlistenAddress\x122\n" + "\x15node_routing_disabled\x18\x02 \x01(\bR\x13nodeRoutingDisabled\x12,\n" + "\x12readonly_role_mode\x18\x03 \x01(\bR\x10readonlyRoleMode\x12;\n" + "\x1askip_verifying_client_cert\x18\x04 \x01(\bR\x17skipVerifyingClientCert\"4\n" + "\x0fBootedEntrySpec\x12!\n" + "\fbooted_entry\x18\x01 \x01(\tR\vbootedEntry\")\n" + "\x11DevicesStatusSpec\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\"D\n" + "\x0eDiagnosticSpec\x12\x18\n" + "\amessage\x18\x01 \x01(\tR\amessage\x12\x18\n" + "\adetails\x18\x02 \x03(\tR\adetails\"/\n" + "\x0fEnvironmentSpec\x12\x1c\n" + "\tvariables\x18\x01 \x03(\tR\tvariables\"1\n" + "\x13EventSinkConfigSpec\x12\x1a\n" + "\bendpoint\x18\x01 \x01(\tR\bendpoint\"U\n" + "\x1aExtensionServiceConfigFile\x12\x18\n" + "\acontent\x18\x01 \x01(\tR\acontent\x12\x1d\n" + "\n" + "mount_path\x18\x02 \x01(\tR\tmountPath\"\x94\x01\n" + "\x1aExtensionServiceConfigSpec\x12T\n" + "\x05files\x18\x01 \x03(\v2>.talos.resource.definitions.runtime.ExtensionServiceConfigFileR\x05files\x12 \n" + "\venvironment\x18\x02 \x03(\tR\venvironment\"E\n" + " ExtensionServiceConfigStatusSpec\x12!\n" + "\fspec_version\x18\x01 \x01(\tR\vspecVersion\"-\n" + "\x11KernelCmdlineSpec\x12\x18\n" + "\acmdline\x18\x01 \x01(\tR\acmdline\"J\n" + "\x14KernelModuleSpecSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1e\n" + "\n" + "parameters\x18\x02 \x03(\tR\n" + "parameters\"P\n" + "\x13KernelParamSpecSpec\x12\x14\n" + "\x05value\x18\x01 \x01(\tR\x05value\x12#\n" + "\rignore_errors\x18\x02 \x01(\bR\fignoreErrors\"m\n" + "\x15KernelParamStatusSpec\x12\x18\n" + "\acurrent\x18\x01 \x01(\tR\acurrent\x12\x18\n" + "\adefault\x18\x02 \x01(\tR\adefault\x12 \n" + "\vunsupported\x18\x03 \x01(\bR\vunsupported\"D\n" + "\x11KmsgLogConfigSpec\x12/\n" + "\fdestinations\x18\x01 \x03(\v2\v.common.URLR\fdestinations\"\xa9\x01\n" + "\x16LoadedKernelModuleSpec\x12\x12\n" + "\x04size\x18\x01 \x01(\x03R\x04size\x12'\n" + "\x0freference_count\x18\x02 \x01(\x03R\x0ereferenceCount\x12\"\n" + "\fdependencies\x18\x03 \x03(\tR\fdependencies\x12\x14\n" + "\x05state\x18\x04 \x01(\tR\x05state\x12\x18\n" + "\aaddress\x18\x05 \x01(\tR\aaddress\"\xb1\x01\n" + "\x11MachineStatusSpec\x12K\n" + "\x05stage\x18\x01 \x01(\x0e25.talos.resource.definitions.enums.RuntimeMachineStageR\x05stage\x12O\n" + "\x06status\x18\x02 \x01(\v27.talos.resource.definitions.runtime.MachineStatusStatusR\x06status\"\x8a\x01\n" + "\x13MachineStatusStatus\x12\x14\n" + "\x05ready\x18\x01 \x01(\bR\x05ready\x12]\n" + "\x10unmet_conditions\x18\x02 \x03(\v22.talos.resource.definitions.runtime.UnmetConditionR\x0funmetConditions\"\x85\x01\n" + "\x1cMaintenanceServiceConfigSpec\x12%\n" + "\x0elisten_address\x18\x01 \x01(\tR\rlistenAddress\x12>\n" + "\x13reachable_addresses\x18\x02 \x03(\v2\r.common.NetIPR\x12reachableAddresses\"#\n" + "\vMetaKeySpec\x12\x14\n" + "\x05value\x18\x01 \x01(\tR\x05value\"$\n" + "\x0eMetaLoadedSpec\x12\x12\n" + "\x04done\x18\x01 \x01(\bR\x04done\"\xd5\x01\n" + "\x0fMountStatusSpec\x12\x16\n" + "\x06source\x18\x01 \x01(\tR\x06source\x12\x16\n" + "\x06target\x18\x02 \x01(\tR\x06target\x12'\n" + "\x0ffilesystem_type\x18\x03 \x01(\tR\x0efilesystemType\x12\x18\n" + "\aoptions\x18\x04 \x03(\tR\aoptions\x12\x1c\n" + "\tencrypted\x18\x05 \x01(\bR\tencrypted\x121\n" + "\x14encryption_providers\x18\x06 \x03(\tR\x13encryptionProviders\"l\n" + "\rOOMActionSpec\x12'\n" + "\x0ftrigger_context\x18\x01 \x01(\tR\x0etriggerContext\x12\x14\n" + "\x05score\x18\x02 \x01(\x01R\x05score\x12\x1c\n" + "\tprocesses\x18\x03 \x03(\tR\tprocesses\"\xcc\x03\n" + "\x14PlatformMetadataSpec\x12\x1a\n" + "\bplatform\x18\x01 \x01(\tR\bplatform\x12\x1a\n" + "\bhostname\x18\x02 \x01(\tR\bhostname\x12\x16\n" + "\x06region\x18\x03 \x01(\tR\x06region\x12\x12\n" + "\x04zone\x18\x04 \x01(\tR\x04zone\x12#\n" + "\rinstance_type\x18\x05 \x01(\tR\finstanceType\x12\x1f\n" + "\vinstance_id\x18\x06 \x01(\tR\n" + "instanceId\x12\x1f\n" + "\vprovider_id\x18\a \x01(\tR\n" + "providerId\x12\x12\n" + "\x04spot\x18\b \x01(\bR\x04spot\x12!\n" + "\finternal_dns\x18\t \x01(\tR\vinternalDns\x12!\n" + "\fexternal_dns\x18\n" + " \x01(\tR\vexternalDns\x12V\n" + "\x04tags\x18\v \x03(\v2B.talos.resource.definitions.runtime.PlatformMetadataSpec.TagsEntryR\x04tags\x1a7\n" + "\tTagsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa0\x01\n" + "\fSBOMItemSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + "\aversion\x18\x02 \x01(\tR\aversion\x12\x18\n" + "\alicense\x18\x03 \x01(\tR\alicense\x12\x13\n" + "\x05cp_es\x18\x04 \x03(\tR\x04cpEs\x12\x15\n" + "\x06pur_ls\x18\x05 \x03(\tR\x05purLs\x12\x1c\n" + "\textension\x18\x06 \x01(\bR\textension\"\xc6\x03\n" + "\x11SecurityStateSpec\x12\x1f\n" + "\vsecure_boot\x18\x01 \x01(\bR\n" + "secureBoot\x12=\n" + "\x1buki_signing_key_fingerprint\x18\x02 \x01(\tR\x18ukiSigningKeyFingerprint\x12=\n" + "\x1bpcr_signing_key_fingerprint\x18\x03 \x01(\tR\x18pcrSigningKeyFingerprint\x12[\n" + "\x0ese_linux_state\x18\x04 \x01(\x0e25.talos.resource.definitions.enums.RuntimeSELinuxStateR\fseLinuxState\x12&\n" + "\x0fbooted_with_uki\x18\x05 \x01(\bR\rbootedWithUki\x12Q\n" + "\n" + "fips_state\x18\x06 \x01(\x0e22.talos.resource.definitions.enums.RuntimeFIPSStateR\tfipsState\x12:\n" + "\x19module_signature_enforced\x18\a \x01(\bR\x17moduleSignatureEnforced\".\n" + "\x16UniqueMachineTokenSpec\x12\x14\n" + "\x05token\x18\x01 \x01(\tR\x05token\"<\n" + "\x0eUnmetCondition\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n" + "\x06reason\x18\x02 \x01(\tR\x06reason\"f\n" + "\x17WatchdogTimerConfigSpec\x12\x16\n" + "\x06device\x18\x01 \x01(\tR\x06device\x123\n" + "\atimeout\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\atimeout\"\xa6\x01\n" + "\x17WatchdogTimerStatusSpec\x12\x16\n" + "\x06device\x18\x01 \x01(\tR\x06device\x123\n" + "\atimeout\x18\x02 \x01(\v2\x19.google.protobuf.DurationR\atimeout\x12>\n" + "\rfeed_interval\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\ffeedIntervalBx\n" + "*dev.talos.api.resource.definitions.runtimeZJgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/runtimeb\x06proto3" var ( file_resource_definitions_runtime_runtime_proto_rawDescOnce sync.Once file_resource_definitions_runtime_runtime_proto_rawDescData []byte ) func file_resource_definitions_runtime_runtime_proto_rawDescGZIP() []byte { file_resource_definitions_runtime_runtime_proto_rawDescOnce.Do(func() { file_resource_definitions_runtime_runtime_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_runtime_runtime_proto_rawDesc), len(file_resource_definitions_runtime_runtime_proto_rawDesc))) }) return file_resource_definitions_runtime_runtime_proto_rawDescData } var file_resource_definitions_runtime_runtime_proto_msgTypes = make([]protoimpl.MessageInfo, 30) var file_resource_definitions_runtime_runtime_proto_goTypes = []any{ (*APIServiceConfigSpec)(nil), // 0: talos.resource.definitions.runtime.APIServiceConfigSpec (*BootedEntrySpec)(nil), // 1: talos.resource.definitions.runtime.BootedEntrySpec (*DevicesStatusSpec)(nil), // 2: talos.resource.definitions.runtime.DevicesStatusSpec (*DiagnosticSpec)(nil), // 3: talos.resource.definitions.runtime.DiagnosticSpec (*EnvironmentSpec)(nil), // 4: talos.resource.definitions.runtime.EnvironmentSpec (*EventSinkConfigSpec)(nil), // 5: talos.resource.definitions.runtime.EventSinkConfigSpec (*ExtensionServiceConfigFile)(nil), // 6: talos.resource.definitions.runtime.ExtensionServiceConfigFile (*ExtensionServiceConfigSpec)(nil), // 7: talos.resource.definitions.runtime.ExtensionServiceConfigSpec (*ExtensionServiceConfigStatusSpec)(nil), // 8: talos.resource.definitions.runtime.ExtensionServiceConfigStatusSpec (*KernelCmdlineSpec)(nil), // 9: talos.resource.definitions.runtime.KernelCmdlineSpec (*KernelModuleSpecSpec)(nil), // 10: talos.resource.definitions.runtime.KernelModuleSpecSpec (*KernelParamSpecSpec)(nil), // 11: talos.resource.definitions.runtime.KernelParamSpecSpec (*KernelParamStatusSpec)(nil), // 12: talos.resource.definitions.runtime.KernelParamStatusSpec (*KmsgLogConfigSpec)(nil), // 13: talos.resource.definitions.runtime.KmsgLogConfigSpec (*LoadedKernelModuleSpec)(nil), // 14: talos.resource.definitions.runtime.LoadedKernelModuleSpec (*MachineStatusSpec)(nil), // 15: talos.resource.definitions.runtime.MachineStatusSpec (*MachineStatusStatus)(nil), // 16: talos.resource.definitions.runtime.MachineStatusStatus (*MaintenanceServiceConfigSpec)(nil), // 17: talos.resource.definitions.runtime.MaintenanceServiceConfigSpec (*MetaKeySpec)(nil), // 18: talos.resource.definitions.runtime.MetaKeySpec (*MetaLoadedSpec)(nil), // 19: talos.resource.definitions.runtime.MetaLoadedSpec (*MountStatusSpec)(nil), // 20: talos.resource.definitions.runtime.MountStatusSpec (*OOMActionSpec)(nil), // 21: talos.resource.definitions.runtime.OOMActionSpec (*PlatformMetadataSpec)(nil), // 22: talos.resource.definitions.runtime.PlatformMetadataSpec (*SBOMItemSpec)(nil), // 23: talos.resource.definitions.runtime.SBOMItemSpec (*SecurityStateSpec)(nil), // 24: talos.resource.definitions.runtime.SecurityStateSpec (*UniqueMachineTokenSpec)(nil), // 25: talos.resource.definitions.runtime.UniqueMachineTokenSpec (*UnmetCondition)(nil), // 26: talos.resource.definitions.runtime.UnmetCondition (*WatchdogTimerConfigSpec)(nil), // 27: talos.resource.definitions.runtime.WatchdogTimerConfigSpec (*WatchdogTimerStatusSpec)(nil), // 28: talos.resource.definitions.runtime.WatchdogTimerStatusSpec nil, // 29: talos.resource.definitions.runtime.PlatformMetadataSpec.TagsEntry (*common.URL)(nil), // 30: common.URL (enums.RuntimeMachineStage)(0), // 31: talos.resource.definitions.enums.RuntimeMachineStage (*common.NetIP)(nil), // 32: common.NetIP (enums.RuntimeSELinuxState)(0), // 33: talos.resource.definitions.enums.RuntimeSELinuxState (enums.RuntimeFIPSState)(0), // 34: talos.resource.definitions.enums.RuntimeFIPSState (*durationpb.Duration)(nil), // 35: google.protobuf.Duration } var file_resource_definitions_runtime_runtime_proto_depIdxs = []int32{ 6, // 0: talos.resource.definitions.runtime.ExtensionServiceConfigSpec.files:type_name -> talos.resource.definitions.runtime.ExtensionServiceConfigFile 30, // 1: talos.resource.definitions.runtime.KmsgLogConfigSpec.destinations:type_name -> common.URL 31, // 2: talos.resource.definitions.runtime.MachineStatusSpec.stage:type_name -> talos.resource.definitions.enums.RuntimeMachineStage 16, // 3: talos.resource.definitions.runtime.MachineStatusSpec.status:type_name -> talos.resource.definitions.runtime.MachineStatusStatus 26, // 4: talos.resource.definitions.runtime.MachineStatusStatus.unmet_conditions:type_name -> talos.resource.definitions.runtime.UnmetCondition 32, // 5: talos.resource.definitions.runtime.MaintenanceServiceConfigSpec.reachable_addresses:type_name -> common.NetIP 29, // 6: talos.resource.definitions.runtime.PlatformMetadataSpec.tags:type_name -> talos.resource.definitions.runtime.PlatformMetadataSpec.TagsEntry 33, // 7: talos.resource.definitions.runtime.SecurityStateSpec.se_linux_state:type_name -> talos.resource.definitions.enums.RuntimeSELinuxState 34, // 8: talos.resource.definitions.runtime.SecurityStateSpec.fips_state:type_name -> talos.resource.definitions.enums.RuntimeFIPSState 35, // 9: talos.resource.definitions.runtime.WatchdogTimerConfigSpec.timeout:type_name -> google.protobuf.Duration 35, // 10: talos.resource.definitions.runtime.WatchdogTimerStatusSpec.timeout:type_name -> google.protobuf.Duration 35, // 11: talos.resource.definitions.runtime.WatchdogTimerStatusSpec.feed_interval:type_name -> google.protobuf.Duration 12, // [12:12] is the sub-list for method output_type 12, // [12:12] is the sub-list for method input_type 12, // [12:12] is the sub-list for extension type_name 12, // [12:12] is the sub-list for extension extendee 0, // [0:12] is the sub-list for field type_name } func init() { file_resource_definitions_runtime_runtime_proto_init() } func file_resource_definitions_runtime_runtime_proto_init() { if File_resource_definitions_runtime_runtime_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_runtime_runtime_proto_rawDesc), len(file_resource_definitions_runtime_runtime_proto_rawDesc)), NumEnums: 0, NumMessages: 30, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_runtime_runtime_proto_goTypes, DependencyIndexes: file_resource_definitions_runtime_runtime_proto_depIdxs, MessageInfos: file_resource_definitions_runtime_runtime_proto_msgTypes, }.Build() File_resource_definitions_runtime_runtime_proto = out.File file_resource_definitions_runtime_runtime_proto_goTypes = nil file_resource_definitions_runtime_runtime_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/runtime/runtime_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/runtime/runtime.proto package runtime import ( binary "encoding/binary" fmt "fmt" io "io" math "math" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb1 "google.golang.org/protobuf/types/known/durationpb" common "github.com/siderolabs/talos/pkg/machinery/api/common" enums "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *APIServiceConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *APIServiceConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *APIServiceConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SkipVerifyingClientCert { i-- if m.SkipVerifyingClientCert { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.ReadonlyRoleMode { i-- if m.ReadonlyRoleMode { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.NodeRoutingDisabled { i-- if m.NodeRoutingDisabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.ListenAddress) > 0 { i -= len(m.ListenAddress) copy(dAtA[i:], m.ListenAddress) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ListenAddress))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BootedEntrySpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BootedEntrySpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BootedEntrySpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.BootedEntry) > 0 { i -= len(m.BootedEntry) copy(dAtA[i:], m.BootedEntry) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BootedEntry))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DevicesStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DevicesStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DevicesStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *DiagnosticSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DiagnosticSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DiagnosticSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Details) > 0 { for iNdEx := len(m.Details) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Details[iNdEx]) copy(dAtA[i:], m.Details[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Details[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Message) > 0 { i -= len(m.Message) copy(dAtA[i:], m.Message) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Message))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EnvironmentSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EnvironmentSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EnvironmentSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Variables) > 0 { for iNdEx := len(m.Variables) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Variables[iNdEx]) copy(dAtA[i:], m.Variables[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Variables[iNdEx]))) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EventSinkConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EventSinkConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EventSinkConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Endpoint) > 0 { i -= len(m.Endpoint) copy(dAtA[i:], m.Endpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Endpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ExtensionServiceConfigFile) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtensionServiceConfigFile) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtensionServiceConfigFile) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.MountPath) > 0 { i -= len(m.MountPath) copy(dAtA[i:], m.MountPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.MountPath))) i-- dAtA[i] = 0x12 } if len(m.Content) > 0 { i -= len(m.Content) copy(dAtA[i:], m.Content) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Content))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ExtensionServiceConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtensionServiceConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtensionServiceConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Environment) > 0 { for iNdEx := len(m.Environment) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Environment[iNdEx]) copy(dAtA[i:], m.Environment[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Environment[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Files) > 0 { for iNdEx := len(m.Files) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Files[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *ExtensionServiceConfigStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ExtensionServiceConfigStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ExtensionServiceConfigStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SpecVersion) > 0 { i -= len(m.SpecVersion) copy(dAtA[i:], m.SpecVersion) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SpecVersion))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KernelCmdlineSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KernelCmdlineSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KernelCmdlineSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Cmdline) > 0 { i -= len(m.Cmdline) copy(dAtA[i:], m.Cmdline) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Cmdline))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KernelModuleSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KernelModuleSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KernelModuleSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Parameters) > 0 { for iNdEx := len(m.Parameters) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Parameters[iNdEx]) copy(dAtA[i:], m.Parameters[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Parameters[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KernelParamSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KernelParamSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KernelParamSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.IgnoreErrors { i-- if m.IgnoreErrors { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KernelParamStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KernelParamStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KernelParamStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Unsupported { i-- if m.Unsupported { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.Default) > 0 { i -= len(m.Default) copy(dAtA[i:], m.Default) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Default))) i-- dAtA[i] = 0x12 } if len(m.Current) > 0 { i -= len(m.Current) copy(dAtA[i:], m.Current) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Current))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KmsgLogConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KmsgLogConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KmsgLogConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Destinations) > 0 { for iNdEx := len(m.Destinations) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.Destinations[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Destinations[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *LoadedKernelModuleSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *LoadedKernelModuleSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *LoadedKernelModuleSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Address) > 0 { i -= len(m.Address) copy(dAtA[i:], m.Address) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Address))) i-- dAtA[i] = 0x2a } if len(m.State) > 0 { i -= len(m.State) copy(dAtA[i:], m.State) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.State))) i-- dAtA[i] = 0x22 } if len(m.Dependencies) > 0 { for iNdEx := len(m.Dependencies) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Dependencies[iNdEx]) copy(dAtA[i:], m.Dependencies[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Dependencies[iNdEx]))) i-- dAtA[i] = 0x1a } } if m.ReferenceCount != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.ReferenceCount)) i-- dAtA[i] = 0x10 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MachineStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Status != nil { size, err := m.Status.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if m.Stage != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Stage)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MachineStatusStatus) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MachineStatusStatus) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MachineStatusStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.UnmetConditions) > 0 { for iNdEx := len(m.UnmetConditions) - 1; iNdEx >= 0; iNdEx-- { size, err := m.UnmetConditions[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Ready { i-- if m.Ready { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MaintenanceServiceConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MaintenanceServiceConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MaintenanceServiceConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.ReachableAddresses) > 0 { for iNdEx := len(m.ReachableAddresses) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ReachableAddresses[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ReachableAddresses[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if len(m.ListenAddress) > 0 { i -= len(m.ListenAddress) copy(dAtA[i:], m.ListenAddress) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ListenAddress))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MetaKeySpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaKeySpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaKeySpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Value) > 0 { i -= len(m.Value) copy(dAtA[i:], m.Value) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Value))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MetaLoadedSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MetaLoadedSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MetaLoadedSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Done { i-- if m.Done { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *MountStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MountStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MountStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.EncryptionProviders) > 0 { for iNdEx := len(m.EncryptionProviders) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.EncryptionProviders[iNdEx]) copy(dAtA[i:], m.EncryptionProviders[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.EncryptionProviders[iNdEx]))) i-- dAtA[i] = 0x32 } } if m.Encrypted { i-- if m.Encrypted { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.Options) > 0 { for iNdEx := len(m.Options) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Options[iNdEx]) copy(dAtA[i:], m.Options[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Options[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.FilesystemType) > 0 { i -= len(m.FilesystemType) copy(dAtA[i:], m.FilesystemType) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.FilesystemType))) i-- dAtA[i] = 0x1a } if len(m.Target) > 0 { i -= len(m.Target) copy(dAtA[i:], m.Target) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Target))) i-- dAtA[i] = 0x12 } if len(m.Source) > 0 { i -= len(m.Source) copy(dAtA[i:], m.Source) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Source))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *OOMActionSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *OOMActionSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *OOMActionSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Processes) > 0 { for iNdEx := len(m.Processes) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.Processes[iNdEx]) copy(dAtA[i:], m.Processes[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Processes[iNdEx]))) i-- dAtA[i] = 0x1a } } if m.Score != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.Score)))) i-- dAtA[i] = 0x11 } if len(m.TriggerContext) > 0 { i -= len(m.TriggerContext) copy(dAtA[i:], m.TriggerContext) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.TriggerContext))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *PlatformMetadataSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *PlatformMetadataSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *PlatformMetadataSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Tags) > 0 { for k := range m.Tags { v := m.Tags[k] baseI := i i -= len(v) copy(dAtA[i:], v) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(v))) i-- dAtA[i] = 0x12 i -= len(k) copy(dAtA[i:], k) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(k))) i-- dAtA[i] = 0xa i = protohelpers.EncodeVarint(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x5a } } if len(m.ExternalDns) > 0 { i -= len(m.ExternalDns) copy(dAtA[i:], m.ExternalDns) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ExternalDns))) i-- dAtA[i] = 0x52 } if len(m.InternalDns) > 0 { i -= len(m.InternalDns) copy(dAtA[i:], m.InternalDns) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InternalDns))) i-- dAtA[i] = 0x4a } if m.Spot { i-- if m.Spot { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x40 } if len(m.ProviderId) > 0 { i -= len(m.ProviderId) copy(dAtA[i:], m.ProviderId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ProviderId))) i-- dAtA[i] = 0x3a } if len(m.InstanceId) > 0 { i -= len(m.InstanceId) copy(dAtA[i:], m.InstanceId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InstanceId))) i-- dAtA[i] = 0x32 } if len(m.InstanceType) > 0 { i -= len(m.InstanceType) copy(dAtA[i:], m.InstanceType) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.InstanceType))) i-- dAtA[i] = 0x2a } if len(m.Zone) > 0 { i -= len(m.Zone) copy(dAtA[i:], m.Zone) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Zone))) i-- dAtA[i] = 0x22 } if len(m.Region) > 0 { i -= len(m.Region) copy(dAtA[i:], m.Region) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Region))) i-- dAtA[i] = 0x1a } if len(m.Hostname) > 0 { i -= len(m.Hostname) copy(dAtA[i:], m.Hostname) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Hostname))) i-- dAtA[i] = 0x12 } if len(m.Platform) > 0 { i -= len(m.Platform) copy(dAtA[i:], m.Platform) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Platform))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SBOMItemSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SBOMItemSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SBOMItemSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Extension { i-- if m.Extension { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x30 } if len(m.PurLs) > 0 { for iNdEx := len(m.PurLs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.PurLs[iNdEx]) copy(dAtA[i:], m.PurLs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PurLs[iNdEx]))) i-- dAtA[i] = 0x2a } } if len(m.CpEs) > 0 { for iNdEx := len(m.CpEs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.CpEs[iNdEx]) copy(dAtA[i:], m.CpEs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CpEs[iNdEx]))) i-- dAtA[i] = 0x22 } } if len(m.License) > 0 { i -= len(m.License) copy(dAtA[i:], m.License) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.License))) i-- dAtA[i] = 0x1a } if len(m.Version) > 0 { i -= len(m.Version) copy(dAtA[i:], m.Version) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Version))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *SecurityStateSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *SecurityStateSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *SecurityStateSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.ModuleSignatureEnforced { i-- if m.ModuleSignatureEnforced { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.FipsState != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.FipsState)) i-- dAtA[i] = 0x30 } if m.BootedWithUki { i-- if m.BootedWithUki { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.SeLinuxState != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.SeLinuxState)) i-- dAtA[i] = 0x20 } if len(m.PcrSigningKeyFingerprint) > 0 { i -= len(m.PcrSigningKeyFingerprint) copy(dAtA[i:], m.PcrSigningKeyFingerprint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.PcrSigningKeyFingerprint))) i-- dAtA[i] = 0x1a } if len(m.UkiSigningKeyFingerprint) > 0 { i -= len(m.UkiSigningKeyFingerprint) copy(dAtA[i:], m.UkiSigningKeyFingerprint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.UkiSigningKeyFingerprint))) i-- dAtA[i] = 0x12 } if m.SecureBoot { i-- if m.SecureBoot { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *UniqueMachineTokenSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UniqueMachineTokenSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *UniqueMachineTokenSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Token) > 0 { i -= len(m.Token) copy(dAtA[i:], m.Token) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *UnmetCondition) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *UnmetCondition) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *UnmetCondition) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Reason) > 0 { i -= len(m.Reason) copy(dAtA[i:], m.Reason) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Reason))) i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WatchdogTimerConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WatchdogTimerConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *WatchdogTimerConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Timeout != nil { size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Device) > 0 { i -= len(m.Device) copy(dAtA[i:], m.Device) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *WatchdogTimerStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *WatchdogTimerStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *WatchdogTimerStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.FeedInterval != nil { size, err := (*durationpb.Duration)(m.FeedInterval).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.Timeout != nil { size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } if len(m.Device) > 0 { i -= len(m.Device) copy(dAtA[i:], m.Device) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *APIServiceConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ListenAddress) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.NodeRoutingDisabled { n += 2 } if m.ReadonlyRoleMode { n += 2 } if m.SkipVerifyingClientCert { n += 2 } n += len(m.unknownFields) return n } func (m *BootedEntrySpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.BootedEntry) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DevicesStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } n += len(m.unknownFields) return n } func (m *DiagnosticSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Message) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Details) > 0 { for _, s := range m.Details { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EnvironmentSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Variables) > 0 { for _, s := range m.Variables { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *EventSinkConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Endpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ExtensionServiceConfigFile) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Content) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.MountPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ExtensionServiceConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Files) > 0 { for _, e := range m.Files { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.Environment) > 0 { for _, s := range m.Environment { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *ExtensionServiceConfigStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.SpecVersion) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KernelCmdlineSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Cmdline) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KernelModuleSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Parameters) > 0 { for _, s := range m.Parameters { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *KernelParamSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.IgnoreErrors { n += 2 } n += len(m.unknownFields) return n } func (m *KernelParamStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Current) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Default) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Unsupported { n += 2 } n += len(m.unknownFields) return n } func (m *KmsgLogConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Destinations) > 0 { for _, e := range m.Destinations { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *LoadedKernelModuleSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } if m.ReferenceCount != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.ReferenceCount)) } if len(m.Dependencies) > 0 { for _, s := range m.Dependencies { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.State) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Address) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MachineStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Stage != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Stage)) } if m.Status != nil { l = m.Status.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MachineStatusStatus) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ready { n += 2 } if len(m.UnmetConditions) > 0 { for _, e := range m.UnmetConditions { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MaintenanceServiceConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ListenAddress) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ReachableAddresses) > 0 { for _, e := range m.ReachableAddresses { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MetaKeySpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Value) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *MetaLoadedSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Done { n += 2 } n += len(m.unknownFields) return n } func (m *MountStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Source) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Target) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.FilesystemType) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Options) > 0 { for _, s := range m.Options { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Encrypted { n += 2 } if len(m.EncryptionProviders) > 0 { for _, s := range m.EncryptionProviders { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *OOMActionSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.TriggerContext) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Score != 0 { n += 9 } if len(m.Processes) > 0 { for _, s := range m.Processes { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *PlatformMetadataSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Platform) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Hostname) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Region) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Zone) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.InstanceType) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.InstanceId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ProviderId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Spot { n += 2 } l = len(m.InternalDns) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ExternalDns) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Tags) > 0 { for k, v := range m.Tags { _ = k _ = v mapEntrySize := 1 + len(k) + protohelpers.SizeOfVarint(uint64(len(k))) + 1 + len(v) + protohelpers.SizeOfVarint(uint64(len(v))) n += mapEntrySize + 1 + protohelpers.SizeOfVarint(uint64(mapEntrySize)) } } n += len(m.unknownFields) return n } func (m *SBOMItemSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Version) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.License) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.CpEs) > 0 { for _, s := range m.CpEs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.PurLs) > 0 { for _, s := range m.PurLs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.Extension { n += 2 } n += len(m.unknownFields) return n } func (m *SecurityStateSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.SecureBoot { n += 2 } l = len(m.UkiSigningKeyFingerprint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.PcrSigningKeyFingerprint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SeLinuxState != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.SeLinuxState)) } if m.BootedWithUki { n += 2 } if m.FipsState != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.FipsState)) } if m.ModuleSignatureEnforced { n += 2 } n += len(m.unknownFields) return n } func (m *UniqueMachineTokenSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Token) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *UnmetCondition) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Reason) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *WatchdogTimerConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Device) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Timeout != nil { l = (*durationpb.Duration)(m.Timeout).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *WatchdogTimerStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Device) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Timeout != nil { l = (*durationpb.Duration)(m.Timeout).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FeedInterval != nil { l = (*durationpb.Duration)(m.FeedInterval).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *APIServiceConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: APIServiceConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: APIServiceConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenAddress", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field NodeRoutingDisabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.NodeRoutingDisabled = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReadonlyRoleMode", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ReadonlyRoleMode = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipVerifyingClientCert", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipVerifyingClientCert = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BootedEntrySpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BootedEntrySpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BootedEntrySpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BootedEntry", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BootedEntry = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DevicesStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DevicesStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DevicesStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DiagnosticSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DiagnosticSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DiagnosticSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Message = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Details", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Details = append(m.Details, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EnvironmentSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EnvironmentSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EnvironmentSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Variables", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Variables = append(m.Variables, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EventSinkConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EventSinkConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EventSinkConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Endpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtensionServiceConfigFile) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtensionServiceConfigFile: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtensionServiceConfigFile: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Content", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Content = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MountPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.MountPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtensionServiceConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtensionServiceConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtensionServiceConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Files", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Files = append(m.Files, &ExtensionServiceConfigFile{}) if err := m.Files[len(m.Files)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Environment", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Environment = append(m.Environment, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ExtensionServiceConfigStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ExtensionServiceConfigStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ExtensionServiceConfigStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SpecVersion", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SpecVersion = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KernelCmdlineSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KernelCmdlineSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KernelCmdlineSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Cmdline", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Cmdline = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KernelModuleSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KernelModuleSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KernelModuleSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Parameters", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Parameters = append(m.Parameters, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KernelParamSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KernelParamSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KernelParamSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field IgnoreErrors", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.IgnoreErrors = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KernelParamStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KernelParamStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KernelParamStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Current = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Default", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Default = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unsupported", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Unsupported = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KmsgLogConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KmsgLogConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KmsgLogConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Destinations", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Destinations = append(m.Destinations, &common.URL{}) if unmarshal, ok := interface{}(m.Destinations[len(m.Destinations)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Destinations[len(m.Destinations)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *LoadedKernelModuleSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: LoadedKernelModuleSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: LoadedKernelModuleSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= int64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ReferenceCount", wireType) } m.ReferenceCount = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.ReferenceCount |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Dependencies", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Dependencies = append(m.Dependencies, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.State = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Address", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Address = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Stage", wireType) } m.Stage = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Stage |= enums.RuntimeMachineStage(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Status == nil { m.Status = &MachineStatusStatus{} } if err := m.Status.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MachineStatusStatus) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MachineStatusStatus: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MachineStatusStatus: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Ready", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Ready = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UnmetConditions", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UnmetConditions = append(m.UnmetConditions, &UnmetCondition{}) if err := m.UnmetConditions[len(m.UnmetConditions)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MaintenanceServiceConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MaintenanceServiceConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MaintenanceServiceConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ListenAddress", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ListenAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ReachableAddresses", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ReachableAddresses = append(m.ReachableAddresses, &common.NetIP{}) if unmarshal, ok := interface{}(m.ReachableAddresses[len(m.ReachableAddresses)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ReachableAddresses[len(m.ReachableAddresses)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaKeySpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaKeySpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaKeySpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Value", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Value = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MetaLoadedSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MetaLoadedSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MetaLoadedSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Done", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Done = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MountStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MountStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MountStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Source", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Source = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Target", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Target = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FilesystemType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.FilesystemType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Options", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Options = append(m.Options, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Encrypted", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Encrypted = bool(v != 0) case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EncryptionProviders", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.EncryptionProviders = append(m.EncryptionProviders, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *OOMActionSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: OOMActionSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: OOMActionSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field TriggerContext", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.TriggerContext = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field Score", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.Score = float64(math.Float64frombits(v)) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Processes", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Processes = append(m.Processes, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *PlatformMetadataSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: PlatformMetadataSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: PlatformMetadataSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Platform", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Platform = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Hostname", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Hostname = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Region", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Region = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Zone", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Zone = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InstanceType", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InstanceType = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InstanceId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InstanceId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ProviderId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ProviderId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Spot", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Spot = bool(v != 0) case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field InternalDns", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.InternalDns = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ExternalDns", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ExternalDns = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Tags == nil { m.Tags = make(map[string]string) } var mapkey string var mapvalue string for iNdEx < postIndex { entryPreIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) if fieldNum == 1 { var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapkey |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapkey := int(stringLenmapkey) if intStringLenmapkey < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapkey := iNdEx + intStringLenmapkey if postStringIndexmapkey < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapkey > l { return io.ErrUnexpectedEOF } mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var stringLenmapvalue uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLenmapvalue |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLenmapvalue := int(stringLenmapvalue) if intStringLenmapvalue < 0 { return protohelpers.ErrInvalidLength } postStringIndexmapvalue := iNdEx + intStringLenmapvalue if postStringIndexmapvalue < 0 { return protohelpers.ErrInvalidLength } if postStringIndexmapvalue > l { return io.ErrUnexpectedEOF } mapvalue = string(dAtA[iNdEx:postStringIndexmapvalue]) iNdEx = postStringIndexmapvalue } else { iNdEx = entryPreIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > postIndex { return io.ErrUnexpectedEOF } iNdEx += skippy } } m.Tags[mapkey] = mapvalue iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SBOMItemSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SBOMItemSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SBOMItemSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Version = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field License", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.License = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CpEs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CpEs = append(m.CpEs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PurLs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PurLs = append(m.PurLs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Extension", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Extension = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *SecurityStateSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: SecurityStateSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: SecurityStateSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SecureBoot", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SecureBoot = bool(v != 0) case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field UkiSigningKeyFingerprint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.UkiSigningKeyFingerprint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PcrSigningKeyFingerprint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.PcrSigningKeyFingerprint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SeLinuxState", wireType) } m.SeLinuxState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.SeLinuxState |= enums.RuntimeSELinuxState(b&0x7F) << shift if b < 0x80 { break } } case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field BootedWithUki", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.BootedWithUki = bool(v != 0) case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field FipsState", wireType) } m.FipsState = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.FipsState |= enums.RuntimeFIPSState(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ModuleSignatureEnforced", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.ModuleSignatureEnforced = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UniqueMachineTokenSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UniqueMachineTokenSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UniqueMachineTokenSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Token = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *UnmetCondition) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: UnmetCondition: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: UnmetCondition: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Reason", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Reason = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WatchdogTimerConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WatchdogTimerConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WatchdogTimerConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Device = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timeout == nil { m.Timeout = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *WatchdogTimerStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: WatchdogTimerStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: WatchdogTimerStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Device = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Timeout", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Timeout == nil { m.Timeout = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Timeout).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FeedInterval", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.FeedInterval == nil { m.FeedInterval = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.FeedInterval).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/secrets/secrets.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/secrets/secrets.proto package secrets import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // APICertsSpec describes etcd certs secrets. type APICertsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Client *common.PEMEncodedCertificateAndKey `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"` Server *common.PEMEncodedCertificateAndKey `protobuf:"bytes,3,opt,name=server,proto3" json:"server,omitempty"` AcceptedCAs []*common.PEMEncodedCertificate `protobuf:"bytes,4,rep,name=accepted_c_as,json=acceptedCAs,proto3" json:"accepted_c_as,omitempty"` SkipVerifyingClientCert bool `protobuf:"varint,5,opt,name=skip_verifying_client_cert,json=skipVerifyingClientCert,proto3" json:"skip_verifying_client_cert,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *APICertsSpec) Reset() { *x = APICertsSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *APICertsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*APICertsSpec) ProtoMessage() {} func (x *APICertsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use APICertsSpec.ProtoReflect.Descriptor instead. func (*APICertsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{0} } func (x *APICertsSpec) GetClient() *common.PEMEncodedCertificateAndKey { if x != nil { return x.Client } return nil } func (x *APICertsSpec) GetServer() *common.PEMEncodedCertificateAndKey { if x != nil { return x.Server } return nil } func (x *APICertsSpec) GetAcceptedCAs() []*common.PEMEncodedCertificate { if x != nil { return x.AcceptedCAs } return nil } func (x *APICertsSpec) GetSkipVerifyingClientCert() bool { if x != nil { return x.SkipVerifyingClientCert } return false } // CertSANSpec describes fields of the cert SANs. type CertSANSpec struct { state protoimpl.MessageState `protogen:"open.v1"` IPs []*common.NetIP `protobuf:"bytes,1,rep,name=i_ps,json=iPs,proto3" json:"i_ps,omitempty"` DnsNames []string `protobuf:"bytes,2,rep,name=dns_names,json=dnsNames,proto3" json:"dns_names,omitempty"` Fqdn string `protobuf:"bytes,3,opt,name=fqdn,proto3" json:"fqdn,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CertSANSpec) Reset() { *x = CertSANSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CertSANSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*CertSANSpec) ProtoMessage() {} func (x *CertSANSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CertSANSpec.ProtoReflect.Descriptor instead. func (*CertSANSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{1} } func (x *CertSANSpec) GetIPs() []*common.NetIP { if x != nil { return x.IPs } return nil } func (x *CertSANSpec) GetDnsNames() []string { if x != nil { return x.DnsNames } return nil } func (x *CertSANSpec) GetFqdn() string { if x != nil { return x.Fqdn } return "" } // EncryptionSaltSpec describes the salt. type EncryptionSaltSpec struct { state protoimpl.MessageState `protogen:"open.v1"` DiskSalt []byte `protobuf:"bytes,1,opt,name=disk_salt,json=diskSalt,proto3" json:"disk_salt,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EncryptionSaltSpec) Reset() { *x = EncryptionSaltSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EncryptionSaltSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EncryptionSaltSpec) ProtoMessage() {} func (x *EncryptionSaltSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EncryptionSaltSpec.ProtoReflect.Descriptor instead. func (*EncryptionSaltSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{2} } func (x *EncryptionSaltSpec) GetDiskSalt() []byte { if x != nil { return x.DiskSalt } return nil } // EtcdCertsSpec describes etcd certs secrets. type EtcdCertsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Etcd *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=etcd,proto3" json:"etcd,omitempty"` EtcdPeer *common.PEMEncodedCertificateAndKey `protobuf:"bytes,2,opt,name=etcd_peer,json=etcdPeer,proto3" json:"etcd_peer,omitempty"` EtcdAdmin *common.PEMEncodedCertificateAndKey `protobuf:"bytes,3,opt,name=etcd_admin,json=etcdAdmin,proto3" json:"etcd_admin,omitempty"` EtcdApiServer *common.PEMEncodedCertificateAndKey `protobuf:"bytes,4,opt,name=etcd_api_server,json=etcdApiServer,proto3" json:"etcd_api_server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdCertsSpec) Reset() { *x = EtcdCertsSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdCertsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdCertsSpec) ProtoMessage() {} func (x *EtcdCertsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdCertsSpec.ProtoReflect.Descriptor instead. func (*EtcdCertsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{3} } func (x *EtcdCertsSpec) GetEtcd() *common.PEMEncodedCertificateAndKey { if x != nil { return x.Etcd } return nil } func (x *EtcdCertsSpec) GetEtcdPeer() *common.PEMEncodedCertificateAndKey { if x != nil { return x.EtcdPeer } return nil } func (x *EtcdCertsSpec) GetEtcdAdmin() *common.PEMEncodedCertificateAndKey { if x != nil { return x.EtcdAdmin } return nil } func (x *EtcdCertsSpec) GetEtcdApiServer() *common.PEMEncodedCertificateAndKey { if x != nil { return x.EtcdApiServer } return nil } // EtcdRootSpec describes etcd CA secrets. type EtcdRootSpec struct { state protoimpl.MessageState `protogen:"open.v1"` EtcdCa *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=etcd_ca,json=etcdCa,proto3" json:"etcd_ca,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *EtcdRootSpec) Reset() { *x = EtcdRootSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *EtcdRootSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*EtcdRootSpec) ProtoMessage() {} func (x *EtcdRootSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use EtcdRootSpec.ProtoReflect.Descriptor instead. func (*EtcdRootSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{4} } func (x *EtcdRootSpec) GetEtcdCa() *common.PEMEncodedCertificateAndKey { if x != nil { return x.EtcdCa } return nil } // KubeletSpec describes root Kubernetes secrets. type KubeletSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Endpoint *common.URL `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` BootstrapTokenId string `protobuf:"bytes,3,opt,name=bootstrap_token_id,json=bootstrapTokenId,proto3" json:"bootstrap_token_id,omitempty"` BootstrapTokenSecret string `protobuf:"bytes,4,opt,name=bootstrap_token_secret,json=bootstrapTokenSecret,proto3" json:"bootstrap_token_secret,omitempty"` AcceptedCAs []*common.PEMEncodedCertificate `protobuf:"bytes,5,rep,name=accepted_c_as,json=acceptedCAs,proto3" json:"accepted_c_as,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubeletSpec) Reset() { *x = KubeletSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubeletSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubeletSpec) ProtoMessage() {} func (x *KubeletSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubeletSpec.ProtoReflect.Descriptor instead. func (*KubeletSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{5} } func (x *KubeletSpec) GetEndpoint() *common.URL { if x != nil { return x.Endpoint } return nil } func (x *KubeletSpec) GetBootstrapTokenId() string { if x != nil { return x.BootstrapTokenId } return "" } func (x *KubeletSpec) GetBootstrapTokenSecret() string { if x != nil { return x.BootstrapTokenSecret } return "" } func (x *KubeletSpec) GetAcceptedCAs() []*common.PEMEncodedCertificate { if x != nil { return x.AcceptedCAs } return nil } // KubernetesCertsSpec describes generated Kubernetes certificates. type KubernetesCertsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` SchedulerKubeconfig string `protobuf:"bytes,4,opt,name=scheduler_kubeconfig,json=schedulerKubeconfig,proto3" json:"scheduler_kubeconfig,omitempty"` ControllerManagerKubeconfig string `protobuf:"bytes,5,opt,name=controller_manager_kubeconfig,json=controllerManagerKubeconfig,proto3" json:"controller_manager_kubeconfig,omitempty"` LocalhostAdminKubeconfig string `protobuf:"bytes,6,opt,name=localhost_admin_kubeconfig,json=localhostAdminKubeconfig,proto3" json:"localhost_admin_kubeconfig,omitempty"` AdminKubeconfig string `protobuf:"bytes,7,opt,name=admin_kubeconfig,json=adminKubeconfig,proto3" json:"admin_kubeconfig,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubernetesCertsSpec) Reset() { *x = KubernetesCertsSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubernetesCertsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubernetesCertsSpec) ProtoMessage() {} func (x *KubernetesCertsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubernetesCertsSpec.ProtoReflect.Descriptor instead. func (*KubernetesCertsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{6} } func (x *KubernetesCertsSpec) GetSchedulerKubeconfig() string { if x != nil { return x.SchedulerKubeconfig } return "" } func (x *KubernetesCertsSpec) GetControllerManagerKubeconfig() string { if x != nil { return x.ControllerManagerKubeconfig } return "" } func (x *KubernetesCertsSpec) GetLocalhostAdminKubeconfig() string { if x != nil { return x.LocalhostAdminKubeconfig } return "" } func (x *KubernetesCertsSpec) GetAdminKubeconfig() string { if x != nil { return x.AdminKubeconfig } return "" } // KubernetesDynamicCertsSpec describes generated KubernetesCerts certificates. type KubernetesDynamicCertsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ApiServer *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=api_server,json=apiServer,proto3" json:"api_server,omitempty"` ApiServerKubeletClient *common.PEMEncodedCertificateAndKey `protobuf:"bytes,2,opt,name=api_server_kubelet_client,json=apiServerKubeletClient,proto3" json:"api_server_kubelet_client,omitempty"` FrontProxy *common.PEMEncodedCertificateAndKey `protobuf:"bytes,3,opt,name=front_proxy,json=frontProxy,proto3" json:"front_proxy,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubernetesDynamicCertsSpec) Reset() { *x = KubernetesDynamicCertsSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubernetesDynamicCertsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubernetesDynamicCertsSpec) ProtoMessage() {} func (x *KubernetesDynamicCertsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubernetesDynamicCertsSpec.ProtoReflect.Descriptor instead. func (*KubernetesDynamicCertsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{7} } func (x *KubernetesDynamicCertsSpec) GetApiServer() *common.PEMEncodedCertificateAndKey { if x != nil { return x.ApiServer } return nil } func (x *KubernetesDynamicCertsSpec) GetApiServerKubeletClient() *common.PEMEncodedCertificateAndKey { if x != nil { return x.ApiServerKubeletClient } return nil } func (x *KubernetesDynamicCertsSpec) GetFrontProxy() *common.PEMEncodedCertificateAndKey { if x != nil { return x.FrontProxy } return nil } // KubernetesRootSpec describes root Kubernetes secrets. type KubernetesRootSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Endpoint *common.URL `protobuf:"bytes,2,opt,name=endpoint,proto3" json:"endpoint,omitempty"` LocalEndpoint *common.URL `protobuf:"bytes,3,opt,name=local_endpoint,json=localEndpoint,proto3" json:"local_endpoint,omitempty"` CertSaNs []string `protobuf:"bytes,4,rep,name=cert_sa_ns,json=certSaNs,proto3" json:"cert_sa_ns,omitempty"` DnsDomain string `protobuf:"bytes,6,opt,name=dns_domain,json=dnsDomain,proto3" json:"dns_domain,omitempty"` IssuingCa *common.PEMEncodedCertificateAndKey `protobuf:"bytes,7,opt,name=issuing_ca,json=issuingCa,proto3" json:"issuing_ca,omitempty"` ServiceAccount *common.PEMEncodedKey `protobuf:"bytes,8,opt,name=service_account,json=serviceAccount,proto3" json:"service_account,omitempty"` AggregatorCa *common.PEMEncodedCertificateAndKey `protobuf:"bytes,9,opt,name=aggregator_ca,json=aggregatorCa,proto3" json:"aggregator_ca,omitempty"` AescbcEncryptionSecret string `protobuf:"bytes,10,opt,name=aescbc_encryption_secret,json=aescbcEncryptionSecret,proto3" json:"aescbc_encryption_secret,omitempty"` BootstrapTokenId string `protobuf:"bytes,11,opt,name=bootstrap_token_id,json=bootstrapTokenId,proto3" json:"bootstrap_token_id,omitempty"` BootstrapTokenSecret string `protobuf:"bytes,12,opt,name=bootstrap_token_secret,json=bootstrapTokenSecret,proto3" json:"bootstrap_token_secret,omitempty"` SecretboxEncryptionSecret string `protobuf:"bytes,13,opt,name=secretbox_encryption_secret,json=secretboxEncryptionSecret,proto3" json:"secretbox_encryption_secret,omitempty"` ApiServerIps []*common.NetIP `protobuf:"bytes,14,rep,name=api_server_ips,json=apiServerIps,proto3" json:"api_server_ips,omitempty"` AcceptedCAs []*common.PEMEncodedCertificate `protobuf:"bytes,15,rep,name=accepted_c_as,json=acceptedCAs,proto3" json:"accepted_c_as,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *KubernetesRootSpec) Reset() { *x = KubernetesRootSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *KubernetesRootSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*KubernetesRootSpec) ProtoMessage() {} func (x *KubernetesRootSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use KubernetesRootSpec.ProtoReflect.Descriptor instead. func (*KubernetesRootSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{8} } func (x *KubernetesRootSpec) GetName() string { if x != nil { return x.Name } return "" } func (x *KubernetesRootSpec) GetEndpoint() *common.URL { if x != nil { return x.Endpoint } return nil } func (x *KubernetesRootSpec) GetLocalEndpoint() *common.URL { if x != nil { return x.LocalEndpoint } return nil } func (x *KubernetesRootSpec) GetCertSaNs() []string { if x != nil { return x.CertSaNs } return nil } func (x *KubernetesRootSpec) GetDnsDomain() string { if x != nil { return x.DnsDomain } return "" } func (x *KubernetesRootSpec) GetIssuingCa() *common.PEMEncodedCertificateAndKey { if x != nil { return x.IssuingCa } return nil } func (x *KubernetesRootSpec) GetServiceAccount() *common.PEMEncodedKey { if x != nil { return x.ServiceAccount } return nil } func (x *KubernetesRootSpec) GetAggregatorCa() *common.PEMEncodedCertificateAndKey { if x != nil { return x.AggregatorCa } return nil } func (x *KubernetesRootSpec) GetAescbcEncryptionSecret() string { if x != nil { return x.AescbcEncryptionSecret } return "" } func (x *KubernetesRootSpec) GetBootstrapTokenId() string { if x != nil { return x.BootstrapTokenId } return "" } func (x *KubernetesRootSpec) GetBootstrapTokenSecret() string { if x != nil { return x.BootstrapTokenSecret } return "" } func (x *KubernetesRootSpec) GetSecretboxEncryptionSecret() string { if x != nil { return x.SecretboxEncryptionSecret } return "" } func (x *KubernetesRootSpec) GetApiServerIps() []*common.NetIP { if x != nil { return x.ApiServerIps } return nil } func (x *KubernetesRootSpec) GetAcceptedCAs() []*common.PEMEncodedCertificate { if x != nil { return x.AcceptedCAs } return nil } // MaintenanceRootSpec describes maintenance service CA. type MaintenanceRootSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Ca *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=ca,proto3" json:"ca,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *MaintenanceRootSpec) Reset() { *x = MaintenanceRootSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *MaintenanceRootSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*MaintenanceRootSpec) ProtoMessage() {} func (x *MaintenanceRootSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use MaintenanceRootSpec.ProtoReflect.Descriptor instead. func (*MaintenanceRootSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{9} } func (x *MaintenanceRootSpec) GetCa() *common.PEMEncodedCertificateAndKey { if x != nil { return x.Ca } return nil } // OSRootSpec describes operating system CA. type OSRootSpec struct { state protoimpl.MessageState `protogen:"open.v1"` IssuingCa *common.PEMEncodedCertificateAndKey `protobuf:"bytes,1,opt,name=issuing_ca,json=issuingCa,proto3" json:"issuing_ca,omitempty"` CertSaniPs []*common.NetIP `protobuf:"bytes,2,rep,name=cert_sani_ps,json=certSaniPs,proto3" json:"cert_sani_ps,omitempty"` CertSandnsNames []string `protobuf:"bytes,3,rep,name=cert_sandns_names,json=certSandnsNames,proto3" json:"cert_sandns_names,omitempty"` Token string `protobuf:"bytes,4,opt,name=token,proto3" json:"token,omitempty"` AcceptedCAs []*common.PEMEncodedCertificate `protobuf:"bytes,5,rep,name=accepted_c_as,json=acceptedCAs,proto3" json:"accepted_c_as,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *OSRootSpec) Reset() { *x = OSRootSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *OSRootSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*OSRootSpec) ProtoMessage() {} func (x *OSRootSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use OSRootSpec.ProtoReflect.Descriptor instead. func (*OSRootSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{10} } func (x *OSRootSpec) GetIssuingCa() *common.PEMEncodedCertificateAndKey { if x != nil { return x.IssuingCa } return nil } func (x *OSRootSpec) GetCertSaniPs() []*common.NetIP { if x != nil { return x.CertSaniPs } return nil } func (x *OSRootSpec) GetCertSandnsNames() []string { if x != nil { return x.CertSandnsNames } return nil } func (x *OSRootSpec) GetToken() string { if x != nil { return x.Token } return "" } func (x *OSRootSpec) GetAcceptedCAs() []*common.PEMEncodedCertificate { if x != nil { return x.AcceptedCAs } return nil } // TrustdCertsSpec describes etcd certs secrets. type TrustdCertsSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Server *common.PEMEncodedCertificateAndKey `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` AcceptedCAs []*common.PEMEncodedCertificate `protobuf:"bytes,3,rep,name=accepted_c_as,json=acceptedCAs,proto3" json:"accepted_c_as,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TrustdCertsSpec) Reset() { *x = TrustdCertsSpec{} mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TrustdCertsSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TrustdCertsSpec) ProtoMessage() {} func (x *TrustdCertsSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_secrets_secrets_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TrustdCertsSpec.ProtoReflect.Descriptor instead. func (*TrustdCertsSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_secrets_secrets_proto_rawDescGZIP(), []int{11} } func (x *TrustdCertsSpec) GetServer() *common.PEMEncodedCertificateAndKey { if x != nil { return x.Server } return nil } func (x *TrustdCertsSpec) GetAcceptedCAs() []*common.PEMEncodedCertificate { if x != nil { return x.AcceptedCAs } return nil } var File_resource_definitions_secrets_secrets_proto protoreflect.FileDescriptor const file_resource_definitions_secrets_secrets_proto_rawDesc = "" + "\n" + "*resource/definitions/secrets/secrets.proto\x12\"talos.resource.definitions.secrets\x1a\x13common/common.proto\"\x88\x02\n" + "\fAPICertsSpec\x12;\n" + "\x06client\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x06client\x12;\n" + "\x06server\x18\x03 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x06server\x12A\n" + "\raccepted_c_as\x18\x04 \x03(\v2\x1d.common.PEMEncodedCertificateR\vacceptedCAs\x12;\n" + "\x1askip_verifying_client_cert\x18\x05 \x01(\bR\x17skipVerifyingClientCert\"`\n" + "\vCertSANSpec\x12 \n" + "\x04i_ps\x18\x01 \x03(\v2\r.common.NetIPR\x03iPs\x12\x1b\n" + "\tdns_names\x18\x02 \x03(\tR\bdnsNames\x12\x12\n" + "\x04fqdn\x18\x03 \x01(\tR\x04fqdn\"1\n" + "\x12EncryptionSaltSpec\x12\x1b\n" + "\tdisk_salt\x18\x01 \x01(\fR\bdiskSalt\"\x9b\x02\n" + "\rEtcdCertsSpec\x127\n" + "\x04etcd\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x04etcd\x12@\n" + "\tetcd_peer\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\betcdPeer\x12B\n" + "\n" + "etcd_admin\x18\x03 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\tetcdAdmin\x12K\n" + "\x0fetcd_api_server\x18\x04 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\retcdApiServer\"L\n" + "\fEtcdRootSpec\x12<\n" + "\aetcd_ca\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x06etcdCa\"\xdd\x01\n" + "\vKubeletSpec\x12'\n" + "\bendpoint\x18\x01 \x01(\v2\v.common.URLR\bendpoint\x12,\n" + "\x12bootstrap_token_id\x18\x03 \x01(\tR\x10bootstrapTokenId\x124\n" + "\x16bootstrap_token_secret\x18\x04 \x01(\tR\x14bootstrapTokenSecret\x12A\n" + "\raccepted_c_as\x18\x05 \x03(\v2\x1d.common.PEMEncodedCertificateR\vacceptedCAs\"\xf5\x01\n" + "\x13KubernetesCertsSpec\x121\n" + "\x14scheduler_kubeconfig\x18\x04 \x01(\tR\x13schedulerKubeconfig\x12B\n" + "\x1dcontroller_manager_kubeconfig\x18\x05 \x01(\tR\x1bcontrollerManagerKubeconfig\x12<\n" + "\x1alocalhost_admin_kubeconfig\x18\x06 \x01(\tR\x18localhostAdminKubeconfig\x12)\n" + "\x10admin_kubeconfig\x18\a \x01(\tR\x0fadminKubeconfig\"\x86\x02\n" + "\x1aKubernetesDynamicCertsSpec\x12B\n" + "\n" + "api_server\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\tapiServer\x12^\n" + "\x19api_server_kubelet_client\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x16apiServerKubeletClient\x12D\n" + "\vfront_proxy\x18\x03 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\n" + "frontProxy\"\xe6\x05\n" + "\x12KubernetesRootSpec\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12'\n" + "\bendpoint\x18\x02 \x01(\v2\v.common.URLR\bendpoint\x122\n" + "\x0elocal_endpoint\x18\x03 \x01(\v2\v.common.URLR\rlocalEndpoint\x12\x1c\n" + "\n" + "cert_sa_ns\x18\x04 \x03(\tR\bcertSaNs\x12\x1d\n" + "\n" + "dns_domain\x18\x06 \x01(\tR\tdnsDomain\x12B\n" + "\n" + "issuing_ca\x18\a \x01(\v2#.common.PEMEncodedCertificateAndKeyR\tissuingCa\x12>\n" + "\x0fservice_account\x18\b \x01(\v2\x15.common.PEMEncodedKeyR\x0eserviceAccount\x12H\n" + "\raggregator_ca\x18\t \x01(\v2#.common.PEMEncodedCertificateAndKeyR\faggregatorCa\x128\n" + "\x18aescbc_encryption_secret\x18\n" + " \x01(\tR\x16aescbcEncryptionSecret\x12,\n" + "\x12bootstrap_token_id\x18\v \x01(\tR\x10bootstrapTokenId\x124\n" + "\x16bootstrap_token_secret\x18\f \x01(\tR\x14bootstrapTokenSecret\x12>\n" + "\x1bsecretbox_encryption_secret\x18\r \x01(\tR\x19secretboxEncryptionSecret\x123\n" + "\x0eapi_server_ips\x18\x0e \x03(\v2\r.common.NetIPR\fapiServerIps\x12A\n" + "\raccepted_c_as\x18\x0f \x03(\v2\x1d.common.PEMEncodedCertificateR\vacceptedCAs\"J\n" + "\x13MaintenanceRootSpec\x123\n" + "\x02ca\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x02ca\"\x86\x02\n" + "\n" + "OSRootSpec\x12B\n" + "\n" + "issuing_ca\x18\x01 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\tissuingCa\x12/\n" + "\fcert_sani_ps\x18\x02 \x03(\v2\r.common.NetIPR\n" + "certSaniPs\x12*\n" + "\x11cert_sandns_names\x18\x03 \x03(\tR\x0fcertSandnsNames\x12\x14\n" + "\x05token\x18\x04 \x01(\tR\x05token\x12A\n" + "\raccepted_c_as\x18\x05 \x03(\v2\x1d.common.PEMEncodedCertificateR\vacceptedCAs\"\x91\x01\n" + "\x0fTrustdCertsSpec\x12;\n" + "\x06server\x18\x02 \x01(\v2#.common.PEMEncodedCertificateAndKeyR\x06server\x12A\n" + "\raccepted_c_as\x18\x03 \x03(\v2\x1d.common.PEMEncodedCertificateR\vacceptedCAsBx\n" + "*dev.talos.api.resource.definitions.secretsZJgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/secretsb\x06proto3" var ( file_resource_definitions_secrets_secrets_proto_rawDescOnce sync.Once file_resource_definitions_secrets_secrets_proto_rawDescData []byte ) func file_resource_definitions_secrets_secrets_proto_rawDescGZIP() []byte { file_resource_definitions_secrets_secrets_proto_rawDescOnce.Do(func() { file_resource_definitions_secrets_secrets_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_secrets_secrets_proto_rawDesc), len(file_resource_definitions_secrets_secrets_proto_rawDesc))) }) return file_resource_definitions_secrets_secrets_proto_rawDescData } var file_resource_definitions_secrets_secrets_proto_msgTypes = make([]protoimpl.MessageInfo, 12) var file_resource_definitions_secrets_secrets_proto_goTypes = []any{ (*APICertsSpec)(nil), // 0: talos.resource.definitions.secrets.APICertsSpec (*CertSANSpec)(nil), // 1: talos.resource.definitions.secrets.CertSANSpec (*EncryptionSaltSpec)(nil), // 2: talos.resource.definitions.secrets.EncryptionSaltSpec (*EtcdCertsSpec)(nil), // 3: talos.resource.definitions.secrets.EtcdCertsSpec (*EtcdRootSpec)(nil), // 4: talos.resource.definitions.secrets.EtcdRootSpec (*KubeletSpec)(nil), // 5: talos.resource.definitions.secrets.KubeletSpec (*KubernetesCertsSpec)(nil), // 6: talos.resource.definitions.secrets.KubernetesCertsSpec (*KubernetesDynamicCertsSpec)(nil), // 7: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec (*KubernetesRootSpec)(nil), // 8: talos.resource.definitions.secrets.KubernetesRootSpec (*MaintenanceRootSpec)(nil), // 9: talos.resource.definitions.secrets.MaintenanceRootSpec (*OSRootSpec)(nil), // 10: talos.resource.definitions.secrets.OSRootSpec (*TrustdCertsSpec)(nil), // 11: talos.resource.definitions.secrets.TrustdCertsSpec (*common.PEMEncodedCertificateAndKey)(nil), // 12: common.PEMEncodedCertificateAndKey (*common.PEMEncodedCertificate)(nil), // 13: common.PEMEncodedCertificate (*common.NetIP)(nil), // 14: common.NetIP (*common.URL)(nil), // 15: common.URL (*common.PEMEncodedKey)(nil), // 16: common.PEMEncodedKey } var file_resource_definitions_secrets_secrets_proto_depIdxs = []int32{ 12, // 0: talos.resource.definitions.secrets.APICertsSpec.client:type_name -> common.PEMEncodedCertificateAndKey 12, // 1: talos.resource.definitions.secrets.APICertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey 13, // 2: talos.resource.definitions.secrets.APICertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 14, // 3: talos.resource.definitions.secrets.CertSANSpec.i_ps:type_name -> common.NetIP 12, // 4: talos.resource.definitions.secrets.EtcdCertsSpec.etcd:type_name -> common.PEMEncodedCertificateAndKey 12, // 5: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_peer:type_name -> common.PEMEncodedCertificateAndKey 12, // 6: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_admin:type_name -> common.PEMEncodedCertificateAndKey 12, // 7: talos.resource.definitions.secrets.EtcdCertsSpec.etcd_api_server:type_name -> common.PEMEncodedCertificateAndKey 12, // 8: talos.resource.definitions.secrets.EtcdRootSpec.etcd_ca:type_name -> common.PEMEncodedCertificateAndKey 15, // 9: talos.resource.definitions.secrets.KubeletSpec.endpoint:type_name -> common.URL 13, // 10: talos.resource.definitions.secrets.KubeletSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 12, // 11: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server:type_name -> common.PEMEncodedCertificateAndKey 12, // 12: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.api_server_kubelet_client:type_name -> common.PEMEncodedCertificateAndKey 12, // 13: talos.resource.definitions.secrets.KubernetesDynamicCertsSpec.front_proxy:type_name -> common.PEMEncodedCertificateAndKey 15, // 14: talos.resource.definitions.secrets.KubernetesRootSpec.endpoint:type_name -> common.URL 15, // 15: talos.resource.definitions.secrets.KubernetesRootSpec.local_endpoint:type_name -> common.URL 12, // 16: talos.resource.definitions.secrets.KubernetesRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey 16, // 17: talos.resource.definitions.secrets.KubernetesRootSpec.service_account:type_name -> common.PEMEncodedKey 12, // 18: talos.resource.definitions.secrets.KubernetesRootSpec.aggregator_ca:type_name -> common.PEMEncodedCertificateAndKey 14, // 19: talos.resource.definitions.secrets.KubernetesRootSpec.api_server_ips:type_name -> common.NetIP 13, // 20: talos.resource.definitions.secrets.KubernetesRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 12, // 21: talos.resource.definitions.secrets.MaintenanceRootSpec.ca:type_name -> common.PEMEncodedCertificateAndKey 12, // 22: talos.resource.definitions.secrets.OSRootSpec.issuing_ca:type_name -> common.PEMEncodedCertificateAndKey 14, // 23: talos.resource.definitions.secrets.OSRootSpec.cert_sani_ps:type_name -> common.NetIP 13, // 24: talos.resource.definitions.secrets.OSRootSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 12, // 25: talos.resource.definitions.secrets.TrustdCertsSpec.server:type_name -> common.PEMEncodedCertificateAndKey 13, // 26: talos.resource.definitions.secrets.TrustdCertsSpec.accepted_c_as:type_name -> common.PEMEncodedCertificate 27, // [27:27] is the sub-list for method output_type 27, // [27:27] is the sub-list for method input_type 27, // [27:27] is the sub-list for extension type_name 27, // [27:27] is the sub-list for extension extendee 0, // [0:27] is the sub-list for field type_name } func init() { file_resource_definitions_secrets_secrets_proto_init() } func file_resource_definitions_secrets_secrets_proto_init() { if File_resource_definitions_secrets_secrets_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_secrets_secrets_proto_rawDesc), len(file_resource_definitions_secrets_secrets_proto_rawDesc)), NumEnums: 0, NumMessages: 12, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_secrets_secrets_proto_goTypes, DependencyIndexes: file_resource_definitions_secrets_secrets_proto_depIdxs, MessageInfos: file_resource_definitions_secrets_secrets_proto_msgTypes, }.Build() File_resource_definitions_secrets_secrets_proto = out.File file_resource_definitions_secrets_secrets_proto_goTypes = nil file_resource_definitions_secrets_secrets_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/secrets/secrets_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/secrets/secrets.proto package secrets import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *APICertsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *APICertsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *APICertsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SkipVerifyingClientCert { i-- if m.SkipVerifyingClientCert { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if len(m.AcceptedCAs) > 0 { for iNdEx := len(m.AcceptedCAs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AcceptedCAs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AcceptedCAs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } } if m.Server != nil { if vtmsg, ok := interface{}(m.Server).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Server) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Client != nil { if vtmsg, ok := interface{}(m.Client).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Client) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } return len(dAtA) - i, nil } func (m *CertSANSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CertSANSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CertSANSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Fqdn) > 0 { i -= len(m.Fqdn) copy(dAtA[i:], m.Fqdn) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Fqdn))) i-- dAtA[i] = 0x1a } if len(m.DnsNames) > 0 { for iNdEx := len(m.DnsNames) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.DnsNames[iNdEx]) copy(dAtA[i:], m.DnsNames[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DnsNames[iNdEx]))) i-- dAtA[i] = 0x12 } } if len(m.IPs) > 0 { for iNdEx := len(m.IPs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.IPs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.IPs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *EncryptionSaltSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EncryptionSaltSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EncryptionSaltSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.DiskSalt) > 0 { i -= len(m.DiskSalt) copy(dAtA[i:], m.DiskSalt) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DiskSalt))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdCertsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdCertsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdCertsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.EtcdApiServer != nil { if vtmsg, ok := interface{}(m.EtcdApiServer).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.EtcdApiServer) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } if m.EtcdAdmin != nil { if vtmsg, ok := interface{}(m.EtcdAdmin).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.EtcdAdmin) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.EtcdPeer != nil { if vtmsg, ok := interface{}(m.EtcdPeer).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.EtcdPeer) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.Etcd != nil { if vtmsg, ok := interface{}(m.Etcd).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Etcd) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *EtcdRootSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *EtcdRootSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *EtcdRootSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.EtcdCa != nil { if vtmsg, ok := interface{}(m.EtcdCa).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.EtcdCa) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubeletSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubeletSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubeletSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AcceptedCAs) > 0 { for iNdEx := len(m.AcceptedCAs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AcceptedCAs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AcceptedCAs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if len(m.BootstrapTokenSecret) > 0 { i -= len(m.BootstrapTokenSecret) copy(dAtA[i:], m.BootstrapTokenSecret) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BootstrapTokenSecret))) i-- dAtA[i] = 0x22 } if len(m.BootstrapTokenId) > 0 { i -= len(m.BootstrapTokenId) copy(dAtA[i:], m.BootstrapTokenId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BootstrapTokenId))) i-- dAtA[i] = 0x1a } if m.Endpoint != nil { if vtmsg, ok := interface{}(m.Endpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubernetesCertsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubernetesCertsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubernetesCertsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AdminKubeconfig) > 0 { i -= len(m.AdminKubeconfig) copy(dAtA[i:], m.AdminKubeconfig) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AdminKubeconfig))) i-- dAtA[i] = 0x3a } if len(m.LocalhostAdminKubeconfig) > 0 { i -= len(m.LocalhostAdminKubeconfig) copy(dAtA[i:], m.LocalhostAdminKubeconfig) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LocalhostAdminKubeconfig))) i-- dAtA[i] = 0x32 } if len(m.ControllerManagerKubeconfig) > 0 { i -= len(m.ControllerManagerKubeconfig) copy(dAtA[i:], m.ControllerManagerKubeconfig) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ControllerManagerKubeconfig))) i-- dAtA[i] = 0x2a } if len(m.SchedulerKubeconfig) > 0 { i -= len(m.SchedulerKubeconfig) copy(dAtA[i:], m.SchedulerKubeconfig) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SchedulerKubeconfig))) i-- dAtA[i] = 0x22 } return len(dAtA) - i, nil } func (m *KubernetesDynamicCertsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubernetesDynamicCertsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubernetesDynamicCertsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.FrontProxy != nil { if vtmsg, ok := interface{}(m.FrontProxy).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.FrontProxy) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.ApiServerKubeletClient != nil { if vtmsg, ok := interface{}(m.ApiServerKubeletClient).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ApiServerKubeletClient) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if m.ApiServer != nil { if vtmsg, ok := interface{}(m.ApiServer).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ApiServer) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *KubernetesRootSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *KubernetesRootSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *KubernetesRootSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AcceptedCAs) > 0 { for iNdEx := len(m.AcceptedCAs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AcceptedCAs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AcceptedCAs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x7a } } if len(m.ApiServerIps) > 0 { for iNdEx := len(m.ApiServerIps) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.ApiServerIps[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ApiServerIps[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x72 } } if len(m.SecretboxEncryptionSecret) > 0 { i -= len(m.SecretboxEncryptionSecret) copy(dAtA[i:], m.SecretboxEncryptionSecret) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SecretboxEncryptionSecret))) i-- dAtA[i] = 0x6a } if len(m.BootstrapTokenSecret) > 0 { i -= len(m.BootstrapTokenSecret) copy(dAtA[i:], m.BootstrapTokenSecret) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BootstrapTokenSecret))) i-- dAtA[i] = 0x62 } if len(m.BootstrapTokenId) > 0 { i -= len(m.BootstrapTokenId) copy(dAtA[i:], m.BootstrapTokenId) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BootstrapTokenId))) i-- dAtA[i] = 0x5a } if len(m.AescbcEncryptionSecret) > 0 { i -= len(m.AescbcEncryptionSecret) copy(dAtA[i:], m.AescbcEncryptionSecret) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.AescbcEncryptionSecret))) i-- dAtA[i] = 0x52 } if m.AggregatorCa != nil { if vtmsg, ok := interface{}(m.AggregatorCa).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AggregatorCa) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x4a } if m.ServiceAccount != nil { if vtmsg, ok := interface{}(m.ServiceAccount).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.ServiceAccount) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x42 } if m.IssuingCa != nil { if vtmsg, ok := interface{}(m.IssuingCa).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.IssuingCa) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x3a } if len(m.DnsDomain) > 0 { i -= len(m.DnsDomain) copy(dAtA[i:], m.DnsDomain) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DnsDomain))) i-- dAtA[i] = 0x32 } if len(m.CertSaNs) > 0 { for iNdEx := len(m.CertSaNs) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.CertSaNs[iNdEx]) copy(dAtA[i:], m.CertSaNs[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CertSaNs[iNdEx]))) i-- dAtA[i] = 0x22 } } if m.LocalEndpoint != nil { if vtmsg, ok := interface{}(m.LocalEndpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.LocalEndpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } if m.Endpoint != nil { if vtmsg, ok := interface{}(m.Endpoint).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Endpoint) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *MaintenanceRootSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *MaintenanceRootSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *MaintenanceRootSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Ca != nil { if vtmsg, ok := interface{}(m.Ca).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Ca) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *OSRootSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *OSRootSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *OSRootSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AcceptedCAs) > 0 { for iNdEx := len(m.AcceptedCAs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AcceptedCAs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AcceptedCAs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x2a } } if len(m.Token) > 0 { i -= len(m.Token) copy(dAtA[i:], m.Token) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Token))) i-- dAtA[i] = 0x22 } if len(m.CertSandnsNames) > 0 { for iNdEx := len(m.CertSandnsNames) - 1; iNdEx >= 0; iNdEx-- { i -= len(m.CertSandnsNames[iNdEx]) copy(dAtA[i:], m.CertSandnsNames[iNdEx]) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CertSandnsNames[iNdEx]))) i-- dAtA[i] = 0x1a } } if len(m.CertSaniPs) > 0 { for iNdEx := len(m.CertSaniPs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.CertSaniPs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.CertSaniPs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } } if m.IssuingCa != nil { if vtmsg, ok := interface{}(m.IssuingCa).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.IssuingCa) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TrustdCertsSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TrustdCertsSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TrustdCertsSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.AcceptedCAs) > 0 { for iNdEx := len(m.AcceptedCAs) - 1; iNdEx >= 0; iNdEx-- { if vtmsg, ok := interface{}(m.AcceptedCAs[iNdEx]).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.AcceptedCAs[iNdEx]) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x1a } } if m.Server != nil { if vtmsg, ok := interface{}(m.Server).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Server) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x12 } return len(dAtA) - i, nil } func (m *APICertsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Client != nil { if size, ok := interface{}(m.Client).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Client) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Server != nil { if size, ok := interface{}(m.Server).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Server) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AcceptedCAs) > 0 { for _, e := range m.AcceptedCAs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if m.SkipVerifyingClientCert { n += 2 } n += len(m.unknownFields) return n } func (m *CertSANSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.IPs) > 0 { for _, e := range m.IPs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.DnsNames) > 0 { for _, s := range m.DnsNames { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Fqdn) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EncryptionSaltSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.DiskSalt) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdCertsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Etcd != nil { if size, ok := interface{}(m.Etcd).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Etcd) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EtcdPeer != nil { if size, ok := interface{}(m.EtcdPeer).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.EtcdPeer) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EtcdAdmin != nil { if size, ok := interface{}(m.EtcdAdmin).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.EtcdAdmin) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EtcdApiServer != nil { if size, ok := interface{}(m.EtcdApiServer).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.EtcdApiServer) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *EtcdRootSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.EtcdCa != nil { if size, ok := interface{}(m.EtcdCa).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.EtcdCa) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KubeletSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Endpoint != nil { if size, ok := interface{}(m.Endpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Endpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BootstrapTokenId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BootstrapTokenSecret) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AcceptedCAs) > 0 { for _, e := range m.AcceptedCAs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *KubernetesCertsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.SchedulerKubeconfig) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.ControllerManagerKubeconfig) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.LocalhostAdminKubeconfig) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.AdminKubeconfig) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KubernetesDynamicCertsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.ApiServer != nil { if size, ok := interface{}(m.ApiServer).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.ApiServer) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ApiServerKubeletClient != nil { if size, ok := interface{}(m.ApiServerKubeletClient).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.ApiServerKubeletClient) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FrontProxy != nil { if size, ok := interface{}(m.FrontProxy).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.FrontProxy) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *KubernetesRootSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Endpoint != nil { if size, ok := interface{}(m.Endpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Endpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.LocalEndpoint != nil { if size, ok := interface{}(m.LocalEndpoint).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.LocalEndpoint) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.CertSaNs) > 0 { for _, s := range m.CertSaNs { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.DnsDomain) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.IssuingCa != nil { if size, ok := interface{}(m.IssuingCa).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.IssuingCa) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.ServiceAccount != nil { if size, ok := interface{}(m.ServiceAccount).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.ServiceAccount) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.AggregatorCa != nil { if size, ok := interface{}(m.AggregatorCa).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.AggregatorCa) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.AescbcEncryptionSecret) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BootstrapTokenId) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.BootstrapTokenSecret) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SecretboxEncryptionSecret) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.ApiServerIps) > 0 { for _, e := range m.ApiServerIps { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.AcceptedCAs) > 0 { for _, e := range m.AcceptedCAs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *MaintenanceRootSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Ca != nil { if size, ok := interface{}(m.Ca).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Ca) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *OSRootSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.IssuingCa != nil { if size, ok := interface{}(m.IssuingCa).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.IssuingCa) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.CertSaniPs) > 0 { for _, e := range m.CertSaniPs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } if len(m.CertSandnsNames) > 0 { for _, s := range m.CertSandnsNames { l = len(s) n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } l = len(m.Token) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AcceptedCAs) > 0 { for _, e := range m.AcceptedCAs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *TrustdCertsSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Server != nil { if size, ok := interface{}(m.Server).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Server) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.AcceptedCAs) > 0 { for _, e := range m.AcceptedCAs { if size, ok := interface{}(e).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(e) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *APICertsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: APICertsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: APICertsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Client", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Client == nil { m.Client = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.Client).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Client); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Server == nil { m.Server = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.Server).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Server); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AcceptedCAs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AcceptedCAs = append(m.AcceptedCAs, &common.PEMEncodedCertificate{}) if unmarshal, ok := interface{}(m.AcceptedCAs[len(m.AcceptedCAs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AcceptedCAs[len(m.AcceptedCAs)-1]); err != nil { return err } } iNdEx = postIndex case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipVerifyingClientCert", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipVerifyingClientCert = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CertSANSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CertSANSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CertSANSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IPs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.IPs = append(m.IPs, &common.NetIP{}) if unmarshal, ok := interface{}(m.IPs[len(m.IPs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.IPs[len(m.IPs)-1]); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsNames", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsNames = append(m.DnsNames, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Fqdn", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Fqdn = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EncryptionSaltSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EncryptionSaltSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EncryptionSaltSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DiskSalt", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DiskSalt = append(m.DiskSalt[:0], dAtA[iNdEx:postIndex]...) if m.DiskSalt == nil { m.DiskSalt = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdCertsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdCertsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdCertsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Etcd", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Etcd == nil { m.Etcd = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.Etcd).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Etcd); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EtcdPeer", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EtcdPeer == nil { m.EtcdPeer = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.EtcdPeer).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.EtcdPeer); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EtcdAdmin", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EtcdAdmin == nil { m.EtcdAdmin = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.EtcdAdmin).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.EtcdAdmin); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EtcdApiServer", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EtcdApiServer == nil { m.EtcdApiServer = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.EtcdApiServer).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.EtcdApiServer); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *EtcdRootSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: EtcdRootSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: EtcdRootSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EtcdCa", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EtcdCa == nil { m.EtcdCa = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.EtcdCa).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.EtcdCa); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubeletSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubeletSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubeletSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Endpoint == nil { m.Endpoint = &common.URL{} } if unmarshal, ok := interface{}(m.Endpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoint); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BootstrapTokenId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BootstrapTokenId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BootstrapTokenSecret", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BootstrapTokenSecret = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AcceptedCAs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AcceptedCAs = append(m.AcceptedCAs, &common.PEMEncodedCertificate{}) if unmarshal, ok := interface{}(m.AcceptedCAs[len(m.AcceptedCAs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AcceptedCAs[len(m.AcceptedCAs)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubernetesCertsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubernetesCertsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubernetesCertsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SchedulerKubeconfig", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SchedulerKubeconfig = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ControllerManagerKubeconfig", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ControllerManagerKubeconfig = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LocalhostAdminKubeconfig", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LocalhostAdminKubeconfig = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AdminKubeconfig", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AdminKubeconfig = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubernetesDynamicCertsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubernetesDynamicCertsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubernetesDynamicCertsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiServer", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ApiServer == nil { m.ApiServer = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.ApiServer).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ApiServer); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiServerKubeletClient", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ApiServerKubeletClient == nil { m.ApiServerKubeletClient = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.ApiServerKubeletClient).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ApiServerKubeletClient); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field FrontProxy", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.FrontProxy == nil { m.FrontProxy = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.FrontProxy).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.FrontProxy); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *KubernetesRootSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: KubernetesRootSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: KubernetesRootSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Endpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Endpoint == nil { m.Endpoint = &common.URL{} } if unmarshal, ok := interface{}(m.Endpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Endpoint); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LocalEndpoint", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LocalEndpoint == nil { m.LocalEndpoint = &common.URL{} } if unmarshal, ok := interface{}(m.LocalEndpoint).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.LocalEndpoint); err != nil { return err } } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CertSaNs", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CertSaNs = append(m.CertSaNs, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DnsDomain", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DnsDomain = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IssuingCa", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.IssuingCa == nil { m.IssuingCa = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.IssuingCa).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.IssuingCa); err != nil { return err } } iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ServiceAccount", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.ServiceAccount == nil { m.ServiceAccount = &common.PEMEncodedKey{} } if unmarshal, ok := interface{}(m.ServiceAccount).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ServiceAccount); err != nil { return err } } iNdEx = postIndex case 9: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AggregatorCa", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.AggregatorCa == nil { m.AggregatorCa = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.AggregatorCa).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AggregatorCa); err != nil { return err } } iNdEx = postIndex case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AescbcEncryptionSecret", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AescbcEncryptionSecret = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BootstrapTokenId", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BootstrapTokenId = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BootstrapTokenSecret", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BootstrapTokenSecret = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SecretboxEncryptionSecret", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SecretboxEncryptionSecret = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 14: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiServerIps", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApiServerIps = append(m.ApiServerIps, &common.NetIP{}) if unmarshal, ok := interface{}(m.ApiServerIps[len(m.ApiServerIps)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.ApiServerIps[len(m.ApiServerIps)-1]); err != nil { return err } } iNdEx = postIndex case 15: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AcceptedCAs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AcceptedCAs = append(m.AcceptedCAs, &common.PEMEncodedCertificate{}) if unmarshal, ok := interface{}(m.AcceptedCAs[len(m.AcceptedCAs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AcceptedCAs[len(m.AcceptedCAs)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *MaintenanceRootSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: MaintenanceRootSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: MaintenanceRootSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ca", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Ca == nil { m.Ca = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.Ca).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Ca); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *OSRootSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: OSRootSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: OSRootSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IssuingCa", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.IssuingCa == nil { m.IssuingCa = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.IssuingCa).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.IssuingCa); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CertSaniPs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CertSaniPs = append(m.CertSaniPs, &common.NetIP{}) if unmarshal, ok := interface{}(m.CertSaniPs[len(m.CertSaniPs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.CertSaniPs[len(m.CertSaniPs)-1]); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CertSandnsNames", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.CertSandnsNames = append(m.CertSandnsNames, string(dAtA[iNdEx:postIndex])) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Token", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Token = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AcceptedCAs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AcceptedCAs = append(m.AcceptedCAs, &common.PEMEncodedCertificate{}) if unmarshal, ok := interface{}(m.AcceptedCAs[len(m.AcceptedCAs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AcceptedCAs[len(m.AcceptedCAs)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TrustdCertsSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TrustdCertsSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TrustdCertsSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Server == nil { m.Server = &common.PEMEncodedCertificateAndKey{} } if unmarshal, ok := interface{}(m.Server).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Server); err != nil { return err } } iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field AcceptedCAs", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.AcceptedCAs = append(m.AcceptedCAs, &common.PEMEncodedCertificate{}) if unmarshal, ok := interface{}(m.AcceptedCAs[len(m.AcceptedCAs)-1]).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.AcceptedCAs[len(m.AcceptedCAs)-1]); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/security/security.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/security/security.proto package security import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ImageKeylessVerifierSpec represents a signature verification provider. type ImageKeylessVerifierSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` Subject string `protobuf:"bytes,2,opt,name=subject,proto3" json:"subject,omitempty"` SubjectRegex string `protobuf:"bytes,3,opt,name=subject_regex,json=subjectRegex,proto3" json:"subject_regex,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageKeylessVerifierSpec) Reset() { *x = ImageKeylessVerifierSpec{} mi := &file_resource_definitions_security_security_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageKeylessVerifierSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageKeylessVerifierSpec) ProtoMessage() {} func (x *ImageKeylessVerifierSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_security_security_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageKeylessVerifierSpec.ProtoReflect.Descriptor instead. func (*ImageKeylessVerifierSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_security_security_proto_rawDescGZIP(), []int{0} } func (x *ImageKeylessVerifierSpec) GetIssuer() string { if x != nil { return x.Issuer } return "" } func (x *ImageKeylessVerifierSpec) GetSubject() string { if x != nil { return x.Subject } return "" } func (x *ImageKeylessVerifierSpec) GetSubjectRegex() string { if x != nil { return x.SubjectRegex } return "" } // ImagePublicKeyVerifierSpec represents a signature verification provider with static public key. type ImagePublicKeyVerifierSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Certificate string `protobuf:"bytes,1,opt,name=certificate,proto3" json:"certificate,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImagePublicKeyVerifierSpec) Reset() { *x = ImagePublicKeyVerifierSpec{} mi := &file_resource_definitions_security_security_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImagePublicKeyVerifierSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImagePublicKeyVerifierSpec) ProtoMessage() {} func (x *ImagePublicKeyVerifierSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_security_security_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImagePublicKeyVerifierSpec.ProtoReflect.Descriptor instead. func (*ImagePublicKeyVerifierSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_security_security_proto_rawDescGZIP(), []int{1} } func (x *ImagePublicKeyVerifierSpec) GetCertificate() string { if x != nil { return x.Certificate } return "" } // ImageVerificationRuleSpec represents a verification rule. type ImageVerificationRuleSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ImagePattern string `protobuf:"bytes,2,opt,name=image_pattern,json=imagePattern,proto3" json:"image_pattern,omitempty"` Skip bool `protobuf:"varint,3,opt,name=skip,proto3" json:"skip,omitempty"` Deny bool `protobuf:"varint,4,opt,name=deny,proto3" json:"deny,omitempty"` KeylessVerifier *ImageKeylessVerifierSpec `protobuf:"bytes,5,opt,name=keyless_verifier,json=keylessVerifier,proto3" json:"keyless_verifier,omitempty"` PublicKeyVerifier *ImagePublicKeyVerifierSpec `protobuf:"bytes,6,opt,name=public_key_verifier,json=publicKeyVerifier,proto3" json:"public_key_verifier,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ImageVerificationRuleSpec) Reset() { *x = ImageVerificationRuleSpec{} mi := &file_resource_definitions_security_security_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ImageVerificationRuleSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ImageVerificationRuleSpec) ProtoMessage() {} func (x *ImageVerificationRuleSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_security_security_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ImageVerificationRuleSpec.ProtoReflect.Descriptor instead. func (*ImageVerificationRuleSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_security_security_proto_rawDescGZIP(), []int{2} } func (x *ImageVerificationRuleSpec) GetImagePattern() string { if x != nil { return x.ImagePattern } return "" } func (x *ImageVerificationRuleSpec) GetSkip() bool { if x != nil { return x.Skip } return false } func (x *ImageVerificationRuleSpec) GetDeny() bool { if x != nil { return x.Deny } return false } func (x *ImageVerificationRuleSpec) GetKeylessVerifier() *ImageKeylessVerifierSpec { if x != nil { return x.KeylessVerifier } return nil } func (x *ImageVerificationRuleSpec) GetPublicKeyVerifier() *ImagePublicKeyVerifierSpec { if x != nil { return x.PublicKeyVerifier } return nil } // TUFTrustedRootSpec represents a sigstore's TUF trusted root information. type TUFTrustedRootSpec struct { state protoimpl.MessageState `protogen:"open.v1"` LastRefreshTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=last_refresh_time,json=lastRefreshTime,proto3" json:"last_refresh_time,omitempty"` JsonData string `protobuf:"bytes,2,opt,name=json_data,json=jsonData,proto3" json:"json_data,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TUFTrustedRootSpec) Reset() { *x = TUFTrustedRootSpec{} mi := &file_resource_definitions_security_security_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TUFTrustedRootSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TUFTrustedRootSpec) ProtoMessage() {} func (x *TUFTrustedRootSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_security_security_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TUFTrustedRootSpec.ProtoReflect.Descriptor instead. func (*TUFTrustedRootSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_security_security_proto_rawDescGZIP(), []int{3} } func (x *TUFTrustedRootSpec) GetLastRefreshTime() *timestamppb.Timestamp { if x != nil { return x.LastRefreshTime } return nil } func (x *TUFTrustedRootSpec) GetJsonData() string { if x != nil { return x.JsonData } return "" } var File_resource_definitions_security_security_proto protoreflect.FileDescriptor const file_resource_definitions_security_security_proto_rawDesc = "" + "\n" + ",resource/definitions/security/security.proto\x12#talos.resource.definitions.security\x1a\x1fgoogle/protobuf/timestamp.proto\"q\n" + "\x18ImageKeylessVerifierSpec\x12\x16\n" + "\x06issuer\x18\x01 \x01(\tR\x06issuer\x12\x18\n" + "\asubject\x18\x02 \x01(\tR\asubject\x12#\n" + "\rsubject_regex\x18\x03 \x01(\tR\fsubjectRegex\">\n" + "\x1aImagePublicKeyVerifierSpec\x12 \n" + "\vcertificate\x18\x01 \x01(\tR\vcertificate\"\xc3\x02\n" + "\x19ImageVerificationRuleSpec\x12#\n" + "\rimage_pattern\x18\x02 \x01(\tR\fimagePattern\x12\x12\n" + "\x04skip\x18\x03 \x01(\bR\x04skip\x12\x12\n" + "\x04deny\x18\x04 \x01(\bR\x04deny\x12h\n" + "\x10keyless_verifier\x18\x05 \x01(\v2=.talos.resource.definitions.security.ImageKeylessVerifierSpecR\x0fkeylessVerifier\x12o\n" + "\x13public_key_verifier\x18\x06 \x01(\v2?.talos.resource.definitions.security.ImagePublicKeyVerifierSpecR\x11publicKeyVerifier\"y\n" + "\x12TUFTrustedRootSpec\x12F\n" + "\x11last_refresh_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\x0flastRefreshTime\x12\x1b\n" + "\tjson_data\x18\x02 \x01(\tR\bjsonDataBz\n" + "+dev.talos.api.resource.definitions.securityZKgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/securityb\x06proto3" var ( file_resource_definitions_security_security_proto_rawDescOnce sync.Once file_resource_definitions_security_security_proto_rawDescData []byte ) func file_resource_definitions_security_security_proto_rawDescGZIP() []byte { file_resource_definitions_security_security_proto_rawDescOnce.Do(func() { file_resource_definitions_security_security_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_security_security_proto_rawDesc), len(file_resource_definitions_security_security_proto_rawDesc))) }) return file_resource_definitions_security_security_proto_rawDescData } var file_resource_definitions_security_security_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_resource_definitions_security_security_proto_goTypes = []any{ (*ImageKeylessVerifierSpec)(nil), // 0: talos.resource.definitions.security.ImageKeylessVerifierSpec (*ImagePublicKeyVerifierSpec)(nil), // 1: talos.resource.definitions.security.ImagePublicKeyVerifierSpec (*ImageVerificationRuleSpec)(nil), // 2: talos.resource.definitions.security.ImageVerificationRuleSpec (*TUFTrustedRootSpec)(nil), // 3: talos.resource.definitions.security.TUFTrustedRootSpec (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_resource_definitions_security_security_proto_depIdxs = []int32{ 0, // 0: talos.resource.definitions.security.ImageVerificationRuleSpec.keyless_verifier:type_name -> talos.resource.definitions.security.ImageKeylessVerifierSpec 1, // 1: talos.resource.definitions.security.ImageVerificationRuleSpec.public_key_verifier:type_name -> talos.resource.definitions.security.ImagePublicKeyVerifierSpec 4, // 2: talos.resource.definitions.security.TUFTrustedRootSpec.last_refresh_time:type_name -> google.protobuf.Timestamp 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_resource_definitions_security_security_proto_init() } func file_resource_definitions_security_security_proto_init() { if File_resource_definitions_security_security_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_security_security_proto_rawDesc), len(file_resource_definitions_security_security_proto_rawDesc)), NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_security_security_proto_goTypes, DependencyIndexes: file_resource_definitions_security_security_proto_depIdxs, MessageInfos: file_resource_definitions_security_security_proto_msgTypes, }.Build() File_resource_definitions_security_security_proto = out.File file_resource_definitions_security_security_proto_goTypes = nil file_resource_definitions_security_security_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/security/security_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/security/security.proto package security import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" timestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb1 "google.golang.org/protobuf/types/known/timestamppb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ImageKeylessVerifierSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageKeylessVerifierSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageKeylessVerifierSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.SubjectRegex) > 0 { i -= len(m.SubjectRegex) copy(dAtA[i:], m.SubjectRegex) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.SubjectRegex))) i-- dAtA[i] = 0x1a } if len(m.Subject) > 0 { i -= len(m.Subject) copy(dAtA[i:], m.Subject) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Subject))) i-- dAtA[i] = 0x12 } if len(m.Issuer) > 0 { i -= len(m.Issuer) copy(dAtA[i:], m.Issuer) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Issuer))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImagePublicKeyVerifierSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImagePublicKeyVerifierSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImagePublicKeyVerifierSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Certificate) > 0 { i -= len(m.Certificate) copy(dAtA[i:], m.Certificate) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Certificate))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageVerificationRuleSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ImageVerificationRuleSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ImageVerificationRuleSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.PublicKeyVerifier != nil { size, err := m.PublicKeyVerifier.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x32 } if m.KeylessVerifier != nil { size, err := m.KeylessVerifier.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x2a } if m.Deny { i-- if m.Deny { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.Skip { i-- if m.Skip { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if len(m.ImagePattern) > 0 { i -= len(m.ImagePattern) copy(dAtA[i:], m.ImagePattern) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ImagePattern))) i-- dAtA[i] = 0x12 } return len(dAtA) - i, nil } func (m *TUFTrustedRootSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TUFTrustedRootSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TUFTrustedRootSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.JsonData) > 0 { i -= len(m.JsonData) copy(dAtA[i:], m.JsonData) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.JsonData))) i-- dAtA[i] = 0x12 } if m.LastRefreshTime != nil { size, err := (*timestamppb.Timestamp)(m.LastRefreshTime).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ImageKeylessVerifierSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Issuer) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Subject) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.SubjectRegex) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImagePublicKeyVerifierSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Certificate) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageVerificationRuleSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ImagePattern) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Skip { n += 2 } if m.Deny { n += 2 } if m.KeylessVerifier != nil { l = m.KeylessVerifier.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.PublicKeyVerifier != nil { l = m.PublicKeyVerifier.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *TUFTrustedRootSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.LastRefreshTime != nil { l = (*timestamppb.Timestamp)(m.LastRefreshTime).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.JsonData) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ImageKeylessVerifierSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageKeylessVerifierSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageKeylessVerifierSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Issuer", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Issuer = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Subject", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Subject = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SubjectRegex", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.SubjectRegex = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImagePublicKeyVerifierSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImagePublicKeyVerifierSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImagePublicKeyVerifierSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Certificate", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Certificate = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *ImageVerificationRuleSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ImageVerificationRuleSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ImageVerificationRuleSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ImagePattern", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ImagePattern = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Skip", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Skip = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Deny", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Deny = bool(v != 0) case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field KeylessVerifier", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.KeylessVerifier == nil { m.KeylessVerifier = &ImageKeylessVerifierSpec{} } if err := m.KeylessVerifier.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field PublicKeyVerifier", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.PublicKeyVerifier == nil { m.PublicKeyVerifier = &ImagePublicKeyVerifierSpec{} } if err := m.PublicKeyVerifier.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TUFTrustedRootSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TUFTrustedRootSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TUFTrustedRootSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastRefreshTime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.LastRefreshTime == nil { m.LastRefreshTime = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.LastRefreshTime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field JsonData", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.JsonData = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/siderolink/siderolink.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/siderolink/siderolink.proto package siderolink import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ConfigSpec describes Siderolink configuration. type ConfigSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ApiEndpoint string `protobuf:"bytes,1,opt,name=api_endpoint,json=apiEndpoint,proto3" json:"api_endpoint,omitempty"` Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` JoinToken string `protobuf:"bytes,3,opt,name=join_token,json=joinToken,proto3" json:"join_token,omitempty"` Insecure bool `protobuf:"varint,4,opt,name=insecure,proto3" json:"insecure,omitempty"` Tunnel bool `protobuf:"varint,5,opt,name=tunnel,proto3" json:"tunnel,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ConfigSpec) Reset() { *x = ConfigSpec{} mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ConfigSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ConfigSpec) ProtoMessage() {} func (x *ConfigSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ConfigSpec.ProtoReflect.Descriptor instead. func (*ConfigSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_siderolink_siderolink_proto_rawDescGZIP(), []int{0} } func (x *ConfigSpec) GetApiEndpoint() string { if x != nil { return x.ApiEndpoint } return "" } func (x *ConfigSpec) GetHost() string { if x != nil { return x.Host } return "" } func (x *ConfigSpec) GetJoinToken() string { if x != nil { return x.JoinToken } return "" } func (x *ConfigSpec) GetInsecure() bool { if x != nil { return x.Insecure } return false } func (x *ConfigSpec) GetTunnel() bool { if x != nil { return x.Tunnel } return false } // StatusSpec describes Siderolink status. type StatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` Connected bool `protobuf:"varint,2,opt,name=connected,proto3" json:"connected,omitempty"` LinkName string `protobuf:"bytes,3,opt,name=link_name,json=linkName,proto3" json:"link_name,omitempty"` GrpcTunnel bool `protobuf:"varint,4,opt,name=grpc_tunnel,json=grpcTunnel,proto3" json:"grpc_tunnel,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StatusSpec) Reset() { *x = StatusSpec{} mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StatusSpec) ProtoMessage() {} func (x *StatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StatusSpec.ProtoReflect.Descriptor instead. func (*StatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_siderolink_siderolink_proto_rawDescGZIP(), []int{1} } func (x *StatusSpec) GetHost() string { if x != nil { return x.Host } return "" } func (x *StatusSpec) GetConnected() bool { if x != nil { return x.Connected } return false } func (x *StatusSpec) GetLinkName() string { if x != nil { return x.LinkName } return "" } func (x *StatusSpec) GetGrpcTunnel() bool { if x != nil { return x.GrpcTunnel } return false } // TunnelSpec describes Siderolink GRPC Tunnel configuration. type TunnelSpec struct { state protoimpl.MessageState `protogen:"open.v1"` ApiEndpoint string `protobuf:"bytes,1,opt,name=api_endpoint,json=apiEndpoint,proto3" json:"api_endpoint,omitempty"` LinkName string `protobuf:"bytes,2,opt,name=link_name,json=linkName,proto3" json:"link_name,omitempty"` Mtu int64 `protobuf:"varint,3,opt,name=mtu,proto3" json:"mtu,omitempty"` NodeAddress *common.NetIPPort `protobuf:"bytes,4,opt,name=node_address,json=nodeAddress,proto3" json:"node_address,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TunnelSpec) Reset() { *x = TunnelSpec{} mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TunnelSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*TunnelSpec) ProtoMessage() {} func (x *TunnelSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_siderolink_siderolink_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TunnelSpec.ProtoReflect.Descriptor instead. func (*TunnelSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_siderolink_siderolink_proto_rawDescGZIP(), []int{2} } func (x *TunnelSpec) GetApiEndpoint() string { if x != nil { return x.ApiEndpoint } return "" } func (x *TunnelSpec) GetLinkName() string { if x != nil { return x.LinkName } return "" } func (x *TunnelSpec) GetMtu() int64 { if x != nil { return x.Mtu } return 0 } func (x *TunnelSpec) GetNodeAddress() *common.NetIPPort { if x != nil { return x.NodeAddress } return nil } var File_resource_definitions_siderolink_siderolink_proto protoreflect.FileDescriptor const file_resource_definitions_siderolink_siderolink_proto_rawDesc = "" + "\n" + "0resource/definitions/siderolink/siderolink.proto\x12%talos.resource.definitions.siderolink\x1a\x13common/common.proto\"\x96\x01\n" + "\n" + "ConfigSpec\x12!\n" + "\fapi_endpoint\x18\x01 \x01(\tR\vapiEndpoint\x12\x12\n" + "\x04host\x18\x02 \x01(\tR\x04host\x12\x1d\n" + "\n" + "join_token\x18\x03 \x01(\tR\tjoinToken\x12\x1a\n" + "\binsecure\x18\x04 \x01(\bR\binsecure\x12\x16\n" + "\x06tunnel\x18\x05 \x01(\bR\x06tunnel\"|\n" + "\n" + "StatusSpec\x12\x12\n" + "\x04host\x18\x01 \x01(\tR\x04host\x12\x1c\n" + "\tconnected\x18\x02 \x01(\bR\tconnected\x12\x1b\n" + "\tlink_name\x18\x03 \x01(\tR\blinkName\x12\x1f\n" + "\vgrpc_tunnel\x18\x04 \x01(\bR\n" + "grpcTunnel\"\x94\x01\n" + "\n" + "TunnelSpec\x12!\n" + "\fapi_endpoint\x18\x01 \x01(\tR\vapiEndpoint\x12\x1b\n" + "\tlink_name\x18\x02 \x01(\tR\blinkName\x12\x10\n" + "\x03mtu\x18\x03 \x01(\x03R\x03mtu\x124\n" + "\fnode_address\x18\x04 \x01(\v2\x11.common.NetIPPortR\vnodeAddressB~\n" + "-dev.talos.api.resource.definitions.siderolinkZMgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/siderolinkb\x06proto3" var ( file_resource_definitions_siderolink_siderolink_proto_rawDescOnce sync.Once file_resource_definitions_siderolink_siderolink_proto_rawDescData []byte ) func file_resource_definitions_siderolink_siderolink_proto_rawDescGZIP() []byte { file_resource_definitions_siderolink_siderolink_proto_rawDescOnce.Do(func() { file_resource_definitions_siderolink_siderolink_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_siderolink_siderolink_proto_rawDesc), len(file_resource_definitions_siderolink_siderolink_proto_rawDesc))) }) return file_resource_definitions_siderolink_siderolink_proto_rawDescData } var file_resource_definitions_siderolink_siderolink_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_resource_definitions_siderolink_siderolink_proto_goTypes = []any{ (*ConfigSpec)(nil), // 0: talos.resource.definitions.siderolink.ConfigSpec (*StatusSpec)(nil), // 1: talos.resource.definitions.siderolink.StatusSpec (*TunnelSpec)(nil), // 2: talos.resource.definitions.siderolink.TunnelSpec (*common.NetIPPort)(nil), // 3: common.NetIPPort } var file_resource_definitions_siderolink_siderolink_proto_depIdxs = []int32{ 3, // 0: talos.resource.definitions.siderolink.TunnelSpec.node_address:type_name -> common.NetIPPort 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name } func init() { file_resource_definitions_siderolink_siderolink_proto_init() } func file_resource_definitions_siderolink_siderolink_proto_init() { if File_resource_definitions_siderolink_siderolink_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_siderolink_siderolink_proto_rawDesc), len(file_resource_definitions_siderolink_siderolink_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_siderolink_siderolink_proto_goTypes, DependencyIndexes: file_resource_definitions_siderolink_siderolink_proto_depIdxs, MessageInfos: file_resource_definitions_siderolink_siderolink_proto_msgTypes, }.Build() File_resource_definitions_siderolink_siderolink_proto = out.File file_resource_definitions_siderolink_siderolink_proto_goTypes = nil file_resource_definitions_siderolink_siderolink_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/siderolink/siderolink_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/siderolink/siderolink.proto package siderolink import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ConfigSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ConfigSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ConfigSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Tunnel { i-- if m.Tunnel { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.Insecure { i-- if m.Insecure { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.JoinToken) > 0 { i -= len(m.JoinToken) copy(dAtA[i:], m.JoinToken) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.JoinToken))) i-- dAtA[i] = 0x1a } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0x12 } if len(m.ApiEndpoint) > 0 { i -= len(m.ApiEndpoint) copy(dAtA[i:], m.ApiEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ApiEndpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.GrpcTunnel { i-- if m.GrpcTunnel { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if len(m.LinkName) > 0 { i -= len(m.LinkName) copy(dAtA[i:], m.LinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LinkName))) i-- dAtA[i] = 0x1a } if m.Connected { i-- if m.Connected { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if len(m.Host) > 0 { i -= len(m.Host) copy(dAtA[i:], m.Host) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Host))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TunnelSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TunnelSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TunnelSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.NodeAddress != nil { if vtmsg, ok := interface{}(m.NodeAddress).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.NodeAddress) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0x22 } if m.Mtu != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Mtu)) i-- dAtA[i] = 0x18 } if len(m.LinkName) > 0 { i -= len(m.LinkName) copy(dAtA[i:], m.LinkName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.LinkName))) i-- dAtA[i] = 0x12 } if len(m.ApiEndpoint) > 0 { i -= len(m.ApiEndpoint) copy(dAtA[i:], m.ApiEndpoint) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ApiEndpoint))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *ConfigSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ApiEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.JoinToken) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Insecure { n += 2 } if m.Tunnel { n += 2 } n += len(m.unknownFields) return n } func (m *StatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Host) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Connected { n += 2 } l = len(m.LinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.GrpcTunnel { n += 2 } n += len(m.unknownFields) return n } func (m *TunnelSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.ApiEndpoint) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.LinkName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Mtu != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Mtu)) } if m.NodeAddress != nil { if size, ok := interface{}(m.NodeAddress).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.NodeAddress) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *ConfigSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ConfigSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ConfigSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApiEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field JoinToken", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.JoinToken = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Insecure", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Insecure = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Tunnel", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Tunnel = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Host", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Host = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Connected", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Connected = bool(v != 0) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field GrpcTunnel", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.GrpcTunnel = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TunnelSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TunnelSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TunnelSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field ApiEndpoint", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.ApiEndpoint = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LinkName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.LinkName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Mtu", wireType) } m.Mtu = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Mtu |= int64(b&0x7F) << shift if b < 0x80 { break } } case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field NodeAddress", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.NodeAddress == nil { m.NodeAddress = &common.NetIPPort{} } if unmarshal, ok := interface{}(m.NodeAddress).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.NodeAddress); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/time/time.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/time/time.proto package time import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb "google.golang.org/protobuf/types/known/durationpb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // AdjtimeStatusSpec describes Linux internal adjtime state. type AdjtimeStatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Offset *durationpb.Duration `protobuf:"bytes,1,opt,name=offset,proto3" json:"offset,omitempty"` FrequencyAdjustmentRatio float64 `protobuf:"fixed64,2,opt,name=frequency_adjustment_ratio,json=frequencyAdjustmentRatio,proto3" json:"frequency_adjustment_ratio,omitempty"` MaxError *durationpb.Duration `protobuf:"bytes,3,opt,name=max_error,json=maxError,proto3" json:"max_error,omitempty"` EstError *durationpb.Duration `protobuf:"bytes,4,opt,name=est_error,json=estError,proto3" json:"est_error,omitempty"` Status string `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` Constant int64 `protobuf:"varint,6,opt,name=constant,proto3" json:"constant,omitempty"` SyncStatus bool `protobuf:"varint,7,opt,name=sync_status,json=syncStatus,proto3" json:"sync_status,omitempty"` State string `protobuf:"bytes,8,opt,name=state,proto3" json:"state,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *AdjtimeStatusSpec) Reset() { *x = AdjtimeStatusSpec{} mi := &file_resource_definitions_time_time_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *AdjtimeStatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*AdjtimeStatusSpec) ProtoMessage() {} func (x *AdjtimeStatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_time_time_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use AdjtimeStatusSpec.ProtoReflect.Descriptor instead. func (*AdjtimeStatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_time_time_proto_rawDescGZIP(), []int{0} } func (x *AdjtimeStatusSpec) GetOffset() *durationpb.Duration { if x != nil { return x.Offset } return nil } func (x *AdjtimeStatusSpec) GetFrequencyAdjustmentRatio() float64 { if x != nil { return x.FrequencyAdjustmentRatio } return 0 } func (x *AdjtimeStatusSpec) GetMaxError() *durationpb.Duration { if x != nil { return x.MaxError } return nil } func (x *AdjtimeStatusSpec) GetEstError() *durationpb.Duration { if x != nil { return x.EstError } return nil } func (x *AdjtimeStatusSpec) GetStatus() string { if x != nil { return x.Status } return "" } func (x *AdjtimeStatusSpec) GetConstant() int64 { if x != nil { return x.Constant } return 0 } func (x *AdjtimeStatusSpec) GetSyncStatus() bool { if x != nil { return x.SyncStatus } return false } func (x *AdjtimeStatusSpec) GetState() string { if x != nil { return x.State } return "" } // StatusSpec describes time sync state. type StatusSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Synced bool `protobuf:"varint,1,opt,name=synced,proto3" json:"synced,omitempty"` Epoch int64 `protobuf:"varint,2,opt,name=epoch,proto3" json:"epoch,omitempty"` SyncDisabled bool `protobuf:"varint,3,opt,name=sync_disabled,json=syncDisabled,proto3" json:"sync_disabled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *StatusSpec) Reset() { *x = StatusSpec{} mi := &file_resource_definitions_time_time_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *StatusSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*StatusSpec) ProtoMessage() {} func (x *StatusSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_time_time_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use StatusSpec.ProtoReflect.Descriptor instead. func (*StatusSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_time_time_proto_rawDescGZIP(), []int{1} } func (x *StatusSpec) GetSynced() bool { if x != nil { return x.Synced } return false } func (x *StatusSpec) GetEpoch() int64 { if x != nil { return x.Epoch } return 0 } func (x *StatusSpec) GetSyncDisabled() bool { if x != nil { return x.SyncDisabled } return false } var File_resource_definitions_time_time_proto protoreflect.FileDescriptor const file_resource_definitions_time_time_proto_rawDesc = "" + "\n" + "$resource/definitions/time/time.proto\x12\x1ftalos.resource.definitions.time\x1a\x1egoogle/protobuf/duration.proto\"\xdf\x02\n" + "\x11AdjtimeStatusSpec\x121\n" + "\x06offset\x18\x01 \x01(\v2\x19.google.protobuf.DurationR\x06offset\x12<\n" + "\x1afrequency_adjustment_ratio\x18\x02 \x01(\x01R\x18frequencyAdjustmentRatio\x126\n" + "\tmax_error\x18\x03 \x01(\v2\x19.google.protobuf.DurationR\bmaxError\x126\n" + "\test_error\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\bestError\x12\x16\n" + "\x06status\x18\x05 \x01(\tR\x06status\x12\x1a\n" + "\bconstant\x18\x06 \x01(\x03R\bconstant\x12\x1f\n" + "\vsync_status\x18\a \x01(\bR\n" + "syncStatus\x12\x14\n" + "\x05state\x18\b \x01(\tR\x05state\"_\n" + "\n" + "StatusSpec\x12\x16\n" + "\x06synced\x18\x01 \x01(\bR\x06synced\x12\x14\n" + "\x05epoch\x18\x02 \x01(\x03R\x05epoch\x12#\n" + "\rsync_disabled\x18\x03 \x01(\bR\fsyncDisabledBr\n" + "'dev.talos.api.resource.definitions.timeZGgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/timeb\x06proto3" var ( file_resource_definitions_time_time_proto_rawDescOnce sync.Once file_resource_definitions_time_time_proto_rawDescData []byte ) func file_resource_definitions_time_time_proto_rawDescGZIP() []byte { file_resource_definitions_time_time_proto_rawDescOnce.Do(func() { file_resource_definitions_time_time_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_time_time_proto_rawDesc), len(file_resource_definitions_time_time_proto_rawDesc))) }) return file_resource_definitions_time_time_proto_rawDescData } var file_resource_definitions_time_time_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_resource_definitions_time_time_proto_goTypes = []any{ (*AdjtimeStatusSpec)(nil), // 0: talos.resource.definitions.time.AdjtimeStatusSpec (*StatusSpec)(nil), // 1: talos.resource.definitions.time.StatusSpec (*durationpb.Duration)(nil), // 2: google.protobuf.Duration } var file_resource_definitions_time_time_proto_depIdxs = []int32{ 2, // 0: talos.resource.definitions.time.AdjtimeStatusSpec.offset:type_name -> google.protobuf.Duration 2, // 1: talos.resource.definitions.time.AdjtimeStatusSpec.max_error:type_name -> google.protobuf.Duration 2, // 2: talos.resource.definitions.time.AdjtimeStatusSpec.est_error:type_name -> google.protobuf.Duration 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name 3, // [3:3] is the sub-list for extension extendee 0, // [0:3] is the sub-list for field type_name } func init() { file_resource_definitions_time_time_proto_init() } func file_resource_definitions_time_time_proto_init() { if File_resource_definitions_time_time_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_time_time_proto_rawDesc), len(file_resource_definitions_time_time_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_time_time_proto_goTypes, DependencyIndexes: file_resource_definitions_time_time_proto_depIdxs, MessageInfos: file_resource_definitions_time_time_proto_msgTypes, }.Build() File_resource_definitions_time_time_proto = out.File file_resource_definitions_time_time_proto_goTypes = nil file_resource_definitions_time_time_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/time/time_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/time/time.proto package time import ( binary "encoding/binary" fmt "fmt" io "io" math "math" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb" protoimpl "google.golang.org/protobuf/runtime/protoimpl" durationpb1 "google.golang.org/protobuf/types/known/durationpb" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *AdjtimeStatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *AdjtimeStatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *AdjtimeStatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.State) > 0 { i -= len(m.State) copy(dAtA[i:], m.State) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.State))) i-- dAtA[i] = 0x42 } if m.SyncStatus { i-- if m.SyncStatus { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x38 } if m.Constant != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Constant)) i-- dAtA[i] = 0x30 } if len(m.Status) > 0 { i -= len(m.Status) copy(dAtA[i:], m.Status) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Status))) i-- dAtA[i] = 0x2a } if m.EstError != nil { size, err := (*durationpb.Duration)(m.EstError).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.MaxError != nil { size, err := (*durationpb.Duration)(m.MaxError).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if m.FrequencyAdjustmentRatio != 0 { i -= 8 binary.LittleEndian.PutUint64(dAtA[i:], uint64(math.Float64bits(float64(m.FrequencyAdjustmentRatio)))) i-- dAtA[i] = 0x11 } if m.Offset != nil { size, err := (*durationpb.Duration)(m.Offset).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *StatusSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *StatusSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *StatusSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SyncDisabled { i-- if m.SyncDisabled { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Epoch != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Epoch)) i-- dAtA[i] = 0x10 } if m.Synced { i-- if m.Synced { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *AdjtimeStatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Offset != nil { l = (*durationpb.Duration)(m.Offset).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.FrequencyAdjustmentRatio != 0 { n += 9 } if m.MaxError != nil { l = (*durationpb.Duration)(m.MaxError).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.EstError != nil { l = (*durationpb.Duration)(m.EstError).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Status) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Constant != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Constant)) } if m.SyncStatus { n += 2 } l = len(m.State) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *StatusSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Synced { n += 2 } if m.Epoch != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Epoch)) } if m.SyncDisabled { n += 2 } n += len(m.unknownFields) return n } func (m *AdjtimeStatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: AdjtimeStatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: AdjtimeStatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Offset", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Offset == nil { m.Offset = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.Offset).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 2: if wireType != 1 { return fmt.Errorf("proto: wrong wireType = %d for field FrequencyAdjustmentRatio", wireType) } var v uint64 if (iNdEx + 8) > l { return io.ErrUnexpectedEOF } v = uint64(binary.LittleEndian.Uint64(dAtA[iNdEx:])) iNdEx += 8 m.FrequencyAdjustmentRatio = float64(math.Float64frombits(v)) case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field MaxError", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.MaxError == nil { m.MaxError = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.MaxError).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field EstError", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.EstError == nil { m.EstError = &durationpb1.Duration{} } if err := (*durationpb.Duration)(m.EstError).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Status = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Constant", wireType) } m.Constant = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Constant |= int64(b&0x7F) << shift if b < 0x80 { break } } case 7: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SyncStatus", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SyncStatus = bool(v != 0) case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.State = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *StatusSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: StatusSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: StatusSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Synced", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Synced = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) } m.Epoch = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Epoch |= int64(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SyncDisabled", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SyncDisabled = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/definitions/v1alpha1/v1alpha1.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/definitions/v1alpha1/v1alpha1.proto package v1alpha1 import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // ServiceSpec describe service state. type ServiceSpec struct { state protoimpl.MessageState `protogen:"open.v1"` Running bool `protobuf:"varint,1,opt,name=running,proto3" json:"running,omitempty"` Healthy bool `protobuf:"varint,2,opt,name=healthy,proto3" json:"healthy,omitempty"` Unknown bool `protobuf:"varint,3,opt,name=unknown,proto3" json:"unknown,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *ServiceSpec) Reset() { *x = ServiceSpec{} mi := &file_resource_definitions_v1alpha1_v1alpha1_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *ServiceSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*ServiceSpec) ProtoMessage() {} func (x *ServiceSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_definitions_v1alpha1_v1alpha1_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use ServiceSpec.ProtoReflect.Descriptor instead. func (*ServiceSpec) Descriptor() ([]byte, []int) { return file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescGZIP(), []int{0} } func (x *ServiceSpec) GetRunning() bool { if x != nil { return x.Running } return false } func (x *ServiceSpec) GetHealthy() bool { if x != nil { return x.Healthy } return false } func (x *ServiceSpec) GetUnknown() bool { if x != nil { return x.Unknown } return false } var File_resource_definitions_v1alpha1_v1alpha1_proto protoreflect.FileDescriptor const file_resource_definitions_v1alpha1_v1alpha1_proto_rawDesc = "" + "\n" + ",resource/definitions/v1alpha1/v1alpha1.proto\x12#talos.resource.definitions.v1alpha1\"[\n" + "\vServiceSpec\x12\x18\n" + "\arunning\x18\x01 \x01(\bR\arunning\x12\x18\n" + "\ahealthy\x18\x02 \x01(\bR\ahealthy\x12\x18\n" + "\aunknown\x18\x03 \x01(\bR\aunknownBz\n" + "+dev.talos.api.resource.definitions.v1alpha1ZKgithub.com/siderolabs/talos/pkg/machinery/api/resource/definitions/v1alpha1b\x06proto3" var ( file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescOnce sync.Once file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescData []byte ) func file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescGZIP() []byte { file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescOnce.Do(func() { file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_definitions_v1alpha1_v1alpha1_proto_rawDesc), len(file_resource_definitions_v1alpha1_v1alpha1_proto_rawDesc))) }) return file_resource_definitions_v1alpha1_v1alpha1_proto_rawDescData } var file_resource_definitions_v1alpha1_v1alpha1_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_resource_definitions_v1alpha1_v1alpha1_proto_goTypes = []any{ (*ServiceSpec)(nil), // 0: talos.resource.definitions.v1alpha1.ServiceSpec } var file_resource_definitions_v1alpha1_v1alpha1_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_definitions_v1alpha1_v1alpha1_proto_init() } func file_resource_definitions_v1alpha1_v1alpha1_proto_init() { if File_resource_definitions_v1alpha1_v1alpha1_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_definitions_v1alpha1_v1alpha1_proto_rawDesc), len(file_resource_definitions_v1alpha1_v1alpha1_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_definitions_v1alpha1_v1alpha1_proto_goTypes, DependencyIndexes: file_resource_definitions_v1alpha1_v1alpha1_proto_depIdxs, MessageInfos: file_resource_definitions_v1alpha1_v1alpha1_proto_msgTypes, }.Build() File_resource_definitions_v1alpha1_v1alpha1_proto = out.File file_resource_definitions_v1alpha1_v1alpha1_proto_goTypes = nil file_resource_definitions_v1alpha1_v1alpha1_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/definitions/v1alpha1/v1alpha1_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/definitions/v1alpha1/v1alpha1.proto package v1alpha1 import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *ServiceSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *ServiceSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *ServiceSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Unknown { i-- if m.Unknown { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Healthy { i-- if m.Healthy { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x10 } if m.Running { i-- if m.Running { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *ServiceSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Running { n += 2 } if m.Healthy { n += 2 } if m.Unknown { n += 2 } n += len(m.unknownFields) return n } func (m *ServiceSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: ServiceSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: ServiceSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Running", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Running = bool(v != 0) case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Healthy", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Healthy = bool(v != 0) case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Unknown", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Unknown = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/resource/network/device_config.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: resource/network/device_config.proto package network import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // DeviceConfigSpecSpec is the spec for the network.DeviceConfigSpec resource. type DeviceConfigSpecSpec struct { state protoimpl.MessageState `protogen:"open.v1"` // Contains YAML marshalled device config (as part of the machine config). YamlMarshalled []byte `protobuf:"bytes,1,opt,name=yaml_marshalled,json=yamlMarshalled,proto3" json:"yaml_marshalled,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DeviceConfigSpecSpec) Reset() { *x = DeviceConfigSpecSpec{} mi := &file_resource_network_device_config_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DeviceConfigSpecSpec) String() string { return protoimpl.X.MessageStringOf(x) } func (*DeviceConfigSpecSpec) ProtoMessage() {} func (x *DeviceConfigSpecSpec) ProtoReflect() protoreflect.Message { mi := &file_resource_network_device_config_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DeviceConfigSpecSpec.ProtoReflect.Descriptor instead. func (*DeviceConfigSpecSpec) Descriptor() ([]byte, []int) { return file_resource_network_device_config_proto_rawDescGZIP(), []int{0} } func (x *DeviceConfigSpecSpec) GetYamlMarshalled() []byte { if x != nil { return x.YamlMarshalled } return nil } var File_resource_network_device_config_proto protoreflect.FileDescriptor const file_resource_network_device_config_proto_rawDesc = "" + "\n" + "$resource/network/device_config.proto\x12\x10resource.network\"?\n" + "\x14DeviceConfigSpecSpec\x12'\n" + "\x0fyaml_marshalled\x18\x01 \x01(\fR\x0eyamlMarshalledB`\n" + "\x1edev.talos.api.resource.networkZ>github.com/siderolabs/talos/pkg/machinery/api/resource/networkb\x06proto3" var ( file_resource_network_device_config_proto_rawDescOnce sync.Once file_resource_network_device_config_proto_rawDescData []byte ) func file_resource_network_device_config_proto_rawDescGZIP() []byte { file_resource_network_device_config_proto_rawDescOnce.Do(func() { file_resource_network_device_config_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_resource_network_device_config_proto_rawDesc), len(file_resource_network_device_config_proto_rawDesc))) }) return file_resource_network_device_config_proto_rawDescData } var file_resource_network_device_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_resource_network_device_config_proto_goTypes = []any{ (*DeviceConfigSpecSpec)(nil), // 0: resource.network.DeviceConfigSpecSpec } var file_resource_network_device_config_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for method output_type 0, // [0:0] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_resource_network_device_config_proto_init() } func file_resource_network_device_config_proto_init() { if File_resource_network_device_config_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_resource_network_device_config_proto_rawDesc), len(file_resource_network_device_config_proto_rawDesc)), NumEnums: 0, NumMessages: 1, NumExtensions: 0, NumServices: 0, }, GoTypes: file_resource_network_device_config_proto_goTypes, DependencyIndexes: file_resource_network_device_config_proto_depIdxs, MessageInfos: file_resource_network_device_config_proto_msgTypes, }.Build() File_resource_network_device_config_proto = out.File file_resource_network_device_config_proto_goTypes = nil file_resource_network_device_config_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/resource/network/device_config_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: resource/network/device_config.proto package network import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *DeviceConfigSpecSpec) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DeviceConfigSpecSpec) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DeviceConfigSpecSpec) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.YamlMarshalled) > 0 { i -= len(m.YamlMarshalled) copy(dAtA[i:], m.YamlMarshalled) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.YamlMarshalled))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DeviceConfigSpecSpec) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.YamlMarshalled) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *DeviceConfigSpecSpec) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DeviceConfigSpecSpec: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DeviceConfigSpecSpec: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field YamlMarshalled", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.YamlMarshalled = append(m.YamlMarshalled[:0], dAtA[iNdEx:postIndex]...) if m.YamlMarshalled == nil { m.YamlMarshalled = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/security/security.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: security/security.proto package security import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The request message containing the certificate signing request. type CertificateRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // Certificate Signing Request in PEM format. Csr []byte `protobuf:"bytes,1,opt,name=csr,proto3" json:"csr,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CertificateRequest) Reset() { *x = CertificateRequest{} mi := &file_security_security_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CertificateRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*CertificateRequest) ProtoMessage() {} func (x *CertificateRequest) ProtoReflect() protoreflect.Message { mi := &file_security_security_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CertificateRequest.ProtoReflect.Descriptor instead. func (*CertificateRequest) Descriptor() ([]byte, []int) { return file_security_security_proto_rawDescGZIP(), []int{0} } func (x *CertificateRequest) GetCsr() []byte { if x != nil { return x.Csr } return nil } // The response message containing signed certificate. type CertificateResponse struct { state protoimpl.MessageState `protogen:"open.v1"` // Certificate of the CA that signed the requested certificate in PEM format. Ca []byte `protobuf:"bytes,1,opt,name=ca,proto3" json:"ca,omitempty"` // Signed X.509 requested certificate in PEM format. Crt []byte `protobuf:"bytes,2,opt,name=crt,proto3" json:"crt,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *CertificateResponse) Reset() { *x = CertificateResponse{} mi := &file_security_security_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *CertificateResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*CertificateResponse) ProtoMessage() {} func (x *CertificateResponse) ProtoReflect() protoreflect.Message { mi := &file_security_security_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use CertificateResponse.ProtoReflect.Descriptor instead. func (*CertificateResponse) Descriptor() ([]byte, []int) { return file_security_security_proto_rawDescGZIP(), []int{1} } func (x *CertificateResponse) GetCa() []byte { if x != nil { return x.Ca } return nil } func (x *CertificateResponse) GetCrt() []byte { if x != nil { return x.Crt } return nil } var File_security_security_proto protoreflect.FileDescriptor const file_security_security_proto_rawDesc = "" + "\n" + "\x17security/security.proto\x12\vsecurityapi\"&\n" + "\x12CertificateRequest\x12\x10\n" + "\x03csr\x18\x01 \x01(\fR\x03csr\"7\n" + "\x13CertificateResponse\x12\x0e\n" + "\x02ca\x18\x01 \x01(\fR\x02ca\x12\x10\n" + "\x03crt\x18\x02 \x01(\fR\x03crt2c\n" + "\x0fSecurityService\x12P\n" + "\vCertificate\x12\x1f.securityapi.CertificateRequest\x1a .securityapi.CertificateResponseBP\n" + "\x16dev.talos.api.securityZ6github.com/siderolabs/talos/pkg/machinery/api/securityb\x06proto3" var ( file_security_security_proto_rawDescOnce sync.Once file_security_security_proto_rawDescData []byte ) func file_security_security_proto_rawDescGZIP() []byte { file_security_security_proto_rawDescOnce.Do(func() { file_security_security_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_security_security_proto_rawDesc), len(file_security_security_proto_rawDesc))) }) return file_security_security_proto_rawDescData } var file_security_security_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_security_security_proto_goTypes = []any{ (*CertificateRequest)(nil), // 0: securityapi.CertificateRequest (*CertificateResponse)(nil), // 1: securityapi.CertificateResponse } var file_security_security_proto_depIdxs = []int32{ 0, // 0: securityapi.SecurityService.Certificate:input_type -> securityapi.CertificateRequest 1, // 1: securityapi.SecurityService.Certificate:output_type -> securityapi.CertificateResponse 1, // [1:2] is the sub-list for method output_type 0, // [0:1] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name } func init() { file_security_security_proto_init() } func file_security_security_proto_init() { if File_security_security_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_security_security_proto_rawDesc), len(file_security_security_proto_rawDesc)), NumEnums: 0, NumMessages: 2, NumExtensions: 0, NumServices: 1, }, GoTypes: file_security_security_proto_goTypes, DependencyIndexes: file_security_security_proto_depIdxs, MessageInfos: file_security_security_proto_msgTypes, }.Build() File_security_security_proto = out.File file_security_security_proto_goTypes = nil file_security_security_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/security/security_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: security/security.proto package security import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( SecurityService_Certificate_FullMethodName = "/securityapi.SecurityService/Certificate" ) // SecurityServiceClient is the client API for SecurityService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The security service definition. type SecurityServiceClient interface { Certificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) } type securityServiceClient struct { cc grpc.ClientConnInterface } func NewSecurityServiceClient(cc grpc.ClientConnInterface) SecurityServiceClient { return &securityServiceClient{cc} } func (c *securityServiceClient) Certificate(ctx context.Context, in *CertificateRequest, opts ...grpc.CallOption) (*CertificateResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(CertificateResponse) err := c.cc.Invoke(ctx, SecurityService_Certificate_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // SecurityServiceServer is the server API for SecurityService service. // All implementations must embed UnimplementedSecurityServiceServer // for forward compatibility. // // The security service definition. type SecurityServiceServer interface { Certificate(context.Context, *CertificateRequest) (*CertificateResponse, error) mustEmbedUnimplementedSecurityServiceServer() } // UnimplementedSecurityServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedSecurityServiceServer struct{} func (UnimplementedSecurityServiceServer) Certificate(context.Context, *CertificateRequest) (*CertificateResponse, error) { return nil, status.Error(codes.Unimplemented, "method Certificate not implemented") } func (UnimplementedSecurityServiceServer) mustEmbedUnimplementedSecurityServiceServer() {} func (UnimplementedSecurityServiceServer) testEmbeddedByValue() {} // UnsafeSecurityServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to SecurityServiceServer will // result in compilation errors. type UnsafeSecurityServiceServer interface { mustEmbedUnimplementedSecurityServiceServer() } func RegisterSecurityServiceServer(s grpc.ServiceRegistrar, srv SecurityServiceServer) { // If the following call panics, it indicates UnimplementedSecurityServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&SecurityService_ServiceDesc, srv) } func _SecurityService_Certificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CertificateRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(SecurityServiceServer).Certificate(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: SecurityService_Certificate_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(SecurityServiceServer).Certificate(ctx, req.(*CertificateRequest)) } return interceptor(ctx, in, info, handler) } // SecurityService_ServiceDesc is the grpc.ServiceDesc for SecurityService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var SecurityService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "securityapi.SecurityService", HandlerType: (*SecurityServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Certificate", Handler: _SecurityService_Certificate_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "security/security.proto", } ================================================ FILE: pkg/machinery/api/security/security_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: security/security.proto package security import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *CertificateRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CertificateRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CertificateRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Csr) > 0 { i -= len(m.Csr) copy(dAtA[i:], m.Csr) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Csr))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CertificateResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *CertificateResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *CertificateResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Crt) > 0 { i -= len(m.Crt) copy(dAtA[i:], m.Crt) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Crt))) i-- dAtA[i] = 0x12 } if len(m.Ca) > 0 { i -= len(m.Ca) copy(dAtA[i:], m.Ca) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Ca))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *CertificateRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Csr) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *CertificateResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Ca) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Crt) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *CertificateRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CertificateRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CertificateRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Csr", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Csr = append(m.Csr[:0], dAtA[iNdEx:postIndex]...) if m.Csr == nil { m.Csr = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *CertificateResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: CertificateResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: CertificateResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Ca", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Ca = append(m.Ca[:0], dAtA[iNdEx:postIndex]...) if m.Ca == nil { m.Ca = []byte{} } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Crt", wireType) } var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } if byteLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + byteLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Crt = append(m.Crt[:0], dAtA[iNdEx:postIndex]...) if m.Crt == nil { m.Crt = []byte{} } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/storage/storage.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: storage/storage.proto package storage import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) type Disk_DiskType int32 const ( Disk_UNKNOWN Disk_DiskType = 0 Disk_SSD Disk_DiskType = 1 Disk_HDD Disk_DiskType = 2 Disk_NVME Disk_DiskType = 3 Disk_SD Disk_DiskType = 4 Disk_CD Disk_DiskType = 5 ) // Enum value maps for Disk_DiskType. var ( Disk_DiskType_name = map[int32]string{ 0: "UNKNOWN", 1: "SSD", 2: "HDD", 3: "NVME", 4: "SD", 5: "CD", } Disk_DiskType_value = map[string]int32{ "UNKNOWN": 0, "SSD": 1, "HDD": 2, "NVME": 3, "SD": 4, "CD": 5, } ) func (x Disk_DiskType) Enum() *Disk_DiskType { p := new(Disk_DiskType) *p = x return p } func (x Disk_DiskType) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (Disk_DiskType) Descriptor() protoreflect.EnumDescriptor { return file_storage_storage_proto_enumTypes[0].Descriptor() } func (Disk_DiskType) Type() protoreflect.EnumType { return &file_storage_storage_proto_enumTypes[0] } func (x Disk_DiskType) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use Disk_DiskType.Descriptor instead. func (Disk_DiskType) EnumDescriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{0, 0} } type BlockDeviceWipeDescriptor_Method int32 const ( // Fast wipe - wipe only filesystem signatures. BlockDeviceWipeDescriptor_FAST BlockDeviceWipeDescriptor_Method = 0 // Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features). BlockDeviceWipeDescriptor_ZEROES BlockDeviceWipeDescriptor_Method = 1 ) // Enum value maps for BlockDeviceWipeDescriptor_Method. var ( BlockDeviceWipeDescriptor_Method_name = map[int32]string{ 0: "FAST", 1: "ZEROES", } BlockDeviceWipeDescriptor_Method_value = map[string]int32{ "FAST": 0, "ZEROES": 1, } ) func (x BlockDeviceWipeDescriptor_Method) Enum() *BlockDeviceWipeDescriptor_Method { p := new(BlockDeviceWipeDescriptor_Method) *p = x return p } func (x BlockDeviceWipeDescriptor_Method) String() string { return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) } func (BlockDeviceWipeDescriptor_Method) Descriptor() protoreflect.EnumDescriptor { return file_storage_storage_proto_enumTypes[1].Descriptor() } func (BlockDeviceWipeDescriptor_Method) Type() protoreflect.EnumType { return &file_storage_storage_proto_enumTypes[1] } func (x BlockDeviceWipeDescriptor_Method) Number() protoreflect.EnumNumber { return protoreflect.EnumNumber(x) } // Deprecated: Use BlockDeviceWipeDescriptor_Method.Descriptor instead. func (BlockDeviceWipeDescriptor_Method) EnumDescriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{4, 0} } // Disk represents a disk. type Disk struct { state protoimpl.MessageState `protogen:"open.v1"` // Size indicates the disk size in bytes. Size uint64 `protobuf:"varint,1,opt,name=size,proto3" json:"size,omitempty"` // Model idicates the disk model. Model string `protobuf:"bytes,2,opt,name=model,proto3" json:"model,omitempty"` // DeviceName indicates the disk name (e.g. `sda`). DeviceName string `protobuf:"bytes,3,opt,name=device_name,json=deviceName,proto3" json:"device_name,omitempty"` // Name as in `/sys/block//device/name`. Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` // Serial as in `/sys/block//device/serial`. Serial string `protobuf:"bytes,5,opt,name=serial,proto3" json:"serial,omitempty"` // Modalias as in `/sys/block//device/modalias`. Modalias string `protobuf:"bytes,6,opt,name=modalias,proto3" json:"modalias,omitempty"` // Uuid as in `/sys/block//device/uuid`. Uuid string `protobuf:"bytes,7,opt,name=uuid,proto3" json:"uuid,omitempty"` // Wwid as in `/sys/block//device/wwid`. Wwid string `protobuf:"bytes,8,opt,name=wwid,proto3" json:"wwid,omitempty"` // Type is a type of the disk: nvme, ssd, hdd, sd card. Type Disk_DiskType `protobuf:"varint,9,opt,name=type,proto3,enum=storage.Disk_DiskType" json:"type,omitempty"` // BusPath is the bus path of the disk. BusPath string `protobuf:"bytes,10,opt,name=bus_path,json=busPath,proto3" json:"bus_path,omitempty"` // SystemDisk indicates that the disk is used as Talos system disk. SystemDisk bool `protobuf:"varint,11,opt,name=system_disk,json=systemDisk,proto3" json:"system_disk,omitempty"` // Subsystem is the symlink path in the `/sys/block//subsystem`. Subsystem string `protobuf:"bytes,12,opt,name=subsystem,proto3" json:"subsystem,omitempty"` // Readonly specifies if the disk is read only. Readonly bool `protobuf:"varint,13,opt,name=readonly,proto3" json:"readonly,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Disk) Reset() { *x = Disk{} mi := &file_storage_storage_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Disk) String() string { return protoimpl.X.MessageStringOf(x) } func (*Disk) ProtoMessage() {} func (x *Disk) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Disk.ProtoReflect.Descriptor instead. func (*Disk) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{0} } func (x *Disk) GetSize() uint64 { if x != nil { return x.Size } return 0 } func (x *Disk) GetModel() string { if x != nil { return x.Model } return "" } func (x *Disk) GetDeviceName() string { if x != nil { return x.DeviceName } return "" } func (x *Disk) GetName() string { if x != nil { return x.Name } return "" } func (x *Disk) GetSerial() string { if x != nil { return x.Serial } return "" } func (x *Disk) GetModalias() string { if x != nil { return x.Modalias } return "" } func (x *Disk) GetUuid() string { if x != nil { return x.Uuid } return "" } func (x *Disk) GetWwid() string { if x != nil { return x.Wwid } return "" } func (x *Disk) GetType() Disk_DiskType { if x != nil { return x.Type } return Disk_UNKNOWN } func (x *Disk) GetBusPath() string { if x != nil { return x.BusPath } return "" } func (x *Disk) GetSystemDisk() bool { if x != nil { return x.SystemDisk } return false } func (x *Disk) GetSubsystem() string { if x != nil { return x.Subsystem } return "" } func (x *Disk) GetReadonly() bool { if x != nil { return x.Readonly } return false } // DisksResponse represents the response of the `Disks` RPC. type Disks struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Disks []*Disk `protobuf:"bytes,2,rep,name=disks,proto3" json:"disks,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Disks) Reset() { *x = Disks{} mi := &file_storage_storage_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Disks) String() string { return protoimpl.X.MessageStringOf(x) } func (*Disks) ProtoMessage() {} func (x *Disks) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Disks.ProtoReflect.Descriptor instead. func (*Disks) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{1} } func (x *Disks) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Disks) GetDisks() []*Disk { if x != nil { return x.Disks } return nil } type DisksResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Disks `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *DisksResponse) Reset() { *x = DisksResponse{} mi := &file_storage_storage_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *DisksResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*DisksResponse) ProtoMessage() {} func (x *DisksResponse) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use DisksResponse.ProtoReflect.Descriptor instead. func (*DisksResponse) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{2} } func (x *DisksResponse) GetMessages() []*Disks { if x != nil { return x.Messages } return nil } type BlockDeviceWipeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Devices []*BlockDeviceWipeDescriptor `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BlockDeviceWipeRequest) Reset() { *x = BlockDeviceWipeRequest{} mi := &file_storage_storage_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BlockDeviceWipeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*BlockDeviceWipeRequest) ProtoMessage() {} func (x *BlockDeviceWipeRequest) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BlockDeviceWipeRequest.ProtoReflect.Descriptor instead. func (*BlockDeviceWipeRequest) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{3} } func (x *BlockDeviceWipeRequest) GetDevices() []*BlockDeviceWipeDescriptor { if x != nil { return x.Devices } return nil } // BlockDeviceWipeDescriptor represents a single block device to be wiped. // // The device can be either a full disk (e.g. vda) or a partition (vda5). // The device should not be used in any of active volumes. // The device should not be used as a secondary (e.g. part of LVM). type BlockDeviceWipeDescriptor struct { state protoimpl.MessageState `protogen:"open.v1"` // Device name to wipe (e.g. sda or sda5). // // The name should be submitted without `/dev/` prefix. Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"` // Wipe method to use. Method BlockDeviceWipeDescriptor_Method `protobuf:"varint,2,opt,name=method,proto3,enum=storage.BlockDeviceWipeDescriptor_Method" json:"method,omitempty"` // Skip the volume in use check. SkipVolumeCheck bool `protobuf:"varint,3,opt,name=skip_volume_check,json=skipVolumeCheck,proto3" json:"skip_volume_check,omitempty"` // Skip the secondary disk check (e.g. underlying disk for RAID or LVM). SkipSecondaryCheck bool `protobuf:"varint,5,opt,name=skip_secondary_check,json=skipSecondaryCheck,proto3" json:"skip_secondary_check,omitempty"` // Drop the partition (only applies if the device is a partition). DropPartition bool `protobuf:"varint,4,opt,name=drop_partition,json=dropPartition,proto3" json:"drop_partition,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BlockDeviceWipeDescriptor) Reset() { *x = BlockDeviceWipeDescriptor{} mi := &file_storage_storage_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BlockDeviceWipeDescriptor) String() string { return protoimpl.X.MessageStringOf(x) } func (*BlockDeviceWipeDescriptor) ProtoMessage() {} func (x *BlockDeviceWipeDescriptor) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BlockDeviceWipeDescriptor.ProtoReflect.Descriptor instead. func (*BlockDeviceWipeDescriptor) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{4} } func (x *BlockDeviceWipeDescriptor) GetDevice() string { if x != nil { return x.Device } return "" } func (x *BlockDeviceWipeDescriptor) GetMethod() BlockDeviceWipeDescriptor_Method { if x != nil { return x.Method } return BlockDeviceWipeDescriptor_FAST } func (x *BlockDeviceWipeDescriptor) GetSkipVolumeCheck() bool { if x != nil { return x.SkipVolumeCheck } return false } func (x *BlockDeviceWipeDescriptor) GetSkipSecondaryCheck() bool { if x != nil { return x.SkipSecondaryCheck } return false } func (x *BlockDeviceWipeDescriptor) GetDropPartition() bool { if x != nil { return x.DropPartition } return false } type BlockDeviceWipeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*BlockDeviceWipe `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BlockDeviceWipeResponse) Reset() { *x = BlockDeviceWipeResponse{} mi := &file_storage_storage_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BlockDeviceWipeResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*BlockDeviceWipeResponse) ProtoMessage() {} func (x *BlockDeviceWipeResponse) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BlockDeviceWipeResponse.ProtoReflect.Descriptor instead. func (*BlockDeviceWipeResponse) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{5} } func (x *BlockDeviceWipeResponse) GetMessages() []*BlockDeviceWipe { if x != nil { return x.Messages } return nil } type BlockDeviceWipe struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *BlockDeviceWipe) Reset() { *x = BlockDeviceWipe{} mi := &file_storage_storage_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *BlockDeviceWipe) String() string { return protoimpl.X.MessageStringOf(x) } func (*BlockDeviceWipe) ProtoMessage() {} func (x *BlockDeviceWipe) ProtoReflect() protoreflect.Message { mi := &file_storage_storage_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use BlockDeviceWipe.ProtoReflect.Descriptor instead. func (*BlockDeviceWipe) Descriptor() ([]byte, []int) { return file_storage_storage_proto_rawDescGZIP(), []int{6} } func (x *BlockDeviceWipe) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } var File_storage_storage_proto protoreflect.FileDescriptor const file_storage_storage_proto_rawDesc = "" + "\n" + "\x15storage/storage.proto\x12\astorage\x1a\x13common/common.proto\x1a\x1bgoogle/protobuf/empty.proto\"\xa8\x03\n" + "\x04Disk\x12\x12\n" + "\x04size\x18\x01 \x01(\x04R\x04size\x12\x14\n" + "\x05model\x18\x02 \x01(\tR\x05model\x12\x1f\n" + "\vdevice_name\x18\x03 \x01(\tR\n" + "deviceName\x12\x12\n" + "\x04name\x18\x04 \x01(\tR\x04name\x12\x16\n" + "\x06serial\x18\x05 \x01(\tR\x06serial\x12\x1a\n" + "\bmodalias\x18\x06 \x01(\tR\bmodalias\x12\x12\n" + "\x04uuid\x18\a \x01(\tR\x04uuid\x12\x12\n" + "\x04wwid\x18\b \x01(\tR\x04wwid\x12*\n" + "\x04type\x18\t \x01(\x0e2\x16.storage.Disk.DiskTypeR\x04type\x12\x19\n" + "\bbus_path\x18\n" + " \x01(\tR\abusPath\x12\x1f\n" + "\vsystem_disk\x18\v \x01(\bR\n" + "systemDisk\x12\x1c\n" + "\tsubsystem\x18\f \x01(\tR\tsubsystem\x12\x1a\n" + "\breadonly\x18\r \x01(\bR\breadonly\"C\n" + "\bDiskType\x12\v\n" + "\aUNKNOWN\x10\x00\x12\a\n" + "\x03SSD\x10\x01\x12\a\n" + "\x03HDD\x10\x02\x12\b\n" + "\x04NVME\x10\x03\x12\x06\n" + "\x02SD\x10\x04\x12\x06\n" + "\x02CD\x10\x05\"Z\n" + "\x05Disks\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12#\n" + "\x05disks\x18\x02 \x03(\v2\r.storage.DiskR\x05disks\";\n" + "\rDisksResponse\x12*\n" + "\bmessages\x18\x01 \x03(\v2\x0e.storage.DisksR\bmessages\"V\n" + "\x16BlockDeviceWipeRequest\x12<\n" + "\adevices\x18\x01 \x03(\v2\".storage.BlockDeviceWipeDescriptorR\adevices\"\x9b\x02\n" + "\x19BlockDeviceWipeDescriptor\x12\x16\n" + "\x06device\x18\x01 \x01(\tR\x06device\x12A\n" + "\x06method\x18\x02 \x01(\x0e2).storage.BlockDeviceWipeDescriptor.MethodR\x06method\x12*\n" + "\x11skip_volume_check\x18\x03 \x01(\bR\x0fskipVolumeCheck\x120\n" + "\x14skip_secondary_check\x18\x05 \x01(\bR\x12skipSecondaryCheck\x12%\n" + "\x0edrop_partition\x18\x04 \x01(\bR\rdropPartition\"\x1e\n" + "\x06Method\x12\b\n" + "\x04FAST\x10\x00\x12\n" + "\n" + "\x06ZEROES\x10\x01\"O\n" + "\x17BlockDeviceWipeResponse\x124\n" + "\bmessages\x18\x01 \x03(\v2\x18.storage.BlockDeviceWipeR\bmessages\"?\n" + "\x0fBlockDeviceWipe\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata2\x9f\x01\n" + "\x0eStorageService\x127\n" + "\x05Disks\x12\x16.google.protobuf.Empty\x1a\x16.storage.DisksResponse\x12T\n" + "\x0fBlockDeviceWipe\x12\x1f.storage.BlockDeviceWipeRequest\x1a .storage.BlockDeviceWipeResponseBN\n" + "\x15dev.talos.api.storageZ5github.com/siderolabs/talos/pkg/machinery/api/storageb\x06proto3" var ( file_storage_storage_proto_rawDescOnce sync.Once file_storage_storage_proto_rawDescData []byte ) func file_storage_storage_proto_rawDescGZIP() []byte { file_storage_storage_proto_rawDescOnce.Do(func() { file_storage_storage_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_storage_storage_proto_rawDesc), len(file_storage_storage_proto_rawDesc))) }) return file_storage_storage_proto_rawDescData } var file_storage_storage_proto_enumTypes = make([]protoimpl.EnumInfo, 2) var file_storage_storage_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_storage_storage_proto_goTypes = []any{ (Disk_DiskType)(0), // 0: storage.Disk.DiskType (BlockDeviceWipeDescriptor_Method)(0), // 1: storage.BlockDeviceWipeDescriptor.Method (*Disk)(nil), // 2: storage.Disk (*Disks)(nil), // 3: storage.Disks (*DisksResponse)(nil), // 4: storage.DisksResponse (*BlockDeviceWipeRequest)(nil), // 5: storage.BlockDeviceWipeRequest (*BlockDeviceWipeDescriptor)(nil), // 6: storage.BlockDeviceWipeDescriptor (*BlockDeviceWipeResponse)(nil), // 7: storage.BlockDeviceWipeResponse (*BlockDeviceWipe)(nil), // 8: storage.BlockDeviceWipe (*common.Metadata)(nil), // 9: common.Metadata (*emptypb.Empty)(nil), // 10: google.protobuf.Empty } var file_storage_storage_proto_depIdxs = []int32{ 0, // 0: storage.Disk.type:type_name -> storage.Disk.DiskType 9, // 1: storage.Disks.metadata:type_name -> common.Metadata 2, // 2: storage.Disks.disks:type_name -> storage.Disk 3, // 3: storage.DisksResponse.messages:type_name -> storage.Disks 6, // 4: storage.BlockDeviceWipeRequest.devices:type_name -> storage.BlockDeviceWipeDescriptor 1, // 5: storage.BlockDeviceWipeDescriptor.method:type_name -> storage.BlockDeviceWipeDescriptor.Method 8, // 6: storage.BlockDeviceWipeResponse.messages:type_name -> storage.BlockDeviceWipe 9, // 7: storage.BlockDeviceWipe.metadata:type_name -> common.Metadata 10, // 8: storage.StorageService.Disks:input_type -> google.protobuf.Empty 5, // 9: storage.StorageService.BlockDeviceWipe:input_type -> storage.BlockDeviceWipeRequest 4, // 10: storage.StorageService.Disks:output_type -> storage.DisksResponse 7, // 11: storage.StorageService.BlockDeviceWipe:output_type -> storage.BlockDeviceWipeResponse 10, // [10:12] is the sub-list for method output_type 8, // [8:10] is the sub-list for method input_type 8, // [8:8] is the sub-list for extension type_name 8, // [8:8] is the sub-list for extension extendee 0, // [0:8] is the sub-list for field type_name } func init() { file_storage_storage_proto_init() } func file_storage_storage_proto_init() { if File_storage_storage_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_storage_storage_proto_rawDesc), len(file_storage_storage_proto_rawDesc)), NumEnums: 2, NumMessages: 7, NumExtensions: 0, NumServices: 1, }, GoTypes: file_storage_storage_proto_goTypes, DependencyIndexes: file_storage_storage_proto_depIdxs, EnumInfos: file_storage_storage_proto_enumTypes, MessageInfos: file_storage_storage_proto_msgTypes, }.Build() File_storage_storage_proto = out.File file_storage_storage_proto_goTypes = nil file_storage_storage_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/storage/storage_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: storage/storage.proto package storage import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( StorageService_Disks_FullMethodName = "/storage.StorageService/Disks" StorageService_BlockDeviceWipe_FullMethodName = "/storage.StorageService/BlockDeviceWipe" ) // StorageServiceClient is the client API for StorageService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // StorageService represents the storage service. type StorageServiceClient interface { Disks(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DisksResponse, error) // BlockDeviceWipe performs a wipe of the blockdevice (partition or disk). // // The method doesn't require a reboot, and it can only wipe blockdevices which are not // being used as volumes at the moment. // Wiping of volumes requires a different API. BlockDeviceWipe(ctx context.Context, in *BlockDeviceWipeRequest, opts ...grpc.CallOption) (*BlockDeviceWipeResponse, error) } type storageServiceClient struct { cc grpc.ClientConnInterface } func NewStorageServiceClient(cc grpc.ClientConnInterface) StorageServiceClient { return &storageServiceClient{cc} } func (c *storageServiceClient) Disks(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*DisksResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(DisksResponse) err := c.cc.Invoke(ctx, StorageService_Disks_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *storageServiceClient) BlockDeviceWipe(ctx context.Context, in *BlockDeviceWipeRequest, opts ...grpc.CallOption) (*BlockDeviceWipeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(BlockDeviceWipeResponse) err := c.cc.Invoke(ctx, StorageService_BlockDeviceWipe_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // StorageServiceServer is the server API for StorageService service. // All implementations must embed UnimplementedStorageServiceServer // for forward compatibility. // // StorageService represents the storage service. type StorageServiceServer interface { Disks(context.Context, *emptypb.Empty) (*DisksResponse, error) // BlockDeviceWipe performs a wipe of the blockdevice (partition or disk). // // The method doesn't require a reboot, and it can only wipe blockdevices which are not // being used as volumes at the moment. // Wiping of volumes requires a different API. BlockDeviceWipe(context.Context, *BlockDeviceWipeRequest) (*BlockDeviceWipeResponse, error) mustEmbedUnimplementedStorageServiceServer() } // UnimplementedStorageServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedStorageServiceServer struct{} func (UnimplementedStorageServiceServer) Disks(context.Context, *emptypb.Empty) (*DisksResponse, error) { return nil, status.Error(codes.Unimplemented, "method Disks not implemented") } func (UnimplementedStorageServiceServer) BlockDeviceWipe(context.Context, *BlockDeviceWipeRequest) (*BlockDeviceWipeResponse, error) { return nil, status.Error(codes.Unimplemented, "method BlockDeviceWipe not implemented") } func (UnimplementedStorageServiceServer) mustEmbedUnimplementedStorageServiceServer() {} func (UnimplementedStorageServiceServer) testEmbeddedByValue() {} // UnsafeStorageServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to StorageServiceServer will // result in compilation errors. type UnsafeStorageServiceServer interface { mustEmbedUnimplementedStorageServiceServer() } func RegisterStorageServiceServer(s grpc.ServiceRegistrar, srv StorageServiceServer) { // If the following call panics, it indicates UnimplementedStorageServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&StorageService_ServiceDesc, srv) } func _StorageService_Disks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(StorageServiceServer).Disks(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: StorageService_Disks_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StorageServiceServer).Disks(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _StorageService_BlockDeviceWipe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(BlockDeviceWipeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(StorageServiceServer).BlockDeviceWipe(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: StorageService_BlockDeviceWipe_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(StorageServiceServer).BlockDeviceWipe(ctx, req.(*BlockDeviceWipeRequest)) } return interceptor(ctx, in, info, handler) } // StorageService_ServiceDesc is the grpc.ServiceDesc for StorageService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var StorageService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "storage.StorageService", HandlerType: (*StorageServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Disks", Handler: _StorageService_Disks_Handler, }, { MethodName: "BlockDeviceWipe", Handler: _StorageService_BlockDeviceWipe_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "storage/storage.proto", } ================================================ FILE: pkg/machinery/api/storage/storage_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: storage/storage.proto package storage import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *Disk) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Disk) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Disk) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Readonly { i-- if m.Readonly { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x68 } if len(m.Subsystem) > 0 { i -= len(m.Subsystem) copy(dAtA[i:], m.Subsystem) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Subsystem))) i-- dAtA[i] = 0x62 } if m.SystemDisk { i-- if m.SystemDisk { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x58 } if len(m.BusPath) > 0 { i -= len(m.BusPath) copy(dAtA[i:], m.BusPath) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.BusPath))) i-- dAtA[i] = 0x52 } if m.Type != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Type)) i-- dAtA[i] = 0x48 } if len(m.Wwid) > 0 { i -= len(m.Wwid) copy(dAtA[i:], m.Wwid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Wwid))) i-- dAtA[i] = 0x42 } if len(m.Uuid) > 0 { i -= len(m.Uuid) copy(dAtA[i:], m.Uuid) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Uuid))) i-- dAtA[i] = 0x3a } if len(m.Modalias) > 0 { i -= len(m.Modalias) copy(dAtA[i:], m.Modalias) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Modalias))) i-- dAtA[i] = 0x32 } if len(m.Serial) > 0 { i -= len(m.Serial) copy(dAtA[i:], m.Serial) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Serial))) i-- dAtA[i] = 0x2a } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name))) i-- dAtA[i] = 0x22 } if len(m.DeviceName) > 0 { i -= len(m.DeviceName) copy(dAtA[i:], m.DeviceName) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.DeviceName))) i-- dAtA[i] = 0x1a } if len(m.Model) > 0 { i -= len(m.Model) copy(dAtA[i:], m.Model) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Model))) i-- dAtA[i] = 0x12 } if m.Size != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Size)) i-- dAtA[i] = 0x8 } return len(dAtA) - i, nil } func (m *Disks) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Disks) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Disks) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Disks) > 0 { for iNdEx := len(m.Disks) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Disks[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x12 } } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *DisksResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *DisksResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *DisksResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *BlockDeviceWipeRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BlockDeviceWipeRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BlockDeviceWipeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Devices) > 0 { for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Devices[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *BlockDeviceWipeDescriptor) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BlockDeviceWipeDescriptor) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BlockDeviceWipeDescriptor) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.SkipSecondaryCheck { i-- if m.SkipSecondaryCheck { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x28 } if m.DropPartition { i-- if m.DropPartition { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x20 } if m.SkipVolumeCheck { i-- if m.SkipVolumeCheck { dAtA[i] = 1 } else { dAtA[i] = 0 } i-- dAtA[i] = 0x18 } if m.Method != 0 { i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Method)) i-- dAtA[i] = 0x10 } if len(m.Device) > 0 { i -= len(m.Device) copy(dAtA[i:], m.Device) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Device))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *BlockDeviceWipeResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BlockDeviceWipeResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BlockDeviceWipeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *BlockDeviceWipe) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *BlockDeviceWipe) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *BlockDeviceWipe) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Disk) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Size != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Size)) } l = len(m.Model) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.DeviceName) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Name) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Serial) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Modalias) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Uuid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Wwid) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Type != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Type)) } l = len(m.BusPath) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.SystemDisk { n += 2 } l = len(m.Subsystem) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Readonly { n += 2 } n += len(m.unknownFields) return n } func (m *Disks) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if len(m.Disks) > 0 { for _, e := range m.Disks { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *DisksResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BlockDeviceWipeRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Devices) > 0 { for _, e := range m.Devices { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BlockDeviceWipeDescriptor) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Device) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Method != 0 { n += 1 + protohelpers.SizeOfVarint(uint64(m.Method)) } if m.SkipVolumeCheck { n += 2 } if m.DropPartition { n += 2 } if m.SkipSecondaryCheck { n += 2 } n += len(m.unknownFields) return n } func (m *BlockDeviceWipeResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *BlockDeviceWipe) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Disk) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Disk: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Disk: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Size", wireType) } m.Size = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Size |= uint64(b&0x7F) << shift if b < 0x80 { break } } case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Model", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Model = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field DeviceName", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.DeviceName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Serial", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Serial = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Modalias", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Modalias = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Uuid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Uuid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Wwid", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Wwid = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) } m.Type = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Type |= Disk_DiskType(b&0x7F) << shift if b < 0x80 { break } } case 10: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field BusPath", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.BusPath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SystemDisk", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SystemDisk = bool(v != 0) case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Subsystem", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Subsystem = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 13: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Readonly", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.Readonly = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Disks) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Disks: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Disks: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Disks", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Disks = append(m.Disks, &Disk{}) if err := m.Disks[len(m.Disks)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *DisksResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: DisksResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: DisksResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Disks{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BlockDeviceWipeRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BlockDeviceWipeRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BlockDeviceWipeRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Devices = append(m.Devices, &BlockDeviceWipeDescriptor{}) if err := m.Devices[len(m.Devices)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BlockDeviceWipeDescriptor) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BlockDeviceWipeDescriptor: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BlockDeviceWipeDescriptor: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Device = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 2: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Method", wireType) } m.Method = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ m.Method |= BlockDeviceWipeDescriptor_Method(b&0x7F) << shift if b < 0x80 { break } } case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipVolumeCheck", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipVolumeCheck = bool(v != 0) case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field DropPartition", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.DropPartition = bool(v != 0) case 5: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field SkipSecondaryCheck", wireType) } var v int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ v |= int(b&0x7F) << shift if b < 0x80 { break } } m.SkipSecondaryCheck = bool(v != 0) default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BlockDeviceWipeResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BlockDeviceWipeResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BlockDeviceWipeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &BlockDeviceWipe{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *BlockDeviceWipe) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: BlockDeviceWipe: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: BlockDeviceWipe: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/api/time/time.pb.go ================================================ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.11-devel // protoc (unknown) // source: time/time.proto package time import ( reflect "reflect" sync "sync" unsafe "unsafe" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" timestamppb "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) // The response message containing the ntp server type TimeRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeRequest) Reset() { *x = TimeRequest{} mi := &file_time_time_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeRequest) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeRequest) ProtoMessage() {} func (x *TimeRequest) ProtoReflect() protoreflect.Message { mi := &file_time_time_proto_msgTypes[0] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeRequest.ProtoReflect.Descriptor instead. func (*TimeRequest) Descriptor() ([]byte, []int) { return file_time_time_proto_rawDescGZIP(), []int{0} } func (x *TimeRequest) GetServer() string { if x != nil { return x.Server } return "" } type Time struct { state protoimpl.MessageState `protogen:"open.v1"` Metadata *common.Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` Server string `protobuf:"bytes,2,opt,name=server,proto3" json:"server,omitempty"` Localtime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=localtime,proto3" json:"localtime,omitempty"` Remotetime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=remotetime,proto3" json:"remotetime,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Time) Reset() { *x = Time{} mi := &file_time_time_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *Time) String() string { return protoimpl.X.MessageStringOf(x) } func (*Time) ProtoMessage() {} func (x *Time) ProtoReflect() protoreflect.Message { mi := &file_time_time_proto_msgTypes[1] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use Time.ProtoReflect.Descriptor instead. func (*Time) Descriptor() ([]byte, []int) { return file_time_time_proto_rawDescGZIP(), []int{1} } func (x *Time) GetMetadata() *common.Metadata { if x != nil { return x.Metadata } return nil } func (x *Time) GetServer() string { if x != nil { return x.Server } return "" } func (x *Time) GetLocaltime() *timestamppb.Timestamp { if x != nil { return x.Localtime } return nil } func (x *Time) GetRemotetime() *timestamppb.Timestamp { if x != nil { return x.Remotetime } return nil } // The response message containing the ntp server, time, and offset type TimeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` Messages []*Time `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *TimeResponse) Reset() { *x = TimeResponse{} mi := &file_time_time_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } func (x *TimeResponse) String() string { return protoimpl.X.MessageStringOf(x) } func (*TimeResponse) ProtoMessage() {} func (x *TimeResponse) ProtoReflect() protoreflect.Message { mi := &file_time_time_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { ms.StoreMessageInfo(mi) } return ms } return mi.MessageOf(x) } // Deprecated: Use TimeResponse.ProtoReflect.Descriptor instead. func (*TimeResponse) Descriptor() ([]byte, []int) { return file_time_time_proto_rawDescGZIP(), []int{2} } func (x *TimeResponse) GetMessages() []*Time { if x != nil { return x.Messages } return nil } var File_time_time_proto protoreflect.FileDescriptor const file_time_time_proto_rawDesc = "" + "\n" + "\x0ftime/time.proto\x12\x04time\x1a\x13common/common.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"%\n" + "\vTimeRequest\x12\x16\n" + "\x06server\x18\x01 \x01(\tR\x06server\"\xc2\x01\n" + "\x04Time\x12,\n" + "\bmetadata\x18\x01 \x01(\v2\x10.common.MetadataR\bmetadata\x12\x16\n" + "\x06server\x18\x02 \x01(\tR\x06server\x128\n" + "\tlocaltime\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tlocaltime\x12:\n" + "\n" + "remotetime\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "remotetime\"6\n" + "\fTimeResponse\x12&\n" + "\bmessages\x18\x01 \x03(\v2\n" + ".time.TimeR\bmessages2u\n" + "\vTimeService\x122\n" + "\x04Time\x12\x16.google.protobuf.Empty\x1a\x12.time.TimeResponse\x122\n" + "\tTimeCheck\x12\x11.time.TimeRequest\x1a\x12.time.TimeResponseBH\n" + "\x12dev.talos.api.timeZ2github.com/siderolabs/talos/pkg/machinery/api/timeb\x06proto3" var ( file_time_time_proto_rawDescOnce sync.Once file_time_time_proto_rawDescData []byte ) func file_time_time_proto_rawDescGZIP() []byte { file_time_time_proto_rawDescOnce.Do(func() { file_time_time_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_time_time_proto_rawDesc), len(file_time_time_proto_rawDesc))) }) return file_time_time_proto_rawDescData } var file_time_time_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_time_time_proto_goTypes = []any{ (*TimeRequest)(nil), // 0: time.TimeRequest (*Time)(nil), // 1: time.Time (*TimeResponse)(nil), // 2: time.TimeResponse (*common.Metadata)(nil), // 3: common.Metadata (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp (*emptypb.Empty)(nil), // 5: google.protobuf.Empty } var file_time_time_proto_depIdxs = []int32{ 3, // 0: time.Time.metadata:type_name -> common.Metadata 4, // 1: time.Time.localtime:type_name -> google.protobuf.Timestamp 4, // 2: time.Time.remotetime:type_name -> google.protobuf.Timestamp 1, // 3: time.TimeResponse.messages:type_name -> time.Time 5, // 4: time.TimeService.Time:input_type -> google.protobuf.Empty 0, // 5: time.TimeService.TimeCheck:input_type -> time.TimeRequest 2, // 6: time.TimeService.Time:output_type -> time.TimeResponse 2, // 7: time.TimeService.TimeCheck:output_type -> time.TimeResponse 6, // [6:8] is the sub-list for method output_type 4, // [4:6] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name 4, // [4:4] is the sub-list for extension extendee 0, // [0:4] is the sub-list for field type_name } func init() { file_time_time_proto_init() } func file_time_time_proto_init() { if File_time_time_proto != nil { return } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_time_time_proto_rawDesc), len(file_time_time_proto_rawDesc)), NumEnums: 0, NumMessages: 3, NumExtensions: 0, NumServices: 1, }, GoTypes: file_time_time_proto_goTypes, DependencyIndexes: file_time_time_proto_depIdxs, MessageInfos: file_time_time_proto_msgTypes, }.Build() File_time_time_proto = out.File file_time_time_proto_goTypes = nil file_time_time_proto_depIdxs = nil } ================================================ FILE: pkg/machinery/api/time/time_grpc.pb.go ================================================ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.1 // - protoc (unknown) // source: time/time.proto package time import ( context "context" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" emptypb "google.golang.org/protobuf/types/known/emptypb" ) // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. // Requires gRPC-Go v1.64.0 or later. const _ = grpc.SupportPackageIsVersion9 const ( TimeService_Time_FullMethodName = "/time.TimeService/Time" TimeService_TimeCheck_FullMethodName = "/time.TimeService/TimeCheck" ) // TimeServiceClient is the client API for TimeService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. // // The time service definition. type TimeServiceClient interface { Time(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TimeResponse, error) TimeCheck(ctx context.Context, in *TimeRequest, opts ...grpc.CallOption) (*TimeResponse, error) } type timeServiceClient struct { cc grpc.ClientConnInterface } func NewTimeServiceClient(cc grpc.ClientConnInterface) TimeServiceClient { return &timeServiceClient{cc} } func (c *timeServiceClient) Time(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*TimeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TimeResponse) err := c.cc.Invoke(ctx, TimeService_Time_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } func (c *timeServiceClient) TimeCheck(ctx context.Context, in *TimeRequest, opts ...grpc.CallOption) (*TimeResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(TimeResponse) err := c.cc.Invoke(ctx, TimeService_TimeCheck_FullMethodName, in, out, cOpts...) if err != nil { return nil, err } return out, nil } // TimeServiceServer is the server API for TimeService service. // All implementations must embed UnimplementedTimeServiceServer // for forward compatibility. // // The time service definition. type TimeServiceServer interface { Time(context.Context, *emptypb.Empty) (*TimeResponse, error) TimeCheck(context.Context, *TimeRequest) (*TimeResponse, error) mustEmbedUnimplementedTimeServiceServer() } // UnimplementedTimeServiceServer must be embedded to have // forward compatible implementations. // // NOTE: this should be embedded by value instead of pointer to avoid a nil // pointer dereference when methods are called. type UnimplementedTimeServiceServer struct{} func (UnimplementedTimeServiceServer) Time(context.Context, *emptypb.Empty) (*TimeResponse, error) { return nil, status.Error(codes.Unimplemented, "method Time not implemented") } func (UnimplementedTimeServiceServer) TimeCheck(context.Context, *TimeRequest) (*TimeResponse, error) { return nil, status.Error(codes.Unimplemented, "method TimeCheck not implemented") } func (UnimplementedTimeServiceServer) mustEmbedUnimplementedTimeServiceServer() {} func (UnimplementedTimeServiceServer) testEmbeddedByValue() {} // UnsafeTimeServiceServer may be embedded to opt out of forward compatibility for this service. // Use of this interface is not recommended, as added methods to TimeServiceServer will // result in compilation errors. type UnsafeTimeServiceServer interface { mustEmbedUnimplementedTimeServiceServer() } func RegisterTimeServiceServer(s grpc.ServiceRegistrar, srv TimeServiceServer) { // If the following call panics, it indicates UnimplementedTimeServiceServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization // time to prevent it from happening at runtime later due to I/O. if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } s.RegisterService(&TimeService_ServiceDesc, srv) } func _TimeService_Time_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(emptypb.Empty) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TimeServiceServer).Time(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TimeService_Time_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TimeServiceServer).Time(ctx, req.(*emptypb.Empty)) } return interceptor(ctx, in, info, handler) } func _TimeService_TimeCheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(TimeRequest) if err := dec(in); err != nil { return nil, err } if interceptor == nil { return srv.(TimeServiceServer).TimeCheck(ctx, in) } info := &grpc.UnaryServerInfo{ Server: srv, FullMethod: TimeService_TimeCheck_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(TimeServiceServer).TimeCheck(ctx, req.(*TimeRequest)) } return interceptor(ctx, in, info, handler) } // TimeService_ServiceDesc is the grpc.ServiceDesc for TimeService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) var TimeService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "time.TimeService", HandlerType: (*TimeServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Time", Handler: _TimeService_Time_Handler, }, { MethodName: "TimeCheck", Handler: _TimeService_TimeCheck_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "time/time.proto", } ================================================ FILE: pkg/machinery/api/time/time_vtproto.pb.go ================================================ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. // protoc-gen-go-vtproto version: v0.6.1-0.20250313105119-ba97887b0a25 // source: time/time.proto package time import ( fmt "fmt" io "io" protohelpers "github.com/planetscale/vtprotobuf/protohelpers" timestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" proto "google.golang.org/protobuf/proto" protoimpl "google.golang.org/protobuf/runtime/protoimpl" timestamppb1 "google.golang.org/protobuf/types/known/timestamppb" common "github.com/siderolabs/talos/pkg/machinery/api/common" ) const ( // Verify that this generated code is sufficiently up-to-date. _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) // Verify that runtime/protoimpl is sufficiently up-to-date. _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) func (m *TimeRequest) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TimeRequest) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TimeRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Server) > 0 { i -= len(m.Server) copy(dAtA[i:], m.Server) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Server))) i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *Time) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *Time) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *Time) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if m.Remotetime != nil { size, err := (*timestamppb.Timestamp)(m.Remotetime).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x22 } if m.Localtime != nil { size, err := (*timestamppb.Timestamp)(m.Localtime).MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0x1a } if len(m.Server) > 0 { i -= len(m.Server) copy(dAtA[i:], m.Server) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Server))) i-- dAtA[i] = 0x12 } if m.Metadata != nil { if vtmsg, ok := interface{}(m.Metadata).(interface { MarshalToSizedBufferVT([]byte) (int, error) }); ok { size, err := vtmsg.MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) } else { encoded, err := proto.Marshal(m.Metadata) if err != nil { return 0, err } i -= len(encoded) copy(dAtA[i:], encoded) i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded))) } i-- dAtA[i] = 0xa } return len(dAtA) - i, nil } func (m *TimeResponse) MarshalVT() (dAtA []byte, err error) { if m == nil { return nil, nil } size := m.SizeVT() dAtA = make([]byte, size) n, err := m.MarshalToSizedBufferVT(dAtA[:size]) if err != nil { return nil, err } return dAtA[:n], nil } func (m *TimeResponse) MarshalToVT(dAtA []byte) (int, error) { size := m.SizeVT() return m.MarshalToSizedBufferVT(dAtA[:size]) } func (m *TimeResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { if m == nil { return 0, nil } i := len(dAtA) _ = i var l int _ = l if m.unknownFields != nil { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } if len(m.Messages) > 0 { for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- { size, err := m.Messages[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) if err != nil { return 0, err } i -= size i = protohelpers.EncodeVarint(dAtA, i, uint64(size)) i-- dAtA[i] = 0xa } } return len(dAtA) - i, nil } func (m *TimeRequest) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l l = len(m.Server) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *Time) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if m.Metadata != nil { if size, ok := interface{}(m.Metadata).(interface { SizeVT() int }); ok { l = size.SizeVT() } else { l = proto.Size(m.Metadata) } n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } l = len(m.Server) if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Localtime != nil { l = (*timestamppb.Timestamp)(m.Localtime).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } if m.Remotetime != nil { l = (*timestamppb.Timestamp)(m.Remotetime).SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n } func (m *TimeResponse) SizeVT() (n int) { if m == nil { return 0 } var l int _ = l if len(m.Messages) > 0 { for _, e := range m.Messages { l = e.SizeVT() n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } } n += len(m.unknownFields) return n } func (m *TimeRequest) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TimeRequest: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TimeRequest: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Server = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *Time) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: Time: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: Time: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Metadata == nil { m.Metadata = &common.Metadata{} } if unmarshal, ok := interface{}(m.Metadata).(interface { UnmarshalVT([]byte) error }); ok { if err := unmarshal.UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } } else { if err := proto.Unmarshal(dAtA[iNdEx:postIndex], m.Metadata); err != nil { return err } } iNdEx = postIndex case 2: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Server", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } intStringLen := int(stringLen) if intStringLen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + intStringLen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Server = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Localtime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Localtime == nil { m.Localtime = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.Localtime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Remotetime", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } if m.Remotetime == nil { m.Remotetime = ×tamppb1.Timestamp{} } if err := (*timestamppb.Timestamp)(m.Remotetime).UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } func (m *TimeResponse) UnmarshalVT(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { preIndex := iNdEx var wire uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ wire |= uint64(b&0x7F) << shift if b < 0x80 { break } } fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { return fmt.Errorf("proto: TimeResponse: wiretype end group for non-group") } if fieldNum <= 0 { return fmt.Errorf("proto: TimeResponse: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType) } var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow } if iNdEx >= l { return io.ErrUnexpectedEOF } b := dAtA[iNdEx] iNdEx++ msglen |= int(b&0x7F) << shift if b < 0x80 { break } } if msglen < 0 { return protohelpers.ErrInvalidLength } postIndex := iNdEx + msglen if postIndex < 0 { return protohelpers.ErrInvalidLength } if postIndex > l { return io.ErrUnexpectedEOF } m.Messages = append(m.Messages, &Time{}) if err := m.Messages[len(m.Messages)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) if err != nil { return err } if (skippy < 0) || (iNdEx+skippy) < 0 { return protohelpers.ErrInvalidLength } if (iNdEx + skippy) > l { return io.ErrUnexpectedEOF } m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) iNdEx += skippy } } if iNdEx > l { return io.ErrUnexpectedEOF } return nil } ================================================ FILE: pkg/machinery/cel/build.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cel import ( "fmt" "github.com/google/cel-go/cel" "github.com/google/cel-go/common" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/types" ) // Builder allows building CEL expressions programmatically. type Builder struct { ast.ExprFactory env *cel.Env nextID int64 } // NewBuilder creates a new builder. func NewBuilder(env *cel.Env) *Builder { return &Builder{ ExprFactory: ast.NewExprFactory(), env: env, } } // NextID returns the next unique ID. func (b *Builder) NextID() int64 { b.nextID++ return b.nextID } // ToBooleanExpression converts the AST to a boolean expression. func (b *Builder) ToBooleanExpression(expr ast.Expr) (*Expression, error) { rawAst := ast.NewAST(expr, nil) pbAst, err := ast.ToProto(rawAst) if err != nil { return nil, err } celAst, err := cel.CheckedExprToAstWithSource(pbAst, common.NewTextSource("")) if err != nil { return nil, err } var issues *cel.Issues celAst, issues = b.env.Check(celAst) if issues != nil && issues.Err() != nil { return nil, issues.Err() } if outputType := celAst.OutputType(); !outputType.IsExactType(types.BoolType) { return nil, fmt.Errorf("expression output type is %s, expected bool", outputType) } return &Expression{ ast: celAst, }, nil } ================================================ FILE: pkg/machinery/cel/build_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cel_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" ) func TestBuildDiskExpression(t *testing.T) { t.Parallel() builder := cel.NewBuilder(celenv.DiskLocator()) expr := builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "rotational", ) out, err := builder.ToBooleanExpression(expr) require.NoError(t, err) assert.Equal(t, "disk.rotational", out.String()) } ================================================ FILE: pkg/machinery/cel/cel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cel provides helpers for working with CEL expressions. package cel import ( "encoding" "fmt" "math" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/siderolabs/protoenc" "go.yaml.in/yaml/v4" exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/proto" ) // Expression is a CEL expression that can be marshaled/unmarshaled as part of the resource. type Expression struct { ast *cel.Ast expression *string } // Check interfaces. var ( _ encoding.TextMarshaler = Expression{} _ encoding.TextUnmarshaler = (*Expression)(nil) _ yaml.IsZeroer = Expression{} ) // MustExpression panics if the expression cannot be parsed. func MustExpression(expr Expression, err error) Expression { if err != nil { panic(err) } return expr } // ParseBooleanExpression parses the expression and asserts the result to boolean. func ParseBooleanExpression(expression string, env *cel.Env) (Expression, error) { ast, err := parseExpressionWithOutputType(expression, env, types.BoolType) if err != nil { return Expression{}, err } return Expression{ast: ast}, nil } // ParseDoubleExpression parses the expression and asserts the result to float. func ParseDoubleExpression(expression string, env *cel.Env) (Expression, error) { ast, err := parseExpressionWithOutputType(expression, env, types.DoubleType) if err != nil { return Expression{}, err } return Expression{ast: ast}, nil } // parseExpressionWithOutputType parses the expression and asserts the result to boolean. func parseExpressionWithOutputType(expression string, env *cel.Env, expectedType *types.Type) (*cel.Ast, error) { ast, issues := env.Parse(expression) if issues != nil && issues.Err() != nil { return nil, issues.Err() } ast, issues = env.Check(ast) if issues != nil && issues.Err() != nil { return nil, issues.Err() } if outputType := ast.OutputType(); !outputType.IsExactType(expectedType) { return nil, fmt.Errorf("expression output type is %s, expected %s", outputType, expectedType) } return ast, nil } // Merge imlements merge.Mergeable. func (expr *Expression) Merge(v any) error { other, ok := v.(Expression) if !ok { return fmt.Errorf("unexpected type for expression merge %T", v) } expr.ast = other.ast expr.expression = other.expression return nil } // ParseBool parses the expression and asserts the result to boolean. // // ParseBoolean can be used after unmarshaling the expression from text. func (expr *Expression) ParseBool(env *cel.Env) error { if expr.ast != nil { return nil } if expr.expression == nil { panic("expression is not set") } var err error expr.ast, err = parseExpressionWithOutputType(*expr.expression, env, types.BoolType) return err } // ParseDouble parses the expression and asserts the result to float. // // ParseDouble can be used after unmarshaling the expression from text. func (expr *Expression) ParseDouble(env *cel.Env) error { if expr.ast != nil { return nil } if expr.expression == nil { panic("expression is not set") } var err error expr.ast, err = parseExpressionWithOutputType(*expr.expression, env, types.DoubleType) return err } // EvalBool evaluates the expression in the given environment. func (expr Expression) EvalBool(env *cel.Env, values map[string]any) (bool, error) { if err := expr.ParseBool(env); err != nil { return false, err } prog, err := env.Program(expr.ast) if err != nil { return false, err } out, _, err := prog.Eval(values) if err != nil { return false, err } val, ok := out.Value().(bool) if !ok { return false, fmt.Errorf("expression output type is %s, expected bool", out.Type()) } return val, nil } // EvalDouble evaluates the expression in the given environment. func (expr Expression) EvalDouble(env *cel.Env, values map[string]any) (float64, error) { if err := expr.ParseDouble(env); err != nil { return math.NaN(), err } prog, err := env.Program(expr.ast) if err != nil { return math.NaN(), err } out, _, err := prog.Eval(values) if err != nil { return math.NaN(), err } val, ok := out.Value().(float64) if !ok { return math.NaN(), fmt.Errorf("expression output type is %s, expected float64", out.Type()) } return val, nil } // MarshalText marshals the expression to text. func (expr Expression) MarshalText() ([]byte, error) { if expr.expression != nil { return []byte(*expr.expression), nil } if expr.ast != nil { repr, err := cel.AstToString(expr.ast) if err != nil { return nil, err } return []byte(repr), nil } return nil, nil } // UnmarshalText unmarshals the expression from text. func (expr *Expression) UnmarshalText(data []byte) error { if len(data) == 0 { return nil } expr.expression = new(string(data)) return nil } // String implements fmt.Stringer. func (expr Expression) String() string { b, err := expr.MarshalText() if err != nil { return "ERROR: " + err.Error() } return string(b) } // IsZero returns true if the expression is zero. func (expr Expression) IsZero() bool { return expr.ast == nil && expr.expression == nil } // MarshalProto marshals the expression to proto. func (expr Expression) MarshalProto() ([]byte, error) { if expr.ast == nil { return nil, nil } pbExpr, err := cel.AstToCheckedExpr(expr.ast) if err != nil { return nil, err } return proto.Marshal(pbExpr) } // UnmarshalProto unmarshals the expression from proto. func (expr *Expression) UnmarshalProto(data []byte) error { if len(data) == 0 { return nil } pbExpr := &exprpb.CheckedExpr{} if err := proto.Unmarshal(data, pbExpr); err != nil { return err } expr.ast = cel.CheckedExprToAst(pbExpr) return nil } func init() { protoenc.RegisterEncoderDecoder( func(v Expression) ([]byte, error) { return v.MarshalProto() }, func(slc []byte) (Expression, error) { var v Expression err := v.UnmarshalProto(slc) return v, err }, ) } ================================================ FILE: pkg/machinery/cel/cel_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cel_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" ) func TestCELMarshal(t *testing.T) { t.Parallel() env := celenv.DiskLocator() type yamlTest struct { Expr cel.Expression `yaml:"expr,omitempty"` } for _, test := range []struct { name string expression cel.Expression expectedYAML string }{ { name: "empty", expectedYAML: "{}\n", }, { name: "system disk", expression: cel.MustExpression(cel.ParseBooleanExpression("system_disk", env)), expectedYAML: "expr: system_disk\n", }, { name: "disk size and rotational", expression: cel.MustExpression(cel.ParseBooleanExpression("disk.size > 1000u && !disk.rotational", env)), expectedYAML: "expr: disk.size > 1000u && !disk.rotational\n", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() yamlTest := yamlTest{ Expr: test.expression, } yaml, err := yaml.Marshal(yamlTest) require.NoError(t, err) require.Equal(t, test.expectedYAML, string(yaml)) }) } } func TestCELEvalFromYAML(t *testing.T) { t.Parallel() env := celenv.DiskLocator() type yamlRaw struct { Expr string `yaml:"expr,omitempty"` } type yamlTest struct { Expr cel.Expression `yaml:"expr,omitempty"` } for _, test := range []struct { name string expression string expected bool }{ { name: "consts", expression: "1u * GiB < 2u * GiB", expected: true, }, { name: "vars", expression: "!system_disk", expected: false, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() yamlRaw := yamlRaw{ Expr: test.expression, } marshaled, err := yaml.Marshal(yamlRaw) require.NoError(t, err) var yamlTest yamlTest err = yaml.Unmarshal(marshaled, &yamlTest) require.NoError(t, err) val, err := yamlTest.Expr.EvalBool(env, map[string]any{ "system_disk": true, "disk": block.DiskSpec{ Size: 1024, }, }) require.NoError(t, err) assert.Equal(t, test.expected, val) }) } } ================================================ FILE: pkg/machinery/cel/celenv/celenv.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package celenv provides standard CEL environments to evaluate CEL expressions. package celenv import ( "net" "slices" "sync" "github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/common/types/traits" "github.com/ryanuber/go-glob" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/network" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // Empty is an empty CEL environment. var Empty = sync.OnceValue(func() *cel.Env { env, err := cel.NewEnv() if err != nil { panic(err) } return env }) // DiskLocator is a disk locator CEL environment. var DiskLocator = sync.OnceValue(func() *cel.Env { var diskSpec block.DiskSpec env, err := cel.NewEnv( slices.Concat( []cel.EnvOption{ cel.Types(&diskSpec), cel.Variable("disk", cel.ObjectType(string(diskSpec.ProtoReflect().Descriptor().FullName()))), cel.Variable("system_disk", types.BoolType), cel.Function("glob", // glob(pattern, string) cel.Overload("glob_string_string", []*cel.Type{cel.StringType, cel.StringType}, cel.BoolType, cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val { return types.Bool(glob.Glob(string(arg1.(types.String)), string(arg2.(types.String)))) }), ), ), }, celUnitMultipliersConstants(), )..., ) if err != nil { panic(err) } return env }) // VolumeLocator is a volume locator CEL environment. var VolumeLocator = sync.OnceValue(func() *cel.Env { var ( volumeSpec block.DiscoveredVolumeSpec diskSpec block.DiskSpec ) env, err := cel.NewEnv( slices.Concat( []cel.EnvOption{ cel.Types(&volumeSpec), cel.Types(&diskSpec), cel.Variable("volume", cel.ObjectType(string(volumeSpec.ProtoReflect().Descriptor().FullName()))), cel.Variable("disk", cel.ObjectType(string(diskSpec.ProtoReflect().Descriptor().FullName()))), }, celUnitMultipliersConstants(), )..., ) if err != nil { panic(err) } return env }) // OOMTrigger is a OOM Trigger Condition CEL environment. var OOMTrigger = sync.OnceValue(func() *cel.Env { env, err := cel.NewEnv( slices.Concat( []cel.EnvOption{ // root cgroup PSI memory metrics cel.Variable("memory_some_avg10", types.DoubleType), cel.Variable("memory_some_avg60", types.DoubleType), cel.Variable("memory_some_avg300", types.DoubleType), cel.Variable("memory_some_total", types.DoubleType), cel.Variable("memory_full_avg10", types.DoubleType), cel.Variable("memory_full_avg60", types.DoubleType), cel.Variable("memory_full_avg300", types.DoubleType), cel.Variable("memory_full_total", types.DoubleType), // root cgroup delta with last observation cel.Variable("d_memory_some_avg10", types.DoubleType), cel.Variable("d_memory_some_avg60", types.DoubleType), cel.Variable("d_memory_some_avg300", types.DoubleType), cel.Variable("d_memory_some_total", types.DoubleType), cel.Variable("d_memory_full_avg10", types.DoubleType), cel.Variable("d_memory_full_avg60", types.DoubleType), cel.Variable("d_memory_full_avg300", types.DoubleType), cel.Variable("d_memory_full_total", types.DoubleType), // per QoS class PSI memory metrics cel.Variable("qos_memory_some_avg10", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_some_avg60", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_some_avg300", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_some_total", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_full_avg10", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_full_avg60", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_full_avg300", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_full_total", types.NewMapType(types.IntType, types.DoubleType)), // per QoS class delta with last observation cel.Variable("d_qos_memory_some_avg10", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_some_avg60", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_some_avg300", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_some_total", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_full_avg10", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_full_avg60", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_full_avg300", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_full_total", types.NewMapType(types.IntType, types.DoubleType)), // per QoS memory usage absolute values cel.Variable("qos_memory_current", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_peak", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("qos_memory_max", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_current", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_peak", types.NewMapType(types.IntType, types.DoubleType)), cel.Variable("d_qos_memory_max", types.NewMapType(types.IntType, types.DoubleType)), // time since last OOM trigger cel.Variable("time_since_trigger", types.DurationType), cel.OptionalTypes(), // multiply_qos_vectors(qos_memory_some_avg_10, {Besteffort: 1.0, Burstable: 0.0, Guaranteed: 0.0, Podruntime: 1.0, System: 1.0}) -> double // // Multiplies the values of two QoS class maps and returns the sum. cel.Function("multiply_qos_vectors", cel.Overload("multiply_qos_vectors_ma_map_double", []*cel.Type{ cel.MapType(cel.DynType, cel.DoubleType), cel.MapType(cel.DynType, cel.DoubleType), }, cel.DoubleType, cel.BinaryBinding(func(lhs, rhs ref.Val) ref.Val { var result float64 lhsMap := lhs.(traits.Mapper) lhsIter := lhsMap.Iterator() rhsIndexer := rhs.(traits.Indexer) for lhsIter.HasNext() == types.True { key := lhsIter.Next() lValue := lhsMap.Get(key) rValue := rhsIndexer.Get(key) if types.IsError(rValue) { continue } result += float64(lValue.(types.Double)) * float64(rValue.(types.Double)) } return types.Double(result) }), ), ), }, celUnitMultipliersConstants(), celCgroupClassConstants(), )..., ) if err != nil { panic(err) } return env }) // OOMCgroupScoring is a OOM Cgroup Scoring CEL environment. var OOMCgroupScoring = sync.OnceValue(func() *cel.Env { env, err := cel.NewEnv( slices.Concat( []cel.EnvOption{ cel.Variable("memory_max", types.NewOptionalType(types.UintType)), cel.Variable("memory_current", types.NewOptionalType(types.UintType)), cel.Variable("memory_peak", types.NewOptionalType(types.UintType)), cel.Variable("path", types.StringType), cel.Variable("class", types.IntType), cel.OptionalTypes(), }, celUnitMultipliersConstants(), celCgroupClassConstants(), )..., ) if err != nil { panic(err) } return env }) // LinkLocator is a network link locator CEL environment. var LinkLocator = sync.OnceValue(func() *cel.Env { var linkSpec network.LinkStatusSpec env, err := cel.NewEnv( slices.Concat( []cel.EnvOption{ cel.Types(&linkSpec), cel.Variable("link", cel.ObjectType(string(linkSpec.ProtoReflect().Descriptor().FullName()))), cel.Function("glob", // glob(pattern, string) -> bool cel.Overload("glob_string_string", []*cel.Type{cel.StringType, cel.StringType}, cel.BoolType, cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val { return types.Bool(glob.Glob(string(arg1.(types.String)), string(arg2.(types.String)))) }), ), ), cel.Function("mac", // mac(bytes) -> string cel.Overload("mac_bytes", []*cel.Type{cel.BytesType}, cel.StringType, cel.UnaryBinding(func(arg ref.Val) ref.Val { return types.String(net.HardwareAddr([]byte(arg.(types.Bytes))).String()) }), ), ), }, )..., ) if err != nil { panic(err) } return env }) type unitMultiplier struct { unit string multiplier uint64 } var unitMultipliers = []unitMultiplier{ // IEC. {"KiB", 1024}, {"MiB", 1024 * 1024}, {"GiB", 1024 * 1024 * 1024}, {"TiB", 1024 * 1024 * 1024 * 1024}, {"PiB", 1024 * 1024 * 1024 * 1024 * 1024}, {"EiB", 1024 * 1024 * 1024 * 1024 * 1024 * 1024}, // Metric (used for disk sizes). {"kB", 1000}, {"MB", 1000 * 1000}, {"GB", 1000 * 1000 * 1000}, {"TB", 1000 * 1000 * 1000 * 1000}, {"PB", 1000 * 1000 * 1000 * 1000 * 1000}, {"EB", 1000 * 1000 * 1000 * 1000 * 1000 * 1000}, } func celUnitMultipliersConstants() []cel.EnvOption { return xslices.Map(unitMultipliers, func(um unitMultiplier) cel.EnvOption { return cel.Constant(um.unit, types.UintType, types.Uint(um.multiplier)) }) } func celCgroupClassConstants() []cel.EnvOption { return []cel.EnvOption{ cel.Constant("Besteffort", types.IntType, types.Int(runtime.QoSCgroupClassBesteffort)), cel.Constant("Burstable", types.IntType, types.Int(runtime.QoSCgroupClassBurstable)), cel.Constant("Guaranteed", types.IntType, types.Int(runtime.QoSCgroupClassGuaranteed)), cel.Constant("Podruntime", types.IntType, types.Int(runtime.QoSCgroupClassPodruntime)), cel.Constant("System", types.IntType, types.Int(runtime.QoSCgroupClassSystem)), } } ================================================ FILE: pkg/machinery/cel/celenv/celenv_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package celenv_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestDiskLocator(t *testing.T) { t.Parallel() env := celenv.DiskLocator() for _, test := range []struct { name string expression string }{ { name: "system disk", expression: "system_disk", }, { name: "disk size", expression: "disk.size > 1000u * GiB && !disk.rotational", }, { name: "glob", expression: "glob('sd[a-z]', disk.dev_path)", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := cel.ParseBooleanExpression(test.expression, env) require.NoError(t, err) }) } } func TestVolumeLocator(t *testing.T) { t.Parallel() env := celenv.VolumeLocator() for _, test := range []struct { name string expression string }{ { name: "by label", expression: "volume.label == 'EPHEMERAL'", }, { name: "by filesystem and size", expression: "volume.name == 'ext4' && volume.size > 1000u * TB", }, { name: "by filesystem and disk transport", expression: "volume.name == 'ext4' && disk.transport == 'nvme'", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := cel.ParseBooleanExpression(test.expression, env) require.NoError(t, err) }) } } func TestOOMCgroupScoring(t *testing.T) { t.Parallel() env := celenv.OOMCgroupScoring() for _, test := range []struct { name string expression string }{ { name: "example 1", expression: constants.DefaultOOMCgroupRankingExpression, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := cel.ParseDoubleExpression(test.expression, env) require.NoError(t, err) }) } } func TestOOMTrigger(t *testing.T) { t.Parallel() env := celenv.OOMTrigger() for _, test := range []struct { name string expression string }{ { name: "example 1", expression: constants.DefaultOOMTriggerExpression, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := cel.ParseBooleanExpression(test.expression, env) require.NoError(t, err) }) } } func TestLinkLocator(t *testing.T) { t.Parallel() env := celenv.LinkLocator() for _, test := range []struct { name string expression string }{ { name: "by driver", expression: `link.driver == "i1000e"`, }, { name: "by mac", expression: `mac(link.hardware_addr) == "00:1a:2b:3c:4d:5e"`, }, { name: "by mac partial", expression: `glob(mac(link.hardware_addr), "00:1a:2b:*")`, }, { name: "by altnames", expression: `"enx728c41bfd443" in link.alt_names`, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := cel.ParseBooleanExpression(test.expression, env) require.NoError(t, err) }) } } ================================================ FILE: pkg/machinery/client/basic_auth.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "encoding/base64" "google.golang.org/grpc" ) // BasicAuth implements the credentials.PerRPCCredentials interface and holds credentials for Basic Auth. type BasicAuth struct { auth string } // GetRequestMetadata implements credentials.PerGRPCCredentials. func (c BasicAuth) GetRequestMetadata(ctx context.Context, url ...string) (map[string]string, error) { enc := base64.StdEncoding.EncodeToString([]byte(c.auth)) return map[string]string{ "Authorization": "Basic " + enc, }, nil } // WithGRPCBasicAuth returns gRPC credentials for basic auth. func WithGRPCBasicAuth(username, password string) grpc.DialOption { return grpc.WithPerRPCCredentials(BasicAuth{ auth: username + ":" + password, }) } ================================================ FILE: pkg/machinery/client/client.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package client provides Talos API client. package client import ( "archive/tar" "bytes" "compress/gzip" "context" "errors" "fmt" "io" "time" cosiv1alpha1 "github.com/cosi-project/runtime/api/v1alpha1" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/protobuf/client" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/emptypb" clusterapi "github.com/siderolabs/talos/pkg/machinery/api/cluster" "github.com/siderolabs/talos/pkg/machinery/api/common" inspectapi "github.com/siderolabs/talos/pkg/machinery/api/inspect" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" storageapi "github.com/siderolabs/talos/pkg/machinery/api/storage" timeapi "github.com/siderolabs/talos/pkg/machinery/api/time" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // Client implements the proto.MachineServiceClient interface. It serves as the // concrete type with the required methods. type Client struct { options *Options conn *grpcConnectionWrapper MachineClient machineapi.MachineServiceClient TimeClient timeapi.TimeServiceClient ClusterClient clusterapi.ClusterServiceClient StorageClient storageapi.StorageServiceClient InspectClient inspectapi.InspectServiceClient ImageClient machineapi.ImageServiceClient DebugClient machineapi.DebugServiceClient LifecycleClient machineapi.LifecycleServiceClient COSI state.State Inspect *InspectClient } func (c *Client) resolveConfigContext() error { var ok bool if c.options.unixSocketPath != "" { return nil } if c.options.configContext != nil { return nil } if c.options.config == nil { if err := WithDefaultConfig()(c.options); err != nil { return fmt.Errorf("failed to load default config: %w", err) } } if c.options.contextOverrideSet { c.options.configContext, ok = c.options.config.Contexts[c.options.contextOverride] if !ok { return fmt.Errorf("context %q not found in config", c.options.contextOverride) } return nil } c.options.configContext, ok = c.options.config.Contexts[c.options.config.Context] if !ok { if c.options.config.Context == "" && len(c.options.config.Contexts) == 0 { return errors.New("talos config file is empty") } return fmt.Errorf("default context %q not found in config", c.options.config.Context) } return nil } // GetConfigContext returns resolved config context. func (c *Client) GetConfigContext() *clientconfig.Context { if err := c.resolveConfigContext(); err != nil { return nil } return c.options.configContext } // GetEndpoints returns the client's endpoints from the override set with WithEndpoints // or from the configuration. func (c *Client) GetEndpoints() []string { if c.options.unixSocketPath != "" { return []string{c.options.unixSocketPath} } if len(c.options.endpointsOverride) > 0 { return c.options.endpointsOverride } if c.options.configContext != nil { return c.options.configContext.Endpoints } if c.options.config != nil { if err := c.resolveConfigContext(); err != nil { return nil } return c.options.configContext.Endpoints } return nil } // GetClusterName returns the client's cluster name from the override set with WithClustername // or from the configuration. func (c *Client) GetClusterName() string { if c.options.clusterNameOverride != "" { return c.options.clusterNameOverride } if c.options.config != nil { if err := c.resolveConfigContext(); err != nil { return "" } if c.options.configContext != nil { return c.options.configContext.Cluster } } return "" } // New returns a new Client. func New(_ context.Context, opts ...OptionFunc) (c *Client, err error) { c = new(Client) c.options = new(Options) for _, opt := range opts { if err = opt(c.options); err != nil { return nil, err } } if len(c.GetEndpoints()) < 1 { return nil, errors.New("failed to determine endpoints") } c.conn, err = c.getConn() if err != nil { return nil, fmt.Errorf("failed to create client connection: %w", err) } c.MachineClient = machineapi.NewMachineServiceClient(c.conn) c.TimeClient = timeapi.NewTimeServiceClient(c.conn) c.ClusterClient = clusterapi.NewClusterServiceClient(c.conn) c.StorageClient = storageapi.NewStorageServiceClient(c.conn) c.InspectClient = inspectapi.NewInspectServiceClient(c.conn) c.ImageClient = machineapi.NewImageServiceClient(c.conn) c.DebugClient = machineapi.NewDebugServiceClient(c.conn) c.LifecycleClient = machineapi.NewLifecycleServiceClient(c.conn) c.Inspect = &InspectClient{c.InspectClient} c.COSI = state.WrapCore(client.NewAdapter(cosiv1alpha1.NewStateClient(c.conn))) return c, nil } // Close shuts down client protocol. func (c *Client) Close() error { return c.conn.Close() } // KubeconfigRaw returns K8s client config (kubeconfig). // // This method doesn't support multiplexing of the result: // * either client.WithNodes is not used, or it contains a single node in the list. func (c *Client) KubeconfigRaw(ctx context.Context) (io.ReadCloser, error) { stream, err := c.MachineClient.Kubeconfig(ctx, &emptypb.Empty{}) if err != nil { return nil, err } return ReadStream(stream) } func (c *Client) extractKubeconfig(r io.ReadCloser) ([]byte, error) { defer r.Close() //nolint:errcheck gzR, err := gzip.NewReader(r) if err != nil { return nil, err } // returned .tar.gz should contain only single file (kubeconfig) var kubeconfigBuf bytes.Buffer tar := tar.NewReader(gzR) for { _, err = tar.Next() if err == io.EOF { break } if err != nil { return nil, err } _, err = io.Copy(&kubeconfigBuf, tar) if err != nil { return nil, err } } if err = gzR.Close(); err != nil { return nil, err } return kubeconfigBuf.Bytes(), nil } // Kubeconfig returns K8s client config (kubeconfig). func (c *Client) Kubeconfig(ctx context.Context) ([]byte, error) { r, err := c.KubeconfigRaw(ctx) if err != nil { return nil, err } return c.extractKubeconfig(r) } // ApplyConfiguration implements proto.MachineServiceClient interface. func (c *Client) ApplyConfiguration(ctx context.Context, req *machineapi.ApplyConfigurationRequest, callOptions ...grpc.CallOption) (resp *machineapi.ApplyConfigurationResponse, err error) { resp, err = c.MachineClient.ApplyConfiguration(ctx, req, callOptions...) return FilterMessages(resp, err) } // Disks returns the list of block devices. func (c *Client) Disks(ctx context.Context, callOptions ...grpc.CallOption) (resp *storageapi.DisksResponse, err error) { resp, err = c.StorageClient.Disks(ctx, &emptypb.Empty{}, callOptions...) return FilterMessages(resp, err) } // Stats implements the proto.MachineServiceClient interface. func (c *Client) Stats(ctx context.Context, namespace string, driver common.ContainerDriver, callOptions ...grpc.CallOption) (resp *machineapi.StatsResponse, err error) { resp, err = c.MachineClient.Stats( ctx, &machineapi.StatsRequest{ Namespace: namespace, Driver: driver, }, callOptions..., ) return FilterMessages(resp, err) } // Containers implements the proto.MachineServiceClient interface. func (c *Client) Containers(ctx context.Context, namespace string, driver common.ContainerDriver, callOptions ...grpc.CallOption) (resp *machineapi.ContainersResponse, err error) { resp, err = c.MachineClient.Containers( ctx, &machineapi.ContainersRequest{ Namespace: namespace, Driver: driver, }, callOptions..., ) return FilterMessages(resp, err) } // Restart implements the proto.MachineServiceClient interface. func (c *Client) Restart(ctx context.Context, namespace string, driver common.ContainerDriver, id string, callOptions ...grpc.CallOption) (err error) { resp, err := c.MachineClient.Restart(ctx, &machineapi.RestartRequest{ Id: id, Namespace: namespace, Driver: driver, }) if err == nil { _, err = FilterMessages(resp, err) } return err } // Reset implements the proto.MachineServiceClient interface. func (c *Client) Reset(ctx context.Context, graceful, reboot bool) (err error) { resp, err := c.MachineClient.Reset(ctx, &machineapi.ResetRequest{Graceful: graceful, Reboot: reboot}) if err == nil { _, err = FilterMessages(resp, err) } return err } // ResetGeneric implements the proto.MachineServiceClient interface. func (c *Client) ResetGeneric(ctx context.Context, req *machineapi.ResetRequest) error { _, err := c.ResetGenericWithResponse(ctx, req) return err } // ResetGenericWithResponse resets the machine and returns the response. func (c *Client) ResetGenericWithResponse(ctx context.Context, req *machineapi.ResetRequest) (*machineapi.ResetResponse, error) { resp, err := c.MachineClient.Reset(ctx, req) if err == nil { _, err = FilterMessages(resp, err) } return resp, err } // RebootMode provides various mode through which the reboot process can be done. type RebootMode func(*machineapi.RebootRequest) // WithRebootMode sets the reboot mode for the Reboot API call. func WithRebootMode(mode machineapi.RebootRequest_Mode) func(req *machineapi.RebootRequest) { return func(req *machineapi.RebootRequest) { req.Mode = mode } } // WithPowerCycle option runs the Reboot fun in powercycle mode. func WithPowerCycle(req *machineapi.RebootRequest) { req.Mode = machineapi.RebootRequest_POWERCYCLE } // WithForce option runs the Reboot fun in force mode. func WithForce(req *machineapi.RebootRequest) { req.Mode = machineapi.RebootRequest_FORCE } // Reboot implements the proto.MachineServiceClient interface. func (c *Client) Reboot(ctx context.Context, opts ...RebootMode) error { _, err := c.RebootWithResponse(ctx, opts...) return err } // RebootWithResponse reboots the machine and returns the response. func (c *Client) RebootWithResponse(ctx context.Context, opts ...RebootMode) (*machineapi.RebootResponse, error) { var req machineapi.RebootRequest for _, opt := range opts { opt(&req) } resp, err := c.MachineClient.Reboot(ctx, &req) if err == nil { _, err = FilterMessages(resp, err) } return resp, err } // Rollback implements the proto.MachineServiceClient interface. func (c *Client) Rollback(ctx context.Context) (err error) { resp, err := c.MachineClient.Rollback(ctx, &machineapi.RollbackRequest{}) if err == nil { _, err = FilterMessages(resp, err) } return err } // Bootstrap implements the proto.MachineServiceClient interface. func (c *Client) Bootstrap(ctx context.Context, req *machineapi.BootstrapRequest) (err error) { resp, err := c.MachineClient.Bootstrap(ctx, req) if err == nil { _, err = FilterMessages(resp, err) } return err } // ShutdownOption provides shutdown API options. type ShutdownOption func(*machineapi.ShutdownRequest) // WithShutdownForce forces the shutdown even if the Kubernetes API is down. func WithShutdownForce(force bool) ShutdownOption { return func(req *machineapi.ShutdownRequest) { req.Force = force } } // Shutdown implements the proto.MachineServiceClient interface. func (c *Client) Shutdown(ctx context.Context, opts ...ShutdownOption) error { _, err := c.ShutdownWithResponse(ctx, opts...) return err } // ShutdownWithResponse shuts down the machine and returns the response. func (c *Client) ShutdownWithResponse(ctx context.Context, opts ...ShutdownOption) (*machineapi.ShutdownResponse, error) { var req machineapi.ShutdownRequest for _, opt := range opts { opt(&req) } resp, err := c.MachineClient.Shutdown(ctx, &req) if err == nil { _, err = FilterMessages(resp, err) } return resp, err } // Dmesg implements the proto.MachineServiceClient interface. func (c *Client) Dmesg(ctx context.Context, follow, tail bool) (machineapi.MachineService_DmesgClient, error) { return c.MachineClient.Dmesg(ctx, &machineapi.DmesgRequest{ Follow: follow, Tail: tail, }) } // Logs implements the proto.MachineServiceClient interface. func (c *Client) Logs(ctx context.Context, namespace string, driver common.ContainerDriver, id string, follow bool, tailLines int32) (stream machineapi.MachineService_LogsClient, err error) { stream, err = c.MachineClient.Logs(ctx, &machineapi.LogsRequest{ Namespace: namespace, Driver: driver, Id: id, Follow: follow, TailLines: tailLines, }) return stream, err } // LogsContainers implements the proto.MachineServiceClient interface. func (c *Client) LogsContainers(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.LogsContainersResponse, err error) { resp, err = c.MachineClient.LogsContainers( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // Version implements the proto.MachineServiceClient interface. func (c *Client) Version(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.VersionResponse, err error) { resp, err = c.MachineClient.Version( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // Processes implements the proto.MachineServiceClient interface. func (c *Client) Processes(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.ProcessesResponse, err error) { resp, err = c.MachineClient.Processes( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // Memory implements the proto.MachineServiceClient interface. func (c *Client) Memory(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.MemoryResponse, err error) { resp, err = c.MachineClient.Memory( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // Mounts implements the proto.MachineServiceClient interface. func (c *Client) Mounts(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.MountsResponse, err error) { resp, err = c.MachineClient.Mounts( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // LS implements the proto.MachineServiceClient interface. func (c *Client) LS(ctx context.Context, req *machineapi.ListRequest) (stream machineapi.MachineService_ListClient, err error) { return c.MachineClient.List(ctx, req) } // DiskUsage implements the proto.MachineServiceClient interface. func (c *Client) DiskUsage(ctx context.Context, req *machineapi.DiskUsageRequest) (stream machineapi.MachineService_DiskUsageClient, err error) { return c.MachineClient.DiskUsage(ctx, req) } // Copy implements the proto.MachineServiceClient interface. // // This method doesn't support multiplexing of the result: // * either client.WithNodes is not used, or it contains a single node in the list. func (c *Client) Copy(ctx context.Context, rootPath string) (io.ReadCloser, error) { stream, err := c.MachineClient.Copy(ctx, &machineapi.CopyRequest{ RootPath: rootPath, }) if err != nil { return nil, err } return ReadStream(stream) } // UpgradeOptions provides upgrade API options. type UpgradeOptions struct { Request machineapi.UpgradeRequest GRPCCallOptions []grpc.CallOption } // UpgradeOption provides upgrade API options. type UpgradeOption func(*UpgradeOptions) // WithUpgradeImage sets the image for the upgrade. func WithUpgradeImage(image string) UpgradeOption { return func(req *UpgradeOptions) { req.Request.Image = image } } // WithUpgradeRebootMode sets the reboot mode for the upgrade. func WithUpgradeRebootMode(mode machineapi.UpgradeRequest_RebootMode) UpgradeOption { return func(req *UpgradeOptions) { req.Request.RebootMode = mode } } // WithUpgradePreserve sets the preserve flag for the upgrade. func WithUpgradePreserve(preserve bool) UpgradeOption { return func(req *UpgradeOptions) { req.Request.Preserve = preserve } } // WithUpgradeStage sets the stage flag for the upgrade. func WithUpgradeStage(stage bool) UpgradeOption { return func(req *UpgradeOptions) { req.Request.Stage = stage } } // WithUpgradeForce sets the force flag for the upgrade. func WithUpgradeForce(force bool) UpgradeOption { return func(req *UpgradeOptions) { req.Request.Force = force } } // WithUpgradeGRPCCallOptions sets the gRPC call options for the upgrade. func WithUpgradeGRPCCallOptions(opts ...grpc.CallOption) UpgradeOption { return func(req *UpgradeOptions) { req.GRPCCallOptions = opts } } // Upgrade initiates a Talos upgrade and implements the proto.MachineServiceClient interface. // // Deprecated: use LifecycleClient instead. func (c *Client) Upgrade(ctx context.Context, image string, stage, force bool, callOptions ...grpc.CallOption) (*machineapi.UpgradeResponse, error) { return c.UpgradeWithOptions( ctx, WithUpgradeImage(image), WithUpgradeRebootMode(machineapi.UpgradeRequest_DEFAULT), WithUpgradeStage(stage), WithUpgradeForce(force), WithUpgradeGRPCCallOptions(callOptions...), ) } // UpgradeWithOptions initiates a Talos upgrade with the given options. // // Deprecated: use LifecycleClient instead. func (c *Client) UpgradeWithOptions(ctx context.Context, opts ...UpgradeOption) (*machineapi.UpgradeResponse, error) { var options UpgradeOptions for _, opt := range opts { opt(&options) } resp, err := c.MachineClient.Upgrade(ctx, &options.Request, options.GRPCCallOptions...) return FilterMessages(resp, err) } // ServiceList returns list of services with their state. func (c *Client) ServiceList(ctx context.Context, callOptions ...grpc.CallOption) (resp *machineapi.ServiceListResponse, err error) { resp, err = c.MachineClient.ServiceList( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // ServiceInfo provides info about a service and node metadata. type ServiceInfo struct { Metadata *common.Metadata Service *machineapi.ServiceInfo } // ServiceInfo returns info about a single service // // This is implemented via service list API, as we don't have many services // If service with given id is not registered, function returns nil. func (c *Client) ServiceInfo(ctx context.Context, id string, callOptions ...grpc.CallOption) (services []ServiceInfo, err error) { var resp *machineapi.ServiceListResponse resp, err = c.MachineClient.ServiceList( ctx, &emptypb.Empty{}, callOptions..., ) if err != nil { return services, err } resp, err = FilterMessages(resp, err) // FilterMessages might remove responses if they actually contain errors, // errors will be merged into `resp`. if resp == nil { return services, err } for _, resp := range resp.Messages { for _, svc := range resp.Services { if svc.Id == id { services = append(services, ServiceInfo{ Metadata: resp.Metadata, Service: svc, }) } } } return services, err } // ServiceStart starts a service. func (c *Client) ServiceStart(ctx context.Context, id string, callOptions ...grpc.CallOption) (resp *machineapi.ServiceStartResponse, err error) { resp, err = c.MachineClient.ServiceStart( ctx, &machineapi.ServiceStartRequest{Id: id}, callOptions..., ) return FilterMessages(resp, err) } // ServiceStop stops a service. func (c *Client) ServiceStop(ctx context.Context, id string, callOptions ...grpc.CallOption) (resp *machineapi.ServiceStopResponse, err error) { resp, err = c.MachineClient.ServiceStop( ctx, &machineapi.ServiceStopRequest{Id: id}, callOptions..., ) return FilterMessages(resp, err) } // ServiceRestart restarts a service. func (c *Client) ServiceRestart(ctx context.Context, id string, callOptions ...grpc.CallOption) (resp *machineapi.ServiceRestartResponse, err error) { resp, err = c.MachineClient.ServiceRestart( ctx, &machineapi.ServiceRestartRequest{Id: id}, callOptions..., ) return FilterMessages(resp, err) } // Time returns the time. func (c *Client) Time(ctx context.Context, callOptions ...grpc.CallOption) (resp *timeapi.TimeResponse, err error) { resp, err = c.TimeClient.Time( ctx, &emptypb.Empty{}, callOptions..., ) return FilterMessages(resp, err) } // TimeCheck returns the time compared to the specified ntp server. func (c *Client) TimeCheck(ctx context.Context, server string, callOptions ...grpc.CallOption) (resp *timeapi.TimeResponse, err error) { resp, err = c.TimeClient.TimeCheck( ctx, &timeapi.TimeRequest{Server: server}, callOptions..., ) return FilterMessages(resp, err) } // Read reads a file. // // This method doesn't support multiplexing of the result: // * either client.WithNodes is not used, or it contains a single node in the list. func (c *Client) Read(ctx context.Context, path string) (io.ReadCloser, error) { stream, err := c.MachineClient.Read(ctx, &machineapi.ReadRequest{Path: path}) if err != nil { return nil, err } return ReadStream(stream) } // ClusterHealthCheck runs a Talos cluster health check. func (c *Client) ClusterHealthCheck(ctx context.Context, waitTimeout time.Duration, clusterInfo *clusterapi.ClusterInfo) (clusterapi.ClusterService_HealthCheckClient, error) { return c.ClusterClient.HealthCheck(ctx, &clusterapi.HealthCheckRequest{ WaitTimeout: durationpb.New(waitTimeout), ClusterInfo: clusterInfo, }) } // EtcdRemoveMemberByID removes a node from etcd cluster by etcd member ID. func (c *Client) EtcdRemoveMemberByID(ctx context.Context, req *machineapi.EtcdRemoveMemberByIDRequest, callOptions ...grpc.CallOption) error { resp, err := c.MachineClient.EtcdRemoveMemberByID(ctx, req, callOptions...) if err == nil { _, err = FilterMessages(resp, err) } return err } // EtcdLeaveCluster makes node leave etcd cluster. func (c *Client) EtcdLeaveCluster(ctx context.Context, req *machineapi.EtcdLeaveClusterRequest, callOptions ...grpc.CallOption) error { resp, err := c.MachineClient.EtcdLeaveCluster(ctx, req, callOptions...) if err == nil { _, err = FilterMessages(resp, err) } return err } // EtcdForfeitLeadership makes node forfeit leadership in the etcd cluster. func (c *Client) EtcdForfeitLeadership(ctx context.Context, req *machineapi.EtcdForfeitLeadershipRequest, callOptions ...grpc.CallOption) (*machineapi.EtcdForfeitLeadershipResponse, error) { resp, err := c.MachineClient.EtcdForfeitLeadership(ctx, req, callOptions...) return FilterMessages(resp, err) } // EtcdMemberList lists etcd members of the cluster. func (c *Client) EtcdMemberList(ctx context.Context, req *machineapi.EtcdMemberListRequest, callOptions ...grpc.CallOption) (*machineapi.EtcdMemberListResponse, error) { resp, err := c.MachineClient.EtcdMemberList(ctx, req, callOptions...) return FilterMessages(resp, err) } // EtcdSnapshot receives a snapshot of the etcd from the node. // // This method doesn't support multiplexing of the result: // * either client.WithNodes is not used, or it contains a single node in the list. func (c *Client) EtcdSnapshot(ctx context.Context, req *machineapi.EtcdSnapshotRequest, callOptions ...grpc.CallOption) (io.ReadCloser, error) { stream, err := c.MachineClient.EtcdSnapshot(ctx, req, callOptions...) if err != nil { return nil, err } return ReadStream(stream) } // EtcdRecover uploads etcd snapshot created with EtcdSnapshot to the node. func (c *Client) EtcdRecover(ctx context.Context, snapshot io.Reader, callOptions ...grpc.CallOption) (*machineapi.EtcdRecoverResponse, error) { cli, err := c.MachineClient.EtcdRecover(ctx, callOptions...) if err != nil { return nil, err } buf := make([]byte, 4096) for { select { case <-ctx.Done(): return nil, ctx.Err() default: } var n int n, err = snapshot.Read(buf) if err != nil { if errors.Is(err, io.EOF) { break } return nil, fmt.Errorf("error reading snapshot: %w", err) } if err = cli.Send(&common.Data{ Bytes: buf[:n], }); err != nil { if errors.Is(err, io.EOF) { break } return nil, err } } resp, err := cli.CloseAndRecv() return FilterMessages(resp, err) } // EtcdAlarmList lists etcd alarms for the current node. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdAlarmList(ctx context.Context, opts ...grpc.CallOption) (*machineapi.EtcdAlarmListResponse, error) { resp, err := c.MachineClient.EtcdAlarmList(ctx, &emptypb.Empty{}, opts...) return FilterMessages(resp, err) } // EtcdAlarmDisarm disarms etcd alarms for the current node. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdAlarmDisarm(ctx context.Context, opts ...grpc.CallOption) (*machineapi.EtcdAlarmDisarmResponse, error) { resp, err := c.MachineClient.EtcdAlarmDisarm(ctx, &emptypb.Empty{}, opts...) return FilterMessages(resp, err) } // EtcdDefragment defragments etcd data directory for the current node. // // Defragmentation is a resource-heavy operation, so it should only run on a specific // node. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdDefragment(ctx context.Context, opts ...grpc.CallOption) (*machineapi.EtcdDefragmentResponse, error) { resp, err := c.MachineClient.EtcdDefragment(ctx, &emptypb.Empty{}, opts...) return FilterMessages(resp, err) } // EtcdStatus returns etcd status for the current member. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdStatus(ctx context.Context, opts ...grpc.CallOption) (*machineapi.EtcdStatusResponse, error) { resp, err := c.MachineClient.EtcdStatus(ctx, &emptypb.Empty{}, opts...) return FilterMessages(resp, err) } // EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdDowngradeCancel(ctx context.Context, opts ...grpc.CallOption) (*machineapi.EtcdDowngradeCancelResponse, error) { resp, err := c.MachineClient.EtcdDowngradeCancel(ctx, &emptypb.Empty{}, opts...) return FilterMessages(resp, err) } // EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdDowngradeEnable(ctx context.Context, req *machineapi.EtcdDowngradeEnableRequest, opts ...grpc.CallOption) (*machineapi.EtcdDowngradeEnableResponse, error) { resp, err := c.MachineClient.EtcdDowngradeEnable(ctx, req, opts...) return FilterMessages(resp, err) } // EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. // // This method is available only on control plane nodes (which run etcd). func (c *Client) EtcdDowngradeValidate(ctx context.Context, req *machineapi.EtcdDowngradeValidateRequest, opts ...grpc.CallOption) (*machineapi.EtcdDowngradeValidateResponse, error) { resp, err := c.MachineClient.EtcdDowngradeValidate(ctx, req, opts...) return FilterMessages(resp, err) } // GenerateClientConfiguration implements proto.MachineServiceClient interface. func (c *Client) GenerateClientConfiguration(ctx context.Context, req *machineapi.GenerateClientConfigurationRequest, callOptions ...grpc.CallOption) (resp *machineapi.GenerateClientConfigurationResponse, err error) { //nolint:lll resp, err = c.MachineClient.GenerateClientConfiguration(ctx, req, callOptions...) return FilterMessages(resp, err) } // PacketCapture implements the proto.MachineServiceClient interface. // // This method doesn't support multiplexing of the result: // * either client.WithNodes is not used, or it contains a single node in the list. func (c *Client) PacketCapture(ctx context.Context, req *machineapi.PacketCaptureRequest) (io.ReadCloser, error) { stream, err := c.MachineClient.PacketCapture(ctx, req) if err != nil { return nil, err } return ReadStream(stream) } // MachineStream is a common interface for streams returned by streaming APIs. type MachineStream interface { Recv() (*common.Data, error) grpc.ClientStream } // ReadStream converts grpc stream into io.Reader. func ReadStream(stream MachineStream) (io.ReadCloser, error) { pr, pw := io.Pipe() go func() { //nolint:errcheck defer pw.Close() for { data, err := stream.Recv() if err != nil { if errors.Is(err, io.EOF) || StatusCode(err) == codes.Canceled || StatusCode(err) == codes.DeadlineExceeded { return } pw.CloseWithError(err) return } if data.Bytes != nil { _, err = pw.Write(data.Bytes) if err != nil { return } } if data.Metadata != nil && data.Metadata.Error != "" { pw.CloseWithError(metaToErr(data.Metadata)) return } } }() return pr, stream.CloseSend() } func metaToErr(md *common.Metadata) error { if md.Status == nil { return errors.New(md.Error) } code := codes.Code(md.Status.Code) if code == codes.Canceled || code == codes.DeadlineExceeded { return nil } return status.FromProto(md.Status).Err() } // Netstat lists the network sockets on the current node. func (c *Client) Netstat(ctx context.Context, req *machineapi.NetstatRequest, callOptions ...grpc.CallOption) (*machineapi.NetstatResponse, error) { resp, err := c.MachineClient.Netstat( ctx, req, callOptions..., ) return FilterMessages(resp, err) } // MetaWrite writes a key to META storage. func (c *Client) MetaWrite(ctx context.Context, key uint8, value []byte, callOptions ...grpc.CallOption) error { resp, err := c.MachineClient.MetaWrite( ctx, &machineapi.MetaWriteRequest{ Key: uint32(key), Value: value, }, callOptions..., ) _, err = FilterMessages(resp, err) return err } // MetaDelete deletes a key from META storage. func (c *Client) MetaDelete(ctx context.Context, key uint8, callOptions ...grpc.CallOption) error { resp, err := c.MachineClient.MetaDelete( ctx, &machineapi.MetaDeleteRequest{ Key: uint32(key), }, callOptions..., ) _, err = FilterMessages(resp, err) return err } // ImageList lists images in the CRI. // // Deprecated: use ImageServiceClient instead. func (c *Client) ImageList(ctx context.Context, namespace common.ContainerdNamespace, callOptions ...grpc.CallOption) (machineapi.MachineService_ImageListClient, error) { return c.MachineClient.ImageList(ctx, &machineapi.ImageListRequest{ Namespace: namespace, }, callOptions..., ) } // ImagePull pre-pulls an image to the CRI. // // Deprecated: use ImageServiceClient instead. func (c *Client) ImagePull(ctx context.Context, namespace common.ContainerdNamespace, imageRef string, callOptions ...grpc.CallOption) error { resp, err := c.MachineClient.ImagePull(ctx, &machineapi.ImagePullRequest{ Namespace: namespace, Reference: imageRef, }, callOptions..., ) _, err = FilterMessages(resp, err) return err } // BlockDeviceWipe wipes a block device which is not used as a volume. func (c *Client) BlockDeviceWipe(ctx context.Context, req *storageapi.BlockDeviceWipeRequest, callOptions ...grpc.CallOption) error { resp, err := c.StorageClient.BlockDeviceWipe(ctx, req, callOptions...) _, err = FilterMessages(resp, err) return err } ================================================ FILE: pkg/machinery/client/client_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client_test import ( "context" "errors" "fmt" "runtime" "runtime/pprof" "time" "github.com/siderolabs/gen/ensure" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "github.com/siderolabs/talos/pkg/machinery/client" ) func ExampleNew() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() connPprof := pprof.Lookup("machinery/client/grpc.grpcConn") if connPprof == nil { panic(errors.New("profile machinery/client/grpc.grpcConn not found")) } fmt.Println("before:", connPprof.Count()) c := ensure.Value( client.New( ctx, client.WithUnixSocket("/path/to/socket"), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ), ) fmt.Println("after client.New:", connPprof.Count()) if err := c.Close(); err != nil { panic(err) } fmt.Println("after client.Close:", connPprof.Count()) c2 := ensure.Value( client.New( ctx, client.WithUnixSocket("/path/to/socket"), client.WithGRPCDialOptions( grpc.WithTransportCredentials(insecure.NewCredentials()), ), ), ) fmt.Println("after client.New 2:", connPprof.Count()) _ = c2 runtime.GC() fmt.Println("after gc:", connPprof.Count()) // Output: // before: 0 // after client.New: 1 // after client.Close: 0 // after client.New 2: 1 // after gc: 1 } ================================================ FILE: pkg/machinery/client/config/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "bytes" "encoding/base64" "errors" "fmt" "io" "io/fs" "os" "path/filepath" "github.com/siderolabs/crypto/x509" "go.yaml.in/yaml/v4" ) // Config represents the client configuration file (talosconfig). type Config struct { Context string `yaml:"context"` Contexts map[string]*Context `yaml:"contexts"` // path is the config Path config is read from. path Path } // NewConfig returns the client configuration file with a single context. func NewConfig(contextName string, endpoints []string, caCrt []byte, client *x509.PEMEncodedCertificateAndKey) *Config { return &Config{ Context: contextName, Contexts: map[string]*Context{ contextName: { Endpoints: endpoints, CA: base64.StdEncoding.EncodeToString(caCrt), Crt: base64.StdEncoding.EncodeToString(client.Crt), Key: base64.StdEncoding.EncodeToString(client.Key), }, }, } } func (c *Config) upgrade() { for _, ctx := range c.Contexts { ctx.upgrade() } } // Context represents the set of credentials required to talk to a target. type Context struct { DeprecatedTarget string `yaml:"target,omitempty"` // Field deprecated in favor of Endpoints Endpoints []string `yaml:"endpoints"` Nodes []string `yaml:"nodes,omitempty"` CA string `yaml:"ca,omitempty"` Crt string `yaml:"crt,omitempty"` Key string `yaml:"key,omitempty"` Auth Auth `yaml:"auth,omitempty"` Cluster string `yaml:"cluster,omitempty"` } // Auth may hold credentials for an authentication method such as Basic Auth. type Auth struct { Basic *Basic `yaml:"basic,omitempty"` SideroV1 *SideroV1 `yaml:"siderov1,omitempty"` } // Basic holds Basic Auth credentials. type Basic struct { Username string `yaml:"username"` Password string `yaml:"password"` } // SideroV1 holds information for SideroV1 API signature auth. type SideroV1 struct { Identity string `yaml:"identity"` } func (c *Context) upgrade() { if c.DeprecatedTarget != "" { c.Endpoints = append(c.Endpoints, c.DeprecatedTarget) c.DeprecatedTarget = "" } } // Open reads the config and initializes a Config struct. // If path is explicitly set, it will be used. // If not, the default path rules will be used. func Open(path string) (*Config, error) { var ( confPath Path err error ) if path != "" { // path is explicitly specified, ensure that is created and use it confPath = Path{ Path: path, WriteAllowed: true, } err = ensure(confPath.Path) if err != nil { return nil, err } } else { // path is implicit, get the first already existing & readable path or ensure that it is created confPath, err = firstValidPath() if err != nil { return nil, err } } config, err := fromFile(confPath.Path) if err != nil { return nil, err } config.path = confPath return config, nil } func fromFile(path string) (*Config, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() //nolint:errcheck return ReadFrom(file) } // FromString returns a config from a string. func FromString(p string) (c *Config, err error) { return ReadFrom(bytes.NewReader([]byte(p))) } // FromBytes returns a config from []byte. func FromBytes(b []byte) (c *Config, err error) { return ReadFrom(bytes.NewReader(b)) } // ReadFrom reads a config from io.Reader. func ReadFrom(r io.Reader) (c *Config, err error) { c = &Config{} if err = yaml.NewDecoder(r).Decode(c); err != nil { return c, err } c.upgrade() return c, err } // Save writes the config to disk. // If the path is not explicitly set, the default path rules will be used. func (c *Config) Save(path string) error { var err error if path != "" { // path is explicitly specified, use it c.path = Path{ Path: path, WriteAllowed: true, } } else if c.path.Path == "" { // path is implicit and is not set on config, get the first already existing & writable path or create it c.path, err = firstValidPath() if err != nil { return err } } if !c.path.WriteAllowed { return fmt.Errorf("not allowed to write to config: %s", c.path.Path) } configBytes, err := c.Bytes() if err != nil { return err } if err = os.MkdirAll(filepath.Dir(c.path.Path), 0o700); err != nil { return err } return os.WriteFile(c.path.Path, configBytes, 0o600) } // Bytes gets yaml encoded config data. func (c *Config) Bytes() ([]byte, error) { return yaml.Marshal(c) } // Path returns the filesystem path config was read from. func (c *Config) Path() Path { return c.path } // Rename describes context rename during merge. type Rename struct { From string To string } // String converts to "from" -> "to". func (r *Rename) String() string { return fmt.Sprintf("%q -> %q", r.From, r.To) } // Merge in additional contexts from another Config. // // Current context is overridden from passed in config. func (c *Config) Merge(cfg *Config) []Rename { if c.Contexts == nil { c.Contexts = map[string]*Context{} } mappedContexts := map[string]string{} var renames []Rename for name, ctx := range cfg.Contexts { mergedName := name if _, exists := c.Contexts[mergedName]; exists { for i := 1; ; i++ { mergedName = fmt.Sprintf("%s-%d", name, i) if _, exists := c.Contexts[mergedName]; !exists { break } } } mappedContexts[name] = mergedName if name != mergedName { renames = append(renames, Rename{name, mergedName}) } c.Contexts[mergedName] = ctx } if cfg.Context != "" { c.Context = mappedContexts[cfg.Context] } return renames } func ensure(path string) error { if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) { config := &Config{ Context: "", Contexts: map[string]*Context{}, } return config.Save(path) } return nil } ================================================ FILE: pkg/machinery/client/config/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( "testing" "github.com/stretchr/testify/assert" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) func TestConfigMerge(t *testing.T) { context1 := &clientconfig.Context{} context2 := &clientconfig.Context{} for _, tt := range []struct { name string config *clientconfig.Config configToMerge *clientconfig.Config expectedContext string expectedContexts map[string]*clientconfig.Context }{ { name: "IntoEmpty", config: &clientconfig.Config{}, configToMerge: &clientconfig.Config{ Context: "foo", Contexts: map[string]*clientconfig.Context{ "foo": context1, }, }, expectedContext: "foo", expectedContexts: map[string]*clientconfig.Context{ "foo": context1, }, }, { name: "NoConflict", config: &clientconfig.Config{ Context: "bar", Contexts: map[string]*clientconfig.Context{ "bar": context2, }, }, configToMerge: &clientconfig.Config{ Context: "", Contexts: map[string]*clientconfig.Context{ "foo": context1, }, }, expectedContext: "bar", expectedContexts: map[string]*clientconfig.Context{ "foo": context1, "bar": context2, }, }, { name: "WithRename", config: &clientconfig.Config{ Context: "bar", Contexts: map[string]*clientconfig.Context{ "bar": context2, }, }, configToMerge: &clientconfig.Config{ Context: "bar", Contexts: map[string]*clientconfig.Context{ "bar": context1, }, }, expectedContext: "bar-1", expectedContexts: map[string]*clientconfig.Context{ "bar-1": context1, "bar": context2, }, }, } { t.Run(tt.name, func(t *testing.T) { c := tt.config c.Merge(tt.configToMerge) assert.Equal(t, tt.expectedContext, c.Context) assert.Equal(t, tt.expectedContexts, c.Contexts) }) } } ================================================ FILE: pkg/machinery/client/config/path.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "errors" "os" "path/filepath" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Path represents a path to a configuration file. type Path struct { // Path is the filesystem path of the config. Path string // WriteAllowed is true if the path is allowed to be written. WriteAllowed bool } // GetTalosDirectory returns path to Talos directory ($TALOS_HOME or ~/.talos). func GetTalosDirectory() (string, error) { if path, ok := os.LookupEnv(constants.TalosHomeEnvVar); ok && filepath.IsAbs(path) { return path, nil } home, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(home, constants.TalosDir), nil } // GetDefaultPaths returns the list of config file paths in order of priority. func GetDefaultPaths() ([]Path, error) { talosDir, err := GetTalosDirectory() if err != nil { return nil, err } result := make([]Path, 0, 3) if path, ok := os.LookupEnv(constants.TalosConfigEnvVar); ok { result = append(result, Path{ Path: path, WriteAllowed: true, }) } result = append( result, Path{ Path: filepath.Join(talosDir, constants.TalosconfigFilename), WriteAllowed: true, }, Path{ Path: filepath.Join(constants.ServiceAccountMountPath, constants.TalosconfigFilename), WriteAllowed: false, }, ) return result, nil } // CustomSideroV1KeysDirPath returns the custom SideroV1 auth keys directory path if it's provided as command line flag or with environment variable. func CustomSideroV1KeysDirPath(customPath string) string { if path, ok := os.LookupEnv(constants.SideroV1KeysDirEnvVar); ok { return path } if customPath != "" { return customPath } home, err := os.UserHomeDir() if err != nil { return "" } return filepath.Join(home, constants.TalosDir, constants.SideroV1KeysDir) } // firstValidPath iterates over the default paths and returns the first one that exists and readable. // If no path is found, it will ensure that the first path that allows writes is created and returned. // If no path is found that is writable, an error is returned. func firstValidPath() (Path, error) { paths, err := GetDefaultPaths() if err != nil { return Path{}, err } var firstWriteAllowed Path for _, path := range paths { _, err = os.Stat(path.Path) if err == nil { return path, nil } if firstWriteAllowed.Path == "" && path.WriteAllowed { firstWriteAllowed = path } } if firstWriteAllowed.Path == "" { return Path{}, errors.New("no valid config paths found") } err = ensure(firstWriteAllowed.Path) if err != nil { return Path{}, err } return firstWriteAllowed, nil } ================================================ FILE: pkg/machinery/client/connection.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "crypto/tls" "crypto/x509" "encoding/base64" "errors" "fmt" "net" "net/url" "slices" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-api-signature/pkg/client/interceptor" "github.com/siderolabs/go-api-signature/pkg/pgp/client" "google.golang.org/grpc" "google.golang.org/grpc/credentials" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/client/resolver" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Conn returns underlying client connection. func (c *Client) Conn() *grpc.ClientConn { return c.conn.ClientConn } // getConn creates new gRPC connection. func (c *Client) getConn(opts ...grpc.DialOption) (*grpcConnectionWrapper, error) { endpoints := c.GetEndpoints() target := c.getTarget( resolver.EnsureEndpointsHavePorts( reduceURLsToAddresses(endpoints), constants.ApidPort), ) dialOpts := slices.Concat( []grpc.DialOption{ grpc.WithDefaultCallOptions( // enable compression by default // TODO: enable compression for Talos 1.7+ // grpc.UseCompressor(gzip.Name), grpc.MaxCallRecvMsgSize(constants.GRPCMaxMessageSize), ), grpc.WithSharedWriteBuffer(true), }, c.options.grpcDialOptions, opts, ) if c.options.unixSocketPath != "" { dialOpts = append(dialOpts, grpc.WithNoProxy(), ) conn, err := grpc.NewClient(target, dialOpts...) return newGRPCConnectionWrapper(c.GetClusterName(), conn), err } tlsConfig := c.options.tlsConfig if tlsConfig != nil { return c.makeConnection(target, credentials.NewTLS(tlsConfig), dialOpts) } if err := c.resolveConfigContext(); err != nil { return nil, fmt.Errorf("failed to resolve configuration context: %w", err) } basicAuth := c.options.configContext.Auth.Basic if basicAuth != nil { dialOpts = append(dialOpts, WithGRPCBasicAuth(basicAuth.Username, basicAuth.Password)) } sideroV1 := c.options.configContext.Auth.SideroV1 if sideroV1 != nil { var contextName string if c.options.config != nil { contextName = c.options.config.Context } if c.options.contextOverrideSet { contextName = c.options.contextOverride } authInterceptor := interceptor.New(interceptor.Options{ UserKeyProvider: getKeyProvider(c.options.sideroV1KeysDir), ContextName: contextName, Identity: sideroV1.Identity, ClientName: "Talos", ServiceAccountBase64: c.options.serviceAccountBase64, }) dialOpts = append(dialOpts, grpc.WithUnaryInterceptor(authInterceptor.Unary()), grpc.WithStreamInterceptor(authInterceptor.Stream()), ) } creds, err := buildCredentials(c.options.configContext, endpoints) if err != nil { return nil, err } return c.makeConnection(target, creds, dialOpts) } func getKeyProvider(customKeysDir string) *client.KeyProvider { if customKeysDir != "" { return client.NewKeyProviderWithFallback("talos/keys", customKeysDir, "", true) } talosDir, err := clientconfig.GetTalosDirectory() if err != nil { // TODO: start failing instead of falling back to XDG data directory if we can't resolve Talos directory return client.NewKeyProvider("talos/keys") } return client.NewKeyProviderWithFallback("talos/keys", talosDir, "keys", true) } func buildTLSConfig(configContext *clientconfig.Context) (*tls.Config, error) { tlsConfig := &tls.Config{} caBytes, err := getCA(configContext) if err != nil { return nil, fmt.Errorf("failed to get CA: %w", err) } if len(caBytes) > 0 { tlsConfig.RootCAs = x509.NewCertPool() if ok := tlsConfig.RootCAs.AppendCertsFromPEM(caBytes); !ok { return nil, errors.New("failed to append CA certificate to RootCAs pool") } } crt, err := CertificateFromConfigContext(configContext) if err != nil { return nil, fmt.Errorf("failed to acquire credentials: %w", err) } if crt != nil { tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert tlsConfig.Certificates = append(tlsConfig.Certificates, *crt) } return tlsConfig, nil } func (c *Client) makeConnection(target string, creds credentials.TransportCredentials, dialOpts []grpc.DialOption) (*grpcConnectionWrapper, error) { dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds), grpc.WithInitialWindowSize(65535*32), grpc.WithInitialConnWindowSize(65535*16)) conn, err := grpc.NewClient(target, dialOpts...) return newGRPCConnectionWrapper(c.GetClusterName(), conn), err } func (c *Client) getTarget(endpoints []string) string { switch { case c.options.unixSocketPath != "": return fmt.Sprintf("unix:///%s", c.options.unixSocketPath) case len(endpoints) > 1: return fmt.Sprintf("%s:///%s", resolver.RoundRobinResolverScheme, strings.Join(endpoints, ",")) default: // NB: we use the `dns` scheme here in order to handle fancier situations // when there is a single endpoint. // Such possibilities include SRV records, multiple IPs from A and/or AAAA // records, and descriptive TXT records which include things like load // balancer specs. return fmt.Sprintf("dns:///%s", endpoints[0]) } } func getCA(context *clientconfig.Context) ([]byte, error) { if context.CA == "" { return nil, nil } caBytes, err := base64.StdEncoding.DecodeString(context.CA) if err != nil { return nil, fmt.Errorf("error decoding CA: %w", err) } return caBytes, err } // CertificateFromConfigContext constructs the client Credentials from the given configuration Context. func CertificateFromConfigContext(context *clientconfig.Context) (*tls.Certificate, error) { if context.Crt == "" && context.Key == "" { return nil, nil } crtBytes, err := base64.StdEncoding.DecodeString(context.Crt) if err != nil { return nil, fmt.Errorf("error decoding certificate: %w", err) } keyBytes, err := base64.StdEncoding.DecodeString(context.Key) if err != nil { return nil, fmt.Errorf("error decoding key: %w", err) } crt, err := tls.X509KeyPair(crtBytes, keyBytes) if err != nil { return nil, fmt.Errorf("could not load client key pair: %s", err) } return &crt, nil } func reduceURLsToAddresses(endpoints []string) []string { return xslices.Map(endpoints, func(endpoint string) string { u, err := url.Parse(endpoint) if err != nil { return endpoint } if u.Scheme == "https" && u.Port() == "" { return net.JoinHostPort(u.Hostname(), "443") } if u.Scheme != "" { if u.Port() != "" { return net.JoinHostPort(u.Hostname(), u.Port()) } if u.Opaque == "" { return u.Host } } return endpoint }) } ================================================ FILE: pkg/machinery/client/connection_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client_test import ( "crypto/tls" "crypto/x509" "encoding/base64" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) func TestReduceURLsToAddresses(t *testing.T) { endpoints := []string{ "123.123.123.123", "exammple.com:111", "234.234.234.234:4000", "https://111.111.222.222:444", "localhost", "localhost:890", "https://[42a1:cfa:5458:3967:e2ce:afaa:6194:12f]:40000", "https://localhost:890", "2001:db8:0:0:0:ff00:42:8329", "https://[be4d:c25e:aca0:9366:68b7:c84:a23b:f7be]", "https://www.somecompany.com", "www.company.com", "[2001:db8:4006:812::200e]:8080", "grpc://222.22.2.1", "grpc://[794b:389:73cb:76a2:59de:62fd:ee38:7c]:111", } expected := []string{ "123.123.123.123", "exammple.com:111", "234.234.234.234:4000", "111.111.222.222:444", "localhost", "localhost:890", "[42a1:cfa:5458:3967:e2ce:afaa:6194:12f]:40000", "localhost:890", "2001:db8:0:0:0:ff00:42:8329", "[be4d:c25e:aca0:9366:68b7:c84:a23b:f7be]:443", "www.somecompany.com:443", "www.company.com", "[2001:db8:4006:812::200e]:8080", "222.22.2.1", "[794b:389:73cb:76a2:59de:62fd:ee38:7c]:111", } actual := client.ReduceURLsToAddresses(endpoints) assert.Equal(t, expected, actual) } func TestBuildTLSConfig(t *testing.T) { //nolint:lll ca := `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEFtbGVURnRuRVY3b3NHYTJFSU9RVUJNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1qQTRNVEl4T0RNeE1EZGFGdzB6TWpBNE1Ea3hPRE14TURkYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBVGZ3RjFMQjVwVjg2cGw4cHN2aS93R2dWWmkvTm5NME8wYUZNCjBoenZZdzZqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVWTRhSGg3UnJxRnVObFNydAo4bXY4ZHduUjRKQXdCUVlESzJWd0EwRUFTaE5jYURXMGwrU24xYSt5c21Sd2M2NGlBa3Y5dUlZNGdXU0t3RWJ4CnpYQlR3SkZWcmNPWlZNNS9pM0Y0UjFWZVkzM3QwdFBQMFBGZVF5MVRWTDlVQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==` caBytes, err := base64.StdEncoding.DecodeString(ca) assert.Nil(t, err) expectedRootCAs := x509.NewCertPool() expectedRootCAs.AppendCertsFromPEM(caBytes) //nolint:lll crt := `LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJNekNCNXFBREFnRUNBaEVBZ1BscnFYWUtDeVNHRkxmazVVK2JQekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdPREV5TVRnek1UQTNXaGNOTXpJd09EQTVNVGd6TVRBM1dqQVRNUkV3RHdZRApWUVFLRXdodmN6cGhaRzFwYmpBcU1BVUdBeXRsY0FNaEFKblVxM1V1TzNTaGg4YW50eEZzNGJnZDlXeGRtcit6CmZURkxIcGpQVWlUaG8xSXdVREFPQmdOVkhROEJBZjhFQkFNQ0I0QXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUgKQXdFR0NDc0dBUVVGQndNQ01COEdBMVVkSXdRWU1CYUFGR09HaDRlMGE2aGJqWlVxN2ZKci9IY0owZUNRTUFVRwpBeXRsY0FOQkFNaW1wdnlxa0RHWDhROFErMTBtVWowYXJoQUpqdHl4OHErQll2QnlWOThxYyt3VldnYlFBc3FmClV3Sy9lN2ZLak1qMi9kRUZqOCs2SGZpOVJMTE5udzQ9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K` key := `LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJQ3FTdHpMTTNzaHNqMlZld2dXaVBPaDJUT01uUmM3cmNyRkczTGhNaFdkQQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K` keyBytes, err := base64.StdEncoding.DecodeString(key) assert.Nil(t, err) crtBytes, err := base64.StdEncoding.DecodeString(crt) assert.Nil(t, err) expectedCert, err := tls.X509KeyPair(crtBytes, keyBytes) assert.Nil(t, err) expectedCerts := []tls.Certificate{expectedCert} t.Run("Returns default tls config for empty config context.", func(t *testing.T) { // given configContext := clientconfig.Context{} // when tlsConfig, err := client.BuildTLSConfig(&configContext) assert.Nil(t, err) // then expected := &tls.Config{} assert.Equal(t, expected, tlsConfig) }) t.Run("Returns tls config with CA for config context with CA.", func(t *testing.T) { // given configContext := clientconfig.Context{ CA: ca, } // when tlsConfig, err := client.BuildTLSConfig(&configContext) assert.Nil(t, err) // then assert.True(t, expectedRootCAs.Equal(tlsConfig.RootCAs)) assert.Len(t, tlsConfig.Certificates, 0) }) t.Run("Returns tls config with Certificate for config context with Crt and Key.", func(t *testing.T) { // given configContext := clientconfig.Context{ Crt: crt, Key: key, } // when tlsConfig, err := client.BuildTLSConfig(&configContext) assert.Nil(t, err) // then assert.Equal(t, expectedCerts, tlsConfig.Certificates) assert.Equal(t, tls.RequireAndVerifyClientCert, tlsConfig.ClientAuth) assert.Nil(t, tlsConfig.RootCAs) }) t.Run("Returns tls config with CA and Certificate for config context with CA, Crt and Key.", func(t *testing.T) { // given configContext := clientconfig.Context{ CA: ca, Crt: crt, Key: key, } // when tlsConfig, err := client.BuildTLSConfig(&configContext) assert.Nil(t, err) // then assert.True(t, expectedRootCAs.Equal(tlsConfig.RootCAs)) assert.Equal(t, expectedCerts, tlsConfig.Certificates) assert.Equal(t, tls.RequireAndVerifyClientCert, tlsConfig.ClientAuth) }) } ================================================ FILE: pkg/machinery/client/context.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "google.golang.org/grpc/metadata" ) // WithNodes wraps the context with metadata to send request to a set of nodes. // // Responses from all nodes are aggregated by the `apid` service and sent back as a single response. func WithNodes(ctx context.Context, nodes ...string) context.Context { md, _ := metadata.FromOutgoingContext(ctx) // overwrite any previous nodes in the context metadata with new value md = md.Copy() md.Delete("node") md.Set("nodes", nodes...) return metadata.NewOutgoingContext(ctx, md) } // WithNode wraps the context with metadata to send request to a single node. // // Request will be proxied by the endpoint to the specified node without any further processing. func WithNode(ctx context.Context, node string) context.Context { md, _ := metadata.FromOutgoingContext(ctx) // overwrite any previous nodes in the context metadata with new value md = md.Copy() md.Delete("nodes") md.Set("node", node) return metadata.NewOutgoingContext(ctx, md) } ================================================ FILE: pkg/machinery/client/dialer/dialer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package dialer ... package dialer import ( "context" "fmt" "net" "net/url" ) // DialUnix is used as a parameter for 'grpc.WithContextDialer' to bypass the // default dialer of gRPC to ensure that proxy vars are not used. func DialUnix() func(context.Context, string) (net.Conn, error) { return func(ctx context.Context, addr string) (net.Conn, error) { u, err := url.Parse(addr) if err != nil { return nil, err } if u.Scheme != "unix" { return nil, fmt.Errorf("invalid scheme: %q", u.Scheme) } return (&net.Dialer{}).DialContext(ctx, u.Scheme, u.Path) } } ================================================ FILE: pkg/machinery/client/dialer/dialer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dialer_test import ( "context" "net" "testing" "time" "github.com/siderolabs/talos/pkg/machinery/client/dialer" ) func TestDynamicProxyDialer_SOCKS5(t *testing.T) { t.Setenv("HTTPS_PROXY", "socks5://localhost:12345") ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() // Expect a connection error because the port is not open _, err := dialer.DynamicProxyDialer(ctx, "example.com:443") if err == nil { t.Fatal("expected a SOCKS5 connection error, but no error received") } if _, ok := err.(net.Error); !ok { t.Fatalf("unexpected error: %v", err) } } ================================================ FILE: pkg/machinery/client/dialer/proxy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package dialer import ( "bufio" "context" "crypto/tls" "encoding/base64" "fmt" "io" "net" "net/http" "net/http/httputil" "net/url" "golang.org/x/net/http/httpproxy" "golang.org/x/net/proxy" "google.golang.org/grpc" ) /* * Copyright 2023 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. * */ const grpcUA = "grpc-go/" + grpc.Version // DynamicProxyDialer is a fork of grpc standard dialer which supports dynamic resolving of proxy settings // on each request (vs. caching it once per process). // // DynamicProxyDialer assumes that the address is using 'tcp' network. func DynamicProxyDialer(ctx context.Context, addr string) (net.Conn, error) { return DynamicProxyDialerWithTLSConfig(func() *tls.Config { return &tls.Config{} })(ctx, addr) } // DynamicProxyDialerWithTLSConfig is like DynamicProxyDialer but allows // specifying custom TLS config to be used when connecting to HTTPS proxy. func DynamicProxyDialerWithTLSConfig(tlsConfigFunc func() *tls.Config) func(ctx context.Context, addr string) (net.Conn, error) { return func(ctx context.Context, addr string) (net.Conn, error) { newAddr := addr proxyURL, err := mapAddress(addr) if err != nil { return nil, err } if proxyURL != nil { newAddr = proxyURL.Host } var conn net.Conn if proxyURL != nil && proxyURL.Scheme == "https" { conn, err = (&tls.Dialer{ NetDialer: NetDialerWithTCPKeepalive(), Config: tlsConfigFunc(), }).DialContext(ctx, "tcp", proxyURL.Host) } else { conn, err = NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", newAddr) } if err != nil { return nil, err } if proxyURL == nil { // proxy is disabled if proxyURL is nil. return conn, err } // Support SOCKS5 if proxyURL.Scheme == "socks5" { socks5Dialer, err := proxySocksFromURL(proxyURL) if err != nil { return nil, err } return socks5Dialer.Dial("tcp", addr) } // Standard HTTP/HTTPS proxy return doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA) } } func proxySocksFromURL(u *url.URL) (proxy.Dialer, error) { var auth *proxy.Auth if u.User != nil { password, _ := u.User.Password() auth = &proxy.Auth{ User: u.User.Username(), Password: password, } } return proxy.SOCKS5("tcp", u.Host, auth, NetDialerWithTCPKeepalive()) } const proxyAuthHeaderKey = "Proxy-Authorization" func mapAddress(address string) (*url.URL, error) { req := &http.Request{ URL: &url.URL{ Scheme: "https", Host: address, }, } return httpproxy.FromEnvironment().ProxyFunc()(req.URL) } // To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader. // It's possible that this reader reads more than what's need for the response and stores // those bytes in the buffer. // bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the // bytes in the buffer. type bufConn struct { net.Conn r io.Reader } func (c *bufConn) Read(b []byte) (int, error) { return c.r.Read(b) } func basicAuth(username, password string) string { auth := username + ":" + password return base64.StdEncoding.EncodeToString([]byte(auth)) } func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr string, proxyURL *url.URL, grpcUA string) (_ net.Conn, err error) { defer func() { if err != nil { conn.Close() //nolint:errcheck } }() req := &http.Request{ Method: http.MethodConnect, URL: &url.URL{Host: backendAddr}, Header: map[string][]string{"User-Agent": {grpcUA}}, } if t := proxyURL.User; t != nil { u := t.Username() p, _ := t.Password() req.Header.Add(proxyAuthHeaderKey, "Basic "+basicAuth(u, p)) } if err := sendHTTPRequest(ctx, req, conn); err != nil { return nil, fmt.Errorf("failed to write the HTTP request: %v", err) } r := bufio.NewReader(conn) resp, err := http.ReadResponse(r, req) if err != nil { return nil, fmt.Errorf("reading server HTTP response: %v", err) } defer resp.Body.Close() //nolint:errcheck if resp.StatusCode != http.StatusOK { dump, err := httputil.DumpResponse(resp, true) if err != nil { return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status) } return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) } // The buffer could contain extra bytes from the target server, so we can't // discard it. However, in many cases where the server waits for the client // to send the first message (e.g. when TLS is being used), the buffer will // be empty, so we can avoid the overhead of reading through this buffer. if r.Buffered() != 0 { return &bufConn{Conn: conn, r: r}, nil } return conn, nil } func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { req = req.WithContext(ctx) if err := req.Write(conn); err != nil { return fmt.Errorf("failed to write the HTTP request: %v", err) } return nil } // NetDialerWithTCPKeepalive returns a net.Dialer that enables TCP keepalives on // the underlying connection with OS default values for keepalive parameters. func NetDialerWithTCPKeepalive() *net.Dialer { return &net.Dialer{ KeepAliveConfig: net.KeepAliveConfig{ Enable: true, Idle: -1, Count: -1, Interval: -1, }, } } ================================================ FILE: pkg/machinery/client/events.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "errors" "fmt" "io" "log" "sync" "time" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EventNotSupportedError is returned from the event decoder when we encounter an unknown event. type EventNotSupportedError struct { TypeURL string } // Error implements the error interface. func (e EventNotSupportedError) Error() string { return fmt.Sprintf("event is not supported: %s", e.TypeURL) } // EventsOptionFunc defines the options for the Events API. type EventsOptionFunc func(opts *machineapi.EventsRequest) // WithTailEvents sets up Events API to return specified number of past events. // // If number is negative, all the available past events are returned. func WithTailEvents(number int32) EventsOptionFunc { return func(opts *machineapi.EventsRequest) { opts.TailEvents = number } } // WithTailID sets up Events API to return events with ID > TailID. func WithTailID(id string) EventsOptionFunc { return func(opts *machineapi.EventsRequest) { opts.TailId = id } } // WithTailDuration sets up Watcher to return events with timestamp >= (now - tailDuration). func WithTailDuration(dur time.Duration) EventsOptionFunc { return func(opts *machineapi.EventsRequest) { opts.TailSeconds = int32(dur / time.Second) } } // WithActorID sets up Watcher to return events with the specified actor ID. func WithActorID(actorID string) EventsOptionFunc { return func(opts *machineapi.EventsRequest) { opts.WithActorId = actorID } } // Events implements the proto.OSClient interface. func (c *Client) Events(ctx context.Context, opts ...EventsOptionFunc) (stream machineapi.MachineService_EventsClient, err error) { var req machineapi.EventsRequest for _, opt := range opts { opt(&req) } return c.MachineClient.Events(ctx, &req) } // Event as received from the API. type Event struct { Node string TypeURL string ID string ActorID string Payload proto.Message } // EventsWatch wraps Events by providing more simple interface. // //nolint:gocyclo func (c *Client) EventsWatch(ctx context.Context, watchFunc func(<-chan Event), opts ...EventsOptionFunc) error { stream, err := c.Events(ctx, opts...) if err != nil { return fmt.Errorf("error fetching events: %s", err) } if err = stream.CloseSend(); err != nil { return err } defaultNode := RemotePeer(stream.Context()) //nolint:contextcheck var wg sync.WaitGroup defer wg.Wait() ch := make(chan Event) defer close(ch) wg.Go(func() { watchFunc(ch) }) for { event, err := stream.Recv() if err != nil { if err == io.EOF || StatusCode(err) == codes.Canceled { return nil } return fmt.Errorf("failed to watch events: %w", err) } ev, err := UnmarshalEvent(event) if err != nil { continue } if ev.Node == "" { ev.Node = defaultNode } select { case ch <- *ev: case <-ctx.Done(): return nil } } } // EventResult is the result of an event watch, containing either an Event or an error. type EventResult struct { // Event is the event that was received. Event Event // Err is the error that occurred. Error error } // EventsWatchV2 watches events of a single node and wraps the Events by providing a simpler interface. // It blocks until the first (empty) event is received, then spawns a goroutine that sends events to the given channel. // EventResult objects sent into the channel contain either the errors or the received events. // //nolint:gocyclo func (c *Client) EventsWatchV2(ctx context.Context, ch chan<- EventResult, opts ...EventsOptionFunc) error { ctx, cancel := context.WithCancel(ctx) stream, err := c.Events(ctx, opts...) if err != nil { cancel() return fmt.Errorf("error fetching events: %w", err) } if err = stream.CloseSend(); err != nil { cancel() return err } defaultNode := RemotePeer(stream.Context()) // receive first (empty) watch event _, err = stream.Recv() if err != nil { cancel() return fmt.Errorf("error while watching events: %w", err) } go func() { defer cancel() err = func() error { for { event, eventErr := stream.Recv() if eventErr != nil { return eventErr } if event.GetMetadata().GetError() != "" { var mdErr error if event.GetMetadata().GetStatus() != nil { mdErr = status.FromProto(event.GetMetadata().GetStatus()).Err() } else { mdErr = errors.New(event.GetMetadata().GetError()) } return fmt.Errorf("%s: %w", event.GetMetadata().GetHostname(), mdErr) } ev, eventErr := UnmarshalEvent(event) if eventErr != nil { return eventErr } if ev == nil { continue } if ev.Node == "" { ev.Node = defaultNode } select { case ch <- EventResult{Event: *ev}: case <-ctx.Done(): return ctx.Err() } } }() if err != nil { select { case ch <- EventResult{Error: err}: case <-ctx.Done(): } } }() return nil } // UnmarshalEvent decodes the event coming from the gRPC stream from any to the exact type. func UnmarshalEvent(event *machineapi.Event) (*Event, error) { typeURL := event.GetData().GetTypeUrl() var msg proto.Message for _, eventType := range []proto.Message{ &machineapi.SequenceEvent{}, &machineapi.PhaseEvent{}, &machineapi.TaskEvent{}, &machineapi.ServiceStateEvent{}, &machineapi.ConfigLoadErrorEvent{}, &machineapi.ConfigValidationErrorEvent{}, &machineapi.AddressEvent{}, &machineapi.MachineStatusEvent{}, &machineapi.RestartEvent{}, } { if typeURL == "talos/runtime/"+string(eventType.ProtoReflect().Descriptor().FullName()) { msg = eventType break } } if msg == nil { // We haven't implemented the handling of this event yet. return nil, EventNotSupportedError{ TypeURL: typeURL, } } if err := proto.Unmarshal(event.GetData().GetValue(), msg); err != nil { log.Printf("failed to unmarshal message: %v", err) return nil, err } ev := Event{ TypeURL: typeURL, ID: event.Id, Payload: msg, ActorID: event.ActorId, } if event.Metadata != nil { ev.Node = event.Metadata.Hostname } return &ev, nil } ================================================ FILE: pkg/machinery/client/export_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "crypto/tls" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) func ReduceURLsToAddresses(endpoints []string) []string { return reduceURLsToAddresses(endpoints) } func BuildTLSConfig(configContext *clientconfig.Context) (*tls.Config, error) { return buildTLSConfig(configContext) } ================================================ FILE: pkg/machinery/client/grpc_connection_wrapper.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "runtime/pprof" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) var grpcConnPprof = pprof.NewProfile("machinery/client/grpc.grpcConn") type grpcConnectionWrapper struct { *grpc.ClientConn clusterName string } func newGRPCConnectionWrapper(clusterName string, conn *grpc.ClientConn) *grpcConnectionWrapper { res := &grpcConnectionWrapper{ ClientConn: conn, clusterName: clusterName, } grpcConnPprof.Add(res, 1) return res } // Invoke performs a unary RPC and returns after the response is received // into reply. func (c *grpcConnectionWrapper) Invoke(ctx context.Context, method string, args any, reply any, opts ...grpc.CallOption) error { return c.ClientConn.Invoke(c.appendMetadata(ctx), method, args, reply, opts...) } // NewStream begins a streaming RPC. func (c *grpcConnectionWrapper) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) { return c.ClientConn.NewStream(c.appendMetadata(ctx), desc, method, opts...) } // Close closes the connection. func (c *grpcConnectionWrapper) Close() error { grpcConnPprof.Remove(c) return c.ClientConn.Close() } func (c *grpcConnectionWrapper) appendMetadata(ctx context.Context) context.Context { ctx = metadata.AppendToOutgoingContext(ctx, "runtime", "Talos") if c.clusterName != "" { ctx = metadata.AppendToOutgoingContext(ctx, "context", c.clusterName) } return ctx } ================================================ FILE: pkg/machinery/client/insecure_credentials.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build sidero.debug package client import ( "net/url" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // shouldInsecureConnectionsBeAllowed returns true if one endpoint starts with http:// func shouldInsecureConnectionsBeAllowed(endpoints []string) bool { for _, endpoint := range endpoints { u, _ := url.Parse(endpoint) if u.Scheme == "http" { return true } } return false } // RequireTransportSecurity enables basic auth with insecure gRPC transport credentials. func (c BasicAuth) RequireTransportSecurity() bool { return false } func buildCredentials(configContext *clientconfig.Context, endpoints []string) (credentials.TransportCredentials, error) { if shouldInsecureConnectionsBeAllowed(endpoints) { return insecure.NewCredentials(), nil } tlsConfig, err := buildTLSConfig(configContext) if err != nil { return nil, err } return credentials.NewTLS(tlsConfig), nil } ================================================ FILE: pkg/machinery/client/inspect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "google.golang.org/grpc" "google.golang.org/protobuf/types/known/emptypb" inspectapi "github.com/siderolabs/talos/pkg/machinery/api/inspect" ) // InspectClient provides access to inspect API. type InspectClient struct { client inspectapi.InspectServiceClient } // ControllerRuntimeDependencies returns graph describing dependencies between controllers. func (c *InspectClient) ControllerRuntimeDependencies(ctx context.Context, callOptions ...grpc.CallOption) (*inspectapi.ControllerRuntimeDependenciesResponse, error) { resp, err := c.client.ControllerRuntimeDependencies(ctx, &emptypb.Empty{}, callOptions...) return FilterMessages(resp, err) } ================================================ FILE: pkg/machinery/client/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "crypto/tls" "fmt" "google.golang.org/grpc" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/client/dialer" ) var defaultDialOptions = []grpc.DialOption{ grpc.WithContextDialer(dialer.DynamicProxyDialer), } // Options contains the set of client configuration options. type Options struct { endpointsOverride []string config *clientconfig.Config configContext *clientconfig.Context tlsConfig *tls.Config grpcDialOptions []grpc.DialOption contextOverride string contextOverrideSet bool unixSocketPath string clusterNameOverride string sideroV1KeysDir string serviceAccountBase64 string } // OptionFunc sets an option for the creation of the Client. type OptionFunc func(*Options) error // WithConfig configures the Client with the configuration provided. // Additionally use WithContextName to override the default context in the Config. func WithConfig(cfg *clientconfig.Config) OptionFunc { return func(o *Options) error { o.config = cfg return nil } } // WithContextName overrides the default context inside a provided client Config. func WithContextName(name string) OptionFunc { return func(o *Options) error { o.contextOverride = name o.contextOverrideSet = true return nil } } // WithConfigContext configures the Client with the configuration context provided. func WithConfigContext(cfg *clientconfig.Context) OptionFunc { return func(o *Options) error { o.configContext = cfg return nil } } // WithDefaultGRPCDialOptions adds the default grpc.DialOptions to a Client. func WithDefaultGRPCDialOptions() OptionFunc { return func(o *Options) error { o.grpcDialOptions = append(o.grpcDialOptions, defaultDialOptions...) return nil } } // WithGRPCDialOptions adds the given grpc.DialOptions to a Client. func WithGRPCDialOptions(opts ...grpc.DialOption) OptionFunc { return func(o *Options) error { o.grpcDialOptions = append(o.grpcDialOptions, opts...) return nil } } // WithTLSConfig overrides the default TLS configuration with the one provided. func WithTLSConfig(tlsConfig *tls.Config) OptionFunc { return func(o *Options) error { o.tlsConfig = tlsConfig return nil } } // WithEndpoints overrides the default endpoints with the provided list. func WithEndpoints(endpoints ...string) OptionFunc { return func(o *Options) error { o.endpointsOverride = endpoints return nil } } // WithDefaultConfig creates a Client with its configuration sourced from the // default config file location. // Additionally use WithContextName to select a context other than the default. func WithDefaultConfig() OptionFunc { return func(o *Options) (err error) { return WithConfigFromFile("")(o) } } // WithConfigFromFile creates a Client with its configuration extracted from the given file. // Additionally use WithContextName to select a context other than the default. func WithConfigFromFile(fn string) OptionFunc { return func(o *Options) (err error) { cfg, err := clientconfig.Open(fn) if err != nil { return fmt.Errorf("failed to read config from %q: %w", fn, err) } o.config = cfg return nil } } // WithUnixSocket creates a Client which connects to apid over local file socket. // // This option disables config parsing and TLS. // // Connection over unix socket is only used within the Talos node. func WithUnixSocket(path string) OptionFunc { return func(o *Options) error { o.unixSocketPath = path return nil } } // WithCluster creates a Client which connects to the named cluster. func WithCluster(cluster string) OptionFunc { return func(o *Options) error { o.clusterNameOverride = cluster return nil } } // WithSideroV1KeysDir overrides the default SideroV1KeysDir configuration with the one provided. func WithSideroV1KeysDir(keysDir string) OptionFunc { return func(o *Options) error { o.sideroV1KeysDir = keysDir return nil } } // WithServiceAccount sets the base64-encoded service account key for authentication. // // When set, the service account key takes priority over SIDERO_SERVICE_ACCOUNT_KEY and OMNI_SERVICE_ACCOUNT_KEY environment variables. func WithServiceAccount(serviceAccountBase64 string) OptionFunc { return func(o *Options) error { o.serviceAccountBase64 = serviceAccountBase64 return nil } } ================================================ FILE: pkg/machinery/client/peer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "net" "google.golang.org/grpc/peer" ) // RemotePeer parses remote peer address from grpc stream context. func RemotePeer(ctx context.Context) (peerHost string) { peerHost = "unknown" remote, ok := peer.FromContext(ctx) if ok { peerHost = AddrFromPeer(remote) } return peerHost } // AddrFromPeer extracts peer address from grpc Peer. func AddrFromPeer(remote *peer.Peer) (peerHost string) { peerHost = remote.Addr.String() peerHost, _, _ = net.SplitHostPort(peerHost) //nolint:errcheck return peerHost } ================================================ FILE: pkg/machinery/client/reply.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "errors" "fmt" "reflect" "github.com/hashicorp/go-multierror" rpcstatus "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" ) // NodeError is RPC error from some node. type NodeError struct { Node string Err error } func (ne *NodeError) Error() string { return fmt.Sprintf("%s: %s", ne.Node, ne.Err) } // Unwrap implements errors.Unwrap interface. func (ne *NodeError) Unwrap() error { return ne.Err } // Message is a generic interface for Messages. type Message[T any] interface { *T proto.Message } // MessageResponse is a generic interface for response with Messages. type MessageResponse[V any, T Message[V]] interface { proto.Message GetMessages() []T } // FilterMessages removes error Messages from resp and builds multierror. func FilterMessages[V any, T Message[V], MR MessageResponse[V, T]](resp MR, err error) (MR, error) { var zero MR res, filteredErr := filterMessages(resp, err) if res == nil { return zero, filteredErr } return resp, filteredErr } //nolint:gocyclo,cyclop func filterMessages(resp any, err error) (any, error) { if resp == nil { return nil, err } respStructPtr := reflect.ValueOf(resp) if respStructPtr.Kind() != reflect.Ptr { panic("response should be pointer to struct") } if respStructPtr.IsNil() { return nil, err } respStruct := respStructPtr.Elem() if respStruct.Kind() != reflect.Struct { panic("response should be struct") } messagesField := respStruct.FieldByName("Messages") if !messagesField.IsValid() { panic("Messages field missing") } if messagesField.Kind() != reflect.Slice { panic("Messages field should be a slice") } var multiErr *multierror.Error for i := 0; i < messagesField.Len(); { MessagesPtr := messagesField.Index(i) if MessagesPtr.Kind() != reflect.Ptr { panic("Messages slice should container pointers") } Messages := MessagesPtr.Elem() if Messages.Kind() != reflect.Struct { panic("Messages slice should container pointers to structs") } metadataField := Messages.FieldByName("Metadata") if !metadataField.IsValid() { panic("Messages metadata field missing") } if metadataField.Kind() != reflect.Ptr { panic("Messages metadata field should be a pointer") } if metadataField.IsNil() { // missing metadata, skip the field i++ continue } metadata := metadataField.Elem() if metadata.Kind() != reflect.Struct { panic("Messages metadata should be struct") } errorField := metadata.FieldByName("Error") if !errorField.IsValid() { panic("metadata.Error field missing") } if errorField.Kind() != reflect.String { panic("metadata.Error should be string") } if errorField.IsZero() { // no error, leave it as is i++ continue } rpcError := errors.New(errorField.String()) statusField := metadata.FieldByName("Status") if !statusField.IsValid() { panic("metadata.Status field missing") } if statusField.Kind() != reflect.Ptr { panic("metadata.Status should be pointer") } if !statusField.IsZero() { statusValue, ok := statusField.Interface().(*rpcstatus.Status) if !ok { panic("metadata.Status should be of type *status.Status") } rpcError = status.FromProto(statusValue).Err() } hostnameField := metadata.FieldByName("Hostname") if !hostnameField.IsValid() { panic("metadata.Hostname field missing") } if hostnameField.Kind() != reflect.String { panic("metadata.Hostname should be string") } // extract error nodeError := &NodeError{ Node: hostnameField.String(), Err: rpcError, } multiErr = multierror.Append(multiErr, nodeError) // remove ith Messages reflect.Copy(messagesField.Slice(i, messagesField.Len()), messagesField.Slice(i+1, messagesField.Len())) messagesField.SetLen(messagesField.Len() - 1) } // if all the Messages were error Messages... if multiErr != nil && messagesField.Len() == 0 { resp = nil } return resp, multiErr.ErrorOrNil() } ================================================ FILE: pkg/machinery/client/reply_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client_test import ( "errors" "testing" "github.com/stretchr/testify/assert" "google.golang.org/genproto/googleapis/rpc/status" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/machinery/api/common" "github.com/siderolabs/talos/pkg/machinery/client" ) func TestFilterMessages(t *testing.T) { reply := &common.DataResponse{ Messages: []*common.Data{ { Metadata: &common.Metadata{ Hostname: "host1", }, Bytes: []byte("abc"), }, { Metadata: &common.Metadata{ Hostname: "host2", Error: "something wrong", }, }, { Bytes: []byte("def"), }, { Metadata: &common.Metadata{ Hostname: "host4", Error: "even more wrong", }, }, }, } filtered, err := client.FilterMessages(reply, nil) assert.EqualError(t, err, "2 errors occurred:\n\t* host2: something wrong\n\t* host4: even more wrong\n\n") assert.Equal(t, filtered, &common.DataResponse{ Messages: []*common.Data{ { Metadata: &common.Metadata{ Hostname: "host1", }, Bytes: []byte("abc"), }, { Bytes: []byte("def"), }, }, }) } func TestFilterMessagesNil(t *testing.T) { e := errors.New("wrong") filtered, err := client.FilterMessages((*common.DataResponse)(nil), e) assert.Nil(t, filtered) assert.Equal(t, e, err) } func TestFilterMessagesOnlyErrors(t *testing.T) { reply := &common.DataResponse{ Messages: []*common.Data{ { Metadata: &common.Metadata{ Hostname: "host2", Error: "something wrong", }, }, { Metadata: &common.Metadata{ Hostname: "host4", Error: "even more wrong", }, }, }, } filtered, err := client.FilterMessages(reply, nil) assert.EqualError(t, err, "2 errors occurred:\n\t* host2: something wrong\n\t* host4: even more wrong\n\n") assert.Nil(t, filtered) } func TestFilterMessagesGRPCStatus(t *testing.T) { reply := &common.DataResponse{ Messages: []*common.Data{ { Metadata: &common.Metadata{ Hostname: "host2", Error: "should be ignored", Status: &status.Status{ Code: int32(codes.Aborted), Message: "something aborted", }, }, }, { Metadata: &common.Metadata{ Hostname: "host4", Error: "should be ignored", Status: &status.Status{ Code: int32(codes.Unknown), Message: "something went wrong", }, }, }, }, } filtered, err := client.FilterMessages(reply, nil) assert.EqualError(t, err, "2 errors occurred:\n\t* host2: rpc error: code = Aborted desc = something aborted\n\t* host4: rpc error: code = Unknown desc = something went wrong\n\n") assert.Nil(t, filtered) } ================================================ FILE: pkg/machinery/client/resolver/resolver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package resolver implements gRPC resolvers. package resolver ================================================ FILE: pkg/machinery/client/resolver/roundrobin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package resolver import ( "math/rand/v2" "net" "strconv" "strings" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc/resolver" ) // RoundRobinResolverScheme is a scheme to use in grpc.Dial for the round-robin gRPC resolver. // This resolver requires that all endpoints have a port appended. // To ensure this, use EnsureEndpointsHavePorts before constructing a connection string. const RoundRobinResolverScheme = "talosroundrobin" func init() { resolver.Register(&roundRobinResolverBuilder{ scheme: RoundRobinResolverScheme, }) } type roundRobinResolverBuilder struct { scheme string } // Build implements resolver.Builder. func (b *roundRobinResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) { r := &roundRobinResolver{ target: target, cc: cc, } if err := r.start(); err != nil { return nil, err } return r, nil } // Build implements resolver.Builder. func (b *roundRobinResolverBuilder) Scheme() string { return b.scheme } type roundRobinResolver struct { target resolver.Target cc resolver.ClientConn } // EnsureEndpointsHavePorts returns the list of endpoints with default port appended to those addresses that don't have a port. func EnsureEndpointsHavePorts(endpoints []string, defaultPort int) []string { return xslices.Map(endpoints, func(endpoint string) string { _, _, err := net.SplitHostPort(endpoint) if err != nil { return net.JoinHostPort(endpoint, strconv.Itoa(defaultPort)) } return endpoint }) } func (r *roundRobinResolver) start() error { var addrs []resolver.Address //nolint:prealloc endpoints := strings.SplitSeq(r.target.Endpoint(), ",") for addr := range endpoints { serverName := addr host, _, err := net.SplitHostPort(serverName) if err == nil { serverName = host } addrs = append(addrs, resolver.Address{ ServerName: serverName, Addr: addr, }) } // shuffle the list in case client does just one request rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) serviceConfigJSON := `{ "loadBalancingConfig": [{ "round_robin": {} }] }` parsedServiceConfig := r.cc.ParseServiceConfig(serviceConfigJSON) if parsedServiceConfig.Err != nil { return parsedServiceConfig.Err } return r.cc.UpdateState(resolver.State{ Addresses: addrs, ServiceConfig: parsedServiceConfig, }) } // ResolveNow implements resolver.Resolver. func (r *roundRobinResolver) ResolveNow(o resolver.ResolveNowOptions) {} // ResolveNow implements resolver.Resolver. func (r *roundRobinResolver) Close() {} ================================================ FILE: pkg/machinery/client/resolver/roundrobin_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package resolver_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/client/resolver" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestEnsureEndpointsHavePorts(t *testing.T) { endpoints := []string{ "123.123.123.123", "exammple.com:111", "234.234.234.234:4000", "localhost", "localhost:890", "2001:db8:0:0:0:ff00:42:8329", "www.company.com", "[2001:db8:4006:812::200e]:8080", } expected := []string{ "123.123.123.123:50000", "exammple.com:111", "234.234.234.234:4000", "localhost:50000", "localhost:890", "[2001:db8:0:0:0:ff00:42:8329]:50000", "www.company.com:50000", "[2001:db8:4006:812::200e]:8080", } actual := resolver.EnsureEndpointsHavePorts(endpoints, constants.ApidPort) assert.Equal(t, expected, actual) } ================================================ FILE: pkg/machinery/client/resources.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "context" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/gen/xslices" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // ResolveResourceKind resolves potentially aliased 'resourceType' and replaces empty 'resourceNamespace' with the default namespace for the resource. func (c *Client) ResolveResourceKind(ctx context.Context, resourceNamespace *resource.Namespace, resourceType resource.Type) (*meta.ResourceDefinition, error) { registeredResources, err := safe.StateListAll[*meta.ResourceDefinition](ctx, c.COSI) if err != nil { return nil, err } var matched []*meta.ResourceDefinition for rd := range registeredResources.All() { if strings.EqualFold(rd.Metadata().ID(), resourceType) { matched = append(matched, rd) continue } spec := rd.TypedSpec() for _, alias := range spec.AllAliases { if strings.EqualFold(alias, resourceType) { matched = append(matched, rd) break } } } switch { case len(matched) == 1: if *resourceNamespace == "" { *resourceNamespace = matched[0].TypedSpec().DefaultNamespace } return matched[0], nil case len(matched) > 1: matchedTypes := xslices.Map(matched, func(rd *meta.ResourceDefinition) string { return rd.Metadata().ID() }) return nil, status.Errorf(codes.InvalidArgument, "resource type %q is ambiguous: %v", resourceType, matchedTypes) default: return nil, status.Errorf(codes.NotFound, "resource %q is not registered", resourceType) } } ================================================ FILE: pkg/machinery/client/secure_credentials.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !sidero.debug package client import ( "google.golang.org/grpc/credentials" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // RequireTransportSecurity implements credentials.PerRPCCredentials. func (c BasicAuth) RequireTransportSecurity() bool { return true } func buildCredentials(configContext *clientconfig.Context, _ []string) (credentials.TransportCredentials, error) { tlsConfig, err := buildTLSConfig(configContext) if err != nil { return nil, err } return credentials.NewTLS(tlsConfig), nil } ================================================ FILE: pkg/machinery/client/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client import ( "errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) // Status returns the status if it is a Status error, nil otherwise. func Status(err error) *status.Status { type grpcStatus interface { GRPCStatus() *status.Status } // Don't use FromError to avoid allocation of OK status. var st grpcStatus if errors.As(err, &st) { return st.GRPCStatus() } return nil } // StatusCode returns the Code of the error if it is a Status error, codes.OK if err // is nil, or codes.Unknown otherwise correctly unwrapping wrapped errors. // // StatusCode is mostly equivalent to grpc `status.Code` method, but it correctly unwraps wrapped errors // including `multierror.Error` used when parsing multi-node responses. func StatusCode(err error) codes.Code { if err == nil { return codes.OK } if st := Status(err); st != nil { return st.Code() } return codes.Unknown } ================================================ FILE: pkg/machinery/client/status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package client_test import ( "errors" "fmt" "testing" "github.com/hashicorp/go-multierror" "github.com/stretchr/testify/assert" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/siderolabs/talos/pkg/machinery/client" ) func TestStatus(t *testing.T) { for _, tt := range []struct { name string err error nilStatus bool message string code codes.Code }{ { name: "nil", err: nil, nilStatus: true, code: codes.OK, }, { name: "not status", err: errors.New("some error"), nilStatus: true, code: codes.Unknown, }, { name: "status", err: status.Error(codes.AlreadyExists, "file already exists"), message: "file already exists", code: codes.AlreadyExists, }, { name: "status wrapped", err: multierror.Append(nil, status.Error(codes.AlreadyExists, "file already exists")).ErrorOrNil(), message: "file already exists", code: codes.AlreadyExists, }, { name: "multiple wrapped", err: multierror.Append(nil, status.Error(codes.FailedPrecondition, "can't be zero"), status.Error(codes.AlreadyExists, "file already exists")).ErrorOrNil(), message: "can't be zero", code: codes.FailedPrecondition, }, { name: "double wrapped", err: multierror.Append(nil, fmt.Errorf("127.0.0.1: %w", status.Error(codes.AlreadyExists, "file already exists"))).ErrorOrNil(), message: "file already exists", code: codes.AlreadyExists, }, } { t.Run(tt.name, func(t *testing.T) { st := client.Status(tt.err) if tt.nilStatus { assert.Nil(t, st) } else { assert.Equal(t, st.Message(), tt.message) assert.Equal(t, st.Code(), tt.code) } assert.Equal(t, client.StatusCode(tt.err), tt.code) }) } } ================================================ FILE: pkg/machinery/compatibility/compatibility.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package compatibility provides version compatibility checks for Talos. package compatibility ================================================ FILE: pkg/machinery/compatibility/kubernetes_version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package compatibility import ( "fmt" "github.com/blang/semver/v4" "github.com/siderolabs/gen/pair/ordered" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos110" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos111" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos112" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos113" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos12" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos13" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos14" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos15" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos16" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos17" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos18" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos19" ) // KubernetesVersion embeds Kubernetes version. type KubernetesVersion struct { vers semver.Version } // ParseKubernetesVersion parses Kubernetes version. func ParseKubernetesVersion(v string) (*KubernetesVersion, error) { parsed, err := semver.ParseTolerant(v) if err != nil { return nil, err } return &KubernetesVersion{ vers: parsed, }, nil } func (v *KubernetesVersion) String() string { return v.vers.String() } // SupportedWith checks if the Kubernetes version is supported with specified version of Talos. // //nolint:gocyclo func (v *KubernetesVersion) SupportedWith(target *TalosVersion) error { var minK8sVersion, maxK8sVersion semver.Version switch target.majorMinor { case talos12.MajorMinor: // upgrades to 1.2.x minK8sVersion, maxK8sVersion = talos12.MinimumKubernetesVersion, talos12.MaximumKubernetesVersion case talos13.MajorMinor: // upgrades to 1.3.x minK8sVersion, maxK8sVersion = talos13.MinimumKubernetesVersion, talos13.MaximumKubernetesVersion case talos14.MajorMinor: // upgrades to 1.4.x minK8sVersion, maxK8sVersion = talos14.MinimumKubernetesVersion, talos14.MaximumKubernetesVersion case talos15.MajorMinor: // upgrades to 1.5.x minK8sVersion, maxK8sVersion = talos15.MinimumKubernetesVersion, talos15.MaximumKubernetesVersion case talos16.MajorMinor: // upgrades to 1.6.x minK8sVersion, maxK8sVersion = talos16.MinimumKubernetesVersion, talos16.MaximumKubernetesVersion case talos17.MajorMinor: // upgrades to 1.7.x minK8sVersion, maxK8sVersion = talos17.MinimumKubernetesVersion, talos17.MaximumKubernetesVersion case talos18.MajorMinor: // upgrades to 1.8.x minK8sVersion, maxK8sVersion = talos18.MinimumKubernetesVersion, talos18.MaximumKubernetesVersion case talos19.MajorMinor: // upgrades to 1.9.x minK8sVersion, maxK8sVersion = talos19.MinimumKubernetesVersion, talos19.MaximumKubernetesVersion case talos110.MajorMinor: // upgrades to 1.10.x minK8sVersion, maxK8sVersion = talos110.MinimumKubernetesVersion, talos110.MaximumKubernetesVersion case talos111.MajorMinor: // upgrades to 1.11.x minK8sVersion, maxK8sVersion = talos111.MinimumKubernetesVersion, talos111.MaximumKubernetesVersion case talos112.MajorMinor: // upgrades to 1.12.x minK8sVersion, maxK8sVersion = talos112.MinimumKubernetesVersion, talos112.MaximumKubernetesVersion case talos113.MajorMinor: // upgrades to 1.13.x minK8sVersion, maxK8sVersion = talos113.MinimumKubernetesVersion, talos113.MaximumKubernetesVersion default: return fmt.Errorf("compatibility with version %s is not supported", target.String()) } core := ordered.MakeTriple(v.vers.Major, v.vers.Minor, v.vers.Patch) minK8sVersionCore := ordered.MakeTriple(minK8sVersion.Major, minK8sVersion.Minor, minK8sVersion.Patch) if core.LessThan(minK8sVersionCore) { return fmt.Errorf("version of Kubernetes %s is too old to be used with Talos %s", v.vers.String(), target.version.String()) } maxK8sVersionCore := ordered.MakeTriple(maxK8sVersion.Major, maxK8sVersion.Minor, maxK8sVersion.Patch) if core.Compare(maxK8sVersionCore) >= 0 { return fmt.Errorf("version of Kubernetes %s is too new to be used with Talos %s", v.vers.String(), target.version.String()) } return nil } ================================================ FILE: pkg/machinery/compatibility/kubernetes_version_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package compatibility_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/compatibility" ) type kubernetesVersionTest struct { kubernetesVersion string target string expectedError string } func runKubernetesVersionTest(t *testing.T, tt kubernetesVersionTest) { t.Run(tt.kubernetesVersion+" -> "+tt.target, func(t *testing.T) { k8sVersion, err := compatibility.ParseKubernetesVersion(tt.kubernetesVersion) require.NoError(t, err) target, err := compatibility.ParseTalosVersion(&machine.VersionInfo{ Tag: tt.target, }) require.NoError(t, err) err = k8sVersion.SupportedWith(target) if tt.expectedError != "" { require.EqualError(t, err, tt.expectedError) } else { require.NoError(t, err) } }) } func TestKubernetesCompatibility12(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.23.1", target: "1.2.0", }, { kubernetesVersion: "1.24.3", target: "1.2.0-beta.0", }, { kubernetesVersion: "1.25.0-rc.0", target: "1.2.7", }, { kubernetesVersion: "1.26.0-alpha.0", target: "1.2.0", expectedError: "version of Kubernetes 1.26.0-alpha.0 is too new to be used with Talos 1.2.0", }, { kubernetesVersion: "1.22.4", target: "1.2.0", expectedError: "version of Kubernetes 1.22.4 is too old to be used with Talos 1.2.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility13(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.24.1", target: "1.3.0", }, { kubernetesVersion: "1.25.3", target: "1.3.0-beta.0", }, { kubernetesVersion: "1.26.0-rc.0", target: "1.3.7", }, { kubernetesVersion: "1.27.0-alpha.0", target: "1.3.0", expectedError: "version of Kubernetes 1.27.0-alpha.0 is too new to be used with Talos 1.3.0", }, { kubernetesVersion: "1.23.4", target: "1.3.0", expectedError: "version of Kubernetes 1.23.4 is too old to be used with Talos 1.3.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility14(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.25.1", target: "1.4.0", }, { kubernetesVersion: "1.26.3", target: "1.4.0-beta.0", }, { kubernetesVersion: "1.27.0-rc.0", target: "1.4.7", }, { kubernetesVersion: "1.28.0-alpha.0", target: "1.4.0", expectedError: "version of Kubernetes 1.28.0-alpha.0 is too new to be used with Talos 1.4.0", }, { kubernetesVersion: "1.24.1", target: "1.4.0", expectedError: "version of Kubernetes 1.24.1 is too old to be used with Talos 1.4.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility15(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.26.1", target: "1.5.0", }, { kubernetesVersion: "1.27.3", target: "1.5.0-beta.0", }, { kubernetesVersion: "1.28.0-rc.0", target: "1.5.7", }, { kubernetesVersion: "1.29.0-alpha.0", target: "1.5.0", expectedError: "version of Kubernetes 1.29.0-alpha.0 is too new to be used with Talos 1.5.0", }, { kubernetesVersion: "1.25.1", target: "1.5.0", expectedError: "version of Kubernetes 1.25.1 is too old to be used with Talos 1.5.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility16(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.27.1", target: "1.6.0", }, { kubernetesVersion: "1.24.1", target: "1.6.0", }, { kubernetesVersion: "1.28.3", target: "1.6.0-beta.0", }, { kubernetesVersion: "1.29.0-rc.0", target: "1.6.7", }, { kubernetesVersion: "1.30.0-alpha.0", target: "1.6.0", expectedError: "version of Kubernetes 1.30.0-alpha.0 is too new to be used with Talos 1.6.0", }, { kubernetesVersion: "1.23.1", target: "1.6.0", expectedError: "version of Kubernetes 1.23.1 is too old to be used with Talos 1.6.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility17(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.27.1", target: "1.7.0", }, { kubernetesVersion: "1.25.1", target: "1.7.0", }, { kubernetesVersion: "1.28.3", target: "1.7.0-beta.0", }, { kubernetesVersion: "1.30.0-rc.0", target: "1.7.7", }, { kubernetesVersion: "1.31.0-alpha.0", target: "1.7.0", expectedError: "version of Kubernetes 1.31.0-alpha.0 is too new to be used with Talos 1.7.0", }, { kubernetesVersion: "1.24.1", target: "1.7.0", expectedError: "version of Kubernetes 1.24.1 is too old to be used with Talos 1.7.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility18(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.27.1", target: "1.8.0", }, { kubernetesVersion: "1.26.1", target: "1.8.0", }, { kubernetesVersion: "1.30.3", target: "1.8.0-beta.0", }, { kubernetesVersion: "1.31.0-rc.0", target: "1.8.7", }, { kubernetesVersion: "1.32.0-alpha.0", target: "1.8.0", expectedError: "version of Kubernetes 1.32.0-alpha.0 is too new to be used with Talos 1.8.0", }, { kubernetesVersion: "1.25.1", target: "1.8.0", expectedError: "version of Kubernetes 1.25.1 is too old to be used with Talos 1.8.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility19(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.28.1", target: "1.9.0", }, { kubernetesVersion: "1.27.1", target: "1.9.0", }, { kubernetesVersion: "1.31.3", target: "1.9.0-beta.0", }, { kubernetesVersion: "1.32.0-rc.0", target: "1.9.7", }, { kubernetesVersion: "1.33.0-alpha.0", target: "1.9.0", expectedError: "version of Kubernetes 1.33.0-alpha.0 is too new to be used with Talos 1.9.0", }, { kubernetesVersion: "1.26.1", target: "1.9.0", expectedError: "version of Kubernetes 1.26.1 is too old to be used with Talos 1.9.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility110(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.29.1", target: "1.10.0", }, { kubernetesVersion: "1.28.1", target: "1.10.0", }, { kubernetesVersion: "1.32.3", target: "1.10.0-beta.0", }, { kubernetesVersion: "1.33.0-rc.0", target: "1.10.7", }, { kubernetesVersion: "1.34.0-alpha.0", target: "1.10.0", expectedError: "version of Kubernetes 1.34.0-alpha.0 is too new to be used with Talos 1.10.0", }, { kubernetesVersion: "1.27.1", target: "1.10.0", expectedError: "version of Kubernetes 1.27.1 is too old to be used with Talos 1.10.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility111(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.30.1", target: "1.11.0", }, { kubernetesVersion: "1.29.1", target: "1.11.0", }, { kubernetesVersion: "1.33.3", target: "1.11.0-beta.0", }, { kubernetesVersion: "1.34.0-rc.0", target: "1.11.7", }, { kubernetesVersion: "1.35.0-alpha.0", target: "1.11.0", expectedError: "version of Kubernetes 1.35.0-alpha.0 is too new to be used with Talos 1.11.0", }, { kubernetesVersion: "1.28.1", target: "1.11.0", expectedError: "version of Kubernetes 1.28.1 is too old to be used with Talos 1.11.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility112(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.31.1", target: "1.12.0", }, { kubernetesVersion: "1.30.1", target: "1.12.0", }, { kubernetesVersion: "1.34.3", target: "1.12.0-beta.0", }, { kubernetesVersion: "1.35.0-rc.0", target: "1.12.7", }, { kubernetesVersion: "1.36.0-alpha.0", target: "1.12.0", expectedError: "version of Kubernetes 1.36.0-alpha.0 is too new to be used with Talos 1.12.0", }, { kubernetesVersion: "1.29.1", target: "1.12.0", expectedError: "version of Kubernetes 1.29.1 is too old to be used with Talos 1.12.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibility113(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.31.1", target: "1.13.0", }, { kubernetesVersion: "1.32.1", target: "1.13.0", }, { kubernetesVersion: "1.35.3", target: "1.13.0-beta.0", }, { kubernetesVersion: "1.36.0-rc.0", target: "1.13.7", }, { kubernetesVersion: "1.37.0-alpha.0", target: "1.13.0", expectedError: "version of Kubernetes 1.37.0-alpha.0 is too new to be used with Talos 1.13.0", }, { kubernetesVersion: "1.30.1", target: "1.13.0", expectedError: "version of Kubernetes 1.30.1 is too old to be used with Talos 1.13.0", }, } { runKubernetesVersionTest(t, tt) } } func TestKubernetesCompatibilityUnsupported(t *testing.T) { for _, tt := range []kubernetesVersionTest{ { kubernetesVersion: "1.25.0", target: "1.14.0-alpha.0", expectedError: "compatibility with version 1.14.0-alpha.0 is not supported", }, { kubernetesVersion: "1.25.0", target: "1.1.0", expectedError: "compatibility with version 1.1.0 is not supported", }, } { runKubernetesVersionTest(t, tt) } } ================================================ FILE: pkg/machinery/compatibility/talos110/talos110.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos110 provides compatibility constants for Talos 1.10. package talos110 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.10. var MajorMinor = [2]uint64{1, 10} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.10. var MinimumHostUpgradeVersion = semver.MustParse("1.8.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.10. var MaximumHostDowngradeVersion = semver.MustParse("1.12.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.10. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.10. var MinimumKubernetesVersion = semver.MustParse("1.28.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.10. var MaximumKubernetesVersion = semver.MustParse("1.33.99") ================================================ FILE: pkg/machinery/compatibility/talos111/talos111.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos111 provides compatibility constants for Talos 1.11. package talos111 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.11. var MajorMinor = [2]uint64{1, 11} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.11. var MinimumHostUpgradeVersion = semver.MustParse("1.9.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.11. var MaximumHostDowngradeVersion = semver.MustParse("1.13.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.11. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.11. var MinimumKubernetesVersion = semver.MustParse("1.29.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.11. var MaximumKubernetesVersion = semver.MustParse("1.34.99") ================================================ FILE: pkg/machinery/compatibility/talos112/talos112.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos112 provides compatibility constants for Talos 1.12. package talos112 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.12. var MajorMinor = [2]uint64{1, 12} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.12. var MinimumHostUpgradeVersion = semver.MustParse("1.10.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.12. var MaximumHostDowngradeVersion = semver.MustParse("1.14.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.12. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.12. var MinimumKubernetesVersion = semver.MustParse("1.30.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.12. var MaximumKubernetesVersion = semver.MustParse("1.35.99") ================================================ FILE: pkg/machinery/compatibility/talos113/talos113.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos113 provides compatibility constants for Talos 1.13. package talos113 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.13. var MajorMinor = [2]uint64{1, 13} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.13. var MinimumHostUpgradeVersion = semver.MustParse("1.11.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.13. var MaximumHostDowngradeVersion = semver.MustParse("1.15.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.13. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.13. var MinimumKubernetesVersion = semver.MustParse("1.31.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.13. var MaximumKubernetesVersion = semver.MustParse("1.36.99") ================================================ FILE: pkg/machinery/compatibility/talos12/talos12.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos12 provides compatibility constants for Talos 1.2. package talos12 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.2. var MajorMinor = [2]uint64{1, 2} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.2. var MinimumHostUpgradeVersion = semver.MustParse("1.0.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.3. var MaximumHostDowngradeVersion = semver.MustParse("1.3.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.2. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.2. var MinimumKubernetesVersion = semver.MustParse("1.23.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.2. var MaximumKubernetesVersion = semver.MustParse("1.25.99") ================================================ FILE: pkg/machinery/compatibility/talos13/talos13.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos13 provides compatibility constants for Talos 1.3. package talos13 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.3. var MajorMinor = [2]uint64{1, 3} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.3. var MinimumHostUpgradeVersion = semver.MustParse("1.0.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.3. var MaximumHostDowngradeVersion = semver.MustParse("1.5.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.3. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.3. var MinimumKubernetesVersion = semver.MustParse("1.24.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.3. var MaximumKubernetesVersion = semver.MustParse("1.26.99") ================================================ FILE: pkg/machinery/compatibility/talos14/talos14.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos14 provides compatibility constants for Talos 1.4 package talos14 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.4. var MajorMinor = [2]uint64{1, 4} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.4. var MinimumHostUpgradeVersion = semver.MustParse("1.0.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.4. var MaximumHostDowngradeVersion = semver.MustParse("1.6.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.4. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.4. var MinimumKubernetesVersion = semver.MustParse("1.25.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.4. var MaximumKubernetesVersion = semver.MustParse("1.27.99") ================================================ FILE: pkg/machinery/compatibility/talos15/talos15.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos15 provides compatibility constants for Talos 1.5. package talos15 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.5. var MajorMinor = [2]uint64{1, 5} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.5. var MinimumHostUpgradeVersion = semver.MustParse("1.2.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.5. var MaximumHostDowngradeVersion = semver.MustParse("1.7.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.5. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.5. var MinimumKubernetesVersion = semver.MustParse("1.26.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.5. var MaximumKubernetesVersion = semver.MustParse("1.28.99") ================================================ FILE: pkg/machinery/compatibility/talos16/talos16.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos16 provides compatibility constants for Talos 1.6. package talos16 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.6. var MajorMinor = [2]uint64{1, 6} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.6. var MinimumHostUpgradeVersion = semver.MustParse("1.3.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.6. var MaximumHostDowngradeVersion = semver.MustParse("1.8.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.6. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.6. var MinimumKubernetesVersion = semver.MustParse("1.24.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.6. var MaximumKubernetesVersion = semver.MustParse("1.29.99") ================================================ FILE: pkg/machinery/compatibility/talos17/talos17.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos17 provides compatibility constants for Talos 1.7. package talos17 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.7. var MajorMinor = [2]uint64{1, 7} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.7. var MinimumHostUpgradeVersion = semver.MustParse("1.4.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.7. var MaximumHostDowngradeVersion = semver.MustParse("1.9.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.7. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.7. var MinimumKubernetesVersion = semver.MustParse("1.25.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.7. var MaximumKubernetesVersion = semver.MustParse("1.30.99") ================================================ FILE: pkg/machinery/compatibility/talos18/talos18.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos18 provides compatibility constants for Talos 1.8. package talos18 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.8. var MajorMinor = [2]uint64{1, 8} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.8. var MinimumHostUpgradeVersion = semver.MustParse("1.5.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.8. var MaximumHostDowngradeVersion = semver.MustParse("1.10.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.8. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.8. var MinimumKubernetesVersion = semver.MustParse("1.26.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.8. var MaximumKubernetesVersion = semver.MustParse("1.31.99") ================================================ FILE: pkg/machinery/compatibility/talos19/talos19.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos19 provides compatibility constants for Talos 1.9. package talos19 import ( "github.com/blang/semver/v4" ) // MajorMinor is the major.minor version of Talos 1.9. var MajorMinor = [2]uint64{1, 9} // MinimumHostUpgradeVersion is the minimum version of Talos that can be upgraded to 1.9. var MinimumHostUpgradeVersion = semver.MustParse("1.8.0") // MaximumHostDowngradeVersion is the maximum (not inclusive) version of Talos that can be downgraded to 1.9. var MaximumHostDowngradeVersion = semver.MustParse("1.11.0") // DeniedHostUpgradeVersions are the versions of Talos that cannot be upgraded to 1.9. var DeniedHostUpgradeVersions []semver.Version // MinimumKubernetesVersion is the minimum version of Kubernetes is supported with 1.9. var MinimumKubernetesVersion = semver.MustParse("1.27.0") // MaximumKubernetesVersion is the maximum version of Kubernetes is supported with 1.9. var MaximumKubernetesVersion = semver.MustParse("1.32.99") ================================================ FILE: pkg/machinery/compatibility/talos_version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package compatibility import ( "fmt" "slices" "github.com/blang/semver/v4" "github.com/siderolabs/gen/pair/ordered" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos110" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos111" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos112" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos113" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos12" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos13" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos14" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos15" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos16" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos17" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos18" "github.com/siderolabs/talos/pkg/machinery/compatibility/talos19" ) // TalosVersion embeds Talos version. type TalosVersion struct { version semver.Version majorMinor [2]uint64 } // ParseTalosVersion parses Talos version. func ParseTalosVersion(v *machine.VersionInfo) (*TalosVersion, error) { parsed, err := semver.ParseTolerant(v.Tag) if err != nil { return nil, err } return &TalosVersion{ version: parsed, majorMinor: [2]uint64{parsed.Major, parsed.Minor}, }, nil } func (v *TalosVersion) String() string { return v.version.String() } // DisablePredictableNetworkInterfaces returns true if predictable network interfaces should be disabled on upgrade. func (v *TalosVersion) DisablePredictableNetworkInterfaces() bool { if v.majorMinor[0] <= talos14.MajorMinor[0] && v.majorMinor[1] <= talos14.MajorMinor[1] { return true } return false } // PrecreateStatePartition returns true if running an 1.8+ installer from a version before <=1.7.x. // // Host Talos needs STATE partition to save the machine configuration. func (v *TalosVersion) PrecreateStatePartition() bool { if v.majorMinor[0] <= talos17.MajorMinor[0] && v.majorMinor[1] <= talos17.MajorMinor[1] { return true } return false } // UpgradeableFrom checks if the current version of Talos can be used as an upgrade for the given host version. // //nolint:gocyclo func (v *TalosVersion) UpgradeableFrom(host *TalosVersion) error { var ( minHostUpgradeVersion, maxHostDowngradeVersion semver.Version deniedHostUpgradeVersions []semver.Version ) switch v.majorMinor { case talos12.MajorMinor: // upgrades to 1.2.x minHostUpgradeVersion, maxHostDowngradeVersion = talos12.MinimumHostUpgradeVersion, talos12.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos12.DeniedHostUpgradeVersions case talos13.MajorMinor: // upgrades to 1.3.x minHostUpgradeVersion, maxHostDowngradeVersion = talos13.MinimumHostUpgradeVersion, talos13.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos13.DeniedHostUpgradeVersions case talos14.MajorMinor: // upgrades to 1.4.x minHostUpgradeVersion, maxHostDowngradeVersion = talos14.MinimumHostUpgradeVersion, talos14.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos14.DeniedHostUpgradeVersions case talos15.MajorMinor: // upgrades to 1.5.x minHostUpgradeVersion, maxHostDowngradeVersion = talos15.MinimumHostUpgradeVersion, talos15.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos15.DeniedHostUpgradeVersions case talos16.MajorMinor: // upgrades to 1.6.x minHostUpgradeVersion, maxHostDowngradeVersion = talos16.MinimumHostUpgradeVersion, talos16.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos16.DeniedHostUpgradeVersions case talos17.MajorMinor: // upgrades to 1.7.x minHostUpgradeVersion, maxHostDowngradeVersion = talos17.MinimumHostUpgradeVersion, talos17.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos17.DeniedHostUpgradeVersions case talos18.MajorMinor: // upgrades to 1.8.x minHostUpgradeVersion, maxHostDowngradeVersion = talos18.MinimumHostUpgradeVersion, talos18.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos18.DeniedHostUpgradeVersions case talos19.MajorMinor: // upgrades to 1.9.x minHostUpgradeVersion, maxHostDowngradeVersion = talos19.MinimumHostUpgradeVersion, talos19.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos19.DeniedHostUpgradeVersions case talos110.MajorMinor: // upgrades to 1.10.x minHostUpgradeVersion, maxHostDowngradeVersion = talos110.MinimumHostUpgradeVersion, talos110.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos110.DeniedHostUpgradeVersions case talos111.MajorMinor: // upgrades to 1.11.x minHostUpgradeVersion, maxHostDowngradeVersion = talos111.MinimumHostUpgradeVersion, talos111.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos111.DeniedHostUpgradeVersions case talos112.MajorMinor: // upgrades to 1.12.x minHostUpgradeVersion, maxHostDowngradeVersion = talos112.MinimumHostUpgradeVersion, talos112.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos112.DeniedHostUpgradeVersions case talos113.MajorMinor: // upgrades to 1.13.x minHostUpgradeVersion, maxHostDowngradeVersion = talos113.MinimumHostUpgradeVersion, talos113.MaximumHostDowngradeVersion deniedHostUpgradeVersions = talos113.DeniedHostUpgradeVersions default: return fmt.Errorf("upgrades to version %s are not supported", v.version.String()) } hostCore := ordered.MakeTriple(host.majorMinor[0], host.majorMinor[1], host.version.Patch) minHostUpgradeVersionCore := ordered.MakeTriple(minHostUpgradeVersion.Major, minHostUpgradeVersion.Minor, minHostUpgradeVersion.Patch) if hostCore.LessThan(minHostUpgradeVersionCore) { return fmt.Errorf("host version %s is too old to upgrade to Talos %s", host.version.String(), v.version.String()) } maxHostDowngradeVersionCore := ordered.MakeTriple(maxHostDowngradeVersion.Major, maxHostDowngradeVersion.Minor, maxHostDowngradeVersion.Patch) if hostCore.Compare(maxHostDowngradeVersionCore) >= 0 { return fmt.Errorf("host version %s is too new to downgrade to Talos %s", host.version.String(), v.version.String()) } if slices.ContainsFunc(deniedHostUpgradeVersions, host.version.EQ) { return fmt.Errorf("host version %s is denied for upgrade to Talos %s", host.version.String(), v.version.String()) } return nil } // SupportsSSAManifestSync returns true if the Talos version supports server side apply manifest sync. func (v *TalosVersion) SupportsSSAManifestSync() bool { // supported from Talos 1.13+ return v.majorMinor[0] > talos113.MajorMinor[0] || (v.majorMinor[0] == talos113.MajorMinor[0] && v.majorMinor[1] >= talos113.MajorMinor[1]) } ================================================ FILE: pkg/machinery/compatibility/talos_version_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package compatibility_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/compatibility" ) type talosVersionTest struct { host string target string expectedError string } func runTalosVersionTest(t *testing.T, tt talosVersionTest) { t.Run(tt.host+" -> "+tt.target, func(t *testing.T) { host, err := compatibility.ParseTalosVersion(&machine.VersionInfo{ Tag: tt.host, }) require.NoError(t, err) target, err := compatibility.ParseTalosVersion(&machine.VersionInfo{ Tag: tt.target, }) require.NoError(t, err) err = target.UpgradeableFrom(host) if tt.expectedError != "" { require.EqualError(t, err, tt.expectedError) } else { require.NoError(t, err) } }) } func TestTalosUpgradeCompatibility13(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.2.0", target: "1.3.0", }, { host: "1.0.0-alpha.0", target: "1.3.0", }, { host: "1.2.0-alpha.0", target: "1.3.0-alpha.0", }, { host: "1.3.0", target: "1.3.1", }, { host: "1.3.0-beta.0", target: "1.3.0", }, { host: "1.4.5", target: "1.3.3", }, { host: "0.14.3", target: "1.3.0", expectedError: `host version 0.14.3 is too old to upgrade to Talos 1.3.0`, }, { host: "1.5.0-alpha.0", target: "1.3.0", expectedError: `host version 1.5.0-alpha.0 is too new to downgrade to Talos 1.3.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility14(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.3.0", target: "1.4.0", }, { host: "1.0.0-alpha.0", target: "1.4.0", }, { host: "1.2.0-alpha.0", target: "1.4.0-alpha.0", }, { host: "1.4.0", target: "1.4.1", }, { host: "1.4.0-beta.0", target: "1.4.0", }, { host: "1.5.5", target: "1.4.3", }, { host: "0.14.3", target: "1.4.0", expectedError: `host version 0.14.3 is too old to upgrade to Talos 1.4.0`, }, { host: "1.6.0-alpha.0", target: "1.4.0", expectedError: `host version 1.6.0-alpha.0 is too new to downgrade to Talos 1.4.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility15(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.3.0", target: "1.5.0", }, { host: "1.2.0-alpha.0", target: "1.5.0", }, { host: "1.2.0", target: "1.5.0-alpha.0", }, { host: "1.5.0", target: "1.5.1", }, { host: "1.5.0-beta.0", target: "1.5.0", }, { host: "1.6.5", target: "1.5.3", }, { host: "1.1.0", target: "1.5.0", expectedError: `host version 1.1.0 is too old to upgrade to Talos 1.5.0`, }, { host: "1.7.0-alpha.0", target: "1.5.0", expectedError: `host version 1.7.0-alpha.0 is too new to downgrade to Talos 1.5.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility16(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.4.0", target: "1.6.0", }, { host: "1.3.0-alpha.0", target: "1.6.0", }, { host: "1.3.0", target: "1.6.0-alpha.0", }, { host: "1.6.0", target: "1.6.1", }, { host: "1.6.0-beta.0", target: "1.6.0", }, { host: "1.7.5", target: "1.6.3", }, { host: "1.2.0", target: "1.6.0", expectedError: `host version 1.2.0 is too old to upgrade to Talos 1.6.0`, }, { host: "1.8.0-alpha.0", target: "1.6.0", expectedError: `host version 1.8.0-alpha.0 is too new to downgrade to Talos 1.6.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility17(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.5.0", target: "1.7.0", }, { host: "1.4.0-alpha.0", target: "1.7.0", }, { host: "1.4.0", target: "1.7.0-alpha.0", }, { host: "1.6.0", target: "1.7.1", }, { host: "1.6.0-beta.0", target: "1.7.0", }, { host: "1.8.5", target: "1.7.3", }, { host: "1.3.0", target: "1.7.0", expectedError: `host version 1.3.0 is too old to upgrade to Talos 1.7.0`, }, { host: "1.9.0-alpha.0", target: "1.7.0", expectedError: `host version 1.9.0-alpha.0 is too new to downgrade to Talos 1.7.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility18(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.6.0", target: "1.8.0", }, { host: "1.5.0-alpha.0", target: "1.8.0", }, { host: "1.5.0", target: "1.8.0-alpha.0", }, { host: "1.7.0", target: "1.8.1", }, { host: "1.7.0-beta.0", target: "1.8.0", }, { host: "1.9.5", target: "1.8.3", }, { host: "1.4.0", target: "1.8.0", expectedError: `host version 1.4.0 is too old to upgrade to Talos 1.8.0`, }, { host: "1.10.0-alpha.0", target: "1.8.0", expectedError: `host version 1.10.0-alpha.0 is too new to downgrade to Talos 1.8.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility19(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.8.0", target: "1.9.0", }, { host: "1.8.0-alpha.0", target: "1.9.0", }, { host: "1.8.0", target: "1.9.0-alpha.0", }, { host: "1.8.3", target: "1.9.1", }, { host: "1.9.0-beta.0", target: "1.9.0", }, { host: "1.9.5", target: "1.9.3", }, { host: "1.7.0", target: "1.9.0", expectedError: `host version 1.7.0 is too old to upgrade to Talos 1.9.0`, }, { host: "1.11.0-alpha.0", target: "1.9.0", expectedError: `host version 1.11.0-alpha.0 is too new to downgrade to Talos 1.9.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility110(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.8.0", target: "1.10.0", }, { host: "1.9.0-alpha.0", target: "1.10.0", }, { host: "1.8.0", target: "1.10.0-alpha.0", }, { host: "1.9.3", target: "1.10.1", }, { host: "1.10.0-beta.0", target: "1.10.0", }, { host: "1.10.5", target: "1.10.3", }, { host: "1.7.0", target: "1.10.0", expectedError: `host version 1.7.0 is too old to upgrade to Talos 1.10.0`, }, { host: "1.12.0-alpha.0", target: "1.10.0", expectedError: `host version 1.12.0-alpha.0 is too new to downgrade to Talos 1.10.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility111(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.9.0", target: "1.11.0", }, { host: "1.10.0-alpha.0", target: "1.11.0", }, { host: "1.9.0", target: "1.11.0-alpha.0", }, { host: "1.10.3", target: "1.11.1", }, { host: "1.11.0-beta.0", target: "1.11.0", }, { host: "1.11.5", target: "1.11.3", }, { host: "1.8.0", target: "1.11.0", expectedError: `host version 1.8.0 is too old to upgrade to Talos 1.11.0`, }, { host: "1.14.0-alpha.0", target: "1.12.0", expectedError: `host version 1.14.0-alpha.0 is too new to downgrade to Talos 1.12.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility112(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.10.0", target: "1.12.0", }, { host: "1.11.0-alpha.0", target: "1.12.0", }, { host: "1.10.0", target: "1.12.0-alpha.0", }, { host: "1.11.3", target: "1.12.1", }, { host: "1.12.0-beta.0", target: "1.12.0", }, { host: "1.12.5", target: "1.12.3", }, { host: "1.9.0", target: "1.12.0", expectedError: `host version 1.9.0 is too old to upgrade to Talos 1.12.0`, }, { host: "1.14.0-alpha.0", target: "1.12.0", expectedError: `host version 1.14.0-alpha.0 is too new to downgrade to Talos 1.12.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibility113(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.11.0", target: "1.13.0", }, { host: "1.12.0-alpha.0", target: "1.13.0", }, { host: "1.11.0", target: "1.13.0-alpha.0", }, { host: "1.12.3", target: "1.13.1", }, { host: "1.13.0-beta.0", target: "1.13.0", }, { host: "1.13.5", target: "1.13.3", }, { host: "1.10.0", target: "1.13.0", expectedError: `host version 1.10.0 is too old to upgrade to Talos 1.13.0`, }, { host: "1.15.0-alpha.0", target: "1.13.0", expectedError: `host version 1.15.0-alpha.0 is too new to downgrade to Talos 1.13.0`, }, } { runTalosVersionTest(t, tt) } } func TestTalosUpgradeCompatibilityUnsupported(t *testing.T) { for _, tt := range []talosVersionTest{ { host: "1.5.0", target: "1.15.0-alpha.0", expectedError: `upgrades to version 1.15.0-alpha.0 are not supported`, }, { host: "1.4.0", target: "1.14.0-alpha.0", expectedError: `upgrades to version 1.14.0-alpha.0 are not supported`, }, } { runTalosVersionTest(t, tt) } } func TestDisablePredictableNetworkInterfaces(t *testing.T) { t.Parallel() for _, tt := range []struct { host string expected bool }{ { host: "1.3.0", expected: true, }, { host: "1.4.0", expected: true, }, { host: "1.5.0", expected: false, }, { host: "1.6.0", expected: false, }, { host: "1.7.0", expected: false, }, } { t.Run(tt.host, func(t *testing.T) { t.Parallel() host, err := compatibility.ParseTalosVersion(&machine.VersionInfo{ Tag: tt.host, }) require.NoError(t, err) require.Equal(t, tt.expected, host.DisablePredictableNetworkInterfaces()) }) } } func TestSupportsSSAManifestSync(t *testing.T) { t.Parallel() for _, tt := range []struct { version string expected bool }{ { version: "1.12.0", expected: false, }, { version: "1.13.0", expected: true, }, { version: "1.14.0", expected: true, }, { version: "2.0.0", expected: true, }, } { t.Run(tt.version, func(t *testing.T) { t.Parallel() v, err := compatibility.ParseTalosVersion(&machine.VersionInfo{ Tag: tt.version, }) require.NoError(t, err) require.Equal(t, tt.expected, v.SupportsSSAManifestSync()) }) } } ================================================ FILE: pkg/machinery/config/bundle/bundle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package bundle provides a set of machine configuration files. package bundle import ( "errors" "fmt" "io/fs" "os" "path/filepath" "strings" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // Bundle defines a set of machine configuration files. type Bundle struct { InitCfg config.Provider ControlPlaneCfg config.Provider WorkerCfg config.Provider TalosCfg *clientconfig.Config } // NewBundle returns a new bundle of configuration files. // //nolint:gocyclo,cyclop func NewBundle(opts ...Option) (*Bundle, error) { options := DefaultOptions() for _, opt := range opts { if err := opt(&options); err != nil { return nil, err } } bundle := &Bundle{} // Configs already exist, we'll pull them in. if options.ExistingConfigs != "" { if options.InputOptions != nil { return bundle, errors.New("both existing config path and input options specified") } // Pull existing machine configs of each type for _, configType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { data, err := os.ReadFile(filepath.Join(options.ExistingConfigs, strings.ToLower(configType.String())+".yaml")) if err != nil { if configType == machine.TypeInit && errors.Is(err, fs.ErrNotExist) { continue } return bundle, err } unmarshalledConfig, err := configloader.NewFromBytes(data) if err != nil { return nil, err } switch configType { case machine.TypeInit: bundle.InitCfg = unmarshalledConfig case machine.TypeControlPlane: bundle.ControlPlaneCfg = unmarshalledConfig case machine.TypeWorker: bundle.WorkerCfg = unmarshalledConfig case machine.TypeUnknown: fallthrough default: panic("unreachable") } } if err := bundle.applyPatches(options); err != nil { return nil, err } // Pull existing talosconfig talosConfig, err := os.Open(filepath.Join(options.ExistingConfigs, "talosconfig")) if errors.Is(err, fs.ErrNotExist) { // talosconfig is optional return bundle, nil } if err != nil { return bundle, err } defer talosConfig.Close() //nolint:errcheck if bundle.TalosCfg, err = clientconfig.ReadFrom(talosConfig); err != nil { return bundle, err } return bundle, nil } // Handle generating net-new configs if options.Verbose { fmt.Fprintln(os.Stderr, "generating PKI and tokens") } if options.InputOptions == nil { return nil, errors.New("no WithInputOptions is defined") } input, err := generate.NewInput( options.InputOptions.ClusterName, options.InputOptions.Endpoint, options.InputOptions.KubeVersion, options.InputOptions.GenOptions..., ) if err != nil { return bundle, err } for _, configType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { var generatedConfig config.Provider generatedConfig, err = input.Config(configType) if err != nil { return bundle, err } switch configType { case machine.TypeInit: bundle.InitCfg = generatedConfig case machine.TypeControlPlane: bundle.ControlPlaneCfg = generatedConfig case machine.TypeWorker: bundle.WorkerCfg = generatedConfig case machine.TypeUnknown: fallthrough default: panic("unreachable") } } if err = bundle.applyPatches(options); err != nil { return nil, err } bundle.TalosCfg, err = input.Talosconfig() if err != nil { return bundle, err } return bundle, nil } // Init implements the ProviderBundle interface. func (bundle *Bundle) Init() config.Provider { return bundle.InitCfg } // ControlPlane implements the ProviderBundle interface. func (bundle *Bundle) ControlPlane() config.Provider { return bundle.ControlPlaneCfg } // Worker implements the ProviderBundle interface. func (bundle *Bundle) Worker() config.Provider { return bundle.WorkerCfg } // TalosConfig implements the ProviderBundle interface. func (bundle *Bundle) TalosConfig() *clientconfig.Config { return bundle.TalosCfg } // Write config files to output directory. func (bundle *Bundle) Write(outputDir string, commentsFlags encoder.CommentsFlags, types ...machine.Type) error { for _, t := range types { name := strings.ToLower(t.String()) + ".yaml" fullFilePath := filepath.Join(outputDir, name) bytes, err := bundle.Serialize(commentsFlags, t) if err != nil { return err } if err = os.WriteFile(fullFilePath, bytes, 0o644); err != nil { return err } fmt.Fprintf(os.Stderr, "created %s\n", fullFilePath) } return nil } // Serialize returns the config for the provided machine type as bytes. func (bundle *Bundle) Serialize(commentsFlags encoder.CommentsFlags, machineType machine.Type) ([]byte, error) { switch machineType { case machine.TypeInit: return bundle.Init().EncodeBytes(encoder.WithComments(commentsFlags)) case machine.TypeControlPlane: return bundle.ControlPlane().EncodeBytes(encoder.WithComments(commentsFlags)) case machine.TypeWorker: return bundle.Worker().EncodeBytes(encoder.WithComments(commentsFlags)) case machine.TypeUnknown: fallthrough default: return nil, fmt.Errorf("unexpected machine type %v", machineType) } } // ApplyPatches patches every config type with a patch. func (bundle *Bundle) ApplyPatches(patches []configpatcher.Patch, patchControlPlane, patchWorker bool) error { if len(patches) == 0 { return nil } apply := func(in config.Provider) (config.Provider, error) { patched, err := configpatcher.Apply(configpatcher.WithConfig(in), patches) if err != nil { return nil, err } return patched.Config() } var err error if patchControlPlane { bundle.InitCfg, err = apply(bundle.InitCfg) if err != nil { return err } bundle.ControlPlaneCfg, err = apply(bundle.ControlPlaneCfg) if err != nil { return err } } if patchWorker { bundle.WorkerCfg, err = apply(bundle.WorkerCfg) if err != nil { return err } } return nil } func (bundle *Bundle) applyPatches(options Options) error { if err := bundle.ApplyPatches(options.Patches, true, true); err != nil { return fmt.Errorf("error patching configs: %w", err) } if err := bundle.ApplyPatches(options.PatchesControlPlane, true, false); err != nil { return fmt.Errorf("error patching control plane configs: %w", err) } if err := bundle.ApplyPatches(options.PatchesWorker, false, true); err != nil { return fmt.Errorf("error patching worker config: %w", err) } return nil } ================================================ FILE: pkg/machinery/config/bundle/bundle_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package bundle_test import ( "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) func TestGenerateConfig(t *testing.T) { configBundleOpts := []bundle.Option{ //nolint:prealloc // this is a test bundle.WithInputOptions( &bundle.InputOptions{ ClusterName: "test-cluster", Endpoint: "https://127.0.0.1:6443", KubeVersion: "1.222.2", }, ), } patches, err := configpatcher.LoadPatches([]string{"cluster:\n clusterName: foo\n"}) require.NoError(t, err) configBundleOpts = append(configBundleOpts, bundle.WithPatch(patches)) configBundle, err := bundle.NewBundle(configBundleOpts...) require.NoError(t, err) tempDir := t.TempDir() require.NoError(t, configBundle.Write(tempDir, encoder.CommentsAll, machine.TypeControlPlane, machine.TypeWorker)) for _, machineType := range []machine.Type{machine.TypeControlPlane, machine.TypeWorker} { var cfg config.Provider switch machineType { //nolint:exhaustive case machine.TypeControlPlane: cfg, err = configloader.NewFromFile(filepath.Join(tempDir, "controlplane.yaml")) case machine.TypeWorker: cfg, err = configloader.NewFromFile(filepath.Join(tempDir, "worker.yaml")) default: require.FailNow(t, "unexpected machine type") } require.NoError(t, err) assert.Equal(t, "foo", cfg.Cluster().Name()) } } ================================================ FILE: pkg/machinery/config/bundle/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package bundle import ( "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/generate" ) // Option controls config options specific to config bundle generation. type Option func(o *Options) error // InputOptions holds necessary params for generating an input. type InputOptions struct { ClusterName string Endpoint string KubeVersion string GenOptions []generate.Option } // Options describes generate parameters. type Options struct { ExistingConfigs string // path to existing config files Verbose bool // whether to write any logs during generate InputOptions *InputOptions Patches []configpatcher.Patch PatchesControlPlane []configpatcher.Patch PatchesWorker []configpatcher.Patch } // DefaultOptions returns default options. func DefaultOptions() Options { return Options{ Verbose: true, } } // WithExistingConfigs sets the path to existing config files. func WithExistingConfigs(configPath string) Option { return func(o *Options) error { o.ExistingConfigs = configPath return nil } } // WithInputOptions allows passing in of various params for net-new input generation. func WithInputOptions(inputOpts *InputOptions) Option { return func(o *Options) error { o.InputOptions = inputOpts return nil } } // WithVerbose allows setting verbose logging. func WithVerbose(verbose bool) Option { return func(o *Options) error { o.Verbose = verbose return nil } } // WithPatch allows patching every config in a bundle with a patch. func WithPatch(patch []configpatcher.Patch) Option { return func(o *Options) error { o.Patches = append(o.Patches, patch...) return nil } } // WithPatchControlPlane allows patching init and controlplane config in a bundle with a patch. func WithPatchControlPlane(patch []configpatcher.Patch) Option { return func(o *Options) error { o.PatchesControlPlane = append(o.PatchesControlPlane, patch...) return nil } } // WithPatchWorker allows patching worker config in a bundle with a patch. func WithPatchWorker(patch []configpatcher.Patch) Option { return func(o *Options) error { o.PatchesWorker = append(o.PatchesWorker, patch...) return nil } } ================================================ FILE: pkg/machinery/config/config/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "net/netip" "net/url" "time" "github.com/siderolabs/crypto/x509" ) // ClusterConfig defines the requirements for a config that pertains to cluster // related options. // //nolint:interfacebloat type ClusterConfig interface { ID() string Name() string Secret() string APIServer() APIServer ControllerManager() ControllerManager Proxy() Proxy Scheduler() Scheduler Endpoint() *url.URL Token() Token CertSANs() []string IssuingCA() *x509.PEMEncodedCertificateAndKey AcceptedCAs() []*x509.PEMEncodedCertificate AggregatorCA() *x509.PEMEncodedCertificateAndKey ServiceAccount() *x509.PEMEncodedKey AESCBCEncryptionSecret() string SecretboxEncryptionSecret() string Etcd() Etcd Network() ClusterNetwork LocalAPIServerPort() int CoreDNS() CoreDNS // ExternalCloudProvider returns external cloud provider settings. ExternalCloudProvider() ExternalCloudProvider ExtraManifestURLs() []string ExtraManifestHeaderMap() map[string]string InlineManifests() []InlineManifest AdminKubeconfig() AdminKubeconfig ScheduleOnControlPlanes() bool Discovery() Discovery } // ClusterNetwork defines the requirements for a config that pertains to cluster // network options. type ClusterNetwork interface { CNI() CNI PodCIDRs() []string ServiceCIDRs() []string DNSDomain() string // APIServerIPs returns kube-apiserver IPs in the ServiceCIDR. APIServerIPs() ([]netip.Addr, error) // DNSServiceIPs returns DNS service IPs in the ServiceCIDR. DNSServiceIPs() ([]netip.Addr, error) } // CNI defines the requirements for a config that pertains to Kubernetes // cni. type CNI interface { Name() string URLs() []string Flannel() FlannelCNI } // FlannelCNI defines the requirements for a config that pertains to configure Flannel. type FlannelCNI interface { ExtraArgs() []string KubeNetworkPoliciesEnabled() bool } // APIServer defines the requirements for a config that pertains to apiserver related // options. type APIServer interface { Image() string ExtraArgs() map[string][]string ExtraVolumes() []VolumeMount Env() Env AdmissionControl() []AdmissionPlugin AuditPolicy() map[string]any Resources() Resources AuthorizationConfig() []AuthorizationConfigAuthorizer } // AdmissionPlugin defines the API server Admission Plugin configuration. type AdmissionPlugin interface { Name() string Configuration() map[string]any } // AuthorizationConfigAuthorizer defines the API server Authorization Authorizer configuration. type AuthorizationConfigAuthorizer interface { Type() string Name() string Webhook() map[string]any } // ControllerManager defines the requirements for a config that pertains to controller manager related // options. type ControllerManager interface { Image() string ExtraArgs() map[string][]string ExtraVolumes() []VolumeMount Env() Env Resources() Resources } // Proxy defines the requirements for a config that pertains to the kube-proxy // options. type Proxy interface { Enabled() bool Image() string // Mode indicates the proxy mode for kube-proxy. By default, this is `iptables`. Other options include `ipvs`. Mode() string // ExtraArgs describe an additional set of arguments to be supplied to the execution of `kube-proxy` ExtraArgs() map[string][]string } // Scheduler defines the requirements for a config that pertains to scheduler related // options. type Scheduler interface { Image() string ExtraArgs() map[string][]string ExtraVolumes() []VolumeMount Env() Env Resources() Resources Config() map[string]any } // Etcd defines the requirements for a config that pertains to etcd related // options. type Etcd interface { Image() string CA() *x509.PEMEncodedCertificateAndKey ExtraArgs() map[string][]string AdvertisedSubnets() []string ListenSubnets() []string } // Token defines the requirements for a config that pertains to Kubernetes // bootstrap token. type Token interface { ID() string Secret() string } // CoreDNS defines the requirements for a config that pertains to CoreDNS // coredns options. type CoreDNS interface { Enabled() bool Image() string } // ExternalCloudProvider defines settings for external cloud provider. type ExternalCloudProvider interface { // Enabled returns true if external cloud provider is enabled. Enabled() bool // ManifestURLs returns external cloud provider manifest URLs if it is enabled. ManifestURLs() []string } // AdminKubeconfig defines settings for admin kubeconfig. type AdminKubeconfig interface { CommonName() string CertOrganization() string CertLifetime() time.Duration } // VolumeMount describes extra volume mount for the static pods. type VolumeMount interface { Name() string HostPath() string MountPath() string ReadOnly() bool } // Resources describes memory/cpu requests/limits for static pods. type Resources interface { CPURequests() string MemoryRequests() string CPULimits() string MemoryLimits() string } // InlineManifest describes inline manifest for the cluster boostrap. type InlineManifest interface { Name() string Contents() string } // Discovery describes cluster membership discovery. type Discovery interface { Enabled() bool Registries() DiscoveryRegistries } // DiscoveryRegistries describes discovery methods. type DiscoveryRegistries interface { Kubernetes() KubernetesRegistry Service() ServiceRegistry } // KubernetesRegistry describes Kubernetes discovery registry. // //nolint:iface type KubernetesRegistry interface { Enabled() bool } // ServiceRegistry describes external service discovery registry. type ServiceRegistry interface { Enabled() bool Endpoint() string } ================================================ FILE: pkg/machinery/config/config/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package config provides interfaces to consume machine configuration values. package config // Config defines the interface to access contents of the machine configuration. type Config interface { //nolint:interfacebloat // old v1alpha1 interface (to be decomposed as we move to multi-doc) Debug() bool Machine() MachineConfig Cluster() ClusterConfig // new multi-doc interfaces: // - network SideroLink() SideroLinkConfig NetworkRules() NetworkRuleConfig KubespanConfig() KubespanConfig EthernetConfigs() []EthernetConfig RunDefaultDHCPOperators() bool NetworkStaticHostConfig() []NetworkStaticHostConfig NetworkHostnameConfig() NetworkHostnameConfig NetworkResolverConfig() NetworkResolverConfig NetworkTimeSyncConfig() NetworkTimeSyncConfig NetworkKubeSpanConfig() NetworkKubeSpanConfig NetworkCommonLinkConfigs() []NetworkCommonLinkConfig NetworkLinkAliasConfigs() []NetworkLinkAliasConfig NetworkDHCPConfigs() []NetworkDHCPConfig NetworkDHCPv4Configs() []NetworkDHCPv4Config NetworkDHCPv6Configs() []NetworkDHCPv6Config NetworkVirtualIPConfigs() []NetworkVirtualIPConfig NetworkProbeConfigs() []NetworkCommonProbeConfig NetworkBlackholeRouteConfigs() []NetworkBlackholeRouteConfig NetworkRoutingRuleConfigs() []NetworkRoutingRuleConfig // - block devices/storage: Volumes() VolumesConfig UserVolumeConfigs() []UserVolumeConfig RawVolumeConfigs() []RawVolumeConfig ExistingVolumeConfigs() []ExistingVolumeConfig ExternalVolumeConfigs() []ExternalVolumeConfig SwapVolumeConfigs() []SwapVolumeConfig ZswapConfig() ZswapConfig // - cri: RegistryMirrorConfigs() map[string]RegistryMirrorConfig RegistryAuthConfigs() map[string]RegistryAuthConfig RegistryTLSConfigs() map[string]RegistryTLSConfig // - misc: ExtensionServiceConfigs() []ExtensionServiceConfig Runtime() RuntimeConfig Environment() EnvironmentConfig TrustedRoots() TrustedRootsConfig PCIDriverRebindConfig() PCIDriverRebindConfig OOMConfig() OOMConfig ImageVerificationConfig() ImageVerificationConfig } ================================================ FILE: pkg/machinery/config/config/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "github.com/siderolabs/crypto/x509" ) // RegistryMirrorConfigDocument is registry mirror configuration document. type RegistryMirrorConfigDocument interface { NamedDocument RegistryMirrorConfig } // RegistryAuthConfigDocument is registry authentication configuration document. type RegistryAuthConfigDocument interface { NamedDocument RegistryAuthConfig } // RegistryTLSConfigDocument is registry TLS configuration document. type RegistryTLSConfigDocument interface { NamedDocument RegistryTLSConfig } // RegistryMirrorConfig represents mirror configuration for a registry. type RegistryMirrorConfig interface { Endpoints() []RegistryEndpointConfig SkipFallback() bool } // RegistryEndpointConfig represents a single registry endpoint. type RegistryEndpointConfig interface { Endpoint() string OverridePath() bool } // RegistryAuthConfig specifies authentication configuration for a registry. type RegistryAuthConfig interface { Username() string Password() string Auth() string IdentityToken() string } // RegistryTLSConfig specifies TLS config for HTTPS registries. type RegistryTLSConfig interface { ClientIdentity() *x509.PEMEncodedCertificateAndKey CA() []byte InsecureSkipVerify() bool } ================================================ FILE: pkg/machinery/config/config/document.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config // Document is a configuration document. type Document interface { // Clone returns a deep copy of the document. Clone() Document // Kind returns the kind of the document. Kind() string // APIVersion returns the API version of the document. APIVersion() string } // NamedDocument is a configuration document which has a name. type NamedDocument interface { // Name of the document. Name() string } // ConflictingDocument is a configuration document which conflicts with other document. // // If the document is named, it conflicts by name, otherwise it conflicts by kind. type ConflictingDocument interface { ConflictsWithKinds() []string } // SecretDocument is a configuration document that contains secrets. type SecretDocument interface { // Redact does in-place replacement of secrets with the given string. Redact(replacement string) } ================================================ FILE: pkg/machinery/config/config/encoder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import "github.com/siderolabs/talos/pkg/machinery/config/encoder" // Encoder provides the interface to encode configuration documents. type Encoder interface { // Bytes returns source YAML representation (if available) or does default encoding. Bytes() ([]byte, error) // Encode configuration to YAML using the provided options. EncodeString(encoderOptions ...encoder.Option) (string, error) EncodeBytes(encoderOptions ...encoder.Option) ([]byte, error) } ================================================ FILE: pkg/machinery/config/config/extension_service_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config // ExtensionServiceConfig is a config for extension services. type ExtensionServiceConfig interface { Name() string ConfigFiles() []ExtensionServiceConfigFile Environment() []string } // ExtensionServiceConfigFile is a config file for extension services. type ExtensionServiceConfigFile interface { Content() string MountPath() string } ================================================ FILE: pkg/machinery/config/config/helpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "iter" "maps" ) func findFirstValue[T any, R comparable](documents []T, getter func(T) R) R { var zeroR R for _, document := range documents { if value := getter(document); value != zeroR { return value } } return zeroR } func aggregateValues[T any, R any](documents []T, getter func(T) []R) []R { result := make([]R, 0, len(documents)) for _, document := range documents { result = append(result, getter(document)...) } if len(result) == 0 { return nil } return result } func mergeMaps[T any, K comparable, V any](documents []T, getter func(T) iter.Seq2[K, V]) map[K]V { result := make(map[K]V) for _, document := range documents { maps.Insert(result, getter(document)) } return result } func filterDocuments[T any, R any](documents []R) []T { var result []T for _, document := range documents { if document, ok := any(document).(T); ok { result = append(result, document) } } return result } ================================================ FILE: pkg/machinery/config/config/kubespan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import "net/netip" // KubespanConfig defines the interface to access KubeSpan configuration. type KubespanConfig interface { ExtraAnnouncedEndpoints() []netip.AddrPort } // WrapKubespanConfig wraps a list of KubespanConfig into a single KubespanConfig aggregating the results. func WrapKubespanConfig(configs ...KubespanConfig) KubespanConfig { return kubespanConfigWrapper(configs) } type kubespanConfigWrapper []KubespanConfig func (w kubespanConfigWrapper) ExtraAnnouncedEndpoints() []netip.AddrPort { return aggregateValues(w, func(c KubespanConfig) []netip.AddrPort { return c.ExtraAnnouncedEndpoints() }) } ================================================ FILE: pkg/machinery/config/config/machine.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "net/url" "os" "time" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // MachineConfig defines the requirements for a config that pertains to machine // related options. // //nolint:interfacebloat type MachineConfig interface { Install() Install Security() Security Network() MachineNetwork Disks() []Disk Env() Env Files() ([]File, error) Type() machine.Type Controlplane() MachineControlPlane Pods() []map[string]any Kubelet() Kubelet Sysctls() map[string]string Sysfs() map[string]string SystemDiskEncryption() SystemDiskEncryption Features() Features Udev() UdevConfig Logging() Logging Kernel() Kernel SeccompProfiles() []SeccompProfile NodeLabels() NodeLabels NodeAnnotations() NodeAnnotations NodeTaints() NodeTaints BaseRuntimeSpecOverrides() map[string]any } // SeccompProfile defines the requirements for a config that pertains to seccomp // related options. type SeccompProfile interface { Name() string Value() map[string]any } // NodeLabels defines the labels that should be set on a node. type NodeLabels map[string]string // NodeAnnotations defines the annotations that should be set on a node. type NodeAnnotations map[string]string // NodeTaints defines the taints that should be set on a node. type NodeTaints map[string]string // Disk represents the options available for partitioning, formatting, and // mounting extra disks. type Disk interface { Device() string Partitions() []Partition } // Partition represents the options for a device partition. type Partition interface { Size() uint64 MountPoint() string } // Env represents a set of environment variables. type Env = map[string]string // File represents a file to write to disk. type File interface { Content() string Permissions() os.FileMode Path() string Op() string } // Install defines the requirements for a config that pertains to install // related options. type Install interface { Image() string Extensions() []Extension Disk() string DiskMatchExpression() (*cel.Expression, error) ExtraKernelArgs() []string Zero() bool LegacyBIOSSupport() bool GrubUseUKICmdline() bool } // Extension defines the system extension. type Extension interface { Image() string } // Security defines the requirements for a config that pertains to security // related options. type Security interface { IssuingCA() *x509.PEMEncodedCertificateAndKey AcceptedCAs() []*x509.PEMEncodedCertificate Token() string CertSANs() []string } // MachineControlPlane defines the requirements for a config that pertains to Controlplane // related options. type MachineControlPlane interface { ControllerManager() MachineControllerManager Scheduler() MachineScheduler } // MachineControllerManager defines the requirements for a config that pertains to ControllerManager // related options. // //nolint:iface type MachineControllerManager interface { Disabled() bool } // MachineScheduler defines the requirements for a config that pertains to Scheduler // related options. // //nolint:iface type MachineScheduler interface { Disabled() bool } // MachineNetwork defines the requirements for a config that pertains to network // related options. // // This is a legacy interface which is going to be decomposed and removed in the future. type MachineNetwork interface { Devices() []Device } // Device represents a network interface. // //nolint:interfacebloat type Device interface { Interface() string Addresses() []string Routes() []Route Bond() Bond Bridge() Bridge BridgePort() BridgePort Vlans() []Vlan MTU() int DHCP() bool Ignore() bool Dummy() bool DHCPOptions() DHCPOptions VIPConfig() VIPConfig WireguardConfig() WireguardConfig Selector() NetworkDeviceSelector } // DHCPOptions represents a set of DHCP options. type DHCPOptions interface { RouteMetric() uint32 IPv4() bool IPv6() bool DUIDv6() string } // VIPConfig contains settings for the Virtual (shared) IP setup. type VIPConfig interface { IP() string EquinixMetal() VIPEquinixMetal HCloud() VIPHCloud } // VIPEquinixMetal contains Equinix Metal API VIP settings. // //nolint:iface type VIPEquinixMetal interface { APIToken() string } // VIPHCloud contains Hetzner Cloud API VIP settings. // //nolint:iface type VIPHCloud interface { APIToken() string } // WireguardConfig contains settings for configuring Wireguard network interface. type WireguardConfig interface { PrivateKey() string ListenPort() int FirewallMark() int Peers() []WireguardPeer } // WireguardPeer a WireGuard device peer configuration. type WireguardPeer interface { PublicKey() string Endpoint() string PersistentKeepaliveInterval() time.Duration AllowedIPs() []string } // Bond contains the various options for configuring a // bonded interface. // //nolint:interfacebloat type Bond interface { Interfaces() []string Selectors() []NetworkDeviceSelector ARPIPTarget() []string Mode() string HashPolicy() string LACPRate() string ADActorSystem() string ARPValidate() string ARPAllTargets() string Primary() string PrimaryReselect() string FailOverMac() string ADSelect() string MIIMon() uint32 UpDelay() uint32 DownDelay() uint32 ARPInterval() uint32 ResendIGMP() uint32 MinLinks() uint32 LPInterval() uint32 PacketsPerSlave() uint32 NumPeerNotif() uint8 TLBDynamicLB() uint8 AllSlavesActive() uint8 UseCarrier() bool ADActorSysPrio() uint16 ADUserPortKey() uint16 PeerNotifyDelay() uint32 } // STP contains the Spanning Tree Protocol settings for a bridge. // //nolint:iface type STP interface { Enabled() bool } // BridgeVLAN contains the VLAN settings for a bridge. type BridgeVLAN interface { FilteringEnabled() bool } // Bridge contains the options for configuring a bridged interface. type Bridge interface { Interfaces() []string STP() STP VLAN() BridgeVLAN } // BridgePort contains the options for a bridge port. type BridgePort interface { Master() string } // Vlan represents vlan settings for a device. type Vlan interface { Addresses() []string Routes() []Route DHCP() bool ID() uint16 MTU() uint32 Mode() nethelpers.VLANProtocol VIPConfig() VIPConfig DHCPOptions() DHCPOptions } // Route represents a network route. type Route interface { Network() string Gateway() string Source() string Metric() uint32 MTU() uint32 } // NetworkDeviceSelector defines the set of fields that can be used to pick network a device. type NetworkDeviceSelector interface { Bus() string HardwareAddress() string PermanentAddress() string PCIID() string KernelDriver() string Physical() *bool } // Kubelet defines the requirements for a config that pertains to kubelet // related options. // //nolint:interfacebloat type Kubelet interface { Image() string ClusterDNS() []string ExtraArgs() map[string][]string ExtraMounts() []specs.Mount ExtraConfig() map[string]any CredentialProviderConfig() map[string]any DefaultRuntimeSeccompProfileEnabled() bool RegisterWithFQDN() bool NodeIP() KubeletNodeIP SkipNodeRegistration() bool DisableManifestsDirectory() bool } // KubeletNodeIP defines the way node IPs are selected for the kubelet. type KubeletNodeIP interface { ValidSubnets() []string } // EncryptionKey defines settings for the partition encryption key handling. type EncryptionKey interface { Static() EncryptionKeyStatic NodeID() EncryptionKeyNodeID KMS() EncryptionKeyKMS Slot() int TPM() EncryptionKeyTPM LockToSTATE() bool } // EncryptionKeyStatic ephemeral encryption key. type EncryptionKeyStatic interface { Key() []byte String() string } // EncryptionKeyKMS encryption key sealed by KMS. type EncryptionKeyKMS interface { Endpoint() string String() string } // EncryptionKeyNodeID deterministically generated encryption key. type EncryptionKeyNodeID interface { String() string } // EncryptionKeyTPM encryption key sealed by TPM. type EncryptionKeyTPM interface { CheckSecurebootOnEnroll() bool PCRs() []int PubKeyPCRs() []int String() string } // EncryptionConfig defines settings for the partition encryption. type EncryptionConfig interface { Provider() block.EncryptionProviderType Cipher() string KeySize() uint BlockSize() uint64 Options() []string Keys() []EncryptionKey } // SystemDiskEncryption accumulates settings for all system partitions encryption. type SystemDiskEncryption interface { Get(label string) EncryptionConfig } // Features describe individual Talos features that can be switched on or off. type Features interface { KubernetesTalosAPIAccess() KubernetesTalosAPIAccess DiskQuotaSupportEnabled() bool HostDNS() HostDNS KubePrism() KubePrism ImageCache() ImageCache NodeAddressSortAlgorithm() nethelpers.AddressSortAlgorithm } // KubernetesTalosAPIAccess describes the Kubernetes Talos API access features. type KubernetesTalosAPIAccess interface { Enabled() bool AllowedRoles() []string AllowedKubernetesNamespaces() []string } // KubePrism describes the API Server load balancer features. type KubePrism interface { Enabled() bool Port() int } // HostDNS describes the host DNS configuration. type HostDNS interface { Enabled() bool ForwardKubeDNSToHost() bool ResolveMemberNames() bool } // ImageCache describes the image cache configuration. type ImageCache interface { LocalEnabled() bool } // UdevConfig describes configuration for udev. type UdevConfig interface { Rules() []string } // Logging describes logging configuration. type Logging interface { Destinations() []LoggingDestination } // LoggingDestination describes logging destination. type LoggingDestination interface { Endpoint() *url.URL ExtraTags() map[string]string Format() string } // Kernel describes Talos Linux kernel configuration. type Kernel interface { Modules() []KernelModule } // KernelModule describes Linux module to load. type KernelModule interface { Name() string Parameters() []string } ================================================ FILE: pkg/machinery/config/config/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "net/netip" "time" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // NetworkRuleConfig defines the interface to access network firewall configuration. type NetworkRuleConfig interface { NetworkRuleConfigRules NetworkRuleConfigDefaultAction } // NetworkRuleConfigRules defines the interface to access network firewall configuration. type NetworkRuleConfigRules interface { Rules() []NetworkRule } // NetworkRuleConfigDefaultAction defines the interface to access network firewall configuration. type NetworkRuleConfigDefaultAction interface { DefaultAction() nethelpers.DefaultAction } // NetworkRuleConfigSignal is used to signal documents which implement either of the NetworkRuleConfig interfaces. type NetworkRuleConfigSignal interface { NetworkRuleConfigSignal() } // NetworkRule defines a network firewall rule. type NetworkRule interface { Protocol() nethelpers.Protocol PortRanges() [][2]uint16 Subnets() []netip.Prefix ExceptSubnets() []netip.Prefix } // WrapNetworkRuleConfigList wraps a list of NetworkConfig into a single NetworkConfig aggregating the results. func WrapNetworkRuleConfigList(configs ...NetworkRuleConfigSignal) NetworkRuleConfig { return networkRuleConfigWrapper(configs) } type networkRuleConfigWrapper []NetworkRuleConfigSignal func (w networkRuleConfigWrapper) DefaultAction() nethelpers.DefaultAction { // DefaultAction zero value is 'accept' which is the default config value as well. return findFirstValue( filterDocuments[NetworkRuleConfigDefaultAction](w), func(c NetworkRuleConfigDefaultAction) nethelpers.DefaultAction { return c.DefaultAction() }, ) } func (w networkRuleConfigWrapper) Rules() []NetworkRule { return aggregateValues( filterDocuments[NetworkRuleConfigRules](w), func(c NetworkRuleConfigRules) []NetworkRule { return c.Rules() }, ) } // EthernetConfig defines a network interface configuration. type EthernetConfig interface { NamedDocument Rings() EthernetRingsConfig Channels() EthernetChannelsConfig Features() map[string]bool WakeOnLAN() []nethelpers.WOLMode } // EthernetRingsConfig defines a configuration for Ethernet link rings. type EthernetRingsConfig struct { RX *uint32 TX *uint32 RXMini *uint32 RXJumbo *uint32 RXBufLen *uint32 CQESize *uint32 TXPush *bool RXPush *bool TXPushBufLen *uint32 TCPDataSplit *bool } // EthernetChannelsConfig defines a configuration for Ethernet link channels. type EthernetChannelsConfig struct { RX *uint32 TX *uint32 Other *uint32 Combined *uint32 } // NetworkStaticHostConfig defines a static host configuration. type NetworkStaticHostConfig interface { IP() string Aliases() []string } // NetworkHostnameConfig defines a hostname configuration. type NetworkHostnameConfig interface { Hostname() string AutoHostname() nethelpers.AutoHostnameKind } // NetworkResolverConfig defines a resolver configuration. type NetworkResolverConfig interface { Resolvers() []netip.Addr SearchDomains() []string DisableSearchDomain() bool } // NetworkTimeSyncConfig defines the requirements for a config that pertains to time related // options. type NetworkTimeSyncConfig interface { Disabled() bool Servers() []string BootTimeout() time.Duration } // NetworkPhysicalLinkConfig defines a physical network link configuration. type NetworkPhysicalLinkConfig interface { PhysicalLinkConfig() NetworkCommonLinkConfig } // NetworkDummyLinkConfig defines a dummy network link configuration. type NetworkDummyLinkConfig interface { DummyLinkConfig() NetworkHardwareAddressConfig NetworkCommonLinkConfig } // NetworkHardwareAddressConfig defines a hardware (MAC) address configuration. type NetworkHardwareAddressConfig interface { HardwareAddress() optional.Optional[nethelpers.HardwareAddr] } // NetworkCommonLinkConfig defines common configuration for network links. type NetworkCommonLinkConfig interface { NamedDocument Up() optional.Optional[bool] MTU() optional.Optional[uint32] Addresses() []NetworkAddressConfig Routes() []NetworkRouteConfig Multicast() optional.Optional[bool] } // NetworkAddressConfig defines a network address configuration. type NetworkAddressConfig interface { Address() netip.Prefix RoutePriority() optional.Optional[uint32] } // NetworkRouteConfig defines a network route configuration. type NetworkRouteConfig interface { Destination() optional.Optional[netip.Prefix] Gateway() optional.Optional[netip.Addr] Source() optional.Optional[netip.Addr] MTU() optional.Optional[uint32] Metric() optional.Optional[uint32] Table() optional.Optional[nethelpers.RoutingTable] } // NetworkLinkAliasConfig defines a network link alias configuration. type NetworkLinkAliasConfig interface { NamedDocument LinkSelector() cel.Expression IsPatternAlias() bool } // NetworkDHCPConfig defines a DHCP configuration for a network link. type NetworkDHCPConfig interface { NamedDocument NetworkDHCPConfig() } // NetworkDHCPv4Config defines a DHCPv4 configuration for a network link. type NetworkDHCPv4Config interface { NamedDocument NetworkDHCPConfig NetworkDHCPv4Config() // signal method RouteMetric() optional.Optional[uint32] IgnoreHostname() optional.Optional[bool] ClientIdentifier() nethelpers.ClientIdentifier DUIDRaw() optional.Optional[nethelpers.HardwareAddr] } // NetworkDHCPv6Config defines a DHCPv6 configuration for a network link. type NetworkDHCPv6Config interface { NamedDocument NetworkDHCPConfig NetworkDHCPv6Config() // signal method RouteMetric() optional.Optional[uint32] IgnoreHostname() optional.Optional[bool] ClientIdentifier() nethelpers.ClientIdentifier DUIDRaw() optional.Optional[nethelpers.HardwareAddr] } // NetworkVirtualIPConfig defines a common virtual IP configuration. // //nolint:iface type NetworkVirtualIPConfig interface { NamedDocument Link() string VIP() netip.Addr } // NetworkLayer2VIPConfig defines a Layer 2 VIP configuration. // //nolint:iface type NetworkLayer2VIPConfig interface { NetworkVirtualIPConfig } // NetworkHCloudVIPConfig defines a Hetzner Cloud VIP configuration. type NetworkHCloudVIPConfig interface { NetworkVirtualIPConfig HCloudAPIToken() string } // NetworkVLANConfig defines a VLAN link configuration. type NetworkVLANConfig interface { NamedDocument NetworkCommonLinkConfig VLANConfig() VLANID() uint16 ParentLink() string VLANMode() optional.Optional[nethelpers.VLANProtocol] } // NetworkBondConfig defines a bond link configuration. // //nolint:interfacebloat type NetworkBondConfig interface { NamedDocument NetworkCommonLinkConfig NetworkHardwareAddressConfig BondConfig() Links() []string Mode() nethelpers.BondMode MIIMon() optional.Optional[uint32] UpDelay() optional.Optional[uint32] DownDelay() optional.Optional[uint32] UseCarrier() optional.Optional[bool] XmitHashPolicy() optional.Optional[nethelpers.BondXmitHashPolicy] ARPInterval() optional.Optional[uint32] ARPIPTargets() []netip.Addr NSIP6Targets() []netip.Addr ARPValidate() optional.Optional[nethelpers.ARPValidate] ARPAllTargets() optional.Optional[nethelpers.ARPAllTargets] LACPRate() optional.Optional[nethelpers.LACPRate] FailOverMAC() optional.Optional[nethelpers.FailOverMAC] ADSelect() optional.Optional[nethelpers.ADSelect] ADActorSysPrio() optional.Optional[uint16] ADUserPortKey() optional.Optional[uint16] ADLACPActive() optional.Optional[nethelpers.ADLACPActive] PrimaryReselect() optional.Optional[nethelpers.PrimaryReselect] ResendIGMP() optional.Optional[uint32] MinLinks() optional.Optional[uint32] LPInterval() optional.Optional[uint32] PacketsPerSlave() optional.Optional[uint32] NumPeerNotif() optional.Optional[uint8] TLBDynamicLB() optional.Optional[uint8] AllSlavesActive() optional.Optional[uint8] PeerNotifyDelay() optional.Optional[uint32] MissedMax() optional.Optional[uint8] } // NetworkBridgeConfig defines a bridge link configuration. type NetworkBridgeConfig interface { NamedDocument NetworkCommonLinkConfig NetworkHardwareAddressConfig BridgeConfig() Links() []string STP() BridgeSTPConfig VLAN() BridgeVLANConfig } // BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration. type BridgeSTPConfig interface { Enabled() optional.Optional[bool] } // BridgeVLANConfig is a bridge VLAN configuration. type BridgeVLANConfig interface { FilteringEnabled() optional.Optional[bool] } // NetworkVRFConfig defines a vrf link configuration. type NetworkVRFConfig interface { NamedDocument NetworkCommonLinkConfig NetworkHardwareAddressConfig VRFConfig() Links() []string Table() nethelpers.RoutingTable } // NetworkWireguardConfig defines a Wireguard link configuration. type NetworkWireguardConfig interface { NamedDocument NetworkCommonLinkConfig WireguardConfig() PrivateKey() string ListenPort() optional.Optional[int] FirewallMark() optional.Optional[int] Peers() []NetworkWireguardPeerConfig } // NetworkWireguardPeerConfig defines a Wireguard peer configuration. type NetworkWireguardPeerConfig interface { PublicKey() string PresharedKey() optional.Optional[string] Endpoint() optional.Optional[string] AllowedIPs() []netip.Prefix PersistentKeepalive() optional.Optional[time.Duration] } // NetworkKubeSpanConfig configures KubeSpan feature. type NetworkKubeSpanConfig interface { Enabled() bool ForceRouting() bool AdvertiseKubernetesNetworks() bool HarvestExtraEndpoints() bool MTU() uint32 Filters() NetworkKubeSpanFilters } // NetworkKubeSpanFilters configures KubeSpan filters. type NetworkKubeSpanFilters interface { Endpoints() []string ExcludeAdvertisedNetworks() []netip.Prefix } // NetworkCommonProbeConfig defines a network connectivity probe configuration. type NetworkCommonProbeConfig interface { NamedDocument Interval() time.Duration FailureThreshold() int } // NetworkTCPProbeConfig defines a TCP probe configuration. type NetworkTCPProbeConfig interface { NetworkCommonProbeConfig Endpoint() string Timeout() time.Duration } // NetworkBlackholeRouteConfig defines a blackhole route configuration. type NetworkBlackholeRouteConfig interface { NamedDocument BlackholeRouteConfig() Metric() optional.Optional[uint32] } // NetworkRoutingRuleConfig defines a policy routing rule configuration. // //nolint:interfacebloat type NetworkRoutingRuleConfig interface { NamedDocument RoutingRuleConfig() Src() optional.Optional[netip.Prefix] Dst() optional.Optional[netip.Prefix] Table() nethelpers.RoutingTable Action() nethelpers.RoutingRuleAction Priority() uint32 IIFName() string OIFName() string FwMark() uint32 FwMask() uint32 } ================================================ FILE: pkg/machinery/config/config/pci_driver_rebind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config // PCIDriverRebindConfig defines the interface to access PCI rebind configuration. type PCIDriverRebindConfig interface { PCIDriverRebindConfigs() []PCIDriverRebindConfigDriver } // PCIDriverRebindConfigDriver defines the interface to access PCI rebind configuration. type PCIDriverRebindConfigDriver interface { PCIID() string TargetDriver() string } // WrapPCIDriverRebindConfig wraps a list of PCIDriverRebindConfig into a single PCIDriverRebindConfig aggregating the results. func WrapPCIDriverRebindConfig(configs ...PCIDriverRebindConfig) PCIDriverRebindConfig { return pciDriverRebindConfigWrapper(configs) } type pciDriverRebindConfigWrapper []PCIDriverRebindConfig func (w pciDriverRebindConfigWrapper) PCIDriverRebindConfigs() []PCIDriverRebindConfigDriver { return aggregateValues(w, func(c PCIDriverRebindConfig) []PCIDriverRebindConfigDriver { return c.PCIDriverRebindConfigs() }) } ================================================ FILE: pkg/machinery/config/config/runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "iter" "maps" "net/url" "time" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/cel" ) // RuntimeConfig defines the interface to access Talos runtime configuration. type RuntimeConfig interface { EventsEndpoint() *string KmsgLogURLs() []*url.URL WatchdogTimer() WatchdogTimerConfig } // EnvironmentConfig defines the interface to access Talos environment configuration. type EnvironmentConfig interface { Variables() map[string]string } // WrapEnvironmentConfigList wraps a list of EnvironmentConfig into a single EnvironmentConfig aggregating the results. func WrapEnvironmentConfigList(configs ...EnvironmentConfig) EnvironmentConfig { return environmentConfigWrapper(configs) } type environmentConfigWrapper []EnvironmentConfig func (w environmentConfigWrapper) Variables() map[string]string { return mergeMaps(w, func(c EnvironmentConfig) iter.Seq2[string, string] { return maps.All(c.Variables()) }) } // WatchdogTimerConfig defines the interface to access Talos watchdog timer configuration. type WatchdogTimerConfig interface { Device() string Timeout() time.Duration } // WrapRuntimeConfigList wraps a list of RuntimeConfig into a single RuntimeConfig aggregating the results. func WrapRuntimeConfigList(configs ...RuntimeConfig) RuntimeConfig { return runtimeConfigWrapper(configs) } type runtimeConfigWrapper []RuntimeConfig func (w runtimeConfigWrapper) EventsEndpoint() *string { return findFirstValue(w, func(c RuntimeConfig) *string { return c.EventsEndpoint() }) } func (w runtimeConfigWrapper) KmsgLogURLs() []*url.URL { return aggregateValues(w, func(c RuntimeConfig) []*url.URL { return c.KmsgLogURLs() }) } func (w runtimeConfigWrapper) WatchdogTimer() WatchdogTimerConfig { return findFirstValue(w, func(c RuntimeConfig) WatchdogTimerConfig { return c.WatchdogTimer() }) } // OOMConfig defines the interface to access OOM configuration. type OOMConfig interface { TriggerExpression() optional.Optional[cel.Expression] CgroupRankingExpression() optional.Optional[cel.Expression] SampleInterval() optional.Optional[time.Duration] } ================================================ FILE: pkg/machinery/config/config/security.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config // TrustedRootsConfig defines the interface to access trusted roots configuration. type TrustedRootsConfig interface { ExtraTrustedRootCertificates() []string } // WrapTrustedRootsConfig wraps a list of TrustedRootsConfig into a single TrustedRootsConfig aggregating the results. func WrapTrustedRootsConfig(configs ...TrustedRootsConfig) TrustedRootsConfig { return trustedRootConfigWrapper(configs) } type trustedRootConfigWrapper []TrustedRootsConfig func (w trustedRootConfigWrapper) ExtraTrustedRootCertificates() []string { return aggregateValues(w, func(c TrustedRootsConfig) []string { return c.ExtraTrustedRootCertificates() }) } // ImageVerificationConfig specifies image signature verification policy. type ImageVerificationConfig interface { // Rules returns the list of verification rules. Rules() []ImageVerificationRule } // ImageVerificationRule represents a rule for image verification. type ImageVerificationRule interface { // ImagePattern returns the image name pattern. ImagePattern() string // Skip returns true if verification should be skipped for this rule. Skip() bool // Deny returns true if pulling images matching the pattern should be denied. Deny() bool // VerifierKeyless returns the keyless verifier to use for this rule (optional). VerifierKeyless() ImageKeylessVerifier // VerifierPublicKey returns the public key verifier to use for this rule (optional). VerifierPublicKey() ImagePublicKeyVerifier } // ImageKeylessVerifier represents a signature verification provider with keyless verification. type ImageKeylessVerifier interface { // Issuer returns the OIDC issuer URL. Issuer() string // Subject returns the expected subject (email, URI, etc). Subject() string // SubjectRegex returns the regex pattern for subject matching. SubjectRegex() string } // ImagePublicKeyVerifier represents a signature verification provider with static public key. type ImagePublicKeyVerifier interface { // Certificate returns a public certificate in PEM format accepted for image signature verification. Certificate() string } ================================================ FILE: pkg/machinery/config/config/siderolink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import "net/url" // SideroLinkConfig defines the interface to access SideroLink configuration. type SideroLinkConfig interface { APIUrl() *url.URL UniqueToken() string } ================================================ FILE: pkg/machinery/config/config/validate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "context" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // Validator is the interface to validate configuration. // // Validator might be implemented by a Container and a single Document. type Validator interface { // Validate checks configuration and returns warnings and fatal errors (as multierror). Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) } // RuntimeValidator is the interface to validate configuration in the runtime context. // // This interface is used by Talos itself to validate configuration on the machine (vs. the Validator interface). // // The errors reported by Validator & RuntimeValidator are different. type RuntimeValidator interface { // RuntimeValidate validates the config in the runtime context. // // The method returns warnings and fatal errors (as multierror). RuntimeValidate(context.Context, state.State, validation.RuntimeMode, ...validation.Option) ([]string, error) } ================================================ FILE: pkg/machinery/config/config/volume.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumesConfig defines the interface to access volume configuration. type VolumesConfig interface { // ByName returns a volume config configuration by name. // // If the configuration is missing, the method a stub which returns implements 'nothing is set' stub. ByName(name string) (VolumeConfig, bool) } // VolumeConfig defines the interface to access volume configuration. type VolumeConfig interface { NamedDocument Provisioning() VolumeProvisioningConfig Encryption() EncryptionConfig Mount() VolumeMountConfig } // VolumeProvisioningConfig defines the interface to access volume provisioning configuration. type VolumeProvisioningConfig interface { DiskSelector() optional.Optional[cel.Expression] Grow() optional.Optional[bool] MinSize() optional.Optional[uint64] MaxSize() optional.Optional[uint64] RelativeMaxSize() optional.Optional[uint64] MaxSizeNegative() bool } // WrapVolumesConfigList wraps a list of VolumeConfig providing access by name. func WrapVolumesConfigList(configs ...VolumeConfig) VolumesConfig { return volumesConfigWrapper(configs) } type volumesConfigWrapper []VolumeConfig func (w volumesConfigWrapper) ByName(name string) (VolumeConfig, bool) { for _, doc := range w { if doc.Name() == name { return doc, true } } return emptyVolumeConfig{}, false } type emptyVolumeConfig struct{} func (emptyVolumeConfig) Name() string { return "" } func (emptyVolumeConfig) Provisioning() VolumeProvisioningConfig { return emptyVolumeConfig{} } func (emptyVolumeConfig) Encryption() EncryptionConfig { return nil } func (emptyVolumeConfig) DiskSelector() optional.Optional[cel.Expression] { return optional.None[cel.Expression]() } func (emptyVolumeConfig) Grow() optional.Optional[bool] { return optional.None[bool]() } func (emptyVolumeConfig) MinSize() optional.Optional[uint64] { return optional.None[uint64]() } func (emptyVolumeConfig) MaxSize() optional.Optional[uint64] { return optional.None[uint64]() } func (emptyVolumeConfig) RelativeMaxSize() optional.Optional[uint64] { return optional.None[uint64]() } func (emptyVolumeConfig) MaxSizeNegative() bool { return false } func (emptyVolumeConfig) Mount() VolumeMountConfig { return emptyVolumeMountConfig{} } type emptyVolumeMountConfig struct{} func (emptyVolumeMountConfig) DisableAccessTime() bool { return false } func (emptyVolumeMountConfig) Secure() bool { return true } func (emptyVolumeMountConfig) ReadOnly() bool { return false } // UserVolumeConfig defines the interface to access user volume configuration. type UserVolumeConfig interface { NamedDocument UserVolumeConfigSignal() Type() optional.Optional[block.VolumeType] Provisioning() VolumeProvisioningConfig Filesystem() FilesystemConfig Encryption() EncryptionConfig Mount() UserVolumeMountConfig } // RawVolumeConfig defines the interface to access raw volume configuration. type RawVolumeConfig interface { NamedDocument RawVolumeConfigSignal() Provisioning() VolumeProvisioningConfig Encryption() EncryptionConfig } // ExistingVolumeConfig defines the interface to access existing volume configuration. type ExistingVolumeConfig interface { NamedDocument ExistingVolumeConfigSignal() VolumeDiscovery() VolumeDiscoveryConfig Mount() ExistingVolumeMountConfig } // ExternalVolumeConfig defines the interface to access external volume configuration. type ExternalVolumeConfig interface { NamedDocument ExternalVolumeConfigSignal() Type() block.FilesystemType Mount() ExternalVolumeMountConfig } // VolumeDiscoveryConfig defines the interface to discover volumes. type VolumeDiscoveryConfig interface { VolumeSelector() cel.Expression } // VolumeMountConfig defines the interface to access volume mount configuration. type VolumeMountConfig interface { Secure() bool } // UserVolumeMountConfig defines the interface to access volume mount configuration. type UserVolumeMountConfig interface { VolumeMountConfig DisableAccessTime() bool } // ExistingVolumeMountConfig defines the interface to access volume mount configuration. type ExistingVolumeMountConfig interface { UserVolumeMountConfig ReadOnly() bool } // ExternalVolumeMountConfig defines the interface to access volume mount configuration. type ExternalVolumeMountConfig interface { ExistingVolumeMountConfig Virtiofs() optional.Optional[ExternalVolumeMountConfigSpec] } // ExternalVolumeMountConfigSpec defines the interface to access external mount configuration spec. type ExternalVolumeMountConfigSpec interface { Source() string Parameters() ([]block.ParameterSpec, error) } // FilesystemConfig defines the interface to access filesystem configuration. type FilesystemConfig interface { // Type returns the filesystem type. Type() block.FilesystemType // ProjectQuotaSupport returns true if the filesystem should support project quotas. ProjectQuotaSupport() bool } // SwapVolumeConfig defines the interface to access swap volume configuration. type SwapVolumeConfig interface { NamedDocument SwapVolumeConfigSignal() Provisioning() VolumeProvisioningConfig Encryption() EncryptionConfig } // ZswapConfig defines the interface to access zswap configuration. type ZswapConfig interface { ZswapConfigSignal() MaxPoolPercent() int ShrinkerEnabled() bool } ================================================ FILE: pkg/machinery/config/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package config provides methods to generate and consume Talos configuration. package config //go:generate go tool github.com/siderolabs/talos/tools/docgen -generate-schema-from-dir types/ -json-schema-output schemas/config.schema.json -version-tag-file ../gendata/data/tag import "github.com/siderolabs/talos/pkg/machinery/config/config" // Config defines the interface to access contents of the machine configuration. type Config = config.Config ================================================ FILE: pkg/machinery/config/config_schema_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( _ "embed" "net/netip" "net/url" "strings" "testing" validatejsonschema "github.com/santhosh-tekuri/jsonschema/v6" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed schemas/config.schema.json var schemaData string func TestSchemaValidation(t *testing.T) { t.Parallel() schemaJSON, err := validatejsonschema.UnmarshalJSON(strings.NewReader(schemaData)) require.NoError(t, err) compiler := validatejsonschema.NewCompiler() err = compiler.AddResource("test-id", schemaJSON) require.NoError(t, err) schema, err := compiler.Compile("test-id") require.NoError(t, err) for _, test := range []struct { name string config map[string]any expectedErrorContains string }{ { name: "v1alpha1_valid", config: newV1Alpha1Config(t, nil, nil), }, { name: "v1alpha1_invalid-version", config: newV1Alpha1Config(t, func(config *v1alpha1.Config) { config.ConfigVersion = "v1alpha2" }, nil), expectedErrorContains: `value must be 'v1alpha1'`, }, { name: "v1alpha1_invalid-control-plane-endpoint", config: newV1Alpha1Config(t, func(config *v1alpha1.Config) { endpointURL, urlErr := url.Parse("ftp://127.0.0.1:6443") require.NoError(t, urlErr) config.ClusterConfig.ControlPlane.Endpoint = &v1alpha1.Endpoint{ URL: endpointURL, } }, nil), expectedErrorContains: `does not match pattern '^https://'`, }, { name: "v1alpha1_invalid-duration", config: newV1Alpha1Config(t, nil, func(rawConfig map[string]any) { setNestedField(t, rawConfig, "100y", "cluster", "adminKubeconfig", "certLifetime") }), expectedErrorContains: `does not match pattern`, }, { name: "v1alpha1_invalid-machine-type", config: newV1Alpha1Config(t, func(config *v1alpha1.Config) { config.MachineConfig.MachineType = "invalidtype" }, nil), expectedErrorContains: `value must be one of 'controlplane', 'worker'`, }, { name: "network/RuleConfigV1Alpha1_valid", config: newRuleConfigV1Alpha1(t, nil, nil), }, { name: "network/RuleConfigV1Alpha1_invalid-cidr-prefix", config: newRuleConfigV1Alpha1(t, nil, func(rawConfig map[string]any) { rawConfig["ingress"] = []any{ map[string]any{ "subnet": "10.42.0.0/16", "except": "10.42.43.0/24", }, map[string]any{ "subnet": "192.168.178.0/24", "except": "invalid-except/12343", }, } }), expectedErrorContains: "'/ingress/1/except': 'invalid-except/12343' does not match pattern", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() testErr := schema.Validate(test.config) if test.expectedErrorContains != "" { errors := gatherValidationErrors(t, testErr) errorsStr := strings.Join(errors, "\n") assert.Contains(t, errorsStr, test.expectedErrorContains) } else { assert.NoError(t, testErr) } }) } } func gatherValidationErrors(t *testing.T, err error) []string { var validationErr *validatejsonschema.ValidationError require.ErrorAs(t, err, &validationErr) messages := make([]string, 0, len(validationErr.Causes)+1) if len(validationErr.Causes) == 0 { messages = append(messages, validationErr.Error()) } for _, cause := range validationErr.Causes { messages = append(messages, gatherValidationErrors(t, cause)...) } return messages } func newV1Alpha1Config(t *testing.T, modifications func(config *v1alpha1.Config), rawModifications func(rawConfig map[string]any)) map[string]any { input, err := generate.NewInput("test", "https://doesntmatter:6443", constants.DefaultKubernetesVersion) require.NoError(t, err) config, err := input.Config(machine.TypeControlPlane) require.NoError(t, err) if modifications != nil { modifications(config.RawV1Alpha1()) } configBytes, err := config.Bytes() require.NoError(t, err) var data map[string]any require.NoError(t, yaml.Unmarshal(configBytes, &data)) if rawModifications != nil { rawModifications(data) } // deprecated field delete(data, "persist") return data } func newRuleConfigV1Alpha1(t *testing.T, modifications func(config *network.RuleConfigV1Alpha1), rawModifications func(rawConfig map[string]any)) map[string]any { config := network.NewRuleConfigV1Alpha1() config.MetaName = "something" config.PortSelector = network.RulePortSelector{ Ports: network.PortRanges{ {Lo: 1000, Hi: 2000}, {Lo: 3000, Hi: 4000}, }, Protocol: nethelpers.ProtocolTCP, } config.Ingress = network.IngressConfig{ { Subnet: netip.MustParsePrefix("10.42.0.0/16"), Except: network.Prefix{Prefix: netip.MustParsePrefix("10.42.43.0/24")}, }, } if modifications != nil { modifications(config) } configBytes, err := yaml.Marshal(config) require.NoError(t, err) var data map[string]any require.NoError(t, yaml.Unmarshal(configBytes, &data)) if rawModifications != nil { rawModifications(data) } return data } func setNestedField(t *testing.T, obj map[string]any, value any, fields ...string) { m := obj for i, field := range fields[:len(fields)-1] { if val, ok := m[field]; ok { valMap, valMapOk := val.(map[string]any) require.Truef(t, valMapOk, "value cannot be set because %v is not a map[string]any", jsonPath(fields[:i+1])) m = valMap } else { newVal := make(map[string]any) m[field] = newVal m = newVal } } m[fields[len(fields)-1]] = value } func jsonPath(fields []string) string { return "." + strings.Join(fields, ".") } ================================================ FILE: pkg/machinery/config/configdiff/configdiff.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package configdiff provides a way to compare two config trees. package configdiff import ( "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/textdiff" ) // DiffConfigs returns a string representation of the diff between two machine configurations. // // One of the resources might be nil. func DiffConfigs(oldCfg, newCfg config.Encoder) (string, error) { var ( oldYaml, newYaml []byte err error ) if oldCfg != nil { oldYaml, err = oldCfg.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return "", err } } if newCfg != nil { newYaml, err = newCfg.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return "", err } } return textdiff.Diff(string(oldYaml), string(newYaml)) } ================================================ FILE: pkg/machinery/config/configdiff/configdiff_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configdiff_test import ( "net/url" "testing" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configdiff" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestDiffString(t *testing.T) { t.Parallel() v1alpha1Cfg := &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineToken: "foo", }, } v1alpha1CfgOther := v1alpha1Cfg.DeepCopy() v1alpha1CfgOther.MachineConfig.MachineType = "worker" siderolinkConfig := siderolink.NewConfigV1Alpha1() siderolinkConfig.APIUrlConfig = meta.URL{ URL: must.Value(url.Parse("https://example.com"))(t), } for _, test := range []struct { name string oldCfg []config.Document newCfg []config.Document want string }{ { name: "empty", oldCfg: nil, newCfg: nil, want: "", }, { name: "same", oldCfg: []config.Document{v1alpha1Cfg}, newCfg: []config.Document{v1alpha1Cfg}, want: "", }, { name: "new doc", oldCfg: []config.Document{v1alpha1Cfg}, newCfg: []config.Document{v1alpha1Cfg, siderolinkConfig}, want: "--- a\n+++ b\n@@ -4,3 +4,7 @@\n token: foo\n certSANs: []\n cluster: null\n+---\n+apiVersion: v1alpha1\n+kind: SideroLinkConfig\n+apiUrl: https://example.com\n", }, { name: "updated field", oldCfg: []config.Document{v1alpha1Cfg, siderolinkConfig}, newCfg: []config.Document{v1alpha1CfgOther, siderolinkConfig}, want: "--- a\n+++ b\n@@ -1,6 +1,6 @@\n version: v1alpha1\n machine:\n- type: controlplane\n+ type: worker\n token: foo\n certSANs: []\n cluster: null\n", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() oldCfg := must.Value(container.New(test.oldCfg...))(t) newCfg := must.Value(container.New(test.newCfg...))(t) got, err := configdiff.DiffConfigs(oldCfg, newCfg) require.NoError(t, err) require.Equal(t, test.want, got) }) } } ================================================ FILE: pkg/machinery/config/configdiff/mergepatch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configdiff import ( "bytes" "fmt" "maps" "reflect" "go.yaml.in/yaml/v4" configcore "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/internal/documentid" ) type unstructured map[string]any // Patch will return a slice of Patches capable of converting the original config to the modified config when applied in order. func Patch(original, modified configcore.Provider) ([]configpatcher.Patch, error) { patches := make([]configpatcher.Patch, 0, 2) firstPass, err := patch(original, modified, true) if err != nil { return nil, err } if firstPass != nil { firstPatch, ok := (*firstPass).(configpatcher.Patch) if !ok { return nil, fmt.Errorf("expected Patch, got %T", *firstPass) } patches = append(patches, firstPatch) } secondPass, err := patch(original, modified, false) if err != nil { return nil, err } if secondPass != nil { secondPatch, ok := (*secondPass).(configpatcher.Patch) if !ok { return nil, fmt.Errorf("expected Patch, got %T", *secondPass) } patches = append(patches, secondPatch) } return patches, nil } // nolint: gocyclo func patch(original, modified configcore.Provider, firstPass bool) (*configpatcher.StrategicMergePatch, error) { originalIDToDoc := documentsToMap(original.Documents()) modifiedIDToDoc := documentsToMap(modified.Documents()) var removed, added, common []documentid.DocumentID // nolint:prealloc for id := range originalIDToDoc { if _, ok := modifiedIDToDoc[id]; !ok { removed = append(removed, id) continue } common = append(common, id) } for id := range modifiedIDToDoc { if _, ok := originalIDToDoc[id]; !ok { added = append(added, id) } } unstructuredPatches := make([]unstructured, 0, len(removed)+len(added)+len(common)) if firstPass { for _, removedID := range removed { meta := removedID.Meta() meta["$patch"] = "delete" unstructuredPatches = append(unstructuredPatches, meta) } for _, addedID := range added { addedDoc := modifiedIDToDoc[addedID] addedUnstructured, err := documentToUnstructured(addedDoc) if err != nil { return nil, err } unstructuredPatches = append(unstructuredPatches, addedUnstructured) } } for _, commonID := range common { originalDoc := originalIDToDoc[commonID] modifiedDoc := modifiedIDToDoc[commonID] originalUnstructured, err := documentToUnstructured(originalDoc) if err != nil { return nil, err } modifiedUnstructured, err := documentToUnstructured(modifiedDoc) if err != nil { return nil, err } mergePatch, err := createMergePatch(originalUnstructured, modifiedUnstructured, &commonID, firstPass) if err != nil { return nil, err } if len(mergePatch) == 0 { continue } unstructuredPatches = append(unstructuredPatches, mergePatch) } if len(unstructuredPatches) == 0 { return nil, nil } patchYAML, err := encodeToYAML(unstructuredPatches) if err != nil { return nil, err } cfg, err := configloader.NewFromBytes(patchYAML, configloader.WithAllowPatchDelete()) if err != nil { return nil, err } mergePatch := configpatcher.NewStrategicMergePatch(cfg) return &mergePatch, nil } func encodeToYAML(docs []unstructured) ([]byte, error) { var buf bytes.Buffer enc := yaml.NewEncoder(&buf) enc.SetIndent(2) for _, doc := range docs { if err := enc.Encode(doc); err != nil { return nil, err } } if err := enc.Close(); err != nil { return nil, err } return buf.Bytes(), nil } func documentsToMap(docs []config.Document) map[documentid.DocumentID]config.Document { out := make(map[documentid.DocumentID]config.Document, len(docs)) for _, doc := range docs { out[documentid.Extract(doc)] = doc } return out } func documentToUnstructured(doc config.Document) (unstructured, error) { c, err := container.New(doc) if err != nil { return nil, err } unstructuredBytes, err := c.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return nil, err } var out unstructured if err = yaml.Unmarshal(unstructuredBytes, &out); err != nil { return nil, err } return out, nil } // nolint: gocyclo,cyclop func createMergePatch(original, modified unstructured, documentID *documentid.DocumentID, firstPass bool) (unstructured, error) { meta := unstructured{} mergePatch := unstructured{} // First, handle all modified and added values for key, modV := range modified { // Discover and keep meta fields obtained from documentId if documentID != nil { if metaV, ok := documentID.Meta()[key]; ok && metaV != "" { meta[key] = metaV continue } } origV, ok := original[key] if !ok { setValue(&mergePatch, key, modV, firstPass) continue } if reflect.TypeOf(origV) != reflect.TypeOf(modV) { setValue(&mergePatch, key, modV, firstPass) continue } switch origT := origV.(type) { case unstructured: modT := modV.(unstructured) patchV, err := createMergePatch(origT, modT, nil, firstPass) if err != nil { return nil, err } if len(patchV) > 0 { mergePatch[key] = patchV } case []any: modT := modV.([]any) if !reflect.DeepEqual(origT, modT) { if firstPass { mergePatch[key] = map[string]string{"$patch": "delete"} } else { setValue(&mergePatch, key, modV, true) } } case string, int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool: if !reflect.DeepEqual(origV, modV) { setValue(&mergePatch, key, modV, firstPass) } case nil: switch modV.(type) { case nil: // Both nil, fine. default: setValue(&mergePatch, key, modV, firstPass) } default: return nil, fmt.Errorf("unknown type:%T in key %s", origV, key) } } // Now handle all deleted keys for key := range original { if _, ok := modified[key]; !ok { setValue(&mergePatch, key, map[string]string{"$patch": "delete"}, firstPass) continue } } // Finally, merge meta into mergePatch if len(mergePatch) > 0 && len(meta) > 0 { maps.Copy(mergePatch, meta) } return mergePatch, nil } func setValue(mergePatch *unstructured, key string, value any, shouldSet bool) { if shouldSet { (*mergePatch)[key] = value } } ================================================ FILE: pkg/machinery/config/configdiff/mergepatch_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configdiff_test import ( _ "embed" "testing" "time" "github.com/siderolabs/gen/xslices" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" coreconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configdiff" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/internal/documentid" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) var ( //go:embed testdata/original.yaml originalYAML []byte //go:embed testdata/modified.yaml modifiedYAML []byte ) func TestMergePatch(t *testing.T) { original, err := configloader.NewFromBytes(originalYAML) require.NoError(t, err) modified, err := configloader.NewFromBytes(modifiedYAML) require.NoError(t, err) patches, err := configdiff.Patch(original, modified) require.NoError(t, err) apply, err := configpatcher.Apply(configpatcher.WithConfig(original), patches) require.NoError(t, err) appliedBytes, err := apply.Bytes() require.NoError(t, err) modifiedBytes, err := modified.Bytes() require.NoError(t, err) require.Equal(t, string(modifiedBytes), string(appliedBytes)) } var inlineOriginal = []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `) func TestMergePatchInline(t *testing.T) { tests := []struct { name string originalAsBytes []byte modifiedAsBytes []byte patchesAsBytes [][]byte }{ { name: "test add field", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-config: /etc/kubernetes/cloud-config cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `), patchesAsBytes: [][]byte{ []byte(`machine: kubelet: extraArgs: cloud-config: /etc/kubernetes/cloud-config version: v1alpha1 `), }, }, { name: "test replace field", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10001 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `), patchesAsBytes: [][]byte{ []byte(`cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10001 version: v1alpha1 `), }, }, { name: "test add nested field", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 discovery: registries: kubernetes: disabled: false service: {} --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `), patchesAsBytes: [][]byte{ []byte(`cluster: discovery: registries: kubernetes: disabled: false service: {} version: v1alpha1 `), }, }, { name: "test replace item in list", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - new-example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `), patchesAsBytes: [][]byte{ []byte(`machine: certSANs: $patch: delete version: v1alpha1 `), []byte(`machine: certSANs: - new-example.com version: v1alpha1 `), }, }, { name: "test remove key", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block `), patchesAsBytes: [][]byte{ []byte(`machine: kubelet: $patch: delete version: v1alpha1 `), }, }, { name: "test add document", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 --- apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block --- apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ `), patchesAsBytes: [][]byte{ []byte(`apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ `), }, }, { name: "test remove document", originalAsBytes: inlineOriginal, modifiedAsBytes: []byte(`version: v1alpha1 machine: type: controlplane token: 2to1o4.gtwik66aods4cznj certSANs: - example.com kubelet: extraArgs: cloud-provider: external cluster: controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 `), patchesAsBytes: [][]byte{ []byte(`$patch: delete apiVersion: v1alpha1 kind: NetworkDefaultActionConfig `), }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { original, err := configloader.NewFromBytes(tt.originalAsBytes) require.NoError(t, err) modified, err := configloader.NewFromBytes(tt.modifiedAsBytes) require.NoError(t, err) patches, err := configdiff.Patch(original, modified) require.NoError(t, err) for i, patch := range patches { provider := patch.(configpatcher.StrategicMergePatch).Provider() patchBytes, err := provider.Bytes() require.NoError(t, err) require.Equal(t, string(patchBytes), string(tt.patchesAsBytes[i])) } apply, err := configpatcher.Apply(configpatcher.WithConfig(original), patches) require.NoError(t, err) appliedBytes, err := apply.Bytes() require.NoError(t, err) modifiedBytes, err := modified.Bytes() require.NoError(t, err) require.Equal(t, string(modifiedBytes), string(appliedBytes)) }) } } var dynamicPatches = [][]byte{ []byte(`machine: network: interfaces: - interface: enp0s2 dhcp: true `), []byte(`apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ `), []byte(`cluster: apiServer: certSANs: - example.com admissionControl: - name: PodSecurity configuration: exemptions: namespaces: - kube-public - kube-node-lease `), []byte(`cluster: apiServer: admissionControl: - name: PrivilegedPodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: privileged audit-version: latest enforce: privileged enforce-version: latest warn: privileged warn-version: latest exemptions: namespaces: [] runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration `), []byte(`apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello-foo mountPath: /etc/foo environment: - FOO=BAR - BAR=FOO --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: var configFiles: - content: hello-var mountPath: /etc/var - content: hello-foo mountPath: /etc/var/foo environment: - FOO=BAR `), []byte(`apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: grpc://omni.example.com:8090?jointoken=testtoken --- apiVersion: v1alpha1 kind: EventSinkConfig endpoint: '[fdae:41e4:649b:9303::1]:8091' --- apiVersion: v1alpha1 kind: KmsgLogConfig name: omni-kmsg url: tcp://[fdae:41e4:649b:9303::1]:8092 `), []byte(`apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink $patch: delete `), []byte(`apiVersion: v1alpha1 kind: EthernetConfig name: enp0s2 features: tx-tcp-segmentation: false `), []byte(`apiVersion: v1alpha1 kind: ExtensionServiceConfig name: var configFiles: - content: hello-var mountPath: /etc/var $patch: delete environment: - FOO=BARFOO `), } func TestMergePatchDynamic(t *testing.T) { bundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) require.NoError(t, err) input, err := generate.NewInput("test", "https://localhost:6443", constants.DefaultKubernetesVersion, generate.WithSecretsBundle(bundle)) require.NoError(t, err) original, err := input.Config(machine.TypeControlPlane) require.NoError(t, err) var modified config.Provider modified = original.Clone() // Apply patches one by one to simulate real world usage for _, patchBytes := range dynamicPatches { patches, patchErr := configpatcher.LoadPatch(patchBytes) require.NoError(t, patchErr) patched, patchErr := configpatcher.Apply(configpatcher.WithConfig(modified), []configpatcher.Patch{patches}) require.NoError(t, patchErr) modified, patchErr = patched.Config() require.NoError(t, patchErr) } // Get merge patches between original and modified patches, err := configdiff.Patch(original, modified) require.NoError(t, err) // Apply the merge patches to the original config patched, err := configpatcher.Apply(configpatcher.WithConfig(original), patches) require.NoError(t, err) patchedConfig, err := patched.Config() require.NoError(t, err) // configpatcher.Apply may change the order of documents, so we need to compare them one by one modifiedDocuments := modified.Documents() patchedDocuments := patchedConfig.Documents() require.Equal(t, len(modifiedDocuments), len(patchedDocuments)) modifiedDocumentsMap := xslices.ToMap(modifiedDocuments, func(doc coreconfig.Document) (documentid.DocumentID, coreconfig.Document) { return documentid.Extract(doc), doc }) patchedDocumentsMap := xslices.ToMap(patchedDocuments, func(doc coreconfig.Document) (documentid.DocumentID, coreconfig.Document) { return documentid.Extract(doc), doc }) for id, modifiedDoc := range modifiedDocumentsMap { patchedDoc, ok := patchedDocumentsMap[id] require.True(t, ok, "document %v not found in patched config", id) modifiedDocContainer, docErr := container.New(modifiedDoc) require.NoError(t, docErr) patchedDocContainer, docErr := container.New(patchedDoc) require.NoError(t, docErr) modifiedBytes, docErr := modifiedDocContainer.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, docErr) patchedBytes, docErr := patchedDocContainer.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, docErr) require.Equal(t, string(modifiedBytes), string(patchedBytes), "document %v does not match", id) } } ================================================ FILE: pkg/machinery/config/configdiff/testdata/modified.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: 2to1o4.gtwik66aods4cznj ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBaCsrS0JQcURzL2EyeTgyMXhRak9CekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qVXdPVEl5TURjek1ERTJXaGNOTXpVd09USXdNRGN6TURFMldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUU3dEthbTdaRUdsNTVUeDVzVDZWRmhKZDJVVFh1Z2g0MjlSCjZvVVk1WkxFbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkZ5dGhPT3ZpVzFYa3dHNAo1ZTdHVUtFSjhSSmtNQVVHQXl0bGNBTkJBRGhMclN0YTFlY1QwNFdVQ1YxYzZQOFVjVkpRTmNhMUl2RGxTQ3hwCm9vZzdudUJxdWxCRFdYVkFqQm9TTGU5VlV6aWQ3dTc4MDdIaHNzeFBneW03b2dRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR3BNOE1iSnplQXpYZ2xXNGRQcDFtUnMvV1lkYU1Nbjl2T0M3QUVXQU5MKwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.34.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: interfaces: - interface: enp0s2 dhcp: true install: disk: /dev/vda image: factory.talos.dev/metal-installer/088171816e905ec439337da75b1bafb81de8c652ee41c099f2b9ef7d90847648:v1.11.1 wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: DyOHp_FdDYr54RvtQR6SKyda2dshZkiUE_y0CQH1wFY= secret: Ig7Us67kfx01ascq3Q44GXA9P9b62iFKuYRV04Fntl8= controlPlane: endpoint: https://talos.local.oguz.pw:6443 clusterName: talos-default network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: ipovvu.zu2q1qef8u1ghx5p secretboxEncryptionSecret: g9/DQ6uJ+MuVLcNHLmrOgfeq5kD7SoJic7U7T74PanI= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpakNDQVMrZ0F3SUJBZ0lRSkJ5azVFYTB1STRUSHc3cG5Ob0E4ekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSTFNRGt5TWpBM016QXhObG9YRFRNMU1Ea3lNREEzTXpBeApObG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQVc2ZC9BQUxOYi9tYklvYUIzUzg2Z2kzTWVrYU9RbFpGdkZ0dkZ2S29COUpzeWR3Q1E1MDlSQW1MNWcKSDFvOWhLbjBoRlVNM0NaSXBWYlppT2JoY09DallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVvWWtIOUZocFRLdlpudDdCYVlMbXhFMjF0UXN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUtINmI3cnYKMmhuT1VhUjlkcVRpdURNOGpiVVVpSFo2RStuU1YzdG9qdlhKQWlFQTdGWUZxYjZFT21TTDUwNXo0VjJtZHJYQQpnWHBzK3R5NzUxckl1RGpFY0gwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1OVTB1YnBKa1JPdnlYbndkdGNVNHFxQTdZZkdNdENiZk02SThXUnhtMkZvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQmJwMzhBQXMxditac2lob0hkTHpxQ0xjeDZSbzVDVmtXOFcyOFc4cWdIMG16SjNBSkRuVAoxRUNZdm1BZldqMkVxZlNFVlF6Y0praWxWdG1JNXVGdzRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFhZ0F3SUJBZ0lSQU5sOElMcm05c1BzZ3Z5K0xKbUhWRGt3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU5UQTVNakl3TnpNd01UWmFGdzB6TlRBNU1qQXdOek13TVRaYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVNsSFBsbk9aSFZsbkRmdUhvNE0raWpqcTY1dUkzUTY4aE45UVBlWU9ZbTlNVVE0V3hyCnA2SjdwbGw3dWQvcFZVaUNyMVBVb0gxUWNHVGs2T09JanhKaG8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGRHhaZTFEU05nUzF3SE8xK2UySTgvNkVYL2pOTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklRQ0dXK0xlMTJOUS9Dd0czVitiUWZ1andRYkZwUmNYLzhZV052TllOZS9GdlFJZ2Z6VGw0VVJZVVdvejlDMzMKdGpLaEkvOFFIKy9jczQzODFrVDkwbGNVMEtrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFGbFo3RG9acStCdldMWExmMDFwcDB3RGpUeE5pa0swQkozS2JsL3NPaEJvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFcFJ6NVp6bVIxWlp3MzdoNk9EUG9vNDZ1dWJpTjBPdklUZlVEM21EbUp2VEZFT0ZzYTZlaQplNlpaZTduZjZWVklncTlUMUtCOVVIQms1T2pqaUk4U1lRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS2dJQkFBS0NBZ0VBdGh5Q3Q5MVpPem9SdTNTaWNsOFJVTnpzdkg3T3BQZ3AvaEdqZVFzZzFWbU80N3ZLCkhOMUxvY1kvTjhEZ3plZnJqc0YzU1ZSZGl2RDd4UXB4bzZmR2pDckxDNU9YZHdyRUVCc2wzQ1QwdzdpZXE5UFUKR1lNT3ROdXZ1M1QwRGIxMVZ1QW1wZVNzUWVEdXJPeDJEY1NzaEdrK2NEVTNJS2xsb2U5VWtuOVllck5MN1JEUAp0R0wvdy9VaTA3QWJXVWkvYnM5RFN1V2QybXBmdnVWSnJaTS9icTBwQ0NBaDR1c29jcnM2OHA2ZG01akpGT3E0CnA1SlN2YWxBemhHNUdwQllyVU54WUlXejJCR3JaTis3dDhlc2V1eEVIeHVCT284dGxhY1RGWmhOVnZXSExaeUoKczFnSTJna2JBdHFyU3F2b1IrWGM0SWwxRWpTK2xscmd5a0Vvc25HaFlaK3Z5SE5VZTd3c3Ntb1lSVkRrYzBWSwpPVzA2d1FEL2lsQXZmbnptKytXcGZ6VjE2dWxzYjZuK2I0V2JBa3VHaVluVjU0SExvQlFFdENUVVk3eERUQkMwCldOSU9TODNkcjRqUVJhcHZpZE5kT25ZRUpZK3J4QVNCK0RXeW1HNkgzUVAxVWNyRW54emtrU3l6R3M0MUFFa2gKZUordnNocTlYVU1UaU5GQ2NmZUplaHV4RmtDdXloVHp3YzVvNU5qeHV4ZEkrRmRvODhRdXh6L1pwNnJzcUtBeQppUWZHRjhqZFRmOFBIZmxuWHliMFU4NmZuUjJsSEs5VjZJeHlUdEVLRGZLR0FzSHZoTEQ5UThuckVqeGhpbEFTCnNhb1NrWXcvcjV2VTRDVDdoaUR0WS91eE5mWk9JWjhBOFI0eFNoV2RGSDlxZktieVRGeWY3MUgxYUZzQ0F3RUEKQVFLQ0FnQVNpbytTaWNKS2dleUpRZVJDTWNTeEQzVTQ4YzQzUko3OTQ2elpwNVRscy9NVTQ2czl5aGdudGVmOAoxTEh1dE9TcVNhOUw4Mzk4cEhGMXk4enJKU3RWWFF3RU56Z3VJaFg1TDlKb2VnakRucG5sRTdHZUVWWmZlcGlIClJPNk9NWkp2VUc0TzZOdlM3MFJOcmR1TkprK01RYXplUHNUSW1nYWplSnNMT1ZUNFZTWHZVbzFiSjlNemo5TkcKYTBFMWsyOE9LS01JenAwR1BsdFdNOEVQSDVWUFB1a1ZEelIwQm80OU5Ddlp4T1YyRUpXMGYvdGg4RWRsVVFTcwpsbWhhdUlTV3kyMlJMcnV6VFlVK1JYczJ5R2thc05CMHZGZXBieWRzZTdDNU82THdMaHBmdmZiVklDcnlqQWZJCmdCdjlnOGduL2RMQnQ2MElOLytKam1JaHBZV3ZqVjFCSGVZT29mSG1VWlJJOGwrcld0QjhNeCtsY21SS0FoQlkKdXhwbVRDU3JJczQyQ3loaFBJMk5Uak9GRUNxcU55eUpNM3dxQTBQV3BlMWZadUVmNTFNK3ZpUEdBNWxHUW5UZgp1c05teW9rMklMWUJPVS9EK0Q3U3V3Mml0TExvV2dtNk9PeVZ5TWIrV2dOM1ZMMFp0T3VpclAvYmpaNGt4cTlHClZKcDF3bWdCZHRzQjZlRGFyTEtwNlJpSmlabk4yRWtGTkRsQld5NTFwRTMrU1BEeXdvOXFMMHNXdTdJMk5LSEgKckxxbjk4ODFCcmVNa2lZajRmVWc1U2dZZFBHVlovc25CcloxT1IwNnArWm5BYzg5YmF2dFFmQWpLc2pXWDdYZQpCYXZYNHFPTnYyNnhCaWxaSzllYnZiVWdSTWM4UEZkSGtuakFiSDZLSkJsQktrMnhBUUtDQVFFQTBNbVVCcHIvCngxbnRKZUFEQkJkSVh3UGpJUjVXR1RwTHFlRWF4RG45UGorRFhnSWc2aUxVcFowRXU3dVRIZFVGejc3Y1UyZWcKcWxxRitQRUN4dTBVdU0zbjd1SUxXMy9Bb25EUTh5RmxZRDlaYTM4QVpjSGh0QWxmQjRTMnZsTnpIZ2FQWVdXaApJVkpxbFdWVE0vdGY3Y3VQV0hUazltMnptYmdGaWsvNzYyWDBvdExneUhpWHd6UHZuOTgzR2hkaVBxak91bUNrCjlCR2Joc25kaE9YTDV3Q21HYklvZGFBRS92dVY1QUtsWFl1YitnOXFXbFFwb3dwNzU5ZE9aeldBdklHQmNwSTkKRG1BZ3NzVXBmQnhGSEhFVGg1V2Vpamtpd1lDTld3cHFYMis2UXUvMHFXR01Bc3EyUlVNR0JwWldaRlFMNmtWTAovRkZjNWpLbE82ZjFBUUtDQVFFQTMwcXhMb2ZybXJadFlidkxGOG9ZbTQ4U2ZMQk9tV1hKdHJia2dqcGxSaU1PCnZFZDZMQ2VmTW43ekZWei9Yb1N3N1ZEWFpVenZ0dEVlQnpQSy9HVHR2TU5BMGZyM0VScmE1OGUyeXg5ZFNqN2wKZGJ1QitDQjBGWW1Jc1dTemE4THZWY0lkQk5nakdGVm4vMkZEd2N4OUFvdDgyUVVhOWhralVyclZvRGVuQjlNWQpRb2RYSU5Ec0FWQXFvaHJ4b3Q0azkwcE9EYjF0MHkrNERNSnlhY2VKbW5EcnhDQnIrVlpjZnhzcFAzY0d2Uk81CjVNTXRRbVFlbHVZWnQrL3pqcEl0RXJDR3dBRnU1UVFxNndPM2ltWHE0WjVjMEhRaG5Ga21CempYcGVrbXFTaEMKVGdxZGloNjc0VllNUDBzV3FUU3FudWc5QlJZS0ZHTWFTeDEvREx4Uld3S0NBUUVBdDY0ZmhCQW9wZ0QvR1NzUwpmQzdmaEhldkFodm1NeHVPSlUzY2RuVnR6YTJpckxuQ3F6a3BTdW53bUJoVlBSR0RvMWlPRFBKRjdwams5RFZUCjlCM3U5UVp3M1VBUUxkY2VhY3BHaVI4QVNNUnlycGQwaWhFZnQzdm5GbjR6SnczVFlMNzB0UUxyMXB6akY3dWsKano2L0RqemZSenJQazl5Ky9LVmdlbVlUZ3V5WFpBZVJxY3d0OTVWaFlvekZ0VGFOUUFMU25EVVo2WDcwRElqYgpVV2U1RXVrSE4rUDhwRDY3Sm5lL1RuRGxlbjZ3SWpZZG9vb1lkMDlwNG5VUWpNd05EY29CVUFKSHBMWDlEa2xXClRkR3hHMngvZWZDdklYdFNrRm5BQWpBUGxSWitEeFY3Y09oWWZMeEp6blZBZjlzUzlnRGEycWRNU2hacVhEcUMKRXhHWUFRS0NBUUVBcnNaRzh6WGVXMFhKVGdOd3p4a1hzOE1ENUdjWHpvZldvRlo5ZTlWN2FhK05IQ0FTWjdkSApxMzJraFNjNmwxL2pJSTN6V3M0aW40VUZMUHdFT1JSQzVVb3JWWEJMcks3Smd6eFdQcDA1SnlFZDk4NGh4L2FYCkJqaVZyc3cwaC9lWDRCZEZJWUtQemI4ajNNZmtBakF0OVN6N050OEJJSG5DcFVHcWJuTEJJYVhBU21xbTd2KzQKaXJxVjlEYUV4a2Q4eGJiNmExVEhQdE1PQzZhemFVcU8wVHpydmFMbkFNM3hMbWd6SEFMdFdsZXpYSFRNMnplRQpuZlpUVVI3WEU5UWt4WWs0bjljL1ZyQ3hheXlJb0NKdis5TTVzTXdGZHR4c29LQ0VZcytndkN6UDRVZjcwWjJHCno3VExkMHN0M0psbHAyWCtSUm5nSDk4R09KOUl6ODRqR3dLQ0FRRUFxV0REby9IalYxaWZ0SGFVZHBKTWRVMXkKSjljUEtRVTVaUGw1NG9zcEc3RHo0a25OVEhwWFZBRjhCSUhmelRpd3QxOTdmdGlQaHRyRnc4aFBsYWpHdkJOKwpXZWx5eEI1MVBWbWxxOWN2enprOGVFNVJmM2t0NHl1djFyczNUb1ZZUlRKZlB4bGQ4K09xUWhXYXhZVEVHem5yCmptZ3NNaUNQUlkyemMrQ2FKeUd5S0ZFMjA3QXlXQ0VjTjVhZHhaV0gySXo2M3BZYnVHK3pJdGlhd01HODhMWEIKeDlFRGlRdjMyR05Ua1B4ODdEL2h5MThnM2d5dEhIendpQUJZZmVMUDJpeVFHb1pucC8zM0pSMzNiWWhLL3FKQQpMNU9LN25oVUR1azM3cEQ5NkhNNWxmbjRiTHhzVUt3U2xZMUJ0SmM0RW5EUmZQMG84Y2VNMzJqeUVMbzhWZz09Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== apiServer: image: registry.k8s.io/kube-apiserver:v1.34.0 disablePodSecurityPolicy: true auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.34.0 proxy: image: registry.k8s.io/kube-proxy:v1.34.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.34.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRYWFxck1lRnRHeEpibHM3NFo1VWhCekFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEkxTURreU1qQTNNekF4TmxvWERUTTFNRGt5TURBM016QXhObG93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkkzYzc3VjNlc2t5CkFvK3YyR1EweXJrZlZOVk5rVDZCNkZDS2FwOU9ZcFpPck53T2FFbFRKTGczYjh0WCtma1lFS0g1TWdTcVlvRXEKMXhnM3NCZGJLN1dqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVUkRDbjhPVGJ0dnlFCktTWnZXK1ZRT0orcFFuRXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFL0hSckNjOXFSNWpCT3dlS2MzcWJKTG4KenU0MWNRMEd5VkFZeVRHVm9lRUNJUUNPMHEvaFpHenRyK2JRL05aSTljQW9hcVNBd1BFYUk1SWx0VC9wNE83WApQUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhvZDhtNFk3aXNNYloyTTdqQ0UxYU85MGhob3dVSHhFQnBPdi9pWTJRUGdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFamR6dnRYZDZ5VElDajYvWVpEVEt1UjlVMVUyUlBvSG9VSXBxbjA1aWxrNnMzQTVvU1ZNawp1RGR2eTFmNStSZ1FvZmt5QktwaWdTclhHRGV3RjFzcnRRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello-foo mountPath: /etc/foo environment: - FOO=BAR - BAR=FOO ================================================ FILE: pkg/machinery/config/configdiff/testdata/original.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: 2to1o4.gtwik66aods4cznj ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBaCsrS0JQcURzL2EyeTgyMXhRak9CekFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qVXdPVEl5TURjek1ERTJXaGNOTXpVd09USXdNRGN6TURFMldqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUU3dEthbTdaRUdsNTVUeDVzVDZWRmhKZDJVVFh1Z2g0MjlSCjZvVVk1WkxFbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkZ5dGhPT3ZpVzFYa3dHNAo1ZTdHVUtFSjhSSmtNQVVHQXl0bGNBTkJBRGhMclN0YTFlY1QwNFdVQ1YxYzZQOFVjVkpRTmNhMUl2RGxTQ3hwCm9vZzdudUJxdWxCRFdYVkFqQm9TTGU5VlV6aWQ3dTc4MDdIaHNzeFBneW03b2dRPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR3BNOE1iSnplQXpYZ2xXNGRQcDFtUnMvV1lkYU1Nbjl2T0M3QUVXQU5MKwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.34.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: DyOHp_FdDYr54RvtQR6SKyda2dshZkiUE_y0CQH1wFY= secret: Ig7Us67kfx01ascq3Q44GXA9P9b62iFKuYRV04Fntl8= controlPlane: endpoint: https://[fdae:41e4:649b:9303::1]:10000 clusterName: talos-default network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: ipovvu.zu2q1qef8u1ghx5p secretboxEncryptionSecret: g9/DQ6uJ+MuVLcNHLmrOgfeq5kD7SoJic7U7T74PanI= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpakNDQVMrZ0F3SUJBZ0lRSkJ5azVFYTB1STRUSHc3cG5Ob0E4ekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSTFNRGt5TWpBM016QXhObG9YRFRNMU1Ea3lNREEzTXpBeApObG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQVc2ZC9BQUxOYi9tYklvYUIzUzg2Z2kzTWVrYU9RbFpGdkZ0dkZ2S29COUpzeWR3Q1E1MDlSQW1MNWcKSDFvOWhLbjBoRlVNM0NaSXBWYlppT2JoY09DallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVvWWtIOUZocFRLdlpudDdCYVlMbXhFMjF0UXN3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loQUtINmI3cnYKMmhuT1VhUjlkcVRpdURNOGpiVVVpSFo2RStuU1YzdG9qdlhKQWlFQTdGWUZxYjZFT21TTDUwNXo0VjJtZHJYQQpnWHBzK3R5NzUxckl1RGpFY0gwPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1OVTB1YnBKa1JPdnlYbndkdGNVNHFxQTdZZkdNdENiZk02SThXUnhtMkZvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQmJwMzhBQXMxditac2lob0hkTHpxQ0xjeDZSbzVDVmtXOFcyOFc4cWdIMG16SjNBSkRuVAoxRUNZdm1BZldqMkVxZlNFVlF6Y0praWxWdG1JNXVGdzRBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFhZ0F3SUJBZ0lSQU5sOElMcm05c1BzZ3Z5K0xKbUhWRGt3Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU5UQTVNakl3TnpNd01UWmFGdzB6TlRBNU1qQXdOek13TVRaYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVNsSFBsbk9aSFZsbkRmdUhvNE0raWpqcTY1dUkzUTY4aE45UVBlWU9ZbTlNVVE0V3hyCnA2SjdwbGw3dWQvcFZVaUNyMVBVb0gxUWNHVGs2T09JanhKaG8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGRHhaZTFEU05nUzF3SE8xK2UySTgvNkVYL2pOTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDCklRQ0dXK0xlMTJOUS9Dd0czVitiUWZ1andRYkZwUmNYLzhZV052TllOZS9GdlFJZ2Z6VGw0VVJZVVdvejlDMzMKdGpLaEkvOFFIKy9jczQzODFrVDkwbGNVMEtrPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFGbFo3RG9acStCdldMWExmMDFwcDB3RGpUeE5pa0swQkozS2JsL3NPaEJvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFcFJ6NVp6bVIxWlp3MzdoNk9EUG9vNDZ1dWJpTjBPdklUZlVEM21EbUp2VEZFT0ZzYTZlaQplNlpaZTduZjZWVklncTlUMUtCOVVIQms1T2pqaUk4U1lRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS2dJQkFBS0NBZ0VBdGh5Q3Q5MVpPem9SdTNTaWNsOFJVTnpzdkg3T3BQZ3AvaEdqZVFzZzFWbU80N3ZLCkhOMUxvY1kvTjhEZ3plZnJqc0YzU1ZSZGl2RDd4UXB4bzZmR2pDckxDNU9YZHdyRUVCc2wzQ1QwdzdpZXE5UFUKR1lNT3ROdXZ1M1QwRGIxMVZ1QW1wZVNzUWVEdXJPeDJEY1NzaEdrK2NEVTNJS2xsb2U5VWtuOVllck5MN1JEUAp0R0wvdy9VaTA3QWJXVWkvYnM5RFN1V2QybXBmdnVWSnJaTS9icTBwQ0NBaDR1c29jcnM2OHA2ZG01akpGT3E0CnA1SlN2YWxBemhHNUdwQllyVU54WUlXejJCR3JaTis3dDhlc2V1eEVIeHVCT284dGxhY1RGWmhOVnZXSExaeUoKczFnSTJna2JBdHFyU3F2b1IrWGM0SWwxRWpTK2xscmd5a0Vvc25HaFlaK3Z5SE5VZTd3c3Ntb1lSVkRrYzBWSwpPVzA2d1FEL2lsQXZmbnptKytXcGZ6VjE2dWxzYjZuK2I0V2JBa3VHaVluVjU0SExvQlFFdENUVVk3eERUQkMwCldOSU9TODNkcjRqUVJhcHZpZE5kT25ZRUpZK3J4QVNCK0RXeW1HNkgzUVAxVWNyRW54emtrU3l6R3M0MUFFa2gKZUordnNocTlYVU1UaU5GQ2NmZUplaHV4RmtDdXloVHp3YzVvNU5qeHV4ZEkrRmRvODhRdXh6L1pwNnJzcUtBeQppUWZHRjhqZFRmOFBIZmxuWHliMFU4NmZuUjJsSEs5VjZJeHlUdEVLRGZLR0FzSHZoTEQ5UThuckVqeGhpbEFTCnNhb1NrWXcvcjV2VTRDVDdoaUR0WS91eE5mWk9JWjhBOFI0eFNoV2RGSDlxZktieVRGeWY3MUgxYUZzQ0F3RUEKQVFLQ0FnQVNpbytTaWNKS2dleUpRZVJDTWNTeEQzVTQ4YzQzUko3OTQ2elpwNVRscy9NVTQ2czl5aGdudGVmOAoxTEh1dE9TcVNhOUw4Mzk4cEhGMXk4enJKU3RWWFF3RU56Z3VJaFg1TDlKb2VnakRucG5sRTdHZUVWWmZlcGlIClJPNk9NWkp2VUc0TzZOdlM3MFJOcmR1TkprK01RYXplUHNUSW1nYWplSnNMT1ZUNFZTWHZVbzFiSjlNemo5TkcKYTBFMWsyOE9LS01JenAwR1BsdFdNOEVQSDVWUFB1a1ZEelIwQm80OU5Ddlp4T1YyRUpXMGYvdGg4RWRsVVFTcwpsbWhhdUlTV3kyMlJMcnV6VFlVK1JYczJ5R2thc05CMHZGZXBieWRzZTdDNU82THdMaHBmdmZiVklDcnlqQWZJCmdCdjlnOGduL2RMQnQ2MElOLytKam1JaHBZV3ZqVjFCSGVZT29mSG1VWlJJOGwrcld0QjhNeCtsY21SS0FoQlkKdXhwbVRDU3JJczQyQ3loaFBJMk5Uak9GRUNxcU55eUpNM3dxQTBQV3BlMWZadUVmNTFNK3ZpUEdBNWxHUW5UZgp1c05teW9rMklMWUJPVS9EK0Q3U3V3Mml0TExvV2dtNk9PeVZ5TWIrV2dOM1ZMMFp0T3VpclAvYmpaNGt4cTlHClZKcDF3bWdCZHRzQjZlRGFyTEtwNlJpSmlabk4yRWtGTkRsQld5NTFwRTMrU1BEeXdvOXFMMHNXdTdJMk5LSEgKckxxbjk4ODFCcmVNa2lZajRmVWc1U2dZZFBHVlovc25CcloxT1IwNnArWm5BYzg5YmF2dFFmQWpLc2pXWDdYZQpCYXZYNHFPTnYyNnhCaWxaSzllYnZiVWdSTWM4UEZkSGtuakFiSDZLSkJsQktrMnhBUUtDQVFFQTBNbVVCcHIvCngxbnRKZUFEQkJkSVh3UGpJUjVXR1RwTHFlRWF4RG45UGorRFhnSWc2aUxVcFowRXU3dVRIZFVGejc3Y1UyZWcKcWxxRitQRUN4dTBVdU0zbjd1SUxXMy9Bb25EUTh5RmxZRDlaYTM4QVpjSGh0QWxmQjRTMnZsTnpIZ2FQWVdXaApJVkpxbFdWVE0vdGY3Y3VQV0hUazltMnptYmdGaWsvNzYyWDBvdExneUhpWHd6UHZuOTgzR2hkaVBxak91bUNrCjlCR2Joc25kaE9YTDV3Q21HYklvZGFBRS92dVY1QUtsWFl1YitnOXFXbFFwb3dwNzU5ZE9aeldBdklHQmNwSTkKRG1BZ3NzVXBmQnhGSEhFVGg1V2Vpamtpd1lDTld3cHFYMis2UXUvMHFXR01Bc3EyUlVNR0JwWldaRlFMNmtWTAovRkZjNWpLbE82ZjFBUUtDQVFFQTMwcXhMb2ZybXJadFlidkxGOG9ZbTQ4U2ZMQk9tV1hKdHJia2dqcGxSaU1PCnZFZDZMQ2VmTW43ekZWei9Yb1N3N1ZEWFpVenZ0dEVlQnpQSy9HVHR2TU5BMGZyM0VScmE1OGUyeXg5ZFNqN2wKZGJ1QitDQjBGWW1Jc1dTemE4THZWY0lkQk5nakdGVm4vMkZEd2N4OUFvdDgyUVVhOWhralVyclZvRGVuQjlNWQpRb2RYSU5Ec0FWQXFvaHJ4b3Q0azkwcE9EYjF0MHkrNERNSnlhY2VKbW5EcnhDQnIrVlpjZnhzcFAzY0d2Uk81CjVNTXRRbVFlbHVZWnQrL3pqcEl0RXJDR3dBRnU1UVFxNndPM2ltWHE0WjVjMEhRaG5Ga21CempYcGVrbXFTaEMKVGdxZGloNjc0VllNUDBzV3FUU3FudWc5QlJZS0ZHTWFTeDEvREx4Uld3S0NBUUVBdDY0ZmhCQW9wZ0QvR1NzUwpmQzdmaEhldkFodm1NeHVPSlUzY2RuVnR6YTJpckxuQ3F6a3BTdW53bUJoVlBSR0RvMWlPRFBKRjdwams5RFZUCjlCM3U5UVp3M1VBUUxkY2VhY3BHaVI4QVNNUnlycGQwaWhFZnQzdm5GbjR6SnczVFlMNzB0UUxyMXB6akY3dWsKano2L0RqemZSenJQazl5Ky9LVmdlbVlUZ3V5WFpBZVJxY3d0OTVWaFlvekZ0VGFOUUFMU25EVVo2WDcwRElqYgpVV2U1RXVrSE4rUDhwRDY3Sm5lL1RuRGxlbjZ3SWpZZG9vb1lkMDlwNG5VUWpNd05EY29CVUFKSHBMWDlEa2xXClRkR3hHMngvZWZDdklYdFNrRm5BQWpBUGxSWitEeFY3Y09oWWZMeEp6blZBZjlzUzlnRGEycWRNU2hacVhEcUMKRXhHWUFRS0NBUUVBcnNaRzh6WGVXMFhKVGdOd3p4a1hzOE1ENUdjWHpvZldvRlo5ZTlWN2FhK05IQ0FTWjdkSApxMzJraFNjNmwxL2pJSTN6V3M0aW40VUZMUHdFT1JSQzVVb3JWWEJMcks3Smd6eFdQcDA1SnlFZDk4NGh4L2FYCkJqaVZyc3cwaC9lWDRCZEZJWUtQemI4ajNNZmtBakF0OVN6N050OEJJSG5DcFVHcWJuTEJJYVhBU21xbTd2KzQKaXJxVjlEYUV4a2Q4eGJiNmExVEhQdE1PQzZhemFVcU8wVHpydmFMbkFNM3hMbWd6SEFMdFdsZXpYSFRNMnplRQpuZlpUVVI3WEU5UWt4WWs0bjljL1ZyQ3hheXlJb0NKdis5TTVzTXdGZHR4c29LQ0VZcytndkN6UDRVZjcwWjJHCno3VExkMHN0M0psbHAyWCtSUm5nSDk4R09KOUl6ODRqR3dLQ0FRRUFxV0REby9IalYxaWZ0SGFVZHBKTWRVMXkKSjljUEtRVTVaUGw1NG9zcEc3RHo0a25OVEhwWFZBRjhCSUhmelRpd3QxOTdmdGlQaHRyRnc4aFBsYWpHdkJOKwpXZWx5eEI1MVBWbWxxOWN2enprOGVFNVJmM2t0NHl1djFyczNUb1ZZUlRKZlB4bGQ4K09xUWhXYXhZVEVHem5yCmptZ3NNaUNQUlkyemMrQ2FKeUd5S0ZFMjA3QXlXQ0VjTjVhZHhaV0gySXo2M3BZYnVHK3pJdGlhd01HODhMWEIKeDlFRGlRdjMyR05Ua1B4ODdEL2h5MThnM2d5dEhIendpQUJZZmVMUDJpeVFHb1pucC8zM0pSMzNiWWhLL3FKQQpMNU9LN25oVUR1azM3cEQ5NkhNNWxmbjRiTHhzVUt3U2xZMUJ0SmM0RW5EUmZQMG84Y2VNMzJqeUVMbzhWZz09Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg== apiServer: image: registry.k8s.io/kube-apiserver:v1.34.0 certSANs: [] disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.34.0 proxy: image: registry.k8s.io/kube-proxy:v1.34.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.34.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRYWFxck1lRnRHeEpibHM3NFo1VWhCekFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEkxTURreU1qQTNNekF4TmxvWERUTTFNRGt5TURBM016QXhObG93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkkzYzc3VjNlc2t5CkFvK3YyR1EweXJrZlZOVk5rVDZCNkZDS2FwOU9ZcFpPck53T2FFbFRKTGczYjh0WCtma1lFS0g1TWdTcVlvRXEKMXhnM3NCZGJLN1dqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVUkRDbjhPVGJ0dnlFCktTWnZXK1ZRT0orcFFuRXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFL0hSckNjOXFSNWpCT3dlS2MzcWJKTG4KenU0MWNRMEd5VkFZeVRHVm9lRUNJUUNPMHEvaFpHenRyK2JRL05aSTljQW9hcVNBd1BFYUk1SWx0VC9wNE83WApQUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhvZDhtNFk3aXNNYloyTTdqQ0UxYU85MGhob3dVSHhFQnBPdi9pWTJRUGdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFamR6dnRYZDZ5VElDajYvWVpEVEt1UjlVMVUyUlBvSG9VSXBxbjA1aWxrNnMzQTVvU1ZNawp1RGR2eTFmNStSZ1FvZmt5QktwaWdTclhHRGV3RjFzcnRRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello-bar mountPath: /etc/bar - content: hello-foobar mountPath: /etc/foobar environment: - FOO=BAR --- apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ ================================================ FILE: pkg/machinery/config/configloader/configloader.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package configloader provides methods to load Talos config. package configloader import ( "bytes" "errors" "io" "os" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader/internal/decoder" "github.com/siderolabs/talos/pkg/machinery/config/container" _ "github.com/siderolabs/talos/pkg/machinery/config/types" // import config types to register them ) // ErrNoConfig is returned when no configuration was found in the input. var ErrNoConfig = errors.New("config not found") // newConfig initializes and returns a Configurator. func newConfig(r io.Reader, opt ...Opt) (config config.Provider, err error) { var opts Opts for _, o := range opt { o(&opts) } dec := decoder.NewDecoder() var buf bytes.Buffer // preserve the original contents r = io.TeeReader(r, &buf) manifests, err := dec.Decode(r, opts.allowPatchDelete) if err != nil { return nil, err } if len(manifests) == 0 { return nil, ErrNoConfig } return container.NewReadonly(buf.Bytes(), manifests...) } // NewFromFile will take a filepath and attempt to parse a config file from it. func NewFromFile(filepath string) (config.Provider, error) { f, err := os.Open(filepath) if err != nil { return nil, err } defer f.Close() //nolint:errcheck return newConfig(f) } // NewFromReader will take a reader and attempt to parse a config file from it. func NewFromReader(f io.Reader) (config.Provider, error) { return newConfig(f) } // NewFromStdin initializes a config provider by reading from stdin. func NewFromStdin() (config.Provider, error) { return newConfig(os.Stdin) } // NewFromBytes will take a byteslice and attempt to parse a config file from it. func NewFromBytes(source []byte, o ...Opt) (config.Provider, error) { return newConfig(bytes.NewReader(source), o...) } // Opts represents the options for the config loader. type Opts struct { allowPatchDelete bool } // Opt is a functional option for the config loader. type Opt func(*Opts) // WithAllowPatchDelete allows the loader to parse patch delete operations. func WithAllowPatchDelete() Opt { return func(o *Opts) { o.allowPatchDelete = true } } // Selector represents a delete selector for a document. type Selector = decoder.Selector // ErrZeroedDocument is returned when the document is empty after applying the delete selector. var ErrZeroedDocument = decoder.ErrZeroedDocument // ErrLookupFailed is returned when the lookup failed. var ErrLookupFailed = decoder.ErrLookupFailed ================================================ FILE: pkg/machinery/config/configloader/configloader_fuzz_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configloader_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) func FuzzConfigLoader(f *testing.F) { files, err := filepath.Glob(filepath.Join("testdata", "*.test")) require.NoError(f, err) for _, file := range files { b, err := os.ReadFile(file) require.NoError(f, err) f.Add(b) } f.Add([]byte(": \xea")) f.Add([]byte(nil)) f.Add([]byte("")) f.Fuzz(func(t *testing.T, b []byte) { t.Parallel() testConfigLoaderBytes(t, b, false) }) } ================================================ FILE: pkg/machinery/config/configloader/configloader_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configloader_test import ( "os" "path/filepath" "reflect" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" ) // callMethods calls obj's "getter" methods recursively and fails on panic. // //nolint:gocyclo func callMethods(t testing.TB, obj reflect.Value, chain ...string) { t.Helper() if (obj.Kind() == reflect.Interface || obj.Kind() == reflect.Pointer) && obj.IsNil() { return } typ := obj.Type() for i := range obj.NumMethod() { method := obj.Method(i) if method.Type().NumIn() != 0 { continue } methodName := typ.Method(i).Name nextChain := make([]string, len(chain)+1) copy(nextChain, chain) nextChain[len(nextChain)-1] = methodName // t.Log(nextChain) // skip known broken methods switch methodName { case "GetRSAKey", "GetEd25519Key", "GetECDSAKey", "GetCert", "GetKey": fallthrough case "MarshalYAML": fallthrough case "Doc": fallthrough case "APIUrl": fallthrough case "Endpoint": // t.Logf("Skipping %v", nextChain) continue } var resS []reflect.Value require.NotPanics(t, func() { resS = method.Call(nil) }, "Method chain: %v", nextChain) if len(resS) == 0 { continue } res := resS[0] // skip result if it has the same type // to avoid infinite recursion on methods like DeepCopy if res.Type() == typ { continue } callMethods(t, res, nextChain...) } } func testConfigLoaderBytes(t testing.TB, b []byte, failOnError bool) { t.Helper() p, err := configloader.NewFromBytes(b) if err != nil { if failOnError { t.Fatalf("Failed to load: %s.", err) } else { t.Skipf("Failed to load, skipping: %s.", err) } } callMethods(t, reflect.ValueOf(p)) } // TODO(aleksi): maybe remove once Go 1.18 is out; see https://github.com/golang/go/issues/47413 func TestConfigLoader(t *testing.T) { t.Parallel() files, err := filepath.Glob(filepath.Join("testdata", "*.test")) require.NoError(t, err) for _, file := range files { t.Run(file, func(t *testing.T) { t.Parallel() b, err := os.ReadFile(file) require.NoError(t, err) testConfigLoaderBytes(t, b, true) }) } } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/decoder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package decoder provides a YAML decoder for machine configuration documents. package decoder import ( "cmp" "errors" "fmt" "io" "github.com/siderolabs/gen/xyaml" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" ) // ErrMissingKind indicates that the manifest is missing a kind. var ErrMissingKind = errors.New("missing kind") // ErrMissingAPIVersion indicates that the manifest is missing an apiVersion. var ErrMissingAPIVersion = errors.New("missing apiVersion") const ( // ManifestAPIVersionKey is the string indicating a manifest's version. ManifestAPIVersionKey = "apiVersion" // ManifestKindKey is the string indicating a manifest's kind. ManifestKindKey = "kind" // ManifestDeprecatedKeyMachine represents the deprecated v1alpha1 manifest. ManifestDeprecatedKeyMachine = "machine" // ManifestDeprecatedKeyCluster represents the deprecated v1alpha1 manifest. ManifestDeprecatedKeyCluster = "cluster" // ManifestDeprecatedKeyDebug represents the deprecated v1alpha1 manifest. ManifestDeprecatedKeyDebug = "debug" // ManifestDeprecatedKeyPersist represents the deprecated v1alpha1 manifest. ManifestDeprecatedKeyPersist = "persist" ) // Decoder represents a multi-doc YAML decoder. type Decoder struct{} // Decode decodes all known manifests. func (d *Decoder) Decode(r io.Reader, allowPatchDelete bool) ([]config.Document, error) { return parse(r, allowPatchDelete) } // NewDecoder initializes and returns a `Decoder`. func NewDecoder() *Decoder { return &Decoder{} } type documentID struct { APIVersion string Kind string Name string } //nolint:gocyclo func parse(r io.Reader, allowPatchDelete bool) (decoded []config.Document, err error) { // Recover from yaml.v3 panics because we rely on machine configuration loading _a lot_. defer func() { if p := recover(); p != nil { err = fmt.Errorf("recovered: %v", p) } }() decoded = []config.Document{} dec := yaml.NewDecoder(r) dec.KnownFields(true) knownDocuments := map[documentID]struct{}{} // Iterate through all defined documents. for i := 0; ; i++ { var manifests yaml.Node if err = dec.Decode(&manifests); err != nil { if errors.Is(err, io.EOF) { return decoded, nil } return nil, fmt.Errorf("decode error: %w", err) } if manifests.Kind != yaml.DocumentNode { return nil, fmt.Errorf("expected a document at line %d", manifests.Line) } if allowPatchDelete { decoded, err = AppendDeletesTo(&manifests, decoded, i) if err != nil { return nil, fmt.Errorf("error processing patch delete statements at line %d: %w", manifests.Line, err) } if manifests.IsZero() { continue } } for _, manifest := range manifests.Content { switch manifest.Kind { //nolint:exhaustive case yaml.MappingNode: // expected case yaml.ScalarNode: if manifest.Tag == "!!null" { // skip null documents continue } fallthrough default: return nil, fmt.Errorf("expected a YAML document at line %d", manifest.Line) } id := documentID{ APIVersion: findValue(manifest, ManifestAPIVersionKey, false), Kind: cmp.Or(findValue(manifest, ManifestKindKey, false), "v1alpha1"), Name: findValue(manifest, "name", false), } if _, ok := knownDocuments[id]; ok { return nil, fmt.Errorf("duplicate document %s/%s/%s is not allowed (line %d)", id.APIVersion, id.Kind, id.Name, manifest.Line) } knownDocuments[id] = struct{}{} var target config.Document if target, err = decode(manifest); err != nil { return nil, fmt.Errorf("error decoding document %s/%s/%s (line %d): %w", id.APIVersion, id.Kind, id.Name, manifest.Line, err) } decoded = append(decoded, target) } } } //nolint:gocyclo func decode(manifest *yaml.Node) (target config.Document, err error) { var ( version string kind string ) for i, node := range manifest.Content { switch node.Value { case ManifestKindKey: if len(manifest.Content) < i+1 { return nil, errors.New("missing manifest content") } if err = manifest.Content[i+1].Decode(&kind); err != nil { return nil, fmt.Errorf("kind decode: %w", err) } case ManifestAPIVersionKey: if len(manifest.Content) < i+1 { return nil, errors.New("missing manifest content") } if err = manifest.Content[i+1].Decode(&version); err != nil { return nil, fmt.Errorf("version decode: %w", err) } case ManifestDeprecatedKeyMachine, ManifestDeprecatedKeyCluster, ManifestDeprecatedKeyDebug, ManifestDeprecatedKeyPersist: version = "v1alpha1" } } switch { case version == "v1alpha1" && kind == "": target, err = registry.New("v1alpha1", "") case kind == "": err = ErrMissingKind case version == "": err = ErrMissingAPIVersion default: target, err = registry.New(kind, version) } if err != nil { return nil, err } if err = manifest.Decode(target); err != nil { return nil, fmt.Errorf("error decoding %s to %T: %w", kind, target, err) } if err = xyaml.CheckUnknownKeys(target, manifest); err != nil { return nil, err } return target, nil } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/decoder_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package decoder_test import ( "bytes" "io/fs" "os" "path/filepath" "testing" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader/internal/decoder" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) type Meta struct { MetaKind string `yaml:"kind"` MetaAPIVersion string `yaml:"apiVersion,omitempty"` } func (m Meta) Kind() string { return m.MetaKind } func (m Meta) APIVersion() string { return m.MetaAPIVersion } type Mock struct { Meta Test bool `yaml:"test"` } func (m *Mock) Clone() config.Document { return m } type MockV2 struct { Meta Slice []Mock `yaml:"slice"` Map map[string]*Mock `yaml:"map"` } func (m *MockV2) Clone() config.Document { return m } type MockV3 struct { Meta Omit bool `yaml:"omit,omitempty"` } func (m *MockV3) Clone() config.Document { return m } type KubeletConfig struct { Meta v1alpha1.KubeletConfig `yaml:",inline"` } func (m *KubeletConfig) Clone() config.Document { return m } type MockUnstructured struct { Meta Pods []v1alpha1.Unstructured `yaml:"pods,omitempty"` } func (m *MockUnstructured) Clone() config.Document { return m } func init() { registry.Register("mock", func(version string) config.Document { switch version { case "v1alpha2": return &MockV2{} case "v1alpha3": return &MockV3{} } return &Mock{} }) registry.Register("kubelet", func(string) config.Document { return &KubeletConfig{} }) registry.Register("unstructured", func(string) config.Document { return &MockUnstructured{} }) } func TestDecoder(t *testing.T) { t.Parallel() tests := []struct { name string source []byte expected []config.Document expectedErr string }{ { name: "valid", source: []byte(`--- kind: mock apiVersion: v1alpha1 test: true `), expected: []config.Document{ &Mock{ Test: true, }, }, expectedErr: "", }, { name: "empty docs", source: []byte(`--- kind: mock apiVersion: v1alpha1 test: true --- --- `), expected: []config.Document{ &Mock{ Test: true, }, }, expectedErr: "", }, { name: "missing kind", source: []byte(`--- apiVersion: v1alpha2 test: true `), expected: nil, expectedErr: "error decoding document v1alpha2/v1alpha1/ (line 2): missing kind", }, { name: "empty kind", source: []byte(`--- kind: apiVersion: v1alpha2 test: true `), expected: nil, expectedErr: "error decoding document v1alpha2/v1alpha1/ (line 2): missing kind", }, { name: "tab instead of spaces", source: []byte(`--- kind: mock apiVersion: v1alpha1 spec: test: true `), expected: nil, expectedErr: "decode error: yaml: while scanning for the next token at line 5: found character that cannot start any token", }, { name: "extra field", source: []byte(`--- kind: mock apiVersion: v1alpha1 test: true extra: fail `), expected: nil, expectedErr: "error decoding document v1alpha1/mock/ (line 2): unknown keys found during decoding:\nextra: fail\n", }, { name: "extra fields in map", source: []byte(`--- kind: mock apiVersion: v1alpha2 map: first: test: true extra: me `), expected: nil, expectedErr: "error decoding document v1alpha2/mock/ (line 2): unknown keys found during decoding:\nmap:\n first:\n extra: me\n", }, { name: "extra fields in slice", source: []byte(`--- kind: mock apiVersion: v1alpha2 slice: - test: true not: working more: extra fields: here `), expected: nil, expectedErr: "error decoding document v1alpha2/mock/ (line 2): unknown keys found during decoding:\nslice:\n - fields: here\n more: extra\n not: working\n", }, { name: "extra zero fields in map", source: []byte(`--- kind: mock apiVersion: v1alpha2 map: second: a: b: {} `), expected: nil, expectedErr: "error decoding document v1alpha2/mock/ (line 2): unknown keys found during decoding:\nmap:\n second:\n a:\n b: {}\n", }, { name: "valid nested", source: []byte(`--- kind: mock apiVersion: v1alpha2 slice: - test: true map: first: test: true second: test: false `), expected: []config.Document{ &MockV2{ Map: map[string]*Mock{ "first": { Test: true, }, "second": { Test: false, }, }, Slice: []Mock{ {Test: true}, }, }, }, expectedErr: "", }, { name: "kubelet config", source: []byte(`--- kind: kubelet apiVersion: v1alpha1 extraMounts: - destination: /var/local options: - rbind - rshared - rw source: /var/local `), expected: nil, expectedErr: "", }, { name: "omit empty test", source: []byte(`--- kind: mock apiVersion: v1alpha3 omit: false `), expected: nil, expectedErr: "", }, { name: "internal error", source: []byte(": \xea"), expected: nil, expectedErr: "decode error: yaml: offset 4: incomplete UTF-8 octet sequence", }, { name: "unstructured config", source: []byte(`--- kind: unstructured apiVersion: v1alpha1 pods: - destination: /var/local options: - rbind - rw source: /var/local something: 1.34 `), expected: nil, expectedErr: "", }, { name: "omit empty test", source: []byte(`--- kind: mock apiVersion: v1alpha3 omit: false `), }, { name: "misspelled apiVersion", source: []byte(`--- apiversion: v1alpha1 kind: ExtensionServiceConfig config: - name: nut-client configFiles: - content: MONITOR ${upsmonHost} 1 remote pass foo mountPath: /usr/local/etc/nut/upsmon.conf `), expectedErr: "error decoding document /ExtensionServiceConfig/ (line 2): missing apiVersion", }, { name: "missing apiVersion", source: []byte(`--- kind: mock test: true `), expectedErr: "error decoding document /mock/ (line 2): missing apiVersion", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() d := decoder.NewDecoder() actual, err := d.Decode(bytes.NewReader(tt.source), false) if tt.expected != nil { assert.Equal(t, tt.expected, actual) } if tt.expectedErr == "" { assert.NoError(t, err) } else { assert.EqualError(t, err, tt.expectedErr) } }) } } func TestDecoderV1Alpha1Config(t *testing.T) { t.Parallel() files, err := filepath.Glob(filepath.Join("testdata", "*.yaml")) require.NoError(t, err) for _, file := range files { t.Run(file, func(t *testing.T) { t.Parallel() contents, err := os.ReadFile(file) require.NoError(t, err) d := decoder.NewDecoder() _, err = d.Decode(bytes.NewReader(contents), false) assert.NoError(t, err) }) } } func TestDoubleV1Alpha1(t *testing.T) { t.Parallel() files := os.DirFS("testdata/double").(fs.ReadFileFS) contents := must.Value(files.ReadFile("v1alpha1.yaml"))(t) d := decoder.NewDecoder() _, err := d.Decode(bytes.NewReader(contents), false) require.Error(t, err) require.ErrorContains(t, err, "not allowed") } func BenchmarkDecoderV1Alpha1Config(b *testing.B) { b.ReportAllocs() contents, err := os.ReadFile("testdata/controlplane.yaml") require.NoError(b, err) for b.Loop() { d := decoder.NewDecoder() _, err = d.Decode(bytes.NewReader(contents), false) assert.NoError(b, err) } } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/delete.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package decoder import ( "errors" "fmt" "slices" "strconv" "strings" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // AppendDeletesTo appends all delete selectors found in the given YAML node to the given destination slice. func AppendDeletesTo(n *yaml.Node, dest []config.Document, idx int) (_ []config.Document, err error) { defer func() { if r := recover(); r != nil { if re, ok := r.(error); ok { err = re } } }() allDeletes(n)(func(path []string, elem delElem) bool { switch elem.parent.Kind { case yaml.DocumentNode: dest = append(dest, makeSelector(path, n.Content[0], idx, "", "")) case yaml.MappingNode: dest = append(dest, makeSelector(path, n.Content[0], idx, "", "")) case yaml.SequenceNode: dest = append(dest, makeSequenceSelector(path, n.Content[0], elem.node, idx)) case yaml.ScalarNode, yaml.AliasNode, yaml.StreamNode: } return true }) return dest, nil } func allDeletes(node *yaml.Node) func(yield func([]string, delElem) bool) { return func(yield func([]string, delElem) bool) { _, okToDel := processNode(nil, node, make([]string, 0, 8), yield) if okToDel { *node = yaml.Node{} } } } func makeSequenceSelector(path []string, root, node *yaml.Node, i int) Selector { if node.Kind != yaml.MappingNode { panic(errors.New("expected a mapping node")) } // map node inside sequence node, collect the first key:val aside from $patch:delete as selector for j := 0; j < len(node.Content)-1; j += 2 { key := node.Content[j] val := node.Content[j+1] if val.Kind == yaml.ScalarNode && key.Value == "$patch" && val.Value == "delete" { continue } return makeSelector(path, root, i, key.Value, val.Value) } panic(errors.New("no key:val found in sequence node for path " + strings.Join(path, "."))) } func makeSelector(path []string, root *yaml.Node, i int, key, val string) Selector { isRequired := len(path) == 0 apiVersion := findValue(root, "apiVersion", isRequired) kind := findValue(root, "kind", isRequired) switch { case kind == "" && apiVersion == "": kind = v1alpha1.Version // legacy document case kind != "" && apiVersion != "": default: panic(fmt.Errorf("kind and apiVersion must be both set for path %s", strings.Join(path, "."))) } sel := selector{ path: slices.Clone(path), docIdx: i, docAPIVersion: apiVersion, docKind: kind, key: key, value: val, } switch name := findValue(root, "name", false); name { case "": return &sel default: return &namedSelector{ selector: sel, name: name, } } } type delElem struct { path []string parent, node *yaml.Node } // processNode recursively processes a YAML node, searching for a "$patch: delete" nodes and calling the yield function // with path for each one found. // //nolint:gocyclo,cyclop func processNode( parent, v *yaml.Node, path []string, yield func(path []string, d delElem) bool, ) (bool, bool) { if v.Kind != yaml.DocumentNode && parent == nil { panic(errors.New("parent must be non-nil for non-document nodes")) } switch v.Kind { case yaml.DocumentNode: okToCont, okToDel := processNode(v, v.Content[0], path, yield) switch { case !okToCont: return false, okToDel case okToDel: return false, true default: return false, isEmptyDoc(v.Content[0]) } case yaml.MappingNode: for i := 0; i < len(v.Content)-1; i += 2 { keyNode := v.Content[i] valueNode := v.Content[i+1] if valueNode.Kind == yaml.ScalarNode && keyNode.Value == "$patch" && valueNode.Value == "delete" { if parent.Kind != yaml.SequenceNode { ensureNoSeqInChain(path) } return yield(path, delElem{path: path, parent: parent, node: v}), true } okToCont, okToDel := processNode(v, valueNode, append(path, keyNode.Value), yield) if !okToCont { return false, okToDel } else if okToDel { v.Content = slices.Delete(v.Content, i, i+2) i -= 2 if len(v.Content) == 0 { return true, true } } } case yaml.SequenceNode: for i := 0; i < len(v.Content); i++ { okToCont, okToDel := processNode(v, v.Content[i], append(path, "["+strconv.Itoa(i)+"]"), yield) if !okToCont { return false, okToDel } else if okToDel { v.Content = slices.Delete(v.Content, i, i+1) i-- if len(v.Content) == 0 { return true, true } } } case yaml.ScalarNode, yaml.AliasNode, yaml.StreamNode: } return true, false } func isEmptyDoc(node *yaml.Node) bool { if node.Kind != yaml.MappingNode { return false } for i := 0; i < len(node.Content)-1; i += 2 { keyNode := node.Content[i] val := node.Content[i+1] if keyNode.Kind != yaml.ScalarNode || val.Kind != yaml.ScalarNode { return false } if keyNode.Value != "version" && keyNode.Value != "kind" && keyNode.Value != "name" { return false } } return true } func ensureNoSeqInChain(path []string) { for _, p := range path { if p[0] == '[' { panic(errors.New("cannot delete an inner key in '" + strings.Join(path, ".") + "'")) } } } func findValue(node *yaml.Node, key string, required bool) string { if node.Kind != yaml.MappingNode { panic(errors.New("expected a mapping node")) } for i := 0; i < len(node.Content)-1; i += 2 { keyNode := node.Content[i] val := node.Content[i+1] if keyNode.Kind == yaml.ScalarNode && keyNode.Value == key { return val.Value } } if required { panic(fmt.Errorf("missing %s in document for which $patch: delete is used", key)) } return "" } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/delete_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package decoder_test import ( "bytes" _ "embed" "fmt" "io" "strings" "testing" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader/internal/decoder" "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) var ( //go:embed testdata/delete/delete.yaml patchDelete []byte //go:embed testdata/delete/delete_expected.yaml patchDeleteExpected []byte ) func TestExtractDeletes(t *testing.T) { result, b := must.Values(extractDeletes(patchDelete))(t) defer func() { if !t.Failed() { return } for _, sel := range result { t.Logf("%#v", sel) } }() require.Equal(t, string(patchDeleteExpected), string(b)) expected := strings.Join( []string{ "{apiVersion:v1alpha1, kind:SideroLinkConfig, idx:0}", "{path:configFiles.[0], apiVersion:v1alpha1, kind:ExtensionServiceConfig, key:content, value:hello, idx:1, name:foo}", "{path:machine.hostname, kind:v1alpha1, idx:2}", "{path:machine.network.[0], kind:v1alpha1, key:interface, value:eth0, idx:2}", }, "\n", ) actual := strings.Join( xslices.Map(result, func(sel config.Document) string { return sel.(fmt.Stringer).String() }), "\n", ) require.Equal(t, expected, actual) } func extractDeletes(in []byte) (result []config.Document, _ []byte, err error) { var cleanedBytes [][]byte dec := yaml.NewDecoder(bytes.NewReader(in)) for i := 0; ; i++ { node := &yaml.Node{} err = dec.Decode(node) if err != nil { if err == io.EOF { break } return nil, nil, err } result, err = decoder.AppendDeletesTo(node, result, i) if err != nil { return nil, nil, err } if !node.IsZero() { b, err := encoder.NewEncoder(node, encoder.WithComments(encoder.CommentsDisabled)).Encode() if err != nil { return nil, nil, err } cleanedBytes = append(cleanedBytes, b) } } return result, bytes.Join(cleanedBytes, []byte("---\n")), nil } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/selector.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package decoder import ( "errors" "fmt" "reflect" "slices" "strconv" "strings" "github.com/siderolabs/talos/pkg/machinery/config/config" ) // Selector represents a delete selector for a document. type Selector interface { config.Document DocIdx() int ApplyTo(config.Document) error } type selector struct { path []string docIdx int docAPIVersion string docKind string key string value string } func (s *selector) Kind() string { return s.docKind } func (s *selector) APIVersion() string { return s.docAPIVersion } func (s *selector) Clone() config.Document { return new(s.clone()) } func (s *selector) DocIdx() int { return s.docIdx } func (s *selector) PathAsString() string { return strings.Join(s.path, ".") } func (s *selector) clone() selector { return selector{ path: slices.Clone(s.path), docIdx: s.docIdx, docAPIVersion: s.docAPIVersion, docKind: s.docKind, key: s.key, value: s.value, } } func (s *selector) String() string { return s.toString("") } func (s *selector) toString(more string) string { var builder strings.Builder writeThing := func(key, val string) { if val != "" { if builder.Len() > 1 { builder.WriteString(", ") } builder.WriteString(key) builder.WriteRune(':') builder.WriteString(val) } } builder.WriteRune('{') writeThing("path", s.PathAsString()) writeThing("apiVersion", s.docAPIVersion) writeThing("kind", s.docKind) writeThing("key", s.key) writeThing("value", s.value) writeThing("idx", strconv.Itoa(s.docIdx)) if more != "" { builder.WriteString(", ") builder.WriteString(more) } builder.WriteRune('}') return builder.String() } // ErrZeroedDocument is returned when the document is empty after applying the delete selector. var ErrZeroedDocument = errors.New("document is empty now") // ApplyTo applies the delete selector to the given document. func (s *selector) ApplyTo(doc config.Document) error { if err := s.applyTo(doc); err != nil { return fmt.Errorf("patch delete: path '%s' in document '%s/%s': %w", s.PathAsString(), doc.APIVersion(), doc.Kind(), err) } return nil } func (s *selector) applyTo(doc config.Document) error { if s.docKind != doc.Kind() || s.docAPIVersion != doc.APIVersion() { return fmt.Errorf( "incorrect document type for %s/%s", s.docAPIVersion, s.docKind, ) } val := reflect.ValueOf(doc) if val.Kind() != reflect.Pointer { return fmt.Errorf("document type is not a pointer") } if len(s.path) == 0 { if doc.Kind() == "" { return errors.New("can't delete the root of the legacy document") } return ErrZeroedDocument } err := deleteForPath(val.Elem(), s.path, s.key, s.value) if err != nil { return fmt.Errorf("failed to delete path '%s': %w", s.PathAsString(), err) } return nil } var searchForType = reflect.TypeFor[string]() // ErrLookupFailed is returned when the lookup failed. var ErrLookupFailed = errors.New("lookup failed") //nolint:gocyclo,cyclop func deleteForPath(val reflect.Value, path []string, key, value string) error { if len(path) == 0 { return errors.New("path is empty") } if val.Kind() == reflect.Pointer || val.Kind() == reflect.Interface { if val.IsNil() { return ErrLookupFailed } return deleteForPath(val.Elem(), path, key, value) } searchFor := path[0] path = path[1:] valType := val.Type() switch val.Kind() { //nolint:exhaustive case reflect.Struct: // Lookup using yaml tag for i := range val.NumField() { structField := valType.Field(i) yamlTagRaw, ok := structField.Tag.Lookup("yaml") if !ok { continue } yamlTags := strings.Split(yamlTagRaw, ",") if yamlTags[0] == searchFor { if len(path) == 0 { val.Field(i).SetZero() return nil } return deleteForPath(val.Field(i), path, key, value) } // if there is an embedded struct, descend into it if len(yamlTags) > 1 && yamlTags[0] == "" && yamlTags[1] == "inline" { err := deleteForPath(val.Field(i), append([]string{searchFor}, path...), key, value) if err == nil { // value found & deleted return nil } // ignore lookup failed errors, we can continue with other fields if !errors.Is(err, ErrLookupFailed) { return err } } } case reflect.Map: if val.IsNil() { break } keyType := valType.Key() // Try assingable and convertible types for key search if searchForType.AssignableTo(keyType) || searchForType.ConvertibleTo(keyType) { searchForVal := reflect.ValueOf(searchFor) if searchForType != keyType { searchForVal = searchForVal.Convert(keyType) } if idx := val.MapIndex(searchForVal); idx.IsValid() { if len(path) == 0 { val.SetMapIndex(searchForVal, reflect.Value{}) return nil } return deleteForPath(idx, path, key, value) } } case reflect.Slice: return deleteStructFrom(val, searchFor, path, key, value) } return ErrLookupFailed } //nolint:gocyclo func deleteStructFrom(searchIn reflect.Value, searchFor string, path []string, key, value string) error { switch { case len(path) != 0: return errors.New("searching for complex paths in slices is not supported") case searchFor == "": return errors.New("searching for '' in a slice is not supported") case searchFor[0] != '[': return errors.New("searching for non-integer keys in slices is not supported") case searchIn.Kind() != reflect.Slice: return errors.New("searching for a key in a non-slice") } for i := 0; i < searchIn.Len(); i++ { //nolint:intrange elem := searchIn.Index(i) for elem.Kind() == reflect.Pointer { elem = elem.Elem() } if elem.Kind() != reflect.Struct && elem.Kind() != reflect.Map { continue } elemType := elem.Type() if elem.Kind() == reflect.Struct { for j := range elemType.NumField() { structField := elemType.Field(j) yamlTagRaw, ok := structField.Tag.Lookup("yaml") if !ok { continue } yamlTags := strings.Split(yamlTagRaw, ",") if yamlTags[0] != key { continue } fieldVal := elem.Field(j) fieldStr := fieldVal.String() // if the field value implements Stringer, use it for comparison instead of the default string conversion if fieldVal.CanInterface() { if stringer, ok := fieldVal.Interface().(fmt.Stringer); ok { fieldStr = stringer.String() } } if fieldStr != value { continue } searchIn.Set(reflect.AppendSlice(searchIn.Slice(0, i), searchIn.Slice(i+1, searchIn.Len()))) return nil } } else { continue } } return ErrLookupFailed } type namedSelector struct { selector name string } func (n *namedSelector) Name() string { return n.name } func (n *namedSelector) String() string { return n.toString("name:" + n.name) } func (n *namedSelector) Clone() config.Document { return &namedSelector{selector: n.selector.clone(), name: n.name} } // ApplyTo applies the delete selector to the given document. func (n *namedSelector) ApplyTo(doc config.Document) error { if err := n.applyTo(doc); err != nil { return fmt.Errorf("named patch delete: document %s/%s: %w", doc.APIVersion(), doc.Kind(), err) } return nil } func (n *namedSelector) applyTo(doc config.Document) error { namedDoc, ok := doc.(config.NamedDocument) if !ok { return errors.New("not a named document, expected " + n.name) } if n.name != namedDoc.Name() { return fmt.Errorf("name mismatch, expected %s, got %s", n.name, namedDoc.Name()) } return n.selector.applyTo(doc) } ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/testdata/delete/delete.yaml ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig $patch: delete --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello $patch: delete - content: hello2 mountPath: /etc/foo2 --- version: v1alpha1 machine: hostname: $patch: delete network: - interface: eth0 $patch: delete - interface: eth1 addresses: [10.3.5.5/32] - interface: eth0 dhcp6: true ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/testdata/delete/delete_expected.yaml ================================================ apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello2 mountPath: /etc/foo2 --- version: v1alpha1 machine: network: - interface: eth1 addresses: [10.3.5.5/32] - interface: eth0 dhcp6: true ================================================ FILE: pkg/machinery/config/configloader/internal/decoder/testdata/double/v1alpha1.yaml ================================================ version: v1alpha1 machine: type: controlplane --- version: v1alpha1 machine: type: worker ================================================ FILE: pkg/machinery/config/configloader/testdata/controlplane.test ================================================ version: v1alpha1 # Indicates the schema used to decode the contents. debug: false # Enable verbose logging to the console. persist: true # Indicates whether to pull the machine config upon every boot. # Provides machine specific configuration options. machine: type: controlplane # Defines the role of the machine within the cluster. token: 7a10fa.izhckmwn5xmsjwi5 # The `token` is used by a machine to join the PKI of the cluster. # The root certificate authority of the PKI. ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEFJazRNbkdRZ01HQmkrM3p6aHZsRlZNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU1UQTRNall4TWpBMU5USmFGdzB6TVRBNE1qUXhNakExTlRKYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBUzFKbXZ3WFFjTTNJVWRQb1FvTk5jVzh2bzBBenVENUIwb1hoCk9sSnpUa2VqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVL25nVE5YaG9YbkFheU5WaQp3RUREQjdGdHgvQXdCUVlESzJWd0EwRUFuTS9EMFNsZVhBcFVoWlpXQkp0djd2NnhjTFl4ekRoWmd3eFIzODFHCmNjM21XS09MSHZvOENVQU85WmpqZGV4UCtLN1dyMjhxZUxuMWwxTG83SWZWQkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJSkJpdHJaQ1VrMFl1YytsblpqZXFHeVl1MGQxTGpNV2Fvd2FQL2diaTIwQwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K # Extra certificate subject alternative names for the machine's certificate. certSANs: [] # # Uncomment this to enable SANs. # - 10.0.0.10 # - 172.16.0.10 # - 192.168.0.10 # Used to provide additional options to the kubelet. kubelet: {} # # The `image` field is an optional reference to an alternative kubelet image. # image: ghcr.io/talos-systems/kubelet:v1.22.1 # # The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list. # clusterDNS: # - 10.96.0.10 # - 169.254.2.53 # # The `extraArgs` field is used to provide additional flags to the kubelet. # extraArgs: # key: value # # The `extraMounts` field is used to add additional mounts to the kubelet container. # extraMounts: # - destination: /var/lib/example # type: bind # source: /var/lib/example # options: # - rshared # - rw # Provides machine specific network configuration options. network: {} # # `interfaces` is used to define the network interface configuration. # interfaces: # - interface: eth0 # The interface name. # # Assigns static IP addresses to the interface. # addresses: # - 192.168.2.0/24 # # A list of routes associated with the interface. # routes: # - network: 0.0.0.0/0 # The route's network. # gateway: 192.168.2.1 # The route's gateway. # metric: 1024 # The optional metric for the route. # mtu: 1500 # The interface's MTU. # # # # Bond specific options. # # bond: # # # The interfaces that make up the bond. # # interfaces: # # - eth0 # # - eth1 # # mode: 802.3ad # A bond option. # # lacpRate: fast # A bond option. # # # Indicates if DHCP should be used to configure the interface. # # dhcp: true # # # DHCP specific options. # # dhcpOptions: # # routeMetric: 1024 # The priority of all routes received via DHCP. # # # Wireguard specific configuration. # # # wireguard server example # # wireguard: # # privateKey: ABCDEF... # Specifies a private key configuration (base64 encoded). # # listenPort: 51111 # Specifies a device's listening port. # # # Specifies a list of peer configurations to apply to a device. # # peers: # # - publicKey: ABCDEF... # Specifies the public key of this peer. # # endpoint: 192.168.1.3 # Specifies the endpoint of this peer entry. # # # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. # # allowedIPs: # # - 192.168.1.0/24 # # # wireguard peer example # # wireguard: # # privateKey: ABCDEF... # Specifies a private key configuration (base64 encoded). # # # Specifies a list of peer configurations to apply to a device. # # peers: # # - publicKey: ABCDEF... # Specifies the public key of this peer. # # endpoint: 192.168.1.2 # Specifies the endpoint of this peer entry. # # persistentKeepaliveInterval: 10s # Specifies the persistent keepalive interval for this peer. # # # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. # # allowedIPs: # # - 192.168.1.0/24 # # # Virtual (shared) IP address configuration. # # vip: # # ip: 172.16.199.55 # Specifies the IP address to be used. # # Used to statically set the nameservers for the machine. # nameservers: # - 8.8.8.8 # - 1.1.1.1 # # Allows for extra entries to be added to the `/etc/hosts` file # extraHostEntries: # - ip: 192.168.1.100 # The IP of the host. # # The host alias. # aliases: # - example # - example.domain.tld # Used to provide instructions for installations. install: disk: /dev/sda # The disk used for installations. image: ghcr.io/aleksi/installer:v0.12.0-alpha.1-20-g5f5ac12f1 # Allows for supplying the image used to perform the installation. bootloader: true # Indicates if a bootloader should be installed. wipe: false # Indicates if the installation disk should be wiped at installation time. # # Look up disk using disk attributes like model, size, serial and others. # diskSelector: # size: 4GB # Disk size. # model: WDC* # Disk model `/sys/block//device/model`. # # Allows for supplying extra kernel args via the bootloader. # extraKernelArgs: # - talos.platform=metal # - reboot=k # Features describe individual Talos features that can be switched on or off. features: rbac: true # Enable role-based access control (RBAC). # # Used to partition, format and mount additional disks. # # MachineDisks list example. # disks: # - device: /dev/sdb # The name of the disk to use. # # A list of partitions to create on the disk. # partitions: # - mountpoint: /var/mnt/extra # Where to mount the partition. # # # # The size of partition: either bytes or human readable representation. If `size:` is omitted, the partition is sized to occupy the full disk. # # # Human readable representation. # # size: 100 MB # # # Precise value in bytes. # # size: 1073741824 # # Allows the addition of user specified files. # # MachineFiles usage example. # files: # - content: '...' # The contents of the file. # permissions: 0o666 # The file's permissions in octal. # path: /tmp/file.txt # The path of the file. # op: append # The operation to use # # The `env` field allows for the addition of environment variables. # # Environment variables definition examples. # env: # GRPC_GO_LOG_SEVERITY_LEVEL: info # GRPC_GO_LOG_VERBOSITY_LEVEL: "99" # https_proxy: http://SERVER:PORT/ # env: # GRPC_GO_LOG_SEVERITY_LEVEL: error # https_proxy: https://USERNAME:PASSWORD@SERVER:PORT/ # env: # https_proxy: http://DOMAIN\USERNAME:PASSWORD@SERVER:PORT/ # # Used to configure the machine's time settings. # # Example configuration for cloudflare ntp server. # time: # disabled: false # Indicates if the time service is disabled for the machine. # # Specifies time (NTP) servers to use for setting the system time. # servers: # - time.cloudflare.com # # Used to configure the machine's sysctls. # # MachineSysctls usage example. # sysctls: # kernel.domainname: talos.dev # net.ipv4.ip_forward: "0" # # Used to configure the machine's container image registry mirrors. # registries: # # Specifies mirror configuration for each registry. # mirrors: # ghcr.io: # # List of endpoints (URLs) for registry mirrors to use. # endpoints: # - https://registry.insecure # - https://ghcr.io/v2/ # # Specifies TLS & auth configuration for HTTPS image registries. # config: # registry.insecure: # # The TLS configuration for the registry. # tls: # insecureSkipVerify: true # Skip TLS server certificate verification (not recommended). # # # # Enable mutual TLS authentication with the registry. # # clientIdentity: # # crt: TFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSklla05DTUhGLi4u # # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # # # The auth configuration for this registry. # # auth: # # username: username # Optional registry authentication. # # password: password # Optional registry authentication. # # Machine system disk encryption configuration. # systemDiskEncryption: # # Ephemeral partition encryption. # ephemeral: # provider: luks2 # Encryption provider to use for the encryption. # # Defines the encryption keys generation and storage method. # keys: # - # Deterministically generated key from the node UUID and PartitionLabel. # nodeID: {} # slot: 0 # Key slot number for LUKS2 encryption. # # # # Cipher kind to use for the encryption. Depends on the encryption provider. # # cipher: aes-xts-plain64 # # # Defines the encryption sector size. # # blockSize: 4096 # # # Additional --perf parameters for the LUKS2 encryption. # # options: # # - no_read_workqueue # # - no_write_workqueue # Provides cluster specific configuration options. cluster: id: Ahg8OEr1t2urQH2wcG2_g0-t1Rtf-QRtA3uBCIe9S3w= # Globally unique identifier for this cluster. secret: y1VWsecCqtdFYWP1xsxw7p2YZJ/Bdwa+H9NzXlIfEbY= # Shared secret of cluster. # Provides control plane specific configuration options. controlPlane: endpoint: https://1.2.3.4:6443 # Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. clusterName: test # Configures the cluster's name. # Provides cluster specific network configuration options. network: dnsDomain: cluster.local # The domain used by Kubernetes DNS. # The pod subnet CIDR. podSubnets: - 10.244.0.0/16 # The service subnet CIDR. serviceSubnets: - 10.96.0.0/12 # # The CNI used. # cni: # name: custom # Name of CNI to use. # # URLs containing manifests to apply for the CNI. # urls: # - https://docs.projectcalico.org/archive/v3.20/manifests/canal.yaml token: y9pn8n.g7wi0m50e0wu9b9v # The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster. aescbcEncryptionSecret: /ZaQCnD11lrRRF2OH7ez96cpn99VWhD1AXdWbZwLQrc= # The key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/). # The base64 encoded root certificate authority used by Kubernetes. ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRRm1GY0ZiaUtCQ1dtazB5YVZUM1Z5ekFLQmdncWhrak9QUVFEQkRBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXhNRGd5TmpFeU1EVTFNbG9YRFRNeE1EZ3lOREV5TURVMQpNbG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQk1LTXBOZTkxanBmYWZxYlgzNDYyTEk1ZHBSRUpNdlY3U0t6YXZWWHRDa29oTFhpcHZMY3BiUkIyRm8KVGFzNURmSFc3Q2gwQng1S1RnOVRJelVrSGlpallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVrWG5oWUFkbXRDVG15aDVLOTNLTTA2Sk9aME13Q2dZSUtvWkl6ajBFQXdRRFNBQXdSUUlnQVZXYUkwQlUKUUt1MHRoRDNmSjBkaitqbEVxTW5nUGdQaXhJck1mdEdmU1FDSVFES3VIMWIvZzdRS1p1QmU3UEJocC9aZ2xrRgpRdTI5aHdJU0JjbE1aak9tK0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUFvV1BZTUluUThPYUVvb3NnZ2ozUE5XMzVwQ0tIZlFDNWg4TERNalI2dmNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFRXdveWsxNzNXT2w5cCtwdGZmanJZc2psMmxFUWt5OVh0SXJOcTlWZTBLU2lFdGVLbTh0eQpsdEVIWVdoTnF6a044ZGJzS0hRSEhrcE9EMU1qTlNRZUtBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= # The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation. aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFXZ0F3SUJBZ0lRRnB2RkhrM1pOOGtQQkUyTElBN2FvekFLQmdncWhrak9QUVFEQkRBQU1CNFgKRFRJeE1EZ3lOakV5TURVMU1sb1hEVE14TURneU5ERXlNRFUxTWxvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRDVDNGprWHhzVWhyb0ZwOGpzY0tkYndyaSsxWURMQm9NMDNlNTMxM2xsbUhEbWZ6dFh1CnNjdkQ2RTFyY0hHV3ljRlJmbEc0K2hVYWtrNHFUSUtRMEF5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVU0VGJqMnUvMUhySU5PSVBPeGZnZHlGeUZwVDh3Q2dZSUtvWkl6ajBFQXdRRFNRQXdSZ0loCkFKRERJZ2lMRE91cHZxWDVVdy9UakJ3NFltRXYzZVRqSHprczRBN1gwNHdWQWlFQTg3Ulo4WjlUdmRIT3BJRHUKdjBEN1ArMFdnS3pCTWpKY05KQ012L1RLSkE0PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUMzUFNaMjZGekVoeEhzdGRMK1Y4Zk9vQS9WV3hsd3YzRWozVnBwaVpiaDVvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUGtMaU9SZkd4U0d1Z1dueU94d3AxdkN1TDdWZ01zR2d6VGQ3bmZYZVdXWWNPWi9PMWU2eAp5OFBvVFd0d2NaYkp3VkYrVWJqNkZScVNUaXBNZ3BEUURBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= # The base64 encoded private key for service account token generation. serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUF0eXJVdG53YmgxUk5wSThyUzdCbEl1OWhWdUFwQ2RxdXhSelVoVXJnSGdvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFKzZmd3Y2RzJvdGdNWExHa0ErS1BKbVQ4ZDJISWx1aEdJVWw2NTgyYmprMVVCR3U1SWNpQwoyanRHYnpGSTEweTlOMlZJZDl5d05Mc3dGbjV4YWRyYy9BPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= # API server specific configuration options. apiServer: # Extra certificate subject alternative names for the API server's certificate. certSANs: - 1.2.3.4 # # The container image used in the API server manifest. # image: k8s.gcr.io/kube-apiserver:v1.22.1 # Controller manager server specific configuration options. controllerManager: {} # # The container image used in the controller manager manifest. # image: k8s.gcr.io/kube-controller-manager:v1.22.1 # Kube-proxy server-specific configuration options proxy: {} # # The container image used in the kube-proxy manifest. # image: k8s.gcr.io/kube-proxy:v1.22.1 # Scheduler server specific configuration options. scheduler: {} # # The container image used in the scheduler manifest. # image: k8s.gcr.io/kube-scheduler:v1.22.1 # Etcd specific configuration options. etcd: # The `ca` is the root certificate authority of the PKI. ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRQTV1QkpPTG1wL3RrbEVRS0FFV1RtakFLQmdncWhrak9QUVFEQkRBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl4TURneU5qRXlNRFUxTWxvWERUTXhNRGd5TkRFeU1EVTFNbG93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkdxeWR0MjZXRUpSCllyV1NJZkp3QnZHSTZTWENqcENvU1Z6U25jWDFYaUwvY2I0RFJINU5WQ3JuZU9EYjZRb3ZwUTFKaEE3RTR6RDAKTHB5dTE1TW1zM2VqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVR0lwR3JsOFpqTHV5CkFOdzR4Y0RMSGxzZ0c1Z3dDZ1lJS29aSXpqMEVBd1FEU0FBd1JRSWhBT2dsQzlZbkIxZlE4cnNDZSsyY2pQR2MKODlUT0ZXMk10bEVFcWtYdm03cWpBaUE3a3JXRjVXMWxpMmtlVEV5cnZlYjZuajBGWGZRYi9MbERUQnh5YzJicwpNZz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUg3V2JCd1pHdVZUUmZkdjZDR3JDUzEzYnR5Q2dGL3JyeUV2YkJ3d0hNYThvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFYXJKMjNicFlRbEZpdFpJaDhuQUc4WWpwSmNLT2tLaEpYTktkeGZWZUl2OXh2Z05FZmsxVQpLdWQ0NE52cENpK2xEVW1FRHNUak1QUXVuSzdYa3lhemR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= # # The container image used to create the etcd service. # image: gcr.io/etcd-development/etcd:v3.4.16 # A list of urls that point to additional manifests. extraManifests: [] # - https://www.example.com/manifest1.yaml # - https://www.example.com/manifest2.yaml # A list of inline Kubernetes manifests. inlineManifests: [] # - name: namespace-ci # Name of the manifest. # contents: |- # Manifest contents as a string. # apiVersion: v1 # kind: Namespace # metadata: # name: ci # # Core DNS specific configuration options. # coreDNS: # image: docker.io/coredns/coredns:1.8.4 # The `image` field is an override to the default coredns image. # # External cloud provider configuration. # externalCloudProvider: # enabled: true # Enable external cloud provider. # # A list of urls that point to additional manifests for an external cloud provider. # manifests: # - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml # - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml # # A map of key value pairs that will be added while fetching the extraManifests. # extraManifestHeaders: # Token: "1234567" # X-ExtraInfo: info # # Settings for admin kubeconfig generation. # adminKubeconfig: # certLifetime: 1h0m0s # Admin kubeconfig certificate lifetime (default is 1 year). ================================================ FILE: pkg/machinery/config/configloader/testdata/empty1.test ================================================ machine: ================================================ FILE: pkg/machinery/config/configloader/testdata/empty2.test ================================================ cluster: ================================================ FILE: pkg/machinery/config/configloader/testdata/fuzz/FuzzConfigLoader/dfd1adfe630cffc2 ================================================ go test fuzz v1 []byte("apiVersion: v1alpha1\nkind: SideroLinkConfig") ================================================ FILE: pkg/machinery/config/configloader/testdata/multidoc1.test ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: grpc://172.20.0.1:4000/?jointoken=foo --- apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: tcp://[fdae:41e4:649b:9303::1]:4001/ --- apiVersion: v1alpha1 kind: EventSinkConfig endpoint: "[fdae:41e4:649b:9303::1]:8080" ================================================ FILE: pkg/machinery/config/configloader/testdata/multidoc2.test ================================================ apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello mountPath: /etc/foo ================================================ FILE: pkg/machinery/config/configloader/testdata/multidoc3.test ================================================ apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block --- apiVersion: v1alpha1 kind: NetworkRuleConfig name: test portSelector: ports: - 53 - 8000-9000 protocol: udp ingress: - subnet: 192.168.0.0/16 except: 192.168.0.3/32 - subnet: 2001::/16 --- apiVersion: v1alpha1 kind: NetworkRuleConfig name: www portSelector: ports: - 80 protocol: tcp ingress: - subnet: 192.168.0.0/16 ================================================ FILE: pkg/machinery/config/configloader/testdata/worker.test ================================================ --- version: v1alpha1 # Indicates the schema used to decode the contents. debug: false # Enable verbose logging to the console. persist: true # Indicates whether to pull the machine config upon every boot. # Provides machine specific configuration options. machine: type: worker # Defines the role of the machine within the cluster. token: 7a10fa.izhckmwn5xmsjwi5 # The `token` is used by a machine to join the PKI of the cluster. # Extra certificate subject alternative names for the machine's certificate. certSANs: [] # # Uncomment this to enable SANs. # - 10.0.0.10 # - 172.16.0.10 # - 192.168.0.10 # Used to provide additional options to the kubelet. kubelet: {} # # The `image` field is an optional reference to an alternative kubelet image. # image: ghcr.io/talos-systems/kubelet:v1.22.1 # # The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list. # clusterDNS: # - 10.96.0.10 # - 169.254.2.53 # # The `extraArgs` field is used to provide additional flags to the kubelet. # extraArgs: # key: value # # The `extraMounts` field is used to add additional mounts to the kubelet container. # extraMounts: # - destination: /var/lib/example # type: bind # source: /var/lib/example # options: # - rshared # - rw # Provides machine specific network configuration options. network: {} # # `interfaces` is used to define the network interface configuration. # interfaces: # - interface: eth0 # The interface name. # # Assigns static IP addresses to the interface. # addresses: # - 192.168.2.0/24 # # A list of routes associated with the interface. # routes: # - network: 0.0.0.0/0 # The route's network. # gateway: 192.168.2.1 # The route's gateway. # metric: 1024 # The optional metric for the route. # mtu: 1500 # The interface's MTU. # # # # Bond specific options. # # bond: # # # The interfaces that make up the bond. # # interfaces: # # - eth0 # # - eth1 # # mode: 802.3ad # A bond option. # # lacpRate: fast # A bond option. # # # Indicates if DHCP should be used to configure the interface. # # dhcp: true # # # DHCP specific options. # # dhcpOptions: # # routeMetric: 1024 # The priority of all routes received via DHCP. # # # Wireguard specific configuration. # # # wireguard server example # # wireguard: # # privateKey: ABCDEF... # Specifies a private key configuration (base64 encoded). # # listenPort: 51111 # Specifies a device's listening port. # # # Specifies a list of peer configurations to apply to a device. # # peers: # # - publicKey: ABCDEF... # Specifies the public key of this peer. # # endpoint: 192.168.1.3 # Specifies the endpoint of this peer entry. # # # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. # # allowedIPs: # # - 192.168.1.0/24 # # # wireguard peer example # # wireguard: # # privateKey: ABCDEF... # Specifies a private key configuration (base64 encoded). # # # Specifies a list of peer configurations to apply to a device. # # peers: # # - publicKey: ABCDEF... # Specifies the public key of this peer. # # endpoint: 192.168.1.2 # Specifies the endpoint of this peer entry. # # persistentKeepaliveInterval: 10s # Specifies the persistent keepalive interval for this peer. # # # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. # # allowedIPs: # # - 192.168.1.0/24 # # # Virtual (shared) IP address configuration. # # vip: # # ip: 172.16.199.55 # Specifies the IP address to be used. # # Used to statically set the nameservers for the machine. # nameservers: # - 8.8.8.8 # - 1.1.1.1 # # Allows for extra entries to be added to the `/etc/hosts` file # extraHostEntries: # - ip: 192.168.1.100 # The IP of the host. # # The host alias. # aliases: # - example # - example.domain.tld # Used to provide instructions for installations. install: disk: /dev/sda # The disk used for installations. image: ghcr.io/aleksi/installer:v0.12.0-alpha.1-20-g5f5ac12f1 # Allows for supplying the image used to perform the installation. bootloader: true # Indicates if a bootloader should be installed. wipe: false # Indicates if the installation disk should be wiped at installation time. # # Look up disk using disk attributes like model, size, serial and others. # diskSelector: # size: 4GB # Disk size. # model: WDC* # Disk model `/sys/block//device/model`. # # Allows for supplying extra kernel args via the bootloader. # extraKernelArgs: # - talos.platform=metal # - reboot=k # Features describe individual Talos features that can be switched on or off. features: rbac: true # Enable role-based access control (RBAC). # # The root certificate authority of the PKI. # # machine CA example # ca: # crt: TFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSklla05DTUhGLi4u # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # Used to partition, format and mount additional disks. # # MachineDisks list example. # disks: # - device: /dev/sdb # The name of the disk to use. # # A list of partitions to create on the disk. # partitions: # - mountpoint: /var/mnt/extra # Where to mount the partition. # # # # The size of partition: either bytes or human readable representation. If `size:` is omitted, the partition is sized to occupy the full disk. # # # Human readable representation. # # size: 100 MB # # # Precise value in bytes. # # size: 1073741824 # # Allows the addition of user specified files. # # MachineFiles usage example. # files: # - content: '...' # The contents of the file. # permissions: 0o666 # The file's permissions in octal. # path: /tmp/file.txt # The path of the file. # op: append # The operation to use # # The `env` field allows for the addition of environment variables. # # Environment variables definition examples. # env: # GRPC_GO_LOG_SEVERITY_LEVEL: info # GRPC_GO_LOG_VERBOSITY_LEVEL: "99" # https_proxy: http://SERVER:PORT/ # env: # GRPC_GO_LOG_SEVERITY_LEVEL: error # https_proxy: https://USERNAME:PASSWORD@SERVER:PORT/ # env: # https_proxy: http://DOMAIN\USERNAME:PASSWORD@SERVER:PORT/ # # Used to configure the machine's time settings. # # Example configuration for cloudflare ntp server. # time: # disabled: false # Indicates if the time service is disabled for the machine. # # Specifies time (NTP) servers to use for setting the system time. # servers: # - time.cloudflare.com # # Used to configure the machine's sysctls. # # MachineSysctls usage example. # sysctls: # kernel.domainname: talos.dev # net.ipv4.ip_forward: "0" # # Used to configure the machine's container image registry mirrors. # registries: # # Specifies mirror configuration for each registry. # mirrors: # ghcr.io: # # List of endpoints (URLs) for registry mirrors to use. # endpoints: # - https://registry.insecure # - https://ghcr.io/v2/ # # Specifies TLS & auth configuration for HTTPS image registries. # config: # registry.insecure: # # The TLS configuration for the registry. # tls: # insecureSkipVerify: true # Skip TLS server certificate verification (not recommended). # # # # Enable mutual TLS authentication with the registry. # # clientIdentity: # # crt: TFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSklla05DTUhGLi4u # # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # # # The auth configuration for this registry. # # auth: # # username: username # Optional registry authentication. # # password: password # Optional registry authentication. # # Machine system disk encryption configuration. # systemDiskEncryption: # # Ephemeral partition encryption. # ephemeral: # provider: luks2 # Encryption provider to use for the encryption. # # Defines the encryption keys generation and storage method. # keys: # - # Deterministically generated key from the node UUID and PartitionLabel. # nodeID: {} # slot: 0 # Key slot number for LUKS2 encryption. # # # # Cipher kind to use for the encryption. Depends on the encryption provider. # # cipher: aes-xts-plain64 # # # Defines the encryption sector size. # # blockSize: 4096 # # # Additional --perf parameters for the LUKS2 encryption. # # options: # # - no_read_workqueue # # - no_write_workqueue # Provides cluster specific configuration options. cluster: id: Ahg8OEr1t2urQH2wcG2_g0-t1Rtf-QRtA3uBCIe9S3w= # Globally unique identifier for this cluster. secret: y1VWsecCqtdFYWP1xsxw7p2YZJ/Bdwa+H9NzXlIfEbY= # Shared secret of cluster. # Provides control plane specific configuration options. controlPlane: endpoint: https://1.2.3.4:6443 # Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. # Provides cluster specific network configuration options. network: dnsDomain: cluster.local # The domain used by Kubernetes DNS. # The pod subnet CIDR. podSubnets: - 10.244.0.0/16 # The service subnet CIDR. serviceSubnets: - 10.96.0.0/12 # # The CNI used. # cni: # name: custom # Name of CNI to use. # # URLs containing manifests to apply for the CNI. # urls: # - https://docs.projectcalico.org/archive/v3.20/manifests/canal.yaml token: y9pn8n.g7wi0m50e0wu9b9v # The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster. aescbcEncryptionSecret: "" # The key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/). # # Decryption secret example (do not use in production!). # z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM= # The base64 encoded root certificate authority used by Kubernetes. ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRRm1GY0ZiaUtCQ1dtazB5YVZUM1Z5ekFLQmdncWhrak9QUVFEQkRBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXhNRGd5TmpFeU1EVTFNbG9YRFRNeE1EZ3lOREV5TURVMQpNbG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCQk1LTXBOZTkxanBmYWZxYlgzNDYyTEk1ZHBSRUpNdlY3U0t6YXZWWHRDa29oTFhpcHZMY3BiUkIyRm8KVGFzNURmSFc3Q2gwQng1S1RnOVRJelVrSGlpallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVrWG5oWUFkbXRDVG15aDVLOTNLTTA2Sk9aME13Q2dZSUtvWkl6ajBFQXdRRFNBQXdSUUlnQVZXYUkwQlUKUUt1MHRoRDNmSjBkaitqbEVxTW5nUGdQaXhJck1mdEdmU1FDSVFES3VIMWIvZzdRS1p1QmU3UEJocC9aZ2xrRgpRdTI5aHdJU0JjbE1aak9tK0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" # # The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation. # # AggregatorCA example. # aggregatorCA: # crt: TFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSklla05DTUhGLi4u # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # The base64 encoded private key for service account token generation. # # AggregatorCA example. # serviceAccount: # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # API server specific configuration options. # apiServer: # image: k8s.gcr.io/kube-apiserver:v1.22.1 # The container image used in the API server manifest. # # Extra arguments to supply to the API server. # extraArgs: # feature-gates: ServerSideApply=true # http2-max-streams-per-connection: "32" # # Extra certificate subject alternative names for the API server's certificate. # certSANs: # - 1.2.3.4 # - 4.5.6.7 # # Controller manager server specific configuration options. # controllerManager: # image: k8s.gcr.io/kube-controller-manager:v1.22.1 # The container image used in the controller manager manifest. # # Extra arguments to supply to the controller manager. # extraArgs: # feature-gates: ServerSideApply=true # # Kube-proxy server-specific configuration options # proxy: # image: k8s.gcr.io/kube-proxy:v1.22.1 # The container image used in the kube-proxy manifest. # mode: ipvs # proxy mode of kube-proxy. # # Extra arguments to supply to kube-proxy. # extraArgs: # proxy-mode: iptables # # Scheduler server specific configuration options. # scheduler: # image: k8s.gcr.io/kube-scheduler:v1.22.1 # The container image used in the scheduler manifest. # # Extra arguments to supply to the scheduler. # extraArgs: # feature-gates: AllBeta=true # # Etcd specific configuration options. # etcd: # image: gcr.io/etcd-development/etcd:v3.4.16 # The container image used to create the etcd service. # # The `ca` is the root certificate authority of the PKI. # ca: # crt: TFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVSklla05DTUhGLi4u # key: TFMwdExTMUNSVWRKVGlCRlJESTFOVEU1SUZCU1NWWkJWRVVnUzBWWkxTMHRMUzBLVFVNLi4u # # Extra arguments to supply to etcd. # extraArgs: # election-timeout: "5000" # # Core DNS specific configuration options. # coreDNS: # image: docker.io/coredns/coredns:1.8.4 # The `image` field is an override to the default coredns image. # # External cloud provider configuration. # externalCloudProvider: # enabled: true # Enable external cloud provider. # # A list of urls that point to additional manifests for an external cloud provider. # manifests: # - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml # - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml # # A list of urls that point to additional manifests. # extraManifests: # - https://www.example.com/manifest1.yaml # - https://www.example.com/manifest2.yaml # # A map of key value pairs that will be added while fetching the extraManifests. # extraManifestHeaders: # Token: "1234567" # X-ExtraInfo: info # # A list of inline Kubernetes manifests. # inlineManifests: # - name: namespace-ci # Name of the manifest. # contents: |- # Manifest contents as a string. # apiVersion: v1 # kind: Namespace # metadata: # name: ci # # Settings for admin kubeconfig generation. # adminKubeconfig: # certLifetime: 1h0m0s # Admin kubeconfig certificate lifetime (default is 1 year). ================================================ FILE: pkg/machinery/config/configpatcher/apply.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher import ( jsonpatch "github.com/evanphx/json-patch" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) // configOrBytes encapsulates either unmarshaled config or raw byte representation. type configOrBytes struct { marshaled []byte config config.Provider } func (cb *configOrBytes) Bytes() ([]byte, error) { if cb.marshaled != nil { return cb.marshaled, nil } var err error cb.marshaled, err = cb.config.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) if err != nil { return nil, err } cb.config = nil return cb.marshaled, nil } func (cb *configOrBytes) Config() (config.Provider, error) { if cb.config != nil { return cb.config, nil } var err error cb.config, err = configloader.NewFromBytes(cb.marshaled) if err != nil { return nil, err } cb.marshaled = nil return cb.config, nil } // Input to the patch application process. type Input interface { Config() (config.Provider, error) Bytes() ([]byte, error) } // WithConfig returns a new Input that wraps the given config. func WithConfig(config config.Provider) Input { return &configOrBytes{config: config} } // WithBytes returns a new Input that wraps the given bytes. func WithBytes(bytes []byte) Input { return &configOrBytes{marshaled: bytes} } // Output of patch application process. type Output = Input // Apply config patches to Talos machine config. // // Apply either JSON6902 or StrategicMergePatch. // // This method tries to minimize conversion between byte and unmarshalled // config representation as much as possible. func Apply(in Input, patches []Patch) (Output, error) { for _, patch := range patches { switch p := patch.(type) { case jsonpatch.Patch: bytes, err := in.Bytes() if err != nil { return nil, err } patched, err := JSON6902(bytes, p) if err != nil { return nil, err } in = WithBytes(patched) case StrategicMergePatch: cfg, err := in.Config() if err != nil { return nil, err } patched, err := StrategicMerge(cfg, p) if err != nil { return nil, err } in = WithConfig(patched) } } return in, nil } ================================================ FILE: pkg/machinery/config/configpatcher/apply_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package configpatcher_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" ) //go:embed testdata/apply/config.yaml var config []byte //go:embed testdata/apply/expected.yaml var expected string //go:embed testdata/multidoc/config.yaml var configMultidoc []byte //go:embed testdata/multidoc/expected.yaml var expectedMultidoc string //go:embed testdata/apply/expected_manifests.yaml var expectedManifests string func TestApply(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/apply/strategic1.yaml", "@testdata/apply/jsonpatch1.yaml", "@testdata/apply/jsonpatch2.yaml", "@testdata/apply/strategic2.yaml", "@testdata/apply/strategic3.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(config) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(config), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, expected, string(bytes)) }) } } func TestApplyMultiDocFail(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/multidoc/jsonpatch.yaml", "@testdata/multidoc/strategic1.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configMultidoc) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configMultidoc), }, } { t.Run(tt.name, func(t *testing.T) { _, err := configpatcher.Apply(tt.input, patches) assert.EqualError(t, err, "JSON6902 patches are not supported for multi-document machine configuration") }) } } func TestApplyMultiDoc(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/multidoc/strategic1.yaml", "@testdata/multidoc/strategic2.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configMultidoc) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configMultidoc), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, expectedMultidoc, string(bytes)) }) } } //go:embed testdata/auditpolicy/config.yaml var configAudit []byte //go:embed testdata/auditpolicy/expected.yaml var expectedAudit []byte func TestApplyAuditPolicy(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/auditpolicy/patch1.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configAudit) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configAudit), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, string(expectedAudit), string(bytes)) }) } } func TestApplyWithManifestNewline(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/apply/strategic4.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(config) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(config), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) // Verify that after all our transformations the YAML is still valid and newline is removed _, err = configloader.NewFromBytes(bytes) require.NoError(t, err) assert.Equal(t, expectedManifests, string(bytes)) }) } } //go:embed testdata/patchdelete/config.yaml var configMultidocDelete []byte //go:embed testdata/patchdelete/expected.yaml var expectedMultidocDelete string func TestApplyMultiDocDelete(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/patchdelete/strategic1.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configMultidocDelete) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configMultidocDelete), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, expectedMultidocDelete, string(bytes)) }) } } //go:embed testdata/patchdelete/controlplane_orig.yaml var controlPlane []byte //go:embed testdata/patchdelete/controlplane_expected.yaml var controlPlaneExpected string func TestApplyMultiDocCPDelete(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/patchdelete/strategic2.yaml", "@testdata/patchdelete/strategic3.yaml", "@testdata/patchdelete/strategic4.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(controlPlane) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(controlPlane), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, controlPlaneExpected, string(bytes)) }) } } //go:embed testdata/patchdeletemissing/config.yaml var configPatchDeleteMissing []byte func TestPatchDeleteMissing(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/patchdeletemissing/strategic1.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configPatchDeleteMissing) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configPatchDeleteMissing), }, } { t.Run(tt.name, func(t *testing.T) { _, err := configpatcher.Apply(tt.input, patches) require.Error(t, err) require.ErrorContains(t, err, `patch delete: path 'machine.network.hostname' in document '/v1alpha1': failed to delete path 'machine.network.hostname': lookup failed`) }) } } //go:embed testdata/patchlink/base.yaml var configPatchBase []byte //go:embed testdata/patchlink/expected.yaml var configPatchExpected string func TestPatchLink(t *testing.T) { patches, err := configpatcher.LoadPatches([]string{ "@testdata/patchlink/patch.yaml", }) require.NoError(t, err) cfg, err := configloader.NewFromBytes(configPatchBase) require.NoError(t, err) for _, tt := range []struct { name string input configpatcher.Input }{ { name: "WithConfig", input: configpatcher.WithConfig(cfg), }, { name: "WithBytes", input: configpatcher.WithBytes(configPatchBase), }, } { t.Run(tt.name, func(t *testing.T) { out, err := configpatcher.Apply(tt.input, patches) require.NoError(t, err) bytes, err := out.Bytes() require.NoError(t, err) assert.Equal(t, configPatchExpected, string(bytes)) }) } } ================================================ FILE: pkg/machinery/config/configpatcher/configpatcher.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package configpatcher provides methods to patch Talos config. package configpatcher // Patch is either JSON patch or strategic merge patch. type Patch any ================================================ FILE: pkg/machinery/config/configpatcher/configpatcher_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher_test import ( "bytes" "testing" jsonpatch "github.com/evanphx/json-patch" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" ) const dummyConfig = `machine: kubelet: {} ` const cloudProviderPatched = `machine: kubelet: extraArgs: cloud-provider: external ` func TestJSON6902(t *testing.T) { type args struct { talosMachineConfig []byte patchAsBytes []byte } tests := []struct { name string args args want []byte wantErr bool }{ { name: "test add patch", args: args{ talosMachineConfig: []byte(dummyConfig), patchAsBytes: []byte(`[{"op": "add", "path": "/machine/kubelet/extraArgs", "value": {"cloud-provider": "external"}}]`), }, want: []byte(cloudProviderPatched), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { patch, err := jsonpatch.DecodePatch(tt.args.patchAsBytes) if err != nil { t.Errorf("JSON6902 error decoding patch: %v", err) return } got, err := configpatcher.JSON6902(tt.args.talosMachineConfig, patch) if (err != nil) != tt.wantErr { t.Errorf("JSON6902 error: %v, but wanted: %v", err, tt.wantErr) return } if !bytes.Equal(got, tt.want) { t.Errorf("JSON6902 got: \n%v\n but wanted: \n%v", string(got), string(tt.want)) } }) } } ================================================ FILE: pkg/machinery/config/configpatcher/json6902.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher import ( "bytes" "errors" "fmt" "io" jsonpatch "github.com/evanphx/json-patch" ghodssyaml "github.com/ghodss/yaml" "go.yaml.in/yaml/v4" ) // JSON6902 is responsible for applying a JSON 6902 patch to the bootstrap data. func JSON6902(talosMachineConfig []byte, patch jsonpatch.Patch) ([]byte, error) { // check number of input documents numDocuments, err := countYAMLDocuments(talosMachineConfig) if err != nil { return nil, err } if numDocuments != 1 { return nil, errors.New("JSON6902 patches are not supported for multi-document machine configuration") } // apply JSON patch jsonDecodedData, err := ghodssyaml.YAMLToJSON(talosMachineConfig) if err != nil { return nil, fmt.Errorf("failure converting talos machine config to json: %s", err) } jsonDecodedData, err = patch.Apply(jsonDecodedData) if err != nil { return nil, fmt.Errorf("failure applying rfc6902 patches to talos machine config: %s", err) } talosMachineConfig, err = ghodssyaml.JSONToYAML(jsonDecodedData) if err != nil { return nil, fmt.Errorf("failure converting talos machine config from json to yaml: %s", err) } return talosMachineConfig, nil } func countYAMLDocuments(talosMachineConfig []byte) (int, error) { decoder := yaml.NewDecoder(bytes.NewReader(talosMachineConfig)) numDocuments := 0 for { var docs yaml.Node err := decoder.Decode(&docs) if err == io.EOF { break } if err != nil { return 0, fmt.Errorf("failure decoding talos machine config: %s", err) } if docs.Kind != yaml.DocumentNode { return 0, errors.New("talos machine config is not a yaml document") } numDocuments++ } return numDocuments, nil } ================================================ FILE: pkg/machinery/config/configpatcher/load.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher import ( "bytes" "encoding/json" "os" "strings" jsonpatch "github.com/evanphx/json-patch" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/configloader" ) type patch []map[string]any // LoadPatch loads the strategic merge patch or JSON patch (JSON/YAML for JSON patch). func LoadPatch(in []byte) (Patch, error) { // Try configloader first, as it is more strict about the config format cfg, strategicErr := configloader.NewFromBytes(in, configloader.WithAllowPatchDelete()) if strategicErr == nil { return NewStrategicMergePatch(cfg), nil } var ( jsonErr error p jsonpatch.Patch ) // try JSON first if p, jsonErr = jsonpatch.DecodePatch(in); jsonErr == nil { return p, nil } // try YAML var yamlPatch patch if err := yaml.Unmarshal(in, &yamlPatch); err != nil { // not YAML either, return previous error // see if input looks like JSON Patch as JSON if bytes.HasPrefix(bytes.TrimSpace(in), []byte("[")) { return nil, jsonErr } // nope, return config loading error (assume it was strategic merge patch) return nil, strategicErr } p = make(jsonpatch.Patch, 0, len(yamlPatch)) for _, yp := range yamlPatch { op := make(jsonpatch.Operation, len(yp)) for key, value := range yp { m, err := json.Marshal(value) if err != nil { return p, err } op[key] = (*json.RawMessage)(&m) } p = append(p, op) } return p, nil } // LoadPatches loads the JSON patch either from value literal or from a file if the patch starts with '@'. // // It also tries to guess if the filename was given without '@' prefix. // //nolint:gocyclo func LoadPatches(in []string) ([]Patch, error) { var result []Patch for _, patchString := range in { var ( p Patch contents []byte err error ) switch { case strings.HasPrefix(patchString, "@"): filename := patchString[1:] contents, err = os.ReadFile(filename) if err != nil { return result, err } case !strings.ContainsAny(patchString, "\n ") && !strings.HasPrefix(patchString, "[") && !strings.HasPrefix(patchString, "{"): // any valid patch supplied inline should contain either '\n' or space, or start with '[' or '{' // so if none of this is true, assume it's a filename, but without '@' prefix contents, err = os.ReadFile(patchString) if err != nil { return result, err } default: contents = []byte(patchString) } p, err = LoadPatch(contents) if err != nil { return result, err } // merge JSON patches if they come one after another _, isJSONPatch := p.(jsonpatch.Patch) lastJSONPatch := false if len(result) > 0 { if _, ok := result[len(result)-1].(jsonpatch.Patch); ok { lastJSONPatch = true } } if isJSONPatch && lastJSONPatch { result[len(result)-1] = append(result[len(result)-1].(jsonpatch.Patch), p.(jsonpatch.Patch)...) } else { result = append(result, p) } } return result, nil } ================================================ FILE: pkg/machinery/config/configpatcher/load_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher_test import ( _ "embed" "testing" jsonpatch "github.com/evanphx/json-patch" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" ) //go:embed testdata/patch.json var jsonPatch []byte //go:embed testdata/patch.yaml var yamlPatch []byte //go:embed testdata/strategic.yaml var strategicPatch []byte func TestLoadJSON(t *testing.T) { raw, err := configpatcher.LoadPatch(jsonPatch) require.NoError(t, err) p, ok := raw.(jsonpatch.Patch) require.True(t, ok) assert.Len(t, p, 1) assert.Equal(t, p[0].Kind(), "add") var path string path, err = p[0].Path() require.NoError(t, err) assert.Equal(t, path, "/machine/certSANs") } func TestLoadYAML(t *testing.T) { raw, err := configpatcher.LoadPatch(yamlPatch) require.NoError(t, err) p, ok := raw.(jsonpatch.Patch) require.True(t, ok) assert.Len(t, p, 1) assert.Equal(t, p[0].Kind(), "add") var path string path, err = p[0].Path() require.NoError(t, err) assert.Equal(t, path, "/some/path") var v any v, err = p[0].ValueInterface() require.NoError(t, err) assert.Equal(t, v, []any{"a", "b", "c"}) } func TestLoadStrategic(t *testing.T) { raw, err := configpatcher.LoadPatch(strategicPatch) require.NoError(t, err) p, ok := raw.(configpatcher.StrategicMergePatch) require.True(t, ok) assert.Equal(t, "foo.bar", p.Provider().NetworkHostnameConfig().Hostname()) } func TestLoadJSONPatches(t *testing.T) { patchList, err := configpatcher.LoadPatches([]string{ "@testdata/patch.json", "@testdata/patch.yaml", `[{"op":"replace","path":"/some","value": []}]`, }) require.NoError(t, err) require.Len(t, patchList, 1) raw := patchList[0] p, ok := raw.(jsonpatch.Patch) require.True(t, ok) assert.Len(t, p, 3) assert.Equal(t, p[0].Kind(), "add") assert.Equal(t, p[1].Kind(), "add") assert.Equal(t, p[2].Kind(), "replace") } func TestLoadMixedPatches(t *testing.T) { patchList, err := configpatcher.LoadPatches([]string{ "@testdata/patch.json", "@testdata/strategic.yaml", "@testdata/patch.yaml", `[{"op":"replace","path":"/some","value": []}]`, }) require.NoError(t, err) require.Len(t, patchList, 3) assert.IsType(t, jsonpatch.Patch{}, patchList[0]) assert.Implements(t, (*configpatcher.StrategicMergePatch)(nil), patchList[1]) assert.IsType(t, jsonpatch.Patch{}, patchList[2]) } func TestLoadStraightFilename(t *testing.T) { patchList, err := configpatcher.LoadPatches([]string{ "testdata/strategic.yaml", `[{"op":"replace","path":"/some","value": []}]`, }) require.NoError(t, err) require.Len(t, patchList, 2) assert.Implements(t, (*configpatcher.StrategicMergePatch)(nil), patchList[0]) assert.IsType(t, jsonpatch.Patch{}, patchList[1]) } ================================================ FILE: pkg/machinery/config/configpatcher/strategic.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package configpatcher import ( "errors" "slices" "github.com/siderolabs/gen/xslices" coreconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/merge" ) // StrategicMergePatch is a strategic merge config patch. type StrategicMergePatch interface { Documents() []config.Document Provider() coreconfig.Provider } // StrategicMerge performs strategic merge config patching. // // Strategic merge on two sets of documents - on the left hand side and on the right hand side. // Documents with matching tuples (apiVersion, kind, name) are merged together. // If the document on the right doesn't exist on the left, it is appended. func StrategicMerge(cfg coreconfig.Provider, patch StrategicMergePatch) (coreconfig.Provider, error) { left := cfg.Clone().Documents() right := patch.Documents() documentID := func(doc config.Document) string { id := doc.APIVersion() + "/" + doc.Kind() if named, ok := doc.(config.NamedDocument); ok { id += "/" + named.Name() } return id } leftIndex := xslices.ToMap(left, func(d config.Document) (string, config.Document) { return documentID(d), d }) for _, rightDoc := range right { id := documentID(rightDoc) if leftDoc, ok := leftIndex[id]; ok { sel, isSel := rightDoc.(configloader.Selector) if !isSel { if err := merge.Merge(leftDoc, rightDoc); err != nil { return nil, err } continue } err := sel.ApplyTo(leftDoc) if err != nil { if !errors.Is(err, configloader.ErrZeroedDocument) { return nil, err } delete(leftIndex, id) idx := slices.Index(left, leftDoc) left = slices.Delete(left, idx, idx+1) } } else { left = append(left, rightDoc) } } return container.New(left...) } // NewStrategicMergePatch creates a new strategic merge patch. deleteSelectors is a list of delete selectors, can be empty. func NewStrategicMergePatch(cfg coreconfig.Provider) StrategicMergePatch { return strategicMergePatch{provider: cfg} } type strategicMergePatch struct { provider coreconfig.Provider } func (s strategicMergePatch) Documents() []config.Document { return s.provider.Documents() } func (s strategicMergePatch) Provider() coreconfig.Provider { return s.provider } var _ StrategicMergePatch = strategicMergePatch{} ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/config.yaml ================================================ version: v1alpha1 machine: network: hostname: hostname1 interfaces: - interface: eth0 dhcp: true cluster: proxy: extraArgs: metrics-bind-address: $(POD_IP):10249 foo: bar ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/expected.yaml ================================================ version: v1alpha1 machine: type: "" token: "" certSANs: [] network: hostname: foo interfaces: - interface: eth0 addresses: - 10.1.2.3/24 dhcp: false vip: ip: 10.3.5.6 - interface: eth1 vlans: - addresses: - 10.3.4.5 - 10.3.4.6 routes: [] vlanId: 100 - addresses: - 10.3.4.7 routes: [] vlanId: 101 cluster: controlPlane: null proxy: extraArgs: metrics-bind-address: $(POD_IP):10249 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/expected_manifests.yaml ================================================ version: v1alpha1 machine: type: "" token: "" certSANs: [] network: hostname: hostname1 interfaces: - interface: eth0 dhcp: true cluster: controlPlane: null proxy: extraArgs: foo: bar metrics-bind-address: $(POD_IP):10249 inlineManifests: - name: cilium contents: | --- apiVersion: v1 kind: ServiceAccount metadata: name: cilium namespace: kube-system --- apiVersion: v1 kind: ServiceAccount metadata: name: cilium-operator namespace: kube-system ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/jsonpatch1.yaml ================================================ - op: add path: /machine/network/interfaces/0/addresses value: - 10.1.2.3/24 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/jsonpatch2.yaml ================================================ - op: replace path: /machine/network/hostname value: foo ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/strategic1.yaml ================================================ machine: network: interfaces: - interface: eth0 dhcp: false ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/strategic2.yaml ================================================ machine: network: interfaces: - interface: eth0 vip: ip: 10.3.5.6 - interface: eth1 vlans: - vlanId: 100 addresses: [10.3.4.5] ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/strategic3.yaml ================================================ machine: network: interfaces: - interface: eth1 vlans: - vlanId: 100 addresses: [10.3.4.6] - vlanId: 101 addresses: [10.3.4.7] cluster: proxy: extraArgs: metrics-bind-address: $(POD_IP):10249 foo: $patch: delete ================================================ FILE: pkg/machinery/config/configpatcher/testdata/apply/strategic4.yaml ================================================ cluster: inlineManifests: - name: cilium contents: | # the empty newline below is important --- apiVersion: v1 kind: ServiceAccount metadata: name: cilium namespace: kube-system --- apiVersion: v1 kind: ServiceAccount metadata: name: cilium-operator namespace: kube-system ================================================ FILE: pkg/machinery/config/configpatcher/testdata/auditpolicy/config.yaml ================================================ version: v1alpha1 machine: network: hostname: hostname-foo cluster: apiServer: auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controlPlane: endpoint: https://localhost:6443 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/auditpolicy/expected.yaml ================================================ version: v1alpha1 machine: type: "" token: "" certSANs: [] network: hostname: hostname-foo cluster: controlPlane: endpoint: https://localhost:6443 apiServer: auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None ================================================ FILE: pkg/machinery/config/configpatcher/testdata/auditpolicy/patch1.yaml ================================================ cluster: apiServer: auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None ================================================ FILE: pkg/machinery/config/configpatcher/testdata/multidoc/config.yaml ================================================ version: v1alpha1 machine: network: hostname: hostname1 interfaces: - interface: eth0 dhcp: true --- apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/join?jointoken=secret&user=alice --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello mountPath: /etc/foo environment: - FOO=BAR ================================================ FILE: pkg/machinery/config/configpatcher/testdata/multidoc/expected.yaml ================================================ version: v1alpha1 machine: type: "" token: "" certSANs: [] network: hostname: hostname1 interfaces: - interface: eth0 dhcp: false cluster: null --- apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/join?jointoken=secret&user=bob --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello world mountPath: /etc/foo - content: another hello mountPath: /etc/bar environment: - FOO=BAR - XXX=YYY ================================================ FILE: pkg/machinery/config/configpatcher/testdata/multidoc/jsonpatch.yaml ================================================ - op: add path: /machine/network/interfaces/0/addresses value: - 10.1.2.3/24 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/multidoc/strategic1.yaml ================================================ machine: network: interfaces: - interface: eth0 dhcp: false ================================================ FILE: pkg/machinery/config/configpatcher/testdata/multidoc/strategic2.yaml ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/join?jointoken=secret&user=bob --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello world mountPath: /etc/foo - content: another hello mountPath: /etc/bar environment: - XXX=YYY ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patch.json ================================================ [ { "op": "add", "path": "/machine/certSANs", "value": ["foo.com"] } ] ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patch.yaml ================================================ - op: add path: /some/path value: - a - b - c ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/config.yaml ================================================ version: v1alpha1 machine: network: hostname: hostname1 interfaces: - interface: eth0 dhcp: true - interface: eth1 addresses: [10.3.5.4/32] --- apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/join?jointoken=secret&user=alice --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello mountPath: /etc/foo environment: - FOO=BAR ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/controlplane_expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/controlplane_orig.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/expected.yaml ================================================ version: v1alpha1 machine: type: "" token: "" certSANs: [] network: interfaces: - interface: eth1 addresses: - 10.3.5.4/32 - 10.3.5.5/32 - interface: eth0 dummy: true cluster: null --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello2 mountPath: /etc/foo2 environment: - FOO=BAR ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/strategic1.yaml ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig $patch: delete --- apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello $patch: delete - content: hello2 mountPath: /etc/foo2 --- version: v1alpha1 machine: network: hostname: $patch: delete interfaces: - interface: eth0 $patch: delete - interface: eth1 addresses: [10.3.5.5/32] - interface: eth0 dummy: true ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/strategic2.yaml ================================================ version: v1alpha1 cluster: apiServer: admissionControl: - name: PodSecurity $patch: delete machine: nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: $patch: delete ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/strategic3.yaml ================================================ version: v1alpha1 cluster: apiServer: admissionControl: - name: PodSecurity2 configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: $patch: delete ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdelete/strategic4.yaml ================================================ version: v1alpha1 cluster: apiServer: admissionControl: - name: PodSecurity2 $patch: delete ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdeletemissing/config.yaml ================================================ version: v1alpha1 machine: {} ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchdeletemissing/strategic1.yaml ================================================ machine: network: hostname: $patch: delete ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchlink/base.yaml ================================================ apiVersion: v1alpha1 kind: LinkConfig name: eth0 addresses: - address: 10.0.42.136/24 routes: - gateway: 10.0.42.1 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchlink/expected.yaml ================================================ apiVersion: v1alpha1 kind: LinkConfig name: eth0 addresses: - address: 10.0.42.137/24 routes: - gateway: 10.0.42.1 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/patchlink/patch.yaml ================================================ apiVersion: v1alpha1 kind: LinkConfig name: eth0 addresses: - address: 10.0.42.136/24 $patch: delete - address: 10.0.42.137/24 ================================================ FILE: pkg/machinery/config/configpatcher/testdata/strategic.yaml ================================================ machine: network: hostname: foo.bar ================================================ FILE: pkg/machinery/config/container/container.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package container implements a wrapper which wraps all configuration documents into a single container. package container import ( "bytes" "context" "errors" "fmt" "slices" "strings" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/xslices" coreconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // V1Alpha1ConflictValidator is the interface implemented by config documents which conflict with legacy v1alpha1 config. type V1Alpha1ConflictValidator interface { V1Alpha1ConflictValidate(*v1alpha1.Config) error } // Container wraps all configuration documents into a single container. type Container struct { v1alpha1Config *v1alpha1.Config documents []config.Document bytes []byte readonly bool } var _ coreconfig.Provider = &Container{} // New creates a container out of the list of documents. // //nolint:gocyclo func New(documents ...config.Document) (*Container, error) { container := &Container{ documents: make([]config.Document, 0, len(documents)), } seenDocuments := make(map[string]struct{}) conflictingDocuments := make(map[string]string) for _, doc := range documents { switch d := doc.(type) { case *v1alpha1.Config: if container.v1alpha1Config != nil { return nil, errors.New("duplicate v1alpha1.Config") } container.v1alpha1Config = d default: if _, ok := d.(selector); !ok { documentID := d.Kind() + "/" if named, ok := d.(config.NamedDocument); ok { documentID += named.Name() } if _, alreadySeen := seenDocuments[documentID]; alreadySeen { return nil, fmt.Errorf("duplicate document: %s", documentID) } seenDocuments[documentID] = struct{}{} if conflictingID, isConflicting := conflictingDocuments[documentID]; isConflicting { return nil, fmt.Errorf("conflicting documents: %s and %s", conflictingID, documentID) } if conflicting, ok := d.(config.ConflictingDocument); ok { for _, kind := range conflicting.ConflictsWithKinds() { conflictingID := kind + "/" if named, ok := d.(config.NamedDocument); ok { conflictingID += named.Name() } if _, alreadySeen := seenDocuments[conflictingID]; alreadySeen { return nil, fmt.Errorf("conflicting documents: %s and %s", conflictingID, documentID) } conflictingDocuments[conflictingID] = documentID } } } container.documents = append(container.documents, d) } } return container, nil } // NewReadonly creates a read-only container which preserves byte representation of contents. func NewReadonly(bytes []byte, documents ...config.Document) (*Container, error) { c, err := New(documents...) if err != nil { return nil, err } c.bytes = bytes c.readonly = true return c, nil } // NewV1Alpha1 creates a container with (only) v1alpha1.Config document. func NewV1Alpha1(config *v1alpha1.Config) *Container { return &Container{ v1alpha1Config: config, } } // Clone the container. // // Cloned container is not readonly. func (container *Container) Clone() coreconfig.Provider { return container.clone() } func (container *Container) clone() *Container { return &Container{ v1alpha1Config: container.v1alpha1Config.DeepCopy(), documents: xslices.Map(container.documents, config.Document.Clone), } } // PatchV1Alpha1 patches the container's v1alpha1.Config while preserving other config documents. func (container *Container) PatchV1Alpha1(patcher func(*v1alpha1.Config) error) (coreconfig.Provider, error) { cfg := container.RawV1Alpha1() if cfg == nil { return nil, fmt.Errorf("v1alpha1.Config is not present in the container") } cfg = cfg.DeepCopy() if err := patcher(cfg); err != nil { return nil, err } otherDocs := xslices.Filter(container.Documents(), func(doc config.Document) bool { _, ok := doc.(*v1alpha1.Config) return !ok }) return New(slices.Insert(otherDocs, 0, config.Document(cfg))...) } // Readonly implements config.Container interface. func (container *Container) Readonly() bool { return container.readonly } // Debug implements config.Config interface. func (container *Container) Debug() bool { if container.v1alpha1Config == nil { return false } return container.v1alpha1Config.Debug() } // Machine implements config.Config interface. func (container *Container) Machine() config.MachineConfig { if container.v1alpha1Config == nil { return nil } return container.v1alpha1Config.Machine() } // Cluster implements config.Config interface. func (container *Container) Cluster() config.ClusterConfig { if container.v1alpha1Config == nil { return nil } return container.v1alpha1Config.Cluster() } func findMatchingDocs[T any](documents []config.Document) []T { var result []T for _, doc := range documents { if c, ok := doc.(T); ok { result = append(result, c) } } return result } // SideroLink implements config.Config interface. func (container *Container) SideroLink() config.SideroLinkConfig { matching := findMatchingDocs[config.SideroLinkConfig](container.documents) if len(matching) == 0 { return nil } return matching[0] } // ExtensionServiceConfigs implements config.Config interface. func (container *Container) ExtensionServiceConfigs() []config.ExtensionServiceConfig { return findMatchingDocs[config.ExtensionServiceConfig](container.documents) } // Runtime implements config.Config interface. func (container *Container) Runtime() config.RuntimeConfig { return config.WrapRuntimeConfigList(findMatchingDocs[config.RuntimeConfig](container.documents)...) } // Environment implements config.Config interface. func (container *Container) Environment() config.EnvironmentConfig { return config.WrapEnvironmentConfigList(findMatchingDocs[config.EnvironmentConfig](container.documents)...) } // NetworkRules implements config.Config interface. func (container *Container) NetworkRules() config.NetworkRuleConfig { return config.WrapNetworkRuleConfigList(findMatchingDocs[config.NetworkRuleConfigSignal](container.documents)...) } // TrustedRoots implements config.Config interface. func (container *Container) TrustedRoots() config.TrustedRootsConfig { return config.WrapTrustedRootsConfig(findMatchingDocs[config.TrustedRootsConfig](container.documents)...) } // Volumes implements config.Config interface. func (container *Container) Volumes() config.VolumesConfig { return config.WrapVolumesConfigList(findMatchingDocs[config.VolumeConfig](container.documents)...) } // KubespanConfig implements config.Config interface. func (container *Container) KubespanConfig() config.KubespanConfig { return config.WrapKubespanConfig(findMatchingDocs[config.KubespanConfig](container.documents)...) } // PCIDriverRebindConfig implements config.Config interface. func (container *Container) PCIDriverRebindConfig() config.PCIDriverRebindConfig { return config.WrapPCIDriverRebindConfig(findMatchingDocs[config.PCIDriverRebindConfig](container.documents)...) } // EthernetConfigs implements config.Config interface. func (container *Container) EthernetConfigs() []config.EthernetConfig { return findMatchingDocs[config.EthernetConfig](container.documents) } // UserVolumeConfigs implements config.Config interface. func (container *Container) UserVolumeConfigs() []config.UserVolumeConfig { return findMatchingDocs[config.UserVolumeConfig](container.documents) } // ExternalVolumeConfigs implements config.Config interface. func (container *Container) ExternalVolumeConfigs() []config.ExternalVolumeConfig { return findMatchingDocs[config.ExternalVolumeConfig](container.documents) } // RawVolumeConfigs implements config.Config interface. func (container *Container) RawVolumeConfigs() []config.RawVolumeConfig { return findMatchingDocs[config.RawVolumeConfig](container.documents) } // ExistingVolumeConfigs implements config.Config interface. func (container *Container) ExistingVolumeConfigs() []config.ExistingVolumeConfig { return findMatchingDocs[config.ExistingVolumeConfig](container.documents) } // SwapVolumeConfigs implements config.Config interface. func (container *Container) SwapVolumeConfigs() []config.SwapVolumeConfig { return findMatchingDocs[config.SwapVolumeConfig](container.documents) } // ZswapConfig implements config.Config interface. func (container *Container) ZswapConfig() config.ZswapConfig { matching := findMatchingDocs[config.ZswapConfig](container.documents) if len(matching) == 0 { return nil } return matching[0] } // NetworkStaticHostConfig implements config.Config interface. func (container *Container) NetworkStaticHostConfig() []config.NetworkStaticHostConfig { return slices.Concat( container.v1alpha1Config.NetworkStaticHostConfig(), findMatchingDocs[config.NetworkStaticHostConfig](container.documents), ) } // NetworkHostnameConfig implements config.Config interface. func (container *Container) NetworkHostnameConfig() config.NetworkHostnameConfig { // first check if we have a dedicated document matching := findMatchingDocs[config.NetworkHostnameConfig](container.documents) if len(matching) > 0 { return matching[0] } // fallback to v1alpha1 if container.v1alpha1Config != nil { return container.v1alpha1Config } return nil } // NetworkResolverConfig implements config.Config interface. func (container *Container) NetworkResolverConfig() config.NetworkResolverConfig { // first check if we have a dedicated document matching := findMatchingDocs[config.NetworkResolverConfig](container.documents) if len(matching) > 0 { return matching[0] } // fallback to v1alpha1 if container.v1alpha1Config != nil { return container.v1alpha1Config } return nil } // NetworkTimeSyncConfig implements config.Config interface. func (container *Container) NetworkTimeSyncConfig() config.NetworkTimeSyncConfig { // first check if we have a dedicated document matching := findMatchingDocs[config.NetworkTimeSyncConfig](container.documents) if len(matching) > 0 { return matching[0] } // fallback to v1alpha1 if container.v1alpha1Config != nil { return container.v1alpha1Config.NetworkTimeSyncConfig() } return nil } // NetworkKubeSpanConfig implements config.Config interface. func (container *Container) NetworkKubeSpanConfig() config.NetworkKubeSpanConfig { // first check if we have a dedicated document matching := findMatchingDocs[config.NetworkKubeSpanConfig](container.documents) if len(matching) > 0 { return matching[0] } // fallback to v1alpha1 if container.v1alpha1Config != nil { return container.v1alpha1Config.NetworkKubeSpanConfig() } return nil } // NetworkCommonLinkConfigs implements config.Config interface. func (container *Container) NetworkCommonLinkConfigs() []config.NetworkCommonLinkConfig { return findMatchingDocs[config.NetworkCommonLinkConfig](container.documents) } // NetworkLinkAliasConfigs implements config.Config interface. func (container *Container) NetworkLinkAliasConfigs() []config.NetworkLinkAliasConfig { return findMatchingDocs[config.NetworkLinkAliasConfig](container.documents) } // NetworkDHCPConfigs implements config.Config interface. func (container *Container) NetworkDHCPConfigs() []config.NetworkDHCPConfig { return findMatchingDocs[config.NetworkDHCPConfig](container.documents) } // NetworkDHCPv4Configs implements config.Config interface. func (container *Container) NetworkDHCPv4Configs() []config.NetworkDHCPv4Config { return findMatchingDocs[config.NetworkDHCPv4Config](container.documents) } // NetworkDHCPv6Configs implements config.Config interface. func (container *Container) NetworkDHCPv6Configs() []config.NetworkDHCPv6Config { return findMatchingDocs[config.NetworkDHCPv6Config](container.documents) } // NetworkVirtualIPConfigs implements config.Config interface. func (container *Container) NetworkVirtualIPConfigs() []config.NetworkVirtualIPConfig { return findMatchingDocs[config.NetworkVirtualIPConfig](container.documents) } // NetworkProbeConfigs implements config.Config interface. func (container *Container) NetworkProbeConfigs() []config.NetworkCommonProbeConfig { return findMatchingDocs[config.NetworkCommonProbeConfig](container.documents) } // NetworkBlackholeRouteConfigs implements config.Config interface. func (container *Container) NetworkBlackholeRouteConfigs() []config.NetworkBlackholeRouteConfig { return findMatchingDocs[config.NetworkBlackholeRouteConfig](container.documents) } // NetworkRoutingRuleConfigs implements config.Config interface. func (container *Container) NetworkRoutingRuleConfigs() []config.NetworkRoutingRuleConfig { return findMatchingDocs[config.NetworkRoutingRuleConfig](container.documents) } // RunDefaultDHCPOperators implements config.Config interface. // // The rules for this are: // - if there is a single new-style network config document for links, // we immediately stop running default DHCP operators (as user is taking full control) func (container *Container) RunDefaultDHCPOperators() bool { return len(findMatchingDocs[config.NetworkCommonLinkConfig](container.documents)) == 0 && len(findMatchingDocs[config.NetworkDHCPConfig](container.documents)) == 0 } // OOMConfig implements config.Config interface. func (container *Container) OOMConfig() config.OOMConfig { matching := findMatchingDocs[config.OOMConfig](container.documents) if len(matching) == 0 { return nil } return matching[0] } // RegistryMirrorConfigs implements config.Config interface. func (container *Container) RegistryMirrorConfigs() map[string]config.RegistryMirrorConfig { var cfg map[string]config.RegistryMirrorConfig if container.v1alpha1Config != nil { cfg = container.v1alpha1Config.RegistryMirrorConfigs() } docs := findMatchingDocs[config.RegistryMirrorConfigDocument](container.documents) if cfg == nil { cfg = make(map[string]config.RegistryMirrorConfig, len(docs)) } for _, doc := range docs { cfg[doc.Name()] = doc } return cfg } // RegistryAuthConfigs implements config.Config interface. func (container *Container) RegistryAuthConfigs() map[string]config.RegistryAuthConfig { var cfg map[string]config.RegistryAuthConfig if container.v1alpha1Config != nil { cfg = container.v1alpha1Config.RegistryAuthConfigs() } docs := findMatchingDocs[config.RegistryAuthConfigDocument](container.documents) if cfg == nil { cfg = make(map[string]config.RegistryAuthConfig, len(docs)) } for _, doc := range docs { cfg[doc.Name()] = doc } return cfg } // RegistryTLSConfigs implements config.Config interface. func (container *Container) RegistryTLSConfigs() map[string]config.RegistryTLSConfig { var cfg map[string]config.RegistryTLSConfig if container.v1alpha1Config != nil { cfg = container.v1alpha1Config.RegistryTLSConfigs() } docs := findMatchingDocs[config.RegistryTLSConfigDocument](container.documents) if cfg == nil { cfg = make(map[string]config.RegistryTLSConfig, len(docs)) } for _, doc := range docs { cfg[doc.Name()] = doc } return cfg } // ImageVerificationConfig implements config.Config interface. func (container *Container) ImageVerificationConfig() config.ImageVerificationConfig { docs := findMatchingDocs[config.ImageVerificationConfig](container.documents) if len(docs) == 0 { return nil } return docs[0] } // Bytes returns source YAML representation (if available) or does default encoding. func (container *Container) Bytes() ([]byte, error) { if !container.readonly { return container.EncodeBytes() } if container.bytes == nil { panic("container.Bytes() called on a readonly container without bytes") } return container.bytes, nil } // EncodeString configuration to YAML using the provided options. func (container *Container) EncodeString(encoderOptions ...encoder.Option) (string, error) { var buf strings.Builder err := container.encodeToBuf(&buf, encoderOptions...) if err != nil { return "", err } return buf.String(), nil } // EncodeBytes configuration to YAML using the provided options. func (container *Container) EncodeBytes(encoderOptions ...encoder.Option) ([]byte, error) { var buf bytes.Buffer err := container.encodeToBuf(&buf, encoderOptions...) if err != nil { return nil, err } return buf.Bytes(), nil } type buffer interface { Len() int Write(p []byte) (int, error) WriteString(s string) (int, error) } func (container *Container) encodeToBuf(buf buffer, encoderOptions ...encoder.Option) error { if container.v1alpha1Config != nil { b, err := encoder.NewEncoder(container.v1alpha1Config, encoderOptions...).Encode() if err != nil { return err } buf.Write(b) //nolint:errcheck } for _, doc := range container.documents { if buf.Len() > 0 { buf.WriteString("---\n") //nolint:errcheck } b, err := encoder.NewEncoder(doc, encoderOptions...).Encode() if err != nil { return err } buf.Write(b) //nolint:errcheck } return nil } func docID(doc config.Document) string { id := doc.Kind() if named, ok := doc.(config.NamedDocument); ok { id += "/" + named.Name() } return id } // Validate checks configuration and returns warnings and fatal errors (as multierror). // //nolint:gocyclo func (container *Container) Validate(mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) { var ( warnings []string err error ) if container.v1alpha1Config != nil { warnings, err = container.v1alpha1Config.Validate(mode, opt...) if err != nil { err = fmt.Errorf("v1alpha1.Config: %w", err) } } var multiErr *multierror.Error if err != nil { multiErr = multierror.Append(multiErr, err) } for _, doc := range container.documents { if validatableDoc, ok := doc.(config.Validator); ok { docWarnings, docErr := validatableDoc.Validate(mode, opt...) if docErr != nil { docErr = fmt.Errorf("%s: %w", docID(doc), docErr) } warnings = append(warnings, docWarnings...) multiErr = multierror.Append(multiErr, docErr) } } // now cross-validate the config if container.v1alpha1Config != nil { for _, doc := range container.documents { if conflictValidator, ok := doc.(V1Alpha1ConflictValidator); ok { err := conflictValidator.V1Alpha1ConflictValidate(container.v1alpha1Config) if err != nil { multiErr = multierror.Append(multiErr, err) } } } } return warnings, multiErr.ErrorOrNil() } // RuntimeValidate validates the config in the runtime context. func (container *Container) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) { var ( warnings []string err error ) if container.v1alpha1Config != nil { warnings, err = container.v1alpha1Config.RuntimeValidate(ctx, st, mode, opt...) if err != nil { err = fmt.Errorf("v1alpha1.Config: %w", err) } } var multiErr *multierror.Error if err != nil { multiErr = multierror.Append(multiErr, err) } for _, doc := range container.documents { if validatableDoc, ok := doc.(config.RuntimeValidator); ok { docWarnings, docErr := validatableDoc.RuntimeValidate(ctx, st, mode, opt...) if docErr != nil { docErr = fmt.Errorf("%s: %w", docID(doc), docErr) } warnings = append(warnings, docWarnings...) multiErr = multierror.Append(multiErr, docErr) } } return warnings, multiErr.ErrorOrNil() } // RedactSecrets returns a copy of the Provider with all secrets replaced with the given string. func (container *Container) RedactSecrets(replacement string) coreconfig.Provider { clone := container.clone() if clone.v1alpha1Config != nil { clone.v1alpha1Config.Redact(replacement) } for _, doc := range clone.documents { if secretDoc, ok := doc.(config.SecretDocument); ok { secretDoc.Redact(replacement) } } return clone } // RawV1Alpha1 returns internal config representation for v1alpha1.Config. func (container *Container) RawV1Alpha1() *v1alpha1.Config { if container.readonly { return container.v1alpha1Config.DeepCopy() } return container.v1alpha1Config } // Documents returns all documents in the container. // // Documents should not be modified. func (container *Container) Documents() []config.Document { result := make([]config.Document, 0, len(container.documents)+1) // first we take deletes for v1alpha1 for _, doc := range container.documents { if _, ok := doc.(selector); ok && doc.Kind() == v1alpha1.Version { result = append(result, doc) } } // then we take the v1alpha1 config if container.v1alpha1Config != nil { result = append(result, container.v1alpha1Config) } // then we take the rest for _, doc := range container.documents { if _, ok := doc.(selector); ok && doc.Kind() == v1alpha1.Version { continue } result = append(result, doc) } return result } type selector interface{ ApplyTo(config.Document) error } // CompleteForBoot return true if the machine config is enough to proceed with the boot process. func (container *Container) CompleteForBoot() bool { // for now, v1alpha1 config is required return container.v1alpha1Config != nil } ================================================ FILE: pkg/machinery/config/container/container_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package container_test import ( "net/url" "testing" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xtesting/must" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestNew(t *testing.T) { t.Parallel() v1alpha1Cfg := &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ DiskQuotaSupport: new(true), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ClusterSecret: "topsecret", }, } sideroLinkCfg := siderolink.NewConfigV1Alpha1() sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/join?jointoken=secret&user=alice"))(t) extensionsCfg := extensions.NewServicesConfigV1Alpha1() extensionsCfg.ServiceName = "test-extension" extensionsCfg.ServiceConfigFiles = []extensions.ConfigFile{ { ConfigFileContent: "test", ConfigFileMountPath: "/etc/test", }, } pciDriverRebindCfg := hardware.NewPCIDriverRebindConfigV1Alpha1() pciDriverRebindCfg.MetaName = "0000:04:00.00" pciDriverRebindCfg.PCITargetDriver = "vfio-pci" cfg, err := container.New(v1alpha1Cfg, sideroLinkCfg, extensionsCfg, pciDriverRebindCfg) require.NoError(t, err) assert.False(t, cfg.Readonly()) assert.False(t, cfg.Debug()) assert.True(t, cfg.Machine().Features().DiskQuotaSupportEnabled()) assert.Equal(t, "topsecret", cfg.Cluster().Secret()) assert.Equal(t, "https://siderolink.api/join?jointoken=secret&user=alice", cfg.SideroLink().APIUrl().String()) assert.Equal(t, "test-extension", cfg.ExtensionServiceConfigs()[0].Name()) assert.Equal(t, "0000:04:00.00", cfg.PCIDriverRebindConfig().PCIDriverRebindConfigs()[0].PCIID()) assert.Same(t, v1alpha1Cfg, cfg.RawV1Alpha1()) assert.Equal(t, []config.Document{v1alpha1Cfg, sideroLinkCfg, extensionsCfg, pciDriverRebindCfg}, cfg.Documents()) bytes, err := cfg.Bytes() require.NoError(t, err) cfgBack, err := configloader.NewFromBytes(bytes) require.NoError(t, err) assert.True(t, cfgBack.Readonly()) assert.NotEqual(t, v1alpha1Cfg, cfgBack.RawV1Alpha1()) cfgRedacted := cfg.RedactSecrets("REDACTED") assert.Equal(t, "REDACTED", cfgRedacted.Cluster().Secret()) assert.Equal(t, "https://siderolink.api/join?jointoken=REDACTED&user=alice", cfgRedacted.SideroLink().APIUrl().String()) } func TestNewDuplicate(t *testing.T) { t.Parallel() v1alpha1Cfg1 := &v1alpha1.Config{} v1alpha1Cfg2 := &v1alpha1.Config{} siderolink1 := siderolink.NewConfigV1Alpha1() siderolink2 := siderolink.NewConfigV1Alpha1() _, err := container.New(v1alpha1Cfg1, siderolink1, v1alpha1Cfg2) assert.EqualError(t, err, "duplicate v1alpha1.Config") _, err = container.New(siderolink1, siderolink2) assert.EqualError(t, err, "duplicate document: SideroLinkConfig/") } func TestNewConflict(t *testing.T) { t.Parallel() v1alpha1Cfg1 := &v1alpha1.Config{} uv1 := block.NewUserVolumeConfigV1Alpha1() uv1.MetaName = "my-user-volume-1" uv2 := block.NewUserVolumeConfigV1Alpha1() uv2.MetaName = "my-user-volume-2" ev1 := block.NewExistingVolumeConfigV1Alpha1() ev1.MetaName = "my-user-volume-1" ev2 := block.NewExistingVolumeConfigV1Alpha1() ev2.MetaName = "my-user-volume-2" _, err := container.New(v1alpha1Cfg1, uv1, uv2, ev1) assert.EqualError(t, err, "conflicting documents: UserVolumeConfig/my-user-volume-1 and ExistingVolumeConfig/my-user-volume-1") _, err = container.New(uv1, uv2) require.NoError(t, err) _, err = container.New(ev2, ev1, uv1, uv2) assert.EqualError(t, err, "conflicting documents: ExistingVolumeConfig/my-user-volume-1 and UserVolumeConfig/my-user-volume-1") } func TestPatchV1Alpha1(t *testing.T) { t.Parallel() v1alpha1Cfg := &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", }, } sideroLinkCfg := siderolink.NewConfigV1Alpha1() sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice"))(t) cfg, err := container.New(v1alpha1Cfg, sideroLinkCfg) require.NoError(t, err) patchedCfg, err := cfg.PatchV1Alpha1(func(cfg *v1alpha1.Config) error { cfg.MachineConfig.MachineType = "controlplane" return nil }) require.NoError(t, err) assert.Equal(t, machine.TypeWorker, cfg.Machine().Type()) assert.Equal(t, machine.TypeControlPlane, patchedCfg.Machine().Type()) assert.Equal(t, "https://siderolink.api/?jointoken=secret&user=alice", cfg.SideroLink().APIUrl().String()) assert.Equal(t, "https://siderolink.api/?jointoken=secret&user=alice", patchedCfg.SideroLink().APIUrl().String()) } func TestValidate(t *testing.T) { t.Parallel() sideroLinkCfg := siderolink.NewConfigV1Alpha1() sideroLinkCfg.APIUrlConfig.URL = must.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice"))(t) invalidSideroLinkCfg := siderolink.NewConfigV1Alpha1() v1alpha1Cfg := &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: must.Value(url.Parse("https://localhost:6443"))(t), }, }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("cert"), }, }, } invalidV1alpha1Config := &v1alpha1.Config{} for _, tt := range []struct { name string documents []config.Document expectedError string expecetedWarnings []string }{ { name: "empty", }, { name: "multi-doc", documents: []config.Document{sideroLinkCfg, v1alpha1Cfg}, }, { name: "only siderolink", documents: []config.Document{sideroLinkCfg}, }, { name: "only v1alpha1", documents: []config.Document{v1alpha1Cfg}, }, { name: "invalid siderolink", documents: []config.Document{invalidSideroLinkCfg}, expectedError: "1 error occurred:\n\t* SideroLinkConfig: apiUrl is required\n\n", }, { name: "invalid v1alpha1", documents: []config.Document{invalidV1alpha1Config}, expectedError: "1 error occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\n", }, { name: "invalid multi-doc", documents: []config.Document{invalidSideroLinkCfg, invalidV1alpha1Config}, expectedError: "2 errors occurred:\n\t* v1alpha1.Config: 1 error occurred:\n\t* machine instructions are required\n\n\n\t* SideroLinkConfig: apiUrl is required\n\n", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctr, err := container.New(tt.documents...) require.NoError(t, err) warnings, err := ctr.Validate(validationMode{}) if tt.expectedError == "" { require.NoError(t, err) } else { require.EqualError(t, err, tt.expectedError) } require.Equal(t, tt.expecetedWarnings, warnings) }) } } func TestCrossValidateEncryption(t *testing.T) { t.Parallel() v1alpha1Cfg := &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: must.Value(url.Parse("https://localhost:6443"))(t), }, }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("cert"), }, MachineSystemDiskEncryption: &v1alpha1.SystemDiskEncryptionConfig{ EphemeralPartition: &v1alpha1.EncryptionConfig{ EncryptionKeys: []*v1alpha1.EncryptionKey{ { KeySlot: 1, KeyStatic: &v1alpha1.EncryptionKeyStatic{ KeyData: "static-key", }, }, }, }, }, }, } defaultEphemeral := block.NewVolumeConfigV1Alpha1() defaultEphemeral.MetaName = constants.EphemeralPartitionLabel encryptedEphemeral := block.NewVolumeConfigV1Alpha1() encryptedEphemeral.MetaName = constants.EphemeralPartitionLabel encryptedEphemeral.EncryptionSpec = block.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionKeys: []block.EncryptionKey{ { KeySlot: 2, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "encrypted-static-key", }, }, }, } encryptedState := block.NewVolumeConfigV1Alpha1() encryptedState.MetaName = constants.StatePartitionLabel encryptedState.EncryptionSpec = block.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionKeys: []block.EncryptionKey{ { KeySlot: 3, KeyTPM: &block.EncryptionKeyTPM{}, }, }, } for _, tt := range []struct { name string documents []config.Document expectedError string expecetedWarnings []string }{ { name: "only v1alpha1", documents: []config.Document{v1alpha1Cfg}, }, { name: "v1alpha1 with no-conflict volumes", documents: []config.Document{v1alpha1Cfg, defaultEphemeral, encryptedState}, }, { name: "v1alpha1 with no-conflict volumes", documents: []config.Document{v1alpha1Cfg, encryptedState}, }, { name: "no v1alpha1", documents: []config.Document{encryptedEphemeral, encryptedState}, }, { name: "conflict on ephemeral encryption", documents: []config.Document{v1alpha1Cfg, encryptedEphemeral}, expectedError: "1 error occurred:\n\t* system disk encryption for \"EPHEMERAL\" is configured in both v1alpha1.Config and VolumeConfig\n\n", }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctr, err := container.New(tt.documents...) require.NoError(t, err) warnings, err := ctr.Validate(validationMode{}) if tt.expectedError == "" { require.NoError(t, err) } else { require.EqualError(t, err, tt.expectedError) } require.Equal(t, tt.expecetedWarnings, warnings) }) } } func TestRunDefaultDHCPOperators(t *testing.T) { t.Parallel() v1alpha1Cfg := &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{}, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", }, } dummyLinkConfig := network.NewDummyLinkConfigV1Alpha1("dummy1") for _, tt := range []struct { name string documents []config.Document expected bool }{ { name: "empty", documents: []config.Document{}, expected: true, }, { name: "only v1alpha1", documents: []config.Document{v1alpha1Cfg}, expected: true, }, { name: "has dummy link config", documents: []config.Document{v1alpha1Cfg, dummyLinkConfig}, expected: false, }, { name: "only dummy link config", documents: []config.Document{dummyLinkConfig}, expected: false, }, } { t.Run(tt.name, func(t *testing.T) { t.Parallel() ctr, err := container.New(tt.documents...) require.NoError(t, err) assert.Equal(t, tt.expected, ctr.RunDefaultDHCPOperators()) }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/contract.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "fmt" "regexp" "strconv" "strings" ) // VersionContract describes Talos version to generate config for. // // Config generation only supports backwards compatibility (e.g. Talos 0.9 can generate configs for Talos 0.9 and 0.8). // Matching version of the machinery package is required to generate configs for the current version of Talos. // // Nil value of *VersionContract always describes current version of Talos. type VersionContract struct { Major int Minor int } // Well-known Talos version contracts. var ( TalosVersionCurrent = (*VersionContract)(nil) TalosVersion1_13 = &VersionContract{1, 13} TalosVersion1_12 = &VersionContract{1, 12} TalosVersion1_11 = &VersionContract{1, 11} TalosVersion1_10 = &VersionContract{1, 10} TalosVersion1_9 = &VersionContract{1, 9} TalosVersion1_8 = &VersionContract{1, 8} TalosVersion1_7 = &VersionContract{1, 7} TalosVersion1_6 = &VersionContract{1, 6} TalosVersion1_5 = &VersionContract{1, 5} TalosVersion1_4 = &VersionContract{1, 4} TalosVersion1_3 = &VersionContract{1, 3} TalosVersion1_2 = &VersionContract{1, 2} TalosVersion1_1 = &VersionContract{1, 1} TalosVersion1_0 = &VersionContract{1, 0} ) var versionRegexp = regexp.MustCompile(`^v(\d+)\.(\d+)($|\.)`) // ParseContractFromVersion parses Talos version into VersionContract. func ParseContractFromVersion(version string) (*VersionContract, error) { version = "v" + strings.TrimPrefix(version, "v") matches := versionRegexp.FindStringSubmatch(version) if len(matches) < 3 { return nil, fmt.Errorf("error parsing version %q", version) } var contract VersionContract contract.Major, _ = strconv.Atoi(matches[1]) //nolint:errcheck contract.Minor, _ = strconv.Atoi(matches[2]) //nolint:errcheck return &contract, nil } // String returns string representation of the contract. func (contract *VersionContract) String() string { if contract == nil { return "current" } return fmt.Sprintf("v%d.%d", contract.Major, contract.Minor) } // Greater compares contract to another contract. func (contract *VersionContract) Greater(other *VersionContract) bool { if contract == nil { return other != nil } if other == nil { return false } return contract.Major > other.Major || (contract.Major == other.Major && contract.Minor > other.Minor) } // PodSecurityAdmissionEnabled returns true if pod security admission should be enabled by default. func (contract *VersionContract) PodSecurityAdmissionEnabled() bool { return contract.Greater(TalosVersion1_0) } // StableHostnameEnabled returns true if stable hostname generation should be enabled by default. func (contract *VersionContract) StableHostnameEnabled() bool { return contract.Greater(TalosVersion1_1) } // KubeletDefaultRuntimeSeccompProfileEnabled returns true if kubelet seccomp profile should be enabled by default. func (contract *VersionContract) KubeletDefaultRuntimeSeccompProfileEnabled() bool { return contract.Greater(TalosVersion1_1) } // KubernetesAlternateImageRegistries returns true if alternate image registries should be enabled by default. // https://github.com/kubernetes/kubernetes/pull/109938 func (contract *VersionContract) KubernetesAlternateImageRegistries() bool { return contract.Greater(TalosVersion1_1) && !contract.Greater(TalosVersion1_2) } // KubernetesAllowSchedulingOnControlPlanes returns true if scheduling on control planes should be enabled by default. func (contract *VersionContract) KubernetesAllowSchedulingOnControlPlanes() bool { return contract.Greater(TalosVersion1_1) } // KubernetesDiscoveryBackendDisabled returns true if Kubernetes cluster discovery backend should be disabled by default. func (contract *VersionContract) KubernetesDiscoveryBackendDisabled() bool { return contract.Greater(TalosVersion1_1) } // ApidExtKeyUsageCheckEnabled returns true if apid should check ext key usage of client certificates. func (contract *VersionContract) ApidExtKeyUsageCheckEnabled() bool { return contract.Greater(TalosVersion1_2) } // APIServerAuditPolicySupported returns true if kube-apiserver custom audit policy is supported. func (contract *VersionContract) APIServerAuditPolicySupported() bool { return contract.Greater(TalosVersion1_2) } // KubeletManifestsDirectoryDisabled returns true if the manifests directory flag is supported. func (contract *VersionContract) KubeletManifestsDirectoryDisabled() bool { return contract.Greater(TalosVersion1_2) } // SecretboxEncryptionSupported returns true if encryption with secretbox is supported. func (contract *VersionContract) SecretboxEncryptionSupported() bool { return contract.Greater(TalosVersion1_2) } // DiskQuotaSupportEnabled returns true if XFS filesystems should enable project quota. func (contract *VersionContract) DiskQuotaSupportEnabled() bool { return contract.Greater(TalosVersion1_4) } // KubePrismEnabled returns true if KubePrism should be enabled by default. func (contract *VersionContract) KubePrismEnabled() bool { return contract.Greater(TalosVersion1_5) } // HostDNSEnabled returns true if host dns router should be enabled by default. func (contract *VersionContract) HostDNSEnabled() bool { return contract.Greater(TalosVersion1_6) } // UseRSAServiceAccountKey returns true if version of Talos should use RSA Service Account key for the kube-apiserver. func (contract *VersionContract) UseRSAServiceAccountKey() bool { return contract.Greater(TalosVersion1_6) } // ClusterNameForWorkers returns true if version of Talos should put cluster name to the worker machine config. func (contract *VersionContract) ClusterNameForWorkers() bool { return contract.Greater(TalosVersion1_7) } // HostDNSForwardKubeDNSToHost returns true if version of Talos forces host dns router to be used as upstream for Kubernetes CoreDNS pods. func (contract *VersionContract) HostDNSForwardKubeDNSToHost() bool { return contract.Greater(TalosVersion1_7) } // AddExcludeFromExternalLoadBalancer returns true if the label 'node.kubernetes.io/exclude-from-external-load-balancers' is automatically added // for controlplane nodes. func (contract *VersionContract) AddExcludeFromExternalLoadBalancer() bool { return contract.Greater(TalosVersion1_7) } // SecureBootEnrollEnforcementSupported returns true if version of Talos supports SecureBoot enforcement on enroll. func (contract *VersionContract) SecureBootEnrollEnforcementSupported() bool { return contract.Greater(TalosVersion1_7) } // VolumeConfigEncryptionSupported returns true if version of Talos supports disk encryption via VolumeConfig. func (contract *VersionContract) VolumeConfigEncryptionSupported() bool { return contract.Greater(TalosVersion1_10) } // MultidocNetworkConfigSupported returns true if version of Talos supports multiple NetworkConfig documents. func (contract *VersionContract) MultidocNetworkConfigSupported() bool { return contract.Greater(TalosVersion1_11) } // HideDisablePSP returns true if version of Talos should hide DisablePodSecurityPolicy field from the user. func (contract *VersionContract) HideDisablePSP() bool { return contract.Greater(TalosVersion1_11) } // HideRBACAndKeyUsage returns true if version of Talos should hide RBAC and ApidCheckExtKeyUsage fields from the user. func (contract *VersionContract) HideRBACAndKeyUsage() bool { return contract.Greater(TalosVersion1_11) } // PopulateClusterSANsFromEndpoint returns true if version of Talos should populate cluster SANs from ControlPlaneEndpoint. func (contract *VersionContract) PopulateClusterSANsFromEndpoint() bool { return !contract.Greater(TalosVersion1_11) } // GrubUseUKICmdlineDefault returns true if version of Talos should use UKI cmdline by default. func (contract *VersionContract) GrubUseUKICmdlineDefault() bool { return contract.Greater(TalosVersion1_11) } // KubeSpanMultidocConfig returns true if version of Talos should use KubeSpan multi-doc config. func (contract *VersionContract) KubeSpanMultidocConfig() bool { return contract.Greater(TalosVersion1_12) } ================================================ FILE: pkg/machinery/config/contract_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package config_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/config" ) func TestContractGreater(t *testing.T) { assert.True(t, config.TalosVersion1_1.Greater(config.TalosVersion1_0)) assert.True(t, config.TalosVersionCurrent.Greater(config.TalosVersion1_2)) assert.True(t, config.TalosVersionCurrent.Greater(config.TalosVersion1_3)) assert.False(t, config.TalosVersion1_2.Greater(config.TalosVersion1_3)) assert.False(t, config.TalosVersion1_2.Greater(config.TalosVersion1_2)) assert.False(t, config.TalosVersionCurrent.Greater(config.TalosVersionCurrent)) } func TestContractParseVersion(t *testing.T) { t.Parallel() for v, expected := range map[string]*config.VersionContract{ "v1.5": config.TalosVersion1_5, "v1.5.": config.TalosVersion1_5, "v1.5.1": config.TalosVersion1_5, "v1.88": {1, 88}, "v1.5.3-alpha.4": config.TalosVersion1_5, "1.6": config.TalosVersion1_6, } { t.Run(v, func(t *testing.T) { t.Parallel() actual, err := config.ParseContractFromVersion(v) assert.NoError(t, err) assert.Equal(t, expected, actual) }) } } func TestContractCurrent(t *testing.T) { contract := config.TalosVersionCurrent assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.True(t, contract.VolumeConfigEncryptionSupported()) assert.True(t, contract.MultidocNetworkConfigSupported()) assert.True(t, contract.HideDisablePSP()) assert.True(t, contract.HideRBACAndKeyUsage()) assert.False(t, contract.PopulateClusterSANsFromEndpoint()) assert.True(t, contract.GrubUseUKICmdlineDefault()) assert.True(t, contract.KubeSpanMultidocConfig()) } func TestContract1_13(t *testing.T) { contract := config.TalosVersion1_13 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.True(t, contract.VolumeConfigEncryptionSupported()) assert.True(t, contract.MultidocNetworkConfigSupported()) assert.True(t, contract.HideDisablePSP()) assert.True(t, contract.HideRBACAndKeyUsage()) assert.False(t, contract.PopulateClusterSANsFromEndpoint()) assert.True(t, contract.GrubUseUKICmdlineDefault()) assert.True(t, contract.KubeSpanMultidocConfig()) } func TestContract1_12(t *testing.T) { contract := config.TalosVersion1_12 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.True(t, contract.VolumeConfigEncryptionSupported()) assert.True(t, contract.MultidocNetworkConfigSupported()) assert.True(t, contract.HideDisablePSP()) assert.True(t, contract.HideRBACAndKeyUsage()) assert.False(t, contract.PopulateClusterSANsFromEndpoint()) assert.True(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_11(t *testing.T) { contract := config.TalosVersion1_11 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.True(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_10(t *testing.T) { contract := config.TalosVersion1_10 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_9(t *testing.T) { contract := config.TalosVersion1_9 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_8(t *testing.T) { contract := config.TalosVersion1_8 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.True(t, contract.ClusterNameForWorkers()) assert.True(t, contract.HostDNSForwardKubeDNSToHost()) assert.True(t, contract.AddExcludeFromExternalLoadBalancer()) assert.True(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_7(t *testing.T) { contract := config.TalosVersion1_7 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.True(t, contract.HostDNSEnabled()) assert.True(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_6(t *testing.T) { contract := config.TalosVersion1_6 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.True(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_5(t *testing.T) { contract := config.TalosVersion1_5 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.True(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_4(t *testing.T) { contract := config.TalosVersion1_4 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.False(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_3(t *testing.T) { contract := config.TalosVersion1_3 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.True(t, contract.ApidExtKeyUsageCheckEnabled()) assert.True(t, contract.APIServerAuditPolicySupported()) assert.True(t, contract.KubeletManifestsDirectoryDisabled()) assert.True(t, contract.SecretboxEncryptionSupported()) assert.False(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_2(t *testing.T) { contract := config.TalosVersion1_2 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.True(t, contract.StableHostnameEnabled()) assert.True(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.True(t, contract.KubernetesAlternateImageRegistries()) assert.True(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.True(t, contract.KubernetesDiscoveryBackendDisabled()) assert.False(t, contract.ApidExtKeyUsageCheckEnabled()) assert.False(t, contract.APIServerAuditPolicySupported()) assert.False(t, contract.KubeletManifestsDirectoryDisabled()) assert.False(t, contract.SecretboxEncryptionSupported()) assert.False(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_1(t *testing.T) { contract := config.TalosVersion1_1 assert.True(t, contract.PodSecurityAdmissionEnabled()) assert.False(t, contract.StableHostnameEnabled()) assert.False(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.False(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.False(t, contract.KubernetesDiscoveryBackendDisabled()) assert.False(t, contract.ApidExtKeyUsageCheckEnabled()) assert.False(t, contract.APIServerAuditPolicySupported()) assert.False(t, contract.KubeletManifestsDirectoryDisabled()) assert.False(t, contract.SecretboxEncryptionSupported()) assert.False(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } func TestContract1_0(t *testing.T) { contract := config.TalosVersion1_0 assert.False(t, contract.PodSecurityAdmissionEnabled()) assert.False(t, contract.StableHostnameEnabled()) assert.False(t, contract.KubeletDefaultRuntimeSeccompProfileEnabled()) assert.False(t, contract.KubernetesAlternateImageRegistries()) assert.False(t, contract.KubernetesAllowSchedulingOnControlPlanes()) assert.False(t, contract.KubernetesDiscoveryBackendDisabled()) assert.False(t, contract.ApidExtKeyUsageCheckEnabled()) assert.False(t, contract.APIServerAuditPolicySupported()) assert.False(t, contract.KubeletManifestsDirectoryDisabled()) assert.False(t, contract.SecretboxEncryptionSupported()) assert.False(t, contract.DiskQuotaSupportEnabled()) assert.False(t, contract.KubePrismEnabled()) assert.False(t, contract.HostDNSEnabled()) assert.False(t, contract.UseRSAServiceAccountKey()) assert.False(t, contract.ClusterNameForWorkers()) assert.False(t, contract.HostDNSForwardKubeDNSToHost()) assert.False(t, contract.AddExcludeFromExternalLoadBalancer()) assert.False(t, contract.SecureBootEnrollEnforcementSupported()) assert.False(t, contract.VolumeConfigEncryptionSupported()) assert.False(t, contract.MultidocNetworkConfigSupported()) assert.False(t, contract.HideDisablePSP()) assert.False(t, contract.HideRBACAndKeyUsage()) assert.True(t, contract.PopulateClusterSANsFromEndpoint()) assert.False(t, contract.GrubUseUKICmdlineDefault()) assert.False(t, contract.KubeSpanMultidocConfig()) } ================================================ FILE: pkg/machinery/config/encoder/documentation.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encoder import ( "reflect" "regexp" "strings" "sync" yaml "go.yaml.in/yaml/v4" ) const ( // HeadComment populates `yaml.Node` `HeadComment`. HeadComment = iota // LineComment populates `yaml.Node` `LineComment`. LineComment // FootComment populates `yaml.Node` `FootComment`. FootComment ) // Doc represents a struct documentation rendered from comments by docgen. type Doc struct { // Comments stores foot, line and head comments. Comments [3]string // Fields contains fields documentation if related item is a struct. Fields []Doc // Examples list of example values for the item. Examples []*Example // Values is only used to render valid values list in the documentation. Values []string // Description represents the full description for the item. Description string // Name represents struct name or field name. Name string // Type represents struct name or field type. Type string // Note is rendered as a note for the example in markdown file. Note string // AppearsIn describes back references for the type. AppearsIn []Appearance // Inline indicates that this field is inlined (embedded). Inline bool } // AddExample adds a new example snippet to the doc. func (d *Doc) AddExample(name string, value any) { if d.Examples == nil { d.Examples = []*Example{} } d.Examples = append(d.Examples, &Example{ Name: name, value: value, }) } // Describe returns a field description. func (d *Doc) Describe(field string, short bool) string { desc := "" for _, f := range d.Fields { if f.Name == field { desc = f.Description } } if short { desc = strings.Split(desc, "\n")[0] } return desc } // Example represents one example snippet for a type. type Example struct { populate sync.Once Name string valueMutex sync.RWMutex value any } // Populate populates example value. func (e *Example) Populate(index int) { e.populate.Do(func() { if reflect.TypeOf(e.value).Kind() != reflect.Ptr { return } v := reflect.ValueOf(e.value).Elem() defaultValue := getExample(v, getDoc(e.value), index) e.valueMutex.Lock() defer e.valueMutex.Unlock() if defaultValue != nil { v.Set(defaultValue.Convert(v.Type())) } populateNestedExamples(v, index) }) } // GetValue returns example value. func (e *Example) GetValue() any { e.valueMutex.RLock() defer e.valueMutex.RUnlock() return e.value } // Field gets field from the list of fields. func (d *Doc) Field(i int) *Doc { if i < len(d.Fields) { return &d.Fields[i] } return nil } // Appearance of a type in a different type. type Appearance struct { TypeName string FieldName string } // Documented is used to check if struct has any documentation defined for it. type Documented interface { // Doc requests documentation object. Doc() *Doc } func mergeDoc(a, b *Doc) *Doc { var res Doc if a != nil { res = *a } if b == nil { return &res } for i, comment := range b.Comments { if comment != "" { res.Comments[i] = comment } } if len(res.Examples) == 0 { res.Examples = b.Examples } return &res } func getDoc(in any) *Doc { v := reflect.ValueOf(in) if v.Kind() == reflect.Ptr && v.IsNil() { in = reflect.New(v.Type().Elem()).Interface() } if d, ok := in.(Documented); ok { return d.Doc() } return nil } func addComments(node *yaml.Node, doc *Doc, comments ...int) { if doc != nil { dest := []*string{ &node.HeadComment, &node.LineComment, &node.FootComment, } if len(comments) == 0 { comments = []int{ HeadComment, LineComment, FootComment, } } for _, i := range comments { if doc.Comments[i] != "" { *dest[i] = doc.Comments[i] } } } } //nolint:gocyclo func renderExample(key string, doc *Doc, options *Options) string { if doc == nil { return "" } examples := make([]string, 0, len(doc.Examples)) for i, e := range doc.Examples { v := reflect.ValueOf(e.GetValue()) if isEmpty(v) { continue } if v.Kind() != reflect.Ptr { v = reflect.Indirect(v) } defaultValue := v.Interface() e.Populate(i) node, err := toYamlNode(defaultValue, options) if err != nil { continue } if key != "" { node, err = toYamlNode(map[string]*yaml.Node{ key: node, }, options) if err != nil { continue } } if i == 0 && options.Comments.enabled(CommentsDocs) { addComments(node, doc, HeadComment, LineComment) } // replace head comment with line comment if node.HeadComment == "" { node.HeadComment = node.LineComment } node.LineComment = "" if e.Name != "" { if node.HeadComment != "" { node.HeadComment += "\n\n" } node.HeadComment = node.HeadComment + e.Name + "\n" } data, err := yaml.Marshal(node) if err != nil { continue } if key == "" { // re-indent data = regexp.MustCompile(`(?m)^(.)`).ReplaceAll(data, []byte(" $1")) } else { // don't collapse comment data = regexp.MustCompile(`(?m)^#`).ReplaceAll(data, []byte("# #")) } examples = append(examples, string(data)) } return strings.Join(examples, "") } func getExample(v reflect.Value, doc *Doc, index int) *reflect.Value { if doc == nil || len(doc.Examples) == 0 { return nil } numExamples := len(doc.Examples) if index >= numExamples { index = numExamples - 1 } defaultValue := reflect.ValueOf(doc.Examples[index].GetValue()) if !isEmpty(defaultValue) { if v.Kind() != reflect.Ptr && defaultValue.Kind() == reflect.Ptr { defaultValue = defaultValue.Elem() } } return &defaultValue } //nolint:gocyclo func populateNestedExamples(v reflect.Value, index int) { //nolint:exhaustive switch v.Kind() { case reflect.Struct: doc := getDoc(v.Interface()) for i := range v.NumField() { field := v.Field(i) if !field.CanInterface() { continue } if doc != nil && i < len(doc.Fields) { defaultValue := getExample(field, doc.Field(i), index) if defaultValue != nil { field.Set(defaultValue.Convert(field.Type())) } } populateNestedExamples(field, index) } case reflect.Map: for _, key := range v.MapKeys() { populateNestedExamples(v.MapIndex(key), index) } case reflect.Slice: for i := range v.Len() { populateNestedExamples(v.Index(i), index) } } } ================================================ FILE: pkg/machinery/config/encoder/encoder.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encoder import ( "cmp" "encoding" "reflect" "slices" "strings" yaml "go.yaml.in/yaml/v4" ) // Encoder implements config encoder. type Encoder struct { value any options *Options } // NewEncoder initializes and returns an `Encoder`. func NewEncoder(value any, opts ...Option) *Encoder { return &Encoder{ value: value, options: newOptions(opts...), } } // Marshal converts value to YAML-serializable value (suitable for MarshalYAML). func (e *Encoder) Marshal() (*yaml.Node, error) { node, err := toYamlNode(e.value, e.options) if err != nil { return nil, err } if e.options.Comments.enabled(CommentsDocs) { addComments(node, getDoc(e.value), HeadComment, LineComment) } return node, nil } // Encode converts value to yaml. // //nolint:gocyclo func (e *Encoder) Encode() ([]byte, error) { if e.options.Comments == CommentsDisabled { return yaml.Marshal(e.value) } node, err := e.Marshal() if err != nil { return nil, err } // special handling for case when we get an empty output if node.Kind == yaml.MappingNode && len(node.Content) == 0 && node.FootComment != "" && e.options.Comments.enabled(CommentsExamples) { res := "" if node.HeadComment != "" { res += node.HeadComment + "\n" } lines := strings.Split(res+node.FootComment, "\n") for i, line := range lines { if strings.HasPrefix(line, "#") || strings.TrimSpace(line) == "" { continue } lines[i] = "# " + line } return []byte(strings.Join(lines, "\n")), nil } return yaml.Marshal(node) } func isEmpty(value reflect.Value) bool { if !value.IsValid() { return true } //nolint:exhaustive switch value.Kind() { case reflect.Ptr: return value.IsNil() case reflect.Map: return len(value.MapKeys()) == 0 case reflect.Slice: return value.Len() == 0 default: return value.IsZero() } } func isNil(value reflect.Value) bool { if !value.IsValid() { return true } //nolint:exhaustive switch value.Kind() { case reflect.Ptr, reflect.Map, reflect.Slice: return value.IsNil() default: return false } } //nolint:gocyclo,cyclop func toYamlNode(in any, options *Options) (*yaml.Node, error) { node := &yaml.Node{} flags := options.Comments // do not wrap yaml.Node into yaml.Node if n, ok := in.(*yaml.Node); ok { return n, nil } // if input implements yaml.Marshaler we should use that marshaller instead // same way as regular yaml marshal does if m, ok := in.(yaml.Marshaler); ok && !isNil(reflect.ValueOf(in)) { res, err := m.MarshalYAML() if err != nil { return nil, err } if n, ok := res.(*yaml.Node); ok { return n, nil } in = res } if _, ok := in.(encoding.TextMarshaler); ok && !isNil(reflect.ValueOf(in)) { return node, node.Encode(in) } v := reflect.ValueOf(in) if v.Kind() == reflect.Ptr { v = v.Elem() } doc := getDoc(in) //nolint:exhaustive switch v.Kind() { case reflect.Struct: node.Kind = yaml.MappingNode t := v.Type() var examples []string for i := range v.NumField() { // skip unexported fields if !v.Field(i).CanInterface() { continue } tag := t.Field(i).Tag.Get("yaml") parts := strings.Split(tag, ",") fieldName := parts[0] parts = parts[1:] tag = t.Field(i).Tag.Get("talos") if tag != "" { parts = append(parts, strings.Split(tag, ",")...) } if fieldName == "" { fieldName = strings.ToLower(t.Field(i).Name) } if fieldName == "-" { continue } var ( empty = isEmpty(v.Field(i)) null = isNil(v.Field(i)) skip bool inline bool flow bool ) for _, part := range parts { if part == "omitempty" && empty && options.OmitEmpty { skip = true } if part == "omitonlyifnil" && !null { skip = false } if part == "inline" { inline = true } if part == "flow" { flow = true } } var value any if v.Field(i).CanInterface() { value = v.Field(i).Interface() } // get documentation data either from field, or from type var fieldDoc *Doc if doc != nil { fieldDoc = mergeDoc(getDoc(value), doc.Field(i)) } else { fieldDoc = getDoc(value) } // inlineExample is rendered after the value var inlineExample string if empty && flags.enabled(CommentsExamples) && fieldDoc != nil { if skip { // render example to be appended to the end of the rendered struct example := renderExample(fieldName, fieldDoc, options) if example != "" { examples = append(examples, example) } } else { // render example to be appended to the empty field fieldDocCopy := *fieldDoc fieldDocCopy.Comments = [3]string{} inlineExample = renderExample("", &fieldDocCopy, options) } } if skip { continue } var style yaml.Style if flow { style |= yaml.FlowStyle } if inline { child, err := toYamlNode(value, options) if err != nil { return nil, err } if child.Kind == yaml.MappingNode || child.Kind == yaml.SequenceNode { appendNodes(node, child.Content...) } } else if err := addToMap(node, fieldDoc, fieldName, value, style, options); err != nil { return nil, err } if inlineExample != "" { nodeToAttach := node.Content[len(node.Content)-1] if nodeToAttach.FootComment != "" { nodeToAttach.FootComment += "\n" } nodeToAttach.FootComment += inlineExample } } if len(examples) > 0 { comment := strings.Join(examples, "\n") // add rendered example to the foot comment of the last node // or to the foot comment of parent node if len(node.Content) > 0 { node.Content[len(node.Content)-2].FootComment += "\n" + comment } else { node.FootComment += comment } } case reflect.Map: node.Kind = yaml.MappingNode keys := v.MapKeys() // always interate keys in alphabetical order to preserve the same output for maps slices.SortFunc(keys, func(a, b reflect.Value) int { return cmp.Compare(a.String(), b.String()) }) for _, k := range keys { element := v.MapIndex(k) value := element.Interface() if err := addToMap(node, nil, k.Interface(), value, 0, options); err != nil { return nil, err } } case reflect.Slice: node.Kind = yaml.SequenceNode nodes := make([]*yaml.Node, v.Len()) for i := range v.Len() { element := v.Index(i) var err error nodes[i], err = toYamlNode(element.Interface(), options) if err != nil { return nil, err } } appendNodes(node, nodes...) default: if err := node.Encode(in); err != nil { return nil, err } } return node, nil } func appendNodes(dest *yaml.Node, nodes ...*yaml.Node) { if dest.Content == nil { dest.Content = []*yaml.Node{} } dest.Content = append(dest.Content, nodes...) } func addToMap(dest *yaml.Node, doc *Doc, fieldName, in any, style yaml.Style, options *Options) error { key, err := toYamlNode(fieldName, options) if err != nil { return err } value, err := toYamlNode(in, options) if err != nil { return err } value.Style = style if options.Comments.enabled(CommentsDocs) { addComments(key, doc, HeadComment, FootComment) addComments(value, doc, LineComment) } // override head comment with line comment for non-scalar nodes if value.Kind != yaml.ScalarNode { if key.HeadComment == "" { key.HeadComment = value.LineComment } value.LineComment = "" } appendNodes(dest, key, value) return nil } ================================================ FILE: pkg/machinery/config/encoder/encoder_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encoder_test import ( "sync" "testing" "github.com/stretchr/testify/suite" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) type Config struct { Integer int `yaml:"integer"` Slice []string `yaml:"slice"` ComplexSlice []*Endpoint `yaml:"complex_slice"` Map map[string]*Endpoint `yaml:"map"` Skip string `yaml:"-"` Omit int `yaml:",omitempty"` Inline *Mixin `yaml:",inline"` CustomMarshaller *WithCustomMarshaller `yaml:",omitempty"` Bytes []byte `yaml:"bytes,flow,omitempty"` NilSlice Manifests `yaml:"nilslice,omitempty" talos:"omitonlyifnil"` unexported int } type FakeConfig struct { Machine Machine `yaml:"machine,omitempty"` } type Mixin struct { MixedIn string `yaml:"mixed_in"` } type Endpoint struct { Host string Port int `yaml:",omitempty"` } type Machine struct { State int Config *MachineConfig `yaml:",omitempty"` } type MachineConfig struct { Version string Capabilities []string } type Manifests []Manifest type Manifest struct { Name string `yaml:"name"` } type WithCustomMarshaller struct { value string } // MarshalYAML implements custom marshaller. func (cm *WithCustomMarshaller) MarshalYAML() (any, error) { node := &yaml.Node{} if err := node.Encode(map[string]string{"value": cm.value}); err != nil { return nil, err } node.HeadComment = "completely custom" return node, nil } // This is manually defined documentation data for Config. // It is intended to be generated by `docgen` command. var ( configDoc encoder.Doc endpointDoc encoder.Doc mixinDoc encoder.Doc machineDoc encoder.Doc machineConfigDoc encoder.Doc ) func init() { configDoc.Comments[encoder.LineComment] = "test configuration" configDoc.Fields = make([]encoder.Doc, 11) configDoc.Fields[1].Comments[encoder.LineComment] = "<<<" configDoc.Fields[2].Comments[encoder.HeadComment] = "complex slice" configDoc.Fields[3].Comments[encoder.FootComment] = "some text example for map" configDoc.Fields[2].AddExample("slice example", []*Endpoint{{ Host: "127.0.0.1", Port: 5554, }}) configDoc.Fields[9].Comments[encoder.LineComment] = "A nilslice field is really cool." configDoc.Fields[9].AddExample("nilslice example", Manifests{{ Name: "foo", }}) endpointDoc.Comments[encoder.LineComment] = "endpoint settings" endpointDoc.Fields = make([]encoder.Doc, 2) endpointDoc.Fields[0].Comments[encoder.LineComment] = "endpoint host" endpointDoc.Fields[1].Comments[encoder.LineComment] = "custom port" mixinDoc.Fields = make([]encoder.Doc, 1) mixinDoc.Fields[0].Comments[encoder.LineComment] = "was inlined" machineDoc.AddExample("uncomment me", &Machine{ State: 100, }) machineDoc.AddExample("second example", &Machine{ State: -1, }) machineDoc.Fields = make([]encoder.Doc, 2) machineDoc.Fields[1].AddExample("", &MachineConfig{ Version: "0.0.2", }) machineConfigDoc.Fields = make([]encoder.Doc, 2) machineConfigDoc.Fields[0].Comments[encoder.HeadComment] = "this is some version" machineConfigDoc.Fields[1].AddExample("", []string{ "reboot", "upgrade", }, ) } func (c Config) Doc() *encoder.Doc { return &configDoc } func (c Endpoint) Doc() *encoder.Doc { return &endpointDoc } func (c Mixin) Doc() *encoder.Doc { return &mixinDoc } func (c Machine) Doc() *encoder.Doc { return &machineDoc } func (c MachineConfig) Doc() *encoder.Doc { return &machineConfigDoc } // tests type EncoderSuite struct { suite.Suite } func (suite *EncoderSuite) TestRun() { e := &Endpoint{ Port: 8080, } tests := []struct { name string value any expectedYAML string incompatible bool options []encoder.Option }{ { name: "default struct with all enabled", value: &Config{}, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] # # slice example # - host: 127.0.0.1 # endpoint host # port: 5554 # custom port map: {} # some text example for map # # A nilslice field is really cool. # # nilslice example # nilslice: # - name: foo `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsAll), }, incompatible: true, }, { name: "default struct only with examples", value: &Config{}, expectedYAML: `integer: 0 slice: [] complex_slice: [] # # slice example # - host: 127.0.0.1 # port: 5554 map: {} # # nilslice example # nilslice: # - name: foo `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsExamples), }, incompatible: true, }, { name: "default struct", value: &Config{}, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: {} # some text example for map `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "struct with custom marshaller", value: &Config{ CustomMarshaller: &WithCustomMarshaller{ value: "abcd", }, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: {} # some text example for map custommarshaller: # completely custom value: abcd `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "bytes flow", value: &Config{ Bytes: []byte("..."), }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: {} # some text example for map bytes: [46, 46, 46] `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "map check", value: &Config{ Map: map[string]*Endpoint{ "endpoint": new(Endpoint), }, unexported: -1, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: endpoint: host: "" # endpoint host # some text example for map `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "nil map element", value: &Config{ Map: map[string]*Endpoint{ "endpoint": nil, }, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: endpoint: null # some text example for map `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "nil map element", value: &Config{ Map: map[string]*Endpoint{ "endpoint": new(Endpoint), }, ComplexSlice: []*Endpoint{e}, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: - host: "" # endpoint host port: 8080 # custom port map: endpoint: host: "" # endpoint host # some text example for map `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "inline", value: &Config{ Inline: &Mixin{ MixedIn: "a", }, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] map: {} # some text example for map mixed_in: a # was inlined `, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "comment example if zero", value: &FakeConfig{}, expectedYAML: `# # uncomment me # machine: # state: 100 # config: # # this is some version # version: 0.0.2 # capabilities: # - reboot # - upgrade # # second example # machine: # state: -1 # config: # # this is some version # version: 0.0.2 # capabilities: # - reboot # - upgrade `, incompatible: true, }, { name: "comment example if partially set", value: &FakeConfig{ Machine{ State: 1000, }, }, expectedYAML: `machine: state: 1000 ` + ` # config: # # this is some version # version: 0.0.2 # capabilities: # - reboot # - upgrade `, incompatible: true, }, { name: "populate map element's examples", value: map[string][]*MachineConfig{ "first": { {}, }, }, expectedYAML: `first: - # this is some version version: "" capabilities: [] # - reboot # - upgrade `, incompatible: true, }, { name: "without comments", value: &FakeConfig{ Machine{ State: 1000, }, }, expectedYAML: `machine: state: 1000 `, incompatible: true, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDisabled), }, }, { name: "only with docs", value: &FakeConfig{}, expectedYAML: `{} `, incompatible: true, options: []encoder.Option{ encoder.WithComments(encoder.CommentsDocs), }, }, { name: "only with examples", value: &FakeConfig{}, expectedYAML: `# # uncomment me # machine: # state: 100 # config: # version: 0.0.2 # capabilities: # - reboot # - upgrade # # second example # machine: # state: -1 # config: # version: 0.0.2 # capabilities: # - reboot # - upgrade `, incompatible: true, options: []encoder.Option{ encoder.WithComments(encoder.CommentsExamples), }, }, { name: "with onlyifnotnil tag", value: &Config{ NilSlice: Manifests{}, }, expectedYAML: `integer: 0 # <<< slice: [] # complex slice complex_slice: [] # # slice example # - host: 127.0.0.1 # endpoint host # port: 5554 # custom port map: {} # some text example for map # A nilslice field is really cool. nilslice: [] # # nilslice example # - name: foo `, incompatible: true, options: []encoder.Option{ encoder.WithComments(encoder.CommentsAll), }, }, } for _, test := range tests { encoder := encoder.NewEncoder(test.value, test.options...) data, err := encoder.Encode() suite.Assert().NoError(err) // compare with expected string output suite.Assert().EqualValues(test.expectedYAML, string(data), test.name) // decode into raw map to strip all comments actualMap, err := decodeToMap(data) suite.Assert().NoError(err) // skip if marshaller output is not the same for our encoder and vanilla one // note: it is only incompatible if config contains nested structs stored as value // and if these nested structs are documented and you try to load generated yaml into map[interface{}]interface{} if !test.incompatible { // compare with regular yaml.Marshal call expected, err := yaml.Marshal(test.value) suite.Assert().NoError(err) expectedMap, err := decodeToMap(expected) suite.Assert().NoError(err) suite.Assert().EqualValues(expectedMap, actualMap) } } } func (suite *EncoderSuite) TestConcurrent() { value := &Machine{} var wg sync.WaitGroup for range 10 { wg.Go(func() { encoder := encoder.NewEncoder(value) _, err := encoder.Encode() suite.Assert().NoError(err) }) } wg.Wait() } func decodeToMap(data []byte) (map[any]any, error) { raw := map[any]any{} err := yaml.Unmarshal(data, &raw) return raw, err } func TestEncoderSuite(t *testing.T) { suite.Run(t, &EncoderSuite{}) } ================================================ FILE: pkg/machinery/config/encoder/markdown.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encoder import ( "bytes" _ "embed" "errors" "fmt" "io/fs" "os" "path/filepath" "slices" "strings" "text/template" yaml "go.yaml.in/yaml/v4" ) //go:embed "markdown.tmpl" var markdownTemplate string // FileDoc represents a single go file documentation. type FileDoc struct { // Name will be used in md file name pattern. Name string // Description file description, supports markdown. Description string // Structs structs defined in the file. Structs []*Doc // Types is map of all non-trivial types defined in the file. Types map[string]*Doc } // Encode encodes file documentation as MD file. func (fd *FileDoc) Encode(root *Doc, frontmatter func(title, description string) string) ([]byte, error) { t := template.Must(template.New("markdown.tmpl"). Funcs(template.FuncMap{ "yaml": encodeYaml, "fmtDesc": formatDescription, "dict": tmplDict, "repeat": strings.Repeat, "trimPrefix": strings.TrimPrefix, "add": func(a, b int) int { return a + b }, "frontmatter": frontmatter, "min": minInt, }). Parse(markdownTemplate)) var buf bytes.Buffer if err := t.Execute(&buf, struct { Root *Doc Types map[string]*Doc }{ Root: root, Types: fd.Types, }); err != nil { return nil, err } return buf.Bytes(), nil } // Write dumps documentation string to folder. // //nolint:gocyclo func (fd *FileDoc) Write(path string, frontmatter func(title, description string) string) error { if stat, err := os.Stat(path); !errors.Is(err, fs.ErrNotExist) { if !stat.IsDir() { return errors.New("destination path should be a directory") } } else { if err := os.MkdirAll(path, 0o777); err != nil { return err } } // generate _index.md if err := os.WriteFile(filepath.Join(path, "_index.md"), []byte(frontmatter(fd.Name, fd.Description)), 0o666); err != nil { return err } // find map of all types fd.Types = map[string]*Doc{} for _, t := range fd.Structs { if t.Type == "" || strings.ToLower(t.Type) == t.Type { continue } fd.Types[t.Type] = t } // find root nodes var roots []*Doc for _, t := range fd.Structs { if len(t.AppearsIn) == 0 { roots = append(roots, t) } } for _, root := range roots { contents, err := fd.Encode(root, frontmatter) if err != nil { return err } if err := os.WriteFile(filepath.Join(path, fmt.Sprintf("%s.%s", strings.ToLower(root.Type), "md")), contents, 0o666); err != nil { return err } } return nil } //nolint:gocyclo func encodeYaml(in any, path string) string { if path != "" { parts := strings.Split(path, ".") parts = parts[1:] // strip first segment, it's root element // if the last element is ""/"-", it means we're at the root of the slice, so we don't need to wrap it once again if len(parts) > 0 && (parts[len(parts)-1] == "" || parts[len(parts)-1] == "-") { parts = parts[:len(parts)-1] } slices.Reverse(parts) for _, part := range parts { switch part { case "": in = []any{in} case "-": in = map[string]any{ "example.com": in, } default: in = map[string]any{ part: in, } } } } node, err := toYamlNode(in, newOptions(WithComments(CommentsAll))) if err != nil { return fmt.Sprintf("yaml encoding failed %s", err) } data, err := yaml.Marshal(node) if err != nil { return fmt.Sprintf("yaml encoding failed %s", err) } lines := strings.Split(string(data), "\n") for i, line := range lines { lines[i] = strings.TrimRight(line, " ") } return fmt.Sprintf("{{< highlight yaml >}}\n%s{{< /highlight >}}", strings.Join(lines, "\n")) } func formatDescription(description string) string { return strings.ReplaceAll(description, "\n", "
") } func tmplDict(vals ...any) (map[string]any, error) { if len(vals)%2 != 0 { return nil, fmt.Errorf("invalid number of arguments: %d", len(vals)) } res := map[string]any{} for i := 0; i < len(vals); i += 2 { key, ok := vals[i].(string) if !ok { return nil, fmt.Errorf("invalid key type: %T", vals[i]) } res[key] = vals[i+1] } return res, nil } func minInt(a, b int) int { return min(a, b) } ================================================ FILE: pkg/machinery/config/encoder/markdown.tmpl ================================================ {{ frontmatter .Root.Type .Root.Description }} {{ define "field" -}} {{ if .Field.Name -}} |`{{ .Field.Name }}` | {{- $type := index $.Types .Field.Type -}} {{- if $type -}}
{{ .Field.Type }} {{- else -}} {{- $type := index .Types (trimPrefix .Field.Type "[]") -}} {{- if $type -}} {{ .Field.Type }} {{- else -}} {{- $type := index .Types (trimPrefix .Field.Type "map[string]") -}} {{- if $type -}} {{ .Field.Type }} {{- else -}} {{ .Field.Type }} {{- end -}} {{- end -}} {{- end }} | {{- fmtDesc .Field.Description }} {{ with .Field.Examples }}
Show example(s){{ range . }}{{ if .Name }}{{ .Name }}:{{ end }}{{ yaml .GetValue (printf ".%s" $.Field.Name) }}{{ end }}
{{ end }} | {{- range $value := .Field.Values }}`{{ $value }}`
{{ end }} | {{ else if .Field.Inline -}} {{- $struct := index .Types .Field.Type -}} {{- if $struct -}} {{- range $inlineField := $struct.Fields -}} {{ template "field" dict "Field" $inlineField "Types" $.Types "Path" $.Path -}} {{- end -}} {{- end -}} {{ end -}} {{ end -}} {{ block "struct" dict "Struct" .Root "Level" 1 "Name" .Root.Type "Path" .Root.Type "Types" .Types }} {{ if gt .Level 1 }}{{ repeat "#" (min .Level 6) }} {{ .Name }} {#{{ .Path }}}{{ end }} {{ if and .Struct.Description (gt .Level 1) -}} {{ .Struct.Description }} {{ end }} {{ range $example := .Struct.Examples }} {{ yaml $example.GetValue $.Path }} {{ end }} {{ if .Struct.Fields -}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| {{ range $field := $.Struct.Fields -}} {{ template "field" dict "Field" $field "Types" $.Types "Path" $.Path -}} {{ end }} {{ end }} {{ range $field := .Struct.Fields }} {{- $struct := index $.Types $field.Type -}} {{- if $field.Inline -}} {{- if $struct -}} {{- range $inlineField := $struct.Fields -}} {{- $struct := index $.Types $inlineField.Type -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" $inlineField.Name "Types" $.Types "Path" (printf "%s.%s" $.Path $inlineField.Name) }} {{- else -}} {{- $struct := index $.Types (trimPrefix $inlineField.Type "[]") -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s[]" $inlineField.Name) "Types" $.Types "Path" (printf "%s.%s." $.Path $inlineField.Name) }} {{- else -}} {{- $struct := index $.Types (trimPrefix $inlineField.Type "map[string]") -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s.*" $inlineField.Name) "Types" $.Types "Path" (printf "%s.%s.-" $.Path $inlineField.Name) }} {{- end -}} {{- end -}} {{- end -}} {{- end -}} {{- end -}} {{- else -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" $field.Name "Types" $.Types "Path" (printf "%s.%s" $.Path $field.Name) }} {{- else -}} {{- $struct := index $.Types (trimPrefix $field.Type "[]") -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s[]" $field.Name) "Types" $.Types "Path" (printf "%s.%s." $.Path $field.Name) }} {{- else -}} {{- $struct := index $.Types (trimPrefix $field.Type "map[string]") -}} {{- if $struct -}} {{ template "struct" dict "Struct" $struct "Level" (add $.Level 1) "Name" (printf "%s.*" $field.Name) "Types" $.Types "Path" (printf "%s.%s.-" $.Path $field.Name) }} {{- end -}} {{- end -}} {{- end -}} {{- end -}} {{ end }} {{ end }} ================================================ FILE: pkg/machinery/config/encoder/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package encoder // CommentsFlags comments encoding flags type. type CommentsFlags int func (f CommentsFlags) enabled(flag CommentsFlags) bool { return (f & flag) == flag } const ( // CommentsDisabled renders no comments. CommentsDisabled CommentsFlags = 0 // CommentsExamples enables commented yaml examples rendering. CommentsExamples CommentsFlags = 1 << iota // CommentsDocs enables rendering each config field short docstring. CommentsDocs // CommentsAll renders all comments. CommentsAll = CommentsExamples | CommentsDocs ) // Options defines encoder config. type Options struct { Comments CommentsFlags OmitEmpty bool } func newOptions(opts ...Option) *Options { res := &Options{ Comments: CommentsAll, OmitEmpty: true, } for _, o := range opts { o(res) } return res } // Option gives ability to alter config encoder output settings. type Option func(*Options) // WithComments enables comments and examples in the encoder. func WithComments(flags CommentsFlags) Option { return func(o *Options) { o.Comments = flags } } // WithOmitEmpty toggles omitempty handling. func WithOmitEmpty(value bool) Option { return func(o *Options) { o.OmitEmpty = value } } ================================================ FILE: pkg/machinery/config/generate/controlplane.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func (in *Input) controlPlane() ([]config.Document, error) { docs, err := in.init() if err != nil { return nil, err } docs[0].(*v1alpha1.Config).MachineConfig.MachineType = machine.TypeControlPlane.String() return docs, nil } ================================================ FILE: pkg/machinery/config/generate/example_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate_test import ( "log" "os" "time" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) //nolint:wsl,testableexamples func Example() { // This is an example of generating a set of machine configuration files for multiple // nodes of the cluster from a single cluster-specific cluster. // Input values for the config generation: // * cluster name and Kubernetes control plane endpoint clusterName := "test-cluster" controlPlaneEndpoint := "https://kubernetes.example.com:6443" // * Kubernetes version to install, using the latest here kubernetesVersion := constants.DefaultKubernetesVersion // * version contract defines the version of the Talos cluster configuration is generated for // generate package can generate machine configuration compatible with current and previous versions of Talos targetVersion := "v1.0" // parse the version contract var ( versionContract = config.TalosVersionCurrent //nolint:wastedassign,ineffassign // version of the Talos machinery package err error ) versionContract, err = config.ParseContractFromVersion(targetVersion) if err != nil { log.Fatalf("failed to parse version contract: %s", err) } // generate the cluster-wide secrets once and use it for every node machine configuration // secrets can be stashed for future use by marshaling the structure to YAML or JSON secretsBundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), versionContract) if err != nil { log.Fatalf("failed to generate secrets bundle: %s", err) } input, err := generate.NewInput(clusterName, controlPlaneEndpoint, kubernetesVersion, generate.WithVersionContract(versionContract), generate.WithSecretsBundle(secretsBundle), generate.WithEndpointList( []string{"172.0.0.1", "172.0.0.2", "172.20.0.3"}, // list of control plane node IP addresses ), // there are many more generate options available which allow to tweak generated config programmatically ) if err != nil { log.Fatalf("failed to generate input: %s", err) } // generate the machine config for each node of the cluster using the secrets for _, node := range []string{"machine1", "machine2"} { var cfg config.Provider // generate the machine config for the node, using the right machine type: // * machine.TypeConrolPlane for control plane nodes // * machine.TypeWorker for worker nodes cfg, err = input.Config(machine.TypeControlPlane) if err != nil { log.Fatalf("failed to generate config for node %q: %s", node, err) } // config can be tweaked at this point to add machine-specific configuration, e.g.: cfg.RawV1Alpha1().MachineConfig.MachineInstall.InstallDisk = "/dev/sdb" // marshal the config to YAML var marshaledCfg []byte marshaledCfg, err = cfg.Bytes() if err != nil { log.Fatalf("failed to generate config for node %q: %s", node, err) } // write the config to a file if err = os.WriteFile(clusterName+"-"+node+".yaml", marshaledCfg, 0o600); err != nil { log.Fatalf("failed to write config for node %q: %s", node, err) } } // generate the client Talos configuration (for API access, e.g. talosctl) clientCfg, err := input.Talosconfig() if err != nil { log.Fatalf("failed to generate client config: %s", err) } if err = clientCfg.Save(clusterName + "-talosconfig"); err != nil { log.Fatalf("failed to save client config: %s", err) } } ================================================ FILE: pkg/machinery/config/generate/generate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package generate provides Talos machine configuration generation and client config generation. // // Please see the example for more information on using this package. package generate import ( "errors" "net/netip" "net/url" "slices" "time" coreconfig "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Input holds info about certs, ips, and node type. // //nolint:maligned type Input struct { Options Options // ControlplaneEndpoint is the canonical address of the kubernetes control // plane. It can be a DNS name, the IP address of a load balancer, or // (default) the IP address of the first controlplane node. It is NOT // multi-valued. It may optionally specify the port. ControlPlaneEndpoint string AdditionalSubjectAltNames []string AdditionalMachineCertSANs []string ClusterName string PodNet []string ServiceNet []string KubernetesVersion string } // GetAPIServerSANs returns the formatted list of Subject Alt Name addresses for the API Server. func (in *Input) GetAPIServerSANs() []string { var list []string if in.Options.VersionContract.PopulateClusterSANsFromEndpoint() { endpointURL, err := url.Parse(in.ControlPlaneEndpoint) if err == nil { list = append(list, endpointURL.Hostname()) } } list = append(list, in.AdditionalSubjectAltNames...) return list } // NewInput prepares a new Input struct to perform machine config generation. func NewInput(clustername, endpoint, kubernetesVersion string, opts ...Option) (*Input, error) { input := &Input{} input.Options = DefaultOptions() for _, opt := range opts { if err := opt(&input.Options); err != nil { return nil, err } } var podNet, serviceNet string if addr, addrErr := netip.ParseAddr(endpoint); addrErr == nil && addr.Is6() { podNet = constants.DefaultIPv6PodNet serviceNet = constants.DefaultIPv6ServiceNet } else { podNet = constants.DefaultIPv4PodNet serviceNet = constants.DefaultIPv4ServiceNet } if input.Options.SecretsBundle == nil { var err error input.Options.SecretsBundle, err = secrets.NewBundle(secrets.NewFixedClock(time.Now()), input.Options.VersionContract) if err != nil { return nil, err } } additionalSubjectAltNames := slices.Clone(input.Options.AdditionalSubjectAltNames) if input.Options.DiscoveryEnabled == nil { input.Options.DiscoveryEnabled = new(true) } input.ClusterName = clustername input.KubernetesVersion = kubernetesVersion input.AdditionalMachineCertSANs = additionalSubjectAltNames input.AdditionalSubjectAltNames = additionalSubjectAltNames input.PodNet = []string{podNet} input.ServiceNet = []string{serviceNet} input.ControlPlaneEndpoint = endpoint input.KubernetesVersion = kubernetesVersion return input, nil } // Config returns the talos config for a given node type. func (in *Input) Config(t machine.Type) (coreconfig.Provider, error) { var ( documents []config.Document err error ) switch t { case machine.TypeInit: documents, err = in.init() case machine.TypeControlPlane: documents, err = in.controlPlane() case machine.TypeWorker: documents, err = in.worker() case machine.TypeUnknown: fallthrough default: return nil, errors.New("failed to determine config type to generate") } if err != nil { return nil, err } return container.New(documents...) } // emptyIf returns empty string if the 2nd argument is empty string, otherwise returns the first argument. func emptyIf(str, check string) string { if check == "" { return "" } return str } ================================================ FILE: pkg/machinery/config/generate/generate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate_test import ( "crypto/x509" "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config" mc "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) type GenerateSuite struct { suite.Suite input *generate.Input genOptions []generate.Option versionContract *config.VersionContract } func TestGenerateSuite(t *testing.T) { t.Parallel() for _, tt := range []struct { label string genOptions []generate.Option }{ { label: "current", }, { label: "1.7", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_7)}, }, { label: "1.6", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_6)}, }, { label: "1.5", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_5)}, }, { label: "1.4", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_4)}, }, { label: "1.3", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_3)}, }, { label: "1.2", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_2)}, }, { label: "1.1", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_1)}, }, { label: "1.0", genOptions: []generate.Option{generate.WithVersionContract(config.TalosVersion1_0)}, }, } { t.Run(tt.label, func(t *testing.T) { t.Parallel() suite.Run(t, &GenerateSuite{ genOptions: tt.genOptions, }) }) } } func (suite *GenerateSuite) SetupSuite() { var err error suite.input, err = generate.NewInput("test", "https://10.0.1.5", constants.DefaultKubernetesVersion, suite.genOptions...) suite.Require().NoError(err) var opts generate.Options for _, opt := range suite.genOptions { suite.Require().NoError(opt(&opts)) } suite.versionContract = suite.input.Options.VersionContract } func (suite *GenerateSuite) TestGenerateInitSuccess() { cfg, err := suite.input.Config(machine.TypeInit) suite.Require().NoError(err) suite.NotEmpty(cfg.Machine().Security().IssuingCA()) } func (suite *GenerateSuite) TestGenerateControlPlaneSuccess() { cfg, err := suite.input.Config(machine.TypeControlPlane) suite.Require().NoError(err) _, err = cfg.Validate(runtimeMode{false}) suite.Require().NoError(err) suite.NotEmpty(cfg.Machine().Security().IssuingCA()) } func (suite *GenerateSuite) TestGenerateWorkerSuccess() { cfg, err := suite.input.Config(machine.TypeWorker) suite.Require().NoError(err) suite.NotEmpty(cfg.Machine().Security().IssuingCA()) } func (suite *GenerateSuite) TestGenerateTalosconfigSuccess() { cfg, err := suite.input.Talosconfig() suite.Require().NoError(err) creds, err := client.CertificateFromConfigContext(cfg.Contexts[cfg.Context]) suite.Require().NoError(err) suite.Require().Len(creds.Certificate, 1) cert, err := x509.ParseCertificate(creds.Certificate[0]) suite.Require().NoError(err) suite.Equal([]string{string(role.Admin)}, cert.Subject.Organization) } func TestGenerateRegistryMirrorsOrder(t *testing.T) { t.Parallel() input, err := generate.NewInput("test", "https://10.0.1.5", constants.DefaultKubernetesVersion, generate.WithRegistryMirror("b.com", "http://127.0.0.1:5004"), generate.WithRegistryMirror("a.com", "http://127.0.0.1:5005"), ) require.NoError(t, err) cfg, err := input.Config(machine.TypeControlPlane) require.NoError(t, err) named, ok := cfg.Documents()[1].(mc.NamedDocument) require.True(t, ok) assert.Equal(t, "a.com", named.Name()) named, ok = cfg.Documents()[2].(mc.NamedDocument) require.True(t, ok) assert.Equal(t, "b.com", named.Name()) } type runtimeMode struct { requiresInstall bool } func (m runtimeMode) String() string { return fmt.Sprintf("runtimeMode(%v)", m.requiresInstall) } func (m runtimeMode) RequiresInstall() bool { return m.requiresInstall } func (runtimeMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/generate/init.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "fmt" "net/url" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" ) //nolint:gocyclo,cyclop func (in *Input) init() ([]config.Document, error) { v1alpha1Config := &v1alpha1.Config{ ConfigVersion: "v1alpha1", ConfigDebug: new(in.Options.Debug), ConfigPersist: new(true), } machine := &v1alpha1.MachineConfig{ MachineType: machine.TypeInit.String(), MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubeletImage, in.KubernetesVersion), in.KubernetesVersion), }, MachineCA: in.Options.SecretsBundle.Certs.OS, MachineCertSANs: in.AdditionalMachineCertSANs, MachineToken: in.Options.SecretsBundle.TrustdInfo.Token, MachineInstall: &v1alpha1.InstallConfig{ InstallDisk: in.Options.InstallDisk, InstallImage: in.Options.InstallImage, InstallWipe: new(false), InstallExtraKernelArgs: in.Options.InstallExtraKernelArgs, }, MachineDisks: in.Options.MachineDisks, MachineSysctls: in.Options.Sysctls, MachineFeatures: &v1alpha1.FeaturesConfig{}, } if in.Options.VersionContract.GrubUseUKICmdlineDefault() { machine.MachineInstall.InstallGrubUseUKICmdline = new(true) } if !in.Options.VersionContract.HideRBACAndKeyUsage() { machine.MachineFeatures.RBAC = new(true) if in.Options.VersionContract.ApidExtKeyUsageCheckEnabled() { machine.MachineFeatures.ApidCheckExtKeyUsage = new(true) } } if in.Options.VersionContract.DiskQuotaSupportEnabled() { machine.MachineFeatures.DiskQuotaSupport = new(true) } if kubePrismPort, optionSet := in.Options.KubePrismPort.Get(); optionSet { // default to enabled, but if set explicitly, allow it to be disabled if kubePrismPort > 0 { machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{ ServerEnabled: new(true), ServerPort: kubePrismPort, } } } else if in.Options.VersionContract.KubePrismEnabled() { machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{ ServerEnabled: new(true), ServerPort: constants.DefaultKubePrismPort, } } if in.Options.VersionContract.KubeletDefaultRuntimeSeccompProfileEnabled() { machine.MachineKubelet.KubeletDefaultRuntimeSeccompProfileEnabled = new(true) } if in.Options.VersionContract.KubeletManifestsDirectoryDisabled() { machine.MachineKubelet.KubeletDisableManifestsDirectory = new(true) } if in.Options.VersionContract.HostDNSEnabled() { machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{ HostDNSEnabled: new(true), HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()), } } if in.Options.VersionContract.AddExcludeFromExternalLoadBalancer() { if machine.MachineNodeLabels == nil { machine.MachineNodeLabels = map[string]string{} } machine.MachineNodeLabels[constants.LabelExcludeFromExternalLB] = "" } certSANs := in.GetAPIServerSANs() controlPlaneURL, err := url.Parse(in.ControlPlaneEndpoint) if err != nil { return nil, err } var admissionControlConfig []*v1alpha1.AdmissionPluginConfig if in.Options.VersionContract.PodSecurityAdmissionEnabled() { admissionControlConfig = append(admissionControlConfig, &v1alpha1.AdmissionPluginConfig{ PluginName: "PodSecurity", PluginConfiguration: v1alpha1.Unstructured{ Object: map[string]any{ "apiVersion": "pod-security.admission.config.k8s.io/v1alpha1", "kind": "PodSecurityConfiguration", "defaults": map[string]any{ "enforce": "baseline", "enforce-version": "latest", "audit": "restricted", "audit-version": "latest", "warn": "restricted", "warn-version": "latest", }, "exemptions": map[string]any{ "usernames": []any{}, "runtimeClasses": []any{}, "namespaces": []any{"kube-system"}, }, }, }, }, ) } var auditPolicyConfig v1alpha1.Unstructured if in.Options.VersionContract.APIServerAuditPolicySupported() { auditPolicyConfig = v1alpha1.APIServerDefaultAuditPolicy } cluster := &v1alpha1.ClusterConfig{ ClusterID: in.Options.SecretsBundle.Cluster.ID, ClusterName: in.ClusterName, ClusterSecret: in.Options.SecretsBundle.Cluster.Secret, ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{URL: controlPlaneURL}, LocalAPIServerPort: in.Options.LocalAPIServerPort, }, APIServerConfig: &v1alpha1.APIServerConfig{ CertSANs: certSANs, ContainerImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubernetesAPIServerImage, in.KubernetesVersion), in.KubernetesVersion), AdmissionControlConfig: admissionControlConfig, AuditPolicyConfig: auditPolicyConfig, }, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{ ContainerImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubernetesControllerManagerImage, in.KubernetesVersion), in.KubernetesVersion), }, ProxyConfig: &v1alpha1.ProxyConfig{ ContainerImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubeProxyImage, in.KubernetesVersion), in.KubernetesVersion), }, SchedulerConfig: &v1alpha1.SchedulerConfig{ ContainerImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubernetesSchedulerImage, in.KubernetesVersion), in.KubernetesVersion), }, EtcdConfig: &v1alpha1.EtcdConfig{ RootCA: in.Options.SecretsBundle.Certs.Etcd, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ DNSDomain: in.Options.DNSDomain, PodSubnet: in.PodNet, ServiceSubnet: in.ServiceNet, CNI: in.Options.CNIConfig, }, ClusterCA: in.Options.SecretsBundle.Certs.K8s, ClusterAggregatorCA: in.Options.SecretsBundle.Certs.K8sAggregator, ClusterServiceAccount: in.Options.SecretsBundle.Certs.K8sServiceAccount, BootstrapToken: in.Options.SecretsBundle.Secrets.BootstrapToken, ExtraManifests: []string{}, ClusterInlineManifests: v1alpha1.ClusterInlineManifests{}, } if in.Options.AllowSchedulingOnControlPlanes { if in.Options.VersionContract.KubernetesAllowSchedulingOnControlPlanes() { cluster.AllowSchedulingOnControlPlanes = new(in.Options.AllowSchedulingOnControlPlanes) } else { // backwards compatibility for Talos versions older than 1.2 cluster.AllowSchedulingOnMasters = new(in.Options.AllowSchedulingOnControlPlanes) //nolint:staticcheck } } if in.Options.DiscoveryEnabled != nil { cluster.ClusterDiscoveryConfig = &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(*in.Options.DiscoveryEnabled), } if in.Options.VersionContract.KubernetesDiscoveryBackendDisabled() { cluster.ClusterDiscoveryConfig.DiscoveryRegistries.RegistryKubernetes.RegistryDisabled = new(true) } } if !in.Options.VersionContract.HideDisablePSP() { cluster.APIServerConfig.DisablePodSecurityPolicyConfig = new(true) } if in.Options.VersionContract.SecretboxEncryptionSupported() { cluster.ClusterSecretboxEncryptionSecret = in.Options.SecretsBundle.Secrets.SecretboxEncryptionSecret } else { cluster.ClusterAESCBCEncryptionSecret = in.Options.SecretsBundle.Secrets.AESCBCEncryptionSecret } v1alpha1Config.MachineConfig = machine v1alpha1Config.ClusterConfig = cluster documents := []config.Document{v1alpha1Config} extraDocuments, err := in.generateRegistryConfigs(machine) if err != nil { return nil, fmt.Errorf("failed to generate registry configs: %w", err) } documents = append(documents, extraDocuments...) extraDocuments, err = in.generateNetworkConfigs(machine) if err != nil { return nil, fmt.Errorf("failed to generate network configs: %w", err) } documents = append(documents, extraDocuments...) return documents, nil } func ptrOrNil(b bool) *bool { if b { return &b } return nil } ================================================ FILE: pkg/machinery/config/generate/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/network" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //nolint:gocyclo func (in *Input) generateNetworkConfigs(machine *v1alpha1.MachineConfig) ([]config.Document, error) { var documents []config.Document if len(in.Options.NetworkConfigOptions) > 0 { networkConfig := &v1alpha1.NetworkConfig{} //nolint:staticcheck // using legacy NetworkConfig for older Talos versions for _, opt := range in.Options.NetworkConfigOptions { if err := opt(machine.Type(), networkConfig); err != nil { return nil, err } } machine.MachineNetwork = networkConfig //nolint:staticcheck // using legacy NetworkConfig for older Talos versions } // generate empty machine.network for backwards compatibility with older Talos versions if machine.MachineNetwork == nil && !in.Options.VersionContract.KubeSpanMultidocConfig() { //nolint:staticcheck // using legacy NetworkConfig for older Talos versions machine.MachineNetwork = &v1alpha1.NetworkConfig{} //nolint:staticcheck // using legacy NetworkConfig for older Talos versions } if in.Options.VersionContract.StableHostnameEnabled() && !in.Options.VersionContract.MultidocNetworkConfigSupported() { machine.MachineFeatures.StableHostname = new(true) //nolint:staticcheck // using legacy field for older Talos versions } if in.Options.VersionContract.MultidocNetworkConfigSupported() { hostnameConfig := network.NewHostnameConfigV1Alpha1() hostnameConfig.ConfigAuto = new(nethelpers.AutoHostnameKindStable) documents = append(documents, hostnameConfig) } if kubeSpanEnabled, isSet := in.Options.KubeSpanEnabled.Get(); isSet { if in.Options.VersionContract.KubeSpanMultidocConfig() { kubeSpanConfig := network.NewKubeSpanV1Alpha1() kubeSpanConfig.ConfigEnabled = new(kubeSpanEnabled) documents = append(documents, kubeSpanConfig) } else { // for older Talos versions, set KubeSpan config in machine.network.kubespan machine.MachineNetwork.NetworkKubeSpan = &v1alpha1.NetworkKubeSpan{ //nolint:staticcheck // using legacy NetworkKubeSpan for older Talos versions KubeSpanEnabled: new(kubeSpanEnabled), } } } return documents, nil } ================================================ FILE: pkg/machinery/config/generate/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "maps" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/role" ) // Option controls generate options specific to input generation. type Option func(o *Options) error // WithEndpointList specifies endpoints to use when accessing Talos cluster. func WithEndpointList(endpoints []string) Option { return func(o *Options) error { o.EndpointList = endpoints return nil } } // WithLocalAPIServerPort specifies the local API server port for the cluster. func WithLocalAPIServerPort(port int) Option { return func(o *Options) error { o.LocalAPIServerPort = port return nil } } // WithKubePrismPort specifies the KubePrism port. // // If 0, load balancer is disabled. // If not set, defaults to enabled with Talos 1.6+. func WithKubePrismPort(port int) Option { return func(o *Options) error { o.KubePrismPort = optional.Some(port) return nil } } // WithInstallDisk specifies install disk to use in Talos cluster. func WithInstallDisk(disk string) Option { return func(o *Options) error { o.InstallDisk = disk return nil } } // WithAdditionalSubjectAltNames specifies additional SANs. func WithAdditionalSubjectAltNames(sans []string) Option { return func(o *Options) error { o.AdditionalSubjectAltNames = append(o.AdditionalSubjectAltNames, sans...) return nil } } // WithInstallImage specifies install container image to use in Talos cluster. func WithInstallImage(imageRef string) Option { return func(o *Options) error { o.InstallImage = imageRef return nil } } // WithInstallExtraKernelArgs specifies extra kernel arguments to pass to the installer. func WithInstallExtraKernelArgs(args []string) Option { return func(o *Options) error { o.InstallExtraKernelArgs = append(o.InstallExtraKernelArgs, args...) return nil } } // WithNetworkOptions adds network config generation option. func WithNetworkOptions(opts ...v1alpha1.NetworkConfigOption) Option { return func(o *Options) error { o.NetworkConfigOptions = append(o.NetworkConfigOptions, opts...) return nil } } // WithRegistryMirror configures registry mirror endpoint(s). func WithRegistryMirror(host string, endpoints ...string) Option { return func(o *Options) error { if o.RegistryEndpoints == nil { o.RegistryEndpoints = make(map[string][]string) } o.RegistryEndpoints[host] = append(o.RegistryEndpoints[host], endpoints...) return nil } } // WithRegistryCACert specifies the certificate of the certificate authority which signed certificate of the registry. func WithRegistryCACert(host, cacert string) Option { return func(o *Options) error { if o.RegistryCACerts == nil { o.RegistryCACerts = make(map[string]string) } o.RegistryCACerts[host] = cacert return nil } } // WithRegistryInsecureSkipVerify marks registry host to skip TLS verification. func WithRegistryInsecureSkipVerify(host string) Option { return func(o *Options) error { if o.RegistryInsecure == nil { o.RegistryInsecure = make(map[string]bool) } o.RegistryInsecure[host] = true return nil } } // WithDNSDomain specifies domain name to use in Talos cluster. func WithDNSDomain(dnsDomain string) Option { return func(o *Options) error { o.DNSDomain = dnsDomain return nil } } // WithDebug enables verbose logging to console for all services. func WithDebug(enable bool) Option { return func(o *Options) error { o.Debug = enable return nil } } // WithClusterCNIConfig specifies custom cluster CNI config. func WithClusterCNIConfig(config *v1alpha1.CNIConfig) Option { return func(o *Options) error { o.CNIConfig = config return nil } } // WithUserDisks generates user partitions config. // // Deprecated: use block.UserVolumeConfig instead. func WithUserDisks(disks []*v1alpha1.MachineDisk) Option { return func(o *Options) error { o.MachineDisks = disks return nil } } // WithAllowSchedulingOnControlPlanes specifies AllowSchedulingOnControlPlane flag. func WithAllowSchedulingOnControlPlanes(enabled bool) Option { return func(o *Options) error { o.AllowSchedulingOnControlPlanes = enabled return nil } } // WithVersionContract specifies version contract to use when generating. func WithVersionContract(versionContract *config.VersionContract) Option { return func(o *Options) error { o.VersionContract = versionContract return nil } } // WithRoles specifies user roles. func WithRoles(roles role.Set) Option { return func(o *Options) error { o.Roles = roles return nil } } // WithClusterDiscovery enables cluster discovery feature. func WithClusterDiscovery(enabled bool) Option { return func(o *Options) error { o.DiscoveryEnabled = new(enabled) return nil } } // WithSysctls merges list of sysctls with new values. func WithSysctls(params map[string]string) Option { return func(o *Options) error { if o.Sysctls == nil { o.Sysctls = make(map[string]string) } maps.Copy(o.Sysctls, params) return nil } } // WithSecretsBundle specifies custom secrets bundle. func WithSecretsBundle(bundle *secrets.Bundle) Option { return func(o *Options) error { o.SecretsBundle = bundle return nil } } // WithHostDNSForwardKubeDNSToHost specifies whether to forward kube-dns to host. func WithHostDNSForwardKubeDNSToHost(forward bool) Option { return func(o *Options) error { o.HostDNSForwardKubeDNSToHost = optional.Some(forward) return nil } } // WithKubeSpanEnabled specifies whether KubeSpan is enabled. func WithKubeSpanEnabled(enabled bool) Option { return func(o *Options) error { o.KubeSpanEnabled = optional.Some(enabled) return nil } } // Options describes generate parameters. type Options struct { VersionContract *config.VersionContract // Custom secrets bundle. SecretsBundle *secrets.Bundle // Base settings. Debug bool // Machine settings: install. InstallDisk string InstallImage string InstallExtraKernelArgs []string // Machine disks. MachineDisks []*v1alpha1.MachineDisk // Machine network settings. NetworkConfigOptions []v1alpha1.NetworkConfigOption // Machine sysctls. Sysctls map[string]string // Machine registries. RegistryEndpoints map[string][]string RegistryCACerts map[string]string RegistryInsecure map[string]bool // Cluster settings. DNSDomain string CNIConfig *v1alpha1.CNIConfig AllowSchedulingOnControlPlanes bool LocalAPIServerPort int AdditionalSubjectAltNames []string DiscoveryEnabled *bool KubePrismPort optional.Optional[int] HostDNSForwardKubeDNSToHost optional.Optional[bool] KubeSpanEnabled optional.Optional[bool] // Client options. Roles role.Set EndpointList []string } // DefaultOptions returns default options. func DefaultOptions() Options { return Options{ DNSDomain: "cluster.local", Roles: role.MakeSet(role.Admin), } } ================================================ FILE: pkg/machinery/config/generate/registry.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "fmt" "net/url" "slices" "strings" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) //nolint:gocyclo,cyclop func (in *Input) generateRegistryConfigs(machine *v1alpha1.MachineConfig) ([]config.Document, error) { if !in.Options.VersionContract.MultidocNetworkConfigSupported() { // old-style registry config machine.MachineRegistries = v1alpha1.RegistriesConfig{ //nolint:staticcheck // backwards compatibility RegistryMirrors: map[string]*v1alpha1.RegistryMirrorConfig{}, RegistryConfig: map[string]*v1alpha1.RegistryConfig{}, } if in.Options.VersionContract.KubernetesAlternateImageRegistries() { if _, ok := machine.MachineRegistries.RegistryMirrors["k8s.gcr.io"]; !ok { //nolint:staticcheck // backwards compatibility, Talos v1.1->1.2 machine.MachineRegistries.RegistryMirrors["k8s.gcr.io"] = &v1alpha1.RegistryMirrorConfig{ //nolint:staticcheck // backwards compatibility, Talos v1.1->1.2 MirrorEndpoints: []string{ "https://registry.k8s.io", "https://k8s.gcr.io", }, } } } for host, endpoints := range in.Options.RegistryEndpoints { machine.MachineRegistries.RegistryMirrors[host] = &v1alpha1.RegistryMirrorConfig{ //nolint:staticcheck // backwards compatibility MirrorEndpoints: endpoints, } } for host, cacert := range in.Options.RegistryCACerts { if _, ok := machine.MachineRegistries.RegistryConfig[host]; !ok { //nolint:staticcheck // backwards compatibility machine.MachineRegistries.RegistryConfig[host] = &v1alpha1.RegistryConfig{} //nolint:staticcheck // backwards compatibility } if machine.MachineRegistries.RegistryConfig[host].RegistryTLS == nil { //nolint:staticcheck // backwards compatibility machine.MachineRegistries.RegistryConfig[host].RegistryTLS = &v1alpha1.RegistryTLSConfig{} //nolint:staticcheck // backwards compatibility } machine.MachineRegistries.RegistryConfig[host].RegistryTLS.TLSCA = v1alpha1.Base64Bytes(cacert) //nolint:staticcheck // backwards compatibility } for host := range in.Options.RegistryInsecure { if _, ok := machine.MachineRegistries.RegistryConfig[host]; !ok { //nolint:staticcheck // backwards compatibility machine.MachineRegistries.RegistryConfig[host] = &v1alpha1.RegistryConfig{} //nolint:staticcheck // backwards compatibility } if machine.MachineRegistries.RegistryConfig[host].RegistryTLS == nil { //nolint:staticcheck // backwards compatibility machine.MachineRegistries.RegistryConfig[host].RegistryTLS = &v1alpha1.RegistryTLSConfig{} //nolint:staticcheck // backwards compatibility } machine.MachineRegistries.RegistryConfig[host].RegistryTLS.TLSInsecureSkipVerify = new(true) //nolint:staticcheck // backwards compatibility } return nil, nil } documents := make([]config.Document, 0, len(in.Options.RegistryEndpoints)) // use new-style registry config via separate documents for host, endpoints := range in.Options.RegistryEndpoints { registryMirrorConfig := cri.NewRegistryMirrorConfigV1Alpha1(host) registryMirrorConfig.RegistryEndpoints = make([]cri.RegistryEndpoint, 0, len(endpoints)) for _, ep := range endpoints { u, err := url.Parse(ep) if err != nil { return nil, fmt.Errorf("failed to parse registry mirror endpoint %q: %w", ep, err) } registryMirrorConfig.RegistryEndpoints = append(registryMirrorConfig.RegistryEndpoints, cri.RegistryEndpoint{ EndpointURL: meta.URL{URL: u}, }) } documents = append(documents, registryMirrorConfig) } tlsConfigs := make(map[string]*cri.RegistryTLSConfigV1Alpha1) for host, cacert := range in.Options.RegistryCACerts { if _, ok := tlsConfigs[host]; !ok { tlsConfigs[host] = cri.NewRegistryTLSConfigV1Alpha1(host) } tlsConfigs[host].TLSCA = cacert } for host := range in.Options.RegistryInsecure { if _, ok := tlsConfigs[host]; !ok { tlsConfigs[host] = cri.NewRegistryTLSConfigV1Alpha1(host) } tlsConfigs[host].TLSInsecureSkipVerify = new(true) } for _, tlsConfig := range tlsConfigs { documents = append(documents, tlsConfig) } // sort the TLS config and registry mirrors docs alphabetically by the name slices.SortStableFunc(documents, func(a, b config.Document) int { na, aok := a.(config.NamedDocument) nb, bok := b.(config.NamedDocument) if c := strings.Compare(a.Kind(), b.Kind()); c != 0 { return c } if aok && bok { return strings.Compare(na.Name(), nb.Name()) } return 0 }) return documents, nil } ================================================ FILE: pkg/machinery/config/generate/secrets/bundle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "encoding/base64" "errors" "fmt" "os" "path/filepath" "time" "github.com/hashicorp/go-multierror" "github.com/siderolabs/crypto/x509" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/cis" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/role" ) // NewBundle creates secrets bundle generating all secrets. func NewBundle(clock Clock, versionContract *config.VersionContract) (*Bundle, error) { bundle := &Bundle{ Clock: clock, } err := bundle.populate(versionContract) if err != nil { return nil, err } return bundle, nil } // LoadBundle loads secrets bundle from the given file. func LoadBundle(path string) (*Bundle, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() //nolint: errcheck bundle := &Bundle{ Clock: NewClock(), } decoder := yaml.NewDecoder(f) if err = decoder.Decode(&bundle); err != nil { return nil, err } return bundle, nil } // NewBundleFromKubernetesPKI creates secrets bundle by reading the contents // of a Kubernetes PKI directory (typically `/etc/kubernetes/pki`) and using the provided bootstrapToken as input. // //nolint:gocyclo func NewBundleFromKubernetesPKI(pkiDir, bootstrapToken string, versionContract *config.VersionContract) (*Bundle, error) { dirStat, err := os.Stat(pkiDir) if err != nil { return nil, err } if !dirStat.IsDir() { return nil, fmt.Errorf("%q is not a directory", pkiDir) } var ( ca *x509.PEMEncodedCertificateAndKey etcdCA *x509.PEMEncodedCertificateAndKey aggregatorCA *x509.PEMEncodedCertificateAndKey sa *x509.PEMEncodedKey ) ca, err = x509.NewCertificateAndKeyFromFiles(filepath.Join(pkiDir, "ca.crt"), filepath.Join(pkiDir, "ca.key")) if err != nil { return nil, err } err = validatePEMEncodedCertificateAndKey(ca) if err != nil { return nil, err } etcdDir := filepath.Join(pkiDir, "etcd") etcdCA, err = x509.NewCertificateAndKeyFromFiles(filepath.Join(etcdDir, "ca.crt"), filepath.Join(etcdDir, "ca.key")) if err != nil { return nil, err } err = validatePEMEncodedCertificateAndKey(etcdCA) if err != nil { return nil, err } aggregatorCACrtPath := filepath.Join(pkiDir, "front-proxy-ca.crt") aggregatorCA, err = x509.NewCertificateAndKeyFromFiles(aggregatorCACrtPath, filepath.Join(pkiDir, "front-proxy-ca.key")) if err != nil { return nil, err } err = validatePEMEncodedCertificateAndKey(aggregatorCA) if err != nil { return nil, err } saKeyPath := filepath.Join(pkiDir, "sa.key") var saBytes []byte saBytes, err = os.ReadFile(saKeyPath) if err != nil { return nil, err } sa = &x509.PEMEncodedKey{ Key: saBytes, } _, err = sa.GetKey() if err != nil { return nil, err } bundle := &Bundle{ Secrets: &Secrets{ BootstrapToken: bootstrapToken, }, Certs: &Certs{ Etcd: etcdCA, K8s: ca, K8sAggregator: aggregatorCA, K8sServiceAccount: sa, }, } err = bundle.populate(versionContract) if err != nil { return nil, err } return bundle, nil } // NewBundleFromConfig creates secrets bundle using existing config. func NewBundleFromConfig(clock Clock, c config.Config) *Bundle { certs := &Certs{ K8s: c.Cluster().IssuingCA(), K8sAggregator: c.Cluster().AggregatorCA(), K8sServiceAccount: c.Cluster().ServiceAccount(), Etcd: c.Cluster().Etcd().CA(), OS: c.Machine().Security().IssuingCA(), } cluster := &Cluster{ ID: c.Cluster().ID(), Secret: c.Cluster().Secret(), } trustd := &TrustdInfo{ Token: c.Machine().Security().Token(), } bootstrapToken := fmt.Sprintf( "%s.%s", c.Cluster().Token().ID(), c.Cluster().Token().Secret(), ) secrets := &Secrets{ AESCBCEncryptionSecret: c.Cluster().AESCBCEncryptionSecret(), SecretboxEncryptionSecret: c.Cluster().SecretboxEncryptionSecret(), BootstrapToken: bootstrapToken, } return &Bundle{ Clock: clock, Cluster: cluster, Secrets: secrets, TrustdInfo: trustd, Certs: certs, } } // populate fills all the missing fields in the secrets bundle. // //nolint:gocyclo,cyclop func (bundle *Bundle) populate(versionContract *config.VersionContract) error { if bundle.Clock == nil { bundle.Clock = NewClock() } if bundle.Certs == nil { bundle.Certs = &Certs{} } if bundle.Certs.Etcd == nil { etcd, err := NewEtcdCA(bundle.Clock.Now(), versionContract) if err != nil { return err } bundle.Certs.Etcd = &x509.PEMEncodedCertificateAndKey{ Crt: etcd.CrtPEM, Key: etcd.KeyPEM, } } if bundle.Certs.K8s == nil { kubernetesCA, err := NewKubernetesCA(bundle.Clock.Now(), versionContract) if err != nil { return err } bundle.Certs.K8s = &x509.PEMEncodedCertificateAndKey{ Crt: kubernetesCA.CrtPEM, Key: kubernetesCA.KeyPEM, } } if bundle.Certs.K8sAggregator == nil { aggregatorCA, err := NewAggregatorCA(bundle.Clock.Now(), versionContract) if err != nil { return err } bundle.Certs.K8sAggregator = &x509.PEMEncodedCertificateAndKey{ Crt: aggregatorCA.CrtPEM, Key: aggregatorCA.KeyPEM, } } if bundle.Certs.K8sServiceAccount == nil { if versionContract.UseRSAServiceAccountKey() { serviceAccount, err := x509.NewRSAKey() if err != nil { return err } bundle.Certs.K8sServiceAccount = &x509.PEMEncodedKey{ Key: serviceAccount.KeyPEM, } } else { serviceAccount, err := x509.NewECDSAKey() if err != nil { return err } bundle.Certs.K8sServiceAccount = &x509.PEMEncodedKey{ Key: serviceAccount.KeyPEM, } } } if bundle.Certs.OS == nil { talosCA, err := NewTalosCA(bundle.Clock.Now()) if err != nil { return err } bundle.Certs.OS = &x509.PEMEncodedCertificateAndKey{ Crt: talosCA.CrtPEM, Key: talosCA.KeyPEM, } } if bundle.Secrets == nil { bundle.Secrets = &Secrets{} } if bundle.Secrets.BootstrapToken == "" { token, err := genToken(6, 16) if err != nil { return err } bundle.Secrets.BootstrapToken = token } if versionContract.Greater(config.TalosVersion1_2) { if bundle.Secrets.SecretboxEncryptionSecret == "" { secretboxEncryptionSecret, err := cis.CreateEncryptionToken() if err != nil { return err } bundle.Secrets.SecretboxEncryptionSecret = secretboxEncryptionSecret } } else { if bundle.Secrets.AESCBCEncryptionSecret == "" { aesCBCEncryptionSecret, err := cis.CreateEncryptionToken() if err != nil { return err } bundle.Secrets.AESCBCEncryptionSecret = aesCBCEncryptionSecret } } if bundle.TrustdInfo == nil { bundle.TrustdInfo = &TrustdInfo{} } if bundle.TrustdInfo.Token == "" { token, err := genToken(6, 16) if err != nil { return err } bundle.TrustdInfo.Token = token } if bundle.Cluster == nil { bundle.Cluster = &Cluster{} } if bundle.Cluster.ID == "" { clusterID, err := randBytes(constants.DefaultClusterIDSize) if err != nil { return fmt.Errorf("failed to generate cluster ID: %w", err) } bundle.Cluster.ID = base64.URLEncoding.EncodeToString(clusterID) } if bundle.Cluster.Secret == "" { clusterSecret, err := randBytes(constants.DefaultClusterSecretSize) if err != nil { return fmt.Errorf("failed to generate cluster secret: %w", err) } bundle.Cluster.Secret = base64.StdEncoding.EncodeToString(clusterSecret) } return nil } // GenerateTalosAPIClientCertificate generates the admin certificate. func (bundle *Bundle) GenerateTalosAPIClientCertificate(roles role.Set) (*x509.PEMEncodedCertificateAndKey, error) { return bundle.GenerateTalosAPIClientCertificateWithTTL(roles, constants.TalosAPIDefaultCertificateValidityDuration) } // GenerateTalosAPIClientCertificateWithTTL generates the admin certificate with specified TTL. func (bundle *Bundle) GenerateTalosAPIClientCertificateWithTTL(roles role.Set, crtTTL time.Duration) (*x509.PEMEncodedCertificateAndKey, error) { return NewAdminCertificateAndKey( bundle.Clock.Now(), bundle.Certs.OS, roles, crtTTL, ) } // Validate the bundle. // //nolint:gocyclo,cyclop func (bundle *Bundle) Validate() error { var multiErr error if bundle.Cluster == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("cluster is required")) } else { if bundle.Cluster.ID == "" { multiErr = multierror.Append(multiErr, fmt.Errorf("cluster.id is required")) } if bundle.Cluster.Secret == "" { multiErr = multierror.Append(multiErr, fmt.Errorf("cluster.secret is required")) } } if bundle.Secrets == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("secrets is required")) } else { if bundle.Secrets.BootstrapToken == "" { multiErr = multierror.Append(multiErr, fmt.Errorf("secrets.bootstraptoken is required")) } if bundle.Secrets.AESCBCEncryptionSecret == "" && bundle.Secrets.SecretboxEncryptionSecret == "" { multiErr = multierror.Append(multiErr, fmt.Errorf("one of [secrets.secretboxencryptionsecret, secrets.aescbcencryptionsecret] is required")) } if bundle.Secrets.AESCBCEncryptionSecret != "" && bundle.Secrets.SecretboxEncryptionSecret != "" { multiErr = multierror.Append(multiErr, fmt.Errorf("only one of [secrets.secretboxencryptionsecret, secrets.aescbcencryptionsecret] is allowed")) } } if bundle.TrustdInfo == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("trustdinfo is required")) } else if bundle.TrustdInfo.Token == "" { multiErr = multierror.Append(multiErr, fmt.Errorf("trustdinfo.token is required")) } if err := bundle.validateCerts(); err != nil { multiErr = multierror.Append(multiErr, err) } return multiErr } //nolint:gocyclo,cyclop func (bundle *Bundle) validateCerts() error { if bundle.Certs == nil { return errors.New("certs is required") } var multiErr error if bundle.Certs.Etcd == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.etcd is required")) } else if err := validatePEMEncodedCertificateAndKey(bundle.Certs.Etcd); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.etcd is invalid: %w", err)) } if bundle.Certs.K8s == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8s is required")) } else if err := validatePEMEncodedCertificateAndKey(bundle.Certs.K8s); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8s is invalid: %w", err)) } if bundle.Certs.K8sAggregator == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8saggregator is required")) } else if err := validatePEMEncodedCertificateAndKey(bundle.Certs.K8sAggregator); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8saggregator is invalid: %w", err)) } if bundle.Certs.OS == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.os is required")) } else if err := validatePEMEncodedCertificateAndKey(bundle.Certs.OS); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.os is invalid: %w", err)) } if bundle.Certs.K8sServiceAccount == nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8sserviceaccount is required")) } else if _, err := bundle.Certs.K8sServiceAccount.GetKey(); err != nil { multiErr = multierror.Append(multiErr, fmt.Errorf("certs.k8sserviceaccount.key is invalid: %w", err)) } return multiErr } ================================================ FILE: pkg/machinery/config/generate/secrets/ca.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( stdx509 "crypto/x509" "time" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/role" ) // NewEtcdCA generates a CA for the Etcd PKI. func NewEtcdCA(currentTime time.Time, contract *config.VersionContract) (ca *x509.CertificateAuthority, err error) { opts := []x509.Option{ x509.Organization("etcd"), x509.NotAfter(currentTime.Add(CAValidityTime)), x509.NotBefore(currentTime), x509.ECDSA(true), } return x509.NewSelfSignedCertificateAuthority(opts...) } // NewKubernetesCA generates a CA for the Kubernetes PKI. func NewKubernetesCA(currentTime time.Time, contract *config.VersionContract) (ca *x509.CertificateAuthority, err error) { opts := []x509.Option{ x509.Organization("kubernetes"), x509.NotAfter(currentTime.Add(CAValidityTime)), x509.NotBefore(currentTime), x509.ECDSA(true), } return x509.NewSelfSignedCertificateAuthority(opts...) } // NewAggregatorCA generates a CA for the Kubernetes aggregator/front-proxy. func NewAggregatorCA(currentTime time.Time, contract *config.VersionContract) (ca *x509.CertificateAuthority, err error) { opts := []x509.Option{ x509.ECDSA(true), x509.CommonName("front-proxy"), x509.NotAfter(currentTime.Add(CAValidityTime)), x509.NotBefore(currentTime), x509.ECDSA(true), } return x509.NewSelfSignedCertificateAuthority(opts...) } // NewTalosCA generates a CA for the Talos PKI. func NewTalosCA(currentTime time.Time) (ca *x509.CertificateAuthority, err error) { opts := []x509.Option{ x509.Organization("talos"), x509.NotAfter(currentTime.Add(CAValidityTime)), x509.NotBefore(currentTime), } return x509.NewSelfSignedCertificateAuthority(opts...) } // NewAdminCertificateAndKey generates the admin Talos certificate and key. func NewAdminCertificateAndKey(currentTime time.Time, ca *x509.PEMEncodedCertificateAndKey, roles role.Set, ttl time.Duration) (p *x509.PEMEncodedCertificateAndKey, err error) { opts := []x509.Option{ x509.Organization(roles.Strings()...), x509.NotAfter(currentTime.Add(ttl)), x509.NotBefore(currentTime), x509.KeyUsage(stdx509.KeyUsageDigitalSignature), x509.ExtKeyUsage([]stdx509.ExtKeyUsage{stdx509.ExtKeyUsageClientAuth}), } talosCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(ca) if err != nil { return nil, err } keyPair, err := x509.NewKeyPair(talosCA, opts...) if err != nil { return nil, err } return x509.NewCertificateAndKeyFromKeyPair(keyPair), nil } ================================================ FILE: pkg/machinery/config/generate/secrets/clock.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import "time" // Clock system clock. type Clock interface { Now() time.Time } // SystemClock is a real system clock, but the time returned can be made fixed. type SystemClock struct { fixedTime time.Time } // NewClock creates new SystemClock. func NewClock() *SystemClock { return &SystemClock{} } // NewFixedClock creates new SystemClock with fixed time. func NewFixedClock(t time.Time) *SystemClock { return &SystemClock{ fixedTime: t, } } // Now implements Clock. func (c *SystemClock) Now() time.Time { if c.fixedTime.IsZero() { return time.Now() } return c.fixedTime } ================================================ FILE: pkg/machinery/config/generate/secrets/generate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( stdx509 "crypto/x509" "testing" "time" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestNewBundle(t *testing.T) { t.Parallel() for _, test := range []struct { name string versionContract *config.VersionContract }{ { name: "v1.0", versionContract: config.TalosVersion1_0, }, { name: "v1.3", versionContract: config.TalosVersion1_3, }, { name: "v1.7", versionContract: config.TalosVersion1_7, }, { name: "current", versionContract: config.TalosVersionCurrent, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() _, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), test.versionContract) require.NoError(t, err) }) } } func TestNewBundleFromConfig(t *testing.T) { t.Parallel() bundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) require.NoError(t, err) osCA, err := x509.NewCertificateAuthorityFromCertificateAndKey(bundle.Certs.OS) require.NoError(t, err) assert.Equal(t, stdx509.Ed25519, osCA.Crt.PublicKeyAlgorithm, "expected Ed25519 signature algorithm") input, err := generate.NewInput("test", "https://localhost:6443", constants.DefaultKubernetesVersion, generate.WithSecretsBundle(bundle)) require.NoError(t, err) cfg, err := input.Config(machine.TypeControlPlane) require.NoError(t, err) bundle2 := secrets.NewBundleFromConfig(bundle.Clock, cfg) assert.Equal(t, bundle, bundle2) } ================================================ FILE: pkg/machinery/config/generate/secrets/marshal_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" ) func TestMarshalUnmarshal(t *testing.T) { t.Parallel() bundle, err := secrets.NewBundle(secrets.NewFixedClock(time.Now()), config.TalosVersionCurrent) require.NoError(t, err) dir := t.TempDir() path := filepath.Join(dir, "secrets.yaml") f, err := os.Create(path) require.NoError(t, err) require.NoError(t, yaml.NewEncoder(f).Encode(bundle)) require.NoError(t, f.Close()) bundle2, err := secrets.LoadBundle(path) require.NoError(t, err) bundle2.Clock = bundle.Clock assert.Equal(t, bundle, bundle2) } func TestUnmarshalStable(t *testing.T) { t.Parallel() bundle, err := secrets.LoadBundle("testdata/secrets.yaml") require.NoError(t, err) assert.NotNil(t, bundle.Certs) assert.NotNil(t, bundle.Certs.Etcd) assert.NotNil(t, bundle.Certs.K8s) assert.NotNil(t, bundle.Certs.K8sAggregator) assert.NotNil(t, bundle.Certs.K8sServiceAccount) assert.NotNil(t, bundle.Cluster) assert.NotEmpty(t, bundle.Cluster.ID) assert.NotEmpty(t, bundle.Cluster.Secret) assert.NotNil(t, bundle.Secrets) assert.NotEmpty(t, bundle.Secrets.BootstrapToken) assert.NotEmpty(t, bundle.Secrets.SecretboxEncryptionSecret) assert.NotNil(t, bundle.TrustdInfo) assert.NotEmpty(t, bundle.TrustdInfo.Token) } ================================================ FILE: pkg/machinery/config/generate/secrets/secrets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package secrets provides types and methods to handle base machine configuration secrets. package secrets import ( "time" "github.com/siderolabs/crypto/x509" ) // CAValidityTime is the default validity time for CA certificates. const CAValidityTime = 87600 * time.Hour // Bundle contains all cluster secrets required to generate machine configuration. // // NB: this structure is marhsalled/unmarshalled to/from JSON in various projects, so // we need to keep representation compatible. type Bundle struct { Clock Clock `yaml:"-" json:"-"` Cluster *Cluster `json:"Cluster"` Secrets *Secrets `json:"Secrets"` TrustdInfo *TrustdInfo `json:"TrustdInfo"` Certs *Certs `json:"Certs"` } // Certs holds the base64 encoded keys and certificates. type Certs struct { // Etcd is etcd CA certificate and key. Etcd *x509.PEMEncodedCertificateAndKey `json:"Etcd"` // K8s is Kubernetes CA certificate and key. K8s *x509.PEMEncodedCertificateAndKey `json:"K8s"` // K8sAggregator is Kubernetes aggregator CA certificate and key. K8sAggregator *x509.PEMEncodedCertificateAndKey `json:"K8sAggregator"` // K8sServiceAccount is Kubernetes service account key. K8sServiceAccount *x509.PEMEncodedKey `json:"K8sServiceAccount"` // OS is Talos API CA certificate and key. OS *x509.PEMEncodedCertificateAndKey `json:"OS"` } // Cluster holds Talos cluster-wide secrets. type Cluster struct { ID string `json:"Id"` Secret string `json:"Secret"` } // Secrets holds the sensitive kubeadm data. type Secrets struct { BootstrapToken string `json:"BootstrapToken"` AESCBCEncryptionSecret string `json:"AESCBCEncryptionSecret,omitempty" yaml:",omitempty"` SecretboxEncryptionSecret string `json:"SecretboxEncryptionSecret,omitempty" yaml:",omitempty"` } // TrustdInfo holds the trustd credentials. type TrustdInfo struct { Token string `json:"Token"` } ================================================ FILE: pkg/machinery/config/generate/secrets/testdata/invalid-secrets.yaml ================================================ cluster: id: cgvhCbucls-WqkESDTRqykFgb4hROI4cBpgB0YJLLtM= secrets: bootstraptoken: 2psnp2.lry8e9gycdix3bpa certs: etcd: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmRENDQVNPZ0F3SUJBZ0lRQ0tGUmRhOVIvY3dIQXo4U1ZHU3I5ekFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TURVeU5URXlNVFEwTWxvWERUTXpNRFV5TWpFeU1UUTBNbG93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkdjUEQ5S2xIa09WCkJhcTFKSXhPaE56UXpHNDgvUG1hRmNTaExZckR1WkZ2MkY3NWd2bmkrbDU5RENTR3h4S3VncnJiZlQzempjSUoKdWRQblN6VEI2eVdqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVb2ZLdXhqa0pyL3hSCkRwQmNKYmlaR3dTRVhKSXdDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdHZ2VzU2NmOUh0ZHRGdEVGVEJlK2pkRFgKTDZUT3UwazRaQ3lmelNtY0JGMENJSDFNeDRTUmx0TGRHaE43aitZNU00aTJzT2liZHBCN0xFOCtQRW9RM081eQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== k8s: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRTkEvQ2JmWWRJaE5wRXVaQlgwWEFWVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFV5TlRFeU1UUTBNbG9YRFRNek1EVXlNakV5TVRRMApNbG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCRUJHWDRoamtDbE5sSnBWNFJUZEVjSjJUcFVOTGlON2pQYnFzQkxIWFM2QWJBQWVDY2l0cHFLaWlUenQKaWdaTE5tdWlvL3ZOL1MxUkxaTTQ1TzFIVVBxallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVIS2NNVExRMWlCQloxL2Qvd0JBNjRsL0UrdW93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnSFgwZXBmd3gKaUxDSGxUeks5Y0pjSjgzOGlOU3VPSmFoZllNcFJXMU5nUkVDSVFDOG5EUm9IUFRKNVpNN3lPRWNycURoR3FVVAp3YWVoMXRFTS83aDYyNWxGaHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhteWM0SzhmN0doWlQwdkVOcUNPbDE0L3FPM0g1LytiaUM2ZU1aL3JkYzNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUUVaZmlHT1FLVTJVbWxYaEZOMFJ3blpPbFEwdUkzdU05dXF3RXNkZExvQnNBQjRKeUsybQpvcUtKUE8yS0JrczJhNktqKzgzOUxWRXRremprN1VkUStnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8sserviceaccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUY3b0ZsaXYrTVZ3Zzc4dUVla3JaMktsVS82ZVJ6RUw2OGUzTWNkWk82STZvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFbHROcm00eDNpQy9vMmVGTDZyWVJjcTVtbExjTHhFVW5NQVJkVnpXeFVpbUpwZHVYeGhuSQpXSXloeTAzenUyRC9qSWlOeVlFR3llMjFUSTFVRFpwNUdRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= os: key: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBaHQrSExpc1Flc2wyQVZRdDByVUpxakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qTXdOVEkxTVRJeE5EUXlXaGNOTXpNd05USXlNVEl4TkRReVdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUdFT3RQV3YvZlc5WVkyV21LVmJqcENKaStkbXhlWlBqUXpICjZvdHp0MWRYbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkxxU3UrREt1V1Jkbyt6NApMSDBjdG9mZUg4OUFNQVVHQXl0bGNBTkJBRnFabUp3ajZ6bTYrZm1SWldsaTBqQldVMElvckhxZGkrYXZzU3lxCkRPQlRPbVplYkFLQ2NqTEVMNGxoQWNhNk81ZVc1U0xVVVJxVUliUktwKzRjZ0FzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== ================================================ FILE: pkg/machinery/config/generate/secrets/testdata/secrets.yaml ================================================ cluster: id: cgvhCbucls-WqkESDTRqykFgb4hROI4cBpgB0YJLLtM= secret: Wiu0qx68V3MKhFDr8+OFwGCNOOPBiQCDsLpRa3MdxEQ= secrets: bootstraptoken: 2psnp2.lry8e9gycdix3bpa secretboxencryptionsecret: 6zWBpno849HlD5jeuTmGToFYP+z8lgkJa0fhHobv7mE= trustdinfo: token: oamejv.9tk1nhuwnlc2nghf certs: etcd: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmRENDQVNPZ0F3SUJBZ0lRQ0tGUmRhOVIvY3dIQXo4U1ZHU3I5ekFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TURVeU5URXlNVFEwTWxvWERUTXpNRFV5TWpFeU1UUTBNbG93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQkdjUEQ5S2xIa09WCkJhcTFKSXhPaE56UXpHNDgvUG1hRmNTaExZckR1WkZ2MkY3NWd2bmkrbDU5RENTR3h4S3VncnJiZlQzempjSUoKdWRQblN6VEI2eVdqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVb2ZLdXhqa0pyL3hSCkRwQmNKYmlaR3dTRVhKSXdDZ1lJS29aSXpqMEVBd0lEUndBd1JBSWdHZ2VzU2NmOUh0ZHRGdEVGVEJlK2pkRFgKTDZUT3UwazRaQ3lmelNtY0JGMENJSDFNeDRTUmx0TGRHaE43aitZNU00aTJzT2liZHBCN0xFOCtQRW9RM081eQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVLb0RIa3I4bnFHQmVsWkRYWkpoVVdySkUrNEx2L0ViZEFrbjZ3TnJ1QkNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFWnc4UDBxVWVRNVVGcXJVa2pFNkUzTkRNYmp6OCtab1Z4S0V0aXNPNWtXL1lYdm1DK2VMNgpYbjBNSkliSEVxNkN1dHQ5UGZPTndnbTUwK2RMTk1IckpRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8s: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRTkEvQ2JmWWRJaE5wRXVaQlgwWEFWVEFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNRFV5TlRFeU1UUTBNbG9YRFRNek1EVXlNakV5TVRRMApNbG93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCRUJHWDRoamtDbE5sSnBWNFJUZEVjSjJUcFVOTGlON2pQYnFzQkxIWFM2QWJBQWVDY2l0cHFLaWlUenQKaWdaTE5tdWlvL3ZOL1MxUkxaTTQ1TzFIVVBxallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVVIS2NNVExRMWlCQloxL2Qvd0JBNjRsL0UrdW93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnSFgwZXBmd3gKaUxDSGxUeks5Y0pjSjgzOGlOU3VPSmFoZllNcFJXMU5nUkVDSVFDOG5EUm9IUFRKNVpNN3lPRWNycURoR3FVVAp3YWVoMXRFTS83aDYyNWxGaHc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUhteWM0SzhmN0doWlQwdkVOcUNPbDE0L3FPM0g1LytiaUM2ZU1aL3JkYzNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUUVaZmlHT1FLVTJVbWxYaEZOMFJ3blpPbFEwdUkzdU05dXF3RXNkZExvQnNBQjRKeUsybQpvcUtKUE8yS0JrczJhNktqKzgzOUxWRXRremprN1VkUStnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8saggregator: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZRENDQVFXZ0F3SUJBZ0lRRnMwdklHTVN6MXA1TnFkdnpOY3k2VEFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1EVXlOVEV5TVRRME1sb1hEVE16TURVeU1qRXlNVFEwTWxvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCSnhZUXlKWUR1dkwvcEYwT1phbHRYc2NoQk9Oc3F3SmJUTW1HN2FMZ1NjNjZKU05yT3dmCkZrSCtSdmdiNFhoOUJiTHpsMEhaWDNTVFAzYStkancvYUJpallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV5cFJHdXVtK0lPM3pCbHJITk5KT2x2ZlhiTnd3Q2dZSUtvWkl6ajBFQXdJRFNRQXdSZ0loCkFKUWVERjhydk8yS2J4QWRlUHF0NmxoOTdDNjlDdzcvUitkY0hUZ1ZtWUFMQWlFQXJ0Yjg0TkxGejlXSzdqa3IKQ2FzR3lrSEdPUHY1THFhUFhXVUJCY3Z4MTc0PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUZxQnZKQytUVWtRUTZxTTJYRU9leFE5Nklsc1VkU0NBaC9MZmJndWhzUnJvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFbkZoRElsZ082OHYra1hRNWxxVzFleHlFRTQyeXJBbHRNeVlidG91Qkp6cm9sSTJzN0I4VwpRZjVHK0J2aGVIMEZzdk9YUWRsZmRKTS9kcjUyUEQ5b0dBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8sserviceaccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUY3b0ZsaXYrTVZ3Zzc4dUVla3JaMktsVS82ZVJ6RUw2OGUzTWNkWk82STZvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFbHROcm00eDNpQy9vMmVGTDZyWVJjcTVtbExjTHhFVW5NQVJkVnpXeFVpbUpwZHVYeGhuSQpXSXloeTAzenUyRC9qSWlOeVlFR3llMjFUSTFVRFpwNUdRPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= os: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBaHQrSExpc1Flc2wyQVZRdDByVUpxakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qTXdOVEkxTVRJeE5EUXlXaGNOTXpNd05USXlNVEl4TkRReVdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUdFT3RQV3YvZlc5WVkyV21LVmJqcENKaStkbXhlWlBqUXpICjZvdHp0MWRYbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkxxU3UrREt1V1Jkbyt6NApMSDBjdG9mZUg4OUFNQVVHQXl0bGNBTkJBRnFabUp3ajZ6bTYrZm1SWldsaTBqQldVMElvckhxZGkrYXZzU3lxCkRPQlRPbVplYkFLQ2NqTEVMNGxoQWNhNk81ZVc1U0xVVVJxVUliUktwKzRjZ0FzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJT0VTT0dBNjE1RktsTEJDWlVsZlhyVUFmQ3NZTFY1dE0wN0pvbjU0d1RDSwotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K ================================================ FILE: pkg/machinery/config/generate/secrets/utils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "bufio" "crypto/rand" "fmt" "io" "github.com/siderolabs/crypto/x509" ) func randBytes(size int) ([]byte, error) { buf := make([]byte, size) n, err := io.ReadFull(rand.Reader, buf) if err != nil { return nil, fmt.Errorf("failed to read from random generator: %w", err) } if n != size { return nil, fmt.Errorf("failed to generate sufficient number of random bytes (%d != %d)", n, size) } return buf, nil } func validatePEMEncodedCertificateAndKey(certs *x509.PEMEncodedCertificateAndKey) error { _, err := certs.GetKey() if err != nil { return err } _, err = certs.GetCert() return err } // randBootstrapTokenString returns a random string consisting of the characters in // validBootstrapTokenChars, with the length customized by the parameter. func randBootstrapTokenString(length int) (string, error) { // validBootstrapTokenChars defines the characters a bootstrap token can consist of const validBootstrapTokenChars = "0123456789abcdefghijklmnopqrstuvwxyz" // len("0123456789abcdefghijklmnopqrstuvwxyz") = 36 which doesn't evenly divide // the possible values of a byte: 256 mod 36 = 4. Discard any random bytes we // read that are >= 252 so the bytes we evenly divide the character set. const maxByteValue = 252 var ( b byte err error token = make([]byte, length) ) reader := bufio.NewReaderSize(rand.Reader, length*2) for i := range token { for { if b, err = reader.ReadByte(); err != nil { return "", err } if b < maxByteValue { break } } token[i] = validBootstrapTokenChars[int(b)%len(validBootstrapTokenChars)] } return string(token), err } // genToken will generate a token of the format abc.123 (like kubeadm/trustd), where the length of the first string (before the dot) // and length of the second string (after dot) are specified as inputs. func genToken(lenFirst, lenSecond int) (string, error) { var err error tokenTemp := make([]string, 2) tokenTemp[0], err = randBootstrapTokenString(lenFirst) if err != nil { return "", err } tokenTemp[1], err = randBootstrapTokenString(lenSecond) if err != nil { return "", err } return tokenTemp[0] + "." + tokenTemp[1], nil } ================================================ FILE: pkg/machinery/config/generate/secrets/validate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( _ "embed" "testing" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" ) var ( //go:embed testdata/invalid-secrets.yaml invalidSecrets []byte //go:embed testdata/secrets.yaml validSecrets []byte ) func TestValidate(t *testing.T) { t.Parallel() var bundle secrets.Bundle require.NoError(t, yaml.Unmarshal(validSecrets, &bundle)) require.NoError(t, bundle.Validate()) var invalidBundle secrets.Bundle require.NoError(t, yaml.Unmarshal(invalidSecrets, &invalidBundle)) require.EqualError(t, invalidBundle.Validate(), `6 errors occurred: * cluster.secret is required * one of [secrets.secretboxencryptionsecret, secrets.aescbcencryptionsecret] is required * trustdinfo is required * certs.etcd is invalid: failed to parse PEM block * certs.k8saggregator is required * certs.os is invalid: unsupported key type: "CERTIFICATE" `) } ================================================ FILE: pkg/machinery/config/generate/stdpatches/stdpatches.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package stdpatches contains standard patches applied to Talos machine configurations. package stdpatches import ( "fmt" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/security" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // WithStaticHostname returns a patch that sets a static hostname in the machine configuration. func WithStaticHostname(versionContract *config.VersionContract, hostname string) ([]byte, error) { if versionContract.MultidocNetworkConfigSupported() { hostnameConfig := network.NewHostnameConfigV1Alpha1() hostnameConfig.ConfigAuto = new(nethelpers.AutoHostnameKindOff) hostnameConfig.ConfigHostname = hostname return patchFromDocument(hostnameConfig) } return patchFromV1Alpha1(map[string]any{ "machine": map[string]any{ "network": map[string]any{ "hostname": hostname, }, }, }) } // WithTrustedRoots returns a patch that sets trusted roots in the machine configuration. func WithTrustedRoots(versionContract *config.VersionContract, trustedRoots string) ([]byte, error) { if versionContract.MultidocNetworkConfigSupported() { trustedRootsConfig := security.NewTrustedRootsConfigV1Alpha1() trustedRootsConfig.Certificates = trustedRoots return patchFromDocument(trustedRootsConfig) } return nil, fmt.Errorf("trusted roots patch is not supported for version contract %s", versionContract.String()) } func patchFromDocument(doc configconfig.Document) ([]byte, error) { ctr, err := container.New(doc) if err != nil { return nil, err } return ctr.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) } func patchFromV1Alpha1(doc any) ([]byte, error) { return yaml.Marshal(doc) } ================================================ FILE: pkg/machinery/config/generate/stdpatches/stdpatches_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package stdpatches_test import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/stdpatches" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) func TestPatches(t *testing.T) { t.Parallel() for _, test := range []struct { name string patch func(*config.VersionContract) ([]byte, error) versionContracts []*config.VersionContract kubernetesVersion string assertion func(t *testing.T, cfg config.Config) }{ { name: "WithStaticHostname", patch: func(vc *config.VersionContract) ([]byte, error) { return stdpatches.WithStaticHostname(vc, "hostname-1") }, versionContracts: []*config.VersionContract{ config.TalosVersion1_11, config.TalosVersion1_12, }, kubernetesVersion: "1.34.0", assertion: func(t *testing.T, cfg config.Config) { assert.Equal(t, "hostname-1", cfg.NetworkHostnameConfig().Hostname()) }, }, { name: "WithTrustedRoots", patch: func(vc *config.VersionContract) ([]byte, error) { return stdpatches.WithTrustedRoots(vc, "trusted-roots-1") }, versionContracts: []*config.VersionContract{ config.TalosVersion1_12, }, kubernetesVersion: "1.34.0", assertion: func(t *testing.T, cfg config.Config) { assert.Len(t, cfg.TrustedRoots().ExtraTrustedRootCertificates(), 1) }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() for _, vc := range test.versionContracts { t.Run(vc.String(), func(t *testing.T) { t.Parallel() in, err := generate.NewInput( strings.ToLower(test.name), "https://127.0.0.1/", test.kubernetesVersion, generate.WithVersionContract(vc), ) require.NoError(t, err) cfg, err := in.Config(machine.TypeControlPlane) require.NoError(t, err) bytesPatch, err := test.patch(vc) require.NoError(t, err) patch, err := configpatcher.LoadPatch(bytesPatch) require.NoError(t, err) patched, err := configpatcher.Apply(configpatcher.WithConfig(cfg), []configpatcher.Patch{patch}) require.NoError(t, err) patchedCfg, err := patched.Config() require.NoError(t, err) _, err = patchedCfg.Validate(mockValidationMode{}, validation.WithLocal()) require.NoError(t, err) test.assertion(t, patchedCfg) }) } }) } } type mockValidationMode struct{} func (mockValidationMode) String() string { return "mock" } func (mockValidationMode) RequiresInstall() bool { return false } func (mockValidationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/generate/talosconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // Talosconfig returns the talos admin Talos config. func (in *Input) Talosconfig() (*clientconfig.Config, error) { clientcert, err := in.Options.SecretsBundle.GenerateTalosAPIClientCertificate(in.Options.Roles) if err != nil { return nil, err } return clientconfig.NewConfig(in.ClusterName, in.Options.EndpointList, in.Options.SecretsBundle.Certs.OS.Crt, clientcert), nil } ================================================ FILE: pkg/machinery/config/generate/worker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package generate import ( "fmt" "net/url" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" v1alpha1 "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/constants" ) //nolint:gocyclo,cyclop func (in *Input) worker() ([]config.Document, error) { v1alpha1Config := &v1alpha1.Config{ ConfigVersion: "v1alpha1", ConfigDebug: new(in.Options.Debug), ConfigPersist: new(true), } machine := &v1alpha1.MachineConfig{ MachineType: machine.TypeWorker.String(), MachineToken: in.Options.SecretsBundle.TrustdInfo.Token, MachineCertSANs: in.AdditionalMachineCertSANs, MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: emptyIf(fmt.Sprintf("%s:v%s", constants.KubeletImage, in.KubernetesVersion), in.KubernetesVersion), }, MachineCA: &x509.PEMEncodedCertificateAndKey{Crt: in.Options.SecretsBundle.Certs.OS.Crt}, MachineInstall: &v1alpha1.InstallConfig{ InstallDisk: in.Options.InstallDisk, InstallImage: in.Options.InstallImage, InstallWipe: new(false), InstallExtraKernelArgs: in.Options.InstallExtraKernelArgs, }, MachineDisks: in.Options.MachineDisks, MachineSysctls: in.Options.Sysctls, MachineFeatures: &v1alpha1.FeaturesConfig{}, } if in.Options.VersionContract.GrubUseUKICmdlineDefault() { machine.MachineInstall.InstallGrubUseUKICmdline = new(true) } if !in.Options.VersionContract.HideRBACAndKeyUsage() { machine.MachineFeatures.RBAC = new(true) if in.Options.VersionContract.ApidExtKeyUsageCheckEnabled() { machine.MachineFeatures.ApidCheckExtKeyUsage = new(true) } } if in.Options.VersionContract.DiskQuotaSupportEnabled() { machine.MachineFeatures.DiskQuotaSupport = new(true) } if kubePrismPort, optionSet := in.Options.KubePrismPort.Get(); optionSet { // default to enabled, but if set explicitly, allow it to be disabled if kubePrismPort > 0 { machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{ ServerEnabled: new(true), ServerPort: kubePrismPort, } } } else if in.Options.VersionContract.KubePrismEnabled() { machine.MachineFeatures.KubePrismSupport = &v1alpha1.KubePrism{ ServerEnabled: new(true), ServerPort: constants.DefaultKubePrismPort, } } if in.Options.VersionContract.KubeletDefaultRuntimeSeccompProfileEnabled() { machine.MachineKubelet.KubeletDefaultRuntimeSeccompProfileEnabled = new(true) } if in.Options.VersionContract.KubeletManifestsDirectoryDisabled() { machine.MachineKubelet.KubeletDisableManifestsDirectory = new(true) } if in.Options.VersionContract.HostDNSEnabled() { machine.MachineFeatures.HostDNSSupport = &v1alpha1.HostDNSConfig{ HostDNSEnabled: new(true), HostDNSForwardKubeDNSToHost: ptrOrNil(in.Options.HostDNSForwardKubeDNSToHost.ValueOrZero() || in.Options.VersionContract.HostDNSForwardKubeDNSToHost()), } } controlPlaneURL, err := url.Parse(in.ControlPlaneEndpoint) if err != nil { return nil, err } cluster := &v1alpha1.ClusterConfig{ ClusterID: in.Options.SecretsBundle.Cluster.ID, ClusterSecret: in.Options.SecretsBundle.Cluster.Secret, ClusterCA: &x509.PEMEncodedCertificateAndKey{Crt: in.Options.SecretsBundle.Certs.K8s.Crt}, BootstrapToken: in.Options.SecretsBundle.Secrets.BootstrapToken, ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{URL: controlPlaneURL}, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{ DNSDomain: in.Options.DNSDomain, PodSubnet: in.PodNet, ServiceSubnet: in.ServiceNet, CNI: in.Options.CNIConfig, }, } if in.Options.DiscoveryEnabled != nil { cluster.ClusterDiscoveryConfig = &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(*in.Options.DiscoveryEnabled), } if in.Options.VersionContract.KubernetesDiscoveryBackendDisabled() { cluster.ClusterDiscoveryConfig.DiscoveryRegistries.RegistryKubernetes.RegistryDisabled = new(true) } } if machine.MachineRegistries.RegistryMirrors == nil { //nolint:staticcheck // backwards compatibility machine.MachineRegistries.RegistryMirrors = map[string]*v1alpha1.RegistryMirrorConfig{} //nolint:staticcheck // backwards compatibility } if in.Options.VersionContract.KubernetesAlternateImageRegistries() { if _, ok := machine.MachineRegistries.RegistryMirrors["k8s.gcr.io"]; !ok { //nolint:staticcheck // backwards compatibility Talos v1.1->1.2 machine.MachineRegistries.RegistryMirrors["k8s.gcr.io"] = &v1alpha1.RegistryMirrorConfig{ //nolint:staticcheck // backwards compatibility Talos v1.1->1.2 MirrorEndpoints: []string{ "https://registry.k8s.io", "https://k8s.gcr.io", }, } } } if in.Options.VersionContract.ClusterNameForWorkers() { cluster.ClusterName = in.ClusterName } v1alpha1Config.MachineConfig = machine v1alpha1Config.ClusterConfig = cluster documents := []config.Document{v1alpha1Config} extraDocuments, err := in.generateRegistryConfigs(machine) if err != nil { return nil, fmt.Errorf("failed to generate registry configs: %w", err) } documents = append(documents, extraDocuments...) extraDocuments, err = in.generateNetworkConfigs(machine) if err != nil { return nil, fmt.Errorf("failed to generate network configs: %w", err) } documents = append(documents, extraDocuments...) return documents, nil } ================================================ FILE: pkg/machinery/config/internal/cis/cis.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cis import ( "crypto/rand" "encoding/base64" ) // CreateEncryptionToken generates an encryption token to be used for secrets. func CreateEncryptionToken() (string, error) { encryptionKey := make([]byte, 32) if _, err := rand.Read(encryptionKey); err != nil { return "", err } str := base64.StdEncoding.EncodeToString(encryptionKey) return str, nil } ================================================ FILE: pkg/machinery/config/internal/cis/cis_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cis_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/machinery/config/internal/documentid/documentid.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package documentid provides methods to create and extract document IDs. package documentid import ( "cmp" "github.com/siderolabs/talos/pkg/machinery/config/config" ) const ( // ManifestAPIVersionKey is the string indicating a manifest's version. ManifestAPIVersionKey = "apiVersion" // ManifestKindKey is the string indicating a manifest's kind. ManifestKindKey = "kind" // ManifestNameKey is the string indicating a manifest's name. ManifestNameKey = "name" // ManifestVersionKey is the string indicating a manifest's version, used only in v1alpha1 document. ManifestVersionKey = "version" ) // DocumentID uniquely identifies a configuration document. type DocumentID struct { APIVersion string Kind string Name string } // Meta returns a map representation of the DocumentID suitable for use as metadata in configuration documents. func (id DocumentID) Meta() map[string]any { meta := map[string]any{} if id.Kind == "v1alpha1" { meta[ManifestVersionKey] = "v1alpha1" return meta } meta[ManifestAPIVersionKey] = id.APIVersion meta[ManifestKindKey] = id.Kind if id.Name != "" { meta[ManifestNameKey] = id.Name } return meta } // Extract extracts a DocumentID from the given configuration document. func Extract(doc config.Document) DocumentID { var apiVersion, kind, name string if doc.APIVersion() != "" && doc.Kind() != "" { apiVersion = doc.APIVersion() kind = doc.Kind() } if named, ok := doc.(config.NamedDocument); ok { name = named.Name() } return DocumentID{ APIVersion: apiVersion, Kind: cmp.Or(kind, "v1alpha1"), Name: name, } } ================================================ FILE: pkg/machinery/config/internal/registry/registry.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package registry provides a registry for configuration documents. package registry import ( "errors" "fmt" "sync" "github.com/siderolabs/talos/pkg/machinery/config/config" ) var ( // ErrNotRegistered indicates that the manifest kind is not registered. ErrNotRegistered = errors.New("not registered") // ErrExists indicates that the manifest is already registered. ErrExists = errors.New("exists") ) // NewDocumentFunc represents a function that creates a new document by version. type NewDocumentFunc func(version string) config.Document var registry = NewRegistry() // Registry represents the document kind/version registry. // // Global registry is available via top-level functions Register and New. type Registry struct { m sync.Mutex registered map[string]NewDocumentFunc } // NewRegistry creates a new registry. func NewRegistry() *Registry { return &Registry{ registered: map[string]NewDocumentFunc{}, } } // Register registers a manifests with the registry. func Register(kind string, f NewDocumentFunc) { registry.Register(kind, f) } // New creates a new instance of the requested manifest. func New(kind, version string) (config.Document, error) { return registry.New(kind, version) } // Register registers a document kind with the registry. func (r *Registry) Register(kind string, f NewDocumentFunc) { r.m.Lock() defer r.m.Unlock() if _, ok := r.registered[kind]; ok { panic(ErrExists) } r.registered[kind] = f } // New creates a new instance of the requested document. func (r *Registry) New(kind, version string) (config.Document, error) { r.m.Lock() defer r.m.Unlock() f, ok := r.registered[kind] if ok { doc := f(version) if doc != nil { return doc, nil } } return nil, fmt.Errorf("%q %q: %w", kind, version, ErrNotRegistered) } ================================================ FILE: pkg/machinery/config/internal/registry/registry_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package registry_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" ) type mockDocument struct { kind, version string } func (d mockDocument) Clone() config.Document { return d } func (d mockDocument) Kind() string { return d.kind } func (d mockDocument) APIVersion() string { return d.version } func mockFactory(kind, version string) registry.NewDocumentFunc { return func(requestedVersion string) config.Document { if requestedVersion == version { return mockDocument{kind, version} } return nil } } func TestRegistry(t *testing.T) { r := registry.NewRegistry() // register document types r.Register("kind1", mockFactory("kind1", "v1alpha1")) r.Register("kind2", mockFactory("kind2", "v1alpha1")) // register duplicate kind assert.Panics(t, func() { r.Register("kind1", mockFactory("kind1", "v1alpha3")) }) // attempt to get unregistered kind _, err := r.New("unknownKind", "unknownVersion") require.Error(t, err) assert.ErrorIs(t, err, registry.ErrNotRegistered) assert.EqualError(t, err, "\"unknownKind\" \"unknownVersion\": not registered") // successful creation of documents d, err := r.New("kind1", "v1alpha1") require.NoError(t, err) assert.Equal(t, "kind1", d.Kind()) assert.Equal(t, "v1alpha1", d.APIVersion()) d, err = r.New("kind2", "v1alpha1") require.NoError(t, err) assert.Equal(t, "kind2", d.Kind()) assert.Equal(t, "v1alpha1", d.APIVersion()) // attempt get registered kind, but wrong version _, err = r.New("kind1", "unknownVersion") require.Error(t, err) assert.ErrorIs(t, err, registry.ErrNotRegistered) assert.EqualError(t, err, "\"kind1\" \"unknownVersion\": not registered") } ================================================ FILE: pkg/machinery/config/machine/machine.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package machine defines common machine type. package machine import ( "fmt" ) //go:generate go tool golang.org/x/tools/cmd/stringer -type=Type -linecomment // Type represents a machine type. type Type int //structprotogen:gen_enum const ( // TypeUnknown represents undefined node type, when there is no machine configuration yet. TypeUnknown Type = iota // unknown // TypeInit type designates the first control plane node to come up. You can think of it like a bootstrap node. // This node will perform the initial steps to bootstrap the cluster -- generation of TLS assets, starting of the control plane, etc. TypeInit // init // TypeControlPlane designates the node as a control plane member. // This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler. TypeControlPlane // controlplane // TypeWorker designates the node as a worker node. // This means it will be an available compute node for scheduling workloads. TypeWorker // worker ) // ParseType parses string constant as Type. func ParseType(s string) (Type, error) { switch s { case "init": return TypeInit, nil case "controlplane": return TypeControlPlane, nil case "worker", "join", "": return TypeWorker, nil case "unknown": return TypeUnknown, nil default: return TypeUnknown, fmt.Errorf("invalid machine type: %q", s) } } // MarshalText implements encoding.TextMarshaler. func (t Type) MarshalText() (text []byte, err error) { return []byte(t.String()), nil } // UnmarshalText implements encoding.TextUnmarshaler. func (t *Type) UnmarshalText(text []byte) error { var err error *t, err = ParseType(string(text)) return err } // IsControlPlane returns true if the type is a control plane node. func (t Type) IsControlPlane() bool { return t == TypeControlPlane || t == TypeInit } ================================================ FILE: pkg/machinery/config/machine/machine_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package machine_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) func TestParseType(t *testing.T) { t.Parallel() t.Run("Values", func(t *testing.T) { // We have to use the same values as defined in proto as we use direct type conversions in many places. assert.EqualValues(t, machineapi.MachineConfig_TYPE_UNKNOWN, machine.TypeUnknown) assert.EqualValues(t, machineapi.MachineConfig_TYPE_INIT, machine.TypeInit) assert.EqualValues(t, machineapi.MachineConfig_TYPE_CONTROL_PLANE, machine.TypeControlPlane) assert.EqualValues(t, machineapi.MachineConfig_TYPE_WORKER, machine.TypeWorker) }) validTests := []struct { s string typ machine.Type }{ {"init", machine.TypeInit}, {"controlplane", machine.TypeControlPlane}, {"worker", machine.TypeWorker}, {"join", machine.TypeWorker}, {"", machine.TypeWorker}, {"unknown", machine.TypeUnknown}, } for _, tt := range validTests { t.Run(tt.s, func(t *testing.T) { t.Parallel() actual, err := machine.ParseType(tt.s) require.NoError(t, err) assert.Equal(t, tt.typ, actual) // check that constant's comment for stringer and ParseType() function are in sync actual, err = machine.ParseType(actual.String()) require.NoError(t, err) assert.Equal(t, tt.typ, actual) }) } for _, s := range []string{ "foo", } { t.Run(s, func(t *testing.T) { t.Parallel() actual, err := machine.ParseType(s) assert.Error(t, err) assert.Equal(t, machine.TypeUnknown, actual) }) } assert.NotEmpty(t, machine.TypeUnknown.String()) } ================================================ FILE: pkg/machinery/config/machine/type_string.go ================================================ // Code generated by "stringer -type=Type -linecomment"; DO NOT EDIT. package machine import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[TypeUnknown-0] _ = x[TypeInit-1] _ = x[TypeControlPlane-2] _ = x[TypeWorker-3] } const _Type_name = "unknowninitcontrolplaneworker" var _Type_index = [...]uint8{0, 7, 11, 23, 29} func (i Type) String() string { if i < 0 || i >= Type(len(_Type_index)-1) { return "Type(" + strconv.FormatInt(int64(i), 10) + ")" } return _Type_name[_Type_index[i]:_Type_index[i+1]] } ================================================ FILE: pkg/machinery/config/merge/merge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package merge import ( "fmt" "reflect" "strings" ) // Merge two config trees together. // // Data in the left is replaced with data in the right unless it's zero value. // // This function is not supposed to be a generic merge function. // It is specifically fine-tuned to merge Talos machine configuration. // // Rules: // - if it is a simple value (int, float, string, etc.), it's merged into the left unless it's zero value, but boolean false is always merged. // - if it is a pointer, merged dereferencing the pointer unless the right is nil // - if it is a slice, merged by concatenating the right to the left. // - if the `merge:"replace"` struct tag is defined, a slice is replaced with the value of the right (unless it's zero value.) // - slices of `[]byte` are always replaced // - if it is a map, for each key value is merged recursively. // - if it is a struct, merge is performed for each field of the struct. // - if the type implements 'merger' interface, Merge function is called to handle the merge process. // - merger interface should be implemented on the pointer to the type. func Merge(left, right any) error { return merge(reflect.ValueOf(left), reflect.ValueOf(right), false, false) } type merger interface { Merge(other any) error } var ( zeroValue reflect.Value mergerType = reflect.TypeFor[merger]() ) //nolint:gocyclo,cyclop func merge(vl, vr reflect.Value, replace, overwrite bool) error { if vl == zeroValue && vr == zeroValue { return nil } tl, tr := vl.Type(), vr.Type() if tl != tr { return fmt.Errorf("merge type mismatch left %v right %v", tl, tr) } if reflect.PointerTo(tl).Implements(mergerType) { return vl.Addr().Interface().(merger).Merge(vr.Interface()) } switch tl.Kind() { //nolint:exhaustive case reflect.Pointer, reflect.Interface: if vr.IsZero() { return nil } if vl.IsZero() { vl.Set(vr) return nil } return merge(vl.Elem(), vr.Elem(), replace, true) case reflect.Slice: if vr.IsZero() { return nil } if !vl.CanSet() { return fmt.Errorf("merge not possible, left %v is not settable", vl) } if replace || tl.Elem().Kind() == reflect.Uint8 { vl.Set(vr) return nil } if vl.IsNil() && vr.Len() == 0 { vl.Set(reflect.MakeSlice(tl, 0, 0)) } else { vl.Set(reflect.AppendSlice(reflect.MakeSlice(tl, 0, 0), reflect.AppendSlice(vl, vr))) } case reflect.Map: if vr.IsZero() { return nil } if replace { vl.Set(vr) return nil } if vl.IsNil() { vl.Set(reflect.MakeMap(tl)) } for _, k := range vr.MapKeys() { if !isNilOrZero(vl.MapIndex(k)) { valueType := tl.Elem() var v, rightV reflect.Value if valueType.Kind() == reflect.Interface { // special case for map[string]interface{} // here we have to instantiate the actual type behind in the `interface{}`, otherwise it's not settable valueType = vl.MapIndex(k).Elem().Type() if valueType != vr.MapIndex(k).Elem().Type() { return fmt.Errorf("merge type mismatch left %v right %v", valueType, vr.MapIndex(k).Elem().Type()) } v = reflect.New(valueType).Elem() v.Set(vl.MapIndex(k).Elem()) rightV = reflect.New(valueType).Elem() rightV.Set(vr.MapIndex(k).Elem()) } else { // "normal" maps v = reflect.New(valueType).Elem() v.Set(vl.MapIndex(k)) rightV = vr.MapIndex(k) } if err := merge(v, rightV, false, false); err != nil { return fmt.Errorf("merge map key %v[%v]: %w", tl, k, err) } vl.SetMapIndex(k, v) } else { vl.SetMapIndex(k, vr.MapIndex(k)) } } case reflect.Struct: if replace { // if the right-hand struct is zero value, skip replacing the left-hand struct if vr.IsZero() { return nil } vl.Set(vr) return nil } for i := range tl.NumField() { var replace bool structTag := tl.Field(i).Tag.Get("merge") for value := range strings.SplitSeq(structTag, ",") { if value == "replace" { replace = true } } fl := vl.FieldByIndex(tl.Field(i).Index) fr := vr.FieldByIndex(tr.Field(i).Index) if err := merge(fl, fr, replace, false); err != nil { return fmt.Errorf("merge field %v.%v: %w", tl, tl.Field(i).Name, err) } } case reflect.String, reflect.Int, reflect.Uint, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.Bool: if !vl.CanSet() { return fmt.Errorf("merge not possible, left %v is not settable", vl) } if tl.Kind() != reflect.Bool && vr.IsZero() && !overwrite { return nil } vl.Set(vr) default: return fmt.Errorf("merge not implemented for %v", tl.Kind()) } return nil } // isNilOrZero returns true if the [reflect.Value] is zero [reflect.Value] or something that is nil. // We need it because if map contains a key with `nil` value, simply comparing that result to the `zeroValue` // is not enough. func isNilOrZero(idx reflect.Value) bool { if idx == zeroValue { return true } switch idx.Kind() { //nolint:exhaustive case reflect.Interface, reflect.Pointer, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func: return idx.IsNil() default: return false } } ================================================ FILE: pkg/machinery/config/merge/merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package merge_test import ( "fmt" "slices" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/merge" ) type Config struct { A string B int C *bool D *int Slice []Struct ReplacedSlice []string `merge:"replace"` Map map[string]Struct CustomSlice CustomSlice } type Struct struct { DA bool DB *int } type CustomSlice []string func (s *CustomSlice) Merge(other any) error { otherSlice, ok := other.(CustomSlice) if !ok { return fmt.Errorf("other is not CustomSlice: %v", other) } *s = append(*s, otherSlice...) slices.Sort(*s) return nil } type Unstructured map[string]any func TestMerge(t *testing.T) { for _, tt := range []struct { name string left, right any expected any }{ { name: "zero", }, { name: "partial merge", left: &Config{ A: "a", B: 3, C: new(true), Slice: []Struct{ { DA: true, DB: new(1), }, }, Map: map[string]Struct{ "a": { DA: true, }, "b": { DB: new(2), }, }, }, right: &Config{ A: "aa", B: 4, Slice: []Struct{ { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DB: new(3), }, "b": { DA: true, DB: new(5), }, "c": { DB: new(4), }, }, }, expected: &Config{ A: "aa", B: 4, C: new(true), Slice: []Struct{ { DA: true, DB: new(1), }, { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DB: new(3), }, "b": { DA: true, DB: new(5), }, "c": { DB: new(4), }, }, }, }, { name: "merge with zero", left: &Config{ A: "a", B: 3, C: new(true), Slice: []Struct{ { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DA: true, }, "b": { DB: new(2), }, }, }, right: &Config{}, expected: &Config{ A: "a", B: 3, C: new(true), Slice: []Struct{ { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DA: true, }, "b": { DB: new(2), }, }, }, }, { name: "merge from zero", left: &Config{}, right: &Config{ A: "a", B: 3, C: new(true), Slice: []Struct{ { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DA: true, }, "b": { DB: new(2), }, }, }, expected: &Config{ A: "a", B: 3, C: new(true), Slice: []Struct{ { DA: false, DB: new(2), }, }, Map: map[string]Struct{ "a": { DA: true, }, "b": { DB: new(2), }, }, }, }, { name: "replace slice", left: &Config{ ReplacedSlice: []string{"a", "b"}, }, right: &Config{ ReplacedSlice: []string{"c", "d"}, }, expected: &Config{ ReplacedSlice: []string{"c", "d"}, }, }, { name: "zero slice", left: &Config{}, right: &Config{ Slice: []Struct{}, }, expected: &Config{ Slice: []Struct{}, }, }, { name: "custom slice", left: &Config{ CustomSlice: []string{"a", "c"}, }, right: &Config{ CustomSlice: []string{"b", "d"}, }, expected: &Config{ CustomSlice: []string{"a", "b", "c", "d"}, }, }, { name: "merge with pointer override", left: &Config{ D: new(1), }, right: &Config{ D: new(0), }, expected: &Config{ D: new(0), }, }, { name: "unstructured", left: &Unstructured{ "a": "aa", "map": map[string]any{ "slice": []any{ "s1", }, "some": "value", }, }, right: &Unstructured{ "b": "bb", "map": map[string]any{ "slice": []any{ "s2", }, "other": "thing", }, }, expected: &Unstructured{ "a": "aa", "b": "bb", "map": map[string]any{ "slice": []any{ "s1", "s2", }, "some": "value", "other": "thing", }, }, }, { name: "unstructed with nil value", left: Unstructured{ "a": nil, "b": []any{ "c", "d", }, }, right: Unstructured{ "a": Unstructured{ "b": []any{ "c", "d", }, }, }, expected: Unstructured{ "a": Unstructured{ "b": []any{ "c", "d", }, }, "b": []any{ "c", "d", }, }, }, } { t.Run(tt.name, func(t *testing.T) { err := merge.Merge(tt.left, tt.right) require.NoError(t, err) assert.Equal(t, tt.expected, tt.left) }) } } ================================================ FILE: pkg/machinery/config/provider.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // Encoder provides the interface to encode configuration documents. type Encoder = config.Encoder // Validator provides the interface to validate configuration. type Validator = config.Validator // RuntimeValidator provides the interface to validate configuration in the runtime context. type RuntimeValidator = config.RuntimeValidator // Container provides the interface to access configuration documents. // // Container might contain multiple config documents, supporting encoding/decoding, // validation, and other operations. type Container interface { Encoder Validator RuntimeValidator Readonly() bool // RawV1Alpha1 returns internal config representation. RawV1Alpha1() *v1alpha1.Config // Documents returns a list of config documents. // // Documents should be not be modified. Documents() []config.Document } // Provider defines the configuration consumption interface combining access and encoding/decoding. type Provider interface { Config Container // Clone returns a copy of the Provider. Clone() Provider // PatchV1Alpha1 patches the container's v1alpha1.Config while preserving other config documents. PatchV1Alpha1(patcher func(*v1alpha1.Config) error) (Provider, error) // RedactSecrets returns a copy of the Provider with all secrets replaced with the given string. RedactSecrets(string) Provider // CompleteForBoot return true if the machine config is enough to proceed with the boot process. CompleteForBoot() bool } ================================================ FILE: pkg/machinery/config/schemas/README.md ================================================ # Deprecation Notice The schema `v1alpha1_config.schema.json` is deprecated, kept only for backward-compatibility. Please use `config.schema.json` instead. ================================================ FILE: pkg/machinery/config/schemas/config.schema.json ================================================ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://talos.dev/v1.13/schemas/config.schema.json", "$defs": { "block.DiskSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the disk.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the disk.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the disk.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "DiskSelector selects a disk for the volume." }, "block.EncryptionKey": { "properties": { "slot": { "type": "integer", "title": "slot", "description": "Key slot number for LUKS2 encryption.\n", "markdownDescription": "Key slot number for LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n" }, "static": { "$ref": "#/$defs/block.EncryptionKeyStatic", "title": "static", "description": "Key which value is stored in the configuration file.\n", "markdownDescription": "Key which value is stored in the configuration file.", "x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n" }, "nodeID": { "$ref": "#/$defs/block.EncryptionKeyNodeID", "title": "nodeID", "description": "Deterministically generated key from the node UUID and PartitionLabel.\n", "markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.", "x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n" }, "kms": { "$ref": "#/$defs/block.EncryptionKeyKMS", "title": "kms", "description": "KMS managed encryption key.\n", "markdownDescription": "KMS managed encryption key.", "x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n" }, "tpm": { "$ref": "#/$defs/block.EncryptionKeyTPM", "title": "tpm", "description": "Enable TPM based disk encryption.\n", "markdownDescription": "Enable TPM based disk encryption.", "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" }, "lockToState": { "type": "boolean", "title": "lockToState", "description": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n", "markdownDescription": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.", "x-intellij-html-description": "\u003cp\u003eLock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKey represents configuration for disk encryption key." }, "block.EncryptionKeyKMS": { "properties": { "endpoint": { "type": "string", "title": "endpoint", "description": "KMS endpoint to Seal/Unseal the key.\n", "markdownDescription": "KMS endpoint to Seal/Unseal the key.", "x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server." }, "block.EncryptionKeyNodeID": { "properties": {}, "additionalProperties": false, "type": "object", "description": "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel." }, "block.EncryptionKeyStatic": { "properties": { "passphrase": { "type": "string", "title": "passphrase", "description": "Defines the static passphrase value.\n", "markdownDescription": "Defines the static passphrase value.", "x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyStatic represents throw away key type." }, "block.EncryptionKeyTPM": { "properties": { "options": { "$ref": "#/$defs/block.EncryptionKeyTPMOptions", "title": "options", "description": "TPM options for key protection.\n", "markdownDescription": "TPM options for key protection.", "x-intellij-html-description": "\u003cp\u003eTPM options for key protection.\u003c/p\u003e\n" }, "checkSecurebootStatusOnEnroll": { "type": "boolean", "title": "checkSecurebootStatusOnEnroll", "description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.\n", "markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.", "x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM." }, "block.EncryptionKeyTPMOptions": { "properties": { "pcrs": { "items": { "type": "integer" }, "type": "array", "title": "pcrs", "description": "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.\n", "markdownDescription": "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.", "x-intellij-html-description": "\u003cp\u003eList of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyTPMOptions represents the options for TPM-based key protection." }, "block.EncryptionSpec": { "properties": { "provider": { "enum": [ "luks2" ], "title": "provider", "description": "Encryption provider to use for the encryption.\n", "markdownDescription": "Encryption provider to use for the encryption.", "x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n" }, "keys": { "items": { "$ref": "#/$defs/block.EncryptionKey" }, "type": "array", "title": "keys", "description": "Defines the encryption keys generation and storage method.\n", "markdownDescription": "Defines the encryption keys generation and storage method.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n" }, "cipher": { "enum": [ "aes-xts-plain64", "xchacha12,aes-adiantum-plain64", "xchacha20,aes-adiantum-plain64" ], "title": "cipher", "description": "Cipher to use for the encryption. Depends on the encryption provider.\n", "markdownDescription": "Cipher to use for the encryption. Depends on the encryption provider.", "x-intellij-html-description": "\u003cp\u003eCipher to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n" }, "keySize": { "type": "integer", "title": "keySize", "description": "Defines the encryption key length.\n", "markdownDescription": "Defines the encryption key length.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n" }, "blockSize": { "type": "integer", "title": "blockSize", "description": "Defines the encryption sector size.\n", "markdownDescription": "Defines the encryption sector size.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n" }, "options": { "enum": [ "no_read_workqueue", "no_write_workqueue", "same_cpu_crypt" ], "title": "options", "description": "Additional –perf parameters for the LUKS2 encryption.\n", "markdownDescription": "Additional --perf parameters for the LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionSpec represents volume encryption settings." }, "block.ExistingMountSpec": { "properties": { "readOnly": { "type": "boolean", "title": "readOnly", "description": "Mount the volume read-only.\n", "markdownDescription": "Mount the volume read-only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read-only.\u003c/p\u003e\n" }, "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExistingMountSpec describes how the volume is mounted." }, "block.ExistingVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExistingVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/block.VolumeDiscoverySpec", "title": "discovery", "description": "The discovery describes how to find a volume.\n", "markdownDescription": "The discovery describes how to find a volume.", "x-intellij-html-description": "\u003cp\u003eThe discovery describes how to find a volume.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.ExistingMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ExistingVolumeConfig is an existing volume configuration document.\\nExisting volumes allow to mount partitions (or whole disks) that were created\\noutside of Talos. Volume will be mounted under `/var/mnt/\u003cname\u003e`.\\nThe existing volume config name should not conflict with user volume names.\\n" }, "block.ExternalMountSpec": { "properties": { "readOnly": { "type": "boolean", "title": "readOnly", "description": "Mount the volume read-only.\n", "markdownDescription": "Mount the volume read-only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read-only.\u003c/p\u003e\n" }, "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" }, "virtiofs": { "$ref": "#/$defs/block.VirtiofsMountSpec", "title": "virtiofs", "description": "Virtiofs mount options.\n", "markdownDescription": "Virtiofs mount options.", "x-intellij-html-description": "\u003cp\u003eVirtiofs mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExternalMountSpec describes how the external volume is mounted." }, "block.ExternalVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExternalVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the mount.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the mount.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the mount.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "filesystemType": { "enum": [ "virtiofs", "nfs" ], "title": "filesystemType", "description": "Filesystem type.\n", "markdownDescription": "Filesystem type.", "x-intellij-html-description": "\u003cp\u003eFilesystem type.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.ExternalMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ExternalVolumeConfig is an external disk mount configuration document.\\nExternal volumes allow to mount volumes that were created outside of Talos,\\nover the network or API. Volume will be mounted under `/var/mnt/\u003cname\u003e`.\\nThe external volume config name should not conflict with user volume names.\\n" }, "block.FilesystemSpec": { "properties": { "type": { "enum": [ "ext4", "xfs" ], "title": "type", "description": "Filesystem type. Default is xfs.\n", "markdownDescription": "Filesystem type. Default is `xfs`.", "x-intellij-html-description": "\u003cp\u003eFilesystem type. Default is \u003ccode\u003exfs\u003c/code\u003e.\u003c/p\u003e\n" }, "projectQuotaSupport": { "type": "boolean", "title": "projectQuotaSupport", "description": "Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n", "markdownDescription": "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.", "x-intellij-html-description": "\u003cp\u003eEnables project quota support, valid only for \u0026lsquo;xfs\u0026rsquo; filesystem.\u003c/p\u003e\n\n\u003cp\u003eNote: changing this value might require a full remount of the filesystem.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FilesystemSpec configures the filesystem for the volume." }, "block.MountSpec": { "properties": { "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MountSpec describes how the volume is mounted." }, "block.ProvisioningSpec": { "properties": { "diskSelector": { "$ref": "#/$defs/block.DiskSelector", "title": "diskSelector", "description": "The disk selector expression.\n", "markdownDescription": "The disk selector expression.", "x-intellij-html-description": "\u003cp\u003eThe disk selector expression.\u003c/p\u003e\n" }, "grow": { "type": "boolean", "title": "grow", "description": "Should the volume grow to the size of the disk (if possible).\n", "markdownDescription": "Should the volume grow to the size of the disk (if possible).", "x-intellij-html-description": "\u003cp\u003eShould the volume grow to the size of the disk (if possible).\u003c/p\u003e\n" }, "minSize": { "type": "string", "title": "minSize", "description": "The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n", "markdownDescription": "The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.", "x-intellij-html-description": "\u003cp\u003eThe minimum size of the volume.\u003c/p\u003e\n\n\u003cp\u003eSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\u003c/p\u003e\n" }, "maxSize": { "type": "string", "title": "maxSize", "description": "The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.\n", "markdownDescription": "The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.", "x-intellij-html-description": "\u003cp\u003eThe maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\u003c/p\u003e\n\n\u003cp\u003eSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ProvisioningSpec describes how the volume is provisioned." }, "block.RawVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RawVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "RawVolumeConfig is a raw volume configuration document.\\nRaw volumes allow to create partitions without formatting them.\\n If you want to use local storage, user volumes is a better choice,\\n raw volumes are intended to be used with CSI provisioners.\\nThe partition label is automatically generated as `r-\u003cname\u003e`.\\n" }, "block.SwapVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "SwapVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "SwapVolumeConfig is a disk swap volume configuration document.\\nSwap volume is automatically allocated as a partition on the specified disk\\nand activated as swap, removing a swap volume deactivates swap.\\nThe partition label is automatically generated as `s-\u003cname\u003e`.\\n" }, "block.UserMountSpec": { "properties": { "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "UserMountSpec describes how the volume is mounted." }, "block.UserVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "UserVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "volumeType": { "enum": [ "directory", "disk", "partition" ], "title": "volumeType", "description": "Volume type.\n", "markdownDescription": "Volume type.", "x-intellij-html-description": "\u003cp\u003eVolume type.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "filesystem": { "$ref": "#/$defs/block.FilesystemSpec", "title": "filesystem", "description": "The filesystem describes how the volume is formatted.\n", "markdownDescription": "The filesystem describes how the volume is formatted.", "x-intellij-html-description": "\u003cp\u003eThe filesystem describes how the volume is formatted.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.UserMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "UserVolumeConfig is a user volume configuration document.\\nUser volume is automatically allocated as a partition on the specified disk\\nand mounted under `/var/mnt/\u003cname\u003e`.\\nThe partition label is automatically generated as `u-\u003cname\u003e`.\\n" }, "block.VirtiofsMountSpec": { "properties": { "tag": { "type": "string", "title": "tag", "description": "Selector tag for the Virtiofs mount.\n", "markdownDescription": "Selector tag for the Virtiofs mount.", "x-intellij-html-description": "\u003cp\u003eSelector tag for the Virtiofs mount.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VirtiofsMountSpec describes Virtiofs mount options." }, "block.VolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n", "markdownDescription": "Name of the volume.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.MountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "VolumeConfig is a system volume configuration document.\\nNote: at the moment, only `STATE`, `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported.\\n" }, "block.VolumeDiscoverySpec": { "properties": { "volumeSelector": { "$ref": "#/$defs/block.VolumeSelector", "title": "volumeSelector", "description": "The volume selector expression.\n", "markdownDescription": "The volume selector expression.", "x-intellij-html-description": "\u003cp\u003eThe volume selector expression.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeDiscoverySpec describes how the volume is discovered." }, "block.VolumeSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the volume.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the volume.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the volume.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeSelector selects an existing volume." }, "block.ZswapConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ZswapConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "maxPoolPercent": { "type": "integer", "title": "maxPoolPercent", "description": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n", "markdownDescription": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.", "x-intellij-html-description": "\u003cp\u003eThe maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\u003c/p\u003e\n" }, "shrinkerEnabled": { "type": "boolean", "title": "shrinkerEnabled", "description": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n", "markdownDescription": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.", "x-intellij-html-description": "\u003cp\u003eEnable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ZswapConfig is a zswap (compressed memory) configuration document.\\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\\nwhen the system is under memory pressure.\\n" }, "cri.RegistryAuthConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryAuthConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry endpoint to apply the authentication configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. ‘my-mirror.local:5000’ for ‘https://my-mirror.local:5000/v2/’.\n\nThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\n", "markdownDescription": "Registry endpoint to apply the authentication configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", "x-intellij-html-description": "\u003cp\u003eRegistry endpoint to apply the authentication configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry endpoint is the hostname part of the endpoint URL,\ne.g. \u0026lsquo;my-mirror.local:5000\u0026rsquo; for \u0026lsquo;\u003ca href=\"https://my-mirror.local:5000/v2/'\" target=\"_blank\"\u003ehttps://my-mirror.local:5000/v2/\u0026rsquo;\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\u003c/p\u003e\n" }, "username": { "type": "string", "title": "username", "description": "Username/password authentication.\n", "markdownDescription": "Username/password authentication.", "x-intellij-html-description": "\u003cp\u003eUsername/password authentication.\u003c/p\u003e\n" }, "password": { "type": "string", "title": "password", "description": "Username/password authentication.\n", "markdownDescription": "Username/password authentication.", "x-intellij-html-description": "\u003cp\u003eUsername/password authentication.\u003c/p\u003e\n" }, "auth": { "type": "string", "title": "auth", "description": "Raw authentication string.\n", "markdownDescription": "Raw authentication string.", "x-intellij-html-description": "\u003cp\u003eRaw authentication string.\u003c/p\u003e\n" }, "identityToken": { "type": "string", "title": "identityToken", "description": "Identity token authentication.\n", "markdownDescription": "Identity token authentication.", "x-intellij-html-description": "\u003cp\u003eIdentity token authentication.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryAuthConfig configures authentication for a registry endpoint." }, "cri.RegistryEndpoint": { "properties": { "url": { "type": "string", "pattern": "^(http|https)://", "title": "url", "description": "The URL of the registry mirror endpoint.\n", "markdownDescription": "The URL of the registry mirror endpoint.", "x-intellij-html-description": "\u003cp\u003eThe URL of the registry mirror endpoint.\u003c/p\u003e\n" }, "overridePath": { "type": "boolean", "title": "overridePath", "description": "Use endpoint path as supplied, without adding /v2/ suffix.\n", "markdownDescription": "Use endpoint path as supplied, without adding `/v2/` suffix.", "x-intellij-html-description": "\u003cp\u003eUse endpoint path as supplied, without adding \u003ccode\u003e/v2/\u003c/code\u003e suffix.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "url" ], "description": "RegistryEndpoint defines a registry mirror endpoint." }, "cri.RegistryMirrorConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryMirrorConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry name to apply the mirror configuration to.\n\nRegistry name is the first segment of image identifier, with ‘docker.io’\nbeing default one.\n\nA special name ‘*’ can be used to define mirror configuration\nthat applies to all registries.\n", "markdownDescription": "Registry name to apply the mirror configuration to.\n\nRegistry name is the first segment of image identifier, with 'docker.io'\nbeing default one.\n\nA special name '*' can be used to define mirror configuration\nthat applies to all registries.", "x-intellij-html-description": "\u003cp\u003eRegistry name to apply the mirror configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry name is the first segment of image identifier, with \u0026lsquo;docker.io\u0026rsquo;\nbeing default one.\u003c/p\u003e\n\n\u003cp\u003eA special name \u0026lsquo;*\u0026rsquo; can be used to define mirror configuration\nthat applies to all registries.\u003c/p\u003e\n" }, "endpoints": { "items": { "$ref": "#/$defs/cri.RegistryEndpoint" }, "type": "array", "title": "endpoints", "description": "List of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is skipFallback is not set to true.\n", "markdownDescription": "List of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is `skipFallback` is not set to true.", "x-intellij-html-description": "\u003cp\u003eList of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is \u003ccode\u003eskipFallback\u003c/code\u003e is not set to true.\u003c/p\u003e\n" }, "skipFallback": { "type": "boolean", "title": "skipFallback", "description": "Skip fallback to the original registry if none of the mirrors are available\nor contain the requested image.\n", "markdownDescription": "Skip fallback to the original registry if none of the mirrors are available\nor contain the requested image.", "x-intellij-html-description": "\u003cp\u003eSkip fallback to the original registry if none of the mirrors are available\nor contain the requested image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryMirrorConfig configures an image registry mirror." }, "cri.RegistryTLSConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryTLSConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry endpoint to apply the TLS configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. ‘my-mirror.local:5000’ for ‘https://my-mirror.local:5000/v2/’.\n\nThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\n", "markdownDescription": "Registry endpoint to apply the TLS configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", "x-intellij-html-description": "\u003cp\u003eRegistry endpoint to apply the TLS configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry endpoint is the hostname part of the endpoint URL,\ne.g. \u0026lsquo;my-mirror.local:5000\u0026rsquo; for \u0026lsquo;\u003ca href=\"https://my-mirror.local:5000/v2/'\" target=\"_blank\"\u003ehttps://my-mirror.local:5000/v2/\u0026rsquo;\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\u003c/p\u003e\n" }, "clientIdentity": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "clientIdentity", "description": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.\n", "markdownDescription": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.", "x-intellij-html-description": "\u003cp\u003eEnable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.\u003c/p\u003e\n" }, "ca": { "type": "string", "title": "ca", "description": "CA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.\n", "markdownDescription": "CA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.", "x-intellij-html-description": "\u003cp\u003eCA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.\u003c/p\u003e\n" }, "insecureSkipVerify": { "type": "boolean", "title": "insecureSkipVerify", "description": "Skip TLS server certificate verification (not recommended).\n", "markdownDescription": "Skip TLS server certificate verification (not recommended).", "x-intellij-html-description": "\u003cp\u003eSkip TLS server certificate verification (not recommended).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryTLSConfig configures TLS for a registry endpoint." }, "extensions.ConfigFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The content of the extension service config file.\n", "markdownDescription": "The content of the extension service config file.", "x-intellij-html-description": "\u003cp\u003eThe content of the extension service config file.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "The mount path of the extension service config file.\n", "markdownDescription": "The mount path of the extension service config file.", "x-intellij-html-description": "\u003cp\u003eThe mount path of the extension service config file.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ConfigFile is a config file for extension services." }, "extensions.ServiceConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExtensionServiceConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the extension service.\n", "markdownDescription": "Name of the extension service.", "x-intellij-html-description": "\u003cp\u003eName of the extension service.\u003c/p\u003e\n" }, "configFiles": { "items": { "$ref": "#/$defs/extensions.ConfigFile" }, "type": "array", "title": "configFiles", "description": "The config files for the extension service.\n", "markdownDescription": "The config files for the extension service.", "x-intellij-html-description": "\u003cp\u003eThe config files for the extension service.\u003c/p\u003e\n" }, "environment": { "items": { "type": "string" }, "type": "array", "title": "environment", "description": "The environment for the extension service.\n", "markdownDescription": "The environment for the extension service.", "x-intellij-html-description": "\u003cp\u003eThe environment for the extension service.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "ExtensionServiceConfig is a extensionserviceconfig document." }, "hardware.PCIDriverRebindConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "PCIDriverRebindConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "PCI device id\n", "markdownDescription": "PCI device id", "x-intellij-html-description": "\u003cp\u003ePCI device id\u003c/p\u003e\n" }, "targetDriver": { "type": "string", "title": "targetDriver", "description": "Target driver to rebind the PCI device to.\n", "markdownDescription": "Target driver to rebind the PCI device to.", "x-intellij-html-description": "\u003cp\u003eTarget driver to rebind the PCI device to.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "targetDriver" ], "description": "PCIDriverRebindConfig allows to configure PCI driver rebinds." }, "network.AddressConfig": { "properties": { "address": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "address", "description": "IP address to be assigned to the link.\n\nThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).\n", "markdownDescription": "IP address to be assigned to the link.\n\nThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).", "x-intellij-html-description": "\u003cp\u003eIP address to be assigned to the link.\u003c/p\u003e\n\n\u003cp\u003eThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).\u003c/p\u003e\n" }, "routePriority": { "type": "integer", "title": "routePriority", "description": "Configure the route priority (metric) for routes created for this address.\n\nIf not specified, the system default route priority will be used.\n", "markdownDescription": "Configure the route priority (metric) for routes created for this address.\n\nIf not specified, the system default route priority will be used.", "x-intellij-html-description": "\u003cp\u003eConfigure the route priority (metric) for routes created for this address.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default route priority will be used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "address" ], "description": "AddressConfig represents a network address configuration." }, "network.BlackholeRouteConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BlackholeRouteConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Route destination as an address prefix.\n", "markdownDescription": "Route destination as an address prefix.", "x-intellij-html-description": "\u003cp\u003eRoute destination as an address prefix.\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "BlackholeRouteConfig is a config document to configure blackhole routes." }, "network.BondConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BondConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the bond link (interface) to be created.\n", "markdownDescription": "Name of the bond link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the bond link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "bondMode": { "enum": [ "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb" ], "title": "bondMode", "description": "Bond mode.\n", "markdownDescription": "Bond mode.", "x-intellij-html-description": "\u003cp\u003eBond mode.\u003c/p\u003e\n" }, "miimon": { "type": "integer", "title": "miimon", "description": "Link monitoring frequency in milliseconds.\n", "markdownDescription": "Link monitoring frequency in milliseconds.", "x-intellij-html-description": "\u003cp\u003eLink monitoring frequency in milliseconds.\u003c/p\u003e\n" }, "updelay": { "type": "integer", "title": "updelay", "description": "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.\n", "markdownDescription": "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.", "x-intellij-html-description": "\u003cp\u003eThe time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.\u003c/p\u003e\n" }, "downdelay": { "type": "integer", "title": "downdelay", "description": "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.\n", "markdownDescription": "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.", "x-intellij-html-description": "\u003cp\u003eThe time, in milliseconds, to wait before disabling a slave after a link failure has been detected.\u003c/p\u003e\n" }, "useCarrier": { "type": "boolean", "title": "useCarrier", "description": "Specifies whether or not miimon should use MII or ETHTOOL.\n", "markdownDescription": "Specifies whether or not miimon should use MII or ETHTOOL.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether or not miimon should use MII or ETHTOOL.\u003c/p\u003e\n" }, "xmitHashPolicy": { "enum": [ "layer2", "layer3+4", "layer2+3", "encap2+3", "encap3+4" ], "title": "xmitHashPolicy", "description": "Selects the transmit hash policy to use for slave selection.\n", "markdownDescription": "Selects the transmit hash policy to use for slave selection.", "x-intellij-html-description": "\u003cp\u003eSelects the transmit hash policy to use for slave selection.\u003c/p\u003e\n" }, "arpInterval": { "type": "integer", "title": "arpInterval", "description": "ARP link monitoring frequency in milliseconds.\n", "markdownDescription": "ARP link monitoring frequency in milliseconds.", "x-intellij-html-description": "\u003cp\u003eARP link monitoring frequency in milliseconds.\u003c/p\u003e\n" }, "arpIpTargets": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+$" }, "type": "array", "title": "arpIpTargets", "description": "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\n", "markdownDescription": "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", "x-intellij-html-description": "\u003cp\u003eThe list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\u003c/p\u003e\n" }, "nsIp6Targets": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+$" }, "type": "array", "title": "nsIp6Targets", "description": "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\n", "markdownDescription": "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", "x-intellij-html-description": "\u003cp\u003eThe list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\u003c/p\u003e\n" }, "arpValidate": { "enum": [ "none", "active", "backup", "all", "filter", "filter-active", "filter-backup" ], "title": "arpValidate", "description": "Specifies whether or not ARP probes and replies should be validated.\n", "markdownDescription": "Specifies whether or not ARP probes and replies should be validated.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether or not ARP probes and replies should be validated.\u003c/p\u003e\n" }, "arpAllTargets": { "enum": [ "any", "all" ], "title": "arpAllTargets", "description": "Specifies whether ARP probes should be sent to any or all targets.\n", "markdownDescription": "Specifies whether ARP probes should be sent to any or all targets.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether ARP probes should be sent to any or all targets.\u003c/p\u003e\n" }, "lacpRate": { "enum": [ "slow", "fast" ], "title": "lacpRate", "description": "LACPDU frames periodic transmission rate.\n", "markdownDescription": "LACPDU frames periodic transmission rate.", "x-intellij-html-description": "\u003cp\u003eLACPDU frames periodic transmission rate.\u003c/p\u003e\n" }, "failOverMac": { "enum": [ "none", "active", "follow" ], "title": "failOverMac", "description": "Specifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.\n", "markdownDescription": "Specifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.\u003c/p\u003e\n" }, "adSelect": { "enum": [ "stable", "bandwidth", "count" ], "title": "adSelect", "description": "Aggregate selection policy for 802.3ad.\n", "markdownDescription": "Aggregate selection policy for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eAggregate selection policy for 802.3ad.\u003c/p\u003e\n" }, "adActorSysPrio": { "type": "integer", "title": "adActorSysPrio", "description": "Actor system priority for 802.3ad.\n", "markdownDescription": "Actor system priority for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eActor system priority for 802.3ad.\u003c/p\u003e\n" }, "adUserPortKey": { "type": "integer", "title": "adUserPortKey", "description": "User port key (upper 10 bits) for 802.3ad.\n", "markdownDescription": "User port key (upper 10 bits) for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eUser port key (upper 10 bits) for 802.3ad.\u003c/p\u003e\n" }, "adLACPActive": { "enum": [ "on", "off" ], "title": "adLACPActive", "description": "Whether to send LACPDU frames periodically.\n", "markdownDescription": "Whether to send LACPDU frames periodically.", "x-intellij-html-description": "\u003cp\u003eWhether to send LACPDU frames periodically.\u003c/p\u003e\n" }, "primaryReselect": { "enum": [ "always", "better", "failure" ], "title": "primaryReselect", "description": "Policy under which the primary slave should be reselected.\n", "markdownDescription": "Policy under which the primary slave should be reselected.", "x-intellij-html-description": "\u003cp\u003ePolicy under which the primary slave should be reselected.\u003c/p\u003e\n" }, "resendIGMP": { "type": "integer", "title": "resendIGMP", "description": "The number of times IGMP packets should be resent.\n", "markdownDescription": "The number of times IGMP packets should be resent.", "x-intellij-html-description": "\u003cp\u003eThe number of times IGMP packets should be resent.\u003c/p\u003e\n" }, "minLinks": { "type": "integer", "title": "minLinks", "description": "The minimum number of active links required for the bond to be considered active.\n", "markdownDescription": "The minimum number of active links required for the bond to be considered active.", "x-intellij-html-description": "\u003cp\u003eThe minimum number of active links required for the bond to be considered active.\u003c/p\u003e\n" }, "lpInterval": { "type": "integer", "title": "lpInterval", "description": "The number of seconds between instances where the bonding driver sends learning packets to each slave’s peer switch.\n", "markdownDescription": "The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch.", "x-intellij-html-description": "\u003cp\u003eThe number of seconds between instances where the bonding driver sends learning packets to each slave\u0026rsquo;s peer switch.\u003c/p\u003e\n" }, "packetsPerSlave": { "type": "integer", "title": "packetsPerSlave", "description": "The number of packets to transmit through a slave before moving to the next one.\n", "markdownDescription": "The number of packets to transmit through a slave before moving to the next one.", "x-intellij-html-description": "\u003cp\u003eThe number of packets to transmit through a slave before moving to the next one.\u003c/p\u003e\n" }, "numPeerNotif": { "type": "integer", "title": "numPeerNotif", "description": "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.\n", "markdownDescription": "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.", "x-intellij-html-description": "\u003cp\u003eThe number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.\u003c/p\u003e\n" }, "tlbLogicalLb": { "type": "integer", "title": "tlbLogicalLb", "description": "Whether dynamic shuffling of flows is enabled in tlb or alb mode.\n", "markdownDescription": "Whether dynamic shuffling of flows is enabled in tlb or alb mode.", "x-intellij-html-description": "\u003cp\u003eWhether dynamic shuffling of flows is enabled in tlb or alb mode.\u003c/p\u003e\n" }, "allSlavesActive": { "type": "integer", "title": "allSlavesActive", "description": "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).\n", "markdownDescription": "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).", "x-intellij-html-description": "\u003cp\u003eWhether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).\u003c/p\u003e\n" }, "peerNotifDelay": { "type": "integer", "title": "peerNotifDelay", "description": "The delay, in milliseconds, between each peer notification.\n", "markdownDescription": "The delay, in milliseconds, between each peer notification.", "x-intellij-html-description": "\u003cp\u003eThe delay, in milliseconds, between each peer notification.\u003c/p\u003e\n" }, "missedMax": { "type": "integer", "title": "missedMax", "description": "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.\n", "markdownDescription": "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.", "x-intellij-html-description": "\u003cp\u003eThe number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "bondMode", "kind", "links", "name" ], "description": "BondConfig is a config document to create a bond (link aggregation) over a set of links." }, "network.BridgeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BridgeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the bridge link (interface) to be created.\n", "markdownDescription": "Name of the bridge link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the bridge link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "stp": { "$ref": "#/$defs/network.BridgeSTPConfig", "title": "stp", "description": "Bridge STP (Spanning Tree Protocol) configuration.\n", "markdownDescription": "Bridge STP (Spanning Tree Protocol) configuration.", "x-intellij-html-description": "\u003cp\u003eBridge STP (Spanning Tree Protocol) configuration.\u003c/p\u003e\n" }, "vlan": { "$ref": "#/$defs/network.BridgeVLANConfig", "title": "vlan", "description": "Bridge VLAN configuration.\n", "markdownDescription": "Bridge VLAN configuration.", "x-intellij-html-description": "\u003cp\u003eBridge VLAN configuration.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "links", "name" ], "description": "BridgeConfig is a config document to create a Bridge (link aggregation) over a set of links." }, "network.BridgeSTPConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable or disable STP on the bridge.\n", "markdownDescription": "Enable or disable STP on the bridge.", "x-intellij-html-description": "\u003cp\u003eEnable or disable STP on the bridge.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration." }, "network.BridgeVLANConfig": { "properties": { "filtering": { "type": "boolean", "title": "filtering", "description": "Enable or disable VLAN filtering on the bridge.\n", "markdownDescription": "Enable or disable VLAN filtering on the bridge.", "x-intellij-html-description": "\u003cp\u003eEnable or disable VLAN filtering on the bridge.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "BridgeVLANConfig is a bridge VLAN configuration." }, "network.CommonLinkConfig": { "properties": { "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CommonLinkConfig is common configuration for network links, and logical links." }, "network.CommonProbeConfig": { "properties": { "interval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "interval", "description": "Interval between probe attempts.\nDefaults to 1s.\n", "markdownDescription": "Interval between probe attempts.\nDefaults to 1s.", "x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n" }, "failureThreshold": { "type": "integer", "title": "failureThreshold", "description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n", "markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).", "x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CommonProbeConfig holds fields common to all probe types." }, "network.DHCPv4ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DHCPv4Config" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "routeMetric": { "type": "integer", "title": "routeMetric", "description": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.\n", "markdownDescription": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", "x-intellij-html-description": "\u003cp\u003eAn optional metric for the routes received from the DHCP server.\u003c/p\u003e\n\n\u003cp\u003eLower values indicate higher priority.\nDefault value is 1024.\u003c/p\u003e\n" }, "ignoreHostname": { "type": "boolean", "title": "ignoreHostname", "description": "Ignore hostname received from the DHCP server.\n", "markdownDescription": "Ignore hostname received from the DHCP server.", "x-intellij-html-description": "\u003cp\u003eIgnore hostname received from the DHCP server.\u003c/p\u003e\n" }, "clientIdentifier": { "enum": [ "none", "mac", "duid" ], "title": "clientIdentifier", "description": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to ‘mac’ if not set.\n", "markdownDescription": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", "x-intellij-html-description": "\u003cp\u003eClient identifier to use when communicating with DHCP servers.\u003c/p\u003e\n\n\u003cp\u003eDefaults to \u0026lsquo;mac\u0026rsquo; if not set.\u003c/p\u003e\n" }, "duidRaw": { "type": "string", "pattern": "^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$", "title": "duidRaw", "description": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if ‘clientIdentifier’ is set to ‘duid’.\n", "markdownDescription": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", "x-intellij-html-description": "\u003cp\u003eRaw value of the DUID to use as client identifier.\u003c/p\u003e\n\n\u003cp\u003eThis field is only used if \u0026lsquo;clientIdentifier\u0026rsquo; is set to \u0026lsquo;duid\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DHCPv4Config is a config document to configure DHCPv4 on a network link." }, "network.DHCPv6ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DHCPv6Config" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "routeMetric": { "type": "integer", "title": "routeMetric", "description": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.\n", "markdownDescription": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", "x-intellij-html-description": "\u003cp\u003eAn optional metric for the routes received from the DHCP server.\u003c/p\u003e\n\n\u003cp\u003eLower values indicate higher priority.\nDefault value is 1024.\u003c/p\u003e\n" }, "ignoreHostname": { "type": "boolean", "title": "ignoreHostname", "description": "Ignore hostname received from the DHCP server.\n", "markdownDescription": "Ignore hostname received from the DHCP server.", "x-intellij-html-description": "\u003cp\u003eIgnore hostname received from the DHCP server.\u003c/p\u003e\n" }, "clientIdentifier": { "enum": [ "none", "mac", "duid" ], "title": "clientIdentifier", "description": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to ‘mac’ if not set.\n", "markdownDescription": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", "x-intellij-html-description": "\u003cp\u003eClient identifier to use when communicating with DHCP servers.\u003c/p\u003e\n\n\u003cp\u003eDefaults to \u0026lsquo;mac\u0026rsquo; if not set.\u003c/p\u003e\n" }, "duidRaw": { "type": "string", "pattern": "^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$", "title": "duidRaw", "description": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if ‘clientIdentifier’ is set to ‘duid’.\n", "markdownDescription": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", "x-intellij-html-description": "\u003cp\u003eRaw value of the DUID to use as client identifier.\u003c/p\u003e\n\n\u003cp\u003eThis field is only used if \u0026lsquo;clientIdentifier\u0026rsquo; is set to \u0026lsquo;duid\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DHCPv6Config is a config document to configure DHCPv6 on a network link." }, "network.DefaultActionConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "NetworkDefaultActionConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "ingress": { "enum": [ "accept", "block" ], "title": "ingress", "description": "Default action for all not explicitly configured ingress traffic: accept or block.\n", "markdownDescription": "Default action for all not explicitly configured ingress traffic: accept or block.", "x-intellij-html-description": "\u003cp\u003eDefault action for all not explicitly configured ingress traffic: accept or block.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "NetworkDefaultActionConfig is a ingress firewall default action configuration document." }, "network.DummyLinkConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DummyLinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the dummy link (interface).\n", "markdownDescription": "Name of the dummy link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the dummy link (interface).\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DummyLinkConfig is a config document to create a dummy (virtual) network link." }, "network.EthernetChannelsConfig": { "properties": { "rx": { "type": "integer", "title": "rx", "description": "Number of RX channels.\n", "markdownDescription": "Number of RX channels.", "x-intellij-html-description": "\u003cp\u003eNumber of RX channels.\u003c/p\u003e\n" }, "tx": { "type": "integer", "title": "tx", "description": "Number of TX channels.\n", "markdownDescription": "Number of TX channels.", "x-intellij-html-description": "\u003cp\u003eNumber of TX channels.\u003c/p\u003e\n" }, "other": { "type": "integer", "title": "other", "description": "Number of other channels.\n", "markdownDescription": "Number of other channels.", "x-intellij-html-description": "\u003cp\u003eNumber of other channels.\u003c/p\u003e\n" }, "combined": { "type": "integer", "title": "combined", "description": "Number of combined channels.\n", "markdownDescription": "Number of combined channels.", "x-intellij-html-description": "\u003cp\u003eNumber of combined channels.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EthernetChannelsConfig is a configuration for Ethernet link channels." }, "network.EthernetConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EthernetConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "features": { "patternProperties": { ".*": { "type": "boolean" } }, "type": "object", "title": "features", "description": "Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse talosctl get ethernetstatus \u0026lt;link\u0026gt; -o yaml to get the list of available features and\ntheir current status.\n", "markdownDescription": "Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse `talosctl get ethernetstatus \u003clink\u003e -o yaml` to get the list of available features and\ntheir current status.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet features.\u003c/p\u003e\n\n\u003cp\u003eSet of features available and whether they can be enabled or disabled is driver specific.\nUse \u003ccode\u003etalosctl get ethernetstatus \u0026lt;link\u0026gt; -o yaml\u003c/code\u003e to get the list of available features and\ntheir current status.\u003c/p\u003e\n" }, "rings": { "$ref": "#/$defs/network.EthernetRingsConfig", "title": "rings", "description": "Configuration for Ethernet link rings.\n\nThis is similar to ethtool -G command.\n", "markdownDescription": "Configuration for Ethernet link rings.\n\nThis is similar to `ethtool -G` command.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet link rings.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -G\u003c/code\u003e command.\u003c/p\u003e\n" }, "channels": { "$ref": "#/$defs/network.EthernetChannelsConfig", "title": "channels", "description": "Configuration for Ethernet link channels.\n\nThis is similar to ethtool -L command.\n", "markdownDescription": "Configuration for Ethernet link channels.\n\nThis is similar to `ethtool -L` command.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet link channels.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -L\u003c/code\u003e command.\u003c/p\u003e\n" }, "wakeOnLan": { "items": { "type": "string" }, "type": "array", "title": "wakeOnLan", "description": "Wake-on-LAN modes to enable.\n\nIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\n\nThis is similar to ethtool -s \u0026lt;link\u0026gt; wol \u0026lt;options\u0026gt; command.\n", "markdownDescription": "Wake-on-LAN modes to enable.\n\nIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\n\nThis is similar to `ethtool -s \u003clink\u003e wol \u003coptions\u003e` command.", "x-intellij-html-description": "\u003cp\u003eWake-on-LAN modes to enable.\u003c/p\u003e\n\n\u003cp\u003eIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -s \u0026lt;link\u0026gt; wol \u0026lt;options\u0026gt;\u003c/code\u003e command.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "EthernetConfig is a config document to configure Ethernet interfaces." }, "network.EthernetRingsConfig": { "properties": { "rx": { "type": "integer", "title": "rx", "description": "Number of RX rings.\n", "markdownDescription": "Number of RX rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX rings.\u003c/p\u003e\n" }, "tx": { "type": "integer", "title": "tx", "description": "Number of TX rings.\n", "markdownDescription": "Number of TX rings.", "x-intellij-html-description": "\u003cp\u003eNumber of TX rings.\u003c/p\u003e\n" }, "rx-mini": { "type": "integer", "title": "rx-mini", "description": "Number of RX mini rings.\n", "markdownDescription": "Number of RX mini rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX mini rings.\u003c/p\u003e\n" }, "rx-jumbo": { "type": "integer", "title": "rx-jumbo", "description": "Number of RX jumbo rings.\n", "markdownDescription": "Number of RX jumbo rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX jumbo rings.\u003c/p\u003e\n" }, "rx-buf-len": { "type": "integer", "title": "rx-buf-len", "description": "RX buffer length.\n", "markdownDescription": "RX buffer length.", "x-intellij-html-description": "\u003cp\u003eRX buffer length.\u003c/p\u003e\n" }, "cqe-size": { "type": "integer", "title": "cqe-size", "description": "CQE size.\n", "markdownDescription": "CQE size.", "x-intellij-html-description": "\u003cp\u003eCQE size.\u003c/p\u003e\n" }, "tx-push": { "type": "boolean", "title": "tx-push", "description": "TX push enabled.\n", "markdownDescription": "TX push enabled.", "x-intellij-html-description": "\u003cp\u003eTX push enabled.\u003c/p\u003e\n" }, "rx-push": { "type": "boolean", "title": "rx-push", "description": "RX push enabled.\n", "markdownDescription": "RX push enabled.", "x-intellij-html-description": "\u003cp\u003eRX push enabled.\u003c/p\u003e\n" }, "tx-push-buf-len": { "type": "integer", "title": "tx-push-buf-len", "description": "TX push buffer length.\n", "markdownDescription": "TX push buffer length.", "x-intellij-html-description": "\u003cp\u003eTX push buffer length.\u003c/p\u003e\n" }, "tcp-data-split": { "type": "boolean", "title": "tcp-data-split", "description": "TCP data split enabled.\n", "markdownDescription": "TCP data split enabled.", "x-intellij-html-description": "\u003cp\u003eTCP data split enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EthernetRingsConfig is a configuration for Ethernet link rings." }, "network.HCloudVIPConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "HCloudVIPConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address to be advertised as a Layer 2 VIP.\n", "markdownDescription": "IP address to be advertised as a Layer 2 VIP.", "x-intellij-html-description": "\u003cp\u003eIP address to be advertised as a Layer 2 VIP.\u003c/p\u003e\n" }, "link": { "type": "string", "title": "link", "description": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eName of the link to assign the VIP to.\u003c/p\u003e\n\n\u003cp\u003eSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" }, "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Hetzner Cloud API Token.\n", "markdownDescription": "Specifies the Hetzner Cloud API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n" }, "network.HostnameConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "HostnameConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "auto": { "enum": [ "stable", "off" ], "title": "auto", "description": "A method to automatically generate a hostname for the machine.\n\nThere are two methods available:\n - stable - generates a stable hostname based on machine identity\n - off - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\n\nAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with hostname field.\n", "markdownDescription": "A method to automatically generate a hostname for the machine.\n\nThere are two methods available:\n - `stable` - generates a stable hostname based on machine identity\n - `off` - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\n\nAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `hostname` field.", "x-intellij-html-description": "\u003cp\u003eA method to automatically generate a hostname for the machine.\u003c/p\u003e\n\n\u003cp\u003eThere are two methods available:\n - \u003ccode\u003estable\u003c/code\u003e - generates a stable hostname based on machine identity\n - \u003ccode\u003eoff\u003c/code\u003e - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\u003c/p\u003e\n\n\u003cp\u003eAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with \u003ccode\u003ehostname\u003c/code\u003e field.\u003c/p\u003e\n" }, "hostname": { "type": "string", "title": "hostname", "description": "A static hostname to set for the machine.\n\nThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with auto field.\n", "markdownDescription": "A static hostname to set for the machine.\n\nThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `auto` field.", "x-intellij-html-description": "\u003cp\u003eA static hostname to set for the machine.\u003c/p\u003e\n\n\u003cp\u003eThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with \u003ccode\u003eauto\u003c/code\u003e field.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "HostnameConfig is a config document to configure the hostname: either a static hostname or an automatically generated hostname." }, "network.IngressRule": { "properties": { "subnet": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "subnet", "description": "Subnet defines a source subnet.\n", "markdownDescription": "Subnet defines a source subnet.", "x-intellij-html-description": "\u003cp\u003eSubnet defines a source subnet.\u003c/p\u003e\n" }, "except": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "except", "description": "Except defines a source subnet to exclude from the rule, it gets excluded from the subnet.\n", "markdownDescription": "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`.", "x-intellij-html-description": "\u003cp\u003eExcept defines a source subnet to exclude from the rule, it gets excluded from the \u003ccode\u003esubnet\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "IngressRule is a ingress rule." }, "network.KubeSpanConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KubeSpanConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.\n", "markdownDescription": "Enable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.", "x-intellij-html-description": "\u003cp\u003eEnable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.\u003c/p\u003e\n" }, "advertiseKubernetesNetworks": { "type": "boolean", "title": "advertiseKubernetesNetworks", "description": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.\n", "markdownDescription": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.", "x-intellij-html-description": "\u003cp\u003eControl whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.\u003c/p\u003e\n" }, "allowDownPeerBypass": { "type": "boolean", "title": "allowDownPeerBypass", "description": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.\n", "markdownDescription": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.", "x-intellij-html-description": "\u003cp\u003eSkip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.\u003c/p\u003e\n" }, "harvestExtraEndpoints": { "type": "boolean", "title": "harvestExtraEndpoints", "description": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u0026gt;50).\n", "markdownDescription": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u003e50).", "x-intellij-html-description": "\u003cp\u003eKubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u0026gt;50).\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "KubeSpan link MTU size.\nDefault value is 1420.\n", "markdownDescription": "KubeSpan link MTU size.\nDefault value is 1420.", "x-intellij-html-description": "\u003cp\u003eKubeSpan link MTU size.\nDefault value is 1420.\u003c/p\u003e\n" }, "filters": { "$ref": "#/$defs/network.KubeSpanFiltersConfig", "title": "filters", "description": "KubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.\n", "markdownDescription": "KubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.", "x-intellij-html-description": "\u003cp\u003eKubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KubeSpanConfig is a config document to configure KubeSpan." }, "network.KubeSpanFiltersConfig": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.\n", "markdownDescription": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\u003c/p\u003e\n\n\u003cp\u003eBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" }, "excludeAdvertisedNetworks": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$" }, "type": "array", "title": "excludeAdvertisedNetworks", "description": "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\n\nBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\n\nNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\n\nDefault value: no filtering.\n", "markdownDescription": "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\n\nBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\n\nNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\u003c/p\u003e\n\n\u003cp\u003eBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\u003c/p\u003e\n\n\u003cp\u003eNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeSpanFiltersConfig configures KubeSpan endpoint filters." }, "network.KubespanEndpointsConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KubeSpanEndpoints" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "extraAnnouncedEndpoints": { "items": { "type": "string" }, "type": "array", "title": "extraAnnouncedEndpoints", "description": "A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\n", "markdownDescription": "A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.", "x-intellij-html-description": "\u003cp\u003eA list of extra Wireguard endpoints to announce from this machine.\u003c/p\u003e\n\n\u003cp\u003eTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KubeSpanEndpointsConfig is a config document to configure KubeSpan endpoints." }, "network.Layer2VIPConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "Layer2VIPConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address to be advertised as a Layer 2 VIP.\n", "markdownDescription": "IP address to be advertised as a Layer 2 VIP.", "x-intellij-html-description": "\u003cp\u003eIP address to be advertised as a Layer 2 VIP.\u003c/p\u003e\n" }, "link": { "type": "string", "title": "link", "description": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eName of the link to assign the VIP to.\u003c/p\u003e\n\n\u003cp\u003eSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "Layer2VIPConfig is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using gratuitous ARP announcements for IPv4.\\n" }, "network.LinkAliasConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "LinkAliasConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Alias for the link.\n\nDon’t use system interface names like “eth0”, “ens3”, “enp0s2”, etc. as those may conflict\nwith existing physical interfaces.\n\nThe name can contain a single integer format verb (%d) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. net0, net1, …) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.\n", "markdownDescription": "Alias for the link.\n\nDon't use system interface names like \"eth0\", \"ens3\", \"enp0s2\", etc. as those may conflict\nwith existing physical interfaces.\n\nThe name can contain a single integer format verb (`%d`) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. `net0`, `net1`, ...) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.", "x-intellij-html-description": "\u003cp\u003eAlias for the link.\u003c/p\u003e\n\n\u003cp\u003eDon\u0026rsquo;t use system interface names like \u0026ldquo;eth0\u0026rdquo;, \u0026ldquo;ens3\u0026rdquo;, \u0026ldquo;enp0s2\u0026rdquo;, etc. as those may conflict\nwith existing physical interfaces.\u003c/p\u003e\n\n\u003cp\u003eThe name can contain a single integer format verb (\u003ccode\u003e%d\u003c/code\u003e) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. \u003ccode\u003enet0\u003c/code\u003e, \u003ccode\u003enet1\u003c/code\u003e, \u0026hellip;) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.\u003c/p\u003e\n" }, "selector": { "$ref": "#/$defs/network.LinkSelector", "title": "selector", "description": "Selector to match the link to alias.\n\nWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. net%d), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Selector to match the link to alias.\n\nWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. `net%d`), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eSelector to match the link to alias.\u003c/p\u003e\n\n\u003cp\u003eWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. \u003ccode\u003enet%d\u003c/code\u003e), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "LinkAliasConfig is a config document to alias (give a different name) to a physical link." }, "network.LinkConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "LinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "LinkConfig is a config document to configure physical interfaces (network links)." }, "network.LinkSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the link.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the link.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LinkSelector selects a link to alias." }, "network.NTPConfig": { "properties": { "servers": { "items": { "type": "string" }, "type": "array", "title": "servers", "description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to time.cloudflare.com.\n", "markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.", "x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "NTPConfig represents a NTP server configuration." }, "network.NameserverConfig": { "properties": { "address": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "address", "description": "The IP address of the nameserver.\n", "markdownDescription": "The IP address of the nameserver.", "x-intellij-html-description": "\u003cp\u003eThe IP address of the nameserver.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "NameserverConfig represents a single nameserver configuration." }, "network.PTPConfig": { "properties": { "devices": { "items": { "type": "string" }, "type": "array", "title": "devices", "description": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\nA PTP device is typically represented as a character device file in the /dev directory,\n\n\nsuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n", "markdownDescription": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.", "x-intellij-html-description": "\u003cp\u003edescription: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eA PTP device is typically represented as a character device file in the /dev directory,\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003esuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "PTPConfig represents a PTP (Precision Time Protocol) configuration." }, "network.ResolverConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ResolverConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "nameservers": { "items": { "$ref": "#/$defs/network.NameserverConfig" }, "type": "array", "title": "nameservers", "description": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\n", "markdownDescription": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.", "x-intellij-html-description": "\u003cp\u003eA list of nameservers (DNS servers) to use for resolving domain names.\u003c/p\u003e\n\n\u003cp\u003eNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\u003c/p\u003e\n" }, "searchDomains": { "$ref": "#/$defs/network.SearchDomainsConfig", "title": "searchDomains", "description": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.\n", "markdownDescription": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.", "x-intellij-html-description": "\u003cp\u003eConfiguration for search domains (in /etc/resolv.conf).\u003c/p\u003e\n\n\u003cp\u003eThe default is to derive search domains from the hostname FQDN.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ResolverConfig is a config document to configure DNS resolving." }, "network.RouteConfig": { "properties": { "destination": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "destination", "description": "The route’s destination as an address prefix.\n\nIf not specified, a default route will be created for the address family of the gateway.\n", "markdownDescription": "The route's destination as an address prefix.\n\nIf not specified, a default route will be created for the address family of the gateway.", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s destination as an address prefix.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, a default route will be created for the address family of the gateway.\u003c/p\u003e\n" }, "gateway": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "gateway", "description": "The route’s gateway (if empty, creates link scope route).\n", "markdownDescription": "The route's gateway (if empty, creates link scope route).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s gateway (if empty, creates link scope route).\u003c/p\u003e\n" }, "source": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "source", "description": "The route’s source address (optional).\n", "markdownDescription": "The route's source address (optional).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s source address (optional).\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The optional MTU for the route.\n", "markdownDescription": "The optional MTU for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional MTU for the route.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "The routing table to use for the route.\n\nIf not specified, the main routing table will be used.\n", "markdownDescription": "The routing table to use for the route.\n\nIf not specified, the main routing table will be used.", "x-intellij-html-description": "\u003cp\u003eThe routing table to use for the route.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the main routing table will be used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RouteConfig represents a network route configuration." }, "network.RoutingRuleConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RoutingRuleConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Priority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.\n", "markdownDescription": "Priority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.", "x-intellij-html-description": "\u003cp\u003ePriority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.\u003c/p\u003e\n" }, "src": { "type": "string", "title": "src", "description": "Source address prefix to match.\nIf empty, matches all sources.\n", "markdownDescription": "Source address prefix to match.\nIf empty, matches all sources.", "x-intellij-html-description": "\u003cp\u003eSource address prefix to match.\nIf empty, matches all sources.\u003c/p\u003e\n" }, "dst": { "type": "string", "title": "dst", "description": "Destination address prefix to match.\nIf empty, matches all destinations.\n", "markdownDescription": "Destination address prefix to match.\nIf empty, matches all destinations.", "x-intellij-html-description": "\u003cp\u003eDestination address prefix to match.\nIf empty, matches all destinations.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "The routing table to look up if the rule matches.\n", "markdownDescription": "The routing table to look up if the rule matches.", "x-intellij-html-description": "\u003cp\u003eThe routing table to look up if the rule matches.\u003c/p\u003e\n" }, "action": { "type": "integer", "title": "action", "description": "The action to perform when the rule matches.\nDefaults to “unicast” (table lookup).\n", "markdownDescription": "The action to perform when the rule matches.\nDefaults to \"unicast\" (table lookup).", "x-intellij-html-description": "\u003cp\u003eThe action to perform when the rule matches.\nDefaults to \u0026ldquo;unicast\u0026rdquo; (table lookup).\u003c/p\u003e\n" }, "iifName": { "type": "string", "title": "iifName", "description": "Match packets arriving on this interface.\n", "markdownDescription": "Match packets arriving on this interface.", "x-intellij-html-description": "\u003cp\u003eMatch packets arriving on this interface.\u003c/p\u003e\n" }, "oifName": { "type": "string", "title": "oifName", "description": "Match packets going out on this interface.\n", "markdownDescription": "Match packets going out on this interface.", "x-intellij-html-description": "\u003cp\u003eMatch packets going out on this interface.\u003c/p\u003e\n" }, "fwMark": { "type": "integer", "title": "fwMark", "description": "Match packets with this firewall mark value.\n", "markdownDescription": "Match packets with this firewall mark value.", "x-intellij-html-description": "\u003cp\u003eMatch packets with this firewall mark value.\u003c/p\u003e\n" }, "fwMask": { "type": "integer", "title": "fwMask", "description": "Mask for the firewall mark comparison.\n", "markdownDescription": "Mask for the firewall mark comparison.", "x-intellij-html-description": "\u003cp\u003eMask for the firewall mark comparison.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "table" ], "description": "RoutingRuleConfig is a config document to configure Linux policy routing rules." }, "network.RuleConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "NetworkRuleConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "portSelector": { "$ref": "#/$defs/network.RulePortSelector", "title": "portSelector", "description": "Port selector defines which ports and protocols on the host are affected by the rule.\n", "markdownDescription": "Port selector defines which ports and protocols on the host are affected by the rule.", "x-intellij-html-description": "\u003cp\u003ePort selector defines which ports and protocols on the host are affected by the rule.\u003c/p\u003e\n" }, "ingress": { "items": { "$ref": "#/$defs/network.IngressRule" }, "type": "array", "title": "ingress", "description": "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the portSelector.\n", "markdownDescription": "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.", "x-intellij-html-description": "\u003cp\u003eIngress defines which source subnets are allowed to access the host ports/protocols defined by the \u003ccode\u003eportSelector\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "NetworkRuleConfig is a network firewall rule config document." }, "network.RulePortSelector": { "properties": { "ports": { "items": { "oneOf": [ { "type": "integer" }, { "type": "string" } ] }, "type": "array", "title": "ports", "description": "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\n", "markdownDescription": "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.", "x-intellij-html-description": "\u003cp\u003ePorts defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\u003c/p\u003e\n" }, "protocol": { "enum": [ "tcp", "udp", "icmp", "icmpv6" ], "title": "protocol", "description": "Protocol defines traffic protocol (e.g. TCP or UDP).\n", "markdownDescription": "Protocol defines traffic protocol (e.g. TCP or UDP).", "x-intellij-html-description": "\u003cp\u003eProtocol defines traffic protocol (e.g. TCP or UDP).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RulePortSelector is a port selector for the network rule." }, "network.SearchDomainsConfig": { "properties": { "domains": { "items": { "type": "string" }, "type": "array", "title": "domains", "description": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if “example.com” is a search domain and a user tries to resolve\n“host”, the system will attempt to resolve “host.example.com”.\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\n", "markdownDescription": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.", "x-intellij-html-description": "\u003cp\u003eA list of search domains to be used for DNS resolution.\u003c/p\u003e\n\n\u003cp\u003eSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \u0026ldquo;example.com\u0026rdquo; is a search domain and a user tries to resolve\n\u0026ldquo;host\u0026rdquo;, the system will attempt to resolve \u0026ldquo;host.example.com\u0026rdquo;.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\u003c/p\u003e\n" }, "disableDefault": { "type": "boolean", "title": "disableDefault", "description": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\n", "markdownDescription": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.", "x-intellij-html-description": "\u003cp\u003eDisable default search domain configuration from hostname FQDN.\u003c/p\u003e\n\n\u003cp\u003eWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "SearchDomainsConfig represents search domains configuration." }, "network.StaticHostConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "StaticHostConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address (IPv4 or IPv6) to map the hostnames to.\n", "markdownDescription": "IP address (IPv4 or IPv6) to map the hostnames to.", "x-intellij-html-description": "\u003cp\u003eIP address (IPv4 or IPv6) to map the hostnames to.\u003c/p\u003e\n" }, "hostnames": { "items": { "type": "string" }, "type": "array", "title": "hostnames", "description": "List of hostnames to map to the IP address.\n", "markdownDescription": "List of hostnames to map to the IP address.", "x-intellij-html-description": "\u003cp\u003eList of hostnames to map to the IP address.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "StaticHostConfig is a config document to set /etc/hosts entries." }, "network.TCPProbeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TCPProbeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the probe.\n", "markdownDescription": "Name of the probe.", "x-intellij-html-description": "\u003cp\u003eName of the probe.\u003c/p\u003e\n" }, "interval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "interval", "description": "Interval between probe attempts.\nDefaults to 1s.\n", "markdownDescription": "Interval between probe attempts.\nDefaults to 1s.", "x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n" }, "failureThreshold": { "type": "integer", "title": "failureThreshold", "description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n", "markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).", "x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "Endpoint to probe in the format host:port.\n", "markdownDescription": "Endpoint to probe in the format host:port.", "x-intellij-html-description": "\u003cp\u003eEndpoint to probe in the format host:port.\u003c/p\u003e\n" }, "timeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "timeout", "description": "Timeout for the probe.\nDefaults to 10s.\n", "markdownDescription": "Timeout for the probe.\nDefaults to 10s.", "x-intellij-html-description": "\u003cp\u003eTimeout for the probe.\nDefaults to 10s.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "endpoint", "kind", "name" ], "description": "TCPProbeConfig is a config document to configure network TCP connectivity probes." }, "network.TimeSyncConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TimeSyncConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "enabled": { "type": "boolean", "title": "enabled", "description": "Indicates if the time synchronization is enabled for the machine.\nDefaults to true.\n", "markdownDescription": "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the time synchronization is enabled for the machine.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "bootTimeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "bootTimeout", "description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n", "markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)", "x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n" }, "ntp": { "$ref": "#/$defs/network.NTPConfig", "title": "ntp", "description": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\n", "markdownDescription": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.", "x-intellij-html-description": "\u003cp\u003eSpecifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\u003c/p\u003e\n" }, "ptp": { "$ref": "#/$defs/network.PTPConfig", "title": "ptp", "description": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\n", "markdownDescription": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.", "x-intellij-html-description": "\u003cp\u003eSpecific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "TimeSyncConfig is a config document to configure time synchronization (NTP)." }, "network.VLANConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VLANConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the VLAN link (interface) to be created.\n", "markdownDescription": "Name of the VLAN link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the VLAN link (interface) to be created.\u003c/p\u003e\n" }, "vlanID": { "type": "integer", "title": "vlanID", "description": "VLAN ID to be used for the VLAN link.\n", "markdownDescription": "VLAN ID to be used for the VLAN link.", "x-intellij-html-description": "\u003cp\u003eVLAN ID to be used for the VLAN link.\u003c/p\u003e\n" }, "vlanMode": { "enum": [ "802.1q", "802.1ad" ], "title": "vlanMode", "description": "Set the VLAN mode to use.\nIf not set, defaults to ‘802.1q’.\n", "markdownDescription": "Set the VLAN mode to use.\nIf not set, defaults to '802.1q'.", "x-intellij-html-description": "\u003cp\u003eSet the VLAN mode to use.\nIf not set, defaults to \u0026lsquo;802.1q\u0026rsquo;.\u003c/p\u003e\n" }, "parent": { "type": "string", "title": "parent", "description": "Name of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.\n", "markdownDescription": "Name of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eName of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "parent", "vlanID" ], "description": "VLANConfig is a config document to create a VLAN (virtual LAN) over a parent link." }, "network.VRFConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VRFConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the vrf link (interface) to be created.\n", "markdownDescription": "Name of the vrf link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the vrf link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "Routing table number to use for this vrf.\n", "markdownDescription": "Routing table number to use for this vrf.", "x-intellij-html-description": "\u003cp\u003eRouting table number to use for this vrf.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "links", "name", "table" ], "description": "VRFConfig is a config document to create a vrf and assign links to it." }, "network.WireguardConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "WireguardConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the Wireguard link (interface).\n", "markdownDescription": "Name of the Wireguard link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the Wireguard link (interface).\u003c/p\u003e\n" }, "privateKey": { "type": "string", "title": "privateKey", "description": "Specifies a private key configuration (base64 encoded).\nCan be generated by wg genkey.\n", "markdownDescription": "Specifies a private key configuration (base64 encoded).\nCan be generated by `wg genkey`.", "x-intellij-html-description": "\u003cp\u003eSpecifies a private key configuration (base64 encoded).\nCan be generated by \u003ccode\u003ewg genkey\u003c/code\u003e.\u003c/p\u003e\n" }, "listenPort": { "type": "integer", "title": "listenPort", "description": "Specifies a device’s listening port (UDP).\nIf not specified, a random port will be chosen.\n", "markdownDescription": "Specifies a device's listening port (UDP).\nIf not specified, a random port will be chosen.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s listening port (UDP).\nIf not specified, a random port will be chosen.\u003c/p\u003e\n" }, "firewallMark": { "type": "integer", "title": "firewallMark", "description": "Specifies a device’s firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.\n", "markdownDescription": "Specifies a device's firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.\u003c/p\u003e\n" }, "peers": { "items": { "$ref": "#/$defs/network.WireguardPeer" }, "type": "array", "title": "peers", "description": "Specifies a list of peer configurations to apply to a device.\n", "markdownDescription": "Specifies a list of peer configurations to apply to a device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a list of peer configurations to apply to a device.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "privateKey" ], "description": "WireguardConfig is a config document to create and configure a Wireguard network link." }, "network.WireguardPeer": { "properties": { "publicKey": { "type": "string", "title": "publicKey", "description": "Specifies the public key of this peer.\nCan be extracted from private key by running wg pubkey \u0026lt; private.key.\n", "markdownDescription": "Specifies the public key of this peer.\nCan be extracted from private key by running `wg pubkey \u003c private.key`.", "x-intellij-html-description": "\u003cp\u003eSpecifies the public key of this peer.\nCan be extracted from private key by running \u003ccode\u003ewg pubkey \u0026lt; private.key\u003c/code\u003e.\u003c/p\u003e\n" }, "presharedKey": { "type": "string", "title": "presharedKey", "description": "Specifies the preshared key for this peer (base64 encoded).\nCan be generated by wg genpsk.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.\n", "markdownDescription": "Specifies the preshared key for this peer (base64 encoded).\nCan be generated by `wg genpsk`.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.", "x-intellij-html-description": "\u003cp\u003eSpecifies the preshared key for this peer (base64 encoded).\nCan be generated by \u003ccode\u003ewg genpsk\u003c/code\u003e.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "pattern": "^([0-9a-f.:]+|\\[[0-9a-f:.]+\\]):\\d{1,5}$", "title": "endpoint", "description": "Specifies the endpoint of this peer entry.\nFormat: :.\nIf not set, the peer should connect to us without us connecting to it first.\n", "markdownDescription": "Specifies the endpoint of this peer entry.\nFormat: \u003cIP address\u003e:\u003cport\u003e.\nIf not set, the peer should connect to us without us connecting to it first.", "x-intellij-html-description": "\u003cp\u003eSpecifies the endpoint of this peer entry.\nFormat: \u003cIP address\u003e:\u003cport\u003e.\nIf not set, the peer should connect to us without us connecting to it first.\u003c/p\u003e\n" }, "persistentKeepaliveInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "persistentKeepaliveInterval", "description": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eSpecifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" }, "allowedIPs": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$" }, "type": "array", "title": "allowedIPs", "description": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.\n", "markdownDescription": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.", "x-intellij-html-description": "\u003cp\u003eAllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "publicKey" ], "description": "WireguardPeer describes a Wireguard peer configuration." }, "runtime.EnvironmentV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EnvironmentConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "variables": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "variables", "description": "This field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore () character, and\n - contain only uppercase and lowercase letters, underscore () characters, and numbers.\n", "markdownDescription": "This field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore (_) character, and\n - contain only uppercase and lowercase letters, underscore (_) characters, and numbers.", "x-intellij-html-description": "\u003cp\u003eThis field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore (\u003cem\u003e) character, and\n - contain only uppercase and lowercase letters, underscore (\u003c/em\u003e) characters, and numbers.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "EnvironmentConfig is an environment config document." }, "runtime.EventSinkV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EventSinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "The endpoint for the event sink as ‘host:port’.\n", "markdownDescription": "The endpoint for the event sink as 'host:port'.", "x-intellij-html-description": "\u003cp\u003eThe endpoint for the event sink as \u0026lsquo;host:port\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "EventSinkConfig is a event sink config document." }, "runtime.KmsgLogV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KmsgLogConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "url": { "type": "string", "pattern": "^(tcp|udp)://", "title": "url", "description": "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\n", "markdownDescription": "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.", "x-intellij-html-description": "\u003cp\u003eThe URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KmsgLogConfig is a event sink config document." }, "runtime.OOMV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "OOMConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "triggerExpression": { "type": "string", "title": "triggerExpression", "description": "This expression defines when to trigger OOM action.\n\nThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\n\nThis expression receives the following parameters:\n- memory{some,full}{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event\n", "markdownDescription": "This expression defines when to trigger OOM action.\n\nThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\n\nThis expression receives the following parameters:\n- memory_{some,full}_{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event", "x-intellij-html-description": "\u003cp\u003eThis expression defines when to trigger OOM action.\u003c/p\u003e\n\n\u003cp\u003eThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\u003c/p\u003e\n\n\u003cp\u003eThis expression receives the following parameters:\n- memory\u003cem\u003e{some,full}\u003c/em\u003e{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event\u003c/p\u003e\n" }, "cgroupRankingExpression": { "type": "string", "title": "cgroupRankingExpression", "description": "This expression defines how to rank cgroups for OOM handler.\n\nThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\n\nThis expression receives the following parameters:\n- memory_max - Optional - in bytes\n- memory_current - Optional - in bytes\n- memory_peak - Optional - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System\n", "markdownDescription": "This expression defines how to rank cgroups for OOM handler.\n\nThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\n\nThis expression receives the following parameters:\n- memory_max - Optional\u003cuint\u003e - in bytes\n- memory_current - Optional\u003cuint\u003e - in bytes\n- memory_peak - Optional\u003cuint\u003e - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System", "x-intellij-html-description": "\u003cp\u003eThis expression defines how to rank cgroups for OOM handler.\u003c/p\u003e\n\n\u003cp\u003eThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\u003c/p\u003e\n\n\u003cp\u003eThis expression receives the following parameters:\n- memory_max - Optional\u003cuint\u003e - in bytes\n- memory_current - Optional\u003cuint\u003e - in bytes\n- memory_peak - Optional\u003cuint\u003e - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System\u003c/p\u003e\n" }, "sampleInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "sampleInterval", "description": "How often should the trigger expression be evaluated.\n\nThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.\n", "markdownDescription": "How often should the trigger expression be evaluated.\n\nThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.", "x-intellij-html-description": "\u003cp\u003eHow often should the trigger expression be evaluated.\u003c/p\u003e\n\n\u003cp\u003eThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "OOMConfig is a Out of Memory handler config document." }, "runtime.WatchdogTimerV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "WatchdogTimerConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "device": { "type": "string", "title": "device", "description": "Path to the watchdog device.\n", "markdownDescription": "Path to the watchdog device.", "x-intellij-html-description": "\u003cp\u003ePath to the watchdog device.\u003c/p\u003e\n" }, "timeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "timeout", "description": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n", "markdownDescription": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", "x-intellij-html-description": "\u003cp\u003eTimeout for the watchdog.\u003c/p\u003e\n\n\u003cp\u003eIf Talos is unresponsive for this duration, the watchdog will reset the system.\u003c/p\u003e\n\n\u003cp\u003eDefault value is 1 minute, minimum value is 10 seconds.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "WatchdogTimerConfig is a watchdog timer config document." }, "security.TrustedRootsConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TrustedRootsConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "certificates": { "type": "string", "title": "certificates", "description": "List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.\n", "markdownDescription": "List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.", "x-intellij-html-description": "\u003cp\u003eList of additional trusted certificate authorities (as PEM-encoded certificates).\u003c/p\u003e\n\n\u003cp\u003eMultiple certificates can be provided in a single config document, separated by newline characters.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "TrustedRootsConfig allows to configure additional trusted CA roots." }, "siderolink.ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "SideroLinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "apiUrl": { "type": "string", "pattern": "^(https|grpc)://", "title": "apiUrl", "description": "SideroLink API URL to connect to.\n", "markdownDescription": "SideroLink API URL to connect to.", "x-intellij-html-description": "\u003cp\u003eSideroLink API URL to connect to.\u003c/p\u003e\n" }, "uniqueToken": { "type": "string", "title": "uniqueToken", "description": "SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.\n", "markdownDescription": "SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.", "x-intellij-html-description": "\u003cp\u003eSideroLink unique token to use for the connection (optional).\u003c/p\u003e\n\n\u003cp\u003eThis value is overridden with META key UniqueMachineToken.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "SideroLinkConfig is a SideroLink connection machine configuration document." }, "v1alpha1.APIServerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the API server manifest.\n", "markdownDescription": "The container image used in the API server manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the API server manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the API server.\n", "markdownDescription": "Extra arguments to supply to the API server.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the API server.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the API server static pod.\n", "markdownDescription": "Extra volumes to mount to the API server static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the API server static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the API server’s certificate.\n", "markdownDescription": "Extra certificate subject alternative names for the API server's certificate.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the API server\u0026rsquo;s certificate.\u003c/p\u003e\n" }, "admissionControl": { "items": { "$ref": "#/$defs/v1alpha1.AdmissionPluginConfig" }, "type": "array", "title": "admissionControl", "description": "Configure the API server admission plugins.\n", "markdownDescription": "Configure the API server admission plugins.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server admission plugins.\u003c/p\u003e\n" }, "auditPolicy": { "type": "object", "title": "auditPolicy", "description": "Configure the API server audit policy.\n", "markdownDescription": "Configure the API server audit policy.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server audit policy.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the API server resources.\n", "markdownDescription": "Configure the API server resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server resources.\u003c/p\u003e\n" }, "authorizationConfig": { "items": { "$ref": "#/$defs/v1alpha1.AuthorizationConfigAuthorizerConfig" }, "type": "array", "title": "authorizationConfig", "description": "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.\n", "markdownDescription": "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "APIServerConfig represents the kube apiserver configuration options." }, "v1alpha1.AdminKubeconfigConfig": { "properties": { "certLifetime": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "certLifetime", "description": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eAdmin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AdminKubeconfigConfig contains admin kubeconfig settings." }, "v1alpha1.AdmissionPluginConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.\n", "markdownDescription": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.", "x-intellij-html-description": "\u003cp\u003eName is the name of the admission controller.\nIt must match the registered admission plugin name.\u003c/p\u003e\n" }, "configuration": { "type": "object", "title": "configuration", "description": "Configuration is an embedded configuration object to be used as the plugin’s\nconfiguration.\n", "markdownDescription": "Configuration is an embedded configuration object to be used as the plugin's\nconfiguration.", "x-intellij-html-description": "\u003cp\u003eConfiguration is an embedded configuration object to be used as the plugin\u0026rsquo;s\nconfiguration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AdmissionPluginConfig represents the API server admission plugin configuration." }, "v1alpha1.AuthorizationConfigAuthorizerConfig": { "properties": { "type": { "type": "string", "title": "type", "description": "Type is the name of the authorizer. Allowed values are Node, RBAC, and Webhook.\n", "markdownDescription": "Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`.", "x-intellij-html-description": "\u003cp\u003eType is the name of the authorizer. Allowed values are \u003ccode\u003eNode\u003c/code\u003e, \u003ccode\u003eRBAC\u003c/code\u003e, and \u003ccode\u003eWebhook\u003c/code\u003e.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name is used to describe the authorizer.\n", "markdownDescription": "Name is used to describe the authorizer.", "x-intellij-html-description": "\u003cp\u003eName is used to describe the authorizer.\u003c/p\u003e\n" }, "webhook": { "type": "object", "title": "webhook", "description": "webhook is the configuration for the webhook authorizer.\n", "markdownDescription": "webhook is the configuration for the webhook authorizer.", "x-intellij-html-description": "\u003cp\u003ewebhook is the configuration for the webhook authorizer.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration." }, "v1alpha1.CNIConfig": { "properties": { "name": { "enum": [ "flannel", "custom", "none" ], "title": "name", "description": "Name of CNI to use.\n", "markdownDescription": "Name of CNI to use.", "x-intellij-html-description": "\u003cp\u003eName of CNI to use.\u003c/p\u003e\n" }, "urls": { "items": { "type": "string" }, "type": "array", "title": "urls", "description": "URLs containing manifests to apply for the CNI.\nShould be present for “custom”, must be empty for “flannel” and “none”.\n", "markdownDescription": "URLs containing manifests to apply for the CNI.\nShould be present for \"custom\", must be empty for \"flannel\" and \"none\".", "x-intellij-html-description": "\u003cp\u003eURLs containing manifests to apply for the CNI.\nShould be present for \u0026ldquo;custom\u0026rdquo;, must be empty for \u0026ldquo;flannel\u0026rdquo; and \u0026ldquo;none\u0026rdquo;.\u003c/p\u003e\n" }, "flannel": { "$ref": "#/$defs/v1alpha1.FlannelCNIConfig", "title": "flannel", "description": "description: |\nFlannel configuration options.\n", "markdownDescription": "description: |\nFlannel configuration options.", "x-intellij-html-description": "\u003cp\u003edescription: |\nFlannel configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CNIConfig represents the CNI configuration options." }, "v1alpha1.ClusterConfig": { "properties": { "id": { "type": "string", "title": "id", "description": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).\n", "markdownDescription": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).", "x-intellij-html-description": "\u003cp\u003eGlobally unique identifier for this cluster (base64 encoded random 32 bytes).\u003c/p\u003e\n" }, "secret": { "type": "string", "title": "secret", "description": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\n", "markdownDescription": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.", "x-intellij-html-description": "\u003cp\u003eShared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/v1alpha1.ControlPlaneConfig", "title": "controlPlane", "description": "Provides control plane specific configuration options.\n", "markdownDescription": "Provides control plane specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides control plane specific configuration options.\u003c/p\u003e\n" }, "clusterName": { "type": "string", "title": "clusterName", "description": "Configures the cluster’s name.\n", "markdownDescription": "Configures the cluster's name.", "x-intellij-html-description": "\u003cp\u003eConfigures the cluster\u0026rsquo;s name.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/v1alpha1.ClusterNetworkConfig", "title": "network", "description": "Provides cluster specific network configuration options.\n", "markdownDescription": "Provides cluster specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific network configuration options.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The bootstrap token used to join the cluster.\n", "markdownDescription": "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.", "x-intellij-html-description": "\u003cp\u003eThe \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/\" target=\"_blank\"\u003ebootstrap token\u003c/a\u003e used to join the cluster.\u003c/p\u003e\n" }, "aescbcEncryptionSecret": { "type": "string", "title": "aescbcEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with AESCBC.\u003c/p\u003e\n" }, "secretboxEncryptionSecret": { "type": "string", "title": "secretboxEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The base64 encoded root certificate authority used by Kubernetes.\n", "markdownDescription": "The base64 encoded root certificate authority used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded root certificate authority used by Kubernetes.\u003c/p\u003e\n" }, "acceptedCAs": { "properties": { "crt": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "acceptedCAs", "description": "The list of base64 encoded accepted certificate authorities used by Kubernetes.\n", "markdownDescription": "The list of base64 encoded accepted certificate authorities used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe list of base64 encoded accepted certificate authorities used by Kubernetes.\u003c/p\u003e\n" }, "aggregatorCA": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "aggregatorCA", "description": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.\n", "markdownDescription": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\u003c/p\u003e\n\n\u003cp\u003eThis CA can be self-signed.\u003c/p\u003e\n" }, "serviceAccount": { "properties": { "key": { "additionalProperties": false, "type": "string" } }, "additionalProperties": false, "type": "object", "title": "serviceAccount", "description": "The base64 encoded private key for service account token generation.\n", "markdownDescription": "The base64 encoded private key for service account token generation.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded private key for service account token generation.\u003c/p\u003e\n" }, "apiServer": { "$ref": "#/$defs/v1alpha1.APIServerConfig", "title": "apiServer", "description": "API server specific configuration options.\n", "markdownDescription": "API server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eAPI server specific configuration options.\u003c/p\u003e\n" }, "controllerManager": { "$ref": "#/$defs/v1alpha1.ControllerManagerConfig", "title": "controllerManager", "description": "Controller manager server specific configuration options.\n", "markdownDescription": "Controller manager server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager server specific configuration options.\u003c/p\u003e\n" }, "proxy": { "$ref": "#/$defs/v1alpha1.ProxyConfig", "title": "proxy", "description": "Kube-proxy server-specific configuration options\n", "markdownDescription": "Kube-proxy server-specific configuration options", "x-intellij-html-description": "\u003cp\u003eKube-proxy server-specific configuration options\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/v1alpha1.SchedulerConfig", "title": "scheduler", "description": "Scheduler server specific configuration options.\n", "markdownDescription": "Scheduler server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler server specific configuration options.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/v1alpha1.ClusterDiscoveryConfig", "title": "discovery", "description": "Configures cluster member discovery.\n", "markdownDescription": "Configures cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigures cluster member discovery.\u003c/p\u003e\n" }, "etcd": { "$ref": "#/$defs/v1alpha1.EtcdConfig", "title": "etcd", "description": "Etcd specific configuration options.\n", "markdownDescription": "Etcd specific configuration options.", "x-intellij-html-description": "\u003cp\u003eEtcd specific configuration options.\u003c/p\u003e\n" }, "coreDNS": { "$ref": "#/$defs/v1alpha1.CoreDNS", "title": "coreDNS", "description": "Core DNS specific configuration options.\n", "markdownDescription": "Core DNS specific configuration options.", "x-intellij-html-description": "\u003cp\u003eCore DNS specific configuration options.\u003c/p\u003e\n" }, "externalCloudProvider": { "$ref": "#/$defs/v1alpha1.ExternalCloudProviderConfig", "title": "externalCloudProvider", "description": "External cloud provider configuration.\n", "markdownDescription": "External cloud provider configuration.", "x-intellij-html-description": "\u003cp\u003eExternal cloud provider configuration.\u003c/p\u003e\n" }, "extraManifests": { "items": { "type": "string" }, "type": "array", "title": "extraManifests", "description": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "extraManifestHeaders": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraManifestHeaders", "description": "A map of key value pairs that will be added while fetching the extraManifests.\n", "markdownDescription": "A map of key value pairs that will be added while fetching the extraManifests.", "x-intellij-html-description": "\u003cp\u003eA map of key value pairs that will be added while fetching the extraManifests.\u003c/p\u003e\n" }, "inlineManifests": { "items": { "$ref": "#/$defs/v1alpha1.ClusterInlineManifest" }, "type": "array", "title": "inlineManifests", "description": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "adminKubeconfig": { "$ref": "#/$defs/v1alpha1.AdminKubeconfigConfig", "title": "adminKubeconfig", "description": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.\n", "markdownDescription": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.", "x-intellij-html-description": "\u003cp\u003eSettings for admin kubeconfig generation.\nCertificate lifetime can be configured.\u003c/p\u003e\n" }, "allowSchedulingOnControlPlanes": { "type": "boolean", "title": "allowSchedulingOnControlPlanes", "description": "Allows running workload on control-plane nodes.\n", "markdownDescription": "Allows running workload on control-plane nodes.", "x-intellij-html-description": "\u003cp\u003eAllows running workload on control-plane nodes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterConfig represents the cluster-wide config values." }, "v1alpha1.ClusterDiscoveryConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\n", "markdownDescription": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.", "x-intellij-html-description": "\u003cp\u003eEnable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/v1alpha1.DiscoveryRegistriesConfig", "title": "registries", "description": "Configure registries used for cluster member discovery.\n", "markdownDescription": "Configure registries used for cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigure registries used for cluster member discovery.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterDiscoveryConfig struct configures cluster membership discovery." }, "v1alpha1.ClusterInlineManifest": { "properties": { "name": { "type": "string", "title": "name", "description": "Name of the manifest.\nName should be unique.\n", "markdownDescription": "Name of the manifest.\nName should be unique.", "x-intellij-html-description": "\u003cp\u003eName of the manifest.\nName should be unique.\u003c/p\u003e\n" }, "contents": { "type": "string", "title": "contents", "description": "Manifest contents as a string.\n", "markdownDescription": "Manifest contents as a string.", "x-intellij-html-description": "\u003cp\u003eManifest contents as a string.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterInlineManifest struct describes inline bootstrap manifests for the user." }, "v1alpha1.ClusterNetworkConfig": { "properties": { "cni": { "$ref": "#/$defs/v1alpha1.CNIConfig", "title": "cni", "description": "The CNI used.\nComposed of “name” and “urls”.\nThe “name” key supports the following options: “flannel”, “custom”, and “none”.\n“flannel” uses Talos-managed Flannel CNI, and that’s the default option.\n“custom” uses custom manifests that should be provided in “urls”.\n“none” indicates that Talos will not manage any CNI installation.\n", "markdownDescription": "The CNI used.\nComposed of \"name\" and \"urls\".\nThe \"name\" key supports the following options: \"flannel\", \"custom\", and \"none\".\n\"flannel\" uses Talos-managed Flannel CNI, and that's the default option.\n\"custom\" uses custom manifests that should be provided in \"urls\".\n\"none\" indicates that Talos will not manage any CNI installation.", "x-intellij-html-description": "\u003cp\u003eThe CNI used.\nComposed of \u0026ldquo;name\u0026rdquo; and \u0026ldquo;urls\u0026rdquo;.\nThe \u0026ldquo;name\u0026rdquo; key supports the following options: \u0026ldquo;flannel\u0026rdquo;, \u0026ldquo;custom\u0026rdquo;, and \u0026ldquo;none\u0026rdquo;.\n\u0026ldquo;flannel\u0026rdquo; uses Talos-managed Flannel CNI, and that\u0026rsquo;s the default option.\n\u0026ldquo;custom\u0026rdquo; uses custom manifests that should be provided in \u0026ldquo;urls\u0026rdquo;.\n\u0026ldquo;none\u0026rdquo; indicates that Talos will not manage any CNI installation.\u003c/p\u003e\n" }, "dnsDomain": { "type": "string", "title": "dnsDomain", "description": "The domain used by Kubernetes DNS.\nThe default is cluster.local\n", "markdownDescription": "The domain used by Kubernetes DNS.\nThe default is `cluster.local`", "x-intellij-html-description": "\u003cp\u003eThe domain used by Kubernetes DNS.\nThe default is \u003ccode\u003ecluster.local\u003c/code\u003e\u003c/p\u003e\n" }, "podSubnets": { "items": { "type": "string" }, "type": "array", "title": "podSubnets", "description": "The pod subnet CIDR.\n", "markdownDescription": "The pod subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe pod subnet CIDR.\u003c/p\u003e\n" }, "serviceSubnets": { "items": { "type": "string" }, "type": "array", "title": "serviceSubnets", "description": "The service subnet CIDR.\n", "markdownDescription": "The service subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe service subnet CIDR.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterNetworkConfig represents kube networking configuration options." }, "v1alpha1.Config": { "properties": { "version": { "enum": [ "v1alpha1" ], "title": "version", "description": "Indicates the schema used to decode the contents.\n", "markdownDescription": "Indicates the schema used to decode the contents.", "x-intellij-html-description": "\u003cp\u003eIndicates the schema used to decode the contents.\u003c/p\u003e\n" }, "debug": { "type": "boolean", "title": "debug", "description": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\nNote: To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\n", "markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.", "x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n" }, "machine": { "$ref": "#/$defs/v1alpha1.MachineConfig", "title": "machine", "description": "Provides machine specific configuration options.\n", "markdownDescription": "Provides machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific configuration options.\u003c/p\u003e\n" }, "cluster": { "$ref": "#/$defs/v1alpha1.ClusterConfig", "title": "cluster", "description": "Provides cluster specific configuration options.\n", "markdownDescription": "Provides cluster specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "Config defines the v1alpha1.Config Talos machine configuration document." }, "v1alpha1.ControlPlaneConfig": { "properties": { "endpoint": { "type": "string", "pattern": "^https://", "format": "uri", "title": "endpoint", "description": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\n", "markdownDescription": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.", "x-intellij-html-description": "\u003cp\u003eEndpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\u003c/p\u003e\n" }, "localAPIServerPort": { "type": "integer", "title": "localAPIServerPort", "description": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is 6443.\n", "markdownDescription": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is `6443`.", "x-intellij-html-description": "\u003cp\u003eThe port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is \u003ccode\u003e6443\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ControlPlaneConfig represents the control plane configuration options." }, "v1alpha1.ControllerManagerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the controller manager manifest.\n", "markdownDescription": "The container image used in the controller manager manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the controller manager manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the controller manager.\n", "markdownDescription": "Extra arguments to supply to the controller manager.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the controller manager.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the controller manager static pod.\n", "markdownDescription": "Extra volumes to mount to the controller manager static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the controller manager static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the controller manager resources.\n", "markdownDescription": "Configure the controller manager resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the controller manager resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ControllerManagerConfig represents the kube controller manager configuration options." }, "v1alpha1.CoreDNS": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable coredns deployment on cluster bootstrap.\n", "markdownDescription": "Disable coredns deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable coredns deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The image field is an override to the default coredns image.\n", "markdownDescription": "The `image` field is an override to the default coredns image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default coredns image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CoreDNS represents the CoreDNS config values." }, "v1alpha1.DiscoveryRegistriesConfig": { "properties": { "kubernetes": { "$ref": "#/$defs/v1alpha1.RegistryKubernetesConfig", "title": "kubernetes", "description": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.\n", "markdownDescription": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.", "x-intellij-html-description": "\u003cp\u003eKubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\u003c/p\u003e\n\n\u003cp\u003eThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee \u003ca href=\"https://github.com/siderolabs/talos/issues/9980\" target=\"_blank\"\u003ehttps://github.com/siderolabs/talos/issues/9980\u003c/a\u003e for more information.\u003c/p\u003e\n" }, "service": { "$ref": "#/$defs/v1alpha1.RegistryServiceConfig", "title": "service", "description": "Service registry is using an external service to push and pull information about cluster members.\n", "markdownDescription": "Service registry is using an external service to push and pull information about cluster members.", "x-intellij-html-description": "\u003cp\u003eService registry is using an external service to push and pull information about cluster members.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "DiscoveryRegistriesConfig struct configures cluster membership discovery." }, "v1alpha1.Endpoint": { "properties": {}, "additionalProperties": false, "type": "object", "description": "Endpoint represents the endpoint URL parsed out of the machine config." }, "v1alpha1.EtcdConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used to create the etcd service.\n", "markdownDescription": "The container image used to create the etcd service.", "x-intellij-html-description": "\u003cp\u003eThe container image used to create the etcd service.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The ca is the root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The `ca` is the root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eca\u003c/code\u003e is the root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n\nname\ndata-dir\ninitial-cluster-state\nlisten-peer-urls\nlisten-client-urls\ncert-file\nkey-file\ntrusted-ca-file\npeer-client-cert-auth\npeer-cert-file\npeer-trusted-ca-file\npeer-key-file\n\n", "markdownDescription": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n- `name`\n- `data-dir`\n- `initial-cluster-state`\n- `listen-peer-urls`\n- `listen-client-urls`\n- `cert-file`\n- `key-file`\n- `trusted-ca-file`\n- `peer-client-cert-auth`\n- `peer-cert-file`\n- `peer-trusted-ca-file`\n- `peer-key-file`", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to etcd.\nNote that the following args are not allowed:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edata-dir\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003einitial-cluster-state\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-peer-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-client-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ekey-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etrusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-client-cert-auth\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-cert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-trusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-key-file\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "advertisedSubnets": { "items": { "type": "string" }, "type": "array", "title": "advertisedSubnets", "description": "The advertisedSubnets field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eadvertisedSubnets\u003c/code\u003e field configures the networks to pick etcd advertised IP from.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" }, "listenSubnets": { "items": { "type": "string" }, "type": "array", "title": "listenSubnets", "description": "The listenSubnets field configures the networks for the etcd to listen for peer and client connections.\n\nIf listenSubnets is not set, but advertisedSubnets is set, listenSubnets defaults to\nadvertisedSubnets.\n\nIf neither advertisedSubnets nor listenSubnets is set, listenSubnets defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.\n\nIf `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to\n`advertisedSubnets`.\n\nIf neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003elistenSubnets\u003c/code\u003e field configures the networks for the etcd to listen for peer and client connections.\u003c/p\u003e\n\n\u003cp\u003eIf \u003ccode\u003elistenSubnets\u003c/code\u003e is not set, but \u003ccode\u003eadvertisedSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to\n\u003ccode\u003eadvertisedSubnets\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eIf neither \u003ccode\u003eadvertisedSubnets\u003c/code\u003e nor \u003ccode\u003elistenSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to listen on all addresses.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EtcdConfig represents the etcd configuration options." }, "v1alpha1.ExternalCloudProviderConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable external cloud provider.\n", "markdownDescription": "Enable external cloud provider.", "x-intellij-html-description": "\u003cp\u003eEnable external cloud provider.\u003c/p\u003e\n" }, "manifests": { "items": { "type": "string" }, "type": "array", "title": "manifests", "description": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExternalCloudProviderConfig contains external cloud provider configuration." }, "v1alpha1.ExtraMount": { "properties": { "destination": { "type": "string", "title": "destination", "description": "Destination is the absolute path where the mount will be placed in the container.\n", "markdownDescription": "Destination is the absolute path where the mount will be placed in the container.", "x-intellij-html-description": "\u003cp\u003eDestination is the absolute path where the mount will be placed in the container.\u003c/p\u003e\n" }, "type": { "type": "string", "title": "type", "description": "Type specifies the mount kind.\n", "markdownDescription": "Type specifies the mount kind.", "x-intellij-html-description": "\u003cp\u003eType specifies the mount kind.\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "Source specifies the source path of the mount.\n", "markdownDescription": "Source specifies the source path of the mount.", "x-intellij-html-description": "\u003cp\u003eSource specifies the source path of the mount.\u003c/p\u003e\n" }, "options": { "items": { "type": "string" }, "type": "array", "title": "options", "description": "Options are fstab style mount options.\n", "markdownDescription": "Options are fstab style mount options.", "x-intellij-html-description": "\u003cp\u003eOptions are fstab style mount options.\u003c/p\u003e\n" }, "uidMappings": { "items": { "$ref": "#/$defs/v1alpha1.LinuxIDMapping" }, "type": "array", "title": "uidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" }, "gidMappings": { "items": { "$ref": "#/$defs/v1alpha1.LinuxIDMapping" }, "type": "array", "title": "gidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExtraMount wraps OCI Mount specification." }, "v1alpha1.FeaturesConfig": { "properties": { "kubernetesTalosAPIAccess": { "$ref": "#/$defs/v1alpha1.KubernetesTalosAPIAccessConfig", "title": "kubernetesTalosAPIAccess", "description": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.\n", "markdownDescription": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.", "x-intellij-html-description": "\u003cp\u003eConfigure Talos API access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eThis feature is disabled if the feature config is not specified.\u003c/p\u003e\n" }, "diskQuotaSupport": { "type": "boolean", "title": "diskQuotaSupport", "description": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\n", "markdownDescription": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.", "x-intellij-html-description": "\u003cp\u003eEnable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\u003c/p\u003e\n" }, "kubePrism": { "$ref": "#/$defs/v1alpha1.KubePrism", "title": "kubePrism", "description": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\n", "markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.", "x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n" }, "hostDNS": { "$ref": "#/$defs/v1alpha1.HostDNSConfig", "title": "hostDNS", "description": "Configures host DNS caching resolver.\n", "markdownDescription": "Configures host DNS caching resolver.", "x-intellij-html-description": "\u003cp\u003eConfigures host DNS caching resolver.\u003c/p\u003e\n" }, "imageCache": { "$ref": "#/$defs/v1alpha1.ImageCacheConfig", "title": "imageCache", "description": "Enable Image Cache feature.\n", "markdownDescription": "Enable Image Cache feature.", "x-intellij-html-description": "\u003cp\u003eEnable Image Cache feature.\u003c/p\u003e\n" }, "nodeAddressSortAlgorithm": { "type": "string", "title": "nodeAddressSortAlgorithm", "description": "Select the node address sort algorithm.\nThe ‘v1’ algorithm sorts addresses by the address itself.\nThe ‘v2’ algorithm prefers more specific prefixes.\nIf unset, defaults to ‘v1’.\n", "markdownDescription": "Select the node address sort algorithm.\nThe 'v1' algorithm sorts addresses by the address itself.\nThe 'v2' algorithm prefers more specific prefixes.\nIf unset, defaults to 'v1'.", "x-intellij-html-description": "\u003cp\u003eSelect the node address sort algorithm.\nThe \u0026lsquo;v1\u0026rsquo; algorithm sorts addresses by the address itself.\nThe \u0026lsquo;v2\u0026rsquo; algorithm prefers more specific prefixes.\nIf unset, defaults to \u0026lsquo;v1\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FeaturesConfig describes individual Talos features that can be switched on or off." }, "v1alpha1.FlannelCNIConfig": { "properties": { "extraArgs": { "items": { "type": "string" }, "type": "array", "title": "extraArgs", "description": "Extra arguments for ‘flanneld’.\n", "markdownDescription": "Extra arguments for 'flanneld'.", "x-intellij-html-description": "\u003cp\u003eExtra arguments for \u0026lsquo;flanneld\u0026rsquo;.\u003c/p\u003e\n" }, "kubeNetworkPoliciesEnabled": { "type": "boolean", "title": "kubeNetworkPoliciesEnabled", "description": "Deploys kube-network-policies along with Flannel.\n\nThis enables Kubernetes Network Policies support in the cluster.\n", "markdownDescription": "Deploys kube-network-policies along with Flannel.\n\nThis enables Kubernetes Network Policies support in the cluster.", "x-intellij-html-description": "\u003cp\u003eDeploys kube-network-policies along with Flannel.\u003c/p\u003e\n\n\u003cp\u003eThis enables Kubernetes Network Policies support in the cluster.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FlannelCNIConfig represents the Flannel CNI configuration options." }, "v1alpha1.HostDNSConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable host DNS caching resolver.\n", "markdownDescription": "Enable host DNS caching resolver.", "x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n" }, "forwardKubeDNSToHost": { "type": "boolean", "title": "forwardKubeDNSToHost", "description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n", "markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).", "x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n" }, "resolveMemberNames": { "type": "boolean", "title": "resolveMemberNames", "description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n", "markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.", "x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "HostDNSConfig describes the configuration for the host DNS resolver." }, "v1alpha1.ImageCacheConfig": { "properties": { "localEnabled": { "type": "boolean", "title": "localEnabled", "description": "Enable local image cache.\n", "markdownDescription": "Enable local image cache.", "x-intellij-html-description": "\u003cp\u003eEnable local image cache.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ImageCacheConfig describes the configuration for the Image Cache feature." }, "v1alpha1.InstallConfig": { "properties": { "disk": { "type": "string", "title": "disk", "description": "The disk used for installations.\n", "markdownDescription": "The disk used for installations.", "x-intellij-html-description": "\u003cp\u003eThe disk used for installations.\u003c/p\u003e\n" }, "diskSelector": { "$ref": "#/$defs/v1alpha1.InstallDiskSelector", "title": "diskSelector", "description": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over disk.\n", "markdownDescription": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over `disk`.", "x-intellij-html-description": "\u003cp\u003eLook up disk using disk attributes like model, size, serial and others.\nAlways has priority over \u003ccode\u003edisk\u003c/code\u003e.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\nGitHub releases page.\n", "markdownDescription": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n[GitHub releases page](https://github.com/siderolabs/talos/releases).", "x-intellij-html-description": "\u003cp\u003eAllows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n\u003ca href=\"https://github.com/siderolabs/talos/releases\" target=\"_blank\"\u003eGitHub releases page\u003c/a\u003e.\u003c/p\u003e\n" }, "wipe": { "type": "boolean", "title": "wipe", "description": "Indicates if the installation disk should be wiped at installation time.\nDefaults to true.\n", "markdownDescription": "Indicates if the installation disk should be wiped at installation time.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the installation disk should be wiped at installation time.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "legacyBIOSSupport": { "type": "boolean", "title": "legacyBIOSSupport", "description": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn’t support GPT partitioning scheme.\n", "markdownDescription": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme.", "x-intellij-html-description": "\u003cp\u003eIndicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn\u0026rsquo;t support GPT partitioning scheme.\u003c/p\u003e\n" }, "grubUseUKICmdline": { "type": "boolean", "title": "grubUseUKICmdline", "description": "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.\n", "markdownDescription": "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.", "x-intellij-html-description": "\u003cp\u003eIndicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "InstallConfig represents the installation options for preparing a node." }, "v1alpha1.InstallDiskSelector": { "properties": { "size": { "type": "string", "title": "size", "description": "Disk size.\n", "markdownDescription": "Disk size.", "x-intellij-html-description": "\u003cp\u003eDisk size.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Disk name /sys/block/\u0026lt;dev\u0026gt;/device/name.\n", "markdownDescription": "Disk name `/sys/block/\u003cdev\u003e/device/name`.", "x-intellij-html-description": "\u003cp\u003eDisk name \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/name\u003c/code\u003e.\u003c/p\u003e\n" }, "model": { "type": "string", "title": "model", "description": "Disk model /sys/block/\u0026lt;dev\u0026gt;/device/model.\n", "markdownDescription": "Disk model `/sys/block/\u003cdev\u003e/device/model`.", "x-intellij-html-description": "\u003cp\u003eDisk model \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/model\u003c/code\u003e.\u003c/p\u003e\n" }, "serial": { "type": "string", "title": "serial", "description": "Disk serial number /sys/block/\u0026lt;dev\u0026gt;/serial.\n", "markdownDescription": "Disk serial number `/sys/block/\u003cdev\u003e/serial`.", "x-intellij-html-description": "\u003cp\u003eDisk serial number \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/serial\u003c/code\u003e.\u003c/p\u003e\n" }, "modalias": { "type": "string", "title": "modalias", "description": "Disk modalias /sys/block/\u0026lt;dev\u0026gt;/device/modalias.\n", "markdownDescription": "Disk modalias `/sys/block/\u003cdev\u003e/device/modalias`.", "x-intellij-html-description": "\u003cp\u003eDisk modalias \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/modalias\u003c/code\u003e.\u003c/p\u003e\n" }, "uuid": { "type": "string", "title": "uuid", "description": "Disk UUID /sys/block/\u0026lt;dev\u0026gt;/uuid.\n", "markdownDescription": "Disk UUID `/sys/block/\u003cdev\u003e/uuid`.", "x-intellij-html-description": "\u003cp\u003eDisk UUID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/uuid\u003c/code\u003e.\u003c/p\u003e\n" }, "wwid": { "type": "string", "title": "wwid", "description": "Disk WWID /sys/block/\u0026lt;dev\u0026gt;/wwid.\n", "markdownDescription": "Disk WWID `/sys/block/\u003cdev\u003e/wwid`.", "x-intellij-html-description": "\u003cp\u003eDisk WWID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/wwid\u003c/code\u003e.\u003c/p\u003e\n" }, "type": { "enum": [ "ssd", "hdd", "nvme", "sd" ], "title": "type", "description": "Disk Type.\n", "markdownDescription": "Disk Type.", "x-intellij-html-description": "\u003cp\u003eDisk Type.\u003c/p\u003e\n" }, "busPath": { "type": "string", "title": "busPath", "description": "Disk bus path.\n", "markdownDescription": "Disk bus path.", "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "InstallDiskSelector represents a disk query parameters for the install disk lookup." }, "v1alpha1.KernelConfig": { "properties": { "modules": { "items": { "$ref": "#/$defs/v1alpha1.KernelModuleConfig" }, "type": "array", "title": "modules", "description": "Kernel modules to load.\n", "markdownDescription": "Kernel modules to load.", "x-intellij-html-description": "\u003cp\u003eKernel modules to load.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KernelConfig struct configures Talos Linux kernel." }, "v1alpha1.KernelModuleConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Module name.\n", "markdownDescription": "Module name.", "x-intellij-html-description": "\u003cp\u003eModule name.\u003c/p\u003e\n" }, "parameters": { "items": { "type": "string" }, "type": "array", "title": "parameters", "description": "Module parameters, changes applied after reboot.\n", "markdownDescription": "Module parameters, changes applied after reboot.", "x-intellij-html-description": "\u003cp\u003eModule parameters, changes applied after reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KernelModuleConfig struct configures Linux kernel modules to load." }, "v1alpha1.KubePrism": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable KubePrism support - will start local load balancing proxy.\n", "markdownDescription": "Enable KubePrism support - will start local load balancing proxy.", "x-intellij-html-description": "\u003cp\u003eEnable KubePrism support - will start local load balancing proxy.\u003c/p\u003e\n" }, "port": { "type": "integer", "title": "port", "description": "KubePrism port.\n", "markdownDescription": "KubePrism port.", "x-intellij-html-description": "\u003cp\u003eKubePrism port.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubePrism describes the configuration for the KubePrism load balancer." }, "v1alpha1.KubeletConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The image field is an optional reference to an alternative kubelet image.\n", "markdownDescription": "The `image` field is an optional reference to an alternative kubelet image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an optional reference to an alternative kubelet image.\u003c/p\u003e\n" }, "clusterDNS": { "items": { "type": "string" }, "type": "array", "title": "clusterDNS", "description": "The ClusterDNS field is an optional reference to an alternative kubelet clusterDNS ip list.\n", "markdownDescription": "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eClusterDNS\u003c/code\u003e field is an optional reference to an alternative kubelet clusterDNS ip list.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "The extraArgs field is used to provide additional flags to the kubelet.\n", "markdownDescription": "The `extraArgs` field is used to provide additional flags to the kubelet.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraArgs\u003c/code\u003e field is used to provide additional flags to the kubelet.\u003c/p\u003e\n" }, "extraMounts": { "items": { "$ref": "#/$defs/v1alpha1.ExtraMount" }, "type": "array", "title": "extraMounts", "description": "The extraMounts field is used to add additional mounts to the kubelet container.\nNote that either bind or rbind are required in the options.\n", "markdownDescription": "The `extraMounts` field is used to add additional mounts to the kubelet container.\nNote that either `bind` or `rbind` are required in the `options`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraMounts\u003c/code\u003e field is used to add additional mounts to the kubelet container.\nNote that either \u003ccode\u003ebind\u003c/code\u003e or \u003ccode\u003erbind\u003c/code\u003e are required in the \u003ccode\u003eoptions\u003c/code\u003e.\u003c/p\u003e\n" }, "extraConfig": { "type": "object", "title": "extraConfig", "description": "The extraConfig field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\n", "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, "credentialProviderConfig": { "type": "object", "title": "credentialProviderConfig", "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled", "description": "Enable container runtime default Seccomp profile.\n", "markdownDescription": "Enable container runtime default Seccomp profile.", "x-intellij-html-description": "\u003cp\u003eEnable container runtime default Seccomp profile.\u003c/p\u003e\n" }, "registerWithFQDN": { "type": "boolean", "title": "registerWithFQDN", "description": "The registerWithFQDN field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\n", "markdownDescription": "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eregisterWithFQDN\u003c/code\u003e field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\u003c/p\u003e\n" }, "nodeIP": { "$ref": "#/$defs/v1alpha1.KubeletNodeIPConfig", "title": "nodeIP", "description": "The nodeIP field is used to configure --node-ip flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\n", "markdownDescription": "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003enodeIP\u003c/code\u003e field is used to configure \u003ccode\u003e--node-ip\u003c/code\u003e flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\u003c/p\u003e\n" }, "skipNodeRegistration": { "type": "boolean", "title": "skipNodeRegistration", "description": "The skipNodeRegistration is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\n", "markdownDescription": "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eskipNodeRegistration\u003c/code\u003e is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\u003c/p\u003e\n" }, "disableManifestsDirectory": { "type": "boolean", "title": "disableManifestsDirectory", "description": "The disableManifestsDirectory field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt’s recommended to configure static pods with the “pods” key instead.\n", "markdownDescription": "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt's recommended to configure static pods with the \"pods\" key instead.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003edisableManifestsDirectory\u003c/code\u003e field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt\u0026rsquo;s recommended to configure static pods with the \u0026ldquo;pods\u0026rdquo; key instead.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeletConfig represents the kubelet config values." }, "v1alpha1.KubeletNodeIPConfig": { "properties": { "validSubnets": { "items": { "type": "string" }, "type": "array", "title": "validSubnets", "description": "The validSubnets field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\n", "markdownDescription": "The `validSubnets` field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalidSubnets\u003c/code\u003e field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeletNodeIPConfig represents the kubelet node IP configuration." }, "v1alpha1.KubernetesTalosAPIAccessConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable Talos API access from Kubernetes pods.\n", "markdownDescription": "Enable Talos API access from Kubernetes pods.", "x-intellij-html-description": "\u003cp\u003eEnable Talos API access from Kubernetes pods.\u003c/p\u003e\n" }, "allowedRoles": { "items": { "type": "string" }, "type": "array", "title": "allowedRoles", "description": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.\n", "markdownDescription": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.", "x-intellij-html-description": "\u003cp\u003eThe list of Talos API roles which can be granted for access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eEmpty list means that no roles can be granted, so access is blocked.\u003c/p\u003e\n" }, "allowedKubernetesNamespaces": { "items": { "type": "string" }, "type": "array", "title": "allowedKubernetesNamespaces", "description": "The list of Kubernetes namespaces Talos API access is available from.\n", "markdownDescription": "The list of Kubernetes namespaces Talos API access is available from.", "x-intellij-html-description": "\u003cp\u003eThe list of Kubernetes namespaces Talos API access is available from.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods." }, "v1alpha1.LinuxIDMapping": { "properties": { "containerID": { "type": "integer", "title": "containerID", "description": "ContainerID is the starting UID/GID in the container.\n", "markdownDescription": "ContainerID is the starting UID/GID in the container.", "x-intellij-html-description": "\u003cp\u003eContainerID is the starting UID/GID in the container.\u003c/p\u003e\n" }, "hostID": { "type": "integer", "title": "hostID", "description": "HostID is the starting UID/GID on the host to be mapped to ‘ContainerID’.\n", "markdownDescription": "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'.", "x-intellij-html-description": "\u003cp\u003eHostID is the starting UID/GID on the host to be mapped to \u0026lsquo;ContainerID\u0026rsquo;.\u003c/p\u003e\n" }, "size": { "type": "integer", "title": "size", "description": "Size is the number of IDs to be mapped.\n", "markdownDescription": "Size is the number of IDs to be mapped.", "x-intellij-html-description": "\u003cp\u003eSize is the number of IDs to be mapped.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LinuxIDMapping represents the Linux ID mapping." }, "v1alpha1.LoggingConfig": { "properties": { "destinations": { "items": { "$ref": "#/$defs/v1alpha1.LoggingDestination" }, "type": "array", "title": "destinations", "description": "Logging destination.\n", "markdownDescription": "Logging destination.", "x-intellij-html-description": "\u003cp\u003eLogging destination.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LoggingConfig struct configures Talos logging." }, "v1alpha1.LoggingDestination": { "properties": { "endpoint": { "$ref": "#/$defs/v1alpha1.Endpoint", "title": "endpoint", "description": "Where to send logs. Supported protocols are “tcp” and “udp”.\n", "markdownDescription": "Where to send logs. Supported protocols are \"tcp\" and \"udp\".", "x-intellij-html-description": "\u003cp\u003eWhere to send logs. Supported protocols are \u0026ldquo;tcp\u0026rdquo; and \u0026ldquo;udp\u0026rdquo;.\u003c/p\u003e\n" }, "format": { "enum": [ "json_lines" ], "title": "format", "description": "Logs format.\n", "markdownDescription": "Logs format.", "x-intellij-html-description": "\u003cp\u003eLogs format.\u003c/p\u003e\n" }, "extraTags": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraTags", "description": "Extra tags (key-value) pairs to attach to every log message sent.\n", "markdownDescription": "Extra tags (key-value) pairs to attach to every log message sent.", "x-intellij-html-description": "\u003cp\u003eExtra tags (key-value) pairs to attach to every log message sent.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LoggingDestination struct configures Talos logging destination." }, "v1alpha1.MachineConfig": { "properties": { "type": { "enum": [ "controlplane", "worker" ], "title": "type", "description": "Defines the role of the machine within the cluster.\n\nControl Plane\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\nWorker\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as “join”; that value is still supported but deprecated.\n", "markdownDescription": "Defines the role of the machine within the cluster.\n\n**Control Plane**\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\n**Worker**\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as \"join\"; that value is still supported but deprecated.", "x-intellij-html-description": "\u003cp\u003eDefines the role of the machine within the cluster.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eControl Plane\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eWorker\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\u003c/p\u003e\n\n\u003cp\u003eThis node type was previously known as \u0026ldquo;join\u0026rdquo;; that value is still supported but deprecated.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The token is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its’ identity.\n", "markdownDescription": "The `token` is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003etoken\u003c/code\u003e is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its\u0026rsquo; identity.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "acceptedCAs": { "properties": { "crt": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "acceptedCAs", "description": "The certificates issued by certificate authorities are accepted in addition to issuing ‘ca’.\nIt is composed of a base64 encoded crt`.\n", "markdownDescription": "The certificates issued by certificate authorities are accepted in addition to issuing 'ca'.\nIt is composed of a base64 encoded `crt``.", "x-intellij-html-description": "\u003cp\u003eThe certificates issued by certificate authorities are accepted in addition to issuing \u0026lsquo;ca\u0026rsquo;.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e`.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the machine’s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate’s SANs.\n", "markdownDescription": "Extra certificate subject alternative names for the machine's certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate's SANs.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the machine\u0026rsquo;s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate\u0026rsquo;s SANs.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/v1alpha1.MachineControlPlaneConfig", "title": "controlPlane", "description": "Provides machine specific control plane configuration options.\n", "markdownDescription": "Provides machine specific control plane configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific control plane configuration options.\u003c/p\u003e\n" }, "kubelet": { "$ref": "#/$defs/v1alpha1.KubeletConfig", "title": "kubelet", "description": "Used to provide additional options to the kubelet.\n", "markdownDescription": "Used to provide additional options to the kubelet.", "x-intellij-html-description": "\u003cp\u003eUsed to provide additional options to the kubelet.\u003c/p\u003e\n" }, "pods": { "items": { "type": "object" }, "type": "array", "title": "pods", "description": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn’t validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.\n", "markdownDescription": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn't validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.", "x-intellij-html-description": "\u003cp\u003eUsed to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\u003c/p\u003e\n\n\u003cp\u003eStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn\u0026rsquo;t validate the pod definition.\nUpdates to this field can be applied without a reboot.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\" target=\"_blank\"\u003ehttps://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\u003c/a\u003e.\u003c/p\u003e\n" }, "install": { "$ref": "#/$defs/v1alpha1.InstallConfig", "title": "install", "description": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\n", "markdownDescription": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.", "x-intellij-html-description": "\u003cp\u003eUsed to provide instructions for installations.\u003c/p\u003e\n\n\u003cp\u003eNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\u003c/p\u003e\n" }, "files": { "items": { "$ref": "#/$defs/v1alpha1.MachineFile" }, "type": "array", "title": "files", "description": "Allows the addition of user specified files.\nThe value of op can be create, overwrite, or append.\nIn the case of create, path must not exist.\nIn the case of overwrite, and append, path must be a valid file.\nIf an op value of append is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\n", "markdownDescription": "Allows the addition of user specified files.\nThe value of `op` can be `create`, `overwrite`, or `append`.\nIn the case of `create`, `path` must not exist.\nIn the case of `overwrite`, and `append`, `path` must be a valid file.\nIf an `op` value of `append` is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.", "x-intellij-html-description": "\u003cp\u003eAllows the addition of user specified files.\nThe value of \u003ccode\u003eop\u003c/code\u003e can be \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003eoverwrite\u003c/code\u003e, or \u003ccode\u003eappend\u003c/code\u003e.\nIn the case of \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must not exist.\nIn the case of \u003ccode\u003eoverwrite\u003c/code\u003e, and \u003ccode\u003eappend\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must be a valid file.\nIf an \u003ccode\u003eop\u003c/code\u003e value of \u003ccode\u003eappend\u003c/code\u003e is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\u003c/p\u003e\n" }, "sysctls": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysctls", "description": "Used to configure the machine’s sysctls.\n", "markdownDescription": "Used to configure the machine's sysctls.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysctls.\u003c/p\u003e\n" }, "sysfs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysfs", "description": "Used to configure the machine’s sysfs.\n", "markdownDescription": "Used to configure the machine's sysfs.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysfs.\u003c/p\u003e\n" }, "features": { "$ref": "#/$defs/v1alpha1.FeaturesConfig", "title": "features", "description": "Features describe individual Talos features that can be switched on or off.\n", "markdownDescription": "Features describe individual Talos features that can be switched on or off.", "x-intellij-html-description": "\u003cp\u003eFeatures describe individual Talos features that can be switched on or off.\u003c/p\u003e\n" }, "udev": { "$ref": "#/$defs/v1alpha1.UdevConfig", "title": "udev", "description": "Configures the udev system.\n", "markdownDescription": "Configures the udev system.", "x-intellij-html-description": "\u003cp\u003eConfigures the udev system.\u003c/p\u003e\n" }, "logging": { "$ref": "#/$defs/v1alpha1.LoggingConfig", "title": "logging", "description": "Configures the logging system.\n", "markdownDescription": "Configures the logging system.", "x-intellij-html-description": "\u003cp\u003eConfigures the logging system.\u003c/p\u003e\n" }, "kernel": { "$ref": "#/$defs/v1alpha1.KernelConfig", "title": "kernel", "description": "Configures the kernel.\n", "markdownDescription": "Configures the kernel.", "x-intellij-html-description": "\u003cp\u003eConfigures the kernel.\u003c/p\u003e\n" }, "seccompProfiles": { "items": { "$ref": "#/$defs/v1alpha1.MachineSeccompProfile" }, "type": "array", "title": "seccompProfiles", "description": "Configures the seccomp profiles for the machine.\n", "markdownDescription": "Configures the seccomp profiles for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the seccomp profiles for the machine.\u003c/p\u003e\n" }, "baseRuntimeSpecOverrides": { "type": "object", "title": "baseRuntimeSpecOverrides", "description": "Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.\n", "markdownDescription": "Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.", "x-intellij-html-description": "\u003cp\u003eOverride (patch) settings in the default OCI runtime spec for CRI containers.\u003c/p\u003e\n\n\u003cp\u003eIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.\u003c/p\u003e\n" }, "nodeLabels": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeLabels", "description": "Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see NodeRestriction admission plugin).\n", "markdownDescription": "Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", "x-intellij-html-description": "\u003cp\u003eConfigures the node labels for the machine.\u003c/p\u003e\n\n\u003cp\u003eNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction\" target=\"_blank\"\u003eNodeRestriction\u003c/a\u003e admission plugin).\u003c/p\u003e\n" }, "nodeAnnotations": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeAnnotations", "description": "Configures the node annotations for the machine.\n", "markdownDescription": "Configures the node annotations for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the node annotations for the machine.\u003c/p\u003e\n" }, "nodeTaints": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeTaints", "description": "Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see NodeRestriction admission plugin).\n", "markdownDescription": "Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", "x-intellij-html-description": "\u003cp\u003eConfigures the node taints for the machine. Effect is optional.\u003c/p\u003e\n\n\u003cp\u003eNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction\" target=\"_blank\"\u003eNodeRestriction\u003c/a\u003e admission plugin).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineConfig represents the machine-specific config values." }, "v1alpha1.MachineControlPlaneConfig": { "properties": { "controllerManager": { "$ref": "#/$defs/v1alpha1.MachineControllerManagerConfig", "title": "controllerManager", "description": "Controller manager machine specific configuration options.\n", "markdownDescription": "Controller manager machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager machine specific configuration options.\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/v1alpha1.MachineSchedulerConfig", "title": "scheduler", "description": "Scheduler machine specific configuration options.\n", "markdownDescription": "Scheduler machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler machine specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineControlPlaneConfig machine specific configuration options." }, "v1alpha1.MachineControllerManagerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-controller-manager on the node.\n", "markdownDescription": "Disable kube-controller-manager on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-controller-manager on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineControllerManagerConfig represents the machine specific ControllerManager config values." }, "v1alpha1.MachineFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The contents of the file.\n", "markdownDescription": "The contents of the file.", "x-intellij-html-description": "\u003cp\u003eThe contents of the file.\u003c/p\u003e\n" }, "permissions": { "type": "integer", "title": "permissions", "description": "The file’s permissions in octal.\n", "markdownDescription": "The file's permissions in octal.", "x-intellij-html-description": "\u003cp\u003eThe file\u0026rsquo;s permissions in octal.\u003c/p\u003e\n" }, "path": { "type": "string", "title": "path", "description": "The path of the file.\n", "markdownDescription": "The path of the file.", "x-intellij-html-description": "\u003cp\u003eThe path of the file.\u003c/p\u003e\n" }, "op": { "enum": [ "create", "append", "overwrite" ], "title": "op", "description": "The operation to use\n", "markdownDescription": "The operation to use", "x-intellij-html-description": "\u003cp\u003eThe operation to use\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineFile represents a file to write to disk." }, "v1alpha1.MachineSchedulerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-scheduler on the node.\n", "markdownDescription": "Disable kube-scheduler on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-scheduler on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineSchedulerConfig represents the machine specific Scheduler config values." }, "v1alpha1.MachineSeccompProfile": { "properties": { "name": { "type": "string", "title": "name", "description": "The name field is used to provide the file name of the seccomp profile.\n", "markdownDescription": "The `name` field is used to provide the file name of the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003ename\u003c/code\u003e field is used to provide the file name of the seccomp profile.\u003c/p\u003e\n" }, "value": { "type": "object", "title": "value", "description": "The value field is used to provide the seccomp profile.\n", "markdownDescription": "The `value` field is used to provide the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalue\u003c/code\u003e field is used to provide the seccomp profile.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineSeccompProfile defines seccomp profiles for the machine." }, "v1alpha1.ProxyConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-proxy deployment on cluster bootstrap.\n", "markdownDescription": "Disable kube-proxy deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable kube-proxy deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The container image used in the kube-proxy manifest.\n", "markdownDescription": "The container image used in the kube-proxy manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the kube-proxy manifest.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "proxy mode of kube-proxy.\nThe default is ‘iptables’.\n", "markdownDescription": "proxy mode of kube-proxy.\nThe default is 'iptables'.", "x-intellij-html-description": "\u003cp\u003eproxy mode of kube-proxy.\nThe default is \u0026lsquo;iptables\u0026rsquo;.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to kube-proxy.\n", "markdownDescription": "Extra arguments to supply to kube-proxy.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to kube-proxy.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ProxyConfig represents the kube proxy configuration options." }, "v1alpha1.RegistryKubernetesConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable Kubernetes discovery registry.\n", "markdownDescription": "Disable Kubernetes discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable Kubernetes discovery registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RegistryKubernetesConfig struct configures Kubernetes discovery registry." }, "v1alpha1.RegistryServiceConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable external service discovery registry.\n", "markdownDescription": "Disable external service discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable external service discovery registry.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "External service endpoint.\n", "markdownDescription": "External service endpoint.", "x-intellij-html-description": "\u003cp\u003eExternal service endpoint.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RegistryServiceConfig struct configures Kubernetes discovery registry." }, "v1alpha1.ResourcesConfig": { "properties": { "requests": { "type": "object", "title": "requests", "description": "Requests configures the reserved cpu/memory resources.\n", "markdownDescription": "Requests configures the reserved cpu/memory resources.", "x-intellij-html-description": "\u003cp\u003eRequests configures the reserved cpu/memory resources.\u003c/p\u003e\n" }, "limits": { "type": "object", "title": "limits", "description": "Limits configures the maximum cpu/memory resources a container can use.\n", "markdownDescription": "Limits configures the maximum cpu/memory resources a container can use.", "x-intellij-html-description": "\u003cp\u003eLimits configures the maximum cpu/memory resources a container can use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ResourcesConfig represents the pod resources." }, "v1alpha1.SchedulerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the scheduler manifest.\n", "markdownDescription": "The container image used in the scheduler manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the scheduler manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the scheduler.\n", "markdownDescription": "Extra arguments to supply to the scheduler.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the scheduler.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the scheduler static pod.\n", "markdownDescription": "Extra volumes to mount to the scheduler static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the scheduler static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the scheduler resources.\n", "markdownDescription": "Configure the scheduler resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the scheduler resources.\u003c/p\u003e\n" }, "config": { "type": "object", "title": "config", "description": "Specify custom kube-scheduler configuration.\n", "markdownDescription": "Specify custom kube-scheduler configuration.", "x-intellij-html-description": "\u003cp\u003eSpecify custom kube-scheduler configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "SchedulerConfig represents the kube scheduler configuration options." }, "v1alpha1.UdevConfig": { "properties": { "rules": { "items": { "type": "string" }, "type": "array", "title": "rules", "description": "List of udev rules to apply to the udev system\n", "markdownDescription": "List of udev rules to apply to the udev system", "x-intellij-html-description": "\u003cp\u003eList of udev rules to apply to the udev system\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "UdevConfig describes how the udev system should be configured." }, "v1alpha1.VolumeMountConfig": { "properties": { "hostPath": { "type": "string", "title": "hostPath", "description": "Path on the host.\n", "markdownDescription": "Path on the host.", "x-intellij-html-description": "\u003cp\u003ePath on the host.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "Path in the container.\n", "markdownDescription": "Path in the container.", "x-intellij-html-description": "\u003cp\u003ePath in the container.\u003c/p\u003e\n" }, "readonly": { "type": "boolean", "title": "readonly", "description": "Mount the volume read only.\n", "markdownDescription": "Mount the volume read only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read only.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeMountConfig struct describes extra volume mount for the static pods." } }, "oneOf": [ { "$ref": "#/$defs/block.ExistingVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.ExternalVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.RawVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.SwapVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.UserVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.VolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.ZswapConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryAuthConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryMirrorConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryTLSConfigV1Alpha1" }, { "$ref": "#/$defs/extensions.ServiceConfigV1Alpha1" }, { "$ref": "#/$defs/hardware.PCIDriverRebindConfigV1Alpha1" }, { "$ref": "#/$defs/network.BlackholeRouteConfigV1Alpha1" }, { "$ref": "#/$defs/network.BondConfigV1Alpha1" }, { "$ref": "#/$defs/network.BridgeConfigV1Alpha1" }, { "$ref": "#/$defs/network.DefaultActionConfigV1Alpha1" }, { "$ref": "#/$defs/network.DHCPv4ConfigV1Alpha1" }, { "$ref": "#/$defs/network.DHCPv6ConfigV1Alpha1" }, { "$ref": "#/$defs/network.DummyLinkConfigV1Alpha1" }, { "$ref": "#/$defs/network.EthernetConfigV1Alpha1" }, { "$ref": "#/$defs/network.HCloudVIPConfigV1Alpha1" }, { "$ref": "#/$defs/network.HostnameConfigV1Alpha1" }, { "$ref": "#/$defs/network.KubeSpanConfigV1Alpha1" }, { "$ref": "#/$defs/network.KubespanEndpointsConfigV1Alpha1" }, { "$ref": "#/$defs/network.Layer2VIPConfigV1Alpha1" }, { "$ref": "#/$defs/network.LinkConfigV1Alpha1" }, { "$ref": "#/$defs/network.LinkAliasConfigV1Alpha1" }, { "$ref": "#/$defs/network.ResolverConfigV1Alpha1" }, { "$ref": "#/$defs/network.RoutingRuleConfigV1Alpha1" }, { "$ref": "#/$defs/network.RuleConfigV1Alpha1" }, { "$ref": "#/$defs/network.StaticHostConfigV1Alpha1" }, { "$ref": "#/$defs/network.TCPProbeConfigV1Alpha1" }, { "$ref": "#/$defs/network.TimeSyncConfigV1Alpha1" }, { "$ref": "#/$defs/network.VLANConfigV1Alpha1" }, { "$ref": "#/$defs/network.VRFConfigV1Alpha1" }, { "$ref": "#/$defs/network.WireguardConfigV1Alpha1" }, { "$ref": "#/$defs/runtime.EnvironmentV1Alpha1" }, { "$ref": "#/$defs/runtime.EventSinkV1Alpha1" }, { "$ref": "#/$defs/runtime.KmsgLogV1Alpha1" }, { "$ref": "#/$defs/runtime.OOMV1Alpha1" }, { "$ref": "#/$defs/runtime.WatchdogTimerV1Alpha1" }, { "$ref": "#/$defs/security.TrustedRootsConfigV1Alpha1" }, { "$ref": "#/$defs/siderolink.ConfigV1Alpha1" }, { "$ref": "#/$defs/v1alpha1.Config" } ] } ================================================ FILE: pkg/machinery/config/schemas/v1alpha1_config.schema.json ================================================ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://talos.dev/v1.6/schemas/v1alpha1_config.schema.json", "$ref": "#/$defs/Config", "$defs": { "APIServerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the API server manifest.\n", "markdownDescription": "The container image used in the API server manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the API server manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the API server.\n", "markdownDescription": "Extra arguments to supply to the API server.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the API server.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the API server static pod.\n", "markdownDescription": "Extra volumes to mount to the API server static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the API server static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the API server’s certificate.\n", "markdownDescription": "Extra certificate subject alternative names for the API server's certificate.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the API server\u0026rsquo;s certificate.\u003c/p\u003e\n" }, "disablePodSecurityPolicy": { "type": "boolean", "title": "disablePodSecurityPolicy", "description": "Disable PodSecurityPolicy in the API server and default manifests.\n", "markdownDescription": "Disable PodSecurityPolicy in the API server and default manifests.", "x-intellij-html-description": "\u003cp\u003eDisable PodSecurityPolicy in the API server and default manifests.\u003c/p\u003e\n" }, "admissionControl": { "items": { "$ref": "#/$defs/AdmissionPluginConfig" }, "type": "array", "title": "admissionControl", "description": "Configure the API server admission plugins.\n", "markdownDescription": "Configure the API server admission plugins.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server admission plugins.\u003c/p\u003e\n" }, "auditPolicy": { "type": "object", "title": "auditPolicy", "description": "Configure the API server audit policy.\n", "markdownDescription": "Configure the API server audit policy.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server audit policy.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the API server resources.\n", "markdownDescription": "Configure the API server resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "AdminKubeconfigConfig": { "properties": { "certLifetime": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "certLifetime", "description": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eAdmin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "AdmissionPluginConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.\n", "markdownDescription": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.", "x-intellij-html-description": "\u003cp\u003eName is the name of the admission controller.\nIt must match the registered admission plugin name.\u003c/p\u003e\n" }, "configuration": { "type": "object", "title": "configuration", "description": "Configuration is an embedded configuration object to be used as the plugin’s\nconfiguration.\n", "markdownDescription": "Configuration is an embedded configuration object to be used as the plugin's\nconfiguration.", "x-intellij-html-description": "\u003cp\u003eConfiguration is an embedded configuration object to be used as the plugin\u0026rsquo;s\nconfiguration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Bond": { "properties": { "interfaces": { "items": { "type": "string" }, "type": "array", "title": "interfaces", "description": "The interfaces that make up the bond.\n", "markdownDescription": "The interfaces that make up the bond.", "x-intellij-html-description": "\u003cp\u003eThe interfaces that make up the bond.\u003c/p\u003e\n" }, "deviceSelectors": { "items": { "$ref": "#/$defs/NetworkDeviceSelector" }, "type": "array", "title": "deviceSelectors", "description": "Picks a network device using the selector.\nMutually exclusive with interfaces.\nSupports partial match using wildcard syntax.\n", "markdownDescription": "Picks a network device using the selector.\nMutually exclusive with `interfaces`.\nSupports partial match using wildcard syntax.", "x-intellij-html-description": "\u003cp\u003ePicks a network device using the selector.\nMutually exclusive with \u003ccode\u003einterfaces\u003c/code\u003e.\nSupports partial match using wildcard syntax.\u003c/p\u003e\n" }, "arpIPTarget": { "items": { "type": "string" }, "type": "array", "title": "arpIPTarget", "description": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "xmitHashPolicy": { "type": "string", "title": "xmitHashPolicy", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "lacpRate": { "type": "string", "title": "lacpRate", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adActorSystem": { "type": "string", "title": "adActorSystem", "description": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\u003c/p\u003e\n" }, "arpValidate": { "type": "string", "title": "arpValidate", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "arpAllTargets": { "type": "string", "title": "arpAllTargets", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "primary": { "type": "string", "title": "primary", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "primaryReselect": { "type": "string", "title": "primaryReselect", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "failOverMac": { "type": "string", "title": "failOverMac", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adSelect": { "type": "string", "title": "adSelect", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "miimon": { "type": "integer", "title": "miimon", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "updelay": { "type": "integer", "title": "updelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "downdelay": { "type": "integer", "title": "downdelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "arpInterval": { "type": "integer", "title": "arpInterval", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "resendIgmp": { "type": "integer", "title": "resendIgmp", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "minLinks": { "type": "integer", "title": "minLinks", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "lpInterval": { "type": "integer", "title": "lpInterval", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "packetsPerSlave": { "type": "integer", "title": "packetsPerSlave", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "numPeerNotif": { "type": "integer", "title": "numPeerNotif", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "tlbDynamicLb": { "type": "integer", "title": "tlbDynamicLb", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "allSlavesActive": { "type": "integer", "title": "allSlavesActive", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "useCarrier": { "type": "boolean", "title": "useCarrier", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adActorSysPrio": { "type": "integer", "title": "adActorSysPrio", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adUserPortKey": { "type": "integer", "title": "adUserPortKey", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "peerNotifyDelay": { "type": "integer", "title": "peerNotifyDelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Bridge": { "properties": { "interfaces": { "items": { "type": "string" }, "type": "array", "title": "interfaces", "description": "The interfaces that make up the bridge.\n", "markdownDescription": "The interfaces that make up the bridge.", "x-intellij-html-description": "\u003cp\u003eThe interfaces that make up the bridge.\u003c/p\u003e\n" }, "stp": { "$ref": "#/$defs/STP", "title": "stp", "description": "A bridge option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bridge option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "CNIConfig": { "properties": { "name": { "enum": [ "flannel", "custom", "none" ], "title": "name", "description": "Name of CNI to use.\n", "markdownDescription": "Name of CNI to use.", "x-intellij-html-description": "\u003cp\u003eName of CNI to use.\u003c/p\u003e\n" }, "urls": { "items": { "type": "string" }, "type": "array", "title": "urls", "description": "URLs containing manifests to apply for the CNI.\nShould be present for “custom”, must be empty for “flannel” and “none”.\n", "markdownDescription": "URLs containing manifests to apply for the CNI.\nShould be present for \"custom\", must be empty for \"flannel\" and \"none\".", "x-intellij-html-description": "\u003cp\u003eURLs containing manifests to apply for the CNI.\nShould be present for \u0026ldquo;custom\u0026rdquo;, must be empty for \u0026ldquo;flannel\u0026rdquo; and \u0026ldquo;none\u0026rdquo;.\u003c/p\u003e\n" }, "flannel": { "$ref": "#/$defs/FlannelCNIConfig", "title": "flannel", "description": "description: |\nFlannel configuration options.\n", "markdownDescription": "description: |\nFlannel configuration options.", "x-intellij-html-description": "\u003cp\u003edescription: |\nFlannel configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterConfig": { "properties": { "id": { "type": "string", "title": "id", "description": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).\n", "markdownDescription": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).", "x-intellij-html-description": "\u003cp\u003eGlobally unique identifier for this cluster (base64 encoded random 32 bytes).\u003c/p\u003e\n" }, "secret": { "type": "string", "title": "secret", "description": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\n", "markdownDescription": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.", "x-intellij-html-description": "\u003cp\u003eShared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/ControlPlaneConfig", "title": "controlPlane", "description": "Provides control plane specific configuration options.\n", "markdownDescription": "Provides control plane specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides control plane specific configuration options.\u003c/p\u003e\n" }, "clusterName": { "type": "string", "title": "clusterName", "description": "Configures the cluster’s name.\n", "markdownDescription": "Configures the cluster's name.", "x-intellij-html-description": "\u003cp\u003eConfigures the cluster\u0026rsquo;s name.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/ClusterNetworkConfig", "title": "network", "description": "Provides cluster specific network configuration options.\n", "markdownDescription": "Provides cluster specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific network configuration options.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The bootstrap token used to join the cluster.\n", "markdownDescription": "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.", "x-intellij-html-description": "\u003cp\u003eThe \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/\" target=\"_blank\"\u003ebootstrap token\u003c/a\u003e used to join the cluster.\u003c/p\u003e\n" }, "aescbcEncryptionSecret": { "type": "string", "title": "aescbcEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with AESCBC.\u003c/p\u003e\n" }, "secretboxEncryptionSecret": { "type": "string", "title": "secretboxEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The base64 encoded root certificate authority used by Kubernetes.\n", "markdownDescription": "The base64 encoded root certificate authority used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded root certificate authority used by Kubernetes.\u003c/p\u003e\n" }, "aggregatorCA": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "aggregatorCA", "description": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.\n", "markdownDescription": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\u003c/p\u003e\n\n\u003cp\u003eThis CA can be self-signed.\u003c/p\u003e\n" }, "serviceAccount": { "properties": { "key": { "additionalProperties": false, "type": "string" } }, "additionalProperties": false, "type": "object", "title": "serviceAccount", "description": "The base64 encoded private key for service account token generation.\n", "markdownDescription": "The base64 encoded private key for service account token generation.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded private key for service account token generation.\u003c/p\u003e\n" }, "apiServer": { "$ref": "#/$defs/APIServerConfig", "title": "apiServer", "description": "API server specific configuration options.\n", "markdownDescription": "API server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eAPI server specific configuration options.\u003c/p\u003e\n" }, "controllerManager": { "$ref": "#/$defs/ControllerManagerConfig", "title": "controllerManager", "description": "Controller manager server specific configuration options.\n", "markdownDescription": "Controller manager server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager server specific configuration options.\u003c/p\u003e\n" }, "proxy": { "$ref": "#/$defs/ProxyConfig", "title": "proxy", "description": "Kube-proxy server-specific configuration options\n", "markdownDescription": "Kube-proxy server-specific configuration options", "x-intellij-html-description": "\u003cp\u003eKube-proxy server-specific configuration options\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/SchedulerConfig", "title": "scheduler", "description": "Scheduler server specific configuration options.\n", "markdownDescription": "Scheduler server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler server specific configuration options.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/ClusterDiscoveryConfig", "title": "discovery", "description": "Configures cluster member discovery.\n", "markdownDescription": "Configures cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigures cluster member discovery.\u003c/p\u003e\n" }, "etcd": { "$ref": "#/$defs/EtcdConfig", "title": "etcd", "description": "Etcd specific configuration options.\n", "markdownDescription": "Etcd specific configuration options.", "x-intellij-html-description": "\u003cp\u003eEtcd specific configuration options.\u003c/p\u003e\n" }, "coreDNS": { "$ref": "#/$defs/CoreDNS", "title": "coreDNS", "description": "Core DNS specific configuration options.\n", "markdownDescription": "Core DNS specific configuration options.", "x-intellij-html-description": "\u003cp\u003eCore DNS specific configuration options.\u003c/p\u003e\n" }, "externalCloudProvider": { "$ref": "#/$defs/ExternalCloudProviderConfig", "title": "externalCloudProvider", "description": "External cloud provider configuration.\n", "markdownDescription": "External cloud provider configuration.", "x-intellij-html-description": "\u003cp\u003eExternal cloud provider configuration.\u003c/p\u003e\n" }, "extraManifests": { "items": { "type": "string" }, "type": "array", "title": "extraManifests", "description": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "extraManifestHeaders": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraManifestHeaders", "description": "A map of key value pairs that will be added while fetching the extraManifests.\n", "markdownDescription": "A map of key value pairs that will be added while fetching the extraManifests.", "x-intellij-html-description": "\u003cp\u003eA map of key value pairs that will be added while fetching the extraManifests.\u003c/p\u003e\n" }, "inlineManifests": { "items": { "$ref": "#/$defs/ClusterInlineManifest" }, "type": "array", "title": "inlineManifests", "description": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "adminKubeconfig": { "$ref": "#/$defs/AdminKubeconfigConfig", "title": "adminKubeconfig", "description": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.\n", "markdownDescription": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.", "x-intellij-html-description": "\u003cp\u003eSettings for admin kubeconfig generation.\nCertificate lifetime can be configured.\u003c/p\u003e\n" }, "allowSchedulingOnControlPlanes": { "type": "boolean", "title": "allowSchedulingOnControlPlanes", "description": "Allows running workload on control-plane nodes.\n", "markdownDescription": "Allows running workload on control-plane nodes.", "x-intellij-html-description": "\u003cp\u003eAllows running workload on control-plane nodes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterDiscoveryConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\n", "markdownDescription": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.", "x-intellij-html-description": "\u003cp\u003eEnable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/DiscoveryRegistriesConfig", "title": "registries", "description": "Configure registries used for cluster member discovery.\n", "markdownDescription": "Configure registries used for cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigure registries used for cluster member discovery.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterInlineManifest": { "properties": { "name": { "type": "string", "title": "name", "description": "Name of the manifest.\nName should be unique.\n", "markdownDescription": "Name of the manifest.\nName should be unique.", "x-intellij-html-description": "\u003cp\u003eName of the manifest.\nName should be unique.\u003c/p\u003e\n" }, "contents": { "type": "string", "title": "contents", "description": "Manifest contents as a string.\n", "markdownDescription": "Manifest contents as a string.", "x-intellij-html-description": "\u003cp\u003eManifest contents as a string.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterNetworkConfig": { "properties": { "cni": { "$ref": "#/$defs/CNIConfig", "title": "cni", "description": "The CNI used.\nComposed of “name” and “urls”.\nThe “name” key supports the following options: “flannel”, “custom”, and “none”.\n“flannel” uses Talos-managed Flannel CNI, and that’s the default option.\n“custom” uses custom manifests that should be provided in “urls”.\n“none” indicates that Talos will not manage any CNI installation.\n", "markdownDescription": "The CNI used.\nComposed of \"name\" and \"urls\".\nThe \"name\" key supports the following options: \"flannel\", \"custom\", and \"none\".\n\"flannel\" uses Talos-managed Flannel CNI, and that's the default option.\n\"custom\" uses custom manifests that should be provided in \"urls\".\n\"none\" indicates that Talos will not manage any CNI installation.", "x-intellij-html-description": "\u003cp\u003eThe CNI used.\nComposed of \u0026ldquo;name\u0026rdquo; and \u0026ldquo;urls\u0026rdquo;.\nThe \u0026ldquo;name\u0026rdquo; key supports the following options: \u0026ldquo;flannel\u0026rdquo;, \u0026ldquo;custom\u0026rdquo;, and \u0026ldquo;none\u0026rdquo;.\n\u0026ldquo;flannel\u0026rdquo; uses Talos-managed Flannel CNI, and that\u0026rsquo;s the default option.\n\u0026ldquo;custom\u0026rdquo; uses custom manifests that should be provided in \u0026ldquo;urls\u0026rdquo;.\n\u0026ldquo;none\u0026rdquo; indicates that Talos will not manage any CNI installation.\u003c/p\u003e\n" }, "dnsDomain": { "type": "string", "title": "dnsDomain", "description": "The domain used by Kubernetes DNS.\nThe default is cluster.local\n", "markdownDescription": "The domain used by Kubernetes DNS.\nThe default is `cluster.local`", "x-intellij-html-description": "\u003cp\u003eThe domain used by Kubernetes DNS.\nThe default is \u003ccode\u003ecluster.local\u003c/code\u003e\u003c/p\u003e\n" }, "podSubnets": { "items": { "type": "string" }, "type": "array", "title": "podSubnets", "description": "The pod subnet CIDR.\n", "markdownDescription": "The pod subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe pod subnet CIDR.\u003c/p\u003e\n" }, "serviceSubnets": { "items": { "type": "string" }, "type": "array", "title": "serviceSubnets", "description": "The service subnet CIDR.\n", "markdownDescription": "The service subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe service subnet CIDR.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Config": { "properties": { "version": { "enum": [ "v1alpha1" ], "title": "version", "description": "Indicates the schema used to decode the contents.\n", "markdownDescription": "Indicates the schema used to decode the contents.", "x-intellij-html-description": "\u003cp\u003eIndicates the schema used to decode the contents.\u003c/p\u003e\n" }, "debug": { "type": "boolean", "title": "debug", "description": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\nNote: To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\n", "markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.", "x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n" }, "machine": { "$ref": "#/$defs/MachineConfig", "title": "machine", "description": "Provides machine specific configuration options.\n", "markdownDescription": "Provides machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific configuration options.\u003c/p\u003e\n" }, "cluster": { "$ref": "#/$defs/ClusterConfig", "title": "cluster", "description": "Provides cluster specific configuration options.\n", "markdownDescription": "Provides cluster specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ControlPlaneConfig": { "properties": { "endpoint": { "type": "string", "pattern": "^https://", "format": "uri", "title": "endpoint", "description": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\n", "markdownDescription": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.", "x-intellij-html-description": "\u003cp\u003eEndpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\u003c/p\u003e\n" }, "localAPIServerPort": { "type": "integer", "title": "localAPIServerPort", "description": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is 6443.\n", "markdownDescription": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is `6443`.", "x-intellij-html-description": "\u003cp\u003eThe port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is \u003ccode\u003e6443\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ControllerManagerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the controller manager manifest.\n", "markdownDescription": "The container image used in the controller manager manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the controller manager manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the controller manager.\n", "markdownDescription": "Extra arguments to supply to the controller manager.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the controller manager.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the controller manager static pod.\n", "markdownDescription": "Extra volumes to mount to the controller manager static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the controller manager static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the controller manager resources.\n", "markdownDescription": "Configure the controller manager resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the controller manager resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "CoreDNS": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable coredns deployment on cluster bootstrap.\n", "markdownDescription": "Disable coredns deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable coredns deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The image field is an override to the default coredns image.\n", "markdownDescription": "The `image` field is an override to the default coredns image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default coredns image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DHCPOptions": { "properties": { "routeMetric": { "type": "integer", "title": "routeMetric", "description": "The priority of all routes received via DHCP.\n", "markdownDescription": "The priority of all routes received via DHCP.", "x-intellij-html-description": "\u003cp\u003eThe priority of all routes received via DHCP.\u003c/p\u003e\n" }, "ipv4": { "type": "boolean", "title": "ipv4", "description": "Enables DHCPv4 protocol for the interface (default is enabled).\n", "markdownDescription": "Enables DHCPv4 protocol for the interface (default is enabled).", "x-intellij-html-description": "\u003cp\u003eEnables DHCPv4 protocol for the interface (default is enabled).\u003c/p\u003e\n" }, "ipv6": { "type": "boolean", "title": "ipv6", "description": "Enables DHCPv6 protocol for the interface (default is disabled).\n", "markdownDescription": "Enables DHCPv6 protocol for the interface (default is disabled).", "x-intellij-html-description": "\u003cp\u003eEnables DHCPv6 protocol for the interface (default is disabled).\u003c/p\u003e\n" }, "duidv6": { "type": "string", "title": "duidv6", "description": "Set client DUID (hex string).\n", "markdownDescription": "Set client DUID (hex string).", "x-intellij-html-description": "\u003cp\u003eSet client DUID (hex string).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Device": { "properties": { "interface": { "type": "string", "title": "interface", "description": "The interface name.\nMutually exclusive with deviceSelector.\n", "markdownDescription": "The interface name.\nMutually exclusive with `deviceSelector`.", "x-intellij-html-description": "\u003cp\u003eThe interface name.\nMutually exclusive with \u003ccode\u003edeviceSelector\u003c/code\u003e.\u003c/p\u003e\n" }, "deviceSelector": { "$ref": "#/$defs/NetworkDeviceSelector", "title": "deviceSelector", "description": "Picks a network device using the selector.\nMutually exclusive with interface.\nSupports partial match using wildcard syntax.\n", "markdownDescription": "Picks a network device using the selector.\nMutually exclusive with `interface`.\nSupports partial match using wildcard syntax.", "x-intellij-html-description": "\u003cp\u003ePicks a network device using the selector.\nMutually exclusive with \u003ccode\u003einterface\u003c/code\u003e.\nSupports partial match using wildcard syntax.\u003c/p\u003e\n" }, "addresses": { "items": { "type": "string" }, "type": "array", "title": "addresses", "description": "Assigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).\n", "markdownDescription": "Assigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).", "x-intellij-html-description": "\u003cp\u003eAssigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/Route" }, "type": "array", "title": "routes", "description": "A list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.\n", "markdownDescription": "A list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.", "x-intellij-html-description": "\u003cp\u003eA list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.\u003c/p\u003e\n" }, "bond": { "$ref": "#/$defs/Bond", "title": "bond", "description": "Bond specific options.\n", "markdownDescription": "Bond specific options.", "x-intellij-html-description": "\u003cp\u003eBond specific options.\u003c/p\u003e\n" }, "bridge": { "$ref": "#/$defs/Bridge", "title": "bridge", "description": "Bridge specific options.\n", "markdownDescription": "Bridge specific options.", "x-intellij-html-description": "\u003cp\u003eBridge specific options.\u003c/p\u003e\n" }, "vlans": { "items": { "$ref": "#/$defs/Vlan" }, "type": "array", "title": "vlans", "description": "VLAN specific options.\n", "markdownDescription": "VLAN specific options.", "x-intellij-html-description": "\u003cp\u003eVLAN specific options.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The interface’s MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.\n", "markdownDescription": "The interface's MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.", "x-intellij-html-description": "\u003cp\u003eThe interface\u0026rsquo;s MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.\u003c/p\u003e\n" }, "dhcp": { "type": "boolean", "title": "dhcp", "description": "Indicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\n\n\nOptionClasslessStaticRoute\nOptionDomainNameServer\nOptionDNSDomainSearchList\nOptionHostName\n\n", "markdownDescription": "Indicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\n\n- `OptionClasslessStaticRoute`\n- `OptionDomainNameServer`\n- `OptionDNSDomainSearchList`\n- `OptionHostName`", "x-intellij-html-description": "\u003cp\u003eIndicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eOptionClasslessStaticRoute\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionDomainNameServer\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionDNSDomainSearchList\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionHostName\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "ignore": { "type": "boolean", "title": "ignore", "description": "Indicates if the interface should be ignored (skips configuration).\n", "markdownDescription": "Indicates if the interface should be ignored (skips configuration).", "x-intellij-html-description": "\u003cp\u003eIndicates if the interface should be ignored (skips configuration).\u003c/p\u003e\n" }, "dummy": { "type": "boolean", "title": "dummy", "description": "Indicates if the interface is a dummy interface.\ndummy is used to specify that this interface should be a virtual-only, dummy interface.\n", "markdownDescription": "Indicates if the interface is a dummy interface.\n`dummy` is used to specify that this interface should be a virtual-only, dummy interface.", "x-intellij-html-description": "\u003cp\u003eIndicates if the interface is a dummy interface.\n\u003ccode\u003edummy\u003c/code\u003e is used to specify that this interface should be a virtual-only, dummy interface.\u003c/p\u003e\n" }, "dhcpOptions": { "$ref": "#/$defs/DHCPOptions", "title": "dhcpOptions", "description": "DHCP specific options.\ndhcp must be set to true for these to take effect.\n", "markdownDescription": "DHCP specific options.\n`dhcp` *must* be set to true for these to take effect.", "x-intellij-html-description": "\u003cp\u003eDHCP specific options.\n\u003ccode\u003edhcp\u003c/code\u003e \u003cem\u003emust\u003c/em\u003e be set to true for these to take effect.\u003c/p\u003e\n" }, "wireguard": { "$ref": "#/$defs/DeviceWireguardConfig", "title": "wireguard", "description": "Wireguard specific configuration.\nIncludes things like private key, listen port, peers.\n", "markdownDescription": "Wireguard specific configuration.\nIncludes things like private key, listen port, peers.", "x-intellij-html-description": "\u003cp\u003eWireguard specific configuration.\nIncludes things like private key, listen port, peers.\u003c/p\u003e\n" }, "vip": { "$ref": "#/$defs/DeviceVIPConfig", "title": "vip", "description": "Virtual (shared) IP address configuration.\n", "markdownDescription": "Virtual (shared) IP address configuration.", "x-intellij-html-description": "\u003cp\u003eVirtual (shared) IP address configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceVIPConfig": { "properties": { "ip": { "type": "string", "title": "ip", "description": "Specifies the IP address to be used.\n", "markdownDescription": "Specifies the IP address to be used.", "x-intellij-html-description": "\u003cp\u003eSpecifies the IP address to be used.\u003c/p\u003e\n" }, "equinixMetal": { "$ref": "#/$defs/VIPEquinixMetalConfig", "title": "equinixMetal", "description": "Specifies the Equinix Metal API settings to assign VIP to the node.\n", "markdownDescription": "Specifies the Equinix Metal API settings to assign VIP to the node.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Equinix Metal API settings to assign VIP to the node.\u003c/p\u003e\n" }, "hcloud": { "$ref": "#/$defs/VIPHCloudConfig", "title": "hcloud", "description": "Specifies the Hetzner Cloud API settings to assign VIP to the node.\n", "markdownDescription": "Specifies the Hetzner Cloud API settings to assign VIP to the node.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API settings to assign VIP to the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceWireguardConfig": { "properties": { "privateKey": { "type": "string", "title": "privateKey", "description": "Specifies a private key configuration (base64 encoded).\nCan be generated by wg genkey.\n", "markdownDescription": "Specifies a private key configuration (base64 encoded).\nCan be generated by `wg genkey`.", "x-intellij-html-description": "\u003cp\u003eSpecifies a private key configuration (base64 encoded).\nCan be generated by \u003ccode\u003ewg genkey\u003c/code\u003e.\u003c/p\u003e\n" }, "listenPort": { "type": "integer", "title": "listenPort", "description": "Specifies a device’s listening port.\n", "markdownDescription": "Specifies a device's listening port.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s listening port.\u003c/p\u003e\n" }, "firewallMark": { "type": "integer", "title": "firewallMark", "description": "Specifies a device’s firewall mark.\n", "markdownDescription": "Specifies a device's firewall mark.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s firewall mark.\u003c/p\u003e\n" }, "peers": { "items": { "$ref": "#/$defs/DeviceWireguardPeer" }, "type": "array", "title": "peers", "description": "Specifies a list of peer configurations to apply to a device.\n", "markdownDescription": "Specifies a list of peer configurations to apply to a device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a list of peer configurations to apply to a device.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceWireguardPeer": { "properties": { "publicKey": { "type": "string", "title": "publicKey", "description": "Specifies the public key of this peer.\nCan be extracted from private key by running wg pubkey \u0026lt; private.key \u0026gt; public.key \u0026amp;\u0026amp; cat public.key.\n", "markdownDescription": "Specifies the public key of this peer.\nCan be extracted from private key by running `wg pubkey \u003c private.key \u003e public.key \u0026\u0026 cat public.key`.", "x-intellij-html-description": "\u003cp\u003eSpecifies the public key of this peer.\nCan be extracted from private key by running \u003ccode\u003ewg pubkey \u0026lt; private.key \u0026gt; public.key \u0026amp;\u0026amp; cat public.key\u003c/code\u003e.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "Specifies the endpoint of this peer entry.\n", "markdownDescription": "Specifies the endpoint of this peer entry.", "x-intellij-html-description": "\u003cp\u003eSpecifies the endpoint of this peer entry.\u003c/p\u003e\n" }, "persistentKeepaliveInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "persistentKeepaliveInterval", "description": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eSpecifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" }, "allowedIPs": { "items": { "type": "string" }, "type": "array", "title": "allowedIPs", "description": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\n", "markdownDescription": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.", "x-intellij-html-description": "\u003cp\u003eAllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DiscoveryRegistriesConfig": { "properties": { "kubernetes": { "$ref": "#/$defs/RegistryKubernetesConfig", "title": "kubernetes", "description": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n", "markdownDescription": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.", "x-intellij-html-description": "\u003cp\u003eKubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\u003c/p\u003e\n" }, "service": { "$ref": "#/$defs/RegistryServiceConfig", "title": "service", "description": "Service registry is using an external service to push and pull information about cluster members.\n", "markdownDescription": "Service registry is using an external service to push and pull information about cluster members.", "x-intellij-html-description": "\u003cp\u003eService registry is using an external service to push and pull information about cluster members.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DiskPartition": { "properties": { "size": { "type": "integer", "title": "size", "description": "The size of partition: either bytes or human readable representation. If size: is omitted, the partition is sized to occupy the full disk.\n", "markdownDescription": "The size of partition: either bytes or human readable representation. If `size:` is omitted, the partition is sized to occupy the full disk.", "x-intellij-html-description": "\u003cp\u003eThe size of partition: either bytes or human readable representation. If \u003ccode\u003esize:\u003c/code\u003e is omitted, the partition is sized to occupy the full disk.\u003c/p\u003e\n" }, "mountpoint": { "type": "string", "title": "mountpoint", "description": "Where to mount the partition.\n", "markdownDescription": "Where to mount the partition.", "x-intellij-html-description": "\u003cp\u003eWhere to mount the partition.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionConfig": { "properties": { "provider": { "type": "string", "title": "provider", "description": "Encryption provider to use for the encryption.\n", "markdownDescription": "Encryption provider to use for the encryption.", "x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n" }, "keys": { "items": { "$ref": "#/$defs/EncryptionKey" }, "type": "array", "title": "keys", "description": "Defines the encryption keys generation and storage method.\n", "markdownDescription": "Defines the encryption keys generation and storage method.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n" }, "cipher": { "enum": [ "aes-xts-plain64", "xchacha12,aes-adiantum-plain64", "xchacha20,aes-adiantum-plain64" ], "title": "cipher", "description": "Cipher kind to use for the encryption. Depends on the encryption provider.\n", "markdownDescription": "Cipher kind to use for the encryption. Depends on the encryption provider.", "x-intellij-html-description": "\u003cp\u003eCipher kind to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n" }, "keySize": { "type": "integer", "title": "keySize", "description": "Defines the encryption key length.\n", "markdownDescription": "Defines the encryption key length.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n" }, "blockSize": { "type": "integer", "title": "blockSize", "description": "Defines the encryption sector size.\n", "markdownDescription": "Defines the encryption sector size.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n" }, "options": { "enum": [ "no_read_workqueue", "no_write_workqueue", "same_cpu_crypt" ], "title": "options", "description": "Additional –perf parameters for the LUKS2 encryption.\n", "markdownDescription": "Additional --perf parameters for the LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKey": { "properties": { "static": { "$ref": "#/$defs/EncryptionKeyStatic", "title": "static", "description": "Key which value is stored in the configuration file.\n", "markdownDescription": "Key which value is stored in the configuration file.", "x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n" }, "nodeID": { "$ref": "#/$defs/EncryptionKeyNodeID", "title": "nodeID", "description": "Deterministically generated key from the node UUID and PartitionLabel.\n", "markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.", "x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n" }, "kms": { "$ref": "#/$defs/EncryptionKeyKMS", "title": "kms", "description": "KMS managed encryption key.\n", "markdownDescription": "KMS managed encryption key.", "x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n" }, "slot": { "type": "integer", "title": "slot", "description": "Key slot number for LUKS2 encryption.\n", "markdownDescription": "Key slot number for LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n" }, "tpm": { "$ref": "#/$defs/EncryptionKeyTPM", "title": "tpm", "description": "Enable TPM based disk encryption.\n", "markdownDescription": "Enable TPM based disk encryption.", "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyKMS": { "properties": { "endpoint": { "type": "string", "title": "endpoint", "description": "KMS endpoint to Seal/Unseal the key.\n", "markdownDescription": "KMS endpoint to Seal/Unseal the key.", "x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyNodeID": { "properties": {}, "additionalProperties": false, "type": "object" }, "EncryptionKeyStatic": { "properties": { "passphrase": { "type": "string", "title": "passphrase", "description": "Defines the static passphrase value.\n", "markdownDescription": "Defines the static passphrase value.", "x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyTPM": { "properties": {}, "additionalProperties": false, "type": "object" }, "Endpoint": { "properties": {}, "additionalProperties": false, "type": "object" }, "EtcdConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used to create the etcd service.\n", "markdownDescription": "The container image used to create the etcd service.", "x-intellij-html-description": "\u003cp\u003eThe container image used to create the etcd service.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The ca is the root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The `ca` is the root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eca\u003c/code\u003e is the root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n\nname\ndata-dir\ninitial-cluster-state\nlisten-peer-urls\nlisten-client-urls\ncert-file\nkey-file\ntrusted-ca-file\npeer-client-cert-auth\npeer-cert-file\npeer-trusted-ca-file\npeer-key-file\n\n", "markdownDescription": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n- `name`\n- `data-dir`\n- `initial-cluster-state`\n- `listen-peer-urls`\n- `listen-client-urls`\n- `cert-file`\n- `key-file`\n- `trusted-ca-file`\n- `peer-client-cert-auth`\n- `peer-cert-file`\n- `peer-trusted-ca-file`\n- `peer-key-file`", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to etcd.\nNote that the following args are not allowed:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edata-dir\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003einitial-cluster-state\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-peer-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-client-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ekey-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etrusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-client-cert-auth\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-cert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-trusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-key-file\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "advertisedSubnets": { "items": { "type": "string" }, "type": "array", "title": "advertisedSubnets", "description": "The advertisedSubnets field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eadvertisedSubnets\u003c/code\u003e field configures the networks to pick etcd advertised IP from.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" }, "listenSubnets": { "items": { "type": "string" }, "type": "array", "title": "listenSubnets", "description": "The listenSubnets field configures the networks for the etcd to listen for peer and client connections.\n\nIf listenSubnets is not set, but advertisedSubnets is set, listenSubnets defaults to\nadvertisedSubnets.\n\nIf neither advertisedSubnets nor listenSubnets is set, listenSubnets defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.\n\nIf `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to\n`advertisedSubnets`.\n\nIf neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003elistenSubnets\u003c/code\u003e field configures the networks for the etcd to listen for peer and client connections.\u003c/p\u003e\n\n\u003cp\u003eIf \u003ccode\u003elistenSubnets\u003c/code\u003e is not set, but \u003ccode\u003eadvertisedSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to\n\u003ccode\u003eadvertisedSubnets\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eIf neither \u003ccode\u003eadvertisedSubnets\u003c/code\u003e nor \u003ccode\u003elistenSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to listen on all addresses.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExternalCloudProviderConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable external cloud provider.\n", "markdownDescription": "Enable external cloud provider.", "x-intellij-html-description": "\u003cp\u003eEnable external cloud provider.\u003c/p\u003e\n" }, "manifests": { "items": { "type": "string" }, "type": "array", "title": "manifests", "description": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExtraHost": { "properties": { "ip": { "type": "string", "title": "ip", "description": "The IP of the host.\n", "markdownDescription": "The IP of the host.", "x-intellij-html-description": "\u003cp\u003eThe IP of the host.\u003c/p\u003e\n" }, "aliases": { "items": { "type": "string" }, "type": "array", "title": "aliases", "description": "The host alias.\n", "markdownDescription": "The host alias.", "x-intellij-html-description": "\u003cp\u003eThe host alias.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExtraMount": { "properties": { "destination": { "type": "string", "title": "destination", "description": "Destination is the absolute path where the mount will be placed in the container.\n", "markdownDescription": "Destination is the absolute path where the mount will be placed in the container.", "x-intellij-html-description": "\u003cp\u003eDestination is the absolute path where the mount will be placed in the container.\u003c/p\u003e\n" }, "type": { "type": "string", "title": "type", "description": "Type specifies the mount kind.\n", "markdownDescription": "Type specifies the mount kind.", "x-intellij-html-description": "\u003cp\u003eType specifies the mount kind.\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "Source specifies the source path of the mount.\n", "markdownDescription": "Source specifies the source path of the mount.", "x-intellij-html-description": "\u003cp\u003eSource specifies the source path of the mount.\u003c/p\u003e\n" }, "options": { "items": { "type": "string" }, "type": "array", "title": "options", "description": "Options are fstab style mount options.\n", "markdownDescription": "Options are fstab style mount options.", "x-intellij-html-description": "\u003cp\u003eOptions are fstab style mount options.\u003c/p\u003e\n" }, "uidMappings": { "items": { "$ref": "#/$defs/LinuxIDMapping" }, "type": "array", "title": "uidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" }, "gidMappings": { "items": { "$ref": "#/$defs/LinuxIDMapping" }, "type": "array", "title": "gidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "FeaturesConfig": { "properties": { "rbac": { "type": "boolean", "title": "rbac", "description": "Enable role-based access control (RBAC).\n", "markdownDescription": "Enable role-based access control (RBAC).", "x-intellij-html-description": "\u003cp\u003eEnable role-based access control (RBAC).\u003c/p\u003e\n" }, "stableHostname": { "type": "boolean", "title": "stableHostname", "description": "Enable stable default hostname.\n", "markdownDescription": "Enable stable default hostname.", "x-intellij-html-description": "\u003cp\u003eEnable stable default hostname.\u003c/p\u003e\n" }, "kubernetesTalosAPIAccess": { "$ref": "#/$defs/KubernetesTalosAPIAccessConfig", "title": "kubernetesTalosAPIAccess", "description": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.\n", "markdownDescription": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.", "x-intellij-html-description": "\u003cp\u003eConfigure Talos API access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eThis feature is disabled if the feature config is not specified.\u003c/p\u003e\n" }, "apidCheckExtKeyUsage": { "type": "boolean", "title": "apidCheckExtKeyUsage", "description": "Enable checks for extended key usage of client certificates in apid.\n", "markdownDescription": "Enable checks for extended key usage of client certificates in apid.", "x-intellij-html-description": "\u003cp\u003eEnable checks for extended key usage of client certificates in apid.\u003c/p\u003e\n" }, "diskQuotaSupport": { "type": "boolean", "title": "diskQuotaSupport", "description": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\n", "markdownDescription": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.", "x-intellij-html-description": "\u003cp\u003eEnable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\u003c/p\u003e\n" }, "kubePrism": { "$ref": "#/$defs/KubePrism", "title": "kubePrism", "description": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\n", "markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.", "x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "FlannelCNIConfig": { "properties": { "extraArgs": { "items": { "type": "string" }, "type": "array", "title": "extraArgs", "description": "Extra arguments for ‘flanneld’.\n", "markdownDescription": "Extra arguments for 'flanneld'.", "x-intellij-html-description": "\u003cp\u003eExtra arguments for \u0026lsquo;flanneld\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallConfig": { "properties": { "disk": { "type": "string", "title": "disk", "description": "The disk used for installations.\n", "markdownDescription": "The disk used for installations.", "x-intellij-html-description": "\u003cp\u003eThe disk used for installations.\u003c/p\u003e\n" }, "diskSelector": { "$ref": "#/$defs/InstallDiskSelector", "title": "diskSelector", "description": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over disk.\n", "markdownDescription": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over `disk`.", "x-intellij-html-description": "\u003cp\u003eLook up disk using disk attributes like model, size, serial and others.\nAlways has priority over \u003ccode\u003edisk\u003c/code\u003e.\u003c/p\u003e\n" }, "extraKernelArgs": { "items": { "type": "string" }, "type": "array", "title": "extraKernelArgs", "description": "Allows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a -.\nFor example -console removes all console=\u0026lt;value\u0026gt; arguments, whereas -console=tty0 removes the console=tty0 default argument.\n", "markdownDescription": "Allows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a `-`.\nFor example `-console` removes all `console=\u003cvalue\u003e` arguments, whereas `-console=tty0` removes the `console=tty0` default argument.", "x-intellij-html-description": "\u003cp\u003eAllows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a \u003ccode\u003e-\u003c/code\u003e.\nFor example \u003ccode\u003e-console\u003c/code\u003e removes all \u003ccode\u003econsole=\u0026lt;value\u0026gt;\u003c/code\u003e arguments, whereas \u003ccode\u003e-console=tty0\u003c/code\u003e removes the \u003ccode\u003econsole=tty0\u003c/code\u003e default argument.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\nGitHub releases page.\n", "markdownDescription": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n[GitHub releases page](https://github.com/siderolabs/talos/releases).", "x-intellij-html-description": "\u003cp\u003eAllows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n\u003ca href=\"https://github.com/siderolabs/talos/releases\" target=\"_blank\"\u003eGitHub releases page\u003c/a\u003e.\u003c/p\u003e\n" }, "extensions": { "items": { "$ref": "#/$defs/InstallExtensionConfig" }, "type": "array", "title": "extensions", "description": "Allows for supplying additional system extension images to install on top of base Talos image.\n", "markdownDescription": "Allows for supplying additional system extension images to install on top of base Talos image.", "x-intellij-html-description": "\u003cp\u003eAllows for supplying additional system extension images to install on top of base Talos image.\u003c/p\u003e\n" }, "wipe": { "type": "boolean", "title": "wipe", "description": "Indicates if the installation disk should be wiped at installation time.\nDefaults to true.\n", "markdownDescription": "Indicates if the installation disk should be wiped at installation time.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the installation disk should be wiped at installation time.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "legacyBIOSSupport": { "type": "boolean", "title": "legacyBIOSSupport", "description": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn’t support GPT partitioning scheme.\n", "markdownDescription": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme.", "x-intellij-html-description": "\u003cp\u003eIndicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn\u0026rsquo;t support GPT partitioning scheme.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallDiskSelector": { "properties": { "size": { "type": "string", "title": "size", "description": "Disk size.\n", "markdownDescription": "Disk size.", "x-intellij-html-description": "\u003cp\u003eDisk size.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Disk name /sys/block/\u0026lt;dev\u0026gt;/device/name.\n", "markdownDescription": "Disk name `/sys/block/\u003cdev\u003e/device/name`.", "x-intellij-html-description": "\u003cp\u003eDisk name \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/name\u003c/code\u003e.\u003c/p\u003e\n" }, "model": { "type": "string", "title": "model", "description": "Disk model /sys/block/\u0026lt;dev\u0026gt;/device/model.\n", "markdownDescription": "Disk model `/sys/block/\u003cdev\u003e/device/model`.", "x-intellij-html-description": "\u003cp\u003eDisk model \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/model\u003c/code\u003e.\u003c/p\u003e\n" }, "serial": { "type": "string", "title": "serial", "description": "Disk serial number /sys/block/\u0026lt;dev\u0026gt;/serial.\n", "markdownDescription": "Disk serial number `/sys/block/\u003cdev\u003e/serial`.", "x-intellij-html-description": "\u003cp\u003eDisk serial number \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/serial\u003c/code\u003e.\u003c/p\u003e\n" }, "modalias": { "type": "string", "title": "modalias", "description": "Disk modalias /sys/block/\u0026lt;dev\u0026gt;/device/modalias.\n", "markdownDescription": "Disk modalias `/sys/block/\u003cdev\u003e/device/modalias`.", "x-intellij-html-description": "\u003cp\u003eDisk modalias \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/modalias\u003c/code\u003e.\u003c/p\u003e\n" }, "uuid": { "type": "string", "title": "uuid", "description": "Disk UUID /sys/block/\u0026lt;dev\u0026gt;/uuid.\n", "markdownDescription": "Disk UUID `/sys/block/\u003cdev\u003e/uuid`.", "x-intellij-html-description": "\u003cp\u003eDisk UUID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/uuid\u003c/code\u003e.\u003c/p\u003e\n" }, "wwid": { "type": "string", "title": "wwid", "description": "Disk WWID /sys/block/\u0026lt;dev\u0026gt;/wwid.\n", "markdownDescription": "Disk WWID `/sys/block/\u003cdev\u003e/wwid`.", "x-intellij-html-description": "\u003cp\u003eDisk WWID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/wwid\u003c/code\u003e.\u003c/p\u003e\n" }, "type": { "enum": [ "ssd", "hdd", "nvme", "sd" ], "title": "type", "description": "Disk Type.\n", "markdownDescription": "Disk Type.", "x-intellij-html-description": "\u003cp\u003eDisk Type.\u003c/p\u003e\n" }, "busPath": { "type": "string", "title": "busPath", "description": "Disk bus path.\n", "markdownDescription": "Disk bus path.", "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallExtensionConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "System extension image.\n", "markdownDescription": "System extension image.", "x-intellij-html-description": "\u003cp\u003eSystem extension image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KernelConfig": { "properties": { "modules": { "items": { "$ref": "#/$defs/KernelModuleConfig" }, "type": "array", "title": "modules", "description": "Kernel modules to load.\n", "markdownDescription": "Kernel modules to load.", "x-intellij-html-description": "\u003cp\u003eKernel modules to load.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KernelModuleConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Module name.\n", "markdownDescription": "Module name.", "x-intellij-html-description": "\u003cp\u003eModule name.\u003c/p\u003e\n" }, "parameters": { "items": { "type": "string" }, "type": "array", "title": "parameters", "description": "Module parameters, changes applied after reboot.\n", "markdownDescription": "Module parameters, changes applied after reboot.", "x-intellij-html-description": "\u003cp\u003eModule parameters, changes applied after reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubePrism": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable KubePrism support - will start local load balacing proxy.\n", "markdownDescription": "Enable KubePrism support - will start local load balacing proxy.", "x-intellij-html-description": "\u003cp\u003eEnable KubePrism support - will start local load balacing proxy.\u003c/p\u003e\n" }, "port": { "type": "integer", "title": "port", "description": "KubePrism port.\n", "markdownDescription": "KubePrism port.", "x-intellij-html-description": "\u003cp\u003eKubePrism port.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeSpanFilters": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.\n", "markdownDescription": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\u003c/p\u003e\n\n\u003cp\u003eBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeletConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The image field is an optional reference to an alternative kubelet image.\n", "markdownDescription": "The `image` field is an optional reference to an alternative kubelet image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an optional reference to an alternative kubelet image.\u003c/p\u003e\n" }, "clusterDNS": { "items": { "type": "string" }, "type": "array", "title": "clusterDNS", "description": "The ClusterDNS field is an optional reference to an alternative kubelet clusterDNS ip list.\n", "markdownDescription": "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eClusterDNS\u003c/code\u003e field is an optional reference to an alternative kubelet clusterDNS ip list.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "The extraArgs field is used to provide additional flags to the kubelet.\n", "markdownDescription": "The `extraArgs` field is used to provide additional flags to the kubelet.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraArgs\u003c/code\u003e field is used to provide additional flags to the kubelet.\u003c/p\u003e\n" }, "extraMounts": { "items": { "$ref": "#/$defs/ExtraMount" }, "type": "array", "title": "extraMounts", "description": "The extraMounts field is used to add additional mounts to the kubelet container.\nNote that either bind or rbind are required in the options.\n", "markdownDescription": "The `extraMounts` field is used to add additional mounts to the kubelet container.\nNote that either `bind` or `rbind` are required in the `options`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraMounts\u003c/code\u003e field is used to add additional mounts to the kubelet container.\nNote that either \u003ccode\u003ebind\u003c/code\u003e or \u003ccode\u003erbind\u003c/code\u003e are required in the \u003ccode\u003eoptions\u003c/code\u003e.\u003c/p\u003e\n" }, "extraConfig": { "type": "object", "title": "extraConfig", "description": "The extraConfig field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\n", "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, "credentialProviderConfig": { "type": "object", "title": "credentialProviderConfig", "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled", "description": "Enable container runtime default Seccomp profile.\n", "markdownDescription": "Enable container runtime default Seccomp profile.", "x-intellij-html-description": "\u003cp\u003eEnable container runtime default Seccomp profile.\u003c/p\u003e\n" }, "registerWithFQDN": { "type": "boolean", "title": "registerWithFQDN", "description": "The registerWithFQDN field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\n", "markdownDescription": "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eregisterWithFQDN\u003c/code\u003e field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\u003c/p\u003e\n" }, "nodeIP": { "$ref": "#/$defs/KubeletNodeIPConfig", "title": "nodeIP", "description": "The nodeIP field is used to configure --node-ip flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\n", "markdownDescription": "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003enodeIP\u003c/code\u003e field is used to configure \u003ccode\u003e--node-ip\u003c/code\u003e flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\u003c/p\u003e\n" }, "skipNodeRegistration": { "type": "boolean", "title": "skipNodeRegistration", "description": "The skipNodeRegistration is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\n", "markdownDescription": "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eskipNodeRegistration\u003c/code\u003e is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\u003c/p\u003e\n" }, "disableManifestsDirectory": { "type": "boolean", "title": "disableManifestsDirectory", "description": "The disableManifestsDirectory field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt’s recommended to configure static pods with the “pods” key instead.\n", "markdownDescription": "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt's recommended to configure static pods with the \"pods\" key instead.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003edisableManifestsDirectory\u003c/code\u003e field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt\u0026rsquo;s recommended to configure static pods with the \u0026ldquo;pods\u0026rdquo; key instead.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeletNodeIPConfig": { "properties": { "validSubnets": { "items": { "type": "string" }, "type": "array", "title": "validSubnets", "description": "The validSubnets field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\n", "markdownDescription": "The `validSubnets` field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalidSubnets\u003c/code\u003e field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubernetesTalosAPIAccessConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable Talos API access from Kubernetes pods.\n", "markdownDescription": "Enable Talos API access from Kubernetes pods.", "x-intellij-html-description": "\u003cp\u003eEnable Talos API access from Kubernetes pods.\u003c/p\u003e\n" }, "allowedRoles": { "items": { "type": "string" }, "type": "array", "title": "allowedRoles", "description": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.\n", "markdownDescription": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.", "x-intellij-html-description": "\u003cp\u003eThe list of Talos API roles which can be granted for access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eEmpty list means that no roles can be granted, so access is blocked.\u003c/p\u003e\n" }, "allowedKubernetesNamespaces": { "items": { "type": "string" }, "type": "array", "title": "allowedKubernetesNamespaces", "description": "The list of Kubernetes namespaces Talos API access is available from.\n", "markdownDescription": "The list of Kubernetes namespaces Talos API access is available from.", "x-intellij-html-description": "\u003cp\u003eThe list of Kubernetes namespaces Talos API access is available from.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LinuxIDMapping": { "properties": { "containerID": { "type": "integer", "title": "containerID", "description": "ContainerID is the starting UID/GID in the container.\n", "markdownDescription": "ContainerID is the starting UID/GID in the container.", "x-intellij-html-description": "\u003cp\u003eContainerID is the starting UID/GID in the container.\u003c/p\u003e\n" }, "hostID": { "type": "integer", "title": "hostID", "description": "HostID is the starting UID/GID on the host to be mapped to ‘ContainerID’.\n", "markdownDescription": "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'.", "x-intellij-html-description": "\u003cp\u003eHostID is the starting UID/GID on the host to be mapped to \u0026lsquo;ContainerID\u0026rsquo;.\u003c/p\u003e\n" }, "size": { "type": "integer", "title": "size", "description": "Size is the number of IDs to be mapped.\n", "markdownDescription": "Size is the number of IDs to be mapped.", "x-intellij-html-description": "\u003cp\u003eSize is the number of IDs to be mapped.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LoggingConfig": { "properties": { "destinations": { "items": { "$ref": "#/$defs/LoggingDestination" }, "type": "array", "title": "destinations", "description": "Logging destination.\n", "markdownDescription": "Logging destination.", "x-intellij-html-description": "\u003cp\u003eLogging destination.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LoggingDestination": { "properties": { "endpoint": { "$ref": "#/$defs/Endpoint", "title": "endpoint", "description": "Where to send logs. Supported protocols are “tcp” and “udp”.\n", "markdownDescription": "Where to send logs. Supported protocols are \"tcp\" and \"udp\".", "x-intellij-html-description": "\u003cp\u003eWhere to send logs. Supported protocols are \u0026ldquo;tcp\u0026rdquo; and \u0026ldquo;udp\u0026rdquo;.\u003c/p\u003e\n" }, "format": { "enum": [ "json_lines" ], "title": "format", "description": "Logs format.\n", "markdownDescription": "Logs format.", "x-intellij-html-description": "\u003cp\u003eLogs format.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineConfig": { "properties": { "type": { "enum": [ "controlplane", "worker" ], "title": "type", "description": "Defines the role of the machine within the cluster.\n\nControl Plane\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\nWorker\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as “join”; that value is still supported but deprecated.\n", "markdownDescription": "Defines the role of the machine within the cluster.\n\n**Control Plane**\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\n**Worker**\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as \"join\"; that value is still supported but deprecated.", "x-intellij-html-description": "\u003cp\u003eDefines the role of the machine within the cluster.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eControl Plane\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eWorker\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\u003c/p\u003e\n\n\u003cp\u003eThis node type was previously known as \u0026ldquo;join\u0026rdquo;; that value is still supported but deprecated.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The token is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its’ identity.\n", "markdownDescription": "The `token` is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003etoken\u003c/code\u003e is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its\u0026rsquo; identity.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the machine’s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate’s SANs.\n", "markdownDescription": "Extra certificate subject alternative names for the machine's certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate's SANs.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the machine\u0026rsquo;s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate\u0026rsquo;s SANs.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/MachineControlPlaneConfig", "title": "controlPlane", "description": "Provides machine specific control plane configuration options.\n", "markdownDescription": "Provides machine specific control plane configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific control plane configuration options.\u003c/p\u003e\n" }, "kubelet": { "$ref": "#/$defs/KubeletConfig", "title": "kubelet", "description": "Used to provide additional options to the kubelet.\n", "markdownDescription": "Used to provide additional options to the kubelet.", "x-intellij-html-description": "\u003cp\u003eUsed to provide additional options to the kubelet.\u003c/p\u003e\n" }, "pods": { "items": { "type": "object" }, "type": "array", "title": "pods", "description": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn’t validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.\n", "markdownDescription": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn't validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.", "x-intellij-html-description": "\u003cp\u003eUsed to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\u003c/p\u003e\n\n\u003cp\u003eStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn\u0026rsquo;t validate the pod definition.\nUpdates to this field can be applied without a reboot.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\" target=\"_blank\"\u003ehttps://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\u003c/a\u003e.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/NetworkConfig", "title": "network", "description": "Provides machine specific network configuration options.\n", "markdownDescription": "Provides machine specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific network configuration options.\u003c/p\u003e\n" }, "disks": { "items": { "$ref": "#/$defs/MachineDisk" }, "type": "array", "title": "disks", "description": "Used to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of /var, mounts are only valid if they are under /var.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf size: is omitted, the partition is sized to occupy the full disk.\n", "markdownDescription": "Used to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of `/var`, mounts are only valid if they are under `/var`.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf `size:` is omitted, the partition is sized to occupy the full disk.", "x-intellij-html-description": "\u003cp\u003eUsed to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of \u003ccode\u003e/var\u003c/code\u003e, mounts are only valid if they are under \u003ccode\u003e/var\u003c/code\u003e.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf \u003ccode\u003esize:\u003c/code\u003e is omitted, the partition is sized to occupy the full disk.\u003c/p\u003e\n" }, "install": { "$ref": "#/$defs/InstallConfig", "title": "install", "description": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\n", "markdownDescription": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.", "x-intellij-html-description": "\u003cp\u003eUsed to provide instructions for installations.\u003c/p\u003e\n\n\u003cp\u003eNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\u003c/p\u003e\n" }, "files": { "items": { "$ref": "#/$defs/MachineFile" }, "type": "array", "title": "files", "description": "Allows the addition of user specified files.\nThe value of op can be create, overwrite, or append.\nIn the case of create, path must not exist.\nIn the case of overwrite, and append, path must be a valid file.\nIf an op value of append is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\n", "markdownDescription": "Allows the addition of user specified files.\nThe value of `op` can be `create`, `overwrite`, or `append`.\nIn the case of `create`, `path` must not exist.\nIn the case of `overwrite`, and `append`, `path` must be a valid file.\nIf an `op` value of `append` is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.", "x-intellij-html-description": "\u003cp\u003eAllows the addition of user specified files.\nThe value of \u003ccode\u003eop\u003c/code\u003e can be \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003eoverwrite\u003c/code\u003e, or \u003ccode\u003eappend\u003c/code\u003e.\nIn the case of \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must not exist.\nIn the case of \u003ccode\u003eoverwrite\u003c/code\u003e, and \u003ccode\u003eappend\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must be a valid file.\nIf an \u003ccode\u003eop\u003c/code\u003e value of \u003ccode\u003eappend\u003c/code\u003e is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\n", "markdownDescription": "The `env` field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\u003c/p\u003e\n" }, "time": { "$ref": "#/$defs/TimeConfig", "title": "time", "description": "Used to configure the machine’s time settings.\n", "markdownDescription": "Used to configure the machine's time settings.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s time settings.\u003c/p\u003e\n" }, "sysctls": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysctls", "description": "Used to configure the machine’s sysctls.\n", "markdownDescription": "Used to configure the machine's sysctls.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysctls.\u003c/p\u003e\n" }, "sysfs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysfs", "description": "Used to configure the machine’s sysfs.\n", "markdownDescription": "Used to configure the machine's sysfs.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysfs.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/RegistriesConfig", "title": "registries", "description": "Used to configure the machine’s container image registry mirrors.\n\nAutomatically generates matching CRI configuration for registry mirrors.\n\nThe mirrors section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\n\nThe config section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in .docker/config.json.\n\nSee also matching configuration for CRI containerd plugin.\n", "markdownDescription": "Used to configure the machine's container image registry mirrors.\n\nAutomatically generates matching CRI configuration for registry mirrors.\n\nThe `mirrors` section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\n\nThe `config` section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).\n\nSee also matching configuration for [CRI containerd plugin](https://github.com/containerd/cri/blob/master/docs/registry.md).", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s container image registry mirrors.\u003c/p\u003e\n\n\u003cp\u003eAutomatically generates matching CRI configuration for registry mirrors.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003emirrors\u003c/code\u003e section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003econfig\u003c/code\u003e section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eSee also matching configuration for \u003ca href=\"https://github.com/containerd/cri/blob/master/docs/registry.md\" target=\"_blank\"\u003eCRI containerd plugin\u003c/a\u003e.\u003c/p\u003e\n" }, "systemDiskEncryption": { "$ref": "#/$defs/SystemDiskEncryptionConfig", "title": "systemDiskEncryption", "description": "Machine system disk encryption configuration.\nDefines each system partition encryption parameters.\n", "markdownDescription": "Machine system disk encryption configuration.\nDefines each system partition encryption parameters.", "x-intellij-html-description": "\u003cp\u003eMachine system disk encryption configuration.\nDefines each system partition encryption parameters.\u003c/p\u003e\n" }, "features": { "$ref": "#/$defs/FeaturesConfig", "title": "features", "description": "Features describe individual Talos features that can be switched on or off.\n", "markdownDescription": "Features describe individual Talos features that can be switched on or off.", "x-intellij-html-description": "\u003cp\u003eFeatures describe individual Talos features that can be switched on or off.\u003c/p\u003e\n" }, "udev": { "$ref": "#/$defs/UdevConfig", "title": "udev", "description": "Configures the udev system.\n", "markdownDescription": "Configures the udev system.", "x-intellij-html-description": "\u003cp\u003eConfigures the udev system.\u003c/p\u003e\n" }, "logging": { "$ref": "#/$defs/LoggingConfig", "title": "logging", "description": "Configures the logging system.\n", "markdownDescription": "Configures the logging system.", "x-intellij-html-description": "\u003cp\u003eConfigures the logging system.\u003c/p\u003e\n" }, "kernel": { "$ref": "#/$defs/KernelConfig", "title": "kernel", "description": "Configures the kernel.\n", "markdownDescription": "Configures the kernel.", "x-intellij-html-description": "\u003cp\u003eConfigures the kernel.\u003c/p\u003e\n" }, "seccompProfiles": { "items": { "$ref": "#/$defs/MachineSeccompProfile" }, "type": "array", "title": "seccompProfiles", "description": "Configures the seccomp profiles for the machine.\n", "markdownDescription": "Configures the seccomp profiles for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the seccomp profiles for the machine.\u003c/p\u003e\n" }, "nodeLabels": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeLabels", "description": "Configures the node labels for the machine.\n", "markdownDescription": "Configures the node labels for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the node labels for the machine.\u003c/p\u003e\n" }, "nodeTaints": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeTaints", "description": "Configures the node taints for the machine. Effect is optional.\n", "markdownDescription": "Configures the node taints for the machine. Effect is optional.", "x-intellij-html-description": "\u003cp\u003eConfigures the node taints for the machine. Effect is optional.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineControlPlaneConfig": { "properties": { "controllerManager": { "$ref": "#/$defs/MachineControllerManagerConfig", "title": "controllerManager", "description": "Controller manager machine specific configuration options.\n", "markdownDescription": "Controller manager machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager machine specific configuration options.\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/MachineSchedulerConfig", "title": "scheduler", "description": "Scheduler machine specific configuration options.\n", "markdownDescription": "Scheduler machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler machine specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineControllerManagerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-controller-manager on the node.\n", "markdownDescription": "Disable kube-controller-manager on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-controller-manager on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineDisk": { "properties": { "device": { "type": "string", "title": "device", "description": "The name of the disk to use.\n", "markdownDescription": "The name of the disk to use.", "x-intellij-html-description": "\u003cp\u003eThe name of the disk to use.\u003c/p\u003e\n" }, "partitions": { "items": { "$ref": "#/$defs/DiskPartition" }, "type": "array", "title": "partitions", "description": "A list of partitions to create on the disk.\n", "markdownDescription": "A list of partitions to create on the disk.", "x-intellij-html-description": "\u003cp\u003eA list of partitions to create on the disk.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The contents of the file.\n", "markdownDescription": "The contents of the file.", "x-intellij-html-description": "\u003cp\u003eThe contents of the file.\u003c/p\u003e\n" }, "permissions": { "type": "integer", "title": "permissions", "description": "The file’s permissions in octal.\n", "markdownDescription": "The file's permissions in octal.", "x-intellij-html-description": "\u003cp\u003eThe file\u0026rsquo;s permissions in octal.\u003c/p\u003e\n" }, "path": { "type": "string", "title": "path", "description": "The path of the file.\n", "markdownDescription": "The path of the file.", "x-intellij-html-description": "\u003cp\u003eThe path of the file.\u003c/p\u003e\n" }, "op": { "enum": [ "create", "append", "overwrite" ], "title": "op", "description": "The operation to use\n", "markdownDescription": "The operation to use", "x-intellij-html-description": "\u003cp\u003eThe operation to use\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineSchedulerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-scheduler on the node.\n", "markdownDescription": "Disable kube-scheduler on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-scheduler on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineSeccompProfile": { "properties": { "name": { "type": "string", "title": "name", "description": "The name field is used to provide the file name of the seccomp profile.\n", "markdownDescription": "The `name` field is used to provide the file name of the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003ename\u003c/code\u003e field is used to provide the file name of the seccomp profile.\u003c/p\u003e\n" }, "value": { "type": "object", "title": "value", "description": "The value field is used to provide the seccomp profile.\n", "markdownDescription": "The `value` field is used to provide the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalue\u003c/code\u003e field is used to provide the seccomp profile.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkConfig": { "properties": { "hostname": { "type": "string", "title": "hostname", "description": "Used to statically set the hostname for the machine.\n", "markdownDescription": "Used to statically set the hostname for the machine.", "x-intellij-html-description": "\u003cp\u003eUsed to statically set the hostname for the machine.\u003c/p\u003e\n" }, "interfaces": { "items": { "$ref": "#/$defs/Device" }, "type": "array", "title": "interfaces", "description": "interfaces is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.\n", "markdownDescription": "`interfaces` is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.", "x-intellij-html-description": "\u003cp\u003e\u003ccode\u003einterfaces\u003c/code\u003e is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.\u003c/p\u003e\n" }, "nameservers": { "items": { "type": "string" }, "type": "array", "title": "nameservers", "description": "Used to statically set the nameservers for the machine.\nDefaults to 1.1.1.1 and 8.8.8.8\n", "markdownDescription": "Used to statically set the nameservers for the machine.\nDefaults to `1.1.1.1` and `8.8.8.8`", "x-intellij-html-description": "\u003cp\u003eUsed to statically set the nameservers for the machine.\nDefaults to \u003ccode\u003e1.1.1.1\u003c/code\u003e and \u003ccode\u003e8.8.8.8\u003c/code\u003e\u003c/p\u003e\n" }, "extraHostEntries": { "items": { "$ref": "#/$defs/ExtraHost" }, "type": "array", "title": "extraHostEntries", "description": "Allows for extra entries to be added to the /etc/hosts file\n", "markdownDescription": "Allows for extra entries to be added to the `/etc/hosts` file", "x-intellij-html-description": "\u003cp\u003eAllows for extra entries to be added to the \u003ccode\u003e/etc/hosts\u003c/code\u003e file\u003c/p\u003e\n" }, "kubespan": { "$ref": "#/$defs/NetworkKubeSpan", "title": "kubespan", "description": "Configures KubeSpan feature.\n", "markdownDescription": "Configures KubeSpan feature.", "x-intellij-html-description": "\u003cp\u003eConfigures KubeSpan feature.\u003c/p\u003e\n" }, "disableSearchDomain": { "type": "boolean", "title": "disableSearchDomain", "description": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to false.\n", "markdownDescription": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to `false`.", "x-intellij-html-description": "\u003cp\u003eDisable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkDeviceSelector": { "properties": { "busPath": { "type": "string", "title": "busPath", "description": "PCI, USB bus prefix, supports matching by wildcard.\n", "markdownDescription": "PCI, USB bus prefix, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003ePCI, USB bus prefix, supports matching by wildcard.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "title": "hardwareAddr", "description": "Device hardware address, supports matching by wildcard.\n", "markdownDescription": "Device hardware address, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003eDevice hardware address, supports matching by wildcard.\u003c/p\u003e\n" }, "pciID": { "type": "string", "title": "pciID", "description": "PCI ID (vendor ID, product ID), supports matching by wildcard.\n", "markdownDescription": "PCI ID (vendor ID, product ID), supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003ePCI ID (vendor ID, product ID), supports matching by wildcard.\u003c/p\u003e\n" }, "driver": { "type": "string", "title": "driver", "description": "Kernel driver, supports matching by wildcard.\n", "markdownDescription": "Kernel driver, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003eKernel driver, supports matching by wildcard.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkKubeSpan": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.\n", "markdownDescription": "Enable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.", "x-intellij-html-description": "\u003cp\u003eEnable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.\u003c/p\u003e\n" }, "advertiseKubernetesNetworks": { "type": "boolean", "title": "advertiseKubernetesNetworks", "description": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.\n", "markdownDescription": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.", "x-intellij-html-description": "\u003cp\u003eControl whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.\u003c/p\u003e\n" }, "allowDownPeerBypass": { "type": "boolean", "title": "allowDownPeerBypass", "description": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can’t be established.\n", "markdownDescription": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can't be established.", "x-intellij-html-description": "\u003cp\u003eSkip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can\u0026rsquo;t be established.\u003c/p\u003e\n" }, "harvestExtraEndpoints": { "type": "boolean", "title": "harvestExtraEndpoints", "description": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u0026gt;50) in the KubeSpan network it can cause performance issues.\n", "markdownDescription": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u003e50) in the KubeSpan network it can cause performance issues.", "x-intellij-html-description": "\u003cp\u003eKubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u0026gt;50) in the KubeSpan network it can cause performance issues.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "KubeSpan link MTU size.\nDefault value is 1420.\n", "markdownDescription": "KubeSpan link MTU size.\nDefault value is 1420.", "x-intellij-html-description": "\u003cp\u003eKubeSpan link MTU size.\nDefault value is 1420.\u003c/p\u003e\n" }, "filters": { "$ref": "#/$defs/KubeSpanFilters", "title": "filters", "description": "KubeSpan advanced filtering of network addresses .\n\nSettings in this section are optional, and settings apply only to the node.\n", "markdownDescription": "KubeSpan advanced filtering of network addresses .\n\nSettings in this section are optional, and settings apply only to the node.", "x-intellij-html-description": "\u003cp\u003eKubeSpan advanced filtering of network addresses .\u003c/p\u003e\n\n\u003cp\u003eSettings in this section are optional, and settings apply only to the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ProxyConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-proxy deployment on cluster bootstrap.\n", "markdownDescription": "Disable kube-proxy deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable kube-proxy deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The container image used in the kube-proxy manifest.\n", "markdownDescription": "The container image used in the kube-proxy manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the kube-proxy manifest.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "proxy mode of kube-proxy.\nThe default is ‘iptables’.\n", "markdownDescription": "proxy mode of kube-proxy.\nThe default is 'iptables'.", "x-intellij-html-description": "\u003cp\u003eproxy mode of kube-proxy.\nThe default is \u0026lsquo;iptables\u0026rsquo;.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to kube-proxy.\n", "markdownDescription": "Extra arguments to supply to kube-proxy.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to kube-proxy.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistriesConfig": { "properties": { "mirrors": { "patternProperties": { ".*": { "$ref": "#/$defs/RegistryMirrorConfig" } }, "type": "object", "title": "mirrors", "description": "Specifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\n\nFor example, when pulling an image with the reference example.com:123/image:v1,\nthe example.com:123 key will be used to lookup the mirror configuration.\n\nOptionally the * key can be used to configure a fallback mirror.\n\nRegistry name is the first segment of image identifier, with ‘docker.io’\nbeing default one.\n", "markdownDescription": "Specifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\n\nFor example, when pulling an image with the reference `example.com:123/image:v1`,\nthe `example.com:123` key will be used to lookup the mirror configuration.\n\nOptionally the `*` key can be used to configure a fallback mirror.\n\nRegistry name is the first segment of image identifier, with 'docker.io'\nbeing default one.", "x-intellij-html-description": "\u003cp\u003eSpecifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\u003c/p\u003e\n\n\u003cp\u003eFor example, when pulling an image with the reference \u003ccode\u003eexample.com:123/image:v1\u003c/code\u003e,\nthe \u003ccode\u003eexample.com:123\u003c/code\u003e key will be used to lookup the mirror configuration.\u003c/p\u003e\n\n\u003cp\u003eOptionally the \u003ccode\u003e*\u003c/code\u003e key can be used to configure a fallback mirror.\u003c/p\u003e\n\n\u003cp\u003eRegistry name is the first segment of image identifier, with \u0026lsquo;docker.io\u0026rsquo;\nbeing default one.\u003c/p\u003e\n" }, "config": { "patternProperties": { ".*": { "$ref": "#/$defs/RegistryConfig" } }, "type": "object", "title": "config", "description": "Specifies TLS \u0026amp; auth configuration for HTTPS image registries.\nMutual TLS can be enabled with ‘clientIdentity’ option.\n\nThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key * can’t be used for TLS configuration.\n\nTLS configuration can be skipped if registry has trusted\nserver certificate.\n", "markdownDescription": "Specifies TLS \u0026 auth configuration for HTTPS image registries.\nMutual TLS can be enabled with 'clientIdentity' option.\n\nThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key `*` can't be used for TLS configuration.\n\nTLS configuration can be skipped if registry has trusted\nserver certificate.", "x-intellij-html-description": "\u003cp\u003eSpecifies TLS \u0026amp; auth configuration for HTTPS image registries.\nMutual TLS can be enabled with \u0026lsquo;clientIdentity\u0026rsquo; option.\u003c/p\u003e\n\n\u003cp\u003eThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key \u003ccode\u003e*\u003c/code\u003e can\u0026rsquo;t be used for TLS configuration.\u003c/p\u003e\n\n\u003cp\u003eTLS configuration can be skipped if registry has trusted\nserver certificate.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryAuthConfig": { "properties": { "username": { "type": "string", "title": "username", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "password": { "type": "string", "title": "password", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "auth": { "type": "string", "title": "auth", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "identityToken": { "type": "string", "title": "identityToken", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryConfig": { "properties": { "tls": { "$ref": "#/$defs/RegistryTLSConfig", "title": "tls", "description": "The TLS configuration for the registry.\n", "markdownDescription": "The TLS configuration for the registry.", "x-intellij-html-description": "\u003cp\u003eThe TLS configuration for the registry.\u003c/p\u003e\n" }, "auth": { "$ref": "#/$defs/RegistryAuthConfig", "title": "auth", "description": "The auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.\n", "markdownDescription": "The auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.", "x-intellij-html-description": "\u003cp\u003eThe auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryKubernetesConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable Kubernetes discovery registry.\n", "markdownDescription": "Disable Kubernetes discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable Kubernetes discovery registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryMirrorConfig": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "List of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to /v2).\n", "markdownDescription": "List of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to `/v2`).", "x-intellij-html-description": "\u003cp\u003eList of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to \u003ccode\u003e/v2\u003c/code\u003e).\u003c/p\u003e\n" }, "overridePath": { "type": "boolean", "title": "overridePath", "description": "Use the exact path specified for the endpoint (don’t append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.\n", "markdownDescription": "Use the exact path specified for the endpoint (don't append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.", "x-intellij-html-description": "\u003cp\u003eUse the exact path specified for the endpoint (don\u0026rsquo;t append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryServiceConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable external service discovery registry.\n", "markdownDescription": "Disable external service discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable external service discovery registry.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "External service endpoint.\n", "markdownDescription": "External service endpoint.", "x-intellij-html-description": "\u003cp\u003eExternal service endpoint.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryTLSConfig": { "properties": { "clientIdentity": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "clientIdentity", "description": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.\n", "markdownDescription": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.", "x-intellij-html-description": "\u003cp\u003eEnable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.\u003c/p\u003e\n" }, "ca": { "type": "string", "title": "ca", "description": "CA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.\n", "markdownDescription": "CA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.", "x-intellij-html-description": "\u003cp\u003eCA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.\u003c/p\u003e\n" }, "insecureSkipVerify": { "type": "boolean", "title": "insecureSkipVerify", "description": "Skip TLS server certificate verification (not recommended).\n", "markdownDescription": "Skip TLS server certificate verification (not recommended).", "x-intellij-html-description": "\u003cp\u003eSkip TLS server certificate verification (not recommended).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ResourcesConfig": { "properties": { "requests": { "$ref": "#/$defs/Unstructured", "title": "requests", "description": "Requests configures the reserved cpu/memory resources.\n", "markdownDescription": "Requests configures the reserved cpu/memory resources.", "x-intellij-html-description": "\u003cp\u003eRequests configures the reserved cpu/memory resources.\u003c/p\u003e\n" }, "limits": { "$ref": "#/$defs/Unstructured", "title": "limits", "description": "Limits configures the maximum cpu/memory resources a container can use.\n", "markdownDescription": "Limits configures the maximum cpu/memory resources a container can use.", "x-intellij-html-description": "\u003cp\u003eLimits configures the maximum cpu/memory resources a container can use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Route": { "properties": { "network": { "type": "string", "title": "network", "description": "The route’s network (destination).\n", "markdownDescription": "The route's network (destination).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s network (destination).\u003c/p\u003e\n" }, "gateway": { "type": "string", "title": "gateway", "description": "The route’s gateway (if empty, creates link scope route).\n", "markdownDescription": "The route's gateway (if empty, creates link scope route).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s gateway (if empty, creates link scope route).\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "The route’s source address (optional).\n", "markdownDescription": "The route's source address (optional).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s source address (optional).\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The optional MTU for the route.\n", "markdownDescription": "The optional MTU for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional MTU for the route.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "STP": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Whether Spanning Tree Protocol (STP) is enabled.\n", "markdownDescription": "Whether Spanning Tree Protocol (STP) is enabled.", "x-intellij-html-description": "\u003cp\u003eWhether Spanning Tree Protocol (STP) is enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "SchedulerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the scheduler manifest.\n", "markdownDescription": "The container image used in the scheduler manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the scheduler manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the scheduler.\n", "markdownDescription": "Extra arguments to supply to the scheduler.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the scheduler.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the scheduler static pod.\n", "markdownDescription": "Extra volumes to mount to the scheduler static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the scheduler static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the scheduler resources.\n", "markdownDescription": "Configure the scheduler resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the scheduler resources.\u003c/p\u003e\n" }, "config": { "type": "object", "title": "config", "description": "Specify custom kube-scheduler configuration.\n", "markdownDescription": "Specify custom kube-scheduler configuration.", "x-intellij-html-description": "\u003cp\u003eSpecify custom kube-scheduler configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "SystemDiskEncryptionConfig": { "properties": { "state": { "$ref": "#/$defs/EncryptionConfig", "title": "state", "description": "State partition encryption.\n", "markdownDescription": "State partition encryption.", "x-intellij-html-description": "\u003cp\u003eState partition encryption.\u003c/p\u003e\n" }, "ephemeral": { "$ref": "#/$defs/EncryptionConfig", "title": "ephemeral", "description": "Ephemeral partition encryption.\n", "markdownDescription": "Ephemeral partition encryption.", "x-intellij-html-description": "\u003cp\u003eEphemeral partition encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "TimeConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Indicates if the time service is disabled for the machine.\nDefaults to false.\n", "markdownDescription": "Indicates if the time service is disabled for the machine.\nDefaults to `false`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the time service is disabled for the machine.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n" }, "servers": { "items": { "type": "string" }, "type": "array", "title": "servers", "description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to pool.ntp.org\n", "markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `pool.ntp.org`", "x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003epool.ntp.org\u003c/code\u003e\u003c/p\u003e\n" }, "bootTimeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "bootTimeout", "description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n", "markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)", "x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "UdevConfig": { "properties": { "rules": { "items": { "type": "string" }, "type": "array", "title": "rules", "description": "List of udev rules to apply to the udev system\n", "markdownDescription": "List of udev rules to apply to the udev system", "x-intellij-html-description": "\u003cp\u003eList of udev rules to apply to the udev system\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VIPEquinixMetalConfig": { "properties": { "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Equinix Metal API Token.\n", "markdownDescription": "Specifies the Equinix Metal API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Equinix Metal API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VIPHCloudConfig": { "properties": { "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Hetzner Cloud API Token.\n", "markdownDescription": "Specifies the Hetzner Cloud API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Vlan": { "properties": { "addresses": { "items": { "type": "string" }, "type": "array", "title": "addresses", "description": "The addresses in CIDR notation or as plain IPs to use.\n", "markdownDescription": "The addresses in CIDR notation or as plain IPs to use.", "x-intellij-html-description": "\u003cp\u003eThe addresses in CIDR notation or as plain IPs to use.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/Route" }, "type": "array", "title": "routes", "description": "A list of routes associated with the VLAN.\n", "markdownDescription": "A list of routes associated with the VLAN.", "x-intellij-html-description": "\u003cp\u003eA list of routes associated with the VLAN.\u003c/p\u003e\n" }, "dhcp": { "type": "boolean", "title": "dhcp", "description": "Indicates if DHCP should be used.\n", "markdownDescription": "Indicates if DHCP should be used.", "x-intellij-html-description": "\u003cp\u003eIndicates if DHCP should be used.\u003c/p\u003e\n" }, "vlanId": { "type": "integer", "title": "vlanId", "description": "The VLAN’s ID.\n", "markdownDescription": "The VLAN's ID.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s ID.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The VLAN’s MTU.\n", "markdownDescription": "The VLAN's MTU.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s MTU.\u003c/p\u003e\n" }, "vip": { "$ref": "#/$defs/DeviceVIPConfig", "title": "vip", "description": "The VLAN’s virtual IP address configuration.\n", "markdownDescription": "The VLAN's virtual IP address configuration.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s virtual IP address configuration.\u003c/p\u003e\n" }, "dhcpOptions": { "$ref": "#/$defs/DHCPOptions", "title": "dhcpOptions", "description": "DHCP specific options.\ndhcp must be set to true for these to take effect.\n", "markdownDescription": "DHCP specific options.\n`dhcp` *must* be set to true for these to take effect.", "x-intellij-html-description": "\u003cp\u003eDHCP specific options.\n\u003ccode\u003edhcp\u003c/code\u003e \u003cem\u003emust\u003c/em\u003e be set to true for these to take effect.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VolumeMountConfig": { "properties": { "hostPath": { "type": "string", "title": "hostPath", "description": "Path on the host.\n", "markdownDescription": "Path on the host.", "x-intellij-html-description": "\u003cp\u003ePath on the host.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "Path in the container.\n", "markdownDescription": "Path in the container.", "x-intellij-html-description": "\u003cp\u003ePath in the container.\u003c/p\u003e\n" }, "readonly": { "type": "boolean", "title": "readonly", "description": "Mount the volume read only.\n", "markdownDescription": "Mount the volume read only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read only.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" } } } ================================================ FILE: pkg/machinery/config/types/block/block.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package block provides block device and volume configuration documents. package block //go:generate go tool github.com/siderolabs/talos/tools/docgen -output block_doc.go block.go encryption.go existing_volume_config.go external_volume_config.go raw_volume_config.go swap_volume_config.go user_volume_config.go volume_config.go zswap_config.go //go:generate go tool github.com/siderolabs/deep-copy -type ExistingVolumeConfigV1Alpha1 -type RawVolumeConfigV1Alpha1 -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type ExternalVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -type ZswapConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/block/block_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package block import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (EncryptionSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionSpec", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionSpec represents volume encryption settings." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionSpec represents volume encryption settings.", AppearsIn: []encoder.Appearance{ { TypeName: "RawVolumeConfigV1Alpha1", FieldName: "encryption", }, { TypeName: "SwapVolumeConfigV1Alpha1", FieldName: "encryption", }, { TypeName: "UserVolumeConfigV1Alpha1", FieldName: "encryption", }, { TypeName: "VolumeConfigV1Alpha1", FieldName: "encryption", }, }, Fields: []encoder.Doc{ { Name: "provider", Type: "EncryptionProviderType", Note: "", Description: "Encryption provider to use for the encryption.", Comments: [3]string{"" /* encoder.HeadComment */, "Encryption provider to use for the encryption." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "luks2", }, }, { Name: "keys", Type: "[]EncryptionKey", Note: "", Description: "Defines the encryption keys generation and storage method.", Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption keys generation and storage method." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "cipher", Type: "string", Note: "", Description: "Cipher to use for the encryption. Depends on the encryption provider.", Comments: [3]string{"" /* encoder.HeadComment */, "Cipher to use for the encryption. Depends on the encryption provider." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "aes-xts-plain64", "xchacha12,aes-adiantum-plain64", "xchacha20,aes-adiantum-plain64", }, }, { Name: "keySize", Type: "uint", Note: "", Description: "Defines the encryption key length.", Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption key length." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "blockSize", Type: "uint64", Note: "", Description: "Defines the encryption sector size.", Comments: [3]string{"" /* encoder.HeadComment */, "Defines the encryption sector size." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "options", Type: "[]string", Note: "", Description: "Additional --perf parameters for the LUKS2 encryption.", Comments: [3]string{"" /* encoder.HeadComment */, "Additional --perf parameters for the LUKS2 encryption." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "no_read_workqueue", "no_write_workqueue", "same_cpu_crypt", }, }, }, } doc.AddExample("", exampleEncryptionSpec()) doc.Fields[2].AddExample("", "aes-xts-plain64") doc.Fields[4].AddExample("", 4096) doc.Fields[5].AddExample("", []string{"no_read_workqueue", "no_write_workqueue"}) return doc } func (EncryptionKey) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKey", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKey represents configuration for disk encryption key." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKey represents configuration for disk encryption key.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionSpec", FieldName: "keys", }, }, Fields: []encoder.Doc{ { Name: "slot", Type: "int", Note: "", Description: "Key slot number for LUKS2 encryption.", Comments: [3]string{"" /* encoder.HeadComment */, "Key slot number for LUKS2 encryption." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "static", Type: "EncryptionKeyStatic", Note: "", Description: "Key which value is stored in the configuration file.", Comments: [3]string{"" /* encoder.HeadComment */, "Key which value is stored in the configuration file." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nodeID", Type: "EncryptionKeyNodeID", Note: "", Description: "Deterministically generated key from the node UUID and PartitionLabel.", Comments: [3]string{"" /* encoder.HeadComment */, "Deterministically generated key from the node UUID and PartitionLabel." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "kms", Type: "EncryptionKeyKMS", Note: "", Description: "KMS managed encryption key.", Comments: [3]string{"" /* encoder.HeadComment */, "KMS managed encryption key." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tpm", Type: "EncryptionKeyTPM", Note: "", Description: "Enable TPM based disk encryption.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable TPM based disk encryption." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "lockToState", Type: "bool", Note: "", Description: "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.", Comments: [3]string{"" /* encoder.HeadComment */, "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (EncryptionKeyStatic) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKeyStatic", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyStatic represents throw away key type." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKeyStatic represents throw away key type.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionKey", FieldName: "static", }, }, Fields: []encoder.Doc{ { Name: "passphrase", Type: "string", Note: "", Description: "Defines the static passphrase value.", Comments: [3]string{"" /* encoder.HeadComment */, "Defines the static passphrase value." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (EncryptionKeyKMS) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKeyKMS", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionKey", FieldName: "kms", }, }, Fields: []encoder.Doc{ { Name: "endpoint", Type: "string", Note: "", Description: "KMS endpoint to Seal/Unseal the key.", Comments: [3]string{"" /* encoder.HeadComment */, "KMS endpoint to Seal/Unseal the key." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleKMSKey()) return doc } func (EncryptionKeyTPM) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKeyTPM", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionKey", FieldName: "tpm", }, }, Fields: []encoder.Doc{ { Name: "options", Type: "EncryptionKeyTPMOptions", Note: "", Description: "TPM options for key protection.", Comments: [3]string{"" /* encoder.HeadComment */, "TPM options for key protection." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "checkSecurebootStatusOnEnroll", Type: "bool", Note: "", Description: "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.", Comments: [3]string{"" /* encoder.HeadComment */, "Check that Secureboot is enabled in the EFI firmware." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (EncryptionKeyTPMOptions) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKeyTPMOptions", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyTPMOptions represents the options for TPM-based key protection." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKeyTPMOptions represents the options for TPM-based key protection.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionKeyTPM", FieldName: "options", }, }, Fields: []encoder.Doc{ { Name: "pcrs", Type: "[]int", Note: "", Description: "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.", Comments: [3]string{"" /* encoder.HeadComment */, "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (EncryptionKeyNodeID) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EncryptionKeyNodeID", Comments: [3]string{"" /* encoder.HeadComment */, "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel.", AppearsIn: []encoder.Appearance{ { TypeName: "EncryptionKey", FieldName: "nodeID", }, }, Fields: []encoder.Doc{}, } return doc } func (ExistingVolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExistingVolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ExistingVolumeConfig is an existing volume configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExistingVolumeConfig is an existing volume configuration document.\nExisting volumes allow to mount partitions (or whole disks) that were created\noutside of Talos. Volume will be mounted under `/var/mnt/`.\nThe existing volume config name should not conflict with user volume names.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "discovery", Type: "VolumeDiscoverySpec", Note: "", Description: "The discovery describes how to find a volume.", Comments: [3]string{"" /* encoder.HeadComment */, "The discovery describes how to find a volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mount", Type: "ExistingMountSpec", Note: "", Description: "The mount describes additional mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "The mount describes additional mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleExistingVolumeConfigV1Alpha1()) return doc } func (VolumeDiscoverySpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VolumeDiscoverySpec", Comments: [3]string{"" /* encoder.HeadComment */, "VolumeDiscoverySpec describes how the volume is discovered." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VolumeDiscoverySpec describes how the volume is discovered.", AppearsIn: []encoder.Appearance{ { TypeName: "ExistingVolumeConfigV1Alpha1", FieldName: "discovery", }, }, Fields: []encoder.Doc{ { Name: "volumeSelector", Type: "VolumeSelector", Note: "", Description: "The volume selector expression.", Comments: [3]string{"" /* encoder.HeadComment */, "The volume selector expression." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (VolumeSelector) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VolumeSelector", Comments: [3]string{"" /* encoder.HeadComment */, "VolumeSelector selects an existing volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VolumeSelector selects an existing volume.", AppearsIn: []encoder.Appearance{ { TypeName: "VolumeDiscoverySpec", FieldName: "volumeSelector", }, }, Fields: []encoder.Doc{ { Name: "match", Type: "Expression", Note: "", Description: "The Common Expression Language (CEL) expression to match the volume.", Comments: [3]string{"" /* encoder.HeadComment */, "The Common Expression Language (CEL) expression to match the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("match volumes with partition label MY-DATA", exampleVolumeSelector1()) doc.Fields[0].AddExample("match xfs volume on disk with serial 'SERIAL123'", exampleVolumeSelector2()) return doc } func (ExistingMountSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExistingMountSpec", Comments: [3]string{"" /* encoder.HeadComment */, "ExistingMountSpec describes how the volume is mounted." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExistingMountSpec describes how the volume is mounted.", AppearsIn: []encoder.Appearance{ { TypeName: "ExistingVolumeConfigV1Alpha1", FieldName: "mount", }, }, Fields: []encoder.Doc{ { Name: "readOnly", Type: "bool", Note: "", Description: "Mount the volume read-only.", Comments: [3]string{"" /* encoder.HeadComment */, "Mount the volume read-only." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "disableAccessTime", Type: "bool", Note: "", Description: "If true, disable file access time updates.", Comments: [3]string{"" /* encoder.HeadComment */, "If true, disable file access time updates." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "secure", Type: "bool", Note: "", Description: "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable secure mount options (nosuid, nodev)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (ExternalVolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExternalVolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ExternalVolumeConfig is an external disk mount configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExternalVolumeConfig is an external disk mount configuration document.\nExternal volumes allow to mount volumes that were created outside of Talos,\nover the network or API. Volume will be mounted under `/var/mnt/`.\nThe external volume config name should not conflict with user volume names.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the mount.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the mount." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "filesystemType", Type: "FilesystemType", Note: "", Description: "Filesystem type.", Comments: [3]string{"" /* encoder.HeadComment */, "Filesystem type." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "virtiofs", "nfs", }, }, { Name: "mount", Type: "ExternalMountSpec", Note: "", Description: "The mount describes additional mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "The mount describes additional mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleExternalVolumeConfigV1Alpha1Virtiofs()) return doc } func (ExternalMountSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExternalMountSpec", Comments: [3]string{"" /* encoder.HeadComment */, "ExternalMountSpec describes how the external volume is mounted." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExternalMountSpec describes how the external volume is mounted.", AppearsIn: []encoder.Appearance{ { TypeName: "ExternalVolumeConfigV1Alpha1", FieldName: "mount", }, }, Fields: []encoder.Doc{ { Name: "readOnly", Type: "bool", Note: "", Description: "Mount the volume read-only.", Comments: [3]string{"" /* encoder.HeadComment */, "Mount the volume read-only." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "disableAccessTime", Type: "bool", Note: "", Description: "If true, disable file access time updates.", Comments: [3]string{"" /* encoder.HeadComment */, "If true, disable file access time updates." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "secure", Type: "bool", Note: "", Description: "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable secure mount options (nosuid, nodev)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "virtiofs", Type: "VirtiofsMountSpec", Note: "", Description: "Virtiofs mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "Virtiofs mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (VirtiofsMountSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VirtiofsMountSpec", Comments: [3]string{"" /* encoder.HeadComment */, "VirtiofsMountSpec describes Virtiofs mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VirtiofsMountSpec describes Virtiofs mount options.", AppearsIn: []encoder.Appearance{ { TypeName: "ExternalMountSpec", FieldName: "virtiofs", }, }, Fields: []encoder.Doc{ { Name: "tag", Type: "string", Note: "", Description: "Selector tag for the Virtiofs mount.", Comments: [3]string{"" /* encoder.HeadComment */, "Selector tag for the Virtiofs mount." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (RawVolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RawVolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RawVolumeConfig is a raw volume configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RawVolumeConfig is a raw volume configuration document.\nRaw volumes allow to create partitions without formatting them.\n If you want to use local storage, user volumes is a better choice,\n raw volumes are intended to be used with CSI provisioners.\nThe partition label is automatically generated as `r-`.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "provisioning", Type: "ProvisioningSpec", Note: "", Description: "The provisioning describes how the volume is provisioned.", Comments: [3]string{"" /* encoder.HeadComment */, "The provisioning describes how the volume is provisioned." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "encryption", Type: "EncryptionSpec", Note: "", Description: "The encryption describes how the volume is encrypted.", Comments: [3]string{"" /* encoder.HeadComment */, "The encryption describes how the volume is encrypted." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRawVolumeConfigV1Alpha1()) return doc } func (SwapVolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "SwapVolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "SwapVolumeConfig is a disk swap volume configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "SwapVolumeConfig is a disk swap volume configuration document.\nSwap volume is automatically allocated as a partition on the specified disk\nand activated as swap, removing a swap volume deactivates swap.\nThe partition label is automatically generated as `s-`.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "provisioning", Type: "ProvisioningSpec", Note: "", Description: "The provisioning describes how the volume is provisioned.", Comments: [3]string{"" /* encoder.HeadComment */, "The provisioning describes how the volume is provisioned." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "encryption", Type: "EncryptionSpec", Note: "", Description: "The encryption describes how the volume is encrypted.", Comments: [3]string{"" /* encoder.HeadComment */, "The encryption describes how the volume is encrypted." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleSwapVolumeConfigV1Alpha1()) return doc } func (UserVolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "UserVolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "UserVolumeConfig is a user volume configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "UserVolumeConfig is a user volume configuration document.\nUser volume is automatically allocated as a partition on the specified disk\nand mounted under `/var/mnt/`.\nThe partition label is automatically generated as `u-`.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "volumeType", Type: "VolumeType", Note: "", Description: "Volume type.", Comments: [3]string{"" /* encoder.HeadComment */, "Volume type." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "directory", "disk", "partition", }, }, { Name: "provisioning", Type: "ProvisioningSpec", Note: "", Description: "The provisioning describes how the volume is provisioned.", Comments: [3]string{"" /* encoder.HeadComment */, "The provisioning describes how the volume is provisioned." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "filesystem", Type: "FilesystemSpec", Note: "", Description: "The filesystem describes how the volume is formatted.", Comments: [3]string{"" /* encoder.HeadComment */, "The filesystem describes how the volume is formatted." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "encryption", Type: "EncryptionSpec", Note: "", Description: "The encryption describes how the volume is encrypted.", Comments: [3]string{"" /* encoder.HeadComment */, "The encryption describes how the volume is encrypted." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mount", Type: "UserMountSpec", Note: "", Description: "The mount describes additional mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "The mount describes additional mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleUserVolumeConfigV1Alpha1Directory()) doc.AddExample("", exampleUserVolumeConfigV1Alpha1Disk()) doc.AddExample("", exampleUserVolumeConfigV1Alpha1Partition()) return doc } func (UserMountSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "UserMountSpec", Comments: [3]string{"" /* encoder.HeadComment */, "UserMountSpec describes how the volume is mounted." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "UserMountSpec describes how the volume is mounted.", AppearsIn: []encoder.Appearance{ { TypeName: "UserVolumeConfigV1Alpha1", FieldName: "mount", }, }, Fields: []encoder.Doc{ { Name: "disableAccessTime", Type: "bool", Note: "", Description: "If true, disable file access time updates.", Comments: [3]string{"" /* encoder.HeadComment */, "If true, disable file access time updates." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "secure", Type: "bool", Note: "", Description: "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable secure mount options (nosuid, nodev)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (FilesystemSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "FilesystemSpec", Comments: [3]string{"" /* encoder.HeadComment */, "FilesystemSpec configures the filesystem for the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "FilesystemSpec configures the filesystem for the volume.", AppearsIn: []encoder.Appearance{ { TypeName: "UserVolumeConfigV1Alpha1", FieldName: "filesystem", }, }, Fields: []encoder.Doc{ { Name: "type", Type: "FilesystemType", Note: "", Description: "Filesystem type. Default is `xfs`.", Comments: [3]string{"" /* encoder.HeadComment */, "Filesystem type. Default is `xfs`." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "ext4", "xfs", }, }, { Name: "projectQuotaSupport", Type: "bool", Note: "", Description: "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.", Comments: [3]string{"" /* encoder.HeadComment */, "Enables project quota support, valid only for 'xfs' filesystem." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (VolumeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VolumeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "VolumeConfig is a system volume configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VolumeConfig is a system volume configuration document.\nNote: at the moment, only `STATE`, `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the volume.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "provisioning", Type: "ProvisioningSpec", Note: "", Description: "The provisioning describes how the volume is provisioned.", Comments: [3]string{"" /* encoder.HeadComment */, "The provisioning describes how the volume is provisioned." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "encryption", Type: "EncryptionSpec", Note: "", Description: "The encryption describes how the volume is encrypted.", Comments: [3]string{"" /* encoder.HeadComment */, "The encryption describes how the volume is encrypted." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mount", Type: "MountSpec", Note: "", Description: "The mount describes additional mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "The mount describes additional mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleVolumeConfigEphemeralV1Alpha1()) return doc } func (MountSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MountSpec", Comments: [3]string{"" /* encoder.HeadComment */, "MountSpec describes how the volume is mounted." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MountSpec describes how the volume is mounted.", AppearsIn: []encoder.Appearance{ { TypeName: "VolumeConfigV1Alpha1", FieldName: "mount", }, }, Fields: []encoder.Doc{ { Name: "secure", Type: "bool", Note: "", Description: "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable secure mount options (nosuid, nodev)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (ProvisioningSpec) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ProvisioningSpec", Comments: [3]string{"" /* encoder.HeadComment */, "ProvisioningSpec describes how the volume is provisioned." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ProvisioningSpec describes how the volume is provisioned.", AppearsIn: []encoder.Appearance{ { TypeName: "RawVolumeConfigV1Alpha1", FieldName: "provisioning", }, { TypeName: "SwapVolumeConfigV1Alpha1", FieldName: "provisioning", }, { TypeName: "UserVolumeConfigV1Alpha1", FieldName: "provisioning", }, { TypeName: "VolumeConfigV1Alpha1", FieldName: "provisioning", }, }, Fields: []encoder.Doc{ { Name: "diskSelector", Type: "DiskSelector", Note: "", Description: "The disk selector expression.", Comments: [3]string{"" /* encoder.HeadComment */, "The disk selector expression." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "grow", Type: "bool", Note: "", Description: "Should the volume grow to the size of the disk (if possible).", Comments: [3]string{"" /* encoder.HeadComment */, "Should the volume grow to the size of the disk (if possible)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "minSize", Type: "ByteSize", Note: "", Description: "The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.", Comments: [3]string{"" /* encoder.HeadComment */, "The minimum size of the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "maxSize", Type: "Size", Note: "", Description: "The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.", Comments: [3]string{"" /* encoder.HeadComment */, "The maximum size of the volume, if not specified the volume can grow to the size of the" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[2].AddExample("", "2.5GiB") doc.Fields[3].AddExample("", "50GiB") doc.Fields[3].AddExample("", "80%") return doc } func (DiskSelector) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "DiskSelector", Comments: [3]string{"" /* encoder.HeadComment */, "DiskSelector selects a disk for the volume." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "DiskSelector selects a disk for the volume.", AppearsIn: []encoder.Appearance{ { TypeName: "ProvisioningSpec", FieldName: "diskSelector", }, }, Fields: []encoder.Doc{ { Name: "match", Type: "Expression", Note: "", Description: "The Common Expression Language (CEL) expression to match the disk.", Comments: [3]string{"" /* encoder.HeadComment */, "The Common Expression Language (CEL) expression to match the disk." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("match disks with size between 120GB and 1TB", exampleDiskSelector1()) doc.Fields[0].AddExample("match SATA disks that are not rotational and not system disks", exampleDiskSelector2()) return doc } func (ZswapConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ZswapConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ZswapConfig is a zswap (compressed memory) configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ZswapConfig is a zswap (compressed memory) configuration document.\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\nwhen the system is under memory pressure.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "maxPoolPercent", Type: "int", Note: "", Description: "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.", Comments: [3]string{"" /* encoder.HeadComment */, "The maximum percent of memory that zswap can use." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "shrinkerEnabled", Type: "bool", Note: "", Description: "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable the shrinker feature: kernel might move" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleZswapConfigV1Alpha1()) return doc } // GetFileDoc returns documentation for the file block_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "block", Description: "Package block provides block device and volume configuration documents.\n", Structs: []*encoder.Doc{ EncryptionSpec{}.Doc(), EncryptionKey{}.Doc(), EncryptionKeyStatic{}.Doc(), EncryptionKeyKMS{}.Doc(), EncryptionKeyTPM{}.Doc(), EncryptionKeyTPMOptions{}.Doc(), EncryptionKeyNodeID{}.Doc(), ExistingVolumeConfigV1Alpha1{}.Doc(), VolumeDiscoverySpec{}.Doc(), VolumeSelector{}.Doc(), ExistingMountSpec{}.Doc(), ExternalVolumeConfigV1Alpha1{}.Doc(), ExternalMountSpec{}.Doc(), VirtiofsMountSpec{}.Doc(), RawVolumeConfigV1Alpha1{}.Doc(), SwapVolumeConfigV1Alpha1{}.Doc(), UserVolumeConfigV1Alpha1{}.Doc(), UserMountSpec{}.Doc(), FilesystemSpec{}.Doc(), VolumeConfigV1Alpha1{}.Doc(), MountSpec{}.Doc(), ProvisioningSpec{}.Doc(), DiskSelector{}.Doc(), ZswapConfigV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/block/blockhelpers/blockhelpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package blockhelpers provides helper functions for working with block resources. package blockhelpers import ( "context" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" blockpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/block" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // MatchDisks returns a list of disks that match the given expression. func MatchDisks(ctx context.Context, st state.State, expression *cel.Expression) ([]*block.Disk, error) { disks, err := safe.StateListAll[*block.Disk](ctx, st) if err != nil { return nil, err } var matchedDisks []*block.Disk for disk := range disks.All() { spec := &blockpb.DiskSpec{} if err = proto.ResourceSpecToProto(disk, spec); err != nil { return nil, err } matches, err := expression.EvalBool(celenv.DiskLocator(), map[string]any{ "disk": spec, "system_disk": false, }) if err != nil { return nil, err } if matches { matchedDisks = append(matchedDisks, disk) } } return matchedDisks, nil } ================================================ FILE: pkg/machinery/config/types/block/byte_size.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bytes" "encoding" "fmt" "slices" "strconv" "github.com/dustin/go-humanize" "github.com/siderolabs/go-pointer" "go.yaml.in/yaml/v4" ) // Check interfaces. var ( _ encoding.TextMarshaler = ByteSize{} _ encoding.TextUnmarshaler = (*ByteSize)(nil) _ yaml.IsZeroer = ByteSize{} ) // ByteSize is a byte size which can be conveniently represented as a human readable string // with IEC sizes, e.g. 100MB. type ByteSize struct { value *uint64 raw []byte negative bool } // Value returns the value. func (bs ByteSize) Value() uint64 { return pointer.SafeDeref(bs.value) } // MarshalText implements encoding.TextMarshaler. func (bs ByteSize) MarshalText() ([]byte, error) { if bs.raw != nil { return bs.raw, nil } negative := "" if bs.negative { negative = "-" } if bs.value != nil { return []byte(negative + strconv.FormatUint(*bs.value, 10)), nil } return nil, nil } // UnmarshalText implements encoding.TextUnmarshaler. func (bs *ByteSize) UnmarshalText(text []byte) error { if len(text) == 0 { return nil } raw := slices.Clone(text) if v, ok := bytes.CutPrefix(text, []byte("-")); ok { text = v bs.negative = true } value, err := humanize.ParseBytes(string(text)) if err != nil { return err } bs.value = new(value) bs.raw = raw return nil } // IsZero implements yaml.IsZeroer. func (bs ByteSize) IsZero() bool { return bs.value == nil && bs.raw == nil } // Merge implements merger interface. func (bs *ByteSize) Merge(other any) error { otherBS, ok := other.(ByteSize) if !ok { return fmt.Errorf("cannot merge %T with %T", bs, other) } bs.raw = otherBS.raw bs.value = otherBS.value return nil } // IsNegative returns true if the value is negative. func (bs ByteSize) IsNegative() bool { return bs.negative } ================================================ FILE: pkg/machinery/config/types/block/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ExistingVolumeConfigV1Alpha1 -type RawVolumeConfigV1Alpha1 -type SwapVolumeConfigV1Alpha1 -type UserVolumeConfigV1Alpha1 -type ExternalVolumeConfigV1Alpha1 -type VolumeConfigV1Alpha1 -type ZswapConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package block // DeepCopy generates a deep copy of *ExistingVolumeConfigV1Alpha1. func (o *ExistingVolumeConfigV1Alpha1) DeepCopy() *ExistingVolumeConfigV1Alpha1 { var cp ExistingVolumeConfigV1Alpha1 = *o if o.MountSpec.MountReadOnly != nil { cp.MountSpec.MountReadOnly = new(bool) *cp.MountSpec.MountReadOnly = *o.MountSpec.MountReadOnly } if o.MountSpec.MountDisableAccessTime != nil { cp.MountSpec.MountDisableAccessTime = new(bool) *cp.MountSpec.MountDisableAccessTime = *o.MountSpec.MountDisableAccessTime } if o.MountSpec.MountSecure != nil { cp.MountSpec.MountSecure = new(bool) *cp.MountSpec.MountSecure = *o.MountSpec.MountSecure } return &cp } // DeepCopy generates a deep copy of *RawVolumeConfigV1Alpha1. func (o *RawVolumeConfigV1Alpha1) DeepCopy() *RawVolumeConfigV1Alpha1 { var cp RawVolumeConfigV1Alpha1 = *o if o.ProvisioningSpec.ProvisioningGrow != nil { cp.ProvisioningSpec.ProvisioningGrow = new(bool) *cp.ProvisioningSpec.ProvisioningGrow = *o.ProvisioningSpec.ProvisioningGrow } if o.ProvisioningSpec.ProvisioningMinSize.value != nil { cp.ProvisioningSpec.ProvisioningMinSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMinSize.value = *o.ProvisioningSpec.ProvisioningMinSize.value } if o.ProvisioningSpec.ProvisioningMinSize.raw != nil { cp.ProvisioningSpec.ProvisioningMinSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMinSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMinSize.raw, o.ProvisioningSpec.ProvisioningMinSize.raw) } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = new(PercentageSize) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw) } } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = new(ByteSize) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw) } } if o.EncryptionSpec.EncryptionKeys != nil { cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys)) copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys) for i3 := range o.EncryptionSpec.EncryptionKeys { if o.EncryptionSpec.EncryptionKeys[i3].KeyStatic != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = new(EncryptionKeyStatic) *cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = *o.EncryptionSpec.EncryptionKeys[i3].KeyStatic } if o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = new(EncryptionKeyNodeID) *cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = *o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID } if o.EncryptionSpec.EncryptionKeys[i3].KeyKMS != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = new(EncryptionKeyKMS) *cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = *o.EncryptionSpec.EncryptionKeys[i3].KeyKMS } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = new(EncryptionKeyTPM) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = new(EncryptionKeyTPMOptions) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs = make([]int, len(o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs)) copy(cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs, o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs) } } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll } } if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE } } } if o.EncryptionSpec.EncryptionPerfOptions != nil { cp.EncryptionSpec.EncryptionPerfOptions = make([]string, len(o.EncryptionSpec.EncryptionPerfOptions)) copy(cp.EncryptionSpec.EncryptionPerfOptions, o.EncryptionSpec.EncryptionPerfOptions) } return &cp } // DeepCopy generates a deep copy of *SwapVolumeConfigV1Alpha1. func (o *SwapVolumeConfigV1Alpha1) DeepCopy() *SwapVolumeConfigV1Alpha1 { var cp SwapVolumeConfigV1Alpha1 = *o if o.ProvisioningSpec.ProvisioningGrow != nil { cp.ProvisioningSpec.ProvisioningGrow = new(bool) *cp.ProvisioningSpec.ProvisioningGrow = *o.ProvisioningSpec.ProvisioningGrow } if o.ProvisioningSpec.ProvisioningMinSize.value != nil { cp.ProvisioningSpec.ProvisioningMinSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMinSize.value = *o.ProvisioningSpec.ProvisioningMinSize.value } if o.ProvisioningSpec.ProvisioningMinSize.raw != nil { cp.ProvisioningSpec.ProvisioningMinSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMinSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMinSize.raw, o.ProvisioningSpec.ProvisioningMinSize.raw) } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = new(PercentageSize) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw) } } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = new(ByteSize) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw) } } if o.EncryptionSpec.EncryptionKeys != nil { cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys)) copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys) for i3 := range o.EncryptionSpec.EncryptionKeys { if o.EncryptionSpec.EncryptionKeys[i3].KeyStatic != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = new(EncryptionKeyStatic) *cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = *o.EncryptionSpec.EncryptionKeys[i3].KeyStatic } if o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = new(EncryptionKeyNodeID) *cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = *o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID } if o.EncryptionSpec.EncryptionKeys[i3].KeyKMS != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = new(EncryptionKeyKMS) *cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = *o.EncryptionSpec.EncryptionKeys[i3].KeyKMS } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = new(EncryptionKeyTPM) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = new(EncryptionKeyTPMOptions) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs = make([]int, len(o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs)) copy(cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs, o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs) } } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll } } if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE } } } if o.EncryptionSpec.EncryptionPerfOptions != nil { cp.EncryptionSpec.EncryptionPerfOptions = make([]string, len(o.EncryptionSpec.EncryptionPerfOptions)) copy(cp.EncryptionSpec.EncryptionPerfOptions, o.EncryptionSpec.EncryptionPerfOptions) } return &cp } // DeepCopy generates a deep copy of *UserVolumeConfigV1Alpha1. func (o *UserVolumeConfigV1Alpha1) DeepCopy() *UserVolumeConfigV1Alpha1 { var cp UserVolumeConfigV1Alpha1 = *o if o.VolumeType != nil { cp.VolumeType = new(VolumeType) *cp.VolumeType = *o.VolumeType } if o.ProvisioningSpec.ProvisioningGrow != nil { cp.ProvisioningSpec.ProvisioningGrow = new(bool) *cp.ProvisioningSpec.ProvisioningGrow = *o.ProvisioningSpec.ProvisioningGrow } if o.ProvisioningSpec.ProvisioningMinSize.value != nil { cp.ProvisioningSpec.ProvisioningMinSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMinSize.value = *o.ProvisioningSpec.ProvisioningMinSize.value } if o.ProvisioningSpec.ProvisioningMinSize.raw != nil { cp.ProvisioningSpec.ProvisioningMinSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMinSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMinSize.raw, o.ProvisioningSpec.ProvisioningMinSize.raw) } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = new(PercentageSize) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw) } } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = new(ByteSize) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw) } } if o.FilesystemSpec.ProjectQuotaSupportConfig != nil { cp.FilesystemSpec.ProjectQuotaSupportConfig = new(bool) *cp.FilesystemSpec.ProjectQuotaSupportConfig = *o.FilesystemSpec.ProjectQuotaSupportConfig } if o.EncryptionSpec.EncryptionKeys != nil { cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys)) copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys) for i3 := range o.EncryptionSpec.EncryptionKeys { if o.EncryptionSpec.EncryptionKeys[i3].KeyStatic != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = new(EncryptionKeyStatic) *cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = *o.EncryptionSpec.EncryptionKeys[i3].KeyStatic } if o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = new(EncryptionKeyNodeID) *cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = *o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID } if o.EncryptionSpec.EncryptionKeys[i3].KeyKMS != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = new(EncryptionKeyKMS) *cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = *o.EncryptionSpec.EncryptionKeys[i3].KeyKMS } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = new(EncryptionKeyTPM) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = new(EncryptionKeyTPMOptions) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs = make([]int, len(o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs)) copy(cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs, o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs) } } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll } } if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE } } } if o.EncryptionSpec.EncryptionPerfOptions != nil { cp.EncryptionSpec.EncryptionPerfOptions = make([]string, len(o.EncryptionSpec.EncryptionPerfOptions)) copy(cp.EncryptionSpec.EncryptionPerfOptions, o.EncryptionSpec.EncryptionPerfOptions) } if o.MountSpec.MountDisableAccessTime != nil { cp.MountSpec.MountDisableAccessTime = new(bool) *cp.MountSpec.MountDisableAccessTime = *o.MountSpec.MountDisableAccessTime } if o.MountSpec.MountSecure != nil { cp.MountSpec.MountSecure = new(bool) *cp.MountSpec.MountSecure = *o.MountSpec.MountSecure } return &cp } // DeepCopy generates a deep copy of *ExternalVolumeConfigV1Alpha1. func (o *ExternalVolumeConfigV1Alpha1) DeepCopy() *ExternalVolumeConfigV1Alpha1 { var cp ExternalVolumeConfigV1Alpha1 = *o if o.MountSpec.MountReadOnly != nil { cp.MountSpec.MountReadOnly = new(bool) *cp.MountSpec.MountReadOnly = *o.MountSpec.MountReadOnly } if o.MountSpec.MountDisableAccessTime != nil { cp.MountSpec.MountDisableAccessTime = new(bool) *cp.MountSpec.MountDisableAccessTime = *o.MountSpec.MountDisableAccessTime } if o.MountSpec.MountSecure != nil { cp.MountSpec.MountSecure = new(bool) *cp.MountSpec.MountSecure = *o.MountSpec.MountSecure } if o.MountSpec.MountVirtiofs != nil { cp.MountSpec.MountVirtiofs = new(VirtiofsMountSpec) *cp.MountSpec.MountVirtiofs = *o.MountSpec.MountVirtiofs } return &cp } // DeepCopy generates a deep copy of *VolumeConfigV1Alpha1. func (o *VolumeConfigV1Alpha1) DeepCopy() *VolumeConfigV1Alpha1 { var cp VolumeConfigV1Alpha1 = *o if o.ProvisioningSpec.ProvisioningGrow != nil { cp.ProvisioningSpec.ProvisioningGrow = new(bool) *cp.ProvisioningSpec.ProvisioningGrow = *o.ProvisioningSpec.ProvisioningGrow } if o.ProvisioningSpec.ProvisioningMinSize.value != nil { cp.ProvisioningSpec.ProvisioningMinSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMinSize.value = *o.ProvisioningSpec.ProvisioningMinSize.value } if o.ProvisioningSpec.ProvisioningMinSize.raw != nil { cp.ProvisioningSpec.ProvisioningMinSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMinSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMinSize.raw, o.ProvisioningSpec.ProvisioningMinSize.raw) } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = new(PercentageSize) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.PercentageSize.raw) } } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = new(ByteSize) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = new(uint64) *cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value = *o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.value } if o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw != nil { cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw = make([]byte, len(o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw)) copy(cp.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw, o.ProvisioningSpec.ProvisioningMaxSize.ByteSize.raw) } } if o.EncryptionSpec.EncryptionKeys != nil { cp.EncryptionSpec.EncryptionKeys = make([]EncryptionKey, len(o.EncryptionSpec.EncryptionKeys)) copy(cp.EncryptionSpec.EncryptionKeys, o.EncryptionSpec.EncryptionKeys) for i3 := range o.EncryptionSpec.EncryptionKeys { if o.EncryptionSpec.EncryptionKeys[i3].KeyStatic != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = new(EncryptionKeyStatic) *cp.EncryptionSpec.EncryptionKeys[i3].KeyStatic = *o.EncryptionSpec.EncryptionKeys[i3].KeyStatic } if o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = new(EncryptionKeyNodeID) *cp.EncryptionSpec.EncryptionKeys[i3].KeyNodeID = *o.EncryptionSpec.EncryptionKeys[i3].KeyNodeID } if o.EncryptionSpec.EncryptionKeys[i3].KeyKMS != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = new(EncryptionKeyKMS) *cp.EncryptionSpec.EncryptionKeys[i3].KeyKMS = *o.EncryptionSpec.EncryptionKeys[i3].KeyKMS } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = new(EncryptionKeyTPM) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = new(EncryptionKeyTPMOptions) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs = make([]int, len(o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs)) copy(cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs, o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMOptions.PCRs) } } if o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll = *o.EncryptionSpec.EncryptionKeys[i3].KeyTPM.TPMCheckSecurebootStatusOnEnroll } } if o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE != nil { cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = new(bool) *cp.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE = *o.EncryptionSpec.EncryptionKeys[i3].KeyLockToSTATE } } } if o.EncryptionSpec.EncryptionPerfOptions != nil { cp.EncryptionSpec.EncryptionPerfOptions = make([]string, len(o.EncryptionSpec.EncryptionPerfOptions)) copy(cp.EncryptionSpec.EncryptionPerfOptions, o.EncryptionSpec.EncryptionPerfOptions) } if o.MountSpec.MountSecure != nil { cp.MountSpec.MountSecure = new(bool) *cp.MountSpec.MountSecure = *o.MountSpec.MountSecure } return &cp } // DeepCopy generates a deep copy of *ZswapConfigV1Alpha1. func (o *ZswapConfigV1Alpha1) DeepCopy() *ZswapConfigV1Alpha1 { var cp ZswapConfigV1Alpha1 = *o if o.MaxPoolPercentConfig != nil { cp.MaxPoolPercentConfig = new(int) *cp.MaxPoolPercentConfig = *o.MaxPoolPercentConfig } if o.ShrinkerEnabledConfig != nil { cp.ShrinkerEnabledConfig = new(bool) *cp.ShrinkerEnabledConfig = *o.ShrinkerEnabledConfig } return &cp } ================================================ FILE: pkg/machinery/config/types/block/encryption.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "errors" "fmt" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) //docgen:jsonschema // EncryptionSpec represents volume encryption settings. // // examples: // - value: exampleEncryptionSpec() type EncryptionSpec struct { // description: > // Encryption provider to use for the encryption. // values: // - luks2 EncryptionProvider block.EncryptionProviderType `yaml:"provider"` // description: > // Defines the encryption keys generation and storage method. EncryptionKeys []EncryptionKey `yaml:"keys"` // description: > // Cipher to use for the encryption. // Depends on the encryption provider. // values: // - aes-xts-plain64 // - xchacha12,aes-adiantum-plain64 // - xchacha20,aes-adiantum-plain64 // examples: // - value: '"aes-xts-plain64"' EncryptionCipher string `yaml:"cipher,omitempty"` // description: > // Defines the encryption key length. EncryptionKeySize uint `yaml:"keySize,omitempty"` // description: > // Defines the encryption sector size. // examples: // - value: '4096' EncryptionBlockSize uint64 `yaml:"blockSize,omitempty"` // description: > // Additional --perf parameters for the LUKS2 encryption. // values: // - no_read_workqueue // - no_write_workqueue // - same_cpu_crypt // examples: // - value: > // []string{"no_read_workqueue","no_write_workqueue"} EncryptionPerfOptions []string `yaml:"options,omitempty"` } func exampleEncryptionSpec() *EncryptionSpec { return &EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []EncryptionKey{ { KeySlot: 0, KeyStatic: &EncryptionKeyStatic{ KeyData: "exampleKey", }, }, { KeySlot: 1, KeyKMS: &EncryptionKeyKMS{ KMSEndpoint: "https://example-kms-endpoint.com", }, }, }, EncryptionCipher: "aes-xts-plain64", EncryptionBlockSize: 4096, } } // IsZero checks if the encryption spec is zero. func (s EncryptionSpec) IsZero() bool { return s.EncryptionProvider == block.EncryptionProviderNone && len(s.EncryptionKeys) == 0 } // EncryptionKey represents configuration for disk encryption key. type EncryptionKey struct { // description: > // Key slot number for LUKS2 encryption. KeySlot int `yaml:"slot"` // description: > // Key which value is stored in the configuration file. KeyStatic *EncryptionKeyStatic `yaml:"static,omitempty"` // description: > // Deterministically generated key from the node UUID and PartitionLabel. KeyNodeID *EncryptionKeyNodeID `yaml:"nodeID,omitempty"` // description: > // KMS managed encryption key. KeyKMS *EncryptionKeyKMS `yaml:"kms,omitempty"` // description: > // Enable TPM based disk encryption. KeyTPM *EncryptionKeyTPM `yaml:"tpm,omitempty"` // description: > // Lock the disk encryption key to the random salt stored in the STATE partition. // This is useful to prevent the volume from being unlocked if STATE partition is compromised // or replaced. It is recommended to use this option with TPM disk encryption for // non-STATE volumes. KeyLockToSTATE *bool `yaml:"lockToState,omitempty"` } // EncryptionKeyStatic represents throw away key type. type EncryptionKeyStatic struct { // description: > // Defines the static passphrase value. KeyData string `yaml:"passphrase,omitempty"` } // EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. // // examples: // - value: exampleKMSKey() type EncryptionKeyKMS struct { // description: > // KMS endpoint to Seal/Unseal the key. KMSEndpoint string `yaml:"endpoint"` } // EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. type EncryptionKeyTPM struct { // description: > // TPM options for key protection. TPMOptions *EncryptionKeyTPMOptions `yaml:"options,omitempty"` // description: > // Check that Secureboot is enabled in the EFI firmware. // // If Secureboot is not enabled, the enrollment of the key will fail. TPMCheckSecurebootStatusOnEnroll *bool `yaml:"checkSecurebootStatusOnEnroll,omitempty"` } // EncryptionKeyTPMOptions represents the options for TPM-based key protection. type EncryptionKeyTPMOptions struct { // description: > // List of PCRs to bind the key to. // If not set, defaults to PCR 7, can be disabled by passing an empty list. PCRs []int `yaml:"pcrs,omitempty"` } // EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. type EncryptionKeyNodeID struct{} func exampleKMSKey() *EncryptionKeyKMS { return &EncryptionKeyKMS{ KMSEndpoint: "https://192.168.88.21:4443", } } // Validate implements config.Validator interface. // //nolint:gocyclo func (s EncryptionSpec) Validate() ([]string, error) { if s.IsZero() { return nil, nil } var errs error switch s.EncryptionProvider { case block.EncryptionProviderLUKS2: case block.EncryptionProviderNone: fallthrough default: errs = errors.Join(errs, fmt.Errorf("unsupported encryption provider: %s", s.EncryptionProvider)) } if len(s.EncryptionKeys) == 0 { errs = errors.Join(errs, errors.New("encryption keys are required")) } slotsInUse := make(map[int]struct{}, len(s.EncryptionKeys)) for _, key := range s.EncryptionKeys { if _, ok := slotsInUse[key.KeySlot]; ok { errs = errors.Join(errs, fmt.Errorf("duplicate key slot %d", key.KeySlot)) } slotsInUse[key.KeySlot] = struct{}{} if key.KeyStatic == nil && key.KeyNodeID == nil && key.KeyKMS == nil && key.KeyTPM == nil { errs = errors.Join(errs, fmt.Errorf("at least one encryption key type must be specified for slot %d", key.KeySlot)) } if key.KeyTPM != nil && key.KeyTPM.TPMOptions != nil { for _, pcr := range key.KeyTPM.TPMOptions.PCRs { if pcr < 0 || pcr > 23 { errs = errors.Join(errs, fmt.Errorf("TPM PCR %d is out of range (0-23)", pcr)) } } } } return nil, errs } // Provider implements the config.Provider interface. func (s EncryptionSpec) Provider() block.EncryptionProviderType { return s.EncryptionProvider } // Cipher implements the config.Provider interface. func (s EncryptionSpec) Cipher() string { return s.EncryptionCipher } // KeySize implements the config.Provider interface. func (s EncryptionSpec) KeySize() uint { return s.EncryptionKeySize } // BlockSize implements the config.Provider interface. func (s EncryptionSpec) BlockSize() uint64 { return s.EncryptionBlockSize } // Options implements the config.Provider interface. func (s EncryptionSpec) Options() []string { return s.EncryptionPerfOptions } // Keys implements the config.Provider interface. func (s EncryptionSpec) Keys() []config.EncryptionKey { return xslices.Map(s.EncryptionKeys, func(k EncryptionKey) config.EncryptionKey { return k }) } // Slot implements the config.Provider interface. func (k EncryptionKey) Slot() int { return k.KeySlot } // LockToSTATE implements the config.Provider interface. func (k EncryptionKey) LockToSTATE() bool { return pointer.SafeDeref(k.KeyLockToSTATE) } // Static implements the config.Provider interface. func (k EncryptionKey) Static() config.EncryptionKeyStatic { if k.KeyStatic == nil { return nil } return k.KeyStatic } // NodeID implements the config.Provider interface. func (k EncryptionKey) NodeID() config.EncryptionKeyNodeID { if k.KeyNodeID == nil { return nil } return k.KeyNodeID } // KMS implements the config.Provider interface. func (k EncryptionKey) KMS() config.EncryptionKeyKMS { if k.KeyKMS == nil { return nil } return k.KeyKMS } // TPM implements the config.Provider interface. func (k EncryptionKey) TPM() config.EncryptionKeyTPM { if k.KeyTPM == nil { return nil } return k.KeyTPM } // String implements the config.Provider interface. func (e *EncryptionKeyNodeID) String() string { return "nodeid" } // String implements the config.Provider interface. func (e *EncryptionKeyTPM) String() string { return "tpm" } // CheckSecurebootOnEnroll implements the config.Provider interface. func (e *EncryptionKeyTPM) CheckSecurebootOnEnroll() bool { if e == nil { return false } return pointer.SafeDeref(e.TPMCheckSecurebootStatusOnEnroll) } // PCRs implements the config.Provider interface. func (e *EncryptionKeyTPM) PCRs() []int { if e == nil { return []int{} } if e.TPMOptions == nil { return []int{constants.SecureBootStatePCR} } return e.TPMOptions.PCRs } // PubKeyPCRs implements the config.Provider interface. func (e *EncryptionKeyTPM) PubKeyPCRs() []int { // we always lock to PCR 11 return []int{constants.UKIPCR} } // Key implements the config.Provider interface. func (e *EncryptionKeyStatic) Key() []byte { return []byte(e.KeyData) } // String implements the config.Provider interface. func (e *EncryptionKeyStatic) String() string { return "static" } // Endpoint implements the config.Provider interface. func (e *EncryptionKeyKMS) Endpoint() string { return e.KMSEndpoint } // String implements the config.Provider interface. func (e *EncryptionKeyKMS) String() string { return "kms" } ================================================ FILE: pkg/machinery/config/types/block/existing_volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // ExistingVolumeConfigKind is a config document kind. const ExistingVolumeConfigKind = "ExistingVolumeConfig" func init() { registry.Register(ExistingVolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &ExistingVolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.ExistingVolumeConfig = &ExistingVolumeConfigV1Alpha1{} _ config.ConflictingDocument = &ExistingVolumeConfigV1Alpha1{} _ config.NamedDocument = &ExistingVolumeConfigV1Alpha1{} _ config.Validator = &ExistingVolumeConfigV1Alpha1{} ) // ExistingVolumeConfigV1Alpha1 is an existing volume configuration document. // // description: | // Existing volumes allow to mount partitions (or whole disks) that were created // outside of Talos. Volume will be mounted under `/var/mnt/`. // The existing volume config name should not conflict with user volume names. // examples: // - value: exampleExistingVolumeConfigV1Alpha1() // alias: ExistingVolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/ExistingVolumeConfig type ExistingVolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the volume. // // Name can only contain: // lowercase and uppercase ASCII letters, digits, and hyphens. MetaName string `yaml:"name"` // description: | // The discovery describes how to find a volume. VolumeDiscoverySpec VolumeDiscoverySpec `yaml:"discovery,omitempty"` // description: | // The mount describes additional mount options. MountSpec ExistingMountSpec `yaml:"mount,omitempty"` } // VolumeDiscoverySpec describes how the volume is discovered. type VolumeDiscoverySpec struct { // description: | // The volume selector expression. VolumeSelectorConfig VolumeSelector `yaml:"volumeSelector,omitempty"` } // VolumeSelector selects an existing volume. type VolumeSelector struct { // description: | // The Common Expression Language (CEL) expression to match the volume. // schema: // type: string // examples: // - value: > // exampleVolumeSelector1() // name: match volumes with partition label MY-DATA // - value: > // exampleVolumeSelector2() // name: match xfs volume on disk with serial 'SERIAL123' Match cel.Expression `yaml:"match,omitempty"` } func exampleVolumeSelector1() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`volume.partition_label == "MY-DATA"`, celenv.VolumeLocator())) } func exampleVolumeSelector2() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`volume.name == "xfs" && disk.serial == "SERIAL123"`, celenv.VolumeLocator())) } // ExistingMountSpec describes how the volume is mounted. type ExistingMountSpec struct { // description: | // Mount the volume read-only. MountReadOnly *bool `yaml:"readOnly,omitempty"` // description: | // If true, disable file access time updates. MountDisableAccessTime *bool `yaml:"disableAccessTime,omitempty"` // description: | // Enable secure mount options (nosuid, nodev). // // Defaults to true for better security. MountSecure *bool `yaml:"secure,omitempty"` } // NewExistingVolumeConfigV1Alpha1 creates a new raw volume config document. func NewExistingVolumeConfigV1Alpha1() *ExistingVolumeConfigV1Alpha1 { return &ExistingVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ExistingVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleExistingVolumeConfigV1Alpha1() *ExistingVolumeConfigV1Alpha1 { cfg := NewExistingVolumeConfigV1Alpha1() cfg.MetaName = "my-existing-volume" cfg.VolumeDiscoverySpec = VolumeDiscoverySpec{ VolumeSelectorConfig: VolumeSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`volume.partition_label == "MY-DATA"`, celenv.VolumeLocator())), }, } return cfg } // Name implements config.NamedDocument interface. func (s *ExistingVolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *ExistingVolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *ExistingVolumeConfigV1Alpha1) ConflictsWithKinds() []string { return []string{UserVolumeConfigKind} } // Validate implements config.Validator interface. // //nolint:gocyclo,dupl func (s *ExistingVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string //nolint:prealloc validationErrors error ) if s.MetaName == "" { validationErrors = errors.Join(validationErrors, errors.New("name is required")) } if strings.ContainsFunc(s.MetaName, func(r rune) bool { switch { case r >= 'a' && r <= 'z': return false case r >= 'A' && r <= 'Z': return false case r >= '0' && r <= '9': return false case r == '-': return false default: // invalid symbol return true } }) { validationErrors = errors.Join(validationErrors, errors.New("name can only contain lowercase and uppercase ASCII letters, digits, and hyphens")) } extraWarnings, extraErrors := s.VolumeDiscoverySpec.Validate(true) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) return warnings, validationErrors } // ExistingVolumeConfigSignal is a signal for user volume config. func (s *ExistingVolumeConfigV1Alpha1) ExistingVolumeConfigSignal() {} // VolumeDiscovery implements config.ExistingVolumeConfig interface. func (s *ExistingVolumeConfigV1Alpha1) VolumeDiscovery() config.VolumeDiscoveryConfig { return s.VolumeDiscoverySpec } // Mount implements config.ExistingVolumeConfig interface. func (s *ExistingVolumeConfigV1Alpha1) Mount() config.ExistingVolumeMountConfig { return s.MountSpec } // Validate the provisioning spec. func (s VolumeDiscoverySpec) Validate(required bool) ([]string, error) { var validationErrors error if !s.VolumeSelectorConfig.Match.IsZero() { if err := s.VolumeSelectorConfig.Match.ParseBool(celenv.VolumeLocator()); err != nil { validationErrors = errors.Join(validationErrors, fmt.Errorf("volume selector is invalid: %w", err)) } } else { validationErrors = errors.Join(validationErrors, errors.New("volume selector is required")) } return nil, validationErrors } // VolumeSelector implements config.VolumeDiscoveryConfig interface. func (s VolumeDiscoverySpec) VolumeSelector() cel.Expression { return s.VolumeSelectorConfig.Match } // ReadOnly implements config.ExistingVolumeMountConfig interface. func (s ExistingMountSpec) ReadOnly() bool { return pointer.SafeDeref(s.MountReadOnly) } // DisableAccessTime implements config.ExistingVolumeMountConfig interface. func (s ExistingMountSpec) DisableAccessTime() bool { return pointer.SafeDeref(s.MountDisableAccessTime) } // Secure implements config.ExistingVolumeMountConfig interface. func (s ExistingMountSpec) Secure() bool { if s.MountSecure == nil { return true } return *s.MountSecure } ================================================ FILE: pkg/machinery/config/types/block/existing_volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestExistingVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 }{ { name: "with selector", filename: "existingvolumeconfig_selector.yaml", cfg: func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 { c := block.NewExistingVolumeConfigV1Alpha1() c.MetaName = "my-lovely-volume" require.NoError(t, c.VolumeDiscoverySpec.VolumeSelectorConfig.Match.UnmarshalText([]byte(`volume.partition_label == "MY-DATA"`))) return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestExistingVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 expectedErrors string }{ { name: "no name", cfg: func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 { c := block.NewExistingVolumeConfigV1Alpha1() require.NoError(t, c.VolumeDiscoverySpec.VolumeSelectorConfig.Match.UnmarshalText([]byte(`volume.partition_label == "MY-DATA"`))) return c }, expectedErrors: "name is required", }, { name: "invalid characters in name", cfg: func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 { c := block.NewExistingVolumeConfigV1Alpha1() c.MetaName = "some/name" require.NoError(t, c.VolumeDiscoverySpec.VolumeSelectorConfig.Match.UnmarshalText([]byte(`volume.partition_label == "MY-DATA"`))) return c }, expectedErrors: "name can only contain lowercase and uppercase ASCII letters, digits, and hyphens", }, { name: "invalid volume selector", cfg: func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 { c := block.NewExistingVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.VolumeDiscoverySpec.VolumeSelectorConfig.Match.UnmarshalText([]byte(`volume.partition_label == 3`))) return c }, expectedErrors: "volume selector is invalid: ERROR: :1:24: found no matching overload for '_==_' applied to '(string, int)'\n | volume.partition_label == 3\n | .......................^", }, { name: "valid", cfg: func(t *testing.T) *block.ExistingVolumeConfigV1Alpha1 { c := block.NewExistingVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.VolumeDiscoverySpec.VolumeSelectorConfig.Match.UnmarshalText([]byte(`volume.partition_label == "MY-DATA"`))) return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/block/external_volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // ExternalVolumeConfigKind is a config document kind. const ExternalVolumeConfigKind = "ExternalVolumeConfig" func init() { registry.Register(ExternalVolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &ExternalVolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.ExternalVolumeConfig = &ExternalVolumeConfigV1Alpha1{} _ config.NamedDocument = &ExternalVolumeConfigV1Alpha1{} _ config.Validator = &ExternalVolumeConfigV1Alpha1{} ) const maxExternalVolumeNameLength = constants.PartitionLabelLength - len(constants.ExternalVolumePrefix) // FilesystemType is an alias for block.FilesystemType. type FilesystemType = block.FilesystemType // ExternalVolumeConfigV1Alpha1 is an external disk mount configuration document. // // description: | // External volumes allow to mount volumes that were created outside of Talos, // over the network or API. Volume will be mounted under `/var/mnt/`. // The external volume config name should not conflict with user volume names. // examples: // - value: exampleExternalVolumeConfigV1Alpha1Virtiofs() // alias: ExternalVolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/ExternalVolumeConfig type ExternalVolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the mount. // // Name might be between 1 and 34 characters long and can only contain: // lowercase and uppercase ASCII letters, digits, and hyphens. MetaName string `yaml:"name"` // description: | // Filesystem type. // values: // - virtiofs // - nfs // schema: // type: string FilesystemType FilesystemType `yaml:"filesystemType"` // description: | // The mount describes additional mount options. MountSpec ExternalMountSpec `yaml:"mount,omitempty"` } // ExternalMountSpec describes how the external volume is mounted. type ExternalMountSpec struct { // description: | // Mount the volume read-only. MountReadOnly *bool `yaml:"readOnly,omitempty"` // description: | // If true, disable file access time updates. MountDisableAccessTime *bool `yaml:"disableAccessTime,omitempty"` // description: | // Enable secure mount options (nosuid, nodev). // // Defaults to true for better security. MountSecure *bool `yaml:"secure,omitempty"` // description: | // Virtiofs mount options. MountVirtiofs *VirtiofsMountSpec `yaml:"virtiofs,omitempty"` } // VirtiofsMountSpec describes Virtiofs mount options. type VirtiofsMountSpec struct { // description: | // Selector tag for the Virtiofs mount. VirtiofsTag string `yaml:"tag"` } // NewExternalVolumeConfigV1Alpha1 creates a new user mount config document. func NewExternalVolumeConfigV1Alpha1() *ExternalVolumeConfigV1Alpha1 { return &ExternalVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ExternalVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleExternalVolumeConfigV1Alpha1Virtiofs() *ExternalVolumeConfigV1Alpha1 { cfg := NewExternalVolumeConfigV1Alpha1() cfg.MetaName = "mount1" cfg.FilesystemType = block.FilesystemTypeVirtiofs cfg.MountSpec.MountVirtiofs = &VirtiofsMountSpec{ VirtiofsTag: "Data", } return cfg } // Name implements config.NamedDocument interface. func (s *ExternalVolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *ExternalVolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo,dupl func (s *ExternalVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string validationErrors error ) if s.MetaName == "" { validationErrors = errors.Join(validationErrors, errors.New("name is required")) } if len(s.MetaName) < 1 || len(s.MetaName) > maxExternalVolumeNameLength { validationErrors = errors.Join(validationErrors, fmt.Errorf("name must be between 1 and %d characters long", maxExternalVolumeNameLength)) } if strings.ContainsFunc(s.MetaName, func(r rune) bool { switch { case r >= 'a' && r <= 'z': return false case r >= 'A' && r <= 'Z': return false case r >= '0' && r <= '9': return false case r == '-': return false default: // invalid symbol return true } }) { validationErrors = errors.Join(validationErrors, errors.New("name can only contain lowercase and uppercase ASCII letters, digits, and hyphens")) } switch s.FilesystemType { case block.FilesystemTypeVirtiofs: extraWarnings, extraErrors := s.MountSpec.MountVirtiofs.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) case block.FilesystemTypeNone, block.FilesystemTypeXFS, block.FilesystemTypeVFAT, block.FilesystemTypeEXT4, block.FilesystemTypeISO9660, block.FilesystemTypeSwap: fallthrough default: validationErrors = errors.Join(validationErrors, fmt.Errorf("invalid filesystem type: %s", s.FilesystemType)) } return warnings, validationErrors } // ExternalVolumeConfigSignal is a signal for user mount config. func (s *ExternalVolumeConfigV1Alpha1) ExternalVolumeConfigSignal() {} // Type implements config.ExternalVolumeConfig interface. func (s *ExternalVolumeConfigV1Alpha1) Type() FilesystemType { return s.FilesystemType } // Mount implements config.ExternalVolumeConfig interface. func (s *ExternalVolumeConfigV1Alpha1) Mount() config.ExternalVolumeMountConfig { return s.MountSpec } // ReadOnly implements config.ExternalVolumeMountConfig interface. func (s ExternalMountSpec) ReadOnly() bool { return pointer.SafeDeref(s.MountReadOnly) } // DisableAccessTime implements config.ExternalVolumeMountConfig interface. func (s ExternalMountSpec) DisableAccessTime() bool { return pointer.SafeDeref(s.MountDisableAccessTime) } // Secure implements config.ExternalVolumeMountConfig interface. func (s ExternalMountSpec) Secure() bool { if s.MountSecure == nil { return true } return *s.MountSecure } // Virtiofs implements config.VolumeMountConfig interface. func (s ExternalMountSpec) Virtiofs() optional.Optional[config.ExternalVolumeMountConfigSpec] { if s.MountVirtiofs == nil { return optional.None[config.ExternalVolumeMountConfigSpec]() } return optional.Some[config.ExternalVolumeMountConfigSpec](*s.MountVirtiofs) } // Source implements config.ExternalVolumeMountConfigSpec interface. func (s VirtiofsMountSpec) Source() string { return s.VirtiofsTag } // Parameters implements config.NFSMountConfig interface. func (s VirtiofsMountSpec) Parameters() ([]block.ParameterSpec, error) { return nil, nil } // Validate implements config.Validator interface. func (s *VirtiofsMountSpec) Validate() ([]string, error) { var validationErrors error if s == nil { return nil, errors.New("virtiofs mount spec is required") } if s.VirtiofsTag == "" { validationErrors = errors.Join(validationErrors, errors.New("virtiofs tag is required")) } return nil, validationErrors } ================================================ FILE: pkg/machinery/config/types/block/external_volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestExternalVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 }{ { name: "basic virtiofs", filename: "externalvolumeconfig_basicvirtiofs.yaml", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = "my-virtiofs-volume" c.FilesystemType = blockres.FilesystemTypeVirtiofs c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestExternalVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 expectedErrors string }{ { name: "no name", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.FilesystemType = blockres.FilesystemTypeVirtiofs c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, expectedErrors: "name is required\nname must be between 1 and 34 characters long", }, { name: "invalid characters in name", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = "some/name" c.FilesystemType = blockres.FilesystemTypeVirtiofs c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, expectedErrors: "name can only contain lowercase and uppercase ASCII letters, digits, and hyphens", }, { name: "no mount spec", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.FilesystemType = blockres.FilesystemTypeVirtiofs return c }, expectedErrors: "virtiofs mount spec is required", }, { name: "invalid type", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.FilesystemType = blockres.FilesystemTypeEXT4 c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, expectedErrors: "invalid filesystem type: ext4", }, { name: "empty type", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, expectedErrors: "invalid filesystem type: none", }, { name: "valid virtiofs", cfg: func(t *testing.T) *block.ExternalVolumeConfigV1Alpha1 { c := block.NewExternalVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.FilesystemType = blockres.FilesystemTypeVirtiofs c.MountSpec.MountVirtiofs = new(block.VirtiofsMountSpec) c.MountSpec.MountVirtiofs.VirtiofsTag = "Data" return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/block/percentage_size.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "bytes" "encoding" "fmt" "slices" "strconv" "github.com/siderolabs/go-pointer" "gopkg.in/yaml.v3" ) // Check interfaces. var ( _ encoding.TextMarshaler = PercentageSize{} _ encoding.TextUnmarshaler = (*PercentageSize)(nil) _ yaml.IsZeroer = PercentageSize{} ) // PercentageSize is a size in percents. type PercentageSize struct { value *uint64 raw []byte negative bool } // Value returns the value. func (ps PercentageSize) Value() uint64 { return pointer.SafeDeref(ps.value) } // MarshalText implements encoding.TextMarshaler. func (ps PercentageSize) MarshalText() ([]byte, error) { if ps.raw != nil { return ps.raw, nil } if ps.value != nil { return []byte(strconv.FormatUint(*ps.value, 10)), nil } return nil, nil } // UnmarshalText implements encoding.TextUnmarshaler. func (ps *PercentageSize) UnmarshalText(text []byte) error { if len(text) == 0 { ps.value = nil ps.raw = nil return nil } if !bytes.HasSuffix(text, []byte("%")) { return fmt.Errorf("percentage must end with '%%'") } raw := slices.Clone(text) if v, ok := bytes.CutPrefix(text, []byte("-")); ok { text = v ps.negative = true } numStr := string(text[:len(text)-1]) value, err := strconv.ParseFloat(numStr, 64) if err != nil { return fmt.Errorf("invalid percentage value: %w", err) } if value < 0 || value > 100 { return fmt.Errorf("percentage must be between 0 and 100, got %v", value) } ps.value = new(uint64(value)) ps.raw = raw return nil } // IsZero implements yaml.IsZeroer. func (ps PercentageSize) IsZero() bool { return ps.value == nil && ps.raw == nil } // IsNegative returns true if the value is negative. func (ps PercentageSize) IsNegative() bool { return ps.negative } // Merge implements merger interface. func (ps *PercentageSize) Merge(other any) error { otherPS, ok := other.(PercentageSize) if !ok { return fmt.Errorf("cannot merge %T with %T", ps, other) } ps.raw = otherPS.raw ps.value = otherPS.value return nil } ================================================ FILE: pkg/machinery/config/types/block/raw_volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // RawVolumeConfigKind is a config document kind. const RawVolumeConfigKind = "RawVolumeConfig" func init() { registry.Register(RawVolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RawVolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RawVolumeConfig = &RawVolumeConfigV1Alpha1{} _ config.NamedDocument = &RawVolumeConfigV1Alpha1{} _ config.Validator = &RawVolumeConfigV1Alpha1{} ) const maxRawVolumeNameLength = constants.PartitionLabelLength - len(constants.RawVolumePrefix) // RawVolumeConfigV1Alpha1 is a raw volume configuration document. // // description: | // Raw volumes allow to create partitions without formatting them. // If you want to use local storage, user volumes is a better choice, // raw volumes are intended to be used with CSI provisioners. // The partition label is automatically generated as `r-`. // examples: // - value: exampleRawVolumeConfigV1Alpha1() // alias: RawVolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/RawVolumeConfig type RawVolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the volume. // // Name might be between 1 and 34 characters long and can only contain: // lowercase and uppercase ASCII letters, digits, and hyphens. MetaName string `yaml:"name"` // description: | // The provisioning describes how the volume is provisioned. ProvisioningSpec ProvisioningSpec `yaml:"provisioning,omitempty"` // description: | // The encryption describes how the volume is encrypted. EncryptionSpec EncryptionSpec `yaml:"encryption,omitempty"` } // NewRawVolumeConfigV1Alpha1 creates a new raw volume config document. func NewRawVolumeConfigV1Alpha1() *RawVolumeConfigV1Alpha1 { return &RawVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RawVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleRawVolumeConfigV1Alpha1() *RawVolumeConfigV1Alpha1 { cfg := NewRawVolumeConfigV1Alpha1() cfg.MetaName = "local-data" cfg.ProvisioningSpec = ProvisioningSpec{ DiskSelectorSpec: DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, ProvisioningMaxSize: MustSize("50GiB"), } return cfg } // Name implements config.NamedDocument interface. func (s *RawVolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *RawVolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo,dupl func (s *RawVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string //nolint:prealloc validationErrors error ) if s.MetaName == "" { validationErrors = errors.Join(validationErrors, errors.New("name is required")) } if len(s.MetaName) < 1 || len(s.MetaName) > maxRawVolumeNameLength { validationErrors = errors.Join(validationErrors, fmt.Errorf("name must be between 1 and %d characters long", maxRawVolumeNameLength)) } if strings.ContainsFunc(s.MetaName, func(r rune) bool { switch { case r >= 'a' && r <= 'z': return false case r >= 'A' && r <= 'Z': return false case r >= '0' && r <= '9': return false case r == '-': return false default: // invalid symbol return true } }) { validationErrors = errors.Join(validationErrors, errors.New("name can only contain lowercase and uppercase ASCII letters, digits, and hyphens")) } extraWarnings, extraErrors := s.ProvisioningSpec.Validate(true, true) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.EncryptionSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) return warnings, validationErrors } // RawVolumeConfigSignal is a signal for user volume config. func (s *RawVolumeConfigV1Alpha1) RawVolumeConfigSignal() {} // Provisioning implements config.RawVolumeConfig interface. func (s *RawVolumeConfigV1Alpha1) Provisioning() config.VolumeProvisioningConfig { return s.ProvisioningSpec } // Encryption implements config.RawVolumeConfig interface. func (s *RawVolumeConfigV1Alpha1) Encryption() config.EncryptionConfig { if s.EncryptionSpec.EncryptionProvider == block.EncryptionProviderNone { return nil } return s.EncryptionSpec } ================================================ FILE: pkg/machinery/config/types/block/raw_volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestRawVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.RawVolumeConfigV1Alpha1 }{ { name: "disk selector", filename: "rawvolumeconfig_diskselector.yaml", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = "ceph-data" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.transport == "nvme" && !system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("100GiB") return c }, }, { name: "encrypted", filename: "rawvolumeconfig_encrypted.yaml", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = "secret-store" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret", }, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestRawVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.RawVolumeConfigV1Alpha1 expectedErrors string }{ { name: "no name", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name is required\nname must be between 1 and 34 characters long", }, { name: "too long name", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = strings.Repeat("X", 35) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name must be between 1 and 34 characters long", }, { name: "invalid characters in name", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = "some/name" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name can only contain lowercase and uppercase ASCII letters, digits, and hyphens", }, { name: "invalid disk selector", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120`))) return c }, expectedErrors: "disk selector is invalid: ERROR: :1:11: found no matching overload for '_>_' applied to '(uint, int)'\n | disk.size > 120\n | ..........^\nmin size or max size is required", }, { name: "min size greater than max size", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "disk selector is required\nmin size is greater than max size", }, { name: "no encryption provider", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "unsupported encryption provider: none", }, { name: "no encryption keys", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 return c }, expectedErrors: "encryption keys are required", }, { name: "invalid encryption key slots", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 0, }, { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "at least one encryption key type must be specified for slot 0\nduplicate key slot 1", }, { name: "valid", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "valid encrypted", cfg: func(t *testing.T) *block.RawVolumeConfigV1Alpha1 { c := block.NewRawVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/block/size.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "encoding" "strings" "gopkg.in/yaml.v3" ) // Check interfaces. var ( _ encoding.TextMarshaler = Size{} _ encoding.TextUnmarshaler = (*PercentageSize)(nil) _ yaml.IsZeroer = Size{} ) // Size is either a PercentageSize or ByteSize. type Size struct { PercentageSize *PercentageSize ByteSize *ByteSize } // MustSize returns a new Size with the given value. // // It panics if the value is invalid. func MustSize(value string) Size { var s Size if err := s.UnmarshalText([]byte(value)); err != nil { panic(err) } return s } // MustByteSize returns a new Size with the given ByteSize value. // // It panics if the value is invalid. func MustByteSize(value string) ByteSize { var bs ByteSize if err := bs.UnmarshalText([]byte(value)); err != nil { panic(err) } return bs } // Value returns the value. func (s Size) Value() uint64 { if s.ByteSize != nil { return s.ByteSize.Value() } return 0 } // RelativeValue returns the relative value. func (s Size) RelativeValue() (uint64, bool) { if s.PercentageSize != nil { return s.PercentageSize.Value(), true } return 0, false } // MarshalText implements encoding.TextMarshaler. func (s Size) MarshalText() ([]byte, error) { if s.ByteSize != nil { return s.ByteSize.MarshalText() } if s.PercentageSize != nil { return s.PercentageSize.MarshalText() } return nil, nil } // UnmarshalText implements encoding.TextUnmarshaler. func (s *Size) UnmarshalText(text []byte) error { if string(text) == "" { return nil } if strings.Contains(string(text), "%") { var ps PercentageSize if err := ps.UnmarshalText(text); err != nil { return err } s.PercentageSize = &ps } else { var bs ByteSize if err := bs.UnmarshalText(text); err != nil { return err } s.ByteSize = &bs } return nil } // IsZero implements yaml.IsZeroer. func (s Size) IsZero() bool { return (s.PercentageSize == nil || s.PercentageSize.IsZero()) && (s.ByteSize == nil || s.ByteSize.IsZero()) } // IsRelative returns if the Size is a relative size. func (s Size) IsRelative() bool { return (s.PercentageSize != nil && !s.PercentageSize.IsZero()) } // IsNegative returns true if the value is negative. func (s Size) IsNegative() bool { if s.ByteSize != nil { return s.ByteSize.IsNegative() } if s.PercentageSize != nil { return s.PercentageSize.IsNegative() } return false } ================================================ FILE: pkg/machinery/config/types/block/size_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/types/block" ) func TestSizeUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { in string want uint64 negative bool }{ {in: "", want: 0}, {in: "100%", want: 100}, {in: "33.4%", want: 33}, {in: "33.4124%", want: 33}, {in: "1048576", want: 1048576}, {in: "2.5GiB", want: 2684354560}, {in: "2.5GB", want: 2500000000}, {in: "2.5G", want: 2500000000}, {in: "1MiB", want: 1048576}, {in: "-100%", want: 100, negative: true}, {in: "-33.4%", want: 33, negative: true}, {in: "-33.4124%", want: 33, negative: true}, {in: "-1048576", want: 1048576, negative: true}, {in: "-2.5GiB", want: 2684354560, negative: true}, {in: "-2.5GB", want: 2500000000, negative: true}, {in: "-2.5G", want: 2500000000, negative: true}, {in: "-1MiB", want: 1048576, negative: true}, } { t.Run(test.in, func(t *testing.T) { t.Parallel() var s block.Size require.NoError(t, s.UnmarshalText([]byte(test.in))) if strings.Contains(test.in, "%") { assert.Zero(t, s.Value()) val, ok := s.RelativeValue() assert.True(t, ok) assert.Equal(t, test.want, val) assert.Equal(t, test.negative, s.IsNegative()) } else { assert.Equal(t, test.want, s.Value()) val, ok := s.RelativeValue() assert.False(t, ok) assert.Zero(t, val) assert.Equal(t, test.negative, s.IsNegative()) } out, err := s.MarshalText() require.NoError(t, err) assert.Equal(t, test.in, string(out)) }) } } ================================================ FILE: pkg/machinery/config/types/block/swap_volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // SwapVolumeConfigKind is a config document kind. const SwapVolumeConfigKind = "SwapVolumeConfig" func init() { registry.Register(SwapVolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &SwapVolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.SwapVolumeConfig = &SwapVolumeConfigV1Alpha1{} _ config.NamedDocument = &SwapVolumeConfigV1Alpha1{} _ config.Validator = &SwapVolumeConfigV1Alpha1{} ) const maxSwapVolumeNameLength = constants.PartitionLabelLength - len(constants.SwapVolumePrefix) // SwapVolumeConfigV1Alpha1 is a disk swap volume configuration document. // // description: | // Swap volume is automatically allocated as a partition on the specified disk // and activated as swap, removing a swap volume deactivates swap. // The partition label is automatically generated as `s-`. // examples: // - value: exampleSwapVolumeConfigV1Alpha1() // alias: SwapVolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/SwapVolumeConfig type SwapVolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the volume. // // Name might be between 1 and 34 characters long and can only contain: // lowercase and uppercase ASCII letters, digits, and hyphens. MetaName string `yaml:"name"` // description: | // The provisioning describes how the volume is provisioned. ProvisioningSpec ProvisioningSpec `yaml:"provisioning,omitempty"` // description: | // The encryption describes how the volume is encrypted. EncryptionSpec EncryptionSpec `yaml:"encryption,omitempty"` } // NewSwapVolumeConfigV1Alpha1 creates a new user volume config document. func NewSwapVolumeConfigV1Alpha1() *SwapVolumeConfigV1Alpha1 { return &SwapVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: SwapVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleSwapVolumeConfigV1Alpha1() *SwapVolumeConfigV1Alpha1 { cfg := NewSwapVolumeConfigV1Alpha1() cfg.MetaName = "swap1" cfg.ProvisioningSpec = ProvisioningSpec{ DiskSelectorSpec: DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, ProvisioningMinSize: MustByteSize("3GiB"), ProvisioningMaxSize: MustSize("4GiB"), } cfg.EncryptionSpec = EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []EncryptionKey{ { KeySlot: 0, KeyStatic: &EncryptionKeyStatic{ KeyData: "swapsecret", }, }, }, } return cfg } // Name implements config.NamedDocument interface. func (s *SwapVolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *SwapVolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo,dupl func (s *SwapVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string //nolint:prealloc validationErrors error ) if s.MetaName == "" { validationErrors = errors.Join(validationErrors, errors.New("name is required")) } if len(s.MetaName) < 1 || len(s.MetaName) > maxSwapVolumeNameLength { validationErrors = errors.Join(validationErrors, fmt.Errorf("name must be between 1 and %d characters long", maxSwapVolumeNameLength)) } if strings.ContainsFunc(s.MetaName, func(r rune) bool { switch { case r >= 'a' && r <= 'z': return false case r >= 'A' && r <= 'Z': return false case r >= '0' && r <= '9': return false case r == '-': return false default: // invalid symbol return true } }) { validationErrors = errors.Join(validationErrors, errors.New("name can only contain lowercase and uppercase ASCII letters, digits, and hyphens")) } extraWarnings, extraErrors := s.ProvisioningSpec.Validate(true, true) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.EncryptionSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) return warnings, validationErrors } // SwapVolumeConfigSignal is a signal for swap volume config. func (s *SwapVolumeConfigV1Alpha1) SwapVolumeConfigSignal() {} // Provisioning implements config.SwapVolumeConfig interface. func (s *SwapVolumeConfigV1Alpha1) Provisioning() config.VolumeProvisioningConfig { return s.ProvisioningSpec } // Encryption implements config.SwapVolumeConfig interface. func (s *SwapVolumeConfigV1Alpha1) Encryption() config.EncryptionConfig { if s.EncryptionSpec.EncryptionProvider == block.EncryptionProviderNone { return nil } return s.EncryptionSpec } ================================================ FILE: pkg/machinery/config/types/block/swap_volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestSwapVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 }{ { name: "disk selector", filename: "swapvolumeconfig_diskselector.yaml", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = "big" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.transport == "nvme" && !system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("100GiB") return c }, }, { name: "encrypted", filename: "swapvolumeconfig_encrypted.yaml", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = "secret-swap" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret", }, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestSwapVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 expectedErrors string }{ { name: "no name", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name is required\nname must be between 1 and 34 characters long", }, { name: "too long name", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = strings.Repeat("X", 35) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name must be between 1 and 34 characters long", }, { name: "invalid characters in name", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = "some/name" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name can only contain lowercase and uppercase ASCII letters, digits, and hyphens", }, { name: "invalid disk selector", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120`))) return c }, expectedErrors: "disk selector is invalid: ERROR: :1:11: found no matching overload for '_>_' applied to '(uint, int)'\n | disk.size > 120\n | ..........^\nmin size or max size is required", }, { name: "min size greater than max size", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "disk selector is required\nmin size is greater than max size", }, { name: "no encryption provider", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "unsupported encryption provider: none", }, { name: "no encryption keys", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 return c }, expectedErrors: "encryption keys are required", }, { name: "invalid encryption key slots", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 0, }, { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "at least one encryption key type must be specified for slot 0\nduplicate key slot 1", }, { name: "valid", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "valid encrypted", cfg: func(t *testing.T) *block.SwapVolumeConfigV1Alpha1 { c := block.NewSwapVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/block/testdata/existingvolumeconfig_selector.yaml ================================================ apiVersion: v1alpha1 kind: ExistingVolumeConfig name: my-lovely-volume discovery: volumeSelector: match: volume.partition_label == "MY-DATA" ================================================ FILE: pkg/machinery/config/types/block/testdata/externalvolumeconfig_basicvirtiofs.yaml ================================================ apiVersion: v1alpha1 kind: ExternalVolumeConfig name: my-virtiofs-volume filesystemType: virtiofs mount: virtiofs: tag: Data ================================================ FILE: pkg/machinery/config/types/block/testdata/rawvolumeconfig_diskselector.yaml ================================================ apiVersion: v1alpha1 kind: RawVolumeConfig name: ceph-data provisioning: diskSelector: match: disk.transport == "nvme" && !system_disk minSize: 10GiB maxSize: 100GiB ================================================ FILE: pkg/machinery/config/types/block/testdata/rawvolumeconfig_encrypted.yaml ================================================ apiVersion: v1alpha1 kind: RawVolumeConfig name: secret-store provisioning: diskSelector: match: '!system_disk' minSize: 10GiB encryption: provider: luks2 keys: - slot: 0 tpm: {} - slot: 1 static: passphrase: topsecret cipher: aes-xts-plain64 ================================================ FILE: pkg/machinery/config/types/block/testdata/swapvolumeconfig_diskselector.yaml ================================================ apiVersion: v1alpha1 kind: SwapVolumeConfig name: big provisioning: diskSelector: match: disk.transport == "nvme" && !system_disk minSize: 10GiB maxSize: 100GiB ================================================ FILE: pkg/machinery/config/types/block/testdata/swapvolumeconfig_encrypted.yaml ================================================ apiVersion: v1alpha1 kind: SwapVolumeConfig name: secret-swap provisioning: diskSelector: match: '!system_disk' minSize: 10GiB encryption: provider: luks2 keys: - slot: 0 tpm: {} - slot: 1 static: passphrase: topsecret cipher: aes-xts-plain64 ================================================ FILE: pkg/machinery/config/types/block/testdata/uservolumeconfig_diskselector.yaml ================================================ apiVersion: v1alpha1 kind: UserVolumeConfig name: ceph-data provisioning: diskSelector: match: disk.transport == "nvme" && !system_disk minSize: 10GiB maxSize: 100GiB filesystem: type: xfs ================================================ FILE: pkg/machinery/config/types/block/testdata/uservolumeconfig_encrypted.yaml ================================================ apiVersion: v1alpha1 kind: UserVolumeConfig name: secret-store provisioning: diskSelector: match: '!system_disk' minSize: 10GiB encryption: provider: luks2 keys: - slot: 0 tpm: {} - slot: 1 static: passphrase: topsecret cipher: aes-xts-plain64 ================================================ FILE: pkg/machinery/config/types/block/testdata/uservolumeconfig_prjquota.yaml ================================================ apiVersion: v1alpha1 kind: UserVolumeConfig name: secret-store provisioning: diskSelector: match: '!system_disk' minSize: 10GiB filesystem: type: xfs projectQuotaSupport: true ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_diskselector.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL provisioning: diskSelector: match: disk.transport == "nvme" && !system_disk ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_empty.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_maxsize.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL provisioning: minSize: 10GiB maxSize: 2.5TiB ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_negativemaxsize.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL provisioning: minSize: 10GiB maxSize: -10GiB ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_state.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: STATE encryption: provider: luks2 keys: - slot: 0 tpm: {} - slot: 1 static: passphrase: topsecret ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_tpm_encryption_no_options.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: STATE encryption: provider: luks2 keys: - tpm: {} ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_tpm_encryption_with_pcr_settings.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: STATE encryption: provider: luks2 keys: - tpm: options: pcrs: [0,7] ================================================ FILE: pkg/machinery/config/types/block/testdata/volumeconfig_tpm_encryption_with_pcrs_disabled.yaml ================================================ apiVersion: v1alpha1 kind: VolumeConfig name: STATE encryption: provider: luks2 keys: - tpm: options: pcrs: [] ================================================ FILE: pkg/machinery/config/types/block/testdata/zswapconfig_full.yaml ================================================ apiVersion: v1alpha1 kind: ZswapConfig maxPoolPercent: 50 shrinkerEnabled: true ================================================ FILE: pkg/machinery/config/types/block/testdata/zswapconfig_min.yaml ================================================ apiVersion: v1alpha1 kind: ZswapConfig ================================================ FILE: pkg/machinery/config/types/block/user_volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // UserVolumeConfigKind is a config document kind. const UserVolumeConfigKind = "UserVolumeConfig" func init() { registry.Register(UserVolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &UserVolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.UserVolumeConfig = &UserVolumeConfigV1Alpha1{} _ config.ConflictingDocument = &UserVolumeConfigV1Alpha1{} _ config.NamedDocument = &UserVolumeConfigV1Alpha1{} _ config.Validator = &UserVolumeConfigV1Alpha1{} ) const maxUserVolumeNameLength = constants.PartitionLabelLength - len(constants.UserVolumePrefix) // VolumeType is an alias for block.VolumeType. type VolumeType = block.VolumeType // UserVolumeConfigV1Alpha1 is a user volume configuration document. // // description: | // User volume is automatically allocated as a partition on the specified disk // and mounted under `/var/mnt/`. // The partition label is automatically generated as `u-`. // examples: // - value: exampleUserVolumeConfigV1Alpha1Directory() // - value: exampleUserVolumeConfigV1Alpha1Disk() // - value: exampleUserVolumeConfigV1Alpha1Partition() // alias: UserVolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/UserVolumeConfig type UserVolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the volume. // // Name might be between 1 and 34 characters long and can only contain: // lowercase and uppercase ASCII letters, digits, and hyphens. MetaName string `yaml:"name"` // description: | // Volume type. // values: // - directory // - disk // - partition // schema: // type: string VolumeType *VolumeType `yaml:"volumeType,omitempty"` // description: | // The provisioning describes how the volume is provisioned. ProvisioningSpec ProvisioningSpec `yaml:"provisioning,omitempty"` // description: | // The filesystem describes how the volume is formatted. FilesystemSpec FilesystemSpec `yaml:"filesystem,omitempty"` // description: | // The encryption describes how the volume is encrypted. EncryptionSpec EncryptionSpec `yaml:"encryption,omitempty"` // description: | // The mount describes additional mount options. MountSpec UserMountSpec `yaml:"mount,omitempty"` } // UserMountSpec describes how the volume is mounted. type UserMountSpec struct { // description: | // If true, disable file access time updates. MountDisableAccessTime *bool `yaml:"disableAccessTime,omitempty"` // description: | // Enable secure mount options (nosuid, nodev). // // Defaults to true for better security. MountSecure *bool `yaml:"secure,omitempty"` } // NewUserVolumeConfigV1Alpha1 creates a new user volume config document. func NewUserVolumeConfigV1Alpha1() *UserVolumeConfigV1Alpha1 { return &UserVolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: UserVolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } const userVolumeName = "local-data" func exampleUserVolumeConfigV1Alpha1Partition() *UserVolumeConfigV1Alpha1 { cfg := NewUserVolumeConfigV1Alpha1() cfg.MetaName = userVolumeName cfg.VolumeType = new(block.VolumeTypePartition) cfg.ProvisioningSpec = ProvisioningSpec{ DiskSelectorSpec: DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, ProvisioningMaxSize: MustSize("50GiB"), } cfg.FilesystemSpec = FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, } cfg.EncryptionSpec = EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []EncryptionKey{ { KeySlot: 0, KeyTPM: &EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &EncryptionKeyStatic{ KeyData: "topsecret", }, }, }, } return cfg } func exampleUserVolumeConfigV1Alpha1Directory() *UserVolumeConfigV1Alpha1 { cfg := NewUserVolumeConfigV1Alpha1() cfg.MetaName = userVolumeName cfg.VolumeType = new(block.VolumeTypeDirectory) return cfg } func exampleUserVolumeConfigV1Alpha1Disk() *UserVolumeConfigV1Alpha1 { cfg := NewUserVolumeConfigV1Alpha1() cfg.MetaName = userVolumeName cfg.VolumeType = new(block.VolumeTypeDisk) cfg.ProvisioningSpec = ProvisioningSpec{ DiskSelectorSpec: DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, } cfg.FilesystemSpec = FilesystemSpec{ FilesystemType: block.FilesystemTypeXFS, } cfg.EncryptionSpec = EncryptionSpec{ EncryptionProvider: block.EncryptionProviderLUKS2, EncryptionKeys: []EncryptionKey{ { KeySlot: 0, KeyTPM: &EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &EncryptionKeyStatic{ KeyData: "topsecret", }, }, }, } return cfg } // Name implements config.NamedDocument interface. func (s *UserVolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *UserVolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *UserVolumeConfigV1Alpha1) ConflictsWithKinds() []string { return []string{ExistingVolumeConfigKind} } // Validate implements config.Validator interface. // //nolint:gocyclo,cyclop func (s *UserVolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string validationErrors error ) if s.MetaName == "" { validationErrors = errors.Join(validationErrors, errors.New("name is required")) } if len(s.MetaName) < 1 || len(s.MetaName) > maxUserVolumeNameLength { validationErrors = errors.Join(validationErrors, fmt.Errorf("name must be between 1 and %d characters long", maxUserVolumeNameLength)) } if strings.ContainsFunc(s.MetaName, func(r rune) bool { switch { case r >= 'a' && r <= 'z': return false case r >= 'A' && r <= 'Z': return false case r >= '0' && r <= '9': return false case r == '-': return false default: // invalid symbol return true } }) { validationErrors = errors.Join(validationErrors, errors.New("name can only contain lowercase and uppercase ASCII letters, digits, and hyphens")) } vtype := block.VolumeTypePartition if s.VolumeType != nil { vtype = *s.VolumeType } switch vtype { case block.VolumeTypeDirectory: if !s.ProvisioningSpec.IsZero() { validationErrors = errors.Join(validationErrors, errors.New("provisioning spec is invalid for volumeType directory")) } if !s.EncryptionSpec.IsZero() { validationErrors = errors.Join(validationErrors, errors.New("encryption spec is invalid for volumeType directory")) } if !s.FilesystemSpec.IsZero() { validationErrors = errors.Join(validationErrors, errors.New("filesystem spec is invalid for volumeType directory")) } if !s.MountSpec.IsZero() { validationErrors = errors.Join(validationErrors, errors.New("mount spec is invalid for volumeType directory")) } case block.VolumeTypeDisk: extraWarnings, extraErrors := s.ProvisioningSpec.Validate(true, false) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.FilesystemSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.EncryptionSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) case block.VolumeTypePartition: extraWarnings, extraErrors := s.ProvisioningSpec.Validate(true, true) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.FilesystemSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.EncryptionSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) case block.VolumeTypeTmpfs, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal: fallthrough default: validationErrors = errors.Join(validationErrors, fmt.Errorf("unsupported volume type %q", vtype)) } return warnings, validationErrors } // UserVolumeConfigSignal is a signal for user volume config. func (s *UserVolumeConfigV1Alpha1) UserVolumeConfigSignal() {} // Type implements config.UserVolumeConfig interface. func (s *UserVolumeConfigV1Alpha1) Type() optional.Optional[VolumeType] { if s.VolumeType == nil { return optional.None[VolumeType]() } return optional.Some(*s.VolumeType) } // Provisioning implements config.UserVolumeConfig interface. func (s *UserVolumeConfigV1Alpha1) Provisioning() config.VolumeProvisioningConfig { return s.ProvisioningSpec } // Filesystem implements config.UserVolumeConfig interface. func (s *UserVolumeConfigV1Alpha1) Filesystem() config.FilesystemConfig { return s.FilesystemSpec } // Encryption implements config.UserVolumeConfig interface. func (s *UserVolumeConfigV1Alpha1) Encryption() config.EncryptionConfig { if s.EncryptionSpec.EncryptionProvider == block.EncryptionProviderNone { return nil } return s.EncryptionSpec } // Mount implements config.UserVolumeConfig interface. func (s *UserVolumeConfigV1Alpha1) Mount() config.UserVolumeMountConfig { return s.MountSpec } // FilesystemSpec configures the filesystem for the volume. type FilesystemSpec struct { // description: | // Filesystem type. Default is `xfs`. // values: // - ext4 // - xfs FilesystemType block.FilesystemType `yaml:"type,omitempty"` // description: | // Enables project quota support, valid only for 'xfs' filesystem. // // Note: changing this value might require a full remount of the filesystem. ProjectQuotaSupportConfig *bool `yaml:"projectQuotaSupport,omitempty"` } // IsZero checks if the filesystem spec is zero. func (s FilesystemSpec) IsZero() bool { return s.FilesystemType == block.FilesystemTypeNone && s.ProjectQuotaSupportConfig == nil } // Type implements config.FilesystemConfig interface. func (s FilesystemSpec) Type() block.FilesystemType { if s.FilesystemType == block.FilesystemTypeNone { return block.FilesystemTypeXFS } return s.FilesystemType } // ProjectQuotaSupport implements config.FilesysteemConfig interface. func (s FilesystemSpec) ProjectQuotaSupport() bool { return pointer.SafeDeref(s.ProjectQuotaSupportConfig) } // Validate implements config.Validator interface. func (s FilesystemSpec) Validate() ([]string, error) { switch s.FilesystemType { //nolint:exhaustive case block.FilesystemTypeNone: case block.FilesystemTypeXFS: case block.FilesystemTypeEXT4: default: return nil, fmt.Errorf("unsupported filesystem type: %s", s.FilesystemType) } if pointer.SafeDeref(s.ProjectQuotaSupportConfig) && s.Type() != block.FilesystemTypeXFS { return nil, fmt.Errorf("project quota support is only available for xfs filesystem") } return nil, nil } // IsZero checks if the mount spec is zero. func (s UserMountSpec) IsZero() bool { return s.MountDisableAccessTime == nil && s.MountSecure == nil } // DisableAccessTime implements config.UserVolumeMountConfig interface. func (s UserMountSpec) DisableAccessTime() bool { return pointer.SafeDeref(s.MountDisableAccessTime) } // Secure implements config.UserVolumeMountConfig interface. func (s UserMountSpec) Secure() bool { if s.MountSecure == nil { return true } return *s.MountSecure } ================================================ FILE: pkg/machinery/config/types/block/user_volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestUserVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.UserVolumeConfigV1Alpha1 }{ { name: "disk selector", filename: "uservolumeconfig_diskselector.yaml", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = "ceph-data" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.transport == "nvme" && !system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("100GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeXFS return c }, }, { name: "encrypted", filename: "uservolumeconfig_encrypted.yaml", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = "secret-store" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret", }, }, } return c }, }, { name: "prjquota", filename: "uservolumeconfig_prjquota.yaml", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = "secret-store" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`!system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeXFS c.FilesystemSpec.ProjectQuotaSupportConfig = new(true) return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestUserVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.UserVolumeConfigV1Alpha1 expectedErrors string }{ { name: "no name", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name is required\nname must be between 1 and 34 characters long", }, { name: "too long name", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = strings.Repeat("X", 35) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name must be between 1 and 34 characters long", }, { name: "invalid characters in name", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = "some/name" require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 1u`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") return c }, expectedErrors: "name can only contain lowercase and uppercase ASCII letters, digits, and hyphens", }, { name: "invalid disk selector", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120`))) return c }, expectedErrors: "disk selector is invalid: ERROR: :1:11: found no matching overload for '_>_' applied to '(uint, int)'\n | disk.size > 120\n | ..........^\nmin size or max size is required", }, { name: "min size greater than max size", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "disk selector is required\nmin size is greater than max size", }, { name: "unsupported filesystem type", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeISO9660 return c }, expectedErrors: "unsupported filesystem type: iso9660", }, { name: "no encryption provider", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "unsupported encryption provider: none", }, { name: "no encryption keys", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 return c }, expectedErrors: "encryption keys are required", }, { name: "invalid encryption key slots", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 0, }, { KeySlot: 1, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "at least one encryption key type must be specified for slot 0\nduplicate key slot 1", }, { name: "prjquota not supported", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4 c.FilesystemSpec.ProjectQuotaSupportConfig = new(true) return c }, expectedErrors: "project quota support is only available for xfs filesystem", }, { name: "provisioning spec for directory", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDirectory) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, expectedErrors: "provisioning spec is invalid for volumeType directory", }, { name: "encryption spec for directory", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDirectory) c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "encryption spec is invalid for volumeType directory", }, { name: "size for disk", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDisk) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4 return c }, expectedErrors: "min size, max size and grow are not supported", }, { name: "filesystem spec for directory", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDirectory) c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeVFAT return c }, expectedErrors: "filesystem spec is invalid for volumeType directory", }, { name: "invalid volumeType", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeTmpfs) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, expectedErrors: "unsupported volume type \"tmpfs\"", }, { name: "valid", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4 return c }, }, { name: "valid partition", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypePartition) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4 return c }, }, { name: "valid directory", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDirectory) return c }, }, { name: "valid disk", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.VolumeType = new(blockres.VolumeTypeDisk) require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.FilesystemSpec.FilesystemType = blockres.FilesystemTypeEXT4 return c }, }, { name: "valid encrypted", cfg: func(t *testing.T) *block.UserVolumeConfigV1Alpha1 { c := block.NewUserVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`system_disk`))) c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionCipher = "aes-xts-plain64" c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/block/volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "slices" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // VolumeConfigKind is a config document kind. const VolumeConfigKind = "VolumeConfig" func init() { registry.Register(VolumeConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &VolumeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.VolumeConfig = &VolumeConfigV1Alpha1{} _ config.NamedDocument = &VolumeConfigV1Alpha1{} _ config.Validator = &VolumeConfigV1Alpha1{} _ container.V1Alpha1ConflictValidator = &VolumeConfigV1Alpha1{} ) // VolumeConfigV1Alpha1 is a system volume configuration document. // // description: | // Note: at the moment, only `STATE`, `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported. // examples: // - value: exampleVolumeConfigEphemeralV1Alpha1() // alias: VolumeConfig // schemaRoot: true // schemaMeta: v1alpha1/VolumeConfig type VolumeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the volume. MetaName string `yaml:"name"` // description: | // The provisioning describes how the volume is provisioned. ProvisioningSpec ProvisioningSpec `yaml:"provisioning,omitempty"` // description: | // The encryption describes how the volume is encrypted. EncryptionSpec EncryptionSpec `yaml:"encryption,omitempty"` // description: | // The mount describes additional mount options. MountSpec MountSpec `yaml:"mount,omitempty"` } // MountSpec describes how the volume is mounted. type MountSpec struct { // description: | // Enable secure mount options (nosuid, nodev). // // Defaults to true for better security. MountSecure *bool `yaml:"secure,omitempty"` } // ProvisioningSpec describes how the volume is provisioned. type ProvisioningSpec struct { // description: | // The disk selector expression. DiskSelectorSpec DiskSelector `yaml:"diskSelector,omitempty"` // description: | // Should the volume grow to the size of the disk (if possible). ProvisioningGrow *bool `yaml:"grow,omitempty"` // description: | // The minimum size of the volume. // // Size is specified in bytes, but can be expressed in human readable format, e.g. 100MB. // examples: // - value: > // "2.5GiB" // schema: // type: string ProvisioningMinSize ByteSize `yaml:"minSize,omitempty"` // description: | // The maximum size of the volume, if not specified the volume can grow to the size of the // disk. // // Size is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB. // examples: // - value: > // "50GiB" // - value: > // "80%" // schema: // type: string ProvisioningMaxSize Size `yaml:"maxSize,omitempty"` } // MaxSizeNegative returns true if the maximum size is negative. func (p ProvisioningSpec) MaxSizeNegative() bool { return p.ProvisioningMaxSize.IsNegative() } // DiskSelector selects a disk for the volume. type DiskSelector struct { // description: | // The Common Expression Language (CEL) expression to match the disk. // schema: // type: string // examples: // - value: > // exampleDiskSelector1() // name: match disks with size between 120GB and 1TB // - value: > // exampleDiskSelector2() // name: match SATA disks that are not rotational and not system disks Match cel.Expression `yaml:"match,omitempty"` } // NewVolumeConfigV1Alpha1 creates a new volume config document. func NewVolumeConfigV1Alpha1() *VolumeConfigV1Alpha1 { return &VolumeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: VolumeConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleVolumeConfigEphemeralV1Alpha1() *VolumeConfigV1Alpha1 { cfg := NewVolumeConfigV1Alpha1() cfg.MetaName = constants.EphemeralPartitionLabel cfg.ProvisioningSpec = ProvisioningSpec{ DiskSelectorSpec: DiskSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "nvme"`, celenv.DiskLocator())), }, ProvisioningMaxSize: MustSize("50GiB"), } return cfg } func exampleDiskSelector1() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`disk.size > 120u * GB && disk.size < 1u * TB`, celenv.DiskLocator())) } func exampleDiskSelector2() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`disk.transport == "sata" && !disk.rotational && !system_disk`, celenv.DiskLocator())) } // Name implements config.NamedDocument interface. func (s *VolumeConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *VolumeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. func (s *VolumeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { allowedVolumes := []string{ constants.StatePartitionLabel, constants.EphemeralPartitionLabel, constants.ImageCachePartitionLabel, } if slices.Index(allowedVolumes, s.MetaName) == -1 { return nil, fmt.Errorf("only %q volumes are supported", allowedVolumes) } var ( warnings []string //nolint:prealloc validationErrors error ) if s.MetaName == constants.StatePartitionLabel { // no provisioning config is allowed for the state partition. if !s.ProvisioningSpec.IsZero() { validationErrors = errors.Join(validationErrors, fmt.Errorf("provisioning config is not allowed for the %q volume", s.MetaName)) } for _, key := range s.EncryptionSpec.EncryptionKeys { if pointer.SafeDeref(key.KeyLockToSTATE) { // state-locked keys are not allowed validationErrors = errors.Join(validationErrors, fmt.Errorf("state-locked key is not allowed for the %q volume", s.MetaName)) } } } extraWarnings, extraErrors := s.ProvisioningSpec.Validate(false, true) warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) extraWarnings, extraErrors = s.EncryptionSpec.Validate() warnings = append(warnings, extraWarnings...) validationErrors = errors.Join(validationErrors, extraErrors) return warnings, validationErrors } // Provisioning implements config.VolumeConfig interface. func (s *VolumeConfigV1Alpha1) Provisioning() config.VolumeProvisioningConfig { return s.ProvisioningSpec } // Encryption implements config.VolumeConfig interface. func (s *VolumeConfigV1Alpha1) Encryption() config.EncryptionConfig { if s.EncryptionSpec.EncryptionProvider == block.EncryptionProviderNone { return nil } return s.EncryptionSpec } // Mount implements config.VolumeConfig interface. func (s *VolumeConfigV1Alpha1) Mount() config.VolumeMountConfig { return s.MountSpec } // Validate the provisioning spec. // //nolint:gocyclo func (p ProvisioningSpec) Validate(required bool, sizeSupported bool) ([]string, error) { var validationErrors error if !p.DiskSelectorSpec.Match.IsZero() { if err := p.DiskSelectorSpec.Match.ParseBool(celenv.DiskLocator()); err != nil { validationErrors = errors.Join(validationErrors, fmt.Errorf("disk selector is invalid: %w", err)) } } else if required { validationErrors = errors.Join(validationErrors, errors.New("disk selector is required")) } if sizeSupported { if !p.ProvisioningMinSize.IsZero() && !p.ProvisioningMaxSize.IsZero() && !p.ProvisioningMaxSize.IsRelative() && !p.ProvisioningMaxSize.IsNegative() { if p.ProvisioningMinSize.Value() > p.ProvisioningMaxSize.Value() { validationErrors = errors.Join(validationErrors, errors.New("min size is greater than max size")) } } else if required && p.ProvisioningMinSize.IsZero() && p.ProvisioningMaxSize.IsZero() { validationErrors = errors.Join(validationErrors, errors.New("min size or max size is required")) } if p.ProvisioningMinSize.IsNegative() { validationErrors = errors.Join(validationErrors, errors.New("min size cannot be negative")) } } else if !p.ProvisioningMinSize.IsZero() || !p.ProvisioningMaxSize.IsZero() || p.Grow().IsPresent() { validationErrors = errors.Join(validationErrors, errors.New("min size, max size and grow are not supported")) } return nil, validationErrors } // V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface. func (s *VolumeConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Config *v1alpha1.Config) error { if !slices.Contains([]string{constants.StatePartitionLabel, constants.EphemeralPartitionLabel}, s.MetaName) { // only STATE and EPHEMERAL volumes can conflict with legacy config. return nil } if s.Encryption() == nil { // no encryption configured, no conflict. return nil } legacy := v1alpha1Config.Machine().SystemDiskEncryption().Get(s.MetaName) if legacy != nil { return fmt.Errorf("system disk encryption for %q is configured in both v1alpha1.Config and VolumeConfig", s.MetaName) } return nil } // IsZero checks if the provisioning spec is zero. func (p ProvisioningSpec) IsZero() bool { return p.ProvisioningGrow == nil && p.ProvisioningMaxSize.IsZero() && p.ProvisioningMinSize.IsZero() && p.DiskSelectorSpec.Match.IsZero() } // DiskSelector implements config.VolumeProvisioningConfig interface. func (p ProvisioningSpec) DiskSelector() optional.Optional[cel.Expression] { if p.DiskSelectorSpec.Match.IsZero() { return optional.None[cel.Expression]() } return optional.Some(p.DiskSelectorSpec.Match) } // Grow implements config.VolumeProvisioningConfig interface. func (p ProvisioningSpec) Grow() optional.Optional[bool] { if p.ProvisioningGrow == nil { return optional.None[bool]() } return optional.Some(*p.ProvisioningGrow) } // MinSize implements config.VolumeProvisioningConfig interface. func (p ProvisioningSpec) MinSize() optional.Optional[uint64] { if p.ProvisioningMinSize.IsZero() { return optional.None[uint64]() } return optional.Some(p.ProvisioningMinSize.Value()) } // MaxSize implements config.VolumeProvisioningConfig interface. func (p ProvisioningSpec) MaxSize() optional.Optional[uint64] { if p.ProvisioningMaxSize.IsZero() { return optional.None[uint64]() } return optional.Some(p.ProvisioningMaxSize.Value()) } // RelativeMaxSize implements config.VolumeProvisioningConfig interface. func (p ProvisioningSpec) RelativeMaxSize() optional.Optional[uint64] { if p.ProvisioningMaxSize.IsZero() { return optional.None[uint64]() } val, ok := p.ProvisioningMaxSize.RelativeValue() if !ok { return optional.None[uint64]() } return optional.Some(val) } // Secure implements config.VolumeMountConfig interface. func (s MountSpec) Secure() bool { if s.MountSecure == nil { return true } return *s.MountSecure } ================================================ FILE: pkg/machinery/config/types/block/volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/config/types/block" "github.com/siderolabs/talos/pkg/machinery/constants" blockres "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestVolumeConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.VolumeConfigV1Alpha1 }{ { name: "empty", filename: "volumeconfig_empty.yaml", cfg: func(*testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel return c }, }, { name: "disk selector", filename: "volumeconfig_diskselector.yaml", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.transport == "nvme" && !system_disk`))) return c }, }, { name: "max size", filename: "volumeconfig_maxsize.yaml", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "negative max size", filename: "volumeconfig_negativemaxsize.yaml", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("-10GiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "state", filename: "volumeconfig_state.yaml", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.StatePartitionLabel c.EncryptionSpec.EncryptionProvider = blockres.EncryptionProviderLUKS2 c.EncryptionSpec.EncryptionKeys = []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{}, }, { KeySlot: 1, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret", }, }, } return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) assert.Empty(t, warnings) }) } } func TestVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.VolumeConfigV1Alpha1 expectedErrors string }{ { name: "wrong name", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = "wrong" return c }, expectedErrors: "only [\"STATE\" \"EPHEMERAL\" \"IMAGECACHE\"] volumes are supported", }, { name: "invalid disk selector", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120`))) return c }, expectedErrors: "disk selector is invalid: ERROR: :1:11: found no matching overload for '_>_' applied to '(uint, int)'\n | disk.size > 120\n | ..........^", }, { name: "min size greater than max size", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5TiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "min size is greater than max size", }, { name: "min size negative", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("-10GiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "min size cannot be negative", }, { name: "state provisioning config", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.StatePartitionLabel c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("2.5GiB") c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("10GiB") return c }, expectedErrors: "provisioning config is not allowed for the \"STATE\" volume", }, { name: "state encryption lock to state", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.StatePartitionLabel c.EncryptionSpec = block.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionKeys: []block.EncryptionKey{ { KeySlot: 0, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret", }, }, { KeySlot: 1, KeyStatic: &block.EncryptionKeyStatic{ KeyData: "topsecret2", }, KeyLockToSTATE: new(true), }, }, } return c }, expectedErrors: "state-locked key is not allowed for the \"STATE\" volume", }, { name: "invalid pcrs", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.StatePartitionLabel c.EncryptionSpec = block.EncryptionSpec{ EncryptionProvider: blockres.EncryptionProviderLUKS2, EncryptionKeys: []block.EncryptionKey{ { KeySlot: 0, KeyTPM: &block.EncryptionKeyTPM{ TPMOptions: &block.EncryptionKeyTPMOptions{ PCRs: []int{24, 25}, }, }, }, }, } return c }, expectedErrors: "TPM PCR 24 is out of range (0-23)\nTPM PCR 25 is out of range (0-23)", }, { name: "valid", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("2.5TiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "valid negative", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("-2GiB") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, { name: "valid percentage", cfg: func(t *testing.T) *block.VolumeConfigV1Alpha1 { c := block.NewVolumeConfigV1Alpha1() c.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120u * GiB`))) c.ProvisioningSpec.ProvisioningMaxSize = block.MustSize("90%") c.ProvisioningSpec.ProvisioningMinSize = block.MustByteSize("10GiB") return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } func TestVolumeConfigTPMPCRsDefaults(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string expectedPCRs []int }{ { name: "tpm encryption no options", filename: "volumeconfig_tpm_encryption_no_options.yaml", expectedPCRs: []int{7}, }, { name: "tpm encryption with pcr settings", filename: "volumeconfig_tpm_encryption_with_pcr_settings.yaml", expectedPCRs: []int{0, 7}, }, { name: "tpm encryption with pcrs disabled", filename: "volumeconfig_tpm_encryption_with_pcrs_disabled.yaml", expectedPCRs: []int{}, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() marshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) provider, err := configloader.NewFromBytes(marshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) cfg, ok := docs[0].(*block.VolumeConfigV1Alpha1) require.True(t, ok) assert.Equal(t, test.expectedPCRs, cfg.Encryption().Keys()[0].TPM().PCRs()) assert.Equal(t, []int{constants.UKIPCR}, cfg.Encryption().Keys()[0].TPM().PubKeyPCRs()) }) } } func TestVolumeConfigMerge(t *testing.T) { c1 := block.NewVolumeConfigV1Alpha1() c1.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c1.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 120`))) c2 := block.NewVolumeConfigV1Alpha1() c2.MetaName = constants.EphemeralPartitionLabel require.NoError(t, c2.ProvisioningSpec.DiskSelectorSpec.Match.UnmarshalText([]byte(`disk.size > 150`))) require.NoError(t, c2.ProvisioningSpec.ProvisioningMaxSize.UnmarshalText([]byte("2.5TiB"))) require.NoError(t, merge.Merge(c1, c2)) assert.Equal(t, c1.ProvisioningSpec.DiskSelectorSpec.Match, c2.ProvisioningSpec.DiskSelectorSpec.Match) assert.Equal(t, c1.ProvisioningSpec.ProvisioningMaxSize, c2.ProvisioningSpec.ProvisioningMaxSize) } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/types/block/zswap_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block //docgen:jsonschema import ( "errors" "fmt" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // ZswapConfigKind is a config document kind. const ZswapConfigKind = "ZswapConfig" func init() { registry.Register(ZswapConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &ZswapConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.ZswapConfig = &ZswapConfigV1Alpha1{} _ config.Validator = &ZswapConfigV1Alpha1{} ) // ZswapConfigV1Alpha1 is a zswap (compressed memory) configuration document. // // description: | // When zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk. // The compressed pages are stored in a memory pool, which is used to avoid writing to disk // when the system is under memory pressure. // examples: // - value: exampleZswapConfigV1Alpha1() // alias: ZswapConfig // schemaRoot: true // schemaMeta: v1alpha1/ZswapConfig type ZswapConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // The maximum percent of memory that zswap can use. // This is a percentage of the total system memory. // The value must be between 0 and 100. MaxPoolPercentConfig *int `yaml:"maxPoolPercent,omitempty"` // description: | // Enable the shrinker feature: kernel might move // cold pages from zswap to swap device to free up memory // for other use cases. ShrinkerEnabledConfig *bool `yaml:"shrinkerEnabled,omitempty"` } // NewZswapConfigV1Alpha1 creates a new zswap config document. func NewZswapConfigV1Alpha1() *ZswapConfigV1Alpha1 { return &ZswapConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ZswapConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleZswapConfigV1Alpha1() *ZswapConfigV1Alpha1 { cfg := NewZswapConfigV1Alpha1() cfg.MaxPoolPercentConfig = new(25) cfg.ShrinkerEnabledConfig = new(true) return cfg } // Clone implements config.Document interface. func (s *ZswapConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo func (s *ZswapConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( warnings []string validationErrors error ) if s.MaxPoolPercentConfig != nil { if *s.MaxPoolPercentConfig < 0 || *s.MaxPoolPercentConfig > 100 { validationErrors = errors.Join(validationErrors, fmt.Errorf("maxPoolPercent must be between 0 and 100")) } } return warnings, validationErrors } // ZswapConfigSignal is a signal for zswap config. func (s *ZswapConfigV1Alpha1) ZswapConfigSignal() {} // MaxPoolPercent implements config.ZswapConfig interface. func (s *ZswapConfigV1Alpha1) MaxPoolPercent() int { if s.MaxPoolPercentConfig == nil { return 20 } return pointer.SafeDeref(s.MaxPoolPercentConfig) } // ShrinkerEnabled implements config.ZswapConfig interface. func (s *ZswapConfigV1Alpha1) ShrinkerEnabled() bool { if s.ShrinkerEnabledConfig == nil { return false } return pointer.SafeDeref(s.ShrinkerEnabledConfig) } ================================================ FILE: pkg/machinery/config/types/block/zswap_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl,goconst package block_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/block" ) func TestZswapConfigMarshalUnmarshal(t *testing.T) { t.Parallel() for _, test := range []struct { name string filename string cfg func(t *testing.T) *block.ZswapConfigV1Alpha1 }{ { name: "full config", filename: "zswapconfig_full.yaml", cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 { c := block.NewZswapConfigV1Alpha1() c.MaxPoolPercentConfig = new(50) c.ShrinkerEnabledConfig = new(true) return c }, }, { name: "min config", filename: "zswapconfig_min.yaml", cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 { c := block.NewZswapConfigV1Alpha1() return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) warnings, err := cfg.Validate(validationMode{}) require.NoError(t, err) require.Empty(t, warnings) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) expectedMarshaled, err := os.ReadFile(filepath.Join("testdata", test.filename)) require.NoError(t, err) assert.Equal(t, string(expectedMarshaled), string(marshaled)) provider, err := configloader.NewFromBytes(expectedMarshaled) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, cfg, docs[0]) }) } } func TestZswapVolumeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(t *testing.T) *block.ZswapConfigV1Alpha1 expectedErrors string }{ { name: "minimal", cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 { c := block.NewZswapConfigV1Alpha1() return c }, }, { name: "full", cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 { c := block.NewZswapConfigV1Alpha1() c.MaxPoolPercentConfig = new(50) c.ShrinkerEnabledConfig = new(true) return c }, }, { name: "invalid percent", cfg: func(t *testing.T) *block.ZswapConfigV1Alpha1 { c := block.NewZswapConfigV1Alpha1() c.MaxPoolPercentConfig = new(150) return c }, expectedErrors: "maxPoolPercent must be between 0 and 100", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) _, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } ================================================ FILE: pkg/machinery/config/types/cri/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cri provides container runtime interface related config documents. package cri //go:generate go tool github.com/siderolabs/talos/tools/docgen -output cri_doc.go registry_auth.go registry_mirror.go registry_tls.go //go:generate go tool github.com/siderolabs/deep-copy -type RegistryAuthConfigV1Alpha1 -type RegistryMirrorConfigV1Alpha1 -type RegistryTLSConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/cri/cri_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package cri import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (RegistryAuthConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryAuthConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryAuthConfig configures authentication for a registry endpoint." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryAuthConfig configures authentication for a registry endpoint.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Registry endpoint to apply the authentication configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", Comments: [3]string{"" /* encoder.HeadComment */, "Registry endpoint to apply the authentication configuration to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "username", Type: "string", Note: "", Description: "Username/password authentication.", Comments: [3]string{"" /* encoder.HeadComment */, "Username/password authentication." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "password", Type: "string", Note: "", Description: "Username/password authentication.", Comments: [3]string{"" /* encoder.HeadComment */, "Username/password authentication." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "auth", Type: "string", Note: "", Description: "Raw authentication string.", Comments: [3]string{"" /* encoder.HeadComment */, "Raw authentication string." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "identityToken", Type: "string", Note: "", Description: "Identity token authentication.", Comments: [3]string{"" /* encoder.HeadComment */, "Identity token authentication." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRegistryAuthConfigVAlpha1()) return doc } func (RegistryMirrorConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryMirrorConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryMirrorConfig configures an image registry mirror." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryMirrorConfig configures an image registry mirror.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Registry name to apply the mirror configuration to.\n\nRegistry name is the first segment of image identifier, with 'docker.io'\nbeing default one.\n\nA special name '*' can be used to define mirror configuration\nthat applies to all registries.", Comments: [3]string{"" /* encoder.HeadComment */, "Registry name to apply the mirror configuration to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "endpoints", Type: "[]RegistryEndpoint", Note: "", Description: "List of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is `skipFallback` is not set to true.", Comments: [3]string{"" /* encoder.HeadComment */, "List of mirror endpoints for the registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "skipFallback", Type: "bool", Note: "", Description: "Skip fallback to the original registry if none of the mirrors are available\nor contain the requested image.", Comments: [3]string{"" /* encoder.HeadComment */, "Skip fallback to the original registry if none of the mirrors are available" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRegistryMirrorConfigVAlpha1()) return doc } func (RegistryEndpoint) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryEndpoint", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryEndpoint defines a registry mirror endpoint." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryEndpoint defines a registry mirror endpoint.", AppearsIn: []encoder.Appearance{ { TypeName: "RegistryMirrorConfigV1Alpha1", FieldName: "endpoints", }, }, Fields: []encoder.Doc{ { Name: "url", Type: "URL", Note: "", Description: "The URL of the registry mirror endpoint.", Comments: [3]string{"" /* encoder.HeadComment */, "The URL of the registry mirror endpoint." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "overridePath", Type: "bool", Note: "", Description: "Use endpoint path as supplied, without adding `/v2/` suffix.", Comments: [3]string{"" /* encoder.HeadComment */, "Use endpoint path as supplied, without adding `/v2/` suffix." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", "https://my-registry-mirror.local:5000") return doc } func (RegistryTLSConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryTLSConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryTLSConfig configures TLS for a registry endpoint." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryTLSConfig configures TLS for a registry endpoint.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Registry endpoint to apply the TLS configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", Comments: [3]string{"" /* encoder.HeadComment */, "Registry endpoint to apply the TLS configuration to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "clientIdentity", Type: "CertificateAndKey", Note: "", Description: "Enable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable mutual TLS authentication with the registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ca", Type: "string", Note: "", Description: "CA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.", Comments: [3]string{"" /* encoder.HeadComment */, "CA registry certificate to add the list of trusted certificates." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "insecureSkipVerify", Type: "bool", Note: "", Description: "Skip TLS server certificate verification (not recommended).", Comments: [3]string{"" /* encoder.HeadComment */, "Skip TLS server certificate verification (not recommended)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRegistryTLSConfigVAlpha1()) doc.Fields[2].AddExample("", exampleCertificateAndKey()) return doc } // GetFileDoc returns documentation for the file cri_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "cri", Description: "", Structs: []*encoder.Doc{ RegistryAuthConfigV1Alpha1{}.Doc(), RegistryMirrorConfigV1Alpha1{}.Doc(), RegistryEndpoint{}.Doc(), RegistryTLSConfigV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/cri/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type RegistryAuthConfigV1Alpha1 -type RegistryMirrorConfigV1Alpha1 -type RegistryTLSConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package cri import ( "net/url" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) // DeepCopy generates a deep copy of *RegistryAuthConfigV1Alpha1. func (o *RegistryAuthConfigV1Alpha1) DeepCopy() *RegistryAuthConfigV1Alpha1 { var cp RegistryAuthConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *RegistryMirrorConfigV1Alpha1. func (o *RegistryMirrorConfigV1Alpha1) DeepCopy() *RegistryMirrorConfigV1Alpha1 { var cp RegistryMirrorConfigV1Alpha1 = *o if o.RegistryEndpoints != nil { cp.RegistryEndpoints = make([]RegistryEndpoint, len(o.RegistryEndpoints)) copy(cp.RegistryEndpoints, o.RegistryEndpoints) for i2 := range o.RegistryEndpoints { if o.RegistryEndpoints[i2].EndpointURL.URL != nil { cp.RegistryEndpoints[i2].EndpointURL.URL = new(url.URL) *cp.RegistryEndpoints[i2].EndpointURL.URL = *o.RegistryEndpoints[i2].EndpointURL.URL if o.RegistryEndpoints[i2].EndpointURL.URL.User != nil { cp.RegistryEndpoints[i2].EndpointURL.URL.User = new(url.Userinfo) *cp.RegistryEndpoints[i2].EndpointURL.URL.User = *o.RegistryEndpoints[i2].EndpointURL.URL.User } } if o.RegistryEndpoints[i2].EndpointOverridePath != nil { cp.RegistryEndpoints[i2].EndpointOverridePath = new(bool) *cp.RegistryEndpoints[i2].EndpointOverridePath = *o.RegistryEndpoints[i2].EndpointOverridePath } } } if o.RegistrySkipFallback != nil { cp.RegistrySkipFallback = new(bool) *cp.RegistrySkipFallback = *o.RegistrySkipFallback } return &cp } // DeepCopy generates a deep copy of *RegistryTLSConfigV1Alpha1. func (o *RegistryTLSConfigV1Alpha1) DeepCopy() *RegistryTLSConfigV1Alpha1 { var cp RegistryTLSConfigV1Alpha1 = *o if o.TLSClientIdentity != nil { cp.TLSClientIdentity = new(meta.CertificateAndKey) *cp.TLSClientIdentity = *o.TLSClientIdentity } if o.TLSInsecureSkipVerify != nil { cp.TLSInsecureSkipVerify = new(bool) *cp.TLSInsecureSkipVerify = *o.TLSInsecureSkipVerify } return &cp } ================================================ FILE: pkg/machinery/config/types/cri/registry_auth.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "errors" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) //docgen:jsonschema // RegistryAuthConfig defines the RegistryAuthConfig configuration name. const RegistryAuthConfig = "RegistryAuthConfig" func init() { registry.Register(RegistryAuthConfig, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RegistryAuthConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RegistryAuthConfigDocument = &RegistryAuthConfigV1Alpha1{} _ config.Validator = &RegistryAuthConfigV1Alpha1{} _ config.SecretDocument = &RegistryAuthConfigV1Alpha1{} _ config.NamedDocument = &RegistryAuthConfigV1Alpha1{} ) // RegistryAuthConfigV1Alpha1 configures authentication for a registry endpoint. // // examples: // - value: exampleRegistryAuthConfigVAlpha1() // alias: RegistryAuthConfig // schemaRoot: true // schemaMeta: v1alpha1/RegistryAuthConfig type RegistryAuthConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Registry endpoint to apply the authentication configuration to. // // Registry endpoint is the hostname part of the endpoint URL, // e.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'. // // The authentication configuration will apply to all image pulls for this // registry endpoint, by Talos or any Kubernetes workloads. // schemaRequired: true MetaName string `yaml:"name"` // description: | // Username/password authentication. RegistryUsername string `yaml:"username,omitempty"` // description: | // Username/password authentication. RegistryPassword string `yaml:"password,omitempty"` // description: | // Raw authentication string. RegistryAuth string `yaml:"auth,omitempty"` // description: | // Identity token authentication. RegistryIdentityToken string `yaml:"identityToken,omitempty"` } // NewRegistryAuthConfigV1Alpha1 creates a new RegistryAuthConfig config document. func NewRegistryAuthConfigV1Alpha1(name string) *RegistryAuthConfigV1Alpha1 { return &RegistryAuthConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RegistryAuthConfig, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleRegistryAuthConfigVAlpha1() *RegistryAuthConfigV1Alpha1 { cfg := NewRegistryAuthConfigV1Alpha1("my-private-registry.local:5000") cfg.RegistryUsername = "my-username" cfg.RegistryPassword = "my-password" return cfg } // Clone implements config.Document interface. func (s *RegistryAuthConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.Document interface. func (s *RegistryAuthConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *RegistryAuthConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } hasUsername := s.RegistryUsername != "" hasIdentityToken := s.RegistryIdentityToken != "" hasAuth := s.RegistryAuth != "" if hasUsername && hasIdentityToken { errs = errors.Join(errs, errors.New("only one of username/password or identityToken authentication can be specified")) } if hasAuth && (hasUsername || hasIdentityToken) { errs = errors.Join(errs, errors.New("only one of auth, username/password or identityToken authentication can be specified")) } return warnings, errs } // Username implements config.RegistryAuthConfig interface. func (s *RegistryAuthConfigV1Alpha1) Username() string { return s.RegistryUsername } // Password implements config.RegistryAuthConfig interface. func (s *RegistryAuthConfigV1Alpha1) Password() string { return s.RegistryPassword } // Auth implements config.RegistryAuthConfig interface. func (s *RegistryAuthConfigV1Alpha1) Auth() string { return s.RegistryAuth } // IdentityToken implements config.RegistryAuthConfig interface. func (s *RegistryAuthConfigV1Alpha1) IdentityToken() string { return s.RegistryIdentityToken } // Redact implements config.SecretDocument interface. func (s *RegistryAuthConfigV1Alpha1) Redact(replacement string) { if s.RegistryPassword != "" { s.RegistryPassword = replacement } if s.RegistryAuth != "" { s.RegistryAuth = replacement } if s.RegistryIdentityToken != "" { s.RegistryIdentityToken = replacement } } ================================================ FILE: pkg/machinery/config/types/cri/registry_auth_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) //go:embed testdata/registryauthconfig.yaml var expectedRegistryAuthConfigDocument []byte func TestRegistryAuthConfigMarshalStability(t *testing.T) { t.Parallel() cfg := cri.NewRegistryAuthConfigV1Alpha1("my-private-registry.io") cfg.RegistryUsername = "agent007" cfg.RegistryPassword = "topsecret" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedRegistryAuthConfigDocument, marshaled) } func TestRegistryAuthConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedRegistryAuthConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &cri.RegistryAuthConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: cri.RegistryAuthConfig, }, MetaName: "my-private-registry.io", RegistryUsername: "agent007", RegistryPassword: "topsecret", }, docs[0]) } func TestRegistryAuthConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *cri.RegistryAuthConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *cri.RegistryAuthConfigV1Alpha1 { return cri.NewRegistryAuthConfigV1Alpha1("") }, expectedError: "name must be specified", }, { name: "username and identity token", cfg: func() *cri.RegistryAuthConfigV1Alpha1 { cfg := cri.NewRegistryAuthConfigV1Alpha1("k8s.io") cfg.RegistryUsername = "user" cfg.RegistryIdentityToken = "token" return cfg }, expectedError: "only one of username/password or identityToken authentication can be specified", }, { name: "valid", cfg: func() *cri.RegistryAuthConfigV1Alpha1 { cfg := cri.NewRegistryAuthConfigV1Alpha1("k8s.io") cfg.RegistryUsername = "user" cfg.RegistryPassword = "pass" return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/cri/registry_mirror.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "errors" "fmt" "net/url" "github.com/siderolabs/gen/ensure" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) //docgen:jsonschema // RegistryMirrorConfig defines the RegistryMirrorConfig configuration name. const RegistryMirrorConfig = "RegistryMirrorConfig" func init() { registry.Register(RegistryMirrorConfig, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RegistryMirrorConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RegistryMirrorConfigDocument = &RegistryMirrorConfigV1Alpha1{} _ config.Validator = &RegistryMirrorConfigV1Alpha1{} _ config.NamedDocument = &RegistryMirrorConfigV1Alpha1{} ) // RegistryMirrorConfigV1Alpha1 configures an image registry mirror. // // examples: // - value: exampleRegistryMirrorConfigVAlpha1() // alias: RegistryMirrorConfig // schemaRoot: true // schemaMeta: v1alpha1/RegistryMirrorConfig type RegistryMirrorConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Registry name to apply the mirror configuration to. // // Registry name is the first segment of image identifier, with 'docker.io' // being default one. // // A special name '*' can be used to define mirror configuration // that applies to all registries. // schemaRequired: true MetaName string `yaml:"name"` // description: | // List of mirror endpoints for the registry. // Mirrors will be used in the order they are specified, // falling back to the default registry is `skipFallback` is not set to true. RegistryEndpoints []RegistryEndpoint `yaml:"endpoints,omitempty"` // description: | // Skip fallback to the original registry if none of the mirrors are available // or contain the requested image. RegistrySkipFallback *bool `yaml:"skipFallback,omitempty"` } // RegistryEndpoint defines a registry mirror endpoint. type RegistryEndpoint struct { // description: | // The URL of the registry mirror endpoint. // schemaRequired: true // examples: // - value: > // "https://my-registry-mirror.local:5000" // schema: // type: string // pattern: "^(http|https)://" EndpointURL meta.URL `yaml:"url"` // description: | // Use endpoint path as supplied, without adding `/v2/` suffix. EndpointOverridePath *bool `yaml:"overridePath,omitempty"` } // NewRegistryMirrorConfigV1Alpha1 creates a new RegistryMirrorConfig config document. func NewRegistryMirrorConfigV1Alpha1(name string) *RegistryMirrorConfigV1Alpha1 { return &RegistryMirrorConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RegistryMirrorConfig, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleRegistryMirrorConfigVAlpha1() *RegistryMirrorConfigV1Alpha1 { cfg := NewRegistryMirrorConfigV1Alpha1("registry.k8s.io") cfg.RegistrySkipFallback = new(true) cfg.RegistryEndpoints = []RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://my-private-registry.local:5000"))}, }, { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("http://my-harbor/v2/registry-k8s.io/"))}, EndpointOverridePath: new(true), }, } return cfg } // Clone implements config.Document interface. func (s *RegistryMirrorConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.Document interface. func (s *RegistryMirrorConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *RegistryMirrorConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } for idx, ep := range s.RegistryEndpoints { if ep.EndpointURL.URL == nil { errs = errors.Join(errs, fmt.Errorf("endpoints[%d].url must be specified", idx)) } else { switch ep.EndpointURL.URL.Scheme { case "http", "https": default: errs = errors.Join(errs, fmt.Errorf("endpoints[%d].url has unsupported scheme: %q", idx, ep.EndpointURL.URL.Scheme)) } } } return warnings, errs } // Endpoints implements RegistryMirrorConfig interface. func (s *RegistryMirrorConfigV1Alpha1) Endpoints() []config.RegistryEndpointConfig { return xslices.Map(s.RegistryEndpoints, func(ep RegistryEndpoint) config.RegistryEndpointConfig { return ep }) } // SkipFallback implements RegistryMirrorConfig interface. func (s *RegistryMirrorConfigV1Alpha1) SkipFallback() bool { return pointer.SafeDeref(s.RegistrySkipFallback) } // Endpoint implements RegistryEndpointConfig interface. func (ep RegistryEndpoint) Endpoint() string { return ep.EndpointURL.String() } // OverridePath implements RegistryEndpointConfig interface. func (ep RegistryEndpoint) OverridePath() bool { return pointer.SafeDeref(ep.EndpointOverridePath) } ================================================ FILE: pkg/machinery/config/types/cri/registry_mirror_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( _ "embed" "net/url" "testing" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) //go:embed testdata/registrymirrorconfig.yaml var expectedRegistryMirrorConfigDocument []byte func TestRegistryMirrorConfigMarshalStability(t *testing.T) { t.Parallel() cfg := cri.NewRegistryMirrorConfigV1Alpha1("ghcr.io") cfg.RegistrySkipFallback = new(true) cfg.RegistryEndpoints = []cri.RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://my-private-registry.local:5000"))}, }, { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("http://my-harbor/v2/registry-k8s.io/"))}, EndpointOverridePath: new(true), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedRegistryMirrorConfigDocument, marshaled) } func TestRegistryMirrorConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedRegistryMirrorConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &cri.RegistryMirrorConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: cri.RegistryMirrorConfig, }, MetaName: "ghcr.io", RegistrySkipFallback: new(true), RegistryEndpoints: []cri.RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://my-private-registry.local:5000"))}, }, { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("http://my-harbor/v2/registry-k8s.io/"))}, EndpointOverridePath: new(true), }, }, }, docs[0]) } func TestRegistryMirrorConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *cri.RegistryMirrorConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *cri.RegistryMirrorConfigV1Alpha1 { return cri.NewRegistryMirrorConfigV1Alpha1("") }, expectedError: "name must be specified", }, { name: "invalid endpoint URL", cfg: func() *cri.RegistryMirrorConfigV1Alpha1 { cfg := cri.NewRegistryMirrorConfigV1Alpha1("docker.io") cfg.RegistryEndpoints = []cri.RegistryEndpoint{ { EndpointURL: meta.URL{}, }, } return cfg }, expectedError: "endpoints[0].url must be specified", }, { name: "unsupported endpoint URL scheme", cfg: func() *cri.RegistryMirrorConfigV1Alpha1 { cfg := cri.NewRegistryMirrorConfigV1Alpha1("docker.io") cfg.RegistryEndpoints = []cri.RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("ftp://my-registry.local:5000"))}, }, } return cfg }, expectedError: "endpoints[0].url has unsupported scheme: \"ftp\"", }, { name: "valid empty endpoints", cfg: func() *cri.RegistryMirrorConfigV1Alpha1 { cfg := cri.NewRegistryMirrorConfigV1Alpha1("docker.io") return cfg }, }, { name: "valid", cfg: func() *cri.RegistryMirrorConfigV1Alpha1 { cfg := cri.NewRegistryMirrorConfigV1Alpha1("gcr.io") cfg.RegistryEndpoints = []cri.RegistryEndpoint{ { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("https://my-private-registry.local:5000"))}, }, { EndpointURL: meta.URL{URL: ensure.Value(url.Parse("http://my-harbor/v2/registry-k8s.io/"))}, EndpointOverridePath: new(true), }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/types/cri/registry_tls.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( stdx509 "crypto/x509" "errors" "fmt" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) //docgen:jsonschema // RegistryTLSConfig defines the RegistryTLSConfig configuration name. const RegistryTLSConfig = "RegistryTLSConfig" func init() { registry.Register(RegistryTLSConfig, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RegistryTLSConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RegistryTLSConfigDocument = &RegistryTLSConfigV1Alpha1{} _ config.Validator = &RegistryTLSConfigV1Alpha1{} _ config.SecretDocument = &RegistryTLSConfigV1Alpha1{} _ config.NamedDocument = &RegistryTLSConfigV1Alpha1{} ) // RegistryTLSConfigV1Alpha1 configures TLS for a registry endpoint. // // examples: // - value: exampleRegistryTLSConfigVAlpha1() // alias: RegistryTLSConfig // schemaRoot: true // schemaMeta: v1alpha1/RegistryTLSConfig type RegistryTLSConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Registry endpoint to apply the TLS configuration to. // // Registry endpoint is the hostname part of the endpoint URL, // e.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'. // // The TLS configuration makes sense only for HTTPS endpoints. // The TLS configuration will apply to all image pulls for this // registry endpoint, by Talos or any Kubernetes workloads. // schemaRequired: true MetaName string `yaml:"name"` // description: | // Enable mutual TLS authentication with the registry. // Client certificate and key should be PEM-encoded. // examples: // - value: exampleCertificateAndKey() // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string TLSClientIdentity *meta.CertificateAndKey `yaml:"clientIdentity,omitempty"` // description: | // CA registry certificate to add the list of trusted certificates. // Certificate should be PEM-encoded. // schema: // type: string TLSCA string `yaml:"ca,omitempty"` // description: | // Skip TLS server certificate verification (not recommended). TLSInsecureSkipVerify *bool `yaml:"insecureSkipVerify,omitempty"` } // NewRegistryTLSConfigV1Alpha1 creates a new RegistryTLSConfig config document. func NewRegistryTLSConfigV1Alpha1(name string) *RegistryTLSConfigV1Alpha1 { return &RegistryTLSConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RegistryTLSConfig, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleRegistryTLSConfigVAlpha1() *RegistryTLSConfigV1Alpha1 { cfg := NewRegistryTLSConfigV1Alpha1("my-private-registry.local:5000") cfg.TLSCA = "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----" return cfg } func exampleCertificateAndKey() *meta.CertificateAndKey { return &meta.CertificateAndKey{ Cert: "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----", Key: "-----BEGIN PRIVATE KEY-----\nMIIE...AB\n-----END PRIVATE KEY-----", } } // Clone implements config.Document interface. func (s *RegistryTLSConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.Document interface. func (s *RegistryTLSConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *RegistryTLSConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if s.TLSCA != "" { if !stdx509.NewCertPool().AppendCertsFromPEM([]byte(s.TLSCA)) { errs = errors.Join(errs, errors.New("ca must be a valid PEM-encoded certificate")) } } if s.TLSClientIdentity != nil { keyPair := s.ClientIdentity() _, err := keyPair.GetCert() if err != nil { errs = errors.Join(errs, fmt.Errorf("client identity certificate is invalid: %w", err)) } _, err = keyPair.GetPrivateKey() if err != nil { errs = errors.Join(errs, fmt.Errorf("client identity key is invalid: %w", err)) } } return warnings, errs } // ClientIdentity implements config.RegistryTLSConfigDocument interface. func (s *RegistryTLSConfigV1Alpha1) ClientIdentity() *x509.PEMEncodedCertificateAndKey { if s.TLSClientIdentity == nil { return nil } return &x509.PEMEncodedCertificateAndKey{ Crt: []byte(s.TLSClientIdentity.Cert), Key: []byte(s.TLSClientIdentity.Key), } } // CA implements config.RegistryTLSConfigDocument interface. func (s *RegistryTLSConfigV1Alpha1) CA() []byte { if s.TLSCA == "" { return nil } return []byte(s.TLSCA) } // InsecureSkipVerify implements config.RegistryTLSConfigDocument interface. func (s *RegistryTLSConfigV1Alpha1) InsecureSkipVerify() bool { return pointer.SafeDeref(s.TLSInsecureSkipVerify) } // Redact implements config.SecretDocument interface. func (s *RegistryTLSConfigV1Alpha1) Redact(replacement string) { if s.TLSClientIdentity != nil { if s.TLSClientIdentity.Key != "" { s.TLSClientIdentity.Key = replacement } } if s.TLSCA != "" { s.TLSCA = replacement } } ================================================ FILE: pkg/machinery/config/types/cri/registry_tls_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( _ "embed" "testing" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/cri" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) //go:embed testdata/registrytlsconfig.yaml var expectedRegistryTLSConfigDocument []byte func TestRegistryTLSConfigMarshalStability(t *testing.T) { t.Parallel() cfg := cri.NewRegistryTLSConfigV1Alpha1("my-tls-registry.io") cfg.TLSCA = "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----" cfg.TLSClientIdentity = &meta.CertificateAndKey{ Cert: "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----", Key: "-----BEGIN PRIVATE KEY-----\nMIIE...AB\n-----END PRIVATE KEY-----", } cfg.TLSInsecureSkipVerify = new(true) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedRegistryTLSConfigDocument, marshaled) } func TestRegistryTLSConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedRegistryTLSConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &cri.RegistryTLSConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: cri.RegistryTLSConfig, }, MetaName: "my-tls-registry.io", TLSCA: "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----", TLSClientIdentity: &meta.CertificateAndKey{ Cert: "-----BEGIN CERTIFICATE-----\nMIID...IDAQAB\n-----END CERTIFICATE-----", Key: "-----BEGIN PRIVATE KEY-----\nMIIE...AB\n-----END PRIVATE KEY-----", }, TLSInsecureSkipVerify: new(true), }, docs[0]) } func TestRegistryTLSConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *cri.RegistryTLSConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *cri.RegistryTLSConfigV1Alpha1 { return cri.NewRegistryTLSConfigV1Alpha1("") }, expectedError: "name must be specified", }, { name: "valid small", cfg: func() *cri.RegistryTLSConfigV1Alpha1 { cfg := cri.NewRegistryTLSConfigV1Alpha1("rr.k8s.io") cfg.TLSInsecureSkipVerify = new(true) return cfg }, }, { name: "valid", cfg: func() *cri.RegistryTLSConfigV1Alpha1 { cfg := cri.NewRegistryTLSConfigV1Alpha1("k8s.io") ca, err := x509.NewSelfSignedCertificateAuthority() require.NoError(t, err) cfg.TLSCA = string(ca.CrtPEM) cfg.TLSClientIdentity = &meta.CertificateAndKey{ Cert: string(ca.CrtPEM), Key: string(ca.KeyPEM), } cfg.TLSInsecureSkipVerify = new(false) return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/cri/testdata/registryauthconfig.yaml ================================================ apiVersion: v1alpha1 kind: RegistryAuthConfig name: my-private-registry.io username: agent007 password: topsecret ================================================ FILE: pkg/machinery/config/types/cri/testdata/registrymirrorconfig.yaml ================================================ apiVersion: v1alpha1 kind: RegistryMirrorConfig name: ghcr.io endpoints: - url: https://my-private-registry.local:5000 - url: http://my-harbor/v2/registry-k8s.io/ overridePath: true skipFallback: true ================================================ FILE: pkg/machinery/config/types/cri/testdata/registrytlsconfig.yaml ================================================ apiVersion: v1alpha1 kind: RegistryTLSConfig name: my-tls-registry.io clientIdentity: cert: |- -----BEGIN CERTIFICATE----- MIID...IDAQAB -----END CERTIFICATE----- key: |- -----BEGIN PRIVATE KEY----- MIIE...AB -----END PRIVATE KEY----- ca: |- -----BEGIN CERTIFICATE----- MIID...IDAQAB -----END CERTIFICATE----- insecureSkipVerify: true ================================================ FILE: pkg/machinery/config/types/hardware/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type PCIDriverRebindConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package hardware // DeepCopy generates a deep copy of *PCIDriverRebindConfigV1Alpha1. func (o *PCIDriverRebindConfigV1Alpha1) DeepCopy() *PCIDriverRebindConfigV1Alpha1 { var cp PCIDriverRebindConfigV1Alpha1 = *o return &cp } ================================================ FILE: pkg/machinery/config/types/hardware/hardware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package hardware provides hardware related config documents. package hardware //go:generate go tool github.com/siderolabs/talos/tools/docgen -output hardware_doc.go hardware.go pci_driver_rebind_config.go //go:generate go tool github.com/siderolabs/deep-copy -type PCIDriverRebindConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/hardware/hardware_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package hardware import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (PCIDriverRebindConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "PCIDriverRebindConfig", Comments: [3]string{"" /* encoder.HeadComment */, "PCIDriverRebindConfig allows to configure PCI driver rebinds." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "PCIDriverRebindConfig allows to configure PCI driver rebinds.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "PCI device id", Comments: [3]string{"" /* encoder.HeadComment */, "PCI device id" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "targetDriver", Type: "string", Note: "", Description: "Target driver to rebind the PCI device to.", Comments: [3]string{"" /* encoder.HeadComment */, "Target driver to rebind the PCI device to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", examplePCIDriverRebindConfigAlpha1()) return doc } // GetFileDoc returns documentation for the file hardware_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "hardware", Description: "Package hardware provides hardware related config documents.\n", Structs: []*encoder.Doc{ PCIDriverRebindConfigV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/hardware/pci_driver_rebind_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "os" "path/filepath" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) //docgen:jsonschema // PCIDriverRebindConfig defines the PCIDriverRebind configuration name. const PCIDriverRebindConfig = "PCIDriverRebindConfig" func init() { registry.Register(PCIDriverRebindConfig, func(version string) config.Document { switch version { case "v1alpha1": return &PCIDriverRebindConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.PCIDriverRebindConfig = &PCIDriverRebindConfigV1Alpha1{} _ config.NamedDocument = &PCIDriverRebindConfigV1Alpha1{} ) // PCIDriverRebindConfigV1Alpha1 allows to configure PCI driver rebinds. // // examples: // - value: examplePCIDriverRebindConfigAlpha1() // alias: PCIDriverRebindConfig // schemaRoot: true // schemaMeta: v1alpha1/PCIDriverRebindConfig type PCIDriverRebindConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // PCI device id // schemaRequired: true MetaName string `yaml:"name"` // description: | // Target driver to rebind the PCI device to. // schemaRequired: true PCITargetDriver string `yaml:"targetDriver"` } // NewPCIDriverRebindConfigV1Alpha1 creates a new PCIDriverRebindConfig config document. func NewPCIDriverRebindConfigV1Alpha1() *PCIDriverRebindConfigV1Alpha1 { return &PCIDriverRebindConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: PCIDriverRebindConfig, MetaAPIVersion: "v1alpha1", }, } } func examplePCIDriverRebindConfigAlpha1() *PCIDriverRebindConfigV1Alpha1 { cfg := NewPCIDriverRebindConfigV1Alpha1() cfg.MetaName = "0000:04:00.00" cfg.PCITargetDriver = "vfio-pci" return cfg } // Clone implements config.Document interface. func (s *PCIDriverRebindConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.Document interface. func (s *PCIDriverRebindConfigV1Alpha1) Name() string { return s.MetaName } // PCIDriverRebindConfigs implements config.PCIDriverRebindConfig interface. func (s *PCIDriverRebindConfigV1Alpha1) PCIDriverRebindConfigs() []config.PCIDriverRebindConfigDriver { return []config.PCIDriverRebindConfigDriver{s} } // PCIID implements config.PCIDriverRebindConfigDriver interface. func (s *PCIDriverRebindConfigV1Alpha1) PCIID() string { return s.MetaName } // TargetDriver implements config.PCIDriverRebindConfigDriver interface. func (s *PCIDriverRebindConfigV1Alpha1) TargetDriver() string { return s.PCITargetDriver } // RuntimeValidate implements config.RuntimeValidatable interface. func (s *PCIDriverRebindConfigV1Alpha1) RuntimeValidate(ctx context.Context, st state.State, v validation.RuntimeMode, opts ...validation.Option) ([]string, error) { var count int if err := filepath.WalkDir("/sys/class/iommu", func(path string, d os.DirEntry, err error) error { if err != nil { return err } rel, err := filepath.Rel("/sys/class/iommu", path) if err != nil { return err } if rel == "." { return nil } count++ return nil }); err != nil { return nil, err } if count == 0 { return []string{"IOMMU is not enabled, this config change might not have any effect"}, nil } return nil, nil } ================================================ FILE: pkg/machinery/config/types/hardware/pci_driver_rebind_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) //go:embed testdata/pcidriverrebindconfig.yaml var expectedPCIDriverRebindConfigDocument []byte func TestPCIDriverRebindConfigMarshal(t *testing.T) { t.Parallel() cfg := hardware.NewPCIDriverRebindConfigV1Alpha1() cfg.MetaName = "0000:04:00.00" cfg.PCITargetDriver = "vfio-pci" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, string(expectedPCIDriverRebindConfigDocument), string(marshaled)) } func TestPCIDriverRebindConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedPCIDriverRebindConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &hardware.PCIDriverRebindConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: hardware.PCIDriverRebindConfig, }, MetaName: "0000:04:00.00", PCITargetDriver: "vfio-pci", }, docs[0]) } ================================================ FILE: pkg/machinery/config/types/hardware/testdata/pcidriverrebindconfig.yaml ================================================ apiVersion: v1alpha1 kind: PCIDriverRebindConfig name: "0000:04:00.00" targetDriver: vfio-pci ================================================ FILE: pkg/machinery/config/types/meta/certificate_key.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package meta // CertificateAndKey represents a PEM-encoded certificate and key pair. type CertificateAndKey struct { Cert string `yaml:"cert,omitempty"` Key string `yaml:"key,omitempty"` } ================================================ FILE: pkg/machinery/config/types/meta/meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package meta provides common meta types for config documents. package meta // Meta is a shared meta information for config documents. type Meta struct { MetaAPIVersion string `yaml:"apiVersion,omitempty"` MetaKind string `yaml:"kind"` } // Kind implements config.Document interface. func (m Meta) Kind() string { return m.MetaKind } // APIVersion implements config.Document interface. func (m Meta) APIVersion() string { return m.MetaAPIVersion } ================================================ FILE: pkg/machinery/config/types/meta/url.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package meta import "net/url" // URL wraps the URL with proper YAML marshal/unmarshal. type URL struct { *url.URL } // UnmarshalYAML is a custom unmarshaller for `URL`. func (u *URL) UnmarshalYAML(unmarshal func(any) error) error { var endpoint string if err := unmarshal(&endpoint); err != nil { return err } if endpoint == "" { return nil } url, err := url.Parse(endpoint) if err != nil { return err } *u = URL{url} return nil } // MarshalYAML is a custom marshaller for `URL`. func (u URL) MarshalYAML() (any, error) { if u.URL == nil { return "", nil } return u.URL.String(), nil } ================================================ FILE: pkg/machinery/config/types/network/blackhole_route.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // BlackholeRouteKind is a BlackholeRoute config document kind. const BlackholeRouteKind = "BlackholeRouteConfig" func init() { registry.Register(BlackholeRouteKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &BlackholeRouteConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkBlackholeRouteConfig = &BlackholeRouteConfigV1Alpha1{} _ config.NamedDocument = &BlackholeRouteConfigV1Alpha1{} _ config.Validator = &BlackholeRouteConfigV1Alpha1{} ) // BlackholeRouteConfigV1Alpha1 is a config document to configure blackhole routes. // // examples: // - value: exampleBlackholeRouteConfigV1Alpha1() // alias: BlackholeRouteConfig // schemaRoot: true // schemaMeta: v1alpha1/BlackholeRouteConfig type BlackholeRouteConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Route destination as an address prefix. // // examples: // - value: > // "10.0.0.0/12" // schemaRequired: true MetaName string `yaml:"name"` // description: | // The optional metric for the route. RouteMetric uint32 `yaml:"metric,omitempty"` } // NewBlackholeRouteConfigV1Alpha1 creates a new BlackholeRouteConfig config document. func NewBlackholeRouteConfigV1Alpha1(name string) *BlackholeRouteConfigV1Alpha1 { return &BlackholeRouteConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: BlackholeRouteKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleBlackholeRouteConfigV1Alpha1() *BlackholeRouteConfigV1Alpha1 { cfg := NewBlackholeRouteConfigV1Alpha1("10.0.0.0/12") cfg.RouteMetric = 100 return cfg } // Clone implements config.Document interface. func (s *BlackholeRouteConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *BlackholeRouteConfigV1Alpha1) Name() string { return s.MetaName } // BlackholeRouteConfig implements BlackholeRouteConfig interface. func (s *BlackholeRouteConfigV1Alpha1) BlackholeRouteConfig() {} // Validate implements config.Validator interface. func (s *BlackholeRouteConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if _, err := netip.ParsePrefix(s.MetaName); err != nil { errs = errors.Join(errs, fmt.Errorf("name must be a valid address prefix: %w", err)) } return nil, errs } // Metric implements NetworkRouteConfig interface. func (s *BlackholeRouteConfigV1Alpha1) Metric() optional.Optional[uint32] { if s.RouteMetric == 0 { return optional.None[uint32]() } return optional.Some(s.RouteMetric) } ================================================ FILE: pkg/machinery/config/types/network/blackhole_route_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/blackholerouteconfig.yaml var expectedBlackholeRouteConfigDocument []byte func TestBlackholeRouteConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewBlackholeRouteConfigV1Alpha1("169.254.1.1/32") cfg.RouteMetric = 2000 marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedBlackholeRouteConfigDocument, marshaled) } func TestBlackholeRouteConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedBlackholeRouteConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.BlackholeRouteConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.BlackholeRouteKind, }, MetaName: "169.254.1.1/32", RouteMetric: 2000, }, docs[0]) } func TestBlackholeRouteConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.BlackholeRouteConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.BlackholeRouteConfigV1Alpha1 { return network.NewBlackholeRouteConfigV1Alpha1("") }, expectedError: "name must be specified\nname must be a valid address prefix: netip.ParsePrefix(\"\"): no '/'", }, { name: "invalid prefix", cfg: func() *network.BlackholeRouteConfigV1Alpha1 { cfg := network.NewBlackholeRouteConfigV1Alpha1("no-prefix") return cfg }, expectedError: "name must be a valid address prefix: netip.ParsePrefix(\"no-prefix\"): no '/'", }, { name: "valid", cfg: func() *network.BlackholeRouteConfigV1Alpha1 { return network.NewBlackholeRouteConfigV1Alpha1("10.0.1.2/24") }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/bond.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "net/netip" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // BondKind is a Bond config document kind. const BondKind = "BondConfig" func init() { registry.Register(BondKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &BondConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkBondConfig = &BondConfigV1Alpha1{} _ config.ConflictingDocument = &BondConfigV1Alpha1{} _ config.NamedDocument = &BondConfigV1Alpha1{} _ config.Validator = &BondConfigV1Alpha1{} ) // BondConfigV1Alpha1 is a config document to create a bond (link aggregation) over a set of links. // // examples: // - value: exampleBondConfigV1Alpha1() // alias: BondConfig // schemaRoot: true // schemaMeta: v1alpha1/BondConfig type BondConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the bond link (interface) to be created. // // examples: // - value: > // "bond.ext" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Override the hardware (MAC) address of the link. // // examples: // - value: > // nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70} // schema: // type: string // pattern: ^[0-9a-f:]+$ HardwareAddressConfig nethelpers.HardwareAddr `yaml:"hardwareAddr,omitempty"` // description: | // Names of the links (interfaces) on which the bond will be created. // Link aliases can be used here as well. // examples: // - value: > // []string{"enp0s3", "enp0s8"} // schemaRequired: true BondLinks []string `yaml:"links,omitempty"` // description: | // Bond mode. // // examples: // - value: > // "802.3ad" // values: // - "balance-rr" // - "active-backup" // - "balance-xor" // - "broadcast" // - "802.3ad" // - "balance-tlb" // - "balance-alb" // schemaRequired: true BondMode *nethelpers.BondMode `yaml:"bondMode,omitempty"` // description: | // Link monitoring frequency in milliseconds. // // examples: // - value: > // 200 BondMIIMon *uint32 `yaml:"miimon,omitempty"` // description: | // The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected. // // examples: // - value: > // 300 BondUpDelay *uint32 `yaml:"updelay,omitempty"` // description: | // The time, in milliseconds, to wait before disabling a slave after a link failure has been detected. // // examples: // - value: > // 100 BondDownDelay *uint32 `yaml:"downdelay,omitempty"` // description: | // Specifies whether or not miimon should use MII or ETHTOOL. BondUseCarrier *bool `yaml:"useCarrier,omitempty"` // description: | // Selects the transmit hash policy to use for slave selection. // examples: // - value: > // "layer2" // values: // - "layer2" // - "layer3+4" // - "layer2+3" // - "encap2+3" // - "encap3+4" BondXmitHashPolicy *nethelpers.BondXmitHashPolicy `yaml:"xmitHashPolicy,omitempty"` // description: | // ARP link monitoring frequency in milliseconds. // examples: // - value: > // 1000 BondARPInterval *uint32 `yaml:"arpInterval,omitempty"` // description: | // The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set. // Maximum of 16 targets are supported. // examples: // - value: > // []netip.Addr{netip.MustParseAddr("10.15.0.1")} // schema: // type: array // items: // type: string // pattern: ^[0-9a-f.:]+$ BondARPIPTargets []netip.Addr `yaml:"arpIpTargets,omitempty"` // description: | // The list of IPv6 addresses to use for NS link monitoring when arpInterval is set. // Maximum of 16 targets are supported. // examples: // - value: > // []netip.Addr{netip.MustParseAddr("fd00::1")} // schema: // type: array // items: // type: string // pattern: ^[0-9a-f.:]+$ BondNSIP6Targets []netip.Addr `yaml:"nsIp6Targets,omitempty"` // description: | // Specifies whether or not ARP probes and replies should be validated. // examples: // - value: > // "active" // values: // - "none" // - "active" // - "backup" // - "all" // - "filter" // - "filter-active" // - "filter-backup" BondARPValidate *nethelpers.ARPValidate `yaml:"arpValidate,omitempty"` // description: | // Specifies whether ARP probes should be sent to any or all targets. // examples: // - value: > // "all" // values: // - "any" // - "all" BondARPAllTargets *nethelpers.ARPAllTargets `yaml:"arpAllTargets,omitempty"` // description: | // LACPDU frames periodic transmission rate. // examples: // - value: > // "fast" // values: // - "slow" // - "fast" BondLACPRate *nethelpers.LACPRate `yaml:"lacpRate,omitempty"` // description: | // Specifies whether active-backup mode should set all slaves to the same MAC address // at enslavement, when enabled, or perform special handling. // examples: // - value: > // "active" // values: // - "none" // - "active" // - "follow" BondFailOverMAC *nethelpers.FailOverMAC `yaml:"failOverMac,omitempty"` // description: | // Aggregate selection policy for 802.3ad. // examples: // - value: > // "stable" // values: // - "stable" // - "bandwidth" // - "count" BondADSelect *nethelpers.ADSelect `yaml:"adSelect,omitempty"` // description: | // Actor system priority for 802.3ad. // // examples: // - value: > // 65535 BondADActorSysPrio *uint16 `yaml:"adActorSysPrio,omitempty"` // description: | // User port key (upper 10 bits) for 802.3ad. // // examples: // - value: > // 0 BondADUserPortKey *uint16 `yaml:"adUserPortKey,omitempty"` // description: | // Whether to send LACPDU frames periodically. // examples: // - value: > // "on" // values: // - "on" // - "off" BondADLACPActive *nethelpers.ADLACPActive `yaml:"adLACPActive,omitempty"` // description: | // Policy under which the primary slave should be reselected. // examples: // - value: > // "always" // values: // - "always" // - "better" // - "failure" BondPrimaryReselect *nethelpers.PrimaryReselect `yaml:"primaryReselect,omitempty"` // description: | // The number of times IGMP packets should be resent. BondResendIGMP *uint32 `yaml:"resendIGMP,omitempty"` // description: | // The minimum number of active links required for the bond to be considered active. BondMinLinks *uint32 `yaml:"minLinks,omitempty"` // description: | // The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch. BondLPInterval *uint32 `yaml:"lpInterval,omitempty"` // description: | // The number of packets to transmit through a slave before moving to the next one. BondPacketsPerSlave *uint32 `yaml:"packetsPerSlave,omitempty"` // description: | // The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) // to be issued after a failover event. BondNumPeerNotif *uint8 `yaml:"numPeerNotif,omitempty"` // description: | // Whether dynamic shuffling of flows is enabled in tlb or alb mode. // examples: // - value: > // 1 BondTLBDynamicLB *uint8 `yaml:"tlbLogicalLb,omitempty"` // description: | // Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1). // examples: // - value: > // 0 BondAllSlavesActive *uint8 `yaml:"allSlavesActive,omitempty"` // description: | // The delay, in milliseconds, between each peer notification. BondPeerNotifyDelay *uint32 `yaml:"peerNotifDelay,omitempty"` // description: | // The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor. BondMissedMax *uint8 `yaml:"missedMax,omitempty"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // NewBondConfigV1Alpha1 creates a new BondConfig config document. func NewBondConfigV1Alpha1(name string) *BondConfigV1Alpha1 { return &BondConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: BondKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleBondConfigV1Alpha1() *BondConfigV1Alpha1 { cfg := NewBondConfigV1Alpha1("bond.int") cfg.BondLinks = []string{"enp1s2", "enp1s2"} cfg.BondMode = new(nethelpers.BondMode8023AD) cfg.BondXmitHashPolicy = new(nethelpers.BondXmitPolicyLayer34) cfg.BondLACPRate = new(nethelpers.LACPRateSlow) cfg.BondMIIMon = new(uint32(100)) cfg.BondUpDelay = new(uint32(200)) cfg.BondDownDelay = new(uint32(200)) cfg.BondResendIGMP = new(uint32(1)) cfg.BondPacketsPerSlave = new(uint32(1)) cfg.BondADActorSysPrio = new(uint16(65535)) cfg.LinkAddresses = []AddressConfig{ { AddressAddress: netip.MustParsePrefix("10.15.0.3/16"), }, } cfg.LinkRoutes = []RouteConfig{ { RouteDestination: Prefix{netip.MustParsePrefix("10.0.0.0/8")}, RouteGateway: Addr{netip.MustParseAddr("10.15.0.1")}, }, } return cfg } // Clone implements config.Document interface. func (s *BondConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *BondConfigV1Alpha1) Name() string { return s.MetaName } // BondConfig implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) BondConfig() {} // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *BondConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(BondKind) } // Validate implements config.Validator interface. func (s *BondConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if len(s.BondLinks) == 0 { errs = errors.Join(errs, errors.New("at least one link must be specified")) } if s.BondMode == nil { errs = errors.Join(errs, errors.New("bond mode must be specified")) } else if *s.BondMode == nethelpers.BondMode8023AD { warnings = append(warnings, s.validateFor8023AD()...) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } func (s *BondConfigV1Alpha1) validateFor8023AD() []string { const warn = " was not specified for 802.3ad bond" var warnings []string if s.BondMIIMon == nil { warnings = append(warnings, "miimon"+warn) } if s.BondUpDelay == nil { warnings = append(warnings, "updelay"+warn) } if s.BondDownDelay == nil { warnings = append(warnings, "downdelay"+warn) } return warnings } // Links implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) Links() []string { return s.BondLinks } // Mode implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) Mode() nethelpers.BondMode { return pointer.SafeDeref(s.BondMode) } // MIIMon implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) MIIMon() optional.Optional[uint32] { if s.BondMIIMon == nil { return optional.None[uint32]() } return optional.Some(*s.BondMIIMon) } // UpDelay implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) UpDelay() optional.Optional[uint32] { if s.BondUpDelay == nil { return optional.None[uint32]() } return optional.Some(*s.BondUpDelay) } // DownDelay implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) DownDelay() optional.Optional[uint32] { if s.BondDownDelay == nil { return optional.None[uint32]() } return optional.Some(*s.BondDownDelay) } // UseCarrier implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) UseCarrier() optional.Optional[bool] { if s.BondUseCarrier == nil { return optional.None[bool]() } return optional.Some(*s.BondUseCarrier) } // XmitHashPolicy implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) XmitHashPolicy() optional.Optional[nethelpers.BondXmitHashPolicy] { if s.BondXmitHashPolicy == nil { return optional.None[nethelpers.BondXmitHashPolicy]() } return optional.Some(*s.BondXmitHashPolicy) } // ARPInterval implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ARPInterval() optional.Optional[uint32] { if s.BondARPInterval == nil { return optional.None[uint32]() } return optional.Some(*s.BondARPInterval) } // ARPIPTargets implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ARPIPTargets() []netip.Addr { return s.BondARPIPTargets } // NSIP6Targets implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) NSIP6Targets() []netip.Addr { return s.BondNSIP6Targets } // ARPValidate implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ARPValidate() optional.Optional[nethelpers.ARPValidate] { if s.BondARPValidate == nil { return optional.None[nethelpers.ARPValidate]() } return optional.Some(*s.BondARPValidate) } // ARPAllTargets implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ARPAllTargets() optional.Optional[nethelpers.ARPAllTargets] { if s.BondARPAllTargets == nil { return optional.None[nethelpers.ARPAllTargets]() } return optional.Some(*s.BondARPAllTargets) } // LACPRate implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) LACPRate() optional.Optional[nethelpers.LACPRate] { if s.BondLACPRate == nil { return optional.None[nethelpers.LACPRate]() } return optional.Some(*s.BondLACPRate) } // FailOverMAC implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) FailOverMAC() optional.Optional[nethelpers.FailOverMAC] { if s.BondFailOverMAC == nil { return optional.None[nethelpers.FailOverMAC]() } return optional.Some(*s.BondFailOverMAC) } // ADSelect implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ADSelect() optional.Optional[nethelpers.ADSelect] { if s.BondADSelect == nil { return optional.None[nethelpers.ADSelect]() } return optional.Some(*s.BondADSelect) } // ADActorSysPrio implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ADActorSysPrio() optional.Optional[uint16] { if s.BondADActorSysPrio == nil { return optional.None[uint16]() } return optional.Some(*s.BondADActorSysPrio) } // ADUserPortKey implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ADUserPortKey() optional.Optional[uint16] { if s.BondADUserPortKey == nil { return optional.None[uint16]() } return optional.Some(*s.BondADUserPortKey) } // ADLACPActive implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ADLACPActive() optional.Optional[nethelpers.ADLACPActive] { if s.BondADLACPActive == nil { return optional.None[nethelpers.ADLACPActive]() } return optional.Some(*s.BondADLACPActive) } // PrimaryReselect implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) PrimaryReselect() optional.Optional[nethelpers.PrimaryReselect] { if s.BondPrimaryReselect == nil { return optional.None[nethelpers.PrimaryReselect]() } return optional.Some(*s.BondPrimaryReselect) } // ResendIGMP implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) ResendIGMP() optional.Optional[uint32] { if s.BondResendIGMP == nil { return optional.None[uint32]() } return optional.Some(*s.BondResendIGMP) } // MinLinks implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) MinLinks() optional.Optional[uint32] { if s.BondMinLinks == nil { return optional.None[uint32]() } return optional.Some(*s.BondMinLinks) } // LPInterval implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) LPInterval() optional.Optional[uint32] { if s.BondLPInterval == nil { return optional.None[uint32]() } return optional.Some(*s.BondLPInterval) } // PacketsPerSlave implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) PacketsPerSlave() optional.Optional[uint32] { if s.BondPacketsPerSlave == nil { return optional.None[uint32]() } return optional.Some(*s.BondPacketsPerSlave) } // NumPeerNotif implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) NumPeerNotif() optional.Optional[uint8] { if s.BondNumPeerNotif == nil { return optional.None[uint8]() } return optional.Some(*s.BondNumPeerNotif) } // TLBDynamicLB implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) TLBDynamicLB() optional.Optional[uint8] { if s.BondTLBDynamicLB == nil { return optional.None[uint8]() } return optional.Some(*s.BondTLBDynamicLB) } // AllSlavesActive implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) AllSlavesActive() optional.Optional[uint8] { if s.BondAllSlavesActive == nil { return optional.None[uint8]() } return optional.Some(*s.BondAllSlavesActive) } // PeerNotifyDelay implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) PeerNotifyDelay() optional.Optional[uint32] { if s.BondPeerNotifyDelay == nil { return optional.None[uint32]() } return optional.Some(*s.BondPeerNotifyDelay) } // MissedMax implements NetworkBondConfig interface. func (s *BondConfigV1Alpha1) MissedMax() optional.Optional[uint8] { if s.BondMissedMax == nil { return optional.None[uint8]() } return optional.Some(*s.BondMissedMax) } // HardwareAddress implements NetworkDummyLinkConfig interface. func (s *BondConfigV1Alpha1) HardwareAddress() optional.Optional[nethelpers.HardwareAddr] { if len(s.HardwareAddressConfig) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.HardwareAddressConfig) } ================================================ FILE: pkg/machinery/config/types/network/bond_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/bondconfig.yaml var expectedBondConfigDocument []byte func TestBondConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewBondConfigV1Alpha1("agg.0") cfg.BondLinks = []string{"eth0", "eth1"} cfg.BondMode = new(nethelpers.BondMode8023AD) cfg.BondXmitHashPolicy = new(nethelpers.BondXmitPolicyLayer34) cfg.BondFailOverMAC = new(nethelpers.FailOverMACFollow) cfg.BondLACPRate = new(nethelpers.LACPRateSlow) cfg.BondMIIMon = new(uint32(100)) cfg.BondUpDelay = new(uint32(200)) cfg.BondDownDelay = new(uint32(200)) cfg.BondResendIGMP = new(uint32(1)) cfg.BondPacketsPerSlave = new(uint32(1)) cfg.BondADActorSysPrio = new(uint16(65535)) cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.4/24"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedBondConfigDocument, marshaled) } func TestBondConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedBondConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.BondConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.BondKind, }, MetaName: "agg.0", BondLinks: []string{"eth0", "eth1"}, BondMode: new(nethelpers.BondMode8023AD), BondXmitHashPolicy: new(nethelpers.BondXmitPolicyLayer34), BondFailOverMAC: new(nethelpers.FailOverMACFollow), BondLACPRate: new(nethelpers.LACPRateSlow), BondMIIMon: new(uint32(100)), BondUpDelay: new(uint32(200)), BondDownDelay: new(uint32(200)), BondResendIGMP: new(uint32(1)), BondPacketsPerSlave: new(uint32(1)), BondADActorSysPrio: new(uint16(65535)), CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.4/24"), }, }, }, }, docs[0]) } func TestBondValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.BondConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.BondConfigV1Alpha1 { return network.NewBondConfigV1Alpha1("") }, expectedError: "name must be specified\nat least one link must be specified\nbond mode must be specified", }, { name: "no links", cfg: func() *network.BondConfigV1Alpha1 { cfg := network.NewBondConfigV1Alpha1("bond0") cfg.BondMode = new(nethelpers.BondModeActiveBackup) return cfg }, expectedError: "at least one link must be specified", }, { name: "no mode", cfg: func() *network.BondConfigV1Alpha1 { cfg := network.NewBondConfigV1Alpha1("bond0") cfg.BondLinks = []string{"eth0", "eth1"} return cfg }, expectedError: "bond mode must be specified", }, { name: "valid", cfg: func() *network.BondConfigV1Alpha1 { cfg := network.NewBondConfigV1Alpha1("bond25") cfg.BondLinks = []string{"eth0", "eth1"} cfg.BondMode = new(nethelpers.BondMode8023AD) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, expectedWarnings: []string{ "miimon was not specified for 802.3ad bond", "updelay was not specified for 802.3ad bond", "downdelay was not specified for 802.3ad bond", }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/bridge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // BridgeKind is a Bridge config document kind. const BridgeKind = "BridgeConfig" func init() { registry.Register(BridgeKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &BridgeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkBridgeConfig = &BridgeConfigV1Alpha1{} _ config.ConflictingDocument = &BridgeConfigV1Alpha1{} _ config.NamedDocument = &BridgeConfigV1Alpha1{} _ config.Validator = &BridgeConfigV1Alpha1{} ) // BridgeConfigV1Alpha1 is a config document to create a Bridge (link aggregation) over a set of links. // // examples: // - value: exampleBridgeConfigV1Alpha1() // alias: BridgeConfig // schemaRoot: true // schemaMeta: v1alpha1/BridgeConfig type BridgeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the bridge link (interface) to be created. // // examples: // - value: > // "Bridge.ext" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Override the hardware (MAC) address of the link. // // examples: // - value: > // nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70} // schema: // type: string // pattern: ^[0-9a-f:]+$ HardwareAddressConfig nethelpers.HardwareAddr `yaml:"hardwareAddr,omitempty"` // description: | // Names of the links (interfaces) to be aggregated. // Link aliases can be used here as well. // examples: // - value: > // []string{"enp1s3", "enp1s2"} // schemaRequired: true BridgeLinks []string `yaml:"links,omitempty"` // description: | // Bridge STP (Spanning Tree Protocol) configuration. BridgeSTP BridgeSTPConfig `yaml:"stp,omitempty"` // description: | // Bridge VLAN configuration. BridgeVLAN BridgeVLANConfig `yaml:"vlan,omitempty"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration. type BridgeSTPConfig struct { // description: | // Enable or disable STP on the bridge. // // examples: // - value: true BridgeSTPEnabled *bool `yaml:"enabled,omitempty"` } // Enabled implements BridgeSTPConfig interface. func (s BridgeSTPConfig) Enabled() optional.Optional[bool] { if s.BridgeSTPEnabled == nil { return optional.None[bool]() } return optional.Some(*s.BridgeSTPEnabled) } // BridgeVLANConfig is a bridge VLAN configuration. type BridgeVLANConfig struct { // description: | // Enable or disable VLAN filtering on the bridge. // // examples: // - value: true BridgeVLANFiltering *bool `yaml:"filtering,omitempty"` } // FilteringEnabled implements BridgeVLANConfig interface. func (s BridgeVLANConfig) FilteringEnabled() optional.Optional[bool] { if s.BridgeVLANFiltering == nil { return optional.None[bool]() } return optional.Some(*s.BridgeVLANFiltering) } // NewBridgeConfigV1Alpha1 creates a new BridgeConfig config document. func NewBridgeConfigV1Alpha1(name string) *BridgeConfigV1Alpha1 { return &BridgeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: BridgeKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleBridgeConfigV1Alpha1() *BridgeConfigV1Alpha1 { cfg := NewBridgeConfigV1Alpha1("bridge.3") cfg.BridgeLinks = []string{"eno1", "eno2"} cfg.BridgeSTP.BridgeSTPEnabled = new(true) return cfg } // Clone implements config.Document interface. func (s *BridgeConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *BridgeConfigV1Alpha1) Name() string { return s.MetaName } // BridgeConfig implements NetworkBridgeConfig interface. func (s *BridgeConfigV1Alpha1) BridgeConfig() {} // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *BridgeConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(BridgeKind) } // Validate implements config.Validator interface. func (s *BridgeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } // Links implements NetworkBridgeConfig interface. func (s *BridgeConfigV1Alpha1) Links() []string { return s.BridgeLinks } // HardwareAddress implements NetworkDummyLinkConfig interface. func (s *BridgeConfigV1Alpha1) HardwareAddress() optional.Optional[nethelpers.HardwareAddr] { if len(s.HardwareAddressConfig) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.HardwareAddressConfig) } // STP implements NetworkBridgeConfig interface. func (s *BridgeConfigV1Alpha1) STP() config.BridgeSTPConfig { return s.BridgeSTP } // VLAN implements NetworkBridgeConfig interface. func (s *BridgeConfigV1Alpha1) VLAN() config.BridgeVLANConfig { return s.BridgeVLAN } ================================================ FILE: pkg/machinery/config/types/network/bridge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/bridgeconfig.yaml var expectedBridgeConfigDocument []byte func TestBridgeConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewBridgeConfigV1Alpha1("bridge.1") cfg.BridgeLinks = []string{"eno1", "eno5"} cfg.BridgeSTP.BridgeSTPEnabled = new(true) cfg.BridgeVLAN.BridgeVLANFiltering = new(false) cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.5/32"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedBridgeConfigDocument, marshaled) } func TestBridgeConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedBridgeConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.BridgeConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.BridgeKind, }, MetaName: "bridge.1", BridgeLinks: []string{"eno1", "eno5"}, BridgeSTP: network.BridgeSTPConfig{BridgeSTPEnabled: new(true)}, BridgeVLAN: network.BridgeVLANConfig{BridgeVLANFiltering: new(false)}, CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.5/32"), }, }, }, }, docs[0]) } func TestBridgeValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.BridgeConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.BridgeConfigV1Alpha1 { return network.NewBridgeConfigV1Alpha1("") }, expectedError: "name must be specified", }, { name: "no links", cfg: func() *network.BridgeConfigV1Alpha1 { cfg := network.NewBridgeConfigV1Alpha1("Bridge0") return cfg }, }, { name: "valid", cfg: func() *network.BridgeConfigV1Alpha1 { cfg := network.NewBridgeConfigV1Alpha1("Bridge25") cfg.BridgeLinks = []string{"eth0", "eth1"} cfg.BridgeSTP.BridgeSTPEnabled = new(true) cfg.BridgeVLAN.BridgeVLANFiltering = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package network import ( "net/netip" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DeepCopy generates a deep copy of *BlackholeRouteConfigV1Alpha1. func (o *BlackholeRouteConfigV1Alpha1) DeepCopy() *BlackholeRouteConfigV1Alpha1 { var cp BlackholeRouteConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *BondConfigV1Alpha1. func (o *BondConfigV1Alpha1) DeepCopy() *BondConfigV1Alpha1 { var cp BondConfigV1Alpha1 = *o if o.HardwareAddressConfig != nil { cp.HardwareAddressConfig = make([]byte, len(o.HardwareAddressConfig)) copy(cp.HardwareAddressConfig, o.HardwareAddressConfig) } if o.BondLinks != nil { cp.BondLinks = make([]string, len(o.BondLinks)) copy(cp.BondLinks, o.BondLinks) } if o.BondMode != nil { cp.BondMode = new(nethelpers.BondMode) *cp.BondMode = *o.BondMode } if o.BondMIIMon != nil { cp.BondMIIMon = new(uint32) *cp.BondMIIMon = *o.BondMIIMon } if o.BondUpDelay != nil { cp.BondUpDelay = new(uint32) *cp.BondUpDelay = *o.BondUpDelay } if o.BondDownDelay != nil { cp.BondDownDelay = new(uint32) *cp.BondDownDelay = *o.BondDownDelay } if o.BondUseCarrier != nil { cp.BondUseCarrier = new(bool) *cp.BondUseCarrier = *o.BondUseCarrier } if o.BondXmitHashPolicy != nil { cp.BondXmitHashPolicy = new(nethelpers.BondXmitHashPolicy) *cp.BondXmitHashPolicy = *o.BondXmitHashPolicy } if o.BondARPInterval != nil { cp.BondARPInterval = new(uint32) *cp.BondARPInterval = *o.BondARPInterval } if o.BondARPIPTargets != nil { cp.BondARPIPTargets = make([]netip.Addr, len(o.BondARPIPTargets)) copy(cp.BondARPIPTargets, o.BondARPIPTargets) } if o.BondNSIP6Targets != nil { cp.BondNSIP6Targets = make([]netip.Addr, len(o.BondNSIP6Targets)) copy(cp.BondNSIP6Targets, o.BondNSIP6Targets) } if o.BondARPValidate != nil { cp.BondARPValidate = new(nethelpers.ARPValidate) *cp.BondARPValidate = *o.BondARPValidate } if o.BondARPAllTargets != nil { cp.BondARPAllTargets = new(nethelpers.ARPAllTargets) *cp.BondARPAllTargets = *o.BondARPAllTargets } if o.BondLACPRate != nil { cp.BondLACPRate = new(nethelpers.LACPRate) *cp.BondLACPRate = *o.BondLACPRate } if o.BondFailOverMAC != nil { cp.BondFailOverMAC = new(nethelpers.FailOverMAC) *cp.BondFailOverMAC = *o.BondFailOverMAC } if o.BondADSelect != nil { cp.BondADSelect = new(nethelpers.ADSelect) *cp.BondADSelect = *o.BondADSelect } if o.BondADActorSysPrio != nil { cp.BondADActorSysPrio = new(uint16) *cp.BondADActorSysPrio = *o.BondADActorSysPrio } if o.BondADUserPortKey != nil { cp.BondADUserPortKey = new(uint16) *cp.BondADUserPortKey = *o.BondADUserPortKey } if o.BondADLACPActive != nil { cp.BondADLACPActive = new(nethelpers.ADLACPActive) *cp.BondADLACPActive = *o.BondADLACPActive } if o.BondPrimaryReselect != nil { cp.BondPrimaryReselect = new(nethelpers.PrimaryReselect) *cp.BondPrimaryReselect = *o.BondPrimaryReselect } if o.BondResendIGMP != nil { cp.BondResendIGMP = new(uint32) *cp.BondResendIGMP = *o.BondResendIGMP } if o.BondMinLinks != nil { cp.BondMinLinks = new(uint32) *cp.BondMinLinks = *o.BondMinLinks } if o.BondLPInterval != nil { cp.BondLPInterval = new(uint32) *cp.BondLPInterval = *o.BondLPInterval } if o.BondPacketsPerSlave != nil { cp.BondPacketsPerSlave = new(uint32) *cp.BondPacketsPerSlave = *o.BondPacketsPerSlave } if o.BondNumPeerNotif != nil { cp.BondNumPeerNotif = new(uint8) *cp.BondNumPeerNotif = *o.BondNumPeerNotif } if o.BondTLBDynamicLB != nil { cp.BondTLBDynamicLB = new(uint8) *cp.BondTLBDynamicLB = *o.BondTLBDynamicLB } if o.BondAllSlavesActive != nil { cp.BondAllSlavesActive = new(uint8) *cp.BondAllSlavesActive = *o.BondAllSlavesActive } if o.BondPeerNotifyDelay != nil { cp.BondPeerNotifyDelay = new(uint32) *cp.BondPeerNotifyDelay = *o.BondPeerNotifyDelay } if o.BondMissedMax != nil { cp.BondMissedMax = new(uint8) *cp.BondMissedMax = *o.BondMissedMax } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *BridgeConfigV1Alpha1. func (o *BridgeConfigV1Alpha1) DeepCopy() *BridgeConfigV1Alpha1 { var cp BridgeConfigV1Alpha1 = *o if o.HardwareAddressConfig != nil { cp.HardwareAddressConfig = make([]byte, len(o.HardwareAddressConfig)) copy(cp.HardwareAddressConfig, o.HardwareAddressConfig) } if o.BridgeLinks != nil { cp.BridgeLinks = make([]string, len(o.BridgeLinks)) copy(cp.BridgeLinks, o.BridgeLinks) } if o.BridgeSTP.BridgeSTPEnabled != nil { cp.BridgeSTP.BridgeSTPEnabled = new(bool) *cp.BridgeSTP.BridgeSTPEnabled = *o.BridgeSTP.BridgeSTPEnabled } if o.BridgeVLAN.BridgeVLANFiltering != nil { cp.BridgeVLAN.BridgeVLANFiltering = new(bool) *cp.BridgeVLAN.BridgeVLANFiltering = *o.BridgeVLAN.BridgeVLANFiltering } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *VRFConfigV1Alpha1. func (o *VRFConfigV1Alpha1) DeepCopy() *VRFConfigV1Alpha1 { var cp VRFConfigV1Alpha1 = *o if o.HardwareAddressConfig != nil { cp.HardwareAddressConfig = make([]byte, len(o.HardwareAddressConfig)) copy(cp.HardwareAddressConfig, o.HardwareAddressConfig) } if o.VRFLinks != nil { cp.VRFLinks = make([]string, len(o.VRFLinks)) copy(cp.VRFLinks, o.VRFLinks) } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *DefaultActionConfigV1Alpha1. func (o *DefaultActionConfigV1Alpha1) DeepCopy() *DefaultActionConfigV1Alpha1 { var cp DefaultActionConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *DHCPv4ConfigV1Alpha1. func (o *DHCPv4ConfigV1Alpha1) DeepCopy() *DHCPv4ConfigV1Alpha1 { var cp DHCPv4ConfigV1Alpha1 = *o if o.ConfigIgnoreHostname != nil { cp.ConfigIgnoreHostname = new(bool) *cp.ConfigIgnoreHostname = *o.ConfigIgnoreHostname } if o.ConfigClientIdentifier != nil { cp.ConfigClientIdentifier = new(nethelpers.ClientIdentifier) *cp.ConfigClientIdentifier = *o.ConfigClientIdentifier } if o.ConfigDUIDRaw != nil { cp.ConfigDUIDRaw = make([]byte, len(o.ConfigDUIDRaw)) copy(cp.ConfigDUIDRaw, o.ConfigDUIDRaw) } return &cp } // DeepCopy generates a deep copy of *DHCPv6ConfigV1Alpha1. func (o *DHCPv6ConfigV1Alpha1) DeepCopy() *DHCPv6ConfigV1Alpha1 { var cp DHCPv6ConfigV1Alpha1 = *o if o.ConfigIgnoreHostname != nil { cp.ConfigIgnoreHostname = new(bool) *cp.ConfigIgnoreHostname = *o.ConfigIgnoreHostname } if o.ConfigClientIdentifier != nil { cp.ConfigClientIdentifier = new(nethelpers.ClientIdentifier) *cp.ConfigClientIdentifier = *o.ConfigClientIdentifier } if o.ConfigDUIDRaw != nil { cp.ConfigDUIDRaw = make([]byte, len(o.ConfigDUIDRaw)) copy(cp.ConfigDUIDRaw, o.ConfigDUIDRaw) } return &cp } // DeepCopy generates a deep copy of *DummyLinkConfigV1Alpha1. func (o *DummyLinkConfigV1Alpha1) DeepCopy() *DummyLinkConfigV1Alpha1 { var cp DummyLinkConfigV1Alpha1 = *o if o.HardwareAddressConfig != nil { cp.HardwareAddressConfig = make([]byte, len(o.HardwareAddressConfig)) copy(cp.HardwareAddressConfig, o.HardwareAddressConfig) } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *EthernetConfigV1Alpha1. func (o *EthernetConfigV1Alpha1) DeepCopy() *EthernetConfigV1Alpha1 { var cp EthernetConfigV1Alpha1 = *o if o.FeaturesConfig != nil { cp.FeaturesConfig = make(map[string]bool, len(o.FeaturesConfig)) for k2, v2 := range o.FeaturesConfig { cp.FeaturesConfig[k2] = v2 } } if o.RingsConfig != nil { cp.RingsConfig = new(EthernetRingsConfig) *cp.RingsConfig = *o.RingsConfig if o.RingsConfig.RX != nil { cp.RingsConfig.RX = new(uint32) *cp.RingsConfig.RX = *o.RingsConfig.RX } if o.RingsConfig.TX != nil { cp.RingsConfig.TX = new(uint32) *cp.RingsConfig.TX = *o.RingsConfig.TX } if o.RingsConfig.RXMini != nil { cp.RingsConfig.RXMini = new(uint32) *cp.RingsConfig.RXMini = *o.RingsConfig.RXMini } if o.RingsConfig.RXJumbo != nil { cp.RingsConfig.RXJumbo = new(uint32) *cp.RingsConfig.RXJumbo = *o.RingsConfig.RXJumbo } if o.RingsConfig.RXBufLen != nil { cp.RingsConfig.RXBufLen = new(uint32) *cp.RingsConfig.RXBufLen = *o.RingsConfig.RXBufLen } if o.RingsConfig.CQESize != nil { cp.RingsConfig.CQESize = new(uint32) *cp.RingsConfig.CQESize = *o.RingsConfig.CQESize } if o.RingsConfig.TXPush != nil { cp.RingsConfig.TXPush = new(bool) *cp.RingsConfig.TXPush = *o.RingsConfig.TXPush } if o.RingsConfig.RXPush != nil { cp.RingsConfig.RXPush = new(bool) *cp.RingsConfig.RXPush = *o.RingsConfig.RXPush } if o.RingsConfig.TXPushBufLen != nil { cp.RingsConfig.TXPushBufLen = new(uint32) *cp.RingsConfig.TXPushBufLen = *o.RingsConfig.TXPushBufLen } if o.RingsConfig.TCPDataSplit != nil { cp.RingsConfig.TCPDataSplit = new(bool) *cp.RingsConfig.TCPDataSplit = *o.RingsConfig.TCPDataSplit } } if o.ChannelsConfig != nil { cp.ChannelsConfig = new(EthernetChannelsConfig) *cp.ChannelsConfig = *o.ChannelsConfig if o.ChannelsConfig.RX != nil { cp.ChannelsConfig.RX = new(uint32) *cp.ChannelsConfig.RX = *o.ChannelsConfig.RX } if o.ChannelsConfig.TX != nil { cp.ChannelsConfig.TX = new(uint32) *cp.ChannelsConfig.TX = *o.ChannelsConfig.TX } if o.ChannelsConfig.Other != nil { cp.ChannelsConfig.Other = new(uint32) *cp.ChannelsConfig.Other = *o.ChannelsConfig.Other } if o.ChannelsConfig.Combined != nil { cp.ChannelsConfig.Combined = new(uint32) *cp.ChannelsConfig.Combined = *o.ChannelsConfig.Combined } } if o.WakeOnLANConfig != nil { cp.WakeOnLANConfig = make([]nethelpers.WOLMode, len(o.WakeOnLANConfig)) copy(cp.WakeOnLANConfig, o.WakeOnLANConfig) } return &cp } // DeepCopy generates a deep copy of *HCloudVIPConfigV1Alpha1. func (o *HCloudVIPConfigV1Alpha1) DeepCopy() *HCloudVIPConfigV1Alpha1 { var cp HCloudVIPConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *HostnameConfigV1Alpha1. func (o *HostnameConfigV1Alpha1) DeepCopy() *HostnameConfigV1Alpha1 { var cp HostnameConfigV1Alpha1 = *o if o.ConfigAuto != nil { cp.ConfigAuto = new(nethelpers.AutoHostnameKind) *cp.ConfigAuto = *o.ConfigAuto } return &cp } // DeepCopy generates a deep copy of *KubeSpanConfigV1Alpha1. func (o *KubeSpanConfigV1Alpha1) DeepCopy() *KubeSpanConfigV1Alpha1 { var cp KubeSpanConfigV1Alpha1 = *o if o.ConfigEnabled != nil { cp.ConfigEnabled = new(bool) *cp.ConfigEnabled = *o.ConfigEnabled } if o.ConfigAdvertiseKubernetesNetworks != nil { cp.ConfigAdvertiseKubernetesNetworks = new(bool) *cp.ConfigAdvertiseKubernetesNetworks = *o.ConfigAdvertiseKubernetesNetworks } if o.ConfigAllowDownPeerBypass != nil { cp.ConfigAllowDownPeerBypass = new(bool) *cp.ConfigAllowDownPeerBypass = *o.ConfigAllowDownPeerBypass } if o.ConfigHarvestExtraEndpoints != nil { cp.ConfigHarvestExtraEndpoints = new(bool) *cp.ConfigHarvestExtraEndpoints = *o.ConfigHarvestExtraEndpoints } if o.ConfigMTU != nil { cp.ConfigMTU = new(uint32) *cp.ConfigMTU = *o.ConfigMTU } if o.ConfigFilters != nil { cp.ConfigFilters = new(KubeSpanFiltersConfig) *cp.ConfigFilters = *o.ConfigFilters if o.ConfigFilters.ConfigEndpoints != nil { cp.ConfigFilters.ConfigEndpoints = make([]string, len(o.ConfigFilters.ConfigEndpoints)) copy(cp.ConfigFilters.ConfigEndpoints, o.ConfigFilters.ConfigEndpoints) } if o.ConfigFilters.ConfigExcludeAdvertisedNetworks != nil { cp.ConfigFilters.ConfigExcludeAdvertisedNetworks = make([]Prefix, len(o.ConfigFilters.ConfigExcludeAdvertisedNetworks)) copy(cp.ConfigFilters.ConfigExcludeAdvertisedNetworks, o.ConfigFilters.ConfigExcludeAdvertisedNetworks) } } return &cp } // DeepCopy generates a deep copy of *KubespanEndpointsConfigV1Alpha1. func (o *KubespanEndpointsConfigV1Alpha1) DeepCopy() *KubespanEndpointsConfigV1Alpha1 { var cp KubespanEndpointsConfigV1Alpha1 = *o if o.ExtraAnnouncedEndpointsConfig != nil { cp.ExtraAnnouncedEndpointsConfig = make([]netip.AddrPort, len(o.ExtraAnnouncedEndpointsConfig)) copy(cp.ExtraAnnouncedEndpointsConfig, o.ExtraAnnouncedEndpointsConfig) } return &cp } // DeepCopy generates a deep copy of *Layer2VIPConfigV1Alpha1. func (o *Layer2VIPConfigV1Alpha1) DeepCopy() *Layer2VIPConfigV1Alpha1 { var cp Layer2VIPConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *LinkConfigV1Alpha1. func (o *LinkConfigV1Alpha1) DeepCopy() *LinkConfigV1Alpha1 { var cp LinkConfigV1Alpha1 = *o if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *LinkAliasConfigV1Alpha1. func (o *LinkAliasConfigV1Alpha1) DeepCopy() *LinkAliasConfigV1Alpha1 { var cp LinkAliasConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *ResolverConfigV1Alpha1. func (o *ResolverConfigV1Alpha1) DeepCopy() *ResolverConfigV1Alpha1 { var cp ResolverConfigV1Alpha1 = *o if o.ResolverNameservers != nil { cp.ResolverNameservers = make([]NameserverConfig, len(o.ResolverNameservers)) copy(cp.ResolverNameservers, o.ResolverNameservers) } if o.ResolverSearchDomains.SearchDomains != nil { cp.ResolverSearchDomains.SearchDomains = make([]string, len(o.ResolverSearchDomains.SearchDomains)) copy(cp.ResolverSearchDomains.SearchDomains, o.ResolverSearchDomains.SearchDomains) } if o.ResolverSearchDomains.SearchDisableDefault != nil { cp.ResolverSearchDomains.SearchDisableDefault = new(bool) *cp.ResolverSearchDomains.SearchDisableDefault = *o.ResolverSearchDomains.SearchDisableDefault } return &cp } // DeepCopy generates a deep copy of *RoutingRuleConfigV1Alpha1. func (o *RoutingRuleConfigV1Alpha1) DeepCopy() *RoutingRuleConfigV1Alpha1 { var cp RoutingRuleConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *RuleConfigV1Alpha1. func (o *RuleConfigV1Alpha1) DeepCopy() *RuleConfigV1Alpha1 { var cp RuleConfigV1Alpha1 = *o if o.PortSelector.Ports != nil { cp.PortSelector.Ports = make([]PortRange, len(o.PortSelector.Ports)) copy(cp.PortSelector.Ports, o.PortSelector.Ports) } if o.Ingress != nil { cp.Ingress = make([]IngressRule, len(o.Ingress)) copy(cp.Ingress, o.Ingress) } return &cp } // DeepCopy generates a deep copy of *StaticHostConfigV1Alpha1. func (o *StaticHostConfigV1Alpha1) DeepCopy() *StaticHostConfigV1Alpha1 { var cp StaticHostConfigV1Alpha1 = *o if o.Hostnames != nil { cp.Hostnames = make([]string, len(o.Hostnames)) copy(cp.Hostnames, o.Hostnames) } return &cp } // DeepCopy generates a deep copy of *TCPProbeConfigV1Alpha1. func (o *TCPProbeConfigV1Alpha1) DeepCopy() *TCPProbeConfigV1Alpha1 { var cp TCPProbeConfigV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *TimeSyncConfigV1Alpha1. func (o *TimeSyncConfigV1Alpha1) DeepCopy() *TimeSyncConfigV1Alpha1 { var cp TimeSyncConfigV1Alpha1 = *o if o.TimeEnabled != nil { cp.TimeEnabled = new(bool) *cp.TimeEnabled = *o.TimeEnabled } if o.TimeNTP != nil { cp.TimeNTP = new(NTPConfig) *cp.TimeNTP = *o.TimeNTP if o.TimeNTP.Servers != nil { cp.TimeNTP.Servers = make([]string, len(o.TimeNTP.Servers)) copy(cp.TimeNTP.Servers, o.TimeNTP.Servers) } } if o.TimePTP != nil { cp.TimePTP = new(PTPConfig) *cp.TimePTP = *o.TimePTP if o.TimePTP.Devices != nil { cp.TimePTP.Devices = make([]string, len(o.TimePTP.Devices)) copy(cp.TimePTP.Devices, o.TimePTP.Devices) } } return &cp } // DeepCopy generates a deep copy of *VLANConfigV1Alpha1. func (o *VLANConfigV1Alpha1) DeepCopy() *VLANConfigV1Alpha1 { var cp VLANConfigV1Alpha1 = *o if o.VLANModeConfig != nil { cp.VLANModeConfig = new(nethelpers.VLANProtocol) *cp.VLANModeConfig = *o.VLANModeConfig } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } // DeepCopy generates a deep copy of *WireguardConfigV1Alpha1. func (o *WireguardConfigV1Alpha1) DeepCopy() *WireguardConfigV1Alpha1 { var cp WireguardConfigV1Alpha1 = *o if o.WireguardPeers != nil { cp.WireguardPeers = make([]WireguardPeer, len(o.WireguardPeers)) copy(cp.WireguardPeers, o.WireguardPeers) for i2 := range o.WireguardPeers { if o.WireguardPeers[i2].WireguardAllowedIPs != nil { cp.WireguardPeers[i2].WireguardAllowedIPs = make([]Prefix, len(o.WireguardPeers[i2].WireguardAllowedIPs)) copy(cp.WireguardPeers[i2].WireguardAllowedIPs, o.WireguardPeers[i2].WireguardAllowedIPs) } } } if o.CommonLinkConfig.LinkUp != nil { cp.CommonLinkConfig.LinkUp = new(bool) *cp.CommonLinkConfig.LinkUp = *o.CommonLinkConfig.LinkUp } if o.CommonLinkConfig.LinkAddresses != nil { cp.CommonLinkConfig.LinkAddresses = make([]AddressConfig, len(o.CommonLinkConfig.LinkAddresses)) copy(cp.CommonLinkConfig.LinkAddresses, o.CommonLinkConfig.LinkAddresses) for i3 := range o.CommonLinkConfig.LinkAddresses { if o.CommonLinkConfig.LinkAddresses[i3].AddressPriority != nil { cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = new(uint32) *cp.CommonLinkConfig.LinkAddresses[i3].AddressPriority = *o.CommonLinkConfig.LinkAddresses[i3].AddressPriority } } } if o.CommonLinkConfig.LinkRoutes != nil { cp.CommonLinkConfig.LinkRoutes = make([]RouteConfig, len(o.CommonLinkConfig.LinkRoutes)) copy(cp.CommonLinkConfig.LinkRoutes, o.CommonLinkConfig.LinkRoutes) } if o.CommonLinkConfig.LinkMulticast != nil { cp.CommonLinkConfig.LinkMulticast = new(bool) *cp.CommonLinkConfig.LinkMulticast = *o.CommonLinkConfig.LinkMulticast } return &cp } ================================================ FILE: pkg/machinery/config/types/network/default_action_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DefaultActionConfig is a default action config document kind. const DefaultActionConfig = "NetworkDefaultActionConfig" func init() { registry.Register(DefaultActionConfig, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &DefaultActionConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkRuleConfigDefaultAction = &DefaultActionConfigV1Alpha1{} _ config.NetworkRuleConfigSignal = &DefaultActionConfigV1Alpha1{} ) // DefaultActionConfigV1Alpha1 is a ingress firewall default action configuration document. // // examples: // - value: exampleDefaultActionConfigV1Alpha1() // alias: NetworkDefaultActionConfig // schemaRoot: true // schemaMeta: v1alpha1/NetworkDefaultActionConfig type DefaultActionConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Default action for all not explicitly configured ingress traffic: accept or block. // values: // - "accept" // - "block" Ingress nethelpers.DefaultAction `yaml:"ingress"` } // NewDefaultActionConfigV1Alpha1 creates a new DefaultActionConfig config document. func NewDefaultActionConfigV1Alpha1() *DefaultActionConfigV1Alpha1 { return &DefaultActionConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: DefaultActionConfig, MetaAPIVersion: "v1alpha1", }, } } func exampleDefaultActionConfigV1Alpha1() *DefaultActionConfigV1Alpha1 { cfg := NewDefaultActionConfigV1Alpha1() cfg.Ingress = nethelpers.DefaultActionAccept return cfg } // Clone implements config.Document interface. func (s *DefaultActionConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // NetworkRuleConfigSignal implements config.NetworkRuleConfigSignal interface. func (s *DefaultActionConfigV1Alpha1) NetworkRuleConfigSignal() {} // DefaultAction implements config.NetworkRuleConfigDefaultAction interface. func (s *DefaultActionConfigV1Alpha1) DefaultAction() nethelpers.DefaultAction { return s.Ingress } ================================================ FILE: pkg/machinery/config/types/network/default_action_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/defaultactionconfig.yaml var expectedDefaultActionConfigDocument []byte func TestDefaultActionConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewDefaultActionConfigV1Alpha1() cfg.Ingress = nethelpers.DefaultActionBlock marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedDefaultActionConfigDocument, marshaled) } func TestDefaultActionConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedDefaultActionConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.DefaultActionConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.DefaultActionConfig, }, Ingress: nethelpers.DefaultActionBlock, }, docs[0]) } ================================================ FILE: pkg/machinery/config/types/network/dhcp4.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network //docgen:jsonschema import ( "errors" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DHCPv4Kind is a DHCPv4 config document kind. const DHCPv4Kind = "DHCPv4Config" func init() { registry.Register(DHCPv4Kind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &DHCPv4ConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkDHCPv4Config = &DHCPv4ConfigV1Alpha1{} _ config.NamedDocument = &DHCPv4ConfigV1Alpha1{} _ config.Validator = &DHCPv4ConfigV1Alpha1{} ) // DHCPv4ConfigV1Alpha1 is a config document to configure DHCPv4 on a network link. // // examples: // - value: exampleDHCPv4ConfigV1Alpha1() // alias: DHCPv4Config // schemaRoot: true // schemaMeta: v1alpha1/DHCPv4Config type DHCPv4ConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the link (interface). // // examples: // - value: > // "enp0s2" // schemaRequired: true MetaName string `yaml:"name"` // description: | // An optional metric for the routes received from the DHCP server. // // Lower values indicate higher priority. // Default value is 1024. ConfigRouteMetric uint32 `yaml:"routeMetric,omitempty"` // description: | // Ignore hostname received from the DHCP server. ConfigIgnoreHostname *bool `yaml:"ignoreHostname,omitempty"` // description: | // Client identifier to use when communicating with DHCP servers. // // Defaults to 'mac' if not set. // values: // - "none" // - "mac" // - "duid" ConfigClientIdentifier *nethelpers.ClientIdentifier `yaml:"clientIdentifier,omitempty"` // description: | // Raw value of the DUID to use as client identifier. // // This field is only used if 'clientIdentifier' is set to 'duid'. // examples: // - value: > // "00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45" // schema: // type: string // pattern: ^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$ ConfigDUIDRaw nethelpers.HardwareAddr `yaml:"duidRaw,omitempty"` } // NewDHCPv4ConfigV1Alpha1 creates a new DHCPv4Config config document. func NewDHCPv4ConfigV1Alpha1(name string) *DHCPv4ConfigV1Alpha1 { return &DHCPv4ConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: DHCPv4Kind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleDHCPv4ConfigV1Alpha1() *DHCPv4ConfigV1Alpha1 { cfg := NewDHCPv4ConfigV1Alpha1("enp0s2") cfg.ConfigClientIdentifier = new(nethelpers.ClientIdentifierMAC) return cfg } // Clone implements config.Document interface. func (s *DHCPv4ConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *DHCPv4ConfigV1Alpha1) Name() string { return s.MetaName } // NetworkDHCPConfig implements config.NetworkDHCPConfig interface. func (s *DHCPv4ConfigV1Alpha1) NetworkDHCPConfig() {} // NetworkDHCPv4Config implements config.NetworkDHCPv4Config interface. func (s *DHCPv4ConfigV1Alpha1) NetworkDHCPv4Config() {} // RouteMetric returns the route metric. func (s *DHCPv4ConfigV1Alpha1) RouteMetric() optional.Optional[uint32] { if s.ConfigRouteMetric == 0 { return optional.None[uint32]() } return optional.Some(s.ConfigRouteMetric) } // IgnoreHostname returns whether to ignore hostname from DHCP server. func (s *DHCPv4ConfigV1Alpha1) IgnoreHostname() optional.Optional[bool] { if s.ConfigIgnoreHostname == nil { return optional.None[bool]() } return optional.Some(*s.ConfigIgnoreHostname) } // ClientIdentifier returns the client identifier. func (s *DHCPv4ConfigV1Alpha1) ClientIdentifier() nethelpers.ClientIdentifier { if s.ConfigClientIdentifier == nil { return nethelpers.ClientIdentifierMAC } return *s.ConfigClientIdentifier } // DUIDRaw returns the DUID raw value. func (s *DHCPv4ConfigV1Alpha1) DUIDRaw() optional.Optional[nethelpers.HardwareAddr] { if len(s.ConfigDUIDRaw) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.ConfigDUIDRaw) } // Validate implements config.Validator interface. func (s *DHCPv4ConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if len(s.ConfigDUIDRaw) > 0 && pointer.SafeDeref(s.ConfigClientIdentifier) != nethelpers.ClientIdentifierDUID { errs = errors.Join(errs, errors.New("duidRaw can only be set if clientIdentifier is 'duid'")) } if pointer.SafeDeref(s.ConfigClientIdentifier) == nethelpers.ClientIdentifierDUID && len(s.ConfigDUIDRaw) == 0 { errs = errors.Join(errs, errors.New("duidRaw must be set if clientIdentifier is 'duid'")) } return warnings, errs } ================================================ FILE: pkg/machinery/config/types/network/dhcp4_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/dhcpv4config.yaml var expectedDHCPv4ConfigDocument []byte func TestDHCPv4ConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewDHCPv4ConfigV1Alpha1("enp0s3") cfg.ConfigRouteMetric = 512 cfg.ConfigIgnoreHostname = new(true) cfg.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) cfg.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45} marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedDHCPv4ConfigDocument, marshaled) } func TestDHCPv4ConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedDHCPv4ConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.DHCPv4ConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.DHCPv4Kind, }, MetaName: "enp0s3", ConfigRouteMetric: 512, ConfigIgnoreHostname: new(true), ConfigClientIdentifier: new(nethelpers.ClientIdentifierDUID), ConfigDUIDRaw: nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45}, }, docs[0]) } func TestDHCPv4ConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.DHCPv4ConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "valid config with duidRaw", cfg: func() *network.DHCPv4ConfigV1Alpha1 { c := network.NewDHCPv4ConfigV1Alpha1("enp0s3") c.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) c.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45} return c }, }, { name: "invalid config missing duidRaw", cfg: func() *network.DHCPv4ConfigV1Alpha1 { c := network.NewDHCPv4ConfigV1Alpha1("enp0s3") c.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) return c }, expectedError: "duidRaw must be set if clientIdentifier is 'duid'", }, { name: "invalid config duidRaw set without duid client identifier", cfg: func() *network.DHCPv4ConfigV1Alpha1 { c := network.NewDHCPv4ConfigV1Alpha1("enp0s3") c.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45} return c }, expectedError: "duidRaw can only be set if clientIdentifier is 'duid'", }, { name: "empty", cfg: func() *network.DHCPv4ConfigV1Alpha1 { return network.NewDHCPv4ConfigV1Alpha1("") }, expectedError: "name must be specified", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/dhcp6.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network //docgen:jsonschema import ( "errors" "github.com/siderolabs/gen/optional" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DHCPv6Kind is a DHCPv6 config document kind. const DHCPv6Kind = "DHCPv6Config" func init() { registry.Register(DHCPv6Kind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &DHCPv6ConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkDHCPv6Config = &DHCPv6ConfigV1Alpha1{} _ config.NamedDocument = &DHCPv6ConfigV1Alpha1{} _ config.Validator = &DHCPv6ConfigV1Alpha1{} ) // DHCPv6ConfigV1Alpha1 is a config document to configure DHCPv6 on a network link. // // examples: // - value: exampleDHCPv6ConfigV1Alpha1() // alias: DHCPv6Config // schemaRoot: true // schemaMeta: v1alpha1/DHCPv6Config type DHCPv6ConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the link (interface). // // examples: // - value: > // "enp0s2" // schemaRequired: true MetaName string `yaml:"name"` // description: | // An optional metric for the routes received from the DHCP server. // // Lower values indicate higher priority. // Default value is 1024. ConfigRouteMetric uint32 `yaml:"routeMetric,omitempty"` // description: | // Ignore hostname received from the DHCP server. ConfigIgnoreHostname *bool `yaml:"ignoreHostname,omitempty"` // description: | // Client identifier to use when communicating with DHCP servers. // // Defaults to 'mac' if not set. // values: // - "none" // - "mac" // - "duid" ConfigClientIdentifier *nethelpers.ClientIdentifier `yaml:"clientIdentifier,omitempty"` // description: | // Raw value of the DUID to use as client identifier. // // This field is only used if 'clientIdentifier' is set to 'duid'. // examples: // - value: > // "00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45" // schema: // type: string // pattern: ^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$ ConfigDUIDRaw nethelpers.HardwareAddr `yaml:"duidRaw,omitempty"` } // NewDHCPv6ConfigV1Alpha1 creates a new DHCPv6Config config document. func NewDHCPv6ConfigV1Alpha1(name string) *DHCPv6ConfigV1Alpha1 { return &DHCPv6ConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: DHCPv6Kind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleDHCPv6ConfigV1Alpha1() *DHCPv6ConfigV1Alpha1 { cfg := NewDHCPv6ConfigV1Alpha1("enp0s2") return cfg } // Clone implements config.Document interface. func (s *DHCPv6ConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *DHCPv6ConfigV1Alpha1) Name() string { return s.MetaName } // NetworkDHCPConfig implements config.NetworkDHCPConfig interface. func (s *DHCPv6ConfigV1Alpha1) NetworkDHCPConfig() {} // NetworkDHCPv6Config implements config.NetworkDHCPv6Config interface. func (s *DHCPv6ConfigV1Alpha1) NetworkDHCPv6Config() {} // RouteMetric returns the route metric. func (s *DHCPv6ConfigV1Alpha1) RouteMetric() optional.Optional[uint32] { if s.ConfigRouteMetric == 0 { return optional.None[uint32]() } return optional.Some(s.ConfigRouteMetric) } // IgnoreHostname returns whether to ignore hostname from DHCP server. func (s *DHCPv6ConfigV1Alpha1) IgnoreHostname() optional.Optional[bool] { if s.ConfigIgnoreHostname == nil { return optional.None[bool]() } return optional.Some(*s.ConfigIgnoreHostname) } // ClientIdentifier returns the client identifier. func (s *DHCPv6ConfigV1Alpha1) ClientIdentifier() nethelpers.ClientIdentifier { if s.ConfigClientIdentifier == nil { return nethelpers.ClientIdentifierMAC } return *s.ConfigClientIdentifier } // DUIDRaw returns the DUID raw value. func (s *DHCPv6ConfigV1Alpha1) DUIDRaw() optional.Optional[nethelpers.HardwareAddr] { if len(s.ConfigDUIDRaw) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.ConfigDUIDRaw) } // Validate implements config.Validator interface. func (s *DHCPv6ConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if len(s.ConfigDUIDRaw) > 0 && pointer.SafeDeref(s.ConfigClientIdentifier) != nethelpers.ClientIdentifierDUID { errs = errors.Join(errs, errors.New("duidRaw can only be set if clientIdentifier is 'duid'")) } if pointer.SafeDeref(s.ConfigClientIdentifier) == nethelpers.ClientIdentifierDUID && len(s.ConfigDUIDRaw) == 0 { errs = errors.Join(errs, errors.New("duidRaw must be set if clientIdentifier is 'duid'")) } return warnings, errs } ================================================ FILE: pkg/machinery/config/types/network/dhcp6_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/dhcpv6config.yaml var expectedDHCPv6ConfigDocument []byte func TestDHCPv6ConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewDHCPv6ConfigV1Alpha1("enp0s3") cfg.ConfigClientIdentifier = new(nethelpers.ClientIdentifierMAC) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedDHCPv6ConfigDocument, marshaled) } func TestDHCPv6ConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedDHCPv6ConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.DHCPv6ConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.DHCPv6Kind, }, MetaName: "enp0s3", ConfigClientIdentifier: new(nethelpers.ClientIdentifierMAC), }, docs[0]) } func TestDHCPv6ConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.DHCPv6ConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "valid config with duidRaw", cfg: func() *network.DHCPv6ConfigV1Alpha1 { c := network.NewDHCPv6ConfigV1Alpha1("enp0s3") c.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) c.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45} return c }, }, { name: "invalid config missing duidRaw", cfg: func() *network.DHCPv6ConfigV1Alpha1 { c := network.NewDHCPv6ConfigV1Alpha1("enp0s3") c.ConfigClientIdentifier = new(nethelpers.ClientIdentifierDUID) return c }, expectedError: "duidRaw must be set if clientIdentifier is 'duid'", }, { name: "invalid config duidRaw set without duid client identifier", cfg: func() *network.DHCPv6ConfigV1Alpha1 { c := network.NewDHCPv6ConfigV1Alpha1("enp0s3") c.ConfigDUIDRaw = nethelpers.HardwareAddr{0x00, 0x01, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45} return c }, expectedError: "duidRaw can only be set if clientIdentifier is 'duid'", }, { name: "empty", cfg: func() *network.DHCPv6ConfigV1Alpha1 { return network.NewDHCPv6ConfigV1Alpha1("") }, expectedError: "name must be specified", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/dummy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "net/netip" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DummyLinkKind is a DummyLink config document kind. const DummyLinkKind = "DummyLinkConfig" func init() { registry.Register(DummyLinkKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &DummyLinkConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkDummyLinkConfig = &DummyLinkConfigV1Alpha1{} _ config.ConflictingDocument = &DummyLinkConfigV1Alpha1{} _ config.NamedDocument = &DummyLinkConfigV1Alpha1{} _ config.Validator = &DummyLinkConfigV1Alpha1{} ) // DummyLinkConfigV1Alpha1 is a config document to create a dummy (virtual) network link. // // examples: // - value: exampleDummyLinkConfigV1Alpha1() // alias: DummyLinkConfig // schemaRoot: true // schemaMeta: v1alpha1/DummyLinkConfig type DummyLinkConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the dummy link (interface). // // examples: // - value: > // "dummy1" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Override the hardware (MAC) address of the link. // // examples: // - value: > // nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70} // schema: // type: string // pattern: ^[0-9a-f:]+$ HardwareAddressConfig nethelpers.HardwareAddr `yaml:"hardwareAddr,omitempty"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // NewDummyLinkConfigV1Alpha1 creates a new DummyLinkConfig config document. func NewDummyLinkConfigV1Alpha1(name string) *DummyLinkConfigV1Alpha1 { return &DummyLinkConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: DummyLinkKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleDummyLinkConfigV1Alpha1() *DummyLinkConfigV1Alpha1 { cfg := NewDummyLinkConfigV1Alpha1("dummy1") cfg.LinkAddresses = []AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, } return cfg } // Clone implements config.Document interface. func (s *DummyLinkConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *DummyLinkConfigV1Alpha1) Name() string { return s.MetaName } // DummyLinkConfig implements NetworkDummyLinkConfig interface. func (s *DummyLinkConfigV1Alpha1) DummyLinkConfig() {} // HardwareAddress implements NetworkDummyLinkConfig interface. func (s *DummyLinkConfigV1Alpha1) HardwareAddress() optional.Optional[nethelpers.HardwareAddr] { if len(s.HardwareAddressConfig) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.HardwareAddressConfig) } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *DummyLinkConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(DummyLinkKind) } // Validate implements config.Validator interface. func (s *DummyLinkConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } ================================================ FILE: pkg/machinery/config/types/network/dummy_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/dummylinkconfig.yaml var expectedDummyLinkConfigDocument []byte func TestDummyLinkConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewDummyLinkConfigV1Alpha1("dummy1") cfg.HardwareAddressConfig = nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70} cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedDummyLinkConfigDocument, marshaled) } func TestDummyLinkConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedDummyLinkConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.DummyLinkConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.DummyLinkKind, }, MetaName: "dummy1", HardwareAddressConfig: nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70}, CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, }, }, }, docs[0]) } ================================================ FILE: pkg/machinery/config/types/network/ethernet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // EthernetKind is a Ethernet config document kind. const EthernetKind = "EthernetConfig" func init() { registry.Register(EthernetKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &EthernetConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.EthernetConfig = &EthernetConfigV1Alpha1{} _ config.NamedDocument = &EthernetConfigV1Alpha1{} _ config.Validator = &EthernetConfigV1Alpha1{} ) // EthernetConfigV1Alpha1 is a config document to configure Ethernet interfaces. // // examples: // - value: exampleEthernetConfigV1Alpha1() // alias: EthernetConfig // schemaRoot: true // schemaMeta: v1alpha1/EthernetConfig type EthernetConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the link (interface). // schemaRequired: true MetaName string `yaml:"name"` // description: | // Configuration for Ethernet features. // // Set of features available and whether they can be enabled or disabled is driver specific. // Use `talosctl get ethernetstatus -o yaml` to get the list of available features and // their current status. FeaturesConfig map[string]bool `yaml:"features,omitempty"` // description: | // Configuration for Ethernet link rings. // // This is similar to `ethtool -G` command. RingsConfig *EthernetRingsConfig `yaml:"rings,omitempty"` // description: | // Configuration for Ethernet link channels. // // This is similar to `ethtool -L` command. ChannelsConfig *EthernetChannelsConfig `yaml:"channels,omitempty"` // description: | // Wake-on-LAN modes to enable. // // If this field is omitted, Wake-on-LAN configuration is not changed. // An empty list disables Wake-on-LAN. // // This is similar to `ethtool -s wol ` command. // values: // - "phy" // - "unicast" // - "multicast" // - "broadcast" // - "arp" // - "magic" // - "magicsecure" // - "filter" // schema: // type: array // items: // type: string // examples: // - value: > // []nethelpers.WOLMode{nethelpers.WOLModeUnicast, nethelpers.WOLModeMagic} WakeOnLANConfig []nethelpers.WOLMode `yaml:"wakeOnLan,omitempty"` } // EthernetRingsConfig is a configuration for Ethernet link rings. type EthernetRingsConfig struct { // description: | // Number of RX rings. RX *uint32 `yaml:"rx,omitempty"` // description: | // Number of TX rings. TX *uint32 `yaml:"tx,omitempty"` // description: | // Number of RX mini rings. RXMini *uint32 `yaml:"rx-mini,omitempty"` // description: | // Number of RX jumbo rings. RXJumbo *uint32 `yaml:"rx-jumbo,omitempty"` // description: | // RX buffer length. RXBufLen *uint32 `yaml:"rx-buf-len,omitempty"` // description: | // CQE size. CQESize *uint32 `yaml:"cqe-size,omitempty"` // description: | // TX push enabled. TXPush *bool `yaml:"tx-push,omitempty"` // description: | // RX push enabled. RXPush *bool `yaml:"rx-push,omitempty"` // description: | // TX push buffer length. TXPushBufLen *uint32 `yaml:"tx-push-buf-len,omitempty"` // description: | // TCP data split enabled. TCPDataSplit *bool `yaml:"tcp-data-split,omitempty"` } // EthernetChannelsConfig is a configuration for Ethernet link channels. type EthernetChannelsConfig struct { // description: | // Number of RX channels. RX *uint32 `yaml:"rx,omitempty"` // description: | // Number of TX channels. TX *uint32 `yaml:"tx,omitempty"` // description: | // Number of other channels. Other *uint32 `yaml:"other,omitempty"` // description: | // Number of combined channels. Combined *uint32 `yaml:"combined,omitempty"` } // NewEthernetConfigV1Alpha1 creates a new EthernetConfig config document. func NewEthernetConfigV1Alpha1(name string) *EthernetConfigV1Alpha1 { return &EthernetConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: EthernetKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleEthernetConfigV1Alpha1() *EthernetConfigV1Alpha1 { cfg := NewEthernetConfigV1Alpha1("enp0s2") cfg.RingsConfig = &EthernetRingsConfig{ RX: new(uint32(256)), } cfg.FeaturesConfig = map[string]bool{ "tx-tcp-segmentation": false, } cfg.ChannelsConfig = &EthernetChannelsConfig{ RX: new(uint32(4)), } return cfg } // Clone implements config.Document interface. func (s *EthernetConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *EthernetConfigV1Alpha1) Name() string { return s.MetaName } // Rings implements config.EthernetConfig interface. func (s *EthernetConfigV1Alpha1) Rings() config.EthernetRingsConfig { return config.EthernetRingsConfig(pointer.SafeDeref(s.RingsConfig)) } // Channels implements config.EthernetConfig interface. func (s *EthernetConfigV1Alpha1) Channels() config.EthernetChannelsConfig { return config.EthernetChannelsConfig(pointer.SafeDeref(s.ChannelsConfig)) } // Features implements config.EthernetConfig interface. func (s *EthernetConfigV1Alpha1) Features() map[string]bool { return s.FeaturesConfig } // WakeOnLAN implements config.EthernetConfig interface. func (s *EthernetConfigV1Alpha1) WakeOnLAN() []nethelpers.WOLMode { return s.WakeOnLANConfig } // Validate implements config.Validator interface. func (s *EthernetConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.MetaName == "" { return nil, errors.New("name is required") } return nil, nil } ================================================ FILE: pkg/machinery/config/types/network/ethernet_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/ethernetconfig.yaml var expectedEthernetConfigDocument []byte func TestEthernetConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewEthernetConfigV1Alpha1("enp0s1") cfg.RingsConfig = &network.EthernetRingsConfig{ RX: new(uint32(16)), } cfg.FeaturesConfig = map[string]bool{ "tx-checksum-ipv4": true, } cfg.ChannelsConfig = &network.EthernetChannelsConfig{ Combined: new(uint32(1)), } cfg.WakeOnLANConfig = []nethelpers.WOLMode{ nethelpers.WOLModeUnicast, nethelpers.WOLModeMulticast, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedEthernetConfigDocument, marshaled) } func TestEthernetConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedEthernetConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.EthernetConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.EthernetKind, }, MetaName: "enp0s1", FeaturesConfig: map[string]bool{ "tx-checksum-ipv4": true, }, RingsConfig: &network.EthernetRingsConfig{ RX: new(uint32(16)), }, ChannelsConfig: &network.EthernetChannelsConfig{ Combined: new(uint32(1)), }, WakeOnLANConfig: []nethelpers.WOLMode{ nethelpers.WOLModeUnicast, nethelpers.WOLModeMulticast, }, }, docs[0]) } func TestEthernetValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.EthernetConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.EthernetConfigV1Alpha1 { return network.NewEthernetConfigV1Alpha1("") }, expectedError: "name is required", }, { name: "valid", cfg: func() *network.EthernetConfigV1Alpha1 { cfg := network.NewEthernetConfigV1Alpha1("enp0s1") cfg.FeaturesConfig = map[string]bool{ "tx-checksum-ipv4": true, } cfg.RingsConfig = &network.EthernetRingsConfig{ RX: new(uint32(16)), } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/hcloud_vip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // HCloudVIPKind is a HCloudVIP config document kind. const HCloudVIPKind = "HCloudVIPConfig" func init() { registry.Register(HCloudVIPKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &HCloudVIPConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkHCloudVIPConfig = &HCloudVIPConfigV1Alpha1{} _ config.ConflictingDocument = &HCloudVIPConfigV1Alpha1{} _ config.NamedDocument = &HCloudVIPConfigV1Alpha1{} _ config.Validator = &HCloudVIPConfigV1Alpha1{} ) // HCloudVIPConfigV1Alpha1 is a config document to configure virtual IP using Hetzner Cloud APIs for announcement. // // description: | // Virtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server. // Any other use cases are not supported and may lead to unexpected behavior. // Virtual IP will be announced from only one node at a time using Hetzner Cloud APIs. // examples: // - value: exampleHCloudVIPConfigV1Alpha1() // alias: HCloudVIPConfig // schemaRoot: true // schemaMeta: v1alpha1/HCloudVIPConfig type HCloudVIPConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // IP address to be advertised as a Layer 2 VIP. // // examples: // - value: > // "192.168.100.1" // - value: > // "fd00::1" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Name of the link to assign the VIP to. // // Selector must match exactly one link, otherwise an error is returned. // If multiple selectors match the same link, the first one is used. LinkName string `yaml:"link"` // description: | // Specifies the Hetzner Cloud API Token. APIToken string `yaml:"apiToken"` } // NewHCloudVIPConfigV1Alpha1 creates a new HCloudVIPConfig config document. func NewHCloudVIPConfigV1Alpha1(name string) *HCloudVIPConfigV1Alpha1 { return &HCloudVIPConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: HCloudVIPKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleHCloudVIPConfigV1Alpha1() *HCloudVIPConfigV1Alpha1 { cfg := NewHCloudVIPConfigV1Alpha1("int0") cfg.LinkName = "enp0s2" cfg.APIToken = "my-secret-token" return cfg } // Clone implements config.Document interface. func (s *HCloudVIPConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *HCloudVIPConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *HCloudVIPConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } else if _, err := netip.ParseAddr(s.MetaName); err != nil { errs = errors.Join(errs, fmt.Errorf("name must be a valid IP address: %w", err)) } if s.LinkName == "" { errs = errors.Join(errs, errors.New("link must be specified")) } if s.APIToken == "" { errs = errors.Join(errs, errors.New("apiToken must be specified")) } return warnings, errs } // Link implements config.NetworkHCloudVIPConfig interface. func (s *HCloudVIPConfigV1Alpha1) Link() string { return s.LinkName } // VIP implements config.NetworkHCloudVIPConfig interface. func (s *HCloudVIPConfigV1Alpha1) VIP() netip.Addr { addr, _ := netip.ParseAddr(s.MetaName) //nolint:errcheck // already validated return addr } // HCloudAPIToken implements config.NetworkHCloudVIPConfig interface. func (s *HCloudVIPConfigV1Alpha1) HCloudAPIToken() string { return s.APIToken } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *HCloudVIPConfigV1Alpha1) ConflictsWithKinds() []string { return []string{Layer2VIPKind} } ================================================ FILE: pkg/machinery/config/types/network/hcloud_vip_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/hcloudvipconfig.yaml var expectedHCloudVIPConfigDocument []byte func TestHCloudVIPConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewHCloudVIPConfigV1Alpha1("1.2.3.4") cfg.LinkName = "net33" cfg.APIToken = "s3cr3t-t0k3n" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedHCloudVIPConfigDocument, marshaled) } func TestHCloudVIPConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedHCloudVIPConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) c := &network.HCloudVIPConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.HCloudVIPKind, }, MetaName: "1.2.3.4", LinkName: "net33", APIToken: "s3cr3t-t0k3n", } assert.Equal(t, c, docs[0]) } func TestHCloudVIPValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.HCloudVIPConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.HCloudVIPConfigV1Alpha1 { return network.NewHCloudVIPConfigV1Alpha1("") }, expectedError: "name must be specified\nlink must be specified\napiToken must be specified", }, { name: "no link name and token", cfg: func() *network.HCloudVIPConfigV1Alpha1 { return network.NewHCloudVIPConfigV1Alpha1("1.1.1.1") }, expectedError: "link must be specified\napiToken must be specified", }, { name: "invalid IP", cfg: func() *network.HCloudVIPConfigV1Alpha1 { c := network.NewHCloudVIPConfigV1Alpha1("net32") c.LinkName = "net32" c.APIToken = "foo" return c }, expectedError: "name must be a valid IP address: ParseAddr(\"net32\"): unable to parse IP", }, { name: "valid", cfg: func() *network.HCloudVIPConfigV1Alpha1 { c := network.NewHCloudVIPConfigV1Alpha1("fd00::1") c.LinkName = "net33" c.APIToken = "my-secret-token" return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/hostname.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // HostnameKind is a Hostname config document kind. const HostnameKind = "HostnameConfig" func init() { registry.Register(HostnameKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &HostnameConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkHostnameConfig = &HostnameConfigV1Alpha1{} _ config.Validator = &HostnameConfigV1Alpha1{} _ container.V1Alpha1ConflictValidator = &HostnameConfigV1Alpha1{} ) // HostnameConfigV1Alpha1 is a config document to configure the hostname: either a static hostname or an automatically generated hostname. // // examples: // - value: exampleHostnameConfigV1Alpha1() // - value: exampleHostnameConfigV1Alpha2() // alias: HostnameConfig // schemaRoot: true // schemaMeta: v1alpha1/HostnameConfig type HostnameConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // A method to automatically generate a hostname for the machine. // // There are two methods available: // - `stable` - generates a stable hostname based on machine identity // - `off` - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc). // // Automatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc. // Conflicts with `hostname` field. // values: // - "stable" // - "off" ConfigAuto *nethelpers.AutoHostnameKind `yaml:"auto,omitempty"` // description: | // A static hostname to set for the machine. // // This hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc. // Conflicts with `auto` field. // examples: // - value: > // "controlplane1" // - value: > // "controlplane1.example.org" ConfigHostname string `yaml:"hostname,omitempty"` } // NewHostnameConfigV1Alpha1 creates a new HostnameConfig config document. func NewHostnameConfigV1Alpha1() *HostnameConfigV1Alpha1 { return &HostnameConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: HostnameKind, MetaAPIVersion: "v1alpha1", }, } } func exampleHostnameConfigV1Alpha1() *HostnameConfigV1Alpha1 { cfg := NewHostnameConfigV1Alpha1() cfg.ConfigHostname = "worker-33" return cfg } func exampleHostnameConfigV1Alpha2() *HostnameConfigV1Alpha1 { cfg := NewHostnameConfigV1Alpha1() cfg.ConfigAuto = new(nethelpers.AutoHostnameKindStable) return cfg } // Clone implements config.Document interface. func (s *HostnameConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo func (s *HostnameConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if pointer.SafeDeref(s.ConfigAuto) == nethelpers.AutoHostnameKindOff && s.ConfigHostname == "" { errs = errors.Join(errs, errors.New("either 'auto' or 'hostname' must be set")) } if pointer.SafeDeref(s.ConfigAuto) != nethelpers.AutoHostnameKindOff && s.ConfigHostname != "" { errs = errors.Join(errs, errors.New("'auto' and 'hostname' cannot be set at the same time")) } switch pointer.SafeDeref(s.ConfigAuto) { case nethelpers.AutoHostnameKindOff, nethelpers.AutoHostnameKindStable: // valid values case nethelpers.AutoHostnameKindAddr: fallthrough default: errs = errors.Join(errs, fmt.Errorf("invalid value for 'auto': %s", s.ConfigAuto)) } if s.ConfigHostname != "" { if len(s.ConfigHostname) > 253 { errs = errors.Join(errs, fmt.Errorf("fqdn is too long: %d", len(s.ConfigHostname))) } hostname, _, _ := strings.Cut(s.ConfigHostname, ".") if len(hostname) == 0 || len(hostname) > 63 { errs = errors.Join(errs, fmt.Errorf("invalid hostname %q", hostname)) } } return nil, errs } // V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface. func (s *HostnameConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error { if v1alpha1Cfg.Hostname() != "" { return errors.New("static hostname is already set in v1alpha1 config") } if v1alpha1Cfg.AutoHostname() != nethelpers.AutoHostnameKindAddr { return errors.New("stable hostname is already set in v1alpha1 config") } return nil } // Hostname implements config.NetworkHostnameConfig interface. func (s *HostnameConfigV1Alpha1) Hostname() string { return s.ConfigHostname } // AutoHostname implements config.NetworkHostnameConfig interface. func (s *HostnameConfigV1Alpha1) AutoHostname() nethelpers.AutoHostnameKind { return pointer.SafeDeref(s.ConfigAuto) } ================================================ FILE: pkg/machinery/config/types/network/hostname_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/hostnameconfig.yaml var expectedHostnameConfigDocument []byte func TestHostnameConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigAuto = new(nethelpers.AutoHostnameKindStable) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedHostnameConfigDocument, marshaled) } func TestHostnameConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.HostnameConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: network.NewHostnameConfigV1Alpha1, expectedError: "either 'auto' or 'hostname' must be set", }, { name: "both set", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = "example.org" cfg.ConfigAuto = new(nethelpers.AutoHostnameKindStable) return cfg }, expectedError: "'auto' and 'hostname' cannot be set at the same time", }, { name: "invalid auto", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigAuto = new(nethelpers.AutoHostnameKindAddr) return cfg }, expectedError: "invalid value for 'auto': talos-addr", }, { name: "too long hostname", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = strings.Repeat("a", 64) return cfg }, expectedError: "invalid hostname \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"", }, { name: "too long hostname", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = strings.Repeat("a", 64) + "." + strings.Repeat("b", 64) return cfg }, expectedError: "invalid hostname \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"", }, { name: "invalid fqdn", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = ".example.org" return cfg }, expectedError: "invalid hostname \"\"", }, { name: "fqdn too long", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = strings.Repeat(strings.Repeat("a", 63)+".", 5) return cfg }, expectedError: "fqdn is too long: 320", }, { name: "valid 1", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigHostname = "example.org" return cfg }, }, { name: "valid 2", cfg: func() *network.HostnameConfigV1Alpha1 { cfg := network.NewHostnameConfigV1Alpha1() cfg.ConfigAuto = new(nethelpers.AutoHostnameKindStable) return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } func TestHostnameV1Alpha1Validate(t *testing.T) { t.Parallel() for _, test := range []struct { name string v1alpha1Cfg *v1alpha1.Config cfg func() *network.HostnameConfigV1Alpha1 expectedError string }{ { name: "empty", v1alpha1Cfg: &v1alpha1.Config{}, cfg: network.NewHostnameConfigV1Alpha1, }, { name: "v1alpha1 static hostname set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkHostname: "foo", }, }, }, cfg: network.NewHostnameConfigV1Alpha1, expectedError: "static hostname is already set in v1alpha1 config", }, { name: "v1alpha1 stable hostname set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineFeatures: &v1alpha1.FeaturesConfig{ StableHostname: new(true), }, }, }, cfg: network.NewHostnameConfigV1Alpha1, expectedError: "stable hostname is already set in v1alpha1 config", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() err := test.cfg().V1Alpha1ConflictValidate(test.v1alpha1Cfg) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/kubespan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" ) // KubeSpanKind is a KubeSpan config document kind. const KubeSpanKind = "KubeSpanConfig" func init() { registry.Register(KubeSpanKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &KubeSpanConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkKubeSpanConfig = &KubeSpanConfigV1Alpha1{} _ config.Validator = &KubeSpanConfigV1Alpha1{} _ container.V1Alpha1ConflictValidator = &KubeSpanConfigV1Alpha1{} ) // KubeSpanConfigV1Alpha1 is a config document to configure KubeSpan. // // examples: // - value: exampleKubeSpanV1Alpha1() // alias: KubeSpanConfig // schemaRoot: true // schemaMeta: v1alpha1/KubeSpanConfig type KubeSpanConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Enable the KubeSpan feature. // Cluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled. // schema: // type: boolean ConfigEnabled *bool `yaml:"enabled,omitempty"` // description: | // Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node. // If disabled, CNI handles pod-to-pod traffic encapsulation. // If enabled, KubeSpan takes over pod-to-pod traffic directly. // schema: // type: boolean ConfigAdvertiseKubernetesNetworks *bool `yaml:"advertiseKubernetesNetworks,omitempty"` // description: | // Skip sending traffic via KubeSpan if the peer connection state is not up. // This provides configurable choice between connectivity and security. // schema: // type: boolean ConfigAllowDownPeerBypass *bool `yaml:"allowDownPeerBypass,omitempty"` // description: | // KubeSpan can collect and publish extra endpoints for each member of the cluster // based on Wireguard endpoint information for each peer. // Disabled by default. Do not enable with high peer counts (>50). // schema: // type: boolean ConfigHarvestExtraEndpoints *bool `yaml:"harvestExtraEndpoints,omitempty"` // description: | // KubeSpan link MTU size. // Default value is 1420. // schema: // type: integer ConfigMTU *uint32 `yaml:"mtu,omitempty"` // description: | // KubeSpan advanced filtering of network addresses. // Settings are optional and apply only to this node. ConfigFilters *KubeSpanFiltersConfig `yaml:"filters,omitempty"` } // KubeSpanFiltersConfig configures KubeSpan endpoint filters. type KubeSpanFiltersConfig struct { // description: | // Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections. // // By default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works. // // Default value: no filtering. // examples: // - name: Exclude addresses in 192.168.0.0/16 subnet. // value: '[]string{"0.0.0.0/0", "!192.168.0.0/16", "::/0"}' // schema: // type: array // items: // type: string ConfigEndpoints []string `yaml:"endpoints,omitempty"` // description: | // Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan. // // By default, all networks are advertised. // Use this filter to exclude some networks from being advertised. // // Note: excluded networks will not be reachable over KubeSpan, so make sure // these networks are still reachable via some other route (e.g., direct connection). // // Default value: no filtering. // examples: // - name: Exclude private networks from being advertised. // value: '[]Prefix{{netip.MustParsePrefix("192.168.1.0/24")}}' // schema: // type: array // items: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ ConfigExcludeAdvertisedNetworks []Prefix `yaml:"excludeAdvertisedNetworks,omitempty"` } // NewKubeSpanV1Alpha1 creates a new KubeSpanConfig config document. func NewKubeSpanV1Alpha1() *KubeSpanConfigV1Alpha1 { return &KubeSpanConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: KubeSpanKind, MetaAPIVersion: "v1alpha1", }, } } func exampleKubeSpanV1Alpha1() *KubeSpanConfigV1Alpha1 { cfg := NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigAdvertiseKubernetesNetworks = new(false) cfg.ConfigAllowDownPeerBypass = new(false) cfg.ConfigHarvestExtraEndpoints = new(false) cfg.ConfigMTU = new(uint32(1420)) cfg.ConfigFilters = &KubeSpanFiltersConfig{ ConfigEndpoints: []string{"0.0.0.0/0", "::/0"}, ConfigExcludeAdvertisedNetworks: []Prefix{{netip.MustParsePrefix("192.168.1.0/24")}, {netip.MustParsePrefix("2003::/16")}}, } return cfg } // Clone implements config.Document interface. func (s *KubeSpanConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. func (s *KubeSpanConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if s.ConfigMTU != nil && *s.ConfigMTU < constants.KubeSpanLinkMinimumMTU { errs = errors.Join(errs, fmt.Errorf("kubespan link MTU must be at least %d", constants.KubeSpanLinkMinimumMTU)) } if s.ConfigFilters != nil { for _, cidr := range s.ConfigFilters.ConfigEndpoints { cidr = strings.TrimPrefix(cidr, "!") if _, err := sideronet.ParseSubnetOrAddress(cidr); err != nil { errs = errors.Join(errs, fmt.Errorf("KubeSpan endpoint filter is not valid: %q", cidr)) } } } return nil, errs } // V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface. func (s *KubeSpanConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error { legacyKubespan := v1alpha1Cfg.NetworkKubeSpanConfig() if legacyKubespan != nil { return fmt.Errorf("kubespan is already configured in v1alpha1 machine.network.kubespan") } return nil } // Enabled implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) Enabled() bool { return pointer.SafeDeref(s.ConfigEnabled) } // AdvertiseKubernetesNetworks implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) AdvertiseKubernetesNetworks() bool { return pointer.SafeDeref(s.ConfigAdvertiseKubernetesNetworks) } // ForceRouting implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) ForceRouting() bool { return !pointer.SafeDeref(s.ConfigAllowDownPeerBypass) } // HarvestExtraEndpoints implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) HarvestExtraEndpoints() bool { return pointer.SafeDeref(s.ConfigHarvestExtraEndpoints) } // MTU implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) MTU() uint32 { if s.ConfigMTU != nil { return *s.ConfigMTU } return constants.KubeSpanLinkMTU } // Filters implements config.NetworkKubeSpanConfig interface. func (s *KubeSpanConfigV1Alpha1) Filters() config.NetworkKubeSpanFilters { if s.ConfigFilters == nil { return nil } return s.ConfigFilters } // Endpoints implements config.NetworkKubeSpanFilters interface. func (f *KubeSpanFiltersConfig) Endpoints() []string { return f.ConfigEndpoints } // ExcludeAdvertisedNetworks implements config.NetworkKubeSpanFilters interface. func (f *KubeSpanFiltersConfig) ExcludeAdvertisedNetworks() []netip.Prefix { return xslices.Map(f.ConfigExcludeAdvertisedNetworks, func(p Prefix) netip.Prefix { return p.Prefix }) } ================================================ FILE: pkg/machinery/config/types/network/kubespan_endpoints.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "net/netip" "slices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) // KubespanEndpointsKind is a KubeSpan endpoints document kind. const KubespanEndpointsKind = "KubeSpanEndpointsConfig" func init() { registry.Register(KubespanEndpointsKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &KubespanEndpointsConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.KubespanConfig = &KubespanEndpointsConfigV1Alpha1{} ) // KubespanEndpointsConfigV1Alpha1 is a config document to configure KubeSpan endpoints. // // examples: // - value: exampleKubespanEndpointsV1Alpha1() // alias: KubeSpanEndpointsConfig // schemaRoot: true // schemaMeta: v1alpha1/KubeSpanEndpoints type KubespanEndpointsConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // A list of extra Wireguard endpoints to announce from this machine. // // Talos automatically adds endpoints based on machine addresses, public IP, etc. // This field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping. // schema: // type: array // items: // type: string ExtraAnnouncedEndpointsConfig []netip.AddrPort `yaml:"extraAnnouncedEndpoints"` } // NewKubespanEndpointsV1Alpha1 creates a new KubespanEndpoints config document. func NewKubespanEndpointsV1Alpha1() *KubespanEndpointsConfigV1Alpha1 { return &KubespanEndpointsConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: KubespanEndpointsKind, MetaAPIVersion: "v1alpha1", }, } } func exampleKubespanEndpointsV1Alpha1() *KubespanEndpointsConfigV1Alpha1 { cfg := NewKubespanEndpointsV1Alpha1() cfg.ExtraAnnouncedEndpointsConfig = []netip.AddrPort{ netip.MustParseAddrPort("192.168.13.46:52000"), } return cfg } // Clone implements config.Document interface. func (s *KubespanEndpointsConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // ExtraAnnouncedEndpoints implements KubespanConfig interface. func (s *KubespanEndpointsConfigV1Alpha1) ExtraAnnouncedEndpoints() []netip.AddrPort { return slices.Clone(s.ExtraAnnouncedEndpointsConfig) } ================================================ FILE: pkg/machinery/config/types/network/kubespan_endpoints_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/kubespanendpointsconfig.yaml var expectedKubespanEndpointsConfigDocument []byte func TestKubespanEndpointsConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewKubespanEndpointsV1Alpha1() cfg.ExtraAnnouncedEndpointsConfig = []netip.AddrPort{ netip.MustParseAddrPort("3.4.5.6:123"), netip.MustParseAddrPort("10.11.12.13:456"), } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedKubespanEndpointsConfigDocument, marshaled) } func TestKubespanEndpointsConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedKubespanEndpointsConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.KubespanEndpointsConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.KubespanEndpointsKind, }, ExtraAnnouncedEndpointsConfig: []netip.AddrPort{ netip.MustParseAddrPort("3.4.5.6:123"), netip.MustParseAddrPort("10.11.12.13:456"), }, }, docs[0]) } ================================================ FILE: pkg/machinery/config/types/network/kubespan_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) //go:embed testdata/kubespanconfig.yaml var expectedKubeSpanConfigDocument []byte func TestKubeSpanConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigAdvertiseKubernetesNetworks = new(false) cfg.ConfigAllowDownPeerBypass = new(false) cfg.ConfigMTU = new(uint32(1420)) cfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigExcludeAdvertisedNetworks: []network.Prefix{{netip.MustParsePrefix("2007::/64")}}, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedKubeSpanConfigDocument, marshaled) } func TestKubeSpanConfigUnmarshal(t *testing.T) { t.Parallel() cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigMTU = new(uint32(1500)) cfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigEndpoints: []string{"0.0.0.0/0", "!192.168.0.0/16"}, ConfigExcludeAdvertisedNetworks: []network.Prefix{{netip.MustParsePrefix("2007::/64")}}, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) // Verify interface methods work assert.True(t, cfg.Enabled()) assert.Equal(t, uint32(1500), cfg.MTU()) assert.Equal(t, []string{"0.0.0.0/0", "!192.168.0.0/16"}, cfg.Filters().Endpoints()) assert.Equal(t, []netip.Prefix{netip.MustParsePrefix("2007::/64")}, cfg.Filters().ExcludeAdvertisedNetworks()) } func TestKubeSpanConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.KubeSpanConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "valid default", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) return cfg }, }, { name: "valid with MTU", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigMTU = new(uint32(1420)) return cfg }, }, { name: "MTU too low", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigMTU = new(uint32(1279)) return cfg }, expectedError: "kubespan link MTU must be at least 1280", }, { name: "MTU at minimum", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigMTU = new(uint32(1280)) return cfg }, }, { name: "with filters", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigEndpoints: []string{"0.0.0.0/0", "!10.0.0.0/8"}, } return cfg }, }, { name: "with invalid filters", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigEndpoints: []string{"0.0.0.0/0", "!/8"}, } return cfg }, expectedError: `KubeSpan endpoint filter is not valid: "/8"`, }, { name: "all options enabled", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigAdvertiseKubernetesNetworks = new(true) cfg.ConfigAllowDownPeerBypass = new(true) cfg.ConfigHarvestExtraEndpoints = new(true) cfg.ConfigMTU = new(uint32(1400)) return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } func TestKubeSpanConfigConflictValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.KubeSpanConfigV1Alpha1 v1 func() *v1alpha1.Config expectedError string }{ { name: "no conflict when v1alpha1 empty", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) return cfg }, v1: func() *v1alpha1.Config { return &v1alpha1.Config{} }, }, { name: "conflict when both set", cfg: func() *network.KubeSpanConfigV1Alpha1 { cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) return cfg }, v1: func() *v1alpha1.Config { cfg := &v1alpha1.Config{} cfg.MachineConfig = &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ //nolint:staticcheck // legacy config KubeSpanEnabled: new(true), }, }, } return cfg }, expectedError: "kubespan is already configured in v1alpha1 machine.network.kubespan", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() err := test.cfg().V1Alpha1ConflictValidate(test.v1()) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } func TestKubeSpanConfigInterface(t *testing.T) { t.Parallel() cfg := network.NewKubeSpanV1Alpha1() cfg.ConfigEnabled = new(true) cfg.ConfigAdvertiseKubernetesNetworks = new(true) cfg.ConfigAllowDownPeerBypass = new(false) cfg.ConfigHarvestExtraEndpoints = new(true) cfg.ConfigMTU = new(uint32(1380)) cfg.ConfigFilters = &network.KubeSpanFiltersConfig{ ConfigEndpoints: []string{"192.168.0.0/16"}, ConfigExcludeAdvertisedNetworks: []network.Prefix{{netip.MustParsePrefix("0.0.0.0/0")}}, } // Test interface methods assert.True(t, cfg.Enabled()) assert.True(t, cfg.AdvertiseKubernetesNetworks()) assert.True(t, cfg.ForceRouting()) assert.True(t, cfg.HarvestExtraEndpoints()) assert.Equal(t, uint32(1380), cfg.MTU()) assert.NotNil(t, cfg.Filters()) assert.Equal(t, []string{"192.168.0.0/16"}, cfg.Filters().Endpoints()) assert.Equal(t, []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0")}, cfg.Filters().ExcludeAdvertisedNetworks()) } ================================================ FILE: pkg/machinery/config/types/network/layer2_vip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // Layer2VIPKind is a Layer2VIP config document kind. const Layer2VIPKind = "Layer2VIPConfig" func init() { registry.Register(Layer2VIPKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &Layer2VIPConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkLayer2VIPConfig = &Layer2VIPConfigV1Alpha1{} _ config.ConflictingDocument = &Layer2VIPConfigV1Alpha1{} _ config.NamedDocument = &Layer2VIPConfigV1Alpha1{} _ config.Validator = &Layer2VIPConfigV1Alpha1{} ) // Layer2VIPConfigV1Alpha1 is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement. // // description: | // Virtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server. // Any other use cases are not supported and may lead to unexpected behavior. // Virtual IP will be announced from only one node at a time using gratuitous ARP announcements for IPv4. // examples: // - value: exampleLayer2VIPConfigV1Alpha1() // alias: Layer2VIPConfig // schemaRoot: true // schemaMeta: v1alpha1/Layer2VIPConfig type Layer2VIPConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // IP address to be advertised as a Layer 2 VIP. // // examples: // - value: > // "192.168.100.1" // - value: > // "fd00::1" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Name of the link to assign the VIP to. // // Selector must match exactly one link, otherwise an error is returned. // If multiple selectors match the same link, the first one is used. LinkName string `yaml:"link"` } // NewLayer2VIPConfigV1Alpha1 creates a new Layer2VIPConfig config document. func NewLayer2VIPConfigV1Alpha1(name string) *Layer2VIPConfigV1Alpha1 { return &Layer2VIPConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: Layer2VIPKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleLayer2VIPConfigV1Alpha1() *Layer2VIPConfigV1Alpha1 { cfg := NewLayer2VIPConfigV1Alpha1("10.3.0.1") cfg.LinkName = "enp0s2" return cfg } // Clone implements config.Document interface. func (s *Layer2VIPConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *Layer2VIPConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *Layer2VIPConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } else if _, err := netip.ParseAddr(s.MetaName); err != nil { errs = errors.Join(errs, fmt.Errorf("name must be a valid IP address: %w", err)) } if s.LinkName == "" { errs = errors.Join(errs, errors.New("link must be specified")) } return warnings, errs } // Link implements config.NetworkLayer2VIPConfig interface. func (s *Layer2VIPConfigV1Alpha1) Link() string { return s.LinkName } // VIP implements config.NetworkLayer2VIPConfig interface. func (s *Layer2VIPConfigV1Alpha1) VIP() netip.Addr { addr, _ := netip.ParseAddr(s.MetaName) //nolint:errcheck // already validated return addr } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *Layer2VIPConfigV1Alpha1) ConflictsWithKinds() []string { return []string{HCloudVIPKind} } ================================================ FILE: pkg/machinery/config/types/network/layer2_vip_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/layer2vipconfig.yaml var expectedLayer2VIPConfigDocument []byte func TestLayer2VIPConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewLayer2VIPConfigV1Alpha1("1.2.3.4") cfg.LinkName = "net0" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedLayer2VIPConfigDocument, marshaled) } func TestLayer2VIPConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedLayer2VIPConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) c := &network.Layer2VIPConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.Layer2VIPKind, }, MetaName: "1.2.3.4", LinkName: "net0", } assert.Equal(t, c, docs[0]) } func TestLayer2VIPValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.Layer2VIPConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.Layer2VIPConfigV1Alpha1 { return network.NewLayer2VIPConfigV1Alpha1("") }, expectedError: "name must be specified\nlink must be specified", }, { name: "no link name", cfg: func() *network.Layer2VIPConfigV1Alpha1 { return network.NewLayer2VIPConfigV1Alpha1("1.1.1.1") }, expectedError: "link must be specified", }, { name: "invalid IP", cfg: func() *network.Layer2VIPConfigV1Alpha1 { c := network.NewLayer2VIPConfigV1Alpha1("net4") c.LinkName = "net0" return c }, expectedError: "name must be a valid IP address: ParseAddr(\"net4\"): unable to parse IP", }, { name: "valid", cfg: func() *network.Layer2VIPConfigV1Alpha1 { c := network.NewLayer2VIPConfigV1Alpha1("fd00::1") c.LinkName = "net45" return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/link.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // LinkKind is a Link config document kind. const LinkKind = "LinkConfig" func init() { registry.Register(LinkKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &LinkConfigV1Alpha1{} default: return nil } }) } func conflictingLinkKinds(selfKind string) []string { return xslices.Filter([]string{ BondKind, BridgeKind, DummyLinkKind, LinkKind, VLANKind, VRFKind, WireguardKind, }, func(kind string) bool { return kind != selfKind }) } // Check interfaces. var ( _ config.NetworkPhysicalLinkConfig = &LinkConfigV1Alpha1{} _ config.ConflictingDocument = &LinkConfigV1Alpha1{} _ config.NamedDocument = &LinkConfigV1Alpha1{} _ config.Validator = &LinkConfigV1Alpha1{} ) // LinkConfigV1Alpha1 is a config document to configure physical interfaces (network links). // // examples: // - value: exampleLinkConfigV1Alpha1() // alias: LinkConfig // schemaRoot: true // schemaMeta: v1alpha1/LinkConfig type LinkConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the link (interface). // // examples: // - value: > // "enp0s2" // - value: > // "eth1" // schemaRequired: true MetaName string `yaml:"name"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // CommonLinkConfig is common configuration for network links, and logical links. type CommonLinkConfig struct { // description: | // Bring the link up or down. // // If not specified, the link will be brought up. LinkUp *bool `yaml:"up,omitempty"` // description: | // Configure LinkMTU (Maximum Transmission Unit) for the link. // // If not specified, the system default LinkMTU will be used (usually 1500). LinkMTU uint32 `yaml:"mtu,omitempty"` // description: | // Configure addresses to be statically assigned to the link. LinkAddresses []AddressConfig `yaml:"addresses,omitempty"` // description: | // Configure routes to be statically created via the link. LinkRoutes []RouteConfig `yaml:"routes,omitempty"` // description: | // Set the multicast capability of the link. LinkMulticast *bool `yaml:"multicast,omitempty"` } // AddressConfig represents a network address configuration. type AddressConfig struct { // description: | // IP address to be assigned to the link. // // This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6). // examples: // - value: > // netip.MustParsePrefix("192.168.1.100/24") // - value: > // netip.MustParsePrefix("fd00::1/64") // schema: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ // schemaRequired: true AddressAddress netip.Prefix `yaml:"address"` // description: | // Configure the route priority (metric) for routes created for this address. // // If not specified, the system default route priority will be used. AddressPriority *uint32 `yaml:"routePriority,omitempty"` } // RouteConfig represents a network route configuration. type RouteConfig struct { // description: | // The route's destination as an address prefix. // // If not specified, a default route will be created for the address family of the gateway. // examples: // - value: > // Prefix{netip.MustParsePrefix("10.0.0.0/8")} // schema: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ RouteDestination Prefix `yaml:"destination,omitempty"` // description: | // The route's gateway (if empty, creates link scope route). // examples: // - value: > // Addr{netip.MustParseAddr("10.0.0.1")} // schema: // type: string // pattern: ^[0-9a-f.:]+$ RouteGateway Addr `yaml:"gateway,omitempty"` // description: | // The route's source address (optional). // schema: // type: string // pattern: ^[0-9a-f.:]+$ RouteSource Addr `yaml:"source,omitempty"` // description: | // The optional metric for the route. RouteMetric uint32 `yaml:"metric,omitempty"` // description: | // The optional MTU for the route. RouteMTU uint32 `yaml:"mtu,omitempty"` // description: | // The routing table to use for the route. // // If not specified, the main routing table will be used. // schema: // type: string RouteTable nethelpers.RoutingTable `yaml:"table,omitempty"` } // NewLinkConfigV1Alpha1 creates a new LinkConfig config document. func NewLinkConfigV1Alpha1(name string) *LinkConfigV1Alpha1 { return &LinkConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: LinkKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleLinkConfigV1Alpha1() *LinkConfigV1Alpha1 { cfg := NewLinkConfigV1Alpha1("enp0s2") cfg.LinkMTU = 9000 cfg.LinkUp = new(true) cfg.LinkAddresses = []AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []RouteConfig{ { RouteDestination: Prefix{netip.MustParsePrefix("10.0.0.0/8")}, RouteGateway: Addr{netip.MustParseAddr("10.0.0.1")}, }, { RouteGateway: Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg } // Clone implements config.Document interface. func (s *LinkConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *LinkConfigV1Alpha1) Name() string { return s.MetaName } // PhysicalLinkConfig implements NetworkPhysicalLinkConfig interface. func (s *LinkConfigV1Alpha1) PhysicalLinkConfig() {} // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *LinkConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(LinkKind) } // Validate implements config.Validator interface. func (s *LinkConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } // Validate validates the common link config. // //nolint:gocyclo func (s *CommonLinkConfig) Validate() ([]string, error) { var ( errs error warnings []string ) for i, addr := range s.LinkAddresses { switch { case addr.AddressAddress == (netip.Prefix{}): errs = errors.Join(errs, fmt.Errorf("address %d must be specified", i)) case !addr.AddressAddress.IsValid(): errs = errors.Join(errs, fmt.Errorf("address %d must be a valid IP prefix", i)) case !addr.AddressAddress.Addr().IsValid() || addr.AddressAddress.Addr().IsUnspecified(): errs = errors.Join(errs, fmt.Errorf("address %d must be a valid IP address", i)) } } for i, route := range s.LinkRoutes { if route.RouteDestination != (Prefix{}) && (!route.RouteDestination.IsValid() || route.RouteDestination.Addr().IsUnspecified()) { errs = errors.Join(errs, fmt.Errorf("route %d destination must be a valid IP prefix", i)) } if route.RouteGateway != (Addr{}) && (!route.RouteGateway.IsValid() || route.RouteGateway.IsUnspecified()) { errs = errors.Join(errs, fmt.Errorf("route %d gateway must be a valid IP address", i)) } if route.RouteSource != (Addr{}) && (!route.RouteSource.IsValid() || route.RouteSource.IsUnspecified()) { errs = errors.Join(errs, fmt.Errorf("route %d source must be a valid IP address", i)) } } return warnings, errs } // Up implements NetworkCommonLinkConfig interface. func (s *CommonLinkConfig) Up() optional.Optional[bool] { if s.LinkUp == nil { return optional.None[bool]() } return optional.Some(*s.LinkUp) } // MTU implements NetworkCommonLinkConfig interface. func (s *CommonLinkConfig) MTU() optional.Optional[uint32] { if s.LinkMTU == 0 { return optional.None[uint32]() } return optional.Some(s.LinkMTU) } // Addresses implements NetworkCommonLinkConfig interface. func (s *CommonLinkConfig) Addresses() []config.NetworkAddressConfig { return xslices.Map(s.LinkAddresses, func(a AddressConfig) config.NetworkAddressConfig { return a }) } // Routes implements NetworkCommonLinkConfig interface. func (s *CommonLinkConfig) Routes() []config.NetworkRouteConfig { return xslices.Map(s.LinkRoutes, func(r RouteConfig) config.NetworkRouteConfig { return r }) } // Multicast implements NetworkCommonLinkConfig interface. func (s *CommonLinkConfig) Multicast() optional.Optional[bool] { if s.LinkMulticast == nil { return optional.None[bool]() } return optional.Some(*s.LinkMulticast) } // Address implements NetworkAddressConfig interface. func (a AddressConfig) Address() netip.Prefix { return a.AddressAddress } // RoutePriority implements NetworkAddressConfig interface. func (a AddressConfig) RoutePriority() optional.Optional[uint32] { if a.AddressPriority == nil { return optional.None[uint32]() } return optional.Some(*a.AddressPriority) } // Destination implements NetworkRouteConfig interface. func (r RouteConfig) Destination() optional.Optional[netip.Prefix] { if r.RouteDestination == (Prefix{}) { return optional.None[netip.Prefix]() } return optional.Some(r.RouteDestination.Prefix) } // Gateway implements NetworkRouteConfig interface. func (r RouteConfig) Gateway() optional.Optional[netip.Addr] { if r.RouteGateway == (Addr{}) { return optional.None[netip.Addr]() } return optional.Some(r.RouteGateway.Addr) } // Source implements NetworkRouteConfig interface. func (r RouteConfig) Source() optional.Optional[netip.Addr] { if r.RouteSource == (Addr{}) { return optional.None[netip.Addr]() } return optional.Some(r.RouteSource.Addr) } // MTU implements NetworkRouteConfig interface. func (r RouteConfig) MTU() optional.Optional[uint32] { if r.RouteMTU == 0 { return optional.None[uint32]() } return optional.Some(r.RouteMTU) } // Metric implements NetworkRouteConfig interface. func (r RouteConfig) Metric() optional.Optional[uint32] { if r.RouteMetric == 0 { return optional.None[uint32]() } return optional.Some(r.RouteMetric) } // Table implements NetworkRouteConfig interface. func (r RouteConfig) Table() optional.Optional[nethelpers.RoutingTable] { if r.RouteTable == nethelpers.TableUnspec { return optional.None[nethelpers.RoutingTable]() } return optional.Some(r.RouteTable) } ================================================ FILE: pkg/machinery/config/types/network/link_alias.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "strings" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // LinkAliasKind is a LinkAlias config document kind. const LinkAliasKind = "LinkAliasConfig" func init() { registry.Register(LinkAliasKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &LinkAliasConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkLinkAliasConfig = &LinkAliasConfigV1Alpha1{} _ config.NamedDocument = &LinkAliasConfigV1Alpha1{} _ config.Validator = &LinkAliasConfigV1Alpha1{} ) // LinkAliasConfigV1Alpha1 is a config document to alias (give a different name) to a physical link. // // examples: // - value: exampleLinkAliasConfigV1Alpha1() // - value: exampleLinkAliasMultipleConfigV1Alpha1() // alias: LinkAliasConfig // schemaRoot: true // schemaMeta: v1alpha1/LinkAliasConfig type LinkAliasConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Alias for the link. // // Don't use system interface names like "eth0", "ens3", "enp0s2", etc. as those may conflict // with existing physical interfaces. // // The name can contain a single integer format verb (`%d`) to create multiple aliases // from a single config document. When a format verb is detected, each matched link receives a sequential // alias (e.g. `net0`, `net1`, ...) based on hardware address order of the links. // Links already aliased by a previous config are automatically skipped. // // examples: // - value: > // "net0" // - value: > // "private" // - value: > // "net%d" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Selector to match the link to alias. // // When the alias name is a fixed string, the selector must match exactly one link. // When the alias name contains a format verb (e.g. `net%d`), the selector may match multiple links // and each match receives a sequential alias. // If multiple selectors match the same link, the first one is used. Selector LinkSelector `yaml:"selector,omitempty"` } // LinkSelector selects a link to alias. type LinkSelector struct { // description: | // The Common Expression Language (CEL) expression to match the link. // schema: // type: string // examples: // - value: > // exampleLinkSelector1() // name: match links with a specific MAC address // - value: > // exampleLinkSelector2() // name: match links by MAC address prefix // - value: > // exampleLinkSelector3() // name: match links by driver name Match cel.Expression `yaml:"match,omitempty"` } // NewLinkAliasConfigV1Alpha1 creates a new LinkAliasConfig config document. func NewLinkAliasConfigV1Alpha1(name string) *LinkAliasConfigV1Alpha1 { return &LinkAliasConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: LinkAliasKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleLinkAliasConfigV1Alpha1() *LinkAliasConfigV1Alpha1 { cfg := NewLinkAliasConfigV1Alpha1("int0") cfg.Selector.Match = exampleLinkSelector2() return cfg } func exampleLinkAliasMultipleConfigV1Alpha1() *LinkAliasConfigV1Alpha1 { cfg := NewLinkAliasConfigV1Alpha1("net%d") cfg.Selector.Match = exampleLinkSelector3() return cfg } func exampleLinkSelector1() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e"`, celenv.LinkLocator())) } func exampleLinkSelector2() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`glob("00:1a:2b:*", mac(link.permanent_addr))`, celenv.LinkLocator())) } func exampleLinkSelector3() cel.Expression { return cel.MustExpression(cel.ParseBooleanExpression(`link.driver == "e1000"`, celenv.LinkLocator())) } // Clone implements config.Document interface. func (s *LinkAliasConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *LinkAliasConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *LinkAliasConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } else if s.IsPatternAlias() { prefix, suffix, _ := strings.Cut(s.MetaName, "%") if suffix != "d" || prefix == "" { errs = errors.Join(errs, fmt.Errorf("name %q contains an invalid format verb, use %%d suffix", s.MetaName)) } } if !s.Selector.Match.IsZero() { if err := s.Selector.Match.ParseBool(celenv.LinkLocator()); err != nil { errs = errors.Join(errs, fmt.Errorf("link selector is invalid: %w", err)) } } else { errs = errors.Join(errs, errors.New("link selector is required")) } return warnings, errs } // LinkSelector implements config.NetworkLinkAliasConfig interface. func (s *LinkAliasConfigV1Alpha1) LinkSelector() cel.Expression { return s.Selector.Match } // IsPatternAlias returns true if the alias name contains a format verb (e.g. %d) // indicating this config should create multiple aliases. func (s *LinkAliasConfigV1Alpha1) IsPatternAlias() bool { return strings.ContainsRune(s.MetaName, '%') } ================================================ FILE: pkg/machinery/config/types/network/link_alias_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/linkaliasconfig.yaml var expectedLinkAliasConfigDocument []byte func TestLinkAliasConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewLinkAliasConfigV1Alpha1("net0") cfg.Selector.Match = cel.MustExpression(cel.ParseBooleanExpression(`mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e"`, celenv.LinkLocator())) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedLinkAliasConfigDocument, marshaled) } func TestLinkAliasConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedLinkAliasConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) c := &network.LinkAliasConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.LinkAliasKind, }, MetaName: "net0", } require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e"`))) assert.Equal(t, c, docs[0]) } func TestLinkAliasValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.LinkAliasConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.LinkAliasConfigV1Alpha1 { return network.NewLinkAliasConfigV1Alpha1("") }, expectedError: "name must be specified\nlink selector is required", }, { name: "no disk selector", cfg: func() *network.LinkAliasConfigV1Alpha1 { return network.NewLinkAliasConfigV1Alpha1("int0") }, expectedError: "link selector is required", }, { name: "invalid disk selector", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("int0") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`disk.size > 120`))) return c }, expectedError: "link selector is invalid: ERROR: :1:1: undeclared reference to 'disk' (in container '')\n | disk.size > 120\n | ^", }, { name: "valid", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("int0") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e"`))) return c }, }, { name: "valid pattern name", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("net%d") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`link.type == 1`))) return c }, }, { name: "invalid pattern name with padding", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("net%02d") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`link.type == 1`))) return c }, expectedError: "name \"net%02d\" contains an invalid format verb, use %d suffix", }, { name: "invalid pattern name with multiple verbs", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("net%d-port%d") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`link.type == 1`))) return c }, expectedError: "name \"net%d-port%d\" contains an invalid format verb, use %d suffix", }, { name: "invalid format verb", cfg: func() *network.LinkAliasConfigV1Alpha1 { c := network.NewLinkAliasConfigV1Alpha1("net%s") require.NoError(t, c.Selector.Match.UnmarshalText([]byte(`link.type == 1`))) return c }, expectedError: `name "net%s" contains an invalid format verb, use %d suffix`, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/link_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/linkconfig.yaml var expectedLinkConfigDocument []byte func TestLinkConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewLinkConfigV1Alpha1("enp0s1") cfg.LinkMTU = 9000 cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("2001:db8::1/64"), AddressPriority: new(uint32(100)), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } cfg.LinkMulticast = new(true) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedLinkConfigDocument, marshaled) } func TestLinkConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedLinkConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.LinkConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.LinkKind, }, MetaName: "enp0s1", CommonLinkConfig: network.CommonLinkConfig{ LinkMTU: 9000, LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("2001:db8::1/64"), AddressPriority: new(uint32(100)), }, }, LinkRoutes: []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, }, LinkMulticast: new(true), }, }, docs[0]) } func TestLinkValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.LinkConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.LinkConfigV1Alpha1 { return network.NewLinkConfigV1Alpha1("") }, expectedError: "name must be specified", }, { name: "invalid addresses", cfg: func() *network.LinkConfigV1Alpha1 { cfg := network.NewLinkConfigV1Alpha1("enp0s2") cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.Prefix{}, }, { AddressAddress: netip.MustParsePrefix("0.0.0.0/0"), }, } return cfg }, expectedError: "address 0 must be specified\naddress 1 must be a valid IP address", }, { name: "valid", cfg: func() *network.LinkConfigV1Alpha1 { cfg := network.NewLinkConfigV1Alpha1("enp0s2") cfg.LinkMTU = 9000 cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides network machine configuration documents. package network //go:generate go tool github.com/siderolabs/talos/tools/docgen -output network_doc.go network.go blackhole_route.go bond.go bridge.go vrf.go default_action_config.go dhcp4.go dhcp6.go dummy.go ethernet.go hcloud_vip.go hostname.go kubespan.go kubespan_endpoints.go layer2_vip.go link.go link_alias.go port_range.go resolver.go routing_rule.go rule_config.go static_host.go tcp_probe.go time_sync.go vlan.go wireguard.go //go:generate go tool github.com/siderolabs/deep-copy -type BlackholeRouteConfigV1Alpha1 -type BondConfigV1Alpha1 -type BridgeConfigV1Alpha1 -type VRFConfigV1Alpha1 -type DefaultActionConfigV1Alpha1 -type DHCPv4ConfigV1Alpha1 -type DHCPv6ConfigV1Alpha1 -type DummyLinkConfigV1Alpha1 -type EthernetConfigV1Alpha1 -type HCloudVIPConfigV1Alpha1 -type HostnameConfigV1Alpha1 -type KubeSpanConfigV1Alpha1 -type KubespanEndpointsConfigV1Alpha1 -type Layer2VIPConfigV1Alpha1 -type LinkConfigV1Alpha1 -type LinkAliasConfigV1Alpha1 -type ResolverConfigV1Alpha1 -type RoutingRuleConfigV1Alpha1 -type RuleConfigV1Alpha1 -type StaticHostConfigV1Alpha1 -type TCPProbeConfigV1Alpha1 -type TimeSyncConfigV1Alpha1 -type VLANConfigV1Alpha1 -type WireguardConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/network/network_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package network import ( "net/netip" "time" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) func (BlackholeRouteConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "BlackholeRouteConfig", Comments: [3]string{"" /* encoder.HeadComment */, "BlackholeRouteConfig is a config document to configure blackhole routes." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "BlackholeRouteConfig is a config document to configure blackhole routes.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Route destination as an address prefix.", Comments: [3]string{"" /* encoder.HeadComment */, "Route destination as an address prefix." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "metric", Type: "uint32", Note: "", Description: "The optional metric for the route.", Comments: [3]string{"" /* encoder.HeadComment */, "The optional metric for the route." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleBlackholeRouteConfigV1Alpha1()) doc.Fields[1].AddExample("", "10.0.0.0/12") return doc } func (BondConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "BondConfig", Comments: [3]string{"" /* encoder.HeadComment */, "BondConfig is a config document to create a bond (link aggregation) over a set of links." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "BondConfig is a config document to create a bond (link aggregation) over a set of links.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the bond link (interface) to be created.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the bond link (interface) to be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hardwareAddr", Type: "HardwareAddr", Note: "", Description: "Override the hardware (MAC) address of the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Override the hardware (MAC) address of the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "links", Type: "[]string", Note: "", Description: "Names of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.", Comments: [3]string{"" /* encoder.HeadComment */, "Names of the links (interfaces) on which the bond will be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "bondMode", Type: "BondMode", Note: "", Description: "Bond mode.", Comments: [3]string{"" /* encoder.HeadComment */, "Bond mode." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb", }, }, { Name: "miimon", Type: "uint32", Note: "", Description: "Link monitoring frequency in milliseconds.", Comments: [3]string{"" /* encoder.HeadComment */, "Link monitoring frequency in milliseconds." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "updelay", Type: "uint32", Note: "", Description: "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.", Comments: [3]string{"" /* encoder.HeadComment */, "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "downdelay", Type: "uint32", Note: "", Description: "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.", Comments: [3]string{"" /* encoder.HeadComment */, "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "useCarrier", Type: "bool", Note: "", Description: "Specifies whether or not miimon should use MII or ETHTOOL.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies whether or not miimon should use MII or ETHTOOL." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "xmitHashPolicy", Type: "BondXmitHashPolicy", Note: "", Description: "Selects the transmit hash policy to use for slave selection.", Comments: [3]string{"" /* encoder.HeadComment */, "Selects the transmit hash policy to use for slave selection." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "layer2", "layer3+4", "layer2+3", "encap2+3", "encap3+4", }, }, { Name: "arpInterval", Type: "uint32", Note: "", Description: "ARP link monitoring frequency in milliseconds.", Comments: [3]string{"" /* encoder.HeadComment */, "ARP link monitoring frequency in milliseconds." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "arpIpTargets", Type: "[]Addr", Note: "", Description: "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", Comments: [3]string{"" /* encoder.HeadComment */, "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nsIp6Targets", Type: "[]Addr", Note: "", Description: "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", Comments: [3]string{"" /* encoder.HeadComment */, "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "arpValidate", Type: "ARPValidate", Note: "", Description: "Specifies whether or not ARP probes and replies should be validated.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies whether or not ARP probes and replies should be validated." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "none", "active", "backup", "all", "filter", "filter-active", "filter-backup", }, }, { Name: "arpAllTargets", Type: "ARPAllTargets", Note: "", Description: "Specifies whether ARP probes should be sent to any or all targets.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies whether ARP probes should be sent to any or all targets." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "any", "all", }, }, { Name: "lacpRate", Type: "LACPRate", Note: "", Description: "LACPDU frames periodic transmission rate.", Comments: [3]string{"" /* encoder.HeadComment */, "LACPDU frames periodic transmission rate." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "slow", "fast", }, }, { Name: "failOverMac", Type: "FailOverMAC", Note: "", Description: "Specifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies whether active-backup mode should set all slaves to the same MAC address" /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "none", "active", "follow", }, }, { Name: "adSelect", Type: "ADSelect", Note: "", Description: "Aggregate selection policy for 802.3ad.", Comments: [3]string{"" /* encoder.HeadComment */, "Aggregate selection policy for 802.3ad." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "stable", "bandwidth", "count", }, }, { Name: "adActorSysPrio", Type: "uint16", Note: "", Description: "Actor system priority for 802.3ad.", Comments: [3]string{"" /* encoder.HeadComment */, "Actor system priority for 802.3ad." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "adUserPortKey", Type: "uint16", Note: "", Description: "User port key (upper 10 bits) for 802.3ad.", Comments: [3]string{"" /* encoder.HeadComment */, "User port key (upper 10 bits) for 802.3ad." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "adLACPActive", Type: "ADLACPActive", Note: "", Description: "Whether to send LACPDU frames periodically.", Comments: [3]string{"" /* encoder.HeadComment */, "Whether to send LACPDU frames periodically." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "on", "off", }, }, { Name: "primaryReselect", Type: "PrimaryReselect", Note: "", Description: "Policy under which the primary slave should be reselected.", Comments: [3]string{"" /* encoder.HeadComment */, "Policy under which the primary slave should be reselected." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "always", "better", "failure", }, }, { Name: "resendIGMP", Type: "uint32", Note: "", Description: "The number of times IGMP packets should be resent.", Comments: [3]string{"" /* encoder.HeadComment */, "The number of times IGMP packets should be resent." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "minLinks", Type: "uint32", Note: "", Description: "The minimum number of active links required for the bond to be considered active.", Comments: [3]string{"" /* encoder.HeadComment */, "The minimum number of active links required for the bond to be considered active." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "lpInterval", Type: "uint32", Note: "", Description: "The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch.", Comments: [3]string{"" /* encoder.HeadComment */, "The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "packetsPerSlave", Type: "uint32", Note: "", Description: "The number of packets to transmit through a slave before moving to the next one.", Comments: [3]string{"" /* encoder.HeadComment */, "The number of packets to transmit through a slave before moving to the next one." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "numPeerNotif", Type: "uint8", Note: "", Description: "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.", Comments: [3]string{"" /* encoder.HeadComment */, "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tlbLogicalLb", Type: "uint8", Note: "", Description: "Whether dynamic shuffling of flows is enabled in tlb or alb mode.", Comments: [3]string{"" /* encoder.HeadComment */, "Whether dynamic shuffling of flows is enabled in tlb or alb mode." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "allSlavesActive", Type: "uint8", Note: "", Description: "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).", Comments: [3]string{"" /* encoder.HeadComment */, "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "peerNotifDelay", Type: "uint32", Note: "", Description: "The delay, in milliseconds, between each peer notification.", Comments: [3]string{"" /* encoder.HeadComment */, "The delay, in milliseconds, between each peer notification." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "missedMax", Type: "uint8", Note: "", Description: "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.", Comments: [3]string{"" /* encoder.HeadComment */, "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleBondConfigV1Alpha1()) doc.Fields[1].AddExample("", "bond.ext") doc.Fields[2].AddExample("", nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70}) doc.Fields[3].AddExample("", []string{"enp0s3", "enp0s8"}) doc.Fields[4].AddExample("", "802.3ad") doc.Fields[5].AddExample("", 200) doc.Fields[6].AddExample("", 300) doc.Fields[7].AddExample("", 100) doc.Fields[9].AddExample("", "layer2") doc.Fields[10].AddExample("", 1000) doc.Fields[11].AddExample("", []netip.Addr{netip.MustParseAddr("10.15.0.1")}) doc.Fields[12].AddExample("", []netip.Addr{netip.MustParseAddr("fd00::1")}) doc.Fields[13].AddExample("", "active") doc.Fields[14].AddExample("", "all") doc.Fields[15].AddExample("", "fast") doc.Fields[16].AddExample("", "active") doc.Fields[17].AddExample("", "stable") doc.Fields[18].AddExample("", 65535) doc.Fields[19].AddExample("", 0) doc.Fields[20].AddExample("", "on") doc.Fields[21].AddExample("", "always") doc.Fields[27].AddExample("", 1) doc.Fields[28].AddExample("", 0) return doc } func (BridgeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "BridgeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "BridgeConfig is a config document to create a Bridge (link aggregation) over a set of links." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "BridgeConfig is a config document to create a Bridge (link aggregation) over a set of links.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the bridge link (interface) to be created.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the bridge link (interface) to be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hardwareAddr", Type: "HardwareAddr", Note: "", Description: "Override the hardware (MAC) address of the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Override the hardware (MAC) address of the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "links", Type: "[]string", Note: "", Description: "Names of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.", Comments: [3]string{"" /* encoder.HeadComment */, "Names of the links (interfaces) to be aggregated." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "stp", Type: "BridgeSTPConfig", Note: "", Description: "Bridge STP (Spanning Tree Protocol) configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Bridge STP (Spanning Tree Protocol) configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "vlan", Type: "BridgeVLANConfig", Note: "", Description: "Bridge VLAN configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Bridge VLAN configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleBridgeConfigV1Alpha1()) doc.Fields[1].AddExample("", "Bridge.ext") doc.Fields[2].AddExample("", nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70}) doc.Fields[3].AddExample("", []string{"enp1s3", "enp1s2"}) return doc } func (BridgeSTPConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "BridgeSTPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "BridgeConfigV1Alpha1", FieldName: "stp", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable or disable STP on the bridge.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable or disable STP on the bridge." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", true) return doc } func (BridgeVLANConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "BridgeVLANConfig", Comments: [3]string{"" /* encoder.HeadComment */, "BridgeVLANConfig is a bridge VLAN configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "BridgeVLANConfig is a bridge VLAN configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "BridgeConfigV1Alpha1", FieldName: "vlan", }, }, Fields: []encoder.Doc{ { Name: "filtering", Type: "bool", Note: "", Description: "Enable or disable VLAN filtering on the bridge.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable or disable VLAN filtering on the bridge." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", true) return doc } func (VRFConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VRFConfig", Comments: [3]string{"" /* encoder.HeadComment */, "VRFConfig is a config document to create a vrf and assign links to it." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VRFConfig is a config document to create a vrf and assign links to it.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the vrf link (interface) to be created.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the vrf link (interface) to be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hardwareAddr", Type: "HardwareAddr", Note: "", Description: "Override the hardware (MAC) address of the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Override the hardware (MAC) address of the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "links", Type: "[]string", Note: "", Description: "Names of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.", Comments: [3]string{"" /* encoder.HeadComment */, "Names of the links (interfaces) to be assigned to this vrf." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "table", Type: "RoutingTable", Note: "", Description: "Routing table number to use for this vrf.", Comments: [3]string{"" /* encoder.HeadComment */, "Routing table number to use for this vrf." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleVRFConfigV1Alpha1()) doc.Fields[1].AddExample("", "vrf-blue") doc.Fields[2].AddExample("", nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70}) doc.Fields[3].AddExample("", []string{"enp1s3", "enp1s2"}) doc.Fields[4].AddExample("", 10) return doc } func (DefaultActionConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "NetworkDefaultActionConfig", Comments: [3]string{"" /* encoder.HeadComment */, "NetworkDefaultActionConfig is a ingress firewall default action configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "NetworkDefaultActionConfig is a ingress firewall default action configuration document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "ingress", Type: "DefaultAction", Note: "", Description: "Default action for all not explicitly configured ingress traffic: accept or block.", Comments: [3]string{"" /* encoder.HeadComment */, "Default action for all not explicitly configured ingress traffic: accept or block." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "accept", "block", }, }, }, } doc.AddExample("", exampleDefaultActionConfigV1Alpha1()) return doc } func (DHCPv4ConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "DHCPv4Config", Comments: [3]string{"" /* encoder.HeadComment */, "DHCPv4Config is a config document to configure DHCPv4 on a network link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "DHCPv4Config is a config document to configure DHCPv4 on a network link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "routeMetric", Type: "uint32", Note: "", Description: "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", Comments: [3]string{"" /* encoder.HeadComment */, "An optional metric for the routes received from the DHCP server." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ignoreHostname", Type: "bool", Note: "", Description: "Ignore hostname received from the DHCP server.", Comments: [3]string{"" /* encoder.HeadComment */, "Ignore hostname received from the DHCP server." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "clientIdentifier", Type: "ClientIdentifier", Note: "", Description: "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", Comments: [3]string{"" /* encoder.HeadComment */, "Client identifier to use when communicating with DHCP servers." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "none", "mac", "duid", }, }, { Name: "duidRaw", Type: "HardwareAddr", Note: "", Description: "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", Comments: [3]string{"" /* encoder.HeadComment */, "Raw value of the DUID to use as client identifier." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleDHCPv4ConfigV1Alpha1()) doc.Fields[1].AddExample("", "enp0s2") doc.Fields[5].AddExample("", "00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45") return doc } func (DHCPv6ConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "DHCPv6Config", Comments: [3]string{"" /* encoder.HeadComment */, "DHCPv6Config is a config document to configure DHCPv6 on a network link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "DHCPv6Config is a config document to configure DHCPv6 on a network link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "routeMetric", Type: "uint32", Note: "", Description: "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", Comments: [3]string{"" /* encoder.HeadComment */, "An optional metric for the routes received from the DHCP server." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ignoreHostname", Type: "bool", Note: "", Description: "Ignore hostname received from the DHCP server.", Comments: [3]string{"" /* encoder.HeadComment */, "Ignore hostname received from the DHCP server." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "clientIdentifier", Type: "ClientIdentifier", Note: "", Description: "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", Comments: [3]string{"" /* encoder.HeadComment */, "Client identifier to use when communicating with DHCP servers." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "none", "mac", "duid", }, }, { Name: "duidRaw", Type: "HardwareAddr", Note: "", Description: "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", Comments: [3]string{"" /* encoder.HeadComment */, "Raw value of the DUID to use as client identifier." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleDHCPv6ConfigV1Alpha1()) doc.Fields[1].AddExample("", "enp0s2") doc.Fields[5].AddExample("", "00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45") return doc } func (DummyLinkConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "DummyLinkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "DummyLinkConfig is a config document to create a dummy (virtual) network link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "DummyLinkConfig is a config document to create a dummy (virtual) network link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the dummy link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the dummy link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hardwareAddr", Type: "HardwareAddr", Note: "", Description: "Override the hardware (MAC) address of the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Override the hardware (MAC) address of the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleDummyLinkConfigV1Alpha1()) doc.Fields[1].AddExample("", "dummy1") doc.Fields[2].AddExample("", nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70}) return doc } func (EthernetConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EthernetConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EthernetConfig is a config document to configure Ethernet interfaces." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EthernetConfig is a config document to configure Ethernet interfaces.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "features", Type: "map[string]bool", Note: "", Description: "Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse `talosctl get ethernetstatus -o yaml` to get the list of available features and\ntheir current status.", Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for Ethernet features." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "rings", Type: "EthernetRingsConfig", Note: "", Description: "Configuration for Ethernet link rings.\n\nThis is similar to `ethtool -G` command.", Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for Ethernet link rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "channels", Type: "EthernetChannelsConfig", Note: "", Description: "Configuration for Ethernet link channels.\n\nThis is similar to `ethtool -L` command.", Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for Ethernet link channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "wakeOnLan", Type: "[]WOLMode", Note: "", Description: "Wake-on-LAN modes to enable.\n\nIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\n\nThis is similar to `ethtool -s wol ` command.", Comments: [3]string{"" /* encoder.HeadComment */, "Wake-on-LAN modes to enable." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "phy", "unicast", "multicast", "broadcast", "arp", "magic", "magicsecure", "filter", }, }, }, } doc.AddExample("", exampleEthernetConfigV1Alpha1()) doc.Fields[5].AddExample("", []nethelpers.WOLMode{nethelpers.WOLModeUnicast, nethelpers.WOLModeMagic}) return doc } func (EthernetRingsConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EthernetRingsConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EthernetRingsConfig is a configuration for Ethernet link rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EthernetRingsConfig is a configuration for Ethernet link rings.", AppearsIn: []encoder.Appearance{ { TypeName: "EthernetConfigV1Alpha1", FieldName: "rings", }, }, Fields: []encoder.Doc{ { Name: "rx", Type: "uint32", Note: "", Description: "Number of RX rings.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of RX rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tx", Type: "uint32", Note: "", Description: "Number of TX rings.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of TX rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "rx-mini", Type: "uint32", Note: "", Description: "Number of RX mini rings.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of RX mini rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "rx-jumbo", Type: "uint32", Note: "", Description: "Number of RX jumbo rings.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of RX jumbo rings." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "rx-buf-len", Type: "uint32", Note: "", Description: "RX buffer length.", Comments: [3]string{"" /* encoder.HeadComment */, "RX buffer length." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "cqe-size", Type: "uint32", Note: "", Description: "CQE size.", Comments: [3]string{"" /* encoder.HeadComment */, "CQE size." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tx-push", Type: "bool", Note: "", Description: "TX push enabled.", Comments: [3]string{"" /* encoder.HeadComment */, "TX push enabled." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "rx-push", Type: "bool", Note: "", Description: "RX push enabled.", Comments: [3]string{"" /* encoder.HeadComment */, "RX push enabled." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tx-push-buf-len", Type: "uint32", Note: "", Description: "TX push buffer length.", Comments: [3]string{"" /* encoder.HeadComment */, "TX push buffer length." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tcp-data-split", Type: "bool", Note: "", Description: "TCP data split enabled.", Comments: [3]string{"" /* encoder.HeadComment */, "TCP data split enabled." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (EthernetChannelsConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EthernetChannelsConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EthernetChannelsConfig is a configuration for Ethernet link channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EthernetChannelsConfig is a configuration for Ethernet link channels.", AppearsIn: []encoder.Appearance{ { TypeName: "EthernetConfigV1Alpha1", FieldName: "channels", }, }, Fields: []encoder.Doc{ { Name: "rx", Type: "uint32", Note: "", Description: "Number of RX channels.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of RX channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "tx", Type: "uint32", Note: "", Description: "Number of TX channels.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of TX channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "other", Type: "uint32", Note: "", Description: "Number of other channels.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of other channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "combined", Type: "uint32", Note: "", Description: "Number of combined channels.", Comments: [3]string{"" /* encoder.HeadComment */, "Number of combined channels." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (HCloudVIPConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "HCloudVIPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\nAny other use cases are not supported and may lead to unexpected behavior.\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "IP address to be advertised as a Layer 2 VIP.", Comments: [3]string{"" /* encoder.HeadComment */, "IP address to be advertised as a Layer 2 VIP." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "link", Type: "string", Note: "", Description: "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link to assign the VIP to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "apiToken", Type: "string", Note: "", Description: "Specifies the Hetzner Cloud API Token.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the Hetzner Cloud API Token." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleHCloudVIPConfigV1Alpha1()) doc.Fields[1].AddExample("", "192.168.100.1") doc.Fields[1].AddExample("", "fd00::1") return doc } func (HostnameConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "HostnameConfig", Comments: [3]string{"" /* encoder.HeadComment */, "HostnameConfig is a config document to configure the hostname: either a static hostname or an automatically generated hostname." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "HostnameConfig is a config document to configure the hostname: either a static hostname or an automatically generated hostname.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "auto", Type: "AutoHostnameKind", Note: "", Description: "A method to automatically generate a hostname for the machine.\n\nThere are two methods available:\n - `stable` - generates a stable hostname based on machine identity\n - `off` - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\n\nAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `hostname` field.", Comments: [3]string{"" /* encoder.HeadComment */, "A method to automatically generate a hostname for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "stable", "off", }, }, { Name: "hostname", Type: "string", Note: "", Description: "A static hostname to set for the machine.\n\nThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `auto` field.", Comments: [3]string{"" /* encoder.HeadComment */, "A static hostname to set for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleHostnameConfigV1Alpha1()) doc.AddExample("", exampleHostnameConfigV1Alpha2()) doc.Fields[2].AddExample("", "controlplane1") doc.Fields[2].AddExample("", "controlplane1.example.org") return doc } func (KubeSpanConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubeSpanConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpanConfig is a config document to configure KubeSpan." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubeSpanConfig is a config document to configure KubeSpan.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "enabled", Type: "bool", Note: "", Description: "Enable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable the KubeSpan feature." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "advertiseKubernetesNetworks", Type: "bool", Note: "", Description: "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.", Comments: [3]string{"" /* encoder.HeadComment */, "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "allowDownPeerBypass", Type: "bool", Note: "", Description: "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.", Comments: [3]string{"" /* encoder.HeadComment */, "Skip sending traffic via KubeSpan if the peer connection state is not up." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "harvestExtraEndpoints", Type: "bool", Note: "", Description: "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (>50).", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpan can collect and publish extra endpoints for each member of the cluster" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mtu", Type: "uint32", Note: "", Description: "KubeSpan link MTU size.\nDefault value is 1420.", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpan link MTU size." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "filters", Type: "KubeSpanFiltersConfig", Note: "", Description: "KubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpan advanced filtering of network addresses." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleKubeSpanV1Alpha1()) return doc } func (KubeSpanFiltersConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubeSpanFiltersConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpanFiltersConfig configures KubeSpan endpoint filters." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubeSpanFiltersConfig configures KubeSpan endpoint filters.", AppearsIn: []encoder.Appearance{ { TypeName: "KubeSpanConfigV1Alpha1", FieldName: "filters", }, }, Fields: []encoder.Doc{ { Name: "endpoints", Type: "[]string", Note: "", Description: "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.", Comments: [3]string{"" /* encoder.HeadComment */, "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "excludeAdvertisedNetworks", Type: "[]Prefix", Note: "", Description: "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\n\nBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\n\nNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\n\nDefault value: no filtering.", Comments: [3]string{"" /* encoder.HeadComment */, "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("Exclude addresses in 192.168.0.0/16 subnet.", []string{"0.0.0.0/0", "!192.168.0.0/16", "::/0"}) doc.Fields[1].AddExample("Exclude private networks from being advertised.", []Prefix{{netip.MustParsePrefix("192.168.1.0/24")}}) return doc } func (KubespanEndpointsConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubeSpanEndpointsConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubeSpanEndpointsConfig is a config document to configure KubeSpan endpoints." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubeSpanEndpointsConfig is a config document to configure KubeSpan endpoints.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "extraAnnouncedEndpoints", Type: "[]AddrPort", Note: "", Description: "A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of extra Wireguard endpoints to announce from this machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleKubespanEndpointsV1Alpha1()) return doc } func (Layer2VIPConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "Layer2VIPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "Layer2VIPConfig is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "Layer2VIPConfig is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement.\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\nAny other use cases are not supported and may lead to unexpected behavior.\nVirtual IP will be announced from only one node at a time using gratuitous ARP announcements for IPv4.\n", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "IP address to be advertised as a Layer 2 VIP.", Comments: [3]string{"" /* encoder.HeadComment */, "IP address to be advertised as a Layer 2 VIP." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "link", Type: "string", Note: "", Description: "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link to assign the VIP to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleLayer2VIPConfigV1Alpha1()) doc.Fields[1].AddExample("", "192.168.100.1") doc.Fields[1].AddExample("", "fd00::1") return doc } func (LinkConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LinkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "LinkConfig is a config document to configure physical interfaces (network links)." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LinkConfig is a config document to configure physical interfaces (network links).", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleLinkConfigV1Alpha1()) doc.Fields[1].AddExample("", "enp0s2") doc.Fields[1].AddExample("", "eth1") return doc } func (CommonLinkConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "CommonLinkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "CommonLinkConfig is common configuration for network links, and logical links." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "CommonLinkConfig is common configuration for network links, and logical links.", AppearsIn: []encoder.Appearance{ { TypeName: "BondConfigV1Alpha1", FieldName: "", }, { TypeName: "BridgeConfigV1Alpha1", FieldName: "", }, { TypeName: "VRFConfigV1Alpha1", FieldName: "", }, { TypeName: "DummyLinkConfigV1Alpha1", FieldName: "", }, { TypeName: "LinkConfigV1Alpha1", FieldName: "", }, { TypeName: "VLANConfigV1Alpha1", FieldName: "", }, { TypeName: "WireguardConfigV1Alpha1", FieldName: "", }, }, Fields: []encoder.Doc{ { Name: "up", Type: "bool", Note: "", Description: "Bring the link up or down.\n\nIf not specified, the link will be brought up.", Comments: [3]string{"" /* encoder.HeadComment */, "Bring the link up or down." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mtu", Type: "uint32", Note: "", Description: "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", Comments: [3]string{"" /* encoder.HeadComment */, "Configure LinkMTU (Maximum Transmission Unit) for the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "addresses", Type: "[]AddressConfig", Note: "", Description: "Configure addresses to be statically assigned to the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure addresses to be statically assigned to the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "routes", Type: "[]RouteConfig", Note: "", Description: "Configure routes to be statically created via the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure routes to be statically created via the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "multicast", Type: "bool", Note: "", Description: "Set the multicast capability of the link.", Comments: [3]string{"" /* encoder.HeadComment */, "Set the multicast capability of the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (AddressConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "AddressConfig", Comments: [3]string{"" /* encoder.HeadComment */, "AddressConfig represents a network address configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "AddressConfig represents a network address configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "CommonLinkConfig", FieldName: "addresses", }, }, Fields: []encoder.Doc{ { Name: "address", Type: "Prefix", Note: "", Description: "IP address to be assigned to the link.\n\nThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).", Comments: [3]string{"" /* encoder.HeadComment */, "IP address to be assigned to the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "routePriority", Type: "uint32", Note: "", Description: "Configure the route priority (metric) for routes created for this address.\n\nIf not specified, the system default route priority will be used.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the route priority (metric) for routes created for this address." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", netip.MustParsePrefix("192.168.1.100/24")) doc.Fields[0].AddExample("", netip.MustParsePrefix("fd00::1/64")) return doc } func (RouteConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RouteConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RouteConfig represents a network route configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RouteConfig represents a network route configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "CommonLinkConfig", FieldName: "routes", }, }, Fields: []encoder.Doc{ { Name: "destination", Type: "Prefix", Note: "", Description: "The route's destination as an address prefix.\n\nIf not specified, a default route will be created for the address family of the gateway.", Comments: [3]string{"" /* encoder.HeadComment */, "The route's destination as an address prefix." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "gateway", Type: "Addr", Note: "", Description: "The route's gateway (if empty, creates link scope route).", Comments: [3]string{"" /* encoder.HeadComment */, "The route's gateway (if empty, creates link scope route)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "source", Type: "Addr", Note: "", Description: "The route's source address (optional).", Comments: [3]string{"" /* encoder.HeadComment */, "The route's source address (optional)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "metric", Type: "uint32", Note: "", Description: "The optional metric for the route.", Comments: [3]string{"" /* encoder.HeadComment */, "The optional metric for the route." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mtu", Type: "uint32", Note: "", Description: "The optional MTU for the route.", Comments: [3]string{"" /* encoder.HeadComment */, "The optional MTU for the route." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "table", Type: "RoutingTable", Note: "", Description: "The routing table to use for the route.\n\nIf not specified, the main routing table will be used.", Comments: [3]string{"" /* encoder.HeadComment */, "The routing table to use for the route." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", Prefix{netip.MustParsePrefix("10.0.0.0/8")}) doc.Fields[1].AddExample("", Addr{netip.MustParseAddr("10.0.0.1")}) return doc } func (LinkAliasConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LinkAliasConfig", Comments: [3]string{"" /* encoder.HeadComment */, "LinkAliasConfig is a config document to alias (give a different name) to a physical link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LinkAliasConfig is a config document to alias (give a different name) to a physical link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Alias for the link.\n\nDon't use system interface names like \"eth0\", \"ens3\", \"enp0s2\", etc. as those may conflict\nwith existing physical interfaces.\n\nThe name can contain a single integer format verb (`%d`) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. `net0`, `net1`, ...) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.", Comments: [3]string{"" /* encoder.HeadComment */, "Alias for the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "selector", Type: "LinkSelector", Note: "", Description: "Selector to match the link to alias.\n\nWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. `net%d`), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.", Comments: [3]string{"" /* encoder.HeadComment */, "Selector to match the link to alias." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleLinkAliasConfigV1Alpha1()) doc.AddExample("", exampleLinkAliasMultipleConfigV1Alpha1()) doc.Fields[1].AddExample("", "net0") doc.Fields[1].AddExample("", "private") doc.Fields[1].AddExample("", "net%d") return doc } func (LinkSelector) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LinkSelector", Comments: [3]string{"" /* encoder.HeadComment */, "LinkSelector selects a link to alias." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LinkSelector selects a link to alias.", AppearsIn: []encoder.Appearance{ { TypeName: "LinkAliasConfigV1Alpha1", FieldName: "selector", }, }, Fields: []encoder.Doc{ { Name: "match", Type: "Expression", Note: "", Description: "The Common Expression Language (CEL) expression to match the link.", Comments: [3]string{"" /* encoder.HeadComment */, "The Common Expression Language (CEL) expression to match the link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("match links with a specific MAC address", exampleLinkSelector1()) doc.Fields[0].AddExample("match links by MAC address prefix", exampleLinkSelector2()) doc.Fields[0].AddExample("match links by driver name", exampleLinkSelector3()) return doc } func (ResolverConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ResolverConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ResolverConfig is a config document to configure DNS resolving." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ResolverConfig is a config document to configure DNS resolving.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "nameservers", Type: "[]NameserverConfig", Note: "", Description: "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of nameservers (DNS servers) to use for resolving domain names." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "searchDomains", Type: "SearchDomainsConfig", Note: "", Description: "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.", Comments: [3]string{"" /* encoder.HeadComment */, "Configuration for search domains (in /etc/resolv.conf)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleResolverConfigV1Alpha1()) doc.AddExample("", exampleResolverConfigV1Alpha2()) return doc } func (NameserverConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "NameserverConfig", Comments: [3]string{"" /* encoder.HeadComment */, "NameserverConfig represents a single nameserver configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "NameserverConfig represents a single nameserver configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "ResolverConfigV1Alpha1", FieldName: "nameservers", }, }, Fields: []encoder.Doc{ { Name: "address", Type: "Addr", Note: "", Description: "The IP address of the nameserver.", Comments: [3]string{"" /* encoder.HeadComment */, "The IP address of the nameserver." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", Addr{netip.MustParseAddr("10.0.0.1")}) return doc } func (SearchDomainsConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "SearchDomainsConfig", Comments: [3]string{"" /* encoder.HeadComment */, "SearchDomainsConfig represents search domains configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "SearchDomainsConfig represents search domains configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "ResolverConfigV1Alpha1", FieldName: "searchDomains", }, }, Fields: []encoder.Doc{ { Name: "domains", Type: "[]string", Note: "", Description: "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of search domains to be used for DNS resolution." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "disableDefault", Type: "bool", Note: "", Description: "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable default search domain configuration from hostname FQDN." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (RoutingRuleConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RoutingRuleConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RoutingRuleConfig is a config document to configure Linux policy routing rules." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RoutingRuleConfig is a config document to configure Linux policy routing rules.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Priority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Priority of the routing rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "src", Type: "Prefix", Note: "", Description: "Source address prefix to match.\nIf empty, matches all sources.", Comments: [3]string{"" /* encoder.HeadComment */, "Source address prefix to match." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "dst", Type: "Prefix", Note: "", Description: "Destination address prefix to match.\nIf empty, matches all destinations.", Comments: [3]string{"" /* encoder.HeadComment */, "Destination address prefix to match." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "table", Type: "RoutingTable", Note: "", Description: "The routing table to look up if the rule matches.", Comments: [3]string{"" /* encoder.HeadComment */, "The routing table to look up if the rule matches." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "action", Type: "RoutingRuleAction", Note: "", Description: "The action to perform when the rule matches.\nDefaults to \"unicast\" (table lookup).", Comments: [3]string{"" /* encoder.HeadComment */, "The action to perform when the rule matches." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "unicast", "blackhole", "unreachable", "prohibit", }, }, { Name: "iifName", Type: "string", Note: "", Description: "Match packets arriving on this interface.", Comments: [3]string{"" /* encoder.HeadComment */, "Match packets arriving on this interface." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "oifName", Type: "string", Note: "", Description: "Match packets going out on this interface.", Comments: [3]string{"" /* encoder.HeadComment */, "Match packets going out on this interface." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "fwMark", Type: "uint32", Note: "", Description: "Match packets with this firewall mark value.", Comments: [3]string{"" /* encoder.HeadComment */, "Match packets with this firewall mark value." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "fwMask", Type: "uint32", Note: "", Description: "Mask for the firewall mark comparison.", Comments: [3]string{"" /* encoder.HeadComment */, "Mask for the firewall mark comparison." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRoutingRuleConfigV1Alpha1()) doc.Fields[1].AddExample("", 1000) doc.Fields[2].AddExample("", "10.0.0.0/8") doc.Fields[3].AddExample("", "192.168.0.0/16") doc.Fields[4].AddExample("", 100) doc.Fields[6].AddExample("", "eth0") doc.Fields[7].AddExample("", "eth1") doc.Fields[8].AddExample("", uint32(0x100)) doc.Fields[9].AddExample("", uint32(0xff00)) return doc } func (RuleConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "NetworkRuleConfig", Comments: [3]string{"" /* encoder.HeadComment */, "NetworkRuleConfig is a network firewall rule config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "NetworkRuleConfig is a network firewall rule config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the config document.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "portSelector", Type: "RulePortSelector", Note: "", Description: "Port selector defines which ports and protocols on the host are affected by the rule.", Comments: [3]string{"" /* encoder.HeadComment */, "Port selector defines which ports and protocols on the host are affected by the rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ingress", Type: "[]IngressRule", Note: "", Description: "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.", Comments: [3]string{"" /* encoder.HeadComment */, "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleRuleConfigV1Alpha1()) return doc } func (RulePortSelector) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RulePortSelector", Comments: [3]string{"" /* encoder.HeadComment */, "RulePortSelector is a port selector for the network rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RulePortSelector is a port selector for the network rule.", AppearsIn: []encoder.Appearance{ { TypeName: "RuleConfigV1Alpha1", FieldName: "portSelector", }, }, Fields: []encoder.Doc{ { Name: "ports", Type: "PortRanges", Note: "", Description: "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.", Comments: [3]string{"" /* encoder.HeadComment */, "Ports defines a list of port ranges or single ports." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "protocol", Type: "Protocol", Note: "", Description: "Protocol defines traffic protocol (e.g. TCP or UDP).", Comments: [3]string{"" /* encoder.HeadComment */, "Protocol defines traffic protocol (e.g. TCP or UDP)." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "tcp", "udp", "icmp", "icmpv6", }, }, }, } doc.Fields[0].AddExample("", examplePortRanges1()) doc.Fields[0].AddExample("", examplePortRanges2()) return doc } func (IngressRule) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "IngressRule", Comments: [3]string{"" /* encoder.HeadComment */, "IngressRule is a ingress rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "IngressRule is a ingress rule.", AppearsIn: []encoder.Appearance{ { TypeName: "RuleConfigV1Alpha1", FieldName: "ingress", }, }, Fields: []encoder.Doc{ { Name: "subnet", Type: "Prefix", Note: "", Description: "Subnet defines a source subnet.", Comments: [3]string{"" /* encoder.HeadComment */, "Subnet defines a source subnet." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "except", Type: "Prefix", Note: "", Description: "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`.", Comments: [3]string{"" /* encoder.HeadComment */, "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", netip.MustParsePrefix("10.3.4.0/24")) doc.Fields[0].AddExample("", netip.MustParsePrefix("2001:db8::/32")) doc.Fields[0].AddExample("", netip.MustParsePrefix("1.3.4.5/32")) return doc } func (StaticHostConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "StaticHostConfig", Comments: [3]string{"" /* encoder.HeadComment */, "StaticHostConfig is a config document to set /etc/hosts entries." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "StaticHostConfig is a config document to set /etc/hosts entries.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "IP address (IPv4 or IPv6) to map the hostnames to.", Comments: [3]string{"" /* encoder.HeadComment */, "IP address (IPv4 or IPv6) to map the hostnames to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hostnames", Type: "[]string", Note: "", Description: "List of hostnames to map to the IP address.", Comments: [3]string{"" /* encoder.HeadComment */, "List of hostnames to map to the IP address." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleStaticHostConfigV1Alpha1()) return doc } func (TCPProbeConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "TCPProbeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "TCPProbeConfig is a config document to configure network TCP connectivity probes." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "TCPProbeConfig is a config document to configure network TCP connectivity probes.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the probe.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the probe." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonProbeConfig", Inline: true, }, { Name: "endpoint", Type: "string", Note: "", Description: "Endpoint to probe in the format host:port.", Comments: [3]string{"" /* encoder.HeadComment */, "Endpoint to probe in the format host:port." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "timeout", Type: "Duration", Note: "", Description: "Timeout for the probe.\nDefaults to 10s.", Comments: [3]string{"" /* encoder.HeadComment */, "Timeout for the probe." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleTCPProbeConfigV1Alpha1()) doc.Fields[1].AddExample("", "proxy-check") doc.Fields[3].AddExample("", "proxy.example.com:3128") doc.Fields[4].AddExample("", 10*time.Second) return doc } func (CommonProbeConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "CommonProbeConfig", Comments: [3]string{"" /* encoder.HeadComment */, "CommonProbeConfig holds fields common to all probe types." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "CommonProbeConfig holds fields common to all probe types.", AppearsIn: []encoder.Appearance{ { TypeName: "TCPProbeConfigV1Alpha1", FieldName: "", }, }, Fields: []encoder.Doc{ { Name: "interval", Type: "Duration", Note: "", Description: "Interval between probe attempts.\nDefaults to 1s.", Comments: [3]string{"" /* encoder.HeadComment */, "Interval between probe attempts." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "failureThreshold", Type: "int", Note: "", Description: "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).", Comments: [3]string{"" /* encoder.HeadComment */, "Number of consecutive failures for the probe to be considered failed after having succeeded." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", time.Second) doc.Fields[1].AddExample("", 3) return doc } func (TimeSyncConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "TimeSyncConfig", Comments: [3]string{"" /* encoder.HeadComment */, "TimeSyncConfig is a config document to configure time synchronization (NTP)." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "TimeSyncConfig is a config document to configure time synchronization (NTP).", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "enabled", Type: "bool", Note: "", Description: "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.", Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if the time synchronization is enabled for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "bootTimeout", Type: "Duration", Note: "", Description: "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ntp", Type: "NTPConfig", Note: "", Description: "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies NTP configuration to sync the time over network." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ptp", Type: "PTPConfig", Note: "", Description: "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleTimeSyncConfigV1Alpha1()) doc.AddExample("", exampleTimeSyncConfigV1Alpha2()) return doc } func (NTPConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "NTPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "NTPConfig represents a NTP server configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "NTPConfig represents a NTP server configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "TimeSyncConfigV1Alpha1", FieldName: "ntp", }, }, Fields: []encoder.Doc{ { Name: "servers", Type: "[]string", Note: "", Description: "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies time (NTP) servers to use for setting the system time." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (PTPConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "PTPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "PTPConfig represents a PTP (Precision Time Protocol) configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "PTPConfig represents a PTP (Precision Time Protocol) configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "TimeSyncConfigV1Alpha1", FieldName: "ptp", }, }, Fields: []encoder.Doc{ { Name: "devices", Type: "[]string", Note: "", Description: "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n", Comments: [3]string{"" /* encoder.HeadComment */, "description: |" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (VLANConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VLANConfig", Comments: [3]string{"" /* encoder.HeadComment */, "VLANConfig is a config document to create a VLAN (virtual LAN) over a parent link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VLANConfig is a config document to create a VLAN (virtual LAN) over a parent link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the VLAN link (interface) to be created.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the VLAN link (interface) to be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "vlanID", Type: "uint16", Note: "", Description: "VLAN ID to be used for the VLAN link.", Comments: [3]string{"" /* encoder.HeadComment */, "VLAN ID to be used for the VLAN link." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "vlanMode", Type: "VLANProtocol", Note: "", Description: "Set the VLAN mode to use.\nIf not set, defaults to '802.1q'.", Comments: [3]string{"" /* encoder.HeadComment */, "Set the VLAN mode to use." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "802.1q", "802.1ad", }, }, { Name: "parent", Type: "string", Note: "", Description: "Name of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the parent link (interface) on which the VLAN link will be created." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleVLANConfigV1Alpha1()) doc.Fields[1].AddExample("", "enp0s3.34") doc.Fields[2].AddExample("", 34) doc.Fields[3].AddExample("", "802.1q") doc.Fields[4].AddExample("", "enp0s3") return doc } func (WireguardConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "WireguardConfig", Comments: [3]string{"" /* encoder.HeadComment */, "WireguardConfig is a config document to create and configure a Wireguard network link." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "WireguardConfig is a config document to create and configure a Wireguard network link.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the Wireguard link (interface).", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the Wireguard link (interface)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "privateKey", Type: "string", Note: "", Description: "Specifies a private key configuration (base64 encoded).\nCan be generated by `wg genkey`.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies a private key configuration (base64 encoded)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "listenPort", Type: "int", Note: "", Description: "Specifies a device's listening port (UDP).\nIf not specified, a random port will be chosen.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies a device's listening port (UDP)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "firewallMark", Type: "int", Note: "", Description: "Specifies a device's firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies a device's firewall mark." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "peers", Type: "[]WireguardPeer", Note: "", Description: "Specifies a list of peer configurations to apply to a device.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies a list of peer configurations to apply to a device." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Type: "CommonLinkConfig", Inline: true, }, }, } doc.AddExample("", exampleWireguardConfigV1Alpha1()) doc.Fields[1].AddExample("", "wg.int") return doc } func (WireguardPeer) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "WireguardPeer", Comments: [3]string{"" /* encoder.HeadComment */, "WireguardPeer describes a Wireguard peer configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "WireguardPeer describes a Wireguard peer configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "WireguardConfigV1Alpha1", FieldName: "peers", }, }, Fields: []encoder.Doc{ { Name: "publicKey", Type: "string", Note: "", Description: "Specifies the public key of this peer.\nCan be extracted from private key by running `wg pubkey < private.key`.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the public key of this peer." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "presharedKey", Type: "string", Note: "", Description: "Specifies the preshared key for this peer (base64 encoded).\nCan be generated by `wg genpsk`.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the preshared key for this peer (base64 encoded)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "endpoint", Type: "AddrPort", Note: "", Description: "Specifies the endpoint of this peer entry.\nFormat: :.\nIf not set, the peer should connect to us without us connecting to it first.", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the endpoint of this peer entry." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "persistentKeepaliveInterval", Type: "Duration", Note: "", Description: "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", Comments: [3]string{"" /* encoder.HeadComment */, "Specifies the persistent keepalive interval for this peer." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "allowedIPs", Type: "[]Prefix", Note: "", Description: "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.", Comments: [3]string{"" /* encoder.HeadComment */, "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } // GetFileDoc returns documentation for the file network_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "network", Description: "Package network provides network machine configuration documents.\n", Structs: []*encoder.Doc{ BlackholeRouteConfigV1Alpha1{}.Doc(), BondConfigV1Alpha1{}.Doc(), BridgeConfigV1Alpha1{}.Doc(), BridgeSTPConfig{}.Doc(), BridgeVLANConfig{}.Doc(), VRFConfigV1Alpha1{}.Doc(), DefaultActionConfigV1Alpha1{}.Doc(), DHCPv4ConfigV1Alpha1{}.Doc(), DHCPv6ConfigV1Alpha1{}.Doc(), DummyLinkConfigV1Alpha1{}.Doc(), EthernetConfigV1Alpha1{}.Doc(), EthernetRingsConfig{}.Doc(), EthernetChannelsConfig{}.Doc(), HCloudVIPConfigV1Alpha1{}.Doc(), HostnameConfigV1Alpha1{}.Doc(), KubeSpanConfigV1Alpha1{}.Doc(), KubeSpanFiltersConfig{}.Doc(), KubespanEndpointsConfigV1Alpha1{}.Doc(), Layer2VIPConfigV1Alpha1{}.Doc(), LinkConfigV1Alpha1{}.Doc(), CommonLinkConfig{}.Doc(), AddressConfig{}.Doc(), RouteConfig{}.Doc(), LinkAliasConfigV1Alpha1{}.Doc(), LinkSelector{}.Doc(), ResolverConfigV1Alpha1{}.Doc(), NameserverConfig{}.Doc(), SearchDomainsConfig{}.Doc(), RoutingRuleConfigV1Alpha1{}.Doc(), RuleConfigV1Alpha1{}.Doc(), RulePortSelector{}.Doc(), IngressRule{}.Doc(), StaticHostConfigV1Alpha1{}.Doc(), TCPProbeConfigV1Alpha1{}.Doc(), CommonProbeConfig{}.Doc(), TimeSyncConfigV1Alpha1{}.Doc(), NTPConfig{}.Doc(), PTPConfig{}.Doc(), VLANConfigV1Alpha1{}.Doc(), WireguardConfigV1Alpha1{}.Doc(), WireguardPeer{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/network/port_range.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "fmt" "slices" "strconv" "strings" ) // PortRange is a port range. // //docgen:nodoc type PortRange struct { Lo uint16 Hi uint16 } // UnmarshalYAML is a custom unmarshaller for `PortRange`. func (pr *PortRange) UnmarshalYAML(unmarshal func(any) error) error { var port uint16 if err := unmarshal(&port); err == nil { pr.Lo = port pr.Hi = port return nil } var rangeStr string if err := unmarshal(&rangeStr); err != nil { return err } lo, hi, ok := strings.Cut(rangeStr, "-") if !ok { return fmt.Errorf("invalid port range: %q", rangeStr) } prLo, err := strconv.ParseUint(lo, 10, 16) if err != nil { return fmt.Errorf("invalid port range: %q", rangeStr) } prHi, err := strconv.ParseUint(hi, 10, 16) if err != nil { return fmt.Errorf("invalid port range: %q", rangeStr) } pr.Lo, pr.Hi = uint16(prLo), uint16(prHi) return nil } // MarshalYAML is a custom marshaller for `PortRange`. func (pr PortRange) MarshalYAML() (any, error) { if pr.Lo == pr.Hi { return pr.Lo, nil } return fmt.Sprintf("%d-%d", pr.Lo, pr.Hi), nil } // String implements fmt.Stringer interface. func (pr PortRange) String() string { return fmt.Sprintf("%d-%d", pr.Lo, pr.Hi) } // PortRanges is a slice of port ranges. // //docgen:nodoc type PortRanges []PortRange // Validate the port ranges. func (prs PortRanges) Validate() error { clone := slices.Clone(prs) slices.SortFunc(clone, func(a, b PortRange) int { return cmp.Compare(a.Lo, b.Lo) }) for i, pr := range clone { if pr.Lo > pr.Hi { return fmt.Errorf("invalid port range: %s", pr) } if i > 0 { prev := clone[i-1] if pr.Lo == prev.Lo { return fmt.Errorf("invalid port range: %s, overlaps with %s", pr, prev) } if pr.Lo <= prev.Hi { return fmt.Errorf("invalid port range: %s, overlaps with %s", pr, prev) } } } return nil } func examplePortRanges1() PortRanges { return PortRanges{ {Lo: 80, Hi: 80}, {Lo: 443, Hi: 443}, } } func examplePortRanges2() PortRanges { return PortRanges{ {Lo: 1200, Hi: 1299}, {Lo: 8080, Hi: 8080}, } } ================================================ FILE: pkg/machinery/config/types/network/port_range_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) func TestPortRange(t *testing.T) { t.Run("MarshalYAML", func(t *testing.T) { for _, test := range []struct { name string pr network.PortRange expected string }{ { name: "single port", pr: network.PortRange{Lo: 80, Hi: 80}, expected: "80\n", }, { name: "port range", pr: network.PortRange{Lo: 80, Hi: 443}, expected: "80-443\n", }, } { t.Run(test.name, func(t *testing.T) { marshaled, err := yaml.Marshal(test.pr) require.NoError(t, err) assert.Equal(t, test.expected, string(marshaled)) }) } }) t.Run("UnmarshalYAML", func(t *testing.T) { for _, test := range []struct { name string yaml string expected network.PortRange }{ { name: "single port", yaml: "80\n", expected: network.PortRange{Lo: 80, Hi: 80}, }, { name: "port range", yaml: "80-443\n", expected: network.PortRange{Lo: 80, Hi: 443}, }, } { t.Run(test.name, func(t *testing.T) { var pr network.PortRange err := yaml.Unmarshal([]byte(test.yaml), &pr) require.NoError(t, err) assert.Equal(t, test.expected, pr) }) } }) } func TestPortRanges(t *testing.T) { t.Run("Validate", func(t *testing.T) { for _, test := range []struct { name string prs network.PortRanges expectedError string }{ { name: "empty", prs: network.PortRanges{}, }, { name: "valid", prs: network.PortRanges{{Lo: 80, Hi: 80}, {Lo: 443, Hi: 443}, {Lo: 8080, Hi: 8081}}, }, { name: "inversion", prs: network.PortRanges{{Lo: 8081, Hi: 8080}}, expectedError: "invalid port range: 8081-8080", }, { name: "overlap", prs: network.PortRanges{{Lo: 1000, Hi: 2000}, {Lo: 80, Hi: 80}, {Lo: 1500, Hi: 2500}}, expectedError: "invalid port range: 1500-2500, overlaps with 1000-2000", }, { name: "duplicate", prs: network.PortRanges{{Lo: 1000, Hi: 1000}, {Lo: 80, Hi: 80}, {Lo: 1000, Hi: 1000}}, expectedError: "invalid port range: 1000-1000, overlaps with 1000-1000", }, } { t.Run(test.name, func(t *testing.T) { err := test.prs.Validate() if test.expectedError != "" { require.EqualError(t, err, test.expectedError) } else { require.NoError(t, err) } }) } }) } ================================================ FILE: pkg/machinery/config/types/network/resolver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "net/netip" "slices" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // ResolverKind is a ResolverConfig document kind. const ResolverKind = "ResolverConfig" func init() { registry.Register(ResolverKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &ResolverConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkResolverConfig = &ResolverConfigV1Alpha1{} _ container.V1Alpha1ConflictValidator = &ResolverConfigV1Alpha1{} ) // ResolverConfigV1Alpha1 is a config document to configure DNS resolving. // // examples: // - value: exampleResolverConfigV1Alpha1() // - value: exampleResolverConfigV1Alpha2() // alias: ResolverConfig // schemaRoot: true // schemaMeta: v1alpha1/ResolverConfig type ResolverConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // A list of nameservers (DNS servers) to use for resolving domain names. // // Nameservers are used to resolve domain names on the host, and they are also // propagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster. // // This overrides any nameservers obtained via DHCP or platform configuration. // Default configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers. ResolverNameservers []NameserverConfig `yaml:"nameservers,omitempty"` // description: | // Configuration for search domains (in /etc/resolv.conf). // // The default is to derive search domains from the hostname FQDN. ResolverSearchDomains SearchDomainsConfig `yaml:"searchDomains,omitempty"` } // NameserverConfig represents a single nameserver configuration. type NameserverConfig struct { // description: | // The IP address of the nameserver. // examples: // - value: > // Addr{netip.MustParseAddr("10.0.0.1")} // schema: // type: string // pattern: ^[0-9a-f.:]+$ Address Addr `yaml:"address"` } // SearchDomainsConfig represents search domains configuration. type SearchDomainsConfig struct { // description: | // A list of search domains to be used for DNS resolution. // // Search domains are appended to unqualified domain names during DNS resolution. // For example, if "example.com" is a search domain and a user tries to resolve // "host", the system will attempt to resolve "host.example.com". // // This overrides any search domains obtained via DHCP or platform configuration. // The default configuration derives the search domain from the hostname FQDN. SearchDomains []string `yaml:"domains,omitempty"` // description: | // Disable default search domain configuration from hostname FQDN. // // When set to true, the system will not derive search domains from the hostname FQDN. // This allows for a custom configuration of search domains without any defaults. SearchDisableDefault *bool `yaml:"disableDefault,omitempty"` } // NewResolverConfigV1Alpha1 creates a new ResolverConfig config document. func NewResolverConfigV1Alpha1() *ResolverConfigV1Alpha1 { return &ResolverConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ResolverKind, MetaAPIVersion: "v1alpha1", }, } } func exampleResolverConfigV1Alpha1() *ResolverConfigV1Alpha1 { cfg := NewResolverConfigV1Alpha1() cfg.ResolverNameservers = []NameserverConfig{ { Address: Addr{netip.MustParseAddr("1.1.1.1")}, }, { Address: Addr{netip.MustParseAddr("ff08::1")}, }, } cfg.ResolverSearchDomains = SearchDomainsConfig{ SearchDomains: []string{"example.com"}, } return cfg } func exampleResolverConfigV1Alpha2() *ResolverConfigV1Alpha1 { cfg := NewResolverConfigV1Alpha1() cfg.ResolverSearchDomains = SearchDomainsConfig{ SearchDisableDefault: new(true), } return cfg } // Clone implements config.Document interface. func (s *ResolverConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface. func (s *ResolverConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error { if v1alpha1Cfg.SearchDomains() != nil { return errors.New(".machine.network.searchDomains is already set in v1alpha1 config") } if v1alpha1Cfg.Resolvers() != nil { return errors.New(".machine.network.nameservers is already set in v1alpha1 config") } if v1alpha1Cfg.DisableSearchDomain() { return errors.New(".machine.network.disableSearchDomain is already set in v1alpha1 config") } return nil } // Resolvers implements NetworkResolverConfig interface. func (s *ResolverConfigV1Alpha1) Resolvers() []netip.Addr { return xslices.Map(s.ResolverNameservers, func(ns NameserverConfig) netip.Addr { return ns.Address.Addr }) } // SearchDomains implements NetworkResolverConfig interface. func (s *ResolverConfigV1Alpha1) SearchDomains() []string { return slices.Clone(s.ResolverSearchDomains.SearchDomains) } // DisableSearchDomain implements NetworkResolverConfig interface. func (s *ResolverConfigV1Alpha1) DisableSearchDomain() bool { return pointer.SafeDeref(s.ResolverSearchDomains.SearchDisableDefault) } ================================================ FILE: pkg/machinery/config/types/network/resolver_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) //go:embed testdata/resolverconfig.yaml var expectedResolverConfigDocument []byte func TestResolverConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewResolverConfigV1Alpha1() cfg.ResolverNameservers = []network.NameserverConfig{ { Address: network.Addr{Addr: netip.MustParseAddr("10.0.0.1")}, }, { Address: network.Addr{Addr: netip.MustParseAddr("2001:4860:4860::8888")}, }, } cfg.ResolverSearchDomains = network.SearchDomainsConfig{ SearchDomains: []string{"example.org", "example.com"}, SearchDisableDefault: new(false), } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedResolverConfigDocument, marshaled) } func TestResolverConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedResolverConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.ResolverConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.ResolverKind, }, ResolverNameservers: []network.NameserverConfig{ { Address: network.Addr{Addr: netip.MustParseAddr("10.0.0.1")}, }, { Address: network.Addr{Addr: netip.MustParseAddr("2001:4860:4860::8888")}, }, }, ResolverSearchDomains: network.SearchDomainsConfig{ SearchDomains: []string{"example.org", "example.com"}, SearchDisableDefault: new(false), }, }, docs[0]) } func TestResolverV1Alpha1Validate(t *testing.T) { t.Parallel() for _, test := range []struct { name string v1alpha1Cfg *v1alpha1.Config cfg func() *network.ResolverConfigV1Alpha1 expectedError string }{ { name: "empty", v1alpha1Cfg: &v1alpha1.Config{}, cfg: network.NewResolverConfigV1Alpha1, }, { name: "v1alpha1 nameservers set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NameServers: []string{"1.1.1.1"}, }, }, }, cfg: network.NewResolverConfigV1Alpha1, expectedError: ".machine.network.nameservers is already set in v1alpha1 config", }, { name: "v1alpha1 search domains set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config Searches: []string{"cluster.org"}, }, }, }, cfg: network.NewResolverConfigV1Alpha1, expectedError: ".machine.network.searchDomains is already set in v1alpha1 config", }, { name: "v1alpha1 disable search domains set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ //nolint:staticcheck // legacy config NetworkDisableSearchDomain: new(true), }, }, }, cfg: network.NewResolverConfigV1Alpha1, expectedError: ".machine.network.disableSearchDomain is already set in v1alpha1 config", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() err := test.cfg().V1Alpha1ConflictValidate(test.v1alpha1Cfg) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/routing_rule.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "slices" "strconv" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // RoutingRuleKind is a RoutingRule config document kind. const RoutingRuleKind = "RoutingRuleConfig" func init() { registry.Register(RoutingRuleKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RoutingRuleConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkRoutingRuleConfig = &RoutingRuleConfigV1Alpha1{} _ config.NamedDocument = &RoutingRuleConfigV1Alpha1{} _ config.Validator = &RoutingRuleConfigV1Alpha1{} ) // RoutingRuleConfigV1Alpha1 is a config document to configure Linux policy routing rules. // // examples: // - value: exampleRoutingRuleConfigV1Alpha1() // alias: RoutingRuleConfig // schemaRoot: true // schemaMeta: v1alpha1/RoutingRuleConfig type RoutingRuleConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Priority of the routing rule. // Lower values are matched first. // Must be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]). // Must be unique across all routing rules in the configuration. // // examples: // - value: "1000" // schemaRequired: true // schema: // type: string RulePriority string `yaml:"name"` // description: | // Source address prefix to match. // If empty, matches all sources. // // examples: // - value: > // "10.0.0.0/8" // schema: // type: string RuleSrc Prefix `yaml:"src,omitempty"` // description: | // Destination address prefix to match. // If empty, matches all destinations. // // examples: // - value: > // "192.168.0.0/16" // schema: // type: string RuleDst Prefix `yaml:"dst,omitempty"` // description: | // The routing table to look up if the rule matches. // // examples: // - value: "100" // schemaRequired: true // schema: // type: string RuleTable nethelpers.RoutingTable `yaml:"table"` // description: | // The action to perform when the rule matches. // Defaults to "unicast" (table lookup). // // values: // - unicast // - blackhole // - unreachable // - prohibit // schema: // type: integer RuleAction nethelpers.RoutingRuleAction `yaml:"action,omitempty"` // description: | // Match packets arriving on this interface. // // examples: // - value: > // "eth0" // schema: // type: string RuleIIFName string `yaml:"iifName,omitempty"` // description: | // Match packets going out on this interface. // // examples: // - value: > // "eth1" // schema: // type: string RuleOIFName string `yaml:"oifName,omitempty"` // description: | // Match packets with this firewall mark value. // // examples: // - value: > // uint32(0x100) // schema: // type: integer RuleFwMark uint32 `yaml:"fwMark,omitempty"` // description: | // Mask for the firewall mark comparison. // // examples: // - value: > // uint32(0xff00) // schema: // type: integer RuleFwMask uint32 `yaml:"fwMask,omitempty"` } // NewRoutingRuleConfigV1Alpha1 creates a new RoutingRuleConfig config document. func NewRoutingRuleConfigV1Alpha1(priority uint32) *RoutingRuleConfigV1Alpha1 { return &RoutingRuleConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RoutingRuleKind, MetaAPIVersion: "v1alpha1", }, RulePriority: strconv.FormatUint(uint64(priority), 10), } } func exampleRoutingRuleConfigV1Alpha1() *RoutingRuleConfigV1Alpha1 { cfg := NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleSrc = Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleAction = nethelpers.RoutingRuleActionUnicast return cfg } // Clone implements config.Document interface. func (s *RoutingRuleConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *RoutingRuleConfigV1Alpha1) Name() string { return s.RulePriority } // RoutingRuleConfig implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) RoutingRuleConfig() {} var reservedPriorities = []uint32{ constants.LinuxReservedRulePriorityLocal, constants.KubeSpanDefaultRulePriority, constants.LinuxReservedRulePriorityMain, constants.LinuxReservedRulePriorityDefault, } // Validate implements config.Validator interface. // //nolint:gocyclo func (s *RoutingRuleConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if s.RulePriority == "" { errs = errors.Join(errs, errors.New("name must be specified")) } priority, err := strconv.ParseUint(s.RulePriority, 10, 32) if err != nil { errs = errors.Join(errs, fmt.Errorf("invalid name: must be priority parsable unsigned integer: %w", err)) } if slices.Contains(reservedPriorities, uint32(priority)) { errs = errors.Join(errs, fmt.Errorf("priority must be between 1 and 32765 (excluding reserved priorities %v)", reservedPriorities)) } if s.RuleTable == nethelpers.TableUnspec && (s.RuleAction == nethelpers.RoutingRuleActionUnspec || s.RuleAction == nethelpers.RoutingRuleActionUnicast) { errs = errors.Join(errs, errors.New("either table or a non-unicast action must be specified")) } if s.RuleFwMask != 0 && s.RuleFwMark == 0 { errs = errors.Join(errs, errors.New("fwMask requires fwMark to be set")) } return nil, errs } // Src implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) Src() optional.Optional[netip.Prefix] { if s.RuleSrc == (Prefix{}) { return optional.None[netip.Prefix]() } return optional.Some(s.RuleSrc.Prefix) } // Dst implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) Dst() optional.Optional[netip.Prefix] { if s.RuleDst == (Prefix{}) { return optional.None[netip.Prefix]() } return optional.Some(s.RuleDst.Prefix) } // Table implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) Table() nethelpers.RoutingTable { return s.RuleTable } // Priority implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) Priority() uint32 { priority, _ := strconv.ParseUint(s.RulePriority, 10, 32) //nolint:errcheck return uint32(priority) } // Action implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) Action() nethelpers.RoutingRuleAction { return s.RuleAction } // IIFName implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) IIFName() string { return s.RuleIIFName } // OIFName implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) OIFName() string { return s.RuleOIFName } // FwMark implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) FwMark() uint32 { return s.RuleFwMark } // FwMask implements NetworkRoutingRuleConfig interface. func (s *RoutingRuleConfigV1Alpha1) FwMask() uint32 { return s.RuleFwMask } ================================================ FILE: pkg/machinery/config/types/network/routing_rule_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/routingruleconfig.yaml var expectedRoutingRuleConfigDocument []byte //nolint:goconst func TestRoutingRuleConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleAction = nethelpers.RoutingRuleActionUnicast marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedRoutingRuleConfigDocument, marshaled) } //nolint:goconst func TestRoutingRuleConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedRoutingRuleConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.RoutingRuleConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.RoutingRuleKind, }, RulePriority: "1000", RuleSrc: network.Prefix{netip.MustParsePrefix("10.0.0.0/8")}, RuleTable: nethelpers.RoutingTable(100), RuleAction: nethelpers.RoutingRuleActionUnicast, }, docs[0]) } //nolint:goconst func TestRoutingRuleConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.RoutingRuleConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.RoutingRuleConfigV1Alpha1 { return &network.RoutingRuleConfigV1Alpha1{} }, expectedError: "name must be specified\ninvalid name: must be priority parsable unsigned integer: strconv.ParseUint: parsing \"\": invalid syntax\npriority must be between 1 and 32765 (excluding reserved priorities [0 32500 32766 32767])\neither table or a non-unicast action must be specified", //nolint:lll }, { name: "fwMask without fwMark", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleFwMask = 0xff00 return cfg }, expectedError: "fwMask requires fwMark to be set", }, { name: "missing name", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := &network.RoutingRuleConfigV1Alpha1{} cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, expectedError: "name must be specified", }, { name: "reserved priority 32766", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(32766) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, expectedError: "priority must be between 1 and 32765 (excluding reserved priorities [0 32500 32766 32767])", }, { name: "reserved priority 32767", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(32767) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, expectedError: "priority must be between 1 and 32765 (excluding reserved priorities [0 32500 32766 32767])", }, { name: "valid priority 1", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, }, { name: "valid priority 32765", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(32765) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, }, { name: "no table no action", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1000) return cfg }, expectedError: "either table or a non-unicast action must be specified", }, { name: "valid with table only", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleTable = nethelpers.RoutingTable(100) return cfg }, }, { name: "valid with blackhole action", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleAction = nethelpers.RoutingRuleActionBlackhole return cfg }, }, { name: "valid with all fields", cfg: func() *network.RoutingRuleConfigV1Alpha1 { cfg := network.NewRoutingRuleConfigV1Alpha1(1000) cfg.RuleSrc = network.Prefix{netip.MustParsePrefix("10.0.0.0/8")} cfg.RuleDst = network.Prefix{netip.MustParsePrefix("192.168.0.0/16")} cfg.RuleTable = nethelpers.RoutingTable(100) cfg.RuleAction = nethelpers.RoutingRuleActionUnicast cfg.RuleIIFName = "eth0" cfg.RuleOIFName = "eth1" cfg.RuleFwMark = 0x100 cfg.RuleFwMask = 0xff00 return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.ErrorContains(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/rule_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "net/netip" "github.com/siderolabs/gen/value" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // RuleConfigKind is a rule config document kind. const RuleConfigKind = "NetworkRuleConfig" func init() { registry.Register(RuleConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &RuleConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkRuleConfigRules = &RuleConfigV1Alpha1{} _ config.NetworkRuleConfigSignal = &RuleConfigV1Alpha1{} _ config.NamedDocument = &RuleConfigV1Alpha1{} _ config.Validator = &RuleConfigV1Alpha1{} ) // RuleConfigV1Alpha1 is a network firewall rule config document. // // examples: // - value: exampleRuleConfigV1Alpha1() // alias: NetworkRuleConfig // schemaRoot: true // schemaMeta: v1alpha1/NetworkRuleConfig type RuleConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the config document. // schemaRequired: true MetaName string `yaml:"name"` // description: | // Port selector defines which ports and protocols on the host are affected by the rule. PortSelector RulePortSelector `yaml:"portSelector"` // description: | // Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`. Ingress IngressConfig `yaml:"ingress" merge:"replace"` } // RulePortSelector is a port selector for the network rule. type RulePortSelector struct { // description: | // Ports defines a list of port ranges or single ports. // The port ranges are inclusive, and should not overlap. // examples: // - value: > // examplePortRanges1() // - value: > // examplePortRanges2() // schema: // type: array // items: // oneOf: // - type: integer // - type: string Ports PortRanges `yaml:"ports" merge:"replace"` // description: | // Protocol defines traffic protocol (e.g. TCP or UDP). // values: // - "tcp" // - "udp" // - "icmp" // - "icmpv6" Protocol nethelpers.Protocol `yaml:"protocol"` } // IngressConfig is a ingress config. // //docgen:alias type IngressConfig []IngressRule // IngressRule is a ingress rule. type IngressRule struct { // description: | // Subnet defines a source subnet. // examples: // - value: > // netip.MustParsePrefix("10.3.4.0/24") // - value: > // netip.MustParsePrefix("2001:db8::/32") // - value: > // netip.MustParsePrefix("1.3.4.5/32") // schema: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ Subnet netip.Prefix `yaml:"subnet"` // description: | // Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`. // schema: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ Except Prefix `yaml:"except,omitempty"` } // NewRuleConfigV1Alpha1 creates a new RuleConfig config document. func NewRuleConfigV1Alpha1() *RuleConfigV1Alpha1 { return &RuleConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: RuleConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleRuleConfigV1Alpha1() *RuleConfigV1Alpha1 { cfg := NewRuleConfigV1Alpha1() cfg.MetaName = "ingress-apid" cfg.PortSelector.Protocol = nethelpers.ProtocolTCP cfg.PortSelector.Ports = PortRanges{ {Lo: 50000, Hi: 50000}, } cfg.Ingress = IngressConfig{ { Subnet: netip.MustParsePrefix("192.168.0.0/16"), }, } return cfg } // Name implements config.NamedDocument interface. func (s *RuleConfigV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *RuleConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. func (s *RuleConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.MetaName == "" { return nil, errors.New("name is required") } if len(s.PortSelector.Ports) == 0 { return nil, errors.New("portSelector.ports is required") } if err := s.PortSelector.Ports.Validate(); err != nil { return nil, err } for _, rule := range s.Ingress { if !rule.Subnet.IsValid() { return nil, fmt.Errorf("invalid subnet: %s", rule.Subnet) } if !value.IsZero(rule.Except) && !rule.Except.IsValid() { return nil, fmt.Errorf("invalid except: %s", rule.Except) } } return nil, nil } // NetworkRuleConfigSignal implements config.NetworkRuleConfigSignal interface. func (s *RuleConfigV1Alpha1) NetworkRuleConfigSignal() {} // Rules implements config.NetworkRuleConfigRules interface. func (s *RuleConfigV1Alpha1) Rules() []config.NetworkRule { return []config.NetworkRule{s} } // Protocol implements config.NetworkRule interface. func (s *RuleConfigV1Alpha1) Protocol() nethelpers.Protocol { return s.PortSelector.Protocol } // PortRanges implements config.NetworkRule interface. func (s *RuleConfigV1Alpha1) PortRanges() [][2]uint16 { return xslices.Map(s.PortSelector.Ports, func(pr PortRange) [2]uint16 { return [2]uint16{pr.Lo, pr.Hi} }) } // Subnets implements config.NetworkRule interface. func (s *RuleConfigV1Alpha1) Subnets() []netip.Prefix { return xslices.Map(s.Ingress, func(rule IngressRule) netip.Prefix { return rule.Subnet }) } // ExceptSubnets implements config.NetworkRule interface. func (s *RuleConfigV1Alpha1) ExceptSubnets() []netip.Prefix { return xslices.Map( xslices.Filter( s.Ingress, func(rule IngressRule) bool { return rule.Except.IsValid() }, ), func(rule IngressRule) netip.Prefix { return rule.Except.Prefix }, ) } ================================================ FILE: pkg/machinery/config/types/network/rule_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/ruleconfig.yaml var expectedRuleConfigDocument []byte func TestRuleConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewRuleConfigV1Alpha1() cfg.MetaName = "test" cfg.PortSelector = network.RulePortSelector{ Protocol: nethelpers.ProtocolUDP, Ports: network.PortRanges{ {Lo: 53, Hi: 53}, {Lo: 8000, Hi: 9000}, }, } cfg.Ingress = network.IngressConfig{ { Subnet: netip.MustParsePrefix("192.168.0.0/16"), Except: network.Prefix{netip.MustParsePrefix("192.168.0.3/32")}, }, { Subnet: netip.MustParsePrefix("2001::/16"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedRuleConfigDocument, marshaled) } func TestRuleConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedRuleConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.RuleConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.RuleConfigKind, }, MetaName: "test", PortSelector: network.RulePortSelector{ Protocol: nethelpers.ProtocolUDP, Ports: network.PortRanges{ {Lo: 53, Hi: 53}, {Lo: 8000, Hi: 9000}, }, }, Ingress: network.IngressConfig{ { Subnet: netip.MustParsePrefix("192.168.0.0/16"), Except: network.Prefix{netip.MustParsePrefix("192.168.0.3/32")}, }, { Subnet: netip.MustParsePrefix("2001::/16"), }, }, }, docs[0]) } func TestRuleConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.RuleConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: network.NewRuleConfigV1Alpha1, expectedError: "name is required", }, { name: "no ports", cfg: func() *network.RuleConfigV1Alpha1 { cfg := network.NewRuleConfigV1Alpha1() cfg.MetaName = "-" return cfg }, expectedError: "portSelector.ports is required", }, { name: "invalid port range", cfg: func() *network.RuleConfigV1Alpha1 { cfg := network.NewRuleConfigV1Alpha1() cfg.MetaName = "-" cfg.PortSelector.Ports = network.PortRanges{ {Lo: 80, Hi: 80}, {Lo: 80, Hi: 79}, } return cfg }, expectedError: "invalid port range: 80-79", }, { name: "invalid subnet", cfg: func() *network.RuleConfigV1Alpha1 { cfg := network.NewRuleConfigV1Alpha1() cfg.MetaName = "--" cfg.PortSelector.Ports = network.PortRanges{ {Lo: 80, Hi: 80}, } cfg.Ingress = network.IngressConfig{ {}, } return cfg }, expectedError: "invalid subnet: invalid Prefix", }, { name: "valid", cfg: func() *network.RuleConfigV1Alpha1 { cfg := network.NewRuleConfigV1Alpha1() cfg.MetaName = "--" cfg.PortSelector.Ports = network.PortRanges{ {Lo: 80, Hi: 80}, {Lo: 6443, Hi: 6444}, } cfg.Ingress = network.IngressConfig{ { Subnet: netip.MustParsePrefix("192.168.0.0/16"), Except: network.Prefix{netip.MustParsePrefix("192.168.3.0/24")}, }, { Subnet: netip.MustParsePrefix("2001::/16"), }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/types/network/static_host.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "net/netip" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // StaticHostKind is a StaticHost config document kind. const StaticHostKind = "StaticHostConfig" func init() { registry.Register(StaticHostKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &StaticHostConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NamedDocument = &StaticHostConfigV1Alpha1{} _ config.Validator = &StaticHostConfigV1Alpha1{} _ config.NetworkStaticHostConfig = &StaticHostConfigV1Alpha1{} ) // StaticHostConfigV1Alpha1 is a config document to set /etc/hosts entries. // // examples: // - value: exampleStaticHostConfigV1Alpha1() // alias: StaticHostConfig // schemaRoot: true // schemaMeta: v1alpha1/StaticHostConfig type StaticHostConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // IP address (IPv4 or IPv6) to map the hostnames to. // schemaRequired: true MetaName string `yaml:"name"` // description: | // List of hostnames to map to the IP address. Hostnames []string `yaml:"hostnames"` } // NewStaticHostConfigV1Alpha1 creates a new StaticHostConfig config document. func NewStaticHostConfigV1Alpha1(name string) *StaticHostConfigV1Alpha1 { return &StaticHostConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: StaticHostKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleStaticHostConfigV1Alpha1() *StaticHostConfigV1Alpha1 { cfg := NewStaticHostConfigV1Alpha1("10.5.0.2") cfg.Hostnames = []string{"my-server", "my-server.example.org"} return cfg } // Clone implements config.Document interface. func (s *StaticHostConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *StaticHostConfigV1Alpha1) Name() string { return s.MetaName } // Validate implements config.Validator interface. func (s *StaticHostConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if s.MetaName == "" { errs = errors.Join(errs, errors.New("name is required")) } if _, err := netip.ParseAddr(s.MetaName); err != nil { errs = errors.Join(errs, errors.New("name must be a valid IP address")) } if len(s.Hostnames) == 0 { errs = errors.Join(errs, errors.New("at least one hostname is required")) } return nil, errs } // IP implements ExtraHost interface. func (s *StaticHostConfigV1Alpha1) IP() string { return s.MetaName } // Aliases implements ExtraHost interface. func (s *StaticHostConfigV1Alpha1) Aliases() []string { return s.Hostnames } ================================================ FILE: pkg/machinery/config/types/network/static_host_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/statichostconfig.yaml var expectedStaticHostConfigDocument []byte func TestStaticHostMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewStaticHostConfigV1Alpha1("10.5.0.2") cfg.Hostnames = []string{"example.org", "example.com"} marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedStaticHostConfigDocument, marshaled) } func TestHostConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.StaticHostConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.StaticHostConfigV1Alpha1 { return network.NewStaticHostConfigV1Alpha1("") }, expectedError: "name is required\nname must be a valid IP address\nat least one hostname is required", }, { name: "invalid ip", cfg: func() *network.StaticHostConfigV1Alpha1 { cfg := network.NewStaticHostConfigV1Alpha1("ab") cfg.Hostnames = []string{"example.org", "example.com"} return cfg }, expectedError: "name must be a valid IP address", }, { name: "no hostnames", cfg: func() *network.StaticHostConfigV1Alpha1 { return network.NewStaticHostConfigV1Alpha1("10.5.0.2") }, expectedError: "at least one hostname is required", }, { name: "valid", cfg: func() *network.StaticHostConfigV1Alpha1 { cfg := network.NewStaticHostConfigV1Alpha1("10.5.0.2") cfg.Hostnames = []string{"example.org", "example.com"} return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/tcp_probe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "time" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // TCPProbeKind is a TCPProbe config document kind. const TCPProbeKind = "TCPProbeConfig" func init() { registry.Register(TCPProbeKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &TCPProbeConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NamedDocument = &TCPProbeConfigV1Alpha1{} _ config.Validator = &TCPProbeConfigV1Alpha1{} _ config.NetworkTCPProbeConfig = &TCPProbeConfigV1Alpha1{} ) // TCPProbeConfigV1Alpha1 is a config document to configure network TCP connectivity probes. // // examples: // - value: exampleTCPProbeConfigV1Alpha1() // alias: TCPProbeConfig // schemaRoot: true // schemaMeta: v1alpha1/TCPProbeConfig type TCPProbeConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the probe. // examples: // - value: > // "proxy-check" // schemaRequired: true MetaName string `yaml:"name"` //nolint:embeddedstructfieldcheck CommonProbeConfig `yaml:",inline"` // description: | // Endpoint to probe in the format host:port. // examples: // - value: > // "proxy.example.com:3128" // schemaRequired: true TCPEndpoint string `yaml:"endpoint"` // description: | // Timeout for the probe. // Defaults to 10s. // examples: // - value: > // 10 * time.Second // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ TCPTimeout time.Duration `yaml:"timeout,omitempty"` } // CommonProbeConfig holds fields common to all probe types. type CommonProbeConfig struct { // description: | // Interval between probe attempts. // Defaults to 1s. // examples: // - value: > // time.Second // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ ProbeInterval time.Duration `yaml:"interval,omitempty"` // description: | // Number of consecutive failures for the probe to be considered failed after having succeeded. // Defaults to 0 (immediately fail on first failure). // examples: // - value: > // 3 ProbeFailureThreshold int `yaml:"failureThreshold,omitempty"` } // NewTCPProbeConfigV1Alpha1 creates a new TCPProbeConfigV1Alpha1 config document. func NewTCPProbeConfigV1Alpha1(name string) *TCPProbeConfigV1Alpha1 { return &TCPProbeConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: TCPProbeKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } // Name implements config.NamedDocument interface. func (p *TCPProbeConfigV1Alpha1) Name() string { return p.MetaName } // Clone implements config.Document interface. func (p *TCPProbeConfigV1Alpha1) Clone() config.Document { return p.DeepCopy() } // Validate implements config.Validator interface. func (p *TCPProbeConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if p.MetaName == "" { errs = errors.Join(errs, errors.New("probe name is required")) } if p.TCPEndpoint == "" { errs = errors.Join(errs, errors.New("TCP probe endpoint is required")) } if p.TCPTimeout < 0 { errs = errors.Join(errs, fmt.Errorf("TCP probe timeout cannot be negative: %s", p.TCPTimeout)) } extraWarnings, extraErrs := p.CommonProbeConfig.Validate() errs = errors.Join(errs, extraErrs) warnings = append(warnings, extraWarnings...) return warnings, errs } func exampleTCPProbeConfigV1Alpha1() *TCPProbeConfigV1Alpha1 { cfg := NewTCPProbeConfigV1Alpha1("proxy-check") cfg.CommonProbeConfig = CommonProbeConfig{ ProbeInterval: time.Second, ProbeFailureThreshold: 3, } cfg.TCPEndpoint = "proxy.example.com:3128" cfg.TCPTimeout = 10 * time.Second return cfg } // Interval implements config.NetworkCommonProbeConfig interface. func (p *CommonProbeConfig) Interval() time.Duration { if p.ProbeInterval == 0 { return time.Second } return p.ProbeInterval } // FailureThreshold implements config.NetworkCommonProbeConfig interface. func (p *CommonProbeConfig) FailureThreshold() int { return p.ProbeFailureThreshold } // Validate the common probe config. func (p *CommonProbeConfig) Validate() ([]string, error) { var errs error if p.ProbeInterval < 0 { errs = errors.Join(errs, fmt.Errorf("probe interval cannot be negative: %s", p.ProbeInterval)) } if p.ProbeFailureThreshold < 0 { errs = errors.Join(errs, fmt.Errorf("probe failure threshold cannot be negative: %d", p.ProbeFailureThreshold)) } return nil, errs } // Endpoint implements config.NetworkTCPProbeConfig interface. func (p *TCPProbeConfigV1Alpha1) Endpoint() string { return p.TCPEndpoint } // Timeout implements config.NetworkTCPProbeConfig interface. func (p *TCPProbeConfigV1Alpha1) Timeout() time.Duration { if p.TCPTimeout == 0 { return 10 * time.Second } return p.TCPTimeout } ================================================ FILE: pkg/machinery/config/types/network/tcp_probe_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/tcpprobeconfig.yaml var expectedTCPProbeConfigDocument []byte func TestTCPProbeConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewTCPProbeConfigV1Alpha1("proxy-check") cfg.CommonProbeConfig = network.CommonProbeConfig{ ProbeInterval: time.Second, ProbeFailureThreshold: 3, } cfg.TCPEndpoint = "proxy.example.com:3128" cfg.TCPTimeout = 10 * time.Second marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedTCPProbeConfigDocument, marshaled) } func TestTCPProbeConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedTCPProbeConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.TCPProbeConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.TCPProbeKind, }, MetaName: "proxy-check", CommonProbeConfig: network.CommonProbeConfig{ ProbeInterval: time.Second, ProbeFailureThreshold: 3, }, TCPEndpoint: "proxy.example.com:3128", TCPTimeout: 10 * time.Second, }, docs[0]) } func TestTCPProbeConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.TCPProbeConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "valid config", cfg: func() *network.TCPProbeConfigV1Alpha1 { c := network.NewTCPProbeConfigV1Alpha1("test-probe") c.CommonProbeConfig = network.CommonProbeConfig{ ProbeInterval: time.Second, ProbeFailureThreshold: 3, } c.TCPEndpoint = "example.com:80" c.TCPTimeout = 5 * time.Second return c }, }, { name: "missing name", cfg: func() *network.TCPProbeConfigV1Alpha1 { c := network.NewTCPProbeConfigV1Alpha1("") c.TCPEndpoint = "example.com:80" return c }, expectedError: "probe name is required", }, { name: "missing TCP endpoint", cfg: func() *network.TCPProbeConfigV1Alpha1 { c := network.NewTCPProbeConfigV1Alpha1("probe44") return c }, expectedError: "TCP probe endpoint is required", }, { name: "negative values", cfg: func() *network.TCPProbeConfigV1Alpha1 { c := network.NewTCPProbeConfigV1Alpha1("probe33") c.CommonProbeConfig.ProbeFailureThreshold = -1 c.CommonProbeConfig.ProbeInterval = -time.Second c.TCPTimeout = -5 * time.Second c.TCPEndpoint = "example.com:443" return c }, expectedError: "TCP probe timeout cannot be negative: -5s\nprobe interval cannot be negative: -1s\nprobe failure threshold cannot be negative: -1", }, { name: "empty", cfg: func() *network.TCPProbeConfigV1Alpha1 { return network.NewTCPProbeConfigV1Alpha1("") }, expectedError: "probe name is required\nTCP probe endpoint is required", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/testdata/blackholerouteconfig.yaml ================================================ apiVersion: v1alpha1 kind: BlackholeRouteConfig name: 169.254.1.1/32 metric: 2000 ================================================ FILE: pkg/machinery/config/types/network/testdata/bondconfig.yaml ================================================ apiVersion: v1alpha1 kind: BondConfig name: agg.0 links: - eth0 - eth1 bondMode: 802.3ad miimon: 100 updelay: 200 downdelay: 200 xmitHashPolicy: layer3+4 lacpRate: slow failOverMac: follow adActorSysPrio: 65535 resendIGMP: 1 packetsPerSlave: 1 up: true addresses: - address: 1.2.3.4/24 ================================================ FILE: pkg/machinery/config/types/network/testdata/bridgeconfig.yaml ================================================ apiVersion: v1alpha1 kind: BridgeConfig name: bridge.1 links: - eno1 - eno5 stp: enabled: true vlan: filtering: false up: true addresses: - address: 1.2.3.5/32 ================================================ FILE: pkg/machinery/config/types/network/testdata/defaultactionconfig.yaml ================================================ apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: block ================================================ FILE: pkg/machinery/config/types/network/testdata/dhcpv4config.yaml ================================================ apiVersion: v1alpha1 kind: DHCPv4Config name: enp0s3 routeMetric: 512 ignoreHostname: true clientIdentifier: duid duidRaw: 00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45 ================================================ FILE: pkg/machinery/config/types/network/testdata/dhcpv6config.yaml ================================================ apiVersion: v1alpha1 kind: DHCPv6Config name: enp0s3 clientIdentifier: mac ================================================ FILE: pkg/machinery/config/types/network/testdata/dummylinkconfig.yaml ================================================ apiVersion: v1alpha1 kind: DummyLinkConfig name: dummy1 hardwareAddr: 2e:3c:4d:5e:6f:70 up: true addresses: - address: 192.168.1.100/32 ================================================ FILE: pkg/machinery/config/types/network/testdata/ethernetconfig.yaml ================================================ apiVersion: v1alpha1 kind: EthernetConfig name: enp0s1 features: tx-checksum-ipv4: true rings: rx: 16 channels: combined: 1 wakeOnLan: - unicast - multicast ================================================ FILE: pkg/machinery/config/types/network/testdata/hcloudvipconfig.yaml ================================================ apiVersion: v1alpha1 kind: HCloudVIPConfig name: 1.2.3.4 link: net33 apiToken: s3cr3t-t0k3n ================================================ FILE: pkg/machinery/config/types/network/testdata/hostnameconfig.yaml ================================================ apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/network/testdata/kubespanconfig.yaml ================================================ apiVersion: v1alpha1 kind: KubeSpanConfig enabled: true advertiseKubernetesNetworks: false allowDownPeerBypass: false mtu: 1420 filters: excludeAdvertisedNetworks: - 2007::/64 ================================================ FILE: pkg/machinery/config/types/network/testdata/kubespanendpointsconfig.yaml ================================================ apiVersion: v1alpha1 kind: KubeSpanEndpointsConfig extraAnnouncedEndpoints: - 3.4.5.6:123 - 10.11.12.13:456 ================================================ FILE: pkg/machinery/config/types/network/testdata/layer2vipconfig.yaml ================================================ apiVersion: v1alpha1 kind: Layer2VIPConfig name: 1.2.3.4 link: net0 ================================================ FILE: pkg/machinery/config/types/network/testdata/linkaliasconfig.yaml ================================================ apiVersion: v1alpha1 kind: LinkAliasConfig name: net0 selector: match: mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e" ================================================ FILE: pkg/machinery/config/types/network/testdata/linkconfig.yaml ================================================ apiVersion: v1alpha1 kind: LinkConfig name: enp0s1 up: true mtu: 9000 addresses: - address: 192.168.1.100/24 - address: 2001:db8::1/64 routePriority: 100 routes: - destination: 10.3.5.0/24 gateway: 10.3.5.1 - gateway: fe80::1 multicast: true ================================================ FILE: pkg/machinery/config/types/network/testdata/resolverconfig.yaml ================================================ apiVersion: v1alpha1 kind: ResolverConfig nameservers: - address: 10.0.0.1 - address: 2001:4860:4860::8888 searchDomains: domains: - example.org - example.com disableDefault: false ================================================ FILE: pkg/machinery/config/types/network/testdata/routingruleconfig.yaml ================================================ apiVersion: v1alpha1 kind: RoutingRuleConfig name: "1000" src: 10.0.0.0/8 table: "100" action: unicast ================================================ FILE: pkg/machinery/config/types/network/testdata/ruleconfig.yaml ================================================ apiVersion: v1alpha1 kind: NetworkRuleConfig name: test portSelector: ports: - 53 - 8000-9000 protocol: udp ingress: - subnet: 192.168.0.0/16 except: 192.168.0.3/32 - subnet: 2001::/16 ================================================ FILE: pkg/machinery/config/types/network/testdata/statichostconfig.yaml ================================================ apiVersion: v1alpha1 kind: StaticHostConfig name: 10.5.0.2 hostnames: - example.org - example.com ================================================ FILE: pkg/machinery/config/types/network/testdata/tcpprobeconfig.yaml ================================================ apiVersion: v1alpha1 kind: TCPProbeConfig name: proxy-check interval: 1s failureThreshold: 3 endpoint: proxy.example.com:3128 timeout: 10s ================================================ FILE: pkg/machinery/config/types/network/testdata/timesyncconfig.yaml ================================================ apiVersion: v1alpha1 kind: TimeSyncConfig enabled: true bootTimeout: 1m0s ntp: servers: - time.cloudflare.com ================================================ FILE: pkg/machinery/config/types/network/testdata/vlanconfig.yaml ================================================ apiVersion: v1alpha1 kind: VLANConfig name: enp0s3.2 vlanID: 2 vlanMode: 802.1q parent: enp0s3 up: true addresses: - address: 192.168.1.100/32 ================================================ FILE: pkg/machinery/config/types/network/testdata/vrfconfig.yaml ================================================ apiVersion: v1alpha1 kind: VRFConfig name: vrf-blue links: - eno1 - eno5 table: "123" up: true addresses: - address: 1.2.3.5/32 ================================================ FILE: pkg/machinery/config/types/network/testdata/wireguardconfig.yaml ================================================ apiVersion: v1alpha1 kind: WireguardConfig name: wg.int privateKey: GA1E1VB+g41Dl0+UH2TMW9C5953y+moVg6JIIqkJbmw= listenPort: 5042 firewallMark: 176 peers: - publicKey: 735jkJdcVDninU5PzLJ/S+bfN6Q3QOk6svWrVLMJQAk= allowedIPs: - 192.168.1.0/24 - publicKey: uvdlJNva1X8/OCOZM+0gGT4Yu9x20odd3AWbbQUF7nM= presharedKey: 6j4UMxwszrHVZZUjY8/SFsZMjgaHkxV7yp9Tz05btho= endpoint: 10.3.4.3:2222 up: true addresses: - address: 192.168.1.100/32 ================================================ FILE: pkg/machinery/config/types/network/testdata/wireguardconfig_redacted.yaml ================================================ apiVersion: v1alpha1 kind: WireguardConfig name: wg.int privateKey: REDACTED listenPort: 5042 firewallMark: 176 peers: - publicKey: 735jkJdcVDninU5PzLJ/S+bfN6Q3QOk6svWrVLMJQAk= allowedIPs: - 192.168.1.0/24 - publicKey: uvdlJNva1X8/OCOZM+0gGT4Yu9x20odd3AWbbQUF7nM= presharedKey: REDACTED endpoint: 10.3.4.3:2222 up: true addresses: - address: 192.168.1.100/32 ================================================ FILE: pkg/machinery/config/types/network/time_sync.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "time" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // TimeSyncKind is a TimeSyncConfig document kind. const TimeSyncKind = "TimeSyncConfig" func init() { registry.Register(TimeSyncKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &TimeSyncConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkTimeSyncConfig = &TimeSyncConfigV1Alpha1{} _ config.Validator = &TimeSyncConfigV1Alpha1{} _ container.V1Alpha1ConflictValidator = &TimeSyncConfigV1Alpha1{} ) // TimeSyncConfigV1Alpha1 is a config document to configure time synchronization (NTP). // // examples: // - value: exampleTimeSyncConfigV1Alpha1() // - value: exampleTimeSyncConfigV1Alpha2() // alias: TimeSyncConfig // schemaRoot: true // schemaMeta: v1alpha1/TimeSyncConfig type TimeSyncConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Indicates if the time synchronization is enabled for the machine. // Defaults to `true`. TimeEnabled *bool `yaml:"enabled,omitempty"` // description: | // Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence. // NTP sync will be still running in the background. // Defaults to "infinity" (waiting forever for time sync) // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ TimeBootTimeout time.Duration `yaml:"bootTimeout,omitempty"` // description: | // Specifies NTP configuration to sync the time over network. // Mutually exclusive with PTP configuration. TimeNTP *NTPConfig `yaml:"ntp,omitempty"` // description: | // Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices. // Mutually exclusive with NTP configuration. TimePTP *PTPConfig `yaml:"ptp,omitempty"` } // NTPConfig represents a NTP server configuration. type NTPConfig struct { // description: | // Specifies time (NTP) servers to use for setting the system time. // Defaults to `time.cloudflare.com`. Servers []string `yaml:"servers,omitempty"` } // PTPConfig represents a PTP (Precision Time Protocol) configuration. type PTPConfig struct { // description: | // A list of PTP devices to sync with (e.g. provided by the hypervisor). // // A PTP device is typically represented as a character device file in the /dev directory, // such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time // with an external time source that supports the Precision Time Protocol. Devices []string `yaml:"devices,omitempty"` } // NewTimeSyncConfigV1Alpha1 creates a new TimeSyncConfig config document. func NewTimeSyncConfigV1Alpha1() *TimeSyncConfigV1Alpha1 { return &TimeSyncConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: TimeSyncKind, MetaAPIVersion: "v1alpha1", }, } } func exampleTimeSyncConfigV1Alpha1() *TimeSyncConfigV1Alpha1 { cfg := NewTimeSyncConfigV1Alpha1() cfg.TimeNTP = &NTPConfig{ Servers: []string{"pool.ntp.org"}, } return cfg } func exampleTimeSyncConfigV1Alpha2() *TimeSyncConfigV1Alpha1 { cfg := NewTimeSyncConfigV1Alpha1() cfg.TimePTP = &PTPConfig{ Devices: []string{"/dev/ptp0"}, } return cfg } // Clone implements config.Document interface. func (s *TimeSyncConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. func (s *TimeSyncConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var errs error if s.TimeBootTimeout < 0 { errs = errors.Join(errs, errors.New("bootTimeout cannot be negative")) } if s.TimeNTP != nil && s.TimePTP != nil { errs = errors.Join(errs, errors.New("only one of ntp or ptp configuration can be specified")) } return nil, errs } // V1Alpha1ConflictValidate implements container.V1Alpha1ConflictValidator interface. func (s *TimeSyncConfigV1Alpha1) V1Alpha1ConflictValidate(v1alpha1Cfg *v1alpha1.Config) error { v1tsc := v1alpha1Cfg.NetworkTimeSyncConfig() if v1tsc == nil { return nil } if v1tsc.Disabled() { return errors.New("time sync cannot be disabled in both v1alpha1 and new-style configuration") } if len(v1tsc.Servers()) > 0 { return errors.New("time servers cannot be specified in both v1alpha1 and new-style configuration") } if v1tsc.BootTimeout() != 0 { return errors.New("boot timeout cannot be specified in both v1alpha1 and new-style configuration") } return nil } // Disabled implements config.NetworkTimeSyncConfig interface. func (s *TimeSyncConfigV1Alpha1) Disabled() bool { if s.TimeEnabled == nil { return false } return !*s.TimeEnabled } // BootTimeout implements config.NetworkTimeSyncConfig interface. func (s *TimeSyncConfigV1Alpha1) BootTimeout() time.Duration { return s.TimeBootTimeout } // Servers implements config.NetworkTimeSyncConfig interface. func (s *TimeSyncConfigV1Alpha1) Servers() []string { // The configuration validates that only one of the NTP or PTP is set. if s.TimeNTP != nil { return s.TimeNTP.Servers } if s.TimePTP != nil { return s.TimePTP.Devices } return nil } ================================================ FILE: pkg/machinery/config/types/network/time_sync_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) //go:embed testdata/timesyncconfig.yaml var expectedTimeSyncConfigDocument []byte func TestTimeSyncConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewTimeSyncConfigV1Alpha1() cfg.TimeEnabled = new(true) cfg.TimeBootTimeout = time.Minute cfg.TimeNTP = &network.NTPConfig{ Servers: []string{"time.cloudflare.com"}, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedTimeSyncConfigDocument, marshaled) } func TestTimeSyncConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedTimeSyncConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.TimeSyncConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.TimeSyncKind, }, TimeEnabled: new(true), TimeBootTimeout: time.Minute, TimeNTP: &network.NTPConfig{ Servers: []string{"time.cloudflare.com"}, }, }, docs[0]) } func TestTimeSyncValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.TimeSyncConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: network.NewTimeSyncConfigV1Alpha1, }, { name: "both NTP and PTP set", cfg: func() *network.TimeSyncConfigV1Alpha1 { cfg := network.NewTimeSyncConfigV1Alpha1() cfg.TimeNTP = &network.NTPConfig{ Servers: []string{"pool.ntp.org"}, } cfg.TimePTP = &network.PTPConfig{ Devices: []string{"/dev/ptp0"}, } return cfg }, expectedError: "only one of ntp or ptp configuration can be specified", }, { name: "negative boot timeout", cfg: func() *network.TimeSyncConfigV1Alpha1 { cfg := network.NewTimeSyncConfigV1Alpha1() cfg.TimeBootTimeout = -time.Second return cfg }, expectedError: "bootTimeout cannot be negative", }, { name: "valid NTP config", cfg: func() *network.TimeSyncConfigV1Alpha1 { cfg := network.NewTimeSyncConfigV1Alpha1() cfg.TimeNTP = &network.NTPConfig{ Servers: []string{"pool.ntp.org"}, } cfg.TimeBootTimeout = time.Second return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } func TestTimeSyncV1Alpha1Validate(t *testing.T) { t.Parallel() for _, test := range []struct { name string v1alpha1Cfg *v1alpha1.Config cfg func() *network.TimeSyncConfigV1Alpha1 expectedError string }{ { name: "empty", v1alpha1Cfg: &v1alpha1.Config{}, cfg: network.NewTimeSyncConfigV1Alpha1, }, { name: "v1alpha1 timeservers set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeServers: []string{"za.pool.ntp.org"}, }, }, }, cfg: network.NewTimeSyncConfigV1Alpha1, expectedError: "time servers cannot be specified in both v1alpha1 and new-style configuration", }, { name: "v1alpha1 boot timeout set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeBootTimeout: time.Second, }, }, }, cfg: network.NewTimeSyncConfigV1Alpha1, expectedError: "boot timeout cannot be specified in both v1alpha1 and new-style configuration", }, { name: "v1alpha1 disable set", v1alpha1Cfg: &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeDisabled: new(true), }, }, }, cfg: network.NewTimeSyncConfigV1Alpha1, expectedError: "time sync cannot be disabled in both v1alpha1 and new-style configuration", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() err := test.cfg().V1Alpha1ConflictValidate(test.v1alpha1Cfg) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/vlan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "net/netip" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // VLANKind is a VLAN config document kind. const VLANKind = "VLANConfig" func init() { registry.Register(VLANKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &VLANConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkVLANConfig = &VLANConfigV1Alpha1{} _ config.ConflictingDocument = &VLANConfigV1Alpha1{} _ config.NamedDocument = &VLANConfigV1Alpha1{} _ config.Validator = &VLANConfigV1Alpha1{} ) // VLANConfigV1Alpha1 is a config document to create a VLAN (virtual LAN) over a parent link. // // examples: // - value: exampleVLANConfigV1Alpha1() // alias: VLANConfig // schemaRoot: true // schemaMeta: v1alpha1/VLANConfig type VLANConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the VLAN link (interface) to be created. // // examples: // - value: > // "enp0s3.34" // schemaRequired: true MetaName string `yaml:"name"` // description: | // VLAN ID to be used for the VLAN link. // // examples: // - value: > // 34 // schemaRequired: true VLANIDConfig uint16 `yaml:"vlanID,omitempty"` // description: | // Set the VLAN mode to use. // If not set, defaults to '802.1q'. // // examples: // - value: > // "802.1q" // values: // - "802.1q" // - "802.1ad" VLANModeConfig *nethelpers.VLANProtocol `yaml:"vlanMode,omitempty"` // description: | // Name of the parent link (interface) on which the VLAN link will be created. // Link aliases can be used here as well. // // examples: // - value: > // "enp0s3" // schemaRequired: true ParentLinkConfig string `yaml:"parent,omitempty"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // NewVLANConfigV1Alpha1 creates a new VLANConfig config document. func NewVLANConfigV1Alpha1(name string) *VLANConfigV1Alpha1 { return &VLANConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: VLANKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleVLANConfigV1Alpha1() *VLANConfigV1Alpha1 { cfg := NewVLANConfigV1Alpha1("enp0s3.34") cfg.VLANIDConfig = 34 cfg.ParentLinkConfig = "enp0s3" cfg.LinkAddresses = []AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, } cfg.LinkRoutes = []RouteConfig{ { RouteDestination: Prefix{netip.MustParsePrefix("192.168.0.0/16")}, RouteGateway: Addr{netip.MustParseAddr("192.168.1.1")}, }, } return cfg } // Clone implements config.Document interface. func (s *VLANConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *VLANConfigV1Alpha1) Name() string { return s.MetaName } // VLANConfig implements NetworkVLANConfig interface. func (s *VLANConfigV1Alpha1) VLANConfig() {} // VLANID returns the VLAN ID. func (s *VLANConfigV1Alpha1) VLANID() uint16 { return s.VLANIDConfig } // ParentLink returns the parent link name. func (s *VLANConfigV1Alpha1) ParentLink() string { return s.ParentLinkConfig } // VLANMode returns the VLAN mode. func (s *VLANConfigV1Alpha1) VLANMode() optional.Optional[nethelpers.VLANProtocol] { if s.VLANModeConfig == nil { return optional.None[nethelpers.VLANProtocol]() } return optional.Some(*s.VLANModeConfig) } // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *VLANConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(VLANKind) } // Validate implements config.Validator interface. func (s *VLANConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if s.VLANIDConfig == 0 || s.VLANIDConfig > 4094 { errs = errors.Join(errs, errors.New("vlanID must be specified and between 1 and 4094")) } if s.ParentLinkConfig == "" { errs = errors.Join(errs, errors.New("parent must be specified")) } switch { case s.VLANModeConfig == nil: // default is 802.1q case *s.VLANModeConfig != nethelpers.VLANProtocol8021Q && *s.VLANModeConfig != nethelpers.VLANProtocol8021AD: errs = errors.Join(errs, errors.New("vlanMode must be either '802.1q' or '802.1ad'")) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } ================================================ FILE: pkg/machinery/config/types/network/vlan_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/vlanconfig.yaml var expectedVLANConfigDocument []byte func TestVLANConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewVLANConfigV1Alpha1("enp0s3.2") cfg.VLANIDConfig = 2 cfg.ParentLinkConfig = "enp0s3" cfg.VLANModeConfig = new(nethelpers.VLANProtocol8021Q) cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedVLANConfigDocument, marshaled) } func TestVLANConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedVLANConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.VLANConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.VLANKind, }, MetaName: "enp0s3.2", VLANIDConfig: 2, ParentLinkConfig: "enp0s3", VLANModeConfig: new(nethelpers.VLANProtocol8021Q), CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, }, }, }, docs[0]) } func TestVLANValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.VLANConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.VLANConfigV1Alpha1 { return network.NewVLANConfigV1Alpha1("") }, expectedError: "name must be specified\nvlanID must be specified and between 1 and 4094\nparent must be specified", }, { name: "invalid addresses", cfg: func() *network.VLANConfigV1Alpha1 { cfg := network.NewVLANConfigV1Alpha1("enx8.2") cfg.VLANIDConfig = 2 cfg.ParentLinkConfig = "enx8" cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.Prefix{}, }, { AddressAddress: netip.MustParsePrefix("0.0.0.0/0"), }, } return cfg }, expectedError: "address 0 must be specified\naddress 1 must be a valid IP address", }, { name: "no VLAN ID", cfg: func() *network.VLANConfigV1Alpha1 { cfg := network.NewVLANConfigV1Alpha1("enx8.2") cfg.ParentLinkConfig = "enx9" return cfg }, expectedError: "vlanID must be specified and between 1 and 4094", }, { name: "high VLAN ID", cfg: func() *network.VLANConfigV1Alpha1 { cfg := network.NewVLANConfigV1Alpha1("enx8.2") cfg.ParentLinkConfig = "enx9" cfg.VLANIDConfig = 5000 return cfg }, expectedError: "vlanID must be specified and between 1 and 4094", }, { name: "valid", cfg: func() *network.VLANConfigV1Alpha1 { cfg := network.NewVLANConfigV1Alpha1("enx8.2") cfg.VLANIDConfig = 2 cfg.ParentLinkConfig = "enx8" cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/vrf.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "errors" "fmt" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // VRFKind is a VRF config document kind. const VRFKind = "VRFConfig" func init() { registry.Register(VRFKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &VRFConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkVRFConfig = &VRFConfigV1Alpha1{} _ config.ConflictingDocument = &VRFConfigV1Alpha1{} _ config.NamedDocument = &VRFConfigV1Alpha1{} _ config.Validator = &VRFConfigV1Alpha1{} ) // VRFConfigV1Alpha1 is a config document to create a vrf and assign links to it. // // examples: // - value: exampleVRFConfigV1Alpha1() // alias: VRFConfig // schemaRoot: true // schemaMeta: v1alpha1/VRFConfig type VRFConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the vrf link (interface) to be created. // // examples: // - value: > // "vrf-blue" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Override the hardware (MAC) address of the link. // // examples: // - value: > // nethelpers.HardwareAddr{0x2e, 0x3c, 0x4d, 0x5e, 0x6f, 0x70} // schema: // type: string // pattern: ^[0-9a-f:]+$ HardwareAddressConfig nethelpers.HardwareAddr `yaml:"hardwareAddr,omitempty"` // description: | // Names of the links (interfaces) to be assigned to this vrf. // Link aliases can be used here as well. // examples: // - value: > // []string{"enp1s3", "enp1s2"} // schemaRequired: true VRFLinks []string `yaml:"links,omitempty"` // description: | // Routing table number to use for this vrf. // examples: // - value: > // 10 // schemaRequired: true // schema: // type: string VRFTable nethelpers.RoutingTable `yaml:"table"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // NewVRFConfigV1Alpha1 creates a new VRFConfig config document. func NewVRFConfigV1Alpha1(name string) *VRFConfigV1Alpha1 { return &VRFConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: VRFKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleVRFConfigV1Alpha1() *VRFConfigV1Alpha1 { cfg := NewVRFConfigV1Alpha1("vrf-blue") cfg.VRFTable = 10 cfg.VRFLinks = []string{"eno1", "eno2"} return cfg } // Clone implements config.Document interface. func (s *VRFConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *VRFConfigV1Alpha1) Name() string { return s.MetaName } // VRFConfig implements NetworkVRFConfig interface. func (s *VRFConfigV1Alpha1) VRFConfig() {} // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *VRFConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(VRFKind) } // Validate implements config.Validator interface. func (s *VRFConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if s.VRFTable == nethelpers.TableUnspec || s.VRFTable == nethelpers.TableLocal || s.VRFTable == nethelpers.TableMain || s.VRFTable == nethelpers.TableDefault { errs = errors.Join(errs, fmt.Errorf("cannot create vrf with reserved table %s", s.VRFTable)) } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } // Links implements NetworkVRFConfig interface. func (s *VRFConfigV1Alpha1) Links() []string { return s.VRFLinks } // Table implements NetworkVRFConfig interface. func (s *VRFConfigV1Alpha1) Table() nethelpers.RoutingTable { return s.VRFTable } // HardwareAddress implements NetworkHardwareAddressConfig interface. func (s *VRFConfigV1Alpha1) HardwareAddress() optional.Optional[nethelpers.HardwareAddr] { if len(s.HardwareAddressConfig) == 0 { return optional.None[nethelpers.HardwareAddr]() } return optional.Some(s.HardwareAddressConfig) } ================================================ FILE: pkg/machinery/config/types/network/vrf_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed testdata/vrfconfig.yaml var expectedVRFConfigDocument []byte func TestVRFConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewVRFConfigV1Alpha1("vrf-blue") cfg.VRFLinks = []string{"eno1", "eno5"} cfg.VRFTable = 123 cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.5/32"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedVRFConfigDocument, marshaled) } func TestVRFConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedVRFConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.VRFConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.VRFKind, }, MetaName: "vrf-blue", VRFLinks: []string{"eno1", "eno5"}, VRFTable: 123, CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("1.2.3.5/32"), }, }, }, }, docs[0]) } func TestVRFValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.VRFConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.VRFConfigV1Alpha1 { return network.NewVRFConfigV1Alpha1("") }, expectedError: "name must be specified\ncannot create vrf with reserved table unspec", }, { name: "reject reserved table id 0", cfg: func() *network.VRFConfigV1Alpha1 { cfg := network.NewVRFConfigV1Alpha1("vrf-blue") return cfg }, expectedError: "cannot create vrf with reserved table unspec", }, { name: "reject reserved table id 253", cfg: func() *network.VRFConfigV1Alpha1 { cfg := network.NewVRFConfigV1Alpha1("vrf-blue") cfg.VRFTable = nethelpers.TableDefault return cfg }, expectedError: "cannot create vrf with reserved table default", }, { name: "reject reserved table id 254", cfg: func() *network.VRFConfigV1Alpha1 { cfg := network.NewVRFConfigV1Alpha1("vrf-blue") cfg.VRFTable = nethelpers.TableMain return cfg }, expectedError: "cannot create vrf with reserved table main", }, { name: "reject reserved table id 255", cfg: func() *network.VRFConfigV1Alpha1 { cfg := network.NewVRFConfigV1Alpha1("vrf-blue") cfg.VRFTable = nethelpers.TableLocal return cfg }, expectedError: "cannot create vrf with reserved table local", }, { name: "valid", cfg: func() *network.VRFConfigV1Alpha1 { cfg := network.NewVRFConfigV1Alpha1("vrf-red") cfg.VRFLinks = []string{"eth0", "eth1"} cfg.VRFTable = nethelpers.RoutingTable(123) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, { AddressAddress: netip.MustParsePrefix("fd00::1/64"), }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/wireguard.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network //docgen:jsonschema import ( "encoding/base64" "errors" "fmt" "net/netip" "time" "github.com/siderolabs/gen/optional" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // WireguardKind is a Wireguard config document kind. const WireguardKind = "WireguardConfig" func init() { registry.Register(WireguardKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &WireguardConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.NetworkWireguardConfig = &WireguardConfigV1Alpha1{} _ config.ConflictingDocument = &WireguardConfigV1Alpha1{} _ config.NamedDocument = &WireguardConfigV1Alpha1{} _ config.Validator = &WireguardConfigV1Alpha1{} _ config.SecretDocument = &WireguardConfigV1Alpha1{} ) // WireguardConfigV1Alpha1 is a config document to create and configure a Wireguard network link. // // examples: // - value: exampleWireguardConfigV1Alpha1() // alias: WireguardConfig // schemaRoot: true // schemaMeta: v1alpha1/WireguardConfig type WireguardConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the Wireguard link (interface). // // examples: // - value: > // "wg.int" // schemaRequired: true MetaName string `yaml:"name"` // description: | // Specifies a private key configuration (base64 encoded). // Can be generated by `wg genkey`. // schemaRequired: true WireguardPrivateKey string `yaml:"privateKey,omitempty"` // description: | // Specifies a device's listening port (UDP). // If not specified, a random port will be chosen. WireguardListenPort int `yaml:"listenPort,omitempty"` // description: | // Specifies a device's firewall mark. // Useful for advanced routing setups, marking packets originating from this device. WireguardFirewallMark int `yaml:"firewallMark,omitempty"` // description: Specifies a list of peer configurations to apply to a device. WireguardPeers []WireguardPeer `yaml:"peers,omitempty"` //nolint:embeddedstructfieldcheck CommonLinkConfig `yaml:",inline"` } // WireguardPeer describes a Wireguard peer configuration. type WireguardPeer struct { // description: | // Specifies the public key of this peer. // Can be extracted from private key by running `wg pubkey < private.key`. // schemaRequired: true WireguardPublicKey string `yaml:"publicKey,omitempty"` // description: | // Specifies the preshared key for this peer (base64 encoded). // Can be generated by `wg genpsk`. // Optional, this key provides an additional layer of symmetric-key cryptography // to the peer connection. WireguardPresharedKey string `yaml:"presharedKey,omitempty"` // description: | // Specifies the endpoint of this peer entry. // Format: :. // If not set, the peer should connect to us without us connecting to it first. // schema: // type: string // pattern: ^([0-9a-f.:]+|\[[0-9a-f:.]+\]):\d{1,5}$ WireguardEndpoint AddrPort `yaml:"endpoint,omitempty"` // description: | // Specifies the persistent keepalive interval for this peer. // Field format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes). // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ WireguardPersistentKeepaliveInterval time.Duration `yaml:"persistentKeepaliveInterval,omitempty"` // description: | // AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. // These IPs will be routed to this peer, and defines which IPs this peer is allowed to use. // schema: // type: array // items: // type: string // pattern: ^[0-9a-f.:]+/\d{1,3}$ WireguardAllowedIPs []Prefix `yaml:"allowedIPs,omitempty"` } // NewWireguardConfigV1Alpha1 creates a new WireguardConfig config document. func NewWireguardConfigV1Alpha1(name string) *WireguardConfigV1Alpha1 { return &WireguardConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: WireguardKind, MetaAPIVersion: "v1alpha1", }, MetaName: name, } } func exampleWireguardConfigV1Alpha1() *WireguardConfigV1Alpha1 { cfg := NewWireguardConfigV1Alpha1("wg1") cfg.WireguardPrivateKey = "OJ34O6J1z4ZZB+t16c+vYrzIrKddxyU3Z2eLhwYzqE8=" cfg.WireguardListenPort = 51820 cfg.WireguardPeers = []WireguardPeer{ { WireguardPublicKey: "fP+xJZvUA5n1Pi/f5wcPiV6tZ6fHwqcGaXe98NfEgkE=", WireguardAllowedIPs: []Prefix{{netip.MustParsePrefix("192.168.2.0/24")}}, WireguardEndpoint: AddrPort{netip.MustParseAddrPort("10.0.0.1:5180")}, }, { WireguardPublicKey: "TDd25Cwq6tMZANIKUaqred+Zt+09HtCqwFeOLtKQ9Cs=", WireguardPresharedKey: "UpH8htYK7yJBPg5+q4M/Tx0o5ipHbeSZtI/h/mHxOeU=", WireguardAllowedIPs: []Prefix{{netip.MustParsePrefix("192.168.3.0/24")}}, }, } cfg.LinkAddresses = []AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/24"), }, } cfg.LinkMTU = 1420 return cfg } // Clone implements config.Document interface. func (s *WireguardConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *WireguardConfigV1Alpha1) Name() string { return s.MetaName } // WireguardConfig implements NetworkWireguardConfig interface. func (s *WireguardConfigV1Alpha1) WireguardConfig() {} // ConflictsWithKinds implements config.ConflictingDocument interface. func (s *WireguardConfigV1Alpha1) ConflictsWithKinds() []string { return conflictingLinkKinds(WireguardKind) } func validateWireguardKey(key string) error { // this is hand-rolled to avoid importing wgtypes into machinery raw, err := base64.StdEncoding.DecodeString(key) if err != nil { return err } if len(raw) != 32 { return errors.New("invalid wireguard key length") } return nil } // Validate implements config.Validator interface. // //nolint:gocyclo func (s *WireguardConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string //nolint:prealloc ) if s.MetaName == "" { errs = errors.Join(errs, errors.New("name must be specified")) } if s.WireguardPrivateKey == "" { errs = errors.Join(errs, errors.New("wireguard private key must be specified")) } else if err := validateWireguardKey(s.WireguardPrivateKey); err != nil { errs = errors.Join(errs, errors.New("wireguard private key is invalid: "+err.Error())) } if s.WireguardListenPort < 0 || s.WireguardListenPort > 65535 { errs = errors.Join(errs, errors.New("wireguard listen port must be between 0 and 65535")) } for i, peer := range s.WireguardPeers { if peer.WireguardPublicKey == "" { errs = errors.Join(errs, fmt.Errorf("wireguard peer public key must be specified (peer index %d)", i)) } else if err := validateWireguardKey(peer.WireguardPublicKey); err != nil { errs = errors.Join(errs, fmt.Errorf("wireguard peer public key is invalid (peer index %d): %w", i, err)) } if peer.WireguardPresharedKey != "" { if err := validateWireguardKey(peer.WireguardPresharedKey); err != nil { errs = errors.Join(errs, fmt.Errorf("wireguard peer preshared key is invalid (peer index %d): %w", i, err)) } } if peer.WireguardPersistentKeepaliveInterval < 0 { errs = errors.Join(errs, fmt.Errorf("wireguard peer persistent keepalive interval cannot be negative (peer index %d)", i)) } } extraWarnings, extraErrs := s.CommonLinkConfig.Validate() errs, warnings = errors.Join(errs, extraErrs), append(warnings, extraWarnings...) return warnings, errs } // PrivateKey implements NetworkWireguardConfig interface. func (s *WireguardConfigV1Alpha1) PrivateKey() string { return s.WireguardPrivateKey } // ListenPort implements NetworkWireguardConfig interface. func (s *WireguardConfigV1Alpha1) ListenPort() optional.Optional[int] { if s.WireguardListenPort == 0 { return optional.None[int]() } return optional.Some(s.WireguardListenPort) } // FirewallMark implements NetworkWireguardConfig interface. func (s *WireguardConfigV1Alpha1) FirewallMark() optional.Optional[int] { if s.WireguardFirewallMark == 0 { return optional.None[int]() } return optional.Some(s.WireguardFirewallMark) } // Peers implements NetworkWireguardConfig interface. func (s *WireguardConfigV1Alpha1) Peers() []config.NetworkWireguardPeerConfig { return xslices.Map(s.WireguardPeers, func(peer WireguardPeer) config.NetworkWireguardPeerConfig { return peer }) } // PublicKey implements NetworkWireguardPeerConfig interface. func (p WireguardPeer) PublicKey() string { return p.WireguardPublicKey } // PresharedKey implements NetworkWireguardPeerConfig interface. func (p WireguardPeer) PresharedKey() optional.Optional[string] { if p.WireguardPresharedKey == "" { return optional.None[string]() } return optional.Some(p.WireguardPresharedKey) } // Endpoint implements NetworkWireguardPeerConfig interface. func (p WireguardPeer) Endpoint() optional.Optional[string] { if p.WireguardEndpoint.IsZero() { return optional.None[string]() } return optional.Some(p.WireguardEndpoint.String()) } // AllowedIPs implements NetworkWireguardPeerConfig interface. func (p WireguardPeer) AllowedIPs() []netip.Prefix { return xslices.Map(p.WireguardAllowedIPs, func(pr Prefix) netip.Prefix { return pr.Prefix }) } // PersistentKeepalive implements NetworkWireguardPeerConfig interface. func (p WireguardPeer) PersistentKeepalive() optional.Optional[time.Duration] { if p.WireguardPersistentKeepaliveInterval == 0 { return optional.None[time.Duration]() } return optional.Some(p.WireguardPersistentKeepaliveInterval) } // Redact does in-place replacement of secrets with the given string. func (s *WireguardConfigV1Alpha1) Redact(replacement string) { if s.WireguardPrivateKey != "" { s.WireguardPrivateKey = replacement } for i := range s.WireguardPeers { s.WireguardPeers[i].Redact(replacement) } } // Redact does in-place replacement of secrets with the given string. func (p *WireguardPeer) Redact(replacement string) { if p.WireguardPresharedKey != "" { p.WireguardPresharedKey = replacement } } ================================================ FILE: pkg/machinery/config/types/network/wireguard_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( _ "embed" "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/network" ) //go:embed testdata/wireguardconfig.yaml var expectedWireguardConfigDocument []byte //go:embed testdata/wireguardconfig_redacted.yaml var expectedWireguardConfigRedactedDocument []byte func TestWireguardConfigMarshalStability(t *testing.T) { t.Parallel() cfg := network.NewWireguardConfigV1Alpha1("wg.int") cfg.WireguardPrivateKey = "GA1E1VB+g41Dl0+UH2TMW9C5953y+moVg6JIIqkJbmw=" cfg.WireguardListenPort = 5042 cfg.WireguardFirewallMark = 0xb0 cfg.WireguardPeers = []network.WireguardPeer{ { WireguardPublicKey: "735jkJdcVDninU5PzLJ/S+bfN6Q3QOk6svWrVLMJQAk=", WireguardAllowedIPs: []network.Prefix{{netip.MustParsePrefix("192.168.1.0/24")}}, }, { WireguardPublicKey: "uvdlJNva1X8/OCOZM+0gGT4Yu9x20odd3AWbbQUF7nM=", WireguardPresharedKey: "6j4UMxwszrHVZZUjY8/SFsZMjgaHkxV7yp9Tz05btho=", WireguardEndpoint: network.AddrPort{netip.MustParseAddrPort("10.3.4.3:2222")}, }, } cfg.LinkUp = new(true) cfg.LinkAddresses = []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedWireguardConfigDocument, marshaled) } func TestWireguardConfigRedact(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedWireguardConfigDocument) require.NoError(t, err) redactedProvider := provider.RedactSecrets("REDACTED") redacted, err := redactedProvider.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, err) t.Log(string(redacted)) assert.Equal(t, expectedWireguardConfigRedactedDocument, redacted) } func TestWireguardConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedWireguardConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &network.WireguardConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: network.WireguardKind, }, MetaName: "wg.int", WireguardPrivateKey: "GA1E1VB+g41Dl0+UH2TMW9C5953y+moVg6JIIqkJbmw=", WireguardListenPort: 5042, WireguardFirewallMark: 0xb0, WireguardPeers: []network.WireguardPeer{ { WireguardPublicKey: "735jkJdcVDninU5PzLJ/S+bfN6Q3QOk6svWrVLMJQAk=", WireguardAllowedIPs: []network.Prefix{{netip.MustParsePrefix("192.168.1.0/24")}}, }, { WireguardPublicKey: "uvdlJNva1X8/OCOZM+0gGT4Yu9x20odd3AWbbQUF7nM=", WireguardPresharedKey: "6j4UMxwszrHVZZUjY8/SFsZMjgaHkxV7yp9Tz05btho=", WireguardEndpoint: network.AddrPort{netip.MustParseAddrPort("10.3.4.3:2222")}, }, }, CommonLinkConfig: network.CommonLinkConfig{ LinkUp: new(true), LinkAddresses: []network.AddressConfig{ { AddressAddress: netip.MustParsePrefix("192.168.1.100/32"), }, }, }, }, docs[0]) } func TestWireguardValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *network.WireguardConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: func() *network.WireguardConfigV1Alpha1 { return network.NewWireguardConfigV1Alpha1("") }, expectedError: "name must be specified\nwireguard private key must be specified", }, { name: "invalid private key", cfg: func() *network.WireguardConfigV1Alpha1 { cfg := network.NewWireguardConfigV1Alpha1("wg.invalid") cfg.WireguardPrivateKey = "invalid-key" return cfg }, expectedError: "wireguard private key is invalid: illegal base64 data at input byte 7", }, { name: "invalid peer public key", cfg: func() *network.WireguardConfigV1Alpha1 { cfg := network.NewWireguardConfigV1Alpha1("wg.invalidpeer") cfg.WireguardPrivateKey = "0CWDz05gWWwVAwr4i5Zaz41k/FgIVYJmCsgAfLHUakU=" cfg.WireguardPeers = []network.WireguardPeer{ { WireguardPublicKey: "invalid-peer-key", }, } return cfg }, expectedError: "wireguard peer public key is invalid (peer index 0): illegal base64 data at input byte 7", }, { name: "invalid peer preshared key", cfg: func() *network.WireguardConfigV1Alpha1 { cfg := network.NewWireguardConfigV1Alpha1("wg.invalidpreshared") cfg.WireguardPrivateKey = "OJ34O6J1z4ZZB+t16c+vYrzIrKddxyU3Z2eLhwYzqE8=" cfg.WireguardPeers = []network.WireguardPeer{ { WireguardPublicKey: "fP+xJZvUA5n1Pi/f5wcPiV6tZ6fHwqcGaXe98NfEgkE=", WireguardPresharedKey: "invalid-preshared-key", }, } return cfg }, expectedError: "wireguard peer preshared key is invalid (peer index 0): illegal base64 data at input byte 7", }, { name: "valid", cfg: func() *network.WireguardConfigV1Alpha1 { cfg := network.NewWireguardConfigV1Alpha1("wg.35") cfg.WireguardPrivateKey = "OJ34O6J1z4ZZB+t16c+vYrzIrKddxyU3Z2eLhwYzqE8=" cfg.WireguardListenPort = 51820 cfg.WireguardFirewallMark = 0x1 cfg.WireguardPeers = []network.WireguardPeer{ { WireguardPublicKey: "735jkJdcVDninU5PzLJ/S+bfN6Q3QOk6svWrVLMJQAk=", WireguardAllowedIPs: []network.Prefix{{netip.MustParsePrefix("192.168.1.0/24")}}, }, { WireguardPublicKey: "uvdlJNva1X8/OCOZM+0gGT4Yu9x20odd3AWbbQUF7nM=", WireguardPresharedKey: "6j4UMxwszrHVZZUjY8/SFsZMjgaHkxV7yp9Tz05btho=", WireguardEndpoint: network.AddrPort{netip.MustParseAddrPort("10.3.4.3:2222")}, }, } cfg.LinkRoutes = []network.RouteConfig{ { RouteDestination: network.Prefix{netip.MustParsePrefix("10.3.5.0/24")}, RouteGateway: network.Addr{netip.MustParseAddr("10.3.5.1")}, }, { RouteGateway: network.Addr{netip.MustParseAddr("fe80::1")}, }, } return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/network/yaml.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import "net/netip" // Prefix is a wrapper for netip.Prefix. // // It implements IsZero() so that yaml.Marshal correctly skips empty values. // //docgen:nodoc type Prefix struct { netip.Prefix } // IsZero implements yaml.IsZeroer interface. func (n Prefix) IsZero() bool { return n.Prefix == netip.Prefix{} } // Addr is a wrapper for netip.Addr. // // It implements IsZero() so that yaml.Marshal correctly skips empty values. // //docgen:nodoc type Addr struct { netip.Addr } // IsZero implements yaml.IsZeroer interface. func (n Addr) IsZero() bool { return n.Addr == netip.Addr{} } // AddrPort is a wrapper for netip.AddrPort. // // It implements IsZero() so that yaml.Marshal correctly skips empty values. // //docgen:nodoc type AddrPort struct { netip.AddrPort } // IsZero implements yaml.IsZeroer interface. func (n AddrPort) IsZero() bool { return n.AddrPort == netip.AddrPort{} } ================================================ FILE: pkg/machinery/config/types/runtime/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type EventSinkV1Alpha1 -type EnvironmentV1Alpha1 -type KmsgLogV1Alpha1 -type OOMV1Alpha1 -type WatchdogTimerV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package runtime import ( "net/url" ) // DeepCopy generates a deep copy of *EventSinkV1Alpha1. func (o *EventSinkV1Alpha1) DeepCopy() *EventSinkV1Alpha1 { var cp EventSinkV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *EnvironmentV1Alpha1. func (o *EnvironmentV1Alpha1) DeepCopy() *EnvironmentV1Alpha1 { var cp EnvironmentV1Alpha1 = *o if o.EnvironmentVariables != nil { cp.EnvironmentVariables = make(map[string]string, len(o.EnvironmentVariables)) for k2, v2 := range o.EnvironmentVariables { cp.EnvironmentVariables[k2] = v2 } } return &cp } // DeepCopy generates a deep copy of *KmsgLogV1Alpha1. func (o *KmsgLogV1Alpha1) DeepCopy() *KmsgLogV1Alpha1 { var cp KmsgLogV1Alpha1 = *o if o.KmsgLogURL.URL != nil { cp.KmsgLogURL.URL = new(url.URL) *cp.KmsgLogURL.URL = *o.KmsgLogURL.URL if o.KmsgLogURL.URL.User != nil { cp.KmsgLogURL.URL.User = new(url.Userinfo) *cp.KmsgLogURL.URL.User = *o.KmsgLogURL.URL.User } } return &cp } // DeepCopy generates a deep copy of *OOMV1Alpha1. func (o *OOMV1Alpha1) DeepCopy() *OOMV1Alpha1 { var cp OOMV1Alpha1 = *o return &cp } // DeepCopy generates a deep copy of *WatchdogTimerV1Alpha1. func (o *WatchdogTimerV1Alpha1) DeepCopy() *WatchdogTimerV1Alpha1 { var cp WatchdogTimerV1Alpha1 = *o return &cp } ================================================ FILE: pkg/machinery/config/types/runtime/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //docgen:jsonschema import ( "errors" "fmt" "regexp" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // EnvironmentConfigKind is an environment config document kind. const EnvironmentConfigKind = "EnvironmentConfig" func init() { registry.Register(EnvironmentConfigKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &EnvironmentV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.EnvironmentConfig = &EnvironmentV1Alpha1{} _ config.Validator = &EnvironmentV1Alpha1{} ) // EnvironmentV1Alpha1 is an environment config document. // // examples: // - value: exampleEnvironmentV1Alpha1() // alias: EnvironmentConfig // schemaRoot: true // schemaMeta: v1alpha1/EnvironmentConfig type EnvironmentV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // This field allows for the addition of environment variables. // All environment variables are set on PID 1 in addition to every service. // Propagation of environment variables to services is done only at initial service start time. // To modify environment variables for services, the node must be restarted. // Multiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence. // Fully removing an environment variable can only be achieved by removing it from the document and restarting the machine. // Environment variable names are validated, and should: // - start with an uppercase letter, lowercase letter, or an underscore (_) character, and // - contain only uppercase and lowercase letters, underscore (_) characters, and numbers. // values: // - "`GRPC_GO_LOG_VERBOSITY_LEVEL`" // - "`GRPC_GO_LOG_SEVERITY_LEVEL`" // - "`http_proxy`" // - "`https_proxy`" // - "`no_proxy`" // examples: // - name: Environment variables definition examples. // value: exampleEnvVars0() // - value: exampleEnvVars1() // - value: exampleEnvVars2() // schema: // type: object // patternProperties: // ".*": // type: string EnvironmentVariables Env `yaml:"variables"` } // Env represents a set of environment variables. type Env = map[string]string // NewEnvironmentV1Alpha1 creates a new Environment config document. func NewEnvironmentV1Alpha1() *EnvironmentV1Alpha1 { return &EnvironmentV1Alpha1{ Meta: meta.Meta{ MetaKind: EnvironmentConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleEnvironmentV1Alpha1() *EnvironmentV1Alpha1 { cfg := NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = exampleEnvVars0() return cfg } func exampleEnvVars0() Env { return Env{ "GRPC_GO_LOG_VERBOSITY_LEVEL": "99", "GRPC_GO_LOG_SEVERITY_LEVEL": "info", "https_proxy": "http://SERVER:PORT/", } } func exampleEnvVars1() Env { return Env{ "GRPC_GO_LOG_SEVERITY_LEVEL": "error", "https_proxy": "https://USERNAME:PASSWORD@SERVER:PORT/", } } func exampleEnvVars2() Env { return Env{ "https_proxy": "http://DOMAIN\\USERNAME:PASSWORD@SERVER:PORT/", } } // Clone implements config.Document interface. func (s *EnvironmentV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Variables implements config.EnvironmentConfig interface. func (s *EnvironmentV1Alpha1) Variables() Env { return s.EnvironmentVariables } // POSIX1EnvKeyRegex is a regex for validating POSIX.1 environment variable names. var POSIX1EnvKeyRegex = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`) // Validate implements config.Validator interface. func (s *EnvironmentV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var err error for key := range s.EnvironmentVariables { if !POSIX1EnvKeyRegex.MatchString(key) { err = errors.Join(err, fmt.Errorf("invalid environment variable name: %q", key)) } } return nil, err } ================================================ FILE: pkg/machinery/config/types/runtime/environment_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" ) //go:embed testdata/environment.yaml var expectedEnvironmentDocument []byte func TestEnvironmentMarshalStability(t *testing.T) { cfg := runtime.NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = map[string]string{ "HTTP_PROXY": "http://proxy.example.com:8080", } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedEnvironmentDocument, marshaled) } func TestEnvironmentValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *runtime.EnvironmentV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: runtime.NewEnvironmentV1Alpha1, }, { name: "valid", cfg: func() *runtime.EnvironmentV1Alpha1 { cfg := runtime.NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = map[string]string{ "HTTP_PROXY": "http://proxy.example.com:8080", "HTTPS_PROXY": "http://proxy.example.com:8080", "NO_PROXY": "localhost", } return cfg }, }, { name: "invalid - starts with invalid character", cfg: func() *runtime.EnvironmentV1Alpha1 { cfg := runtime.NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = map[string]string{ "1INVALID": "value", } return cfg }, expectedError: "invalid environment variable name: \"1INVALID\"", }, { name: "invalid - contains invalid character", cfg: func() *runtime.EnvironmentV1Alpha1 { cfg := runtime.NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = map[string]string{ "INVALID-CHAR": "value", } return cfg }, expectedError: "invalid environment variable name: \"INVALID-CHAR\"", }, { name: "invalid - empty", cfg: func() *runtime.EnvironmentV1Alpha1 { cfg := runtime.NewEnvironmentV1Alpha1() cfg.EnvironmentVariables = map[string]string{ "": "value", } return cfg }, expectedError: "invalid environment variable name: \"\"", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/runtime/event_sink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //docgen:jsonschema import ( "fmt" "net" "net/url" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // EventSinkKind is a event sink config document kind. const EventSinkKind = "EventSinkConfig" func init() { registry.Register(EventSinkKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &EventSinkV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RuntimeConfig = &EventSinkV1Alpha1{} _ config.Validator = &EventSinkV1Alpha1{} ) // EventSinkV1Alpha1 is a event sink config document. // // examples: // - value: exampleEventSinkV1Alpha1() // alias: EventSinkConfig // schemaRoot: true // schemaMeta: v1alpha1/EventSinkConfig type EventSinkV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // The endpoint for the event sink as 'host:port'. // examples: // - value: > // "10.3.7.3:2810" Endpoint string `yaml:"endpoint"` } // NewEventSinkV1Alpha1 creates a new eventsink config document. func NewEventSinkV1Alpha1() *EventSinkV1Alpha1 { return &EventSinkV1Alpha1{ Meta: meta.Meta{ MetaKind: EventSinkKind, MetaAPIVersion: "v1alpha1", }, } } func exampleEventSinkV1Alpha1() *EventSinkV1Alpha1 { cfg := NewEventSinkV1Alpha1() cfg.Endpoint = "192.168.10.3:3247" return cfg } // Clone implements config.Document interface. func (s *EventSinkV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Runtime implements config.Config interface. func (s *EventSinkV1Alpha1) Runtime() config.RuntimeConfig { return s } // EventsEndpoint implements config.RuntimeConfig interface. func (s *EventSinkV1Alpha1) EventsEndpoint() *string { return new(s.Endpoint) } // KmsgLogURLs implements config.RuntimeConfig interface. func (s *EventSinkV1Alpha1) KmsgLogURLs() []*url.URL { return nil } // WatchdogTimer implements config.RuntimeConfig interface. func (s *EventSinkV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { return nil } // Validate implements config.Validator interface. func (s *EventSinkV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { _, _, err := net.SplitHostPort(s.Endpoint) if err != nil { return nil, fmt.Errorf("event sink endpoint: %w", err) } return nil, nil } ================================================ FILE: pkg/machinery/config/types/runtime/event_sink_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" ) //go:embed testdata/eventsink.yaml var expectedEventSinkDocument []byte func TestEventSinkMarshalStability(t *testing.T) { cfg := runtime.NewEventSinkV1Alpha1() cfg.Endpoint = "10.0.0.1:3333" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedEventSinkDocument, marshaled) } func TestEventSinkValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *runtime.EventSinkV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: runtime.NewEventSinkV1Alpha1, expectedError: "event sink endpoint: missing port in address", }, { name: "just IP", cfg: func() *runtime.EventSinkV1Alpha1 { cfg := runtime.NewEventSinkV1Alpha1() cfg.Endpoint = "10.0.0.1" return cfg }, expectedError: "event sink endpoint: address 10.0.0.1: missing port in address", }, { name: "valid", cfg: func() *runtime.EventSinkV1Alpha1 { cfg := runtime.NewEventSinkV1Alpha1() cfg.Endpoint = "[ff:80::01]:334" return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/types/runtime/extensions/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ServiceConfigV1Alpha1 -pointer-receiver -header-file ../../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package extensions // DeepCopy generates a deep copy of *ServiceConfigV1Alpha1. func (o *ServiceConfigV1Alpha1) DeepCopy() *ServiceConfigV1Alpha1 { var cp ServiceConfigV1Alpha1 = *o if o.ServiceConfigFiles != nil { cp.ServiceConfigFiles = make([]ConfigFile, len(o.ServiceConfigFiles)) copy(cp.ServiceConfigFiles, o.ServiceConfigFiles) } if o.ServiceEnvironment != nil { cp.ServiceEnvironment = make([]string, len(o.ServiceEnvironment)) copy(cp.ServiceEnvironment, o.ServiceEnvironment) } return &cp } ================================================ FILE: pkg/machinery/config/types/runtime/extensions/extensions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package extensions provides extensions config documents. package extensions //go:generate go tool github.com/siderolabs/talos/tools/docgen -output extensions_doc.go extensions.go service_config.go //go:generate go tool github.com/siderolabs/deep-copy -type ServiceConfigV1Alpha1 -pointer-receiver -header-file ../../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/runtime/extensions/extensions_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package extensions import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (ServiceConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExtensionServiceConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ExtensionServiceConfig is a extensionserviceconfig document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExtensionServiceConfig is a extensionserviceconfig document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the extension service.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the extension service." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "configFiles", Type: "[]ConfigFile", Note: "", Description: "The config files for the extension service.", Comments: [3]string{"" /* encoder.HeadComment */, "The config files for the extension service." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "environment", Type: "[]string", Note: "", Description: "The environment for the extension service.", Comments: [3]string{"" /* encoder.HeadComment */, "The environment for the extension service." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", extensionServiceConfigV1Alpha1()) return doc } func (ConfigFile) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ConfigFile", Comments: [3]string{"" /* encoder.HeadComment */, "ConfigFile is a config file for extension services." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ConfigFile is a config file for extension services.", AppearsIn: []encoder.Appearance{ { TypeName: "ServiceConfigV1Alpha1", FieldName: "configFiles", }, }, Fields: []encoder.Doc{ { Name: "content", Type: "string", Note: "", Description: "The content of the extension service config file.", Comments: [3]string{"" /* encoder.HeadComment */, "The content of the extension service config file." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mountPath", Type: "string", Note: "", Description: "The mount path of the extension service config file.", Comments: [3]string{"" /* encoder.HeadComment */, "The mount path of the extension service config file." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } // GetFileDoc returns documentation for the file extensions_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "extensions", Description: "Package extensions provides extensions config documents.\n", Structs: []*encoder.Doc{ ServiceConfigV1Alpha1{}.Doc(), ConfigFile{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/runtime/extensions/service_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions //docgen:jsonschema import ( "fmt" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // ServiceConfigKind is a Extension config document kind. const ServiceConfigKind = "ExtensionServiceConfig" func init() { registry.Register(ServiceConfigKind, func(version string) config.Document { switch version { case "v1alpha1": return &ServiceConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.ExtensionServiceConfig = &ServiceConfigV1Alpha1{} _ config.Document = &ServiceConfigV1Alpha1{} _ config.Validator = &ServiceConfigV1Alpha1{} ) // ServiceConfigV1Alpha1 is a extensionserviceconfig document. // // examples: // - value: extensionServiceConfigV1Alpha1() // alias: ExtensionServiceConfig // schemaRoot: true // schemaMeta: v1alpha1/ExtensionServiceConfig type ServiceConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the extension service. // schemaRequired: true ServiceName string `yaml:"name"` // description: | // The config files for the extension service. ServiceConfigFiles ConfigFileList `yaml:"configFiles,omitempty"` // description: | // The environment for the extension service. ServiceEnvironment []string `yaml:"environment,omitempty"` } // ConfigFileList is a list of ConfigFiles. // //docgen:alias type ConfigFileList []ConfigFile // Merge the config files by mountPath. func (list *ConfigFileList) Merge(other any) error { otherFiles, ok := other.(ConfigFileList) if !ok { return fmt.Errorf("unexpected type for config file merge %T", other) } for _, configFile := range otherFiles { if err := list.mergeConfigFile(configFile); err != nil { return err } } return nil } func (list *ConfigFileList) mergeConfigFile(configFile ConfigFile) error { var existing *ConfigFile for idx, cf := range *list { if cf.ConfigFileMountPath == configFile.ConfigFileMountPath { existing = &(*list)[idx] break } } if existing != nil { return merge.Merge(existing, &configFile) } *list = append(*list, configFile) return nil } // ConfigFile is a config file for extension services. type ConfigFile struct { // description: | // The content of the extension service config file. ConfigFileContent string `yaml:"content"` // description: | // The mount path of the extension service config file. ConfigFileMountPath string `yaml:"mountPath"` } // NewServicesConfigV1Alpha1 creates a new siderolink config document. func NewServicesConfigV1Alpha1() *ServiceConfigV1Alpha1 { return &ServiceConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ServiceConfigKind, MetaAPIVersion: "v1alpha1", }, } } // Clone implements config.Document interface. func (e *ServiceConfigV1Alpha1) Clone() config.Document { return e.DeepCopy() } // Validate implements config.Validatator interface. func (e *ServiceConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if e.ServiceName == "" { return nil, fmt.Errorf("name is required") } if len(e.ServiceConfigFiles) == 0 && len(e.ServiceEnvironment) == 0 { if len(e.ServiceConfigFiles) == 0 { return nil, fmt.Errorf("no config files found for extension %q", e.ServiceName) } if len(e.ServiceEnvironment) == 0 { return nil, fmt.Errorf("no environment defined for extension %q", e.ServiceName) } } for _, file := range e.ServiceConfigFiles { if file.ConfigFileContent == "" { return nil, fmt.Errorf("extension content is required for extension %q", e.ServiceName) } if file.ConfigFileMountPath == "" { return nil, fmt.Errorf("extension mount path is required for extension %q", e.ServiceName) } } return nil, nil } // Name implements config.ExtensionServiceConfig interface. func (e *ServiceConfigV1Alpha1) Name() string { return e.ServiceName } // ConfigFiles implements config.ExtensionServiceConfig interface. func (e *ServiceConfigV1Alpha1) ConfigFiles() []config.ExtensionServiceConfigFile { return xslices.Map(e.ServiceConfigFiles, func(c ConfigFile) config.ExtensionServiceConfigFile { return c }) } // Environment implements config.ExtensionServiceConfig interface. func (e *ServiceConfigV1Alpha1) Environment() []string { return e.ServiceEnvironment } // Content implements config.ExtensionServiceConfigFile interface. func (e ConfigFile) Content() string { return e.ConfigFileContent } // MountPath implements config.ExtensionServiceConfigFile interface. func (e ConfigFile) MountPath() string { return e.ConfigFileMountPath } func extensionServiceConfigV1Alpha1() *ServiceConfigV1Alpha1 { cfg := NewServicesConfigV1Alpha1() cfg.ServiceName = "nut-client" cfg.ServiceConfigFiles = []ConfigFile{ { ConfigFileContent: "MONITOR ${upsmonHost} 1 remote username password", ConfigFileMountPath: "/usr/local/etc/nut/upsmon.conf", }, } cfg.ServiceEnvironment = []string{"NUT_UPS=upsname"} return cfg } ================================================ FILE: pkg/machinery/config/types/runtime/extensions/service_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions" ) //go:embed testdata/extension_service_config.yaml var expectedExtensionServiceConfigDocument []byte func TestExtensionServiceConfigMarshalStability(t *testing.T) { t.Parallel() cfg := extensions.NewServicesConfigV1Alpha1() cfg.ServiceName = "foo" cfg.ServiceConfigFiles = []extensions.ConfigFile{ { ConfigFileContent: "hello", ConfigFileMountPath: "/etc/foo", }, } cfg.ServiceEnvironment = []string{"FOO=BAR"} marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedExtensionServiceConfigDocument, marshaled) } func TestExtensionServiceConfigMerge(t *testing.T) { t.Parallel() cfgLeft := extensions.NewServicesConfigV1Alpha1() cfgLeft.ServiceName = "foo" cfgLeft.ServiceConfigFiles = []extensions.ConfigFile{ { ConfigFileContent: "hello", ConfigFileMountPath: "/etc/foo", }, } cfgRight := cfgLeft.DeepCopy() cfgRight.ServiceConfigFiles[0].ConfigFileContent = "hello world" cfgRight.ServiceConfigFiles = append(cfgRight.ServiceConfigFiles, extensions.ConfigFile{ ConfigFileContent: "bar", ConfigFileMountPath: "/etc/bar", }, ) require.NoError(t, merge.Merge(cfgLeft, cfgRight)) require.Len(t, cfgLeft.ConfigFiles(), 2) assert.Equal(t, "hello world", cfgLeft.ConfigFiles()[0].Content()) assert.Equal(t, "bar", cfgLeft.ConfigFiles()[1].Content()) } ================================================ FILE: pkg/machinery/config/types/runtime/extensions/testdata/extension_service_config.yaml ================================================ apiVersion: v1alpha1 kind: ExtensionServiceConfig name: foo configFiles: - content: hello mountPath: /etc/foo environment: - FOO=BAR ================================================ FILE: pkg/machinery/config/types/runtime/kmsg_log.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //docgen:jsonschema import ( "errors" "net/url" "github.com/siderolabs/gen/ensure" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // KmsgLogKind is a kmsg log config document kind. const KmsgLogKind = "KmsgLogConfig" func init() { registry.Register(KmsgLogKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &KmsgLogV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RuntimeConfig = &KmsgLogV1Alpha1{} _ config.NamedDocument = &KmsgLogV1Alpha1{} _ config.Validator = &KmsgLogV1Alpha1{} ) // KmsgLogV1Alpha1 is a event sink config document. // // examples: // - value: exampleKmsgLogV1Alpha1() // alias: KmsgLogConfig // schemaRoot: true // schemaMeta: v1alpha1/KmsgLogConfig type KmsgLogV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the config document. MetaName string `yaml:"name"` // description: | // The URL encodes the log destination. // The scheme must be tcp:// or udp://. // The path must be empty. // The port is required. // examples: // - value: > // "udp://10.3.7.3:2810" // schema: // type: string // pattern: "^(tcp|udp)://" KmsgLogURL meta.URL `yaml:"url"` } // NewKmsgLogV1Alpha1 creates a new eventsink config document. func NewKmsgLogV1Alpha1() *KmsgLogV1Alpha1 { return &KmsgLogV1Alpha1{ Meta: meta.Meta{ MetaKind: KmsgLogKind, MetaAPIVersion: "v1alpha1", }, } } func exampleKmsgLogV1Alpha1() *KmsgLogV1Alpha1 { cfg := NewKmsgLogV1Alpha1() cfg.MetaName = "remote-log" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://192.168.3.7:3478/")) return cfg } // Name implements config.NamedDocument interface. func (s *KmsgLogV1Alpha1) Name() string { return s.MetaName } // Clone implements config.Document interface. func (s *KmsgLogV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Runtime implements config.Config interface. func (s *KmsgLogV1Alpha1) Runtime() config.RuntimeConfig { return s } // EventsEndpoint implements config.RuntimeConfig interface. func (s *KmsgLogV1Alpha1) EventsEndpoint() *string { return nil } // KmsgLogURLs implements config.RuntimeConfig interface. func (s *KmsgLogV1Alpha1) KmsgLogURLs() []*url.URL { return []*url.URL{s.KmsgLogURL.URL} } // WatchdogTimer implements config.RuntimeConfig interface. func (s *KmsgLogV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { return nil } // Validate implements config.Validator interface. func (s *KmsgLogV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.MetaName == "" { return nil, errors.New("name is required") } if s.KmsgLogURL.URL == nil { return nil, errors.New("url is required") } switch s.KmsgLogURL.URL.Scheme { case "tcp": case "udp": default: return nil, errors.New("url scheme must be tcp:// or udp://") } switch s.KmsgLogURL.URL.Path { case "/": case "": default: return nil, errors.New("url path must be empty") } if s.KmsgLogURL.URL.Port() == "" { return nil, errors.New("url port is required") } return nil, nil } ================================================ FILE: pkg/machinery/config/types/runtime/kmsg_log_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( _ "embed" "net/url" "testing" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" ) //go:embed testdata/kmsglog.yaml var expectedKmsgLogDocument []byte func TestKmsgLogMarshalStability(t *testing.T) { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "apiSink" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("https://kmsglog.api/logs")) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedKmsgLogDocument, marshaled) } func TestKmsgLogValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *runtime.KmsgLogV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: runtime.NewKmsgLogV1Alpha1, expectedError: "name is required", }, { name: "no URL", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name1" return cfg }, expectedError: "url is required", }, { name: "wrong scheme", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name2" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("https://some.destination/path")) return cfg }, expectedError: "url scheme must be tcp:// or udp://", }, { name: "extra path", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name5" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://some.destination:34/path")) return cfg }, expectedError: "url path must be empty", }, { name: "no port", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name6" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://some.destination/")) return cfg }, expectedError: "url port is required", }, { name: "valid TCP", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name3" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("tcp://10.2.3.4:5000/")) return cfg }, }, { name: "valid UDP", cfg: func() *runtime.KmsgLogV1Alpha1 { cfg := runtime.NewKmsgLogV1Alpha1() cfg.MetaName = "name4" cfg.KmsgLogURL.URL = ensure.Value(url.Parse("udp://10.2.3.4:5000/")) return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/runtime/oom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //docgen:jsonschema import ( "errors" "fmt" "time" "github.com/siderolabs/gen/optional" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" ) // OOMKind is a Out of Memory Handler document kind. const OOMKind = "OOMConfig" func init() { registry.Register(OOMKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &OOMV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.Validator = &OOMV1Alpha1{} _ config.OOMConfig = &OOMV1Alpha1{} ) // OOMV1Alpha1 is a Out of Memory handler config document. // // examples: // - value: exampleOOMV1Alpha1() // alias: OOMConfig // schemaRoot: true // schemaMeta: v1alpha1/OOMConfig type OOMV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // This expression defines when to trigger OOM action. // // The expression must evaluate to a boolean value. // If the expression returns true, then OOM ranking and killing will be handled. // // This expression receives the following parameters: // - memory_{some,full}_{avg10,avg60,avg300,total} - double, representing PSI values // - time_since_trigger - duration since the last OOM handler trigger event // schema: // type: string OOMTriggerExpression cel.Expression `yaml:"triggerExpression,omitempty"` // description: | // This expression defines how to rank cgroups for OOM handler. // // The cgroup with the highest rank (score) will be evicted first. // The expression must evaluate to a double value. // // This expression receives the following parameters: // - memory_max - Optional - in bytes // - memory_current - Optional - in bytes // - memory_peak - Optional - in bytes // - path - string, path to the cgroup // - class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System // schema: // type: string OOMCgroupRankingExpression cel.Expression `yaml:"cgroupRankingExpression,omitempty"` // description: | // How often should the trigger expression be evaluated. // // This interval determines how often should the OOM controller // check for the OOM condition using the provided expression. // Adjusting it can help tune the reactivity of the OOM handler. // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ OOMSampleInterval time.Duration `yaml:"sampleInterval,omitempty"` } // NewOOMV1Alpha1 creates a new eventsink config document. func NewOOMV1Alpha1() *OOMV1Alpha1 { return &OOMV1Alpha1{ Meta: meta.Meta{ MetaKind: OOMKind, MetaAPIVersion: "v1alpha1", }, } } func exampleOOMV1Alpha1() *OOMV1Alpha1 { cfg := NewOOMV1Alpha1() cfg.OOMSampleInterval = 100 * time.Millisecond cfg.OOMTriggerExpression = cel.MustExpression(cel.ParseBooleanExpression( constants.DefaultOOMTriggerExpression, celenv.OOMTrigger(), )) cfg.OOMCgroupRankingExpression = cel.MustExpression(cel.ParseDoubleExpression( constants.DefaultOOMCgroupRankingExpression, celenv.OOMCgroupScoring(), )) return cfg } // Clone implements config.Document interface. func (s *OOMV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. func (s *OOMV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var validationErrors error if !s.OOMTriggerExpression.IsZero() { if err := s.OOMTriggerExpression.ParseBool(celenv.OOMTrigger()); err != nil { validationErrors = errors.Join(validationErrors, fmt.Errorf("OOM trigger expression is invalid: %w", err)) } } if !s.OOMCgroupRankingExpression.IsZero() { if err := s.OOMCgroupRankingExpression.ParseDouble(celenv.OOMCgroupScoring()); err != nil { validationErrors = errors.Join(validationErrors, fmt.Errorf("OOM cgroup scoring expression is invalid: %w", err)) } } if s.OOMSampleInterval < 0 { validationErrors = errors.Join(validationErrors, fmt.Errorf("OOM sample interval must be longer than 0")) } return nil, validationErrors } // TriggerExpression returns the OOM trigger expression. func (s *OOMV1Alpha1) TriggerExpression() optional.Optional[cel.Expression] { if s.OOMCgroupRankingExpression.IsZero() { return optional.None[cel.Expression]() } return optional.Some(s.OOMTriggerExpression) } // CgroupRankingExpression returns the OOM cgroup ranking expression. func (s *OOMV1Alpha1) CgroupRankingExpression() optional.Optional[cel.Expression] { if s.OOMCgroupRankingExpression.IsZero() { return optional.None[cel.Expression]() } return optional.Some(s.OOMCgroupRankingExpression) } // SampleInterval returns the OOM sampling interval. func (s *OOMV1Alpha1) SampleInterval() optional.Optional[time.Duration] { if s.OOMSampleInterval == 0 { return optional.None[time.Duration]() } return optional.Some(s.OOMSampleInterval) } ================================================ FILE: pkg/machinery/config/types/runtime/oom_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( _ "embed" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" "github.com/siderolabs/talos/pkg/machinery/constants" ) //go:embed testdata/oom.yaml var expectedOOMDocument []byte func TestOOMMarshalStability(t *testing.T) { cfg := runtime.NewOOMV1Alpha1() cfg.OOMSampleInterval = 100 * time.Millisecond cfg.OOMTriggerExpression = cel.MustExpression(cel.ParseBooleanExpression( constants.DefaultOOMTriggerExpression, celenv.OOMTrigger(), )) cfg.OOMCgroupRankingExpression = cel.MustExpression(cel.ParseDoubleExpression( constants.DefaultOOMCgroupRankingExpression, celenv.OOMCgroupScoring(), )) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, string(expectedOOMDocument), string(marshaled)) } func TestOOMValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *runtime.OOMV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: runtime.NewOOMV1Alpha1, }, { name: "invalid expression", cfg: func() *runtime.OOMV1Alpha1 { cfg := runtime.NewOOMV1Alpha1() require.NoError(t, cfg.OOMCgroupRankingExpression.UnmarshalText([]byte(`disk.transport`))) return cfg }, expectedError: "OOM cgroup scoring expression is invalid: ERROR: :1:1: undeclared reference to 'disk' (in container '')\n | disk.transport\n | ^", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/runtime/runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package runtime provides runtime machine configuration documents. package runtime //go:generate go tool github.com/siderolabs/talos/tools/docgen -output runtime_doc.go runtime.go kmsg_log.go event_sink.go environment.go oom.go watchdog_timer.go //go:generate go tool github.com/siderolabs/deep-copy -type EventSinkV1Alpha1 -type EnvironmentV1Alpha1 -type KmsgLogV1Alpha1 -type OOMV1Alpha1 -type WatchdogTimerV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/runtime/runtime_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package runtime import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (KmsgLogV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KmsgLogConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KmsgLogConfig is a event sink config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KmsgLogConfig is a event sink config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the config document.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "url", Type: "URL", Note: "", Description: "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.", Comments: [3]string{"" /* encoder.HeadComment */, "The URL encodes the log destination." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleKmsgLogV1Alpha1()) doc.Fields[2].AddExample("", "udp://10.3.7.3:2810") return doc } func (EventSinkV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EventSinkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EventSinkConfig is a event sink config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EventSinkConfig is a event sink config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "endpoint", Type: "string", Note: "", Description: "The endpoint for the event sink as 'host:port'.", Comments: [3]string{"" /* encoder.HeadComment */, "The endpoint for the event sink as 'host:port'." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleEventSinkV1Alpha1()) doc.Fields[1].AddExample("", "10.3.7.3:2810") return doc } func (EnvironmentV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EnvironmentConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EnvironmentConfig is an environment config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EnvironmentConfig is an environment config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "variables", Type: "Env", Note: "", Description: "This field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore (_) character, and\n - contain only uppercase and lowercase letters, underscore (_) characters, and numbers.", Comments: [3]string{"" /* encoder.HeadComment */, "This field allows for the addition of environment variables." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "`GRPC_GO_LOG_VERBOSITY_LEVEL`", "`GRPC_GO_LOG_SEVERITY_LEVEL`", "`http_proxy`", "`https_proxy`", "`no_proxy`", }, }, }, } doc.AddExample("", exampleEnvironmentV1Alpha1()) doc.Fields[1].AddExample("Environment variables definition examples.", exampleEnvVars0()) doc.Fields[1].AddExample("", exampleEnvVars1()) doc.Fields[1].AddExample("", exampleEnvVars2()) return doc } func (OOMV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "OOMConfig", Comments: [3]string{"" /* encoder.HeadComment */, "OOMConfig is a Out of Memory handler config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "OOMConfig is a Out of Memory handler config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "triggerExpression", Type: "Expression", Note: "", Description: "This expression defines when to trigger OOM action.\n\nThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\n\nThis expression receives the following parameters:\n- memory_{some,full}_{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event", Comments: [3]string{"" /* encoder.HeadComment */, "This expression defines when to trigger OOM action." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "cgroupRankingExpression", Type: "Expression", Note: "", Description: "This expression defines how to rank cgroups for OOM handler.\n\nThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\n\nThis expression receives the following parameters:\n- memory_max - Optional - in bytes\n- memory_current - Optional - in bytes\n- memory_peak - Optional - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System", Comments: [3]string{"" /* encoder.HeadComment */, "This expression defines how to rank cgroups for OOM handler." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "sampleInterval", Type: "Duration", Note: "", Description: "How often should the trigger expression be evaluated.\n\nThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.", Comments: [3]string{"" /* encoder.HeadComment */, "How often should the trigger expression be evaluated." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleOOMV1Alpha1()) return doc } func (WatchdogTimerV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "WatchdogTimerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "WatchdogTimerConfig is a watchdog timer config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "WatchdogTimerConfig is a watchdog timer config document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "device", Type: "string", Note: "", Description: "Path to the watchdog device.", Comments: [3]string{"" /* encoder.HeadComment */, "Path to the watchdog device." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "timeout", Type: "Duration", Note: "", Description: "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", Comments: [3]string{"" /* encoder.HeadComment */, "Timeout for the watchdog." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleWatchdogTimerV1Alpha1()) doc.Fields[1].AddExample("", "/dev/watchdog0") return doc } // GetFileDoc returns documentation for the file runtime_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "runtime", Description: "Package runtime provides runtime machine configuration documents.\n", Structs: []*encoder.Doc{ KmsgLogV1Alpha1{}.Doc(), EventSinkV1Alpha1{}.Doc(), EnvironmentV1Alpha1{}.Doc(), OOMV1Alpha1{}.Doc(), WatchdogTimerV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/runtime/testdata/environment.yaml ================================================ apiVersion: v1alpha1 kind: EnvironmentConfig variables: HTTP_PROXY: http://proxy.example.com:8080 ================================================ FILE: pkg/machinery/config/types/runtime/testdata/eventsink.yaml ================================================ apiVersion: v1alpha1 kind: EventSinkConfig endpoint: 10.0.0.1:3333 ================================================ FILE: pkg/machinery/config/types/runtime/testdata/kmsglog.yaml ================================================ apiVersion: v1alpha1 kind: KmsgLogConfig name: apiSink url: https://kmsglog.api/logs ================================================ FILE: pkg/machinery/config/types/runtime/testdata/oom.yaml ================================================ apiVersion: v1alpha1 kind: OOMConfig triggerExpression: |- multiply_qos_vectors(d_qos_memory_full_total, {System: 8.0, Podruntime: 4.0}) > 3000.0 && multiply_qos_vectors(qos_memory_full_avg10, {System: 1.0, Podruntime: 1.0}) > 5.0 || memory_full_avg10 > 75.0 && time_since_trigger > duration("10s") cgroupRankingExpression: 'memory_max.hasValue() ? 0.0 : ({Besteffort: 1.0, Burstable: 0.5, Guaranteed: 0.0, Podruntime: 0.0, System: 0.0}[class] * double(memory_current.orValue(0u)))' sampleInterval: 100ms ================================================ FILE: pkg/machinery/config/types/runtime/testdata/watchdogtimer.yaml ================================================ apiVersion: v1alpha1 kind: WatchdogTimerConfig device: /dev/watchdog0 timeout: 3m0s ================================================ FILE: pkg/machinery/config/types/runtime/watchdog_timer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //docgen:jsonschema import ( "fmt" "net/url" "time" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // WatchdogTimerKind is a watchdog timer config document kind. const WatchdogTimerKind = "WatchdogTimerConfig" func init() { registry.Register(WatchdogTimerKind, func(version string) config.Document { switch version { case "v1alpha1": //nolint:goconst return &WatchdogTimerV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.RuntimeConfig = &WatchdogTimerV1Alpha1{} _ config.Validator = &WatchdogTimerV1Alpha1{} ) // Timeout constants. const ( MinWatchdogTimeout = 10 * time.Second DefaultWatchdogTimeout = time.Minute ) // WatchdogTimerV1Alpha1 is a watchdog timer config document. // // examples: // - value: exampleWatchdogTimerV1Alpha1() // alias: WatchdogTimerConfig // schemaRoot: true // schemaMeta: v1alpha1/WatchdogTimerConfig type WatchdogTimerV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Path to the watchdog device. // examples: // - value: > // "/dev/watchdog0" WatchdogDevice string `yaml:"device"` // description: | // Timeout for the watchdog. // // If Talos is unresponsive for this duration, the watchdog will reset the system. // // Default value is 1 minute, minimum value is 10 seconds. // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ WatchdogTimeout time.Duration `yaml:"timeout,omitempty"` } // NewWatchdogTimerV1Alpha1 creates a new eventsink config document. func NewWatchdogTimerV1Alpha1() *WatchdogTimerV1Alpha1 { return &WatchdogTimerV1Alpha1{ Meta: meta.Meta{ MetaKind: WatchdogTimerKind, MetaAPIVersion: "v1alpha1", }, } } func exampleWatchdogTimerV1Alpha1() *WatchdogTimerV1Alpha1 { cfg := NewWatchdogTimerV1Alpha1() cfg.WatchdogDevice = "/dev/watchdog0" cfg.WatchdogTimeout = 2 * time.Minute return cfg } // Clone implements config.Document interface. func (s *WatchdogTimerV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Runtime implements config.Config interface. func (s *WatchdogTimerV1Alpha1) Runtime() config.RuntimeConfig { return s } // EventsEndpoint implements config.RuntimeConfig interface. func (s *WatchdogTimerV1Alpha1) EventsEndpoint() *string { return nil } // KmsgLogURLs implements config.RuntimeConfig interface. func (s *WatchdogTimerV1Alpha1) KmsgLogURLs() []*url.URL { return nil } // WatchdogTimer implements config.RuntimeConfig interface. func (s *WatchdogTimerV1Alpha1) WatchdogTimer() config.WatchdogTimerConfig { return s } // Device implements config.WatchdogTimerConfig interface. func (s *WatchdogTimerV1Alpha1) Device() string { return s.WatchdogDevice } // Timeout implements config.WatchdogTimerConfig interface. func (s *WatchdogTimerV1Alpha1) Timeout() time.Duration { if s.WatchdogTimeout == 0 { return DefaultWatchdogTimeout } return s.WatchdogTimeout } // Validate implements config.Validator interface. func (s *WatchdogTimerV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.WatchdogDevice == "" { return nil, fmt.Errorf("watchdog device: empty value") } if s.WatchdogTimeout != 0 && s.WatchdogTimeout < MinWatchdogTimeout { return nil, fmt.Errorf("watchdog timeout: minimum value is %s", MinWatchdogTimeout) } return nil, nil } ================================================ FILE: pkg/machinery/config/types/runtime/watchdog_timer_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( _ "embed" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" ) //go:embed testdata/watchdogtimer.yaml var expectedWatchdogTimerDocument []byte func TestWatchdogTimerMarshalStability(t *testing.T) { cfg := runtime.NewWatchdogTimerV1Alpha1() cfg.WatchdogDevice = "/dev/watchdog0" cfg.WatchdogTimeout = 3 * time.Minute marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedWatchdogTimerDocument, marshaled) } func TestWatchdogTimerValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *runtime.WatchdogTimerV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: runtime.NewWatchdogTimerV1Alpha1, expectedError: "watchdog device: empty value", }, { name: "negative timeout", cfg: func() *runtime.WatchdogTimerV1Alpha1 { cfg := runtime.NewWatchdogTimerV1Alpha1() cfg.WatchdogDevice = "/dev/watchdog1" cfg.WatchdogTimeout = -1 * time.Minute return cfg }, expectedError: "watchdog timeout: minimum value is 10s", }, { name: "small timeout", cfg: func() *runtime.WatchdogTimerV1Alpha1 { cfg := runtime.NewWatchdogTimerV1Alpha1() cfg.WatchdogDevice = "/dev/watchdog1" cfg.WatchdogTimeout = time.Second return cfg }, expectedError: "watchdog timeout: minimum value is 10s", }, { name: "valid", cfg: func() *runtime.WatchdogTimerV1Alpha1 { cfg := runtime.NewWatchdogTimerV1Alpha1() cfg.WatchdogDevice = "/dev/watchdog2" return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/config/types/security/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ImageVerificationConfigV1Alpha1 -type TrustedRootsConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package security // DeepCopy generates a deep copy of *ImageVerificationConfigV1Alpha1. func (o *ImageVerificationConfigV1Alpha1) DeepCopy() *ImageVerificationConfigV1Alpha1 { var cp ImageVerificationConfigV1Alpha1 = *o if o.ConfigRules != nil { cp.ConfigRules = make([]ImageVerificationRuleV1Alpha1, len(o.ConfigRules)) copy(cp.ConfigRules, o.ConfigRules) for i2 := range o.ConfigRules { if o.ConfigRules[i2].RuleSkip != nil { cp.ConfigRules[i2].RuleSkip = new(bool) *cp.ConfigRules[i2].RuleSkip = *o.ConfigRules[i2].RuleSkip } if o.ConfigRules[i2].RuleDeny != nil { cp.ConfigRules[i2].RuleDeny = new(bool) *cp.ConfigRules[i2].RuleDeny = *o.ConfigRules[i2].RuleDeny } if o.ConfigRules[i2].RuleKeylessVerifier != nil { cp.ConfigRules[i2].RuleKeylessVerifier = new(ImageKeylessVerifierV1Alpha1) *cp.ConfigRules[i2].RuleKeylessVerifier = *o.ConfigRules[i2].RuleKeylessVerifier } if o.ConfigRules[i2].RulePublicKeyVerifier != nil { cp.ConfigRules[i2].RulePublicKeyVerifier = new(ImagePublicKeyVerifierV1Alpha1) *cp.ConfigRules[i2].RulePublicKeyVerifier = *o.ConfigRules[i2].RulePublicKeyVerifier } } } return &cp } // DeepCopy generates a deep copy of *TrustedRootsConfigV1Alpha1. func (o *TrustedRootsConfigV1Alpha1) DeepCopy() *TrustedRootsConfigV1Alpha1 { var cp TrustedRootsConfigV1Alpha1 = *o return &cp } ================================================ FILE: pkg/machinery/config/types/security/image_verification.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security import ( "errors" "fmt" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) // ImageVerificationConfigKind defines the ImageVerificationConfig configuration kind. const ImageVerificationConfigKind = "ImageVerificationConfig" func init() { registry.Register(ImageVerificationConfigKind, func(version string) config.Document { switch version { case "v1alpha1": return &ImageVerificationConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.ImageVerificationConfig = &ImageVerificationConfigV1Alpha1{} _ config.Validator = &ImageVerificationConfigV1Alpha1{} ) // ImageVerificationConfigV1Alpha1 configures image signature verification policy. // // examples: // - value: exampleImageVerificationConfigV1Alpha1() // alias: ImageVerificationConfig // schemaRoot: true // schemaMeta: v1alpha1/ImageVerificationConfig type ImageVerificationConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // List of verification rules. // Rules are evaluated in order; first matching rule applies. ConfigRules ImageVerificationRules `yaml:"rules,omitempty"` } // ImageVerificationRules is a list of ImageVerificationRuleV1Alpha1. // //docgen:alias type ImageVerificationRules []ImageVerificationRuleV1Alpha1 //docgen:nodoc type indexedRule struct { i int rule ImageVerificationRuleV1Alpha1 } // Merge the network interface configuration intelligently. func (r *ImageVerificationRules) Merge(other any) error { otherRules, ok := other.(ImageVerificationRules) if !ok { return fmt.Errorf("unexpected type for merge %T", other) } rulesByPattern := make(map[string]indexedRule) for i, rule := range *r { rulesByPattern[rule.RuleImagePattern] = indexedRule{i: i, rule: rule} } for _, otherRule := range otherRules { iRule, exists := rulesByPattern[otherRule.RuleImagePattern] if exists { // replace (*r)[iRule.i] = otherRule } else { // append *r = append(*r, otherRule) } } return nil } // ImageVerificationRuleV1Alpha1 defines a verification rule. type ImageVerificationRuleV1Alpha1 struct { // description: | // Image reference pattern to match for this rule. // Supports glob patterns. // examples: // - value: > // "docker.io/library/nginx" // - value: > // "registry.k8s.io/*" RuleImagePattern string `yaml:"image,omitempty"` // description: | // Skip verification for this image pattern (default: false). RuleSkip *bool `yaml:"skip,omitempty"` // description: | // Deny pulling images matching the pattern (default: false). RuleDeny *bool `yaml:"deny,omitempty"` // description: | // Keyless verifier configuration to use for this rule. RuleKeylessVerifier *ImageKeylessVerifierV1Alpha1 `yaml:"keyless,omitempty"` // description: | // Public key verifier configuration to use for this rule. RulePublicKeyVerifier *ImagePublicKeyVerifierV1Alpha1 `yaml:"publicKey,omitempty"` } // ImageKeylessVerifierV1Alpha1 configures a signature verification provider using Cosign keyless verification. type ImageKeylessVerifierV1Alpha1 struct { // description: | // OIDC issuer URL for keyless verification. // examples: // - value: > // "https://accounts.google.com" // - value: > // "https://token.actions.githubusercontent.com" KeylessIssuer string `yaml:"issuer,omitempty"` // description: | // Expected subject for keyless verification. // // This is the identity (email, URI) that signed the image. KeylessSubject string `yaml:"subject,omitempty"` // description: | // Regex pattern for subject matching. // // Use this instead of subject for flexible matching. // examples: // - value: > // ".*@example\\.com" KeylessSubjectRegex string `yaml:"subjectRegex,omitempty"` } // ImagePublicKeyVerifierV1Alpha1 configures a signature verification provider using a static public key. type ImagePublicKeyVerifierV1Alpha1 struct { // description: | // A public certificate in PEM format accepted for image signature verification. ConfigCertificate string `yaml:"certificate,omitempty"` } // NewImageVerificationConfigV1Alpha1 creates a new ImageVerificationConfig config document. func NewImageVerificationConfigV1Alpha1() *ImageVerificationConfigV1Alpha1 { return &ImageVerificationConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: ImageVerificationConfigKind, MetaAPIVersion: "v1alpha1", }, } } func exampleImageVerificationConfigV1Alpha1() *ImageVerificationConfigV1Alpha1 { cfg := NewImageVerificationConfigV1Alpha1() cfg.ConfigRules = []ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "registry.k8s.io/*", RuleKeylessVerifier: &ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubject: "krel-trust@k8s-releng-prod.iam.gserviceaccount.com", }, }, { RuleImagePattern: "my-registry/*", RulePublicKeyVerifier: &ImagePublicKeyVerifierV1Alpha1{ ConfigCertificate: `-----BEGIN CERTIFICATE----- MII--Sample Value-- -----END CERTIFICATE-----`, }, }, { RuleImagePattern: "locahost:3000/*", RuleDeny: new(true), }, } return cfg } // Clone implements config.Document interface. func (s *ImageVerificationConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Validate implements config.Validator interface. // //nolint:gocyclo func (s *ImageVerificationConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { var ( errs error warnings []string ) for i, rule := range s.ConfigRules { if rule.RuleImagePattern == "" { errs = errors.Join(errs, fmt.Errorf("rule %d: imagePattern must be specified", i)) } skip := pointer.SafeDeref(rule.RuleSkip) deny := pointer.SafeDeref(rule.RuleDeny) hasRules := rule.RuleKeylessVerifier != nil || rule.RulePublicKeyVerifier != nil if skip && deny { errs = errors.Join(errs, fmt.Errorf("rule %d: skip and deny cannot both be true", i)) } if (skip || deny) && hasRules { errs = errors.Join(errs, fmt.Errorf("rule %d: verifiers cannot be configured if skip or deny is true", i)) } if !skip && !deny && !hasRules { errs = errors.Join(errs, fmt.Errorf("rule %d: at least one verifier must be configured", i)) } if rule.RuleKeylessVerifier != nil && rule.RulePublicKeyVerifier != nil { errs = errors.Join(errs, fmt.Errorf("rule %d: only one of keyless or publicKey verifier can be configured", i)) } if rule.RuleKeylessVerifier != nil { ruleVerifier := rule.RuleKeylessVerifier if ruleVerifier.KeylessIssuer == "" { errs = errors.Join(errs, fmt.Errorf("rule %d: verifier OIDC issuer must be specified", i)) } if ruleVerifier.KeylessSubject == "" && ruleVerifier.KeylessSubjectRegex == "" { errs = errors.Join(errs, fmt.Errorf("rule %d: verifier subject or subjectRegex must be specified", i)) } } if rule.RulePublicKeyVerifier != nil { ruleVerifier := rule.RulePublicKeyVerifier if ruleVerifier.ConfigCertificate == "" { errs = errors.Join(errs, fmt.Errorf("rule %d: verifier certificates must be specified", i)) } } } return warnings, errs } // Rules implements config.ImageVerificationConfig interface. func (s *ImageVerificationConfigV1Alpha1) Rules() []config.ImageVerificationRule { return xslices.Map(s.ConfigRules, func(r ImageVerificationRuleV1Alpha1) config.ImageVerificationRule { return &r }) } // ImagePattern implements config.ImageVerificationRule interface. func (r *ImageVerificationRuleV1Alpha1) ImagePattern() string { return r.RuleImagePattern } // Skip implements config.ImageVerificationRule interface. func (r *ImageVerificationRuleV1Alpha1) Skip() bool { return pointer.SafeDeref(r.RuleSkip) } // Deny implements config.ImageVerificationRule interface. func (r *ImageVerificationRuleV1Alpha1) Deny() bool { return pointer.SafeDeref(r.RuleDeny) } // VerifierKeyless implements config.ImageVerificationRule interface. func (r *ImageVerificationRuleV1Alpha1) VerifierKeyless() config.ImageKeylessVerifier { if r.RuleKeylessVerifier == nil { return nil } return r.RuleKeylessVerifier } // VerifierPublicKey implements config.ImageVerificationRule interface. func (r *ImageVerificationRuleV1Alpha1) VerifierPublicKey() config.ImagePublicKeyVerifier { if r.RulePublicKeyVerifier == nil { return nil } return r.RulePublicKeyVerifier } // Issuer implements config.ImageVerifierKeyless interface. func (k *ImageKeylessVerifierV1Alpha1) Issuer() string { return k.KeylessIssuer } // Subject implements config.ImageVerifierKeyless interface. func (k *ImageKeylessVerifierV1Alpha1) Subject() string { return k.KeylessSubject } // SubjectRegex implements config.ImageVerifierKeyless interface. func (k *ImageKeylessVerifierV1Alpha1) SubjectRegex() string { return k.KeylessSubjectRegex } // Certificate implements config.ImagePublicKeyVerifier interface. func (p *ImagePublicKeyVerifierV1Alpha1) Certificate() string { return p.ConfigCertificate } ================================================ FILE: pkg/machinery/config/types/security/image_verification_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security_test import ( _ "embed" "testing" "github.com/siderolabs/go-pointer" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/security" ) //go:embed testdata/imageverificationconfig.yaml var expectedImageVerificationConfigDocument []byte func TestImageVerificationConfigMarshalStability(t *testing.T) { t.Parallel() cfg := security.NewImageVerificationConfigV1Alpha1() cfg.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "ghcr.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/myorg/.*", }, }, { RuleImagePattern: "my-registry/*", RulePublicKeyVerifier: &security.ImagePublicKeyVerifierV1Alpha1{ ConfigCertificate: `-----BEGIN CERTIFICATE----- MII--Sample Value-- -----END CERTIFICATE-----`, }, }, { RuleImagePattern: "no-verifier/*", RuleSkip: new(true), }, { RuleImagePattern: "deny-all/*", RuleDeny: new(true), }, } marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, string(expectedImageVerificationConfigDocument), string(marshaled)) } func TestImageVerificationConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedImageVerificationConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &security.ImageVerificationConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: security.ImageVerificationConfigKind, }, ConfigRules: []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "ghcr.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/myorg/.*", }, }, { RuleImagePattern: "my-registry/*", RulePublicKeyVerifier: &security.ImagePublicKeyVerifierV1Alpha1{ ConfigCertificate: `-----BEGIN CERTIFICATE----- MII--Sample Value-- -----END CERTIFICATE-----`, }, }, { RuleImagePattern: "no-verifier/*", RuleSkip: new(true), }, { RuleImagePattern: "deny-all/*", RuleDeny: new(true), }, }, }, docs[0]) } func TestImageVerificationConfigRules(t *testing.T) { t.Parallel() cfg := security.NewImageVerificationConfigV1Alpha1() cfg.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/library/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubjectRegex: "foo@bork.gserviceaccount.com", }, }, { RuleImagePattern: "**", RuleSkip: new(true), }, } rules := cfg.Rules() require.Len(t, rules, 2) assert.Equal(t, "docker.io/library/*", rules[0].ImagePattern()) assert.False(t, rules[0].Skip()) assert.False(t, rules[0].Deny()) assert.NotNil(t, rules[0].VerifierKeyless()) assert.Nil(t, rules[0].VerifierPublicKey()) assert.Equal(t, "https://accounts.google.com", rules[0].VerifierKeyless().Issuer()) assert.Equal(t, "foo@bork.gserviceaccount.com", rules[0].VerifierKeyless().SubjectRegex()) assert.Equal(t, "**", rules[1].ImagePattern()) assert.True(t, rules[1].Skip()) assert.False(t, rules[1].Deny()) assert.Nil(t, rules[1].VerifierKeyless()) assert.Nil(t, rules[1].VerifierPublicKey()) } func TestImageVerificationConfigValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *security.ImageVerificationConfigV1Alpha1 expectedErrors string }{ { name: "valid config", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "ghcr.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/myorg/.*", }, }, } return c }, }, { name: "valid config with subject instead of subjectRegex", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubject: "user@example.com", }, }, } return c }, }, { name: "valid config with skip and no verifier", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "registry.internal.example.com/*", RuleSkip: new(true), }, } return c }, }, { name: "valid config with deny and no verifier", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "registry.internal.example.com/*", RuleDeny: new(true), }, } return c }, }, { name: "rule missing imagePattern", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ {}, } return c }, expectedErrors: "rule 0: imagePattern must be specified\nrule 0: at least one verifier must be configured", }, { name: "no verifier", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", }, } return c }, expectedErrors: "rule 0: at least one verifier must be configured", }, { name: "verifier missing issuer", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessSubject: "user@example.com", }, }, } return c }, expectedErrors: "rule 0: verifier OIDC issuer must be specified", }, { name: "verifier missing subject and subjectRegex", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", }, }, } return c }, expectedErrors: "rule 0: verifier subject or subjectRegex must be specified", }, { name: "rule 0: verifiers cannot be configured if skip or deny is true", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "docker.io/*", RuleSkip: new(true), RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubject: "user@example.com", }, }, } return c }, expectedErrors: "rule 0: verifiers cannot be configured if skip or deny is true", }, { name: "multiple errors", cfg: func() *security.ImageVerificationConfigV1Alpha1 { c := security.NewImageVerificationConfigV1Alpha1() c.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleSkip: new(true), }, { RuleImagePattern: "docker.io/*", }, } return c }, expectedErrors: "rule 0: imagePattern must be specified\nrule 1: at least one verifier must be configured", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg() warnings, err := cfg.Validate(validationMode{}) if test.expectedErrors == "" { require.NoError(t, err) assert.Empty(t, warnings) } else { require.Error(t, err) assert.EqualError(t, err, test.expectedErrors) } }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } func TestImageVerificationConfigMerge(t *testing.T) { t.Parallel() baseCfg := security.NewImageVerificationConfigV1Alpha1() baseCfg.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "ghcr.io/siderolabs/talos*", RuleSkip: new(true), }, { RuleImagePattern: "docker.io/*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://accounts.google.com", KeylessSubject: "user@example.com", }, }, { RuleImagePattern: "**", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/fallbackorg/.*", }, }, } overrideCfg := security.NewImageVerificationConfigV1Alpha1() overrideCfg.ConfigRules = []security.ImageVerificationRuleV1Alpha1{ { RuleImagePattern: "ghcr.io/siderolabs/talos*", RuleKeylessVerifier: &security.ImageKeylessVerifierV1Alpha1{ KeylessIssuer: "https://token.actions.githubusercontent.com", KeylessSubjectRegex: "https://github.com/differentorg/.*", }, }, { RuleImagePattern: "**", RuleSkip: new(true), }, } require.NoError(t, merge.Merge(baseCfg, overrideCfg)) require.Len(t, baseCfg.ConfigRules, 3) assert.Equal(t, "ghcr.io/siderolabs/talos*", baseCfg.ConfigRules[0].RuleImagePattern) assert.False(t, pointer.SafeDeref(baseCfg.ConfigRules[0].RuleSkip)) assert.Equal(t, "https://token.actions.githubusercontent.com", baseCfg.ConfigRules[0].RuleKeylessVerifier.KeylessIssuer) assert.Equal(t, "https://github.com/differentorg/.*", baseCfg.ConfigRules[0].RuleKeylessVerifier.KeylessSubjectRegex) assert.Equal(t, "docker.io/*", baseCfg.ConfigRules[1].RuleImagePattern) assert.False(t, pointer.SafeDeref(baseCfg.ConfigRules[1].RuleSkip)) assert.Equal(t, "https://accounts.google.com", baseCfg.ConfigRules[1].RuleKeylessVerifier.KeylessIssuer) assert.Equal(t, "user@example.com", baseCfg.ConfigRules[1].RuleKeylessVerifier.KeylessSubject) assert.Equal(t, "**", baseCfg.ConfigRules[2].RuleImagePattern) assert.True(t, pointer.SafeDeref(baseCfg.ConfigRules[2].RuleSkip)) assert.Nil(t, baseCfg.ConfigRules[2].RuleKeylessVerifier) } ================================================ FILE: pkg/machinery/config/types/security/security.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package security provides security-related machine configuration documents. package security //go:generate go tool github.com/siderolabs/talos/tools/docgen -output security_doc.go security.go trusted_roots.go image_verification.go //go:generate go tool github.com/siderolabs/deep-copy -type ImageVerificationConfigV1Alpha1 -type TrustedRootsConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/config/types/security/security_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package security import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (TrustedRootsConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "TrustedRootsConfig", Comments: [3]string{"" /* encoder.HeadComment */, "TrustedRootsConfig allows to configure additional trusted CA roots." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "TrustedRootsConfig allows to configure additional trusted CA roots.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "name", Type: "string", Note: "", Description: "Name of the config document.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the config document." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "certificates", Type: "string", Note: "", Description: "List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.", Comments: [3]string{"" /* encoder.HeadComment */, "List of additional trusted certificate authorities (as PEM-encoded certificates)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleTrustedRootsConfigV1Alpha1()) return doc } func (ImageVerificationConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ImageVerificationConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ImageVerificationConfig configures image signature verification policy." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ImageVerificationConfig configures image signature verification policy.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "rules", Type: "[]ImageVerificationRuleV1Alpha1", Note: "", Description: "List of verification rules.\nRules are evaluated in order; first matching rule applies.", Comments: [3]string{"" /* encoder.HeadComment */, "List of verification rules." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleImageVerificationConfigV1Alpha1()) return doc } func (ImageVerificationRuleV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ImageVerificationRuleV1Alpha1", Comments: [3]string{"" /* encoder.HeadComment */, "ImageVerificationRuleV1Alpha1 defines a verification rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ImageVerificationRuleV1Alpha1 defines a verification rule.", AppearsIn: []encoder.Appearance{ { TypeName: "ImageVerificationConfigV1Alpha1", FieldName: "rules", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "Image reference pattern to match for this rule.\nSupports glob patterns.", Comments: [3]string{"" /* encoder.HeadComment */, "Image reference pattern to match for this rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "skip", Type: "bool", Note: "", Description: "Skip verification for this image pattern (default: false).", Comments: [3]string{"" /* encoder.HeadComment */, "Skip verification for this image pattern (default: false)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "deny", Type: "bool", Note: "", Description: "Deny pulling images matching the pattern (default: false).", Comments: [3]string{"" /* encoder.HeadComment */, "Deny pulling images matching the pattern (default: false)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "keyless", Type: "ImageKeylessVerifierV1Alpha1", Note: "", Description: "Keyless verifier configuration to use for this rule.", Comments: [3]string{"" /* encoder.HeadComment */, "Keyless verifier configuration to use for this rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "publicKey", Type: "ImagePublicKeyVerifierV1Alpha1", Note: "", Description: "Public key verifier configuration to use for this rule.", Comments: [3]string{"" /* encoder.HeadComment */, "Public key verifier configuration to use for this rule." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", "docker.io/library/nginx") doc.Fields[0].AddExample("", "registry.k8s.io/*") return doc } func (ImageKeylessVerifierV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ImageKeylessVerifierV1Alpha1", Comments: [3]string{"" /* encoder.HeadComment */, "ImageKeylessVerifierV1Alpha1 configures a signature verification provider using Cosign keyless verification." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ImageKeylessVerifierV1Alpha1 configures a signature verification provider using Cosign keyless verification.", AppearsIn: []encoder.Appearance{ { TypeName: "ImageVerificationRuleV1Alpha1", FieldName: "keyless", }, }, Fields: []encoder.Doc{ { Name: "issuer", Type: "string", Note: "", Description: "OIDC issuer URL for keyless verification.", Comments: [3]string{"" /* encoder.HeadComment */, "OIDC issuer URL for keyless verification." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "subject", Type: "string", Note: "", Description: "Expected subject for keyless verification.\n\nThis is the identity (email, URI) that signed the image.", Comments: [3]string{"" /* encoder.HeadComment */, "Expected subject for keyless verification." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "subjectRegex", Type: "string", Note: "", Description: "Regex pattern for subject matching.\n\nUse this instead of subject for flexible matching.", Comments: [3]string{"" /* encoder.HeadComment */, "Regex pattern for subject matching." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", "https://accounts.google.com") doc.Fields[0].AddExample("", "https://token.actions.githubusercontent.com") doc.Fields[2].AddExample("", ".*@example\\.com") return doc } func (ImagePublicKeyVerifierV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ImagePublicKeyVerifierV1Alpha1", Comments: [3]string{"" /* encoder.HeadComment */, "ImagePublicKeyVerifierV1Alpha1 configures a signature verification provider using a static public key." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ImagePublicKeyVerifierV1Alpha1 configures a signature verification provider using a static public key.", AppearsIn: []encoder.Appearance{ { TypeName: "ImageVerificationRuleV1Alpha1", FieldName: "publicKey", }, }, Fields: []encoder.Doc{ { Name: "certificate", Type: "string", Note: "", Description: "A public certificate in PEM format accepted for image signature verification.", Comments: [3]string{"" /* encoder.HeadComment */, "A public certificate in PEM format accepted for image signature verification." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } // GetFileDoc returns documentation for the file security_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "security", Description: "Package security provides security-related machine configuration documents.\n", Structs: []*encoder.Doc{ TrustedRootsConfigV1Alpha1{}.Doc(), ImageVerificationConfigV1Alpha1{}.Doc(), ImageVerificationRuleV1Alpha1{}.Doc(), ImageKeylessVerifierV1Alpha1{}.Doc(), ImagePublicKeyVerifierV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/security/testdata/imageverificationconfig.yaml ================================================ apiVersion: v1alpha1 kind: ImageVerificationConfig rules: - image: ghcr.io/* keyless: issuer: https://token.actions.githubusercontent.com subjectRegex: https://github.com/myorg/.* - image: my-registry/* publicKey: certificate: |- -----BEGIN CERTIFICATE----- MII--Sample Value-- -----END CERTIFICATE----- - image: no-verifier/* skip: true - image: deny-all/* deny: true ================================================ FILE: pkg/machinery/config/types/security/testdata/trustedrootsconfig.yaml ================================================ apiVersion: v1alpha1 kind: TrustedRootsConfig name: custom-ca certificates: |- -----BEGIN CERTIFICATE----- MIIC0DCCAbigAwIBAgIUI7z -----END CERTIFICATE----- ================================================ FILE: pkg/machinery/config/types/security/trusted_roots.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security //docgen:jsonschema import ( "strings" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" ) // TrustedRootsConfig is a default action config document kind. const TrustedRootsConfig = "TrustedRootsConfig" func init() { registry.Register(TrustedRootsConfig, func(version string) config.Document { switch version { case "v1alpha1": return &TrustedRootsConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.TrustedRootsConfig = &TrustedRootsConfigV1Alpha1{} _ config.NamedDocument = &TrustedRootsConfigV1Alpha1{} ) // TrustedRootsConfigV1Alpha1 allows to configure additional trusted CA roots. // // examples: // - value: exampleTrustedRootsConfigV1Alpha1() // alias: TrustedRootsConfig // schemaRoot: true // schemaMeta: v1alpha1/TrustedRootsConfig type TrustedRootsConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // Name of the config document. // schemaRequired: true MetaName string `yaml:"name"` // description: | // List of additional trusted certificate authorities (as PEM-encoded certificates). // // Multiple certificates can be provided in a single config document, separated by newline characters. Certificates string `yaml:"certificates"` } // NewTrustedRootsConfigV1Alpha1 creates a new TrustedRootsConfig config document. func NewTrustedRootsConfigV1Alpha1() *TrustedRootsConfigV1Alpha1 { return &TrustedRootsConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: TrustedRootsConfig, MetaAPIVersion: "v1alpha1", }, } } func exampleTrustedRootsConfigV1Alpha1() *TrustedRootsConfigV1Alpha1 { cfg := NewTrustedRootsConfigV1Alpha1() cfg.MetaName = "my-enterprise-ca" cfg.Certificates = `-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- ` return cfg } // Clone implements config.Document interface. func (s *TrustedRootsConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Name implements config.NamedDocument interface. func (s *TrustedRootsConfigV1Alpha1) Name() string { return s.MetaName } // ExtraTrustedRootCertificates implements config.TrustedRootsConfig interface. func (s *TrustedRootsConfigV1Alpha1) ExtraTrustedRootCertificates() []string { // build a header with the config name header := "\n" + s.MetaName + ":\n" + strings.Repeat("=", len(s.MetaName)+1) + "\n" return []string{header + s.Certificates} } ================================================ FILE: pkg/machinery/config/types/security/trusted_roots_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security_test import ( _ "embed" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/types/security" ) //go:embed testdata/trustedrootsconfig.yaml var expectedTrustedRootsConfigDocument []byte func TestTrustedRootsMarshalStability(t *testing.T) { t.Parallel() cfg := security.NewTrustedRootsConfigV1Alpha1() cfg.MetaName = "custom-ca" cfg.Certificates = "-----BEGIN CERTIFICATE-----\nMIIC0DCCAbigAwIBAgIUI7z\n-----END CERTIFICATE-----" marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) t.Log(string(marshaled)) assert.Equal(t, expectedTrustedRootsConfigDocument, marshaled) } func TestTrustedConfigUnmarshal(t *testing.T) { t.Parallel() provider, err := configloader.NewFromBytes(expectedTrustedRootsConfigDocument) require.NoError(t, err) docs := provider.Documents() require.Len(t, docs, 1) assert.Equal(t, &security.TrustedRootsConfigV1Alpha1{ Meta: meta.Meta{ MetaAPIVersion: "v1alpha1", MetaKind: security.TrustedRootsConfig, }, MetaName: "custom-ca", Certificates: "-----BEGIN CERTIFICATE-----\nMIIC0DCCAbigAwIBAgIUI7z\n-----END CERTIFICATE-----", }, docs[0]) } func TestTrustedConfigHeader(t *testing.T) { t.Parallel() cfg := security.NewTrustedRootsConfigV1Alpha1() cfg.MetaName = "custom-ca" cfg.Certificates = "-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----" assert.Equal(t, []string{"\ncustom-ca:\n==========\n-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"}, cfg.ExtraTrustedRootCertificates()) } ================================================ FILE: pkg/machinery/config/types/siderolink/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package siderolink import ( "net/url" ) // DeepCopy generates a deep copy of *ConfigV1Alpha1. func (o *ConfigV1Alpha1) DeepCopy() *ConfigV1Alpha1 { var cp ConfigV1Alpha1 = *o if o.APIUrlConfig.URL != nil { cp.APIUrlConfig.URL = new(url.URL) *cp.APIUrlConfig.URL = *o.APIUrlConfig.URL if o.APIUrlConfig.URL.User != nil { cp.APIUrlConfig.URL.User = new(url.Userinfo) *cp.APIUrlConfig.URL.User = *o.APIUrlConfig.URL.User } } return &cp } ================================================ FILE: pkg/machinery/config/types/siderolink/siderolink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package siderolink provides SideroLink machine configuration documents. package siderolink //docgen:jsonschema import ( "errors" "net/url" "github.com/siderolabs/gen/ensure" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/types/meta" "github.com/siderolabs/talos/pkg/machinery/config/validation" ) //go:generate go tool github.com/siderolabs/talos/tools/docgen -output ./siderolink_doc.go ./siderolink.go //go:generate go tool github.com/siderolabs/deep-copy -type ConfigV1Alpha1 -pointer-receiver -header-file ../../../../../hack/boilerplate.txt -o deep_copy.generated.go . // Kind is a siderolink config document kind. const Kind = "SideroLinkConfig" func init() { registry.Register(Kind, func(version string) config.Document { switch version { case "v1alpha1": return &ConfigV1Alpha1{} default: return nil } }) } // Check interfaces. var ( _ config.SecretDocument = &ConfigV1Alpha1{} _ config.SideroLinkConfig = &ConfigV1Alpha1{} _ config.Validator = &ConfigV1Alpha1{} ) // ConfigV1Alpha1 is a SideroLink connection machine configuration document. // // examples: // - value: exampleConfigV1Alpha1() // alias: SideroLinkConfig // schemaRoot: true // schemaMeta: v1alpha1/SideroLinkConfig type ConfigV1Alpha1 struct { meta.Meta `yaml:",inline"` // description: | // SideroLink API URL to connect to. // examples: // - value: > // "https://siderolink.api/?jointoken=secret" // schema: // type: string // pattern: "^(https|grpc)://" APIUrlConfig meta.URL `yaml:"apiUrl"` // description: | // SideroLink unique token to use for the connection (optional). // // This value is overridden with META key UniqueMachineToken. UniqueTokenConfig string `yaml:"uniqueToken,omitempty"` } // NewConfigV1Alpha1 creates a new siderolink config document. func NewConfigV1Alpha1() *ConfigV1Alpha1 { return &ConfigV1Alpha1{ Meta: meta.Meta{ MetaKind: Kind, MetaAPIVersion: "v1alpha1", }, } } func exampleConfigV1Alpha1() *ConfigV1Alpha1 { cfg := NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api/jointoken?token=secret")) return cfg } // Clone implements config.Document interface. func (s *ConfigV1Alpha1) Clone() config.Document { return s.DeepCopy() } // Redact implements config.SecretDocument interface. func (s *ConfigV1Alpha1) Redact(replacement string) { if s.APIUrlConfig.URL != nil { query := s.APIUrlConfig.Query() if query.Has("jointoken") { query.Set("jointoken", replacement) } s.APIUrlConfig.RawQuery = query.Encode() } } // SideroLink implements config.SideroLink interface. func (s *ConfigV1Alpha1) SideroLink() config.SideroLinkConfig { return s } // APIUrl implements config.SideroLink interface. func (s *ConfigV1Alpha1) APIUrl() *url.URL { if s == nil { return nil } return s.APIUrlConfig.URL } // UniqueToken implements config.SideroLink interface. func (s *ConfigV1Alpha1) UniqueToken() string { if s == nil { return "" } return s.UniqueTokenConfig } // Validate implements config.Validator interface. func (s *ConfigV1Alpha1) Validate(validation.RuntimeMode, ...validation.Option) ([]string, error) { if s.APIUrlConfig.URL == nil { return nil, errors.New("apiUrl is required") } switch s.APIUrlConfig.URL.Scheme { case "https": case "grpc": default: return nil, errors.New("apiUrl scheme must be https:// or grpc://") } switch s.APIUrlConfig.URL.Path { case "/": case "": default: return nil, errors.New("apiUrl path must be empty") } return nil, nil } ================================================ FILE: pkg/machinery/config/types/siderolink/siderolink_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package siderolink import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) func (ConfigV1Alpha1) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "SideroLinkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "SideroLinkConfig is a SideroLink connection machine configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "SideroLinkConfig is a SideroLink connection machine configuration document.", Fields: []encoder.Doc{ { Type: "Meta", Inline: true, }, { Name: "apiUrl", Type: "URL", Note: "", Description: "SideroLink API URL to connect to.", Comments: [3]string{"" /* encoder.HeadComment */, "SideroLink API URL to connect to." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "uniqueToken", Type: "string", Note: "", Description: "SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.", Comments: [3]string{"" /* encoder.HeadComment */, "SideroLink unique token to use for the connection (optional)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", exampleConfigV1Alpha1()) doc.Fields[1].AddExample("", "https://siderolink.api/?jointoken=secret") return doc } // GetFileDoc returns documentation for the file ./siderolink_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "siderolink", Description: "Package siderolink provides SideroLink machine configuration documents.\n", Structs: []*encoder.Doc{ ConfigV1Alpha1{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/siderolink/siderolink_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink_test import ( _ "embed" "net/url" "testing" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" ) func TestRedact(t *testing.T) { t.Parallel() cfg := siderolink.NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice")) assert.Equal(t, "https://siderolink.api/?jointoken=secret&user=alice", cfg.SideroLink().APIUrl().String()) cfg.Redact("REDACTED") assert.Equal(t, "https://siderolink.api/?jointoken=REDACTED&user=alice", cfg.APIUrlConfig.String()) } //go:embed testdata/document.yaml var expectedDocument []byte func TestMarshalStability(t *testing.T) { t.Parallel() cfg := siderolink.NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api/?jointoken=secret&user=alice")) marshaled, err := encoder.NewEncoder(cfg, encoder.WithComments(encoder.CommentsDisabled)).Encode() require.NoError(t, err) assert.Equal(t, expectedDocument, marshaled) } func TestValidate(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func() *siderolink.ConfigV1Alpha1 expectedError string expectedWarnings []string }{ { name: "empty", cfg: siderolink.NewConfigV1Alpha1, expectedError: "apiUrl is required", }, { name: "wrong scheme", cfg: func() *siderolink.ConfigV1Alpha1 { cfg := siderolink.NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("http://siderolink.api/")) return cfg }, expectedError: "apiUrl scheme must be https:// or grpc://", }, { name: "extra path", cfg: func() *siderolink.ConfigV1Alpha1 { cfg := siderolink.NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("grpc://siderolink.api/path?jointoken=foo")) return cfg }, expectedError: "apiUrl path must be empty", }, { name: "valid", cfg: func() *siderolink.ConfigV1Alpha1 { cfg := siderolink.NewConfigV1Alpha1() cfg.APIUrlConfig.URL = ensure.Value(url.Parse("https://siderolink.api:434/?jointoken=foo")) return cfg }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() warnings, err := test.cfg().Validate(validationMode{}) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) } }) } } type validationMode struct{} func (validationMode) String() string { return "" } func (validationMode) RequiresInstall() bool { return false } func (validationMode) InContainer() bool { return false } ================================================ FILE: pkg/machinery/config/types/siderolink/testdata/document.yaml ================================================ apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/?jointoken=secret&user=alice ================================================ FILE: pkg/machinery/config/types/types.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package types imports all configuration document types to register them. // //nolint:revive package types import ( _ "github.com/siderolabs/talos/pkg/machinery/config/types/block" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/cri" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/hardware" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/network" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/runtime" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/runtime/extensions" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/security" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/siderolink" // import config types to register them _ "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" // import config types to register them ) ================================================ FILE: pkg/machinery/config/types/v1alpha1/doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // +k8s:deepcopy-gen=package package v1alpha1 ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/patch.yaml ================================================ machine: kubelet: extraMounts: - source: /var/opt destination: /var/opt type: bind options: - rshared ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/secrets.yaml ================================================ # test set of secrets for config generation, please never use in production cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= secrets: bootstraptoken: inn7ol.u4ehnti8qyls9ymo secretboxencryptionsecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= trustdinfo: token: d8cwfa.eyvpi0xwxyarbfid certs: etcd: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8s: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8saggregator: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= k8sserviceaccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= os: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.10/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.10/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.10/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.10/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.11/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.11/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.11/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.11/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.12/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false grubUseUKICmdline: true features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.12/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false grubUseUKICmdline: true features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.12/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false grubUseUKICmdline: true sysctls: foo: bar features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - foo - bar admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: ghcr.io endpoints: - url: https://ghcr.io.my-mirror.com --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.12/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false grubUseUKICmdline: true sysctls: foo: bar features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: ghcr.io endpoints: - url: https://ghcr.io.my-mirror.com --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.13/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true install: wipe: false grubUseUKICmdline: true features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.13/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true install: wipe: false grubUseUKICmdline: true features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.13/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false grubUseUKICmdline: true sysctls: foo: bar features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - foo - bar admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: ghcr.io endpoints: - url: https://ghcr.io.my-mirror.com --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.13/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false grubUseUKICmdline: true sysctls: foo: bar features: diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} --- apiVersion: v1alpha1 kind: RegistryMirrorConfig name: ghcr.io endpoints: - url: https://ghcr.io.my-mirror.com --- apiVersion: v1alpha1 kind: HostnameConfig auto: stable ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.3/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.3/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.3/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.3/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.4/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.4/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.4/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.4/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.5/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.5/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.5/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.5/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.6/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.6/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.6/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.6/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.7/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.7/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.7/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.7/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.8/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.8/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.8/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.8/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.9/base-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.9/base-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: wipe: false features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.9/overrides-controlplane.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJTURXbklEdVpSdlhQcW1tbSt6bk15SWMrdk53ZjdnYksvSmR3WC9iN2d1RQotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true nodeLabels: node.kubernetes.io/exclude-from-external-load-balancers: "" cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 localAPIServerPort: 5443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo secretboxEncryptionSecret: 45yd2Ke+sytiICojDf8aibTfgt99nzJmO53cjDqrCto= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUVZbFloNzVTUTZ6VUJFTUZ6em5pUzZuVVg3Q2VxQ013S3k0RTZHVEVFMGNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFeXhvUi9JYklTZ3V2NG01azY2OFJTSzR6WDRjSHFoMlJHNVRCMEczenRtbnU0a1NHRUNWLwo2cmhCdzdHbE9KK2tjT3NEd0JNWGNGZ2dRVnBhQXM0MWF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJYakNDQVFXZ0F3SUJBZ0lRWnNnVDRZZzVxRkNIbS9QTnV5QUVSekFLQmdncWhrak9QUVFEQWpBQU1CNFgKRFRJek1UQXhNakV3TkRZd09Wb1hEVE16TVRBd09URXdORFl3T1Zvd0FEQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxRwpTTTQ5QXdFSEEwSUFCRmQ1eEhFWHhZRndQeTdaWjhmd3FHRGU2YVQ5ZmxNRVlWZENRNDlEaWZobWVteTVDaHZRCnlVRkpZcFM4b21HODVTS1dnOEpFTkoyNnhEdm9WMFBCS2srallUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWQKQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZApCZ05WSFE0RUZnUVV4K0xab1FrYjlmOTN0Y0g4NnZjOUc2ZE13T2t3Q2dZSUtvWkl6ajBFQXdJRFJ3QXdSQUlnClhudDVXdmEzOGtWVTB3NjExMEp4bU43Qm5zcWl2NnNMaXlJNXRUR1BDQk1DSUZDQlJ3RXZSYTNnU3pkdXB6ajcKQVJLV3NlK3V5YW9rMnlNYXZnaUVITWpUCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlMblhpQ3hOWU1CWHpncjVuYmc3bnVtUWM2UGlHaXdmWUN2eFF3Tlhxc3dvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjNuRWNSZkZnWEEvTHRsbngvQ29ZTjdwcFAxK1V3UmhWMEpEajBPSitHWjZiTGtLRzlESgpRVWxpbEx5aVliemxJcGFEd2tRMG5ickVPK2hYUThFcVR3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUlHVElBQjZZUzV0cFcrUnYxeDBPY09Jb1h0SXgzdGZteVFZNGxOWWRCbmpvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFQ3drbVVTUmtrbnlOc0NjTFJNUTlmZWx6cFY0dDdIdlNRcnp6ZGRvK2pWYmlqd2kwVVE1YQp0VW8vZkxQbDlBckVNOHNRWTVOSlgraVdxYjFkQWFXa2VnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: registry.k8s.io/kube-apiserver:v1.28.0 certSANs: - base - foo - bar disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata controllerManager: image: registry.k8s.io/kube-controller-manager:v1.28.0 proxy: image: registry.k8s.io/kube-proxy:v1.28.0 scheduler: image: registry.k8s.io/kube-scheduler:v1.28.0 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNPZ0F3SUJBZ0lRVkNTWmFQU3Z0TlZTcjYrVkRyUks0akFLQmdncWhrak9QUVFEQWpBUE1RMHcKQ3dZRFZRUUtFd1JsZEdOa01CNFhEVEl6TVRBeE1qRXdORFl3T1ZvWERUTXpNVEF3T1RFd05EWXdPVm93RHpFTgpNQXNHQTFVRUNoTUVaWFJqWkRCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk9wVXN0MHN3MEJZCkFDN0hpTGNrRElvdVdTRVhWTlJVWE42UmNLTWVRQU9VOEhJQkZBaTJlS2Rka2VJOEhZOTJNWTU1U21xQlhNK3cKRTh0RFgyT3kxSk9qWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjRApBUVlJS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVejVmai9oZTZoUjhMCkFRTU5qTjgxNS8zV3B6d3dDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWdFWWcyTlp3NkExek02eURNWTRHN1JPVkwKc0JOU0VhSDd4VmVSalBSblAvZ0NJUURiYzFMNmI0SkU0MCtuUCtYNG5pZlB0QWp5REhhUzVMS0YzQWZkUkRWdApMUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU03Q2VnMk1GQW5TM3ROMzV6QTc0aFZ3VElkTkthK0ZwUHlYVERCdU4wVFlvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFNmxTeTNTekRRRmdBTHNlSXR5UU1paTVaSVJkVTFGUmMzcEZ3b3g1QUE1VHdjZ0VVQ0xaNApwMTJSNGp3ZGozWXhqbmxLYW9GY3o3QVR5ME5mWTdMVWt3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= allowSchedulingOnControlPlanes: true ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/stability/v1.9/overrides-worker.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: worker token: d8cwfa.eyvpi0xwxyarbfid ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQakNCOGFBREFnRUNBaEI5cStGVXpodzkycHVPemtpNzB1eGRNQVVHQXl0bGNEQVFNUTR3REFZRFZRUUsKRXdWMFlXeHZjekFlRncweU16RXdNVEl4TURRMk1EbGFGdzB6TXpFd01Ea3hNRFEyTURsYU1CQXhEakFNQmdOVgpCQW9UQlhSaGJHOXpNQ293QlFZREsyVndBeUVBaHVLczZxeCtKWi8wWG8ybXdpQUNjK1EwSVYySGhMd3ozVTZICmUxemZjS2lqWVRCZk1BNEdBMVVkRHdFQi93UUVBd0lDaERBZEJnTlZIU1VFRmpBVUJnZ3JCZ0VGQlFjREFRWUkKS3dZQkJRVUhBd0l3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFkQmdOVkhRNEVGZ1FVSlgzWlVNRktWWFZ5NWhKWQozZG9NWENpVEJZRXdCUVlESzJWd0EwRUFCbUxrbDhITmQ3cUpEN3VqQkk2UG9abVRQQWlEcU9GQ0NTVDZJYlZDClF3UzQ1bk1tMldtalRIc3ZrYU5FQ0dneTBhQXJaaFdsbnVYWUswY0t3Z2VJQ0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" certSANs: - foo - bar kubelet: image: ghcr.io/siderolabs/kubelet:v1.28.0 extraMounts: - destination: /var/opt type: bind source: /var/opt options: - rshared defaultRuntimeSeccompProfileEnabled: true disableManifestsDirectory: true network: {} install: disk: /dev/vda extraKernelArgs: - foo=bar - bar=baz wipe: false sysctls: foo: bar registries: mirrors: ghcr.io: endpoints: - https://ghcr.io.my-mirror.com features: rbac: true stableHostname: true apidCheckExtKeyUsage: true diskQuotaSupport: true kubePrism: enabled: true port: 7445 hostDNS: enabled: true forwardKubeDNSToHost: true cluster: id: 0raF93qnkMvF-FZNuvyGozXNdLiT2FOWSlyBaW4PR-w= secret: pofHbABZq7VXuObsdLdy/bHmz6hlMHZ3p8+6WKrv1ic= controlPlane: endpoint: https://base:6443 clusterName: base network: cni: name: custom urls: - https://example.com/cni.yaml dnsDomain: example.com podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: inn7ol.u4ehnti8qyls9ymo ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRYm1hNDNPalRwR0I5TjVxOVFEc3RFekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXpNVEF4TWpFd05EWXdPVm9YRFRNek1UQXdPVEV3TkRZdwpPVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTXNhRWZ5R3lFb0xyK0p1Wk91dkVVaXVNMStIQjZvZGtSdVV3ZEJ0ODdacDd1SkVoaEFsZitxNFFjT3gKcFRpZnBIRHJBOEFURjNCWUlFRmFXZ0xPTld1allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU0ZEVkM1RoVzRKWlVWcXR1OEFZNWx1NUhQeGN3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUpJbkFMb0EKY1VhRUp4VlJ5dkhQenFQcTBvaGJOY2oyT3N2d3VKUFMzSktVQWlCSmhwNGFWMG9zUURRSGJnbjdXUWFYaHZFTwo5bWxTbVRURTAyOXBWb0YyWkE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: "" discovery: enabled: true registries: kubernetes: disabled: true service: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/001/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: interfaces: - interface: eth0 addresses: - 10.3.5.7/24 dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: true features: rbac: false env: http_proxy: http://127.0.0.1:3128/ cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://10.0.0.1/ clusterName: foo network: dnsDomain: cluster.local podSubnets: ["10.0.0.0/24"] serviceSubnets: ["192.168.0.0/24", "ff:08::/64"] token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: disabled: true service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/001/left.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: {} install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/001/right.yaml ================================================ machine: network: interfaces: - interface: eth0 addresses: - 10.3.5.7/24 dhcp: false features: rbac: false env: http_proxy: http://127.0.0.1:3128/ install: wipe: true cluster: controlPlane: endpoint: https://10.0.0.1 discovery: registries: kubernetes: disabled: true network: podSubnets: ["10.0.0.0/24"] serviceSubnets: ["192.168.0.0/24", "ff:08::/64"] serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/002/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: {} install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/002/left.yaml ================================================ version: v1alpha1 machine: {} ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/002/right.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: {} install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/003/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true vip: ip: 10.3.5.7 - deviceSelector: driver: macvtap dhcp: false routes: - network: 10.3.4.0/24 gateway: 10.3.4.1 - interface: eth1 addresses: - "192.168.0.1/24" install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/003/left.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/003/right.yaml ================================================ machine: network: interfaces: - interface: eth1 addresses: - "192.168.0.1/24" - interface: eth0 vip: ip: 10.3.5.7 - deviceSelector: driver: macvtap routes: - network: 10.3.4.0/24 gateway: 10.3.4.1 ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/004/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: restricted enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system - rook-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/004/left.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/004/right.yaml ================================================ cluster: apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 admissionControl: - name: PodSecurity configuration: defaults: enforce: restricted exemptions: namespaces: - rook-system ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/005/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.3 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/005/left.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/005/right.yaml ================================================ cluster: apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.3 ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/006/expected.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/006/left.yaml ================================================ version: v1alpha1 debug: false persist: true machine: type: controlplane token: u8ei4i.iymakyzguuqaw30r ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJQekNCOHFBREFnRUNBaEVBMENmN3VtOHV6akE4ZkRlY3FySElXakFGQmdNclpYQXdFREVPTUF3R0ExVUUKQ2hNRmRHRnNiM013SGhjTk1qSXdOakk1TVRNME9ERXhXaGNOTXpJd05qSTJNVE0wT0RFeFdqQVFNUTR3REFZRApWUVFLRXdWMFlXeHZjekFxTUFVR0F5dGxjQU1oQUR2dk1ESFdSY2xtRHdtOGRkNUZDV0w0djJQSlFnMkZ0bDBtCklLd1MwYjFObzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSEF3RUcKQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRk5TZGU2ZG9JZDFjckdLVwpxek1YbG80dStoVjRNQVVHQXl0bGNBTkJBR0c3aEQ3Z2FJckhnTnhKYjByOGVDdkozMS96eHQraW8wQlVoSi9FCnBvUkVxaWxOV1RHUDViSDRMcERqU2ZIOE1UcGdiZWhkZTRJUGxnRW5iR3VVYmdzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0KTUM0Q0FRQXdCUVlESzJWd0JDSUVJR2NZL0ZIU2lsNUhJTzlrcTNKZVBGbFdLNDJLZG9MOUwxYXBLZm1tdVJZaAotLS0tLUVORCBFRDI1NTE5IFBSSVZBVEUgS0VZLS0tLS0K certSANs: [] kubelet: image: ghcr.io/siderolabs/kubelet:v1.24.2 network: hostname: interfaces: - interface: eth0 addresses: - 172.20.0.2/24 dhcp: true - deviceSelector: driver: macvtap dhcp: false install: disk: /dev/sda image: ghcr.io/siderolabs/installer:v1.1.0-alpha.2-105-g2deff6b6e bootloader: true wipe: false features: rbac: true cluster: id: GGsG0g9PKDxVr1mV1hT929fu9lmC0MlTHfOkN63GJuQ= secret: se0RJPQ6v2aN0ExMc7yE4L5fMuK/N9wuyGr57R0MskI= controlPlane: endpoint: https://127.0.0.1:6643/ clusterName: foo network: dnsDomain: cluster.local podSubnets: - 10.244.0.0/16 serviceSubnets: - 10.96.0.0/12 token: 4pcl58.l0i5cv8h9k3k1az8 aescbcEncryptionSecret: A3U0/d6dmFeEO2/M6zQRWj9TqmhvOsM/RV8ZuxeIpXg= ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJpVENDQVMrZ0F3SUJBZ0lRVU1UcVNRYlR4a2Iwb2gxdnEyRVdRekFLQmdncWhrak9QUVFEQWpBVk1STXcKRVFZRFZRUUtFd3ByZFdKbGNtNWxkR1Z6TUI0WERUSXlNRFl5T1RFek5EZ3hNVm9YRFRNeU1EWXlOakV6TkRneApNVm93RlRFVE1CRUdBMVVFQ2hNS2EzVmlaWEp1WlhSbGN6QlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VICkEwSUFCTTZvTVUvcGJuY043SUI3OC9NMTVkMHBFRDVvK3FaWEZFUmJsd1VxUzJXUmplM0d3S2RDYWpXZjM2YUcKbHR5RWFJUlNzZnNISU4vZm45SFNWL08zUTR5allUQmZNQTRHQTFVZER3RUIvd1FFQXdJQ2hEQWRCZ05WSFNVRQpGakFVQmdnckJnRUZCUWNEQVFZSUt3WUJCUVVIQXdJd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBZEJnTlZIUTRFCkZnUVU5SjNqTHJmY2Z5TmlmNnBJampxVUlWdUQrbTR3Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUlnVE1QVnR5TnMKRW5Mc1k1dXVOajdLcldVcFFQNTRyaVJ3WVZueGZOeStQRzRDSVFDUm5sR3k0bTExZFc5RjBJWVFmQUoweUZKRQpXRHRsT3QraEJaZzczWXR6N0E9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU1IeFZuVGFXdlFjNTNBTnlWMjhYVmltTW83U24zWnRhbTFGa2JwQjFmczNvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFenFneFQrbHVkdzNzZ0h2ejh6WGwzU2tRUG1qNnBsY1VSRnVYQlNwTFpaR043Y2JBcDBKcQpOWi9mcG9hVzNJUm9oRkt4K3djZzM5K2YwZEpYODdkRGpBPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= aggregatorCA: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJZVENDQVFhZ0F3SUJBZ0lSQUlwbllnQVl3UTlWYUthNXI3K1Y1bW93Q2dZSUtvWkl6ajBFQXdJd0FEQWUKRncweU1qQTJNamt4TXpRNE1URmFGdzB6TWpBMk1qWXhNelE0TVRGYU1BQXdXVEFUQmdjcWhrak9QUUlCQmdncQpoa2pPUFFNQkJ3TkNBQVJINXpHY1M5NVZ4bjl4T1hOVXg5ai9PVytHTkkxcFREU1FRc3V0S0NtakhKRHN2VTNKCmFTa1NVWmxzSVpYbWkxZXhlTHRZeS9TN202M0JMaUZnUTFYMm8yRXdYekFPQmdOVkhROEJBZjhFQkFNQ0FvUXcKSFFZRFZSMGxCQll3RkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01BOEdBMVVkRXdFQi93UUZNQU1CQWY4dwpIUVlEVlIwT0JCWUVGSDZ1Uk1IVUFBaXJwRUU1SkhQS3ZrT3g2RUljTUFvR0NDcUdTTTQ5QkFNQ0Ewa0FNRVlDCklRQ0FBems2YjBrK0VNTkZBbzVaTHo0bElqQmtFZnZwWDdsaGtBclM4MjZmcWdJaEFJeUI5OVNJVkFuYkFONlAKeFlSaWFqNS81R1d2OTFiT0pZQ2N0Y2tGL294YwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSU5YS0M4YkkxbTBVd0NWb0NWQlZERndtL3lqaWdQUVNrdGV0MVAra0pjWU9vQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFUitjeG5FdmVWY1ovY1RselZNZlkvemx2aGpTTmFVdzBrRUxMclNncG94eVE3TDFOeVdrcApFbEdaYkNHVjVvdFhzWGk3V012MHU1dXR3UzRoWUVOVjlnPT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= serviceAccount: key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUd6VlpnLzlmdityQW9DNmZmRmRRZzdKMzk0ZFMxc3p2cmFTRklBZVJsV0lvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFVjYwUVgwN0hOVCtIbjJRNkJaQ1BPeVNqMExqS2FvMDM2TUJqMG5PMFFKNVVnZkhhaDVMUwp3QzRLajMwNU52bmZ4bnNnUnI5MWUrbjJreDMxTnJIaFF3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= apiServer: image: k8s.gcr.io/kube-apiserver:v1.24.2 certSANs: - 127.0.0.1 disablePodSecurityPolicy: true auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata admissionControl: - name: PodSecurity configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration controllerManager: image: k8s.gcr.io/kube-controller-manager:v1.24.2 proxy: image: k8s.gcr.io/kube-proxy:v1.24.2 scheduler: image: k8s.gcr.io/kube-scheduler:v1.24.2 discovery: enabled: true registries: kubernetes: {} service: {} etcd: ca: crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJmVENDQVNTZ0F3SUJBZ0lSQU9jRytGVk5EYTI0SXJ1YnA5QVRTVkl3Q2dZSUtvWkl6ajBFQXdJd0R6RU4KTUFzR0ExVUVDaE1FWlhSalpEQWVGdzB5TWpBMk1qa3hNelE0TVRGYUZ3MHpNakEyTWpZeE16UTRNVEZhTUE4eApEVEFMQmdOVkJBb1RCR1YwWTJRd1dUQVRCZ2NxaGtqT1BRSUJCZ2dxaGtqT1BRTUJCd05DQUFRa2pRd3puYmF4ClFyOUVyOG51TUQyUEdEUHBRak1nNGc5czMzeW1GMnpCQWZhMGlta2RMK0hCRkZhaDZ4OHNNVnFsOWJIZEFyRWIKcjVjUFdEeUpRUkxmbzJFd1h6QU9CZ05WSFE4QkFmOEVCQU1DQW9Rd0hRWURWUjBsQkJZd0ZBWUlLd1lCQlFVSApBd0VHQ0NzR0FRVUZCd01DTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3SFFZRFZSME9CQllFRkl3VmNlNHhrNjI0ClZseVcvaHVwek40U2FZZGVNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJRnphUzkyMExjK1dlOEpjNkk4dm9LWlQKZXJ3NDlMQ0o0VGpaeUwwVzl5RzdBaUI5QWRlOWNVa1AwSitDelZIdUVVU3NmVjBENFg4N0RyM3lUbGV0NHVVSQpDdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1IY0NBUUVFSUxqUCtiL1FnckZqTlh6WCswZWNQTU8xc1MvYzM4NUFObWFFU3VIbENSR0hvQW9HQ0NxR1NNNDkKQXdFSG9VUURRZ0FFSkkwTU01MjJzVUsvUksvSjdqQTlqeGd6NlVJeklPSVBiTjk4cGhkc3dRSDJ0SXBwSFMvaAp3UlJXb2VzZkxERmFwZld4M1FLeEc2K1hEMWc4aVVFUzN3PT0KLS0tLS1FTkQgRUMgUFJJVkFURSBLRVktLS0tLQo= ================================================ FILE: pkg/machinery/config/types/v1alpha1/testdata/strategic/006/right.yaml ================================================ cluster: apiServer: auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: None ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_admissionplugin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 // Name implements the config.AdmissionPlugin interface. func (a *AdmissionPluginConfig) Name() string { return a.PluginName } // Configuration implements the config.AdmissionPlugin interface. func (a *AdmissionPluginConfig) Configuration() map[string]any { return a.PluginConfiguration.Object } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_apiserverconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "strings" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) // APIServerDefaultAuditPolicy is the default kube-apiserver audit policy. var APIServerDefaultAuditPolicy = Unstructured{ Object: map[string]any{ "apiVersion": "audit.k8s.io/v1", "kind": "Policy", "rules": []any{ map[string]any{ "level": "Metadata", }, }, }, } // APIServerDefaultAuthorizationConfigAuthorizers is the default kube-apiserver authorization authorizers. var APIServerDefaultAuthorizationConfigAuthorizers = []k8s.AuthorizationAuthorizersSpec{ { Type: "Node", Name: "node", }, { Type: "RBAC", Name: "rbac", }, } // Image implements the config.APIServer interface. func (a *APIServerConfig) Image() string { image := a.ContainerImage if image == "" { image = fmt.Sprintf("%s:v%s", constants.KubernetesAPIServerImage, constants.DefaultKubernetesVersion) } return image } // ExtraArgs implements the config.APIServer interface. func (a *APIServerConfig) ExtraArgs() map[string][]string { return a.ExtraArgsConfig.ToMap() } // ExtraVolumes implements the config.APIServer interface. func (a *APIServerConfig) ExtraVolumes() []config.VolumeMount { return xslices.Map(a.ExtraVolumesConfig, func(v VolumeMountConfig) config.VolumeMount { return v }) } // Env implements the config.APIServer interface. func (a *APIServerConfig) Env() Env { return a.EnvConfig } // AdmissionControl implements the config.APIServer interface. func (a *APIServerConfig) AdmissionControl() []config.AdmissionPlugin { return xslices.Map(a.AdmissionControlConfig, func(c *AdmissionPluginConfig) config.AdmissionPlugin { return c }) } // AuditPolicy implements the config.APIServer interface. func (a *APIServerConfig) AuditPolicy() map[string]any { if len(a.AuditPolicyConfig.Object) == 0 { return APIServerDefaultAuditPolicy.DeepCopy().Object } return a.AuditPolicyConfig.Object } // Resources implements the config.Resources interface. func (a *APIServerConfig) Resources() config.Resources { return a.ResourcesConfig } // AuthorizationConfig implements the config.APIServer interface. func (a *APIServerConfig) AuthorizationConfig() []config.AuthorizationConfigAuthorizer { return xslices.Map(a.AuthorizationConfigConfig, func(c *AuthorizationConfigAuthorizerConfig) config.AuthorizationConfigAuthorizer { return c }) } // Validate performs config validation. func (a *APIServerConfig) Validate() error { if a == nil { return nil } if a.AuthorizationConfigConfig != nil { for k := range a.ExtraArgs() { if k == "authorization-mode" { return fmt.Errorf("authorization-mode cannot be used in conjunction with AuthorizationConfig, use eitherr AuthorizationConfig or authorization-mode") } if strings.HasPrefix(k, "authorization-webhook-") { return fmt.Errorf("authorization-webhook-* flags cannot be used in conjunction with AuthorizationConfig, use either AuthorizationConfig or authorization-webhook-* flags") } } } for _, authorizationConfig := range a.AuthorizationConfigConfig { if err := authorizationConfig.Validate(); err != nil { return fmt.Errorf("apiserver authorization config validation failed: %w", err) } } if err := a.ResourcesConfig.Validate(); err != nil { return fmt.Errorf("apiserver resource validation failed: %w", err) } return nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_authorizaationconfigauthorizer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "slices" ) var allowedAuthorizationAuthorizerTypes = []string{"Node", "RBAC", "Webhook"} // Type implements the config.AuthorizationConfig interface. func (a *AuthorizationConfigAuthorizerConfig) Type() string { return a.AuthorizerType } // Name implements the config.AuthorizationConfig interface. func (a *AuthorizationConfigAuthorizerConfig) Name() string { return a.AuthorizerName } // Webhook implements the config.AuthorizationConfig interface. func (a *AuthorizationConfigAuthorizerConfig) Webhook() map[string]any { return a.AuthorizerWebhook.Object } // Validate validates the AuthorizationConfigAuthorizerConfig. func (a *AuthorizationConfigAuthorizerConfig) Validate() error { if a.AuthorizerType == "" { return fmt.Errorf("authorizer type must be set") } if a.AuthorizerName == "" { return fmt.Errorf("authorizer name must be set") } if !slices.Contains(allowedAuthorizationAuthorizerTypes, a.AuthorizerType) { return fmt.Errorf("authorizer type %s is not allowed, allowed types are %v", a.AuthorizerType, allowedAuthorizationAuthorizerTypes) } return nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_clusterconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "net/netip" "net/url" "slices" "strings" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // ClusterConfig implements config.ClusterConfig, config.Token, and config.ClusterNetwork interfaces. // Name implements the config.ClusterConfig interface. func (c *ClusterConfig) Name() string { return c.ClusterName } // APIServer implements the config.ClusterConfig interface. func (c *ClusterConfig) APIServer() config.APIServer { if c.APIServerConfig == nil { return &APIServerConfig{} } return c.APIServerConfig } // ControllerManager implements the config.ClusterConfig interface. func (c *ClusterConfig) ControllerManager() config.ControllerManager { if c.ControllerManagerConfig == nil { return &ControllerManagerConfig{} } return c.ControllerManagerConfig } // Proxy implements the config.ClusterConfig interface. func (c *ClusterConfig) Proxy() config.Proxy { if c.ProxyConfig == nil { return &ProxyConfig{} } return c.ProxyConfig } // Scheduler implements the config.ClusterConfig interface. func (c *ClusterConfig) Scheduler() config.Scheduler { if c.SchedulerConfig == nil { return &SchedulerConfig{} } return c.SchedulerConfig } // Endpoint implements the config.ClusterConfig interface. func (c *ClusterConfig) Endpoint() *url.URL { return c.ControlPlane.Endpoint.URL } // Token implements the config.ClusterConfig interface. func (c *ClusterConfig) Token() config.Token { return clusterToken(c.BootstrapToken) } // CertSANs implements the config.ClusterConfig interface. func (c *ClusterConfig) CertSANs() []string { if c.APIServerConfig == nil { return nil } return c.APIServerConfig.CertSANs } // IssuingCA implements the config.ClusterConfig interface. func (c *ClusterConfig) IssuingCA() *x509.PEMEncodedCertificateAndKey { return c.ClusterCA } // AcceptedCAs implements the config.ClusterConfig interface. func (c *ClusterConfig) AcceptedCAs() []*x509.PEMEncodedCertificate { return slices.Clone(c.ClusterAcceptedCAs) } // AggregatorCA implements the config.ClusterConfig interface. func (c *ClusterConfig) AggregatorCA() *x509.PEMEncodedCertificateAndKey { return c.ClusterAggregatorCA } // ServiceAccount implements the config.ClusterConfig interface. func (c *ClusterConfig) ServiceAccount() *x509.PEMEncodedKey { return c.ClusterServiceAccount } // AESCBCEncryptionSecret implements the config.ClusterConfig interface. func (c *ClusterConfig) AESCBCEncryptionSecret() string { return c.ClusterAESCBCEncryptionSecret } // SecretboxEncryptionSecret implements the config.ClusterConfig interface. func (c *ClusterConfig) SecretboxEncryptionSecret() string { return c.ClusterSecretboxEncryptionSecret } // Etcd implements the config.ClusterConfig interface. func (c *ClusterConfig) Etcd() config.Etcd { if c.EtcdConfig == nil { return &EtcdConfig{} } return c.EtcdConfig } // Network implements the config.ClusterConfig interface. func (c *ClusterConfig) Network() config.ClusterNetwork { return c } // LocalAPIServerPort implements the config.ClusterConfig interface. func (c *ClusterConfig) LocalAPIServerPort() int { if c.ControlPlane == nil || c.ControlPlane.LocalAPIServerPort == 0 { return constants.DefaultControlPlanePort } return c.ControlPlane.LocalAPIServerPort } // CoreDNS implements the config.ClusterConfig interface. func (c *ClusterConfig) CoreDNS() config.CoreDNS { if c.CoreDNSConfig == nil { return &CoreDNS{} } return c.CoreDNSConfig } // ExternalCloudProvider implements the config.ClusterConfig interface. func (c *ClusterConfig) ExternalCloudProvider() config.ExternalCloudProvider { if c.ExternalCloudProviderConfig == nil { return &ExternalCloudProviderConfig{} } return c.ExternalCloudProviderConfig } // ExtraManifestURLs implements the config.ClusterConfig interface. func (c *ClusterConfig) ExtraManifestURLs() []string { return c.ExtraManifests } // ExtraManifestHeaderMap implements the config.ClusterConfig interface. func (c *ClusterConfig) ExtraManifestHeaderMap() map[string]string { return c.ExtraManifestHeaders } // InlineManifests implements the config.ClusterConfig interface. func (c *ClusterConfig) InlineManifests() []config.InlineManifest { return xslices.Map(c.ClusterInlineManifests, func(m ClusterInlineManifest) config.InlineManifest { return m }) } // AdminKubeconfig implements the config.ClusterConfig interface. func (c *ClusterConfig) AdminKubeconfig() config.AdminKubeconfig { if c.AdminKubeconfigConfig == nil { return &AdminKubeconfigConfig{} } return c.AdminKubeconfigConfig } // ScheduleOnControlPlanes implements the config.ClusterConfig interface. func (c *ClusterConfig) ScheduleOnControlPlanes() bool { if c.AllowSchedulingOnControlPlanes != nil { return pointer.SafeDeref(c.AllowSchedulingOnControlPlanes) } return pointer.SafeDeref(c.AllowSchedulingOnMasters) } // ID returns the unique identifier for the cluster. func (c *ClusterConfig) ID() string { return c.ClusterID } // Secret returns the cluster secret. func (c *ClusterConfig) Secret() string { return c.ClusterSecret } // CNI implements the config.ClusterNetwork interface. func (c *ClusterConfig) CNI() config.CNI { switch { case c.ClusterNetwork == nil: fallthrough case c.ClusterNetwork.CNI == nil: return &CNIConfig{ CNIName: constants.FlannelCNI, } } return c.ClusterNetwork.CNI } // PodCIDRs implements the config.ClusterNetwork interface. func (c *ClusterConfig) PodCIDRs() []string { switch { case c.ClusterNetwork == nil: fallthrough case len(c.ClusterNetwork.PodSubnet) == 0: return []string{constants.DefaultIPv4PodNet} } return c.ClusterNetwork.PodSubnet } // ServiceCIDRs implements the config.ClusterNetwork interface. func (c *ClusterConfig) ServiceCIDRs() []string { switch { case c.ClusterNetwork == nil: fallthrough case len(c.ClusterNetwork.ServiceSubnet) == 0: return []string{constants.DefaultIPv4ServiceNet} } return c.ClusterNetwork.ServiceSubnet } // DNSDomain implements the config.ClusterNetwork interface. func (c *ClusterConfig) DNSDomain() string { if c.ClusterNetwork == nil || c.ClusterNetwork.DNSDomain == "" { return constants.DefaultDNSDomain } return c.ClusterNetwork.DNSDomain } // APIServerIPs implements the config.ClusterNetwork interface. func (c *ClusterConfig) APIServerIPs() ([]netip.Addr, error) { serviceCIDRs, err := sideronet.SplitCIDRs(strings.Join(c.ServiceCIDRs(), ",")) if err != nil { return nil, fmt.Errorf("failed to process Service CIDRs: %w", err) } return sideronet.NthIPInCIDRSet(serviceCIDRs, 1) } // DNSServiceIPs implements the config.ClusterNetwork interface. func (c *ClusterConfig) DNSServiceIPs() ([]netip.Addr, error) { serviceCIDRs, err := sideronet.SplitCIDRs(strings.Join(c.ServiceCIDRs(), ",")) if err != nil { return nil, fmt.Errorf("failed to process Service CIDRs: %w", err) } return sideronet.NthIPInCIDRSet(serviceCIDRs, 10) } // Discovery implements the config.Cluster interface. func (c *ClusterConfig) Discovery() config.Discovery { if c.ClusterDiscoveryConfig == nil { return &ClusterDiscoveryConfig{} } return c.ClusterDiscoveryConfig } type clusterToken string // ID implements the config.Token interface. func (t clusterToken) ID() string { parts := strings.Split(string(t), ".") if len(parts) != 2 { return "" } return parts[0] } // Secret implements the config.Token interface. func (t clusterToken) Secret() string { parts := strings.Split(string(t), ".") if len(parts) != 2 { return "" } return parts[1] } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_cniconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" ) // Name implements the config.CNI interface. func (c *CNIConfig) Name() string { return c.CNIName } // URLs implements the config.CNI interface. func (c *CNIConfig) URLs() []string { return c.CNIUrls } // Flannel implements the config.CNI interface. func (c *CNIConfig) Flannel() config.FlannelCNI { return c.CNIFlannel } // ExtraArgs implements the config.FlannelCNI interface. func (c *FlannelCNIConfig) ExtraArgs() []string { if c == nil { return nil } return c.FlanneldExtraArgs } // KubeNetworkPoliciesEnabled implements the config.FlannelCNI interface. func (c *FlannelCNIConfig) KubeNetworkPoliciesEnabled() bool { if c == nil { return false } return pointer.SafeDeref(c.FlannelKubeNetworkPoliciesEnabled) } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_controllermanagerconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Image implements the config.ControllerManager interface. func (c *ControllerManagerConfig) Image() string { image := c.ContainerImage if image == "" { image = fmt.Sprintf("%s:v%s", constants.KubernetesControllerManagerImage, constants.DefaultKubernetesVersion) } return image } // ExtraArgs implements the config.ControllerManager interface. func (c *ControllerManagerConfig) ExtraArgs() map[string][]string { return c.ExtraArgsConfig.ToMap() } // ExtraVolumes implements the config.ControllerManager interface. func (c *ControllerManagerConfig) ExtraVolumes() []config.VolumeMount { return xslices.Map(c.ExtraVolumesConfig, func(v VolumeMountConfig) config.VolumeMount { return v }) } // Env implements the config.ControllerManager interface. func (c *ControllerManagerConfig) Env() Env { return c.EnvConfig } // Resources implements the config.Resources interface. func (c *ControllerManagerConfig) Resources() config.Resources { return c.ResourcesConfig } // Validate performs config validation. func (c *ControllerManagerConfig) Validate() error { if c == nil { return nil } if err := c.ResourcesConfig.Validate(); err != nil { return fmt.Errorf("controller-manager resource validation failed: %w", err) } return nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_discoveryconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Enabled implements the config.ClusterDiscovery interface. func (c *ClusterDiscoveryConfig) Enabled() bool { return pointer.SafeDeref(c.DiscoveryEnabled) } // Registries implements the config.ClusterDiscovery interface. func (c *ClusterDiscoveryConfig) Registries() config.DiscoveryRegistries { return c.DiscoveryRegistries } // Kubernetes implements the config.DiscoveryRegistries interface. func (c DiscoveryRegistriesConfig) Kubernetes() config.KubernetesRegistry { return c.RegistryKubernetes } // Service implements the config.DiscoveryRegistries interface. func (c DiscoveryRegistriesConfig) Service() config.ServiceRegistry { return c.RegistryService } // Enabled implements the config.KubernetesRegistry interface. func (c RegistryKubernetesConfig) Enabled() bool { return !pointer.SafeDeref(c.RegistryDisabled) } // Enabled implements the config.ServiceRegistry interface. func (c RegistryServiceConfig) Enabled() bool { return !pointer.SafeDeref(c.RegistryDisabled) } // Endpoint implements the config.ServiceRegistry interface. func (c RegistryServiceConfig) Endpoint() string { if c.RegistryEndpoint == "" { return constants.DefaultDiscoveryServiceEndpoint } return c.RegistryEndpoint } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_etcdconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Image implements the config.Etcd interface. func (e *EtcdConfig) Image() string { image := e.ContainerImage if image == "" { image = fmt.Sprintf("%s:%s", constants.EtcdImage, constants.DefaultEtcdVersion) } return image } // CA implements the config.Etcd interface. func (e *EtcdConfig) CA() *x509.PEMEncodedCertificateAndKey { return e.RootCA } // ExtraArgs implements the config.Etcd interface. func (e *EtcdConfig) ExtraArgs() map[string][]string { return e.EtcdExtraArgs.ToMap() } // AdvertisedSubnets implements the config.Etcd interface. func (e *EtcdConfig) AdvertisedSubnets() []string { if len(e.EtcdAdvertisedSubnets) > 0 { return e.EtcdAdvertisedSubnets } if e.EtcdSubnet != "" { return []string{e.EtcdSubnet} } return nil } // ListenSubnets implements the config.Etcd interface. func (e *EtcdConfig) ListenSubnets() []string { if len(e.EtcdListenSubnets) > 0 { return e.EtcdListenSubnets } // if advertised subnets are set, use them if len(e.EtcdAdvertisedSubnets) > 0 { return e.EtcdAdvertisedSubnets } // nothing set, rely on defaults (listen on all interfaces) return nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_examples.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "net/url" "strings" "time" "github.com/siderolabs/crypto/x509" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) func mustParseURL(uri string) *url.URL { u, err := url.Parse(uri) if err != nil { panic(err) } return u } // this is using custom type to avoid generating full example with all the nested structs. func configExample() any { return struct { Version string `yaml:"version"` Machine *yaml.Node Cluster *yaml.Node }{ Version: "v1alpha1", Machine: &yaml.Node{Kind: yaml.ScalarNode, LineComment: "..."}, Cluster: &yaml.Node{Kind: yaml.ScalarNode, LineComment: "..."}, } } func machineConfigExample() any { return struct { Type string Install *InstallConfig }{ Type: machine.TypeControlPlane.String(), Install: machineInstallExample(), } } func pemEncodedCertificateExample() *x509.PEMEncodedCertificateAndKey { return &x509.PEMEncodedCertificateAndKey{ Crt: []byte("--- EXAMPLE CERTIFICATE ---"), Key: []byte("--- EXAMPLE KEY ---"), } } func pemEncodedKeyExample() *x509.PEMEncodedKey { return &x509.PEMEncodedKey{ Key: []byte("--- EXAMPLE KEY ---"), } } func machineControlplaneExample() *MachineControlPlaneConfig { return &MachineControlPlaneConfig{ MachineControllerManager: &MachineControllerManagerConfig{ MachineControllerManagerDisabled: new(false), }, MachineScheduler: &MachineSchedulerConfig{ MachineSchedulerDisabled: new(true), }, } } func machineKubeletExample() *KubeletConfig { return &KubeletConfig{ KubeletImage: (&KubeletConfig{}).Image(), KubeletExtraArgs: Args{ "feature-gates": ArgValue{strValue: "ServerSideApply=true"}, }, } } func kubeletImageExample() string { return (&KubeletConfig{}).Image() } func machineInstallExample() *InstallConfig { return &InstallConfig{ InstallDisk: "/dev/sda", InstallImage: "ghcr.io/siderolabs/installer:latest", InstallWipe: new(false), InstallGrubUseUKICmdline: new(true), } } func machineInstallDiskSelectorExample() *InstallDiskSelector { return &InstallDiskSelector{ Model: "WDC*", Size: &InstallDiskSizeMatcher{ condition: ">= 1TB", }, } } func machineInstallDiskSizeMatcherExamples0() *InstallDiskSizeMatcher { return &InstallDiskSizeMatcher{ condition: "4GB", } } func machineInstallDiskSizeMatcherExamples1() *InstallDiskSizeMatcher { return &InstallDiskSizeMatcher{ condition: "> 1TB", } } func machineInstallDiskSizeMatcherExamples2() *InstallDiskSizeMatcher { return &InstallDiskSizeMatcher{ condition: "<= 2TB", } } func machineFilesExample() []*MachineFile { return []*MachineFile{ { FileContent: "...", FilePermissions: 0o666, FilePath: "/tmp/file.txt", FileOp: "append", }, } } func machineSysctlsExample() map[string]string { return map[string]string{ "kernel.domainname": "talos.dev", "net.ipv4.ip_forward": "0", "net/ipv6/conf/eth0.100/disable_ipv6": "1", } } func machineSysfsExample() map[string]string { return map[string]string{ "devices.system.cpu.cpu0.cpufreq.scaling_governor": "performance", } } func machineFeaturesExample() *FeaturesConfig { return &FeaturesConfig{ DiskQuotaSupport: new(true), } } func machineUdevExample() *UdevConfig { return &UdevConfig{ UdevRules: []string{"SUBSYSTEM==\"drm\", KERNEL==\"renderD*\", GROUP=\"44\", MODE=\"0660\""}, } } func clusterConfigExample() any { return struct { ControlPlane *ControlPlaneConfig `yaml:"controlPlane"` ClusterName string `yaml:"clusterName"` Network *ClusterNetworkConfig `yaml:"network"` }{ ControlPlane: clusterControlPlaneExample(), ClusterName: "talos.local", Network: clusterNetworkExample(), } } func clusterControlPlaneExample() *ControlPlaneConfig { return &ControlPlaneConfig{ Endpoint: &Endpoint{ &url.URL{ Host: "1.2.3.4", Scheme: "https", }, }, LocalAPIServerPort: 443, } } func clusterNetworkExample() *ClusterNetworkConfig { return &ClusterNetworkConfig{ CNI: &CNIConfig{ CNIName: constants.FlannelCNI, }, DNSDomain: "cluster.local", PodSubnet: []string{"10.244.0.0/16"}, ServiceSubnet: []string{"10.96.0.0/12"}, } } func resourcesConfigRequestsExample() Unstructured { return Unstructured{ Object: map[string]any{ "cpu": 1, "memory": "1Gi", }, } } func resourcesConfigLimitsExample() Unstructured { return Unstructured{ Object: map[string]any{ "cpu": 2, "memory": "2500Mi", }, } } func clusterAPIServerExample() *APIServerConfig { return &APIServerConfig{ ContainerImage: (&APIServerConfig{}).Image(), ExtraArgsConfig: Args{ "feature-gates": ArgValue{strValue: "ServerSideApply=true"}, "http2-max-streams-per-connection": ArgValue{strValue: "32"}, }, CertSANs: []string{ "1.2.3.4", "4.5.6.7", }, } } func clusterAPIServerImageExample() string { return (&APIServerConfig{}).Image() } func clusterControllerManagerExample() *ControllerManagerConfig { return &ControllerManagerConfig{ ContainerImage: (&ControllerManagerConfig{}).Image(), ExtraArgsConfig: Args{ "feature-gates": ArgValue{strValue: "ServerSideApply=true"}, }, } } func clusterControllerManagerImageExample() string { return (&ControllerManagerConfig{}).Image() } func clusterProxyExample() *ProxyConfig { return &ProxyConfig{ ContainerImage: (&ProxyConfig{}).Image(), ExtraArgsConfig: Args{ "proxy-mode": ArgValue{strValue: "iptables"}, }, ModeConfig: "ipvs", } } func clusterProxyImageExample() string { return (&ProxyConfig{}).Image() } func clusterSchedulerExample() *SchedulerConfig { return &SchedulerConfig{ ContainerImage: (&SchedulerConfig{}).Image(), ExtraArgsConfig: Args{ "feature-gates": ArgValue{strValue: "AllBeta=true"}, }, } } func clusterSchedulerImageExample() string { return (&SchedulerConfig{}).Image() } func clusterEtcdExample() *EtcdConfig { return &EtcdConfig{ ContainerImage: (&EtcdConfig{}).Image(), EtcdExtraArgs: Args{ "election-timeout": ArgValue{strValue: "5000"}, }, RootCA: pemEncodedCertificateExample(), } } func clusterEtcdImageExample() string { return (&EtcdConfig{}).Image() } func clusterEtcdAdvertisedSubnetsExample() []string { return []string{"10.0.0.0/8"} } func clusterCoreDNSExample() *CoreDNS { return &CoreDNS{ CoreDNSImage: (&CoreDNS{}).Image(), } } func clusterExternalCloudProviderConfigExample() *ExternalCloudProviderConfig { return &ExternalCloudProviderConfig{ ExternalEnabled: new(true), ExternalManifests: []string{ "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", }, } } func clusterAdminKubeconfigExample() *AdminKubeconfigConfig { return &AdminKubeconfigConfig{ AdminKubeconfigCertLifetime: time.Hour, } } func machineSeccompExample() []*MachineSeccompProfile { return []*MachineSeccompProfile{ { MachineSeccompProfileName: "audit.json", MachineSeccompProfileValue: Unstructured{ Object: map[string]any{ "defaultAction": "SCMP_ACT_LOG", }, }, }, } } func clusterEndpointExample1() *Endpoint { return &Endpoint{ mustParseURL("https://1.2.3.4:6443"), } } func clusterEndpointExample2() *Endpoint { return &Endpoint{ mustParseURL("https://cluster1.internal:6443"), } } func kubeletExtraMountsExample() []ExtraMount { return []ExtraMount{ { Source: "/var/lib/example", Destination: "/var/lib/example", Type: "bind", Options: []string{ "bind", "rshared", "rw", }, }, } } func clusterCustomCNIExample() *CNIConfig { return &CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{ "https://docs.projectcalico.org/archive/v3.20/manifests/canal.yaml", }, } } func clusterInlineManifestsExample() ClusterInlineManifests { return ClusterInlineManifests{ { InlineManifestName: "namespace-ci", InlineManifestContents: strings.TrimSpace(` apiVersion: v1 kind: Namespace metadata: name: ci `), }, } } func clusterDiscoveryExample() ClusterDiscoveryConfig { return ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), DiscoveryRegistries: DiscoveryRegistriesConfig{ RegistryService: RegistryServiceConfig{ RegistryEndpoint: constants.DefaultDiscoveryServiceEndpoint, }, }, } } func kubeletNodeIPExample() *KubeletNodeIPConfig { return &KubeletNodeIPConfig{ KubeletNodeIPValidSubnets: []string{ "10.0.0.0/8", "!10.0.0.3/32", "fdc7::/16", }, } } func kubeletExtraConfigExample() Unstructured { return Unstructured{ Object: map[string]any{ "serverTLSBootstrap": true, }, } } func kubeletCredentialProviderConfigExample() Unstructured { return Unstructured{ Object: map[string]any{ "apiVersion": "kubelet.config.k8s.io/v1", "kind": "CredentialProviderConfig", "providers": []any{ map[string]any{ "name": "ecr-credential-provider", "apiVersion": "credentialprovider.kubelet.k8s.io/v1", "matchImages": []any{ "*.dkr.ecr.*.amazonaws.com", "*.dkr.ecr.*.amazonaws.com.cn", "*.dkr.ecr-fips.*.amazonaws.com", "*.dkr.ecr.us-iso-east-1.c2s.ic.gov", "*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov", }, "defaultCacheDuration": "12h", }, }, }, } } func loggingEndpointExample1() *Endpoint { return &Endpoint{ mustParseURL("udp://127.0.0.1:12345"), } } func loggingEndpointExample2() *Endpoint { return &Endpoint{ mustParseURL("tcp://1.2.3.4:12345"), } } func machineLoggingExample() LoggingConfig { return LoggingConfig{ LoggingDestinations: []LoggingDestination{ { LoggingEndpoint: loggingEndpointExample2(), LoggingFormat: constants.LoggingFormatJSONLines, }, }, } } func machineKernelExample() *KernelConfig { return &KernelConfig{ KernelModules: []*KernelModuleConfig{ { ModuleName: "btrfs", }, }, } } func machinePodsExample() []Unstructured { return []Unstructured{ { Object: map[string]any{ "apiVersion": "v1", "kind": "pod", "metadata": map[string]any{ "name": "nginx", }, "spec": map[string]any{ "containers": []any{ map[string]any{ "name": "nginx", "image": "nginx", }, }, }, }, }, } } func admissionControlConfigExample() []*AdmissionPluginConfig { return []*AdmissionPluginConfig{ { PluginName: "PodSecurity", PluginConfiguration: Unstructured{ Object: map[string]any{ "apiVersion": "pod-security.admission.config.k8s.io/v1alpha1", "kind": "PodSecurityConfiguration", "defaults": map[string]any{ "enforce": "baseline", "enforce-version": "latest", "audit": "restricted", "audit-version": "latest", "warn": "restricted", "warn-version": "latest", }, "exemptions": map[string]any{ "usernames": []any{}, "runtimeClasses": []any{}, "namespaces": []any{"kube-system"}, }, }, }, }, } } func authorizationConfigExample() []*AuthorizationConfigAuthorizerConfig { return []*AuthorizationConfigAuthorizerConfig{ { AuthorizerType: "Webhook", AuthorizerName: "webhook", AuthorizerWebhook: Unstructured{ Object: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "Deny", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, "matchConditions": []map[string]any{ { "expression": "has(request.resourceAttributes)", }, { "expression": "!(\\'system:serviceaccounts:kube-system\\' in request.groups)", }, }, }, }, }, { AuthorizerType: "Webhook", AuthorizerName: "in-cluster-authorizer", AuthorizerWebhook: Unstructured{ Object: map[string]any{ "timeout": "3s", "subjectAccessReviewVersion": "v1", "matchConditionSubjectAccessReviewVersion": "v1", "failurePolicy": "NoOpinion", "connectionInfo": map[string]any{ "type": "InClusterConfig", }, }, }, }, } } func kubernetesTalosAPIAccessConfigExample() *KubernetesTalosAPIAccessConfig { return &KubernetesTalosAPIAccessConfig{ AccessEnabled: new(true), AccessAllowedRoles: []string{ "os:reader", }, AccessAllowedKubernetesNamespaces: []string{ "kube-system", }, } } func machineBaseRuntimeSpecOverridesExample() Unstructured { return Unstructured{ Object: map[string]any{ "process": map[string]any{ "rlimits": []map[string]any{ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024, }, }, }, }, } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_externalcloudproviderconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import "github.com/siderolabs/go-pointer" // Enabled implements the config.ExternalCloudProvider interface. func (ecp *ExternalCloudProviderConfig) Enabled() bool { return pointer.SafeDeref(ecp.ExternalEnabled) } // ManifestURLs implements the config.ExternalCloudProvider interface. func (ecp *ExternalCloudProviderConfig) ManifestURLs() []string { return ecp.ExternalManifests } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_features.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // KubernetesTalosAPIAccess implements config.Features interface. func (f *FeaturesConfig) KubernetesTalosAPIAccess() config.KubernetesTalosAPIAccess { return f.KubernetesTalosAPIAccessConfig } // DiskQuotaSupportEnabled implements config.Features interface. func (f *FeaturesConfig) DiskQuotaSupportEnabled() bool { return pointer.SafeDeref(f.DiskQuotaSupport) } // HostDNS implements config.Features interface. func (f *FeaturesConfig) HostDNS() config.HostDNS { if f.HostDNSSupport == nil { return &HostDNSConfig{} } return f.HostDNSSupport } // KubePrism implements config.Features interface. func (f *FeaturesConfig) KubePrism() config.KubePrism { if f.KubePrismSupport == nil { return &KubePrism{} } return f.KubePrismSupport } // ImageCache implements config.Features interface. func (f *FeaturesConfig) ImageCache() config.ImageCache { if f.ImageCacheSupport == nil { return &ImageCacheConfig{} } return f.ImageCacheSupport } // NodeAddressSortAlgorithm implements config.Features interface. func (f *FeaturesConfig) NodeAddressSortAlgorithm() nethelpers.AddressSortAlgorithm { if f.FeatureNodeAddressSortAlgorithm == "" { return nethelpers.AddressSortAlgorithmV1 } res, err := nethelpers.AddressSortAlgorithmString(f.FeatureNodeAddressSortAlgorithm) if err != nil { return nethelpers.AddressSortAlgorithmV1 } return res } const defaultKubePrismPort = 7445 // Enabled implements [config.KubePrism]. func (a *KubePrism) Enabled() bool { return pointer.SafeDeref(a.ServerEnabled) } // Port implements [config.KubePrism]. func (a *KubePrism) Port() int { if a.ServerPort == 0 { return defaultKubePrismPort } return a.ServerPort } // Enabled implements config.HostDNS. func (h *HostDNSConfig) Enabled() bool { return pointer.SafeDeref(h.HostDNSEnabled) } // ForwardKubeDNSToHost implements config.HostDNS. func (h *HostDNSConfig) ForwardKubeDNSToHost() bool { return pointer.SafeDeref(h.HostDNSForwardKubeDNSToHost) } // ResolveMemberNames implements config.HostDNS. func (h *HostDNSConfig) ResolveMemberNames() bool { return pointer.SafeDeref(h.HostDNSResolveMemberNames) } // LocalEnabled implements config.ImageCache. func (i *ImageCacheConfig) LocalEnabled() bool { return pointer.SafeDeref(i.CacheLocalEnabled) } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_inlinemanifest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 // Name implements the config.InlineManifest interface. func (m ClusterInlineManifest) Name() string { return m.InlineManifestName } // Contents implements the config.InlineManifest interface. func (m ClusterInlineManifest) Contents() string { return m.InlineManifestContents } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_kernel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" ) // Modules implements config.Kernel interface. func (kc *KernelConfig) Modules() []config.KernelModule { return xslices.Map(kc.KernelModules, func(kmc *KernelModuleConfig) config.KernelModule { return kmc }) } // Name implements config.KernelModule interface. func (kmc *KernelModuleConfig) Name() string { return kmc.ModuleName } // Parameters implements config.KernelModule interface. func (kmc *KernelModuleConfig) Parameters() []string { return kmc.ModuleParameters } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_kubernetestalosapiaccess.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import "github.com/siderolabs/go-pointer" // Enabled implements config.KubernetesTalosAPIAccess. func (c *KubernetesTalosAPIAccessConfig) Enabled() bool { if c == nil { return false } return pointer.SafeDeref(c.AccessEnabled) } // AllowedRoles implements config.KubernetesTalosAPIAccess. func (c *KubernetesTalosAPIAccessConfig) AllowedRoles() []string { if c == nil { return nil } return c.AccessAllowedRoles } // AllowedKubernetesNamespaces implements config.KubernetesTalosAPIAccess. func (c *KubernetesTalosAPIAccessConfig) AllowedKubernetesNamespaces() []string { if c == nil { return nil } return c.AccessAllowedKubernetesNamespaces } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_logging.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "errors" "fmt" "net/url" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Validate checks logging configuration for errors. func (lc *LoggingConfig) Validate() error { var errs *multierror.Error for _, dest := range lc.LoggingDestinations { var endpoint *url.URL if dest.LoggingEndpoint != nil && dest.LoggingEndpoint.URL != nil { endpoint = dest.LoggingEndpoint.URL } if endpoint == nil { errs = multierror.Append(errs, errors.New("empty logging endpoint")) } else { if endpoint.Host == "" { errs = multierror.Append(errs, errors.New("empty logging endpoint's host")) } if endpoint.Scheme != "tcp" && endpoint.Scheme != "udp" { errs = multierror.Append(errs, fmt.Errorf("unexpected logging endpoint scheme %q", endpoint.Scheme)) } } switch f := dest.LoggingFormat; f { case constants.LoggingFormatJSONLines: // nothing default: errs = multierror.Append(errs, fmt.Errorf("unknown logging format %q", f)) } } return errs.ErrorOrNil() } // Destinations implements config.Logging interface. func (lc *LoggingConfig) Destinations() []config.LoggingDestination { return xslices.Map(lc.LoggingDestinations, func(ld LoggingDestination) config.LoggingDestination { return ld }) } // Endpoint implements config.LoggingDestination interface. func (ld LoggingDestination) Endpoint() *url.URL { return ld.LoggingEndpoint.URL } // ExtraTags implements config.LoggingDestination interface. func (ld LoggingDestination) ExtraTags() map[string]string { return ld.LoggingExtraTags } // Format implements config.LoggingDestination interface. func (ld LoggingDestination) Format() string { return ld.LoggingFormat } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_marshal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "encoding/base64" ) // Base64Bytes implements YAML marshaling/unmarshaling via base64 encoding. type Base64Bytes []byte // UnmarshalYAML implements the yaml.Unmarshaler interface. func (b *Base64Bytes) UnmarshalYAML(unmarshal func(any) error) error { var data string if err := unmarshal(&data); err != nil { return err } decoded, err := base64.StdEncoding.DecodeString(data) if err != nil { return err } *b = decoded return nil } // MarshalYAML implements the yaml.Marshaler interface. func (b Base64Bytes) MarshalYAML() (any, error) { return base64.StdEncoding.EncodeToString(b), nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_marshal_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" yaml "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestBase64Bytes(t *testing.T) { // docgen: nodoc type test struct { CA v1alpha1.Base64Bytes `yaml:"ca,omitempty"` } input := test{ CA: []byte{0xde, 0xad, 0xbe, 0xef}, } out, err := yaml.Marshal(&input) require.NoError(t, err) assert.Equal(t, "ca: 3q2+7w==\n", string(out)) var decoded test require.NoError(t, yaml.Unmarshal(out, &decoded)) assert.Equal(t, input.CA, decoded.CA) } func TestDiskSizeMatcherUnmarshal(t *testing.T) { obj := struct { M *v1alpha1.InstallDiskSizeMatcher `yaml:"m"` }{ M: &v1alpha1.InstallDiskSizeMatcher{}, } for _, test := range []struct { condition string size string match bool err bool }{ { condition: "<= 256GB", size: "200GB", match: true, }, { condition: ">= 256GB", size: "200GB", match: false, }, { condition: "<256GB", size: "256GB", match: false, }, { condition: ">256GB", size: "256GB", match: false, }, { condition: ">256GB", size: "257GB", match: true, }, { condition: "==256GB", size: "256GB", match: true, }, { condition: "==256GB", size: "257GB", match: false, }, { condition: "== 256GB", size: "256GB", match: true, }, { condition: "256GB", size: "256GB", match: true, }, { condition: " 256GB", size: "256GB", match: true, }, { condition: "9a256GB", err: true, }, { condition: "--256GB", err: true, }, { condition: "<<256GB", err: true, }, { condition: ">1", size: "1GB", match: true, }, { condition: "< 1", size: "1GB", match: false, }, } { err := yaml.Unmarshal(fmt.Appendf(nil, "m: '%s'\n", test.condition), &obj) if test.err { require.Error(t, err) } else { require.NoError(t, err) } } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_network_bridge.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "net/netip" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // This file contains methods which bridge v1alpha1 (legacy) config types to new-style config interfaces for networking. // NetworkStaticHostConfig implements config.NetworkStaticHostConfig interface. func (c *Config) NetworkStaticHostConfig() []config.NetworkStaticHostConfig { if c == nil || c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil { return nil } return c.MachineConfig.MachineNetwork.ExtraHosts() } // Hostname implements config.NetworkHostnameConfig interface. func (c *Config) Hostname() string { if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil { return "" } return c.MachineConfig.MachineNetwork.NetworkHostname } // AutoHostname implements config.NetworkHostnameConfig interface. func (c *Config) AutoHostname() nethelpers.AutoHostnameKind { if c.MachineConfig == nil || c.MachineConfig.MachineFeatures == nil { // legacy mode return nethelpers.AutoHostnameKindAddr } if pointer.SafeDeref(c.MachineConfig.MachineFeatures.StableHostname) { return nethelpers.AutoHostnameKindStable } return nethelpers.AutoHostnameKindAddr } // Resolvers implements config.NetworkResolverConfig interface. func (c *Config) Resolvers() []netip.Addr { if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil { return nil } var result []netip.Addr for _, r := range c.MachineConfig.MachineNetwork.NameServers { if addr, err := netip.ParseAddr(r); err == nil { result = append(result, addr) } } return result } // SearchDomains implements config.NetworkResolverConfig interface. func (c *Config) SearchDomains() []string { if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil { return nil } return c.MachineConfig.MachineNetwork.Searches } // DisableSearchDomain implements config.NetworkResolverConfig interface. func (c *Config) DisableSearchDomain() bool { if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil { return false } return pointer.SafeDeref(c.MachineConfig.MachineNetwork.NetworkDisableSearchDomain) } // NetworkTimeSyncConfig implements config.NetworkTimeSyncConfig interface. func (c *Config) NetworkTimeSyncConfig() config.NetworkTimeSyncConfig { if c.MachineConfig == nil || c.MachineConfig.MachineTime == nil { return nil } return c.MachineConfig.MachineTime } // NetworkKubeSpanConfig implements the config.NetworkKubeSpanConfig interface. func (c *Config) NetworkKubeSpanConfig() config.NetworkKubeSpanConfig { if c.MachineConfig == nil || c.MachineConfig.MachineNetwork == nil || c.MachineConfig.MachineNetwork.NetworkKubeSpan == nil { return nil } return c.MachineConfig.MachineNetwork.NetworkKubeSpan } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_network_bridge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // These tests ensure that v1alpha1 types properly implement new-style config interfaces. func TestStaticHostsBridging(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(*testing.T) config.Config }{ { name: "v1alpha1 only", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ ExtraHostEntries: []*v1alpha1.ExtraHost{ { HostIP: "10.5.0.2", HostAliases: []string{ "example.com", "example", }, }, { HostIP: "10.5.0.3", HostAliases: []string{ "my-machine", }, }, { HostIP: "2001:db8::1", HostAliases: []string{ "ipv6-host", }, }, }, }, }, }) }, }, { name: "new style only", cfg: func(*testing.T) config.Config { host1 := network.NewStaticHostConfigV1Alpha1("10.5.0.2") host1.Hostnames = []string{"example.com", "example"} host2 := network.NewStaticHostConfigV1Alpha1("10.5.0.3") host2.Hostnames = []string{"my-machine"} host3 := network.NewStaticHostConfigV1Alpha1("2001:db8::1") host3.Hostnames = []string{"ipv6-host"} c, err := container.New( host1, host2, host3, ) require.NoError(t, err) return c }, }, { name: "mixed", cfg: func(*testing.T) config.Config { host2 := network.NewStaticHostConfigV1Alpha1("10.5.0.3") host2.Hostnames = []string{"my-machine"} host3 := network.NewStaticHostConfigV1Alpha1("2001:db8::1") host3.Hostnames = []string{"ipv6-host"} c, err := container.New( host2, host3, &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ ExtraHostEntries: []*v1alpha1.ExtraHost{ { HostIP: "10.5.0.2", HostAliases: []string{ "example.com", "example", }, }, }, }, }, }, ) require.NoError(t, err) return c }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) staticHosts := cfg.NetworkStaticHostConfig() require.Len(t, staticHosts, 3) assert.Equal(t, "10.5.0.2", staticHosts[0].IP()) assert.Equal(t, []string{"example.com", "example"}, staticHosts[0].Aliases()) assert.Equal(t, "10.5.0.3", staticHosts[1].IP()) assert.Equal(t, []string{"my-machine"}, staticHosts[1].Aliases()) assert.Equal(t, "2001:db8::1", staticHosts[2].IP()) assert.Equal(t, []string{"ipv6-host"}, staticHosts[2].Aliases()) }) } } func TestHostnameBridging(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(*testing.T) config.Config expectedHostname string expectedAutoHostname nethelpers.AutoHostnameKind }{ { name: "v1alpha1 only", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ NetworkHostname: "my-machine", }, MachineFeatures: &v1alpha1.FeaturesConfig{ StableHostname: new(true), }, }, }) }, expectedHostname: "my-machine", expectedAutoHostname: nethelpers.AutoHostnameKindStable, }, { name: "v1alpha1 empty", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{}, }) }, expectedHostname: "", expectedAutoHostname: nethelpers.AutoHostnameKindAddr, }, { name: "new style only", cfg: func(*testing.T) config.Config { hc := network.NewHostnameConfigV1Alpha1() hc.ConfigHostname = "my-machine" c, err := container.New( hc, ) require.NoError(t, err) return c }, expectedHostname: "my-machine", expectedAutoHostname: nethelpers.AutoHostnameKindOff, }, { name: "mixed", cfg: func(*testing.T) config.Config { hc := network.NewHostnameConfigV1Alpha1() hc.ConfigAuto = new(nethelpers.AutoHostnameKindStable) c, err := container.New( hc, &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{}, }, }, ) require.NoError(t, err) return c }, expectedHostname: "", expectedAutoHostname: nethelpers.AutoHostnameKindStable, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) hostnameConfig := cfg.NetworkHostnameConfig() require.NotNil(t, hostnameConfig) assert.Equal(t, test.expectedHostname, hostnameConfig.Hostname()) assert.Equal(t, test.expectedAutoHostname, hostnameConfig.AutoHostname()) }) } } func TestResolverBridging(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(*testing.T) config.Config expectedNameservers []netip.Addr expectedSearchDomains []string expectedDisableSearch bool }{ { name: "v1alpha1 only", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ NameServers: []string{"2.2.2.2", "3.3.3.3"}, Searches: []string{"universe.com", "galaxy.org"}, NetworkDisableSearchDomain: new(true), }, }, }) }, expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")}, expectedSearchDomains: []string{"universe.com", "galaxy.org"}, expectedDisableSearch: true, }, { name: "v1alpha1 empty", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{}, }) }, expectedNameservers: nil, expectedSearchDomains: nil, expectedDisableSearch: false, }, { name: "new style only", cfg: func(*testing.T) config.Config { rc := network.NewResolverConfigV1Alpha1() rc.ResolverNameservers = []network.NameserverConfig{ { Address: network.Addr{Addr: netip.MustParseAddr("2.2.2.2")}, }, { Address: network.Addr{Addr: netip.MustParseAddr("3.3.3.3")}, }, } rc.ResolverSearchDomains = network.SearchDomainsConfig{ SearchDomains: []string{"universe.com", "galaxy.org"}, SearchDisableDefault: new(true), } c, err := container.New( rc, ) require.NoError(t, err) return c }, expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")}, expectedSearchDomains: []string{"universe.com", "galaxy.org"}, expectedDisableSearch: true, }, { name: "mixed", cfg: func(*testing.T) config.Config { rc := network.NewResolverConfigV1Alpha1() rc.ResolverNameservers = []network.NameserverConfig{ { Address: network.Addr{Addr: netip.MustParseAddr("2.2.2.2")}, }, { Address: network.Addr{Addr: netip.MustParseAddr("3.3.3.3")}, }, } c, err := container.New( rc, &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{}, }, }, ) require.NoError(t, err) return c }, expectedNameservers: []netip.Addr{netip.MustParseAddr("2.2.2.2"), netip.MustParseAddr("3.3.3.3")}, expectedSearchDomains: nil, expectedDisableSearch: false, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) resolverConfig := cfg.NetworkResolverConfig() require.NotNil(t, resolverConfig) assert.Equal(t, test.expectedNameservers, resolverConfig.Resolvers()) assert.Equal(t, test.expectedSearchDomains, resolverConfig.SearchDomains()) assert.Equal(t, test.expectedDisableSearch, resolverConfig.DisableSearchDomain()) }) } } func TestTimeSyncBridging(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(*testing.T) config.Config expectedNil bool expectedDisabled bool expectedTimeservers []string expectedBootTimeout time.Duration }{ { name: "v1alpha1 only", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineTime: &v1alpha1.TimeConfig{ TimeDisabled: new(true), TimeServers: []string{"time1.example.com", "time2.example.com"}, TimeBootTimeout: 30 * time.Second, }, }, }) }, expectedDisabled: true, expectedTimeservers: []string{"time1.example.com", "time2.example.com"}, expectedBootTimeout: 30 * time.Second, }, { name: "v1alpha1 empty", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{}, }) }, expectedNil: true, }, { name: "new style only", cfg: func(*testing.T) config.Config { tsc := network.NewTimeSyncConfigV1Alpha1() tsc.TimeBootTimeout = 10 * time.Second tsc.TimeNTP = &network.NTPConfig{ Servers: []string{"time1.example.com", "time2.example.com"}, } c, err := container.New( tsc, ) require.NoError(t, err) return c }, expectedDisabled: false, expectedTimeservers: []string{"time1.example.com", "time2.example.com"}, expectedBootTimeout: 10 * time.Second, }, { name: "mixed", cfg: func(*testing.T) config.Config { tsc := network.NewTimeSyncConfigV1Alpha1() tsc.TimeBootTimeout = 10 * time.Second tsc.TimeNTP = &network.NTPConfig{ Servers: []string{"time1.example.com", "time2.example.com"}, } c, err := container.New( tsc, &v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{}, }, }, ) require.NoError(t, err) return c }, expectedDisabled: false, expectedTimeservers: []string{"time1.example.com", "time2.example.com"}, expectedBootTimeout: 10 * time.Second, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) timesyncConfig := cfg.NetworkTimeSyncConfig() if test.expectedNil { require.Nil(t, timesyncConfig) return } require.NotNil(t, timesyncConfig) assert.Equal(t, test.expectedTimeservers, timesyncConfig.Servers()) assert.Equal(t, test.expectedDisabled, timesyncConfig.Disabled()) assert.Equal(t, test.expectedBootTimeout, timesyncConfig.BootTimeout()) }) } } func TestKubeSpanBridging(t *testing.T) { t.Parallel() for _, test := range []struct { name string cfg func(*testing.T) config.Config expectedKubeSpanExists bool expectedKubeSpanEnabled bool }{ { name: "v1alpha1 only", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{ MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), }, }, }, }) }, expectedKubeSpanExists: true, expectedKubeSpanEnabled: true, }, { name: "v1alpha1 empty", cfg: func(*testing.T) config.Config { return container.NewV1Alpha1(&v1alpha1.Config{ MachineConfig: &v1alpha1.MachineConfig{}, }) }, expectedKubeSpanExists: false, expectedKubeSpanEnabled: false, }, { name: "new style only", cfg: func(*testing.T) config.Config { kc := network.NewKubeSpanV1Alpha1() kc.ConfigEnabled = new(true) c, err := container.New( kc, ) require.NoError(t, err) return c }, expectedKubeSpanExists: true, expectedKubeSpanEnabled: true, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() cfg := test.cfg(t) kubespanConfig := cfg.NetworkKubeSpanConfig() if !test.expectedKubeSpanExists { require.Nil(t, kubespanConfig) return } require.NotNil(t, kubespanConfig) assert.Equal(t, test.expectedKubeSpanEnabled, kubespanConfig.Enabled()) }) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_network_options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // NetworkConfigOption generates NetworkConfig. type NetworkConfigOption func(machine.Type, *NetworkConfig) error // WithNetworkConfig sets whole network config structure, overwrites any previous options. func WithNetworkConfig(c *NetworkConfig) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { *cfg = *c return nil } } // WithNetworkNameservers sets global nameservers list. func WithNetworkNameservers(nameservers ...string) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.NameServers = append(cfg.NameServers, nameservers...) return nil } } // IfaceSelector is a helper type to select network interface. // // It might either to select interface by name or by selector. type IfaceSelector struct { Name *string Selector *NetworkDeviceSelector } // matches checks if Device matches selector. func (selector IfaceSelector) matches(dev *Device) bool { if selector.Name != nil && *selector.Name == dev.DeviceInterface { return true } if selector.Selector != nil && dev.DeviceSelector != nil && *selector.Selector == *dev.DeviceSelector { return true } return false } // new returns new Device with selector. func (selector IfaceSelector) new() *Device { dev := &Device{} if selector.Name != nil { dev.DeviceInterface = *selector.Name } if selector.Selector != nil { dev.DeviceSelector = selector.Selector } return dev } // IfaceByName selects interface by name. func IfaceByName(name string) IfaceSelector { return IfaceSelector{ Name: &name, } } // IfaceBySelector selects interface by selector. func IfaceBySelector(selector NetworkDeviceSelector) IfaceSelector { return IfaceSelector{ Selector: &selector, } } // WithNetworkInterfaceIgnore marks interface as ignored. func WithNetworkInterfaceIgnore(iface IfaceSelector) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.getDevice(iface).DeviceIgnore = new(true) return nil } } // WithNetworkInterfaceDHCP enables DHCP for the interface. func WithNetworkInterfaceDHCP(iface IfaceSelector, enable bool) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.getDevice(iface).DeviceDHCP = new(true) return nil } } // WithNetworkInterfaceDHCPv4 enables DHCPv4 for the interface. func WithNetworkInterfaceDHCPv4(iface IfaceSelector, enable bool) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { dev := cfg.getDevice(iface) if dev.DeviceDHCPOptions == nil { dev.DeviceDHCPOptions = &DHCPOptions{} } dev.DeviceDHCPOptions.DHCPIPv4 = new(enable) return nil } } // WithNetworkInterfaceDHCPv6 enables DHCPv6 for the interface. func WithNetworkInterfaceDHCPv6(iface IfaceSelector, enable bool) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { dev := cfg.getDevice(iface) if dev.DeviceDHCPOptions == nil { dev.DeviceDHCPOptions = &DHCPOptions{} } dev.DeviceDHCPOptions.DHCPIPv6 = new(enable) return nil } } // WithNetworkInterfaceCIDR configures interface for static addressing. func WithNetworkInterfaceCIDR(iface IfaceSelector, cidr string) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.getDevice(iface).DeviceAddresses = append(cfg.getDevice(iface).DeviceAddresses, cidr) return nil } } // WithNetworkInterfaceMTU configures interface MTU. func WithNetworkInterfaceMTU(iface IfaceSelector, mtu int) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.getDevice(iface).DeviceMTU = mtu return nil } } // WithNetworkInterfaceWireguard configures interface for Wireguard. func WithNetworkInterfaceWireguard(iface IfaceSelector, wireguardConfig *DeviceWireguardConfig) NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { cfg.getDevice(iface).DeviceWireguardConfig = wireguardConfig return nil } } // WithNetworkInterfaceVirtualIP configures interface for Virtual IP. func WithNetworkInterfaceVirtualIP(iface IfaceSelector, cidr string) NetworkConfigOption { return func(machineType machine.Type, cfg *NetworkConfig) error { if machineType == machine.TypeWorker { return nil } cfg.getDevice(iface).DeviceVIPConfig = &DeviceVIPConfig{ SharedIP: cidr, } return nil } } // WithKubeSpan configures a KubeSpan interface. // // Deprecated: use generate.WithKubeSpanEnabled option instead. func WithKubeSpan() NetworkConfigOption { return func(_ machine.Type, cfg *NetworkConfig) error { if cfg.NetworkKubeSpan == nil { cfg.NetworkKubeSpan = &NetworkKubeSpan{} } cfg.NetworkKubeSpan.KubeSpanEnabled = new(true) return nil } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_provider.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "net/netip" "os" "slices" "strings" "time" "github.com/google/cel-go/common/ast" "github.com/google/cel-go/common/operators" "github.com/google/cel-go/common/types" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) // Verify interfaces. var ( _ config.Document = (*Config)(nil) _ config.SecretDocument = (*Config)(nil) _ config.Validator = (*Config)(nil) ) const ( // Version is the version string for v1alpha1. Version = "v1alpha1" ) // Clone implements config.Document interface. func (c *Config) Clone() config.Document { return c.DeepCopy() } // Kind returns the kind of the document. func (c *Config) Kind() string { return Version // legacy document } // APIVersion returns the API version of the document. func (c *Config) APIVersion() string { return "" // legacy document } // Debug implements the config.Provider interface. func (c *Config) Debug() bool { if c == nil { return false } return pointer.SafeDeref(c.ConfigDebug) } // Machine implements the config.Provider interface. func (c *Config) Machine() config.MachineConfig { if c == nil || c.MachineConfig == nil { return &MachineConfig{} } return c.MachineConfig } // SeccompProfiles implements the config.Provider interface. func (m *MachineConfig) SeccompProfiles() []config.SeccompProfile { return xslices.Map(m.MachineSeccompProfiles, func(m *MachineSeccompProfile) config.SeccompProfile { return m }) } // Name implements the config.Provider interface. func (m *MachineSeccompProfile) Name() string { return m.MachineSeccompProfileName } // Value implements the config.Provider interface. func (m *MachineSeccompProfile) Value() map[string]any { return m.MachineSeccompProfileValue.Object } // NodeLabels implements the config.Provider interface. func (m *MachineConfig) NodeLabels() config.NodeLabels { return m.MachineNodeLabels } // NodeAnnotations implements the config.Provider interface. func (m *MachineConfig) NodeAnnotations() config.NodeAnnotations { return m.MachineNodeAnnotations } // NodeTaints implements the config.Provider interface. func (m *MachineConfig) NodeTaints() config.NodeTaints { return m.MachineNodeTaints } // BaseRuntimeSpecOverrides implements the config.Provider interface. func (m *MachineConfig) BaseRuntimeSpecOverrides() map[string]any { return m.MachineBaseRuntimeSpecOverrides.Object } // Cluster implements the config.Provider interface. func (c *Config) Cluster() config.ClusterConfig { if c == nil || c.ClusterConfig == nil { return &ClusterConfig{} } return c.ClusterConfig } // Redact implements the config.SecretDocument interface. // //nolint:gocyclo func (c *Config) Redact(replacement string) { if c == nil { return } redactBytes := func(b []byte) []byte { if len(b) == 0 { return b } return []byte(replacement) } redactStr := func(s string) string { return string(redactBytes([]byte(s))) } if c.MachineConfig != nil { c.MachineConfig.MachineToken = redactStr(c.MachineConfig.MachineToken) if c.MachineConfig.MachineCA != nil { c.MachineConfig.MachineCA.Key = redactBytes(c.MachineConfig.MachineCA.Key) } } if c.ClusterConfig != nil { c.ClusterConfig.ClusterSecret = redactStr(c.ClusterConfig.ClusterSecret) c.ClusterConfig.BootstrapToken = redactStr(c.ClusterConfig.BootstrapToken) c.ClusterConfig.ClusterAESCBCEncryptionSecret = redactStr(c.ClusterConfig.ClusterAESCBCEncryptionSecret) c.ClusterConfig.ClusterSecretboxEncryptionSecret = redactStr(c.ClusterConfig.ClusterSecretboxEncryptionSecret) if c.ClusterConfig.ClusterServiceAccount != nil { c.ClusterConfig.ClusterServiceAccount.Key = redactBytes(c.ClusterConfig.ClusterServiceAccount.Key) } if c.ClusterConfig.ClusterCA != nil { c.ClusterConfig.ClusterCA.Key = redactBytes(c.ClusterConfig.ClusterCA.Key) } if c.ClusterConfig.ClusterAggregatorCA != nil { c.ClusterConfig.ClusterAggregatorCA.Key = redactBytes(c.ClusterConfig.ClusterAggregatorCA.Key) } if c.ClusterConfig.EtcdConfig != nil && c.ClusterConfig.EtcdConfig.RootCA != nil { c.ClusterConfig.EtcdConfig.RootCA.Key = redactBytes(c.ClusterConfig.EtcdConfig.RootCA.Key) } } } // Install implements the config.Provider interface. func (m *MachineConfig) Install() config.Install { if m.MachineInstall == nil { return &InstallConfig{} } return m.MachineInstall } // Security implements the config.Provider interface. func (m *MachineConfig) Security() config.Security { return m } // Disks implements the config.Provider interface. func (m *MachineConfig) Disks() []config.Disk { return xslices.Map(m.MachineDisks, func(d *MachineDisk) config.Disk { return d }) } // Network implements the config.Provider interface. func (m *MachineConfig) Network() config.MachineNetwork { if m.MachineNetwork == nil { return &NetworkConfig{} } return m.MachineNetwork } // Controlplane implements the config.Provider interface. func (m *MachineConfig) Controlplane() config.MachineControlPlane { if m.MachineControlPlane == nil { return &MachineControlPlaneConfig{} } return m.MachineControlPlane } // Pods implements the config.Provider interface. func (m *MachineConfig) Pods() []map[string]any { return xslices.Map(m.MachinePods, func(u Unstructured) map[string]any { return u.Object }) } // ControllerManager implements the config.Provider interface. func (m *MachineControlPlaneConfig) ControllerManager() config.MachineControllerManager { if m.MachineControllerManager == nil { return &MachineControllerManagerConfig{} } return m.MachineControllerManager } // Scheduler implements the config.Provider interface. func (m *MachineControlPlaneConfig) Scheduler() config.MachineScheduler { if m.MachineScheduler == nil { return &MachineSchedulerConfig{} } return m.MachineScheduler } // Disabled implements the config.Provider interface. func (m *MachineControllerManagerConfig) Disabled() bool { return pointer.SafeDeref(m.MachineControllerManagerDisabled) } // Disabled implements the config.Provider interface. func (m *MachineSchedulerConfig) Disabled() bool { return pointer.SafeDeref(m.MachineSchedulerDisabled) } // Kubelet implements the config.Provider interface. func (m *MachineConfig) Kubelet() config.Kubelet { if m.MachineKubelet == nil { return &KubeletConfig{} } return m.MachineKubelet } // Env implements the config.Provider interface. func (m *MachineConfig) Env() config.Env { return m.MachineEnv } // Files implements the config.Provider interface. func (m *MachineConfig) Files() ([]config.File, error) { return xslices.Map(m.MachineFiles, func(f *MachineFile) config.File { return f }), nil } // Type implements the config.Provider interface. func (m *MachineConfig) Type() machine.Type { t, _ := machine.ParseType(m.MachineType) //nolint:errcheck return t } // Server implements the config.Provider interface. func (m *MachineConfig) Server() string { return "" } // Sysctls implements the config.Provider interface. func (m *MachineConfig) Sysctls() map[string]string { if m.MachineSysctls == nil { return make(map[string]string) } return m.MachineSysctls } // Sysfs implements the config.Provider interface. func (m *MachineConfig) Sysfs() map[string]string { if m.MachineSysfs == nil { return make(map[string]string) } return m.MachineSysfs } // IssuingCA implements the config.Provider interface. func (m *MachineConfig) IssuingCA() *x509.PEMEncodedCertificateAndKey { return m.MachineCA } // AcceptedCAs implements the config.Provider interface. func (m *MachineConfig) AcceptedCAs() []*x509.PEMEncodedCertificate { return slices.Clone(m.MachineAcceptedCAs) } // Token implements the config.Provider interface. func (m *MachineConfig) Token() string { return m.MachineToken } // CertSANs implements the config.Provider interface. func (m *MachineConfig) CertSANs() []string { return m.MachineCertSANs } // SystemDiskEncryption implements the config.Provider interface. func (m *MachineConfig) SystemDiskEncryption() config.SystemDiskEncryption { if m.MachineSystemDiskEncryption == nil { return &SystemDiskEncryptionConfig{} } return m.MachineSystemDiskEncryption } // Features implements the config.MachineConfig interface. func (m *MachineConfig) Features() config.Features { if m.MachineFeatures == nil { return &FeaturesConfig{} } return m.MachineFeatures } // Udev implements the config.MachineConfig interface. func (m *MachineConfig) Udev() config.UdevConfig { if m.MachineUdev == nil { return &UdevConfig{} } return m.MachineUdev } // Logging implements the config.MachineConfig interface. func (m *MachineConfig) Logging() config.Logging { if m.MachineLogging == nil { return &LoggingConfig{} } return m.MachineLogging } // Kernel implements the config.MachineConfig interface. func (m *MachineConfig) Kernel() config.Kernel { if m.MachineKernel == nil { return &KernelConfig{} } return m.MachineKernel } // Image implements the config.Provider interface. func (k *KubeletConfig) Image() string { image := k.KubeletImage if image == "" { image = fmt.Sprintf("%s:v%s", constants.KubeletImage, constants.DefaultKubernetesVersion) } return image } // ClusterDNS implements the config.Provider interface. func (k *KubeletConfig) ClusterDNS() []string { if k == nil || k.KubeletClusterDNS == nil { return nil } return k.KubeletClusterDNS } // ExtraArgs implements the config.Provider interface. func (k *KubeletConfig) ExtraArgs() map[string][]string { if k == nil || k.KubeletExtraArgs == nil { return make(map[string][]string) } return k.KubeletExtraArgs.ToMap() } // ExtraMounts implements the config.Provider interface. func (k *KubeletConfig) ExtraMounts() []specs.Mount { // use the intermediate type which is assignable to specs.Mount so that // we can be sure that `specs.Mount` and `Mount` have exactly same fields. // // as in Go []T1 is not assignable to []T2, even if T1 and T2 are assignable, we cannot // use direct conversion of Mount and specs.Mount type mountConverter struct { Destination string Type string Source string Options []string UIDMappings []specs.LinuxIDMapping GIDMappings []specs.LinuxIDMapping } return xslices.Map(k.KubeletExtraMounts, func(m ExtraMount) specs.Mount { return specs.Mount(func() mountConverter { return mountConverter{ Destination: m.Destination, Type: m.Type, Source: m.Source, Options: m.Options, UIDMappings: xslices.Map(m.UIDMappings, func(m LinuxIDMapping) specs.LinuxIDMapping { return specs.LinuxIDMapping(m) }), GIDMappings: xslices.Map(m.GIDMappings, func(m LinuxIDMapping) specs.LinuxIDMapping { return specs.LinuxIDMapping(m) }), } }()) }) } // ExtraConfig implements the config.Provider interface. func (k *KubeletConfig) ExtraConfig() map[string]any { return k.KubeletExtraConfig.Object } // CredentialProviderConfig implements the config.Provider interface. func (k *KubeletConfig) CredentialProviderConfig() map[string]any { return k.KubeletCredentialProviderConfig.Object } // DefaultRuntimeSeccompProfileEnabled implements the config.Provider interface. func (k *KubeletConfig) DefaultRuntimeSeccompProfileEnabled() bool { return pointer.SafeDeref(k.KubeletDefaultRuntimeSeccompProfileEnabled) } // RegisterWithFQDN implements the config.Provider interface. func (k *KubeletConfig) RegisterWithFQDN() bool { return pointer.SafeDeref(k.KubeletRegisterWithFQDN) } // NodeIP implements the config.Provider interface. func (k *KubeletConfig) NodeIP() config.KubeletNodeIP { if k.KubeletNodeIP == nil { return &KubeletNodeIPConfig{} } return k.KubeletNodeIP } // SkipNodeRegistration implements the config.Provider interface. func (k *KubeletConfig) SkipNodeRegistration() bool { return pointer.SafeDeref(k.KubeletSkipNodeRegistration) } // DisableManifestsDirectory implements the KubeletConfig interface. func (k *KubeletConfig) DisableManifestsDirectory() bool { return pointer.SafeDeref(k.KubeletDisableManifestsDirectory) } // ValidSubnets implements the config.Provider interface. func (k *KubeletNodeIPConfig) ValidSubnets() []string { return k.KubeletNodeIPValidSubnets } // RegistryMirrorConfigs returns a map of registry mirror configurations. func (c *Config) RegistryMirrorConfigs() map[string]config.RegistryMirrorConfig { if c == nil || c.MachineConfig == nil { return nil } result := make(map[string]config.RegistryMirrorConfig, len(c.MachineConfig.MachineRegistries.RegistryMirrors)) for k, v := range c.MachineConfig.MachineRegistries.RegistryMirrors { result[k] = v } return result } // RegistryAuthConfigs returns a map of registry authentication configurations. func (c *Config) RegistryAuthConfigs() map[string]config.RegistryAuthConfig { if c == nil || c.MachineConfig == nil { return nil } result := make(map[string]config.RegistryAuthConfig, len(c.MachineConfig.MachineRegistries.RegistryConfig)) for k, v := range c.MachineConfig.MachineRegistries.RegistryConfig { if v.RegistryAuth == nil { continue } result[k] = v.RegistryAuth } return result } // RegistryTLSConfigs returns a map of registry TLS configurations. func (c *Config) RegistryTLSConfigs() map[string]config.RegistryTLSConfig { if c == nil || c.MachineConfig == nil { return nil } result := make(map[string]config.RegistryTLSConfig, len(c.MachineConfig.MachineRegistries.RegistryConfig)) for k, v := range c.MachineConfig.MachineRegistries.RegistryConfig { if v.RegistryTLS == nil { continue } result[k] = v.RegistryTLS } return result } // ImageVerificationConfigs implements the config.Config interface. // // v1alpha1 config does not support image verification configs in the main document, // they are provided as separate documents. func (c *Config) ImageVerificationConfigs() map[string]config.ImageVerificationConfig { return nil } type registryEndpointWrapper struct { endpoint string overridePath bool } func (wrapper *registryEndpointWrapper) Endpoint() string { return wrapper.endpoint } func (wrapper *registryEndpointWrapper) OverridePath() bool { return wrapper.overridePath } // Mirrors implements the Registries interface. func (r *RegistriesConfig) Mirrors() map[string]config.RegistryMirrorConfig { mirrors := make(map[string]config.RegistryMirrorConfig, len(r.RegistryMirrors)) for k, v := range r.RegistryMirrors { mirrors[k] = v } return mirrors } // Username implements the Registries interface. func (r *RegistryAuthConfig) Username() string { return r.RegistryUsername } // Password implements the Registries interface. func (r *RegistryAuthConfig) Password() string { return r.RegistryPassword } // Auth implements the Registries interface. func (r *RegistryAuthConfig) Auth() string { return r.RegistryAuth } // IdentityToken implements the Registries interface. func (r *RegistryAuthConfig) IdentityToken() string { return r.RegistryIdentityToken } // ClientIdentity implements the Registries interface. func (r *RegistryTLSConfig) ClientIdentity() *x509.PEMEncodedCertificateAndKey { return r.TLSClientIdentity } // CA implements the Registries interface. func (r *RegistryTLSConfig) CA() []byte { return r.TLSCA } // InsecureSkipVerify implements the Registries interface. func (r *RegistryTLSConfig) InsecureSkipVerify() bool { return pointer.SafeDeref(r.TLSInsecureSkipVerify) } // Devices implements the config.Provider interface. func (n *NetworkConfig) Devices() []config.Device { return xslices.Map(n.NetworkInterfaces, func(d *Device) config.Device { return d }) } // getDevice adds or returns existing Device by name. // // This method mutates configuration, but it's only used in config generation. func (n *NetworkConfig) getDevice(iface IfaceSelector) *Device { for _, dev := range n.NetworkInterfaces { if iface.matches(dev) { return dev } } dev := iface.new() n.NetworkInterfaces = append(n.NetworkInterfaces, dev) return dev } // ExtraHosts implements the config.Provider interface. func (n *NetworkConfig) ExtraHosts() []config.NetworkStaticHostConfig { return xslices.Map(n.ExtraHostEntries, func(e *ExtraHost) config.NetworkStaticHostConfig { return e }) } // IP implements the MachineNetwork interface. func (e *ExtraHost) IP() string { return e.HostIP } // Aliases implements the MachineNetwork interface. func (e *ExtraHost) Aliases() []string { return e.HostAliases } // Interface implements the MachineNetwork interface. func (d *Device) Interface() string { return d.DeviceInterface } // Addresses implements the MachineNetwork interface. func (d *Device) Addresses() []string { switch { case len(d.DeviceAddresses) > 0: return slices.Clone(d.DeviceAddresses) case d.DeviceCIDR != "": return []string{d.DeviceCIDR} default: return nil } } // Routes implements the MachineNetwork interface. func (d *Device) Routes() []config.Route { return xslices.Map(d.DeviceRoutes, func(r *Route) config.Route { return r }) } // Bond implements the MachineNetwork interface. func (d *Device) Bond() config.Bond { if d.DeviceBond == nil { return nil } return d.DeviceBond } // Bridge implements the MachineNetwork interface. func (d *Device) Bridge() config.Bridge { if d.DeviceBridge == nil { return nil } return d.DeviceBridge } // BridgePort implements the MachineNetwork interface. func (d *Device) BridgePort() config.BridgePort { if d.DeviceBridgePort == nil { return nil } return d.DeviceBridgePort } // Vlans implements the MachineNetwork interface. func (d *Device) Vlans() []config.Vlan { return xslices.Map(d.DeviceVlans, func(v *Vlan) config.Vlan { return v }) } // MTU implements the MachineNetwork interface. func (d *Device) MTU() int { return d.DeviceMTU } // DHCP implements the MachineNetwork interface. func (d *Device) DHCP() bool { return pointer.SafeDeref(d.DeviceDHCP) } // Ignore implements the MachineNetwork interface. func (d *Device) Ignore() bool { return pointer.SafeDeref(d.DeviceIgnore) } // Dummy implements the MachineNetwork interface. func (d *Device) Dummy() bool { return pointer.SafeDeref(d.DeviceDummy) } // DHCPOptions implements the MachineNetwork interface. func (d *Device) DHCPOptions() config.DHCPOptions { // Default route metric on systemd is 1024. This sets the same. if d.DeviceDHCPOptions == nil { return &DHCPOptions{ DHCPRouteMetric: uint32(0), } } return d.DeviceDHCPOptions } // VIPConfig implements the MachineNetwork interface. func (d *Device) VIPConfig() config.VIPConfig { if d.DeviceVIPConfig == nil { return nil } return d.DeviceVIPConfig } // Selector implements the config.Device interface. func (d *Device) Selector() config.NetworkDeviceSelector { if d.DeviceSelector == nil { return nil } return d.DeviceSelector } // IP implements the config.VIPConfig interface. func (d *DeviceVIPConfig) IP() string { return d.SharedIP } // EquinixMetal implements the config.VIPConfig interface. func (d *DeviceVIPConfig) EquinixMetal() config.VIPEquinixMetal { if d.EquinixMetalConfig == nil { return nil } return d.EquinixMetalConfig } // APIToken implements the config.VIPEquinixMetal interface. func (v *VIPEquinixMetalConfig) APIToken() string { return v.EquinixMetalAPIToken } // HCloud implements the config.VIPConfig interface. func (d *DeviceVIPConfig) HCloud() config.VIPHCloud { if d.HCloudConfig == nil { return nil } return d.HCloudConfig } // APIToken implements the config.VIPHCloud interface. func (v *VIPHCloudConfig) APIToken() string { return v.HCloudAPIToken } // WireguardConfig implements the MachineNetwork interface. func (d *Device) WireguardConfig() config.WireguardConfig { if d.DeviceWireguardConfig == nil { return nil } return d.DeviceWireguardConfig } // RouteMetric implements the DHCPOptions interface. func (d *DHCPOptions) RouteMetric() uint32 { return d.DHCPRouteMetric } // IPv4 implements the DHCPOptions interface. func (d *DHCPOptions) IPv4() bool { if d.DHCPIPv4 == nil { return true } return *d.DHCPIPv4 } // IPv6 implements the DHCPOptions interface. func (d *DHCPOptions) IPv6() bool { if d.DHCPIPv6 == nil { return false } return *d.DHCPIPv6 } // DUIDv6 implements the DHCPOptions interface. func (d *DHCPOptions) DUIDv6() string { return d.DHCPDUIDv6 } // PrivateKey implements the MachineNetwork interface. func (wc *DeviceWireguardConfig) PrivateKey() string { return wc.WireguardPrivateKey } // ListenPort implements the MachineNetwork interface. func (wc *DeviceWireguardConfig) ListenPort() int { return wc.WireguardListenPort } // FirewallMark implements the MachineNetwork interface. func (wc *DeviceWireguardConfig) FirewallMark() int { return wc.WireguardFirewallMark } // Peers implements the MachineNetwork interface. func (wc *DeviceWireguardConfig) Peers() []config.WireguardPeer { return xslices.Map(wc.WireguardPeers, func(p *DeviceWireguardPeer) config.WireguardPeer { return p }) } // PublicKey implements the MachineNetwork interface. func (wd *DeviceWireguardPeer) PublicKey() string { return wd.WireguardPublicKey } // Endpoint implements the MachineNetwork interface. func (wd *DeviceWireguardPeer) Endpoint() string { return wd.WireguardEndpoint } // PersistentKeepaliveInterval implements the MachineNetwork interface. func (wd *DeviceWireguardPeer) PersistentKeepaliveInterval() time.Duration { return wd.WireguardPersistentKeepaliveInterval } // AllowedIPs implements the MachineNetwork interface. func (wd *DeviceWireguardPeer) AllowedIPs() []string { return wd.WireguardAllowedIPs } // Bus implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) Bus() string { return s.NetworkDeviceBus } // HardwareAddress implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) HardwareAddress() string { return s.NetworkDeviceHardwareAddress } // PermanentAddress implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) PermanentAddress() string { return s.NetworkDevicePermanentAddress } // PCIID implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) PCIID() string { return s.NetworkDevicePCIID } // KernelDriver implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) KernelDriver() string { return s.NetworkDeviceKernelDriver } // Physical implements config.NetworkDeviceSelector interface. func (s *NetworkDeviceSelector) Physical() *bool { return s.NetworkDevicePhysical } // Network implements the MachineNetwork interface. func (r *Route) Network() string { return r.RouteNetwork } // Gateway implements the MachineNetwork interface. func (r *Route) Gateway() string { return r.RouteGateway } // Source implements the MachineNetwork interface. func (r *Route) Source() string { return r.RouteSource } // Metric implements the MachineNetwork interface. func (r *Route) Metric() uint32 { return r.RouteMetric } // MTU implements the MachineNetwork interface. func (r *Route) MTU() uint32 { return r.RouteMTU } // Interfaces implements the MachineNetwork interface. func (b *Bond) Interfaces() []string { if b == nil { return nil } return b.BondInterfaces } // Selectors implements the Bond interface. func (b *Bond) Selectors() []config.NetworkDeviceSelector { if b == nil || b.BondDeviceSelectors == nil { return nil } return xslices.Map(b.BondDeviceSelectors, func(d NetworkDeviceSelector) config.NetworkDeviceSelector { return &d }) } // ARPIPTarget implements the MachineNetwork interface. func (b *Bond) ARPIPTarget() []string { if b == nil { return nil } return b.BondARPIPTarget } // Mode implements the MachineNetwork interface. func (b *Bond) Mode() string { return b.BondMode } // HashPolicy implements the MachineNetwork interface. func (b *Bond) HashPolicy() string { return b.BondHashPolicy } // LACPRate implements the MachineNetwork interface. func (b *Bond) LACPRate() string { return b.BondLACPRate } // ADActorSystem implements the MachineNetwork interface. func (b *Bond) ADActorSystem() string { return b.BondADActorSystem } // ARPValidate implements the MachineNetwork interface. func (b *Bond) ARPValidate() string { return b.BondARPValidate } // ARPAllTargets implements the MachineNetwork interface. func (b *Bond) ARPAllTargets() string { return b.BondARPAllTargets } // Primary implements the MachineNetwork interface. func (b *Bond) Primary() string { return b.BondPrimary } // PrimaryReselect implements the MachineNetwork interface. func (b *Bond) PrimaryReselect() string { return b.BondPrimaryReselect } // FailOverMac implements the MachineNetwork interface. func (b *Bond) FailOverMac() string { return b.BondFailOverMac } // ADSelect implements the MachineNetwork interface. func (b *Bond) ADSelect() string { return b.BondADSelect } // MIIMon implements the MachineNetwork interface. func (b *Bond) MIIMon() uint32 { return b.BondMIIMon } // UpDelay implements the MachineNetwork interface. func (b *Bond) UpDelay() uint32 { return b.BondUpDelay } // DownDelay implements the MachineNetwork interface. func (b *Bond) DownDelay() uint32 { return b.BondDownDelay } // ARPInterval implements the MachineNetwork interface. func (b *Bond) ARPInterval() uint32 { return b.BondARPInterval } // ResendIGMP implements the MachineNetwork interface. func (b *Bond) ResendIGMP() uint32 { return b.BondResendIGMP } // MinLinks implements the MachineNetwork interface. func (b *Bond) MinLinks() uint32 { return b.BondMinLinks } // LPInterval implements the MachineNetwork interface. func (b *Bond) LPInterval() uint32 { return b.BondLPInterval } // PacketsPerSlave implements the MachineNetwork interface. func (b *Bond) PacketsPerSlave() uint32 { return b.BondPacketsPerSlave } // NumPeerNotif implements the MachineNetwork interface. func (b *Bond) NumPeerNotif() uint8 { return b.BondNumPeerNotif } // TLBDynamicLB implements the MachineNetwork interface. func (b *Bond) TLBDynamicLB() uint8 { return b.BondTLBDynamicLB } // AllSlavesActive implements the MachineNetwork interface. func (b *Bond) AllSlavesActive() uint8 { return b.BondAllSlavesActive } // UseCarrier implements the MachineNetwork interface. func (b *Bond) UseCarrier() bool { if b.BondUseCarrier == nil { return true } return *b.BondUseCarrier } // ADActorSysPrio implements the MachineNetwork interface. func (b *Bond) ADActorSysPrio() uint16 { return b.BondADActorSysPrio } // ADUserPortKey implements the MachineNetwork interface. func (b *Bond) ADUserPortKey() uint16 { return b.BondADUserPortKey } // PeerNotifyDelay implements the MachineNetwork interface. func (b *Bond) PeerNotifyDelay() uint32 { return b.BondPeerNotifyDelay } // Enabled implements the config.STP interface. func (s *STP) Enabled() bool { if s == nil || s.STPEnabled == nil { return true } return *s.STPEnabled } // FilteringEnabled implements the config.BridgeVLAN interface. func (v *BridgeVLAN) FilteringEnabled() bool { if v == nil { return false } return pointer.SafeDeref(v.BridgeVLANFiltering) } // Interfaces implements the config.Bridge interface. func (b *Bridge) Interfaces() []string { return b.BridgedInterfaces } // STP implements the config.Bridge interface. func (b *Bridge) STP() config.STP { if b.BridgeSTP == nil { return (*STP)(nil) } return b.BridgeSTP } // VLAN implements the config.Bridge interface. func (b *Bridge) VLAN() config.BridgeVLAN { if b.BridgeVLAN == nil { return (*BridgeVLAN)(nil) } return b.BridgeVLAN } // Master implements the config.BridgePort interface. func (b *BridgePort) Master() string { if b == nil { return "" } return b.BridgePortMaster } // Addresses implements the MachineNetwork interface. func (v *Vlan) Addresses() []string { switch { case len(v.VlanAddresses) > 0: return slices.Clone(v.VlanAddresses) case v.VlanCIDR != "": return []string{v.VlanCIDR} default: return nil } } // MTU implements the MachineNetwork interface. func (v *Vlan) MTU() uint32 { return v.VlanMTU } // VIPConfig implements the MachineNetwork interface. func (v *Vlan) VIPConfig() config.VIPConfig { if v.VlanVIP == nil { return nil } return v.VlanVIP } // Routes implements the MachineNetwork interface. func (v *Vlan) Routes() []config.Route { return xslices.Map(v.VlanRoutes, func(r *Route) config.Route { return r }) } // DHCP implements the MachineNetwork interface. func (v *Vlan) DHCP() bool { return pointer.SafeDeref(v.VlanDHCP) } // DHCPOptions implements the MachineNetwork interface. func (v *Vlan) DHCPOptions() config.DHCPOptions { // Default route metric on systemd is 1024. This sets the same. if v.VlanDHCPOptions == nil { return &DHCPOptions{ DHCPRouteMetric: uint32(0), } } return v.VlanDHCPOptions } // ID implements the MachineNetwork interface. func (v *Vlan) ID() uint16 { return v.VlanID } // Mode implements the MachineNetwork interface. func (v *Vlan) Mode() nethelpers.VLANProtocol { return nethelpers.VLANProtocol8021Q } // Enabled implements KubeSpan interface. func (k *NetworkKubeSpan) Enabled() bool { return pointer.SafeDeref(k.KubeSpanEnabled) } // ForceRouting implements KubeSpan interface. func (k *NetworkKubeSpan) ForceRouting() bool { return !pointer.SafeDeref(k.KubeSpanAllowDownPeerBypass) } // AdvertiseKubernetesNetworks implements KubeSpan interface. func (k *NetworkKubeSpan) AdvertiseKubernetesNetworks() bool { return pointer.SafeDeref(k.KubeSpanAdvertiseKubernetesNetworks) } // HarvestExtraEndpoints implements KubeSpan interface. func (k *NetworkKubeSpan) HarvestExtraEndpoints() bool { return pointer.SafeDeref(k.KubeSpanHarvestExtraEndpoints) } // MTU implements the KubeSpan interface. func (k *NetworkKubeSpan) MTU() uint32 { mtu := pointer.SafeDeref(k.KubeSpanMTU) if mtu == 0 { mtu = constants.KubeSpanLinkMTU } return mtu } // Filters implements the NetworkKubeSpanConfig interface. func (k *NetworkKubeSpan) Filters() config.NetworkKubeSpanFilters { if k.KubeSpanFilters == nil { return &KubeSpanFilters{} } return k.KubeSpanFilters } // Endpoints implements the config.KubeSpanFilters interface. func (k *KubeSpanFilters) Endpoints() []string { return k.KubeSpanFiltersEndpoints } // ExcludeAdvertisedNetworks implements the config.KubeSpanFilters interface. func (k *KubeSpanFilters) ExcludeAdvertisedNetworks() []netip.Prefix { if len(k.KubeSpanFiltersExcludeAdvertisedNetworks) == 0 { return nil } result := make([]netip.Prefix, 0, len(k.KubeSpanFiltersExcludeAdvertisedNetworks)) for _, cidrStr := range k.KubeSpanFiltersExcludeAdvertisedNetworks { // prefixes are validated, so for defensive programming, we can ignore errors here. prefix, err := netip.ParsePrefix(cidrStr) if err != nil { continue } result = append(result, prefix) } return result } // Disabled implements the config.Provider interface. func (t *TimeConfig) Disabled() bool { return pointer.SafeDeref(t.TimeDisabled) } // Servers implements the config.Provider interface. func (t *TimeConfig) Servers() []string { return t.TimeServers } // BootTimeout implements the config.Provider interface. func (t *TimeConfig) BootTimeout() time.Duration { return t.TimeBootTimeout } // Image implements the config.Provider interface. func (i *InstallConfig) Image() string { return i.InstallImage } // Extensions implements the config.Provider interface. func (i *InstallConfig) Extensions() []config.Extension { return xslices.Map(i.InstallExtensions, func(e InstallExtensionConfig) config.Extension { return e }) } // Disk implements the config.Provider interface. func (i *InstallConfig) Disk() string { return i.InstallDisk } // DiskMatchExpression returns the disk matcher expression by inspecting the InstallDiskSelector. // //nolint:gocyclo func (i *InstallConfig) DiskMatchExpression() (*cel.Expression, error) { if i.InstallDiskSelector == nil { return nil, nil } var exprs []ast.Expr builder := cel.NewBuilder(celenv.DiskLocator()) selector := i.InstallDiskSelector if selector.Size != nil { op := selector.Size.MatchData.Op if op == "" { op = "==" } exprs = append(exprs, // disk.size op value builder.NewCall( builder.NextID(), "_"+op+"_", builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "size", ), builder.NewLiteral( builder.NextID(), types.Uint(selector.Size.MatchData.Size), ), ), ) } patternMatcherExpr := func(pattern, field string) ast.Expr { // glob(pattern, disk.$field) return builder.NewCall( builder.NextID(), "glob", builder.NewLiteral(builder.NextID(), types.String(pattern)), builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), field, ), ) } directMatchExpr := func(value, field string) ast.Expr { // disk.$field == value return builder.NewCall( builder.NextID(), operators.Equals, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), field, ), builder.NewLiteral(builder.NextID(), types.String(value)), ) } if selector.UUID != "" { exprs = append(exprs, patternMatcherExpr(selector.UUID, "uuid")) } if selector.WWID != "" { exprs = append(exprs, patternMatcherExpr(selector.WWID, "wwid")) } if selector.Model != "" { exprs = append(exprs, patternMatcherExpr(selector.Model, "model")) } if selector.Name != "" { // not supported return nil, fmt.Errorf("selector on name is not supported") } if selector.Serial != "" { exprs = append(exprs, patternMatcherExpr(selector.Serial, "serial")) } if selector.Modalias != "" { exprs = append(exprs, patternMatcherExpr(selector.Modalias, "modalias")) } // disk.transport != "" (otherwise it might select e.g. DM devices) exprs = append(exprs, builder.NewCall( builder.NextID(), operators.NotEquals, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "transport", ), builder.NewLiteral(builder.NextID(), types.String("")), ), ) if selector.Type != "" { switch selector.Type { case "nvme": // disk.transport == "nvme" exprs = append(exprs, directMatchExpr("nvme", "transport")) case "sd": // disk.transport == "mmc" exprs = append(exprs, directMatchExpr("mmc", "transport")) case "hdd": // disk.rotational exprs = append(exprs, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "rotational", )) case "ssd": // !disk.rotational exprs = append(exprs, builder.NewCall( builder.NextID(), operators.LogicalNot, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "rotational", ), ), ) default: return nil, fmt.Errorf("unsupported disk type %q", selector.Type) } } if selector.BusPath != "" { exprs = append(exprs, patternMatcherExpr(selector.BusPath, "bus_path")) } // exclude readonly disks: !disk.readonly exprs = append(exprs, builder.NewCall( builder.NextID(), operators.LogicalNot, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "readonly", ), )) // exclude CD-ROMs: !disk.cdrom exprs = append(exprs, builder.NewCall( builder.NextID(), operators.LogicalNot, builder.NewSelect( builder.NextID(), builder.NewIdent(builder.NextID(), "disk"), "cdrom", ), )) // reduce all expressions to a single one with && for len(exprs) > 1 { exprs = append(exprs[:len(exprs)-2], builder.NewCall( builder.NextID(), operators.LogicalAnd, exprs[len(exprs)-2], exprs[len(exprs)-1], )) } return builder.ToBooleanExpression(exprs[0]) } // ExtraKernelArgs implements the config.Provider interface. func (i *InstallConfig) ExtraKernelArgs() []string { return i.InstallExtraKernelArgs } // Zero implements the config.Provider interface. func (i *InstallConfig) Zero() bool { return pointer.SafeDeref(i.InstallWipe) } // LegacyBIOSSupport implements the config.Provider interface. func (i *InstallConfig) LegacyBIOSSupport() bool { return pointer.SafeDeref(i.InstallLegacyBIOSSupport) } // GrubUseUKICmdline implements the config.Provider interface. func (i *InstallConfig) GrubUseUKICmdline() bool { return pointer.SafeDeref(i.InstallGrubUseUKICmdline) } // Image implements the config.Provider interface. func (i InstallExtensionConfig) Image() string { return i.ExtensionImage } // Enabled implements the config.Provider interface. func (c *CoreDNS) Enabled() bool { return c.CoreDNSDisabled == nil || !*c.CoreDNSDisabled } // Image implements the config.Provider interface. func (c *CoreDNS) Image() string { coreDNSImage := fmt.Sprintf("%s:%s", constants.CoreDNSImage, constants.DefaultCoreDNSVersion) if c.CoreDNSImage != "" { coreDNSImage = c.CoreDNSImage } return coreDNSImage } // CertLifetime implements the config.Provider interface. func (a *AdminKubeconfigConfig) CertLifetime() time.Duration { if a.AdminKubeconfigCertLifetime == 0 { return constants.KubernetesAdminCertDefaultLifetime } return a.AdminKubeconfigCertLifetime } // CommonName implements the config.Provider interface. func (a *AdminKubeconfigConfig) CommonName() string { return constants.KubernetesAdminCertCommonName } // CertOrganization implements the config.Provider interface. func (a *AdminKubeconfigConfig) CertOrganization() string { return constants.KubernetesAdminCertOrganization } // Endpoints implements the Registries interface. func (r *RegistryMirrorConfig) Endpoints() []config.RegistryEndpointConfig { return xslices.Map(r.MirrorEndpoints, func(e string) config.RegistryEndpointConfig { return ®istryEndpointWrapper{ endpoint: e, overridePath: pointer.SafeDeref(r.MirrorOverridePath), } }) } // SkipFallback implements the Registries interface. func (r *RegistryMirrorConfig) SkipFallback() bool { return pointer.SafeDeref(r.MirrorSkipFallback) } // Content implements the config.Provider interface. func (f *MachineFile) Content() string { return f.FileContent } // Permissions implements the config.Provider interface. func (f *MachineFile) Permissions() os.FileMode { return os.FileMode(f.FilePermissions) } // Path implements the config.Provider interface. func (f *MachineFile) Path() string { return f.FilePath } // Op implements the config.Provider interface. func (f *MachineFile) Op() string { return f.FileOp } // Device implements the config.Provider interface. func (d *MachineDisk) Device() string { return d.DeviceName } // Partitions implements the config.Provider interface. func (d *MachineDisk) Partitions() []config.Partition { return xslices.Map(d.DiskPartitions, func(p *DiskPartition) config.Partition { return p }) } // Size implements the config.Provider interface. func (p *DiskPartition) Size() uint64 { return uint64(p.DiskSize) } // MountPoint implements the config.Provider interface. func (p *DiskPartition) MountPoint() string { return p.DiskMountPoint } // Provider implements the config.Provider interface. func (e *EncryptionConfig) Provider() block.EncryptionProviderType { if e.EncryptionProvider == "" { return block.EncryptionProviderLUKS2 } // the provider is validated in the machine config validation provider, _ := block.EncryptionProviderTypeString(e.EncryptionProvider) //nolint:errcheck return provider } // Cipher implements the config.Provider interface. func (e *EncryptionConfig) Cipher() string { return e.EncryptionCipher } // KeySize implements the config.Provider interface. func (e *EncryptionConfig) KeySize() uint { return e.EncryptionKeySize } // BlockSize implements the config.Provider interface. func (e *EncryptionConfig) BlockSize() uint64 { return e.EncryptionBlockSize } // Options implements the config.Provider interface. func (e *EncryptionConfig) Options() []string { return e.EncryptionPerfOptions } // Keys implements the config.Provider interface. func (e *EncryptionConfig) Keys() []config.EncryptionKey { return xslices.Map(e.EncryptionKeys, func(k *EncryptionKey) config.EncryptionKey { return k }) } // Static implements the config.Provider interface. func (e *EncryptionKey) Static() config.EncryptionKeyStatic { if e.KeyStatic == nil { return nil } return e.KeyStatic } // NodeID implements the config.Provider interface. func (e *EncryptionKey) NodeID() config.EncryptionKeyNodeID { if e.KeyNodeID == nil { return nil } return e.KeyNodeID } // KMS implements the config.Provider interface. func (e *EncryptionKey) KMS() config.EncryptionKeyKMS { if e.KeyKMS == nil { return nil } return e.KeyKMS } // TPM implements the config.Provider interface. func (e *EncryptionKey) TPM() config.EncryptionKeyTPM { if e.KeyTPM == nil { return nil } return e.KeyTPM } // LockToSTATE implements the config.Provider interface. func (e *EncryptionKey) LockToSTATE() bool { // not supported in v1alpha1 return false } // String implements the config.Provider interface. func (e *EncryptionKeyNodeID) String() string { return "nodeid" } // String implements the config.Provider interface. func (e *EncryptionKeyTPM) String() string { return "tpm" } // CheckSecurebootOnEnroll implements the config.Provider interface. func (e *EncryptionKeyTPM) CheckSecurebootOnEnroll() bool { if e == nil { return false } return pointer.SafeDeref(e.TPMCheckSecurebootStatusOnEnroll) } // PCRs implements the config.Provider interface. func (e *EncryptionKeyTPM) PCRs() []int { // v1alpha1 always used PCR 7 return []int{constants.SecureBootStatePCR} } // PubKeyPCRs implements the config.Provider interface. func (e *EncryptionKeyTPM) PubKeyPCRs() []int { // we always lock to PCR 11 return []int{constants.UKIPCR} } // Slot implements the config.Provider interface. func (e *EncryptionKey) Slot() int { return e.KeySlot } // Key implements the config.Provider interface. func (e *EncryptionKeyStatic) Key() []byte { return []byte(e.KeyData) } // String implements the config.Provider interface. func (e *EncryptionKeyStatic) String() string { return "static" } // Endpoint implements the config.Provider interface. func (e *EncryptionKeyKMS) Endpoint() string { return e.KMSEndpoint } // String implements the config.Provider interface. func (e *EncryptionKeyKMS) String() string { return "kms" } // Get implements the config.Provider interface. func (e *SystemDiskEncryptionConfig) Get(label string) config.EncryptionConfig { switch label { case constants.StatePartitionLabel: if e.StatePartition == nil { return nil } return e.StatePartition case constants.EphemeralPartitionLabel: if e.EphemeralPartition == nil { return nil } return e.EphemeralPartition } return nil } // HostPath implements the config.VolumeMount interface. func (v VolumeMountConfig) HostPath() string { return v.VolumeHostPath } // MountPath implements the config.VolumeMount interface. func (v VolumeMountConfig) MountPath() string { return v.VolumeMountPath } var volumeNameSanitizer = strings.NewReplacer("/", "-", "_", "-", ".", "-") // Name implements the config.VolumeMount interface. func (v VolumeMountConfig) Name() string { return strings.Trim(volumeNameSanitizer.Replace(v.VolumeMountPath), "-") } // ReadOnly implements the config.VolumeMount interface. func (v VolumeMountConfig) ReadOnly() bool { return v.VolumeReadOnly } // Rules implements config.Udev interface. func (u *UdevConfig) Rules() []string { return u.UdevRules } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_provider_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestInstallDiskSelector(t *testing.T) { t.Parallel() for _, test := range []struct { name string selector v1alpha1.InstallDiskSelector expected string }{ { name: "size", selector: v1alpha1.InstallDiskSelector{ Size: &v1alpha1.InstallDiskSizeMatcher{ MatchData: v1alpha1.InstallDiskSizeMatchData{ Op: "<=", Size: 256 * 1024, }, }, }, expected: `disk.size <= 262144u && disk.transport != "" && !disk.readonly && !disk.cdrom`, }, { name: "size and type", selector: v1alpha1.InstallDiskSelector{ Size: &v1alpha1.InstallDiskSizeMatcher{ MatchData: v1alpha1.InstallDiskSizeMatchData{ Size: 1024 * 1024, }, }, Type: v1alpha1.InstallDiskType("nvme"), }, expected: `disk.size == 1048576u && disk.transport != "" && disk.transport == "nvme" && !disk.readonly && !disk.cdrom`, }, { name: "size and type and modalias", selector: v1alpha1.InstallDiskSelector{ Size: &v1alpha1.InstallDiskSizeMatcher{ MatchData: v1alpha1.InstallDiskSizeMatchData{ Size: 1024 * 1024, }, }, Type: v1alpha1.InstallDiskType("hdd"), Modalias: "pci:1234:5678*", }, expected: `disk.size == 1048576u && glob("pci:1234:5678*", disk.modalias) && disk.transport != "" && disk.rotational && !disk.readonly && !disk.cdrom`, }, { name: "ssd", selector: v1alpha1.InstallDiskSelector{ Type: v1alpha1.InstallDiskType("ssd"), }, expected: `disk.transport != "" && !disk.rotational && !disk.readonly && !disk.cdrom`, }, { name: "bus path", selector: v1alpha1.InstallDiskSelector{ BusPath: "/pci-0000:00:1f.2/*", }, expected: `disk.transport != "" && glob("/pci-0000:00:1f.2/*", disk.bus_path) && !disk.readonly && !disk.cdrom`, }, { name: "uuid", selector: v1alpha1.InstallDiskSelector{ UUID: "0000-0001-*", }, expected: `glob("0000-0001-*", disk.uuid) && disk.transport != "" && !disk.readonly && !disk.cdrom`, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() installCfg := &v1alpha1.InstallConfig{ InstallDiskSelector: &test.selector, } expr, err := installCfg.DiskMatchExpression() require.NoError(t, err) assert.Equal(t, test.expected, expr.String()) }) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_proxyconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "github.com/siderolabs/go-pointer" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Enabled implements the config.Proxy interface. func (p *ProxyConfig) Enabled() bool { return !pointer.SafeDeref(p.Disabled) } // Image implements the config.Proxy interface. func (p *ProxyConfig) Image() string { image := p.ContainerImage if image == "" { image = fmt.Sprintf("%s:v%s", constants.KubeProxyImage, constants.DefaultKubernetesVersion) } return image } // Mode implements the config.Proxy interface. func (p *ProxyConfig) Mode() string { return p.ModeConfig } // ExtraArgs implements the config.Proxy interface. func (p *ProxyConfig) ExtraArgs() map[string][]string { return p.ExtraArgsConfig.ToMap() } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_redact_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" ) func TestRedactSecrets(t *testing.T) { input, err := generate.NewInput("test", "https://doesntmatter:6443", constants.DefaultKubernetesVersion) require.NoError(t, err) container, err := input.Config(machine.TypeControlPlane) if err != nil { return } config := container.RawV1Alpha1() require.NotEmpty(t, config.MachineConfig.MachineToken) require.NotEmpty(t, config.MachineConfig.MachineCA.Key) require.NotEmpty(t, config.ClusterConfig.ClusterSecret) require.NotEmpty(t, config.ClusterConfig.BootstrapToken) require.Empty(t, config.ClusterConfig.ClusterAESCBCEncryptionSecret) require.NotEmpty(t, config.ClusterConfig.ClusterSecretboxEncryptionSecret) require.NotEmpty(t, config.ClusterConfig.ClusterCA.Key) require.NotEmpty(t, config.ClusterConfig.EtcdConfig.RootCA.Key) require.NotEmpty(t, config.ClusterConfig.ClusterServiceAccount.Key) replacement := "**.***" config.Redact(replacement) require.Equal(t, replacement, config.Machine().Security().Token()) require.Equal(t, replacement, string(config.Machine().Security().IssuingCA().Key)) require.Equal(t, replacement, config.Cluster().Secret()) require.Equal(t, "***", config.Cluster().Token().Secret()) require.Equal(t, "", config.Cluster().AESCBCEncryptionSecret()) require.Equal(t, replacement, config.Cluster().SecretboxEncryptionSecret()) require.Equal(t, replacement, string(config.Cluster().IssuingCA().Key)) require.Equal(t, replacement, string(config.Cluster().Etcd().CA().Key)) require.Equal(t, replacement, string(config.Cluster().ServiceAccount().Key)) } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_resourcesconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "strconv" ) // CPURequests implements the config.Resources interface. func (r *ResourcesConfig) CPURequests() string { if r != nil { return convertResource(r.Requests, "cpu") } return "" } // MemoryRequests implements the config.Resources interface. func (r *ResourcesConfig) MemoryRequests() string { if r != nil { return convertResource(r.Requests, "memory") } return "" } // CPULimits implements the config.Resources interface. func (r *ResourcesConfig) CPULimits() string { if r != nil { return convertResource(r.Limits, "cpu") } return "" } // MemoryLimits implements the config.Resources interface. func (r *ResourcesConfig) MemoryLimits() string { if r != nil { return convertResource(r.Limits, "memory") } return "" } // Validate performs config validation. func (r *ResourcesConfig) Validate() error { if r == nil { return nil } checkKeys := func(resource Unstructured) error { for key := range resource.Object { switch key { case "memory": case "cpu": default: return fmt.Errorf("unsupported pod resource %q", key) } } return nil } if err := checkKeys(r.Requests); err != nil { return err } return checkKeys(r.Limits) } func convertResource(resources Unstructured, key string) string { if resources.Object == nil { return "" } if _, ok := resources.Object[key]; !ok { return "" } val := resources.Object[key] switch typedVal := val.(type) { case int: return strconv.Itoa(typedVal) case string: return typedVal default: return "" } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_schedulerconfig.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Image implements the config.Scheduler interface. func (s *SchedulerConfig) Image() string { image := s.ContainerImage if image == "" { image = fmt.Sprintf("%s:v%s", constants.KubernetesSchedulerImage, constants.DefaultKubernetesVersion) } return image } // ExtraArgs implements the config.Scheduler interface. func (s *SchedulerConfig) ExtraArgs() map[string][]string { return s.ExtraArgsConfig.ToMap() } // ExtraVolumes implements the config.Scheduler interface. func (s *SchedulerConfig) ExtraVolumes() []config.VolumeMount { return xslices.Map(s.ExtraVolumesConfig, func(v VolumeMountConfig) config.VolumeMount { return v }) } // Env implements the config.Scheduler interface. func (s *SchedulerConfig) Env() Env { return s.EnvConfig } // Resources implements the config.Resources interface. func (s *SchedulerConfig) Resources() config.Resources { return s.ResourcesConfig } // Config implements the config.Scheduler interface. func (s *SchedulerConfig) Config() map[string]any { return s.SchedulerConfig.Object } // Validate performs config validation. func (s *SchedulerConfig) Validate() error { if s == nil { return nil } if err := s.ResourcesConfig.Validate(); err != nil { return fmt.Errorf("scheduler resource validation failed: %w", err) } return nil } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_stability_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "errors" "fmt" "io/fs" "os" "testing" "github.com/blang/semver/v4" "github.com/siderolabs/gen/ensure" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/gendata" ) // TestConfigEncodingStability ensures that the encoding of a configuration is stable as we moved forward with the config format. func TestConfigEncodingStability(t *testing.T) { t.Parallel() // flip this to generate missing configs const generateMode = false secretsBundle, err := secrets.LoadBundle("testdata/stability/secrets.yaml") require.NoError(t, err) versionContracts := []*config.VersionContract{ config.TalosVersion1_3, config.TalosVersion1_4, config.TalosVersion1_5, config.TalosVersion1_6, config.TalosVersion1_7, config.TalosVersion1_8, config.TalosVersion1_9, config.TalosVersion1_10, config.TalosVersion1_11, config.TalosVersion1_12, config.TalosVersion1_13, } currentVersion := ensure.Value(semver.ParseTolerant(gendata.VersionTag)) currentVersion.Patch = 0 maxContractVersion := ensure.Value(semver.ParseTolerant(versionContracts[len(versionContracts)-1].String())) require.True(t, currentVersion.LTE(maxContractVersion), "latest version contract is not tested") for _, versionContract := range versionContracts { t.Run(versionContract.String(), func(t *testing.T) { t.Parallel() t.Run("base", func(t *testing.T) { t.Parallel() in, err := generate.NewInput("base", "https://base:6443", "1.28.0", generate.WithSecretsBundle(secretsBundle), generate.WithVersionContract(versionContract), ) require.NoError(t, err) testConfigStability(t, in, versionContract, "base", generateMode) }) t.Run("with overrides", func(t *testing.T) { t.Parallel() in, err := generate.NewInput("base", "https://base:6443", "1.28.0", generate.WithSecretsBundle(secretsBundle), generate.WithVersionContract(versionContract), generate.WithAdditionalSubjectAltNames([]string{"foo", "bar"}), generate.WithAllowSchedulingOnControlPlanes(true), generate.WithDNSDomain("example.com"), generate.WithInstallDisk("/dev/vda"), generate.WithInstallExtraKernelArgs([]string{"foo=bar", "bar=baz"}), generate.WithLocalAPIServerPort(5443), generate.WithSysctls(map[string]string{"foo": "bar"}), generate.WithClusterCNIConfig(&v1alpha1.CNIConfig{ CNIName: "custom", CNIUrls: []string{"https://example.com/cni.yaml"}, }), generate.WithRegistryMirror("ghcr.io", "https://ghcr.io.my-mirror.com"), ) require.NoError(t, err) patches, err := configpatcher.LoadPatches([]string{"@testdata/stability/patch.yaml"}) require.NoError(t, err) testConfigStability(t, in, versionContract, "overrides", generateMode, patches...) }) }) } } func testConfigStability(t *testing.T, in *generate.Input, versionContract *config.VersionContract, flavor string, generateMode bool, patches ...configpatcher.Patch) { t.Helper() for _, machineType := range []machine.Type{ machine.TypeControlPlane, machine.TypeWorker, } { cfg, err := in.Config(machineType) require.NoError(t, err) cfgBytes, err := cfg.EncodeBytes(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, err) patched, err := configpatcher.Apply(configpatcher.WithBytes(cfgBytes), patches) require.NoError(t, err) cfgBytes, err = patched.Bytes() require.NoError(t, err) expectedPath := fmt.Sprintf("testdata/stability/%s/%s-%s.yaml", versionContract, flavor, machineType) expectedBytes, err := os.ReadFile(expectedPath) if errors.Is(err, fs.ErrNotExist) && generateMode { require.NoError(t, os.WriteFile(expectedPath, cfgBytes, 0o644)) t.Logf("generated %s", expectedPath) continue } require.NoError(t, err) assert.Equal(t, string(expectedBytes), string(cfgBytes), "config encoding mismatch for %s", expectedPath) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_strategic_merge_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/merge" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestStrategicMergePatch(t *testing.T) { t.Parallel() entries, err := os.ReadDir("testdata/strategic") require.NoError(t, err) for _, entry := range entries { if !entry.IsDir() { continue } t.Run(entry.Name(), testMerge(filepath.Join("testdata/strategic", entry.Name()))) } } func load(t *testing.T, path string) *v1alpha1.Config { provider, err := configloader.NewFromFile(path) require.NoError(t, err) return provider.RawV1Alpha1() } func testMerge(path string) func(t *testing.T) { return func(t *testing.T) { t.Parallel() left := load(t, filepath.Join(path, "left.yaml")) right := load(t, filepath.Join(path, "right.yaml")) expected := load(t, filepath.Join(path, "expected.yaml")) result := left.DeepCopy() err := merge.Merge(result, right) require.NoError(t, err) ctr, err := container.New(result) require.NoError(t, err) marshaled, err := ctr.EncodeString(encoder.WithComments(encoder.CommentsDisabled)) require.NoError(t, err) assert.Equal(t, expected, result, "got:\n%v", marshaled) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_types.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. /* Package v1alpha1 contains definition of the `v1alpha1` configuration document. Even though the machine configuration in Talos Linux is multi-document, at the moment this configuration document contains most of the configuration options. It is expected that new configuration options will be added as new documents, and existing ones migrated to their own documents. */ package v1alpha1 //go:generate go tool github.com/siderolabs/talos/tools/docgen -output ./v1alpha1_types_doc.go ./v1alpha1_types.go //go:generate go tool k8s.io/code-generator/cmd/deepcopy-gen --go-header-file ../../../../../hack/boilerplate.txt --bounding-dirs ../v1alpha1 --output-file zz_generated.deepcopy //docgen:jsonschema import ( "fmt" "maps" "net/url" "os" "regexp" "strconv" "strings" "time" "github.com/dustin/go-humanize" "github.com/siderolabs/crypto/x509" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/internal/registry" "github.com/siderolabs/talos/pkg/machinery/config/merge" ) func init() { registry.Register("v1alpha1", func(version string) config.Document { return &Config{} }) } // Args represents a map of argument names to their values. type Args map[string]ArgValue // ToMap converts Args to a map of string slices. func (a Args) ToMap() map[string][]string { result := make(map[string][]string) for key, argValue := range a { // technically this shouldn't happen due to validation during unmarshalling // but just in case, we handle case when both are set value := make([]string, 0) if argValue.strValue != "" { value = append(value, argValue.strValue) } if argValue.listValue != nil { value = append(value, argValue.listValue...) } result[key] = value } return result } // Merge with another Args. func (a *Args) Merge(other any) error { otherArgs, ok := other.(Args) if !ok { return fmt.Errorf("cannot merge Args with %T", other) } if len(otherArgs) == 0 { return nil } if *a == nil { *a = make(Args) } maps.Copy(*a, otherArgs) return nil } // ArgValue represents a value for an argument, which can be either a single string or a list of strings. // docgen:nodoc type ArgValue struct { listValue []string strValue string } // NewArgValue creates a new ArgValue from either a string or a list of strings. func NewArgValue(s string, l []string) ArgValue { return ArgValue{ listValue: l, strValue: s, } } // MarshalYAML is a custom marshaller for `ArgValue`. func (a ArgValue) MarshalYAML() (any, error) { if a.listValue != nil { return &yaml.Node{ Kind: yaml.SequenceNode, Tag: "!!seq", Content: func() []*yaml.Node { nodes := make([]*yaml.Node, 0, len(a.listValue)) for _, item := range a.listValue { nodes = append(nodes, &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!str", Value: item, }) } return nodes }(), }, nil } if a.strValue != "" { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!str", Value: a.strValue, }, nil } return nil, nil } // UnmarshalYAML is a custom unmarshaller for `ArgValue`. func (a *ArgValue) UnmarshalYAML(unmarshal func(any) error) error { // Try scalar string first var s string if err := unmarshal(&s); err == nil { a.strValue = s a.listValue = nil return nil } // Then try list of strings var l []string if err := unmarshal(&l); err == nil { a.listValue = l a.strValue = "" return nil } return fmt.Errorf("arg value must be a string or list of strings") } // Config defines the v1alpha1.Config Talos machine configuration document. // // examples: // - value: configExample() // schemaRoot: true type Config struct { // description: | // Indicates the schema used to decode the contents. // values: // - "v1alpha1" ConfigVersion string `yaml:"version"` // description: | // Enable verbose logging to the console. // All system containers logs will flow into serial console. // // **Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput. // values: // - true // - yes // - false // - no ConfigDebug *bool `yaml:"debug,omitempty"` // docgen:nodoc // // Deprecated: Not supported anymore. ConfigPersist *bool `yaml:"persist,omitempty"` // description: | // Provides machine specific configuration options. MachineConfig *MachineConfig `yaml:"machine"` // description: | // Provides cluster specific configuration options. ClusterConfig *ClusterConfig `yaml:"cluster"` } var _ config.MachineConfig = (*MachineConfig)(nil) // MachineConfig represents the machine-specific config values. // // examples: // - value: machineConfigExample() type MachineConfig struct { // description: | // Defines the role of the machine within the cluster. // // **Control Plane** // // Control Plane node type designates the node as a control plane member. // This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler. // // **Worker** // // Worker node type designates the node as a worker node. // This means it will be an available compute node for scheduling workloads. // // This node type was previously known as "join"; that value is still supported but deprecated. // values: // - "controlplane" // - "worker" MachineType string `yaml:"type"` // description: | // The `token` is used by a machine to join the PKI of the cluster. // Using this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity. // examples: // - name: example token // value: "\"328hom.uqjzh6jnn2eie9oi\"" MachineToken string `yaml:"token"` // Warning: It is important to ensure that this token is correct since a machine's certificate has a short TTL by default. // description: | // The root certificate authority of the PKI. // It is composed of a base64 encoded `crt` and `key`. // examples: // - value: pemEncodedCertificateExample() // name: machine CA example // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string MachineCA *x509.PEMEncodedCertificateAndKey `yaml:"ca,omitempty"` // description: | // The certificates issued by certificate authorities are accepted in addition to issuing 'ca'. // It is composed of a base64 encoded `crt``. // schema: // type: object // additionalProperties: false // properties: // crt: // type: string MachineAcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs,omitempty"` // description: | // Extra certificate subject alternative names for the machine's certificate. // By default, all non-loopback interface IPs are automatically added to the certificate's SANs. // examples: // - name: Uncomment this to enable SANs. // value: '[]string{"10.0.0.10", "172.16.0.10", "192.168.0.10"}' MachineCertSANs []string `yaml:"certSANs"` // description: | // Provides machine specific control plane configuration options. // examples: // - name: ControlPlane definition example. // value: machineControlplaneExample() MachineControlPlane *MachineControlPlaneConfig `yaml:"controlPlane,omitempty"` // description: | // Used to provide additional options to the kubelet. // examples: // - name: Kubelet definition example. // value: machineKubeletExample() MachineKubelet *KubeletConfig `yaml:"kubelet,omitempty"` // description: | // Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver. // // Static pods can be used to run components which should be started before the Kubernetes control plane is up. // Talos doesn't validate the pod definition. // Updates to this field can be applied without a reboot. // // See https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/. // examples: // - name: nginx static pod. // value: machinePodsExample() // schema: // type: array // items: // type: object MachinePods []Unstructured `yaml:"pods,omitempty"` // docgen:nodoc // // Deprecated: All fields within NetworkConfig are deprecated. Use multi-document network config types instead: // HostnameConfig, NetworkDeviceConfig, ResolverConfig, StaticHostConfig, KubeSpanConfig. MachineNetwork *NetworkConfig `yaml:"network,omitempty"` // docgen:nodoc // // Deprecated: Use 'UserVolumeConfig' instead. MachineDisks []*MachineDisk `yaml:"disks,omitempty"` // Note: `size` is in units of bytes. // description: | // Used to provide instructions for installations. // // Note that this configuration section gets silently ignored by Talos images that are considered pre-installed. // To make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted. // examples: // - name: MachineInstall config usage example. // value: machineInstallExample() MachineInstall *InstallConfig `yaml:"install,omitempty"` // description: | // Allows the addition of user specified files. // The value of `op` can be `create`, `overwrite`, or `append`. // In the case of `create`, `path` must not exist. // In the case of `overwrite`, and `append`, `path` must be a valid file. // If an `op` value of `append` is used, the existing file will be appended. // Note that the file contents are not required to be base64 encoded. // examples: // - name: MachineFiles usage example. // value: machineFilesExample() MachineFiles []*MachineFile `yaml:"files,omitempty"` // Note: The specified `path` is relative to `/var`. // docgen:nodoc // // Deprecated: Use 'EnvironmentConfig' instead. MachineEnv Env `yaml:"env,omitempty"` // docgen:nodoc // // Deprecated: Use 'TimeSyncConfig' instead. MachineTime *TimeConfig `yaml:"time,omitempty"` // description: | // Used to configure the machine's sysctls. // examples: // - name: MachineSysctls usage example. // value: machineSysctlsExample() MachineSysctls map[string]string `yaml:"sysctls,omitempty"` // description: | // Used to configure the machine's sysfs. // examples: // - name: MachineSysfs usage example. // value: machineSysfsExample() MachineSysfs map[string]string `yaml:"sysfs,omitempty"` // docgen:nodoc // // Deprecated: Use `Registry*Config` instead. MachineRegistries RegistriesConfig `yaml:"registries,omitempty"` // docgen:nodoc // // Deprecated: Use `VolumeConfig` instead. MachineSystemDiskEncryption *SystemDiskEncryptionConfig `yaml:"systemDiskEncryption,omitempty"` // description: | // Features describe individual Talos features that can be switched on or off. // examples: // - value: machineFeaturesExample() MachineFeatures *FeaturesConfig `yaml:"features,omitempty"` // description: | // Configures the udev system. // examples: // - value: machineUdevExample() MachineUdev *UdevConfig `yaml:"udev,omitempty"` // description: | // Configures the logging system. // examples: // - value: machineLoggingExample() MachineLogging *LoggingConfig `yaml:"logging,omitempty"` // description: | // Configures the kernel. // examples: // - value: machineKernelExample() MachineKernel *KernelConfig `yaml:"kernel,omitempty"` // description: | // Configures the seccomp profiles for the machine. // examples: // - value: machineSeccompExample() MachineSeccompProfiles []*MachineSeccompProfile `yaml:"seccompProfiles,omitempty" talos:"omitonlyifnil"` // description: | // Override (patch) settings in the default OCI runtime spec for CRI containers. // // It can be used to set some default container settings which are not configurable in Kubernetes, // for example default ulimits. // Note: this change applies to all newly created containers, and it requires a reboot to take effect. // examples: // - name: override default open file limit // value: machineBaseRuntimeSpecOverridesExample() // schema: // type: object MachineBaseRuntimeSpecOverrides Unstructured `yaml:"baseRuntimeSpecOverrides,omitempty"` // description: | // Configures the node labels for the machine. // // Note: In the default Kubernetes configuration, worker nodes are restricted to set // labels with some prefixes (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin). // examples: // - name: node labels example. // value: 'map[string]string{"exampleLabel": "exampleLabelValue"}' MachineNodeLabels map[string]string `yaml:"nodeLabels,omitempty"` // description: | // Configures the node annotations for the machine. // examples: // - name: node annotations example. // value: 'map[string]string{"customer.io/rack": "r13a25"}' MachineNodeAnnotations map[string]string `yaml:"nodeAnnotations,omitempty"` // description: | // Configures the node taints for the machine. Effect is optional. // // Note: In the default Kubernetes configuration, worker nodes are not allowed to // modify the taints (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin). // examples: // - name: node taints example. // value: 'map[string]string{"exampleTaint": "exampleTaintValue:NoSchedule"}' MachineNodeTaints map[string]string `yaml:"nodeTaints,omitempty"` } // MachineSeccompProfile defines seccomp profiles for the machine. type MachineSeccompProfile struct { // description: | // The `name` field is used to provide the file name of the seccomp profile. MachineSeccompProfileName string `yaml:"name"` // description: | // The `value` field is used to provide the seccomp profile. // schema: // type: object MachineSeccompProfileValue Unstructured `yaml:"value"` } var ( _ config.ClusterConfig = (*ClusterConfig)(nil) _ config.ClusterNetwork = (*ClusterConfig)(nil) _ config.Token = (*ClusterConfig)(nil) ) // ClusterConfig represents the cluster-wide config values. // // examples: // - value: clusterConfigExample() type ClusterConfig struct { // description: | // Globally unique identifier for this cluster (base64 encoded random 32 bytes). ClusterID string `yaml:"id,omitempty"` // description: | // Shared secret of cluster (base64 encoded random 32 bytes). // This secret is shared among cluster members but should never be sent over the network. ClusterSecret string `yaml:"secret,omitempty"` // description: | // Provides control plane specific configuration options. // examples: // - name: Setting controlplane endpoint address to 1.2.3.4 and port to 443 example. // value: clusterControlPlaneExample() ControlPlane *ControlPlaneConfig `yaml:"controlPlane"` // description: | // Configures the cluster's name. ClusterName string `yaml:"clusterName,omitempty"` // description: | // Provides cluster specific network configuration options. // examples: // - name: Configuring with flannel CNI and setting up subnets. // value: clusterNetworkExample() ClusterNetwork *ClusterNetworkConfig `yaml:"network,omitempty"` // description: | // The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster. // examples: // - name: Bootstrap token example (do not use in production!). // value: '"wlzjyw.bei2zfylhs2by0wd"' BootstrapToken string `yaml:"token,omitempty"` // description: | // A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/). // Enables encryption with AESCBC. // examples: // - name: Decryption secret example (do not use in production!). // value: '"z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM="' ClusterAESCBCEncryptionSecret string `yaml:"aescbcEncryptionSecret,omitempty"` // description: | // A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/). // Enables encryption with secretbox. // Secretbox has precedence over AESCBC. // examples: // - name: Decryption secret example (do not use in production!). // value: '"z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM="' ClusterSecretboxEncryptionSecret string `yaml:"secretboxEncryptionSecret,omitempty"` // description: | // The base64 encoded root certificate authority used by Kubernetes. // examples: // - name: ClusterCA example. // value: pemEncodedCertificateExample() // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string ClusterCA *x509.PEMEncodedCertificateAndKey `yaml:"ca,omitempty"` // description: | // The list of base64 encoded accepted certificate authorities used by Kubernetes. // schema: // type: object // additionalProperties: false // properties: // crt: // type: string ClusterAcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs,omitempty"` // description: | // The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation. // // This CA can be self-signed. // examples: // - name: AggregatorCA example. // value: pemEncodedCertificateExample() // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string ClusterAggregatorCA *x509.PEMEncodedCertificateAndKey `yaml:"aggregatorCA,omitempty"` // description: | // The base64 encoded private key for service account token generation. // examples: // - name: AggregatorCA example. // value: pemEncodedKeyExample() // schema: // type: object // additionalProperties: false // properties: // key: // type: string // additionalProperties: false ClusterServiceAccount *x509.PEMEncodedKey `yaml:"serviceAccount,omitempty"` // description: | // API server specific configuration options. // examples: // - value: clusterAPIServerExample() APIServerConfig *APIServerConfig `yaml:"apiServer,omitempty"` // description: | // Controller manager server specific configuration options. // examples: // - value: clusterControllerManagerExample() ControllerManagerConfig *ControllerManagerConfig `yaml:"controllerManager,omitempty"` // description: | // Kube-proxy server-specific configuration options // examples: // - value: clusterProxyExample() ProxyConfig *ProxyConfig `yaml:"proxy,omitempty"` // description: | // Scheduler server specific configuration options. // examples: // - value: clusterSchedulerExample() SchedulerConfig *SchedulerConfig `yaml:"scheduler,omitempty"` // description: | // Configures cluster member discovery. // examples: // - value: clusterDiscoveryExample() ClusterDiscoveryConfig *ClusterDiscoveryConfig `yaml:"discovery,omitempty"` // description: | // Etcd specific configuration options. // examples: // - value: clusterEtcdExample() EtcdConfig *EtcdConfig `yaml:"etcd,omitempty"` // description: | // Core DNS specific configuration options. // examples: // - value: clusterCoreDNSExample() CoreDNSConfig *CoreDNS `yaml:"coreDNS,omitempty"` // description: | // External cloud provider configuration. // examples: // - value: clusterExternalCloudProviderConfigExample() ExternalCloudProviderConfig *ExternalCloudProviderConfig `yaml:"externalCloudProvider,omitempty"` // description: | // A list of urls that point to additional manifests. // These will get automatically deployed as part of the bootstrap. // examples: // - value: > // []string{ // "https://www.example.com/manifest1.yaml", // "https://www.example.com/manifest2.yaml", // } ExtraManifests []string `yaml:"extraManifests,omitempty" talos:"omitonlyifnil"` // description: | // A map of key value pairs that will be added while fetching the extraManifests. // examples: // - value: > // map[string]string{ // "Token": "1234567", // "X-ExtraInfo": "info", // } ExtraManifestHeaders map[string]string `yaml:"extraManifestHeaders,omitempty"` // description: | // A list of inline Kubernetes manifests. // These will get automatically deployed as part of the bootstrap. // examples: // - value: clusterInlineManifestsExample() // schema: // type: array // items: // $ref: "#/$defs/v1alpha1.ClusterInlineManifest" ClusterInlineManifests ClusterInlineManifests `yaml:"inlineManifests,omitempty" talos:"omitonlyifnil"` // description: | // Settings for admin kubeconfig generation. // Certificate lifetime can be configured. // examples: // - value: clusterAdminKubeconfigExample() AdminKubeconfigConfig *AdminKubeconfigConfig `yaml:"adminKubeconfig,omitempty"` // docgen:nodoc // // Deprecated: Use `AllowSchedulingOnControlPlanes` instead. AllowSchedulingOnMasters *bool `yaml:"allowSchedulingOnMasters,omitempty"` // description: | // Allows running workload on control-plane nodes. // values: // - true // - yes // - false // - no // examples: // - value: true AllowSchedulingOnControlPlanes *bool `yaml:"allowSchedulingOnControlPlanes,omitempty"` } // LinuxIDMapping represents the Linux ID mapping. type LinuxIDMapping struct { // description: | // ContainerID is the starting UID/GID in the container. ContainerID uint32 `yaml:"containerID"` // description: | // HostID is the starting UID/GID on the host to be mapped to 'ContainerID'. HostID uint32 `yaml:"hostID"` // description: | // Size is the number of IDs to be mapped. Size uint32 `yaml:"size"` } // ExtraMount wraps OCI Mount specification. type ExtraMount struct { // description: | // Destination is the absolute path where the mount will be placed in the container. Destination string `yaml:"destination"` // description: | // Type specifies the mount kind. Type string `yaml:"type,omitempty"` // description: | // Source specifies the source path of the mount. Source string `yaml:"source,omitempty"` // description: | // Options are fstab style mount options. Options []string `yaml:"options,omitempty"` // description: | // UID/GID mappings used for changing file owners w/o calling chown, fs should support it. // // Every mount point could have its own mapping. UIDMappings []LinuxIDMapping `yaml:"uidMappings,omitempty"` // description: | // UID/GID mappings used for changing file owners w/o calling chown, fs should support it. // // Every mount point could have its own mapping. GIDMappings []LinuxIDMapping `yaml:"gidMappings,omitempty"` } // MachineControlPlaneConfig machine specific configuration options. type MachineControlPlaneConfig struct { // description: | // Controller manager machine specific configuration options. MachineControllerManager *MachineControllerManagerConfig `yaml:"controllerManager,omitempty"` // description: | // Scheduler machine specific configuration options. MachineScheduler *MachineSchedulerConfig `yaml:"scheduler,omitempty"` } // MachineControllerManagerConfig represents the machine specific ControllerManager config values. type MachineControllerManagerConfig struct { // description: | // Disable kube-controller-manager on the node. MachineControllerManagerDisabled *bool `yaml:"disabled,omitempty"` } // MachineSchedulerConfig represents the machine specific Scheduler config values. type MachineSchedulerConfig struct { // description: | // Disable kube-scheduler on the node. MachineSchedulerDisabled *bool `yaml:"disabled,omitempty"` } // KubeletConfig represents the kubelet config values. type KubeletConfig struct { // description: | // The `image` field is an optional reference to an alternative kubelet image. // examples: // - value: kubeletImageExample() KubeletImage string `yaml:"image,omitempty"` // description: | // The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list. // examples: // - value: '[]string{"10.96.0.10", "169.254.2.53"}' KubeletClusterDNS []string `yaml:"clusterDNS,omitempty"` // description: | // The `extraArgs` field is used to provide additional flags to the kubelet. // examples: // - value: > // Args{ // "key": ArgValue{strValue: "value"}, // } // - value: > // Args{ // "key": ArgValue{listValue: []string{"value1", "value2"}}, // } // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string KubeletExtraArgs Args `yaml:"extraArgs,omitempty"` // description: | // The `extraMounts` field is used to add additional mounts to the kubelet container. // Note that either `bind` or `rbind` are required in the `options`. // examples: // - value: kubeletExtraMountsExample() KubeletExtraMounts []ExtraMount `yaml:"extraMounts,omitempty"` // description: | // The `extraConfig` field is used to provide kubelet configuration overrides. // // Some fields are not allowed to be overridden: authentication and authorization, cgroups // configuration, ports, etc. // examples: // - value: kubeletExtraConfigExample() // schema: // type: object KubeletExtraConfig Unstructured `yaml:"extraConfig,omitempty"` // description: | // The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. // examples: // - value: kubeletCredentialProviderConfigExample() // schema: // type: object KubeletCredentialProviderConfig Unstructured `yaml:"credentialProviderConfig,omitempty"` // description: | // Enable container runtime default Seccomp profile. // values: // - true // - yes // - false // - no KubeletDefaultRuntimeSeccompProfileEnabled *bool `yaml:"defaultRuntimeSeccompProfileEnabled,omitempty"` // description: | // The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration. // This is required in clouds like AWS. // values: // - true // - yes // - false // - no KubeletRegisterWithFQDN *bool `yaml:"registerWithFQDN,omitempty"` // description: | // The `nodeIP` field is used to configure `--node-ip` flag for the kubelet. // This is used when a node has multiple addresses to choose from. // examples: // - value: kubeletNodeIPExample() KubeletNodeIP *KubeletNodeIPConfig `yaml:"nodeIP,omitempty"` // description: | // The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver. // This runs kubelet as standalone and only runs static pods. // values: // - true // - yes // - false // - no KubeletSkipNodeRegistration *bool `yaml:"skipNodeRegistration,omitempty"` // description: | // The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory. // It's recommended to configure static pods with the "pods" key instead. // values: // - true // - yes // - false // - no KubeletDisableManifestsDirectory *bool `yaml:"disableManifestsDirectory,omitempty"` } // KubeletNodeIPConfig represents the kubelet node IP configuration. type KubeletNodeIPConfig struct { // description: | // The `validSubnets` field configures the networks to pick kubelet node IP from. // For dual stack configuration, there should be two subnets: one for IPv4, another for IPv6. // IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`. // Negative subnet matches should be specified last to filter out IPs picked by positive matches. // If not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both. KubeletNodeIPValidSubnets []string `yaml:"validSubnets,omitempty"` } // NetworkConfig represents the machine's networking config values. // // Deprecated: all fields in NetworkConfig are deprecated, use corresponding multi-doc config types instead. // //docgen:nodoc type NetworkConfig struct { // docgen:nodoc // // Deprecated: use `HostnameConfig` instead. NetworkHostname string `yaml:"hostname,omitempty"` // docgen:nodoc // // Deprecated: use multi-doc network config. NetworkInterfaces NetworkDeviceList `yaml:"interfaces,omitempty"` // docgen:nodoc // // Deprecated: Use `ResolverConfig` instead. NameServers []string `yaml:"nameservers,omitempty"` // docgen:nodoc // // Deprecated: Use `ResolverConfig` instead. Searches []string `yaml:"searchDomains,omitempty"` // docgen:nodoc // // Deprecated: Use `StatisHostConfig` instead. ExtraHostEntries []*ExtraHost `yaml:"extraHostEntries,omitempty"` // docgen:nodoc // // Deprecated: Use `KubeSpanConfig` document instead. NetworkKubeSpan *NetworkKubeSpan `yaml:"kubespan,omitempty"` // docgen:nodoc // // Deprecated: Use `ResolverConfig` instead. NetworkDisableSearchDomain *bool `yaml:"disableSearchDomain,omitempty"` } // NetworkDeviceList is a list of *Device structures with overridden merge process. // // docgen:nodoc type NetworkDeviceList []*Device // Merge the network interface configuration intelligently. func (devices *NetworkDeviceList) Merge(other any) error { otherDevices, ok := other.(NetworkDeviceList) if !ok { return fmt.Errorf("unexpected type for device merge %T", other) } for _, device := range otherDevices { if err := devices.mergeDevice(device); err != nil { return err } } return nil } func (devices *NetworkDeviceList) mergeDevice(device *Device) error { var existing *Device switch { case device.DeviceInterface != "": for _, d := range *devices { if d.DeviceInterface == device.DeviceInterface { existing = d break } } case device.DeviceSelector != nil: for _, d := range *devices { if d.DeviceSelector != nil && *d.DeviceSelector == *device.DeviceSelector { existing = d break } } } if existing != nil { return merge.Merge(existing, device) } *devices = append(*devices, device) return nil } // InstallConfig represents the installation options for preparing a node. type InstallConfig struct { // description: | // The disk used for installations. // examples: // - value: '"/dev/sda"' // - value: '"/dev/nvme0"' InstallDisk string `yaml:"disk,omitempty"` // description: | // Look up disk using disk attributes like model, size, serial and others. // Always has priority over `disk`. // examples: // - value: machineInstallDiskSelectorExample() InstallDiskSelector *InstallDiskSelector `yaml:"diskSelector,omitempty"` // docgen:nodoc // // Deprecated: Use Image Factory/imager instead to build a proper installer. InstallExtraKernelArgs []string `yaml:"extraKernelArgs,omitempty"` // description: | // Allows for supplying the image used to perform the installation. // Image reference for each Talos release can be found on // [GitHub releases page](https://github.com/siderolabs/talos/releases). // examples: // - value: '"ghcr.io/siderolabs/installer:latest"' InstallImage string `yaml:"image,omitempty"` // docgen:nodoc // // Deprecated: Use custom `InstallImage` instead. InstallExtensions []InstallExtensionConfig `yaml:"extensions,omitempty"` // docgen:nodoc // // Deprecated: It never worked. InstallBootloader *bool `yaml:"bootloader,omitempty"` // description: | // Indicates if the installation disk should be wiped at installation time. // Defaults to `true`. // values: // - true // - yes // - false // - no InstallWipe *bool `yaml:"wipe"` // description: | // Indicates if MBR partition should be marked as bootable (active). // Should be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme. InstallLegacyBIOSSupport *bool `yaml:"legacyBIOSSupport,omitempty"` // description: | // Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host. // This changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot. InstallGrubUseUKICmdline *bool `yaml:"grubUseUKICmdline,omitempty"` } // InstallDiskSizeMatcher disk size condition parser. // docgen:nodoc type InstallDiskSizeMatcher struct { MatchData InstallDiskSizeMatchData condition string } // MarshalYAML is a custom marshaller for `InstallDiskSizeMatcher`. func (m *InstallDiskSizeMatcher) MarshalYAML() (any, error) { return m.condition, nil } // UnmarshalYAML is a custom unmarshaller for `InstallDiskSizeMatcher`. func (m *InstallDiskSizeMatcher) UnmarshalYAML(unmarshal func(any) error) error { if err := unmarshal(&m.condition); err != nil { return err } m.condition = strings.TrimSpace(m.condition) re := regexp.MustCompile(`(>=|<=|>|<|==)?\b*(.*)$`) parts := re.FindStringSubmatch(m.condition) if len(parts) < 2 { return fmt.Errorf("failed to parse the condition: expected [>=|<=|>|<|==][units], got %s", m.condition) } var op string switch parts[1] { case ">=", "<=", ">", "<", "", "==": op = parts[1] default: return fmt.Errorf("unknown binary operator %s", parts[1]) } size, err := humanize.ParseBytes(strings.TrimSpace(parts[2])) if err != nil { return fmt.Errorf("failed to parse disk size %s: %s", parts[2], err) } m.MatchData = InstallDiskSizeMatchData{ Op: op, Size: size, } return nil } // InstallDiskSizeMatchData contains data for comparison - Op and Size. // //docgen:nodoc type InstallDiskSizeMatchData struct { Op string Size uint64 } // InstallDiskType custom type for disk type selector. type InstallDiskType string // InstallDiskSelector represents a disk query parameters for the install disk lookup. type InstallDiskSelector struct { // description: Disk size. // examples: // - name: Select a disk which size is equal to 4GB. // value: machineInstallDiskSizeMatcherExamples0() // - name: Select a disk which size is greater than 1TB. // value: machineInstallDiskSizeMatcherExamples1() // - name: Select a disk which size is less or equal than 2TB. // value: machineInstallDiskSizeMatcherExamples2() // schema: // type: string Size *InstallDiskSizeMatcher `yaml:"size,omitempty"` // description: Disk name `/sys/block//device/name`. Name string `yaml:"name,omitempty"` // description: Disk model `/sys/block//device/model`. Model string `yaml:"model,omitempty"` // description: Disk serial number `/sys/block//serial`. Serial string `yaml:"serial,omitempty"` // description: Disk modalias `/sys/block//device/modalias`. Modalias string `yaml:"modalias,omitempty"` // description: Disk UUID `/sys/block//uuid`. UUID string `yaml:"uuid,omitempty"` // description: Disk WWID `/sys/block//wwid`. WWID string `yaml:"wwid,omitempty"` // description: Disk Type. // values: // - ssd // - hdd // - nvme // - sd Type InstallDiskType `yaml:"type,omitempty"` // description: | // Disk bus path. // examples: // - value: '"/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0"' // - value: '"/pci0000:00/*"' BusPath string `yaml:"busPath,omitempty"` } // InstallExtensionConfig represents a configuration for a system extension. // // docgen:nodoc type InstallExtensionConfig struct { // description: System extension image. ExtensionImage string `yaml:"image"` } // TimeConfig represents the options for configuring time on a machine. // //docgen:nodoc type TimeConfig struct { // description: | // Indicates if the time service is disabled for the machine. // Defaults to `false`. TimeDisabled *bool `yaml:"disabled,omitempty"` // description: | // Specifies time (NTP) servers to use for setting the system time. // Defaults to `time.cloudflare.com`. // // Talos can also sync to the PTP time source (e.g provided by the hypervisor), // provide the path to the PTP device as "/dev/ptp0" or "/dev/ptp_kvm". TimeServers []string `yaml:"servers,omitempty"` // description: | // Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence. // NTP sync will be still running in the background. // Defaults to "infinity" (waiting forever for time sync) // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ TimeBootTimeout time.Duration `yaml:"bootTimeout,omitempty"` } // RegistriesConfig represents the image pull options. // // docgen:nodoc type RegistriesConfig struct { // description: | // Specifies mirror configuration for each registry host namespace. // This setting allows to configure local pull-through caching registires, // air-gapped installations, etc. // // For example, when pulling an image with the reference `example.com:123/image:v1`, // the `example.com:123` key will be used to lookup the mirror configuration. // // Optionally the `*` key can be used to configure a fallback mirror. // // Registry name is the first segment of image identifier, with 'docker.io' // being default one. RegistryMirrors map[string]*RegistryMirrorConfig `yaml:"mirrors,omitempty"` // description: | // Specifies TLS & auth configuration for HTTPS image registries. // Mutual TLS can be enabled with 'clientIdentity' option. // // The full hostname and port (if not using a default port 443) // should be used as the key. // The fallback key `*` can't be used for TLS configuration. // // TLS configuration can be skipped if registry has trusted // server certificate. RegistryConfig map[string]*RegistryConfig `yaml:"config,omitempty"` } // PodCheckpointer represents the pod-checkpointer config values. // //docgen:nodoc type PodCheckpointer struct { // description: | // The `image` field is an override to the default pod-checkpointer image. PodCheckpointerImage string `yaml:"image,omitempty"` } // CoreDNS represents the CoreDNS config values. type CoreDNS struct { // description: | // Disable coredns deployment on cluster bootstrap. CoreDNSDisabled *bool `yaml:"disabled,omitempty"` // description: | // The `image` field is an override to the default coredns image. CoreDNSImage string `yaml:"image,omitempty"` } // Endpoint represents the endpoint URL parsed out of the machine config. type Endpoint struct { *url.URL } // UnmarshalYAML is a custom unmarshaller for `Endpoint`. func (e *Endpoint) UnmarshalYAML(unmarshal func(any) error) error { var endpoint string if err := unmarshal(&endpoint); err != nil { return err } url, err := url.Parse(endpoint) if err != nil { return err } *e = Endpoint{url} return nil } // MarshalYAML is a custom marshaller for `Endpoint`. func (e *Endpoint) MarshalYAML() (any, error) { return e.URL.String(), nil } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (e *Endpoint) DeepCopyInto(out *Endpoint) { *out = *e if e.URL != nil { in, out := &e.URL, &out.URL *out = new(url.URL) *out = *in } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint. func (e *Endpoint) DeepCopy() *Endpoint { if e == nil { return nil } out := new(Endpoint) e.DeepCopyInto(out) return out } // ControlPlaneConfig represents the control plane configuration options. type ControlPlaneConfig struct { // description: | // Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. // It is single-valued, and may optionally include a port number. // examples: // - value: clusterEndpointExample1() // - value: clusterEndpointExample2() // schema: // type: string // format: uri // pattern: "^https://" Endpoint *Endpoint `yaml:"endpoint"` // description: | // The port that the API server listens on internally. // This may be different than the port portion listed in the endpoint field above. // The default is `6443`. LocalAPIServerPort int `yaml:"localAPIServerPort,omitempty"` } var _ config.APIServer = (*APIServerConfig)(nil) // APIServerConfig represents the kube apiserver configuration options. type APIServerConfig struct { // description: | // The container image used in the API server manifest. // examples: // - value: clusterAPIServerImageExample() ContainerImage string `yaml:"image,omitempty"` // description: | // Extra arguments to supply to the API server. // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string ExtraArgsConfig Args `yaml:"extraArgs,omitempty"` // description: | // Extra volumes to mount to the API server static pod. ExtraVolumesConfig []VolumeMountConfig `yaml:"extraVolumes,omitempty"` // description: | // The `env` field allows for the addition of environment variables for the control plane component. // schema: // type: object // patternProperties: // ".*": // type: string EnvConfig Env `yaml:"env,omitempty"` // description: | // Extra certificate subject alternative names for the API server's certificate. CertSANs []string `yaml:"certSANs,omitempty"` // docgen:nodoc DisablePodSecurityPolicyConfig *bool `yaml:"disablePodSecurityPolicy,omitempty"` // description: | // Configure the API server admission plugins. // examples: // - value: admissionControlConfigExample() AdmissionControlConfig AdmissionPluginConfigList `yaml:"admissionControl,omitempty"` // description: | // Configure the API server audit policy. // examples: // - value: APIServerDefaultAuditPolicy // schema: // type: object AuditPolicyConfig Unstructured `yaml:"auditPolicy,omitempty" merge:"replace"` // description: | // Configure the API server resources. // schema: // type: object ResourcesConfig *ResourcesConfig `yaml:"resources,omitempty"` // description: | // Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration. // examples: // - value: authorizationConfigExample() AuthorizationConfigConfig AuthorizationConfigAuthorizerConfigList `yaml:"authorizationConfig,omitempty"` } // AdmissionPluginConfigList represents the admission plugin configuration list. // //docgen:alias type AdmissionPluginConfigList []*AdmissionPluginConfig // Merge the admission plugin configuration intelligently. func (configs *AdmissionPluginConfigList) Merge(other any) error { otherConfigs, ok := other.(AdmissionPluginConfigList) if !ok { return fmt.Errorf("unexpected type for device merge %T", other) } for _, config := range otherConfigs { if err := configs.mergeConfig(config); err != nil { return err } } return nil } func (configs *AdmissionPluginConfigList) mergeConfig(config *AdmissionPluginConfig) error { var existing *AdmissionPluginConfig for _, c := range *configs { if c.PluginName == config.PluginName { existing = c break } } if existing != nil { return merge.Merge(existing, config) } *configs = append(*configs, config) return nil } // AdmissionPluginConfig represents the API server admission plugin configuration. type AdmissionPluginConfig struct { // description: | // Name is the name of the admission controller. // It must match the registered admission plugin name. PluginName string `yaml:"name"` // description: | // Configuration is an embedded configuration object to be used as the plugin's // configuration. // schema: // type: object PluginConfiguration Unstructured `yaml:"configuration"` } // AuthorizationConfigAuthorizerConfigList represents the authorization config authorizer configuration list. // //docgen:alias type AuthorizationConfigAuthorizerConfigList []*AuthorizationConfigAuthorizerConfig // AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration. type AuthorizationConfigAuthorizerConfig struct { // description: | // Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. AuthorizerType string `yaml:"type"` // description: | // Name is used to describe the authorizer. AuthorizerName string `yaml:"name"` // description: | // webhook is the configuration for the webhook authorizer. // schema: // type: object AuthorizerWebhook Unstructured `yaml:"webhook,omitempty"` } var _ config.ControllerManager = (*ControllerManagerConfig)(nil) // ControllerManagerConfig represents the kube controller manager configuration options. type ControllerManagerConfig struct { // description: | // The container image used in the controller manager manifest. // examples: // - value: clusterControllerManagerImageExample() ContainerImage string `yaml:"image,omitempty"` // description: | // Extra arguments to supply to the controller manager. // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string ExtraArgsConfig Args `yaml:"extraArgs,omitempty"` // description: | // Extra volumes to mount to the controller manager static pod. ExtraVolumesConfig []VolumeMountConfig `yaml:"extraVolumes,omitempty"` // description: | // The `env` field allows for the addition of environment variables for the control plane component. // schema: // type: object // patternProperties: // ".*": // type: string EnvConfig Env `yaml:"env,omitempty"` // description: | // Configure the controller manager resources. // schema: // type: object ResourcesConfig *ResourcesConfig `yaml:"resources,omitempty"` } // ProxyConfig represents the kube proxy configuration options. type ProxyConfig struct { // description: | // Disable kube-proxy deployment on cluster bootstrap. // examples: // - value: new(false) Disabled *bool `yaml:"disabled,omitempty"` // description: | // The container image used in the kube-proxy manifest. // examples: // - value: clusterProxyImageExample() ContainerImage string `yaml:"image,omitempty"` // description: | // proxy mode of kube-proxy. // The default is 'iptables'. ModeConfig string `yaml:"mode,omitempty"` // description: | // Extra arguments to supply to kube-proxy. // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string ExtraArgsConfig Args `yaml:"extraArgs,omitempty"` } var _ config.Scheduler = (*SchedulerConfig)(nil) // SchedulerConfig represents the kube scheduler configuration options. type SchedulerConfig struct { // description: | // The container image used in the scheduler manifest. // examples: // - value: clusterSchedulerImageExample() ContainerImage string `yaml:"image,omitempty"` // description: | // Extra arguments to supply to the scheduler. // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string ExtraArgsConfig Args `yaml:"extraArgs,omitempty"` // description: | // Extra volumes to mount to the scheduler static pod. ExtraVolumesConfig []VolumeMountConfig `yaml:"extraVolumes,omitempty"` // description: | // The `env` field allows for the addition of environment variables for the control plane component. // schema: // type: object // patternProperties: // ".*": // type: string EnvConfig Env `yaml:"env,omitempty"` // description: | // Configure the scheduler resources. // schema: // type: object ResourcesConfig *ResourcesConfig `yaml:"resources,omitempty"` // description: | // Specify custom kube-scheduler configuration. // schema: // type: object SchedulerConfig Unstructured `yaml:"config,omitempty"` } var _ config.Etcd = (*EtcdConfig)(nil) // EtcdConfig represents the etcd configuration options. type EtcdConfig struct { // description: | // The container image used to create the etcd service. // examples: // - value: clusterEtcdImageExample() ContainerImage string `yaml:"image,omitempty"` // description: | // The `ca` is the root certificate authority of the PKI. // It is composed of a base64 encoded `crt` and `key`. // examples: // - value: pemEncodedCertificateExample() // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string RootCA *x509.PEMEncodedCertificateAndKey `yaml:"ca"` // description: | // Extra arguments to supply to etcd. // Note that the following args are not allowed: // // - `name` // - `data-dir` // - `initial-cluster-state` // - `listen-peer-urls` // - `listen-client-urls` // - `cert-file` // - `key-file` // - `trusted-ca-file` // - `peer-client-cert-auth` // - `peer-cert-file` // - `peer-trusted-ca-file` // - `peer-key-file` // examples: // - values: > // Args{ // "initial-cluster": ArgValue{strValue: "https://1.2.3.4:2380"}, // "advertise-client-urls": ArgValue{strValue: "https://1.2.3.4:2379"}, // } // schema: // type: object // additionalProperties: // oneOf: // - type: string // - type: array // items: // type: string EtcdExtraArgs Args `yaml:"extraArgs,omitempty"` // docgen:nodoc // // Deprecated: use EtcdAdvertistedSubnets EtcdSubnet string `yaml:"subnet,omitempty"` // description: | // The `advertisedSubnets` field configures the networks to pick etcd advertised IP from. // // IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`. // Negative subnet matches should be specified last to filter out IPs picked by positive matches. // If not specified, advertised IP is selected as the first routable address of the node. // // examples: // - value: clusterEtcdAdvertisedSubnetsExample() EtcdAdvertisedSubnets []string `yaml:"advertisedSubnets,omitempty"` // description: | // The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections. // // If `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to // `advertisedSubnets`. // // If neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses. // // IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`. // Negative subnet matches should be specified last to filter out IPs picked by positive matches. // If not specified, advertised IP is selected as the first routable address of the node. EtcdListenSubnets []string `yaml:"listenSubnets,omitempty"` } // ClusterNetworkConfig represents kube networking configuration options. type ClusterNetworkConfig struct { // description: | // The CNI used. // Composed of "name" and "urls". // The "name" key supports the following options: "flannel", "custom", and "none". // "flannel" uses Talos-managed Flannel CNI, and that's the default option. // "custom" uses custom manifests that should be provided in "urls". // "none" indicates that Talos will not manage any CNI installation. // examples: // - value: clusterCustomCNIExample() CNI *CNIConfig `yaml:"cni,omitempty"` // description: | // The domain used by Kubernetes DNS. // The default is `cluster.local` // examples: // - value: '"cluster.local"' DNSDomain string `yaml:"dnsDomain"` // description: | // The pod subnet CIDR. // examples: // - value: > // []string{"10.244.0.0/16"} PodSubnet []string `yaml:"podSubnets" merge:"replace"` // description: | // The service subnet CIDR. // examples: // - value: > // []string{"10.96.0.0/12"} ServiceSubnet []string `yaml:"serviceSubnets" merge:"replace"` } // CNIConfig represents the CNI configuration options. type CNIConfig struct { // description: | // Name of CNI to use. // values: // - flannel // - custom // - none CNIName string `yaml:"name,omitempty"` // description: | // URLs containing manifests to apply for the CNI. // Should be present for "custom", must be empty for "flannel" and "none". CNIUrls []string `yaml:"urls,omitempty"` // description: | // Flannel configuration options. CNIFlannel *FlannelCNIConfig `yaml:"flannel,omitempty"` } // FlannelCNIConfig represents the Flannel CNI configuration options. type FlannelCNIConfig struct { // description: | // Extra arguments for 'flanneld'. // examples: // - value: > // []string{"--iface-can-reach=192.168.1.1"} FlanneldExtraArgs []string `yaml:"extraArgs,omitempty"` // description: | // Deploys kube-network-policies along with Flannel. // // This enables Kubernetes Network Policies support in the cluster. FlannelKubeNetworkPoliciesEnabled *bool `yaml:"kubeNetworkPoliciesEnabled,omitempty"` } var _ config.ExternalCloudProvider = (*ExternalCloudProviderConfig)(nil) // ExternalCloudProviderConfig contains external cloud provider configuration. type ExternalCloudProviderConfig struct { // description: | // Enable external cloud provider. // values: // - true // - yes // - false // - no ExternalEnabled *bool `yaml:"enabled,omitempty"` // description: | // A list of urls that point to additional manifests for an external cloud provider. // These will get automatically deployed as part of the bootstrap. // examples: // - value: > // []string{ // "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", // "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", // } ExternalManifests []string `yaml:"manifests,omitempty"` } // AdminKubeconfigConfig contains admin kubeconfig settings. type AdminKubeconfigConfig struct { // description: | // Admin kubeconfig certificate lifetime (default is 1 year). // Field format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes). // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ AdminKubeconfigCertLifetime time.Duration `yaml:"certLifetime,omitempty"` } // MachineDisk represents the options available for partitioning, formatting, and // mounting extra disks. // // docgen:nodoc type MachineDisk struct { // description: The name of the disk to use. DeviceName string `yaml:"device,omitempty"` // description: A list of partitions to create on the disk. DiskPartitions []*DiskPartition `yaml:"partitions,omitempty"` } // DiskSize partition size in bytes. // // docgen:nodoc type DiskSize uint64 // MarshalYAML write as human readable string. func (ds DiskSize) MarshalYAML() (any, error) { if ds%DiskSize(1000) == 0 { bytesString := humanize.Bytes(uint64(ds)) // ensure that stringifying bytes as human readable string // doesn't lose precision parsed, err := humanize.ParseBytes(bytesString) if err == nil && parsed == uint64(ds) { return bytesString, nil } } return uint64(ds), nil } // UnmarshalYAML read from human readable string. func (ds *DiskSize) UnmarshalYAML(unmarshal func(any) error) error { var size string if err := unmarshal(&size); err != nil { return err } s, err := humanize.ParseBytes(size) if err != nil { return err } *ds = DiskSize(s) return nil } // DiskPartition represents the options for a disk partition. // // docgen:nodoc type DiskPartition struct { // description: > // The size of partition: either bytes or human readable representation. If `size:` // is omitted, the partition is sized to occupy the full disk. // examples: // - name: Human readable representation. // value: DiskSize(100000000) // - name: Precise value in bytes. // value: 1024 * 1024 * 1024 // schema: // type: integer DiskSize DiskSize `yaml:"size,omitempty"` // description: // Where to mount the partition. DiskMountPoint string `yaml:"mountpoint,omitempty"` } // EncryptionConfig represents partition encryption settings. // //docgen:nodoc type EncryptionConfig struct { // description: > // Encryption provider to use for the encryption. // examples: // - value: '"luks2"' EncryptionProvider string `yaml:"provider"` // description: > // Defines the encryption keys generation and storage method. EncryptionKeys []*EncryptionKey `yaml:"keys"` // description: > // Cipher kind to use for the encryption. // Depends on the encryption provider. // values: // - aes-xts-plain64 // - xchacha12,aes-adiantum-plain64 // - xchacha20,aes-adiantum-plain64 // examples: // - value: '"aes-xts-plain64"' EncryptionCipher string `yaml:"cipher,omitempty"` // description: > // Defines the encryption key length. EncryptionKeySize uint `yaml:"keySize,omitempty"` // description: > // Defines the encryption sector size. // examples: // - value: '4096' EncryptionBlockSize uint64 `yaml:"blockSize,omitempty"` // description: > // Additional --perf parameters for the LUKS2 encryption. // values: // - no_read_workqueue // - no_write_workqueue // - same_cpu_crypt // examples: // - value: > // []string{"no_read_workqueue","no_write_workqueue"} EncryptionPerfOptions []string `yaml:"options,omitempty"` } // EncryptionKey represents configuration for disk encryption key. // //docgen:nodoc type EncryptionKey struct { // description: > // Key which value is stored in the configuration file. KeyStatic *EncryptionKeyStatic `yaml:"static,omitempty"` // description: > // Deterministically generated key from the node UUID and PartitionLabel. KeyNodeID *EncryptionKeyNodeID `yaml:"nodeID,omitempty"` // description: > // KMS managed encryption key. // examples: // - value: kmsKeyExample() KeyKMS *EncryptionKeyKMS `yaml:"kms,omitempty"` // description: > // Key slot number for LUKS2 encryption. KeySlot int `yaml:"slot"` // description: > // Enable TPM based disk encryption. KeyTPM *EncryptionKeyTPM `yaml:"tpm,omitempty"` } // EncryptionKeyStatic represents throw away key type. // //docgen:nodoc type EncryptionKeyStatic struct { // description: > // Defines the static passphrase value. KeyData string `yaml:"passphrase,omitempty"` } // EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. // //docgen:nodoc type EncryptionKeyKMS struct { // description: > // KMS endpoint to Seal/Unseal the key. KMSEndpoint string `yaml:"endpoint"` } // EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. // //docgen:nodoc type EncryptionKeyTPM struct { // description: > // Check that Secureboot is enabled in the EFI firmware. // // If Secureboot is not enabled, the enrollment of the key will fail. // As the TPM key is anyways bound to the value of PCR 7, // changing Secureboot status or configuration // after the initial enrollment will make the key unusable. TPMCheckSecurebootStatusOnEnroll *bool `yaml:"checkSecurebootStatusOnEnroll,omitempty"` } // EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. // //docgen:nodoc type EncryptionKeyNodeID struct{} // Env represents a set of environment variables. // //docgen:nodoc type Env = map[string]string // ResourcesConfig represents the pod resources. type ResourcesConfig struct { // description: | // Requests configures the reserved cpu/memory resources. // examples: // - name: resources requests. // value: resourcesConfigRequestsExample() // schema: // type: object Requests Unstructured `yaml:"requests,omitempty"` // description: | // Limits configures the maximum cpu/memory resources a container can use. // examples: // - name: resources requests. // value: resourcesConfigLimitsExample() // schema: // type: object Limits Unstructured `yaml:"limits,omitempty"` } // FileMode represents file's permissions. type FileMode os.FileMode // String convert file mode to octal string. func (fm FileMode) String() string { return "0o" + strconv.FormatUint(uint64(fm), 8) } // MarshalYAML encodes as an octal value. func (fm FileMode) MarshalYAML() (any, error) { return &yaml.Node{ Kind: yaml.ScalarNode, Tag: "!!int", Value: fm.String(), }, nil } // MachineFile represents a file to write to disk. type MachineFile struct { // description: The contents of the file. FileContent string `yaml:"content"` // description: The file's permissions in octal. // schema: // type: integer FilePermissions FileMode `yaml:"permissions"` // description: The path of the file. FilePath string `yaml:"path"` // description: The operation to use // values: // - create // - append // - overwrite FileOp string `yaml:"op"` } // ExtraHost represents a host entry in /etc/hosts. // // docgen:nodoc type ExtraHost struct { // description: The IP of the host. HostIP string `yaml:"ip"` // description: The host alias. HostAliases []string `yaml:"aliases"` } // Device represents a network interface. // // docgen:nodoc type Device struct { // description: | // The interface name. // Mutually exclusive with `deviceSelector`. // examples: // - value: '"enp0s3"' DeviceInterface string `yaml:"interface,omitempty"` // description: | // Picks a network device using the selector. // Mutually exclusive with `interface`. // Supports partial match using wildcard syntax. // examples: // - name: select a device with bus prefix 00:*. // value: networkDeviceSelectorExamples()[0] // - name: select a device with mac address matching `*:f0:ab` and `virtio` kernel driver. // value: networkDeviceSelectorExamples()[1] DeviceSelector *NetworkDeviceSelector `yaml:"deviceSelector,omitempty"` // description: | // Assigns static IP addresses to the interface. // An address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed). // examples: // - value: '[]string{"10.5.0.0/16", "192.168.3.7"}' DeviceAddresses []string `yaml:"addresses,omitempty"` // docgen:nodoc DeviceCIDR string `yaml:"cidr,omitempty"` // description: | // A list of routes associated with the interface. // If used in combination with DHCP, these routes will be appended to routes returned by DHCP server. // examples: // - value: networkConfigRoutesExample() DeviceRoutes []*Route `yaml:"routes,omitempty"` // description: Bond specific options. // examples: // - value: networkConfigBondExample() DeviceBond *Bond `yaml:"bond,omitempty"` // description: Bridge specific options. // examples: // - value: networkConfigBridgeExample() DeviceBridge *Bridge `yaml:"bridge,omitempty"` // description: | // Configure this device as a bridge port. // This can be used to dynamically assign network interfaces to a bridge. // examples: // - value: networkConfigDynamicBridgePortsExample() DeviceBridgePort *BridgePort `yaml:"bridgePort,omitempty"` // description: VLAN specific options. DeviceVlans VlanList `yaml:"vlans,omitempty"` // description: | // The interface's MTU. // If used in combination with DHCP, this will override any MTU settings returned from DHCP server. DeviceMTU int `yaml:"mtu,omitempty"` // description: | // Indicates if DHCP should be used to configure the interface. // The following DHCP options are supported: // // - `OptionClasslessStaticRoute` // - `OptionDomainNameServer` // - `OptionDNSDomainSearchList` // - `OptionHostName` // // examples: // - value: true DeviceDHCP *bool `yaml:"dhcp,omitempty"` // description: Indicates if the interface should be ignored (skips configuration). DeviceIgnore *bool `yaml:"ignore,omitempty"` // description: | // Indicates if the interface is a dummy interface. // `dummy` is used to specify that this interface should be a virtual-only, dummy interface. DeviceDummy *bool `yaml:"dummy,omitempty"` // description: | // DHCP specific options. // `dhcp` *must* be set to true for these to take effect. // examples: // - value: networkConfigDHCPOptionsExample() DeviceDHCPOptions *DHCPOptions `yaml:"dhcpOptions,omitempty"` // description: | // Wireguard specific configuration. // Includes things like private key, listen port, peers. // examples: // - name: wireguard server example // value: networkConfigWireguardHostExample() // - name: wireguard peer example // value: networkConfigWireguardPeerExample() DeviceWireguardConfig *DeviceWireguardConfig `yaml:"wireguard,omitempty"` // description: Virtual (shared) IP address configuration. // examples: // - name: layer2 vip example // value: networkConfigVIPLayer2Example() DeviceVIPConfig *DeviceVIPConfig `yaml:"vip,omitempty"` } // DHCPOptions contains options for configuring the DHCP settings for a given interface. // // docgen:nodoc type DHCPOptions struct { // description: The priority of all routes received via DHCP. DHCPRouteMetric uint32 `yaml:"routeMetric"` // description: Enables DHCPv4 protocol for the interface (default is enabled). DHCPIPv4 *bool `yaml:"ipv4,omitempty"` // description: Enables DHCPv6 protocol for the interface (default is disabled). DHCPIPv6 *bool `yaml:"ipv6,omitempty"` // description: Set client DUID (hex string). DHCPDUIDv6 string `yaml:"duidv6,omitempty"` } // DeviceWireguardConfig contains settings for configuring Wireguard network interface. // // docgen:nodoc type DeviceWireguardConfig struct { // description: | // Specifies a private key configuration (base64 encoded). // Can be generated by `wg genkey`. WireguardPrivateKey string `yaml:"privateKey,omitempty"` // description: Specifies a device's listening port. WireguardListenPort int `yaml:"listenPort,omitempty"` // description: Specifies a device's firewall mark. WireguardFirewallMark int `yaml:"firewallMark,omitempty"` // description: Specifies a list of peer configurations to apply to a device. WireguardPeers []*DeviceWireguardPeer `yaml:"peers,omitempty"` } // DeviceWireguardPeer a WireGuard device peer configuration. // // docgen:nodoc type DeviceWireguardPeer struct { // description: | // Specifies the public key of this peer. // Can be extracted from private key by running `wg pubkey < private.key > public.key && cat public.key`. WireguardPublicKey string `yaml:"publicKey,omitempty"` // description: Specifies the endpoint of this peer entry. WireguardEndpoint string `yaml:"endpoint,omitempty"` // description: | // Specifies the persistent keepalive interval for this peer. // Field format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes). // schema: // type: string // pattern: ^[-+]?(((\d+(\.\d*)?|\d*(\.\d+)+)([nuµm]?s|m|h))|0)+$ WireguardPersistentKeepaliveInterval time.Duration `yaml:"persistentKeepaliveInterval,omitempty"` // description: AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. WireguardAllowedIPs []string `yaml:"allowedIPs,omitempty"` } // DeviceVIPConfig contains settings for configuring a Virtual Shared IP on an interface. // // docgen:nodoc type DeviceVIPConfig struct { // description: Specifies the IP address to be used. SharedIP string `yaml:"ip,omitempty"` // description: Specifies the Equinix Metal API settings to assign VIP to the node. EquinixMetalConfig *VIPEquinixMetalConfig `yaml:"equinixMetal,omitempty"` // description: Specifies the Hetzner Cloud API settings to assign VIP to the node. HCloudConfig *VIPHCloudConfig `yaml:"hcloud,omitempty"` } // VIPEquinixMetalConfig contains settings for Equinix Metal VIP management. // // docgen:nodoc type VIPEquinixMetalConfig struct { // description: Specifies the Equinix Metal API Token. EquinixMetalAPIToken string `yaml:"apiToken"` } // VIPHCloudConfig contains settings for Hetzner Cloud VIP management. // // docgen:nodoc type VIPHCloudConfig struct { // description: Specifies the Hetzner Cloud API Token. HCloudAPIToken string `yaml:"apiToken"` } // Bond contains the various options for configuring a bonded interface. // // docgen:nodoc type Bond struct { // description: The interfaces that make up the bond. BondInterfaces []string `yaml:"interfaces"` // description: | // Picks a network device using the selector. // Mutually exclusive with `interfaces`. // Supports partial match using wildcard syntax. // examples: // - name: select a device with bus prefix 00:*, a device with mac address matching `*:f0:ab` and `virtio` kernel driver. // value: networkDeviceSelectorExamples() BondDeviceSelectors []NetworkDeviceSelector `yaml:"deviceSelectors,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. // Not supported at the moment. BondARPIPTarget []string `yaml:"arpIPTarget,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondMode string `yaml:"mode"` // description: | // A bond option. // Please see the official kernel documentation. BondHashPolicy string `yaml:"xmitHashPolicy,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondLACPRate string `yaml:"lacpRate,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. // Not supported at the moment. BondADActorSystem string `yaml:"adActorSystem,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondARPValidate string `yaml:"arpValidate,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondARPAllTargets string `yaml:"arpAllTargets,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondPrimary string `yaml:"primary,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondPrimaryReselect string `yaml:"primaryReselect,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondFailOverMac string `yaml:"failOverMac,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondADSelect string `yaml:"adSelect,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondMIIMon uint32 `yaml:"miimon,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondUpDelay uint32 `yaml:"updelay,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondDownDelay uint32 `yaml:"downdelay,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondARPInterval uint32 `yaml:"arpInterval,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondResendIGMP uint32 `yaml:"resendIgmp,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondMinLinks uint32 `yaml:"minLinks,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondLPInterval uint32 `yaml:"lpInterval,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondPacketsPerSlave uint32 `yaml:"packetsPerSlave,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondNumPeerNotif uint8 `yaml:"numPeerNotif,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondTLBDynamicLB uint8 `yaml:"tlbDynamicLb,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondAllSlavesActive uint8 `yaml:"allSlavesActive,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondUseCarrier *bool `yaml:"useCarrier,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondADActorSysPrio uint16 `yaml:"adActorSysPrio,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondADUserPortKey uint16 `yaml:"adUserPortKey,omitempty"` // description: | // A bond option. // Please see the official kernel documentation. BondPeerNotifyDelay uint32 `yaml:"peerNotifyDelay,omitempty"` } // STP contains the various options for configuring the STP properties of a bridge interface. // // docgen:nodoc type STP struct { // description: Whether Spanning Tree Protocol (STP) is enabled. STPEnabled *bool `yaml:"enabled,omitempty"` } // BridgeVLAN contains the various options for configuring the VLAN properties of a bridge interface. // // docgen:nodoc type BridgeVLAN struct { // description: Whether VLAN filtering is enabled. BridgeVLANFiltering *bool `yaml:"vlanFiltering,omitempty"` } // Bridge contains the various options for configuring a bridge interface. // // docgen:nodoc type Bridge struct { // description: The interfaces that make up the bridge. BridgedInterfaces []string `yaml:"interfaces"` // description: | // Enable STP on this bridge. // Please see the official kernel documentation. BridgeSTP *STP `yaml:"stp,omitempty"` // description: | // Enable VLAN-awareness on this bridge. // Please see the official kernel documentation. BridgeVLAN *BridgeVLAN `yaml:"vlan,omitempty"` } // BridgePort contains settings for assigning a link to a bridge interface. // // docgen:nodoc type BridgePort struct { // description: The name of the bridge master interface BridgePortMaster string `yaml:"master,omitempty"` } // VlanList is a list of *Vlan structures with overridden merge process. // // docgen:nodoc type VlanList []*Vlan // Merge the network interface configuration intelligently. func (vlans *VlanList) Merge(other any) error { otherVlans, ok := other.(VlanList) if !ok { return fmt.Errorf("unexpected type for vlan merge %T", other) } for _, vlan := range otherVlans { if err := vlans.mergeVlan(vlan); err != nil { return err } } return nil } func (vlans *VlanList) mergeVlan(vlan *Vlan) error { var existing *Vlan for _, v := range *vlans { if v.VlanID == vlan.VlanID { existing = v break } } if existing != nil { return merge.Merge(existing, vlan) } *vlans = append(*vlans, vlan) return nil } // Vlan represents vlan settings for a device. // // docgen:nodoc type Vlan struct { // description: The addresses in CIDR notation or as plain IPs to use. VlanAddresses []string `yaml:"addresses,omitempty"` // docgen:nodoc VlanCIDR string `yaml:"cidr,omitempty"` // description: A list of routes associated with the VLAN. VlanRoutes []*Route `yaml:"routes"` // description: Indicates if DHCP should be used. VlanDHCP *bool `yaml:"dhcp,omitempty"` // description: The VLAN's ID. VlanID uint16 `yaml:"vlanId"` // description: The VLAN's MTU. VlanMTU uint32 `yaml:"mtu,omitempty"` // description: The VLAN's virtual IP address configuration. VlanVIP *DeviceVIPConfig `yaml:"vip,omitempty"` // description: | // DHCP specific options. // `dhcp` *must* be set to true for these to take effect. VlanDHCPOptions *DHCPOptions `yaml:"dhcpOptions,omitempty"` } // Route represents a network route. // // docgen:nodoc type Route struct { // description: The route's network (destination). RouteNetwork string `yaml:"network"` // description: The route's gateway (if empty, creates link scope route). RouteGateway string `yaml:"gateway"` // description: The route's source address (optional). RouteSource string `yaml:"source,omitempty"` // description: The optional metric for the route. RouteMetric uint32 `yaml:"metric,omitempty"` // description: The optional MTU for the route. RouteMTU uint32 `yaml:"mtu,omitempty"` } // RegistryMirrorConfig represents mirror configuration for a registry. // // docgen:nodoc type RegistryMirrorConfig struct { // description: | // List of endpoints (URLs) for registry mirrors to use. // Endpoint configures HTTP/HTTPS access mode, host name, // port and path (if path is not set, it defaults to `/v2`). MirrorEndpoints []string `yaml:"endpoints"` // description: | // Use the exact path specified for the endpoint (don't append /v2/). // This setting is often required for setting up multiple mirrors // on a single instance of a registry. MirrorOverridePath *bool `yaml:"overridePath,omitempty"` // description: | // Skip fallback to the upstream endpoint, for example the mirror configuration // for `docker.io` will not fallback to `registry-1.docker.io`. MirrorSkipFallback *bool `yaml:"skipFallback,omitempty"` } // RegistryConfig specifies auth & TLS config per registry. // // docgen:nodoc type RegistryConfig struct { // description: | // The TLS configuration for the registry. // examples: // - value: machineConfigRegistryTLSConfigExample1() // - value: machineConfigRegistryTLSConfigExample2() RegistryTLS *RegistryTLSConfig `yaml:"tls,omitempty"` // description: | // The auth configuration for this registry. // Note: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot. // examples: // - value: machineConfigRegistryAuthConfigExample() RegistryAuth *RegistryAuthConfig `yaml:"auth,omitempty"` } // RegistryAuthConfig specifies authentication configuration for a registry. // // docgen:nodoc type RegistryAuthConfig struct { // description: | // Optional registry authentication. // The meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication). RegistryUsername string `yaml:"username,omitempty"` // description: | // Optional registry authentication. // The meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication). RegistryPassword string `yaml:"password,omitempty"` // description: | // Optional registry authentication. // The meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication). RegistryAuth string `yaml:"auth,omitempty"` // description: | // Optional registry authentication. // The meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication). RegistryIdentityToken string `yaml:"identityToken,omitempty"` } // RegistryTLSConfig specifies TLS config for HTTPS registries. // // docgen:nodoc type RegistryTLSConfig struct { // description: | // Enable mutual TLS authentication with the registry. // Client certificate and key should be base64-encoded. // examples: // - value: pemEncodedCertificateExample() // schema: // type: object // additionalProperties: false // properties: // crt: // type: string // key: // type: string TLSClientIdentity *x509.PEMEncodedCertificateAndKey `yaml:"clientIdentity,omitempty"` // description: | // CA registry certificate to add the list of trusted certificates. // Certificate should be base64-encoded. // schema: // type: string TLSCA Base64Bytes `yaml:"ca,omitempty"` // description: | // Skip TLS server certificate verification (not recommended). TLSInsecureSkipVerify *bool `yaml:"insecureSkipVerify,omitempty"` } // SystemDiskEncryptionConfig specifies system disk partitions encryption settings. // //docgen:nodoc type SystemDiskEncryptionConfig struct { // description: | // State partition encryption. StatePartition *EncryptionConfig `yaml:"state,omitempty"` // description: | // Ephemeral partition encryption. EphemeralPartition *EncryptionConfig `yaml:"ephemeral,omitempty"` } var _ config.Features = (*FeaturesConfig)(nil) // FeaturesConfig describes individual Talos features that can be switched on or off. type FeaturesConfig struct { // docgen:nodoc RBAC *bool `yaml:"rbac,omitempty"` // docgen:nodoc // // Deprecated: use HostConfig instead. StableHostname *bool `yaml:"stableHostname,omitempty"` // description: | // Configure Talos API access from Kubernetes pods. // // This feature is disabled if the feature config is not specified. // examples: // - value: kubernetesTalosAPIAccessConfigExample() KubernetesTalosAPIAccessConfig *KubernetesTalosAPIAccessConfig `yaml:"kubernetesTalosAPIAccess,omitempty"` // docgen:nodoc ApidCheckExtKeyUsage *bool `yaml:"apidCheckExtKeyUsage,omitempty"` // description: | // Enable XFS project quota support for EPHEMERAL partition and user disks. // Also enables kubelet tracking of ephemeral disk usage in the kubelet via quota. DiskQuotaSupport *bool `yaml:"diskQuotaSupport,omitempty"` // description: | // KubePrism - local proxy/load balancer on defined port that will distribute // requests to all API servers in the cluster. KubePrismSupport *KubePrism `yaml:"kubePrism,omitempty"` // description: | // Configures host DNS caching resolver. HostDNSSupport *HostDNSConfig `yaml:"hostDNS,omitempty"` // description: | // Enable Image Cache feature. ImageCacheSupport *ImageCacheConfig `yaml:"imageCache,omitempty"` // description: | // Select the node address sort algorithm. // The 'v1' algorithm sorts addresses by the address itself. // The 'v2' algorithm prefers more specific prefixes. // If unset, defaults to 'v1'. FeatureNodeAddressSortAlgorithm string `yaml:"nodeAddressSortAlgorithm,omitempty"` } // KubePrism describes the configuration for the KubePrism load balancer. type KubePrism struct { // description: | // Enable KubePrism support - will start local load balancing proxy. ServerEnabled *bool `yaml:"enabled,omitempty"` // description: | // KubePrism port. ServerPort int `yaml:"port,omitempty"` } // ImageCacheConfig describes the configuration for the Image Cache feature. type ImageCacheConfig struct { // description: | // Enable local image cache. CacheLocalEnabled *bool `yaml:"localEnabled,omitempty"` } // KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods. type KubernetesTalosAPIAccessConfig struct { // description: | // Enable Talos API access from Kubernetes pods. AccessEnabled *bool `yaml:"enabled,omitempty"` // description: | // The list of Talos API roles which can be granted for access from Kubernetes pods. // // Empty list means that no roles can be granted, so access is blocked. AccessAllowedRoles []string `yaml:"allowedRoles,omitempty"` // description: | // The list of Kubernetes namespaces Talos API access is available from. AccessAllowedKubernetesNamespaces []string `yaml:"allowedKubernetesNamespaces,omitempty"` } // HostDNSConfig describes the configuration for the host DNS resolver. type HostDNSConfig struct { // description: | // Enable host DNS caching resolver. HostDNSEnabled *bool `yaml:"enabled,omitempty"` // description: | // Use the host DNS resolver as upstream for Kubernetes CoreDNS pods. // // When enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of // using configured upstream DNS resolvers directly). HostDNSForwardKubeDNSToHost *bool `yaml:"forwardKubeDNSToHost,omitempty"` // description: | // Resolve member hostnames using the host DNS resolver. // // When enabled, cluster member hostnames and node names are resolved using the host DNS resolver. // This requires service discovery to be enabled. HostDNSResolveMemberNames *bool `yaml:"resolveMemberNames,omitempty"` } // VolumeMountConfig struct describes extra volume mount for the static pods. type VolumeMountConfig struct { // description: | // Path on the host. // examples: // - value: '"/var/lib/auth"' VolumeHostPath string `yaml:"hostPath"` // description: | // Path in the container. // examples: // - value: '"/etc/kubernetes/auth"' VolumeMountPath string `yaml:"mountPath"` // description: | // Mount the volume read only. // examples: // - value: true VolumeReadOnly bool `yaml:"readonly,omitempty"` } // ClusterInlineManifests is a list of ClusterInlineManifest. // //docgen:alias type ClusterInlineManifests []ClusterInlineManifest // UnmarshalYAML implements yaml.Unmarshaler. func (manifests *ClusterInlineManifests) UnmarshalYAML(value *yaml.Node) error { var result []ClusterInlineManifest if err := value.Decode(&result); err != nil { return err } for i := range result { result[i].InlineManifestContents = strings.TrimLeft(result[i].InlineManifestContents, "\t\n\v\f\r") } *manifests = result return nil } // ClusterInlineManifest struct describes inline bootstrap manifests for the user. type ClusterInlineManifest struct { // description: | // Name of the manifest. // Name should be unique. // examples: // - value: '"csi"' InlineManifestName string `yaml:"name"` // description: | // Manifest contents as a string. // examples: // - value: '"/etc/kubernetes/auth"' InlineManifestContents string `yaml:"contents"` } // NetworkKubeSpan struct describes KubeSpan configuration. // // Deprecated: Use KubeSpanConfig document instead. // // docgen:nodoc type NetworkKubeSpan struct { // description: | // Enable the KubeSpan feature. // Cluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled. KubeSpanEnabled *bool `yaml:"enabled,omitempty"` // description: | // Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node. // If disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel, // and KubeSpan handles the node-to-node traffic. // If enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly. // When enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which // is not always the case with CNIs not relying on Kubernetes for IPAM. KubeSpanAdvertiseKubernetesNetworks *bool `yaml:"advertiseKubernetesNetworks,omitempty"` // description: | // Skip sending traffic via KubeSpan if the peer connection state is not up. // This provides configurable choice between connectivity and security: either traffic is always // forced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly // to the peer if Wireguard connection can't be established. KubeSpanAllowDownPeerBypass *bool `yaml:"allowDownPeerBypass,omitempty"` // description: | // KubeSpan can collect and publish extra endpoints for each member of the cluster // based on Wireguard endpoint information for each peer. // This feature is disabled by default, don't enable it // with high number of peers (>50) in the KubeSpan network (performance issues). KubeSpanHarvestExtraEndpoints *bool `yaml:"harvestExtraEndpoints,omitempty"` // description: | // KubeSpan link MTU size. // Default value is 1420. KubeSpanMTU *uint32 `yaml:"mtu,omitempty"` // description: | // KubeSpan advanced filtering of network addresses . // // Settings in this section are optional, and settings apply only to the node. KubeSpanFilters *KubeSpanFilters `yaml:"filters,omitempty"` } // KubeSpanFilters struct describes KubeSpan advanced network addresses filtering. // // docgen:nodoc type KubeSpanFilters struct { // description: | // Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections. // // By default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works. // // Default value: no filtering. // examples: // - name: Exclude addresses in 192.168.0.0/16 subnet. // value: '[]string{"0.0.0.0/0", "!192.168.0.0/16", "::/0"}' KubeSpanFiltersEndpoints []string `yaml:"endpoints,omitempty"` // description: | // Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan. // // By default, all networks are advertised. // Use this filter to exclude some networks from being advertised. // // Note: excluded networks will not be reachable over KubeSpan, so make sure // these networks are still reachable via some other route (e.g., direct connection). // // Default value: no filtering. // examples: // - name: Exclude private networks from being advertised. // value: '[]string{"172.16.0.0/12"}' KubeSpanFiltersExcludeAdvertisedNetworks []string `yaml:"excludeAdvertisedNetworks,omitempty"` } // NetworkDeviceSelector struct describes network device selector. // // docgen:nodoc type NetworkDeviceSelector struct { // description: PCI, USB bus prefix, supports matching by wildcard. NetworkDeviceBus string `yaml:"busPath,omitempty"` // description: Device hardware (MAC) address, supports matching by wildcard. NetworkDeviceHardwareAddress string `yaml:"hardwareAddr,omitempty"` // description: | // Device permanent hardware address, supports matching by wildcard. // The permanent address doesn't change when the link is enslaved to a bond, // so it's recommended to use this field for bond members. NetworkDevicePermanentAddress string `yaml:"permanentAddr,omitempty"` // description: PCI ID (vendor ID, product ID), supports matching by wildcard. NetworkDevicePCIID string `yaml:"pciID,omitempty"` // description: Kernel driver, supports matching by wildcard. NetworkDeviceKernelDriver string `yaml:"driver,omitempty"` // description: Select only physical devices. NetworkDevicePhysical *bool `yaml:"physical,omitempty"` } // ClusterDiscoveryConfig struct configures cluster membership discovery. type ClusterDiscoveryConfig struct { // description: | // Enable the cluster membership discovery feature. // Cluster discovery is based on individual registries which are configured under the registries field. DiscoveryEnabled *bool `yaml:"enabled,omitempty"` // description: | // Configure registries used for cluster member discovery. DiscoveryRegistries DiscoveryRegistriesConfig `yaml:"registries"` } // DiscoveryRegistriesConfig struct configures cluster membership discovery. type DiscoveryRegistriesConfig struct { // description: | // Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information // as annotations on the Node resources. // // This feature is deprecated as it is not compatible with Kubernetes 1.32+. // See https://github.com/siderolabs/talos/issues/9980 for more information. RegistryKubernetes RegistryKubernetesConfig `yaml:"kubernetes"` // description: | // Service registry is using an external service to push and pull information about cluster members. RegistryService RegistryServiceConfig `yaml:"service"` } // RegistryKubernetesConfig struct configures Kubernetes discovery registry. type RegistryKubernetesConfig struct { // description: | // Disable Kubernetes discovery registry. RegistryDisabled *bool `yaml:"disabled,omitempty"` } // RegistryServiceConfig struct configures Kubernetes discovery registry. type RegistryServiceConfig struct { // description: | // Disable external service discovery registry. RegistryDisabled *bool `yaml:"disabled,omitempty"` // description: | // External service endpoint. // examples: // - value: constants.DefaultDiscoveryServiceEndpoint RegistryEndpoint string `yaml:"endpoint,omitempty"` } // UdevConfig describes how the udev system should be configured. type UdevConfig struct { // description: | // List of udev rules to apply to the udev system UdevRules []string `yaml:"rules,omitempty"` } // LoggingConfig struct configures Talos logging. type LoggingConfig struct { // description: | // Logging destination. LoggingDestinations []LoggingDestination `yaml:"destinations"` } // LoggingDestination struct configures Talos logging destination. type LoggingDestination struct { // description: | // Where to send logs. Supported protocols are "tcp" and "udp". // examples: // - value: loggingEndpointExample1() // - value: loggingEndpointExample2() LoggingEndpoint *Endpoint `yaml:"endpoint"` // description: | // Logs format. // values: // - json_lines LoggingFormat string `yaml:"format"` // description: | // Extra tags (key-value) pairs to attach to every log message sent. LoggingExtraTags map[string]string `yaml:"extraTags,omitempty"` } // KernelConfig struct configures Talos Linux kernel. type KernelConfig struct { // description: | // Kernel modules to load. KernelModules []*KernelModuleConfig `yaml:"modules,omitempty"` } // KernelModuleConfig struct configures Linux kernel modules to load. type KernelModuleConfig struct { // description: | // Module name. ModuleName string `yaml:"name"` // description: | // Module parameters, changes applied after reboot. ModuleParameters []string `yaml:"parameters,omitempty"` } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_types_doc.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package v1alpha1 import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/constants" ) func (Config) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "Config", Comments: [3]string{"" /* encoder.HeadComment */, "Config defines the v1alpha1.Config Talos machine configuration document." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "Config defines the v1alpha1.Config Talos machine configuration document.", Fields: []encoder.Doc{ { Name: "version", Type: "string", Note: "", Description: "Indicates the schema used to decode the contents.", Comments: [3]string{"" /* encoder.HeadComment */, "Indicates the schema used to decode the contents." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "v1alpha1", }, }, { Name: "debug", Type: "bool", Note: "", Description: "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable verbose logging to the console." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, {}, { Name: "machine", Type: "MachineConfig", Note: "", Description: "Provides machine specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Provides machine specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "cluster", Type: "ClusterConfig", Note: "", Description: "Provides cluster specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Provides cluster specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", configExample()) return doc } func (MachineConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineConfig", Comments: [3]string{"" /* encoder.HeadComment */, "MachineConfig represents the machine-specific config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineConfig represents the machine-specific config values.", AppearsIn: []encoder.Appearance{ { TypeName: "Config", FieldName: "machine", }, }, Fields: []encoder.Doc{ { Name: "type", Type: "string", Note: "", Description: "Defines the role of the machine within the cluster.\n\n**Control Plane**\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\n**Worker**\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as \"join\"; that value is still supported but deprecated.", Comments: [3]string{"" /* encoder.HeadComment */, "Defines the role of the machine within the cluster." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "controlplane", "worker", }, }, { Name: "token", Type: "string", Note: "Warning: It is important to ensure that this token is correct since a machine's certificate has a short TTL by default.\n", Description: "The `token` is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.", Comments: [3]string{"" /* encoder.HeadComment */, "The `token` is used by a machine to join the PKI of the cluster." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ca", Type: "PEMEncodedCertificateAndKey", Note: "", Description: "The root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", Comments: [3]string{"" /* encoder.HeadComment */, "The root certificate authority of the PKI." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "acceptedCAs", Type: "[]PEMEncodedCertificate", Note: "", Description: "The certificates issued by certificate authorities are accepted in addition to issuing 'ca'.\nIt is composed of a base64 encoded `crt``.", Comments: [3]string{"" /* encoder.HeadComment */, "The certificates issued by certificate authorities are accepted in addition to issuing 'ca'." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "certSANs", Type: "[]string", Note: "", Description: "Extra certificate subject alternative names for the machine's certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate's SANs.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra certificate subject alternative names for the machine's certificate." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "controlPlane", Type: "MachineControlPlaneConfig", Note: "", Description: "Provides machine specific control plane configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Provides machine specific control plane configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "kubelet", Type: "KubeletConfig", Note: "", Description: "Used to provide additional options to the kubelet.", Comments: [3]string{"" /* encoder.HeadComment */, "Used to provide additional options to the kubelet." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "pods", Type: "[]Unstructured", Note: "", Description: "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn't validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.", Comments: [3]string{"" /* encoder.HeadComment */, "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, {}, { Name: "install", Type: "InstallConfig", Note: "", Description: "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.", Comments: [3]string{"" /* encoder.HeadComment */, "Used to provide instructions for installations." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "files", Type: "[]MachineFile", Note: "Note: The specified `path` is relative to `/var`.\n", Description: "Allows the addition of user specified files.\nThe value of `op` can be `create`, `overwrite`, or `append`.\nIn the case of `create`, `path` must not exist.\nIn the case of `overwrite`, and `append`, `path` must be a valid file.\nIf an `op` value of `append` is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.", Comments: [3]string{"" /* encoder.HeadComment */, "Allows the addition of user specified files." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, {}, { Name: "sysctls", Type: "map[string]string", Note: "", Description: "Used to configure the machine's sysctls.", Comments: [3]string{"" /* encoder.HeadComment */, "Used to configure the machine's sysctls." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "sysfs", Type: "map[string]string", Note: "", Description: "Used to configure the machine's sysfs.", Comments: [3]string{"" /* encoder.HeadComment */, "Used to configure the machine's sysfs." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, {}, { Name: "features", Type: "FeaturesConfig", Note: "", Description: "Features describe individual Talos features that can be switched on or off.", Comments: [3]string{"" /* encoder.HeadComment */, "Features describe individual Talos features that can be switched on or off." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "udev", Type: "UdevConfig", Note: "", Description: "Configures the udev system.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the udev system." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "logging", Type: "LoggingConfig", Note: "", Description: "Configures the logging system.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the logging system." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "kernel", Type: "KernelConfig", Note: "", Description: "Configures the kernel.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the kernel." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "seccompProfiles", Type: "[]MachineSeccompProfile", Note: "", Description: "Configures the seccomp profiles for the machine.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the seccomp profiles for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "baseRuntimeSpecOverrides", Type: "Unstructured", Note: "", Description: "Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.", Comments: [3]string{"" /* encoder.HeadComment */, "Override (patch) settings in the default OCI runtime spec for CRI containers." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nodeLabels", Type: "map[string]string", Note: "", Description: "Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the node labels for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nodeAnnotations", Type: "map[string]string", Note: "", Description: "Configures the node annotations for the machine.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the node annotations for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nodeTaints", Type: "map[string]string", Note: "", Description: "Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the node taints for the machine. Effect is optional." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineConfigExample()) doc.Fields[1].AddExample("example token", "328hom.uqjzh6jnn2eie9oi") doc.Fields[2].AddExample("machine CA example", pemEncodedCertificateExample()) doc.Fields[4].AddExample("Uncomment this to enable SANs.", []string{"10.0.0.10", "172.16.0.10", "192.168.0.10"}) doc.Fields[5].AddExample("ControlPlane definition example.", machineControlplaneExample()) doc.Fields[6].AddExample("Kubelet definition example.", machineKubeletExample()) doc.Fields[7].AddExample("nginx static pod.", machinePodsExample()) doc.Fields[10].AddExample("MachineInstall config usage example.", machineInstallExample()) doc.Fields[11].AddExample("MachineFiles usage example.", machineFilesExample()) doc.Fields[14].AddExample("MachineSysctls usage example.", machineSysctlsExample()) doc.Fields[15].AddExample("MachineSysfs usage example.", machineSysfsExample()) doc.Fields[18].AddExample("", machineFeaturesExample()) doc.Fields[19].AddExample("", machineUdevExample()) doc.Fields[20].AddExample("", machineLoggingExample()) doc.Fields[21].AddExample("", machineKernelExample()) doc.Fields[22].AddExample("", machineSeccompExample()) doc.Fields[23].AddExample("override default open file limit", machineBaseRuntimeSpecOverridesExample()) doc.Fields[24].AddExample("node labels example.", map[string]string{"exampleLabel": "exampleLabelValue"}) doc.Fields[25].AddExample("node annotations example.", map[string]string{"customer.io/rack": "r13a25"}) doc.Fields[26].AddExample("node taints example.", map[string]string{"exampleTaint": "exampleTaintValue:NoSchedule"}) return doc } func (MachineSeccompProfile) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineSeccompProfile", Comments: [3]string{"" /* encoder.HeadComment */, "MachineSeccompProfile defines seccomp profiles for the machine." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineSeccompProfile defines seccomp profiles for the machine.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "seccompProfiles", }, }, Fields: []encoder.Doc{ { Name: "name", Type: "string", Note: "", Description: "The `name` field is used to provide the file name of the seccomp profile.", Comments: [3]string{"" /* encoder.HeadComment */, "The `name` field is used to provide the file name of the seccomp profile." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "value", Type: "Unstructured", Note: "", Description: "The `value` field is used to provide the seccomp profile.", Comments: [3]string{"" /* encoder.HeadComment */, "The `value` field is used to provide the seccomp profile." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineSeccompExample()) return doc } func (ClusterConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ClusterConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ClusterConfig represents the cluster-wide config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ClusterConfig represents the cluster-wide config values.", AppearsIn: []encoder.Appearance{ { TypeName: "Config", FieldName: "cluster", }, }, Fields: []encoder.Doc{ { Name: "id", Type: "string", Note: "", Description: "Globally unique identifier for this cluster (base64 encoded random 32 bytes).", Comments: [3]string{"" /* encoder.HeadComment */, "Globally unique identifier for this cluster (base64 encoded random 32 bytes)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "secret", Type: "string", Note: "", Description: "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.", Comments: [3]string{"" /* encoder.HeadComment */, "Shared secret of cluster (base64 encoded random 32 bytes)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "controlPlane", Type: "ControlPlaneConfig", Note: "", Description: "Provides control plane specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Provides control plane specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "clusterName", Type: "string", Note: "", Description: "Configures the cluster's name.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures the cluster's name." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "network", Type: "ClusterNetworkConfig", Note: "", Description: "Provides cluster specific network configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Provides cluster specific network configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "token", Type: "string", Note: "", Description: "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.", Comments: [3]string{"" /* encoder.HeadComment */, "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "aescbcEncryptionSecret", Type: "string", Note: "", Description: "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with AESCBC.", Comments: [3]string{"" /* encoder.HeadComment */, "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "secretboxEncryptionSecret", Type: "string", Note: "", Description: "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.", Comments: [3]string{"" /* encoder.HeadComment */, "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ca", Type: "PEMEncodedCertificateAndKey", Note: "", Description: "The base64 encoded root certificate authority used by Kubernetes.", Comments: [3]string{"" /* encoder.HeadComment */, "The base64 encoded root certificate authority used by Kubernetes." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "acceptedCAs", Type: "[]PEMEncodedCertificate", Note: "", Description: "The list of base64 encoded accepted certificate authorities used by Kubernetes.", Comments: [3]string{"" /* encoder.HeadComment */, "The list of base64 encoded accepted certificate authorities used by Kubernetes." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "aggregatorCA", Type: "PEMEncodedCertificateAndKey", Note: "", Description: "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.", Comments: [3]string{"" /* encoder.HeadComment */, "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "serviceAccount", Type: "PEMEncodedKey", Note: "", Description: "The base64 encoded private key for service account token generation.", Comments: [3]string{"" /* encoder.HeadComment */, "The base64 encoded private key for service account token generation." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "apiServer", Type: "APIServerConfig", Note: "", Description: "API server specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "API server specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "controllerManager", Type: "ControllerManagerConfig", Note: "", Description: "Controller manager server specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Controller manager server specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "proxy", Type: "ProxyConfig", Note: "", Description: "Kube-proxy server-specific configuration options", Comments: [3]string{"" /* encoder.HeadComment */, "Kube-proxy server-specific configuration options" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "scheduler", Type: "SchedulerConfig", Note: "", Description: "Scheduler server specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Scheduler server specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "discovery", Type: "ClusterDiscoveryConfig", Note: "", Description: "Configures cluster member discovery.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures cluster member discovery." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "etcd", Type: "EtcdConfig", Note: "", Description: "Etcd specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Etcd specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "coreDNS", Type: "CoreDNS", Note: "", Description: "Core DNS specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Core DNS specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "externalCloudProvider", Type: "ExternalCloudProviderConfig", Note: "", Description: "External cloud provider configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "External cloud provider configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraManifests", Type: "[]string", Note: "", Description: "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of urls that point to additional manifests." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraManifestHeaders", Type: "map[string]string", Note: "", Description: "A map of key value pairs that will be added while fetching the extraManifests.", Comments: [3]string{"" /* encoder.HeadComment */, "A map of key value pairs that will be added while fetching the extraManifests." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "inlineManifests", Type: "[]ClusterInlineManifest", Note: "", Description: "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of inline Kubernetes manifests." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "adminKubeconfig", Type: "AdminKubeconfigConfig", Note: "", Description: "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.", Comments: [3]string{"" /* encoder.HeadComment */, "Settings for admin kubeconfig generation." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, { Name: "allowSchedulingOnControlPlanes", Type: "bool", Note: "", Description: "Allows running workload on control-plane nodes.", Comments: [3]string{"" /* encoder.HeadComment */, "Allows running workload on control-plane nodes." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, }, } doc.AddExample("", clusterConfigExample()) doc.Fields[2].AddExample("Setting controlplane endpoint address to 1.2.3.4 and port to 443 example.", clusterControlPlaneExample()) doc.Fields[4].AddExample("Configuring with flannel CNI and setting up subnets.", clusterNetworkExample()) doc.Fields[5].AddExample("Bootstrap token example (do not use in production!).", "wlzjyw.bei2zfylhs2by0wd") doc.Fields[6].AddExample("Decryption secret example (do not use in production!).", "z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM=") doc.Fields[7].AddExample("Decryption secret example (do not use in production!).", "z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM=") doc.Fields[8].AddExample("ClusterCA example.", pemEncodedCertificateExample()) doc.Fields[10].AddExample("AggregatorCA example.", pemEncodedCertificateExample()) doc.Fields[11].AddExample("AggregatorCA example.", pemEncodedKeyExample()) doc.Fields[12].AddExample("", clusterAPIServerExample()) doc.Fields[13].AddExample("", clusterControllerManagerExample()) doc.Fields[14].AddExample("", clusterProxyExample()) doc.Fields[15].AddExample("", clusterSchedulerExample()) doc.Fields[16].AddExample("", clusterDiscoveryExample()) doc.Fields[17].AddExample("", clusterEtcdExample()) doc.Fields[18].AddExample("", clusterCoreDNSExample()) doc.Fields[19].AddExample("", clusterExternalCloudProviderConfigExample()) doc.Fields[20].AddExample("", []string{ "https://www.example.com/manifest1.yaml", "https://www.example.com/manifest2.yaml", }) doc.Fields[21].AddExample("", map[string]string{ "Token": "1234567", "X-ExtraInfo": "info", }) doc.Fields[22].AddExample("", clusterInlineManifestsExample()) doc.Fields[23].AddExample("", clusterAdminKubeconfigExample()) doc.Fields[25].AddExample("", true) return doc } func (LinuxIDMapping) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LinuxIDMapping", Comments: [3]string{"" /* encoder.HeadComment */, "LinuxIDMapping represents the Linux ID mapping." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LinuxIDMapping represents the Linux ID mapping.", AppearsIn: []encoder.Appearance{ { TypeName: "ExtraMount", FieldName: "uidMappings", }, { TypeName: "ExtraMount", FieldName: "gidMappings", }, }, Fields: []encoder.Doc{ { Name: "containerID", Type: "uint32", Note: "", Description: "ContainerID is the starting UID/GID in the container.", Comments: [3]string{"" /* encoder.HeadComment */, "ContainerID is the starting UID/GID in the container." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hostID", Type: "uint32", Note: "", Description: "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'.", Comments: [3]string{"" /* encoder.HeadComment */, "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "size", Type: "uint32", Note: "", Description: "Size is the number of IDs to be mapped.", Comments: [3]string{"" /* encoder.HeadComment */, "Size is the number of IDs to be mapped." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (ExtraMount) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExtraMount", Comments: [3]string{"" /* encoder.HeadComment */, "ExtraMount wraps OCI Mount specification." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExtraMount wraps OCI Mount specification.", AppearsIn: []encoder.Appearance{ { TypeName: "KubeletConfig", FieldName: "extraMounts", }, }, Fields: []encoder.Doc{ { Name: "destination", Type: "string", Note: "", Description: "Destination is the absolute path where the mount will be placed in the container.", Comments: [3]string{"" /* encoder.HeadComment */, "Destination is the absolute path where the mount will be placed in the container." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "type", Type: "string", Note: "", Description: "Type specifies the mount kind.", Comments: [3]string{"" /* encoder.HeadComment */, "Type specifies the mount kind." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "source", Type: "string", Note: "", Description: "Source specifies the source path of the mount.", Comments: [3]string{"" /* encoder.HeadComment */, "Source specifies the source path of the mount." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "options", Type: "[]string", Note: "", Description: "Options are fstab style mount options.", Comments: [3]string{"" /* encoder.HeadComment */, "Options are fstab style mount options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "uidMappings", Type: "[]LinuxIDMapping", Note: "", Description: "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", Comments: [3]string{"" /* encoder.HeadComment */, "UID/GID mappings used for changing file owners w/o calling chown, fs should support it." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "gidMappings", Type: "[]LinuxIDMapping", Note: "", Description: "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", Comments: [3]string{"" /* encoder.HeadComment */, "UID/GID mappings used for changing file owners w/o calling chown, fs should support it." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", kubeletExtraMountsExample()) return doc } func (MachineControlPlaneConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineControlPlaneConfig", Comments: [3]string{"" /* encoder.HeadComment */, "MachineControlPlaneConfig machine specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineControlPlaneConfig machine specific configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "controlPlane", }, }, Fields: []encoder.Doc{ { Name: "controllerManager", Type: "MachineControllerManagerConfig", Note: "", Description: "Controller manager machine specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Controller manager machine specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "scheduler", Type: "MachineSchedulerConfig", Note: "", Description: "Scheduler machine specific configuration options.", Comments: [3]string{"" /* encoder.HeadComment */, "Scheduler machine specific configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("ControlPlane definition example.", machineControlplaneExample()) return doc } func (MachineControllerManagerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineControllerManagerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "MachineControllerManagerConfig represents the machine specific ControllerManager config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineControllerManagerConfig represents the machine specific ControllerManager config values.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineControlPlaneConfig", FieldName: "controllerManager", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable kube-controller-manager on the node.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable kube-controller-manager on the node." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (MachineSchedulerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineSchedulerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "MachineSchedulerConfig represents the machine specific Scheduler config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineSchedulerConfig represents the machine specific Scheduler config values.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineControlPlaneConfig", FieldName: "scheduler", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable kube-scheduler on the node.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable kube-scheduler on the node." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (KubeletConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubeletConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubeletConfig represents the kubelet config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubeletConfig represents the kubelet config values.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "kubelet", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "The `image` field is an optional reference to an alternative kubelet image.", Comments: [3]string{"" /* encoder.HeadComment */, "The `image` field is an optional reference to an alternative kubelet image." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "clusterDNS", Type: "[]string", Note: "", Description: "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.", Comments: [3]string{"" /* encoder.HeadComment */, "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "The `extraArgs` field is used to provide additional flags to the kubelet.", Comments: [3]string{"" /* encoder.HeadComment */, "The `extraArgs` field is used to provide additional flags to the kubelet." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraMounts", Type: "[]ExtraMount", Note: "", Description: "The `extraMounts` field is used to add additional mounts to the kubelet container.\nNote that either `bind` or `rbind` are required in the `options`.", Comments: [3]string{"" /* encoder.HeadComment */, "The `extraMounts` field is used to add additional mounts to the kubelet container." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraConfig", Type: "Unstructured", Note: "", Description: "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", Comments: [3]string{"" /* encoder.HeadComment */, "The `extraConfig` field is used to provide kubelet configuration overrides." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "credentialProviderConfig", Type: "Unstructured", Note: "", Description: "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "defaultRuntimeSeccompProfileEnabled", Type: "bool", Note: "", Description: "Enable container runtime default Seccomp profile.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable container runtime default Seccomp profile." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, { Name: "registerWithFQDN", Type: "bool", Note: "", Description: "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.", Comments: [3]string{"" /* encoder.HeadComment */, "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, { Name: "nodeIP", Type: "KubeletNodeIPConfig", Note: "", Description: "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.", Comments: [3]string{"" /* encoder.HeadComment */, "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "skipNodeRegistration", Type: "bool", Note: "", Description: "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.", Comments: [3]string{"" /* encoder.HeadComment */, "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, { Name: "disableManifestsDirectory", Type: "bool", Note: "", Description: "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt's recommended to configure static pods with the \"pods\" key instead.", Comments: [3]string{"" /* encoder.HeadComment */, "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, }, } doc.AddExample("Kubelet definition example.", machineKubeletExample()) doc.Fields[0].AddExample("", kubeletImageExample()) doc.Fields[1].AddExample("", []string{"10.96.0.10", "169.254.2.53"}) doc.Fields[2].AddExample("", Args{ "key": ArgValue{strValue: "value"}, }) doc.Fields[2].AddExample("", Args{ "key": ArgValue{listValue: []string{"value1", "value2"}}, }) doc.Fields[3].AddExample("", kubeletExtraMountsExample()) doc.Fields[4].AddExample("", kubeletExtraConfigExample()) doc.Fields[5].AddExample("", kubeletCredentialProviderConfigExample()) doc.Fields[8].AddExample("", kubeletNodeIPExample()) return doc } func (KubeletNodeIPConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubeletNodeIPConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubeletNodeIPConfig represents the kubelet node IP configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubeletNodeIPConfig represents the kubelet node IP configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "KubeletConfig", FieldName: "nodeIP", }, }, Fields: []encoder.Doc{ { Name: "validSubnets", Type: "[]string", Note: "", Description: "The `validSubnets` field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.", Comments: [3]string{"" /* encoder.HeadComment */, "The `validSubnets` field configures the networks to pick kubelet node IP from." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", kubeletNodeIPExample()) return doc } func (InstallConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "InstallConfig", Comments: [3]string{"" /* encoder.HeadComment */, "InstallConfig represents the installation options for preparing a node." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "InstallConfig represents the installation options for preparing a node.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "install", }, }, Fields: []encoder.Doc{ { Name: "disk", Type: "string", Note: "", Description: "The disk used for installations.", Comments: [3]string{"" /* encoder.HeadComment */, "The disk used for installations." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "diskSelector", Type: "InstallDiskSelector", Note: "", Description: "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over `disk`.", Comments: [3]string{"" /* encoder.HeadComment */, "Look up disk using disk attributes like model, size, serial and others." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, { Name: "image", Type: "string", Note: "", Description: "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n[GitHub releases page](https://github.com/siderolabs/talos/releases).", Comments: [3]string{"" /* encoder.HeadComment */, "Allows for supplying the image used to perform the installation." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, {}, { Name: "wipe", Type: "bool", Note: "", Description: "Indicates if the installation disk should be wiped at installation time.\nDefaults to `true`.", Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if the installation disk should be wiped at installation time." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, { Name: "legacyBIOSSupport", Type: "bool", Note: "", Description: "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme.", Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if MBR partition should be marked as bootable (active)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "grubUseUKICmdline", Type: "bool", Note: "", Description: "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.", Comments: [3]string{"" /* encoder.HeadComment */, "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("MachineInstall config usage example.", machineInstallExample()) doc.Fields[0].AddExample("", "/dev/sda") doc.Fields[0].AddExample("", "/dev/nvme0") doc.Fields[1].AddExample("", machineInstallDiskSelectorExample()) doc.Fields[3].AddExample("", "ghcr.io/siderolabs/installer:latest") return doc } func (InstallDiskSelector) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "InstallDiskSelector", Comments: [3]string{"" /* encoder.HeadComment */, "InstallDiskSelector represents a disk query parameters for the install disk lookup." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "InstallDiskSelector represents a disk query parameters for the install disk lookup.", AppearsIn: []encoder.Appearance{ { TypeName: "InstallConfig", FieldName: "diskSelector", }, }, Fields: []encoder.Doc{ { Name: "size", Type: "InstallDiskSizeMatcher", Note: "", Description: "Disk size.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk size." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "name", Type: "string", Note: "", Description: "Disk name `/sys/block//device/name`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk name `/sys/block//device/name`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "model", Type: "string", Note: "", Description: "Disk model `/sys/block//device/model`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk model `/sys/block//device/model`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "serial", Type: "string", Note: "", Description: "Disk serial number `/sys/block//serial`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk serial number `/sys/block//serial`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "modalias", Type: "string", Note: "", Description: "Disk modalias `/sys/block//device/modalias`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk modalias `/sys/block//device/modalias`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "uuid", Type: "string", Note: "", Description: "Disk UUID `/sys/block//uuid`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk UUID `/sys/block//uuid`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "wwid", Type: "string", Note: "", Description: "Disk WWID `/sys/block//wwid`.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk WWID `/sys/block//wwid`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "type", Type: "InstallDiskType", Note: "", Description: "Disk Type.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk Type." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "ssd", "hdd", "nvme", "sd", }, }, { Name: "busPath", Type: "string", Note: "", Description: "Disk bus path.", Comments: [3]string{"" /* encoder.HeadComment */, "Disk bus path." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineInstallDiskSelectorExample()) doc.Fields[0].AddExample("Select a disk which size is equal to 4GB.", machineInstallDiskSizeMatcherExamples0()) doc.Fields[0].AddExample("Select a disk which size is greater than 1TB.", machineInstallDiskSizeMatcherExamples1()) doc.Fields[0].AddExample("Select a disk which size is less or equal than 2TB.", machineInstallDiskSizeMatcherExamples2()) doc.Fields[8].AddExample("", "/pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0") doc.Fields[8].AddExample("", "/pci0000:00/*") return doc } func (CoreDNS) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "CoreDNS", Comments: [3]string{"" /* encoder.HeadComment */, "CoreDNS represents the CoreDNS config values." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "CoreDNS represents the CoreDNS config values.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "coreDNS", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable coredns deployment on cluster bootstrap.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable coredns deployment on cluster bootstrap." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "image", Type: "string", Note: "", Description: "The `image` field is an override to the default coredns image.", Comments: [3]string{"" /* encoder.HeadComment */, "The `image` field is an override to the default coredns image." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterCoreDNSExample()) return doc } func (Endpoint) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "Endpoint", Comments: [3]string{"" /* encoder.HeadComment */, "Endpoint represents the endpoint URL parsed out of the machine config." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "Endpoint represents the endpoint URL parsed out of the machine config.", AppearsIn: []encoder.Appearance{ { TypeName: "ControlPlaneConfig", FieldName: "endpoint", }, { TypeName: "LoggingDestination", FieldName: "endpoint", }, }, Fields: []encoder.Doc{ {}, }, } doc.AddExample("", clusterEndpointExample1()) doc.AddExample("", clusterEndpointExample2()) doc.AddExample("", loggingEndpointExample1()) doc.AddExample("", loggingEndpointExample2()) return doc } func (ControlPlaneConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ControlPlaneConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ControlPlaneConfig represents the control plane configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ControlPlaneConfig represents the control plane configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "controlPlane", }, }, Fields: []encoder.Doc{ { Name: "endpoint", Type: "Endpoint", Note: "", Description: "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.", Comments: [3]string{"" /* encoder.HeadComment */, "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "localAPIServerPort", Type: "int", Note: "", Description: "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is `6443`.", Comments: [3]string{"" /* encoder.HeadComment */, "The port that the API server listens on internally." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("Setting controlplane endpoint address to 1.2.3.4 and port to 443 example.", clusterControlPlaneExample()) doc.Fields[0].AddExample("", clusterEndpointExample1()) doc.Fields[0].AddExample("", clusterEndpointExample2()) return doc } func (APIServerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "APIServerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "APIServerConfig represents the kube apiserver configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "APIServerConfig represents the kube apiserver configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "apiServer", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "The container image used in the API server manifest.", Comments: [3]string{"" /* encoder.HeadComment */, "The container image used in the API server manifest." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "Extra arguments to supply to the API server.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments to supply to the API server." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraVolumes", Type: "[]VolumeMountConfig", Note: "", Description: "Extra volumes to mount to the API server static pod.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra volumes to mount to the API server static pod." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "env", Type: "Env", Note: "", Description: "The `env` field allows for the addition of environment variables for the control plane component.", Comments: [3]string{"" /* encoder.HeadComment */, "The `env` field allows for the addition of environment variables for the control plane component." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "certSANs", Type: "[]string", Note: "", Description: "Extra certificate subject alternative names for the API server's certificate.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra certificate subject alternative names for the API server's certificate." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, { Name: "admissionControl", Type: "[]AdmissionPluginConfig", Note: "", Description: "Configure the API server admission plugins.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the API server admission plugins." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "auditPolicy", Type: "Unstructured", Note: "", Description: "Configure the API server audit policy.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the API server audit policy." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "resources", Type: "ResourcesConfig", Note: "", Description: "Configure the API server resources.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the API server resources." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "authorizationConfig", Type: "[]AuthorizationConfigAuthorizerConfig", Note: "", Description: "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterAPIServerExample()) doc.Fields[0].AddExample("", clusterAPIServerImageExample()) doc.Fields[6].AddExample("", admissionControlConfigExample()) doc.Fields[7].AddExample("", APIServerDefaultAuditPolicy) doc.Fields[9].AddExample("", authorizationConfigExample()) return doc } func (AdmissionPluginConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "AdmissionPluginConfig", Comments: [3]string{"" /* encoder.HeadComment */, "AdmissionPluginConfig represents the API server admission plugin configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "AdmissionPluginConfig represents the API server admission plugin configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "APIServerConfig", FieldName: "admissionControl", }, }, Fields: []encoder.Doc{ { Name: "name", Type: "string", Note: "", Description: "Name is the name of the admission controller.\nIt must match the registered admission plugin name.", Comments: [3]string{"" /* encoder.HeadComment */, "Name is the name of the admission controller." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "configuration", Type: "Unstructured", Note: "", Description: "Configuration is an embedded configuration object to be used as the plugin's\nconfiguration.", Comments: [3]string{"" /* encoder.HeadComment */, "Configuration is an embedded configuration object to be used as the plugin's" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", admissionControlConfigExample()) return doc } func (AuthorizationConfigAuthorizerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "AuthorizationConfigAuthorizerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "APIServerConfig", FieldName: "authorizationConfig", }, }, Fields: []encoder.Doc{ { Name: "type", Type: "string", Note: "", Description: "Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`.", Comments: [3]string{"" /* encoder.HeadComment */, "Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "name", Type: "string", Note: "", Description: "Name is used to describe the authorizer.", Comments: [3]string{"" /* encoder.HeadComment */, "Name is used to describe the authorizer." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "webhook", Type: "Unstructured", Note: "", Description: "webhook is the configuration for the webhook authorizer.", Comments: [3]string{"" /* encoder.HeadComment */, "webhook is the configuration for the webhook authorizer." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", authorizationConfigExample()) return doc } func (ControllerManagerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ControllerManagerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ControllerManagerConfig represents the kube controller manager configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ControllerManagerConfig represents the kube controller manager configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "controllerManager", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "The container image used in the controller manager manifest.", Comments: [3]string{"" /* encoder.HeadComment */, "The container image used in the controller manager manifest." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "Extra arguments to supply to the controller manager.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments to supply to the controller manager." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraVolumes", Type: "[]VolumeMountConfig", Note: "", Description: "Extra volumes to mount to the controller manager static pod.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra volumes to mount to the controller manager static pod." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "env", Type: "Env", Note: "", Description: "The `env` field allows for the addition of environment variables for the control plane component.", Comments: [3]string{"" /* encoder.HeadComment */, "The `env` field allows for the addition of environment variables for the control plane component." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "resources", Type: "ResourcesConfig", Note: "", Description: "Configure the controller manager resources.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the controller manager resources." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterControllerManagerExample()) doc.Fields[0].AddExample("", clusterControllerManagerImageExample()) return doc } func (ProxyConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ProxyConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ProxyConfig represents the kube proxy configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ProxyConfig represents the kube proxy configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "proxy", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable kube-proxy deployment on cluster bootstrap.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable kube-proxy deployment on cluster bootstrap." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "image", Type: "string", Note: "", Description: "The container image used in the kube-proxy manifest.", Comments: [3]string{"" /* encoder.HeadComment */, "The container image used in the kube-proxy manifest." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mode", Type: "string", Note: "", Description: "proxy mode of kube-proxy.\nThe default is 'iptables'.", Comments: [3]string{"" /* encoder.HeadComment */, "proxy mode of kube-proxy." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "Extra arguments to supply to kube-proxy.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments to supply to kube-proxy." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterProxyExample()) doc.Fields[0].AddExample("", new(false)) doc.Fields[1].AddExample("", clusterProxyImageExample()) return doc } func (SchedulerConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "SchedulerConfig", Comments: [3]string{"" /* encoder.HeadComment */, "SchedulerConfig represents the kube scheduler configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "SchedulerConfig represents the kube scheduler configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "scheduler", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "The container image used in the scheduler manifest.", Comments: [3]string{"" /* encoder.HeadComment */, "The container image used in the scheduler manifest." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "Extra arguments to supply to the scheduler.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments to supply to the scheduler." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraVolumes", Type: "[]VolumeMountConfig", Note: "", Description: "Extra volumes to mount to the scheduler static pod.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra volumes to mount to the scheduler static pod." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "env", Type: "Env", Note: "", Description: "The `env` field allows for the addition of environment variables for the control plane component.", Comments: [3]string{"" /* encoder.HeadComment */, "The `env` field allows for the addition of environment variables for the control plane component." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "resources", Type: "ResourcesConfig", Note: "", Description: "Configure the scheduler resources.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure the scheduler resources." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "config", Type: "Unstructured", Note: "", Description: "Specify custom kube-scheduler configuration.", Comments: [3]string{"" /* encoder.HeadComment */, "Specify custom kube-scheduler configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterSchedulerExample()) doc.Fields[0].AddExample("", clusterSchedulerImageExample()) return doc } func (EtcdConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "EtcdConfig", Comments: [3]string{"" /* encoder.HeadComment */, "EtcdConfig represents the etcd configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "EtcdConfig represents the etcd configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "etcd", }, }, Fields: []encoder.Doc{ { Name: "image", Type: "string", Note: "", Description: "The container image used to create the etcd service.", Comments: [3]string{"" /* encoder.HeadComment */, "The container image used to create the etcd service." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "ca", Type: "PEMEncodedCertificateAndKey", Note: "", Description: "The `ca` is the root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", Comments: [3]string{"" /* encoder.HeadComment */, "The `ca` is the root certificate authority of the PKI." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "extraArgs", Type: "Args", Note: "", Description: "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n- `name`\n- `data-dir`\n- `initial-cluster-state`\n- `listen-peer-urls`\n- `listen-client-urls`\n- `cert-file`\n- `key-file`\n- `trusted-ca-file`\n- `peer-client-cert-auth`\n- `peer-cert-file`\n- `peer-trusted-ca-file`\n- `peer-key-file`", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments to supply to etcd." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, { Name: "advertisedSubnets", Type: "[]string", Note: "", Description: "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", Comments: [3]string{"" /* encoder.HeadComment */, "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "listenSubnets", Type: "[]string", Note: "", Description: "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.\n\nIf `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to\n`advertisedSubnets`.\n\nIf neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", Comments: [3]string{"" /* encoder.HeadComment */, "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterEtcdExample()) doc.Fields[0].AddExample("", clusterEtcdImageExample()) doc.Fields[1].AddExample("", pemEncodedCertificateExample()) doc.Fields[4].AddExample("", clusterEtcdAdvertisedSubnetsExample()) return doc } func (ClusterNetworkConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ClusterNetworkConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ClusterNetworkConfig represents kube networking configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ClusterNetworkConfig represents kube networking configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "network", }, }, Fields: []encoder.Doc{ { Name: "cni", Type: "CNIConfig", Note: "", Description: "The CNI used.\nComposed of \"name\" and \"urls\".\nThe \"name\" key supports the following options: \"flannel\", \"custom\", and \"none\".\n\"flannel\" uses Talos-managed Flannel CNI, and that's the default option.\n\"custom\" uses custom manifests that should be provided in \"urls\".\n\"none\" indicates that Talos will not manage any CNI installation.", Comments: [3]string{"" /* encoder.HeadComment */, "The CNI used." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "dnsDomain", Type: "string", Note: "", Description: "The domain used by Kubernetes DNS.\nThe default is `cluster.local`", Comments: [3]string{"" /* encoder.HeadComment */, "The domain used by Kubernetes DNS." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "podSubnets", Type: "[]string", Note: "", Description: "The pod subnet CIDR.", Comments: [3]string{"" /* encoder.HeadComment */, "The pod subnet CIDR." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "serviceSubnets", Type: "[]string", Note: "", Description: "The service subnet CIDR.", Comments: [3]string{"" /* encoder.HeadComment */, "The service subnet CIDR." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("Configuring with flannel CNI and setting up subnets.", clusterNetworkExample()) doc.Fields[0].AddExample("", clusterCustomCNIExample()) doc.Fields[1].AddExample("", "cluster.local") doc.Fields[2].AddExample("", []string{"10.244.0.0/16"}) doc.Fields[3].AddExample("", []string{"10.96.0.0/12"}) return doc } func (CNIConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "CNIConfig", Comments: [3]string{"" /* encoder.HeadComment */, "CNIConfig represents the CNI configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "CNIConfig represents the CNI configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterNetworkConfig", FieldName: "cni", }, }, Fields: []encoder.Doc{ { Name: "name", Type: "string", Note: "", Description: "Name of CNI to use.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of CNI to use." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "flannel", "custom", "none", }, }, { Name: "urls", Type: "[]string", Note: "", Description: "URLs containing manifests to apply for the CNI.\nShould be present for \"custom\", must be empty for \"flannel\" and \"none\".", Comments: [3]string{"" /* encoder.HeadComment */, "URLs containing manifests to apply for the CNI." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "flannel", Type: "FlannelCNIConfig", Note: "", Description: "description: |\nFlannel configuration options.\n", Comments: [3]string{"" /* encoder.HeadComment */, "description: |" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterCustomCNIExample()) return doc } func (FlannelCNIConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "FlannelCNIConfig", Comments: [3]string{"" /* encoder.HeadComment */, "FlannelCNIConfig represents the Flannel CNI configuration options." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "FlannelCNIConfig represents the Flannel CNI configuration options.", AppearsIn: []encoder.Appearance{ { TypeName: "CNIConfig", FieldName: "flannel", }, }, Fields: []encoder.Doc{ { Name: "extraArgs", Type: "[]string", Note: "", Description: "Extra arguments for 'flanneld'.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra arguments for 'flanneld'." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "kubeNetworkPoliciesEnabled", Type: "bool", Note: "", Description: "Deploys kube-network-policies along with Flannel.\n\nThis enables Kubernetes Network Policies support in the cluster.", Comments: [3]string{"" /* encoder.HeadComment */, "Deploys kube-network-policies along with Flannel." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", []string{"--iface-can-reach=192.168.1.1"}) return doc } func (ExternalCloudProviderConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ExternalCloudProviderConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ExternalCloudProviderConfig contains external cloud provider configuration." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ExternalCloudProviderConfig contains external cloud provider configuration.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "externalCloudProvider", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable external cloud provider.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable external cloud provider." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "true", "yes", "false", "no", }, }, { Name: "manifests", Type: "[]string", Note: "", Description: "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.", Comments: [3]string{"" /* encoder.HeadComment */, "A list of urls that point to additional manifests for an external cloud provider." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterExternalCloudProviderConfigExample()) doc.Fields[1].AddExample("", []string{ "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml", "https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml", }) return doc } func (AdminKubeconfigConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "AdminKubeconfigConfig", Comments: [3]string{"" /* encoder.HeadComment */, "AdminKubeconfigConfig contains admin kubeconfig settings." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "AdminKubeconfigConfig contains admin kubeconfig settings.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "adminKubeconfig", }, }, Fields: []encoder.Doc{ { Name: "certLifetime", Type: "Duration", Note: "", Description: "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", Comments: [3]string{"" /* encoder.HeadComment */, "Admin kubeconfig certificate lifetime (default is 1 year)." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterAdminKubeconfigExample()) return doc } func (ResourcesConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ResourcesConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ResourcesConfig represents the pod resources." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ResourcesConfig represents the pod resources.", AppearsIn: []encoder.Appearance{ { TypeName: "APIServerConfig", FieldName: "resources", }, { TypeName: "ControllerManagerConfig", FieldName: "resources", }, { TypeName: "SchedulerConfig", FieldName: "resources", }, }, Fields: []encoder.Doc{ { Name: "requests", Type: "Unstructured", Note: "", Description: "Requests configures the reserved cpu/memory resources.", Comments: [3]string{"" /* encoder.HeadComment */, "Requests configures the reserved cpu/memory resources." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "limits", Type: "Unstructured", Note: "", Description: "Limits configures the maximum cpu/memory resources a container can use.", Comments: [3]string{"" /* encoder.HeadComment */, "Limits configures the maximum cpu/memory resources a container can use." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("resources requests.", resourcesConfigRequestsExample()) doc.Fields[1].AddExample("resources requests.", resourcesConfigLimitsExample()) return doc } func (MachineFile) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "MachineFile", Comments: [3]string{"" /* encoder.HeadComment */, "MachineFile represents a file to write to disk." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "MachineFile represents a file to write to disk.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "files", }, }, Fields: []encoder.Doc{ { Name: "content", Type: "string", Note: "", Description: "The contents of the file.", Comments: [3]string{"" /* encoder.HeadComment */, "The contents of the file." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "permissions", Type: "FileMode", Note: "", Description: "The file's permissions in octal.", Comments: [3]string{"" /* encoder.HeadComment */, "The file's permissions in octal." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "path", Type: "string", Note: "", Description: "The path of the file.", Comments: [3]string{"" /* encoder.HeadComment */, "The path of the file." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "op", Type: "string", Note: "", Description: "The operation to use", Comments: [3]string{"" /* encoder.HeadComment */, "The operation to use" /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "create", "append", "overwrite", }, }, }, } doc.AddExample("MachineFiles usage example.", machineFilesExample()) return doc } func (FeaturesConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "FeaturesConfig", Comments: [3]string{"" /* encoder.HeadComment */, "FeaturesConfig describes individual Talos features that can be switched on or off." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "FeaturesConfig describes individual Talos features that can be switched on or off.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "features", }, }, Fields: []encoder.Doc{ {}, {}, { Name: "kubernetesTalosAPIAccess", Type: "KubernetesTalosAPIAccessConfig", Note: "", Description: "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure Talos API access from Kubernetes pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, {}, { Name: "diskQuotaSupport", Type: "bool", Note: "", Description: "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable XFS project quota support for EPHEMERAL partition and user disks." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "kubePrism", Type: "KubePrism", Note: "", Description: "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.", Comments: [3]string{"" /* encoder.HeadComment */, "KubePrism - local proxy/load balancer on defined port that will distribute" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "hostDNS", Type: "HostDNSConfig", Note: "", Description: "Configures host DNS caching resolver.", Comments: [3]string{"" /* encoder.HeadComment */, "Configures host DNS caching resolver." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "imageCache", Type: "ImageCacheConfig", Note: "", Description: "Enable Image Cache feature.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable Image Cache feature." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "nodeAddressSortAlgorithm", Type: "string", Note: "", Description: "Select the node address sort algorithm.\nThe 'v1' algorithm sorts addresses by the address itself.\nThe 'v2' algorithm prefers more specific prefixes.\nIf unset, defaults to 'v1'.", Comments: [3]string{"" /* encoder.HeadComment */, "Select the node address sort algorithm." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineFeaturesExample()) doc.Fields[2].AddExample("", kubernetesTalosAPIAccessConfigExample()) return doc } func (KubePrism) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubePrism", Comments: [3]string{"" /* encoder.HeadComment */, "KubePrism describes the configuration for the KubePrism load balancer." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubePrism describes the configuration for the KubePrism load balancer.", AppearsIn: []encoder.Appearance{ { TypeName: "FeaturesConfig", FieldName: "kubePrism", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable KubePrism support - will start local load balancing proxy.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable KubePrism support - will start local load balancing proxy." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "port", Type: "int", Note: "", Description: "KubePrism port.", Comments: [3]string{"" /* encoder.HeadComment */, "KubePrism port." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (ImageCacheConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ImageCacheConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ImageCacheConfig describes the configuration for the Image Cache feature." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ImageCacheConfig describes the configuration for the Image Cache feature.", AppearsIn: []encoder.Appearance{ { TypeName: "FeaturesConfig", FieldName: "imageCache", }, }, Fields: []encoder.Doc{ { Name: "localEnabled", Type: "bool", Note: "", Description: "Enable local image cache.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable local image cache." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (KubernetesTalosAPIAccessConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KubernetesTalosAPIAccessConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods.", AppearsIn: []encoder.Appearance{ { TypeName: "FeaturesConfig", FieldName: "kubernetesTalosAPIAccess", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable Talos API access from Kubernetes pods.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable Talos API access from Kubernetes pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "allowedRoles", Type: "[]string", Note: "", Description: "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.", Comments: [3]string{"" /* encoder.HeadComment */, "The list of Talos API roles which can be granted for access from Kubernetes pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "allowedKubernetesNamespaces", Type: "[]string", Note: "", Description: "The list of Kubernetes namespaces Talos API access is available from.", Comments: [3]string{"" /* encoder.HeadComment */, "The list of Kubernetes namespaces Talos API access is available from." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", kubernetesTalosAPIAccessConfigExample()) return doc } func (HostDNSConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "HostDNSConfig", Comments: [3]string{"" /* encoder.HeadComment */, "HostDNSConfig describes the configuration for the host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "HostDNSConfig describes the configuration for the host DNS resolver.", AppearsIn: []encoder.Appearance{ { TypeName: "FeaturesConfig", FieldName: "hostDNS", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable host DNS caching resolver.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable host DNS caching resolver." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "forwardKubeDNSToHost", Type: "bool", Note: "", Description: "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).", Comments: [3]string{"" /* encoder.HeadComment */, "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "resolveMemberNames", Type: "bool", Note: "", Description: "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.", Comments: [3]string{"" /* encoder.HeadComment */, "Resolve member hostnames using the host DNS resolver." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (VolumeMountConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "VolumeMountConfig", Comments: [3]string{"" /* encoder.HeadComment */, "VolumeMountConfig struct describes extra volume mount for the static pods." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "VolumeMountConfig struct describes extra volume mount for the static pods.", AppearsIn: []encoder.Appearance{ { TypeName: "APIServerConfig", FieldName: "extraVolumes", }, { TypeName: "ControllerManagerConfig", FieldName: "extraVolumes", }, { TypeName: "SchedulerConfig", FieldName: "extraVolumes", }, }, Fields: []encoder.Doc{ { Name: "hostPath", Type: "string", Note: "", Description: "Path on the host.", Comments: [3]string{"" /* encoder.HeadComment */, "Path on the host." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "mountPath", Type: "string", Note: "", Description: "Path in the container.", Comments: [3]string{"" /* encoder.HeadComment */, "Path in the container." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "readonly", Type: "bool", Note: "", Description: "Mount the volume read only.", Comments: [3]string{"" /* encoder.HeadComment */, "Mount the volume read only." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", "/var/lib/auth") doc.Fields[1].AddExample("", "/etc/kubernetes/auth") doc.Fields[2].AddExample("", true) return doc } func (ClusterInlineManifest) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ClusterInlineManifest", Comments: [3]string{"" /* encoder.HeadComment */, "ClusterInlineManifest struct describes inline bootstrap manifests for the user." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ClusterInlineManifest struct describes inline bootstrap manifests for the user.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "inlineManifests", }, }, Fields: []encoder.Doc{ { Name: "name", Type: "string", Note: "", Description: "Name of the manifest.\nName should be unique.", Comments: [3]string{"" /* encoder.HeadComment */, "Name of the manifest." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "contents", Type: "string", Note: "", Description: "Manifest contents as a string.", Comments: [3]string{"" /* encoder.HeadComment */, "Manifest contents as a string." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterInlineManifestsExample()) doc.Fields[0].AddExample("", "csi") doc.Fields[1].AddExample("", "/etc/kubernetes/auth") return doc } func (ClusterDiscoveryConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "ClusterDiscoveryConfig", Comments: [3]string{"" /* encoder.HeadComment */, "ClusterDiscoveryConfig struct configures cluster membership discovery." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "ClusterDiscoveryConfig struct configures cluster membership discovery.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterConfig", FieldName: "discovery", }, }, Fields: []encoder.Doc{ { Name: "enabled", Type: "bool", Note: "", Description: "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.", Comments: [3]string{"" /* encoder.HeadComment */, "Enable the cluster membership discovery feature." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "registries", Type: "DiscoveryRegistriesConfig", Note: "", Description: "Configure registries used for cluster member discovery.", Comments: [3]string{"" /* encoder.HeadComment */, "Configure registries used for cluster member discovery." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", clusterDiscoveryExample()) return doc } func (DiscoveryRegistriesConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "DiscoveryRegistriesConfig", Comments: [3]string{"" /* encoder.HeadComment */, "DiscoveryRegistriesConfig struct configures cluster membership discovery." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "DiscoveryRegistriesConfig struct configures cluster membership discovery.", AppearsIn: []encoder.Appearance{ { TypeName: "ClusterDiscoveryConfig", FieldName: "registries", }, }, Fields: []encoder.Doc{ { Name: "kubernetes", Type: "RegistryKubernetesConfig", Note: "", Description: "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.", Comments: [3]string{"" /* encoder.HeadComment */, "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "service", Type: "RegistryServiceConfig", Note: "", Description: "Service registry is using an external service to push and pull information about cluster members.", Comments: [3]string{"" /* encoder.HeadComment */, "Service registry is using an external service to push and pull information about cluster members." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (RegistryKubernetesConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryKubernetesConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryKubernetesConfig struct configures Kubernetes discovery registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryKubernetesConfig struct configures Kubernetes discovery registry.", AppearsIn: []encoder.Appearance{ { TypeName: "DiscoveryRegistriesConfig", FieldName: "kubernetes", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable Kubernetes discovery registry.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable Kubernetes discovery registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } func (RegistryServiceConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "RegistryServiceConfig", Comments: [3]string{"" /* encoder.HeadComment */, "RegistryServiceConfig struct configures Kubernetes discovery registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "RegistryServiceConfig struct configures Kubernetes discovery registry.", AppearsIn: []encoder.Appearance{ { TypeName: "DiscoveryRegistriesConfig", FieldName: "service", }, }, Fields: []encoder.Doc{ { Name: "disabled", Type: "bool", Note: "", Description: "Disable external service discovery registry.", Comments: [3]string{"" /* encoder.HeadComment */, "Disable external service discovery registry." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "endpoint", Type: "string", Note: "", Description: "External service endpoint.", Comments: [3]string{"" /* encoder.HeadComment */, "External service endpoint." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[1].AddExample("", constants.DefaultDiscoveryServiceEndpoint) return doc } func (UdevConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "UdevConfig", Comments: [3]string{"" /* encoder.HeadComment */, "UdevConfig describes how the udev system should be configured." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "UdevConfig describes how the udev system should be configured.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "udev", }, }, Fields: []encoder.Doc{ { Name: "rules", Type: "[]string", Note: "", Description: "List of udev rules to apply to the udev system", Comments: [3]string{"" /* encoder.HeadComment */, "List of udev rules to apply to the udev system" /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineUdevExample()) return doc } func (LoggingConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LoggingConfig", Comments: [3]string{"" /* encoder.HeadComment */, "LoggingConfig struct configures Talos logging." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LoggingConfig struct configures Talos logging.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "logging", }, }, Fields: []encoder.Doc{ { Name: "destinations", Type: "[]LoggingDestination", Note: "", Description: "Logging destination.", Comments: [3]string{"" /* encoder.HeadComment */, "Logging destination." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineLoggingExample()) return doc } func (LoggingDestination) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "LoggingDestination", Comments: [3]string{"" /* encoder.HeadComment */, "LoggingDestination struct configures Talos logging destination." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "LoggingDestination struct configures Talos logging destination.", AppearsIn: []encoder.Appearance{ { TypeName: "LoggingConfig", FieldName: "destinations", }, }, Fields: []encoder.Doc{ { Name: "endpoint", Type: "Endpoint", Note: "", Description: "Where to send logs. Supported protocols are \"tcp\" and \"udp\".", Comments: [3]string{"" /* encoder.HeadComment */, "Where to send logs. Supported protocols are \"tcp\" and \"udp\"." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "format", Type: "string", Note: "", Description: "Logs format.", Comments: [3]string{"" /* encoder.HeadComment */, "Logs format." /* encoder.LineComment */, "" /* encoder.FootComment */}, Values: []string{ "json_lines", }, }, { Name: "extraTags", Type: "map[string]string", Note: "", Description: "Extra tags (key-value) pairs to attach to every log message sent.", Comments: [3]string{"" /* encoder.HeadComment */, "Extra tags (key-value) pairs to attach to every log message sent." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.Fields[0].AddExample("", loggingEndpointExample1()) doc.Fields[0].AddExample("", loggingEndpointExample2()) return doc } func (KernelConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KernelConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KernelConfig struct configures Talos Linux kernel." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KernelConfig struct configures Talos Linux kernel.", AppearsIn: []encoder.Appearance{ { TypeName: "MachineConfig", FieldName: "kernel", }, }, Fields: []encoder.Doc{ { Name: "modules", Type: "[]KernelModuleConfig", Note: "", Description: "Kernel modules to load.", Comments: [3]string{"" /* encoder.HeadComment */, "Kernel modules to load." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } doc.AddExample("", machineKernelExample()) return doc } func (KernelModuleConfig) Doc() *encoder.Doc { doc := &encoder.Doc{ Type: "KernelModuleConfig", Comments: [3]string{"" /* encoder.HeadComment */, "KernelModuleConfig struct configures Linux kernel modules to load." /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "KernelModuleConfig struct configures Linux kernel modules to load.", AppearsIn: []encoder.Appearance{ { TypeName: "KernelConfig", FieldName: "modules", }, }, Fields: []encoder.Doc{ { Name: "name", Type: "string", Note: "", Description: "Module name.", Comments: [3]string{"" /* encoder.HeadComment */, "Module name." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, { Name: "parameters", Type: "[]string", Note: "", Description: "Module parameters, changes applied after reboot.", Comments: [3]string{"" /* encoder.HeadComment */, "Module parameters, changes applied after reboot." /* encoder.LineComment */, "" /* encoder.FootComment */}, }, }, } return doc } // GetFileDoc returns documentation for the file ./v1alpha1_types_doc.go. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "v1alpha1", Description: "Package v1alpha1 contains definition of the `v1alpha1` configuration document.\n\nEven though the machine configuration in Talos Linux is multi-document, at the moment\nthis configuration document contains most of the configuration options.\n\nIt is expected that new configuration options will be added as new documents, and existing ones\nmigrated to their own documents.\n", Structs: []*encoder.Doc{ Config{}.Doc(), MachineConfig{}.Doc(), MachineSeccompProfile{}.Doc(), ClusterConfig{}.Doc(), LinuxIDMapping{}.Doc(), ExtraMount{}.Doc(), MachineControlPlaneConfig{}.Doc(), MachineControllerManagerConfig{}.Doc(), MachineSchedulerConfig{}.Doc(), KubeletConfig{}.Doc(), KubeletNodeIPConfig{}.Doc(), InstallConfig{}.Doc(), InstallDiskSelector{}.Doc(), CoreDNS{}.Doc(), Endpoint{}.Doc(), ControlPlaneConfig{}.Doc(), APIServerConfig{}.Doc(), AdmissionPluginConfig{}.Doc(), AuthorizationConfigAuthorizerConfig{}.Doc(), ControllerManagerConfig{}.Doc(), ProxyConfig{}.Doc(), SchedulerConfig{}.Doc(), EtcdConfig{}.Doc(), ClusterNetworkConfig{}.Doc(), CNIConfig{}.Doc(), FlannelCNIConfig{}.Doc(), ExternalCloudProviderConfig{}.Doc(), AdminKubeconfigConfig{}.Doc(), ResourcesConfig{}.Doc(), MachineFile{}.Doc(), FeaturesConfig{}.Doc(), KubePrism{}.Doc(), ImageCacheConfig{}.Doc(), KubernetesTalosAPIAccessConfig{}.Doc(), HostDNSConfig{}.Doc(), VolumeMountConfig{}.Doc(), ClusterInlineManifest{}.Doc(), ClusterDiscoveryConfig{}.Doc(), DiscoveryRegistriesConfig{}.Doc(), RegistryKubernetesConfig{}.Doc(), RegistryServiceConfig{}.Doc(), UdevConfig{}.Doc(), LoggingConfig{}.Doc(), LoggingDestination{}.Doc(), KernelConfig{}.Doc(), KernelModuleConfig{}.Doc(), }, } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_unstructured.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "fmt" "slices" ) // Unstructured allows wrapping any map[string]interface{} into a config object. // // docgen: nodoc // +k8s:deepcopy-gen=true type Unstructured struct { Object map[string]any `yaml:",inline"` } // DeepCopy performs copying of the Object contents. func (in *Unstructured) DeepCopy() *Unstructured { if in == nil { return nil } out := new(Unstructured) out.Object = deepCopyUnstructured(in.Object).(map[string]any) //nolint:forcetypeassert return out } func deepCopyUnstructured(x any) any { switch x := x.(type) { case map[string]any: if x == nil { return x } clone := make(map[string]any, len(x)) for k, v := range x { clone[k] = deepCopyUnstructured(v) } return clone case []any: if x == nil { return x } clone := make([]any, len(x)) for i, v := range x { clone[i] = deepCopyUnstructured(v) } return clone case string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128, nil: return x case []byte: return slices.Clone(x) default: panic(fmt.Errorf("cannot deep copy %T", x)) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_unstructured_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) func TestUnstructuredDeepCopy(t *testing.T) { u := v1alpha1.Unstructured{ Object: map[string]any{ "strings": map[string]any{ "foo": "bar", }, "numbers": []any{ map[string]any{ "int": 32, "int8": int8(34), "byte": byte(35), "int16": int16(36), "int32": int32(37), "int64": int64(38), "uint": uint(39), "uint8": uint8(40), "uint16": uint16(41), "uint32": uint32(42), "uint64": uint64(43), }, float32(44.0), float64(45.0), complex64(complex(46.0, 47.0)), complex128(complex(48.0, 49.0)), }, "bytes": []byte("abc"), }, } assert.Equal(t, u.DeepCopy(), &u) } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_validation.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "context" "encoding/base64" "encoding/json" "errors" "fmt" "net" "net/netip" "net/url" "os" "reflect" "regexp" "strconv" "strings" "sync" "github.com/cosi-project/runtime/pkg/state" "github.com/hashicorp/go-multierror" "github.com/opencontainers/runtime-spec/specs-go" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/block/blockhelpers" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/kubelet" "github.com/siderolabs/talos/pkg/machinery/labels" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/machinery/version" ) var ( // General. // ErrRequiredSection denotes a section is required. ErrRequiredSection = errors.New("required config section") // ErrRequiredSectionOptions denotes at least one section is required. ErrRequiredSectionOptions = errors.New("required either config section to be set") // ErrInvalidVersion denotes that the config file version is invalid. ErrInvalidVersion = errors.New("invalid config version") // ErrMutuallyExclusive denotes that config sections are mutually exclusive. ErrMutuallyExclusive = errors.New("config sections are mutually exclusive") // ErrEmpty denotes that config section should have at least a single field defined. ErrEmpty = errors.New("config section should contain at least one field") // Security. // ErrEmptyKeyCert denotes that crypto key/cert combination should not be empty. ErrEmptyKeyCert = errors.New("key/cert combination should not be empty") // ErrInvalidCert denotes that the certificate specified is invalid. ErrInvalidCert = errors.New("certificate is invalid") // ErrInvalidCertType denotes that the certificate type is invalid. ErrInvalidCertType = errors.New("certificate type is invalid") // Services. // ErrUnsupportedCNI denotes that the specified CNI is invalid. ErrUnsupportedCNI = errors.New("unsupported CNI driver") // ErrInvalidTrustdToken denotes that a trustd token has not been specified. ErrInvalidTrustdToken = errors.New("trustd token is invalid") // Networking. // ErrInvalidAddress denotes that a bad address was provided. ErrInvalidAddress = errors.New("invalid network address") ) // NetworkDeviceCheck defines the function type for checks. type NetworkDeviceCheck func(*Device, map[string]string) ([]string, error) // Validate implements the config.Provider interface. // //nolint:gocyclo,cyclop func (c *Config) Validate(mode validation.RuntimeMode, options ...validation.Option) ([]string, error) { var ( warnings []string result *multierror.Error ) opts := validation.NewOptions(options...) if c.MachineConfig == nil { result = multierror.Append(result, errors.New("machine instructions are required")) return nil, result.ErrorOrNil() } if err := c.ClusterConfig.Validate(c.Machine().Type().IsControlPlane()); err != nil { result = multierror.Append(result, err) } if mode.RequiresInstall() { if c.MachineConfig.MachineInstall == nil { result = multierror.Append(result, fmt.Errorf("install instructions are required in %q mode", mode)) } else { matcher, err := c.MachineConfig.MachineInstall.DiskMatchExpression() if err != nil { result = multierror.Append(result, fmt.Errorf("install disk selector is invalid: %w", err)) } if c.MachineConfig.MachineInstall.InstallDisk == "" && matcher == nil { result = multierror.Append(result, errors.New("either install disk or diskSelector should be defined")) } if len(c.MachineConfig.MachineInstall.InstallExtraKernelArgs) > 0 && c.MachineConfig.MachineInstall.GrubUseUKICmdline() { result = multierror.Append(result, errors.New("install.extraKernelArgs and install.grubUseUKICmdline can't be used together")) } } } if mode.InContainer() { // require that HostDNS features are enabled to passthrough container DNS to kube-dns if !c.Machine().Features().HostDNS().Enabled() { result = multierror.Append(result, errors.New("feature HostDNS should be enabled in container mode (.machine.features.hostDNS.enabled)")) } if !c.Machine().Features().HostDNS().ForwardKubeDNSToHost() { result = multierror.Append(result, errors.New("feature HostDNS should forward kube-dns to host in container mode (.machine.features.hostDNS.forwardKubeDNSToHost)")) } } if t := c.Machine().Type(); t != machine.TypeUnknown && t.String() != c.MachineConfig.MachineType { warnings = append(warnings, fmt.Sprintf("use %q instead of %q for machine type", t.String(), c.MachineConfig.MachineType)) } if c.Machine().Security().IssuingCA() == nil && len(c.Machine().Security().AcceptedCAs()) == 0 { result = multierror.Append(result, errors.New("issuing CA or some accepted CAs are required (.machine.ca, machine.acceptedCAs)")) } switch c.Machine().Type() { case machine.TypeInit, machine.TypeControlPlane: warn, err := ValidateCNI(c.Cluster().Network().CNI()) warnings = append(warnings, warn...) result = multierror.Append(result, err) if c.Machine().Security().IssuingCA() == nil { result = multierror.Append(result, errors.New("issuing CA is required (.machine.ca)")) } else if len(c.Machine().Security().IssuingCA().Key) == 0 { result = multierror.Append(result, errors.New("issuing CA key is required for controlplane nodes (.machine.ca.key)")) } case machine.TypeWorker: for _, d := range c.Machine().Network().Devices() { if d.VIPConfig() != nil { result = multierror.Append(result, errors.New("virtual (shared) IP is not allowed on non-controlplane nodes")) } for _, vlan := range d.Vlans() { if vlan.VIPConfig() != nil { result = multierror.Append(result, errors.New("virtual (shared) IP is not allowed on non-controlplane nodes")) } } } if c.Machine().Security().IssuingCA() != nil { if len(c.Machine().Security().IssuingCA().Key) > 0 { result = multierror.Append(result, errors.New("issuing Talos API CA key is not allowed on non-controlplane nodes (.machine.ca)")) } if len(c.Machine().Security().IssuingCA().Crt) == 0 && len(c.Machine().Security().AcceptedCAs()) == 0 { result = multierror.Append(result, errors.New("trusted CA certificates are required on non-controlplane nodes (.machine.ca.crt, .machine.acceptedCAs)")) } } if c.Cluster().IssuingCA() != nil && len(c.Cluster().IssuingCA().Key) > 0 { result = multierror.Append(result, errors.New("issuing Kubernetes API CA key is not allowed on non-controlplane nodes (.cluster.ca)")) } case machine.TypeUnknown: fallthrough default: result = multierror.Append(result, fmt.Errorf("unknown machine type %q", c.MachineConfig.MachineType)) } if c.MachineConfig.MachineNetwork != nil { allSecondaryInterfaces := map[string]string{} for _, device := range c.MachineConfig.MachineNetwork.NetworkInterfaces { if device.Bond() != nil && device.Bridge() != nil { result = multierror.Append(result, fmt.Errorf("interface has both bridge and bond sections set %q: %w", device.Interface(), ErrMutuallyExclusive)) } var myInterfaces []string if device.Bond() != nil { myInterfaces = device.Bond().Interfaces() if len(device.Bond().Interfaces()) > 0 && len(device.Bond().Selectors()) > 0 { result = multierror.Append(result, fmt.Errorf("interface %q has both interfaces and selectors set: %w", device.Interface(), ErrMutuallyExclusive)) } } if device.Bridge() != nil { myInterfaces = device.Bridge().Interfaces() } for _, iface := range myInterfaces { if otherIface, exists := allSecondaryInterfaces[iface]; exists && otherIface != device.Interface() { result = multierror.Append(result, fmt.Errorf("interface %q is declared as part of two separate links: %q and %q", iface, otherIface, device.Interface())) } allSecondaryInterfaces[iface] = device.Interface() } } for _, device := range c.MachineConfig.MachineNetwork.NetworkInterfaces { warn, err := ValidateNetworkDevices(device, allSecondaryInterfaces, CheckDeviceInterface, CheckDeviceAddressing, CheckDeviceRoutes) warnings = append(warnings, warn...) result = multierror.Append(result, err) } if kcfg := c.NetworkKubeSpanConfig(); kcfg != nil && kcfg.Enabled() { if kcfg.MTU() < constants.KubeSpanLinkMinimumMTU { result = multierror.Append(result, fmt.Errorf("kubespan link MTU must be at least %d", constants.KubeSpanLinkMinimumMTU)) } } } for i, disk := range c.MachineConfig.MachineDisks { if disk == nil { result = multierror.Append(result, fmt.Errorf("machine.disks[%d] is null", i)) continue } for i, pt := range disk.DiskPartitions { if pt.DiskSize == 0 && i != len(disk.DiskPartitions)-1 { result = multierror.Append(result, fmt.Errorf("partition for disk %q is set to occupy full disk, but it's not the last partition in the list", disk.Device())) } } } if c.MachineConfig.MachineKubelet != nil { warn, err := c.MachineConfig.MachineKubelet.Validate() warnings = append(warnings, warn...) result = multierror.Append(result, err) } for _, label := range []string{constants.EphemeralPartitionLabel, constants.StatePartitionLabel} { encryptionConfig := c.MachineConfig.SystemDiskEncryption().Get(label) if encryptionConfig != nil { if len(encryptionConfig.Keys()) == 0 { result = multierror.Append(result, fmt.Errorf("partition %q: no encryption keys provided", label)) } slotsInUse := map[int]struct{}{} for _, key := range encryptionConfig.Keys() { if _, inUse := slotsInUse[key.Slot()]; inUse { result = multierror.Append(result, fmt.Errorf("partition %q: encryption key slot %d is already in use", label, key.Slot())) } slotsInUse[key.Slot()] = struct{}{} if key.NodeID() == nil && key.Static() == nil && key.KMS() == nil && key.TPM() == nil { result = multierror.Append(result, fmt.Errorf("partition %q: encryption key at slot %d doesn't have the configuration parameters", label, key.Slot())) } } } } if kcfg := c.NetworkKubeSpanConfig(); kcfg != nil && kcfg.Enabled() { if !c.Cluster().Discovery().Enabled() { result = multierror.Append(result, errors.New(".cluster.discovery should be enabled when .machine.network.kubespan is enabled")) } if c.Cluster().ID() == "" { result = multierror.Append(result, errors.New(".cluster.id should be set when .machine.network.kubespan is enabled")) } if c.Cluster().Secret() == "" { result = multierror.Append(result, errors.New(".cluster.secret should be set when .machine.network.kubespan is enabled")) } for _, cidr := range kcfg.Filters().Endpoints() { cidr = strings.TrimPrefix(cidr, "!") if _, err := sideronet.ParseSubnetOrAddress(cidr); err != nil { result = multierror.Append(result, fmt.Errorf("KubeSpan endpoint filter is not valid: %q", cidr)) } } if c.MachineConfig.MachineNetwork.NetworkKubeSpan.KubeSpanFilters != nil { for _, cidr := range c.MachineConfig.MachineNetwork.NetworkKubeSpan.KubeSpanFilters.KubeSpanFiltersExcludeAdvertisedNetworks { _, err := netip.ParsePrefix(cidr) if err != nil { result = multierror.Append(result, fmt.Errorf("KubeSpan exclude advertised networks filter is not valid: %q", cidr)) } } } } if c.MachineConfig.MachineLogging != nil { err := c.MachineConfig.MachineLogging.Validate() result = multierror.Append(result, err) } if c.MachineConfig.MachineInstall != nil { extensions := map[string]struct{}{} for _, ext := range c.MachineConfig.MachineInstall.InstallExtensions { if _, exists := extensions[ext.Image()]; exists { result = multierror.Append(result, fmt.Errorf("duplicate system extension %q", ext.Image())) } extensions[ext.Image()] = struct{}{} } if len(extensions) > 0 { warnings = append(warnings, ".machine.install.extensions is deprecated, please see https://docs.siderolabs.com/talos/latest/platform-specific-installations/boot-assets") } } if err := labels.Validate(c.MachineConfig.MachineNodeLabels); err != nil { result = multierror.Append(result, fmt.Errorf("invalid machine node labels: %w", err)) } if err := labels.ValidateAnnotations(c.MachineConfig.MachineNodeAnnotations); err != nil { result = multierror.Append(result, fmt.Errorf("invalid machine node annotations: %w", err)) } if err := labels.ValidateTaints(c.MachineConfig.MachineNodeTaints); err != nil { result = multierror.Append(result, fmt.Errorf("invalid machine node taints: %w", err)) } if c.Machine().Features().KubernetesTalosAPIAccess().Enabled() { if !c.Machine().Type().IsControlPlane() { result = multierror.Append(result, errors.New("feature Kubernetes Talos API Access can only be enabled on control plane machines")) } for _, r := range c.Machine().Features().KubernetesTalosAPIAccess().AllowedRoles() { if !role.All.Includes(role.Role(r)) { result = multierror.Append(result, fmt.Errorf("invalid role %q in allowed roles for Kubernetes Talos API Access", r)) } } } if c.MachineConfig.MachineFeatures != nil && c.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm != "" { if _, err := nethelpers.AddressSortAlgorithmString(c.MachineConfig.MachineFeatures.FeatureNodeAddressSortAlgorithm); err != nil { result = multierror.Append(result, fmt.Errorf("invalid node address sort algorithm: %w", err)) } } if c.ConfigPersist != nil && !*c.ConfigPersist { result = multierror.Append(result, errors.New(".persist should be enabled")) } if len(c.Machine().BaseRuntimeSpecOverrides()) > 0 { // try to unmarshal the overrides to ensure they are valid jsonSpec, err := json.Marshal(c.Machine().BaseRuntimeSpecOverrides()) if err != nil { result = multierror.Append(result, fmt.Errorf("failed to marshal base runtime spec overrides: %w", err)) } else { var ociSpec specs.Spec if err := json.Unmarshal(jsonSpec, &ociSpec); err != nil { result = multierror.Append(result, fmt.Errorf("failed to unmarshal base runtime spec overrides: %w", err)) } } } for key, val := range c.MachineConfig.MachineRegistries.RegistryConfig { if val == nil { result = multierror.Append(result, fmt.Errorf("registries.config[%q] is null", key)) } } for key, val := range c.MachineConfig.MachineRegistries.RegistryMirrors { if val == nil { result = multierror.Append(result, fmt.Errorf("registries.mirrors[%q] is null", key)) } } if opts.Strict { for _, w := range warnings { result = multierror.Append(result, fmt.Errorf("warning: %s", w)) } warnings = nil } return warnings, result.ErrorOrNil() } var rxDNSNameRegexp = sync.OnceValue(func() *regexp.Regexp { return regexp.MustCompile(`^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`) }) func isValidDNSName(name string) bool { if name == "" || len(name)-strings.Count(name, ".") > 255 { return false } return rxDNSNameRegexp().MatchString(name) } // Validate validates the config. // //nolint:gocyclo func (c *ClusterConfig) Validate(isControlPlane bool) error { var result *multierror.Error if c == nil { return errors.New("cluster instructions are required") } if c.ControlPlane == nil || c.ControlPlane.Endpoint == nil { return errors.New("cluster controlplane endpoint is required") } if err := sideronet.ValidateEndpointURI(c.ControlPlane.Endpoint.URL.String()); err != nil { result = multierror.Append(result, fmt.Errorf("invalid controlplane endpoint: %w", err)) } if c.ClusterNetwork != nil && c.ClusterNetwork.DNSDomain != "" && !isValidDNSName(c.ClusterNetwork.DNSDomain) { result = multierror.Append(result, fmt.Errorf("%q is not a valid DNS name", c.ClusterNetwork.DNSDomain)) } if ecp := c.ExternalCloudProviderConfig; ecp != nil { result = multierror.Append(result, ecp.Validate()) } if c.EtcdConfig != nil { if isControlPlane { result = multierror.Append(result, c.EtcdConfig.Validate()) } else { result = multierror.Append(result, errors.New("etcd config is only allowed on control plane machines")) } } if c.ClusterCA != nil && !isControlPlane && len(c.ClusterCA.Key) > 0 { result = multierror.Append(result, errors.New("cluster CA key is not allowed on non-controlplane nodes (.cluster.ca)")) } result = multierror.Append( result, c.ClusterInlineManifests.Validate(), c.ClusterDiscoveryConfig.Validate(c), c.APIServerConfig.Validate(), c.ControllerManagerConfig.Validate(), c.SchedulerConfig.Validate(), ) return result.ErrorOrNil() } // ValidateCNI validates CNI config. // //nolint:gocyclo func ValidateCNI(cni config.CNI) ([]string, error) { var ( warnings []string result *multierror.Error ) switch cni.Name() { case constants.FlannelCNI: if len(cni.URLs()) != 0 { err := fmt.Errorf(`"urls" field should be empty for %q CNI`, cni.Name()) result = multierror.Append(result, err) } case constants.NoneCNI: if len(cni.URLs()) != 0 { err := fmt.Errorf(`"urls" field should be empty for %q CNI`, cni.Name()) result = multierror.Append(result, err) } if len(cni.Flannel().ExtraArgs()) != 0 { err := fmt.Errorf(`"flanneldExtraArgs" field should be empty for %q CNI`, cni.Name()) result = multierror.Append(result, err) } if cni.Flannel().KubeNetworkPoliciesEnabled() { err := fmt.Errorf(`"flannelKubeNetworkPoliciesEnabled" should not be enabled for %q CNI`, cni.Name()) result = multierror.Append(result, err) } case constants.CustomCNI: if len(cni.URLs()) == 0 { warn := fmt.Sprintf(`"urls" field should not be empty for %q CNI`, cni.Name()) warnings = append(warnings, warn) } if len(cni.Flannel().ExtraArgs()) != 0 { err := fmt.Errorf(`"flanneldExtraArgs" field should be empty for %q CNI`, cni.Name()) result = multierror.Append(result, err) } if cni.Flannel().KubeNetworkPoliciesEnabled() { err := fmt.Errorf(`"flannelKubeNetworkPoliciesEnabled" should not be enabled for %q CNI`, cni.Name()) result = multierror.Append(result, err) } for _, u := range cni.URLs() { if err := sideronet.ValidateEndpointURI(u); err != nil { result = multierror.Append(result, err) } } default: err := fmt.Errorf("cni name should be one of [%q, %q, %q]", constants.FlannelCNI, constants.CustomCNI, constants.NoneCNI) result = multierror.Append(result, err) } return warnings, result.ErrorOrNil() } // Validate validates external cloud provider configuration. func (ecp *ExternalCloudProviderConfig) Validate() error { if !ecp.Enabled() && (len(ecp.ExternalManifests) != 0) { return errors.New("external cloud provider is disabled, but manifests are provided") } var result *multierror.Error for _, url := range ecp.ExternalManifests { if err := sideronet.ValidateEndpointURI(url); err != nil { err = fmt.Errorf("invalid external cloud provider manifest url %q: %w", url, err) result = multierror.Append(result, err) } } return result.ErrorOrNil() } // Validate the inline manifests. func (manifests ClusterInlineManifests) Validate() error { var result *multierror.Error manifestNames := map[string]struct{}{} for _, manifest := range manifests { if strings.TrimSpace(manifest.InlineManifestName) == "" { result = multierror.Append(result, errors.New("inline manifest name can't be empty")) } if _, ok := manifestNames[manifest.InlineManifestName]; ok { result = multierror.Append(result, fmt.Errorf("inline manifest name %q is duplicate", manifest.InlineManifestName)) } manifestNames[manifest.InlineManifestName] = struct{}{} } return result.ErrorOrNil() } // Validate the discovery config. func (c *ClusterDiscoveryConfig) Validate(clusterCfg *ClusterConfig) error { var result *multierror.Error if c == nil || !c.Enabled() { return nil } if c.Registries().Service().Enabled() { url, err := url.ParseRequestURI(c.Registries().Service().Endpoint()) if err != nil { result = multierror.Append(result, fmt.Errorf("cluster discovery service registry endpoint is invalid: %w", err)) } else if url.Path != "" && url.Path != "/" { result = multierror.Append(result, errors.New("cluster discovery service path should be empty")) } if clusterCfg.ID() == "" { result = multierror.Append(result, errors.New("cluster discovery service requires .cluster.id")) } if clusterCfg.Secret() == "" { result = multierror.Append(result, errors.New("cluster discovery service requires .cluster.secret")) } } return result.ErrorOrNil() } // ValidateNetworkDevices runs the specified validation checks specific to the // network devices. func ValidateNetworkDevices(d *Device, secondaryInterfaces map[string]string, checks ...NetworkDeviceCheck) ([]string, error) { var result *multierror.Error if d == nil { return nil, errors.New("empty device") } if d.Ignore() { return nil, result.ErrorOrNil() } var warnings []string for _, check := range checks { warn, err := check(d, secondaryInterfaces) warnings = append(warnings, warn...) result = multierror.Append(result, err) } return warnings, result.ErrorOrNil() } // CheckDeviceInterface ensures that the interface has been specified. // //nolint:gocyclo func CheckDeviceInterface(d *Device, _ map[string]string) ([]string, error) { var result *multierror.Error if d == nil { return nil, errors.New("empty device") } if d.DeviceInterface == "" && d.DeviceSelector == nil { result = multierror.Append(result, fmt.Errorf("[%s], [%s]: %w", "networking.os.device.interface", "networking.os.device.deviceSelector", ErrRequiredSectionOptions)) } else if d.DeviceInterface != "" && d.DeviceSelector != nil { result = multierror.Append(result, fmt.Errorf("[%s], [%s]: %w", "networking.os.device.interface", "networking.os.device.deviceSelector", ErrMutuallyExclusive)) } if d.DeviceSelector != nil && reflect.ValueOf(d.DeviceSelector).Elem().IsZero() { result = multierror.Append(result, fmt.Errorf("[%s]: %w", "networking.os.device.deviceSelector", ErrEmpty)) } if d.DeviceBond != nil { result = multierror.Append(result, checkBond(d.DeviceBond)) } if d.DeviceWireguardConfig != nil { result = multierror.Append(result, checkWireguard(d.DeviceWireguardConfig)) } if d.DeviceVlans != nil { result = multierror.Append(result, checkVlans(d)) } return nil, result.ErrorOrNil() } //nolint:gocyclo,cyclop func checkBond(b *Bond) error { var result *multierror.Error bondMode, err := nethelpers.BondModeByName(b.BondMode) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.BondXmitHashPolicyByName(b.BondHashPolicy) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.LACPRateByName(b.BondLACPRate) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.ARPValidateByName(b.BondARPValidate) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.ARPAllTargetsByName(b.BondARPAllTargets) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.PrimaryReselectByName(b.BondPrimaryReselect) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.FailOverMACByName(b.BondFailOverMac) if err != nil { result = multierror.Append(result, err) } _, err = nethelpers.ADSelectByName(b.BondADSelect) if err != nil { result = multierror.Append(result, err) } if b.BondMIIMon == 0 { if b.BondUpDelay != 0 { result = multierror.Append(result, errors.New("bond.upDelay can't be set if miiMon is zero")) } if b.BondDownDelay != 0 { result = multierror.Append(result, errors.New("bond.downDelay can't be set if miiMon is zero")) } } else { if b.BondUpDelay%b.BondMIIMon != 0 { result = multierror.Append(result, errors.New("bond.upDelay should be a multiple of miiMon")) } if b.BondDownDelay%b.BondMIIMon != 0 { result = multierror.Append(result, errors.New("bond.downDelay should be a multiple of miiMon")) } } if len(b.BondARPIPTarget) > 0 { result = multierror.Append(result, errors.New("bond.arpIPTarget is not supported")) } if b.BondLACPRate != "" && bondMode != nethelpers.BondMode8023AD { result = multierror.Append(result, errors.New("bond.lacpRate is only available in 802.3ad mode")) } if b.BondADActorSystem != "" { result = multierror.Append(result, errors.New("bond.adActorSystem is not supported")) } if (bondMode == nethelpers.BondMode8023AD || bondMode == nethelpers.BondModeALB || bondMode == nethelpers.BondModeTLB) && b.BondARPValidate != "" { result = multierror.Append(result, fmt.Errorf("bond.arpValidate is not available in %s mode", bondMode)) } if !(bondMode == nethelpers.BondModeActiveBackup || bondMode == nethelpers.BondModeALB || bondMode == nethelpers.BondModeTLB) && b.BondPrimary != "" { result = multierror.Append(result, fmt.Errorf("bond.primary is not available in %s mode", bondMode)) } if (bondMode == nethelpers.BondMode8023AD || bondMode == nethelpers.BondModeALB || bondMode == nethelpers.BondModeTLB) && b.BondARPInterval > 0 { result = multierror.Append(result, fmt.Errorf("bond.arpInterval is not available in %s mode", bondMode)) } if bondMode != nethelpers.BondModeRoundrobin && b.BondPacketsPerSlave > 1 { result = multierror.Append(result, fmt.Errorf("bond.packetsPerSlave is not available in %s mode", bondMode)) } if !(bondMode == nethelpers.BondModeALB || bondMode == nethelpers.BondModeTLB) && b.BondTLBDynamicLB > 0 { result = multierror.Append(result, fmt.Errorf("bond.tlbDynamicTLB is not available in %s mode", bondMode)) } if bondMode != nethelpers.BondMode8023AD && b.BondADActorSysPrio > 0 { result = multierror.Append(result, errors.New("bond.adActorSysPrio is only available in 802.3ad mode")) } if bondMode != nethelpers.BondMode8023AD && b.BondADUserPortKey > 0 { result = multierror.Append(result, errors.New("bond.adUserPortKey is only available in 802.3ad mode")) } return result.ErrorOrNil() } func checkWireguard(b *DeviceWireguardConfig) error { var result *multierror.Error // avoid pulling in wgctrl code to keep machinery dependencies slim checkKey := func(key string) error { raw, err := base64.StdEncoding.DecodeString(key) if err != nil { return err } if len(raw) != 32 { return fmt.Errorf("wrong key %q length: %d", key, len(raw)) } return nil } if err := checkKey(b.WireguardPrivateKey); err != nil { result = multierror.Append(result, fmt.Errorf("private key is invalid: %w", err)) } for _, peer := range b.WireguardPeers { if err := checkKey(peer.WireguardPublicKey); err != nil { result = multierror.Append(result, fmt.Errorf("public key invalid: %w", err)) } if peer.WireguardEndpoint != "" { if !sideronet.AddressContainsPort(peer.WireguardEndpoint) { result = multierror.Append(result, fmt.Errorf("peer endpoint %q is invalid", peer.WireguardEndpoint)) } } for _, allowedIP := range peer.WireguardAllowedIPs { if _, _, err := net.ParseCIDR(allowedIP); err != nil { result = multierror.Append(result, fmt.Errorf("peer allowed IP %q is invalid: %w", allowedIP, err)) } } } return result.ErrorOrNil() } func checkVlans(d *Device) error { var result *multierror.Error // check VLAN addressing for _, vlan := range d.DeviceVlans { if len(vlan.VlanAddresses) > 0 && vlan.VlanCIDR != "" { result = multierror.Append(result, fmt.Errorf("[%s] %s.%d: %s", "networking.os.device.vlan", d.DeviceInterface, vlan.VlanID, "vlan can't have both .cidr and .addresses set")) } if vlan.VlanCIDR != "" { if err := validateIPOrCIDR(vlan.VlanCIDR); err != nil { result = multierror.Append(result, fmt.Errorf("[%s] %s.%d: %w", "networking.os.device.vlan.CIDR", d.DeviceInterface, vlan.VlanID, err)) } } for _, address := range vlan.VlanAddresses { if err := validateIPOrCIDR(address); err != nil { result = multierror.Append(result, fmt.Errorf("[%s] %s.%d: %w", "networking.os.device.vlan.addresses", d.DeviceInterface, vlan.VlanID, err)) } } } return result.ErrorOrNil() } func validateIPOrCIDR(address string) error { if strings.IndexByte(address, '/') >= 0 { _, _, err := net.ParseCIDR(address) return err } if ip := net.ParseIP(address); ip == nil { return fmt.Errorf("failed to parse IP address %q", address) } return nil } // CheckDeviceAddressing ensures that an appropriate addressing method. // has been specified. // //nolint:gocyclo func CheckDeviceAddressing(d *Device, secondaryInterfaces map[string]string) ([]string, error) { var result *multierror.Error if d == nil { return nil, errors.New("empty device") } var warnings []string if _, paired := secondaryInterfaces[d.Interface()]; paired { if d.DHCP() || d.DeviceCIDR != "" || len(d.DeviceAddresses) > 0 || d.DeviceVIPConfig != nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %s", "networking.os.device", d.DeviceInterface, "bonded/bridged interface shouldn't have any addressing methods configured")) } } // ensure either legacy CIDR is set or new addresses, but not both if len(d.DeviceAddresses) > 0 && d.DeviceCIDR != "" { result = multierror.Append(result, fmt.Errorf("[%s] %q: %s", "networking.os.device", d.DeviceInterface, "interface can't have both .cidr and .addresses set")) } // ensure cidr is a valid address if d.DeviceCIDR != "" { if err := validateIPOrCIDR(d.DeviceCIDR); err != nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device.CIDR", d.DeviceInterface, err)) } warnings = append(warnings, fmt.Sprintf("%q: machine.network.interface.cidr is deprecated, please use machine.network.interface.addresses", d.DeviceInterface)) } // ensure addresses are valid addresses for _, address := range d.DeviceAddresses { if err := validateIPOrCIDR(address); err != nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device.addresses", d.DeviceInterface, err)) } } // check VIP IP is valid if d.DeviceVIPConfig != nil { if ip := net.ParseIP(d.DeviceVIPConfig.IP()); ip == nil { result = multierror.Append(result, fmt.Errorf("[%s] failed to parse %q as IP address", "networking.os.device.vip", d.DeviceVIPConfig.IP())) } } return warnings, result.ErrorOrNil() } // CheckDeviceRoutes ensures that the specified routes are valid. // //nolint:gocyclo func CheckDeviceRoutes(d *Device, _ map[string]string) ([]string, error) { var result *multierror.Error if d == nil { return nil, errors.New("empty device") } if len(d.DeviceRoutes) == 0 { return nil, result.ErrorOrNil() } for idx, route := range d.DeviceRoutes { if route.Network() != "" { if _, _, err := net.ParseCIDR(route.Network()); err != nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device.route["+strconv.Itoa(idx)+"].network", route.Network(), ErrInvalidAddress)) } } if route.Gateway() != "" { if ip := net.ParseIP(route.Gateway()); ip == nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device.route["+strconv.Itoa(idx)+"].gateway", route.Gateway(), ErrInvalidAddress)) } } if route.Gateway() == "" && route.Network() == "" { result = multierror.Append(result, fmt.Errorf("[%s]: %s", "networking.os.device.route["+strconv.Itoa(idx)+"]", "either network or gateway should be set")) } if route.Source() != "" { if ip := net.ParseIP(route.Source()); ip == nil { result = multierror.Append(result, fmt.Errorf("[%s] %q: %w", "networking.os.device.route["+strconv.Itoa(idx)+"].source", route.Source(), ErrInvalidAddress)) } } } return nil, result.ErrorOrNil() } // Validate kubelet configuration. func (k *KubeletConfig) Validate() ([]string, error) { var result *multierror.Error if k.KubeletNodeIP != nil { for _, cidr := range k.KubeletNodeIP.KubeletNodeIPValidSubnets { cidr = strings.TrimPrefix(cidr, "!") if _, err := sideronet.ParseSubnetOrAddress(cidr); err != nil { result = multierror.Append(result, fmt.Errorf("kubelet nodeIP subnet is not valid: %q", cidr)) } } } for _, field := range kubelet.ProtectedConfigurationFields { if _, exists := k.KubeletExtraConfig.Object[field]; exists { result = multierror.Append(result, fmt.Errorf("kubelet configuration field %q can't be overridden", field)) } } return nil, result.ErrorOrNil() } // Validate etcd configuration. func (e *EtcdConfig) Validate() error { var result *multierror.Error if e.CA() == nil { result = multierror.Append(result, ErrEmptyKeyCert) } if e.EtcdSubnet != "" && len(e.EtcdAdvertisedSubnets) > 0 { result = multierror.Append(result, errors.New("etcd subnet can't be set when advertised subnets are set")) } for _, cidr := range e.AdvertisedSubnets() { cidr = strings.TrimPrefix(cidr, "!") if _, err := sideronet.ParseSubnetOrAddress(cidr); err != nil { result = multierror.Append(result, fmt.Errorf("etcd advertised subnet is not valid: %q", cidr)) } } for _, cidr := range e.ListenSubnets() { cidr = strings.TrimPrefix(cidr, "!") if _, err := sideronet.ParseSubnetOrAddress(cidr); err != nil { result = multierror.Append(result, fmt.Errorf("etcd listen subnet is not valid: %q", cidr)) } } return result.ErrorOrNil() } // RuntimeValidate validates the config in runtime context. // // In runtime context, resource state is available. // //nolint:gocyclo func (c *Config) RuntimeValidate(ctx context.Context, st state.State, mode validation.RuntimeMode, opt ...validation.Option) ([]string, error) { var ( warnings []string result *multierror.Error ) if c.MachineConfig != nil { if mode.RequiresInstall() && c.MachineConfig.MachineInstall != nil { diskExpr, err := c.MachineConfig.MachineInstall.DiskMatchExpression() if err != nil { result = multierror.Append(result, fmt.Errorf("install disk selector is invalid: %w", err)) } else if diskExpr != nil { matchedDisks, err := blockhelpers.MatchDisks(ctx, st, diskExpr) if err != nil { result = multierror.Append(result, err) } if len(matchedDisks) == 0 { result = multierror.Append(result, fmt.Errorf("no disks matched the expression: %s", diskExpr)) } } } // if booted using sd-boot, extra kernel arguments are not supported if _, err := os.Stat("/sys/firmware/efi/efivars/StubInfo-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f"); err == nil { if len(c.MachineConfig.Install().ExtraKernelArgs()) > 0 { warnings = append(warnings, "extra kernel arguments are not supported when booting using SDBoot") } } if len(c.MachineConfig.Install().Extensions()) > 0 { warnings = append(warnings, ".machine.install.extensions is deprecated, please see https://docs.siderolabs.com/talos/latest/platform-specific-installations/boot-assets") } if err := ValidateKubernetesImageTag(c.Machine().Kubelet().Image()); err != nil { result = multierror.Append(result, fmt.Errorf("kubelet image is not valid: %w", err)) } } if c.ClusterConfig != nil && c.MachineConfig != nil { if c.Machine().Type().IsControlPlane() { for _, spec := range []struct { name string imageRef string }{ { name: "kube-apiserver", imageRef: c.Cluster().APIServer().Image(), }, { name: "kube-controller-manager", imageRef: c.Cluster().ControllerManager().Image(), }, { name: "kube-scheduler", imageRef: c.Cluster().Scheduler().Image(), }, } { if err := ValidateKubernetesImageTag(spec.imageRef); err != nil { result = multierror.Append(result, fmt.Errorf("%s image is not valid: %w", spec.name, err)) } } } } return warnings, result.ErrorOrNil() } // ValidateKubernetesImageTag validates the Kubernetes image tag format. func ValidateKubernetesImageTag(imageRef string) error { // this method is called from RuntimeValidate, so we are inside running Talos, // so the version of Talos is available, and we can check compatibility currentTalosVersion, err := compatibility.ParseTalosVersion(version.NewVersion()) if err != nil { return fmt.Errorf("failed to parse Talos version: %w", err) } k8sVersion, err := KubernetesVersionFromImageRef(imageRef) if err != nil { return fmt.Errorf("failed to parse Kubernetes version from image reference %q: %w", imageRef, err) } return k8sVersion.SupportedWith(currentTalosVersion) } // KubernetesVersionFromImageRef parses the Kubernetes version from the image reference. func KubernetesVersionFromImageRef(ref string) (*compatibility.KubernetesVersion, error) { idx := strings.LastIndex(ref, ":v") if idx == -1 { return nil, fmt.Errorf("invalid image reference: %q", ref) } versionPart := ref[idx+2:] if shaIndex := strings.Index(versionPart, "@"); shaIndex != -1 { versionPart = versionPart[:shaIndex] } return compatibility.ParseKubernetesVersion(versionPart) } ================================================ FILE: pkg/machinery/config/types/v1alpha1/v1alpha1_validation_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "fmt" "net/url" "strings" "testing" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/compatibility" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/config/validation" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/version" ) type runtimeMode struct { requiresInstall bool } func (m runtimeMode) String() string { return fmt.Sprintf("runtimeMode(%v)", m.requiresInstall) } func (m runtimeMode) RequiresInstall() bool { return m.requiresInstall } func (runtimeMode) InContainer() bool { return false } func TestValidate(t *testing.T) { t.Parallel() endpointURL, err := url.Parse("https://localhost:6443/") require.NoError(t, err) for _, test := range []struct { name string config *v1alpha1.Config requiresInstall bool strict bool expectedWarnings []string expectedError string }{ { name: "NoMachine", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", }, expectedError: "1 error occurred:\n\t* machine instructions are required\n\n", }, { name: "NoMachineType", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedWarnings: []string{ `use "worker" instead of "" for machine type`, }, }, { name: "JoinMachineType", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "join", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedWarnings: []string{ `use "worker" instead of "join" for machine type`, }, }, { name: "NoMachineTypeStrict", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, strict: true, expectedError: "1 error occurred:\n\t* warning: use \"worker\" instead of \"\" for machine type\n\n", }, { name: "WorkerNoAcceptedCAs", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{}, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, strict: true, expectedError: "1 error occurred:\n\t* trusted CA certificates are required on non-controlplane nodes (.machine.ca.crt, .machine.acceptedCAs)\n\n", }, { name: "WorkerOnlyAcceptedCAs", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineAcceptedCAs: []*x509.PEMEncodedCertificate{ { Crt: []byte("foo"), }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, strict: true, }, { name: "ControlplaneNoCAKey", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, strict: true, expectedError: "1 error occurred:\n\t* issuing CA key is required for controlplane nodes (.machine.ca.key)\n\n", }, { name: "NoMachineInstall", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, }, { name: "NoMachineInstallRequired", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, requiresInstall: true, expectedError: "1 error occurred:\n\t* install instructions are required in \"runtimeMode(true)\" mode\n\n", }, { name: "MachineInstallDisk", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineInstall: &v1alpha1.InstallConfig{ InstallDisk: "/dev/vda", }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, requiresInstall: true, }, { name: "MachineInstallExtensionsDuplicate", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineInstall: &v1alpha1.InstallConfig{ InstallDisk: "/dev/vda", InstallExtensions: []v1alpha1.InstallExtensionConfig{ { ExtensionImage: "ghcr.io/siderolabs/gvisor:v0.1.0", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, requiresInstall: true, expectedWarnings: []string{ ".machine.install.extensions is deprecated, please see https://docs.siderolabs.com/talos/latest/platform-specific-installations/boot-assets", }, }, { name: "MachineInstallExtraArgsAndGrubUseUKICmdline", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineInstall: &v1alpha1.InstallConfig{ InstallDisk: "/dev/vda", InstallExtraKernelArgs: []string{"foo=bar"}, InstallGrubUseUKICmdline: new(true), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, requiresInstall: true, expectedError: "1 error occurred:\n\t* install.extraKernelArgs and install.grubUseUKICmdline can't be used together\n\n", }, { name: "ExternalCloudProviderEnabled", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalEnabled: new(true), ExternalManifests: []string{ "https://www.example.com/manifest1.yaml", "https://www.example.com/manifest2.yaml", }, }, }, }, }, { name: "ExternalCloudProviderEnabledNoManifests", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalEnabled: new(true), }, }, }, }, { name: "ExternalCloudProviderDisabled", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{}, }, }, }, { name: "ExternalCloudProviderExtraManifests", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalManifests: []string{ "https://www.example.com/manifest1.yaml", "https://www.example.com/manifest2.yaml", }, }, }, }, expectedError: "1 error occurred:\n\t* external cloud provider is disabled, but manifests are provided\n\n", }, { name: "ExternalCloudProviderInvalidManifests", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ExternalCloudProviderConfig: &v1alpha1.ExternalCloudProviderConfig{ ExternalEnabled: new(true), ExternalManifests: []string{ "/manifest.yaml", }, }, }, }, expectedError: "1 error occurred:\n\t* invalid external cloud provider manifest url \"/manifest.yaml\": hostname must not be blank\n\n", }, { name: "InlineManifests", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterInlineManifests: v1alpha1.ClusterInlineManifests{ { InlineManifestName: "", }, { InlineManifestName: "foo", }, { InlineManifestName: "bar", }, { InlineManifestName: "foo", }, }, }, }, expectedError: "2 errors occurred:\n\t* inline manifest name can't be empty\n\t* inline manifest name \"foo\" is duplicate\n\n", }, { name: "DNSDomainEmpty", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterNetwork: &v1alpha1.ClusterNetworkConfig{}, }, }, }, { name: "DeviceCIDRInvalid", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceCIDR: "10.3.x", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.CIDR] \"eth0\": failed to parse IP address \"10.3.x\"\n\n", expectedWarnings: []string{"\"eth0\": machine.network.interface.cidr is deprecated, please use machine.network.interface.addresses"}, }, { name: "DeviceAddressInvalid", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceAddresses: []string{ "192.168.0.5/24", "10.35.7.8", "10.3.x/24", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.addresses] \"eth0\": invalid CIDR address: 10.3.x/24\n\n", }, { name: "DeviceAddressAndCIDR", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceCIDR: "192.24.3.45", DeviceAddresses: []string{ "192.168.0.5/24", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device] \"eth0\": interface can't have both .cidr and .addresses set\n\n", expectedWarnings: []string{"\"eth0\": machine.network.interface.cidr is deprecated, please use machine.network.interface.addresses"}, }, { name: "VlanCIDRInvalid", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 25, VlanCIDR: "10.3.x", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.vlan.CIDR] eth0.25: failed to parse IP address \"10.3.x\"\n\n", }, { name: "VlanAddressInvalid", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 25, VlanAddresses: []string{ "192.168.0.5/24", "10.35.7.8", "10.3.x/24", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.vlan.addresses] eth0.25: invalid CIDR address: 10.3.x/24\n\n", }, { name: "VlanAddressAndCIDR", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceVlans: []*v1alpha1.Vlan{ { VlanID: 26, VlanCIDR: "192.24.3.45", VlanAddresses: []string{ "192.168.0.5/24", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.vlan] eth0.26: vlan can't have both .cidr and .addresses set\n\n", }, { name: "BondDefaultConfig", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{}, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, }, { name: "BondWrongMode", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondMode: "roundrobin", BondUpDelay: 100, BondPacketsPerSlave: 8, BondADActorSysPrio: 48, BondInterfaces: []string{ "eth0", "eth1", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "3 errors occurred:\n\t* invalid bond type roundrobin\n\t* bond.upDelay can't be set if miiMon is zero\n\t* bond.adActorSysPrio is only available in 802.3ad mode\n\n", }, { name: "BondInterfacesAndSelectors", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth0", "eth1", }, BondDeviceSelectors: []v1alpha1.NetworkDeviceSelector{ { NetworkDeviceKernelDriver: "virtio", }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* interface \"bond0\" has both interfaces and selectors set: config sections are mutually exclusive\n\n", }, { name: "BondDoubleBond", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth0", "eth1", }, }, }, { DeviceInterface: "bond1", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth1", "eth2", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* interface \"eth1\" is declared as part of two separate links: \"bond0\" and \"bond1\"\n\n", }, { name: "BondSlaveAddressing", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth0", "eth1", "eth2", }, }, }, { DeviceInterface: "eth0", DeviceDHCP: new(true), }, { DeviceInterface: "eth1", DeviceAddresses: []string{ "192.168.0.1/24", }, }, { DeviceInterface: "eth2", DeviceCIDR: "192.168.1.1/24", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "3 errors occurred:\n" + "\t* [networking.os.device] \"eth0\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\t* [networking.os.device] \"eth1\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\t* [networking.os.device] \"eth2\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\n", expectedWarnings: []string{ "\"eth2\": machine.network.interface.cidr is deprecated, please use machine.network.interface.addresses", }, }, { name: "BridgeSlaveAddressing", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{ "eth0", "eth1", "eth2", }, }, }, { DeviceInterface: "eth0", DeviceDHCP: new(true), }, { DeviceInterface: "eth1", DeviceAddresses: []string{ "192.168.0.1/24", }, }, { DeviceInterface: "eth2", DeviceCIDR: "192.168.1.1/24", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "3 errors occurred:\n" + "\t* [networking.os.device] \"eth0\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\t* [networking.os.device] \"eth1\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\t* [networking.os.device] \"eth2\": bonded/bridged interface shouldn't have any addressing methods configured\n" + "\n", expectedWarnings: []string{ "\"eth2\": machine.network.interface.cidr is deprecated, please use machine.network.interface.addresses", }, }, { name: "BridgeDoubleBridge", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{ "eth0", "eth1", }, }, }, { DeviceInterface: "br1", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{ "eth1", "eth2", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* interface \"eth1\" is declared as part of two separate links: \"br0\" and \"br1\"\n\n", }, { name: "InterfaceIsBothBridgeAndBond", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "bridgebond0", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{ "eth0", "eth1", }, }, DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth2", "eth3", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* interface has both bridge and bond sections set \"bridgebond0\": config sections are mutually exclusive\n\n", }, { name: "InterfacePartOfBridgeAndBond", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "br0", DeviceBridge: &v1alpha1.Bridge{ BridgedInterfaces: []string{ "eth0", "eth1", }, }, }, { DeviceInterface: "bond0", DeviceBond: &v1alpha1.Bond{ BondInterfaces: []string{ "eth1", "eth2", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* interface \"eth1\" is declared as part of two separate links: \"br0\" and \"bond0\"\n\n", }, { name: "Wireguard", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "wireguard0", DeviceWireguardConfig: &v1alpha1.DeviceWireguardConfig{ WireguardPrivateKey: "ONtS+jU1Q+ZHLgs7DbYvnF5Iyj+koxBtvknDigFsdG8=", WireguardPeers: []*v1alpha1.DeviceWireguardPeer{ {}, { WireguardPublicKey: "4A3rogGVHuVjeZz5cbqryWXGkGBdIGC0E6+5mX2Iz1A=", WireguardEndpoint: "example.com:1234", WireguardAllowedIPs: []string{ "10.2.0.5/31", "2.4.5.3/32", }, }, { WireguardPublicKey: "4A3rogGVHuVjeZz5cbqryWXGkGBdIGC0E6+5mX2Iz1==", WireguardEndpoint: "12.3.4.5", WireguardAllowedIPs: []string{ "10.2.0", }, }, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "4 errors occurred:\n\t* public key invalid: wrong key \"\" length: 0\n\t* public key invalid: wrong key \"4A3rogGVHuVjeZz5cbqryWXGkGBdIGC0E6+5mX2Iz1==\" length: 31\n" + "\t* peer endpoint \"12.3.4.5\" is invalid\n\t* peer allowed IP \"10.2.0\" is invalid: invalid CIDR address: 10.2.0\n\n", }, { name: "StaticRoutes", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceRoutes: []*v1alpha1.Route{ { RouteGateway: "172.0.0.1", }, { RouteNetwork: "10.0.0.0/24", RouteGateway: "10.0.0.1", }, { RouteNetwork: "10.0.0.0/24", RouteGateway: "10.0.0.1", RouteSource: "10.0.0.5", }, { RouteGateway: "172.0.0.x", }, { RouteNetwork: "10.0.0.0", RouteGateway: "10.0.0.1", }, { RouteNetwork: "10.0.0.0/24", RouteGateway: "10.0.0.1", RouteSource: "10.0.0.3/32", }, { RouteNetwork: "169.254.254.254/32", }, { RouteSource: "10.0.0.3", }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "4 errors occurred:\n\t* [networking.os.device.route[3].gateway] \"172.0.0.x\": invalid network address\n" + "\t* [networking.os.device.route[4].network] \"10.0.0.0\": invalid network address\n" + "\t* [networking.os.device.route[5].source] \"10.0.0.3/32\": invalid network address\n" + "\t* [networking.os.device.route[7]]: either network or gateway should be set\n\n", }, { name: "KubeSpanNoDiscovery", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "3 errors occurred:\n\t* .cluster.discovery should be enabled when .machine.network.kubespan is enabled\n" + "\t* .cluster.id should be set when .machine.network.kubespan is enabled\n" + "\t* .cluster.secret should be set when .machine.network.kubespan is enabled\n\n", }, { name: "DiscoveryServiceEndpoint", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ClusterID: "foo", ClusterSecret: "bar", ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), DiscoveryRegistries: v1alpha1.DiscoveryRegistriesConfig{ RegistryService: v1alpha1.RegistryServiceConfig{ RegistryEndpoint: "foo", }, }, }, }, }, expectedError: "1 error occurred:\n\t* cluster discovery service registry endpoint is invalid: parse \"foo\": invalid URI for request\n\n", }, { name: "DiscoveryServiceClusterIDSecret", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "2 errors occurred:\n\t* cluster discovery service requires .cluster.id\n\t* cluster discovery service requires .cluster.secret\n\n", }, { name: "EtcdMissingCa", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, EtcdConfig: &v1alpha1.EtcdConfig{}, }, }, expectedError: "1 error occurred:\n\t* key/cert combination should not be empty\n\n", }, { name: "EtcdConfigProvidedForWorker", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, EtcdConfig: &v1alpha1.EtcdConfig{}, }, }, expectedError: "1 error occurred:\n\t* etcd config is only allowed on control plane machines\n\n", }, { name: "GoodEtcdSubnet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, EtcdConfig: &v1alpha1.EtcdConfig{ RootCA: &x509.PEMEncodedCertificateAndKey{}, EtcdAdvertisedSubnets: []string{ "10.0.0.0/8", "!1.1.1.1/32", }, EtcdListenSubnets: []string{ "10.0.0.0/8", "1.1.1.1/32", }, }, }, }, expectedError: "", }, { name: "BadEtcdSubnet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, EtcdConfig: &v1alpha1.EtcdConfig{ RootCA: &x509.PEMEncodedCertificateAndKey{}, EtcdAdvertisedSubnets: []string{ "1234:", }, EtcdListenSubnets: []string{ "10", }, }, }, }, expectedError: "2 errors occurred:\n\t* etcd advertised subnet is not valid: \"1234:\"\n\t* etcd listen subnet is not valid: \"10\"\n\n", }, { name: "GoodKubeletSubnet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineKubelet: &v1alpha1.KubeletConfig{ KubeletNodeIP: &v1alpha1.KubeletNodeIPConfig{ KubeletNodeIPValidSubnets: []string{ "10.0.0.0/8", "!10.0.0.3/32", "!fd00::169:254:2:53/128", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "", }, { name: "BadKubeletSubnet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineKubelet: &v1alpha1.KubeletConfig{ KubeletNodeIP: &v1alpha1.KubeletNodeIPConfig{ KubeletNodeIPValidSubnets: []string{ "10.0.0.0.3", "[fd00::169:254:2:53]:344", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "2 errors occurred:\n" + "\t* kubelet nodeIP subnet is not valid: \"10.0.0.0.3\"\n" + "\t* kubelet nodeIP subnet is not valid: \"[fd00::169:254:2:53]:344\"\n" + "\n", }, { name: "BadKubeletExtraConfig", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineAcceptedCAs: []*x509.PEMEncodedCertificate{ { Crt: []byte("foo"), }, }, MachineKubelet: &v1alpha1.KubeletConfig{ KubeletExtraConfig: v1alpha1.Unstructured{ Object: map[string]any{ "port": 345, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* kubelet configuration field \"port\" can't be overridden\n\n", }, { name: "DeviceInterfaceInvalid", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ {}, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.interface], [networking.os.device.deviceSelector]: required either config section to be set\n\n", }, { name: "DeviceSelectorAndInterfaceSet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceInterface: "eth0", DeviceSelector: &v1alpha1.NetworkDeviceSelector{ NetworkDeviceBus: "00:01", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.interface], [networking.os.device.deviceSelector]: config sections are mutually exclusive\n\n", }, { name: "DeviceSelectorAndInterfaceSet", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkInterfaces: []*v1alpha1.Device{ { DeviceSelector: &v1alpha1.NetworkDeviceSelector{}, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* [networking.os.device.deviceSelector]: config section should contain at least one field\n\n", }, { name: "TalosAPIAccessWorker", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineFeatures: &v1alpha1.FeaturesConfig{ RBAC: new(true), KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{ AccessEnabled: new(true), }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* feature Kubernetes Talos API Access can only be enabled on control plane machines\n\n", }, { name: "TalosAPIAccessInvalidRole", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineFeatures: &v1alpha1.FeaturesConfig{ RBAC: new(true), KubernetesTalosAPIAccessConfig: &v1alpha1.KubernetesTalosAPIAccessConfig{ AccessEnabled: new(true), AccessAllowedRoles: []string{ "os:reader", "invalid:role1", "os:etcd:backup", "invalid:role2", }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "2 errors occurred:\n\t* invalid role \"invalid:role1\" in allowed roles for " + "Kubernetes Talos API Access\n\t* invalid role \"invalid:role2\" in allowed roles for " + "Kubernetes Talos API Access\n\n", }, { name: "NodeLabels", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNodeLabels: map[string]string{ "/foo": "bar", "key": "value", "talos.dev/foo": "bar", "@!": "#$", "123@.dev/": "456", "a/b/c": strings.Repeat("a", 64), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* invalid machine node labels: 6 errors occurred:\n\t* prefix cannot be empty: \"/foo\"\n\t* prefix \"123@.dev\" is invalid: domain doesn't match required format: \"123@.dev\"\n\t* name \"@!\" is invalid\n\t* label value \"#$\" is invalid\n\t* invalid format: too many slashes: \"a/b/c\"\n\t* label value length exceeds limit of 63: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\n\n\n", //nolint:lll }, { name: "GoodKubeSpanEndpointFilters", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), KubeSpanFilters: &v1alpha1.KubeSpanFilters{ KubeSpanFiltersEndpoints: []string{ "0.0.0.0/0", "::/0", "!192.168.0.0/16", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterID: "test", ClusterSecret: "test", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "", }, { name: "BadKubeSpanEndpointFilters", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), KubeSpanFilters: &v1alpha1.KubeSpanFilters{ KubeSpanFiltersEndpoints: []string{ "!10", "123::/456", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterID: "test", ClusterSecret: "test", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "2 errors occurred:\n\t* KubeSpan endpoint filter is not valid: \"10\"\n\t* KubeSpan endpoint filter is not valid: \"123::/456\"\n\n", }, { name: "GoodKubeSpanAdvertisedNetworkFilters", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), KubeSpanFilters: &v1alpha1.KubeSpanFilters{ KubeSpanFiltersExcludeAdvertisedNetworks: []string{ "0.0.0.0/0", "10.0.0.0/8", "172.16.0.0/12", "::/0", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterID: "test", ClusterSecret: "test", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "", }, { name: "BadKubeSpanAdvertisedNetworkFilters", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), KubeSpanFilters: &v1alpha1.KubeSpanFilters{ KubeSpanFiltersExcludeAdvertisedNetworks: []string{ "invalid", "123::/456", }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterID: "test", ClusterSecret: "test", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "2 errors occurred:\n\t* KubeSpan exclude advertised networks filter is not valid: \"invalid\"\n\t* KubeSpan exclude advertised networks filter is not valid: \"123::/456\"\n\n", }, { name: "KubeSpanSmallMTU", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), }, MachineNetwork: &v1alpha1.NetworkConfig{ NetworkKubeSpan: &v1alpha1.NetworkKubeSpan{ KubeSpanEnabled: new(true), KubeSpanMTU: new(uint32(576)), }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, ClusterID: "test", ClusterSecret: "test", ClusterDiscoveryConfig: &v1alpha1.ClusterDiscoveryConfig{ DiscoveryEnabled: new(true), }, }, }, expectedError: "1 error occurred:\n\t* kubespan link MTU must be at least 1280\n\n", }, { name: "ControlPlanePodResources", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Requests: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": "1m", "invalid1": "23", }, }, }, }, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Limits: v1alpha1.Unstructured{ Object: map[string]any{ "memory": "1m", "invalid2": "23", }, }, }, }, SchedulerConfig: &v1alpha1.SchedulerConfig{ ResourcesConfig: &v1alpha1.ResourcesConfig{ Requests: v1alpha1.Unstructured{ Object: map[string]any{ "cpu": "1m", }, }, Limits: v1alpha1.Unstructured{ Object: map[string]any{ "invalid3": "23", }, }, }, }, }, }, expectedError: "3 errors occurred:\n\t* apiserver resource validation failed: unsupported pod resource \"invalid1\"\n\t* controller-manager resource validation failed: unsupported pod resource \"invalid2\"\n\t* scheduler resource validation failed: unsupported pod resource \"invalid3\"\n\n", //nolint:lll }, { name: "ControlPlaneInvalidAuthorizationConfig", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{ { AuthorizerName: "foo", }, }, }, }, }, expectedError: "1 error occurred:\n\t* apiserver authorization config validation failed: authorizer type must be set\n\n", }, { name: "ControlPlaneAuthorizationConfigWithAuthorizationModeFlag", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{}, ExtraArgsConfig: v1alpha1.Args{ "authorization-mode": v1alpha1.NewArgValue("Node", nil), }, }, }, }, expectedError: "1 error occurred:\n\t* authorization-mode cannot be used in conjunction with AuthorizationConfig, use eitherr AuthorizationConfig or authorization-mode\n\n", }, { name: "ControlPlaneAuthorizationConfigWithAuthorizationWebhook", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ AuthorizationConfigConfig: []*v1alpha1.AuthorizationConfigAuthorizerConfig{}, ExtraArgsConfig: v1alpha1.Args{ "authorization-webhook-version": v1alpha1.NewArgValue("v1", nil), }, }, }, }, expectedError: "1 error occurred:\n\t* authorization-webhook-* flags cannot be used in conjunction with AuthorizationConfig, use either AuthorizationConfig or authorization-webhook-* flags\n\n", }, { name: "MachineBaseRuntimeSpecOverrides", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineBaseRuntimeSpecOverrides: v1alpha1.Unstructured{ Object: map[string]any{ "process": map[string]any{ "rlimits": []map[string]any{ { "type": "RLIMIT_NOFILE", "hard": 1024, "soft": 1024, }, }, }, }, }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, }, { name: "MachineFeaturesInvalidAddressSortingAlgorithm", config: &v1alpha1.Config{ ConfigVersion: "v1alpha1", MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineCA: &x509.PEMEncodedCertificateAndKey{ Crt: []byte("foo"), Key: []byte("bar"), }, MachineFeatures: &v1alpha1.FeaturesConfig{ FeatureNodeAddressSortAlgorithm: "xyz", }, }, ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ endpointURL, }, }, }, }, expectedError: "1 error occurred:\n\t* invalid node address sort algorithm: xyz does not belong to AddressSortAlgorithm values\n\n", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() opts := []validation.Option{validation.WithLocal()} if test.strict { opts = append(opts, validation.WithStrict()) } warnings, errors := test.config.Validate(runtimeMode{test.requiresInstall}, opts...) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError == "" { assert.NoError(t, errors) } else { assert.EqualError(t, errors, test.expectedError) } }) } } func TestValidateCNI(t *testing.T) { t.Parallel() for _, test := range []struct { name string config *v1alpha1.CNIConfig expectedWarnings []string expectedError string }{ { name: "Nil", expectedError: "", // Flannel is used by default }, { name: "Empty", config: &v1alpha1.CNIConfig{}, expectedError: "1 error occurred:\n\t* cni name should be one of [\"flannel\", \"custom\", \"none\"]\n\n", }, { name: "FlannelNoManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.FlannelCNI, }, }, { name: "FlannelManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.FlannelCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, }, expectedError: "1 error occurred:\n\t* \"urls\" field should be empty for \"flannel\" CNI\n\n", }, { name: "FlannelExtraArgs", config: &v1alpha1.CNIConfig{ CNIName: constants.FlannelCNI, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlanneldExtraArgs: []string{"--foo"}, }, }, }, { name: "FlannelKubeNetworkPoliciesDisabled", config: &v1alpha1.CNIConfig{ CNIName: constants.FlannelCNI, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(false), }, }, }, { name: "FlannelKubeNetworkPoliciesEnabled", config: &v1alpha1.CNIConfig{ CNIName: constants.FlannelCNI, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(true), }, }, }, { name: "CustomNoManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, }, expectedWarnings: []string{ "\"urls\" field should not be empty for \"custom\" CNI", }, }, { name: "CustomFlannelExtraArgs", config: &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlanneldExtraArgs: []string{"--foo"}, }, }, expectedError: "1 error occurred:\n\t* \"flanneldExtraArgs\" field should be empty for \"custom\" CNI\n\n", }, { name: "CustomFlannelKubeNetworkPoliciesDisabled", config: &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(false), }, }, }, { name: "CustomFlannelKubeNetworkPoliciesEnabled", config: &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(true), }, }, expectedError: "1 error occurred:\n\t* \"flannelKubeNetworkPoliciesEnabled\" should not be enabled for \"custom\" CNI\n\n", }, { name: "CustomManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.CustomCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, }, }, { name: "NoneNoManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.NoneCNI, }, }, { name: "NoneManifests", config: &v1alpha1.CNIConfig{ CNIName: constants.NoneCNI, CNIUrls: []string{ "https://host.test/quick-install.yaml", }, }, expectedError: "1 error occurred:\n\t* \"urls\" field should be empty for \"none\" CNI\n\n", }, { name: "NoneFlannelKubeNetworkPoliciesDisabled", config: &v1alpha1.CNIConfig{ CNIName: constants.NoneCNI, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(false), }, }, }, { name: "NoneFlannelKubeNetworkPoliciesEnabled", config: &v1alpha1.CNIConfig{ CNIName: constants.NoneCNI, CNIFlannel: &v1alpha1.FlannelCNIConfig{ FlannelKubeNetworkPoliciesEnabled: new(true), }, }, expectedError: "1 error occurred:\n\t* \"flannelKubeNetworkPoliciesEnabled\" should not be enabled for \"none\" CNI\n\n", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() var cc v1alpha1.ClusterConfig if test.config != nil { cc.ClusterNetwork = &v1alpha1.ClusterNetworkConfig{ CNI: test.config, } } cni := cc.CNI() require.NotNil(t, cni, "CNI() method should return default value for empty config") warnings, errrors := v1alpha1.ValidateCNI(cni) assert.Equal(t, test.expectedWarnings, warnings) if test.expectedError == "" { assert.NoError(t, errrors) } else { assert.EqualError(t, errrors, test.expectedError) } }) } } func TestKubernetesVersionFromImageRef(t *testing.T) { t.Parallel() for _, test := range []struct { imageRef string expectedVersion string }{ { imageRef: "ghcr.io/siderolabs/kubelet:v1.32.2", expectedVersion: "1.32.2", }, { imageRef: "ghcr.io/siderolabs/kubelet:v1.32.2@sha256:123456", expectedVersion: "1.32.2", }, } { t.Run(test.imageRef, func(t *testing.T) { t.Parallel() version, err := v1alpha1.KubernetesVersionFromImageRef(test.imageRef) require.NoError(t, err) assert.Equal(t, test.expectedVersion, version.String()) }) } } func TestRuntimeValidate(t *testing.T) { t.Parallel() endpointURL, err := url.Parse("https://localhost:6443/") require.NoError(t, err) for _, test := range []struct { name string config *v1alpha1.Config requiresInstall bool strict bool expectedWarnings []string expectedError string }{ { name: "valid", config: &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: endpointURL, }, }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", }, }, }, { name: "old kubelet version", config: &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: endpointURL, }, }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "worker", MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: constants.KubeletImage + ":v1.24.0", }, }, }, expectedError: "1 error occurred:\n\t* kubelet image is not valid: version of Kubernetes 1.24.0 is too old to be used with Talos VERSION\n\n", }, { name: "old api-server version", config: &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ContainerImage: constants.KubernetesAPIServerImage + ":v1.24.0", }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: constants.KubeletImage + ":v" + constants.DefaultKubernetesVersion, }, }, }, expectedError: "1 error occurred:\n\t* kube-apiserver image is not valid: version of Kubernetes 1.24.0 is too old to be used with Talos VERSION\n\n", }, { name: "old controller-manager and scheduler version", config: &v1alpha1.Config{ ClusterConfig: &v1alpha1.ClusterConfig{ ControlPlane: &v1alpha1.ControlPlaneConfig{ Endpoint: &v1alpha1.Endpoint{ URL: endpointURL, }, }, APIServerConfig: &v1alpha1.APIServerConfig{ ContainerImage: constants.KubernetesAPIServerImage + ":v" + constants.DefaultKubernetesVersion, }, ControllerManagerConfig: &v1alpha1.ControllerManagerConfig{ ContainerImage: constants.KubernetesControllerManagerImage + ":v1.24.0", }, SchedulerConfig: &v1alpha1.SchedulerConfig{ ContainerImage: constants.KubernetesSchedulerImage + ":v1.24.0", }, }, MachineConfig: &v1alpha1.MachineConfig{ MachineType: "controlplane", MachineKubelet: &v1alpha1.KubeletConfig{ KubeletImage: constants.KubeletImage + ":v" + constants.DefaultKubernetesVersion, }, }, }, expectedError: "2 errors occurred:\n\t* kube-controller-manager image is not valid: version of Kubernetes 1.24.0 is too old to be used with Talos VERSION\n\t* kube-scheduler image is not valid: version of Kubernetes 1.24.0 is too old to be used with Talos VERSION\n\n", //nolint:lll }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() var opts []validation.Option if test.strict { opts = append(opts, validation.WithStrict()) } st := state.WrapCore(inmem.NewState("")) warnings, errors := test.config.RuntimeValidate(t.Context(), st, runtimeMode{test.requiresInstall}, opts...) assert.Equal(t, test.expectedWarnings, warnings) currentTalosVersion, err := compatibility.ParseTalosVersion(version.NewVersion()) require.NoError(t, err) if test.expectedError == "" { assert.NoError(t, errors) } else { test.expectedError = strings.ReplaceAll(test.expectedError, "VERSION", currentTalosVersion.String()) assert.EqualError(t, errors, test.expectedError) } }) } } ================================================ FILE: pkg/machinery/config/types/v1alpha1/zz_generated.deepcopy.go ================================================ //go:build !ignore_autogenerated // +build !ignore_autogenerated // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by deepcopy-gen. DO NOT EDIT. package v1alpha1 import ( x509 "github.com/siderolabs/crypto/x509" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *APIServerConfig) DeepCopyInto(out *APIServerConfig) { *out = *in if in.ExtraArgsConfig != nil { in, out := &in.ExtraArgsConfig, &out.ExtraArgsConfig *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ExtraVolumesConfig != nil { in, out := &in.ExtraVolumesConfig, &out.ExtraVolumesConfig *out = make([]VolumeMountConfig, len(*in)) copy(*out, *in) } if in.EnvConfig != nil { in, out := &in.EnvConfig, &out.EnvConfig *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.CertSANs != nil { in, out := &in.CertSANs, &out.CertSANs *out = make([]string, len(*in)) copy(*out, *in) } if in.DisablePodSecurityPolicyConfig != nil { in, out := &in.DisablePodSecurityPolicyConfig, &out.DisablePodSecurityPolicyConfig *out = new(bool) **out = **in } if in.AdmissionControlConfig != nil { in, out := &in.AdmissionControlConfig, &out.AdmissionControlConfig *out = make(AdmissionPluginConfigList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(AdmissionPluginConfig) (*in).DeepCopyInto(*out) } } } in.AuditPolicyConfig.DeepCopyInto(&out.AuditPolicyConfig) if in.ResourcesConfig != nil { in, out := &in.ResourcesConfig, &out.ResourcesConfig *out = new(ResourcesConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new APIServerConfig. func (in *APIServerConfig) DeepCopy() *APIServerConfig { if in == nil { return nil } out := new(APIServerConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AdminKubeconfigConfig) DeepCopyInto(out *AdminKubeconfigConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdminKubeconfigConfig. func (in *AdminKubeconfigConfig) DeepCopy() *AdminKubeconfigConfig { if in == nil { return nil } out := new(AdminKubeconfigConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AdmissionPluginConfig) DeepCopyInto(out *AdmissionPluginConfig) { *out = *in in.PluginConfiguration.DeepCopyInto(&out.PluginConfiguration) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionPluginConfig. func (in *AdmissionPluginConfig) DeepCopy() *AdmissionPluginConfig { if in == nil { return nil } out := new(AdmissionPluginConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in AdmissionPluginConfigList) DeepCopyInto(out *AdmissionPluginConfigList) { { in := &in *out = make(AdmissionPluginConfigList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(AdmissionPluginConfig) (*in).DeepCopyInto(*out) } } return } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdmissionPluginConfigList. func (in AdmissionPluginConfigList) DeepCopy() AdmissionPluginConfigList { if in == nil { return nil } out := new(AdmissionPluginConfigList) in.DeepCopyInto(out) return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in Base64Bytes) DeepCopyInto(out *Base64Bytes) { { in := &in *out = make(Base64Bytes, len(*in)) copy(*out, *in) return } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Base64Bytes. func (in Base64Bytes) DeepCopy() Base64Bytes { if in == nil { return nil } out := new(Base64Bytes) in.DeepCopyInto(out) return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bond) DeepCopyInto(out *Bond) { *out = *in if in.BondInterfaces != nil { in, out := &in.BondInterfaces, &out.BondInterfaces *out = make([]string, len(*in)) copy(*out, *in) } if in.BondDeviceSelectors != nil { in, out := &in.BondDeviceSelectors, &out.BondDeviceSelectors *out = make([]NetworkDeviceSelector, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.BondARPIPTarget != nil { in, out := &in.BondARPIPTarget, &out.BondARPIPTarget *out = make([]string, len(*in)) copy(*out, *in) } if in.BondUseCarrier != nil { in, out := &in.BondUseCarrier, &out.BondUseCarrier *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bond. func (in *Bond) DeepCopy() *Bond { if in == nil { return nil } out := new(Bond) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Bridge) DeepCopyInto(out *Bridge) { *out = *in if in.BridgedInterfaces != nil { in, out := &in.BridgedInterfaces, &out.BridgedInterfaces *out = make([]string, len(*in)) copy(*out, *in) } if in.BridgeSTP != nil { in, out := &in.BridgeSTP, &out.BridgeSTP *out = new(STP) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bridge. func (in *Bridge) DeepCopy() *Bridge { if in == nil { return nil } out := new(Bridge) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CNIConfig) DeepCopyInto(out *CNIConfig) { *out = *in if in.CNIUrls != nil { in, out := &in.CNIUrls, &out.CNIUrls *out = make([]string, len(*in)) copy(*out, *in) } if in.CNIFlannel != nil { in, out := &in.CNIFlannel, &out.CNIFlannel *out = new(FlannelCNIConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CNIConfig. func (in *CNIConfig) DeepCopy() *CNIConfig { if in == nil { return nil } out := new(CNIConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterConfig) DeepCopyInto(out *ClusterConfig) { *out = *in if in.ControlPlane != nil { in, out := &in.ControlPlane, &out.ControlPlane *out = new(ControlPlaneConfig) (*in).DeepCopyInto(*out) } if in.ClusterNetwork != nil { in, out := &in.ClusterNetwork, &out.ClusterNetwork *out = new(ClusterNetworkConfig) (*in).DeepCopyInto(*out) } if in.ClusterCA != nil { in, out := &in.ClusterCA, &out.ClusterCA *out = (*in).DeepCopy() } if in.ClusterAcceptedCAs != nil { in, out := &in.ClusterAcceptedCAs, &out.ClusterAcceptedCAs *out = make([]*x509.PEMEncodedCertificate, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = (*in).DeepCopy() } } } if in.ClusterAggregatorCA != nil { in, out := &in.ClusterAggregatorCA, &out.ClusterAggregatorCA *out = (*in).DeepCopy() } if in.ClusterServiceAccount != nil { in, out := &in.ClusterServiceAccount, &out.ClusterServiceAccount *out = (*in).DeepCopy() } if in.APIServerConfig != nil { in, out := &in.APIServerConfig, &out.APIServerConfig *out = new(APIServerConfig) (*in).DeepCopyInto(*out) } if in.ControllerManagerConfig != nil { in, out := &in.ControllerManagerConfig, &out.ControllerManagerConfig *out = new(ControllerManagerConfig) (*in).DeepCopyInto(*out) } if in.ProxyConfig != nil { in, out := &in.ProxyConfig, &out.ProxyConfig *out = new(ProxyConfig) (*in).DeepCopyInto(*out) } if in.SchedulerConfig != nil { in, out := &in.SchedulerConfig, &out.SchedulerConfig *out = new(SchedulerConfig) (*in).DeepCopyInto(*out) } if in.ClusterDiscoveryConfig != nil { in, out := &in.ClusterDiscoveryConfig, &out.ClusterDiscoveryConfig *out = new(ClusterDiscoveryConfig) (*in).DeepCopyInto(*out) } if in.EtcdConfig != nil { in, out := &in.EtcdConfig, &out.EtcdConfig *out = new(EtcdConfig) (*in).DeepCopyInto(*out) } if in.CoreDNSConfig != nil { in, out := &in.CoreDNSConfig, &out.CoreDNSConfig *out = new(CoreDNS) (*in).DeepCopyInto(*out) } if in.ExternalCloudProviderConfig != nil { in, out := &in.ExternalCloudProviderConfig, &out.ExternalCloudProviderConfig *out = new(ExternalCloudProviderConfig) (*in).DeepCopyInto(*out) } if in.ExtraManifests != nil { in, out := &in.ExtraManifests, &out.ExtraManifests *out = make([]string, len(*in)) copy(*out, *in) } if in.ExtraManifestHeaders != nil { in, out := &in.ExtraManifestHeaders, &out.ExtraManifestHeaders *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ClusterInlineManifests != nil { in, out := &in.ClusterInlineManifests, &out.ClusterInlineManifests *out = make(ClusterInlineManifests, len(*in)) copy(*out, *in) } if in.AdminKubeconfigConfig != nil { in, out := &in.AdminKubeconfigConfig, &out.AdminKubeconfigConfig *out = new(AdminKubeconfigConfig) **out = **in } if in.AllowSchedulingOnMasters != nil { in, out := &in.AllowSchedulingOnMasters, &out.AllowSchedulingOnMasters *out = new(bool) **out = **in } if in.AllowSchedulingOnControlPlanes != nil { in, out := &in.AllowSchedulingOnControlPlanes, &out.AllowSchedulingOnControlPlanes *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterConfig. func (in *ClusterConfig) DeepCopy() *ClusterConfig { if in == nil { return nil } out := new(ClusterConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterDiscoveryConfig) DeepCopyInto(out *ClusterDiscoveryConfig) { *out = *in if in.DiscoveryEnabled != nil { in, out := &in.DiscoveryEnabled, &out.DiscoveryEnabled *out = new(bool) **out = **in } in.DiscoveryRegistries.DeepCopyInto(&out.DiscoveryRegistries) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterDiscoveryConfig. func (in *ClusterDiscoveryConfig) DeepCopy() *ClusterDiscoveryConfig { if in == nil { return nil } out := new(ClusterDiscoveryConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterInlineManifest) DeepCopyInto(out *ClusterInlineManifest) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInlineManifest. func (in *ClusterInlineManifest) DeepCopy() *ClusterInlineManifest { if in == nil { return nil } out := new(ClusterInlineManifest) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in ClusterInlineManifests) DeepCopyInto(out *ClusterInlineManifests) { { in := &in *out = make(ClusterInlineManifests, len(*in)) copy(*out, *in) return } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterInlineManifests. func (in ClusterInlineManifests) DeepCopy() ClusterInlineManifests { if in == nil { return nil } out := new(ClusterInlineManifests) in.DeepCopyInto(out) return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterNetworkConfig) DeepCopyInto(out *ClusterNetworkConfig) { *out = *in if in.CNI != nil { in, out := &in.CNI, &out.CNI *out = new(CNIConfig) (*in).DeepCopyInto(*out) } if in.PodSubnet != nil { in, out := &in.PodSubnet, &out.PodSubnet *out = make([]string, len(*in)) copy(*out, *in) } if in.ServiceSubnet != nil { in, out := &in.ServiceSubnet, &out.ServiceSubnet *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterNetworkConfig. func (in *ClusterNetworkConfig) DeepCopy() *ClusterNetworkConfig { if in == nil { return nil } out := new(ClusterNetworkConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (c *Config) DeepCopyInto(out *Config) { *out = *c if c.ConfigDebug != nil { in, out := &c.ConfigDebug, &out.ConfigDebug *out = new(bool) **out = **in } if c.ConfigPersist != nil { in, out := &c.ConfigPersist, &out.ConfigPersist *out = new(bool) **out = **in } if c.MachineConfig != nil { in, out := &c.MachineConfig, &out.MachineConfig *out = new(MachineConfig) (*in).DeepCopyInto(*out) } if c.ClusterConfig != nil { in, out := &c.ClusterConfig, &out.ClusterConfig *out = new(ClusterConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. func (c *Config) DeepCopy() *Config { if c == nil { return nil } out := new(Config) c.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ControlPlaneConfig) DeepCopyInto(out *ControlPlaneConfig) { *out = *in if in.Endpoint != nil { in, out := &in.Endpoint, &out.Endpoint *out = (*in).DeepCopy() } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControlPlaneConfig. func (in *ControlPlaneConfig) DeepCopy() *ControlPlaneConfig { if in == nil { return nil } out := new(ControlPlaneConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ControllerManagerConfig) DeepCopyInto(out *ControllerManagerConfig) { *out = *in if in.ExtraArgsConfig != nil { in, out := &in.ExtraArgsConfig, &out.ExtraArgsConfig *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ExtraVolumesConfig != nil { in, out := &in.ExtraVolumesConfig, &out.ExtraVolumesConfig *out = make([]VolumeMountConfig, len(*in)) copy(*out, *in) } if in.EnvConfig != nil { in, out := &in.EnvConfig, &out.EnvConfig *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ResourcesConfig != nil { in, out := &in.ResourcesConfig, &out.ResourcesConfig *out = new(ResourcesConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ControllerManagerConfig. func (in *ControllerManagerConfig) DeepCopy() *ControllerManagerConfig { if in == nil { return nil } out := new(ControllerManagerConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CoreDNS) DeepCopyInto(out *CoreDNS) { *out = *in if in.CoreDNSDisabled != nil { in, out := &in.CoreDNSDisabled, &out.CoreDNSDisabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CoreDNS. func (in *CoreDNS) DeepCopy() *CoreDNS { if in == nil { return nil } out := new(CoreDNS) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DHCPOptions) DeepCopyInto(out *DHCPOptions) { *out = *in if in.DHCPIPv4 != nil { in, out := &in.DHCPIPv4, &out.DHCPIPv4 *out = new(bool) **out = **in } if in.DHCPIPv6 != nil { in, out := &in.DHCPIPv6, &out.DHCPIPv6 *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DHCPOptions. func (in *DHCPOptions) DeepCopy() *DHCPOptions { if in == nil { return nil } out := new(DHCPOptions) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Device) DeepCopyInto(out *Device) { *out = *in if in.DeviceSelector != nil { in, out := &in.DeviceSelector, &out.DeviceSelector *out = new(NetworkDeviceSelector) (*in).DeepCopyInto(*out) } if in.DeviceAddresses != nil { in, out := &in.DeviceAddresses, &out.DeviceAddresses *out = make([]string, len(*in)) copy(*out, *in) } if in.DeviceRoutes != nil { in, out := &in.DeviceRoutes, &out.DeviceRoutes *out = make([]*Route, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Route) **out = **in } } } if in.DeviceBond != nil { in, out := &in.DeviceBond, &out.DeviceBond *out = new(Bond) (*in).DeepCopyInto(*out) } if in.DeviceBridge != nil { in, out := &in.DeviceBridge, &out.DeviceBridge *out = new(Bridge) (*in).DeepCopyInto(*out) } if in.DeviceVlans != nil { in, out := &in.DeviceVlans, &out.DeviceVlans *out = make(VlanList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Vlan) (*in).DeepCopyInto(*out) } } } if in.DeviceDHCP != nil { in, out := &in.DeviceDHCP, &out.DeviceDHCP *out = new(bool) **out = **in } if in.DeviceIgnore != nil { in, out := &in.DeviceIgnore, &out.DeviceIgnore *out = new(bool) **out = **in } if in.DeviceDummy != nil { in, out := &in.DeviceDummy, &out.DeviceDummy *out = new(bool) **out = **in } if in.DeviceDHCPOptions != nil { in, out := &in.DeviceDHCPOptions, &out.DeviceDHCPOptions *out = new(DHCPOptions) (*in).DeepCopyInto(*out) } if in.DeviceWireguardConfig != nil { in, out := &in.DeviceWireguardConfig, &out.DeviceWireguardConfig *out = new(DeviceWireguardConfig) (*in).DeepCopyInto(*out) } if in.DeviceVIPConfig != nil { in, out := &in.DeviceVIPConfig, &out.DeviceVIPConfig *out = new(DeviceVIPConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Device. func (in *Device) DeepCopy() *Device { if in == nil { return nil } out := new(Device) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeviceVIPConfig) DeepCopyInto(out *DeviceVIPConfig) { *out = *in if in.EquinixMetalConfig != nil { in, out := &in.EquinixMetalConfig, &out.EquinixMetalConfig *out = new(VIPEquinixMetalConfig) **out = **in } if in.HCloudConfig != nil { in, out := &in.HCloudConfig, &out.HCloudConfig *out = new(VIPHCloudConfig) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceVIPConfig. func (in *DeviceVIPConfig) DeepCopy() *DeviceVIPConfig { if in == nil { return nil } out := new(DeviceVIPConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeviceWireguardConfig) DeepCopyInto(out *DeviceWireguardConfig) { *out = *in if in.WireguardPeers != nil { in, out := &in.WireguardPeers, &out.WireguardPeers *out = make([]*DeviceWireguardPeer, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(DeviceWireguardPeer) (*in).DeepCopyInto(*out) } } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceWireguardConfig. func (in *DeviceWireguardConfig) DeepCopy() *DeviceWireguardConfig { if in == nil { return nil } out := new(DeviceWireguardConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeviceWireguardPeer) DeepCopyInto(out *DeviceWireguardPeer) { *out = *in if in.WireguardAllowedIPs != nil { in, out := &in.WireguardAllowedIPs, &out.WireguardAllowedIPs *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceWireguardPeer. func (in *DeviceWireguardPeer) DeepCopy() *DeviceWireguardPeer { if in == nil { return nil } out := new(DeviceWireguardPeer) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiscoveryRegistriesConfig) DeepCopyInto(out *DiscoveryRegistriesConfig) { *out = *in in.RegistryKubernetes.DeepCopyInto(&out.RegistryKubernetes) in.RegistryService.DeepCopyInto(&out.RegistryService) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiscoveryRegistriesConfig. func (in *DiscoveryRegistriesConfig) DeepCopy() *DiscoveryRegistriesConfig { if in == nil { return nil } out := new(DiscoveryRegistriesConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DiskPartition) DeepCopyInto(out *DiskPartition) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DiskPartition. func (in *DiskPartition) DeepCopy() *DiskPartition { if in == nil { return nil } out := new(DiskPartition) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionConfig) DeepCopyInto(out *EncryptionConfig) { *out = *in if in.EncryptionKeys != nil { in, out := &in.EncryptionKeys, &out.EncryptionKeys *out = make([]*EncryptionKey, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(EncryptionKey) (*in).DeepCopyInto(*out) } } } if in.EncryptionPerfOptions != nil { in, out := &in.EncryptionPerfOptions, &out.EncryptionPerfOptions *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionConfig. func (in *EncryptionConfig) DeepCopy() *EncryptionConfig { if in == nil { return nil } out := new(EncryptionConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionKey) DeepCopyInto(out *EncryptionKey) { *out = *in if in.KeyStatic != nil { in, out := &in.KeyStatic, &out.KeyStatic *out = new(EncryptionKeyStatic) **out = **in } if in.KeyNodeID != nil { in, out := &in.KeyNodeID, &out.KeyNodeID *out = new(EncryptionKeyNodeID) **out = **in } if in.KeyKMS != nil { in, out := &in.KeyKMS, &out.KeyKMS *out = new(EncryptionKeyKMS) **out = **in } if in.KeyTPM != nil { in, out := &in.KeyTPM, &out.KeyTPM *out = new(EncryptionKeyTPM) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKey. func (in *EncryptionKey) DeepCopy() *EncryptionKey { if in == nil { return nil } out := new(EncryptionKey) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionKeyKMS) DeepCopyInto(out *EncryptionKeyKMS) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKeyKMS. func (in *EncryptionKeyKMS) DeepCopy() *EncryptionKeyKMS { if in == nil { return nil } out := new(EncryptionKeyKMS) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionKeyNodeID) DeepCopyInto(out *EncryptionKeyNodeID) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKeyNodeID. func (in *EncryptionKeyNodeID) DeepCopy() *EncryptionKeyNodeID { if in == nil { return nil } out := new(EncryptionKeyNodeID) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionKeyStatic) DeepCopyInto(out *EncryptionKeyStatic) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKeyStatic. func (in *EncryptionKeyStatic) DeepCopy() *EncryptionKeyStatic { if in == nil { return nil } out := new(EncryptionKeyStatic) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EncryptionKeyTPM) DeepCopyInto(out *EncryptionKeyTPM) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncryptionKeyTPM. func (in *EncryptionKeyTPM) DeepCopy() *EncryptionKeyTPM { if in == nil { return nil } out := new(EncryptionKeyTPM) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EtcdConfig) DeepCopyInto(out *EtcdConfig) { *out = *in if in.RootCA != nil { in, out := &in.RootCA, &out.RootCA *out = (*in).DeepCopy() } if in.EtcdExtraArgs != nil { in, out := &in.EtcdExtraArgs, &out.EtcdExtraArgs *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.EtcdAdvertisedSubnets != nil { in, out := &in.EtcdAdvertisedSubnets, &out.EtcdAdvertisedSubnets *out = make([]string, len(*in)) copy(*out, *in) } if in.EtcdListenSubnets != nil { in, out := &in.EtcdListenSubnets, &out.EtcdListenSubnets *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdConfig. func (in *EtcdConfig) DeepCopy() *EtcdConfig { if in == nil { return nil } out := new(EtcdConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalCloudProviderConfig) DeepCopyInto(out *ExternalCloudProviderConfig) { *out = *in if in.ExternalEnabled != nil { in, out := &in.ExternalEnabled, &out.ExternalEnabled *out = new(bool) **out = **in } if in.ExternalManifests != nil { in, out := &in.ExternalManifests, &out.ExternalManifests *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalCloudProviderConfig. func (in *ExternalCloudProviderConfig) DeepCopy() *ExternalCloudProviderConfig { if in == nil { return nil } out := new(ExternalCloudProviderConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtraHost) DeepCopyInto(out *ExtraHost) { *out = *in if in.HostAliases != nil { in, out := &in.HostAliases, &out.HostAliases *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraHost. func (in *ExtraHost) DeepCopy() *ExtraHost { if in == nil { return nil } out := new(ExtraHost) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExtraMount) DeepCopyInto(out *ExtraMount) { *out = *in if in.Options != nil { in, out := &in.Options, &out.Options *out = make([]string, len(*in)) copy(*out, *in) } if in.UIDMappings != nil { in, out := &in.UIDMappings, &out.UIDMappings *out = make([]LinuxIDMapping, len(*in)) copy(*out, *in) } if in.GIDMappings != nil { in, out := &in.GIDMappings, &out.GIDMappings *out = make([]LinuxIDMapping, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtraMount. func (in *ExtraMount) DeepCopy() *ExtraMount { if in == nil { return nil } out := new(ExtraMount) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FeaturesConfig) DeepCopyInto(out *FeaturesConfig) { *out = *in if in.RBAC != nil { in, out := &in.RBAC, &out.RBAC *out = new(bool) **out = **in } if in.StableHostname != nil { in, out := &in.StableHostname, &out.StableHostname *out = new(bool) **out = **in } if in.KubernetesTalosAPIAccessConfig != nil { in, out := &in.KubernetesTalosAPIAccessConfig, &out.KubernetesTalosAPIAccessConfig *out = new(KubernetesTalosAPIAccessConfig) (*in).DeepCopyInto(*out) } if in.ApidCheckExtKeyUsage != nil { in, out := &in.ApidCheckExtKeyUsage, &out.ApidCheckExtKeyUsage *out = new(bool) **out = **in } if in.DiskQuotaSupport != nil { in, out := &in.DiskQuotaSupport, &out.DiskQuotaSupport *out = new(bool) **out = **in } if in.KubePrismSupport != nil { in, out := &in.KubePrismSupport, &out.KubePrismSupport *out = new(KubePrism) (*in).DeepCopyInto(*out) } if in.HostDNSSupport != nil { in, out := &in.HostDNSSupport, &out.HostDNSSupport *out = new(HostDNSConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeaturesConfig. func (in *FeaturesConfig) DeepCopy() *FeaturesConfig { if in == nil { return nil } out := new(FeaturesConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FlannelCNIConfig) DeepCopyInto(out *FlannelCNIConfig) { *out = *in if in.FlanneldExtraArgs != nil { in, out := &in.FlanneldExtraArgs, &out.FlanneldExtraArgs *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlannelCNIConfig. func (in *FlannelCNIConfig) DeepCopy() *FlannelCNIConfig { if in == nil { return nil } out := new(FlannelCNIConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HostDNSConfig) DeepCopyInto(out *HostDNSConfig) { *out = *in if in.HostDNSEnabled != nil { in, out := &in.HostDNSEnabled, &out.HostDNSEnabled *out = new(bool) **out = **in } if in.HostDNSForwardKubeDNSToHost != nil { in, out := &in.HostDNSForwardKubeDNSToHost, &out.HostDNSForwardKubeDNSToHost *out = new(bool) **out = **in } if in.HostDNSResolveMemberNames != nil { in, out := &in.HostDNSResolveMemberNames, &out.HostDNSResolveMemberNames *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HostDNSConfig. func (in *HostDNSConfig) DeepCopy() *HostDNSConfig { if in == nil { return nil } out := new(HostDNSConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IfaceSelector) DeepCopyInto(out *IfaceSelector) { *out = *in if in.Name != nil { in, out := &in.Name, &out.Name *out = new(string) **out = **in } if in.Selector != nil { in, out := &in.Selector, &out.Selector *out = new(NetworkDeviceSelector) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IfaceSelector. func (in *IfaceSelector) DeepCopy() *IfaceSelector { if in == nil { return nil } out := new(IfaceSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallConfig) DeepCopyInto(out *InstallConfig) { *out = *in if in.InstallDiskSelector != nil { in, out := &in.InstallDiskSelector, &out.InstallDiskSelector *out = new(InstallDiskSelector) (*in).DeepCopyInto(*out) } if in.InstallExtraKernelArgs != nil { in, out := &in.InstallExtraKernelArgs, &out.InstallExtraKernelArgs *out = make([]string, len(*in)) copy(*out, *in) } if in.InstallExtensions != nil { in, out := &in.InstallExtensions, &out.InstallExtensions *out = make([]InstallExtensionConfig, len(*in)) copy(*out, *in) } if in.InstallBootloader != nil { in, out := &in.InstallBootloader, &out.InstallBootloader *out = new(bool) **out = **in } if in.InstallWipe != nil { in, out := &in.InstallWipe, &out.InstallWipe *out = new(bool) **out = **in } if in.InstallLegacyBIOSSupport != nil { in, out := &in.InstallLegacyBIOSSupport, &out.InstallLegacyBIOSSupport *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallConfig. func (in *InstallConfig) DeepCopy() *InstallConfig { if in == nil { return nil } out := new(InstallConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallDiskSelector) DeepCopyInto(out *InstallDiskSelector) { *out = *in if in.Size != nil { in, out := &in.Size, &out.Size *out = new(InstallDiskSizeMatcher) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallDiskSelector. func (in *InstallDiskSelector) DeepCopy() *InstallDiskSelector { if in == nil { return nil } out := new(InstallDiskSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallDiskSizeMatchData) DeepCopyInto(out *InstallDiskSizeMatchData) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallDiskSizeMatchData. func (in *InstallDiskSizeMatchData) DeepCopy() *InstallDiskSizeMatchData { if in == nil { return nil } out := new(InstallDiskSizeMatchData) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallDiskSizeMatcher) DeepCopyInto(out *InstallDiskSizeMatcher) { *out = *in out.MatchData = in.MatchData return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallDiskSizeMatcher. func (in *InstallDiskSizeMatcher) DeepCopy() *InstallDiskSizeMatcher { if in == nil { return nil } out := new(InstallDiskSizeMatcher) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstallExtensionConfig) DeepCopyInto(out *InstallExtensionConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstallExtensionConfig. func (in *InstallExtensionConfig) DeepCopy() *InstallExtensionConfig { if in == nil { return nil } out := new(InstallExtensionConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KernelConfig) DeepCopyInto(out *KernelConfig) { *out = *in if in.KernelModules != nil { in, out := &in.KernelModules, &out.KernelModules *out = make([]*KernelModuleConfig, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(KernelModuleConfig) (*in).DeepCopyInto(*out) } } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KernelConfig. func (in *KernelConfig) DeepCopy() *KernelConfig { if in == nil { return nil } out := new(KernelConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KernelModuleConfig) DeepCopyInto(out *KernelModuleConfig) { *out = *in if in.ModuleParameters != nil { in, out := &in.ModuleParameters, &out.ModuleParameters *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KernelModuleConfig. func (in *KernelModuleConfig) DeepCopy() *KernelModuleConfig { if in == nil { return nil } out := new(KernelModuleConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubePrism) DeepCopyInto(out *KubePrism) { *out = *in if in.ServerEnabled != nil { in, out := &in.ServerEnabled, &out.ServerEnabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubePrism. func (in *KubePrism) DeepCopy() *KubePrism { if in == nil { return nil } out := new(KubePrism) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeSpanFilters) DeepCopyInto(out *KubeSpanFilters) { *out = *in if in.KubeSpanFiltersEndpoints != nil { in, out := &in.KubeSpanFiltersEndpoints, &out.KubeSpanFiltersEndpoints *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeSpanFilters. func (in *KubeSpanFilters) DeepCopy() *KubeSpanFilters { if in == nil { return nil } out := new(KubeSpanFilters) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeletConfig) DeepCopyInto(out *KubeletConfig) { *out = *in if in.KubeletClusterDNS != nil { in, out := &in.KubeletClusterDNS, &out.KubeletClusterDNS *out = make([]string, len(*in)) copy(*out, *in) } if in.KubeletExtraArgs != nil { in, out := &in.KubeletExtraArgs, &out.KubeletExtraArgs *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.KubeletExtraMounts != nil { in, out := &in.KubeletExtraMounts, &out.KubeletExtraMounts *out = make([]ExtraMount, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } in.KubeletExtraConfig.DeepCopyInto(&out.KubeletExtraConfig) in.KubeletCredentialProviderConfig.DeepCopyInto(&out.KubeletCredentialProviderConfig) if in.KubeletDefaultRuntimeSeccompProfileEnabled != nil { in, out := &in.KubeletDefaultRuntimeSeccompProfileEnabled, &out.KubeletDefaultRuntimeSeccompProfileEnabled *out = new(bool) **out = **in } if in.KubeletRegisterWithFQDN != nil { in, out := &in.KubeletRegisterWithFQDN, &out.KubeletRegisterWithFQDN *out = new(bool) **out = **in } if in.KubeletNodeIP != nil { in, out := &in.KubeletNodeIP, &out.KubeletNodeIP *out = new(KubeletNodeIPConfig) (*in).DeepCopyInto(*out) } if in.KubeletSkipNodeRegistration != nil { in, out := &in.KubeletSkipNodeRegistration, &out.KubeletSkipNodeRegistration *out = new(bool) **out = **in } if in.KubeletDisableManifestsDirectory != nil { in, out := &in.KubeletDisableManifestsDirectory, &out.KubeletDisableManifestsDirectory *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeletConfig. func (in *KubeletConfig) DeepCopy() *KubeletConfig { if in == nil { return nil } out := new(KubeletConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubeletNodeIPConfig) DeepCopyInto(out *KubeletNodeIPConfig) { *out = *in if in.KubeletNodeIPValidSubnets != nil { in, out := &in.KubeletNodeIPValidSubnets, &out.KubeletNodeIPValidSubnets *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeletNodeIPConfig. func (in *KubeletNodeIPConfig) DeepCopy() *KubeletNodeIPConfig { if in == nil { return nil } out := new(KubeletNodeIPConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubernetesTalosAPIAccessConfig) DeepCopyInto(out *KubernetesTalosAPIAccessConfig) { *out = *in if in.AccessEnabled != nil { in, out := &in.AccessEnabled, &out.AccessEnabled *out = new(bool) **out = **in } if in.AccessAllowedRoles != nil { in, out := &in.AccessAllowedRoles, &out.AccessAllowedRoles *out = make([]string, len(*in)) copy(*out, *in) } if in.AccessAllowedKubernetesNamespaces != nil { in, out := &in.AccessAllowedKubernetesNamespaces, &out.AccessAllowedKubernetesNamespaces *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesTalosAPIAccessConfig. func (in *KubernetesTalosAPIAccessConfig) DeepCopy() *KubernetesTalosAPIAccessConfig { if in == nil { return nil } out := new(KubernetesTalosAPIAccessConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LinuxIDMapping) DeepCopyInto(out *LinuxIDMapping) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LinuxIDMapping. func (in *LinuxIDMapping) DeepCopy() *LinuxIDMapping { if in == nil { return nil } out := new(LinuxIDMapping) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LoggingConfig) DeepCopyInto(out *LoggingConfig) { *out = *in if in.LoggingDestinations != nil { in, out := &in.LoggingDestinations, &out.LoggingDestinations *out = make([]LoggingDestination, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingConfig. func (in *LoggingConfig) DeepCopy() *LoggingConfig { if in == nil { return nil } out := new(LoggingConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *LoggingDestination) DeepCopyInto(out *LoggingDestination) { *out = *in if in.LoggingEndpoint != nil { in, out := &in.LoggingEndpoint, &out.LoggingEndpoint *out = (*in).DeepCopy() } if in.LoggingExtraTags != nil { in, out := &in.LoggingExtraTags, &out.LoggingExtraTags *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoggingDestination. func (in *LoggingDestination) DeepCopy() *LoggingDestination { if in == nil { return nil } out := new(LoggingDestination) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineConfig) DeepCopyInto(out *MachineConfig) { *out = *in if in.MachineCA != nil { in, out := &in.MachineCA, &out.MachineCA *out = (*in).DeepCopy() } if in.MachineAcceptedCAs != nil { in, out := &in.MachineAcceptedCAs, &out.MachineAcceptedCAs *out = make([]*x509.PEMEncodedCertificate, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = (*in).DeepCopy() } } } if in.MachineCertSANs != nil { in, out := &in.MachineCertSANs, &out.MachineCertSANs *out = make([]string, len(*in)) copy(*out, *in) } if in.MachineControlPlane != nil { in, out := &in.MachineControlPlane, &out.MachineControlPlane *out = new(MachineControlPlaneConfig) (*in).DeepCopyInto(*out) } if in.MachineKubelet != nil { in, out := &in.MachineKubelet, &out.MachineKubelet *out = new(KubeletConfig) (*in).DeepCopyInto(*out) } if in.MachinePods != nil { in, out := &in.MachinePods, &out.MachinePods *out = make([]Unstructured, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.MachineNetwork != nil { in, out := &in.MachineNetwork, &out.MachineNetwork *out = new(NetworkConfig) (*in).DeepCopyInto(*out) } if in.MachineDisks != nil { in, out := &in.MachineDisks, &out.MachineDisks *out = make([]*MachineDisk, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(MachineDisk) (*in).DeepCopyInto(*out) } } } if in.MachineInstall != nil { in, out := &in.MachineInstall, &out.MachineInstall *out = new(InstallConfig) (*in).DeepCopyInto(*out) } if in.MachineFiles != nil { in, out := &in.MachineFiles, &out.MachineFiles *out = make([]*MachineFile, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(MachineFile) **out = **in } } } if in.MachineEnv != nil { in, out := &in.MachineEnv, &out.MachineEnv *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.MachineTime != nil { in, out := &in.MachineTime, &out.MachineTime *out = new(TimeConfig) (*in).DeepCopyInto(*out) } if in.MachineSysctls != nil { in, out := &in.MachineSysctls, &out.MachineSysctls *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.MachineSysfs != nil { in, out := &in.MachineSysfs, &out.MachineSysfs *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } in.MachineRegistries.DeepCopyInto(&out.MachineRegistries) if in.MachineSystemDiskEncryption != nil { in, out := &in.MachineSystemDiskEncryption, &out.MachineSystemDiskEncryption *out = new(SystemDiskEncryptionConfig) (*in).DeepCopyInto(*out) } if in.MachineFeatures != nil { in, out := &in.MachineFeatures, &out.MachineFeatures *out = new(FeaturesConfig) (*in).DeepCopyInto(*out) } if in.MachineUdev != nil { in, out := &in.MachineUdev, &out.MachineUdev *out = new(UdevConfig) (*in).DeepCopyInto(*out) } if in.MachineLogging != nil { in, out := &in.MachineLogging, &out.MachineLogging *out = new(LoggingConfig) (*in).DeepCopyInto(*out) } if in.MachineKernel != nil { in, out := &in.MachineKernel, &out.MachineKernel *out = new(KernelConfig) (*in).DeepCopyInto(*out) } if in.MachineSeccompProfiles != nil { in, out := &in.MachineSeccompProfiles, &out.MachineSeccompProfiles *out = make([]*MachineSeccompProfile, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(MachineSeccompProfile) (*in).DeepCopyInto(*out) } } } if in.MachineNodeLabels != nil { in, out := &in.MachineNodeLabels, &out.MachineNodeLabels *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.MachineNodeTaints != nil { in, out := &in.MachineNodeTaints, &out.MachineNodeTaints *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineConfig. func (in *MachineConfig) DeepCopy() *MachineConfig { if in == nil { return nil } out := new(MachineConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineControlPlaneConfig) DeepCopyInto(out *MachineControlPlaneConfig) { *out = *in if in.MachineControllerManager != nil { in, out := &in.MachineControllerManager, &out.MachineControllerManager *out = new(MachineControllerManagerConfig) (*in).DeepCopyInto(*out) } if in.MachineScheduler != nil { in, out := &in.MachineScheduler, &out.MachineScheduler *out = new(MachineSchedulerConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineControlPlaneConfig. func (in *MachineControlPlaneConfig) DeepCopy() *MachineControlPlaneConfig { if in == nil { return nil } out := new(MachineControlPlaneConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineControllerManagerConfig) DeepCopyInto(out *MachineControllerManagerConfig) { *out = *in if in.MachineControllerManagerDisabled != nil { in, out := &in.MachineControllerManagerDisabled, &out.MachineControllerManagerDisabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineControllerManagerConfig. func (in *MachineControllerManagerConfig) DeepCopy() *MachineControllerManagerConfig { if in == nil { return nil } out := new(MachineControllerManagerConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineDisk) DeepCopyInto(out *MachineDisk) { *out = *in if in.DiskPartitions != nil { in, out := &in.DiskPartitions, &out.DiskPartitions *out = make([]*DiskPartition, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(DiskPartition) **out = **in } } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineDisk. func (in *MachineDisk) DeepCopy() *MachineDisk { if in == nil { return nil } out := new(MachineDisk) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineFile) DeepCopyInto(out *MachineFile) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineFile. func (in *MachineFile) DeepCopy() *MachineFile { if in == nil { return nil } out := new(MachineFile) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineSchedulerConfig) DeepCopyInto(out *MachineSchedulerConfig) { *out = *in if in.MachineSchedulerDisabled != nil { in, out := &in.MachineSchedulerDisabled, &out.MachineSchedulerDisabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineSchedulerConfig. func (in *MachineSchedulerConfig) DeepCopy() *MachineSchedulerConfig { if in == nil { return nil } out := new(MachineSchedulerConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MachineSeccompProfile) DeepCopyInto(out *MachineSeccompProfile) { *out = *in in.MachineSeccompProfileValue.DeepCopyInto(&out.MachineSeccompProfileValue) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineSeccompProfile. func (in *MachineSeccompProfile) DeepCopy() *MachineSeccompProfile { if in == nil { return nil } out := new(MachineSeccompProfile) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkConfig) DeepCopyInto(out *NetworkConfig) { *out = *in if in.NetworkInterfaces != nil { in, out := &in.NetworkInterfaces, &out.NetworkInterfaces *out = make(NetworkDeviceList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Device) (*in).DeepCopyInto(*out) } } } if in.NameServers != nil { in, out := &in.NameServers, &out.NameServers *out = make([]string, len(*in)) copy(*out, *in) } if in.ExtraHostEntries != nil { in, out := &in.ExtraHostEntries, &out.ExtraHostEntries *out = make([]*ExtraHost, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(ExtraHost) (*in).DeepCopyInto(*out) } } } if in.NetworkKubeSpan != nil { in, out := &in.NetworkKubeSpan, &out.NetworkKubeSpan *out = new(NetworkKubeSpan) (*in).DeepCopyInto(*out) } if in.NetworkDisableSearchDomain != nil { in, out := &in.NetworkDisableSearchDomain, &out.NetworkDisableSearchDomain *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkConfig. func (in *NetworkConfig) DeepCopy() *NetworkConfig { if in == nil { return nil } out := new(NetworkConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in NetworkDeviceList) DeepCopyInto(out *NetworkDeviceList) { { in := &in *out = make(NetworkDeviceList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Device) (*in).DeepCopyInto(*out) } } return } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDeviceList. func (in NetworkDeviceList) DeepCopy() NetworkDeviceList { if in == nil { return nil } out := new(NetworkDeviceList) in.DeepCopyInto(out) return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkDeviceSelector) DeepCopyInto(out *NetworkDeviceSelector) { *out = *in if in.NetworkDevicePhysical != nil { in, out := &in.NetworkDevicePhysical, &out.NetworkDevicePhysical *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDeviceSelector. func (in *NetworkDeviceSelector) DeepCopy() *NetworkDeviceSelector { if in == nil { return nil } out := new(NetworkDeviceSelector) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkKubeSpan) DeepCopyInto(out *NetworkKubeSpan) { *out = *in if in.KubeSpanEnabled != nil { in, out := &in.KubeSpanEnabled, &out.KubeSpanEnabled *out = new(bool) **out = **in } if in.KubeSpanAdvertiseKubernetesNetworks != nil { in, out := &in.KubeSpanAdvertiseKubernetesNetworks, &out.KubeSpanAdvertiseKubernetesNetworks *out = new(bool) **out = **in } if in.KubeSpanAllowDownPeerBypass != nil { in, out := &in.KubeSpanAllowDownPeerBypass, &out.KubeSpanAllowDownPeerBypass *out = new(bool) **out = **in } if in.KubeSpanHarvestExtraEndpoints != nil { in, out := &in.KubeSpanHarvestExtraEndpoints, &out.KubeSpanHarvestExtraEndpoints *out = new(bool) **out = **in } if in.KubeSpanMTU != nil { in, out := &in.KubeSpanMTU, &out.KubeSpanMTU *out = new(uint32) **out = **in } if in.KubeSpanFilters != nil { in, out := &in.KubeSpanFilters, &out.KubeSpanFilters *out = new(KubeSpanFilters) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkKubeSpan. func (in *NetworkKubeSpan) DeepCopy() *NetworkKubeSpan { if in == nil { return nil } out := new(NetworkKubeSpan) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodCheckpointer) DeepCopyInto(out *PodCheckpointer) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodCheckpointer. func (in *PodCheckpointer) DeepCopy() *PodCheckpointer { if in == nil { return nil } out := new(PodCheckpointer) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProxyConfig) DeepCopyInto(out *ProxyConfig) { *out = *in if in.Disabled != nil { in, out := &in.Disabled, &out.Disabled *out = new(bool) **out = **in } if in.ExtraArgsConfig != nil { in, out := &in.ExtraArgsConfig, &out.ExtraArgsConfig *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxyConfig. func (in *ProxyConfig) DeepCopy() *ProxyConfig { if in == nil { return nil } out := new(ProxyConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistriesConfig) DeepCopyInto(out *RegistriesConfig) { *out = *in if in.RegistryMirrors != nil { in, out := &in.RegistryMirrors, &out.RegistryMirrors *out = make(map[string]*RegistryMirrorConfig, len(*in)) for key, val := range *in { var outVal *RegistryMirrorConfig if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal *out = new(RegistryMirrorConfig) (*in).DeepCopyInto(*out) } (*out)[key] = outVal } } if in.RegistryConfig != nil { in, out := &in.RegistryConfig, &out.RegistryConfig *out = make(map[string]*RegistryConfig, len(*in)) for key, val := range *in { var outVal *RegistryConfig if val == nil { (*out)[key] = nil } else { in, out := &val, &outVal *out = new(RegistryConfig) (*in).DeepCopyInto(*out) } (*out)[key] = outVal } } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistriesConfig. func (in *RegistriesConfig) DeepCopy() *RegistriesConfig { if in == nil { return nil } out := new(RegistriesConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryAuthConfig) DeepCopyInto(out *RegistryAuthConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryAuthConfig. func (in *RegistryAuthConfig) DeepCopy() *RegistryAuthConfig { if in == nil { return nil } out := new(RegistryAuthConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryConfig) DeepCopyInto(out *RegistryConfig) { *out = *in if in.RegistryTLS != nil { in, out := &in.RegistryTLS, &out.RegistryTLS *out = new(RegistryTLSConfig) (*in).DeepCopyInto(*out) } if in.RegistryAuth != nil { in, out := &in.RegistryAuth, &out.RegistryAuth *out = new(RegistryAuthConfig) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryConfig. func (in *RegistryConfig) DeepCopy() *RegistryConfig { if in == nil { return nil } out := new(RegistryConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryKubernetesConfig) DeepCopyInto(out *RegistryKubernetesConfig) { *out = *in if in.RegistryDisabled != nil { in, out := &in.RegistryDisabled, &out.RegistryDisabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryKubernetesConfig. func (in *RegistryKubernetesConfig) DeepCopy() *RegistryKubernetesConfig { if in == nil { return nil } out := new(RegistryKubernetesConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryMirrorConfig) DeepCopyInto(out *RegistryMirrorConfig) { *out = *in if in.MirrorEndpoints != nil { in, out := &in.MirrorEndpoints, &out.MirrorEndpoints *out = make([]string, len(*in)) copy(*out, *in) } if in.MirrorOverridePath != nil { in, out := &in.MirrorOverridePath, &out.MirrorOverridePath *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryMirrorConfig. func (in *RegistryMirrorConfig) DeepCopy() *RegistryMirrorConfig { if in == nil { return nil } out := new(RegistryMirrorConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryServiceConfig) DeepCopyInto(out *RegistryServiceConfig) { *out = *in if in.RegistryDisabled != nil { in, out := &in.RegistryDisabled, &out.RegistryDisabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryServiceConfig. func (in *RegistryServiceConfig) DeepCopy() *RegistryServiceConfig { if in == nil { return nil } out := new(RegistryServiceConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RegistryTLSConfig) DeepCopyInto(out *RegistryTLSConfig) { *out = *in if in.TLSClientIdentity != nil { in, out := &in.TLSClientIdentity, &out.TLSClientIdentity *out = (*in).DeepCopy() } if in.TLSCA != nil { in, out := &in.TLSCA, &out.TLSCA *out = make(Base64Bytes, len(*in)) copy(*out, *in) } if in.TLSInsecureSkipVerify != nil { in, out := &in.TLSInsecureSkipVerify, &out.TLSInsecureSkipVerify *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RegistryTLSConfig. func (in *RegistryTLSConfig) DeepCopy() *RegistryTLSConfig { if in == nil { return nil } out := new(RegistryTLSConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourcesConfig) DeepCopyInto(out *ResourcesConfig) { *out = *in in.Requests.DeepCopyInto(&out.Requests) in.Limits.DeepCopyInto(&out.Limits) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourcesConfig. func (in *ResourcesConfig) DeepCopy() *ResourcesConfig { if in == nil { return nil } out := new(ResourcesConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Route) DeepCopyInto(out *Route) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Route. func (in *Route) DeepCopy() *Route { if in == nil { return nil } out := new(Route) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *STP) DeepCopyInto(out *STP) { *out = *in if in.STPEnabled != nil { in, out := &in.STPEnabled, &out.STPEnabled *out = new(bool) **out = **in } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STP. func (in *STP) DeepCopy() *STP { if in == nil { return nil } out := new(STP) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SchedulerConfig) DeepCopyInto(out *SchedulerConfig) { *out = *in if in.ExtraArgsConfig != nil { in, out := &in.ExtraArgsConfig, &out.ExtraArgsConfig *out = make(Args, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ExtraVolumesConfig != nil { in, out := &in.ExtraVolumesConfig, &out.ExtraVolumesConfig *out = make([]VolumeMountConfig, len(*in)) copy(*out, *in) } if in.EnvConfig != nil { in, out := &in.EnvConfig, &out.EnvConfig *out = make(map[string]string, len(*in)) for key, val := range *in { (*out)[key] = val } } if in.ResourcesConfig != nil { in, out := &in.ResourcesConfig, &out.ResourcesConfig *out = new(ResourcesConfig) (*in).DeepCopyInto(*out) } in.SchedulerConfig.DeepCopyInto(&out.SchedulerConfig) return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SchedulerConfig. func (in *SchedulerConfig) DeepCopy() *SchedulerConfig { if in == nil { return nil } out := new(SchedulerConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SystemDiskEncryptionConfig) DeepCopyInto(out *SystemDiskEncryptionConfig) { *out = *in if in.StatePartition != nil { in, out := &in.StatePartition, &out.StatePartition *out = new(EncryptionConfig) (*in).DeepCopyInto(*out) } if in.EphemeralPartition != nil { in, out := &in.EphemeralPartition, &out.EphemeralPartition *out = new(EncryptionConfig) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SystemDiskEncryptionConfig. func (in *SystemDiskEncryptionConfig) DeepCopy() *SystemDiskEncryptionConfig { if in == nil { return nil } out := new(SystemDiskEncryptionConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TimeConfig) DeepCopyInto(out *TimeConfig) { *out = *in if in.TimeDisabled != nil { in, out := &in.TimeDisabled, &out.TimeDisabled *out = new(bool) **out = **in } if in.TimeServers != nil { in, out := &in.TimeServers, &out.TimeServers *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TimeConfig. func (in *TimeConfig) DeepCopy() *TimeConfig { if in == nil { return nil } out := new(TimeConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UdevConfig) DeepCopyInto(out *UdevConfig) { *out = *in if in.UdevRules != nil { in, out := &in.UdevRules, &out.UdevRules *out = make([]string, len(*in)) copy(*out, *in) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UdevConfig. func (in *UdevConfig) DeepCopy() *UdevConfig { if in == nil { return nil } out := new(UdevConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Unstructured) DeepCopyInto(out *Unstructured) { clone := in.DeepCopy() *out = *clone return } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VIPEquinixMetalConfig) DeepCopyInto(out *VIPEquinixMetalConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VIPEquinixMetalConfig. func (in *VIPEquinixMetalConfig) DeepCopy() *VIPEquinixMetalConfig { if in == nil { return nil } out := new(VIPEquinixMetalConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VIPHCloudConfig) DeepCopyInto(out *VIPHCloudConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VIPHCloudConfig. func (in *VIPHCloudConfig) DeepCopy() *VIPHCloudConfig { if in == nil { return nil } out := new(VIPHCloudConfig) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Vlan) DeepCopyInto(out *Vlan) { *out = *in if in.VlanAddresses != nil { in, out := &in.VlanAddresses, &out.VlanAddresses *out = make([]string, len(*in)) copy(*out, *in) } if in.VlanRoutes != nil { in, out := &in.VlanRoutes, &out.VlanRoutes *out = make([]*Route, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Route) **out = **in } } } if in.VlanDHCP != nil { in, out := &in.VlanDHCP, &out.VlanDHCP *out = new(bool) **out = **in } if in.VlanVIP != nil { in, out := &in.VlanVIP, &out.VlanVIP *out = new(DeviceVIPConfig) (*in).DeepCopyInto(*out) } if in.VlanDHCPOptions != nil { in, out := &in.VlanDHCPOptions, &out.VlanDHCPOptions *out = new(DHCPOptions) (*in).DeepCopyInto(*out) } return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Vlan. func (in *Vlan) DeepCopy() *Vlan { if in == nil { return nil } out := new(Vlan) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in VlanList) DeepCopyInto(out *VlanList) { { in := &in *out = make(VlanList, len(*in)) for i := range *in { if (*in)[i] != nil { in, out := &(*in)[i], &(*out)[i] *out = new(Vlan) (*in).DeepCopyInto(*out) } } return } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VlanList. func (in VlanList) DeepCopy() VlanList { if in == nil { return nil } out := new(VlanList) in.DeepCopyInto(out) return *out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolumeMountConfig) DeepCopyInto(out *VolumeMountConfig) { *out = *in return } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeMountConfig. func (in *VolumeMountConfig) DeepCopy() *VolumeMountConfig { if in == nil { return nil } out := new(VolumeMountConfig) in.DeepCopyInto(out) return out } ================================================ FILE: pkg/machinery/config/validation/mode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package validation import "fmt" // RuntimeMode abstracts current runtime mode. type RuntimeMode interface { fmt.Stringer RequiresInstall() bool InContainer() bool } ================================================ FILE: pkg/machinery/config/validation/validation.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package validation provides validation options for the config Validate method. package validation // Options additional validation parameters for the config Validate method. type Options struct { // Local should disable part of the validation flow which won't work on the host machine. Local bool // Strict mode returns warnings as errors. Strict bool } // Option represents an additional validation parameter for the config Validate method. type Option func(opts *Options) // NewOptions creates new validation options. func NewOptions(options ...Option) *Options { opts := &Options{} for _, f := range options { f(opts) } return opts } // WithLocal enables local flag. func WithLocal() Option { return func(opts *Options) { opts.Local = true } } // WithStrict enables strict flag. func WithStrict() Option { return func(opts *Options) { opts.Strict = true } } ================================================ FILE: pkg/machinery/constants/amd64.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !amd64.v1 package constants const ( // MinimumGOAMD64Level is the minimum x86_64 microarchitecture level required by Talos. MinimumGOAMD64Level = 2 ) ================================================ FILE: pkg/machinery/constants/amd64_v1.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build amd64.v1 package constants const ( // MinimumGOAMD64Level is the minimum x86_64 microarchitecture level required by Talos. MinimumGOAMD64Level = 1 ) ================================================ FILE: pkg/machinery/constants/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package constants defines constants used throughout Talos. package constants import ( "time" "github.com/siderolabs/crypto/x509" ) const ( // DefaultKernelVersion is the default Linux kernel version. DefaultKernelVersion = "6.18.18-talos" // KernelParamConfig is the kernel parameter name for specifying the URL. // to the config. KernelParamConfig = "talos.config" // KernelParamConfigInline is the kernel parameter name for specifying the inline config. // // The inline config should be base64 encoded and zstd-compressed. KernelParamConfigInline = "talos.config.inline" // KernelParamConfigEarly is the kernel parameter name for specifying the inline config (as the first source). // // The inline config should be base64 encoded and zstd-compressed. KernelParamConfigEarly = "talos.config.early" // KernelParamConfigOAuthClientID is the kernel parameter name for specifying the OAuth2 client ID. KernelParamConfigOAuthClientID = "talos.config.oauth.client_id" // KernelParamConfigOAuthClientSecret is the kernel parameter name for specifying the OAuth2 client secret. KernelParamConfigOAuthClientSecret = "talos.config.oauth.client_secret" // KernelParamConfigOAuthAudience is the kernel parameter name for specifying the OAuth2 audience. KernelParamConfigOAuthAudience = "talos.config.oauth.audience" // KernelParamConfigOAuthScope is the kernel parameter name for specifying the OAuth2 scopes (might be repeated). KernelParamConfigOAuthScope = "talos.config.oauth.scope" // KernelParamConfigOAuthDeviceAuthURL is the kernel parameter name for specifying the OAuth2 device auth URL. KernelParamConfigOAuthDeviceAuthURL = "talos.config.oauth.device_auth_url" // KernelParamConfigOAuthTokenURL is the kernel parameter name for specifying the OAuth2 token URL. KernelParamConfigOAuthTokenURL = "talos.config.oauth.token_url" // KernelParamConfigOAuthExtraVariable is the kernel parameter name for specifying the OAuth2 extra variable (might be repeated). KernelParamConfigOAuthExtraVariable = "talos.config.oauth.extra_variable" // ConfigNone indicates no config is required. ConfigNone = "none" // KernelParamPlatform is the kernel parameter name for specifying the // platform. KernelParamPlatform = "talos.platform" // KernelParamEventsSink is the kernel parameter name for specifying the // events sink server. KernelParamEventsSink = "talos.events.sink" // KernelParamLoggingKernel is the kernel parameter name for specifying the // kernel log delivery destination. KernelParamLoggingKernel = "talos.logging.kernel" // KernelParamWipe is the kernel parameter name for specifying the // disk to wipe on the next boot and reboot. KernelParamWipe = "talos.experimental.wipe" // KernelParamDeviceSettleTime is the kernel parameter name for specifying the // extra device settle timeout. KernelParamDeviceSettleTime = "talos.device.settle_time" // KernelParamCGroups is the legacy kernel parameter not supported anymore. KernelParamCGroups = "talos.unified_cgroup_hierarchy" // KernelParamAuditdDisabled is the kernel parameter name for disabling auditd service. KernelParamAuditdDisabled = "talos.auditd.disabled" // KernelParamDashboardDisabled is the kernel parameter name for disabling the dashboard. KernelParamDashboardDisabled = "talos.dashboard.disabled" // KernelParamDashboardConsole is the kernel parameter name for specifying the dashboard console. KernelParamDashboardConsole = "talos.dashboard.console" // KernelParamEnvironment is the kernel parameter name for passing process environment. KernelParamEnvironment = "talos.environment" // KernelParamNetIfnames is the kernel parameter name to control predictable network interface names. KernelParamNetIfnames = "net.ifnames" // KernelParamHaltIfInstalled is the kernel parameter name to control if Talos should pause if booting from boot media while Talos is already installed. KernelParamHaltIfInstalled = "talos.halt_if_installed" // KernelParamSELinux is the kernel parameter name to enable/disable SELinux. KernelParamSELinux = "selinux" // KernelParamSELinuxEnforcing is the kernel parameter name to control SELinux enforcement mode. KernelParamSELinuxEnforcing = "enforcing" // KernelParamHostname is the kernel parameter name for specifying the // hostname. KernelParamHostname = "talos.hostname" // KernelParamShutdown is the kernel parameter for specifying the // shutdown type (halt/poweroff). KernelParamShutdown = "talos.shutdown" // KernelParamNetworkInterfaceIgnore is the kernel parameter for specifying network interfaces which should be ignored by talos. KernelParamNetworkInterfaceIgnore = "talos.network.interface.ignore" // KernelParamVlan is the kernel parameter for specifying vlan for the interface. KernelParamVlan = "vlan" // KernelParamBonding is the kernel parameter for specifying bonded network interfaces. KernelParamBonding = "bond" // KernelParamPanic is the kernel parameter name for specifying the time to wait until rebooting after kernel panic (0 disables reboot). KernelParamPanic = "panic" // KernelParamSideroLink is the kernel parameter name to specify SideroLink API endpoint. KernelParamSideroLink = "siderolink.api" // KernelParamEquinixMetalEvents is the kernel parameter name to specify the Equinix Metal phone home endpoint. // This param is injected by Equinix Metal and depends on the device ID and datacenter. KernelParamEquinixMetalEvents = "em.events_url" // KernelParamEnforceModuleSigVerify is the kernel parameter name to specify module signature verification enforcement. // see https://github.com/siderolabs/talos/issues/11989 KernelParamEnforceModuleSigVerify = "module.sig_enforce" // NewRoot is the path where the switchroot target is mounted. NewRoot = "/root" // ExtensionLayers is the path where the extensions layers are stored. ExtensionLayers = "/layers" // ExtensionsConfigFile is the extensions layers configuration file name in the initramfs. ExtensionsConfigFile = "/extensions.yaml" // ExtensionsRuntimeConfigFile extensions layers configuration file name in the rootfs. ExtensionsRuntimeConfigFile = "/etc/extensions.yaml" // EFIPartitionLabel is the label of the partition to use for mounting at // the boot path. EFIPartitionLabel = "EFI" // EFIMountPoint is the label of the partition to use for mounting at // the boot path. EFIMountPoint = BootMountPoint + "/EFI" // EFIVarsMountPoint is mount point for efivars filesystem type. // https://www.kernel.org/doc/html/next/filesystems/efivarfs.html EFIVarsMountPoint = "/sys/firmware/efi/efivars" // BIOSGrubPartitionLabel is the label of the partition used by grub's second // stage bootloader. BIOSGrubPartitionLabel = "BIOS" // MetaPartitionLabel is the label of the meta partition. MetaPartitionLabel = "META" // StatePartitionLabel is the label of the state partition. StatePartitionLabel = "STATE" // StateMountPoint is the label of the partition to use for mounting at // the state path. StateMountPoint = "/system/state" // StateSelinuxLabel is the label to be assigned to the state mount. StateSelinuxLabel = "system_u:object_r:system_state_t:s0" // BootPartitionLabel is the label of the partition to use for mounting at // the boot path. BootPartitionLabel = "BOOT" // BootMountPoint is the label of the partition to use for mounting at // the boot path. BootMountPoint = "/boot" // EphemeralPartitionLabel is the label of the partition to use for // mounting at the data path. EphemeralPartitionLabel = "EPHEMERAL" // EphemeralMountPoint is the label of the partition to use for mounting at // the data path. EphemeralMountPoint = "/var" // EphemeralSelinuxLabel is the label to be assigned to the ephemeral mount. EphemeralSelinuxLabel = "system_u:object_r:ephemeral_t:s0" // OptSELinuxLabel is the SELinux label to be set for /opt overlay mount. OptSELinuxLabel = "system_u:object_r:opt_t:s0" // RootMountPoint is the label of the partition to use for mounting at // the root path. RootMountPoint = "/" // ISOFilesystemLabel is the label of the ISO file system for the Talos // installer. ISOFilesystemLabel = "TALOS" // KubernetesDefaultCertificateValidityDuration specifies default certificate duration for Kubernetes generated certificates. KubernetesDefaultCertificateValidityDuration = time.Hour * 24 * 365 // KubernetesConfigBaseDir is the path to the base Kubernetes configuration directory. KubernetesConfigBaseDir = "/etc/kubernetes" // KubernetesConfigSELinuxLabel is the SELinux label to be set for the Kubernetes configuration directory overlay mount. KubernetesConfigSELinuxLabel = "system_u:object_r:k8s_conf_t:s0" // KubeletPluginsSELinuxLabel is the SELinux label to be set for the Kubernetes plugin directory overlay mount. KubeletPluginsSELinuxLabel = "system_u:object_r:k8s_plugin_t:s0" // DefaultCertificatesDir is the path the Kubernetes PKI directory. DefaultCertificatesDir = KubernetesConfigBaseDir + "/" + "pki" // KubernetesCACert is the path to the root CA certificate. KubernetesCACert = DefaultCertificatesDir + "/" + "ca.crt" // EtcdCACert is the path to the etcd CA certificate. EtcdCACert = EtcdPKIPath + "/" + "ca.crt" // EtcdCAKey is the path to the etcd CA private key. EtcdCAKey = EtcdPKIPath + "/" + "ca.key" // EtcdCert is the path to the etcd server certificate. EtcdCert = EtcdPKIPath + "/" + "server.crt" // EtcdKey is the path to the etcd server private key. EtcdKey = EtcdPKIPath + "/" + "server.key" // EtcdPeerCert is the path to the etcd peer certificate. EtcdPeerCert = EtcdPKIPath + "/" + "peer.crt" // EtcdPeerKey is the path to the etcd peer private key. EtcdPeerKey = EtcdPKIPath + "/" + "peer.key" // EtcdAdminCert is the path to the talos client certificate. EtcdAdminCert = EtcdPKIPath + "/" + "admin.crt" // EtcdAdminKey is the path to the talos client private key. EtcdAdminKey = EtcdPKIPath + "/" + "admin.key" // EtcdClientPort defines the port etcd listen on for client traffic. EtcdClientPort = 2379 // EtcdPeerPort defines the port etcd listens on for peer traffic. EtcdPeerPort = 2380 // KubernetesAdminCertCommonName defines CN property of Kubernetes admin certificate. KubernetesAdminCertCommonName = "admin" // KubernetesTalosAdminCertCommonName defines CN property of Kubernetes admin certificate used by Talos itself. KubernetesTalosAdminCertCommonName = "talos:admin" // KubernetesAdminCertOrganization defines Organization values of Kubernetes admin certificate. KubernetesAdminCertOrganization = "system:masters" // KubernetesAPIServerKubeletClientCommonName defines CN property of Kubernetes API server certificate to access kubelet API. KubernetesAPIServerKubeletClientCommonName = "apiserver-kubelet-client" // KubernetesControllerManagerOrganization defines Organization value of kube-controller-manager client certificate. KubernetesControllerManagerOrganization = "system:kube-controller-manager" // KubernetesSchedulerOrganization defines Organization value of kube-scheduler client certificate. KubernetesSchedulerOrganization = "system:kube-scheduler" // KubernetesAdminCertDefaultLifetime defines default lifetime for Kubernetes generated admin certificate. KubernetesAdminCertDefaultLifetime = 365 * 24 * time.Hour // KubebernetesStaticSecretsDir defines ephemeral directory which contains rendered secrets for controlplane components. KubebernetesStaticSecretsDir = "/system/secrets/kubernetes" // KubebernetesStaticConfigDir defines ephemeral directory which contains rendered configs for controlplane components. KubebernetesStaticConfigDir = "/system/config/kubernetes" // KubernetesAuditLogDir defines the ephemeral directory where the kube-apiserver will store its audit logs. KubernetesAuditLogDir = EphemeralMountPoint + "/" + "log" + "/" + "audit" + "/" + "kube" // KubernetesAPIServerSecretsDir defines directory with kube-apiserver secrets. KubernetesAPIServerSecretsDir = KubebernetesStaticSecretsDir + "/" + "kube-apiserver" // KubernetesAPIServerSecretsDirSELinuxLabel defines SELinux label for the directory with kube-apiserver secrets. KubernetesAPIServerSecretsDirSELinuxLabel = "system_u:object_r:kube_apiserver_secret_t:s0" // KubernetesAPIServerConfigDir defines directory with kube-apiserver configs. KubernetesAPIServerConfigDir = KubebernetesStaticConfigDir + "/" + "kube-apiserver" // KubernetesAPIServerConfigDirSELinuxLabel defines SELinux label for the directory with kube-apiserver configs. KubernetesAPIServerConfigDirSELinuxLabel = "system_u:object_r:kube_apiserver_config_t:s0" // KubernetesControllerManagerSecretsDir defines ephemeral directory with kube-controller-manager secrets. KubernetesControllerManagerSecretsDir = KubebernetesStaticSecretsDir + "/" + "kube-controller-manager" // KubernetesControllerManagerSecretsDirSELinuxLabel defines SELinux label for the ephemeral directory with kube-controller-manager secrets. KubernetesControllerManagerSecretsDirSELinuxLabel = "system_u:object_r:kube_controller_manager_secret_t:s0" // KubernetesSchedulerSecretsDir defines ephemeral directory with kube-scheduler secrets. KubernetesSchedulerSecretsDir = KubebernetesStaticSecretsDir + "/" + "kube-scheduler" // KubernetesSchedulerSecretsDirSELinuxLabel defines SELinux label for the ephemeral directory with kube-scheduler secrets. KubernetesSchedulerSecretsDirSELinuxLabel = "system_u:object_r:kube_scheduler_secret_t:s0" // KubernetesSchedulerConfigDir defines ephemeral directory with kube-scheduler configs. KubernetesSchedulerConfigDir = KubebernetesStaticConfigDir + "/" + "kube-scheduler" // KubernetesSchedulerConfigDirSELinuxLabel defines SELinux label for the ephemeral directory with kube-scheduler configs. KubernetesSchedulerConfigDirSELinuxLabel = "system_u:object_r:kube_scheduler_config_t:s0" // KubernetesAPIServerRunUser defines UID to the API Server. KubernetesAPIServerRunUser = 65534 // KubernetesAPIServerRunGroup defines GID to run the API Server. KubernetesAPIServerRunGroup = 65534 // KubernetesControllerManagerRunUser defines UID to the Controller Manager. KubernetesControllerManagerRunUser = 65535 // KubernetesControllerManagerRunGroup defines GID to run the Controller Manager. KubernetesControllerManagerRunGroup = 65535 // KubernetesSchedulerRunUser defines UID to the Scheduler. KubernetesSchedulerRunUser = 65536 // KubernetesSchedulerRunGroup defines GID to run the Scheduler. KubernetesSchedulerRunGroup = 65536 // KubeletBootstrapKubeconfig is the path to the kubeconfig required to // bootstrap the kubelet. KubeletBootstrapKubeconfig = KubernetesConfigBaseDir + "/" + "bootstrap-kubeconfig" // KubeletCredentialProviderBinDir is the path to the directory where kubelet credential provider binaries are stored. KubeletCredentialProviderBinDir = "/usr/local/lib/kubelet/credentialproviders" // KubeletCredentialProviderConfig is the path to the kubelet credential provider config. KubeletCredentialProviderConfig = KubernetesConfigBaseDir + "/" + "kubelet-credentialproviderconfig.yaml" // KubeletPort is the kubelet port for secure API. KubeletPort = 10250 // KubeletOOMScoreAdj oom_score_adj config. KubeletOOMScoreAdj = -450 // KubeletPKIDir is the path to the directory where kubelet stores issued certificates and keys. KubeletPKIDir = "/var/lib/kubelet/pki" // SystemKubeletPKIDir is the path to the directory where Talos copies kubelet issued certificates and keys. SystemKubeletPKIDir = "/system/secrets/kubelet" // KubeletShutdownGracePeriod is the kubelet shutdown grace period. KubeletShutdownGracePeriod = 30 * time.Second // KubeletShutdownGracePeriodCriticalPods is the kubelet shutdown grace period for critical pods. // // Should be less than KubeletShutdownGracePeriod. KubeletShutdownGracePeriodCriticalPods = 10 * time.Second // SeccompProfilesDirectory is the path to the directory where user provided seccomp profiles are mounted inside Kubelet. SeccompProfilesDirectory = "/var/lib/kubelet/seccomp/profiles" // DefaultKubernetesVersion is the default target version of the control plane. // renovate: datasource=github-releases depName=kubernetes/kubernetes DefaultKubernetesVersion = "1.36.0-alpha.2" // SupportedKubernetesVersions is the number of Kubernetes versions supported by Talos starting from DefaultKubernesVersion going backwards. SupportedKubernetesVersions = 6 // DefaultControlPlanePort is the default port to use for the control plane. DefaultControlPlanePort = 6443 // KubeletImage is the enforced kubelet image to use. KubeletImage = "ghcr.io/siderolabs/kubelet" // KubeProxyImage is the enforced kube-proxy image to use for the control plane. KubeProxyImage = "registry.k8s.io/kube-proxy" // KubernetesAPIServerImage is the enforced apiserver image to use for the control plane. KubernetesAPIServerImage = "registry.k8s.io/kube-apiserver" // KubernetesControllerManagerImage is the enforced controllermanager image to use for the control plane. KubernetesControllerManagerImage = "registry.k8s.io/kube-controller-manager" // KubernetesSchedulerImage is the enforced scheduler image to use for the control plane. KubernetesSchedulerImage = "registry.k8s.io/kube-scheduler" // CoreDNSImage is the enforced CoreDNS image to use. CoreDNSImage = "registry.k8s.io/coredns/coredns" // DefaultCoreDNSVersion is the default version for the CoreDNS. // renovate: datasource=docker depName=registry.k8s.io/coredns/coredns DefaultCoreDNSVersion = "v1.14.2" // LabelNodeRoleControlPlane is the node label required by a control plane node. LabelNodeRoleControlPlane = "node-role.kubernetes.io/control-plane" // LabelExcludeFromExternalLB can be set on a node to exclude it from external load balancers. LabelExcludeFromExternalLB = "node.kubernetes.io/exclude-from-external-load-balancers" // ManifestsDirectory is the directory that contains all static manifests. ManifestsDirectory = KubernetesConfigBaseDir + "/" + "manifests" // TalosManifestPrefix is the prefix for static pod files created in ManifestsDirectory by Talos. TalosManifestPrefix = "talos-" // KubeletKubeconfig is the generated kubeconfig for kubelet. KubeletKubeconfig = KubernetesConfigBaseDir + "/" + "kubeconfig-kubelet" // KubeletSystemReservedCPU cpu system reservation value for kubelet kubeconfig. KubeletSystemReservedCPU = "50m" // KubeletSystemReservedMemoryControlPlane memory system reservation value for kubelet kubeconfig (controlplane nodes). KubeletSystemReservedMemoryControlPlane = "512Mi" // KubeletSystemReservedMemoryWorker memory system reservation value for kubelet kubeconfig (worker nodes). KubeletSystemReservedMemoryWorker = "384Mi" // KubeletSystemReservedPid pid system reservation value for kubelet kubeconfig. KubeletSystemReservedPid = "100" // KubeletSystemReservedEphemeralStorage ephemeral-storage system reservation value for kubelet kubeconfig. KubeletSystemReservedEphemeralStorage = "256Mi" // DefaultEtcdVersion is the default target version of etcd. // renovate: datasource=docker depName=registry.k8s.io/etcd DefaultEtcdVersion = "v3.6.8" // EtcdRootTalosKey is the root etcd key for Talos-specific storage. EtcdRootTalosKey = "talos:v1" // EtcdTalosEtcdUpgradeMutex is the etcd mutex prefix to be used to set an etcd upgrade lock. EtcdTalosEtcdUpgradeMutex = EtcdRootTalosKey + ":etcdUpgradeMutex" // EtcdTalosManifestApplyMutex is the etcd mutex prefix used by manifest apply controller. EtcdTalosManifestApplyMutex = EtcdRootTalosKey + ":manifestApplyMutex" // EtcdTalosServiceAccountCRDControllerMutex is the etcd mutex prefix used by Talos ServiceAccount crd controller. EtcdTalosServiceAccountCRDControllerMutex = EtcdRootTalosKey + ":serviceAccountCRDController" // EtcdImage is the reposistory for the etcd image. EtcdImage = "registry.k8s.io/etcd" // EtcdPKIPath is the path to the etcd PKI directory. EtcdPKIPath = "/system/secrets/etcd" // EtcdPKISELinuxLabel is the SELinux label for the etcd PKI directory. EtcdPKISELinuxLabel = "system_u:object_r:etcd_pki_t:s0" // EtcdDataPath is the path where etcd stores its' data. EtcdDataPath = "/var/lib/etcd" // EtcdDataVolumeID is the ID of the etcd data volume. EtcdDataVolumeID = "ETCD" // EtcdDataSELinuxLabel is the SELinux label for the etcd data directory. EtcdDataSELinuxLabel = "system_u:object_r:etcd_data_t:s0" // EtcdRecoverySnapshotPath is the path where etcd snapshot is uploaded for recovery. EtcdRecoverySnapshotPath = "/var/lib/etcd.snapshot" // EtcdUserID is the user ID for the etcd process. EtcdUserID = 60 // ConfigFilename is the filename of the saved config in STATE partition. ConfigFilename = "config.yaml" // EmbeddedConfigDirectory is the path to the embedded config is placed inside rootfs. EmbeddedConfigDirectory = "/usr/local/etc/talos/" // ConfigTryTimeout is the timeout of the config apply in try mode. ConfigTryTimeout = time.Minute // MetalConfigISOLabel is the volume label for ISO based configuration. MetalConfigISOLabel = "metal-iso" // ConfigGuestInfo is the name of the VMware guestinfo config strategy. ConfigGuestInfo = "guestinfo" // VMwareGuestInfoPrefix is the prefix to extraConfig variables. VMwareGuestInfoPrefix = "guestinfo." // VMwareGuestInfoConfigKey is the guestinfo key used to provide a config file. VMwareGuestInfoConfigKey = "talos.config" // VMwareGuestInfoFallbackKey is the fallback guestinfo key used to provide a config file. VMwareGuestInfoFallbackKey = "userdata" // VMwareGuestInfoMetadataKey is the guestinfo key used to provide metadata. VMwareGuestInfoMetadataKey = "metadata" // VMwareGuestInfoOvfEnvKey is the guestinfo key used to provide the OVF environment. VMwareGuestInfoOvfEnvKey = "ovfenv" // AuditPolicyPath is the path to the audit-policy.yaml relative to initramfs. AuditPolicyPath = KubernetesConfigBaseDir + "/" + "audit-policy.yaml" // EncryptionConfigPath is the path to the EncryptionConfig relative to initramfs. EncryptionConfigPath = KubernetesConfigBaseDir + "/" + "encryptionconfig.yaml" // EncryptionConfigRootfsPath is the path to the EncryptionConfig relative to rootfs. EncryptionConfigRootfsPath = KubernetesConfigBaseDir + "/" + "encryptionconfig.yaml" // ApidPort is the port for the apid service. ApidPort = 50000 // ApidUserID is the user ID for apid. ApidUserID = 50 // DashboardUserID is the user ID for dashboard. // We use the same user ID as apid so that the dashboard can write to the machined unix socket. DashboardUserID = ApidUserID // DashboardPriority is the priority for the dashboard service. // Higher nice value for the dashboard to give more CPU time to other services when under load. DashboardPriority = 10 // TrustdPort is the port for the trustd service. TrustdPort = 50001 // TrustdUserID is the user ID for trustd. TrustdUserID = 51 // DefaultContainerdVersion is the default container runtime version. DefaultContainerdVersion = "2.2.2" // RuncVersion is the runc version. RuncVersion = "1.4.1" // SystemContainerdNamespace is the Containerd namespace for Talos services. SystemContainerdNamespace = "system" // SystemContainerdAddress is the path to the system containerd socket. SystemContainerdAddress = SystemRunPath + "/containerd/containerd.sock" // K8sContainerdNamespace is the Containerd namespace for CRI pods. K8sContainerdNamespace = "k8s.io" // CRIContainerdAddress is the path to the CRI containerd socket address. CRIContainerdAddress = "/run/containerd/containerd.sock" // CRIContainerdConfig is the path to the config for the containerd instance that provides the CRI. CRIContainerdConfig = "/etc/cri/containerd.toml" // EtcCRIConfdPath is the path to the directory providing parts of CRI plugin configuration. EtcCRIConfdPath = "/etc/cri/conf.d" // CRIConfdPath is the path to the subdirectory providing parts of CRI plugin configuration. CRIConfdPath = "cri/conf.d" // CRIConfig is the path to the CRI merged configuration file relative to /etc. CRIConfig = "cri/conf.d/cri.toml" // CRIRegistryConfigPart is the path to the CRI generated registry configuration relative to /etc. CRIRegistryConfigPart = "cri/conf.d/01-registries.part" // CRICustomizationConfigPart is the path to the CRI generated registry configuration relative to /etc. CRICustomizationConfigPart = "cri/conf.d/20-customization.part" // CRIBaseRuntimeSpec is the path to the base runtime spec for the CRI. CRIBaseRuntimeSpec = "cri/conf.d/base-spec.json" // TalosConfigEnvVar is the environment variable for setting the Talos configuration file path. TalosConfigEnvVar = "TALOSCONFIG" // TalosHomeEnvVar is the environment variable for setting the Talos state directory file path. TalosHomeEnvVar = "TALOS_HOME" // APISocketPath is the path to file socket of apid. APISocketPath = SystemRunPath + "/apid/apid.sock" // APIRuntimeSocketPath is the path to file socket of runtime server for apid. APIRuntimeSocketPath = SystemRunPath + "/apid/runtime.sock" // APIRuntimeSocketLabel is the SELinux label for apid runtime socket file. APIRuntimeSocketLabel = "system_u:object_r:apid_runtime_socket_t:s0" // TrustdRuntimeSocketPath is the path to file socket of runtime server for trustd. TrustdRuntimeSocketPath = SystemRunPath + "/trustd/runtime.sock" // TrustdRuntimeSocketLabel is the SELinux label for trustd runtime socket file. TrustdRuntimeSocketLabel = "system_u:object_r:trustd_runtime_socket_t:s0" // MachineSocketPath is the path to file socket of machine API. MachineSocketPath = SystemRunPath + "/machined/machine.sock" // MachineSocketLabel is the SELinux label for socket of machine API. MachineSocketLabel = "system_u:object_r:machine_socket_t:s0" // NetworkSocketPath is the path to file socket of network API. NetworkSocketPath = SystemRunPath + "/networkd/networkd.sock" // ArchVariable is replaced automatically by the target cluster arch. ArchVariable = "${ARCH}" // KernelAsset defines a well known name for our kernel filename. KernelAsset = "vmlinuz" // KernelAssetWithArch defines a well known name for our kernel filename with arch variable. KernelAssetWithArch = "vmlinuz-" + ArchVariable // KernelAssetPath is the path to the kernel on disk. KernelAssetPath = "/usr/install/%s/" + KernelAsset // InitramfsAsset defines a well known name for our initramfs filename. InitramfsAsset = "initramfs.xz" // InitramfsAssetWithArch defines a well known name for our initramfs filename with arch variable. InitramfsAssetWithArch = "initramfs-" + ArchVariable + ".xz" // InitramfsAssetPath is the path to the initramfs on disk. InitramfsAssetPath = "/usr/install/%s/" + InitramfsAsset // RootfsAsset defines a well known name for our rootfs filename. RootfsAsset = "rootfs.sqsh" // UKIAsset defines a well known name for our UKI filename. UKIAsset = "vmlinuz.efi" // UKIAssetPath is the path to the UKI in the installer. UKIAssetPath = "/usr/install/%s/" + UKIAsset // SDStubAsset defines a well known name for our systemd-stub filename. SDStubAsset = "systemd-stub.efi" // SDStubAssetPath is the path to the systemd-stub in the installer. SDStubAssetPath = "/usr/install/%s/" + SDStubAsset // SDBootAsset defines a well known name for our SDBoot filename. SDBootAsset = "systemd-boot.efi" // SDBootAssetPath is the path to the SDBoot in the installer. SDBootAssetPath = "/usr/install/%s/" + SDBootAsset // ImagerOverlayBasePath is the base path for the imager overlay. ImagerOverlayBasePath = "/overlay" // ImagerOverlayArtifactsPath is the path to the artifacts in the imager overlay. ImagerOverlayArtifactsPath = ImagerOverlayBasePath + "/" + "artifacts" // ImagerOverlayInstallersPath is the path to the installers in the imager overlay. ImagerOverlayInstallersPath = ImagerOverlayBasePath + "/" + "installers" // ImagerOverlayProfilesPath is the path to the profiles in the imager overlay. ImagerOverlayProfilesPath = ImagerOverlayBasePath + "/" + "profiles" // ImagerOverlayInstallerDefault is the default installer name. ImagerOverlayInstallerDefault = "default" // ImagerOverlayInstallerDefaultPath is the path to the default installer in the imager overlay. ImagerOverlayInstallerDefaultPath = ImagerOverlayInstallersPath + "/" + ImagerOverlayInstallerDefault // ImagerOverlayExtraOptionsPath is the path to the generated extra options file in the imager overlay. ImagerOverlayExtraOptionsPath = ImagerOverlayBasePath + "/" + "extra-options" // PlatformKeyAsset defines a well known name for the platform key filename used for auto-enrolling. PlatformKeyAsset = "PK.auth" // KeyExchangeKeyAsset defines a well known name for the key exchange key filename used for auto-enrolling. KeyExchangeKeyAsset = "KEK.auth" // SignatureKeyAsset defines a well known name for the signature key filename used for auto-enrolling. SignatureKeyAsset = "db.auth" // SecureBootSigningKeyAsset defines a well known name for the secure boot signing key filename. SecureBootSigningKeyAsset = "uki-signing-key.pem" // SecureBootSigningCertAsset defines a well known name for the secure boot signing key filename. SecureBootSigningCertAsset = "uki-signing-cert.pem" // PCRSigningKeyAsset defines a well known name for the PCR signing key filename. PCRSigningKeyAsset = "pcr-signing-key.pem" // SDStubDynamicInitrdPath is the path where dynamically generated initrds are placed by systemd-stub. // https://www.mankier.com/7/systemd-stub#Description SDStubDynamicInitrdPath = "/.extra" // PCRSignatureJSON is the path to the PCR signature JSON file. // https://www.mankier.com/7/systemd-stub#Initrd_Resources PCRSignatureJSON = SDStubDynamicInitrdPath + "/" + "tpm2-pcr-signature.json" // PCRPublicKey is the path to the PCR public key file. // https://www.mankier.com/7/systemd-stub#Initrd_Resources PCRPublicKey = SDStubDynamicInitrdPath + "/" + "tpm2-pcr-public-key.pem" // UKIPCR is the PCR number where systemd-stub measures the UKI. UKIPCR = 11 // SecureBootStatePCR is the PCR number where the secure boot state and the signature are measured. // PCR 7 changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) are updated. SecureBootStatePCR = 7 // DefaultCertificateValidityDuration is the default duration for a certificate. DefaultCertificateValidityDuration = x509.DefaultCertificateValidityDuration // SystemPath is the path to write temporary runtime system related files // and directories. SystemPath = "/system" // SystemSelinuxLabel is the SELinux label for runtime system related files and directories. SystemSelinuxLabel = "system_u:object_r:system_t:s0" // RunPath is the path to the system run directory. RunPath = "/run" // RunSelinuxLabel is the SELinux label for the run directory. RunSelinuxLabel = "system_u:object_r:run_t:s0" // VarSystemOverlaysPath is the path where overlay mounts are created. VarSystemOverlaysPath = "/var/system/overlays" // SystemRunPath is the path to the system run directory. SystemRunPath = SystemPath + "/run" // SystemVarPath is the path to the system var directory. SystemVarPath = SystemPath + "/var" // SystemVarSelinuxLabel is the SELinux label for the system var directory. SystemVarSelinuxLabel = "system_u:object_r:system_var_t:s0" // SystemEtcPath is the path to the system etc directory. SystemEtcPath = SystemPath + "/etc" // EtcSelinuxLabel is the SELinux label for the /etc and /system/etc directories. EtcSelinuxLabel = "system_u:object_r:etc_t:s0" // SystemLibexecPath is the path to the system libexec directory. SystemLibexecPath = SystemPath + "/libexec" // SystemOverlaysPath is the path to the system overlay directory. SystemOverlaysPath = SystemPath + "/overlays" // CgroupMountPath is the default mount path for unified cgroupsv2 setup. CgroupMountPath = "/sys/fs/cgroup" // CgroupInit is the cgroup name for init process. CgroupInit = "/init" // CgroupInitReservedMemory is the hard memory protection for the init process. CgroupInitReservedMemory = 96 * 1024 * 1024 // CgroupInitMillicores is the CPU weight for the init process. CgroupInitMillicores = 2000 // CgroupSystem is the cgroup name for system processes. CgroupSystem = "/system" // CgroupSystemMillicores is the CPU weight for the system cgroup. CgroupSystemMillicores = 1500 // CgroupSystemReservedMemory is the hard memory protection for the system processes. CgroupSystemReservedMemory = 96 * 1024 * 1024 // CgroupSystemRuntime is the cgroup name for containerd runtime processes. CgroupSystemRuntime = CgroupSystem + "/runtime" // CgroupSystemRuntimeReservedMemory is the hard memory protection for the system containerd process. CgroupSystemRuntimeReservedMemory = 48 * 1024 * 1024 // CgroupSystemRuntimeMillicores is the CPU weight for the system containerd process. CgroupSystemRuntimeMillicores = 500 // CgroupSystemDebug is the cgroup name for debug processes. CgroupSystemDebug = CgroupSystem + "/debug" // SelinuxLabelMachined is the SELinux label for machined. SelinuxLabelMachined = "system_u:system_r:init_t:s0" // SelinuxLabelInstaller is the SELinux label for the installer. SelinuxLabelInstaller = "system_u:system_r:installer_t:s0" // SelinuxLabelUnconfinedSysContainer is the SELinux label for system containers without label set (normally extensions). SelinuxLabelUnconfinedSysContainer = "system_u:system_r:unconfined_container_t:s0" // SelinuxLabelUnconfinedService is the SELinux label for process without label set (normally should not occur). SelinuxLabelUnconfinedService = "system_u:system_r:unconfined_service_t:s0" // SelinuxLabelSystemRuntime is the SELinux label for containerd runtime processes. SelinuxLabelSystemRuntime = "system_u:system_r:sys_containerd_t:s0" // CgroupApid is the cgroup name for apid runtime processes. CgroupApid = CgroupSystem + "/apid" // CgroupApidReservedMemory is the hard memory protection for the apid processes. CgroupApidReservedMemory = 16 * 1024 * 1024 // CgroupApidMaxMemory is the hard memory limit for the apid process. CgroupApidMaxMemory = 128 * 1024 * 1024 // CgroupApidMillicores is the CPU weight for the apid process. CgroupApidMillicores = 500 // SelinuxLabelApid is the SELinux label for apid runtime processes. SelinuxLabelApid = "system_u:system_r:apid_t:s0" // CgroupTrustd is the cgroup name for trustd runtime processes. CgroupTrustd = CgroupSystem + "/trustd" // CgroupTrustdReservedMemory is the hard memory protection for the trustd processes. CgroupTrustdReservedMemory = 8 * 1024 * 1024 // CgroupTrustdMaxMemory is the hard memory limit for the trustd process. CgroupTrustdMaxMemory = 128 * 1024 * 1024 // CgroupTrustdMillicores is the CPU weight for the trustd process. CgroupTrustdMillicores = 250 // SelinuxLabelTrustd is the SELinux label for trustd runtime processes. SelinuxLabelTrustd = "system_u:system_r:trustd_t:s0" // CgroupUdevd is the cgroup name for udevd runtime processes. CgroupUdevd = CgroupSystem + "/udevd" // CgroupUdevdReservedMemory is the hard memory protection for the udevd processes. CgroupUdevdReservedMemory = 8 * 1024 * 1024 // CgroupUdevdMillicores is the CPU weight for the udevd process. CgroupUdevdMillicores = 250 // SelinuxLabelUdevd is the SELinux label for udevd runtime processes. SelinuxLabelUdevd = "system_u:system_r:udev_t:s0" // CgroupExtensions is the cgroup name for system extension processes. CgroupExtensions = CgroupSystem + "/extensions" // CgroupDashboard is the cgroup name for dashboard process. CgroupDashboard = CgroupSystem + "/dashboard" // SelinuxLabelDashboard is the SELinux label for dashboard process. SelinuxLabelDashboard = "system_u:system_r:dashboard_t:s0" // CgroupPodRuntimeRoot is the cgroup containing Kubernetes runtime components. CgroupPodRuntimeRoot = "/podruntime" // CgroupPodRuntimeRootMillicores is the CPU weight for the pod runtime cgroup. CgroupPodRuntimeRootMillicores = 4000 // CgroupPodRuntime is the cgroup name for kubernetes containerd runtime processes. CgroupPodRuntime = CgroupPodRuntimeRoot + "/runtime" // CgroupPodRuntimeMillicores is the CPU weight for the pod runtime cgroup. CgroupPodRuntimeMillicores = 1000 // SelinuxLabelPodRuntime is the SELinux label for kubernetes containerd runtime processes. SelinuxLabelPodRuntime = "system_u:system_r:pod_containerd_t:s0" // CgroupPodRuntimeReservedMemory is the hard memory protection for the cri runtime processes. CgroupPodRuntimeReservedMemory = 196 * 1024 * 1024 // CgroupEtcd is the cgroup name for etcd process. CgroupEtcd = CgroupPodRuntimeRoot + "/etcd" // CgroupEtcdReservedMemory is the soft memory protection for the etcd processes. CgroupEtcdReservedMemory = 256 * 1024 * 1024 // CgroupEtcdMillicores is the CPU weight for the etcd process. CgroupEtcdMillicores = 2000 // SELinuxLabelEtcd is the SELinux label for etcd process. SELinuxLabelEtcd = "system_u:system_r:etcd_t:s0" // CgroupKubelet is the cgroup name for kubelet process. CgroupKubelet = CgroupPodRuntimeRoot + "/kubelet" // SelinuxLabelKubelet is the SELinux label for kubelet process. SelinuxLabelKubelet = "system_u:system_r:kubelet_t:s0" // CgroupKubeletReservedMemory is the hard memory protection for the kubelet processes. CgroupKubeletReservedMemory = 96 * 1024 * 1024 // CgroupKubeletMillicores is the CPU weight for the kubelet process. CgroupKubeletMillicores = 1000 // CgroupDashboardMaxMemory is the hard memory limit for the dashboard process. CgroupDashboardMaxMemory = 196 * 1024 * 1024 // CgroupDashboardMillicores is the CPU weight for the dashboard process. CgroupDashboardMillicores = 200 // FlannelCNI is the string to use Tanos-managed Flannel CNI (default). FlannelCNI = "flannel" // CustomCNI is the string to use custom CNI managed by Tanos with extra manifests. CustomCNI = "custom" // NoneCNI is the string to indicate that CNI will not be managed by Talos. NoneCNI = "none" // CNISELinuxLabel is the SELinux label to be set for CNI configuration overlay mount. CNISELinuxLabel = "system_u:object_r:cni_conf_t:s0" // DefaultIPv4PodNet is the IPv4 network to be used for kubernetes Pods. DefaultIPv4PodNet = "10.244.0.0/16" // DefaultIPv4ServiceNet is the IPv4 network to be used for kubernetes Services. DefaultIPv4ServiceNet = "10.96.0.0/12" // DefaultIPv6PodNet is the IPv6 network to be used for kubernetes Pods. DefaultIPv6PodNet = "fc00:db8:10::/56" // DefaultIPv6ServiceNet is the IPv6 network to be used for kubernetes Services. DefaultIPv6ServiceNet = "fc00:db8:20::/112" // DefaultDNSDomain is the default DNS domain. DefaultDNSDomain = "cluster.local" // ConfigLoadTimeout is the timeout to wait for the config to be loaded from an external source. ConfigLoadTimeout = 3 * time.Hour // ConfigLoadAttemptTimeout is the timeout for a single attempt to download config. ConfigLoadAttemptTimeout = 3 * time.Minute // BootTimeout is the timeout to run all services. BootTimeout = 70 * time.Minute // FailurePauseTimeout is the timeout for the sequencer failures which can be fixed by updating the machine config. FailurePauseTimeout = 35 * time.Minute // EtcdJoinTimeout is the timeout for etcd to join the existing cluster. // // BootTimeout should be higher than EtcdJoinTimeout. EtcdJoinTimeout = 30 * time.Minute // NodeReadyTimeout is the timeout to wait for the node to be ready (CNI to be running). // For bootstrap API, this includes time to run bootstrap. NodeReadyTimeout = BootTimeout // AnnotationCordonedKey is the annotation key for the nodes cordoned by Talos. AnnotationCordonedKey = "talos.dev/cordoned" // AnnotationCordonedValue is the annotation key for the nodes cordoned by Talos. AnnotationCordonedValue = "true" // AnnotationStaticPodSecretsVersion is the annotation key for the static pod secret version. AnnotationStaticPodSecretsVersion = "talos.dev/secrets-version" // AnnotationStaticPodConfigVersion is the annotation key for the static pod config version. AnnotationStaticPodConfigVersion = "talos.dev/config-version" // AnnotationStaticPodConfigFileVersion is the annotation key for the static pod configuration file version. AnnotationStaticPodConfigFileVersion = "talos.dev/config-file-version" // AnnotationOwnedLabels is the annotation key for the list of node labels owned by Talos. AnnotationOwnedLabels = "talos.dev/owned-labels" // AnnotationOwnedAnnotations is the annotation key for the list of node annotations owned by Talos. AnnotationOwnedAnnotations = "talos.dev/owned-annotations" // AnnotationOwnedTaints is the annotation key for the list of node taints owned by Talos. AnnotationOwnedTaints = "talos.dev/owned-taints" // K8sExtensionPrefix is the prefix for node labels/annotations listing extensions. K8sExtensionPrefix = "extensions.talos.dev/" // DefaultNTPServer is the NTP server to use if not configured explicitly. DefaultNTPServer = "time.cloudflare.com" // DefaultPrimaryResolver is the default primary DNS server. DefaultPrimaryResolver = "1.1.1.1" // DefaultSecondaryResolver is the default secondary DNS server. DefaultSecondaryResolver = "8.8.8.8" // DefaultClusterIDSize is the default size in bytes for the cluster ID token. DefaultClusterIDSize = 32 // DefaultClusterSecretSize is the default size in bytes for the cluster secret. DefaultClusterSecretSize = 32 // DefaultNodeIdentitySize is the default size in bytes for the node ID. DefaultNodeIdentitySize = 32 // NodeIdentityFilename is the filename to cache node identity across reboots. NodeIdentityFilename = "node-identity.yaml" // DefaultDiscoveryServiceEndpoint is the default endpoint for Talos discovery service. DefaultDiscoveryServiceEndpoint = "https://discovery.talos.dev/" // KubeSpanIdentityFilename is the filename to cache KubeSpan identity across reboots. KubeSpanIdentityFilename = "kubespan-identity.yaml" // KubeSpanDefaultPort is the default Wireguard listening port for incoming connections. KubeSpanDefaultPort = 51820 // KubeSpanDefaultRoutingTable is the default routing table for KubeSpan LAN targets. // // This specifies the routing table which will be used for Wireguard-available destinations. KubeSpanDefaultRoutingTable = 180 // KubeSpanDefaultFirewallMark is the default firewall mark to use for Wireguard encrypted egress packets. // // Normal Wireguard configurations will NOT use this firewall mark. KubeSpanDefaultFirewallMark = 0x20 // KubeSpanDefaultForceFirewallMark is the default firewall mark to use for packets destined to IPs serviced by KubeSpan. // // It is used to signal that matching packets should be forced into the Wireguard interface. KubeSpanDefaultForceFirewallMark = 0x40 // KubeSpanDefaultFirewallMask is the mask applied to the packet mark when matching and setting the mark. // // This mask signals the bits of the firewall mark used by KubeSpan. KubeSpanDefaultFirewallMask = KubeSpanDefaultFirewallMark | KubeSpanDefaultForceFirewallMark // KubeSpanDefaultPeerKeepalive is the interval at which Wireguard Peer Keepalives should be sent. KubeSpanDefaultPeerKeepalive = 25 * time.Second // KubeSpanDefaultRulePriority is the default priority for KubeSpan IPv4 routing rules. KubeSpanDefaultRulePriority = 32500 // LinuxReservedRulePriorityLocal is the priority for the local table lookup (ip rule show shows "from all lookup local"). // This priority is reserved by the Linux kernel and should not be used by user-defined rules. LinuxReservedRulePriorityLocal = 0 // LinuxReservedRulePriorityMain is the priority for the main table lookup (ip rule show shows "from all lookup main"). // This priority is reserved by the Linux kernel and should not be used by user-defined rules. LinuxReservedRulePriorityMain = 32766 // LinuxReservedRulePriorityDefault is the priority for the default table lookup (ip rule show shows "from all lookup default"). // This priority is reserved by the Linux kernel and should not be used by user-defined rules. LinuxReservedRulePriorityDefault = 32767 // NetworkSelfIPsAnnotation is the node annotation used to list the (comma-separated) IP addresses of the host, as discovered by Talos tooling. NetworkSelfIPsAnnotation = "networking.talos.dev/self-ips" // NetworkAPIServerPortAnnotation is the node annotation used to report the control plane API server port. NetworkAPIServerPortAnnotation = "networking.talos.dev/api-server-port" // ClusterNodeIDAnnotation is the node annotation used to represent node ID. ClusterNodeIDAnnotation = "cluster.talos.dev/node-id" // KubeSpanIPAnnotation is the node annotation to be used for indicating the Wireguard IP of the node. KubeSpanIPAnnotation = "networking.talos.dev/kubespan-ip" // KubeSpanPublicKeyAnnotation is the node annotation to be used for indicating the Wireguard Public Key of the node. KubeSpanPublicKeyAnnotation = "networking.talos.dev/kubespan-public-key" // KubeSpanAssignedPrefixesAnnotation is the node annotation use to list the (comma-separated) set of IP prefixes for which the annotated node should be responsible. KubeSpanAssignedPrefixesAnnotation = "networking.talos.dev/assigned-prefixes" // KubeSpanKnownEndpointsAnnotation is the node annotation used to list the (comma-separated) known-good Wireguard endpoints for the node, as seen by other peers. KubeSpanKnownEndpointsAnnotation = "networking.talos.dev/kubespan-endpoints" // KubeSpanExcludeAdvertisedNetworksAnnotation is the node annotation used to list the (comma-separated) set of subnets to be excluded from advertisement. KubeSpanExcludeAdvertisedNetworksAnnotation = "networking.talos.dev/kubespan-exclude-advertised-networks" // KubeSpanLinkName is the link name for the KubeSpan Wireguard interface. KubeSpanLinkName = "kubespan" // KubeSpanLinkMTU is the default link MTU size for the KubeSpan Wireguard interface. KubeSpanLinkMTU = 1420 // KubeSpanLinkMinimumMTU is the minimum link MTU size for the KubeSpan Wireguard interface. // // This is the minimum MTU size for the Wireguard interface with IPv6 enabled. // See: https://lore.kernel.org/wireguard/20190321033638.1ff82682@natsu/t/ KubeSpanLinkMinimumMTU = 1280 // UdevDir is the path to the udev directory. UdevDir = "/usr/lib/udev" // UdevRulesPath rules file path. UdevRulesPath = UdevDir + "/" + "rules.d/99-talos.rules" // UdevRulesLabel rules file SELinux label. UdevRulesLabel = "system_u:object_r:udev_rules_t:s0" // LoggingFormatJSONLines represents "JSON lines" logging format. LoggingFormatJSONLines = "json_lines" // SideroLinkName is the interface name for SideroLink. SideroLinkName = "siderolink" // SideroLinkTunnelName is the tunnel name for SideroLink in tunnel (Wireguard-over-GRPC) mode. SideroLinkTunnelName = "siderolinktun" // SideroLinkDefaultPeerKeepalive is the interval at which Wireguard Peer Keepalives should be sent. SideroLinkDefaultPeerKeepalive = 25 * time.Second // PlatformNetworkConfigFilename is the filename to cache platform network configuration reboots. PlatformNetworkConfigFilename = "platform-network.yaml" // ExtensionServiceConfigPath is the directory path which contains configuration files of extension services. // // See pkg/machinery/extensions/services for the file format. ExtensionServiceConfigPath = "/usr/local/etc/containers" // ExtensionServiceRootfsPath is the path to the extracted rootfs files of extension services. ExtensionServiceRootfsPath = "/usr/local/lib/containers" // ExtensionServiceUserConfigPath is the path to the user provided extension services config directory. ExtensionServiceUserConfigPath = SystemOverlaysPath + "/extensions" // DBusServiceSocketPath is the path to the D-Bus socket for the logind mock to connect to. DBusServiceSocketPath = SystemRunPath + "/dbus/service.socket" // DBusServiceSocketLabel is the SELinux label for the D-Bus socket for the logind mock to connect to. DBusServiceSocketLabel = "system_u:object_r:dbus_service_socket_t:s0" // DBusClientSocketPath is the path to the D-Bus socket for the kubelet to connect to. DBusClientSocketPath = "/run/dbus/system_bus_socket" // DBusClientSocketLabel is the SELinux label for the D-Bus socket for the kubelet to connect to. DBusClientSocketLabel = "system_u:object_r:dbus_client_socket_t:s0" // GoVersion is the version of Go compiler this release was built with. GoVersion = "go1.26.1" // KubernetesTalosAPIServiceName is the name of the Kubernetes service to access Talos API. KubernetesTalosAPIServiceName = "talos" // KubernetesTalosAPIServiceNamespace is the namespace of the Kubernetes service to access Talos API. KubernetesTalosAPIServiceNamespace = "default" // TalosDir is the default name of the Talos directory under user home. TalosDir = ".talos" // TalosconfigFilename is the file name of Talosconfig under TalosDir or under ServiceAccountMountPath inside a pod. TalosconfigFilename = "config" // KubernetesTalosProvider is the name of the Talos provider as a Kubernetes label. KubernetesTalosProvider = "talos.dev" // KubernetesInventoryNamespace is the namespace where Talos inventory objects are stored. KubernetesInventoryNamespace = "kube-system" // KubernetesBootstrapManifestsInventoryName is the name of the Talos bootstrap manifests inventory object. KubernetesBootstrapManifestsInventoryName = "talos-bootstrap-manifests-inventory" // KubernetesFieldManagerName is the field manager name used by Talos when applying manifests. KubernetesFieldManagerName = "talos" // ServiceAccountResourceGroup is the group name of the Talos service account CRD. ServiceAccountResourceGroup = "talos.dev" // ServiceAccountResourceVersion is the version of the Talos service account CRD. ServiceAccountResourceVersion = "v1alpha1" // ServiceAccountResourceKind is the kind name of the Talos service account CRD. ServiceAccountResourceKind = "ServiceAccount" // ServiceAccountResourceSingular is the singular name of the Talos service account CRD. ServiceAccountResourceSingular = "serviceaccount" // ServiceAccountResourceShortName is the short name of the service account CRD. ServiceAccountResourceShortName = "tsa" // ServiceAccountResourcePlural is the plural name of the service account CRD. ServiceAccountResourcePlural = ServiceAccountResourceSingular + "s" // ServiceAccountMountPath is the path of the directory in which the Talos service account secrets are mounted. ServiceAccountMountPath = "/var/run/secrets/talos.dev" // DefaultTrustedRelativeCAFile is the default path to the trusted CA file relative to the /etc. DefaultTrustedRelativeCAFile = "ssl/certs/ca-certificates.crt" // DefaultTrustedCAFile is the default path to the trusted CA file. DefaultTrustedCAFile = "/etc/" + DefaultTrustedRelativeCAFile // MachinedMaxProcs is the maximum number of GOMAXPROCS for machined. MachinedMaxProcs = 4 // ApidMaxProcs is the maximum number of GOMAXPROCS for apid. ApidMaxProcs = 2 // TrustdMaxProcs is the maximum number of GOMAXPROCS for trustd. TrustdMaxProcs = 2 // DashboardMaxProcs is the maximum number of GOMAXPROCS for dashboard. DashboardMaxProcs = 2 // APIAuthzRoleMetadataKey is the gRPC metadata key used to submit a role with os:impersonator. APIAuthzRoleMetadataKey = "talos-role" // KernelLogsTTY is the number of the TTY device (/dev/ttyN) to redirect Kernel logs to. KernelLogsTTY = 1 // DashboardTTY is the number of the TTY device (/dev/ttyN) for dashboard. DashboardTTY = 2 // FlannelVersion is the version of flannel to use. // // Note: while updating, make sure to copy flannel image from docker.io to ghcr.io: // crane cp docker.io/flannel/flannel:vX.Y.Z ghcr.io/siderolabs/flannel:vX.Y.Z // And sign the image using image-signer. // // renovate: datasource=github-releases depName=flannel-io/flannel FlannelVersion = "v0.28.1" // KubeNetworkPoliciesVersion is the version of kube-network-policies when network policies are enabled for flannel. // // renovate: datasource=docker depName=registry.k8s.io/networking/kube-network-policies KubeNetworkPoliciesVersion = "v1.0.0" // PlatformMetal is the name of the metal platform. PlatformMetal = "metal" // MetaValuesEnvVar is the name of the environment variable to store encoded meta values for the disk image (installer). MetaValuesEnvVar = "INSTALLER_META_BASE64" // MaintenanceServiceCommonName is the CN of the maintenance service server certificate. MaintenanceServiceCommonName = "maintenance-service.talos.dev" // GRPCMaxMessageSize is the maximum message size for Talos API. GRPCMaxMessageSize = 32 * 1024 * 1024 // DefaultKubePrismPort is the default port for the KubePrism loadbalancer. DefaultKubePrismPort = 7445 // KubePrismDialTimeout is the timeout for the KubePrism loadbalancer dialing an endpoint. KubePrismDialTimeout = 15 * time.Second // KubePrismKeepAlivePeriod is the TCP keepalive period for the KubePrism loadbalancer. KubePrismKeepAlivePeriod = 30 * time.Second // KubePrismTCPUserTimeout is the TCP user timeout for the KubePrism loadbalancer. KubePrismTCPUserTimeout = 30 * time.Second // KubePrismHealthCheckInterval is the interval between health checks for the KubePrism loadbalancer. KubePrismHealthCheckInterval = 20 * time.Second // KubePrismHealthCheckTimeout is the timeout for health checks for the KubePrism loadbalancer. KubePrismHealthCheckTimeout = 15 * time.Second // TalosAPIDefaultCertificateValidityDuration specifies default certificate duration for Talos API generated client certificates. TalosAPIDefaultCertificateValidityDuration = time.Hour * 24 * 365 // DefaultNfTablesTableName is the default name of the nftables table created by Talos. DefaultNfTablesTableName = "talos" // SystemResolvedPath is the path to the resolved dir. SystemResolvedPath = SystemPath + "/resolved" // PodResolvConfPath is the path to the pod resolv.conf file. PodResolvConfPath = SystemResolvedPath + "/resolv.conf" // SyslogListenSocketPath is the path to the syslog socket. SyslogListenSocketPath = "/dev/log" // ConsoleLogErrorSuppressThreshold is the threshold for suppressing console log errors. ConsoleLogErrorSuppressThreshold = 4 // HostDNSAddress is the address of the host DNS server. // // Note: 116 = 't' and 108 = 'l' in ASCII. HostDNSAddress = "169.254.116.108" // MetalAgentModeFlagPath is the path to the file indicating if the node is running in Metal Agent mode. MetalAgentModeFlagPath = "/usr/local/etc/is-metal-agent" // ImageCachePartitionLabel is the label for the image cache partition. ImageCachePartitionLabel = "IMAGECACHE" // ImageCacheISOMountPoint is the mount point for the image cache ISO. ImageCacheISOMountPoint = "/system/imagecache/iso" // ImageCacheDiskMountPoint is the mount point for the image cache partition. ImageCacheDiskMountPoint = "/system/imagecache/disk" // RegistrydListenAddress is the address to listen on for the registryd service. RegistrydListenAddress = "127.0.0.1:3172" // KubernetesInformerDefaultResyncPeriod is the default resync period for Kubernetes informers. KubernetesInformerDefaultResyncPeriod = 30 * time.Second // UserVolumeMountPoint is the path to the volume mount point for the user volumes. UserVolumeMountPoint = "/var/mnt" // LogMountPoint is the path to the logs mount point, and ID of the logs volume. LogMountPoint = "/var/log" // UserVolumePrefix is the prefix for the user volumes. UserVolumePrefix = "u-" // ExternalVolumePrefix is the prefix for the user volumes. ExternalVolumePrefix = "x-" // RawVolumePrefix is the prefix for the raw volumes. RawVolumePrefix = "r-" // ExistingVolumePrefix is the prefix for the existing volumes. ExistingVolumePrefix = "e-" // SwapVolumePrefix is the prefix for the swap volumes. SwapVolumePrefix = "s-" // PartitionLabelLength is the length of the partition label. // // See https://en.wikipedia.org/wiki/GUID_Partition_Table#Partition_entries_(LBA_2%E2%80%9333) PartitionLabelLength = 36 // SPDXPath is the path to the SBOM file(s). SPDXPath = "/usr/share/spdx" // ExtensionSPDXPath is the path to the SBOM file(s) provided by system extensions. ExtensionSPDXPath = "/usr/local/share/spdx" // EncryptionSaltFilename is the filename for the encryption salt file. EncryptionSaltFilename = "encryption-salt.yaml" // DiskEncryptionSaltSize is the size of the disk encryption salt in bytes. DiskEncryptionSaltSize = 32 // SideroV1KeysDirEnvVar is the environment variable that points to the directory containing user PGP keys for SideroV1 auth. SideroV1KeysDirEnvVar = "SIDEROV1_KEYS_DIR" // SideroV1KeysDir is the default directory containing user PGP keys for SideroV1 auth. SideroV1KeysDir = "keys" // ContainerMarkerFilePath is the path to the file added to container builds of Talos for platform detection. ContainerMarkerFilePath = "/usr/etc/in-container" // DefaultOOMTriggerExpression is the default CEL expression used to determine whether to trigger OOM. DefaultOOMTriggerExpression = `(multiply_qos_vectors(d_qos_memory_full_total, {System: 8.0, Podruntime: 4.0}) > 3000.0 && multiply_qos_vectors(qos_memory_full_avg10, {System: 1.0, Podruntime: 1.0}) > 5.0) || (memory_full_avg10 > 75.0 && time_since_trigger > duration("10s"))` // DefaultOOMCgroupRankingExpression is the default CEL expression used to rank cgroups for OOM killer. DefaultOOMCgroupRankingExpression = `memory_max.hasValue() ? 0.0 : {Besteffort: 1.0, Burstable: 0.5, Guaranteed: 0.0, Podruntime: 0.0, System: 0.0}[class] * double(memory_current.orValue(0u))` // OOMActionLogKeep is the number of OOM action log entries to keep in memory. OOMActionLogKeep = 50 // SDStubCmdlineExtraOEMVar is the name of the SMBIOS OEM variable that can be used to pass extra kernel command line parameters to systemd-stub. SDStubCmdlineExtraOEMVar = "io.systemd.stub.kernel-cmdline-extra" // LogRotateThreshold is the size (in bytes), upon exceeding which the log file should be rotated. LogRotateThreshold = 5 * 1024 * 1024 // LogFlushPeriod is the period for flushing in-memory log buffers to the filesystem. LogFlushPeriod = 15 * time.Second // ImageLabelVerified is the label key for the verified image label. ImageLabelVerified = "talos.dev/verified" ) // names of variable that can be substituted in the talos.config kernel parameter. const ( UUIDKey = "uuid" SerialNumberKey = "serial" HostnameKey = "hostname" MacKey = "mac" CodeKey = "code" ) // SELinuxLabeledPath is an object used to describe overlay mounts with SELinux labels applied on creation. type SELinuxLabeledPath struct { Path string Label string } // Overlays is the set of paths to create overlay mounts for. var Overlays = []SELinuxLabeledPath{ {"/etc/cni", CNISELinuxLabel}, {KubernetesConfigBaseDir, KubernetesConfigSELinuxLabel}, {"/usr/libexec/kubernetes", KubeletPluginsSELinuxLabel}, {"/opt", OptSELinuxLabel}, } // DefaultDroppedCapabilities is the default set of capabilities to drop. var DefaultDroppedCapabilities = map[string]struct{}{ "cap_sys_boot": {}, "cap_sys_module": {}, } // UdevdDroppedCapabilities is the set of capabilities to drop for udevd. var UdevdDroppedCapabilities = map[string]struct{}{ "cap_sys_boot": {}, } // ValidEffects is the set of valid taint effects. var ValidEffects = []string{ "NoSchedule", "PreferNoSchedule", "NoExecute", } // OSReleaseTemplate is the template for /etc/os-release. const OSReleaseTemplate = `NAME="%[1]s" ID=%[2]s VERSION_ID=%[3]s PRETTY_NAME="%[1]s (%[3]s)" HOME_URL="https://www.talos.dev/" BUG_REPORT_URL="https://github.com/siderolabs/talos/issues" VENDOR_NAME="Sidero Labs" VENDOR_URL="https://www.siderolabs.com/" ` ================================================ FILE: pkg/machinery/constants/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package constants import ( "strconv" "github.com/containerd/go-cni" ) const ( // PATH defines all locations where executables are stored. PATH = "/usr/bin:/usr/local/sbin:/usr/local/bin:" + cni.DefaultCNIDir // EnvPath is the environment variable to set PATH. EnvPath = "PATH=" + PATH // EnvPathWithBin is the environment variable to set PATH with /bin prepended. EnvPathWithBin = "PATH=/bin:" + PATH // EnvTcellMinimizeEnvironment is the environment variable to minimize tcell library memory usage (skips rune width calculation). EnvTcellMinimizeEnvironment = "TCELL_MINIMIZE=1" // EnvGRPCEnforccceALPNEnabled is the environment variable to disable gRPC ALPN enforcement. EnvGRPCEnforccceALPNEnabled = "GRPC_ENFORCE_ALPN_ENABLED=false" // EnvTerm is the environment variable to set terminal type. EnvTerm = "TERM=linux" // EnvGoraceHaltOnError is the environment variable to set GORACE to halt on error. EnvGoraceHaltOnError = "GORACE=halt_on_error=1" // EnvFIPS140ModeStrict is the environment variable to set Go crypto to FIPS 140 strict mode. EnvFIPS140ModeStrict = "GODEBUG=fips140=only" // EnvXDGRuntimeDir is a default value for XDG_RUNTIME_DIR for the services running on the host. // // See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html EnvXDGRuntimeDir = "XDG_RUNTIME_DIR=/run" ) // EnvApidGomemlimit is the environment variable to set GOMEMLIMIT for apid process. func EnvApidGomemlimit() string { return "GOMEMLIMIT=" + strconv.Itoa(CgroupApidMaxMemory/5*4) } // EnvDashboardGomemlimit is the environment variable to set GOMEMLIMIT for dashboard process. func EnvDashboardGomemlimit() string { return "GOMEMLIMIT=" + strconv.Itoa(CgroupDashboardMaxMemory/5*4) } // EnvTrustdGomemlimit is the environment variable to set GOMEMLIMIT for trustd process. func EnvTrustdGomemlimit() string { return "GOMEMLIMIT=" + strconv.Itoa(CgroupTrustdMaxMemory/5*4) } ================================================ FILE: pkg/machinery/extensions/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "os" "go.yaml.in/yaml/v4" ) //go:generate go tool github.com/siderolabs/deep-copy -type Layer -header-file ../../../hack/boilerplate.txt -o deep_copy.generated.go . // Config specifies Talos installer extensions configuration. type Config struct { Layers []*Layer `yaml:"layers"` } // Layer defines overlay mount layer. // //gotagsrewrite:gen type Layer struct { Image string `yaml:"image" protobuf:"1"` Metadata Metadata `yaml:"metadata" protobuf:"2"` } // Read extensions config from a file. func (cfg *Config) Read(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() //nolint:errcheck return yaml.NewDecoder(f).Decode(cfg) } // Write extensions config to a file. func (cfg *Config) Write(path string) error { f, err := os.Create(path) if err != nil { return err } defer f.Close() //nolint:errcheck return yaml.NewEncoder(f).Encode(cfg) } ================================================ FILE: pkg/machinery/extensions/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type Layer -header-file ../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package extensions // DeepCopy generates a deep copy of Layer. func (o Layer) DeepCopy() Layer { var cp Layer = o return cp } ================================================ FILE: pkg/machinery/extensions/extensions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package extensions contains Talos extensions specific API. package extensions import "path/filepath" // AllowedPaths lists paths allowed in the extension images. var AllowedPaths = []string{ "/etc/cri/conf.d", "/usr/lib/firmware", "/usr/lib/modules", // The glibc loader is required by glibc dynamic binaries. // It will be accessible via /lib64/ld-linux-x86-64.so.2 on x86_64 // and /lib/ld-linux-aarch64.so.1 on aarch64. "/usr/lib/ld-linux-x86-64.so.2", "/usr/lib/ld-linux-aarch64.so.1", // /sbin/ldconfig is required by the nvidia container toolkit. "/usr/bin/ldconfig", "/usr/lib/udev/rules.d", "/usr/local", // glvnd, egl and vulkan are needed for OpenGL/Vulkan. "/usr/share/glvnd", "/usr/share/egl", "/etc/vulkan", } // Extension represents unpacked extension in the filesystem. type Extension struct { Manifest Manifest directory string rootfsPath string } // RootfsPath returns the path to the rootfs directory. func (ext *Extension) RootfsPath() string { return ext.rootfsPath } // Directory returns the directory name of the extension. func (ext *Extension) Directory() string { return ext.directory } // New creates a new extension from the rootfs path, directory name and manifest. func New(rootfsPath, directory string, manifest Manifest) *Extension { extension := &Extension{ Manifest: manifest, rootfsPath: rootfsPath, directory: directory, } if extension.directory == "" { extension.directory = filepath.Base(rootfsPath) } return extension } ================================================ FILE: pkg/machinery/extensions/extensions_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions_test import ( "path/filepath" "testing" "github.com/blang/semver/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/extensions" ) func TestLoadValidate(t *testing.T) { ext, err := extensions.Load("testdata/good/extension1") require.NoError(t, err) assert.Equal(t, "gvisor", ext.Manifest.Metadata.Name) version, err := semver.Parse("1.0.0") require.NoError(t, err) assert.NoError(t, ext.Validate( extensions.WithValidateConstraints(), extensions.WithValidateContents(), extensions.WithTalosVersion(&version), )) } func TestValidateFailures(t *testing.T) { version, err := semver.Parse("1.0.0") require.NoError(t, err) for _, tt := range []struct { name string loadError string validateError string }{ { name: "wrongfiles", loadError: "unexpected file \"a\"", }, { name: "emptymanifest", loadError: "unsupported manifest version: \"\"", }, { name: "norootfs", loadError: "extension rootfs is missing", }, { name: "badpaths", validateError: "path \"/boot/vmlinuz\" is not allowed in extensions", }, { name: "usrmerge", validateError: "path \"/usr/lib64/a.so\" is not allowed in extensions", }, } { t.Run(tt.name, func(t *testing.T) { ext, err := extensions.Load(filepath.Join("testdata/bad", tt.name)) if tt.loadError == "" { require.NoError(t, err) } else { assert.EqualError(t, err, tt.loadError) } if err == nil { err = ext.Validate( extensions.WithValidateConstraints(), extensions.WithValidateContents(), extensions.WithTalosVersion(&version), ) assert.EqualError(t, err, tt.validateError) } }) } } ================================================ FILE: pkg/machinery/extensions/load.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "errors" "fmt" "os" "path/filepath" "go.yaml.in/yaml/v4" ) // Load extension from the filesystem. // // This performs initial validation of the extension file structure. func Load(path string) (*Extension, error) { extension := &Extension{ directory: filepath.Base(path), } items, err := os.ReadDir(path) if err != nil { return nil, err } for _, item := range items { switch item.Name() { case "manifest.yaml": if err = extension.loadManifest(filepath.Join(path, item.Name())); err != nil { return nil, err } case "rootfs": extension.rootfsPath = filepath.Join(path, item.Name()) default: return nil, fmt.Errorf("unexpected file %q", item.Name()) } } var zeroManifest Manifest if extension.Manifest == zeroManifest { return nil, errors.New("extension manifest is missing") } if extension.rootfsPath == "" { return nil, errors.New("extension rootfs is missing") } return extension, nil } func (ext *Extension) loadManifest(path string) error { f, err := os.Open(path) if err != nil { return err } defer f.Close() //nolint:errcheck if err = yaml.NewDecoder(f).Decode(&ext.Manifest); err != nil { return err } if ext.Manifest.Version != "v1alpha1" { return fmt.Errorf("unsupported manifest version: %q", ext.Manifest.Version) } return nil } ================================================ FILE: pkg/machinery/extensions/metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions // Manifest is the structure of the extension manifest.yaml file. type Manifest struct { Version string `yaml:"version"` Metadata Metadata `yaml:"metadata"` } // Metadata describes base extension metadata. // //gotagsrewrite:gen type Metadata struct { Name string `yaml:"name" protobuf:"1"` Version string `yaml:"version" protobuf:"2"` Author string `yaml:"author" protobuf:"3"` Description string `yaml:"description" protobuf:"4"` Compatibility Compatibility `yaml:"compatibility" protobuf:"5"` ExtraInfo string `yaml:"extraInfo,omitempty" protobuf:"6"` } // Compatibility describes extension compatibility. // //gotagsrewrite:gen type Compatibility struct { Talos Constraint `yaml:"talos" protobuf:"1"` } // Constraint describes compatibility constraint. // //gotagsrewrite:gen type Constraint struct { Version string `yaml:"version" protobuf:"1"` } ================================================ FILE: pkg/machinery/extensions/services/restart_kind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services //go:generate go tool github.com/dmarkham/enumer -type=RestartKind -linecomment -text // RestartKind specifies how the service should be restarted. type RestartKind int // RestartKind constants. const ( RestartAlways RestartKind = 1 // always RestartNever RestartKind = 2 // never RestartUntilSuccess RestartKind = 3 // untilSuccess ) ================================================ FILE: pkg/machinery/extensions/services/restartkind_enumer.go ================================================ // Code generated by "enumer -type=RestartKind -linecomment -text"; DO NOT EDIT. package services import ( "fmt" "strings" ) const _RestartKindName = "alwaysneveruntilSuccess" var _RestartKindIndex = [...]uint8{0, 6, 11, 23} const _RestartKindLowerName = "alwaysneveruntilsuccess" func (i RestartKind) String() string { i -= 1 if i < 0 || i >= RestartKind(len(_RestartKindIndex)-1) { return fmt.Sprintf("RestartKind(%d)", i+1) } return _RestartKindName[_RestartKindIndex[i]:_RestartKindIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RestartKindNoOp() { var x [1]struct{} _ = x[RestartAlways-(1)] _ = x[RestartNever-(2)] _ = x[RestartUntilSuccess-(3)] } var _RestartKindValues = []RestartKind{RestartAlways, RestartNever, RestartUntilSuccess} var _RestartKindNameToValueMap = map[string]RestartKind{ _RestartKindName[0:6]: RestartAlways, _RestartKindLowerName[0:6]: RestartAlways, _RestartKindName[6:11]: RestartNever, _RestartKindLowerName[6:11]: RestartNever, _RestartKindName[11:23]: RestartUntilSuccess, _RestartKindLowerName[11:23]: RestartUntilSuccess, } var _RestartKindNames = []string{ _RestartKindName[0:6], _RestartKindName[6:11], _RestartKindName[11:23], } // RestartKindString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RestartKindString(s string) (RestartKind, error) { if val, ok := _RestartKindNameToValueMap[s]; ok { return val, nil } if val, ok := _RestartKindNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RestartKind values", s) } // RestartKindValues returns all values of the enum func RestartKindValues() []RestartKind { return _RestartKindValues } // RestartKindStrings returns a slice of all String values of the enum func RestartKindStrings() []string { strs := make([]string, len(_RestartKindNames)) copy(strs, _RestartKindNames) return strs } // IsARestartKind returns "true" if the value is listed in the enum definition. "false" otherwise func (i RestartKind) IsARestartKind() bool { for _, v := range _RestartKindValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RestartKind func (i RestartKind) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RestartKind func (i *RestartKind) UnmarshalText(text []byte) error { var err error *i, err = RestartKindString(string(text)) return err } ================================================ FILE: pkg/machinery/extensions/services/services.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package services contains definitions for non-system services. package services import ( "errors" "fmt" "path/filepath" "regexp" "github.com/hashicorp/go-multierror" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // Spec is represents non-system service definition. type Spec struct { // Name of the service to run, will be prefixed with `ext-` when registered as Talos service. // // Valid: [-_a-z0-9]+ Name string `yaml:"name"` // Container to run. // // Container rootfs should be extracted to the /usr/local/lib/containers/. Container Container `yaml:"container"` // Service dependencies. Depends []Dependency `yaml:"depends"` // Restart configuration. Restart RestartKind `yaml:"restart"` // LogToConsole enables sending service logs to the console. LogToConsole bool `yaml:"logToConsole"` } // Container specifies service container to run. type Container struct { // Entrypoint for the service, relative to the container rootfs. Entrypoint string `yaml:"entrypoint"` // Environment variables for the service. Environment []string `yaml:"environment"` // EnvironmentFile to load environment vars before running the service. EnvironmentFile string `yaml:"environmentFile"` // Args to pass to the entrypoint. Args []string `yaml:"args"` // Volume mounts. Mounts []specs.Mount `yaml:"mounts"` // Security options. Security Security `yaml:"security"` } // Security options for containers. type Security struct { // WriteableSysfs makes the '/sys' path writeable in the container namespace if set to true. WriteableSysfs bool `yaml:"writeableSysfs"` // MaskedPaths is a list of paths in the container namespace that should not be readable. MaskedPaths []string `yaml:"maskedPaths"` // ReadonlyPaths is a list of paths in the container namespace that should be read-only. ReadonlyPaths []string `yaml:"readonlyPaths"` // WriteableRootfs WriteableRootfs bool `yaml:"writeableRootfs"` // RootfsPropagation is the propagation mode for the rootfs mount. RootfsPropagation string `yaml:"rootfsPropagation,omitempty"` } // Dependency describes a service Dependency. // // Only a single dependency out of the list might be specified. type Dependency struct { // Depends on a service being running and healthy (if health checks are available). Service string `yaml:"service,omitempty"` // Depends on file/directory existence. Path string `yaml:"path,omitempty"` // Network readiness checks. // // Valid options are nethelpers.Status string values. Network []nethelpers.Status `yaml:"network,omitempty"` // Time sync check. Time bool `yaml:"time,omitempty"` // Depends on configuration files to be present. Configuration bool `yaml:"configuration,omitempty"` } var nameRe = regexp.MustCompile(`^[-_a-z0-9]{1,}$`) // Validate the service spec. func (spec *Spec) Validate() error { var multiErr *multierror.Error if !nameRe.MatchString(spec.Name) { multiErr = multierror.Append(multiErr, fmt.Errorf("name %q is invalid", spec.Name)) } if !spec.Restart.IsARestartKind() { multiErr = multierror.Append(multiErr, fmt.Errorf("restart kind is invalid: %s", spec.Restart)) } multiErr = multierror.Append(multiErr, spec.Container.Validate()) for _, dep := range spec.Depends { multiErr = multierror.Append(multiErr, dep.Validate()) } return multiErr.ErrorOrNil() } // Validate the container spec. func (ctr *Container) Validate() error { var multiErr *multierror.Error if ctr.Entrypoint == "" { multiErr = multierror.Append(multiErr, errors.New("container endpoint can't be empty")) } return multiErr.ErrorOrNil() } // Validate the dependency spec. // //nolint:gocyclo func (dep *Dependency) Validate() error { var multiErr *multierror.Error nonZeroDeps := 0 if dep.Service != "" { nonZeroDeps++ } if dep.Path != "" { nonZeroDeps++ if !filepath.IsAbs(dep.Path) { multiErr = multierror.Append(multiErr, fmt.Errorf("path is not absolute: %q", dep.Path)) } } if len(dep.Network) > 0 { nonZeroDeps++ for _, st := range dep.Network { if !st.IsAStatus() { multiErr = multierror.Append(multiErr, fmt.Errorf("invalid network dependency: %s", st)) } } } if dep.Time { nonZeroDeps++ } if dep.Configuration { nonZeroDeps++ } if nonZeroDeps == 0 { multiErr = multierror.Append(multiErr, errors.New("no dependency specified")) } if nonZeroDeps > 1 { multiErr = multierror.Append(multiErr, errors.New("more than a single dependency is set")) } return multiErr.ErrorOrNil() } ================================================ FILE: pkg/machinery/extensions/services/services_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package services_test import ( _ "embed" "testing" "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/extensions/services" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:embed "testdata/hello.yaml" var helloYAML []byte func TestUnmarshal(t *testing.T) { var spec services.Spec require.NoError(t, yaml.Unmarshal(helloYAML, &spec)) assert.Equal(t, services.Spec{ Name: "hello", Container: services.Container{ Entrypoint: "hello-world", Args: []string{"--development", "--log=debug"}, Mounts: []specs.Mount{ { Destination: "/var/lib/example", Type: "bind", Source: "/var/lib/example", Options: []string{"rbind", "ro"}, }, }, }, Depends: []services.Dependency{ { Service: "cri", }, { Path: "/system/run/machined/machined.sock", }, { Network: []nethelpers.Status{nethelpers.StatusAddresses}, }, }, Restart: services.RestartNever, }, spec) assert.NoError(t, spec.Validate()) } func TestValidate(t *testing.T) { for _, tt := range []struct { name string spec services.Spec expectedError string }{ { name: "empty", spec: services.Spec{}, expectedError: "3 errors occurred:\n\t* name \"\" is invalid\n\t* restart kind is invalid: RestartKind(0)\n\t* container endpoint can't be empty\n\n", }, { name: "invalid name", spec: services.Spec{ Name: "FOO", Container: services.Container{ Entrypoint: "foo", }, Restart: services.RestartAlways, }, expectedError: "1 error occurred:\n\t* name \"FOO\" is invalid\n\n", }, { name: "invalid deps", spec: services.Spec{ Name: "foo", Container: services.Container{ Entrypoint: "foo", }, Depends: []services.Dependency{ {}, { Path: "./somefile", }, { Network: []nethelpers.Status{ 0, }, }, { Network: []nethelpers.Status{ nethelpers.StatusAddresses, }, Path: "/foo", }, }, Restart: services.RestartAlways, }, expectedError: "4 errors occurred:\n\t* no dependency specified\n\t* path is not absolute: \"./somefile\"\n\t* invalid network dependency: Status(0)\n\t* more than a single dependency is set\n\n", }, } { t.Run(tt.name, func(t *testing.T) { err := tt.spec.Validate() assert.EqualError(t, err, tt.expectedError) }) } } ================================================ FILE: pkg/machinery/extensions/services/testdata/hello.yaml ================================================ name: hello container: entrypoint: hello-world args: - --development - --log=debug mounts: - source: /var/lib/example destination: /var/lib/example type: bind options: - rbind - ro depends: - service: cri - path: /system/run/machined/machined.sock - network: - addresses restart: never ================================================ FILE: pkg/machinery/extensions/testdata/bad/badpaths/manifest.yaml ================================================ version: v1alpha1 metadata: name: gvisor version: 20220117.0-v1.0.0 author: Andrew Rynhard description: > This system extension provides gVisor using containerd's runtime handler. compatibility: talos: version: ">= v1.0.0" ================================================ FILE: pkg/machinery/extensions/testdata/bad/badpaths/rootfs/boot/vmlinuz ================================================ ================================================ FILE: pkg/machinery/extensions/testdata/bad/emptymanifest/manifest.yaml ================================================ --- ================================================ FILE: pkg/machinery/extensions/testdata/bad/norootfs/manifest.yaml ================================================ version: v1alpha1 metadata: name: gvisor version: 20220117.0-v1.0.0 author: Andrew Rynhard description: > This system extension provides gVisor using containerd's runtime handler. compatibility: talos: version: ">= v1.0.0" ================================================ FILE: pkg/machinery/extensions/testdata/bad/usrmerge/manifest.yaml ================================================ version: v1alpha1 metadata: name: gvisor version: 20220117.0-v1.0.0 author: Andrew Rynhard description: > This system extension provides gVisor using containerd's runtime handler. compatibility: talos: version: ">= v1.0.0" ================================================ FILE: pkg/machinery/extensions/testdata/bad/wrongfiles/a ================================================ ================================================ FILE: pkg/machinery/extensions/testdata/good/extension1/manifest.yaml ================================================ version: v1alpha1 metadata: name: gvisor version: 20220117.0-v1.0.0 author: Andrew Rynhard description: > This system extension provides gVisor using containerd's runtime handler. compatibility: talos: version: ">= v1.0.0" ================================================ FILE: pkg/machinery/extensions/testdata/good/extension1/rootfs/usr/lib/firmware/amd/cpu ================================================ ================================================ FILE: pkg/machinery/extensions/testdata/good/extension1/rootfs/usr/lib/ld-linux-x86-64.so.2 ================================================ ================================================ FILE: pkg/machinery/extensions/validate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package extensions import ( "fmt" "io/fs" "os" "path/filepath" "strings" "github.com/blang/semver/v4" "github.com/siderolabs/talos/pkg/machinery/version" ) // ValidationOptions are used to configure the validation process. type ValidationOptions struct { // ValidateContstraints enables validation of the extension constraints. ValidateContstraints bool // ValidateContents enables validation of the extension contents. ValidateContents bool // TalosVersion is the version of Talos to validate against. TalosVersion *semver.Version } // WithValidateConstraints enables validation of the extension constraints. func WithValidateConstraints() ValidationOption { return func(o *ValidationOptions) error { o.ValidateContstraints = true return nil } } // WithValidateContents enables validation of the extension contents. func WithValidateContents() ValidationOption { return func(o *ValidationOptions) error { o.ValidateContents = true return nil } } // WithTalosVersion sets the Talos version to validate against. func WithTalosVersion(version *semver.Version) ValidationOption { return func(o *ValidationOptions) error { o.TalosVersion = version return nil } } // ValidationOption is a function that configures the validation options. type ValidationOption func(*ValidationOptions) error // Validate the extension: compatibility, contents, etc. func (ext *Extension) Validate(opts ...ValidationOption) error { validationOptions := &ValidationOptions{} for _, opt := range opts { if err := opt(validationOptions); err != nil { panic(err) } } if validationOptions.TalosVersion == nil { version, err := semver.ParseTolerant(version.Tag) if err != nil { return err } validationOptions.TalosVersion = &version } if validationOptions.ValidateContstraints { if err := ext.validateConstraints(*validationOptions.TalosVersion); err != nil { return err } } if validationOptions.ValidateContents { return ext.validateContents() } return nil } func (ext *Extension) validateConstraints(talosVersion semver.Version) error { constraint := ext.Manifest.Metadata.Compatibility.Talos.Version if constraint != "" { versionConstraint, err := semver.ParseRange(trim(constraint)) if err != nil { return fmt.Errorf("error parsing Talos version constraint: %w", err) } if !versionConstraint(coreVersion(talosVersion)) { return fmt.Errorf("version constraint %s can't be satisfied with Talos version %s", constraint, talosVersion) } } return nil } // trim removes 'v' symbol anywhere in string if it's located before the number. func trim(constraint string) string { for i := 0; i < len(constraint); i++ { if constraint[i] == 'v' && i+1 < len(constraint) && constraint[i+1] >= '0' && constraint[i+1] <= '9' { constraint = constraint[:i] + constraint[i+1:] } } return constraint } func coreVersion(talosVersion semver.Version) semver.Version { return semver.Version{ Major: talosVersion.Major, Minor: talosVersion.Minor, Patch: talosVersion.Patch, } } //nolint:gocyclo func (ext *Extension) validateContents() error { return filepath.WalkDir(ext.rootfsPath, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } itemPath, err := filepath.Rel(ext.rootfsPath, path) if err != nil { return err } itemPath = filepath.Join("/", itemPath) // check for -------w- if d.Type().Perm()&0o002 > 0 { return fmt.Errorf("world-writeable files are not allowed: %q", itemPath) } // no special files if !d.IsDir() && !d.Type().IsRegular() && d.Type().Type() != os.ModeSymlink { return fmt.Errorf("special files are not allowed: %q", itemPath) } // regular file: check for file path being whitelisted if !d.IsDir() { allowed := false for _, allowedPath := range AllowedPaths { if strings.HasPrefix(itemPath, allowedPath) { _, err = filepath.Rel(allowedPath, itemPath) if err == nil { allowed = true break } } } if !allowed { return fmt.Errorf("path %q is not allowed in extensions", itemPath) } } return nil }) } ================================================ FILE: pkg/machinery/fipsmode/fipsmode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package fipsmode provides a way to check if the system is running in FIPS mode. package fipsmode import ( "crypto/fips140" "crypto/sha1" "sync" ) // Enabled checks if the system is running in FIPS mode. func Enabled() bool { return fips140.Enabled() } // Strict checks if the strict FIPS mode is enabled. // // Go doesn't provide a simple way to check for strict FIPS mode, so we // use a side-effect of SHA-1 to fail. var Strict = sync.OnceValue(func() bool { if !Enabled() { return false } _, err := sha1.New().Write(nil) strict := err != nil return strict }) ================================================ FILE: pkg/machinery/fipsmode/fipsmode_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package fipsmode_test import ( "crypto/fips140" "os" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/fipsmode" ) func TestEnabled(t *testing.T) { t.Parallel() assert.Equal(t, fips140.Enabled(), fipsmode.Enabled()) t.Logf("fips140.Enabled() = %v", fips140.Enabled()) } func TestStrict(t *testing.T) { t.Parallel() // guess strict mode from the environment godebug := os.Getenv("GODEBUG") shouldbeStrict := strings.Contains(godebug, "fips140=only") assert.Equal(t, shouldbeStrict, fipsmode.Strict()) t.Logf("fipsmode.Strict() = %v", fipsmode.Strict()) } ================================================ FILE: pkg/machinery/formatters/formatters.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package formatters contains the API response formatters used in the CLI output. package formatters import ( "context" "fmt" "io" "math" "slices" "strings" "text/tabwriter" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/emicklei/dot" "google.golang.org/grpc/metadata" "google.golang.org/grpc/peer" "github.com/siderolabs/talos/pkg/machinery/api/inspect" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" ) // RenderMounts renders mounts output. func RenderMounts(resp *machine.MountsResponse, output io.Writer, remotePeer *peer.Peer) error { w := tabwriter.NewWriter(output, 0, 0, 3, ' ', 0) parts := []string{"FILESYSTEM", "SIZE(GB)", "USED(GB)", "AVAILABLE(GB)", "PERCENT USED", "MOUNTED ON"} var defaultNode string if remotePeer != nil { parts = append([]string{"NODE"}, parts...) defaultNode = client.AddrFromPeer(remotePeer) } fmt.Fprintln(w, strings.Join(parts, "\t")) for _, msg := range resp.Messages { for _, r := range msg.Stats { percentAvailable := 100.0 - 100.0*(float64(r.Available)/float64(r.Size)) if math.IsNaN(percentAvailable) { continue } node := defaultNode if msg.Metadata != nil { node = msg.Metadata.Hostname } format := "%s\t%.02f\t%.02f\t%.02f\t%.02f%%\t%s\n" args := []any{r.Filesystem, float64(r.Size) * 1e-9, float64(r.Size-r.Available) * 1e-9, float64(r.Available) * 1e-9, percentAvailable, r.MountedOn} if defaultNode != "" { format = "%s\t" + format args = slices.Insert(args, 0, any(node)) } fmt.Fprintf(w, format, args...) } } return w.Flush() } // RenderGraph renders inspect controller runtime graph. // //nolint:gocyclo,cyclop func RenderGraph(ctx context.Context, c *client.Client, resp *inspect.ControllerRuntimeDependenciesResponse, output io.Writer, withResources bool) error { graph := dot.NewGraph(dot.Directed) resourceTypeID := func(edge *inspect.ControllerDependencyEdge) string { return edge.GetResourceType() } resourceID := func(r resource.Resource) string { return fmt.Sprintf("%s/%s/%s", r.Metadata().Namespace(), r.Metadata().Type(), r.Metadata().ID()) } if withResources { resources := map[string][]resource.Resource{} md, _ := metadata.FromOutgoingContext(ctx) nodes := md["nodes"] nodeCtx := ctx if len(nodes) > 0 { nodeCtx = client.WithNode(ctx, nodes[0]) } for _, msg := range resp.GetMessages() { for _, edge := range msg.GetEdges() { resourceType := resourceTypeID(edge) if _, ok := resources[resourceType]; ok { continue } namespace := edge.GetResourceNamespace() rd, err := c.ResolveResourceKind(nodeCtx, &namespace, edge.GetResourceType()) if err != nil { return err } items, err := c.COSI.List(nodeCtx, resource.NewMetadata(namespace, rd.TypedSpec().Type, "", resource.VersionUndefined)) if err != nil { // ignore errors here continue } resources[resourceType] = append(resources[resourceType], items.Items...) } } for _, msg := range resp.GetMessages() { for _, edge := range msg.GetEdges() { graph.Node(edge.ControllerName).Box() } } for resourceType, resourceList := range resources { cluster := graph.Subgraph(resourceType, dot.ClusterOption{}) for _, resource := range resourceList { cluster.Node(resourceID(resource)). Attr("shape", "note"). Attr("fillcolor", "azure2"). Attr("style", "filled") } } for _, msg := range resp.GetMessages() { for _, edge := range msg.GetEdges() { for _, resource := range resources[resourceTypeID(edge)] { if edge.GetResourceId() != "" && resource.Metadata().ID() != edge.GetResourceId() { continue } if (edge.GetEdgeType() == inspect.DependencyEdgeType_OUTPUT_EXCLUSIVE || edge.GetEdgeType() == inspect.DependencyEdgeType_OUTPUT_SHARED) && edge.GetControllerName() != resource.Metadata().Owner() { continue } switch edge.GetEdgeType() { case inspect.DependencyEdgeType_OUTPUT_EXCLUSIVE: graph.Edge(graph.Node(edge.ControllerName), graph.Subgraph(resourceTypeID(edge)).Node(resourceID(resource))).Solid() case inspect.DependencyEdgeType_OUTPUT_SHARED: graph.Edge(graph.Node(edge.ControllerName), graph.Subgraph(resourceTypeID(edge)).Node(resourceID(resource))).Solid() case inspect.DependencyEdgeType_INPUT_STRONG: graph.Edge(graph.Subgraph(resourceTypeID(edge)).Node(resourceID(resource)), graph.Node(edge.ControllerName)).Solid() case inspect.DependencyEdgeType_INPUT_WEAK: graph.Edge(graph.Subgraph(resourceTypeID(edge)).Node(resourceID(resource)), graph.Node(edge.ControllerName)).Dotted() case inspect.DependencyEdgeType_INPUT_DESTROY_READY: // don't show the DestroyReady inputs to reduce the visual clutter } } } } } else { for _, msg := range resp.GetMessages() { for _, edge := range msg.GetEdges() { graph.Node(edge.ControllerName).Box() graph.Node(resourceTypeID(edge)). Attr("shape", "note"). Attr("fillcolor", "azure2"). Attr("style", "filled") } } for _, msg := range resp.GetMessages() { for _, edge := range msg.GetEdges() { var idLabels []string if edge.GetResourceId() != "" { idLabels = append(idLabels, edge.GetResourceId()) } switch edge.GetEdgeType() { case inspect.DependencyEdgeType_OUTPUT_EXCLUSIVE: graph.Edge(graph.Node(edge.ControllerName), graph.Node(resourceTypeID(edge))).Bold() case inspect.DependencyEdgeType_OUTPUT_SHARED: graph.Edge(graph.Node(edge.ControllerName), graph.Node(resourceTypeID(edge))).Solid() case inspect.DependencyEdgeType_INPUT_STRONG: graph.Edge(graph.Node(resourceTypeID(edge)), graph.Node(edge.ControllerName), idLabels...).Solid() case inspect.DependencyEdgeType_INPUT_WEAK: graph.Edge(graph.Node(resourceTypeID(edge)), graph.Node(edge.ControllerName), idLabels...).Dotted() case inspect.DependencyEdgeType_INPUT_DESTROY_READY: // don't show the DestroyReady inputs to reduce the visual clutter } } } } graph.Write(output) return nil } // RenderServicesInfo writes human readable service information to the io.Writer. func RenderServicesInfo(services []client.ServiceInfo, output io.Writer, defaultNode string, withNodeInfo bool) error { w := tabwriter.NewWriter(output, 0, 0, 3, ' ', 0) node := defaultNode for _, s := range services { if s.Metadata != nil { node = s.Metadata.Hostname } if withNodeInfo { fmt.Fprintf(w, "NODE\t%s\n", node) } svc := ServiceInfoWrapper{s.Service} fmt.Fprintf(w, "ID\t%s\n", svc.Id) fmt.Fprintf(w, "STATE\t%s\n", svc.State) fmt.Fprintf(w, "HEALTH\t%s\n", svc.HealthStatus()) if svc.Health.LastMessage != "" { fmt.Fprintf(w, "LAST HEALTH MESSAGE\t%s\n", svc.Health.LastMessage) } label := "EVENTS" for i := range svc.Events.Events { event := svc.Events.Events[len(svc.Events.Events)-1-i] ts := event.Ts.AsTime() fmt.Fprintf(w, "%s\t[%s]: %s (%s ago)\n", label, event.State, event.Msg, time.Since(ts).Round(time.Second)) label = "" } } return w.Flush() } // ServiceInfoWrapper helper that allows generating rich service information. type ServiceInfoWrapper struct { *machine.ServiceInfo } // LastUpdated derive last updated time from events stream. func (svc ServiceInfoWrapper) LastUpdated() string { if len(svc.Events.Events) == 0 { return "" } ts := svc.Events.Events[len(svc.Events.Events)-1].Ts.AsTime() return time.Since(ts).Round(time.Second).String() } // LastEvent return last service event. func (svc ServiceInfoWrapper) LastEvent() string { if len(svc.Events.Events) == 0 { return "" } return svc.Events.Events[len(svc.Events.Events)-1].Msg } // HealthStatus service health status. func (svc ServiceInfoWrapper) HealthStatus() string { if svc.Health.Unknown { return "?" } if svc.Health.Healthy { return "OK" } return "Fail" } ================================================ FILE: pkg/machinery/gendata/data/artifacts ================================================ _out ================================================ FILE: pkg/machinery/gendata/data/name ================================================ Talos ================================================ FILE: pkg/machinery/gendata/data/pkgs ================================================ v1.13.0-beta.0 ================================================ FILE: pkg/machinery/gendata/data/registry ================================================ ghcr.io ================================================ FILE: pkg/machinery/gendata/data/sha ================================================ undefined ================================================ FILE: pkg/machinery/gendata/data/tag ================================================ v1.13.0-alpha.2 ================================================ FILE: pkg/machinery/gendata/data/tools ================================================ v1.13.0-beta.0 ================================================ FILE: pkg/machinery/gendata/data/username ================================================ siderolabs ================================================ FILE: pkg/machinery/gendata/gendata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package gendata contains that a variables generated from Makefile script. It's a proper alternative to using // -ldflags '-X ...'. package gendata import ( _ "embed" ) var ( // VersionName declares variable used by version package. //go:embed data/name VersionName string // VersionTag declares variable used by version package. //go:embed data/tag VersionTag string // VersionSHA declares variable used by version package. //go:embed data/sha VersionSHA string // VersionPkgs declares variable used by version package. //go:embed data/pkgs VersionPkgs string // ImagesUsername declares variable used by images package. //go:embed data/username ImagesUsername string // ImagesRegistry declares variable used by images package. //go:embed data/registry ImagesRegistry string // ArtifactsPath declares variable used by helpers package. //go:embed data/artifacts ArtifactsPath string ) ================================================ FILE: pkg/machinery/go.mod ================================================ module github.com/siderolabs/talos/pkg/machinery go 1.26.1 // forked ethtool introduces missing APIs replace github.com/mdlayher/ethtool => github.com/siderolabs/ethtool v0.4.0-sidero require ( github.com/blang/semver/v4 v4.0.0 github.com/containerd/go-cni v1.1.13 github.com/cosi-project/runtime v1.14.0 github.com/dustin/go-humanize v1.0.1 github.com/emicklei/dot v1.11.0 github.com/evanphx/json-patch v5.9.11+incompatible github.com/ghodss/yaml v1.0.0 github.com/google/cel-go v0.27.0 github.com/hashicorp/go-multierror v1.1.1 github.com/jsimonetti/rtnetlink/v2 v2.2.0 github.com/mdlayher/ethtool v0.5.1 github.com/neticdk/go-stdlib v1.0.1 github.com/opencontainers/runtime-spec v1.3.0 github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 github.com/ryanuber/go-glob v1.0.0 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/siderolabs/crypto v0.6.4 github.com/siderolabs/gen v0.8.6 github.com/siderolabs/go-api-signature v0.3.12 github.com/siderolabs/go-pointer v1.0.1 github.com/siderolabs/net v0.4.0 github.com/siderolabs/protoenc v0.2.4 github.com/stretchr/testify v1.11.1 go.yaml.in/yaml/v4 v4.0.0-rc.4 golang.org/x/net v0.52.0 google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c google.golang.org/grpc v1.79.3 google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af gopkg.in/yaml.v3 v3.0.1 ) require ( cel.dev/expr v0.25.1 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/ProtonMail/gopenpgp/v2 v2.9.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/containernetworking/cni v1.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/gertd/go-pluralize v0.2.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/netlink v1.8.0 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/text v0.35.0 // indirect golang.org/x/time v0.14.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) ================================================ FILE: pkg/machinery/go.sum ================================================ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= github.com/ProtonMail/gopenpgp/v2 v2.9.0 h1:ruLzBmwe4dR1hdnrsEJ/S7psSBmV15gFttFUPP/+/kE= github.com/ProtonMail/gopenpgp/v2 v2.9.0/go.mod h1:IldDyh9Hv1ZCCYatTuuEt1XZJ0OPjxLpTarDfglih7s= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/brianvoe/gofakeit/v7 v7.7.3 h1:RWOATEGpJ5EVg2nN8nlaEyaV/aB4d6c3GqYrbqQekss= github.com/brianvoe/gofakeit/v7 v7.7.3/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.20.0 h1:atwWj9d3NffHyPZzVlx3hmw1on5CLe9eljR8VuHTwhM= github.com/cilium/ebpf v0.20.0/go.mod h1:pzLjFymM+uZPLk/IXZUL63xdx5VXEo+enTzxkZXdycw= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/containerd/go-cni v1.1.13 h1:eFSGOKlhoYNxpJ51KRIMHZNlg5UgocXEIEBGkY7Hnis= github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI= github.com/containernetworking/cni v1.3.0 h1:v6EpN8RznAZj9765HhXQrtXgX+ECGebEYEmnuFjskwo= github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= github.com/cosi-project/runtime v1.14.0 h1:puGI7sssk1h2KScC4ETjC+M7nyN+0ur44bAuSLdY91A= github.com/cosi-project/runtime v1.14.0/go.mod h1:sd2+E6DjC/QjrnlEEglINDZ4FUW7cVDMB5aG98Dl3LA= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/dot v1.11.0 h1:zsrhCuFHAJge/aZIC4N4LdHy5tqYu4tWEaUzIwdYj4Y= github.com/emicklei/dot v1.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA= github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= 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/grpc-gateway/v2 v2.27.8 h1:NpbJl/eVbvrGE0MJ6X16X9SAifesl6Fwxg/YmCvubRI= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.8/go.mod h1:mi7YA+gCzVem12exXy46ZespvGtX/lZmD/RLnQhVW7U= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink/v2 v2.2.0 h1:/KfZ310gOAFrXXol5VwnFEt+ucldD/0dsSRZwpHCP9w= github.com/jsimonetti/rtnetlink/v2 v2.2.0/go.mod h1:lbjDHxC+5RJ08lzPeA90Ls2pEoId3F08MoEMlhfHxeI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v1.8.0 h1:e7XNIYJKD7hUct3Px04RuIGJbBxy1/c4nX7D5YyvvlM= github.com/mdlayher/netlink v1.8.0/go.mod h1:UhgKXUlDQhzb09DrCl2GuRNEglHmhYoWAHid9HK3594= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/onsi/ginkgo/v2 v2.20.1 h1:YlVIbqct+ZmnEph770q9Q7NVAz4wwIiVNahee6JyUzo= github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5JiKP7883xBzZAr1ydcxrKNSVNm7+3+JwjxZEsg= github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/siderolabs/crypto v0.6.4 h1:uMoe/X/mABOv6yOgvKcjmjIMdv6U8JegBXlPKtyjn3g= github.com/siderolabs/crypto v0.6.4/go.mod h1:39B7Mdrd8qTfEYOjsWPQOk7gLTWrEI30isAW+YYj9nk= github.com/siderolabs/ethtool v0.4.0-sidero h1:Ls/M4bFUjfcB1RDVviPZlL3kWcXaEVVSbKke+EZ2A9U= github.com/siderolabs/ethtool v0.4.0-sidero/go.mod h1:nOIR88fiFTdBfakYLEUAhxdy75Ih/fgnSlsSKAHRpfc= github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4= github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ= github.com/siderolabs/go-api-signature v0.3.12 h1:i1X+kPh9fzo+lEjtEplZSbtq1p21vKv4FCWJcB/ozvk= github.com/siderolabs/go-api-signature v0.3.12/go.mod h1:dPLiXohup4qHX7KUgF/wwOE3lRU5uAr3ssEomNxiyxY= github.com/siderolabs/go-pointer v1.0.1 h1:f7Yi4IK1jptS8yrT9GEbwhmGcVxvPQgBUG/weH3V3DM= github.com/siderolabs/go-pointer v1.0.1/go.mod h1:C8Q/3pNHT4RE9e4rYR9PHeS6KPMlStRBgYrJQJNy/vA= github.com/siderolabs/go-retry v0.3.3 h1:zKV+S1vumtO72E6sYsLlmIdV/G/GcYSBLiEx/c9oCEg= github.com/siderolabs/go-retry v0.3.3/go.mod h1:Ff/VGc7v7un4uQg3DybgrmOWHEmJ8BzZds/XNn/BqMI= github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I= github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM= github.com/siderolabs/protoenc v0.2.4 h1:D3Fpn2nQSQOhl8ZlAxijZAf7K6F8CM1uZq0afIGsr8Q= github.com/siderolabs/protoenc v0.2.4/go.mod h1:i5XLHjfv5vyi7LhQrSEo19HCA+lYtDd7CWxsoWp9XE8= github.com/smira/go-stdlib v0.0.0-20260318082201-9d387eb2130d h1:/A1ysgd+d93H+qhSUwosk6O6WoppaHQc45XzbLUilRU= github.com/smira/go-stdlib v0.0.0-20260318082201-9d387eb2130d/go.mod h1:KP9nLuDoanLbM8Wturn+hage2FtcrJaF1+1Znu+MKEw= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 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/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= 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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc= golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20260311181403-84a4fc48630c h1:OyQPd6I3pN/9gDxz6L13kYGJgqkpdrAohJRBeXyxlgI= google.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg= google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk= google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= 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.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/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= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: pkg/machinery/imager/imageropts/bootloaderkind_enumer.go ================================================ // Code generated by "enumer -type BootloaderKind -linecomment -text"; DO NOT EDIT. package imageropts import ( "fmt" "strings" ) const _BootloaderKindName = "nonedual-bootsd-bootgrub" var _BootloaderKindIndex = [...]uint8{0, 4, 13, 20, 24} const _BootloaderKindLowerName = "nonedual-bootsd-bootgrub" func (i BootloaderKind) String() string { if i < 0 || i >= BootloaderKind(len(_BootloaderKindIndex)-1) { return fmt.Sprintf("BootloaderKind(%d)", i) } return _BootloaderKindName[_BootloaderKindIndex[i]:_BootloaderKindIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _BootloaderKindNoOp() { var x [1]struct{} _ = x[BootLoaderKindNone-(0)] _ = x[BootLoaderKindDualBoot-(1)] _ = x[BootLoaderKindSDBoot-(2)] _ = x[BootLoaderKindGrub-(3)] } var _BootloaderKindValues = []BootloaderKind{BootLoaderKindNone, BootLoaderKindDualBoot, BootLoaderKindSDBoot, BootLoaderKindGrub} var _BootloaderKindNameToValueMap = map[string]BootloaderKind{ _BootloaderKindName[0:4]: BootLoaderKindNone, _BootloaderKindLowerName[0:4]: BootLoaderKindNone, _BootloaderKindName[4:13]: BootLoaderKindDualBoot, _BootloaderKindLowerName[4:13]: BootLoaderKindDualBoot, _BootloaderKindName[13:20]: BootLoaderKindSDBoot, _BootloaderKindLowerName[13:20]: BootLoaderKindSDBoot, _BootloaderKindName[20:24]: BootLoaderKindGrub, _BootloaderKindLowerName[20:24]: BootLoaderKindGrub, } var _BootloaderKindNames = []string{ _BootloaderKindName[0:4], _BootloaderKindName[4:13], _BootloaderKindName[13:20], _BootloaderKindName[20:24], } // BootloaderKindString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func BootloaderKindString(s string) (BootloaderKind, error) { if val, ok := _BootloaderKindNameToValueMap[s]; ok { return val, nil } if val, ok := _BootloaderKindNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to BootloaderKind values", s) } // BootloaderKindValues returns all values of the enum func BootloaderKindValues() []BootloaderKind { return _BootloaderKindValues } // BootloaderKindStrings returns a slice of all String values of the enum func BootloaderKindStrings() []string { strs := make([]string, len(_BootloaderKindNames)) copy(strs, _BootloaderKindNames) return strs } // IsABootloaderKind returns "true" if the value is listed in the enum definition. "false" otherwise func (i BootloaderKind) IsABootloaderKind() bool { for _, v := range _BootloaderKindValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for BootloaderKind func (i BootloaderKind) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for BootloaderKind func (i *BootloaderKind) UnmarshalText(text []byte) error { var err error *i, err = BootloaderKindString(string(text)) return err } ================================================ FILE: pkg/machinery/imager/imageropts/imageropts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package imageropts contains exportable types used in imager profile options. package imageropts //go:generate go tool github.com/dmarkham/enumer -type BootloaderKind -linecomment -text // BootloaderKind is a bootloader for the disk image. type BootloaderKind int const ( // BootLoaderKindNone is the zero value. BootLoaderKindNone BootloaderKind = iota // none // BootLoaderKindDualBoot is the dual-boot bootloader. // using sd-boot for UEFI and GRUB for BIOS. BootLoaderKindDualBoot // dual-boot // BootLoaderKindSDBoot is the sd-boot bootloader. BootLoaderKindSDBoot // sd-boot // BootLoaderKindGrub is the GRUB bootloader. BootLoaderKindGrub // grub ) ================================================ FILE: pkg/machinery/imager/quirks/partitions.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package quirks // PartitionSizes capture partition sizes for Talos. type PartitionSizes struct { bootSize uint64 } const ( mib = 1024 * 1024 gib = 1024 * mib ) // GrubEFISize return EFI partition size for GRUB layout. func (p PartitionSizes) GrubEFISize() uint64 { return 100 * mib } // GrubBIOSSize return BIOS GRUB partition size. func (p PartitionSizes) GrubBIOSSize() uint64 { return 1 * mib } // GrubBootSize return boot partition size for GRUB layout. func (p PartitionSizes) GrubBootSize() uint64 { return p.bootSize } // UKIEFISize return EFI partition size for UKI layout. func (p PartitionSizes) UKIEFISize() uint64 { // EFIUKISize is the size of the EFI partition when UKI is enabled. // With UKI all assets are stored in the EFI partition. // This is the size of the old EFISize + BIOSGrubSize + BootSize. return p.GrubEFISize() + p.GrubBIOSSize() + p.GrubBootSize() } // METASize return META partition size. func (p PartitionSizes) METASize() uint64 { return 1 * mib } // StateSize return state partition size. func (p PartitionSizes) StateSize() uint64 { return 100 * mib } // EphemeralMinSize return minimum size for ephemeral partition. func (p PartitionSizes) EphemeralMinSize() uint64 { return 2 * gib } ================================================ FILE: pkg/machinery/imager/quirks/quirks.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package quirks contains the quirks for Talos image generation. package quirks import "github.com/blang/semver/v4" // Quirks contains the quirks for Talos image generation. type Quirks struct { v *semver.Version } // New returns a new Quirks instance based on Talos version for the image. func New(talosVersion string) Quirks { v, err := semver.ParseTolerant(talosVersion) // ignore the error if err != nil { return Quirks{} } // we only care about major, minor, and patch, so that alpha, beta, etc. are ignored return Quirks{v: &semver.Version{ Major: v.Major, Minor: v.Minor, Patch: v.Patch, }} } // Version returns the Talos version. func (q Quirks) Version() *semver.Version { return q.v } var minVersionResetOption = semver.MustParse("1.4.0") // SupportsResetGRUBOption returns true if the Talos version supports the reset option in GRUB menu (image and ISO). func (q Quirks) SupportsResetGRUBOption() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionResetOption) } var minVersionUKI = semver.MustParse("1.5.0") // SupportsUKI returns true if the Talos version supports building UKIs. func (q Quirks) SupportsUKI() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionUKI) } var minVersionCompressedMETA = semver.MustParse("1.6.3") // SupportsCompressedEncodedMETA returns true if the Talos version supports compressed and encoded META as an environment variable. func (q Quirks) SupportsCompressedEncodedMETA() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionCompressedMETA) } var minVersionOverlay = semver.MustParse("1.7.0") // SupportsOverlay returns true if the Talos imager version supports overlay. func (q Quirks) SupportsOverlay() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionOverlay) } var minVersionZstd = semver.MustParse("1.8.0") // UseZSTDCompression returns true if the Talos should use zstd compression in place of xz. func (q Quirks) UseZSTDCompression() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionZstd) } var minVersionISOLabel = semver.MustParse("1.8.0") // SupportsISOLabel returns true if the Talos version supports setting the ISO label. func (q Quirks) SupportsISOLabel() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionISOLabel) } var minVersionMultidoc = semver.MustParse("1.5.0") // SupportsMultidoc returns true if the Talos version supports multidoc machine configs. func (q Quirks) SupportsMultidoc() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionMultidoc) } // maxVersionMetalPlatformConsoleTTYS0Dropped is the version that dropped console=ttyS0 for metal image. var maxVersionMetalPlatformConsoleTTYS0Dropped = semver.MustParse("1.8.0") // SupportsMetalPlatformConsoleTTYS0 returns true if the Talos version supports already has console=ttyS0 kernel argument. func (q Quirks) SupportsMetalPlatformConsoleTTYS0() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return false } return q.v.LT(maxVersionMetalPlatformConsoleTTYS0Dropped) } // minVersionSupportsHalfIfInstalled is the version that supports half if installed. var minVersionSupportsHalfIfInstalled = semver.MustParse("1.8.0") // SupportsHaltIfInstalled returns true if the Talos version supports half if installed. func (q Quirks) SupportsHaltIfInstalled() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionSupportsHalfIfInstalled) } var minVersionSkipDataPartitions = semver.MustParse("1.8.0") // SkipDataPartitions returns true if the Talos version supports creating EPHEMERAL/STATE partitions on its own. func (q Quirks) SkipDataPartitions() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionSkipDataPartitions) } // minVersionSELinux is the version that enabled SELinux and added respective parameters. var minVersionSELinux = semver.MustParse("1.10.0") // SupportsSELinux returns true if the Talos version enables selinux=1 by default. func (q Quirks) SupportsSELinux() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minVersionSELinux) } // minVersionUseSDBootOnly is the version that supports only SDBoot for UEFI. var minTalosVersionUseSDBootOnly = semver.MustParse("1.10.0") // UseSDBootForUEFI returns true if the Talos version supports only SDBoot for UEFI. func (q Quirks) UseSDBootForUEFI() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return false } return q.v.GTE(minTalosVersionUseSDBootOnly) } // minTalosVersionUsrMerge is the version that has /lib and /bin symlinked into /usr. var minTalosVersionUsrMerge = semver.MustParse("1.10.0") // KernelModulesPath returns kernel module storage path for the given Talos version. func (q Quirks) KernelModulesPath() string { // if the version doesn't parse, we assume it's latest Talos if q.v == nil || q.v.GTE(minTalosVersionUsrMerge) { return "/usr/lib/modules" } return "/lib/modules" } // FirmwarePath returns firmware storage path for the given Talos version. func (q Quirks) FirmwarePath() string { // if the version doesn't parse, we assume it's latest Talos if q.v == nil || q.v.GTE(minTalosVersionUsrMerge) { return "/usr/lib/firmware" } return "/lib/firmware" } // minTalosVersionUKIProfiles is the version that supports UKI profiles. var minTalosVersionUKIProfiles = semver.MustParse("1.10.0") // SupportsUKIProfiles returns true if the Talos version supports UKI profiles. func (q Quirks) SupportsUKIProfiles() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionUKIProfiles) } var minTalosVersionUnifiedInstaller = semver.MustParse("1.10.0") // SupportsUnifiedInstaller returns true if the Talos version supports unified installer. func (q Quirks) SupportsUnifiedInstaller() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionUnifiedInstaller) } var minTalosVersionBoot2G = semver.MustParse("1.11.0") // PartitionSizes returns partition sizes for the given Talos version. func (q Quirks) PartitionSizes() PartitionSizes { bootSize := uint64(2000 * mib) // 2000 MiB if q.v != nil && q.v.LT(minTalosVersionBoot2G) { // legacy size of 1000 MiB bootSize = uint64(1000 * mib) } return PartitionSizes{ bootSize: bootSize, } } // XFSMkfsConfig returns the mkfs.xfs config for the given Talos version. func (q Quirks) XFSMkfsConfig() string { switch version := q.v; { // if the version doesn't parse, we assume it's latest Talos // update when we have a new LTS config case version == nil: return "/usr/share/xfsprogs/mkfs/lts_6.18.conf" // add new version once we have a new LTS config case version.GTE(semver.MustParse("1.13.0")): return "/usr/share/xfsprogs/mkfs/lts_6.18.conf" case version.GTE(semver.MustParse("1.10.0")): return "/usr/share/xfsprogs/mkfs/lts_6.12.conf" case version.GTE(semver.MustParse("1.8.0")) && version.LT(semver.MustParse("1.10.0")): return "/usr/share/xfsprogs/mkfs/lts_6.6.conf" case version.GTE(semver.MustParse("1.5.0")) && version.LT(semver.MustParse("1.8.0")): return "/usr/share/xfsprogs/mkfs/lts_6.1.conf" default: return "/usr/share/xfsprogs/mkfs/lts_6.1.conf" } } var maxTalosVersionIMASupported = semver.MustParse("1.10.99") // SupportsIMA returns true if the Talos version has IMA support. func (q Quirks) SupportsIMA() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return false } return q.v.LTE(maxTalosVersionIMASupported) } var minTalosVersionEmbeddedConfig = semver.MustParse("1.12.0") // SupportsEmbeddedConfig returns true if the Talos version supports embedded machine configuration. func (q Quirks) SupportsEmbeddedConfig() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionEmbeddedConfig) } var minTalosVersionDisableModSigVerify = semver.MustParse("1.12.0") // SupportsDisablingModuleSignatureVerification returns true if the Talos version supports disabling module signature verification. func (q Quirks) SupportsDisablingModuleSignatureVerification() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionDisableModSigVerify) } var minTalosVersionISOSupportsSettingBootloader = semver.MustParse("1.12.0") // ISOSupportsSettingBootloader returns true if the Talos version supports setting bootloader for ISO output. func (q Quirks) ISOSupportsSettingBootloader() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionISOSupportsSettingBootloader) } var minTalosVersionProcMemOverrideNever = semver.MustParse("1.13.0") // ProcMemOverrideNever returns true if the Talos version should enforce 'proc_mem.force_override=never'. func (q Quirks) ProcMemOverrideNever() bool { // if the version doesn't parse, we assume it's latest Talos if q.v == nil { return true } return q.v.GTE(minTalosVersionProcMemOverrideNever) } ================================================ FILE: pkg/machinery/imager/quirks/quirks_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package quirks_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) func TestSupportsResetOption(t *testing.T) { for _, test := range []struct { version string expected bool }{ { version: "1.5.0", expected: true, }, { expected: true, }, { version: "1.3.7", expected: false, }, } { t.Run(test.version, func(t *testing.T) { assert.Equal(t, test.expected, quirks.New(test.version).SupportsResetGRUBOption()) }) } } func TestSupportsCompressedEncodedMETA(t *testing.T) { for _, test := range []struct { version string expected bool }{ { version: "1.6.3", expected: true, }, { version: "1.7.0", expected: true, }, { expected: true, }, { version: "1.6.2", expected: false, }, } { t.Run(test.version, func(t *testing.T) { assert.Equal(t, test.expected, quirks.New(test.version).SupportsCompressedEncodedMETA()) }) } } func TestSupportsOverlay(t *testing.T) { for _, test := range []struct { version string expected bool }{ { version: "1.6.3", expected: false, }, { version: "1.7.0", expected: true, }, { expected: true, }, { version: "1.6.2", expected: false, }, { version: "1.7.0-alpha.0", expected: true, }, { version: "v1.7.0-alpha.0-75-gff08e2821", expected: true, }, } { t.Run(test.version, func(t *testing.T) { assert.Equal(t, test.expected, quirks.New(test.version).SupportsOverlay()) }) } } func TestSupportsZstd(t *testing.T) { for _, test := range []struct { version string expected bool }{ { version: "1.7.3", expected: false, }, { expected: true, }, { version: "1.6.2", expected: false, }, { version: "1.8.0-alpha.0", expected: true, }, { version: "v1.8.3", expected: true, }, } { t.Run(test.version, func(t *testing.T) { assert.Equal(t, test.expected, quirks.New(test.version).UseZSTDCompression()) }) } } func TestXFSMkfsConfigFile(t *testing.T) { for _, test := range []struct { version string expected string }{ { version: "1.5.0", expected: "/usr/share/xfsprogs/mkfs/lts_6.1.conf", }, { version: "1.6.2", expected: "/usr/share/xfsprogs/mkfs/lts_6.1.conf", }, { version: "1.7.0", expected: "/usr/share/xfsprogs/mkfs/lts_6.1.conf", }, { version: "1.8.1", expected: "/usr/share/xfsprogs/mkfs/lts_6.6.conf", }, { version: "1.9.3", expected: "/usr/share/xfsprogs/mkfs/lts_6.6.conf", }, { version: "1.10.0", expected: "/usr/share/xfsprogs/mkfs/lts_6.12.conf", }, { version: "1.12.0", expected: "/usr/share/xfsprogs/mkfs/lts_6.12.conf", }, { version: "1.13.0", expected: "/usr/share/xfsprogs/mkfs/lts_6.18.conf", }, { expected: "/usr/share/xfsprogs/mkfs/lts_6.18.conf", }, } { t.Run(test.version, func(t *testing.T) { assert.Equal(t, test.expected, quirks.New(test.version).XFSMkfsConfig()) }) } } func TestPartitionSizes(t *testing.T) { const ( MiB = 1024 * 1024 GiB = 1024 * MiB ) for _, test := range []struct { version string // expected partition sizes grubEFISize uint64 grubBIOSSize uint64 grubBootSize uint64 ukiEFISize uint64 metaSize uint64 stateSize uint64 ephemeralMinSize uint64 }{ { version: "1.9.0", grubEFISize: 100 * MiB, grubBIOSSize: 1 * MiB, grubBootSize: 1000 * MiB, ukiEFISize: 1000*MiB + 100*MiB + 1*MiB, metaSize: 1 * MiB, stateSize: 100 * MiB, ephemeralMinSize: 2 * GiB, }, { version: "1.10.0", grubEFISize: 100 * MiB, grubBIOSSize: 1 * MiB, grubBootSize: 1000 * MiB, ukiEFISize: 1000*MiB + 100*MiB + 1*MiB, metaSize: 1 * MiB, stateSize: 100 * MiB, ephemeralMinSize: 2 * GiB, }, { version: "1.11.0", grubEFISize: 100 * MiB, grubBIOSSize: 1 * MiB, grubBootSize: 2000 * MiB, ukiEFISize: 2000*MiB + 100*MiB + 1*MiB, metaSize: 1 * MiB, stateSize: 100 * MiB, ephemeralMinSize: 2 * GiB, }, { version: "", grubEFISize: 100 * MiB, grubBIOSSize: 1 * MiB, grubBootSize: 2000 * MiB, ukiEFISize: 2000*MiB + 100*MiB + 1*MiB, metaSize: 1 * MiB, stateSize: 100 * MiB, ephemeralMinSize: 2 * GiB, }, } { t.Run(test.version, func(t *testing.T) { ps := quirks.New(test.version).PartitionSizes() assert.Equal(t, test.grubEFISize, ps.GrubEFISize()) assert.Equal(t, test.grubBIOSSize, ps.GrubBIOSSize()) assert.Equal(t, test.grubBootSize, ps.GrubBootSize()) assert.Equal(t, test.ukiEFISize, ps.UKIEFISize()) assert.Equal(t, test.metaSize, ps.METASize()) assert.Equal(t, test.stateSize, ps.StateSize()) assert.Equal(t, test.ephemeralMinSize, ps.EphemeralMinSize()) }) } } ================================================ FILE: pkg/machinery/kernel/kernel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kernel import ( "path" "path/filepath" "strings" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) const ( // Sysfs defines prefix for sysfs kernel params. Sysfs = "sys" // Sysctl defines prefix for sysctl kernel params. Sysctl = "proc.sys" ) // DefaultArgs returns the Talos default kernel commandline options. func DefaultArgs(quirks quirks.Quirks) []string { result := []string{ "init_on_alloc=1", "slab_nomerge=", "pti=on", "consoleblank=0", // AWS recommends setting the nvme_core.io_timeout to the highest value possible. // See https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html. "nvme_core.io_timeout=4294967295", // Disable rate limited printk "printk.devkmsg=on", } if quirks.SupportsIMA() { // Enable IMAs for integrity measurement result = append( result, "ima_template=ima-ng", "ima_appraise=fix", "ima_hash=sha512", ) } if quirks.SupportsSELinux() { result = append(result, constants.KernelParamSELinux+"=1") } if quirks.SupportsDisablingModuleSignatureVerification() { result = append(result, constants.KernelParamEnforceModuleSigVerify+"=1") // see https://github.com/siderolabs/talos/issues/11989 } if quirks.ProcMemOverrideNever() { result = append(result, "proc_mem.force_override=never") } return result } // SecureBootArgs returns the kernel commandline options required for secure boot. func SecureBootArgs(quirks.Quirks) []string { return []string{ "lockdown=confidentiality", } } // Param represents a kernel system property. type Param struct { Key string Value string } // Path returns the path to the systctl file under /proc/sys or /sys. func (prop *Param) Path() string { // From: https://man7.org/linux/man-pages/man5/sysctl.d.5.html // // Note that either "/" or "." may be used as separators within // sysctl variable names. If the first separator is a slash, // remaining slashes and dots are left intact. If the first // separator is a dot, dots and slashes are interchanged. // "kernel.domainname=foo" and "kernel/domainname=foo" are // equivalent and will cause "foo" to be written to // /proc/sys/kernel/domainname. Either // "net.ipv4.conf.enp3s0/200.forwarding" or // "net/ipv4/conf/enp3s0.200/forwarding" may be used to refer to // /proc/sys/net/ipv4/conf/enp3s0.200/forwarding // // detect the first separator, either '.' or '/' // according to the sysctl man page, if the first separator is '/', we keep slashes intact, // otherwise we convert dots to slashes keyPath := prop.Key prefix := "" // trim standard prefix for _, stdPrefix := range []string{Sysctl, Sysfs} { if strings.HasPrefix(prop.Key, stdPrefix+".") { keyPath = keyPath[len(stdPrefix)+1:] prefix = stdPrefix break } } firstSepIndex := strings.IndexAny(keyPath, "./") // if the first separator is a dot, remap '.' to '/', and '/' to '.' if firstSepIndex != -1 && keyPath[firstSepIndex] == '.' { keyPath = strings.Map( func(r rune) rune { switch r { case '.': return '/' case '/': return '.' default: return r } }, keyPath, ) } return path.Clean("/" + filepath.Join(strings.ReplaceAll(prefix, ".", "/"), keyPath)) } ================================================ FILE: pkg/machinery/kernel/kernel_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kernel_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/machinery/kernel" ) func TestParamPath(t *testing.T) { t.Parallel() tests := []struct { name string param *kernel.Param want string }{ { name: "Test Sysfs Path", param: &kernel.Param{ Key: kernel.Sysfs + ".block.sda.queue.scheduler", }, want: "/sys/block/sda/queue/scheduler", }, { name: "Test Sysctl Path", param: &kernel.Param{ Key: kernel.Sysctl + ".net.ipv6.conf.eth0.accept_ra", }, want: "/proc/sys/net/ipv6/conf/eth0/accept_ra", }, { name: "Test Sysctl Path with vlan interface untouched", param: &kernel.Param{ Key: kernel.Sysctl + ".net/ipv6/conf/eth0.103/disable_ipv6", }, want: "/proc/sys/net/ipv6/conf/eth0.103/disable_ipv6", }, { name: "Test Sysctl Path with vlan interface inverted", param: &kernel.Param{ Key: kernel.Sysctl + ".net.ipv6.conf.eth0/103.disable_ipv6", }, want: "/proc/sys/net/ipv6/conf/eth0.103/disable_ipv6", }, { name: "Test Sysctl Path with invalid symbols which translate to '..'", param: &kernel.Param{ Key: kernel.Sysctl + ".net.ipv6.conf.eth0/103.//.disable_ipv6", }, want: "/proc/sys/net/ipv6/conf/disable_ipv6", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.param.Path(); got != tt.want { t.Errorf("Param.Path() = %v, want %v", got, tt.want) } }) } } func TestDefaultKernelArgs(t *testing.T) { t.Parallel() for _, test := range []struct { name string quirks quirks.Quirks expected []string }{ { name: "latest", expected: []string{ "init_on_alloc=1", "slab_nomerge=", "pti=on", "consoleblank=0", "nvme_core.io_timeout=4294967295", "printk.devkmsg=on", "selinux=1", "module.sig_enforce=1", "proc_mem.force_override=never", }, }, { name: "v1.9", quirks: quirks.New("v1.9.0"), expected: []string{ "init_on_alloc=1", "slab_nomerge=", "pti=on", "consoleblank=0", "nvme_core.io_timeout=4294967295", "printk.devkmsg=on", "ima_template=ima-ng", "ima_appraise=fix", "ima_hash=sha512", }, }, { name: "v1.12", quirks: quirks.New("v1.12.0"), expected: []string{ "init_on_alloc=1", "slab_nomerge=", "pti=on", "consoleblank=0", "nvme_core.io_timeout=4294967295", "printk.devkmsg=on", "selinux=1", "module.sig_enforce=1", }, }, { name: "v1.13", quirks: quirks.New("v1.13.0"), expected: []string{ "init_on_alloc=1", "slab_nomerge=", "pti=on", "consoleblank=0", "nvme_core.io_timeout=4294967295", "printk.devkmsg=on", "selinux=1", "module.sig_enforce=1", "proc_mem.force_override=never", }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() assert.Equal(t, test.expected, kernel.DefaultArgs(test.quirks)) }) } } func TestSecureBootArgs(t *testing.T) { t.Parallel() for _, test := range []struct { name string quirks quirks.Quirks expected []string }{ { name: "latest", expected: []string{ "lockdown=confidentiality", }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() assert.Equal(t, test.expected, kernel.SecureBootArgs(test.quirks)) }) } } ================================================ FILE: pkg/machinery/kubelet/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubelet defines Talos interface for the kubelet. package kubelet // ProtectedConfigurationFields is a list of kubelet config fields that can't be overridden // with the machine configuration. var ProtectedConfigurationFields = []string{ "apiVersion", "authentication", "authorization", "cgroupRoot", "kind", "kubeletCgroups", "port", "protectKernelDefaults", "resolvConf", "rotateCertificates", "systemCgroups", "staticPodPath", "seccompDefault", } ================================================ FILE: pkg/machinery/labels/validate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package labels contains adapter label validation functions from Kubernetes. // // We want to avoid dependency of machinery on Kubernetes packages. package labels import ( "fmt" "regexp" "slices" "strings" "github.com/hashicorp/go-multierror" "github.com/siderolabs/gen/maps" "github.com/siderolabs/talos/pkg/machinery/constants" ) // Validate validates that a set of labels are correctly defined. func Validate(labels map[string]string) error { var multiErr *multierror.Error keys := maps.Keys(labels) slices.Sort(keys) for _, k := range keys { if err := ValidateQualifiedName(k); err != nil { multiErr = multierror.Append(multiErr, err) } if err := ValidateLabelValue(labels[k]); err != nil { multiErr = multierror.Append(multiErr, err) } } return multiErr.ErrorOrNil() } // TotalAnnotationSizeLimitB is the maximum size of all annotations in bytes. const TotalAnnotationSizeLimitB int64 = 256 * (1 << 10) // 256 kB // ValidateAnnotations validates that a set of annotations are correctly defined. func ValidateAnnotations(annotations map[string]string) error { var multiErr *multierror.Error keys := maps.Keys(annotations) slices.Sort(keys) var size int64 for _, k := range keys { if err := ValidateQualifiedName(k); err != nil { multiErr = multierror.Append(multiErr, err) } switch k { case constants.AnnotationOwnedAnnotations, constants.AnnotationOwnedLabels, constants.AnnotationOwnedTaints: multiErr = multierror.Append(multiErr, fmt.Errorf("annotation %q is reserved", k)) } size += int64(len(k)) + int64(len(annotations[k])) } if size > TotalAnnotationSizeLimitB { multiErr = multierror.Append(multiErr, fmt.Errorf("total annotation size exceeds limit of %d bytes", TotalAnnotationSizeLimitB)) } return multiErr.ErrorOrNil() } const ( dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*" dns1123SubdomainMaxLength int = 253 ) var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$") // ValidateDNS1123Subdomain tests for a string that conforms to the definition of a // subdomain in DNS (RFC 1123). func ValidateDNS1123Subdomain(value string) error { if len(value) > dns1123SubdomainMaxLength { return fmt.Errorf("domain name length exceeds limit of %d: %q", dns1123SubdomainMaxLength, value) } if !dns1123SubdomainRegexp.MatchString(value) { return fmt.Errorf("domain doesn't match required format: %q", value) } return nil } const ( qnameCharFmt string = "[A-Za-z0-9]" qnameExtCharFmt string = "[-A-Za-z0-9_.]" qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt qualifiedNameMaxLength int = 63 ) var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$") // ValidateQualifiedName tests whether the value passed is what Kubernetes calls a // "qualified name". // // This is a format used in various places throughout the // system. If the value is not valid, a list of error strings is returned. // Otherwise an empty list (or nil) is returned. func ValidateQualifiedName(value string) error { parts := strings.Split(value, "/") var name string switch len(parts) { case 1: name = parts[0] case 2: var prefix string prefix, name = parts[0], parts[1] if len(prefix) == 0 { return fmt.Errorf("prefix cannot be empty: %q", value) } else if err := ValidateDNS1123Subdomain(prefix); err != nil { return fmt.Errorf("prefix %q is invalid: %v", prefix, err) } default: return fmt.Errorf("invalid format: too many slashes: %q", value) } switch { case len(name) == 0: return fmt.Errorf("name cannot be empty: %q", value) case len(name) > qualifiedNameMaxLength: return fmt.Errorf("name is too long: %q (limit is %d)", value, qualifiedNameMaxLength) case !qualifiedNameRegexp.MatchString(name): return fmt.Errorf("name %q is invalid", name) } return nil } const ( labelValueFmt string = "(" + qualifiedNameFmt + ")?" labelValueMaxLength int = 63 ) var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$") // ValidateLabelValue tests whether the value passed is a valid label value. // // If the value is not valid, a list of error strings is returned. // Otherwise an empty list (or nil) is returned. func ValidateLabelValue(value string) error { if len(value) > labelValueMaxLength { return fmt.Errorf("label value length exceeds limit of %d: %q", labelValueMaxLength, value) } if !labelValueRegexp.MatchString(value) { return fmt.Errorf("label value %q is invalid", value) } return nil } // ValidateTaints validates that a set of taints is correctly defined. func ValidateTaints(taints map[string]string) error { var multiErr *multierror.Error keys := maps.Keys(taints) slices.Sort(keys) for _, k := range keys { if err := ValidateQualifiedName(k); err != nil { multiErr = multierror.Append(multiErr, err) continue } val, effect, found := strings.Cut(taints[k], ":") if !found { effect = val } // validate that the taint has a valid effect, which is required to add the taint if !slices.Contains(constants.ValidEffects, effect) { multiErr = multierror.Append(multiErr, fmt.Errorf("invalid taint effect: %q", effect)) continue } if found { if err := ValidateLabelValue(val); err != nil { multiErr = multierror.Append(multiErr, err) continue } } } return multiErr.ErrorOrNil() } ================================================ FILE: pkg/machinery/labels/validate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package labels_test import ( "strings" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/labels" ) func TestValidate(t *testing.T) { for _, tt := range []struct { name string labels map[string]string expectedError string }{ { name: "empty", }, { name: "valid", labels: map[string]string{ "talos.dev/label": "value", "foo": "bar", "kubernetes.io/hostname": "hostname1", }, }, { name: "invalid", labels: map[string]string{ "345@.345/label": "value", "foo_": "bar", "/foo": "bar", "a/b/c": "bar", "kubernetes.io/hostname": "hostname1_", strings.Repeat("a", 64): "bar", "bar": strings.Repeat("a", 64), }, expectedError: "7 errors occurred:\n\t* prefix cannot be empty: \"/foo\"\n\t* prefix \"345@.345\" is invalid: domain doesn't match required format: \"345@.345\"\n\t* invalid format: too many slashes: \"a/b/c\"\n\t* name is too long: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" (limit is 63)\n\t* label value length exceeds limit of 63: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t* name \"foo_\" is invalid\n\t* label value \"hostname1_\" is invalid\n\n", //nolint:lll }, } { t.Run(tt.name, func(t *testing.T) { err := labels.Validate(tt.labels) if tt.expectedError != "" { assert.EqualError(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateAnnotations(t *testing.T) { for _, tt := range []struct { name string annotations map[string]string expectedError string }{ { name: "empty", }, { name: "valid", annotations: map[string]string{ "talos.dev/ann1": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", "foo": "bar", "kubernetes.io/rack": "rack2", }, }, { name: "invalid", annotations: map[string]string{ "foo_": "bar", "/foo": "bar", "a/b/c": "bar", "kubernetes.io/hostname": "hostname1_", constants.AnnotationOwnedAnnotations: "a", }, expectedError: "4 errors occurred:\n\t* prefix cannot be empty: \"/foo\"\n\t* invalid format: too many slashes: \"a/b/c\"\n\t* name \"foo_\" is invalid\n\t* annotation \"talos.dev/owned-annotations\" is reserved\n\n", //nolint:lll }, } { t.Run(tt.name, func(t *testing.T) { err := labels.ValidateAnnotations(tt.annotations) if tt.expectedError != "" { assert.EqualError(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } func TestValidateTaints(t *testing.T) { for _, tt := range []struct { name string taints map[string]string expectedError string }{ { name: "empty", }, { name: "valid", taints: map[string]string{ "foor": "bar:NoExecute", "doo": "NoExecute", }, }, { name: "invalid", taints: map[string]string{ strings.Repeat("a", 64): "bar", "bar": strings.Repeat("a", 64), "foo": "bar:NoExecute:NoSchedule", "loo": "bar:", "zoo": "bar:NoExocute", "koo": "key", }, expectedError: "6 errors occurred:\n\t* name is too long: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" (limit is 63)\n\t* invalid taint effect: \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"\n\t* invalid taint effect: \"NoExecute:NoSchedule\"\n\t* invalid taint effect: \"key\"\n\t* invalid taint effect: \"\"\n\t* invalid taint effect: \"NoExocute\"\n\n", //nolint:lll }, } { t.Run(tt.name, func(t *testing.T) { err := labels.ValidateTaints(tt.taints) if tt.expectedError != "" { assert.EqualError(t, err, tt.expectedError) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/meta/constants.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package meta const ( // Upgrade is the upgrade tag. Upgrade = iota + 6 // StagedUpgradeImageRef stores image reference for staged upgrade. StagedUpgradeImageRef // StagedUpgradeInstallOptions stores JSON-serialized install.Options. StagedUpgradeInstallOptions // StateEncryptionConfig stores JSON-serialized v1alpha1.Encryption. StateEncryptionConfig // MetalNetworkPlatformConfig stores serialized NetworkPlatformConfig for the `metal` platform. MetalNetworkPlatformConfig // DownloadURLCode stores the value of the `${code}` variable in the download URL for talos.config= URL. DownloadURLCode // UserReserved1 is reserved for user-defined metadata. UserReserved1 // UserReserved2 is reserved for user-defined metadata. UserReserved2 // UserReserved3 is reserved for user-defined metadata. UserReserved3 // UUIDOverride stores the UUID that this machine will use instead of the one from the hardware. UUIDOverride // UniqueMachineToken store the unique token for this machine. It's useful because UUID may repeat or be filled with zeros. UniqueMachineToken // DiskImageBootloader stores the bootloader used for the disk image, this key is wiped on first boot. DiskImageBootloader ) ================================================ FILE: pkg/machinery/meta/meta.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package meta provides interfaces for encoding and decoding META values. package meta import ( "bytes" "compress/gzip" "encoding/base64" "fmt" "io" "strconv" "strings" "github.com/siderolabs/gen/xslices" ) // Value represents a key/value pair for META. type Value struct { Key uint8 Value string } func (v Value) String() string { return fmt.Sprintf("0x%x=%s", v.Key, v.Value) } // Parse k=v expression. func (v *Value) Parse(s string) error { k, vv, ok := strings.Cut(s, "=") if !ok { return fmt.Errorf("invalid value %q", s) } key, err := strconv.ParseUint(k, 0, 8) if err != nil { return fmt.Errorf("invalid key %q", k) } v.Key = uint8(key) v.Value = vv return nil } // Values is a collection of Value. type Values []Value // Encode returns a string representation of Values for the environment variable. // // Each Value is encoded a k=v, split by ';' character. // The result is base64 encoded. func (v Values) Encode(allowGzip bool) string { raw := []byte(strings.Join(xslices.Map(v, Value.String), ";")) if allowGzip && len(raw) > 256 { var buf bytes.Buffer gzW, err := gzip.NewWriterLevel(&buf, gzip.BestCompression) if err != nil { panic(err) } if _, err := gzW.Write(raw); err != nil { panic(err) } if err := gzW.Close(); err != nil { panic(err) } raw = buf.Bytes() } return base64.StdEncoding.EncodeToString(raw) } // DecodeValues parses a string representation of Values for the environment variable. // // See Encode for the details of the encoding. func DecodeValues(s string) (Values, error) { b, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, err } if len(b) == 0 { return nil, nil } // do un-gzip if needed if hasGzipMagic(b) { gzR, err := gzip.NewReader(bytes.NewReader(b)) if err != nil { return nil, err } defer gzR.Close() //nolint:errcheck b, err = io.ReadAll(gzR) if err != nil { return nil, err } if err := gzR.Close(); err != nil { return nil, err } } parts := strings.Split(string(b), ";") result := make(Values, 0, len(parts)) for _, v := range parts { var vv Value if err := vv.Parse(v); err != nil { return nil, err } result = append(result, vv) } return result, nil } func hasGzipMagic(b []byte) bool { if len(b) < 10 { return false } // See https://en.wikipedia.org/wiki/Gzip#File_format. return b[0] == 0x1f && b[1] == 0x8b } ================================================ FILE: pkg/machinery/meta/meta_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package meta_test import ( "fmt" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/meta" ) func TestValue(t *testing.T) { t.Parallel() var v meta.Value require.NoError(t, v.Parse("10=foo")) assert.Equal(t, uint8(10), v.Key) assert.Equal(t, "foo", v.Value) assert.Equal(t, "0xa=foo", v.String()) var v2 meta.Value require.NoError(t, v2.Parse(v.String())) assert.Equal(t, v, v2) } func TestEncodeDecodeValues(t *testing.T) { t.Parallel() for _, allowGzip := range []bool{false, true} { t.Run(fmt.Sprintf("allowGzip=%v", allowGzip), func(t *testing.T) { t.Parallel() for _, test := range []struct { name string values []string expectedEncodedSize int expectedGzippedSize int }{ { name: "empty", }, { name: "simple", values: []string{ "10=foo", "0xb=bar", }, expectedEncodedSize: 20, expectedGzippedSize: 20, }, { name: "huge", values: []string{ "10=" + strings.Repeat("foobar", 256), "0xb=" + strings.Repeat("baz", 256), }, expectedEncodedSize: 3084, expectedGzippedSize: 80, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() values := make(meta.Values, len(test.values)) for i, v := range test.values { require.NoError(t, values[i].Parse(v)) } if len(values) == 0 { values = nil } encoded := values.Encode(allowGzip) switch { case test.expectedEncodedSize > 0 && !allowGzip: assert.Equal(t, test.expectedEncodedSize, len(encoded)) case test.expectedGzippedSize > 0 && allowGzip: assert.Equal(t, test.expectedGzippedSize, len(encoded)) } decoded, err := meta.DecodeValues(encoded) require.NoError(t, err) assert.Equal(t, values, decoded) }) } }) } } ================================================ FILE: pkg/machinery/nethelpers/address_flags.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "strings" ) // AddressFlags is a bitmask of AddressFlag. type AddressFlags uint32 func (flags AddressFlags) String() string { var values []string for flag := AddressTemporary; flag <= AddressStablePrivacy; flag <<= 1 { if (AddressFlag(flags) & flag) == flag { values = append(values, flag.String()) } } return strings.Join(values, ",") } // AddressFlagsString converts string representation of flags into AddressFlags. func AddressFlagsString(s string) (AddressFlags, error) { flags := AddressFlags(0) for p := range strings.SplitSeq(s, ",") { flag, err := AddressFlagString(p) if err != nil { return flags, err } flags |= AddressFlags(flag) } return flags, nil } // MarshalText implements text.Marshaler. func (flags AddressFlags) MarshalText() ([]byte, error) { return []byte(flags.String()), nil } // UnmarshalText implements text.Unmarshaler. func (flags *AddressFlags) UnmarshalText(b []byte) error { var err error *flags, err = AddressFlagsString(string(b)) return err } // AddressFlag wraps IFF_* constants. type AddressFlag uint32 // AddressFlag constants. // //structprotogen:gen_enum const ( AddressTemporary AddressFlag = 1 << iota // temporary AddressNoDAD // nodad AddressOptimistic // optimistic AddressDADFailed // dadfailed AddressHome // homeaddress AddressDeprecated // deprecated AddressTentative // tentative AddressPermanent // permanent AddressManagementTemp // mngmtmpaddr AddressNoPrefixRoute // noprefixroute AddressMCAutoJoin // mcautojoin AddressStablePrivacy // stableprivacy ) ================================================ FILE: pkg/machinery/nethelpers/addresssortalgorithm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // AddressSortAlgorithm is an internal address sorting algorithm. type AddressSortAlgorithm int // AddressSortAlgorithm constants. // //structprotogen:gen_enum const ( AddressSortAlgorithmV1 AddressSortAlgorithm = iota // v1 AddressSortAlgorithmV2 // v2 ) ================================================ FILE: pkg/machinery/nethelpers/adlacpactive.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // ADLACPActive is ADLACPActive. type ADLACPActive uint8 // ADLACPActive constants. // //structprotogen:gen_enum const ( ADLACPActiveOff ADLACPActive = iota // off ADLACPActiveOn // on ) ================================================ FILE: pkg/machinery/nethelpers/adselect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // ADSelect is ADSelect. type ADSelect uint8 // ADSelect constants. // //structprotogen:gen_enum const ( ADSelectStable ADSelect = iota // stable ADSelectBandwidth // bandwidth ADSelectCount // count ) // ADSelectByName parses ADSelect. func ADSelectByName(sel string) (ADSelect, error) { switch sel { case "", "stable": return ADSelectStable, nil case "bandwidth": return ADSelectBandwidth, nil case "count": return ADSelectCount, nil default: return 0, fmt.Errorf("invalid ad_select mode %v", sel) } } ================================================ FILE: pkg/machinery/nethelpers/arpalltargets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // ARPAllTargets is an ARP targets mode. type ARPAllTargets uint32 // ARPAllTargets constants. // //structprotogen:gen_enum const ( ARPAllTargetsAny ARPAllTargets = iota // any ARPAllTargetsAll // all ) // ARPAllTargetsByName parses ARPAllTargets. func ARPAllTargetsByName(a string) (ARPAllTargets, error) { switch a { case "", "any": return ARPAllTargetsAny, nil case "all": return ARPAllTargetsAll, nil default: return 0, fmt.Errorf("invalid arp_all_targets mode %v", a) } } ================================================ FILE: pkg/machinery/nethelpers/arpalltargets_enumer.go ================================================ // Code generated by "enumer -type=ARPAllTargets,ARPValidate,AddressFlag,AddressSortAlgorithm,ADSelect,ADLACPActive,AutoHostnameKind,BondMode,BondXmitHashPolicy,ClientIdentifier,ConntrackState,DefaultAction,Duplex,Family,LACPRate,LinkFlag,LinkType,MatchOperator,NfTablesChainHook,NfTablesChainPriority,NfTablesVerdict,OperationalState,Port,PrimaryReselect,Protocol,RouteFlag,RouteProtocol,RouteType,RoutingRuleAction,RoutingTable,Scope,Status,VLANProtocol,WOLMode -linecomment -text"; DO NOT EDIT. package nethelpers import ( "fmt" "strings" ) const _ARPAllTargetsName = "anyall" var _ARPAllTargetsIndex = [...]uint8{0, 3, 6} const _ARPAllTargetsLowerName = "anyall" func (i ARPAllTargets) String() string { if i >= ARPAllTargets(len(_ARPAllTargetsIndex)-1) { return fmt.Sprintf("ARPAllTargets(%d)", i) } return _ARPAllTargetsName[_ARPAllTargetsIndex[i]:_ARPAllTargetsIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ARPAllTargetsNoOp() { var x [1]struct{} _ = x[ARPAllTargetsAny-(0)] _ = x[ARPAllTargetsAll-(1)] } var _ARPAllTargetsValues = []ARPAllTargets{ARPAllTargetsAny, ARPAllTargetsAll} var _ARPAllTargetsNameToValueMap = map[string]ARPAllTargets{ _ARPAllTargetsName[0:3]: ARPAllTargetsAny, _ARPAllTargetsLowerName[0:3]: ARPAllTargetsAny, _ARPAllTargetsName[3:6]: ARPAllTargetsAll, _ARPAllTargetsLowerName[3:6]: ARPAllTargetsAll, } var _ARPAllTargetsNames = []string{ _ARPAllTargetsName[0:3], _ARPAllTargetsName[3:6], } // ARPAllTargetsString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ARPAllTargetsString(s string) (ARPAllTargets, error) { if val, ok := _ARPAllTargetsNameToValueMap[s]; ok { return val, nil } if val, ok := _ARPAllTargetsNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ARPAllTargets values", s) } // ARPAllTargetsValues returns all values of the enum func ARPAllTargetsValues() []ARPAllTargets { return _ARPAllTargetsValues } // ARPAllTargetsStrings returns a slice of all String values of the enum func ARPAllTargetsStrings() []string { strs := make([]string, len(_ARPAllTargetsNames)) copy(strs, _ARPAllTargetsNames) return strs } // IsAARPAllTargets returns "true" if the value is listed in the enum definition. "false" otherwise func (i ARPAllTargets) IsAARPAllTargets() bool { for _, v := range _ARPAllTargetsValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ARPAllTargets func (i ARPAllTargets) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ARPAllTargets func (i *ARPAllTargets) UnmarshalText(text []byte) error { var err error *i, err = ARPAllTargetsString(string(text)) return err } const _ARPValidateName = "noneactivebackupallfilterfilter-activefilter-backup" var _ARPValidateIndex = [...]uint8{0, 4, 10, 16, 19, 25, 38, 51} const _ARPValidateLowerName = "noneactivebackupallfilterfilter-activefilter-backup" func (i ARPValidate) String() string { if i >= ARPValidate(len(_ARPValidateIndex)-1) { return fmt.Sprintf("ARPValidate(%d)", i) } return _ARPValidateName[_ARPValidateIndex[i]:_ARPValidateIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ARPValidateNoOp() { var x [1]struct{} _ = x[ARPValidateNone-(0)] _ = x[ARPValidateActive-(1)] _ = x[ARPValidateBackup-(2)] _ = x[ARPValidateAll-(3)] _ = x[ARPValidateFilter-(4)] _ = x[ARPValidateFilterActive-(5)] _ = x[ARPValidateFilterBackup-(6)] } var _ARPValidateValues = []ARPValidate{ARPValidateNone, ARPValidateActive, ARPValidateBackup, ARPValidateAll, ARPValidateFilter, ARPValidateFilterActive, ARPValidateFilterBackup} var _ARPValidateNameToValueMap = map[string]ARPValidate{ _ARPValidateName[0:4]: ARPValidateNone, _ARPValidateLowerName[0:4]: ARPValidateNone, _ARPValidateName[4:10]: ARPValidateActive, _ARPValidateLowerName[4:10]: ARPValidateActive, _ARPValidateName[10:16]: ARPValidateBackup, _ARPValidateLowerName[10:16]: ARPValidateBackup, _ARPValidateName[16:19]: ARPValidateAll, _ARPValidateLowerName[16:19]: ARPValidateAll, _ARPValidateName[19:25]: ARPValidateFilter, _ARPValidateLowerName[19:25]: ARPValidateFilter, _ARPValidateName[25:38]: ARPValidateFilterActive, _ARPValidateLowerName[25:38]: ARPValidateFilterActive, _ARPValidateName[38:51]: ARPValidateFilterBackup, _ARPValidateLowerName[38:51]: ARPValidateFilterBackup, } var _ARPValidateNames = []string{ _ARPValidateName[0:4], _ARPValidateName[4:10], _ARPValidateName[10:16], _ARPValidateName[16:19], _ARPValidateName[19:25], _ARPValidateName[25:38], _ARPValidateName[38:51], } // ARPValidateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ARPValidateString(s string) (ARPValidate, error) { if val, ok := _ARPValidateNameToValueMap[s]; ok { return val, nil } if val, ok := _ARPValidateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ARPValidate values", s) } // ARPValidateValues returns all values of the enum func ARPValidateValues() []ARPValidate { return _ARPValidateValues } // ARPValidateStrings returns a slice of all String values of the enum func ARPValidateStrings() []string { strs := make([]string, len(_ARPValidateNames)) copy(strs, _ARPValidateNames) return strs } // IsAARPValidate returns "true" if the value is listed in the enum definition. "false" otherwise func (i ARPValidate) IsAARPValidate() bool { for _, v := range _ARPValidateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ARPValidate func (i ARPValidate) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ARPValidate func (i *ARPValidate) UnmarshalText(text []byte) error { var err error *i, err = ARPValidateString(string(text)) return err } const _AddressFlagName = "temporarynodadoptimisticdadfailedhomeaddressdeprecatedtentativepermanentmngmtmpaddrnoprefixroutemcautojoinstableprivacy" const _AddressFlagLowerName = "temporarynodadoptimisticdadfailedhomeaddressdeprecatedtentativepermanentmngmtmpaddrnoprefixroutemcautojoinstableprivacy" var _AddressFlagMap = map[AddressFlag]string{ 1: _AddressFlagName[0:9], 2: _AddressFlagName[9:14], 4: _AddressFlagName[14:24], 8: _AddressFlagName[24:33], 16: _AddressFlagName[33:44], 32: _AddressFlagName[44:54], 64: _AddressFlagName[54:63], 128: _AddressFlagName[63:72], 256: _AddressFlagName[72:83], 512: _AddressFlagName[83:96], 1024: _AddressFlagName[96:106], 2048: _AddressFlagName[106:119], } func (i AddressFlag) String() string { if str, ok := _AddressFlagMap[i]; ok { return str } return fmt.Sprintf("AddressFlag(%d)", i) } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _AddressFlagNoOp() { var x [1]struct{} _ = x[AddressTemporary-(1)] _ = x[AddressNoDAD-(2)] _ = x[AddressOptimistic-(4)] _ = x[AddressDADFailed-(8)] _ = x[AddressHome-(16)] _ = x[AddressDeprecated-(32)] _ = x[AddressTentative-(64)] _ = x[AddressPermanent-(128)] _ = x[AddressManagementTemp-(256)] _ = x[AddressNoPrefixRoute-(512)] _ = x[AddressMCAutoJoin-(1024)] _ = x[AddressStablePrivacy-(2048)] } var _AddressFlagValues = []AddressFlag{AddressTemporary, AddressNoDAD, AddressOptimistic, AddressDADFailed, AddressHome, AddressDeprecated, AddressTentative, AddressPermanent, AddressManagementTemp, AddressNoPrefixRoute, AddressMCAutoJoin, AddressStablePrivacy} var _AddressFlagNameToValueMap = map[string]AddressFlag{ _AddressFlagName[0:9]: AddressTemporary, _AddressFlagLowerName[0:9]: AddressTemporary, _AddressFlagName[9:14]: AddressNoDAD, _AddressFlagLowerName[9:14]: AddressNoDAD, _AddressFlagName[14:24]: AddressOptimistic, _AddressFlagLowerName[14:24]: AddressOptimistic, _AddressFlagName[24:33]: AddressDADFailed, _AddressFlagLowerName[24:33]: AddressDADFailed, _AddressFlagName[33:44]: AddressHome, _AddressFlagLowerName[33:44]: AddressHome, _AddressFlagName[44:54]: AddressDeprecated, _AddressFlagLowerName[44:54]: AddressDeprecated, _AddressFlagName[54:63]: AddressTentative, _AddressFlagLowerName[54:63]: AddressTentative, _AddressFlagName[63:72]: AddressPermanent, _AddressFlagLowerName[63:72]: AddressPermanent, _AddressFlagName[72:83]: AddressManagementTemp, _AddressFlagLowerName[72:83]: AddressManagementTemp, _AddressFlagName[83:96]: AddressNoPrefixRoute, _AddressFlagLowerName[83:96]: AddressNoPrefixRoute, _AddressFlagName[96:106]: AddressMCAutoJoin, _AddressFlagLowerName[96:106]: AddressMCAutoJoin, _AddressFlagName[106:119]: AddressStablePrivacy, _AddressFlagLowerName[106:119]: AddressStablePrivacy, } var _AddressFlagNames = []string{ _AddressFlagName[0:9], _AddressFlagName[9:14], _AddressFlagName[14:24], _AddressFlagName[24:33], _AddressFlagName[33:44], _AddressFlagName[44:54], _AddressFlagName[54:63], _AddressFlagName[63:72], _AddressFlagName[72:83], _AddressFlagName[83:96], _AddressFlagName[96:106], _AddressFlagName[106:119], } // AddressFlagString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func AddressFlagString(s string) (AddressFlag, error) { if val, ok := _AddressFlagNameToValueMap[s]; ok { return val, nil } if val, ok := _AddressFlagNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to AddressFlag values", s) } // AddressFlagValues returns all values of the enum func AddressFlagValues() []AddressFlag { return _AddressFlagValues } // AddressFlagStrings returns a slice of all String values of the enum func AddressFlagStrings() []string { strs := make([]string, len(_AddressFlagNames)) copy(strs, _AddressFlagNames) return strs } // IsAAddressFlag returns "true" if the value is listed in the enum definition. "false" otherwise func (i AddressFlag) IsAAddressFlag() bool { _, ok := _AddressFlagMap[i] return ok } // MarshalText implements the encoding.TextMarshaler interface for AddressFlag func (i AddressFlag) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for AddressFlag func (i *AddressFlag) UnmarshalText(text []byte) error { var err error *i, err = AddressFlagString(string(text)) return err } const _AddressSortAlgorithmName = "v1v2" var _AddressSortAlgorithmIndex = [...]uint8{0, 2, 4} const _AddressSortAlgorithmLowerName = "v1v2" func (i AddressSortAlgorithm) String() string { if i < 0 || i >= AddressSortAlgorithm(len(_AddressSortAlgorithmIndex)-1) { return fmt.Sprintf("AddressSortAlgorithm(%d)", i) } return _AddressSortAlgorithmName[_AddressSortAlgorithmIndex[i]:_AddressSortAlgorithmIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _AddressSortAlgorithmNoOp() { var x [1]struct{} _ = x[AddressSortAlgorithmV1-(0)] _ = x[AddressSortAlgorithmV2-(1)] } var _AddressSortAlgorithmValues = []AddressSortAlgorithm{AddressSortAlgorithmV1, AddressSortAlgorithmV2} var _AddressSortAlgorithmNameToValueMap = map[string]AddressSortAlgorithm{ _AddressSortAlgorithmName[0:2]: AddressSortAlgorithmV1, _AddressSortAlgorithmLowerName[0:2]: AddressSortAlgorithmV1, _AddressSortAlgorithmName[2:4]: AddressSortAlgorithmV2, _AddressSortAlgorithmLowerName[2:4]: AddressSortAlgorithmV2, } var _AddressSortAlgorithmNames = []string{ _AddressSortAlgorithmName[0:2], _AddressSortAlgorithmName[2:4], } // AddressSortAlgorithmString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func AddressSortAlgorithmString(s string) (AddressSortAlgorithm, error) { if val, ok := _AddressSortAlgorithmNameToValueMap[s]; ok { return val, nil } if val, ok := _AddressSortAlgorithmNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to AddressSortAlgorithm values", s) } // AddressSortAlgorithmValues returns all values of the enum func AddressSortAlgorithmValues() []AddressSortAlgorithm { return _AddressSortAlgorithmValues } // AddressSortAlgorithmStrings returns a slice of all String values of the enum func AddressSortAlgorithmStrings() []string { strs := make([]string, len(_AddressSortAlgorithmNames)) copy(strs, _AddressSortAlgorithmNames) return strs } // IsAAddressSortAlgorithm returns "true" if the value is listed in the enum definition. "false" otherwise func (i AddressSortAlgorithm) IsAAddressSortAlgorithm() bool { for _, v := range _AddressSortAlgorithmValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for AddressSortAlgorithm func (i AddressSortAlgorithm) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for AddressSortAlgorithm func (i *AddressSortAlgorithm) UnmarshalText(text []byte) error { var err error *i, err = AddressSortAlgorithmString(string(text)) return err } const _ADSelectName = "stablebandwidthcount" var _ADSelectIndex = [...]uint8{0, 6, 15, 20} const _ADSelectLowerName = "stablebandwidthcount" func (i ADSelect) String() string { if i >= ADSelect(len(_ADSelectIndex)-1) { return fmt.Sprintf("ADSelect(%d)", i) } return _ADSelectName[_ADSelectIndex[i]:_ADSelectIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ADSelectNoOp() { var x [1]struct{} _ = x[ADSelectStable-(0)] _ = x[ADSelectBandwidth-(1)] _ = x[ADSelectCount-(2)] } var _ADSelectValues = []ADSelect{ADSelectStable, ADSelectBandwidth, ADSelectCount} var _ADSelectNameToValueMap = map[string]ADSelect{ _ADSelectName[0:6]: ADSelectStable, _ADSelectLowerName[0:6]: ADSelectStable, _ADSelectName[6:15]: ADSelectBandwidth, _ADSelectLowerName[6:15]: ADSelectBandwidth, _ADSelectName[15:20]: ADSelectCount, _ADSelectLowerName[15:20]: ADSelectCount, } var _ADSelectNames = []string{ _ADSelectName[0:6], _ADSelectName[6:15], _ADSelectName[15:20], } // ADSelectString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ADSelectString(s string) (ADSelect, error) { if val, ok := _ADSelectNameToValueMap[s]; ok { return val, nil } if val, ok := _ADSelectNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ADSelect values", s) } // ADSelectValues returns all values of the enum func ADSelectValues() []ADSelect { return _ADSelectValues } // ADSelectStrings returns a slice of all String values of the enum func ADSelectStrings() []string { strs := make([]string, len(_ADSelectNames)) copy(strs, _ADSelectNames) return strs } // IsAADSelect returns "true" if the value is listed in the enum definition. "false" otherwise func (i ADSelect) IsAADSelect() bool { for _, v := range _ADSelectValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ADSelect func (i ADSelect) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ADSelect func (i *ADSelect) UnmarshalText(text []byte) error { var err error *i, err = ADSelectString(string(text)) return err } const _ADLACPActiveName = "offon" var _ADLACPActiveIndex = [...]uint8{0, 3, 5} const _ADLACPActiveLowerName = "offon" func (i ADLACPActive) String() string { if i >= ADLACPActive(len(_ADLACPActiveIndex)-1) { return fmt.Sprintf("ADLACPActive(%d)", i) } return _ADLACPActiveName[_ADLACPActiveIndex[i]:_ADLACPActiveIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ADLACPActiveNoOp() { var x [1]struct{} _ = x[ADLACPActiveOff-(0)] _ = x[ADLACPActiveOn-(1)] } var _ADLACPActiveValues = []ADLACPActive{ADLACPActiveOff, ADLACPActiveOn} var _ADLACPActiveNameToValueMap = map[string]ADLACPActive{ _ADLACPActiveName[0:3]: ADLACPActiveOff, _ADLACPActiveLowerName[0:3]: ADLACPActiveOff, _ADLACPActiveName[3:5]: ADLACPActiveOn, _ADLACPActiveLowerName[3:5]: ADLACPActiveOn, } var _ADLACPActiveNames = []string{ _ADLACPActiveName[0:3], _ADLACPActiveName[3:5], } // ADLACPActiveString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ADLACPActiveString(s string) (ADLACPActive, error) { if val, ok := _ADLACPActiveNameToValueMap[s]; ok { return val, nil } if val, ok := _ADLACPActiveNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ADLACPActive values", s) } // ADLACPActiveValues returns all values of the enum func ADLACPActiveValues() []ADLACPActive { return _ADLACPActiveValues } // ADLACPActiveStrings returns a slice of all String values of the enum func ADLACPActiveStrings() []string { strs := make([]string, len(_ADLACPActiveNames)) copy(strs, _ADLACPActiveNames) return strs } // IsAADLACPActive returns "true" if the value is listed in the enum definition. "false" otherwise func (i ADLACPActive) IsAADLACPActive() bool { for _, v := range _ADLACPActiveValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ADLACPActive func (i ADLACPActive) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ADLACPActive func (i *ADLACPActive) UnmarshalText(text []byte) error { var err error *i, err = ADLACPActiveString(string(text)) return err } const _AutoHostnameKindName = "offtalos-addrstable" var _AutoHostnameKindIndex = [...]uint8{0, 3, 13, 19} const _AutoHostnameKindLowerName = "offtalos-addrstable" func (i AutoHostnameKind) String() string { if i >= AutoHostnameKind(len(_AutoHostnameKindIndex)-1) { return fmt.Sprintf("AutoHostnameKind(%d)", i) } return _AutoHostnameKindName[_AutoHostnameKindIndex[i]:_AutoHostnameKindIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _AutoHostnameKindNoOp() { var x [1]struct{} _ = x[AutoHostnameKindOff-(0)] _ = x[AutoHostnameKindAddr-(1)] _ = x[AutoHostnameKindStable-(2)] } var _AutoHostnameKindValues = []AutoHostnameKind{AutoHostnameKindOff, AutoHostnameKindAddr, AutoHostnameKindStable} var _AutoHostnameKindNameToValueMap = map[string]AutoHostnameKind{ _AutoHostnameKindName[0:3]: AutoHostnameKindOff, _AutoHostnameKindLowerName[0:3]: AutoHostnameKindOff, _AutoHostnameKindName[3:13]: AutoHostnameKindAddr, _AutoHostnameKindLowerName[3:13]: AutoHostnameKindAddr, _AutoHostnameKindName[13:19]: AutoHostnameKindStable, _AutoHostnameKindLowerName[13:19]: AutoHostnameKindStable, } var _AutoHostnameKindNames = []string{ _AutoHostnameKindName[0:3], _AutoHostnameKindName[3:13], _AutoHostnameKindName[13:19], } // AutoHostnameKindString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func AutoHostnameKindString(s string) (AutoHostnameKind, error) { if val, ok := _AutoHostnameKindNameToValueMap[s]; ok { return val, nil } if val, ok := _AutoHostnameKindNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to AutoHostnameKind values", s) } // AutoHostnameKindValues returns all values of the enum func AutoHostnameKindValues() []AutoHostnameKind { return _AutoHostnameKindValues } // AutoHostnameKindStrings returns a slice of all String values of the enum func AutoHostnameKindStrings() []string { strs := make([]string, len(_AutoHostnameKindNames)) copy(strs, _AutoHostnameKindNames) return strs } // IsAAutoHostnameKind returns "true" if the value is listed in the enum definition. "false" otherwise func (i AutoHostnameKind) IsAAutoHostnameKind() bool { for _, v := range _AutoHostnameKindValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for AutoHostnameKind func (i AutoHostnameKind) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for AutoHostnameKind func (i *AutoHostnameKind) UnmarshalText(text []byte) error { var err error *i, err = AutoHostnameKindString(string(text)) return err } const _BondModeName = "balance-rractive-backupbalance-xorbroadcast802.3adbalance-tlbbalance-alb" var _BondModeIndex = [...]uint8{0, 10, 23, 34, 43, 50, 61, 72} const _BondModeLowerName = "balance-rractive-backupbalance-xorbroadcast802.3adbalance-tlbbalance-alb" func (i BondMode) String() string { if i >= BondMode(len(_BondModeIndex)-1) { return fmt.Sprintf("BondMode(%d)", i) } return _BondModeName[_BondModeIndex[i]:_BondModeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _BondModeNoOp() { var x [1]struct{} _ = x[BondModeRoundrobin-(0)] _ = x[BondModeActiveBackup-(1)] _ = x[BondModeXOR-(2)] _ = x[BondModeBroadcast-(3)] _ = x[BondMode8023AD-(4)] _ = x[BondModeTLB-(5)] _ = x[BondModeALB-(6)] } var _BondModeValues = []BondMode{BondModeRoundrobin, BondModeActiveBackup, BondModeXOR, BondModeBroadcast, BondMode8023AD, BondModeTLB, BondModeALB} var _BondModeNameToValueMap = map[string]BondMode{ _BondModeName[0:10]: BondModeRoundrobin, _BondModeLowerName[0:10]: BondModeRoundrobin, _BondModeName[10:23]: BondModeActiveBackup, _BondModeLowerName[10:23]: BondModeActiveBackup, _BondModeName[23:34]: BondModeXOR, _BondModeLowerName[23:34]: BondModeXOR, _BondModeName[34:43]: BondModeBroadcast, _BondModeLowerName[34:43]: BondModeBroadcast, _BondModeName[43:50]: BondMode8023AD, _BondModeLowerName[43:50]: BondMode8023AD, _BondModeName[50:61]: BondModeTLB, _BondModeLowerName[50:61]: BondModeTLB, _BondModeName[61:72]: BondModeALB, _BondModeLowerName[61:72]: BondModeALB, } var _BondModeNames = []string{ _BondModeName[0:10], _BondModeName[10:23], _BondModeName[23:34], _BondModeName[34:43], _BondModeName[43:50], _BondModeName[50:61], _BondModeName[61:72], } // BondModeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func BondModeString(s string) (BondMode, error) { if val, ok := _BondModeNameToValueMap[s]; ok { return val, nil } if val, ok := _BondModeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to BondMode values", s) } // BondModeValues returns all values of the enum func BondModeValues() []BondMode { return _BondModeValues } // BondModeStrings returns a slice of all String values of the enum func BondModeStrings() []string { strs := make([]string, len(_BondModeNames)) copy(strs, _BondModeNames) return strs } // IsABondMode returns "true" if the value is listed in the enum definition. "false" otherwise func (i BondMode) IsABondMode() bool { for _, v := range _BondModeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for BondMode func (i BondMode) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for BondMode func (i *BondMode) UnmarshalText(text []byte) error { var err error *i, err = BondModeString(string(text)) return err } const _BondXmitHashPolicyName = "layer2layer3+4layer2+3encap2+3encap3+4" var _BondXmitHashPolicyIndex = [...]uint8{0, 6, 14, 22, 30, 38} const _BondXmitHashPolicyLowerName = "layer2layer3+4layer2+3encap2+3encap3+4" func (i BondXmitHashPolicy) String() string { if i >= BondXmitHashPolicy(len(_BondXmitHashPolicyIndex)-1) { return fmt.Sprintf("BondXmitHashPolicy(%d)", i) } return _BondXmitHashPolicyName[_BondXmitHashPolicyIndex[i]:_BondXmitHashPolicyIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _BondXmitHashPolicyNoOp() { var x [1]struct{} _ = x[BondXmitPolicyLayer2-(0)] _ = x[BondXmitPolicyLayer34-(1)] _ = x[BondXmitPolicyLayer23-(2)] _ = x[BondXmitPolicyEncap23-(3)] _ = x[BondXmitPolicyEncap34-(4)] } var _BondXmitHashPolicyValues = []BondXmitHashPolicy{BondXmitPolicyLayer2, BondXmitPolicyLayer34, BondXmitPolicyLayer23, BondXmitPolicyEncap23, BondXmitPolicyEncap34} var _BondXmitHashPolicyNameToValueMap = map[string]BondXmitHashPolicy{ _BondXmitHashPolicyName[0:6]: BondXmitPolicyLayer2, _BondXmitHashPolicyLowerName[0:6]: BondXmitPolicyLayer2, _BondXmitHashPolicyName[6:14]: BondXmitPolicyLayer34, _BondXmitHashPolicyLowerName[6:14]: BondXmitPolicyLayer34, _BondXmitHashPolicyName[14:22]: BondXmitPolicyLayer23, _BondXmitHashPolicyLowerName[14:22]: BondXmitPolicyLayer23, _BondXmitHashPolicyName[22:30]: BondXmitPolicyEncap23, _BondXmitHashPolicyLowerName[22:30]: BondXmitPolicyEncap23, _BondXmitHashPolicyName[30:38]: BondXmitPolicyEncap34, _BondXmitHashPolicyLowerName[30:38]: BondXmitPolicyEncap34, } var _BondXmitHashPolicyNames = []string{ _BondXmitHashPolicyName[0:6], _BondXmitHashPolicyName[6:14], _BondXmitHashPolicyName[14:22], _BondXmitHashPolicyName[22:30], _BondXmitHashPolicyName[30:38], } // BondXmitHashPolicyString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func BondXmitHashPolicyString(s string) (BondXmitHashPolicy, error) { if val, ok := _BondXmitHashPolicyNameToValueMap[s]; ok { return val, nil } if val, ok := _BondXmitHashPolicyNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to BondXmitHashPolicy values", s) } // BondXmitHashPolicyValues returns all values of the enum func BondXmitHashPolicyValues() []BondXmitHashPolicy { return _BondXmitHashPolicyValues } // BondXmitHashPolicyStrings returns a slice of all String values of the enum func BondXmitHashPolicyStrings() []string { strs := make([]string, len(_BondXmitHashPolicyNames)) copy(strs, _BondXmitHashPolicyNames) return strs } // IsABondXmitHashPolicy returns "true" if the value is listed in the enum definition. "false" otherwise func (i BondXmitHashPolicy) IsABondXmitHashPolicy() bool { for _, v := range _BondXmitHashPolicyValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for BondXmitHashPolicy func (i BondXmitHashPolicy) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for BondXmitHashPolicy func (i *BondXmitHashPolicy) UnmarshalText(text []byte) error { var err error *i, err = BondXmitHashPolicyString(string(text)) return err } const _ClientIdentifierName = "nonemacduid" var _ClientIdentifierIndex = [...]uint8{0, 4, 7, 11} const _ClientIdentifierLowerName = "nonemacduid" func (i ClientIdentifier) String() string { if i < 0 || i >= ClientIdentifier(len(_ClientIdentifierIndex)-1) { return fmt.Sprintf("ClientIdentifier(%d)", i) } return _ClientIdentifierName[_ClientIdentifierIndex[i]:_ClientIdentifierIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ClientIdentifierNoOp() { var x [1]struct{} _ = x[ClientIdentifierNone-(0)] _ = x[ClientIdentifierMAC-(1)] _ = x[ClientIdentifierDUID-(2)] } var _ClientIdentifierValues = []ClientIdentifier{ClientIdentifierNone, ClientIdentifierMAC, ClientIdentifierDUID} var _ClientIdentifierNameToValueMap = map[string]ClientIdentifier{ _ClientIdentifierName[0:4]: ClientIdentifierNone, _ClientIdentifierLowerName[0:4]: ClientIdentifierNone, _ClientIdentifierName[4:7]: ClientIdentifierMAC, _ClientIdentifierLowerName[4:7]: ClientIdentifierMAC, _ClientIdentifierName[7:11]: ClientIdentifierDUID, _ClientIdentifierLowerName[7:11]: ClientIdentifierDUID, } var _ClientIdentifierNames = []string{ _ClientIdentifierName[0:4], _ClientIdentifierName[4:7], _ClientIdentifierName[7:11], } // ClientIdentifierString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ClientIdentifierString(s string) (ClientIdentifier, error) { if val, ok := _ClientIdentifierNameToValueMap[s]; ok { return val, nil } if val, ok := _ClientIdentifierNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ClientIdentifier values", s) } // ClientIdentifierValues returns all values of the enum func ClientIdentifierValues() []ClientIdentifier { return _ClientIdentifierValues } // ClientIdentifierStrings returns a slice of all String values of the enum func ClientIdentifierStrings() []string { strs := make([]string, len(_ClientIdentifierNames)) copy(strs, _ClientIdentifierNames) return strs } // IsAClientIdentifier returns "true" if the value is listed in the enum definition. "false" otherwise func (i ClientIdentifier) IsAClientIdentifier() bool { for _, v := range _ClientIdentifierValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ClientIdentifier func (i ClientIdentifier) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ClientIdentifier func (i *ClientIdentifier) UnmarshalText(text []byte) error { var err error *i, err = ClientIdentifierString(string(text)) return err } const ( _ConntrackStateName_0 = "invalidestablished" _ConntrackStateLowerName_0 = "invalidestablished" _ConntrackStateName_1 = "related" _ConntrackStateLowerName_1 = "related" _ConntrackStateName_2 = "new" _ConntrackStateLowerName_2 = "new" ) var ( _ConntrackStateIndex_0 = [...]uint8{0, 7, 18} _ConntrackStateIndex_1 = [...]uint8{0, 7} _ConntrackStateIndex_2 = [...]uint8{0, 3} ) func (i ConntrackState) String() string { switch { case 1 <= i && i <= 2: i -= 1 return _ConntrackStateName_0[_ConntrackStateIndex_0[i]:_ConntrackStateIndex_0[i+1]] case i == 4: return _ConntrackStateName_1 case i == 8: return _ConntrackStateName_2 default: return fmt.Sprintf("ConntrackState(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ConntrackStateNoOp() { var x [1]struct{} _ = x[ConntrackStateInvalid-(1)] _ = x[ConntrackStateEstablished-(2)] _ = x[ConntrackStateRelated-(4)] _ = x[ConntrackStateNew-(8)] } var _ConntrackStateValues = []ConntrackState{ConntrackStateInvalid, ConntrackStateEstablished, ConntrackStateRelated, ConntrackStateNew} var _ConntrackStateNameToValueMap = map[string]ConntrackState{ _ConntrackStateName_0[0:7]: ConntrackStateInvalid, _ConntrackStateLowerName_0[0:7]: ConntrackStateInvalid, _ConntrackStateName_0[7:18]: ConntrackStateEstablished, _ConntrackStateLowerName_0[7:18]: ConntrackStateEstablished, _ConntrackStateName_1[0:7]: ConntrackStateRelated, _ConntrackStateLowerName_1[0:7]: ConntrackStateRelated, _ConntrackStateName_2[0:3]: ConntrackStateNew, _ConntrackStateLowerName_2[0:3]: ConntrackStateNew, } var _ConntrackStateNames = []string{ _ConntrackStateName_0[0:7], _ConntrackStateName_0[7:18], _ConntrackStateName_1[0:7], _ConntrackStateName_2[0:3], } // ConntrackStateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ConntrackStateString(s string) (ConntrackState, error) { if val, ok := _ConntrackStateNameToValueMap[s]; ok { return val, nil } if val, ok := _ConntrackStateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ConntrackState values", s) } // ConntrackStateValues returns all values of the enum func ConntrackStateValues() []ConntrackState { return _ConntrackStateValues } // ConntrackStateStrings returns a slice of all String values of the enum func ConntrackStateStrings() []string { strs := make([]string, len(_ConntrackStateNames)) copy(strs, _ConntrackStateNames) return strs } // IsAConntrackState returns "true" if the value is listed in the enum definition. "false" otherwise func (i ConntrackState) IsAConntrackState() bool { for _, v := range _ConntrackStateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ConntrackState func (i ConntrackState) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ConntrackState func (i *ConntrackState) UnmarshalText(text []byte) error { var err error *i, err = ConntrackStateString(string(text)) return err } const _DefaultActionName = "acceptblock" var _DefaultActionIndex = [...]uint8{0, 6, 11} const _DefaultActionLowerName = "acceptblock" func (i DefaultAction) String() string { if i < 0 || i >= DefaultAction(len(_DefaultActionIndex)-1) { return fmt.Sprintf("DefaultAction(%d)", i) } return _DefaultActionName[_DefaultActionIndex[i]:_DefaultActionIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _DefaultActionNoOp() { var x [1]struct{} _ = x[DefaultActionAccept-(0)] _ = x[DefaultActionBlock-(1)] } var _DefaultActionValues = []DefaultAction{DefaultActionAccept, DefaultActionBlock} var _DefaultActionNameToValueMap = map[string]DefaultAction{ _DefaultActionName[0:6]: DefaultActionAccept, _DefaultActionLowerName[0:6]: DefaultActionAccept, _DefaultActionName[6:11]: DefaultActionBlock, _DefaultActionLowerName[6:11]: DefaultActionBlock, } var _DefaultActionNames = []string{ _DefaultActionName[0:6], _DefaultActionName[6:11], } // DefaultActionString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func DefaultActionString(s string) (DefaultAction, error) { if val, ok := _DefaultActionNameToValueMap[s]; ok { return val, nil } if val, ok := _DefaultActionNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to DefaultAction values", s) } // DefaultActionValues returns all values of the enum func DefaultActionValues() []DefaultAction { return _DefaultActionValues } // DefaultActionStrings returns a slice of all String values of the enum func DefaultActionStrings() []string { strs := make([]string, len(_DefaultActionNames)) copy(strs, _DefaultActionNames) return strs } // IsADefaultAction returns "true" if the value is listed in the enum definition. "false" otherwise func (i DefaultAction) IsADefaultAction() bool { for _, v := range _DefaultActionValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for DefaultAction func (i DefaultAction) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for DefaultAction func (i *DefaultAction) UnmarshalText(text []byte) error { var err error *i, err = DefaultActionString(string(text)) return err } const ( _DuplexName_0 = "HalfFull" _DuplexLowerName_0 = "halffull" _DuplexName_1 = "Unknown" _DuplexLowerName_1 = "unknown" ) var ( _DuplexIndex_0 = [...]uint8{0, 4, 8} _DuplexIndex_1 = [...]uint8{0, 7} ) func (i Duplex) String() string { switch { case 0 <= i && i <= 1: return _DuplexName_0[_DuplexIndex_0[i]:_DuplexIndex_0[i+1]] case i == 255: return _DuplexName_1 default: return fmt.Sprintf("Duplex(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _DuplexNoOp() { var x [1]struct{} _ = x[Half-(0)] _ = x[Full-(1)] _ = x[Unknown-(255)] } var _DuplexValues = []Duplex{Half, Full, Unknown} var _DuplexNameToValueMap = map[string]Duplex{ _DuplexName_0[0:4]: Half, _DuplexLowerName_0[0:4]: Half, _DuplexName_0[4:8]: Full, _DuplexLowerName_0[4:8]: Full, _DuplexName_1[0:7]: Unknown, _DuplexLowerName_1[0:7]: Unknown, } var _DuplexNames = []string{ _DuplexName_0[0:4], _DuplexName_0[4:8], _DuplexName_1[0:7], } // DuplexString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func DuplexString(s string) (Duplex, error) { if val, ok := _DuplexNameToValueMap[s]; ok { return val, nil } if val, ok := _DuplexNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Duplex values", s) } // DuplexValues returns all values of the enum func DuplexValues() []Duplex { return _DuplexValues } // DuplexStrings returns a slice of all String values of the enum func DuplexStrings() []string { strs := make([]string, len(_DuplexNames)) copy(strs, _DuplexNames) return strs } // IsADuplex returns "true" if the value is listed in the enum definition. "false" otherwise func (i Duplex) IsADuplex() bool { for _, v := range _DuplexValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Duplex func (i Duplex) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Duplex func (i *Duplex) UnmarshalText(text []byte) error { var err error *i, err = DuplexString(string(text)) return err } const ( _FamilyName_0 = "inet4" _FamilyLowerName_0 = "inet4" _FamilyName_1 = "inet6" _FamilyLowerName_1 = "inet6" ) var ( _FamilyIndex_0 = [...]uint8{0, 5} _FamilyIndex_1 = [...]uint8{0, 5} ) func (i Family) String() string { switch { case i == 2: return _FamilyName_0 case i == 10: return _FamilyName_1 default: return fmt.Sprintf("Family(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _FamilyNoOp() { var x [1]struct{} _ = x[FamilyInet4-(2)] _ = x[FamilyInet6-(10)] } var _FamilyValues = []Family{FamilyInet4, FamilyInet6} var _FamilyNameToValueMap = map[string]Family{ _FamilyName_0[0:5]: FamilyInet4, _FamilyLowerName_0[0:5]: FamilyInet4, _FamilyName_1[0:5]: FamilyInet6, _FamilyLowerName_1[0:5]: FamilyInet6, } var _FamilyNames = []string{ _FamilyName_0[0:5], _FamilyName_1[0:5], } // FamilyString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func FamilyString(s string) (Family, error) { if val, ok := _FamilyNameToValueMap[s]; ok { return val, nil } if val, ok := _FamilyNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Family values", s) } // FamilyValues returns all values of the enum func FamilyValues() []Family { return _FamilyValues } // FamilyStrings returns a slice of all String values of the enum func FamilyStrings() []string { strs := make([]string, len(_FamilyNames)) copy(strs, _FamilyNames) return strs } // IsAFamily returns "true" if the value is listed in the enum definition. "false" otherwise func (i Family) IsAFamily() bool { for _, v := range _FamilyValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Family func (i Family) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Family func (i *Family) UnmarshalText(text []byte) error { var err error *i, err = FamilyString(string(text)) return err } const _LACPRateName = "slowfast" var _LACPRateIndex = [...]uint8{0, 4, 8} const _LACPRateLowerName = "slowfast" func (i LACPRate) String() string { if i >= LACPRate(len(_LACPRateIndex)-1) { return fmt.Sprintf("LACPRate(%d)", i) } return _LACPRateName[_LACPRateIndex[i]:_LACPRateIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _LACPRateNoOp() { var x [1]struct{} _ = x[LACPRateSlow-(0)] _ = x[LACPRateFast-(1)] } var _LACPRateValues = []LACPRate{LACPRateSlow, LACPRateFast} var _LACPRateNameToValueMap = map[string]LACPRate{ _LACPRateName[0:4]: LACPRateSlow, _LACPRateLowerName[0:4]: LACPRateSlow, _LACPRateName[4:8]: LACPRateFast, _LACPRateLowerName[4:8]: LACPRateFast, } var _LACPRateNames = []string{ _LACPRateName[0:4], _LACPRateName[4:8], } // LACPRateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func LACPRateString(s string) (LACPRate, error) { if val, ok := _LACPRateNameToValueMap[s]; ok { return val, nil } if val, ok := _LACPRateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to LACPRate values", s) } // LACPRateValues returns all values of the enum func LACPRateValues() []LACPRate { return _LACPRateValues } // LACPRateStrings returns a slice of all String values of the enum func LACPRateStrings() []string { strs := make([]string, len(_LACPRateNames)) copy(strs, _LACPRateNames) return strs } // IsALACPRate returns "true" if the value is listed in the enum definition. "false" otherwise func (i LACPRate) IsALACPRate() bool { for _, v := range _LACPRateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for LACPRate func (i LACPRate) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for LACPRate func (i *LACPRate) UnmarshalText(text []byte) error { var err error *i, err = LACPRateString(string(text)) return err } const _LinkFlagName = "UPBROADCASTDEBUGLOOPBACKPOINTTOPOINTNOTRAILERSRUNNINGNOARPPROMISCALLMULTIMASTERSLAVEMULTICASTPORTSELAUTOMEDIADYNAMICLOWER_UPDORMANTECHO" const _LinkFlagLowerName = "upbroadcastdebugloopbackpointtopointnotrailersrunningnoarppromiscallmultimasterslavemulticastportselautomediadynamiclower_updormantecho" var _LinkFlagMap = map[LinkFlag]string{ 1: _LinkFlagName[0:2], 2: _LinkFlagName[2:11], 4: _LinkFlagName[11:16], 8: _LinkFlagName[16:24], 16: _LinkFlagName[24:36], 32: _LinkFlagName[36:46], 64: _LinkFlagName[46:53], 128: _LinkFlagName[53:58], 256: _LinkFlagName[58:65], 512: _LinkFlagName[65:73], 1024: _LinkFlagName[73:79], 2048: _LinkFlagName[79:84], 4096: _LinkFlagName[84:93], 8192: _LinkFlagName[93:100], 16384: _LinkFlagName[100:109], 32768: _LinkFlagName[109:116], 65536: _LinkFlagName[116:124], 131072: _LinkFlagName[124:131], 262144: _LinkFlagName[131:135], } func (i LinkFlag) String() string { if str, ok := _LinkFlagMap[i]; ok { return str } return fmt.Sprintf("LinkFlag(%d)", i) } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _LinkFlagNoOp() { var x [1]struct{} _ = x[LinkUp-(1)] _ = x[LinkBroadcast-(2)] _ = x[LinkDebug-(4)] _ = x[LinkLoopback-(8)] _ = x[LinkPointToPoint-(16)] _ = x[LinkNoTrailers-(32)] _ = x[LinkRunning-(64)] _ = x[LinkNoArp-(128)] _ = x[LinkPromisc-(256)] _ = x[LinkAllMulti-(512)] _ = x[LinkMaster-(1024)] _ = x[LinkSlave-(2048)] _ = x[LinkMulticast-(4096)] _ = x[LinkPortsel-(8192)] _ = x[LinKAutoMedia-(16384)] _ = x[LinkDynamic-(32768)] _ = x[LinkLowerUp-(65536)] _ = x[LinkDormant-(131072)] _ = x[LinkEcho-(262144)] } var _LinkFlagValues = []LinkFlag{LinkUp, LinkBroadcast, LinkDebug, LinkLoopback, LinkPointToPoint, LinkNoTrailers, LinkRunning, LinkNoArp, LinkPromisc, LinkAllMulti, LinkMaster, LinkSlave, LinkMulticast, LinkPortsel, LinKAutoMedia, LinkDynamic, LinkLowerUp, LinkDormant, LinkEcho} var _LinkFlagNameToValueMap = map[string]LinkFlag{ _LinkFlagName[0:2]: LinkUp, _LinkFlagLowerName[0:2]: LinkUp, _LinkFlagName[2:11]: LinkBroadcast, _LinkFlagLowerName[2:11]: LinkBroadcast, _LinkFlagName[11:16]: LinkDebug, _LinkFlagLowerName[11:16]: LinkDebug, _LinkFlagName[16:24]: LinkLoopback, _LinkFlagLowerName[16:24]: LinkLoopback, _LinkFlagName[24:36]: LinkPointToPoint, _LinkFlagLowerName[24:36]: LinkPointToPoint, _LinkFlagName[36:46]: LinkNoTrailers, _LinkFlagLowerName[36:46]: LinkNoTrailers, _LinkFlagName[46:53]: LinkRunning, _LinkFlagLowerName[46:53]: LinkRunning, _LinkFlagName[53:58]: LinkNoArp, _LinkFlagLowerName[53:58]: LinkNoArp, _LinkFlagName[58:65]: LinkPromisc, _LinkFlagLowerName[58:65]: LinkPromisc, _LinkFlagName[65:73]: LinkAllMulti, _LinkFlagLowerName[65:73]: LinkAllMulti, _LinkFlagName[73:79]: LinkMaster, _LinkFlagLowerName[73:79]: LinkMaster, _LinkFlagName[79:84]: LinkSlave, _LinkFlagLowerName[79:84]: LinkSlave, _LinkFlagName[84:93]: LinkMulticast, _LinkFlagLowerName[84:93]: LinkMulticast, _LinkFlagName[93:100]: LinkPortsel, _LinkFlagLowerName[93:100]: LinkPortsel, _LinkFlagName[100:109]: LinKAutoMedia, _LinkFlagLowerName[100:109]: LinKAutoMedia, _LinkFlagName[109:116]: LinkDynamic, _LinkFlagLowerName[109:116]: LinkDynamic, _LinkFlagName[116:124]: LinkLowerUp, _LinkFlagLowerName[116:124]: LinkLowerUp, _LinkFlagName[124:131]: LinkDormant, _LinkFlagLowerName[124:131]: LinkDormant, _LinkFlagName[131:135]: LinkEcho, _LinkFlagLowerName[131:135]: LinkEcho, } var _LinkFlagNames = []string{ _LinkFlagName[0:2], _LinkFlagName[2:11], _LinkFlagName[11:16], _LinkFlagName[16:24], _LinkFlagName[24:36], _LinkFlagName[36:46], _LinkFlagName[46:53], _LinkFlagName[53:58], _LinkFlagName[58:65], _LinkFlagName[65:73], _LinkFlagName[73:79], _LinkFlagName[79:84], _LinkFlagName[84:93], _LinkFlagName[93:100], _LinkFlagName[100:109], _LinkFlagName[109:116], _LinkFlagName[116:124], _LinkFlagName[124:131], _LinkFlagName[131:135], } // LinkFlagString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func LinkFlagString(s string) (LinkFlag, error) { if val, ok := _LinkFlagNameToValueMap[s]; ok { return val, nil } if val, ok := _LinkFlagNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to LinkFlag values", s) } // LinkFlagValues returns all values of the enum func LinkFlagValues() []LinkFlag { return _LinkFlagValues } // LinkFlagStrings returns a slice of all String values of the enum func LinkFlagStrings() []string { strs := make([]string, len(_LinkFlagNames)) copy(strs, _LinkFlagNames) return strs } // IsALinkFlag returns "true" if the value is listed in the enum definition. "false" otherwise func (i LinkFlag) IsALinkFlag() bool { _, ok := _LinkFlagMap[i] return ok } // MarshalText implements the encoding.TextMarshaler interface for LinkFlag func (i LinkFlag) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for LinkFlag func (i *LinkFlag) UnmarshalText(text []byte) error { var err error *i, err = LinkFlagString(string(text)) return err } const _LinkTypeName = "netromethereetherax25pronetchaosieee802arcnetatalkdlciatmmetricomieee1394eui64infinibandslipcslipslip6cslip6rsrvdadaptrosex25hwx25canpppciscolapbddcmprawhdlcipiptunnel6fradskiploopbacklocaltlkfddibifsitip/ddpgrepimreghippiasheconetirdafcppfcalfcplfcfb_0fcfb_1fcfb_2fcfb_3fcfb_4fcfb_5fcfb_6fcfb_7fcfb_8fcfb_9fcfb_10fcfb_11fcfb_12trieee802.11ieee802.11_prismieee802.11_radiotapieee802.15.4ieee802.15.4_monitorphonetphonet_pipecaifip6grenetlink6lowpannohdrvoid" const _LinkTypeLowerName = "netromethereetherax25pronetchaosieee802arcnetatalkdlciatmmetricomieee1394eui64infinibandslipcslipslip6cslip6rsrvdadaptrosex25hwx25canpppciscolapbddcmprawhdlcipiptunnel6fradskiploopbacklocaltlkfddibifsitip/ddpgrepimreghippiasheconetirdafcppfcalfcplfcfb_0fcfb_1fcfb_2fcfb_3fcfb_4fcfb_5fcfb_6fcfb_7fcfb_8fcfb_9fcfb_10fcfb_11fcfb_12trieee802.11ieee802.11_prismieee802.11_radiotapieee802.15.4ieee802.15.4_monitorphonetphonet_pipecaifip6grenetlink6lowpannohdrvoid" var _LinkTypeMap = map[LinkType]string{ 0: _LinkTypeName[0:6], 1: _LinkTypeName[6:11], 2: _LinkTypeName[11:17], 3: _LinkTypeName[17:21], 4: _LinkTypeName[21:27], 5: _LinkTypeName[27:32], 6: _LinkTypeName[32:39], 7: _LinkTypeName[39:45], 8: _LinkTypeName[45:50], 15: _LinkTypeName[50:54], 19: _LinkTypeName[54:57], 23: _LinkTypeName[57:65], 24: _LinkTypeName[65:73], 27: _LinkTypeName[73:78], 32: _LinkTypeName[78:88], 256: _LinkTypeName[88:92], 257: _LinkTypeName[92:97], 258: _LinkTypeName[97:102], 259: _LinkTypeName[102:108], 260: _LinkTypeName[108:113], 264: _LinkTypeName[113:118], 270: _LinkTypeName[118:122], 271: _LinkTypeName[122:125], 272: _LinkTypeName[125:130], 280: _LinkTypeName[130:133], 512: _LinkTypeName[133:136], 513: _LinkTypeName[136:141], 516: _LinkTypeName[141:145], 517: _LinkTypeName[145:150], 518: _LinkTypeName[150:157], 768: _LinkTypeName[157:161], 769: _LinkTypeName[161:168], 770: _LinkTypeName[168:172], 771: _LinkTypeName[172:176], 772: _LinkTypeName[176:184], 773: _LinkTypeName[184:192], 774: _LinkTypeName[192:196], 775: _LinkTypeName[196:199], 776: _LinkTypeName[199:202], 777: _LinkTypeName[202:208], 778: _LinkTypeName[208:211], 779: _LinkTypeName[211:217], 780: _LinkTypeName[217:222], 781: _LinkTypeName[222:225], 782: _LinkTypeName[225:231], 783: _LinkTypeName[231:235], 784: _LinkTypeName[235:239], 785: _LinkTypeName[239:243], 786: _LinkTypeName[243:247], 787: _LinkTypeName[247:253], 788: _LinkTypeName[253:259], 789: _LinkTypeName[259:265], 790: _LinkTypeName[265:271], 791: _LinkTypeName[271:277], 792: _LinkTypeName[277:283], 793: _LinkTypeName[283:289], 794: _LinkTypeName[289:295], 795: _LinkTypeName[295:301], 796: _LinkTypeName[301:307], 797: _LinkTypeName[307:314], 798: _LinkTypeName[314:321], 799: _LinkTypeName[321:328], 800: _LinkTypeName[328:330], 801: _LinkTypeName[330:340], 802: _LinkTypeName[340:356], 803: _LinkTypeName[356:375], 804: _LinkTypeName[375:387], 805: _LinkTypeName[387:407], 820: _LinkTypeName[407:413], 821: _LinkTypeName[413:424], 822: _LinkTypeName[424:428], 823: _LinkTypeName[428:434], 824: _LinkTypeName[434:441], 825: _LinkTypeName[441:448], 65534: _LinkTypeName[448:453], 65535: _LinkTypeName[453:457], } func (i LinkType) String() string { if str, ok := _LinkTypeMap[i]; ok { return str } return fmt.Sprintf("LinkType(%d)", i) } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _LinkTypeNoOp() { var x [1]struct{} _ = x[LinkNetrom-(0)] _ = x[LinkEther-(1)] _ = x[LinkEether-(2)] _ = x[LinkAx25-(3)] _ = x[LinkPronet-(4)] _ = x[LinkChaos-(5)] _ = x[LinkIee802-(6)] _ = x[LinkArcnet-(7)] _ = x[LinkAtalk-(8)] _ = x[LinkDlci-(15)] _ = x[LinkAtm-(19)] _ = x[LinkMetricom-(23)] _ = x[LinkIeee1394-(24)] _ = x[LinkEui64-(27)] _ = x[LinkInfiniband-(32)] _ = x[LinkSlip-(256)] _ = x[LinkCslip-(257)] _ = x[LinkSlip6-(258)] _ = x[LinkCslip6-(259)] _ = x[LinkRsrvd-(260)] _ = x[LinkAdapt-(264)] _ = x[LinkRose-(270)] _ = x[LinkX25-(271)] _ = x[LinkHwx25-(272)] _ = x[LinkCan-(280)] _ = x[LinkPpp-(512)] _ = x[LinkCisco-(513)] _ = x[LinkLapb-(516)] _ = x[LinkDdcmp-(517)] _ = x[LinkRawhdlc-(518)] _ = x[LinkTunnel-(768)] _ = x[LinkTunnel6-(769)] _ = x[LinkFrad-(770)] _ = x[LinkSkip-(771)] _ = x[LinkLoopbck-(772)] _ = x[LinkLocaltlk-(773)] _ = x[LinkFddi-(774)] _ = x[LinkBif-(775)] _ = x[LinkSit-(776)] _ = x[LinkIpddp-(777)] _ = x[LinkIpgre-(778)] _ = x[LinkPimreg-(779)] _ = x[LinkHippi-(780)] _ = x[LinkAsh-(781)] _ = x[LinkEconet-(782)] _ = x[LinkIrda-(783)] _ = x[LinkFcpp-(784)] _ = x[LinkFcal-(785)] _ = x[LinkFcpl-(786)] _ = x[LinkFcfabric-(787)] _ = x[LinkFcfabric1-(788)] _ = x[LinkFcfabric2-(789)] _ = x[LinkFcfabric3-(790)] _ = x[LinkFcfabric4-(791)] _ = x[LinkFcfabric5-(792)] _ = x[LinkFcfabric6-(793)] _ = x[LinkFcfabric7-(794)] _ = x[LinkFcfabric8-(795)] _ = x[LinkFcfabric9-(796)] _ = x[LinkFcfabric10-(797)] _ = x[LinkFcfabric11-(798)] _ = x[LinkFcfabric12-(799)] _ = x[LinkIee802tr-(800)] _ = x[LinkIee80211-(801)] _ = x[LinkIee80211prism-(802)] _ = x[LinkIee80211Radiotap-(803)] _ = x[LinkIee8021154-(804)] _ = x[LinkIee8021154monitor-(805)] _ = x[LinkPhonet-(820)] _ = x[LinkPhonetpipe-(821)] _ = x[LinkCaif-(822)] _ = x[LinkIP6gre-(823)] _ = x[LinkNetlink-(824)] _ = x[Link6Lowpan-(825)] _ = x[LinkNone-(65534)] _ = x[LinkVoid-(65535)] } var _LinkTypeValues = []LinkType{LinkNetrom, LinkEther, LinkEether, LinkAx25, LinkPronet, LinkChaos, LinkIee802, LinkArcnet, LinkAtalk, LinkDlci, LinkAtm, LinkMetricom, LinkIeee1394, LinkEui64, LinkInfiniband, LinkSlip, LinkCslip, LinkSlip6, LinkCslip6, LinkRsrvd, LinkAdapt, LinkRose, LinkX25, LinkHwx25, LinkCan, LinkPpp, LinkCisco, LinkLapb, LinkDdcmp, LinkRawhdlc, LinkTunnel, LinkTunnel6, LinkFrad, LinkSkip, LinkLoopbck, LinkLocaltlk, LinkFddi, LinkBif, LinkSit, LinkIpddp, LinkIpgre, LinkPimreg, LinkHippi, LinkAsh, LinkEconet, LinkIrda, LinkFcpp, LinkFcal, LinkFcpl, LinkFcfabric, LinkFcfabric1, LinkFcfabric2, LinkFcfabric3, LinkFcfabric4, LinkFcfabric5, LinkFcfabric6, LinkFcfabric7, LinkFcfabric8, LinkFcfabric9, LinkFcfabric10, LinkFcfabric11, LinkFcfabric12, LinkIee802tr, LinkIee80211, LinkIee80211prism, LinkIee80211Radiotap, LinkIee8021154, LinkIee8021154monitor, LinkPhonet, LinkPhonetpipe, LinkCaif, LinkIP6gre, LinkNetlink, Link6Lowpan, LinkNone, LinkVoid} var _LinkTypeNameToValueMap = map[string]LinkType{ _LinkTypeName[0:6]: LinkNetrom, _LinkTypeLowerName[0:6]: LinkNetrom, _LinkTypeName[6:11]: LinkEther, _LinkTypeLowerName[6:11]: LinkEther, _LinkTypeName[11:17]: LinkEether, _LinkTypeLowerName[11:17]: LinkEether, _LinkTypeName[17:21]: LinkAx25, _LinkTypeLowerName[17:21]: LinkAx25, _LinkTypeName[21:27]: LinkPronet, _LinkTypeLowerName[21:27]: LinkPronet, _LinkTypeName[27:32]: LinkChaos, _LinkTypeLowerName[27:32]: LinkChaos, _LinkTypeName[32:39]: LinkIee802, _LinkTypeLowerName[32:39]: LinkIee802, _LinkTypeName[39:45]: LinkArcnet, _LinkTypeLowerName[39:45]: LinkArcnet, _LinkTypeName[45:50]: LinkAtalk, _LinkTypeLowerName[45:50]: LinkAtalk, _LinkTypeName[50:54]: LinkDlci, _LinkTypeLowerName[50:54]: LinkDlci, _LinkTypeName[54:57]: LinkAtm, _LinkTypeLowerName[54:57]: LinkAtm, _LinkTypeName[57:65]: LinkMetricom, _LinkTypeLowerName[57:65]: LinkMetricom, _LinkTypeName[65:73]: LinkIeee1394, _LinkTypeLowerName[65:73]: LinkIeee1394, _LinkTypeName[73:78]: LinkEui64, _LinkTypeLowerName[73:78]: LinkEui64, _LinkTypeName[78:88]: LinkInfiniband, _LinkTypeLowerName[78:88]: LinkInfiniband, _LinkTypeName[88:92]: LinkSlip, _LinkTypeLowerName[88:92]: LinkSlip, _LinkTypeName[92:97]: LinkCslip, _LinkTypeLowerName[92:97]: LinkCslip, _LinkTypeName[97:102]: LinkSlip6, _LinkTypeLowerName[97:102]: LinkSlip6, _LinkTypeName[102:108]: LinkCslip6, _LinkTypeLowerName[102:108]: LinkCslip6, _LinkTypeName[108:113]: LinkRsrvd, _LinkTypeLowerName[108:113]: LinkRsrvd, _LinkTypeName[113:118]: LinkAdapt, _LinkTypeLowerName[113:118]: LinkAdapt, _LinkTypeName[118:122]: LinkRose, _LinkTypeLowerName[118:122]: LinkRose, _LinkTypeName[122:125]: LinkX25, _LinkTypeLowerName[122:125]: LinkX25, _LinkTypeName[125:130]: LinkHwx25, _LinkTypeLowerName[125:130]: LinkHwx25, _LinkTypeName[130:133]: LinkCan, _LinkTypeLowerName[130:133]: LinkCan, _LinkTypeName[133:136]: LinkPpp, _LinkTypeLowerName[133:136]: LinkPpp, _LinkTypeName[136:141]: LinkCisco, _LinkTypeLowerName[136:141]: LinkCisco, _LinkTypeName[141:145]: LinkLapb, _LinkTypeLowerName[141:145]: LinkLapb, _LinkTypeName[145:150]: LinkDdcmp, _LinkTypeLowerName[145:150]: LinkDdcmp, _LinkTypeName[150:157]: LinkRawhdlc, _LinkTypeLowerName[150:157]: LinkRawhdlc, _LinkTypeName[157:161]: LinkTunnel, _LinkTypeLowerName[157:161]: LinkTunnel, _LinkTypeName[161:168]: LinkTunnel6, _LinkTypeLowerName[161:168]: LinkTunnel6, _LinkTypeName[168:172]: LinkFrad, _LinkTypeLowerName[168:172]: LinkFrad, _LinkTypeName[172:176]: LinkSkip, _LinkTypeLowerName[172:176]: LinkSkip, _LinkTypeName[176:184]: LinkLoopbck, _LinkTypeLowerName[176:184]: LinkLoopbck, _LinkTypeName[184:192]: LinkLocaltlk, _LinkTypeLowerName[184:192]: LinkLocaltlk, _LinkTypeName[192:196]: LinkFddi, _LinkTypeLowerName[192:196]: LinkFddi, _LinkTypeName[196:199]: LinkBif, _LinkTypeLowerName[196:199]: LinkBif, _LinkTypeName[199:202]: LinkSit, _LinkTypeLowerName[199:202]: LinkSit, _LinkTypeName[202:208]: LinkIpddp, _LinkTypeLowerName[202:208]: LinkIpddp, _LinkTypeName[208:211]: LinkIpgre, _LinkTypeLowerName[208:211]: LinkIpgre, _LinkTypeName[211:217]: LinkPimreg, _LinkTypeLowerName[211:217]: LinkPimreg, _LinkTypeName[217:222]: LinkHippi, _LinkTypeLowerName[217:222]: LinkHippi, _LinkTypeName[222:225]: LinkAsh, _LinkTypeLowerName[222:225]: LinkAsh, _LinkTypeName[225:231]: LinkEconet, _LinkTypeLowerName[225:231]: LinkEconet, _LinkTypeName[231:235]: LinkIrda, _LinkTypeLowerName[231:235]: LinkIrda, _LinkTypeName[235:239]: LinkFcpp, _LinkTypeLowerName[235:239]: LinkFcpp, _LinkTypeName[239:243]: LinkFcal, _LinkTypeLowerName[239:243]: LinkFcal, _LinkTypeName[243:247]: LinkFcpl, _LinkTypeLowerName[243:247]: LinkFcpl, _LinkTypeName[247:253]: LinkFcfabric, _LinkTypeLowerName[247:253]: LinkFcfabric, _LinkTypeName[253:259]: LinkFcfabric1, _LinkTypeLowerName[253:259]: LinkFcfabric1, _LinkTypeName[259:265]: LinkFcfabric2, _LinkTypeLowerName[259:265]: LinkFcfabric2, _LinkTypeName[265:271]: LinkFcfabric3, _LinkTypeLowerName[265:271]: LinkFcfabric3, _LinkTypeName[271:277]: LinkFcfabric4, _LinkTypeLowerName[271:277]: LinkFcfabric4, _LinkTypeName[277:283]: LinkFcfabric5, _LinkTypeLowerName[277:283]: LinkFcfabric5, _LinkTypeName[283:289]: LinkFcfabric6, _LinkTypeLowerName[283:289]: LinkFcfabric6, _LinkTypeName[289:295]: LinkFcfabric7, _LinkTypeLowerName[289:295]: LinkFcfabric7, _LinkTypeName[295:301]: LinkFcfabric8, _LinkTypeLowerName[295:301]: LinkFcfabric8, _LinkTypeName[301:307]: LinkFcfabric9, _LinkTypeLowerName[301:307]: LinkFcfabric9, _LinkTypeName[307:314]: LinkFcfabric10, _LinkTypeLowerName[307:314]: LinkFcfabric10, _LinkTypeName[314:321]: LinkFcfabric11, _LinkTypeLowerName[314:321]: LinkFcfabric11, _LinkTypeName[321:328]: LinkFcfabric12, _LinkTypeLowerName[321:328]: LinkFcfabric12, _LinkTypeName[328:330]: LinkIee802tr, _LinkTypeLowerName[328:330]: LinkIee802tr, _LinkTypeName[330:340]: LinkIee80211, _LinkTypeLowerName[330:340]: LinkIee80211, _LinkTypeName[340:356]: LinkIee80211prism, _LinkTypeLowerName[340:356]: LinkIee80211prism, _LinkTypeName[356:375]: LinkIee80211Radiotap, _LinkTypeLowerName[356:375]: LinkIee80211Radiotap, _LinkTypeName[375:387]: LinkIee8021154, _LinkTypeLowerName[375:387]: LinkIee8021154, _LinkTypeName[387:407]: LinkIee8021154monitor, _LinkTypeLowerName[387:407]: LinkIee8021154monitor, _LinkTypeName[407:413]: LinkPhonet, _LinkTypeLowerName[407:413]: LinkPhonet, _LinkTypeName[413:424]: LinkPhonetpipe, _LinkTypeLowerName[413:424]: LinkPhonetpipe, _LinkTypeName[424:428]: LinkCaif, _LinkTypeLowerName[424:428]: LinkCaif, _LinkTypeName[428:434]: LinkIP6gre, _LinkTypeLowerName[428:434]: LinkIP6gre, _LinkTypeName[434:441]: LinkNetlink, _LinkTypeLowerName[434:441]: LinkNetlink, _LinkTypeName[441:448]: Link6Lowpan, _LinkTypeLowerName[441:448]: Link6Lowpan, _LinkTypeName[448:453]: LinkNone, _LinkTypeLowerName[448:453]: LinkNone, _LinkTypeName[453:457]: LinkVoid, _LinkTypeLowerName[453:457]: LinkVoid, } var _LinkTypeNames = []string{ _LinkTypeName[0:6], _LinkTypeName[6:11], _LinkTypeName[11:17], _LinkTypeName[17:21], _LinkTypeName[21:27], _LinkTypeName[27:32], _LinkTypeName[32:39], _LinkTypeName[39:45], _LinkTypeName[45:50], _LinkTypeName[50:54], _LinkTypeName[54:57], _LinkTypeName[57:65], _LinkTypeName[65:73], _LinkTypeName[73:78], _LinkTypeName[78:88], _LinkTypeName[88:92], _LinkTypeName[92:97], _LinkTypeName[97:102], _LinkTypeName[102:108], _LinkTypeName[108:113], _LinkTypeName[113:118], _LinkTypeName[118:122], _LinkTypeName[122:125], _LinkTypeName[125:130], _LinkTypeName[130:133], _LinkTypeName[133:136], _LinkTypeName[136:141], _LinkTypeName[141:145], _LinkTypeName[145:150], _LinkTypeName[150:157], _LinkTypeName[157:161], _LinkTypeName[161:168], _LinkTypeName[168:172], _LinkTypeName[172:176], _LinkTypeName[176:184], _LinkTypeName[184:192], _LinkTypeName[192:196], _LinkTypeName[196:199], _LinkTypeName[199:202], _LinkTypeName[202:208], _LinkTypeName[208:211], _LinkTypeName[211:217], _LinkTypeName[217:222], _LinkTypeName[222:225], _LinkTypeName[225:231], _LinkTypeName[231:235], _LinkTypeName[235:239], _LinkTypeName[239:243], _LinkTypeName[243:247], _LinkTypeName[247:253], _LinkTypeName[253:259], _LinkTypeName[259:265], _LinkTypeName[265:271], _LinkTypeName[271:277], _LinkTypeName[277:283], _LinkTypeName[283:289], _LinkTypeName[289:295], _LinkTypeName[295:301], _LinkTypeName[301:307], _LinkTypeName[307:314], _LinkTypeName[314:321], _LinkTypeName[321:328], _LinkTypeName[328:330], _LinkTypeName[330:340], _LinkTypeName[340:356], _LinkTypeName[356:375], _LinkTypeName[375:387], _LinkTypeName[387:407], _LinkTypeName[407:413], _LinkTypeName[413:424], _LinkTypeName[424:428], _LinkTypeName[428:434], _LinkTypeName[434:441], _LinkTypeName[441:448], _LinkTypeName[448:453], _LinkTypeName[453:457], } // LinkTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func LinkTypeString(s string) (LinkType, error) { if val, ok := _LinkTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _LinkTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to LinkType values", s) } // LinkTypeValues returns all values of the enum func LinkTypeValues() []LinkType { return _LinkTypeValues } // LinkTypeStrings returns a slice of all String values of the enum func LinkTypeStrings() []string { strs := make([]string, len(_LinkTypeNames)) copy(strs, _LinkTypeNames) return strs } // IsALinkType returns "true" if the value is listed in the enum definition. "false" otherwise func (i LinkType) IsALinkType() bool { _, ok := _LinkTypeMap[i] return ok } // MarshalText implements the encoding.TextMarshaler interface for LinkType func (i LinkType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for LinkType func (i *LinkType) UnmarshalText(text []byte) error { var err error *i, err = LinkTypeString(string(text)) return err } const _MatchOperatorName = "==!=" var _MatchOperatorIndex = [...]uint8{0, 2, 4} const _MatchOperatorLowerName = "==!=" func (i MatchOperator) String() string { if i < 0 || i >= MatchOperator(len(_MatchOperatorIndex)-1) { return fmt.Sprintf("MatchOperator(%d)", i) } return _MatchOperatorName[_MatchOperatorIndex[i]:_MatchOperatorIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _MatchOperatorNoOp() { var x [1]struct{} _ = x[OperatorEqual-(0)] _ = x[OperatorNotEqual-(1)] } var _MatchOperatorValues = []MatchOperator{OperatorEqual, OperatorNotEqual} var _MatchOperatorNameToValueMap = map[string]MatchOperator{ _MatchOperatorName[0:2]: OperatorEqual, _MatchOperatorLowerName[0:2]: OperatorEqual, _MatchOperatorName[2:4]: OperatorNotEqual, _MatchOperatorLowerName[2:4]: OperatorNotEqual, } var _MatchOperatorNames = []string{ _MatchOperatorName[0:2], _MatchOperatorName[2:4], } // MatchOperatorString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func MatchOperatorString(s string) (MatchOperator, error) { if val, ok := _MatchOperatorNameToValueMap[s]; ok { return val, nil } if val, ok := _MatchOperatorNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to MatchOperator values", s) } // MatchOperatorValues returns all values of the enum func MatchOperatorValues() []MatchOperator { return _MatchOperatorValues } // MatchOperatorStrings returns a slice of all String values of the enum func MatchOperatorStrings() []string { strs := make([]string, len(_MatchOperatorNames)) copy(strs, _MatchOperatorNames) return strs } // IsAMatchOperator returns "true" if the value is listed in the enum definition. "false" otherwise func (i MatchOperator) IsAMatchOperator() bool { for _, v := range _MatchOperatorValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for MatchOperator func (i MatchOperator) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for MatchOperator func (i *MatchOperator) UnmarshalText(text []byte) error { var err error *i, err = MatchOperatorString(string(text)) return err } const _NfTablesChainHookName = "preroutinginputforwardoutputpostrouting" var _NfTablesChainHookIndex = [...]uint8{0, 10, 15, 22, 28, 39} const _NfTablesChainHookLowerName = "preroutinginputforwardoutputpostrouting" func (i NfTablesChainHook) String() string { if i >= NfTablesChainHook(len(_NfTablesChainHookIndex)-1) { return fmt.Sprintf("NfTablesChainHook(%d)", i) } return _NfTablesChainHookName[_NfTablesChainHookIndex[i]:_NfTablesChainHookIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _NfTablesChainHookNoOp() { var x [1]struct{} _ = x[ChainHookPrerouting-(0)] _ = x[ChainHookInput-(1)] _ = x[ChainHookForward-(2)] _ = x[ChainHookOutput-(3)] _ = x[ChainHookPostrouting-(4)] } var _NfTablesChainHookValues = []NfTablesChainHook{ChainHookPrerouting, ChainHookInput, ChainHookForward, ChainHookOutput, ChainHookPostrouting} var _NfTablesChainHookNameToValueMap = map[string]NfTablesChainHook{ _NfTablesChainHookName[0:10]: ChainHookPrerouting, _NfTablesChainHookLowerName[0:10]: ChainHookPrerouting, _NfTablesChainHookName[10:15]: ChainHookInput, _NfTablesChainHookLowerName[10:15]: ChainHookInput, _NfTablesChainHookName[15:22]: ChainHookForward, _NfTablesChainHookLowerName[15:22]: ChainHookForward, _NfTablesChainHookName[22:28]: ChainHookOutput, _NfTablesChainHookLowerName[22:28]: ChainHookOutput, _NfTablesChainHookName[28:39]: ChainHookPostrouting, _NfTablesChainHookLowerName[28:39]: ChainHookPostrouting, } var _NfTablesChainHookNames = []string{ _NfTablesChainHookName[0:10], _NfTablesChainHookName[10:15], _NfTablesChainHookName[15:22], _NfTablesChainHookName[22:28], _NfTablesChainHookName[28:39], } // NfTablesChainHookString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func NfTablesChainHookString(s string) (NfTablesChainHook, error) { if val, ok := _NfTablesChainHookNameToValueMap[s]; ok { return val, nil } if val, ok := _NfTablesChainHookNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to NfTablesChainHook values", s) } // NfTablesChainHookValues returns all values of the enum func NfTablesChainHookValues() []NfTablesChainHook { return _NfTablesChainHookValues } // NfTablesChainHookStrings returns a slice of all String values of the enum func NfTablesChainHookStrings() []string { strs := make([]string, len(_NfTablesChainHookNames)) copy(strs, _NfTablesChainHookNames) return strs } // IsANfTablesChainHook returns "true" if the value is listed in the enum definition. "false" otherwise func (i NfTablesChainHook) IsANfTablesChainHook() bool { for _, v := range _NfTablesChainHookValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for NfTablesChainHook func (i NfTablesChainHook) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for NfTablesChainHook func (i *NfTablesChainHook) UnmarshalText(text []byte) error { var err error *i, err = NfTablesChainHookString(string(text)) return err } const _NfTablesChainPriorityName = "firstconntrack-defragrawselinux-firstconntrackmanglenat-destfiltersecuritynat-sourceselinux-lastconntrack-helperlast" const _NfTablesChainPriorityLowerName = "firstconntrack-defragrawselinux-firstconntrackmanglenat-destfiltersecuritynat-sourceselinux-lastconntrack-helperlast" var _NfTablesChainPriorityMap = map[NfTablesChainPriority]string{ -2147483648: _NfTablesChainPriorityName[0:5], -400: _NfTablesChainPriorityName[5:21], -300: _NfTablesChainPriorityName[21:24], -225: _NfTablesChainPriorityName[24:37], -200: _NfTablesChainPriorityName[37:46], -150: _NfTablesChainPriorityName[46:52], -100: _NfTablesChainPriorityName[52:60], 0: _NfTablesChainPriorityName[60:66], 50: _NfTablesChainPriorityName[66:74], 100: _NfTablesChainPriorityName[74:84], 225: _NfTablesChainPriorityName[84:96], 300: _NfTablesChainPriorityName[96:112], 2147483647: _NfTablesChainPriorityName[112:116], } func (i NfTablesChainPriority) String() string { if str, ok := _NfTablesChainPriorityMap[i]; ok { return str } return fmt.Sprintf("NfTablesChainPriority(%d)", i) } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _NfTablesChainPriorityNoOp() { var x [1]struct{} _ = x[ChainPriorityFirst-(-2147483648)] _ = x[ChainPriorityConntrackDefrag-(-400)] _ = x[ChainPriorityRaw-(-300)] _ = x[ChainPrioritySELinuxFirst-(-225)] _ = x[ChainPriorityConntrack-(-200)] _ = x[ChainPriorityMangle-(-150)] _ = x[ChainPriorityNATDest-(-100)] _ = x[ChainPriorityFilter-(0)] _ = x[ChainPrioritySecurity-(50)] _ = x[ChainPriorityNATSource-(100)] _ = x[ChainPrioritySELinuxLast-(225)] _ = x[ChainPriorityConntrackHelper-(300)] _ = x[ChainPriorityLast-(2147483647)] } var _NfTablesChainPriorityValues = []NfTablesChainPriority{ChainPriorityFirst, ChainPriorityConntrackDefrag, ChainPriorityRaw, ChainPrioritySELinuxFirst, ChainPriorityConntrack, ChainPriorityMangle, ChainPriorityNATDest, ChainPriorityFilter, ChainPrioritySecurity, ChainPriorityNATSource, ChainPrioritySELinuxLast, ChainPriorityConntrackHelper, ChainPriorityLast} var _NfTablesChainPriorityNameToValueMap = map[string]NfTablesChainPriority{ _NfTablesChainPriorityName[0:5]: ChainPriorityFirst, _NfTablesChainPriorityLowerName[0:5]: ChainPriorityFirst, _NfTablesChainPriorityName[5:21]: ChainPriorityConntrackDefrag, _NfTablesChainPriorityLowerName[5:21]: ChainPriorityConntrackDefrag, _NfTablesChainPriorityName[21:24]: ChainPriorityRaw, _NfTablesChainPriorityLowerName[21:24]: ChainPriorityRaw, _NfTablesChainPriorityName[24:37]: ChainPrioritySELinuxFirst, _NfTablesChainPriorityLowerName[24:37]: ChainPrioritySELinuxFirst, _NfTablesChainPriorityName[37:46]: ChainPriorityConntrack, _NfTablesChainPriorityLowerName[37:46]: ChainPriorityConntrack, _NfTablesChainPriorityName[46:52]: ChainPriorityMangle, _NfTablesChainPriorityLowerName[46:52]: ChainPriorityMangle, _NfTablesChainPriorityName[52:60]: ChainPriorityNATDest, _NfTablesChainPriorityLowerName[52:60]: ChainPriorityNATDest, _NfTablesChainPriorityName[60:66]: ChainPriorityFilter, _NfTablesChainPriorityLowerName[60:66]: ChainPriorityFilter, _NfTablesChainPriorityName[66:74]: ChainPrioritySecurity, _NfTablesChainPriorityLowerName[66:74]: ChainPrioritySecurity, _NfTablesChainPriorityName[74:84]: ChainPriorityNATSource, _NfTablesChainPriorityLowerName[74:84]: ChainPriorityNATSource, _NfTablesChainPriorityName[84:96]: ChainPrioritySELinuxLast, _NfTablesChainPriorityLowerName[84:96]: ChainPrioritySELinuxLast, _NfTablesChainPriorityName[96:112]: ChainPriorityConntrackHelper, _NfTablesChainPriorityLowerName[96:112]: ChainPriorityConntrackHelper, _NfTablesChainPriorityName[112:116]: ChainPriorityLast, _NfTablesChainPriorityLowerName[112:116]: ChainPriorityLast, } var _NfTablesChainPriorityNames = []string{ _NfTablesChainPriorityName[0:5], _NfTablesChainPriorityName[5:21], _NfTablesChainPriorityName[21:24], _NfTablesChainPriorityName[24:37], _NfTablesChainPriorityName[37:46], _NfTablesChainPriorityName[46:52], _NfTablesChainPriorityName[52:60], _NfTablesChainPriorityName[60:66], _NfTablesChainPriorityName[66:74], _NfTablesChainPriorityName[74:84], _NfTablesChainPriorityName[84:96], _NfTablesChainPriorityName[96:112], _NfTablesChainPriorityName[112:116], } // NfTablesChainPriorityString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func NfTablesChainPriorityString(s string) (NfTablesChainPriority, error) { if val, ok := _NfTablesChainPriorityNameToValueMap[s]; ok { return val, nil } if val, ok := _NfTablesChainPriorityNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to NfTablesChainPriority values", s) } // NfTablesChainPriorityValues returns all values of the enum func NfTablesChainPriorityValues() []NfTablesChainPriority { return _NfTablesChainPriorityValues } // NfTablesChainPriorityStrings returns a slice of all String values of the enum func NfTablesChainPriorityStrings() []string { strs := make([]string, len(_NfTablesChainPriorityNames)) copy(strs, _NfTablesChainPriorityNames) return strs } // IsANfTablesChainPriority returns "true" if the value is listed in the enum definition. "false" otherwise func (i NfTablesChainPriority) IsANfTablesChainPriority() bool { _, ok := _NfTablesChainPriorityMap[i] return ok } // MarshalText implements the encoding.TextMarshaler interface for NfTablesChainPriority func (i NfTablesChainPriority) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for NfTablesChainPriority func (i *NfTablesChainPriority) UnmarshalText(text []byte) error { var err error *i, err = NfTablesChainPriorityString(string(text)) return err } const _NfTablesVerdictName = "dropaccept" var _NfTablesVerdictIndex = [...]uint8{0, 4, 10} const _NfTablesVerdictLowerName = "dropaccept" func (i NfTablesVerdict) String() string { if i < 0 || i >= NfTablesVerdict(len(_NfTablesVerdictIndex)-1) { return fmt.Sprintf("NfTablesVerdict(%d)", i) } return _NfTablesVerdictName[_NfTablesVerdictIndex[i]:_NfTablesVerdictIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _NfTablesVerdictNoOp() { var x [1]struct{} _ = x[VerdictDrop-(0)] _ = x[VerdictAccept-(1)] } var _NfTablesVerdictValues = []NfTablesVerdict{VerdictDrop, VerdictAccept} var _NfTablesVerdictNameToValueMap = map[string]NfTablesVerdict{ _NfTablesVerdictName[0:4]: VerdictDrop, _NfTablesVerdictLowerName[0:4]: VerdictDrop, _NfTablesVerdictName[4:10]: VerdictAccept, _NfTablesVerdictLowerName[4:10]: VerdictAccept, } var _NfTablesVerdictNames = []string{ _NfTablesVerdictName[0:4], _NfTablesVerdictName[4:10], } // NfTablesVerdictString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func NfTablesVerdictString(s string) (NfTablesVerdict, error) { if val, ok := _NfTablesVerdictNameToValueMap[s]; ok { return val, nil } if val, ok := _NfTablesVerdictNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to NfTablesVerdict values", s) } // NfTablesVerdictValues returns all values of the enum func NfTablesVerdictValues() []NfTablesVerdict { return _NfTablesVerdictValues } // NfTablesVerdictStrings returns a slice of all String values of the enum func NfTablesVerdictStrings() []string { strs := make([]string, len(_NfTablesVerdictNames)) copy(strs, _NfTablesVerdictNames) return strs } // IsANfTablesVerdict returns "true" if the value is listed in the enum definition. "false" otherwise func (i NfTablesVerdict) IsANfTablesVerdict() bool { for _, v := range _NfTablesVerdictValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for NfTablesVerdict func (i NfTablesVerdict) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for NfTablesVerdict func (i *NfTablesVerdict) UnmarshalText(text []byte) error { var err error *i, err = NfTablesVerdictString(string(text)) return err } const _OperationalStateName = "unknownnotPresentdownlowerLayerDowntestingdormantup" var _OperationalStateIndex = [...]uint8{0, 7, 17, 21, 35, 42, 49, 51} const _OperationalStateLowerName = "unknownnotpresentdownlowerlayerdowntestingdormantup" func (i OperationalState) String() string { if i >= OperationalState(len(_OperationalStateIndex)-1) { return fmt.Sprintf("OperationalState(%d)", i) } return _OperationalStateName[_OperationalStateIndex[i]:_OperationalStateIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _OperationalStateNoOp() { var x [1]struct{} _ = x[OperStateUnknown-(0)] _ = x[OperStateNotPresent-(1)] _ = x[OperStateDown-(2)] _ = x[OperStateLowerLayerDown-(3)] _ = x[OperStateTesting-(4)] _ = x[OperStateDormant-(5)] _ = x[OperStateUp-(6)] } var _OperationalStateValues = []OperationalState{OperStateUnknown, OperStateNotPresent, OperStateDown, OperStateLowerLayerDown, OperStateTesting, OperStateDormant, OperStateUp} var _OperationalStateNameToValueMap = map[string]OperationalState{ _OperationalStateName[0:7]: OperStateUnknown, _OperationalStateLowerName[0:7]: OperStateUnknown, _OperationalStateName[7:17]: OperStateNotPresent, _OperationalStateLowerName[7:17]: OperStateNotPresent, _OperationalStateName[17:21]: OperStateDown, _OperationalStateLowerName[17:21]: OperStateDown, _OperationalStateName[21:35]: OperStateLowerLayerDown, _OperationalStateLowerName[21:35]: OperStateLowerLayerDown, _OperationalStateName[35:42]: OperStateTesting, _OperationalStateLowerName[35:42]: OperStateTesting, _OperationalStateName[42:49]: OperStateDormant, _OperationalStateLowerName[42:49]: OperStateDormant, _OperationalStateName[49:51]: OperStateUp, _OperationalStateLowerName[49:51]: OperStateUp, } var _OperationalStateNames = []string{ _OperationalStateName[0:7], _OperationalStateName[7:17], _OperationalStateName[17:21], _OperationalStateName[21:35], _OperationalStateName[35:42], _OperationalStateName[42:49], _OperationalStateName[49:51], } // OperationalStateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func OperationalStateString(s string) (OperationalState, error) { if val, ok := _OperationalStateNameToValueMap[s]; ok { return val, nil } if val, ok := _OperationalStateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to OperationalState values", s) } // OperationalStateValues returns all values of the enum func OperationalStateValues() []OperationalState { return _OperationalStateValues } // OperationalStateStrings returns a slice of all String values of the enum func OperationalStateStrings() []string { strs := make([]string, len(_OperationalStateNames)) copy(strs, _OperationalStateNames) return strs } // IsAOperationalState returns "true" if the value is listed in the enum definition. "false" otherwise func (i OperationalState) IsAOperationalState() bool { for _, v := range _OperationalStateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for OperationalState func (i OperationalState) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for OperationalState func (i *OperationalState) UnmarshalText(text []byte) error { var err error *i, err = OperationalStateString(string(text)) return err } const ( _PortName_0 = "TwistedPairAUIMIIBNCDirectAttach" _PortLowerName_0 = "twistedpairauimiibncdirectattach" _PortName_1 = "None" _PortLowerName_1 = "none" _PortName_2 = "Other" _PortLowerName_2 = "other" ) var ( _PortIndex_0 = [...]uint8{0, 11, 14, 17, 17, 20, 32} _PortIndex_1 = [...]uint8{0, 4} _PortIndex_2 = [...]uint8{0, 5} ) func (i Port) String() string { switch { case 0 <= i && i <= 5: return _PortName_0[_PortIndex_0[i]:_PortIndex_0[i+1]] case i == 239: return _PortName_1 case i == 255: return _PortName_2 default: return fmt.Sprintf("Port(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _PortNoOp() { var x [1]struct{} _ = x[TwistedPair-(0)] _ = x[AUI-(1)] _ = x[MII-(2)] _ = x[Fibre-(3)] _ = x[BNC-(4)] _ = x[DirectAttach-(5)] _ = x[None-(239)] _ = x[Other-(255)] } var _PortValues = []Port{TwistedPair, AUI, MII, Fibre, BNC, DirectAttach, None, Other} var _PortNameToValueMap = map[string]Port{ _PortName_0[0:11]: TwistedPair, _PortLowerName_0[0:11]: TwistedPair, _PortName_0[11:14]: AUI, _PortLowerName_0[11:14]: AUI, _PortName_0[14:17]: MII, _PortLowerName_0[14:17]: MII, _PortName_0[17:17]: Fibre, _PortLowerName_0[17:17]: Fibre, _PortName_0[17:20]: BNC, _PortLowerName_0[17:20]: BNC, _PortName_0[20:32]: DirectAttach, _PortLowerName_0[20:32]: DirectAttach, _PortName_1[0:4]: None, _PortLowerName_1[0:4]: None, _PortName_2[0:5]: Other, _PortLowerName_2[0:5]: Other, } var _PortNames = []string{ _PortName_0[0:11], _PortName_0[11:14], _PortName_0[14:17], _PortName_0[17:17], _PortName_0[17:20], _PortName_0[20:32], _PortName_1[0:4], _PortName_2[0:5], } // PortString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func PortString(s string) (Port, error) { if val, ok := _PortNameToValueMap[s]; ok { return val, nil } if val, ok := _PortNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Port values", s) } // PortValues returns all values of the enum func PortValues() []Port { return _PortValues } // PortStrings returns a slice of all String values of the enum func PortStrings() []string { strs := make([]string, len(_PortNames)) copy(strs, _PortNames) return strs } // IsAPort returns "true" if the value is listed in the enum definition. "false" otherwise func (i Port) IsAPort() bool { for _, v := range _PortValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Port func (i Port) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Port func (i *Port) UnmarshalText(text []byte) error { var err error *i, err = PortString(string(text)) return err } const _PrimaryReselectName = "alwaysbetterfailure" var _PrimaryReselectIndex = [...]uint8{0, 6, 12, 19} const _PrimaryReselectLowerName = "alwaysbetterfailure" func (i PrimaryReselect) String() string { if i >= PrimaryReselect(len(_PrimaryReselectIndex)-1) { return fmt.Sprintf("PrimaryReselect(%d)", i) } return _PrimaryReselectName[_PrimaryReselectIndex[i]:_PrimaryReselectIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _PrimaryReselectNoOp() { var x [1]struct{} _ = x[PrimaryReselectAlways-(0)] _ = x[PrimaryReselectBetter-(1)] _ = x[PrimaryReselectFailure-(2)] } var _PrimaryReselectValues = []PrimaryReselect{PrimaryReselectAlways, PrimaryReselectBetter, PrimaryReselectFailure} var _PrimaryReselectNameToValueMap = map[string]PrimaryReselect{ _PrimaryReselectName[0:6]: PrimaryReselectAlways, _PrimaryReselectLowerName[0:6]: PrimaryReselectAlways, _PrimaryReselectName[6:12]: PrimaryReselectBetter, _PrimaryReselectLowerName[6:12]: PrimaryReselectBetter, _PrimaryReselectName[12:19]: PrimaryReselectFailure, _PrimaryReselectLowerName[12:19]: PrimaryReselectFailure, } var _PrimaryReselectNames = []string{ _PrimaryReselectName[0:6], _PrimaryReselectName[6:12], _PrimaryReselectName[12:19], } // PrimaryReselectString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func PrimaryReselectString(s string) (PrimaryReselect, error) { if val, ok := _PrimaryReselectNameToValueMap[s]; ok { return val, nil } if val, ok := _PrimaryReselectNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to PrimaryReselect values", s) } // PrimaryReselectValues returns all values of the enum func PrimaryReselectValues() []PrimaryReselect { return _PrimaryReselectValues } // PrimaryReselectStrings returns a slice of all String values of the enum func PrimaryReselectStrings() []string { strs := make([]string, len(_PrimaryReselectNames)) copy(strs, _PrimaryReselectNames) return strs } // IsAPrimaryReselect returns "true" if the value is listed in the enum definition. "false" otherwise func (i PrimaryReselect) IsAPrimaryReselect() bool { for _, v := range _PrimaryReselectValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for PrimaryReselect func (i PrimaryReselect) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for PrimaryReselect func (i *PrimaryReselect) UnmarshalText(text []byte) error { var err error *i, err = PrimaryReselectString(string(text)) return err } const ( _ProtocolName_0 = "icmp" _ProtocolLowerName_0 = "icmp" _ProtocolName_1 = "tcp" _ProtocolLowerName_1 = "tcp" _ProtocolName_2 = "udp" _ProtocolLowerName_2 = "udp" _ProtocolName_3 = "icmpv6" _ProtocolLowerName_3 = "icmpv6" ) var ( _ProtocolIndex_0 = [...]uint8{0, 4} _ProtocolIndex_1 = [...]uint8{0, 3} _ProtocolIndex_2 = [...]uint8{0, 3} _ProtocolIndex_3 = [...]uint8{0, 6} ) func (i Protocol) String() string { switch { case i == 1: return _ProtocolName_0 case i == 6: return _ProtocolName_1 case i == 17: return _ProtocolName_2 case i == 58: return _ProtocolName_3 default: return fmt.Sprintf("Protocol(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ProtocolNoOp() { var x [1]struct{} _ = x[ProtocolICMP-(1)] _ = x[ProtocolTCP-(6)] _ = x[ProtocolUDP-(17)] _ = x[ProtocolICMPv6-(58)] } var _ProtocolValues = []Protocol{ProtocolICMP, ProtocolTCP, ProtocolUDP, ProtocolICMPv6} var _ProtocolNameToValueMap = map[string]Protocol{ _ProtocolName_0[0:4]: ProtocolICMP, _ProtocolLowerName_0[0:4]: ProtocolICMP, _ProtocolName_1[0:3]: ProtocolTCP, _ProtocolLowerName_1[0:3]: ProtocolTCP, _ProtocolName_2[0:3]: ProtocolUDP, _ProtocolLowerName_2[0:3]: ProtocolUDP, _ProtocolName_3[0:6]: ProtocolICMPv6, _ProtocolLowerName_3[0:6]: ProtocolICMPv6, } var _ProtocolNames = []string{ _ProtocolName_0[0:4], _ProtocolName_1[0:3], _ProtocolName_2[0:3], _ProtocolName_3[0:6], } // ProtocolString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ProtocolString(s string) (Protocol, error) { if val, ok := _ProtocolNameToValueMap[s]; ok { return val, nil } if val, ok := _ProtocolNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Protocol values", s) } // ProtocolValues returns all values of the enum func ProtocolValues() []Protocol { return _ProtocolValues } // ProtocolStrings returns a slice of all String values of the enum func ProtocolStrings() []string { strs := make([]string, len(_ProtocolNames)) copy(strs, _ProtocolNames) return strs } // IsAProtocol returns "true" if the value is listed in the enum definition. "false" otherwise func (i Protocol) IsAProtocol() bool { for _, v := range _ProtocolValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Protocol func (i Protocol) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Protocol func (i *Protocol) UnmarshalText(text []byte) error { var err error *i, err = ProtocolString(string(text)) return err } const ( _RouteFlagName_0 = "notify" _RouteFlagLowerName_0 = "notify" _RouteFlagName_1 = "cloned" _RouteFlagLowerName_1 = "cloned" _RouteFlagName_2 = "equalize" _RouteFlagLowerName_2 = "equalize" _RouteFlagName_3 = "prefix" _RouteFlagLowerName_3 = "prefix" _RouteFlagName_4 = "lookup_table" _RouteFlagLowerName_4 = "lookup_table" _RouteFlagName_5 = "fib_match" _RouteFlagLowerName_5 = "fib_match" _RouteFlagName_6 = "offload" _RouteFlagLowerName_6 = "offload" _RouteFlagName_7 = "trap" _RouteFlagLowerName_7 = "trap" ) var ( _RouteFlagIndex_0 = [...]uint8{0, 6} _RouteFlagIndex_1 = [...]uint8{0, 6} _RouteFlagIndex_2 = [...]uint8{0, 8} _RouteFlagIndex_3 = [...]uint8{0, 6} _RouteFlagIndex_4 = [...]uint8{0, 12} _RouteFlagIndex_5 = [...]uint8{0, 9} _RouteFlagIndex_6 = [...]uint8{0, 7} _RouteFlagIndex_7 = [...]uint8{0, 4} ) func (i RouteFlag) String() string { switch { case i == 256: return _RouteFlagName_0 case i == 512: return _RouteFlagName_1 case i == 1024: return _RouteFlagName_2 case i == 2048: return _RouteFlagName_3 case i == 4096: return _RouteFlagName_4 case i == 8192: return _RouteFlagName_5 case i == 16384: return _RouteFlagName_6 case i == 32768: return _RouteFlagName_7 default: return fmt.Sprintf("RouteFlag(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RouteFlagNoOp() { var x [1]struct{} _ = x[RouteNotify-(256)] _ = x[RouteCloned-(512)] _ = x[RouteEqualize-(1024)] _ = x[RoutePrefix-(2048)] _ = x[RouteLookupTable-(4096)] _ = x[RouteFIBMatch-(8192)] _ = x[RouteOffload-(16384)] _ = x[RouteTrap-(32768)] } var _RouteFlagValues = []RouteFlag{RouteNotify, RouteCloned, RouteEqualize, RoutePrefix, RouteLookupTable, RouteFIBMatch, RouteOffload, RouteTrap} var _RouteFlagNameToValueMap = map[string]RouteFlag{ _RouteFlagName_0[0:6]: RouteNotify, _RouteFlagLowerName_0[0:6]: RouteNotify, _RouteFlagName_1[0:6]: RouteCloned, _RouteFlagLowerName_1[0:6]: RouteCloned, _RouteFlagName_2[0:8]: RouteEqualize, _RouteFlagLowerName_2[0:8]: RouteEqualize, _RouteFlagName_3[0:6]: RoutePrefix, _RouteFlagLowerName_3[0:6]: RoutePrefix, _RouteFlagName_4[0:12]: RouteLookupTable, _RouteFlagLowerName_4[0:12]: RouteLookupTable, _RouteFlagName_5[0:9]: RouteFIBMatch, _RouteFlagLowerName_5[0:9]: RouteFIBMatch, _RouteFlagName_6[0:7]: RouteOffload, _RouteFlagLowerName_6[0:7]: RouteOffload, _RouteFlagName_7[0:4]: RouteTrap, _RouteFlagLowerName_7[0:4]: RouteTrap, } var _RouteFlagNames = []string{ _RouteFlagName_0[0:6], _RouteFlagName_1[0:6], _RouteFlagName_2[0:8], _RouteFlagName_3[0:6], _RouteFlagName_4[0:12], _RouteFlagName_5[0:9], _RouteFlagName_6[0:7], _RouteFlagName_7[0:4], } // RouteFlagString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RouteFlagString(s string) (RouteFlag, error) { if val, ok := _RouteFlagNameToValueMap[s]; ok { return val, nil } if val, ok := _RouteFlagNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RouteFlag values", s) } // RouteFlagValues returns all values of the enum func RouteFlagValues() []RouteFlag { return _RouteFlagValues } // RouteFlagStrings returns a slice of all String values of the enum func RouteFlagStrings() []string { strs := make([]string, len(_RouteFlagNames)) copy(strs, _RouteFlagNames) return strs } // IsARouteFlag returns "true" if the value is listed in the enum definition. "false" otherwise func (i RouteFlag) IsARouteFlag() bool { for _, v := range _RouteFlagValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RouteFlag func (i RouteFlag) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RouteFlag func (i *RouteFlag) UnmarshalText(text []byte) error { var err error *i, err = RouteFlagString(string(text)) return err } const ( _RouteProtocolName_0 = "unspecredirectkernelbootstatic" _RouteProtocolLowerName_0 = "unspecredirectkernelbootstatic" _RouteProtocolName_1 = "ramrtzebrabirddnroutedxorpntkdhcpmrtdkeepalived" _RouteProtocolLowerName_1 = "ramrtzebrabirddnroutedxorpntkdhcpmrtdkeepalived" _RouteProtocolName_2 = "babel" _RouteProtocolLowerName_2 = "babel" _RouteProtocolName_3 = "openr" _RouteProtocolLowerName_3 = "openr" _RouteProtocolName_4 = "bgpisisospfrip" _RouteProtocolLowerName_4 = "bgpisisospfrip" _RouteProtocolName_5 = "eigrp" _RouteProtocolLowerName_5 = "eigrp" ) var ( _RouteProtocolIndex_0 = [...]uint8{0, 6, 14, 20, 24, 30} _RouteProtocolIndex_1 = [...]uint8{0, 2, 5, 10, 14, 22, 26, 29, 33, 37, 47} _RouteProtocolIndex_2 = [...]uint8{0, 5} _RouteProtocolIndex_3 = [...]uint8{0, 5} _RouteProtocolIndex_4 = [...]uint8{0, 3, 7, 11, 14} _RouteProtocolIndex_5 = [...]uint8{0, 5} ) func (i RouteProtocol) String() string { switch { case 0 <= i && i <= 4: return _RouteProtocolName_0[_RouteProtocolIndex_0[i]:_RouteProtocolIndex_0[i+1]] case 9 <= i && i <= 18: i -= 9 return _RouteProtocolName_1[_RouteProtocolIndex_1[i]:_RouteProtocolIndex_1[i+1]] case i == 42: return _RouteProtocolName_2 case i == 99: return _RouteProtocolName_3 case 186 <= i && i <= 189: i -= 186 return _RouteProtocolName_4[_RouteProtocolIndex_4[i]:_RouteProtocolIndex_4[i+1]] case i == 192: return _RouteProtocolName_5 default: return fmt.Sprintf("RouteProtocol(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RouteProtocolNoOp() { var x [1]struct{} _ = x[ProtocolUnspec-(0)] _ = x[ProtocolRedirect-(1)] _ = x[ProtocolKernel-(2)] _ = x[ProtocolBoot-(3)] _ = x[ProtocolStatic-(4)] _ = x[ProtocolRA-(9)] _ = x[ProtocolMRT-(10)] _ = x[ProtocolZebra-(11)] _ = x[ProtocolBird-(12)] _ = x[ProtocolDnrouted-(13)] _ = x[ProtocolXorp-(14)] _ = x[ProtocolNTK-(15)] _ = x[ProtocolDHCP-(16)] _ = x[ProtocolMRTD-(17)] _ = x[ProtocolKeepalived-(18)] _ = x[ProtocolBabel-(42)] _ = x[ProtocolOpenr-(99)] _ = x[ProtocolBGP-(186)] _ = x[ProtocolISIS-(187)] _ = x[ProtocolOSPF-(188)] _ = x[ProtocolRIP-(189)] _ = x[ProtocolEIGRP-(192)] } var _RouteProtocolValues = []RouteProtocol{ProtocolUnspec, ProtocolRedirect, ProtocolKernel, ProtocolBoot, ProtocolStatic, ProtocolRA, ProtocolMRT, ProtocolZebra, ProtocolBird, ProtocolDnrouted, ProtocolXorp, ProtocolNTK, ProtocolDHCP, ProtocolMRTD, ProtocolKeepalived, ProtocolBabel, ProtocolOpenr, ProtocolBGP, ProtocolISIS, ProtocolOSPF, ProtocolRIP, ProtocolEIGRP} var _RouteProtocolNameToValueMap = map[string]RouteProtocol{ _RouteProtocolName_0[0:6]: ProtocolUnspec, _RouteProtocolLowerName_0[0:6]: ProtocolUnspec, _RouteProtocolName_0[6:14]: ProtocolRedirect, _RouteProtocolLowerName_0[6:14]: ProtocolRedirect, _RouteProtocolName_0[14:20]: ProtocolKernel, _RouteProtocolLowerName_0[14:20]: ProtocolKernel, _RouteProtocolName_0[20:24]: ProtocolBoot, _RouteProtocolLowerName_0[20:24]: ProtocolBoot, _RouteProtocolName_0[24:30]: ProtocolStatic, _RouteProtocolLowerName_0[24:30]: ProtocolStatic, _RouteProtocolName_1[0:2]: ProtocolRA, _RouteProtocolLowerName_1[0:2]: ProtocolRA, _RouteProtocolName_1[2:5]: ProtocolMRT, _RouteProtocolLowerName_1[2:5]: ProtocolMRT, _RouteProtocolName_1[5:10]: ProtocolZebra, _RouteProtocolLowerName_1[5:10]: ProtocolZebra, _RouteProtocolName_1[10:14]: ProtocolBird, _RouteProtocolLowerName_1[10:14]: ProtocolBird, _RouteProtocolName_1[14:22]: ProtocolDnrouted, _RouteProtocolLowerName_1[14:22]: ProtocolDnrouted, _RouteProtocolName_1[22:26]: ProtocolXorp, _RouteProtocolLowerName_1[22:26]: ProtocolXorp, _RouteProtocolName_1[26:29]: ProtocolNTK, _RouteProtocolLowerName_1[26:29]: ProtocolNTK, _RouteProtocolName_1[29:33]: ProtocolDHCP, _RouteProtocolLowerName_1[29:33]: ProtocolDHCP, _RouteProtocolName_1[33:37]: ProtocolMRTD, _RouteProtocolLowerName_1[33:37]: ProtocolMRTD, _RouteProtocolName_1[37:47]: ProtocolKeepalived, _RouteProtocolLowerName_1[37:47]: ProtocolKeepalived, _RouteProtocolName_2[0:5]: ProtocolBabel, _RouteProtocolLowerName_2[0:5]: ProtocolBabel, _RouteProtocolName_3[0:5]: ProtocolOpenr, _RouteProtocolLowerName_3[0:5]: ProtocolOpenr, _RouteProtocolName_4[0:3]: ProtocolBGP, _RouteProtocolLowerName_4[0:3]: ProtocolBGP, _RouteProtocolName_4[3:7]: ProtocolISIS, _RouteProtocolLowerName_4[3:7]: ProtocolISIS, _RouteProtocolName_4[7:11]: ProtocolOSPF, _RouteProtocolLowerName_4[7:11]: ProtocolOSPF, _RouteProtocolName_4[11:14]: ProtocolRIP, _RouteProtocolLowerName_4[11:14]: ProtocolRIP, _RouteProtocolName_5[0:5]: ProtocolEIGRP, _RouteProtocolLowerName_5[0:5]: ProtocolEIGRP, } var _RouteProtocolNames = []string{ _RouteProtocolName_0[0:6], _RouteProtocolName_0[6:14], _RouteProtocolName_0[14:20], _RouteProtocolName_0[20:24], _RouteProtocolName_0[24:30], _RouteProtocolName_1[0:2], _RouteProtocolName_1[2:5], _RouteProtocolName_1[5:10], _RouteProtocolName_1[10:14], _RouteProtocolName_1[14:22], _RouteProtocolName_1[22:26], _RouteProtocolName_1[26:29], _RouteProtocolName_1[29:33], _RouteProtocolName_1[33:37], _RouteProtocolName_1[37:47], _RouteProtocolName_2[0:5], _RouteProtocolName_3[0:5], _RouteProtocolName_4[0:3], _RouteProtocolName_4[3:7], _RouteProtocolName_4[7:11], _RouteProtocolName_4[11:14], _RouteProtocolName_5[0:5], } // RouteProtocolString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RouteProtocolString(s string) (RouteProtocol, error) { if val, ok := _RouteProtocolNameToValueMap[s]; ok { return val, nil } if val, ok := _RouteProtocolNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RouteProtocol values", s) } // RouteProtocolValues returns all values of the enum func RouteProtocolValues() []RouteProtocol { return _RouteProtocolValues } // RouteProtocolStrings returns a slice of all String values of the enum func RouteProtocolStrings() []string { strs := make([]string, len(_RouteProtocolNames)) copy(strs, _RouteProtocolNames) return strs } // IsARouteProtocol returns "true" if the value is listed in the enum definition. "false" otherwise func (i RouteProtocol) IsARouteProtocol() bool { for _, v := range _RouteProtocolValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RouteProtocol func (i RouteProtocol) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RouteProtocol func (i *RouteProtocol) UnmarshalText(text []byte) error { var err error *i, err = RouteProtocolString(string(text)) return err } const _RouteTypeName = "unspecunicastlocalbroadcastanycastmulticastblackholeunreachableprohibitthrownatxresolve" var _RouteTypeIndex = [...]uint8{0, 6, 13, 18, 27, 34, 43, 52, 63, 71, 76, 79, 87} const _RouteTypeLowerName = "unspecunicastlocalbroadcastanycastmulticastblackholeunreachableprohibitthrownatxresolve" func (i RouteType) String() string { if i >= RouteType(len(_RouteTypeIndex)-1) { return fmt.Sprintf("RouteType(%d)", i) } return _RouteTypeName[_RouteTypeIndex[i]:_RouteTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RouteTypeNoOp() { var x [1]struct{} _ = x[TypeUnspec-(0)] _ = x[TypeUnicast-(1)] _ = x[TypeLocal-(2)] _ = x[TypeBroadcast-(3)] _ = x[TypeAnycast-(4)] _ = x[TypeMulticast-(5)] _ = x[TypeBlackhole-(6)] _ = x[TypeUnreachable-(7)] _ = x[TypeProhibit-(8)] _ = x[TypeThrow-(9)] _ = x[TypeNAT-(10)] _ = x[TypeXResolve-(11)] } var _RouteTypeValues = []RouteType{TypeUnspec, TypeUnicast, TypeLocal, TypeBroadcast, TypeAnycast, TypeMulticast, TypeBlackhole, TypeUnreachable, TypeProhibit, TypeThrow, TypeNAT, TypeXResolve} var _RouteTypeNameToValueMap = map[string]RouteType{ _RouteTypeName[0:6]: TypeUnspec, _RouteTypeLowerName[0:6]: TypeUnspec, _RouteTypeName[6:13]: TypeUnicast, _RouteTypeLowerName[6:13]: TypeUnicast, _RouteTypeName[13:18]: TypeLocal, _RouteTypeLowerName[13:18]: TypeLocal, _RouteTypeName[18:27]: TypeBroadcast, _RouteTypeLowerName[18:27]: TypeBroadcast, _RouteTypeName[27:34]: TypeAnycast, _RouteTypeLowerName[27:34]: TypeAnycast, _RouteTypeName[34:43]: TypeMulticast, _RouteTypeLowerName[34:43]: TypeMulticast, _RouteTypeName[43:52]: TypeBlackhole, _RouteTypeLowerName[43:52]: TypeBlackhole, _RouteTypeName[52:63]: TypeUnreachable, _RouteTypeLowerName[52:63]: TypeUnreachable, _RouteTypeName[63:71]: TypeProhibit, _RouteTypeLowerName[63:71]: TypeProhibit, _RouteTypeName[71:76]: TypeThrow, _RouteTypeLowerName[71:76]: TypeThrow, _RouteTypeName[76:79]: TypeNAT, _RouteTypeLowerName[76:79]: TypeNAT, _RouteTypeName[79:87]: TypeXResolve, _RouteTypeLowerName[79:87]: TypeXResolve, } var _RouteTypeNames = []string{ _RouteTypeName[0:6], _RouteTypeName[6:13], _RouteTypeName[13:18], _RouteTypeName[18:27], _RouteTypeName[27:34], _RouteTypeName[34:43], _RouteTypeName[43:52], _RouteTypeName[52:63], _RouteTypeName[63:71], _RouteTypeName[71:76], _RouteTypeName[76:79], _RouteTypeName[79:87], } // RouteTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RouteTypeString(s string) (RouteType, error) { if val, ok := _RouteTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _RouteTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RouteType values", s) } // RouteTypeValues returns all values of the enum func RouteTypeValues() []RouteType { return _RouteTypeValues } // RouteTypeStrings returns a slice of all String values of the enum func RouteTypeStrings() []string { strs := make([]string, len(_RouteTypeNames)) copy(strs, _RouteTypeNames) return strs } // IsARouteType returns "true" if the value is listed in the enum definition. "false" otherwise func (i RouteType) IsARouteType() bool { for _, v := range _RouteTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RouteType func (i RouteType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RouteType func (i *RouteType) UnmarshalText(text []byte) error { var err error *i, err = RouteTypeString(string(text)) return err } const ( _RoutingRuleActionName_0 = "unspecunicast" _RoutingRuleActionLowerName_0 = "unspecunicast" _RoutingRuleActionName_1 = "blackholeunreachableprohibit" _RoutingRuleActionLowerName_1 = "blackholeunreachableprohibit" ) var ( _RoutingRuleActionIndex_0 = [...]uint8{0, 6, 13} _RoutingRuleActionIndex_1 = [...]uint8{0, 9, 20, 28} ) func (i RoutingRuleAction) String() string { switch { case 0 <= i && i <= 1: return _RoutingRuleActionName_0[_RoutingRuleActionIndex_0[i]:_RoutingRuleActionIndex_0[i+1]] case 6 <= i && i <= 8: i -= 6 return _RoutingRuleActionName_1[_RoutingRuleActionIndex_1[i]:_RoutingRuleActionIndex_1[i+1]] default: return fmt.Sprintf("RoutingRuleAction(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RoutingRuleActionNoOp() { var x [1]struct{} _ = x[RoutingRuleActionUnspec-(0)] _ = x[RoutingRuleActionUnicast-(1)] _ = x[RoutingRuleActionBlackhole-(6)] _ = x[RoutingRuleActionUnreachable-(7)] _ = x[RoutingRuleActionProhibit-(8)] } var _RoutingRuleActionValues = []RoutingRuleAction{RoutingRuleActionUnspec, RoutingRuleActionUnicast, RoutingRuleActionBlackhole, RoutingRuleActionUnreachable, RoutingRuleActionProhibit} var _RoutingRuleActionNameToValueMap = map[string]RoutingRuleAction{ _RoutingRuleActionName_0[0:6]: RoutingRuleActionUnspec, _RoutingRuleActionLowerName_0[0:6]: RoutingRuleActionUnspec, _RoutingRuleActionName_0[6:13]: RoutingRuleActionUnicast, _RoutingRuleActionLowerName_0[6:13]: RoutingRuleActionUnicast, _RoutingRuleActionName_1[0:9]: RoutingRuleActionBlackhole, _RoutingRuleActionLowerName_1[0:9]: RoutingRuleActionBlackhole, _RoutingRuleActionName_1[9:20]: RoutingRuleActionUnreachable, _RoutingRuleActionLowerName_1[9:20]: RoutingRuleActionUnreachable, _RoutingRuleActionName_1[20:28]: RoutingRuleActionProhibit, _RoutingRuleActionLowerName_1[20:28]: RoutingRuleActionProhibit, } var _RoutingRuleActionNames = []string{ _RoutingRuleActionName_0[0:6], _RoutingRuleActionName_0[6:13], _RoutingRuleActionName_1[0:9], _RoutingRuleActionName_1[9:20], _RoutingRuleActionName_1[20:28], } // RoutingRuleActionString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RoutingRuleActionString(s string) (RoutingRuleAction, error) { if val, ok := _RoutingRuleActionNameToValueMap[s]; ok { return val, nil } if val, ok := _RoutingRuleActionNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RoutingRuleAction values", s) } // RoutingRuleActionValues returns all values of the enum func RoutingRuleActionValues() []RoutingRuleAction { return _RoutingRuleActionValues } // RoutingRuleActionStrings returns a slice of all String values of the enum func RoutingRuleActionStrings() []string { strs := make([]string, len(_RoutingRuleActionNames)) copy(strs, _RoutingRuleActionNames) return strs } // IsARoutingRuleAction returns "true" if the value is listed in the enum definition. "false" otherwise func (i RoutingRuleAction) IsARoutingRuleAction() bool { for _, v := range _RoutingRuleActionValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RoutingRuleAction func (i RoutingRuleAction) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RoutingRuleAction func (i *RoutingRuleAction) UnmarshalText(text []byte) error { var err error *i, err = RoutingRuleActionString(string(text)) return err } const _RoutingTableName = "unspec123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252defaultmainlocal" var _RoutingTableIndex = [...]uint16{0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129, 131, 133, 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 161, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 198, 201, 204, 207, 210, 213, 216, 219, 222, 225, 228, 231, 234, 237, 240, 243, 246, 249, 252, 255, 258, 261, 264, 267, 270, 273, 276, 279, 282, 285, 288, 291, 294, 297, 300, 303, 306, 309, 312, 315, 318, 321, 324, 327, 330, 333, 336, 339, 342, 345, 348, 351, 354, 357, 360, 363, 366, 369, 372, 375, 378, 381, 384, 387, 390, 393, 396, 399, 402, 405, 408, 411, 414, 417, 420, 423, 426, 429, 432, 435, 438, 441, 444, 447, 450, 453, 456, 459, 462, 465, 468, 471, 474, 477, 480, 483, 486, 489, 492, 495, 498, 501, 504, 507, 510, 513, 516, 519, 522, 525, 528, 531, 534, 537, 540, 543, 546, 549, 552, 555, 558, 561, 564, 567, 570, 573, 576, 579, 582, 585, 588, 591, 594, 597, 600, 603, 606, 609, 612, 615, 618, 621, 624, 627, 630, 633, 636, 639, 642, 645, 648, 651, 654, 661, 665, 670} const _RoutingTableLowerName = "unspec123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252defaultmainlocal" func (i RoutingTable) String() string { if i >= RoutingTable(len(_RoutingTableIndex)-1) { return fmt.Sprintf("RoutingTable(%d)", i) } return _RoutingTableName[_RoutingTableIndex[i]:_RoutingTableIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _RoutingTableNoOp() { var x [1]struct{} _ = x[TableUnspec-(0)] _ = x[Table1-(1)] _ = x[Table2-(2)] _ = x[Table3-(3)] _ = x[Table4-(4)] _ = x[Table5-(5)] _ = x[Table6-(6)] _ = x[Table7-(7)] _ = x[Table8-(8)] _ = x[Table9-(9)] _ = x[Table10-(10)] _ = x[Table11-(11)] _ = x[Table12-(12)] _ = x[Table13-(13)] _ = x[Table14-(14)] _ = x[Table15-(15)] _ = x[Table16-(16)] _ = x[Table17-(17)] _ = x[Table18-(18)] _ = x[Table19-(19)] _ = x[Table20-(20)] _ = x[Table21-(21)] _ = x[Table22-(22)] _ = x[Table23-(23)] _ = x[Table24-(24)] _ = x[Table25-(25)] _ = x[Table26-(26)] _ = x[Table27-(27)] _ = x[Table28-(28)] _ = x[Table29-(29)] _ = x[Table30-(30)] _ = x[Table31-(31)] _ = x[Table32-(32)] _ = x[Table33-(33)] _ = x[Table34-(34)] _ = x[Table35-(35)] _ = x[Table36-(36)] _ = x[Table37-(37)] _ = x[Table38-(38)] _ = x[Table39-(39)] _ = x[Table40-(40)] _ = x[Table41-(41)] _ = x[Table42-(42)] _ = x[Table43-(43)] _ = x[Table44-(44)] _ = x[Table45-(45)] _ = x[Table46-(46)] _ = x[Table47-(47)] _ = x[Table48-(48)] _ = x[Table49-(49)] _ = x[Table50-(50)] _ = x[Table51-(51)] _ = x[Table52-(52)] _ = x[Table53-(53)] _ = x[Table54-(54)] _ = x[Table55-(55)] _ = x[Table56-(56)] _ = x[Table57-(57)] _ = x[Table58-(58)] _ = x[Table59-(59)] _ = x[Table60-(60)] _ = x[Table61-(61)] _ = x[Table62-(62)] _ = x[Table63-(63)] _ = x[Table64-(64)] _ = x[Table65-(65)] _ = x[Table66-(66)] _ = x[Table67-(67)] _ = x[Table68-(68)] _ = x[Table69-(69)] _ = x[Table70-(70)] _ = x[Table71-(71)] _ = x[Table72-(72)] _ = x[Table73-(73)] _ = x[Table74-(74)] _ = x[Table75-(75)] _ = x[Table76-(76)] _ = x[Table77-(77)] _ = x[Table78-(78)] _ = x[Table79-(79)] _ = x[Table80-(80)] _ = x[Table81-(81)] _ = x[Table82-(82)] _ = x[Table83-(83)] _ = x[Table84-(84)] _ = x[Table85-(85)] _ = x[Table86-(86)] _ = x[Table87-(87)] _ = x[Table88-(88)] _ = x[Table89-(89)] _ = x[Table90-(90)] _ = x[Table91-(91)] _ = x[Table92-(92)] _ = x[Table93-(93)] _ = x[Table94-(94)] _ = x[Table95-(95)] _ = x[Table96-(96)] _ = x[Table97-(97)] _ = x[Table98-(98)] _ = x[Table99-(99)] _ = x[Table100-(100)] _ = x[Table101-(101)] _ = x[Table102-(102)] _ = x[Table103-(103)] _ = x[Table104-(104)] _ = x[Table105-(105)] _ = x[Table106-(106)] _ = x[Table107-(107)] _ = x[Table108-(108)] _ = x[Table109-(109)] _ = x[Table110-(110)] _ = x[Table111-(111)] _ = x[Table112-(112)] _ = x[Table113-(113)] _ = x[Table114-(114)] _ = x[Table115-(115)] _ = x[Table116-(116)] _ = x[Table117-(117)] _ = x[Table118-(118)] _ = x[Table119-(119)] _ = x[Table120-(120)] _ = x[Table121-(121)] _ = x[Table122-(122)] _ = x[Table123-(123)] _ = x[Table124-(124)] _ = x[Table125-(125)] _ = x[Table126-(126)] _ = x[Table127-(127)] _ = x[Table128-(128)] _ = x[Table129-(129)] _ = x[Table130-(130)] _ = x[Table131-(131)] _ = x[Table132-(132)] _ = x[Table133-(133)] _ = x[Table134-(134)] _ = x[Table135-(135)] _ = x[Table136-(136)] _ = x[Table137-(137)] _ = x[Table138-(138)] _ = x[Table139-(139)] _ = x[Table140-(140)] _ = x[Table141-(141)] _ = x[Table142-(142)] _ = x[Table143-(143)] _ = x[Table144-(144)] _ = x[Table145-(145)] _ = x[Table146-(146)] _ = x[Table147-(147)] _ = x[Table148-(148)] _ = x[Table149-(149)] _ = x[Table150-(150)] _ = x[Table151-(151)] _ = x[Table152-(152)] _ = x[Table153-(153)] _ = x[Table154-(154)] _ = x[Table155-(155)] _ = x[Table156-(156)] _ = x[Table157-(157)] _ = x[Table158-(158)] _ = x[Table159-(159)] _ = x[Table160-(160)] _ = x[Table161-(161)] _ = x[Table162-(162)] _ = x[Table163-(163)] _ = x[Table164-(164)] _ = x[Table165-(165)] _ = x[Table166-(166)] _ = x[Table167-(167)] _ = x[Table168-(168)] _ = x[Table169-(169)] _ = x[Table170-(170)] _ = x[Table171-(171)] _ = x[Table172-(172)] _ = x[Table173-(173)] _ = x[Table174-(174)] _ = x[Table175-(175)] _ = x[Table176-(176)] _ = x[Table177-(177)] _ = x[Table178-(178)] _ = x[Table179-(179)] _ = x[Table180-(180)] _ = x[Table181-(181)] _ = x[Table182-(182)] _ = x[Table183-(183)] _ = x[Table184-(184)] _ = x[Table185-(185)] _ = x[Table186-(186)] _ = x[Table187-(187)] _ = x[Table188-(188)] _ = x[Table189-(189)] _ = x[Table190-(190)] _ = x[Table191-(191)] _ = x[Table192-(192)] _ = x[Table193-(193)] _ = x[Table194-(194)] _ = x[Table195-(195)] _ = x[Table196-(196)] _ = x[Table197-(197)] _ = x[Table198-(198)] _ = x[Table199-(199)] _ = x[Table200-(200)] _ = x[Table201-(201)] _ = x[Table202-(202)] _ = x[Table203-(203)] _ = x[Table204-(204)] _ = x[Table205-(205)] _ = x[Table206-(206)] _ = x[Table207-(207)] _ = x[Table208-(208)] _ = x[Table209-(209)] _ = x[Table210-(210)] _ = x[Table211-(211)] _ = x[Table212-(212)] _ = x[Table213-(213)] _ = x[Table214-(214)] _ = x[Table215-(215)] _ = x[Table216-(216)] _ = x[Table217-(217)] _ = x[Table218-(218)] _ = x[Table219-(219)] _ = x[Table220-(220)] _ = x[Table221-(221)] _ = x[Table222-(222)] _ = x[Table223-(223)] _ = x[Table224-(224)] _ = x[Table225-(225)] _ = x[Table226-(226)] _ = x[Table227-(227)] _ = x[Table228-(228)] _ = x[Table229-(229)] _ = x[Table230-(230)] _ = x[Table231-(231)] _ = x[Table232-(232)] _ = x[Table233-(233)] _ = x[Table234-(234)] _ = x[Table235-(235)] _ = x[Table236-(236)] _ = x[Table237-(237)] _ = x[Table238-(238)] _ = x[Table239-(239)] _ = x[Table240-(240)] _ = x[Table241-(241)] _ = x[Table242-(242)] _ = x[Table243-(243)] _ = x[Table244-(244)] _ = x[Table245-(245)] _ = x[Table246-(246)] _ = x[Table247-(247)] _ = x[Table248-(248)] _ = x[Table249-(249)] _ = x[Table250-(250)] _ = x[Table251-(251)] _ = x[Table252-(252)] _ = x[TableDefault-(253)] _ = x[TableMain-(254)] _ = x[TableLocal-(255)] } var _RoutingTableValues = []RoutingTable{TableUnspec, Table1, Table2, Table3, Table4, Table5, Table6, Table7, Table8, Table9, Table10, Table11, Table12, Table13, Table14, Table15, Table16, Table17, Table18, Table19, Table20, Table21, Table22, Table23, Table24, Table25, Table26, Table27, Table28, Table29, Table30, Table31, Table32, Table33, Table34, Table35, Table36, Table37, Table38, Table39, Table40, Table41, Table42, Table43, Table44, Table45, Table46, Table47, Table48, Table49, Table50, Table51, Table52, Table53, Table54, Table55, Table56, Table57, Table58, Table59, Table60, Table61, Table62, Table63, Table64, Table65, Table66, Table67, Table68, Table69, Table70, Table71, Table72, Table73, Table74, Table75, Table76, Table77, Table78, Table79, Table80, Table81, Table82, Table83, Table84, Table85, Table86, Table87, Table88, Table89, Table90, Table91, Table92, Table93, Table94, Table95, Table96, Table97, Table98, Table99, Table100, Table101, Table102, Table103, Table104, Table105, Table106, Table107, Table108, Table109, Table110, Table111, Table112, Table113, Table114, Table115, Table116, Table117, Table118, Table119, Table120, Table121, Table122, Table123, Table124, Table125, Table126, Table127, Table128, Table129, Table130, Table131, Table132, Table133, Table134, Table135, Table136, Table137, Table138, Table139, Table140, Table141, Table142, Table143, Table144, Table145, Table146, Table147, Table148, Table149, Table150, Table151, Table152, Table153, Table154, Table155, Table156, Table157, Table158, Table159, Table160, Table161, Table162, Table163, Table164, Table165, Table166, Table167, Table168, Table169, Table170, Table171, Table172, Table173, Table174, Table175, Table176, Table177, Table178, Table179, Table180, Table181, Table182, Table183, Table184, Table185, Table186, Table187, Table188, Table189, Table190, Table191, Table192, Table193, Table194, Table195, Table196, Table197, Table198, Table199, Table200, Table201, Table202, Table203, Table204, Table205, Table206, Table207, Table208, Table209, Table210, Table211, Table212, Table213, Table214, Table215, Table216, Table217, Table218, Table219, Table220, Table221, Table222, Table223, Table224, Table225, Table226, Table227, Table228, Table229, Table230, Table231, Table232, Table233, Table234, Table235, Table236, Table237, Table238, Table239, Table240, Table241, Table242, Table243, Table244, Table245, Table246, Table247, Table248, Table249, Table250, Table251, Table252, TableDefault, TableMain, TableLocal} var _RoutingTableNameToValueMap = map[string]RoutingTable{ _RoutingTableName[0:6]: TableUnspec, _RoutingTableLowerName[0:6]: TableUnspec, _RoutingTableName[6:7]: Table1, _RoutingTableLowerName[6:7]: Table1, _RoutingTableName[7:8]: Table2, _RoutingTableLowerName[7:8]: Table2, _RoutingTableName[8:9]: Table3, _RoutingTableLowerName[8:9]: Table3, _RoutingTableName[9:10]: Table4, _RoutingTableLowerName[9:10]: Table4, _RoutingTableName[10:11]: Table5, _RoutingTableLowerName[10:11]: Table5, _RoutingTableName[11:12]: Table6, _RoutingTableLowerName[11:12]: Table6, _RoutingTableName[12:13]: Table7, _RoutingTableLowerName[12:13]: Table7, _RoutingTableName[13:14]: Table8, _RoutingTableLowerName[13:14]: Table8, _RoutingTableName[14:15]: Table9, _RoutingTableLowerName[14:15]: Table9, _RoutingTableName[15:17]: Table10, _RoutingTableLowerName[15:17]: Table10, _RoutingTableName[17:19]: Table11, _RoutingTableLowerName[17:19]: Table11, _RoutingTableName[19:21]: Table12, _RoutingTableLowerName[19:21]: Table12, _RoutingTableName[21:23]: Table13, _RoutingTableLowerName[21:23]: Table13, _RoutingTableName[23:25]: Table14, _RoutingTableLowerName[23:25]: Table14, _RoutingTableName[25:27]: Table15, _RoutingTableLowerName[25:27]: Table15, _RoutingTableName[27:29]: Table16, _RoutingTableLowerName[27:29]: Table16, _RoutingTableName[29:31]: Table17, _RoutingTableLowerName[29:31]: Table17, _RoutingTableName[31:33]: Table18, _RoutingTableLowerName[31:33]: Table18, _RoutingTableName[33:35]: Table19, _RoutingTableLowerName[33:35]: Table19, _RoutingTableName[35:37]: Table20, _RoutingTableLowerName[35:37]: Table20, _RoutingTableName[37:39]: Table21, _RoutingTableLowerName[37:39]: Table21, _RoutingTableName[39:41]: Table22, _RoutingTableLowerName[39:41]: Table22, _RoutingTableName[41:43]: Table23, _RoutingTableLowerName[41:43]: Table23, _RoutingTableName[43:45]: Table24, _RoutingTableLowerName[43:45]: Table24, _RoutingTableName[45:47]: Table25, _RoutingTableLowerName[45:47]: Table25, _RoutingTableName[47:49]: Table26, _RoutingTableLowerName[47:49]: Table26, _RoutingTableName[49:51]: Table27, _RoutingTableLowerName[49:51]: Table27, _RoutingTableName[51:53]: Table28, _RoutingTableLowerName[51:53]: Table28, _RoutingTableName[53:55]: Table29, _RoutingTableLowerName[53:55]: Table29, _RoutingTableName[55:57]: Table30, _RoutingTableLowerName[55:57]: Table30, _RoutingTableName[57:59]: Table31, _RoutingTableLowerName[57:59]: Table31, _RoutingTableName[59:61]: Table32, _RoutingTableLowerName[59:61]: Table32, _RoutingTableName[61:63]: Table33, _RoutingTableLowerName[61:63]: Table33, _RoutingTableName[63:65]: Table34, _RoutingTableLowerName[63:65]: Table34, _RoutingTableName[65:67]: Table35, _RoutingTableLowerName[65:67]: Table35, _RoutingTableName[67:69]: Table36, _RoutingTableLowerName[67:69]: Table36, _RoutingTableName[69:71]: Table37, _RoutingTableLowerName[69:71]: Table37, _RoutingTableName[71:73]: Table38, _RoutingTableLowerName[71:73]: Table38, _RoutingTableName[73:75]: Table39, _RoutingTableLowerName[73:75]: Table39, _RoutingTableName[75:77]: Table40, _RoutingTableLowerName[75:77]: Table40, _RoutingTableName[77:79]: Table41, _RoutingTableLowerName[77:79]: Table41, _RoutingTableName[79:81]: Table42, _RoutingTableLowerName[79:81]: Table42, _RoutingTableName[81:83]: Table43, _RoutingTableLowerName[81:83]: Table43, _RoutingTableName[83:85]: Table44, _RoutingTableLowerName[83:85]: Table44, _RoutingTableName[85:87]: Table45, _RoutingTableLowerName[85:87]: Table45, _RoutingTableName[87:89]: Table46, _RoutingTableLowerName[87:89]: Table46, _RoutingTableName[89:91]: Table47, _RoutingTableLowerName[89:91]: Table47, _RoutingTableName[91:93]: Table48, _RoutingTableLowerName[91:93]: Table48, _RoutingTableName[93:95]: Table49, _RoutingTableLowerName[93:95]: Table49, _RoutingTableName[95:97]: Table50, _RoutingTableLowerName[95:97]: Table50, _RoutingTableName[97:99]: Table51, _RoutingTableLowerName[97:99]: Table51, _RoutingTableName[99:101]: Table52, _RoutingTableLowerName[99:101]: Table52, _RoutingTableName[101:103]: Table53, _RoutingTableLowerName[101:103]: Table53, _RoutingTableName[103:105]: Table54, _RoutingTableLowerName[103:105]: Table54, _RoutingTableName[105:107]: Table55, _RoutingTableLowerName[105:107]: Table55, _RoutingTableName[107:109]: Table56, _RoutingTableLowerName[107:109]: Table56, _RoutingTableName[109:111]: Table57, _RoutingTableLowerName[109:111]: Table57, _RoutingTableName[111:113]: Table58, _RoutingTableLowerName[111:113]: Table58, _RoutingTableName[113:115]: Table59, _RoutingTableLowerName[113:115]: Table59, _RoutingTableName[115:117]: Table60, _RoutingTableLowerName[115:117]: Table60, _RoutingTableName[117:119]: Table61, _RoutingTableLowerName[117:119]: Table61, _RoutingTableName[119:121]: Table62, _RoutingTableLowerName[119:121]: Table62, _RoutingTableName[121:123]: Table63, _RoutingTableLowerName[121:123]: Table63, _RoutingTableName[123:125]: Table64, _RoutingTableLowerName[123:125]: Table64, _RoutingTableName[125:127]: Table65, _RoutingTableLowerName[125:127]: Table65, _RoutingTableName[127:129]: Table66, _RoutingTableLowerName[127:129]: Table66, _RoutingTableName[129:131]: Table67, _RoutingTableLowerName[129:131]: Table67, _RoutingTableName[131:133]: Table68, _RoutingTableLowerName[131:133]: Table68, _RoutingTableName[133:135]: Table69, _RoutingTableLowerName[133:135]: Table69, _RoutingTableName[135:137]: Table70, _RoutingTableLowerName[135:137]: Table70, _RoutingTableName[137:139]: Table71, _RoutingTableLowerName[137:139]: Table71, _RoutingTableName[139:141]: Table72, _RoutingTableLowerName[139:141]: Table72, _RoutingTableName[141:143]: Table73, _RoutingTableLowerName[141:143]: Table73, _RoutingTableName[143:145]: Table74, _RoutingTableLowerName[143:145]: Table74, _RoutingTableName[145:147]: Table75, _RoutingTableLowerName[145:147]: Table75, _RoutingTableName[147:149]: Table76, _RoutingTableLowerName[147:149]: Table76, _RoutingTableName[149:151]: Table77, _RoutingTableLowerName[149:151]: Table77, _RoutingTableName[151:153]: Table78, _RoutingTableLowerName[151:153]: Table78, _RoutingTableName[153:155]: Table79, _RoutingTableLowerName[153:155]: Table79, _RoutingTableName[155:157]: Table80, _RoutingTableLowerName[155:157]: Table80, _RoutingTableName[157:159]: Table81, _RoutingTableLowerName[157:159]: Table81, _RoutingTableName[159:161]: Table82, _RoutingTableLowerName[159:161]: Table82, _RoutingTableName[161:163]: Table83, _RoutingTableLowerName[161:163]: Table83, _RoutingTableName[163:165]: Table84, _RoutingTableLowerName[163:165]: Table84, _RoutingTableName[165:167]: Table85, _RoutingTableLowerName[165:167]: Table85, _RoutingTableName[167:169]: Table86, _RoutingTableLowerName[167:169]: Table86, _RoutingTableName[169:171]: Table87, _RoutingTableLowerName[169:171]: Table87, _RoutingTableName[171:173]: Table88, _RoutingTableLowerName[171:173]: Table88, _RoutingTableName[173:175]: Table89, _RoutingTableLowerName[173:175]: Table89, _RoutingTableName[175:177]: Table90, _RoutingTableLowerName[175:177]: Table90, _RoutingTableName[177:179]: Table91, _RoutingTableLowerName[177:179]: Table91, _RoutingTableName[179:181]: Table92, _RoutingTableLowerName[179:181]: Table92, _RoutingTableName[181:183]: Table93, _RoutingTableLowerName[181:183]: Table93, _RoutingTableName[183:185]: Table94, _RoutingTableLowerName[183:185]: Table94, _RoutingTableName[185:187]: Table95, _RoutingTableLowerName[185:187]: Table95, _RoutingTableName[187:189]: Table96, _RoutingTableLowerName[187:189]: Table96, _RoutingTableName[189:191]: Table97, _RoutingTableLowerName[189:191]: Table97, _RoutingTableName[191:193]: Table98, _RoutingTableLowerName[191:193]: Table98, _RoutingTableName[193:195]: Table99, _RoutingTableLowerName[193:195]: Table99, _RoutingTableName[195:198]: Table100, _RoutingTableLowerName[195:198]: Table100, _RoutingTableName[198:201]: Table101, _RoutingTableLowerName[198:201]: Table101, _RoutingTableName[201:204]: Table102, _RoutingTableLowerName[201:204]: Table102, _RoutingTableName[204:207]: Table103, _RoutingTableLowerName[204:207]: Table103, _RoutingTableName[207:210]: Table104, _RoutingTableLowerName[207:210]: Table104, _RoutingTableName[210:213]: Table105, _RoutingTableLowerName[210:213]: Table105, _RoutingTableName[213:216]: Table106, _RoutingTableLowerName[213:216]: Table106, _RoutingTableName[216:219]: Table107, _RoutingTableLowerName[216:219]: Table107, _RoutingTableName[219:222]: Table108, _RoutingTableLowerName[219:222]: Table108, _RoutingTableName[222:225]: Table109, _RoutingTableLowerName[222:225]: Table109, _RoutingTableName[225:228]: Table110, _RoutingTableLowerName[225:228]: Table110, _RoutingTableName[228:231]: Table111, _RoutingTableLowerName[228:231]: Table111, _RoutingTableName[231:234]: Table112, _RoutingTableLowerName[231:234]: Table112, _RoutingTableName[234:237]: Table113, _RoutingTableLowerName[234:237]: Table113, _RoutingTableName[237:240]: Table114, _RoutingTableLowerName[237:240]: Table114, _RoutingTableName[240:243]: Table115, _RoutingTableLowerName[240:243]: Table115, _RoutingTableName[243:246]: Table116, _RoutingTableLowerName[243:246]: Table116, _RoutingTableName[246:249]: Table117, _RoutingTableLowerName[246:249]: Table117, _RoutingTableName[249:252]: Table118, _RoutingTableLowerName[249:252]: Table118, _RoutingTableName[252:255]: Table119, _RoutingTableLowerName[252:255]: Table119, _RoutingTableName[255:258]: Table120, _RoutingTableLowerName[255:258]: Table120, _RoutingTableName[258:261]: Table121, _RoutingTableLowerName[258:261]: Table121, _RoutingTableName[261:264]: Table122, _RoutingTableLowerName[261:264]: Table122, _RoutingTableName[264:267]: Table123, _RoutingTableLowerName[264:267]: Table123, _RoutingTableName[267:270]: Table124, _RoutingTableLowerName[267:270]: Table124, _RoutingTableName[270:273]: Table125, _RoutingTableLowerName[270:273]: Table125, _RoutingTableName[273:276]: Table126, _RoutingTableLowerName[273:276]: Table126, _RoutingTableName[276:279]: Table127, _RoutingTableLowerName[276:279]: Table127, _RoutingTableName[279:282]: Table128, _RoutingTableLowerName[279:282]: Table128, _RoutingTableName[282:285]: Table129, _RoutingTableLowerName[282:285]: Table129, _RoutingTableName[285:288]: Table130, _RoutingTableLowerName[285:288]: Table130, _RoutingTableName[288:291]: Table131, _RoutingTableLowerName[288:291]: Table131, _RoutingTableName[291:294]: Table132, _RoutingTableLowerName[291:294]: Table132, _RoutingTableName[294:297]: Table133, _RoutingTableLowerName[294:297]: Table133, _RoutingTableName[297:300]: Table134, _RoutingTableLowerName[297:300]: Table134, _RoutingTableName[300:303]: Table135, _RoutingTableLowerName[300:303]: Table135, _RoutingTableName[303:306]: Table136, _RoutingTableLowerName[303:306]: Table136, _RoutingTableName[306:309]: Table137, _RoutingTableLowerName[306:309]: Table137, _RoutingTableName[309:312]: Table138, _RoutingTableLowerName[309:312]: Table138, _RoutingTableName[312:315]: Table139, _RoutingTableLowerName[312:315]: Table139, _RoutingTableName[315:318]: Table140, _RoutingTableLowerName[315:318]: Table140, _RoutingTableName[318:321]: Table141, _RoutingTableLowerName[318:321]: Table141, _RoutingTableName[321:324]: Table142, _RoutingTableLowerName[321:324]: Table142, _RoutingTableName[324:327]: Table143, _RoutingTableLowerName[324:327]: Table143, _RoutingTableName[327:330]: Table144, _RoutingTableLowerName[327:330]: Table144, _RoutingTableName[330:333]: Table145, _RoutingTableLowerName[330:333]: Table145, _RoutingTableName[333:336]: Table146, _RoutingTableLowerName[333:336]: Table146, _RoutingTableName[336:339]: Table147, _RoutingTableLowerName[336:339]: Table147, _RoutingTableName[339:342]: Table148, _RoutingTableLowerName[339:342]: Table148, _RoutingTableName[342:345]: Table149, _RoutingTableLowerName[342:345]: Table149, _RoutingTableName[345:348]: Table150, _RoutingTableLowerName[345:348]: Table150, _RoutingTableName[348:351]: Table151, _RoutingTableLowerName[348:351]: Table151, _RoutingTableName[351:354]: Table152, _RoutingTableLowerName[351:354]: Table152, _RoutingTableName[354:357]: Table153, _RoutingTableLowerName[354:357]: Table153, _RoutingTableName[357:360]: Table154, _RoutingTableLowerName[357:360]: Table154, _RoutingTableName[360:363]: Table155, _RoutingTableLowerName[360:363]: Table155, _RoutingTableName[363:366]: Table156, _RoutingTableLowerName[363:366]: Table156, _RoutingTableName[366:369]: Table157, _RoutingTableLowerName[366:369]: Table157, _RoutingTableName[369:372]: Table158, _RoutingTableLowerName[369:372]: Table158, _RoutingTableName[372:375]: Table159, _RoutingTableLowerName[372:375]: Table159, _RoutingTableName[375:378]: Table160, _RoutingTableLowerName[375:378]: Table160, _RoutingTableName[378:381]: Table161, _RoutingTableLowerName[378:381]: Table161, _RoutingTableName[381:384]: Table162, _RoutingTableLowerName[381:384]: Table162, _RoutingTableName[384:387]: Table163, _RoutingTableLowerName[384:387]: Table163, _RoutingTableName[387:390]: Table164, _RoutingTableLowerName[387:390]: Table164, _RoutingTableName[390:393]: Table165, _RoutingTableLowerName[390:393]: Table165, _RoutingTableName[393:396]: Table166, _RoutingTableLowerName[393:396]: Table166, _RoutingTableName[396:399]: Table167, _RoutingTableLowerName[396:399]: Table167, _RoutingTableName[399:402]: Table168, _RoutingTableLowerName[399:402]: Table168, _RoutingTableName[402:405]: Table169, _RoutingTableLowerName[402:405]: Table169, _RoutingTableName[405:408]: Table170, _RoutingTableLowerName[405:408]: Table170, _RoutingTableName[408:411]: Table171, _RoutingTableLowerName[408:411]: Table171, _RoutingTableName[411:414]: Table172, _RoutingTableLowerName[411:414]: Table172, _RoutingTableName[414:417]: Table173, _RoutingTableLowerName[414:417]: Table173, _RoutingTableName[417:420]: Table174, _RoutingTableLowerName[417:420]: Table174, _RoutingTableName[420:423]: Table175, _RoutingTableLowerName[420:423]: Table175, _RoutingTableName[423:426]: Table176, _RoutingTableLowerName[423:426]: Table176, _RoutingTableName[426:429]: Table177, _RoutingTableLowerName[426:429]: Table177, _RoutingTableName[429:432]: Table178, _RoutingTableLowerName[429:432]: Table178, _RoutingTableName[432:435]: Table179, _RoutingTableLowerName[432:435]: Table179, _RoutingTableName[435:438]: Table180, _RoutingTableLowerName[435:438]: Table180, _RoutingTableName[438:441]: Table181, _RoutingTableLowerName[438:441]: Table181, _RoutingTableName[441:444]: Table182, _RoutingTableLowerName[441:444]: Table182, _RoutingTableName[444:447]: Table183, _RoutingTableLowerName[444:447]: Table183, _RoutingTableName[447:450]: Table184, _RoutingTableLowerName[447:450]: Table184, _RoutingTableName[450:453]: Table185, _RoutingTableLowerName[450:453]: Table185, _RoutingTableName[453:456]: Table186, _RoutingTableLowerName[453:456]: Table186, _RoutingTableName[456:459]: Table187, _RoutingTableLowerName[456:459]: Table187, _RoutingTableName[459:462]: Table188, _RoutingTableLowerName[459:462]: Table188, _RoutingTableName[462:465]: Table189, _RoutingTableLowerName[462:465]: Table189, _RoutingTableName[465:468]: Table190, _RoutingTableLowerName[465:468]: Table190, _RoutingTableName[468:471]: Table191, _RoutingTableLowerName[468:471]: Table191, _RoutingTableName[471:474]: Table192, _RoutingTableLowerName[471:474]: Table192, _RoutingTableName[474:477]: Table193, _RoutingTableLowerName[474:477]: Table193, _RoutingTableName[477:480]: Table194, _RoutingTableLowerName[477:480]: Table194, _RoutingTableName[480:483]: Table195, _RoutingTableLowerName[480:483]: Table195, _RoutingTableName[483:486]: Table196, _RoutingTableLowerName[483:486]: Table196, _RoutingTableName[486:489]: Table197, _RoutingTableLowerName[486:489]: Table197, _RoutingTableName[489:492]: Table198, _RoutingTableLowerName[489:492]: Table198, _RoutingTableName[492:495]: Table199, _RoutingTableLowerName[492:495]: Table199, _RoutingTableName[495:498]: Table200, _RoutingTableLowerName[495:498]: Table200, _RoutingTableName[498:501]: Table201, _RoutingTableLowerName[498:501]: Table201, _RoutingTableName[501:504]: Table202, _RoutingTableLowerName[501:504]: Table202, _RoutingTableName[504:507]: Table203, _RoutingTableLowerName[504:507]: Table203, _RoutingTableName[507:510]: Table204, _RoutingTableLowerName[507:510]: Table204, _RoutingTableName[510:513]: Table205, _RoutingTableLowerName[510:513]: Table205, _RoutingTableName[513:516]: Table206, _RoutingTableLowerName[513:516]: Table206, _RoutingTableName[516:519]: Table207, _RoutingTableLowerName[516:519]: Table207, _RoutingTableName[519:522]: Table208, _RoutingTableLowerName[519:522]: Table208, _RoutingTableName[522:525]: Table209, _RoutingTableLowerName[522:525]: Table209, _RoutingTableName[525:528]: Table210, _RoutingTableLowerName[525:528]: Table210, _RoutingTableName[528:531]: Table211, _RoutingTableLowerName[528:531]: Table211, _RoutingTableName[531:534]: Table212, _RoutingTableLowerName[531:534]: Table212, _RoutingTableName[534:537]: Table213, _RoutingTableLowerName[534:537]: Table213, _RoutingTableName[537:540]: Table214, _RoutingTableLowerName[537:540]: Table214, _RoutingTableName[540:543]: Table215, _RoutingTableLowerName[540:543]: Table215, _RoutingTableName[543:546]: Table216, _RoutingTableLowerName[543:546]: Table216, _RoutingTableName[546:549]: Table217, _RoutingTableLowerName[546:549]: Table217, _RoutingTableName[549:552]: Table218, _RoutingTableLowerName[549:552]: Table218, _RoutingTableName[552:555]: Table219, _RoutingTableLowerName[552:555]: Table219, _RoutingTableName[555:558]: Table220, _RoutingTableLowerName[555:558]: Table220, _RoutingTableName[558:561]: Table221, _RoutingTableLowerName[558:561]: Table221, _RoutingTableName[561:564]: Table222, _RoutingTableLowerName[561:564]: Table222, _RoutingTableName[564:567]: Table223, _RoutingTableLowerName[564:567]: Table223, _RoutingTableName[567:570]: Table224, _RoutingTableLowerName[567:570]: Table224, _RoutingTableName[570:573]: Table225, _RoutingTableLowerName[570:573]: Table225, _RoutingTableName[573:576]: Table226, _RoutingTableLowerName[573:576]: Table226, _RoutingTableName[576:579]: Table227, _RoutingTableLowerName[576:579]: Table227, _RoutingTableName[579:582]: Table228, _RoutingTableLowerName[579:582]: Table228, _RoutingTableName[582:585]: Table229, _RoutingTableLowerName[582:585]: Table229, _RoutingTableName[585:588]: Table230, _RoutingTableLowerName[585:588]: Table230, _RoutingTableName[588:591]: Table231, _RoutingTableLowerName[588:591]: Table231, _RoutingTableName[591:594]: Table232, _RoutingTableLowerName[591:594]: Table232, _RoutingTableName[594:597]: Table233, _RoutingTableLowerName[594:597]: Table233, _RoutingTableName[597:600]: Table234, _RoutingTableLowerName[597:600]: Table234, _RoutingTableName[600:603]: Table235, _RoutingTableLowerName[600:603]: Table235, _RoutingTableName[603:606]: Table236, _RoutingTableLowerName[603:606]: Table236, _RoutingTableName[606:609]: Table237, _RoutingTableLowerName[606:609]: Table237, _RoutingTableName[609:612]: Table238, _RoutingTableLowerName[609:612]: Table238, _RoutingTableName[612:615]: Table239, _RoutingTableLowerName[612:615]: Table239, _RoutingTableName[615:618]: Table240, _RoutingTableLowerName[615:618]: Table240, _RoutingTableName[618:621]: Table241, _RoutingTableLowerName[618:621]: Table241, _RoutingTableName[621:624]: Table242, _RoutingTableLowerName[621:624]: Table242, _RoutingTableName[624:627]: Table243, _RoutingTableLowerName[624:627]: Table243, _RoutingTableName[627:630]: Table244, _RoutingTableLowerName[627:630]: Table244, _RoutingTableName[630:633]: Table245, _RoutingTableLowerName[630:633]: Table245, _RoutingTableName[633:636]: Table246, _RoutingTableLowerName[633:636]: Table246, _RoutingTableName[636:639]: Table247, _RoutingTableLowerName[636:639]: Table247, _RoutingTableName[639:642]: Table248, _RoutingTableLowerName[639:642]: Table248, _RoutingTableName[642:645]: Table249, _RoutingTableLowerName[642:645]: Table249, _RoutingTableName[645:648]: Table250, _RoutingTableLowerName[645:648]: Table250, _RoutingTableName[648:651]: Table251, _RoutingTableLowerName[648:651]: Table251, _RoutingTableName[651:654]: Table252, _RoutingTableLowerName[651:654]: Table252, _RoutingTableName[654:661]: TableDefault, _RoutingTableLowerName[654:661]: TableDefault, _RoutingTableName[661:665]: TableMain, _RoutingTableLowerName[661:665]: TableMain, _RoutingTableName[665:670]: TableLocal, _RoutingTableLowerName[665:670]: TableLocal, } var _RoutingTableNames = []string{ _RoutingTableName[0:6], _RoutingTableName[6:7], _RoutingTableName[7:8], _RoutingTableName[8:9], _RoutingTableName[9:10], _RoutingTableName[10:11], _RoutingTableName[11:12], _RoutingTableName[12:13], _RoutingTableName[13:14], _RoutingTableName[14:15], _RoutingTableName[15:17], _RoutingTableName[17:19], _RoutingTableName[19:21], _RoutingTableName[21:23], _RoutingTableName[23:25], _RoutingTableName[25:27], _RoutingTableName[27:29], _RoutingTableName[29:31], _RoutingTableName[31:33], _RoutingTableName[33:35], _RoutingTableName[35:37], _RoutingTableName[37:39], _RoutingTableName[39:41], _RoutingTableName[41:43], _RoutingTableName[43:45], _RoutingTableName[45:47], _RoutingTableName[47:49], _RoutingTableName[49:51], _RoutingTableName[51:53], _RoutingTableName[53:55], _RoutingTableName[55:57], _RoutingTableName[57:59], _RoutingTableName[59:61], _RoutingTableName[61:63], _RoutingTableName[63:65], _RoutingTableName[65:67], _RoutingTableName[67:69], _RoutingTableName[69:71], _RoutingTableName[71:73], _RoutingTableName[73:75], _RoutingTableName[75:77], _RoutingTableName[77:79], _RoutingTableName[79:81], _RoutingTableName[81:83], _RoutingTableName[83:85], _RoutingTableName[85:87], _RoutingTableName[87:89], _RoutingTableName[89:91], _RoutingTableName[91:93], _RoutingTableName[93:95], _RoutingTableName[95:97], _RoutingTableName[97:99], _RoutingTableName[99:101], _RoutingTableName[101:103], _RoutingTableName[103:105], _RoutingTableName[105:107], _RoutingTableName[107:109], _RoutingTableName[109:111], _RoutingTableName[111:113], _RoutingTableName[113:115], _RoutingTableName[115:117], _RoutingTableName[117:119], _RoutingTableName[119:121], _RoutingTableName[121:123], _RoutingTableName[123:125], _RoutingTableName[125:127], _RoutingTableName[127:129], _RoutingTableName[129:131], _RoutingTableName[131:133], _RoutingTableName[133:135], _RoutingTableName[135:137], _RoutingTableName[137:139], _RoutingTableName[139:141], _RoutingTableName[141:143], _RoutingTableName[143:145], _RoutingTableName[145:147], _RoutingTableName[147:149], _RoutingTableName[149:151], _RoutingTableName[151:153], _RoutingTableName[153:155], _RoutingTableName[155:157], _RoutingTableName[157:159], _RoutingTableName[159:161], _RoutingTableName[161:163], _RoutingTableName[163:165], _RoutingTableName[165:167], _RoutingTableName[167:169], _RoutingTableName[169:171], _RoutingTableName[171:173], _RoutingTableName[173:175], _RoutingTableName[175:177], _RoutingTableName[177:179], _RoutingTableName[179:181], _RoutingTableName[181:183], _RoutingTableName[183:185], _RoutingTableName[185:187], _RoutingTableName[187:189], _RoutingTableName[189:191], _RoutingTableName[191:193], _RoutingTableName[193:195], _RoutingTableName[195:198], _RoutingTableName[198:201], _RoutingTableName[201:204], _RoutingTableName[204:207], _RoutingTableName[207:210], _RoutingTableName[210:213], _RoutingTableName[213:216], _RoutingTableName[216:219], _RoutingTableName[219:222], _RoutingTableName[222:225], _RoutingTableName[225:228], _RoutingTableName[228:231], _RoutingTableName[231:234], _RoutingTableName[234:237], _RoutingTableName[237:240], _RoutingTableName[240:243], _RoutingTableName[243:246], _RoutingTableName[246:249], _RoutingTableName[249:252], _RoutingTableName[252:255], _RoutingTableName[255:258], _RoutingTableName[258:261], _RoutingTableName[261:264], _RoutingTableName[264:267], _RoutingTableName[267:270], _RoutingTableName[270:273], _RoutingTableName[273:276], _RoutingTableName[276:279], _RoutingTableName[279:282], _RoutingTableName[282:285], _RoutingTableName[285:288], _RoutingTableName[288:291], _RoutingTableName[291:294], _RoutingTableName[294:297], _RoutingTableName[297:300], _RoutingTableName[300:303], _RoutingTableName[303:306], _RoutingTableName[306:309], _RoutingTableName[309:312], _RoutingTableName[312:315], _RoutingTableName[315:318], _RoutingTableName[318:321], _RoutingTableName[321:324], _RoutingTableName[324:327], _RoutingTableName[327:330], _RoutingTableName[330:333], _RoutingTableName[333:336], _RoutingTableName[336:339], _RoutingTableName[339:342], _RoutingTableName[342:345], _RoutingTableName[345:348], _RoutingTableName[348:351], _RoutingTableName[351:354], _RoutingTableName[354:357], _RoutingTableName[357:360], _RoutingTableName[360:363], _RoutingTableName[363:366], _RoutingTableName[366:369], _RoutingTableName[369:372], _RoutingTableName[372:375], _RoutingTableName[375:378], _RoutingTableName[378:381], _RoutingTableName[381:384], _RoutingTableName[384:387], _RoutingTableName[387:390], _RoutingTableName[390:393], _RoutingTableName[393:396], _RoutingTableName[396:399], _RoutingTableName[399:402], _RoutingTableName[402:405], _RoutingTableName[405:408], _RoutingTableName[408:411], _RoutingTableName[411:414], _RoutingTableName[414:417], _RoutingTableName[417:420], _RoutingTableName[420:423], _RoutingTableName[423:426], _RoutingTableName[426:429], _RoutingTableName[429:432], _RoutingTableName[432:435], _RoutingTableName[435:438], _RoutingTableName[438:441], _RoutingTableName[441:444], _RoutingTableName[444:447], _RoutingTableName[447:450], _RoutingTableName[450:453], _RoutingTableName[453:456], _RoutingTableName[456:459], _RoutingTableName[459:462], _RoutingTableName[462:465], _RoutingTableName[465:468], _RoutingTableName[468:471], _RoutingTableName[471:474], _RoutingTableName[474:477], _RoutingTableName[477:480], _RoutingTableName[480:483], _RoutingTableName[483:486], _RoutingTableName[486:489], _RoutingTableName[489:492], _RoutingTableName[492:495], _RoutingTableName[495:498], _RoutingTableName[498:501], _RoutingTableName[501:504], _RoutingTableName[504:507], _RoutingTableName[507:510], _RoutingTableName[510:513], _RoutingTableName[513:516], _RoutingTableName[516:519], _RoutingTableName[519:522], _RoutingTableName[522:525], _RoutingTableName[525:528], _RoutingTableName[528:531], _RoutingTableName[531:534], _RoutingTableName[534:537], _RoutingTableName[537:540], _RoutingTableName[540:543], _RoutingTableName[543:546], _RoutingTableName[546:549], _RoutingTableName[549:552], _RoutingTableName[552:555], _RoutingTableName[555:558], _RoutingTableName[558:561], _RoutingTableName[561:564], _RoutingTableName[564:567], _RoutingTableName[567:570], _RoutingTableName[570:573], _RoutingTableName[573:576], _RoutingTableName[576:579], _RoutingTableName[579:582], _RoutingTableName[582:585], _RoutingTableName[585:588], _RoutingTableName[588:591], _RoutingTableName[591:594], _RoutingTableName[594:597], _RoutingTableName[597:600], _RoutingTableName[600:603], _RoutingTableName[603:606], _RoutingTableName[606:609], _RoutingTableName[609:612], _RoutingTableName[612:615], _RoutingTableName[615:618], _RoutingTableName[618:621], _RoutingTableName[621:624], _RoutingTableName[624:627], _RoutingTableName[627:630], _RoutingTableName[630:633], _RoutingTableName[633:636], _RoutingTableName[636:639], _RoutingTableName[639:642], _RoutingTableName[642:645], _RoutingTableName[645:648], _RoutingTableName[648:651], _RoutingTableName[651:654], _RoutingTableName[654:661], _RoutingTableName[661:665], _RoutingTableName[665:670], } // RoutingTableString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func RoutingTableString(s string) (RoutingTable, error) { if val, ok := _RoutingTableNameToValueMap[s]; ok { return val, nil } if val, ok := _RoutingTableNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to RoutingTable values", s) } // RoutingTableValues returns all values of the enum func RoutingTableValues() []RoutingTable { return _RoutingTableValues } // RoutingTableStrings returns a slice of all String values of the enum func RoutingTableStrings() []string { strs := make([]string, len(_RoutingTableNames)) copy(strs, _RoutingTableNames) return strs } // IsARoutingTable returns "true" if the value is listed in the enum definition. "false" otherwise func (i RoutingTable) IsARoutingTable() bool { for _, v := range _RoutingTableValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for RoutingTable func (i RoutingTable) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for RoutingTable func (i *RoutingTable) UnmarshalText(text []byte) error { var err error *i, err = RoutingTableString(string(text)) return err } const ( _ScopeName_0 = "global" _ScopeLowerName_0 = "global" _ScopeName_1 = "site" _ScopeLowerName_1 = "site" _ScopeName_2 = "linkhostnowhere" _ScopeLowerName_2 = "linkhostnowhere" ) var ( _ScopeIndex_0 = [...]uint8{0, 6} _ScopeIndex_1 = [...]uint8{0, 4} _ScopeIndex_2 = [...]uint8{0, 4, 8, 15} ) func (i Scope) String() string { switch { case i == 0: return _ScopeName_0 case i == 200: return _ScopeName_1 case 253 <= i && i <= 255: i -= 253 return _ScopeName_2[_ScopeIndex_2[i]:_ScopeIndex_2[i+1]] default: return fmt.Sprintf("Scope(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ScopeNoOp() { var x [1]struct{} _ = x[ScopeGlobal-(0)] _ = x[ScopeSite-(200)] _ = x[ScopeLink-(253)] _ = x[ScopeHost-(254)] _ = x[ScopeNowhere-(255)] } var _ScopeValues = []Scope{ScopeGlobal, ScopeSite, ScopeLink, ScopeHost, ScopeNowhere} var _ScopeNameToValueMap = map[string]Scope{ _ScopeName_0[0:6]: ScopeGlobal, _ScopeLowerName_0[0:6]: ScopeGlobal, _ScopeName_1[0:4]: ScopeSite, _ScopeLowerName_1[0:4]: ScopeSite, _ScopeName_2[0:4]: ScopeLink, _ScopeLowerName_2[0:4]: ScopeLink, _ScopeName_2[4:8]: ScopeHost, _ScopeLowerName_2[4:8]: ScopeHost, _ScopeName_2[8:15]: ScopeNowhere, _ScopeLowerName_2[8:15]: ScopeNowhere, } var _ScopeNames = []string{ _ScopeName_0[0:6], _ScopeName_1[0:4], _ScopeName_2[0:4], _ScopeName_2[4:8], _ScopeName_2[8:15], } // ScopeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ScopeString(s string) (Scope, error) { if val, ok := _ScopeNameToValueMap[s]; ok { return val, nil } if val, ok := _ScopeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Scope values", s) } // ScopeValues returns all values of the enum func ScopeValues() []Scope { return _ScopeValues } // ScopeStrings returns a slice of all String values of the enum func ScopeStrings() []string { strs := make([]string, len(_ScopeNames)) copy(strs, _ScopeNames) return strs } // IsAScope returns "true" if the value is listed in the enum definition. "false" otherwise func (i Scope) IsAScope() bool { for _, v := range _ScopeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Scope func (i Scope) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Scope func (i *Scope) UnmarshalText(text []byte) error { var err error *i, err = ScopeString(string(text)) return err } const _StatusName = "addressesconnectivityhostnameetcfiles" var _StatusIndex = [...]uint8{0, 9, 21, 29, 37} const _StatusLowerName = "addressesconnectivityhostnameetcfiles" func (i Status) String() string { i -= 1 if i < 0 || i >= Status(len(_StatusIndex)-1) { return fmt.Sprintf("Status(%d)", i+1) } return _StatusName[_StatusIndex[i]:_StatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _StatusNoOp() { var x [1]struct{} _ = x[StatusAddresses-(1)] _ = x[StatusConnectivity-(2)] _ = x[StatusHostname-(3)] _ = x[StatusEtcFiles-(4)] } var _StatusValues = []Status{StatusAddresses, StatusConnectivity, StatusHostname, StatusEtcFiles} var _StatusNameToValueMap = map[string]Status{ _StatusName[0:9]: StatusAddresses, _StatusLowerName[0:9]: StatusAddresses, _StatusName[9:21]: StatusConnectivity, _StatusLowerName[9:21]: StatusConnectivity, _StatusName[21:29]: StatusHostname, _StatusLowerName[21:29]: StatusHostname, _StatusName[29:37]: StatusEtcFiles, _StatusLowerName[29:37]: StatusEtcFiles, } var _StatusNames = []string{ _StatusName[0:9], _StatusName[9:21], _StatusName[21:29], _StatusName[29:37], } // StatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func StatusString(s string) (Status, error) { if val, ok := _StatusNameToValueMap[s]; ok { return val, nil } if val, ok := _StatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Status values", s) } // StatusValues returns all values of the enum func StatusValues() []Status { return _StatusValues } // StatusStrings returns a slice of all String values of the enum func StatusStrings() []string { strs := make([]string, len(_StatusNames)) copy(strs, _StatusNames) return strs } // IsAStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i Status) IsAStatus() bool { for _, v := range _StatusValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Status func (i Status) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Status func (i *Status) UnmarshalText(text []byte) error { var err error *i, err = StatusString(string(text)) return err } const ( _VLANProtocolName_0 = "802.1q" _VLANProtocolLowerName_0 = "802.1q" _VLANProtocolName_1 = "802.1ad" _VLANProtocolLowerName_1 = "802.1ad" ) var ( _VLANProtocolIndex_0 = [...]uint8{0, 6} _VLANProtocolIndex_1 = [...]uint8{0, 7} ) func (i VLANProtocol) String() string { switch { case i == 33024: return _VLANProtocolName_0 case i == 34984: return _VLANProtocolName_1 default: return fmt.Sprintf("VLANProtocol(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _VLANProtocolNoOp() { var x [1]struct{} _ = x[VLANProtocol8021Q-(33024)] _ = x[VLANProtocol8021AD-(34984)] } var _VLANProtocolValues = []VLANProtocol{VLANProtocol8021Q, VLANProtocol8021AD} var _VLANProtocolNameToValueMap = map[string]VLANProtocol{ _VLANProtocolName_0[0:6]: VLANProtocol8021Q, _VLANProtocolLowerName_0[0:6]: VLANProtocol8021Q, _VLANProtocolName_1[0:7]: VLANProtocol8021AD, _VLANProtocolLowerName_1[0:7]: VLANProtocol8021AD, } var _VLANProtocolNames = []string{ _VLANProtocolName_0[0:6], _VLANProtocolName_1[0:7], } // VLANProtocolString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func VLANProtocolString(s string) (VLANProtocol, error) { if val, ok := _VLANProtocolNameToValueMap[s]; ok { return val, nil } if val, ok := _VLANProtocolNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to VLANProtocol values", s) } // VLANProtocolValues returns all values of the enum func VLANProtocolValues() []VLANProtocol { return _VLANProtocolValues } // VLANProtocolStrings returns a slice of all String values of the enum func VLANProtocolStrings() []string { strs := make([]string, len(_VLANProtocolNames)) copy(strs, _VLANProtocolNames) return strs } // IsAVLANProtocol returns "true" if the value is listed in the enum definition. "false" otherwise func (i VLANProtocol) IsAVLANProtocol() bool { for _, v := range _VLANProtocolValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for VLANProtocol func (i VLANProtocol) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for VLANProtocol func (i *VLANProtocol) UnmarshalText(text []byte) error { var err error *i, err = VLANProtocolString(string(text)) return err } const ( _WOLModeName_0 = "phyunicast" _WOLModeLowerName_0 = "phyunicast" _WOLModeName_1 = "multicast" _WOLModeLowerName_1 = "multicast" _WOLModeName_2 = "broadcast" _WOLModeLowerName_2 = "broadcast" _WOLModeName_3 = "magic" _WOLModeLowerName_3 = "magic" _WOLModeName_4 = "magicsecure" _WOLModeLowerName_4 = "magicsecure" _WOLModeName_5 = "filter" _WOLModeLowerName_5 = "filter" ) var ( _WOLModeIndex_0 = [...]uint8{0, 3, 10} _WOLModeIndex_1 = [...]uint8{0, 9} _WOLModeIndex_2 = [...]uint8{0, 9} _WOLModeIndex_3 = [...]uint8{0, 5} _WOLModeIndex_4 = [...]uint8{0, 11} _WOLModeIndex_5 = [...]uint8{0, 6} ) func (i WOLMode) String() string { switch { case 1 <= i && i <= 2: i -= 1 return _WOLModeName_0[_WOLModeIndex_0[i]:_WOLModeIndex_0[i+1]] case i == 4: return _WOLModeName_1 case i == 8: return _WOLModeName_2 case i == 32: return _WOLModeName_3 case i == 64: return _WOLModeName_4 case i == 128: return _WOLModeName_5 default: return fmt.Sprintf("WOLMode(%d)", i) } } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _WOLModeNoOp() { var x [1]struct{} _ = x[WOLModePhy-(1)] _ = x[WOLModeUnicast-(2)] _ = x[WOLModeMulticast-(4)] _ = x[WOLModeBroadcast-(8)] _ = x[WOLModeMagic-(32)] _ = x[WOLModeMagicSecure-(64)] _ = x[WOLModeFilter-(128)] } var _WOLModeValues = []WOLMode{WOLModePhy, WOLModeUnicast, WOLModeMulticast, WOLModeBroadcast, WOLModeMagic, WOLModeMagicSecure, WOLModeFilter} var _WOLModeNameToValueMap = map[string]WOLMode{ _WOLModeName_0[0:3]: WOLModePhy, _WOLModeLowerName_0[0:3]: WOLModePhy, _WOLModeName_0[3:10]: WOLModeUnicast, _WOLModeLowerName_0[3:10]: WOLModeUnicast, _WOLModeName_1[0:9]: WOLModeMulticast, _WOLModeLowerName_1[0:9]: WOLModeMulticast, _WOLModeName_2[0:9]: WOLModeBroadcast, _WOLModeLowerName_2[0:9]: WOLModeBroadcast, _WOLModeName_3[0:5]: WOLModeMagic, _WOLModeLowerName_3[0:5]: WOLModeMagic, _WOLModeName_4[0:11]: WOLModeMagicSecure, _WOLModeLowerName_4[0:11]: WOLModeMagicSecure, _WOLModeName_5[0:6]: WOLModeFilter, _WOLModeLowerName_5[0:6]: WOLModeFilter, } var _WOLModeNames = []string{ _WOLModeName_0[0:3], _WOLModeName_0[3:10], _WOLModeName_1[0:9], _WOLModeName_2[0:9], _WOLModeName_3[0:5], _WOLModeName_4[0:11], _WOLModeName_5[0:6], } // WOLModeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func WOLModeString(s string) (WOLMode, error) { if val, ok := _WOLModeNameToValueMap[s]; ok { return val, nil } if val, ok := _WOLModeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to WOLMode values", s) } // WOLModeValues returns all values of the enum func WOLModeValues() []WOLMode { return _WOLModeValues } // WOLModeStrings returns a slice of all String values of the enum func WOLModeStrings() []string { strs := make([]string, len(_WOLModeNames)) copy(strs, _WOLModeNames) return strs } // IsAWOLMode returns "true" if the value is listed in the enum definition. "false" otherwise func (i WOLMode) IsAWOLMode() bool { for _, v := range _WOLModeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for WOLMode func (i WOLMode) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for WOLMode func (i *WOLMode) UnmarshalText(text []byte) error { var err error *i, err = WOLModeString(string(text)) return err } ================================================ FILE: pkg/machinery/nethelpers/arpvalidate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // ARPValidate is an ARP Validation mode. type ARPValidate uint32 // ARPValidate constants. // //structprotogen:gen_enum const ( ARPValidateNone ARPValidate = iota // none ARPValidateActive // active ARPValidateBackup // backup ARPValidateAll // all ARPValidateFilter // filter ARPValidateFilterActive // filter-active ARPValidateFilterBackup // filter-backup ) // ARPValidateByName parses ARPValidate. func ARPValidateByName(a string) (ARPValidate, error) { switch a { case "", "none": return ARPValidateNone, nil case "active": return ARPValidateActive, nil case "backup": return ARPValidateBackup, nil case "all": return ARPValidateAll, nil case "filter": return ARPValidateFilter, nil case "filter-active": return ARPValidateFilterActive, nil case "filter-backup": return ARPValidateFilterBackup, nil default: return 0, fmt.Errorf("invalid arp_validate mode %v", a) } } ================================================ FILE: pkg/machinery/nethelpers/autohostnamekind.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // AutoHostnameKind is a kind of automatically generated hostname. type AutoHostnameKind byte // AutoHostnameKind constants. // // Note: AutoHostnameKindAddr is a legacy setting for backwards compatibility, and // it is no longer exposed in network multi-doc configuration. // //structprotogen:gen_enum const ( AutoHostnameKindOff AutoHostnameKind = iota // off AutoHostnameKindAddr // talos-addr AutoHostnameKindStable // stable ) ================================================ FILE: pkg/machinery/nethelpers/bondmode.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // BondMode is a bond mode. type BondMode uint8 // BondMode constants. // // See linux/if_bonding.h. // //structprotogen:gen_enum const ( BondModeRoundrobin BondMode = iota // balance-rr BondModeActiveBackup // active-backup BondModeXOR // balance-xor BondModeBroadcast // broadcast BondMode8023AD // 802.3ad BondModeTLB // balance-tlb BondModeALB // balance-alb ) // BondModeByName converts string bond mode into a constant. func BondModeByName(mode string) (bm BondMode, err error) { switch mode { case "", "balance-rr": return BondModeRoundrobin, nil case "active-backup": return BondModeActiveBackup, nil case "balance-xor": return BondModeXOR, nil case "broadcast": return BondModeBroadcast, nil case "802.3ad": return BondMode8023AD, nil case "balance-tlb": return BondModeTLB, nil case "balance-alb": return BondModeALB, nil default: return 0, fmt.Errorf("invalid bond type %s", mode) } } ================================================ FILE: pkg/machinery/nethelpers/bondxmithashpolicy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // BondXmitHashPolicy is a bond hash policy. type BondXmitHashPolicy uint8 // Bond hash policy constants. // //structprotogen:gen_enum const ( BondXmitPolicyLayer2 BondXmitHashPolicy = iota // layer2 BondXmitPolicyLayer34 // layer3+4 BondXmitPolicyLayer23 // layer2+3 BondXmitPolicyEncap23 // encap2+3 BondXmitPolicyEncap34 // encap3+4 ) // BondXmitHashPolicyByName parses bond hash policy. func BondXmitHashPolicyByName(policy string) (BondXmitHashPolicy, error) { switch policy { case "", "layer2": return BondXmitPolicyLayer2, nil case "layer3+4": return BondXmitPolicyLayer34, nil case "layer2+3": return BondXmitPolicyLayer23, nil case "encap2+3": return BondXmitPolicyEncap23, nil case "encap3+4": return BondXmitPolicyEncap34, nil default: return 0, fmt.Errorf("invalid xmit hash policy %v", policy) } } ================================================ FILE: pkg/machinery/nethelpers/client_identifier.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // ClientIdentifier is a DHCP client identifier. type ClientIdentifier int // ClientIdentifier constants. // //structprotogen:gen_enum const ( ClientIdentifierNone ClientIdentifier = iota // none ClientIdentifierMAC // mac ClientIdentifierDUID // duid ) ================================================ FILE: pkg/machinery/nethelpers/conntrack_state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // ConntrackState is a conntrack state. type ConntrackState uint32 // ConntrackState constants. // //structprotogen:gen_enum const ( ConntrackStateNew ConntrackState = 0x08 // new ConntrackStateRelated ConntrackState = 0x04 // related ConntrackStateEstablished ConntrackState = 0x02 // established ConntrackStateInvalid ConntrackState = 0x01 // invalid ) ================================================ FILE: pkg/machinery/nethelpers/default_action.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // DefaultAction is a default firewall action. type DefaultAction int // DefaultAction constants. const ( DefaultActionAccept DefaultAction = iota // accept DefaultActionBlock // block ) ================================================ FILE: pkg/machinery/nethelpers/device.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "bytes" "errors" "io" "io/fs" "os" "path/filepath" "strings" ) // DeviceInfo contains device hardware information that can be read from /sys/. type DeviceInfo struct { BusPath string PCIID string Driver string } // GetDeviceInfo get additional device information by reading /sys/ directory. // //nolint:gocyclo func GetDeviceInfo(deviceName string) (*DeviceInfo, error) { path := filepath.Join("/sys/class/net/", deviceName, "/device/") readFile := func(path string) (string, error) { f, err := os.Open(path) if err != nil { return "", err } res, err := io.ReadAll(f) if err != nil { return "", err } return string(bytes.TrimSpace(res)), nil } _, err := os.Stat(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { return &DeviceInfo{}, nil } return nil, err } ueventContents, err := readFile(filepath.Join(path, "uevent")) if err != nil { return nil, err } if ueventContents == "" { return &DeviceInfo{}, nil } device := &DeviceInfo{} for line := range strings.SplitSeq(ueventContents, "\n") { key, value, found := strings.Cut(line, "=") if !found { continue } switch key { case "DRIVER": device.Driver = value case "PCI_ID": device.PCIID = value case "PCI_SLOT_NAME": device.BusPath = value } } return device, nil } ================================================ FILE: pkg/machinery/nethelpers/duplex.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "github.com/mdlayher/ethtool" // Duplex wraps ethtool.Duplex for YAML marshaling. type Duplex ethtool.Duplex // Possible Duplex type values. // //structprotogen:gen_enum const ( Half Duplex = Duplex(ethtool.Half) // Half Full Duplex = Duplex(ethtool.Full) // Full Unknown Duplex = Duplex(ethtool.Unknown) // Unknown ) ================================================ FILE: pkg/machinery/nethelpers/failovermac.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "fmt" "strconv" ) // FailOverMAC is a MAC failover mode. type FailOverMAC uint8 // FailOverMAC constants. // //structprotogen:gen_enum const ( FailOverMACNone FailOverMAC = iota // none FailOverMACActive // active FailOverMACFollow // follow ) // FailOverMACByName parses FailOverMac. func FailOverMACByName(f string) (FailOverMAC, error) { switch f { case "", "none": return FailOverMACNone, nil case "active": return FailOverMACActive, nil case "follow": return FailOverMACFollow, nil default: return 0, fmt.Errorf("invalid fail_over_mac value %v", f) } } // MarshalText implements encoding.TextMarshaler. func (f FailOverMAC) MarshalText() ([]byte, error) { return []byte(f.String()), nil } // UnmarshalText implements encoding.TextUnmarshaler. // // There is some old historical reason we don't use enumer's -text method to automatically // generate the MarshalText/UnmarshalText methods - so we have to implement UnmarshalText manually // to support both name and numeric representation for backward compatibility. func (f *FailOverMAC) UnmarshalText(text []byte) error { parsed, err := FailOverMACByName(string(text)) if err != nil { // for legacy compatibility try to parse as a number out, parseErr := strconv.ParseInt(string(text), 10, 8) if parseErr == nil && out >= int64(FailOverMACNone) && out <= int64(FailOverMACFollow) { *f = FailOverMAC(out) return nil } return err } *f = parsed return nil } ================================================ FILE: pkg/machinery/nethelpers/failovermac_enumer.go ================================================ // Code generated by "enumer -type=FailOverMAC -linecomment"; DO NOT EDIT. package nethelpers import ( "fmt" "strings" ) const _FailOverMACName = "noneactivefollow" var _FailOverMACIndex = [...]uint8{0, 4, 10, 16} const _FailOverMACLowerName = "noneactivefollow" func (i FailOverMAC) String() string { if i >= FailOverMAC(len(_FailOverMACIndex)-1) { return fmt.Sprintf("FailOverMAC(%d)", i) } return _FailOverMACName[_FailOverMACIndex[i]:_FailOverMACIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _FailOverMACNoOp() { var x [1]struct{} _ = x[FailOverMACNone-(0)] _ = x[FailOverMACActive-(1)] _ = x[FailOverMACFollow-(2)] } var _FailOverMACValues = []FailOverMAC{FailOverMACNone, FailOverMACActive, FailOverMACFollow} var _FailOverMACNameToValueMap = map[string]FailOverMAC{ _FailOverMACName[0:4]: FailOverMACNone, _FailOverMACLowerName[0:4]: FailOverMACNone, _FailOverMACName[4:10]: FailOverMACActive, _FailOverMACLowerName[4:10]: FailOverMACActive, _FailOverMACName[10:16]: FailOverMACFollow, _FailOverMACLowerName[10:16]: FailOverMACFollow, } var _FailOverMACNames = []string{ _FailOverMACName[0:4], _FailOverMACName[4:10], _FailOverMACName[10:16], } // FailOverMACString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func FailOverMACString(s string) (FailOverMAC, error) { if val, ok := _FailOverMACNameToValueMap[s]; ok { return val, nil } if val, ok := _FailOverMACNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to FailOverMAC values", s) } // FailOverMACValues returns all values of the enum func FailOverMACValues() []FailOverMAC { return _FailOverMACValues } // FailOverMACStrings returns a slice of all String values of the enum func FailOverMACStrings() []string { strs := make([]string, len(_FailOverMACNames)) copy(strs, _FailOverMACNames) return strs } // IsAFailOverMAC returns "true" if the value is listed in the enum definition. "false" otherwise func (i FailOverMAC) IsAFailOverMAC() bool { for _, v := range _FailOverMACValues { if i == v { return true } } return false } ================================================ FILE: pkg/machinery/nethelpers/failovermac_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) func TestFailOverMACUnmarshalText(t *testing.T) { t.Parallel() for _, test := range []struct { input string expected nethelpers.FailOverMAC }{ { input: "none", expected: nethelpers.FailOverMACNone, }, { input: "active", expected: nethelpers.FailOverMACActive, }, { input: "follow", expected: nethelpers.FailOverMACFollow, }, { input: "", expected: nethelpers.FailOverMACNone, }, { input: "0", expected: nethelpers.FailOverMACNone, }, { input: "1", expected: nethelpers.FailOverMACActive, }, { input: "2", expected: nethelpers.FailOverMACFollow, }, } { t.Run(test.input, func(t *testing.T) { t.Parallel() var f nethelpers.FailOverMAC err := f.UnmarshalText([]byte(test.input)) require.NoError(t, err) assert.Equal(t, test.expected, f) }) } } ================================================ FILE: pkg/machinery/nethelpers/family.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // Family is a network family. type Family uint8 // Family constants. // //structprotogen:gen_enum const ( FamilyInet4 Family = 2 // inet4 FamilyInet6 Family = 10 // inet6 ) ================================================ FILE: pkg/machinery/nethelpers/host_port.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "fmt" "net" ) type ints interface { ~int16 | ~int32 | ~int64 | ~uint16 | ~uint32 | ~uint64 | int | uint } // JoinHostPort is a wrapper around net.JoinHostPort which accepts port any integer type. func JoinHostPort[T ints](host string, port T) string { return net.JoinHostPort(host, fmt.Sprintf("%d", port)) } ================================================ FILE: pkg/machinery/nethelpers/hwaddr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "bytes" "encoding/hex" "net" ) // HardwareAddr wraps net.HardwareAddr for YAML marshaling. type HardwareAddr net.HardwareAddr // MarshalText implements text.Marshaler interface. func (addr HardwareAddr) MarshalText() ([]byte, error) { return []byte(net.HardwareAddr(addr).String()), nil } // UnmarshalText implements text.Unmarshaler interface. func (addr *HardwareAddr) UnmarshalText(b []byte) error { rawHex := bytes.ReplaceAll(b, []byte(":"), []byte("")) dstLen := hex.DecodedLen(len(rawHex)) dst := make([]byte, dstLen) n, err := hex.Decode(dst, rawHex) if err != nil { return err } *addr = HardwareAddr(dst[:n]) return nil } func (addr HardwareAddr) String() string { return net.HardwareAddr(addr).String() } ================================================ FILE: pkg/machinery/nethelpers/icmp_type.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // ICMPType is a ICMP packet type. type ICMPType byte // ICMPType constants. // //structprotogen:gen_enum const ( ICMPTypeTimestampRequest ICMPType = 13 // timestamp-request ICMPTypeTimestampReply ICMPType = 14 // timestamp-reply ICMPTypeAddressMaskRequest ICMPType = 17 // address-mask-request ICMPTypeAddressMaskReply ICMPType = 18 // address-mask-reply ) ================================================ FILE: pkg/machinery/nethelpers/lacprate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // LACPRate is a LACP rate. type LACPRate uint8 // LACP rate constants. // //structprotogen:gen_enum const ( LACPRateSlow LACPRate = iota // slow LACPRateFast // fast ) // LACPRateByName parses LACPRate. func LACPRateByName(mode string) (LACPRate, error) { switch mode { case "", "slow": return LACPRateSlow, nil case "fast": return LACPRateFast, nil default: return 0, fmt.Errorf("invalid lacp rate %v", mode) } } ================================================ FILE: pkg/machinery/nethelpers/linkflag.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "strings" ) // LinkFlags is a bitmask of LinkFlags. type LinkFlags uint32 func (flags LinkFlags) String() string { var values []string for flag := LinkUp; flag <= LinkEcho; flag <<= 1 { if (LinkFlag(flags) & flag) == flag { values = append(values, flag.String()) } } return strings.Join(values, ",") } // LinkFlagsString parses string representation of LinkFlags. func LinkFlagsString(s string) (LinkFlags, error) { flags := LinkFlags(0) for p := range strings.SplitSeq(s, ",") { flag, err := LinkFlagString(p) if err != nil { return flags, err } flags |= LinkFlags(flag) } return flags, nil } // MarshalText implements text.Marshaler. func (flags LinkFlags) MarshalText() ([]byte, error) { return []byte(flags.String()), nil } // UnmarshalText implements text.Unmarshaler. func (flags *LinkFlags) UnmarshalText(b []byte) error { var err error *flags, err = LinkFlagsString(string(b)) return err } // LinkFlag wraps IFF_* constants. type LinkFlag uint32 // LinkFlag constants. const ( LinkUp LinkFlag = 1 << iota // UP LinkBroadcast // BROADCAST LinkDebug // DEBUG LinkLoopback // LOOPBACK LinkPointToPoint // POINTTOPOINT LinkNoTrailers // NOTRAILERS LinkRunning // RUNNING LinkNoArp // NOARP LinkPromisc // PROMISC LinkAllMulti // ALLMULTI LinkMaster // MASTER LinkSlave // SLAVE LinkMulticast // MULTICAST LinkPortsel // PORTSEL LinKAutoMedia // AUTOMEDIA LinkDynamic // DYNAMIC LinkLowerUp // LOWER_UP LinkDormant // DORMANT LinkEcho // ECHO ) ================================================ FILE: pkg/machinery/nethelpers/linktype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // LinkType is a link type. type LinkType uint16 // LinkType constants. // //structprotogen:gen_enum const ( LinkNetrom LinkType = 0 // netrom LinkEther LinkType = 1 // ether LinkEether LinkType = 2 // eether LinkAx25 LinkType = 3 // ax25 LinkPronet LinkType = 4 // pronet LinkChaos LinkType = 5 // chaos LinkIee802 LinkType = 6 // ieee802 LinkArcnet LinkType = 7 // arcnet LinkAtalk LinkType = 8 // atalk LinkDlci LinkType = 15 // dlci LinkAtm LinkType = 19 // atm LinkMetricom LinkType = 23 // metricom LinkIeee1394 LinkType = 24 // ieee1394 LinkEui64 LinkType = 27 // eui64 LinkInfiniband LinkType = 32 // infiniband LinkSlip LinkType = 256 // slip LinkCslip LinkType = 257 // cslip LinkSlip6 LinkType = 258 // slip6 LinkCslip6 LinkType = 259 // cslip6 LinkRsrvd LinkType = 260 // rsrvd LinkAdapt LinkType = 264 // adapt LinkRose LinkType = 270 // rose LinkX25 LinkType = 271 // x25 LinkHwx25 LinkType = 272 // hwx25 LinkCan LinkType = 280 // can LinkPpp LinkType = 512 // ppp LinkCisco LinkType = 513 // cisco LinkHdlc LinkType = 513 // hdlc LinkLapb LinkType = 516 // lapb LinkDdcmp LinkType = 517 // ddcmp LinkRawhdlc LinkType = 518 // rawhdlc LinkTunnel LinkType = 768 // ipip LinkTunnel6 LinkType = 769 // tunnel6 LinkFrad LinkType = 770 // frad LinkSkip LinkType = 771 // skip LinkLoopbck LinkType = 772 // loopback LinkLocaltlk LinkType = 773 // localtlk LinkFddi LinkType = 774 // fddi LinkBif LinkType = 775 // bif LinkSit LinkType = 776 // sit LinkIpddp LinkType = 777 // ip/ddp LinkIpgre LinkType = 778 // gre LinkPimreg LinkType = 779 // pimreg LinkHippi LinkType = 780 // hippi LinkAsh LinkType = 781 // ash LinkEconet LinkType = 782 // econet LinkIrda LinkType = 783 // irda LinkFcpp LinkType = 784 // fcpp LinkFcal LinkType = 785 // fcal LinkFcpl LinkType = 786 // fcpl LinkFcfabric LinkType = 787 // fcfb_0 LinkFcfabric1 LinkType = LinkFcfabric + 1 // fcfb_1 LinkFcfabric2 LinkType = LinkFcfabric + 2 // fcfb_2 LinkFcfabric3 LinkType = LinkFcfabric + 3 // fcfb_3 LinkFcfabric4 LinkType = LinkFcfabric + 4 // fcfb_4 LinkFcfabric5 LinkType = LinkFcfabric + 5 // fcfb_5 LinkFcfabric6 LinkType = LinkFcfabric + 6 // fcfb_6 LinkFcfabric7 LinkType = LinkFcfabric + 7 // fcfb_7 LinkFcfabric8 LinkType = LinkFcfabric + 8 // fcfb_8 LinkFcfabric9 LinkType = LinkFcfabric + 9 // fcfb_9 LinkFcfabric10 LinkType = LinkFcfabric + 10 // fcfb_10 LinkFcfabric11 LinkType = LinkFcfabric + 11 // fcfb_11 LinkFcfabric12 LinkType = LinkFcfabric + 12 // fcfb_12 LinkIee802tr LinkType = 800 // tr LinkIee80211 LinkType = 801 // ieee802.11 LinkIee80211prism LinkType = 802 // ieee802.11_prism LinkIee80211Radiotap LinkType = 803 // ieee802.11_radiotap LinkIee8021154 LinkType = 804 // ieee802.15.4 LinkIee8021154monitor LinkType = 805 // ieee802.15.4_monitor LinkPhonet LinkType = 820 // phonet LinkPhonetpipe LinkType = 821 // phonet_pipe LinkCaif LinkType = 822 // caif LinkIP6gre LinkType = 823 // ip6gre LinkNetlink LinkType = 824 // netlink Link6Lowpan LinkType = 825 // 6lowpan LinkVoid LinkType = 65535 // void LinkNone LinkType = 65534 // nohdr ) ================================================ FILE: pkg/machinery/nethelpers/match_operator.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // MatchOperator is a netfilter match operator. type MatchOperator int // MatchOperator constants. // //structprotogen:gen_enum const ( OperatorEqual MatchOperator = 0 // == OperatorNotEqual MatchOperator = 1 // != ) ================================================ FILE: pkg/machinery/nethelpers/nethelpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package nethelpers provides types and type wrappers to support network resources. package nethelpers //go:generate go tool github.com/dmarkham/enumer -type=ARPAllTargets,ARPValidate,AddressFlag,AddressSortAlgorithm,ADSelect,ADLACPActive,AutoHostnameKind,BondMode,BondXmitHashPolicy,ClientIdentifier,ConntrackState,DefaultAction,Duplex,Family,LACPRate,LinkFlag,LinkType,MatchOperator,NfTablesChainHook,NfTablesChainPriority,NfTablesVerdict,OperationalState,Port,PrimaryReselect,Protocol,RouteFlag,RouteProtocol,RouteType,RoutingRuleAction,RoutingTable,Scope,Status,VLANProtocol,WOLMode -linecomment -text //go:generate go tool github.com/dmarkham/enumer -type=FailOverMAC -linecomment ================================================ FILE: pkg/machinery/nethelpers/nftables_chain_hook.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // NfTablesChainHook wraps nftables.ChainHook for YAML marshaling. type NfTablesChainHook uint32 // Constants copied from nftables to provide Stringer interface. // //structprotogen:gen_enum const ( ChainHookPrerouting NfTablesChainHook = 0 // prerouting ChainHookInput NfTablesChainHook = 1 // input ChainHookForward NfTablesChainHook = 2 // forward ChainHookOutput NfTablesChainHook = 3 // output ChainHookPostrouting NfTablesChainHook = 4 // postrouting ) ================================================ FILE: pkg/machinery/nethelpers/nftables_chain_priority.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "math" // NfTablesChainPriority wraps nftables.ChainPriority for YAML marshaling. type NfTablesChainPriority int32 // Constants copied from nftables to provide Stringer interface. // //structprotogen:gen_enum const ( ChainPriorityFirst NfTablesChainPriority = math.MinInt32 // first ChainPriorityConntrackDefrag NfTablesChainPriority = -400 // conntrack-defrag ChainPriorityRaw NfTablesChainPriority = -300 // raw ChainPrioritySELinuxFirst NfTablesChainPriority = -225 // selinux-first ChainPriorityConntrack NfTablesChainPriority = -200 // conntrack ChainPriorityMangle NfTablesChainPriority = -150 // mangle ChainPriorityNATDest NfTablesChainPriority = -100 // nat-dest ChainPriorityFilter NfTablesChainPriority = 0 // filter ChainPrioritySecurity NfTablesChainPriority = 50 // security ChainPriorityNATSource NfTablesChainPriority = 100 // nat-source ChainPrioritySELinuxLast NfTablesChainPriority = 225 // selinux-last ChainPriorityConntrackHelper NfTablesChainPriority = 300 // conntrack-helper ChainPriorityLast NfTablesChainPriority = math.MaxInt32 // last ) ================================================ FILE: pkg/machinery/nethelpers/nftables_chain_type.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // NfTablesChainType defines what this chain will be used for. See also // https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types type NfTablesChainType = string // Possible ChainType values. const ( ChainTypeFilter NfTablesChainType = "filter" ChainTypeRoute NfTablesChainType = "route" ChainTypeNAT NfTablesChainType = "nat" ) ================================================ FILE: pkg/machinery/nethelpers/nftables_verdict.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // NfTablesVerdict wraps nftables.Verdict for YAML marshaling. type NfTablesVerdict int64 // Constants copied from nftables to provide Stringer interface. // //structprotogen:gen_enum const ( VerdictDrop NfTablesVerdict = 0 // drop VerdictAccept NfTablesVerdict = 1 // accept ) ================================================ FILE: pkg/machinery/nethelpers/operstate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "github.com/jsimonetti/rtnetlink/v2" ) // OperationalState wraps rtnetlink.OperationalState for YAML marshaling. type OperationalState uint8 // Constants copied from rtnetlink to provide Stringer interface. // //structprotogen:gen_enum const ( OperStateUnknown OperationalState = OperationalState(rtnetlink.OperStateUnknown) // unknown OperStateNotPresent OperationalState = OperationalState(rtnetlink.OperStateNotPresent) // notPresent OperStateDown OperationalState = OperationalState(rtnetlink.OperStateDown) // down OperStateLowerLayerDown OperationalState = OperationalState(rtnetlink.OperStateLowerLayerDown) // lowerLayerDown OperStateTesting OperationalState = OperationalState(rtnetlink.OperStateTesting) // testing OperStateDormant OperationalState = OperationalState(rtnetlink.OperStateDormant) // dormant OperStateUp OperationalState = OperationalState(rtnetlink.OperStateUp) // up ) ================================================ FILE: pkg/machinery/nethelpers/port.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "github.com/mdlayher/ethtool" // Port wraps ethtool.Port for YAML marshaling. type Port ethtool.Port // Possible Port type values. // //structprotogen:gen_enum const ( TwistedPair Port = Port(ethtool.TwistedPair) AUI Port = Port(ethtool.AUI) MII Port = Port(ethtool.MII) Fibre Port = Port(ethtool.Fibre) //nolint:misspell BNC Port = Port(ethtool.BNC) DirectAttach Port = Port(ethtool.DirectAttach) None Port = Port(ethtool.None) Other Port = Port(ethtool.Other) ) ================================================ FILE: pkg/machinery/nethelpers/primaryreselect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "fmt" // PrimaryReselect is an ARP targets mode. type PrimaryReselect uint8 // PrimaryReslect constants. // //structprotogen:gen_enum const ( PrimaryReselectAlways PrimaryReselect = iota // always PrimaryReselectBetter // better PrimaryReselectFailure // failure ) // PrimaryReselectByName parses PrimaryReselect. func PrimaryReselectByName(p string) (PrimaryReselect, error) { switch p { case "", "always": return PrimaryReselectAlways, nil case "better": return PrimaryReselectBetter, nil case "failure": return PrimaryReselectFailure, nil default: return 0, fmt.Errorf("invalid primary_reselect mode %v", p) } } ================================================ FILE: pkg/machinery/nethelpers/protocol.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // Protocol is a inet protocol. type Protocol uint8 // Protocol constants. // //structprotogen:gen_enum const ( ProtocolICMP Protocol = 0x1 // icmp ProtocolTCP Protocol = 0x6 // tcp ProtocolUDP Protocol = 0x11 // udp ProtocolICMPv6 Protocol = 0x3a // icmpv6 ) ================================================ FILE: pkg/machinery/nethelpers/routeflags.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "strings" ) // RouteFlags is a bitmask of RouteFlag. type RouteFlags uint32 func (flags RouteFlags) String() string { var values []string for flag := RouteNotify; flag <= RouteTrap; flag <<= 1 { if (RouteFlag(flags) & flag) == flag { values = append(values, flag.String()) } } return strings.Join(values, ",") } // RouteFlagsString parses string representation into RouteFlags. func RouteFlagsString(s string) (RouteFlags, error) { flags := RouteFlags(0) if s == "" { return flags, nil } for p := range strings.SplitSeq(s, ",") { flag, err := RouteFlagString(p) if err != nil { return flags, err } flags |= RouteFlags(flag) } return flags, nil } // Equal tests for RouteFlags equality ignoring flags not managed by this implementation. func (flags RouteFlags) Equal(other RouteFlags) bool { return (flags & RouteFlags(RouteFlagsMask)) == (other & RouteFlags(RouteFlagsMask)) } // MarshalText implements text.Marshaler. func (flags RouteFlags) MarshalText() ([]byte, error) { return []byte(flags.String()), nil } // UnmarshalText implements text.Unmarshaler. func (flags *RouteFlags) UnmarshalText(b []byte) error { var err error *flags, err = RouteFlagsString(string(b)) return err } // RouteFlag wraps RTM_F_* constants. type RouteFlag uint32 // RouteFlag constants. // //structprotogen:gen_enum const ( RouteNotify RouteFlag = 256 << iota // notify RouteCloned // cloned RouteEqualize // equalize RoutePrefix // prefix RouteLookupTable // lookup_table RouteFIBMatch // fib_match RouteOffload // offload RouteTrap // trap ) // RouteFlagsMask is a supported set of flags to manage. const RouteFlagsMask = RouteNotify | RouteCloned | RouteEqualize | RoutePrefix ================================================ FILE: pkg/machinery/nethelpers/routeflags_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) func TestRouteFlagsStrings(t *testing.T) { for _, tt := range []struct { routeFlagsString string expected nethelpers.RouteFlags }{ { routeFlagsString: "", expected: nethelpers.RouteFlags(0), }, { routeFlagsString: "cloned", expected: nethelpers.RouteFlags(nethelpers.RouteCloned), }, { routeFlagsString: "cloned,prefix", expected: nethelpers.RouteFlags(nethelpers.RouteCloned | nethelpers.RoutePrefix), }, } { routeFlags, err := nethelpers.RouteFlagsString(tt.routeFlagsString) assert.NoError(t, err) assert.Equal(t, tt.expected, routeFlags) } } ================================================ FILE: pkg/machinery/nethelpers/routeprotocol.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // RouteProtocol is a routing protocol. type RouteProtocol uint8 // RouteType constants. // //structprotogen:gen_enum const ( ProtocolUnspec RouteProtocol = 0 // unspec ProtocolRedirect RouteProtocol = 1 // redirect ProtocolKernel RouteProtocol = 2 // kernel ProtocolBoot RouteProtocol = 3 // boot ProtocolStatic RouteProtocol = 4 // static ProtocolRA RouteProtocol = 9 // ra ProtocolMRT RouteProtocol = 10 // mrt ProtocolZebra RouteProtocol = 11 // zebra ProtocolBird RouteProtocol = 12 // bird ProtocolDnrouted RouteProtocol = 13 // dnrouted ProtocolXorp RouteProtocol = 14 // xorp ProtocolNTK RouteProtocol = 15 // ntk ProtocolDHCP RouteProtocol = 16 // dhcp ProtocolMRTD RouteProtocol = 17 // mrtd ProtocolKeepalived RouteProtocol = 18 // keepalived ProtocolBabel RouteProtocol = 42 // babel ProtocolOpenr RouteProtocol = 99 // openr ProtocolBGP RouteProtocol = 186 // bgp ProtocolISIS RouteProtocol = 187 // isis ProtocolOSPF RouteProtocol = 188 // ospf ProtocolRIP RouteProtocol = 189 // rip ProtocolEIGRP RouteProtocol = 192 // eigrp ) ================================================ FILE: pkg/machinery/nethelpers/routetype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // RouteType is a route type. type RouteType uint8 // RouteType constants. // //structprotogen:gen_enum const ( TypeUnspec RouteType = iota // unspec TypeUnicast // unicast TypeLocal // local TypeBroadcast // broadcast TypeAnycast // anycast TypeMulticast // multicast TypeBlackhole // blackhole TypeUnreachable // unreachable TypeProhibit // prohibit TypeThrow // throw TypeNAT // nat TypeXResolve // xresolve ) ================================================ FILE: pkg/machinery/nethelpers/routingruleaction.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // RoutingRuleAction is a routing rule action. type RoutingRuleAction uint8 // RoutingRuleAction constants. // //structprotogen:gen_enum const ( RoutingRuleActionUnspec RoutingRuleAction = 0 // unspec RoutingRuleActionUnicast RoutingRuleAction = 1 // unicast RoutingRuleActionBlackhole RoutingRuleAction = 6 // blackhole RoutingRuleActionUnreachable RoutingRuleAction = 7 // unreachable RoutingRuleActionProhibit RoutingRuleAction = 8 // prohibit ) ================================================ FILE: pkg/machinery/nethelpers/routingtable.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // RoutingTable is a routing table ID. type RoutingTable uint32 // RoutingTable constants. // //structprotogen:gen_enum const ( TableUnspec RoutingTable = 0 // unspec Table1 RoutingTable = 1 // 1 Table2 RoutingTable = 2 // 2 Table3 RoutingTable = 3 // 3 Table4 RoutingTable = 4 // 4 Table5 RoutingTable = 5 // 5 Table6 RoutingTable = 6 // 6 Table7 RoutingTable = 7 // 7 Table8 RoutingTable = 8 // 8 Table9 RoutingTable = 9 // 9 Table10 RoutingTable = 10 // 10 Table11 RoutingTable = 11 // 11 Table12 RoutingTable = 12 // 12 Table13 RoutingTable = 13 // 13 Table14 RoutingTable = 14 // 14 Table15 RoutingTable = 15 // 15 Table16 RoutingTable = 16 // 16 Table17 RoutingTable = 17 // 17 Table18 RoutingTable = 18 // 18 Table19 RoutingTable = 19 // 19 Table20 RoutingTable = 20 // 20 Table21 RoutingTable = 21 // 21 Table22 RoutingTable = 22 // 22 Table23 RoutingTable = 23 // 23 Table24 RoutingTable = 24 // 24 Table25 RoutingTable = 25 // 25 Table26 RoutingTable = 26 // 26 Table27 RoutingTable = 27 // 27 Table28 RoutingTable = 28 // 28 Table29 RoutingTable = 29 // 29 Table30 RoutingTable = 30 // 30 Table31 RoutingTable = 31 // 31 Table32 RoutingTable = 32 // 32 Table33 RoutingTable = 33 // 33 Table34 RoutingTable = 34 // 34 Table35 RoutingTable = 35 // 35 Table36 RoutingTable = 36 // 36 Table37 RoutingTable = 37 // 37 Table38 RoutingTable = 38 // 38 Table39 RoutingTable = 39 // 39 Table40 RoutingTable = 40 // 40 Table41 RoutingTable = 41 // 41 Table42 RoutingTable = 42 // 42 Table43 RoutingTable = 43 // 43 Table44 RoutingTable = 44 // 44 Table45 RoutingTable = 45 // 45 Table46 RoutingTable = 46 // 46 Table47 RoutingTable = 47 // 47 Table48 RoutingTable = 48 // 48 Table49 RoutingTable = 49 // 49 Table50 RoutingTable = 50 // 50 Table51 RoutingTable = 51 // 51 Table52 RoutingTable = 52 // 52 Table53 RoutingTable = 53 // 53 Table54 RoutingTable = 54 // 54 Table55 RoutingTable = 55 // 55 Table56 RoutingTable = 56 // 56 Table57 RoutingTable = 57 // 57 Table58 RoutingTable = 58 // 58 Table59 RoutingTable = 59 // 59 Table60 RoutingTable = 60 // 60 Table61 RoutingTable = 61 // 61 Table62 RoutingTable = 62 // 62 Table63 RoutingTable = 63 // 63 Table64 RoutingTable = 64 // 64 Table65 RoutingTable = 65 // 65 Table66 RoutingTable = 66 // 66 Table67 RoutingTable = 67 // 67 Table68 RoutingTable = 68 // 68 Table69 RoutingTable = 69 // 69 Table70 RoutingTable = 70 // 70 Table71 RoutingTable = 71 // 71 Table72 RoutingTable = 72 // 72 Table73 RoutingTable = 73 // 73 Table74 RoutingTable = 74 // 74 Table75 RoutingTable = 75 // 75 Table76 RoutingTable = 76 // 76 Table77 RoutingTable = 77 // 77 Table78 RoutingTable = 78 // 78 Table79 RoutingTable = 79 // 79 Table80 RoutingTable = 80 // 80 Table81 RoutingTable = 81 // 81 Table82 RoutingTable = 82 // 82 Table83 RoutingTable = 83 // 83 Table84 RoutingTable = 84 // 84 Table85 RoutingTable = 85 // 85 Table86 RoutingTable = 86 // 86 Table87 RoutingTable = 87 // 87 Table88 RoutingTable = 88 // 88 Table89 RoutingTable = 89 // 89 Table90 RoutingTable = 90 // 90 Table91 RoutingTable = 91 // 91 Table92 RoutingTable = 92 // 92 Table93 RoutingTable = 93 // 93 Table94 RoutingTable = 94 // 94 Table95 RoutingTable = 95 // 95 Table96 RoutingTable = 96 // 96 Table97 RoutingTable = 97 // 97 Table98 RoutingTable = 98 // 98 Table99 RoutingTable = 99 // 99 Table100 RoutingTable = 100 // 100 Table101 RoutingTable = 101 // 101 Table102 RoutingTable = 102 // 102 Table103 RoutingTable = 103 // 103 Table104 RoutingTable = 104 // 104 Table105 RoutingTable = 105 // 105 Table106 RoutingTable = 106 // 106 Table107 RoutingTable = 107 // 107 Table108 RoutingTable = 108 // 108 Table109 RoutingTable = 109 // 109 Table110 RoutingTable = 110 // 110 Table111 RoutingTable = 111 // 111 Table112 RoutingTable = 112 // 112 Table113 RoutingTable = 113 // 113 Table114 RoutingTable = 114 // 114 Table115 RoutingTable = 115 // 115 Table116 RoutingTable = 116 // 116 Table117 RoutingTable = 117 // 117 Table118 RoutingTable = 118 // 118 Table119 RoutingTable = 119 // 119 Table120 RoutingTable = 120 // 120 Table121 RoutingTable = 121 // 121 Table122 RoutingTable = 122 // 122 Table123 RoutingTable = 123 // 123 Table124 RoutingTable = 124 // 124 Table125 RoutingTable = 125 // 125 Table126 RoutingTable = 126 // 126 Table127 RoutingTable = 127 // 127 Table128 RoutingTable = 128 // 128 Table129 RoutingTable = 129 // 129 Table130 RoutingTable = 130 // 130 Table131 RoutingTable = 131 // 131 Table132 RoutingTable = 132 // 132 Table133 RoutingTable = 133 // 133 Table134 RoutingTable = 134 // 134 Table135 RoutingTable = 135 // 135 Table136 RoutingTable = 136 // 136 Table137 RoutingTable = 137 // 137 Table138 RoutingTable = 138 // 138 Table139 RoutingTable = 139 // 139 Table140 RoutingTable = 140 // 140 Table141 RoutingTable = 141 // 141 Table142 RoutingTable = 142 // 142 Table143 RoutingTable = 143 // 143 Table144 RoutingTable = 144 // 144 Table145 RoutingTable = 145 // 145 Table146 RoutingTable = 146 // 146 Table147 RoutingTable = 147 // 147 Table148 RoutingTable = 148 // 148 Table149 RoutingTable = 149 // 149 Table150 RoutingTable = 150 // 150 Table151 RoutingTable = 151 // 151 Table152 RoutingTable = 152 // 152 Table153 RoutingTable = 153 // 153 Table154 RoutingTable = 154 // 154 Table155 RoutingTable = 155 // 155 Table156 RoutingTable = 156 // 156 Table157 RoutingTable = 157 // 157 Table158 RoutingTable = 158 // 158 Table159 RoutingTable = 159 // 159 Table160 RoutingTable = 160 // 160 Table161 RoutingTable = 161 // 161 Table162 RoutingTable = 162 // 162 Table163 RoutingTable = 163 // 163 Table164 RoutingTable = 164 // 164 Table165 RoutingTable = 165 // 165 Table166 RoutingTable = 166 // 166 Table167 RoutingTable = 167 // 167 Table168 RoutingTable = 168 // 168 Table169 RoutingTable = 169 // 169 Table170 RoutingTable = 170 // 170 Table171 RoutingTable = 171 // 171 Table172 RoutingTable = 172 // 172 Table173 RoutingTable = 173 // 173 Table174 RoutingTable = 174 // 174 Table175 RoutingTable = 175 // 175 Table176 RoutingTable = 176 // 176 Table177 RoutingTable = 177 // 177 Table178 RoutingTable = 178 // 178 Table179 RoutingTable = 179 // 179 Table180 RoutingTable = 180 // 180 Table181 RoutingTable = 181 // 181 Table182 RoutingTable = 182 // 182 Table183 RoutingTable = 183 // 183 Table184 RoutingTable = 184 // 184 Table185 RoutingTable = 185 // 185 Table186 RoutingTable = 186 // 186 Table187 RoutingTable = 187 // 187 Table188 RoutingTable = 188 // 188 Table189 RoutingTable = 189 // 189 Table190 RoutingTable = 190 // 190 Table191 RoutingTable = 191 // 191 Table192 RoutingTable = 192 // 192 Table193 RoutingTable = 193 // 193 Table194 RoutingTable = 194 // 194 Table195 RoutingTable = 195 // 195 Table196 RoutingTable = 196 // 196 Table197 RoutingTable = 197 // 197 Table198 RoutingTable = 198 // 198 Table199 RoutingTable = 199 // 199 Table200 RoutingTable = 200 // 200 Table201 RoutingTable = 201 // 201 Table202 RoutingTable = 202 // 202 Table203 RoutingTable = 203 // 203 Table204 RoutingTable = 204 // 204 Table205 RoutingTable = 205 // 205 Table206 RoutingTable = 206 // 206 Table207 RoutingTable = 207 // 207 Table208 RoutingTable = 208 // 208 Table209 RoutingTable = 209 // 209 Table210 RoutingTable = 210 // 210 Table211 RoutingTable = 211 // 211 Table212 RoutingTable = 212 // 212 Table213 RoutingTable = 213 // 213 Table214 RoutingTable = 214 // 214 Table215 RoutingTable = 215 // 215 Table216 RoutingTable = 216 // 216 Table217 RoutingTable = 217 // 217 Table218 RoutingTable = 218 // 218 Table219 RoutingTable = 219 // 219 Table220 RoutingTable = 220 // 220 Table221 RoutingTable = 221 // 221 Table222 RoutingTable = 222 // 222 Table223 RoutingTable = 223 // 223 Table224 RoutingTable = 224 // 224 Table225 RoutingTable = 225 // 225 Table226 RoutingTable = 226 // 226 Table227 RoutingTable = 227 // 227 Table228 RoutingTable = 228 // 228 Table229 RoutingTable = 229 // 229 Table230 RoutingTable = 230 // 230 Table231 RoutingTable = 231 // 231 Table232 RoutingTable = 232 // 232 Table233 RoutingTable = 233 // 233 Table234 RoutingTable = 234 // 234 Table235 RoutingTable = 235 // 235 Table236 RoutingTable = 236 // 236 Table237 RoutingTable = 237 // 237 Table238 RoutingTable = 238 // 238 Table239 RoutingTable = 239 // 239 Table240 RoutingTable = 240 // 240 Table241 RoutingTable = 241 // 241 Table242 RoutingTable = 242 // 242 Table243 RoutingTable = 243 // 243 Table244 RoutingTable = 244 // 244 Table245 RoutingTable = 245 // 245 Table246 RoutingTable = 246 // 246 Table247 RoutingTable = 247 // 247 Table248 RoutingTable = 248 // 248 Table249 RoutingTable = 249 // 249 Table250 RoutingTable = 250 // 250 Table251 RoutingTable = 251 // 251 Table252 RoutingTable = 252 // 252 TableDefault RoutingTable = 253 // default TableMain RoutingTable = 254 // main TableLocal RoutingTable = 255 // local ) ================================================ FILE: pkg/machinery/nethelpers/scope.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // Scope is an address scope. type Scope uint8 // Scope constants. // //structprotogen:gen_enum const ( ScopeGlobal Scope = 0 // global ScopeSite Scope = 200 // site ScopeLink Scope = 253 // link ScopeHost Scope = 254 // host ScopeNowhere Scope = 255 // nowhere ) ================================================ FILE: pkg/machinery/nethelpers/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // Status is a network status. // // Please see resources/network/status.go. type Status int // Status constants. const ( StatusAddresses Status = 1 // addresses StatusConnectivity Status = 2 // connectivity StatusHostname Status = 3 // hostname StatusEtcFiles Status = 4 // etcfiles ) ================================================ FILE: pkg/machinery/nethelpers/vlan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import ( "crypto/sha256" "fmt" ) const maxLinkNameLength = 15 // VLANLinkName builds a VLAN link name out of the base device name and VLAN ID. // // The function takes care of the maximum length of the link name. func VLANLinkName(base string, vlanID uint16) string { // VLAN ID is actually 12-bit, so the allowed values are 0-4095. // In ".%d" format, vlanID can be up to 5 characters long. if len(base)+5 <= maxLinkNameLength { return fmt.Sprintf("%s.%d", base, vlanID) } // If the base name is too long, we need to truncate it, but simply // truncating might lead to ambiguous link name, so take some hash of the original // name. prefix := base[:4] hash := sha256.Sum256([]byte(base)) return fmt.Sprintf("%s%x.%d", prefix, hash[:(maxLinkNameLength-len(prefix)-5)/2], vlanID) } ================================================ FILE: pkg/machinery/nethelpers/vlan_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers_test import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) func TestVLANLinkName(t *testing.T) { t.Parallel() for _, test := range []struct { base string vlanID uint16 expected string }{ { base: "eth0", vlanID: 1, expected: "eth0.1", }, { base: "en9s0", vlanID: 4095, expected: "en9s0.4095", }, { base: "0123456789", vlanID: 4095, expected: "0123456789.4095", }, { base: "enx12545f8c99cd", vlanID: 25, expected: "enx1ee6413.25", }, { base: "enx12545f8c99cd", vlanID: 4095, expected: "enx1ee6413.4095", }, { base: "enx12545f8c99ce", vlanID: 4095, expected: "enx1ef972f.4095", }, } { t.Run(fmt.Sprintf("%s.%d", test.base, test.vlanID), func(t *testing.T) { t.Parallel() assert.Equal(t, test.expected, nethelpers.VLANLinkName(test.base, test.vlanID)) }) } } ================================================ FILE: pkg/machinery/nethelpers/vlanprotocol.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers // VLANProtocol is a VLAN protocol. type VLANProtocol uint16 // VLANProtocol constants. // //structprotogen:gen_enum const ( VLANProtocol8021Q VLANProtocol = 33024 // 802.1q VLANProtocol8021AD VLANProtocol = 34984 // 802.1ad ) ================================================ FILE: pkg/machinery/nethelpers/wol.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package nethelpers import "github.com/mdlayher/ethtool" // WOLMode wraps ethtool.WOLMode for YAML marshaling. type WOLMode int // Constants copied from ethtool to provide Stringer interface. // //structprotogen:gen_enum const ( WOLModePhy WOLMode = WOLMode(ethtool.PHY) // phy WOLModeUnicast WOLMode = WOLMode(ethtool.Unicast) // unicast WOLModeMulticast WOLMode = WOLMode(ethtool.Multicast) // multicast WOLModeBroadcast WOLMode = WOLMode(ethtool.Broadcast) // broadcast WOLModeMagic WOLMode = WOLMode(ethtool.Magic) // magic WOLModeMagicSecure WOLMode = WOLMode(ethtool.MagicSecure) // magicsecure WOLModeFilter WOLMode = WOLMode(ethtool.Filter) // filter ) // WOLModeMin is the minimum valid WOLMode. const WOLModeMin = WOLModePhy // WOLModeMax is the maximum valid WOLMode. const WOLModeMax = WOLModeFilter ================================================ FILE: pkg/machinery/overlay/adapter/adapter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package adapter provides an adapter for the overlay installer. package adapter import ( "context" "fmt" "os" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/overlay" ) // Execute executes the overlay installer. func Execute[T any](ctx context.Context, installer overlay.Installer[T]) { if len(os.Args) < 2 { fmt.Fprint(os.Stderr, "missing command") os.Exit(1) } switch os.Args[1] { case "install": install(ctx, installer) case "get-options": getOptions(ctx, installer) default: fmt.Fprintf(os.Stderr, "unknown command: %s", os.Args[1]) os.Exit(1) } } func getOptions[T any](ctx context.Context, installer overlay.Installer[T]) { var opts T withErrorHandler(yaml.NewDecoder(os.Stdin).Decode(&opts)) opt, err := installer.GetOptions(ctx, opts) if err != nil { fmt.Fprint(os.Stderr, err.Error()) os.Exit(1) } withErrorHandler(yaml.NewEncoder(os.Stdout).Encode(opt)) } func install[T any](ctx context.Context, installer overlay.Installer[T]) { var opts overlay.InstallOptions[T] withErrorHandler(yaml.NewDecoder(os.Stdin).Decode(&opts)) withErrorHandler(installer.Install(ctx, opts)) } func withErrorHandler(err error) { if err != nil { fmt.Fprint(os.Stderr, err.Error()) os.Exit(1) } } ================================================ FILE: pkg/machinery/overlay/overlay.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package overlay provides an interface for overlay installers. package overlay import "context" // Installer is an interface for overlay installers. type Installer[T any] interface { GetOptions(ctx context.Context, extra T) (Options, error) Install(ctx context.Context, options InstallOptions[T]) error } // Options for the overlay installer. type Options struct { Name string `yaml:"name"` KernelArgs []string `yaml:"kernelArgs,omitempty"` PartitionOptions PartitionOptions `yaml:"partitionOptions,omitempty"` } // PartitionOptions for the overlay installer. type PartitionOptions struct { Offset uint64 `yaml:"offset,omitempty"` } // InstallOptions for the overlay installer. type InstallOptions[T any] struct { InstallDisk string `yaml:"installDisk"` MountPrefix string `yaml:"mountPrefix"` ArtifactsPath string `yaml:"artifactsPath"` ExtraOptions T `yaml:"extraOptions,omitempty"` } // ExtraOptions for the overlay installer. type ExtraOptions map[string]any ================================================ FILE: pkg/machinery/platforms/platforms.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package platforms provides meta information about supported Talos platforms. package platforms import ( "fmt" "github.com/blang/semver/v4" ) // Arch represents an architecture supported by Talos. type Arch = string // Architecture constants. const ( ArchAmd64 = "amd64" ArchArm64 = "arm64" ) // BootMethod represents a boot method supported by Talos. type BootMethod = string // BootMethod constants. const ( BootMethodDiskImage = "disk-image" BootMethodISO = "iso" BootMethodPXE = "pxe" ) // Platform represents a platform supported by Talos. type Platform struct { Name string Label string Description string MinVersion semver.Version Architectures []Arch Documentation string DiskImageSuffix string BootMethods []BootMethod SecureBootSupported bool } // NotOnlyDiskImage is true if the platform supports boot methods other than disk-image. func (p Platform) NotOnlyDiskImage() bool { if len(p.BootMethods) == 1 && p.BootMethods[0] == BootMethodDiskImage { return false } return true } // DiskImageDefaultPath returns the path to the disk image for the platform. func (p Platform) DiskImageDefaultPath(arch Arch) string { return p.DiskImagePath(arch, p.DiskImageSuffix) } // SecureBootDiskImageDefaultPath returns the path to the SecureBoot disk image for the platform. func (p Platform) SecureBootDiskImageDefaultPath(arch Arch) string { return p.SecureBootDiskImagePath(arch, p.DiskImageSuffix) } // DiskImagePath returns the path to the disk image for the platform and suffix. func (p Platform) DiskImagePath(arch Arch, suffix string) string { return fmt.Sprintf("%s-%s.%s", p.Name, arch, suffix) } // SecureBootDiskImagePath returns the path to the SecureBoot disk image for the platform and suffix. func (p Platform) SecureBootDiskImagePath(arch Arch, suffix string) string { return fmt.Sprintf("%s-%s-secureboot.%s", p.Name, arch, suffix) } // ISOPath returns the path to the ISO for the platform. func (p Platform) ISOPath(arch Arch) string { return fmt.Sprintf("%s-%s.iso", p.Name, arch) } // SecureBootISOPath returns the path to the SecureBoot ISO for the platform. func (p Platform) SecureBootISOPath(arch Arch) string { return fmt.Sprintf("%s-%s-secureboot.iso", p.Name, arch) } // PXEScriptPath returns the path to the PXE script for the platform. func (p Platform) PXEScriptPath(arch Arch) string { return fmt.Sprintf("%s-%s", p.Name, arch) } // SecureBootPXEScriptPath returns the path to the SecureBoot PXE script for the platform. func (p Platform) SecureBootPXEScriptPath(arch Arch) string { return fmt.Sprintf("%s-%s-secureboot", p.Name, arch) } // UKIPath returns the path to the UKI for the platform. func (p Platform) UKIPath(arch Arch) string { return fmt.Sprintf("%s-%s-uki.efi", p.Name, arch) } // SecureBootUKIPath returns the path to the SecureBoot UKI for the platform. func (p Platform) SecureBootUKIPath(arch Arch) string { return fmt.Sprintf("%s-%s-secureboot-uki.efi", p.Name, arch) } // KernelPath returns the path to the kernel for the platform. // // NB: Kernel path doesn't depend on the platform. func (p Platform) KernelPath(arch Arch) string { return fmt.Sprintf("kernel-%s", arch) } // InitramfsPath returns the path to the initramfs for the platform. // // NB: Initramfs path doesn't depend on the platform. func (p Platform) InitramfsPath(arch Arch) string { return fmt.Sprintf("initramfs-%s.xz", arch) } // CmdlinePath returns the path to the cmdline for the platform. func (p Platform) CmdlinePath(arch Arch) string { return fmt.Sprintf("cmdline-%s-%s", p.Name, arch) } // MetalPlatform returns a metal platform. func MetalPlatform() Platform { return Platform{ Name: "metal", Label: "Bare Metal", Description: "Runs on bare-metal servers", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/bare-metal-platforms/bootloader", DiskImageSuffix: "raw.zst", BootMethods: []BootMethod{ BootMethodISO, BootMethodDiskImage, BootMethodPXE, }, SecureBootSupported: true, } } // CloudPlatforms returns a list of supported cloud platforms. func CloudPlatforms() []Platform { // metal platform is not listed here, as it is handled separately. return []Platform{ // Tier 1 { Name: "aws", Label: "Amazon Web Services (AWS)", Description: "Runs on AWS VMs booted from an AMI", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/aws", DiskImageSuffix: "raw.xz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "gcp", Label: "Google Cloud (GCP)", Description: "Runs on Google Cloud VMs booted from a disk image", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/gcp", DiskImageSuffix: "raw.tar.gz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "equinixMetal", Label: "Equinix Metal", Description: "Runs on Equinix Metal bare-metal servers", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/bare-metal-platforms/equinix-metal", BootMethods: []BootMethod{ BootMethodPXE, }, }, // Tier 2 { Name: "azure", Label: "Microsoft Azure", Description: "Runs on Microsoft Azure Linux Virtual Machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/azure", DiskImageSuffix: "vhd.xz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "digital-ocean", Label: "Digital Ocean", Description: "Runs on Digital Ocean droplets", Architectures: []Arch{ArchAmd64}, Documentation: "/platform-specific-installations/cloud-platforms/digitalocean", DiskImageSuffix: "raw.gz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "nocloud", Label: "Nocloud", Description: "Runs on various hypervisors supporting 'nocloud' metadata (Proxmox, Oxide Computer, etc.)", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/nocloud", DiskImageSuffix: "raw.xz", BootMethods: []BootMethod{ BootMethodDiskImage, BootMethodISO, BootMethodPXE, }, SecureBootSupported: true, }, { Name: "openstack", Label: "OpenStack", Description: "Runs on OpenStack virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/openstack", DiskImageSuffix: "raw.xz", BootMethods: []BootMethod{ BootMethodDiskImage, BootMethodISO, BootMethodPXE, }, }, { Name: "vmware", Label: "VMWare", Description: "Runs on VMWare ESXi virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/virtualized-platforms/vmware", DiskImageSuffix: "ova", BootMethods: []BootMethod{ BootMethodDiskImage, BootMethodISO, }, }, // Tier 3 { Name: "akamai", Label: "Akamai", Description: "Runs on Akamai Cloud (Linode) virtual machines", Architectures: []Arch{ArchAmd64}, MinVersion: semver.MustParse("1.7.0"), Documentation: "/platform-specific-installations/cloud-platforms/akamai", DiskImageSuffix: "raw.gz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "cloudstack", Label: "Apache CloudStack", Description: "Runs on Apache CloudStack virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/cloudstack", DiskImageSuffix: "raw.gz", BootMethods: []BootMethod{ BootMethodDiskImage, }, SecureBootSupported: true, MinVersion: semver.MustParse("1.8.0-alpha.2"), }, { Name: "hcloud", Label: "Hetzner", Description: "Runs on Hetzner virtual machines", Architectures: []Arch{ArchAmd64}, Documentation: "/platform-specific-installations/cloud-platforms/hetzner", DiskImageSuffix: "raw.xz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "oracle", Label: "Oracle Cloud", Description: "Runs on Oracle Cloud virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/oracle", DiskImageSuffix: "qcow2", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "upcloud", Label: "UpCloud", Description: "Runs on UpCloud virtual machines", Architectures: []Arch{ArchAmd64}, Documentation: "/platform-specific-installations/cloud-platforms/upcloud", DiskImageSuffix: "raw.xz", BootMethods: []BootMethod{ BootMethodDiskImage, }, }, { Name: "vultr", Label: "Vultr", Description: "Runs on Vultr Cloud Compute virtual machines", Architectures: []Arch{ArchAmd64}, Documentation: "/platform-specific-installations/cloud-platforms/vultr", BootMethods: []BootMethod{ BootMethodISO, BootMethodPXE, }, }, // Tier 4 (no documentation). { Name: "exoscale", Label: "Exoscale", Description: "Runs on Exoscale virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, Documentation: "/platform-specific-installations/cloud-platforms/exoscale", DiskImageSuffix: "qcow2", BootMethods: []BootMethod{ BootMethodDiskImage, }, MinVersion: semver.MustParse("1.3.0"), }, { Name: "opennebula", Label: "OpenNebula", Description: "Runs on OpenNebula virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, DiskImageSuffix: "raw.zst", Documentation: "/platform-specific-installations/virtualized-platforms/opennebula", BootMethods: []BootMethod{ BootMethodDiskImage, BootMethodISO, }, MinVersion: semver.MustParse("1.7.0"), }, { Name: "scaleway", Label: "Scaleway", Description: "Runs on Scaleway virtual machines", Architectures: []Arch{ArchAmd64, ArchArm64}, DiskImageSuffix: "raw.zst", Documentation: "/platform-specific-installations/cloud-platforms/scaleway", BootMethods: []BootMethod{ BootMethodDiskImage, BootMethodISO, }, }, } } ================================================ FILE: pkg/machinery/platforms/platforms_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package platforms_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/platforms" ) func TestPlatform(t *testing.T) { t.Parallel() for _, test := range []struct { name string platform platforms.Platform expectedDefaultDiskImagePath string expectedISOPath string expectedSecureBootISOPath string expectedPXEPath string expectedSecureBootPXEPath string expectedUKIPath string expectedSecureBootUKIPath string expectedKernelPath string expectedInitramfsPath string expectedCmdlinePath string expectedNotOnlyDiskImage bool }{ { name: "metal", platform: platforms.MetalPlatform(), expectedDefaultDiskImagePath: "metal-amd64.raw.zst", expectedISOPath: "metal-amd64.iso", expectedSecureBootISOPath: "metal-amd64-secureboot.iso", expectedPXEPath: "metal-amd64", expectedSecureBootPXEPath: "metal-amd64-secureboot", expectedUKIPath: "metal-amd64-uki.efi", expectedSecureBootUKIPath: "metal-amd64-secureboot-uki.efi", expectedKernelPath: "kernel-amd64", expectedInitramfsPath: "initramfs-amd64.xz", expectedCmdlinePath: "cmdline-metal-amd64", expectedNotOnlyDiskImage: true, }, { name: "aws", platform: platforms.CloudPlatforms()[0], expectedDefaultDiskImagePath: "aws-amd64.raw.xz", expectedISOPath: "aws-amd64.iso", expectedSecureBootISOPath: "aws-amd64-secureboot.iso", expectedPXEPath: "aws-amd64", expectedSecureBootPXEPath: "aws-amd64-secureboot", expectedUKIPath: "aws-amd64-uki.efi", expectedSecureBootUKIPath: "aws-amd64-secureboot-uki.efi", expectedKernelPath: "kernel-amd64", expectedInitramfsPath: "initramfs-amd64.xz", expectedCmdlinePath: "cmdline-aws-amd64", expectedNotOnlyDiskImage: false, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() assert.Equal(t, test.expectedDefaultDiskImagePath, test.platform.DiskImageDefaultPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedISOPath, test.platform.ISOPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedSecureBootISOPath, test.platform.SecureBootISOPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedPXEPath, test.platform.PXEScriptPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedSecureBootPXEPath, test.platform.SecureBootPXEScriptPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedUKIPath, test.platform.UKIPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedSecureBootUKIPath, test.platform.SecureBootUKIPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedKernelPath, test.platform.KernelPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedInitramfsPath, test.platform.InitramfsPath(platforms.ArchAmd64)) assert.Equal(t, test.expectedCmdlinePath, test.platform.CmdlinePath(platforms.ArchAmd64)) assert.Equal(t, test.expectedNotOnlyDiskImage, test.platform.NotOnlyDiskImage()) }) } } ================================================ FILE: pkg/machinery/platforms/sbcs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package platforms import ( "github.com/blang/semver/v4" ) // SBC describes a Single Board Computer configuration. type SBC struct { Name string // For Talos 1.7+ OverlayName string OverlayImage string Label string Documentation string MinVersion semver.Version } // DiskImagePath returns the path to the disk image for the SBC. func (s SBC) DiskImagePath(talosVersion string) string { return "metal-arm64.raw.xz" } // SBCs returns a list of supported Single Board Computers. func SBCs() []SBC { return []SBC{ { Name: "rpi_5", OverlayName: "rpi_5", OverlayImage: "siderolabs/sbc-raspberrypi", Label: "Raspberry Pi 5", MinVersion: semver.MustParse("1.12.3"), }, { Name: "rpi_generic", OverlayName: "rpi_generic", OverlayImage: "siderolabs/sbc-raspberrypi", Label: "Raspberry Pi Series", Documentation: "/platform-specific-installations/single-board-computers/rpi_generic", }, { Name: "revpi_generic", OverlayName: "revpi_generic", OverlayImage: "siderolabs/sbc-raspberrypi", Label: "Revolution Pi Series", MinVersion: semver.MustParse("1.10.0-beta.0"), }, { Name: "bananapi_m64", OverlayName: "bananapi_m64", OverlayImage: "siderolabs/sbc-allwinner", Label: "Banana Pi M64", Documentation: "/platform-specific-installations/single-board-computers/bananapi_m64", }, { Name: "nanopi_r4s", OverlayName: "nanopi-r4s", OverlayImage: "siderolabs/sbc-rockchip", Label: "Friendlyelec Nano PI R4S", Documentation: "/platform-specific-installations/single-board-computers/nanopi_r4s", MinVersion: semver.MustParse("1.3.0"), }, { Name: "nanopi_r5s", OverlayName: "nanopi-r5s", OverlayImage: "siderolabs/sbc-rockchip", Label: "Friendlyelec Nano PI R5S", MinVersion: semver.MustParse("1.8.0-alpha.2"), }, { Name: "jetson_nano", OverlayName: "jetson_nano", OverlayImage: "siderolabs/sbc-jetson", Label: "Jetson Nano", Documentation: "/platform-specific-installations/single-board-computers/jetson_nano", }, { Name: "libretech_all_h3_cc_h5", OverlayName: "libretech_all_h3_cc_h5", OverlayImage: "siderolabs/sbc-allwinner", Label: "Libre Computer Board ALL-H3-CC", Documentation: "/platform-specific-installations/single-board-computers/libretech_all_h3_cc_h5", }, { Name: "orangepi_r1_plus_lts", OverlayName: "orangepi-r1-plus-lts", OverlayImage: "siderolabs/sbc-rockchip", Label: "Orange Pi R1 Plus LTS", Documentation: "/platform-specific-installations/single-board-computers/orangepi_r1_plus_lts", MinVersion: semver.MustParse("1.7.0"), }, { Name: "pine64", OverlayName: "pine64", OverlayImage: "siderolabs/sbc-allwinner", Label: "Pine64", Documentation: "/platform-specific-installations/single-board-computers/pine64", }, { Name: "rock64", OverlayName: "rock64", OverlayImage: "siderolabs/sbc-rockchip", Label: "Pine64 Rock64", Documentation: "/platform-specific-installations/single-board-computers/rock64", }, { Name: "rock4cplus", OverlayName: "rock4cplus", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK 4C Plus", Documentation: "/platform-specific-installations/single-board-computers/rock4cplus", MinVersion: semver.MustParse("1.7.0"), }, { Name: "rock4se", OverlayName: "rock4se", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK 4SE", MinVersion: semver.MustParse("1.8.0-alpha.1"), }, { Name: "rock5a", OverlayName: "rock5a", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK 5A", Documentation: "/platform-specific-installations/single-board-computers/rock5b", MinVersion: semver.MustParse("1.10.0-beta.0"), }, { Name: "rock5b", OverlayName: "rock5b", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK 5B", Documentation: "/platform-specific-installations/single-board-computers/rock5b", MinVersion: semver.MustParse("1.9.2"), }, { Name: "rockpi_4", OverlayName: "rockpi4", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK PI 4", Documentation: "/platform-specific-installations/single-board-computers/rockpi_4", }, { Name: "rockpi_4c", OverlayName: "rockpi4c", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa ROCK PI 4C", Documentation: "/platform-specific-installations/single-board-computers/rockpi_4c", }, { Name: "helios64", OverlayName: "helios64", OverlayImage: "siderolabs/sbc-rockchip", Label: "Kobol Helios64", MinVersion: semver.MustParse("1.8.0-alpha.2"), }, { Name: "turingrk1", OverlayName: "turingrk1", OverlayImage: "siderolabs/sbc-rockchip", Label: "Turing RK1", Documentation: "/platform-specific-installations/single-board-computers/turing_rk1", MinVersion: semver.MustParse("1.9.0-beta.0"), }, { Name: "orangepi-5", OverlayName: "orangepi-5", OverlayImage: "siderolabs/sbc-rockchip", Label: "Orange Pi 5", Documentation: "/platform-specific-installations/single-board-computers/orangepi_5", MinVersion: semver.MustParse("1.9.2"), }, { Name: "orangepi-5-plus", OverlayName: "orangepi-5-plus", OverlayImage: "siderolabs/sbc-rockchip", Label: "Orange Pi 5 Plus", MinVersion: semver.MustParse("1.10.0-beta.0"), }, { Name: "rockpro64", OverlayName: "rockpro64", OverlayImage: "siderolabs/sbc-rockchip", Label: "Pine64 RockPro64", MinVersion: semver.MustParse("1.10.0-beta.0"), }, { Name: "odroid-m1", OverlayName: "odroid-m1", OverlayImage: "siderolabs/sbc-rockchip", Label: "Odroid M1", MinVersion: semver.MustParse("1.12.0-beta.0"), }, { Name: "radxa-zero-3e", OverlayName: "radxa-zero-3e", OverlayImage: "siderolabs/sbc-rockchip", Label: "Radxa Zero 3E", MinVersion: semver.MustParse("1.12.0-beta.0"), }, { Name: "rock3b", OverlayName: "rock3b", OverlayImage: "siderolabs/sbc-rockchip", Label: "Rock 3B", MinVersion: semver.MustParse("1.12.0-beta.0"), }, { Name: "orangepi-5-max", OverlayName: "orangepi-5-max", OverlayImage: "siderolabs/sbc-rockchip", Label: "Orange Pi 5 Max", MinVersion: semver.MustParse("1.12.0-beta.0"), }, { Name: "rock5t", OverlayName: "rock5t", OverlayImage: "siderolabs/sbc-rockchip", Label: "Rock 5T", MinVersion: semver.MustParse("1.12.0-beta.0"), }, { Name: "friendlyelec-cm3588-nas", OverlayName: "friendlyelec-cm3588-nas", OverlayImage: "siderolabs/sbc-rockchip", Label: "FriendlyELEC CM3588 NAS", MinVersion: semver.MustParse("1.13.0-alpha.1"), }, { Name: "rock5b-plus", OverlayName: "rock5b-plus", OverlayImage: "siderolabs/sbc-rockchip", Label: "Rock 5B Plus", MinVersion: semver.MustParse("1.13.0-alpha.1"), }, } } ================================================ FILE: pkg/machinery/platforms/sbcs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package platforms_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/platforms" ) func TestSBC(t *testing.T) { t.Parallel() for _, test := range []struct { name string sbc platforms.SBC talosVersion string expectedDiskImagePath string }{ { name: "rpi_generic", sbc: platforms.SBCs()[0], talosVersion: "1.9.0", expectedDiskImagePath: "metal-arm64.raw.xz", }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() assert.Equal(t, test.expectedDiskImagePath, test.sbc.DiskImagePath(test.talosVersion)) }) } } ================================================ FILE: pkg/machinery/proto/proto.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package proto defines a functions to work with proto messages. package proto import ( "net/netip" "net/url" "sync" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/protoenc" _ "google.golang.org/grpc/encoding/gzip" //nolint:depguard // enable compression server-side "google.golang.org/protobuf/proto" //nolint:depguard "github.com/siderolabs/talos/pkg/machinery/api/common" ) // Message is the main interface for protobuf API v2 messages. type Message = proto.Message // UnmarshalOptions is alias for [proto.UnmarshalOptions]. type UnmarshalOptions = proto.UnmarshalOptions type vtprotoEqual interface { EqualMessageVT(proto.Message) bool } // Equal reports whether two messages are equal. func Equal(a, b Message) bool { if vm, ok := a.(vtprotoEqual); ok { return vm.EqualMessageVT(b) } return proto.Equal(a, b) } // vtprotoMessage is the interface for vtproto additions. // // We use only a subset of that interface but include additional methods // to prevent accidental successful type assertion for unrelated types. type vtprotoMessage interface { MarshalVT() ([]byte, error) MarshalToVT([]byte) (int, error) MarshalToSizedBufferVT([]byte) (int, error) UnmarshalVT([]byte) error } // Marshal returns the wire-format encoding of m. func Marshal(m Message) ([]byte, error) { if vm, ok := m.(vtprotoMessage); ok { return vm.MarshalVT() } return proto.Marshal(m) } // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). func Unmarshal(b []byte, m Message) error { if vm, ok := m.(vtprotoMessage); ok { return vm.UnmarshalVT(b) } return proto.Unmarshal(b, m) } var once sync.Once // RegisterDefaultTypes registers the pair of encoders/decoders for common types. func RegisterDefaultTypes() { once.Do(registerDefaultTypes) } // Mount specifies a mount for a container. // //gotagsrewrite:gen type Mount struct { Destination string `protobuf:"1"` Type string `protobuf:"2"` Source string `protobuf:"3"` Options []string `protobuf:"4"` UIDMappings []LinuxIDMapping `protobuf:"5"` GIDMappings []LinuxIDMapping `protobuf:"6"` } // LinuxIDMapping specifies UID/GID mappings. // //gotagsrewrite:gen type LinuxIDMapping struct { ContainerID uint32 `protobuf:"1"` HostID uint32 `protobuf:"2"` Size uint32 `protobuf:"3"` } //nolint:gocyclo func registerDefaultTypes() { protoenc.RegisterEncoderDecoder( func(v *url.URL) ([]byte, error) { source := common.URL{ FullPath: v.String(), } return proto.Marshal(&source) }, func(slc []byte) (*url.URL, error) { var dest common.URL if err := proto.Unmarshal(slc, &dest); err != nil { return nil, err } return url.Parse(dest.FullPath) }, ) protoenc.RegisterEncoderDecoder( func(v *x509.PEMEncodedCertificateAndKey) ([]byte, error) { source := common.PEMEncodedCertificateAndKey{ Crt: v.Crt, Key: v.Key, } return proto.Marshal(&source) }, func(slc []byte) (*x509.PEMEncodedCertificateAndKey, error) { var dest common.PEMEncodedCertificateAndKey if err := proto.Unmarshal(slc, &dest); err != nil { return nil, err } return &x509.PEMEncodedCertificateAndKey{ Crt: dest.Crt, Key: dest.Key, }, nil }, ) protoenc.RegisterEncoderDecoder( func(v *x509.PEMEncodedKey) ([]byte, error) { source := common.PEMEncodedKey{ Key: v.Key, } return proto.Marshal(&source) }, func(slc []byte) (*x509.PEMEncodedKey, error) { var dest common.PEMEncodedKey if err := proto.Unmarshal(slc, &dest); err != nil { return nil, err } return &x509.PEMEncodedKey{ Key: dest.Key, }, nil }, ) protoenc.RegisterEncoderDecoder( func(v *x509.PEMEncodedCertificate) ([]byte, error) { source := common.PEMEncodedCertificate{ Crt: v.Crt, } return proto.Marshal(&source) }, func(slc []byte) (*x509.PEMEncodedCertificate, error) { var dest common.PEMEncodedCertificate if err := proto.Unmarshal(slc, &dest); err != nil { return nil, err } return &x509.PEMEncodedCertificate{ Crt: dest.Crt, }, nil }, ) protoenc.RegisterEncoderDecoder( func(v netip.Addr) ([]byte, error) { ipEncoded, err := v.MarshalBinary() if err != nil { return nil, err } source := common.NetIP{ Ip: ipEncoded, } return proto.Marshal(&source) }, func(slc []byte) (netip.Addr, error) { if len(slc) == 0 || len(slc) == 4 || len(slc) == 16 { var parsedIP netip.Addr if err := parsedIP.UnmarshalBinary(slc); err != nil { return netip.Addr{}, err } return parsedIP, nil } var dest common.NetIP if err := proto.Unmarshal(slc, &dest); err != nil { return netip.Addr{}, err } var parsedIP netip.Addr if err := parsedIP.UnmarshalBinary(dest.Ip); err != nil { return netip.Addr{}, err } return parsedIP, nil }, ) protoenc.RegisterEncoderDecoder( func(v netip.AddrPort) ([]byte, error) { ipEncoded, err := v.Addr().MarshalBinary() if err != nil { return nil, err } source := common.NetIPPort{ Ip: ipEncoded, Port: int32(v.Port()), } return proto.Marshal(&source) }, func(slc []byte) (netip.AddrPort, error) { var dest common.NetIPPort if err := proto.Unmarshal(slc, &dest); err != nil { return netip.AddrPort{}, err } var parsedIP netip.Addr if err := parsedIP.UnmarshalBinary(dest.Ip); err != nil { return netip.AddrPort{}, err } return netip.AddrPortFrom(parsedIP, uint16(dest.Port)), nil }, ) protoenc.RegisterEncoderDecoder( func(v netip.Prefix) ([]byte, error) { ipEncoded, err := v.Addr().WithZone("").MarshalBinary() if err != nil { return nil, err } source := common.NetIPPrefix{ Ip: ipEncoded, PrefixLength: int32(v.Bits()), } return proto.Marshal(&source) }, func(slc []byte) (netip.Prefix, error) { var dest common.NetIPPrefix if err := proto.Unmarshal(slc, &dest); err != nil { return netip.Prefix{}, err } var parsedIP netip.Addr if err := parsedIP.UnmarshalBinary(dest.Ip); err != nil { return netip.Prefix{}, err } return netip.PrefixFrom(parsedIP, int(dest.PrefixLength)), nil }, ) protoenc.RegisterEncoderDecoder( func(v specs.Mount) ([]byte, error) { // use the intermediate type which is assignable to specs.Mount so that // we can be sure that `specs.Mount` and `Mount` have exactly same fields. // // as in Go []T1 is not assignable to []T2, even if T1 and T2 are assignable, we cannot // use direct conversion of Mount and specs.Mount type mountConverter struct { Destination string Type string Source string Options []string UIDMappings []specs.LinuxIDMapping GIDMappings []specs.LinuxIDMapping } mount := func(m mountConverter) Mount { return Mount{ Destination: m.Destination, Type: m.Type, Source: m.Source, Options: m.Options, UIDMappings: xslices.Map(m.UIDMappings, func(m specs.LinuxIDMapping) LinuxIDMapping { return LinuxIDMapping(m) }), GIDMappings: xslices.Map(m.GIDMappings, func(m specs.LinuxIDMapping) LinuxIDMapping { return LinuxIDMapping(m) }), } }(mountConverter(v)) return protoenc.Marshal(&mount) }, func(slc []byte) (specs.Mount, error) { var result Mount err := protoenc.Unmarshal(slc, &result) if err != nil { return specs.Mount{}, err } return specs.Mount{ Destination: result.Destination, Type: result.Type, Source: result.Source, Options: result.Options, UIDMappings: xslices.Map(result.UIDMappings, func(v LinuxIDMapping) specs.LinuxIDMapping { return specs.LinuxIDMapping(v) }), GIDMappings: xslices.Map(result.GIDMappings, func(v LinuxIDMapping) specs.LinuxIDMapping { return specs.LinuxIDMapping(v) }), }, nil }, ) } ================================================ FILE: pkg/machinery/proto/proto_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package proto_test import ( "encoding/hex" "fmt" "net/netip" "testing" "github.com/siderolabs/protoenc" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/api/common" clusterpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cluster" "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums" networkpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/network" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) //nolint:lll func TestMemberSpecOldEncoding(t *testing.T) { t.Parallel() // Input: // 00000000 0a 2c 37 78 31 53 75 43 38 45 67 65 35 42 47 58 |.,7x1SuC8Ege5BGX| // 00000010 64 41 66 54 45 66 66 35 69 51 6e 6c 57 5a 4c 66 |dAfTEff5iQnlWZLf| // 00000020 76 39 68 31 4c 47 4d 78 41 32 70 59 6b 43 12 04 |v9h1LGMxA2pYkC..| // 00000030 ac 14 00 02 12 10 fd 50 8d 60 42 38 63 02 f8 57 |.......P.`B8c..W| // 00000040 23 ff fe 21 d1 e0 1a 1c 74 61 6c 6f 73 2d 64 65 |#..!....talos-de| // 00000050 66 61 75 6c 74 2d 63 6f 6e 74 72 6f 6c 70 6c 61 |fault-controlpla| // 00000060 6e 65 2d 31 20 02 2a 0e 54 61 6c 6f 73 20 28 76 |ne-1 .*.Talos (v| // 00000070 31 2e 30 2e 30 29 |1.0.0)| const encodedString = "0a2c3778315375433845676535424758644166544566663569516e6c575a4c66763968314c474d78413270596b431204ac1400021210fd508d6042386302f85723fffe21d1e01a1c74616c6f732d64656661756c742d636f6e74726f6c706c616e652d3120022a0e54616c6f73202876312e302e3029" type T = cluster.MemberSpec encoded := must(hex.DecodeString(encodedString))(t) addresses := []netip.Addr{netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0")} expected := T{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: addresses, Hostname: "talos-default-controlplane-1", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", } var decoded T require.NoError(t, protoenc.Unmarshal(encoded, &decoded)) require.Equal(t, expected, decoded) } func TestVIPOperatorSpecOldEncoding(t *testing.T) { t.Parallel() // Input: // 00000000 0a 04 c0 a8 01 01 10 01 1a 09 0a 01 61 12 01 62 |............a..b| // 00000010 1a 01 63 22 07 08 03 10 04 1a 01 64 |..c".......d| const ecnodedString = "0a04c0a8010110011a090a01611201621a01632207080310041a0164" type T = network.VIPOperatorSpec encoded := must(hex.DecodeString(ecnodedString))(t) expected := T{ IP: netip.MustParseAddr("192.168.1.1"), GratuitousARP: true, EquinixMetal: network.VIPEquinixMetalSpec{ ProjectID: "a", DeviceID: "b", APIToken: "c", }, HCloud: network.VIPHCloudSpec{ DeviceID: 3, NetworkID: 4, APIToken: "d", }, } var decoded T require.NoError(t, protoenc.Unmarshal(encoded, &decoded)) require.Equal(t, expected, decoded) } //nolint:lll func ExampleMemberSpec_outputProtoMarshal() { addresses := []netip.Addr{netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0")} spec := &clusterpb.MemberSpec{ NodeId: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: []*common.NetIP{ { Ip: try(addresses[0].MarshalBinary()), }, { Ip: try(addresses[1].MarshalBinary()), }, }, Hostname: "talos-default-controlplane-1", MachineType: enums.MachineType_TYPE_CONTROL_PLANE, OperatingSystem: "Talos (v1.0.0)", } result := try(proto.Marshal(spec)) fmt.Println(hex.Dump(result)) fmt.Println(hex.EncodeToString(result)) // Output: // 00000000 0a 2c 37 78 31 53 75 43 38 45 67 65 35 42 47 58 |.,7x1SuC8Ege5BGX| // 00000010 64 41 66 54 45 66 66 35 69 51 6e 6c 57 5a 4c 66 |dAfTEff5iQnlWZLf| // 00000020 76 39 68 31 4c 47 4d 78 41 32 70 59 6b 43 12 06 |v9h1LGMxA2pYkC..| // 00000030 0a 04 ac 14 00 02 12 12 0a 10 fd 50 8d 60 42 38 |...........P.`B8| // 00000040 63 02 f8 57 23 ff fe 21 d1 e0 1a 1c 74 61 6c 6f |c..W#..!....talo| // 00000050 73 2d 64 65 66 61 75 6c 74 2d 63 6f 6e 74 72 6f |s-default-contro| // 00000060 6c 70 6c 61 6e 65 2d 31 20 02 2a 0e 54 61 6c 6f |lplane-1 .*.Talo| // 00000070 73 20 28 76 31 2e 30 2e 30 29 |s (v1.0.0)| // // 0a2c3778315375433845676535424758644166544566663569516e6c575a4c66763968314c474d78413270596b4312060a04ac14000212120a10fd508d6042386302f85723fffe21d1e01a1c74616c6f732d64656661756c742d636f6e74726f6c706c616e652d3120022a0e54616c6f73202876312e302e3029 } //nolint:lll func ExampleMemberSpec_outputProtoencMarshal() { addresses := []netip.Addr{netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0")} spec := &cluster.MemberSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: addresses, Hostname: "talos-default-controlplane-1", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", } result := try(protoenc.Marshal(spec)) fmt.Println(hex.Dump(result)) fmt.Println(hex.EncodeToString(result)) // Output: // 00000000 0a 2c 37 78 31 53 75 43 38 45 67 65 35 42 47 58 |.,7x1SuC8Ege5BGX| // 00000010 64 41 66 54 45 66 66 35 69 51 6e 6c 57 5a 4c 66 |dAfTEff5iQnlWZLf| // 00000020 76 39 68 31 4c 47 4d 78 41 32 70 59 6b 43 12 06 |v9h1LGMxA2pYkC..| // 00000030 0a 04 ac 14 00 02 12 12 0a 10 fd 50 8d 60 42 38 |...........P.`B8| // 00000040 63 02 f8 57 23 ff fe 21 d1 e0 1a 1c 74 61 6c 6f |c..W#..!....talo| // 00000050 73 2d 64 65 66 61 75 6c 74 2d 63 6f 6e 74 72 6f |s-default-contro| // 00000060 6c 70 6c 61 6e 65 2d 31 20 02 2a 0e 54 61 6c 6f |lplane-1 .*.Talo| // 00000070 73 20 28 76 31 2e 30 2e 30 29 |s (v1.0.0)| // // 0a2c3778315375433845676535424758644166544566663569516e6c575a4c66763968314c474d78413270596b4312060a04ac14000212120a10fd508d6042386302f85723fffe21d1e01a1c74616c6f732d64656661756c742d636f6e74726f6c706c616e652d3120022a0e54616c6f73202876312e302e3029 } func ExampleVIPOperatorSpec_outputProtoMarshal() { spec := &networkpb.VIPOperatorSpec{ Ip: &common.NetIP{Ip: try(netip.MustParseAddr("192.168.1.1").MarshalBinary())}, GratuitousArp: true, EquinixMetal: &networkpb.VIPEquinixMetalSpec{ ProjectId: "a", DeviceId: "b", ApiToken: "c", }, HCloud: &networkpb.VIPHCloudSpec{ DeviceId: 3, NetworkId: 4, ApiToken: "d", }, } result := try(proto.Marshal(spec)) fmt.Println(hex.Dump(result)) fmt.Println(hex.EncodeToString(result)) // Output: // 00000000 0a 06 0a 04 c0 a8 01 01 10 01 1a 09 0a 01 61 12 |..............a.| // 00000010 01 62 1a 01 63 22 07 08 03 10 04 1a 01 64 |.b..c".......d| // // 0a060a04c0a8010110011a090a01611201621a01632207080310041a0164 } //nolint:dupword func ExampleVIPOperatorSpec_outputProtoencMarshal() { spec := &network.VIPOperatorSpec{ IP: netip.MustParseAddr("192.168.1.1"), GratuitousARP: true, EquinixMetal: network.VIPEquinixMetalSpec{ ProjectID: "a", DeviceID: "b", APIToken: "c", }, HCloud: network.VIPHCloudSpec{ DeviceID: 3, NetworkID: 4, APIToken: "d", }, } result := try(protoenc.Marshal(spec)) fmt.Println(hex.Dump(result)) fmt.Println(hex.EncodeToString(result)) // Output: // 00000000 0a 06 0a 04 c0 a8 01 01 10 01 1a 09 0a 01 61 12 |..............a.| // 00000010 01 62 1a 01 63 22 07 08 03 10 04 1a 01 64 |.b..c".......d| // // 0a060a04c0a8010110011a090a01611201621a01632207080310041a0164 } func TestMemberSpec(t *testing.T) { addresses := []netip.Addr{netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0")} spec := cluster.MemberSpec{ NodeID: "7x1SuC8Ege5BGXdAfTEff5iQnlWZLfv9h1LGMxA2pYkC", Addresses: addresses, Hostname: "talos-default-controlplane-1", MachineType: machine.TypeControlPlane, OperatingSystem: "Talos (v1.0.0)", } runTestPipe[clusterpb.MemberSpec](t, spec) } func TestVIPOperatorSpec(t *testing.T) { spec := network.VIPOperatorSpec{ IP: netip.MustParseAddr("192.168.1.1"), GratuitousARP: true, EquinixMetal: network.VIPEquinixMetalSpec{ ProjectID: "a", DeviceID: "b", APIToken: "c", }, HCloud: network.VIPHCloudSpec{ DeviceID: 3, NetworkID: 4, APIToken: "d", }, } runTestPipe[networkpb.VIPOperatorSpec](t, spec) } //nolint:dupword func TestVLANSpecOldEncoding(t *testing.T) { t.Parallel() // Input: // 00000000 0d 19 00 00 00 15 a8 88 00 00 |..........| const ecnodedString = "0d1900000015a8880000" type T = network.VLANSpec encoded := must(hex.DecodeString(ecnodedString))(t) expected := T{ VID: 25, Protocol: nethelpers.VLANProtocol8021AD, } var decoded T require.NoError(t, protoenc.Unmarshal(encoded, &decoded)) require.Equal(t, expected, decoded) encodedThis := must(protoenc.Marshal(&decoded))(t) t.Logf("encoded original:\n%s", hex.Dump(encoded)) t.Logf("encoded in this version:\n%s", hex.Dump(encodedThis)) } type msg[T any] interface { *T proto.Message } func runTestPipe[R any, RP msg[R], T any](t *testing.T, original T) { encoded1 := must(protoenc.Marshal(&original))(t) var decoded1 R require.NoError(t, proto.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(encoded1, RP(&decoded1))) encoded2 := must(proto.Marshal(RP(&decoded1)))(t) var decoded2 T require.NoError(t, protoenc.Unmarshal(encoded2, &decoded2)) require.Equal(t, original, decoded2) } func try[T any](v T, err error) T { if err != nil { panic(err) } return v } func must[T any](v T, err error) func(t *testing.T) T { return func(t *testing.T) T { if err != nil { t.Fatal(err) } return v } } ================================================ FILE: pkg/machinery/proto/resource.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package proto defines a functions to work with proto messages. package proto import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/protoenc" ) // ResourceSpecToProto converts a resource spec to a proto message. func ResourceSpecToProto(i resource.Resource, o Message, opts ...protoenc.MarshalOption) error { marshaled, err := protoenc.Marshal(i.Spec(), opts...) if err != nil { return err } return Unmarshal(marshaled, o) } ================================================ FILE: pkg/machinery/resources/block/block.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package block provides resources related to blockdevices, mounts, etc. package block import ( "context" "fmt" "slices" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/maps" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) //go:generate go tool github.com/siderolabs/deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type ParameterSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -type ZswapStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . //go:generate go tool github.com/dmarkham/enumer -type=VolumeType,VolumePhase,FilesystemType,EncryptionKeyType,EncryptionProviderType,FSParameterType -linecomment -text // NamespaceName contains configuration resources. const NamespaceName resource.Namespace = v1alpha1.NamespaceName // UserDiskLabel is the label for user disks. const UserDiskLabel = "talos.dev/user-disk" // UserVolumeLabel is the label for user volumes. const UserVolumeLabel = "talos.dev/user-volume" // SystemVolumeLabel is the label for system volumes. const SystemVolumeLabel = "talos.dev/system-volume" // RawVolumeLabel is the label for raw volumes. const RawVolumeLabel = "talos.dev/raw-volume" // ExistingVolumeLabel is the label for existing volumes. const ExistingVolumeLabel = "talos.dev/existing-volume" // ExternalVolumeLabel is the label for external volumes. const ExternalVolumeLabel = "talos.dev/external-volume" // SwapVolumeLabel is the label for swap volumes. const SwapVolumeLabel = "talos.dev/swap-volume" // PlatformLabel is the label for platform volumes. const PlatformLabel = "talos.dev/platform" // WaitForVolumePhase waits for the volume to reach the expected phase(s). func WaitForVolumePhase(ctx context.Context, st state.State, volumeID string, expectedPhases ...VolumePhase) (*VolumeStatus, error) { volumeStatus, err := st.WatchFor(ctx, NewVolumeStatus(NamespaceName, volumeID).Metadata(), state.WithCondition(func(r resource.Resource) (bool, error) { volumeStatus, ok := r.(*VolumeStatus) if !ok { return false, nil } return slices.Index(expectedPhases, volumeStatus.TypedSpec().Phase) != -1, nil }), ) if err != nil { return nil, fmt.Errorf("error waiting for volume %q to be ready: %w", volumeID, err) } return volumeStatus.(*VolumeStatus), nil } // GetSystemDisk returns the system disk from the state. // // If the system disk is not found, it returns nil. func GetSystemDisk(ctx context.Context, st state.State) (*SystemDiskSpec, error) { systemDisk, err := safe.StateGetByID[*SystemDisk](ctx, st, SystemDiskID) if err != nil { if state.IsNotFoundError(err) { return nil, nil } return nil, fmt.Errorf("error getting system disk: %w", err) } return systemDisk.TypedSpec(), nil } // GetSystemDiskPaths returns the path(s) of system disk and STATE/EPHEMERAL partitions. // // This is a legacy method to map old concept of system disk wipe into new volume subsystem. func GetSystemDiskPaths(ctx context.Context, st state.State) ([]string, error) { systemDisks := map[string]struct{}{} // fetch system disk (where Talos is installed) systemDisk, err := GetSystemDisk(ctx, st) if err != nil { return nil, err } if systemDisk != nil { systemDisks[systemDisk.DevPath] = struct{}{} } // fetch additional system volumes (which might be on the same or other disks) for _, volumeID := range []string{constants.StatePartitionLabel, constants.EphemeralPartitionLabel} { volumeStatus, err := safe.ReaderGetByID[*VolumeStatus](ctx, st, volumeID) if err != nil { if state.IsNotFoundError(err) { continue } return nil, err } if volumeStatus.TypedSpec().ParentLocation != "" { systemDisks[volumeStatus.TypedSpec().ParentLocation] = struct{}{} } else if volumeStatus.TypedSpec().Location != "" { systemDisks[volumeStatus.TypedSpec().Location] = struct{}{} } } return maps.Keys(systemDisks), nil } ================================================ FILE: pkg/machinery/resources/block/block_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &block.Device{}, &block.DiscoveryRefreshRequest{}, &block.DiscoveryRefreshStatus{}, &block.DiscoveredVolume{}, &block.Disk{}, &block.MountRequest{}, &block.MountStatus{}, &block.SwapStatus{}, &block.Symlink{}, &block.SystemDisk{}, &block.UserDiskConfigStatus{}, &block.VolumeConfig{}, &block.VolumeLifecycle{}, &block.VolumeMountRequest{}, &block.VolumeMountStatus{}, &block.VolumeStatus{}, &block.ZswapStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/block/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type DeviceSpec -type DiscoveredVolumeSpec -type DiscoveryRefreshRequestSpec -type DiscoveryRefreshStatusSpec -type DiskSpec -type MountRequestSpec -type MountStatusSpec -type ParameterSpec -type SwapStatusSpec -type SymlinkSpec -type SystemDiskSpec -type UserDiskConfigStatusSpec -type VolumeConfigSpec -type VolumeLifecycleSpec -type VolumeMountRequestSpec -type VolumeMountStatusSpec -type VolumeStatusSpec -type ZswapStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package block // DeepCopy generates a deep copy of DeviceSpec. func (o DeviceSpec) DeepCopy() DeviceSpec { var cp DeviceSpec = o if o.Secondaries != nil { cp.Secondaries = make([]string, len(o.Secondaries)) copy(cp.Secondaries, o.Secondaries) } return cp } // DeepCopy generates a deep copy of DiscoveredVolumeSpec. func (o DiscoveredVolumeSpec) DeepCopy() DiscoveredVolumeSpec { var cp DiscoveredVolumeSpec = o return cp } // DeepCopy generates a deep copy of DiscoveryRefreshRequestSpec. func (o DiscoveryRefreshRequestSpec) DeepCopy() DiscoveryRefreshRequestSpec { var cp DiscoveryRefreshRequestSpec = o return cp } // DeepCopy generates a deep copy of DiscoveryRefreshStatusSpec. func (o DiscoveryRefreshStatusSpec) DeepCopy() DiscoveryRefreshStatusSpec { var cp DiscoveryRefreshStatusSpec = o return cp } // DeepCopy generates a deep copy of DiskSpec. func (o DiskSpec) DeepCopy() DiskSpec { var cp DiskSpec = o if o.SecondaryDisks != nil { cp.SecondaryDisks = make([]string, len(o.SecondaryDisks)) copy(cp.SecondaryDisks, o.SecondaryDisks) } if o.Symlinks != nil { cp.Symlinks = make([]string, len(o.Symlinks)) copy(cp.Symlinks, o.Symlinks) } return cp } // DeepCopy generates a deep copy of MountRequestSpec. func (o MountRequestSpec) DeepCopy() MountRequestSpec { var cp MountRequestSpec = o if o.Requesters != nil { cp.Requesters = make([]string, len(o.Requesters)) copy(cp.Requesters, o.Requesters) } if o.RequesterIDs != nil { cp.RequesterIDs = make([]string, len(o.RequesterIDs)) copy(cp.RequesterIDs, o.RequesterIDs) } return cp } // DeepCopy generates a deep copy of MountStatusSpec. func (o MountStatusSpec) DeepCopy() MountStatusSpec { var cp MountStatusSpec = o cp.Spec = o.Spec.DeepCopy() return cp } // DeepCopy generates a deep copy of ParameterSpec. func (o ParameterSpec) DeepCopy() ParameterSpec { var cp ParameterSpec = o if o.String != nil { cp.String = new(string) *cp.String = *o.String } if o.Binary != nil { cp.Binary = make([]byte, len(o.Binary)) copy(cp.Binary, o.Binary) } return cp } // DeepCopy generates a deep copy of SwapStatusSpec. func (o SwapStatusSpec) DeepCopy() SwapStatusSpec { var cp SwapStatusSpec = o return cp } // DeepCopy generates a deep copy of SymlinkSpec. func (o SymlinkSpec) DeepCopy() SymlinkSpec { var cp SymlinkSpec = o if o.Paths != nil { cp.Paths = make([]string, len(o.Paths)) copy(cp.Paths, o.Paths) } return cp } // DeepCopy generates a deep copy of SystemDiskSpec. func (o SystemDiskSpec) DeepCopy() SystemDiskSpec { var cp SystemDiskSpec = o return cp } // DeepCopy generates a deep copy of UserDiskConfigStatusSpec. func (o UserDiskConfigStatusSpec) DeepCopy() UserDiskConfigStatusSpec { var cp UserDiskConfigStatusSpec = o return cp } // DeepCopy generates a deep copy of VolumeConfigSpec. func (o VolumeConfigSpec) DeepCopy() VolumeConfigSpec { var cp VolumeConfigSpec = o if o.Encryption.Keys != nil { cp.Encryption.Keys = make([]EncryptionKey, len(o.Encryption.Keys)) copy(cp.Encryption.Keys, o.Encryption.Keys) for i3 := range o.Encryption.Keys { if o.Encryption.Keys[i3].StaticPassphrase != nil { cp.Encryption.Keys[i3].StaticPassphrase = make([]byte, len(o.Encryption.Keys[i3].StaticPassphrase)) copy(cp.Encryption.Keys[i3].StaticPassphrase, o.Encryption.Keys[i3].StaticPassphrase) } if o.Encryption.Keys[i3].TPMPCRs != nil { cp.Encryption.Keys[i3].TPMPCRs = make([]int, len(o.Encryption.Keys[i3].TPMPCRs)) copy(cp.Encryption.Keys[i3].TPMPCRs, o.Encryption.Keys[i3].TPMPCRs) } if o.Encryption.Keys[i3].TPMPubKeyPCRs != nil { cp.Encryption.Keys[i3].TPMPubKeyPCRs = make([]int, len(o.Encryption.Keys[i3].TPMPubKeyPCRs)) copy(cp.Encryption.Keys[i3].TPMPubKeyPCRs, o.Encryption.Keys[i3].TPMPubKeyPCRs) } } } if o.Encryption.PerfOptions != nil { cp.Encryption.PerfOptions = make([]string, len(o.Encryption.PerfOptions)) copy(cp.Encryption.PerfOptions, o.Encryption.PerfOptions) } if o.Mount.BindTarget != nil { cp.Mount.BindTarget = new(string) *cp.Mount.BindTarget = *o.Mount.BindTarget } if o.Mount.Parameters != nil { cp.Mount.Parameters = make([]ParameterSpec, len(o.Mount.Parameters)) copy(cp.Mount.Parameters, o.Mount.Parameters) for i3 := range o.Mount.Parameters { cp.Mount.Parameters[i3] = o.Mount.Parameters[i3].DeepCopy() } } return cp } // DeepCopy generates a deep copy of VolumeLifecycleSpec. func (o VolumeLifecycleSpec) DeepCopy() VolumeLifecycleSpec { var cp VolumeLifecycleSpec = o return cp } // DeepCopy generates a deep copy of VolumeMountRequestSpec. func (o VolumeMountRequestSpec) DeepCopy() VolumeMountRequestSpec { var cp VolumeMountRequestSpec = o return cp } // DeepCopy generates a deep copy of VolumeMountStatusSpec. func (o VolumeMountStatusSpec) DeepCopy() VolumeMountStatusSpec { var cp VolumeMountStatusSpec = o return cp } // DeepCopy generates a deep copy of VolumeStatusSpec. func (o VolumeStatusSpec) DeepCopy() VolumeStatusSpec { var cp VolumeStatusSpec = o if o.EncryptionFailedSyncs != nil { cp.EncryptionFailedSyncs = make([]string, len(o.EncryptionFailedSyncs)) copy(cp.EncryptionFailedSyncs, o.EncryptionFailedSyncs) } if o.ConfiguredEncryptionKeys != nil { cp.ConfiguredEncryptionKeys = make([]string, len(o.ConfiguredEncryptionKeys)) copy(cp.ConfiguredEncryptionKeys, o.ConfiguredEncryptionKeys) } if o.EncryptionSlot != nil { cp.EncryptionSlot = new(int) *cp.EncryptionSlot = *o.EncryptionSlot } if o.TPMEncryptionOptions.PCRs != nil { cp.TPMEncryptionOptions.PCRs = make([]int, len(o.TPMEncryptionOptions.PCRs)) copy(cp.TPMEncryptionOptions.PCRs, o.TPMEncryptionOptions.PCRs) } if o.TPMEncryptionOptions.PubKeyPCRs != nil { cp.TPMEncryptionOptions.PubKeyPCRs = make([]int, len(o.TPMEncryptionOptions.PubKeyPCRs)) copy(cp.TPMEncryptionOptions.PubKeyPCRs, o.TPMEncryptionOptions.PubKeyPCRs) } if o.MountSpec.BindTarget != nil { cp.MountSpec.BindTarget = new(string) *cp.MountSpec.BindTarget = *o.MountSpec.BindTarget } if o.MountSpec.Parameters != nil { cp.MountSpec.Parameters = make([]ParameterSpec, len(o.MountSpec.Parameters)) copy(cp.MountSpec.Parameters, o.MountSpec.Parameters) for i3 := range o.MountSpec.Parameters { cp.MountSpec.Parameters[i3] = o.MountSpec.Parameters[i3].DeepCopy() } } return cp } // DeepCopy generates a deep copy of ZswapStatusSpec. func (o ZswapStatusSpec) DeepCopy() ZswapStatusSpec { var cp ZswapStatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/block/device.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DeviceType is type of BlockDevice resource. const DeviceType = resource.Type("BlockDevices.block.talos.dev") // Device resource holds status of hardware devices (overall). type Device = typed.Resource[DeviceSpec, DeviceExtension] // DeviceSpec is the spec for devices status. // //gotagsrewrite:gen type DeviceSpec struct { Type string `yaml:"type" protobuf:"1"` Major int `yaml:"major" protobuf:"2"` Minor int `yaml:"minor" protobuf:"3"` PartitionName string `yaml:"partitionName,omitempty" protobuf:"4"` PartitionNumber int `yaml:"partitionNumber,omitempty" protobuf:"5"` DevicePath string `yaml:"devicePath" protobuf:"7"` // Generation is bumped every time the device might have changed and might need to be re-probed. Generation int `yaml:"generation" protobuf:"6"` // Parent (if set) specifies the parent device ID. Parent string `yaml:"parent,omitempty" protobuf:"8"` // Secondaries (if set) specifies the secondary device IDs. // // E.g. for a LVM volume secondary is a list of blockdevices that the volume consists of. Secondaries []string `yaml:"secondaries,omitempty" protobuf:"9"` } // DeviceType constants. const ( DeviceTypeDisk = "disk" DeviceTypePartition = "partition" ) // NewDevice initializes a BlockDevice resource. func NewDevice(namespace resource.Namespace, id resource.ID) *Device { return typed.NewResource[DeviceSpec, DeviceExtension]( resource.NewMetadata(namespace, DeviceType, id, resource.VersionUndefined), DeviceSpec{}, ) } // DeviceExtension is auxiliary resource data for BlockDevice. type DeviceExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DeviceExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DeviceType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Type", JSONPath: `{.type}`, }, { Name: "PartitionName", JSONPath: `{.partitionName}`, }, { Name: "Generation", JSONPath: `{.generation}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(DeviceType, &Device{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/discovered_volume.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/dustin/go-humanize" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DiscoveredVolumeType is type of DiscoveredVolume resource. const DiscoveredVolumeType = resource.Type("DiscoveredVolumes.block.talos.dev") // DiscoveredVolume resource holds status of hardware DiscoveredVolumes (overall). type DiscoveredVolume = typed.Resource[DiscoveredVolumeSpec, DiscoveredVolumeExtension] // DiscoveredVolumeSpec is the spec for DiscoveredVolumes resource. // //gotagsrewrite:gen type DiscoveredVolumeSpec struct { DevPath string `yaml:"dev_path" protobuf:"17"` Type string `yaml:"type" protobuf:"14"` DevicePath string `yaml:"device_path" protobuf:"15"` Parent string `yaml:"parent,omitempty" protobuf:"16"` ParentDevPath string `yaml:"parent_dev_path,omitempty" protobuf:"18"` // Offset of the partition/volume inside Parent device (in bytes). Offset uint64 `yaml:"offset" protobuf:"20"` // Overall size of the probed device (in bytes). Size uint64 `yaml:"size" protobuf:"1"` PrettySize string `yaml:"pretty_size" protobuf:"19"` // Sector size of the device (in bytes). SectorSize uint `yaml:"sector_size,omitempty" protobuf:"2"` // Optimal I/O size for the device (in bytes). IOSize uint `yaml:"io_size,omitempty" protobuf:"3"` Name string `yaml:"name" protobuf:"4"` UUID string `yaml:"uuid,omitempty" protobuf:"5"` Label string `yaml:"label,omitempty" protobuf:"6"` BlockSize uint32 `yaml:"block_size,omitempty" protobuf:"7"` FilesystemBlockSize uint32 `yaml:"filesystem_block_size,omitempty" protobuf:"8"` ProbedSize uint64 `yaml:"probed_size,omitempty" protobuf:"9"` PartitionUUID string `yaml:"partition_uuid,omitempty" protobuf:"10"` PartitionType string `yaml:"partition_type,omitempty" protobuf:"11"` PartitionLabel string `yaml:"partition_label,omitempty" protobuf:"12"` PartitionIndex uint `yaml:"partition_index,omitempty" protobuf:"13"` } // SetSize sets the size of the DiscoveredVolume, including the pretty size. func (s *DiscoveredVolumeSpec) SetSize(size uint64) { s.Size = size s.PrettySize = humanize.Bytes(size) } // NewDiscoveredVolume initializes a BlockDiscoveredVolume resource. func NewDiscoveredVolume(namespace resource.Namespace, id resource.ID) *DiscoveredVolume { return typed.NewResource[DiscoveredVolumeSpec, DiscoveredVolumeExtension]( resource.NewMetadata(namespace, DiscoveredVolumeType, id, resource.VersionUndefined), DiscoveredVolumeSpec{}, ) } // DiscoveredVolumeExtension is auxiliary resource data for BlockDiscoveredVolume. type DiscoveredVolumeExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DiscoveredVolumeExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DiscoveredVolumeType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Type", JSONPath: `{.type}`, }, { Name: "Size", JSONPath: `{.pretty_size}`, }, { Name: "Discovered", JSONPath: `{.name}`, }, { Name: "Label", JSONPath: `{.label}`, }, { Name: "PartitionLabel", JSONPath: `{.partition_label}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(DiscoveredVolumeType, &DiscoveredVolume{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/discovery_refresh_request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DiscoveryRefreshRequestType is type of DiscoveryRefreshRequest resource. const DiscoveryRefreshRequestType = resource.Type("DiscoveryRefreshRequests.block.talos.dev") // RefreshID is the ID of the singleton discovery refresh request/statue. const RefreshID resource.ID = "refresh" // DiscoveryRefreshRequest resource holds a request to refresh the discovered volumes. type DiscoveryRefreshRequest = typed.Resource[DiscoveryRefreshRequestSpec, DiscoveryRefreshRequestExtension] // DiscoveryRefreshRequestSpec is the spec for DiscoveryRefreshRequest. // //gotagsrewrite:gen type DiscoveryRefreshRequestSpec struct { Request int `yaml:"request" protobuf:"1"` } // NewDiscoveryRefreshRequest initializes a DiscoveryRefreshRequest resource. func NewDiscoveryRefreshRequest(namespace resource.Namespace, id resource.ID) *DiscoveryRefreshRequest { return typed.NewResource[DiscoveryRefreshRequestSpec, DiscoveryRefreshRequestExtension]( resource.NewMetadata(namespace, DiscoveryRefreshRequestType, id, resource.VersionUndefined), DiscoveryRefreshRequestSpec{}, ) } // DiscoveryRefreshRequestExtension is auxiliary resource data for DiscoveryRefreshRequest. type DiscoveryRefreshRequestExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DiscoveryRefreshRequestExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DiscoveryRefreshRequestType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Request", JSONPath: `{.request}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(DiscoveryRefreshRequestType, &DiscoveryRefreshRequest{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/discovery_refresh_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DiscoveryRefreshStatusType is type of DiscoveryRefreshStatus resource. const DiscoveryRefreshStatusType = resource.Type("DiscoveryRefreshStatuses.block.talos.dev") // DiscoveryRefreshStatus resource holds a status of refresh. type DiscoveryRefreshStatus = typed.Resource[DiscoveryRefreshStatusSpec, DiscoveryRefreshStatusExtension] // DiscoveryRefreshStatusSpec is the spec for DiscoveryRefreshStatus status. // //gotagsrewrite:gen type DiscoveryRefreshStatusSpec struct { Request int `yaml:"request" protobuf:"1"` } // NewDiscoveryRefreshStatus initializes a DiscoveryRefreshStatus resource. func NewDiscoveryRefreshStatus(namespace resource.Namespace, id resource.ID) *DiscoveryRefreshStatus { return typed.NewResource[DiscoveryRefreshStatusSpec, DiscoveryRefreshStatusExtension]( resource.NewMetadata(namespace, DiscoveryRefreshStatusType, id, resource.VersionUndefined), DiscoveryRefreshStatusSpec{}, ) } // DiscoveryRefreshStatusExtension is auxiliary resource data for DiscoveryRefreshStatus. type DiscoveryRefreshStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DiscoveryRefreshStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DiscoveryRefreshStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Request", JSONPath: `{.request}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(DiscoveryRefreshStatusType, &DiscoveryRefreshStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/dustin/go-humanize" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DiskType is type of Disk resource. const DiskType = resource.Type("Disks.block.talos.dev") // Disk resource holds status of hardware disks. type Disk = typed.Resource[DiskSpec, DiskExtension] // DiskSpec is the spec for Disks status. // //gotagsrewrite:gen type DiskSpec struct { DevPath string `yaml:"dev_path" protobuf:"14"` Size uint64 `yaml:"size" protobuf:"1"` PrettySize string `yaml:"pretty_size" protobuf:"15"` IOSize uint `yaml:"io_size" protobuf:"2"` SectorSize uint `yaml:"sector_size" protobuf:"3"` Readonly bool `yaml:"readonly" protobuf:"4"` CDROM bool `yaml:"cdrom" protobuf:"13"` Model string `yaml:"model,omitempty" protobuf:"5"` Serial string `yaml:"serial,omitempty" protobuf:"6"` Modalias string `yaml:"modalias,omitempty" protobuf:"7"` WWID string `yaml:"wwid,omitempty" protobuf:"8"` UUID string `yaml:"uuid,omitempty" protobuf:"17"` BusPath string `yaml:"bus_path,omitempty" protobuf:"9"` SubSystem string `yaml:"sub_system,omitempty" protobuf:"10"` Transport string `yaml:"transport,omitempty" protobuf:"11"` Rotational bool `yaml:"rotational,omitempty" protobuf:"12"` // SecondaryDisks (if set) specifies the secondary disk IDs. // // E.g. if the blockdevice secondary is vda5, the secondary disk will be set as vda. // This allows to map secondaries between disks ignoring the partitions. SecondaryDisks []string `yaml:"secondary_disks,omitempty" protobuf:"16"` Symlinks []string `yaml:"symlinks,omitempty" protobuf:"18"` } // SetSize sets the size of the disk, including the pretty size. func (s *DiskSpec) SetSize(size uint64) { s.Size = size s.PrettySize = humanize.Bytes(size) } // NewDisk initializes a BlockDisk resource. func NewDisk(namespace resource.Namespace, id resource.ID) *Disk { return typed.NewResource[DiskSpec, DiskExtension]( resource.NewMetadata(namespace, DiskType, id, resource.VersionUndefined), DiskSpec{}, ) } // DiskExtension is auxiliary resource data for BlockDisk. type DiskExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DiskExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DiskType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Size", JSONPath: `{.pretty_size}`, }, { Name: "Read Only", JSONPath: `{.readonly}`, }, { Name: "Transport", JSONPath: `{.transport}`, }, { Name: "Rotational", JSONPath: `{.rotational}`, }, { Name: "WWID", JSONPath: `{.wwid}`, }, { Name: "Model", JSONPath: `{.model}`, }, { Name: "Serial", JSONPath: `{.serial}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(DiskType, &Disk{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/encryptionkeytype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // EncryptionKeyType describes encryption key type. type EncryptionKeyType int // Encryption key types. // //structprotogen:gen_enum const ( EncryptionKeyStatic EncryptionKeyType = iota // static EncryptionKeyNodeID // nodeID EncryptionKeyKMS // kms EncryptionKeyTPM // tpm ) ================================================ FILE: pkg/machinery/resources/block/encryptionprovidertype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // EncryptionProviderType describes encryption provider type. type EncryptionProviderType int // Encryption provider types. // //structprotogen:gen_enum const ( EncryptionProviderNone EncryptionProviderType = iota // none EncryptionProviderLUKS2 // luks2 ) ================================================ FILE: pkg/machinery/resources/block/filesystemtype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // FilesystemType describes filesystem type. type FilesystemType int // Filesystem types. // //structprotogen:gen_enum const ( FilesystemTypeNone FilesystemType = iota // none FilesystemTypeXFS // xfs FilesystemTypeVFAT // vfat FilesystemTypeEXT4 // ext4 FilesystemTypeISO9660 // iso9660 FilesystemTypeSwap // swapi FilesystemTypeVirtiofs // virtiofs ) ================================================ FILE: pkg/machinery/resources/block/fsparametertype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // FSParameterType describes Filesystem Parameter type. type FSParameterType int // NFS Version types. // //structprotogen:gen_enum const ( FSParameterTypeStringValue FSParameterType = iota // string FSParameterTypeBooleanValue // boolean FSParameterTypeBinaryValue // binary ) ================================================ FILE: pkg/machinery/resources/block/mount_request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MountRequestType is type of MountRequest resource. const MountRequestType = resource.Type("MountRequests.block.talos.dev") // MountRequest resource is a final mount request spec. type MountRequest = typed.Resource[MountRequestSpec, MountRequestExtension] // MountRequestSpec is the spec for MountRequest. // //gotagsrewrite:gen type MountRequestSpec struct { VolumeID string `yaml:"volumeID" protobuf:"1"` ParentMountID string `yaml:"parentID" protobuf:"2"` ReadOnly bool `yaml:"readOnly" protobuf:"5"` Detached bool `yaml:"detached" protobuf:"6"` DisableAccessTime bool `yaml:"disableAccessTime,omitempty" protobuf:"7"` Secure bool `yaml:"secure,omitempty" protobuf:"8"` Requesters []string `yaml:"requesters" protobuf:"3"` RequesterIDs []string `yaml:"requesterIDs" protobuf:"4"` } // NewMountRequest initializes a MountRequest resource. func NewMountRequest(namespace resource.Namespace, id resource.ID) *MountRequest { return typed.NewResource[MountRequestSpec, MountRequestExtension]( resource.NewMetadata(namespace, MountRequestType, id, resource.VersionUndefined), MountRequestSpec{}, ) } // MountRequestExtension is auxiliary resource data for BlockMountRequest. type MountRequestExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MountRequestExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MountRequestType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Volume", JSONPath: `{.volumeID}`, }, { Name: "Parent", JSONPath: `{.parentID}`, }, { Name: "Requesters", JSONPath: `{.requesters}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(MountRequestType, &MountRequest{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MountStatusType is type of MountStatus resource. const MountStatusType = resource.Type("MountStatuses.block.talos.dev") // MountStatus resource is a final mount request spec. type MountStatus = typed.Resource[MountStatusSpec, MountStatusExtension] // MountStatusSpec is the spec for MountStatus. // //gotagsrewrite:gen type MountStatusSpec struct { Spec MountRequestSpec `yaml:"spec" protobuf:"1"` Source string `yaml:"source" protobuf:"3"` Target string `yaml:"target" protobuf:"2"` Filesystem FilesystemType `yaml:"filesystem" protobuf:"4"` EncryptionProvider EncryptionProviderType `yaml:"encryptionProvider,omitempty" protobuf:"7"` ReadOnly bool `yaml:"readOnly" protobuf:"5"` ProjectQuotaSupport bool `yaml:"projectQuotaSupport" protobuf:"6"` Detached bool `yaml:"detached" protobuf:"8"` root any } // SetRoot sets the XFS root for the mount. func (m *MountStatusSpec) SetRoot(root any) { m.root = root } // Root gets the XFS root for the mount. // It's not guaranteed to be set (may be nil). func (m *MountStatusSpec) Root() any { return m.root } // NewMountStatus initializes a MountStatus resource. func NewMountStatus(namespace resource.Namespace, id resource.ID) *MountStatus { return typed.NewResource[MountStatusSpec, MountStatusExtension]( resource.NewMetadata(namespace, MountStatusType, id, resource.VersionUndefined), MountStatusSpec{}, ) } // MountStatusExtension is auxiliary resource data for BlockMountStatus. type MountStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MountStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MountStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Source", JSONPath: `{.source}`, }, { Name: "Target", JSONPath: `{.target}`, }, { Name: "Filesystem", JSONPath: `{.filesystem}`, }, { Name: "Volume", JSONPath: `{.spec.volumeID}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(MountStatusType, &MountStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/swap_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SwapStatusType is type of SwapStatus resource. const SwapStatusType = resource.Type("SwapStatuses.block.talos.dev") // SwapStatus resource holds a list of stable symlinks to the blockdevice. type SwapStatus = typed.Resource[SwapStatusSpec, SwapStatusExtension] // SwapStatusSpec is the spec for SwapStatuss resource. // //gotagsrewrite:gen type SwapStatusSpec struct { Device string `yaml:"device" protobuf:"1"` Type string `yaml:"type" protobuf:"7"` SizeBytes uint64 `yaml:"size" protobuf:"2"` SizeHuman string `yaml:"sizeHuman" protobuf:"3"` UsedBytes uint64 `yaml:"used" protobuf:"4"` UsedHuman string `yaml:"usedHuman" protobuf:"5"` Priority int32 `yaml:"priority" protobuf:"6"` } // NewSwapStatus initializes a SwapStatus resource. func NewSwapStatus(namespace resource.Namespace, id resource.ID) *SwapStatus { return typed.NewResource[SwapStatusSpec, SwapStatusExtension]( resource.NewMetadata(namespace, SwapStatusType, id, resource.VersionUndefined), SwapStatusSpec{}, ) } // SwapStatusExtension is auxiliary resource data for SwapStatus. type SwapStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (SwapStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SwapStatusType, Aliases: []resource.Type{"swap", "swaps"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Device", JSONPath: "{.device}", }, { Name: "Size", JSONPath: "{.sizeHuman}", }, { Name: "Used", JSONPath: "{.usedHuman}", }, { Name: "Priority", JSONPath: "{.priority}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(SwapStatusType, &SwapStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/symlink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SymlinkType is type of Symlink resource. const SymlinkType = resource.Type("BlockSymlinks.block.talos.dev") // Symlink resource holds a list of stable symlinks to the blockdevice. type Symlink = typed.Resource[SymlinkSpec, SymlinkExtension] // SymlinkID is the singleton resource ID. const SymlinkID resource.ID = "system-disk" // SymlinkSpec is the spec for Symlinks resource. // //gotagsrewrite:gen type SymlinkSpec struct { Paths []string `yaml:"paths" protobuf:"1"` } // NewSymlink initializes a BlockSymlink resource. func NewSymlink(namespace resource.Namespace, id resource.ID) *Symlink { return typed.NewResource[SymlinkSpec, SymlinkExtension]( resource.NewMetadata(namespace, SymlinkType, id, resource.VersionUndefined), SymlinkSpec{}, ) } // SymlinkExtension is auxiliary resource data for BlockSymlink. type SymlinkExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (SymlinkExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SymlinkType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(SymlinkType, &Symlink{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/system_disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SystemDiskType is type of SystemDisk resource. const SystemDiskType = resource.Type("SystemDisks.block.talos.dev") // SystemDisk resource holds ID of the disk which is the Talos system disk. type SystemDisk = typed.Resource[SystemDiskSpec, SystemDiskExtension] // SystemDiskID is the singleton resource ID. const SystemDiskID resource.ID = "system-disk" // SystemDiskSpec is the spec for SystemDisks resource. // //gotagsrewrite:gen type SystemDiskSpec struct { DiskID string `yaml:"diskID" protobuf:"1"` DevPath string `yaml:"devPath" protobuf:"2"` } // NewSystemDisk initializes a BlockSystemDisk resource. func NewSystemDisk(namespace resource.Namespace, id resource.ID) *SystemDisk { return typed.NewResource[SystemDiskSpec, SystemDiskExtension]( resource.NewMetadata(namespace, SystemDiskType, id, resource.VersionUndefined), SystemDiskSpec{}, ) } // SystemDiskExtension is auxiliary resource data for BlockSystemDisk. type SystemDiskExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (SystemDiskExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SystemDiskType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Disk", JSONPath: `{.diskID}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(SystemDiskType, &SystemDisk{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/user_disk_config_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // UserDiskConfigStatusType is type of UserDiskConfigStatus resource. const UserDiskConfigStatusType = resource.Type("UserDiskConfigStatuses.block.talos.dev") // UserDiskConfigStatus resource holds a status of user disk machine configuration. type UserDiskConfigStatus = typed.Resource[UserDiskConfigStatusSpec, UserDiskConfigStatusExtension] // UserDiskConfigStatusID is the ID of the UserDiskConfigStatus resource. const UserDiskConfigStatusID = "user-disks" // UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus resource. // //gotagsrewrite:gen type UserDiskConfigStatusSpec struct { Ready bool `yaml:"ready" protobuf:"1"` TornDown bool `yaml:"tornDown" protobuf:"2"` } // NewUserDiskConfigStatus initializes a UserDiskConfigStatus resource. func NewUserDiskConfigStatus(namespace resource.Namespace, id resource.ID) *UserDiskConfigStatus { return typed.NewResource[UserDiskConfigStatusSpec, UserDiskConfigStatusExtension]( resource.NewMetadata(namespace, UserDiskConfigStatusType, id, resource.VersionUndefined), UserDiskConfigStatusSpec{}, ) } // UserDiskConfigStatusExtension is auxiliary resource data for UserDiskConfigStatus. type UserDiskConfigStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (UserDiskConfigStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: UserDiskConfigStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: `{.ready}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(UserDiskConfigStatusType, &UserDiskConfigStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volume_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "fmt" "os" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/yamlutils" ) // VolumeConfigType is type of VolumeConfig resource. const VolumeConfigType = resource.Type("VolumeConfigs.block.talos.dev") // VolumeConfig resource contains configuration for machine volumes. type VolumeConfig = typed.Resource[VolumeConfigSpec, VolumeConfigExtension] // VolumeConfigSpec is the spec for VolumeConfig resource. // //gotagsrewrite:gen type VolumeConfigSpec struct { // Parent volume ID, if set no operations on the volume continue until the parent volume is ready. ParentID string `yaml:"parentId,omitempty" protobuf:"1"` // Volume type. Type VolumeType `yaml:"type" protobuf:"2"` // Provisioning configuration (how to provision a volume). Provisioning ProvisioningSpec `yaml:"provisioning" protobuf:"3"` // Encryption configuration (how to encrypt a volume). Encryption EncryptionSpec `yaml:"encryption,omitempty" protobuf:"6"` // How to find a volume. Locator LocatorSpec `yaml:"locator" protobuf:"4"` // Mount options for the volume. Mount MountSpec `yaml:"mount,omitempty" protobuf:"5"` // Symlink options for the volume. Symlink SymlinkProvisioningSpec `yaml:"symlink,omitempty" protobuf:"7"` } // Wave constants. const ( WaveSystemDisk = -1 WaveUserVolumes = 0 WaveLegacyUserDisks = 1000000 // legacy user disks rely on specific order of provisioning ) // ProvisioningSpec is the spec for volume provisioning. // //gotagsrewrite:gen type ProvisioningSpec struct { // Provisioning wave for the volume. // // Waves are processed sequentially - the volumes in the wave are only provisioned after the previous wave is done. Wave int `yaml:"wave,omitempty" protobuf:"3"` // DiskSelector selects a disk for the volume. DiskSelector DiskSelector `yaml:"diskSelector,omitempty" protobuf:"1"` // PartitionSpec describes how to provision the volume (partition type). PartitionSpec PartitionSpec `yaml:"partitionSpec,omitempty" protobuf:"2"` // FilesystemSpec describes how to provision the volume (filesystem type). FilesystemSpec FilesystemSpec `yaml:"filesystemSpec,omitempty" protobuf:"4"` } // DiskSelector selects a disk for the volume. // //gotagsrewrite:gen type DiskSelector struct { Match cel.Expression `yaml:"match,omitempty" protobuf:"1"` External string `yaml:"external,omitempty" protobuf:"2"` } // PartitionSpec is the spec for volume partitioning. // //gotagsrewrite:gen type PartitionSpec struct { // Partition minimum size in bytes. MinSize uint64 `yaml:"minSize" protobuf:"1"` // Partition maximum size in bytes, if not set, grows to the maximum size. MaxSize uint64 `yaml:"maxSize,omitempty" protobuf:"2"` // Partition maximum size (relative), if not set, grows to the maximum size. RelativeMaxSize uint64 `yaml:"relativeMaxSize" protobuf:"6"` // NegativeMaxSize indicates that MaxSize or RelativeMaxSize represents space to be left free on the device rather than space to consume. NegativeMaxSize bool `yaml:"negativeMaxSize" protobuf:"7"` // Grow the partition automatically to the maximum size. Grow bool `yaml:"grow" protobuf:"3"` // Label for the partition. Label string `yaml:"label,omitempty" protobuf:"4"` // Partition type UUID. TypeUUID string `yaml:"typeUUID,omitempty" protobuf:"5"` } // ResolveMaxSize resolves the maximum size of the partition. // Returns the maximum size of the partition and an error if the partition size cannot be resolved. func (ps *PartitionSpec) ResolveMaxSize(available uint64) (uint64, error) { var size uint64 if ps.RelativeMaxSize != 0 { size = available * ps.RelativeMaxSize / 100 } else { size = ps.MaxSize } if ps.NegativeMaxSize { if size > available { return 0, fmt.Errorf("partition size cannot be negative") } return available - size, nil } return size, nil } // LocatorSpec is the spec for volume locator. // //gotagsrewrite:gen type LocatorSpec struct { // Match is a volume locator match expression. Match cel.Expression `yaml:"match,omitempty" protobuf:"1"` // DiskMatch is a disk locator match expression. DiskMatch cel.Expression `yaml:"diskMatch,omitempty" protobuf:"2"` } // FilesystemSpec is the spec for volume filesystem. // //gotagsrewrite:gen type FilesystemSpec struct { // Filesystem type. Type FilesystemType `yaml:"type" protobuf:"1"` // Filesystem label. Label string `yaml:"label,omitempty" protobuf:"2"` } // EncryptionSpec is the spec for volume encryption. // //gotagsrewrite:gen type EncryptionSpec struct { Provider EncryptionProviderType `yaml:"provider" protobuf:"1"` Keys []EncryptionKey `yaml:"keys" protobuf:"2"` Cipher string `yaml:"cipher,omitempty" protobuf:"3"` KeySize uint `yaml:"keySize,omitempty" protobuf:"4"` BlockSize uint64 `yaml:"blockSize,omitempty" protobuf:"5"` PerfOptions []string `yaml:"perfOptions,omitempty" protobuf:"6"` } // EncryptionKey is the spec for volume encryption key. // //gotagsrewrite:gen type EncryptionKey struct { Slot int `yaml:"slot" protobuf:"1"` Type EncryptionKeyType `yaml:"type" protobuf:"2"` LockToSTATE bool `yaml:"lockToState,omitempty" protobuf:"6"` // Only for Type == "static": StaticPassphrase yamlutils.StringBytes `yaml:"staticPassphrase,omitempty" protobuf:"3"` // Only for Type == "kms": KMSEndpoint string `yaml:"kmsEndpoint,omitempty" protobuf:"4"` // Only for Type == "tpm": TPMCheckSecurebootStatusOnEnroll bool `yaml:"tpmCheckSecurebootStatusOnEnroll,omitempty" protobuf:"5"` // Only for Type == "tpm": TPMPCRs []int `yaml:"pcrs,omitempty" protobuf:"7"` // Only for Type == "tpm": TPMPubKeyPCRs []int `yaml:"pubKeyPcrs,omitempty" protobuf:"8"` } // ParameterSpec is a mount parameter. // //gotagsrewrite:gen type ParameterSpec struct { // Type of the parameter. Type FSParameterType `yaml:"type" protobuf:"1"` // Name of the parameter. Name string `yaml:"name" protobuf:"2"` // String value of the parameter. String *string `yaml:"string,omitempty" protobuf:"3"` // Binary value of the parameter. Binary []byte `yaml:"binary,omitempty" protobuf:"5"` } // NewBooleanParameter creates a new boolean parameter. func NewBooleanParameter(name string) ParameterSpec { return ParameterSpec{ Type: FSParameterTypeBooleanValue, Name: name, } } // NewBinaryParameter creates a new binary parameter. func NewBinaryParameter(name string, value []byte) ParameterSpec { return ParameterSpec{ Type: FSParameterTypeBinaryValue, Name: name, Binary: value, } } // NewStringParameter creates a new string parameter. func NewStringParameter(name, value string) ParameterSpec { return ParameterSpec{ Type: FSParameterTypeStringValue, Name: name, String: new(value), } } // MountSpec is the spec for volume mount. // //gotagsrewrite:gen type MountSpec struct { // Mount path for the volume. TargetPath string `yaml:"targetPath" protobuf:"1"` // SELinux label for the volume. SelinuxLabel string `yaml:"selinuxLabel" protobuf:"2"` // Enable project quota (xfs) for the volume. ProjectQuotaSupport bool `yaml:"projectQuotaSupport" protobuf:"3"` // Parent mount request ID. ParentID string `yaml:"parentId,omitempty" protobuf:"4"` // FileMode is the file mode for the mount target. FileMode os.FileMode `yaml:"fileMode,omitempty" protobuf:"5"` // UID is the user ID for the mount target. UID int `yaml:"uid,omitempty" protobuf:"6"` // GID is the group ID for the mount target. GID int `yaml:"gid,omitempty" protobuf:"7"` // RecursiveRelabel is the recursive relabel/chown flag for the mount target. RecursiveRelabel bool `yaml:"recursiveRelabel,omitempty" protobuf:"8"` // BindTarget is an optional path on the host to bind-mount the volume onto. BindTarget *string `yaml:"bindTarget,omitempty" protobuf:"9"` // Parameters are additional filesystem mount options used when mounting the volume. Parameters []ParameterSpec `yaml:"parameters,omitempty" protobuf:"10"` } // SymlinkProvisioningSpec is the spec for volume symlink. // //gotagsrewrite:gen type SymlinkProvisioningSpec struct { // Symlink target path for the volume. SymlinkTargetPath string `yaml:"symlinkTargetPath" protobuf:"1"` // Force symlink creation. Force bool `yaml:"force" protobuf:"2"` } // NewVolumeConfig initializes a BlockVolumeConfig resource. func NewVolumeConfig(namespace resource.Namespace, id resource.ID) *VolumeConfig { return typed.NewResource[VolumeConfigSpec, VolumeConfigExtension]( resource.NewMetadata(namespace, VolumeConfigType, id, resource.VersionUndefined), VolumeConfigSpec{}, ) } // VolumeConfigExtension is auxiliary resource data for BlockVolumeConfig. type VolumeConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (VolumeConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VolumeConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VolumeConfigType, &VolumeConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volume_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/block" ) func TestPartitionSpec_ResolveMaxSize(t *testing.T) { tests := []struct { name string ps *block.PartitionSpec available uint64 want uint64 err string }{ { name: "absolute max size", ps: &block.PartitionSpec{ MaxSize: 50, }, available: 100, want: 50, }, { name: "relative max size", ps: &block.PartitionSpec{ RelativeMaxSize: 50, }, available: 100, want: 50, }, { name: "negative absolute max size", ps: &block.PartitionSpec{ MaxSize: 20, NegativeMaxSize: true, }, available: 100, want: 80, }, { name: "negative relative max size", ps: &block.PartitionSpec{ RelativeMaxSize: 25, NegativeMaxSize: true, }, available: 200, want: 150, }, { name: "zero sizes", ps: &block.PartitionSpec{}, available: 100, want: 0, }, { name: "negative absolute max size > available", ps: &block.PartitionSpec{ MaxSize: 2500, NegativeMaxSize: true, }, available: 200, err: "partition size cannot be negative", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { actual, err := tt.ps.ResolveMaxSize(tt.available) assert.Equal(t, tt.want, actual) if tt.err != "" { assert.EqualError(t, err, tt.err) } else { assert.NoError(t, err) } }) } } ================================================ FILE: pkg/machinery/resources/block/volume_lifecycle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // VolumeLifecycleType is type of VolumeLifecycle resource. const VolumeLifecycleType = resource.Type("VolumeLifecycles.block.talos.dev") // VolumeLifecycleID is the singleton ID of the resource. const VolumeLifecycleID = resource.ID("volumes") // VolumeLifecycle resource exists to signal that the volumes are to be closed. // // Volume manager controller puts a finalizer on this resource, and when // this resource is being torn down, it will close all the volumes, and release the finalizer. type VolumeLifecycle = typed.Resource[VolumeLifecycleSpec, VolumeLifecycleExtension] // VolumeLifecycleSpec is empty. type VolumeLifecycleSpec struct{} // NewVolumeLifecycle initializes an empty VolumeLifecycle resource. func NewVolumeLifecycle(namespace resource.Namespace, id resource.ID) *VolumeLifecycle { return typed.NewResource[VolumeLifecycleSpec, VolumeLifecycleExtension]( resource.NewMetadata(namespace, VolumeLifecycleType, id, resource.VersionUndefined), VolumeLifecycleSpec{}, ) } // VolumeLifecycleExtension provides auxiliary methods for VolumeLifecycle. type VolumeLifecycleExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (VolumeLifecycleExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VolumeLifecycleType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VolumeLifecycleType, &VolumeLifecycle{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volume_mount_request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // VolumeMountRequestType is type of VolumeMountRequest resource. const VolumeMountRequestType = resource.Type("VolumeMountRequests.block.talos.dev") // VolumeMountRequest resource holds a request of a subsystem to mount some volume. type VolumeMountRequest = typed.Resource[VolumeMountRequestSpec, VolumeMountRequestExtension] // VolumeMountRequestSpec is the spec for VolumeMountRequest. // //gotagsrewrite:gen type VolumeMountRequestSpec struct { VolumeID string `yaml:"volumeID" protobuf:"1"` Requester string `yaml:"requester" protobuf:"2"` ReadOnly bool `yaml:"readOnly" protobuf:"3"` Detached bool `yaml:"detached" protobuf:"4"` DisableAccessTime bool `yaml:"disableAccessTime" protobuf:"5"` Secure bool `yaml:"secure" protobuf:"6"` } // NewVolumeMountRequest initializes a VolumeMountRequest resource. func NewVolumeMountRequest(namespace resource.Namespace, id resource.ID) *VolumeMountRequest { return typed.NewResource[VolumeMountRequestSpec, VolumeMountRequestExtension]( resource.NewMetadata(namespace, VolumeMountRequestType, id, resource.VersionUndefined), VolumeMountRequestSpec{}, ) } // VolumeMountRequestExtension is auxiliary resource data for BlockVolumeMountRequest. type VolumeMountRequestExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (VolumeMountRequestExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VolumeMountRequestType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Volume ID", JSONPath: `{.volumeID}`, }, { Name: "Requester", JSONPath: `{.requester}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VolumeMountRequestType, &VolumeMountRequest{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volume_mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // VolumeMountStatusType is type of VolumeMountStatus resource. const VolumeMountStatusType = resource.Type("VolumeMountStatuses.block.talos.dev") // VolumeMountStatus resource holds a status of a subsystem to mount some volume. type VolumeMountStatus = typed.Resource[VolumeMountStatusSpec, VolumeMountStatusExtension] // VolumeMountStatusSpec is the spec for VolumeMountStatus. // //gotagsrewrite:gen type VolumeMountStatusSpec struct { VolumeID string `yaml:"volumeID" protobuf:"1"` Requester string `yaml:"requester" protobuf:"2"` Target string `yaml:"target" protobuf:"3"` ReadOnly bool `yaml:"readOnly" protobuf:"4"` Detached bool `yaml:"detached" protobuf:"5"` DisableAccessTime bool `yaml:"disableAccessTime" protobuf:"6"` Secure bool `yaml:"secure" protobuf:"7"` root any } // SetRoot sets the XFS root for the mount. func (m *VolumeMountStatusSpec) SetRoot(root any) { m.root = root } // Root gets the XFS root for the mount. // It's not guaranteed to be set (may be nil). func (m *VolumeMountStatusSpec) Root() any { return m.root } // NewVolumeMountStatus initializes a VolumeMountStatus resource. func NewVolumeMountStatus(namespace resource.Namespace, id resource.ID) *VolumeMountStatus { return typed.NewResource[VolumeMountStatusSpec, VolumeMountStatusExtension]( resource.NewMetadata(namespace, VolumeMountStatusType, id, resource.VersionUndefined), VolumeMountStatusSpec{}, ) } // VolumeMountStatusExtension is auxiliary resource data for BlockVolumeMountStatus. type VolumeMountStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (VolumeMountStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VolumeMountStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Volume ID", JSONPath: `{.volumeID}`, }, { Name: "Requester", JSONPath: `{.requester}`, }, { Name: "Target", JSONPath: `{.target}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VolumeMountStatusType, &VolumeMountStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volume_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/dustin/go-humanize" "github.com/siderolabs/talos/pkg/machinery/proto" ) // VolumeStatusType is type of VolumeStatus resource. const VolumeStatusType = resource.Type("VolumeStatuses.block.talos.dev") // VolumeStatus resource contains information about the volume status. type VolumeStatus = typed.Resource[VolumeStatusSpec, VolumeStatusExtension] // VolumeStatusSpec is the spec for VolumeStatus resource. // //gotagsrewrite:gen type VolumeStatusSpec struct { Phase VolumePhase `yaml:"phase" protobuf:"1"` PreFailPhase VolumePhase `yaml:"preFailPhase,omitempty" protobuf:"6"` Type VolumeType `yaml:"type" protobuf:"16"` ParentID string `yaml:"parentID,omitempty" protobuf:"19"` // Location is the path to the block device (raw). Location string `yaml:"location,omitempty" protobuf:"2"` // MountLocation is the location to be mounted, might be different from location. MountLocation string `yaml:"mountLocation,omitempty" protobuf:"11"` PartitionIndex int `yaml:"partitionIndex,omitempty" protobuf:"8"` // ParentLocation (if present) is the location of the parent block device for partitions. ParentLocation string `yaml:"parentLocation,omitempty" protobuf:"7"` UUID string `yaml:"uuid,omitempty" protobuf:"4"` PartitionUUID string `yaml:"partitionUUID,omitempty" protobuf:"5"` Size uint64 `yaml:"size,omitempty" protobuf:"9"` PrettySize string `yaml:"prettySize,omitempty" protobuf:"13"` // Filesystem is the filesystem type. Filesystem FilesystemType `yaml:"filesystem,omitempty" protobuf:"10"` // EncryptionProvider is the provider of the encryption which was used to unlock the volume. EncryptionProvider EncryptionProviderType `yaml:"encryptionProvider,omitempty" protobuf:"12"` // EncryptionFailedSyncs is the list of failed syncs for the volume (per key/provider)/ EncryptionFailedSyncs []string `yaml:"encryptionFailedSyncs,omitempty" protobuf:"14"` // ConfiguredEncryptionKeys is the list of configured encryption keys for the volume. ConfiguredEncryptionKeys []string `yaml:"configuredEncryptionKeys,omitempty" protobuf:"17"` // EncryptionLockedToState indicates if the encryption is locked to STATE partition EncryptionLockedToState bool `yaml:"encryptionLockedToState,omitempty" protobuf:"20"` // EncryptionSlot indicates the currently used encryption slot used for decryption. EncryptionSlot *int `yaml:"encryptionSlot,omitempty" protobuf:"21"` // TPMEncryptionOptions is the options for TPM-based encryption. TPMEncryptionOptions TPMEncryptionOptionsInfo `yaml:"tpmEncryptionOptions,omitempty" protobuf:"22"` // MountSpec is the mount specification. MountSpec MountSpec `yaml:"mountSpec,omitempty" protobuf:"15"` // Symlink is the symlink specification. SymlinkSpec SymlinkProvisioningSpec `yaml:"symlink,omitempty" protobuf:"18"` ErrorMessage string `yaml:"errorMessage,omitempty" protobuf:"3"` } // TPMEncryptionOptionsInfo is the options for TPM-based encryption. // //gotagsrewrite:gen type TPMEncryptionOptionsInfo struct { PCRs []int `yaml:"pcrs,omitempty" protobuf:"1"` PubKeyPCRs []int `yaml:"pubKeyPcrs,omitempty" protobuf:"2"` } // SetSize sets the size of the volume status, including the pretty size. func (s *VolumeStatusSpec) SetSize(size uint64) { s.Size = size s.PrettySize = humanize.Bytes(size) } // NewVolumeStatus initializes a BlockVolumeStatus resource. func NewVolumeStatus(namespace resource.Namespace, id resource.ID) *VolumeStatus { return typed.NewResource[VolumeStatusSpec, VolumeStatusExtension]( resource.NewMetadata(namespace, VolumeStatusType, id, resource.VersionUndefined), VolumeStatusSpec{}, ) } // VolumeStatusExtension is auxiliary resource data for BlockVolumeStatus. type VolumeStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (VolumeStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VolumeStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Type", JSONPath: `{.type}`, }, { Name: "Phase", JSONPath: `{.phase}`, }, { Name: "Location", JSONPath: `{.location}`, }, { Name: "Size", JSONPath: `{.prettySize}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VolumeStatusType, &VolumeStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/block/volumephase.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // VolumePhase describes volume phase. type VolumePhase int // Volume phases. // //structprotogen:gen_enum const ( VolumePhaseWaiting VolumePhase = iota // waiting VolumePhaseFailed // failed VolumePhaseMissing // missing VolumePhaseLocated // located VolumePhaseProvisioned // provisioned VolumePhasePrepared // prepared VolumePhaseReady // ready VolumePhaseClosed // closed ) ================================================ FILE: pkg/machinery/resources/block/volumetype.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block // VolumeType describes volume type. type VolumeType int // Volume types. // //structprotogen:gen_enum const ( VolumeTypePartition VolumeType = iota // partition VolumeTypeDisk // disk VolumeTypeTmpfs // tmpfs VolumeTypeDirectory // directory VolumeTypeSymlink // symlink VolumeTypeOverlay // overlay VolumeTypeExternal // external ) ================================================ FILE: pkg/machinery/resources/block/volumetype_enumer.go ================================================ // Code generated by "enumer -type=VolumeType,VolumePhase,FilesystemType,EncryptionKeyType,EncryptionProviderType,FSParameterType -linecomment -text"; DO NOT EDIT. package block import ( "fmt" "strings" ) const _VolumeTypeName = "partitiondisktmpfsdirectorysymlinkoverlayexternal" var _VolumeTypeIndex = [...]uint8{0, 9, 13, 18, 27, 34, 41, 49} const _VolumeTypeLowerName = "partitiondisktmpfsdirectorysymlinkoverlayexternal" func (i VolumeType) String() string { if i < 0 || i >= VolumeType(len(_VolumeTypeIndex)-1) { return fmt.Sprintf("VolumeType(%d)", i) } return _VolumeTypeName[_VolumeTypeIndex[i]:_VolumeTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _VolumeTypeNoOp() { var x [1]struct{} _ = x[VolumeTypePartition-(0)] _ = x[VolumeTypeDisk-(1)] _ = x[VolumeTypeTmpfs-(2)] _ = x[VolumeTypeDirectory-(3)] _ = x[VolumeTypeSymlink-(4)] _ = x[VolumeTypeOverlay-(5)] _ = x[VolumeTypeExternal-(6)] } var _VolumeTypeValues = []VolumeType{VolumeTypePartition, VolumeTypeDisk, VolumeTypeTmpfs, VolumeTypeDirectory, VolumeTypeSymlink, VolumeTypeOverlay, VolumeTypeExternal} var _VolumeTypeNameToValueMap = map[string]VolumeType{ _VolumeTypeName[0:9]: VolumeTypePartition, _VolumeTypeLowerName[0:9]: VolumeTypePartition, _VolumeTypeName[9:13]: VolumeTypeDisk, _VolumeTypeLowerName[9:13]: VolumeTypeDisk, _VolumeTypeName[13:18]: VolumeTypeTmpfs, _VolumeTypeLowerName[13:18]: VolumeTypeTmpfs, _VolumeTypeName[18:27]: VolumeTypeDirectory, _VolumeTypeLowerName[18:27]: VolumeTypeDirectory, _VolumeTypeName[27:34]: VolumeTypeSymlink, _VolumeTypeLowerName[27:34]: VolumeTypeSymlink, _VolumeTypeName[34:41]: VolumeTypeOverlay, _VolumeTypeLowerName[34:41]: VolumeTypeOverlay, _VolumeTypeName[41:49]: VolumeTypeExternal, _VolumeTypeLowerName[41:49]: VolumeTypeExternal, } var _VolumeTypeNames = []string{ _VolumeTypeName[0:9], _VolumeTypeName[9:13], _VolumeTypeName[13:18], _VolumeTypeName[18:27], _VolumeTypeName[27:34], _VolumeTypeName[34:41], _VolumeTypeName[41:49], } // VolumeTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func VolumeTypeString(s string) (VolumeType, error) { if val, ok := _VolumeTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _VolumeTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to VolumeType values", s) } // VolumeTypeValues returns all values of the enum func VolumeTypeValues() []VolumeType { return _VolumeTypeValues } // VolumeTypeStrings returns a slice of all String values of the enum func VolumeTypeStrings() []string { strs := make([]string, len(_VolumeTypeNames)) copy(strs, _VolumeTypeNames) return strs } // IsAVolumeType returns "true" if the value is listed in the enum definition. "false" otherwise func (i VolumeType) IsAVolumeType() bool { for _, v := range _VolumeTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for VolumeType func (i VolumeType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for VolumeType func (i *VolumeType) UnmarshalText(text []byte) error { var err error *i, err = VolumeTypeString(string(text)) return err } const _VolumePhaseName = "waitingfailedmissinglocatedprovisionedpreparedreadyclosed" var _VolumePhaseIndex = [...]uint8{0, 7, 13, 20, 27, 38, 46, 51, 57} const _VolumePhaseLowerName = "waitingfailedmissinglocatedprovisionedpreparedreadyclosed" func (i VolumePhase) String() string { if i < 0 || i >= VolumePhase(len(_VolumePhaseIndex)-1) { return fmt.Sprintf("VolumePhase(%d)", i) } return _VolumePhaseName[_VolumePhaseIndex[i]:_VolumePhaseIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _VolumePhaseNoOp() { var x [1]struct{} _ = x[VolumePhaseWaiting-(0)] _ = x[VolumePhaseFailed-(1)] _ = x[VolumePhaseMissing-(2)] _ = x[VolumePhaseLocated-(3)] _ = x[VolumePhaseProvisioned-(4)] _ = x[VolumePhasePrepared-(5)] _ = x[VolumePhaseReady-(6)] _ = x[VolumePhaseClosed-(7)] } var _VolumePhaseValues = []VolumePhase{VolumePhaseWaiting, VolumePhaseFailed, VolumePhaseMissing, VolumePhaseLocated, VolumePhaseProvisioned, VolumePhasePrepared, VolumePhaseReady, VolumePhaseClosed} var _VolumePhaseNameToValueMap = map[string]VolumePhase{ _VolumePhaseName[0:7]: VolumePhaseWaiting, _VolumePhaseLowerName[0:7]: VolumePhaseWaiting, _VolumePhaseName[7:13]: VolumePhaseFailed, _VolumePhaseLowerName[7:13]: VolumePhaseFailed, _VolumePhaseName[13:20]: VolumePhaseMissing, _VolumePhaseLowerName[13:20]: VolumePhaseMissing, _VolumePhaseName[20:27]: VolumePhaseLocated, _VolumePhaseLowerName[20:27]: VolumePhaseLocated, _VolumePhaseName[27:38]: VolumePhaseProvisioned, _VolumePhaseLowerName[27:38]: VolumePhaseProvisioned, _VolumePhaseName[38:46]: VolumePhasePrepared, _VolumePhaseLowerName[38:46]: VolumePhasePrepared, _VolumePhaseName[46:51]: VolumePhaseReady, _VolumePhaseLowerName[46:51]: VolumePhaseReady, _VolumePhaseName[51:57]: VolumePhaseClosed, _VolumePhaseLowerName[51:57]: VolumePhaseClosed, } var _VolumePhaseNames = []string{ _VolumePhaseName[0:7], _VolumePhaseName[7:13], _VolumePhaseName[13:20], _VolumePhaseName[20:27], _VolumePhaseName[27:38], _VolumePhaseName[38:46], _VolumePhaseName[46:51], _VolumePhaseName[51:57], } // VolumePhaseString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func VolumePhaseString(s string) (VolumePhase, error) { if val, ok := _VolumePhaseNameToValueMap[s]; ok { return val, nil } if val, ok := _VolumePhaseNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to VolumePhase values", s) } // VolumePhaseValues returns all values of the enum func VolumePhaseValues() []VolumePhase { return _VolumePhaseValues } // VolumePhaseStrings returns a slice of all String values of the enum func VolumePhaseStrings() []string { strs := make([]string, len(_VolumePhaseNames)) copy(strs, _VolumePhaseNames) return strs } // IsAVolumePhase returns "true" if the value is listed in the enum definition. "false" otherwise func (i VolumePhase) IsAVolumePhase() bool { for _, v := range _VolumePhaseValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for VolumePhase func (i VolumePhase) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for VolumePhase func (i *VolumePhase) UnmarshalText(text []byte) error { var err error *i, err = VolumePhaseString(string(text)) return err } const _FilesystemTypeName = "nonexfsvfatext4iso9660swapivirtiofs" var _FilesystemTypeIndex = [...]uint8{0, 4, 7, 11, 15, 22, 27, 35} const _FilesystemTypeLowerName = "nonexfsvfatext4iso9660swapivirtiofs" func (i FilesystemType) String() string { if i < 0 || i >= FilesystemType(len(_FilesystemTypeIndex)-1) { return fmt.Sprintf("FilesystemType(%d)", i) } return _FilesystemTypeName[_FilesystemTypeIndex[i]:_FilesystemTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _FilesystemTypeNoOp() { var x [1]struct{} _ = x[FilesystemTypeNone-(0)] _ = x[FilesystemTypeXFS-(1)] _ = x[FilesystemTypeVFAT-(2)] _ = x[FilesystemTypeEXT4-(3)] _ = x[FilesystemTypeISO9660-(4)] _ = x[FilesystemTypeSwap-(5)] _ = x[FilesystemTypeVirtiofs-(6)] } var _FilesystemTypeValues = []FilesystemType{FilesystemTypeNone, FilesystemTypeXFS, FilesystemTypeVFAT, FilesystemTypeEXT4, FilesystemTypeISO9660, FilesystemTypeSwap, FilesystemTypeVirtiofs} var _FilesystemTypeNameToValueMap = map[string]FilesystemType{ _FilesystemTypeName[0:4]: FilesystemTypeNone, _FilesystemTypeLowerName[0:4]: FilesystemTypeNone, _FilesystemTypeName[4:7]: FilesystemTypeXFS, _FilesystemTypeLowerName[4:7]: FilesystemTypeXFS, _FilesystemTypeName[7:11]: FilesystemTypeVFAT, _FilesystemTypeLowerName[7:11]: FilesystemTypeVFAT, _FilesystemTypeName[11:15]: FilesystemTypeEXT4, _FilesystemTypeLowerName[11:15]: FilesystemTypeEXT4, _FilesystemTypeName[15:22]: FilesystemTypeISO9660, _FilesystemTypeLowerName[15:22]: FilesystemTypeISO9660, _FilesystemTypeName[22:27]: FilesystemTypeSwap, _FilesystemTypeLowerName[22:27]: FilesystemTypeSwap, _FilesystemTypeName[27:35]: FilesystemTypeVirtiofs, _FilesystemTypeLowerName[27:35]: FilesystemTypeVirtiofs, } var _FilesystemTypeNames = []string{ _FilesystemTypeName[0:4], _FilesystemTypeName[4:7], _FilesystemTypeName[7:11], _FilesystemTypeName[11:15], _FilesystemTypeName[15:22], _FilesystemTypeName[22:27], _FilesystemTypeName[27:35], } // FilesystemTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func FilesystemTypeString(s string) (FilesystemType, error) { if val, ok := _FilesystemTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _FilesystemTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to FilesystemType values", s) } // FilesystemTypeValues returns all values of the enum func FilesystemTypeValues() []FilesystemType { return _FilesystemTypeValues } // FilesystemTypeStrings returns a slice of all String values of the enum func FilesystemTypeStrings() []string { strs := make([]string, len(_FilesystemTypeNames)) copy(strs, _FilesystemTypeNames) return strs } // IsAFilesystemType returns "true" if the value is listed in the enum definition. "false" otherwise func (i FilesystemType) IsAFilesystemType() bool { for _, v := range _FilesystemTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for FilesystemType func (i FilesystemType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for FilesystemType func (i *FilesystemType) UnmarshalText(text []byte) error { var err error *i, err = FilesystemTypeString(string(text)) return err } const _EncryptionKeyTypeName = "staticnodeIDkmstpm" var _EncryptionKeyTypeIndex = [...]uint8{0, 6, 12, 15, 18} const _EncryptionKeyTypeLowerName = "staticnodeidkmstpm" func (i EncryptionKeyType) String() string { if i < 0 || i >= EncryptionKeyType(len(_EncryptionKeyTypeIndex)-1) { return fmt.Sprintf("EncryptionKeyType(%d)", i) } return _EncryptionKeyTypeName[_EncryptionKeyTypeIndex[i]:_EncryptionKeyTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _EncryptionKeyTypeNoOp() { var x [1]struct{} _ = x[EncryptionKeyStatic-(0)] _ = x[EncryptionKeyNodeID-(1)] _ = x[EncryptionKeyKMS-(2)] _ = x[EncryptionKeyTPM-(3)] } var _EncryptionKeyTypeValues = []EncryptionKeyType{EncryptionKeyStatic, EncryptionKeyNodeID, EncryptionKeyKMS, EncryptionKeyTPM} var _EncryptionKeyTypeNameToValueMap = map[string]EncryptionKeyType{ _EncryptionKeyTypeName[0:6]: EncryptionKeyStatic, _EncryptionKeyTypeLowerName[0:6]: EncryptionKeyStatic, _EncryptionKeyTypeName[6:12]: EncryptionKeyNodeID, _EncryptionKeyTypeLowerName[6:12]: EncryptionKeyNodeID, _EncryptionKeyTypeName[12:15]: EncryptionKeyKMS, _EncryptionKeyTypeLowerName[12:15]: EncryptionKeyKMS, _EncryptionKeyTypeName[15:18]: EncryptionKeyTPM, _EncryptionKeyTypeLowerName[15:18]: EncryptionKeyTPM, } var _EncryptionKeyTypeNames = []string{ _EncryptionKeyTypeName[0:6], _EncryptionKeyTypeName[6:12], _EncryptionKeyTypeName[12:15], _EncryptionKeyTypeName[15:18], } // EncryptionKeyTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func EncryptionKeyTypeString(s string) (EncryptionKeyType, error) { if val, ok := _EncryptionKeyTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _EncryptionKeyTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to EncryptionKeyType values", s) } // EncryptionKeyTypeValues returns all values of the enum func EncryptionKeyTypeValues() []EncryptionKeyType { return _EncryptionKeyTypeValues } // EncryptionKeyTypeStrings returns a slice of all String values of the enum func EncryptionKeyTypeStrings() []string { strs := make([]string, len(_EncryptionKeyTypeNames)) copy(strs, _EncryptionKeyTypeNames) return strs } // IsAEncryptionKeyType returns "true" if the value is listed in the enum definition. "false" otherwise func (i EncryptionKeyType) IsAEncryptionKeyType() bool { for _, v := range _EncryptionKeyTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for EncryptionKeyType func (i EncryptionKeyType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for EncryptionKeyType func (i *EncryptionKeyType) UnmarshalText(text []byte) error { var err error *i, err = EncryptionKeyTypeString(string(text)) return err } const _EncryptionProviderTypeName = "noneluks2" var _EncryptionProviderTypeIndex = [...]uint8{0, 4, 9} const _EncryptionProviderTypeLowerName = "noneluks2" func (i EncryptionProviderType) String() string { if i < 0 || i >= EncryptionProviderType(len(_EncryptionProviderTypeIndex)-1) { return fmt.Sprintf("EncryptionProviderType(%d)", i) } return _EncryptionProviderTypeName[_EncryptionProviderTypeIndex[i]:_EncryptionProviderTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _EncryptionProviderTypeNoOp() { var x [1]struct{} _ = x[EncryptionProviderNone-(0)] _ = x[EncryptionProviderLUKS2-(1)] } var _EncryptionProviderTypeValues = []EncryptionProviderType{EncryptionProviderNone, EncryptionProviderLUKS2} var _EncryptionProviderTypeNameToValueMap = map[string]EncryptionProviderType{ _EncryptionProviderTypeName[0:4]: EncryptionProviderNone, _EncryptionProviderTypeLowerName[0:4]: EncryptionProviderNone, _EncryptionProviderTypeName[4:9]: EncryptionProviderLUKS2, _EncryptionProviderTypeLowerName[4:9]: EncryptionProviderLUKS2, } var _EncryptionProviderTypeNames = []string{ _EncryptionProviderTypeName[0:4], _EncryptionProviderTypeName[4:9], } // EncryptionProviderTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func EncryptionProviderTypeString(s string) (EncryptionProviderType, error) { if val, ok := _EncryptionProviderTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _EncryptionProviderTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to EncryptionProviderType values", s) } // EncryptionProviderTypeValues returns all values of the enum func EncryptionProviderTypeValues() []EncryptionProviderType { return _EncryptionProviderTypeValues } // EncryptionProviderTypeStrings returns a slice of all String values of the enum func EncryptionProviderTypeStrings() []string { strs := make([]string, len(_EncryptionProviderTypeNames)) copy(strs, _EncryptionProviderTypeNames) return strs } // IsAEncryptionProviderType returns "true" if the value is listed in the enum definition. "false" otherwise func (i EncryptionProviderType) IsAEncryptionProviderType() bool { for _, v := range _EncryptionProviderTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for EncryptionProviderType func (i EncryptionProviderType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for EncryptionProviderType func (i *EncryptionProviderType) UnmarshalText(text []byte) error { var err error *i, err = EncryptionProviderTypeString(string(text)) return err } const _FSParameterTypeName = "stringbooleanbinary" var _FSParameterTypeIndex = [...]uint8{0, 6, 13, 19} const _FSParameterTypeLowerName = "stringbooleanbinary" func (i FSParameterType) String() string { if i < 0 || i >= FSParameterType(len(_FSParameterTypeIndex)-1) { return fmt.Sprintf("FSParameterType(%d)", i) } return _FSParameterTypeName[_FSParameterTypeIndex[i]:_FSParameterTypeIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _FSParameterTypeNoOp() { var x [1]struct{} _ = x[FSParameterTypeStringValue-(0)] _ = x[FSParameterTypeBooleanValue-(1)] _ = x[FSParameterTypeBinaryValue-(2)] } var _FSParameterTypeValues = []FSParameterType{FSParameterTypeStringValue, FSParameterTypeBooleanValue, FSParameterTypeBinaryValue} var _FSParameterTypeNameToValueMap = map[string]FSParameterType{ _FSParameterTypeName[0:6]: FSParameterTypeStringValue, _FSParameterTypeLowerName[0:6]: FSParameterTypeStringValue, _FSParameterTypeName[6:13]: FSParameterTypeBooleanValue, _FSParameterTypeLowerName[6:13]: FSParameterTypeBooleanValue, _FSParameterTypeName[13:19]: FSParameterTypeBinaryValue, _FSParameterTypeLowerName[13:19]: FSParameterTypeBinaryValue, } var _FSParameterTypeNames = []string{ _FSParameterTypeName[0:6], _FSParameterTypeName[6:13], _FSParameterTypeName[13:19], } // FSParameterTypeString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func FSParameterTypeString(s string) (FSParameterType, error) { if val, ok := _FSParameterTypeNameToValueMap[s]; ok { return val, nil } if val, ok := _FSParameterTypeNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to FSParameterType values", s) } // FSParameterTypeValues returns all values of the enum func FSParameterTypeValues() []FSParameterType { return _FSParameterTypeValues } // FSParameterTypeStrings returns a slice of all String values of the enum func FSParameterTypeStrings() []string { strs := make([]string, len(_FSParameterTypeNames)) copy(strs, _FSParameterTypeNames) return strs } // IsAFSParameterType returns "true" if the value is listed in the enum definition. "false" otherwise func (i FSParameterType) IsAFSParameterType() bool { for _, v := range _FSParameterTypeValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for FSParameterType func (i FSParameterType) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for FSParameterType func (i *FSParameterType) UnmarshalText(text []byte) error { var err error *i, err = FSParameterTypeString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/block/zswap_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package block import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ZswapStatusType is type of ZswapStatus resource. const ZswapStatusType = resource.Type("ZswapStatuses.block.talos.dev") // ZswapStatus resource holds status of zwap subsystem. type ZswapStatus = typed.Resource[ZswapStatusSpec, ZswapStatusExtension] // ZswapStatusID is the ID of the singleton ZswapStatus resource. const ZswapStatusID resource.ID = "zswap" // ZswapStatusSpec is the spec for ZswapStatus resource. // //gotagsrewrite:gen type ZswapStatusSpec struct { TotalSizeBytes uint64 `yaml:"totalSize" protobuf:"1"` TotalSizeHuman string `yaml:"totalSizeHuman" protobuf:"2"` StoredPages uint64 `yaml:"storedPages" protobuf:"3"` PoolLimitHit uint64 `yaml:"poolLimitHit" protobuf:"4"` RejectReclaimFail uint64 `yaml:"rejectReclaimFail" protobuf:"5"` RejectAllocFail uint64 `yaml:"rejectAllocFail" protobuf:"6"` RejectKmemcacheFail uint64 `yaml:"rejectKmemcacheFail" protobuf:"7"` RejectCompressFail uint64 `yaml:"rejectCompressFail" protobuf:"8"` RejectCompressPoor uint64 `yaml:"rejectCompressPoor" protobuf:"9"` WrittenBackPages uint64 `yaml:"writtenBackPages" protobuf:"10"` } // NewZswapStatus initializes a ZswapStatus resource. func NewZswapStatus(namespace resource.Namespace, id resource.ID) *ZswapStatus { return typed.NewResource[ZswapStatusSpec, ZswapStatusExtension]( resource.NewMetadata(namespace, ZswapStatusType, id, resource.VersionUndefined), ZswapStatusSpec{}, ) } // ZswapStatusExtension is auxiliary resource data for ZswapStatus. type ZswapStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ZswapStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ZswapStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Total Size", JSONPath: "{.totalSizeHuman}", }, { Name: "Stored Pages", JSONPath: "{.storedPages}", }, { Name: "Written Back", JSONPath: "{.writtenBackPages}", }, { Name: "Pool Limit Hit", JSONPath: "{.poolLimitHit}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(ZswapStatusType, &ZswapStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cluster/affiliate.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "net/netip" "slices" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/gen/value" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/proto" ) //go:generate go tool github.com/siderolabs/deep-copy -type AffiliateSpec -type ConfigSpec -type IdentitySpec -type MemberSpec -type InfoSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // AffiliateType is type of Affiliate resource. const AffiliateType = resource.Type("Affiliates.cluster.talos.dev") // Affiliate resource holds information about cluster affiliate: it is discovered potential cluster member and/or KubeSpan peer. // // Controller builds local Affiliate structure for the node itself, other Affiliates are pulled from the registry during the discovery process. type Affiliate = typed.Resource[AffiliateSpec, AffiliateExtension] // KubeSpanAffiliateSpec describes additional information specific for the KubeSpan. // //gotagsrewrite:gen type KubeSpanAffiliateSpec struct { PublicKey string `yaml:"publicKey" protobuf:"1"` Address netip.Addr `yaml:"address" protobuf:"2"` AdditionalAddresses []netip.Prefix `yaml:"additionalAddresses" protobuf:"3"` Endpoints []netip.AddrPort `yaml:"endpoints" protobuf:"4"` ExcludeAdvertisedNetworks []netip.Prefix `yaml:"excludeAdvertisedNetworks" protobuf:"5"` } // NewAffiliate initializes the Affiliate resource. func NewAffiliate(namespace resource.Namespace, id resource.ID) *Affiliate { return typed.NewResource[AffiliateSpec, AffiliateExtension]( resource.NewMetadata(namespace, AffiliateType, id, resource.VersionUndefined), AffiliateSpec{}, ) } // AffiliateExtension provides auxiliary methods for Affiliate. type AffiliateExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (r AffiliateExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AffiliateType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Hostname", JSONPath: `{.hostname}`, }, { Name: "Machine Type", JSONPath: `{.machineType}`, }, { Name: "Addresses", JSONPath: `{.addresses}`, }, }, } } // AffiliateSpec describes Affiliate state. // //gotagsrewrite:gen type AffiliateSpec struct { NodeID string `yaml:"nodeId" protobuf:"1"` Addresses []netip.Addr `yaml:"addresses" protobuf:"2"` Hostname string `yaml:"hostname" protobuf:"3"` Nodename string `yaml:"nodename,omitempty" protobuf:"4"` OperatingSystem string `yaml:"operatingSystem" protobuf:"5"` MachineType machine.Type `yaml:"machineType" protobuf:"6"` KubeSpan KubeSpanAffiliateSpec `yaml:"kubespan,omitempty" protobuf:"7"` ControlPlane *ControlPlane `yaml:"controlPlane,omitempty" protobuf:"8"` } // ControlPlane describes ControlPlane data if any. // //gotagsrewrite:gen type ControlPlane struct { APIServerPort int `yaml:"port" protobuf:"1"` } // Merge two AffiliateSpecs. // //nolint:gocyclo func (spec *AffiliateSpec) Merge(other *AffiliateSpec) { for _, addr := range other.Addresses { found := slices.Contains(spec.Addresses, addr) if !found { spec.Addresses = append(spec.Addresses, addr) } } if other.ControlPlane != nil { spec.ControlPlane = other.ControlPlane } if other.Hostname != "" { spec.Hostname = other.Hostname } if other.Nodename != "" { spec.Nodename = other.Nodename } if other.MachineType != machine.TypeUnknown { spec.MachineType = other.MachineType } if other.KubeSpan.PublicKey != "" { spec.KubeSpan.PublicKey = other.KubeSpan.PublicKey } if !value.IsZero(other.KubeSpan.Address) { spec.KubeSpan.Address = other.KubeSpan.Address } for _, addr := range other.KubeSpan.AdditionalAddresses { found := slices.Contains(spec.KubeSpan.AdditionalAddresses, addr) if !found { spec.KubeSpan.AdditionalAddresses = append(spec.KubeSpan.AdditionalAddresses, addr) } } for _, filter := range other.KubeSpan.ExcludeAdvertisedNetworks { found := slices.Contains(spec.KubeSpan.ExcludeAdvertisedNetworks, filter) if !found { spec.KubeSpan.ExcludeAdvertisedNetworks = append(spec.KubeSpan.ExcludeAdvertisedNetworks, filter) } } for _, addr := range other.KubeSpan.Endpoints { found := slices.Contains(spec.KubeSpan.Endpoints, addr) if !found { spec.KubeSpan.Endpoints = append(spec.KubeSpan.Endpoints, addr) } } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AffiliateSpec](AffiliateType, &Affiliate{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cluster/affiliate_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "net/netip" "testing" "github.com/siderolabs/protoenc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" cluster2 "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func TestAffiliateSpec_Merge(t *testing.T) { for _, tt := range []struct { name string a, b, expected cluster.AffiliateSpec }{ { name: "zero", }, { name: "merge kubespan", a: cluster.AffiliateSpec{ Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")}, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, }, b: cluster.AffiliateSpec{ Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("2007::/64")}, }, }, expected: cluster.AffiliateSpec{ Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("2007::/64")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, }, }, { name: "merge mixed", a: cluster.AffiliateSpec{ Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), Endpoints: []netip.AddrPort{netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("2007::/64")}, }, }, b: cluster.AffiliateSpec{ Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("10.0.0.2:51820"), netip.MustParseAddrPort("192.168.3.4:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("2007::/64")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, }, expected: cluster.AffiliateSpec{ Hostname: "foo.com", Nodename: "bar", MachineType: machine.TypeControlPlane, Addresses: []netip.Addr{netip.MustParseAddr("10.0.0.2"), netip.MustParseAddr("192.168.3.4")}, KubeSpan: cluster.KubeSpanAffiliateSpec{ PublicKey: "PLPNBddmTgHJhtw0vxltq1ZBdPP9RNOEUd5JjJZzBRY=", Address: netip.MustParseAddr("fd50:8d60:4238:6302:f857:23ff:fe21:d1e0"), AdditionalAddresses: []netip.Prefix{netip.MustParsePrefix("10.244.3.1/24")}, Endpoints: []netip.AddrPort{netip.MustParseAddrPort("192.168.3.4:51820"), netip.MustParseAddrPort("10.0.0.2:51820")}, ExcludeAdvertisedNetworks: []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("2007::/64")}, }, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, }, }, } { t.Run(tt.name, func(t *testing.T) { spec := tt.a spec.Merge(&tt.b) assert.Equal(t, tt.expected, spec) }) } } func TestAffiliateSpecMarshal(t *testing.T) { original := &cluster.AffiliateSpec{ NodeID: "myNodeID", Hostname: "foo.com", MachineType: machine.TypeControlPlane, ControlPlane: &cluster.ControlPlane{APIServerPort: 6443}, } wire, err := protoenc.Marshal(original) require.NoError(t, err) var unmarshaled cluster2.AffiliateSpec err = proto.Unmarshal(wire, &unmarshaled) require.NoError(t, err) require.EqualValues(t, original.NodeID, unmarshaled.NodeId) require.EqualValues(t, original.Hostname, unmarshaled.Hostname) require.EqualValues(t, original.MachineType, unmarshaled.MachineType) require.EqualValues(t, original.ControlPlane.APIServerPort, unmarshaled.ControlPlane.ApiServerPort) unmarshaled.ControlPlane = nil wire, err = proto.Marshal(&unmarshaled) require.NoError(t, err) spec := &cluster.AffiliateSpec{} err = protoenc.Unmarshal(wire, spec) require.NoError(t, err) original.ControlPlane = nil require.Equal(t, original, spec) } ================================================ FILE: pkg/machinery/resources/cluster/cluster.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains resources related to cluster as a whole. const NamespaceName resource.Namespace = "cluster" // RawNamespaceName contains raw resources which haven't gone through the merge phase yet. const RawNamespaceName resource.Namespace = "cluster-raw" ================================================ FILE: pkg/machinery/resources/cluster/cluster_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/cluster" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &cluster.Affiliate{}, &cluster.Config{}, &cluster.Identity{}, &cluster.Member{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/cluster/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // ConfigType is type of Config resource. const ConfigType = resource.Type("DiscoveryConfigs.cluster.talos.dev") // ConfigID the singleton config resource ID. const ConfigID = resource.ID("cluster") // Config resource holds KubeSpan configuration. type Config = typed.Resource[ConfigSpec, ConfigExtension] // ConfigSpec describes KubeSpan configuration. // //gotagsrewrite:gen type ConfigSpec struct { DiscoveryEnabled bool `yaml:"discoveryEnabled" protobuf:"1"` RegistryKubernetesEnabled bool `yaml:"registryKubernetesEnabled" protobuf:"2"` RegistryServiceEnabled bool `yaml:"registryServiceEnabled" protobuf:"3"` ServiceEndpoint string `yaml:"serviceEndpoint" protobuf:"4"` ServiceEndpointInsecure bool `yaml:"serviceEndpointInsecure,omitempty" protobuf:"5"` ServiceEncryptionKey []byte `yaml:"serviceEncryptionKey" protobuf:"6"` ServiceClusterID string `yaml:"serviceClusterID" protobuf:"7"` } // NewConfig initializes a Config resource. func NewConfig(namespace resource.Namespace, id resource.ID) *Config { return typed.NewResource[ConfigSpec, ConfigExtension]( resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined), ConfigSpec{}, ) } // ConfigExtension provides auxiliary methods for Config. type ConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigSpec](ConfigType, &Config{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cluster/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type AffiliateSpec -type ConfigSpec -type IdentitySpec -type MemberSpec -type InfoSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package cluster import ( "net/netip" ) // DeepCopy generates a deep copy of AffiliateSpec. func (o AffiliateSpec) DeepCopy() AffiliateSpec { var cp AffiliateSpec = o if o.Addresses != nil { cp.Addresses = make([]netip.Addr, len(o.Addresses)) copy(cp.Addresses, o.Addresses) } if o.KubeSpan.AdditionalAddresses != nil { cp.KubeSpan.AdditionalAddresses = make([]netip.Prefix, len(o.KubeSpan.AdditionalAddresses)) copy(cp.KubeSpan.AdditionalAddresses, o.KubeSpan.AdditionalAddresses) } if o.KubeSpan.Endpoints != nil { cp.KubeSpan.Endpoints = make([]netip.AddrPort, len(o.KubeSpan.Endpoints)) copy(cp.KubeSpan.Endpoints, o.KubeSpan.Endpoints) } if o.KubeSpan.ExcludeAdvertisedNetworks != nil { cp.KubeSpan.ExcludeAdvertisedNetworks = make([]netip.Prefix, len(o.KubeSpan.ExcludeAdvertisedNetworks)) copy(cp.KubeSpan.ExcludeAdvertisedNetworks, o.KubeSpan.ExcludeAdvertisedNetworks) } if o.ControlPlane != nil { cp.ControlPlane = new(ControlPlane) *cp.ControlPlane = *o.ControlPlane } return cp } // DeepCopy generates a deep copy of ConfigSpec. func (o ConfigSpec) DeepCopy() ConfigSpec { var cp ConfigSpec = o if o.ServiceEncryptionKey != nil { cp.ServiceEncryptionKey = make([]byte, len(o.ServiceEncryptionKey)) copy(cp.ServiceEncryptionKey, o.ServiceEncryptionKey) } return cp } // DeepCopy generates a deep copy of IdentitySpec. func (o IdentitySpec) DeepCopy() IdentitySpec { var cp IdentitySpec = o return cp } // DeepCopy generates a deep copy of MemberSpec. func (o MemberSpec) DeepCopy() MemberSpec { var cp MemberSpec = o if o.Addresses != nil { cp.Addresses = make([]netip.Addr, len(o.Addresses)) copy(cp.Addresses, o.Addresses) } if o.ControlPlane != nil { cp.ControlPlane = new(ControlPlane) *cp.ControlPlane = *o.ControlPlane } return cp } // DeepCopy generates a deep copy of InfoSpec. func (o InfoSpec) DeepCopy() InfoSpec { var cp InfoSpec = o return cp } ================================================ FILE: pkg/machinery/resources/cluster/identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // IdentityType is type of Identity resource. const IdentityType = resource.Type("Identities.cluster.talos.dev") // LocalIdentity is the resource ID for the local node identity. const LocalIdentity = resource.ID("local") // Identity resource holds node identity (as a member of the cluster). type Identity = typed.Resource[IdentitySpec, IdentityExtension] // IdentitySpec describes status of rendered secrets. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. // //gotagsrewrite:gen type IdentitySpec struct { // NodeID is a random value which is persisted across reboots, // but it gets reset on wipe. NodeID string `yaml:"nodeId" protobuf:"1"` } // NewIdentity initializes a Identity resource. func NewIdentity(namespace resource.Namespace, id resource.ID) *Identity { return typed.NewResource[IdentitySpec, IdentityExtension]( resource.NewMetadata(namespace, IdentityType, id, resource.VersionUndefined), IdentitySpec{}, ) } // IdentityExtension provides auxiliary methods for Identity. type IdentityExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (IdentityExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: IdentityType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "ID", JSONPath: `{.nodeId}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[IdentitySpec](IdentityType, &Identity{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cluster/info.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // InfoType is type of Info resource. const InfoType = resource.Type("Infos.cluster.talos.dev") // InfoID is the resource ID for the current cluster info. const InfoID = resource.ID("current") // Info resource holds cluster information. type Info = typed.Resource[InfoSpec, InfoExtension] // InfoSpec describes cluster information. // //gotagsrewrite:gen type InfoSpec struct { ClusterID string `yaml:"clusterId" protobuf:"1"` ClusterName string `yaml:"clusterName" protobuf:"2"` } // NewInfo initializes an Info resource. func NewInfo() *Info { return typed.NewResource[InfoSpec, InfoExtension]( resource.NewMetadata(NamespaceName, InfoType, InfoID, resource.VersionUndefined), InfoSpec{}, ) } // InfoExtension provides auxiliary methods for Info. type InfoExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (InfoExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: InfoType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Cluster ID", JSONPath: `{.clusterId}`, }, { Name: "Cluster Name", JSONPath: `{.clusterName}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[InfoSpec](InfoType, &Info{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cluster/member.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cluster import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MemberType is type of Member resource. const MemberType = resource.Type("Members.cluster.talos.dev") // Member resource contains information about discovered cluster members. // // Members are usually derived from Affiliates. type Member = typed.Resource[MemberSpec, MemberExtension] // MemberSpec describes Member state. // //gotagsrewrite:gen type MemberSpec struct { NodeID string `yaml:"nodeId" protobuf:"1"` Addresses []netip.Addr `yaml:"addresses" protobuf:"2"` Hostname string `yaml:"hostname" protobuf:"3"` MachineType machine.Type `yaml:"machineType" protobuf:"4"` OperatingSystem string `yaml:"operatingSystem" protobuf:"5"` ControlPlane *ControlPlane `yaml:"controlPlane,omitempty" protobuf:"6"` } // NewMember initializes a Member resource. func NewMember(namespace resource.Namespace, id resource.ID) *Member { return typed.NewResource[MemberSpec, MemberExtension]( resource.NewMetadata(namespace, MemberType, id, resource.VersionUndefined), MemberSpec{}, ) } // MemberExtension provides auxiliary methods for Member. type MemberExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (MemberExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MemberType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Hostname", JSONPath: `{.hostname}`, }, { Name: "Machine Type", JSONPath: `{.machineType}`, }, { Name: "OS", JSONPath: `{.operatingSystem}`, }, { Name: "Addresses", JSONPath: `{.addresses}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MemberSpec](MemberType, &Member{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/config/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package config provides resources which hold Talos node configuration. package config import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains configuration resources. const NamespaceName resource.Namespace = "config" ================================================ FILE: pkg/machinery/resources/config/config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &config.MachineType{}, &config.MachineConfig{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/config/machine_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" configpb "github.com/siderolabs/talos/pkg/machinery/api/resource/config" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MachineConfigType is type of Service resource. const MachineConfigType = resource.Type("MachineConfigs.config.talos.dev") // ActiveID is the ID of active (applied to the running OS) machine configuration. const ActiveID = resource.ID("v1alpha1") // PersistentID is the ID of the config saved to the persistent storage. // // Note: PersistentID might be ahead of the "current" ID if the config was submitted in e.g. "staged" mode. // Note: PersistentID might be behind the "current" ID if the config was submitted in e.g. "try" mode. const PersistentID = resource.ID("persistent") // MachineConfig resource holds v1alpha Talos configuration. type MachineConfig struct { md resource.Metadata spec *v1alpha1Spec } type v1alpha1Spec struct { cfg config.Provider } // MarshalYAML implements yaml.Marshaler interface. // // Machine configuration in the spec is presented as raw YAML string. func (s *v1alpha1Spec) MarshalYAML() (any, error) { b, err := s.cfg.Bytes() if err != nil { return nil, err } return string(b), nil } // NewMachineConfig initializes a V1Alpha1 resource. func NewMachineConfig(spec config.Provider) *MachineConfig { return NewMachineConfigWithID(spec, ActiveID) } // NewMachineConfigWithID initializes a MachineConfig resource. func NewMachineConfigWithID(spec config.Provider, id resource.ID) *MachineConfig { r := &MachineConfig{ md: resource.NewMetadata(NamespaceName, MachineConfigType, id, resource.VersionUndefined), spec: &v1alpha1Spec{ cfg: spec, }, } // set the annotation to mark that the spec is marshaled as YAML string properly // the actual annotation value does not matter, as the key right now r.Metadata().Annotations().Set("talos.dev/yaml-spec", "1") return r } // Metadata implements resource.Resource. func (r *MachineConfig) Metadata() *resource.Metadata { return &r.md } // Spec implements resource.Resource. func (r *MachineConfig) Spec() any { return r.spec } // DeepCopy implements resource.Resource. func (r *MachineConfig) DeepCopy() resource.Resource { cfgCopy := r.spec.cfg if !cfgCopy.Readonly() { cfgCopy = cfgCopy.Clone() } return &MachineConfig{ md: r.md, spec: &v1alpha1Spec{ cfg: cfgCopy, }, } } // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (r *MachineConfig) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MachineConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } // MarshalProto implements ProtoMarshaler. func (s *v1alpha1Spec) MarshalProto() ([]byte, error) { yamlBytes, err := s.cfg.Bytes() if err != nil { return nil, err } protoSpec := configpb.MachineConfigSpec{ YamlMarshalled: yamlBytes, } return proto.Marshal(&protoSpec) } // UnmarshalProto implements protobuf.ResourceUnmarshaler. func (r *MachineConfig) UnmarshalProto(md *resource.Metadata, protoBytes []byte) error { protoSpec := configpb.MachineConfigSpec{} if err := proto.Unmarshal(protoBytes, &protoSpec); err != nil { return err } cfg, err := configloader.NewFromBytes(protoSpec.YamlMarshalled) if err != nil { return err } r.md = *md r.spec = &v1alpha1Spec{ cfg: cfg, } return nil } // Config returns config.Config to access config fields. func (r *MachineConfig) Config() config.Config { return r.spec.cfg } // Container returns config.Container to access config as a whole. func (r *MachineConfig) Container() config.Container { return r.spec.cfg } // Provider returns config.Provider to access config provider. // // This method should only be used when Container() and Config() methods are not enough. // Config()/Container() provides better semantic split on the config access. func (r *MachineConfig) Provider() config.Provider { return r.spec.cfg } func init() { if err := protobuf.RegisterResource(MachineConfigType, &MachineConfig{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/config/machine_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( _ "embed" "regexp" "testing" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/config/configloader" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) const sampleConfig = `version: v1alpha1 persist: true # foo debug: false machine: type: controlplane ` //go:embed testdata/machineconfig.yaml var machineConfigYAML string func TestMachineConfigMarshal(t *testing.T) { cfg, err := configloader.NewFromBytes([]byte(sampleConfig)) require.NoError(t, err) r := config.NewMachineConfig(cfg) m, err := resource.MarshalYAML(r) require.NoError(t, err) enc, err := yaml.Marshal(m) require.NoError(t, err) enc = regexp.MustCompile("(created|updated): [0-9-:TZ+]+").ReplaceAll(enc, []byte("$1: ")) assert.Equal(t, machineConfigYAML, string(enc)) } func TestMachineConfigProtobufMarshal(t *testing.T) { if _, offset := time.Now().Zone(); offset != 0 { t.Skipf("timezone offset is not zero: %d", offset) } cfg, err := configloader.NewFromBytes([]byte(sampleConfig)) require.NoError(t, err) r := config.NewMachineConfig(cfg) protoR, err := protobuf.FromResource(r) require.NoError(t, err) marshaled, err := protoR.Marshal() require.NoError(t, err) protoR, err = protobuf.Unmarshal(marshaled) require.NoError(t, err) r2, err := protobuf.UnmarshalResource(protoR) require.NoError(t, err) require.True(t, resource.Equal(r, r2)) m1, err := resource.MarshalYAML(r) require.NoError(t, err) yaml1, err := yaml.Marshal(m1) require.NoError(t, err) m2, err := resource.MarshalYAML(r2) require.NoError(t, err) yaml2, err := yaml.Marshal(m2) require.NoError(t, err) assert.Equal(t, string(yaml1), string(yaml2)) } ================================================ FILE: pkg/machinery/resources/config/machine_type.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" configpb "github.com/siderolabs/talos/pkg/machinery/api/resource/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MachineTypeType is type of MachineType resource. const MachineTypeType = resource.Type("MachineTypes.config.talos.dev") // MachineTypeID is singleton resource ID. const MachineTypeID = resource.ID("machine-type") // MachineType describes machine type. type MachineType struct { md resource.Metadata spec machineTypeSpec } type machineTypeSpec struct { machine.Type } func (spec machineTypeSpec) MarshalYAML() (any, error) { return spec.Type.String(), nil } // NewMachineType initializes a MachineType resource. func NewMachineType() *MachineType { r := &MachineType{ md: resource.NewMetadata(NamespaceName, MachineTypeType, MachineTypeID, resource.VersionUndefined), spec: machineTypeSpec{}, } return r } // Metadata implements resource.Resource. func (r *MachineType) Metadata() *resource.Metadata { return &r.md } // Spec implements resource.Resource. func (r *MachineType) Spec() any { return r.spec } // DeepCopy implements resource.Resource. func (r *MachineType) DeepCopy() resource.Resource { return &MachineType{ md: r.md, spec: r.spec, } } // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (r *MachineType) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MachineTypeType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Type", JSONPath: "{@}", }, }, } } // MachineType returns machine.Type. func (r *MachineType) MachineType() machine.Type { return r.spec.Type } // SetMachineType sets machine.Type. func (r *MachineType) SetMachineType(typ machine.Type) { r.spec.Type = typ } // MarshalProto implements ProtoMarshaler. func (spec machineTypeSpec) MarshalProto() ([]byte, error) { protoSpec := configpb.MachineTypeSpec{ MachineType: configpb.MachineType(spec.Type), } return proto.Marshal(&protoSpec) } // UnmarshalProto implements protobuf.ResourceUnmarshaler. func (r *MachineType) UnmarshalProto(md *resource.Metadata, protoBytes []byte) error { protoSpec := configpb.MachineTypeSpec{} if err := proto.Unmarshal(protoBytes, &protoSpec); err != nil { return err } r.md = *md r.spec = machineTypeSpec{ Type: machine.Type(protoSpec.MachineType), } return nil } func init() { if err := protobuf.RegisterResource(MachineTypeType, &MachineType{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/config/machine_type_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package config_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) func TestMachineTypeProtobufMarshal(t *testing.T) { r := config.NewMachineType() r.SetMachineType(machine.TypeWorker) protoR, err := protobuf.FromResource(r) require.NoError(t, err) marshaled, err := protoR.Marshal() require.NoError(t, err) protoR, err = protobuf.Unmarshal(marshaled) require.NoError(t, err) r2, err := protobuf.UnmarshalResource(protoR) require.NoError(t, err) require.True(t, resource.Equal(r, r2)) } ================================================ FILE: pkg/machinery/resources/cri/cri.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cri contains resources related to the Container Runtime Interface (CRI). package cri import ( "context" "crypto/tls" "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" config2 "github.com/siderolabs/talos/pkg/machinery/config/config" ) //go:generate go tool github.com/siderolabs/deep-copy -type RegistriesConfigSpec -type ImageCacheConfigSpec -type SeccompProfileSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . //go:generate go tool github.com/dmarkham/enumer -type=ImageCacheStatus -type=ImageCacheCopyStatus -linecomment -text // NamespaceName contains resources related to stats. const NamespaceName resource.Namespace = "cri" // RegistryTLSConfigExtended is an extended registry TLS config. type RegistryTLSConfigExtended interface { config2.RegistryTLSConfig GetTLSConfig() (*tls.Config, error) } // Registries is the interface for accessing container registries configuration. type Registries interface { Mirrors() map[string]config2.RegistryMirrorConfig Auths() map[string]config2.RegistryAuthConfig TLSs() map[string]RegistryTLSConfigExtended } // RegistryBuilder implements image.RegistriesBuilder. func RegistryBuilder(st state.State, opts ...func(*RegistriesConfigSpec)) func(ctx context.Context) (Registries, error) { return func(ctx context.Context) (Registries, error) { regs, err := safe.StateWatchFor[*RegistriesConfig](ctx, st, NewRegistriesConfig().Metadata(), state.WithEventTypes(state.Created, state.Updated)) if err != nil { return nil, err } spec := regs.TypedSpec() // mutate the spec with the provided options for _, opt := range opts { opt(spec) } return spec, nil } } // WaitForImageCache waits for the image cache config to be either disabled or ready. func WaitForImageCache(ctx context.Context, st state.State) error { _, err := st.WatchFor(ctx, NewImageCacheConfig().Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { imageCacheConfig, ok := r.(*ImageCacheConfig) if !ok { return false, fmt.Errorf("unexpected resource type: %T", r) } s := imageCacheConfig.TypedSpec().Status return s == ImageCacheStatusDisabled || s == ImageCacheStatusReady, nil }), ) return err } // WaitForImageCacheCopy waits for the image cache copy to be done (or skipped). func WaitForImageCacheCopy(ctx context.Context, st state.State) error { _, err := st.WatchFor(ctx, NewImageCacheConfig().Metadata(), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { imageCacheConfig, ok := r.(*ImageCacheConfig) if !ok { return false, fmt.Errorf("unexpected resource type: %T", r) } s := imageCacheConfig.TypedSpec().CopyStatus return s == ImageCacheCopyStatusReady || s == ImageCacheCopyStatusSkipped, nil }), ) return err } ================================================ FILE: pkg/machinery/resources/cri/cri_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/cri" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &cri.ImageCacheConfig{}, &cri.SeccompProfile{}, &cri.RegistriesConfig{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/cri/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type RegistriesConfigSpec -type ImageCacheConfigSpec -type SeccompProfileSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package cri // DeepCopy generates a deep copy of RegistriesConfigSpec. func (o RegistriesConfigSpec) DeepCopy() RegistriesConfigSpec { var cp RegistriesConfigSpec = o if o.RegistryMirrors != nil { cp.RegistryMirrors = make(map[string]*RegistryMirrorConfig, len(o.RegistryMirrors)) for k2, v2 := range o.RegistryMirrors { var cp_RegistryMirrors_v2 *RegistryMirrorConfig if v2 != nil { cp_RegistryMirrors_v2 = new(RegistryMirrorConfig) *cp_RegistryMirrors_v2 = *v2 if v2.MirrorEndpoints != nil { cp_RegistryMirrors_v2.MirrorEndpoints = make([]RegistryEndpointConfig, len(v2.MirrorEndpoints)) copy(cp_RegistryMirrors_v2.MirrorEndpoints, v2.MirrorEndpoints) } } cp.RegistryMirrors[k2] = cp_RegistryMirrors_v2 } } if o.RegistryAuths != nil { cp.RegistryAuths = make(map[string]*RegistryAuthConfig, len(o.RegistryAuths)) for k2, v2 := range o.RegistryAuths { var cp_RegistryAuths_v2 *RegistryAuthConfig if v2 != nil { cp_RegistryAuths_v2 = new(RegistryAuthConfig) *cp_RegistryAuths_v2 = *v2 } cp.RegistryAuths[k2] = cp_RegistryAuths_v2 } } if o.RegistryTLSs != nil { cp.RegistryTLSs = make(map[string]*RegistryTLSConfig, len(o.RegistryTLSs)) for k2, v2 := range o.RegistryTLSs { var cp_RegistryTLSs_v2 *RegistryTLSConfig if v2 != nil { cp_RegistryTLSs_v2 = new(RegistryTLSConfig) *cp_RegistryTLSs_v2 = *v2 if v2.TLSClientIdentity != nil { cp_RegistryTLSs_v2.TLSClientIdentity = v2.TLSClientIdentity.DeepCopy() } cp_RegistryTLSs_v2.TLSCA = v2.TLSCA.DeepCopy() } cp.RegistryTLSs[k2] = cp_RegistryTLSs_v2 } } return cp } // DeepCopy generates a deep copy of ImageCacheConfigSpec. func (o ImageCacheConfigSpec) DeepCopy() ImageCacheConfigSpec { var cp ImageCacheConfigSpec = o if o.Roots != nil { cp.Roots = make([]string, len(o.Roots)) copy(cp.Roots, o.Roots) } return cp } // DeepCopy generates a deep copy of SeccompProfileSpec. func (o SeccompProfileSpec) DeepCopy() SeccompProfileSpec { var cp SeccompProfileSpec = o if o.Value != nil { cp.Value = make(map[string]any, len(o.Value)) for k2, v2 := range o.Value { cp.Value[k2] = v2 } } return cp } ================================================ FILE: pkg/machinery/resources/cri/image_cache_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ImageCacheConfigType is type of ImageCacheConfig resource. const ImageCacheConfigType = resource.Type("ImageCacheConfigs.cri.talos.dev") // ImageCacheConfig represents ImageCacheConfig typed resource. type ImageCacheConfig = typed.Resource[ImageCacheConfigSpec, ImageCacheConfigExtension] // ImageCacheConfigID is the ID of the ImageCacheConfig resource. const ImageCacheConfigID = "image-cache" // ImageCacheConfigSpec represents the ImageCacheConfig. // //gotagsrewrite:gen type ImageCacheConfigSpec struct { Status ImageCacheStatus `yaml:"status" protobuf:"1"` CopyStatus ImageCacheCopyStatus `yaml:"copyStatus" protobuf:"3"` Roots []string `yaml:"roots" protobuf:"2"` } // NewImageCacheConfig creates new ImageCacheConfig object. func NewImageCacheConfig() *ImageCacheConfig { return typed.NewResource[ImageCacheConfigSpec, ImageCacheConfigExtension]( resource.NewMetadata(NamespaceName, ImageCacheConfigType, ImageCacheConfigID, resource.VersionUndefined), ImageCacheConfigSpec{}, ) } // ImageCacheConfigExtension is an auxiliary type for ImageCacheConfig resource. type ImageCacheConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ImageCacheConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ImageCacheConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Status", JSONPath: "{.status}", }, { Name: "CopyStatus", JSONPath: "{.copyStatus}", }, { Name: "Roots", JSONPath: "{.roots}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(ImageCacheConfigType, &ImageCacheConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cri/imagecachecopystatus_enumer.go ================================================ // Code generated by "enumer -type=ImageCacheStatus -type=ImageCacheCopyStatus -linecomment -text"; DO NOT EDIT. package cri import ( "fmt" "strings" ) const _ImageCacheCopyStatusName = "unknownskippedcopyingready" var _ImageCacheCopyStatusIndex = [...]uint8{0, 7, 14, 21, 26} const _ImageCacheCopyStatusLowerName = "unknownskippedcopyingready" func (i ImageCacheCopyStatus) String() string { if i < 0 || i >= ImageCacheCopyStatus(len(_ImageCacheCopyStatusIndex)-1) { return fmt.Sprintf("ImageCacheCopyStatus(%d)", i) } return _ImageCacheCopyStatusName[_ImageCacheCopyStatusIndex[i]:_ImageCacheCopyStatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ImageCacheCopyStatusNoOp() { var x [1]struct{} _ = x[ImageCacheCopyStatusUnknown-(0)] _ = x[ImageCacheCopyStatusSkipped-(1)] _ = x[ImageCacheCopyStatusPending-(2)] _ = x[ImageCacheCopyStatusReady-(3)] } var _ImageCacheCopyStatusValues = []ImageCacheCopyStatus{ImageCacheCopyStatusUnknown, ImageCacheCopyStatusSkipped, ImageCacheCopyStatusPending, ImageCacheCopyStatusReady} var _ImageCacheCopyStatusNameToValueMap = map[string]ImageCacheCopyStatus{ _ImageCacheCopyStatusName[0:7]: ImageCacheCopyStatusUnknown, _ImageCacheCopyStatusLowerName[0:7]: ImageCacheCopyStatusUnknown, _ImageCacheCopyStatusName[7:14]: ImageCacheCopyStatusSkipped, _ImageCacheCopyStatusLowerName[7:14]: ImageCacheCopyStatusSkipped, _ImageCacheCopyStatusName[14:21]: ImageCacheCopyStatusPending, _ImageCacheCopyStatusLowerName[14:21]: ImageCacheCopyStatusPending, _ImageCacheCopyStatusName[21:26]: ImageCacheCopyStatusReady, _ImageCacheCopyStatusLowerName[21:26]: ImageCacheCopyStatusReady, } var _ImageCacheCopyStatusNames = []string{ _ImageCacheCopyStatusName[0:7], _ImageCacheCopyStatusName[7:14], _ImageCacheCopyStatusName[14:21], _ImageCacheCopyStatusName[21:26], } // ImageCacheCopyStatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ImageCacheCopyStatusString(s string) (ImageCacheCopyStatus, error) { if val, ok := _ImageCacheCopyStatusNameToValueMap[s]; ok { return val, nil } if val, ok := _ImageCacheCopyStatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ImageCacheCopyStatus values", s) } // ImageCacheCopyStatusValues returns all values of the enum func ImageCacheCopyStatusValues() []ImageCacheCopyStatus { return _ImageCacheCopyStatusValues } // ImageCacheCopyStatusStrings returns a slice of all String values of the enum func ImageCacheCopyStatusStrings() []string { strs := make([]string, len(_ImageCacheCopyStatusNames)) copy(strs, _ImageCacheCopyStatusNames) return strs } // IsAImageCacheCopyStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i ImageCacheCopyStatus) IsAImageCacheCopyStatus() bool { for _, v := range _ImageCacheCopyStatusValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ImageCacheCopyStatus func (i ImageCacheCopyStatus) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ImageCacheCopyStatus func (i *ImageCacheCopyStatus) UnmarshalText(text []byte) error { var err error *i, err = ImageCacheCopyStatusString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/cri/imagecachestatus.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri // ImageCacheStatus describes image cache status type. type ImageCacheStatus int // ImageCacheStatus values. // //structprotogen:gen_enum const ( ImageCacheStatusUnknown ImageCacheStatus = iota // unknown ImageCacheStatusDisabled // disabled ImageCacheStatusPreparing // preparing ImageCacheStatusReady // ready ) // ImageCacheCopyStatus describes image cache copy status type. type ImageCacheCopyStatus int // ImageCacheCopyStatus values. // //structprotogen:gen_enum const ( ImageCacheCopyStatusUnknown ImageCacheCopyStatus = iota // unknown ImageCacheCopyStatusSkipped // skipped ImageCacheCopyStatusPending // copying ImageCacheCopyStatusReady // ready ) ================================================ FILE: pkg/machinery/resources/cri/imagecachestatus_enumer.go ================================================ // Code generated by "enumer -type=ImageCacheStatus -linecomment -text"; DO NOT EDIT. package cri import ( "fmt" "strings" ) const _ImageCacheStatusName = "unknowndisabledpreparingready" var _ImageCacheStatusIndex = [...]uint8{0, 7, 15, 24, 29} const _ImageCacheStatusLowerName = "unknowndisabledpreparingready" func (i ImageCacheStatus) String() string { if i < 0 || i >= ImageCacheStatus(len(_ImageCacheStatusIndex)-1) { return fmt.Sprintf("ImageCacheStatus(%d)", i) } return _ImageCacheStatusName[_ImageCacheStatusIndex[i]:_ImageCacheStatusIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ImageCacheStatusNoOp() { var x [1]struct{} _ = x[ImageCacheStatusUnknown-(0)] _ = x[ImageCacheStatusDisabled-(1)] _ = x[ImageCacheStatusPreparing-(2)] _ = x[ImageCacheStatusReady-(3)] } var _ImageCacheStatusValues = []ImageCacheStatus{ImageCacheStatusUnknown, ImageCacheStatusDisabled, ImageCacheStatusPreparing, ImageCacheStatusReady} var _ImageCacheStatusNameToValueMap = map[string]ImageCacheStatus{ _ImageCacheStatusName[0:7]: ImageCacheStatusUnknown, _ImageCacheStatusLowerName[0:7]: ImageCacheStatusUnknown, _ImageCacheStatusName[7:15]: ImageCacheStatusDisabled, _ImageCacheStatusLowerName[7:15]: ImageCacheStatusDisabled, _ImageCacheStatusName[15:24]: ImageCacheStatusPreparing, _ImageCacheStatusLowerName[15:24]: ImageCacheStatusPreparing, _ImageCacheStatusName[24:29]: ImageCacheStatusReady, _ImageCacheStatusLowerName[24:29]: ImageCacheStatusReady, } var _ImageCacheStatusNames = []string{ _ImageCacheStatusName[0:7], _ImageCacheStatusName[7:15], _ImageCacheStatusName[15:24], _ImageCacheStatusName[24:29], } // ImageCacheStatusString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ImageCacheStatusString(s string) (ImageCacheStatus, error) { if val, ok := _ImageCacheStatusNameToValueMap[s]; ok { return val, nil } if val, ok := _ImageCacheStatusNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ImageCacheStatus values", s) } // ImageCacheStatusValues returns all values of the enum func ImageCacheStatusValues() []ImageCacheStatus { return _ImageCacheStatusValues } // ImageCacheStatusStrings returns a slice of all String values of the enum func ImageCacheStatusStrings() []string { strs := make([]string, len(_ImageCacheStatusNames)) copy(strs, _ImageCacheStatusNames) return strs } // IsAImageCacheStatus returns "true" if the value is listed in the enum definition. "false" otherwise func (i ImageCacheStatus) IsAImageCacheStatus() bool { for _, v := range _ImageCacheStatusValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ImageCacheStatus func (i ImageCacheStatus) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ImageCacheStatus func (i *ImageCacheStatus) UnmarshalText(text []byte) error { var err error *i, err = ImageCacheStatusString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/cri/registries_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "crypto/tls" stdx509 "crypto/x509" "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/gen/xslices" config2 "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/proto" ) // RegistriesConfigType is type of RegistriesConfig resource. const RegistriesConfigType = resource.Type("RegistryConfigs.cri.talos.dev") // RegistriesConfigID is the singleton resource ID. const RegistriesConfigID resource.ID = "registries" // RegistriesConfig resource holds info about container registries. type RegistriesConfig = typed.Resource[RegistriesConfigSpec, RegistriesConfigExtension] // RegistriesConfigSpec describes status of rendered secrets. // //gotagsrewrite:gen type RegistriesConfigSpec struct { RegistryMirrors map[string]*RegistryMirrorConfig `yaml:"mirrors,omitempty" protobuf:"1"` RegistryAuths map[string]*RegistryAuthConfig `yaml:"auths,omitempty" protobuf:"2"` RegistryTLSs map[string]*RegistryTLSConfig `yaml:"tls,omitempty" protobuf:"3"` } // Mirrors implements the Registries interface. func (r RegistriesConfigSpec) Mirrors() map[string]config2.RegistryMirrorConfig { mirrors := make(map[string]config2.RegistryMirrorConfig, len(r.RegistryMirrors)) for k, v := range r.RegistryMirrors { mirrors[k] = v } return mirrors } // Auths implements the Registries interface. func (r RegistriesConfigSpec) Auths() map[string]config2.RegistryAuthConfig { auths := make(map[string]config2.RegistryAuthConfig, len(r.RegistryAuths)) for k, v := range r.RegistryAuths { auths[k] = v } return auths } // TLSs implements the Registries interface. func (r RegistriesConfigSpec) TLSs() map[string]RegistryTLSConfigExtended { tlss := make(map[string]RegistryTLSConfigExtended, len(r.RegistryTLSs)) for k, v := range r.RegistryTLSs { tlss[k] = v } return tlss } // RegistryMirrorConfig represents mirror configuration for a registry. // //gotagsrewrite:gen type RegistryMirrorConfig struct { MirrorEndpoints []RegistryEndpointConfig `yaml:"endpoints" protobuf:"1"` MirrorSkipFallback bool `yaml:"skipFallback,omitempty" protobuf:"3"` } // SkipFallback implements the Registries interface. func (r *RegistryMirrorConfig) SkipFallback() bool { return r.MirrorSkipFallback } // Endpoints implements the Registries interface. func (r *RegistryMirrorConfig) Endpoints() []config2.RegistryEndpointConfig { return xslices.Map(r.MirrorEndpoints, func(endpoint RegistryEndpointConfig) config2.RegistryEndpointConfig { return endpoint }) } // RegistryEndpointConfig represents a single registry endpoint. // //gotagsrewrite:gen type RegistryEndpointConfig struct { EndpointEndpoint string `yaml:"endpoint" protobuf:"1"` EndpointOverridePath bool `yaml:"overridePath" protobuf:"2"` } // Endpoint implements the Registries interface. func (r RegistryEndpointConfig) Endpoint() string { return r.EndpointEndpoint } // OverridePath implements the Registries interface. func (r RegistryEndpointConfig) OverridePath() bool { return r.EndpointOverridePath } // RegistryAuthConfig specifies authentication configuration for a registry. // //gotagsrewrite:gen type RegistryAuthConfig struct { RegistryUsername string `yaml:"username,omitempty" protobuf:"1"` RegistryPassword string `yaml:"password,omitempty" protobuf:"2"` RegistryAuth string `yaml:"auth,omitempty" protobuf:"3"` RegistryIdentityToken string `yaml:"identityToken,omitempty" protobuf:"4"` } // Username implements the Registries interface. func (r *RegistryAuthConfig) Username() string { return r.RegistryUsername } // Password implements the Registries interface. func (r *RegistryAuthConfig) Password() string { return r.RegistryPassword } // Auth implements the Registries interface. func (r *RegistryAuthConfig) Auth() string { return r.RegistryAuth } // IdentityToken implements the Registries interface. func (r *RegistryAuthConfig) IdentityToken() string { return r.RegistryIdentityToken } // RegistryTLSConfig specifies TLS config for HTTPS registries. // //gotagsrewrite:gen type RegistryTLSConfig struct { TLSClientIdentity *x509.PEMEncodedCertificateAndKey `yaml:"clientIdentity,omitempty" protobuf:"1"` TLSCA v1alpha1.Base64Bytes `yaml:"ca,omitempty" protobuf:"2"` TLSInsecureSkipVerify bool `yaml:"insecureSkipVerify,omitempty" protobuf:"3"` } // ClientIdentity implements the Registries interface. func (r *RegistryTLSConfig) ClientIdentity() *x509.PEMEncodedCertificateAndKey { return r.TLSClientIdentity } // CA implements the Registries interface. func (r *RegistryTLSConfig) CA() []byte { return r.TLSCA } // InsecureSkipVerify implements the Registries interface. func (r *RegistryTLSConfig) InsecureSkipVerify() bool { return r.TLSInsecureSkipVerify } // GetTLSConfig prepares TLS configuration for connection. func (r *RegistryTLSConfig) GetTLSConfig() (*tls.Config, error) { tlsConfig := &tls.Config{} if r.TLSClientIdentity != nil { cert, err := tls.X509KeyPair(r.TLSClientIdentity.Crt, r.TLSClientIdentity.Key) if err != nil { return nil, fmt.Errorf("error parsing client identity: %w", err) } tlsConfig.Certificates = []tls.Certificate{cert} } if r.CA() != nil { tlsConfig.RootCAs = stdx509.NewCertPool() tlsConfig.RootCAs.AppendCertsFromPEM(r.TLSCA) } if r.InsecureSkipVerify() { tlsConfig.InsecureSkipVerify = true } return tlsConfig, nil } // NewRegistriesConfig initializes a RegistriesConfig resource. func NewRegistriesConfig() *RegistriesConfig { return typed.NewResource[RegistriesConfigSpec, RegistriesConfigExtension]( resource.NewMetadata(NamespaceName, RegistriesConfigType, RegistriesConfigID, resource.VersionUndefined), RegistriesConfigSpec{}, ) } // RegistriesConfigExtension provides auxiliary methods for RegistriesConfig. type RegistriesConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (RegistriesConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: RegistriesConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(RegistriesConfigType, &RegistriesConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/cri/seccomp_profile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package cri import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SeccompProfileType is type of SeccompProfile resource. const SeccompProfileType = resource.Type("SeccompProfiles.cri.talos.dev") // SeccompProfile represents SeccompProfile typed resource. type SeccompProfile = typed.Resource[SeccompProfileSpec, SeccompProfileExtension] // SeccompProfileSpec represents the SeccompProfile. // //gotagsrewrite:gen type SeccompProfileSpec struct { Name string `yaml:"name" protobuf:"1"` Value map[string]any `yaml:"value" protobuf:"2"` } // NewSeccompProfile creates new SeccompProfile object. func NewSeccompProfile(id string) *SeccompProfile { return typed.NewResource[SeccompProfileSpec, SeccompProfileExtension]( resource.NewMetadata(NamespaceName, SeccompProfileType, id, resource.VersionUndefined), SeccompProfileSpec{}, ) } // SeccompProfileExtension is an auxiliary type for SeccompProfile resource. type SeccompProfileExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (SeccompProfileExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SeccompProfileType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(SeccompProfileType, &SeccompProfile{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/etcd/condition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "context" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" ) // SpecReadyCondition implements condition which waits for the etcd spec to be ready. type SpecReadyCondition struct { state state.State } // NewSpecReadyCondition builds a condition which waits for the etcd spec to be ready. func NewSpecReadyCondition(state state.State) *SpecReadyCondition { return &SpecReadyCondition{ state: state, } } func (condition *SpecReadyCondition) String() string { return "etcd spec" } // Wait implements condition interface. func (condition *SpecReadyCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, SpecType, SpecID, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), ) return err } ================================================ FILE: pkg/machinery/resources/etcd/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ConfigType is type of Config resource. const ConfigType = resource.Type("EtcdConfigs.etcd.talos.dev") // ConfigID is resource ID for Config resource for etcd. const ConfigID = resource.ID("etcd") // Config resource holds status of rendered secrets. type Config = typed.Resource[ConfigSpec, ConfigExtension] // ConfigSpec describes (some) configuration settings of etcd. // //gotagsrewrite:gen type ConfigSpec struct { AdvertiseValidSubnets []string `yaml:"advertiseValidSubnets,omitempty" protobuf:"1"` AdvertiseExcludeSubnets []string `yaml:"advertiseExcludeSubnets" protobuf:"2"` ListenValidSubnets []string `yaml:"listenValidSubnets,omitempty" protobuf:"5"` ListenExcludeSubnets []string `yaml:"listenExcludeSubnets" protobuf:"6"` Image string `yaml:"image" protobuf:"3"` ExtraArgs map[string]ArgValues `yaml:"extraArgs" protobuf:"4"` } // ArgValues represents values for a command line argument which can be specified multiple times. // //gotagsrewrite:gen type ArgValues struct { Values []string `yaml:"values" protobuf:"1"` } // NewConfig initializes a Config resource. func NewConfig(namespace resource.Namespace, id resource.ID) *Config { return typed.NewResource[ConfigSpec, ConfigExtension]( resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined), ConfigSpec{}, ) } // ConfigExtension provides auxiliary methods for Config. type ConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Image", JSONPath: "{.image}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigSpec](ConfigType, &Config{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/etcd/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ConfigSpec -type PKIStatusSpec -type SpecSpec -type MemberSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package etcd import ( "net/netip" ) // DeepCopy generates a deep copy of ConfigSpec. func (o ConfigSpec) DeepCopy() ConfigSpec { var cp ConfigSpec = o if o.AdvertiseValidSubnets != nil { cp.AdvertiseValidSubnets = make([]string, len(o.AdvertiseValidSubnets)) copy(cp.AdvertiseValidSubnets, o.AdvertiseValidSubnets) } if o.AdvertiseExcludeSubnets != nil { cp.AdvertiseExcludeSubnets = make([]string, len(o.AdvertiseExcludeSubnets)) copy(cp.AdvertiseExcludeSubnets, o.AdvertiseExcludeSubnets) } if o.ListenValidSubnets != nil { cp.ListenValidSubnets = make([]string, len(o.ListenValidSubnets)) copy(cp.ListenValidSubnets, o.ListenValidSubnets) } if o.ListenExcludeSubnets != nil { cp.ListenExcludeSubnets = make([]string, len(o.ListenExcludeSubnets)) copy(cp.ListenExcludeSubnets, o.ListenExcludeSubnets) } if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } return cp } // DeepCopy generates a deep copy of PKIStatusSpec. func (o PKIStatusSpec) DeepCopy() PKIStatusSpec { var cp PKIStatusSpec = o return cp } // DeepCopy generates a deep copy of SpecSpec. func (o SpecSpec) DeepCopy() SpecSpec { var cp SpecSpec = o if o.AdvertisedAddresses != nil { cp.AdvertisedAddresses = make([]netip.Addr, len(o.AdvertisedAddresses)) copy(cp.AdvertisedAddresses, o.AdvertisedAddresses) } if o.ListenPeerAddresses != nil { cp.ListenPeerAddresses = make([]netip.Addr, len(o.ListenPeerAddresses)) copy(cp.ListenPeerAddresses, o.ListenPeerAddresses) } if o.ListenClientAddresses != nil { cp.ListenClientAddresses = make([]netip.Addr, len(o.ListenClientAddresses)) copy(cp.ListenClientAddresses, o.ListenClientAddresses) } if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } return cp } // DeepCopy generates a deep copy of MemberSpec. func (o MemberSpec) DeepCopy() MemberSpec { var cp MemberSpec = o return cp } ================================================ FILE: pkg/machinery/resources/etcd/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package etcd provides resources which interface with etcd. package etcd import ( "fmt" "strconv" "github.com/cosi-project/runtime/pkg/resource" ) //go:generate go tool github.com/siderolabs/deep-copy -type ConfigSpec -type PKIStatusSpec -type SpecSpec -type MemberSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains resources supporting etcd service. const NamespaceName resource.Namespace = "etcd" // FormatMemberID represents a uint64 in hexadecimal notation. func FormatMemberID(memberID uint64) string { return fmt.Sprintf("%016x", memberID) } // ParseMemberID converts a member ID in hexadecimal notation to a uint64. func ParseMemberID(memberID string) (uint64, error) { id, err := strconv.ParseUint(memberID, 16, 64) if err != nil { return 0, fmt.Errorf("error parsing etcd member id: %w", err) } return id, nil } ================================================ FILE: pkg/machinery/resources/etcd/etcd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/resources/etcd" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &etcd.PKIStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } func TestFormatMemberID(t *testing.T) { tests := []struct { name string id uint64 str string }{ { name: "small id", id: 1, str: "0000000000000001", }, { name: "big id", id: uint64(1) << 63, str: "8000000000000000", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, tt.str, etcd.FormatMemberID(tt.id)) id, err := etcd.ParseMemberID(tt.str) require.NoError(t, err) assert.Equal(t, tt.id, id) }) } } ================================================ FILE: pkg/machinery/resources/etcd/member.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MemberType is type of Member resource. const MemberType = resource.Type("EtcdMembers.etcd.talos.dev") // LocalMemberID is resource ID for Member resource for etcd. const LocalMemberID = resource.ID("local") // Member resource holds status of rendered secrets. type Member = typed.Resource[MemberSpec, MemberExtension] // MemberSpec holds information about an etcd member. // //gotagsrewrite:gen type MemberSpec struct { MemberID string `yaml:"memberID" protobuf:"1"` } // NewMember initializes a Member resource. func NewMember(namespace resource.Namespace, id resource.ID) *Member { return typed.NewResource[MemberSpec, MemberExtension]( resource.NewMetadata(namespace, MemberType, id, resource.VersionUndefined), MemberSpec{}, ) } // MemberExtension provides auxiliary methods for Member. type MemberExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (MemberExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MemberType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Member ID", JSONPath: "{.memberID}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MemberSpec](MemberType, &Member{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/etcd/pki_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PKIStatusType is type of PKIStatus resource. const PKIStatusType = resource.Type("PKIStatuses.etcd.talos.dev") // PKIID is resource ID for PKIStatus resource for etcd. const PKIID = resource.ID("etcd") // PKIStatus resource holds status of rendered secrets. type PKIStatus = typed.Resource[PKIStatusSpec, PKIStatusExtension] // PKIStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type PKIStatusSpec struct { Ready bool `yaml:"ready" protobuf:"1"` Version string `yaml:"version" protobuf:"2"` } // NewPKIStatus initializes a PKIStatus resource. func NewPKIStatus(namespace resource.Namespace, id resource.ID) *PKIStatus { return typed.NewResource[PKIStatusSpec, PKIStatusExtension]( resource.NewMetadata(namespace, PKIStatusType, id, resource.VersionUndefined), PKIStatusSpec{}, ) } // PKIStatusExtension provides auxiliary methods for PKIStatus. type PKIStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PKIStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PKIStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: "{.ready}", }, { Name: "Secrets Version", JSONPath: "{.version}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PKIStatusSpec](PKIStatusType, &PKIStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/etcd/spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package etcd import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SpecType is type of Spec resource. const SpecType = resource.Type("EtcdSpecs.etcd.talos.dev") // SpecID is resource ID for Spec resource for etcd. const SpecID = resource.ID("etcd") // Spec resource holds status of rendered secrets. type Spec = typed.Resource[SpecSpec, SpecExtension] // SpecSpec describes (some) Specuration settings of etcd. // //gotagsrewrite:gen type SpecSpec struct { Name string `yaml:"name" protobuf:"1"` AdvertisedAddresses []netip.Addr `yaml:"advertisedAddresses" protobuf:"2"` ListenPeerAddresses []netip.Addr `yaml:"listenPeerAddresses" protobuf:"5"` ListenClientAddresses []netip.Addr `yaml:"listenClientAddresses" protobuf:"6"` Image string `yaml:"image" protobuf:"3"` ExtraArgs map[string]ArgValues `yaml:"extraArgs" protobuf:"4"` } // NewSpec initializes a Spec resource. func NewSpec(namespace resource.Namespace, id resource.ID) *Spec { return typed.NewResource[SpecSpec, SpecExtension]( resource.NewMetadata(namespace, SpecType, id, resource.VersionUndefined), SpecSpec{}, ) } // SpecExtension provides auxiliary methods for Spec. type SpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (SpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Name", JSONPath: "{.name}", }, { Name: "AdvertisedAddresses", JSONPath: "{.advertisedAddresses}", }, { Name: "ListenPeerAddresses", JSONPath: "{.listenPeerAddresses}", }, { Name: "ListenClientAddresses", JSONPath: "{.listenClientAddresses}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SpecSpec](SpecType, &Spec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/files/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type EtcFileSpecSpec -type EtcFileStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package files // DeepCopy generates a deep copy of EtcFileSpecSpec. func (o EtcFileSpecSpec) DeepCopy() EtcFileSpecSpec { var cp EtcFileSpecSpec = o if o.Contents != nil { cp.Contents = make([]byte, len(o.Contents)) copy(cp.Contents, o.Contents) } return cp } // DeepCopy generates a deep copy of EtcFileStatusSpec. func (o EtcFileStatusSpec) DeepCopy() EtcFileStatusSpec { var cp EtcFileStatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/files/etcfile_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "io/fs" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/yamlutils" ) //go:generate go tool github.com/siderolabs/deep-copy -type EtcFileSpecSpec -type EtcFileStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // EtcFileSpecType is type of EtcFile resource. const EtcFileSpecType = resource.Type("EtcFileSpecs.files.talos.dev") // EtcFileSpec resource holds contents of the file which should be put to `/etc` directory. type EtcFileSpec = typed.Resource[EtcFileSpecSpec, EtcFileSpecExtension] // EtcFileSpecSpec describes status of rendered secrets. // //gotagsrewrite:gen type EtcFileSpecSpec struct { Contents yamlutils.StringBytes `yaml:"contents" protobuf:"1"` Mode fs.FileMode `yaml:"mode" protobuf:"2"` SelinuxLabel string `yaml:"selinux_label" protobuf:"3"` } // NewEtcFileSpec initializes a EtcFileSpec resource. func NewEtcFileSpec(namespace resource.Namespace, id resource.ID) *EtcFileSpec { return typed.NewResource[EtcFileSpecSpec, EtcFileSpecExtension]( resource.NewMetadata(namespace, EtcFileSpecType, id, resource.VersionUndefined), EtcFileSpecSpec{}, ) } // EtcFileSpecExtension provides auxiliary methods for EtcFileSpec. type EtcFileSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EtcFileSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EtcFileSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EtcFileSpecSpec](EtcFileSpecType, &EtcFileSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/files/etcfile_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EtcFileStatusType is type of EtcFile resource. const EtcFileStatusType = resource.Type("EtcFileStatuses.files.talos.dev") // EtcFileStatus resource holds contents of the file which should be put to `/etc` directory. type EtcFileStatus = typed.Resource[EtcFileStatusSpec, EtcFileStatusMD] // EtcFileStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type EtcFileStatusSpec struct { SpecVersion string `yaml:"specVersion" protobuf:"1"` } // NewEtcFileStatus initializes a EtcFileStatus resource. func NewEtcFileStatus(namespace resource.Namespace, id resource.ID) *EtcFileStatus { return typed.NewResource[EtcFileStatusSpec, EtcFileStatusMD]( resource.NewMetadata(namespace, EtcFileStatusType, id, resource.VersionUndefined), EtcFileStatusSpec{}, ) } // EtcFileStatusMD provides auxiliary methods for EtcFileStatus. type EtcFileStatusMD struct{} // ResourceDefinition implements [typed.Extension] interface. func (EtcFileStatusMD) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EtcFileStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EtcFileStatusSpec](EtcFileStatusType, &EtcFileStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/files/files.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package files provides resources which describe files on disk. package files import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains file resources. const NamespaceName resource.Namespace = "files" // SourceFileAnnotation is used to annotate a file resource with the source file path(s). const SourceFileAnnotation = "talos.dev/source-file" ================================================ FILE: pkg/machinery/resources/files/files_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package files_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/files" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &files.EtcFileSpec{}, &files.EtcFileStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/hardware/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type MemoryModuleSpec -type PCIDeviceSpec -type PCIDriverRebindConfigSpec -type PCIDriverRebindStatusSpec -type PCRStatusSpec -type ProcessorSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package hardware // DeepCopy generates a deep copy of MemoryModuleSpec. func (o MemoryModuleSpec) DeepCopy() MemoryModuleSpec { var cp MemoryModuleSpec = o return cp } // DeepCopy generates a deep copy of PCIDeviceSpec. func (o PCIDeviceSpec) DeepCopy() PCIDeviceSpec { var cp PCIDeviceSpec = o return cp } // DeepCopy generates a deep copy of PCIDriverRebindConfigSpec. func (o PCIDriverRebindConfigSpec) DeepCopy() PCIDriverRebindConfigSpec { var cp PCIDriverRebindConfigSpec = o return cp } // DeepCopy generates a deep copy of PCIDriverRebindStatusSpec. func (o PCIDriverRebindStatusSpec) DeepCopy() PCIDriverRebindStatusSpec { var cp PCIDriverRebindStatusSpec = o return cp } // DeepCopy generates a deep copy of PCRStatusSpec. func (o PCRStatusSpec) DeepCopy() PCRStatusSpec { var cp PCRStatusSpec = o return cp } // DeepCopy generates a deep copy of ProcessorSpec. func (o ProcessorSpec) DeepCopy() ProcessorSpec { var cp ProcessorSpec = o return cp } // DeepCopy generates a deep copy of SystemInformationSpec. func (o SystemInformationSpec) DeepCopy() SystemInformationSpec { var cp SystemInformationSpec = o return cp } ================================================ FILE: pkg/machinery/resources/hardware/hardware.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" ) //go:generate go tool github.com/siderolabs/deep-copy -type MemoryModuleSpec -type PCIDeviceSpec -type PCIDriverRebindConfigSpec -type PCIDriverRebindStatusSpec -type PCRStatusSpec -type ProcessorSpec -type SystemInformationSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains resources related to hardware as a whole. const NamespaceName resource.Namespace = "hardware" ================================================ FILE: pkg/machinery/resources/hardware/hardware_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/hardware" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &hardware.MemoryModule{}, &hardware.PCIDevice{}, &hardware.PCIDriverRebindConfig{}, &hardware.PCIDriverRebindStatus{}, &hardware.PCRStatus{}, &hardware.Processor{}, &hardware.SystemInformation{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/hardware/memorymodule.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MemoryModuleType is type of MemoryModule resource. const MemoryModuleType = resource.Type("MemoryModules.hardware.talos.dev") // MemoryModule resource holds node MemoryModule information. type MemoryModule = typed.Resource[MemoryModuleSpec, MemoryModuleExtension] // MemoryModuleSpec represents a single Memory. // //gotagsrewrite:gen type MemoryModuleSpec struct { Size uint32 `yaml:"sizeMiB,omitempty" protobuf:"1"` DeviceLocator string `yaml:"deviceLocator,omitempty" protobuf:"2"` BankLocator string `yaml:"bankLocator,omitempty" protobuf:"3"` Speed uint32 `yaml:"speed,omitempty" protobuf:"4"` Manufacturer string `yaml:"manufacturer,omitempty" protobuf:"5"` SerialNumber string `yaml:"serialNumber,omitempty" protobuf:"6"` AssetTag string `yaml:"assetTag,omitempty" protobuf:"7"` ProductName string `yaml:"productName,omitempty" protobuf:"8"` } // NewMemoryModuleInfo initializes a MemoryModuleInfo resource. func NewMemoryModuleInfo(id string) *MemoryModule { return typed.NewResource[MemoryModuleSpec, MemoryModuleExtension]( resource.NewMetadata(NamespaceName, MemoryModuleType, id, resource.VersionUndefined), MemoryModuleSpec{}, ) } // MemoryModuleExtension provides auxiliary methods for Memory info. type MemoryModuleExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (MemoryModuleExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MemoryModuleType, Aliases: []resource.Type{ "memorymodules", "ram", }, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Manufacturer", JSONPath: `{.manufacturer}`, }, { Name: "Model", JSONPath: `{.productName}`, }, { Name: "SizeMiB", JSONPath: `{.sizeMiB}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MemoryModuleSpec](MemoryModuleType, &MemoryModule{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/pci_driver_rebind_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PCIDriverRebindConfigType is type of PCIDriverRebindConfig resource. const PCIDriverRebindConfigType = resource.Type("PCIDriverRebindConfigs.runtime.talos.dev") // PCIDriverRebindConfig resource holds PCI rebind configuration. type PCIDriverRebindConfig = typed.Resource[PCIDriverRebindConfigSpec, PCIDriverRebindConfigExtension] // PCIDriverRebindConfigSpec describes PCI rebind configuration. // //gotagsrewrite:gen type PCIDriverRebindConfigSpec struct { PCIID string `yaml:"pciID" protobuf:"1"` TargetDriver string `yaml:"targetDriver" protobuf:"2"` } // PCIDriverRebindConfigExtension is auxiliary resource data for PCIDriverRebindConfig. type PCIDriverRebindConfigExtension struct{} // NewPCIDriverRebindConfig initializes a PCIDriverRebindConfig resource. func NewPCIDriverRebindConfig(id resource.ID) *PCIDriverRebindConfig { return typed.NewResource[PCIDriverRebindConfigSpec, PCIDriverRebindConfigExtension]( resource.NewMetadata(NamespaceName, PCIDriverRebindConfigType, id, resource.VersionUndefined), PCIDriverRebindConfigSpec{}, ) } // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (PCIDriverRebindConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PCIDriverRebindConfigType, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Name", JSONPath: `{.name}`, }, { Name: "PCI ID", JSONPath: `{.pciID}`, }, { Name: "TargetDriver", JSONPath: `{.targetDriver}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PCIDriverRebindConfigSpec](PCIDriverRebindConfigType, &PCIDriverRebindConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/pci_driver_rebind_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PCIDriverRebindStatusType is the type of the PCIDriverRebindStatus resource. const PCIDriverRebindStatusType = resource.Type("PCIDriverRebindStatuses.runtime.talos.dev") // PCIDriverRebindStatus resource holds status of rebinded drivers. type PCIDriverRebindStatus = typed.Resource[PCIDriverRebindStatusSpec, PCIDriverRebindStatusExtension] // PCIDriverRebindStatusSpec describes status of rebinded drivers. // //gotagsrewrite:gen type PCIDriverRebindStatusSpec struct { PCIID string `yaml:"pciID" protobuf:"1"` TargetDriver string `yaml:"targetDriver" protobuf:"2"` } // NewPCIDriverRebindStatus initializes a PCIDriverRebindStatus resource. func NewPCIDriverRebindStatus(id resource.ID) *PCIDriverRebindStatus { return typed.NewResource[PCIDriverRebindStatusSpec, PCIDriverRebindStatusExtension]( resource.NewMetadata(NamespaceName, PCIDriverRebindStatusType, id, resource.VersionUndefined), PCIDriverRebindStatusSpec{}, ) } // PCIDriverRebindStatusExtension is auxiliary resource data for PCIDriverRebindStatus. type PCIDriverRebindStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (PCIDriverRebindStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PCIDriverRebindStatusType, Aliases: []resource.Type{"pcidriverrebinds"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Name", JSONPath: `{.name}`, }, { Name: "PCI ID", JSONPath: `{.pciID}`, }, { Name: "TargetDriver", JSONPath: `{.targetDriver}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PCIDriverRebindStatusSpec](PCIDriverRebindStatusType, &PCIDriverRebindStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/pcidevice.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PCIDeviceType is type of PCIDevice resource. const PCIDeviceType = resource.Type("PCIDevices.hardware.talos.dev") // PCIDevice resource holds node PCIDevice information. type PCIDevice = typed.Resource[PCIDeviceSpec, PCIDeviceExtension] // PCIDeviceSpec represents a single processor. // //gotagsrewrite:gen type PCIDeviceSpec struct { Class string `yaml:"class,omitempty" protobuf:"1"` Subclass string `yaml:"subclass,omitempty" protobuf:"2"` Vendor string `yaml:"vendor,omitempty" protobuf:"3"` Product string `yaml:"product,omitempty" protobuf:"4"` ClassID string `yaml:"class_id" protobuf:"5"` SubclassID string `yaml:"subclass_id" protobuf:"6"` VendorID string `yaml:"vendor_id" protobuf:"7"` ProductID string `yaml:"product_id" protobuf:"8"` Driver string `yaml:"driver,omitempty" protobuf:"9"` } // NewPCIDeviceInfo initializes a PCIDeviceInfo resource. func NewPCIDeviceInfo(id string) *PCIDevice { return typed.NewResource[PCIDeviceSpec, PCIDeviceExtension]( resource.NewMetadata(NamespaceName, PCIDeviceType, id, resource.VersionUndefined), PCIDeviceSpec{}, ) } // PCIDeviceExtension provides auxiliary methods for PCIDevice info. type PCIDeviceExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PCIDeviceExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PCIDeviceType, Aliases: []resource.Type{ "devices", "device", }, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Class", JSONPath: `{.class}`, }, { Name: "Subclass", JSONPath: `{.subclass}`, }, { Name: "Vendor", JSONPath: `{.vendor}`, }, { Name: "Product", JSONPath: `{.product}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(PCIDeviceType, &PCIDevice{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/pcr_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "context" "errors" "fmt" "strconv" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PCRStatusType is type of PCRStatus resource. const PCRStatusType = resource.Type("PCRStatuses.hardware.talos.dev") // PCRStatus resource holds node PCRStatus information. type PCRStatus = typed.Resource[PCRStatusSpec, PCRStatusExtension] // PCRStatusSpec represents a single PCR status. // // The resource is created when the PCR is ready to be used, and // torn down/destroyed as the PCR value is extended to prevent it from being // used. // //gotagsrewrite:gen type PCRStatusSpec struct{} // NewPCCRStatus initializes a PCRStatus resource. func NewPCCRStatus(pcr int) *PCRStatus { return typed.NewResource[PCRStatusSpec, PCRStatusExtension]( resource.NewMetadata(NamespaceName, PCRStatusType, strconv.Itoa(pcr), resource.VersionUndefined), PCRStatusSpec{}, ) } // PCRStatusExtension provides auxiliary methods for PCRStatus info. type PCRStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PCRStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PCRStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } // FinalizerState is a minimal interface for state that supports finalizers. type FinalizerState interface { AddFinalizer(context.Context, resource.Pointer, ...resource.Finalizer) error RemoveFinalizer(context.Context, resource.Pointer, ...resource.Finalizer) error } // LockPCRStatus locks the PCR status resource. func LockPCRStatus(st FinalizerState, pcr int, finalizerName string) func(context.Context, func() error) error { return func(ctx context.Context, fn func() error) error { pcrStatus := NewPCCRStatus(pcr) if err := st.AddFinalizer(ctx, pcrStatus.Metadata(), finalizerName); err != nil { if state.IsNotFoundError(err) { return fmt.Errorf("failed to lock PCR %d, as it is in the wrong state, a reboot might be required", pcr) } return fmt.Errorf("failed to lock PCR %d: %w", pcr, err) } fnErr := fn() if err := st.RemoveFinalizer(ctx, pcrStatus.Metadata(), finalizerName); err != nil { fnErr = errors.Join(fnErr, fmt.Errorf("failed to unlock PCR %d: %w", pcr, err)) } return fnErr } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(PCRStatusType, &PCRStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/processor.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ProcessorType is type of Processor resource. const ProcessorType = resource.Type("Processors.hardware.talos.dev") // Processor resource holds node Processor information. type Processor = typed.Resource[ProcessorSpec, ProcessorExtension] // ProcessorSpec represents a single processor. // //gotagsrewrite:gen type ProcessorSpec struct { Socket string `yaml:"socket,omitempty" protobuf:"1"` Manufacturer string `yaml:"manufacturer,omitempty" protobuf:"2"` ProductName string `yaml:"productName,omitempty" protobuf:"3"` // MaxSpeed is in megahertz (Mhz) MaxSpeed uint32 `yaml:"maxSpeedMhz,omitempty" protobuf:"4"` // Speed is in megahertz (Mhz) BootSpeed uint32 `yaml:"bootSpeedMhz,omitempty" protobuf:"5"` Status uint32 `yaml:"status,omitempty" protobuf:"6"` SerialNumber string `yaml:"serialNumber,omitempty" protobuf:"7"` AssetTag string `yaml:"assetTag,omitempty" protobuf:"8"` PartNumber string `yaml:"partNumber,omitempty" protobuf:"9"` CoreCount uint32 `yaml:"coreCount,omitempty" protobuf:"10"` CoreEnabled uint32 `yaml:"coreEnabled,omitempty" protobuf:"11"` ThreadCount uint32 `yaml:"threadCount,omitempty" protobuf:"12"` } // NewProcessorInfo initializes a ProcessorInfo resource. func NewProcessorInfo(id string) *Processor { return typed.NewResource[ProcessorSpec, ProcessorExtension]( resource.NewMetadata(NamespaceName, ProcessorType, id, resource.VersionUndefined), ProcessorSpec{}, ) } // ProcessorExtension provides auxiliary methods for Processor info. type ProcessorExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ProcessorExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ProcessorType, Aliases: []resource.Type{ "cpus", "cpu", }, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Manufacturer", JSONPath: `{.manufacturer}`, }, { Name: "Model", JSONPath: `{.productName}`, }, { Name: "Cores", JSONPath: `{.coreCount}`, }, { Name: "Threads", JSONPath: `{.threadCount}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ProcessorSpec](ProcessorType, &Processor{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/hardware/system_information.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package hardware import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SystemInformationType is type of SystemInformation resource. const SystemInformationType = resource.Type("SystemInformations.hardware.talos.dev") // SystemInformationID is the ID of the SystemInformation resource. const SystemInformationID = resource.ID("systeminformation") // SystemInformation resource holds node SystemInformation information. type SystemInformation = typed.Resource[SystemInformationSpec, SystemInformationExtension] // SystemInformationSpec represents the system information obtained from smbios. // //gotagsrewrite:gen type SystemInformationSpec struct { Manufacturer string `yaml:"manufacturer,omitempty" protobuf:"1"` ProductName string `yaml:"productName,omitempty" protobuf:"2"` Version string `yaml:"version,omitempty" protobuf:"3"` SerialNumber string `yaml:"serialnumber,omitempty" protobuf:"4"` UUID string `yaml:"uuid,omitempty" protobuf:"5"` WakeUpType string `yaml:"wakeUpType,omitempty" protobuf:"6"` SKUNumber string `yaml:"skuNumber,omitempty" protobuf:"7"` } // NewSystemInformation initializes a SystemInformationInfo resource. func NewSystemInformation(id string) *SystemInformation { return typed.NewResource[SystemInformationSpec, SystemInformationExtension]( resource.NewMetadata(NamespaceName, SystemInformationType, id, resource.VersionUndefined), SystemInformationSpec{}, ) } // SystemInformationExtension provides auxiliary methods for SystemInformation. type SystemInformationExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (SystemInformationExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SystemInformationType, Aliases: []resource.Type{ "systeminformation", }, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Manufacturer", JSONPath: `{.manufacturer}`, }, { Name: "ProductName", JSONPath: `{.productName}`, }, { Name: "Version", JSONPath: `{.version}`, }, { Name: "SerialNumber", JSONPath: `{.serialnumber}`, }, { Name: "UUID", JSONPath: `{.uuid}`, }, { Name: "WakeUpType", JSONPath: `{.wakeUpType}`, }, { Name: "SKUNumber", JSONPath: `{.skuNumber}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SystemInformationSpec](SystemInformationType, &SystemInformation{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/admissioncontrol_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // AdmissionControlConfigType is type of AdmissionControlConfig resource. const AdmissionControlConfigType = resource.Type("AdmissionControlConfigs.kubernetes.talos.dev") // AdmissionControlConfigID is a singleton resource ID for AdmissionControlConfig. const AdmissionControlConfigID = resource.ID("admission-control") // AdmissionControlConfig represents configuration for kube-apiserver Admission Control plugins. type AdmissionControlConfig = typed.Resource[AdmissionControlConfigSpec, AdmissionControlConfigExtension] // AdmissionControlConfigSpec is configuration for kube-apiserver. // //gotagsrewrite:gen type AdmissionControlConfigSpec struct { Config []AdmissionPluginSpec `yaml:"config" protobuf:"1"` } // AdmissionPluginSpec is a single admission plugin configuration Admission Control plugins. // //gotagsrewrite:gen type AdmissionPluginSpec struct { Name string `yaml:"name" protobuf:"1"` Configuration map[string]any `yaml:"configuration" protobuf:"2"` } // NewAdmissionControlConfig returns new AdmissionControlConfig resource. func NewAdmissionControlConfig() *AdmissionControlConfig { return typed.NewResource[AdmissionControlConfigSpec, AdmissionControlConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, AdmissionControlConfigType, AdmissionControlConfigID, resource.VersionUndefined), AdmissionControlConfigSpec{}) } // AdmissionControlConfigExtension defines AdmissionControlConfig resource definition. type AdmissionControlConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AdmissionControlConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AdmissionControlConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AdmissionControlConfigSpec](AdmissionControlConfigType, &AdmissionControlConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/apiserver_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // APIServerConfigType is type of APIServerConfig resource. const APIServerConfigType = resource.Type("APIServerConfigs.kubernetes.talos.dev") // APIServerConfigID is a singleton resource ID for APIServerConfig. const APIServerConfigID = resource.ID(APIServerID) // APIServerConfig represents configuration for kube-apiserver. type APIServerConfig = typed.Resource[APIServerConfigSpec, APIServerConfigExtension] // ExtraVolume is a configuration of extra volume. // //gotagsrewrite:gen type ExtraVolume struct { Name string `yaml:"name" protobuf:"1"` HostPath string `yaml:"hostPath" protobuf:"2"` MountPath string `yaml:"mountPath" protobuf:"3"` ReadOnly bool `yaml:"readonly" protobuf:"4"` } // Resources is a configuration of cpu and memory resources. // //gotagsrewrite:gen type Resources struct { Requests map[string]string `yaml:"requests" protobuf:"1"` Limits map[string]string `yaml:"limits" protobuf:"2"` } // APIServerConfigSpec is configuration for kube-apiserver. // //gotagsrewrite:gen type APIServerConfigSpec struct { Image string `yaml:"image" protobuf:"1"` CloudProvider string `yaml:"cloudProvider" protobuf:"2"` ControlPlaneEndpoint string `yaml:"controlPlaneEndpoint" protobuf:"3"` EtcdServers []string `yaml:"etcdServers" protobuf:"4"` LocalPort int `yaml:"localPort" protobuf:"5"` ServiceCIDRs []string `yaml:"serviceCIDR" protobuf:"6"` ExtraArgs map[string]ArgValues `yaml:"extraArgs" protobuf:"7"` ExtraVolumes []ExtraVolume `yaml:"extraVolumes" protobuf:"8"` EnvironmentVariables map[string]string `yaml:"environmentVariables" protobuf:"9"` AdvertisedAddress string `yaml:"advertisedAddress" protobuf:"11"` Resources Resources `yaml:"resources" protobuf:"12"` } // ArgValues represents values for a command line argument which can be specified multiple times. // //gotagsrewrite:gen type ArgValues struct { Values []string `yaml:"values" protobuf:"1"` } // NewAPIServerConfig returns new APIServerConfig resource. func NewAPIServerConfig() *APIServerConfig { return typed.NewResource[APIServerConfigSpec, APIServerConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, APIServerConfigType, APIServerConfigID, resource.VersionUndefined), APIServerConfigSpec{}) } // APIServerConfigExtension defines APIServerConfig resource definition. type APIServerConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (APIServerConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: APIServerConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[APIServerConfigSpec](APIServerConfigType, &APIServerConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/auditpolicy_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // AuditPolicyConfigType is type of AuditPolicyConfig resource. const AuditPolicyConfigType = resource.Type("AuditPolicyConfigs.kubernetes.talos.dev") // AuditPolicyConfigID is a singleton resource ID for AuditPolicyConfig. const AuditPolicyConfigID = resource.ID("audit-policy") // AuditPolicyConfig represents configuration for kube-apiserver audit policy. type AuditPolicyConfig = typed.Resource[AuditPolicyConfigSpec, AuditPolicyConfigExtension] // AuditPolicyConfigSpec is audit policy configuration for kube-apiserver. // //gotagsrewrite:gen type AuditPolicyConfigSpec struct { Config map[string]any `yaml:"config" protobuf:"1"` } // NewAuditPolicyConfig returns new AuditPolicyConfig resource. func NewAuditPolicyConfig() *AuditPolicyConfig { return typed.NewResource[AuditPolicyConfigSpec, AuditPolicyConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, AuditPolicyConfigType, AuditPolicyConfigID, resource.VersionUndefined), AuditPolicyConfigSpec{}) } // AuditPolicyConfigExtension defines AuditPolicyConfig resource definition. type AuditPolicyConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AuditPolicyConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AuditPolicyConfigType, DefaultNamespace: ControlPlaneNamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AuditPolicyConfigSpec](AuditPolicyConfigType, &AuditPolicyConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/authorization_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" ) // AuthorizationConfigType is type of AuthorizationConfig resource. const AuthorizationConfigType = resource.Type("AuthorizationConfigs.kubernetes.talos.dev") // AuthorizationConfigID is a singleton resource ID for AuthorizationConfig. const AuthorizationConfigID = resource.ID("authorization") // AuthorizationConfig represents configuration for kube-apiserver authorization. type AuthorizationConfig = typed.Resource[AuthorizationConfigSpec, AuthorizationConfigExtension] // AuthorizationConfigSpec is authorization configuration for kube-apiserver. // //gotagsrewrite:gen type AuthorizationConfigSpec struct { Image string `yaml:"image" protobuf:"1"` Config []AuthorizationAuthorizersSpec `yaml:"config" protobuf:"2"` } // AuthorizationAuthorizersSpec is a configuration of authorization authorizers. // //gotagsrewrite:gen type AuthorizationAuthorizersSpec struct { Type string `yaml:"type" protobuf:"1"` Name string `yaml:"name" protobuf:"2"` Webhook map[string]any `yaml:"webhook" protobuf:"3"` } // NewAuthorizationConfig returns new AuthorizationConfig resource. func NewAuthorizationConfig() *AuthorizationConfig { return typed.NewResource[AuthorizationConfigSpec, AuthorizationConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, AuthorizationConfigType, AuthorizationConfigID, resource.VersionUndefined), AuthorizationConfigSpec{}) } // AuthorizationConfigExtension defines AuthorizationConfig resource definition. type AuthorizationConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AuthorizationConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AuthorizationConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { err := protobuf.RegisterDynamic[AuthorizationConfigSpec](AuthorizationConfigType, &AuthorizationConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/config_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. // //nolint:dupl package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ConfigStatusType is type of ConfigStatus resource. const ConfigStatusType = resource.Type("ConfigStatuses.kubernetes.talos.dev") // ConfigStatusStaticPodID is resource ID for ConfigStatus resource for static pods. const ConfigStatusStaticPodID = resource.ID("static-pods") // ConfigStatus resource holds definition of rendered secrets. type ConfigStatus = typed.Resource[ConfigStatusSpec, ConfigStatusExtension] // ConfigStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type ConfigStatusSpec struct { Ready bool `yaml:"ready" protobuf:"1"` Version string `yaml:"version" protobuf:"2"` } // NewConfigStatus initializes a ConfigStatus resource. func NewConfigStatus(namespace resource.Namespace, id resource.ID) *ConfigStatus { return typed.NewResource[ConfigStatusSpec, ConfigStatusExtension]( resource.NewMetadata(namespace, ConfigStatusType, id, resource.VersionUndefined), ConfigStatusSpec{}, ) } // ConfigStatusExtension provides auxiliary methods for ConfigStatus. type ConfigStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ConfigStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigStatusType, Aliases: []resource.Type{}, DefaultNamespace: ControlPlaneNamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: "{.ready}", }, { Name: "Secrets Version", JSONPath: "{.version}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigStatusSpec](ConfigStatusType, &ConfigStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/controllermanager_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ControllerManagerConfigType is type of ControllerManagerConfig resource. const ControllerManagerConfigType = resource.Type("ControllerManagerConfigs.kubernetes.talos.dev") // ControllerManagerConfigID is a singleton resource ID for ControllerManagerConfig. const ControllerManagerConfigID = resource.ID(ControllerManagerID) // ControllerManagerConfig represents configuration for kube-controller-manager. type ControllerManagerConfig = typed.Resource[ControllerManagerConfigSpec, ControllerManagerConfigExtension] // ControllerManagerConfigSpec is configuration for kube-controller-manager. // //gotagsrewrite:gen type ControllerManagerConfigSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` Image string `yaml:"image" protobuf:"2"` CloudProvider string `yaml:"cloudProvider" protobuf:"3"` PodCIDRs []string `yaml:"podCIDRs" protobuf:"4"` ServiceCIDRs []string `yaml:"serviceCIDRs" protobuf:"5"` ExtraArgs map[string]ArgValues `yaml:"extraArgs" protobuf:"6"` ExtraVolumes []ExtraVolume `yaml:"extraVolumes" protobuf:"7"` EnvironmentVariables map[string]string `yaml:"environmentVariables" protobuf:"8"` Resources Resources `yaml:"resources" protobuf:"9"` } // NewControllerManagerConfig returns new ControllerManagerConfig resource. func NewControllerManagerConfig() *ControllerManagerConfig { return typed.NewResource[ControllerManagerConfigSpec, ControllerManagerConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, ControllerManagerConfigType, ControllerManagerConfigID, resource.VersionUndefined), ControllerManagerConfigSpec{}) } // ControllerManagerConfigExtension defines ControllerManagerConfig resource definition. type ControllerManagerConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ControllerManagerConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ControllerManagerConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ControllerManagerConfigSpec](ControllerManagerConfigType, &ControllerManagerConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type AdmissionControlConfigSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type AuthorizationConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubePrismConfigSpec -type KubePrismEndpointsSpec -type KubePrismStatusesSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeAnnotationSpecSpec -type NodeCordonedSpecSpec -type NodeLabelSpecSpec -type NodeTaintSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodeStatusSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package k8s import ( "net/netip" specs "github.com/opencontainers/runtime-spec/specs-go" ) // DeepCopy generates a deep copy of AdmissionControlConfigSpec. func (o AdmissionControlConfigSpec) DeepCopy() AdmissionControlConfigSpec { var cp AdmissionControlConfigSpec = o if o.Config != nil { cp.Config = make([]AdmissionPluginSpec, len(o.Config)) copy(cp.Config, o.Config) for i2 := range o.Config { if o.Config[i2].Configuration != nil { cp.Config[i2].Configuration = make(map[string]any, len(o.Config[i2].Configuration)) for k4, v4 := range o.Config[i2].Configuration { cp.Config[i2].Configuration[k4] = v4 } } } } return cp } // DeepCopy generates a deep copy of APIServerConfigSpec. func (o APIServerConfigSpec) DeepCopy() APIServerConfigSpec { var cp APIServerConfigSpec = o if o.EtcdServers != nil { cp.EtcdServers = make([]string, len(o.EtcdServers)) copy(cp.EtcdServers, o.EtcdServers) } if o.ServiceCIDRs != nil { cp.ServiceCIDRs = make([]string, len(o.ServiceCIDRs)) copy(cp.ServiceCIDRs, o.ServiceCIDRs) } if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } if o.ExtraVolumes != nil { cp.ExtraVolumes = make([]ExtraVolume, len(o.ExtraVolumes)) copy(cp.ExtraVolumes, o.ExtraVolumes) } if o.EnvironmentVariables != nil { cp.EnvironmentVariables = make(map[string]string, len(o.EnvironmentVariables)) for k2, v2 := range o.EnvironmentVariables { cp.EnvironmentVariables[k2] = v2 } } if o.Resources.Requests != nil { cp.Resources.Requests = make(map[string]string, len(o.Resources.Requests)) for k3, v3 := range o.Resources.Requests { cp.Resources.Requests[k3] = v3 } } if o.Resources.Limits != nil { cp.Resources.Limits = make(map[string]string, len(o.Resources.Limits)) for k3, v3 := range o.Resources.Limits { cp.Resources.Limits[k3] = v3 } } return cp } // DeepCopy generates a deep copy of AuditPolicyConfigSpec. func (o AuditPolicyConfigSpec) DeepCopy() AuditPolicyConfigSpec { var cp AuditPolicyConfigSpec = o if o.Config != nil { cp.Config = make(map[string]any, len(o.Config)) for k2, v2 := range o.Config { cp.Config[k2] = v2 } } return cp } // DeepCopy generates a deep copy of AuthorizationConfigSpec. func (o AuthorizationConfigSpec) DeepCopy() AuthorizationConfigSpec { var cp AuthorizationConfigSpec = o if o.Config != nil { cp.Config = make([]AuthorizationAuthorizersSpec, len(o.Config)) copy(cp.Config, o.Config) for i2 := range o.Config { if o.Config[i2].Webhook != nil { cp.Config[i2].Webhook = make(map[string]any, len(o.Config[i2].Webhook)) for k4, v4 := range o.Config[i2].Webhook { cp.Config[i2].Webhook[k4] = v4 } } } } return cp } // DeepCopy generates a deep copy of BootstrapManifestsConfigSpec. func (o BootstrapManifestsConfigSpec) DeepCopy() BootstrapManifestsConfigSpec { var cp BootstrapManifestsConfigSpec = o if o.PodCIDRs != nil { cp.PodCIDRs = make([]string, len(o.PodCIDRs)) copy(cp.PodCIDRs, o.PodCIDRs) } if o.ProxyArgs != nil { cp.ProxyArgs = make([]string, len(o.ProxyArgs)) copy(cp.ProxyArgs, o.ProxyArgs) } if o.FlannelExtraArgs != nil { cp.FlannelExtraArgs = make([]string, len(o.FlannelExtraArgs)) copy(cp.FlannelExtraArgs, o.FlannelExtraArgs) } return cp } // DeepCopy generates a deep copy of ConfigStatusSpec. func (o ConfigStatusSpec) DeepCopy() ConfigStatusSpec { var cp ConfigStatusSpec = o return cp } // DeepCopy generates a deep copy of ControllerManagerConfigSpec. func (o ControllerManagerConfigSpec) DeepCopy() ControllerManagerConfigSpec { var cp ControllerManagerConfigSpec = o if o.PodCIDRs != nil { cp.PodCIDRs = make([]string, len(o.PodCIDRs)) copy(cp.PodCIDRs, o.PodCIDRs) } if o.ServiceCIDRs != nil { cp.ServiceCIDRs = make([]string, len(o.ServiceCIDRs)) copy(cp.ServiceCIDRs, o.ServiceCIDRs) } if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } if o.ExtraVolumes != nil { cp.ExtraVolumes = make([]ExtraVolume, len(o.ExtraVolumes)) copy(cp.ExtraVolumes, o.ExtraVolumes) } if o.EnvironmentVariables != nil { cp.EnvironmentVariables = make(map[string]string, len(o.EnvironmentVariables)) for k2, v2 := range o.EnvironmentVariables { cp.EnvironmentVariables[k2] = v2 } } if o.Resources.Requests != nil { cp.Resources.Requests = make(map[string]string, len(o.Resources.Requests)) for k3, v3 := range o.Resources.Requests { cp.Resources.Requests[k3] = v3 } } if o.Resources.Limits != nil { cp.Resources.Limits = make(map[string]string, len(o.Resources.Limits)) for k3, v3 := range o.Resources.Limits { cp.Resources.Limits[k3] = v3 } } return cp } // DeepCopy generates a deep copy of EndpointSpec. func (o EndpointSpec) DeepCopy() EndpointSpec { var cp EndpointSpec = o if o.Addresses != nil { cp.Addresses = make([]netip.Addr, len(o.Addresses)) copy(cp.Addresses, o.Addresses) } if o.Hosts != nil { cp.Hosts = make([]string, len(o.Hosts)) copy(cp.Hosts, o.Hosts) } return cp } // DeepCopy generates a deep copy of ExtraManifestsConfigSpec. func (o ExtraManifestsConfigSpec) DeepCopy() ExtraManifestsConfigSpec { var cp ExtraManifestsConfigSpec = o if o.ExtraManifests != nil { cp.ExtraManifests = make([]ExtraManifest, len(o.ExtraManifests)) copy(cp.ExtraManifests, o.ExtraManifests) for i2 := range o.ExtraManifests { if o.ExtraManifests[i2].ExtraHeaders != nil { cp.ExtraManifests[i2].ExtraHeaders = make(map[string]string, len(o.ExtraManifests[i2].ExtraHeaders)) for k4, v4 := range o.ExtraManifests[i2].ExtraHeaders { cp.ExtraManifests[i2].ExtraHeaders[k4] = v4 } } } } return cp } // DeepCopy generates a deep copy of KubeletLifecycleSpec. func (o KubeletLifecycleSpec) DeepCopy() KubeletLifecycleSpec { var cp KubeletLifecycleSpec = o return cp } // DeepCopy generates a deep copy of KubePrismConfigSpec. func (o KubePrismConfigSpec) DeepCopy() KubePrismConfigSpec { var cp KubePrismConfigSpec = o if o.Endpoints != nil { cp.Endpoints = make([]KubePrismEndpoint, len(o.Endpoints)) copy(cp.Endpoints, o.Endpoints) } return cp } // DeepCopy generates a deep copy of KubePrismEndpointsSpec. func (o KubePrismEndpointsSpec) DeepCopy() KubePrismEndpointsSpec { var cp KubePrismEndpointsSpec = o if o.Endpoints != nil { cp.Endpoints = make([]KubePrismEndpoint, len(o.Endpoints)) copy(cp.Endpoints, o.Endpoints) } return cp } // DeepCopy generates a deep copy of KubePrismStatusesSpec. func (o KubePrismStatusesSpec) DeepCopy() KubePrismStatusesSpec { var cp KubePrismStatusesSpec = o return cp } // DeepCopy generates a deep copy of KubeletSpecSpec. func (o KubeletSpecSpec) DeepCopy() KubeletSpecSpec { var cp KubeletSpecSpec = o if o.Args != nil { cp.Args = make([]string, len(o.Args)) copy(cp.Args, o.Args) } if o.ExtraMounts != nil { cp.ExtraMounts = make([]specs.Mount, len(o.ExtraMounts)) copy(cp.ExtraMounts, o.ExtraMounts) for i2 := range o.ExtraMounts { if o.ExtraMounts[i2].Options != nil { cp.ExtraMounts[i2].Options = make([]string, len(o.ExtraMounts[i2].Options)) copy(cp.ExtraMounts[i2].Options, o.ExtraMounts[i2].Options) } if o.ExtraMounts[i2].UIDMappings != nil { cp.ExtraMounts[i2].UIDMappings = make([]specs.LinuxIDMapping, len(o.ExtraMounts[i2].UIDMappings)) copy(cp.ExtraMounts[i2].UIDMappings, o.ExtraMounts[i2].UIDMappings) } if o.ExtraMounts[i2].GIDMappings != nil { cp.ExtraMounts[i2].GIDMappings = make([]specs.LinuxIDMapping, len(o.ExtraMounts[i2].GIDMappings)) copy(cp.ExtraMounts[i2].GIDMappings, o.ExtraMounts[i2].GIDMappings) } } } if o.Config != nil { cp.Config = make(map[string]any, len(o.Config)) for k2, v2 := range o.Config { cp.Config[k2] = v2 } } if o.CredentialProviderConfig != nil { cp.CredentialProviderConfig = make(map[string]any, len(o.CredentialProviderConfig)) for k2, v2 := range o.CredentialProviderConfig { cp.CredentialProviderConfig[k2] = v2 } } return cp } // DeepCopy generates a deep copy of ManifestSpec. func (o ManifestSpec) DeepCopy() ManifestSpec { var cp ManifestSpec = o if o.Items != nil { cp.Items = make([]SingleManifest, len(o.Items)) copy(cp.Items, o.Items) for i2 := range o.Items { if o.Items[i2].Object != nil { cp.Items[i2].Object = make(map[string]any, len(o.Items[i2].Object)) for k4, v4 := range o.Items[i2].Object { cp.Items[i2].Object[k4] = v4 } } } } return cp } // DeepCopy generates a deep copy of ManifestStatusSpec. func (o ManifestStatusSpec) DeepCopy() ManifestStatusSpec { var cp ManifestStatusSpec = o if o.ManifestsApplied != nil { cp.ManifestsApplied = make([]string, len(o.ManifestsApplied)) copy(cp.ManifestsApplied, o.ManifestsApplied) } return cp } // DeepCopy generates a deep copy of NodeAnnotationSpecSpec. func (o NodeAnnotationSpecSpec) DeepCopy() NodeAnnotationSpecSpec { var cp NodeAnnotationSpecSpec = o return cp } // DeepCopy generates a deep copy of NodeCordonedSpecSpec. func (o NodeCordonedSpecSpec) DeepCopy() NodeCordonedSpecSpec { var cp NodeCordonedSpecSpec = o return cp } // DeepCopy generates a deep copy of NodeLabelSpecSpec. func (o NodeLabelSpecSpec) DeepCopy() NodeLabelSpecSpec { var cp NodeLabelSpecSpec = o return cp } // DeepCopy generates a deep copy of NodeTaintSpecSpec. func (o NodeTaintSpecSpec) DeepCopy() NodeTaintSpecSpec { var cp NodeTaintSpecSpec = o return cp } // DeepCopy generates a deep copy of KubeletConfigSpec. func (o KubeletConfigSpec) DeepCopy() KubeletConfigSpec { var cp KubeletConfigSpec = o if o.ClusterDNS != nil { cp.ClusterDNS = make([]string, len(o.ClusterDNS)) copy(cp.ClusterDNS, o.ClusterDNS) } if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } if o.ExtraMounts != nil { cp.ExtraMounts = make([]specs.Mount, len(o.ExtraMounts)) copy(cp.ExtraMounts, o.ExtraMounts) for i2 := range o.ExtraMounts { if o.ExtraMounts[i2].Options != nil { cp.ExtraMounts[i2].Options = make([]string, len(o.ExtraMounts[i2].Options)) copy(cp.ExtraMounts[i2].Options, o.ExtraMounts[i2].Options) } if o.ExtraMounts[i2].UIDMappings != nil { cp.ExtraMounts[i2].UIDMappings = make([]specs.LinuxIDMapping, len(o.ExtraMounts[i2].UIDMappings)) copy(cp.ExtraMounts[i2].UIDMappings, o.ExtraMounts[i2].UIDMappings) } if o.ExtraMounts[i2].GIDMappings != nil { cp.ExtraMounts[i2].GIDMappings = make([]specs.LinuxIDMapping, len(o.ExtraMounts[i2].GIDMappings)) copy(cp.ExtraMounts[i2].GIDMappings, o.ExtraMounts[i2].GIDMappings) } } } if o.ExtraConfig != nil { cp.ExtraConfig = make(map[string]any, len(o.ExtraConfig)) for k2, v2 := range o.ExtraConfig { cp.ExtraConfig[k2] = v2 } } if o.CredentialProviderConfig != nil { cp.CredentialProviderConfig = make(map[string]any, len(o.CredentialProviderConfig)) for k2, v2 := range o.CredentialProviderConfig { cp.CredentialProviderConfig[k2] = v2 } } return cp } // DeepCopy generates a deep copy of NodeIPSpec. func (o NodeIPSpec) DeepCopy() NodeIPSpec { var cp NodeIPSpec = o if o.Addresses != nil { cp.Addresses = make([]netip.Addr, len(o.Addresses)) copy(cp.Addresses, o.Addresses) } return cp } // DeepCopy generates a deep copy of NodeIPConfigSpec. func (o NodeIPConfigSpec) DeepCopy() NodeIPConfigSpec { var cp NodeIPConfigSpec = o if o.ValidSubnets != nil { cp.ValidSubnets = make([]string, len(o.ValidSubnets)) copy(cp.ValidSubnets, o.ValidSubnets) } if o.ExcludeSubnets != nil { cp.ExcludeSubnets = make([]string, len(o.ExcludeSubnets)) copy(cp.ExcludeSubnets, o.ExcludeSubnets) } return cp } // DeepCopy generates a deep copy of NodeStatusSpec. func (o NodeStatusSpec) DeepCopy() NodeStatusSpec { var cp NodeStatusSpec = o if o.Labels != nil { cp.Labels = make(map[string]string, len(o.Labels)) for k2, v2 := range o.Labels { cp.Labels[k2] = v2 } } if o.Annotations != nil { cp.Annotations = make(map[string]string, len(o.Annotations)) for k2, v2 := range o.Annotations { cp.Annotations[k2] = v2 } } if o.PodCIDRs != nil { cp.PodCIDRs = make([]netip.Prefix, len(o.PodCIDRs)) copy(cp.PodCIDRs, o.PodCIDRs) } return cp } // DeepCopy generates a deep copy of NodenameSpec. func (o NodenameSpec) DeepCopy() NodenameSpec { var cp NodenameSpec = o return cp } // DeepCopy generates a deep copy of SchedulerConfigSpec. func (o SchedulerConfigSpec) DeepCopy() SchedulerConfigSpec { var cp SchedulerConfigSpec = o if o.ExtraArgs != nil { cp.ExtraArgs = make(map[string]ArgValues, len(o.ExtraArgs)) for k2, v2 := range o.ExtraArgs { var cp_ExtraArgs_v2 ArgValues if v2.Values != nil { cp_ExtraArgs_v2.Values = make([]string, len(v2.Values)) copy(cp_ExtraArgs_v2.Values, v2.Values) } cp.ExtraArgs[k2] = cp_ExtraArgs_v2 } } if o.ExtraVolumes != nil { cp.ExtraVolumes = make([]ExtraVolume, len(o.ExtraVolumes)) copy(cp.ExtraVolumes, o.ExtraVolumes) } if o.EnvironmentVariables != nil { cp.EnvironmentVariables = make(map[string]string, len(o.EnvironmentVariables)) for k2, v2 := range o.EnvironmentVariables { cp.EnvironmentVariables[k2] = v2 } } if o.Resources.Requests != nil { cp.Resources.Requests = make(map[string]string, len(o.Resources.Requests)) for k3, v3 := range o.Resources.Requests { cp.Resources.Requests[k3] = v3 } } if o.Resources.Limits != nil { cp.Resources.Limits = make(map[string]string, len(o.Resources.Limits)) for k3, v3 := range o.Resources.Limits { cp.Resources.Limits[k3] = v3 } } if o.Config != nil { cp.Config = make(map[string]any, len(o.Config)) for k2, v2 := range o.Config { cp.Config[k2] = v2 } } return cp } // DeepCopy generates a deep copy of SecretsStatusSpec. func (o SecretsStatusSpec) DeepCopy() SecretsStatusSpec { var cp SecretsStatusSpec = o return cp } // DeepCopy generates a deep copy of StaticPodSpec. func (o StaticPodSpec) DeepCopy() StaticPodSpec { var cp StaticPodSpec = o if o.Pod != nil { cp.Pod = make(map[string]any, len(o.Pod)) for k2, v2 := range o.Pod { cp.Pod[k2] = v2 } } return cp } // DeepCopy generates a deep copy of StaticPodStatusSpec. func (o StaticPodStatusSpec) DeepCopy() StaticPodStatusSpec { var cp StaticPodStatusSpec = o if o.PodStatus != nil { cp.PodStatus = make(map[string]any, len(o.PodStatus)) for k2, v2 := range o.PodStatus { cp.PodStatus[k2] = v2 } } return cp } // DeepCopy generates a deep copy of StaticPodServerStatusSpec. func (o StaticPodServerStatusSpec) DeepCopy() StaticPodServerStatusSpec { var cp StaticPodServerStatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/k8s/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "net/netip" "slices" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EndpointType is type of Endpoint resource. const EndpointType = resource.Type("Endpoints.kubernetes.talos.dev") // ControlPlaneAPIServerEndpointsID is resource ID for kube-apiserver based Endpoints. const ControlPlaneAPIServerEndpointsID = resource.ID("kube-apiserver") // ControlPlaneDiscoveredEndpointsID is resource ID for cluster discovery based Endpoints. const ControlPlaneDiscoveredEndpointsID = resource.ID("discovery") // ControlPlaneKubernetesEndpointsID is resource ID for control plane endpoint-based Endpoints. const ControlPlaneKubernetesEndpointsID = resource.ID("controlplane") // Endpoint resource holds definition of rendered secrets. type Endpoint = typed.Resource[EndpointSpec, EndpointExtension] // EndpointSpec describes a list of endpoints to connect to. // //gotagsrewrite:gen type EndpointSpec struct { Addresses []netip.Addr `yaml:"addresses" protobuf:"1"` Hosts []string `yaml:"hosts" protobuf:"2"` } // NewEndpoint initializes the Endpoint resource. func NewEndpoint(namespace resource.Namespace, id resource.ID) *Endpoint { return typed.NewResource[EndpointSpec, EndpointExtension]( resource.NewMetadata(namespace, EndpointType, id, resource.VersionUndefined), EndpointSpec{}, ) } // EndpointExtension provides auxiliary methods for Endpoint. type EndpointExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (EndpointExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EndpointType, Aliases: []resource.Type{}, DefaultNamespace: ControlPlaneNamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Addresses", JSONPath: "{.addresses}", }, { Name: "Hosts", JSONPath: "{.hosts}", }, }, } } // EndpointList is a flattened list of endpoints. type EndpointList struct { Addresses []netip.Addr Hosts []string } // Merge endpoints from multiple Endpoint resources into a single list. func (l EndpointList) Merge(endpoint *Endpoint) EndpointList { for _, ip := range endpoint.TypedSpec().Addresses { idx, _ := slices.BinarySearchFunc(l.Addresses, ip, func(a netip.Addr, target netip.Addr) int { return a.Compare(target) }) if idx < len(l.Addresses) && l.Addresses[idx].Compare(ip) == 0 { continue } l.Addresses = slices.Insert(l.Addresses, idx, ip) } for _, host := range endpoint.TypedSpec().Hosts { idx, _ := slices.BinarySearch(l.Hosts, host) if idx < len(l.Hosts) && l.Hosts[idx] == host { continue } l.Hosts = slices.Insert(l.Hosts, idx, host) } return l } // IsEmpty checks if the EndpointList is empty. func (l EndpointList) IsEmpty() bool { return len(l.Addresses) == 0 && len(l.Hosts) == 0 } // Strings returns a slice of formatted endpoints to string. func (l EndpointList) Strings() []string { return slices.Concat( xslices.Map(l.Addresses, netip.Addr.String), l.Hosts, ) } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EndpointSpec](EndpointType, &Endpoint{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/endpoint_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) func TestEndpointList(t *testing.T) { t.Parallel() var l k8s.EndpointList e1 := k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, "1") e1.TypedSpec().Addresses = []netip.Addr{ netip.MustParseAddr("172.20.0.2"), netip.MustParseAddr("172.20.0.3"), } e2 := k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, "2") e2.TypedSpec().Addresses = []netip.Addr{ netip.MustParseAddr("172.20.0.4"), netip.MustParseAddr("172.20.0.3"), } l = l.Merge(e1) l = l.Merge(e2) assert.Equal(t, []string{"172.20.0.2", "172.20.0.3", "172.20.0.4"}, l.Strings()) } func TestEndpointListWithHosts(t *testing.T) { t.Parallel() var l k8s.EndpointList assert.True(t, l.IsEmpty()) e1 := k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, "1") e1.TypedSpec().Addresses = []netip.Addr{ netip.MustParseAddr("172.20.0.2"), } e1.TypedSpec().Hosts = []string{ "host1.example.com", "host2.example.com", } e2 := k8s.NewEndpoint(k8s.ControlPlaneNamespaceName, "2") e2.TypedSpec().Addresses = []netip.Addr{ netip.MustParseAddr("172.20.0.3"), } e2.TypedSpec().Hosts = []string{ "host2.example.com", "host3.example.com", } l = l.Merge(e1) l = l.Merge(e2) assert.Equal(t, []string{ "172.20.0.2", "172.20.0.3", "host1.example.com", "host2.example.com", "host3.example.com", }, l.Strings(), ) } ================================================ FILE: pkg/machinery/resources/k8s/extramanifests_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ExtraManifestsConfigType is type of ExtraManifestsConfig resource. const ExtraManifestsConfigType = resource.Type("ExtraManifestsConfigs.kubernetes.talos.dev") // ExtraManifestsConfigID is a singleton resource ID for ExtraManifestsConfig. const ExtraManifestsConfigID = resource.ID("extra-manifests") // ExtraManifestsConfig represents configuration for extra bootstrap manifests. type ExtraManifestsConfig = typed.Resource[ExtraManifestsConfigSpec, ExtraManifestsConfigExtension] // ExtraManifestsConfigSpec is configuration for extra bootstrap manifests. // //gotagsrewrite:gen type ExtraManifestsConfigSpec struct { ExtraManifests []ExtraManifest `yaml:"extraManifests" protobuf:"1"` } // ExtraManifest defines a single extra manifest to download. // //gotagsrewrite:gen type ExtraManifest struct { Name string `yaml:"name" protobuf:"1"` URL string `yaml:"url" protobuf:"2"` Priority string `yaml:"priority" protobuf:"3"` ExtraHeaders map[string]string `yaml:"extraHeaders" protobuf:"4"` InlineManifest string `yaml:"inlineManifest" protobuf:"5"` } // NewExtraManifestsConfig returns new ExtraManifestsConfig resource. func NewExtraManifestsConfig() *ExtraManifestsConfig { return typed.NewResource[ExtraManifestsConfigSpec, ExtraManifestsConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, ExtraManifestsConfigType, ExtraManifestsConfigID, resource.VersionUndefined), ExtraManifestsConfigSpec{}) } // ExtraManifestsConfigExtension defines ExtraManifestsConfig resource definition. type ExtraManifestsConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ExtraManifestsConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ExtraManifestsConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ExtraManifestsConfigSpec](ExtraManifestsConfigType, &ExtraManifestsConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/k8s.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import "github.com/cosi-project/runtime/pkg/resource" //go:generate go tool github.com/siderolabs/deep-copy -type AdmissionControlConfigSpec -type APIServerConfigSpec -type AuditPolicyConfigSpec -type AuthorizationConfigSpec -type BootstrapManifestsConfigSpec -type ConfigStatusSpec -type ControllerManagerConfigSpec -type EndpointSpec -type ExtraManifestsConfigSpec -type KubeletLifecycleSpec -type KubePrismConfigSpec -type KubePrismEndpointsSpec -type KubePrismStatusesSpec -type KubeletSpecSpec -type ManifestSpec -type ManifestStatusSpec -type NodeAnnotationSpecSpec -type NodeCordonedSpecSpec -type NodeLabelSpecSpec -type NodeTaintSpecSpec -type KubeletConfigSpec -type NodeIPSpec -type NodeIPConfigSpec -type NodeStatusSpec -type NodenameSpec -type SchedulerConfigSpec -type SecretsStatusSpec -type StaticPodSpec -type StaticPodStatusSpec -type StaticPodServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains resources supporting Kubernetes components on all node types. const NamespaceName resource.Namespace = "k8s" // ControlPlaneNamespaceName contains resources supporting Kubernetes control plane. const ControlPlaneNamespaceName resource.Namespace = "controlplane" // NodeAddressFilterOnlyK8s is the ID for the node address filter which leaves only Kubernetes IPs. const NodeAddressFilterOnlyK8s = "only-k8s" // NodeAddressFilterNoK8s is the ID for the node address filter which removes any Kubernetes IPs. const NodeAddressFilterNoK8s = "no-k8s" // APIServerID is a generic ID for resources related to kube-apiserver. const APIServerID = "kube-apiserver" // ControllerManagerID is a generic ID for resources related to kube-controller-manager. const ControllerManagerID = "kube-controller-manager" // SchedulerID is a generic ID for resources related to kube-scheduler. const SchedulerID = "kube-scheduler" ================================================ FILE: pkg/machinery/resources/k8s/k8s_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/resources/k8s" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &k8s.AdmissionControlConfig{}, &k8s.APIServerConfig{}, &k8s.KubePrismEndpoints{}, &k8s.AuditPolicyConfig{}, &k8s.ConfigStatus{}, &k8s.ControllerManagerConfig{}, &k8s.Endpoint{}, &k8s.ExtraManifestsConfig{}, &k8s.KubeletConfig{}, &k8s.KubeletLifecycle{}, &k8s.KubeletSpec{}, &k8s.KubePrismStatuses{}, &k8s.KubePrismConfig{}, &k8s.ManifestStatus{}, &k8s.Manifest{}, &k8s.BootstrapManifestsConfig{}, &k8s.NodeAnnotationSpec{}, &k8s.NodeCordonedSpec{}, &k8s.NodeLabelSpec{}, &k8s.NodeTaintSpec{}, &k8s.Nodename{}, &k8s.NodeIP{}, &k8s.NodeIPConfig{}, &k8s.SchedulerConfig{}, &k8s.SecretsStatus{}, &k8s.StaticPodStatus{}, &k8s.StaticPod{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } func TestKubeletConfig(t *testing.T) { cfg := k8s.NewKubeletConfig(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v1.0.0" cfg.TypedSpec().ClusterDNS = []string{"10.96.0.10"} cfg.TypedSpec().ClusterDomain = "cluster.local" cfg.TypedSpec().ExtraArgs = map[string]k8s.ArgValues{"foo": {Values: []string{"bar"}}} cfg.TypedSpec().ExtraMounts = []specs.Mount{ { Destination: "/tmp", Source: "/var", Type: "tmpfs", }, } cfg.TypedSpec().CloudProviderExternal = true res, err := protobuf.FromResource(cfg) require.NoError(t, err) require.NotNil(t, res) } func TestKubeletSpec(t *testing.T) { cfg := k8s.NewKubeletSpec(k8s.NamespaceName, k8s.KubeletID) cfg.TypedSpec().Image = "kubelet:v1.0.0" cfg.TypedSpec().ExtraMounts = []specs.Mount{ { Destination: "/tmp", Source: "/var", Type: "tmpfs", }, } res, err := protobuf.FromResource(cfg) require.NoError(t, err) require.NotNil(t, res) } ================================================ FILE: pkg/machinery/resources/k8s/kubelet_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubeletConfigType is type of KubeletConfig resource. const KubeletConfigType = resource.Type("KubeletConfigs.kubernetes.talos.dev") // KubeletID is the ID of KubeletConfig resource. const KubeletID = resource.ID("kubelet") // KubeletConfig resource holds source of kubelet configuration. type KubeletConfig = typed.Resource[KubeletConfigSpec, KubeletConfigExtension] // KubeletConfigSpec holds the source of kubelet configuration. // //gotagsrewrite:gen type KubeletConfigSpec struct { Image string `yaml:"image" protobuf:"1"` ClusterDNS []string `yaml:"clusterDNS" protobuf:"2"` ClusterDomain string `yaml:"clusterDomain" protobuf:"3"` ExtraArgs map[string]ArgValues `yaml:"extraArgs,omitempty" protobuf:"4"` ExtraMounts []specs.Mount `yaml:"extraMounts,omitempty" protobuf:"5"` ExtraConfig map[string]any `yaml:"extraConfig,omitempty" protobuf:"6"` CloudProviderExternal bool `yaml:"cloudProviderExternal" protobuf:"7"` DefaultRuntimeSeccompEnabled bool `yaml:"defaultRuntimeSeccompEnabled" protobuf:"8"` SkipNodeRegistration bool `yaml:"skipNodeRegistration" protobuf:"9"` StaticPodListURL string `yaml:"staticPodListURL" protobuf:"10"` DisableManifestsDirectory bool `yaml:"disableManifestsDirectory" protobuf:"11"` EnableFSQuotaMonitoring bool `yaml:"enableFSQuotaMonitoring" protobuf:"12"` CredentialProviderConfig map[string]any `yaml:"credentialProviderConfig,omitempty" protobuf:"13"` AllowSchedulingOnControlPlane bool `yaml:"allowSchedulingOnControlPlane" protobuf:"14"` } // NewKubeletConfig initializes an empty KubeletConfig resource. func NewKubeletConfig(namespace resource.Namespace, id resource.ID) *KubeletConfig { return typed.NewResource[KubeletConfigSpec, KubeletConfigExtension]( resource.NewMetadata(namespace, KubeletConfigType, id, resource.VersionUndefined), KubeletConfigSpec{}, ) } // KubeletConfigExtension provides auxiliary methods for KubeletConfig. type KubeletConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubeletConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubeletConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubeletConfigSpec](KubeletConfigType, &KubeletConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/kubelet_lifecycle.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubeletLifecycleType is type of KubeletLifecycle resource. const KubeletLifecycleType = resource.Type("KubeletLifecycles.kubernetes.talos.dev") // KubeletLifecycleID is the singleton ID of the resource. const KubeletLifecycleID = resource.ID("kubelet") // KubeletLifecycle resource exists to signal that the kubelet pods are running. // // Components might put finalizers on the KubeletLifecycle resource to signal that additional // actions should be taken before the kubelet is about to be shut down. // // KubeletLifecycle is mostly about status of the workloads kubelet is running vs. // the actual status of the kubelet service itself. type KubeletLifecycle = typed.Resource[KubeletLifecycleSpec, KubeletLifecycleExtension] // KubeletLifecycleSpec is empty. type KubeletLifecycleSpec struct{} // NewKubeletLifecycle initializes an empty KubeletLifecycle resource. func NewKubeletLifecycle(namespace resource.Namespace, id resource.ID) *KubeletLifecycle { return typed.NewResource[KubeletLifecycleSpec, KubeletLifecycleExtension]( resource.NewMetadata(namespace, KubeletLifecycleType, id, resource.VersionUndefined), KubeletLifecycleSpec{}, ) } // KubeletLifecycleExtension provides auxiliary methods for KubeletLifecycle. type KubeletLifecycleExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubeletLifecycleExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubeletLifecycleType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubeletLifecycleSpec](KubeletLifecycleType, &KubeletLifecycle{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/kubelet_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/opencontainers/runtime-spec/specs-go" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubeletSpecType is type of KubeletSpec resource. const KubeletSpecType = resource.Type("KubeletSpecs.kubernetes.talos.dev") // KubeletSpec resource holds final definition of kubelet runtime configuration. type KubeletSpec = typed.Resource[KubeletSpecSpec, KubeletSpecExtension] // KubeletSpecSpec holds the source of kubelet configuration. // //gotagsrewrite:gen type KubeletSpecSpec struct { Image string `yaml:"image" protobuf:"1"` Args []string `yaml:"args,omitempty" protobuf:"2"` ExtraMounts []specs.Mount `yaml:"extraMounts,omitempty" protobuf:"3"` ExpectedNodename string `yaml:"expectedNodename,omitempty" protobuf:"4"` Config map[string]any `yaml:"config" protobuf:"5"` CredentialProviderConfig map[string]any `yaml:"credentialProviderConfig,omitempty" protobuf:"6"` } // NewKubeletSpec initializes an empty KubeletSpec resource. func NewKubeletSpec(namespace resource.Namespace, id resource.ID) *KubeletSpec { return typed.NewResource[KubeletSpecSpec, KubeletSpecExtension]( resource.NewMetadata(namespace, KubeletSpecType, id, resource.VersionUndefined), KubeletSpecSpec{}, ) } // KubeletSpecExtension provides auxiliary methods for KubeletSpec. type KubeletSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubeletSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubeletSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubeletSpecSpec](KubeletSpecType, &KubeletSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/kubeprism.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubePrismStatusesType is type of KubePrismStatuses resource. const KubePrismStatusesType = resource.Type("KubePrismStatuses.kubernetes.talos.dev") // KubePrismStatusesID the singleton balancer health data resource ID. const KubePrismStatusesID = resource.ID("k8s-loadbalancer") // KubePrismStatuses resource holds load balancer health data. type KubePrismStatuses = typed.Resource[KubePrismStatusesSpec, KubePrismStatusesExtension] // NewKubePrismStatuses initializes an KubePrismStatuses resource. func NewKubePrismStatuses(namespace resource.Namespace, id resource.ID) *KubePrismStatuses { return typed.NewResource[KubePrismStatusesSpec, KubePrismStatusesExtension]( resource.NewMetadata(namespace, KubePrismStatusesType, id, resource.VersionUndefined), KubePrismStatusesSpec{}, ) } // KubePrismStatusesSpec describes KubePrismStatuses data. // //gotagsrewrite:gen type KubePrismStatusesSpec struct { Host string `yaml:"host" protobuf:"1"` Healthy bool `yaml:"healthy" protobuf:"2"` } // KubePrismStatusesExtension provides auxiliary methods for KubePrismStatuses. type KubePrismStatusesExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubePrismStatusesExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubePrismStatusesType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "HOST", JSONPath: "{.host}", }, { Name: "HEALTHY", JSONPath: "{.healthy}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubePrismStatusesSpec](KubePrismStatusesType, &KubePrismStatuses{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/kubeprism_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubePrismConfigType is type of KubePrismConfig resource. const KubePrismConfigType = resource.Type("KubePrismConfigs.kubernetes.talos.dev") // KubePrismConfigID the singleton config resource ID. const KubePrismConfigID = resource.ID("k8s-loadbalancer-config") // KubePrismConfig resource holds load balancer health data. type KubePrismConfig = typed.Resource[KubePrismConfigSpec, KubePrismConfigExtension] // NewKubePrismConfig initializes an KubePrismConfig resource. func NewKubePrismConfig(namespace resource.Namespace, id resource.ID) *KubePrismConfig { return typed.NewResource[KubePrismConfigSpec, KubePrismConfigExtension]( resource.NewMetadata(namespace, KubePrismConfigType, id, resource.VersionUndefined), KubePrismConfigSpec{}, ) } // KubePrismConfigSpec describes KubePrismConfig data. // //gotagsrewrite:gen type KubePrismConfigSpec struct { Host string `yaml:"host" protobuf:"1"` Port int `yaml:"port" protobuf:"2"` Endpoints []KubePrismEndpoint `yaml:"endpoints" protobuf:"3"` } // KubePrismConfigExtension provides auxiliary methods for KubePrismConfig. type KubePrismConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubePrismConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubePrismConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Host", JSONPath: "{.host}", }, { Name: "Port", JSONPath: "{.port}", }, { Name: "Endpoints", JSONPath: "{.endpoints}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubePrismConfigSpec](KubePrismConfigType, &KubePrismConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/kubeprism_endpoints.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubePrismEndpointsType is type of KubePrismEndpoints resource. const KubePrismEndpointsType = resource.Type("KubePrismEndpoints.kubernetes.talos.dev") // KubePrismEndpointsID the singleton balancer data resource ID. const KubePrismEndpointsID = resource.ID("k8s-cluster") // KubePrismEndpoints resource holds endpoints data. type KubePrismEndpoints = typed.Resource[KubePrismEndpointsSpec, KubePrismEndpointsExtension] // NewKubePrismEndpoints initializes an KubePrismEndpoints resource. func NewKubePrismEndpoints(namespace resource.Namespace, id resource.ID) *KubePrismEndpoints { return typed.NewResource[KubePrismEndpointsSpec, KubePrismEndpointsExtension]( resource.NewMetadata(namespace, KubePrismEndpointsType, id, resource.VersionUndefined), KubePrismEndpointsSpec{}, ) } // KubePrismEndpointsSpec describes KubePrismEndpoints configuration. // //gotagsrewrite:gen type KubePrismEndpointsSpec struct { Endpoints []KubePrismEndpoint `yaml:"endpoints" protobuf:"1"` } // KubePrismEndpoint holds data for control plane endpoint. // //gotagsrewrite:gen type KubePrismEndpoint struct { Host string `yaml:"host" protobuf:"1"` Port uint32 `yaml:"port" protobuf:"2"` } // String returns string representation of KubePrismEndpoint. func (e KubePrismEndpoint) String() string { return fmt.Sprintf("host: %s, port: %d", e.Host, e.Port) } // KubePrismEndpointsExtension provides auxiliary methods for KubePrismEndpoints. type KubePrismEndpointsExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (KubePrismEndpointsExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubePrismEndpointsType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Hosts", JSONPath: "{.endpoints[*].host}", }, { Name: "Ports", JSONPath: "{.endpoints[*].port}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubePrismEndpointsSpec](KubePrismEndpointsType, &KubePrismEndpoints{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/manifest.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ManifestType is type of Manifest resource. const ManifestType = resource.Type("Manifests.kubernetes.talos.dev") // Manifest resource holds definition of kubelet static pod. type Manifest = typed.Resource[ManifestSpec, ManifestExtension] // ManifestSpec holds the Kubernetes resources spec. // //gotagsrewrite:gen type ManifestSpec struct { Items []SingleManifest `protobuf:"1" yaml:"items"` } // SingleManifest is a single manifest. // //gotagsrewrite:gen type SingleManifest struct { Object map[string]any `protobuf:"1" yaml:",inline"` } // MarshalYAML implements yaml.Marshaler. func (spec ManifestSpec) MarshalYAML() (any, error) { return spec.Items, nil } // NewManifest initializes an empty Manifest resource. func NewManifest(namespace resource.Namespace, id resource.ID) *Manifest { return typed.NewResource[ManifestSpec, ManifestExtension]( resource.NewMetadata(namespace, ManifestType, id, resource.VersionUndefined), ManifestSpec{}, ) } // ManifestExtension provides auxiliary methods for Manifest. type ManifestExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ManifestExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ManifestType, Aliases: []resource.Type{}, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ManifestSpec](ManifestType, &Manifest{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/manifest_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ManifestStatusType is type of ManifestStatus resource. const ManifestStatusType = resource.Type("ManifestStatuses.kubernetes.talos.dev") // ManifestStatusID is a singleton resource ID. const ManifestStatusID = resource.ID("manifests") // ManifestStatus resource holds definition of kubelet static pod. type ManifestStatus = typed.Resource[ManifestStatusSpec, ManifestStatusExtension] // ManifestStatusSpec describes manifest application status. // //gotagsrewrite:gen type ManifestStatusSpec struct { ManifestsApplied []string `yaml:"manifestsApplied" protobuf:"1"` } // NewManifestStatus initializes an empty ManifestStatus resource. func NewManifestStatus(namespace resource.Namespace) *ManifestStatus { return typed.NewResource[ManifestStatusSpec, ManifestStatusExtension]( resource.NewMetadata(namespace, ManifestStatusType, ManifestStatusID, resource.VersionUndefined), ManifestStatusSpec{}, ) } // ManifestStatusExtension provides auxiliary methods for ManifestStatus. type ManifestStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ManifestStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ManifestStatusType, Aliases: []resource.Type{}, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ManifestStatusSpec](ManifestStatusType, &ManifestStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/manifests_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // BootstrapManifestsConfigType is type of BootstrapManifestsConfig resource. const BootstrapManifestsConfigType = resource.Type("BootstrapManifestsConfigs.kubernetes.talos.dev") // BootstrapManifestsConfigID is a singleton resource ID for BootstrapManifestsConfig. const BootstrapManifestsConfigID = resource.ID("manifests") // BootstrapManifestsConfig represents configuration for bootstrap manifests. type BootstrapManifestsConfig = typed.Resource[BootstrapManifestsConfigSpec, BootstrapManifestsConfigExtension] // BootstrapManifestsConfigSpec is configuration for bootstrap manifests. // //gotagsrewrite:gen type BootstrapManifestsConfigSpec struct { Server string `yaml:"string" protobuf:"1"` ClusterDomain string `yaml:"clusterDomain" protobuf:"2"` PodCIDRs []string `yaml:"podCIDRs" protobuf:"3"` ProxyEnabled bool `yaml:"proxyEnabled" protobuf:"4"` ProxyImage string `yaml:"proxyImage" protobuf:"5"` ProxyArgs []string `yaml:"proxyArgs" protobuf:"6"` CoreDNSEnabled bool `yaml:"coreDNSEnabled" protobuf:"7"` CoreDNSImage string `yaml:"coreDNSImage" protobuf:"8"` DNSServiceIP string `yaml:"dnsServiceIP" protobuf:"9"` DNSServiceIPv6 string `yaml:"dnsServiceIPv6" protobuf:"10"` FlannelEnabled bool `yaml:"flannelEnabled" protobuf:"11"` FlannelImage string `yaml:"flannelImage" protobuf:"12"` FlannelExtraArgs []string `yaml:"flannelExtraArgs" protobuf:"16"` FlannelKubeServiceHost string `yaml:"flannelKubeServiceHost" protobuf:"17"` FlannelKubeServicePort string `yaml:"flannelKubeServicePort" protobuf:"18"` FlannelKubeNetworkPoliciesEnabled bool `yaml:"flannelKubeNetworkPoliciesEnabled" protobuf:"19"` FlannelKubeNetworkPoliciesImage string `yaml:"flannelKubeNetworkPoliciesImage" protobuf:"20"` PodSecurityPolicyEnabled bool `yaml:"podSecurityPolicyEnabled" protobuf:"14"` TalosAPIServiceEnabled bool `yaml:"talosAPIServiceEnabled" protobuf:"15"` CNIName string `yaml:"cniName" protobuf:"21"` } // NewBootstrapManifestsConfig returns new BootstrapManifestsConfig resource. func NewBootstrapManifestsConfig() *BootstrapManifestsConfig { return typed.NewResource[BootstrapManifestsConfigSpec, BootstrapManifestsConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, BootstrapManifestsConfigType, BootstrapManifestsConfigID, resource.VersionUndefined), BootstrapManifestsConfigSpec{}) } // BootstrapManifestsConfigExtension defines BootstrapManifestsConfig resource definition. type BootstrapManifestsConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (BootstrapManifestsConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: BootstrapManifestsConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[BootstrapManifestsConfigSpec](BootstrapManifestsConfigType, &BootstrapManifestsConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/node_annotation_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s //nolint:dupl import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeAnnotationSpecType is the type. const NodeAnnotationSpecType = resource.Type("NodeAnnotationSpecs.k8s.talos.dev") // NodeAnnotationSpecSpec represents an annoation that's attached to a Talos node. // //gotagsrewrite:gen type NodeAnnotationSpecSpec struct { Key string `yaml:"key" protobuf:"1"` Value string `yaml:"value" protobuf:"2"` } // NodeAnnotationSpec ... type NodeAnnotationSpec = typed.Resource[NodeAnnotationSpecSpec, NodeAnnotationSpecExtension] // NewNodeAnnotationSpec initializes a NodeAnnotation resource. func NewNodeAnnotationSpec(id resource.ID) *NodeAnnotationSpec { return typed.NewResource[NodeAnnotationSpecSpec, NodeAnnotationSpecExtension]( resource.NewMetadata(NamespaceName, NodeAnnotationSpecType, id, resource.VersionUndefined), NodeAnnotationSpecSpec{}, ) } // NodeAnnotationSpecExtension provides auxiliary methods for NodeAnnotation. type NodeAnnotationSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (NodeAnnotationSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeAnnotationSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, PrintColumns: []meta.PrintColumn{ { Name: "Value", JSONPath: "{.value}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeAnnotationSpecSpec](NodeAnnotationSpecType, &NodeAnnotationSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/node_cordoned_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeCordonedSpecType is the type. const NodeCordonedSpecType = resource.Type("NodeCordonedSpecs.k8s.talos.dev") // NodeCordonedSpecSpec represents an intention to make a node cordoned (unschedulable). // //gotagsrewrite:gen type NodeCordonedSpecSpec struct{} // NodeCordonedID is the ID of the NodeCordonedSpec resource. const NodeCordonedID = resource.ID("cordoned") // NodeCordonedSpec ... type NodeCordonedSpec = typed.Resource[NodeCordonedSpecSpec, NodeCordonedSpecExtension] // NewNodeCordonedSpec initializes a NodeLabel resource. func NewNodeCordonedSpec(id resource.ID) *NodeCordonedSpec { return typed.NewResource[NodeCordonedSpecSpec, NodeCordonedSpecExtension]( resource.NewMetadata(NamespaceName, NodeCordonedSpecType, id, resource.VersionUndefined), NodeCordonedSpecSpec{}, ) } // NodeCordonedSpecExtension provides auxiliary methods for NodeLabel. type NodeCordonedSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (NodeCordonedSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeCordonedSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeCordonedSpecSpec](NodeCordonedSpecType, &NodeCordonedSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/node_label_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s //nolint:dupl import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeLabelSpecType is the type. const NodeLabelSpecType = resource.Type("NodeLabelSpecs.k8s.talos.dev") // NodeLabelSpecSpec represents a label that's attached to a Talos node. // //gotagsrewrite:gen type NodeLabelSpecSpec struct { Key string `yaml:"key" protobuf:"1"` Value string `yaml:"value" protobuf:"2"` } // NodeLabelSpec ... type NodeLabelSpec = typed.Resource[NodeLabelSpecSpec, NodeLabelSpecExtension] // NewNodeLabelSpec initializes a NodeLabel resource. func NewNodeLabelSpec(id resource.ID) *NodeLabelSpec { return typed.NewResource[NodeLabelSpecSpec, NodeLabelSpecExtension]( resource.NewMetadata(NamespaceName, NodeLabelSpecType, id, resource.VersionUndefined), NodeLabelSpecSpec{}, ) } // NodeLabelSpecExtension provides auxiliary methods for NodeLabel. type NodeLabelSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (NodeLabelSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeLabelSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, PrintColumns: []meta.PrintColumn{ { Name: "Value", JSONPath: "{.value}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeLabelSpecSpec](NodeLabelSpecType, &NodeLabelSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/node_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeStatusType is type of NodeStatus resource. const NodeStatusType = resource.Type("NodeStatuses.kubernetes.talos.dev") // NodeStatus resource holds Kubernetes NodeStatus. type NodeStatus = typed.Resource[NodeStatusSpec, NodeStatusExtension] // NodeStatusSpec describes Kubernetes NodeStatus. // //gotagsrewrite:gen type NodeStatusSpec struct { Nodename string `yaml:"nodename" protobuf:"1"` NodeReady bool `yaml:"nodeReady" protobuf:"2"` Unschedulable bool `yaml:"unschedulable" protobuf:"3"` Labels map[string]string `yaml:"labels" protobuf:"4"` Annotations map[string]string `yaml:"annotations" protobuf:"5"` PodCIDRs []netip.Prefix `yaml:"podCIDRs" protobuf:"6"` } // NewNodeStatus initializes a NodeStatus resource. func NewNodeStatus(namespace resource.Namespace, id resource.ID) *NodeStatus { return typed.NewResource[NodeStatusSpec, NodeStatusExtension]( resource.NewMetadata(namespace, NodeStatusType, id, resource.VersionUndefined), NodeStatusSpec{}, ) } // NodeStatusExtension provides auxiliary methods for NodeStatus. type NodeStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: "{.nodeReady}", }, { Name: "Unschedulable", JSONPath: "{.unschedulable}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeStatusSpec](NodeStatusType, &NodeStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/node_taint_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeTaintSpecType is the type. const NodeTaintSpecType = resource.Type("NodeTaintSpecs.k8s.talos.dev") // NodeTaintSpecSpec represents a label that's attached to a Talos node. // //gotagsrewrite:gen type NodeTaintSpecSpec struct { Key string `yaml:"key" protobuf:"1"` Effect string `yaml:"effect" protobuf:"2"` Value string `yaml:"value" protobuf:"3"` } // NodeTaintSpec ... type NodeTaintSpec = typed.Resource[NodeTaintSpecSpec, NodeTaintSpecExtension] // NewNodeTaintSpec initializes a NodeLabel resource. func NewNodeTaintSpec(id resource.ID) *NodeTaintSpec { return typed.NewResource[NodeTaintSpecSpec, NodeTaintSpecExtension]( resource.NewMetadata(NamespaceName, NodeTaintSpecType, id, resource.VersionUndefined), NodeTaintSpecSpec{}, ) } // NodeTaintSpecExtension provides auxiliary methods for NodeLabel. type NodeTaintSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (NodeTaintSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeTaintSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, PrintColumns: []meta.PrintColumn{ { Name: "Effect", JSONPath: "{.effect}", }, { Name: "Value", JSONPath: "{.value}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeTaintSpecSpec](NodeTaintSpecType, &NodeTaintSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/nodeip.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeIPType is type of NodeIP resource. const NodeIPType = resource.Type("NodeIPs.kubernetes.talos.dev") // NodeIP resource holds definition of Node IP specification. type NodeIP = typed.Resource[NodeIPSpec, NodeIPExtension] // NodeIPSpec holds the Node IP specification. // //gotagsrewrite:gen type NodeIPSpec struct { Addresses []netip.Addr `yaml:"addresses" protobuf:"1"` } // NewNodeIP initializes an empty NodeIP resource. func NewNodeIP(namespace resource.Namespace, id resource.ID) *NodeIP { return typed.NewResource[NodeIPSpec, NodeIPExtension]( resource.NewMetadata(namespace, NodeIPType, id, resource.VersionUndefined), NodeIPSpec{}, ) } // NodeIPExtension provides auxiliary methods for NodeIP. type NodeIPExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeIPExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeIPType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeIPSpec](NodeIPType, &NodeIP{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/nodeip_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeIPConfigType is type of NodeIPConfig resource. const NodeIPConfigType = resource.Type("NodeIPConfigs.kubernetes.talos.dev") // NodeIPConfig resource holds definition of Node IP specification. type NodeIPConfig = typed.Resource[NodeIPConfigSpec, NodeIPConfigExtension] // NodeIPConfigSpec holds the Node IP specification. // //gotagsrewrite:gen type NodeIPConfigSpec struct { ValidSubnets []string `yaml:"validSubnets,omitempty" protobuf:"1"` ExcludeSubnets []string `yaml:"excludeSubnets" protobuf:"2"` } // NewNodeIPConfig initializes an empty NodeIPConfig resource. func NewNodeIPConfig(namespace resource.Namespace, id resource.ID) *NodeIPConfig { return typed.NewResource[NodeIPConfigSpec, NodeIPConfigExtension]( resource.NewMetadata(namespace, NodeIPConfigType, id, resource.VersionUndefined), NodeIPConfigSpec{}, ) } // NodeIPConfigExtension provides auxiliary methods for NodeIPConfig. type NodeIPConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeIPConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeIPConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeIPConfigSpec](NodeIPConfigType, &NodeIPConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/nodename.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodenameType is type of Nodename resource. const NodenameType = resource.Type("Nodenames.kubernetes.talos.dev") // NodenameID is a singleton resource ID for Nodename. const NodenameID = resource.ID("nodename") // Nodename resource holds Kubernetes nodename. type Nodename = typed.Resource[NodenameSpec, NodenameExtension] // NodenameSpec describes Kubernetes nodename. // //gotagsrewrite:gen type NodenameSpec struct { Nodename string `yaml:"nodename" protobuf:"1"` HostnameVersion string `yaml:"hostnameVersion" protobuf:"2"` SkipNodeRegistration bool `yaml:"skipNodeRegistration" protobuf:"3"` } // NewNodename initializes a Nodename resource. func NewNodename(namespace resource.Namespace, id resource.ID) *Nodename { return typed.NewResource[NodenameSpec, NodenameExtension]( resource.NewMetadata(namespace, NodenameType, id, resource.VersionUndefined), NodenameSpec{}, ) } // NodenameExtension provides auxiliary methods for Nodename. type NodenameExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodenameExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodenameType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Nodename", JSONPath: "{.nodename}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodenameSpec](NodenameType, &Nodename{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/scheduler_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SchedulerConfigType is type of SchedulerConfig resource. const SchedulerConfigType = resource.Type("SchedulerConfigs.kubernetes.talos.dev") // SchedulerConfigID is a singleton resource ID for SchedulerConfig. const SchedulerConfigID = resource.ID(SchedulerID) // SchedulerConfig represents configuration for kube-scheduler. type SchedulerConfig = typed.Resource[SchedulerConfigSpec, SchedulerConfigExtension] // SchedulerConfigSpec is configuration for kube-scheduler. // //gotagsrewrite:gen type SchedulerConfigSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` Image string `yaml:"image" protobuf:"2"` ExtraArgs map[string]ArgValues `yaml:"extraArgs" protobuf:"3"` ExtraVolumes []ExtraVolume `yaml:"extraVolumes" protobuf:"4"` EnvironmentVariables map[string]string `yaml:"environmentVariables" protobuf:"5"` Resources Resources `yaml:"resources" protobuf:"6"` Config map[string]any `yaml:"config" protobuf:"7"` } // NewSchedulerConfig returns new SchedulerConfig resource. func NewSchedulerConfig() *SchedulerConfig { return typed.NewResource[SchedulerConfigSpec, SchedulerConfigExtension]( resource.NewMetadata(ControlPlaneNamespaceName, SchedulerConfigType, SchedulerConfigID, resource.VersionUndefined), SchedulerConfigSpec{}) } // SchedulerConfigExtension defines SchedulerConfig resource definition. type SchedulerConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (SchedulerConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SchedulerConfigType, DefaultNamespace: ControlPlaneNamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SchedulerConfigSpec](SchedulerConfigType, &SchedulerConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/secrets_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package k8s provides resources which interface with Kubernetes. // //nolint:dupl package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SecretsStatusType is type of SecretsStatus resource. const SecretsStatusType = resource.Type("SecretStatuses.kubernetes.talos.dev") // StaticPodSecretsStaticPodID is resource ID for SecretStatus resource for static pods. const StaticPodSecretsStaticPodID = resource.ID("static-pods") // SecretsStatus resource holds definition of rendered secrets. type SecretsStatus = typed.Resource[SecretsStatusSpec, SecretsStatusExtension] // SecretsStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type SecretsStatusSpec struct { Ready bool `yaml:"ready" protobuf:"1"` Version string `yaml:"version" protobuf:"2"` } // NewSecretsStatus initializes a SecretsStatus resource. func NewSecretsStatus(namespace resource.Namespace, id resource.ID) *SecretsStatus { return typed.NewResource[SecretsStatusSpec, SecretsStatusExtension]( resource.NewMetadata(namespace, SecretsStatusType, id, resource.VersionUndefined), SecretsStatusSpec{}, ) } // SecretsStatusExtension provides auxiliary methods for SecretsStatus. type SecretsStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (SecretsStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SecretsStatusType, Aliases: []resource.Type{}, DefaultNamespace: ControlPlaneNamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: "{.ready}", }, { Name: "Secrets Version", JSONPath: "{.version}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SecretsStatusSpec](SecretsStatusType, &SecretsStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/static_pod.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // StaticPodType is type of StaticPod resource. const StaticPodType = resource.Type("StaticPods.kubernetes.talos.dev") // StaticPod resource holds definition of kubelet static pod. type StaticPod = typed.Resource[StaticPodSpec, StaticPodExtension] // StaticPodSpec describes static pod spec, it contains marshaled *v1.Pod spec. // //gotagsrewrite:gen type StaticPodSpec struct { Pod map[string]any `protobuf:"1"` } // MarshalYAML implements yaml.Marshaler. func (spec StaticPodSpec) MarshalYAML() (any, error) { return spec.Pod, nil } // NewStaticPod initializes a StaticPod resource. func NewStaticPod(namespace resource.Namespace, id resource.ID) *StaticPod { return typed.NewResource[StaticPodSpec, StaticPodExtension]( resource.NewMetadata(namespace, StaticPodType, id, resource.VersionUndefined), StaticPodSpec{}, ) } // StaticPodExtension provides auxiliary methods for StaticPod. type StaticPodExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (StaticPodExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StaticPodType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StaticPodSpec](StaticPodType, &StaticPod{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/static_pod_server_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // StaticPodServerStatusType is type of StaticPodServerStatus resource. const StaticPodServerStatusType = resource.Type("StaticPodServerStatuses.kubernetes.talos.dev") // StaticPodServerStatus resource holds definition of kubelet static pod. type StaticPodServerStatus = typed.Resource[StaticPodServerStatusSpec, StaticPodServerStatusExtension] // StaticPodServerStatusSpec describes static pod spec, it contains marshaled *v1.Pod spec. // //gotagsrewrite:gen type StaticPodServerStatusSpec struct { URL string `yaml:"url" protobuf:"1"` } // StaticPodServerStatusResourceID is the resource ID under which the static pod server status will be saved. const StaticPodServerStatusResourceID = "static-pod-server-status" // NewStaticPodServerStatus initializes a StaticPodServerStatus resource. func NewStaticPodServerStatus(namespace resource.Namespace, id resource.ID) *StaticPodServerStatus { return typed.NewResource[StaticPodServerStatusSpec, StaticPodServerStatusExtension]( resource.NewMetadata(namespace, StaticPodServerStatusType, id, resource.VersionUndefined), StaticPodServerStatusSpec{}, ) } // StaticPodServerStatusExtension provides auxiliary methods for StaticPodServerStatus. type StaticPodServerStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (StaticPodServerStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StaticPodServerStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StaticPodServerStatusSpec](StaticPodServerStatusType, &StaticPodServerStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/k8s/static_pod_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package k8s import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // StaticPodStatusType is type of StaticPodStatus resource. const StaticPodStatusType = resource.Type("StaticPodStatuses.kubernetes.talos.dev") // StaticPodStatus resource holds definition of kubelet static pod. type StaticPodStatus = typed.Resource[StaticPodStatusSpec, StaticPodStatusExtension] // StaticPodStatusSpec describes kubelet static pod status. // //gotagsrewrite:gen type StaticPodStatusSpec struct { PodStatus map[string]any `protobuf:"1"` } // MarshalYAML implements yaml.Marshaler. func (spec StaticPodStatusSpec) MarshalYAML() (any, error) { return spec.PodStatus, nil } // NewStaticPodStatus initializes a StaticPodStatus resource. func NewStaticPodStatus(namespace resource.Namespace, id resource.ID) *StaticPodStatus { return typed.NewResource[StaticPodStatusSpec, StaticPodStatusExtension]( resource.NewMetadata(namespace, StaticPodStatusType, id, resource.VersionUndefined), StaticPodStatusSpec{}, ) } // StaticPodStatusExtension provides auxiliary methods for StaticPodStatus. type StaticPodStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (StaticPodStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StaticPodStatusType, Aliases: []resource.Type{"podstatus"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: `{.conditions[?(@.type=="Ready")].status}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StaticPodStatusSpec](StaticPodStatusType, &StaticPodStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubeaccess/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // ConfigType is type of Config resource. const ConfigType = resource.Type("KubernetesAccessConfigs.cluster.talos.dev") // ConfigID the singleton config resource ID. const ConfigID = resource.ID("config") // Config resource holds KubeSpan configuration. type Config = typed.Resource[ConfigSpec, ConfigExtension] // ConfigSpec describes KubeSpan configuration.. // //gotagsrewrite:gen type ConfigSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` AllowedAPIRoles []string `yaml:"allowedAPIRoles" protobuf:"2"` AllowedKubernetesNamespaces []string `yaml:"allowedKubernetesNamespaces" protobuf:"3"` } // DeepCopy generates a deep copy of ConfigSpec. func (cs ConfigSpec) DeepCopy() ConfigSpec { cp := cs if cs.AllowedAPIRoles != nil { cp.AllowedAPIRoles = make([]string, len(cs.AllowedAPIRoles)) copy(cp.AllowedAPIRoles, cs.AllowedAPIRoles) } if cs.AllowedKubernetesNamespaces != nil { cp.AllowedKubernetesNamespaces = make([]string, len(cs.AllowedKubernetesNamespaces)) copy(cp.AllowedKubernetesNamespaces, cs.AllowedKubernetesNamespaces) } return cp } // NewConfig initializes a Config resource. func NewConfig(namespace resource.Namespace, id resource.ID) *Config { return typed.NewResource[ConfigSpec, ConfigExtension]( resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined), ConfigSpec{}, ) } // ConfigExtension provides auxiliary methods for Config. type ConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (c ConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.NonSensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigSpec](ConfigType, &Config{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubeaccess/kubeaccess.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubeaccess provides resources related to the Talos API access from Kubernetes workloads. package kubeaccess ================================================ FILE: pkg/machinery/resources/kubeaccess/kubeaccess_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubeaccess_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/kubeaccess" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &kubeaccess.Config{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/kubespan/config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) //go:generate go tool github.com/siderolabs/deep-copy -type ConfigSpec -type EndpointSpec -type IdentitySpec -type PeerSpecSpec -type PeerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // ConfigType is type of Config resource. const ConfigType = resource.Type("KubeSpanConfigs.kubespan.talos.dev") // ConfigID the singleton config resource ID. const ConfigID = resource.ID("kubespan") // Config resource holds KubeSpan configuration. type Config = typed.Resource[ConfigSpec, ConfigExtension] // ConfigSpec describes KubeSpan configuration.. // //gotagsrewrite:gen type ConfigSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` ClusterID string `yaml:"clusterId" protobuf:"2"` SharedSecret string `yaml:"sharedSecret" protobuf:"3"` // Force routing via KubeSpan even if the peer connection is not up. ForceRouting bool `yaml:"forceRouting" protobuf:"4"` // Advertise Kubernetes pod networks or skip it completely. AdvertiseKubernetesNetworks bool `yaml:"advertiseKubernetesNetworks" protobuf:"5"` // Force kubeSpan MTU size. MTU uint32 `yaml:"mtu,omitempty" protobuf:"6"` // If not empty, filter advertised endpoints using the list of CIDRs. EndpointFilters []string `yaml:"endpointFilters,omitempty" protobuf:"7"` // Harvest endpoints from the peer statuses. HarvestExtraEndpoints bool `yaml:"harvestExtraEndpoints" protobuf:"8"` // Extra endpoints to announce. ExtraEndpoints []netip.AddrPort `yaml:"extraEndpoints,omitempty" protobuf:"9"` // If not empty, filter advertised networks using the list of CIDRs. ExcludeAdvertisedNetworks []netip.Prefix `yaml:"excludeAdvertisedNetworks,omitempty" protobuf:"10"` } // NewConfig initializes a Config resource. func NewConfig(namespace resource.Namespace, id resource.ID) *Config { return typed.NewResource[ConfigSpec, ConfigExtension]( resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined), ConfigSpec{}, ) } // ConfigExtension provides auxiliary methods for Config. type ConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigSpec](ConfigType, &Config{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubespan/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ConfigSpec -type EndpointSpec -type IdentitySpec -type PeerSpecSpec -type PeerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package kubespan import ( "net/netip" ) // DeepCopy generates a deep copy of ConfigSpec. func (o ConfigSpec) DeepCopy() ConfigSpec { var cp ConfigSpec = o if o.EndpointFilters != nil { cp.EndpointFilters = make([]string, len(o.EndpointFilters)) copy(cp.EndpointFilters, o.EndpointFilters) } if o.ExtraEndpoints != nil { cp.ExtraEndpoints = make([]netip.AddrPort, len(o.ExtraEndpoints)) copy(cp.ExtraEndpoints, o.ExtraEndpoints) } if o.ExcludeAdvertisedNetworks != nil { cp.ExcludeAdvertisedNetworks = make([]netip.Prefix, len(o.ExcludeAdvertisedNetworks)) copy(cp.ExcludeAdvertisedNetworks, o.ExcludeAdvertisedNetworks) } return cp } // DeepCopy generates a deep copy of EndpointSpec. func (o EndpointSpec) DeepCopy() EndpointSpec { var cp EndpointSpec = o return cp } // DeepCopy generates a deep copy of IdentitySpec. func (o IdentitySpec) DeepCopy() IdentitySpec { var cp IdentitySpec = o return cp } // DeepCopy generates a deep copy of PeerSpecSpec. func (o PeerSpecSpec) DeepCopy() PeerSpecSpec { var cp PeerSpecSpec = o if o.AllowedIPs != nil { cp.AllowedIPs = make([]netip.Prefix, len(o.AllowedIPs)) copy(cp.AllowedIPs, o.AllowedIPs) } if o.Endpoints != nil { cp.Endpoints = make([]netip.AddrPort, len(o.Endpoints)) copy(cp.Endpoints, o.Endpoints) } return cp } // DeepCopy generates a deep copy of PeerStatusSpec. func (o PeerStatusSpec) DeepCopy() PeerStatusSpec { var cp PeerStatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/kubespan/endpoint.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EndpointType is type of Endpoint resource. const EndpointType = resource.Type("KubeSpanEndpoints.kubespan.talos.dev") // Endpoint is produced from KubeSpanPeerStatuses by mapping back discovered endpoints to the affiliates. // // Endpoint is identified by the public key of the peer. type Endpoint = typed.Resource[EndpointSpec, EndpointExtension] // EndpointSpec describes Endpoint state. // //gotagsrewrite:gen type EndpointSpec struct { AffiliateID string `yaml:"affiliateID" protobuf:"1"` Endpoint netip.AddrPort `yaml:"endpoint" protobuf:"2"` } // NewEndpoint initializes a Endpoint resource. func NewEndpoint(namespace resource.Namespace, id resource.ID) *Endpoint { return typed.NewResource[EndpointSpec, EndpointExtension]( resource.NewMetadata(namespace, EndpointType, id, resource.VersionUndefined), EndpointSpec{}, ) } // EndpointExtension provides auxiliary methods for Endpoint. type EndpointExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (EndpointExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EndpointType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Endpoint", JSONPath: `{.endpoint}`, }, { Name: "Affiliate ID", JSONPath: `{.affiliateID}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EndpointSpec](EndpointType, &Endpoint{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubespan/identity.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // IdentityType is type of Identity resource. const IdentityType = resource.Type("KubeSpanIdentities.kubespan.talos.dev") // LocalIdentity is the resource ID for the local node KubeSpan identity. const LocalIdentity = resource.ID("local") // Identity resource holds node identity (as a member of the cluster). type Identity = typed.Resource[IdentitySpec, IdentityExtension] // IdentitySpec describes KubeSpan keys and address. // // Note: IdentitySpec is persisted on disk in the STATE partition, // so YAML serialization should be kept backwards compatible. // //gotagsrewrite:gen type IdentitySpec struct { // Address of the node on the Wireguard network. Address netip.Prefix `yaml:"address" protobuf:"1"` Subnet netip.Prefix `yaml:"subnet" protobuf:"2"` // Public and private Wireguard keys. PrivateKey string `yaml:"privateKey" protobuf:"3"` PublicKey string `yaml:"publicKey" protobuf:"4"` } // NewIdentity initializes a Identity resource. func NewIdentity(namespace resource.Namespace, id resource.ID) *Identity { return typed.NewResource[IdentitySpec, IdentityExtension]( resource.NewMetadata(namespace, IdentityType, id, resource.VersionUndefined), IdentitySpec{}, ) } // IdentityExtension provides auxiliary methods for Identity. type IdentityExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (IdentityExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: IdentityType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Address", JSONPath: `{.address}`, }, { Name: "PublicKey", JSONPath: `{.publicKey}`, }, }, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[IdentitySpec](IdentityType, &Identity{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubespan/kubespan.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains resources related to KubeSpan. const NamespaceName resource.Namespace = "kubespan" ================================================ FILE: pkg/machinery/resources/kubespan/kubespan_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/kubespan" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &kubespan.Config{}, &kubespan.Endpoint{}, &kubespan.Identity{}, &kubespan.PeerSpec{}, &kubespan.PeerStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/kubespan/peer_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PeerSpecType is type of PeerSpec resource. const PeerSpecType = resource.Type("KubeSpanPeerSpecs.kubespan.talos.dev") // PeerSpec is produced from cluster.Affiliate which has KubeSpan information attached. // // PeerSpec is identified by the public key. type PeerSpec = typed.Resource[PeerSpecSpec, PeerSpecExtension] // PeerSpecSpec describes PeerSpec state. // //gotagsrewrite:gen type PeerSpecSpec struct { Address netip.Addr `yaml:"address" protobuf:"1"` AllowedIPs []netip.Prefix `yaml:"allowedIPs" protobuf:"2"` Endpoints []netip.AddrPort `yaml:"endpoints" protobuf:"3"` Label string `yaml:"label" protobuf:"4"` } // NewPeerSpec initializes a PeerSpec resource. func NewPeerSpec(namespace resource.Namespace, id resource.ID) *PeerSpec { return typed.NewResource[PeerSpecSpec, PeerSpecExtension]( resource.NewMetadata(namespace, PeerSpecType, id, resource.VersionUndefined), PeerSpecSpec{}, ) } // PeerSpecExtension provides auxiliary methods for PeerSpec. type PeerSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PeerSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PeerSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Label", JSONPath: `{.label}`, }, { Name: "Endpoints", JSONPath: `{.endpoints}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PeerSpecSpec](PeerSpecType, &PeerSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubespan/peer_state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import "fmt" //go:generate go tool golang.org/x/tools/cmd/stringer -type=PeerState -linecomment // PeerState is KubeSpan peer current state. type PeerState int // MarshalText implements encoding.TextMarshaler. func (v PeerState) MarshalText() ([]byte, error) { return []byte(v.String()), nil } // UnmarshalText implements encoding.TextUnmarshaler. func (v *PeerState) UnmarshalText(b []byte) error { switch string(b) { case "unknown": *v = PeerStateUnknown case "up": *v = PeerStateUp case "down": *v = PeerStateDown default: return fmt.Errorf("unsupported value for PeerState: %q", string(b)) } return nil } // PeerState constants. // //structprotogen:gen_enum const ( PeerStateUnknown PeerState = iota // unknown PeerStateUp // up PeerStateDown // down ) ================================================ FILE: pkg/machinery/resources/kubespan/peer_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package kubespan import ( "net/netip" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PeerStatusType is type of PeerStatus resource. const PeerStatusType = resource.Type("KubeSpanPeerStatuses.kubespan.talos.dev") // PeerStatus the Wireguard peer state for KubeSpan. // // PeerStatus is identified by the public key. type PeerStatus = typed.Resource[PeerStatusSpec, PeerStatusExtension] // PeerStatusSpec describes PeerStatus state. // //gotagsrewrite:gen type PeerStatusSpec struct { // Active endpoint as seen by the Wireguard. Endpoint netip.AddrPort `yaml:"endpoint" protobuf:"1"` // Label derived from the peer spec. Label string `yaml:"label" protobuf:"2"` // Calculated state. State PeerState `yaml:"state" protobuf:"3"` // Tx/Rx bytes. ReceiveBytes int64 `yaml:"receiveBytes" protobuf:"4"` TransmitBytes int64 `yaml:"transmitBytes" protobuf:"5"` // Handshake. LastHandshakeTime time.Time `yaml:"lastHandshakeTime" protobuf:"6"` // Endpoint selection input. LastUsedEndpoint netip.AddrPort `yaml:"lastUsedEndpoint" protobuf:"7"` LastEndpointChange time.Time `yaml:"lastEndpointChange" protobuf:"8"` } // NewPeerStatus initializes a PeerStatus resource. func NewPeerStatus(namespace resource.Namespace, id resource.ID) *PeerStatus { return typed.NewResource[PeerStatusSpec, PeerStatusExtension]( resource.NewMetadata(namespace, PeerStatusType, id, resource.VersionUndefined), PeerStatusSpec{}, ) } // PeerStatusExtension provides auxiliary methods for PeerStatus. type PeerStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PeerStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PeerStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Label", JSONPath: `{.label}`, }, { Name: "Endpoint", JSONPath: `{.endpoint}`, }, { Name: "State", JSONPath: `{.state}`, }, { Name: "Rx", JSONPath: `{.receiveBytes}`, }, { Name: "Tx", JSONPath: `{.transmitBytes}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PeerStatusSpec](PeerStatusType, &PeerStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/kubespan/peerstate_string.go ================================================ // Code generated by "stringer -type=PeerState -linecomment"; DO NOT EDIT. package kubespan import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[PeerStateUnknown-0] _ = x[PeerStateUp-1] _ = x[PeerStateDown-2] } const _PeerState_name = "unknownupdown" var _PeerState_index = [...]uint8{0, 7, 9, 13} func (i PeerState) String() string { idx := int(i) - 0 if i < 0 || idx >= len(_PeerState_index)-1 { return "PeerState(" + strconv.FormatInt(int64(i), 10) + ")" } return _PeerState_name[_PeerState_index[idx]:_PeerState_index[idx+1]] } ================================================ FILE: pkg/machinery/resources/network/address_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) //go:generate go tool github.com/siderolabs/deep-copy -type AddressSpecSpec -type AddressStatusSpec -type BondMasterSpec -type DNSResolveCacheSpec -type EthernetSpecSpec -type EthernetStatusSpec -type HardwareAddrSpec -type HostDNSConfigSpec -type HostnameSpecSpec -type HostnameStatusSpec -type LinkAliasSpecSpec -type LinkRefreshSpec -type LinkSpecSpec -type LinkStatusSpec -type NfTablesChainSpec -type NodeAddressSpec -type NodeAddressSortAlgorithmSpec -type NodeAddressFilterSpec -type OperatorSpecSpec -type PlatformConfigSpec -type ProbeSpecSpec -type ProbeStatusSpec -type ResolverSpecSpec -type ResolverStatusSpec -type RouteSpecSpec -type RouteStatusSpec -type RoutingRuleSpecSpec -type RoutingRuleStatusSpec -type StatusSpec -type TimeServerSpecSpec -type TimeServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // AddressSpecType is type of AddressSpec resource. const AddressSpecType = resource.Type("AddressSpecs.net.talos.dev") // AddressSpec resource holds physical network link status. type AddressSpec = typed.Resource[AddressSpecSpec, AddressSpecExtension] // AddressSpecSpec describes status of rendered secrets. // //gotagsrewrite:gen type AddressSpecSpec struct { Address netip.Prefix `yaml:"address" protobuf:"1"` LinkName string `yaml:"linkName" protobuf:"2"` Family nethelpers.Family `yaml:"family" protobuf:"3"` Scope nethelpers.Scope `yaml:"scope" protobuf:"4"` Flags nethelpers.AddressFlags `yaml:"flags" protobuf:"5"` AnnounceWithARP bool `yaml:"announceWithARP,omitempty" protobuf:"6"` Priority uint32 `yaml:"priority,omitempty" protobuf:"8"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"7"` } // NewAddressSpec initializes a AddressSpec resource. func NewAddressSpec(namespace resource.Namespace, id resource.ID) *AddressSpec { return typed.NewResource[AddressSpecSpec, AddressSpecExtension]( resource.NewMetadata(namespace, AddressSpecType, id, resource.VersionUndefined), AddressSpecSpec{}, ) } // AddressSpecExtension provides auxiliary methods for AddressSpec. type AddressSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (AddressSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AddressSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AddressSpecSpec](AddressSpecType, &AddressSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/address_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestAddressSpecMarshalYAML(t *testing.T) { spec := network.AddressSpecSpec{ Address: netip.MustParsePrefix("192.168.3.6/27"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeLink, Flags: nethelpers.AddressFlags(nethelpers.AddressPermanent), ConfigLayer: network.ConfigMachineConfiguration, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, "address: 192.168.3.6/27\nlinkName: eth0\nfamily: inet4\nscope: link\nflags: permanent\nlayer: configuration\n", string(marshaled)) var spec2 network.AddressSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/address_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // AddressStatusType is type of AddressStatus resource. const AddressStatusType = resource.Type("AddressStatuses.net.talos.dev") // AddressStatus resource holds physical network link status. type AddressStatus = typed.Resource[AddressStatusSpec, AddressStatusExtension] // AddressStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type AddressStatusSpec struct { Address netip.Prefix `yaml:"address" protobuf:"1"` Local netip.Addr `yaml:"local,omitempty" protobuf:"2"` Broadcast netip.Addr `yaml:"broadcast,omitempty" protobuf:"3"` Anycast netip.Addr `yaml:"anycast,omitempty" protobuf:"4"` Multicast netip.Addr `yaml:"multicast,omitempty" protobuf:"5"` LinkIndex uint32 `yaml:"linkIndex" protobuf:"6"` LinkName string `yaml:"linkName" protobuf:"7"` Family nethelpers.Family `yaml:"family" protobuf:"8"` Scope nethelpers.Scope `yaml:"scope" protobuf:"9"` Flags nethelpers.AddressFlags `yaml:"flags" protobuf:"10"` Priority uint32 `yaml:"priority" protobuf:"11"` } // NewAddressStatus initializes a AddressStatus resource. func NewAddressStatus(namespace resource.Namespace, id resource.ID) *AddressStatus { return typed.NewResource[AddressStatusSpec, AddressStatusExtension]( resource.NewMetadata(namespace, AddressStatusType, id, resource.VersionUndefined), AddressStatusSpec{}, ) } // AddressStatusExtension provides auxiliary methods for AddressStatus. type AddressStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (AddressStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AddressStatusType, Aliases: []resource.Type{"address", "addresses"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Address", JSONPath: `{.address}`, }, { Name: "Link", JSONPath: `{.linkName}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AddressStatusSpec](AddressStatusType, &AddressStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/condition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "context" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // ReadyCondition implements condition which waits for the network to be ready. type ReadyCondition struct { state state.State checks []StatusCheck } // NewReadyCondition builds a condition which waits for the network to be ready. func NewReadyCondition(state state.State, checks ...StatusCheck) *ReadyCondition { return &ReadyCondition{ state: state, checks: checks, } } func (condition *ReadyCondition) String() string { return "network" } // Wait implements condition interface. func (condition *ReadyCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, StatusType, StatusID, resource.VersionUndefined), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } status := r.(*Status).TypedSpec() for _, check := range condition.checks { if !check(status) { return false, nil } } return true, nil }), ) return err } // StatusCheck asserts specific part of Status to be true. type StatusCheck func(*StatusSpec) bool // AddressReady checks if address is ready. func AddressReady(spec *StatusSpec) bool { return spec.AddressReady } // ConnectivityReady checks if connectivity is ready. func ConnectivityReady(spec *StatusSpec) bool { return spec.ConnectivityReady } // HostnameReady checks if hostname is ready. func HostnameReady(spec *StatusSpec) bool { return spec.HostnameReady } // EtcFilesReady checks if etc files are ready. func EtcFilesReady(spec *StatusSpec) bool { return spec.EtcFilesReady } // StatusChecksFromStatuses converts nethelpers.Status list into list of checks. func StatusChecksFromStatuses(statuses ...nethelpers.Status) []StatusCheck { checks := make([]StatusCheck, 0, len(statuses)) for _, st := range statuses { switch st { case nethelpers.StatusAddresses: checks = append(checks, AddressReady) case nethelpers.StatusConnectivity: checks = append(checks, ConnectivityReady) case nethelpers.StatusHostname: checks = append(checks, HostnameReady) case nethelpers.StatusEtcFiles: checks = append(checks, EtcFilesReady) } } return checks } ================================================ FILE: pkg/machinery/resources/network/condition_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "context" "errors" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestCondition(t *testing.T) { ctx, ctxCancel := context.WithTimeout(t.Context(), time.Second) t.Cleanup(ctxCancel) t.Parallel() for _, tt := range []struct { Name string Status network.StatusSpec Checks []network.StatusCheck Succeeds bool }{ { Name: "no checks", Succeeds: true, }, { Name: "timeout", Checks: []network.StatusCheck{network.AddressReady, network.ConnectivityReady}, Succeeds: false, }, { Name: "partial", Status: network.StatusSpec{ AddressReady: true, }, Checks: []network.StatusCheck{network.AddressReady, network.ConnectivityReady}, Succeeds: false, }, { Name: "okay", Status: network.StatusSpec{ AddressReady: true, ConnectivityReady: true, EtcFilesReady: true, }, Checks: []network.StatusCheck{network.AddressReady, network.ConnectivityReady}, Succeeds: true, }, } { t.Run(tt.Name, func(t *testing.T) { t.Parallel() state := state.WrapCore(namespaced.NewState(inmem.Build)) status := network.NewStatus(network.NamespaceName, network.StatusID) *status.TypedSpec() = tt.Status require.NoError(t, state.Create(ctx, status)) err := network.NewReadyCondition(state, tt.Checks...).Wait(ctx) if tt.Succeeds { assert.NoError(t, err) } else { assert.True(t, errors.Is(err, context.DeadlineExceeded), "error is %v", err) } }) } } ================================================ FILE: pkg/machinery/resources/network/configlayer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network // ConfigLayer describes network configuration layers, with lowest priority first. type ConfigLayer int // Configuration layers. // //structprotogen:gen_enum const ( ConfigDefault ConfigLayer = iota // default ConfigCmdline // cmdline ConfigPlatform // platform ConfigOperator // operator ConfigMachineConfiguration // configuration ) ================================================ FILE: pkg/machinery/resources/network/configlayer_enumer.go ================================================ // Code generated by "enumer -type=ConfigLayer,Operator -linecomment -text"; DO NOT EDIT. package network import ( "fmt" "strings" ) const _ConfigLayerName = "defaultcmdlineplatformoperatorconfiguration" var _ConfigLayerIndex = [...]uint8{0, 7, 14, 22, 30, 43} const _ConfigLayerLowerName = "defaultcmdlineplatformoperatorconfiguration" func (i ConfigLayer) String() string { if i < 0 || i >= ConfigLayer(len(_ConfigLayerIndex)-1) { return fmt.Sprintf("ConfigLayer(%d)", i) } return _ConfigLayerName[_ConfigLayerIndex[i]:_ConfigLayerIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _ConfigLayerNoOp() { var x [1]struct{} _ = x[ConfigDefault-(0)] _ = x[ConfigCmdline-(1)] _ = x[ConfigPlatform-(2)] _ = x[ConfigOperator-(3)] _ = x[ConfigMachineConfiguration-(4)] } var _ConfigLayerValues = []ConfigLayer{ConfigDefault, ConfigCmdline, ConfigPlatform, ConfigOperator, ConfigMachineConfiguration} var _ConfigLayerNameToValueMap = map[string]ConfigLayer{ _ConfigLayerName[0:7]: ConfigDefault, _ConfigLayerLowerName[0:7]: ConfigDefault, _ConfigLayerName[7:14]: ConfigCmdline, _ConfigLayerLowerName[7:14]: ConfigCmdline, _ConfigLayerName[14:22]: ConfigPlatform, _ConfigLayerLowerName[14:22]: ConfigPlatform, _ConfigLayerName[22:30]: ConfigOperator, _ConfigLayerLowerName[22:30]: ConfigOperator, _ConfigLayerName[30:43]: ConfigMachineConfiguration, _ConfigLayerLowerName[30:43]: ConfigMachineConfiguration, } var _ConfigLayerNames = []string{ _ConfigLayerName[0:7], _ConfigLayerName[7:14], _ConfigLayerName[14:22], _ConfigLayerName[22:30], _ConfigLayerName[30:43], } // ConfigLayerString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func ConfigLayerString(s string) (ConfigLayer, error) { if val, ok := _ConfigLayerNameToValueMap[s]; ok { return val, nil } if val, ok := _ConfigLayerNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to ConfigLayer values", s) } // ConfigLayerValues returns all values of the enum func ConfigLayerValues() []ConfigLayer { return _ConfigLayerValues } // ConfigLayerStrings returns a slice of all String values of the enum func ConfigLayerStrings() []string { strs := make([]string, len(_ConfigLayerNames)) copy(strs, _ConfigLayerNames) return strs } // IsAConfigLayer returns "true" if the value is listed in the enum definition. "false" otherwise func (i ConfigLayer) IsAConfigLayer() bool { for _, v := range _ConfigLayerValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for ConfigLayer func (i ConfigLayer) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for ConfigLayer func (i *ConfigLayer) UnmarshalText(text []byte) error { var err error *i, err = ConfigLayerString(string(text)) return err } const _OperatorName = "dhcp4dhcp6vip" var _OperatorIndex = [...]uint8{0, 5, 10, 13} const _OperatorLowerName = "dhcp4dhcp6vip" func (i Operator) String() string { if i < 0 || i >= Operator(len(_OperatorIndex)-1) { return fmt.Sprintf("Operator(%d)", i) } return _OperatorName[_OperatorIndex[i]:_OperatorIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _OperatorNoOp() { var x [1]struct{} _ = x[OperatorDHCP4-(0)] _ = x[OperatorDHCP6-(1)] _ = x[OperatorVIP-(2)] } var _OperatorValues = []Operator{OperatorDHCP4, OperatorDHCP6, OperatorVIP} var _OperatorNameToValueMap = map[string]Operator{ _OperatorName[0:5]: OperatorDHCP4, _OperatorLowerName[0:5]: OperatorDHCP4, _OperatorName[5:10]: OperatorDHCP6, _OperatorLowerName[5:10]: OperatorDHCP6, _OperatorName[10:13]: OperatorVIP, _OperatorLowerName[10:13]: OperatorVIP, } var _OperatorNames = []string{ _OperatorName[0:5], _OperatorName[5:10], _OperatorName[10:13], } // OperatorString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func OperatorString(s string) (Operator, error) { if val, ok := _OperatorNameToValueMap[s]; ok { return val, nil } if val, ok := _OperatorNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to Operator values", s) } // OperatorValues returns all values of the enum func OperatorValues() []Operator { return _OperatorValues } // OperatorStrings returns a slice of all String values of the enum func OperatorStrings() []string { strs := make([]string, len(_OperatorNames)) copy(strs, _OperatorNames) return strs } // IsAOperator returns "true" if the value is listed in the enum definition. "false" otherwise func (i Operator) IsAOperator() bool { for _, v := range _OperatorValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for Operator func (i Operator) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for Operator func (i *Operator) UnmarshalText(text []byte) error { var err error *i, err = OperatorString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/network/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type AddressSpecSpec -type AddressStatusSpec -type BondMasterSpec -type DNSResolveCacheSpec -type EthernetSpecSpec -type EthernetStatusSpec -type HardwareAddrSpec -type HostDNSConfigSpec -type HostnameSpecSpec -type HostnameStatusSpec -type LinkAliasSpecSpec -type LinkRefreshSpec -type LinkSpecSpec -type LinkStatusSpec -type NfTablesChainSpec -type NodeAddressSpec -type NodeAddressSortAlgorithmSpec -type NodeAddressFilterSpec -type OperatorSpecSpec -type PlatformConfigSpec -type ProbeSpecSpec -type ProbeStatusSpec -type ResolverSpecSpec -type ResolverStatusSpec -type RouteSpecSpec -type RouteStatusSpec -type RoutingRuleSpecSpec -type RoutingRuleStatusSpec -type StatusSpec -type TimeServerSpecSpec -type TimeServerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package network import ( "net/netip" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // DeepCopy generates a deep copy of AddressSpecSpec. func (o AddressSpecSpec) DeepCopy() AddressSpecSpec { var cp AddressSpecSpec = o return cp } // DeepCopy generates a deep copy of AddressStatusSpec. func (o AddressStatusSpec) DeepCopy() AddressStatusSpec { var cp AddressStatusSpec = o return cp } // DeepCopy generates a deep copy of BondMasterSpec. func (o BondMasterSpec) DeepCopy() BondMasterSpec { var cp BondMasterSpec = o if o.PrimaryIndex != nil { cp.PrimaryIndex = new(uint32) *cp.PrimaryIndex = *o.PrimaryIndex } if o.ARPIPTargets != nil { cp.ARPIPTargets = make([]netip.Addr, len(o.ARPIPTargets)) copy(cp.ARPIPTargets, o.ARPIPTargets) } if o.NSIP6Targets != nil { cp.NSIP6Targets = make([]netip.Addr, len(o.NSIP6Targets)) copy(cp.NSIP6Targets, o.NSIP6Targets) } return cp } // DeepCopy generates a deep copy of DNSResolveCacheSpec. func (o DNSResolveCacheSpec) DeepCopy() DNSResolveCacheSpec { var cp DNSResolveCacheSpec = o return cp } // DeepCopy generates a deep copy of EthernetSpecSpec. func (o EthernetSpecSpec) DeepCopy() EthernetSpecSpec { var cp EthernetSpecSpec = o if o.Rings.RX != nil { cp.Rings.RX = new(uint32) *cp.Rings.RX = *o.Rings.RX } if o.Rings.TX != nil { cp.Rings.TX = new(uint32) *cp.Rings.TX = *o.Rings.TX } if o.Rings.RXMini != nil { cp.Rings.RXMini = new(uint32) *cp.Rings.RXMini = *o.Rings.RXMini } if o.Rings.RXJumbo != nil { cp.Rings.RXJumbo = new(uint32) *cp.Rings.RXJumbo = *o.Rings.RXJumbo } if o.Rings.RXBufLen != nil { cp.Rings.RXBufLen = new(uint32) *cp.Rings.RXBufLen = *o.Rings.RXBufLen } if o.Rings.CQESize != nil { cp.Rings.CQESize = new(uint32) *cp.Rings.CQESize = *o.Rings.CQESize } if o.Rings.TXPush != nil { cp.Rings.TXPush = new(bool) *cp.Rings.TXPush = *o.Rings.TXPush } if o.Rings.RXPush != nil { cp.Rings.RXPush = new(bool) *cp.Rings.RXPush = *o.Rings.RXPush } if o.Rings.TXPushBufLen != nil { cp.Rings.TXPushBufLen = new(uint32) *cp.Rings.TXPushBufLen = *o.Rings.TXPushBufLen } if o.Rings.TCPDataSplit != nil { cp.Rings.TCPDataSplit = new(bool) *cp.Rings.TCPDataSplit = *o.Rings.TCPDataSplit } if o.Features != nil { cp.Features = make(map[string]bool, len(o.Features)) for k2, v2 := range o.Features { cp.Features[k2] = v2 } } if o.Channels.RX != nil { cp.Channels.RX = new(uint32) *cp.Channels.RX = *o.Channels.RX } if o.Channels.TX != nil { cp.Channels.TX = new(uint32) *cp.Channels.TX = *o.Channels.TX } if o.Channels.Other != nil { cp.Channels.Other = new(uint32) *cp.Channels.Other = *o.Channels.Other } if o.Channels.Combined != nil { cp.Channels.Combined = new(uint32) *cp.Channels.Combined = *o.Channels.Combined } if o.WakeOnLAN != nil { cp.WakeOnLAN = make([]nethelpers.WOLMode, len(o.WakeOnLAN)) copy(cp.WakeOnLAN, o.WakeOnLAN) } return cp } // DeepCopy generates a deep copy of EthernetStatusSpec. func (o EthernetStatusSpec) DeepCopy() EthernetStatusSpec { var cp EthernetStatusSpec = o if o.LinkState != nil { cp.LinkState = new(bool) *cp.LinkState = *o.LinkState } if o.OurModes != nil { cp.OurModes = make([]string, len(o.OurModes)) copy(cp.OurModes, o.OurModes) } if o.PeerModes != nil { cp.PeerModes = make([]string, len(o.PeerModes)) copy(cp.PeerModes, o.PeerModes) } if o.Rings != nil { cp.Rings = new(EthernetRingsStatus) *cp.Rings = *o.Rings if o.Rings.RXMax != nil { cp.Rings.RXMax = new(uint32) *cp.Rings.RXMax = *o.Rings.RXMax } if o.Rings.RXMiniMax != nil { cp.Rings.RXMiniMax = new(uint32) *cp.Rings.RXMiniMax = *o.Rings.RXMiniMax } if o.Rings.RXJumboMax != nil { cp.Rings.RXJumboMax = new(uint32) *cp.Rings.RXJumboMax = *o.Rings.RXJumboMax } if o.Rings.TXMax != nil { cp.Rings.TXMax = new(uint32) *cp.Rings.TXMax = *o.Rings.TXMax } if o.Rings.TXPushBufLenMax != nil { cp.Rings.TXPushBufLenMax = new(uint32) *cp.Rings.TXPushBufLenMax = *o.Rings.TXPushBufLenMax } if o.Rings.RX != nil { cp.Rings.RX = new(uint32) *cp.Rings.RX = *o.Rings.RX } if o.Rings.RXMini != nil { cp.Rings.RXMini = new(uint32) *cp.Rings.RXMini = *o.Rings.RXMini } if o.Rings.RXJumbo != nil { cp.Rings.RXJumbo = new(uint32) *cp.Rings.RXJumbo = *o.Rings.RXJumbo } if o.Rings.TX != nil { cp.Rings.TX = new(uint32) *cp.Rings.TX = *o.Rings.TX } if o.Rings.RXBufLen != nil { cp.Rings.RXBufLen = new(uint32) *cp.Rings.RXBufLen = *o.Rings.RXBufLen } if o.Rings.CQESize != nil { cp.Rings.CQESize = new(uint32) *cp.Rings.CQESize = *o.Rings.CQESize } if o.Rings.TXPush != nil { cp.Rings.TXPush = new(bool) *cp.Rings.TXPush = *o.Rings.TXPush } if o.Rings.RXPush != nil { cp.Rings.RXPush = new(bool) *cp.Rings.RXPush = *o.Rings.RXPush } if o.Rings.TXPushBufLen != nil { cp.Rings.TXPushBufLen = new(uint32) *cp.Rings.TXPushBufLen = *o.Rings.TXPushBufLen } if o.Rings.TCPDataSplit != nil { cp.Rings.TCPDataSplit = new(bool) *cp.Rings.TCPDataSplit = *o.Rings.TCPDataSplit } } if o.Features != nil { cp.Features = make([]EthernetFeatureStatus, len(o.Features)) copy(cp.Features, o.Features) } if o.Channels != nil { cp.Channels = new(EthernetChannelsStatus) *cp.Channels = *o.Channels if o.Channels.RXMax != nil { cp.Channels.RXMax = new(uint32) *cp.Channels.RXMax = *o.Channels.RXMax } if o.Channels.TXMax != nil { cp.Channels.TXMax = new(uint32) *cp.Channels.TXMax = *o.Channels.TXMax } if o.Channels.OtherMax != nil { cp.Channels.OtherMax = new(uint32) *cp.Channels.OtherMax = *o.Channels.OtherMax } if o.Channels.CombinedMax != nil { cp.Channels.CombinedMax = new(uint32) *cp.Channels.CombinedMax = *o.Channels.CombinedMax } if o.Channels.RX != nil { cp.Channels.RX = new(uint32) *cp.Channels.RX = *o.Channels.RX } if o.Channels.TX != nil { cp.Channels.TX = new(uint32) *cp.Channels.TX = *o.Channels.TX } if o.Channels.Other != nil { cp.Channels.Other = new(uint32) *cp.Channels.Other = *o.Channels.Other } if o.Channels.Combined != nil { cp.Channels.Combined = new(uint32) *cp.Channels.Combined = *o.Channels.Combined } } if o.WakeOnLAN != nil { cp.WakeOnLAN = make([]nethelpers.WOLMode, len(o.WakeOnLAN)) copy(cp.WakeOnLAN, o.WakeOnLAN) } return cp } // DeepCopy generates a deep copy of HardwareAddrSpec. func (o HardwareAddrSpec) DeepCopy() HardwareAddrSpec { var cp HardwareAddrSpec = o if o.HardwareAddr != nil { cp.HardwareAddr = make([]byte, len(o.HardwareAddr)) copy(cp.HardwareAddr, o.HardwareAddr) } return cp } // DeepCopy generates a deep copy of HostDNSConfigSpec. func (o HostDNSConfigSpec) DeepCopy() HostDNSConfigSpec { var cp HostDNSConfigSpec = o if o.ListenAddresses != nil { cp.ListenAddresses = make([]netip.AddrPort, len(o.ListenAddresses)) copy(cp.ListenAddresses, o.ListenAddresses) } return cp } // DeepCopy generates a deep copy of HostnameSpecSpec. func (o HostnameSpecSpec) DeepCopy() HostnameSpecSpec { var cp HostnameSpecSpec = o return cp } // DeepCopy generates a deep copy of HostnameStatusSpec. func (o HostnameStatusSpec) DeepCopy() HostnameStatusSpec { var cp HostnameStatusSpec = o return cp } // DeepCopy generates a deep copy of LinkAliasSpecSpec. func (o LinkAliasSpecSpec) DeepCopy() LinkAliasSpecSpec { var cp LinkAliasSpecSpec = o return cp } // DeepCopy generates a deep copy of LinkRefreshSpec. func (o LinkRefreshSpec) DeepCopy() LinkRefreshSpec { var cp LinkRefreshSpec = o return cp } // DeepCopy generates a deep copy of LinkSpecSpec. func (o LinkSpecSpec) DeepCopy() LinkSpecSpec { var cp LinkSpecSpec = o if o.HardwareAddress != nil { cp.HardwareAddress = make([]byte, len(o.HardwareAddress)) copy(cp.HardwareAddress, o.HardwareAddress) } cp.BondMaster = o.BondMaster.DeepCopy() if o.Wireguard.Peers != nil { cp.Wireguard.Peers = make([]WireguardPeer, len(o.Wireguard.Peers)) copy(cp.Wireguard.Peers, o.Wireguard.Peers) for i3 := range o.Wireguard.Peers { if o.Wireguard.Peers[i3].AllowedIPs != nil { cp.Wireguard.Peers[i3].AllowedIPs = make([]netip.Prefix, len(o.Wireguard.Peers[i3].AllowedIPs)) copy(cp.Wireguard.Peers[i3].AllowedIPs, o.Wireguard.Peers[i3].AllowedIPs) } } } if o.Multicast != nil { cp.Multicast = new(bool) *cp.Multicast = *o.Multicast } return cp } // DeepCopy generates a deep copy of LinkStatusSpec. func (o LinkStatusSpec) DeepCopy() LinkStatusSpec { var cp LinkStatusSpec = o if o.AltNames != nil { cp.AltNames = make([]string, len(o.AltNames)) copy(cp.AltNames, o.AltNames) } if o.HardwareAddr != nil { cp.HardwareAddr = make([]byte, len(o.HardwareAddr)) copy(cp.HardwareAddr, o.HardwareAddr) } if o.PermanentAddr != nil { cp.PermanentAddr = make([]byte, len(o.PermanentAddr)) copy(cp.PermanentAddr, o.PermanentAddr) } if o.BroadcastAddr != nil { cp.BroadcastAddr = make([]byte, len(o.BroadcastAddr)) copy(cp.BroadcastAddr, o.BroadcastAddr) } cp.BondMaster = o.BondMaster.DeepCopy() if o.Wireguard.Peers != nil { cp.Wireguard.Peers = make([]WireguardPeer, len(o.Wireguard.Peers)) copy(cp.Wireguard.Peers, o.Wireguard.Peers) for i3 := range o.Wireguard.Peers { if o.Wireguard.Peers[i3].AllowedIPs != nil { cp.Wireguard.Peers[i3].AllowedIPs = make([]netip.Prefix, len(o.Wireguard.Peers[i3].AllowedIPs)) copy(cp.Wireguard.Peers[i3].AllowedIPs, o.Wireguard.Peers[i3].AllowedIPs) } } } return cp } // DeepCopy generates a deep copy of NfTablesChainSpec. func (o NfTablesChainSpec) DeepCopy() NfTablesChainSpec { var cp NfTablesChainSpec = o if o.Rules != nil { cp.Rules = make([]NfTablesRule, len(o.Rules)) copy(cp.Rules, o.Rules) for i2 := range o.Rules { if o.Rules[i2].MatchIIfName != nil { cp.Rules[i2].MatchIIfName = new(NfTablesIfNameMatch) *cp.Rules[i2].MatchIIfName = *o.Rules[i2].MatchIIfName if o.Rules[i2].MatchIIfName.InterfaceNames != nil { cp.Rules[i2].MatchIIfName.InterfaceNames = make([]string, len(o.Rules[i2].MatchIIfName.InterfaceNames)) copy(cp.Rules[i2].MatchIIfName.InterfaceNames, o.Rules[i2].MatchIIfName.InterfaceNames) } } if o.Rules[i2].MatchOIfName != nil { cp.Rules[i2].MatchOIfName = new(NfTablesIfNameMatch) *cp.Rules[i2].MatchOIfName = *o.Rules[i2].MatchOIfName if o.Rules[i2].MatchOIfName.InterfaceNames != nil { cp.Rules[i2].MatchOIfName.InterfaceNames = make([]string, len(o.Rules[i2].MatchOIfName.InterfaceNames)) copy(cp.Rules[i2].MatchOIfName.InterfaceNames, o.Rules[i2].MatchOIfName.InterfaceNames) } } if o.Rules[i2].MatchMark != nil { cp.Rules[i2].MatchMark = new(NfTablesMark) *cp.Rules[i2].MatchMark = *o.Rules[i2].MatchMark } if o.Rules[i2].MatchConntrackState != nil { cp.Rules[i2].MatchConntrackState = new(NfTablesConntrackStateMatch) *cp.Rules[i2].MatchConntrackState = *o.Rules[i2].MatchConntrackState if o.Rules[i2].MatchConntrackState.States != nil { cp.Rules[i2].MatchConntrackState.States = make([]nethelpers.ConntrackState, len(o.Rules[i2].MatchConntrackState.States)) copy(cp.Rules[i2].MatchConntrackState.States, o.Rules[i2].MatchConntrackState.States) } } if o.Rules[i2].MatchSourceAddress != nil { cp.Rules[i2].MatchSourceAddress = new(NfTablesAddressMatch) *cp.Rules[i2].MatchSourceAddress = *o.Rules[i2].MatchSourceAddress if o.Rules[i2].MatchSourceAddress.IncludeSubnets != nil { cp.Rules[i2].MatchSourceAddress.IncludeSubnets = make([]netip.Prefix, len(o.Rules[i2].MatchSourceAddress.IncludeSubnets)) copy(cp.Rules[i2].MatchSourceAddress.IncludeSubnets, o.Rules[i2].MatchSourceAddress.IncludeSubnets) } if o.Rules[i2].MatchSourceAddress.ExcludeSubnets != nil { cp.Rules[i2].MatchSourceAddress.ExcludeSubnets = make([]netip.Prefix, len(o.Rules[i2].MatchSourceAddress.ExcludeSubnets)) copy(cp.Rules[i2].MatchSourceAddress.ExcludeSubnets, o.Rules[i2].MatchSourceAddress.ExcludeSubnets) } } if o.Rules[i2].MatchDestinationAddress != nil { cp.Rules[i2].MatchDestinationAddress = new(NfTablesAddressMatch) *cp.Rules[i2].MatchDestinationAddress = *o.Rules[i2].MatchDestinationAddress if o.Rules[i2].MatchDestinationAddress.IncludeSubnets != nil { cp.Rules[i2].MatchDestinationAddress.IncludeSubnets = make([]netip.Prefix, len(o.Rules[i2].MatchDestinationAddress.IncludeSubnets)) copy(cp.Rules[i2].MatchDestinationAddress.IncludeSubnets, o.Rules[i2].MatchDestinationAddress.IncludeSubnets) } if o.Rules[i2].MatchDestinationAddress.ExcludeSubnets != nil { cp.Rules[i2].MatchDestinationAddress.ExcludeSubnets = make([]netip.Prefix, len(o.Rules[i2].MatchDestinationAddress.ExcludeSubnets)) copy(cp.Rules[i2].MatchDestinationAddress.ExcludeSubnets, o.Rules[i2].MatchDestinationAddress.ExcludeSubnets) } } if o.Rules[i2].MatchLayer4 != nil { cp.Rules[i2].MatchLayer4 = new(NfTablesLayer4Match) *cp.Rules[i2].MatchLayer4 = *o.Rules[i2].MatchLayer4 if o.Rules[i2].MatchLayer4.MatchSourcePort != nil { cp.Rules[i2].MatchLayer4.MatchSourcePort = new(NfTablesPortMatch) *cp.Rules[i2].MatchLayer4.MatchSourcePort = *o.Rules[i2].MatchLayer4.MatchSourcePort if o.Rules[i2].MatchLayer4.MatchSourcePort.Ranges != nil { cp.Rules[i2].MatchLayer4.MatchSourcePort.Ranges = make([]PortRange, len(o.Rules[i2].MatchLayer4.MatchSourcePort.Ranges)) copy(cp.Rules[i2].MatchLayer4.MatchSourcePort.Ranges, o.Rules[i2].MatchLayer4.MatchSourcePort.Ranges) } } if o.Rules[i2].MatchLayer4.MatchDestinationPort != nil { cp.Rules[i2].MatchLayer4.MatchDestinationPort = new(NfTablesPortMatch) *cp.Rules[i2].MatchLayer4.MatchDestinationPort = *o.Rules[i2].MatchLayer4.MatchDestinationPort if o.Rules[i2].MatchLayer4.MatchDestinationPort.Ranges != nil { cp.Rules[i2].MatchLayer4.MatchDestinationPort.Ranges = make([]PortRange, len(o.Rules[i2].MatchLayer4.MatchDestinationPort.Ranges)) copy(cp.Rules[i2].MatchLayer4.MatchDestinationPort.Ranges, o.Rules[i2].MatchLayer4.MatchDestinationPort.Ranges) } } if o.Rules[i2].MatchLayer4.MatchICMPType != nil { cp.Rules[i2].MatchLayer4.MatchICMPType = new(NfTablesICMPTypeMatch) *cp.Rules[i2].MatchLayer4.MatchICMPType = *o.Rules[i2].MatchLayer4.MatchICMPType if o.Rules[i2].MatchLayer4.MatchICMPType.Types != nil { cp.Rules[i2].MatchLayer4.MatchICMPType.Types = make([]nethelpers.ICMPType, len(o.Rules[i2].MatchLayer4.MatchICMPType.Types)) copy(cp.Rules[i2].MatchLayer4.MatchICMPType.Types, o.Rules[i2].MatchLayer4.MatchICMPType.Types) } } } if o.Rules[i2].MatchLimit != nil { cp.Rules[i2].MatchLimit = new(NfTablesLimitMatch) *cp.Rules[i2].MatchLimit = *o.Rules[i2].MatchLimit } if o.Rules[i2].ClampMSS != nil { cp.Rules[i2].ClampMSS = new(NfTablesClampMSS) *cp.Rules[i2].ClampMSS = *o.Rules[i2].ClampMSS } if o.Rules[i2].SetMark != nil { cp.Rules[i2].SetMark = new(NfTablesMark) *cp.Rules[i2].SetMark = *o.Rules[i2].SetMark } if o.Rules[i2].Verdict != nil { cp.Rules[i2].Verdict = new(nethelpers.NfTablesVerdict) *cp.Rules[i2].Verdict = *o.Rules[i2].Verdict } } } return cp } // DeepCopy generates a deep copy of NodeAddressSpec. func (o NodeAddressSpec) DeepCopy() NodeAddressSpec { var cp NodeAddressSpec = o if o.Addresses != nil { cp.Addresses = make([]netip.Prefix, len(o.Addresses)) copy(cp.Addresses, o.Addresses) } return cp } // DeepCopy generates a deep copy of NodeAddressSortAlgorithmSpec. func (o NodeAddressSortAlgorithmSpec) DeepCopy() NodeAddressSortAlgorithmSpec { var cp NodeAddressSortAlgorithmSpec = o return cp } // DeepCopy generates a deep copy of NodeAddressFilterSpec. func (o NodeAddressFilterSpec) DeepCopy() NodeAddressFilterSpec { var cp NodeAddressFilterSpec = o if o.IncludeSubnets != nil { cp.IncludeSubnets = make([]netip.Prefix, len(o.IncludeSubnets)) copy(cp.IncludeSubnets, o.IncludeSubnets) } if o.ExcludeSubnets != nil { cp.ExcludeSubnets = make([]netip.Prefix, len(o.ExcludeSubnets)) copy(cp.ExcludeSubnets, o.ExcludeSubnets) } return cp } // DeepCopy generates a deep copy of OperatorSpecSpec. func (o OperatorSpecSpec) DeepCopy() OperatorSpecSpec { var cp OperatorSpecSpec = o return cp } // DeepCopy generates a deep copy of PlatformConfigSpec. func (o PlatformConfigSpec) DeepCopy() PlatformConfigSpec { var cp PlatformConfigSpec = o if o.Addresses != nil { cp.Addresses = make([]AddressSpecSpec, len(o.Addresses)) copy(cp.Addresses, o.Addresses) for i2 := range o.Addresses { cp.Addresses[i2] = o.Addresses[i2].DeepCopy() } } if o.Links != nil { cp.Links = make([]LinkSpecSpec, len(o.Links)) copy(cp.Links, o.Links) for i2 := range o.Links { cp.Links[i2] = o.Links[i2].DeepCopy() } } if o.Routes != nil { cp.Routes = make([]RouteSpecSpec, len(o.Routes)) copy(cp.Routes, o.Routes) for i2 := range o.Routes { cp.Routes[i2] = o.Routes[i2].DeepCopy() } } if o.Hostnames != nil { cp.Hostnames = make([]HostnameSpecSpec, len(o.Hostnames)) copy(cp.Hostnames, o.Hostnames) for i2 := range o.Hostnames { cp.Hostnames[i2] = o.Hostnames[i2].DeepCopy() } } if o.Resolvers != nil { cp.Resolvers = make([]ResolverSpecSpec, len(o.Resolvers)) copy(cp.Resolvers, o.Resolvers) for i2 := range o.Resolvers { cp.Resolvers[i2] = o.Resolvers[i2].DeepCopy() } } if o.TimeServers != nil { cp.TimeServers = make([]TimeServerSpecSpec, len(o.TimeServers)) copy(cp.TimeServers, o.TimeServers) for i2 := range o.TimeServers { cp.TimeServers[i2] = o.TimeServers[i2].DeepCopy() } } if o.Operators != nil { cp.Operators = make([]OperatorSpecSpec, len(o.Operators)) copy(cp.Operators, o.Operators) for i2 := range o.Operators { cp.Operators[i2] = o.Operators[i2].DeepCopy() } } if o.ExternalIPs != nil { cp.ExternalIPs = make([]netip.Addr, len(o.ExternalIPs)) copy(cp.ExternalIPs, o.ExternalIPs) } if o.Probes != nil { cp.Probes = make([]ProbeSpecSpec, len(o.Probes)) copy(cp.Probes, o.Probes) for i2 := range o.Probes { cp.Probes[i2] = o.Probes[i2].DeepCopy() } } if o.Metadata != nil { retV := o.Metadata.DeepCopy() cp.Metadata = &retV } return cp } // DeepCopy generates a deep copy of ProbeSpecSpec. func (o ProbeSpecSpec) DeepCopy() ProbeSpecSpec { var cp ProbeSpecSpec = o return cp } // DeepCopy generates a deep copy of ProbeStatusSpec. func (o ProbeStatusSpec) DeepCopy() ProbeStatusSpec { var cp ProbeStatusSpec = o return cp } // DeepCopy generates a deep copy of ResolverSpecSpec. func (o ResolverSpecSpec) DeepCopy() ResolverSpecSpec { var cp ResolverSpecSpec = o if o.DNSServers != nil { cp.DNSServers = make([]netip.Addr, len(o.DNSServers)) copy(cp.DNSServers, o.DNSServers) } if o.SearchDomains != nil { cp.SearchDomains = make([]string, len(o.SearchDomains)) copy(cp.SearchDomains, o.SearchDomains) } return cp } // DeepCopy generates a deep copy of ResolverStatusSpec. func (o ResolverStatusSpec) DeepCopy() ResolverStatusSpec { var cp ResolverStatusSpec = o if o.DNSServers != nil { cp.DNSServers = make([]netip.Addr, len(o.DNSServers)) copy(cp.DNSServers, o.DNSServers) } if o.SearchDomains != nil { cp.SearchDomains = make([]string, len(o.SearchDomains)) copy(cp.SearchDomains, o.SearchDomains) } return cp } // DeepCopy generates a deep copy of RouteSpecSpec. func (o RouteSpecSpec) DeepCopy() RouteSpecSpec { var cp RouteSpecSpec = o return cp } // DeepCopy generates a deep copy of RouteStatusSpec. func (o RouteStatusSpec) DeepCopy() RouteStatusSpec { var cp RouteStatusSpec = o return cp } // DeepCopy generates a deep copy of RoutingRuleSpecSpec. func (o RoutingRuleSpecSpec) DeepCopy() RoutingRuleSpecSpec { var cp RoutingRuleSpecSpec = o return cp } // DeepCopy generates a deep copy of RoutingRuleStatusSpec. func (o RoutingRuleStatusSpec) DeepCopy() RoutingRuleStatusSpec { var cp RoutingRuleStatusSpec = o return cp } // DeepCopy generates a deep copy of StatusSpec. func (o StatusSpec) DeepCopy() StatusSpec { var cp StatusSpec = o return cp } // DeepCopy generates a deep copy of TimeServerSpecSpec. func (o TimeServerSpecSpec) DeepCopy() TimeServerSpecSpec { var cp TimeServerSpecSpec = o if o.NTPServers != nil { cp.NTPServers = make([]string, len(o.NTPServers)) copy(cp.NTPServers, o.NTPServers) } return cp } // DeepCopy generates a deep copy of TimeServerStatusSpec. func (o TimeServerStatusSpec) DeepCopy() TimeServerStatusSpec { var cp TimeServerStatusSpec = o if o.NTPServers != nil { cp.NTPServers = make([]string, len(o.NTPServers)) copy(cp.NTPServers, o.NTPServers) } return cp } ================================================ FILE: pkg/machinery/resources/network/device_config_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "go.yaml.in/yaml/v4" networkpb "github.com/siderolabs/talos/pkg/machinery/api/resource/network" "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DeviceConfigSpecType is type of DeviceConfigSpec resource. const DeviceConfigSpecType = resource.Type("DeviceConfigSpecs.net.talos.dev") // DeviceConfigSpec resource holds network interface configs. type DeviceConfigSpec = typed.Resource[DeviceConfigSpecSpec, DeviceConfigSpecExtension] // DeviceConfigSpecSpec contains the spec of a device config. // //gotagsrewrite:gen type DeviceConfigSpecSpec struct { Device config.Device `protobuf:"1"` } // NewDeviceConfig creates new interface config. func NewDeviceConfig(id resource.ID, device config.Device) *DeviceConfigSpec { return typed.NewResource[DeviceConfigSpecSpec, DeviceConfigSpecExtension]( resource.NewMetadata(NamespaceName, DeviceConfigSpecType, id, resource.VersionUndefined), DeviceConfigSpecSpec{Device: device}, ) } // DeepCopy generates a deep copy of DeviceConfigSpecSpec. func (spec DeviceConfigSpecSpec) DeepCopy() DeviceConfigSpecSpec { cp := spec cp.Device = spec.Device.(*v1alpha1.Device).DeepCopy() return cp } // DeviceConfigSpecExtension providers auxiliary methods for DeviceConfigSpec. type DeviceConfigSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (DeviceConfigSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DeviceConfigSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } // MarshalProto implements ProtoMarshaler. func (spec *DeviceConfigSpecSpec) MarshalProto() ([]byte, error) { yamlBytes, err := yaml.Marshal(spec.Device) if err != nil { return nil, err } protoSpec := networkpb.DeviceConfigSpecSpec{ YamlMarshalled: yamlBytes, } return proto.Marshal(&protoSpec) } // UnmarshalProto implements protobuf.ResourceUnmarshaler. func (spec *DeviceConfigSpecSpec) UnmarshalProto(protoBytes []byte) error { protoSpec := networkpb.DeviceConfigSpecSpec{} if err := proto.Unmarshal(protoBytes, &protoSpec); err != nil { return err } var dev v1alpha1.Device if err := yaml.Unmarshal(protoSpec.YamlMarshalled, &dev); err != nil { return err } spec.Device = &dev return nil } func init() { if err := protobuf.RegisterResource(DeviceConfigSpecType, &DeviceConfigSpec{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/device_config_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestDeviceConfigProtobufMarshal(t *testing.T) { d := &v1alpha1.Device{ DeviceInterface: "eth0", DeviceAddresses: []string{"10.0.0.8/32"}, } r := network.NewDeviceConfig("id1", d) protoR, err := protobuf.FromResource(r) require.NoError(t, err) marshaled, err := protoR.Marshal() require.NoError(t, err) protoR, err = protobuf.Unmarshal(marshaled) require.NoError(t, err) r2, err := protobuf.UnmarshalResource(protoR) require.NoError(t, err) require.True(t, resource.Equal(r, r2)) } ================================================ FILE: pkg/machinery/resources/network/dns_resolve_cache.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DNSResolveCacheType is type of DNSResolveCache resource. const DNSResolveCacheType = resource.Type("DNSResolveCaches.net.talos.dev") // DNSResolveCache resource holds DNS resolver info. type DNSResolveCache = typed.Resource[DNSResolveCacheSpec, DNSResolveCacheExtension] // DNSResolveCacheSpec describes DNS servers status. // //gotagsrewrite:gen type DNSResolveCacheSpec struct { Status string `yaml:"status" protobuf:"1"` } // NewDNSResolveCache initializes a DNSResolveCache resource. func NewDNSResolveCache(id resource.ID) *DNSResolveCache { return typed.NewResource[DNSResolveCacheSpec, DNSResolveCacheExtension]( resource.NewMetadata(NamespaceName, DNSResolveCacheType, id, resource.VersionUndefined), DNSResolveCacheSpec{}, ) } // DNSResolveCacheExtension provides auxiliary methods for DNSResolveCache. type DNSResolveCacheExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (DNSResolveCacheExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DNSResolveCacheType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Status", JSONPath: "{.status}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[DNSResolveCacheSpec](DNSResolveCacheType, &DNSResolveCache{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/dns_upstream.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "strconv" "sync/atomic" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/handle" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/typed" ) // DNSUpstreamType is type of DNSUpstream resource. const DNSUpstreamType = resource.Type("DNSUpstreams.net.talos.dev") // DNSUpstream resource holds DNS resolver info. type DNSUpstream = typed.Resource[DNSUpstreamSpec, DNSUpstreamExtension] // DNSUpstreamSpec describes DNS upstreams status. type DNSUpstreamSpec = handle.ResourceSpec[DNSUpstreamSpecSpec] // DNSUpstreamSpecSpec describes DNS upstreams status. type DNSUpstreamSpecSpec struct { Conn *DNSConn } // MarshalYAML implements yaml.Marshaler interface. func (d DNSUpstreamSpecSpec) MarshalYAML() (any, error) { d.Conn.Healthcheck() return map[string]string{ "healthy": strconv.FormatBool(d.Conn.Fails() == 0), "addr": d.Conn.Addr(), }, nil } // NewDNSUpstream initializes a DNSUpstream resource. func NewDNSUpstream(id resource.ID) *DNSUpstream { return typed.NewResource[DNSUpstreamSpec, DNSUpstreamExtension]( resource.NewMetadata(NamespaceName, DNSUpstreamType, id, resource.VersionUndefined), DNSUpstreamSpec{Value: DNSUpstreamSpecSpec{}}, ) } // DNSUpstreamExtension provides auxiliary methods for DNSUpstream. type DNSUpstreamExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (DNSUpstreamExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DNSUpstreamType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Healthy", JSONPath: "{.healthy}", }, { Name: "Address", JSONPath: "{.addr}", }, }, } } // Proxy is essentially a proxy.Proxy interface. It's here because we don't want machinery to depend on coredns. // The good thing we don't need any additional methods from coredns, so we can use a simple interface. type Proxy interface { Addr() string Fails() uint32 Healthcheck() Close() Start(time.Duration) } // DNSConn is a wrapper around a Proxy. type DNSConn struct { counter atomic.Int64 // Proxy is essentially a *proxy.Proxy interface. It's here because we don't want machinery to depend on coredns. // We could use a generic struct here, but without generic aliases the usage would look ugly. // Once generic aliases are here, redo the type above as `type DNSUpstream[P Proxy] = typed.Resource[...]`. proxy Proxy } // NewDNSConn initializes a new DNSConn. func NewDNSConn(proxy Proxy) *DNSConn { proxy.Start(500 * time.Millisecond) res := &DNSConn{proxy: proxy} res.counter.Add(1) return res } // Addr returns the address of the DNSConn. func (u *DNSConn) Addr() string { return u.proxy.Addr() } // Fails returns the number of fails of the DNSConn. func (u *DNSConn) Fails() uint32 { return u.proxy.Fails() } // Proxy returns the Proxy field of the DNSConn. func (u *DNSConn) Proxy() Proxy { return u.proxy } // Healthcheck kicks of a round of health checks for this DNSConn. func (u *DNSConn) Healthcheck() { u.proxy.Healthcheck() } // Close stops the DNSConn. func (u *DNSConn) Close() { if u.counter.Add(-1) == 0 { u.proxy.Close() } } // NewRef returns a new reference to the DNSConn. func (u *DNSConn) NewRef() *DNSConn { u.counter.Add(1) return u } ================================================ FILE: pkg/machinery/resources/network/ethernet_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EthernetSpecType is type of EthernetSpec resource. const EthernetSpecType = resource.Type("EthernetSpecs.net.talos.dev") // EthernetSpec resource holds Ethernet network link status. type EthernetSpec = typed.Resource[EthernetSpecSpec, EthernetSpecExtension] // EthernetSpecSpec describes config of Ethernet link. // //gotagsrewrite:gen type EthernetSpecSpec struct { Rings EthernetRingsSpec `yaml:"rings,omitempty" protobuf:"1"` Features map[string]bool `yaml:"features,omitempty" protobuf:"2"` Channels EthernetChannelsSpec `yaml:"channels,omitempty" protobuf:"3"` WakeOnLAN []nethelpers.WOLMode `yaml:"wakeOnLan,omitempty" protobuf:"4"` } // EthernetRingsSpec describes config of Ethernet rings. // //gotagsrewrite:gen type EthernetRingsSpec struct { RX *uint32 `yaml:"rx,omitempty" protobuf:"1"` TX *uint32 `yaml:"tx,omitempty" protobuf:"4"` RXMini *uint32 `yaml:"rx-mini,omitempty" protobuf:"2"` RXJumbo *uint32 `yaml:"rx-jumbo,omitempty" protobuf:"3"` RXBufLen *uint32 `yaml:"rx-buf-len,omitempty" protobuf:"5"` CQESize *uint32 `yaml:"cqe-size,omitempty" protobuf:"6"` TXPush *bool `yaml:"tx-push,omitempty" protobuf:"7"` RXPush *bool `yaml:"rx-push,omitempty" protobuf:"8"` TXPushBufLen *uint32 `yaml:"tx-push-buf-len,omitempty" protobuf:"9"` TCPDataSplit *bool `yaml:"tcp-data-split,omitempty" protobuf:"10"` } // EthernetChannelsSpec describes config of Ethernet channels. // //gotagsrewrite:gen type EthernetChannelsSpec struct { RX *uint32 `yaml:"rx,omitempty" protobuf:"1"` TX *uint32 `yaml:"tx,omitempty" protobuf:"2"` Other *uint32 `yaml:"other,omitempty" protobuf:"3"` Combined *uint32 `yaml:"combined,omitempty" protobuf:"4"` } // NewEthernetSpec initializes a EthernetSpec resource. func NewEthernetSpec(namespace resource.Namespace, id resource.ID) *EthernetSpec { return typed.NewResource[EthernetSpecSpec, EthernetSpecExtension]( resource.NewMetadata(namespace, EthernetSpecType, id, resource.VersionUndefined), EthernetSpecSpec{}, ) } // EthernetSpecExtension provides auxiliary methods for EthernetSpec. type EthernetSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (EthernetSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EthernetSpecType, DefaultNamespace: NamespaceName, Sensitivity: meta.NonSensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EthernetSpecSpec](EthernetSpecType, &EthernetSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/ethernet_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EthernetStatusType is type of EthernetStatus resource. const EthernetStatusType = resource.Type("EthernetStatuses.net.talos.dev") // EthernetStatus resource holds Ethernet network link status. type EthernetStatus = typed.Resource[EthernetStatusSpec, EthernetStatusExtension] // EthernetStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type EthernetStatusSpec struct { LinkState *bool `yaml:"linkState,omitempty" protobuf:"1"` SpeedMegabits int `yaml:"speedMbit,omitempty" protobuf:"2"` Port nethelpers.Port `yaml:"port" protobuf:"3"` Duplex nethelpers.Duplex `yaml:"duplex" protobuf:"4"` OurModes []string `yaml:"ourModes,omitempty" protobuf:"5"` PeerModes []string `yaml:"peerModes,omitempty" protobuf:"6"` Rings *EthernetRingsStatus `yaml:"rings,omitempty" protobuf:"7"` Features EthernetFeatureStatusList `yaml:"features,omitempty" protobuf:"8"` Channels *EthernetChannelsStatus `yaml:"channels,omitempty" protobuf:"9"` WakeOnLAN []nethelpers.WOLMode `yaml:"wakeOnLAN,omitempty" protobuf:"10"` } // EthernetFeatureStatusList is a list of EthernetFeatureStatus. type EthernetFeatureStatusList []EthernetFeatureStatus // MarshalYAML implements yaml.Marshaler interface. func (l EthernetFeatureStatusList) MarshalYAML() (any, error) { // marshal into a custom dict for easier reading // we use this to preserve the order of Features which is important for the user node := &yaml.Node{ Kind: yaml.MappingNode, Content: make([]*yaml.Node, 0, len(l)*2), } for _, f := range l { node.Content = append(node.Content, &yaml.Node{ Kind: yaml.ScalarNode, Value: f.Name, }, &yaml.Node{ Kind: yaml.ScalarNode, Value: f.Status, }) } return node, nil } // EthernetRingsStatus describes status of Ethernet rings. // //gotagsrewrite:gen type EthernetRingsStatus struct { // Read-only settings. RXMax *uint32 `yaml:"rx-max,omitempty" protobuf:"1"` RXMiniMax *uint32 `yaml:"rx-mini-max,omitempty" protobuf:"2"` RXJumboMax *uint32 `yaml:"rx-jumbo-max,omitempty" protobuf:"3"` TXMax *uint32 `yaml:"tx-max,omitempty" protobuf:"4"` TXPushBufLenMax *uint32 `yaml:"tx-push-buf-len-max,omitempty" protobuf:"5"` // Current settings (read-write). RX *uint32 `yaml:"rx,omitempty" protobuf:"6"` RXMini *uint32 `yaml:"rx-mini,omitempty" protobuf:"7"` RXJumbo *uint32 `yaml:"rx-jumbo,omitempty" protobuf:"8"` TX *uint32 `yaml:"tx,omitempty" protobuf:"9"` RXBufLen *uint32 `yaml:"rx-buf-len,omitempty" protobuf:"10"` CQESize *uint32 `yaml:"cqe-size,omitempty" protobuf:"11"` TXPush *bool `yaml:"tx-push,omitempty" protobuf:"12"` RXPush *bool `yaml:"rx-push,omitempty" protobuf:"13"` TXPushBufLen *uint32 `yaml:"tx-push-buf-len,omitempty" protobuf:"14"` TCPDataSplit *bool `yaml:"tcp-data-split,omitempty" protobuf:"15"` } // EthernetChannelsStatus describes status of Ethernet channels. // //gotagsrewrite:gen type EthernetChannelsStatus struct { // Read-only settings. RXMax *uint32 `yaml:"rx-max,omitempty" protobuf:"1"` TXMax *uint32 `yaml:"tx-max,omitempty" protobuf:"2"` OtherMax *uint32 `yaml:"other-max,omitempty" protobuf:"3"` CombinedMax *uint32 `yaml:"combined-max,omitempty" protobuf:"4"` // Current settings (read-write). RX *uint32 `yaml:"rx,omitempty" protobuf:"5"` TX *uint32 `yaml:"tx,omitempty" protobuf:"6"` Other *uint32 `yaml:"other,omitempty" protobuf:"7"` Combined *uint32 `yaml:"combined,omitempty" protobuf:"8"` } // EthernetFeatureStatus describes status of Ethernet features. // //gotagsrewrite:gen type EthernetFeatureStatus struct { Name string `yaml:"name" protobuf:"1"` Status string `yaml:"status" protobuf:"2"` } // NewEthernetStatus initializes a EthernetStatus resource. func NewEthernetStatus(namespace resource.Namespace, id resource.ID) *EthernetStatus { return typed.NewResource[EthernetStatusSpec, EthernetStatusExtension]( resource.NewMetadata(namespace, EthernetStatusType, id, resource.VersionUndefined), EthernetStatusSpec{}, ) } // EthernetStatusExtension provides auxiliary methods for EthernetStatus. type EthernetStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (EthernetStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EthernetStatusType, Aliases: []resource.Type{"ethtool"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Link", JSONPath: `{.linkState}`, }, { Name: "Speed", JSONPath: `{.speedMbit}`, }, }, Sensitivity: meta.NonSensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EthernetStatusSpec](EthernetStatusType, &EthernetStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/hardrware_addr.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // HardwareAddrType is type of HardwareAddr resource. const HardwareAddrType = resource.Type("HardwareAddresses.net.talos.dev") // FirstHardwareAddr is a resource ID for the first NIC HW addr. const FirstHardwareAddr = resource.ID("first") // HardwareAddr resource describes hardware address of the physical links. type HardwareAddr = typed.Resource[HardwareAddrSpec, HardwareAddrExtension] // HardwareAddrSpec describes spec for the link. // //gotagsrewrite:gen type HardwareAddrSpec struct { // Name defines link name Name string `yaml:"name" protobuf:"1"` // Hardware address HardwareAddr nethelpers.HardwareAddr `yaml:"hardwareAddr" protobuf:"2"` } // NewHardwareAddr initializes a HardwareAddr resource. func NewHardwareAddr(namespace resource.Namespace, id resource.ID) *HardwareAddr { return typed.NewResource[HardwareAddrSpec, HardwareAddrExtension]( resource.NewMetadata(namespace, HardwareAddrType, id, resource.VersionUndefined), HardwareAddrSpec{}, ) } // HardwareAddrExtension provides auxiliary methods for HardwareAddr. type HardwareAddrExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (HardwareAddrExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: HardwareAddrType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[HardwareAddrSpec](HardwareAddrType, &HardwareAddr{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/hostdns_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // HostDNSConfigType is type of HostDNSConfig resource. const HostDNSConfigType = resource.Type("HostDNSConfigs.net.talos.dev") // HostDNSConfig resource holds host DNS config. type HostDNSConfig = typed.Resource[HostDNSConfigSpec, HostDNSConfigExtension] // HostDNSConfigID is the singleton ID for HostDNSConfig. const HostDNSConfigID resource.ID = "config" // HostDNSConfigSpec describes host DNS config. // //gotagsrewrite:gen type HostDNSConfigSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` ListenAddresses []netip.AddrPort `yaml:"listenAddresses,omitempty" protobuf:"2"` ServiceHostDNSAddress netip.Addr `yaml:"serviceHostDNSAddress,omitempty" protobuf:"3"` ResolveMemberNames bool `yaml:"resolveMemberNames,omitempty" protobuf:"4"` } // NewHostDNSConfig initializes a HostDNSConfig resource. func NewHostDNSConfig(id resource.ID) *HostDNSConfig { return typed.NewResource[HostDNSConfigSpec, HostDNSConfigExtension]( resource.NewMetadata(NamespaceName, HostDNSConfigType, id, resource.VersionUndefined), HostDNSConfigSpec{}, ) } // HostDNSConfigExtension provides auxiliary methods for HostDNSConfig. type HostDNSConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (HostDNSConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: HostDNSConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Enabled", JSONPath: "{.enabled}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[HostDNSConfigSpec](HostDNSConfigType, &HostDNSConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/hostname_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "fmt" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // HostnameSpecType is type of HostnameSpec resource. const HostnameSpecType = resource.Type("HostnameSpecs.net.talos.dev") // HostnameSpec resource holds node hostname. type HostnameSpec = typed.Resource[HostnameSpecSpec, HostnameSpecExtension] // HostnameID is the ID of the singleton instance. const HostnameID resource.ID = "hostname" // HostnameSpecSpec describes node hostname. // //gotagsrewrite:gen type HostnameSpecSpec struct { Hostname string `yaml:"hostname" protobuf:"1"` Domainname string `yaml:"domainname" protobuf:"2"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"3"` } // Validate the hostname. func (spec *HostnameSpecSpec) Validate() error { lenHostname := len(spec.Hostname) if lenHostname == 0 || lenHostname > 63 { return fmt.Errorf("invalid hostname %q", spec.Hostname) } if len(spec.FQDN()) > 253 { return fmt.Errorf("fqdn is too long: %d", len(spec.FQDN())) } return nil } // FQDN returns the fully-qualified domain name. func (spec *HostnameSpecSpec) FQDN() string { if spec.Domainname == "" { return spec.Hostname } return spec.Hostname + "." + spec.Domainname } // ParseFQDN into parts and validate it. func (spec *HostnameSpecSpec) ParseFQDN(fqdn string) error { parts := strings.SplitN(fqdn, ".", 2) spec.Hostname = parts[0] if len(parts) > 1 { spec.Domainname = parts[1] } return spec.Validate() } // NewHostnameSpec initializes a HostnameSpec resource. func NewHostnameSpec(namespace resource.Namespace, id resource.ID) *HostnameSpec { return typed.NewResource[HostnameSpecSpec, HostnameSpecExtension]( resource.NewMetadata(namespace, HostnameSpecType, id, resource.VersionUndefined), HostnameSpecSpec{}, ) } // HostnameSpecExtension provides auxiliary methods for HostnameSpec. type HostnameSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (HostnameSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: HostnameSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[HostnameSpecSpec](HostnameSpecType, &HostnameSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/hostname_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestHostnameSpecMarshalYAML(t *testing.T) { spec := network.HostnameSpecSpec{ Hostname: "foo", Domainname: "example.com", ConfigLayer: network.ConfigPlatform, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, "hostname: foo\ndomainname: example.com\nlayer: platform\n", string(marshaled)) var spec2 network.HostnameSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/hostname_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // HostnameStatusType is type of HostnameStatus resource. const HostnameStatusType = resource.Type("HostnameStatuses.net.talos.dev") // HostnameStatus resource holds node hostname. type HostnameStatus = typed.Resource[HostnameStatusSpec, HostnameStatusExtension] // HostnameStatusSpec describes node hostname. // //gotagsrewrite:gen type HostnameStatusSpec struct { Hostname string `yaml:"hostname" protobuf:"1"` Domainname string `yaml:"domainname" protobuf:"2"` } // FQDN returns the fully-qualified domain name. func (spec *HostnameStatusSpec) FQDN() string { if spec.Domainname == "" { return spec.Hostname } return spec.Hostname + "." + spec.Domainname } // DNSNames returns DNS names to be added to the certificate based on the hostname and fqdn. func (spec *HostnameStatusSpec) DNSNames() []string { result := []string{spec.Hostname} if spec.Domainname != "" { result = append(result, spec.FQDN()) } return result } // NewHostnameStatus initializes a HostnameStatus resource. func NewHostnameStatus(namespace resource.Namespace, id resource.ID) *HostnameStatus { return typed.NewResource[HostnameStatusSpec, HostnameStatusExtension]( resource.NewMetadata(namespace, HostnameStatusType, id, resource.VersionUndefined), HostnameStatusSpec{}, ) } // HostnameStatusExtension provides auxiliary methods for HostnameStatus. type HostnameStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (HostnameStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: HostnameStatusType, Aliases: []resource.Type{"hostname"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Hostname", JSONPath: "{.hostname}", }, { Name: "Domainname", JSONPath: "{.domainname}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[HostnameStatusSpec](HostnameStatusType, &HostnameStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/link.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "cmp" "net/netip" "slices" "time" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) // VLANSpec describes VLAN settings if Kind == "vlan". // //gotagsrewrite:gen type VLANSpec struct { // VID is the vlan ID. VID uint16 `yaml:"vlanID" protobuf:"1"` // Protocol is the vlan protocol. Protocol nethelpers.VLANProtocol `yaml:"vlanProtocol" protobuf:"2"` } // BondMasterSpec describes bond settings if Kind == "bond". // //gotagsrewrite:gen type BondMasterSpec struct { // Mode specifies the bonding policy Mode nethelpers.BondMode `yaml:"mode" protobuf:"1"` // HashPolicy selects the transmit hash policy to use for slave selection. HashPolicy nethelpers.BondXmitHashPolicy `yaml:"xmitHashPolicy" protobuf:"2"` // LACPRate specifies the rate at which LACPDU frames are sent. LACPRate nethelpers.LACPRate `yaml:"lacpRate" protobuf:"3"` // ARPValidate specifies whether or not ARP probes and replies should be validated. ARPValidate nethelpers.ARPValidate `yaml:"arpValidate" protobuf:"4"` // ARPAllTargets specifies whether ARP probes should be sent to any or all targets. ARPAllTargets nethelpers.ARPAllTargets `yaml:"arpAllTargets" protobuf:"5"` // PrimaryIndex is a device index specifying which slave is the primary device. PrimaryIndex *uint32 `yaml:"primary,omitempty" protobuf:"6"` // PrimaryReselect specifies the policy under which the primary slave should be reselected. PrimaryReselect nethelpers.PrimaryReselect `yaml:"primaryReselect" protobuf:"7"` // FailOverMac whether active-backup mode should set all slaves to the same MAC address at enslavement, when enabled, or perform special handling. FailOverMac nethelpers.FailOverMAC `yaml:"failOverMac" protobuf:"8"` // ADSelect specifies the aggregate selection policy for 802.3ad. ADSelect nethelpers.ADSelect `yaml:"adSelect,omitempty" protobuf:"9"` // MIIMon is the link monitoring frequency in milliseconds. MIIMon uint32 `yaml:"miimon,omitempty" protobuf:"10"` // UpDelay is the time, in milliseconds, to wait before enabling a slave after a link recovery has been detected. UpDelay uint32 `yaml:"updelay,omitempty" protobuf:"11"` // DownDelay is the time, in milliseconds, to wait before disabling a slave after a link failure has been detected. DownDelay uint32 `yaml:"downdelay,omitempty" protobuf:"12"` // ARPInterval is the ARP link monitoring frequency in milliseconds. ARPInterval uint32 `yaml:"arpInterval,omitempty" protobuf:"13"` // ResendIGMP specifies the number of times IGMP packets should be resent. ResendIGMP uint32 `yaml:"resendIgmp,omitempty" protobuf:"14"` // MinLinks specifies the minimum number of active links to assert carrier. MinLinks uint32 `yaml:"minLinks,omitempty" protobuf:"15"` // LPInterval specifies the number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch. LPInterval uint32 `yaml:"lpInterval,omitempty" protobuf:"16"` // PacketsPerSlave specifies the number of packets to transmit through a slave before moving to the next one. PacketsPerSlave uint32 `yaml:"packetsPerSlave,omitempty" protobuf:"17"` // NumPeerNotif specifies the number of peer notifications // (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements) to be issued after a failover event. NumPeerNotif uint8 `yaml:"numPeerNotif,omitempty" protobuf:"18"` // TLBDynamicLB specifies if dynamic shuffling of flows is enabled in tlb or alb mode. TLBDynamicLB uint8 `yaml:"tlbLogicalLb,omitempty" protobuf:"19"` // AllSlavesActive specifies that duplicate frames (received on inactive ports) should be dropped (0) or delivered (1). AllSlavesActive uint8 `yaml:"allSlavesActive,omitempty" protobuf:"20"` // UseCarrier specifies whether or not miimon should use MII or ETHTOOL. UseCarrier bool `yaml:"useCarrier,omitempty" protobuf:"21"` // ADActorSysPrio is the actor system priority for 802.3ad. ADActorSysPrio uint16 `yaml:"adActorSysPrio,omitempty" protobuf:"22"` // ADUserPortKey is the user port key (upper 10 bits) for 802.3ad. ADUserPortKey uint16 `yaml:"adUserPortKey,omitempty" protobuf:"23"` // PeerNotifyDelay is the delay, in milliseconds, between each peer notification. PeerNotifyDelay uint32 `yaml:"peerNotifyDelay,omitempty" protobuf:"24"` // ARPIPTargets is the list of IP addresses to use for ARP link monitoring when ARPInterval is set. // // Maximum of 16 targets are supported. ARPIPTargets []netip.Addr `yaml:"arpIpTargets,omitempty" protobuf:"25"` // NSIP6Targets is the list of IPv6 addresses to use for NS link monitoring when ARPInterval is set. // // Maximum of 16 targets are supported. NSIP6Targets []netip.Addr `yaml:"nsIp6Targets,omitempty" protobuf:"26"` // ADLACPActive specifies whether to send LACPDU frames periodically. ADLACPActive nethelpers.ADLACPActive `yaml:"adLacpActive,omitempty" protobuf:"27"` // MissedMax is the number of arp_interval monitor checks that must fail in order for an interface to be marked down by the ARP monitor. MissedMax uint8 `yaml:"missedMax,omitempty" protobuf:"28"` } // Equal checks two BondMasterSpecs for equality. // //nolint:gocyclo,cyclop func (spec *BondMasterSpec) Equal(other *BondMasterSpec) bool { if spec.Mode != other.Mode { return false } if spec.HashPolicy != other.HashPolicy { return false } if spec.LACPRate != other.LACPRate { return false } if spec.ARPValidate != other.ARPValidate { return false } if spec.ARPAllTargets != other.ARPAllTargets { return false } if spec.PrimaryIndex != nil && other.PrimaryIndex != nil && *spec.PrimaryIndex != *other.PrimaryIndex { return false } if spec.PrimaryReselect != other.PrimaryReselect { return false } if spec.FailOverMac != other.FailOverMac { return false } if spec.ADSelect != other.ADSelect { return false } if spec.MIIMon != other.MIIMon { return false } if spec.UpDelay != other.UpDelay { return false } if spec.DownDelay != other.DownDelay { return false } if spec.ARPInterval != other.ARPInterval { return false } if spec.ResendIGMP != other.ResendIGMP { return false } if spec.MinLinks != other.MinLinks { return false } if spec.LPInterval != other.LPInterval { return false } if spec.PacketsPerSlave != other.PacketsPerSlave { return false } if spec.NumPeerNotif != other.NumPeerNotif { return false } if spec.TLBDynamicLB != other.TLBDynamicLB { return false } if spec.AllSlavesActive != other.AllSlavesActive { return false } if spec.UseCarrier != other.UseCarrier { return false } if spec.ADActorSysPrio != other.ADActorSysPrio { return false } if spec.ADUserPortKey != other.ADUserPortKey { return false } if spec.PeerNotifyDelay != other.PeerNotifyDelay { return false } if len(spec.ARPIPTargets) != len(other.ARPIPTargets) { return false } for i := range spec.ARPIPTargets { if spec.ARPIPTargets[i] != other.ARPIPTargets[i] { return false } } if len(spec.NSIP6Targets) != len(other.NSIP6Targets) { return false } for i := range spec.NSIP6Targets { if spec.NSIP6Targets[i] != other.NSIP6Targets[i] { return false } } if spec.ADLACPActive != other.ADLACPActive { return false } if spec.Mode != nethelpers.BondMode8023AD && spec.Mode != nethelpers.BondModeALB && spec.Mode != nethelpers.BondModeTLB { if spec.MissedMax != other.MissedMax { return false } } return true } // IsZero checks if the BondMasterSpec is zero value. // //nolint:gocyclo,cyclop func (spec *BondMasterSpec) IsZero() bool { return spec.Mode == 0 && spec.HashPolicy == 0 && spec.LACPRate == 0 && spec.ARPValidate == 0 && spec.ARPAllTargets == 0 && spec.PrimaryIndex == nil && spec.PrimaryReselect == 0 && spec.FailOverMac == 0 && spec.ADSelect == 0 && spec.MIIMon == 0 && spec.UpDelay == 0 && spec.DownDelay == 0 && spec.ARPInterval == 0 && spec.ResendIGMP == 0 && spec.MinLinks == 0 && spec.LPInterval == 0 && spec.PacketsPerSlave == 0 && spec.NumPeerNotif == 0 && spec.TLBDynamicLB == 0 && spec.AllSlavesActive == 0 && !spec.UseCarrier && spec.ADActorSysPrio == 0 && spec.ADUserPortKey == 0 && spec.PeerNotifyDelay == 0 && len(spec.ARPIPTargets) == 0 && len(spec.NSIP6Targets) == 0 && spec.ADLACPActive == 0 && spec.MissedMax == 0 } // BridgeMasterSpec describes bridge settings if Kind == "bridge". // //gotagsrewrite:gen type BridgeMasterSpec struct { STP STPSpec `yaml:"stp,omitempty" protobuf:"1"` VLAN BridgeVLANSpec `yaml:"vlan,omitempty" protobuf:"2"` } // STPSpec describes Spanning Tree Protocol (STP) settings of a bridge. // //gotagsrewrite:gen type STPSpec struct { Enabled bool `yaml:"enabled" protobuf:"1"` } // BridgeVLANSpec describes VLAN settings of a bridge. // //gotagsrewrite:gen type BridgeVLANSpec struct { FilteringEnabled bool `yaml:"filteringEnabled" protobuf:"1"` } // VRFMasterSpec describes vrf settings if Kind == "vrf". // //gotagsrewrite:gen type VRFMasterSpec struct { Table nethelpers.RoutingTable `yaml:"table" protobuf:"1"` } // WireguardSpec describes Wireguard settings if Kind == "wireguard". // //gotagsrewrite:gen type WireguardSpec struct { // PrivateKey is used to configure the link, present only in the LinkSpec. PrivateKey string `yaml:"privateKey,omitempty" protobuf:"1"` // PublicKey is only used in LinkStatus to show the link status. PublicKey string `yaml:"publicKey,omitempty" protobuf:"2"` ListenPort int `yaml:"listenPort" protobuf:"3"` FirewallMark int `yaml:"firewallMark" protobuf:"4"` Peers []WireguardPeer `yaml:"peers" protobuf:"5"` } // WireguardPeer describes a single peer. // //gotagsrewrite:gen type WireguardPeer struct { PublicKey string `yaml:"publicKey" protobuf:"1"` PresharedKey string `yaml:"presharedKey" protobuf:"2"` Endpoint string `yaml:"endpoint" protobuf:"3"` PersistentKeepaliveInterval time.Duration `yaml:"persistentKeepaliveInterval" protobuf:"4"` AllowedIPs []netip.Prefix `yaml:"allowedIPs" protobuf:"5"` } // ID Returns the VID for type VLANSpec. func (vlan VLANSpec) ID() uint16 { return vlan.VID } // MTU Returns MTU=0 for type VLANSpec. func (vlan VLANSpec) MTU() uint32 { return 0 } // Mode returns the protocol (mode) for type VLANSpec. func (vlan VLANSpec) Mode() nethelpers.VLANProtocol { return vlan.Protocol } // Equal checks two WireguardPeer structs for equality. // // `spec` is considered to be the result of getting current Wireguard configuration, // while `other` is the new (updated configuration). func (peer *WireguardPeer) Equal(other *WireguardPeer) bool { if peer.PublicKey != other.PublicKey { return false } if peer.PresharedKey != other.PresharedKey { return false } // if the Endpoint is not set in `other`, don't consider this to be a change if other.Endpoint != "" && peer.Endpoint != other.Endpoint { return false } if peer.PersistentKeepaliveInterval != other.PersistentKeepaliveInterval { return false } if len(peer.AllowedIPs) != len(other.AllowedIPs) { return false } for i := range peer.AllowedIPs { if peer.AllowedIPs[i].Addr().Compare(other.AllowedIPs[i].Addr()) != 0 { return false } if peer.AllowedIPs[i].Bits() != other.AllowedIPs[i].Bits() { return false } } return true } // IsZero checks if the WireguardSpec is zero value. func (spec *WireguardSpec) IsZero() bool { return spec.PrivateKey == "" && spec.ListenPort == 0 && spec.FirewallMark == 0 && len(spec.Peers) == 0 } // Equal checks two WireguardSpecs for equality. // // Both specs should be sorted before calling this method. // // `spec` is considered to be the result of getting current Wireguard configuration, // while `other` is the new (updated configuration). func (spec *WireguardSpec) Equal(other *WireguardSpec) bool { if spec.PrivateKey != other.PrivateKey { return false } // listenPort of '0' means use any available port, so we shouldn't consider this to be a "change" if spec.ListenPort != other.ListenPort && other.ListenPort != 0 { return false } if spec.FirewallMark != other.FirewallMark { return false } if len(spec.Peers) != len(other.Peers) { return false } for i := range spec.Peers { if !spec.Peers[i].Equal(&other.Peers[i]) { return false } } return true } // Sort the spec so that comparison is possible. func (spec *WireguardSpec) Sort() { slices.SortFunc(spec.Peers, func(a, b WireguardPeer) int { return cmp.Compare(a.PublicKey, b.PublicKey) }) for k := range spec.Peers { slices.SortFunc(spec.Peers[k].AllowedIPs, func(left, right netip.Prefix) int { if res := left.Addr().Compare(right.Addr()); res != 0 { return res } return cmp.Compare(left.Bits(), right.Bits()) }) } } // Merge with other Wireguard spec overwriting non-zero values. func (spec *WireguardSpec) Merge(other WireguardSpec) { if other.ListenPort != 0 { spec.ListenPort = other.ListenPort } if other.FirewallMark != 0 { spec.FirewallMark = other.FirewallMark } if other.PrivateKey != "" { spec.PrivateKey = other.PrivateKey } // avoid adding same peer twice, no real peer information merging for now for _, peer := range other.Peers { exists := false for _, p := range spec.Peers { if p.PublicKey == peer.PublicKey { exists = true break } } if !exists { spec.Peers = append(spec.Peers, peer) } } } ================================================ FILE: pkg/machinery/resources/network/link_alias_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // LinkAliasSpecType is type of LinkAliasSpec resource. const LinkAliasSpecType = resource.Type("LinkAliasSpecs.net.talos.dev") // LinkAliasSpec resource tells which link should have which alias (name). // // If the link shouldn't have the alias, resource is removed. type LinkAliasSpec = typed.Resource[LinkAliasSpecSpec, LinkAliasSpecExtension] // LinkAliasSpecSpec describes status of rendered secrets. // //gotagsrewrite:gen type LinkAliasSpecSpec struct { Alias string `yaml:"alias" protobuf:"1"` } // NewLinkAliasSpec initializes a LinkAliasSpec resource. func NewLinkAliasSpec(namespace resource.Namespace, id resource.ID) *LinkAliasSpec { return typed.NewResource[LinkAliasSpecSpec, LinkAliasSpecExtension]( resource.NewMetadata(namespace, LinkAliasSpecType, id, resource.VersionUndefined), LinkAliasSpecSpec{}, ) } // LinkAliasSpecExtension provides auxiliary methods for LinkAliasSpec. type LinkAliasSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (LinkAliasSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: LinkAliasSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Alias", JSONPath: `{.alias}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[LinkAliasSpecSpec](LinkAliasSpecType, &LinkAliasSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/link_name_resolver.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import "iter" // LinkResolver resolves link names and aliases to actual link names. type LinkResolver struct { lookup map[string]string // map of link names/aliases to link names } // Resolve resolves the link name or alias to the actual link name. // // If the link name or alias is not found in the lookup table, it is returned as is. func (r *LinkResolver) Resolve(name string) string { if resolved, ok := r.lookup[name]; ok { return resolved } return name } // NewLinkResolver creates a new link name resolver. func NewLinkResolver(f func() iter.Seq[*LinkStatus]) *LinkResolver { lookup := make(map[string]string) for link := range f() { for alias := range AllLinkAliases(link) { lookup[alias] = link.Metadata().ID() } } for link := range f() { lookup[link.Metadata().ID()] = link.Metadata().ID() } return &LinkResolver{lookup: lookup} } // NewEmptyLinkResolver creates a new link name resolver with an empty lookup table. func NewEmptyLinkResolver() *LinkResolver { return &LinkResolver{} } ================================================ FILE: pkg/machinery/resources/network/link_name_resolver_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "iter" "slices" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestLinkNameResolver(t *testing.T) { t.Parallel() link1 := network.NewLinkStatus(network.NamespaceName, "eth0") link1.TypedSpec().Alias = "net0" link1.TypedSpec().AltNames = []string{"ext0"} link2 := network.NewLinkStatus(network.NamespaceName, "eth1") link3 := network.NewLinkStatus(network.NamespaceName, "eth2") link3.TypedSpec().AltNames = []string{"ext2"} links := []*network.LinkStatus{ link1, link2, link3, } resolver := network.NewLinkResolver(func() iter.Seq[*network.LinkStatus] { return slices.Values(links) }) assert.Equal(t, "eth0", resolver.Resolve("eth0")) assert.Equal(t, "eth0", resolver.Resolve("net0")) assert.Equal(t, "eth0", resolver.Resolve("ext0")) assert.Equal(t, "eth1", resolver.Resolve("eth1")) assert.Equal(t, "eth2", resolver.Resolve("eth2")) assert.Equal(t, "eth2", resolver.Resolve("ext2")) assert.Equal(t, "eth3", resolver.Resolve("eth3")) } ================================================ FILE: pkg/machinery/resources/network/link_refresh.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // LinkRefreshType is type of LinkRefresh resource. const LinkRefreshType = resource.Type("LinkRefreshes.net.talos.dev") // LinkRefresh resource is used to communicate link changes which can't be subscribed to via netlink. // // The only usecase for now is the Wireguards, as there's no way subscribe to wireguard updates // via the netlink API. // // Whenever Wireguard interface is updated, LinkRefresh resource is modified to trigger a reconcile // loop in the LinkStatusController. type LinkRefresh = typed.Resource[LinkRefreshSpec, LinkRefreshExtension] // LinkRefreshSpec describes status of rendered secrets. // //gotagsrewrite:gen type LinkRefreshSpec struct { Generation int `yaml:"generation" protobuf:"1"` } // Bump performs an update. func (s *LinkRefreshSpec) Bump() { s.Generation++ } // NewLinkRefresh initializes a LinkRefresh resource. func NewLinkRefresh(namespace resource.Namespace, id resource.ID) *LinkRefresh { return typed.NewResource[LinkRefreshSpec, LinkRefreshExtension]( resource.NewMetadata(namespace, LinkRefreshType, id, resource.VersionUndefined), LinkRefreshSpec{}, ) } // LinkRefreshExtension provides auxiliary methods for LinkRefresh. type LinkRefreshExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (LinkRefreshExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: LinkRefreshType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[LinkRefreshSpec](LinkRefreshType, &LinkRefresh{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/link_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "slices" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // LinkSpecType is type of LinkSpec resource. const LinkSpecType = resource.Type("LinkSpecs.net.talos.dev") // LinkSpec resource describes desired state of the link (network interface). type LinkSpec = typed.Resource[LinkSpecSpec, LinkSpecExtension] // LinkSpecSpec describes spec for the link. // //gotagsrewrite:gen type LinkSpecSpec struct { // Name defines link name Name string `yaml:"name" protobuf:"1"` // Logical describes if the interface should be created on the fly if it doesn't exist. Logical bool `yaml:"logical" protobuf:"2"` // If Up is true, bring interface up, otherwise bring interface down. // // TODO: make *bool ? Up bool `yaml:"up" protobuf:"3"` // Interface MTU (always applies). MTU uint32 `yaml:"mtu" protobuf:"4"` // Kind and Type are only required for Logical interfaces. Kind string `yaml:"kind" protobuf:"5"` Type nethelpers.LinkType `yaml:"type" protobuf:"6"` // Override hardware (MAC) address (if supported). HardwareAddress nethelpers.HardwareAddr `yaml:"hardwareAddr,omitempty" protobuf:"15"` // ParentName indicates link parent for VLAN interfaces. ParentName string `yaml:"parentName,omitempty" protobuf:"7"` // MasterName indicates master link for enslaved bonded interfaces. BondSlave BondSlave `yaml:",omitempty,inline" protobuf:"8"` // BridgeSlave indicates master link for bridged interfaces. BridgeSlave BridgeSlave `yaml:"bridgeSlave,omitempty" protobuf:"9"` // VRFSlave indicates master link for interfaces in a vrf VRFSlave VRFSlave `yaml:"vrfSlave,omitempty" protobuf:"18"` // These structures are present depending on "Kind" for Logical interfaces. VLAN VLANSpec `yaml:"vlan,omitempty" protobuf:"10"` BondMaster BondMasterSpec `yaml:"bondMaster,omitempty" protobuf:"11"` BridgeMaster BridgeMasterSpec `yaml:"bridgeMaster,omitempty" protobuf:"12"` VRFMaster VRFMasterSpec `yaml:"vrfMaster,omitempty" protobuf:"17"` Wireguard WireguardSpec `yaml:"wireguard,omitempty" protobuf:"13"` // Configuration layer. ConfigLayer ConfigLayer `yaml:"layer" protobuf:"14"` // Multicast indicates whether the multicast flag should be set on the interface to the value. Multicast *bool `yaml:"multicast,omitempty" protobuf:"16"` } // BondSlave contains a bond's master name and slave index. // //gotagsrewrite:gen type BondSlave struct { // MasterName indicates master link for enslaved bonded interfaces. MasterName string `yaml:"masterName,omitempty" protobuf:"1"` // SlaveIndex indicates a slave's position in bond. SlaveIndex int `yaml:"slaveIndex,omitempty" protobuf:"2"` } // BridgeSlave contains the name of the master bridge of a bridged interface // //gotagsrewrite:gen type BridgeSlave struct { // MasterName indicates master link for enslaved bridged interfaces. MasterName string `yaml:"masterName,omitempty" protobuf:"1"` } // VRFSlave contains the name of the master vrf for an interface // //gotagsrewrite:gen type VRFSlave struct { MasterName string `yaml:"masterName,omitempty" protobuf:"1"` } // Merge with other, overwriting fields from other if set. func (spec *LinkSpecSpec) Merge(other *LinkSpecSpec) error { // prefer Logical, as it is defined for bonds/vlans, etc. updateIfNotZero(&spec.Logical, other.Logical) updateIfNotZero(&spec.Up, other.Up) updateIfNotZero(&spec.MTU, other.MTU) updateIfNotZero(&spec.Kind, other.Kind) updateIfNotZero(&spec.Type, other.Type) updateIfNotZero(&spec.ParentName, other.ParentName) updateIfNotZero(&spec.BondSlave, other.BondSlave) updateIfNotZero(&spec.VLAN, other.VLAN) updateIfNotZero(&spec.BridgeMaster, other.BridgeMaster) updateIfNotZero(&spec.BridgeSlave, other.BridgeSlave) updateIfNotZero(&spec.VRFMaster, other.VRFMaster) updateIfNotZero(&spec.VRFSlave, other.VRFSlave) if !other.BondMaster.IsZero() { spec.BondMaster = other.BondMaster.DeepCopy() } if other.HardwareAddress != nil { spec.HardwareAddress = slices.Clone(other.HardwareAddress) } // Wireguard config should be able to apply non-zero values in earlier config layers which may be zero values in later layers. // Thus, we handle each Wireguard configuration value discretely. if !other.Wireguard.IsZero() { if spec.Wireguard.IsZero() { spec.Wireguard = other.Wireguard } else { spec.Wireguard.Merge(other.Wireguard) } } spec.ConfigLayer = other.ConfigLayer return nil } func updateIfNotZero[T comparable](target *T, source T) { var zero T if source != zero { *target = source } } // NewLinkSpec initializes a LinkSpec resource. func NewLinkSpec(namespace resource.Namespace, id resource.ID) *LinkSpec { return typed.NewResource[LinkSpecSpec, LinkSpecExtension]( resource.NewMetadata(namespace, LinkSpecType, id, resource.VersionUndefined), LinkSpecSpec{}, ) } // LinkSpecExtension provides auxiliary methods for LinkSpec. type LinkSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (LinkSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: LinkSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[LinkSpecSpec](LinkSpecType, &LinkSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/link_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestLinkSpecMarshalYAML(t *testing.T) { spec := network.LinkSpecSpec{ Name: "eth0", Logical: true, Up: true, MTU: 1437, Kind: "eth", Type: nethelpers.LinkEther, ParentName: "eth1", BondSlave: network.BondSlave{ MasterName: "bond0", SlaveIndex: 0, }, VRFSlave: network.VRFSlave{ MasterName: "vrf-blue", }, VLAN: network.VLANSpec{ VID: 25, Protocol: nethelpers.VLANProtocol8021AD, }, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondMode8023AD, HashPolicy: nethelpers.BondXmitPolicyEncap34, LACPRate: nethelpers.LACPRateFast, ARPValidate: nethelpers.ARPValidateAll, ARPAllTargets: nethelpers.ARPAllTargetsAny, PrimaryIndex: new(uint32(3)), PrimaryReselect: nethelpers.PrimaryReselectBetter, FailOverMac: nethelpers.FailOverMACFollow, ADSelect: nethelpers.ADSelectCount, MIIMon: 33, UpDelay: 100, DownDelay: 200, ARPInterval: 10, ResendIGMP: 30, MinLinks: 1, LPInterval: 3, PacketsPerSlave: 4, NumPeerNotif: 4, TLBDynamicLB: 5, AllSlavesActive: 1, UseCarrier: true, ADActorSysPrio: 6, ADUserPortKey: 7, PeerNotifyDelay: 40, }, VRFMaster: network.VRFMasterSpec{ Table: 123, }, Wireguard: network.WireguardSpec{ PrivateKey: "foo=", PublicKey: "bar=", ListenPort: 51820, FirewallMark: 11233, Peers: []network.WireguardPeer{ { PublicKey: "peer=", PresharedKey: "key=", Endpoint: "127.0.0.1:3333", PersistentKeepaliveInterval: 30 * time.Second, AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("192.83.93.94/31"), }, }, }, }, ConfigLayer: network.ConfigPlatform, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, `name: eth0 logical: true up: true mtu: 1437 kind: eth type: ether parentName: eth1 masterName: bond0 vrfSlave: masterName: vrf-blue vlan: vlanID: 25 vlanProtocol: 802.1ad bondMaster: mode: 802.3ad xmitHashPolicy: encap3+4 lacpRate: fast arpValidate: all arpAllTargets: any primary: 3 primaryReselect: better failOverMac: follow adSelect: count miimon: 33 updelay: 100 downdelay: 200 arpInterval: 10 resendIgmp: 30 minLinks: 1 lpInterval: 3 packetsPerSlave: 4 numPeerNotif: 4 tlbLogicalLb: 5 allSlavesActive: 1 useCarrier: true adActorSysPrio: 6 adUserPortKey: 7 peerNotifyDelay: 40 vrfMaster: table: "123" wireguard: privateKey: foo= publicKey: bar= listenPort: 51820 firewallMark: 11233 peers: - publicKey: peer= presharedKey: key= endpoint: 127.0.0.1:3333 persistentKeepaliveInterval: 30s allowedIPs: - 192.83.93.94/31 layer: platform `, string(marshaled)) var spec2 network.LinkSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/link_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "iter" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // LinkStatusType is type of LinkStatus resource. const LinkStatusType = resource.Type("LinkStatuses.net.talos.dev") // LinkStatus resource holds physical network link status. type LinkStatus = typed.Resource[LinkStatusSpec, LinkStatusExtension] // LinkStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type LinkStatusSpec struct { // Fields coming from rtnetlink API. Alias string `yaml:"alias,omitempty" protobuf:"31"` AltNames []string `yaml:"altNames,omitempty" protobuf:"32"` Index uint32 `yaml:"index" protobuf:"1"` Type nethelpers.LinkType `yaml:"type" protobuf:"2"` LinkIndex uint32 `yaml:"linkIndex" protobuf:"3"` Flags nethelpers.LinkFlags `yaml:"flags" protobuf:"4"` HardwareAddr nethelpers.HardwareAddr `yaml:"hardwareAddr" protobuf:"5"` PermanentAddr nethelpers.HardwareAddr `yaml:"permanentAddr" protobuf:"30"` BroadcastAddr nethelpers.HardwareAddr `yaml:"broadcastAddr" protobuf:"6"` MTU uint32 `yaml:"mtu" protobuf:"7"` QueueDisc string `yaml:"queueDisc" protobuf:"8"` MasterIndex uint32 `yaml:"masterIndex,omitempty" protobuf:"9"` OperationalState nethelpers.OperationalState `yaml:"operationalState" protobuf:"10"` Kind string `yaml:"kind" protobuf:"11"` SlaveKind string `yaml:"slaveKind" protobuf:"12"` BusPath string `yaml:"busPath,omitempty" protobuf:"13"` PCIID string `yaml:"pciID,omitempty" protobuf:"14"` Driver string `yaml:"driver,omitempty" protobuf:"15"` DriverVersion string `yaml:"driverVersion,omitempty" protobuf:"16"` FirmwareVersion string `yaml:"firmwareVersion,omitempty" protobuf:"17"` ProductID string `yaml:"productID,omitempty" protobuf:"18"` VendorID string `yaml:"vendorID,omitempty" protobuf:"19"` Product string `yaml:"product,omitempty" protobuf:"20"` Vendor string `yaml:"vendor,omitempty" protobuf:"21"` // Fields coming from ethtool API. LinkState bool `yaml:"linkState" protobuf:"22"` SpeedMegabits int `yaml:"speedMbit,omitempty" protobuf:"23"` Port nethelpers.Port `yaml:"port" protobuf:"24"` Duplex nethelpers.Duplex `yaml:"duplex" protobuf:"25"` // Following fields are only populated with respective Kind. VLAN VLANSpec `yaml:"vlan,omitempty" protobuf:"26"` BridgeMaster BridgeMasterSpec `yaml:"bridgeMaster,omitempty" protobuf:"27"` BondMaster BondMasterSpec `yaml:"bondMaster,omitempty" protobuf:"28"` VRFMaster VRFMasterSpec `yaml:"vrfMaster,omitempty" protobuf:"33"` Wireguard WireguardSpec `yaml:"wireguard,omitempty" protobuf:"29"` } // Physical checks if the link is physical ethernet. func (s LinkStatusSpec) Physical() bool { return s.Type == nethelpers.LinkEther && s.Kind == "" } // AllLinkNames returns all link names, including name, alias and altnames. func AllLinkNames(link *LinkStatus) iter.Seq[string] { return func(yield func(string) bool) { if !yield(link.Metadata().ID()) { return } for alias := range AllLinkAliases(link) { if !yield(alias) { return } } } } // AllLinkAliases returns all link aliases (altnames and alias). func AllLinkAliases(link *LinkStatus) iter.Seq[string] { return func(yield func(string) bool) { if link.TypedSpec().Alias != "" { if !yield(link.TypedSpec().Alias) { return } } for _, altName := range link.TypedSpec().AltNames { if !yield(altName) { return } } } } // NewLinkStatus initializes a LinkStatus resource. func NewLinkStatus(namespace resource.Namespace, id resource.ID) *LinkStatus { return typed.NewResource[LinkStatusSpec, LinkStatusExtension]( resource.NewMetadata(namespace, LinkStatusType, id, resource.VersionUndefined), LinkStatusSpec{}, ) } // LinkStatusExtension provides auxiliary methods for LinkStatus. type LinkStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (LinkStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: LinkStatusType, Aliases: []resource.Type{"link", "links"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Alias", JSONPath: `{.alias}`, }, { Name: "Type", JSONPath: `{.type}`, }, { Name: "Kind", JSONPath: `{.kind}`, }, { Name: "Hw Addr", JSONPath: `{.hardwareAddr}`, }, { Name: "Oper State", JSONPath: `{.operationalState}`, }, { Name: "Link State", JSONPath: `{.linkState}`, }, }, Sensitivity: meta.NonSensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[LinkStatusSpec](LinkStatusType, &LinkStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/link_status_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:dupl package network_test import ( "net" "net/netip" "testing" "time" "github.com/mdlayher/ethtool" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestLinkStatusMarshalYAML(t *testing.T) { hwAddr, err := net.ParseMAC("01:23:45:67:89:ab") require.NoError(t, err) bcAddr, err := net.ParseMAC("ff:ff:ff:ff:ff:ff") require.NoError(t, err) spec := network.LinkStatusSpec{ Index: 3, Type: nethelpers.LinkEther, LinkIndex: 44, Flags: nethelpers.LinkFlags(nethelpers.LinkUp | nethelpers.LinkRunning), HardwareAddr: nethelpers.HardwareAddr(hwAddr), PermanentAddr: nethelpers.HardwareAddr(hwAddr), BroadcastAddr: nethelpers.HardwareAddr(bcAddr), MTU: 1500, QueueDisc: "fifo", MasterIndex: 4, OperationalState: nethelpers.OperStateLowerLayerDown, Kind: "bridge", SlaveKind: "ether", BusPath: "00:11:22", PCIID: "0000:00:00.0", Driver: "bonding", DriverVersion: "1.0.0", FirmwareVersion: "3.1.5", ProductID: "0x3ebf", VendorID: "0x1d6b", Product: "10Gbase-T", Vendor: "Intel Corporation", LinkState: true, SpeedMegabits: 1024, Port: nethelpers.Port(ethtool.TwistedPair), Duplex: nethelpers.Duplex(ethtool.Full), VLAN: network.VLANSpec{ VID: 25, Protocol: nethelpers.VLANProtocol8021AD, }, BondMaster: network.BondMasterSpec{ Mode: nethelpers.BondMode8023AD, HashPolicy: nethelpers.BondXmitPolicyEncap34, LACPRate: nethelpers.LACPRateFast, ARPValidate: nethelpers.ARPValidateAll, ARPAllTargets: nethelpers.ARPAllTargetsAny, PrimaryIndex: new(uint32(3)), PrimaryReselect: nethelpers.PrimaryReselectBetter, FailOverMac: nethelpers.FailOverMACFollow, ADSelect: nethelpers.ADSelectCount, MIIMon: 33, UpDelay: 100, DownDelay: 200, ARPInterval: 10, ResendIGMP: 30, MinLinks: 1, LPInterval: 3, PacketsPerSlave: 4, NumPeerNotif: 4, TLBDynamicLB: 5, AllSlavesActive: 1, UseCarrier: true, ADActorSysPrio: 6, ADUserPortKey: 7, PeerNotifyDelay: 40, }, VRFMaster: network.VRFMasterSpec{ Table: 123, }, Wireguard: network.WireguardSpec{ PublicKey: "bar=", ListenPort: 51820, FirewallMark: 11233, Peers: []network.WireguardPeer{ { PublicKey: "peer=", PresharedKey: "key=", Endpoint: "127.0.0.1:3333", PersistentKeepaliveInterval: 30 * time.Second, AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("192.83.93.94/31"), }, }, }, }, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, `index: 3 type: ether linkIndex: 44 flags: UP,RUNNING hardwareAddr: 01:23:45:67:89:ab permanentAddr: 01:23:45:67:89:ab broadcastAddr: ff:ff:ff:ff:ff:ff mtu: 1500 queueDisc: fifo masterIndex: 4 operationalState: lowerLayerDown kind: bridge slaveKind: ether busPath: "00:11:22" pciID: "0000:00:00.0" driver: bonding driverVersion: 1.0.0 firmwareVersion: 3.1.5 productID: "0x3ebf" vendorID: "0x1d6b" product: 10Gbase-T vendor: Intel Corporation linkState: true speedMbit: 1024 port: TwistedPair duplex: Full vlan: vlanID: 25 vlanProtocol: 802.1ad bondMaster: mode: 802.3ad xmitHashPolicy: encap3+4 lacpRate: fast arpValidate: all arpAllTargets: any primary: 3 primaryReselect: better failOverMac: follow adSelect: count miimon: 33 updelay: 100 downdelay: 200 arpInterval: 10 resendIgmp: 30 minLinks: 1 lpInterval: 3 packetsPerSlave: 4 numPeerNotif: 4 tlbLogicalLb: 5 allSlavesActive: 1 useCarrier: true adActorSysPrio: 6 adUserPortKey: 7 peerNotifyDelay: 40 vrfMaster: table: "123" wireguard: publicKey: bar= listenPort: 51820 firewallMark: 11233 peers: - publicKey: peer= presharedKey: key= endpoint: 127.0.0.1:3333 persistentKeepaliveInterval: 30s allowedIPs: - 192.83.93.94/31 `, string(marshaled)) var spec2 network.LinkStatusSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/link_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "time" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestWireguardPeer(t *testing.T) { key1 := "2t4fMmV1fBhI6RgoUzHp9BoWLT7oq0C/fOV17f7FqTI=" key2 := "zHyf80qsjQ1EfiXkjxaLf9K9VZ6YRwcXx8GrpXQ6/yQ=" peer1 := network.WireguardPeer{ PublicKey: key1, Endpoint: "127.0.0.1:1000", PersistentKeepaliveInterval: 10 * time.Second, AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.2.0.0/16"), netip.MustParsePrefix("10.2.0.0/24"), }, } peer2 := network.WireguardPeer{ PublicKey: key2, Endpoint: "127.0.0.1:2000", AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.2.0.0/15"), netip.MustParsePrefix("10.3.0.0/28"), }, } peer1_1 := network.WireguardPeer{ PublicKey: key1, Endpoint: "127.0.0.1:1000", PersistentKeepaliveInterval: 10 * time.Second, AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.2.0.0/15"), netip.MustParsePrefix("10.3.0.0/28"), }, } peer1_2 := network.WireguardPeer{ PublicKey: key1, PersistentKeepaliveInterval: 10 * time.Second, AllowedIPs: []netip.Prefix{ netip.MustParsePrefix("10.2.0.0/16"), netip.MustParsePrefix("10.2.0.0/24"), }, } assert.True(t, peer1.Equal(&peer1)) assert.False(t, peer1.Equal(&peer2)) assert.False(t, peer1.Equal(&peer1_1)) assert.True(t, peer1.Equal(&peer1_2)) } func TestWireguardSpecZero(t *testing.T) { zeroSpec := network.WireguardSpec{} assert.True(t, zeroSpec.IsZero()) } func TestWireguardSpecMerge(t *testing.T) { priv := "KIT4Pe7jFbCnH+ZMwsqsIbX2xiTdmemQU9w9sYItqXY=" pub1 := "VHlgUWcakWcZyrtKI476PJSdoINTc1G5PYO1SEkr4FQ=" pub2 := "EiBteTHU1Dk3w9CYJtHFaSgkuZBVBZLEa+Y07xu+xno=" for _, tt := range []struct { name string spec network.WireguardSpec other network.WireguardSpec expected network.WireguardSpec }{ { name: "zero", }, { name: "speczero", other: network.WireguardSpec{ ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, expected: network.WireguardSpec{ ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, }, { name: "otherzero", spec: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, Peers: []network.WireguardPeer{ { PublicKey: pub1, }, }, }, expected: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, Peers: []network.WireguardPeer{ { PublicKey: pub1, }, }, }, }, { name: "mixed", spec: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, Peers: []network.WireguardPeer{ { PublicKey: pub1, }, }, }, other: network.WireguardSpec{ ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, expected: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub1, }, { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, }, { name: "peerconflict", spec: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, Peers: []network.WireguardPeer{ { PublicKey: pub1, PersistentKeepaliveInterval: time.Second, }, }, }, other: network.WireguardSpec{ ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub1, Endpoint: "127.0.0.1:466", }, { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, expected: network.WireguardSpec{ PrivateKey: priv, FirewallMark: 34, ListenPort: 456, Peers: []network.WireguardPeer{ { PublicKey: pub1, PersistentKeepaliveInterval: time.Second, }, { PublicKey: pub2, Endpoint: "127.0.0.1:3445", }, }, }, }, } { t.Run(tt.name, func(t *testing.T) { spec := tt.spec spec.Merge(tt.other) assert.Equal(t, tt.expected, spec) }) } } ================================================ FILE: pkg/machinery/resources/network/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package network provides resources which describe networking subsystem state. package network import ( "fmt" "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/talos/pkg/machinery/nethelpers" ) //go:generate go tool github.com/dmarkham/enumer -type=ConfigLayer,Operator -linecomment -text // NamespaceName contains resources related to networking. const NamespaceName resource.Namespace = "network" // ConfigNamespaceName contains umerged resources related to networking generate from the configuration. // // Resources in the ConfigNamespaceName namespace are merged to produce final versions in the NamespaceName namespace. const ConfigNamespaceName resource.Namespace = "network-config" // DefaultRouteMetric is the default route metric if no metric was specified explicitly. const DefaultRouteMetric = 1024 // AddressID builds ID (primary key) for the address. func AddressID(linkName string, addr netip.Prefix) string { return fmt.Sprintf("%s/%s", linkName, addr) } // LinkID builds ID (primary key) for the link (interface). func LinkID(linkName string) string { return linkName } // RouteID builds ID (primary key) for the route. func RouteID(table nethelpers.RoutingTable, family nethelpers.Family, destination netip.Prefix, gateway netip.Addr, priority uint32, outLinkName string) string { dst, _ := destination.MarshalText() //nolint:errcheck gw, _ := gateway.MarshalText() //nolint:errcheck prefix := "" if table != nethelpers.TableMain { prefix = fmt.Sprintf("%s/", table) } if family == nethelpers.FamilyInet6 { prefix += fmt.Sprintf("%s/", outLinkName) } return fmt.Sprintf("%s%s/%s/%s/%d", prefix, family, string(gw), string(dst), priority) } // RoutingRuleID builds ID (primary key) for the routing rule. func RoutingRuleID(family nethelpers.Family, priority uint32) string { return fmt.Sprintf("%s/%05d", family, priority) } // OperatorID builds ID (primary key) for the operators. func OperatorID(spec OperatorSpecSpec) string { switch spec.Operator { case OperatorVIP: return fmt.Sprintf("%s/%s/%s", spec.Operator, spec.LinkName, spec.VIP.IP.String()) case OperatorDHCP4: fallthrough case OperatorDHCP6: fallthrough default: return fmt.Sprintf("%s/%s", spec.Operator, spec.LinkName) } } // LayeredID builds configuration for the entity at some layer. func LayeredID(layer ConfigLayer, id string) string { return fmt.Sprintf("%s/%s", layer, id) } // Link kinds. const ( LinkKindVLAN = "vlan" LinkKindBond = "bond" LinkKindBridge = "bridge" LinkKindVRF = "vrf" LinkKindWireguard = "wireguard" ) ================================================ FILE: pkg/machinery/resources/network/network_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/siderolabs/protoenc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" networkpb "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/network" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &network.AddressStatus{}, &network.AddressSpec{}, &network.HardwareAddr{}, &network.DNSUpstream{}, &network.EthernetSpec{}, &network.EthernetStatus{}, &network.HostDNSConfig{}, &network.HostnameStatus{}, &network.HostnameSpec{}, &network.LinkAliasSpec{}, &network.LinkRefresh{}, &network.LinkStatus{}, &network.LinkSpec{}, &network.NfTablesChain{}, &network.NodeAddress{}, &network.NodeAddressFilter{}, &network.NodeAddressSortAlgorithm{}, &network.OperatorSpec{}, &network.PlatformConfig{}, &network.ProbeSpec{}, &network.ResolverStatus{}, &network.ResolverSpec{}, &network.RouteStatus{}, &network.RouteSpec{}, &network.Status{}, &network.TimeServerStatus{}, &network.TimeServerSpec{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } func TestProtobufInterop(t *testing.T) { t.Parallel() // TODO: this should be auto-generated, but for now we just want to fix the bug and add regression for _, test := range []struct { res interface { resource.Resource ResourceDefinition() meta.ResourceDefinitionSpec } spec proto.Message }{ { res: &network.AddressStatus{}, spec: &networkpb.AddressStatusSpec{}, }, { res: &network.EthernetStatus{}, spec: &networkpb.EthernetStatusSpec{}, }, { res: &network.LinkSpec{}, spec: &networkpb.LinkSpecSpec{}, }, { res: &network.LinkStatus{}, spec: &networkpb.LinkStatusSpec{}, }, { res: &network.NfTablesChain{}, spec: &networkpb.NfTablesChainSpec{}, }, } { t.Run(test.res.ResourceDefinition().Type, func(t *testing.T) { t.Parallel() require.NoError(t, proto.ResourceSpecToProto(test.res, test.spec, protoenc.WithMarshalZeroFields())) }) } } ================================================ FILE: pkg/machinery/resources/network/nftables_chain.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NfTablesChainType is type of NfTablesChain resource. const NfTablesChainType = resource.Type("NfTablesChains.net.talos.dev") // NfTablesChain resource holds definition of the nftables chain. type NfTablesChain = typed.Resource[NfTablesChainSpec, NfTablesChainExtension] // NfTablesChainSpec describes status of rendered secrets. // //gotagsrewrite:gen type NfTablesChainSpec struct { Type nethelpers.NfTablesChainType `yaml:"type" protobuf:"1"` Hook nethelpers.NfTablesChainHook `yaml:"hook" protobuf:"2"` Priority nethelpers.NfTablesChainPriority `yaml:"priority" protobuf:"3"` Policy nethelpers.NfTablesVerdict `yaml:"policy" protobuf:"5"` Rules []NfTablesRule `yaml:"rules" protobuf:"4"` } // NfTablesRule describes a single rule in the nftables chain. // //gotagsrewrite:gen type NfTablesRule struct { MatchIIfName *NfTablesIfNameMatch `yaml:"matchIIfName,omitempty" protobuf:"8"` MatchOIfName *NfTablesIfNameMatch `yaml:"matchOIfName,omitempty" protobuf:"1"` MatchMark *NfTablesMark `yaml:"matchMark,omitempty" protobuf:"3"` MatchConntrackState *NfTablesConntrackStateMatch `yaml:"matchConntrackState,omitempty" protobuf:"11"` MatchSourceAddress *NfTablesAddressMatch `yaml:"matchSourceAddress,omitempty" protobuf:"5"` MatchDestinationAddress *NfTablesAddressMatch `yaml:"matchDestinationAddress,omitempty" protobuf:"6"` MatchLayer4 *NfTablesLayer4Match `yaml:"matchLayer4,omitempty" protobuf:"7"` MatchLimit *NfTablesLimitMatch `yaml:"matchLimit,omitempty" protobuf:"10"` ClampMSS *NfTablesClampMSS `yaml:"clampMSS,omitempty" protobuf:"9"` SetMark *NfTablesMark `yaml:"setMark,omitempty" protobuf:"4"` AnonCounter bool `yaml:"anonymousCounter,omitempty" protobuf:"12"` Verdict *nethelpers.NfTablesVerdict `yaml:"verdict,omitempty" protobuf:"2"` } // NfTablesIfNameMatch describes the match on the interface name. // //gotagsrewrite:gen type NfTablesIfNameMatch struct { InterfaceNames []string `yaml:"interfaceName" protobuf:"3"` Operator nethelpers.MatchOperator `yaml:"operator" protobuf:"2"` } // NfTablesMark encodes packet mark match/update operation. // // When used as a match computes the following condition: // (mark & mask) ^ xor == value // // When used as an update computes the following operation: // mark = (mark & mask) ^ xor. // //gotagsrewrite:gen type NfTablesMark struct { Mask uint32 `yaml:"mask,omitempty" protobuf:"1"` Xor uint32 `yaml:"xor,omitempty" protobuf:"2"` Value uint32 `yaml:"value,omitempty" protobuf:"3"` } // NfTablesAddressMatch describes the match on the IP address. // //gotagsrewrite:gen type NfTablesAddressMatch struct { IncludeSubnets []netip.Prefix `yaml:"includeSubnets,omitempty" protobuf:"1"` ExcludeSubnets []netip.Prefix `yaml:"excludeSubnets,omitempty" protobuf:"2"` Invert bool `yaml:"invert,omitempty" protobuf:"3"` } // NfTablesLayer4Match describes the match on the transport layer protocol. // //gotagsrewrite:gen type NfTablesLayer4Match struct { Protocol nethelpers.Protocol `yaml:"protocol" protobuf:"1"` MatchSourcePort *NfTablesPortMatch `yaml:"matchSourcePort,omitempty" protobuf:"2"` MatchDestinationPort *NfTablesPortMatch `yaml:"matchDestinationPort,omitempty" protobuf:"3"` MatchICMPType *NfTablesICMPTypeMatch `yaml:"matchICMPType,omitempty" protobuf:"4"` } // NfTablesPortMatch describes the match on the transport layer port. // //gotagsrewrite:gen type NfTablesPortMatch struct { Ranges []PortRange `yaml:"ranges,omitempty" protobuf:"1"` } // NfTablesICMPTypeMatch describes the match on the ICMP type. // //gotagsrewrite:gen type NfTablesICMPTypeMatch struct { Types []nethelpers.ICMPType `yaml:"types" protobuf:"1"` } // PortRange describes a range of ports. // // Range is [lo, hi]. // //gotagsrewrite:gen type PortRange struct { Lo uint16 `yaml:"lo" protobuf:"1"` Hi uint16 `yaml:"hi" protobuf:"2"` } // NfTablesClampMSS describes the TCP MSS clamping operation. // // MSS is limited by the `MaxMTU` so that: // - IPv4: MSS = MaxMTU - 40 // - IPv6: MSS = MaxMTU - 60. // //gotagsrewrite:gen type NfTablesClampMSS struct { MTU uint16 `yaml:"mtu" protobuf:"1"` } // NfTablesLimitMatch describes the match on the packet rate. // //gotagsrewrite:gen type NfTablesLimitMatch struct { PacketRatePerSecond uint64 `yaml:"packetRatePerSecond" protobuf:"1"` } // NfTablesConntrackStateMatch describes the match on the connection tracking state. // //gotagsrewrite:gen type NfTablesConntrackStateMatch struct { States []nethelpers.ConntrackState `yaml:"states" protobuf:"1"` } // NewNfTablesChain initializes a NfTablesChain resource. func NewNfTablesChain(namespace resource.Namespace, id resource.ID) *NfTablesChain { return typed.NewResource[NfTablesChainSpec, NfTablesChainExtension]( resource.NewMetadata(namespace, NfTablesChainType, id, resource.VersionUndefined), NfTablesChainSpec{}, ) } // NfTablesChainExtension provides auxiliary methods for NfTablesChain. type NfTablesChainExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NfTablesChainExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NfTablesChainType, Aliases: []resource.Type{"chain", "chains"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Type", JSONPath: `{.type}`, }, { Name: "Hook", JSONPath: `{.hook}`, }, { Name: "Priority", JSONPath: `{.priority}`, }, { Name: "Policy", JSONPath: `{.policy}`, }, }, Sensitivity: meta.NonSensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NfTablesChainSpec](NfTablesChainType, &NfTablesChain{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/node_address.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "fmt" "net/netip" "slices" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeAddressType is type of NodeAddress resource. const NodeAddressType = resource.Type("NodeAddresses.net.talos.dev") // NodeAddress resource holds physical network link status. type NodeAddress = typed.Resource[NodeAddressSpec, NodeAddressExtension] // NodeAddress well-known IDs. const ( // Default node address (should be a single address in the spec). // // Used to set for example published etcd peer address. NodeAddressDefaultID = "default" // Current node addresses (as seen at the moment). // // Shows a list of addresses for the node for the UP interfaces. NodeAddressCurrentID = "current" // Accumulative list of the addresses node had over time. // // If some address is no longer present, it will be still kept in the accumulative list. NodeAddressAccumulativeID = "accumulative" // Routed current node addresses (as seen at the moment). // // This is current addresses minus 'external' IPs, and SideroLink IPs. // // This list is used to pick advertised/listen addresses for different services. NodeAddressRoutedID = "routed" ) // NodeAddressSpec describes a set of node addresses. // //gotagsrewrite:gen type NodeAddressSpec struct { Addresses []netip.Prefix `yaml:"addresses" protobuf:"1"` SortAlgorithm nethelpers.AddressSortAlgorithm `yaml:"sortAlgorithm" protobuf:"2"` } // NewNodeAddress initializes a NodeAddress resource. func NewNodeAddress(namespace resource.Namespace, id resource.ID) *NodeAddress { return typed.NewResource[NodeAddressSpec, NodeAddressExtension]( resource.NewMetadata(namespace, NodeAddressType, id, resource.VersionUndefined), NodeAddressSpec{}, ) } // NodeAddressExtension provides auxiliary methods for NodeAddress. type NodeAddressExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeAddressExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeAddressType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Addresses", JSONPath: `{.addresses}`, }, { Name: "SortAlgorithm", JSONPath: `{.sortAlgorithm}`, }, }, } } // IPs returns IP without prefix. func (spec *NodeAddressSpec) IPs() []netip.Addr { // make sure addresses are unique, as different prefixes can have the same IP // at the same we want to preserve order ips := xslices.Map(spec.Addresses, netip.Prefix.Addr) result := make([]netip.Addr, 0, len(ips)) for _, ip := range ips { if slices.ContainsFunc(result, func(addr netip.Addr) bool { return addr == ip }) { continue } result = append(result, ip) } return result } // FilteredNodeAddressID returns resource ID for node addresses with filter applied. func FilteredNodeAddressID(kind resource.ID, filterID string) resource.ID { return fmt.Sprintf("%s-%s", kind, filterID) } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeAddressSpec](NodeAddressType, &NodeAddress{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/node_address_filter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeAddressFilterType is type of NodeAddressFilter resource. const NodeAddressFilterType = resource.Type("NodeAddressFilters.net.talos.dev") // NodeAddressFilter resource holds filter for NodeAddress resources. type NodeAddressFilter = typed.Resource[NodeAddressFilterSpec, NodeAddressFilterExtension] // NodeAddressFilterSpec describes a filter for NodeAddresses. // //gotagsrewrite:gen type NodeAddressFilterSpec struct { // Address is skipped if it doesn't match any of the includeSubnets (if includeSubnets is not empty). IncludeSubnets []netip.Prefix `yaml:"includeSubnets" protobuf:"1"` // Address is skipped if it matches any of the includeSubnets. ExcludeSubnets []netip.Prefix `yaml:"excludeSubnets" protobuf:"2"` } // NewNodeAddressFilter initializes a NodeAddressFilter resource. func NewNodeAddressFilter(namespace resource.Namespace, id resource.ID) *NodeAddressFilter { return typed.NewResource[NodeAddressFilterSpec, NodeAddressFilterExtension]( resource.NewMetadata(namespace, NodeAddressFilterType, id, resource.VersionUndefined), NodeAddressFilterSpec{}, ) } // NodeAddressFilterExtension provides auxiliary methods for NodeAddressFilter. type NodeAddressFilterExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeAddressFilterExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeAddressFilterType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Include Subnets", JSONPath: `{.includeSubnets}`, }, { Name: "Exclude Subnets", JSONPath: `{.excludeSubnets}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeAddressFilterSpec](NodeAddressFilterType, &NodeAddressFilter{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/node_address_sort_algorithm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // NodeAddressSortAlgorithmType is type of NodeAddressSortAlgorithm resource. const NodeAddressSortAlgorithmType = resource.Type("NodeAddressSortAlgorithms.net.talos.dev") // NodeAddressSortAlgorithm resource holds filter for NodeAddress resources. type NodeAddressSortAlgorithm = typed.Resource[NodeAddressSortAlgorithmSpec, NodeAddressSortAlgorithmExtension] // NodeAddressSortAlgorithmID is the resource ID for NodeAddressSortAlgorithm. const NodeAddressSortAlgorithmID = "default" // NodeAddressSortAlgorithmSpec describes a filter for NodeAddresses. // //gotagsrewrite:gen type NodeAddressSortAlgorithmSpec struct { Algorithm nethelpers.AddressSortAlgorithm `yaml:"algorithm" protobuf:"1"` } // NewNodeAddressSortAlgorithm initializes a NodeAddressSortAlgorithm resource. func NewNodeAddressSortAlgorithm(namespace resource.Namespace, id resource.ID) *NodeAddressSortAlgorithm { return typed.NewResource[NodeAddressSortAlgorithmSpec, NodeAddressSortAlgorithmExtension]( resource.NewMetadata(namespace, NodeAddressSortAlgorithmType, id, resource.VersionUndefined), NodeAddressSortAlgorithmSpec{}, ) } // NodeAddressSortAlgorithmExtension provides auxiliary methods for NodeAddressSortAlgorithm. type NodeAddressSortAlgorithmExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (NodeAddressSortAlgorithmExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: NodeAddressSortAlgorithmType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Algorithm", JSONPath: `{.algorithm}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[NodeAddressSortAlgorithmSpec](NodeAddressSortAlgorithmType, &NodeAddressSortAlgorithm{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/operator.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network // Operator enumerates Talos network operators. type Operator int // Operator list. // //structprotogen:gen_enum const ( OperatorDHCP4 Operator = iota // dhcp4 OperatorDHCP6 // dhcp6 OperatorVIP // vip ) ================================================ FILE: pkg/machinery/resources/network/operator_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // OperatorSpecType is type of OperatorSpec resource. const OperatorSpecType = resource.Type("OperatorSpecs.net.talos.dev") // OperatorSpec resource holds operator specification. type OperatorSpec = typed.Resource[OperatorSpecSpec, OperatorSpecExtension] // OperatorSpecSpec describes operator specification. // //gotagsrewrite:gen type OperatorSpecSpec struct { Operator Operator `yaml:"operator" protobuf:"1"` LinkName string `yaml:"linkName" protobuf:"2"` RequireUp bool `yaml:"requireUp" protobuf:"3"` DHCP4 DHCP4OperatorSpec `yaml:"dhcp4,omitempty" protobuf:"4"` DHCP6 DHCP6OperatorSpec `yaml:"dhcp6,omitempty" protobuf:"5"` VIP VIPOperatorSpec `yaml:"vip,omitempty" protobuf:"6"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"7"` } // Equal implements equality check for OperatorSpecSpec. func (spec OperatorSpecSpec) Equal(other OperatorSpecSpec) bool { // config layer is not important for equality check spec.ConfigLayer = other.ConfigLayer return spec == other } // ClientIdentifierSpec is a shared DHCP4/DHCP6 client identifier spec. // //gotagsrewrite:gen type ClientIdentifierSpec struct { ClientIdentifier nethelpers.ClientIdentifier `yaml:"clientIdentifier" protobuf:"1"` DUIDRawHex string `yaml:"duidRawHex,omitempty" protobuf:"2"` } // DHCP4OperatorSpec describes DHCP4 operator options. // //gotagsrewrite:gen type DHCP4OperatorSpec struct { RouteMetric uint32 `yaml:"routeMetric" protobuf:"1"` SkipHostnameRequest bool `yaml:"skipHostnameRequest,omitempty" protobuf:"2"` ClientIdentifier ClientIdentifierSpec `yaml:"clientIdentifier,omitempty" protobuf:"3"` } // DHCP6OperatorSpec describes DHCP6 operator options. // //gotagsrewrite:gen type DHCP6OperatorSpec struct { RouteMetric uint32 `yaml:"routeMetric" protobuf:"2"` SkipHostnameRequest bool `yaml:"skipHostnameRequest,omitempty" protobuf:"3"` ClientIdentifier ClientIdentifierSpec `yaml:"clientIdentifier,omitempty" protobuf:"4"` } // VIPOperatorSpec describes virtual IP operator options. // //gotagsrewrite:gen type VIPOperatorSpec struct { IP netip.Addr `yaml:"ip" protobuf:"1"` GratuitousARP bool `yaml:"gratuitousARP" protobuf:"2"` EquinixMetal VIPEquinixMetalSpec `yaml:"equinixMetal,omitempty" protobuf:"3"` HCloud VIPHCloudSpec `yaml:"hcloud,omitempty" protobuf:"4"` } // VIPEquinixMetalSpec describes virtual (elastic) IP settings for Equinix Metal. // //gotagsrewrite:gen type VIPEquinixMetalSpec struct { ProjectID string `yaml:"projectID" protobuf:"1"` DeviceID string `yaml:"deviceID" protobuf:"2"` APIToken string `yaml:"apiToken" protobuf:"3"` } // VIPHCloudSpec describes virtual (elastic) IP settings for Hetzner Cloud. // //gotagsrewrite:gen type VIPHCloudSpec struct { DeviceID int64 `yaml:"deviceID" protobuf:"1"` NetworkID int64 `yaml:"networkID" protobuf:"2"` APIToken string `yaml:"apiToken" protobuf:"3"` } // NewOperatorSpec initializes a OperatorSpec resource. func NewOperatorSpec(namespace resource.Namespace, id resource.ID) *OperatorSpec { return typed.NewResource[OperatorSpecSpec, OperatorSpecExtension]( resource.NewMetadata(namespace, OperatorSpecType, id, resource.VersionUndefined), OperatorSpecSpec{}, ) } // OperatorSpecExtension provides auxiliary methods for OperatorSpec. type OperatorSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (OperatorSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: OperatorSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[OperatorSpecSpec](OperatorSpecType, &OperatorSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/operator_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestOperatorSpecMarshalYAML(t *testing.T) { spec := network.OperatorSpecSpec{ Operator: network.OperatorDHCP4, LinkName: "eth0", RequireUp: true, DHCP4: network.DHCP4OperatorSpec{ RouteMetric: 1024, }, DHCP6: network.DHCP6OperatorSpec{ RouteMetric: 1024, }, VIP: network.VIPOperatorSpec{ IP: netip.MustParseAddr("192.168.1.1"), GratuitousARP: true, EquinixMetal: network.VIPEquinixMetalSpec{ ProjectID: "a", DeviceID: "b", APIToken: "c", }, HCloud: network.VIPHCloudSpec{ DeviceID: 3, NetworkID: 4, APIToken: "d", }, }, ConfigLayer: network.ConfigMachineConfiguration, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, `operator: dhcp4 linkName: eth0 requireUp: true dhcp4: routeMetric: 1024 dhcp6: routeMetric: 1024 vip: ip: 192.168.1.1 gratuitousARP: true equinixMetal: projectID: a deviceID: b apiToken: c hcloud: deviceID: 3 networkID: 4 apiToken: d layer: configuration `, string(marshaled)) var spec2 network.OperatorSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/platform_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "bytes" "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) // PlatformConfigType is type of PlatformConfig resource. const PlatformConfigType = resource.Type("PlatformConfigs.net.talos.dev") // PlatformConfig resource holds DNS resolver info. type PlatformConfig = typed.Resource[PlatformConfigSpec, PlatformConfigExtension] // PlatformConfigActiveID is the ID of instance containing active config. const PlatformConfigActiveID resource.ID = "active" // PlatformConfigCachedID is the ID of instance containing cached (persisted) config. const PlatformConfigCachedID resource.ID = "cached" // PlatformConfigSpec describes platform network configuration. // // This structure is marshaled to STATE partition to persist cached network configuration across // reboots. // //gotagsrewrite:gen type PlatformConfigSpec struct { Addresses []AddressSpecSpec `yaml:"addresses" protobuf:"1"` Links []LinkSpecSpec `yaml:"links" protobuf:"2"` Routes []RouteSpecSpec `yaml:"routes" protobuf:"3"` Hostnames []HostnameSpecSpec `yaml:"hostnames" protobuf:"4"` Resolvers []ResolverSpecSpec `yaml:"resolvers" protobuf:"5"` TimeServers []TimeServerSpecSpec `yaml:"timeServers" protobuf:"6"` Operators []OperatorSpecSpec `yaml:"operators" protobuf:"7"` ExternalIPs []netip.Addr `yaml:"externalIPs" protobuf:"8"` Probes []ProbeSpecSpec `yaml:"probes,omitempty" protobuf:"9"` Metadata *runtime.PlatformMetadataSpec `yaml:"metadata,omitempty" protobuf:"10"` } // Equal compares two platform network configurations. func (p *PlatformConfigSpec) Equal(other *PlatformConfigSpec) bool { // we will compare by marshaling to YAML // and then comparing the bytes // this is not the most efficient way to do this, // but it will handle omitting empty fields m1, err1 := yaml.Marshal(p) m2, err2 := yaml.Marshal(other) if err1 != nil || err2 != nil { return false } return bytes.Equal(m1, m2) } // NewPlatformConfig initializes a PlatformConfig resource. func NewPlatformConfig(namespace resource.Namespace, id resource.ID) *PlatformConfig { return typed.NewResource[PlatformConfigSpec, PlatformConfigExtension]( resource.NewMetadata(namespace, PlatformConfigType, id, resource.VersionUndefined), PlatformConfigSpec{}, ) } // PlatformConfigExtension provides auxiliary methods for PlatformConfig. type PlatformConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PlatformConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PlatformConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PlatformConfigSpec](PlatformConfigType, &PlatformConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/platform_config_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestPlatformConfigEqual(t *testing.T) { t.Parallel() assert.True(t, (&network.PlatformConfigSpec{}).Equal(&network.PlatformConfigSpec{})) assert.True(t, (&network.PlatformConfigSpec{Addresses: []network.AddressSpecSpec{}}).Equal(&network.PlatformConfigSpec{})) assert.True(t, (&network.PlatformConfigSpec{Addresses: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("192.168.68.54/22"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, }, }}).Equal(&network.PlatformConfigSpec{Addresses: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("192.168.68.54/22"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, }, }})) assert.False(t, (&network.PlatformConfigSpec{}).Equal(nil)) assert.False(t, (&network.PlatformConfigSpec{Addresses: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("192.168.68.1/22"), LinkName: "eth0", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, }, }}).Equal(&network.PlatformConfigSpec{Addresses: []network.AddressSpecSpec{ { Address: netip.MustParsePrefix("192.168.68.2/22"), LinkName: "eth1", Family: nethelpers.FamilyInet4, Scope: nethelpers.ScopeGlobal, }, }})) } ================================================ FILE: pkg/machinery/resources/network/probe_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "errors" "fmt" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ProbeSpecType is type of ProbeSpec resource. const ProbeSpecType = resource.Type("ProbeSpecs.net.talos.dev") // ProbeSpec resource holds Probe specification to be run. type ProbeSpec = typed.Resource[ProbeSpecSpec, ProbeSpecExtension] // ProbeSpecSpec describes the Probe. // //gotagsrewrite:gen type ProbeSpecSpec struct { // Interval between the probes. Interval time.Duration `yaml:"interval" protobuf:"1"` // FailureThreshold is the number of consecutive failures for the probe to be considered failed after having succeeded. FailureThreshold int `yaml:"failureThreshold" protobuf:"2"` // One of the probe types should be specified, for now it's only TCP. TCP TCPProbeSpec `yaml:"tcp,omitempty" protobuf:"3"` // Configuration layer. ConfigLayer ConfigLayer `yaml:"layer" protobuf:"4"` } // ID returns the ID of the resource based on the spec. func (spec *ProbeSpecSpec) ID() (resource.ID, error) { var zeroTCP TCPProbeSpec if spec.TCP == zeroTCP { return "", errors.New("no probe type specified") } return fmt.Sprintf("tcp:%s", spec.TCP.Endpoint), nil } // Equal returns true if the specs are equal. func (spec ProbeSpecSpec) Equal(other ProbeSpecSpec) bool { return spec == other } // TCPProbeSpec describes the TCP Probe. // //gotagsrewrite:gen type TCPProbeSpec struct { // Endpoint to probe: host:port. Endpoint string `yaml:"endpoint" protobuf:"1"` // Timeout for the probe. Timeout time.Duration `yaml:"timeout" protobuf:"2"` } // NewProbeSpec initializes a ProbeSpec resource. func NewProbeSpec(namespace resource.Namespace, id resource.ID) *ProbeSpec { return typed.NewResource[ProbeSpecSpec, ProbeSpecExtension]( resource.NewMetadata(namespace, ProbeSpecType, id, resource.VersionUndefined), ProbeSpecSpec{}, ) } // ProbeSpecExtension provides auxiliary methods for ProbeSpec. type ProbeSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ProbeSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ProbeSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ProbeSpecSpec](ProbeSpecType, &ProbeSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/probe_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ProbeStatusType is type of ProbeStatus resource. const ProbeStatusType = resource.Type("ProbeStatuses.net.talos.dev") // ProbeStatus resource holds Probe result. type ProbeStatus = typed.Resource[ProbeStatusSpec, ProbeStatusExtension] // ProbeStatusSpec describes the Probe. // //gotagsrewrite:gen type ProbeStatusSpec struct { // Success of the check. Success bool `yaml:"success" protobuf:"1"` // Last error of the probe. LastError string `yaml:"lastError" protobuf:"2"` } // NewProbeStatus initializes a ProbeStatus resource. func NewProbeStatus(namespace resource.Namespace, id resource.ID) *ProbeStatus { return typed.NewResource[ProbeStatusSpec, ProbeStatusExtension]( resource.NewMetadata(namespace, ProbeStatusType, id, resource.VersionUndefined), ProbeStatusSpec{}, ) } // ProbeStatusExtension provides auxiliary methods for ProbeStatus. type ProbeStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ProbeStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ProbeStatusType, Aliases: []resource.Type{"probe", "probes"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Success", JSONPath: "{.success}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ProbeStatusSpec](ProbeStatusType, &ProbeStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/resolver_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ResolverSpecType is type of ResolverSpec resource. const ResolverSpecType = resource.Type("ResolverSpecs.net.talos.dev") // ResolverSpec resource holds DNS resolver info. type ResolverSpec = typed.Resource[ResolverSpecSpec, ResolverSpecExtension] // ResolverID is the ID of the singleton instance. const ResolverID resource.ID = "resolvers" // ResolverSpecSpec describes DNS resolvers. // //gotagsrewrite:gen type ResolverSpecSpec struct { DNSServers []netip.Addr `yaml:"dnsServers" protobuf:"1"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"2"` SearchDomains []string `yaml:"searchDomains,omitempty" protobuf:"3"` } // NewResolverSpec initializes a ResolverSpec resource. func NewResolverSpec(namespace resource.Namespace, id resource.ID) *ResolverSpec { return typed.NewResource[ResolverSpecSpec, ResolverSpecExtension]( resource.NewMetadata(namespace, ResolverSpecType, id, resource.VersionUndefined), ResolverSpecSpec{}, ) } // ResolverSpecExtension provides auxiliary methods for ResolverSpec. type ResolverSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ResolverSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ResolverSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Layer", JSONPath: "{.layer}", }, { Name: "Resolvers", JSONPath: "{.dnsServers}", }, { Name: "Search Domains", JSONPath: "{.searchDomains}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ResolverSpecSpec](ResolverSpecType, &ResolverSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/resolver_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestResolverSpecMarshalYAML(t *testing.T) { spec := network.ResolverSpecSpec{ DNSServers: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("8.8.8.8")}, ConfigLayer: network.ConfigPlatform, SearchDomains: []string{"example.com"}, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, "dnsServers:\n - 1.1.1.1\n - 8.8.8.8\nlayer: platform\nsearchDomains:\n - example.com\n", string(marshaled)) var spec2 network.ResolverSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/resolver_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ResolverStatusType is type of ResolverStatus resource. const ResolverStatusType = resource.Type("ResolverStatuses.net.talos.dev") // ResolverStatus resource holds DNS resolver info. type ResolverStatus = typed.Resource[ResolverStatusSpec, ResolverStatusExtension] // ResolverStatusSpec describes DNS resolvers. // //gotagsrewrite:gen type ResolverStatusSpec struct { DNSServers []netip.Addr `yaml:"dnsServers" protobuf:"1"` SearchDomains []string `yaml:"searchDomains" protobuf:"2"` } // NewResolverStatus initializes a ResolverStatus resource. func NewResolverStatus(namespace resource.Namespace, id resource.ID) *ResolverStatus { return typed.NewResource[ResolverStatusSpec, ResolverStatusExtension]( resource.NewMetadata(namespace, ResolverStatusType, id, resource.VersionUndefined), ResolverStatusSpec{}, ) } // ResolverStatusExtension provides auxiliary methods for ResolverStatus. type ResolverStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ResolverStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ResolverStatusType, Aliases: []resource.Type{"resolvers"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Resolvers", JSONPath: "{.dnsServers}", }, { Name: "Search Domains", JSONPath: "{.searchDomains}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ResolverStatusSpec](ResolverStatusType, &ResolverStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/route_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/gen/value" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // RouteSpecType is type of RouteSpec resource. const RouteSpecType = resource.Type("RouteSpecs.net.talos.dev") // RouteSpec resource holds route specification to be applied to the kernel. type RouteSpec = typed.Resource[RouteSpecSpec, RouteSpecExtension] // RouteSpecSpec describes the route. // //gotagsrewrite:gen type RouteSpecSpec struct { Family nethelpers.Family `yaml:"family" protobuf:"1"` Destination netip.Prefix `yaml:"dst" protobuf:"2"` Source netip.Addr `yaml:"src" protobuf:"3"` Gateway netip.Addr `yaml:"gateway" protobuf:"4"` OutLinkName string `yaml:"outLinkName,omitempty" protobuf:"5"` Table nethelpers.RoutingTable `yaml:"table" protobuf:"6"` Priority uint32 `yaml:"priority,omitempty" protobuf:"7"` Scope nethelpers.Scope `yaml:"scope" protobuf:"8"` Type nethelpers.RouteType `yaml:"type" protobuf:"9"` Flags nethelpers.RouteFlags `yaml:"flags" protobuf:"10"` Protocol nethelpers.RouteProtocol `yaml:"protocol" protobuf:"11"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"12"` MTU uint32 `yaml:"mtu,omitempty" protobuf:"13"` } var ( zero16 = netip.MustParseAddr("::") zero4 = netip.MustParseAddr("0.0.0.0") ) // Normalize converts 0.0.0.0 to zero value. // //nolint:gocyclo func (route *RouteSpecSpec) Normalize() nethelpers.Family { var family nethelpers.Family if route.Destination.Bits() == 0 { // clear destination to be zero value to support "0.0.0.0/0" routes if route.Destination.Addr().Compare(zero4) == 0 { family = nethelpers.FamilyInet4 route.Destination = netip.Prefix{} } if route.Destination.Addr().Compare(zero16) == 0 { family = nethelpers.FamilyInet6 route.Destination = netip.Prefix{} } } if route.Gateway.Compare(zero4) == 0 { family = nethelpers.FamilyInet4 route.Gateway = netip.Addr{} } if route.Gateway.Compare(zero16) == 0 { family = nethelpers.FamilyInet6 route.Gateway = netip.Addr{} } if route.Source.Compare(zero4) == 0 { family = nethelpers.FamilyInet4 route.Source = netip.Addr{} } if route.Source.Compare(zero16) == 0 { family = nethelpers.FamilyInet6 route.Source = netip.Addr{} } switch { case value.IsZero(route.Gateway) && !value.IsZero(route.Destination): route.Scope = nethelpers.ScopeLink case route.Destination.Addr().IsLoopback(): route.Scope = nethelpers.ScopeHost default: route.Scope = nethelpers.ScopeGlobal } return family } // NewRouteSpec initializes a RouteSpec resource. func NewRouteSpec(namespace resource.Namespace, id resource.ID) *RouteSpec { return typed.NewResource[RouteSpecSpec, RouteSpecExtension]( resource.NewMetadata(namespace, RouteSpecType, id, resource.VersionUndefined), RouteSpecSpec{}, ) } // RouteSpecExtension provides auxiliary methods for RouteSpec. type RouteSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (RouteSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: RouteSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[RouteSpecSpec](RouteSpecType, &RouteSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/route_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "net/netip" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestRoutSpecMarshalYAML(t *testing.T) { spec := network.RouteSpecSpec{ Family: nethelpers.FamilyInet6, Destination: netip.MustParsePrefix("192.168.3.4/25"), Source: netip.MustParseAddr("1.1.1.1"), Gateway: netip.MustParseAddr("2.2.2.2"), OutLinkName: "eth0", Table: nethelpers.TableLocal, Priority: 1024, Scope: nethelpers.ScopeHost, Type: nethelpers.TypeAnycast, Flags: nethelpers.RouteFlags(nethelpers.RouteOffload | nethelpers.RouteCloned), Protocol: nethelpers.ProtocolBoot, ConfigLayer: network.ConfigPlatform, MTU: 1400, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, `family: inet6 dst: 192.168.3.4/25 src: 1.1.1.1 gateway: 2.2.2.2 outLinkName: eth0 table: local priority: 1024 scope: host type: anycast flags: cloned,offload protocol: boot layer: platform mtu: 1400 `, string(marshaled)) var spec2 network.RouteSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } func TestRoutSpecNormalize(t *testing.T) { spec := network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Destination: netip.MustParsePrefix("0.0.0.0/0"), Source: netip.MustParseAddr("0.0.0.0"), Gateway: netip.MustParseAddr("0.0.0.0"), OutLinkName: "eth0", Table: nethelpers.TableLocal, Priority: 1024, ConfigLayer: network.ConfigPlatform, MTU: 1400, } normalizedFamily := spec.Normalize() assert.Equal(t, netip.Prefix{}, spec.Destination) assert.Equal(t, netip.Addr{}, spec.Source) assert.Equal(t, netip.Addr{}, spec.Gateway) assert.Equal(t, nethelpers.FamilyInet4, normalizedFamily) assert.Equal(t, nethelpers.ScopeGlobal, spec.Scope) } func TestRoutSpecNormalizeV6(t *testing.T) { spec := network.RouteSpecSpec{ Family: nethelpers.FamilyInet4, Destination: netip.MustParsePrefix("::/0"), OutLinkName: "eth0", Table: nethelpers.TableLocal, Priority: 1024, ConfigLayer: network.ConfigPlatform, MTU: 1400, } normalizedFamily := spec.Normalize() assert.Equal(t, netip.Prefix{}, spec.Destination) assert.Equal(t, netip.Addr{}, spec.Source) assert.Equal(t, netip.Addr{}, spec.Gateway) assert.Equal(t, nethelpers.FamilyInet6, normalizedFamily) assert.Equal(t, nethelpers.ScopeGlobal, spec.Scope) } ================================================ FILE: pkg/machinery/resources/network/route_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // RouteStatusType is type of RouteStatus resource. const RouteStatusType = resource.Type("RouteStatuses.net.talos.dev") // RouteStatus resource holds physical network link status. type RouteStatus = typed.Resource[RouteStatusSpec, RouteStatusExtension] // RouteStatusSpec describes status of rendered secrets. // //gotagsrewrite:gen type RouteStatusSpec struct { Family nethelpers.Family `yaml:"family" protobuf:"1"` Destination netip.Prefix `yaml:"dst" protobuf:"2"` Source netip.Addr `yaml:"src" protobuf:"3"` Gateway netip.Addr `yaml:"gateway" protobuf:"4"` OutLinkIndex uint32 `yaml:"outLinkIndex,omitempty" protobuf:"5"` OutLinkName string `yaml:"outLinkName,omitempty" protobuf:"6"` Table nethelpers.RoutingTable `yaml:"table" protobuf:"7"` Priority uint32 `yaml:"priority" protobuf:"8"` Scope nethelpers.Scope `yaml:"scope" protobuf:"9"` Type nethelpers.RouteType `yaml:"type" protobuf:"10"` Flags nethelpers.RouteFlags `yaml:"flags" protobuf:"11"` Protocol nethelpers.RouteProtocol `yaml:"protocol" protobuf:"12"` MTU uint32 `yaml:"mtu,omitempty" protobuf:"13"` } // NewRouteStatus initializes a RouteStatus resource. func NewRouteStatus(namespace resource.Namespace, id resource.ID) *RouteStatus { return typed.NewResource[RouteStatusSpec, RouteStatusExtension]( resource.NewMetadata(namespace, RouteStatusType, id, resource.VersionUndefined), RouteStatusSpec{}, ) } // RouteStatusExtension provides auxiliary methods for RouteStatus. type RouteStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (RouteStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: RouteStatusType, Aliases: []resource.Type{"route", "routes"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Destination", JSONPath: `{.dst}`, }, { Name: "Gateway", JSONPath: `{.gateway}`, }, { Name: "Link", JSONPath: `{.outLinkName}`, }, { Name: "Metric", JSONPath: `{.priority}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[RouteStatusSpec](RouteStatusType, &RouteStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/routing_rule_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // RoutingRuleSpecType is type of RoutingRuleSpec resource. const RoutingRuleSpecType = resource.Type("RoutingRuleSpecs.net.talos.dev") // RoutingRuleSpec resource holds routing rule specification to be applied to the kernel. type RoutingRuleSpec = typed.Resource[RoutingRuleSpecSpec, RoutingRuleSpecExtension] // RoutingRuleSpecSpec describes the routing rule. // //gotagsrewrite:gen type RoutingRuleSpecSpec struct { Family nethelpers.Family `yaml:"family" protobuf:"1"` Src netip.Prefix `yaml:"src" protobuf:"2"` Dst netip.Prefix `yaml:"dst" protobuf:"3"` Table nethelpers.RoutingTable `yaml:"table" protobuf:"4"` Priority uint32 `yaml:"priority" protobuf:"5"` Action nethelpers.RoutingRuleAction `yaml:"action" protobuf:"6"` IIFName string `yaml:"iifName,omitempty" protobuf:"7"` OIFName string `yaml:"oifName,omitempty" protobuf:"8"` FwMark uint32 `yaml:"fwMark,omitempty" protobuf:"9"` FwMask uint32 `yaml:"fwMask,omitempty" protobuf:"10"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"11"` } // NewRoutingRuleSpec initializes a RoutingRuleSpec resource. func NewRoutingRuleSpec(namespace resource.Namespace, id resource.ID) *RoutingRuleSpec { return typed.NewResource[RoutingRuleSpecSpec, RoutingRuleSpecExtension]( resource.NewMetadata(namespace, RoutingRuleSpecType, id, resource.VersionUndefined), RoutingRuleSpecSpec{}, ) } // RoutingRuleSpecExtension provides auxiliary methods for RoutingRuleSpec. type RoutingRuleSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (RoutingRuleSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: RoutingRuleSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[RoutingRuleSpecSpec](RoutingRuleSpecType, &RoutingRuleSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/routing_rule_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/machinery/proto" ) // RoutingRuleStatusType is type of RoutingRuleStatus resource. const RoutingRuleStatusType = resource.Type("RoutingRuleStatuses.net.talos.dev") // RoutingRuleStatus resource holds routing rule status observed from the kernel. type RoutingRuleStatus = typed.Resource[RoutingRuleStatusSpec, RoutingRuleStatusExtension] // RoutingRuleStatusSpec describes the observed routing rule state. // //gotagsrewrite:gen type RoutingRuleStatusSpec struct { Family nethelpers.Family `yaml:"family" protobuf:"1"` Src netip.Prefix `yaml:"src" protobuf:"2"` Dst netip.Prefix `yaml:"dst" protobuf:"3"` Table nethelpers.RoutingTable `yaml:"table" protobuf:"4"` Priority uint32 `yaml:"priority" protobuf:"5"` Action nethelpers.RoutingRuleAction `yaml:"action" protobuf:"6"` IIFName string `yaml:"iifName,omitempty" protobuf:"7"` OIFName string `yaml:"oifName,omitempty" protobuf:"8"` FwMark uint32 `yaml:"fwMark,omitempty" protobuf:"9"` FwMask uint32 `yaml:"fwMask,omitempty" protobuf:"10"` } // NewRoutingRuleStatus initializes a RoutingRuleStatus resource. func NewRoutingRuleStatus(namespace resource.Namespace, id resource.ID) *RoutingRuleStatus { return typed.NewResource[RoutingRuleStatusSpec, RoutingRuleStatusExtension]( resource.NewMetadata(namespace, RoutingRuleStatusType, id, resource.VersionUndefined), RoutingRuleStatusSpec{}, ) } // RoutingRuleStatusExtension provides auxiliary methods for RoutingRuleStatus. type RoutingRuleStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (RoutingRuleStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: RoutingRuleStatusType, Aliases: []resource.Type{"routingrule", "routingrules"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Src", JSONPath: `{.src}`, }, { Name: "Dst", JSONPath: `{.dst}`, }, { Name: "Table", JSONPath: `{.table}`, }, { Name: "Priority", JSONPath: `{.priority}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[RoutingRuleStatusSpec](RoutingRuleStatusType, &RoutingRuleStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // StatusType is type of Status resource. const StatusType = resource.Type("NetworkStatuses.net.talos.dev") // Status resource holds status of networking setup. type Status = typed.Resource[StatusSpec, StatusExtension] // StatusSpec describes network state. // //gotagsrewrite:gen type StatusSpec struct { AddressReady bool `yaml:"addressReady" protobuf:"1"` ConnectivityReady bool `yaml:"connectivityReady" protobuf:"2"` HostnameReady bool `yaml:"hostnameReady" protobuf:"3"` EtcFilesReady bool `yaml:"etcFilesReady" protobuf:"4"` } // StatusID is the resource ID of the singleton instance. const StatusID resource.ID = "status" // NewStatus initializes a Status resource. func NewStatus(namespace resource.Namespace, id resource.ID) *Status { return typed.NewResource[StatusSpec, StatusExtension]( resource.NewMetadata(namespace, StatusType, id, resource.VersionUndefined), StatusSpec{}, ) } // StatusExtension provides auxiliary methods for Status. type StatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (StatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StatusType, Aliases: []resource.Type{"netstatus", "netstatuses"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Address", JSONPath: "{.addressReady}", }, { Name: "Connectivity", JSONPath: "{.connectivityReady}", }, { Name: "Hostname", JSONPath: "{.hostnameReady}", }, { Name: "Etc", JSONPath: "{.etcFilesReady}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StatusSpec](StatusType, &Status{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/timeserver_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // TimeServerSpecType is type of TimeServerSpec resource. const TimeServerSpecType = resource.Type("TimeServerSpecs.net.talos.dev") // TimeServerSpec resource holds NTP server info. type TimeServerSpec = typed.Resource[TimeServerSpecSpec, TimeServerSpecExtension] // TimeServerID is the ID of the singleton instance. const TimeServerID resource.ID = "timeservers" // TimeServerSpecSpec describes NTP servers. // //gotagsrewrite:gen type TimeServerSpecSpec struct { NTPServers []string `yaml:"timeServers" protobuf:"1"` ConfigLayer ConfigLayer `yaml:"layer" protobuf:"2"` } // NewTimeServerSpec initializes a TimeServerSpec resource. func NewTimeServerSpec(namespace resource.Namespace, id resource.ID) *TimeServerSpec { return typed.NewResource[TimeServerSpecSpec, TimeServerSpecExtension]( resource.NewMetadata(namespace, TimeServerSpecType, id, resource.VersionUndefined), TimeServerSpecSpec{}, ) } // TimeServerSpecExtension provides auxiliary methods for TimeServerSpec. type TimeServerSpecExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (TimeServerSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: TimeServerSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[TimeServerSpecSpec](TimeServerSpecType, &TimeServerSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/timeserver_spec_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestTimeServerSpecMarshalYAML(t *testing.T) { spec := network.TimeServerSpecSpec{ NTPServers: []string{"pool.ntp.org"}, ConfigLayer: network.ConfigPlatform, } marshaled, err := yaml.Marshal(spec) require.NoError(t, err) assert.Equal(t, "timeServers:\n - pool.ntp.org\nlayer: platform\n", string(marshaled)) var spec2 network.TimeServerSpecSpec require.NoError(t, yaml.Unmarshal(marshaled, &spec2)) assert.Equal(t, spec, spec2) } ================================================ FILE: pkg/machinery/resources/network/timeserver_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // TimeServerStatusType is type of TimeServerStatus resource. const TimeServerStatusType = resource.Type("TimeServerStatuses.net.talos.dev") // TimeServerStatus resource holds NTP server info. type TimeServerStatus = typed.Resource[TimeServerStatusSpec, TimeServerStatusExtension] // TimeServerStatusSpec describes NTP servers. // //gotagsrewrite:gen type TimeServerStatusSpec struct { NTPServers []string `yaml:"timeServers" protobuf:"1"` } // NewTimeServerStatus initializes a TimeServerStatus resource. func NewTimeServerStatus(namespace resource.Namespace, id resource.ID) *TimeServerStatus { return typed.NewResource[TimeServerStatusSpec, TimeServerStatusExtension]( resource.NewMetadata(namespace, TimeServerStatusType, id, resource.VersionUndefined), TimeServerStatusSpec{}, ) } // TimeServerStatusExtension provides auxiliary methods for TimeServerStatus. type TimeServerStatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (TimeServerStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: TimeServerStatusType, Aliases: []resource.Type{"timeserver", "timeservers"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Timeservers", JSONPath: "{.timeServers}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[TimeServerStatusSpec](TimeServerStatusType, &TimeServerStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/network/ula.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network import ( "crypto/sha256" "net/netip" ) // ULAPurpose is the Unique Local Addressing key for the Talos-specific purpose of the prefix. type ULAPurpose byte const ( // ULAUnknown indicates an unknown ULA Purpose. ULAUnknown = 0x00 // ULABootstrap is the Unique Local Addressing space key for the Talos Self-Bootstrapping protocol. ULABootstrap = 0x01 // ULAKubeSpan is the Unique Local Addressing space key for the Talos KubeSpan feature. ULAKubeSpan = 0x02 // ULASideroLink is the Unique Local Addressing space key for the SideroLink feature. ULASideroLink = 0x03 // ULAVirtualSideroLink is the Unique Local Addressing space key for the Virtual SideroLink over GRPC feature. ULAVirtualSideroLink = 0x04 ) // ULAPrefix calculates and returns a Talos-specific Unique Local Address prefix for the given purpose. // This implements a Talos-specific implementation of RFC4193. // The Talos implementation uses a combination of a 48-bit cluster-unique portion with an 8-bit purpose portion. func ULAPrefix(clusterID string, purpose ULAPurpose) netip.Prefix { var prefixData [16]byte hash := sha256.Sum256([]byte(clusterID)) // Take the last 16 bytes of the clusterID's hash. copy(prefixData[:], hash[sha256.Size-16:]) // Apply the ULA prefix as per RFC4193 prefixData[0] = 0xfd // Apply the Talos-specific ULA Purpose suffix prefixData[7] = byte(purpose) return netip.PrefixFrom(netip.AddrFrom16(prefixData), 64).Masked() } // IsULA checks whether IP address is a Unique Local Address with the specific purpose. func IsULA(ip netip.Addr, purpose ULAPurpose) bool { if !ip.Is6() { return false } raw := ip.As16() return raw[0] == 0xfd && raw[7] == byte(purpose) } // NotSideroLinkIP is a shorthand for !IsULA(ip, ULASideroLink). func NotSideroLinkIP(ip netip.Addr) bool { return !IsULA(ip, ULASideroLink) } ================================================ FILE: pkg/machinery/resources/network/ula_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package network_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/network" ) func TestULAPrefix(t *testing.T) { assert.Equal(t, "fd7f:175a:b97c:5602::/64", network.ULAPrefix("8XuV9TZHW08DOk3bVxQjH9ih_TBKjnh-j44tsCLSBzo=", network.ULAKubeSpan).String()) } ================================================ FILE: pkg/machinery/resources/perf/cpu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) //go:generate go tool github.com/siderolabs/deep-copy -type CPUSpec -type MemorySpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // CPUType is type of Etcd resource. const CPUType = resource.Type("CPUStats.perf.talos.dev") // CPUID is a resource ID of singleton instance. const CPUID = resource.ID("latest") // CPU represents the last CPU stats snapshot. type CPU = typed.Resource[CPUSpec, CPUExtension] // CPUSpec represents the last CPU stats snapshot. // //gotagsrewrite:gen type CPUSpec struct { CPU []CPUStat `yaml:"cpu" protobuf:"1"` CPUTotal CPUStat `yaml:"cpuTotal" protobuf:"2"` IRQTotal uint64 `yaml:"irqTotal" protobuf:"3"` ContextSwitches uint64 `yaml:"contextSwitches" protobuf:"4"` ProcessCreated uint64 `yaml:"processCreated" protobuf:"5"` ProcessRunning uint64 `yaml:"processRunning" protobuf:"6"` ProcessBlocked uint64 `yaml:"processBlocked" protobuf:"7"` SoftIrqTotal uint64 `yaml:"softIrqTotal" protobuf:"8"` } // CPUStat represents a single cpu stat. // //gotagsrewrite:gen type CPUStat struct { User float64 `yaml:"user" protobuf:"1"` Nice float64 `yaml:"nice" protobuf:"2"` System float64 `yaml:"system" protobuf:"3"` Idle float64 `yaml:"idle" protobuf:"4"` Iowait float64 `yaml:"iowait" protobuf:"5"` Irq float64 `yaml:"irq" protobuf:"6"` SoftIrq float64 `yaml:"softIrq" protobuf:"7"` Steal float64 `yaml:"steal" protobuf:"8"` Guest float64 `yaml:"guest" protobuf:"9"` GuestNice float64 `yaml:"guestNice" protobuf:"10"` } // NewCPU creates new default CPU stats object. func NewCPU() *CPU { return typed.NewResource[CPUSpec, CPUExtension]( resource.NewMetadata(NamespaceName, CPUType, CPUID, resource.VersionUndefined), CPUSpec{}, ) } // CPUExtension is an auxiliary type for CPU resource. type CPUExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (CPUExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: CPUType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "User", JSONPath: "{.cpuTotal.user}", }, { Name: "System", JSONPath: "{.cpuTotal.system}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[CPUSpec](CPUType, &CPU{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/perf/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type CPUSpec -type MemorySpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package perf // DeepCopy generates a deep copy of CPUSpec. func (o CPUSpec) DeepCopy() CPUSpec { var cp CPUSpec = o if o.CPU != nil { cp.CPU = make([]CPUStat, len(o.CPU)) copy(cp.CPU, o.CPU) } return cp } // DeepCopy generates a deep copy of MemorySpec. func (o MemorySpec) DeepCopy() MemorySpec { var cp MemorySpec = o return cp } ================================================ FILE: pkg/machinery/resources/perf/mem.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MemoryType is type of Etcd resource. const MemoryType = resource.Type("MemoryStats.perf.talos.dev") // MemoryID is a resource ID of singleton instance. const MemoryID = resource.ID("latest") // Memory represents the last Memory stats snapshot. type Memory = typed.Resource[MemorySpec, MemoryExtension] // MemorySpec represents the last Memory stats snapshot. // //gotagsrewrite:gen type MemorySpec struct { MemTotal uint64 `yaml:"total" protobuf:"1"` MemUsed uint64 `yaml:"used" protobuf:"2"` MemAvailable uint64 `yaml:"available" protobuf:"3"` Buffers uint64 `yaml:"buffers" protobuf:"4"` Cached uint64 `yaml:"cached" protobuf:"5"` SwapCached uint64 `yaml:"swapCached" protobuf:"6"` Active uint64 `yaml:"active" protobuf:"7"` Inactive uint64 `yaml:"inactive" protobuf:"8"` ActiveAnon uint64 `yaml:"activeAnon" protobuf:"9"` InactiveAnon uint64 `yaml:"inactiveAnon" protobuf:"10"` ActiveFile uint64 `yaml:"activeFile" protobuf:"11"` InactiveFile uint64 `yaml:"inactiveFile" protobuf:"12"` Unevictable uint64 `yaml:"unevictable" protobuf:"13"` Mlocked uint64 `yaml:"mlocked" protobuf:"14"` SwapTotal uint64 `yaml:"swapTotal" protobuf:"15"` SwapFree uint64 `yaml:"swapFree" protobuf:"16"` Dirty uint64 `yaml:"dirty" protobuf:"17"` Writeback uint64 `yaml:"writeback" protobuf:"18"` AnonPages uint64 `yaml:"anonPages" protobuf:"19"` Mapped uint64 `yaml:"mapped" protobuf:"20"` Shmem uint64 `yaml:"shmem" protobuf:"21"` Slab uint64 `yaml:"slab" protobuf:"22"` SReclaimable uint64 `yaml:"sreclaimable" protobuf:"23"` SUnreclaim uint64 `yaml:"sunreclaim" protobuf:"24"` KernelStack uint64 `yaml:"kernelStack" protobuf:"25"` PageTables uint64 `yaml:"pageTables" protobuf:"26"` NFSunstable uint64 `yaml:"nfsunstable" protobuf:"27"` Bounce uint64 `yaml:"bounce" protobuf:"28"` WritebackTmp uint64 `yaml:"writeBacktmp" protobuf:"29"` CommitLimit uint64 `yaml:"commitLimit" protobuf:"30"` CommittedAS uint64 `yaml:"commitTedas" protobuf:"31"` VmallocTotal uint64 `yaml:"vmallocTotal" protobuf:"32"` VmallocUsed uint64 `yaml:"vmallocUsed" protobuf:"33"` VmallocChunk uint64 `yaml:"vmallocChunk" protobuf:"34"` HardwareCorrupted uint64 `yaml:"hardwareCorrupted" protobuf:"35"` AnonHugePages uint64 `yaml:"anonHugePages" protobuf:"36"` ShmemHugePages uint64 `yaml:"shmemHugePages" protobuf:"37"` ShmemPmdMapped uint64 `yaml:"shmemPmdMapped" protobuf:"38"` CmaTotal uint64 `yaml:"cmaTotal" protobuf:"39"` CmaFree uint64 `yaml:"cmaFree" protobuf:"40"` HugePagesTotal uint64 `yaml:"hugePagesTotal" protobuf:"41"` HugePagesFree uint64 `yaml:"hugePagesFree" protobuf:"42"` HugePagesRsvd uint64 `yaml:"hugePagesRsvd" protobuf:"43"` HugePagesSurp uint64 `yaml:"hugePagesSurp" protobuf:"44"` Hugepagesize uint64 `yaml:"hugepagesize" protobuf:"45"` DirectMap4k uint64 `yaml:"directMap4k" protobuf:"46"` DirectMap2m uint64 `yaml:"directMap2m" protobuf:"47"` DirectMap1g uint64 `yaml:"directMap1g" protobuf:"48"` } // NewMemory creates new default Memory stats object. func NewMemory() *Memory { return typed.NewResource[MemorySpec, MemoryExtension]( resource.NewMetadata(NamespaceName, MemoryType, MemoryID, resource.VersionUndefined), MemorySpec{}, ) } // MemoryExtension is an auxiliary type for Memory resource. type MemoryExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MemoryExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MemoryType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Used", JSONPath: "{.used}", }, { Name: "Total", JSONPath: "{.total}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MemorySpec](MemoryType, &Memory{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/perf/perf.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains resources related to stats. const NamespaceName resource.Namespace = "perf" ================================================ FILE: pkg/machinery/resources/perf/perf_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package perf_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/perf" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &perf.Memory{}, &perf.CPU{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/resources.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package resources provides common Talos resources settings. package resources ================================================ FILE: pkg/machinery/resources/runtime/api_service_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // APIServiceConfigType is type of MaintenanceConfig resource. const APIServiceConfigType = resource.Type("APIServiceConfigs.runtime.talos.dev") // APIServiceConfig resource holds configuration for Talos API service (apid). type APIServiceConfig = typed.Resource[APIServiceConfigSpec, APIServiceConfigExtension] // APIServiceConfigID is a resource ID for APIServiceConfig. const APIServiceConfigID resource.ID = "apid" // APIServiceConfigSpec describes configuration for Talos API service (apid). // //gotagsrewrite:gen type APIServiceConfigSpec struct { ListenAddress string `yaml:"listenAddress" protobuf:"1"` NodeRoutingDisabled bool `yaml:"nodeRoutingDisabled" protobuf:"2"` ReadonlyRoleMode bool `yaml:"readonlyRoleMode" protobuf:"3"` SkipVerifyingClientCert bool `yaml:"skipVerifyingClientCert" protobuf:"4"` } // NewAPIServiceConfig initializes an APIServiceConfig resource. func NewAPIServiceConfig() *APIServiceConfig { return typed.NewResource[APIServiceConfigSpec, APIServiceConfigExtension]( resource.NewMetadata(NamespaceName, APIServiceConfigType, APIServiceConfigID, resource.VersionUndefined), APIServiceConfigSpec{}, ) } // APIServiceConfigExtension is auxiliary resource data for APIServiceConfig. type APIServiceConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (APIServiceConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: APIServiceConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[APIServiceConfigSpec](APIServiceConfigType, &APIServiceConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/booted_entry_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // BootedEntryType is the type of booted entry resource. const BootedEntryType = resource.Type("BootedEntries.talos.dev") // BootedEntryID is the ID of the booted entry resource. const BootedEntryID = resource.ID("bootedentry") // BootedEntry is the booted entry resource. type BootedEntry = typed.Resource[BootedEntrySpec, BootedEntryExtension] // BootedEntrySpec describes the booted entry resource properties. // //gotagsrewrite:gen type BootedEntrySpec struct { BootedEntry string `yaml:"bootedEntry,omitempty" protobuf:"1"` } // BootedEntryExtension provides auxiliary methods for BootedEntry resource. type BootedEntryExtension struct{} // NewBootedEntrySpec initializes a new BootedEntrySpec. func NewBootedEntrySpec() *BootedEntry { return typed.NewResource[BootedEntrySpec, BootedEntryExtension]( resource.NewMetadata(NamespaceName, BootedEntryType, BootedEntryID, resource.VersionUndefined), BootedEntrySpec{}, ) } // ResourceDefinition implements [typed.Extension] interface. func (BootedEntryExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: BootedEntryType, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Booted Entry", JSONPath: `{.bootedEntry}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(BootedEntryType, &BootedEntry{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/condition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "context" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/kernel" ) // KernelParamsSetCondition implements condition which waits for the kernels to be in sync. type KernelParamsSetCondition struct { state state.State props []*kernel.Param } // NewKernelParamsSetCondition builds a coondition which waits for the kernel to be in sync. func NewKernelParamsSetCondition(state state.State, props ...*kernel.Param) *KernelParamsSetCondition { return &KernelParamsSetCondition{ state: state, props: props, } } func (condition *KernelParamsSetCondition) String() string { return "kernelParams" } // Wait implements condition interface. func (condition *KernelParamsSetCondition) Wait(ctx context.Context) error { for _, prop := range condition.props { if _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, KernelParamStatusType, prop.Key, resource.VersionUndefined), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } status := r.(*KernelParamStatus).TypedSpec() if status.Current != prop.Value { return false, nil } return true, nil }), ); err != nil { return err } } return nil } // ExtensionServiceConfigStatusCondition implements condition which waits for extension service config to be available. type ExtensionServiceConfigStatusCondition struct { state state.State serviceName string } // NewExtensionServiceConfigStatusCondition builds a condition which waits for extension service config to be available. func NewExtensionServiceConfigStatusCondition(state state.State, serviceName string) *ExtensionServiceConfigStatusCondition { return &ExtensionServiceConfigStatusCondition{ state: state, serviceName: serviceName, } } func (condition *ExtensionServiceConfigStatusCondition) String() string { return "extension service config" } // Wait implements condition interface. func (condition *ExtensionServiceConfigStatusCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, ExtensionServiceConfigStatusType, condition.serviceName, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), ) return err } // DevicesStatusCondition implements condition which waits for devices to be ready. type DevicesStatusCondition struct { state state.State } // NewDevicesStatusCondition builds a condition which waits for devices to be ready. func NewDevicesStatusCondition(state state.State) *DevicesStatusCondition { return &DevicesStatusCondition{ state: state, } } func (condition *DevicesStatusCondition) String() string { return "devices to be ready" } // Wait implements condition interface. func (condition *DevicesStatusCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, DevicesStatusType, DevicesID, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } return r.(*DevicesStatus).TypedSpec().Ready, nil }), ) return err } // APIServiceConfigCondition implements condition which waits for api service config to be ready. type APIServiceConfigCondition struct { state state.State } // NewAPIServiceConfigCondition builds a condition which waits for api service config to be ready. func NewAPIServiceConfigCondition(state state.State) *APIServiceConfigCondition { return &APIServiceConfigCondition{ state: state, } } func (condition *APIServiceConfigCondition) String() string { return "config to be ready" } // Wait implements condition interface. func (condition *APIServiceConfigCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, APIServiceConfigType, APIServiceConfigID, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } return true, nil }), ) return err } ================================================ FILE: pkg/machinery/resources/runtime/condition_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "context" "errors" "testing" "time" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestCondition(t *testing.T) { ctx, ctxCancel := context.WithTimeout(t.Context(), time.Second) t.Cleanup(ctxCancel) t.Parallel() for _, tt := range []struct { Name string ActualKernelParams []*kernel.Param AwaitKernelParams []*kernel.Param Succeeds bool }{ { Name: "okay", ActualKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, }, AwaitKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, }, Succeeds: true, }, { Name: "timeout", ActualKernelParams: []*kernel.Param{}, AwaitKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, }, Succeeds: false, }, { Name: "value differs", ActualKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "0", }, }, AwaitKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, }, Succeeds: false, }, { Name: "multiple values", ActualKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, { Key: "proc.sys.kernel.dmesg_restrict", Value: "1", }, { Key: "proc.sys.kernel.perf_event_paranoid", Value: "3", }, }, AwaitKernelParams: []*kernel.Param{ { Key: "proc.sys.kernel.kptr_restrict", Value: "1", }, { Key: "proc.sys.kernel.dmesg_restrict", Value: "1", }, { Key: "proc.sys.kernel.perf_event_paranoid", Value: "3", }, }, Succeeds: true, }, } { t.Run(tt.Name, func(t *testing.T) { t.Parallel() state := state.WrapCore(namespaced.NewState(inmem.Build)) for _, prop := range tt.ActualKernelParams { status := runtime.NewKernelParamStatus(runtime.NamespaceName, prop.Key) *status.TypedSpec() = runtime.KernelParamStatusSpec{ Current: prop.Value, } require.NoError(t, state.Create(ctx, status)) } err := runtime.NewKernelParamsSetCondition(state, tt.AwaitKernelParams...).Wait(ctx) if tt.Succeeds { assert.NoError(t, err) } else { assert.True(t, errors.Is(err, context.DeadlineExceeded), "error is %v", err) } }) } } ================================================ FILE: pkg/machinery/resources/runtime/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type APIServiceConfigSpec -type BootedEntrySpec -type DevicesStatusSpec -type DiagnosticSpec -type EnvironmentSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelCmdlineSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type LoadedKernelModuleSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type OOMActionSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type SBOMItemSpec -type UniqueMachineTokenSpec -type VersionSpec -type WatchdogTimerConfigSpec -type WatchdogTimerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package runtime import ( "net/netip" "net/url" ) // DeepCopy generates a deep copy of APIServiceConfigSpec. func (o APIServiceConfigSpec) DeepCopy() APIServiceConfigSpec { var cp APIServiceConfigSpec = o return cp } // DeepCopy generates a deep copy of BootedEntrySpec. func (o BootedEntrySpec) DeepCopy() BootedEntrySpec { var cp BootedEntrySpec = o return cp } // DeepCopy generates a deep copy of DevicesStatusSpec. func (o DevicesStatusSpec) DeepCopy() DevicesStatusSpec { var cp DevicesStatusSpec = o return cp } // DeepCopy generates a deep copy of DiagnosticSpec. func (o DiagnosticSpec) DeepCopy() DiagnosticSpec { var cp DiagnosticSpec = o if o.Details != nil { cp.Details = make([]string, len(o.Details)) copy(cp.Details, o.Details) } return cp } // DeepCopy generates a deep copy of EnvironmentSpec. func (o EnvironmentSpec) DeepCopy() EnvironmentSpec { var cp EnvironmentSpec = o if o.Variables != nil { cp.Variables = make([]string, len(o.Variables)) copy(cp.Variables, o.Variables) } return cp } // DeepCopy generates a deep copy of EventSinkConfigSpec. func (o EventSinkConfigSpec) DeepCopy() EventSinkConfigSpec { var cp EventSinkConfigSpec = o return cp } // DeepCopy generates a deep copy of ExtensionServiceConfigSpec. func (o ExtensionServiceConfigSpec) DeepCopy() ExtensionServiceConfigSpec { var cp ExtensionServiceConfigSpec = o if o.Files != nil { cp.Files = make([]ExtensionServiceConfigFile, len(o.Files)) copy(cp.Files, o.Files) } if o.Environment != nil { cp.Environment = make([]string, len(o.Environment)) copy(cp.Environment, o.Environment) } return cp } // DeepCopy generates a deep copy of ExtensionServiceConfigStatusSpec. func (o ExtensionServiceConfigStatusSpec) DeepCopy() ExtensionServiceConfigStatusSpec { var cp ExtensionServiceConfigStatusSpec = o return cp } // DeepCopy generates a deep copy of KernelCmdlineSpec. func (o KernelCmdlineSpec) DeepCopy() KernelCmdlineSpec { var cp KernelCmdlineSpec = o return cp } // DeepCopy generates a deep copy of KernelModuleSpecSpec. func (o KernelModuleSpecSpec) DeepCopy() KernelModuleSpecSpec { var cp KernelModuleSpecSpec = o if o.Parameters != nil { cp.Parameters = make([]string, len(o.Parameters)) copy(cp.Parameters, o.Parameters) } return cp } // DeepCopy generates a deep copy of KernelParamSpecSpec. func (o KernelParamSpecSpec) DeepCopy() KernelParamSpecSpec { var cp KernelParamSpecSpec = o return cp } // DeepCopy generates a deep copy of KernelParamStatusSpec. func (o KernelParamStatusSpec) DeepCopy() KernelParamStatusSpec { var cp KernelParamStatusSpec = o return cp } // DeepCopy generates a deep copy of KmsgLogConfigSpec. func (o KmsgLogConfigSpec) DeepCopy() KmsgLogConfigSpec { var cp KmsgLogConfigSpec = o if o.Destinations != nil { cp.Destinations = make([]*url.URL, len(o.Destinations)) copy(cp.Destinations, o.Destinations) for i2 := range o.Destinations { if o.Destinations[i2] != nil { cp.Destinations[i2] = new(url.URL) *cp.Destinations[i2] = *o.Destinations[i2] if o.Destinations[i2].User != nil { cp.Destinations[i2].User = new(url.Userinfo) *cp.Destinations[i2].User = *o.Destinations[i2].User } } } } return cp } // DeepCopy generates a deep copy of LoadedKernelModuleSpec. func (o LoadedKernelModuleSpec) DeepCopy() LoadedKernelModuleSpec { var cp LoadedKernelModuleSpec = o if o.Dependencies != nil { cp.Dependencies = make([]string, len(o.Dependencies)) copy(cp.Dependencies, o.Dependencies) } return cp } // DeepCopy generates a deep copy of MaintenanceServiceConfigSpec. func (o MaintenanceServiceConfigSpec) DeepCopy() MaintenanceServiceConfigSpec { var cp MaintenanceServiceConfigSpec = o if o.ReachableAddresses != nil { cp.ReachableAddresses = make([]netip.Addr, len(o.ReachableAddresses)) copy(cp.ReachableAddresses, o.ReachableAddresses) } return cp } // DeepCopy generates a deep copy of MaintenanceServiceRequestSpec. func (o MaintenanceServiceRequestSpec) DeepCopy() MaintenanceServiceRequestSpec { var cp MaintenanceServiceRequestSpec = o return cp } // DeepCopy generates a deep copy of MachineResetSignalSpec. func (o MachineResetSignalSpec) DeepCopy() MachineResetSignalSpec { var cp MachineResetSignalSpec = o return cp } // DeepCopy generates a deep copy of MachineStatusSpec. func (o MachineStatusSpec) DeepCopy() MachineStatusSpec { var cp MachineStatusSpec = o if o.Status.UnmetConditions != nil { cp.Status.UnmetConditions = make([]UnmetCondition, len(o.Status.UnmetConditions)) copy(cp.Status.UnmetConditions, o.Status.UnmetConditions) } return cp } // DeepCopy generates a deep copy of MetaKeySpec. func (o MetaKeySpec) DeepCopy() MetaKeySpec { var cp MetaKeySpec = o return cp } // DeepCopy generates a deep copy of MountStatusSpec. func (o MountStatusSpec) DeepCopy() MountStatusSpec { var cp MountStatusSpec = o if o.Options != nil { cp.Options = make([]string, len(o.Options)) copy(cp.Options, o.Options) } if o.EncryptionProviders != nil { cp.EncryptionProviders = make([]string, len(o.EncryptionProviders)) copy(cp.EncryptionProviders, o.EncryptionProviders) } return cp } // DeepCopy generates a deep copy of OOMActionSpec. func (o OOMActionSpec) DeepCopy() OOMActionSpec { var cp OOMActionSpec = o if o.Processes != nil { cp.Processes = make([]string, len(o.Processes)) copy(cp.Processes, o.Processes) } return cp } // DeepCopy generates a deep copy of PlatformMetadataSpec. func (o PlatformMetadataSpec) DeepCopy() PlatformMetadataSpec { var cp PlatformMetadataSpec = o if o.Tags != nil { cp.Tags = make(map[string]string, len(o.Tags)) for k2, v2 := range o.Tags { cp.Tags[k2] = v2 } } return cp } // DeepCopy generates a deep copy of SecurityStateSpec. func (o SecurityStateSpec) DeepCopy() SecurityStateSpec { var cp SecurityStateSpec = o return cp } // DeepCopy generates a deep copy of MetaLoadedSpec. func (o MetaLoadedSpec) DeepCopy() MetaLoadedSpec { var cp MetaLoadedSpec = o return cp } // DeepCopy generates a deep copy of SBOMItemSpec. func (o SBOMItemSpec) DeepCopy() SBOMItemSpec { var cp SBOMItemSpec = o if o.CPEs != nil { cp.CPEs = make([]string, len(o.CPEs)) copy(cp.CPEs, o.CPEs) } if o.PURLs != nil { cp.PURLs = make([]string, len(o.PURLs)) copy(cp.PURLs, o.PURLs) } return cp } // DeepCopy generates a deep copy of UniqueMachineTokenSpec. func (o UniqueMachineTokenSpec) DeepCopy() UniqueMachineTokenSpec { var cp UniqueMachineTokenSpec = o return cp } // DeepCopy generates a deep copy of VersionSpec. func (o VersionSpec) DeepCopy() VersionSpec { var cp VersionSpec = o return cp } // DeepCopy generates a deep copy of WatchdogTimerConfigSpec. func (o WatchdogTimerConfigSpec) DeepCopy() WatchdogTimerConfigSpec { var cp WatchdogTimerConfigSpec = o return cp } // DeepCopy generates a deep copy of WatchdogTimerStatusSpec. func (o WatchdogTimerStatusSpec) DeepCopy() WatchdogTimerStatusSpec { var cp WatchdogTimerStatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/runtime/devices_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DevicesStatusType is type of DevicesStatus resource. const DevicesStatusType = resource.Type("DevicesStatuses.runtime.talos.dev") // DevicesStatus resource holds status of hardware devices (overall). type DevicesStatus = typed.Resource[DevicesStatusSpec, DevicesStatusExtension] // DevicesID is the ID of DevicesStatus resource. const DevicesID = resource.ID("devices") // DevicesStatusSpec is the spec for devices status. // //gotagsrewrite:gen type DevicesStatusSpec struct { // Devices are settled down and ready to be used. Ready bool `yaml:"ready" protobuf:"1"` } // NewDevicesStatus initializes a DevicesStatus resource. func NewDevicesStatus(namespace resource.Namespace, id resource.ID) *DevicesStatus { return typed.NewResource[DevicesStatusSpec, DevicesStatusExtension]( resource.NewMetadata(namespace, DevicesStatusType, id, resource.VersionUndefined), DevicesStatusSpec{}, ) } // DevicesStatusExtension is auxiliary resource data for DevicesStatus. type DevicesStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DevicesStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DevicesStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Ready", JSONPath: `{.ready}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[DevicesStatusSpec](DevicesStatusType, &DevicesStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/diagnostic.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // DiagnosticType is type of Diagnostic resource. const DiagnosticType = resource.Type("Diagnostics.runtime.talos.dev") // Diagnostic resource contains warnings produced by Talos Diagnostics. type Diagnostic = typed.Resource[DiagnosticSpec, DiagnosticExtension] // DiagnosticSpec is the spec for devices status. // //gotagsrewrite:gen type DiagnosticSpec struct { // Short message describing the problem. Message string `yaml:"message" protobuf:"1"` // Details about the problem. Details []string `yaml:"details" protobuf:"2"` } // DocumentationURL returns the URL to the documentation for the warning. func (spec *DiagnosticSpec) DocumentationURL(id string) string { return "https://talos.dev/diagnostic/" + id } // NewDiagnostic initializes a Diagnostic resource. func NewDiagnostic(namespace resource.Namespace, id resource.ID) *Diagnostic { return typed.NewResource[DiagnosticSpec, DiagnosticExtension]( resource.NewMetadata(namespace, DiagnosticType, id, resource.VersionUndefined), DiagnosticSpec{}, ) } // DiagnosticExtension is auxiliary resource data for Diagnostic. type DiagnosticExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (DiagnosticExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: DiagnosticType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Message", JSONPath: `{.message}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[DiagnosticSpec](DiagnosticType, &Diagnostic{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/environment.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EnvironmentType is type of Environment resource. const EnvironmentType = resource.Type("Environments.runtime.talos.dev") // Environment resource holds information about environment variables. type Environment = typed.Resource[EnvironmentSpec, EnvironmentExtension] // EnvironmentSpec describes the specification of Environment resource. // //gotagsrewrite:gen type EnvironmentSpec struct { Variables []string `yaml:"variables" protobuf:"1"` } // NewEnvironment initializes a Environment resource. func NewEnvironment(id resource.ID) *Environment { return typed.NewResource[EnvironmentSpec, EnvironmentExtension]( resource.NewMetadata(NamespaceName, EnvironmentType, id, resource.VersionUndefined), EnvironmentSpec{}, ) } // EnvironmentExtension is auxiliary resource data for Environment. type EnvironmentExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EnvironmentExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EnvironmentType, Aliases: []resource.Type{ "env", }, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Variables", JSONPath: "{.variables}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EnvironmentSpec](EnvironmentType, &Environment{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/event_sink_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EventSinkConfigType is type of EventSinkConfig resource. const EventSinkConfigType = resource.Type("EventSinkConfigs.runtime.talos.dev") // EventSinkConfig resource holds configuration for Talos event log streaming. type EventSinkConfig = typed.Resource[EventSinkConfigSpec, EventSinkConfigExtension] // EventSinkConfigID is a resource ID for EventSinkConfig. const EventSinkConfigID resource.ID = "event-sink" // EventSinkConfigSpec describes configuration of Talos event log streaming. // //gotagsrewrite:gen type EventSinkConfigSpec struct { Endpoint string `yaml:"endpoint" protobuf:"1"` } // NewEventSinkConfig initializes a EventSinkConfig resource. func NewEventSinkConfig() *EventSinkConfig { return typed.NewResource[EventSinkConfigSpec, EventSinkConfigExtension]( resource.NewMetadata(NamespaceName, EventSinkConfigType, EventSinkConfigID, resource.VersionUndefined), EventSinkConfigSpec{}, ) } // EventSinkConfigExtension is auxiliary resource data for EventSinkConfig. type EventSinkConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EventSinkConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EventSinkConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EventSinkConfigSpec](EventSinkConfigType, &EventSinkConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/extension_services_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ExtensionServiceConfigType is a type of ExtensionServiceConfig. const ExtensionServiceConfigType = resource.Type("ExtensionServiceConfigs.runtime.talos.dev") // ExtensionServiceConfig represents a resource that describes status of rendered extensions service config files. type ExtensionServiceConfig = typed.Resource[ExtensionServiceConfigSpec, ExtensionServiceConfigExtension] // ExtensionServiceConfigSpec describes status of rendered extensions service config files. // //gotagsrewrite:gen type ExtensionServiceConfigSpec struct { Files []ExtensionServiceConfigFile `yaml:"files,omitempty" protobuf:"1"` Environment []string `yaml:"environment,omitempty" protobuf:"2"` } // ExtensionServiceConfigFile describes extensions service config files. // //gotagsrewrite:gen type ExtensionServiceConfigFile struct { Content string `yaml:"content" protobuf:"1"` MountPath string `yaml:"mountPath" protobuf:"2"` } // NewExtensionServiceConfigSpec initializes a new ExtensionServiceConfigSpec. func NewExtensionServiceConfigSpec(namespace resource.Namespace, id resource.ID) *ExtensionServiceConfig { return typed.NewResource[ExtensionServiceConfigSpec, ExtensionServiceConfigExtension]( resource.NewMetadata(namespace, ExtensionServiceConfigType, id, resource.VersionUndefined), ExtensionServiceConfigSpec{}, ) } // ExtensionServiceConfigExtension provides auxiliary methods for ExtensionServiceConfig. type ExtensionServiceConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ExtensionServiceConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ExtensionServiceConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ExtensionServiceConfigSpec](ExtensionServiceConfigType, &ExtensionServiceConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/extension_services_config_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ExtensionServiceConfigStatusType is a type of ExtensionServiceConfig. const ExtensionServiceConfigStatusType = resource.Type("ExtensionServiceConfigStatuses.runtime.talos.dev") // ExtensionServiceConfigStatus represents a resource that describes status of rendered extensions service config files. type ExtensionServiceConfigStatus = typed.Resource[ExtensionServiceConfigStatusSpec, ExtensionServiceConfigStatusExtension] // ExtensionServiceConfigStatusSpec describes status of rendered extensions service config files. // //gotagsrewrite:gen type ExtensionServiceConfigStatusSpec struct { SpecVersion string `yaml:"specVersion" protobuf:"1"` } // NewExtensionServiceConfigStatusSpec initializes a new ExtensionServiceConfigStatusSpec. func NewExtensionServiceConfigStatusSpec(namespace resource.Namespace, id resource.ID) *ExtensionServiceConfigStatus { return typed.NewResource[ExtensionServiceConfigStatusSpec, ExtensionServiceConfigStatusExtension]( resource.NewMetadata(namespace, ExtensionServiceConfigStatusType, id, resource.VersionUndefined), ExtensionServiceConfigStatusSpec{}, ) } // ExtensionServiceConfigStatusExtension provides auxiliary methods for ExtensionServiceConfig. type ExtensionServiceConfigStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ExtensionServiceConfigStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ExtensionServiceConfigStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ExtensionServiceConfigStatusSpec](ExtensionServiceConfigStatusType, &ExtensionServiceConfigStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/extension_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/extensions" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ExtensionStatusType is type of Extension resource. const ExtensionStatusType = resource.Type("ExtensionStatuses.runtime.talos.dev") // ExtensionStatus resource holds status of installed system extensions. type ExtensionStatus = typed.Resource[ExtensionStatusSpec, ExtensionStatusExtension] // ExtensionStatusSpec is the spec for system extensions. type ExtensionStatusSpec = extensions.Layer // NewExtensionStatus initializes a ExtensionStatus resource. func NewExtensionStatus(namespace resource.Namespace, id resource.ID) *ExtensionStatus { return typed.NewResource[ExtensionStatusSpec, ExtensionStatusExtension]( resource.NewMetadata(namespace, ExtensionStatusType, id, resource.VersionUndefined), ExtensionStatusSpec{}, ) } // ExtensionStatusExtension is auxiliary resource data for ExtensionStatus. type ExtensionStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ExtensionStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ExtensionStatusType, Aliases: []resource.Type{"extensions"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Name", JSONPath: `{.metadata.name}`, }, { Name: "Version", JSONPath: `{.metadata.version}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ExtensionStatusSpec](ExtensionStatusType, &ExtensionStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/fipsstate_enumer.go ================================================ // Code generated by "enumer -type=SELinuxState -type FIPSState -linecomment -text"; DO NOT EDIT. package runtime import ( "fmt" "strings" ) const _FIPSStateName = "disabledenabledenabled, strict" var _FIPSStateIndex = [...]uint8{0, 8, 15, 30} const _FIPSStateLowerName = "disabledenabledenabled, strict" func (i FIPSState) String() string { if i < 0 || i >= FIPSState(len(_FIPSStateIndex)-1) { return fmt.Sprintf("FIPSState(%d)", i) } return _FIPSStateName[_FIPSStateIndex[i]:_FIPSStateIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _FIPSStateNoOp() { var x [1]struct{} _ = x[FIPSStateDisabled-(0)] _ = x[FIPSStateEnabled-(1)] _ = x[FIPSStateStrict-(2)] } var _FIPSStateValues = []FIPSState{FIPSStateDisabled, FIPSStateEnabled, FIPSStateStrict} var _FIPSStateNameToValueMap = map[string]FIPSState{ _FIPSStateName[0:8]: FIPSStateDisabled, _FIPSStateLowerName[0:8]: FIPSStateDisabled, _FIPSStateName[8:15]: FIPSStateEnabled, _FIPSStateLowerName[8:15]: FIPSStateEnabled, _FIPSStateName[15:30]: FIPSStateStrict, _FIPSStateLowerName[15:30]: FIPSStateStrict, } var _FIPSStateNames = []string{ _FIPSStateName[0:8], _FIPSStateName[8:15], _FIPSStateName[15:30], } // FIPSStateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func FIPSStateString(s string) (FIPSState, error) { if val, ok := _FIPSStateNameToValueMap[s]; ok { return val, nil } if val, ok := _FIPSStateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to FIPSState values", s) } // FIPSStateValues returns all values of the enum func FIPSStateValues() []FIPSState { return _FIPSStateValues } // FIPSStateStrings returns a slice of all String values of the enum func FIPSStateStrings() []string { strs := make([]string, len(_FIPSStateNames)) copy(strs, _FIPSStateNames) return strs } // IsAFIPSState returns "true" if the value is listed in the enum definition. "false" otherwise func (i FIPSState) IsAFIPSState() bool { for _, v := range _FIPSStateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for FIPSState func (i FIPSState) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for FIPSState func (i *FIPSState) UnmarshalText(text []byte) error { var err error *i, err = FIPSStateString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/runtime/kernel_cmdline.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KernelCmdlineType is type of KernelCmdline resource. const KernelCmdlineType = resource.Type("KernelCmdlines.runtime.talos.dev") // KernelCmdline resource holds configuration for kernel message log streaming. type KernelCmdline = typed.Resource[KernelCmdlineSpec, KernelCmdlineExtension] // KernelCmdlineID is a resource ID for KernelCmdline. const KernelCmdlineID resource.ID = "cmdline" // KernelCmdlineSpec presents kernel command line (contents of /proc/cmdline). // //gotagsrewrite:gen type KernelCmdlineSpec struct { Cmdline string `yaml:"cmdline" protobuf:"1"` } // NewKernelCmdline initializes a KernelCmdline resource. func NewKernelCmdline() *KernelCmdline { return typed.NewResource[KernelCmdlineSpec, KernelCmdlineExtension]( resource.NewMetadata(NamespaceName, KernelCmdlineType, KernelCmdlineID, resource.VersionUndefined), KernelCmdlineSpec{}, ) } // KernelCmdlineExtension is auxiliary resource data for KernelCmdline. type KernelCmdlineExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KernelCmdlineExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KernelCmdlineType, Aliases: []resource.Type{"cmdline"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Cmdline", JSONPath: "{.cmdline}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KernelCmdlineSpec](KernelCmdlineType, &KernelCmdline{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/kernel_module_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KernelModuleSpecType is type of KernelModuleSpec resource. const KernelModuleSpecType = resource.Type("KernelModuleSpecs.runtime.talos.dev") // KernelModuleSpec resource holds information about Linux kernel module to load. type KernelModuleSpec = typed.Resource[KernelModuleSpecSpec, KernelModuleSpecExtension] // KernelModuleSpecSpec describes Linux kernel module to load. // //gotagsrewrite:gen type KernelModuleSpecSpec struct { Name string `yaml:"string" protobuf:"1"` Parameters []string `yaml:"parameters" protobuf:"2"` // more options in the future: aliases, etc. } // NewKernelModuleSpec initializes a KernelModuleSpec resource. func NewKernelModuleSpec(namespace resource.Namespace, id resource.ID) *KernelModuleSpec { return typed.NewResource[KernelModuleSpecSpec, KernelModuleSpecExtension]( resource.NewMetadata(namespace, KernelModuleSpecType, id, resource.VersionUndefined), KernelModuleSpecSpec{}, ) } // KernelModuleSpecExtension is auxiliary resource data for KernelModuleSpec. type KernelModuleSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KernelModuleSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KernelModuleSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KernelModuleSpecSpec](KernelModuleSpecType, &KernelModuleSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/kernel_params_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KernelParamSpecType is type of KernelParam resource. const KernelParamSpecType = resource.Type("KernelParamSpecs.runtime.talos.dev") // KernelParamDefaultSpecType is type of KernelParam resource for default kernel params. const KernelParamDefaultSpecType = resource.Type("KernelParamDefaultSpecs.runtime.talos.dev") // KernelParam interface. type KernelParam interface { TypedSpec() *KernelParamSpecSpec } // KernelParamSpec resource holds sysctl flags to define. type KernelParamSpec = typed.Resource[KernelParamSpecSpec, KernelParamSpecExtension] // KernelParamSpecSpec describes status of the defined sysctls. // //gotagsrewrite:gen type KernelParamSpecSpec struct { Value string `yaml:"value" protobuf:"1"` IgnoreErrors bool `yaml:"ignoreErrors" protobuf:"2"` } // NewKernelParamSpec initializes a KernelParamSpec resource. func NewKernelParamSpec(namespace resource.Namespace, id resource.ID) *KernelParamSpec { return typed.NewResource[KernelParamSpecSpec, KernelParamSpecExtension]( resource.NewMetadata(namespace, KernelParamSpecType, id, resource.VersionUndefined), KernelParamSpecSpec{}, ) } // KernelParamSpecExtension is the typed.Extension for KernelParamSpec. type KernelParamSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KernelParamSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KernelParamSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } // KernelParamDefaultSpec implements meta.ResourceDefinitionProvider interface. type KernelParamDefaultSpec = typed.Resource[KernelParamDefaultSpecSpec, KernelParamDefaultSpecExtension] // KernelParamDefaultSpecSpec is same as KernelParamSpecSpec. type KernelParamDefaultSpecSpec = KernelParamSpecSpec // NewKernelParamDefaultSpec initializes a KernelParamDefaultSpec resource. func NewKernelParamDefaultSpec(namespace resource.Namespace, id resource.ID) *KernelParamDefaultSpec { return typed.NewResource[KernelParamDefaultSpecSpec, KernelParamDefaultSpecExtension]( resource.NewMetadata(namespace, KernelParamDefaultSpecType, id, resource.VersionUndefined), KernelParamSpecSpec{}, ) } // KernelParamDefaultSpecExtension is the typed.Extension for KernelParamDefaultSpec. type KernelParamDefaultSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KernelParamDefaultSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KernelParamDefaultSpecType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{}, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KernelParamSpecSpec](KernelParamSpecType, &KernelParamSpec{}) if err != nil { panic(err) } err = protobuf.RegisterDynamic[KernelParamDefaultSpecSpec](KernelParamDefaultSpecType, &KernelParamDefaultSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/kernel_params_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KernelParamStatusType is type of KernelParam resource. const KernelParamStatusType = resource.Type("KernelParamStatuses.runtime.talos.dev") // KernelParamStatus resource holds defined sysctl flags status. type KernelParamStatus = typed.Resource[KernelParamStatusSpec, KernelParamStatusExtension] // KernelParamStatusSpec describes status of the defined sysctls. // //gotagsrewrite:gen type KernelParamStatusSpec struct { Current string `yaml:"current" protobuf:"1"` Default string `yaml:"default" protobuf:"2"` Unsupported bool `yaml:"unsupported" protobuf:"3"` } // NewKernelParamStatus initializes a KernelParamStatus resource. func NewKernelParamStatus(namespace resource.Namespace, id resource.ID) *KernelParamStatus { return typed.NewResource[KernelParamStatusSpec, KernelParamStatusExtension]( resource.NewMetadata(namespace, KernelParamStatusType, id, resource.VersionUndefined), KernelParamStatusSpec{}, ) } // KernelParamStatusExtension is auxiliary resource data for KernelParamStatus. type KernelParamStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KernelParamStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KernelParamStatusType, Aliases: []resource.Type{"sysctls", "kernelparameters", "kernelparams"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Current", JSONPath: `{.current}`, }, { Name: "Default", JSONPath: `{.default}`, }, { Name: "Unsupported", JSONPath: `{.unsupported}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KernelParamStatusSpec](KernelParamStatusType, &KernelParamStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/kmsg_log_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "net/url" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KmsgLogConfigType is type of KmsgLogConfig resource. const KmsgLogConfigType = resource.Type("KmsgLogConfigs.runtime.talos.dev") // KmsgLogConfig resource holds configuration for kernel message log streaming. type KmsgLogConfig = typed.Resource[KmsgLogConfigSpec, KmsgLogConfigExtension] // KmsgLogConfigID is a resource ID for KmsgLogConfig. const KmsgLogConfigID resource.ID = "kmsg-log" // KmsgLogConfigSpec describes configuration for kmsg log streaming. // //gotagsrewrite:gen type KmsgLogConfigSpec struct { Destinations []*url.URL `yaml:"destinations" protobuf:"1"` } // NewKmsgLogConfig initializes a KmsgLogConfig resource. func NewKmsgLogConfig() *KmsgLogConfig { return typed.NewResource[KmsgLogConfigSpec, KmsgLogConfigExtension]( resource.NewMetadata(NamespaceName, KmsgLogConfigType, KmsgLogConfigID, resource.VersionUndefined), KmsgLogConfigSpec{}, ) } // KmsgLogConfigExtension is auxiliary resource data for KmsgLogConfig. type KmsgLogConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KmsgLogConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KmsgLogConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KmsgLogConfigSpec](KmsgLogConfigType, &KmsgLogConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/loaded_kernel_module.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // LoadedKernelModuleType is type of LoadedKernelModule resource. const LoadedKernelModuleType = resource.Type("LoadedKernelModules.runtime.talos.dev") // LoadedKernelModule resource holds information about Linux kernel module to load. type LoadedKernelModule = typed.Resource[LoadedKernelModuleSpec, LoadedKernelModuleExtension] // LoadedKernelModuleSpec describes Linux kernel module to load. // //gotagsrewrite:gen type LoadedKernelModuleSpec struct { Size int `yaml:"size" protobuf:"1"` ReferenceCount int `yaml:"referenceCount" protobuf:"2"` Dependencies []string `yaml:"dependencies,omitempty" protobuf:"3"` State string `yaml:"state" protobuf:"4"` Address string `yaml:"address" protobuf:"5"` } // NewLoadedKernelModule initializes a LoadedKernelModule resource. func NewLoadedKernelModule(namespace resource.Namespace, id resource.ID) *LoadedKernelModule { return typed.NewResource[LoadedKernelModuleSpec, LoadedKernelModuleExtension]( resource.NewMetadata(namespace, LoadedKernelModuleType, id, resource.VersionUndefined), LoadedKernelModuleSpec{}, ) } // LoadedKernelModuleExtension is auxiliary resource data for LoadedKernelModule. type LoadedKernelModuleExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (LoadedKernelModuleExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: LoadedKernelModuleType, Aliases: []resource.Type{"module", "modules"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "State", JSONPath: "{.state}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[LoadedKernelModuleSpec](LoadedKernelModuleType, &LoadedKernelModule{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/machine_reset_signal.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MachineResetSignalType is type of MachineResetSignal resource. const MachineResetSignalType = resource.Type("MachineResetSignals.runtime.talos.dev") // MachineResetSignalID is singleton MachineResetSignal resource ID. const MachineResetSignalID = resource.ID("machine") // MachineResetSignal resource is created to signal that the machine is going to be reset soon. // // This resource is created when all remaining actions are local to the node, and network communication is not required. type MachineResetSignal = typed.Resource[MachineResetSignalSpec, MachineResetSignalExtension] // MachineResetSignalSpec describes the spec of MachineResetSignal. // //gotagsrewrite:gen type MachineResetSignalSpec struct{} // NewMachineResetSignal initializes a MachineResetSignal resource. func NewMachineResetSignal() *MachineResetSignal { return typed.NewResource[MachineResetSignalSpec, MachineResetSignalExtension]( resource.NewMetadata(NamespaceName, MachineResetSignalType, MachineResetSignalID, resource.VersionUndefined), MachineResetSignalSpec{}, ) } // MachineResetSignalExtension is auxiliary resource data for MachineResetSignal. type MachineResetSignalExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MachineResetSignalExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MachineResetSignalType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MachineResetSignalSpec](MachineResetSignalType, &MachineResetSignal{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/machine_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MachineStatusType is type of MachineStatus resource. const MachineStatusType = resource.Type("MachineStatuses.runtime.talos.dev") // MachineStatusID is singleton MachineStatus resource ID. const MachineStatusID = resource.ID("machine") // MachineStatus resource holds information about aggregated machine status. type MachineStatus = typed.Resource[MachineStatusSpec, MachineStatusExtension] // MachineStatusSpec describes status of the defined sysctls. // //gotagsrewrite:gen type MachineStatusSpec struct { Stage MachineStage `yaml:"stage" protobuf:"1"` Status MachineStatusStatus `yaml:"status" protobuf:"2"` } // MachineStatusStatus describes machine current status at the stage. // //gotagsrewrite:gen type MachineStatusStatus struct { Ready bool `yaml:"ready" protobuf:"1"` UnmetConditions []UnmetCondition `yaml:"unmetConditions" protobuf:"2"` } // UnmetCondition is a failure which prevents machine from being ready at the stage. // //gotagsrewrite:gen type UnmetCondition struct { Name string `yaml:"name" protobuf:"1"` Reason string `yaml:"reason" protobuf:"2"` } // NewMachineStatus initializes a MachineStatus resource. func NewMachineStatus() *MachineStatus { return typed.NewResource[MachineStatusSpec, MachineStatusExtension]( resource.NewMetadata(NamespaceName, MachineStatusType, MachineStatusID, resource.VersionUndefined), MachineStatusSpec{}, ) } // MachineStatusExtension is auxiliary resource data for MachineStatus. type MachineStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MachineStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MachineStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Stage", JSONPath: `{.stage}`, }, { Name: "Ready", JSONPath: `{.status.ready}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MachineStatusSpec](MachineStatusType, &MachineStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/machinestage.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime //go:generate go tool github.com/dmarkham/enumer -type=MachineStage -linecomment -text // MachineStage describes the stage of the machine boot/run process. type MachineStage int // Machine stages. // //structprotogen:gen_enum const ( MachineStageUnknown MachineStage = iota // unknown MachineStageBooting // booting MachineStageInstalling // installing MachineStageMaintenance // maintenance MachineStageRunning // running MachineStageRebooting // rebooting MachineStageShuttingDown // shutting down MachineStageResetting // resetting MachineStageUpgrading // upgrading ) ================================================ FILE: pkg/machinery/resources/runtime/machinestage_enumer.go ================================================ // Code generated by "enumer -type=MachineStage -linecomment -text"; DO NOT EDIT. package runtime import ( "fmt" "strings" ) const _MachineStageName = "unknownbootinginstallingmaintenancerunningrebootingshutting downresettingupgrading" var _MachineStageIndex = [...]uint8{0, 7, 14, 24, 35, 42, 51, 64, 73, 82} const _MachineStageLowerName = "unknownbootinginstallingmaintenancerunningrebootingshutting downresettingupgrading" func (i MachineStage) String() string { if i < 0 || i >= MachineStage(len(_MachineStageIndex)-1) { return fmt.Sprintf("MachineStage(%d)", i) } return _MachineStageName[_MachineStageIndex[i]:_MachineStageIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _MachineStageNoOp() { var x [1]struct{} _ = x[MachineStageUnknown-(0)] _ = x[MachineStageBooting-(1)] _ = x[MachineStageInstalling-(2)] _ = x[MachineStageMaintenance-(3)] _ = x[MachineStageRunning-(4)] _ = x[MachineStageRebooting-(5)] _ = x[MachineStageShuttingDown-(6)] _ = x[MachineStageResetting-(7)] _ = x[MachineStageUpgrading-(8)] } var _MachineStageValues = []MachineStage{MachineStageUnknown, MachineStageBooting, MachineStageInstalling, MachineStageMaintenance, MachineStageRunning, MachineStageRebooting, MachineStageShuttingDown, MachineStageResetting, MachineStageUpgrading} var _MachineStageNameToValueMap = map[string]MachineStage{ _MachineStageName[0:7]: MachineStageUnknown, _MachineStageLowerName[0:7]: MachineStageUnknown, _MachineStageName[7:14]: MachineStageBooting, _MachineStageLowerName[7:14]: MachineStageBooting, _MachineStageName[14:24]: MachineStageInstalling, _MachineStageLowerName[14:24]: MachineStageInstalling, _MachineStageName[24:35]: MachineStageMaintenance, _MachineStageLowerName[24:35]: MachineStageMaintenance, _MachineStageName[35:42]: MachineStageRunning, _MachineStageLowerName[35:42]: MachineStageRunning, _MachineStageName[42:51]: MachineStageRebooting, _MachineStageLowerName[42:51]: MachineStageRebooting, _MachineStageName[51:64]: MachineStageShuttingDown, _MachineStageLowerName[51:64]: MachineStageShuttingDown, _MachineStageName[64:73]: MachineStageResetting, _MachineStageLowerName[64:73]: MachineStageResetting, _MachineStageName[73:82]: MachineStageUpgrading, _MachineStageLowerName[73:82]: MachineStageUpgrading, } var _MachineStageNames = []string{ _MachineStageName[0:7], _MachineStageName[7:14], _MachineStageName[14:24], _MachineStageName[24:35], _MachineStageName[35:42], _MachineStageName[42:51], _MachineStageName[51:64], _MachineStageName[64:73], _MachineStageName[73:82], } // MachineStageString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func MachineStageString(s string) (MachineStage, error) { if val, ok := _MachineStageNameToValueMap[s]; ok { return val, nil } if val, ok := _MachineStageNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to MachineStage values", s) } // MachineStageValues returns all values of the enum func MachineStageValues() []MachineStage { return _MachineStageValues } // MachineStageStrings returns a slice of all String values of the enum func MachineStageStrings() []string { strs := make([]string, len(_MachineStageNames)) copy(strs, _MachineStageNames) return strs } // IsAMachineStage returns "true" if the value is listed in the enum definition. "false" otherwise func (i MachineStage) IsAMachineStage() bool { for _, v := range _MachineStageValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for MachineStage func (i MachineStage) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for MachineStage func (i *MachineStage) UnmarshalText(text []byte) error { var err error *i, err = MachineStageString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/runtime/machinestage_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestMachineStageMatchesProto(t *testing.T) { assert := assert.New(t) assert.EqualValues(runtime.MachineStageUnknown, machine.MachineStatusEvent_UNKNOWN) assert.EqualValues(runtime.MachineStageBooting, machine.MachineStatusEvent_BOOTING) assert.EqualValues(runtime.MachineStageInstalling, machine.MachineStatusEvent_INSTALLING) assert.EqualValues(runtime.MachineStageMaintenance, machine.MachineStatusEvent_MAINTENANCE) assert.EqualValues(runtime.MachineStageRunning, machine.MachineStatusEvent_RUNNING) assert.EqualValues(runtime.MachineStageRebooting, machine.MachineStatusEvent_REBOOTING) assert.EqualValues(runtime.MachineStageShuttingDown, machine.MachineStatusEvent_SHUTTING_DOWN) assert.EqualValues(runtime.MachineStageResetting, machine.MachineStatusEvent_RESETTING) assert.EqualValues(runtime.MachineStageUpgrading, machine.MachineStatusEvent_UPGRADING) } ================================================ FILE: pkg/machinery/resources/runtime/maintenance_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MaintenanceServiceConfigType is type of MaintenanceConfig resource. const MaintenanceServiceConfigType = resource.Type("MaintenanceServiceConfigs.runtime.talos.dev") // MaintenanceServiceConfig resource holds configuration for maintenance service API. type MaintenanceServiceConfig = typed.Resource[MaintenanceServiceConfigSpec, MaintenanceServiceConfigExtension] // MaintenanceServiceConfigID is a resource ID for MaintenanceConfig. const MaintenanceServiceConfigID resource.ID = "maintenance" // MaintenanceServiceConfigSpec describes configuration for maintenance service API. // //gotagsrewrite:gen type MaintenanceServiceConfigSpec struct { ListenAddress string `yaml:"listenAddress" protobuf:"1"` ReachableAddresses []netip.Addr `yaml:"reachableAddresses" protobuf:"2"` } // NewMaintenanceServiceConfig initializes a MaintenanceConfig resource. func NewMaintenanceServiceConfig() *MaintenanceServiceConfig { return typed.NewResource[MaintenanceServiceConfigSpec, MaintenanceServiceConfigExtension]( resource.NewMetadata(NamespaceName, MaintenanceServiceConfigType, MaintenanceServiceConfigID, resource.VersionUndefined), MaintenanceServiceConfigSpec{}, ) } // MaintenanceServiceConfigExtension is auxiliary resource data for MaintenanceConfig. type MaintenanceServiceConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MaintenanceServiceConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MaintenanceServiceConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MaintenanceServiceConfigSpec](MaintenanceServiceConfigType, &MaintenanceServiceConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/maintenance_request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MaintenanceServiceRequestType is type of MaintenanceServiceConfig resource. const MaintenanceServiceRequestType = resource.Type("MaintenanceServiceRequests.runtime.talos.dev") // MaintenanceServiceRequest resource indicates that the maintenance service should run. type MaintenanceServiceRequest = typed.Resource[MaintenanceServiceRequestSpec, MaintenanceServiceRequestExtension] // MaintenanceServiceRequestID is a resource ID for MaintenanceConfig. const MaintenanceServiceRequestID resource.ID = "maintenance" // MaintenanceServiceRequestSpec indicates that maintenance service API should be started. // //gotagsrewrite:gen type MaintenanceServiceRequestSpec struct{} // NewMaintenanceServiceRequest initializes a MaintenanceConfig resource. func NewMaintenanceServiceRequest() *MaintenanceServiceRequest { return typed.NewResource[MaintenanceServiceRequestSpec, MaintenanceServiceRequestExtension]( resource.NewMetadata(NamespaceName, MaintenanceServiceRequestType, MaintenanceServiceRequestID, resource.VersionUndefined), MaintenanceServiceRequestSpec{}, ) } // MaintenanceServiceRequestExtension is auxiliary resource data for MaintenanceConfig. type MaintenanceServiceRequestExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MaintenanceServiceRequestExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MaintenanceServiceRequestType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MaintenanceServiceRequestSpec](MaintenanceServiceRequestType, &MaintenanceServiceRequest{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/meta_key.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MetaKeyType is type of MetaKey resource. const MetaKeyType = resource.Type("MetaKeys.runtime.talos.dev") // MetaKey resource holds value of a key in META partition. type MetaKey = typed.Resource[MetaKeySpec, MetaKeyExtension] // MetaKeySpec describes status of the defined sysctls. // //gotagsrewrite:gen type MetaKeySpec struct { Value string `yaml:"value" protobuf:"1"` } // NewMetaKey initializes a MetaKey resource. func NewMetaKey(namespace resource.Namespace, id resource.ID) *MetaKey { return typed.NewResource[MetaKeySpec, MetaKeyExtension]( resource.NewMetadata(namespace, MetaKeyType, id, resource.VersionUndefined), MetaKeySpec{}, ) } // MetaKeyExtension is auxiliary resource data for MetaKey. type MetaKeyExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MetaKeyExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MetaKeyType, Aliases: []resource.Type{"meta"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Value", JSONPath: `{.value}`, }, }, } } // MetaKeyTagToID converts a tag to ID for MetaKey resource. func MetaKeyTagToID(t uint8) resource.ID { return fmt.Sprintf("0x%02x", t) } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MetaKeySpec](MetaKeyType, &MetaKey{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/meta_loaded.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MetaLoadedType is type of [MetaLoaded] resource. const MetaLoadedType = resource.Type("MetaLoads.runtime.talos.dev") // MetaLoaded resource appears when all meta keys are loaded. type MetaLoaded = typed.Resource[MetaLoadedSpec, MetaLoadedExtension] // MetaLoadedID is the ID of [MetaLoaded] resource. const MetaLoadedID = resource.ID("meta-loaded") // MetaLoadedSpec is the spec for meta loaded. The Done field is always true when resource exists. // //gotagsrewrite:gen type MetaLoadedSpec struct { Done bool `yaml:"done" protobuf:"1"` } // NewMetaLoaded initializes a [MetaLoaded] resource. func NewMetaLoaded() *MetaLoaded { return typed.NewResource[MetaLoadedSpec, MetaLoadedExtension]( resource.NewMetadata(NamespaceName, MetaLoadedType, MetaLoadedID, resource.VersionUndefined), MetaLoadedSpec{}, ) } // MetaLoadedExtension is auxiliary resource data for [MetaLoaded]. type MetaLoadedExtension struct{} // ResourceDefinition implements [meta.ResourceDefinitionProvider] interface. func (MetaLoadedExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MetaLoadedType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Done", JSONPath: `{.done}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MetaLoadedSpec](MetaLoadedType, &MetaLoaded{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/mount_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MountStatusType is type of Mount resource. const MountStatusType = resource.Type("MountStatuses.runtime.talos.dev") // MountStatus resource holds defined sysctl flags status. type MountStatus = typed.Resource[MountStatusSpec, MountStatusExtension] // MountStatusSpec describes status of the defined sysctls. // //gotagsrewrite:gen type MountStatusSpec struct { Source string `yaml:"source" protobuf:"1"` Target string `yaml:"target" protobuf:"2"` FilesystemType string `yaml:"filesystemType" protobuf:"3"` Options []string `yaml:"options" protobuf:"4"` Encrypted bool `yaml:"encrypted" protobuf:"5"` EncryptionProviders []string `yaml:"encryptionProviders,omitempty" protobuf:"6"` } // NewMountStatus initializes a MountStatus resource. func NewMountStatus(namespace resource.Namespace, id resource.ID) *MountStatus { return typed.NewResource[MountStatusSpec, MountStatusExtension]( resource.NewMetadata(namespace, MountStatusType, id, resource.VersionUndefined), MountStatusSpec{}, ) } // MountStatusExtension is auxiliary resource data for MountStatus. type MountStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MountStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MountStatusType, Aliases: []resource.Type{"mounts"}, DefaultNamespace: NamespaceName, SkipAutomaticAliases: true, PrintColumns: []meta.PrintColumn{ { Name: "Source", JSONPath: `{.source}`, }, { Name: "Target", JSONPath: `{.target}`, }, { Name: "Filesystem Type", JSONPath: `{.filesystemType}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[MountStatusSpec](MountStatusType, &MountStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/oom.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime // QoSCgroupClass is a quality of service class of cgroup. type QoSCgroupClass int // QoSCgroupClass constants. // // Higher value corresponds to a more important cgroup. const ( QoSCgroupClassBesteffort QoSCgroupClass = iota QoSCgroupClassBurstable QoSCgroupClassGuaranteed QoSCgroupClassPodruntime QoSCgroupClassSystem ) ================================================ FILE: pkg/machinery/resources/runtime/oom_action.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // OOMActionType is the type of the OOM action record resource. const OOMActionType = resource.Type("OOMActions.talos.dev") // OOMAction is the OOM action record resource. type OOMAction = typed.Resource[OOMActionSpec, OOMActionExtension] // OOMActionSpec describes the OOM action record resource properties. // //gotagsrewrite:gen type OOMActionSpec struct { TriggerContext string `yaml:"triggerContext,omitempty" protobuf:"1"` Score float64 `yaml:"score,omitempty" protobuf:"2"` Processes []string `yaml:"processes,omitempty" protobuf:"3"` } // NewOOMActionSpec initializes an OOM action log entry resource. func NewOOMActionSpec(namespace resource.Namespace, id resource.ID) *OOMAction { return typed.NewResource[OOMActionSpec, OOMActionExtension]( resource.NewMetadata(namespace, OOMActionType, id, resource.VersionUndefined), OOMActionSpec{}, ) } // OOMActionExtension provides auxiliary methods for OOMAction. type OOMActionExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (OOMActionExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: OOMActionType, DefaultNamespace: NamespaceName, Aliases: []string{"oomaction", "oomactions"}, PrintColumns: []meta.PrintColumn{ { Name: "Time", JSONPath: `{.time}`, }, { Name: "Score", JSONPath: `{.score}`, }, }, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[OOMActionSpec](OOMActionType, &OOMAction{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/pcirebind_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime ================================================ FILE: pkg/machinery/resources/runtime/platform_metadata.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // PlatformMetadataType is type of Metadata resource. const PlatformMetadataType = resource.Type("PlatformMetadatas.talos.dev") // PlatformMetadataID is the ID for Metadata resource platform. const PlatformMetadataID resource.ID = "platformmetadata" // PlatformMetadata resource holds. type PlatformMetadata = typed.Resource[PlatformMetadataSpec, PlatformMetadataExtension] // PlatformMetadataSpec describes platform metadata properties. // //gotagsrewrite:gen type PlatformMetadataSpec struct { Platform string `yaml:"platform,omitempty" protobuf:"1"` Hostname string `yaml:"hostname,omitempty" protobuf:"2"` Region string `yaml:"region,omitempty" protobuf:"3"` Zone string `yaml:"zone,omitempty" protobuf:"4"` InstanceType string `yaml:"instanceType,omitempty" protobuf:"5"` InstanceID string `yaml:"instanceId,omitempty" protobuf:"6"` ProviderID string `yaml:"providerId,omitempty" protobuf:"7"` Spot bool `yaml:"spot,omitempty" protobuf:"8"` InternalDNS string `yaml:"internalDNS,omitempty" protobuf:"9"` ExternalDNS string `yaml:"externalDNS,omitempty" protobuf:"10"` Tags map[string]string `yaml:"tags,omitempty" protobuf:"11"` } // NewPlatformMetadataSpec initializes a MetadataSpec resource. func NewPlatformMetadataSpec(namespace resource.Namespace, _ resource.ID) *PlatformMetadata { return typed.NewResource[PlatformMetadataSpec, PlatformMetadataExtension]( resource.NewMetadata(namespace, PlatformMetadataType, PlatformMetadataID, resource.VersionUndefined), PlatformMetadataSpec{}, ) } // PlatformMetadataExtension provides auxiliary methods for PlatformMetadata. type PlatformMetadataExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (PlatformMetadataExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: PlatformMetadataType, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Platform", JSONPath: `{.platform}`, }, { Name: "Type", JSONPath: `{.instanceType}`, }, { Name: "Region", JSONPath: `{.region}`, }, { Name: "Zone", JSONPath: `{.zone}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[PlatformMetadataSpec](PlatformMetadataType, &PlatformMetadata{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/runtime.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) //go:generate go tool github.com/siderolabs/deep-copy -type APIServiceConfigSpec -type BootedEntrySpec -type DevicesStatusSpec -type DiagnosticSpec -type EnvironmentSpec -type EventSinkConfigSpec -type ExtensionServiceConfigSpec -type ExtensionServiceConfigStatusSpec -type KernelCmdlineSpec -type KernelModuleSpecSpec -type KernelParamSpecSpec -type KernelParamStatusSpec -type KmsgLogConfigSpec -type LoadedKernelModuleSpec -type MaintenanceServiceConfigSpec -type MaintenanceServiceRequestSpec -type MachineResetSignalSpec -type MachineStatusSpec -type MetaKeySpec -type MountStatusSpec -type OOMActionSpec -type PlatformMetadataSpec -type SecurityStateSpec -type MetaLoadedSpec -type SBOMItemSpec -type UniqueMachineTokenSpec -type VersionSpec -type WatchdogTimerConfigSpec -type WatchdogTimerStatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains configuration resources. const NamespaceName resource.Namespace = v1alpha1.NamespaceName ================================================ FILE: pkg/machinery/resources/runtime/runtime_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/runtime" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &runtime.APIServiceConfig{}, &runtime.BootedEntry{}, &runtime.DevicesStatus{}, &runtime.Diagnostic{}, &runtime.EventSinkConfig{}, &runtime.ExtensionStatus{}, &runtime.KernelCmdline{}, &runtime.KernelModuleSpec{}, &runtime.KernelParamSpec{}, &runtime.KernelParamStatus{}, &runtime.KmsgLogConfig{}, &runtime.LoadedKernelModule{}, &runtime.MachineStatus{}, &runtime.MachineResetSignal{}, &runtime.MaintenanceServiceConfig{}, &runtime.MaintenanceServiceRequest{}, &runtime.MetaKey{}, &runtime.MetaLoaded{}, &runtime.MountStatus{}, &runtime.OOMAction{}, &runtime.PlatformMetadata{}, &runtime.SBOMItem{}, &runtime.SecurityState{}, &runtime.UniqueMachineToken{}, &runtime.WatchdogTimerConfig{}, &runtime.WatchdogTimerStatus{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/runtime/sbom_item.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SBOMItemType is the type of the SBOM item resource. const SBOMItemType = resource.Type("SBOMItems.talos.dev") // SBOMItem is the SBOM item resource. type SBOMItem = typed.Resource[SBOMItemSpec, SBOMItemExtension] // SBOMItemSpec describes the SBOM item resource properties. // //gotagsrewrite:gen type SBOMItemSpec struct { Name string `yaml:"name" protobuf:"1"` Version string `yaml:"version" protobuf:"2"` License string `yaml:"license,omitempty" protobuf:"3"` CPEs []string `yaml:"cpes,omitempty" protobuf:"4"` PURLs []string `yaml:"purls,omitempty" protobuf:"5"` Extension bool `yaml:"extension,omitempty" protobuf:"6"` } // NewSBOMItemSpec initializes a security state resource. func NewSBOMItemSpec(namespace resource.Namespace, id resource.ID) *SBOMItem { return typed.NewResource[SBOMItemSpec, SBOMItemExtension]( resource.NewMetadata(namespace, SBOMItemType, id, resource.VersionUndefined), SBOMItemSpec{}, ) } // SBOMItemExtension provides auxiliary methods for SBOMItem. type SBOMItemExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (SBOMItemExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SBOMItemType, DefaultNamespace: NamespaceName, Aliases: []string{"sbom", "sboms"}, PrintColumns: []meta.PrintColumn{ { Name: "Version", JSONPath: `{.version}`, }, { Name: "License", JSONPath: `{.license}`, }, { Name: "Extension", JSONPath: `{.extension}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SBOMItemSpec](SBOMItemType, &SBOMItem{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/security_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // SecurityStateType is the type of the security state resource. const SecurityStateType = resource.Type("SecurityStates.talos.dev") // SecurityStateID is the ID of the security state resource. const SecurityStateID = resource.ID("securitystate") // SecurityState is the security state resource. type SecurityState = typed.Resource[SecurityStateSpec, SecurityStateExtension] //go:generate go tool github.com/dmarkham/enumer -type FIPSState -type=SELinuxState -linecomment -text // SELinuxState describes the current SELinux status. type SELinuxState int // SELinux state. // //structprotogen:gen_enum const ( SELinuxStateDisabled SELinuxState = iota // disabled SELinuxStatePermissive // enabled, permissive SELinuxStateEnforcing // enabled, enforcing ) // FIPSState describes the current FIPS status. type FIPSState int // FIPS state. // //structprotogen:gen_enum const ( FIPSStateDisabled FIPSState = iota // disabled FIPSStateEnabled // enabled FIPSStateStrict // enabled, strict ) // SecurityStateSpec describes the security state resource properties. // //gotagsrewrite:gen type SecurityStateSpec struct { SecureBoot bool `yaml:"secureBoot" protobuf:"1"` UKISigningKeyFingerprint string `yaml:"ukiSigningKeyFingerprint,omitempty" protobuf:"2"` PCRSigningKeyFingerprint string `yaml:"pcrSigningKeyFingerprint,omitempty" protobuf:"3"` SELinuxState SELinuxState `yaml:"selinuxState,omitempty" protobuf:"4"` FIPSState FIPSState `yaml:"fipsState,omitempty" protobuf:"6"` BootedWithUKI bool `yaml:"bootedWithUKI,omitempty" protobuf:"5"` ModuleSignatureEnforced bool `yaml:"moduleSignatureEnforced,omitempty" protobuf:"7"` } // NewSecurityStateSpec initializes a security state resource. func NewSecurityStateSpec(namespace resource.Namespace) *SecurityState { return typed.NewResource[SecurityStateSpec, SecurityStateExtension]( resource.NewMetadata(namespace, SecurityStateType, SecurityStateID, resource.VersionUndefined), SecurityStateSpec{}, ) } // SecurityStateExtension provides auxiliary methods for SecurityState. type SecurityStateExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (SecurityStateExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: SecurityStateType, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "SecureBoot", JSONPath: `{.secureBoot}`, }, { Name: "UKISigningKeyFingerprint", JSONPath: `{.ukiSigningKeyFingerprint}`, }, { Name: "PCRSigningKeyFingerprint", JSONPath: `{.pcrSigningKeyFingerprint}`, }, { Name: "SELinuxState", JSONPath: `{.selinuxState}`, }, { Name: "ModuleSignatureEnforced", JSONPath: `{.moduleSignatureEnforced}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[SecurityStateSpec](SecurityStateType, &SecurityState{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/selinuxstate_enumer.go ================================================ // Code generated by "enumer -type FIPSState -type=SELinuxState -linecomment -text"; DO NOT EDIT. package runtime import ( "fmt" "strings" ) const _SELinuxStateName = "disabledenabled, permissiveenabled, enforcing" var _SELinuxStateIndex = [...]uint8{0, 8, 27, 45} const _SELinuxStateLowerName = "disabledenabled, permissiveenabled, enforcing" func (i SELinuxState) String() string { if i < 0 || i >= SELinuxState(len(_SELinuxStateIndex)-1) { return fmt.Sprintf("SELinuxState(%d)", i) } return _SELinuxStateName[_SELinuxStateIndex[i]:_SELinuxStateIndex[i+1]] } // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. func _SELinuxStateNoOp() { var x [1]struct{} _ = x[SELinuxStateDisabled-(0)] _ = x[SELinuxStatePermissive-(1)] _ = x[SELinuxStateEnforcing-(2)] } var _SELinuxStateValues = []SELinuxState{SELinuxStateDisabled, SELinuxStatePermissive, SELinuxStateEnforcing} var _SELinuxStateNameToValueMap = map[string]SELinuxState{ _SELinuxStateName[0:8]: SELinuxStateDisabled, _SELinuxStateLowerName[0:8]: SELinuxStateDisabled, _SELinuxStateName[8:27]: SELinuxStatePermissive, _SELinuxStateLowerName[8:27]: SELinuxStatePermissive, _SELinuxStateName[27:45]: SELinuxStateEnforcing, _SELinuxStateLowerName[27:45]: SELinuxStateEnforcing, } var _SELinuxStateNames = []string{ _SELinuxStateName[0:8], _SELinuxStateName[8:27], _SELinuxStateName[27:45], } // SELinuxStateString retrieves an enum value from the enum constants string name. // Throws an error if the param is not part of the enum. func SELinuxStateString(s string) (SELinuxState, error) { if val, ok := _SELinuxStateNameToValueMap[s]; ok { return val, nil } if val, ok := _SELinuxStateNameToValueMap[strings.ToLower(s)]; ok { return val, nil } return 0, fmt.Errorf("%s does not belong to SELinuxState values", s) } // SELinuxStateValues returns all values of the enum func SELinuxStateValues() []SELinuxState { return _SELinuxStateValues } // SELinuxStateStrings returns a slice of all String values of the enum func SELinuxStateStrings() []string { strs := make([]string, len(_SELinuxStateNames)) copy(strs, _SELinuxStateNames) return strs } // IsASELinuxState returns "true" if the value is listed in the enum definition. "false" otherwise func (i SELinuxState) IsASELinuxState() bool { for _, v := range _SELinuxStateValues { if i == v { return true } } return false } // MarshalText implements the encoding.TextMarshaler interface for SELinuxState func (i SELinuxState) MarshalText() ([]byte, error) { return []byte(i.String()), nil } // UnmarshalText implements the encoding.TextUnmarshaler interface for SELinuxState func (i *SELinuxState) UnmarshalText(text []byte) error { var err error *i, err = SELinuxStateString(string(text)) return err } ================================================ FILE: pkg/machinery/resources/runtime/unique_machine_token.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) const ( // UniqueMachineTokenType is type of [UniqueMachineToken] resource. UniqueMachineTokenType = resource.Type("UniqueMachineTokens.runtime.talos.dev") // UniqueMachineTokenID is the ID of [UniqueMachineToken] resource. UniqueMachineTokenID = resource.ID("unique-machine-token") ) // UniqueMachineToken resource appears when all meta keys are loaded. type UniqueMachineToken = typed.Resource[UniqueMachineTokenSpec, UniqueMachineTokenExtension] // UniqueMachineTokenSpec is the spec for the machine unique token. Token can be empty if machine wasn't assigned any. // //gotagsrewrite:gen type UniqueMachineTokenSpec struct { Token string `yaml:"token" protobuf:"1"` } // NewUniqueMachineToken initializes a [UniqueMachineToken] resource. func NewUniqueMachineToken() *UniqueMachineToken { return typed.NewResource[UniqueMachineTokenSpec, UniqueMachineTokenExtension]( resource.NewMetadata(NamespaceName, UniqueMachineTokenType, UniqueMachineTokenID, resource.VersionUndefined), UniqueMachineTokenSpec{}, ) } // UniqueMachineTokenExtension is auxiliary resource data for [UniqueMachineToken]. type UniqueMachineTokenExtension struct{} // ResourceDefinition implements [meta.ResourceDefinitionProvider] interface. func (UniqueMachineTokenExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: UniqueMachineTokenType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Token", JSONPath: `{.token}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[UniqueMachineTokenSpec](UniqueMachineTokenType, &UniqueMachineToken{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // VersionType is type of VersionStatus resource. const VersionType = resource.Type("Versions.runtime.talos.dev") // Version resource holds version of Talos. type Version = typed.Resource[VersionSpec, VersionExtension] // VersionSpec describes version of Talos. type VersionSpec struct { Version string `yaml:"version" protobuf:"1"` } // NewVersion initializes a VersionStatus resource. func NewVersion() *Version { return typed.NewResource[VersionSpec, VersionExtension]( resource.NewMetadata(NamespaceName, VersionType, "version", resource.VersionUndefined), VersionSpec{}, ) } // VersionExtension is auxiliary resource data for VersionStatus. type VersionExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (VersionExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: VersionType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Version", JSONPath: `{.version}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(VersionType, &Version{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/watchdog_timer_config.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // WatchdogTimerConfigType is type of WatchdogTimerConfig resource. const WatchdogTimerConfigType = resource.Type("WatchdogTimerConfigs.runtime.talos.dev") // WatchdogTimerConfig resource holds configuration for watchdog timer. type WatchdogTimerConfig = typed.Resource[WatchdogTimerConfigSpec, WatchdogTimerConfigExtension] // WatchdogTimerConfigID is a resource ID for WatchdogTimerConfig. const WatchdogTimerConfigID resource.ID = "timer" // WatchdogTimerConfigSpec describes configuration of watchdog timer. // //gotagsrewrite:gen type WatchdogTimerConfigSpec struct { Device string `yaml:"device" protobuf:"1"` Timeout time.Duration `yaml:"timeout" protobuf:"2"` } // NewWatchdogTimerConfig initializes a WatchdogTimerConfig resource. func NewWatchdogTimerConfig() *WatchdogTimerConfig { return typed.NewResource[WatchdogTimerConfigSpec, WatchdogTimerConfigExtension]( resource.NewMetadata(NamespaceName, WatchdogTimerConfigType, WatchdogTimerConfigID, resource.VersionUndefined), WatchdogTimerConfigSpec{}, ) } // WatchdogTimerConfigExtension is auxiliary resource data for WatchdogTimerConfig. type WatchdogTimerConfigExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (WatchdogTimerConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: WatchdogTimerConfigType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Device", JSONPath: `{.device}`, }, { Name: "Timeout", JSONPath: `{.timeout}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[WatchdogTimerConfigSpec](WatchdogTimerConfigType, &WatchdogTimerConfig{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/runtime/watchdog_timer_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package runtime import ( "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // WatchdogTimerStatusType is type of WatchdogTimerStatus resource. const WatchdogTimerStatusType = resource.Type("WatchdogTimerStatuses.runtime.talos.dev") // WatchdogTimerStatus resource holds status of watchdog timer. type WatchdogTimerStatus = typed.Resource[WatchdogTimerStatusSpec, WatchdogTimerStatusExtension] // WatchdogTimerStatusSpec describes configuration of watchdog timer. // //gotagsrewrite:gen type WatchdogTimerStatusSpec struct { Device string `yaml:"device" protobuf:"1"` Timeout time.Duration `yaml:"timeout" protobuf:"2"` FeedInterval time.Duration `yaml:"feedInterval" protobuf:"3"` } // NewWatchdogTimerStatus initializes a WatchdogTimerStatus resource. func NewWatchdogTimerStatus(id string) *WatchdogTimerStatus { return typed.NewResource[WatchdogTimerStatusSpec, WatchdogTimerStatusExtension]( resource.NewMetadata(NamespaceName, WatchdogTimerStatusType, id, resource.VersionUndefined), WatchdogTimerStatusSpec{}, ) } // WatchdogTimerStatusExtension is auxiliary resource data for WatchdogTimerStatus. type WatchdogTimerStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (WatchdogTimerStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: WatchdogTimerStatusType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Device", JSONPath: `{.device}`, }, { Name: "Timeout", JSONPath: `{.timeout}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[WatchdogTimerStatusSpec](WatchdogTimerStatusType, &WatchdogTimerStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/api.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // APIType is type of API resource. const APIType = resource.Type("ApiCertificates.secrets.talos.dev") // APIID is a resource ID of singleton instance. const APIID = resource.ID("api") // API contains apid generated secrets. type API = typed.Resource[APICertsSpec, APIExtension] // APICertsSpec describes etcd certs secrets. // //gotagsrewrite:gen type APICertsSpec struct { AcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs" protobuf:"4"` Client *x509.PEMEncodedCertificateAndKey `yaml:"client" protobuf:"2"` Server *x509.PEMEncodedCertificateAndKey `yaml:"server" protobuf:"3"` // Skip verifying client certificate, to be used only with the maintenance mode operations. SkipVerifyingClientCert bool `yaml:"skipVerifyingClientCert" protobuf:"5"` } // NewAPI initializes an API resource. func NewAPI() *API { return typed.NewResource[APICertsSpec, APIExtension]( resource.NewMetadata(NamespaceName, APIType, APIID, resource.VersionUndefined), APICertsSpec{}, ) } // APIExtension provides auxiliary methods for API. type APIExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (APIExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: APIType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() if err := protobuf.RegisterDynamic[APICertsSpec](APIType, &API{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/api_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestAPIProtobufMarshal(t *testing.T) { r := secrets.NewAPI() r.TypedSpec().AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: []byte("foo"), }, } r.TypedSpec().Client = &x509.PEMEncodedCertificateAndKey{ Crt: []byte("bar"), Key: []byte("baz"), } r.TypedSpec().Server = &x509.PEMEncodedCertificateAndKey{ Crt: []byte("car"), Key: []byte("caz"), } protoR, err := protobuf.FromResource(r) require.NoError(t, err) marshaled, err := protoR.Marshal() require.NoError(t, err) protoR, err = protobuf.Unmarshal(marshaled) require.NoError(t, err) r2, err := protobuf.UnmarshalResource(protoR) require.NoError(t, err) require.True(t, resource.Equal(r, r2)) } ================================================ FILE: pkg/machinery/resources/secrets/cert_sans.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "net" "net/netip" "slices" "strings" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/proto" ) // CertSANType is type of CertSAN resource. const CertSANType = resource.Type("CertSANs.secrets.talos.dev") // CertSANAPIID is a resource ID of singleton instance for the Talos API. const CertSANAPIID = resource.ID("api") // CertSANMaintenanceID is a resource ID of singleton instance for the Talos Maintenance API. const CertSANMaintenanceID = resource.ID("maintenance") // CertSANKubernetesID is a resource ID of singleton instance for the Kubernetes API Server. const CertSANKubernetesID = resource.ID("k8s") // CertSAN contains certficiate subject alternative names. type CertSAN = typed.Resource[CertSANSpec, CertSANExtension] // CertSANSpec describes fields of the cert SANs. // //gotagsrewrite:gen type CertSANSpec struct { IPs []netip.Addr `yaml:"ips" protobuf:"1"` DNSNames []string `yaml:"dnsNames" protobuf:"2"` FQDN string `yaml:"fqdn" protobuf:"3"` } // NewCertSAN initializes a Etc resource. func NewCertSAN(namespace resource.Namespace, id resource.ID) *CertSAN { return typed.NewResource[CertSANSpec, CertSANExtension]( resource.NewMetadata(namespace, CertSANType, id, resource.VersionUndefined), CertSANSpec{}, ) } // CertSANExtension is a resource data of CertSAN. type CertSANExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (CertSANExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: CertSANType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } // Reset the list of SANs. func (spec *CertSANSpec) Reset() { spec.DNSNames = nil spec.IPs = nil spec.FQDN = "" } // Append list of SANs splitting into IPs/DNS names. func (spec *CertSANSpec) Append(sans ...string) { for _, san := range sans { if ip, err := netip.ParseAddr(san); err == nil { spec.AppendIPs(ip) } else { spec.AppendDNSNames(san) } } } // AppendIPs skipping duplicates. func (spec *CertSANSpec) AppendIPs(ips ...netip.Addr) { for _, ip := range ips { found := slices.Contains(spec.IPs, ip) if !found { spec.IPs = append(spec.IPs, ip) } } } // AppendDNSNames skipping duplicates. func (spec *CertSANSpec) AppendDNSNames(dnsNames ...string) { for _, dnsName := range dnsNames { // remove trailing dot from the DNS name, as it shouldn't be stored in the cert SANs dnsName = strings.TrimRight(dnsName, ".") found := slices.Contains(spec.DNSNames, dnsName) if !found && dnsName != "" { spec.DNSNames = append(spec.DNSNames, dnsName) } } } // StdIPs returns a list of converted std.IPs. func (spec *CertSANSpec) StdIPs() []net.IP { return xslices.Map(spec.IPs, func(ip netip.Addr) net.IP { return ip.AsSlice() }) } // Sort the CertSANs. func (spec *CertSANSpec) Sort() { slices.Sort(spec.DNSNames) slices.SortFunc(spec.IPs, func(a, b netip.Addr) int { return a.Compare(b) }) } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[CertSANSpec](CertSANType, &CertSAN{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/condition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "context" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" ) // APIReadyCondition implements condition which waits for the API certs to be ready. type APIReadyCondition struct { state state.State } // NewAPIReadyCondition builds a coondition which waits for the API certs to be ready. func NewAPIReadyCondition(state state.State) *APIReadyCondition { return &APIReadyCondition{ state: state, } } func (condition *APIReadyCondition) String() string { return "api certificates" } // Wait implements condition interface. func (condition *APIReadyCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(NamespaceName, APIType, APIID, resource.VersionUndefined), state.WithCondition(func(r resource.Resource) (bool, error) { if resource.IsTombstone(r) { return false, nil } return true, nil }), ) return err } ================================================ FILE: pkg/machinery/resources/secrets/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type EncryptionSaltSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package secrets import ( "net/netip" "net/url" "github.com/siderolabs/crypto/x509" ) // DeepCopy generates a deep copy of APICertsSpec. func (o APICertsSpec) DeepCopy() APICertsSpec { var cp APICertsSpec = o if o.AcceptedCAs != nil { cp.AcceptedCAs = make([]*x509.PEMEncodedCertificate, len(o.AcceptedCAs)) copy(cp.AcceptedCAs, o.AcceptedCAs) for i2 := range o.AcceptedCAs { if o.AcceptedCAs[i2] != nil { cp.AcceptedCAs[i2] = o.AcceptedCAs[i2].DeepCopy() } } } if o.Client != nil { cp.Client = o.Client.DeepCopy() } if o.Server != nil { cp.Server = o.Server.DeepCopy() } return cp } // DeepCopy generates a deep copy of CertSANSpec. func (o CertSANSpec) DeepCopy() CertSANSpec { var cp CertSANSpec = o if o.IPs != nil { cp.IPs = make([]netip.Addr, len(o.IPs)) copy(cp.IPs, o.IPs) } if o.DNSNames != nil { cp.DNSNames = make([]string, len(o.DNSNames)) copy(cp.DNSNames, o.DNSNames) } return cp } // DeepCopy generates a deep copy of EtcdCertsSpec. func (o EtcdCertsSpec) DeepCopy() EtcdCertsSpec { var cp EtcdCertsSpec = o if o.Etcd != nil { cp.Etcd = o.Etcd.DeepCopy() } if o.EtcdPeer != nil { cp.EtcdPeer = o.EtcdPeer.DeepCopy() } if o.EtcdAdmin != nil { cp.EtcdAdmin = o.EtcdAdmin.DeepCopy() } if o.EtcdAPIServer != nil { cp.EtcdAPIServer = o.EtcdAPIServer.DeepCopy() } return cp } // DeepCopy generates a deep copy of EtcdRootSpec. func (o EtcdRootSpec) DeepCopy() EtcdRootSpec { var cp EtcdRootSpec = o if o.EtcdCA != nil { cp.EtcdCA = o.EtcdCA.DeepCopy() } return cp } // DeepCopy generates a deep copy of EncryptionSaltSpec. func (o EncryptionSaltSpec) DeepCopy() EncryptionSaltSpec { var cp EncryptionSaltSpec = o if o.DiskSalt != nil { cp.DiskSalt = make([]byte, len(o.DiskSalt)) copy(cp.DiskSalt, o.DiskSalt) } return cp } // DeepCopy generates a deep copy of KubeletSpec. func (o KubeletSpec) DeepCopy() KubeletSpec { var cp KubeletSpec = o if o.Endpoint != nil { cp.Endpoint = new(url.URL) *cp.Endpoint = *o.Endpoint if o.Endpoint.User != nil { cp.Endpoint.User = new(url.Userinfo) *cp.Endpoint.User = *o.Endpoint.User } } if o.AcceptedCAs != nil { cp.AcceptedCAs = make([]*x509.PEMEncodedCertificate, len(o.AcceptedCAs)) copy(cp.AcceptedCAs, o.AcceptedCAs) for i2 := range o.AcceptedCAs { if o.AcceptedCAs[i2] != nil { cp.AcceptedCAs[i2] = o.AcceptedCAs[i2].DeepCopy() } } } return cp } // DeepCopy generates a deep copy of KubernetesCertsSpec. func (o KubernetesCertsSpec) DeepCopy() KubernetesCertsSpec { var cp KubernetesCertsSpec = o return cp } // DeepCopy generates a deep copy of KubernetesDynamicCertsSpec. func (o KubernetesDynamicCertsSpec) DeepCopy() KubernetesDynamicCertsSpec { var cp KubernetesDynamicCertsSpec = o if o.APIServer != nil { cp.APIServer = o.APIServer.DeepCopy() } if o.APIServerKubeletClient != nil { cp.APIServerKubeletClient = o.APIServerKubeletClient.DeepCopy() } if o.FrontProxy != nil { cp.FrontProxy = o.FrontProxy.DeepCopy() } return cp } // DeepCopy generates a deep copy of KubernetesRootSpec. func (o KubernetesRootSpec) DeepCopy() KubernetesRootSpec { var cp KubernetesRootSpec = o if o.Endpoint != nil { cp.Endpoint = new(url.URL) *cp.Endpoint = *o.Endpoint if o.Endpoint.User != nil { cp.Endpoint.User = new(url.Userinfo) *cp.Endpoint.User = *o.Endpoint.User } } if o.LocalEndpoint != nil { cp.LocalEndpoint = new(url.URL) *cp.LocalEndpoint = *o.LocalEndpoint if o.LocalEndpoint.User != nil { cp.LocalEndpoint.User = new(url.Userinfo) *cp.LocalEndpoint.User = *o.LocalEndpoint.User } } if o.CertSANs != nil { cp.CertSANs = make([]string, len(o.CertSANs)) copy(cp.CertSANs, o.CertSANs) } if o.APIServerIPs != nil { cp.APIServerIPs = make([]netip.Addr, len(o.APIServerIPs)) copy(cp.APIServerIPs, o.APIServerIPs) } if o.IssuingCA != nil { cp.IssuingCA = o.IssuingCA.DeepCopy() } if o.AcceptedCAs != nil { cp.AcceptedCAs = make([]*x509.PEMEncodedCertificate, len(o.AcceptedCAs)) copy(cp.AcceptedCAs, o.AcceptedCAs) for i2 := range o.AcceptedCAs { if o.AcceptedCAs[i2] != nil { cp.AcceptedCAs[i2] = o.AcceptedCAs[i2].DeepCopy() } } } if o.ServiceAccount != nil { cp.ServiceAccount = o.ServiceAccount.DeepCopy() } if o.AggregatorCA != nil { cp.AggregatorCA = o.AggregatorCA.DeepCopy() } return cp } // DeepCopy generates a deep copy of MaintenanceRootSpec. func (o MaintenanceRootSpec) DeepCopy() MaintenanceRootSpec { var cp MaintenanceRootSpec = o if o.CA != nil { cp.CA = o.CA.DeepCopy() } return cp } // DeepCopy generates a deep copy of OSRootSpec. func (o OSRootSpec) DeepCopy() OSRootSpec { var cp OSRootSpec = o if o.IssuingCA != nil { cp.IssuingCA = o.IssuingCA.DeepCopy() } if o.AcceptedCAs != nil { cp.AcceptedCAs = make([]*x509.PEMEncodedCertificate, len(o.AcceptedCAs)) copy(cp.AcceptedCAs, o.AcceptedCAs) for i2 := range o.AcceptedCAs { if o.AcceptedCAs[i2] != nil { cp.AcceptedCAs[i2] = o.AcceptedCAs[i2].DeepCopy() } } } if o.CertSANIPs != nil { cp.CertSANIPs = make([]netip.Addr, len(o.CertSANIPs)) copy(cp.CertSANIPs, o.CertSANIPs) } if o.CertSANDNSNames != nil { cp.CertSANDNSNames = make([]string, len(o.CertSANDNSNames)) copy(cp.CertSANDNSNames, o.CertSANDNSNames) } return cp } // DeepCopy generates a deep copy of TrustdCertsSpec. func (o TrustdCertsSpec) DeepCopy() TrustdCertsSpec { var cp TrustdCertsSpec = o if o.AcceptedCAs != nil { cp.AcceptedCAs = make([]*x509.PEMEncodedCertificate, len(o.AcceptedCAs)) copy(cp.AcceptedCAs, o.AcceptedCAs) for i2 := range o.AcceptedCAs { if o.AcceptedCAs[i2] != nil { cp.AcceptedCAs[i2] = o.AcceptedCAs[i2].DeepCopy() } } } if o.Server != nil { cp.Server = o.Server.DeepCopy() } return cp } ================================================ FILE: pkg/machinery/resources/secrets/encryption_salt.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EncryptionSaltType is type of EncryptionSalt resource. const EncryptionSaltType = resource.Type("EncryptionSalts.secrets.talos.dev") // EncryptionSaltID is a resource ID of singleton instance. const EncryptionSaltID = resource.ID("salt") // EncryptionSalt contains salt data used to mix in into disk encryption keys. type EncryptionSalt = typed.Resource[EncryptionSaltSpec, EncryptionSaltExtension] // EncryptionSaltSpec describes the salt. // //gotagsrewrite:gen type EncryptionSaltSpec struct { DiskSalt []byte `yaml:"diskSalt" protobuf:"1"` } // NewEncryptionSalt initializes a EncryptionSalt resource. func NewEncryptionSalt() *EncryptionSalt { return typed.NewResource[EncryptionSaltSpec, EncryptionSaltExtension]( resource.NewMetadata(NamespaceName, EncryptionSaltType, EncryptionSaltID, resource.VersionUndefined), EncryptionSaltSpec{}, ) } // EncryptionSaltExtension provides auxiliary methods for EncryptionSalt. type EncryptionSaltExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EncryptionSaltExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EncryptionSaltType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() if err := protobuf.RegisterDynamic[EncryptionSaltSpec](EncryptionSaltType, &EncryptionSalt{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/etcd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EtcdType is type of Etcd resource. const EtcdType = resource.Type("EtcdSecrets.secrets.talos.dev") // EtcdID is a resource ID of singleton instance. const EtcdID = resource.ID("etcd") // Etcd contains etcd generated secrets. type Etcd = typed.Resource[EtcdCertsSpec, EtcdExtension] // EtcdCertsSpec describes etcd certs secrets. // //gotagsrewrite:gen type EtcdCertsSpec struct { Etcd *x509.PEMEncodedCertificateAndKey `yaml:"etcd" protobuf:"1"` EtcdPeer *x509.PEMEncodedCertificateAndKey `yaml:"etcdPeer" protobuf:"2"` EtcdAdmin *x509.PEMEncodedCertificateAndKey `yaml:"etcdAdmin" protobuf:"3"` EtcdAPIServer *x509.PEMEncodedCertificateAndKey `yaml:"etcdAPIServer" protobuf:"4"` } // NewEtcd initializes a Etc resource. func NewEtcd() *Etcd { return typed.NewResource[EtcdCertsSpec, EtcdExtension]( resource.NewMetadata(NamespaceName, EtcdType, EtcdID, resource.VersionUndefined), EtcdCertsSpec{}, ) } // EtcdExtension provides auxiliary methods for Etcd. type EtcdExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EtcdExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EtcdType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EtcdCertsSpec](EtcdType, &Etcd{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/etcd_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // EtcdRootType is type of EtcdRoot secret resource. const EtcdRootType = resource.Type("EtcdRootSecrets.secrets.talos.dev") // EtcdRootID is the IDs of EtcdRoot. const EtcdRootID = resource.ID("etcd") // EtcdRoot contains root (not generated) secrets. type EtcdRoot = typed.Resource[EtcdRootSpec, EtcdRootExtension] // EtcdRootSpec describes etcd CA secrets. // //gotagsrewrite:gen type EtcdRootSpec struct { EtcdCA *x509.PEMEncodedCertificateAndKey `yaml:"etcdCA" protobuf:"1"` } // NewEtcdRoot initializes a EtcdRoot resource. func NewEtcdRoot(id resource.ID) *EtcdRoot { return typed.NewResource[EtcdRootSpec, EtcdRootExtension]( resource.NewMetadata(NamespaceName, EtcdRootType, id, resource.VersionUndefined), EtcdRootSpec{}, ) } // EtcdRootExtension provides auxiliary methods for EtcdRoot. type EtcdRootExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (EtcdRootExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: EtcdRootType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[EtcdRootSpec](EtcdRootType, &EtcdRoot{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/kubelet.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "net/url" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubeletType is type of Kubelet secret resource. const KubeletType = resource.Type("KubeletSecrets.secrets.talos.dev") // KubeletID is the ID of KubeletType resource. const KubeletID = resource.ID("kubelet") // Kubelet contains root (not generated) secrets. type Kubelet = typed.Resource[KubeletSpec, KubeletExtension] // KubeletSpec describes root Kubernetes secrets. // //gotagsrewrite:gen type KubeletSpec struct { Endpoint *url.URL `yaml:"endpoint" protobuf:"1"` AcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs" protobuf:"5"` BootstrapTokenID string `yaml:"bootstrapTokenID" protobuf:"3"` BootstrapTokenSecret string `yaml:"bootstrapTokenSecret" protobuf:"4"` } // NewKubelet initializes a Kubelet resource. func NewKubelet(id resource.ID) *Kubelet { return typed.NewResource[KubeletSpec, KubeletExtension]( resource.NewMetadata(NamespaceName, KubeletType, id, resource.VersionUndefined), KubeletSpec{}, ) } // KubeletExtension provides auxiliary methods for Kubelet. type KubeletExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KubeletExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubeletType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubeletSpec](KubeletType, &Kubelet{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubernetesType is type of Kubernetes resource. const KubernetesType = resource.Type("KubernetesSecrets.secrets.talos.dev") // KubernetesID is a resource ID of singleton instance. const KubernetesID = resource.ID("k8s-certs") // Kubernetes contains K8s generated secrets. // // Kubernetes resource contains secrets which require reload of the control plane pods if updated. type Kubernetes = typed.Resource[KubernetesCertsSpec, KubernetesExtension] // KubernetesCertsSpec describes generated Kubernetes certificates. // //gotagsrewrite:gen type KubernetesCertsSpec struct { SchedulerKubeconfig string `yaml:"schedulerKubeconfig" protobuf:"4"` ControllerManagerKubeconfig string `yaml:"controllerManagerKubeconfig" protobuf:"5"` // Admin-level kubeconfig with access through the localhost endpoint and cluster endpoints. LocalhostAdminKubeconfig string `yaml:"localhostAdminKubeconfig" protobuf:"6"` AdminKubeconfig string `yaml:"adminKubeconfig" protobuf:"7"` } // NewKubernetes initializes a Kubernetes resource. func NewKubernetes() *Kubernetes { return typed.NewResource[KubernetesCertsSpec, KubernetesExtension]( resource.NewMetadata(NamespaceName, KubernetesType, KubernetesID, resource.VersionUndefined), KubernetesCertsSpec{}, ) } // KubernetesExtension provides auxiliary methods for Kubernetes. type KubernetesExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KubernetesExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubernetesType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubernetesCertsSpec](KubernetesType, &Kubernetes{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/kubernetes_certs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubernetesDynamicCertsType is type of KubernetesCerts resource. const KubernetesDynamicCertsType = resource.Type("KubernetesDynamicCerts.secrets.talos.dev") // KubernetesDynamicCertsID is a resource ID of singleton instance. const KubernetesDynamicCertsID = resource.ID("k8s-dynamic-certs") // KubernetesDynamicCerts contains K8s generated secrets. // // KubernetesDynamicCerts resource contains secrets which do not require reload when updated. type KubernetesDynamicCerts = typed.Resource[KubernetesDynamicCertsSpec, KubernetesDynamicCertsExtension] // KubernetesDynamicCertsSpec describes generated KubernetesCerts certificates. // //gotagsrewrite:gen type KubernetesDynamicCertsSpec struct { APIServer *x509.PEMEncodedCertificateAndKey `yaml:"apiServer" protobuf:"1"` APIServerKubeletClient *x509.PEMEncodedCertificateAndKey `yaml:"apiServerKubeletClient" protobuf:"2"` FrontProxy *x509.PEMEncodedCertificateAndKey `yaml:"frontProxy" protobuf:"3"` } // NewKubernetesDynamicCerts initializes a KubernetesCerts resource. func NewKubernetesDynamicCerts() *KubernetesDynamicCerts { return typed.NewResource[KubernetesDynamicCertsSpec, KubernetesDynamicCertsExtension]( resource.NewMetadata(NamespaceName, KubernetesDynamicCertsType, KubernetesDynamicCertsID, resource.VersionUndefined), KubernetesDynamicCertsSpec{}, ) } // KubernetesDynamicCertsExtension provides auxiliary methods for KubernetesCerts. type KubernetesDynamicCertsExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KubernetesDynamicCertsExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubernetesDynamicCertsType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubernetesDynamicCertsSpec](KubernetesDynamicCertsType, &KubernetesDynamicCerts{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/kubernetes_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "net/netip" "net/url" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // KubernetesRootType is type of KubernetesRoot secret resource. const KubernetesRootType = resource.Type("KubernetesRootSecrets.secrets.talos.dev") // KubernetesRootID is the ID of KubernetesRootType resource. const KubernetesRootID = resource.ID("k8s") // KubernetesRoot contains root (not generated) secrets. type KubernetesRoot = typed.Resource[KubernetesRootSpec, KubernetesRootExtension] // KubernetesRootSpec describes root Kubernetes secrets. // //gotagsrewrite:gen type KubernetesRootSpec struct { Name string `yaml:"name" protobuf:"1"` Endpoint *url.URL `yaml:"endpoint" protobuf:"2"` LocalEndpoint *url.URL `yaml:"local_endpoint" protobuf:"3"` CertSANs []string `yaml:"certSANs" protobuf:"4"` APIServerIPs []netip.Addr `yaml:"apiServerIPs" protobuf:"14"` DNSDomain string `yaml:"dnsDomain" protobuf:"6"` IssuingCA *x509.PEMEncodedCertificateAndKey `yaml:"issuingCA" protobuf:"7"` AcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs" protobuf:"15"` ServiceAccount *x509.PEMEncodedKey `yaml:"serviceAccount" protobuf:"8"` AggregatorCA *x509.PEMEncodedCertificateAndKey `yaml:"aggregatorCA" protobuf:"9"` AESCBCEncryptionSecret string `yaml:"aesCBCEncryptionSecret" protobuf:"10"` BootstrapTokenID string `yaml:"bootstrapTokenID" protobuf:"11"` BootstrapTokenSecret string `yaml:"bootstrapTokenSecret" protobuf:"12"` SecretboxEncryptionSecret string `yaml:"secretboxEncryptionSecret" protobuf:"13"` } // NewKubernetesRoot initializes a KubernetesRoot resource. func NewKubernetesRoot(id resource.ID) *KubernetesRoot { return typed.NewResource[KubernetesRootSpec, KubernetesRootExtension]( resource.NewMetadata(NamespaceName, KubernetesRootType, id, resource.VersionUndefined), KubernetesRootSpec{}, ) } // KubernetesRootExtension provides auxiliary methods for KubernetesRoot. type KubernetesRootExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (KubernetesRootExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: KubernetesRootType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[KubernetesRootSpec](KubernetesRootType, &KubernetesRoot{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/maintenance_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // MaintenanceRootType is type of MaintenanceRoot secret resource. const MaintenanceRootType = resource.Type("MaintenanceRootSecrets.secrets.talos.dev") // MaintenanceRootID is the Resource ID for MaintenanceRoot. const MaintenanceRootID = resource.ID("maintenance") // MaintenanceRoot contains root secrets for the maintenance service. type MaintenanceRoot = typed.Resource[MaintenanceRootSpec, MaintenanceRootExtension] // MaintenanceRootSpec describes maintenance service CA. // //gotagsrewrite:gen type MaintenanceRootSpec struct { CA *x509.PEMEncodedCertificateAndKey `yaml:"ca" protobuf:"1"` } // NewMaintenanceRoot initializes a MaintenanceRoot resource. func NewMaintenanceRoot(id resource.ID) *MaintenanceRoot { return typed.NewResource[MaintenanceRootSpec, MaintenanceRootExtension]( resource.NewMetadata(NamespaceName, MaintenanceRootType, id, resource.VersionUndefined), MaintenanceRootSpec{}, ) } // MaintenanceRootExtension provides auxiliary methods for MaintenanceRoot. type MaintenanceRootExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (MaintenanceRootExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: MaintenanceRootType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() if err := protobuf.RegisterDynamic[MaintenanceRootSpec](MaintenanceRootType, &MaintenanceRoot{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/os_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // OSRootType is type of OSRoot secret resource. const OSRootType = resource.Type("OSRootSecrets.secrets.talos.dev") // OSRootID is the Resource ID for OSRoot. const OSRootID = resource.ID("os") // OSRoot contains root (not generated) secrets. type OSRoot = typed.Resource[OSRootSpec, OSRootExtension] // OSRootSpec describes operating system CA. // //gotagsrewrite:gen type OSRootSpec struct { IssuingCA *x509.PEMEncodedCertificateAndKey `yaml:"issuingCA" protobuf:"1"` AcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs" protobuf:"5"` CertSANIPs []netip.Addr `yaml:"certSANIPs" protobuf:"2"` CertSANDNSNames []string `yaml:"certSANDNSNames" protobuf:"3"` Token string `yaml:"token" protobuf:"4"` } // NewOSRoot initializes a OSRoot resource. func NewOSRoot(id resource.ID) *OSRoot { return typed.NewResource[OSRootSpec, OSRootExtension]( resource.NewMetadata(NamespaceName, OSRootType, id, resource.VersionUndefined), OSRootSpec{}, ) } // OSRootExtension provides auxiliary methods for OSRoot. type OSRootExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (OSRootExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: OSRootType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() if err := protobuf.RegisterDynamic[OSRootSpec](OSRootType, &OSRoot{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/secrets.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package secrets provides resources which store secrets. package secrets import "github.com/cosi-project/runtime/pkg/resource" // NamespaceName contains resources containing secret material. const NamespaceName resource.Namespace = "secrets" //go:generate go tool github.com/siderolabs/deep-copy -type APICertsSpec -type CertSANSpec -type EtcdCertsSpec -type EtcdRootSpec -type EncryptionSaltSpec -type KubeletSpec -type KubernetesCertsSpec -type KubernetesDynamicCertsSpec -type KubernetesRootSpec -type MaintenanceRootSpec -type OSRootSpec -type TrustdCertsSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/resources/secrets/secrets_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &secrets.API{}, &secrets.CertSAN{}, &secrets.EncryptionSalt{}, &secrets.Etcd{}, &secrets.EtcdRoot{}, &secrets.Kubelet{}, &secrets.Kubernetes{}, &secrets.KubernetesDynamicCerts{}, &secrets.KubernetesRoot{}, &secrets.MaintenanceRoot{}, &secrets.OSRoot{}, &secrets.Trustd{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/secrets/trustd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/talos/pkg/machinery/proto" ) // TrustdType is type of Trustd resource. const TrustdType = resource.Type("TrustdCertificates.secrets.talos.dev") // TrustdID is a resource ID of singleton instance. const TrustdID = resource.ID("trustd") // Trustd contains trustd generated secrets. type Trustd = typed.Resource[TrustdCertsSpec, TrustdExtension] // TrustdCertsSpec describes etcd certs secrets. // //gotagsrewrite:gen type TrustdCertsSpec struct { AcceptedCAs []*x509.PEMEncodedCertificate `yaml:"acceptedCAs" protobuf:"3"` Server *x509.PEMEncodedCertificateAndKey `yaml:"server" protobuf:"2"` } // NewTrustd initializes a Trustd resource. func NewTrustd() *Trustd { return typed.NewResource[TrustdCertsSpec, TrustdExtension]( resource.NewMetadata(NamespaceName, TrustdType, TrustdID, resource.VersionUndefined), TrustdCertsSpec{}, ) } // TrustdExtension provides auxiliary methods for Trustd. type TrustdExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (TrustdExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: TrustdType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() if err := protobuf.RegisterDynamic[TrustdCertsSpec](TrustdType, &Trustd{}); err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/secrets/trustd_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package secrets_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/siderolabs/crypto/x509" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/resources/secrets" ) func TestTrustdProtobufMarshal(t *testing.T) { r := secrets.NewTrustd() r.TypedSpec().AcceptedCAs = []*x509.PEMEncodedCertificate{ { Crt: []byte("foo"), }, } r.TypedSpec().Server = &x509.PEMEncodedCertificateAndKey{ Crt: []byte("car"), Key: []byte("caz"), } protoR, err := protobuf.FromResource(r) require.NoError(t, err) marshaled, err := protoR.Marshal() require.NoError(t, err) protoR, err = protobuf.Unmarshal(marshaled) require.NoError(t, err) r2, err := protobuf.UnmarshalResource(protoR) require.NoError(t, err) require.True(t, resource.Equal(r, r2)) } ================================================ FILE: pkg/machinery/resources/security/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ImageVerificationRuleSpec -type TUFTrustedRootSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package security // DeepCopy generates a deep copy of ImageVerificationRuleSpec. func (o ImageVerificationRuleSpec) DeepCopy() ImageVerificationRuleSpec { var cp ImageVerificationRuleSpec = o if o.KeylessVerifier != nil { cp.KeylessVerifier = new(ImageKeylessVerifierSpec) *cp.KeylessVerifier = *o.KeylessVerifier } if o.PublicKeyVerifier != nil { cp.PublicKeyVerifier = new(ImagePublicKeyVerifierSpec) *cp.PublicKeyVerifier = *o.PublicKeyVerifier } return cp } // DeepCopy generates a deep copy of TUFTrustedRootSpec. func (o TUFTrustedRootSpec) DeepCopy() TUFTrustedRootSpec { var cp TUFTrustedRootSpec = o return cp } ================================================ FILE: pkg/machinery/resources/security/image_verification_rule.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package security import ( "context" "fmt" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/ryanuber/go-glob" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ImageVerificationRuleType is type of ImageVerificationRule resource. const ImageVerificationRuleType = resource.Type("ImageVerificationRules.security.talos.dev") // ImageVerificationRule represents ImageVerificationRule typed resource. type ImageVerificationRule = typed.Resource[ImageVerificationRuleSpec, ImageVerificationRuleExtension] // ImageVerificationRuleSpec represents a verification rule. // //gotagsrewrite:gen type ImageVerificationRuleSpec struct { // ImagePattern is the image name pattern. ImagePattern string `yaml:"imagePattern,omitempty" protobuf:"2"` // Skip is the action for matching images. Skip bool `yaml:"skip" protobuf:"3"` // Deny is the action for matching images. Deny bool `yaml:"deny" protobuf:"4"` // KeylessVerifier is the keyless verifier configuration to use. KeylessVerifier *ImageKeylessVerifierSpec `yaml:"keylessVerifier,omitempty" protobuf:"5"` // PublicKeyVerifier is the public key verifier configuration to use. PublicKeyVerifier *ImagePublicKeyVerifierSpec `yaml:"publicKeyVerifier,omitempty" protobuf:"6"` } // ImageKeylessVerifierSpec represents a signature verification provider. // //gotagsrewrite:gen type ImageKeylessVerifierSpec struct { // Issuer is the OIDC issuer URL. Issuer string `yaml:"issuer" protobuf:"1"` // Subject is the expected subject. Subject string `yaml:"subject,omitempty" protobuf:"2"` // SubjectRegex is a regex pattern for subject matching. SubjectRegex string `yaml:"subjectRegex,omitempty" protobuf:"3"` } // ImagePublicKeyVerifierSpec represents a signature verification provider with static public key. // //gotagsrewrite:gen type ImagePublicKeyVerifierSpec struct { // Certificate is a public certificate in PEM format accepted for image signature verification. Certificate string `yaml:"certificate" protobuf:"1"` } // NewImageVerificationRule creates new ImageVerificationRule object. func NewImageVerificationRule(id resource.ID) *ImageVerificationRule { return typed.NewResource[ImageVerificationRuleSpec, ImageVerificationRuleExtension]( resource.NewMetadata(NamespaceName, ImageVerificationRuleType, id, resource.VersionUndefined), ImageVerificationRuleSpec{}, ) } // ImageVerificationRuleExtension is an auxiliary type for ImageVerificationRule resource. type ImageVerificationRuleExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ImageVerificationRuleExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ImageVerificationRuleType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Pattern", JSONPath: "{.imagePattern}", }, { Name: "Skip", JSONPath: "{.skip}", }, { Name: "Deny", JSONPath: "{.deny}", }, }, } } // ImageVerificationRuleMatchFunc is a function type for matching image references to verification rules. type ImageVerificationRuleMatchFunc func(imageRef string) *ImageVerificationRule // ImageVerificationRuleMatcher creates the matcher for the given image reference against the provided rules and returns the first matching rule. func ImageVerificationRuleMatcher(ctx context.Context, st state.State) (ImageVerificationRuleMatchFunc, error) { rules, err := safe.StateListAll[*ImageVerificationRule](ctx, st) if err != nil { return nil, fmt.Errorf("failed to list image verification rules: %w", err) } return func(imageRef string) *ImageVerificationRule { for rule := range rules.All() { if rule.TypedSpec().ImagePattern != "" && glob.Glob(rule.TypedSpec().ImagePattern, imageRef) { return rule } } return nil }, nil } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(ImageVerificationRuleType, &ImageVerificationRule{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/security/security.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package security import "github.com/cosi-project/runtime/pkg/resource" //go:generate go tool github.com/siderolabs/deep-copy -type ImageVerificationRuleSpec -type TUFTrustedRootSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName is the namespace for security resources. const NamespaceName resource.Namespace = "security" ================================================ FILE: pkg/machinery/resources/security/security_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package security_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/security" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &security.ImageVerificationRule{}, &security.TUFTrustedRoot{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/security/tuf_trusted_root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package security import ( "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // TUFTrustedRootType is type of TUFTrustedRoot resource. const TUFTrustedRootType = resource.Type("TUFTrustedRoots.security.talos.dev") // TUFTrustedRoot represents TUFTrustedRoot typed resource. type TUFTrustedRoot = typed.Resource[TUFTrustedRootSpec, TUFTrustedRootExtension] // TrustedRootID is the ID for the TUF trusted root resource. const TrustedRootID = resource.ID("trusted_root.json") // TUFTrustedRootSpec represents a sigstore's TUF trusted root information. // //gotagsrewrite:gen type TUFTrustedRootSpec struct { // LastRefreshTime is the last time the trusted root was refreshed. LastRefreshTime time.Time `yaml:"lastRefreshTime,omitempty" protobuf:"1"` // JSONData is the trusted root data in JSON format. JSONData string `yaml:"jsonData,omitempty" protobuf:"2"` } // NewTUFTrustedRoot creates new TUFTrustedRoot object. func NewTUFTrustedRoot(id resource.ID) *TUFTrustedRoot { return typed.NewResource[TUFTrustedRootSpec, TUFTrustedRootExtension]( resource.NewMetadata(NamespaceName, TUFTrustedRootType, id, resource.VersionUndefined), TUFTrustedRootSpec{}, ) } // TUFTrustedRootExtension is an auxiliary type for TUFTrustedRoot resource. type TUFTrustedRootExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (TUFTrustedRootExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: TUFTrustedRootType, Aliases: []resource.Type{}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Last Refresh", JSONPath: "{.lastRefreshTime}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic(TUFTrustedRootType, &TUFTrustedRoot{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/siderolink/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type ConfigSpec -type StatusSpec -type TunnelSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package siderolink // DeepCopy generates a deep copy of ConfigSpec. func (o ConfigSpec) DeepCopy() ConfigSpec { var cp ConfigSpec = o return cp } // DeepCopy generates a deep copy of StatusSpec. func (o StatusSpec) DeepCopy() StatusSpec { var cp StatusSpec = o return cp } // DeepCopy generates a deep copy of TunnelSpec. func (o TunnelSpec) DeepCopy() TunnelSpec { var cp TunnelSpec = o return cp } ================================================ FILE: pkg/machinery/resources/siderolink/siderolink.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package siderolink contains SideroLink-related resources. package siderolink import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) //go:generate go tool github.com/siderolabs/deep-copy -type ConfigSpec -type StatusSpec -type TunnelSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // ConfigType is type of Config resource. const ConfigType = resource.Type("SiderolinkConfigs.siderolink.talos.dev") // ConfigID the singleton config resource ID. const ConfigID = resource.ID("siderolink") // Config resource holds Siderolink configuration. type Config = typed.Resource[ConfigSpec, ConfigExtension] // ConfigSpec describes Siderolink configuration. // //gotagsrewrite:gen type ConfigSpec struct { APIEndpoint string `yaml:"apiEndpoint" protobuf:"1"` Host string `yaml:"host" protobuf:"2"` JoinToken string `yaml:"joinToken" protobuf:"3"` Insecure bool `yaml:"insecure" protobuf:"4"` Tunnel bool `yaml:"tunnel" protobuf:"5"` } // NewConfig initializes a Config resource. func NewConfig(namespace resource.Namespace, id resource.ID) *Config { return typed.NewResource[ConfigSpec, ConfigExtension]( resource.NewMetadata(namespace, ConfigType, id, resource.VersionUndefined), ConfigSpec{}, ) } // ConfigExtension provides auxiliary methods for Config. type ConfigExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (ConfigExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ConfigType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "API Endpoint", JSONPath: `{.apiEndpoint}`, }, { Name: "Tunnel", JSONPath: `{.tunnel}`, }, }, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ConfigSpec](ConfigType, &Config{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/siderolink/siderolink_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // StatusType is the type of Status resource. const StatusType = resource.Type("SiderolinkStatuses.siderolink.talos.dev") // StatusID the singleton status resource ID. const StatusID = resource.ID("siderolink-status") // Status resource holds Siderolink status. type Status = typed.Resource[StatusSpec, StatusExtension] // StatusSpec describes Siderolink status. // //gotagsrewrite:gen type StatusSpec struct { // Host is the Siderolink target host. Host string `yaml:"host" protobuf:"1"` // Connected is the status of the Siderolink GRPC connection. Connected bool `yaml:"connected" protobuf:"2"` // LinkName is the name of the interface used for the Siderolink tunnel. LinkName string `yaml:"linkName" protobuf:"3"` // GRPCTunnel is true if the Wireguard-over-GRPC tunnel is being used. GRPCTunnel bool `yaml:"grpcTunnel" protobuf:"4"` } // NewStatus initializes a Status resource. func NewStatus() *Status { return typed.NewResource[StatusSpec, StatusExtension]( resource.NewMetadata(config.NamespaceName, StatusType, StatusID, resource.VersionUndefined), StatusSpec{}, ) } // StatusExtension provides auxiliary methods for Status. type StatusExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (StatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StatusType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Host", JSONPath: `{.host}`, }, { Name: "Connected", JSONPath: `{.connected}`, }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StatusSpec](StatusType, &Status{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/siderolink/siderolink_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/siderolink" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &siderolink.Config{}, &siderolink.Status{}, &siderolink.Tunnel{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/siderolink/siderolink_tunnel.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package siderolink import ( "net/netip" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/config" ) // TunnelType is type of Tunnel resource. const TunnelType = resource.Type("SiderolinkTunnels.siderolink.talos.dev") // TunnelID the singleton tunnel resource ID. const TunnelID = resource.ID("siderolink-tunnel") // Tunnel resource holds Siderolink GRPC Tunnel configuration. type Tunnel = typed.Resource[TunnelSpec, TunnelExtension] // TunnelSpec describes Siderolink GRPC Tunnel configuration. // //gotagsrewrite:gen type TunnelSpec struct { // APIEndpoint is the Siderolink WireGuard over GRPC endpoint. APIEndpoint string `yaml:"apiEndpoint" protobuf:"1"` // LinkName is the name to use for WireGuard tunnel. LinkName string `yaml:"linkName" protobuf:"2"` // MTU is the maximum transmission unit for the tunnel. MTU int `yaml:"mtu" protobuf:"3"` // NodeAddress is the virtual address of our node. It's used to identify our node in the WireGuard GRPC streamer. // It's not the address of the actual WireGuard interface. NodeAddress netip.AddrPort `yaml:"nodeAddress" protobuf:"4"` } // NewTunnel initializes a Config resource. func NewTunnel() *Tunnel { return typed.NewResource[TunnelSpec, TunnelExtension]( resource.NewMetadata(config.NamespaceName, TunnelType, TunnelID, resource.VersionUndefined), TunnelSpec{}, ) } // TunnelExtension provides auxiliary methods for Tunnel. type TunnelExtension struct{} // ResourceDefinition implements [typed.Extension] interface. func (TunnelExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: TunnelType, Aliases: []resource.Type{}, DefaultNamespace: config.NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "API Endpoint", JSONPath: `{.apiEndpoint}`, }, { Name: "Link name", JSONPath: `{.linkName}`, }, { Name: "MTU", JSONPath: `{.mtu}`, }, { Name: "Node address", JSONPath: `{.nodeAddress}`, }, }, Sensitivity: meta.Sensitive, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[TunnelSpec](TunnelType, &Tunnel{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/time/adjtime_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time import ( "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // AdjtimeStatusType is type of AdjtimeStatus resource. const AdjtimeStatusType = resource.Type("AdjtimeStatuses.v1alpha1.talos.dev") // AdjtimeStatusID is the ID of the singletone resource. const AdjtimeStatusID = resource.ID("node") // AdjtimeStatus describes running current time sync AdjtimeStatus. type AdjtimeStatus = typed.Resource[AdjtimeStatusSpec, AdjtimeStatusExtension] // AdjtimeStatusSpec describes Linux internal adjtime state. // //gotagsrewrite:gen type AdjtimeStatusSpec struct { Offset time.Duration `yaml:"offset" protobuf:"1"` FrequencyAdjustmentRatio float64 `yaml:"frequencyAdjustmentRatio" protobuf:"2"` MaxError time.Duration `yaml:"maxError" protobuf:"3"` EstError time.Duration `yaml:"estError" protobuf:"4"` Status string `yaml:"status" protobuf:"5"` Constant int `yaml:"constant" protobuf:"6"` SyncStatus bool `yaml:"syncStatus" protobuf:"7"` State string `yaml:"state" protobuf:"8"` } // NewAdjtimeStatus initializes a TimeSync resource. func NewAdjtimeStatus() *AdjtimeStatus { return typed.NewResource[AdjtimeStatusSpec, AdjtimeStatusExtension]( resource.NewMetadata(v1alpha1.NamespaceName, AdjtimeStatusType, AdjtimeStatusID, resource.VersionUndefined), AdjtimeStatusSpec{}, ) } // AdjtimeStatusExtension provides auxiliary methods for AdjtimeStatus. type AdjtimeStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AdjtimeStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AdjtimeStatusType, Aliases: []resource.Type{}, DefaultNamespace: v1alpha1.NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Offset", JSONPath: "{.offset}", }, { Name: "EstError", JSONPath: "{.estError}", }, { Name: "MaxError", JSONPath: "{.maxError}", }, { Name: "Status", JSONPath: "{.status}", }, { Name: "Sync", JSONPath: "{.syncStatus}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AdjtimeStatusSpec](AdjtimeStatusType, &AdjtimeStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/time/condition.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time import ( "context" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // SyncCondition implements condition which waits for the time to be in sync. type SyncCondition struct { state state.State } // NewSyncCondition builds a coondition which waits for the time to be in sync. func NewSyncCondition(state state.State) *SyncCondition { return &SyncCondition{ state: state, } } func (condition *SyncCondition) String() string { return "time sync" } // Wait implements condition interface. func (condition *SyncCondition) Wait(ctx context.Context) error { _, err := condition.state.WatchFor( ctx, resource.NewMetadata(v1alpha1.NamespaceName, StatusType, StatusID, resource.VersionUndefined), state.WithEventTypes(state.Created, state.Updated), state.WithCondition(func(r resource.Resource) (bool, error) { return r.(*Status).TypedSpec().Synced, nil }), ) return err } ================================================ FILE: pkg/machinery/resources/time/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type AdjtimeStatusSpec -type StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package time // DeepCopy generates a deep copy of AdjtimeStatusSpec. func (o AdjtimeStatusSpec) DeepCopy() AdjtimeStatusSpec { var cp AdjtimeStatusSpec = o return cp } // DeepCopy generates a deep copy of StatusSpec. func (o StatusSpec) DeepCopy() StatusSpec { var cp StatusSpec = o return cp } ================================================ FILE: pkg/machinery/resources/time/status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // StatusType is type of TimeSync resource. const StatusType = resource.Type("TimeStatuses.v1alpha1.talos.dev") // StatusID is the ID of the singletone resource. const StatusID = resource.ID("node") // Status describes running current time sync status. type Status = typed.Resource[StatusSpec, StatusExtension] // StatusSpec describes time sync state. // //gotagsrewrite:gen type StatusSpec struct { // Synced indicates whether time is in sync. Synced bool `yaml:"synced" protobuf:"1"` // Epoch is incremented every time clock jumps more than 15min. Epoch int `yaml:"epoch" protobuf:"2"` // SyncDisabled indicates if time sync is disabled. SyncDisabled bool `yaml:"syncDisabled" protobuf:"3"` } // NewStatus initializes a TimeSync resource. func NewStatus() *Status { return typed.NewResource[StatusSpec, StatusExtension]( resource.NewMetadata(v1alpha1.NamespaceName, StatusType, StatusID, resource.VersionUndefined), StatusSpec{}, ) } // StatusExtension provides auxiliary methods for Status. type StatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (StatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: StatusType, Aliases: []resource.Type{}, DefaultNamespace: v1alpha1.NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Synced", JSONPath: "{.synced}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[StatusSpec](StatusType, &Status{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/time/time.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package time provides time-related resources. package time //go:generate go tool github.com/siderolabs/deep-copy -type AdjtimeStatusSpec -type StatusSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . ================================================ FILE: pkg/machinery/resources/time/time_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package time_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/time" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &time.AdjtimeStatus{}, &time.Status{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/resources/v1alpha1/acquire_config_spec.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 //nolint:dupl import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // AcquireConfigSpecType is type of AcquireConfigSpec resource. const AcquireConfigSpecType = resource.Type("AcquireConfigSpecs.v1alpha1.talos.dev") // AcquireConfigSpec is created when Talos is ready to start acquiring machine configuration. type AcquireConfigSpec = typed.Resource[AcquireConfigSpecSpec, AcquireConfigSpecExtension] // AcquireConfigSpecSpec describe state of ready to acquire config. // //gotagsrewrite:gen type AcquireConfigSpecSpec struct{} // NewAcquireConfigSpec initializes a AcquireConfigSpec resource. func NewAcquireConfigSpec() *AcquireConfigSpec { return typed.NewResource[AcquireConfigSpecSpec, AcquireConfigSpecExtension]( resource.NewMetadata(NamespaceName, AcquireConfigSpecType, "machine-config", resource.VersionUndefined), AcquireConfigSpecSpec{}, ) } // AcquireConfigSpecExtension provides auxiliary methods for AcquireConfigSpec. type AcquireConfigSpecExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AcquireConfigSpecExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AcquireConfigSpecType, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AcquireConfigSpecSpec](AcquireConfigSpecType, &AcquireConfigSpec{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/v1alpha1/acquire_config_status.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 //nolint:dupl import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // AcquireConfigStatusType is type of AcquireConfigStatus resource. const AcquireConfigStatusType = resource.Type("AcquireConfigStatuses.v1alpha1.talos.dev") // AcquireConfigStatus is created when machine configuration is ready and boot process is ok to proceed. type AcquireConfigStatus = typed.Resource[AcquireConfigStatusSpec, AcquireConfigStatusExtension] // AcquireConfigStatusSpec describe state of ready proceed booting with machine config. // //gotagsrewrite:gen type AcquireConfigStatusSpec struct{} // NewAcquireConfigStatus initializes a AcquireConfigStatus resource. func NewAcquireConfigStatus() *AcquireConfigStatus { return typed.NewResource[AcquireConfigStatusSpec, AcquireConfigStatusExtension]( resource.NewMetadata(NamespaceName, AcquireConfigStatusType, "machine-config", resource.VersionUndefined), AcquireConfigStatusSpec{}, ) } // AcquireConfigStatusExtension provides auxiliary methods for AcquireConfigStatus. type AcquireConfigStatusExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (AcquireConfigStatusExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: AcquireConfigStatusType, DefaultNamespace: NamespaceName, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[AcquireConfigStatusSpec](AcquireConfigStatusType, &AcquireConfigStatus{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/v1alpha1/deep_copy.generated.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by "deep-copy -type AcquireConfigSpecSpec -type AcquireConfigStatusSpec -type ServiceSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go ."; DO NOT EDIT. package v1alpha1 // DeepCopy generates a deep copy of AcquireConfigSpecSpec. func (o AcquireConfigSpecSpec) DeepCopy() AcquireConfigSpecSpec { var cp AcquireConfigSpecSpec = o return cp } // DeepCopy generates a deep copy of AcquireConfigStatusSpec. func (o AcquireConfigStatusSpec) DeepCopy() AcquireConfigStatusSpec { var cp AcquireConfigStatusSpec = o return cp } // DeepCopy generates a deep copy of ServiceSpec. func (o ServiceSpec) DeepCopy() ServiceSpec { var cp ServiceSpec = o return cp } ================================================ FILE: pkg/machinery/resources/v1alpha1/service.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1 import ( "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/resource/protobuf" "github.com/cosi-project/runtime/pkg/resource/typed" "github.com/siderolabs/talos/pkg/machinery/proto" ) // ServiceType is type of Service resource. const ServiceType = resource.Type("Services.v1alpha1.talos.dev") // Service describes running service state. type Service = typed.Resource[ServiceSpec, ServiceExtension] // ServiceSpec describe service state. // //gotagsrewrite:gen type ServiceSpec struct { Running bool `yaml:"running" protobuf:"1"` Healthy bool `yaml:"healthy" protobuf:"2"` Unknown bool `yaml:"unknown" protobuf:"3"` } // NewService initializes a Service resource. func NewService(id resource.ID) *Service { return typed.NewResource[ServiceSpec, ServiceExtension]( resource.NewMetadata(NamespaceName, ServiceType, id, resource.VersionUndefined), ServiceSpec{}, ) } // ServiceExtension provides auxiliary methods for Service. type ServiceExtension struct{} // ResourceDefinition implements meta.ResourceDefinitionProvider interface. func (ServiceExtension) ResourceDefinition() meta.ResourceDefinitionSpec { return meta.ResourceDefinitionSpec{ Type: ServiceType, Aliases: []resource.Type{"svc"}, DefaultNamespace: NamespaceName, PrintColumns: []meta.PrintColumn{ { Name: "Running", JSONPath: "{.running}", }, { Name: "Healthy", JSONPath: "{.healthy}", }, { Name: "Health Unknown", JSONPath: "{.unknown}", }, }, } } func init() { proto.RegisterDefaultTypes() err := protobuf.RegisterDynamic[ServiceSpec](ServiceType, &Service{}) if err != nil { panic(err) } } ================================================ FILE: pkg/machinery/resources/v1alpha1/v1alpha1.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package v1alpha1 provides resources which implement "glue" code from v1alpha1 Talos init system. package v1alpha1 import "github.com/cosi-project/runtime/pkg/resource" //go:generate go tool github.com/siderolabs/deep-copy -type AcquireConfigSpecSpec -type AcquireConfigStatusSpec -type ServiceSpec -header-file ../../../../hack/boilerplate.txt -o deep_copy.generated.go . // NamespaceName contains resources linking v1alpha2 components with v1alpha1 Talos runtime. const NamespaceName resource.Namespace = "runtime" ================================================ FILE: pkg/machinery/resources/v1alpha1/v1alpha1_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package v1alpha1_test import ( "testing" "github.com/cosi-project/runtime/pkg/resource/meta" "github.com/cosi-project/runtime/pkg/state" "github.com/cosi-project/runtime/pkg/state/impl/inmem" "github.com/cosi-project/runtime/pkg/state/impl/namespaced" "github.com/cosi-project/runtime/pkg/state/registry" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) func TestRegisterResource(t *testing.T) { ctx := t.Context() resources := state.WrapCore(namespaced.NewState(inmem.Build)) resourceRegistry := registry.NewResourceRegistry(resources) for _, resource := range []meta.ResourceWithRD{ &v1alpha1.AcquireConfigSpec{}, &v1alpha1.AcquireConfigStatus{}, &v1alpha1.Service{}, } { assert.NoError(t, resourceRegistry.Register(ctx, resource)) } } ================================================ FILE: pkg/machinery/role/role.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package role import ( "slices" "strings" "github.com/siderolabs/gen/maps" ) // Role represents Talos user role. // Its string value is used everywhere: as the Organization value of Talos client certificate, // as the value of talosctl flag, etc. type Role string const ( // Prefix for all built-in roles. Prefix = string("os:") // Admin defines Talos role for admins (every API is available). Admin = Role(Prefix + "admin") // Operator defines Talos role for operators (Reader + management APIs which do not allow secret access, e.g. rebooting a node). Operator = Role(Prefix + "operator") // Reader defines Talos role for readers who can access read-only APIs that do not expose secrets. Reader = Role(Prefix + "reader") // EtcdBackup defines Talos role that allows making etcd backups. EtcdBackup = Role(Prefix + "etcd:backup") // Impersonator defines Talos role for impersonating another user (and their role). // Used internally, but may also be granted to the user. Impersonator = Role(Prefix + "impersonator") ) // Set represents a set of roles. type Set struct { roles map[Role]struct{} } var ( // All roles that can be granted to users. All = MakeSet(Admin, Operator, Reader, EtcdBackup, Impersonator) // Zero is an empty set of roles. Zero = MakeSet() ) // MakeSet makes a set of roles from constants. // Use Parse in other cases. func MakeSet(roles ...Role) Set { res := Set{ roles: make(map[Role]struct{}, len(roles)), } for _, r := range roles { res.roles[r] = struct{}{} } return res } // Parse parses a set of roles. // The returned set is always non-nil and contains all roles, including unknown (for compatibility with future versions). // The returned slice contains roles unknown to the current version. func Parse(str []string) (Set, []string) { res := MakeSet() var unknownRoles []string for _, r := range str { r = strings.TrimSpace(r) // Client certificates generated by previous Talos versions contained one empty organization. if r == "" { continue } role := Role(r) if _, ok := All.roles[role]; !ok { unknownRoles = append(unknownRoles, r) } res.roles[role] = struct{}{} } return res, unknownRoles } // Strings returns a set as a slice of strings. func (s Set) Strings() []string { res := maps.KeysFunc(s.roles, func(r Role) string { return string(r) }) slices.Sort(res) return res } // IncludesAny returns true if there is a non-empty intersection between sets. // // Returns false if any set is empty. func (s Set) IncludesAny(other Set) bool { for r := range other.roles { if _, ok := s.roles[r]; ok { return true } } return false } // Includes returns true if given role is present in the set. func (s Set) Includes(role Role) bool { _, ok := s.roles[role] return ok } ================================================ FILE: pkg/machinery/role/role_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package role_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/machinery/role" ) func TestSet(t *testing.T) { t.Parallel() roles, unknownRoles := role.Parse([]string{"os:admin", "os:operator", "os:reader", "os:future", "os:impersonator", "", " "}) assert.Equal(t, []string{"os:future"}, unknownRoles) assert.Equal(t, role.MakeSet(role.Admin, role.Operator, role.Reader, role.Role("os:future"), role.Impersonator), roles) assert.Equal(t, []string{"os:admin", "os:future", "os:impersonator", "os:operator", "os:reader"}, roles.Strings()) assert.Equal(t, []string(nil), role.MakeSet().Strings()) assert.True(t, roles.Includes(role.Admin)) assert.False(t, roles.Includes(role.Role("wrong"))) assert.True(t, roles.IncludesAny(role.MakeSet(role.Admin))) assert.False(t, roles.IncludesAny(role.MakeSet(role.Role("wrong")))) assert.False(t, roles.IncludesAny(role.MakeSet())) assert.False(t, role.MakeSet().IncludesAny(roles)) assert.False(t, role.MakeSet().IncludesAny(role.MakeSet())) } ================================================ FILE: pkg/machinery/textdiff/testdata/fuzz/FuzzDiff/4436c698e19c9ea6 ================================================ go test fuzz v1 string("1\n\n\n\n\n\n") string("\n1\n\n\n\n\n\n\n\n\n\n\n0") ================================================ FILE: pkg/machinery/textdiff/testdata/fuzz/FuzzDiff/72f7d10cdb1b4c78 ================================================ go test fuzz v1 string("\n\n\n\n\n\n\n\n\n0") string("0") ================================================ FILE: pkg/machinery/textdiff/testdata/fuzz/FuzzDiff/e8e3f58f18e0ab28 ================================================ go test fuzz v1 string("\n\n\n\n\n\n\n\n\n0") string("\n") ================================================ FILE: pkg/machinery/textdiff/textdiff.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package textdiff provides a way to compare two text blobs. package textdiff import ( "fmt" "strings" "github.com/neticdk/go-stdlib/diff/myers" ) // MaxLines is the maximum number of lines that the diff function will process before giving up and returning a message instead. const MaxLines = 75_000 // Diff is a function that computes unified diff between two strings. // // The diff is limited to MaxLines lines, and if the diff is larger than that, a message is returned instead of the actual diff. // This is to prevent the function from consuming too much memory or CPU time when comparing large text blobs. func Diff(a, b string) (string, error) { if a == b { return "", nil } prevLines := strings.Count(a, "\n") newLines := strings.Count(b, "\n") if prevLines+newLines > MaxLines { return fmt.Sprintf("@@ -%d,%d +%d,%d @@ diff too large to display\n", 1, prevLines, 1, newLines), nil } differ := myers.NewCustomDiffer( myers.WithUnifiedFormatter(), myers.WithLinearSpace(true), // Disable the library's standard-Myers and LCS fallback paths: // - Standard Myers (< smallInputThreshold) is O((N+M)²) when inputs are asymmetric. // - LCS (> largeInputThreshold) is O(N*M) for the DP table. // By setting these to 0 and MaxLines respectively, only Hirschberg's // O(N+M) linear-space algorithm runs. Our MaxLines guard above ensures // inputs never exceed largeInputThreshold. myers.WithSmallInputThreshold(0), myers.WithLargeInputThreshold(MaxLines), ) return differ.Diff(a, b) } // DiffWithCustomPaths is almost same as Diff, but allows to specify custom paths for the diff header. func DiffWithCustomPaths(a, b, aPath, bPath string) (string, error) { diff, err := Diff(a, b) if err != nil { return "", err } if diff == "" { return "", nil } // patch the diff header to include the manifest path diff, ok := strings.CutPrefix(diff, "--- a\n+++ b\n") if !ok { return "", fmt.Errorf("unexpected diff format") } var sb strings.Builder sb.WriteString("--- ") sb.WriteString(aPath) sb.WriteString("\n+++ ") sb.WriteString(bPath) sb.WriteString("\n") sb.WriteString(diff) return sb.String(), nil } ================================================ FILE: pkg/machinery/textdiff/textdiff_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package textdiff_test import ( "fmt" "math/rand/v2" "runtime" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/textdiff" ) func TestDiff(t *testing.T) { t.Parallel() for _, test := range []struct { name string a, b string expected string }{ { name: "empty", a: "", b: "", expected: "", }, { name: "identical", a: "line 1\nline 2\nline 3\n", b: "line 1\nline 2\nline 3\n", expected: "", }, { name: "completely different", a: "line 1\nline 2\nline 3\n", b: "line A\nline B\nline C\n", expected: `--- a +++ b @@ -1,3 +1,3 @@ -line 1 -line 2 -line 3 +line A +line B +line C `, }, { name: "inserted line", a: "line 1\nline 3\n", b: "line 1\nline 2\nline 3\n", expected: `--- a +++ b @@ -1,2 +1,3 @@ line 1 +line 2 line 3 `, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() diff, err := textdiff.Diff(test.a, test.b) require.NoError(t, err) assert.Equal(t, test.expected, diff) }) } } func TestDiffWithCustomPaths(t *testing.T) { t.Parallel() diff, err := textdiff.DiffWithCustomPaths("line 1\nline 3\n", "line 1\nline 2\nline 3\n", "fileA.txt", "fileB.txt") require.NoError(t, err) assert.Equal(t, `--- fileA.txt +++ fileB.txt @@ -1,2 +1,3 @@ line 1 +line 2 line 3 `, diff) } func genRandomLines(n int) string { var sb strings.Builder for range n { fmt.Fprintf(&sb, "line %d\n", rand.Int()) } return sb.String() } func TestDiffMemoryBudge(t *testing.T) { // not parallel, we need to measure memory allocations linesA := genRandomLines(1000) linesB := genRandomLines(20000) linesC := genRandomLines(1000) for _, test := range []struct { name string a, b string memoryBudget uint64 }{ { name: "empty", a: "", b: "", memoryBudget: 1024, // 1KB }, { name: "large", a: linesA + linesB, b: linesB + linesC, memoryBudget: 3 * 1024 * 1024, // 3MB }, { name: "large 2", a: linesA + linesB, b: linesA + linesB + linesC, memoryBudget: 2 * 1024 * 1024, // 2MB }, { name: "completely different", a: linesA, b: linesC, memoryBudget: 512 * 1024, // 512KB }, { name: "large to empty", a: linesA + linesB + linesC, b: "", memoryBudget: 6 * 1024 * 1024, // 6MB }, { name: "empty to large", a: "", b: linesA + linesB + linesC, memoryBudget: 6 * 1024 * 1024, // 6MB }, } { t.Run(test.name, func(t *testing.T) { differ := textdiff.Diff allocs := MemoryAllocated(func() { _, err := differ(test.a, test.b) require.NoError(t, err) }) require.LessOrEqual(t, allocs, test.memoryBudget, "memory allocations exceeded the budget") }) } } func MemoryAllocated(f func()) uint64 { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) runtime.GC() // Measure the starting statistics var memstats runtime.MemStats runtime.ReadMemStats(&memstats) allocs := 0 - memstats.TotalAlloc f() // Read the final statistics runtime.ReadMemStats(&memstats) allocs += memstats.TotalAlloc return allocs } func FuzzDiff(f *testing.F) { f.Add("", "") f.Add("line 1\nline 2\n", "line 1\nline 2\n") f.Add("line 1\nline 2\n", "line 1\nline 2\nline 3\n") f.Fuzz(func(t *testing.T, a, b string) { _, err := textdiff.Diff(a, b) require.NoError(t, err) }) } ================================================ FILE: pkg/machinery/version/gen.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build generate package main import ( "log" "os" "github.com/siderolabs/talos/pkg/machinery/version" ) func main() { data, err := version.OSRelease() if err != nil { log.Fatal(err) } if err = os.WriteFile("os-release", data, 0o644); err != nil { log.Fatal(err) } } ================================================ FILE: pkg/machinery/version/os-release ================================================ NAME="Talos" ID=talos VERSION_ID=v1.13.0-alpha.2 PRETTY_NAME="Talos (v1.13.0-alpha.2)" HOME_URL="https://www.talos.dev/" BUG_REPORT_URL="https://github.com/siderolabs/talos/issues" VENDOR_NAME="Sidero Labs" VENDOR_URL="https://www.siderolabs.com/" ================================================ FILE: pkg/machinery/version/osrelease.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package version //go:generate env CGO_ENABLED=0 go run ./gen.go import ( "fmt" "strings" "github.com/siderolabs/talos/pkg/machinery/constants" ) // OSRelease returns the contents of /etc/os-release. func OSRelease() ([]byte, error) { var v string switch Tag { case "none": v = SHA default: v = Tag } return OSReleaseFor(Name, v) } // OSReleaseFor returns the contents of /etc/os-release for a given name and version. func OSReleaseFor(name, version string) ([]byte, error) { return fmt.Appendf(nil, constants.OSReleaseTemplate, name, strings.ToLower(name), version), nil } ================================================ FILE: pkg/machinery/version/osrelease_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package version_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/version" ) func TestOSRelease(t *testing.T) { t.Parallel() // simply verify generation without errors _, err := version.OSRelease() require.NoError(t, err) } func TestOSReleaseFor(t *testing.T) { t.Parallel() contents, err := version.OSReleaseFor("Talos", "v1.0.0") require.NoError(t, err) assert.Equal( t, "NAME=\"Talos\"\nID=talos\nVERSION_ID=v1.0.0\nPRETTY_NAME=\"Talos (v1.0.0)\"\nHOME_URL=\"https://www.talos.dev/\"\nBUG_REPORT_URL=\"https://github.com/siderolabs/talos/issues\"\nVENDOR_NAME=\"Sidero Labs\"\nVENDOR_URL=\"https://www.siderolabs.com/\"\n", //nolint:lll string(contents), ) } ================================================ FILE: pkg/machinery/version/version.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package version defines version information. package version import ( "fmt" "io" "os" "regexp" "runtime" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/gendata" ) var ( // Name is set at build time. Name = gendata.VersionName // Tag is set at build time. Tag = gendata.VersionTag // SHA is set at build time. SHA = gendata.VersionSHA // Built is set at build time. // TODO: its not. Built string // PkgsVersion is set at build time. PkgsVersion = gendata.VersionPkgs ) const versionTemplate = ` Tag: %[1]s SHA: %[2]s Built: %[3]s Go version: %[4]s OS/Arch: %[5]s/%[6]s ` // NewVersion prints verbose version information. func NewVersion() *machineapi.VersionInfo { return &machineapi.VersionInfo{ Tag: Tag, Sha: SHA, Built: Built, GoVersion: runtime.Version(), Os: runtime.GOOS, Arch: runtime.GOARCH, } } // PrintLongVersion prints verbose version information. func PrintLongVersion() { printLong(os.Stdout, NewVersion()) } // PrintLongVersionFromExisting prints verbose version information. func PrintLongVersionFromExisting(v *machineapi.VersionInfo) { printLong(os.Stdout, v) } // WriteLongVersionFromExisting writes verbose version to io.Writer. func WriteLongVersionFromExisting(w io.Writer, v *machineapi.VersionInfo) { printLong(w, v) } func printLong(w io.Writer, v *machineapi.VersionInfo) { fmt.Fprintf(w, versionTemplate, v.Tag, v.Sha, v.Built, v.GoVersion, v.Os, v.Arch) } // PrintShortVersion prints the tag and SHA. func PrintShortVersion() { fmt.Println(Short()) } // Short returns the short version string consist of name, tag and SHA. func Short() string { return fmt.Sprintf("%s %s", Name, Tag) } // Trim removes anything extra after semantic version core, `v0.3.2-1-abcd` -> `v0.3.2`. func Trim(version string) string { return regexp.MustCompile(`(-\d+(-g[0-9a-f]+)?(-dirty)?)$`).ReplaceAllString(version, "") } ================================================ FILE: pkg/machinery/yamlutils/yamlutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package yamlutils provides utility types to work with YAML marshaling and unmarshaling. package yamlutils import "bytes" // StringBytes is a type that represents a byte slice as a string when marshaled to YAML. type StringBytes []byte // MarshalYAML implements yaml.Marshaller interface for StringBytes. func (s StringBytes) MarshalYAML() (any, error) { if bytes.Equal(bytes.ToValidUTF8(s, nil), s) { // If the byte slice is valid UTF-8, return it as a string. return string(s), nil } return s.Bytes(), nil } // UnmarshalYAML implements yaml.Unmarshaler interface for StringBytes. func (s *StringBytes) UnmarshalYAML(unmarshal func(any) error) error { var str string if err := unmarshal(&str); err == nil { *s = []byte(str) return nil } var data []byte if err := unmarshal(&data); err != nil { return err } *s = data return nil } // Bytes returns the byte slice representation of StringBytes. func (s StringBytes) Bytes() []byte { return []byte(s) } ================================================ FILE: pkg/machinery/yamlutils/yamlutils_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package yamlutils_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v4" "github.com/siderolabs/talos/pkg/machinery/yamlutils" ) func TestStringBytes(t *testing.T) { t.Parallel() type sbStruct struct { Field yamlutils.StringBytes `yaml:"field"` } for _, test := range []struct { name string in any expected string empty func() any // extraMarshaled is a list of strings that should be unmarshaled from YAML into the same `in` extraMarshaled []string }{ { name: "simple", in: &sbStruct{yamlutils.StringBytes([]byte("abcde"))}, expected: "field: abcde\n", empty: func() any { return &sbStruct{} }, extraMarshaled: []string{ "field:\n - 0x61\n - 0x62\n - 0x63\n - 0x64\n - 0x65\n", "field:\n - 97\n - 98\n - 99\n - 100\n - 101\n", }, }, { name: "empty", in: &sbStruct{yamlutils.StringBytes([]byte{})}, expected: "field: \"\"\n", empty: func() any { return &sbStruct{} }, }, { name: "invalid utf8", in: &sbStruct{yamlutils.StringBytes([]byte{0xff})}, expected: "field:\n - 255\n", empty: func() any { return &sbStruct{} }, extraMarshaled: []string{ "field:\n - 0xff\n", }, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() out, err := yaml.Marshal(test.in) require.NoError(t, err) assert.Equal(t, test.expected, string(out)) back := test.empty() err = yaml.Unmarshal(out, back) require.NoError(t, err) assert.Equal(t, test.in, back) for _, extra := range test.extraMarshaled { back := test.empty() err = yaml.Unmarshal([]byte(extra), back) require.NoError(t, err) assert.Equal(t, test.in, back) } }) } } ================================================ FILE: pkg/makefs/ext4.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs import ( "context" "errors" "fmt" "io" "os" "os/exec" "strconv" "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/pkg/imager/utils" ) const ( // FilesystemTypeEXT4 is the filesystem type for EXT4. FilesystemTypeEXT4 = "ext4" ) // Ext4 creates a ext4 filesystem on the specified partition. // //nolint:gocyclo func Ext4(ctx context.Context, partname string, setters ...Option) error { if partname == "" { return errors.New("missing path to disk") } opts := NewDefaultOptions(setters...) var args []string if opts.Label != "" { args = append(args, "-L", opts.Label) } if opts.Force { args = append(args, "-F") } if opts.Reproducible { if opts.Label == "" { return errors.New("label must be set for reproducible ext4 filesystem") } partitionGUID := GUIDFromLabel(opts.Label) args = append(args, "-U", partitionGUID.String()) args = append(args, "-E", fmt.Sprintf("hash_seed=%s", partitionGUID.String())) } var errCh chan error // Use a tar archive instead of passing a source directory directly to mke2fs. // This allows us to force all files in the image to be owned by root (uid/gid 0) // regardless of their ownership on the host, enabling rootless operation without // requiring elevated privileges. if opts.SourceDirectory != "" { errCh = make(chan error, 1) pr, pw, err := os.Pipe() if err != nil { return fmt.Errorf("failed to create pipe: %w", err) } defer pr.Close() //nolint:errcheck args = append(args, "-d", "-") ctx = cmd.WithStdin(ctx, pr) go func() { defer pw.Close() //nolint:errcheck errCh <- handleTarArchive(ctx, opts.SourceDirectory, pw) }() } args = append(args, partname) opts.Printf("creating ext4 filesystem on %s with args: %v", partname, args) _, err := cmd.RunWithOptions(ctx, "mkfs.ext4", args) if err != nil { return err } if errCh != nil { return <-errCh } return nil } // Ext4Resize expands a ext4 filesystem to the maximum possible. func Ext4Resize(ctx context.Context, partname string) error { // resizing the filesystem requires a check first if err := Ext4Repair(ctx, partname); err != nil { return fmt.Errorf("failed to repair before growing ext4 filesystem: %w", err) } _, err := cmd.RunWithOptions(ctx, "resize2fs", []string{partname}) if err != nil { return fmt.Errorf("failed to grow ext4 filesystem: %w", err) } return nil } // Ext4Repair repairs a ext4 filesystem. func Ext4Repair(ctx context.Context, partname string) error { _, err := cmd.RunWithOptions(ctx, "e2fsck", []string{"-f", "-p", partname}) if err != nil { return fmt.Errorf("failed to repair ext4 filesystem: %w", err) } return nil } // handleTarArchive creates a tar archive from the sourceDir and writes it to the provided // io.WriteCloser. func handleTarArchive(ctx context.Context, sourceDir string, w io.WriteCloser) error { timestamp, ok, err := utils.SourceDateEpoch() if err != nil { return fmt.Errorf("failed to get SOURCE_DATE_EPOCH: %w", err) } if !ok { timestamp = time.Now().Unix() } cmd := exec.CommandContext( ctx, "tar", "-cf", "-", "-C", sourceDir, "--sort=name", "--owner=0", "--group=0", "--numeric-owner", "--mtime=@"+strconv.FormatInt(timestamp, 10), ".", ) cmd.Stdout = w cmd.Stderr = os.Stderr if err := cmd.Start(); err != nil { return fmt.Errorf("failed to start tar command: %w", err) } closeErr := w.Close() waitErr := cmd.Wait() if closeErr != nil { return fmt.Errorf("failed to close pipe writer: %w", closeErr) } return waitErr } ================================================ FILE: pkg/makefs/ext4_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs_test import ( "crypto/sha256" "io" "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/makefs" ) // TestExt4Reproducibility tests that the ext4 filesystem is reproducible. func TestExt4Reproducibility(t *testing.T) { t.Setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin") t.Setenv("SOURCE_DATE_EPOCH", "1234567890") tmpDir := t.TempDir() tempFile := filepath.Join(tmpDir, "reproducible-ext4.img") f, err := os.Create(tempFile) require.NoError(t, err) require.NoError(t, f.Truncate(512*1024*1024)) require.NoError(t, f.Close()) sourceDirectory := filepath.Join(tmpDir, "source") populateTestDir(t, sourceDirectory, []string{ "file1.txt", "dir1/", "dir1/file2.txt", "dir1/subdir1/", "dir1/subdir1/file3.txt", }) require.NoError(t, makefs.Ext4(t.Context(), tempFile, makefs.WithReproducible(true), makefs.WithLabel("TESTLABEL"), makefs.WithSourceDirectory(sourceDirectory), )) fileData, err := os.Open(tempFile) require.NoError(t, err) sum1 := sha256.New() _, err = io.Copy(sum1, fileData) require.NoError(t, err) require.NoError(t, fileData.Close()) // create the filesystem again require.NoError(t, makefs.Ext4(t.Context(), tempFile, makefs.WithReproducible(true), makefs.WithLabel("TESTLABEL"), makefs.WithSourceDirectory(sourceDirectory), makefs.WithForce(true))) fileData, err = os.Open(tempFile) require.NoError(t, err) sum2 := sha256.New() _, err = io.Copy(sum2, fileData) require.NoError(t, err) require.NoError(t, fileData.Close()) assert.Equal(t, sum1.Sum(nil), sum2.Sum(nil), "ext4 filesystem is not reproducible") } // TestExt4Resize tests that the ext4 filesystem can be resized. func TestExt4Resize(t *testing.T) { t.Setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin") tmpDir := t.TempDir() tempFile := filepath.Join(tmpDir, "resize-ext4.img") if _, err := os.Create(tempFile); err != nil { t.Fatalf("failed to create file: %v", err) } if err := os.Truncate(tempFile, 64*1024*1024); err != nil { t.Fatalf("failed to create file: %v", err) } if err := makefs.Ext4(t.Context(), tempFile); err != nil { t.Fatalf("failed to create ext4 filesystem: %v", err) } if err := os.Truncate(tempFile, 128*1024*1024); err != nil { t.Fatalf("failed to resize file: %v", err) } assert.NoError(t, makefs.Ext4Resize(t.Context(), tempFile)) } ================================================ FILE: pkg/makefs/makefs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package makefs provides function to format and grow filesystems. package makefs import ( "crypto/sha256" "github.com/google/uuid" ) // Option to control makefs settings. type Option func(*Options) // Options for makefs. type Options struct { Label string ConfigFile string SourceDirectory string Force bool Reproducible bool UnsupportedFSOption bool Printf func(string, ...any) } // WithLabel sets the label for the filesystem to be created. func WithLabel(label string) Option { return func(o *Options) { o.Label = label } } // WithForce forces creation of a filesystem even if one already exists. func WithForce(force bool) Option { return func(o *Options) { o.Force = force } } // WithReproducible sets the reproducible flag for the filesystem to be created. // This should only be used when creating filesystems on raw disk images. func WithReproducible(reproducible bool) Option { return func(o *Options) { o.Reproducible = reproducible } } // WithUnsupportedFSOption sets the unsupported filesystem option. func WithUnsupportedFSOption(unsupported bool) Option { return func(o *Options) { o.UnsupportedFSOption = unsupported } } // WithConfigFile sets the config file for the filesystem to be created. func WithConfigFile(configFile string) Option { return func(o *Options) { o.ConfigFile = configFile } } // WithSourceDirectory sets the source directory for populating the filesystem. func WithSourceDirectory(sourceDir string) Option { return func(o *Options) { o.SourceDirectory = sourceDir } } // WithPrintf sets the printf function for logging. func WithPrintf(printf func(string, ...any)) Option { return func(o *Options) { o.Printf = printf } } // NewDefaultOptions builds options with specified setters applied. func NewDefaultOptions(setters ...Option) Options { var opt Options for _, o := range setters { o(&opt) } if opt.Printf == nil { opt.Printf = func(string, ...any) {} } return opt } // GUIDFromLabel generates a deterministic partition GUID from a label by // creating a version 8 UUID derived from a SHA-256 hash of the label bytes. func GUIDFromLabel(label string) uuid.UUID { // version 8 UUID since we're doing custom hashing return uuid.NewHash(sha256.New(), uuid.Nil, []byte(label), 8) } ================================================ FILE: pkg/makefs/makefs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs_test import ( "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/makefs" ) func TestPartitionGUIDFromLabel(t *testing.T) { tests := []struct { label string expected string }{ { label: constants.EFIPartitionLabel, expected: "bca5174b-0118-8a6a-af0b-2c6e1585acae", }, { label: constants.BootPartitionLabel, expected: "24c73076-4092-85dd-9d98-686a2dbf6d81", }, { label: constants.MetaPartitionLabel, expected: "cf04f137-101e-8ec6-9627-fa3f51cdabb8", }, { label: constants.ImageCachePartitionLabel, expected: "3b5449bc-c21f-8009-8b51-18945967f8df", }, } for _, tt := range tests { t.Run(tt.label, func(t *testing.T) { require.Equal(t, tt.expected, makefs.GUIDFromLabel(tt.label).String()) }) } } // populateTestDir is a helper function to populate a test directory with files, // it can handle nested files and directories, file content is populated with the name of the file. // All files are created relative to the provided dir. func populateTestDir(t *testing.T, dir string, files []string) { t.Helper() for _, file := range files { fullPath := filepath.Join(dir, file) if strings.HasSuffix(file, "/") { require.NoError(t, os.MkdirAll(fullPath, 0o755)) } else { require.NoError(t, os.MkdirAll(filepath.Dir(fullPath), 0o755)) require.NoError(t, os.WriteFile(fullPath, []byte(file), 0o644)) } } } ================================================ FILE: pkg/makefs/protofile.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs import ( "bytes" "fmt" "io" "io/fs" "os" "path/filepath" "strings" "syscall" "golang.org/x/sys/unix" ) // GenerateProtofile walks a filesystem tree and generates an XFS protofile. // All files and directories will have uid/gid mapped to 0:0. func GenerateProtofile(sourcePath string) (io.Reader, error) { var buf bytes.Buffer // Emit protofile header fmt.Fprintln(&buf, "/") fmt.Fprintln(&buf, "0 0") // Get stat of the root directory statbuf, err := os.Stat(sourcePath) if err != nil { return nil, fmt.Errorf("failed to stat path %s: %w", sourcePath, err) } if !statbuf.IsDir() { return nil, fmt.Errorf("path %s is not a directory", sourcePath) } // Write root directory stat fmt.Fprintln(&buf, statToProtoStr(statbuf)) // Walk the tree if err := walkTree(&buf, sourcePath, 1); err != nil { return nil, err } fmt.Fprintln(&buf, "$") return &buf, nil } // statToProtoStr converts a FileInfo to a proto string. func statToProtoStr(info fs.FileInfo) string { mode := info.Mode() var fileType rune switch { case mode.IsRegular(): fileType = '-' case mode&fs.ModeCharDevice != 0: fileType = 'c' case mode&fs.ModeDevice != 0: fileType = 'b' case mode&fs.ModeNamedPipe != 0: fileType = 'p' case mode.IsDir(): fileType = 'd' case mode&fs.ModeSymlink != 0: fileType = 'l' default: fileType = '-' } var suid, sgid rune if mode&fs.ModeSetuid != 0 { suid = 'u' } else { suid = '-' } if mode&fs.ModeSetgid != 0 { sgid = 'g' } else { sgid = '-' } // Extract permissions (mask out file type and special bits) perms := mode.Perm() return fmt.Sprintf("%c%c%c%03o %d %d", fileType, suid, sgid, perms, 0, 0) } // statToExtra computes the extras column for a protofile entry. func statToExtra(info fs.FileInfo, fullpath string) (string, error) { mode := info.Mode() switch { case mode.IsRegular(): return fmt.Sprintf(" %s", fullpath), nil case mode&fs.ModeCharDevice != 0, mode&fs.ModeDevice != 0: if sys := info.Sys(); sys != nil { if stat, ok := sys.(*syscall.Stat_t); ok { major := unix.Major(stat.Rdev) minor := unix.Minor(stat.Rdev) return fmt.Sprintf(" %d %d", major, minor), nil } } return " 0 0", nil case mode&fs.ModeSymlink != 0: target, err := os.Readlink(fullpath) if err != nil { return "", fmt.Errorf("failed to read symlink %s: %w", fullpath, err) } return fmt.Sprintf(" %s", target), nil } return "", nil } // walkTree walks the directory tree rooted by path. // //nolint:gocyclo func walkTree(w io.Writer, path string, depth int) error { type entry struct { name string fullpath string info fs.FileInfo isDir bool } var entries []entry err := filepath.WalkDir(path, func(p string, d fs.DirEntry, err error) error { if err != nil { return err } // Skip the root directory itself if p == path { return nil } // Only process direct children of the current path rel, err := filepath.Rel(path, p) if err != nil { return err } if strings.Contains(rel, string(filepath.Separator)) { return filepath.SkipDir } info, err := d.Info() if err != nil { return fmt.Errorf("failed to stat %s: %w", p, err) } // Skip sockets if info.Mode()&fs.ModeSocket != 0 { return nil } // Validate no spaces in name if strings.Contains(d.Name(), " ") { return fmt.Errorf("spaces not allowed in file names: %s", d.Name()) } entries = append(entries, entry{ name: d.Name(), fullpath: p, info: info, isDir: d.IsDir(), }) // Skip descending into directories, as we'll recurse manually later if d.IsDir() { return filepath.SkipDir } return nil }) if err != nil { return fmt.Errorf("failed to walk directory %s: %w", path, err) } // Print files first for _, e := range entries { if !e.isDir { extra, err := statToExtra(e.info, e.fullpath) if err != nil { return err } indent := strings.Repeat(" ", depth) fmt.Fprintf(w, "%s%s %s%s\n", indent, e.name, statToProtoStr(e.info), extra) } } // Print and recurse into directories for _, e := range entries { if e.isDir { extra, err := statToExtra(e.info, e.fullpath) if err != nil { return err } indent := strings.Repeat(" ", depth) fmt.Fprintf(w, "%s%s %s%s\n", indent, e.name, statToProtoStr(e.info), extra) if err := walkTree(w, e.fullpath, depth+1); err != nil { return err } } } // Close directory marker (except for depth 1) if depth > 1 { indent := strings.Repeat(" ", depth-1) fmt.Fprintf(w, "%s$\n", indent) } return nil } ================================================ FILE: pkg/makefs/protofile_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs_test import ( _ "embed" "io" "os" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/makefs" ) func TestGenerateProtofile(t *testing.T) { tests := []struct { name string protoFile string setupFunc func(t *testing.T, dir string) }{ { name: "simple directory with files", protoFile: "testdata/simple_directory.proto", setupFunc: func(t *testing.T, dir string) { populateTestDir(t, dir, []string{ "file1.txt", "file2.txt", }) }, }, { name: "nested directories", protoFile: "testdata/nested_directories.proto", setupFunc: func(t *testing.T, dir string) { populateTestDir(t, dir, []string{ "subdir/", "root.txt", "subdir/nested.txt", }) }, }, { name: "symlinks", protoFile: "testdata/symlinks.proto", setupFunc: func(t *testing.T, dir string) { populateTestDir(t, dir, []string{ "target.txt", }) link := filepath.Join(dir, "link.txt") require.NoError(t, os.Symlink("target.txt", link)) }, }, { name: "deeply nested directories", protoFile: "testdata/deeply_nested.proto", setupFunc: func(t *testing.T, dir string) { populateTestDir(t, dir, []string{ "root1.txt", "root2.txt", "subdir/", "subdir/file1.txt", "subdir/file2.txt", "subdir/nested/", "subdir/nested/deep.txt", "anotherdir/", "anotherdir/file.txt", }) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpDir := t.TempDir() testDir := filepath.Join(tmpDir, "testdata") require.NoError(t, os.Mkdir(testDir, 0o755)) tt.setupFunc(t, testDir) r, err := makefs.GenerateProtofile(testDir) require.NoError(t, err) protoFileData, err := io.ReadAll(r) require.NoError(t, err) // Read expected protofile protoData, err := os.ReadFile(tt.protoFile) require.NoError(t, err, "failed to read protofile %s", tt.protoFile) protoDataStr := strings.ReplaceAll(string(protoData), "FILEPATH", testDir) assert.Equal(t, protoDataStr, string(protoFileData), "protofile output does not match expected %s", tt.protoFile) }) } } func TestProtofileRejectsSpaces(t *testing.T) { tmpDir := t.TempDir() testDir := filepath.Join(tmpDir, "testdata") require.NoError(t, os.Mkdir(testDir, 0o755)) // Create a file with space in name require.NoError(t, os.WriteFile(filepath.Join(testDir, "file with space.txt"), []byte("content"), 0o644)) _, err := makefs.GenerateProtofile(testDir) require.Error(t, err) assert.Contains(t, err.Error(), "spaces not allowed") } func TestProtofileErrorCases(t *testing.T) { t.Run("nonexistent directory", func(t *testing.T) { _, err := makefs.GenerateProtofile("/nonexistent/path") require.Error(t, err) }) t.Run("not a directory", func(t *testing.T) { tmpDir := t.TempDir() filePath := filepath.Join(tmpDir, "file.txt") require.NoError(t, os.WriteFile(filePath, []byte("content"), 0o644)) _, err := makefs.GenerateProtofile(filePath) require.Error(t, err) assert.Contains(t, err.Error(), "not a directory") }) } ================================================ FILE: pkg/makefs/testdata/deeply_nested.proto ================================================ / 0 0 d--755 0 0 root1.txt ---644 0 0 FILEPATH/root1.txt root2.txt ---644 0 0 FILEPATH/root2.txt anotherdir d--755 0 0 file.txt ---644 0 0 FILEPATH/anotherdir/file.txt $ subdir d--755 0 0 file1.txt ---644 0 0 FILEPATH/subdir/file1.txt file2.txt ---644 0 0 FILEPATH/subdir/file2.txt nested d--755 0 0 deep.txt ---644 0 0 FILEPATH/subdir/nested/deep.txt $ $ $ ================================================ FILE: pkg/makefs/testdata/nested_directories.proto ================================================ / 0 0 d--755 0 0 root.txt ---644 0 0 FILEPATH/root.txt subdir d--755 0 0 nested.txt ---644 0 0 FILEPATH/subdir/nested.txt $ $ ================================================ FILE: pkg/makefs/testdata/simple_directory.proto ================================================ / 0 0 d--755 0 0 file1.txt ---644 0 0 FILEPATH/file1.txt file2.txt ---644 0 0 FILEPATH/file2.txt $ ================================================ FILE: pkg/makefs/testdata/symlinks.proto ================================================ / 0 0 d--755 0 0 link.txt l--777 0 0 target.txt target.txt ---644 0 0 FILEPATH/target.txt $ ================================================ FILE: pkg/makefs/vfat.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs import ( "context" "fmt" "os" "path/filepath" "github.com/siderolabs/go-cmd/pkg/cmd" ) const ( // FilesystemTypeVFAT is the filesystem type for VFAT. FilesystemTypeVFAT = "vfat" ) // VFAT creates a VFAT filesystem on the specified partition. func VFAT(ctx context.Context, partname string, setters ...Option) error { opts := NewDefaultOptions(setters...) var args []string if opts.Label != "" { args = append(args, "-F", "32", "-n", opts.Label) } if opts.Reproducible { args = append(args, "--invariant") } args = append(args, partname) _, err := cmd.RunWithOptions(ctx, "mkfs.vfat", args) if err != nil { return err } // If source directory is specified, populate the filesystem using mtools if opts.SourceDirectory != "" { if err := populateVFAT(ctx, partname, opts.SourceDirectory); err != nil { return fmt.Errorf("failed to populate VFAT filesystem: %w", err) } } return nil } // populateVFAT populates a VFAT filesystem on the given partition with the // contents of sourceDir. func populateVFAT(ctx context.Context, partname, sourceDir string) error { entries, err := os.ReadDir(sourceDir) if err != nil { return fmt.Errorf("failed to read source directory %q: %w", sourceDir, err) } for _, entry := range entries { switch { case entry.Type().IsDir(): // copy directories case entry.Type().IsRegular(): // copy regular files default: return fmt.Errorf("unsupported file type for entry %q in source directory %q", entry.Name(), sourceDir) } if _, err := cmd.RunWithOptions( ctx, "mcopy", []string{ "-s", // recursive "-p", // preserve attributes "-Q", // quit on error "-m", // preserve modification time "-i", partname, filepath.Join(sourceDir, entry.Name()), "::", }, ); err != nil { return err } } return nil } ================================================ FILE: pkg/makefs/vfat_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs_test import ( "os" "os/exec" "path/filepath" "testing" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/makefs" ) func TestVFATWithSourceDirectory(t *testing.T) { _, err := exec.LookPath("mcopy") if err != nil { t.Skip("mcopy not found in PATH, skipping test") } tempDir := t.TempDir() // Create source directory structure sourceDir := filepath.Join(tempDir, "source") require.NoError(t, os.MkdirAll(filepath.Join(sourceDir, "subdir"), 0o755)) // Create test files require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "file1.txt"), []byte("content1"), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "file2.txt"), []byte("content2"), 0o644)) require.NoError(t, os.WriteFile(filepath.Join(sourceDir, "subdir", "file3.txt"), []byte("content3"), 0o644)) // Create VFAT image vfatImg := filepath.Join(tempDir, "test.img") // Create a 10MB image file f, err := os.Create(vfatImg) require.NoError(t, err) require.NoError(t, f.Truncate(10*1024*1024)) require.NoError(t, f.Close()) // Format and populate VFAT err = makefs.VFAT(t.Context(), vfatImg, makefs.WithLabel("TEST"), makefs.WithSourceDirectory(sourceDir)) require.NoError(t, err) // Verify file contents using mcopy extractDir := filepath.Join(tempDir, "extract") require.NoError(t, os.MkdirAll(extractDir, 0o755)) // Extract all files _, err = cmd.RunWithOptions(t.Context(), "mcopy", []string{"-s", "-i", vfatImg, "::/", extractDir}) require.NoError(t, err) // Verify extracted files with full filenames content, err := os.ReadFile(filepath.Join(extractDir, "file1.txt")) require.NoError(t, err) require.Equal(t, "content1", string(content)) content, err = os.ReadFile(filepath.Join(extractDir, "file2.txt")) require.NoError(t, err) require.Equal(t, "content2", string(content)) content, err = os.ReadFile(filepath.Join(extractDir, "subdir", "file3.txt")) require.NoError(t, err) require.Equal(t, "content3", string(content)) } ================================================ FILE: pkg/makefs/xfs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs import ( "context" "errors" "fmt" "io" "os" "github.com/siderolabs/go-cmd/pkg/cmd" "golang.org/x/sys/unix" ) const ( // FilesystemTypeXFS is the filesystem type for XFS. FilesystemTypeXFS = "xfs" ) // XFSGrow expands a XFS filesystem to the maximum possible. The partition // MUST be mounted, or this will fail. func XFSGrow(ctx context.Context, partname string) error { _, err := cmd.RunWithOptions(ctx, "xfs_growfs", []string{"-d", partname}) if err != nil { return fmt.Errorf("failed to grow XFS filesystem: %w", err) } return err } // XFSRepair repairs a XFS filesystem on the specified partition. func XFSRepair(ctx context.Context, partname string) error { _, err := cmd.RunWithOptions(ctx, "xfs_repair", []string{partname}) if err != nil { return fmt.Errorf("error repairing XFS filesystem: %w", err) } return nil } // XFS creates a XFS filesystem on the specified partition. // //nolint:gocyclo func XFS(ctx context.Context, partname string, setters ...Option) error { if partname == "" { return errors.New("missing path to disk") } opts := NewDefaultOptions(setters...) // The ftype=1 naming option is required by overlayfs. args := []string{"-n", "ftype=1"} if opts.ConfigFile != "" { args = append(args, "-c", fmt.Sprintf("options=%s", opts.ConfigFile)) } if opts.Force { args = append(args, "-f") } if opts.Label != "" { args = append(args, "-L", opts.Label) } if opts.UnsupportedFSOption { args = append(args, "--unsupported") } if opts.SourceDirectory != "" { r, err := GenerateProtofile(opts.SourceDirectory) if err != nil { return fmt.Errorf("failed to generate protofile: %w", err) } fd, err := unix.MemfdCreate("protofile", 0) if err != nil { return fmt.Errorf("error creating memfd for protofile: %w", err) } protoMemfd := os.NewFile(uintptr(fd), "protofile") defer protoMemfd.Close() //nolint:errcheck _, err = io.Copy(protoMemfd, r) if err != nil { return fmt.Errorf("failed to write protofile to memfd: %w", err) } // Seek back to the beginning so mkfs.xfs can read from start if _, err := protoMemfd.Seek(0, 0); err != nil { return fmt.Errorf("failed to seek protofile: %w", err) } args = append(args, "-p", fmt.Sprintf("file=/proc/self/fd/%d", protoMemfd.Fd())) } if opts.Reproducible { if opts.Label == "" { return errors.New("label must be set for reproducible XFS filesystem") } partitionGUID := GUIDFromLabel(opts.Label) args = append(args, "-m", fmt.Sprintf("uuid=%s", partitionGUID.String())) } args = append(args, partname) opts.Printf("creating xfs filesystem on %s with args: %v", partname, args) _, err := cmd.RunWithOptions(ctx, "mkfs.xfs", args) return err } ================================================ FILE: pkg/makefs/xfs_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package makefs_test import ( "bytes" "crypto/sha256" "io" "os" "os/exec" "path/filepath" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/makefs" ) func TestXFSInfo(t *testing.T) { //nolint:tparallel t.Setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin") for _, test := range []struct { name string size int64 expected string }{ { name: "1G", size: 1024 * 1024 * 1024, expected: `meta-data=image isize=512 agcount=4, agsize=65536 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=1 metadir=0 data = bsize=4096 blocks=262144, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=1 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 = rgcount=0 rgsize=0 extents = zoned=0 start=0 reserved=0 `, }, { name: "10G", size: 10 * 1024 * 1024 * 1024, expected: `meta-data=image isize=512 agcount=4, agsize=655360 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=1 metadir=0 data = bsize=4096 blocks=2621440, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=1 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 = rgcount=0 rgsize=0 extents = zoned=0 start=0 reserved=0 `, }, { name: "100G", size: 100 * 1024 * 1024 * 1024, expected: `meta-data=image isize=512 agcount=4, agsize=6553600 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=1 = reflink=1 bigtime=1 inobtcount=1 nrext64=1 = exchange=1 metadir=0 data = bsize=4096 blocks=26214400, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=1 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 = rgcount=0 rgsize=0 extents = zoned=0 start=0 reserved=0 `, }, } { t.Run(test.name, func(t *testing.T) { t.Parallel() tmpDir := t.TempDir() tempFile := filepath.Join(tmpDir, "xfs.img") require.NoError(t, os.WriteFile(tempFile, nil, 0o644)) require.NoError(t, os.Truncate(tempFile, test.size)) require.NoError(t, makefs.XFS(t.Context(), tempFile)) var stdout bytes.Buffer cmd := exec.CommandContext(t.Context(), "xfs_db", "-p", "xfs_info", "-c", "info", tempFile) cmd.Stdout = &stdout require.NoError(t, cmd.Run()) actual := strings.ReplaceAll(stdout.String(), tempFile, "image") assert.Equal(t, test.expected, actual) }) } } func TestXFSReproducibility(t *testing.T) { t.Setenv("SOURCE_DATE_EPOCH", "1732109929") t.Setenv("DETERMINISTIC_SEED", "1") t.Setenv("PATH", "/usr/bin:/bin:/usr/sbin:/sbin") tmpDir := t.TempDir() tempFile := filepath.Join(tmpDir, "reproducible-xfs.img") f, err := os.Create(tempFile) require.NoError(t, err) require.NoError(t, f.Truncate(512*1024*1024)) require.NoError(t, f.Close()) sourceDirectory := filepath.Join(tmpDir, "source") populateTestDir(t, sourceDirectory, []string{ "file1.txt", "dir1/", "dir1/file2.txt", "dir1/subdir1/", "dir1/subdir1/file3.txt", }) require.NoError(t, makefs.XFS(t.Context(), tempFile, makefs.WithReproducible(true), makefs.WithLabel("TESTLABEL"), makefs.WithSourceDirectory(sourceDirectory), )) fileData, err := os.Open(tempFile) require.NoError(t, err) sum1 := sha256.New() _, err = io.Copy(sum1, fileData) require.NoError(t, err) require.NoError(t, fileData.Close()) // create the filesystem again require.NoError(t, makefs.XFS(t.Context(), tempFile, makefs.WithReproducible(true), makefs.WithForce(true), makefs.WithLabel("TESTLABEL"), makefs.WithSourceDirectory(sourceDirectory), )) // get the file sha256 checksum fileData, err = os.Open(tempFile) require.NoError(t, err) sum2 := sha256.New() _, err = io.Copy(sum2, fileData) require.NoError(t, err) require.NoError(t, fileData.Close()) assert.Equal(t, sum1.Sum(nil), sum2.Sum(nil)) } ================================================ FILE: pkg/minimal/limits.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package minimal provides the minimal/recommended limits for different machine types. package minimal import ( "fmt" "github.com/dustin/go-humanize" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // Memory returns the minimal/recommended amount of memory required to run the node. func Memory(typ machine.Type) (minimum, recommended uint64, err error) { // We remove 150 MiB from the recommended memory to account for the kernel switch typ { //nolint:exhaustive case machine.TypeControlPlane, machine.TypeInit: minimum = 1848*humanize.MiByte - 150*humanize.MiByte recommended = 4*humanize.GiByte - 150*humanize.MiByte case machine.TypeWorker: minimum = 1*humanize.GiByte - 150*humanize.MiByte recommended = 2*humanize.GiByte - 150*humanize.MiByte default: return 0, 0, fmt.Errorf("unknown machine type %q", typ) } return minimum, recommended, nil } // DiskSize returns the minimal/recommended amount of disk space required to run the node. func DiskSize() uint64 { return 6 * humanize.GiByte } ================================================ FILE: pkg/provision/access/access.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package access provides methods to access provisioned Talos cluster. package access ================================================ FILE: pkg/provision/access/access_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package access_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/provision/access/adapter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package access import ( "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/provision" ) // Adapter provides cluster access via provision.Cluster. type Adapter struct { cluster.ConfigClientProvider cluster.KubernetesClient cluster.APIBootstrapper cluster.Info cluster.ApplyConfigClient } type infoWrapper struct { clusterInfo provision.ClusterInfo nodes []cluster.NodeInfo nodesByType map[machine.Type][]cluster.NodeInfo } func (wrapper *infoWrapper) Nodes() []cluster.NodeInfo { return wrapper.nodes } func (wrapper *infoWrapper) NodesByType(t machine.Type) []cluster.NodeInfo { return wrapper.nodesByType[t] } // NewAdapter returns ClusterAccess object from Cluster. func NewAdapter(clusterInfo provision.Cluster, opts ...provision.Option) *Adapter { options := provision.DefaultOptions() for _, opt := range opts { if err := opt(&options); err != nil { panic(err) } } c := clusterInfo.Info() nodeInfos, err := cluster.MapProvisionNodeInfosToClusterNodeInfos(c.Nodes) if err != nil { panic(err) } nodeInfosByType, err := cluster.MapProvisionNodeInfosToNodeInfosByType(c.Nodes) if err != nil { panic(err) } infoW := &infoWrapper{ clusterInfo: c, nodes: nodeInfos, nodesByType: nodeInfosByType, } configProvider := cluster.ConfigClientProvider{ DefaultClient: options.TalosClient, TalosConfig: options.TalosConfig, } return &Adapter{ ConfigClientProvider: configProvider, KubernetesClient: cluster.KubernetesClient{ ClientProvider: &configProvider, ForceEndpoint: options.KubernetesEndpoint, }, APIBootstrapper: cluster.APIBootstrapper{ ClientProvider: &configProvider, Info: infoW, }, Info: infoW, } } ================================================ FILE: pkg/provision/internal/cniutils/cniutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package cniutils provides helper functions to parse CNI results. package cniutils import ( "fmt" types100 "github.com/containernetworking/cni/pkg/types/100" ) // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the // License is located at // // http://aws.amazon.com/apache2.0/ // // or in the "license" file accompanying this file. This file 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. // FilterBySandbox returns scans the provided list of interfaces and returns two lists: the first // are a list of interfaces with the provided sandboxID, the second are the other interfaces not // in that sandboxID. func FilterBySandbox(sandbox string, ifaces ...*types100.Interface) (in, out []*types100.Interface) { for _, iface := range ifaces { if iface.Sandbox == sandbox { in = append(in, iface) } else { out = append(out, iface) } } return in, out } // IfacesWithName scans the provided list of ifaces and returns the ones with the provided name. func IfacesWithName(name string, ifaces ...*types100.Interface) []*types100.Interface { var foundIfaces []*types100.Interface for _, iface := range ifaces { if iface.Name == name { foundIfaces = append(foundIfaces, iface) } } return foundIfaces } // VMTapPair takes a CNI result and returns the vm iface and the tap iface corresponding // to the provided vmID. See the vmconf package docs for details on the expected // vm and tap iface configurations. func VMTapPair(result *types100.Result, vmID string) (vmIface, tapIface *types100.Interface, err error) { vmIfaces, otherIfaces := FilterBySandbox(vmID, result.Interfaces...) if len(vmIfaces) > 1 { return nil, nil, fmt.Errorf( "expected to find at most 1 interface in sandbox %q, but instead found %d", vmID, len(vmIfaces)) } else if len(vmIfaces) == 0 { return nil, nil, fmt.Errorf("no pseudo-device for %s", vmID) } vmIface = vmIfaces[0] // As specified in the package docstring, the vm interface is given the same name as the // corresponding tap device outside the VM. The tap device, however, will be in a sandbox // corresponding to a network namespace path. tapName := vmIface.Name tapIfaces := IfacesWithName(tapName, otherIfaces...) if len(tapIfaces) > 1 { return nil, nil, fmt.Errorf( "expected to find at most 1 interface with name %q, but instead found %d", tapName, len(tapIfaces)) } else if len(tapIfaces) == 0 { return nil, nil, fmt.Errorf("device not found: %s", tapName) } tapIface = tapIfaces[0] return vmIface, tapIface, nil } ================================================ FILE: pkg/provision/internal/inmemhttp/inmemhttp.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package inmemhttp implements temporary HTTP server which is based off memory fs. package inmemhttp import ( "context" "net" "net/http" "slices" "strconv" "time" ) // Server is an in-memory http web server. type Server interface { AddFile(filename string, contents []byte) error AddHandler(pattern string, handler http.Handler) GetAddr() net.Addr Serve() Shutdown(ctx context.Context) error } type server struct { l net.Listener addr net.Addr srv *http.Server mux *http.ServeMux } // NewServer creates in-mem HTTP server. func NewServer(ctx context.Context, address string) (Server, error) { s := &server{ mux: http.NewServeMux(), } var err error s.l, err = (&net.ListenConfig{}).Listen(ctx, "tcp", address) if err != nil { return nil, err } s.addr = s.l.Addr() s.srv = &http.Server{ Handler: s.mux, } return s, nil } func (s *server) AddFile(filename string, contents []byte) error { contentsCopy := slices.Clone(contents) s.mux.Handle("/"+filename, http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { switch req.Method { case http.MethodHead: w.Header().Add("Content-Length", strconv.Itoa(len(contentsCopy))) w.WriteHeader(http.StatusOK) case http.MethodGet: w.Header().Add("Content-Length", strconv.Itoa(len(contentsCopy))) w.WriteHeader(http.StatusOK) w.Write(contentsCopy) //nolint:errcheck default: w.WriteHeader(http.StatusNotImplemented) } })) return nil } func (s *server) AddHandler(pattern string, handler http.Handler) { s.mux.Handle(pattern, handler) } func (s *server) GetAddr() net.Addr { return s.addr } func (s *server) Serve() { go s.srv.Serve(s.l) //nolint:errcheck } func (s *server) Shutdown(ctx context.Context) error { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() return s.srv.Shutdown(ctx) } ================================================ FILE: pkg/provision/internal/inmemhttp/inmemhttp_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package inmemhttp_test import ( "fmt" "io" "net/http" "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/provision/internal/inmemhttp" ) func TestServer(t *testing.T) { srv, err := inmemhttp.NewServer(t.Context(), "localhost:0") assert.NoError(t, err) contents := []byte("DEADBEEF") assert.NoError(t, srv.AddFile("test.txt", contents)) srv.Serve() defer srv.Shutdown(t.Context()) //nolint:errcheck resp, err := http.Get(fmt.Sprintf("http://%s/test.txt", srv.GetAddr())) //nolint:noctx assert.NoError(t, err) defer resp.Body.Close() //nolint:errcheck assert.Equal(t, http.StatusOK, resp.StatusCode) got, err := io.ReadAll(resp.Body) assert.NoError(t, err) assert.Equal(t, contents, got) assert.NoError(t, resp.Body.Close()) resp, err = http.Head(fmt.Sprintf("http://%s/test.txt", srv.GetAddr())) //nolint:noctx assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) assert.EqualValues(t, 8, resp.ContentLength) assert.NoError(t, resp.Body.Close()) resp, err = http.Get(fmt.Sprintf("http://%s/test.txt2", srv.GetAddr())) //nolint:noctx assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) assert.NoError(t, resp.Body.Close()) assert.NoError(t, srv.Shutdown(t.Context())) } ================================================ FILE: pkg/provision/options.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provision import ( "io" "os" "runtime" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" ) // Option controls Provisioner. type Option func(o *Options) error // WithLogWriter sets logging destination. func WithLogWriter(w io.Writer) Option { return func(o *Options) error { o.LogWriter = w return nil } } // WithKubernetesEndpoint specifies full external Kubernetes API endpoint to use when accessing Talos cluster. func WithKubernetesEndpoint(endpoint string) Option { return func(o *Options) error { o.KubernetesEndpoint = endpoint return nil } } // WithTalosConfig specifies talosconfig to use when acessing Talos cluster. func WithTalosConfig(talosConfig *clientconfig.Config) Option { return func(o *Options) error { o.TalosConfig = talosConfig return nil } } // WithTalosClient specifies client to use when acessing Talos cluster. func WithTalosClient(client *client.Client) Option { return func(o *Options) error { o.TalosClient = client return nil } } // WithBootlader enables or disables bootloader (bootloader is enabled by default). func WithBootlader(enabled bool) Option { return func(o *Options) error { o.BootloaderEnabled = enabled return nil } } // WithUEFI enables or disables UEFI boot on amd64 (default for amd64 is BIOS boot). func WithUEFI(enabled bool) Option { return func(o *Options) error { o.UEFIEnabled = enabled return nil } } // WithTPM1_2 enables or disables TPM1.2 emulation. func WithTPM1_2(enabled bool) Option { return func(o *Options) error { o.TPM1_2Enabled = enabled return nil } } // WithTPM2 enables or disables TPM2.0 emulation. func WithTPM2(enabled bool) Option { return func(o *Options) error { o.TPM2Enabled = enabled return nil } } // WithDebugShell drops into debug shell in initramfs. func WithDebugShell(enabled bool) Option { return func(o *Options) error { o.WithDebugShell = enabled return nil } } // WithIOMMU enables or disables IOMMU. func WithIOMMU(enabled bool) Option { return func(o *Options) error { o.IOMMUEnabled = enabled return nil } } // WithExtraUEFISearchPaths configures additional search paths to look for UEFI firmware. func WithExtraUEFISearchPaths(extraUEFISearchPaths []string) Option { return func(o *Options) error { o.ExtraUEFISearchPaths = extraUEFISearchPaths return nil } } // WithTargetArch specifies target architecture for the cluster. func WithTargetArch(arch string) Option { return func(o *Options) error { o.TargetArch = arch return nil } } // WithDockerPorts allows docker provisioner to expose ports on workers. func WithDockerPorts(ports []string) Option { return func(o *Options) error { o.DockerPorts = ports return nil } } // WithDockerPortsHostIP sets host IP for docker provisioner to expose ports on workers. func WithDockerPortsHostIP(hostIP string) Option { return func(o *Options) error { o.DockerPortsHostIP = hostIP return nil } } // WithDeleteOnErr informs the provisioner to delete cluster state folder on error. func WithDeleteOnErr(v bool) Option { return func(o *Options) error { o.DeleteStateOnErr = v return nil } } // WithSaveSupportArchivePath specifies path to save support archive on destroy. func WithSaveSupportArchivePath(path string) Option { return func(o *Options) error { o.SaveSupportArchivePath = path return nil } } // WithSaveClusterLogsArchivePath specifies path to save cluster logs archive on destroy. func WithSaveClusterLogsArchivePath(path string) Option { return func(o *Options) error { o.SaveClusterLogsArchivePath = path return nil } } // WithKMS inits KMS server in the provisioner. func WithKMS(endpoint string) Option { return func(o *Options) error { o.KMSEndpoint = endpoint return nil } } // WithJSONLogs specifies endpoint to send logs in JSON format. func WithJSONLogs(endpoint string) Option { return func(o *Options) error { o.JSONLogsEndpoint = endpoint return nil } } // WithSiderolinkAgent enables or disables siderolink agent. func WithSiderolinkAgent(v bool) Option { return func(o *Options) error { o.SiderolinkEnabled = v return nil } } // WithSkipInjectingExtraCmdline prevents injecting extra kernel args into EFI vars. func WithSkipInjectingExtraCmdline(v bool) Option { return func(o *Options) error { o.SkipInjectingExtraCmdline = v return nil } } // Options describes Provisioner parameters. type Options struct { LogWriter io.Writer TalosConfig *clientconfig.Config TalosClient *client.Client KubernetesEndpoint string TargetArch string // Enable bootloader by booting from disk image after install. BootloaderEnabled bool // SkipInjectingExtraCmdline prevents injecting extra kernel args, e.g., console=ttyS0, into the EFI vars. Only applies when UEFI is enabled. SkipInjectingExtraCmdline bool // Enable UEFI (for amd64), arm64 can only boot UEFI UEFIEnabled bool // Enable TPM 1.2 emulation using swtpm. TPM1_2Enabled bool // Enable TPM 2.0 emulation using swtpm. TPM2Enabled bool // Enable debug shell in the bootloader. WithDebugShell bool // Enable IOMMU for VMs and add a new PCI root controller and network interface. IOMMUEnabled bool // Configure additional search paths to look for UEFI firmware. ExtraUEFISearchPaths []string // Expose ports to worker machines in docker provisioner DockerPorts []string DockerPortsHostIP string SaveSupportArchivePath string SaveClusterLogsArchivePath string DeleteStateOnErr bool KMSEndpoint string JSONLogsEndpoint string SiderolinkEnabled bool } // DefaultOptions returns default options. func DefaultOptions() Options { return Options{ BootloaderEnabled: true, TargetArch: runtime.GOARCH, LogWriter: os.Stderr, DockerPortsHostIP: "0.0.0.0", } } ================================================ FILE: pkg/provision/providers/docker/create.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "context" "fmt" "path/filepath" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) // Create Talos cluster as a set of docker containers on docker network. func (p *provisioner) Create(ctx context.Context, request provision.ClusterRequest, opts ...provision.Option) (provision.Cluster, error) { var err error options := provision.DefaultOptions() for _, opt := range opts { if err = opt(&options); err != nil { return nil, err } } statePath := filepath.Join(request.StateDirectory, request.Name) fmt.Fprintf(options.LogWriter, "creating state directory in %q\n", statePath) state, err := provision.NewState(statePath, "docker", request.Name) if err != nil { return nil, fmt.Errorf("failed to initialize provisioner state: %w", err) } if err = p.ensureImageExists(ctx, request.Image, &options); err != nil { return nil, err } fmt.Fprintln(options.LogWriter, "creating network", request.Network.Name) if err = p.createNetwork(ctx, request.Network); err != nil { return nil, fmt.Errorf("unable to create or re-use a docker network: %w", err) } var nodeInfo []provision.NodeInfo //nolint:prealloc // this is created by p.createNodes fmt.Fprintln(options.LogWriter, "creating controlplane nodes") if nodeInfo, err = p.createNodes(ctx, request, request.Nodes.ControlPlaneNodes(), &options, true); err != nil { return nil, err } fmt.Fprintln(options.LogWriter, "creating worker nodes") var workerNodeInfo []provision.NodeInfo //nolint:prealloc // this is created by p.createNodes if workerNodeInfo, err = p.createNodes(ctx, request, request.Nodes.WorkerNodes(), &options, false); err != nil { return nil, err } nodeInfo = append(nodeInfo, workerNodeInfo...) state.ClusterInfo = provision.ClusterInfo{ ClusterName: request.Name, Network: provision.NetworkInfo{ Name: request.Network.Name, CIDRs: request.Network.CIDRs[:1], GatewayAddrs: request.Network.GatewayAddrs[:1], MTU: request.Network.MTU, }, Nodes: nodeInfo, KubernetesEndpoint: p.GetExternalKubernetesControlPlaneEndpoint(request.Network, constants.DefaultControlPlanePort), } if err := state.Save(); err != nil { return nil, err } res := &result{ clusterInfo: state.ClusterInfo, statePath: statePath, } return res, nil } ================================================ FILE: pkg/provision/providers/docker/destroy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "bytes" "context" "fmt" "io" "os" "path/filepath" "github.com/moby/moby/client" cl "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/provision" ) // Destroy Talos cluster as set of Docker nodes. // // Only cluster.Info().ClusterName and cluster.Info().Network.Name is being used. func (p *provisioner) Destroy(ctx context.Context, cluster provision.Cluster, opts ...provision.Option) error { options := provision.DefaultOptions() for _, opt := range opts { if err := opt(&options); err != nil { return err } } stateDirectoryPath, err := cluster.StatePath() if err != nil { return err } complete := false deleteStateDirectory := func(stateDir string, shouldDelete bool) error { if complete || !shouldDelete { return nil } complete = true return os.RemoveAll(stateDir) } defer deleteStateDirectory(stateDirectoryPath, options.DeleteStateOnErr) //nolint:errcheck if options.SaveClusterLogsArchivePath != "" { fmt.Fprintf(options.LogWriter, "saving cluster logs archive to %s\n", options.SaveClusterLogsArchivePath) p.saveContainerLogs(ctx, cluster, options.SaveClusterLogsArchivePath) } if options.SaveSupportArchivePath != "" { fmt.Fprintf(options.LogWriter, "saving support archive to %s\n", options.SaveSupportArchivePath) cl.Crashdump(ctx, cluster, options.LogWriter, options.SaveSupportArchivePath) } if err := p.destroyNodes(ctx, cluster.Info().ClusterName, &options); err != nil { return err } fmt.Fprintln(os.Stderr, "destroying network", cluster.Info().Network.Name) if err := p.destroyNetwork(ctx, cluster.Info().Network.Name); err != nil { return err } return deleteStateDirectory(stateDirectoryPath, true) } func (p *provisioner) saveContainerLogs(ctx context.Context, cluster provision.Cluster, logsArchivePath string) { containers, err := p.listNodes(ctx, cluster.Info().ClusterName) if err != nil { fmt.Fprintf(os.Stderr, "error listing containers: %s\n", err) return } statePath, err := cluster.StatePath() if err != nil { fmt.Fprintf(os.Stderr, "error getting state path: %s\n", err) return } for _, ctr := range containers { name := ctr.Names[0][1:] logs, err := p.client.ContainerLogs(ctx, ctr.ID, client.ContainerLogsOptions{ ShowStdout: true, ShowStderr: true, Tail: "1000", }) if err != nil { fmt.Fprintf(os.Stderr, "error querying container logs: %s\n", err) continue } logPath := filepath.Join(statePath, fmt.Sprintf("%s.log", name)) var logData bytes.Buffer if _, err := io.Copy(&logData, logs); err != nil { fmt.Fprintf(os.Stderr, "error reading container logs: %s\n", err) continue } if err := os.WriteFile(logPath, logData.Bytes(), 0o644); err != nil { fmt.Fprintf(os.Stderr, "error writing container logs: %s\n", err) continue } } cl.SaveClusterLogsArchive(statePath, logsArchivePath) } ================================================ FILE: pkg/provision/providers/docker/docker.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package docker implements Provisioner via docker. package docker import ( "context" "fmt" "net" "strconv" "github.com/moby/moby/client" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/provision" ) type provisioner struct { client *client.Client mappedKubernetesPort, mappedTalosAPIPort int } func getAvailableTCPPort(ctx context.Context) (int, error) { l, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:0") if err != nil { return 0, err } _, portStr, err := net.SplitHostPort(l.Addr().String()) if err != nil { l.Close() //nolint:errcheck return 0, err } err = l.Close() if err != nil { return 0, err } port, err := strconv.Atoi(portStr) if err != nil { return 0, err } return port, nil } // NewProvisioner initializes docker provisioner. func NewProvisioner(ctx context.Context) (provision.Provisioner, error) { p := &provisioner{} var err error p.client, err = client.New(client.FromEnv) if err != nil { return nil, err } p.mappedKubernetesPort, err = getAvailableTCPPort(ctx) if err != nil { return nil, fmt.Errorf("failed to get available port for Kubernetes API: %w", err) } p.mappedTalosAPIPort, err = getAvailableTCPPort(ctx) if err != nil { return nil, fmt.Errorf("failed to get available port for Talos API: %w", err) } return p, nil } // Close and release resources. func (p *provisioner) Close() error { if p.client != nil { return p.client.Close() } return nil } // GenOptions provides a list of additional config generate options. func (p *provisioner) GenOptions(networkReq provision.NetworkRequest, contract *config.VersionContract) ([]generate.Option, []bundle.Option) { var genOptions []generate.Option if contract.HostDNSEnabled() { genOptions = append(genOptions, generate.WithHostDNSForwardKubeDNSToHost(true), ) } if !contract.MultidocNetworkConfigSupported() { genOptions = append(genOptions, generate.WithNetworkOptions( v1alpha1.WithNetworkInterfaceIgnore(v1alpha1.IfaceByName("eth0")), ), ) } return genOptions, nil } // GetInClusterKubernetesControlPlaneEndpoint returns the Kubernetes control plane endpoint. func (p *provisioner) GetInClusterKubernetesControlPlaneEndpoint(networkReq provision.NetworkRequest, controlPlanePort int) string { // Docker doesn't have a loadbalancer, so use the first container IP. return "https://" + nethelpers.JoinHostPort(networkReq.CIDRs[0].Addr().Next().Next().String(), controlPlanePort) } // GetExternalKubernetesControlPlaneEndpoint returns the Kubernetes control plane endpoint. func (p *provisioner) GetExternalKubernetesControlPlaneEndpoint(provision.NetworkRequest, int) string { // return a mapped to the localhost first container Kubernetes API endpoint. return "https://" + nethelpers.JoinHostPort("127.0.0.1", p.mappedKubernetesPort) } // GetTalosAPIEndpoints returns a list of Talos API endpoints. func (p *provisioner) GetTalosAPIEndpoints(provision.NetworkRequest) []string { // return a mapped to the localhost first container Talos API endpoint. return []string{nethelpers.JoinHostPort("127.0.0.1", p.mappedTalosAPIPort)} } // UserDiskName not implemented for docker. func (p *provisioner) UserDiskName(index int) string { return "" } // GetFirstInterface returns first network interface name. func (p *provisioner) GetFirstInterface() v1alpha1.IfaceSelector { return v1alpha1.IfaceByName(p.GetFirstInterfaceName()) } // GetFirstInterfaceName returns first network interface name as string. func (p *provisioner) GetFirstInterfaceName() string { return "eth0" } ================================================ FILE: pkg/provision/providers/docker/docker_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/provision/providers/docker/image.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "context" "fmt" "io" "github.com/distribution/reference" "github.com/moby/moby/client" "github.com/siderolabs/talos/pkg/provision" ) func (p *provisioner) ensureImageExists(ctx context.Context, containerImage string, options *provision.Options) error { // In order to pull an image, the reference must be in canonical // format (e.g. domain/repo/image:tag). ref, err := reference.ParseNormalizedNamed(containerImage) if err != nil { return err } containerImage = ref.String() // To filter the images, we need a familiar name and a tag // (e.g. domain/repo/image:tag => repo/image:tag). familiarName := reference.FamiliarName(ref) tag := "" if tagged, isTagged := ref.(reference.Tagged); isTagged { tag = tagged.Tag() } filters := client.Filters{} filters.Add("reference", familiarName+":"+tag) images, err := p.client.ImageList(ctx, client.ImageListOptions{Filters: filters}) if err != nil { return err } if len(images.Items) == 0 { fmt.Fprintln(options.LogWriter, "downloading", containerImage) var reader io.ReadCloser if reader, err = p.client.ImagePull(ctx, containerImage, client.ImagePullOptions{}); err != nil { return err } //nolint:errcheck defer reader.Close() if _, err = io.Copy(io.Discard, reader); err != nil { return err } } return nil } ================================================ FILE: pkg/provision/providers/docker/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "context" "fmt" "strconv" "github.com/hashicorp/go-multierror" "github.com/moby/moby/api/types/network" "github.com/moby/moby/client" "github.com/siderolabs/talos/pkg/provision" ) // createNetwork will take a network request and check if a network with the same name + cidr exists. // If so, it simply returns without error and assumes we will re-use that network. Otherwise it will create a new one. func (p *provisioner) createNetwork(ctx context.Context, req provision.NetworkRequest) error { existingNet, err := p.listNetworks(ctx, req.Name) if err != nil { return err } // If named net already exists, see if we can reuse it if len(existingNet) > 0 { if existingNet[0].IPAM.Config[0].Subnet != req.CIDRs[0] { return fmt.Errorf("existing network has differing cidr: %s vs %s", existingNet[0].IPAM.Config[0].Subnet, req.CIDRs[0].String()) } // CIDRs match, we'll reuse return nil } // Create new net options := client.NetworkCreateOptions{ Labels: map[string]string{ "talos.owned": "true", "talos.cluster.name": req.Name, }, IPAM: &network.IPAM{ Config: []network.IPAMConfig{ { Subnet: req.CIDRs[0], }, }, }, Options: map[string]string{ "com.docker.network.driver.mtu": strconv.Itoa(req.MTU), }, } _, err = p.client.NetworkCreate(ctx, req.Name, options) return err } func (p *provisioner) listNetworks(ctx context.Context, name string) ([]network.Summary, error) { filters := client.Filters{} filters.Add("label", "talos.owned=true") filters.Add("label", "talos.cluster.name="+name) options := client.NetworkListOptions{ Filters: filters, } result, err := p.client.NetworkList(ctx, options) if err != nil { return nil, err } return result.Items, nil } func (p *provisioner) destroyNetwork(ctx context.Context, name string) error { networks, err := p.listNetworks(ctx, name) if err != nil { return err } var result *multierror.Error for _, network := range networks { if _, err := p.client.NetworkRemove(ctx, network.ID, client.NetworkRemoveOptions{}); err != nil { result = multierror.Append(result, err) } } return result.ErrorOrNil() } ================================================ FILE: pkg/provision/providers/docker/node.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "context" "encoding/base64" "errors" "fmt" "net/netip" "runtime" "slices" "strings" "github.com/hashicorp/go-multierror" "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/mount" "github.com/moby/moby/api/types/network" "github.com/moby/moby/client" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) type portMap struct { exposedPorts network.PortSet portBindings network.PortMap } func (p *provisioner) createNodes( ctx context.Context, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest, options *provision.Options, isControlplane bool, ) ([]provision.NodeInfo, error) { errCh := make(chan error) nodeCh := make(chan provision.NodeInfo, len(nodeReqs)) for i, nodeReq := range nodeReqs { go func(i int, nodeReq provision.NodeRequest) { if i == 0 && isControlplane { hostPrefix := "" // on Linux, limit listening to localhost, on other OSes Docker engine VM is separate from the host if runtime.GOOS == "linux" { hostPrefix = "127.0.0.1:" } nodeReq.Ports = append( []string{ fmt.Sprintf("%s%d:%d/tcp", hostPrefix, p.mappedTalosAPIPort, constants.ApidPort), fmt.Sprintf("%s%d:%d/tcp", hostPrefix, p.mappedKubernetesPort, constants.DefaultControlPlanePort), }, nodeReq.Ports..., ) } nodeInfo, err := p.createNode(ctx, clusterReq, nodeReq, options) if err == nil { nodeCh <- nodeInfo } errCh <- err }(i, nodeReq) } var multiErr *multierror.Error for range nodeReqs { multiErr = multierror.Append(multiErr, <-errCh) } close(nodeCh) nodesInfo := make([]provision.NodeInfo, 0, len(nodeReqs)) for nodeInfo := range nodeCh { nodesInfo = append(nodesInfo, nodeInfo) } return nodesInfo, multiErr.ErrorOrNil() } //nolint:gocyclo func (p *provisioner) createNode(ctx context.Context, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, options *provision.Options) (provision.NodeInfo, error) { env := []string{ "PLATFORM=container", fmt.Sprintf("TALOSSKU=%dCPU-%dRAM", nodeReq.NanoCPUs/(1000*1000*1000), nodeReq.Memory/(1024*1024)), } if !nodeReq.SkipInjectingConfig { cfg, err := nodeReq.Config.EncodeString() if err != nil { return provision.NodeInfo{}, err } env = append(env, "USERDATA="+base64.StdEncoding.EncodeToString([]byte(cfg))) } // Create the container config. containerConfig := &container.Config{ Hostname: nodeReq.Name, Image: clusterReq.Image, Env: env, Labels: map[string]string{ "talos.owned": "true", "talos.cluster.name": clusterReq.Name, "talos.type": nodeReq.Type.String(), }, } // Create the host config. mounts := make([]mount.Mount, 0, len(constants.Overlays)+5+len(nodeReq.Mounts)) for _, path := range []string{"/run", "/system", "/tmp"} { mounts = append(mounts, mount.Mount{ Type: mount.TypeTmpfs, Target: path, }) } for _, path := range append( []string{constants.EphemeralMountPoint, constants.StateMountPoint}, xslices.Map(constants.Overlays, func(overlay constants.SELinuxLabeledPath) string { return overlay.Path })..., ) { mounts = append(mounts, mount.Mount{ Type: mount.TypeVolume, Target: path, }) } mounts = slices.Concat(mounts, nodeReq.Mounts) hostConfig := &container.HostConfig{ Privileged: true, SecurityOpt: []string{"seccomp:unconfined"}, Resources: container.Resources{ NanoCPUs: nodeReq.NanoCPUs, Memory: nodeReq.Memory, }, ReadonlyRootfs: true, Mounts: mounts, } if !clusterReq.Network.DockerDisableIPv6 { // enable IPv6 hostConfig.Sysctls = map[string]string{ "net.ipv6.conf.all.disable_ipv6": "0", } } // Ensure that the container is created in the talos network. networkConfig := &network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ clusterReq.Network.Name: { NetworkID: clusterReq.Network.Name, }, }, } // Mutate the container configurations based on the node type. if nodeReq.Type == machine.TypeInit || nodeReq.Type == machine.TypeControlPlane { portsToOpen := nodeReq.Ports if len(options.DockerPorts) > 0 { portsToOpen = append(portsToOpen, options.DockerPorts...) } generatedPortMap, err := genPortMap(portsToOpen, options.DockerPortsHostIP) if err != nil { return provision.NodeInfo{}, err } containerConfig.ExposedPorts = generatedPortMap.exposedPorts hostConfig.PortBindings = generatedPortMap.portBindings if nodeReq.IPs == nil { return provision.NodeInfo{}, errors.New("an IP address must be provided when creating a controlplane node") } } if nodeReq.IPs != nil { networkConfig.EndpointsConfig[clusterReq.Network.Name].IPAMConfig = &network.EndpointIPAMConfig{IPv4Address: nodeReq.IPs[0]} } // Create the container. resp, err := p.client.ContainerCreate(ctx, client.ContainerCreateOptions{ Config: containerConfig, HostConfig: hostConfig, NetworkingConfig: networkConfig, Name: nodeReq.Name, }) if err != nil { return provision.NodeInfo{}, err } // Start the container. _, err = p.client.ContainerStart(ctx, resp.ID, client.ContainerStartOptions{}) if err != nil { return provision.NodeInfo{}, err } // Inspect the container. info, err := p.client.ContainerInspect(ctx, resp.ID, client.ContainerInspectOptions{}) if err != nil { return provision.NodeInfo{}, err } // Get the container's IP address. var addr netip.Addr if network, ok := info.Container.NetworkSettings.Networks[clusterReq.Network.Name]; ok { ip := network.IPAddress if ip.IsUnspecified() && network.IPAMConfig != nil { ip = network.IPAMConfig.IPv4Address } addr = ip } nodeInfo := provision.NodeInfo{ ID: info.Container.ID, Name: info.Container.Name, Type: nodeReq.Type, NanoCPUs: nodeReq.NanoCPUs, Memory: nodeReq.Memory, IPs: []netip.Addr{addr}, } return nodeInfo, nil } func (p *provisioner) listNodes(ctx context.Context, clusterName string) ([]container.Summary, error) { filters := client.Filters{} filters.Add("label", "talos.owned=true") filters.Add("label", "talos.cluster.name="+clusterName) summary, err := p.client.ContainerList(ctx, client.ContainerListOptions{All: true, Filters: filters}) if err != nil { return nil, err } return summary.Items, nil } func (p *provisioner) destroyNodes(ctx context.Context, clusterName string, options *provision.Options) error { containers, err := p.listNodes(ctx, clusterName) if err != nil { return err } errCh := make(chan error) for _, ctr := range containers { go func(ctr container.Summary) { fmt.Fprintln(options.LogWriter, "destroying node", ctr.Names[0][1:]) _, err := p.client.ContainerRemove(ctx, ctr.ID, client.ContainerRemoveOptions{RemoveVolumes: true, Force: true}) errCh <- err }(ctr) } var multiErr *multierror.Error for range containers { multiErr = multierror.Append(multiErr, <-errCh) } return multiErr.ErrorOrNil() } func genPortMap(portList []string, defaultHostIP string) (portMap, error) { portSetRet := network.PortSet{} portMapRet := network.PortMap{} for _, port := range portList { portsAndHost, protocol, ok := strings.Cut(port, "/") if !ok { return portMap{}, errors.New("incorrect format for exposed port/protocols") } expodedPortsAndHost := strings.Split(portsAndHost, ":") var containerPort, hostPort string hostIP := defaultHostIP switch len(expodedPortsAndHost) { case 2: hostPort, containerPort = expodedPortsAndHost[0], expodedPortsAndHost[1] case 3: hostIP, hostPort, containerPort = expodedPortsAndHost[0], expodedPortsAndHost[1], expodedPortsAndHost[2] default: return portMap{}, errors.New("incorrect format for exposed ports") } natPort, err := network.ParsePort(containerPort + "/" + protocol) if err != nil { return portMap{}, err } hostAddr, err := netip.ParseAddr(hostIP) if err != nil { return portMap{}, fmt.Errorf("invalid host IP address '%s': %w", hostIP, err) } portSetRet[natPort] = struct{}{} portMapRet[natPort] = append(portMapRet[natPort], network.PortBinding{ HostIP: hostAddr, HostPort: hostPort, }) } return portMap{portSetRet, portMapRet}, nil } ================================================ FILE: pkg/provision/providers/docker/reflect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "context" "net" "net/netip" "strconv" "strings" "github.com/moby/moby/client" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) //nolint:gocyclo func (p *provisioner) Reflect(ctx context.Context, clusterName, stateDirectory string) (provision.Cluster, error) { res := &result{ clusterInfo: provision.ClusterInfo{ ClusterName: clusterName, }, statePath: stateDirectory, } // find network assuming network name == cluster name networks, err := p.listNetworks(ctx, clusterName) if err != nil { return nil, err } if len(networks) > 0 { network := networks[0] cidr := network.IPAM.Config[0].Subnet res.clusterInfo.Network.Name = network.Name res.clusterInfo.Network.CIDRs = []netip.Prefix{cidr} res.clusterInfo.Network.GatewayAddrs = []netip.Addr{network.IPAM.Config[0].Gateway} mtuStr, ok := network.Options["com.docker.network.driver.mtu"] if !ok { mtuStr = network.Options["mtu"] // Use the podman version of the option } res.clusterInfo.Network.MTU, err = strconv.Atoi(mtuStr) if err != nil { return nil, err } } // find nodes (containers) nodes, err := p.listNodes(ctx, clusterName) if err != nil { return nil, err } var mappedKubernetesPort string for _, node := range nodes { t, err := machine.ParseType(node.Labels["talos.type"]) if err != nil { return nil, err } container, err := p.client.ContainerInspect(ctx, node.ID, client.ContainerInspectOptions{}) if err != nil { return nil, err } var ips []netip.Addr if network, ok := node.NetworkSettings.Networks[res.clusterInfo.Network.Name]; ok { ip := network.IPAddress if ip.IsUnspecified() && network.IPAMConfig != nil { ip = network.IPAMConfig.IPv4Address } ips = append(ips, ip) } for port, portBinding := range container.Container.HostConfig.PortBindings { if port.Num() == constants.DefaultControlPlanePort { for _, binding := range portBinding { mappedKubernetesPort = binding.HostPort } } } res.clusterInfo.Nodes = append(res.clusterInfo.Nodes, provision.NodeInfo{ ID: node.ID, Name: strings.TrimLeft(node.Names[0], "/"), Type: t, IPs: ips, NanoCPUs: container.Container.HostConfig.Resources.NanoCPUs, Memory: container.Container.HostConfig.Resources.Memory, }) } if mappedKubernetesPort != "" { res.clusterInfo.KubernetesEndpoint = "https://" + net.JoinHostPort("127.0.0.1", mappedKubernetesPort) } return res, nil } ================================================ FILE: pkg/provision/providers/docker/result.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package docker import ( "errors" "github.com/siderolabs/talos/pkg/provision" ) type result struct { clusterInfo provision.ClusterInfo statePath string } func (res *result) Provisioner() string { return "docker" } func (res *result) Info() provision.ClusterInfo { return res.clusterInfo } func (res *result) StatePath() (string, error) { if res.statePath == "" { return "", errors.New("state path is not used for docker provisioner") } return res.statePath, nil } ================================================ FILE: pkg/provision/providers/factory.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package providers import ( "context" "fmt" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers/docker" ) const ( // QemuProviderName is the name of the qemu provider. QemuProviderName = "qemu" // DockerProviderName is the name of the docker provider. DockerProviderName = "docker" ) // Factory instantiates provision provider by name. func Factory(ctx context.Context, name string) (provision.Provisioner, error) { if err := IsValidProvider(name); err != nil { return nil, err } switch name { case DockerProviderName: return docker.NewProvisioner(ctx) case QemuProviderName: return newQemu(ctx) } panic("unknown valid provisioner") } // IsValidProvider returns an error if the passed provider doesn't exist. func IsValidProvider(name string) error { switch name { case QemuProviderName, DockerProviderName: return nil } return fmt.Errorf("unsupported provisioner %q", name) } ================================================ FILE: pkg/provision/providers/qemu/arch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "fmt" "os/exec" "path/filepath" "slices" ) // Arch abstracts away differences between different architectures. type Arch string // Arch constants. const ( ArchAmd64 Arch = "amd64" ArchArm64 Arch = "arm64" ) // Valid returns an error if the architecture is not supported. func (arch Arch) Valid() error { switch arch { case ArchArm64, ArchAmd64: return nil default: return fmt.Errorf("unsupported arch: %q", arch) } } // QemuArch defines which qemu binary to use. func (arch Arch) QemuArch() string { switch arch { case ArchAmd64: return "x86_64" case ArchArm64: return "aarch64" default: panic("unsupported architecture") } } // QemuMachine defines the machine type for qemu. func (arch Arch) QemuMachine() string { switch arch { case ArchAmd64: return "q35" case ArchArm64: return "virt,gic-version=max" default: panic("unsupported architecture") } } // Console defines proper argument for the kernel to send logs to serial console. func (arch Arch) Console() string { switch arch { case ArchAmd64: return "ttyS0" case ArchArm64: return "ttyAMA0,115200n8" default: panic("unsupported architecture") } } // PFlash for UEFI boot. type PFlash struct { Size int64 SourcePaths []string } // PFlash returns settings for parallel flash. func (arch Arch) PFlash(uefiEnabled bool, extraUEFISearchPaths []string) []PFlash { switch arch { case ArchArm64: // default search paths uefiSourcePathPrefixes := []string{ //nolint:prealloc // complex length "/usr/share/AAVMF", // most standard location "/usr/share/qemu-efi-aarch64", "/usr/share/OVMF", "/usr/share/edk2/aarch64", // Fedora "/usr/share/edk2/experimental", // Fedora "/opt/homebrew/share/qemu", // Darwin } // Secure boot enabled firmware files uefiSourceFiles := []string{ "AAVMF_CODE.secboot.fd", // debian, EFI vars not protected "QEMU_EFI.secboot.testonly.fd", // Fedora, ref: https://bugzilla.redhat.com/show_bug.cgi?id=1882135 } // Non-secure boot firmware files uefiSourceFilesInsecure := []string{ "AAVMF_CODE.fd", "QEMU_EFI.fd", "OVMF.stateless.fd", "edk2-aarch64-code.fd", } // Empty vars files uefiVarsFiles := []string{ "AAVMF_VARS.fd", "QEMU_VARS.fd", "edk2-arm-vars.fd", } // Append extra search paths uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...) uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure) return []PFlash{ { Size: 64 * 1024 * 1024, SourcePaths: uefiSourcePaths, }, { SourcePaths: uefiVarsPaths, Size: 64 * 1024 * 1024, }, } case ArchAmd64: if !uefiEnabled { return nil } // Default search paths uefiSourcePathPrefixes := []string{ //nolint:prealloc // complex length "/usr/share/ovmf", "/usr/share/OVMF", "/usr/share/qemu", "/usr/share/ovmf/x64", // Arch Linux "/opt/homebrew/share/qemu", // Darwin } // Secure boot enabled firmware files uefiSourceFiles := []string{ "OVMF_CODE_4M.secboot.fd", "OVMF_CODE.secboot.4m.fd", // Arch Linux "OVMF_CODE.secboot.fd", "OVMF.secboot.fd", "edk2-x86_64-secure-code.fd", // Alpine Linux, Darwin "ovmf-x86_64-ms-4m-code.bin", } // Non-secure boot firmware files uefiSourceFilesInsecure := []string{ "OVMF_CODE_4M.fd", "OVMF_CODE.4m.fd", // Arch Linux "OVMF_CODE.fd", "OVMF.fd", "edk2-x86_64-code.fd", // Darwin "ovmf-x86_64-4m-code.bin", } // Empty vars files uefiVarsFiles := []string{ "OVMF_VARS_4M.fd", "OVMF_VARS.4m.fd", // Arch Linux "OVMF_VARS.fd", "edk2-i386-vars.fd", // Darwin "ovmf-x86_64-4m-vars.bin", } // Append extra search paths uefiSourcePathPrefixes = append(uefiSourcePathPrefixes, extraUEFISearchPaths...) uefiSourcePaths, uefiVarsPaths := generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure) return []PFlash{ { Size: 0, SourcePaths: uefiSourcePaths, }, { Size: 0, SourcePaths: uefiVarsPaths, }, } default: return nil } } func generateUEFIPFlashList(uefiSourcePathPrefixes, uefiSourceFiles, uefiVarsFiles, uefiSourceFilesInsecure []string) (uefiSourcePaths, uefiVarsPaths []string) { for _, p := range uefiSourcePathPrefixes { for _, f := range uefiSourceFiles { uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, f)) } for _, f := range uefiVarsFiles { uefiVarsPaths = append(uefiVarsPaths, filepath.Join(p, f)) } } for _, p := range uefiSourcePathPrefixes { for _, f := range uefiSourceFilesInsecure { uefiSourcePaths = append(uefiSourcePaths, filepath.Join(p, f)) } } return uefiSourcePaths, uefiVarsPaths } // QemuExecutable returns name of qemu executable for the arch. func (arch Arch) QemuExecutable() string { binaries := []string{ "qemu-system-" + arch.QemuArch(), "qemu-kvm", "/usr/libexec/qemu-kvm", } for _, binary := range binaries { if path, err := exec.LookPath(binary); err == nil { return path } } return "" } // TPMDeviceArgs returns arguments for qemu to enable TPM device. func (arch Arch) TPMDeviceArgs(socketPath string) []string { tpmDeviceArgs := []string{ "-chardev", fmt.Sprintf("socket,id=chrtpm,path=%s", socketPath), "-tpmdev", "emulator,id=tpm0,chardev=chrtpm", "-device", } switch arch { case ArchAmd64: return slices.Concat(tpmDeviceArgs, []string{"tpm-tis,tpmdev=tpm0"}) case ArchArm64: return slices.Concat(tpmDeviceArgs, []string{"tpm-tis-device,tpmdev=tpm0"}) default: panic("unsupported architecture") } } func (arch Arch) getMachineArgs(iommu bool) []string { args := arch.QemuMachine() if arch.acceleratorAvailable() { args += ",accel=" + accelerator } // ref: https://wiki.qemu.org/Features/VT-d if iommu { args += ",kernel-irqchip=split" } if arch == ArchAmd64 { args += ",smm=on" } return []string{"-machine", args} } ================================================ FILE: pkg/provision/providers/qemu/arch_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import "runtime" const accelerator = "hvf" func (arch Arch) acceleratorAvailable() bool { // hvf only supports emulating native architectures return string(arch) == runtime.GOARCH } ================================================ FILE: pkg/provision/providers/qemu/arch_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import "runtime" const accelerator = "kvm" func (arch Arch) acceleratorAvailable() bool { if err := checkKVM(); err != nil { return false } // kvm only supports emulating native architectures return string(arch) == runtime.GOARCH } ================================================ FILE: pkg/provision/providers/qemu/controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "sync" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) // PowerState is current VM power state. type PowerState string // Virtual machine power state. const ( PoweredOn PowerState = "on" PoweredOff PowerState = "off" ) // VMCommand is a translated VM command. type VMCommand string // Virtual machine commands. const ( VMCommandStart VMCommand = "start" VMCommandStop VMCommand = "stop" ) // Controller supports IPMI-like machine control. type Controller struct { mu sync.Mutex state PowerState forcePXEBoot bool commandsCh chan VMCommand } // NewController initializes controller in "powered on" state. func NewController() *Controller { return &Controller{ state: PoweredOn, commandsCh: make(chan VMCommand), } } // PowerOn implements vm.Controller interface. func (c *Controller) PowerOn() error { c.mu.Lock() if c.state == PoweredOn { c.mu.Unlock() return nil } c.state = PoweredOn c.mu.Unlock() c.commandsCh <- VMCommandStart return nil } // PowerOff implements vm.Controller interface. func (c *Controller) PowerOff() error { c.mu.Lock() if c.state == PoweredOff { c.mu.Unlock() return nil } c.state = PoweredOff c.mu.Unlock() c.commandsCh <- VMCommandStop return nil } // Reboot implements vm.Controller interface. func (c *Controller) Reboot() error { c.mu.Lock() if c.state == PoweredOff { c.state = PoweredOn c.mu.Unlock() c.commandsCh <- VMCommandStart return nil } c.mu.Unlock() c.commandsCh <- VMCommandStop return nil } // PXEBootOnce implements vm.Controller interface. func (c *Controller) PXEBootOnce() error { c.mu.Lock() defer c.mu.Unlock() c.forcePXEBoot = true return nil } // Status implements vm.Controller interface. func (c *Controller) Status() vm.Status { return vm.Status{ PoweredOn: c.PowerState() == PoweredOn, } } // PowerState returns current power state. func (c *Controller) PowerState() PowerState { c.mu.Lock() defer c.mu.Unlock() return c.state } // ForcePXEBoot returns whether next boot should be PXE boot. func (c *Controller) ForcePXEBoot() bool { c.mu.Lock() defer c.mu.Unlock() if c.forcePXEBoot { c.forcePXEBoot = false return true } return false } // CommandsCh returns channel with commands. func (c *Controller) CommandsCh() <-chan VMCommand { return c.commandsCh } ================================================ FILE: pkg/provision/providers/qemu/create.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "fmt" "path/filepath" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision" ) // Create Talos cluster as a set of qemu VMs. // //nolint:gocyclo,cyclop func (p *provisioner) Create(ctx context.Context, request provision.ClusterRequest, opts ...provision.Option) (provision.Cluster, error) { options := provision.DefaultOptions() for _, opt := range opts { if err := opt(&options); err != nil { return nil, err } } arch := Arch(options.TargetArch) if err := arch.Valid(); err != nil { return nil, err } if err := p.preflightChecks(ctx, request, options, arch); err != nil { return nil, err } statePath := filepath.Join(request.StateDirectory, request.Name) fmt.Fprintf(options.LogWriter, "creating state directory in %q\n", statePath) state, err := provision.NewState( statePath, p.Name, request.Name, ) if err != nil { return nil, err } if options.SiderolinkEnabled { fmt.Fprintln(options.LogWriter, "creating siderolink agent") if err = p.CreateSiderolinkAgent(state, request); err != nil { return nil, err } fmt.Fprintln(options.LogWriter, "created siderolink agent") } fmt.Fprintln(options.LogWriter, "creating network", request.Network.Name) if err = p.CreateNetwork(ctx, state, request.Network, options); err != nil { return nil, fmt.Errorf("unable to provision CNI network: %w", err) } fmt.Fprintln(options.LogWriter, "creating load balancer") if err = p.CreateLoadBalancer(state, request); err != nil { return nil, fmt.Errorf("error creating loadbalancer: %w", err) } fmt.Fprintln(options.LogWriter, "creating dnsd") if err = p.CreateDNSd(state, request); err != nil { return nil, fmt.Errorf("error creating dnsd: %w", err) } if hasVirtiofsDisk(request.Nodes.WorkerNodes()) { virtiofsdBin, err := p.FindVirtiofsd() if err != nil { return nil, fmt.Errorf("virtiofsd lookup: %w", err) } if virtiofsdBin != "" { fmt.Fprintln(options.LogWriter, "creating virtiofsd using", virtiofsdBin) if err = p.CreateVirtiofsd(state, request, virtiofsdBin); err != nil { return nil, fmt.Errorf("error creating virtiofsd: %w", err) } } else { fmt.Fprintln(options.LogWriter, "virtiofsd not found, skipping") } } if request.Network.ImageCachePath != "" { fmt.Fprintln(options.LogWriter, "creating image cache") if err = p.CreateImageCache(state, request); err != nil { return nil, fmt.Errorf("error creating image cache: %w", err) } } if options.KMSEndpoint != "" { fmt.Fprintln(options.LogWriter, "creating KMS server") if err = p.CreateKMS(state, request, options); err != nil { return nil, fmt.Errorf("error creating KMS server: %w", err) } } if options.JSONLogsEndpoint != "" { fmt.Fprintln(options.LogWriter, "creating JSON logs server") if err = p.CreateJSONLogs(state, request, options); err != nil { return nil, fmt.Errorf("error creating JSON logs server: %w", err) } } var nodeInfo []provision.NodeInfo //nolint:prealloc // this is created by p.createNodes fmt.Fprintln(options.LogWriter, "creating controlplane nodes") if nodeInfo, err = p.createNodes(ctx, state, request, request.Nodes.ControlPlaneNodes(), &options); err != nil { return nil, err } // On darwin, qemu creates the bridge interface to which the dhcpd server is attached to, so at least one machine has to be created first. fmt.Fprintln(options.LogWriter, "creating dhcpd") if err = p.CreateDHCPd(ctx, state, request); err != nil { return nil, fmt.Errorf("error creating dhcpd: %w", err) } fmt.Fprintln(options.LogWriter, "creating worker nodes") var workerNodeInfo []provision.NodeInfo if workerNodeInfo, err = p.createNodes(ctx, state, request, request.Nodes.WorkerNodes(), &options); err != nil { return nil, err } var pxeNodeInfo []provision.NodeInfo pxeNodes := request.Nodes.PXENodes() if len(pxeNodes) > 0 { fmt.Fprintln(options.LogWriter, "creating PXE nodes") if pxeNodeInfo, err = p.createNodes(ctx, state, request, pxeNodes, &options); err != nil { return nil, err } } nodeInfo = append(nodeInfo, workerNodeInfo...) lbPort := constants.DefaultControlPlanePort if len(request.Network.LoadBalancerPorts) > 0 { lbPort = request.Network.LoadBalancerPorts[0] } state.ClusterInfo = provision.ClusterInfo{ ClusterName: request.Name, Network: provision.NetworkInfo{ Name: request.Network.Name, CIDRs: request.Network.CIDRs, NoMasqueradeCIDRs: request.Network.NoMasqueradeCIDRs, GatewayAddrs: request.Network.GatewayAddrs, MTU: request.Network.MTU, }, Nodes: nodeInfo, ExtraNodes: pxeNodeInfo, KubernetesEndpoint: p.GetExternalKubernetesControlPlaneEndpoint(request.Network, lbPort), } if err := state.Save(); err != nil { return nil, err } return state, nil } func hasVirtiofsDisk(nodes []provision.NodeRequest) bool { for _, node := range nodes { for _, disk := range node.Disks { if disk.Driver == "virtiofs" { return true } } } return false } ================================================ FILE: pkg/provision/providers/qemu/destroy.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "fmt" "os" cl "github.com/siderolabs/talos/pkg/cluster" "github.com/siderolabs/talos/pkg/provision" ) // Destroy Talos cluster as set of qemu VMs. // //nolint:gocyclo,cyclop func (p *provisioner) Destroy(ctx context.Context, cluster provision.Cluster, opts ...provision.Option) error { options := provision.DefaultOptions() for _, opt := range opts { if err := opt(&options); err != nil { return err } } stateDirectoryPath, err := cluster.StatePath() if err != nil { return err } complete := false deleteStateDirectory := func(stateDir string, shouldDelete bool) error { if complete || !shouldDelete { return nil } complete = true return os.RemoveAll(stateDir) } defer deleteStateDirectory(stateDirectoryPath, options.DeleteStateOnErr) //nolint:errcheck if options.SaveSupportArchivePath != "" { fmt.Fprintf(options.LogWriter, "saving support archive to %s\n", options.SaveSupportArchivePath) cl.Crashdump(ctx, cluster, options.LogWriter, options.SaveSupportArchivePath) } fmt.Fprintln(options.LogWriter, "stopping VMs") if err := p.DestroyNodes(cluster.Info(), &options); err != nil { return err } if err := p.destroyVirtualTPMs(cluster.Info()); err != nil { return err } state, ok := cluster.(*provision.State) if !ok { return fmt.Errorf("error inspecting QEMU state, %#+v", cluster) } if err := p.destroySHMFiles(state); err != nil { return err } fmt.Fprintln(options.LogWriter, "removing dhcpd") if err := p.DestroyDHCPd(state); err != nil { return fmt.Errorf("error stopping dhcpd: %w", err) } fmt.Fprintln(options.LogWriter, "removing image cache") if err = p.DestroyImageCache(state); err != nil { return fmt.Errorf("error stopping image cache: %w", err) } fmt.Fprintln(options.LogWriter, "removing virtiofsd") if err := p.DestroyVirtiofsd(state); err != nil { return fmt.Errorf("error stopping virtiofsd: %w", err) } fmt.Fprintln(options.LogWriter, "removing dnsd") if err := p.DestroyDNSd(state); err != nil { return fmt.Errorf("error stopping dnsd: %w", err) } fmt.Fprintln(options.LogWriter, "removing load balancer") if err := p.DestroyLoadBalancer(state); err != nil { return fmt.Errorf("error stopping loadbalancer: %w", err) } fmt.Fprintln(options.LogWriter, "removing kms") if err := p.DestroyKMS(state); err != nil { return err } fmt.Fprintln(options.LogWriter, "removing network") if err := p.DestroyNetwork(state); err != nil { return err } fmt.Fprintln(options.LogWriter, "removing siderolink agent") if err := p.DestroySiderolinkAgent(state); err != nil { return err } fmt.Fprintln(options.LogWriter, "removing state directory") if options.SaveClusterLogsArchivePath != "" { fmt.Fprintf(options.LogWriter, "saving cluster logs archive to %s\n", options.SaveClusterLogsArchivePath) cl.SaveClusterLogsArchive(stateDirectoryPath, options.SaveClusterLogsArchivePath) } fmt.Fprintln(options.LogWriter, "removing json logs") if err := p.DestroyJSONLogs(state); err != nil { return err } return deleteStateDirectory(stateDirectoryPath, true) } ================================================ FILE: pkg/provision/providers/qemu/launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "errors" "fmt" "log" "net" "net/netip" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "time" "github.com/google/uuid" "github.com/siderolabs/go-blockdevice/v2/blkid" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) // LaunchConfig is passed in to the Launch function over stdin. type LaunchConfig struct { StatePath string // VM options DiskPaths []string DiskDrivers []string DiskTags []string DiskSerials []string DiskBlockSizes []uint VCPUCount int64 MemSize int64 MemShmPath string KernelImagePath string InitrdPath string ISOPath string USBPath string UKIPath string ExtraISOPath string PFlashImages []string KernelArgs string SDStubKernelArgs string MonitorPath string DefaultBootOrder string BootloaderEnabled bool TPMConfig tpmConfig NodeUUID uuid.UUID BadRTC bool ArchitectureData Arch WithDebugShell bool IOMMUEnabled bool SkipInjectingExtraCmdline bool // Talos config Config string // PXE TFTPServer string BootFilename string IPXEBootFileName string // API APIBindAddress *net.TCPAddr // sd-stub sdStubExtraCmdline string sdStubExtraCmdlineConfig string // platform specific Network configuration Network networkConfig VMMac string // signals c chan os.Signal // controller controller *Controller } type networkConfigBase struct { BridgeName string IPs []netip.Addr CIDRs []netip.Prefix GatewayAddrs []netip.Addr Hostname string MTU int Nameservers []netip.Addr } type tpmConfig struct { NodeName string StateDir string TPM2 bool } // launchVM runs qemu with args built based on config. // //nolint:gocyclo,cyclop func launchVM(config *LaunchConfig) error { bootOrder := config.DefaultBootOrder if config.controller.ForcePXEBoot() { bootOrder = "nc" } cpuArg := "max" if config.BadRTC { cpuArg += ",-kvmclock" } args := []string{ "-m", strconv.FormatInt(config.MemSize, 10), "-smp", fmt.Sprintf("cpus=%d", config.VCPUCount), "-cpu", cpuArg, "-nographic", "-netdev", getNetdevParams(config.Network, "net0"), "-device", fmt.Sprintf("virtio-net-pci,netdev=net0,mac=%s,host_mtu=%d", config.VMMac, config.Network.MTU), // TODO: uncomment the following line to get another eth interface not connected to anything // "-nic", "tap,model=e1000,script=no,downscript=no", "-device", "virtio-rng-pci", "-device", "virtio-balloon,deflate-on-oom=on", "-monitor", fmt.Sprintf("unix:%s,server,nowait", config.MonitorPath), "-no-reboot", "-boot", fmt.Sprintf("order=%s,reboot-timeout=5000", bootOrder), "-smbios", fmt.Sprintf("type=1,uuid=%s", config.NodeUUID), "-chardev", fmt.Sprintf("socket,path=%s/%s.sock,server=on,wait=off,id=qga0", config.StatePath, config.Network.Hostname), "-device", "virtio-serial", "-device", "virtserialport,chardev=qga0,name=org.qemu.guest_agent.0", "-device", "i6300esb,id=watchdog0", "-watchdog-action", "pause", } if config.WithDebugShell { args = append( args, "-serial", fmt.Sprintf("unix:%s/%s.serial,server,nowait", config.StatePath, config.Network.Hostname), ) } var ( scsiAttached, ahciAttached, nvmeAttached, megaraidAttached, virtiofsAttached bool ahciBus int ) for i, disk := range config.DiskPaths { driver := config.DiskDrivers[i] blockSize := config.DiskBlockSizes[i] tag := config.DiskTags[i] if tag == "" { tag = fmt.Sprintf("disk%d", i) } serial := "" if config.DiskSerials[i] != "" { serial = ",serial=" + config.DiskSerials[i] } switch driver { case "virtio": args = append(args, "-drive", fmt.Sprintf("id=virtio%d,format=raw,if=none,file=%s,cache=none", i, disk), "-device", fmt.Sprintf("virtio-blk-pci,drive=virtio%d,logical_block_size=%d,physical_block_size=%d%s", i, blockSize, blockSize, serial), ) case "ide": args = append(args, "-drive", fmt.Sprintf("format=raw,if=ide,file=%s,cache=none", disk)) case "ahci": if !ahciAttached { args = append(args, "-device", "ahci,id=ahci0") ahciAttached = true } args = append(args, "-drive", fmt.Sprintf("id=ide%d,format=raw,if=none,file=%s", i, disk), "-device", fmt.Sprintf("ide-hd,drive=ide%d,bus=ahci0.%d", i, ahciBus), ) ahciBus++ case "scsi": if !scsiAttached { args = append(args, "-device", "virtio-scsi-pci,id=scsi0") scsiAttached = true } args = append(args, "-drive", fmt.Sprintf("id=scsi%d,format=raw,if=none,file=%s,discard=unmap,aio=native,cache=none", i, disk), "-device", fmt.Sprintf("scsi-hd,drive=scsi%d,bus=scsi0.0,logical_block_size=%d,physical_block_size=%d", i, blockSize, blockSize), ) case "nvme": if !nvmeAttached { // [TODO]: once Talos is fixed, use multipath NVME: https://qemu-project.gitlab.io/qemu/system/devices/nvme.html args = append(args, "-device", "nvme,id=nvme-ctrl-0,serial=deadbeef", ) nvmeAttached = true } args = append(args, "-drive", fmt.Sprintf("id=nvme%d,format=raw,if=none,file=%s,discard=unmap,aio=native,cache=none", i, disk), "-device", fmt.Sprintf("nvme-ns,drive=nvme%d,logical_block_size=%d,physical_block_size=%d", i, blockSize, blockSize), ) case "megaraid": if !megaraidAttached { args = append(args, "-device", "megasas-gen2,id=scsi1") megaraidAttached = true } args = append(args, "-drive", fmt.Sprintf("id=scsi%d,format=raw,if=none,file=%s,discard=unmap,aio=native,cache=none", i, disk), "-device", fmt.Sprintf("scsi-hd,drive=scsi%d,bus=scsi1.0,channel=0,scsi-id=%d,lun=0,logical_block_size=%d,physical_block_size=%d", i, i, blockSize, blockSize), ) case "virtiofs": if runtime.GOOS != "linux" { return fmt.Errorf("virtiofs driver is only supported on linux hosts") } if !virtiofsAttached { args = append(args, "-object", fmt.Sprintf("memory-backend-file,id=mem,size=%sM,mem-path=%s,share=on", strconv.FormatInt(config.MemSize, 10), config.MemShmPath), "-numa", "node,memdev=mem", ) virtiofsAttached = true } args = append(args, "-chardev", fmt.Sprintf("socket,id=char%d,path=%s", i, disk), "-device", fmt.Sprintf("vhost-user-fs-pci,queue-size=1024,chardev=char%d,tag=%s", i, tag), ) default: return fmt.Errorf("unsupported disk driver %q", driver) } } args = append(args, config.ArchitectureData.getMachineArgs(config.IOMMUEnabled)...) pflashArgs := make([]string, 2*len(config.PFlashImages)) for i := range config.PFlashImages { pflashArgs[2*i] = "-drive" pflashArgs[2*i+1] = fmt.Sprintf("file=%s,format=raw,if=pflash", config.PFlashImages[i]) } args = append(args, pflashArgs...) if config.ExtraISOPath != "" { args = append(args, "-drive", fmt.Sprintf("id=cdrom1,file=%s,media=cdrom", config.ExtraISOPath), ) } // check if disk is empty/wiped diskBootable, err := checkPartitions(config) if err != nil { return err } if config.TPMConfig.NodeName != "" { tpm2SocketPath := filepath.Join(config.TPMConfig.StateDir, "swtpm.sock") swtpmArgs := []string{ "socket", "--tpmstate", fmt.Sprintf("dir=%s,mode=0644", config.TPMConfig.StateDir), "--ctrl", fmt.Sprintf("type=unixio,path=%s", tpm2SocketPath), "--pid", fmt.Sprintf("file=%s", filepath.Join(config.TPMConfig.StateDir, "swtpm.pid")), "--log", fmt.Sprintf("file=%s,level=20", filepath.Join(config.TPMConfig.StateDir, "swtpm.log")), } if config.TPMConfig.TPM2 { swtpmArgs = append(swtpmArgs, "--tpm2") } cmd := exec.Command("swtpm", swtpmArgs...) //nolint:noctx // runs in background log.Printf("starting swtpm: %s", cmd.String()) if err := cmd.Start(); err != nil { return err } if err := waitForFileToExist(tpm2SocketPath, 5*time.Second); err != nil { return err } args = append(args, config.ArchitectureData.TPMDeviceArgs(tpm2SocketPath)..., ) } // ref: https://wiki.qemu.org/Features/VT-d if config.IOMMUEnabled { args = append(args, "-device", "intel-iommu,intremap=on,device-iotlb=on", "-device", "ioh3420,id=pcie.1,chassis=1", "-device", "e1000,bus=pcie.1,netdev=net1", "-netdev", "tap,id=net1,vhostforce=on,script=no,downscript=no", ) } if !diskBootable || !config.BootloaderEnabled { // if the disk is bootable, and we were forced to disable disk bootloader, // we need to skip ISO/USB boot, as it will fall back to boot from disk skipBootloader := diskBootable && !config.BootloaderEnabled switch { case config.ISOPath != "" && !skipBootloader: args = append(args, "-drive", fmt.Sprintf("id=cdrom0,file=%s,media=cdrom", config.ISOPath), ) case config.USBPath != "" && !skipBootloader: args = append(args, "-drive", fmt.Sprintf("if=none,id=stick,format=raw,read-only=on,file=%s", config.USBPath), "-device", "nec-usb-xhci,id=xhci", "-device", "usb-storage,bus=xhci.0,drive=stick,removable=on", ) case config.UKIPath != "": args = append(args, "-kernel", config.UKIPath, "-append", config.KernelArgs, ) config.sdStubExtraCmdline += config.sdStubExtraCmdlineConfig case config.KernelImagePath != "": args = append(args, "-kernel", config.KernelImagePath, "-initrd", config.InitrdPath, "-append", config.KernelArgs, ) config.sdStubExtraCmdline += config.sdStubExtraCmdlineConfig } } if !config.SkipInjectingExtraCmdline { args = append(args, "-smbios", fmt.Sprintf("type=11,value=%s=%s", constants.SDStubCmdlineExtraOEMVar, config.sdStubExtraCmdline), ) } if config.BadRTC { args = append(args, "-rtc", "base=2011-11-11T11:11:00,clock=rt", ) } fmt.Fprintf(os.Stderr, "starting %s with args:\n%s\n", config.ArchitectureData.QemuExecutable(), strings.Join(args, " ")) cmd := exec.Command( //nolint:noctx // runs in background config.ArchitectureData.QemuExecutable(), args..., ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := startQemuCmd(config, cmd); err != nil { return err } done := make(chan error) go func() { done <- cmd.Wait() }() for { select { case sig := <-config.c: fmt.Fprintf(os.Stderr, "exiting VM as signal %s was received\n", sig) if err := cmd.Process.Kill(); err != nil { return fmt.Errorf("failed to kill process %w", err) } <-done return errors.New("process stopped") case err := <-done: if err != nil { return fmt.Errorf("process exited with error %s", err) } // graceful exit return nil case command := <-config.controller.CommandsCh(): if command == VMCommandStop { fmt.Fprintf(os.Stderr, "exiting VM as stop command via API was received\n") if err := cmd.Process.Kill(); err != nil { return fmt.Errorf("failed to kill process %w", err) } <-done return nil } } } } // Launch a control process around qemu VM manager. // // This function is invoked from 'talosctl qemu-launch' hidden command // and wraps starting, controlling 'qemu' VM process. // // Launch restarts VM forever until control process is stopped itself with a signal. // // Process is expected to receive configuration on stdin. Current working directory // should be cluster state directory, process output should be redirected to the // logfile in state directory. // // When signals SIGINT, SIGTERM are received, control process stops qemu and exits. // //nolint:gocyclo func Launch() error { var config LaunchConfig ctx := context.Background() if err := vm.ReadConfig(&config); err != nil { return err } config.c = vm.ConfigureSignals() config.controller = NewController() apiBindAddrs, err := netip.ParseAddr(config.APIBindAddress.IP.String()) if err != nil { return err } httpServer, err := vm.NewHTTPServer(ctx, apiBindAddrs, config.APIBindAddress.Port, []byte(config.Config), config.controller) if err != nil { return err } httpServer.Serve() defer httpServer.Shutdown(ctx) //nolint:errcheck if err := patchKernelArgs(&config, httpServer.GetAddr()); err != nil { return err } return withNetworkContext(ctx, &config, func(config *LaunchConfig) error { err = dumpIpam(*config) if err != nil { return err } for { for config.controller.PowerState() != PoweredOn { select { case <-config.controller.CommandsCh(): // machine might have been powered on case sig := <-config.c: fmt.Fprintf(os.Stderr, "exiting stopped launcher as signal %s was received\n", sig) return errors.New("process stopped") } } if err := launchVM(config); err != nil { return err } } }) } func patchKernelArgs(config *LaunchConfig, httpServerAddr net.Addr) error { configServerAddr, err := getConfigServerAddr(httpServerAddr, *config) if err != nil { return err } config.sdStubExtraCmdline = "console=ttyS0" if config.SDStubKernelArgs != "" { config.sdStubExtraCmdline += " " + config.SDStubKernelArgs } if strings.Contains(config.KernelArgs, "{TALOS_CONFIG_URL}") { config.KernelArgs = strings.ReplaceAll(config.KernelArgs, "{TALOS_CONFIG_URL}", fmt.Sprintf("http://%s/config.yaml", configServerAddr)) config.sdStubExtraCmdlineConfig = fmt.Sprintf(" talos.config=http://%s/config.yaml", httpServerAddr) } return nil } func waitForFileToExist(path string, timeout time.Duration) error { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() for { select { case <-ctx.Done(): return ctx.Err() default: if _, err := os.Stat(path); err == nil { return nil } } time.Sleep(100 * time.Millisecond) } } func dumpIpam(config LaunchConfig) error { for j := range config.Network.CIDRs { nameservers := make([]netip.Addr, 0, len(config.Network.Nameservers)) // filter nameservers by IPv4/IPv6 matching IPs for i := range config.Network.Nameservers { if config.Network.IPs[j].Is6() { if config.Network.Nameservers[i].Is6() { nameservers = append(nameservers, config.Network.Nameservers[i]) } } else { if config.Network.Nameservers[i].Is4() { nameservers = append(nameservers, config.Network.Nameservers[i]) } } } // dump node IP/mac/hostname for dhcp if err := vm.DumpIPAMRecord(config.StatePath, vm.IPAMRecord{ IP: config.Network.IPs[j], Netmask: byte(config.Network.CIDRs[j].Bits()), MAC: config.VMMac, Hostname: config.Network.Hostname, Gateway: config.Network.GatewayAddrs[j], MTU: config.Network.MTU, Nameservers: nameservers, TFTPServer: config.TFTPServer, IPXEBootFilename: config.IPXEBootFileName, }); err != nil { return err } } return nil } func checkPartitions(config *LaunchConfig) (bool, error) { info, err := blkid.ProbePath(config.DiskPaths[0], blkid.WithSectorSize(config.DiskBlockSizes[0])) if err != nil { return false, fmt.Errorf("error probing disk: %w", err) } return info.Name == "gpt" && len(info.Parts) > 0, nil } ================================================ FILE: pkg/provision/providers/qemu/launch_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "fmt" "net" "net/netip" "os/exec" "github.com/siderolabs/talos/pkg/provision" ) type networkConfig struct { networkConfigBase StartAddr netip.Addr EndAddr netip.Addr } func getLaunchNetworkConfig(state *provision.State, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest) networkConfig { // This ip will be assigned to the bridge // The following ips will be assigned to the vms startAddr := clusterReq.Nodes[0].IPs[0].Prev() endAddr := clusterReq.Nodes[len(clusterReq.Nodes)-1].IPs[0].Next() return networkConfig{ networkConfigBase: getLaunchNetworkConfigBase(state, clusterReq, nodeReq), StartAddr: startAddr, EndAddr: endAddr, } } func getNetdevParams(networkConfig networkConfig, id string) string { netDevArg := "vmnet-shared,id=" + id cidr := networkConfig.CIDRs[0] m := net.CIDRMask(cidr.Bits(), 32) subnetMask := fmt.Sprintf("%d.%d.%d.%d", m[0], m[1], m[2], m[3]) netDevArg += fmt.Sprintf(",start-address=%s,end-address=%s,subnet-mask=%s", networkConfig.StartAddr, networkConfig.EndAddr, subnetMask) return netDevArg } // getConfigServerAddr returns the ip accessible to the VM that will route to the config server. // hostAddrs is the address on which the server is accessible from the host network. func getConfigServerAddr(hostAddrs net.Addr, config LaunchConfig) (netip.AddrPort, error) { addrPort, err := netip.ParseAddrPort(hostAddrs.String()) if err != nil { return netip.AddrPort{}, err } return netip.AddrPortFrom(config.Network.GatewayAddrs[0], addrPort.Port()), nil } // withNetworkContext runs the f on the host network on darwin. func withNetworkContext(ctx context.Context, config *LaunchConfig, f func(config *LaunchConfig) error) error { return f(config) } // startQemuCmd on darwin just runs cmd.Start. func startQemuCmd(_ *LaunchConfig, cmd *exec.Cmd) error { return cmd.Start() } ================================================ FILE: pkg/provision/providers/qemu/launch_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "errors" "fmt" "log" "net" "net/netip" "os/exec" "path/filepath" "strings" "github.com/alexflint/go-filemutex" "github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/pkg/types" types100 "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/utils" "github.com/coreos/go-iptables/iptables" "github.com/google/uuid" "github.com/siderolabs/gen/xslices" sideronet "github.com/siderolabs/net" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/internal/cniutils" ) type networkConfig struct { networkConfigBase Airgapped bool // TODO: rename field to cniNetworkConfig CniNetworkConfig *libcni.NetworkConfigList CNI provision.CNIConfig NoMasqueradeCIDRs []netip.Prefix // filled by CNI invocation tapName string ns ns.NetNS } func getLaunchNetworkConfig(state *provision.State, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest) networkConfig { return networkConfig{ networkConfigBase: getLaunchNetworkConfigBase(state, clusterReq, nodeReq), CniNetworkConfig: state.VMCNIConfig, CNI: clusterReq.Network.CNI, NoMasqueradeCIDRs: clusterReq.Network.NoMasqueradeCIDRs, Airgapped: clusterReq.Network.Airgapped, } } func getNetdevParams(networkConfig networkConfig, id string) string { return fmt.Sprintf("tap,id=%s,ifname=%s,script=no,downscript=no", id, networkConfig.tapName) } func getConfigServerAddr(hostAddrs net.Addr, _ LaunchConfig) (net.Addr, error) { return hostAddrs, nil } // withCNIOperationLocked ensures that CNI operations don't run concurrently. // // There are race conditions in the CNI plugins that can cause a failure if called concurrently. func withCNIOperationLocked[T any](config *LaunchConfig, f func() (T, error)) (T, error) { var zeroT T lock, err := filemutex.New(filepath.Join(config.StatePath, "cni.lock")) if err != nil { return zeroT, fmt.Errorf("failed to create CNI lock: %w", err) } if err = lock.Lock(); err != nil { return zeroT, fmt.Errorf("failed to acquire CNI lock: %w", err) } defer func() { if err := lock.Close(); err != nil { log.Printf("failed to release CNI lock: %s", err) } }() return f() } // withCNIOperationLockedNoResult ensures that CNI operations don't run concurrently. func withCNIOperationLockedNoResult(config *LaunchConfig, f func() error) error { _, err := withCNIOperationLocked(config, func() (struct{}, error) { return struct{}{}, f() }) return err } // withNetworkContext creates a network namespace, launches CNI and passes control to the next function // filling config with netNS and interface details. // //nolint:gocyclo func withNetworkContext(ctx context.Context, config *LaunchConfig, f func(config *LaunchConfig) error) error { // random ID for the CNI, maps to single VM containerID := uuid.New().String() cniConfig := libcni.NewCNIConfigWithCacheDir(config.Network.CNI.BinPath, config.Network.CNI.CacheDir, nil) // create a network namespace ns, err := testutils.NewNS() if err != nil { return err } defer func() { ns.Close() //nolint:errcheck testutils.UnmountNS(ns) //nolint:errcheck }() ips := make([]string, len(config.Network.IPs)) for j := range ips { ips[j] = sideronet.FormatCIDR(config.Network.IPs[j], config.Network.CIDRs[j]) } gatewayAddrs := xslices.Map(config.Network.GatewayAddrs, netip.Addr.String) runtimeConf := libcni.RuntimeConf{ ContainerID: containerID, NetNS: ns.Path(), IfName: "veth0", Args: [][2]string{ {"IP", strings.Join(ips, ",")}, {"GATEWAY", strings.Join(gatewayAddrs, ",")}, {"IgnoreUnknown", "1"}, }, } // attempt to clean up network in case it was deployed previously err = withCNIOperationLockedNoResult( config, func() error { return cniConfig.DelNetworkList(ctx, config.Network.CniNetworkConfig, &runtimeConf) }, ) if err != nil { return fmt.Errorf("error deleting CNI network: %w", err) } res, err := withCNIOperationLocked( config, func() (types.Result, error) { return cniConfig.AddNetworkList(ctx, config.Network.CniNetworkConfig, &runtimeConf) }, ) if err != nil { return fmt.Errorf("error provisioning CNI network: %w", err) } defer func() { if e := withCNIOperationLockedNoResult( config, func() error { return cniConfig.DelNetworkList(ctx, config.Network.CniNetworkConfig, &runtimeConf) }, ); e != nil { log.Printf("error cleaning up CNI: %s", e) } }() currentResult, err := types100.NewResultFromResult(res) if err != nil { return fmt.Errorf("failed to parse cni result: %w", err) } vmIface, tapIface, err := cniutils.VMTapPair(currentResult, containerID) if err != nil { return errors.New( "failed to parse VM network configuration from CNI output, ensure CNI is configured with a plugin " + "that supports automatic VM network configuration such as tc-redirect-tap") } if !config.Network.Airgapped { cniChain := utils.FormatChainName(config.Network.CniNetworkConfig.Name, containerID) ipt, err := iptables.New() if err != nil { return fmt.Errorf("failed to initialize iptables: %w", err) } // don't masquerade traffic with "broadcast" destination from the VM // // no need to clean up the rule, as CNI drops the whole chain if err = ipt.InsertUnique("nat", cniChain, 1, "--destination", "255.255.255.255/32", "-j", "ACCEPT"); err != nil { return fmt.Errorf("failed to insert iptables rule to allow broadcast traffic: %w", err) } for _, cidr := range config.Network.NoMasqueradeCIDRs { if err = ipt.InsertUnique("nat", cniChain, 1, "--destination", cidr.String(), "-j", "ACCEPT"); err != nil { return fmt.Errorf("failed to insert iptables rule to allow non-masquerade traffic to cidr %q: %w", cidr.String(), err) } } } config.Network.tapName = tapIface.Name config.VMMac = vmIface.Mac config.Network.ns = ns return f(config) } func startQemuCmd(config *LaunchConfig, cmd *exec.Cmd) error { if err := ns.WithNetNSPath(config.Network.ns.Path(), func(_ ns.NetNS) error { return cmd.Start() }); err != nil { return err } return nil } ================================================ FILE: pkg/provision/providers/qemu/node.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "crypto/rand" "encoding/json" "fmt" "io" "math" "net" "os" "os/exec" "path/filepath" "strconv" "strings" "syscall" "github.com/google/uuid" "github.com/hashicorp/go-multierror" "github.com/klauspost/compress/zstd" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/constants" "github.com/siderolabs/talos/pkg/machinery/kernel" "github.com/siderolabs/talos/pkg/provision" ) //nolint:gocyclo,cyclop func (p *provisioner) createNode(ctx context.Context, state *provision.State, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest, opts *provision.Options) (provision.NodeInfo, error) { arch := Arch(opts.TargetArch) pidPath := state.GetRelativePath(fmt.Sprintf("%s.pid", nodeReq.Name)) var pflashImages []string if pflashSpec := arch.PFlash(opts.UEFIEnabled, opts.ExtraUEFISearchPaths); pflashSpec != nil { var err error if pflashImages, err = p.createPFlashImages(state, nodeReq.Name, pflashSpec); err != nil { return provision.NodeInfo{}, fmt.Errorf("error creating flash images: %w", err) } } vcpuCount := int64(math.RoundToEven(float64(nodeReq.NanoCPUs) / 1000 / 1000 / 1000)) if vcpuCount < 2 { vcpuCount = 1 } memSize := nodeReq.Memory / 1024 / 1024 diskPaths, err := p.CreateDisks(state, nodeReq) if err != nil { return provision.NodeInfo{}, err } err = p.populateSystemDisk(diskPaths, clusterReq) if err != nil { return provision.NodeInfo{}, err } logFile, err := os.OpenFile(state.GetRelativePath(fmt.Sprintf("%s.log", nodeReq.Name)), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return provision.NodeInfo{}, err } defer logFile.Close() //nolint:errcheck cmdline := procfs.NewCmdline("") cmdline.SetAll(kernel.DefaultArgs(nodeReq.Quirks)) // required to get kernel console cmdline.Append("console", arch.Console()) // reboot configuration cmdline.Append("reboot", "k") cmdline.Append("panic", "1") cmdline.Append("talos.shutdown", "halt") // Talos config cmdline.Append("talos.platform", constants.PlatformMetal) // add overrides if nodeReq.ExtraKernelArgs != nil { if err = cmdline.AppendAll( nodeReq.ExtraKernelArgs.Strings(), procfs.WithDeleteNegatedArgs(), ); err != nil { return provision.NodeInfo{}, err } } if opts.WithDebugShell { cmdline.Append("talos.debugshell", "") } var ( nodeConfig string extraISOPath string ) if !nodeReq.SkipInjectingConfig { nodeConfig, err = nodeReq.Config.EncodeString() if err != nil { return provision.NodeInfo{}, err } switch nodeReq.ConfigInjectionMethod { case provision.ConfigInjectionMethodHTTP: cmdline.Append("talos.config", "{TALOS_CONFIG_URL}") // to be patched by launcher case provision.ConfigInjectionMethodMetalISO: cmdline.Append("talos.config", "metal-iso") extraISOPath, err = p.createMetalConfigISO(ctx, state, nodeReq.Name, nodeConfig) if err != nil { return provision.NodeInfo{}, fmt.Errorf("error creating metal-iso: %w", err) } } } nodeUUID := uuid.New() if nodeReq.UUID != nil { nodeUUID = *nodeReq.UUID } apiBind, err := p.findAPIBindAddrs(ctx, clusterReq) if err != nil { return provision.NodeInfo{}, fmt.Errorf("error finding listen address for the API: %w", err) } defaultBootOrder := "cd" if nodeReq.DefaultBootOrder != "" { defaultBootOrder = nodeReq.DefaultBootOrder } // backwards compatibility, set Driver/BlockSize if not set for i := range nodeReq.Disks { if nodeReq.Disks[i].BlockSize == 0 { nodeReq.Disks[i].BlockSize = 512 } if nodeReq.Disks[i].Driver != "" { continue } if i == 0 { nodeReq.Disks[i].Driver = "virtio" } else { nodeReq.Disks[i].Driver = "ide" } } launchConfig := LaunchConfig{ ArchitectureData: arch, DiskPaths: diskPaths, DiskDrivers: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) string { return disk.Driver }), DiskTags: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) string { return disk.Tag }), DiskSerials: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) string { return disk.Serial }), DiskBlockSizes: xslices.Map(nodeReq.Disks, func(disk *provision.Disk) uint { return disk.BlockSize }), VCPUCount: vcpuCount, MemSize: memSize, MemShmPath: state.GetShmPath(fmt.Sprintf("shm-%s", nodeReq.Name)), // this is used only when attaching virtiofs disks KernelArgs: cmdline.String(), ExtraISOPath: extraISOPath, PFlashImages: pflashImages, MonitorPath: state.GetRelativePath(fmt.Sprintf("%s.monitor", nodeReq.Name)), BadRTC: nodeReq.BadRTC, DefaultBootOrder: defaultBootOrder, BootloaderEnabled: opts.BootloaderEnabled, SkipInjectingExtraCmdline: opts.SkipInjectingExtraCmdline, NodeUUID: nodeUUID, Config: nodeConfig, TFTPServer: nodeReq.TFTPServer, IPXEBootFileName: nodeReq.IPXEBootFilename, APIBindAddress: apiBind, WithDebugShell: opts.WithDebugShell, IOMMUEnabled: opts.IOMMUEnabled, Network: getLaunchNetworkConfig(state, clusterReq, nodeReq), // Generate a random MAC address. // On linux this is later overridden to the interface mac. VMMac: getRandomMacAddress(), } if clusterReq.IPXEBootScript != "" { launchConfig.TFTPServer = clusterReq.Network.GatewayAddrs[0].String() launchConfig.IPXEBootFileName = fmt.Sprintf("ipxe/%s/snp.efi", string(arch)) } nodeInfo := provision.NodeInfo{ ID: pidPath, UUID: nodeUUID, Name: nodeReq.Name, Type: nodeReq.Type, NanoCPUs: nodeReq.NanoCPUs, Memory: nodeReq.Memory, DiskSize: nodeReq.Disks[0].Size, IPs: nodeReq.IPs, APIPort: apiBind.Port, } if opts.TPM1_2Enabled || opts.TPM2Enabled { tpmConfig, tpm2Err := p.createVirtualTPMState(state, nodeReq.Name, opts.TPM2Enabled) if tpm2Err != nil { return provision.NodeInfo{}, tpm2Err } launchConfig.TPMConfig = tpmConfig nodeInfo.TPMStateDir = tpmConfig.StateDir } if !clusterReq.Network.DHCPSkipHostname { launchConfig.Network.Hostname = nodeReq.Name } if nodeReq.SDStubKernelArgs != nil { launchConfig.SDStubKernelArgs = nodeReq.SDStubKernelArgs.String() } if !nodeReq.PXEBooted && launchConfig.IPXEBootFileName == "" { launchConfig.KernelImagePath = strings.ReplaceAll(clusterReq.KernelPath, constants.ArchVariable, opts.TargetArch) launchConfig.InitrdPath = strings.ReplaceAll(clusterReq.InitramfsPath, constants.ArchVariable, opts.TargetArch) launchConfig.ISOPath = strings.ReplaceAll(clusterReq.ISOPath, constants.ArchVariable, opts.TargetArch) launchConfig.USBPath = strings.ReplaceAll(clusterReq.USBPath, constants.ArchVariable, opts.TargetArch) launchConfig.UKIPath = strings.ReplaceAll(clusterReq.UKIPath, constants.ArchVariable, opts.TargetArch) } launchConfig.StatePath, err = state.StatePath() if err != nil { return provision.NodeInfo{}, err } launchConfigFile, err := os.Create(state.GetRelativePath(fmt.Sprintf("%s.config", nodeReq.Name))) if err != nil { return provision.NodeInfo{}, err } if err = json.NewEncoder(launchConfigFile).Encode(&launchConfig); err != nil { return provision.NodeInfo{}, err } if _, err = launchConfigFile.Seek(0, io.SeekStart); err != nil { return provision.NodeInfo{}, err } defer launchConfigFile.Close() //nolint:errcheck cmd := exec.Command(clusterReq.SelfExecutable, "qemu-launch") //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.Stdin = launchConfigFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return provision.NodeInfo{}, err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return provision.NodeInfo{}, fmt.Errorf("error writing PID file: %w", err) } // no need to wait here, as cmd has all the Stdin/out/err via *os.File return nodeInfo, nil } func (p *provisioner) createNodes( ctx context.Context, state *provision.State, clusterReq provision.ClusterRequest, nodeReqs []provision.NodeRequest, opts *provision.Options, ) ([]provision.NodeInfo, error) { errCh := make(chan error) nodeCh := make(chan provision.NodeInfo, len(nodeReqs)) for _, nodeReq := range nodeReqs { go func(nodeReq provision.NodeRequest) { nodeInfo, err := p.createNode(ctx, state, clusterReq, nodeReq, opts) if err == nil { nodeCh <- nodeInfo } errCh <- err }(nodeReq) } var multiErr *multierror.Error for range nodeReqs { multiErr = multierror.Append(multiErr, <-errCh) } close(nodeCh) nodesInfo := make([]provision.NodeInfo, 0, len(nodeReqs)) for nodeInfo := range nodeCh { nodesInfo = append(nodesInfo, nodeInfo) } return nodesInfo, multiErr.ErrorOrNil() } func (p *provisioner) populateSystemDisk(disks []string, clusterReq provision.ClusterRequest) error { if len(disks) > 0 && clusterReq.DiskImagePath != "" { if err := p.handleOptionalZSTDDiskImage(disks[0], clusterReq.DiskImagePath); err != nil { return err } } return nil } func (p *provisioner) destroySHMFiles(state *provision.State) error { for _, node := range state.Info().Nodes { if node.Type != machine.TypeWorker { continue } shmFilePath := state.GetShmPath(fmt.Sprintf("shm-%s", node.Name)) if err := os.RemoveAll(shmFilePath); err != nil && !os.IsNotExist(err) { return err } } return nil } func (p *provisioner) handleOptionalZSTDDiskImage(provisionerDisk, diskImagePath string) error { image, err := os.Open(diskImagePath) if err != nil { return err } defer image.Close() //nolint:errcheck disk, err := os.OpenFile(provisionerDisk, os.O_RDWR, 0o755) if err != nil { return err } defer disk.Close() //nolint:errcheck if strings.HasSuffix(diskImagePath, ".zst") { zstdReader, err := zstd.NewReader(image) if err != nil { return err } defer zstdReader.Close() //nolint:errcheck _, err = io.Copy(disk, zstdReader) return err } _, err = io.Copy(disk, image) return err } func (p *provisioner) createMetalConfigISO(ctx context.Context, state *provision.State, nodeName, config string) (string, error) { isoPath := state.GetRelativePath(nodeName + "-metal-config.iso") tmpDir, err := os.MkdirTemp("", "talos-metal-config-iso") if err != nil { return "", err } defer os.RemoveAll(tmpDir) //nolint:errcheck if err = os.WriteFile(filepath.Join(tmpDir, "config.yaml"), []byte(config), 0o644); err != nil { return "", err } _, err = cmd.RunWithOptions(ctx, "mkisofs", []string{"-joliet", "-rock", "-volid", "metal-iso", "-output", isoPath, tmpDir}) if err != nil { return "", err } return isoPath, nil } func getLaunchNetworkConfigBase(state *provision.State, clusterReq provision.ClusterRequest, nodeReq provision.NodeRequest) networkConfigBase { return networkConfigBase{ BridgeName: state.BridgeName, CIDRs: clusterReq.Network.CIDRs, IPs: nodeReq.IPs, GatewayAddrs: clusterReq.Network.GatewayAddrs, MTU: clusterReq.Network.MTU, Nameservers: clusterReq.Network.Nameservers, } } // getRandomMacAddress generates a random local MAC address // https://stackoverflow.com/a/21027407/10938317 func getRandomMacAddress() string { const ( local = 0b10 multicast = 0b1 ) buf := make([]byte, 6) rand.Read(buf) //nolint:errcheck // clear multicast bit (&^), ensure local bit (|) buf[0] = buf[0]&^multicast | local return net.HardwareAddr(buf).String() } ================================================ FILE: pkg/provision/providers/qemu/node_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "net" "github.com/siderolabs/talos/pkg/provision" ) // findAPIBindAddrs returns the 0.0.0.0 address to bind to all interfaces on macos with a random port on macos. // The bridge interface address is not used as the bridge is not yet created at this stage. func (p *provisioner) findAPIBindAddrs(_ context.Context, _ provision.ClusterRequest) (*net.TCPAddr, error) { l, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", "0")) if err != nil { return nil, err } return l.Addr().(*net.TCPAddr), l.Close() } ================================================ FILE: pkg/provision/providers/qemu/node_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "net" "github.com/siderolabs/talos/pkg/provision" ) func (p *provisioner) findAPIBindAddrs(ctx context.Context, clusterReq provision.ClusterRequest) (*net.TCPAddr, error) { l, err := (&net.ListenConfig{}).Listen(ctx, "tcp", net.JoinHostPort(clusterReq.Network.GatewayAddrs[0].String(), "0")) if err != nil { return nil, err } return l.Addr().(*net.TCPAddr), l.Close() } ================================================ FILE: pkg/provision/providers/qemu/pflash.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "errors" "fmt" "io" "io/fs" "os" "github.com/siderolabs/talos/pkg/provision" ) //nolint:gocyclo func (p *provisioner) createPFlashImages(state *provision.State, nodeName string, pflashSpec []PFlash) ([]string, error) { var images []string for i, pflash := range pflashSpec { if err := func(i int, pflash PFlash) error { path := state.GetRelativePath(fmt.Sprintf("%s-flash%d.img", nodeName, i)) f, err := os.Create(path) if err != nil { return err } defer f.Close() //nolint:errcheck if err = f.Truncate(pflash.Size); err != nil { return err } if pflash.SourcePaths != nil { for _, sourcePath := range pflash.SourcePaths { var src *os.File src, err = os.Open(sourcePath) if err != nil { if errors.Is(err, fs.ErrNotExist) { continue } return err } defer src.Close() //nolint:errcheck if _, err = io.Copy(f, src); err != nil { return err } break } if err != nil { return err } } images = append(images, path) return nil }(i, pflash); err != nil { return nil, err } } return images, nil } ================================================ FILE: pkg/provision/providers/qemu/preflight.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "errors" "fmt" "os" "github.com/siderolabs/talos/pkg/provision" ) func (p *provisioner) preflightChecks(ctx context.Context, request provision.ClusterRequest, options provision.Options, arch Arch) error { checkContext := preflightCheckContext{ request: request, options: options, arch: arch, } for _, check := range []func(ctx context.Context) error{ checkContext.verifyRoot, checkContext.qemuExecutable, checkContext.checkFlashImages, } { if err := check(ctx); err != nil { return err } } return checkContext.verifyPlatformSpecific(ctx) } type preflightCheckContext struct { request provision.ClusterRequest options provision.Options arch Arch } func (check *preflightCheckContext) verifyRoot(context.Context) error { if os.Geteuid() != 0 { return errors.New("error: please run as root user (CNI, qemu hvf requirement), we recommend running with `sudo -E`") } return nil } func (check *preflightCheckContext) qemuExecutable(context.Context) error { if check.arch.QemuExecutable() == "" { return fmt.Errorf("QEMU executable (qemu-system-%s or qemu-kvm) not found, please install QEMU with package manager", check.arch.QemuArch()) } return nil } func (check *preflightCheckContext) checkFlashImages(context.Context) error { for _, flashImage := range check.arch.PFlash(check.options.UEFIEnabled, check.options.ExtraUEFISearchPaths) { if len(flashImage.SourcePaths) == 0 { continue } found := false for _, path := range flashImage.SourcePaths { _, err := os.Stat(path) if err == nil { found = true break } } if !found { return fmt.Errorf("the required flash image was not found in any of the expected paths for (%q), "+ "please install it with the package manager or specify --extra-uefi-search-paths", flashImage.SourcePaths) } } return nil } ================================================ FILE: pkg/provision/providers/qemu/preflight_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "errors" "runtime" ) func (check *preflightCheckContext) verifyPlatformSpecific(ctx context.Context) error { return check.verifyAppleMachine(ctx) } func (check *preflightCheckContext) verifyAppleMachine(context.Context) error { if runtime.GOARCH != "arm64" { return errors.New("currently qemu on darwin is supported only on arm machines") } return nil } ================================================ FILE: pkg/provision/providers/qemu/preflight_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "errors" "fmt" "io/fs" "os" "os/exec" "path/filepath" "runtime" "strings" "github.com/coreos/go-iptables/iptables" "github.com/hashicorp/go-getter/v2" "github.com/siderolabs/talos/pkg/machinery/constants" ) func (check *preflightCheckContext) verifyPlatformSpecific(ctx context.Context) error { for _, check := range []func(ctx context.Context) error{ check.cniDirectories, check.cniBundle, check.checkIptables, check.swtpmExecutable, check.checkKVM, } { if err := check(ctx); err != nil { return err } } return nil } func (check *preflightCheckContext) cniDirectories(ctx context.Context) error { cniDirs := append([]string{}, check.request.Network.CNI.BinPath...) cniDirs = append(cniDirs, check.request.Network.CNI.CacheDir, check.request.Network.CNI.ConfDir) for _, cniDir := range cniDirs { st, err := os.Stat(cniDir) if err != nil { if !errors.Is(err, fs.ErrNotExist) { return fmt.Errorf("error checking CNI directory %q: %w", cniDir, err) } fmt.Fprintf(check.options.LogWriter, "creating %q\n", cniDir) err = os.MkdirAll(cniDir, 0o777) if err != nil { return err } continue } if !st.IsDir() { return fmt.Errorf("CNI path %q exists, but it's not a directory", cniDir) } } return nil } func (check *preflightCheckContext) cniBundle(ctx context.Context) error { var missing bool requiredCNIPlugins := []string{"bridge", "firewall", "static", "tc-redirect-tap"} for _, cniPlugin := range requiredCNIPlugins { missing = true for _, binPath := range check.request.Network.CNI.BinPath { _, err := os.Stat(filepath.Join(binPath, cniPlugin)) if err == nil { missing = false break } } if missing { break } } if !missing { return nil } if check.request.Network.CNI.BundleURL == "" { return fmt.Errorf("error: required CNI plugins %q were not found in %q", requiredCNIPlugins, check.request.Network.CNI.BinPath) } pwd, err := os.Getwd() if err != nil { return err } client := getter.Client{} src := strings.ReplaceAll(check.request.Network.CNI.BundleURL, constants.ArchVariable, runtime.GOARCH) dst := check.request.Network.CNI.BinPath[0] fmt.Fprintf(check.options.LogWriter, "downloading CNI bundle from %q to %q\n", src, dst) _, err = client.Get(ctx, &getter.Request{ Src: src, Dst: dst, Pwd: pwd, GetMode: getter.ModeDir, }) return err } func (check *preflightCheckContext) checkIptables(ctx context.Context) error { _, err := iptables.New() if err != nil { return fmt.Errorf("error accessing iptables: %w", err) } return nil } func (check *preflightCheckContext) swtpmExecutable(ctx context.Context) error { if check.options.TPM1_2Enabled || check.options.TPM2Enabled { if _, err := exec.LookPath("swtpm"); err != nil { return fmt.Errorf("swtpm not found in PATH, please install swtpm-tools with the package manager: %w", err) } } return nil } func (check *preflightCheckContext) checkKVM(ctx context.Context) error { err := checkKVM() if err != nil { fmt.Printf("error opening /dev/kvm, please make sure KVM support is enabled in Linux kernel: %s\n", err) fmt.Println("running without KVM") } return nil } func checkKVM() error { f, err := os.OpenFile("/dev/kvm", os.O_RDWR, 0) if err != nil { return err } return f.Close() } ================================================ FILE: pkg/provision/providers/qemu/qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "context" "github.com/siderolabs/talos/pkg/machinery/cel" "github.com/siderolabs/talos/pkg/machinery/cel/celenv" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" configconfig "github.com/siderolabs/talos/pkg/machinery/config/config" "github.com/siderolabs/talos/pkg/machinery/config/configpatcher" "github.com/siderolabs/talos/pkg/machinery/config/container" "github.com/siderolabs/talos/pkg/machinery/config/generate" networkcfg "github.com/siderolabs/talos/pkg/machinery/config/types/network" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) type provisioner struct { vm.Provisioner } // NewProvisioner initializes qemu provisioner. func NewProvisioner(ctx context.Context) (provision.Provisioner, error) { p := &provisioner{ vm.Provisioner{ Name: "qemu", }, } return p, nil } // Close and release resources. func (p *provisioner) Close() error { return nil } // GenOptions provides a list of additional config generate options. func (p *provisioner) GenOptions(networkReq provision.NetworkRequest, contract *config.VersionContract) ([]generate.Option, []bundle.Option) { hasIPv4 := false hasIPv6 := false for _, cidr := range networkReq.CIDRs { if cidr.Addr().Is6() { hasIPv6 = true } else { hasIPv4 = true } } genOpts := []generate.Option{ generate.WithInstallDisk("/dev/vda"), } var bundleOpts []bundle.Option if contract.MultidocNetworkConfigSupported() { aliasConfig := networkcfg.NewLinkAliasConfigV1Alpha1("net0") aliasConfig.Selector = networkcfg.LinkSelector{ Match: cel.MustExpression(cel.ParseBooleanExpression(`link.driver == "virtio_net"`, celenv.LinkLocator())), } documents := []configconfig.Document{aliasConfig} if hasIPv4 { dhcp4Config := networkcfg.NewDHCPv4ConfigV1Alpha1("net0") documents = append(documents, dhcp4Config) } else if hasIPv6 { dhcp6Config := networkcfg.NewDHCPv6ConfigV1Alpha1("net0") documents = append(documents, dhcp6Config) } ctr, err := container.New(documents...) if err != nil { panic(err) } bundleOpts = append(bundleOpts, bundle.WithPatch([]configpatcher.Patch{configpatcher.NewStrategicMergePatch(ctr)}), ) } else { virtioSelector := v1alpha1.IfaceBySelector(v1alpha1.NetworkDeviceSelector{ NetworkDeviceKernelDriver: "virtio_net", }) genOpts = append(genOpts, generate.WithNetworkOptions( v1alpha1.WithNetworkInterfaceDHCP(virtioSelector, true), v1alpha1.WithNetworkInterfaceDHCPv4(virtioSelector, hasIPv4), v1alpha1.WithNetworkInterfaceDHCPv6(virtioSelector, hasIPv6), ), ) } if !contract.GrubUseUKICmdlineDefault() { genOpts = append(genOpts, generate.WithInstallExtraKernelArgs([]string{ "console=ttyS0", // TODO: should depend on arch // reboot configuration "reboot=k", "panic=1", "talos.shutdown=halt", // Talos-specific "talos.platform=metal", }), ) } return genOpts, bundleOpts } // GetInClusterKubernetesControlPlaneEndpoint returns the Kubernetes control plane endpoint. func (p *provisioner) GetInClusterKubernetesControlPlaneEndpoint(networkReq provision.NetworkRequest, controlPlanePort int) string { // QEMU provisioner always runs TCP loadbalancer on the bridge IP and port 6443. return "https://" + nethelpers.JoinHostPort(networkReq.GatewayAddrs[0].String(), controlPlanePort) } // GetExternalKubernetesControlPlaneEndpoint returns the Kubernetes control plane endpoint. func (p *provisioner) GetExternalKubernetesControlPlaneEndpoint(networkReq provision.NetworkRequest, controlPlanePort int) string { // for QEMU, external and in-cluster endpoints are same. return p.GetInClusterKubernetesControlPlaneEndpoint(networkReq, controlPlanePort) } // GetTalosAPIEndpoints returns a list of Talos API endpoints. func (p *provisioner) GetTalosAPIEndpoints(provision.NetworkRequest) []string { // nil means that the API of controlplane endpoints should be used return nil } // GetFirstInterface returns first network interface name. func (p *provisioner) GetFirstInterface() v1alpha1.IfaceSelector { return v1alpha1.IfaceBySelector(v1alpha1.NetworkDeviceSelector{ NetworkDeviceKernelDriver: "virtio_net", }) } // GetFirstInterfaceName return the first network interface name. func (p *provisioner) GetFirstInterfaceName() string { return "net0" // real interface will be aliased to net0 } ================================================ FILE: pkg/provision/providers/qemu/tpm2.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package qemu import ( "fmt" "os" "path/filepath" "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) func (p *provisioner) createVirtualTPMState(state *provision.State, nodeName string, tpm2Enabled bool) (tpmConfig, error) { tpmStateDir := state.GetRelativePath(fmt.Sprintf("%s-tpm", nodeName)) if err := os.MkdirAll(tpmStateDir, 0o755); err != nil { return tpmConfig{}, err } return tpmConfig{ NodeName: nodeName, StateDir: tpmStateDir, TPM2: tpm2Enabled, }, nil } func (p *provisioner) destroyVirtualTPMs(cluster provision.ClusterInfo) error { errCh := make(chan error) nodes := append([]provision.NodeInfo{}, cluster.Nodes...) for _, node := range nodes { if node.TPMStateDir == "" { continue } tpm2PidPath := filepath.Join(node.TPMStateDir, "swtpm.pid") go func() { errCh <- p.destroyVirtualTPM(tpm2PidPath) }() } var multiErr *multierror.Error for _, node := range nodes { if node.TPMStateDir == "" { continue } multiErr = multierror.Append(multiErr, <-errCh) } return multiErr.ErrorOrNil() } func (p *provisioner) destroyVirtualTPM(pid string) error { return vm.StopProcessByPidfile(pid) } ================================================ FILE: pkg/provision/providers/qemu.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux || darwin package providers import ( "context" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers/qemu" ) func newQemu(ctx context.Context) (provision.Provisioner, error) { return qemu.NewProvisioner(ctx) } ================================================ FILE: pkg/provision/providers/qemu_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux && !darwin package providers import ( "context" "errors" "github.com/siderolabs/talos/pkg/provision" ) func newQemu(ctx context.Context) (provision.Provisioner, error) { return nil, errors.New("qemu provisioner is not supported on this platform") } ================================================ FILE: pkg/provision/providers/vm/controller.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm // Controller interface should be implemented by the VM to be controlled via the API. type Controller interface { PowerOn() error PowerOff() error Reboot() error PXEBootOnce() error Status() Status } // Status describes current VM status. type Status struct { PoweredOn bool } ================================================ FILE: pkg/provision/providers/vm/dhcpd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "log" "net" "net/netip" "os" "os/exec" "strconv" "strings" "syscall" "time" "github.com/insomniacslk/dhcp/dhcpv4" "github.com/insomniacslk/dhcp/dhcpv4/server4" "github.com/insomniacslk/dhcp/dhcpv6" "github.com/insomniacslk/dhcp/dhcpv6/server6" "github.com/insomniacslk/dhcp/iana" "github.com/siderolabs/gen/xslices" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/provision" "github.com/siderolabs/talos/pkg/provision/providers/vm/internal/ethtool" ) //nolint:gocyclo func handlerDHCP4(serverIP net.IP, statePath string) server4.Handler { return func(conn net.PacketConn, peer net.Addr, m *dhcpv4.DHCPv4) { log.Printf("DHCPv4: got %s", m.Summary()) if m.OpCode != dhcpv4.OpcodeBootRequest { return } db, err := LoadIPAMRecords(statePath) if err != nil { log.Printf("failed loading the IPAM db: %s", err) return } if db == nil { return } row, ok := db[m.ClientHWAddr.String()] if !ok { log.Printf("no match for MAC: %s", m.ClientHWAddr.String()) return } match, ok := row[4] if !ok { log.Printf("no match for MAC on IPv4: %s", m.ClientHWAddr.String()) return } modifiers := []dhcpv4.Modifier{ dhcpv4.WithServerIP(serverIP), dhcpv4.WithNetmask(net.CIDRMask(int(match.Netmask), match.IP.BitLen())), dhcpv4.WithYourIP(match.IP.AsSlice()), dhcpv4.WithOption(dhcpv4.OptRouter(match.Gateway.AsSlice())), dhcpv4.WithOption(dhcpv4.OptIPAddressLeaseTime(5 * time.Minute)), dhcpv4.WithOption(dhcpv4.OptServerIdentifier(serverIP)), } if m.IsOptionRequested(dhcpv4.OptionDomainNameServer) { modifiers = append(modifiers, dhcpv4.WithOption(dhcpv4.OptDNS(netipAddrsToIPs(match.Nameservers)...))) } if match.Hostname != "" && m.IsOptionRequested(dhcpv4.OptionHostName) { modifiers = append(modifiers, dhcpv4.WithOption(dhcpv4.OptHostName(match.Hostname)), ) } resp, err := dhcpv4.NewReplyFromRequest(m, modifiers..., ) if err != nil { log.Printf("failure building response: %s", err) return } if m.IsOptionRequested(dhcpv4.OptionBootfileName) { log.Printf("received PXE boot request from %s", m.ClientHWAddr) log.Printf("sending PXE response to %s: %s/%s", m.ClientHWAddr, match.TFTPServer, match.IPXEBootFilename) if match.TFTPServer != "" { resp.ServerIPAddr = net.ParseIP(match.TFTPServer) resp.UpdateOption(dhcpv4.OptTFTPServerName(match.TFTPServer)) } if match.IPXEBootFilename != "" { resp.UpdateOption(dhcpv4.OptBootFileName(match.IPXEBootFilename)) } } if m.IsOptionRequested(dhcpv4.OptionInterfaceMTU) { resp.UpdateOption(dhcpv4.OptGeneric(dhcpv4.OptionInterfaceMTU, dhcpv4.Uint16(match.MTU).ToBytes())) } switch mt := m.MessageType(); mt { //nolint:exhaustive case dhcpv4.MessageTypeDiscover: resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeOffer)) case dhcpv4.MessageTypeRequest, dhcpv4.MessageTypeInform: resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck)) default: log.Printf("unhandled message type: %v", mt) return } _, err = conn.WriteTo(resp.ToBytes(), peer) if err != nil { log.Printf("failure sending response: %s", err) } } } //nolint:gocyclo func handlerDHCP6(serverHwAddr net.HardwareAddr, statePath string) server6.Handler { return func(conn net.PacketConn, peer net.Addr, m dhcpv6.DHCPv6) { log.Printf("DHCPv6: got %s", m.Summary()) db, err := LoadIPAMRecords(statePath) if err != nil { log.Printf("failed loading the IPAM db: %s", err) return } if db == nil { return } msg, err := m.GetInnerMessage() if err != nil { log.Printf("failed loading inner message: %s", err) return } hwaddr, err := dhcpv6.ExtractMAC(m) if err != nil { log.Printf("error extracting hwaddr: %s", err) return } row, ok := db[hwaddr.String()] if !ok { log.Printf("no match for MAC: %s", hwaddr) return } match, ok := row[6] if !ok { log.Printf("no match for MAC on IPv6: %s", hwaddr) return } modifiers := []dhcpv6.Modifier{ dhcpv6.WithDNS(netipAddrsToIPs(match.Nameservers)...), dhcpv6.WithIANA(dhcpv6.OptIAAddress{ IPv6Addr: match.IP.AsSlice(), PreferredLifetime: 5 * time.Minute, ValidLifetime: 5 * time.Minute, }), dhcpv6.WithServerID(&dhcpv6.DUIDLLT{ HWType: iana.HWTypeEthernet, Time: dhcpv6.GetTime(), LinkLayerAddr: serverHwAddr, }), } if match.Hostname != "" { modifiers = append(modifiers, dhcpv6.WithFQDN(0, match.Hostname), ) } var resp *dhcpv6.Message switch msg.MessageType { //nolint:exhaustive case dhcpv6.MessageTypeSolicit: resp, err = dhcpv6.NewAdvertiseFromSolicit(msg, modifiers...) case dhcpv6.MessageTypeRequest: resp, err = dhcpv6.NewReplyFromMessage(msg, modifiers...) default: log.Printf("unsupported message type %s", msg.Summary()) } if err != nil { log.Printf("failure building response: %s", err) return } _, err = conn.WriteTo(resp.ToBytes(), peer) if err != nil { log.Printf("failure sending response: %s", err) } } } func netipAddrsToIPs(addrs []netip.Addr) []net.IP { return xslices.Map(addrs, func(addr netip.Addr) net.IP { return addr.AsSlice() }) } // DHCPd entrypoint. func DHCPd(ifName string, ips []net.IP, statePath string) error { iface, err := net.InterfaceByName(ifName) if err != nil { return fmt.Errorf("error looking up interface: %w", err) } if err := ethtool.TXOff(iface.Name); err != nil { return fmt.Errorf("error disabling TX checksum offload: %w", err) } var eg errgroup.Group for _, ip := range ips { eg.Go(func() error { if ip.To4() == nil { server, err := server6.NewServer( ifName, nil, handlerDHCP6(iface.HardwareAddr, statePath), server6.WithDebugLogger(), ) if err != nil { log.Printf("error on dhcp6 startup: %s", err) return err } return server.Serve() } server, err := server4.NewServer( ifName, nil, handlerDHCP4(ip, statePath), server4.WithSummaryLogger(), ) if err != nil { log.Printf("error on dhcp4 startup: %s", err) return err } return server.Serve() }) } return eg.Wait() } const ( dhcpPid = "dhcpd.pid" dhcpLog = "dhcpd.log" ) // startDHCPd starts the DHCPd server. func (p *Provisioner) startDHCPd(state *provision.State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(dhcpPid) logFile, err := os.OpenFile(state.GetRelativePath(dhcpLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck statePath, err := state.StatePath() if err != nil { return err } gatewayAddrs := xslices.Map(clusterReq.Network.GatewayAddrs, netip.Addr.String) args := []string{ "dhcpd-launch", "--state-path", statePath, "--addr", strings.Join(gatewayAddrs, ","), "--interface", state.BridgeName, "--ipxe-next-handler", clusterReq.IPXEBootScript, } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing dhcp PID file: %w", err) } return nil } // DestroyDHCPd destoys load balancer. func (p *Provisioner) DestroyDHCPd(state *provision.State) error { pidPath := state.GetRelativePath(dhcpPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/dhcpd_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "context" "fmt" "net" "os" "os/exec" "strings" "time" "github.com/siderolabs/go-retry/retry" "github.com/siderolabs/talos/pkg/provision" ) // CreateDHCPd creates a DHCP server on darwin. // It waits for the interface to appear, shut's down the apple bootp DHCPd server created by qemu by default, // starts the talos DHCP server and then starts the apple bootp server again, which is configured such // that it detects existing dhcp servers on interfaces and doesn't interfare with them. func (p *Provisioner) CreateDHCPd(ctx context.Context, state *provision.State, clusterReq provision.ClusterRequest) error { err := waitForInterface(ctx, state.BridgeName) if err != nil { return err } cmd := exec.CommandContext(ctx, "/bin/launchctl", "unload", "-w", "/System/Library/LaunchDaemons/bootps.plist") err = cmd.Run() if err != nil { return fmt.Errorf("failed to stop native dhcp server: %w", err) } err = p.startDHCPd(state, clusterReq) if err != nil { return err } err = waitForDHCPServerUp(ctx, state) if err != nil { return err } time.Sleep(time.Second) cmd = exec.CommandContext(ctx, "/bin/launchctl", "load", "-w", "/System/Library/LaunchDaemons/bootps.plist") err = cmd.Run() if err != nil { fmt.Printf("warning: failed to start native dhcp server after creating a talos dhcp server: %s", err) } return nil } // waitForInterface returns when interface is found or errors after a minute. func waitForInterface(ctx context.Context, interfaceName string) error { return retry.Constant(1*time.Minute, retry.WithUnits(50*time.Millisecond)).RetryWithContext(ctx, func(_ context.Context) error { ifaces, err := net.Interfaces() if err != nil { return err } for _, iface := range ifaces { if iface.Name == interfaceName { return nil } } return retry.ExpectedError(fmt.Errorf("interface %s not found", interfaceName)) }) } func waitForDHCPServerUp(ctx context.Context, state *provision.State) error { return retry.Constant(1*time.Minute, retry.WithUnits(100*time.Millisecond)).RetryWithContext(ctx, func(_ context.Context) error { logFileData, err := os.ReadFile(state.GetRelativePath(dhcpLog)) if err != nil { return retry.ExpectedError(err) } if strings.Contains(string(logFileData), "Ready to handle requests") { return nil } return retry.ExpectedError(fmt.Errorf("failure: DHCPd server has not started")) }) } ================================================ FILE: pkg/provision/providers/vm/dhcpd_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "context" "github.com/siderolabs/talos/pkg/provision" ) // CreateDHCPd creates a DHCP server. func (p *Provisioner) CreateDHCPd(ctx context.Context, state *provision.State, clusterReq provision.ClusterRequest) error { return p.startDHCPd(state, clusterReq) } ================================================ FILE: pkg/provision/providers/vm/disk.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "errors" "fmt" "os" "github.com/detailyang/go-fallocate" "github.com/siderolabs/talos/pkg/provision" ) // UserDiskName returns disk device path. func (p *Provisioner) UserDiskName(index int) string { // the disk IDs are assigned in the following way: // * ata-QEMU_HARDDISK_QM00001 // * ata-QEMU_HARDDISK_QM00003 // * ata-QEMU_HARDDISK_QM00005 return fmt.Sprintf("/dev/disk/by-id/ata-QEMU_HARDDISK_QM%05d", (index-1)*2+1) } // CreateDisks creates empty disk files for each disk. // //nolint:gocyclo func (p *Provisioner) CreateDisks(state *provision.State, nodeReq provision.NodeRequest) (diskPaths []string, err error) { const QEMUAlignment = 4 * 1024 * 1024 // 4 MiB, required by QEMU diskPaths = make([]string, len(nodeReq.Disks)) for i, disk := range nodeReq.Disks { if disk.Driver == "virtiofs" { // virtiofs does not require disk image files // skip creating disk files for such disks, but keep the index consistent // and add socket path instead // // however we need to preallocate a file for shm backing store diskPaths[i] = state.GetRelativePath(fmt.Sprintf("%s-%d.virtiofs.sock", nodeReq.Name, i)) // check if we already have a shm file created shmFilePath := state.GetShmPath(fmt.Sprintf("shm-%s", nodeReq.Name)) if _, err = os.Stat(shmFilePath); os.IsNotExist(err) { var shmF *os.File shmF, err = os.Create(shmFilePath) if err != nil { return nil, fmt.Errorf("failed to create shm backing file: %w", err) } defer shmF.Close() //nolint:errcheck shmSize := nodeReq.Memory // already in bytes if err = shmF.Truncate(shmSize); err != nil { return nil, fmt.Errorf("failed to truncate shm backing file: %w", err) } if err = fallocate.Fallocate(shmF, 0, shmSize); err != nil { return nil, fmt.Errorf("failed to preallocate shm backing file: %w", err) } } else if err != nil { return nil, fmt.Errorf("failed to stat shm backing file: %w", err) } continue } diskPath := state.GetRelativePath(fmt.Sprintf("%s-%d.disk", nodeReq.Name, i)) diskSize := (disk.Size + QEMUAlignment - 1) / QEMUAlignment * QEMUAlignment var diskF *os.File diskF, err = os.Create(diskPath) if err != nil { return nil, err } defer diskF.Close() //nolint:errcheck if err = diskF.Truncate(int64(diskSize)); err != nil { return nil, err } if !disk.SkipPreallocate { if err = fallocate.Fallocate(diskF, 0, int64(disk.Size)); err != nil { fmt.Fprintf(os.Stderr, "WARNING: failed to preallocate disk space for %q (size %d): %s", diskPath, diskSize, err) } } diskPaths[i] = diskPath } if len(diskPaths) == 0 { return nil, errors.New("node request must have at least one disk defined to be used as primary disk") } return diskPaths, nil } ================================================ FILE: pkg/provision/providers/vm/dnsd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "log/slog" "net" "os" "github.com/miekg/dns" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/provision" ) // DNSd entrypoint. func DNSd(ips []net.IP, resolvConfPath string) error { var eg errgroup.Group config, err := dns.ClientConfigFromFile(resolvConfPath) if err != nil { return fmt.Errorf("failed to read %q: %v", resolvConfPath, err) } initLog := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ AddSource: true, Level: slog.LevelDebug, })) defer initLog.Info("bye") dnsServe := func(ip net.IP, mode string) error { addr := net.JoinHostPort(ip.String(), "53") log := initLog.With("mode", mode, "addr", addr) server := &dns.Server{ Addr: addr, Net: mode, Handler: dns.HandlerFunc(forwardHandler(mode, log, config)), } log.Info("starting DNS forwarder server") return server.ListenAndServe() } for _, ip := range ips { eg.Go(func() error { return dnsServe(ip, "udp") }) eg.Go(func() error { return dnsServe(ip, "tcp") }) } return eg.Wait() } const ( dnsPid = "dnsd.pid" dnsLog = "dnsd.log" ) // CreateDNSd creates the DNSd server. func (p *Provisioner) CreateDNSd(state *State, clusterReq provision.ClusterRequest) error { return p.startDNSd(state, clusterReq) } // DestroyDNSd destoys DNSd server. func (p *Provisioner) DestroyDNSd(state *State) error { return p.stopDNSd(state) } func forwardHandler(mode string, log *slog.Logger, config *dns.ClientConfig) func(w dns.ResponseWriter, r *dns.Msg) { return func(w dns.ResponseWriter, r *dns.Msg) { slog.Debug("handling DNS request", "request", r.String()) c := new(dns.Client) c.Net = mode var ( resp *dns.Msg err error ) for _, serverAddr := range config.Servers { log.Debug("making DNS request", "target", serverAddr+":"+config.Port) resp, _, err = c.Exchange(r, net.JoinHostPort(serverAddr, config.Port)) if err != nil { log.Debug("DNS request failed", "error", err) continue } if resp != nil && (resp.Rcode == dns.RcodeServerFailure || resp.Rcode == dns.RcodeRefused) { log.Debug("DNS request succeeded, but RCODE reports failure", "response", resp.String()) continue } break } if resp == nil { log.Error("DNS exchange failed", "error", err) dns.HandleFailed(w, r) return } log.Debug("writing DNS response", "response", resp.String()) if err := w.WriteMsg(resp); err != nil { log.Error("failed to write response", "error", err) } } } ================================================ FILE: pkg/provision/providers/vm/dnsd_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "github.com/siderolabs/talos/pkg/provision" ) func (p *Provisioner) startDNSd(_ *State, _ provision.ClusterRequest) error { return nil } func (p *Provisioner) stopDNSd(_ *State) error { return nil } ================================================ FILE: pkg/provision/providers/vm/dnsd_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "net/netip" "os" "os/exec" "strconv" "strings" "syscall" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/provision" ) func (p *Provisioner) startDNSd(state *State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(dnsPid) logFile, err := os.OpenFile(state.GetRelativePath(dnsLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck gatewayAddrs := xslices.Map(clusterReq.Network.GatewayAddrs, netip.Addr.String) args := []string{ "dnsd-launch", "--addr", strings.Join(gatewayAddrs, ","), "--resolv-conf", "/etc/resolv.conf", } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing dns PID file: %w", err) } return nil } func (p *Provisioner) stopDNSd(state *State) error { pidPath := state.GetRelativePath(dnsPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/image_cache.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "net" "net/netip" "os" "os/exec" "strconv" "syscall" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/provision" ) const ( imageCachePid = "image-cache.pid" imageCacheLog = "image-cache.log" ) // CreateImageCache creates the Image Cache server. func (p *Provisioner) CreateImageCache(state *State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(imageCachePid) logFile, err := os.OpenFile(state.GetRelativePath(imageCacheLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck gatewayAddrs := xslices.Map(clusterReq.Network.GatewayAddrs, netip.Addr.String) gatewayAddrs = xslices.Map(gatewayAddrs, func(s string) string { return net.JoinHostPort(s, fmt.Sprint(clusterReq.Network.ImageCachePort)) }) args := []string{ "image", "cache-serve", "--address", gatewayAddrs[0], "--image-cache-path", clusterReq.Network.ImageCachePath, "--tls-cert-file", clusterReq.Network.ImageCacheTLSCertFile, "--tls-key-file", clusterReq.Network.ImageCacheTLSKeyFile, } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing imageCache PID file: %w", err) } return nil } // DestroyImageCache destoys Image Cache server. func (p *Provisioner) DestroyImageCache(state *State) error { pidPath := state.GetRelativePath(imageCachePid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/internal/ethtool/ethtool_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package ethtool // Origin: https://github.com/weaveworks/weave/blob/master/net/ethtool.go import ( "fmt" "syscall" "unsafe" ) // Linux constants. // //nolint:revive const ( SIOCETHTOOL = 0x8946 // linux/sockios.h ETHTOOL_GTXCSUM = 0x00000016 // linux/ethtool.h ETHTOOL_STXCSUM = 0x00000017 // linux/ethtool.h IFNAMSIZ = 16 // linux/if.h ) // linux/if.h 'struct ifreq'. type ifReqData struct { Name [IFNAMSIZ]byte Data uintptr } // linux/ethtool.h 'struct ethtool_value'. type ethtoolValue struct { Cmd uint32 Data uint32 } func ioctlEthtool(fd int, argp uintptr) error { _, _, errno := syscall.RawSyscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(SIOCETHTOOL), argp) if errno != 0 { return errno } return nil } // TXOff disables TX checksum offload on specified interface. func TXOff(name string) error { if len(name)+1 > IFNAMSIZ { return fmt.Errorf("name too long") } socket, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0) if err != nil { return err } defer syscall.Close(socket) //nolint:errcheck // Request current value value := ethtoolValue{Cmd: ETHTOOL_GTXCSUM} request := ifReqData{Data: uintptr(unsafe.Pointer(&value))} copy(request.Name[:], name) if err := ioctlEthtool(socket, uintptr(unsafe.Pointer(&request))); err != nil { return err } if value.Data == 0 { // if already off, don't try to change return nil } value = ethtoolValue{ETHTOOL_STXCSUM, 0} return ioctlEthtool(socket, uintptr(unsafe.Pointer(&request))) } ================================================ FILE: pkg/provision/providers/vm/internal/ethtool/ethtool_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux package ethtool // TXOff disables TX checksum offload on specified interface. // // Not applicable on non-linux systems. func TXOff(name string) error { return nil } ================================================ FILE: pkg/provision/providers/vm/internal/ipxe/ipxe.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ipxe provides utility to deliver iPXE images and build iPXE scripts. package ipxe import ( "bytes" "embed" "errors" "fmt" "io" "log" "path/filepath" "text/template" ) //go:embed "data/*" var ipxeFiles embed.FS // TFTPHandler is called when client starts file download from the TFTP server. // // TFTP handler also patches the iPXE binary on the fly with the script // which chainloads next handler. func TFTPHandler(next string) func(filename string, rf io.ReaderFrom) error { return func(filename string, rf io.ReaderFrom) error { log.Printf("tftp request: %s", filename) file, err := ipxeFiles.Open(filepath.Join("data", filename)) if err != nil { return err } defer file.Close() //nolint:errcheck contents, err := io.ReadAll(file) if err != nil { return err } var script bytes.Buffer if err = scriptTemplate.Execute(&script, struct { Next string }{ Next: next, }); err != nil { return err } contents, err = patchScript(contents, script.Bytes()) if err != nil { return fmt.Errorf("error patching %q: %w", filename, err) } _, err = rf.ReadFrom(bytes.NewReader(contents)) return err } } // scriptTemplate to run DHCP and chain the boot to the .Next endpoint. var scriptTemplate = template.Must(template.New("iPXE embedded").Parse(`#!ipxe prompt --key 0x02 --timeout 2000 Press Ctrl-B for the iPXE command line... && shell || {{/* print interfaces */}} ifstat {{/* retry 10 times overall */}} set attempts:int32 10 set x:int32 0 :retry_loop set idx:int32 0 :loop {{/* try DHCP on each interface */}} isset ${net${idx}/mac} || goto exhausted ifclose iflinkwait --timeout 5000 net${idx} || goto next_iface dhcp net${idx} || goto next_iface goto boot :next_iface inc idx && goto loop :boot {{/* attempt boot, if fails try next iface */}} route chain --replace {{ .Next }} || goto next_iface :exhausted echo echo Failed to iPXE boot successfully via all interfaces iseq ${x} ${attempts} && goto fail || echo Retrying... echo inc x goto retry_loop :fail echo echo Failed to get a valid response after ${attempts} attempts echo echo Rebooting in 5 seconds... sleep 5 reboot `)) var ( placeholderStart = []byte("# *PLACEHOLDER START*") placeholderEnd = []byte("# *PLACEHOLDER END*") ) // patchScript patches the iPXE script into the iPXE binary. // // The iPXE binary should be built uncompressed with an embedded // script stub which contains abovementioned placeholders. func patchScript(contents, script []byte) ([]byte, error) { start := bytes.Index(contents, placeholderStart) if start == -1 { return nil, errors.New("placeholder start not found") } end := bytes.Index(contents, placeholderEnd) if end == -1 { return nil, errors.New("placeholder end not found") } if end < start { return nil, errors.New("placeholder end before start") } end += len(placeholderEnd) length := end - start if len(script) > length { return nil, fmt.Errorf("script size %d is larger than placeholder space %d", len(script), length) } script = append(script, bytes.Repeat([]byte{'\n'}, length-len(script))...) copy(contents[start:end], script) return contents, nil } ================================================ FILE: pkg/provision/providers/vm/ipam.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "bufio" "encoding/json" "errors" "fmt" "io/fs" "net/netip" "os" "path/filepath" ) // IPAMRecord describes a single record about a node. type IPAMRecord struct { IP netip.Addr Netmask byte MAC string Hostname string Gateway netip.Addr MTU int Nameservers []netip.Addr TFTPServer string IPXEBootFilename string } // IPAMDatabase is a mapping from MAC address to records with IPv4/IPv6 flag. type IPAMDatabase map[string]map[int]IPAMRecord const dbFile = "ipam.db" // DumpIPAMRecord appends IPAM record to the database. func DumpIPAMRecord(statePath string, record IPAMRecord) error { f, err := os.OpenFile(filepath.Join(statePath, dbFile), os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModePerm) if err != nil { return err } defer f.Close() //nolint:errcheck // need atomic write, buffering json b, err := json.Marshal(record) if err != nil { return fmt.Errorf("error marshaling IPAM record: %w", err) } _, err = f.Write(append(b, '\n')) return err } // LoadIPAMRecords loads all the IPAM records indexed by the MAC address. func LoadIPAMRecords(statePath string) (IPAMDatabase, error) { f, err := os.Open(filepath.Join(statePath, dbFile)) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, nil } return nil, err } defer f.Close() //nolint:errcheck result := make(IPAMDatabase) scanner := bufio.NewScanner(f) for scanner.Scan() { var record IPAMRecord if err := json.Unmarshal(scanner.Bytes(), &record); err != nil { return nil, err } ipFormat := 4 if record.IP.Is6() { ipFormat = 6 } if result[record.MAC] == nil { result[record.MAC] = make(map[int]IPAMRecord) } result[record.MAC][ipFormat] = record } return result, scanner.Err() } ================================================ FILE: pkg/provision/providers/vm/json-logs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "crypto/rand" "fmt" "io" "os" "os/exec" "strconv" "syscall" "github.com/siderolabs/talos/pkg/provision" ) const ( jsonLogsPid = "json-logs.pid" jsonLogsLog = "json-logs.log" ) // CreateJSONLogs creates JSON logs server. func (p *Provisioner) CreateJSONLogs(state *provision.State, clusterReq provision.ClusterRequest, options provision.Options) error { pidPath := state.GetRelativePath(jsonLogsPid) logFile, err := os.OpenFile(state.GetRelativePath(jsonLogsLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck key := make([]byte, 32) if _, err = io.ReadFull(rand.Reader, key); err != nil { return err } args := []string{ "json-logs-launch", "--addr", options.JSONLogsEndpoint, } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing LB PID file: %w", err) } return nil } // DestroyJSONLogs destroys JSON logs server. func (p *Provisioner) DestroyJSONLogs(state *provision.State) error { pidPath := state.GetRelativePath(jsonLogsPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/kms.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "crypto/rand" "encoding/base64" "fmt" "io" "os" "os/exec" "strconv" "syscall" "github.com/siderolabs/talos/pkg/provision" ) const ( kmsPid = "kms.pid" kmsLog = "kms.log" ) // CreateKMS creates KMS server. func (p *Provisioner) CreateKMS(state *provision.State, clusterReq provision.ClusterRequest, options provision.Options) error { pidPath := state.GetRelativePath(kmsPid) logFile, err := os.OpenFile(state.GetRelativePath(kmsLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck key := make([]byte, 32) if _, err = io.ReadFull(rand.Reader, key); err != nil { return err } args := []string{ "kms-launch", "--kms-addr", options.KMSEndpoint, "--kms-key", base64.StdEncoding.EncodeToString(key), } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing LB PID file: %w", err) } return nil } // DestroyKMS destroys KMS server. func (p *Provisioner) DestroyKMS(state *provision.State) error { pidPath := state.GetRelativePath(kmsPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/launch.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "context" "encoding/json" "errors" "fmt" "io" "net/http" "net/netip" "os" "os/signal" "syscall" "github.com/siderolabs/talos/pkg/machinery/nethelpers" "github.com/siderolabs/talos/pkg/provision/internal/inmemhttp" ) // ReadConfig loads configuration from stdin. func ReadConfig(config any) error { d := json.NewDecoder(os.Stdin) if err := d.Decode(config); err != nil { return fmt.Errorf("error decoding config from stdin: %w", err) } if d.More() { return errors.New("extra unexpected input on stdin") } return os.Stdin.Close() } // ConfigureSignals configures signal handling for the process. func ConfigureSignals() chan os.Signal { signal.Ignore(syscall.SIGHUP) c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT) return c } func httpPostWrapper(f func() error) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, req *http.Request) { if req.Body != nil { _, _ = io.Copy(io.Discard, req.Body) //nolint:errcheck req.Body.Close() //nolint:errcheck } if req.Method != http.MethodPost { w.WriteHeader(http.StatusNotImplemented) return } err := f() if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprint(w, err.Error()) return } w.WriteHeader(http.StatusOK) }, ) } func httpGetWrapper(f func(w io.Writer)) http.Handler { return http.HandlerFunc( func(w http.ResponseWriter, req *http.Request) { if req.Body != nil { _, _ = io.Copy(io.Discard, req.Body) //nolint:errcheck req.Body.Close() //nolint:errcheck } switch req.Method { case http.MethodHead: w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) case http.MethodGet: w.Header().Add("Content-Type", "application/json") w.WriteHeader(http.StatusOK) f(w) default: w.WriteHeader(http.StatusNotImplemented) } }, ) } // NewHTTPServer creates new inmemhttp.Server and mounts config file into it. func NewHTTPServer(ctx context.Context, gatewayAddr netip.Addr, port int, config []byte, controller Controller) (inmemhttp.Server, error) { httpServer, err := inmemhttp.NewServer(ctx, nethelpers.JoinHostPort(gatewayAddr.String(), port)) if err != nil { return nil, fmt.Errorf("error launching in-memory HTTP server: %w", err) } if err = httpServer.AddFile("config.yaml", config); err != nil { return nil, err } if controller != nil { for _, method := range []struct { pattern string f func() error }{ { pattern: "/poweron", f: controller.PowerOn, }, { pattern: "/poweroff", f: controller.PowerOff, }, { pattern: "/reboot", f: controller.Reboot, }, { pattern: "/pxeboot", f: controller.PXEBootOnce, }, } { httpServer.AddHandler(method.pattern, httpPostWrapper(method.f)) } httpServer.AddHandler( "/status", httpGetWrapper( func(w io.Writer) { json.NewEncoder(w).Encode(controller.Status()) //nolint:errcheck }, ), ) } return httpServer, nil } ================================================ FILE: pkg/provision/providers/vm/loadbalancer.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "os" "os/exec" "strconv" "strings" "syscall" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/provision" ) const ( lbPid = "lb.pid" lbLog = "lb.log" ) // CreateLoadBalancer creates load balancer. func (p *Provisioner) CreateLoadBalancer(state *provision.State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(lbPid) logFile, err := os.OpenFile(state.GetRelativePath(lbLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck controlPlaneIPs := xslices.Map(clusterReq.Nodes.ControlPlaneNodes(), func(req provision.NodeRequest) string { return req.IPs[0].String() }) ports := xslices.Map(clusterReq.Network.LoadBalancerPorts, strconv.Itoa) args := []string{ "loadbalancer-launch", "--loadbalancer-addr", getLbBindIP(clusterReq.Network.GatewayAddrs[0]), "--loadbalancer-upstreams", strings.Join(controlPlaneIPs, ","), } if len(ports) > 0 { args = append(args, "--loadbalancer-ports", strings.Join(ports, ",")) } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing LB PID file: %w", err) } return nil } // DestroyLoadBalancer destroys load balancer. func (p *Provisioner) DestroyLoadBalancer(state *provision.State) error { pidPath := state.GetRelativePath(lbPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/loadbalancer_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import "net/netip" // getLbBindIP returns the 0.0.0.0 address to bind to all interfaces on macos. // The bridge interface address is not used as the bridge is not yet created at this stage. // Multiple loadbalancers can be assigned via different ports. func getLbBindIP(_ netip.Addr) string { return "0.0.0.0" } ================================================ FILE: pkg/provision/providers/vm/loadbalancer_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import "net/netip" // getLbBindIP returns the gateway address to bind the loadbalancer to the bridge interface. func getLbBindIP(gateway netip.Addr) string { return gateway.String() } ================================================ FILE: pkg/provision/providers/vm/network.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "errors" "fmt" "slices" ) // GetVmnetInterfaceName returns the name of the interface that will be assigned to the next vmnet interface. // The name is assigned incrementing by one, starting from "bridge100". func GetVmnetInterfaceName(allCurrentInterfaces []string) (string, error) { for i := 100; i < 200; i++ { interfaceName := fmt.Sprintf("bridge%d", i) if slices.Index(allCurrentInterfaces, interfaceName) == -1 { return interfaceName, nil } } return "", errors.New("all interface names seem to be already in use") } ================================================ FILE: pkg/provision/providers/vm/network_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "context" "net" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/talos/pkg/provision" ) // CreateNetwork on darwin assigns the bridge name to the to-be created interface name. // The interface itself is later created by qemu, but the name needs to be known so that the dhcp server can be linked to the interface. func (p *Provisioner) CreateNetwork(ctx context.Context, state *provision.State, network provision.NetworkRequest, options provision.Options) error { ifaces, err := net.Interfaces() if err != nil { return err } ifNames := xslices.Map(ifaces, func(iface net.Interface) string { return iface.Name }) bridgeNAme, err := GetVmnetInterfaceName(ifNames) if err != nil { return err } state.BridgeName = bridgeNAme return nil } // DestroyNetwork does nothing on darwin as the network is automatically cleaned up by qemu when the final machine of a cidr block is killed. func (p *Provisioner) DestroyNetwork(state *provision.State) error { return nil } ================================================ FILE: pkg/provision/providers/vm/network_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "bytes" "context" "crypto/sha256" "encoding/hex" "errors" "fmt" "math" "net" "net/netip" "os" "strconv" "strings" "text/template" "github.com/containernetworking/cni/libcni" "github.com/containernetworking/plugins/pkg/testutils" "github.com/coreos/go-iptables/iptables" "github.com/florianl/go-tc" "github.com/florianl/go-tc/core" "github.com/google/uuid" "github.com/jsimonetti/rtnetlink/v2" "github.com/siderolabs/gen/xslices" sideronet "github.com/siderolabs/net" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/provision" ) // CreateNetwork builds bridge interface name by taking part of checksum of the network name // so that interface name is defined by network name, and different networks have // different bridge interfaces. // //nolint:gocyclo func (p *Provisioner) CreateNetwork(ctx context.Context, state *provision.State, network provision.NetworkRequest, options provision.Options) error { networkNameHash := sha256.Sum256([]byte(network.Name)) state.BridgeName = fmt.Sprintf("%s%s", "talos", hex.EncodeToString(networkNameHash[:])[:8]) // bring up the bridge interface for the first time to get gateway IP assigned t := template.Must(template.New("bridge").Parse(bridgeTemplate)) var buf bytes.Buffer err := t.Execute(&buf, struct { NetworkName string InterfaceName string MTU string IPMasquerade bool }{ NetworkName: network.Name, InterfaceName: state.BridgeName, MTU: strconv.Itoa(network.MTU), IPMasquerade: !network.Airgapped, }) if err != nil { return fmt.Errorf("error templating bridge CNI config: %w", err) } bridgeConfig, err := libcni.NetworkPluginConfFromBytes(buf.Bytes()) if err != nil { return fmt.Errorf("error parsing bridge CNI config: %w", err) } cniConfig := libcni.NewCNIConfigWithCacheDir(network.CNI.BinPath, network.CNI.CacheDir, nil) ns, err := testutils.NewNS() if err != nil { return err } defer func() { ns.Close() //nolint:errcheck testutils.UnmountNS(ns) //nolint:errcheck }() // pick a fake address to use for provisioning an interface fakeIPs := make([]string, len(network.CIDRs)) for j := range fakeIPs { var fakeIP netip.Addr fakeIP, err = sideronet.NthIPInNetwork(network.CIDRs[j], 2) if err != nil { return err } fakeIPs[j] = sideronet.FormatCIDR(fakeIP, network.CIDRs[j]) } gatewayAddrs := xslices.Map(network.GatewayAddrs, netip.Addr.String) containerID := uuid.New().String() runtimeConf := libcni.RuntimeConf{ ContainerID: containerID, NetNS: ns.Path(), IfName: "veth0", Args: [][2]string{ {"IP", strings.Join(fakeIPs, ",")}, {"GATEWAY", strings.Join(gatewayAddrs, ",")}, {"IgnoreUnknown", "1"}, }, } _, err = cniConfig.AddNetwork(ctx, bridgeConfig, &runtimeConf) if err != nil { return fmt.Errorf("error provisioning bridge CNI network: %w", err) } err = cniConfig.DelNetwork(ctx, bridgeConfig, &runtimeConf) if err != nil { return fmt.Errorf("error deleting bridge CNI network: %w", err) } // prepare an actual network config to be used by the VMs t = template.Must(template.New("network").Parse(networkTemplate)) buf.Reset() err = t.Execute(&buf, struct { NetworkName string InterfaceName string MTU string IPMasquerade bool }{ NetworkName: network.Name, InterfaceName: state.BridgeName, MTU: strconv.Itoa(network.MTU), IPMasquerade: !network.Airgapped, }) if err != nil { return fmt.Errorf("error templating VM CNI config: %w", err) } if state.VMCNIConfig, err = libcni.ConfListFromBytes(buf.Bytes()); err != nil { return fmt.Errorf("error parsing VM CNI config: %w", err) } // allow traffic on the bridge via `DOCKER-USER` chain // Docker enables br-netfilter which causes layer2 packets to be filtered with iptables, but we'd like to skip that // if Docker is not running, this will be no-op // // See https://serverfault.com/questions/963759/docker-breaks-libvirt-bridge-network for more details if err = p.allowBridgeTraffic(state.BridgeName); err != nil { return fmt.Errorf("error configuring DOCKER-USER chain: %w", err) } // configure bridge interface with network chaos if flag is set if network.NetworkChaos { if err = p.configureNetworkChaos(network, state, options); err != nil { return err } } return nil } func (p *Provisioner) allowBridgeTraffic(bridgeName string) error { ipt, err := iptables.New() if err != nil { return fmt.Errorf("error initializing iptables: %w", err) } chainExists, err := ipt.ChainExists("filter", "DOCKER-USER") if err != nil { return fmt.Errorf("error checking chain existence: %w", err) } if !chainExists { if err = ipt.NewChain("filter", "DOCKER-USER"); err != nil { return fmt.Errorf("error creating DOCKER-USER chain: %w", err) } } if err := ipt.InsertUnique("filter", "DOCKER-USER", 1, "-i", bridgeName, "-o", bridgeName, "-j", "ACCEPT"); err != nil { return fmt.Errorf("error inserting rule into DOCKER-USER chain: %w", err) } return nil } func (p *Provisioner) dropBridgeTrafficRule(bridgeName string) error { ipt, err := iptables.New() if err != nil { return fmt.Errorf("error initializing iptables: %w", err) } chainExists, err := ipt.ChainExists("filter", "DOCKER-USER") if err != nil { return fmt.Errorf("error checking chain existence: %w", err) } if !chainExists { return nil } if err := ipt.DeleteIfExists("filter", "DOCKER-USER", "-i", bridgeName, "-o", bridgeName, "-j", "ACCEPT"); err != nil { return fmt.Errorf("error deleting rule in DOCKER-USER chain: %w", err) } return nil } func getTicksInUsec() (float64, error) { data, err := os.ReadFile("/proc/net/psched") if err != nil { return 0, err } parts := strings.Split(strings.TrimSpace(string(data)), " ") if len(parts) < 3 { return 0, errors.New("unexpected format") } var vals [3]uint64 for i := range vals { vals[i], err = strconv.ParseUint(parts[i], 16, 32) if err != nil { return 0, err } } // compatibility if vals[2] == 1000000000 { vals[0] = vals[1] } clockFactor := float64(vals[2]) / 1000000 return float64(vals[0]) / float64(vals[1]) * clockFactor, nil } //nolint:gocyclo func (p *Provisioner) configureNetworkChaos(network provision.NetworkRequest, state *provision.State, options provision.Options) error { if (network.Bandwidth != 0) && (network.Latency != 0 || network.Jitter != 0 || network.PacketLoss != 0 || network.PacketReorder != 0 || network.PacketCorrupt != 0) { return errors.New("bandwidth and other chaos options cannot be used together") } tcnl, err := tc.Open(&tc.Config{}) if err != nil { return fmt.Errorf("could not open tc: %v", err) } defer tcnl.Close() //nolint:errcheck link, err := net.InterfaceByName(state.BridgeName) if err != nil { return fmt.Errorf("could not get link: %v", err) } fmt.Fprintln(options.LogWriter, "network chaos enabled on interface:", state.BridgeName) if network.Bandwidth != 0 { fmt.Fprintf(options.LogWriter, " bandwidth: %4d kbps\n", network.Bandwidth) ticksInUsec, err := getTicksInUsec() if err != nil { return fmt.Errorf("could not get ticks in usec: %w", err) } rate := network.Bandwidth * 1000 / 8 // rate in kbps latency := 0.2 // 200ms burst := 50 * 1000 // 50kb limit := uint32(float64(rate)*latency + float64(burst)) buffer := uint32(1000000.0 * float64(burst) / float64(rate) * ticksInUsec) qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(link.Index), Handle: core.BuildHandle(tc.HandleRoot, 0x0), Parent: tc.HandleRoot, Info: 0, }, Attribute: tc.Attribute{ Kind: "tbf", Tbf: &tc.Tbf{ Parms: &tc.TbfQopt{ Limit: limit, Rate: tc.RateSpec{ Rate: uint32(rate), Linklayer: 1, }, Buffer: buffer, }, }, }, } if err := tcnl.Qdisc().Add(&qdisc); err != nil { return fmt.Errorf("could not add netem qdisc: %v", err) } } else { packetLoss := network.PacketLoss * 100 packetReorder := network.PacketReorder * 100 packetCorrupt := network.PacketCorrupt * 100 fmt.Fprintf(options.LogWriter, " jitter: %4dms\n", network.Jitter.Milliseconds()) fmt.Fprintf(options.LogWriter, " latency: %4dms\n", network.Latency.Milliseconds()) fmt.Fprintf(options.LogWriter, " packet loss: %4v%%\n", packetLoss) fmt.Fprintf(options.LogWriter, " packet reordering: %4v%%\n", packetReorder) fmt.Fprintf(options.LogWriter, " packet corruption: %4v%%\n", packetCorrupt) qdisc := tc.Object{ Msg: tc.Msg{ Family: unix.AF_UNSPEC, Ifindex: uint32(link.Index), Handle: core.BuildHandle(tc.HandleRoot, 0x0), Parent: tc.HandleRoot, Info: 0, }, Attribute: tc.Attribute{ Kind: "netem", Netem: &tc.Netem{ Jitter64: new(int64(network.Jitter)), Latency64: new(int64(network.Latency)), Qopt: tc.NetemQopt{ Limit: 1000, Loss: uint32(packetLoss / 100 * math.MaxUint32), }, Corrupt: &tc.NetemCorrupt{ Probability: uint32(packetCorrupt / 100 * math.MaxUint32), }, Reorder: &tc.NetemReorder{ Probability: uint32(packetReorder / 100 * math.MaxUint32), }, }, }, } if err := tcnl.Qdisc().Add(&qdisc); err != nil { return fmt.Errorf("could not add netem qdisc: %v", err) } } return nil } // DestroyNetwork destroy bridge interface by name to clean up. func (p *Provisioner) DestroyNetwork(state *provision.State) error { iface, err := net.InterfaceByName(state.BridgeName) if err != nil { return fmt.Errorf("error looking up bridge interface %q: %w", state.BridgeName, err) } rtconn, err := rtnetlink.Dial(nil) if err != nil { return fmt.Errorf("error dialing rnetlink: %w", err) } if err = rtconn.Link.Delete(uint32(iface.Index)); err != nil { return fmt.Errorf("error deleting bridge interface: %w", err) } if err = p.dropBridgeTrafficRule(state.BridgeName); err != nil { return fmt.Errorf("error dropping bridge traffic rule: %w", err) } return nil } const bridgeTemplate = ` { "name": "{{ .NetworkName }}", "cniVersion": "0.4.0", "type": "bridge", "bridge": "{{ .InterfaceName }}", "ipMasq": {{ .IPMasquerade }}, "isGateway": true, "isDefaultGateway": true, "ipam": { "type": "static" }, "mtu": {{ .MTU }} } ` const networkTemplate = ` { "name": "{{ .NetworkName }}", "cniVersion": "0.4.0", "plugins": [ { "type": "bridge", "bridge": "{{ .InterfaceName }}", "ipMasq": {{ .IPMasquerade }}, "isGateway": true, "isDefaultGateway": true, "ipam": { "type": "static" }, "mtu": {{ .MTU }} }, { "type": "firewall" }, { "type": "tc-redirect-tap" } ] } ` ================================================ FILE: pkg/provision/providers/vm/network_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm_test import ( "testing" "github.com/stretchr/testify/assert" "github.com/siderolabs/talos/pkg/provision/providers/vm" ) func TestGetVmnetInterfaceNameNoVmnetInterface(t *testing.T) { interfaces := []string{ "bridge", "bridge1", "eth0", "utun1", "bridge001", "bridge1001", } result, err := vm.GetVmnetInterfaceName(interfaces) assert.NoError(t, err) assert.Equal(t, "bridge100", result) } func TestGetVmnetInterfaceNameWithExistingVmnetInterfaces(t *testing.T) { interfaces := []string{ "bridge", "bridge1", "eth0", "utun1", "bridge001", "bridge1001", "bridge100", "bridge101", "bridge104", } result, err := vm.GetVmnetInterfaceName(interfaces) assert.NoError(t, err) assert.Equal(t, "bridge102", result) } ================================================ FILE: pkg/provision/providers/vm/node.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "slices" multierror "github.com/hashicorp/go-multierror" "github.com/siderolabs/talos/pkg/provision" ) // DestroyNodes destroys all VMs. func (p *Provisioner) DestroyNodes(cluster provision.ClusterInfo, options *provision.Options) error { errCh := make(chan error) nodes := slices.Concat(cluster.Nodes, cluster.ExtraNodes) for _, node := range nodes { go func(node provision.NodeInfo) { fmt.Fprintln(options.LogWriter, "stopping VM", node.Name) errCh <- p.DestroyNode(node) }(node) } var multiErr *multierror.Error for range nodes { multiErr = multierror.Append(multiErr, <-errCh) } return multiErr.ErrorOrNil() } // DestroyNode destroys VM. func (p *Provisioner) DestroyNode(node provision.NodeInfo) error { return StopProcessByPidfile(node.ID) // node.ID stores PID path for control process } ================================================ FILE: pkg/provision/providers/vm/process.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "errors" "fmt" "io/fs" "os" "syscall" "time" "github.com/siderolabs/go-retry/retry" ) // StopProcessByPidfile stops a process by reading its PID from a file. func StopProcessByPidfile(pidPath string) error { pidFile, err := os.Open(pidPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil } return fmt.Errorf("error checking PID file %q: %w", pidPath, err) } defer pidFile.Close() //nolint:errcheck var pid int if _, err = fmt.Fscanf(pidFile, "%d", &pid); err != nil { return fmt.Errorf("error reading PID for %q: %w", pidPath, err) } proc, err := os.FindProcess(pid) if err != nil { return fmt.Errorf("error finding process %d for %q: %w", pid, pidPath, err) } if err = proc.Signal(syscall.SIGTERM); err != nil { if err.Error() == "os: process already finished" { return nil } return fmt.Errorf("error sending SIGTERM to %d (path %q): %w", pid, pidPath, err) } // wait for the process to exit, this is using (unreliable and slow) polling return retry.Constant(30*time.Second, retry.WithUnits(100*time.Millisecond)).Retry(func() error { // wait for the process if it's our child, we should clean up zombies, but if it's not our child, it would return ECHILD proc.Wait() //nolint:errcheck signalErr := proc.Signal(syscall.Signal(0)) if signalErr == nil { return retry.ExpectedErrorf("process %d still running", pid) } return nil }) } ================================================ FILE: pkg/provision/providers/vm/reflect.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "context" "fmt" "github.com/siderolabs/talos/pkg/provision" ) // Reflect decode state file. func (p *Provisioner) Reflect(ctx context.Context, clusterName, stateDirectory string) (provision.Cluster, error) { state, err := provision.ReadState(ctx, clusterName, stateDirectory) if err != nil { return nil, err } if state.ProvisionerName != p.Name { return nil, fmt.Errorf("cluster %q was created with different provisioner %q", clusterName, state.ProvisionerName) } return state, nil } ================================================ FILE: pkg/provision/providers/vm/siderolink-agent.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "errors" "fmt" "os" "os/exec" "strconv" "syscall" "github.com/siderolabs/talos/pkg/provision" ) const ( siderolinkAgentPid = "siderolink-agent.pid" siderolinkAgentLog = "siderolink-agent.log" siderolinkCert = "siderolink-agent-cert.pem" siderolinkKey = "siderolink-agent-key.pem" ) // CreateSiderolinkAgent creates siderlink agent. func (p *Provisioner) CreateSiderolinkAgent(state *provision.State, clusterReq provision.ClusterRequest) error { pidPath := state.GetRelativePath(siderolinkAgentPid) logFile, err := os.OpenFile(state.GetRelativePath(siderolinkAgentLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck args := []string{ "siderolink-launch", "--sidero-link-join-token", "foo", "--sidero-link-wireguard-endpoint", clusterReq.SiderolinkRequest.WireguardEndpoint, "--event-sink-endpoint", clusterReq.SiderolinkRequest.SinkEndpoint, "--sidero-link-api-endpoint", clusterReq.SiderolinkRequest.APIEndpoint, "--log-receiver-endpoint", clusterReq.SiderolinkRequest.LogEndpoint, } if clusterReq.SiderolinkRequest.APICertificate != nil && clusterReq.SiderolinkRequest.APIKey != nil { apiCertPath := state.GetRelativePath(siderolinkCert) apiKeyPath := state.GetRelativePath(siderolinkKey) if err = os.WriteFile(apiCertPath, clusterReq.SiderolinkRequest.APICertificate, 0o600); err != nil { return fmt.Errorf("error writing SideroLink API certificate: %w", err) } if err = os.WriteFile(apiKeyPath, clusterReq.SiderolinkRequest.APIKey, 0o600); err != nil { return fmt.Errorf("error writing SideroLink API key: %w", err) } args = append(args, "--sidero-link-api-cert", apiCertPath, "--sidero-link-api-key", apiKeyPath) } for _, bind := range clusterReq.SiderolinkRequest.SiderolinkBind { args = append(args, "--predefined-pair", bind.UUID.String()+"="+bind.Addr.String()) } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing SA PID file: %w", err) } return nil } // DestroySiderolinkAgent destroys siderolink agent. func (p *Provisioner) DestroySiderolinkAgent(state *provision.State) error { pidPath := state.GetRelativePath(siderolinkAgentPid) if _, err := os.Stat(pidPath); errors.Is(err, os.ErrNotExist) { // If the pid file does not exist, the process was not started. return nil } return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //nolint:revive package vm import "github.com/siderolabs/talos/pkg/provision" // Deprecated: Use provision.State instead. type State = provision.State // Deprecated: Use provision.NewState instead. var NewState = provision.NewState ================================================ FILE: pkg/provision/providers/vm/tftpd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "net" "time" "github.com/pin/tftp/v3" "golang.org/x/sync/errgroup" "github.com/siderolabs/talos/pkg/provision/providers/vm/internal/ipxe" ) // TFTPd starts a TFTP server on the given IPs. func TFTPd(ips []net.IP, nextHandler string) error { server := tftp.NewServer(ipxe.TFTPHandler(nextHandler), nil) server.SetTimeout(5 * time.Second) var eg errgroup.Group for _, ip := range ips { eg.Go(func() error { return server.ListenAndServe(net.JoinHostPort(ip.String(), "69")) }) } return eg.Wait() } ================================================ FILE: pkg/provision/providers/vm/virtiofsd.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "bufio" "context" "errors" "fmt" "io" "os" "os/exec" "strings" "github.com/siderolabs/talos/pkg/provision" ) const ( virtiofsdPid = "virtiofsd.pid" virtiofsdLog = "virtiofsd.log" ) // FindVirtiofsd tries to find the virtiofsd binary in common locations. func (p *Provisioner) FindVirtiofsd() (string, error) { return p.findVirtiofsd() } // Virtiofsd starts virtiofsd and restarts it if it exits. // The restart is needed, because the virtiofsd exits when client disconnects. func Virtiofsd(ctx context.Context, virtiofsdBin, share, socket string) error { if virtiofsdBin == "" { return errors.New("virtiofsd binary path is empty") } if err := os.MkdirAll(share, 0o755); err != nil { fmt.Fprintf(os.Stderr, "failed to create shared dir %s: %v\n", share, err) } args := []string{ "--shared-dir", share, "--socket-path", socket, "--announce-submounts", "--inode-file-handles=mandatory", } fmt.Printf("Starting virtiofsd with restart loop: %s %s\n", virtiofsdBin, strings.Join(args, " ")) for { err := runVirtiofsd(ctx, share, virtiofsdBin, args) switch { case err == nil: fmt.Printf("virtiofsd exited - restarting...\n") case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded): return nil default: fmt.Printf("virtiofsd exited: %v - restarting...\n", err) } } } func runVirtiofsd(ctx context.Context, share, virtiofsdBin string, args []string) error { if ctx.Err() != nil { return ctx.Err() } cmd := exec.CommandContext(ctx, virtiofsdBin, args...) stdoutPipe, err := cmd.StdoutPipe() if err != nil { return err } defer stdoutPipe.Close() //nolint:errcheck stderrPipe, err := cmd.StderrPipe() if err != nil { return err } defer stderrPipe.Close() //nolint:errcheck if err := cmd.Start(); err != nil { return err } fmt.Printf("virtiofsd started with PID %d\n", cmd.Process.Pid) go printPipe(share, os.Stdout, stdoutPipe) go printPipe(share, os.Stderr, stderrPipe) err = cmd.Wait() if ctx.Err() != nil { return ctx.Err() } return err } func printPipe(prefix string, wr io.Writer, r io.Reader) { if len(prefix) > 32 { // print only last 32 characters prefix = prefix[len(prefix)-32:] } scanner := bufio.NewScanner(r) for scanner.Scan() { fmt.Fprintf(wr, "[%s] %s\n", prefix, scanner.Text()) } if err := scanner.Err(); err != nil { fmt.Fprintf(wr, "error reading from pipe: %v\n", err) } } // CreateVirtiofsd creates the Virtiofsd server. func (p *Provisioner) CreateVirtiofsd(state *State, clusterReq provision.ClusterRequest, virtiofdPath string) error { return p.startVirtiofsd(state, clusterReq, virtiofdPath) } // DestroyVirtiofsd destoys Virtiofsd server. func (p *Provisioner) DestroyVirtiofsd(state *State) error { return p.stopVirtiofsd(state) } ================================================ FILE: pkg/provision/providers/vm/virtiofsd_darwin.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "github.com/siderolabs/talos/pkg/provision" ) func (p *Provisioner) findVirtiofsd() (string, error) { return "", nil } func (p *Provisioner) startVirtiofsd(_ *State, _ provision.ClusterRequest, _ string) error { return nil } func (p *Provisioner) stopVirtiofsd(_ *State) error { return nil } ================================================ FILE: pkg/provision/providers/vm/virtiofsd_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package vm import ( "fmt" "os" "os/exec" "strconv" "strings" "syscall" "github.com/siderolabs/talos/pkg/provision" ) func (p *Provisioner) findVirtiofsd() (string, error) { virtiofsdPaths := []string{ "virtiofsd", "/usr/libexec/virtiofsd", } for _, p := range virtiofsdPaths { if full, err := exec.LookPath(p); err == nil { return full, nil } } return "", fmt.Errorf("virtiofsd not found in paths: %v", virtiofsdPaths) } func (p *Provisioner) startVirtiofsd(state *State, clusterReq provision.ClusterRequest, virtiofdPath string) error { pidPath := state.GetRelativePath(virtiofsdPid) logFile, err := os.OpenFile(state.GetRelativePath(virtiofsdLog), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o666) if err != nil { return err } defer logFile.Close() //nolint:errcheck virtiofs := []string{} for _, nodeReq := range clusterReq.Nodes { for i, disk := range nodeReq.Disks { if disk.Driver != "virtiofs" { continue } virtiofs = append(virtiofs, fmt.Sprintf("%s:%s", state.GetRelativePath(fmt.Sprintf("%s-%d.virtiofs.d", nodeReq.Name, i)), state.GetRelativePath(fmt.Sprintf("%s-%d.virtiofs.sock", nodeReq.Name, i)), ), ) } } args := []string{ "virtiofsd-launch", "--bin", virtiofdPath, "--virtiofs", strings.Join(virtiofs, ","), } cmd := exec.Command(clusterReq.SelfExecutable, args...) //nolint:noctx // runs in background cmd.Stdout = logFile cmd.Stderr = logFile cmd.SysProcAttr = &syscall.SysProcAttr{ Setsid: true, // daemonize } if err = cmd.Start(); err != nil { return err } if err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm); err != nil { return fmt.Errorf("error writing virtiofsd PID file: %w", err) } return nil } func (p *Provisioner) stopVirtiofsd(state *State) error { pidPath := state.GetRelativePath(virtiofsdPid) return StopProcessByPidfile(pidPath) } ================================================ FILE: pkg/provision/providers/vm/vm.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package vm implements common methods for VM provisioners. package vm // Provisioner base for VM provisioners. type Provisioner struct { // Name actual provisioner type. Name string } ================================================ FILE: pkg/provision/provision.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package provision provides abstract definitions for Talos cluster provisioners. package provision import ( "context" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/bundle" "github.com/siderolabs/talos/pkg/machinery/config/generate" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" ) // Provisioner is an interface each provisioner should implement. // //nolint:interfacebloat type Provisioner interface { Create(context.Context, ClusterRequest, ...Option) (Cluster, error) Destroy(context.Context, Cluster, ...Option) error Reflect(ctx context.Context, clusterName, stateDirectory string) (Cluster, error) GenOptions(NetworkRequest, *config.VersionContract) ([]generate.Option, []bundle.Option) GetInClusterKubernetesControlPlaneEndpoint(req NetworkRequest, controlPlanePort int) string GetExternalKubernetesControlPlaneEndpoint(req NetworkRequest, controlPlanePort int) string GetTalosAPIEndpoints(NetworkRequest) []string GetFirstInterface() v1alpha1.IfaceSelector GetFirstInterfaceName() string Close() error UserDiskName(index int) string } ================================================ FILE: pkg/provision/provision_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provision_test import "testing" func TestEmpty(t *testing.T) { // added for accurate coverage estimation // // please remove it once any unit-test is added // for this package } ================================================ FILE: pkg/provision/request.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provision import ( "errors" "net/netip" "slices" "time" "github.com/google/uuid" mounttypes "github.com/moby/moby/api/types/mount" "github.com/siderolabs/go-procfs/procfs" "github.com/siderolabs/talos/pkg/machinery/config" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/imager/quirks" ) // ClusterRequest is the root object describing cluster to be provisioned. type ClusterRequest struct { Name string Network NetworkRequest Nodes NodeRequests // Docker specific parameters. Image string // Boot options (QEMU). KernelPath string InitramfsPath string ISOPath string USBPath string UKIPath string DiskImagePath string IPXEBootScript string // Encryption KMSEndpoint string // Path to talosctl executable to re-execute itself as needed. SelfExecutable string // Path to root of state directory (~/.talos/clusters by default). StateDirectory string SiderolinkRequest SiderolinkRequest } // CNIConfig describes CNI part of NetworkRequest. type CNIConfig struct { BinPath []string ConfDir string CacheDir string BundleURL string } // NetworkRequest describes cluster network. type NetworkRequest struct { Name string CIDRs []netip.Prefix NoMasqueradeCIDRs []netip.Prefix GatewayAddrs []netip.Addr MTU int Nameservers []netip.Addr LoadBalancerPorts []int // CNI-specific parameters. CNI CNIConfig // DHCP options DHCPSkipHostname bool // Docker-specific parameters. DockerDisableIPv6 bool // Network chaos parameters. NetworkChaos bool Jitter time.Duration Latency time.Duration PacketLoss float64 PacketReorder float64 PacketCorrupt float64 Bandwidth int // Airgapped limits VM network access to the provisioning network only (qemu/linux). Airgapped bool ImageCachePath string ImageCacheTLSCertFile string ImageCacheTLSKeyFile string ImageCachePort uint16 } // NodeRequests is a list of NodeRequest. type NodeRequests []NodeRequest // FindInitNode looks up init node, it returns an error if no init node is present or if it's duplicate. func (reqs NodeRequests) FindInitNode() (req NodeRequest, err error) { found := false for i := range reqs { if reqs[i].Config == nil { continue } if reqs[i].Config.Machine().Type() == machine.TypeInit { if found { err = errors.New("duplicate init node in requests") return req, err } req = reqs[i] found = true } } if !found { err = errors.New("no init node found in requests") } return req, err } // ControlPlaneNodes returns subset of nodes which are Init/ControlPlane type. func (reqs NodeRequests) ControlPlaneNodes() (nodes []NodeRequest) { for i := range reqs { if reqs[i].Type == machine.TypeInit || reqs[i].Type == machine.TypeControlPlane { nodes = append(nodes, reqs[i]) } } return nodes } // WorkerNodes returns subset of nodes which are Init/ControlPlane type. func (reqs NodeRequests) WorkerNodes() (nodes []NodeRequest) { for i := range reqs { if reqs[i].Type == machine.TypeWorker { nodes = append(nodes, reqs[i]) } } return nodes } // PXENodes returns subset of nodes which are PXE booted. func (reqs NodeRequests) PXENodes() (nodes []NodeRequest) { for i := range reqs { if reqs[i].PXEBooted { nodes = append(nodes, reqs[i]) } } return nodes } // Disk represents a disk size and name in NodeRequest. type Disk struct { // Size in bytes. Size uint64 // Whether to skip preallocating the disk space. SkipPreallocate bool // Driver for the disk. // // Supported types: "virtio", "ide", "ahci", "scsi", "nvme", "megaraid", "virtiofs" (special). Driver string // Block size for the disk, defaults to 512 if not set. BlockSize uint // Serial number for the disk. Serial string // Tag for the disk, only used for Virtiofs disks. Tag string } // ConfigInjectionMethod describes how to inject configuration into the node. type ConfigInjectionMethod int const ( // ConfigInjectionMethodHTTP injects configuration via HTTP. ConfigInjectionMethodHTTP ConfigInjectionMethod = iota // ConfigInjectionMethodMetalISO injects configuration via Metal ISO. ConfigInjectionMethodMetalISO ) // NodeRequest describes a request for a node. type NodeRequest struct { Name string IPs []netip.Addr Type machine.Type Quirks quirks.Quirks Config config.Provider ConfigInjectionMethod ConfigInjectionMethod // Share of CPUs, in 1e-9 fractions NanoCPUs int64 // Memory limit in bytes Memory int64 // Disks (volumes), if applicable (VM only) Disks []*Disk // Mounts (containers only) Mounts []mounttypes.Mount // Ports Ports []string // SkipInjectingConfig disables reading configuration from http server SkipInjectingConfig bool // DefaultBootOrder overrides default boot order "cn" (disk, then network boot). // // BootOrder can be forced to be "nc" (PXE boot) via the API in QEMU provisioner. DefaultBootOrder string // ExtraKernelArgs passes additional kernel args // to the initial boot from initramfs and vmlinuz. // // This doesn't apply to boots from ISO or from the disk image. ExtraKernelArgs *procfs.Cmdline // SDStubKernelArgs passes additional kernel args via the systemd-stub. // // This applies to boots from ISO and from the disk image. SDStubKernelArgs *procfs.Cmdline // UUID allows to specify the UUID of the node (VMs only). // // If not specified, a random UUID will be generated. UUID *uuid.UUID // Testing features // BadRTC resets RTC to well known time in the past (QEMU provisioner). BadRTC bool // PXE-booted VMs PXEBooted bool TFTPServer string IPXEBootFilename string } // SiderolinkRequest describes a request for SideroLink agent. type SiderolinkRequest struct { WireguardEndpoint string APIEndpoint string APICertificate []byte APIKey []byte SinkEndpoint string LogEndpoint string SiderolinkBind []SiderolinkBind } // GetAddr returns the address for the given UUID. func (sr *SiderolinkRequest) GetAddr(u *uuid.UUID) (netip.Addr, bool) { if idx := slices.IndexFunc(sr.SiderolinkBind, func(sb SiderolinkBind) bool { return sb.UUID == *u }); idx != -1 { return sr.SiderolinkBind[idx].Addr, true } return netip.Addr{}, false } // SiderolinkBind describes a pair of prebinded UUID->Addr for SideroLink agent. type SiderolinkBind struct { UUID uuid.UUID Addr netip.Addr } ================================================ FILE: pkg/provision/result.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provision import ( "net/netip" "github.com/google/uuid" "github.com/siderolabs/talos/pkg/machinery/config/machine" ) // Cluster describes the provisioned Cluster. type Cluster interface { // Provisioner returns name of the provisioner used to build the cluster. Provisioner() string // StatePath returns path to the state directory of the cluster. StatePath() (string, error) // Info returns running cluster information. Info() ClusterInfo } // ClusterInfo describes the cluster. type ClusterInfo struct { ClusterName string Network NetworkInfo Nodes []NodeInfo // ExtraNodes are not part of the cluster. ExtraNodes []NodeInfo // KubernetesEndpoint is the endpoint of the Kubernetes API server. KubernetesEndpoint string } // NetworkInfo describes cluster network. type NetworkInfo struct { Name string CIDRs []netip.Prefix GatewayAddrs []netip.Addr MTU int NoMasqueradeCIDRs []netip.Prefix } // NodeInfo describes a node. type NodeInfo struct { ID string UUID uuid.UUID Name string Type machine.Type // Share of CPUs, in 1e-9 fractions NanoCPUs int64 // Memory limit in bytes Memory int64 // Disk (volume) size in bytes, if applicable DiskSize uint64 IPs []netip.Addr APIPort int TPMStateDir string } ================================================ FILE: pkg/provision/state.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package provision import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/containernetworking/cni/libcni" yaml "go.yaml.in/yaml/v4" ) // StateFileName is the name of the yaml state file. const StateFileName = "state.yaml" // State common state representation for vm provisioners. type State struct { ProvisionerName string BridgeName string ClusterInfo ClusterInfo VMCNIConfig *libcni.NetworkConfigList statePath string } // NewState create new vm provisioner state. func NewState(statePath, provisionerName, clusterName string) (*State, error) { s := &State{ ProvisionerName: provisionerName, statePath: statePath, } _, err := os.Stat(s.statePath) if err == nil { return nil, fmt.Errorf( "state directory %q already exists, is the cluster %q already running? remove cluster state with talosctl cluster destroy", s.statePath, clusterName, ) } if !errors.Is(err, fs.ErrNotExist) { return nil, fmt.Errorf("error checking state directory: %w", err) } if err = os.MkdirAll(s.statePath, os.ModePerm); err != nil { return nil, fmt.Errorf("error creating state directory: %w", err) } return s, nil } // ReadState reads and parses the state saved to a file. func ReadState(ctx context.Context, clusterName, stateDirectory string) (*State, error) { statePath := filepath.Join(stateDirectory, clusterName) st, err := os.Stat(statePath) if err != nil { if errors.Is(err, fs.ErrNotExist) { return nil, fmt.Errorf("cluster %q not found: %w", clusterName, err) } return nil, err } if !st.IsDir() { return nil, fmt.Errorf("state path %q is not a directory: %s", statePath, st.Mode()) } stateFile, err := os.Open(filepath.Join(statePath, StateFileName)) if err != nil { return nil, err } defer stateFile.Close() //nolint:errcheck state := &State{} if err = yaml.NewDecoder(stateFile).Decode(state); err != nil { return nil, fmt.Errorf("error unmarshalling state file: %w", err) } state.statePath = statePath return state, nil } // Provisioner get provisioner name. func (s *State) Provisioner() string { return s.ProvisionerName } // Info get cluster info. func (s *State) Info() ClusterInfo { return s.ClusterInfo } // StatePath get state config file path. func (s *State) StatePath() (string, error) { if s.statePath == "" { return "", errors.New("state path is not set") } return s.statePath, nil } // Save save state to config file. func (s *State) Save() error { // save state stateFile, err := os.Create(filepath.Join(s.statePath, StateFileName)) if err != nil { return err } defer stateFile.Close() //nolint:errcheck if err = yaml.NewEncoder(stateFile).Encode(&s); err != nil { return fmt.Errorf("error marshaling state: %w", err) } return stateFile.Close() } // GetRelativePath get file path relative to config folder. func (s *State) GetRelativePath(path string) string { return filepath.Join(s.statePath, path) } // GetShmPath get shm path. func (s *State) GetShmPath(path string) string { if s.isDevShmAvailable() { return filepath.Join("/dev/shm", path) } return filepath.Join("/tmp", path) } ================================================ FILE: pkg/provision/state_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package provision import ( "os" "golang.org/x/sys/unix" ) const defaultContainerShmSize = 64 * 1024 * 1024 // 64MiB func (s *State) isDevShmAvailable() bool { // check if /dev/shm exists if _, err := os.Stat("/dev/shm"); err != nil { return false } // get /dev/shm stats var stat unix.Statfs_t if err := unix.Statfs("/dev/shm", &stat); err != nil { return false } // check if is tmpfs if stat.Type != unix.TMPFS_MAGIC { return false } // check if /dev/shm has potentially enough space if stat.Blocks*uint64(stat.Bsize) <= defaultContainerShmSize { return false } return true } ================================================ FILE: pkg/provision/state_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux package provision func (s *State) isDevShmAvailable() bool { return false } ================================================ FILE: pkg/reporter/reporter.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package reporter implements the console reporter. package reporter import ( "fmt" "os" "strings" "unicode/utf8" "github.com/fatih/color" "github.com/mattn/go-isatty" "golang.org/x/term" "github.com/siderolabs/talos/pkg/flags" ) // Status represents the status of an Update. type Status int const ( // StatusError represents an error status. StatusError Status = iota // StatusRunning represents a running status. StatusRunning // StatusSucceeded represents a success status. StatusSucceeded // StatusSkip represents a skipped status. StatusSkip ) var spinner = []string{"◰", "◳", "◲", "◱"} // Update represents an update to be reported. type Update struct { Message string Status Status } // Reporter is a console reporter with stderr output. type Reporter struct { w *os.File lastLine string lastLineTemporary bool colorized bool spinnerIdx int } // OutputMode represents the output mode of the reporter. type OutputMode int32 // String implements the fmt.Stringer interface for OutputMode. func (o OutputMode) String() string { return outputModeNames[int32(o)] } var outputModeValues = map[string]int32{ "AUTO": int32(OutputModeAuto), "PLAIN": int32(OutputModePlain), } var outputModeNames = map[int32]string{ int32(OutputModeAuto): "AUTO", int32(OutputModePlain): "PLAIN", } const ( // OutputModeAuto represents the automatic output mode, which detects if the output is a terminal. OutputModeAuto OutputMode = iota // OutputModePlain represents the plain output mode, which disables colorization and spinner. OutputModePlain ) // NewOutputModeFlag returns a pflag.Value for the output mode. func NewOutputModeFlag() flags.PflagExtended[OutputMode] { return flags.ProtoEnum( OutputModeAuto, outputModeValues, outputModeNames, ) } // Option represents an option for configuring the Reporter. type Option func(*Reporter) // WithOutputMode returns an Option that sets the output mode of the Reporter. func WithOutputMode(mode OutputMode) Option { return func(r *Reporter) { switch mode { case OutputModePlain: r.colorized = false case OutputModeAuto: r.colorized = isatty.IsTerminal(r.w.Fd()) } } } // New returns a console reporter with stderr output. func New(opts ...Option) *Reporter { rep := &Reporter{ w: os.Stderr, colorized: isatty.IsTerminal(os.Stderr.Fd()), } for _, opt := range opts { opt(rep) } return rep } // IsColorized returns true if the reporter is colorized. func (r *Reporter) IsColorized() bool { return r.colorized } // Report reports an update to the reporter. // //nolint:gocyclo func (r *Reporter) Report(update Update) { line := strings.TrimSpace(update.Message) // replace tabs with spaces to get consistent output length line = strings.ReplaceAll(line, "\t", " ") if !r.colorized { if line != r.lastLine { fmt.Fprintln(r.w, line) r.lastLine = line } return } w, _, _ := term.GetSize(int(r.w.Fd())) //nolint:errcheck if w <= 0 { w = 80 } var coloredLine string showSpinner := false prevLineTemporary := r.lastLineTemporary switch update.Status { case StatusRunning: line = fmt.Sprintf("%s %s", spinner[r.spinnerIdx], line) coloredLine = color.YellowString("%s", line) r.lastLineTemporary = true showSpinner = true case StatusSucceeded: coloredLine = color.GreenString("%s", line) r.lastLineTemporary = false case StatusSkip: coloredLine = color.BlueString("%s", line) r.lastLineTemporary = false case StatusError: fallthrough default: line = fmt.Sprintf("%s %s", spinner[r.spinnerIdx], line) coloredLine = color.RedString("%s", line) r.lastLineTemporary = true showSpinner = true } if line == r.lastLine { return } if showSpinner { r.spinnerIdx = (r.spinnerIdx + 1) % len(spinner) } if prevLineTemporary { for outputLine := range strings.SplitSeq(r.lastLine, "\n") { for range (utf8.RuneCountInString(outputLine) + w - 1) / w { fmt.Fprint(r.w, "\033[A\033[K") // cursor up, clear line } } } fmt.Fprintln(r.w, coloredLine) r.lastLine = line } ================================================ FILE: pkg/rotate/pki/internal/helpers/helpers.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package helpers provides helper functions for the rotate/pki package. package helpers import ( "context" "errors" "fmt" "time" "github.com/cosi-project/runtime/pkg/resource" "github.com/cosi-project/runtime/pkg/safe" "github.com/cosi-project/runtime/pkg/state" "github.com/siderolabs/gen/xslices" "github.com/siderolabs/go-retry/retry" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/cluster" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" configres "github.com/siderolabs/talos/pkg/machinery/resources/config" v1alpha1res "github.com/siderolabs/talos/pkg/machinery/resources/v1alpha1" ) // MapToInternalIP maps a slice of NodeInfo to a slice of internal IPs. func MapToInternalIP(in []cluster.NodeInfo) []string { return xslices.Map(in, func(i cluster.NodeInfo) string { return i.InternalIP.String() }) } // PatchNodeConfig patches the node config for the given node. func PatchNodeConfig(ctx context.Context, c *client.Client, node string, encoderOpt encoder.Option, patchFunc func(config *v1alpha1.Config) error) error { return retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond), retry.WithErrorLogging(true)).RetryWithContext( ctx, func(ctx context.Context) error { err := patchNodeConfigInternal(ctx, c, node, encoderOpt, patchFunc) if err != nil { if client.StatusCode(err) == codes.Unavailable || client.StatusCode(err) == codes.Canceled { return retry.ExpectedError(err) } } return err }, ) } // PatchNodeConfigWithKubeletRestart patches the node config for the given node waiting for the kubelet to be restarted. // //nolint:gocyclo,cyclop func PatchNodeConfigWithKubeletRestart(ctx context.Context, c *client.Client, node string, encoderOpt encoder.Option, patchFunc func(config *v1alpha1.Config) error) error { ctx, cancel := context.WithCancel(ctx) defer cancel() ctx = client.WithNode(ctx, node) watchCh := make(chan safe.WrappedStateEvent[*v1alpha1res.Service]) if err := safe.StateWatch(ctx, c.COSI, resource.NewMetadata(v1alpha1res.NamespaceName, v1alpha1res.ServiceType, "kubelet", resource.VersionUndefined), watchCh); err != nil { return fmt.Errorf("error watching service: %w", err) } var ev safe.WrappedStateEvent[*v1alpha1res.Service] select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() != state.Created { return fmt.Errorf("unexpected event type: %s", ev.Type()) } initialService, err := ev.Resource() if err != nil { return fmt.Errorf("error inspecting service: %w", err) } if !initialService.TypedSpec().Running || !initialService.TypedSpec().Healthy { return errors.New("kubelet is not healthy") } if err = PatchNodeConfig(ctx, c, node, encoderOpt, patchFunc); err != nil { return fmt.Errorf("error patching node config: %w", err) } // first, wait for kubelet to go down for { select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() == state.Destroyed { break } } // now wait for kubelet to go up & healthy for { select { case ev = <-watchCh: case <-ctx.Done(): return ctx.Err() } if ev.Type() == state.Created || ev.Type() == state.Updated { var service *v1alpha1res.Service service, err = ev.Resource() if err != nil { return fmt.Errorf("error inspecting service: %w", err) } if service.TypedSpec().Running && service.TypedSpec().Healthy { break } } } return nil } func patchNodeConfigInternal(ctx context.Context, c *client.Client, node string, encoderOpt encoder.Option, patchFunc func(config *v1alpha1.Config) error) error { ctx = client.WithNode(ctx, node) mc, err := safe.StateGetByID[*configres.MachineConfig](ctx, c.COSI, configres.ActiveID) if err != nil { return fmt.Errorf("error fetching config resource: %w", err) } provider := mc.Provider() newProvider, err := provider.PatchV1Alpha1(patchFunc) if err != nil { return fmt.Errorf("error patching config: %w", err) } cfgBytes, err := newProvider.EncodeBytes(encoderOpt) if err != nil { return fmt.Errorf("error serializing config: %w", err) } _, err = c.ApplyConfiguration(ctx, &machineapi.ApplyConfigurationRequest{ Data: cfgBytes, Mode: machineapi.ApplyConfigurationRequest_NO_REBOOT, }) if err != nil { return fmt.Errorf("error applying config: %w", err) } return nil } ================================================ FILE: pkg/rotate/pki/kubernetes/kubernetes.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package kubernetes implements safe Talos API PKI rotation for the cluster. package kubernetes import ( "bufio" "bytes" "context" "fmt" "slices" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-retry/retry" "go.yaml.in/yaml/v4" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/siderolabs/talos/pkg/cluster" taloskubernetes "github.com/siderolabs/talos/pkg/kubernetes" "github.com/siderolabs/talos/pkg/machinery/client" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" secretsres "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/rotate/pki/internal/helpers" ) // Options is the input to the Kubernetes API rotation process. type Options struct { // DryRun is the flag to enable dry-run mode. // // In dry-run mode, the rotation process will not make any changes to the cluster. DryRun bool // TalosClient is a Talos API client TalosClient *client.Client // ClusterInfo provides information about cluster topology. ClusterInfo cluster.Info // KubernetesEndpoint overrides the default Kubernetes API endpoint. KubernetesEndpoint string // NewKubernetesCA is the new CA for Kubernetes API. NewKubernetesCA *x509.PEMEncodedCertificateAndKey // EncoderOption is the option for encoding machine configuration (while patching). EncoderOption encoder.Option // Printf is the function used to print messages. Printf func(format string, args ...any) } type rotator struct { opts Options currentCA []byte talosClientProvider *cluster.ConfigClientProvider currentKubernetes *cluster.KubernetesClient newKubernetes *cluster.KubernetesClient } // Rotate rotates the Kubernetes API PKI. // // The process overview: // - fetch current information // - verify connectivity with the existing PKI // - add new Kubernetes CA as accepted // - verify connectivity // - make new CA issuing, old CA is still accepted // - verify connectivity with the new PKI // - remove old CA // - verify connectivity with the new PKI. func Rotate(ctx context.Context, opts Options) error { r := rotator{ opts: opts, } defer func() { if r.currentKubernetes != nil { r.currentKubernetes.K8sClose() //nolint:errcheck } if r.newKubernetes != nil { r.newKubernetes.K8sClose() //nolint:errcheck } }() r.talosClientProvider = &cluster.ConfigClientProvider{ DefaultClient: opts.TalosClient, } return r.rotate(ctx) } //nolint:gocyclo func (r *rotator) rotate(ctx context.Context) error { r.printIntro() if err := r.fetchClient(ctx, &r.currentKubernetes, "current"); err != nil { return err } if err := r.fetchCurrentCA(ctx); err != nil { return err } if err := r.printNewCA(); err != nil { return err } if err := r.verifyConnectivity(ctx, r.currentKubernetes, "existing PKI"); err != nil { return err } if err := r.addNewCAAccepted(ctx); err != nil { return err } if err := r.swapCAs(ctx); err != nil { return err } if err := r.fetchClient(ctx, &r.newKubernetes, "new"); err != nil { return err } if err := r.verifyConnectivity(ctx, r.newKubernetes, "new PKI"); err != nil { return err } if err := r.dropOldCA(ctx); err != nil { return err } if err := r.verifyConnectivity(ctx, r.newKubernetes, "new PKI"); err != nil { return err } return nil } func (r *rotator) printIntro() { r.opts.Printf("> Starting Kubernetes API PKI rotation, dry-run mode %v...\n", r.opts.DryRun) r.opts.Printf("> Cluster topology:\n") r.opts.Printf(" - control plane nodes: %q\n", append( helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeInit)), helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeControlPlane))..., ), ) r.opts.Printf(" - worker nodes: %q\n", helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeWorker)), ) } func (r *rotator) fetchClient(ctx context.Context, clientPtr **cluster.KubernetesClient, label string) error { r.opts.Printf("> Building %s Kubernetes client...\n", label) firstNode := append( r.opts.ClusterInfo.NodesByType(machine.TypeInit), r.opts.ClusterInfo.NodesByType(machine.TypeControlPlane)..., )[0] *clientPtr = &cluster.KubernetesClient{ ClientProvider: r.talosClientProvider, ForceEndpoint: r.opts.KubernetesEndpoint, } _, err := (*clientPtr).K8sClient(client.WithNode(ctx, firstNode.InternalIP.String())) if err != nil { return fmt.Errorf("error fetching kubeconfig: %w", err) } return nil } func (r *rotator) fetchCurrentCA(ctx context.Context) error { r.opts.Printf("> Current Kubernetes CA:\n") firstNode := append( r.opts.ClusterInfo.NodesByType(machine.TypeInit), r.opts.ClusterInfo.NodesByType(machine.TypeControlPlane)..., )[0] k8sRoot, err := safe.StateGetByID[*secretsres.KubernetesRoot](client.WithNode(ctx, firstNode.InternalIP.String()), r.opts.TalosClient.COSI, secretsres.KubernetesRootID) if err != nil { return fmt.Errorf("error fetching current Kubernetes CA: %w", err) } r.currentCA = k8sRoot.TypedSpec().IssuingCA.Crt var b bytes.Buffer if err = yaml.NewEncoder(&b).Encode(k8sRoot.TypedSpec().IssuingCA); err != nil { return fmt.Errorf("error encoding current Kubernetes CA: %w", err) } for scanner := bufio.NewScanner(&b); scanner.Scan(); { r.opts.Printf(" %s\n", scanner.Text()) } return nil } func (r *rotator) printNewCA() error { r.opts.Printf("> New Kubernetes CA:\n") var b bytes.Buffer if err := yaml.NewEncoder(&b).Encode(r.opts.NewKubernetesCA); err != nil { return fmt.Errorf("error encoding new Talos CA: %w", err) } for scanner := bufio.NewScanner(&b); scanner.Scan(); { r.opts.Printf(" %s\n", scanner.Text()) } return nil } func (r *rotator) verifyConnectivity(ctx context.Context, client *cluster.KubernetesClient, label string) error { r.opts.Printf("> Verifying connectivity with %s...\n", label) if r.opts.DryRun { r.opts.Printf(" - OK (dry-run mode)\n") return nil } clientset, err := client.K8sClient(ctx) if err != nil { return fmt.Errorf("error building Kubernetes client: %w", err) } return retry.Constant(3*time.Minute, retry.WithUnits(time.Second), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error { nodes, err := clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { if taloskubernetes.IsRetryableError(err) { return retry.ExpectedError(err) } return err } var notReadyNodes []string for _, node := range nodes.Items { for _, cond := range node.Status.Conditions { if cond.Type == v1.NodeReady { if cond.Status != v1.ConditionTrue { notReadyNodes = append(notReadyNodes, node.Name) break } } } } if len(notReadyNodes) > 0 { return retry.ExpectedErrorf("nodes not ready: %q", notReadyNodes) } r.opts.Printf(" - OK (%d nodes ready)\n", len(nodes.Items)) return nil }) } func (r *rotator) addNewCAAccepted(ctx context.Context) error { r.opts.Printf("> Adding new Kubernetes CA as accepted...\n") if err := r.patchAllNodes(ctx, func(_ machine.Type, config *v1alpha1.Config) error { config.ClusterConfig.ClusterAcceptedCAs = append( config.ClusterConfig.ClusterAcceptedCAs, &x509.PEMEncodedCertificate{ Crt: r.opts.NewKubernetesCA.Crt, }, ) return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) swapCAs(ctx context.Context) error { r.opts.Printf("> Making new Kubernetes CA the issuing CA, old Kubernetes CA the accepted CA...\n") if err := r.patchAllNodes(ctx, func(machineType machine.Type, config *v1alpha1.Config) error { config.ClusterConfig.ClusterAcceptedCAs = append( config.ClusterConfig.ClusterAcceptedCAs, &x509.PEMEncodedCertificate{ Crt: r.currentCA, }, ) config.ClusterConfig.ClusterAcceptedCAs = slices.DeleteFunc(config.Cluster().AcceptedCAs(), func(ca *x509.PEMEncodedCertificate) bool { return bytes.Equal(ca.Crt, r.opts.NewKubernetesCA.Crt) }) if machineType.IsControlPlane() { config.ClusterConfig.ClusterCA = r.opts.NewKubernetesCA } else { config.ClusterConfig.ClusterCA = &x509.PEMEncodedCertificateAndKey{ Crt: r.opts.NewKubernetesCA.Crt, } } return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) dropOldCA(ctx context.Context) error { r.opts.Printf("> Removing old Kubernetes CA from the accepted CAs...\n") if err := r.patchAllNodes(ctx, func(_ machine.Type, config *v1alpha1.Config) error { config.ClusterConfig.ClusterAcceptedCAs = slices.DeleteFunc(config.Cluster().AcceptedCAs(), func(ca *x509.PEMEncodedCertificate) bool { return bytes.Equal(ca.Crt, r.currentCA) }) return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) patchAllNodes(ctx context.Context, patchFunc func(machineType machine.Type, config *v1alpha1.Config) error) error { for _, machineType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { for _, node := range r.opts.ClusterInfo.NodesByType(machineType) { if r.opts.DryRun { r.opts.Printf(" - %s: skipped (dry-run)\n", node.InternalIP) continue } if err := helpers.PatchNodeConfigWithKubeletRestart(ctx, r.opts.TalosClient, node.InternalIP.String(), r.opts.EncoderOption, func(config *v1alpha1.Config) error { return patchFunc(machineType, config) }); err != nil { return fmt.Errorf("error patching node %s: %w", node.InternalIP, err) } r.opts.Printf(" - %s: OK\n", node.InternalIP) } } return nil } ================================================ FILE: pkg/rotate/pki/talos/talos.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package talos implements safe Talos API PKI rotation for the cluster. package talos import ( "bufio" "bytes" "context" "fmt" "slices" "time" "github.com/cosi-project/runtime/pkg/safe" "github.com/siderolabs/crypto/x509" "github.com/siderolabs/go-retry/retry" "go.yaml.in/yaml/v4" "google.golang.org/grpc/codes" "github.com/siderolabs/talos/pkg/cluster" machineapi "github.com/siderolabs/talos/pkg/machinery/api/machine" "github.com/siderolabs/talos/pkg/machinery/client" clientconfig "github.com/siderolabs/talos/pkg/machinery/client/config" "github.com/siderolabs/talos/pkg/machinery/config/encoder" "github.com/siderolabs/talos/pkg/machinery/config/generate/secrets" "github.com/siderolabs/talos/pkg/machinery/config/machine" "github.com/siderolabs/talos/pkg/machinery/config/types/v1alpha1" secretsres "github.com/siderolabs/talos/pkg/machinery/resources/secrets" "github.com/siderolabs/talos/pkg/machinery/role" "github.com/siderolabs/talos/pkg/rotate/pki/internal/helpers" ) // Options is the input to the Talos API rotation process. type Options struct { // DryRun is the flag to enable dry-run mode. // // In dry-run mode, the rotation process will not make any changes to the cluster. DryRun bool // CurrentClient is a Talos client for the existing PKI. CurrentClient *client.Client // ClusterInfo provides information about cluster topology. ClusterInfo cluster.Info // ContextName is the context name for the 'talosconfig'. ContextName string // Endpoints is the list of endpoints for the 'talosconfig'. Endpoints []string // NewTalosCA is the new CA for Talos API. NewTalosCA *x509.PEMEncodedCertificateAndKey // EncoderOption is the option for encoding machine configuration (while patching). EncoderOption encoder.Option // Printf is the function used to print messages. Printf func(format string, args ...any) } type rotator struct { opts Options currentCA []byte intermediateTalosconfig *clientconfig.Config newTalosconfig *clientconfig.Config intermediateClient *client.Client newClient *client.Client } // Rotate rotates the Talos API PKI. // // The process overview: // - fetch current information // - verify connectivity with the existing PKI // - add new Talos CA as accepted // - verify connectivity with the intermediate PKI // - make new CA issuing, old CA is still accepted // - verify connectivity with the new PKI // - remove old Talos CA // - verify connectivity with the new PKI. func Rotate(ctx context.Context, opts Options) (*clientconfig.Config, error) { r := rotator{ opts: opts, } defer func() { if r.intermediateClient != nil { r.intermediateClient.Close() //nolint:errcheck } if r.newClient != nil { r.newClient.Close() //nolint:errcheck } }() err := r.rotate(ctx) return r.newTalosconfig, err } //nolint:gocyclo func (r *rotator) rotate(ctx context.Context) error { r.printIntro() if err := r.fetchCurrentCA(ctx); err != nil { return err } if err := r.printNewCA(); err != nil { return err } if err := r.generateClients(ctx); err != nil { return err } if err := r.verifyConnectivity(ctx, r.opts.CurrentClient, "existing PKI"); err != nil { return err } if err := r.addNewCAAccepted(ctx); err != nil { return err } if err := r.verifyConnectivity(ctx, r.intermediateClient, "new client cert, but old server CA"); err != nil { return err } if err := r.swapCAs(ctx); err != nil { return err } if err := r.verifyConnectivity(ctx, r.newClient, "new PKI"); err != nil { return err } if err := r.dropOldCA(ctx); err != nil { return err } if err := r.verifyConnectivity(ctx, r.newClient, "new PKI"); err != nil { return err } return nil } func (r *rotator) printIntro() { r.opts.Printf("> Starting Talos API PKI rotation, dry-run mode %v...\n", r.opts.DryRun) r.opts.Printf("> Using config context: %q\n", r.opts.ContextName) r.opts.Printf("> Using Talos API endpoints: %q\n", r.opts.Endpoints) r.opts.Printf("> Cluster topology:\n") r.opts.Printf(" - control plane nodes: %q\n", append( helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeInit)), helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeControlPlane))..., ), ) r.opts.Printf(" - worker nodes: %q\n", helpers.MapToInternalIP(r.opts.ClusterInfo.NodesByType(machine.TypeWorker)), ) } func (r *rotator) fetchCurrentCA(ctx context.Context) error { r.opts.Printf("> Current Talos CA:\n") firstNode := append( r.opts.ClusterInfo.NodesByType(machine.TypeInit), r.opts.ClusterInfo.NodesByType(machine.TypeControlPlane)..., )[0] osRoot, err := safe.StateGetByID[*secretsres.OSRoot](client.WithNode(ctx, firstNode.InternalIP.String()), r.opts.CurrentClient.COSI, secretsres.OSRootID) if err != nil { return fmt.Errorf("error fetching existing Talos CA: %w", err) } r.currentCA = osRoot.TypedSpec().IssuingCA.Crt var b bytes.Buffer if err = yaml.NewEncoder(&b).Encode(osRoot.TypedSpec().IssuingCA); err != nil { return fmt.Errorf("error encoding new Talos CA: %w", err) } for scanner := bufio.NewScanner(&b); scanner.Scan(); { r.opts.Printf(" %s\n", scanner.Text()) } return nil } func (r *rotator) printNewCA() error { r.opts.Printf("> New Talos CA:\n") var b bytes.Buffer if err := yaml.NewEncoder(&b).Encode(r.opts.NewTalosCA); err != nil { return fmt.Errorf("error encoding new Talos CA: %w", err) } for scanner := bufio.NewScanner(&b); scanner.Scan(); { r.opts.Printf(" %s\n", scanner.Text()) } return nil } func (r *rotator) generateClients(ctx context.Context) error { r.opts.Printf("> Generating new talosconfig:\n") newBundle := &secrets.Bundle{ Clock: secrets.NewFixedClock(time.Now()), Certs: &secrets.Certs{ OS: r.opts.NewTalosCA, }, } cert, err := newBundle.GenerateTalosAPIClientCertificate(role.MakeSet(role.Admin)) if err != nil { return fmt.Errorf("error generating new talosconfig: %w", err) } // using old server CA, but new client cert r.intermediateTalosconfig = clientconfig.NewConfig(r.opts.ContextName, r.opts.Endpoints, r.currentCA, cert) // using new server CA and a new client cert r.newTalosconfig = clientconfig.NewConfig(r.opts.ContextName, r.opts.Endpoints, r.opts.NewTalosCA.Crt, cert) marshalledTalosconfig, err := r.newTalosconfig.Bytes() if err != nil { return fmt.Errorf("error marshaling talosconfig: %w", err) } r.opts.Printf("%s\n", string(marshalledTalosconfig)) r.intermediateClient, err = client.New(ctx, client.WithConfig(r.intermediateTalosconfig), ) if err != nil { return fmt.Errorf("error creating intermediate client: %w", err) } r.newClient, err = client.New(ctx, client.WithConfig(r.newTalosconfig), ) if err != nil { return fmt.Errorf("error creating new client: %w", err) } return nil } func (r *rotator) verifyConnectivity(ctx context.Context, c *client.Client, label string) error { r.opts.Printf("> Verifying connectivity with %s:\n", label) for _, node := range r.opts.ClusterInfo.Nodes() { if r.opts.DryRun { r.opts.Printf(" - %s: OK (dry-run)\n", node.InternalIP) continue } var resp *machineapi.VersionResponse if err := retry.Constant(10*time.Second, retry.WithUnits(100*time.Millisecond), retry.WithErrorLogging(true)).RetryWithContext(ctx, func(ctx context.Context) error { nodeCtx := client.WithNode(ctx, node.InternalIP.String()) var respErr error resp, respErr = c.Version(nodeCtx) if respErr != nil { if client.StatusCode(respErr) == codes.Unavailable { return retry.ExpectedError(respErr) } return respErr } return nil }); err != nil { return fmt.Errorf("error calling version API on node %s: %w", node.InternalIP, err) } r.opts.Printf(" - %s: OK (version %s)\n", node.InternalIP, resp.Messages[0].Version.GetTag()) } return nil } func (r *rotator) addNewCAAccepted(ctx context.Context) error { r.opts.Printf("> Adding new Talos CA as accepted...\n") if err := r.patchAllNodes(ctx, r.opts.CurrentClient, func(_ machine.Type, config *v1alpha1.Config) error { config.MachineConfig.MachineAcceptedCAs = append( config.MachineConfig.MachineAcceptedCAs, &x509.PEMEncodedCertificate{ Crt: r.opts.NewTalosCA.Crt, }, ) return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) swapCAs(ctx context.Context) error { r.opts.Printf("> Making new Talos CA the issuing CA, old Talos CA the accepted CA...\n") if err := r.patchAllNodes(ctx, r.intermediateClient, func(machineType machine.Type, config *v1alpha1.Config) error { config.MachineConfig.MachineAcceptedCAs = append( config.MachineConfig.MachineAcceptedCAs, &x509.PEMEncodedCertificate{ Crt: r.currentCA, }, ) config.MachineConfig.MachineAcceptedCAs = slices.DeleteFunc(config.Machine().Security().AcceptedCAs(), func(ca *x509.PEMEncodedCertificate) bool { return bytes.Equal(ca.Crt, r.opts.NewTalosCA.Crt) }) if machineType.IsControlPlane() { config.MachineConfig.MachineCA = r.opts.NewTalosCA } else { config.MachineConfig.MachineCA = &x509.PEMEncodedCertificateAndKey{ Crt: r.opts.NewTalosCA.Crt, } } return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) dropOldCA(ctx context.Context) error { r.opts.Printf("> Removing old Talos CA from the accepted CAs...\n") if err := r.patchAllNodes(ctx, r.newClient, func(_ machine.Type, config *v1alpha1.Config) error { config.MachineConfig.MachineAcceptedCAs = slices.DeleteFunc(config.Machine().Security().AcceptedCAs(), func(ca *x509.PEMEncodedCertificate) bool { return bytes.Equal(ca.Crt, r.currentCA) }) return nil }); err != nil { return fmt.Errorf("error patching all machine configs: %w", err) } return nil } func (r *rotator) patchAllNodes(ctx context.Context, c *client.Client, patchFunc func(machineType machine.Type, config *v1alpha1.Config) error) error { for _, machineType := range []machine.Type{machine.TypeInit, machine.TypeControlPlane, machine.TypeWorker} { for _, node := range r.opts.ClusterInfo.NodesByType(machineType) { if r.opts.DryRun { r.opts.Printf(" - %s: skipped (dry-run)\n", node.InternalIP) continue } if err := helpers.PatchNodeConfig(ctx, c, node.InternalIP.String(), r.opts.EncoderOption, func(config *v1alpha1.Config) error { return patchFunc(machineType, config) }); err != nil { return fmt.Errorf("error patching node %s: %w", node.InternalIP, err) } r.opts.Printf(" - %s: OK\n", node.InternalIP) } } return nil } ================================================ FILE: pkg/safepath/safepath.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package safepath provides a set of functions to make paths safe for use with. package safepath import ( "os" "path/filepath" ) // CleanPath makes a path safe for use with filepath.Join. This is done by not // only cleaning the path, but also (if the path is relative) adding a leading // '/' and cleaning it (then removing the leading '/'). This ensures that a // path resulting from prepending another path will always resolve to lexically // be a subdirectory of the prefixed path. This is all done lexically, so paths // that include symlinks won't be safe as a result of using CleanPath. func CleanPath(path string) string { // Deal with empty strings nicely. if path == "" { return "" } // Ensure that all paths are cleaned (especially problematic ones like // "/../../../../../" which can cause lots of issues). path = filepath.Clean(path) // If the path isn't absolute, we need to do more processing to fix paths // such as "../../../..//some/path". We also shouldn't convert absolute // paths to relative ones. if !filepath.IsAbs(path) { path = filepath.Clean(string(os.PathSeparator) + path) // This can't fail, as (by definition) all paths are relative to root. path, _ = filepath.Rel(string(os.PathSeparator), path) //nolint:errcheck } // Clean the path again for good measure. return filepath.Clean(path) } ================================================ FILE: pkg/safepath/safepath_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package safepath_test import ( "testing" "github.com/siderolabs/talos/pkg/safepath" ) func TestCleanPath(t *testing.T) { path := safepath.CleanPath("") if path != "" { t.Errorf("expected to receive empty string and received %s", path) } path = safepath.CleanPath("rootfs") if path != "rootfs" { t.Errorf("expected to receive 'rootfs' and received %s", path) } path = safepath.CleanPath("../../../var") if path != "var" { t.Errorf("expected to receive 'var' and received %s", path) } path = safepath.CleanPath("/../../../var") if path != "/var" { t.Errorf("expected to receive '/var' and received %s", path) } path = safepath.CleanPath("/foo/bar/") if path != "/foo/bar" { t.Errorf("expected to receive '/foo/bar' and received %s", path) } path = safepath.CleanPath("/foo/bar/../") if path != "/foo" { t.Errorf("expected to receive '/foo' and received %s", path) } } ================================================ FILE: pkg/splash/splash.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package splash contains boot splash images. package splash import ( _ "embed" ) //go:embed sidero.bmp var sideroBMP []byte // GetBootImage returns boot splash image. func GetBootImage() []byte { return sideroBMP } ================================================ FILE: pkg/startup/maxprocs.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package startup import ( "log" "runtime" ) // LimitMaxProcs limits the GOMAXPROCS to the number specified. func LimitMaxProcs(maxProcs int) { curProcs := runtime.GOMAXPROCS(0) if curProcs > maxProcs { runtime.GOMAXPROCS(maxProcs) log.Printf("limited GOMAXPROCS to %d", maxProcs) } } ================================================ FILE: pkg/startup/startup.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package startup provides utility function for process startup package startup ================================================ FILE: pkg/xfs/fsopen/fsopen_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux // Package fsopen provides a simple interface to create and manage a filesystem // using the Linux syscalls for filesystem operations. package fsopen import ( "context" "errors" "fmt" "os" "path/filepath" "golang.org/x/sys/unix" "github.com/siderolabs/talos/pkg/makefs" "github.com/siderolabs/talos/pkg/xfs" ) // ErrRepairUnsupported is reported when the filesystem does not support repairs. var ErrRepairUnsupported = errors.New("unsupported filesystem type for repair") // FS represents a filesystem that can be created and managed. // It holds the flags and strings used for configuration, as well as the file descriptor // for the mounted filesystem. type FS struct { fstype string source string boolParams map[string]struct{} stringParams map[string][]string binaryParams map[string][][]byte mntfd int mntflags int } // Interface guard. var ( _ xfs.FS = (*FS)(nil) ) // New creates a new FS instance with the provided options. func New(fstype string, opts ...Option) *FS { fs := &FS{ fstype: fstype, boolParams: make(map[string]struct{}), stringParams: make(map[string][]string), binaryParams: make(map[string][][]byte), } for _, opt := range defaultOpts(fstype, opts...) { opt.set(fs) } return fs } // defaultOpts applies default options for filesystems. func defaultOpts(fstype string, opts ...Option) []Option { if fstype == "iso9660" { opts = append( opts, WithBoolParameter("ro"), ) } return opts } // Open initializes the filesystem and returns the file descriptor for the mounted filesystem. // If the filesystem is already created, it returns the existing file descriptor. // This method is idempotent, meaning it can be called multiple times without side effects. func (fs *FS) Open() (int, error) { if fs.mntfd != 0 { return fs.mntfd, nil } err := fs.new() return fs.mntfd, err } //nolint:gocyclo func (fs *FS) new() (err error) { var fsfd int fsfd, err = unix.Fsopen(fs.fstype, unix.FSOPEN_CLOEXEC) if err != nil { return fmt.Errorf("unix.Fsopen fstype=%q failed: %w", fs.fstype, err) } defer func() { if cloErr := unix.Close(fsfd); err == nil { err = cloErr } else { err = errors.Join(err, cloErr) } }() if fs.source != "" { if err := unix.FsconfigSetString(fsfd, "source", fs.source); err != nil { return fmt.Errorf("FSCONFIG_SET_STRING failed: %w: key=%q value=%q", err, "source", fs.source) } } for key := range fs.boolParams { if err := unix.FsconfigSetFlag(fsfd, key); err != nil { return fmt.Errorf("FSCONFIG_SET_FLAG failed: %w: key=%q", err, key) } } for key, binary := range fs.binaryParams { for _, bf := range binary { if err := unix.FsconfigSetBinary(fsfd, key, bf); err != nil { return fmt.Errorf("FSCONFIG_SET_BINARY failed: %w: key=%q", err, key) } } } for key, values := range fs.stringParams { for _, value := range values { if err := unix.FsconfigSetString(fsfd, key, value); err != nil { return fmt.Errorf("FSCONFIG_SET_BINARY failed: %w: key=%q", err, key) } } } err = unix.FsconfigCreate(fsfd) if err != nil { return fmt.Errorf("FSCONFIG_CMD_CREATE failed: %w", err) } fs.mntflags |= unix.FSMOUNT_CLOEXEC fs.mntfd, err = unix.Fsmount(fsfd, fs.mntflags, 0) if err != nil { return fmt.Errorf("FSMOUNT failed: %w", err) } return nil } // Close closes the file descriptor. func (fs *FS) Close() error { if fs.mntfd == 0 { return os.ErrClosed } oldmntfd := fs.mntfd fs.mntfd = 0 return unix.Close(oldmntfd) } // Repair attempts to repair the filesystem if it is in a dirty state. func (fs *FS) Repair(ctx context.Context) error { var repairFunc func(ctx context.Context, partition string) error switch fs.fstype { case makefs.FilesystemTypeEXT4: repairFunc = makefs.Ext4Repair case makefs.FilesystemTypeXFS: repairFunc = makefs.XFSRepair default: return fmt.Errorf("%w: %s", ErrRepairUnsupported, fs.fstype) } if err := repairFunc(ctx, fs.source); err != nil { return fmt.Errorf("repair %q: %w", fs.source, err) } return nil } // MountAt mounts the filesystem at the specified path. // // EXPERIMENTAL: This function is experimental and may change in the future. func (fs *FS) MountAt(path string) (string, error) { realpath, err := filepath.EvalSymlinks(path) if err != nil { return "", fmt.Errorf("%w: %q -> %q", err, path, realpath) } if err := fs.mountAt(realpath); err != nil { return "", fmt.Errorf("%w: %q", err, realpath) } return realpath, nil } func (fs *FS) mountAt(path string) error { return unix.MoveMount(fs.mntfd, "", unix.AT_FDCWD, path, unix.MOVE_MOUNT_F_EMPTY_PATH) } // UnmountFrom unmounts the filesystem from the specified path. // // EXPERIMENTAL: This function is experimental and may change in the future. func (fs *FS) UnmountFrom(path string) error { return unix.Unmount(path, 0) } // Source returns the source string used to create the filesystem. func (fs *FS) Source() string { return fs.source } // FSType returns the filesystem type string. func (fs *FS) FSType() string { return fs.fstype } ================================================ FILE: pkg/xfs/fsopen/fsopen_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux // Package fsopen provides a simple interface to create and manage a filesystem // using the Linux syscalls for filesystem operations. package fsopen ================================================ FILE: pkg/xfs/fsopen/options_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package fsopen // Option is a functional option for configuring a filesystem instance. type Option struct { set func(*FS) } // WithSource adds a source to the filesystem configuration. func WithSource(source string) Option { return Option{ set: func(t *FS) { t.source = source }, } } // WithMountFlags adds a flag set that will be passed to Fsmount syscall. func WithMountFlags(flag int) Option { return Option{ set: func(f *FS) { f.mntflags |= flag }, } } // WithProjectQuota sets the project quota flag. func WithProjectQuota(enabled bool) Option { return Option{ set: func(t *FS) { if !enabled { return } if t.boolParams == nil { t.boolParams = make(map[string]struct{}) } t.boolParams["prjquota"] = struct{}{} }, } } // WithStringParameter adds a map of strings to the filesystem configuration. func WithStringParameter(param, value string) Option { return Option{ set: func(t *FS) { if t.stringParams == nil { t.stringParams = make(map[string][]string) } t.stringParams[param] = append(t.stringParams[param], value) }, } } // WithBoolParameter adds a flag parameter to the filesystem configuration. func WithBoolParameter(param string) Option { return Option{ set: func(t *FS) { if t.boolParams == nil { t.boolParams = make(map[string]struct{}) } t.boolParams[param] = struct{}{} }, } } // WithBinaryParameters adds a map of byte arrays to the filesystem configuration. func WithBinaryParameters(param string, value []byte) Option { return Option{ set: func(t *FS) { if t.binaryParams == nil { t.binaryParams = make(map[string][][]byte) } t.binaryParams[param] = append(t.binaryParams[param], value) }, } } ================================================ FILE: pkg/xfs/fsopen_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package xfs_test import ( "os" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" ) func TestFsopen(t *testing.T) { t.Parallel() if uid := os.Getuid(); uid != 0 { t.Skipf("skipping test, not running as root (uid %d)", uid) } for _, tc := range []struct { fstype string opts []fsopen.Option }{ {fstype: "tmpfs"}, } { t.Run(tc.fstype, func(t *testing.T) { t.Parallel() root := &xfs.UnixRoot{FS: fsopen.New(tc.fstype, tc.opts...)} err := root.OpenFS() require.NoError(t, err) t.Cleanup(func() { require.NoError(t, root.Close()) }) testFilesystem(t, root, nil) }) } } ================================================ FILE: pkg/xfs/helpers_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package xfs import ( "errors" "fmt" "io/fs" "os" "syscall" ) // AsOSFile attempts to convert fs.File to *os.File. func AsOSFile(f fs.File, name string) (*os.File, error) { ff, ok := f.(File) if !ok { return nil, errors.ErrUnsupported } newFd, err := syscall.Dup(int(ff.Fd())) if err != nil { return nil, fmt.Errorf("failed to duplicate file descriptor: %w", err) } return os.NewFile(uintptr(newFd), name), nil } ================================================ FILE: pkg/xfs/opentree/opentree_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux // Package opentree provides a simple interface to create and manage a subfilesystem // using the `open_tree` syscall. It allows for creating a new subfilesystem // by cloning an existing filesystem tree and provides a method to close the filesystem // when it is no longer needed. package opentree import ( "context" "fmt" "golang.org/x/sys/unix" xfs "github.com/siderolabs/talos/pkg/xfs" ) // FS represents a subfilesystem that can be created and managed. // It uses a file descriptor to represent the mounted filesystem and provides methods // to create and close the filesystem. The creation of the filesystem is idempotent, // meaning it can be called multiple times without side effects. type FS struct { root string rootfd int mntfd int } // Interface guard. var _ xfs.FS = (*FS)(nil) // NewFromPath creates a new fs instance from path. func NewFromPath(path string) *FS { return &FS{ root: path, rootfd: unix.AT_FDCWD, } } // NewFromFd creates a new fs instance from file descriptor. func NewFromFd(fd int) *FS { return &FS{ rootfd: fd, } } // Open initializes the fs filesystem and returns the file descriptor for the mounted filesystem. // If the filesystem is already created, it returns the existing file descriptor. // This method is idempotent, meaning it can be called multiple times without side effects. func (fs *FS) Open() (int, error) { if fs.mntfd != 0 { return fs.mntfd, nil } flags := unix.OPEN_TREE_CLONE | unix.OPEN_TREE_CLOEXEC if fs.root == "" { flags |= unix.AT_EMPTY_PATH } err := fs.new(uint(flags)) return fs.mntfd, err } func (fs *FS) new(flags uint) error { mntfd, err := unix.OpenTree(fs.rootfd, fs.root, flags) if err != nil { return fmt.Errorf("unix.OpenTree on %q failed: %w", fs.root, err) } fs.mntfd = mntfd return nil } // Close closes the file descriptor. func (fs *FS) Close() error { if fs.mntfd == 0 { return nil } oldmntfd := fs.mntfd fs.mntfd = 0 return unix.Close(oldmntfd) } // Repair repairs the filesystem if needed. // This method is a no-op as the `open_tree` syscall does not support repair operations. func (fs *FS) Repair(context.Context) error { return nil } // Source returns the source path of the filesystem. func (fs *FS) Source() string { return fs.root } // FSType is no-op for opentree fs. func (fs *FS) FSType() string { return "" } ================================================ FILE: pkg/xfs/opentree/opentree_other.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !linux // Package opentree provides a simple interface to create and manage a subfilesystem // using the `open_tree` syscall. It allows for creating a new subfilesystem // by cloning an existing filesystem tree and provides a method to close the filesystem // when it is no longer needed. package opentree ================================================ FILE: pkg/xfs/opentree_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package xfs_test import ( "os" "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/internal/app/machined/pkg/runtime" "github.com/siderolabs/talos/pkg/xfs" "github.com/siderolabs/talos/pkg/xfs/fsopen" "github.com/siderolabs/talos/pkg/xfs/opentree" ) func TestOpentree(t *testing.T) { t.Parallel() if uid := os.Getuid(); uid != 0 { t.Skipf("skipping test, not running as root (uid %d)", uid) } t.Run("TempDir", func(t *testing.T) { t.Parallel() testRoot := t.TempDir() root := &xfs.UnixRoot{FS: opentree.NewFromPath(testRoot)} err := root.OpenFS() require.NoError(t, err) t.Cleanup(func() { require.NoError(t, root.Close()) }) testFilesystem(t, root, root) }) t.Run("MountDir", func(t *testing.T) { t.Parallel() fs := fsopen.New("tmpfs") roRoot := &xfs.UnixRoot{FS: fs} err := roRoot.OpenFS() require.NoError(t, err) roRoot.Shadow, err = fs.MountAt(t.TempDir()) require.NoError(t, err) t.Cleanup(func() { require.NoError(t, roRoot.Close()) require.NoError(t, fs.UnmountFrom(roRoot.Shadow)) }) bfs := opentree.NewFromPath(roRoot.Shadow) rwRoot := &xfs.UnixRoot{FS: bfs} err = rwRoot.OpenFS() require.NoError(t, err) t.Cleanup(func() { require.NoError(t, rwRoot.Close()) }) testFilesystem(t, rwRoot, roRoot) }) t.Run("FileDescriptor", func(t *testing.T) { t.Parallel() ok, err := runtime.KernelCapabilities().OpentreeOnAnonymousFS() require.NoError(t, err) if !ok { t.Skip("OpenTree on Anonymous FS requires kernel 6.15.0+") } roRoot := &xfs.UnixRoot{FS: fsopen.New("tmpfs")} err = roRoot.OpenFS() require.NoError(t, err) t.Cleanup(func() { require.NoError(t, roRoot.Close()) }) fd, err := roRoot.Fd() require.NoError(t, err) bfs := opentree.NewFromFd(fd) rwRoot := &xfs.UnixRoot{FS: bfs} err = rwRoot.OpenFS() require.NoError(t, err) testFilesystem(t, rwRoot, roRoot) }) } ================================================ FILE: pkg/xfs/osroot.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package xfs import ( "context" "io/fs" "os" "path/filepath" ) // OSRoot represents a filesystem wrapper using os interface. type OSRoot struct { Shadow string } // Interface guard. var _ interface { Root } = (*OSRoot)(nil) // OpenFS is no-op for OSRoot. func (root *OSRoot) OpenFS() error { return nil } // Close is no-op for OSRoot. func (root *OSRoot) Close() error { return nil } // RepairFS is no-op for OSRoot. func (root *OSRoot) RepairFS(context.Context) error { return nil } // Fd is no-op for OSRoot. func (root *OSRoot) Fd() (int, error) { return 0, os.ErrInvalid } // Mkdir creates a new directory in the root filesystem with the specified name and permissions. func (root *OSRoot) Mkdir(name string, perm os.FileMode) error { return os.Mkdir(filepath.Join(root.Shadow, name), perm) } // Open opens a file in the root filesystem with the specified name in read-only mode. func (root *OSRoot) Open(name string) (fs.File, error) { return os.Open(filepath.Join(root.Shadow, name)) } // OpenFile opens a file in the root filesystem with the specified name, flags, and permissions. func (root *OSRoot) OpenFile(name string, flags int, perm os.FileMode) (File, error) { return os.OpenFile(filepath.Join(root.Shadow, name), flags, perm) } // Remove removes a file or directory from the root filesystem. func (root *OSRoot) Remove(name string) error { return os.Remove(filepath.Join(root.Shadow, name)) } // Rename renames a file or directory in the root filesystem from old to new. func (root *OSRoot) Rename(oldname, newname string) error { return os.Rename(filepath.Join(root.Shadow, oldname), filepath.Join(root.Shadow, newname)) } // Source returns the source of the underlying filesystem. func (root *OSRoot) Source() string { return root.Shadow } // FSType returns the type of the underlying filesystem. func (root *OSRoot) FSType() string { return "os" } ================================================ FILE: pkg/xfs/osroot_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package xfs_test import ( "testing" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/xfs" ) func TestOs(t *testing.T) { t.Parallel() t.Run("OSRoot", func(t *testing.T) { t.Parallel() root := &xfs.OSRoot{Shadow: t.TempDir()} require.NoError(t, root.OpenFS()) t.Cleanup(func() { require.NoError(t, root.Close()) }) testFilesystem(t, root, nil) }) } ================================================ FILE: pkg/xfs/root.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package xfs provides an extended file system interface that includes // additional methods for writing files and directories, as well as utility // functions for reading, writing, and manipulating files and directories // within a specified file system. package xfs import ( "context" "errors" "fmt" "io" "io/fs" "math/rand/v2" "os" "path/filepath" "strconv" "strings" ) // FS is an interface for creating file system handles. type FS interface { Open() (int, error) io.Closer Repair(context.Context) error Source() string FSType() string } // Root is an interface that extends the standard fs.FS interface with Write capabilities. // //nolint:interfacebloat type Root interface { fs.FS io.Closer OpenFS() error RepairFS(context.Context) error Fd() (int, error) Mkdir(name string, perm os.FileMode) error OpenFile(name string, flags int, perm os.FileMode) (File, error) Remove(name string) error Rename(oldname, newname string) error Source() string FSType() string } // File is an interface that extends the standard fs.File interface with additional methods for writing. type File interface { fs.File io.Closer io.Reader io.ReaderAt io.Seeker io.Writer io.WriterAt Fd() uintptr } // Interface guard. var _ File = (*os.File)(nil) // ReadFile wraps fs.ReadFile to read a file from the specified FileSystem. func ReadFile(root Root, name string) ([]byte, error) { return fs.ReadFile(root, name) } // ReadDir wraps fs.ReadDir to read a directory from the specified FileSystem. func ReadDir(root Root, name string) ([]fs.DirEntry, error) { return fs.ReadDir(root, name) } // Stat wraps fs.Stat to get the file or directory information from the specified FileSystem. func Stat(root Root, name string) (fs.FileInfo, error) { return fs.Stat(root, name) } // WriteFile is equivalent of os.WriteFile acting on specified FileSystem. func WriteFile(root Root, name string, data []byte, perm os.FileMode) error { f, err := root.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) if err != nil { return err } _, err = f.Write(data) if err1 := f.Close(); err1 != nil && err == nil { err = err1 } return err } // Open wraps (FS).Open. func Open(root Root, name string) (fs.File, error) { return root.Open(name) } // OpenFile wraps (FS).OpenFile. func OpenFile(root Root, name string, flags int, perm os.FileMode) (File, error) { return root.OpenFile(name, flags, perm) } // Mkdir wraps (FS).Mkdir. func Mkdir(root Root, name string, perm os.FileMode) error { return root.Mkdir(name, perm) } // MkdirAll is equivalent of os.MkdirAll acting on specified FileSystem. func MkdirAll(root Root, name string, perm os.FileMode) error { components := SplitPath(name) for i := range len(components) + 1 { dir := filepath.Join(components[:i]...) if dir == "" { // empty name, continue... continue } err := root.Mkdir(dir, perm) if err != nil && !errors.Is(err, fs.ErrExist) { return fmt.Errorf("%w: %s", err, dir) } } return nil } // MkdirTemp creates a temporary directory in the specified directory with a given pattern. func MkdirTemp(root Root, dir, pattern string) (string, error) { if dir == "" { dir = os.TempDir() } if pattern == "" { pattern = "tmp" } if strings.Count(pattern, "*") > 1 { return "", fmt.Errorf("pattern %q must contain at most one '*'", pattern) } const maxAttempts = 10000 for range maxAttempts { suffix := strconv.Itoa(rand.Int()) name := strings.Replace(pattern, "*", suffix, 1) if !strings.Contains(pattern, "*") { name = pattern + suffix } tempDir := filepath.Join(dir, name) err := MkdirAll(root, tempDir, 0o777) if err == nil { return tempDir, nil } if errors.Is(err, fs.ErrExist) { continue } return "", err } return "", fmt.Errorf("failed to create temporary directory after %d attempts", maxAttempts) } // Remove wraps (FS).Remove. func Remove(root Root, name string) error { return root.Remove(name) } // RemoveAll is equivalent of os.RemoveAll acting on specified FileSystem. func RemoveAll(root Root, name string) (err error) { var f fs.File f, err = root.Open(name) if err != nil { if errors.Is(err, os.ErrNotExist) { return nil } return err } stat, err := f.Stat() if err != nil { return err } if err := f.Close(); err != nil { return err } if !stat.IsDir() { return root.Remove(name) } entries, err := ReadDir(root, name) if err != nil { return err } for _, entry := range entries { childPath := filepath.Join(name, entry.Name()) if err := RemoveAll(root, childPath); err != nil { return err } } return root.Remove(name) } // Rename wraps (FS).Rename. func Rename(root Root, oldname, newname string) error { return root.Rename(oldname, newname) } // SplitPath splits a path into its components, similar to filepath.Split but returns all parts. func SplitPath(path string) []string { var parts []string for { dir, file := filepath.Split(path) if file != "" { parts = append([]string{file}, parts...) } if dir == "" || dir == path { break } path = filepath.Clean(dir) } return parts } ================================================ FILE: pkg/xfs/root_linux.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build linux package xfs import ( "context" "fmt" "io/fs" "os" "strings" "golang.org/x/sys/unix" ) // UnixRoot represents a filesystem wrapper for Unix-like systems. type UnixRoot struct { Shadow string FS FS mntfd int } // Interface guard. var _ interface { Root } = (*UnixRoot)(nil) // OpenFS opens the underlying filesystem. func (root *UnixRoot) OpenFS() error { var err error if root.mntfd != 0 { return nil } root.mntfd, err = root.FS.Open() if err != nil { return fmt.Errorf("failed to create root filesystem: %w", err) } return nil } // Close closes the underlying filesystem. func (root *UnixRoot) Close() error { if root.mntfd == 0 { return nil } root.mntfd = 0 return root.FS.Close() } // RepairFS repairs the underlying filesystem if necessary. func (root *UnixRoot) RepairFS(ctx context.Context) error { return root.FS.Repair(ctx) } // Fd returns the file descriptor of the mounted root filesystem. // It returns an error if the filesystem is not open or has been closed. // Usage of the returned file descriptior is no longer thread safe. func (root *UnixRoot) Fd() (int, error) { if root.mntfd == 0 { return 0, os.ErrClosed } return root.mntfd, nil } // Mkdir creates a new directory in the root filesystem with the specified name and permissions. func (root *UnixRoot) Mkdir(name string, perm os.FileMode) error { return unix.Mkdirat(root.mntfd, strings.TrimLeft(name, "/"), uint32(perm)) } // Open opens a file in the root filesystem with the specified name in read-only mode. func (root *UnixRoot) Open(name string) (fs.File, error) { return root.OpenFile(strings.TrimLeft(name, "/"), unix.O_RDONLY, 0) } // OpenFile opens a file in the root filesystem with the specified name, flags, and permissions. func (root *UnixRoot) OpenFile(name string, flags int, perm os.FileMode) (File, error) { fd, err := unix.Openat(root.mntfd, strings.TrimLeft(name, "/"), flags, uint32(perm)) if err != nil { return nil, err } return os.NewFile(uintptr(fd), strings.TrimLeft(name, "/")), nil } // Remove removes a file or directory from the root filesystem. func (root *UnixRoot) Remove(name string) error { flags := 0 info, err := root.stat(strings.TrimLeft(name, "/")) if err != nil { return err } if info.IsDir() { flags = unix.AT_REMOVEDIR } return unix.Unlinkat(root.mntfd, strings.TrimLeft(name, "/"), flags) } // Rename renames a file or directory in the root filesystem from old to new. func (root *UnixRoot) Rename(oldname, newname string) error { return unix.Renameat(root.mntfd, strings.TrimLeft(oldname, "/"), root.mntfd, strings.TrimLeft(newname, "/")) } func (root *UnixRoot) stat(name string) (os.FileInfo, error) { f, err := root.Open(strings.TrimLeft(name, "/")) if err != nil { return nil, err } defer f.Close() //nolint:errcheck return f.Stat() } // Source returns the source of the underlying filesystem. func (root *UnixRoot) Source() string { return root.FS.Source() } // FSType returns the type of the underlying filesystem. func (root *UnixRoot) FSType() string { return root.FS.FSType() } ================================================ FILE: pkg/xfs/root_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package xfs_test import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/siderolabs/talos/pkg/xfs" ) const testDir = "testdir" var testFileContent = []byte("test content") func testFilesystem(t *testing.T, rwRoot xfs.Root, roRoot xfs.Root) { if roRoot == nil { roRoot = rwRoot } touchTree(t, rwRoot, filepath.Join(testDir, "root.test")) t.Run("Open", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "open.test") touchTree(t, rwRoot, name) actual, err := xfs.Open(rwRoot, name) assert.NoError(t, err) assert.NoError(t, actual.Close()) }) t.Run("OpenFile", func(t *testing.T) { t.Parallel() flags := os.O_RDWR | os.O_CREATE name := filepath.Join(testDir, "open-file.test") actual, err := xfs.OpenFile(rwRoot, name, flags, 0o644) require.NoError(t, err) assert.NoError(t, actual.Close()) }) t.Run("WriteFile", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "write-file.test") err := xfs.WriteFile(rwRoot, name, testFileContent, 0o644) assert.NoError(t, err) }) t.Run("ReadFile", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "read-file.test") writeFile(t, rwRoot, name, testFileContent) actual, err := xfs.ReadFile(rwRoot, name) assert.NoError(t, err) assert.Equal(t, testFileContent, actual) }) t.Run("Mkdir", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "mkdir.test") err := xfs.Mkdir(rwRoot, name, 0o755) assert.NoError(t, err) testIsDir(t, roRoot, name) }) t.Run("MkdirAll", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "mkdir-all.d", "test.d") err := xfs.MkdirAll(rwRoot, name, 0o755) assert.NoError(t, err) components := xfs.SplitPath(name) for i := range len(components) + 1 { dir := filepath.Join(components[:i]...) if dir == "" { // empty name, continue... continue } testIsDir(t, roRoot, dir) } }) t.Run("MkdirTemp", func(t *testing.T) { t.Parallel() name, err := xfs.MkdirTemp(rwRoot, testDir, "") assert.NoError(t, err) components := xfs.SplitPath(name) for i := range len(components) + 1 { dir := filepath.Join(components[:i]...) if dir == "" { // empty name, continue... continue } testIsDir(t, roRoot, dir) } }) t.Run("Remove", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "remove.test") touchTree(t, rwRoot, name) err := xfs.Remove(rwRoot, name) require.NoError(t, err) _, err = xfs.Stat(roRoot, name) require.ErrorIs(t, err, os.ErrNotExist) }) t.Run("RemoveAll", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "remove-all.d", "file.test") touchTree(t, rwRoot, name) components := xfs.SplitPath(name) err := xfs.RemoveAll(rwRoot, filepath.Join(components[:2]...)) require.NoError(t, err) for i := range len(components[1:]) { test := filepath.Join(components[:len(components)-i]...) if test == "" { // empty name, continue... continue } _, err := xfs.Stat(roRoot, test) require.ErrorIs(t, err, os.ErrNotExist, "stat %q should not exist", test) } }) t.Run("Stat", func(t *testing.T) { t.Parallel() name := filepath.Join(testDir, "stat.d", "stat.test") touchTree(t, rwRoot, name) components := xfs.SplitPath(name) for i := range components { dir := filepath.Join(components[:i]...) if dir == "" { // empty name, continue... continue } actual, err := xfs.Stat(roRoot, dir) require.NoError(t, err, "stat dir %q failed", dir) assert.True(t, actual.IsDir()) } actual, err := xfs.Stat(roRoot, name) require.NoError(t, err, "stat file %q failed", name) assert.False(t, actual.IsDir()) }) t.Run("Rename", func(t *testing.T) { t.Parallel() t.Run("Dir", func(t *testing.T) { t.Parallel() oldName := filepath.Join(testDir, "rename.old.d", "test") newName := filepath.Join(testDir, "rename.new.d", "test") touchTree(t, rwRoot, oldName) err := xfs.Rename(rwRoot, filepath.Dir(oldName), filepath.Dir(newName)) assert.NoError(t, err) newDirStat, err := xfs.Stat(roRoot, filepath.Dir(newName)) require.NoError(t, err, "stat dir %q failed", filepath.Dir(newName)) assert.True(t, newDirStat.IsDir()) _, err = xfs.Stat(roRoot, newName) require.NoError(t, err, "stat file %q failed", newName) _, err = xfs.Stat(roRoot, oldName) require.ErrorIs(t, err, os.ErrNotExist, "stat dir %q failed", filepath.Dir(oldName)) }) t.Run("File", func(t *testing.T) { t.Parallel() oldName := filepath.Join(testDir, "rename.old.test") newName := filepath.Join(testDir, "rename.new.test") touchTree(t, rwRoot, oldName) err := xfs.Rename(rwRoot, oldName, newName) assert.NoError(t, err) _, err = xfs.Stat(roRoot, newName) require.NoError(t, err, "stat file %q failed", newName) _, err = xfs.Stat(roRoot, oldName) require.ErrorIs(t, err, os.ErrNotExist, "stat file %q failed", oldName) }) }) } func writeFile(tb testing.TB, root xfs.Root, name string, content []byte) { tb.Helper() err := xfs.WriteFile(root, name, content, 0o644) require.NoError(tb, err) } func touchTree(tb testing.TB, root xfs.Root, tree string) { tb.Helper() components := xfs.SplitPath(tree) for i := range components { dir := filepath.Join(components[:i]...) if dir == "" { // empty name, continue... continue } err := root.Mkdir(dir, 0o755) if os.IsExist(err) { continue } require.NoError(tb, err, "creating dir %q failed", dir) } f, err := root.OpenFile(tree, os.O_CREATE|os.O_TRUNC, 0o644) require.NoError(tb, err, "creating tree %q failed", tree) tb.Cleanup(func() { assert.NoError(tb, f.Close(), "closing file %q failed", tree) }) } func testIsDir(tb testing.TB, root xfs.Root, name string) { actual, err := root.Open(name) require.NoError(tb, err, "opening %q failed", name) tb.Cleanup(func() { assert.NoError(tb, actual.Close(), "closing %q failed", name) }) info, err := actual.Stat() require.NoError(tb, err, "stat on %q failed", name) assert.True(tb, info.IsDir()) } ================================================ FILE: tools/docgen/go.mod ================================================ module github.com/siderolabs/talos/tools/docgen go 1.26.0 require ( github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab github.com/invopop/jsonschema v0.13.0 github.com/microcosm-cc/bluemonday v1.0.27 github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 github.com/siderolabs/gen v0.8.6 github.com/wk8/go-ordered-map/v2 v2.1.8 go.yaml.in/yaml/v4 v4.0.0-rc.4 mvdan.cc/gofumpt v0.9.2 ) require ( github.com/aymerick/douceur v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/mailru/easyjson v0.7.7 // indirect golang.org/x/net v0.46.0 // indirect golang.org/x/text v0.30.0 // indirect golang.org/x/tools v0.38.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: tools/docgen/go.sum ================================================ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc= github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4= github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= ================================================ FILE: tools/docgen/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "bytes" "flag" "fmt" "go/ast" "go/parser" "go/token" "log" "maps" "os" "path/filepath" "reflect" "slices" "strings" "text/template" "github.com/siderolabs/gen/xslices" "go.yaml.in/yaml/v4" "mvdan.cc/gofumpt/format" ) var tpl = `// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Code generated by hack/docgen tool. DO NOT EDIT. package {{ .Package }} import ( "github.com/siderolabs/talos/pkg/machinery/config/encoder" ) {{ $tick := "` + "`" + `" -}} {{ range $struct := .Structs -}} func ({{ $struct.Name }}) Doc() *encoder.Doc { doc := &encoder.Doc{ Type : "{{ if $struct.Text.Alias }}{{ $struct.Text.Alias}}{{ else }}{{ $struct.Name }}{{ end }}", Comments: [3]string{ "" /* encoder.HeadComment */, "{{ $struct.Text.Comment }}" /* encoder.LineComment */, "" /* encoder.FootComment */}, Description: "{{ $struct.Text.Description }}", {{ if $struct.AppearsIn -}} AppearsIn: []encoder.Appearance{ {{ range $value := $struct.AppearsIn -}} { TypeName: "{{ $value.Struct.Name }}", FieldName: "{{ $value.FieldName }}", }, {{ end -}} }, {{ end -}} Fields: []encoder.Doc{ {{ range $index, $field := $struct.Fields -}} {{ if $field.Tag -}} { Name: "{{ $field.Tag }}", Type: "{{ $field.Type }}", Note: "{{ $field.Note }}", Description: "{{ $field.Text.Description }}", Comments: [3]string{ "" /* encoder.HeadComment */, "{{ $field.Text.Comment }}" /* encoder.LineComment */, "" /* encoder.FootComment */}, {{ if $field.Text.Values -}} Values : []string{ {{ range $value := $field.Text.Values -}} "{{ $value }}", {{ end -}} }, {{ end -}} }, {{ else if $field.Inline -}} { Type: "{{ $field.Type }}", Inline: true, }, {{ else -}} {}, {{- end }} {{- end }} }, } {{ range $example := $struct.Text.Examples }} {{ if $example.Value }} doc.AddExample("{{ $example.Name }}", {{ $example.Value }}) {{ end -}} {{ end }} {{ range $index, $field := $struct.Fields -}} {{ if $field.Tag -}} {{ if $field.Text.Examples -}} {{ range $example := $field.Text.Examples -}} {{- if $example.Value }} doc.Fields[{{ $index }}].AddExample("{{ $example.Name }}", {{ $example.Value }}) {{- end }} {{- end }} {{- end }} {{- end }} {{- end }} return doc } {{ end -}} // GetFileDoc returns documentation for the file {{ .File }}. func GetFileDoc() *encoder.FileDoc { return &encoder.FileDoc{ Name: "{{ .Name }}", Description: "{{ .Header }}", Structs: []*encoder.Doc{ {{ range $struct := .Structs -}} {{ $struct.Name }}{}.Doc(), {{ end -}} }, } } ` type Doc struct { Name string Package string Title string Header string File string Structs []*Struct } type Struct struct { Name string Text *Text Fields []*Field AppearsIn []Appearance } type Appearance struct { Struct *Struct FieldName string } type Example struct { Name string `yaml:"name"` Value string `yaml:"value"` } type Field struct { Name string Type string TypeRef string Text *Text Tag string Note string Inline bool } type Text struct { Comment string `json:"-"` Description string `json:"description"` Examples []*Example `json:"examples"` Alias string `json:"alias"` Values []string `json:"values"` Schema *SchemaWrapper `json:"schema"` SchemaRoot bool `json:"schemaRoot" yaml:"schemaRoot"` SchemaRequired bool `json:"schemaRequired" yaml:"schemaRequired"` SchemaMeta string `json:"schemaMeta" yaml:"schemaMeta"` } func in(p string) (string, error) { return filepath.Abs(p) } func out(p string) (*os.File, error) { abs, err := filepath.Abs(p) if err != nil { return nil, err } return os.Create(abs) } type packageType struct { name string doc string file string structs []*structType } type structType struct { name string text *Text pos token.Pos node *ast.StructType } type aliasType struct { fieldType string fieldTypeRef string } func collectStructs(node ast.Node) ([]*structType, map[string]aliasType) { var structs []*structType aliases := map[string]aliasType{} collectStructs := func(n ast.Node) bool { g, ok := n.(*ast.GenDecl) if !ok { return true } isAlias := false if g.Doc != nil { for _, comment := range g.Doc.List { if strings.Contains(comment.Text, "docgen:nodoc") { return true } if strings.Contains(comment.Text, "docgen:alias") { isAlias = true } } } for _, spec := range g.Specs { t, ok := spec.(*ast.TypeSpec) if !ok { return true } if t.Type == nil { return true } x, ok := t.Type.(*ast.StructType) if !ok { if isAlias { aliases[t.Name.Name] = aliasType{ fieldType: formatFieldType(t.Type), fieldTypeRef: getFieldType(t.Type), } } return true } structName := t.Name.Name text := &Text{} if t.Doc != nil { text = parseComment([]byte(t.Doc.Text())) } else if g.Doc != nil { text = parseComment([]byte(g.Doc.Text())) } s := &structType{ name: structName, text: text, node: x, pos: x.Pos(), } structs = append(structs, s) } return true } ast.Inspect(node, collectStructs) return structs, aliases } func parseComment(comment []byte) *Text { text := &Text{} if err := yaml.Unmarshal(comment, text); err != nil { lines := strings.Split(string(comment), "\n") for i := range lines { lines[i] = strings.TrimLeft(lines[i], "\t") } // not yaml, fallback text.Description = strings.Join(lines, "\n") // take only the first line from the Description for the comment text.Comment = lines[0] oldDescription := text.Description // try to parse everything except for the first line as yaml if err = yaml.Unmarshal([]byte(strings.Join(lines[1:], "\n")), text); err == nil { // if parsed, remove it from the description if text.Description != oldDescription { // something got parsed text.Description = text.Comment + "\n" + text.Description } else { // nothing got parsed text.Description = text.Comment } } } else { text.Description = strings.TrimSpace(text.Description) // take only the first line from the Description for the comment text.Comment = strings.Split(text.Description, "\n")[0] } text.Comment = escape(text.Comment) text.Description = escape(text.Description) for _, example := range text.Examples { example.Name = escape(example.Name) example.Value = strings.TrimSpace(example.Value) } return text } func getFieldType(p any) string { if m, ok := p.(*ast.MapType); ok { return getFieldType(m.Value) } switch t := p.(type) { case *ast.Ident: return t.Name case *ast.ArrayType: return getFieldType(p.(*ast.ArrayType).Elt) case *ast.StarExpr: return getFieldType(t.X) case *ast.SelectorExpr: return getFieldType(t.Sel) default: return "" } } func formatFieldType(p any) string { if m, ok := p.(*ast.MapType); ok { return fmt.Sprintf("map[%s]%s", formatFieldType(m.Key), formatFieldType(m.Value)) } switch t := p.(type) { case *ast.Ident: return t.Name case *ast.ArrayType: return "[]" + formatFieldType(p.(*ast.ArrayType).Elt) case *ast.StructType: return "struct" case *ast.StarExpr: return formatFieldType(t.X) case *ast.SelectorExpr: return formatFieldType(t.Sel) default: log.Printf("unknown: %#v", t) return "" } } func escape(value string) string { value = strings.ReplaceAll(value, `"`, `\"`) value = strings.ReplaceAll(value, "\n", `\n`) return strings.TrimSpace(value) } func collectFields(s *structType, aliases map[string]aliasType) (fields []*Field) { fields = []*Field{} for _, f := range s.node.Fields.List { if f.Names == nil { // This is an embedded struct. if f.Tag != nil && reflect.StructTag(strings.Trim(f.Tag.Value, "`")).Get("yaml") == ",inline" { fields = append(fields, &Field{ Type: formatFieldType(f.Type), TypeRef: getFieldType(f.Type), Inline: true, }) } else { fields = append(fields, &Field{Type: "unknown"}) } continue } name := f.Names[0].Name if f.Doc == nil { log.Fatalf("field %q is missing a documentation", name) } if strings.Contains(f.Doc.Text(), "docgen:nodoc") { fields = append(fields, &Field{Type: "unknown"}) continue } if f.Tag == nil { log.Fatalf("field %q is missing a tag", name) } fieldType := formatFieldType(f.Type) if alias, ok := aliases[fieldType]; ok { fieldType = alias.fieldType } fieldTypeRef := getFieldType(f.Type) if alias, ok := aliases[fieldTypeRef]; ok { fieldTypeRef = alias.fieldTypeRef } tag := reflect.StructTag(strings.Trim(f.Tag.Value, "`")) yamlTag := tag.Get("yaml") yamlTag = strings.Split(yamlTag, ",")[0] if yamlTag == "" { log.Fatalf("field %q is missing a `yaml` tag or name in it", name) } text := parseComment([]byte(f.Doc.Text())) field := &Field{ Name: name, Tag: yamlTag, Type: fieldType, TypeRef: fieldTypeRef, Text: text, } if f.Comment != nil { field.Note = escape(f.Comment.Text()) } fields = append(fields, field) } return fields } func renderDoc(doc *Doc, dest string) { t := template.Must(template.New("docfile.tpl").Parse(tpl)) buf := bytes.Buffer{} err := t.Execute(&buf, doc) if err != nil { log.Fatalf("failed to render template: %v", err) } formatted, err := format.Source(buf.Bytes(), format.Options{}) if err != nil { log.Printf("data: %s", buf.Bytes()) log.Fatalf("failed to format source: %v", err) } out, err := out(dest) if err != nil { log.Fatalf("failed to create output file: %v", err) } defer out.Close() _, err = out.Write(formatted) if err != nil { log.Fatalf("failed to write output file: %v", err) } } func processFiles(inputFiles []string, outputFile, schemaOutputFile, versionTagFile string) { var packageNames []string packageNameToType := map[string]*packageType{} aliases := map[string]aliasType{} for _, inputFile := range inputFiles { abs, err := in(inputFile) if err != nil { log.Fatal(err) } log.Printf("creating package file set: %q", abs) fset := token.NewFileSet() node, err := parser.ParseFile(fset, abs, nil, parser.ParseComments) if err != nil { log.Fatal(err) } packageName := node.Name.Name if _, ok := packageNameToType[packageName]; !ok { packageNameToType[packageName] = &packageType{ name: packageName, file: outputFile, } packageNames = append(packageNames, packageName) } pkg := packageNameToType[packageName] if node.Doc != nil && node.Doc.Text() != "" { pkg.doc = node.Doc.Text() } tokenFile := fset.File(node.Pos()) if tokenFile == nil { log.Fatalf("No token") } log.Printf("parsing file in package %q: %s", packageName, tokenFile.Name()) fileStructs, fileAliases := collectStructs(node) pkg.structs = append(pkg.structs, fileStructs...) maps.Copy(aliases, fileAliases) } slices.Sort(packageNames) docs := xslices.Map(packageNames, func(name string) *Doc { return packageToDoc(packageNameToType[name], aliases) }) if schemaOutputFile != "" { renderSchema(docs, schemaOutputFile, versionTagFile) } if outputFile == "" { return } if len(docs) != 1 { log.Fatalf("expected exactly one package to generate docs, got %d", len(docs)) } renderDoc(docs[0], outputFile) } func packageToDoc(pkg *packageType, aliases map[string]aliasType) *Doc { if len(pkg.structs) == 0 { log.Fatalf("failed to find types that could be documented in %v (package %v)", pkg.file, pkg.name) } doc := &Doc{ Package: pkg.name, Structs: []*Struct{}, } extraExamples := map[string][]*Example{} backReferences := map[string][]Appearance{} for _, s := range pkg.structs { log.Printf("generating docs for type: %q", s.name) fields := collectFields(s, aliases) s := &Struct{ Name: s.name, Text: s.text, Fields: fields, } for _, field := range fields { if field.TypeRef == "" { continue } if field.Text != nil && len(field.Text.Examples) > 0 { extraExamples[field.TypeRef] = append(extraExamples[field.TypeRef], field.Text.Examples...) } backReferences[field.TypeRef] = append(backReferences[field.TypeRef], Appearance{ Struct: s, FieldName: field.Tag, }) } doc.Structs = append(doc.Structs, s) } for _, s := range doc.Structs { if s.Text.Alias != "" { s.Text.Description = strings.ReplaceAll(s.Text.Description, s.Name, s.Text.Alias) s.Text.Comment = strings.ReplaceAll(s.Text.Comment, s.Name, s.Text.Alias) } if extra, ok := extraExamples[s.Name]; ok { s.Text.Examples = append(s.Text.Examples, extra...) } if ref, ok := backReferences[s.Name]; ok { s.AppearsIn = append(s.AppearsIn, ref...) } } doc.Package = pkg.name doc.Name = doc.Package doc.Header = escape(pkg.doc) doc.File = pkg.file return doc } func sourcesWithJSONSchema(dir string) []string { var sources []string if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") { return nil } fileBytes, err := os.ReadFile(path) if err != nil { return err } if strings.Contains(string(fileBytes), "//docgen:jsonschema") { sources = append(sources, path) } return nil }); err != nil { log.Fatalf("failed to walk directory %q: %v", dir, err) } return sources } func determineFiles(generateSchemaFromDir string, args []string) []string { if generateSchemaFromDir != "" { if len(args) > 0 { log.Fatalf("cannot specify both -generate-schema-from-dir and input files as args") } files := sourcesWithJSONSchema(generateSchemaFromDir) if len(files) == 0 { log.Fatalf("no Go files annotated with //docgen:jsonschema found in %q", generateSchemaFromDir) } return files } if len(args) == 0 { log.Fatalf("no input files") } return args } func main() { outputFile := flag.String("output", "", "output file name") jsonSchemaOutputFile := flag.String("json-schema-output", "", "output file name for json schema") versionTagFile := flag.String("version-tag-file", "", "file name for version tag") generateSchemaFromDir := flag.String("generate-schema-from-dir", "", "generate a JSON schema by recursively parsing the sources in the specified directory") flag.Parse() files := determineFiles(*generateSchemaFromDir, flag.Args()) processFiles(files, *outputFile, *jsonSchemaOutputFile, *versionTagFile) } ================================================ FILE: tools/docgen/main_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "path/filepath" "testing" ) // This test exists mainly for easier debugging with debugger. func TestProcessFile(t *testing.T) { inputFile := filepath.Join("..", "..", "pkg", "machinery", "config", "types", "v1alpha1", "v1alpha1_types.go") outputFile := filepath.Join(t.TempDir(), "out.go") schemaOutputFile := filepath.Join(t.TempDir(), "out.schema.json") versionTagFile := filepath.Join("..", "..", "pkg", "machinery", "gendata", "data", "tag") processFiles([]string{inputFile}, outputFile, schemaOutputFile, versionTagFile) } ================================================ FILE: tools/docgen/schema.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "encoding/json" "fmt" "log" "os" "path/filepath" "slices" "strings" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/invopop/jsonschema" "github.com/microcosm-cc/bluemonday" validatejsonschema "github.com/santhosh-tekuri/jsonschema/v6" orderedmap "github.com/wk8/go-ordered-map/v2" ) const ConfigSchemaURLFormat = "https://talos.dev/%s/schemas/%s" // SchemaWrapper wraps jsonschema.Schema to provide correct YAML unmarshalling using its internal JSON marshaller. type SchemaWrapper struct { jsonschema.Schema } // UnmarshalYAML unmarshals the schema from YAML. // // This converts the YAML that was read from the comments to JSON, // then uses the custom JSON unmarshaler of the wrapped Schema. func (t *SchemaWrapper) UnmarshalYAML(unmarshal func(any) error) error { var data map[string]any err := unmarshal(&data) if err != nil { return err } jsonBytes, err := json.Marshal(data) if err != nil { return err } return t.UnmarshalJSON(jsonBytes) } type SchemaTypeInfo struct { typeName string ref string } type SchemaDefinitionInfo struct { typeInfo SchemaTypeInfo arrayItemsTypeInfo *SchemaTypeInfo mapValueTypeInfo *SchemaTypeInfo enumValues []any } func goTypeToTypeInfo(pkg, goType string) *SchemaTypeInfo { switch goType { case "string": return &SchemaTypeInfo{typeName: "string"} case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64": return &SchemaTypeInfo{typeName: "integer"} case "bool": return &SchemaTypeInfo{typeName: "boolean"} default: return &SchemaTypeInfo{ref: "#/$defs/" + pkg + "." + goType} } } func fieldToDefinitionInfo(pkg string, field *Field) SchemaDefinitionInfo { goType := field.Type if field.Text != nil { // skip enumifying boolean fields so that we don't pick up the "on", "off", "yes", "no" from the values if goType != "bool" && field.Text.Values != nil { enumValues := make([]any, 0, len(field.Text.Values)) for _, val := range field.Text.Values { enumValues = append(enumValues, val) } return SchemaDefinitionInfo{enumValues: enumValues} } } if strings.HasPrefix(goType, "[]") { return SchemaDefinitionInfo{ typeInfo: SchemaTypeInfo{typeName: "array"}, arrayItemsTypeInfo: goTypeToTypeInfo(pkg, strings.TrimPrefix(goType, "[]")), } } if strings.HasPrefix(goType, "map[string]") { return SchemaDefinitionInfo{ typeInfo: SchemaTypeInfo{typeName: "object"}, mapValueTypeInfo: goTypeToTypeInfo(pkg, strings.TrimPrefix(goType, "map[string]")), } } return SchemaDefinitionInfo{ typeInfo: *goTypeToTypeInfo(pkg, goType), } } func typeInfoToSchema(typeInfo *SchemaTypeInfo) *jsonschema.Schema { schema := jsonschema.Schema{} if typeInfo.typeName != "" { schema.Type = typeInfo.typeName } if typeInfo.ref != "" { schema.Ref = typeInfo.ref } return &schema } func fieldToSchema(pkg string, field *Field) *jsonschema.Schema { schema := jsonschema.Schema{} if field.Text != nil { // if there is an explicit schema, use it if field.Text.Schema != nil { schema = field.Text.Schema.Schema } // if no title is provided on the explicit schema, grab it from the comment if schema.Title == "" { schema.Title = strings.ReplaceAll(field.Tag, "\\n", "\n") } populateDescriptionFields(field.Text.Description, &schema) // if an explicit schema was provided, return it if field.Text.Schema != nil { return &schema } } // schema was not explicitly provided, generate it from the comment info := fieldToDefinitionInfo(pkg, field) if info.typeInfo.ref != "" { schema.Ref = info.typeInfo.ref } if info.enumValues != nil { schema.Enum = info.enumValues } if info.typeInfo.typeName != "" { schema.Type = info.typeInfo.typeName } if info.arrayItemsTypeInfo != nil { schema.Items = typeInfoToSchema(info.arrayItemsTypeInfo) } if info.mapValueTypeInfo != nil { schema.PatternProperties = map[string]*jsonschema.Schema{ ".*": typeInfoToSchema(info.mapValueTypeInfo), } } return &schema } func populateDescriptionFields(description string, schema *jsonschema.Schema) { if schema.Extras == nil { schema.Extras = make(map[string]any) } markdownDescription := normalizeDescription(description) htmlFlags := html.CommonFlags | html.HrefTargetBlank opts := html.RendererOptions{Flags: htmlFlags} renderer := html.NewRenderer(opts) htmlDescription := string(markdown.ToHTML([]byte(markdownDescription), nil, renderer)) policy := bluemonday.StrictPolicy() plaintextDescription := policy.Sanitize(htmlDescription) // set description if schema.Description == "" { schema.Description = plaintextDescription } // set markdownDescription for vscode/monaco editor if schema.Extras["markdownDescription"] == nil { schema.Extras["markdownDescription"] = markdownDescription } // set htmlDescription for Jetbrains IDEs if schema.Extras["x-intellij-html-description"] == nil { schema.Extras["x-intellij-html-description"] = htmlDescription } } func structToSchema(pkg string, st *Struct, allStructs []*Struct) *jsonschema.Schema { schema := jsonschema.Schema{ Type: "object", AdditionalProperties: jsonschema.FalseSchema, } var requiredFields []string properties := orderedmap.New[string, *jsonschema.Schema]() if st.Text != nil && st.Text.SchemaMeta != "" { parts := strings.Split(st.Text.SchemaMeta, "/") if len(parts) != 2 { log.Fatalf("invalid schema meta: %s", st.Text.SchemaMeta) } apiVersionVal := parts[0] kindVal := parts[1] apiVersionSchema := &jsonschema.Schema{ Title: "apiVersion", Enum: []any{apiVersionVal}, } kindSchema := &jsonschema.Schema{ Title: "kind", Enum: []any{kindVal}, } populateDescriptionFields("apiVersion is the API version of the resource.", apiVersionSchema) populateDescriptionFields("kind is the kind of the resource.", kindSchema) properties.Set("apiVersion", apiVersionSchema) properties.Set("kind", kindSchema) requiredFields = append(requiredFields, "apiVersion", "kind") } for _, field := range st.Fields { if field.Inline { var inlinedStruct *Struct for _, otherStruct := range allStructs { if otherStruct.Name == field.Type { inlinedStruct = otherStruct break } } if inlinedStruct != nil { for _, inlineField := range inlinedStruct.Fields { if inlineField.Tag == "" { // skip unknown/untagged field continue } if inlineField.Text != nil && inlineField.Text.SchemaRequired { requiredFields = append(requiredFields, inlineField.Tag) } properties.Set(inlineField.Tag, fieldToSchema(pkg, inlineField)) } } } if field.Tag == "" { // skip unknown/untagged field continue } if field.Text != nil && field.Text.SchemaRequired { requiredFields = append(requiredFields, field.Tag) } properties.Set(field.Tag, fieldToSchema(pkg, field)) } slices.Sort(requiredFields) schema.Properties = properties schema.Required = requiredFields if st.Text.Description != "" { schema.Description = st.Text.Description } return &schema } func docsToSchema(docs []*Doc, schemaURL string) *jsonschema.Schema { schema := jsonschema.Schema{ Version: jsonschema.Version, ID: jsonschema.ID(schemaURL), Definitions: make(jsonschema.Definitions), } for _, doc := range docs { for _, docStruct := range doc.Structs { name := doc.Package + "." + docStruct.Name if docStruct.Text != nil && docStruct.Text.SchemaRoot { schema.OneOf = append(schema.OneOf, &jsonschema.Schema{ Ref: "#/$defs/" + name, }) } schema.Definitions[name] = structToSchema(doc.Package, docStruct, doc.Structs) } } return &schema } func renderSchema(docs []*Doc, destinationFile, versionTagFile string) { version := readMajorMinorVersion(versionTagFile) schemaFileName := filepath.Base(destinationFile) schemaURL := fmt.Sprintf(ConfigSchemaURLFormat, version, schemaFileName) schema := docsToSchema(docs, schemaURL) marshaled, err := json.MarshalIndent(schema, "", " ") if err != nil { log.Fatalf("failed to marshal schema: %v", err) } validateSchema(string(marshaled), schemaURL) destDir := filepath.Dir(destinationFile) if err = os.MkdirAll(destDir, 0o755); err != nil { log.Fatalf("failed to create destination directory %q: %v", destDir, err) } err = os.WriteFile(destinationFile, marshaled, 0o644) if err != nil { log.Fatalf("failed to write schema to %s: %v", destinationFile, err) } } // validateSchema validates the schema itself by compiling it. func validateSchema(schema, schemaURL string) { schemaJSON, err := validatejsonschema.UnmarshalJSON(strings.NewReader(schema)) if err != nil { log.Fatalf("failed to unmarshal schema JSON: %v", err) } compiler := validatejsonschema.NewCompiler() if err := compiler.AddResource(schemaURL, schemaJSON); err != nil { log.Fatalf("failed to add schema resource: %v", err) } if _, err := compiler.Compile(schemaURL); err != nil { log.Fatalf("failed to compile schema: %v", err) } } func normalizeDescription(description string) string { description = strings.ReplaceAll(description, `\n`, "\n") description = strings.ReplaceAll(description, `\"`, `"`) description = strings.TrimSpace(description) return description } func readMajorMinorVersion(filePath string) string { fileBytes, err := os.ReadFile(filePath) if err != nil { log.Fatalf("failed to read version file: %v", err) } version := string(fileBytes) versionParts := strings.Split(version, ".") if len(versionParts) < 2 { log.Fatalf("unexpected version in version file: %s", version) } return versionParts[0] + "." + versionParts[1] } ================================================ FILE: tools/go.mod ================================================ module github.com/siderolabs/talos/tools // upgrading all tools: go get tool go 1.26.0 tool ( github.com/aarzilli/whydeadcode github.com/anchore/grype/cmd/grype github.com/anchore/syft/cmd/syft github.com/bufbuild/buf/cmd/buf github.com/dmarkham/enumer github.com/golangci/golangci-lint/v2/cmd/golangci-lint github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc github.com/siderolabs/deep-copy github.com/siderolabs/importvet/cmd/importvet github.com/siderolabs/talos/tools/docgen github.com/siderolabs/talos/tools/gotagsrewrite github.com/siderolabs/talos/tools/structprotogen golang.org/x/tools/cmd/goimports golang.org/x/tools/cmd/stringer golang.org/x/vuln/cmd/govulncheck google.golang.org/grpc/cmd/protoc-gen-go-grpc google.golang.org/protobuf/cmd/protoc-gen-go k8s.io/code-generator/cmd/deepcopy-gen mvdan.cc/gofumpt ) // local modules replace ( github.com/siderolabs/talos/tools/docgen => ./docgen github.com/siderolabs/talos/tools/gotagsrewrite => ./gotagsrewrite github.com/siderolabs/talos/tools/structprotogen => ./structprotogen ) // Changes from https://github.com/anchore/syft/pull/3932 for deterministic SBOM replace github.com/anchore/syft => github.com/dsseng/syft v1.42.1-0.20260219105507-8cf142cbcb79 require ( 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20250718181942-e35f9b667443.1 // indirect buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20260126144947-819582968857.2 // indirect buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260126144947-819582968857.1 // indirect buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1 // indirect buf.build/go/app v0.2.0 // indirect buf.build/go/bufplugin v0.9.0 // indirect buf.build/go/bufprivateusage v0.1.0 // indirect buf.build/go/interrupt v1.1.0 // indirect buf.build/go/protovalidate v1.1.3 // indirect buf.build/go/protoyaml v0.6.0 // indirect buf.build/go/spdx v0.2.0 // indirect buf.build/go/standard v0.1.0 // indirect cel.dev/expr v0.25.1 // indirect cloud.google.com/go v0.123.0 // indirect cloud.google.com/go/auth v0.18.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect cloud.google.com/go/iam v1.5.3 // indirect cloud.google.com/go/monitoring v1.24.3 // indirect cloud.google.com/go/storage v1.60.0 // indirect codeberg.org/chavacava/garif v0.2.0 // indirect codeberg.org/polyfloyd/go-errorlint v1.9.0 // indirect connectrpc.com/connect v1.19.1 // indirect connectrpc.com/otelconnect v0.9.0 // indirect cyphar.com/go-pathrs v0.2.3 // indirect dario.cat/mergo v1.0.2 // indirect dev.gaijin.team/go/exhaustruct/v4 v4.0.0 // indirect dev.gaijin.team/go/golib v0.8.1 // indirect github.com/4meepo/tagalign v1.4.3 // indirect github.com/Abirdcfly/dupword v0.1.7 // indirect github.com/AdminBenni/iota-mixing v1.0.0 // indirect github.com/AlwxSin/noinlineerr v1.0.5 // indirect github.com/Antonboom/errname v1.1.1 // indirect github.com/Antonboom/nilnil v1.1.1 // indirect github.com/Antonboom/testifylint v1.6.4 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/CycloneDX/cyclonedx-go v0.10.0 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/Djarvur/go-err113 v0.1.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect github.com/Intevation/gval v1.3.0 // indirect github.com/Intevation/jsonpath v0.2.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.14.0-rc.1 // indirect github.com/MirrexOne/unqueryvet v1.5.4 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect github.com/STARRY-S/zip v0.2.3 // indirect github.com/aarzilli/whydeadcode v0.0.0-20260303092945-8d908f77de3a // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/adrg/xdg v0.5.3 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/alecthomas/chroma/v2 v2.23.1 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect github.com/alexkohler/nakedret/v2 v2.0.6 // indirect github.com/alexkohler/prealloc v1.1.0 // indirect github.com/alfatraining/structtag v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/alingse/nilnesserr v0.2.0 // indirect github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf // indirect github.com/anchore/clio v0.0.0-20260217161816-1b390cf973f8 // indirect github.com/anchore/fangs v0.0.0-20260205223337-3833021a87e1 // indirect github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c // indirect github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d // indirect github.com/anchore/go-logger v0.0.0-20260217144723-3bb369b8046c // indirect github.com/anchore/go-lzo v0.1.0 // indirect github.com/anchore/go-macholibre v0.0.0-20260203040931-2d1882573092 // indirect github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec // indirect github.com/anchore/go-struct-converter v0.1.0 // indirect github.com/anchore/go-sync v0.0.0-20260122203928-582959aeb913 // indirect github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 // indirect github.com/anchore/grype v0.109.1 // indirect github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 // indirect github.com/anchore/stereoscope v0.1.21 // indirect github.com/anchore/syft v1.42.2 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aquasecurity/go-pep440-version v0.0.1 // indirect github.com/aquasecurity/go-version v0.0.1 // indirect github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect github.com/ashanbrown/forbidigo/v2 v2.3.0 // indirect github.com/ashanbrown/makezero/v2 v2.1.0 // indirect github.com/aws/aws-sdk-go-v2 v1.41.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect github.com/aws/aws-sdk-go-v2/config v1.32.10 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.10 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 // indirect github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 // indirect github.com/aws/smithy-go v1.24.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bitnami/go-version v0.0.0-20251209151629-b46c9d8f1f27 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bmatcuk/doublestar/v2 v2.0.4 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/sevenzip v1.6.1 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/bombsimon/wsl/v4 v4.7.0 // indirect github.com/bombsimon/wsl/v5 v5.6.0 // indirect github.com/breml/bidichk v0.3.3 // indirect github.com/breml/errchkjson v0.4.1 // indirect github.com/bufbuild/buf v1.66.1 // indirect github.com/bufbuild/protocompile v0.14.2-0.20260306221011-519528254156 // indirect github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/butuzov/ireturn v0.4.0 // indirect github.com/butuzov/mirror v1.3.0 // indirect github.com/catenacyber/perfsprint v0.10.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.4 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.11 // indirect github.com/charmbracelet/bubbles v1.0.0 // indirect github.com/charmbracelet/bubbletea v1.3.10 // indirect github.com/charmbracelet/colorprofile v0.4.2 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/lipgloss v1.1.0 // indirect github.com/charmbracelet/x/ansi v0.11.6 // indirect github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/term v0.2.2 // indirect github.com/ckaznocha/intrange v0.3.1 // indirect github.com/cli/browser v1.3.0 // indirect github.com/clipperhouse/displaywidth v0.10.0 // indirect github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/containerd/cgroups/v3 v3.1.3 // indirect github.com/containerd/containerd/api v1.10.0 // indirect github.com/containerd/containerd/v2 v2.2.1 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v1.0.0-rc.2 // indirect github.com/containerd/plugin v1.0.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/daixiang0/gci v0.13.7 // indirect github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dghubble/trie v0.1.0 // indirect github.com/diskfs/go-diskfs v1.7.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/dmarkham/enumer v1.6.3 // indirect github.com/docker/cli v29.3.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker v28.5.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.5 // indirect github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/elliotchance/phpserialize v1.4.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/facebookincubator/nvdtools v0.1.5 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/firefart/nonamedreturns v1.0.6 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/ghostiam/protogetter v0.3.20 // indirect github.com/github/go-spdx/v2 v2.4.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/glebarez/sqlite v1.11.0 // indirect github.com/go-critic/go-critic v0.14.3 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.8.0 // indirect github.com/go-git/go-git/v5 v5.17.0 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.2.0 // indirect github.com/go-toolsmith/astfmt v1.1.0 // indirect github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-yaml v1.19.2 // indirect github.com/gocsaf/csaf/v3 v3.5.1 // indirect github.com/godoc-lint/godoc-lint v0.11.2 // indirect github.com/gofrs/flock v0.13.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/gohugoio/hashstructure v0.6.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golangci/asciicheck v0.5.0 // indirect github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect github.com/golangci/go-printf-func-name v0.1.1 // indirect github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect github.com/golangci/golangci-lint/v2 v2.11.3 // indirect github.com/golangci/golines v0.15.0 // indirect github.com/golangci/misspell v0.8.0 // indirect github.com/golangci/plugin-module-register v0.1.2 // indirect github.com/golangci/revgrep v0.8.0 // indirect github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e // indirect github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab // indirect github.com/google/cel-go v0.27.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry v0.21.2 // indirect github.com/google/licensecheck v0.3.1 // indirect github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.12 // indirect github.com/googleapis/gax-go/v2 v2.17.0 // indirect github.com/gookit/color v1.6.0 // indirect github.com/gordonklaus/ineffassign v0.2.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.2 // indirect github.com/gpustack/gguf-parser-go v0.24.0 // indirect github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b // indirect github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.8.4 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl/v2 v2.24.0 // indirect github.com/henvic/httpretty v0.1.4 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/invopop/jsonschema v0.13.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jdx/go-netrc v1.0.0 // indirect github.com/jedib0t/go-pretty/v6 v6.7.8 // indirect github.com/jgautheron/goconst v1.8.2 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/jjti/go-spancheck v0.6.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.2 // indirect github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect github.com/kisielk/errcheck v1.10.0 // indirect github.com/kkHAIKE/contextcheck v1.1.6 // indirect github.com/klauspost/compress v1.18.4 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 // indirect github.com/kulti/thelper v0.7.1 // indirect github.com/kunwardeep/paralleltest v1.0.15 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect github.com/ldez/exptostd v0.4.5 // indirect github.com/ldez/gomoddirectives v0.8.0 // indirect github.com/ldez/grignotin v0.10.1 // indirect github.com/ldez/structtags v0.6.1 // indirect github.com/ldez/tagliatelle v0.7.2 // indirect github.com/ldez/usetesting v0.5.0 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/macabu/inamedparam v0.2.0 // indirect github.com/mailru/easyjson v0.9.1 // indirect github.com/manuelarte/embeddedstructfieldcheck v0.4.0 // indirect github.com/manuelarte/funcorder v0.5.0 // indirect github.com/maratori/testableexamples v1.0.1 // indirect github.com/maratori/testpackage v1.1.2 // indirect github.com/masahiro331/go-mvn-version v0.0.0-20260119054159-d21fcd2e7de1 // indirect github.com/matoous/godox v1.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.20 // indirect github.com/mgechev/revive v1.15.0 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mholt/archives v0.1.5 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/minlz v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/moby/api v1.53.0 // indirect github.com/moby/moby/client v0.2.2 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/moricho/tparallel v0.3.2 // indirect github.com/morikuni/aec v1.1.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mwitkow/go-proto-validators v0.3.2 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 // indirect github.com/nunnatsa/ginkgolinter v0.23.0 // indirect github.com/nwaples/rardecode/v2 v2.2.2 // indirect github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect github.com/olekukonko/errors v1.2.0 // indirect github.com/olekukonko/ll v0.1.6 // indirect github.com/olekukonko/tablewriter v1.1.3 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.3.0 // indirect github.com/opencontainers/selinux v1.13.1 // indirect github.com/openvex/go-vex v0.2.7 // indirect github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 // indirect github.com/package-url/packageurl-go v0.1.3 // indirect github.com/pandatix/go-cvss v0.6.2 // indirect github.com/pascaldekloe/name v1.0.1 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 // indirect github.com/pierrec/lz4/v4 v4.1.25 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect github.com/pkg/xattr v0.4.12 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.19.2 // indirect github.com/pseudomuto/protoc-gen-doc v1.5.1 // indirect github.com/pseudomuto/protokit v0.2.1 // indirect github.com/quasilyte/go-ruleguard v0.4.5 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.23 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/raeperd/recvcheck v0.2.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect github.com/ryancurrah/gomodguard v1.4.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect github.com/sassoftware/go-rpmutils v0.4.0 // indirect github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 // indirect github.com/segmentio/asm v1.2.1 // indirect github.com/segmentio/encoding v0.5.3 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/siderolabs/deep-copy v0.5.8 // indirect github.com/siderolabs/gen v0.8.6 // indirect github.com/siderolabs/importvet v0.2.0 // indirect github.com/siderolabs/talos/tools/docgen v0.0.0-20260313141749-6bb5cf57a28c // indirect github.com/siderolabs/talos/tools/gotagsrewrite v0.0.0-20260313141749-6bb5cf57a28c // indirect github.com/siderolabs/talos/tools/structprotogen v0.0.0-20260313141749-6bb5cf57a28c // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/sivchari/containedctx v1.0.3 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/smallnest/ringbuffer v0.1.1 // indirect github.com/sonatard/noctx v0.5.0 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb // indirect github.com/spdx/tools-golang v0.5.7 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/spf13/viper v1.21.0 // indirect github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylabs/sif/v2 v2.23.0 // indirect github.com/sylabs/squashfs v1.0.6 // indirect github.com/tetafro/godot v1.5.4 // indirect github.com/tetratelabs/wazero v1.11.0 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/tidwall/btree v1.8.1 // indirect github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 // indirect github.com/timonwong/loggercheck v0.11.0 // indirect github.com/tomarrell/wrapcheck/v2 v2.12.0 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/ultraware/funlen v0.2.0 // indirect github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.1 // indirect github.com/uudashr/iface v1.4.1 // indirect github.com/vbatts/go-mtree v0.7.0 // indirect github.com/vbatts/tar-split v0.12.2 // indirect github.com/vifraa/gopom v1.0.0 // indirect github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 // indirect github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b // indirect github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect github.com/zclconf/go-cty v1.17.0 // indirect github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.14.0 // indirect go-simpler.org/sloglint v0.11.1 // indirect go.augendre.info/arangolint v0.4.0 // indirect go.augendre.info/fatcontext v0.9.0 // indirect go.etcd.io/bbolt v1.4.3 // indirect go.lsp.dev/jsonrpc2 v0.10.0 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect go.lsp.dev/protocol v0.12.0 // indirect go.lsp.dev/uri v0.3.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.40.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect go.opentelemetry.io/otel v1.42.0 // indirect go.opentelemetry.io/otel/metric v1.42.0 // indirect go.opentelemetry.io/otel/sdk v1.42.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect go.opentelemetry.io/otel/trace v1.42.0 // indirect go.uber.org/mock v0.6.0 // indirect go.uber.org/multierr v1.11.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 go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect go4.org v0.0.0-20260112195520-a5071408f32f // indirect golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/exp/typeparams v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/net v0.52.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sync v0.20.0 // indirect golang.org/x/sys v0.42.0 // indirect golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c // indirect golang.org/x/term v0.41.0 // indirect golang.org/x/text v0.35.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/tools v0.43.0 // indirect golang.org/x/vuln v1.1.4 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.17.0 // indirect google.golang.org/api v0.267.0 // indirect google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/grpc v1.79.1 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/typ.v4 v4.4.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/gorm v1.31.1 // indirect honnef.co/go/tools v0.7.0 // indirect k8s.io/code-generator v0.35.2 // indirect k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b // indirect k8s.io/klog/v2 v2.130.1 // indirect modernc.org/libc v1.68.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.46.1 // indirect mvdan.cc/gofumpt v0.9.2 // indirect mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect mvdan.cc/xurls/v2 v2.6.0 // indirect pluginrpc.com/pluginrpc v0.5.0 // indirect ) ================================================ FILE: tools/go.sum ================================================ 4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= 4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20250718181942-e35f9b667443.1 h1:zQ9C3e6FtwSZUFuKAQfpIKGFk5ZuRoGt5g35Bix55sI= buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.11-20250718181942-e35f9b667443.1/go.mod h1:1Znr6gmYBhbxWUPRrrVnSLXQsz8bvFVw1HHJq2bI3VQ= buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.11-20250109164928-1da0de137947.1 h1:HwzzCRS4ZrEm1++rzSDxHnO0DOjiT1b8I/24e8a4exY= buf.build/gen/go/bufbuild/protodescriptor/protocolbuffers/go v1.36.11-20250109164928-1da0de137947.1/go.mod h1:8PRKXhgNes29Tjrnv8KdZzg3I1QceOkzibW1QK7EXv0= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6ASge9uRcHy0jtqPd+fM35LmsQ= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20260126144947-819582968857.2 h1:XPrWCd9ydEo5Ofv1aNJVJaxndMXLQjRO9vVzsJG3jL8= buf.build/gen/go/bufbuild/registry/connectrpc/go v1.19.1-20260126144947-819582968857.2/go.mod h1:mpsjeEaxOYPIJV2cz4IagLghZufRvx+NPVtInjEeoQ8= buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260126144947-819582968857.1 h1:Yreby6Ypa58wdQUEm9Fnc5g8n/jP487Dq3aK5yBYwfk= buf.build/gen/go/bufbuild/registry/protocolbuffers/go v1.36.11-20260126144947-819582968857.1/go.mod h1:1JJi9jvOqRxSMa+JxiZSm57doB+db/1WYCIa2lHfc40= buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1 h1:iGPvEJltOXUMANWf0zajcRcbiOXLD90ZwPUFvbcuv6Q= buf.build/gen/go/pluginrpc/pluginrpc/protocolbuffers/go v1.36.11-20241007202033-cf42259fcbfc.1/go.mod h1:nWVKKRA29zdt4uvkjka3i/y4mkrswyWwiu0TbdX0zts= buf.build/go/app v0.2.0 h1:NYaH13A+RzPb7M5vO8uZYZ2maBZI5+MS9A9tQm66fy8= buf.build/go/app v0.2.0/go.mod h1:0XVOYemubVbxNXVY0DnsVgWeGkcbbAvjDa1fmhBC+Wo= buf.build/go/bufplugin v0.9.0 h1:ktZJNP3If7ldcWVqh46XKeiYJVPxHQxCfjzVQDzZ/lo= buf.build/go/bufplugin v0.9.0/go.mod h1:Z0CxA3sKQ6EPz/Os4kJJneeRO6CjPeidtP1ABh5jPPY= buf.build/go/bufprivateusage v0.1.0 h1:SzCoCcmzS3zyXHEXHeSQhGI7OTkgtljoknLzsUz9Gg4= buf.build/go/bufprivateusage v0.1.0/go.mod h1:GlCCJ3VVF7EqqU0CoRmo1FzAwwaKymEWSr+ty69xU5w= buf.build/go/interrupt v1.1.0 h1:olBuhgv9Sav4/9pkSLoxgiOsZDgM5VhRhvRpn3DL0lE= buf.build/go/interrupt v1.1.0/go.mod h1:ql56nXPG1oHlvZa6efNC7SKAQ/tUjS6z0mhJl0gyeRM= buf.build/go/protovalidate v1.1.3 h1:m2GVEgQWd7rk+vIoAZ+f0ygGjvQTuqPQapBBdcpWVPE= buf.build/go/protovalidate v1.1.3/go.mod h1:9XIuohWz+kj+9JVn3WQneHA5LZP50mjvneZMnbLkiIE= buf.build/go/protoyaml v0.6.0 h1:Nzz1lvcXF8YgNZXk+voPPwdU8FjDPTUV4ndNTXN0n2w= buf.build/go/protoyaml v0.6.0/go.mod h1:RgUOsBu/GYKLDSIRgQXniXbNgFlGEZnQpRAUdLAFV2Q= buf.build/go/spdx v0.2.0 h1:IItqM0/cMxvFJJumcBuP8NrsIzMs/UYjp/6WSpq8LTw= buf.build/go/spdx v0.2.0/go.mod h1:bXdwQFem9Si3nsbNy8aJKGPoaPi5DKwdeEp5/ArZ6w8= buf.build/go/standard v0.1.0 h1:g98T9IyvAl0vS3Pq8iVk6Cvj2ZiFvoUJRtfyGa0120U= buf.build/go/standard v0.1.0/go.mod h1:PiqpHz/7ZFq+kqvYhc/SK3lxFIB9N/aiH2CFC2JHIQg= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM= cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM= cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc= cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA= cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8= cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.60.0 h1:oBfZrSOCimggVNz9Y/bXY35uUcts7OViubeddTTVzQ8= cloud.google.com/go/storage v1.60.0/go.mod h1:q+5196hXfejkctrnx+VYU8RKQr/L3c0cBIlrjmiAKE0= cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U= cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= codeberg.org/chavacava/garif v0.2.0 h1:F0tVjhYbuOCnvNcU3YSpO6b3Waw6Bimy4K0mM8y6MfY= codeberg.org/chavacava/garif v0.2.0/go.mod h1:P2BPbVbT4QcvLZrORc2T29szK3xEOlnl0GiPTJmEqBQ= codeberg.org/polyfloyd/go-errorlint v1.9.0 h1:VkdEEmA1VBpH6ecQoMR4LdphVI3fA4RrCh2an7YmodI= codeberg.org/polyfloyd/go-errorlint v1.9.0/go.mod h1:GPRRu2LzVijNn4YkrZYJfatQIdS+TrcK8rL5Xs24qw8= connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= connectrpc.com/otelconnect v0.9.0 h1:NggB3pzRC3pukQWaYbRHJulxuXvmCKCKkQ9hbrHAWoA= connectrpc.com/otelconnect v0.9.0/go.mod h1:AEkVLjCPXra+ObGFCOClcJkNjS7zPaQSqvO0lCyjfZc= cyphar.com/go-pathrs v0.2.3 h1:0pH8gep37wB0BgaXrEaN1OtZhUMeS7VvaejSr6i822o= cyphar.com/go-pathrs v0.2.3/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dev.gaijin.team/go/exhaustruct/v4 v4.0.0 h1:873r7aNneqoBB3IaFIzhvt2RFYTuHgmMjoKfwODoI1Y= dev.gaijin.team/go/exhaustruct/v4 v4.0.0/go.mod h1:aZ/k2o4Y05aMJtiux15x8iXaumE88YdiB0Ai4fXOzPI= dev.gaijin.team/go/golib v0.8.1 h1:JYju4x9BSo+QD/AYeHULVDcvEhiFg8wOi6pT0IaZF5E= dev.gaijin.team/go/golib v0.8.1/go.mod h1:c5fu7t1RSGMxSQgcUYO1sODbzsYnOCXJLmHeNG1Eb+0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/4meepo/tagalign v1.4.3 h1:Bnu7jGWwbfpAie2vyl63Zup5KuRv21olsPIha53BJr8= github.com/4meepo/tagalign v1.4.3/go.mod h1:00WwRjiuSbrRJnSVeGWPLp2epS5Q/l4UEy0apLLS37c= github.com/Abirdcfly/dupword v0.1.7 h1:2j8sInznrje4I0CMisSL6ipEBkeJUJAmK1/lfoNGWrQ= github.com/Abirdcfly/dupword v0.1.7/go.mod h1:K0DkBeOebJ4VyOICFdppB23Q0YMOgVafM0zYW0n9lF4= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdminBenni/iota-mixing v1.0.0 h1:Os6lpjG2dp/AE5fYBPAA1zfa2qMdCAWwPMCgpwKq7wo= github.com/AdminBenni/iota-mixing v1.0.0/go.mod h1:i4+tpAaB+qMVIV9OK3m4/DAynOd5bQFaOu+2AhtBCNY= github.com/AlwxSin/noinlineerr v1.0.5 h1:RUjt63wk1AYWTXtVXbSqemlbVTb23JOSRiNsshj7TbY= github.com/AlwxSin/noinlineerr v1.0.5/go.mod h1:+QgkkoYrMH7RHvcdxdlI7vYYEdgeoFOVjU9sUhw/rQc= github.com/Antonboom/errname v1.1.1 h1:bllB7mlIbTVzO9jmSWVWLjxTEbGBVQ1Ff/ClQgtPw9Q= github.com/Antonboom/errname v1.1.1/go.mod h1:gjhe24xoxXp0ScLtHzjiXp0Exi1RFLKJb0bVBtWKCWQ= github.com/Antonboom/nilnil v1.1.1 h1:9Mdr6BYd8WHCDngQnNVV0b554xyisFioEKi30sksufQ= github.com/Antonboom/nilnil v1.1.1/go.mod h1:yCyAmSw3doopbOWhJlVci+HuyNRuHJKIv6V2oYQa8II= github.com/Antonboom/testifylint v1.6.4 h1:gs9fUEy+egzxkEbq9P4cpcMB6/G0DYdMeiFS87UiqmQ= github.com/Antonboom/testifylint v1.6.4/go.mod h1:YO33FROXX2OoUfwjz8g+gUxQXio5i9qpVy7nXGbxDD4= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CycloneDX/cyclonedx-go v0.10.0 h1:7xyklU7YD+CUyGzSFIARG18NYLsKVn4QFg04qSsu+7Y= github.com/CycloneDX/cyclonedx-go v0.10.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE= github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Djarvur/go-err113 v0.1.1 h1:eHfopDqXRwAi+YmCUas75ZE0+hoBHJ2GQNLYRSxao4g= github.com/Djarvur/go-err113 v0.1.1/go.mod h1:IaWJdYFLg76t2ihfflPZnM1LIQszWOsFDh2hhhAVF6k= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o= github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A= github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= github.com/MirrexOne/unqueryvet v1.5.4 h1:38QOxShO7JmMWT+eCdDMbcUgGCOeJphVkzzRgyLJgsQ= github.com/MirrexOne/unqueryvet v1.5.4/go.mod h1:fs9Zq6eh1LRIhsDIsxf9PONVUjYdFHdtkHIgZdJnyPU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= github.com/aarzilli/whydeadcode v0.0.0-20260303092945-8d908f77de3a h1:qHtQIeFjOta2EylF59J2UJxZrYT0dCprYXe399PO8xw= github.com/aarzilli/whydeadcode v0.0.0-20260303092945-8d908f77de3a/go.mod h1:rHV6WbQI4oOtjbl+J0d9UYVs8eBU9bqZIp9UB5QACiQ= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY= github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o= github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= github.com/alexkohler/prealloc v1.1.0 h1:cKGRBqlXw5iyQGLYhrXrDlcHxugXpTq4tQ5c91wkf8M= github.com/alexkohler/prealloc v1.1.0/go.mod h1:fT39Jge3bQrfA7nPMDngUfvUbQGQeJyGQnR+913SCig= github.com/alfatraining/structtag v1.0.0 h1:2qmcUqNcCoyVJ0up879K614L9PazjBSFruTB0GOFjCc= github.com/alfatraining/structtag v1.0.0/go.mod h1:p3Xi5SwzTi+Ryj64DqjLWz7XurHxbGsq6y3ubePJPus= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf h1:UY7SQkfVVaeGUpPZrJxqmTc8M0ZSWc5ChiKF6I6aL3I= github.com/anchore/bubbly v0.0.0-20250717181826-8a411f9d8cbf/go.mod h1:w8Br1ZKk1Nk82YRSh10pcD7LO7avPyFmNnaY1TRPgs0= github.com/anchore/clio v0.0.0-20260217161816-1b390cf973f8 h1:bMBDwRpDFGH86AGZwvERDPCn3sUl9Ezzjwya6docleI= github.com/anchore/clio v0.0.0-20260217161816-1b390cf973f8/go.mod h1:X9RG7xV3Uam2q050c0XcQv+BHgVS8Phr9BbSpnkcDNc= github.com/anchore/fangs v0.0.0-20260205223337-3833021a87e1 h1:wfmBzsWSaLHKktgyoDugnw3TYjIPOmkdgM8Oe9CvR7o= github.com/anchore/fangs v0.0.0-20260205223337-3833021a87e1/go.mod h1:yYsgDLrzipKlyDAzrjtd35lMcXyZ60b8Ko8vTaMwqoU= github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c h1:eoJXyC0n7DZ4YvySG/ETdYkTar2Due7eH+UmLK6FbrA= github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8= github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc= github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50= github.com/anchore/go-logger v0.0.0-20260217144723-3bb369b8046c h1:AgdlwjTK8Lr+CA8Mq1gg1nxpSDbkLzmHATM8vr3d5do= github.com/anchore/go-logger v0.0.0-20260217144723-3bb369b8046c/go.mod h1:0l63X5VyHqfWQwsNaTmkgXErsfohze3NAFCXFdhSjdQ= github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs= github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk= github.com/anchore/go-macholibre v0.0.0-20260203040931-2d1882573092 h1:Qd2kg6T/hW/KJSG6Y10dAuo+eQ7xU7TCMqegdNcUU34= github.com/anchore/go-macholibre v0.0.0-20260203040931-2d1882573092/go.mod h1:eu0gbwaZ+ocVFJLePdmPPDKU8MboV1MKsUCr36Ckd5s= github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec h1:SjjPMOXTzpuU1ZME4XeoHyek+dry3/C7I8gzaCo02eg= github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec/go.mod h1:eQVa6QFGzKy0qMcnW2pez0XBczvgwSjw9vA23qifEyU= github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30= github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anchore/go-sync v0.0.0-20260122203928-582959aeb913 h1:oLusMayBRYlFy/E+6PDX77JRTFjLy6HpxFdP31b9Cqc= github.com/anchore/go-sync v0.0.0-20260122203928-582959aeb913/go.mod h1:G58yNg7ki6sPGmC88zS7lslJZvfdgQuf7b/GTnjTn5M= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/grype v0.109.1 h1:DASABCFZ+xM41dP9jCz9i43/GpN04C7efeq+KPr6/RE= github.com/anchore/grype v0.109.1/go.mod h1:UPOdf/+lggCHIYL6YAkTv0hsP226bloueGb2lawEfSE= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.21 h1:rIwPDjJQdOdIvMrkNyZFkUS5dSyQPDjAwUqU0XjVF74= github.com/anchore/stereoscope v0.1.21/go.mod h1:8652axKmO8itOcGX8gzAvHW3cVWvbxpJ4woLNMEO4a0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M= github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4= github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E= github.com/aquasecurity/go-version v0.0.1/go.mod h1:s1UU6/v2hctXcOa3OLwfj5d9yoXHa3ahf+ipSwEvGT0= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA= github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/ashanbrown/forbidigo/v2 v2.3.0 h1:OZZDOchCgsX5gvToVtEBoV2UWbFfI6RKQTir2UZzSxo= github.com/ashanbrown/forbidigo/v2 v2.3.0/go.mod h1:5p6VmsG5/1xx3E785W9fouMxIOkvY2rRV9nMdWadd6c= github.com/ashanbrown/makezero/v2 v2.1.0 h1:snuKYMbqosNokUKm+R6/+vOPs8yVAi46La7Ck6QYSaE= github.com/ashanbrown/makezero/v2 v2.1.0/go.mod h1:aEGT/9q3S8DHeE57C88z2a6xydvgx8J5hgXIGWgo0MY= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4= github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g= github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA= github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo= github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami/go-version v0.0.0-20251209151629-b46c9d8f1f27 h1:YuYtreKQCWQ4nVexOCcoMcAJSESJXTNLKRCaaKMYL1s= github.com/bitnami/go-version v0.0.0-20251209151629-b46c9d8f1f27/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5w= github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI= github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= github.com/bombsimon/wsl/v5 v5.6.0 h1:4z+/sBqC5vUmSp1O0mS+czxwH9+LKXtCWtHH9rZGQL8= github.com/bombsimon/wsl/v5 v5.6.0/go.mod h1:Uqt2EfrMj2NV8UGoN1f1Y3m0NpUVCsUdrNCdet+8LvU= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= github.com/bufbuild/buf v1.66.1 h1:wqmmU+6uoxB/eYDOmXq2To4qEXvOJN7gR6L9AxrPL1E= github.com/bufbuild/buf v1.66.1/go.mod h1:Vd3ELm8IePWaDJaS9FLy94FFOnLrjLi4mDxmXtw9Xio= github.com/bufbuild/protocompile v0.14.2-0.20260306221011-519528254156 h1:XOfIInPVufMjifwy3fli8qQVsGHWVCDVY/zp6elAOsY= github.com/bufbuild/protocompile v0.14.2-0.20260306221011-519528254156/go.mod h1:cxhE8h+14t0Yxq2H9MV/UggzQ1L0gh0t2tJobITWsBE= github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1 h1:V1xulAoqLqVg44rY97xOR+mQpD2N+GzhMHVwJ030WEU= github.com/bufbuild/protoplugin v0.0.0-20250218205857-750e09ce93e1/go.mod h1:c5D8gWRIZ2HLWO3gXYTtUfw/hbJyD8xikv2ooPxnklQ= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= github.com/catenacyber/perfsprint v0.10.1 h1:u7Riei30bk46XsG8nknMhKLXG9BcXz3+3tl/WpKm0PQ= github.com/catenacyber/perfsprint v0.10.1/go.mod h1:DJTGsi/Zufpuus6XPGJyKOTMELe347o6akPvWG9Zcsc= github.com/ccojocar/zxcvbn-go v1.0.4 h1:FWnCIRMXPj43ukfX000kvBZvV6raSxakYr1nzyNrUcc= github.com/ccojocar/zxcvbn-go v1.0.4/go.mod h1:3GxGX+rHmueTUMvm5ium7irpyjmm7ikxYFOSJB21Das= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.11 h1:g1/EX1eIiKS57NTWsYtHDZ/APfeXKhye1DidBcABctk= github.com/charithe/durationcheck v0.0.11/go.mod h1:x5iZaixRNl8ctbM+3B2RrPG5t856TxRyVQEnbIEM2X4= github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY= github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo= github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g= github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs= github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ= github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw= github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= github.com/containerd/containerd/v2 v2.2.1 h1:TpyxcY4AL5A+07dxETevunVS5zxqzuq7ZqJXknM11yk= github.com/containerd/containerd/v2 v2.2.1/go.mod h1:NR70yW1iDxe84F2iFWbR9xfAN0N2F0NcjTi1OVth4nU= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw= github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY= github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE= github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/daixiang0/gci v0.13.7 h1:+0bG5eK9vlI08J+J/NWGbWPTNiXPG4WhNLJOkSxWITQ= github.com/daixiang0/gci v0.13.7/go.mod h1:812WVN6JLFY9S6Tv76twqmNqevN0pa3SX3nih0brVzQ= github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb h1:4W/2rQ3wzEimF5s+J6OY3ODiQtJZ5W1sForSgogVXkY= github.com/deitch/magic v0.0.0-20240306090643-c67ab88f10cb/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/dghubble/trie v0.1.0 h1:kJnjBLFFElBwS60N4tkPvnLhnpcDxbBjIulgI8CpNGM= github.com/dghubble/trie v0.1.0/go.mod h1:sOmnzfBNH7H92ow2292dDFWNsVQuh/izuD7otCYb1ak= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8= github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dmarkham/enumer v1.6.3 h1:B4aV4OsfzbrS5rvjILt4mMjiWBA//cKxJUMsvHZ8mEI= github.com/dmarkham/enumer v1.6.3/go.mod h1:DyjXaqCglj4GhELF73oWiparNkYkXvmOBLza/o4kO74= github.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk= github.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dsseng/syft v1.42.1-0.20260219105507-8cf142cbcb79 h1:j32iisjF/lFkyDMQ18zTZFi2ojbJxqAt5TovGryBXmQ= github.com/dsseng/syft v1.42.1-0.20260219105507-8cf142cbcb79/go.mod h1:uo2xEPi6gyc/qabZFv0Oni6W2pL0gE7sshAyZJCnHNg= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs= github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY= github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk= github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ= github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghostiam/protogetter v0.3.20 h1:oW7OPFit2FxZOpmMRPP9FffU4uUpfeE/rEdE1f+MzD0= github.com/ghostiam/protogetter v0.3.20/go.mod h1:FjIu5Yfs6FT391m+Fjp3fbAYJ6rkL/J6ySpZBfnODuI= github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV/M= github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-snaps v0.5.20 h1:FGKonEeQPJ12t7RQj6cTPa881fl5c8HYarMLv5vP7sg= github.com/gkampitakis/go-snaps v0.5.20/go.mod h1:gC3YqxQTPyIXvQrw/Vpt3a8VqR1MO8sVpZFWN4DGwNs= github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-critic/go-critic v0.14.3 h1:5R1qH2iFeo4I/RJU8vTezdqs08Egi4u5p6vOESA0pog= github.com/go-critic/go-critic v0.14.3/go.mod h1:xwntfW6SYAd7h1OqDzmN6hBX/JxsEKl5up/Y2bsxgVQ= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0= github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM= github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY= github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gocsaf/csaf/v3 v3.5.1 h1:jTA1fLrK0/JIczPs7itTD53qANoO4tn2VaGvUeitePc= github.com/gocsaf/csaf/v3 v3.5.1/go.mod h1:pga89lE+iWJm7smTdzYcXuetYUbgY8caXfaIP4BJG98= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godoc-lint/godoc-lint v0.11.2 h1:Bp0FkJWoSdNsBikdNgIcgtaoo+xz6I/Y9s5WSBQUeeM= github.com/godoc-lint/godoc-lint v0.11.2/go.mod h1:iVpGdL1JCikNH2gGeAn3Hh+AgN5Gx/I/cxV+91L41jo= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg= github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/asciicheck v0.5.0 h1:jczN/BorERZwK8oiFBOGvlGPknhvq0bjnysTj4nUfo0= github.com/golangci/asciicheck v0.5.0/go.mod h1:5RMNAInbNFw2krqN6ibBxN/zfRFa9S6tA1nPdM0l8qQ= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= github.com/golangci/go-printf-func-name v0.1.1 h1:hIYTFJqAGp1iwoIfsNTpoq1xZAarogrvjO9AfiW3B4U= github.com/golangci/go-printf-func-name v0.1.1/go.mod h1:Es64MpWEZbh0UBtTAICOZiB+miW53w/K9Or/4QogJss= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= github.com/golangci/golangci-lint/v2 v2.11.3 h1:ySX1GtLwlwOEzcLKJifI/aIVesrcHDno+5mrro8rWes= github.com/golangci/golangci-lint/v2 v2.11.3/go.mod h1:HmDEVZuxz77cNLumPfNNHAFyMX/b7IbA0tpmAbwiVfo= github.com/golangci/golines v0.15.0 h1:Qnph25g8Y1c5fdo1X7GaRDGgnMHgnxh4Gk4VfPTtRx0= github.com/golangci/golines v0.15.0/go.mod h1:AZjXd23tbHMpowhtnGlj9KCNsysj72aeZVVHnVcZx10= github.com/golangci/misspell v0.8.0 h1:qvxQhiE2/5z+BVRo1kwYA8yGz+lOlu5Jfvtx2b04Jbg= github.com/golangci/misspell v0.8.0/go.mod h1:WZyyI2P3hxPY2UVHs3cS8YcllAeyfquQcKfdeE9AFVg= github.com/golangci/plugin-module-register v0.1.2 h1:e5WM6PO6NIAEcij3B053CohVp3HIYbzSuP53UAYgOpg= github.com/golangci/plugin-module-register v0.1.2/go.mod h1:1+QGTsKBvAIvPvoY/os+G5eoqxWn70HYDm2uvUyGuVw= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e h1:ai0EfmVYE2bRA5htgAG9r7s3tHsfjIhN98WshBTJ9jM= github.com/golangci/swaggoswag v0.0.0-20250504205917-77f2aca3143e/go.mod h1:Vrn4B5oR9qRwM+f54koyeH3yzphlecwERs0el27Fr/s= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc= github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.21.2 h1:vYaMU4nU55JJGFC9JR/s8NZcTjbE9DBBbvusTW9NeS0= github.com/google/go-containerregistry v0.21.2/go.mod h1:ctO5aCaewH4AK1AumSF5DPW+0+R+d2FmylMJdp5G7p0= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs= github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= 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-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno= github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ= github.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc= github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY= github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0= github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E= github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg= github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA= github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs= github.com/gordonklaus/ineffassign v0.2.0 h1:Uths4KnmwxNJNzq87fwQQDDnbNb7De00VOk9Nu0TySs= github.com/gordonklaus/ineffassign v0.2.0/go.mod h1:TIpymnagPSexySzs7F9FnO1XFTy8IT3a59vmZp5Y9Lw= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= github.com/gostaticanalysis/nilerr v0.1.2 h1:S6nk8a9N8g062nsx63kUkF6AzbHGw7zzyHMcpu52xQU= github.com/gostaticanalysis/nilerr v0.1.2/go.mod h1:A19UHhoY3y8ahoL7YKz6sdjDtduwTSI4CsymaC2htPA= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= github.com/gpustack/gguf-parser-go v0.24.0 h1:tdJceXYp9e5RhE9RwVYIuUpir72Jz2D68NEtDXkKCKc= github.com/gpustack/gguf-parser-go v0.24.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA= github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4= github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0= github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70 h1:0HADrxxqaQkGycO1JoUUA+B4FnIkuo8d2bz/hSaTFFQ= github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.70/go.mod h1:fm2FdDCzJdtbXF7WKAMvBb5NEPouXPHFbGNYs9ShFns= github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-getter v1.8.4 h1:hGEd2xsuVKgwkMtPVufq73fAmZU/x65PPcqH3cb0D9A= github.com/hashicorp/go-getter v1.8.4/go.mod h1:x27pPGSg9kzoB147QXI8d/nDvp2IgYGcwuRjpaXE9Yg= github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU= github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 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/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ= github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8= github.com/jedib0t/go-pretty/v6 v6.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o= github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU= github.com/jgautheron/goconst v1.8.2 h1:y0XF7X8CikZ93fSNT6WBTb/NElBu9IjaY7CCYQrCMX4= github.com/jgautheron/goconst v1.8.2/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= github.com/jhump/protoreflect/v2 v2.0.0-beta.2 h1:qZU+rEZUOYTz1Bnhi3xbwn+VxdXkLVeEpAeZzVXLY88= github.com/jhump/protoreflect/v2 v2.0.0-beta.2/go.mod h1:4tnOYkB/mq7QTyS3YKtVtNrJv4Psqout8HA1U+hZtgM= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jjti/go-spancheck v0.6.5 h1:lmi7pKxa37oKYIMScialXUK6hP3iY5F1gu+mLBPgYB8= github.com/jjti/go-spancheck v0.6.5/go.mod h1:aEogkeatBrbYsyW6y5TgDfihCulDYciL1B7rG2vSsrU= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.2 h1:yfNQvP9YaGQR7VaWLYcfZUlRP2eo2vhExWKxD/fP6q0= github.com/karamaru-alpha/copyloopvar v1.2.2/go.mod h1:oY4rGZqZ879JkJMtX3RRkcXRkmUvH0x35ykgaKgsgJY= github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y= github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc= github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY= github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.10.0 h1:Lvs/YAHP24YKg08LA8oDw2z9fJVme090RAXd90S+rrw= github.com/kisielk/errcheck v1.10.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GXhHq+7LeOzx/haG7HSIZokl3/0GkoUFzsRJjg= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8= github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23 h1:dWzdsqjh1p2gNtRKqNwuBvKqMNwnLOPLzVZT1n6DK7s= github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23/go.mod h1:lUaIXCWzf7BRKTY5iEcrYy1TfgbYLYVIS/B2vPkJzOc= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.7.1 h1:fI8QITAoFVLx+y+vSyuLBP+rcVIB8jKooNSCT2EiI98= github.com/kulti/thelper v0.7.1/go.mod h1:NsMjfQEy6sd+9Kfw8kCP61W1I0nerGSYSFnGaxQkcbs= github.com/kunwardeep/paralleltest v1.0.15 h1:ZMk4Qt306tHIgKISHWFJAO1IDQJLc6uDyJMLyncOb6w= github.com/kunwardeep/paralleltest v1.0.15/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= github.com/ldez/exptostd v0.4.5 h1:kv2ZGUVI6VwRfp/+bcQ6Nbx0ghFWcGIKInkG/oFn1aQ= github.com/ldez/exptostd v0.4.5/go.mod h1:QRjHRMXJrCTIm9WxVNH6VW7oN7KrGSht69bIRwvdFsM= github.com/ldez/gomoddirectives v0.8.0 h1:JqIuTtgvFC2RdH1s357vrE23WJF2cpDCPFgA/TWDGpk= github.com/ldez/gomoddirectives v0.8.0/go.mod h1:jutzamvZR4XYJLr0d5Honycp4Gy6GEg2mS9+2YX3F1Q= github.com/ldez/grignotin v0.10.1 h1:keYi9rYsgbvqAZGI1liek5c+jv9UUjbvdj3Tbn5fn4o= github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufpTEbwOas= github.com/ldez/structtags v0.6.1 h1:bUooFLbXx41tW8SvkfwfFkkjPYvFFs59AAMgVg6DUBk= github.com/ldez/structtags v0.6.1/go.mod h1:YDxVSgDy/MON6ariaxLF2X09bh19qL7MtGBN5MrvbdY= github.com/ldez/tagliatelle v0.7.2 h1:KuOlL70/fu9paxuxbeqlicJnCspCRjH0x8FW+NfgYUk= github.com/ldez/tagliatelle v0.7.2/go.mod h1:PtGgm163ZplJfZMZ2sf5nhUT170rSuPgBimoyYtdaSI= github.com/ldez/usetesting v0.5.0 h1:3/QtzZObBKLy1F4F8jLuKJiKBjjVFi1IavpoWbmqLwc= github.com/ldez/usetesting v0.5.0/go.mod h1:Spnb4Qppf8JTuRgblLrEWb7IE6rDmUpGvxY3iRrzvDQ= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/manuelarte/embeddedstructfieldcheck v0.4.0 h1:3mAIyaGRtjK6EO9E73JlXLtiy7ha80b2ZVGyacxgfww= github.com/manuelarte/embeddedstructfieldcheck v0.4.0/go.mod h1:z8dFSyXqp+fC6NLDSljRJeNQJJDWnY7RoWFzV3PC6UM= github.com/manuelarte/funcorder v0.5.0 h1:llMuHXXbg7tD0i/LNw8vGnkDTHFpTnWqKPI85Rknc+8= github.com/manuelarte/funcorder v0.5.0/go.mod h1:Yt3CiUQthSBMBxjShjdXMexmzpP8YGvGLjrxJNkO2hA= github.com/maratori/testableexamples v1.0.1 h1:HfOQXs+XgfeRBJ+Wz0XfH+FHnoY9TVqL6Fcevpzy4q8= github.com/maratori/testableexamples v1.0.1/go.mod h1:XE2F/nQs7B9N08JgyRmdGjYVGqxWwClLPCGSQhXQSrQ= github.com/maratori/testpackage v1.1.2 h1:ffDSh+AgqluCLMXhM19f/cpvQAKygKAJXFl9aUjmbqs= github.com/maratori/testpackage v1.1.2/go.mod h1:8F24GdVDFW5Ew43Et02jamrVMNXLUNaOynhDssITGfc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/masahiro331/go-mvn-version v0.0.0-20260119054159-d21fcd2e7de1 h1:XC9v1PB6rMOCTnQIInoFvgSA+vhTCt0wQ7B+Tjpombk= github.com/masahiro331/go-mvn-version v0.0.0-20260119054159-d21fcd2e7de1/go.mod h1:jZ3F25l7DbD7l7DcA8aj7eo1EZ84nbzcQHBB4lCSrI8= github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw= github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.20 h1:WcT52H91ZUAwy8+HUkdM3THM6gXqXuLJi9O3rjcQQaQ= github.com/mattn/go-runewidth v0.0.20/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgechev/revive v1.15.0 h1:vJ0HzSBzfNyPbHKolgiFjHxLek9KUijhqh42yGoqZ8Q= github.com/mgechev/revive v1.15.0/go.mod h1:LlAKO3QQe9OJ0pVZzI2GPa8CbXGZ/9lNpCGvK4T/a8A= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/moby/api v1.53.0 h1:PihqG1ncw4W+8mZs69jlwGXdaYBeb5brF6BL7mPIS/w= github.com/moby/moby/api v1.53.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/moby/client v0.2.2 h1:Pt4hRMCAIlyjL3cr8M5TrXCwKzguebPAc2do2ur7dEM= github.com/moby/moby/client v0.2.2/go.mod h1:2EkIPVNCqR05CMIzL1mfA07t0HvVUUOl85pasRz/GmQ= github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.3.2 h1:qRlmpTzm2pstMKKzTdvwPCF5QfBNURSlAgN/R+qbKos= github.com/mwitkow/go-proto-validators v0.3.2/go.mod h1:ej0Qp0qMgHN/KtDyUt+Q1/tA7a5VarXUOUxD+oeD30w= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w= github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+EDG4s40hMwE//d5SBgDjUOrITReV2u4aA= github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nunnatsa/ginkgolinter v0.23.0 h1:x3o4DGYOWbBMP/VdNQKgSj+25aJKx2Pe6lHr8gBcgf8= github.com/nunnatsa/ginkgolinter v0.23.0/go.mod h1:9qN1+0akwXEccwV1CAcCDfcoBlWXHB+ML9884pL4SZ4= github.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU= github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= 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.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA= github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88= github.com/olekukonko/tablewriter v1.1.3 h1:VSHhghXxrP0JHl+0NnKid7WoEmd9/urKRJLysb70nnA= github.com/olekukonko/tablewriter v1.1.3/go.mod h1:9VU0knjhmMkXjnMKrZ3+L2JhhtsQ/L38BbL3CRNE8tM= github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE= github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg= github.com/openvex/go-vex v0.2.7 h1:/pN3bqvS4QOc6WkkL0hbKzJuAtsUD9vmvk9IZkzD3Zc= github.com/openvex/go-vex v0.2.7/go.mod h1:ZyQC3NXl9jjS53JOpBG3LAUXySkW8IlJ/GIhsnf5D54= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 h1:FvA4bwjKpPqik5WsQ8+4z4DKWgA1tO1RTTtNKr5oYNA= github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554/go.mod h1:n73K/hcuJ50MiVznXyN4rde6fZY7naGKWBXOLFTyc94= github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoXLtmE3I0PLs= github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0= github.com/pandatix/go-cvss v0.6.2 h1:TFiHlzUkT67s6UkelHmK6s1INKVUG7nlKYiWWDTITGI= github.com/pandatix/go-cvss v0.6.2/go.mod h1:jDXYlQBZrc8nvrMUVVvTG8PhmuShOnKrxP53nOFkt8Q= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM= github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM= github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 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.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6 h1:rh2lKw/P/EqHa724vYH2+VVQ1YnW4u6EOXl0PMAovZE= github.com/petermattis/goid v0.0.0-20260226131333-17d1149c6ac6/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0= github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM= github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9 h1:arwj11zP0yJIxIRiDn22E0H8PxfF7TsTrc2wIPFIsf4= github.com/protocolbuffers/protoscope v0.0.0-20221109213918-8e7a6aafa2c9/go.mod h1:SKZx6stCn03JN3BOWTwvVIO2ajMkb/zQdTceXYhKw/4= github.com/pseudomuto/protoc-gen-doc v1.5.1 h1:Ah259kcrio7Ix1Rhb6u8FCaOkzf9qRBqXnvAufg061w= github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM= github.com/pseudomuto/protokit v0.2.1 h1:kCYpE3thoR6Esm0CUvd5xbrDTOZPvQPTDeyXpZfrJdk= github.com/pseudomuto/protokit v0.2.1/go.mod h1:gt7N5Rz2flBzYafvaxyIxMZC0TTF5jDZfRnw25hAAyo= github.com/quasilyte/go-ruleguard v0.4.5 h1:AGY0tiOT5hJX9BTdx/xBdoCubQUAE2grkqY2lSwvZcA= github.com/quasilyte/go-ruleguard v0.4.5/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY= github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8= github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8gOLsYwaY2JwlTMT4brS5/9XJdrdIbmk2obvQ748CC0= github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA= github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= 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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg= github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sanposhiho/wastedassign/v2 v2.1.0 h1:crurBF7fJKIORrV85u9UUpePDYGWnwvv3+A96WvwXT0= github.com/sanposhiho/wastedassign/v2 v2.1.0/go.mod h1:+oSmSC+9bQ+VUAxA66nBb0Z7N8CK7mscKTDYC6aIek4= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ= github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= github.com/sashamelentyev/usestdlibvars v1.29.0 h1:8J0MoRrw4/NAXtjQqTHrbW9NN+3iMf7Knkq057v4XOQ= github.com/sashamelentyev/usestdlibvars v1.29.0/go.mod h1:8PpnjHMk5VdeWlVb4wCdrB8PNbLqZ3wBZTZWkrpZZL8= github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc= github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08 h1:AoLtJX4WUtZkhhUUMFy3GgecAALp/Mb4S1iyQOA2s0U= github.com/securego/gosec/v2 v2.24.8-0.20260309165252-619ce2117e08/go.mod h1:+XLCJiRE95ga77XInNELh2M6zQP+PdqiT9Zpm0D9Wpk= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/encoding v0.5.3 h1:OjMgICtcSFuNvQCdwqMCv9Tg7lEOXGwm1J5RPQccx6w= github.com/segmentio/encoding v0.5.3/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/siderolabs/deep-copy v0.5.8 h1:43G8qJBTwGuKZX+UYNe29ZOtyDFqayS7/GfJVviz6RU= github.com/siderolabs/deep-copy v0.5.8/go.mod h1:PNX2/lqNu3oyDZGWe1eKW8bkYkhs583WcUBBB2EviX8= github.com/siderolabs/gen v0.8.6 h1:pE6shuqov3L+5rEcAUJ/kY6iJofimljQw5G95P8a5c4= github.com/siderolabs/gen v0.8.6/go.mod h1:J9IbusbES2W6QWjtSHpDV9iPGZHc978h1+KJ4oQRspQ= github.com/siderolabs/importvet v0.2.0 h1:oGQXke2/TnSg5xEFD/ktc+G59INzmgRU3yJ8wt++QRE= github.com/siderolabs/importvet v0.2.0/go.mod h1:X0AIl/3MnvlEeCzYRJe/t9YOk4FjPhT53doU4xOG4AU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= github.com/smallnest/ringbuffer v0.1.1 h1:KL2iILLdDCr9nWxYrNcsQ7Px7EVnoNBDJ0r/M/hEksA= github.com/smallnest/ringbuffer v0.1.1/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0= github.com/sonatard/noctx v0.5.0 h1:e/jdaqAsuWVOKQ0P6NWiIdDNHmHT5SwuuSfojFjzwrw= github.com/sonatard/noctx v0.5.0/go.mod h1:64XdbzFb18XL4LporKXp8poqZtPKbCrqQ402CV+kJas= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= 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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb h1:7G2Czq97VORM5xNRrD8tSQdhoXPRs8s+Otlc7st9TS0= github.com/spdx/gordf v0.0.0-20250128162952-000978ccd6fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg= github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 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.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 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 v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 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.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= 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= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.3.1 h1:AyX7+dxI4IdLBPtDbsGAyqiTSLpCP9hWRrXQDU4Cm/g= github.com/stbenjam/no-sprintf-host-port v0.3.1/go.mod h1:ODbZesTCHMVKthBHskvUUexdcNHAQRXk9NpSsL8p/HQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/sylabs/sif/v2 v2.23.0 h1:VWJC7iryINdIgyIK8EdxMCJkx8EDPVlvFaskgY+ruFk= github.com/sylabs/sif/v2 v2.23.0/go.mod h1:0UJk7MMSQS2n/95yYd+CvAfK7K2mE0aWHRYYybd+ouw= github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI= github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo= github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/tetafro/godot v1.5.4 h1:u1ww+gqpRLiIA16yF2PV1CV1n/X3zhyezbNXC3E14Sg= github.com/tetafro/godot v1.5.4/go.mod h1:eOkMrVQurDui411nBY2FA05EYH01r14LuWY/NrVDVcU= github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA= github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4 h1:SiHe5XLTn9sFWJ5pBwJ5FN/4j34q9ZlOAD//kMoMYp0= github.com/timakin/bodyclose v0.0.0-20260129054331-73d1f95b84b4/go.mod h1:sDHLK7rb/59v/ZxZ7KtymgcoxuUMxjXq8gtu9VMOK8M= github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= github.com/tomarrell/wrapcheck/v2 v2.12.0 h1:H/qQ1aNWz/eeIhxKAFvkfIA+N7YDvq6TWVFL27Of9is= github.com/tomarrell/wrapcheck/v2 v2.12.0/go.mod h1:AQhQuZd0p7b6rfW+vUwHm5OMCGgp63moQ9Qr/0BpIWo= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= github.com/ultraware/funlen v0.2.0/go.mod h1:ZE0q4TsJ8T1SQcjmkhN/w+MceuatI6pBFSxxyteHIJA= github.com/ultraware/whitespace v0.2.0 h1:TYowo2m9Nfj1baEQBjuHzvMRbp19i+RCcRYrSWoFa+g= github.com/ultraware/whitespace v0.2.0/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= github.com/uudashr/gocognit v1.2.1 h1:CSJynt5txTnORn/DkhiB4mZjwPuifyASC8/6Q0I/QS4= github.com/uudashr/gocognit v1.2.1/go.mod h1:acaubQc6xYlXFEMb9nWX2dYBzJ/bIjEkc1zzvyIZg5Q= github.com/uudashr/iface v1.4.1 h1:J16Xl1wyNX9ofhpHmQ9h9gk5rnv2A6lX/2+APLTo0zU= github.com/uudashr/iface v1.4.1/go.mod h1:pbeBPlbuU2qkNDn0mmfrxP2X+wjPMIQAy+r1MBXSXtg= github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E= github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY= github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b h1:uWNQ0khA6RdFzODOMwKo9XXu7fuewnnkHykUtuKru8s= github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b/go.mod h1:ewlIKbKV8l+jCj8rkdXIs361ocR5x3qGyoCSca47Gx8= github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 h1:EHsPe0Q0ANoLOZff1dBLAyeWLTA4sbPTpGI+2zb0FnM= github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0/go.mod h1:g/D9uEUFp5YLyciwCpVsSOZOm56hfv4rzGJod6MlqIM= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4= github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.14.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0= github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg= github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= go-simpler.org/musttag v0.14.0 h1:XGySZATqQYSEV3/YTy+iX+aofbZZllJaqwFWs+RTtSo= go-simpler.org/musttag v0.14.0/go.mod h1:uP8EymctQjJ4Z1kUnjX0u2l60WfUdQxCwSNKzE1JEOE= go-simpler.org/sloglint v0.11.1 h1:xRbPepLT/MHPTCA6TS/wNfZrDzkGvCCqUv4Bdwc3H7s= go-simpler.org/sloglint v0.11.1/go.mod h1:2PowwiCOK8mjiF+0KGifVOT8ZsCNiFzvfyJeJOIt8MQ= go.augendre.info/arangolint v0.4.0 h1:xSCZjRoS93nXazBSg5d0OGCi9APPLNMmmLrC995tR50= go.augendre.info/arangolint v0.4.0/go.mod h1:l+f/b4plABuFISuKnTGD4RioXiCCgghv2xqst/xOvAA= go.augendre.info/fatcontext v0.9.0 h1:Gt5jGD4Zcj8CDMVzjOJITlSb9cEch54hjRRlN3qDojE= go.augendre.info/fatcontext v0.9.0/go.mod h1:L94brOAT1OOUNue6ph/2HnwxoNlds9aXDF2FcUntbNw= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs= go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac= go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 h1:hCzQgh6UcwbKgNSRurYWSqh8MufqRRPODRBblutn4TE= go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2/go.mod h1:gtSHRuYfbCT0qnbLnovpie/WEmqyJ7T4n6VXiFMBtcw= go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg= go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ= go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 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/detectors/gcp v1.40.0 h1:Awaf8gmW99tZTOWqkLCOl6aw1/rxAWVlHsHIZ3fT2sA= go.opentelemetry.io/contrib/detectors/gcp v1.40.0/go.mod h1:99OY9ZCqyLkzJLTh5XhECpLRSxcZl+ZDKBEO+jMBFR4= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0 h1:5gn2urDL/FBnK8OkCfD1j3/ER79rUuTYmCvlXBKeYL8= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.39.0/go.mod h1:0fBG6ZJxhqByfFZDwSwpZGzJU671HkwpWaNe2t4VUPI= go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 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/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 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.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 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= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw= go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20260218203240-3dfff04db8fa h1:6Wi43P0isP8Nl8D4qJmA3VC4FswVH0RJkr5cauo67SQ= golang.org/x/exp/typeparams v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:PqrXSW65cXDZH0k4IeUbhmg/bcAZDbzNz3byBpKCsXo= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc= golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I= golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU= google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw= google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE= google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d h1:vsOm753cOAMkt76efriTCDKjpCbK18XGHMJHo0JUKhc= google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d/go.mod h1:0oz9d7g9QLSdv9/lgbIjowW1JoxMbxmBVNe8i6tORJI= google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 h1:tu/dtnW1o3wfaxCOjSLn5IRX4YDcJrtlpzYkhHhGaC4= google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171/go.mod h1:M5krXqk4GhBKvB596udGL3UyjL4I1+cTbK0orROM9ng= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 h1:/WILD1UcXj/ujCxgoL/DvRgt2CP3txG8+FwkUbb9110= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1/go.mod h1:YNKnb2OAApgYn2oYY47Rn7alMr1zWjb2U8Q0aoGWiNc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/typ.v4 v4.4.0 h1:O9vTueEmZd0iA9DF+g2wXeNCeloN2TOpxu6FXKl3AqM= gopkg.in/typ.v4 v4.4.0/go.mod h1:wolXe8DlewxRCjA7SOiT3zjrZ0eQJZcr8cmV6bQWJUM= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= k8s.io/code-generator v0.35.2 h1:3874swbO2c26VWTf6lKD4NWGyHIfyBeTCk7caCG3TuU= k8s.io/code-generator v0.35.2/go.mod h1:id4XLCm0yAQq5nlvyfAKibMOKnMjzlesAwGw6kM3Adc= k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b h1:0YkdvW3rX2vaBWsqCGZAekxPRwaI5NuYNprOsMNVLns= k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b/go.mod h1:yvyl3l9E+UxlqOMUULdKTAYB0rEhsmjr7+2Vb/1pCSo= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis= modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.30.2 h1:4yPaaq9dXYXZ2V8s1UgrC3KIj580l2N4ClrLwnbv2so= modernc.org/ccgo/v4 v4.30.2/go.mod h1:yZMnhWEdW0qw3EtCndG1+ldRrVGS+bIwyWmAWzS0XEw= modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA= modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc= modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo= modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY= modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks= modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI= modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= modernc.org/libc v1.68.0/go.mod h1:NnKCYeoYgsEqnY3PgvNgAeaJnso968ygU8Z0DxjoEc0= modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI= modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 h1:ssMzja7PDPJV8FStj7hq9IKiuiKhgz9ErWw+m68e7DI= mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15/go.mod h1:4M5MMXl2kW6fivUT6yRGpLLPNfuGtU2Z0cPvFquGDYU= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= pluginrpc.com/pluginrpc v0.5.0 h1:tOQj2D35hOmvHyPu8e7ohW2/QvAnEtKscy2IJYWQ2yo= pluginrpc.com/pluginrpc v0.5.0/go.mod h1:UNWZ941hcVAoOZUn8YZsMmOZBzbUjQa3XMns8RQLp9o= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= ================================================ FILE: tools/gotagsrewrite/ast.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "go/ast" "go/parser" "go/token" ) // parseAst parses the given Go source file and returns the AST. func parseAst(fset *token.FileSet, path string) (*ast.File, error) { f, err := parser.ParseFile(fset, path, nil, parser.ParseComments) if err != nil { return nil, err } return f, nil } // findAllStructs with comments finds all structs in the given AST. func findAllStructs(f ast.Node) []*ast.TypeSpec { var nodes []*ast.TypeSpec ast.Inspect(f, func(n ast.Node) bool { genDecl, ok := n.(*ast.GenDecl) if !ok || genDecl.Tok != token.TYPE { return true } typeSpecs := filter(genDecl.Specs, typeSpecFilter) switch len(typeSpecs) { case 0: return false case 1: if isTarget(genDecl.Doc) || isTarget(typeSpecs[0].Doc) { nodes = append(nodes, typeSpecs[0]) } return false default: for _, typeSpec := range typeSpecs { if isTarget(typeSpec.Doc) { nodes = append(nodes, typeSpec) } } return false } }) return nodes } func typeSpecFilter(n ast.Spec) (*ast.TypeSpec, bool) { typeSpec, ok := n.(*ast.TypeSpec) if !ok { return typeSpec, false } structType, ok := typeSpec.Type.(*ast.StructType) if !ok { return typeSpec, false } if structType.Fields.NumFields() == 0 { return typeSpec, false } if !isCapitalCase(typeSpec.Name.Name) { return typeSpec, false } return typeSpec, true } func isTarget(doc *ast.CommentGroup) bool { if doc == nil { return false } for _, c := range doc.List { if c.Text == "//gotagsrewrite:gen" { return true } } return false } func filter[T, V any](slc []T, f func(n T) (V, bool)) []V { var result []V for _, v := range slc { res, ok := f(v) if ok { result = append(result, res) } } return result } ================================================ FILE: tools/gotagsrewrite/go.mod ================================================ module github.com/siderolabs/talos/tools/gotagsrewrite go 1.26.0 require ( github.com/fatih/structtag v1.2.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.9 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ================================================ FILE: tools/gotagsrewrite/go.sum ================================================ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 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= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ================================================ FILE: tools/gotagsrewrite/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "fmt" "go/ast" "go/format" "go/token" "io" "io/ioutil" "os" "path/filepath" "strconv" "strings" "unicode" "github.com/fatih/structtag" "github.com/spf13/cobra" ) // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "gotagsrewrite path", Short: "This CLI is used to add `protobuf:` tags to structs with //gotagsrewrite:gen comment", Example: "gotagsrewrite .", Args: cobra.ExactArgs(1), Version: "v1.0.0", RunE: func(cmd *cobra.Command, args []string) error { return Run(args[0]) }, SilenceUsage: true, DisableAutoGenTag: true, } func main() { err := rootCmd.Execute() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } // Run runs the main logic of the program. func Run(path string) error { paths, err := findGoFiles(path) if err != nil { return fmt.Errorf("failed to find Go files: %w", err) } filesAndStructs := map[string]fileStruct{} tokenSet := token.NewFileSet() for _, path := range paths { parsedAST, err := parseAst(tokenSet, path) //nolint:govet if err != nil { return fmt.Errorf("failed to parse AST for %s: %w", path, err) } structs := findAllStructs(parsedAST) if len(structs) == 0 { continue } filesAndStructs[path] = fileStruct{ path: path, fullAST: parsedAST, structs: append(filesAndStructs[path].structs, structs...), } } err = updateStructs(filesAndStructs, tokenSet) if err != nil { return err } return nil } func updateStructs(filesAndStructs map[string]fileStruct, tokenSet *token.FileSet) error { for path, val := range filesAndStructs { for _, t := range val.structs { err := updateProtoTags(t) if err != nil { return err } goFile, err := os.OpenFile(path, os.O_RDWR|os.O_TRUNC, os.ModePerm) if err != nil { return fmt.Errorf("failed to open file %s: %w", path, err) } err = format.Node(goFile, tokenSet, val.fullAST) if err != nil { return err } } fmt.Printf("updated %s\n", path) } return nil } // findGoFiles recursevly finds all Go files in the given directory. func findGoFiles(dir string) ([]string, error) { var files []string err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if info.Name() == "testdata" || strings.HasPrefix(info.Name(), ".") { return nil } if strings.HasSuffix(path, ".go") { files = append(files, path) } return nil }) return files, err } type fileStruct struct { path string fullAST *ast.File structs []*ast.TypeSpec } func updateProtoTags(t *ast.TypeSpec) error { structNode := t.Type.(*ast.StructType) //nolint:errcheck num, err := findHighestProtoNum(structNode) if err != nil { return fmt.Errorf("failed to update proto tags for %s: %w", t.Name.Name, err) } if num == -1 { return nil } err = forEachFieldTag(structNode, func(tags *structtag.Tags) (*structtag.Tags, error) { _, err := tags.Get("protobuf") //nolint:govet if err == nil { return nil, nil } num++ newTag := &structtag.Tag{ Key: "protobuf", Name: strconv.Itoa(num), Options: nil, } tags.Set(newTag) //nolint:errcheck return tags, nil }) if err != nil { return err } return nil } // findHighestProtoNum returns the highest proto num in the given struct. // It returns -1 if struct has no exported fields. It returns 0 if there is no fields with "protobuf" tag. // Otherwise, it returns the highest proto num extracted from the fields tags. func findHighestProtoNum(structNode *ast.StructType) (int, error) { highestNum := -1 err := forEachFieldTag(structNode, func(tags *structtag.Tags) (*structtag.Tags, error) { if highestNum == -1 { highestNum = 0 } tag, err := tags.Get("protobuf") if err != nil { return nil, nil } num, err := strconv.Atoi(tag.Name) if err != nil { return nil, err } highestNum = max(highestNum, num) return nil, nil }) return highestNum, err } func forEachFieldTag(structNode *ast.StructType, fn func(tags *structtag.Tags) (*structtag.Tags, error)) error { for _, field := range structNode.Fields.List { if len(field.Names) < 1 { continue } fieldName := field.Names[0] if fieldName == nil || !isCapitalCase(fieldName.Name) { continue } tags := &structtag.Tags{} tagValue := "" if field.Tag != nil { tagValue = strings.Trim(field.Tag.Value, "`") var err error tags, err = structtag.Parse(tagValue) if err != nil { return fmt.Errorf("invalid tag: field '%s', tag '%s': %w", fieldName.Name, tagValue, err) } } newTags, err := fn(tags) switch { case err != nil: return fmt.Errorf("tag failure: field '%s', tag '%s': %w", fieldName.Name, tagValue, err) case newTags == nil: continue } if field.Tag == nil { field.Tag = &ast.BasicLit{ Kind: token.STRING, } } field.Tag.Value = "`" + newTags.String() + "`" } return nil } // isCapitalCase returns true if the given string is in capital case. func isCapitalCase(s string) bool { return len(s) > 0 && unicode.IsUpper(rune(s[0])) } const fileMode = 0o644 // CopyFile copies the contents of src to dst atomically. func CopyFile(src, dst string) (err error) { in, err := os.Open(src) if err != nil { return err } defer wrapErr(&err, in.Close) tmp, err := ioutil.TempFile(filepath.Dir(dst), "copyfile") if err != nil { return err } _, err = io.Copy(tmp, in) if err != nil { panicOnErr(tmp.Close()) panicOnErr(os.Remove(tmp.Name())) return err } if err := tmp.Close(); err != nil { panicOnErr(os.Remove(tmp.Name())) return err } if err := os.Chmod(tmp.Name(), fileMode); err != nil { panicOnErr(os.Remove(tmp.Name())) return err } if err := os.Rename(tmp.Name(), dst); err != nil { panicOnErr(os.Remove(tmp.Name())) return err } return nil } func panicOnErr(err error) { if err != nil { panic(err) } } func wrapErr(e *error, c func() error) { err := c() if err != nil && *e == nil { *e = err } } ================================================ FILE: tools/gotagsrewrite/main_test.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package main import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) func TestRun(t *testing.T) { tests := map[string]struct { original string golden string }{ "default_test": { original: "a.orig", golden: "a.golden", }, } for name, test := range tests { t.Run(name, func(t *testing.T) { tempDir := t.TempDir() tmpFile := filepath.Join(tempDir, "my.go") origPath := filepath.Join("testdata", test.original) require.NoError(t, CopyFile(origPath, tmpFile)) err := Run(tempDir) require.NoError(t, err) fileData := string(must(os.ReadFile(tmpFile))(t)) goldenPath := filepath.Join("testdata", test.golden) goldenData := string(must(os.ReadFile(goldenPath))(t)) require.Equal(t, goldenData, fileData) }) } } func must[V any](v V, err error) func(t *testing.T) V { return func(t *testing.T) V { require.NoError(t, err) return v } } ================================================ FILE: tools/gotagsrewrite/testdata/a.golden ================================================ package testdata //gotagsrewrite:gen type MySpecSpec struct { NewField string `yaml:"new_field" protobuf:"4"` FirstField string `protobuf:"5"` Name string `yaml:"string" protobuf:"6"` unexported string AnotherField string `yaml:"another_field" protobuf:"3"` LastField string `protobuf:"7"` } type MySpec struct{} type MySpecSpec2 struct { unexported string MySpec unexported2 string } type MySpecSpec3 struct { NewField string `yaml:"new_field"` } // MyCustomStruct is a custom struct // //gotagsrewrite:gen type MyCustomStruct struct { NewField string `yaml:"new_field" protobuf:"1"` } type ( //gotagsrewrite:gen MyAnotherStruct struct { NewField string `yaml:"new_field" protobuf:"1"` } //gotagsrewrite:gen MyBasicStruct struct { NewField string `yaml:"new_field" protobuf:"1"` } MyNoneStruct struct { NewField string `yaml:"new_field"` } ) //gotagsrewrite:gen type ( MyOnlyStruct struct { NewField string `yaml:"new_field" protobuf:"1"` } ) //gotagsrewrite:gen type ( MyOnlyStruct2 struct { NewField string `yaml:"new_field"` } MyOnlyStruct3 struct { NewField string `yaml:"new_field"` } ) ================================================ FILE: tools/gotagsrewrite/testdata/a.orig ================================================ package testdata //gotagsrewrite:gen type MySpecSpec struct { NewField string `yaml:"new_field"` FirstField string Name string `yaml:"string"` unexported string AnotherField string `yaml:"another_field" protobuf:"3"` LastField string } type MySpec struct{} type MySpecSpec2 struct { unexported string MySpec unexported2 string } type MySpecSpec3 struct { NewField string `yaml:"new_field"` } // MyCustomStruct is a custom struct //gotagsrewrite:gen type MyCustomStruct struct { NewField string `yaml:"new_field"` } type ( //gotagsrewrite:gen MyAnotherStruct struct { NewField string `yaml:"new_field"` } //gotagsrewrite:gen MyBasicStruct struct { NewField string `yaml:"new_field"` } MyNoneStruct struct { NewField string `yaml:"new_field"` } ) //gotagsrewrite:gen type ( MyOnlyStruct struct { NewField string `yaml:"new_field"` } ) //gotagsrewrite:gen type ( MyOnlyStruct2 struct { NewField string `yaml:"new_field"` } MyOnlyStruct3 struct { NewField string `yaml:"new_field"` } ) ================================================ FILE: tools/structprotogen/ast/ast.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package ast is used to find all structs with expected tag in the given AST. package ast import ( "fmt" "go/ast" "go/token" "strconv" "strings" "unicode" "github.com/fatih/structtag" "golang.org/x/tools/go/packages" ) // FindAllTaggedStructs extracts all structs with comments which contain tag in the given AST. func FindAllTaggedStructs(pkgs []*packages.Package) TaggedStructs { result := TaggedStructs{} for _, pkg := range pkgs { for _, file := range pkg.Syntax { structs := findAllStructs(file) for _, structDef := range structs { result.Add(pkg.PkgPath, structDef.Struct.Name.Name, TaggedStruct{ Comments: formatComments(structDef.Comment), Fields: structDef.Fields, }) } } } return result } // TaggedStructs is a map of tagged struct declarations to their data. type TaggedStructs map[StructDecl]TaggedStruct // TaggedStruct contains struct comments and fields. type TaggedStruct struct { Comments []string Fields Fields } // Get returns the struct data for given pkg and pkgPath. func (t TaggedStructs) Get(pkg, name string) (TaggedStruct, bool) { val, ok := t[StructDecl{pkg, name}] return val, ok } // Add adds the given struct data to the map. func (t TaggedStructs) Add(pkg, name string, structData TaggedStruct) { t[StructDecl{Pkg: pkg, Name: name}] = structData } // findAllStructs finds all structs with comments in the given AST. func findAllStructs(f ast.Node) []structData { var result []structData ast.Inspect(f, func(n ast.Node) bool { genDecl, ok := n.(*ast.GenDecl) if !ok || genDecl.Tok != token.TYPE { return true } typeSpecs := filter(genDecl.Specs, typeSpecFilter) switch len(typeSpecs) { case 0: return false case 1: switch { case isTarget(genDecl.Doc): fields := getStructFieldsWithTags(typeSpecs[0]) result = append(result, structData{ Struct: typeSpecs[0], Comment: genDecl.Doc, Fields: fields, }) case isTarget(typeSpecs[0].Doc): fields := getStructFieldsWithTags(typeSpecs[0]) result = append(result, structData{ Struct: typeSpecs[0], Comment: typeSpecs[0].Doc, Fields: fields, }) } return false default: for _, typeSpec := range typeSpecs { if isTarget(typeSpec.Doc) { fields := getStructFieldsWithTags(typeSpec) result = append(result, structData{ Struct: typeSpec, Comment: typeSpec.Doc, Fields: fields, }) } } return false } }) return result } type structData struct { Struct *ast.TypeSpec Comment *ast.CommentGroup Fields Fields } func typeSpecFilter(n ast.Spec) (*ast.TypeSpec, bool) { typeSpec, ok := n.(*ast.TypeSpec) if !ok { return nil, false } structType, ok := typeSpec.Type.(*ast.StructType) if !ok { return nil, false } if structType.Fields.NumFields() == 0 { return nil, false } if !isCapitalCase(typeSpec.Name.Name) { return nil, false } return typeSpec, true } // isCapitalCase returns true if the given string is in capital case. func isCapitalCase(s string) bool { return len(s) > 0 && unicode.IsUpper(rune(s[0])) } func isTarget(doc *ast.CommentGroup) bool { if doc == nil { return false } for _, c := range doc.List { if isTargetComment(c.Text) { return true } } return false } func isTargetComment(str string) bool { return str == "//gotagsrewrite:gen" } func filter[T, V any](slc []T, f func(n T) (V, bool)) []V { var result []V for _, v := range slc { res, ok := f(v) if ok { result = append(result, res) } } return result } // StructDecl is a struct declaration. type StructDecl struct { Pkg string Name string } func formatComments(comment *ast.CommentGroup) []string { if comment == nil { return nil } result := make([]string, 0, len(comment.List)) for i := range comment.List { if len(comment.List)-1 >= i+1 && isTargetComment(comment.List[i+1].Text) { continue } if isTargetComment(comment.List[i].Text) { continue } result = append(result, comment.List[i].Text) } return result } // Fields represents a struct field and its protobuf number. type Fields map[string]int // getStructFieldsWithTags returns all fields of the given struct with their tags. func getStructFieldsWithTags(structDecl *ast.TypeSpec) Fields { result := Fields{} structType := structDecl.Type.(*ast.StructType) //nolint:errcheck for _, field := range structType.Fields.List { if field.Names == nil { continue } for _, name := range field.Names { if field.Tag == nil { continue } tagValue := strings.Trim(field.Tag.Value, "`") tags, err := structtag.Parse(tagValue) if err != nil { panic(fmt.Errorf("invalid tag: field '%s', tag '%s': %w", name, tagValue, err)) } tag, err := tags.Get("protobuf") if err != nil { panic(fmt.Errorf("cannot find protobuf tag: field '%s', tag '%s': %w", name, tagValue, err)) } num, err := strconv.Atoi(tag.Name) if err != nil { panic(fmt.Errorf("invalid protobuf tag: field '%s', tag '%s': %w", name, tagValue, err)) } result[name.Name] = num } } return result } ================================================ FILE: tools/structprotogen/consts/consts.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package consts is used to find all consts with expected tag in the given AST. package consts import ( "fmt" "go/ast" "go/token" "go/types" "io" "regexp" "slices" "strconv" "strings" "golang.org/x/tools/go/packages" ) const tag = "structprotogen:gen_enum" // FindIn looks up all const blocks with the specific comment in the given packages. // //nolint:gocyclo func FindIn(pkgs []*packages.Package) (ConstBlocks, error) { var result ConstBlocks for _, pkg := range pkgs { for _, f := range pkg.Syntax { for _, constBlock := range findGenDecls(f.Decls) { var consts []Constant var typeData typeData valueSpecs := filter(constBlock.Specs, func(spec ast.Spec) (*ast.ValueSpec, bool) { valueSpec, ok := spec.(*ast.ValueSpec) return valueSpec, ok }) for _, valueSpec := range valueSpecs { for _, name := range valueSpec.Names { def := pkg.TypesInfo.Defs[name] if !def.Exported() { continue } td, err := getTypeData(pkg.Syntax, def.Type()) if err != nil { return nil, fmt.Errorf("%s: const named '%s': %w", pkg.PkgPath, def.Name(), err) } if typeData.name == "" { typeData = td } else if typeData.name != td.name { return nil, fmt.Errorf("const type mismatch: %s != %s", typeData.name, def.Type().String()) } val, err := getValue(def) if err != nil { return nil, err } consts = append(consts, Constant{ Name: name.Name, Value: val, CommentLines: commentToStrings(valueSpec.Doc), }) } } if len(consts) == 0 { return nil, fmt.Errorf("%s: const block with no exported consts", pkg.PkgPath) } result = append(result, ConstBlock{ TypeName: typeData.name, TypePkg: typeData.pkgName, TypePath: typeData.pkgPath, CommentLines: typeData.comments, Consts: consts, }) } } } return result, nil } func getValue(obj types.Object) (string, error) { result := obj.(*types.Const).Val().String() _, err := strconv.Atoi(result) if err != nil { return "", fmt.Errorf("value %s is not an integer: %s", obj.Name(), result) } return result, nil } func findGenDecls(decl []ast.Decl) []*ast.GenDecl { return filter(decl, func(decl ast.Decl) (*ast.GenDecl, bool) { genDecl, ok := decl.(*ast.GenDecl) if !ok || genDecl.Tok != token.CONST || genDecl.Lparen == token.NoPos || // single const declaration, ignore len(genDecl.Specs) == 0 { return nil, false } strs := commentToStrings(genDecl.Doc) if len(strs) == 0 { return nil, false } if findInStrings(strs, tag) == -1 { return nil, false } return genDecl, true }) } // findInStrings finds a string in a list of strings. func findInStrings(strs []string, find string) int { for i, str := range strs { if strings.Contains(str, find) { return i } } return -1 } func getTypeData(files []*ast.File, t types.Type) (typeData, error) { switch t := t.(type) { case *types.Named: commentGroup, err := findTypeComment(files, t.Obj().Name()) if err != nil { return typeData{}, err } return typeData{ name: t.Obj().Name(), pkgName: t.Obj().Pkg().Name(), pkgPath: t.Obj().Pkg().Path(), comments: commentToStrings(commentGroup), }, nil default: return typeData{}, fmt.Errorf("unsupported type: %s", t.String()) } } type typeData struct { name string pkgName string pkgPath string comments []string } func findTypeComment(files []*ast.File, typeName string) (*ast.CommentGroup, error) { for _, f := range files { for _, decl := range f.Decls { genDecl, ok := decl.(*ast.GenDecl) if !ok || genDecl.Tok != token.TYPE || len(genDecl.Specs) == 0 { continue } for _, spec := range genDecl.Specs { typeSpec, ok := spec.(*ast.TypeSpec) if !ok { continue } if typeSpec.Name.Name == typeName { return genDecl.Doc, nil } } } } return nil, fmt.Errorf("type %s not found", typeName) } // commentToStrings converts a list of comments to a list of strings. func commentToStrings(doc *ast.CommentGroup) []string { if doc == nil { return nil } result := make([]string, 0, len(doc.List)) for _, c := range doc.List { result = append(result, c.Text) } return result } // ConstBlock is a block of constants. type ConstBlock struct { TypeName string TypePkg string TypePath string CommentLines []string Consts []Constant } // ProtoMessageName returns the name of the proto message for this const block. func (b *ConstBlock) ProtoMessageName() string { return strings.Title(b.TypePkg) + strings.Title(b.TypeName) //nolint:staticcheck } // Constant represents a constant. type Constant struct { Name string Value string CommentLines []string } // ConstBlocks is a slice of ConstBlock. type ConstBlocks []ConstBlock // FormatProtoFile generates proto file from the list of ConstBlocks. func (b *ConstBlocks) FormatProtoFile(w io.Writer) error { fmt.Fprint(w, "syntax = \"proto3\";\n\n") fmt.Fprint(w, "package talos.resource.definitions.enums;\n\n") fmt.Fprint(w, `option go_package = "github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/enums";`+"\n") fmt.Fprint(w, `option java_package = "dev.talos.api.resource.definitions.enums";`+"\n\n") for _, block := range *b { for _, comment := range block.CommentLines { fmt.Fprintln(w, strings.ReplaceAll(comment, " "+block.TypeName+" ", " "+block.ProtoMessageName()+" ")) } fmt.Fprintf(w, "enum %s {\n", block.ProtoMessageName()) hasZeroNotFirstConstValue := slices.IndexFunc(block.Consts, func(c Constant) bool { return c.Value == "0" }) > 0 if hasDuplicates(block.Consts, func(c Constant) string { return c.Value }) || hasZeroNotFirstConstValue { fmt.Fprintln(w, " option allow_alias = true;") } for i, constant := range block.Consts { for _, comment := range constant.CommentLines { fmt.Fprintln(w, " ", comment) } if i == 0 && constant.Value != "0" { fmt.Fprintf(w, " %s_%s_UNSPECIFIED = 0;\n", strings.ToUpper(block.TypePkg), strings.ToUpper(block.TypeName), ) } fmt.Fprintf(w, " %s = %s;\n", toCapitalSnakeCase(constant.Name), constant.Value) } fmt.Fprintf(w, "}\n\n") } return nil } // HaveType returns true if the list of ConstBlocks contains a block with the given type. func (b *ConstBlocks) HaveType(pkgPath, typeName string) bool { _, ok := b.Get(pkgPath, typeName) return ok } // Get returns a ConstBlock for a given type. func (b *ConstBlocks) Get(pkgPath, typeName string) (ConstBlock, bool) { for _, block := range *b { if block.TypePath == pkgPath && block.TypeName == typeName { return block, true } } return ConstBlock{}, false } func hasDuplicates[T any, K comparable](slc []T, fn func(T) K) bool { seen := make(map[K]struct{}, len(slc)) for _, elem := range slc { k := fn(elem) if _, ok := seen[k]; ok { return true } seen[k] = struct{}{} } return false } // toCapitalSnakeCase converts a string to a capital snake case. func toCapitalSnakeCase(str string) string { snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") snake = strings.ToUpper(snake) // special case for "SomethingsIps" if strings.HasSuffix(snake, "_i_ps") { snake = strings.TrimSuffix(snake, "_i_ps") + "_ips" } return snake } var ( matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") ) func filter[T, V any](slc []T, f func(n T) (V, bool)) []V { var result []V for _, v := range slc { res, ok := f(v) if ok { result = append(result, res) } } return result } ================================================ FILE: tools/structprotogen/go.mod ================================================ module github.com/siderolabs/talos/tools/structprotogen go 1.26.0 require ( github.com/fatih/structtag v1.2.0 github.com/spf13/cobra v1.10.2 golang.org/x/tools v0.43.0 gopkg.in/typ.v4 v4.4.0 ) require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/spf13/pflag v1.0.9 // indirect golang.org/x/mod v0.34.0 // indirect golang.org/x/sync v0.20.0 // indirect ) ================================================ FILE: tools/structprotogen/go.sum ================================================ github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/typ.v4 v4.4.0 h1:O9vTueEmZd0iA9DF+g2wXeNCeloN2TOpxu6FXKl3AqM= gopkg.in/typ.v4 v4.4.0/go.mod h1:wolXe8DlewxRCjA7SOiT3zjrZ0eQJZcr8cmV6bQWJUM= ================================================ FILE: tools/structprotogen/loader/loader.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package loader is used to load all packages from the given path. package loader import ( "errors" "fmt" "strings" "golang.org/x/tools/go/packages" ) // LoadPackages loads all packages from the given path. func LoadPackages(pkgPath string) ([]*packages.Package, error) { cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedDeps} pkgs, err := packages.Load(cfg, pkgPath) if err != nil { return nil, fmt.Errorf("failed to load pkgs from '%s': %w", pkgPath, err) } if len(pkgs) == 0 { return nil, errors.New("no packages found") } err = collectErrors(pkgs) if err != nil { return nil, fmt.Errorf("error during processing '%s' packages: %w", pkgPath, err) } return pkgs, nil } func collectErrors(pkgs []*packages.Package) error { var ( builder strings.Builder n int ) packages.Visit(pkgs, nil, func(pkg *packages.Package) { for _, err := range pkg.Errors { fmt.Fprintf(&builder, "Error '%d': %s\n", n, err) n++ } }) if n > 0 { return errors.New(builder.String()) } return nil } ================================================ FILE: tools/structprotogen/main.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // structprotogen is a tool to generate proto files from Go structs. package main //nolint:gci import ( "errors" "fmt" "io/fs" "os" "path" "path/filepath" "github.com/spf13/cobra" "github.com/siderolabs/talos/tools/structprotogen/ast" "github.com/siderolabs/talos/tools/structprotogen/consts" "github.com/siderolabs/talos/tools/structprotogen/loader" "github.com/siderolabs/talos/tools/structprotogen/proto" "github.com/siderolabs/talos/tools/structprotogen/types" ) // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "structprotogen path dest", Short: "This CLI is used to generate proto files from Go structs into one proto file", Example: "structprotogen github.com/siderolabs/talos/pkg/machinery/resources/... ./api/resource/definitions", Args: cobra.ExactArgs(2), Version: "v1.0.0", RunE: func(cmd *cobra.Command, args []string) error { return run(args[0], args[1]) }, SilenceUsage: true, DisableAutoGenTag: true, } func main() { err := rootCmd.Execute() if err != nil { os.Exit(1) } } // TODO(DmitriyMV): get comments for fields //nolint:gocyclo func run(pkgPath, dst string) error { loadedPkgs, err := loader.LoadPackages(pkgPath) if err != nil { return err } constants, err := consts.FindIn(loadedPkgs) if err != nil { return err } taggedStructs := ast.FindAllTaggedStructs(loadedPkgs) printFoundStructs(taggedStructs) sortedPkgs, err := types.FindPkgDecls(taggedStructs, loadedPkgs) if err != nil { return fmt.Errorf("error finding path '%s' declarations: %w", pkgPath, err) } pkgsTypes, err := types.ParseDeclsData(sortedPkgs, taggedStructs) if err != nil { return fmt.Errorf("error parsing path '%s' declarations data: %w", pkgPath, err) } externalTypes := types.FindExternalTypes(pkgsTypes, taggedStructs) for i := 0; i < externalTypes.Len(); i++ { externalType := externalTypes.Get(i) if constants.HaveType(externalType.Pkg, externalType.Name) { continue } if !proto.IsSupportedExternalType(externalType) { return fmt.Errorf("external type '%s.%s' is not supported", externalType.Pkg, externalType.Name) } } data := proto.PrepareProtoData(pkgsTypes, constants) for i := 0; i < data.Len(); i++ { protoData := data.Get(i) fmt.Println("--------") protoData.WriteDebug(os.Stdout) } err = os.MkdirAll(dst, 0o755) if err != nil { return fmt.Errorf("failed to create directory for proto files: %w", err) } if len(constants) > 0 { err = withFile(filepath.Join(dst, "enums", "enums.proto"), func(f *os.File) error { return constants.FormatProtoFile(f) }) if err != nil { return fmt.Errorf("failed to write enums proto file: %w", err) } } for i := 0; i < data.Len(); i++ { protoData := data.Get(i) dstDir, err := filepath.Abs(filepath.Join(dst, protoData.Name)) if err != nil { return fmt.Errorf("failed to get absolute path for pkg '%s': %w", protoData.GoPkg, err) } err = os.MkdirAll(dstDir, 0o755) if err != nil { return fmt.Errorf("failed to create directory for pkg '%s' proto files: %w", protoData.GoPkg, err) } dstFile, err := filepath.Abs(filepath.Join(dstDir, path.Base(protoData.GoPkg)+".proto")) if err != nil { return fmt.Errorf("failed to get absolute path for destination file: %w", err) } fmt.Println("writing file", dstFile) err = withFile(dstFile, func(f *os.File) error { protoData.Format(f) return nil }) if err != nil { return fmt.Errorf("failed to write file '%s': %w", dstFile, err) } } return nil } func printFoundStructs(structs ast.TaggedStructs) { for decl := range structs { fmt.Printf("found tagged struct '%s' in pkg '%s'\n", decl.Name, decl.Pkg) } } func withFile(filename string, fn func(f *os.File) error) error { dir := filepath.Dir(filename) _, err := os.Stat(dir) if errors.Is(err, fs.ErrNotExist) { err = os.MkdirAll(dir, 0o755) if err != nil { return err } } else if err != nil { return err } file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { return err } defer func() { err := file.Close() if err != nil { fmt.Println("failed to close file:", err) } }() return fn(file) } ================================================ FILE: tools/structprotogen/proto/proto.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package proto contains the protobuf generation logic. package proto //nolint:gci import ( "fmt" "io" "regexp" "strings" "gopkg.in/typ.v4/slices" "github.com/siderolabs/talos/tools/structprotogen/consts" "github.com/siderolabs/talos/tools/structprotogen/sliceutil" "github.com/siderolabs/talos/tools/structprotogen/types" ) // Pkg represents a protobuf package. type Pkg struct { Name string GoPkg string isInit bool protoDefs slices.Sorted[*protoDef] imports slices.Sorted[string] } func protoPkgsCmp(left, right *Pkg) int { return strings.Compare(left.Name, right.Name) } func (p *Pkg) init() { if !p.isInit { p.protoDefs = slices.NewSortedCompare([]*protoDef{}, protoDefCmp) p.imports = slices.NewSortedCompare([]string{}, strings.Compare) p.isInit = true } } // Defs returns the list of definitions. func (p *Pkg) Defs() *slices.Sorted[*protoDef] { p.init() return &p.protoDefs } // Imports returns the list of imports. func (p *Pkg) Imports() *slices.Sorted[string] { p.init() return &p.imports } // WriteDebug is like Format, but writes additional debug info. func (p *Pkg) WriteDebug(w io.Writer) { pkgName := p.Name fmt.Fprint(w, "syntax = \"proto3\";\n\n") fmt.Fprintf(w, "package talos.resource.definitions.%s; // %s\n\n", p.Name, p.GoPkg) fmt.Fprintf(w, "option go_package = \"github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/%s\";\n", pkgName) // TODO: insert proper path fmt.Fprintf(w, "option java_package = \"dev.talos.api.resource.definitions.%s\";\n\n", pkgName) if p.imports.Len() > 0 { for i := 0; i < p.imports.Len(); i++ { importPath := p.imports.Get(i) if !strings.ContainsRune(importPath, '.') { importPath = "talos.resource.definitions." + importPath } fmt.Fprintf(w, "import \"%s\";\n", importPath) } fmt.Fprintln(w, ``) } for i := 0; i < p.protoDefs.Len(); i++ { p.protoDefs.Get(i).WriteDebug(w) fmt.Fprintln(w) } } // Format formats the protobuf data. func (p *Pkg) Format(w io.Writer) { pkgName := p.Name fmt.Fprint(w, "syntax = \"proto3\";\n\n") fmt.Fprintf(w, "package talos.resource.definitions.%s;\n\n", p.Name) fmt.Fprintf(w, "option go_package = \"github.com/siderolabs/talos/pkg/machinery/api/resource/definitions/%s\";\n", pkgName) // TODO: insert proper path fmt.Fprintf(w, "option java_package = \"dev.talos.api.resource.definitions.%s\";\n\n", pkgName) if p.imports.Len() > 0 { for i := 0; i < p.imports.Len(); i++ { importPath := p.imports.Get(i) if !strings.ContainsRune(importPath, '.') { importPath = "talos.resource.definitions." + importPath } fmt.Fprintf(w, "import \"%s\";\n", importPath) } fmt.Fprintln(w, ``) } for i := 0; i < p.protoDefs.Len(); i++ { p.protoDefs.Get(i).Format(w) fmt.Fprintln(w) } } type protoDef struct { name string goPkg string comments []string isInit bool fields slices.Sorted[protoField] } func protoDefCmp(left, right *protoDef) int { return strings.Compare(left.name, right.name) } func (p *protoDef) init() { if !p.isInit { p.fields = slices.NewSortedCompare([]protoField{}, protoFieldCmp) p.isInit = true } } func (p *protoDef) Fields() *slices.Sorted[protoField] { p.init() return &p.fields } func (p *protoDef) WriteDebug(w io.Writer) { for _, comment := range p.comments { fmt.Fprintf(w, "%s\n", comment) } fmt.Fprintf(w, "message %s { //%s.%s\n", p.name, p.goPkg, p.name) for i := 0; i < p.fields.Len(); i++ { fmt.Fprintf(w, " ") p.fields.Get(i).WriteDebug(w) } fmt.Fprintln(w, "}") } func (p *protoDef) Format(w io.Writer) { for _, comment := range p.comments { fmt.Fprintf(w, "%s\n", comment) } fmt.Fprintf(w, "message %s {\n", p.name) for i := 0; i < p.fields.Len(); i++ { fmt.Fprintf(w, " ") p.fields.Get(i).Format(w) } fmt.Fprintln(w, "}") } type protoField struct { name string typ string num int goType string } func protoFieldCmp(left, right protoField) int { if left.num == 0 { panic(fmt.Errorf("left field '%s' has no number", left.name)) } if right.num == 0 { panic(fmt.Errorf("right field '%s' has no number", right.name)) } switch { case left.num < right.num: return -1 case left.num > right.num: return 1 default: return 0 } } func (pf protoField) WriteDebug(w io.Writer) { fmt.Fprintf(w, "%s %s = %d; // %s \n", pf.typ, ToSnakeCase(pf.name), pf.num, pf.goType) } func (pf protoField) Format(w io.Writer) { fmt.Fprintf(w, "%s %s = %d;\n", pf.typ, ToSnakeCase(pf.name), pf.num) } // PrepareProtoData prepares the data for the protobuf generation. // //nolint:gocyclo,cyclop func PrepareProtoData(pkgsTypes slices.Sorted[*types.Type], constants consts.ConstBlocks) slices.Sorted[*Pkg] { result := slices.NewSortedCompare([]*Pkg{}, protoPkgsCmp) for i := 0; i < pkgsTypes.Len(); i++ { pkgType := pkgsTypes.Get(i) protoPkg := sliceutil.GetOrAdd(&result, &Pkg{ Name: pkgType.PkgName(), GoPkg: pkgType.Pkg, }) def := sliceutil.GetOrAdd(protoPkg.Defs(), &protoDef{ name: pkgType.Name, goPkg: pkgType.Pkg, comments: pkgType.Comments, }) for j := 0; j < pkgType.Fields().Len(); j++ { field := pkgType.Fields().Get(j) fieldTypeData := types.TypeInfo(field.TypeData.Type()) if fieldTyp, ok := types.MatchTypeData[types.Complex](fieldTypeData); ok { importName, typeName := mustFormatTypeName(fieldTyp.Pkg, fieldTyp.Name, pkgType.Pkg) if importName != "" { sliceutil.AddIfNotFound(protoPkg.Imports(), importName) } sliceutil.AddIfNotFound(def.Fields(), protoField{ name: field.Name, typ: typeName, num: field.Num, goType: field.TypeData.Type().String(), }) continue } if fieldTyp, ok := types.MatchTypeData[types.Basic](fieldTypeData); ok { var importName, typeName string if block, ok := constants.Get(fieldTyp.Pkg, fieldTyp.Name); ok { importName = "resource/definitions/enums/enums.proto" typeName = "talos.resource.definitions.enums." + block.ProtoMessageName() } else { importName, typeName = mustFormatBasicTypeName(fieldTyp.Pkg, fieldTyp.Name) } if importName != "" { sliceutil.AddIfNotFound(protoPkg.Imports(), importName) } sliceutil.AddIfNotFound(def.Fields(), protoField{ name: field.Name, typ: typeName, num: field.Num, goType: field.TypeData.Type().String(), }) continue } if fieldTyp, ok := types.MatchTypeData[types.Slice](fieldTypeData); ok { var importName, typeName string block, isEnum := constants.Get(fieldTyp.Pkg, fieldTyp.Name) switch { case isEnum: importName = "resource/definitions/enums/enums.proto" typeName = "repeated talos.resource.definitions.enums." + block.ProtoMessageName() case fieldTyp.Pkg == "" && fieldTyp.Name == "byte" && fieldTyp.Is2DSlice: //nolint:goconst typeName = "repeated bytes" case fieldTyp.Pkg == "" && fieldTyp.Name == "byte": typeName = "bytes" case fieldTyp.Pkg == "": typeName = fmt.Sprintf("repeated %s", getProtoBasicName(fieldTyp.Name)) default: importName, typeName = mustFormatTypeName(fieldTyp.Pkg, fieldTyp.Name, pkgType.Pkg) typeName = fmt.Sprintf("repeated %s", typeName) } if importName != "" { sliceutil.AddIfNotFound(protoPkg.Imports(), importName) } sliceutil.AddIfNotFound(def.Fields(), protoField{ name: field.Name, typ: typeName, num: field.Num, goType: field.TypeData.Type().String(), }) continue } if fieldTyp, ok := types.MatchTypeData[types.Map](fieldTypeData); ok { // key cannot be anything but a basic type importKeyName, keyTypeName := mustFormatBasicTypeName(fieldTyp.KeyTypePkg, fieldTyp.KeyTypeName) if importKeyName != "" { panic(fmt.Errorf("map key type '%s.%s' is not basic type", fieldTyp.KeyTypePkg, fieldTyp.KeyTypeName)) } var ( typText string importElem string ) switch { case fieldTyp.ElemTypeName == "interface{}": // handle map[key]interface{} importElem = "google/protobuf/struct.proto" typText = "google.protobuf.Struct" case fieldTyp.ElemTypePkg == "": elemTypeName := getProtoBasicName(fieldTyp.ElemTypeName) typText = fmt.Sprintf("map<%s, %s>", keyTypeName, elemTypeName) case fieldTyp.ElemTypePkg == pkgType.Pkg: var elemTypeName string importElem, elemTypeName = mustFormatTypeName(fieldTyp.ElemTypePkg, fieldTyp.ElemTypeName, pkgType.Pkg) typText = fmt.Sprintf("map<%s, %s>", keyTypeName, elemTypeName) default: panic(fmt.Errorf("map value type '%s.%s' is not known type", fieldTyp.ElemTypePkg, fieldTyp.ElemTypeName)) } if importElem != "" { sliceutil.AddIfNotFound(protoPkg.Imports(), importElem) } sliceutil.AddIfNotFound(def.Fields(), protoField{ name: field.Name, typ: typText, num: field.Num, goType: field.TypeData.Type().String(), }) continue } } } return result } func mustFormatTypeName(fieldTypePkg string, fieldType string, declPkg string) (string, string) { importPath, name := formatTypeName(fieldTypePkg, fieldType, declPkg) if name == "" { panic(fmt.Errorf("unknown type %s.%s", fieldTypePkg, fieldType)) } return importPath, name } func formatTypeName(fieldTypePkg string, fieldType string, declPkg string) (string, string) { if fieldTypePkg == declPkg { return "", fieldType } type typeData struct { pkg string name string } td := typeData{ name: fieldType, pkg: fieldTypePkg, } const commoProto = "common/common.proto" switch td { case typeData{"time", "Time"}: return "google/protobuf/timestamp.proto", "google.protobuf.Timestamp" case typeData{"net/url", "URL"}: return commoProto, "common.URL" case typeData{"net/netip", "Prefix"}: return commoProto, "common.NetIPPrefix" case typeData{"net/netip", "AddrPort"}: return commoProto, "common.NetIPPort" case typeData{"net/netip", "Addr"}: return commoProto, "common.NetIP" case typeData{"github.com/opencontainers/runtime-spec/specs-go", "Mount"}: return "resource/definitions/proto/proto.proto", "talos.resource.definitions.proto.Mount" case typeData{"github.com/siderolabs/crypto/x509", "PEMEncodedCertificateAndKey"}: return commoProto, "common.PEMEncodedCertificateAndKey" case typeData{"github.com/siderolabs/crypto/x509", "PEMEncodedKey"}: return commoProto, "common.PEMEncodedKey" case typeData{"github.com/siderolabs/crypto/x509", "PEMEncodedCertificate"}: return commoProto, "common.PEMEncodedCertificate" case typeData{"github.com/siderolabs/talos/pkg/machinery/cel", "Expression"}: return "google/api/expr/v1alpha1/checked.proto", "google.api.expr.v1alpha1.CheckedExpr" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/cri", "RegistryMirrorConfig"}: // This is a hack, but I (Dmitry) don't have enough patience to figure out why we don't support complex maps return "resource/definitions/cri/registry.proto", "talos.resource.definitions.cri.RegistryMirrorConfig" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/cri", "RegistryAuthConfig"}: // This is a hack, but I (Dmitry) don't have enough patience to figure out why we don't support complex maps return "resource/definitions/cri/registry.proto", "talos.resource.definitions.cri.RegistryAuthConfig" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/cri", "RegistryTLSConfig"}: // This is a hack, but I (Dmitry) don't have enough patience to figure out why we don't support complex maps return "resource/definitions/cri/registry.proto", "talos.resource.definitions.cri.RegistryTLSConfig" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/runtime", "PlatformMetadataSpec"}: return "resource/definitions/runtime/runtime.proto", "talos.resource.definitions.runtime.PlatformMetadataSpec" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/block", "ParameterSpec"}: return "resource/definitions/block/block.proto", "talos.resource.definitions.block.ParameterSpec" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/etcd", "ArgValues"}: return "resource/definitions/etcd/etcd.proto", "talos.resource.definitions.etcd.ArgValues" case typeData{"github.com/siderolabs/talos/pkg/machinery/resources/k8s", "ArgValues"}: return "resource/definitions/k8s/k8s.proto", "talos.resource.definitions.k8s.ArgValues" default: return "", "" } } func mustFormatBasicTypeName(fieldTypePkg string, fieldType string) (string, string) { if fieldTypePkg == "" { return "", getProtoBasicName(fieldType) } importPath, fullName := formatBasicTypeName(fieldTypePkg, fieldType) if fullName == "" { panic(fmt.Errorf("unknown type %s.%s", fieldTypePkg, fieldType)) } return importPath, fullName } // IsSupportedExternalType checks if external type is supported. func IsSupportedExternalType(typ types.ExternalType) bool { if _, name := formatBasicTypeName(typ.Pkg, typ.Name); name != "" { return true } if _, name := formatTypeName(typ.Pkg, typ.Name, ""); name != "" { return true } return false } //nolint:gocyclo,cyclop func formatBasicTypeName(typPkg string, typ string) (importPath, fullName string) { type typeData struct { pkg string name string } td := typeData{ name: typ, pkg: typPkg, } switch td { case typeData{"time", "Duration"}: return "google/protobuf/duration.proto", "google.protobuf.Duration" case typeData{"io/fs", "FileMode"}: return "", "uint32" //nolint:goconst case typeData{"github.com/siderolabs/talos/pkg/machinery/nethelpers", "AddressFlags"}: return "", "uint32" case typeData{"github.com/siderolabs/talos/pkg/machinery/nethelpers", "LinkFlags"}: return "", "uint32" case typeData{"github.com/siderolabs/talos/pkg/machinery/nethelpers", "RouteFlags"}: return "", "uint32" default: return "", "" } } //nolint:gocyclo func getProtoBasicName(typ string) string { // Note: this should match: https://github.com/siderolabs/protoenc/blob/4b9363e618950aa6243872c640d89578c68fd1ed/marshal.go#L168-L204 // // Note: protoenc marshals to protowire, not proto3, so for mapping see: https://protobuf.dev/programming-guides/encoding/#structure switch typ { case "bool": return "bool" case "int8", "int16", "int32": return "int32" case "int64", "int": return "int64" case "byte", "uint8", "uint16", "uint32": return "uint32" case "uint64", "uint": return "uint64" case "float32": return "float" case "float64": return "double" case "string": return "string" default: panic(fmt.Sprintf("unknown type %s", typ)) } } // ToSnakeCase converts a string to snake case. func ToSnakeCase(str string) string { snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}") snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") snake = strings.ToLower(snake) // special case for "SomethingsIps" if strings.HasSuffix(snake, "_i_ps") { snake = strings.TrimSuffix(snake, "_i_ps") + "_ips" } return snake } var ( matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") ) ================================================ FILE: tools/structprotogen/sliceutil/sliceutils.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package sliceutil contains utility functions for slices. package sliceutil import "gopkg.in/typ.v4/slices" // GetOrAdd returns existing value or adds new one. func GetOrAdd[T any](slc *slices.Sorted[T], v T) T { var index int if index = slc.Index(v); index == -1 { index = slc.Add(v) } return slc.Get(index) } // AddIfNotFound adds value to the slice if it's not found. func AddIfNotFound[T any](slc *slices.Sorted[T], v T) { if index := slc.Index(v); index == -1 { slc.Add(v) } } ================================================ FILE: tools/structprotogen/types/types.go ================================================ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. // Package types contains utils to work with types. package types //nolint:gci import ( "errors" "fmt" "go/types" "path" "strings" "golang.org/x/tools/go/packages" "gopkg.in/typ.v4/slices" "github.com/siderolabs/talos/tools/structprotogen/ast" "github.com/siderolabs/talos/tools/structprotogen/sliceutil" ) // PkgDecl is a struct which contains package path and tagged struct declarations. type PkgDecl struct { path string decl []types.Object } func pkgCmp(left, right *PkgDecl) int { return strings.Compare(left.path, right.path) } // FindPkgDecls finds all declarations in the given packages. func FindPkgDecls(taggedStructs ast.TaggedStructs, loadedPkgs []*packages.Package) (slices.Sorted[*PkgDecl], error) { result := slices.NewSortedCompare([]*PkgDecl{}, pkgCmp) for _, loadedPkg := range loadedPkgs { forEachTaggedStruct(taggedStructs, loadedPkg, func(decl types.Object) { v := sliceutil.GetOrAdd(&result, &PkgDecl{ path: decl.Pkg().Path(), }) v.decl = append(v.decl, decl) }) } if result.Len() == 0 { return slices.Sorted[*PkgDecl]{}, errors.New("no definitions found") } return result, nil } func forEachTaggedStruct(taggedStructs ast.TaggedStructs, pkg *packages.Package, f func(decl types.Object)) { scope := pkg.Types.Scope() for _, name := range scope.Names() { _, ok := taggedStructs.Get(pkg.PkgPath, name) if !ok { continue } obj := scope.Lookup(name) // This is special resource - ignore it. if pkg.PkgPath == "github.com/siderolabs/talos/pkg/machinery/resources/network" && name == "DeviceConfigSpecSpec" { continue } f(obj) } } // Type is a struct which contains type pkg, name, comments and fields. type Type struct { Pkg string Name string Comments []string isInit bool fields slices.Sorted[FieldData] } // PkgName returns a package name for the given type. func (t *Type) PkgName() string { return path.Base(t.Pkg) } // Fields returns a list of fields for the given type. func (t *Type) Fields() *slices.Sorted[FieldData] { if !t.isInit { t.fields = slices.NewSortedCompare([]FieldData{}, fieldCmp) t.isInit = true } return &t.fields } func pkgTypeCmp(left, right *Type) int { pathCmp := strings.Compare(left.Pkg, right.Pkg) if pathCmp != 0 { return pathCmp } return strings.Compare(left.Name, right.Name) } // FieldData is a struct which contains field name, proto num and type data. type FieldData struct { Name string Num int TypeData *types.Var } func fieldCmp(left, right FieldData) int { return strings.Compare(left.Name, right.Name) } // ParseDeclsData parses all declarations and returns a list of packages with proper types. func ParseDeclsData(sortedPkgs slices.Sorted[*PkgDecl], taggedStructs ast.TaggedStructs) (slices.Sorted[*Type], error) { result := slices.NewSortedCompare([]*Type{}, pkgTypeCmp) for i := 0; i < sortedPkgs.Len(); i++ { pkg := sortedPkgs.Get(i) for _, decl := range pkg.decl { structName := decl.Name() structType, ok := decl.Type().Underlying().(*types.Struct) if !ok { return slices.Sorted[*Type]{}, fmt.Errorf("type %s is not a struct", structName) } taggedStruct, ok := taggedStructs.Get(pkg.path, structName) if !ok { return slices.Sorted[*Type]{}, fmt.Errorf("type %s is unknown struct", structName) } for j := 0; j < structType.NumFields(); j++ { field := structType.Field(j) if !field.Exported() { continue } v := sliceutil.GetOrAdd(&result, &Type{ Pkg: pkg.path, Name: structName, Comments: taggedStruct.Comments, }) v.Fields().Add(FieldData{ Name: field.Name(), Num: taggedStruct.Fields[field.Name()], TypeData: field, }) } } } return result, nil } // ExternalType is a struct which contains external type pkg and name. type ExternalType struct { Pkg string Name string } // String returns a string representation of the ExternalType. func (e ExternalType) String() string { return fmt.Sprintf("%s.%s", e.Pkg, e.Name) } func externalTypesCmp(left, right ExternalType) int { pkgCmp := strings.Compare(left.Pkg, right.Pkg) if pkgCmp != 0 { return pkgCmp } return strings.Compare(left.Name, right.Name) } // FindExternalTypes finds all external types in the given list of types. func FindExternalTypes(pkgsTypes slices.Sorted[*Type], taggedStructs ast.TaggedStructs) slices.Sorted[ExternalType] { result := slices.NewSortedCompare([]ExternalType{}, externalTypesCmp) for i := 0; i < pkgsTypes.Len(); i++ { typ := pkgsTypes.Get(i) for j := 0; j < typ.fields.Len(); j++ { field := typ.fields.Get(j) if !field.TypeData.Exported() { continue } typeData := TypeInfo(field.TypeData.Type()) if typePkg := typeData.typePkg(); typePkg != "" { typeName := typeData.typeName() if _, ok := taggedStructs.Get(typePkg, typeName); !ok { sliceutil.AddIfNotFound(&result, ExternalType{ Pkg: typePkg, Name: typeName, }) } } if typ, ok := MatchTypeData[Basic](typeData); ok && typ.Pkg != "" { sliceutil.AddIfNotFound(&result, ExternalType{ Pkg: typ.Pkg, Name: typ.Name, }) } if typ, ok := MatchTypeData[Map](typeData); ok && typ.ElemTypePkg != "" { sliceutil.AddIfNotFound(&result, ExternalType{ Pkg: typ.ElemTypePkg, Name: typ.ElemTypeName, }) } } } return result } // TypeInfo extracts all type data from the given type. // //nolint:gocyclo func TypeInfo(t types.Type) TypeInfoData { switch t := t.(type) { case *types.Slice: return makeSliceType(TypeInfo(t.Elem())) case *types.Map: return makeMapType(TypeInfo(t.Key()), TypeInfo(t.Elem())) case *types.Basic: return makeType[Basic]("", t.Name()) case *types.Pointer: return TypeInfo(t.Elem()) case *types.Alias: return TypeInfo(types.Unalias(t)) case *types.Named: if _, ok := t.Underlying().(*types.Basic); ok { return makeType[Basic](t.Obj().Pkg().Path(), t.Obj().Name()) } if underlying, ok := t.Underlying().(*types.Slice); ok { return makeSliceType(TypeInfo(underlying.Elem())) } return makeType[Complex](t.Obj().Pkg().Path(), t.Obj().Name()) case *types.Interface: return makeType[Complex]("", "interface{}") case *types.Struct: panic(fmt.Errorf("unsupported unnamed struct: %T", t)) case *types.Chan: panic(fmt.Errorf("unsupported type: %T", t)) default: panic(fmt.Errorf("unknown type: %T", t)) } } func makeSliceType(td TypeInfoData) TypeInfoData { isSliceInSlice := false if td, ok := MatchTypeData[Slice](td); ok { if td.Pkg != "" || td.Name != "byte" { panic(fmt.Errorf("unsupported slice in slice: %s.%s", td.Pkg, td.Name)) } isSliceInSlice = true } return MakeTypeData(Slice{ Pkg: td.typePkg(), Name: td.typeName(), Is2DSlice: isSliceInSlice, }) } func makeType[T Basic | Complex](pkg string, name string) TypeInfoData { return MakeTypeData(T{Pkg: pkg, Name: name}) } func makeMapType(key, elem TypeInfoData) TypeInfoData { return MakeTypeData(Map{ KeyTypePkg: key.typePkg(), KeyTypeName: key.typeName(), ElemTypePkg: elem.typePkg(), ElemTypeName: elem.typeName(), }) } // MakeTypeData creates a new TypeInfoData from the given type. func MakeTypeData[T Basic | Complex | Map | Slice](v T) TypeInfoData { return TypeInfoData{v: v} } // MatchTypeData matches the given TypeInfoData to the given type. func MatchTypeData[T Basic | Complex | Map | Slice](d TypeInfoData) (T, bool) { v, ok := d.v.(T) return v, ok } // TypeInfoData is a struct which contains type data. type TypeInfoData struct{ v any } func (td TypeInfoData) typeName() string { switch v := td.v.(type) { case Basic: return v.Name case Complex: return v.Name case Map: return v.KeyTypeName case Slice: return v.Name default: panic(fmt.Errorf("unknown type: %T", v)) } } func (td TypeInfoData) typePkg() string { switch v := td.v.(type) { case Basic: return v.Pkg case Complex: return v.Pkg case Map: return v.KeyTypePkg case Slice: return v.Pkg default: panic(fmt.Errorf("unknown type: %T", v)) } } // Basic is a basic type. type Basic struct { Name string Pkg string } // Complex is a complex type. type Complex struct { Name string Pkg string } // Slice is a slice type. type Slice struct { Name string Pkg string Is2DSlice bool } // Map is a map type. type Map struct { KeyTypeName string KeyTypePkg string ElemTypeName string ElemTypePkg string } ================================================ FILE: website/content/v1.13/reference/_index.md ================================================ --- title: "Reference" weight: 70 --- ================================================ FILE: website/content/v1.13/reference/api.md ================================================ --- title: API description: Talos gRPC API reference. --- ## Table of Contents - [common/common.proto](#common/common.proto) - [ContainerdInstance](#common.ContainerdInstance) - [Data](#common.Data) - [DataResponse](#common.DataResponse) - [Empty](#common.Empty) - [EmptyResponse](#common.EmptyResponse) - [Error](#common.Error) - [Metadata](#common.Metadata) - [NetIP](#common.NetIP) - [NetIPPort](#common.NetIPPort) - [NetIPPrefix](#common.NetIPPrefix) - [PEMEncodedCertificate](#common.PEMEncodedCertificate) - [PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) - [PEMEncodedKey](#common.PEMEncodedKey) - [URL](#common.URL) - [Code](#common.Code) - [ContainerDriver](#common.ContainerDriver) - [ContainerdNamespace](#common.ContainerdNamespace) - [File-level Extensions](#common/common.proto-extensions) - [cluster/cluster.proto](#cluster/cluster.proto) - [ClusterInfo](#cluster.ClusterInfo) - [HealthCheckProgress](#cluster.HealthCheckProgress) - [HealthCheckRequest](#cluster.HealthCheckRequest) - [ClusterService](#cluster.ClusterService) - [inspect/inspect.proto](#inspect/inspect.proto) - [ControllerDependencyEdge](#inspect.ControllerDependencyEdge) - [ControllerRuntimeDependenciesResponse](#inspect.ControllerRuntimeDependenciesResponse) - [ControllerRuntimeDependency](#inspect.ControllerRuntimeDependency) - [DependencyEdgeType](#inspect.DependencyEdgeType) - [InspectService](#inspect.InspectService) - [machine/debug.proto](#machine/debug.proto) - [DebugContainerRunRequest](#machine.DebugContainerRunRequest) - [DebugContainerRunRequestSpec](#machine.DebugContainerRunRequestSpec) - [DebugContainerRunRequestSpec.EnvEntry](#machine.DebugContainerRunRequestSpec.EnvEntry) - [DebugContainerRunResponse](#machine.DebugContainerRunResponse) - [DebugContainerTerminalResize](#machine.DebugContainerTerminalResize) - [DebugContainerRunRequestSpec.Profile](#machine.DebugContainerRunRequestSpec.Profile) - [DebugService](#machine.DebugService) - [machine/image.proto](#machine/image.proto) - [ImageServiceCredentials](#machine.ImageServiceCredentials) - [ImageServiceImportRequest](#machine.ImageServiceImportRequest) - [ImageServiceImportResponse](#machine.ImageServiceImportResponse) - [ImageServiceListRequest](#machine.ImageServiceListRequest) - [ImageServiceListResponse](#machine.ImageServiceListResponse) - [ImageServiceListResponse.LabelsEntry](#machine.ImageServiceListResponse.LabelsEntry) - [ImageServicePullLayerProgress](#machine.ImageServicePullLayerProgress) - [ImageServicePullProgress](#machine.ImageServicePullProgress) - [ImageServicePullRequest](#machine.ImageServicePullRequest) - [ImageServicePullResponse](#machine.ImageServicePullResponse) - [ImageServiceRemoveRequest](#machine.ImageServiceRemoveRequest) - [ImageServiceVerifyRequest](#machine.ImageServiceVerifyRequest) - [ImageServiceVerifyResponse](#machine.ImageServiceVerifyResponse) - [ImageServicePullLayerProgress.Status](#machine.ImageServicePullLayerProgress.Status) - [ImageService](#machine.ImageService) - [machine/lifecycle.proto](#machine/lifecycle.proto) - [InstallArtifactsSource](#machine.InstallArtifactsSource) - [InstallDestination](#machine.InstallDestination) - [LifecycleServiceInstallProgress](#machine.LifecycleServiceInstallProgress) - [LifecycleServiceInstallRequest](#machine.LifecycleServiceInstallRequest) - [LifecycleServiceInstallResponse](#machine.LifecycleServiceInstallResponse) - [LifecycleServiceUpgradeRequest](#machine.LifecycleServiceUpgradeRequest) - [LifecycleServiceUpgradeResponse](#machine.LifecycleServiceUpgradeResponse) - [LifecycleService](#machine.LifecycleService) - [machine/machine.proto](#machine/machine.proto) - [AddressEvent](#machine.AddressEvent) - [ApplyConfiguration](#machine.ApplyConfiguration) - [ApplyConfigurationRequest](#machine.ApplyConfigurationRequest) - [ApplyConfigurationResponse](#machine.ApplyConfigurationResponse) - [BPFInstruction](#machine.BPFInstruction) - [Bootstrap](#machine.Bootstrap) - [BootstrapRequest](#machine.BootstrapRequest) - [BootstrapResponse](#machine.BootstrapResponse) - [CNIConfig](#machine.CNIConfig) - [CPUFreqStats](#machine.CPUFreqStats) - [CPUFreqStatsResponse](#machine.CPUFreqStatsResponse) - [CPUInfo](#machine.CPUInfo) - [CPUInfoResponse](#machine.CPUInfoResponse) - [CPUStat](#machine.CPUStat) - [CPUsFreqStats](#machine.CPUsFreqStats) - [CPUsInfo](#machine.CPUsInfo) - [ClusterConfig](#machine.ClusterConfig) - [ClusterNetworkConfig](#machine.ClusterNetworkConfig) - [ConfigLoadErrorEvent](#machine.ConfigLoadErrorEvent) - [ConfigValidationErrorEvent](#machine.ConfigValidationErrorEvent) - [ConnectRecord](#machine.ConnectRecord) - [ConnectRecord.Process](#machine.ConnectRecord.Process) - [Container](#machine.Container) - [ContainerInfo](#machine.ContainerInfo) - [ContainersRequest](#machine.ContainersRequest) - [ContainersResponse](#machine.ContainersResponse) - [ControlPlaneConfig](#machine.ControlPlaneConfig) - [CopyRequest](#machine.CopyRequest) - [DHCPOptionsConfig](#machine.DHCPOptionsConfig) - [DiskStat](#machine.DiskStat) - [DiskStats](#machine.DiskStats) - [DiskStatsResponse](#machine.DiskStatsResponse) - [DiskUsageInfo](#machine.DiskUsageInfo) - [DiskUsageRequest](#machine.DiskUsageRequest) - [DmesgRequest](#machine.DmesgRequest) - [EtcdAlarm](#machine.EtcdAlarm) - [EtcdAlarmDisarm](#machine.EtcdAlarmDisarm) - [EtcdAlarmDisarmResponse](#machine.EtcdAlarmDisarmResponse) - [EtcdAlarmListResponse](#machine.EtcdAlarmListResponse) - [EtcdClusterDowngrade](#machine.EtcdClusterDowngrade) - [EtcdDefragment](#machine.EtcdDefragment) - [EtcdDefragmentResponse](#machine.EtcdDefragmentResponse) - [EtcdDowngradeCancel](#machine.EtcdDowngradeCancel) - [EtcdDowngradeCancelResponse](#machine.EtcdDowngradeCancelResponse) - [EtcdDowngradeEnable](#machine.EtcdDowngradeEnable) - [EtcdDowngradeEnableRequest](#machine.EtcdDowngradeEnableRequest) - [EtcdDowngradeEnableResponse](#machine.EtcdDowngradeEnableResponse) - [EtcdDowngradeValidate](#machine.EtcdDowngradeValidate) - [EtcdDowngradeValidateRequest](#machine.EtcdDowngradeValidateRequest) - [EtcdDowngradeValidateResponse](#machine.EtcdDowngradeValidateResponse) - [EtcdForfeitLeadership](#machine.EtcdForfeitLeadership) - [EtcdForfeitLeadershipRequest](#machine.EtcdForfeitLeadershipRequest) - [EtcdForfeitLeadershipResponse](#machine.EtcdForfeitLeadershipResponse) - [EtcdLeaveCluster](#machine.EtcdLeaveCluster) - [EtcdLeaveClusterRequest](#machine.EtcdLeaveClusterRequest) - [EtcdLeaveClusterResponse](#machine.EtcdLeaveClusterResponse) - [EtcdMember](#machine.EtcdMember) - [EtcdMemberAlarm](#machine.EtcdMemberAlarm) - [EtcdMemberListRequest](#machine.EtcdMemberListRequest) - [EtcdMemberListResponse](#machine.EtcdMemberListResponse) - [EtcdMemberStatus](#machine.EtcdMemberStatus) - [EtcdMembers](#machine.EtcdMembers) - [EtcdRecover](#machine.EtcdRecover) - [EtcdRecoverResponse](#machine.EtcdRecoverResponse) - [EtcdRemoveMember](#machine.EtcdRemoveMember) - [EtcdRemoveMemberByID](#machine.EtcdRemoveMemberByID) - [EtcdRemoveMemberByIDRequest](#machine.EtcdRemoveMemberByIDRequest) - [EtcdRemoveMemberByIDResponse](#machine.EtcdRemoveMemberByIDResponse) - [EtcdRemoveMemberRequest](#machine.EtcdRemoveMemberRequest) - [EtcdRemoveMemberResponse](#machine.EtcdRemoveMemberResponse) - [EtcdSnapshotRequest](#machine.EtcdSnapshotRequest) - [EtcdStatus](#machine.EtcdStatus) - [EtcdStatusResponse](#machine.EtcdStatusResponse) - [Event](#machine.Event) - [EventsRequest](#machine.EventsRequest) - [FeaturesInfo](#machine.FeaturesInfo) - [FileInfo](#machine.FileInfo) - [GenerateClientConfiguration](#machine.GenerateClientConfiguration) - [GenerateClientConfigurationRequest](#machine.GenerateClientConfigurationRequest) - [GenerateClientConfigurationResponse](#machine.GenerateClientConfigurationResponse) - [Hostname](#machine.Hostname) - [HostnameResponse](#machine.HostnameResponse) - [ImageListRequest](#machine.ImageListRequest) - [ImageListResponse](#machine.ImageListResponse) - [ImagePull](#machine.ImagePull) - [ImagePullRequest](#machine.ImagePullRequest) - [ImagePullResponse](#machine.ImagePullResponse) - [InstallConfig](#machine.InstallConfig) - [ListRequest](#machine.ListRequest) - [LoadAvg](#machine.LoadAvg) - [LoadAvgResponse](#machine.LoadAvgResponse) - [LogsContainer](#machine.LogsContainer) - [LogsContainersResponse](#machine.LogsContainersResponse) - [LogsRequest](#machine.LogsRequest) - [MachineConfig](#machine.MachineConfig) - [MachineStatusEvent](#machine.MachineStatusEvent) - [MachineStatusEvent.MachineStatus](#machine.MachineStatusEvent.MachineStatus) - [MachineStatusEvent.MachineStatus.UnmetCondition](#machine.MachineStatusEvent.MachineStatus.UnmetCondition) - [MemInfo](#machine.MemInfo) - [Memory](#machine.Memory) - [MemoryResponse](#machine.MemoryResponse) - [MetaDelete](#machine.MetaDelete) - [MetaDeleteRequest](#machine.MetaDeleteRequest) - [MetaDeleteResponse](#machine.MetaDeleteResponse) - [MetaWrite](#machine.MetaWrite) - [MetaWriteRequest](#machine.MetaWriteRequest) - [MetaWriteResponse](#machine.MetaWriteResponse) - [MountStat](#machine.MountStat) - [Mounts](#machine.Mounts) - [MountsResponse](#machine.MountsResponse) - [NetDev](#machine.NetDev) - [Netstat](#machine.Netstat) - [NetstatRequest](#machine.NetstatRequest) - [NetstatRequest.Feature](#machine.NetstatRequest.Feature) - [NetstatRequest.L4proto](#machine.NetstatRequest.L4proto) - [NetstatRequest.NetNS](#machine.NetstatRequest.NetNS) - [NetstatResponse](#machine.NetstatResponse) - [NetworkConfig](#machine.NetworkConfig) - [NetworkDeviceConfig](#machine.NetworkDeviceConfig) - [NetworkDeviceStats](#machine.NetworkDeviceStats) - [NetworkDeviceStatsResponse](#machine.NetworkDeviceStatsResponse) - [PacketCaptureRequest](#machine.PacketCaptureRequest) - [PhaseEvent](#machine.PhaseEvent) - [PlatformInfo](#machine.PlatformInfo) - [Process](#machine.Process) - [ProcessInfo](#machine.ProcessInfo) - [ProcessesResponse](#machine.ProcessesResponse) - [ReadRequest](#machine.ReadRequest) - [Reboot](#machine.Reboot) - [RebootRequest](#machine.RebootRequest) - [RebootResponse](#machine.RebootResponse) - [Reset](#machine.Reset) - [ResetPartitionSpec](#machine.ResetPartitionSpec) - [ResetRequest](#machine.ResetRequest) - [ResetResponse](#machine.ResetResponse) - [Restart](#machine.Restart) - [RestartEvent](#machine.RestartEvent) - [RestartRequest](#machine.RestartRequest) - [RestartResponse](#machine.RestartResponse) - [Rollback](#machine.Rollback) - [RollbackRequest](#machine.RollbackRequest) - [RollbackResponse](#machine.RollbackResponse) - [RouteConfig](#machine.RouteConfig) - [SequenceEvent](#machine.SequenceEvent) - [ServiceEvent](#machine.ServiceEvent) - [ServiceEvents](#machine.ServiceEvents) - [ServiceHealth](#machine.ServiceHealth) - [ServiceInfo](#machine.ServiceInfo) - [ServiceList](#machine.ServiceList) - [ServiceListResponse](#machine.ServiceListResponse) - [ServiceRestart](#machine.ServiceRestart) - [ServiceRestartRequest](#machine.ServiceRestartRequest) - [ServiceRestartResponse](#machine.ServiceRestartResponse) - [ServiceStart](#machine.ServiceStart) - [ServiceStartRequest](#machine.ServiceStartRequest) - [ServiceStartResponse](#machine.ServiceStartResponse) - [ServiceStateEvent](#machine.ServiceStateEvent) - [ServiceStop](#machine.ServiceStop) - [ServiceStopRequest](#machine.ServiceStopRequest) - [ServiceStopResponse](#machine.ServiceStopResponse) - [Shutdown](#machine.Shutdown) - [ShutdownRequest](#machine.ShutdownRequest) - [ShutdownResponse](#machine.ShutdownResponse) - [SoftIRQStat](#machine.SoftIRQStat) - [Stat](#machine.Stat) - [Stats](#machine.Stats) - [StatsRequest](#machine.StatsRequest) - [StatsResponse](#machine.StatsResponse) - [SystemStat](#machine.SystemStat) - [SystemStatResponse](#machine.SystemStatResponse) - [TaskEvent](#machine.TaskEvent) - [Upgrade](#machine.Upgrade) - [UpgradeRequest](#machine.UpgradeRequest) - [UpgradeResponse](#machine.UpgradeResponse) - [Version](#machine.Version) - [VersionInfo](#machine.VersionInfo) - [VersionResponse](#machine.VersionResponse) - [Xattr](#machine.Xattr) - [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode) - [ConnectRecord.State](#machine.ConnectRecord.State) - [ConnectRecord.TimerActive](#machine.ConnectRecord.TimerActive) - [EtcdMemberAlarm.AlarmType](#machine.EtcdMemberAlarm.AlarmType) - [ListRequest.Type](#machine.ListRequest.Type) - [MachineConfig.MachineType](#machine.MachineConfig.MachineType) - [MachineStatusEvent.MachineStage](#machine.MachineStatusEvent.MachineStage) - [NetstatRequest.Filter](#machine.NetstatRequest.Filter) - [PhaseEvent.Action](#machine.PhaseEvent.Action) - [RebootRequest.Mode](#machine.RebootRequest.Mode) - [ResetRequest.WipeMode](#machine.ResetRequest.WipeMode) - [SequenceEvent.Action](#machine.SequenceEvent.Action) - [ServiceStateEvent.Action](#machine.ServiceStateEvent.Action) - [TaskEvent.Action](#machine.TaskEvent.Action) - [UpgradeRequest.RebootMode](#machine.UpgradeRequest.RebootMode) - [MachineService](#machine.MachineService) - [resource/config/config.proto](#resource/config/config.proto) - [MachineConfigSpec](#resource.config.MachineConfigSpec) - [MachineTypeSpec](#resource.config.MachineTypeSpec) - [MachineType](#resource.config.MachineType) - [resource/definitions/enums/enums.proto](#resource/definitions/enums/enums.proto) - [BlockEncryptionKeyType](#talos.resource.definitions.enums.BlockEncryptionKeyType) - [BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) - [BlockFSParameterType](#talos.resource.definitions.enums.BlockFSParameterType) - [BlockFilesystemType](#talos.resource.definitions.enums.BlockFilesystemType) - [BlockVolumePhase](#talos.resource.definitions.enums.BlockVolumePhase) - [BlockVolumeType](#talos.resource.definitions.enums.BlockVolumeType) - [CriImageCacheCopyStatus](#talos.resource.definitions.enums.CriImageCacheCopyStatus) - [CriImageCacheStatus](#talos.resource.definitions.enums.CriImageCacheStatus) - [KubespanPeerState](#talos.resource.definitions.enums.KubespanPeerState) - [MachineType](#talos.resource.definitions.enums.MachineType) - [NethelpersADLACPActive](#talos.resource.definitions.enums.NethelpersADLACPActive) - [NethelpersADSelect](#talos.resource.definitions.enums.NethelpersADSelect) - [NethelpersARPAllTargets](#talos.resource.definitions.enums.NethelpersARPAllTargets) - [NethelpersARPValidate](#talos.resource.definitions.enums.NethelpersARPValidate) - [NethelpersAddressFlag](#talos.resource.definitions.enums.NethelpersAddressFlag) - [NethelpersAddressSortAlgorithm](#talos.resource.definitions.enums.NethelpersAddressSortAlgorithm) - [NethelpersAutoHostnameKind](#talos.resource.definitions.enums.NethelpersAutoHostnameKind) - [NethelpersBondMode](#talos.resource.definitions.enums.NethelpersBondMode) - [NethelpersBondXmitHashPolicy](#talos.resource.definitions.enums.NethelpersBondXmitHashPolicy) - [NethelpersClientIdentifier](#talos.resource.definitions.enums.NethelpersClientIdentifier) - [NethelpersConntrackState](#talos.resource.definitions.enums.NethelpersConntrackState) - [NethelpersDuplex](#talos.resource.definitions.enums.NethelpersDuplex) - [NethelpersFailOverMAC](#talos.resource.definitions.enums.NethelpersFailOverMAC) - [NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) - [NethelpersICMPType](#talos.resource.definitions.enums.NethelpersICMPType) - [NethelpersLACPRate](#talos.resource.definitions.enums.NethelpersLACPRate) - [NethelpersLinkType](#talos.resource.definitions.enums.NethelpersLinkType) - [NethelpersMatchOperator](#talos.resource.definitions.enums.NethelpersMatchOperator) - [NethelpersNfTablesChainHook](#talos.resource.definitions.enums.NethelpersNfTablesChainHook) - [NethelpersNfTablesChainPriority](#talos.resource.definitions.enums.NethelpersNfTablesChainPriority) - [NethelpersNfTablesVerdict](#talos.resource.definitions.enums.NethelpersNfTablesVerdict) - [NethelpersOperationalState](#talos.resource.definitions.enums.NethelpersOperationalState) - [NethelpersPort](#talos.resource.definitions.enums.NethelpersPort) - [NethelpersPrimaryReselect](#talos.resource.definitions.enums.NethelpersPrimaryReselect) - [NethelpersProtocol](#talos.resource.definitions.enums.NethelpersProtocol) - [NethelpersRouteFlag](#talos.resource.definitions.enums.NethelpersRouteFlag) - [NethelpersRouteProtocol](#talos.resource.definitions.enums.NethelpersRouteProtocol) - [NethelpersRouteType](#talos.resource.definitions.enums.NethelpersRouteType) - [NethelpersRoutingRuleAction](#talos.resource.definitions.enums.NethelpersRoutingRuleAction) - [NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) - [NethelpersScope](#talos.resource.definitions.enums.NethelpersScope) - [NethelpersVLANProtocol](#talos.resource.definitions.enums.NethelpersVLANProtocol) - [NethelpersWOLMode](#talos.resource.definitions.enums.NethelpersWOLMode) - [NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) - [NetworkOperator](#talos.resource.definitions.enums.NetworkOperator) - [RuntimeFIPSState](#talos.resource.definitions.enums.RuntimeFIPSState) - [RuntimeMachineStage](#talos.resource.definitions.enums.RuntimeMachineStage) - [RuntimeSELinuxState](#talos.resource.definitions.enums.RuntimeSELinuxState) - [resource/definitions/block/block.proto](#resource/definitions/block/block.proto) - [DeviceSpec](#talos.resource.definitions.block.DeviceSpec) - [DiscoveredVolumeSpec](#talos.resource.definitions.block.DiscoveredVolumeSpec) - [DiscoveryRefreshRequestSpec](#talos.resource.definitions.block.DiscoveryRefreshRequestSpec) - [DiscoveryRefreshStatusSpec](#talos.resource.definitions.block.DiscoveryRefreshStatusSpec) - [DiskSelector](#talos.resource.definitions.block.DiskSelector) - [DiskSpec](#talos.resource.definitions.block.DiskSpec) - [EncryptionKey](#talos.resource.definitions.block.EncryptionKey) - [EncryptionSpec](#talos.resource.definitions.block.EncryptionSpec) - [FilesystemSpec](#talos.resource.definitions.block.FilesystemSpec) - [LocatorSpec](#talos.resource.definitions.block.LocatorSpec) - [MountRequestSpec](#talos.resource.definitions.block.MountRequestSpec) - [MountSpec](#talos.resource.definitions.block.MountSpec) - [MountStatusSpec](#talos.resource.definitions.block.MountStatusSpec) - [ParameterSpec](#talos.resource.definitions.block.ParameterSpec) - [PartitionSpec](#talos.resource.definitions.block.PartitionSpec) - [ProvisioningSpec](#talos.resource.definitions.block.ProvisioningSpec) - [SwapStatusSpec](#talos.resource.definitions.block.SwapStatusSpec) - [SymlinkProvisioningSpec](#talos.resource.definitions.block.SymlinkProvisioningSpec) - [SymlinkSpec](#talos.resource.definitions.block.SymlinkSpec) - [SystemDiskSpec](#talos.resource.definitions.block.SystemDiskSpec) - [TPMEncryptionOptionsInfo](#talos.resource.definitions.block.TPMEncryptionOptionsInfo) - [UserDiskConfigStatusSpec](#talos.resource.definitions.block.UserDiskConfigStatusSpec) - [VolumeConfigSpec](#talos.resource.definitions.block.VolumeConfigSpec) - [VolumeMountRequestSpec](#talos.resource.definitions.block.VolumeMountRequestSpec) - [VolumeMountStatusSpec](#talos.resource.definitions.block.VolumeMountStatusSpec) - [VolumeStatusSpec](#talos.resource.definitions.block.VolumeStatusSpec) - [ZswapStatusSpec](#talos.resource.definitions.block.ZswapStatusSpec) - [resource/definitions/cluster/cluster.proto](#resource/definitions/cluster/cluster.proto) - [AffiliateSpec](#talos.resource.definitions.cluster.AffiliateSpec) - [ConfigSpec](#talos.resource.definitions.cluster.ConfigSpec) - [ControlPlane](#talos.resource.definitions.cluster.ControlPlane) - [IdentitySpec](#talos.resource.definitions.cluster.IdentitySpec) - [InfoSpec](#talos.resource.definitions.cluster.InfoSpec) - [KubeSpanAffiliateSpec](#talos.resource.definitions.cluster.KubeSpanAffiliateSpec) - [MemberSpec](#talos.resource.definitions.cluster.MemberSpec) - [resource/definitions/cri/cri.proto](#resource/definitions/cri/cri.proto) - [ImageCacheConfigSpec](#talos.resource.definitions.cri.ImageCacheConfigSpec) - [RegistriesConfigSpec](#talos.resource.definitions.cri.RegistriesConfigSpec) - [RegistriesConfigSpec.RegistryAuthsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntry) - [RegistriesConfigSpec.RegistryMirrorsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntry) - [RegistriesConfigSpec.RegistryTlSsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntry) - [RegistryAuthConfig](#talos.resource.definitions.cri.RegistryAuthConfig) - [RegistryEndpointConfig](#talos.resource.definitions.cri.RegistryEndpointConfig) - [RegistryMirrorConfig](#talos.resource.definitions.cri.RegistryMirrorConfig) - [RegistryTLSConfig](#talos.resource.definitions.cri.RegistryTLSConfig) - [SeccompProfileSpec](#talos.resource.definitions.cri.SeccompProfileSpec) - [resource/definitions/etcd/etcd.proto](#resource/definitions/etcd/etcd.proto) - [ArgValues](#talos.resource.definitions.etcd.ArgValues) - [ConfigSpec](#talos.resource.definitions.etcd.ConfigSpec) - [ConfigSpec.ExtraArgsEntry](#talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntry) - [MemberSpec](#talos.resource.definitions.etcd.MemberSpec) - [PKIStatusSpec](#talos.resource.definitions.etcd.PKIStatusSpec) - [SpecSpec](#talos.resource.definitions.etcd.SpecSpec) - [SpecSpec.ExtraArgsEntry](#talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntry) - [resource/definitions/extensions/extensions.proto](#resource/definitions/extensions/extensions.proto) - [Compatibility](#talos.resource.definitions.extensions.Compatibility) - [Constraint](#talos.resource.definitions.extensions.Constraint) - [Layer](#talos.resource.definitions.extensions.Layer) - [Metadata](#talos.resource.definitions.extensions.Metadata) - [resource/definitions/files/files.proto](#resource/definitions/files/files.proto) - [EtcFileSpecSpec](#talos.resource.definitions.files.EtcFileSpecSpec) - [EtcFileStatusSpec](#talos.resource.definitions.files.EtcFileStatusSpec) - [resource/definitions/hardware/hardware.proto](#resource/definitions/hardware/hardware.proto) - [MemoryModuleSpec](#talos.resource.definitions.hardware.MemoryModuleSpec) - [PCIDeviceSpec](#talos.resource.definitions.hardware.PCIDeviceSpec) - [PCIDriverRebindConfigSpec](#talos.resource.definitions.hardware.PCIDriverRebindConfigSpec) - [PCIDriverRebindStatusSpec](#talos.resource.definitions.hardware.PCIDriverRebindStatusSpec) - [ProcessorSpec](#talos.resource.definitions.hardware.ProcessorSpec) - [SystemInformationSpec](#talos.resource.definitions.hardware.SystemInformationSpec) - [resource/definitions/proto/proto.proto](#resource/definitions/proto/proto.proto) - [LinuxIDMapping](#talos.resource.definitions.proto.LinuxIDMapping) - [Mount](#talos.resource.definitions.proto.Mount) - [resource/definitions/k8s/k8s.proto](#resource/definitions/k8s/k8s.proto) - [APIServerConfigSpec](#talos.resource.definitions.k8s.APIServerConfigSpec) - [APIServerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntry) - [APIServerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry) - [AdmissionControlConfigSpec](#talos.resource.definitions.k8s.AdmissionControlConfigSpec) - [AdmissionPluginSpec](#talos.resource.definitions.k8s.AdmissionPluginSpec) - [ArgValues](#talos.resource.definitions.k8s.ArgValues) - [AuditPolicyConfigSpec](#talos.resource.definitions.k8s.AuditPolicyConfigSpec) - [AuthorizationAuthorizersSpec](#talos.resource.definitions.k8s.AuthorizationAuthorizersSpec) - [AuthorizationConfigSpec](#talos.resource.definitions.k8s.AuthorizationConfigSpec) - [BootstrapManifestsConfigSpec](#talos.resource.definitions.k8s.BootstrapManifestsConfigSpec) - [ConfigStatusSpec](#talos.resource.definitions.k8s.ConfigStatusSpec) - [ControllerManagerConfigSpec](#talos.resource.definitions.k8s.ControllerManagerConfigSpec) - [ControllerManagerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.ControllerManagerConfigSpec.EnvironmentVariablesEntry) - [ControllerManagerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntry) - [EndpointSpec](#talos.resource.definitions.k8s.EndpointSpec) - [ExtraManifest](#talos.resource.definitions.k8s.ExtraManifest) - [ExtraManifest.ExtraHeadersEntry](#talos.resource.definitions.k8s.ExtraManifest.ExtraHeadersEntry) - [ExtraManifestsConfigSpec](#talos.resource.definitions.k8s.ExtraManifestsConfigSpec) - [ExtraVolume](#talos.resource.definitions.k8s.ExtraVolume) - [KubePrismConfigSpec](#talos.resource.definitions.k8s.KubePrismConfigSpec) - [KubePrismEndpoint](#talos.resource.definitions.k8s.KubePrismEndpoint) - [KubePrismEndpointsSpec](#talos.resource.definitions.k8s.KubePrismEndpointsSpec) - [KubePrismStatusesSpec](#talos.resource.definitions.k8s.KubePrismStatusesSpec) - [KubeletConfigSpec](#talos.resource.definitions.k8s.KubeletConfigSpec) - [KubeletConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry) - [KubeletSpecSpec](#talos.resource.definitions.k8s.KubeletSpecSpec) - [ManifestSpec](#talos.resource.definitions.k8s.ManifestSpec) - [ManifestStatusSpec](#talos.resource.definitions.k8s.ManifestStatusSpec) - [NodeAnnotationSpecSpec](#talos.resource.definitions.k8s.NodeAnnotationSpecSpec) - [NodeIPConfigSpec](#talos.resource.definitions.k8s.NodeIPConfigSpec) - [NodeIPSpec](#talos.resource.definitions.k8s.NodeIPSpec) - [NodeLabelSpecSpec](#talos.resource.definitions.k8s.NodeLabelSpecSpec) - [NodeStatusSpec](#talos.resource.definitions.k8s.NodeStatusSpec) - [NodeStatusSpec.AnnotationsEntry](#talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry) - [NodeStatusSpec.LabelsEntry](#talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry) - [NodeTaintSpecSpec](#talos.resource.definitions.k8s.NodeTaintSpecSpec) - [NodenameSpec](#talos.resource.definitions.k8s.NodenameSpec) - [Resources](#talos.resource.definitions.k8s.Resources) - [Resources.LimitsEntry](#talos.resource.definitions.k8s.Resources.LimitsEntry) - [Resources.RequestsEntry](#talos.resource.definitions.k8s.Resources.RequestsEntry) - [SchedulerConfigSpec](#talos.resource.definitions.k8s.SchedulerConfigSpec) - [SchedulerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry) - [SchedulerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry) - [SecretsStatusSpec](#talos.resource.definitions.k8s.SecretsStatusSpec) - [SingleManifest](#talos.resource.definitions.k8s.SingleManifest) - [StaticPodServerStatusSpec](#talos.resource.definitions.k8s.StaticPodServerStatusSpec) - [StaticPodSpec](#talos.resource.definitions.k8s.StaticPodSpec) - [StaticPodStatusSpec](#talos.resource.definitions.k8s.StaticPodStatusSpec) - [resource/definitions/kubeaccess/kubeaccess.proto](#resource/definitions/kubeaccess/kubeaccess.proto) - [ConfigSpec](#talos.resource.definitions.kubeaccess.ConfigSpec) - [resource/definitions/kubespan/kubespan.proto](#resource/definitions/kubespan/kubespan.proto) - [ConfigSpec](#talos.resource.definitions.kubespan.ConfigSpec) - [EndpointSpec](#talos.resource.definitions.kubespan.EndpointSpec) - [IdentitySpec](#talos.resource.definitions.kubespan.IdentitySpec) - [PeerSpecSpec](#talos.resource.definitions.kubespan.PeerSpecSpec) - [PeerStatusSpec](#talos.resource.definitions.kubespan.PeerStatusSpec) - [resource/definitions/runtime/runtime.proto](#resource/definitions/runtime/runtime.proto) - [APIServiceConfigSpec](#talos.resource.definitions.runtime.APIServiceConfigSpec) - [BootedEntrySpec](#talos.resource.definitions.runtime.BootedEntrySpec) - [DevicesStatusSpec](#talos.resource.definitions.runtime.DevicesStatusSpec) - [DiagnosticSpec](#talos.resource.definitions.runtime.DiagnosticSpec) - [EnvironmentSpec](#talos.resource.definitions.runtime.EnvironmentSpec) - [EventSinkConfigSpec](#talos.resource.definitions.runtime.EventSinkConfigSpec) - [ExtensionServiceConfigFile](#talos.resource.definitions.runtime.ExtensionServiceConfigFile) - [ExtensionServiceConfigSpec](#talos.resource.definitions.runtime.ExtensionServiceConfigSpec) - [ExtensionServiceConfigStatusSpec](#talos.resource.definitions.runtime.ExtensionServiceConfigStatusSpec) - [KernelCmdlineSpec](#talos.resource.definitions.runtime.KernelCmdlineSpec) - [KernelModuleSpecSpec](#talos.resource.definitions.runtime.KernelModuleSpecSpec) - [KernelParamSpecSpec](#talos.resource.definitions.runtime.KernelParamSpecSpec) - [KernelParamStatusSpec](#talos.resource.definitions.runtime.KernelParamStatusSpec) - [KmsgLogConfigSpec](#talos.resource.definitions.runtime.KmsgLogConfigSpec) - [LoadedKernelModuleSpec](#talos.resource.definitions.runtime.LoadedKernelModuleSpec) - [MachineStatusSpec](#talos.resource.definitions.runtime.MachineStatusSpec) - [MachineStatusStatus](#talos.resource.definitions.runtime.MachineStatusStatus) - [MaintenanceServiceConfigSpec](#talos.resource.definitions.runtime.MaintenanceServiceConfigSpec) - [MetaKeySpec](#talos.resource.definitions.runtime.MetaKeySpec) - [MetaLoadedSpec](#talos.resource.definitions.runtime.MetaLoadedSpec) - [MountStatusSpec](#talos.resource.definitions.runtime.MountStatusSpec) - [OOMActionSpec](#talos.resource.definitions.runtime.OOMActionSpec) - [PlatformMetadataSpec](#talos.resource.definitions.runtime.PlatformMetadataSpec) - [PlatformMetadataSpec.TagsEntry](#talos.resource.definitions.runtime.PlatformMetadataSpec.TagsEntry) - [SBOMItemSpec](#talos.resource.definitions.runtime.SBOMItemSpec) - [SecurityStateSpec](#talos.resource.definitions.runtime.SecurityStateSpec) - [UniqueMachineTokenSpec](#talos.resource.definitions.runtime.UniqueMachineTokenSpec) - [UnmetCondition](#talos.resource.definitions.runtime.UnmetCondition) - [WatchdogTimerConfigSpec](#talos.resource.definitions.runtime.WatchdogTimerConfigSpec) - [WatchdogTimerStatusSpec](#talos.resource.definitions.runtime.WatchdogTimerStatusSpec) - [resource/definitions/network/network.proto](#resource/definitions/network/network.proto) - [AddressSpecSpec](#talos.resource.definitions.network.AddressSpecSpec) - [AddressStatusSpec](#talos.resource.definitions.network.AddressStatusSpec) - [BondMasterSpec](#talos.resource.definitions.network.BondMasterSpec) - [BondSlave](#talos.resource.definitions.network.BondSlave) - [BridgeMasterSpec](#talos.resource.definitions.network.BridgeMasterSpec) - [BridgeSlave](#talos.resource.definitions.network.BridgeSlave) - [BridgeVLANSpec](#talos.resource.definitions.network.BridgeVLANSpec) - [ClientIdentifierSpec](#talos.resource.definitions.network.ClientIdentifierSpec) - [DHCP4OperatorSpec](#talos.resource.definitions.network.DHCP4OperatorSpec) - [DHCP6OperatorSpec](#talos.resource.definitions.network.DHCP6OperatorSpec) - [DNSResolveCacheSpec](#talos.resource.definitions.network.DNSResolveCacheSpec) - [EthernetChannelsSpec](#talos.resource.definitions.network.EthernetChannelsSpec) - [EthernetChannelsStatus](#talos.resource.definitions.network.EthernetChannelsStatus) - [EthernetFeatureStatus](#talos.resource.definitions.network.EthernetFeatureStatus) - [EthernetRingsSpec](#talos.resource.definitions.network.EthernetRingsSpec) - [EthernetRingsStatus](#talos.resource.definitions.network.EthernetRingsStatus) - [EthernetSpecSpec](#talos.resource.definitions.network.EthernetSpecSpec) - [EthernetSpecSpec.FeaturesEntry](#talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntry) - [EthernetStatusSpec](#talos.resource.definitions.network.EthernetStatusSpec) - [HardwareAddrSpec](#talos.resource.definitions.network.HardwareAddrSpec) - [HostDNSConfigSpec](#talos.resource.definitions.network.HostDNSConfigSpec) - [HostnameSpecSpec](#talos.resource.definitions.network.HostnameSpecSpec) - [HostnameStatusSpec](#talos.resource.definitions.network.HostnameStatusSpec) - [LinkAliasSpecSpec](#talos.resource.definitions.network.LinkAliasSpecSpec) - [LinkRefreshSpec](#talos.resource.definitions.network.LinkRefreshSpec) - [LinkSpecSpec](#talos.resource.definitions.network.LinkSpecSpec) - [LinkStatusSpec](#talos.resource.definitions.network.LinkStatusSpec) - [NfTablesAddressMatch](#talos.resource.definitions.network.NfTablesAddressMatch) - [NfTablesChainSpec](#talos.resource.definitions.network.NfTablesChainSpec) - [NfTablesClampMSS](#talos.resource.definitions.network.NfTablesClampMSS) - [NfTablesConntrackStateMatch](#talos.resource.definitions.network.NfTablesConntrackStateMatch) - [NfTablesICMPTypeMatch](#talos.resource.definitions.network.NfTablesICMPTypeMatch) - [NfTablesIfNameMatch](#talos.resource.definitions.network.NfTablesIfNameMatch) - [NfTablesLayer4Match](#talos.resource.definitions.network.NfTablesLayer4Match) - [NfTablesLimitMatch](#talos.resource.definitions.network.NfTablesLimitMatch) - [NfTablesMark](#talos.resource.definitions.network.NfTablesMark) - [NfTablesPortMatch](#talos.resource.definitions.network.NfTablesPortMatch) - [NfTablesRule](#talos.resource.definitions.network.NfTablesRule) - [NodeAddressFilterSpec](#talos.resource.definitions.network.NodeAddressFilterSpec) - [NodeAddressSortAlgorithmSpec](#talos.resource.definitions.network.NodeAddressSortAlgorithmSpec) - [NodeAddressSpec](#talos.resource.definitions.network.NodeAddressSpec) - [OperatorSpecSpec](#talos.resource.definitions.network.OperatorSpecSpec) - [PlatformConfigSpec](#talos.resource.definitions.network.PlatformConfigSpec) - [PortRange](#talos.resource.definitions.network.PortRange) - [ProbeSpecSpec](#talos.resource.definitions.network.ProbeSpecSpec) - [ProbeStatusSpec](#talos.resource.definitions.network.ProbeStatusSpec) - [ResolverSpecSpec](#talos.resource.definitions.network.ResolverSpecSpec) - [ResolverStatusSpec](#talos.resource.definitions.network.ResolverStatusSpec) - [RouteSpecSpec](#talos.resource.definitions.network.RouteSpecSpec) - [RouteStatusSpec](#talos.resource.definitions.network.RouteStatusSpec) - [RoutingRuleSpecSpec](#talos.resource.definitions.network.RoutingRuleSpecSpec) - [RoutingRuleStatusSpec](#talos.resource.definitions.network.RoutingRuleStatusSpec) - [STPSpec](#talos.resource.definitions.network.STPSpec) - [StatusSpec](#talos.resource.definitions.network.StatusSpec) - [TCPProbeSpec](#talos.resource.definitions.network.TCPProbeSpec) - [TimeServerSpecSpec](#talos.resource.definitions.network.TimeServerSpecSpec) - [TimeServerStatusSpec](#talos.resource.definitions.network.TimeServerStatusSpec) - [VIPEquinixMetalSpec](#talos.resource.definitions.network.VIPEquinixMetalSpec) - [VIPHCloudSpec](#talos.resource.definitions.network.VIPHCloudSpec) - [VIPOperatorSpec](#talos.resource.definitions.network.VIPOperatorSpec) - [VLANSpec](#talos.resource.definitions.network.VLANSpec) - [VRFMasterSpec](#talos.resource.definitions.network.VRFMasterSpec) - [VRFSlave](#talos.resource.definitions.network.VRFSlave) - [WireguardPeer](#talos.resource.definitions.network.WireguardPeer) - [WireguardSpec](#talos.resource.definitions.network.WireguardSpec) - [resource/definitions/perf/perf.proto](#resource/definitions/perf/perf.proto) - [CPUSpec](#talos.resource.definitions.perf.CPUSpec) - [CPUStat](#talos.resource.definitions.perf.CPUStat) - [MemorySpec](#talos.resource.definitions.perf.MemorySpec) - [resource/definitions/secrets/secrets.proto](#resource/definitions/secrets/secrets.proto) - [APICertsSpec](#talos.resource.definitions.secrets.APICertsSpec) - [CertSANSpec](#talos.resource.definitions.secrets.CertSANSpec) - [EncryptionSaltSpec](#talos.resource.definitions.secrets.EncryptionSaltSpec) - [EtcdCertsSpec](#talos.resource.definitions.secrets.EtcdCertsSpec) - [EtcdRootSpec](#talos.resource.definitions.secrets.EtcdRootSpec) - [KubeletSpec](#talos.resource.definitions.secrets.KubeletSpec) - [KubernetesCertsSpec](#talos.resource.definitions.secrets.KubernetesCertsSpec) - [KubernetesDynamicCertsSpec](#talos.resource.definitions.secrets.KubernetesDynamicCertsSpec) - [KubernetesRootSpec](#talos.resource.definitions.secrets.KubernetesRootSpec) - [MaintenanceRootSpec](#talos.resource.definitions.secrets.MaintenanceRootSpec) - [OSRootSpec](#talos.resource.definitions.secrets.OSRootSpec) - [TrustdCertsSpec](#talos.resource.definitions.secrets.TrustdCertsSpec) - [resource/definitions/security/security.proto](#resource/definitions/security/security.proto) - [ImageKeylessVerifierSpec](#talos.resource.definitions.security.ImageKeylessVerifierSpec) - [ImagePublicKeyVerifierSpec](#talos.resource.definitions.security.ImagePublicKeyVerifierSpec) - [ImageVerificationRuleSpec](#talos.resource.definitions.security.ImageVerificationRuleSpec) - [TUFTrustedRootSpec](#talos.resource.definitions.security.TUFTrustedRootSpec) - [resource/definitions/siderolink/siderolink.proto](#resource/definitions/siderolink/siderolink.proto) - [ConfigSpec](#talos.resource.definitions.siderolink.ConfigSpec) - [StatusSpec](#talos.resource.definitions.siderolink.StatusSpec) - [TunnelSpec](#talos.resource.definitions.siderolink.TunnelSpec) - [resource/definitions/time/time.proto](#resource/definitions/time/time.proto) - [AdjtimeStatusSpec](#talos.resource.definitions.time.AdjtimeStatusSpec) - [StatusSpec](#talos.resource.definitions.time.StatusSpec) - [resource/definitions/v1alpha1/v1alpha1.proto](#resource/definitions/v1alpha1/v1alpha1.proto) - [ServiceSpec](#talos.resource.definitions.v1alpha1.ServiceSpec) - [resource/network/device_config.proto](#resource/network/device_config.proto) - [DeviceConfigSpecSpec](#resource.network.DeviceConfigSpecSpec) - [security/security.proto](#security/security.proto) - [CertificateRequest](#securityapi.CertificateRequest) - [CertificateResponse](#securityapi.CertificateResponse) - [SecurityService](#securityapi.SecurityService) - [storage/storage.proto](#storage/storage.proto) - [BlockDeviceWipe](#storage.BlockDeviceWipe) - [BlockDeviceWipeDescriptor](#storage.BlockDeviceWipeDescriptor) - [BlockDeviceWipeRequest](#storage.BlockDeviceWipeRequest) - [BlockDeviceWipeResponse](#storage.BlockDeviceWipeResponse) - [Disk](#storage.Disk) - [Disks](#storage.Disks) - [DisksResponse](#storage.DisksResponse) - [BlockDeviceWipeDescriptor.Method](#storage.BlockDeviceWipeDescriptor.Method) - [Disk.DiskType](#storage.Disk.DiskType) - [StorageService](#storage.StorageService) - [time/time.proto](#time/time.proto) - [Time](#time.Time) - [TimeRequest](#time.TimeRequest) - [TimeResponse](#time.TimeResponse) - [TimeService](#time.TimeService) - [Scalar Value Types](#scalar-value-types)

Top

## common/common.proto ### ContainerdInstance | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | driver | [ContainerDriver](#common.ContainerDriver) | | Containerd instance to use. | | namespace | [ContainerdNamespace](#common.ContainerdNamespace) | | Containerd namespace to use. | ### Data | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [Metadata](#common.Metadata) | | | | bytes | [bytes](#bytes) | | | ### DataResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Data](#common.Data) | repeated | | ### Empty | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [Metadata](#common.Metadata) | | | ### EmptyResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Empty](#common.Empty) | repeated | | ### Error | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | code | [Code](#common.Code) | | | | message | [string](#string) | | | | details | [google.protobuf.Any](#google.protobuf.Any) | repeated | | ### Metadata Common metadata message nested in all reply message types | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostname | [string](#string) | | hostname of the server response comes from (injected by proxy) | | error | [string](#string) | | error is set if request failed to the upstream (rest of response is undefined) | | status | [google.rpc.Status](#google.rpc.Status) | | error as gRPC Status | ### NetIP | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ip | [bytes](#bytes) | | | ### NetIPPort | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ip | [bytes](#bytes) | | | | port | [int32](#int32) | | | ### NetIPPrefix | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ip | [bytes](#bytes) | | | | prefix_length | [int32](#int32) | | | ### PEMEncodedCertificate | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | crt | [bytes](#bytes) | | | ### PEMEncodedCertificateAndKey | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | crt | [bytes](#bytes) | | | | key | [bytes](#bytes) | | | ### PEMEncodedKey | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [bytes](#bytes) | | | ### URL | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | full_path | [string](#string) | | | ### Code | Name | Number | Description | | ---- | ------ | ----------- | | FATAL | 0 | | | LOCKED | 1 | | | CANCELED | 2 | | ### ContainerDriver | Name | Number | Description | | ---- | ------ | ----------- | | CONTAINERD | 0 | | | CRI | 1 | | ### ContainerdNamespace | Name | Number | Description | | ---- | ------ | ----------- | | NS_UNKNOWN | 0 | | | NS_SYSTEM | 1 | | | NS_CRI | 2 | | ### File-level Extensions | Extension | Type | Base | Number | Description | | --------- | ---- | ---- | ------ | ----------- | | remove_deprecated_enum | string | .google.protobuf.EnumOptions | 93117 | Indicates the Talos version when this deprecated enum will be removed from API. | | remove_deprecated_enum_value | string | .google.protobuf.EnumValueOptions | 93117 | Indicates the Talos version when this deprecated enum value will be removed from API. | | remove_deprecated_field | string | .google.protobuf.FieldOptions | 93117 | Indicates the Talos version when this deprecated filed will be removed from API. | | remove_deprecated_message | string | .google.protobuf.MessageOptions | 93117 | Indicates the Talos version when this deprecated message will be removed from API. | | remove_deprecated_method | string | .google.protobuf.MethodOptions | 93117 | Indicates the Talos version when this deprecated method will be removed from API. | | remove_deprecated_service | string | .google.protobuf.ServiceOptions | 93117 | Indicates the Talos version when this deprecated service will be removed from API. |

Top

## cluster/cluster.proto ### ClusterInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | control_plane_nodes | [string](#string) | repeated | | | worker_nodes | [string](#string) | repeated | | | force_endpoint | [string](#string) | | | ### HealthCheckProgress | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | message | [string](#string) | | | ### HealthCheckRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | wait_timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | | cluster_info | [ClusterInfo](#cluster.ClusterInfo) | | | ### ClusterService The cluster service definition. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | HealthCheck | [HealthCheckRequest](#cluster.HealthCheckRequest) | [HealthCheckProgress](#cluster.HealthCheckProgress) stream | |

Top

## inspect/inspect.proto ### ControllerDependencyEdge | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | controller_name | [string](#string) | | | | edge_type | [DependencyEdgeType](#inspect.DependencyEdgeType) | | | | resource_namespace | [string](#string) | | | | resource_type | [string](#string) | | | | resource_id | [string](#string) | | | ### ControllerRuntimeDependenciesResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ControllerRuntimeDependency](#inspect.ControllerRuntimeDependency) | repeated | | ### ControllerRuntimeDependency The ControllerRuntimeDependency message contains the graph of controller-resource dependencies. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | edges | [ControllerDependencyEdge](#inspect.ControllerDependencyEdge) | repeated | | ### DependencyEdgeType | Name | Number | Description | | ---- | ------ | ----------- | | OUTPUT_EXCLUSIVE | 0 | | | OUTPUT_SHARED | 3 | | | INPUT_STRONG | 1 | | | INPUT_WEAK | 2 | | | INPUT_DESTROY_READY | 4 | | ### InspectService The inspect service definition. InspectService provides auxiliary API to inspect OS internals. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | ControllerRuntimeDependencies | [.google.protobuf.Empty](#google.protobuf.Empty) | [ControllerRuntimeDependenciesResponse](#inspect.ControllerRuntimeDependenciesResponse) | |

Top

## machine/debug.proto ### DebugContainerRunRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | spec | [DebugContainerRunRequestSpec](#machine.DebugContainerRunRequestSpec) | | 1. send the container spec | | stdin_data | [bytes](#bytes) | | 2. send either of the three below to interact with the running container | | signal | [int32](#int32) | | | | term_resize | [DebugContainerTerminalResize](#machine.DebugContainerTerminalResize) | | | ### DebugContainerRunRequestSpec | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | | | image_name | [string](#string) | | | | args | [string](#string) | repeated | | | env | [DebugContainerRunRequestSpec.EnvEntry](#machine.DebugContainerRunRequestSpec.EnvEntry) | repeated | | | profile | [DebugContainerRunRequestSpec.Profile](#machine.DebugContainerRunRequestSpec.Profile) | | | | tty | [bool](#bool) | | | ### DebugContainerRunRequestSpec.EnvEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### DebugContainerRunResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | stdout_data | [bytes](#bytes) | | | | exit_code | [int32](#int32) | | | ### DebugContainerTerminalResize | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | width | [int32](#int32) | | | | height | [int32](#int32) | | | ### DebugContainerRunRequestSpec.Profile | Name | Number | Description | | ---- | ------ | ----------- | | PROFILE_UNSPECIFIED | 0 | | | PROFILE_PRIVILEGED | 1 | | ### DebugService DebugService provides debugging and inspection capabilities for a Talos node. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | ContainerRun | [DebugContainerRunRequest](#machine.DebugContainerRunRequest) stream | [DebugContainerRunResponse](#machine.DebugContainerRunResponse) stream | ContainerRun runs a debug container, attaches to it, and streams I/O. |

Top

## machine/image.proto ### ImageServiceCredentials | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | host | [string](#string) | | Host of the registry (e.g. "docker.io"). | | username | [string](#string) | | Username for the registry. | | password | [string](#string) | | Password (token) for the registry. | ### ImageServiceImportRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | Containerd instance to use. | | image_chunk | [common.Data](#common.Data) | | Chunk of the image tarball. | ### ImageServiceImportResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | Name of the imported image. | ### ImageServiceListRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | | ### ImageServiceListResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | digest | [string](#string) | | | | size | [int64](#int64) | | | | created_at | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | | labels | [ImageServiceListResponse.LabelsEntry](#machine.ImageServiceListResponse.LabelsEntry) | repeated | | ### ImageServiceListResponse.LabelsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### ImageServicePullLayerProgress | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | status | [ImageServicePullLayerProgress.Status](#machine.ImageServicePullLayerProgress.Status) | | | | elapsed | [google.protobuf.Duration](#google.protobuf.Duration) | | | | offset | [int64](#int64) | | | | total | [int64](#int64) | | | ### ImageServicePullProgress | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | layer_id | [string](#string) | | | | progress | [ImageServicePullLayerProgress](#machine.ImageServicePullLayerProgress) | | | ### ImageServicePullRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | | | image_ref | [string](#string) | | Image reference to pull. | ### ImageServicePullResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | Name of the pulled image (when done). | | pull_progress | [ImageServicePullProgress](#machine.ImageServicePullProgress) | | Progress of the image pull (intermediate updates). | ### ImageServiceRemoveRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | | | image_ref | [string](#string) | | Image reference to remove. | ### ImageServiceVerifyRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image_ref | [string](#string) | | Image reference to verify.

The image reference could be either in: * the digest form (e.g. "docker.io/library/nginx@sha256:abc123...") to ensure that the exact image is verified. * the tag form (e.g. "docker.io/library/nginx:latest") to verify the image currently pointed by the tag, and the resolved digested will be returned in the response.

Any other format will cause the error. | | credentials | [ImageServiceCredentials](#machine.ImageServiceCredentials) | | Authentication credentials for the registry (if needed).

By default Talos will use configured auth, but additional image pull secret can be submitted here. | ### ImageServiceVerifyResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | verified | [bool](#bool) | | Was the image verified: if it didn't match any verify rule, false will be returned. If the image matched the rule, but the verification failed, an error will be returned. | | message | [string](#string) | | Free-form verification result message, e.g. with details about the matched rule and how the image was verified. | | digested_image_ref | [string](#string) | | The pinned image reference with resolved digest that was verified (e.g. "docker.io/library/nginx@sha256:abc123...").

This is only set if verified=true. | ### ImageServicePullLayerProgress.Status | Name | Number | Description | | ---- | ------ | ----------- | | DOWNLOADING | 0 | Keep this in sync with ImagePullLayerProgress.Status. | | DOWNLOAD_COMPLETE | 1 | | | EXTRACTING | 2 | | | EXTRACT_COMPLETE | 3 | | | ALREADY_EXISTS | 4 | | ### ImageService The machine service definition. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | List | [ImageServiceListRequest](#machine.ImageServiceListRequest) | [ImageServiceListResponse](#machine.ImageServiceListResponse) stream | List images in the containerd. | | Pull | [ImageServicePullRequest](#machine.ImageServicePullRequest) | [ImageServicePullResponse](#machine.ImageServicePullResponse) stream | Pull an image into the containerd. | | Import | [ImageServiceImportRequest](#machine.ImageServiceImportRequest) stream | [ImageServiceImportResponse](#machine.ImageServiceImportResponse) | Import an image from a stream (tarball). | | Remove | [ImageServiceRemoveRequest](#machine.ImageServiceRemoveRequest) | [.google.protobuf.Empty](#google.protobuf.Empty) | Remove an image from the containerd. | | Verify | [ImageServiceVerifyRequest](#machine.ImageServiceVerifyRequest) | [ImageServiceVerifyResponse](#machine.ImageServiceVerifyResponse) | Verify an image signature. |

Top

## machine/lifecycle.proto ### InstallArtifactsSource InstallArtifactsSource specifies the source of the installation artifacts. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image_name | [string](#string) | | The reference name of the image, as returned by `talosctl image pull`. | ### InstallDestination InstallDestination specifies the target for installation. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | disk | [string](#string) | | The disk to which Talos should be installed, e.g. "/dev/sda". | ### LifecycleServiceInstallProgress LifecycleServiceInstallProgress represents the progress of the installation or upgrade process. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | message | [string](#string) | | A message indicating the current progress of the installation or upgrade. | | exit_code | [int32](#int32) | | An exit code indicating the result of the installation or upgrade process. A non-zero value indicates an error. Server SHOULD NOT respond with error, even if the value is non-zero. It's responsibility of the client to handle the exit code appropriately. | ### LifecycleServiceInstallRequest LifecycleServiceInstallRequest contains the necessary information to perform an installation. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | The containerd instance to use for pulling the installation artifacts. | | source | [InstallArtifactsSource](#machine.InstallArtifactsSource) | | The source of the installation artifacts. | | destination | [InstallDestination](#machine.InstallDestination) | | The destination for the installation. | ### LifecycleServiceInstallResponse LifecycleServiceInstallResponse is the response message for the Install RPC, containing progress updates. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | progress | [LifecycleServiceInstallProgress](#machine.LifecycleServiceInstallProgress) | | The progress of the installation process. | ### LifecycleServiceUpgradeRequest LifecycleServiceUpgradeRequest contains the necessary information to perform an upgrade. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | containerd | [common.ContainerdInstance](#common.ContainerdInstance) | | The containerd instance to use for pulling the installation artifacts. | | source | [InstallArtifactsSource](#machine.InstallArtifactsSource) | | The source of the installation artifacts for the upgrade. | ### LifecycleServiceUpgradeResponse LifecycleServiceUpgradeResponse is the response message for the Upgrade RPC, containing progress updates. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | progress | [LifecycleServiceInstallProgress](#machine.LifecycleServiceInstallProgress) | | The progress of the upgrade process. | ### LifecycleService The LifecycleService handles installation and upgrade operations. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | Install | [LifecycleServiceInstallRequest](#machine.LifecycleServiceInstallRequest) | [LifecycleServiceInstallResponse](#machine.LifecycleServiceInstallResponse) stream | Install Talos to disk. The RPC should fail if the Talos is already installed on the target disk. | | Upgrade | [LifecycleServiceUpgradeRequest](#machine.LifecycleServiceUpgradeRequest) | [LifecycleServiceUpgradeResponse](#machine.LifecycleServiceUpgradeResponse) stream | Upgrade Talos to a new version. The RPC should fail if Talos is not already installed on the target disk. |

Top

## machine/machine.proto ### AddressEvent AddressEvent reports node endpoints aggregated from k8s.Endpoints and network.Hostname. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostname | [string](#string) | | | | addresses | [string](#string) | repeated | | ### ApplyConfiguration ApplyConfigurationResponse describes the response to a configuration request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | warnings | [string](#string) | repeated | Configuration validation warnings. | | mode | [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode) | | States which mode was actually chosen. | | mode_details | [string](#string) | | Human-readable message explaining the result of the apply configuration call. | ### ApplyConfigurationRequest rpc applyConfiguration ApplyConfiguration describes a request to assert a new configuration upon a node. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | data | [bytes](#bytes) | | | | mode | [ApplyConfigurationRequest.Mode](#machine.ApplyConfigurationRequest.Mode) | | | | dry_run | [bool](#bool) | | | | try_mode_timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | ### ApplyConfigurationResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ApplyConfiguration](#machine.ApplyConfiguration) | repeated | | ### BPFInstruction | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | op | [uint32](#uint32) | | | | jt | [uint32](#uint32) | | | | jf | [uint32](#uint32) | | | | k | [uint32](#uint32) | | | ### Bootstrap The bootstrap message containing the bootstrap status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### BootstrapRequest rpc Bootstrap | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | recover_etcd | [bool](#bool) | | Enable etcd recovery from the snapshot. Snapshot should be uploaded before this call via EtcdRecover RPC. | | recover_skip_hash_check | [bool](#bool) | | Skip hash check on the snapshot (etcd). Enable this when recovering from data directory copy to skip integrity check. | ### BootstrapResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Bootstrap](#machine.Bootstrap) | repeated | | ### CNIConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | urls | [string](#string) | repeated | | ### CPUFreqStats | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | current_frequency | [uint64](#uint64) | | | | minimum_frequency | [uint64](#uint64) | | | | maximum_frequency | [uint64](#uint64) | | | | governor | [string](#string) | | | ### CPUFreqStatsResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [CPUsFreqStats](#machine.CPUsFreqStats) | repeated | | ### CPUInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | processor | [uint32](#uint32) | | | | vendor_id | [string](#string) | | | | cpu_family | [string](#string) | | | | model | [string](#string) | | | | model_name | [string](#string) | | | | stepping | [string](#string) | | | | microcode | [string](#string) | | | | cpu_mhz | [double](#double) | | | | cache_size | [string](#string) | | | | physical_id | [string](#string) | | | | siblings | [uint32](#uint32) | | | | core_id | [string](#string) | | | | cpu_cores | [uint32](#uint32) | | | | apic_id | [string](#string) | | | | initial_apic_id | [string](#string) | | | | fpu | [string](#string) | | | | fpu_exception | [string](#string) | | | | cpu_id_level | [uint32](#uint32) | | | | wp | [string](#string) | | | | flags | [string](#string) | repeated | | | bugs | [string](#string) | repeated | | | bogo_mips | [double](#double) | | | | cl_flush_size | [uint32](#uint32) | | | | cache_alignment | [uint32](#uint32) | | | | address_sizes | [string](#string) | | | | power_management | [string](#string) | | | ### CPUInfoResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [CPUsInfo](#machine.CPUsInfo) | repeated | | ### CPUStat | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | user | [double](#double) | | | | nice | [double](#double) | | | | system | [double](#double) | | | | idle | [double](#double) | | | | iowait | [double](#double) | | | | irq | [double](#double) | | | | soft_irq | [double](#double) | | | | steal | [double](#double) | | | | guest | [double](#double) | | | | guest_nice | [double](#double) | | | ### CPUsFreqStats | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | cpu_freq_stats | [CPUFreqStats](#machine.CPUFreqStats) | repeated | | ### CPUsInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | cpu_info | [CPUInfo](#machine.CPUInfo) | repeated | | ### ClusterConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | control_plane | [ControlPlaneConfig](#machine.ControlPlaneConfig) | | | | cluster_network | [ClusterNetworkConfig](#machine.ClusterNetworkConfig) | | | | allow_scheduling_on_control_planes | [bool](#bool) | | | ### ClusterNetworkConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | dns_domain | [string](#string) | | | | cni_config | [CNIConfig](#machine.CNIConfig) | | | ### ConfigLoadErrorEvent ConfigLoadErrorEvent is reported when the config loading has failed. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | error | [string](#string) | | | ### ConfigValidationErrorEvent ConfigValidationErrorEvent is reported when config validation has failed. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | error | [string](#string) | | | ### ConnectRecord | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | l4proto | [string](#string) | | | | localip | [string](#string) | | | | localport | [uint32](#uint32) | | | | remoteip | [string](#string) | | | | remoteport | [uint32](#uint32) | | | | state | [ConnectRecord.State](#machine.ConnectRecord.State) | | | | txqueue | [uint64](#uint64) | | | | rxqueue | [uint64](#uint64) | | | | tr | [ConnectRecord.TimerActive](#machine.ConnectRecord.TimerActive) | | | | timerwhen | [uint64](#uint64) | | | | retrnsmt | [uint64](#uint64) | | | | uid | [uint32](#uint32) | | | | timeout | [uint64](#uint64) | | | | inode | [uint64](#uint64) | | | | ref | [uint64](#uint64) | | | | pointer | [uint64](#uint64) | | | | process | [ConnectRecord.Process](#machine.ConnectRecord.Process) | | | | netns | [string](#string) | | | ### ConnectRecord.Process | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pid | [uint32](#uint32) | | | | name | [string](#string) | | | ### Container The messages message containing the requested containers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | containers | [ContainerInfo](#machine.ContainerInfo) | repeated | | ### ContainerInfo The messages message containing the requested containers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | id | [string](#string) | | | | uid | [string](#string) | | | | internal_id | [string](#string) | | | | image | [string](#string) | | | | pid | [uint32](#uint32) | | | | status | [string](#string) | | | | pod_id | [string](#string) | | | | name | [string](#string) | | | | network_namespace | [string](#string) | | | ### ContainersRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | driver | [common.ContainerDriver](#common.ContainerDriver) | | driver might be default "containerd" or "cri" | ### ContainersResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Container](#machine.Container) | repeated | | ### ControlPlaneConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint | [string](#string) | | | ### CopyRequest CopyRequest describes a request to copy data out of Talos node Copy produces .tar.gz archive which is streamed back to the caller | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | root_path | [string](#string) | | Root path to start copying data out, it might be either a file or directory | ### DHCPOptionsConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | route_metric | [uint32](#uint32) | | | ### DiskStat | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | read_completed | [uint64](#uint64) | | | | read_merged | [uint64](#uint64) | | | | read_sectors | [uint64](#uint64) | | | | read_time_ms | [uint64](#uint64) | | | | write_completed | [uint64](#uint64) | | | | write_merged | [uint64](#uint64) | | | | write_sectors | [uint64](#uint64) | | | | write_time_ms | [uint64](#uint64) | | | | io_in_progress | [uint64](#uint64) | | | | io_time_ms | [uint64](#uint64) | | | | io_time_weighted_ms | [uint64](#uint64) | | | | discard_completed | [uint64](#uint64) | | | | discard_merged | [uint64](#uint64) | | | | discard_sectors | [uint64](#uint64) | | | | discard_time_ms | [uint64](#uint64) | | | ### DiskStats | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | total | [DiskStat](#machine.DiskStat) | | | | devices | [DiskStat](#machine.DiskStat) | repeated | | ### DiskStatsResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [DiskStats](#machine.DiskStats) | repeated | | ### DiskUsageInfo DiskUsageInfo describes a file or directory's information for du command | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | name | [string](#string) | | Name is the name (including prefixed path) of the file or directory | | size | [int64](#int64) | | Size indicates the number of bytes contained within the file | | error | [string](#string) | | Error describes any error encountered while trying to read the file information. | | relative_name | [string](#string) | | RelativeName is the name of the file or directory relative to the RootPath | ### DiskUsageRequest DiskUsageRequest describes a request to list disk usage of directories and regular files | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | recursion_depth | [int32](#int32) | | RecursionDepth indicates how many levels of subdirectories should be recursed. The default (0) indicates that no limit should be enforced. | | all | [bool](#bool) | | All write sizes for all files, not just directories. | | threshold | [int64](#int64) | | Threshold exclude entries smaller than SIZE if positive, or entries greater than SIZE if negative. | | paths | [string](#string) | repeated | DiskUsagePaths is the list of directories to calculate disk usage for. | ### DmesgRequest dmesg | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | follow | [bool](#bool) | | | | tail | [bool](#bool) | | | ### EtcdAlarm | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | member_alarms | [EtcdMemberAlarm](#machine.EtcdMemberAlarm) | repeated | | ### EtcdAlarmDisarm | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | member_alarms | [EtcdMemberAlarm](#machine.EtcdMemberAlarm) | repeated | | ### EtcdAlarmDisarmResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdAlarmDisarm](#machine.EtcdAlarmDisarm) | repeated | | ### EtcdAlarmListResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdAlarm](#machine.EtcdAlarm) | repeated | | ### EtcdClusterDowngrade | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cluster_version | [string](#string) | | | ### EtcdDefragment | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### EtcdDefragmentResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdDefragment](#machine.EtcdDefragment) | repeated | | ### EtcdDowngradeCancel | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | cluster_downgrade | [EtcdClusterDowngrade](#machine.EtcdClusterDowngrade) | | | ### EtcdDowngradeCancelResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdDowngradeCancel](#machine.EtcdDowngradeCancel) | repeated | | ### EtcdDowngradeEnable | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | cluster_downgrade | [EtcdClusterDowngrade](#machine.EtcdClusterDowngrade) | | | ### EtcdDowngradeEnableRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | version | [string](#string) | | | ### EtcdDowngradeEnableResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdDowngradeEnable](#machine.EtcdDowngradeEnable) | repeated | | ### EtcdDowngradeValidate | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | cluster_downgrade | [EtcdClusterDowngrade](#machine.EtcdClusterDowngrade) | | | ### EtcdDowngradeValidateRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | version | [string](#string) | | | ### EtcdDowngradeValidateResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdDowngradeValidate](#machine.EtcdDowngradeValidate) | repeated | | ### EtcdForfeitLeadership | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | member | [string](#string) | | | ### EtcdForfeitLeadershipRequest ### EtcdForfeitLeadershipResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdForfeitLeadership](#machine.EtcdForfeitLeadership) | repeated | | ### EtcdLeaveCluster | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### EtcdLeaveClusterRequest ### EtcdLeaveClusterResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdLeaveCluster](#machine.EtcdLeaveCluster) | repeated | | ### EtcdMember EtcdMember describes a single etcd member. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [uint64](#uint64) | | member ID. | | hostname | [string](#string) | | human-readable name of the member. | | peer_urls | [string](#string) | repeated | the list of URLs the member exposes to clients for communication. | | client_urls | [string](#string) | repeated | the list of URLs the member exposes to the cluster for communication. | | is_learner | [bool](#bool) | | learner flag | ### EtcdMemberAlarm | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member_id | [uint64](#uint64) | | | | alarm | [EtcdMemberAlarm.AlarmType](#machine.EtcdMemberAlarm.AlarmType) | | | ### EtcdMemberListRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | query_local | [bool](#bool) | | | ### EtcdMemberListResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdMembers](#machine.EtcdMembers) | repeated | | ### EtcdMemberStatus | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | storage_version | [string](#string) | | | | member_id | [uint64](#uint64) | | | | protocol_version | [string](#string) | | | | db_size | [int64](#int64) | | | | db_size_in_use | [int64](#int64) | | | | leader | [uint64](#uint64) | | | | raft_index | [uint64](#uint64) | | | | raft_term | [uint64](#uint64) | | | | raft_applied_index | [uint64](#uint64) | | | | errors | [string](#string) | repeated | | | is_learner | [bool](#bool) | | | ### EtcdMembers EtcdMembers contains the list of members registered on the host. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | legacy_members | [string](#string) | repeated | list of member hostnames. | | members | [EtcdMember](#machine.EtcdMember) | repeated | the list of etcd members registered on the node. | ### EtcdRecover | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### EtcdRecoverResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdRecover](#machine.EtcdRecover) | repeated | | ### EtcdRemoveMember | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### EtcdRemoveMemberByID | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### EtcdRemoveMemberByIDRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member_id | [uint64](#uint64) | | | ### EtcdRemoveMemberByIDResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdRemoveMemberByID](#machine.EtcdRemoveMemberByID) | repeated | | ### EtcdRemoveMemberRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member | [string](#string) | | | ### EtcdRemoveMemberResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdRemoveMember](#machine.EtcdRemoveMember) | repeated | | ### EtcdSnapshotRequest ### EtcdStatus | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | member_status | [EtcdMemberStatus](#machine.EtcdMemberStatus) | | | ### EtcdStatusResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [EtcdStatus](#machine.EtcdStatus) | repeated | | ### Event | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | data | [google.protobuf.Any](#google.protobuf.Any) | | | | id | [string](#string) | | | | actor_id | [string](#string) | | | ### EventsRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tail_events | [int32](#int32) | | | | tail_id | [string](#string) | | | | tail_seconds | [int32](#int32) | | | | with_actor_id | [string](#string) | | | ### FeaturesInfo FeaturesInfo describes individual Talos features that can be switched on or off. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rbac | [bool](#bool) | | RBAC is true if role-based access control is enabled. | ### FileInfo FileInfo describes a file or directory's information | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | name | [string](#string) | | Name is the name (including prefixed path) of the file or directory | | size | [int64](#int64) | | Size indicates the number of bytes contained within the file | | mode | [uint32](#uint32) | | Mode is the bitmap of UNIX mode/permission flags of the file | | modified | [int64](#int64) | | Modified indicates the UNIX timestamp at which the file was last modified | | is_dir | [bool](#bool) | | IsDir indicates that the file is a directory | | error | [string](#string) | | Error describes any error encountered while trying to read the file information. | | link | [string](#string) | | Link is filled with symlink target | | relative_name | [string](#string) | | RelativeName is the name of the file or directory relative to the RootPath | | uid | [uint32](#uint32) | | Owner uid | | gid | [uint32](#uint32) | | Owner gid | | xattrs | [Xattr](#machine.Xattr) | repeated | Extended attributes (if present and requested) | ### GenerateClientConfiguration | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | ca | [bytes](#bytes) | | PEM-encoded CA certificate. | | crt | [bytes](#bytes) | | PEM-encoded generated client certificate. | | key | [bytes](#bytes) | | PEM-encoded generated client key. | | talosconfig | [bytes](#bytes) | | Client configuration (talosconfig) file content. | ### GenerateClientConfigurationRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | roles | [string](#string) | repeated | Roles in the generated client certificate. | | crt_ttl | [google.protobuf.Duration](#google.protobuf.Duration) | | Client certificate TTL. | ### GenerateClientConfigurationResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [GenerateClientConfiguration](#machine.GenerateClientConfiguration) | repeated | | ### Hostname | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | hostname | [string](#string) | | | ### HostnameResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Hostname](#machine.Hostname) | repeated | | ### ImageListRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [common.ContainerdNamespace](#common.ContainerdNamespace) | | Containerd namespace to use. | ### ImageListResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | name | [string](#string) | | | | digest | [string](#string) | | | | size | [int64](#int64) | | | | created_at | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | ### ImagePull | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### ImagePullRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [common.ContainerdNamespace](#common.ContainerdNamespace) | | Containerd namespace to use. | | reference | [string](#string) | | Image reference to pull. | ### ImagePullResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ImagePull](#machine.ImagePull) | repeated | | ### InstallConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | install_disk | [string](#string) | | | | install_image | [string](#string) | | | ### ListRequest ListRequest describes a request to list the contents of a directory. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | root | [string](#string) | | Root indicates the root directory for the list. If not indicated, '/' is presumed. | | recurse | [bool](#bool) | | Recurse indicates that subdirectories should be recursed. | | recursion_depth | [int32](#int32) | | RecursionDepth indicates how many levels of subdirectories should be recursed. The default (0) indicates that no limit should be enforced. | | types | [ListRequest.Type](#machine.ListRequest.Type) | repeated | Types indicates what file type should be returned. If not indicated, all files will be returned. | | report_xattrs | [bool](#bool) | | Report xattrs | ### LoadAvg | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | load1 | [double](#double) | | | | load5 | [double](#double) | | | | load15 | [double](#double) | | | ### LoadAvgResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [LoadAvg](#machine.LoadAvg) | repeated | | ### LogsContainer LogsContainer describes all available registered log containers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | ids | [string](#string) | repeated | | ### LogsContainersResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [LogsContainer](#machine.LogsContainer) | repeated | | ### LogsRequest rpc logs The request message containing the process name. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | id | [string](#string) | | | | driver | [common.ContainerDriver](#common.ContainerDriver) | | driver might be default "containerd" or "cri" | | follow | [bool](#bool) | | | | tail_lines | [int32](#int32) | | | ### MachineConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [MachineConfig.MachineType](#machine.MachineConfig.MachineType) | | | | install_config | [InstallConfig](#machine.InstallConfig) | | | | network_config | [NetworkConfig](#machine.NetworkConfig) | | | | kubernetes_version | [string](#string) | | | ### MachineStatusEvent MachineStatusEvent reports changes to the MachineStatus resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | stage | [MachineStatusEvent.MachineStage](#machine.MachineStatusEvent.MachineStage) | | | | status | [MachineStatusEvent.MachineStatus](#machine.MachineStatusEvent.MachineStatus) | | | ### MachineStatusEvent.MachineStatus | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | unmet_conditions | [MachineStatusEvent.MachineStatus.UnmetCondition](#machine.MachineStatusEvent.MachineStatus.UnmetCondition) | repeated | | ### MachineStatusEvent.MachineStatus.UnmetCondition | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | reason | [string](#string) | | | ### MemInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | memtotal | [uint64](#uint64) | | | | memfree | [uint64](#uint64) | | | | memavailable | [uint64](#uint64) | | | | buffers | [uint64](#uint64) | | | | cached | [uint64](#uint64) | | | | swapcached | [uint64](#uint64) | | | | active | [uint64](#uint64) | | | | inactive | [uint64](#uint64) | | | | activeanon | [uint64](#uint64) | | | | inactiveanon | [uint64](#uint64) | | | | activefile | [uint64](#uint64) | | | | inactivefile | [uint64](#uint64) | | | | unevictable | [uint64](#uint64) | | | | mlocked | [uint64](#uint64) | | | | swaptotal | [uint64](#uint64) | | | | swapfree | [uint64](#uint64) | | | | dirty | [uint64](#uint64) | | | | writeback | [uint64](#uint64) | | | | anonpages | [uint64](#uint64) | | | | mapped | [uint64](#uint64) | | | | shmem | [uint64](#uint64) | | | | slab | [uint64](#uint64) | | | | sreclaimable | [uint64](#uint64) | | | | sunreclaim | [uint64](#uint64) | | | | kernelstack | [uint64](#uint64) | | | | pagetables | [uint64](#uint64) | | | | nfsunstable | [uint64](#uint64) | | | | bounce | [uint64](#uint64) | | | | writebacktmp | [uint64](#uint64) | | | | commitlimit | [uint64](#uint64) | | | | committedas | [uint64](#uint64) | | | | vmalloctotal | [uint64](#uint64) | | | | vmallocused | [uint64](#uint64) | | | | vmallocchunk | [uint64](#uint64) | | | | hardwarecorrupted | [uint64](#uint64) | | | | anonhugepages | [uint64](#uint64) | | | | shmemhugepages | [uint64](#uint64) | | | | shmempmdmapped | [uint64](#uint64) | | | | cmatotal | [uint64](#uint64) | | | | cmafree | [uint64](#uint64) | | | | hugepagestotal | [uint64](#uint64) | | | | hugepagesfree | [uint64](#uint64) | | | | hugepagesrsvd | [uint64](#uint64) | | | | hugepagessurp | [uint64](#uint64) | | | | hugepagesize | [uint64](#uint64) | | | | directmap4k | [uint64](#uint64) | | | | directmap2m | [uint64](#uint64) | | | | directmap1g | [uint64](#uint64) | | | ### Memory | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | meminfo | [MemInfo](#machine.MemInfo) | | | ### MemoryResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Memory](#machine.Memory) | repeated | | ### MetaDelete | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### MetaDeleteRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [uint32](#uint32) | | | ### MetaDeleteResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [MetaDelete](#machine.MetaDelete) | repeated | | ### MetaWrite | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### MetaWriteRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [uint32](#uint32) | | | | value | [bytes](#bytes) | | | ### MetaWriteResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [MetaWrite](#machine.MetaWrite) | repeated | | ### MountStat The messages message containing the requested processes. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | filesystem | [string](#string) | | | | size | [uint64](#uint64) | | | | available | [uint64](#uint64) | | | | mounted_on | [string](#string) | | | ### Mounts The messages message containing the requested df stats. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | stats | [MountStat](#machine.MountStat) | repeated | | ### MountsResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Mounts](#machine.Mounts) | repeated | | ### NetDev | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | rx_bytes | [uint64](#uint64) | | | | rx_packets | [uint64](#uint64) | | | | rx_errors | [uint64](#uint64) | | | | rx_dropped | [uint64](#uint64) | | | | rx_fifo | [uint64](#uint64) | | | | rx_frame | [uint64](#uint64) | | | | rx_compressed | [uint64](#uint64) | | | | rx_multicast | [uint64](#uint64) | | | | tx_bytes | [uint64](#uint64) | | | | tx_packets | [uint64](#uint64) | | | | tx_errors | [uint64](#uint64) | | | | tx_dropped | [uint64](#uint64) | | | | tx_fifo | [uint64](#uint64) | | | | tx_collisions | [uint64](#uint64) | | | | tx_carrier | [uint64](#uint64) | | | | tx_compressed | [uint64](#uint64) | | | ### Netstat | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | connectrecord | [ConnectRecord](#machine.ConnectRecord) | repeated | | ### NetstatRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | filter | [NetstatRequest.Filter](#machine.NetstatRequest.Filter) | | | | feature | [NetstatRequest.Feature](#machine.NetstatRequest.Feature) | | | | l4proto | [NetstatRequest.L4proto](#machine.NetstatRequest.L4proto) | | | | netns | [NetstatRequest.NetNS](#machine.NetstatRequest.NetNS) | | | ### NetstatRequest.Feature | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pid | [bool](#bool) | | | ### NetstatRequest.L4proto | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tcp | [bool](#bool) | | | | tcp6 | [bool](#bool) | | | | udp | [bool](#bool) | | | | udp6 | [bool](#bool) | | | | udplite | [bool](#bool) | | | | udplite6 | [bool](#bool) | | | | raw | [bool](#bool) | | | | raw6 | [bool](#bool) | | | ### NetstatRequest.NetNS | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostnetwork | [bool](#bool) | | | | netns | [string](#string) | repeated | | | allnetns | [bool](#bool) | | | ### NetstatResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Netstat](#machine.Netstat) | repeated | | ### NetworkConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostname | [string](#string) | | | | interfaces | [NetworkDeviceConfig](#machine.NetworkDeviceConfig) | repeated | | ### NetworkDeviceConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | interface | [string](#string) | | | | cidr | [string](#string) | | | | mtu | [int32](#int32) | | | | dhcp | [bool](#bool) | | | | ignore | [bool](#bool) | | | | dhcp_options | [DHCPOptionsConfig](#machine.DHCPOptionsConfig) | | | | routes | [RouteConfig](#machine.RouteConfig) | repeated | | ### NetworkDeviceStats | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | total | [NetDev](#machine.NetDev) | | | | devices | [NetDev](#machine.NetDev) | repeated | | ### NetworkDeviceStatsResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [NetworkDeviceStats](#machine.NetworkDeviceStats) | repeated | | ### PacketCaptureRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | interface | [string](#string) | | Interface name to perform packet capture on. | | promiscuous | [bool](#bool) | | Enable promiscuous mode. | | snap_len | [uint32](#uint32) | | Snap length in bytes. | | bpf_filter | [BPFInstruction](#machine.BPFInstruction) | repeated | BPF filter. | ### PhaseEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | phase | [string](#string) | | | | action | [PhaseEvent.Action](#machine.PhaseEvent.Action) | | | ### PlatformInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | mode | [string](#string) | | | ### Process | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | processes | [ProcessInfo](#machine.ProcessInfo) | repeated | | ### ProcessInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pid | [int32](#int32) | | | | ppid | [int32](#int32) | | | | state | [string](#string) | | | | threads | [int32](#int32) | | | | cpu_time | [double](#double) | | | | virtual_memory | [uint64](#uint64) | | | | resident_memory | [uint64](#uint64) | | | | command | [string](#string) | | | | executable | [string](#string) | | | | args | [string](#string) | | | | label | [string](#string) | | | ### ProcessesResponse rpc processes | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Process](#machine.Process) | repeated | | ### ReadRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | path | [string](#string) | | | ### Reboot The reboot message containing the reboot status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | actor_id | [string](#string) | | | ### RebootRequest rpc reboot | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mode | [RebootRequest.Mode](#machine.RebootRequest.Mode) | | | ### RebootResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Reboot](#machine.Reboot) | repeated | | ### Reset The reset message containing the restart status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | actor_id | [string](#string) | | | ### ResetPartitionSpec rpc reset | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | label | [string](#string) | | | | wipe | [bool](#bool) | | | ### ResetRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | graceful | [bool](#bool) | | Graceful indicates whether node should leave etcd before the upgrade, it also enforces etcd checks before leaving. | | reboot | [bool](#bool) | | Reboot indicates whether node should reboot or halt after resetting. | | system_partitions_to_wipe | [ResetPartitionSpec](#machine.ResetPartitionSpec) | repeated | System_partitions_to_wipe lists specific system disk partitions to be reset (wiped). If system_partitions_to_wipe is empty, all the partitions are erased. | | user_disks_to_wipe | [string](#string) | repeated | UserDisksToWipe lists specific connected block devices to be reset (wiped). | | mode | [ResetRequest.WipeMode](#machine.ResetRequest.WipeMode) | | WipeMode defines which devices should be wiped. | ### ResetResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Reset](#machine.Reset) | repeated | | ### Restart | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### RestartEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cmd | [int64](#int64) | | | ### RestartRequest rpc restart The request message containing the process to restart. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | id | [string](#string) | | | | driver | [common.ContainerDriver](#common.ContainerDriver) | | driver might be default "containerd" or "cri" | ### RestartResponse The messages message containing the restart status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Restart](#machine.Restart) | repeated | | ### Rollback | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### RollbackRequest rpc rollback ### RollbackResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Rollback](#machine.Rollback) | repeated | | ### RouteConfig | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | network | [string](#string) | | | | gateway | [string](#string) | | | | metric | [uint32](#uint32) | | | ### SequenceEvent rpc events | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | sequence | [string](#string) | | | | action | [SequenceEvent.Action](#machine.SequenceEvent.Action) | | | | error | [common.Error](#common.Error) | | | ### ServiceEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | msg | [string](#string) | | | | state | [string](#string) | | | | ts | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | ### ServiceEvents | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | events | [ServiceEvent](#machine.ServiceEvent) | repeated | | ### ServiceHealth | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | unknown | [bool](#bool) | | | | healthy | [bool](#bool) | | | | last_message | [string](#string) | | | | last_change | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | ### ServiceInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | | | state | [string](#string) | | | | events | [ServiceEvents](#machine.ServiceEvents) | | | | health | [ServiceHealth](#machine.ServiceHealth) | | | ### ServiceList rpc servicelist | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | services | [ServiceInfo](#machine.ServiceInfo) | repeated | | ### ServiceListResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ServiceList](#machine.ServiceList) | repeated | | ### ServiceRestart | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | resp | [string](#string) | | | ### ServiceRestartRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | | ### ServiceRestartResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ServiceRestart](#machine.ServiceRestart) | repeated | | ### ServiceStart | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | resp | [string](#string) | | | ### ServiceStartRequest rpc servicestart | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | | ### ServiceStartResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ServiceStart](#machine.ServiceStart) | repeated | | ### ServiceStateEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | service | [string](#string) | | | | action | [ServiceStateEvent.Action](#machine.ServiceStateEvent.Action) | | | | message | [string](#string) | | | | health | [ServiceHealth](#machine.ServiceHealth) | | | ### ServiceStop | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | resp | [string](#string) | | | ### ServiceStopRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [string](#string) | | | ### ServiceStopResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [ServiceStop](#machine.ServiceStop) | repeated | | ### Shutdown rpc shutdown The messages message containing the shutdown status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | actor_id | [string](#string) | | | ### ShutdownRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | force | [bool](#bool) | | Force indicates whether node should shutdown without first cordening and draining | ### ShutdownResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Shutdown](#machine.Shutdown) | repeated | | ### SoftIRQStat | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hi | [uint64](#uint64) | | | | timer | [uint64](#uint64) | | | | net_tx | [uint64](#uint64) | | | | net_rx | [uint64](#uint64) | | | | block | [uint64](#uint64) | | | | block_io_poll | [uint64](#uint64) | | | | tasklet | [uint64](#uint64) | | | | sched | [uint64](#uint64) | | | | hrtimer | [uint64](#uint64) | | | | rcu | [uint64](#uint64) | | | ### Stat The messages message containing the requested stat. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | id | [string](#string) | | | | memory_usage | [uint64](#uint64) | | | | cpu_usage | [uint64](#uint64) | | | | pod_id | [string](#string) | | | | name | [string](#string) | | | ### Stats The messages message containing the requested stats. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | stats | [Stat](#machine.Stat) | repeated | | ### StatsRequest The request message containing the containerd namespace. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | namespace | [string](#string) | | | | driver | [common.ContainerDriver](#common.ContainerDriver) | | driver might be default "containerd" or "cri" | ### StatsResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Stats](#machine.Stats) | repeated | | ### SystemStat | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | boot_time | [uint64](#uint64) | | | | cpu_total | [CPUStat](#machine.CPUStat) | | | | cpu | [CPUStat](#machine.CPUStat) | repeated | | | irq_total | [uint64](#uint64) | | | | irq | [uint64](#uint64) | repeated | | | context_switches | [uint64](#uint64) | | | | process_created | [uint64](#uint64) | | | | process_running | [uint64](#uint64) | | | | process_blocked | [uint64](#uint64) | | | | soft_irq_total | [uint64](#uint64) | | | | soft_irq | [SoftIRQStat](#machine.SoftIRQStat) | | | ### SystemStatResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [SystemStat](#machine.SystemStat) | repeated | | ### TaskEvent | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | task | [string](#string) | | | | action | [TaskEvent.Action](#machine.TaskEvent.Action) | | | ### Upgrade | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | ack | [string](#string) | | | | actor_id | [string](#string) | | | ### UpgradeRequest rpc upgrade | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | preserve | [bool](#bool) | | | | stage | [bool](#bool) | | | | force | [bool](#bool) | | | | reboot_mode | [UpgradeRequest.RebootMode](#machine.UpgradeRequest.RebootMode) | | | ### UpgradeResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Upgrade](#machine.Upgrade) | repeated | | ### Version | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | version | [VersionInfo](#machine.VersionInfo) | | | | platform | [PlatformInfo](#machine.PlatformInfo) | | | | features | [FeaturesInfo](#machine.FeaturesInfo) | | Features describe individual Talos features that can be switched on or off. | ### VersionInfo | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tag | [string](#string) | | | | sha | [string](#string) | | | | built | [string](#string) | | | | go_version | [string](#string) | | | | os | [string](#string) | | | | arch | [string](#string) | | | ### VersionResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Version](#machine.Version) | repeated | | ### Xattr | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | data | [bytes](#bytes) | | | ### ApplyConfigurationRequest.Mode | Name | Number | Description | | ---- | ------ | ----------- | | REBOOT | 0 | | | AUTO | 1 | | | NO_REBOOT | 2 | | | STAGED | 3 | | | TRY | 4 | | ### ConnectRecord.State | Name | Number | Description | | ---- | ------ | ----------- | | RESERVED | 0 | | | ESTABLISHED | 1 | | | SYN_SENT | 2 | | | SYN_RECV | 3 | | | FIN_WAIT1 | 4 | | | FIN_WAIT2 | 5 | | | TIME_WAIT | 6 | | | CLOSE | 7 | | | CLOSEWAIT | 8 | | | LASTACK | 9 | | | LISTEN | 10 | | | CLOSING | 11 | | ### ConnectRecord.TimerActive | Name | Number | Description | | ---- | ------ | ----------- | | OFF | 0 | | | ON | 1 | | | KEEPALIVE | 2 | | | TIMEWAIT | 3 | | | PROBE | 4 | | ### EtcdMemberAlarm.AlarmType | Name | Number | Description | | ---- | ------ | ----------- | | NONE | 0 | | | NOSPACE | 1 | | | CORRUPT | 2 | | ### ListRequest.Type File type. | Name | Number | Description | | ---- | ------ | ----------- | | REGULAR | 0 | Regular file (not directory, symlink, etc). | | DIRECTORY | 1 | Directory. | | SYMLINK | 2 | Symbolic link. | ### MachineConfig.MachineType | Name | Number | Description | | ---- | ------ | ----------- | | TYPE_UNKNOWN | 0 | | | TYPE_INIT | 1 | | | TYPE_CONTROL_PLANE | 2 | | | TYPE_WORKER | 3 | | ### MachineStatusEvent.MachineStage | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN | 0 | | | BOOTING | 1 | | | INSTALLING | 2 | | | MAINTENANCE | 3 | | | RUNNING | 4 | | | REBOOTING | 5 | | | SHUTTING_DOWN | 6 | | | RESETTING | 7 | | | UPGRADING | 8 | | ### NetstatRequest.Filter | Name | Number | Description | | ---- | ------ | ----------- | | ALL | 0 | | | CONNECTED | 1 | | | LISTENING | 2 | | ### PhaseEvent.Action | Name | Number | Description | | ---- | ------ | ----------- | | START | 0 | | | STOP | 1 | | ### RebootRequest.Mode | Name | Number | Description | | ---- | ------ | ----------- | | DEFAULT | 0 | | | POWERCYCLE | 1 | | | FORCE | 2 | | ### ResetRequest.WipeMode | Name | Number | Description | | ---- | ------ | ----------- | | ALL | 0 | | | SYSTEM_DISK | 1 | | | USER_DISKS | 2 | | ### SequenceEvent.Action | Name | Number | Description | | ---- | ------ | ----------- | | NOOP | 0 | | | START | 1 | | | STOP | 2 | | ### ServiceStateEvent.Action | Name | Number | Description | | ---- | ------ | ----------- | | INITIALIZED | 0 | | | PREPARING | 1 | | | WAITING | 2 | | | RUNNING | 3 | | | STOPPING | 4 | | | FINISHED | 5 | | | FAILED | 6 | | | SKIPPED | 7 | | | STARTING | 8 | | ### TaskEvent.Action | Name | Number | Description | | ---- | ------ | ----------- | | START | 0 | | | STOP | 1 | | ### UpgradeRequest.RebootMode | Name | Number | Description | | ---- | ------ | ----------- | | DEFAULT | 0 | | | POWERCYCLE | 1 | | ### MachineService The machine service definition. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | ApplyConfiguration | [ApplyConfigurationRequest](#machine.ApplyConfigurationRequest) | [ApplyConfigurationResponse](#machine.ApplyConfigurationResponse) | | | Bootstrap | [BootstrapRequest](#machine.BootstrapRequest) | [BootstrapResponse](#machine.BootstrapResponse) | Bootstrap method makes control plane node enter etcd bootstrap mode. Node aborts etcd join sequence and creates single-node etcd cluster. If recover_etcd argument is specified, etcd is recovered from a snapshot uploaded with EtcdRecover. | | Containers | [ContainersRequest](#machine.ContainersRequest) | [ContainersResponse](#machine.ContainersResponse) | | | Copy | [CopyRequest](#machine.CopyRequest) | [.common.Data](#common.Data) stream | | | CPUFreqStats | [.google.protobuf.Empty](#google.protobuf.Empty) | [CPUFreqStatsResponse](#machine.CPUFreqStatsResponse) | | | CPUInfo | [.google.protobuf.Empty](#google.protobuf.Empty) | [CPUInfoResponse](#machine.CPUInfoResponse) | | | DiskStats | [.google.protobuf.Empty](#google.protobuf.Empty) | [DiskStatsResponse](#machine.DiskStatsResponse) | | | Dmesg | [DmesgRequest](#machine.DmesgRequest) | [.common.Data](#common.Data) stream | | | Events | [EventsRequest](#machine.EventsRequest) | [Event](#machine.Event) stream | | | EtcdMemberList | [EtcdMemberListRequest](#machine.EtcdMemberListRequest) | [EtcdMemberListResponse](#machine.EtcdMemberListResponse) | | | EtcdRemoveMemberByID | [EtcdRemoveMemberByIDRequest](#machine.EtcdRemoveMemberByIDRequest) | [EtcdRemoveMemberByIDResponse](#machine.EtcdRemoveMemberByIDResponse) | EtcdRemoveMemberByID removes a member from the etcd cluster identified by member ID. This API should be used to remove members which don't have an associated Talos node anymore. To remove a member with a running Talos node, use EtcdLeaveCluster API on the node to be removed. | | EtcdLeaveCluster | [EtcdLeaveClusterRequest](#machine.EtcdLeaveClusterRequest) | [EtcdLeaveClusterResponse](#machine.EtcdLeaveClusterResponse) | | | EtcdForfeitLeadership | [EtcdForfeitLeadershipRequest](#machine.EtcdForfeitLeadershipRequest) | [EtcdForfeitLeadershipResponse](#machine.EtcdForfeitLeadershipResponse) | | | EtcdRecover | [.common.Data](#common.Data) stream | [EtcdRecoverResponse](#machine.EtcdRecoverResponse) | EtcdRecover method uploads etcd data snapshot created with EtcdSnapshot to the node. Snapshot can be later used to recover the cluster via Bootstrap method. | | EtcdSnapshot | [EtcdSnapshotRequest](#machine.EtcdSnapshotRequest) | [.common.Data](#common.Data) stream | EtcdSnapshot method creates etcd data snapshot (backup) from the local etcd instance and streams it back to the client. This method is available only on control plane nodes (which run etcd). | | EtcdAlarmList | [.google.protobuf.Empty](#google.protobuf.Empty) | [EtcdAlarmListResponse](#machine.EtcdAlarmListResponse) | EtcdAlarmList lists etcd alarms for the current node. This method is available only on control plane nodes (which run etcd). | | EtcdAlarmDisarm | [.google.protobuf.Empty](#google.protobuf.Empty) | [EtcdAlarmDisarmResponse](#machine.EtcdAlarmDisarmResponse) | EtcdAlarmDisarm disarms etcd alarms for the current node. This method is available only on control plane nodes (which run etcd). | | EtcdDefragment | [.google.protobuf.Empty](#google.protobuf.Empty) | [EtcdDefragmentResponse](#machine.EtcdDefragmentResponse) | EtcdDefragment defragments etcd data directory for the current node. Defragmentation is a resource-heavy operation, so it should only run on a specific node. This method is available only on control plane nodes (which run etcd). | | EtcdStatus | [.google.protobuf.Empty](#google.protobuf.Empty) | [EtcdStatusResponse](#machine.EtcdStatusResponse) | EtcdStatus returns etcd status for the current member. This method is available only on control plane nodes (which run etcd). | | EtcdDowngradeValidate | [EtcdDowngradeValidateRequest](#machine.EtcdDowngradeValidateRequest) | [EtcdDowngradeValidateResponse](#machine.EtcdDowngradeValidateResponse) | EtcdDowngradeValidate validates etcd cluster for downgrade to a specific version. This method is available only on control plane nodes (which run etcd). | | EtcdDowngradeEnable | [EtcdDowngradeEnableRequest](#machine.EtcdDowngradeEnableRequest) | [EtcdDowngradeEnableResponse](#machine.EtcdDowngradeEnableResponse) | EtcdDowngradeEnable enables etcd cluster downgrade to a specific version. This method is available only on control plane nodes (which run etcd). | | EtcdDowngradeCancel | [.google.protobuf.Empty](#google.protobuf.Empty) | [EtcdDowngradeCancelResponse](#machine.EtcdDowngradeCancelResponse) | EtcdDowngradeCancel cancels etcd cluster downgrade that is in progress. This method is available only on control plane nodes (which run etcd). | | Hostname | [.google.protobuf.Empty](#google.protobuf.Empty) | [HostnameResponse](#machine.HostnameResponse) | | | Kubeconfig | [.google.protobuf.Empty](#google.protobuf.Empty) | [.common.Data](#common.Data) stream | | | List | [ListRequest](#machine.ListRequest) | [FileInfo](#machine.FileInfo) stream | | | DiskUsage | [DiskUsageRequest](#machine.DiskUsageRequest) | [DiskUsageInfo](#machine.DiskUsageInfo) stream | | | LoadAvg | [.google.protobuf.Empty](#google.protobuf.Empty) | [LoadAvgResponse](#machine.LoadAvgResponse) | | | Logs | [LogsRequest](#machine.LogsRequest) | [.common.Data](#common.Data) stream | | | LogsContainers | [.google.protobuf.Empty](#google.protobuf.Empty) | [LogsContainersResponse](#machine.LogsContainersResponse) | | | Memory | [.google.protobuf.Empty](#google.protobuf.Empty) | [MemoryResponse](#machine.MemoryResponse) | | | Mounts | [.google.protobuf.Empty](#google.protobuf.Empty) | [MountsResponse](#machine.MountsResponse) | | | NetworkDeviceStats | [.google.protobuf.Empty](#google.protobuf.Empty) | [NetworkDeviceStatsResponse](#machine.NetworkDeviceStatsResponse) | | | Processes | [.google.protobuf.Empty](#google.protobuf.Empty) | [ProcessesResponse](#machine.ProcessesResponse) | | | Read | [ReadRequest](#machine.ReadRequest) | [.common.Data](#common.Data) stream | | | Reboot | [RebootRequest](#machine.RebootRequest) | [RebootResponse](#machine.RebootResponse) | | | Restart | [RestartRequest](#machine.RestartRequest) | [RestartResponse](#machine.RestartResponse) | | | Rollback | [RollbackRequest](#machine.RollbackRequest) | [RollbackResponse](#machine.RollbackResponse) | | | Reset | [ResetRequest](#machine.ResetRequest) | [ResetResponse](#machine.ResetResponse) | | | ServiceList | [.google.protobuf.Empty](#google.protobuf.Empty) | [ServiceListResponse](#machine.ServiceListResponse) | | | ServiceRestart | [ServiceRestartRequest](#machine.ServiceRestartRequest) | [ServiceRestartResponse](#machine.ServiceRestartResponse) | | | ServiceStart | [ServiceStartRequest](#machine.ServiceStartRequest) | [ServiceStartResponse](#machine.ServiceStartResponse) | | | ServiceStop | [ServiceStopRequest](#machine.ServiceStopRequest) | [ServiceStopResponse](#machine.ServiceStopResponse) | | | Shutdown | [ShutdownRequest](#machine.ShutdownRequest) | [ShutdownResponse](#machine.ShutdownResponse) | | | Stats | [StatsRequest](#machine.StatsRequest) | [StatsResponse](#machine.StatsResponse) | | | SystemStat | [.google.protobuf.Empty](#google.protobuf.Empty) | [SystemStatResponse](#machine.SystemStatResponse) | | | Upgrade | [UpgradeRequest](#machine.UpgradeRequest) | [UpgradeResponse](#machine.UpgradeResponse) | Upgrade initiates the upgrade of the node to a new version of Talos.

Use LifecycleService Upgrade RPC instead. | | Version | [.google.protobuf.Empty](#google.protobuf.Empty) | [VersionResponse](#machine.VersionResponse) | | | GenerateClientConfiguration | [GenerateClientConfigurationRequest](#machine.GenerateClientConfigurationRequest) | [GenerateClientConfigurationResponse](#machine.GenerateClientConfigurationResponse) | GenerateClientConfiguration generates talosctl client configuration (talosconfig). | | PacketCapture | [PacketCaptureRequest](#machine.PacketCaptureRequest) | [.common.Data](#common.Data) stream | PacketCapture performs packet capture and streams back pcap file. | | Netstat | [NetstatRequest](#machine.NetstatRequest) | [NetstatResponse](#machine.NetstatResponse) | Netstat provides information about network connections. | | MetaWrite | [MetaWriteRequest](#machine.MetaWriteRequest) | [MetaWriteResponse](#machine.MetaWriteResponse) | MetaWrite writes a META key-value pair. | | MetaDelete | [MetaDeleteRequest](#machine.MetaDeleteRequest) | [MetaDeleteResponse](#machine.MetaDeleteResponse) | MetaDelete deletes a META key. | | ImageList | [ImageListRequest](#machine.ImageListRequest) | [ImageListResponse](#machine.ImageListResponse) stream | ImageList lists images in the CRI.

Use ImageService List RPC instead. | | ImagePull | [ImagePullRequest](#machine.ImagePullRequest) | [ImagePullResponse](#machine.ImagePullResponse) | ImagePull pulls an image into the CRI.

Use ImageService Pull RPC instead. |

Top

## resource/config/config.proto ### MachineConfigSpec MessageConfigSpec is the spec for the config.MachineConfig resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | yaml_marshalled | [bytes](#bytes) | | Contains YAML marshalled machine configuration.

Byte representation is preserved as the machine configuration was submitted to the node. | ### MachineTypeSpec MachineTypeSpec is the spec for the config.MachineType resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | machine_type | [MachineType](#resource.config.MachineType) | | | ### MachineType MachineType matches machine.Type constants. | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN | 0 | | | INIT | 1 | | | CONTROL_PLANE | 2 | | | WORKER | 3 | |

Top

## resource/definitions/enums/enums.proto ### BlockEncryptionKeyType BlockEncryptionKeyType describes encryption key type. | Name | Number | Description | | ---- | ------ | ----------- | | ENCRYPTION_KEY_STATIC | 0 | | | ENCRYPTION_KEY_NODE_ID | 1 | | | ENCRYPTION_KEY_KMS | 2 | | | ENCRYPTION_KEY_TPM | 3 | | ### BlockEncryptionProviderType BlockEncryptionProviderType describes encryption provider type. | Name | Number | Description | | ---- | ------ | ----------- | | ENCRYPTION_PROVIDER_NONE | 0 | | | ENCRYPTION_PROVIDER_LUKS2 | 1 | | ### BlockFSParameterType BlockFSParameterType describes Filesystem Parameter type. | Name | Number | Description | | ---- | ------ | ----------- | | FS_PARAMETER_TYPE_STRING_VALUE | 0 | | | FS_PARAMETER_TYPE_BOOLEAN_VALUE | 1 | | | FS_PARAMETER_TYPE_BINARY_VALUE | 2 | | ### BlockFilesystemType BlockFilesystemType describes filesystem type. | Name | Number | Description | | ---- | ------ | ----------- | | FILESYSTEM_TYPE_NONE | 0 | | | FILESYSTEM_TYPE_XFS | 1 | | | FILESYSTEM_TYPE_VFAT | 2 | | | FILESYSTEM_TYPE_EXT4 | 3 | | | FILESYSTEM_TYPE_ISO9660 | 4 | | | FILESYSTEM_TYPE_SWAP | 5 | | | FILESYSTEM_TYPE_VIRTIOFS | 6 | | ### BlockVolumePhase BlockVolumePhase describes volume phase. | Name | Number | Description | | ---- | ------ | ----------- | | VOLUME_PHASE_WAITING | 0 | | | VOLUME_PHASE_FAILED | 1 | | | VOLUME_PHASE_MISSING | 2 | | | VOLUME_PHASE_LOCATED | 3 | | | VOLUME_PHASE_PROVISIONED | 4 | | | VOLUME_PHASE_PREPARED | 5 | | | VOLUME_PHASE_READY | 6 | | | VOLUME_PHASE_CLOSED | 7 | | ### BlockVolumeType BlockVolumeType describes volume type. | Name | Number | Description | | ---- | ------ | ----------- | | VOLUME_TYPE_PARTITION | 0 | | | VOLUME_TYPE_DISK | 1 | | | VOLUME_TYPE_TMPFS | 2 | | | VOLUME_TYPE_DIRECTORY | 3 | | | VOLUME_TYPE_SYMLINK | 4 | | | VOLUME_TYPE_OVERLAY | 5 | | | VOLUME_TYPE_EXTERNAL | 6 | | ### CriImageCacheCopyStatus CriImageCacheCopyStatus describes image cache copy status type. | Name | Number | Description | | ---- | ------ | ----------- | | IMAGE_CACHE_COPY_STATUS_UNKNOWN | 0 | | | IMAGE_CACHE_COPY_STATUS_SKIPPED | 1 | | | IMAGE_CACHE_COPY_STATUS_PENDING | 2 | | | IMAGE_CACHE_COPY_STATUS_READY | 3 | | ### CriImageCacheStatus CriImageCacheStatus describes image cache status type. | Name | Number | Description | | ---- | ------ | ----------- | | IMAGE_CACHE_STATUS_UNKNOWN | 0 | | | IMAGE_CACHE_STATUS_DISABLED | 1 | | | IMAGE_CACHE_STATUS_PREPARING | 2 | | | IMAGE_CACHE_STATUS_READY | 3 | | ### KubespanPeerState KubespanPeerState is KubeSpan peer current state. | Name | Number | Description | | ---- | ------ | ----------- | | PEER_STATE_UNKNOWN | 0 | | | PEER_STATE_UP | 1 | | | PEER_STATE_DOWN | 2 | | ### MachineType MachineType represents a machine type. | Name | Number | Description | | ---- | ------ | ----------- | | TYPE_UNKNOWN | 0 | TypeUnknown represents undefined node type, when there is no machine configuration yet. | | TYPE_INIT | 1 | TypeInit type designates the first control plane node to come up. You can think of it like a bootstrap node. This node will perform the initial steps to bootstrap the cluster -- generation of TLS assets, starting of the control plane, etc. | | TYPE_CONTROL_PLANE | 2 | TypeControlPlane designates the node as a control plane member. This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler. | | TYPE_WORKER | 3 | TypeWorker designates the node as a worker node. This means it will be an available compute node for scheduling workloads. | ### NethelpersADLACPActive NethelpersADLACPActive is ADLACPActive. | Name | Number | Description | | ---- | ------ | ----------- | | ADLACP_ACTIVE_OFF | 0 | | | ADLACP_ACTIVE_ON | 1 | | ### NethelpersADSelect NethelpersADSelect is ADSelect. | Name | Number | Description | | ---- | ------ | ----------- | | AD_SELECT_STABLE | 0 | | | AD_SELECT_BANDWIDTH | 1 | | | AD_SELECT_COUNT | 2 | | ### NethelpersARPAllTargets NethelpersARPAllTargets is an ARP targets mode. | Name | Number | Description | | ---- | ------ | ----------- | | ARP_ALL_TARGETS_ANY | 0 | | | ARP_ALL_TARGETS_ALL | 1 | | ### NethelpersARPValidate NethelpersARPValidate is an ARP Validation mode. | Name | Number | Description | | ---- | ------ | ----------- | | ARP_VALIDATE_NONE | 0 | | | ARP_VALIDATE_ACTIVE | 1 | | | ARP_VALIDATE_BACKUP | 2 | | | ARP_VALIDATE_ALL | 3 | | | ARP_VALIDATE_FILTER | 4 | | | ARP_VALIDATE_FILTER_ACTIVE | 5 | | | ARP_VALIDATE_FILTER_BACKUP | 6 | | ### NethelpersAddressFlag NethelpersAddressFlag wraps IFF_* constants. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_ADDRESSFLAG_UNSPECIFIED | 0 | | | ADDRESS_TEMPORARY | 1 | | | ADDRESS_NO_DAD | 2 | | | ADDRESS_OPTIMISTIC | 4 | | | ADDRESS_DAD_FAILED | 8 | | | ADDRESS_HOME | 16 | | | ADDRESS_DEPRECATED | 32 | | | ADDRESS_TENTATIVE | 64 | | | ADDRESS_PERMANENT | 128 | | | ADDRESS_MANAGEMENT_TEMP | 256 | | | ADDRESS_NO_PREFIX_ROUTE | 512 | | | ADDRESS_MC_AUTO_JOIN | 1024 | | | ADDRESS_STABLE_PRIVACY | 2048 | | ### NethelpersAddressSortAlgorithm NethelpersAddressSortAlgorithm is an internal address sorting algorithm. | Name | Number | Description | | ---- | ------ | ----------- | | ADDRESS_SORT_ALGORITHM_V1 | 0 | | | ADDRESS_SORT_ALGORITHM_V2 | 1 | | ### NethelpersAutoHostnameKind NethelpersAutoHostnameKind is a kind of automatically generated hostname. | Name | Number | Description | | ---- | ------ | ----------- | | AUTO_HOSTNAME_KIND_OFF | 0 | | | AUTO_HOSTNAME_KIND_ADDR | 1 | | | AUTO_HOSTNAME_KIND_STABLE | 2 | | ### NethelpersBondMode NethelpersBondMode is a bond mode. | Name | Number | Description | | ---- | ------ | ----------- | | BOND_MODE_ROUNDROBIN | 0 | | | BOND_MODE_ACTIVE_BACKUP | 1 | | | BOND_MODE_XOR | 2 | | | BOND_MODE_BROADCAST | 3 | | | BOND_MODE8023_AD | 4 | | | BOND_MODE_TLB | 5 | | | BOND_MODE_ALB | 6 | | ### NethelpersBondXmitHashPolicy NethelpersBondXmitHashPolicy is a bond hash policy. | Name | Number | Description | | ---- | ------ | ----------- | | BOND_XMIT_POLICY_LAYER2 | 0 | | | BOND_XMIT_POLICY_LAYER34 | 1 | | | BOND_XMIT_POLICY_LAYER23 | 2 | | | BOND_XMIT_POLICY_ENCAP23 | 3 | | | BOND_XMIT_POLICY_ENCAP34 | 4 | | ### NethelpersClientIdentifier NethelpersClientIdentifier is a DHCP client identifier. | Name | Number | Description | | ---- | ------ | ----------- | | CLIENT_IDENTIFIER_NONE | 0 | | | CLIENT_IDENTIFIER_MAC | 1 | | | CLIENT_IDENTIFIER_DUID | 2 | | ### NethelpersConntrackState NethelpersConntrackState is a conntrack state. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_CONNTRACKSTATE_UNSPECIFIED | 0 | | | CONNTRACK_STATE_NEW | 8 | | | CONNTRACK_STATE_RELATED | 4 | | | CONNTRACK_STATE_ESTABLISHED | 2 | | | CONNTRACK_STATE_INVALID | 1 | | ### NethelpersDuplex NethelpersDuplex wraps ethtool.Duplex for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | HALF | 0 | | | FULL | 1 | | | UNKNOWN | 255 | | ### NethelpersFailOverMAC NethelpersFailOverMAC is a MAC failover mode. | Name | Number | Description | | ---- | ------ | ----------- | | FAIL_OVER_MAC_NONE | 0 | | | FAIL_OVER_MAC_ACTIVE | 1 | | | FAIL_OVER_MAC_FOLLOW | 2 | | ### NethelpersFamily NethelpersFamily is a network family. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_FAMILY_UNSPECIFIED | 0 | | | FAMILY_INET4 | 2 | | | FAMILY_INET6 | 10 | | ### NethelpersICMPType NethelpersICMPType is a ICMP packet type. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_ICMPTYPE_UNSPECIFIED | 0 | | | ICMP_TYPE_TIMESTAMP_REQUEST | 13 | | | ICMP_TYPE_TIMESTAMP_REPLY | 14 | | | ICMP_TYPE_ADDRESS_MASK_REQUEST | 17 | | | ICMP_TYPE_ADDRESS_MASK_REPLY | 18 | | ### NethelpersLACPRate NethelpersLACPRate is a LACP rate. | Name | Number | Description | | ---- | ------ | ----------- | | LACP_RATE_SLOW | 0 | | | LACP_RATE_FAST | 1 | | ### NethelpersLinkType NethelpersLinkType is a link type. | Name | Number | Description | | ---- | ------ | ----------- | | LINK_NETROM | 0 | | | LINK_ETHER | 1 | | | LINK_EETHER | 2 | | | LINK_AX25 | 3 | | | LINK_PRONET | 4 | | | LINK_CHAOS | 5 | | | LINK_IEE802 | 6 | | | LINK_ARCNET | 7 | | | LINK_ATALK | 8 | | | LINK_DLCI | 15 | | | LINK_ATM | 19 | | | LINK_METRICOM | 23 | | | LINK_IEEE1394 | 24 | | | LINK_EUI64 | 27 | | | LINK_INFINIBAND | 32 | | | LINK_SLIP | 256 | | | LINK_CSLIP | 257 | | | LINK_SLIP6 | 258 | | | LINK_CSLIP6 | 259 | | | LINK_RSRVD | 260 | | | LINK_ADAPT | 264 | | | LINK_ROSE | 270 | | | LINK_X25 | 271 | | | LINK_HWX25 | 272 | | | LINK_CAN | 280 | | | LINK_PPP | 512 | | | LINK_CISCO | 513 | | | LINK_HDLC | 513 | | | LINK_LAPB | 516 | | | LINK_DDCMP | 517 | | | LINK_RAWHDLC | 518 | | | LINK_TUNNEL | 768 | | | LINK_TUNNEL6 | 769 | | | LINK_FRAD | 770 | | | LINK_SKIP | 771 | | | LINK_LOOPBCK | 772 | | | LINK_LOCALTLK | 773 | | | LINK_FDDI | 774 | | | LINK_BIF | 775 | | | LINK_SIT | 776 | | | LINK_IPDDP | 777 | | | LINK_IPGRE | 778 | | | LINK_PIMREG | 779 | | | LINK_HIPPI | 780 | | | LINK_ASH | 781 | | | LINK_ECONET | 782 | | | LINK_IRDA | 783 | | | LINK_FCPP | 784 | | | LINK_FCAL | 785 | | | LINK_FCPL | 786 | | | LINK_FCFABRIC | 787 | | | LINK_FCFABRIC1 | 788 | | | LINK_FCFABRIC2 | 789 | | | LINK_FCFABRIC3 | 790 | | | LINK_FCFABRIC4 | 791 | | | LINK_FCFABRIC5 | 792 | | | LINK_FCFABRIC6 | 793 | | | LINK_FCFABRIC7 | 794 | | | LINK_FCFABRIC8 | 795 | | | LINK_FCFABRIC9 | 796 | | | LINK_FCFABRIC10 | 797 | | | LINK_FCFABRIC11 | 798 | | | LINK_FCFABRIC12 | 799 | | | LINK_IEE802TR | 800 | | | LINK_IEE80211 | 801 | | | LINK_IEE80211PRISM | 802 | | | LINK_IEE80211_RADIOTAP | 803 | | | LINK_IEE8021154 | 804 | | | LINK_IEE8021154MONITOR | 805 | | | LINK_PHONET | 820 | | | LINK_PHONETPIPE | 821 | | | LINK_CAIF | 822 | | | LINK_IP6GRE | 823 | | | LINK_NETLINK | 824 | | | LINK6_LOWPAN | 825 | | | LINK_VOID | 65535 | | | LINK_NONE | 65534 | | ### NethelpersMatchOperator NethelpersMatchOperator is a netfilter match operator. | Name | Number | Description | | ---- | ------ | ----------- | | OPERATOR_EQUAL | 0 | | | OPERATOR_NOT_EQUAL | 1 | | ### NethelpersNfTablesChainHook NethelpersNfTablesChainHook wraps nftables.ChainHook for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | CHAIN_HOOK_PREROUTING | 0 | | | CHAIN_HOOK_INPUT | 1 | | | CHAIN_HOOK_FORWARD | 2 | | | CHAIN_HOOK_OUTPUT | 3 | | | CHAIN_HOOK_POSTROUTING | 4 | | ### NethelpersNfTablesChainPriority NethelpersNfTablesChainPriority wraps nftables.ChainPriority for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_NFTABLESCHAINPRIORITY_UNSPECIFIED | 0 | | | CHAIN_PRIORITY_FIRST | -2147483648 | | | CHAIN_PRIORITY_CONNTRACK_DEFRAG | -400 | | | CHAIN_PRIORITY_RAW | -300 | | | CHAIN_PRIORITY_SE_LINUX_FIRST | -225 | | | CHAIN_PRIORITY_CONNTRACK | -200 | | | CHAIN_PRIORITY_MANGLE | -150 | | | CHAIN_PRIORITY_NAT_DEST | -100 | | | CHAIN_PRIORITY_FILTER | 0 | | | CHAIN_PRIORITY_SECURITY | 50 | | | CHAIN_PRIORITY_NAT_SOURCE | 100 | | | CHAIN_PRIORITY_SE_LINUX_LAST | 225 | | | CHAIN_PRIORITY_CONNTRACK_HELPER | 300 | | | CHAIN_PRIORITY_LAST | 2147483647 | | ### NethelpersNfTablesVerdict NethelpersNfTablesVerdict wraps nftables.Verdict for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | VERDICT_DROP | 0 | | | VERDICT_ACCEPT | 1 | | ### NethelpersOperationalState NethelpersOperationalState wraps rtnetlink.OperationalState for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | OPER_STATE_UNKNOWN | 0 | | | OPER_STATE_NOT_PRESENT | 1 | | | OPER_STATE_DOWN | 2 | | | OPER_STATE_LOWER_LAYER_DOWN | 3 | | | OPER_STATE_TESTING | 4 | | | OPER_STATE_DORMANT | 5 | | | OPER_STATE_UP | 6 | | ### NethelpersPort NethelpersPort wraps ethtool.Port for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | TWISTED_PAIR | 0 | | | AUI | 1 | | | MII | 2 | | | FIBRE | 3 | | | BNC | 4 | | | DIRECT_ATTACH | 5 | | | NONE | 239 | | | OTHER | 255 | | ### NethelpersPrimaryReselect NethelpersPrimaryReselect is an ARP targets mode. | Name | Number | Description | | ---- | ------ | ----------- | | PRIMARY_RESELECT_ALWAYS | 0 | | | PRIMARY_RESELECT_BETTER | 1 | | | PRIMARY_RESELECT_FAILURE | 2 | | ### NethelpersProtocol NethelpersProtocol is a inet protocol. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_PROTOCOL_UNSPECIFIED | 0 | | | PROTOCOL_ICMP | 1 | | | PROTOCOL_TCP | 6 | | | PROTOCOL_UDP | 17 | | | PROTOCOL_ICM_PV6 | 58 | | ### NethelpersRouteFlag NethelpersRouteFlag wraps RTM_F_* constants. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_ROUTEFLAG_UNSPECIFIED | 0 | | | ROUTE_NOTIFY | 256 | | | ROUTE_CLONED | 512 | | | ROUTE_EQUALIZE | 1024 | | | ROUTE_PREFIX | 2048 | | | ROUTE_LOOKUP_TABLE | 4096 | | | ROUTE_FIB_MATCH | 8192 | | | ROUTE_OFFLOAD | 16384 | | | ROUTE_TRAP | 32768 | | ### NethelpersRouteProtocol NethelpersRouteProtocol is a routing protocol. | Name | Number | Description | | ---- | ------ | ----------- | | PROTOCOL_UNSPEC | 0 | | | PROTOCOL_REDIRECT | 1 | | | PROTOCOL_KERNEL | 2 | | | PROTOCOL_BOOT | 3 | | | PROTOCOL_STATIC | 4 | | | PROTOCOL_RA | 9 | | | PROTOCOL_MRT | 10 | | | PROTOCOL_ZEBRA | 11 | | | PROTOCOL_BIRD | 12 | | | PROTOCOL_DNROUTED | 13 | | | PROTOCOL_XORP | 14 | | | PROTOCOL_NTK | 15 | | | PROTOCOL_DHCP | 16 | | | PROTOCOL_MRTD | 17 | | | PROTOCOL_KEEPALIVED | 18 | | | PROTOCOL_BABEL | 42 | | | PROTOCOL_OPENR | 99 | | | PROTOCOL_BGP | 186 | | | PROTOCOL_ISIS | 187 | | | PROTOCOL_OSPF | 188 | | | PROTOCOL_RIP | 189 | | | PROTOCOL_EIGRP | 192 | | ### NethelpersRouteType NethelpersRouteType is a route type. | Name | Number | Description | | ---- | ------ | ----------- | | TYPE_UNSPEC | 0 | | | TYPE_UNICAST | 1 | | | TYPE_LOCAL | 2 | | | TYPE_BROADCAST | 3 | | | TYPE_ANYCAST | 4 | | | TYPE_MULTICAST | 5 | | | TYPE_BLACKHOLE | 6 | | | TYPE_UNREACHABLE | 7 | | | TYPE_PROHIBIT | 8 | | | TYPE_THROW | 9 | | | TYPE_NAT | 10 | | | TYPE_X_RESOLVE | 11 | | ### NethelpersRoutingRuleAction NethelpersRoutingRuleAction is a routing rule action. | Name | Number | Description | | ---- | ------ | ----------- | | ROUTING_RULE_ACTION_UNSPEC | 0 | | | ROUTING_RULE_ACTION_UNICAST | 1 | | | ROUTING_RULE_ACTION_BLACKHOLE | 6 | | | ROUTING_RULE_ACTION_UNREACHABLE | 7 | | | ROUTING_RULE_ACTION_PROHIBIT | 8 | | ### NethelpersRoutingTable NethelpersRoutingTable is a routing table ID. | Name | Number | Description | | ---- | ------ | ----------- | | TABLE_UNSPEC | 0 | | | TABLE1 | 1 | | | TABLE2 | 2 | | | TABLE3 | 3 | | | TABLE4 | 4 | | | TABLE5 | 5 | | | TABLE6 | 6 | | | TABLE7 | 7 | | | TABLE8 | 8 | | | TABLE9 | 9 | | | TABLE10 | 10 | | | TABLE11 | 11 | | | TABLE12 | 12 | | | TABLE13 | 13 | | | TABLE14 | 14 | | | TABLE15 | 15 | | | TABLE16 | 16 | | | TABLE17 | 17 | | | TABLE18 | 18 | | | TABLE19 | 19 | | | TABLE20 | 20 | | | TABLE21 | 21 | | | TABLE22 | 22 | | | TABLE23 | 23 | | | TABLE24 | 24 | | | TABLE25 | 25 | | | TABLE26 | 26 | | | TABLE27 | 27 | | | TABLE28 | 28 | | | TABLE29 | 29 | | | TABLE30 | 30 | | | TABLE31 | 31 | | | TABLE32 | 32 | | | TABLE33 | 33 | | | TABLE34 | 34 | | | TABLE35 | 35 | | | TABLE36 | 36 | | | TABLE37 | 37 | | | TABLE38 | 38 | | | TABLE39 | 39 | | | TABLE40 | 40 | | | TABLE41 | 41 | | | TABLE42 | 42 | | | TABLE43 | 43 | | | TABLE44 | 44 | | | TABLE45 | 45 | | | TABLE46 | 46 | | | TABLE47 | 47 | | | TABLE48 | 48 | | | TABLE49 | 49 | | | TABLE50 | 50 | | | TABLE51 | 51 | | | TABLE52 | 52 | | | TABLE53 | 53 | | | TABLE54 | 54 | | | TABLE55 | 55 | | | TABLE56 | 56 | | | TABLE57 | 57 | | | TABLE58 | 58 | | | TABLE59 | 59 | | | TABLE60 | 60 | | | TABLE61 | 61 | | | TABLE62 | 62 | | | TABLE63 | 63 | | | TABLE64 | 64 | | | TABLE65 | 65 | | | TABLE66 | 66 | | | TABLE67 | 67 | | | TABLE68 | 68 | | | TABLE69 | 69 | | | TABLE70 | 70 | | | TABLE71 | 71 | | | TABLE72 | 72 | | | TABLE73 | 73 | | | TABLE74 | 74 | | | TABLE75 | 75 | | | TABLE76 | 76 | | | TABLE77 | 77 | | | TABLE78 | 78 | | | TABLE79 | 79 | | | TABLE80 | 80 | | | TABLE81 | 81 | | | TABLE82 | 82 | | | TABLE83 | 83 | | | TABLE84 | 84 | | | TABLE85 | 85 | | | TABLE86 | 86 | | | TABLE87 | 87 | | | TABLE88 | 88 | | | TABLE89 | 89 | | | TABLE90 | 90 | | | TABLE91 | 91 | | | TABLE92 | 92 | | | TABLE93 | 93 | | | TABLE94 | 94 | | | TABLE95 | 95 | | | TABLE96 | 96 | | | TABLE97 | 97 | | | TABLE98 | 98 | | | TABLE99 | 99 | | | TABLE100 | 100 | | | TABLE101 | 101 | | | TABLE102 | 102 | | | TABLE103 | 103 | | | TABLE104 | 104 | | | TABLE105 | 105 | | | TABLE106 | 106 | | | TABLE107 | 107 | | | TABLE108 | 108 | | | TABLE109 | 109 | | | TABLE110 | 110 | | | TABLE111 | 111 | | | TABLE112 | 112 | | | TABLE113 | 113 | | | TABLE114 | 114 | | | TABLE115 | 115 | | | TABLE116 | 116 | | | TABLE117 | 117 | | | TABLE118 | 118 | | | TABLE119 | 119 | | | TABLE120 | 120 | | | TABLE121 | 121 | | | TABLE122 | 122 | | | TABLE123 | 123 | | | TABLE124 | 124 | | | TABLE125 | 125 | | | TABLE126 | 126 | | | TABLE127 | 127 | | | TABLE128 | 128 | | | TABLE129 | 129 | | | TABLE130 | 130 | | | TABLE131 | 131 | | | TABLE132 | 132 | | | TABLE133 | 133 | | | TABLE134 | 134 | | | TABLE135 | 135 | | | TABLE136 | 136 | | | TABLE137 | 137 | | | TABLE138 | 138 | | | TABLE139 | 139 | | | TABLE140 | 140 | | | TABLE141 | 141 | | | TABLE142 | 142 | | | TABLE143 | 143 | | | TABLE144 | 144 | | | TABLE145 | 145 | | | TABLE146 | 146 | | | TABLE147 | 147 | | | TABLE148 | 148 | | | TABLE149 | 149 | | | TABLE150 | 150 | | | TABLE151 | 151 | | | TABLE152 | 152 | | | TABLE153 | 153 | | | TABLE154 | 154 | | | TABLE155 | 155 | | | TABLE156 | 156 | | | TABLE157 | 157 | | | TABLE158 | 158 | | | TABLE159 | 159 | | | TABLE160 | 160 | | | TABLE161 | 161 | | | TABLE162 | 162 | | | TABLE163 | 163 | | | TABLE164 | 164 | | | TABLE165 | 165 | | | TABLE166 | 166 | | | TABLE167 | 167 | | | TABLE168 | 168 | | | TABLE169 | 169 | | | TABLE170 | 170 | | | TABLE171 | 171 | | | TABLE172 | 172 | | | TABLE173 | 173 | | | TABLE174 | 174 | | | TABLE175 | 175 | | | TABLE176 | 176 | | | TABLE177 | 177 | | | TABLE178 | 178 | | | TABLE179 | 179 | | | TABLE180 | 180 | | | TABLE181 | 181 | | | TABLE182 | 182 | | | TABLE183 | 183 | | | TABLE184 | 184 | | | TABLE185 | 185 | | | TABLE186 | 186 | | | TABLE187 | 187 | | | TABLE188 | 188 | | | TABLE189 | 189 | | | TABLE190 | 190 | | | TABLE191 | 191 | | | TABLE192 | 192 | | | TABLE193 | 193 | | | TABLE194 | 194 | | | TABLE195 | 195 | | | TABLE196 | 196 | | | TABLE197 | 197 | | | TABLE198 | 198 | | | TABLE199 | 199 | | | TABLE200 | 200 | | | TABLE201 | 201 | | | TABLE202 | 202 | | | TABLE203 | 203 | | | TABLE204 | 204 | | | TABLE205 | 205 | | | TABLE206 | 206 | | | TABLE207 | 207 | | | TABLE208 | 208 | | | TABLE209 | 209 | | | TABLE210 | 210 | | | TABLE211 | 211 | | | TABLE212 | 212 | | | TABLE213 | 213 | | | TABLE214 | 214 | | | TABLE215 | 215 | | | TABLE216 | 216 | | | TABLE217 | 217 | | | TABLE218 | 218 | | | TABLE219 | 219 | | | TABLE220 | 220 | | | TABLE221 | 221 | | | TABLE222 | 222 | | | TABLE223 | 223 | | | TABLE224 | 224 | | | TABLE225 | 225 | | | TABLE226 | 226 | | | TABLE227 | 227 | | | TABLE228 | 228 | | | TABLE229 | 229 | | | TABLE230 | 230 | | | TABLE231 | 231 | | | TABLE232 | 232 | | | TABLE233 | 233 | | | TABLE234 | 234 | | | TABLE235 | 235 | | | TABLE236 | 236 | | | TABLE237 | 237 | | | TABLE238 | 238 | | | TABLE239 | 239 | | | TABLE240 | 240 | | | TABLE241 | 241 | | | TABLE242 | 242 | | | TABLE243 | 243 | | | TABLE244 | 244 | | | TABLE245 | 245 | | | TABLE246 | 246 | | | TABLE247 | 247 | | | TABLE248 | 248 | | | TABLE249 | 249 | | | TABLE250 | 250 | | | TABLE251 | 251 | | | TABLE252 | 252 | | | TABLE_DEFAULT | 253 | | | TABLE_MAIN | 254 | | | TABLE_LOCAL | 255 | | ### NethelpersScope NethelpersScope is an address scope. | Name | Number | Description | | ---- | ------ | ----------- | | SCOPE_GLOBAL | 0 | | | SCOPE_SITE | 200 | | | SCOPE_LINK | 253 | | | SCOPE_HOST | 254 | | | SCOPE_NOWHERE | 255 | | ### NethelpersVLANProtocol NethelpersVLANProtocol is a VLAN protocol. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_VLANPROTOCOL_UNSPECIFIED | 0 | | | VLAN_PROTOCOL8021_Q | 33024 | | | VLAN_PROTOCOL8021_AD | 34984 | | ### NethelpersWOLMode NethelpersWOLMode wraps ethtool.WOLMode for YAML marshaling. | Name | Number | Description | | ---- | ------ | ----------- | | NETHELPERS_WOLMODE_UNSPECIFIED | 0 | | | WOL_MODE_PHY | 1 | | | WOL_MODE_UNICAST | 2 | | | WOL_MODE_MULTICAST | 4 | | | WOL_MODE_BROADCAST | 8 | | | WOL_MODE_MAGIC | 32 | | | WOL_MODE_MAGIC_SECURE | 64 | | | WOL_MODE_FILTER | 128 | | ### NetworkConfigLayer NetworkConfigLayer describes network configuration layers, with lowest priority first. | Name | Number | Description | | ---- | ------ | ----------- | | CONFIG_DEFAULT | 0 | | | CONFIG_CMDLINE | 1 | | | CONFIG_PLATFORM | 2 | | | CONFIG_OPERATOR | 3 | | | CONFIG_MACHINE_CONFIGURATION | 4 | | ### NetworkOperator NetworkOperator enumerates Talos network operators. | Name | Number | Description | | ---- | ------ | ----------- | | OPERATOR_DHCP4 | 0 | | | OPERATOR_DHCP6 | 1 | | | OPERATOR_VIP | 2 | | ### RuntimeFIPSState RuntimeFIPSState describes the current FIPS status. | Name | Number | Description | | ---- | ------ | ----------- | | FIPS_STATE_DISABLED | 0 | | | FIPS_STATE_ENABLED | 1 | | | FIPS_STATE_STRICT | 2 | | ### RuntimeMachineStage RuntimeMachineStage describes the stage of the machine boot/run process. | Name | Number | Description | | ---- | ------ | ----------- | | MACHINE_STAGE_UNKNOWN | 0 | | | MACHINE_STAGE_BOOTING | 1 | | | MACHINE_STAGE_INSTALLING | 2 | | | MACHINE_STAGE_MAINTENANCE | 3 | | | MACHINE_STAGE_RUNNING | 4 | | | MACHINE_STAGE_REBOOTING | 5 | | | MACHINE_STAGE_SHUTTING_DOWN | 6 | | | MACHINE_STAGE_RESETTING | 7 | | | MACHINE_STAGE_UPGRADING | 8 | | ### RuntimeSELinuxState RuntimeSELinuxState describes the current SELinux status. | Name | Number | Description | | ---- | ------ | ----------- | | SE_LINUX_STATE_DISABLED | 0 | | | SE_LINUX_STATE_PERMISSIVE | 1 | | | SE_LINUX_STATE_ENFORCING | 2 | |

Top

## resource/definitions/block/block.proto ### DeviceSpec DeviceSpec is the spec for devices status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [string](#string) | | | | major | [int64](#int64) | | | | minor | [int64](#int64) | | | | partition_name | [string](#string) | | | | partition_number | [int64](#int64) | | | | generation | [int64](#int64) | | | | device_path | [string](#string) | | | | parent | [string](#string) | | | | secondaries | [string](#string) | repeated | | ### DiscoveredVolumeSpec DiscoveredVolumeSpec is the spec for DiscoveredVolumes resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | size | [uint64](#uint64) | | | | sector_size | [uint64](#uint64) | | | | io_size | [uint64](#uint64) | | | | name | [string](#string) | | | | uuid | [string](#string) | | | | label | [string](#string) | | | | block_size | [uint32](#uint32) | | | | filesystem_block_size | [uint32](#uint32) | | | | probed_size | [uint64](#uint64) | | | | partition_uuid | [string](#string) | | | | partition_type | [string](#string) | | | | partition_label | [string](#string) | | | | partition_index | [uint64](#uint64) | | | | type | [string](#string) | | | | device_path | [string](#string) | | | | parent | [string](#string) | | | | dev_path | [string](#string) | | | | parent_dev_path | [string](#string) | | | | pretty_size | [string](#string) | | | | offset | [uint64](#uint64) | | | ### DiscoveryRefreshRequestSpec DiscoveryRefreshRequestSpec is the spec for DiscoveryRefreshRequest. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | request | [int64](#int64) | | | ### DiscoveryRefreshStatusSpec DiscoveryRefreshStatusSpec is the spec for DiscoveryRefreshStatus status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | request | [int64](#int64) | | | ### DiskSelector DiskSelector selects a disk for the volume. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | match | [google.api.expr.v1alpha1.CheckedExpr](#google.api.expr.v1alpha1.CheckedExpr) | | | | external | [string](#string) | | | ### DiskSpec DiskSpec is the spec for Disks status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | size | [uint64](#uint64) | | | | io_size | [uint64](#uint64) | | | | sector_size | [uint64](#uint64) | | | | readonly | [bool](#bool) | | | | model | [string](#string) | | | | serial | [string](#string) | | | | modalias | [string](#string) | | | | wwid | [string](#string) | | | | bus_path | [string](#string) | | | | sub_system | [string](#string) | | | | transport | [string](#string) | | | | rotational | [bool](#bool) | | | | cdrom | [bool](#bool) | | | | dev_path | [string](#string) | | | | pretty_size | [string](#string) | | | | secondary_disks | [string](#string) | repeated | | | uuid | [string](#string) | | | | symlinks | [string](#string) | repeated | | ### EncryptionKey EncryptionKey is the spec for volume encryption key. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | slot | [int64](#int64) | | | | type | [talos.resource.definitions.enums.BlockEncryptionKeyType](#talos.resource.definitions.enums.BlockEncryptionKeyType) | | | | static_passphrase | [bytes](#bytes) | | | | kms_endpoint | [string](#string) | | | | tpm_check_secureboot_status_on_enroll | [bool](#bool) | | | | lock_to_state | [bool](#bool) | | | | tpmpc_rs | [int64](#int64) | repeated | | | tpm_pub_key_pc_rs | [int64](#int64) | repeated | | ### EncryptionSpec EncryptionSpec is the spec for volume encryption. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | provider | [talos.resource.definitions.enums.BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) | | | | keys | [EncryptionKey](#talos.resource.definitions.block.EncryptionKey) | repeated | | | cipher | [string](#string) | | | | key_size | [uint64](#uint64) | | | | block_size | [uint64](#uint64) | | | | perf_options | [string](#string) | repeated | | ### FilesystemSpec FilesystemSpec is the spec for volume filesystem. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [talos.resource.definitions.enums.BlockFilesystemType](#talos.resource.definitions.enums.BlockFilesystemType) | | | | label | [string](#string) | | | ### LocatorSpec LocatorSpec is the spec for volume locator. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | match | [google.api.expr.v1alpha1.CheckedExpr](#google.api.expr.v1alpha1.CheckedExpr) | | | | disk_match | [google.api.expr.v1alpha1.CheckedExpr](#google.api.expr.v1alpha1.CheckedExpr) | | | ### MountRequestSpec MountRequestSpec is the spec for MountRequest. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | volume_id | [string](#string) | | | | parent_mount_id | [string](#string) | | | | requesters | [string](#string) | repeated | | | requester_i_ds | [string](#string) | repeated | | | read_only | [bool](#bool) | | | | detached | [bool](#bool) | | | | disable_access_time | [bool](#bool) | | | | secure | [bool](#bool) | | | ### MountSpec MountSpec is the spec for volume mount. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | target_path | [string](#string) | | | | selinux_label | [string](#string) | | | | project_quota_support | [bool](#bool) | | | | parent_id | [string](#string) | | | | file_mode | [uint32](#uint32) | | | | uid | [int64](#int64) | | | | gid | [int64](#int64) | | | | recursive_relabel | [bool](#bool) | | | | bind_target | [string](#string) | | | | parameters | [ParameterSpec](#talos.resource.definitions.block.ParameterSpec) | repeated | | ### MountStatusSpec MountStatusSpec is the spec for MountStatus. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | spec | [MountRequestSpec](#talos.resource.definitions.block.MountRequestSpec) | | | | target | [string](#string) | | | | source | [string](#string) | | | | filesystem | [talos.resource.definitions.enums.BlockFilesystemType](#talos.resource.definitions.enums.BlockFilesystemType) | | | | read_only | [bool](#bool) | | | | project_quota_support | [bool](#bool) | | | | encryption_provider | [talos.resource.definitions.enums.BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) | | | | detached | [bool](#bool) | | | ### ParameterSpec ParameterSpec is a mount parameter. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [talos.resource.definitions.enums.BlockFSParameterType](#talos.resource.definitions.enums.BlockFSParameterType) | | | | name | [string](#string) | | | | string | [string](#string) | | | | binary | [bytes](#bytes) | | | ### PartitionSpec PartitionSpec is the spec for volume partitioning. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | min_size | [uint64](#uint64) | | | | max_size | [uint64](#uint64) | | | | grow | [bool](#bool) | | | | label | [string](#string) | | | | type_uuid | [string](#string) | | | | relative_max_size | [uint64](#uint64) | | | | negative_max_size | [bool](#bool) | | | ### ProvisioningSpec ProvisioningSpec is the spec for volume provisioning. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | disk_selector | [DiskSelector](#talos.resource.definitions.block.DiskSelector) | | | | partition_spec | [PartitionSpec](#talos.resource.definitions.block.PartitionSpec) | | | | wave | [int64](#int64) | | | | filesystem_spec | [FilesystemSpec](#talos.resource.definitions.block.FilesystemSpec) | | | ### SwapStatusSpec SwapStatusSpec is the spec for SwapStatuss resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device | [string](#string) | | | | size_bytes | [uint64](#uint64) | | | | size_human | [string](#string) | | | | used_bytes | [uint64](#uint64) | | | | used_human | [string](#string) | | | | priority | [int32](#int32) | | | | type | [string](#string) | | | ### SymlinkProvisioningSpec SymlinkProvisioningSpec is the spec for volume symlink. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | symlink_target_path | [string](#string) | | | | force | [bool](#bool) | | | ### SymlinkSpec SymlinkSpec is the spec for Symlinks resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | paths | [string](#string) | repeated | | ### SystemDiskSpec SystemDiskSpec is the spec for SystemDisks resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | disk_id | [string](#string) | | | | dev_path | [string](#string) | | | ### TPMEncryptionOptionsInfo TPMEncryptionOptionsInfo is the options for TPM-based encryption. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pc_rs | [int64](#int64) | repeated | | | pub_key_pc_rs | [int64](#int64) | repeated | | ### UserDiskConfigStatusSpec UserDiskConfigStatusSpec is the spec for UserDiskConfigStatus resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | torn_down | [bool](#bool) | | | ### VolumeConfigSpec VolumeConfigSpec is the spec for VolumeConfig resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | parent_id | [string](#string) | | | | type | [talos.resource.definitions.enums.BlockVolumeType](#talos.resource.definitions.enums.BlockVolumeType) | | | | provisioning | [ProvisioningSpec](#talos.resource.definitions.block.ProvisioningSpec) | | | | locator | [LocatorSpec](#talos.resource.definitions.block.LocatorSpec) | | | | mount | [MountSpec](#talos.resource.definitions.block.MountSpec) | | | | encryption | [EncryptionSpec](#talos.resource.definitions.block.EncryptionSpec) | | | | symlink | [SymlinkProvisioningSpec](#talos.resource.definitions.block.SymlinkProvisioningSpec) | | | ### VolumeMountRequestSpec VolumeMountRequestSpec is the spec for VolumeMountRequest. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | volume_id | [string](#string) | | | | requester | [string](#string) | | | | read_only | [bool](#bool) | | | | detached | [bool](#bool) | | | | disable_access_time | [bool](#bool) | | | | secure | [bool](#bool) | | | ### VolumeMountStatusSpec VolumeMountStatusSpec is the spec for VolumeMountStatus. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | volume_id | [string](#string) | | | | requester | [string](#string) | | | | target | [string](#string) | | | | read_only | [bool](#bool) | | | | detached | [bool](#bool) | | | | disable_access_time | [bool](#bool) | | | | secure | [bool](#bool) | | | ### VolumeStatusSpec VolumeStatusSpec is the spec for VolumeStatus resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | phase | [talos.resource.definitions.enums.BlockVolumePhase](#talos.resource.definitions.enums.BlockVolumePhase) | | | | location | [string](#string) | | | | error_message | [string](#string) | | | | uuid | [string](#string) | | | | partition_uuid | [string](#string) | | | | pre_fail_phase | [talos.resource.definitions.enums.BlockVolumePhase](#talos.resource.definitions.enums.BlockVolumePhase) | | | | parent_location | [string](#string) | | | | partition_index | [int64](#int64) | | | | size | [uint64](#uint64) | | | | filesystem | [talos.resource.definitions.enums.BlockFilesystemType](#talos.resource.definitions.enums.BlockFilesystemType) | | | | mount_location | [string](#string) | | | | encryption_provider | [talos.resource.definitions.enums.BlockEncryptionProviderType](#talos.resource.definitions.enums.BlockEncryptionProviderType) | | | | pretty_size | [string](#string) | | | | encryption_failed_syncs | [string](#string) | repeated | | | mount_spec | [MountSpec](#talos.resource.definitions.block.MountSpec) | | | | type | [talos.resource.definitions.enums.BlockVolumeType](#talos.resource.definitions.enums.BlockVolumeType) | | | | configured_encryption_keys | [string](#string) | repeated | | | symlink_spec | [SymlinkProvisioningSpec](#talos.resource.definitions.block.SymlinkProvisioningSpec) | | | | parent_id | [string](#string) | | | | encryption_locked_to_state | [bool](#bool) | | | | encryption_slot | [int64](#int64) | | | | tpm_encryption_options | [TPMEncryptionOptionsInfo](#talos.resource.definitions.block.TPMEncryptionOptionsInfo) | | | ### ZswapStatusSpec ZswapStatusSpec is the spec for ZswapStatus resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | total_size_bytes | [uint64](#uint64) | | | | total_size_human | [string](#string) | | | | stored_pages | [uint64](#uint64) | | | | pool_limit_hit | [uint64](#uint64) | | | | reject_reclaim_fail | [uint64](#uint64) | | | | reject_alloc_fail | [uint64](#uint64) | | | | reject_kmemcache_fail | [uint64](#uint64) | | | | reject_compress_fail | [uint64](#uint64) | | | | reject_compress_poor | [uint64](#uint64) | | | | written_back_pages | [uint64](#uint64) | | |

Top

## resource/definitions/cluster/cluster.proto ### AffiliateSpec AffiliateSpec describes Affiliate state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | node_id | [string](#string) | | | | addresses | [common.NetIP](#common.NetIP) | repeated | | | hostname | [string](#string) | | | | nodename | [string](#string) | | | | operating_system | [string](#string) | | | | machine_type | [talos.resource.definitions.enums.MachineType](#talos.resource.definitions.enums.MachineType) | | | | kube_span | [KubeSpanAffiliateSpec](#talos.resource.definitions.cluster.KubeSpanAffiliateSpec) | | | | control_plane | [ControlPlane](#talos.resource.definitions.cluster.ControlPlane) | | | ### ConfigSpec ConfigSpec describes KubeSpan configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | discovery_enabled | [bool](#bool) | | | | registry_kubernetes_enabled | [bool](#bool) | | | | registry_service_enabled | [bool](#bool) | | | | service_endpoint | [string](#string) | | | | service_endpoint_insecure | [bool](#bool) | | | | service_encryption_key | [bytes](#bytes) | | | | service_cluster_id | [string](#string) | | | ### ControlPlane ControlPlane describes ControlPlane data if any. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | api_server_port | [int64](#int64) | | | ### IdentitySpec IdentitySpec describes status of rendered secrets. Note: IdentitySpec is persisted on disk in the STATE partition, so YAML serialization should be kept backwards compatible. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | node_id | [string](#string) | | | ### InfoSpec InfoSpec describes cluster information. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cluster_id | [string](#string) | | | | cluster_name | [string](#string) | | | ### KubeSpanAffiliateSpec KubeSpanAffiliateSpec describes additional information specific for the KubeSpan. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | public_key | [string](#string) | | | | address | [common.NetIP](#common.NetIP) | | | | additional_addresses | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | endpoints | [common.NetIPPort](#common.NetIPPort) | repeated | | | exclude_advertised_networks | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | ### MemberSpec MemberSpec describes Member state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | node_id | [string](#string) | | | | addresses | [common.NetIP](#common.NetIP) | repeated | | | hostname | [string](#string) | | | | machine_type | [talos.resource.definitions.enums.MachineType](#talos.resource.definitions.enums.MachineType) | | | | operating_system | [string](#string) | | | | control_plane | [ControlPlane](#talos.resource.definitions.cluster.ControlPlane) | | |

Top

## resource/definitions/cri/cri.proto ### ImageCacheConfigSpec ImageCacheConfigSpec represents the ImageCacheConfig. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | status | [talos.resource.definitions.enums.CriImageCacheStatus](#talos.resource.definitions.enums.CriImageCacheStatus) | | | | roots | [string](#string) | repeated | | | copy_status | [talos.resource.definitions.enums.CriImageCacheCopyStatus](#talos.resource.definitions.enums.CriImageCacheCopyStatus) | | | ### RegistriesConfigSpec RegistriesConfigSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | registry_mirrors | [RegistriesConfigSpec.RegistryMirrorsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryMirrorsEntry) | repeated | | | registry_auths | [RegistriesConfigSpec.RegistryAuthsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryAuthsEntry) | repeated | | | registry_tl_ss | [RegistriesConfigSpec.RegistryTlSsEntry](#talos.resource.definitions.cri.RegistriesConfigSpec.RegistryTlSsEntry) | repeated | | ### RegistriesConfigSpec.RegistryAuthsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [RegistryAuthConfig](#talos.resource.definitions.cri.RegistryAuthConfig) | | | ### RegistriesConfigSpec.RegistryMirrorsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [RegistryMirrorConfig](#talos.resource.definitions.cri.RegistryMirrorConfig) | | | ### RegistriesConfigSpec.RegistryTlSsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [RegistryTLSConfig](#talos.resource.definitions.cri.RegistryTLSConfig) | | | ### RegistryAuthConfig RegistryAuthConfig specifies authentication configuration for a registry. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | registry_username | [string](#string) | | | | registry_password | [string](#string) | | | | registry_auth | [string](#string) | | | | registry_identity_token | [string](#string) | | | ### RegistryEndpointConfig RegistryEndpointConfig represents a single registry endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint_endpoint | [string](#string) | | | | endpoint_override_path | [bool](#bool) | | | ### RegistryMirrorConfig RegistryMirrorConfig represents mirror configuration for a registry. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mirror_endpoints | [RegistryEndpointConfig](#talos.resource.definitions.cri.RegistryEndpointConfig) | repeated | | | mirror_skip_fallback | [bool](#bool) | | | ### RegistryTLSConfig RegistryTLSConfig specifies TLS config for HTTPS registries. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | tls_client_identity | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | tlsca | [bytes](#bytes) | | | | tls_insecure_skip_verify | [bool](#bool) | | | ### SeccompProfileSpec SeccompProfileSpec represents the SeccompProfile. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | value | [google.protobuf.Struct](#google.protobuf.Struct) | | |

Top

## resource/definitions/etcd/etcd.proto ### ArgValues ArgValues represents values for a command line argument which can be specified multiple times. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | values | [string](#string) | repeated | | ### ConfigSpec ConfigSpec describes (some) configuration settings of etcd. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | advertise_valid_subnets | [string](#string) | repeated | | | advertise_exclude_subnets | [string](#string) | repeated | | | image | [string](#string) | | | | extra_args | [ConfigSpec.ExtraArgsEntry](#talos.resource.definitions.etcd.ConfigSpec.ExtraArgsEntry) | repeated | | | listen_valid_subnets | [string](#string) | repeated | | | listen_exclude_subnets | [string](#string) | repeated | | ### ConfigSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.etcd.ArgValues) | | | ### MemberSpec MemberSpec holds information about an etcd member. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | member_id | [string](#string) | | | ### PKIStatusSpec PKIStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | version | [string](#string) | | | ### SpecSpec SpecSpec describes (some) Specuration settings of etcd. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | advertised_addresses | [common.NetIP](#common.NetIP) | repeated | | | image | [string](#string) | | | | extra_args | [SpecSpec.ExtraArgsEntry](#talos.resource.definitions.etcd.SpecSpec.ExtraArgsEntry) | repeated | | | listen_peer_addresses | [common.NetIP](#common.NetIP) | repeated | | | listen_client_addresses | [common.NetIP](#common.NetIP) | repeated | | ### SpecSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.etcd.ArgValues) | | |

Top

## resource/definitions/extensions/extensions.proto ### Compatibility Compatibility describes extension compatibility. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | talos | [Constraint](#talos.resource.definitions.extensions.Constraint) | | | ### Constraint Constraint describes compatibility constraint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | version | [string](#string) | | | ### Layer Layer defines overlay mount layer. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | metadata | [Metadata](#talos.resource.definitions.extensions.Metadata) | | | ### Metadata Metadata describes base extension metadata. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | version | [string](#string) | | | | author | [string](#string) | | | | description | [string](#string) | | | | compatibility | [Compatibility](#talos.resource.definitions.extensions.Compatibility) | | | | extra_info | [string](#string) | | |

Top

## resource/definitions/files/files.proto ### EtcFileSpecSpec EtcFileSpecSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | contents | [bytes](#bytes) | | | | mode | [uint32](#uint32) | | | | selinux_label | [string](#string) | | | ### EtcFileStatusSpec EtcFileStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | spec_version | [string](#string) | | |

Top

## resource/definitions/hardware/hardware.proto ### MemoryModuleSpec MemoryModuleSpec represents a single Memory. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | size | [uint32](#uint32) | | | | device_locator | [string](#string) | | | | bank_locator | [string](#string) | | | | speed | [uint32](#uint32) | | | | manufacturer | [string](#string) | | | | serial_number | [string](#string) | | | | asset_tag | [string](#string) | | | | product_name | [string](#string) | | | ### PCIDeviceSpec PCIDeviceSpec represents a single processor. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | class | [string](#string) | | | | subclass | [string](#string) | | | | vendor | [string](#string) | | | | product | [string](#string) | | | | class_id | [string](#string) | | | | subclass_id | [string](#string) | | | | vendor_id | [string](#string) | | | | product_id | [string](#string) | | | | driver | [string](#string) | | | ### PCIDriverRebindConfigSpec PCIDriverRebindConfigSpec describes PCI rebind configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pciid | [string](#string) | | | | target_driver | [string](#string) | | | ### PCIDriverRebindStatusSpec PCIDriverRebindStatusSpec describes status of rebinded drivers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pciid | [string](#string) | | | | target_driver | [string](#string) | | | ### ProcessorSpec ProcessorSpec represents a single processor. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | socket | [string](#string) | | | | manufacturer | [string](#string) | | | | product_name | [string](#string) | | | | max_speed | [uint32](#uint32) | | | | boot_speed | [uint32](#uint32) | | | | status | [uint32](#uint32) | | | | serial_number | [string](#string) | | | | asset_tag | [string](#string) | | | | part_number | [string](#string) | | | | core_count | [uint32](#uint32) | | | | core_enabled | [uint32](#uint32) | | | | thread_count | [uint32](#uint32) | | | ### SystemInformationSpec SystemInformationSpec represents the system information obtained from smbios. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | manufacturer | [string](#string) | | | | product_name | [string](#string) | | | | version | [string](#string) | | | | serial_number | [string](#string) | | | | uuid | [string](#string) | | | | wake_up_type | [string](#string) | | | | sku_number | [string](#string) | | |

Top

## resource/definitions/proto/proto.proto ### LinuxIDMapping LinuxIDMapping specifies UID/GID mappings. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | container_id | [uint32](#uint32) | | | | host_id | [uint32](#uint32) | | | | size | [uint32](#uint32) | | | ### Mount Mount specifies a mount for a container. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | destination | [string](#string) | | | | type | [string](#string) | | | | source | [string](#string) | | | | options | [string](#string) | repeated | | | uid_mappings | [LinuxIDMapping](#talos.resource.definitions.proto.LinuxIDMapping) | repeated | | | gid_mappings | [LinuxIDMapping](#talos.resource.definitions.proto.LinuxIDMapping) | repeated | |

Top

## resource/definitions/k8s/k8s.proto ### APIServerConfigSpec APIServerConfigSpec is configuration for kube-apiserver. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | cloud_provider | [string](#string) | | | | control_plane_endpoint | [string](#string) | | | | etcd_servers | [string](#string) | repeated | | | local_port | [int64](#int64) | | | | service_cid_rs | [string](#string) | repeated | | | extra_args | [APIServerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.ExtraArgsEntry) | repeated | | | extra_volumes | [ExtraVolume](#talos.resource.definitions.k8s.ExtraVolume) | repeated | | | environment_variables | [APIServerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.APIServerConfigSpec.EnvironmentVariablesEntry) | repeated | | | advertised_address | [string](#string) | | | | resources | [Resources](#talos.resource.definitions.k8s.Resources) | | | ### APIServerConfigSpec.EnvironmentVariablesEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### APIServerConfigSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.k8s.ArgValues) | | | ### AdmissionControlConfigSpec AdmissionControlConfigSpec is configuration for kube-apiserver. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | config | [AdmissionPluginSpec](#talos.resource.definitions.k8s.AdmissionPluginSpec) | repeated | | ### AdmissionPluginSpec AdmissionPluginSpec is a single admission plugin configuration Admission Control plugins. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | configuration | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### ArgValues ArgValues represents values for a command line argument which can be specified multiple times. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | values | [string](#string) | repeated | | ### AuditPolicyConfigSpec AuditPolicyConfigSpec is audit policy configuration for kube-apiserver. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | config | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### AuthorizationAuthorizersSpec AuthorizationAuthorizersSpec is a configuration of authorization authorizers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [string](#string) | | | | name | [string](#string) | | | | webhook | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### AuthorizationConfigSpec AuthorizationConfigSpec is authorization configuration for kube-apiserver. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | config | [AuthorizationAuthorizersSpec](#talos.resource.definitions.k8s.AuthorizationAuthorizersSpec) | repeated | | ### BootstrapManifestsConfigSpec BootstrapManifestsConfigSpec is configuration for bootstrap manifests. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | server | [string](#string) | | | | cluster_domain | [string](#string) | | | | pod_cid_rs | [string](#string) | repeated | | | proxy_enabled | [bool](#bool) | | | | proxy_image | [string](#string) | | | | proxy_args | [string](#string) | repeated | | | core_dns_enabled | [bool](#bool) | | | | core_dns_image | [string](#string) | | | | dns_service_ip | [string](#string) | | | | dns_service_i_pv6 | [string](#string) | | | | flannel_enabled | [bool](#bool) | | | | flannel_image | [string](#string) | | | | pod_security_policy_enabled | [bool](#bool) | | | | talos_api_service_enabled | [bool](#bool) | | | | flannel_extra_args | [string](#string) | repeated | | | flannel_kube_service_host | [string](#string) | | | | flannel_kube_service_port | [string](#string) | | | | flannel_kube_network_policies_enabled | [bool](#bool) | | | | flannel_kube_network_policies_image | [string](#string) | | | | cni_name | [string](#string) | | | ### ConfigStatusSpec ConfigStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | version | [string](#string) | | | ### ControllerManagerConfigSpec ControllerManagerConfigSpec is configuration for kube-controller-manager. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | | image | [string](#string) | | | | cloud_provider | [string](#string) | | | | pod_cid_rs | [string](#string) | repeated | | | service_cid_rs | [string](#string) | repeated | | | extra_args | [ControllerManagerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.ControllerManagerConfigSpec.ExtraArgsEntry) | repeated | | | extra_volumes | [ExtraVolume](#talos.resource.definitions.k8s.ExtraVolume) | repeated | | | environment_variables | [ControllerManagerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.ControllerManagerConfigSpec.EnvironmentVariablesEntry) | repeated | | | resources | [Resources](#talos.resource.definitions.k8s.Resources) | | | ### ControllerManagerConfigSpec.EnvironmentVariablesEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### ControllerManagerConfigSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.k8s.ArgValues) | | | ### EndpointSpec EndpointSpec describes a list of endpoints to connect to. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | addresses | [common.NetIP](#common.NetIP) | repeated | | | hosts | [string](#string) | repeated | | ### ExtraManifest ExtraManifest defines a single extra manifest to download. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | url | [string](#string) | | | | priority | [string](#string) | | | | extra_headers | [ExtraManifest.ExtraHeadersEntry](#talos.resource.definitions.k8s.ExtraManifest.ExtraHeadersEntry) | repeated | | | inline_manifest | [string](#string) | | | ### ExtraManifest.ExtraHeadersEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### ExtraManifestsConfigSpec ExtraManifestsConfigSpec is configuration for extra bootstrap manifests. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | extra_manifests | [ExtraManifest](#talos.resource.definitions.k8s.ExtraManifest) | repeated | | ### ExtraVolume ExtraVolume is a configuration of extra volume. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | host_path | [string](#string) | | | | mount_path | [string](#string) | | | | read_only | [bool](#bool) | | | ### KubePrismConfigSpec KubePrismConfigSpec describes KubePrismConfig data. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | host | [string](#string) | | | | port | [int64](#int64) | | | | endpoints | [KubePrismEndpoint](#talos.resource.definitions.k8s.KubePrismEndpoint) | repeated | | ### KubePrismEndpoint KubePrismEndpoint holds data for control plane endpoint. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | host | [string](#string) | | | | port | [uint32](#uint32) | | | ### KubePrismEndpointsSpec KubePrismEndpointsSpec describes KubePrismEndpoints configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoints | [KubePrismEndpoint](#talos.resource.definitions.k8s.KubePrismEndpoint) | repeated | | ### KubePrismStatusesSpec KubePrismStatusesSpec describes KubePrismStatuses data. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | host | [string](#string) | | | | healthy | [bool](#bool) | | | ### KubeletConfigSpec KubeletConfigSpec holds the source of kubelet configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | cluster_dns | [string](#string) | repeated | | | cluster_domain | [string](#string) | | | | extra_args | [KubeletConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.KubeletConfigSpec.ExtraArgsEntry) | repeated | | | extra_mounts | [talos.resource.definitions.proto.Mount](#talos.resource.definitions.proto.Mount) | repeated | | | extra_config | [google.protobuf.Struct](#google.protobuf.Struct) | | | | cloud_provider_external | [bool](#bool) | | | | default_runtime_seccomp_enabled | [bool](#bool) | | | | skip_node_registration | [bool](#bool) | | | | static_pod_list_url | [string](#string) | | | | disable_manifests_directory | [bool](#bool) | | | | enable_fs_quota_monitoring | [bool](#bool) | | | | credential_provider_config | [google.protobuf.Struct](#google.protobuf.Struct) | | | | allow_scheduling_on_control_plane | [bool](#bool) | | | ### KubeletConfigSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.k8s.ArgValues) | | | ### KubeletSpecSpec KubeletSpecSpec holds the source of kubelet configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image | [string](#string) | | | | args | [string](#string) | repeated | | | extra_mounts | [talos.resource.definitions.proto.Mount](#talos.resource.definitions.proto.Mount) | repeated | | | expected_nodename | [string](#string) | | | | config | [google.protobuf.Struct](#google.protobuf.Struct) | | | | credential_provider_config | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### ManifestSpec ManifestSpec holds the Kubernetes resources spec. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | items | [SingleManifest](#talos.resource.definitions.k8s.SingleManifest) | repeated | | ### ManifestStatusSpec ManifestStatusSpec describes manifest application status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | manifests_applied | [string](#string) | repeated | | ### NodeAnnotationSpecSpec NodeAnnotationSpecSpec represents an annoation that's attached to a Talos node. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### NodeIPConfigSpec NodeIPConfigSpec holds the Node IP specification. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | valid_subnets | [string](#string) | repeated | | | exclude_subnets | [string](#string) | repeated | | ### NodeIPSpec NodeIPSpec holds the Node IP specification. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | addresses | [common.NetIP](#common.NetIP) | repeated | | ### NodeLabelSpecSpec NodeLabelSpecSpec represents a label that's attached to a Talos node. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### NodeStatusSpec NodeStatusSpec describes Kubernetes NodeStatus. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | nodename | [string](#string) | | | | node_ready | [bool](#bool) | | | | unschedulable | [bool](#bool) | | | | labels | [NodeStatusSpec.LabelsEntry](#talos.resource.definitions.k8s.NodeStatusSpec.LabelsEntry) | repeated | | | annotations | [NodeStatusSpec.AnnotationsEntry](#talos.resource.definitions.k8s.NodeStatusSpec.AnnotationsEntry) | repeated | | | pod_cid_rs | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | ### NodeStatusSpec.AnnotationsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### NodeStatusSpec.LabelsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### NodeTaintSpecSpec NodeTaintSpecSpec represents a label that's attached to a Talos node. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | effect | [string](#string) | | | | value | [string](#string) | | | ### NodenameSpec NodenameSpec describes Kubernetes nodename. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | nodename | [string](#string) | | | | hostname_version | [string](#string) | | | | skip_node_registration | [bool](#bool) | | | ### Resources Resources is a configuration of cpu and memory resources. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | requests | [Resources.RequestsEntry](#talos.resource.definitions.k8s.Resources.RequestsEntry) | repeated | | | limits | [Resources.LimitsEntry](#talos.resource.definitions.k8s.Resources.LimitsEntry) | repeated | | ### Resources.LimitsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### Resources.RequestsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### SchedulerConfigSpec SchedulerConfigSpec is configuration for kube-scheduler. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | | image | [string](#string) | | | | extra_args | [SchedulerConfigSpec.ExtraArgsEntry](#talos.resource.definitions.k8s.SchedulerConfigSpec.ExtraArgsEntry) | repeated | | | extra_volumes | [ExtraVolume](#talos.resource.definitions.k8s.ExtraVolume) | repeated | | | environment_variables | [SchedulerConfigSpec.EnvironmentVariablesEntry](#talos.resource.definitions.k8s.SchedulerConfigSpec.EnvironmentVariablesEntry) | repeated | | | resources | [Resources](#talos.resource.definitions.k8s.Resources) | | | | config | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### SchedulerConfigSpec.EnvironmentVariablesEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### SchedulerConfigSpec.ExtraArgsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [ArgValues](#talos.resource.definitions.k8s.ArgValues) | | | ### SecretsStatusSpec SecretsStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | version | [string](#string) | | | ### SingleManifest SingleManifest is a single manifest. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | object | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### StaticPodServerStatusSpec StaticPodServerStatusSpec describes static pod spec, it contains marshaled *v1.Pod spec. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | url | [string](#string) | | | ### StaticPodSpec StaticPodSpec describes static pod spec, it contains marshaled *v1.Pod spec. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pod | [google.protobuf.Struct](#google.protobuf.Struct) | | | ### StaticPodStatusSpec StaticPodStatusSpec describes kubelet static pod status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | pod_status | [google.protobuf.Struct](#google.protobuf.Struct) | | |

Top

## resource/definitions/kubeaccess/kubeaccess.proto ### ConfigSpec ConfigSpec describes KubeSpan configuration.. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | | allowed_api_roles | [string](#string) | repeated | | | allowed_kubernetes_namespaces | [string](#string) | repeated | |

Top

## resource/definitions/kubespan/kubespan.proto ### ConfigSpec ConfigSpec describes KubeSpan configuration.. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | | cluster_id | [string](#string) | | | | shared_secret | [string](#string) | | | | force_routing | [bool](#bool) | | | | advertise_kubernetes_networks | [bool](#bool) | | | | mtu | [uint32](#uint32) | | | | endpoint_filters | [string](#string) | repeated | | | harvest_extra_endpoints | [bool](#bool) | | | | extra_endpoints | [common.NetIPPort](#common.NetIPPort) | repeated | | | exclude_advertised_networks | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | ### EndpointSpec EndpointSpec describes Endpoint state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | affiliate_id | [string](#string) | | | | endpoint | [common.NetIPPort](#common.NetIPPort) | | | ### IdentitySpec IdentitySpec describes KubeSpan keys and address. Note: IdentitySpec is persisted on disk in the STATE partition, so YAML serialization should be kept backwards compatible. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address | [common.NetIPPrefix](#common.NetIPPrefix) | | | | subnet | [common.NetIPPrefix](#common.NetIPPrefix) | | | | private_key | [string](#string) | | | | public_key | [string](#string) | | | ### PeerSpecSpec PeerSpecSpec describes PeerSpec state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address | [common.NetIP](#common.NetIP) | | | | allowed_ips | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | endpoints | [common.NetIPPort](#common.NetIPPort) | repeated | | | label | [string](#string) | | | ### PeerStatusSpec PeerStatusSpec describes PeerStatus state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint | [common.NetIPPort](#common.NetIPPort) | | | | label | [string](#string) | | | | state | [talos.resource.definitions.enums.KubespanPeerState](#talos.resource.definitions.enums.KubespanPeerState) | | | | receive_bytes | [int64](#int64) | | | | transmit_bytes | [int64](#int64) | | | | last_handshake_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | | last_used_endpoint | [common.NetIPPort](#common.NetIPPort) | | | | last_endpoint_change | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | |

Top

## resource/definitions/runtime/runtime.proto ### APIServiceConfigSpec APIServiceConfigSpec describes configuration for Talos API service (apid). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | listen_address | [string](#string) | | | | node_routing_disabled | [bool](#bool) | | | | readonly_role_mode | [bool](#bool) | | | | skip_verifying_client_cert | [bool](#bool) | | | ### BootedEntrySpec BootedEntrySpec describes the booted entry resource properties. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | booted_entry | [string](#string) | | | ### DevicesStatusSpec DevicesStatusSpec is the spec for devices status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | ### DiagnosticSpec DiagnosticSpec is the spec for devices status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | message | [string](#string) | | | | details | [string](#string) | repeated | | ### EnvironmentSpec EnvironmentSpec describes the specification of Environment resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | variables | [string](#string) | repeated | | ### EventSinkConfigSpec EventSinkConfigSpec describes configuration of Talos event log streaming. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint | [string](#string) | | | ### ExtensionServiceConfigFile ExtensionServiceConfigFile describes extensions service config files. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | content | [string](#string) | | | | mount_path | [string](#string) | | | ### ExtensionServiceConfigSpec ExtensionServiceConfigSpec describes status of rendered extensions service config files. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | files | [ExtensionServiceConfigFile](#talos.resource.definitions.runtime.ExtensionServiceConfigFile) | repeated | | | environment | [string](#string) | repeated | | ### ExtensionServiceConfigStatusSpec ExtensionServiceConfigStatusSpec describes status of rendered extensions service config files. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | spec_version | [string](#string) | | | ### KernelCmdlineSpec KernelCmdlineSpec presents kernel command line (contents of /proc/cmdline). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cmdline | [string](#string) | | | ### KernelModuleSpecSpec KernelModuleSpecSpec describes Linux kernel module to load. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | parameters | [string](#string) | repeated | | ### KernelParamSpecSpec KernelParamSpecSpec describes status of the defined sysctls. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | value | [string](#string) | | | | ignore_errors | [bool](#bool) | | | ### KernelParamStatusSpec KernelParamStatusSpec describes status of the defined sysctls. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | current | [string](#string) | | | | default | [string](#string) | | | | unsupported | [bool](#bool) | | | ### KmsgLogConfigSpec KmsgLogConfigSpec describes configuration for kmsg log streaming. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | destinations | [common.URL](#common.URL) | repeated | | ### LoadedKernelModuleSpec LoadedKernelModuleSpec describes Linux kernel module to load. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | size | [int64](#int64) | | | | reference_count | [int64](#int64) | | | | dependencies | [string](#string) | repeated | | | state | [string](#string) | | | | address | [string](#string) | | | ### MachineStatusSpec MachineStatusSpec describes status of the defined sysctls. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | stage | [talos.resource.definitions.enums.RuntimeMachineStage](#talos.resource.definitions.enums.RuntimeMachineStage) | | | | status | [MachineStatusStatus](#talos.resource.definitions.runtime.MachineStatusStatus) | | | ### MachineStatusStatus MachineStatusStatus describes machine current status at the stage. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ready | [bool](#bool) | | | | unmet_conditions | [UnmetCondition](#talos.resource.definitions.runtime.UnmetCondition) | repeated | | ### MaintenanceServiceConfigSpec MaintenanceServiceConfigSpec describes configuration for maintenance service API. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | listen_address | [string](#string) | | | | reachable_addresses | [common.NetIP](#common.NetIP) | repeated | | ### MetaKeySpec MetaKeySpec describes status of the defined sysctls. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | value | [string](#string) | | | ### MetaLoadedSpec MetaLoadedSpec is the spec for meta loaded. The Done field is always true when resource exists. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | done | [bool](#bool) | | | ### MountStatusSpec MountStatusSpec describes status of the defined sysctls. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | source | [string](#string) | | | | target | [string](#string) | | | | filesystem_type | [string](#string) | | | | options | [string](#string) | repeated | | | encrypted | [bool](#bool) | | | | encryption_providers | [string](#string) | repeated | | ### OOMActionSpec OOMActionSpec describes the OOM action record resource properties. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | trigger_context | [string](#string) | | | | score | [double](#double) | | | | processes | [string](#string) | repeated | | ### PlatformMetadataSpec PlatformMetadataSpec describes platform metadata properties. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | platform | [string](#string) | | | | hostname | [string](#string) | | | | region | [string](#string) | | | | zone | [string](#string) | | | | instance_type | [string](#string) | | | | instance_id | [string](#string) | | | | provider_id | [string](#string) | | | | spot | [bool](#bool) | | | | internal_dns | [string](#string) | | | | external_dns | [string](#string) | | | | tags | [PlatformMetadataSpec.TagsEntry](#talos.resource.definitions.runtime.PlatformMetadataSpec.TagsEntry) | repeated | | ### PlatformMetadataSpec.TagsEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [string](#string) | | | ### SBOMItemSpec SBOMItemSpec describes the SBOM item resource properties. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | version | [string](#string) | | | | license | [string](#string) | | | | cp_es | [string](#string) | repeated | | | pur_ls | [string](#string) | repeated | | | extension | [bool](#bool) | | | ### SecurityStateSpec SecurityStateSpec describes the security state resource properties. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | secure_boot | [bool](#bool) | | | | uki_signing_key_fingerprint | [string](#string) | | | | pcr_signing_key_fingerprint | [string](#string) | | | | se_linux_state | [talos.resource.definitions.enums.RuntimeSELinuxState](#talos.resource.definitions.enums.RuntimeSELinuxState) | | | | booted_with_uki | [bool](#bool) | | | | fips_state | [talos.resource.definitions.enums.RuntimeFIPSState](#talos.resource.definitions.enums.RuntimeFIPSState) | | | | module_signature_enforced | [bool](#bool) | | | ### UniqueMachineTokenSpec UniqueMachineTokenSpec is the spec for the machine unique token. Token can be empty if machine wasn't assigned any. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | token | [string](#string) | | | ### UnmetCondition UnmetCondition is a failure which prevents machine from being ready at the stage. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | reason | [string](#string) | | | ### WatchdogTimerConfigSpec WatchdogTimerConfigSpec describes configuration of watchdog timer. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device | [string](#string) | | | | timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | ### WatchdogTimerStatusSpec WatchdogTimerStatusSpec describes configuration of watchdog timer. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device | [string](#string) | | | | timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | | feed_interval | [google.protobuf.Duration](#google.protobuf.Duration) | | |

Top

## resource/definitions/network/network.proto ### AddressSpecSpec AddressSpecSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address | [common.NetIPPrefix](#common.NetIPPrefix) | | | | link_name | [string](#string) | | | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | scope | [talos.resource.definitions.enums.NethelpersScope](#talos.resource.definitions.enums.NethelpersScope) | | | | flags | [uint32](#uint32) | | | | announce_with_arp | [bool](#bool) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | | priority | [uint32](#uint32) | | | ### AddressStatusSpec AddressStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address | [common.NetIPPrefix](#common.NetIPPrefix) | | | | local | [common.NetIP](#common.NetIP) | | | | broadcast | [common.NetIP](#common.NetIP) | | | | anycast | [common.NetIP](#common.NetIP) | | | | multicast | [common.NetIP](#common.NetIP) | | | | link_index | [uint32](#uint32) | | | | link_name | [string](#string) | | | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | scope | [talos.resource.definitions.enums.NethelpersScope](#talos.resource.definitions.enums.NethelpersScope) | | | | flags | [uint32](#uint32) | | | | priority | [uint32](#uint32) | | | ### BondMasterSpec BondMasterSpec describes bond settings if Kind == "bond". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mode | [talos.resource.definitions.enums.NethelpersBondMode](#talos.resource.definitions.enums.NethelpersBondMode) | | | | hash_policy | [talos.resource.definitions.enums.NethelpersBondXmitHashPolicy](#talos.resource.definitions.enums.NethelpersBondXmitHashPolicy) | | | | lacp_rate | [talos.resource.definitions.enums.NethelpersLACPRate](#talos.resource.definitions.enums.NethelpersLACPRate) | | | | arp_validate | [talos.resource.definitions.enums.NethelpersARPValidate](#talos.resource.definitions.enums.NethelpersARPValidate) | | | | arp_all_targets | [talos.resource.definitions.enums.NethelpersARPAllTargets](#talos.resource.definitions.enums.NethelpersARPAllTargets) | | | | primary_index | [uint32](#uint32) | | | | primary_reselect | [talos.resource.definitions.enums.NethelpersPrimaryReselect](#talos.resource.definitions.enums.NethelpersPrimaryReselect) | | | | fail_over_mac | [talos.resource.definitions.enums.NethelpersFailOverMAC](#talos.resource.definitions.enums.NethelpersFailOverMAC) | | | | ad_select | [talos.resource.definitions.enums.NethelpersADSelect](#talos.resource.definitions.enums.NethelpersADSelect) | | | | mii_mon | [uint32](#uint32) | | | | up_delay | [uint32](#uint32) | | | | down_delay | [uint32](#uint32) | | | | arp_interval | [uint32](#uint32) | | | | resend_igmp | [uint32](#uint32) | | | | min_links | [uint32](#uint32) | | | | lp_interval | [uint32](#uint32) | | | | packets_per_slave | [uint32](#uint32) | | | | num_peer_notif | [uint32](#uint32) | | | | tlb_dynamic_lb | [uint32](#uint32) | | | | all_slaves_active | [uint32](#uint32) | | | | use_carrier | [bool](#bool) | | | | ad_actor_sys_prio | [uint32](#uint32) | | | | ad_user_port_key | [uint32](#uint32) | | | | peer_notify_delay | [uint32](#uint32) | | | | arpip_targets | [common.NetIP](#common.NetIP) | repeated | | | nsip6_targets | [common.NetIP](#common.NetIP) | repeated | | | adlacp_active | [talos.resource.definitions.enums.NethelpersADLACPActive](#talos.resource.definitions.enums.NethelpersADLACPActive) | | | | missed_max | [uint32](#uint32) | | | ### BondSlave BondSlave contains a bond's master name and slave index. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | master_name | [string](#string) | | | | slave_index | [int64](#int64) | | | ### BridgeMasterSpec BridgeMasterSpec describes bridge settings if Kind == "bridge". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | stp | [STPSpec](#talos.resource.definitions.network.STPSpec) | | | | vlan | [BridgeVLANSpec](#talos.resource.definitions.network.BridgeVLANSpec) | | | ### BridgeSlave BridgeSlave contains the name of the master bridge of a bridged interface | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | master_name | [string](#string) | | | ### BridgeVLANSpec BridgeVLANSpec describes VLAN settings of a bridge. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | filtering_enabled | [bool](#bool) | | | ### ClientIdentifierSpec ClientIdentifierSpec is a shared DHCP4/DHCP6 client identifier spec. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | client_identifier | [talos.resource.definitions.enums.NethelpersClientIdentifier](#talos.resource.definitions.enums.NethelpersClientIdentifier) | | | | duid_raw_hex | [string](#string) | | | ### DHCP4OperatorSpec DHCP4OperatorSpec describes DHCP4 operator options. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | route_metric | [uint32](#uint32) | | | | skip_hostname_request | [bool](#bool) | | | | client_identifier | [ClientIdentifierSpec](#talos.resource.definitions.network.ClientIdentifierSpec) | | | ### DHCP6OperatorSpec DHCP6OperatorSpec describes DHCP6 operator options. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | route_metric | [uint32](#uint32) | | | | skip_hostname_request | [bool](#bool) | | | | client_identifier | [ClientIdentifierSpec](#talos.resource.definitions.network.ClientIdentifierSpec) | | | ### DNSResolveCacheSpec DNSResolveCacheSpec describes DNS servers status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | status | [string](#string) | | | ### EthernetChannelsSpec EthernetChannelsSpec describes config of Ethernet channels. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rx | [uint32](#uint32) | | | | tx | [uint32](#uint32) | | | | other | [uint32](#uint32) | | | | combined | [uint32](#uint32) | | | ### EthernetChannelsStatus EthernetChannelsStatus describes status of Ethernet channels. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rx_max | [uint32](#uint32) | | | | tx_max | [uint32](#uint32) | | | | other_max | [uint32](#uint32) | | | | combined_max | [uint32](#uint32) | | | | rx | [uint32](#uint32) | | | | tx | [uint32](#uint32) | | | | other | [uint32](#uint32) | | | | combined | [uint32](#uint32) | | | ### EthernetFeatureStatus EthernetFeatureStatus describes status of Ethernet features. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | status | [string](#string) | | | ### EthernetRingsSpec EthernetRingsSpec describes config of Ethernet rings. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rx | [uint32](#uint32) | | | | rx_mini | [uint32](#uint32) | | | | rx_jumbo | [uint32](#uint32) | | | | tx | [uint32](#uint32) | | | | rx_buf_len | [uint32](#uint32) | | | | cqe_size | [uint32](#uint32) | | | | tx_push | [bool](#bool) | | | | rx_push | [bool](#bool) | | | | tx_push_buf_len | [uint32](#uint32) | | | | tcp_data_split | [bool](#bool) | | | ### EthernetRingsStatus EthernetRingsStatus describes status of Ethernet rings. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rx_max | [uint32](#uint32) | | | | rx_mini_max | [uint32](#uint32) | | | | rx_jumbo_max | [uint32](#uint32) | | | | tx_max | [uint32](#uint32) | | | | tx_push_buf_len_max | [uint32](#uint32) | | | | rx | [uint32](#uint32) | | | | rx_mini | [uint32](#uint32) | | | | rx_jumbo | [uint32](#uint32) | | | | tx | [uint32](#uint32) | | | | rx_buf_len | [uint32](#uint32) | | | | cqe_size | [uint32](#uint32) | | | | tx_push | [bool](#bool) | | | | rx_push | [bool](#bool) | | | | tx_push_buf_len | [uint32](#uint32) | | | | tcp_data_split | [bool](#bool) | | | ### EthernetSpecSpec EthernetSpecSpec describes config of Ethernet link. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | rings | [EthernetRingsSpec](#talos.resource.definitions.network.EthernetRingsSpec) | | | | features | [EthernetSpecSpec.FeaturesEntry](#talos.resource.definitions.network.EthernetSpecSpec.FeaturesEntry) | repeated | | | channels | [EthernetChannelsSpec](#talos.resource.definitions.network.EthernetChannelsSpec) | | | | wake_on_lan | [talos.resource.definitions.enums.NethelpersWOLMode](#talos.resource.definitions.enums.NethelpersWOLMode) | repeated | | ### EthernetSpecSpec.FeaturesEntry | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | key | [string](#string) | | | | value | [bool](#bool) | | | ### EthernetStatusSpec EthernetStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | link_state | [bool](#bool) | | | | speed_megabits | [int64](#int64) | | | | port | [talos.resource.definitions.enums.NethelpersPort](#talos.resource.definitions.enums.NethelpersPort) | | | | duplex | [talos.resource.definitions.enums.NethelpersDuplex](#talos.resource.definitions.enums.NethelpersDuplex) | | | | our_modes | [string](#string) | repeated | | | peer_modes | [string](#string) | repeated | | | rings | [EthernetRingsStatus](#talos.resource.definitions.network.EthernetRingsStatus) | | | | features | [EthernetFeatureStatus](#talos.resource.definitions.network.EthernetFeatureStatus) | repeated | | | channels | [EthernetChannelsStatus](#talos.resource.definitions.network.EthernetChannelsStatus) | | | | wake_on_lan | [talos.resource.definitions.enums.NethelpersWOLMode](#talos.resource.definitions.enums.NethelpersWOLMode) | repeated | | ### HardwareAddrSpec HardwareAddrSpec describes spec for the link. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | hardware_addr | [bytes](#bytes) | | | ### HostDNSConfigSpec HostDNSConfigSpec describes host DNS config. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | | listen_addresses | [common.NetIPPort](#common.NetIPPort) | repeated | | | service_host_dns_address | [common.NetIP](#common.NetIP) | | | | resolve_member_names | [bool](#bool) | | | ### HostnameSpecSpec HostnameSpecSpec describes node hostname. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostname | [string](#string) | | | | domainname | [string](#string) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | ### HostnameStatusSpec HostnameStatusSpec describes node hostname. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | hostname | [string](#string) | | | | domainname | [string](#string) | | | ### LinkAliasSpecSpec LinkAliasSpecSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | alias | [string](#string) | | | ### LinkRefreshSpec LinkRefreshSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | generation | [int64](#int64) | | | ### LinkSpecSpec LinkSpecSpec describes spec for the link. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | logical | [bool](#bool) | | | | up | [bool](#bool) | | | | mtu | [uint32](#uint32) | | | | kind | [string](#string) | | | | type | [talos.resource.definitions.enums.NethelpersLinkType](#talos.resource.definitions.enums.NethelpersLinkType) | | | | parent_name | [string](#string) | | | | bond_slave | [BondSlave](#talos.resource.definitions.network.BondSlave) | | | | bridge_slave | [BridgeSlave](#talos.resource.definitions.network.BridgeSlave) | | | | vlan | [VLANSpec](#talos.resource.definitions.network.VLANSpec) | | | | bond_master | [BondMasterSpec](#talos.resource.definitions.network.BondMasterSpec) | | | | bridge_master | [BridgeMasterSpec](#talos.resource.definitions.network.BridgeMasterSpec) | | | | wireguard | [WireguardSpec](#talos.resource.definitions.network.WireguardSpec) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | | hardware_address | [bytes](#bytes) | | | | multicast | [bool](#bool) | | | | vrf_master | [VRFMasterSpec](#talos.resource.definitions.network.VRFMasterSpec) | | | | vrf_slave | [VRFSlave](#talos.resource.definitions.network.VRFSlave) | | | ### LinkStatusSpec LinkStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | index | [uint32](#uint32) | | | | type | [talos.resource.definitions.enums.NethelpersLinkType](#talos.resource.definitions.enums.NethelpersLinkType) | | | | link_index | [uint32](#uint32) | | | | flags | [uint32](#uint32) | | | | hardware_addr | [bytes](#bytes) | | | | broadcast_addr | [bytes](#bytes) | | | | mtu | [uint32](#uint32) | | | | queue_disc | [string](#string) | | | | master_index | [uint32](#uint32) | | | | operational_state | [talos.resource.definitions.enums.NethelpersOperationalState](#talos.resource.definitions.enums.NethelpersOperationalState) | | | | kind | [string](#string) | | | | slave_kind | [string](#string) | | | | bus_path | [string](#string) | | | | pciid | [string](#string) | | | | driver | [string](#string) | | | | driver_version | [string](#string) | | | | firmware_version | [string](#string) | | | | product_id | [string](#string) | | | | vendor_id | [string](#string) | | | | product | [string](#string) | | | | vendor | [string](#string) | | | | link_state | [bool](#bool) | | | | speed_megabits | [int64](#int64) | | | | port | [talos.resource.definitions.enums.NethelpersPort](#talos.resource.definitions.enums.NethelpersPort) | | | | duplex | [talos.resource.definitions.enums.NethelpersDuplex](#talos.resource.definitions.enums.NethelpersDuplex) | | | | vlan | [VLANSpec](#talos.resource.definitions.network.VLANSpec) | | | | bridge_master | [BridgeMasterSpec](#talos.resource.definitions.network.BridgeMasterSpec) | | | | bond_master | [BondMasterSpec](#talos.resource.definitions.network.BondMasterSpec) | | | | wireguard | [WireguardSpec](#talos.resource.definitions.network.WireguardSpec) | | | | permanent_addr | [bytes](#bytes) | | | | alias | [string](#string) | | | | alt_names | [string](#string) | repeated | | | vrf_master | [VRFMasterSpec](#talos.resource.definitions.network.VRFMasterSpec) | | | ### NfTablesAddressMatch NfTablesAddressMatch describes the match on the IP address. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | include_subnets | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | exclude_subnets | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | invert | [bool](#bool) | | | ### NfTablesChainSpec NfTablesChainSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | type | [string](#string) | | | | hook | [talos.resource.definitions.enums.NethelpersNfTablesChainHook](#talos.resource.definitions.enums.NethelpersNfTablesChainHook) | | | | priority | [talos.resource.definitions.enums.NethelpersNfTablesChainPriority](#talos.resource.definitions.enums.NethelpersNfTablesChainPriority) | | | | rules | [NfTablesRule](#talos.resource.definitions.network.NfTablesRule) | repeated | | | policy | [talos.resource.definitions.enums.NethelpersNfTablesVerdict](#talos.resource.definitions.enums.NethelpersNfTablesVerdict) | | | ### NfTablesClampMSS NfTablesClampMSS describes the TCP MSS clamping operation. MSS is limited by the `MaxMTU` so that: - IPv4: MSS = MaxMTU - 40 - IPv6: MSS = MaxMTU - 60. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mtu | [uint32](#uint32) | | | ### NfTablesConntrackStateMatch NfTablesConntrackStateMatch describes the match on the connection tracking state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | states | [talos.resource.definitions.enums.NethelpersConntrackState](#talos.resource.definitions.enums.NethelpersConntrackState) | repeated | | ### NfTablesICMPTypeMatch NfTablesICMPTypeMatch describes the match on the ICMP type. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | types | [talos.resource.definitions.enums.NethelpersICMPType](#talos.resource.definitions.enums.NethelpersICMPType) | repeated | | ### NfTablesIfNameMatch NfTablesIfNameMatch describes the match on the interface name. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | operator | [talos.resource.definitions.enums.NethelpersMatchOperator](#talos.resource.definitions.enums.NethelpersMatchOperator) | | | | interface_names | [string](#string) | repeated | | ### NfTablesLayer4Match NfTablesLayer4Match describes the match on the transport layer protocol. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | protocol | [talos.resource.definitions.enums.NethelpersProtocol](#talos.resource.definitions.enums.NethelpersProtocol) | | | | match_source_port | [NfTablesPortMatch](#talos.resource.definitions.network.NfTablesPortMatch) | | | | match_destination_port | [NfTablesPortMatch](#talos.resource.definitions.network.NfTablesPortMatch) | | | | match_icmp_type | [NfTablesICMPTypeMatch](#talos.resource.definitions.network.NfTablesICMPTypeMatch) | | | ### NfTablesLimitMatch NfTablesLimitMatch describes the match on the packet rate. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | packet_rate_per_second | [uint64](#uint64) | | | ### NfTablesMark NfTablesMark encodes packet mark match/update operation. When used as a match computes the following condition: (mark & mask) ^ xor == value When used as an update computes the following operation: mark = (mark & mask) ^ xor. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mask | [uint32](#uint32) | | | | xor | [uint32](#uint32) | | | | value | [uint32](#uint32) | | | ### NfTablesPortMatch NfTablesPortMatch describes the match on the transport layer port. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ranges | [PortRange](#talos.resource.definitions.network.PortRange) | repeated | | ### NfTablesRule NfTablesRule describes a single rule in the nftables chain. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | match_o_if_name | [NfTablesIfNameMatch](#talos.resource.definitions.network.NfTablesIfNameMatch) | | | | verdict | [talos.resource.definitions.enums.NethelpersNfTablesVerdict](#talos.resource.definitions.enums.NethelpersNfTablesVerdict) | | | | match_mark | [NfTablesMark](#talos.resource.definitions.network.NfTablesMark) | | | | set_mark | [NfTablesMark](#talos.resource.definitions.network.NfTablesMark) | | | | match_source_address | [NfTablesAddressMatch](#talos.resource.definitions.network.NfTablesAddressMatch) | | | | match_destination_address | [NfTablesAddressMatch](#talos.resource.definitions.network.NfTablesAddressMatch) | | | | match_layer4 | [NfTablesLayer4Match](#talos.resource.definitions.network.NfTablesLayer4Match) | | | | match_i_if_name | [NfTablesIfNameMatch](#talos.resource.definitions.network.NfTablesIfNameMatch) | | | | clamp_mss | [NfTablesClampMSS](#talos.resource.definitions.network.NfTablesClampMSS) | | | | match_limit | [NfTablesLimitMatch](#talos.resource.definitions.network.NfTablesLimitMatch) | | | | match_conntrack_state | [NfTablesConntrackStateMatch](#talos.resource.definitions.network.NfTablesConntrackStateMatch) | | | | anon_counter | [bool](#bool) | | | ### NodeAddressFilterSpec NodeAddressFilterSpec describes a filter for NodeAddresses. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | include_subnets | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | exclude_subnets | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | ### NodeAddressSortAlgorithmSpec NodeAddressSortAlgorithmSpec describes a filter for NodeAddresses. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | algorithm | [talos.resource.definitions.enums.NethelpersAddressSortAlgorithm](#talos.resource.definitions.enums.NethelpersAddressSortAlgorithm) | | | ### NodeAddressSpec NodeAddressSpec describes a set of node addresses. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | addresses | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | | sort_algorithm | [talos.resource.definitions.enums.NethelpersAddressSortAlgorithm](#talos.resource.definitions.enums.NethelpersAddressSortAlgorithm) | | | ### OperatorSpecSpec OperatorSpecSpec describes operator specification. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | operator | [talos.resource.definitions.enums.NetworkOperator](#talos.resource.definitions.enums.NetworkOperator) | | | | link_name | [string](#string) | | | | require_up | [bool](#bool) | | | | dhcp4 | [DHCP4OperatorSpec](#talos.resource.definitions.network.DHCP4OperatorSpec) | | | | dhcp6 | [DHCP6OperatorSpec](#talos.resource.definitions.network.DHCP6OperatorSpec) | | | | vip | [VIPOperatorSpec](#talos.resource.definitions.network.VIPOperatorSpec) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | ### PlatformConfigSpec PlatformConfigSpec describes platform network configuration. This structure is marshaled to STATE partition to persist cached network configuration across reboots. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | addresses | [AddressSpecSpec](#talos.resource.definitions.network.AddressSpecSpec) | repeated | | | links | [LinkSpecSpec](#talos.resource.definitions.network.LinkSpecSpec) | repeated | | | routes | [RouteSpecSpec](#talos.resource.definitions.network.RouteSpecSpec) | repeated | | | hostnames | [HostnameSpecSpec](#talos.resource.definitions.network.HostnameSpecSpec) | repeated | | | resolvers | [ResolverSpecSpec](#talos.resource.definitions.network.ResolverSpecSpec) | repeated | | | time_servers | [TimeServerSpecSpec](#talos.resource.definitions.network.TimeServerSpecSpec) | repeated | | | operators | [OperatorSpecSpec](#talos.resource.definitions.network.OperatorSpecSpec) | repeated | | | external_ips | [common.NetIP](#common.NetIP) | repeated | | | probes | [ProbeSpecSpec](#talos.resource.definitions.network.ProbeSpecSpec) | repeated | | | metadata | [talos.resource.definitions.runtime.PlatformMetadataSpec](#talos.resource.definitions.runtime.PlatformMetadataSpec) | | | ### PortRange PortRange describes a range of ports. Range is [lo, hi]. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | lo | [uint32](#uint32) | | | | hi | [uint32](#uint32) | | | ### ProbeSpecSpec ProbeSpecSpec describes the Probe. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | interval | [google.protobuf.Duration](#google.protobuf.Duration) | | | | failure_threshold | [int64](#int64) | | | | tcp | [TCPProbeSpec](#talos.resource.definitions.network.TCPProbeSpec) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | ### ProbeStatusSpec ProbeStatusSpec describes the Probe. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | success | [bool](#bool) | | | | last_error | [string](#string) | | | ### ResolverSpecSpec ResolverSpecSpec describes DNS resolvers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | dns_servers | [common.NetIP](#common.NetIP) | repeated | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | | search_domains | [string](#string) | repeated | | ### ResolverStatusSpec ResolverStatusSpec describes DNS resolvers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | dns_servers | [common.NetIP](#common.NetIP) | repeated | | | search_domains | [string](#string) | repeated | | ### RouteSpecSpec RouteSpecSpec describes the route. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | destination | [common.NetIPPrefix](#common.NetIPPrefix) | | | | source | [common.NetIP](#common.NetIP) | | | | gateway | [common.NetIP](#common.NetIP) | | | | out_link_name | [string](#string) | | | | table | [talos.resource.definitions.enums.NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) | | | | priority | [uint32](#uint32) | | | | scope | [talos.resource.definitions.enums.NethelpersScope](#talos.resource.definitions.enums.NethelpersScope) | | | | type | [talos.resource.definitions.enums.NethelpersRouteType](#talos.resource.definitions.enums.NethelpersRouteType) | | | | flags | [uint32](#uint32) | | | | protocol | [talos.resource.definitions.enums.NethelpersRouteProtocol](#talos.resource.definitions.enums.NethelpersRouteProtocol) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | | mtu | [uint32](#uint32) | | | ### RouteStatusSpec RouteStatusSpec describes status of rendered secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | destination | [common.NetIPPrefix](#common.NetIPPrefix) | | | | source | [common.NetIP](#common.NetIP) | | | | gateway | [common.NetIP](#common.NetIP) | | | | out_link_index | [uint32](#uint32) | | | | out_link_name | [string](#string) | | | | table | [talos.resource.definitions.enums.NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) | | | | priority | [uint32](#uint32) | | | | scope | [talos.resource.definitions.enums.NethelpersScope](#talos.resource.definitions.enums.NethelpersScope) | | | | type | [talos.resource.definitions.enums.NethelpersRouteType](#talos.resource.definitions.enums.NethelpersRouteType) | | | | flags | [uint32](#uint32) | | | | protocol | [talos.resource.definitions.enums.NethelpersRouteProtocol](#talos.resource.definitions.enums.NethelpersRouteProtocol) | | | | mtu | [uint32](#uint32) | | | ### RoutingRuleSpecSpec RoutingRuleSpecSpec describes the routing rule. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | src | [common.NetIPPrefix](#common.NetIPPrefix) | | | | dst | [common.NetIPPrefix](#common.NetIPPrefix) | | | | table | [talos.resource.definitions.enums.NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) | | | | priority | [uint32](#uint32) | | | | action | [talos.resource.definitions.enums.NethelpersRoutingRuleAction](#talos.resource.definitions.enums.NethelpersRoutingRuleAction) | | | | iif_name | [string](#string) | | | | oif_name | [string](#string) | | | | fw_mark | [uint32](#uint32) | | | | fw_mask | [uint32](#uint32) | | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | ### RoutingRuleStatusSpec RoutingRuleStatusSpec describes the observed routing rule state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | family | [talos.resource.definitions.enums.NethelpersFamily](#talos.resource.definitions.enums.NethelpersFamily) | | | | src | [common.NetIPPrefix](#common.NetIPPrefix) | | | | dst | [common.NetIPPrefix](#common.NetIPPrefix) | | | | table | [talos.resource.definitions.enums.NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) | | | | priority | [uint32](#uint32) | | | | action | [talos.resource.definitions.enums.NethelpersRoutingRuleAction](#talos.resource.definitions.enums.NethelpersRoutingRuleAction) | | | | iif_name | [string](#string) | | | | oif_name | [string](#string) | | | | fw_mark | [uint32](#uint32) | | | | fw_mask | [uint32](#uint32) | | | ### STPSpec STPSpec describes Spanning Tree Protocol (STP) settings of a bridge. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | enabled | [bool](#bool) | | | ### StatusSpec StatusSpec describes network state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | address_ready | [bool](#bool) | | | | connectivity_ready | [bool](#bool) | | | | hostname_ready | [bool](#bool) | | | | etc_files_ready | [bool](#bool) | | | ### TCPProbeSpec TCPProbeSpec describes the TCP Probe. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint | [string](#string) | | | | timeout | [google.protobuf.Duration](#google.protobuf.Duration) | | | ### TimeServerSpecSpec TimeServerSpecSpec describes NTP servers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ntp_servers | [string](#string) | repeated | | | config_layer | [talos.resource.definitions.enums.NetworkConfigLayer](#talos.resource.definitions.enums.NetworkConfigLayer) | | | ### TimeServerStatusSpec TimeServerStatusSpec describes NTP servers. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ntp_servers | [string](#string) | repeated | | ### VIPEquinixMetalSpec VIPEquinixMetalSpec describes virtual (elastic) IP settings for Equinix Metal. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | project_id | [string](#string) | | | | device_id | [string](#string) | | | | api_token | [string](#string) | | | ### VIPHCloudSpec VIPHCloudSpec describes virtual (elastic) IP settings for Hetzner Cloud. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device_id | [int64](#int64) | | | | network_id | [int64](#int64) | | | | api_token | [string](#string) | | | ### VIPOperatorSpec VIPOperatorSpec describes virtual IP operator options. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ip | [common.NetIP](#common.NetIP) | | | | gratuitous_arp | [bool](#bool) | | | | equinix_metal | [VIPEquinixMetalSpec](#talos.resource.definitions.network.VIPEquinixMetalSpec) | | | | h_cloud | [VIPHCloudSpec](#talos.resource.definitions.network.VIPHCloudSpec) | | | ### VLANSpec VLANSpec describes VLAN settings if Kind == "vlan". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | vid | [uint32](#uint32) | | | | protocol | [talos.resource.definitions.enums.NethelpersVLANProtocol](#talos.resource.definitions.enums.NethelpersVLANProtocol) | | | ### VRFMasterSpec VRFMasterSpec describes vrf settings if Kind == "vrf". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | table | [talos.resource.definitions.enums.NethelpersRoutingTable](#talos.resource.definitions.enums.NethelpersRoutingTable) | | | ### VRFSlave VRFSlave contains the name of the master vrf for an interface | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | master_name | [string](#string) | | | ### WireguardPeer WireguardPeer describes a single peer. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | public_key | [string](#string) | | | | preshared_key | [string](#string) | | | | endpoint | [string](#string) | | | | persistent_keepalive_interval | [google.protobuf.Duration](#google.protobuf.Duration) | | | | allowed_ips | [common.NetIPPrefix](#common.NetIPPrefix) | repeated | | ### WireguardSpec WireguardSpec describes Wireguard settings if Kind == "wireguard". | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | private_key | [string](#string) | | | | public_key | [string](#string) | | | | listen_port | [int64](#int64) | | | | firewall_mark | [int64](#int64) | | | | peers | [WireguardPeer](#talos.resource.definitions.network.WireguardPeer) | repeated | |

Top

## resource/definitions/perf/perf.proto ### CPUSpec CPUSpec represents the last CPU stats snapshot. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | cpu | [CPUStat](#talos.resource.definitions.perf.CPUStat) | repeated | | | cpu_total | [CPUStat](#talos.resource.definitions.perf.CPUStat) | | | | irq_total | [uint64](#uint64) | | | | context_switches | [uint64](#uint64) | | | | process_created | [uint64](#uint64) | | | | process_running | [uint64](#uint64) | | | | process_blocked | [uint64](#uint64) | | | | soft_irq_total | [uint64](#uint64) | | | ### CPUStat CPUStat represents a single cpu stat. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | user | [double](#double) | | | | nice | [double](#double) | | | | system | [double](#double) | | | | idle | [double](#double) | | | | iowait | [double](#double) | | | | irq | [double](#double) | | | | soft_irq | [double](#double) | | | | steal | [double](#double) | | | | guest | [double](#double) | | | | guest_nice | [double](#double) | | | ### MemorySpec MemorySpec represents the last Memory stats snapshot. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | mem_total | [uint64](#uint64) | | | | mem_used | [uint64](#uint64) | | | | mem_available | [uint64](#uint64) | | | | buffers | [uint64](#uint64) | | | | cached | [uint64](#uint64) | | | | swap_cached | [uint64](#uint64) | | | | active | [uint64](#uint64) | | | | inactive | [uint64](#uint64) | | | | active_anon | [uint64](#uint64) | | | | inactive_anon | [uint64](#uint64) | | | | active_file | [uint64](#uint64) | | | | inactive_file | [uint64](#uint64) | | | | unevictable | [uint64](#uint64) | | | | mlocked | [uint64](#uint64) | | | | swap_total | [uint64](#uint64) | | | | swap_free | [uint64](#uint64) | | | | dirty | [uint64](#uint64) | | | | writeback | [uint64](#uint64) | | | | anon_pages | [uint64](#uint64) | | | | mapped | [uint64](#uint64) | | | | shmem | [uint64](#uint64) | | | | slab | [uint64](#uint64) | | | | s_reclaimable | [uint64](#uint64) | | | | s_unreclaim | [uint64](#uint64) | | | | kernel_stack | [uint64](#uint64) | | | | page_tables | [uint64](#uint64) | | | | nf_sunstable | [uint64](#uint64) | | | | bounce | [uint64](#uint64) | | | | writeback_tmp | [uint64](#uint64) | | | | commit_limit | [uint64](#uint64) | | | | committed_as | [uint64](#uint64) | | | | vmalloc_total | [uint64](#uint64) | | | | vmalloc_used | [uint64](#uint64) | | | | vmalloc_chunk | [uint64](#uint64) | | | | hardware_corrupted | [uint64](#uint64) | | | | anon_huge_pages | [uint64](#uint64) | | | | shmem_huge_pages | [uint64](#uint64) | | | | shmem_pmd_mapped | [uint64](#uint64) | | | | cma_total | [uint64](#uint64) | | | | cma_free | [uint64](#uint64) | | | | huge_pages_total | [uint64](#uint64) | | | | huge_pages_free | [uint64](#uint64) | | | | huge_pages_rsvd | [uint64](#uint64) | | | | huge_pages_surp | [uint64](#uint64) | | | | hugepagesize | [uint64](#uint64) | | | | direct_map4k | [uint64](#uint64) | | | | direct_map2m | [uint64](#uint64) | | | | direct_map1g | [uint64](#uint64) | | |

Top

## resource/definitions/secrets/secrets.proto ### APICertsSpec APICertsSpec describes etcd certs secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | client | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | server | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | accepted_c_as | [common.PEMEncodedCertificate](#common.PEMEncodedCertificate) | repeated | | | skip_verifying_client_cert | [bool](#bool) | | | ### CertSANSpec CertSANSpec describes fields of the cert SANs. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | i_ps | [common.NetIP](#common.NetIP) | repeated | | | dns_names | [string](#string) | repeated | | | fqdn | [string](#string) | | | ### EncryptionSaltSpec EncryptionSaltSpec describes the salt. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | disk_salt | [bytes](#bytes) | | | ### EtcdCertsSpec EtcdCertsSpec describes etcd certs secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | etcd | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | etcd_peer | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | etcd_admin | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | etcd_api_server | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | ### EtcdRootSpec EtcdRootSpec describes etcd CA secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | etcd_ca | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | ### KubeletSpec KubeletSpec describes root Kubernetes secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | endpoint | [common.URL](#common.URL) | | | | bootstrap_token_id | [string](#string) | | | | bootstrap_token_secret | [string](#string) | | | | accepted_c_as | [common.PEMEncodedCertificate](#common.PEMEncodedCertificate) | repeated | | ### KubernetesCertsSpec KubernetesCertsSpec describes generated Kubernetes certificates. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | scheduler_kubeconfig | [string](#string) | | | | controller_manager_kubeconfig | [string](#string) | | | | localhost_admin_kubeconfig | [string](#string) | | | | admin_kubeconfig | [string](#string) | | | ### KubernetesDynamicCertsSpec KubernetesDynamicCertsSpec describes generated KubernetesCerts certificates. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | api_server | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | api_server_kubelet_client | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | front_proxy | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | ### KubernetesRootSpec KubernetesRootSpec describes root Kubernetes secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | name | [string](#string) | | | | endpoint | [common.URL](#common.URL) | | | | local_endpoint | [common.URL](#common.URL) | | | | cert_sa_ns | [string](#string) | repeated | | | dns_domain | [string](#string) | | | | issuing_ca | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | service_account | [common.PEMEncodedKey](#common.PEMEncodedKey) | | | | aggregator_ca | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | aescbc_encryption_secret | [string](#string) | | | | bootstrap_token_id | [string](#string) | | | | bootstrap_token_secret | [string](#string) | | | | secretbox_encryption_secret | [string](#string) | | | | api_server_ips | [common.NetIP](#common.NetIP) | repeated | | | accepted_c_as | [common.PEMEncodedCertificate](#common.PEMEncodedCertificate) | repeated | | ### MaintenanceRootSpec MaintenanceRootSpec describes maintenance service CA. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ca | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | ### OSRootSpec OSRootSpec describes operating system CA. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | issuing_ca | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | cert_sani_ps | [common.NetIP](#common.NetIP) | repeated | | | cert_sandns_names | [string](#string) | repeated | | | token | [string](#string) | | | | accepted_c_as | [common.PEMEncodedCertificate](#common.PEMEncodedCertificate) | repeated | | ### TrustdCertsSpec TrustdCertsSpec describes etcd certs secrets. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | server | [common.PEMEncodedCertificateAndKey](#common.PEMEncodedCertificateAndKey) | | | | accepted_c_as | [common.PEMEncodedCertificate](#common.PEMEncodedCertificate) | repeated | |

Top

## resource/definitions/security/security.proto ### ImageKeylessVerifierSpec ImageKeylessVerifierSpec represents a signature verification provider. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | issuer | [string](#string) | | | | subject | [string](#string) | | | | subject_regex | [string](#string) | | | ### ImagePublicKeyVerifierSpec ImagePublicKeyVerifierSpec represents a signature verification provider with static public key. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | certificate | [string](#string) | | | ### ImageVerificationRuleSpec ImageVerificationRuleSpec represents a verification rule. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | image_pattern | [string](#string) | | | | skip | [bool](#bool) | | | | deny | [bool](#bool) | | | | keyless_verifier | [ImageKeylessVerifierSpec](#talos.resource.definitions.security.ImageKeylessVerifierSpec) | | | | public_key_verifier | [ImagePublicKeyVerifierSpec](#talos.resource.definitions.security.ImagePublicKeyVerifierSpec) | | | ### TUFTrustedRootSpec TUFTrustedRootSpec represents a sigstore's TUF trusted root information. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | last_refresh_time | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | | json_data | [string](#string) | | |

Top

## resource/definitions/siderolink/siderolink.proto ### ConfigSpec ConfigSpec describes Siderolink configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | api_endpoint | [string](#string) | | | | host | [string](#string) | | | | join_token | [string](#string) | | | | insecure | [bool](#bool) | | | | tunnel | [bool](#bool) | | | ### StatusSpec StatusSpec describes Siderolink status. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | host | [string](#string) | | | | connected | [bool](#bool) | | | | link_name | [string](#string) | | | | grpc_tunnel | [bool](#bool) | | | ### TunnelSpec TunnelSpec describes Siderolink GRPC Tunnel configuration. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | api_endpoint | [string](#string) | | | | link_name | [string](#string) | | | | mtu | [int64](#int64) | | | | node_address | [common.NetIPPort](#common.NetIPPort) | | |

Top

## resource/definitions/time/time.proto ### AdjtimeStatusSpec AdjtimeStatusSpec describes Linux internal adjtime state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | offset | [google.protobuf.Duration](#google.protobuf.Duration) | | | | frequency_adjustment_ratio | [double](#double) | | | | max_error | [google.protobuf.Duration](#google.protobuf.Duration) | | | | est_error | [google.protobuf.Duration](#google.protobuf.Duration) | | | | status | [string](#string) | | | | constant | [int64](#int64) | | | | sync_status | [bool](#bool) | | | | state | [string](#string) | | | ### StatusSpec StatusSpec describes time sync state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | synced | [bool](#bool) | | | | epoch | [int64](#int64) | | | | sync_disabled | [bool](#bool) | | |

Top

## resource/definitions/v1alpha1/v1alpha1.proto ### ServiceSpec ServiceSpec describe service state. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | running | [bool](#bool) | | | | healthy | [bool](#bool) | | | | unknown | [bool](#bool) | | |

Top

## resource/network/device_config.proto ### DeviceConfigSpecSpec DeviceConfigSpecSpec is the spec for the network.DeviceConfigSpec resource. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | yaml_marshalled | [bytes](#bytes) | | Contains YAML marshalled device config (as part of the machine config). |

Top

## security/security.proto ### CertificateRequest The request message containing the certificate signing request. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | csr | [bytes](#bytes) | | Certificate Signing Request in PEM format. | ### CertificateResponse The response message containing signed certificate. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | ca | [bytes](#bytes) | | Certificate of the CA that signed the requested certificate in PEM format. | | crt | [bytes](#bytes) | | Signed X.509 requested certificate in PEM format. | ### SecurityService The security service definition. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | Certificate | [CertificateRequest](#securityapi.CertificateRequest) | [CertificateResponse](#securityapi.CertificateResponse) | |

Top

## storage/storage.proto ### BlockDeviceWipe | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | ### BlockDeviceWipeDescriptor BlockDeviceWipeDescriptor represents a single block device to be wiped. The device can be either a full disk (e.g. vda) or a partition (vda5). The device should not be used in any of active volumes. The device should not be used as a secondary (e.g. part of LVM). | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | device | [string](#string) | | Device name to wipe (e.g. sda or sda5).

The name should be submitted without `/dev/` prefix. | | method | [BlockDeviceWipeDescriptor.Method](#storage.BlockDeviceWipeDescriptor.Method) | | Wipe method to use. | | skip_volume_check | [bool](#bool) | | Skip the volume in use check. | | skip_secondary_check | [bool](#bool) | | Skip the secondary disk check (e.g. underlying disk for RAID or LVM). | | drop_partition | [bool](#bool) | | Drop the partition (only applies if the device is a partition). | ### BlockDeviceWipeRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | devices | [BlockDeviceWipeDescriptor](#storage.BlockDeviceWipeDescriptor) | repeated | | ### BlockDeviceWipeResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [BlockDeviceWipe](#storage.BlockDeviceWipe) | repeated | | ### Disk Disk represents a disk. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | size | [uint64](#uint64) | | Size indicates the disk size in bytes. | | model | [string](#string) | | Model idicates the disk model. | | device_name | [string](#string) | | DeviceName indicates the disk name (e.g. `sda`). | | name | [string](#string) | | Name as in `/sys/block//device/name`. | | serial | [string](#string) | | Serial as in `/sys/block//device/serial`. | | modalias | [string](#string) | | Modalias as in `/sys/block//device/modalias`. | | uuid | [string](#string) | | Uuid as in `/sys/block//device/uuid`. | | wwid | [string](#string) | | Wwid as in `/sys/block//device/wwid`. | | type | [Disk.DiskType](#storage.Disk.DiskType) | | Type is a type of the disk: nvme, ssd, hdd, sd card. | | bus_path | [string](#string) | | BusPath is the bus path of the disk. | | system_disk | [bool](#bool) | | SystemDisk indicates that the disk is used as Talos system disk. | | subsystem | [string](#string) | | Subsystem is the symlink path in the `/sys/block//subsystem`. | | readonly | [bool](#bool) | | Readonly specifies if the disk is read only. | ### Disks DisksResponse represents the response of the `Disks` RPC. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | disks | [Disk](#storage.Disk) | repeated | | ### DisksResponse | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Disks](#storage.Disks) | repeated | | ### BlockDeviceWipeDescriptor.Method | Name | Number | Description | | ---- | ------ | ----------- | | FAST | 0 | Fast wipe - wipe only filesystem signatures. | | ZEROES | 1 | Zeroes wipe - wipe by overwriting with zeroes (might be slow depending on the disk size and available hardware features). | ### Disk.DiskType | Name | Number | Description | | ---- | ------ | ----------- | | UNKNOWN | 0 | | | SSD | 1 | | | HDD | 2 | | | NVME | 3 | | | SD | 4 | | | CD | 5 | | ### StorageService StorageService represents the storage service. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | Disks | [.google.protobuf.Empty](#google.protobuf.Empty) | [DisksResponse](#storage.DisksResponse) | | | BlockDeviceWipe | [BlockDeviceWipeRequest](#storage.BlockDeviceWipeRequest) | [BlockDeviceWipeResponse](#storage.BlockDeviceWipeResponse) | BlockDeviceWipe performs a wipe of the blockdevice (partition or disk).

The method doesn't require a reboot, and it can only wipe blockdevices which are not being used as volumes at the moment. Wiping of volumes requires a different API. |

Top

## time/time.proto ### Time | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | metadata | [common.Metadata](#common.Metadata) | | | | server | [string](#string) | | | | localtime | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | | remotetime | [google.protobuf.Timestamp](#google.protobuf.Timestamp) | | | ### TimeRequest The response message containing the ntp server | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | server | [string](#string) | | | ### TimeResponse The response message containing the ntp server, time, and offset | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | messages | [Time](#time.Time) | repeated | | ### TimeService The time service definition. | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| | Time | [.google.protobuf.Empty](#google.protobuf.Empty) | [TimeResponse](#time.TimeResponse) | | | TimeCheck | [TimeRequest](#time.TimeRequest) | [TimeResponse](#time.TimeResponse) | | ## Scalar Value Types | .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | | ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | | double | | double | double | float | float64 | double | float | Float | | float | | float | float | float | float32 | float | float | Float | | int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | | uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | | uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | | sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | | fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | | fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | | sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | | sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | | bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | | string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | | bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | ================================================ FILE: website/content/v1.13/reference/cli.md ================================================ --- description: Talosctl CLI tool reference. title: CLI --- ## talosctl apply-config Apply a new configuration to a node ``` talosctl apply-config [flags] ``` ### Options ``` --cert-fingerprint strings list of server certificate fingeprints to accept (defaults to no check) -c, --cluster string Cluster to connect to if a proxy endpoint is used. -p, --config-patch stringArray the list of config patches to apply to the local config file before sending it to the node --context string Context to be used in command --dry-run check how the config change will be applied in dry-run mode -e, --endpoints strings override default endpoints in Talos configuration -f, --file string the filename of the updated configuration -h, --help help for apply-config -i, --insecure apply the config using the insecure (encrypted with no auth) maintenance service -m, --mode auto, no-reboot, reboot, staged, try apply config mode (default auto) -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration the config will be rolled back after specified timeout (if try mode is selected) (default 1m0s) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl bootstrap Bootstrap the etcd cluster on the specified node. ### Synopsis When Talos cluster is created etcd service on control plane nodes enter the join loop waiting to join etcd peers from other control plane nodes. One node should be picked as the bootstrap node. When bootstrap command is issued, the node aborts join process and bootstraps etcd cluster as a single node cluster. Other control plane nodes will join etcd cluster once Kubernetes is bootstrapped on the bootstrap node. This command should not be used when "init" type node are used. Talos etcd cluster can be recovered from a known snapshot with '--recover-from=' flag. ``` talosctl bootstrap [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for bootstrap -n, --nodes strings target the specified nodes --recover-from string recover etcd cluster from the snapshot --recover-skip-hash-check skip integrity check when recovering etcd (use when recovering from data directory copy) --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl cgroups Retrieve cgroups usage information ### Synopsis The cgroups command fetches control group v2 (cgroupv2) usage details from the machine. Several presets are available to focus on specific cgroup subsystems: * cpu * cpuset * io * memory * process * swap You can specify the preset using the --preset flag. Alternatively, a custom schema can be provided using the --schema-file flag. To see schema examples, refer to https://github.com/siderolabs/talos/tree/main/cmd/talosctl/cmd/talos/cgroupsprinter/schemas. ``` talosctl cgroups [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for cgroups -n, --nodes strings target the specified nodes --preset string preset name (one of: [cpu cpuset io memory process psi swap]) --schema-file string path to the columns schema file --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --skip-cri-resolve do not resolve cgroup names via a request to CRI --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl cluster create dev Creates a local QEMU-based cluster for Talos development. ``` talosctl cluster create dev [flags] ``` ### Options ``` --airgapped limit VM network access to the provisioning network only --arch string cluster architecture (default "amd64") --bad-rtc launch VM with bad RTC state --cidr string CIDR of the cluster network (IPv4, ULA network for IPv6 is derived in automated way) (default "10.5.0.0/24") --cni-bin-path strings search path for CNI binaries (default [/home/user/.talos/cni/bin]) --cni-bundle-url string URL to download CNI bundle from (default "https://github.com/siderolabs/talos/releases/download/v1.13.0-alpha.2/talosctl-cni-bundle-${ARCH}.tar.gz") --cni-cache-dir string CNI cache directory path (default "/home/user/.talos/cni/cache") --cni-conf-dir string CNI config directory path (default "/home/user/.talos/cni/conf.d") --config-injection-method string a method to inject machine config: default is HTTP server, 'metal-iso' to mount an ISO --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-control-plane stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-worker stringArray patch generated machineconfigs (applied to 'worker' type) --control-plane-port int control plane port (load balancer and local API port) (default 6443) --controlplanes int the number of controlplanes to create (default 1) --cpus string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") --custom-cni-url string install custom CNI from the URL (Talos cluster) --disable-dhcp-hostname skip announcing hostname via DHCP --disk int default limit on disk size in MB (each VM) (default 6144) --disk-block-size uint disk block size (default 512) --disk-encryption-key-types stringArray encryption key types to use for disk encryption (uuid, kms) (default [uuid]) --disk-image-path string disk image to use --disk-preallocate whether disk space should be preallocated (default true) --dns-domain string the dns domain to use for cluster (default "cluster.local") --encrypt-ephemeral enable ephemeral partition encryption --encrypt-state enable state partition encryption --encrypt-user-volumes enable ephemeral partition encryption --endpoint string use endpoint instead of provider defaults --extra-boot-kernel-args string add extra kernel args to the initial boot from vmlinuz and initramfs --extra-disks int number of extra disks to create for each worker VM --extra-disks-drivers strings driver for each extra disk (virtio, ide, ahci, scsi, nvme, megaraid) --extra-disks-serials strings serials for each extra disk --extra-disks-size int default limit on disk size in MB (each VM) (default 5120) --extra-disks-tags strings tags for each extra disk (only used by virtiofs) --extra-uefi-search-paths strings additional search paths for UEFI firmware (only applies when UEFI is enabled) -h, --help help for dev --image-cache-path string path to image cache --image-cache-port uint16 port on which to serve image cache (default 5000) --image-cache-tls-cert-file string path to image cache TLS cert --image-cache-tls-key-file string path to image cache TLS key --init-node-as-endpoint use init node as endpoint instead of any load balancer endpoint --initrd-path string initramfs image to use (default "_out/initramfs-${ARCH}.xz") --install-image string the installer image to use (default "ghcr.io/siderolabs/installer:latest") --ipv4 enable IPv4 network in the cluster (default true) --ipv6 enable IPv6 network in the cluster --ipxe-boot-script string iPXE boot script (URL) to use --iso-path string the ISO path to use for the initial boot --kubeprism-port int KubePrism port (set to 0 to disable) (default 7445) --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --mtu int MTU of the cluster network (default 1500) --nameservers strings list of nameservers to use --no-masquerade-cidrs strings list of CIDRs to exclude from NAT --omni-api-endpoint string the Omni API endpoint (must include a scheme, a hostname and a join token, e.g. 'https://siderolink.omni.example?jointoken=foobar') --registry-insecure-skip-verify strings list of registry hostnames to skip TLS verification for --registry-mirror strings list of registry mirrors to use in format: = --skip-injecting-config skip injecting config from embedded metadata server, write config files to current directory --skip-injecting-extra-cmdline skip injecting extra kernel cmdline parameters via EFI vars through bootloader --skip-k8s-node-readiness-check skip k8s node readiness checks --skip-kubeconfig skip merging kubeconfig from the created cluster --talos-version string the desired Talos version to generate config for (default "latest") --talosconfig string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --uki-path string the UKI image path to use for the initial boot --usb-path string the USB stick image path to use for the initial boot --use-vip use a virtual IP for the controlplane endpoint instead of the loadbalancer --user-volumes strings list of user volumes to create for each VM in format: ::: --vmlinuz-path string the compressed kernel image to use (default "_out/vmlinuz-${ARCH}") --wait wait for the cluster to be ready before returning (default true) --wait-timeout duration timeout to wait for the cluster to be ready (default 20m0s) --wireguard-cidr string CIDR of the wireguard network --with-apply-config enable apply config when the VM is starting in maintenance mode --with-bootloader enable bootloader to load kernel and initramfs from disk image after install (default true) --with-cluster-discovery enable cluster discovery (default true) --with-debug enable debug in Talos config to send service logs to the console --with-firewall string inject firewall rules into the cluster, value is default policy - accept/block --with-init-node create the cluster with an init node --with-iommu enable IOMMU support, this also add a new PCI root port and an interface attached to it --with-json-logs enable JSON logs receiver and configure Talos to send logs there --with-kubespan enable KubeSpan system --with-network-bandwidth int specify bandwidth restriction (in kbps) on the bridge interface --with-network-chaos enable to use network chaos parameters --with-network-jitter duration specify jitter on the bridge interface --with-network-latency duration specify latency on the bridge interface --with-network-packet-corrupt float specify percent of corrupt packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-network-packet-loss float specify percent of packet loss on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-network-packet-reorder float specify percent of reordered packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-siderolink true enables the use of siderolink agent as configuration apply mechanism. true or `wireguard` enables the agent, `tunnel` enables the agent with grpc tunneling (default none) --with-tpm1_2 enable TPM 1.2 emulation support using swtpm --with-tpm2 enable TPM 2.0 emulation support using swtpm --with-uefi enable UEFI on x86_64 architecture (default true) --with-uuid-hostnames use machine UUIDs as default hostnames --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster create docker Create a local Docker based kubernetes cluster ``` talosctl cluster create docker [flags] ``` ### Options ``` --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-controlplanes stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-workers stringArray patch generated machineconfigs (applied to 'worker' type) --cpus-controlplanes string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") -p, --exposed-ports string comma-separated list of ports/protocols to expose on init node. Ex -p :/ -h, --help help for docker --host-ip string Host IP to forward exposed ports to (default "0.0.0.0") --image string the talos image to run (default "ghcr.io/siderolabs/talos:latest") --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory-controlplanes string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --mount mount attach a mount to the container (docker --mount syntax) --subnet string Docker network subnet CIDR (default "10.5.0.0/24") --talosconfig-destination string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster create qemu Create a local QEMU based Talos cluster. ### Synopsis Create a local QEMU based Talos cluster. Available presets: - iso: Configure Talos to boot from an ISO from the Image Factory. - iso-secureboot: Configure Talos for Secureboot via ISO. Only available on Linux hosts. - pxe: Configure Talos to boot via PXE from the Image Factory. - disk-image: Configure Talos to boot from a disk image from the Image Factory. - maintenance: Skip applying machine configuration and leave the machines in maintenance mode. The machine configuration files are written to the working directory. Note: exactly one of 'iso', 'iso-secureboot', 'pxe' or 'disk-image' presets must be specified. ``` talosctl cluster create qemu [flags] ``` ### Options ``` --cidr string CIDR of the cluster network (default "10.5.0.0/24") --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-controlplanes stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-workers stringArray patch generated machineconfigs (applied to 'worker' type) --controlplanes int the number of controlplanes to create (default 1) --cpus-controlplanes string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") --disks disks list of disks to create in format ":" (disks after the first one are added only to worker machines) (default virtio:10GiB,virtio:6GiB) -h, --help help for qemu --image-factory-url string image factory url (default "https://factory.talos.dev/") --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory-controlplanes string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --omni-api-endpoint string the Omni API endpoint (must include a scheme, a hostname and a join token, e.g. 'https://siderolink.omni.example?jointoken=foobar') --presets strings list of presets to apply (default [iso]) --schematic-id string image factory schematic id (defaults to an empty schematic) --talos-version string the desired talos version (default "latest") --talosconfig-destination string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster create dev Creates a local QEMU-based cluster for Talos development. ``` talosctl cluster create dev [flags] ``` ### Options ``` --airgapped limit VM network access to the provisioning network only --arch string cluster architecture (default "amd64") --bad-rtc launch VM with bad RTC state --cidr string CIDR of the cluster network (IPv4, ULA network for IPv6 is derived in automated way) (default "10.5.0.0/24") --cni-bin-path strings search path for CNI binaries (default [/home/user/.talos/cni/bin]) --cni-bundle-url string URL to download CNI bundle from (default "https://github.com/siderolabs/talos/releases/download/v1.13.0-alpha.2/talosctl-cni-bundle-${ARCH}.tar.gz") --cni-cache-dir string CNI cache directory path (default "/home/user/.talos/cni/cache") --cni-conf-dir string CNI config directory path (default "/home/user/.talos/cni/conf.d") --config-injection-method string a method to inject machine config: default is HTTP server, 'metal-iso' to mount an ISO --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-control-plane stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-worker stringArray patch generated machineconfigs (applied to 'worker' type) --control-plane-port int control plane port (load balancer and local API port) (default 6443) --controlplanes int the number of controlplanes to create (default 1) --cpus string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") --custom-cni-url string install custom CNI from the URL (Talos cluster) --disable-dhcp-hostname skip announcing hostname via DHCP --disk int default limit on disk size in MB (each VM) (default 6144) --disk-block-size uint disk block size (default 512) --disk-encryption-key-types stringArray encryption key types to use for disk encryption (uuid, kms) (default [uuid]) --disk-image-path string disk image to use --disk-preallocate whether disk space should be preallocated (default true) --dns-domain string the dns domain to use for cluster (default "cluster.local") --encrypt-ephemeral enable ephemeral partition encryption --encrypt-state enable state partition encryption --encrypt-user-volumes enable ephemeral partition encryption --endpoint string use endpoint instead of provider defaults --extra-boot-kernel-args string add extra kernel args to the initial boot from vmlinuz and initramfs --extra-disks int number of extra disks to create for each worker VM --extra-disks-drivers strings driver for each extra disk (virtio, ide, ahci, scsi, nvme, megaraid) --extra-disks-serials strings serials for each extra disk --extra-disks-size int default limit on disk size in MB (each VM) (default 5120) --extra-disks-tags strings tags for each extra disk (only used by virtiofs) --extra-uefi-search-paths strings additional search paths for UEFI firmware (only applies when UEFI is enabled) -h, --help help for dev --image-cache-path string path to image cache --image-cache-port uint16 port on which to serve image cache (default 5000) --image-cache-tls-cert-file string path to image cache TLS cert --image-cache-tls-key-file string path to image cache TLS key --init-node-as-endpoint use init node as endpoint instead of any load balancer endpoint --initrd-path string initramfs image to use (default "_out/initramfs-${ARCH}.xz") --install-image string the installer image to use (default "ghcr.io/siderolabs/installer:latest") --ipv4 enable IPv4 network in the cluster (default true) --ipv6 enable IPv6 network in the cluster --ipxe-boot-script string iPXE boot script (URL) to use --iso-path string the ISO path to use for the initial boot --kubeprism-port int KubePrism port (set to 0 to disable) (default 7445) --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --mtu int MTU of the cluster network (default 1500) --nameservers strings list of nameservers to use --no-masquerade-cidrs strings list of CIDRs to exclude from NAT --omni-api-endpoint string the Omni API endpoint (must include a scheme, a hostname and a join token, e.g. 'https://siderolink.omni.example?jointoken=foobar') --registry-insecure-skip-verify strings list of registry hostnames to skip TLS verification for --registry-mirror strings list of registry mirrors to use in format: = --skip-injecting-config skip injecting config from embedded metadata server, write config files to current directory --skip-injecting-extra-cmdline skip injecting extra kernel cmdline parameters via EFI vars through bootloader --skip-k8s-node-readiness-check skip k8s node readiness checks --skip-kubeconfig skip merging kubeconfig from the created cluster --talos-version string the desired Talos version to generate config for (default "latest") --talosconfig string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --uki-path string the UKI image path to use for the initial boot --usb-path string the USB stick image path to use for the initial boot --use-vip use a virtual IP for the controlplane endpoint instead of the loadbalancer --user-volumes strings list of user volumes to create for each VM in format: ::: --vmlinuz-path string the compressed kernel image to use (default "_out/vmlinuz-${ARCH}") --wait wait for the cluster to be ready before returning (default true) --wait-timeout duration timeout to wait for the cluster to be ready (default 20m0s) --wireguard-cidr string CIDR of the wireguard network --with-apply-config enable apply config when the VM is starting in maintenance mode --with-bootloader enable bootloader to load kernel and initramfs from disk image after install (default true) --with-cluster-discovery enable cluster discovery (default true) --with-debug enable debug in Talos config to send service logs to the console --with-firewall string inject firewall rules into the cluster, value is default policy - accept/block --with-init-node create the cluster with an init node --with-iommu enable IOMMU support, this also add a new PCI root port and an interface attached to it --with-json-logs enable JSON logs receiver and configure Talos to send logs there --with-kubespan enable KubeSpan system --with-network-bandwidth int specify bandwidth restriction (in kbps) on the bridge interface --with-network-chaos enable to use network chaos parameters --with-network-jitter duration specify jitter on the bridge interface --with-network-latency duration specify latency on the bridge interface --with-network-packet-corrupt float specify percent of corrupt packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-network-packet-loss float specify percent of packet loss on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-network-packet-reorder float specify percent of reordered packets on the bridge interface. e.g. 50% = 0.50 (default: 0.0) --with-siderolink true enables the use of siderolink agent as configuration apply mechanism. true or `wireguard` enables the agent, `tunnel` enables the agent with grpc tunneling (default none) --with-tpm1_2 enable TPM 1.2 emulation support using swtpm --with-tpm2 enable TPM 2.0 emulation support using swtpm --with-uefi enable UEFI on x86_64 architecture (default true) --with-uuid-hostnames use machine UUIDs as default hostnames --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster create docker Create a local Docker based kubernetes cluster ``` talosctl cluster create docker [flags] ``` ### Options ``` --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-controlplanes stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-workers stringArray patch generated machineconfigs (applied to 'worker' type) --cpus-controlplanes string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") -p, --exposed-ports string comma-separated list of ports/protocols to expose on init node. Ex -p :/ -h, --help help for docker --host-ip string Host IP to forward exposed ports to (default "0.0.0.0") --image string the talos image to run (default "ghcr.io/siderolabs/talos:latest") --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory-controlplanes string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --mount mount attach a mount to the container (docker --mount syntax) --subnet string Docker network subnet CIDR (default "10.5.0.0/24") --talosconfig-destination string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster create qemu Create a local QEMU based Talos cluster. ### Synopsis Create a local QEMU based Talos cluster. Available presets: - iso: Configure Talos to boot from an ISO from the Image Factory. - iso-secureboot: Configure Talos for Secureboot via ISO. Only available on Linux hosts. - pxe: Configure Talos to boot via PXE from the Image Factory. - disk-image: Configure Talos to boot from a disk image from the Image Factory. - maintenance: Skip applying machine configuration and leave the machines in maintenance mode. The machine configuration files are written to the working directory. Note: exactly one of 'iso', 'iso-secureboot', 'pxe' or 'disk-image' presets must be specified. ``` talosctl cluster create qemu [flags] ``` ### Options ``` --cidr string CIDR of the cluster network (default "10.5.0.0/24") --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-controlplanes stringArray patch generated machineconfigs (applied to 'controlplane' type) --config-patch-workers stringArray patch generated machineconfigs (applied to 'worker' type) --controlplanes int the number of controlplanes to create (default 1) --cpus-controlplanes string the share of CPUs as fraction for each control plane/VM (default "2.0") --cpus-workers string the share of CPUs as fraction for each worker/VM (default "2.0") --disks disks list of disks to create in format ":" (disks after the first one are added only to worker machines) (default virtio:10GiB,virtio:6GiB) -h, --help help for qemu --image-factory-url string image factory url (default "https://factory.talos.dev/") --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") --memory-controlplanes string(mb,gb) the limit on memory usage for each control plane/VM (default 2.0GiB) --memory-workers string(mb,gb) the limit on memory usage for each worker/VM (default 2.0GiB) --omni-api-endpoint string the Omni API endpoint (must include a scheme, a hostname and a join token, e.g. 'https://siderolink.omni.example?jointoken=foobar') --presets strings list of presets to apply (default [iso]) --schematic-id string image factory schematic id (defaults to an empty schematic) --talos-version string the desired talos version (default "latest") --talosconfig-destination string The location to save the generated Talos configuration file to. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --workers int the number of workers to create (default 1) ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. ## talosctl cluster destroy Destroys a local Talos kubernetes cluster ``` talosctl cluster destroy [flags] ``` ### Options ``` -f, --force force deletion of cluster directory if there were errors -h, --help help for destroy --save-cluster-logs-archive-path string save cluster logs archive to the specified file on destroy --save-support-archive-path string save support archive to the specified file on destroy ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster](#talosctl-cluster) - A collection of commands for managing local docker-based or QEMU-based clusters ## talosctl cluster show Shows info about a local provisioned kubernetes cluster ``` talosctl cluster show [flags] ``` ### Options ``` -h, --help help for show --provisioner string Talos cluster provisioner to use (default "docker") ``` ### Options inherited from parent commands ``` --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl cluster](#talosctl-cluster) - A collection of commands for managing local docker-based or QEMU-based clusters ## talosctl cluster A collection of commands for managing local docker-based or QEMU-based clusters ### Options ``` -h, --help help for cluster --name string the name of the cluster (default "talos-default") --state string directory path to store cluster state (default "/home/user/.talos/clusters") ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl cluster create](#talosctl-cluster-create) - Create a local Talos cluster. * [talosctl cluster destroy](#talosctl-cluster-destroy) - Destroys a local Talos kubernetes cluster * [talosctl cluster show](#talosctl-cluster-show) - Shows info about a local provisioned kubernetes cluster ## talosctl completion Output shell completion code for the specified shell (bash, fish or zsh) ### Synopsis Output shell completion code for the specified shell (bash, fish or zsh). The shell code must be evaluated to provide interactive completion of talosctl commands. This can be done by sourcing it from the .bash_profile. Note for zsh users: [1] zsh completions are only supported in versions of zsh >= 5.2 ``` talosctl completion SHELL [flags] ``` ### Examples ``` # Installing bash completion on macOS using homebrew ## If running Bash 3.2 included with macOS brew install bash-completion ## or, if running Bash 4.1+ brew install bash-completion@2 ## If talosctl is installed via homebrew, this should start working immediately. ## If you've installed via other means, you may need add the completion to your completion directory talosctl completion bash > $(brew --prefix)/etc/bash_completion.d/talosctl # Installing bash completion on Linux ## If bash-completion is not installed on Linux, please install the 'bash-completion' package ## via your distribution's package manager. ## Load the talosctl completion code for bash into the current shell source <(talosctl completion bash) ## Write bash completion code to a file and source if from .bash_profile talosctl completion bash > "${TALOS_HOME:-$HOME/.talos}/completion.bash.inc" printf ' # talosctl shell completion source "${TALOS_HOME:-$HOME/.talos}/completion.bash.inc" ' >> $HOME/.bash_profile source $HOME/.bash_profile # Load the talosctl completion code for fish[1] into the current shell talosctl completion fish | source # Set the talosctl completion code for fish[1] to autoload on startup talosctl completion fish > ~/.config/fish/completions/talosctl.fish # Load the talosctl completion code for zsh[1] into the current shell source <(talosctl completion zsh) # Set the talosctl completion code for zsh[1] to autoload on startup talosctl completion zsh > "${fpath[1]}/_talosctl" ``` ### Options ``` -h, --help help for completion ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl config add Add a new context ``` talosctl config add [flags] ``` ### Options ``` --ca string the path to the CA certificate --crt string the path to the certificate -h, --help help for add --key string the path to the key ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config context Set the current context ``` talosctl config context [flags] ``` ### Options ``` -h, --help help for context ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config contexts List defined contexts ``` talosctl config contexts [flags] ``` ### Options ``` -h, --help help for contexts ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config endpoint Set the endpoint(s) for the current context ``` talosctl config endpoint ... [flags] ``` ### Options ``` -h, --help help for endpoint ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config info Show information about the current context ``` talosctl config info [flags] ``` ### Options ``` -h, --help help for info -o, --output string output format (json|yaml|text). Default text. (default "text") ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config merge Merge additional contexts from another client configuration file ### Synopsis Contexts with the same name are renamed while merging configs. ``` talosctl config merge [flags] ``` ### Options ``` -h, --help help for merge ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config new Generate a new client configuration file ``` talosctl config new [] [flags] ``` ### Options ``` --crt-ttl duration certificate TTL (default 8760h0m0s) -h, --help help for new --roles strings roles (default [os:admin]) ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config node Set the node(s) for the current context ``` talosctl config node ... [flags] ``` ### Options ``` -h, --help help for node ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config remove Remove contexts ``` talosctl config remove [flags] ``` ### Options ``` --dry-run dry run -h, --help help for remove -y, --noconfirm do not ask for confirmation ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) ## talosctl config Manage the client configuration file (talosconfig) ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for config -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl config add](#talosctl-config-add) - Add a new context * [talosctl config context](#talosctl-config-context) - Set the current context * [talosctl config contexts](#talosctl-config-contexts) - List defined contexts * [talosctl config endpoint](#talosctl-config-endpoint) - Set the endpoint(s) for the current context * [talosctl config info](#talosctl-config-info) - Show information about the current context * [talosctl config merge](#talosctl-config-merge) - Merge additional contexts from another client configuration file * [talosctl config new](#talosctl-config-new) - Generate a new client configuration file * [talosctl config node](#talosctl-config-node) - Set the node(s) for the current context * [talosctl config remove](#talosctl-config-remove) - Remove contexts ## talosctl conformance kubernetes Run Kubernetes conformance tests ``` talosctl conformance kubernetes [flags] ``` ### Options ``` -h, --help help for kubernetes --mode string conformance test mode: [fast, certified, network-policy] (default "fast") ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl conformance](#talosctl-conformance) - Run conformance tests ## talosctl conformance Run conformance tests ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for conformance -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl conformance kubernetes](#talosctl-conformance-kubernetes) - Run Kubernetes conformance tests ## talosctl containers List containers ``` talosctl containers [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for containers -k, --kubernetes use the k8s.io containerd namespace -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl copy Copy data out from the node ### Synopsis Creates an .tar.gz archive at the node starting at and streams it back to the client. If '-' is given for , archive is written to stdout. Otherwise archive is extracted to which should be an empty directory or talosctl creates a directory if doesn't exist. Command doesn't preserve ownership and access mode for the files in extract mode, while streamed .tar archive captures ownership and permission bits. ``` talosctl copy -| [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for copy -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl dashboard Cluster dashboard with node overview, logs and real-time metrics ### Synopsis Provide a text-based UI to navigate node overview, logs and real-time metrics. Keyboard shortcuts: - h, - switch one node to the left - l, - switch one node to the right - j, - scroll logs/process list down - k, - scroll logs/process list up - - scroll logs/process list half page down - - scroll logs/process list half page up - - scroll logs/process list one page down - - scroll logs/process list one page up ``` talosctl dashboard [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for dashboard -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -d, --update-interval duration interval between updates (default 3s) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl debug Run a debug container from an image archive or reference ``` talosctl debug [args] [flags] ``` ### Examples ``` # Run a debug container from a local tar archive (image will be loaded into Talos from the archive) talosctl debug ./debug-tools.tar --args /bin/sh # Run a debug container from an image reference (Talos will pull the image if not present) talosctl debug docker.io/library/alpine:latest --args /bin/sh ``` ### Options ``` --args strings arguments to pass to the container -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for debug --namespace system namespace to use: system (CRI containerd) or `inmem` for in-memory containerd instance (default "inmem") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl dmesg Retrieve kernel logs ``` talosctl dmesg [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -f, --follow specify if the kernel log should be streamed -h, --help help for dmesg -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --tail specify if only new messages should be sent (makes sense only when combined with --follow) --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl edit Edit Talos node machine configuration with the default editor. ### Synopsis The edit command allows you to directly edit the machine configuration of a Talos node using your preferred text editor. It will open the editor defined by your TALOS_EDITOR, or EDITOR environment variables, or fall back to 'vi' for Linux or 'notepad' for Windows. ``` talosctl edit machineconfig [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --dry-run do not apply the change after editing and print the change summary instead -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for edit -m, --mode auto, no-reboot, reboot, staged, try apply config mode (default auto) --namespace string resource namespace (default is to use default namespace per resource) -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration the config will be rolled back after specified timeout (if try mode is selected) (default 1m0s) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl etcd alarm disarm Disarm the etcd alarms for the node. ``` talosctl etcd alarm disarm [flags] ``` ### Options ``` -h, --help help for disarm ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd alarm](#talosctl-etcd-alarm) - Manage etcd alarms ## talosctl etcd alarm list List the etcd alarms for the node. ``` talosctl etcd alarm list [flags] ``` ### Options ``` -h, --help help for list ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd alarm](#talosctl-etcd-alarm) - Manage etcd alarms ## talosctl etcd alarm Manage etcd alarms ### Options ``` -h, --help help for alarm ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd * [talosctl etcd alarm disarm](#talosctl-etcd-alarm-disarm) - Disarm the etcd alarms for the node. * [talosctl etcd alarm list](#talosctl-etcd-alarm-list) - List the etcd alarms for the node. ## talosctl etcd defrag Defragment etcd database on the node ### Synopsis Defragmentation is a maintenance operation that releases unused space from the etcd database file. Defragmentation is a resource heavy operation and should be performed only when necessary on a single node at a time. ``` talosctl etcd defrag [flags] ``` ### Options ``` -h, --help help for defrag ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd downgrade cancel Cancel etcd storage system downgrade. ``` talosctl etcd downgrade cancel [flags] ``` ### Options ``` -h, --help help for cancel ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd downgrade](#talosctl-etcd-downgrade) - Manage etcd storage system downgrades ## talosctl etcd downgrade enable Enable etcd storage system downgrade to the specified version. ``` talosctl etcd downgrade enable [flags] ``` ### Options ``` -h, --help help for enable ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd downgrade](#talosctl-etcd-downgrade) - Manage etcd storage system downgrades ## talosctl etcd downgrade validate Validate if the etcd storage system can be downgraded to the specified version. ``` talosctl etcd downgrade validate [flags] ``` ### Options ``` -h, --help help for validate ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd downgrade](#talosctl-etcd-downgrade) - Manage etcd storage system downgrades ## talosctl etcd downgrade Manage etcd storage system downgrades ### Options ``` -h, --help help for downgrade ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd * [talosctl etcd downgrade cancel](#talosctl-etcd-downgrade-cancel) - Cancel etcd storage system downgrade. * [talosctl etcd downgrade enable](#talosctl-etcd-downgrade-enable) - Enable etcd storage system downgrade to the specified version. * [talosctl etcd downgrade validate](#talosctl-etcd-downgrade-validate) - Validate if the etcd storage system can be downgraded to the specified version. ## talosctl etcd forfeit-leadership Tell node to forfeit etcd cluster leadership ``` talosctl etcd forfeit-leadership [flags] ``` ### Options ``` -h, --help help for forfeit-leadership ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd leave Tell nodes to leave etcd cluster ``` talosctl etcd leave [flags] ``` ### Options ``` -h, --help help for leave ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd members Get the list of etcd cluster members ``` talosctl etcd members [flags] ``` ### Options ``` -h, --help help for members ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd remove-member Remove the node from etcd cluster ### Synopsis Use this command only if you want to remove a member which is in broken state. If there is no access to the node, or the node can't access etcd to call etcd leave. Always prefer etcd leave over this command. ``` talosctl etcd remove-member [flags] ``` ### Options ``` -h, --help help for remove-member ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd snapshot Stream snapshot of the etcd node to the path. ``` talosctl etcd snapshot [flags] ``` ### Options ``` -h, --help help for snapshot ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd status Get the status of etcd cluster member ### Synopsis Returns the status of etcd member on the node, use multiple nodes to get status of all members. ``` talosctl etcd status [flags] ``` ### Options ``` -h, --help help for status ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl etcd](#talosctl-etcd) - Manage etcd ## talosctl etcd Manage etcd ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for etcd -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl etcd alarm](#talosctl-etcd-alarm) - Manage etcd alarms * [talosctl etcd defrag](#talosctl-etcd-defrag) - Defragment etcd database on the node * [talosctl etcd downgrade](#talosctl-etcd-downgrade) - Manage etcd storage system downgrades * [talosctl etcd forfeit-leadership](#talosctl-etcd-forfeit-leadership) - Tell node to forfeit etcd cluster leadership * [talosctl etcd leave](#talosctl-etcd-leave) - Tell nodes to leave etcd cluster * [talosctl etcd members](#talosctl-etcd-members) - Get the list of etcd cluster members * [talosctl etcd remove-member](#talosctl-etcd-remove-member) - Remove the node from etcd cluster * [talosctl etcd snapshot](#talosctl-etcd-snapshot) - Stream snapshot of the etcd node to the path. * [talosctl etcd status](#talosctl-etcd-status) - Get the status of etcd cluster member ## talosctl events Stream runtime events ``` talosctl events [flags] ``` ### Options ``` --actor-id string filter events by the specified actor ID (default is no filter) -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --duration duration show events for the past duration interval (one second resolution, default is to show no history) -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for events -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --since string show events after the specified event ID (default is to show no history) --tail int32 show specified number of past events (use -1 to show full history, default is to show no history) --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl gen ca Generates a self-signed X.509 certificate authority ``` talosctl gen ca [flags] ``` ### Options ``` -h, --help help for ca --hours int the hours from now on which the certificate validity period ends (default 87600) --organization string X.509 distinguished name for the Organization --rsa generate in RSA format ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen config Generates a set of configuration files for Talos cluster ### Synopsis The cluster endpoint is the URL for the Kubernetes API. If you decide to use a control plane node, common in a single node control plane setup, use port 6443 as this is the port that the API server binds to on every control plane node. For an HA setup, usually involving a load balancer, use the IP and port of the load balancer. ``` talosctl gen config [flags] ``` ### Options ``` --additional-sans strings additional Subject-Alt-Names for the APIServer certificate --config-patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file --config-patch-control-plane stringArray patch generated machineconfigs (applied to 'init' and 'controlplane' types) --config-patch-worker stringArray patch generated machineconfigs (applied to 'worker' type) --dns-domain string the dns domain to use for cluster (default "cluster.local") -h, --help help for config --install-disk string the disk to install to (default "/dev/sda") --install-image string the image used to perform an installation (default "ghcr.io/siderolabs/installer:latest") --kubernetes-version string desired kubernetes version to run (default "1.36.0-alpha.2") -o, --output string destination to output generated files. when multiple output types are specified, it must be a directory. for a single output type, it must either be a file path, or "-" for stdout -t, --output-types strings types of outputs to be generated. valid types are: ["controlplane" "worker" "talosconfig"] (default [controlplane,worker,talosconfig]) --registry-mirror strings list of registry mirrors to use in format: = --talos-version string the desired Talos version to generate config for (backwards compatibility, e.g. v0.8) --version string the desired machine config version to generate (default "v1alpha1") --with-cluster-discovery enable cluster discovery feature (default true) --with-docs renders all machine configs adding the documentation for each field (default true) --with-examples renders all machine configs with the commented examples (default true) --with-kubespan enable KubeSpan feature --with-secrets string use a secrets file generated using 'gen secrets' ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen crt Generates an X.509 Ed25519 certificate ``` talosctl gen crt [flags] ``` ### Options ``` --ca string path to the PEM encoded CERTIFICATE --csr string path to the PEM encoded CERTIFICATE REQUEST -h, --help help for crt --hours int the hours from now on which the certificate validity period ends (default 24) --name string the basename of the generated file ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen csr Generates a CSR using an Ed25519 private key ``` talosctl gen csr [flags] ``` ### Options ``` -h, --help help for csr --ip string generate the certificate for this IP address --key string path to the PEM encoded EC or RSA PRIVATE KEY --roles strings roles (default [os:admin]) ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen key Generates an Ed25519 private key ``` talosctl gen key [flags] ``` ### Options ``` -h, --help help for key --name string the basename of the generated file ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen keypair Generates an X.509 Ed25519 key pair ``` talosctl gen keypair [flags] ``` ### Options ``` -h, --help help for keypair --ip string generate the certificate for this IP address --organization string X.509 distinguished name for the Organization ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen secrets Generates a secrets bundle file which can later be used to generate a config ``` talosctl gen secrets [flags] ``` ### Options ``` --from-controlplane-config string use the provided controlplane Talos machine configuration as input -p, --from-kubernetes-pki string use a Kubernetes PKI directory (e.g. /etc/kubernetes/pki) as input -h, --help help for secrets -t, --kubernetes-bootstrap-token string use the provided bootstrap token as input -o, --output-file string path of the output file, or "-" for stdout (default "secrets.yaml") --talos-version string the desired Talos version to generate secrets bundle for (backwards compatibility, e.g. v0.8) ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys ## talosctl gen secureboot database Generates a UEFI database to enroll the signing certificate ``` talosctl gen secureboot database [flags] ``` ### Options ``` --enrolled-certificate string path to the certificate to enroll (default "_out/uki-signing-cert.pem") -h, --help help for database --include-well-known-uefi-certs include well-known UEFI (Microsoft) certificates in the database --signing-certificate string path to the certificate used to sign the database (default "_out/uki-signing-cert.pem") --signing-key string path to the key used to sign the database (default "_out/uki-signing-key.pem") ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files -o, --output string path to the directory storing the generated files (default "_out") ``` ### SEE ALSO * [talosctl gen secureboot](#talosctl-gen-secureboot) - Generates secrets for the SecureBoot process ## talosctl gen secureboot pcr Generates a key which is used to sign TPM PCR values ``` talosctl gen secureboot pcr [flags] ``` ### Options ``` -h, --help help for pcr ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files -o, --output string path to the directory storing the generated files (default "_out") ``` ### SEE ALSO * [talosctl gen secureboot](#talosctl-gen-secureboot) - Generates secrets for the SecureBoot process ## talosctl gen secureboot uki Generates a certificate which is used to sign boot assets (UKI) ``` talosctl gen secureboot uki [flags] ``` ### Options ``` --common-name string common name for the certificate (default "Test UKI Signing Key") -h, --help help for uki ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files -o, --output string path to the directory storing the generated files (default "_out") ``` ### SEE ALSO * [talosctl gen secureboot](#talosctl-gen-secureboot) - Generates secrets for the SecureBoot process ## talosctl gen secureboot Generates secrets for the SecureBoot process ### Options ``` -h, --help help for secureboot -o, --output string path to the directory storing the generated files (default "_out") ``` ### Options inherited from parent commands ``` -f, --force will overwrite existing files ``` ### SEE ALSO * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys * [talosctl gen secureboot database](#talosctl-gen-secureboot-database) - Generates a UEFI database to enroll the signing certificate * [talosctl gen secureboot pcr](#talosctl-gen-secureboot-pcr) - Generates a key which is used to sign TPM PCR values * [talosctl gen secureboot uki](#talosctl-gen-secureboot-uki) - Generates a certificate which is used to sign boot assets (UKI) ## talosctl gen Generate CAs, certificates, and private keys ### Options ``` -f, --force will overwrite existing files -h, --help help for gen ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl gen ca](#talosctl-gen-ca) - Generates a self-signed X.509 certificate authority * [talosctl gen config](#talosctl-gen-config) - Generates a set of configuration files for Talos cluster * [talosctl gen crt](#talosctl-gen-crt) - Generates an X.509 Ed25519 certificate * [talosctl gen csr](#talosctl-gen-csr) - Generates a CSR using an Ed25519 private key * [talosctl gen key](#talosctl-gen-key) - Generates an Ed25519 private key * [talosctl gen keypair](#talosctl-gen-keypair) - Generates an X.509 Ed25519 key pair * [talosctl gen secrets](#talosctl-gen-secrets) - Generates a secrets bundle file which can later be used to generate a config * [talosctl gen secureboot](#talosctl-gen-secureboot) - Generates secrets for the SecureBoot process ## talosctl get Get a specific resource or list of resources (use 'talosctl get rd' to see all available resource types). ### Synopsis Similar to 'kubectl get', 'talosctl get' returns a set of resources from the OS. To get a list of all available resource definitions, issue 'talosctl get rd' ``` talosctl get [] [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for get -i, --insecure get resources using the insecure (encrypted with no auth) maintenance service --namespace string resource namespace (default is to use default namespace per resource) -n, --nodes strings target the specified nodes -o, --output string output mode (json, table, yaml, jsonpath) (default "table") --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -w, --watch watch resource changes ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl health Check cluster health ``` talosctl health [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --control-plane-nodes strings specify IPs of control plane nodes -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for health --init-node string specify IPs of init node --k8s-endpoint string use endpoint instead of kubeconfig default -n, --nodes strings target the specified nodes --run-e2e run Kubernetes e2e test --server run server-side check (default true) --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --wait-timeout duration timeout to wait for the cluster to be ready (default 20m0s) --worker-nodes strings specify IPs of worker nodes ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl image cache-cert-gen Generate TLS certificates and CA patch required for securing image cache to Talos communication ### Synopsis Generate TLS certificates and CA patch required for securing image cache to Talos communication ``` talosctl image cache-cert-gen [flags] ``` ### Options ``` --advertised-address ipSlice The addresses to advertise. (default []) --advertised-name strings The DNS names to advertise. -h, --help help for cache-cert-gen --tls-ca-file string TLS certificate authority file (default "ca.crt") --tls-cert-file string TLS certificate file to use for serving (default "tls.crt") --tls-key-file string TLS key file to use for serving (default "tls.key") ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image cache-create Create a cache of images in OCI format into a directory ### Synopsis Create a cache of images in OCI format into a directory ``` talosctl image cache-create [flags] ``` ### Examples ``` talosctl images cache-create --images=ghcr.io/siderolabs/kubelet:v1.36.0-alpha.2 --image-cache-path=/tmp/talos-image-cache Alternatively, stdin can be piped to the command: talosctl images default | talosctl images cache-create --image-cache-path=/tmp/talos-image-cache --images=- ``` ### Options ``` --cosign-signatures pull and cache cosign signatures for images (default true) --force force overwrite of existing image cache -h, --help help for cache-create --image-cache-path string directory to save the image cache in OCI format --image-layer-cache-path string directory to save the image layer cache --images strings images to cache --insecure allow insecure registries --layout string Specifies the cache layout format: "oci" for an OCI image layout directory, or "flat" for a registry-like flat file structure (default "oci") --platform strings platform to use for the cache (default [linux/amd64]) ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image cache-serve Serve an OCI image cache directory over HTTP(S) as a container registry ### Synopsis Serve an OCI image cache directory over HTTP(S) as a container registry ``` talosctl image cache-serve [flags] ``` ### Options ``` --address string address to serve the registry on (default "127.0.0.1:3172") -h, --help help for cache-serve --image-cache-path string directory to save the image cache in flat format --mirror strings list of registry mirrors to add to the Talos config patch (default [docker.io,ghcr.io,registry.k8s.io]) --tls-cert-file string TLS certificate file to use for serving --tls-key-file string TLS key file to use for serving ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image k8s-bundle List the default Kubernetes images used by Talos ``` talosctl image k8s-bundle [flags] ``` ### Options ``` --coredns-version semver CoreDNS semantic version (default v1.14.2) --etcd-version semver ETCD semantic version (default v3.6.8) --flannel-version semver Flannel CNI semantic version (default v0.28.1) -h, --help help for k8s-bundle --k8s-version semver Kubernetes semantic version (default v1.36.0-alpha.2) --kube-network-policies-version semver kube-network-policies semantic version (default v1.0.0) ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image list List images in the machine's container runtime ``` talosctl image list [flags] ``` ### Options ``` -h, --help help for list ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image pull Pull an image into the machine's container runtime ``` talosctl image pull [flags] ``` ### Options ``` -h, --help help for pull ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image remove Remove an image from the machine's container runtime ``` talosctl image remove [flags] ``` ### Options ``` -h, --help help for remove ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image talos-bundle List the default system images and extensions used for Talos ``` talosctl image talos-bundle [talos-version] [flags] ``` ### Options ``` --extensions Include images that belong to Talos extensions (default true) -h, --help help for talos-bundle --overlays Include images that belong to Talos overlays (default true) ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl image](#talosctl-image) - Manage container images ## talosctl image Manage container images ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for image --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "cri") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl image cache-cert-gen](#talosctl-image-cache-cert-gen) - Generate TLS certificates and CA patch required for securing image cache to Talos communication * [talosctl image cache-create](#talosctl-image-cache-create) - Create a cache of images in OCI format into a directory * [talosctl image cache-serve](#talosctl-image-cache-serve) - Serve an OCI image cache directory over HTTP(S) as a container registry * [talosctl image k8s-bundle](#talosctl-image-k8s-bundle) - List the default Kubernetes images used by Talos * [talosctl image list](#talosctl-image-list) - List images in the machine's container runtime * [talosctl image pull](#talosctl-image-pull) - Pull an image into the machine's container runtime * [talosctl image remove](#talosctl-image-remove) - Remove an image from the machine's container runtime * [talosctl image talos-bundle](#talosctl-image-talos-bundle) - List the default system images and extensions used for Talos ## talosctl inject serviceaccount Inject Talos API ServiceAccount into Kubernetes manifests ``` talosctl inject serviceaccount [--roles=','] -f [flags] ``` ### Examples ``` talosctl inject serviceaccount --roles="os:admin" -f deployment.yaml > deployment-injected.yaml Alternatively, stdin can be piped to the command: cat deployment.yaml | talosctl inject serviceaccount --roles="os:admin" -f - > deployment-injected.yaml ``` ### Options ``` -f, --file string file with Kubernetes manifests to be injected with ServiceAccount -h, --help help for serviceaccount -r, --roles strings roles to add to the generated ServiceAccount manifests (default [os:reader]) ``` ### SEE ALSO * [talosctl inject](#talosctl-inject) - Inject Talos API resources into Kubernetes manifests ## talosctl inject Inject Talos API resources into Kubernetes manifests ### Options ``` -h, --help help for inject ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl inject serviceaccount](#talosctl-inject-serviceaccount) - Inject Talos API ServiceAccount into Kubernetes manifests ## talosctl inspect dependencies Inspect controller-resource dependencies as graphviz graph. ### Synopsis Inspect controller-resource dependencies as graphviz graph. Pipe the output of the command through the "dot" program (part of graphviz package) to render the graph: talosctl inspect dependencies | dot -Tpng > graph.png ``` talosctl inspect dependencies [flags] ``` ### Options ``` -h, --help help for dependencies --with-resources display live resource information with dependencies ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl inspect](#talosctl-inspect) - Inspect internals of Talos ## talosctl inspect Inspect internals of Talos ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for inspect -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl inspect dependencies](#talosctl-inspect-dependencies) - Inspect controller-resource dependencies as graphviz graph. ## talosctl kubeconfig Download the admin kubeconfig from the node ### Synopsis Download the admin kubeconfig from the node. If merge flag is true, config will be merged with ~/.kube/config or [local-path] if specified. Otherwise, kubeconfig will be written to PWD or [local-path] if specified. If merge flag is false and [local-path] is "-", config will be written to stdout. ``` talosctl kubeconfig [local-path] [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -f, --force Force overwrite of kubeconfig if already present, force overwrite on kubeconfig merge --force-context-name string Force context name for kubeconfig merge -h, --help help for kubeconfig -m, --merge Merge with existing kubeconfig (default true) -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl list Retrieve a directory listing ``` talosctl list [path] [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -d, --depth int32 maximum recursion depth (default 1) -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for list -H, --humanize humanize size and time in the output -l, --long display additional file details -n, --nodes strings target the specified nodes -r, --recurse recurse into subdirectories --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -t, --type strings filter by specified types: f regular file d directory l, L symbolic link ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl logs Retrieve logs for a service ``` talosctl logs [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -f, --follow specify if the logs should be streamed -h, --help help for logs -k, --kubernetes use the k8s.io containerd namespace -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --tail int32 lines of log file to display (default is to show from the beginning) (default -1) --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl machineconfig gen Generates a set of configuration files for Talos cluster ### Synopsis The cluster endpoint is the URL for the Kubernetes API. If you decide to use a control plane node, common in a single node control plane setup, use port 6443 as this is the port that the API server binds to on every control plane node. For an HA setup, usually involving a load balancer, use the IP and port of the load balancer. ``` talosctl machineconfig gen [flags] ``` ### Options ``` -h, --help help for gen ``` ### SEE ALSO * [talosctl machineconfig](#talosctl-machineconfig) - Machine config related commands ## talosctl machineconfig patch Patch a machine config ``` talosctl machineconfig patch [flags] ``` ### Options ``` -h, --help help for patch -o, --output string output destination. if not specified, output will be printed to stdout -p, --patch stringArray patch generated machineconfigs (applied to all node types), use @file to read a patch from file ``` ### SEE ALSO * [talosctl machineconfig](#talosctl-machineconfig) - Machine config related commands ## talosctl machineconfig Machine config related commands ### Options ``` -h, --help help for machineconfig ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl machineconfig gen](#talosctl-machineconfig-gen) - Generates a set of configuration files for Talos cluster * [talosctl machineconfig patch](#talosctl-machineconfig-patch) - Patch a machine config ## talosctl memory Show memory usage ``` talosctl memory [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for memory -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -v, --verbose display extended memory statistics ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl meta delete Delete a key from the META partition. ``` talosctl meta delete key [flags] ``` ### Options ``` -h, --help help for delete ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -i, --insecure write|delete meta using the insecure (encrypted with no auth) maintenance service -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl meta](#talosctl-meta) - Write and delete keys in the META partition ## talosctl meta write Write a key-value pair to the META partition. ``` talosctl meta write key value [flags] ``` ### Options ``` -h, --help help for write ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -i, --insecure write|delete meta using the insecure (encrypted with no auth) maintenance service -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl meta](#talosctl-meta) - Write and delete keys in the META partition ## talosctl meta Write and delete keys in the META partition ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for meta -i, --insecure write|delete meta using the insecure (encrypted with no auth) maintenance service -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl meta delete](#talosctl-meta-delete) - Delete a key from the META partition. * [talosctl meta write](#talosctl-meta-write) - Write a key-value pair to the META partition. ## talosctl mounts List mounts ``` talosctl mounts [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for mounts -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl netstat Show network connections and sockets ### Synopsis Show network connections and sockets. You can pass an optional argument to view a specific pod's connections. To do this, format the argument as "namespace/pod". Note that only pods with a pod network namespace are allowed. If you don't pass an argument, the command will show host connections. ``` talosctl netstat [flags] ``` ### Options ``` -a, --all display all sockets states (default: connected) -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -x, --extend show detailed socket information -h, --help help for netstat -4, --ipv4 display only ipv4 sockets -6, --ipv6 display only ipv6 sockets -l, --listening display listening server sockets -n, --nodes strings target the specified nodes -k, --pods show sockets used by Kubernetes pods -p, --programs show process using socket -w, --raw display only RAW sockets --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -t, --tcp display only TCP sockets -o, --timers display timers -u, --udp display only UDP sockets -U, --udplite display only UDPLite sockets -v, --verbose display sockets of all supported transport protocols ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl patch Patch machine configuration of a Talos node with a local patch. ``` talosctl patch machineconfig [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --dry-run print the change summary and patch preview without applying the changes -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for patch -m, --mode auto, no-reboot, reboot, staged, try apply config mode (default auto) --namespace string resource namespace (default is to use default namespace per resource) -n, --nodes strings target the specified nodes -p, --patch stringArray the patch to be applied to the resource file, use @file to read a patch from file. --patch-file string a file containing a patch to be applied to the resource. --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration the config will be rolled back after specified timeout (if try mode is selected) (default 1m0s) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl pcap Capture the network packets from the node. ### Synopsis The command launches packet capture on the node and streams back the packets as raw pcap file. ``` talosctl pcap [flags] ``` ### Examples ``` Default behavior is to decode the packets with internal decoder to stdout: talosctl pcap -i eth0 Raw pcap file can be saved with `--output` flag: talosctl pcap -i eth0 --output eth0.pcap Output can be piped to tcpdump: talosctl pcap -i eth0 -o - | tcpdump -vvv -r - BPF filter can be applied, but it has to compiled to BPF instructions first using tcpdump. Correct link type should be specified for the tcpdump: EN10MB for Ethernet links and RAW for e.g. Wireguard tunnels: talosctl pcap -i eth0 --bpf-filter "$(tcpdump -dd -y EN10MB 'tcp and dst port 80')" talosctl pcap -i kubespan --bpf-filter "$(tcpdump -dd -y RAW 'port 50000')" As packet capture is transmitted over the network, it is recommended to filter out the Talos API traffic, e.g. by excluding packets with the port 50000. ``` ### Options ``` --bpf-filter string bpf filter to apply, tcpdump -dd format -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --duration duration duration of the capture -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for pcap -i, --interface string interface name to capture packets on (default "eth0") -n, --nodes strings target the specified nodes -o, --output string if not set, decode packets to stdout; if set write raw pcap data to a file, use '-' for stdout --promiscuous put interface into promiscuous mode --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl processes List running processes ``` talosctl processes [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for processes -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. -s, --sort string Column to sort output by. [rss|cpu] (default "rss") --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl read Read a file on the machine ``` talosctl read [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for read -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl reboot Reboot a node ``` talosctl reboot [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --debug debug operation from kernel logs. --wait is set to true when this flag is set -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for reboot -m, --mode string select the reboot mode: "default", "powercycle" (skips kexec), "force" (skips graceful teardown) (default "default") -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s) --wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl reset Reset a node ``` talosctl reset [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --debug debug operation from kernel logs. --wait is set to true when this flag is set -e, --endpoints strings override default endpoints in Talos configuration --graceful if true, attempt to cordon/drain node and leave etcd (if applicable) (default true) -h, --help help for reset --insecure reset using the insecure (encrypted with no auth) maintenance service -n, --nodes strings target the specified nodes --reboot if true, reboot the node after resetting instead of shutting down --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --system-labels-to-wipe strings if set, just wipe selected system disk partitions by label but keep other partitions intact --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s) --user-disks-to-wipe strings if set, wipes defined devices in the list --wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true) --wipe-mode all, system-disk, user-disks disk reset mode (default all) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl restart Restart a process ``` talosctl restart [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for restart -k, --kubernetes use the k8s.io containerd namespace -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl rollback Rollback a node to the previous installation ``` talosctl rollback [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for rollback -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl rotate-ca Rotate cluster CAs (Talos and Kubernetes APIs). ### Synopsis The command can rotate both Talos and Kubernetes root CAs (for the API). By default both CAs are rotated, but you can choose to rotate just one or another. The command starts by generating new CAs, and gracefully applying it to the cluster. For Kubernetes, the command only rotates the API server issuing CA, and other Kubernetes PKI can be rotated by applying machine config changes to the controlplane nodes. ``` talosctl rotate-ca [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --control-plane-nodes strings specify IPs of control plane nodes --dry-run dry-run mode (no changes to the cluster) (default true) -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for rotate-ca --init-node string specify IPs of init node --k8s-endpoint string use endpoint instead of kubeconfig default --kubernetes rotate Kubernetes API CA (default true) -n, --nodes strings target the specified nodes -o, --output talosconfig path to the output new talosconfig (default "talosconfig") --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talos rotate Talos API CA (default true) --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --with-docs patch all machine configs adding the documentation for each field (default true) --with-examples patch all machine configs with the commented examples (default true) --worker-nodes strings specify IPs of worker nodes ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl service Retrieve the state of a service (or all services), control service state ### Synopsis Service control command. If run without arguments, lists all the services and their state. If service ID is specified, default action 'status' is executed which shows status of a single list service. With actions 'start', 'stop', 'restart', service state is updated respectively. ``` talosctl service [ [start|stop|restart|status]] [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for service -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl shutdown Shutdown a node ``` talosctl shutdown [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --debug debug operation from kernel logs. --wait is set to true when this flag is set -e, --endpoints strings override default endpoints in Talos configuration --force if true, force a node to shutdown without a cordon/drain -h, --help help for shutdown -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s) --wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl stats Get container stats ``` talosctl stats [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for stats -k, --kubernetes use the k8s.io containerd namespace -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl support Dump debug information about the cluster ### Synopsis Generated bundle contains the following debug information: - For each node: - Kernel logs. - All Talos internal services logs. - All kube-system pods logs. - Talos COSI resources without secrets. - COSI runtime state graph. - Processes snapshot. - IO pressure snapshot. - Mounts list. - PCI devices info. - Talos version. - For the cluster: - Kubernetes nodes and kube-system pods manifests. ``` talosctl support [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for support -n, --nodes strings target the specified nodes -w, --num-workers int number of workers per node (default 1) -O, --output string output file to write support archive to --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -v, --verbose verbose output ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl time Gets current server time ``` talosctl time [--check server] [flags] ``` ### Options ``` --check string checks server time against specified ntp server -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for time -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl upgrade Upgrade Talos on the target node ``` talosctl upgrade [flags] ``` ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --debug debug operation from kernel logs. --wait is set to true when this flag is set -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for upgrade -i, --image string the container image to use for performing the install (default "ghcr.io/siderolabs/installer:v1.13.0-alpha.2") --namespace string namespace to use: "system" (etcd and kubelet images), "cri" for all Kubernetes workloads, "inmem" for in-memory containerd instance (default "system") -n, --nodes strings target the specified nodes --progress string output mode for upgrade progress. Values: [auto plain] (default "auto") -m, --reboot-mode string select the reboot mode during upgrade. Mode "powercycle" bypasses kexec. Values: [default force powercycle] (default "default") --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --timeout duration time to wait for the operation is complete if --debug or --wait is set (default 30m0s) --wait wait for the operation to complete, tracking its progress. always set to true when --debug is set (default true) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl upgrade-k8s Upgrade Kubernetes control plane in the Talos cluster. ### Synopsis Command runs upgrade of Kubernetes control plane components between specified versions. ``` talosctl upgrade-k8s [flags] ``` ### Options ``` --apiserver-image string kube-apiserver image to use (default "registry.k8s.io/kube-apiserver") -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command --controller-manager-image string kube-controller-manager image to use (default "registry.k8s.io/kube-controller-manager") --dry-run skip the actual upgrade and show the upgrade plan instead --endpoint string the cluster control plane endpoint -e, --endpoints strings override default endpoints in Talos configuration --from string the Kubernetes control plane version to upgrade from -h, --help help for upgrade-k8s --kubelet-image string kubelet image to use (default "ghcr.io/siderolabs/kubelet") --manifests-force whether to recreate objects that contain immutable field changes --manifests-inventory-policy string kubernetes SSA inventory policy (one of 'MustMatch', 'AdoptIfNoInventory' or 'AdoptAll') (default "AdoptIfNoInventory") --manifests-no-prune whether pruning of previously applied objects should happen after apply --manifests-reconcile-timeout duration how long to wait for resources to be fully reconciled (set to zero to disable waiting) (default 5m0s) -n, --nodes strings target the specified nodes --pre-pull-images pre-pull images before upgrade (default true) --proxy-image string kube-proxy image to use (default "registry.k8s.io/kube-proxy") --scheduler-image string kube-scheduler image to use (default "registry.k8s.io/kube-scheduler") --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. --to string the Kubernetes control plane version to upgrade to (default "1.36.0-alpha.2") --upgrade-kubelet upgrade kubelet service (default true) --with-docs patch all machine configs adding the documentation for each field (default true) --with-examples patch all machine configs with the commented examples (default true) ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl usage Retrieve a disk usage ``` talosctl usage [path1] [path2] ... [pathN] [flags] ``` ### Options ``` -a, --all write counts for all files, not just directories -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -d, --depth int32 maximum recursion depth -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for usage -H, --humanize humanize size and time in the output -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. -t, --threshold int threshold exclude entries smaller than SIZE if positive, or entries greater than SIZE if negative ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl validate Validate config ``` talosctl validate [flags] ``` ### Options ``` -c, --config string the path of the config file -h, --help help for validate -m, --mode string the mode to validate the config for (valid values are metal, cloud, and container) --strict treat validation warnings as errors ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl version Prints the version ``` talosctl version [flags] ``` ### Options ``` --client Print client version only -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for version -i, --insecure use Talos maintenance mode API -n, --nodes strings target the specified nodes --short Print the short version --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos ## talosctl wipe disk Wipe a block device (disk or partition) which is not used as a volume ### Synopsis Wipe a block device (disk or partition) which is not used as a volume. Use device names as arguments, for example: vda or sda5. ``` talosctl wipe disk ... [flags] ``` ### Options ``` --drop-partition drop partition after wipe (if applicable) -h, --help help for disk -i, --insecure use Talos maintenance mode API --method string wipe method to use [FAST ZEROES] (default "FAST") ``` ### Options inherited from parent commands ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl wipe](#talosctl-wipe) - Wipe block device or volumes ## talosctl wipe Wipe block device or volumes ### Options ``` -c, --cluster string Cluster to connect to if a proxy endpoint is used. --context string Context to be used in command -e, --endpoints strings override default endpoints in Talos configuration -h, --help help for wipe -n, --nodes strings target the specified nodes --siderov1-keys-dir string The path to the SideroV1 auth PGP keys directory. Defaults to 'SIDEROV1_KEYS_DIR' env variable if set, otherwise '$HOME/.talos/keys'. Only valid for Contexts that use SideroV1 auth. --talosconfig string The path to the Talos configuration file. Defaults to 'TALOSCONFIG' env variable if set, otherwise '$HOME/.talos/config' and '/var/run/secrets/talos.dev/config' in order. ``` ### SEE ALSO * [talosctl](#talosctl) - A CLI for out-of-band management of Kubernetes nodes created by Talos * [talosctl wipe disk](#talosctl-wipe-disk) - Wipe a block device (disk or partition) which is not used as a volume ## talosctl A CLI for out-of-band management of Kubernetes nodes created by Talos ### Options ``` -h, --help help for talosctl ``` ### SEE ALSO * [talosctl apply-config](#talosctl-apply-config) - Apply a new configuration to a node * [talosctl bootstrap](#talosctl-bootstrap) - Bootstrap the etcd cluster on the specified node. * [talosctl cgroups](#talosctl-cgroups) - Retrieve cgroups usage information * [talosctl cluster](#talosctl-cluster) - A collection of commands for managing local docker-based or QEMU-based clusters * [talosctl completion](#talosctl-completion) - Output shell completion code for the specified shell (bash, fish or zsh) * [talosctl config](#talosctl-config) - Manage the client configuration file (talosconfig) * [talosctl conformance](#talosctl-conformance) - Run conformance tests * [talosctl containers](#talosctl-containers) - List containers * [talosctl copy](#talosctl-copy) - Copy data out from the node * [talosctl dashboard](#talosctl-dashboard) - Cluster dashboard with node overview, logs and real-time metrics * [talosctl debug](#talosctl-debug) - Run a debug container from an image archive or reference * [talosctl dmesg](#talosctl-dmesg) - Retrieve kernel logs * [talosctl edit](#talosctl-edit) - Edit Talos node machine configuration with the default editor. * [talosctl etcd](#talosctl-etcd) - Manage etcd * [talosctl events](#talosctl-events) - Stream runtime events * [talosctl gen](#talosctl-gen) - Generate CAs, certificates, and private keys * [talosctl get](#talosctl-get) - Get a specific resource or list of resources (use 'talosctl get rd' to see all available resource types). * [talosctl health](#talosctl-health) - Check cluster health * [talosctl image](#talosctl-image) - Manage container images * [talosctl inject](#talosctl-inject) - Inject Talos API resources into Kubernetes manifests * [talosctl inspect](#talosctl-inspect) - Inspect internals of Talos * [talosctl kubeconfig](#talosctl-kubeconfig) - Download the admin kubeconfig from the node * [talosctl list](#talosctl-list) - Retrieve a directory listing * [talosctl logs](#talosctl-logs) - Retrieve logs for a service * [talosctl machineconfig](#talosctl-machineconfig) - Machine config related commands * [talosctl memory](#talosctl-memory) - Show memory usage * [talosctl meta](#talosctl-meta) - Write and delete keys in the META partition * [talosctl mounts](#talosctl-mounts) - List mounts * [talosctl netstat](#talosctl-netstat) - Show network connections and sockets * [talosctl patch](#talosctl-patch) - Patch machine configuration of a Talos node with a local patch. * [talosctl pcap](#talosctl-pcap) - Capture the network packets from the node. * [talosctl processes](#talosctl-processes) - List running processes * [talosctl read](#talosctl-read) - Read a file on the machine * [talosctl reboot](#talosctl-reboot) - Reboot a node * [talosctl reset](#talosctl-reset) - Reset a node * [talosctl restart](#talosctl-restart) - Restart a process * [talosctl rollback](#talosctl-rollback) - Rollback a node to the previous installation * [talosctl rotate-ca](#talosctl-rotate-ca) - Rotate cluster CAs (Talos and Kubernetes APIs). * [talosctl service](#talosctl-service) - Retrieve the state of a service (or all services), control service state * [talosctl shutdown](#talosctl-shutdown) - Shutdown a node * [talosctl stats](#talosctl-stats) - Get container stats * [talosctl support](#talosctl-support) - Dump debug information about the cluster * [talosctl time](#talosctl-time) - Gets current server time * [talosctl upgrade](#talosctl-upgrade) - Upgrade Talos on the target node * [talosctl upgrade-k8s](#talosctl-upgrade-k8s) - Upgrade Kubernetes control plane in the Talos cluster. * [talosctl usage](#talosctl-usage) - Retrieve a disk usage * [talosctl validate](#talosctl-validate) - Validate config * [talosctl version](#talosctl-version) - Prints the version * [talosctl wipe](#talosctl-wipe) - Wipe block device or volumes ================================================ FILE: website/content/v1.13/reference/configuration/_index.md ================================================ --- title: Configuration description: Talos Linux machine configuration reference. --- Talos Linux machine is fully configured via a single YAML file called *machine configuration*. The file might contain one or more configuration documents separated by `---` (three dashes) lines. At the moment, majority of the configuration options are within the [v1alpha1]({{< relref "./v1alpha1" >}}) document, so this is the only mandatory document in the configuration file. Configuration documents might be named (contain a `name:` field) or unnamed. Unnamed documents can be supplied to the machine configuration file only once, while named documents can be supplied multiple times with unique names. The `v1alpha1` document has its own (legacy) structure, while every other document has the following set of fields: ```yaml apiVersion: v1alpha1 # version of the document kind: NetworkRuleConfig # type of document name: rule1 # only for named documents ``` This section contains the configuration reference, to learn more about Talos Linux machine configuration management, please see: * [quick guide to configuration generation]({{< relref "../../introduction/getting-started#configure-talos-linux" >}}) * [configuration management in production]({{< relref "../../introduction/prodnotes#configure-talos" >}}) * [configuration patches]({{< relref "../../talos-guides/configuration/patching" >}}) * [editing live machine configuration]({{< relref "../../talos-guides/configuration/editing-machine-configuration" >}}) ================================================ FILE: website/content/v1.13/reference/configuration/block/_index.md ================================================ --- description: | Package block provides block device and volume configuration documents. title: block --- ================================================ FILE: website/content/v1.13/reference/configuration/block/existingvolumeconfig.md ================================================ --- description: | ExistingVolumeConfig is an existing volume configuration document. Existing volumes allow to mount partitions (or whole disks) that were created outside of Talos. Volume will be mounted under `/var/mnt/`. The existing volume config name should not conflict with user volume names. title: ExistingVolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ExistingVolumeConfig name: my-existing-volume # Name of the volume. # The discovery describes how to find a volume. discovery: # The volume selector expression. volumeSelector: match: volume.partition_label == "MY-DATA" # The Common Expression Language (CEL) expression to match the volume. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the volume.

Name can only contain:
lowercase and uppercase ASCII letters, digits, and hyphens. | | |`discovery` |
VolumeDiscoverySpec |The discovery describes how to find a volume. | | |`mount` |ExistingMountSpec |The mount describes additional mount options. | | ## discovery {#ExistingVolumeConfig.discovery} VolumeDiscoverySpec describes how the volume is discovered. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`volumeSelector` |VolumeSelector |The volume selector expression. | | ### volumeSelector {#ExistingVolumeConfig.discovery.volumeSelector} VolumeSelector selects an existing volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the volume.
Show example(s)match volumes with partition label MY-DATA:{{< highlight yaml >}} match: volume.partition_label == "MY-DATA" {{< /highlight >}}match xfs volume on disk with serial 'SERIAL123':{{< highlight yaml >}} match: volume.name == "xfs" && disk.serial == "SERIAL123" {{< /highlight >}}
| | ## mount {#ExistingVolumeConfig.mount} ExistingMountSpec describes how the volume is mounted. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`readOnly` |bool |Mount the volume read-only. | | |`disableAccessTime` |bool |If true, disable file access time updates. | | |`secure` |bool |Enable secure mount options (nosuid, nodev).

Defaults to true for better security. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/externalvolumeconfig.md ================================================ --- description: | ExternalVolumeConfig is an external disk mount configuration document. External volumes allow to mount volumes that were created outside of Talos, over the network or API. Volume will be mounted under `/var/mnt/`. The external volume config name should not conflict with user volume names. title: ExternalVolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ExternalVolumeConfig name: mount1 # Name of the mount. filesystemType: virtiofs # Filesystem type. # The mount describes additional mount options. mount: # Virtiofs mount options. virtiofs: tag: Data # Selector tag for the Virtiofs mount. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the mount.

Name might be between 1 and 34 characters long and can only contain:
lowercase and uppercase ASCII letters, digits, and hyphens. | | |`filesystemType` |FilesystemType |Filesystem type. |`virtiofs`
`nfs`
| |`mount` |ExternalMountSpec |The mount describes additional mount options. | | ## mount {#ExternalVolumeConfig.mount} ExternalMountSpec describes how the external volume is mounted. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`readOnly` |bool |Mount the volume read-only. | | |`disableAccessTime` |bool |If true, disable file access time updates. | | |`secure` |bool |Enable secure mount options (nosuid, nodev).

Defaults to true for better security. | | |`virtiofs` |VirtiofsMountSpec |Virtiofs mount options. | | ### virtiofs {#ExternalVolumeConfig.mount.virtiofs} VirtiofsMountSpec describes Virtiofs mount options. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`tag` |string |Selector tag for the Virtiofs mount. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/rawvolumeconfig.md ================================================ --- description: | RawVolumeConfig is a raw volume configuration document. Raw volumes allow to create partitions without formatting them. If you want to use local storage, user volumes is a better choice, raw volumes are intended to be used with CSI provisioners. The partition label is automatically generated as `r-`. title: RawVolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: RawVolumeConfig name: local-data # Name of the volume. # The provisioning describes how the volume is provisioned. provisioning: # The disk selector expression. diskSelector: match: disk.transport == "nvme" # The Common Expression Language (CEL) expression to match the disk. maxSize: 50GiB # The maximum size of the volume, if not specified the volume can grow to the size of the # # The minimum size of the volume. # minSize: 2.5GiB # # The encryption describes how the volume is encrypted. # encryption: # provider: luks2 # Encryption provider to use for the encryption. # # Defines the encryption keys generation and storage method. # keys: # - slot: 0 # Key slot number for LUKS2 encryption. # # Key which value is stored in the configuration file. # static: # passphrase: exampleKey # Defines the static passphrase value. # # # # KMS managed encryption key. # # kms: # # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # - slot: 1 # Key slot number for LUKS2 encryption. # # KMS managed encryption key. # kms: # endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. # cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. # blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the volume.

Name might be between 1 and 34 characters long and can only contain:
lowercase and uppercase ASCII letters, digits, and hyphens. | | |`provisioning` |ProvisioningSpec |The provisioning describes how the volume is provisioned. | | |`encryption` |EncryptionSpec |The encryption describes how the volume is encrypted. | | ## provisioning {#RawVolumeConfig.provisioning} ProvisioningSpec describes how the volume is provisioned. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`diskSelector` |DiskSelector |The disk selector expression. | | |`grow` |bool |Should the volume grow to the size of the disk (if possible). | | |`minSize` |ByteSize |The minimum size of the volume.

Size is specified in bytes, but can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} minSize: 2.5GiB {{< /highlight >}}
| | |`maxSize` |Size |The maximum size of the volume, if not specified the volume can grow to the size of the
disk.

Size is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} maxSize: 50GiB {{< /highlight >}}{{< highlight yaml >}} maxSize: 80% {{< /highlight >}}
| | ### diskSelector {#RawVolumeConfig.provisioning.diskSelector} DiskSelector selects a disk for the volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the disk.
Show example(s)match disks with size between 120GB and 1TB:{{< highlight yaml >}} match: disk.size > 120u * GB && disk.size < 1u * TB {{< /highlight >}}match SATA disks that are not rotational and not system disks:{{< highlight yaml >}} match: disk.transport == "sata" && !disk.rotational && !system_disk {{< /highlight >}}
| | ## encryption {#RawVolumeConfig.encryption} EncryptionSpec represents volume encryption settings. {{< highlight yaml >}} encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: exampleKey # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # KMS managed encryption key. kms: endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`provider` |EncryptionProviderType |Encryption provider to use for the encryption. |`luks2`
| |`keys` |[]EncryptionKey |Defines the encryption keys generation and storage method. | | |`cipher` |string |Cipher to use for the encryption. Depends on the encryption provider.
Show example(s){{< highlight yaml >}} cipher: aes-xts-plain64 {{< /highlight >}}
|`aes-xts-plain64`
`xchacha12,aes-adiantum-plain64`
`xchacha20,aes-adiantum-plain64`
| |`keySize` |uint |Defines the encryption key length. | | |`blockSize` |uint64 |Defines the encryption sector size.
Show example(s){{< highlight yaml >}} blockSize: 4096 {{< /highlight >}}
| | |`options` |[]string |Additional --perf parameters for the LUKS2 encryption.
Show example(s){{< highlight yaml >}} options: - no_read_workqueue - no_write_workqueue {{< /highlight >}}
|`no_read_workqueue`
`no_write_workqueue`
`same_cpu_crypt`
| ### keys[] {#RawVolumeConfig.encryption.keys.} EncryptionKey represents configuration for disk encryption key. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`slot` |int |Key slot number for LUKS2 encryption. | | |`static` |EncryptionKeyStatic |Key which value is stored in the configuration file. | | |`nodeID` |EncryptionKeyNodeID |Deterministically generated key from the node UUID and PartitionLabel. | | |`kms` |EncryptionKeyKMS |KMS managed encryption key. | | |`tpm` |EncryptionKeyTPM |Enable TPM based disk encryption. | | |`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | | #### static {#RawVolumeConfig.encryption.keys..static} EncryptionKeyStatic represents throw away key type. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`passphrase` |string |Defines the static passphrase value. | | #### nodeID {#RawVolumeConfig.encryption.keys..nodeID} EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. #### kms {#RawVolumeConfig.encryption.keys..kms} EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. {{< highlight yaml >}} encryption: keys: - kms: endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |string |KMS endpoint to Seal/Unseal the key. | | #### tpm {#RawVolumeConfig.encryption.keys..tpm} EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`options` |EncryptionKeyTPMOptions |TPM options for key protection. | | |`checkSecurebootStatusOnEnroll` |bool |Check that Secureboot is enabled in the EFI firmware.
If Secureboot is not enabled, the enrollment of the key will fail. | | ##### options {#RawVolumeConfig.encryption.keys..tpm.options} EncryptionKeyTPMOptions represents the options for TPM-based key protection. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`pcrs` |[]int |List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/swapvolumeconfig.md ================================================ --- description: | SwapVolumeConfig is a disk swap volume configuration document. Swap volume is automatically allocated as a partition on the specified disk and activated as swap, removing a swap volume deactivates swap. The partition label is automatically generated as `s-`. title: SwapVolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: SwapVolumeConfig name: swap1 # Name of the volume. # The provisioning describes how the volume is provisioned. provisioning: # The disk selector expression. diskSelector: match: disk.transport == "nvme" # The Common Expression Language (CEL) expression to match the disk. minSize: 3GiB # The minimum size of the volume. maxSize: 4GiB # The maximum size of the volume, if not specified the volume can grow to the size of the # The encryption describes how the volume is encrypted. encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: swapsecret # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # # Cipher to use for the encryption. Depends on the encryption provider. # cipher: aes-xts-plain64 # # Defines the encryption sector size. # blockSize: 4096 # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the volume.

Name might be between 1 and 34 characters long and can only contain:
lowercase and uppercase ASCII letters, digits, and hyphens. | | |`provisioning` |ProvisioningSpec |The provisioning describes how the volume is provisioned. | | |`encryption` |EncryptionSpec |The encryption describes how the volume is encrypted. | | ## provisioning {#SwapVolumeConfig.provisioning} ProvisioningSpec describes how the volume is provisioned. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`diskSelector` |DiskSelector |The disk selector expression. | | |`grow` |bool |Should the volume grow to the size of the disk (if possible). | | |`minSize` |ByteSize |The minimum size of the volume.

Size is specified in bytes, but can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} minSize: 2.5GiB {{< /highlight >}}
| | |`maxSize` |Size |The maximum size of the volume, if not specified the volume can grow to the size of the
disk.

Size is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} maxSize: 50GiB {{< /highlight >}}{{< highlight yaml >}} maxSize: 80% {{< /highlight >}}
| | ### diskSelector {#SwapVolumeConfig.provisioning.diskSelector} DiskSelector selects a disk for the volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the disk.
Show example(s)match disks with size between 120GB and 1TB:{{< highlight yaml >}} match: disk.size > 120u * GB && disk.size < 1u * TB {{< /highlight >}}match SATA disks that are not rotational and not system disks:{{< highlight yaml >}} match: disk.transport == "sata" && !disk.rotational && !system_disk {{< /highlight >}}
| | ## encryption {#SwapVolumeConfig.encryption} EncryptionSpec represents volume encryption settings. {{< highlight yaml >}} encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: exampleKey # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # KMS managed encryption key. kms: endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`provider` |EncryptionProviderType |Encryption provider to use for the encryption. |`luks2`
| |`keys` |[]EncryptionKey |Defines the encryption keys generation and storage method. | | |`cipher` |string |Cipher to use for the encryption. Depends on the encryption provider.
Show example(s){{< highlight yaml >}} cipher: aes-xts-plain64 {{< /highlight >}}
|`aes-xts-plain64`
`xchacha12,aes-adiantum-plain64`
`xchacha20,aes-adiantum-plain64`
| |`keySize` |uint |Defines the encryption key length. | | |`blockSize` |uint64 |Defines the encryption sector size.
Show example(s){{< highlight yaml >}} blockSize: 4096 {{< /highlight >}}
| | |`options` |[]string |Additional --perf parameters for the LUKS2 encryption.
Show example(s){{< highlight yaml >}} options: - no_read_workqueue - no_write_workqueue {{< /highlight >}}
|`no_read_workqueue`
`no_write_workqueue`
`same_cpu_crypt`
| ### keys[] {#SwapVolumeConfig.encryption.keys.} EncryptionKey represents configuration for disk encryption key. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`slot` |int |Key slot number for LUKS2 encryption. | | |`static` |EncryptionKeyStatic |Key which value is stored in the configuration file. | | |`nodeID` |EncryptionKeyNodeID |Deterministically generated key from the node UUID and PartitionLabel. | | |`kms` |EncryptionKeyKMS |KMS managed encryption key. | | |`tpm` |EncryptionKeyTPM |Enable TPM based disk encryption. | | |`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | | #### static {#SwapVolumeConfig.encryption.keys..static} EncryptionKeyStatic represents throw away key type. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`passphrase` |string |Defines the static passphrase value. | | #### nodeID {#SwapVolumeConfig.encryption.keys..nodeID} EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. #### kms {#SwapVolumeConfig.encryption.keys..kms} EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. {{< highlight yaml >}} encryption: keys: - kms: endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |string |KMS endpoint to Seal/Unseal the key. | | #### tpm {#SwapVolumeConfig.encryption.keys..tpm} EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`options` |EncryptionKeyTPMOptions |TPM options for key protection. | | |`checkSecurebootStatusOnEnroll` |bool |Check that Secureboot is enabled in the EFI firmware.
If Secureboot is not enabled, the enrollment of the key will fail. | | ##### options {#SwapVolumeConfig.encryption.keys..tpm.options} EncryptionKeyTPMOptions represents the options for TPM-based key protection. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`pcrs` |[]int |List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/uservolumeconfig.md ================================================ --- description: | UserVolumeConfig is a user volume configuration document. User volume is automatically allocated as a partition on the specified disk and mounted under `/var/mnt/`. The partition label is automatically generated as `u-`. title: UserVolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: UserVolumeConfig name: local-data # Name of the volume. volumeType: directory # Volume type. # # The encryption describes how the volume is encrypted. # encryption: # provider: luks2 # Encryption provider to use for the encryption. # # Defines the encryption keys generation and storage method. # keys: # - slot: 0 # Key slot number for LUKS2 encryption. # # Key which value is stored in the configuration file. # static: # passphrase: exampleKey # Defines the static passphrase value. # # # # KMS managed encryption key. # # kms: # # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # - slot: 1 # Key slot number for LUKS2 encryption. # # KMS managed encryption key. # kms: # endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. # cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. # blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: UserVolumeConfig name: local-data # Name of the volume. volumeType: disk # Volume type. # The provisioning describes how the volume is provisioned. provisioning: # The disk selector expression. diskSelector: match: disk.transport == "nvme" # The Common Expression Language (CEL) expression to match the disk. # # The minimum size of the volume. # minSize: 2.5GiB # # The maximum size of the volume, if not specified the volume can grow to the size of the # maxSize: 50GiB # maxSize: 80% # The filesystem describes how the volume is formatted. filesystem: type: xfs # Filesystem type. Default is `xfs`. # The encryption describes how the volume is encrypted. encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Enable TPM based disk encryption. tpm: {} # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: topsecret # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # # Cipher to use for the encryption. Depends on the encryption provider. # cipher: aes-xts-plain64 # # Defines the encryption sector size. # blockSize: 4096 # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: UserVolumeConfig name: local-data # Name of the volume. volumeType: partition # Volume type. # The provisioning describes how the volume is provisioned. provisioning: # The disk selector expression. diskSelector: match: disk.transport == "nvme" # The Common Expression Language (CEL) expression to match the disk. maxSize: 50GiB # The maximum size of the volume, if not specified the volume can grow to the size of the # # The minimum size of the volume. # minSize: 2.5GiB # The filesystem describes how the volume is formatted. filesystem: type: xfs # Filesystem type. Default is `xfs`. # The encryption describes how the volume is encrypted. encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Enable TPM based disk encryption. tpm: {} # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: topsecret # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # # Cipher to use for the encryption. Depends on the encryption provider. # cipher: aes-xts-plain64 # # Defines the encryption sector size. # blockSize: 4096 # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the volume.

Name might be between 1 and 34 characters long and can only contain:
lowercase and uppercase ASCII letters, digits, and hyphens. | | |`volumeType` |VolumeType |Volume type. |`directory`
`disk`
`partition`
| |`provisioning` |ProvisioningSpec |The provisioning describes how the volume is provisioned. | | |`filesystem` |FilesystemSpec |The filesystem describes how the volume is formatted. | | |`encryption` |EncryptionSpec |The encryption describes how the volume is encrypted. | | |`mount` |UserMountSpec |The mount describes additional mount options. | | ## provisioning {#UserVolumeConfig.provisioning} ProvisioningSpec describes how the volume is provisioned. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`diskSelector` |DiskSelector |The disk selector expression. | | |`grow` |bool |Should the volume grow to the size of the disk (if possible). | | |`minSize` |ByteSize |The minimum size of the volume.

Size is specified in bytes, but can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} minSize: 2.5GiB {{< /highlight >}}
| | |`maxSize` |Size |The maximum size of the volume, if not specified the volume can grow to the size of the
disk.

Size is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} maxSize: 50GiB {{< /highlight >}}{{< highlight yaml >}} maxSize: 80% {{< /highlight >}}
| | ### diskSelector {#UserVolumeConfig.provisioning.diskSelector} DiskSelector selects a disk for the volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the disk.
Show example(s)match disks with size between 120GB and 1TB:{{< highlight yaml >}} match: disk.size > 120u * GB && disk.size < 1u * TB {{< /highlight >}}match SATA disks that are not rotational and not system disks:{{< highlight yaml >}} match: disk.transport == "sata" && !disk.rotational && !system_disk {{< /highlight >}}
| | ## filesystem {#UserVolumeConfig.filesystem} FilesystemSpec configures the filesystem for the volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`type` |FilesystemType |Filesystem type. Default is `xfs`. |`ext4`
`xfs`
| |`projectQuotaSupport` |bool |Enables project quota support, valid only for 'xfs' filesystem.

Note: changing this value might require a full remount of the filesystem. | | ## encryption {#UserVolumeConfig.encryption} EncryptionSpec represents volume encryption settings. {{< highlight yaml >}} encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: exampleKey # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # KMS managed encryption key. kms: endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`provider` |EncryptionProviderType |Encryption provider to use for the encryption. |`luks2`
| |`keys` |[]EncryptionKey |Defines the encryption keys generation and storage method. | | |`cipher` |string |Cipher to use for the encryption. Depends on the encryption provider.
Show example(s){{< highlight yaml >}} cipher: aes-xts-plain64 {{< /highlight >}}
|`aes-xts-plain64`
`xchacha12,aes-adiantum-plain64`
`xchacha20,aes-adiantum-plain64`
| |`keySize` |uint |Defines the encryption key length. | | |`blockSize` |uint64 |Defines the encryption sector size.
Show example(s){{< highlight yaml >}} blockSize: 4096 {{< /highlight >}}
| | |`options` |[]string |Additional --perf parameters for the LUKS2 encryption.
Show example(s){{< highlight yaml >}} options: - no_read_workqueue - no_write_workqueue {{< /highlight >}}
|`no_read_workqueue`
`no_write_workqueue`
`same_cpu_crypt`
| ### keys[] {#UserVolumeConfig.encryption.keys.} EncryptionKey represents configuration for disk encryption key. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`slot` |int |Key slot number for LUKS2 encryption. | | |`static` |EncryptionKeyStatic |Key which value is stored in the configuration file. | | |`nodeID` |EncryptionKeyNodeID |Deterministically generated key from the node UUID and PartitionLabel. | | |`kms` |EncryptionKeyKMS |KMS managed encryption key. | | |`tpm` |EncryptionKeyTPM |Enable TPM based disk encryption. | | |`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | | #### static {#UserVolumeConfig.encryption.keys..static} EncryptionKeyStatic represents throw away key type. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`passphrase` |string |Defines the static passphrase value. | | #### nodeID {#UserVolumeConfig.encryption.keys..nodeID} EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. #### kms {#UserVolumeConfig.encryption.keys..kms} EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. {{< highlight yaml >}} encryption: keys: - kms: endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |string |KMS endpoint to Seal/Unseal the key. | | #### tpm {#UserVolumeConfig.encryption.keys..tpm} EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`options` |EncryptionKeyTPMOptions |TPM options for key protection. | | |`checkSecurebootStatusOnEnroll` |bool |Check that Secureboot is enabled in the EFI firmware.
If Secureboot is not enabled, the enrollment of the key will fail. | | ##### options {#UserVolumeConfig.encryption.keys..tpm.options} EncryptionKeyTPMOptions represents the options for TPM-based key protection. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`pcrs` |[]int |List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list. | | ## mount {#UserVolumeConfig.mount} UserMountSpec describes how the volume is mounted. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disableAccessTime` |bool |If true, disable file access time updates. | | |`secure` |bool |Enable secure mount options (nosuid, nodev).

Defaults to true for better security. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/volumeconfig.md ================================================ --- description: | VolumeConfig is a system volume configuration document. Note: at the moment, only `STATE`, `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported. title: VolumeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: VolumeConfig name: EPHEMERAL # Name of the volume. # The provisioning describes how the volume is provisioned. provisioning: # The disk selector expression. diskSelector: match: disk.transport == "nvme" # The Common Expression Language (CEL) expression to match the disk. maxSize: 50GiB # The maximum size of the volume, if not specified the volume can grow to the size of the # # The minimum size of the volume. # minSize: 2.5GiB # # The encryption describes how the volume is encrypted. # encryption: # provider: luks2 # Encryption provider to use for the encryption. # # Defines the encryption keys generation and storage method. # keys: # - slot: 0 # Key slot number for LUKS2 encryption. # # Key which value is stored in the configuration file. # static: # passphrase: exampleKey # Defines the static passphrase value. # # # # KMS managed encryption key. # # kms: # # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. # - slot: 1 # Key slot number for LUKS2 encryption. # # KMS managed encryption key. # kms: # endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. # cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. # blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the volume. | | |`provisioning` |ProvisioningSpec |The provisioning describes how the volume is provisioned. | | |`encryption` |EncryptionSpec |The encryption describes how the volume is encrypted. | | |`mount` |MountSpec |The mount describes additional mount options. | | ## provisioning {#VolumeConfig.provisioning} ProvisioningSpec describes how the volume is provisioned. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`diskSelector` |DiskSelector |The disk selector expression. | | |`grow` |bool |Should the volume grow to the size of the disk (if possible). | | |`minSize` |ByteSize |The minimum size of the volume.

Size is specified in bytes, but can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} minSize: 2.5GiB {{< /highlight >}}
| | |`maxSize` |Size |The maximum size of the volume, if not specified the volume can grow to the size of the
disk.

Size is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.
Show example(s){{< highlight yaml >}} maxSize: 50GiB {{< /highlight >}}{{< highlight yaml >}} maxSize: 80% {{< /highlight >}}
| | ### diskSelector {#VolumeConfig.provisioning.diskSelector} DiskSelector selects a disk for the volume. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the disk.
Show example(s)match disks with size between 120GB and 1TB:{{< highlight yaml >}} match: disk.size > 120u * GB && disk.size < 1u * TB {{< /highlight >}}match SATA disks that are not rotational and not system disks:{{< highlight yaml >}} match: disk.transport == "sata" && !disk.rotational && !system_disk {{< /highlight >}}
| | ## encryption {#VolumeConfig.encryption} EncryptionSpec represents volume encryption settings. {{< highlight yaml >}} encryption: provider: luks2 # Encryption provider to use for the encryption. # Defines the encryption keys generation and storage method. keys: - slot: 0 # Key slot number for LUKS2 encryption. # Key which value is stored in the configuration file. static: passphrase: exampleKey # Defines the static passphrase value. # # KMS managed encryption key. # kms: # endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. - slot: 1 # Key slot number for LUKS2 encryption. # KMS managed encryption key. kms: endpoint: https://example-kms-endpoint.com # KMS endpoint to Seal/Unseal the key. cipher: aes-xts-plain64 # Cipher to use for the encryption. Depends on the encryption provider. blockSize: 4096 # Defines the encryption sector size. # # Additional --perf parameters for the LUKS2 encryption. # options: # - no_read_workqueue # - no_write_workqueue {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`provider` |EncryptionProviderType |Encryption provider to use for the encryption. |`luks2`
| |`keys` |[]EncryptionKey |Defines the encryption keys generation and storage method. | | |`cipher` |string |Cipher to use for the encryption. Depends on the encryption provider.
Show example(s){{< highlight yaml >}} cipher: aes-xts-plain64 {{< /highlight >}}
|`aes-xts-plain64`
`xchacha12,aes-adiantum-plain64`
`xchacha20,aes-adiantum-plain64`
| |`keySize` |uint |Defines the encryption key length. | | |`blockSize` |uint64 |Defines the encryption sector size.
Show example(s){{< highlight yaml >}} blockSize: 4096 {{< /highlight >}}
| | |`options` |[]string |Additional --perf parameters for the LUKS2 encryption.
Show example(s){{< highlight yaml >}} options: - no_read_workqueue - no_write_workqueue {{< /highlight >}}
|`no_read_workqueue`
`no_write_workqueue`
`same_cpu_crypt`
| ### keys[] {#VolumeConfig.encryption.keys.} EncryptionKey represents configuration for disk encryption key. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`slot` |int |Key slot number for LUKS2 encryption. | | |`static` |EncryptionKeyStatic |Key which value is stored in the configuration file. | | |`nodeID` |EncryptionKeyNodeID |Deterministically generated key from the node UUID and PartitionLabel. | | |`kms` |EncryptionKeyKMS |KMS managed encryption key. | | |`tpm` |EncryptionKeyTPM |Enable TPM based disk encryption. | | |`lockToState` |bool |Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes. | | #### static {#VolumeConfig.encryption.keys..static} EncryptionKeyStatic represents throw away key type. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`passphrase` |string |Defines the static passphrase value. | | #### nodeID {#VolumeConfig.encryption.keys..nodeID} EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel. #### kms {#VolumeConfig.encryption.keys..kms} EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server. {{< highlight yaml >}} encryption: keys: - kms: endpoint: https://192.168.88.21:4443 # KMS endpoint to Seal/Unseal the key. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |string |KMS endpoint to Seal/Unseal the key. | | #### tpm {#VolumeConfig.encryption.keys..tpm} EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`options` |EncryptionKeyTPMOptions |TPM options for key protection. | | |`checkSecurebootStatusOnEnroll` |bool |Check that Secureboot is enabled in the EFI firmware.
If Secureboot is not enabled, the enrollment of the key will fail. | | ##### options {#VolumeConfig.encryption.keys..tpm.options} EncryptionKeyTPMOptions represents the options for TPM-based key protection. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`pcrs` |[]int |List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list. | | ## mount {#VolumeConfig.mount} MountSpec describes how the volume is mounted. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`secure` |bool |Enable secure mount options (nosuid, nodev).

Defaults to true for better security. | | ================================================ FILE: website/content/v1.13/reference/configuration/block/zswapconfig.md ================================================ --- description: | ZswapConfig is a zswap (compressed memory) configuration document. When zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk. The compressed pages are stored in a memory pool, which is used to avoid writing to disk when the system is under memory pressure. title: ZswapConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ZswapConfig maxPoolPercent: 25 # The maximum percent of memory that zswap can use. shrinkerEnabled: true # Enable the shrinker feature: kernel might move {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`maxPoolPercent` |int |The maximum percent of memory that zswap can use.
This is a percentage of the total system memory.
The value must be between 0 and 100. | | |`shrinkerEnabled` |bool |Enable the shrinker feature: kernel might move
cold pages from zswap to swap device to free up memory
for other use cases. | | ================================================ FILE: website/content/v1.13/reference/configuration/cri/_index.md ================================================ --- description: "" title: cri --- ================================================ FILE: website/content/v1.13/reference/configuration/cri/registryauthconfig.md ================================================ --- description: RegistryAuthConfig configures authentication for a registry endpoint. title: RegistryAuthConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: RegistryAuthConfig name: my-private-registry.local:5000 # Registry endpoint to apply the authentication configuration to. username: my-username # Username/password authentication. password: my-password # Username/password authentication. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Registry endpoint to apply the authentication configuration to.

Registry endpoint is the hostname part of the endpoint URL,
e.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.

The authentication configuration will apply to all image pulls for this
registry endpoint, by Talos or any Kubernetes workloads. | | |`username` |string |Username/password authentication. | | |`password` |string |Username/password authentication. | | |`auth` |string |Raw authentication string. | | |`identityToken` |string |Identity token authentication. | | ================================================ FILE: website/content/v1.13/reference/configuration/cri/registrymirrorconfig.md ================================================ --- description: RegistryMirrorConfig configures an image registry mirror. title: RegistryMirrorConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: RegistryMirrorConfig name: registry.k8s.io # Registry name to apply the mirror configuration to. # List of mirror endpoints for the registry. endpoints: - url: https://my-private-registry.local:5000 # The URL of the registry mirror endpoint. - url: http://my-harbor/v2/registry-k8s.io/ # The URL of the registry mirror endpoint. overridePath: true # Use endpoint path as supplied, without adding `/v2/` suffix. skipFallback: true # Skip fallback to the original registry if none of the mirrors are available {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Registry name to apply the mirror configuration to.

Registry name is the first segment of image identifier, with 'docker.io'
being default one.

A special name '*' can be used to define mirror configuration
that applies to all registries. | | |`endpoints` |[]RegistryEndpoint |List of mirror endpoints for the registry.
Mirrors will be used in the order they are specified,
falling back to the default registry is `skipFallback` is not set to true. | | |`skipFallback` |bool |Skip fallback to the original registry if none of the mirrors are available
or contain the requested image. | | ## endpoints[] {#RegistryMirrorConfig.endpoints.} RegistryEndpoint defines a registry mirror endpoint. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`url` |URL |The URL of the registry mirror endpoint.
Show example(s){{< highlight yaml >}} url: https://my-registry-mirror.local:5000 {{< /highlight >}}
| | |`overridePath` |bool |Use endpoint path as supplied, without adding `/v2/` suffix. | | ================================================ FILE: website/content/v1.13/reference/configuration/cri/registrytlsconfig.md ================================================ --- description: RegistryTLSConfig configures TLS for a registry endpoint. title: RegistryTLSConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: RegistryTLSConfig name: my-private-registry.local:5000 # Registry endpoint to apply the TLS configuration to. ca: |- # CA registry certificate to add the list of trusted certificates. -----BEGIN CERTIFICATE----- MIID...IDAQAB -----END CERTIFICATE----- # # Enable mutual TLS authentication with the registry. # clientIdentity: # cert: |- # -----BEGIN CERTIFICATE----- # MIID...IDAQAB # -----END CERTIFICATE----- # key: |- # -----BEGIN PRIVATE KEY----- # MIIE...AB # -----END PRIVATE KEY----- {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Registry endpoint to apply the TLS configuration to.

Registry endpoint is the hostname part of the endpoint URL,
e.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.

The TLS configuration makes sense only for HTTPS endpoints.
The TLS configuration will apply to all image pulls for this
registry endpoint, by Talos or any Kubernetes workloads. | | |`clientIdentity` |CertificateAndKey |Enable mutual TLS authentication with the registry.
Client certificate and key should be PEM-encoded.
Show example(s){{< highlight yaml >}} clientIdentity: cert: |- -----BEGIN CERTIFICATE----- MIID...IDAQAB -----END CERTIFICATE----- key: |- -----BEGIN PRIVATE KEY----- MIIE...AB -----END PRIVATE KEY----- {{< /highlight >}}
| | |`ca` |string |CA registry certificate to add the list of trusted certificates.
Certificate should be PEM-encoded. | | |`insecureSkipVerify` |bool |Skip TLS server certificate verification (not recommended). | | ================================================ FILE: website/content/v1.13/reference/configuration/extensions/_index.md ================================================ --- description: | Package extensions provides extensions config documents. title: extensions --- ================================================ FILE: website/content/v1.13/reference/configuration/extensions/extensionserviceconfig.md ================================================ --- description: ExtensionServiceConfig is a extensionserviceconfig document. title: ExtensionServiceConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ExtensionServiceConfig name: nut-client # Name of the extension service. # The config files for the extension service. configFiles: - content: MONITOR ${upsmonHost} 1 remote username password # The content of the extension service config file. mountPath: /usr/local/etc/nut/upsmon.conf # The mount path of the extension service config file. # The environment for the extension service. environment: - NUT_UPS=upsname {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the extension service. | | |`configFiles` |[]ConfigFile |The config files for the extension service. | | |`environment` |[]string |The environment for the extension service. | | ## configFiles[] {#ExtensionServiceConfig.configFiles.} ConfigFile is a config file for extension services. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`content` |string |The content of the extension service config file. | | |`mountPath` |string |The mount path of the extension service config file. | | ================================================ FILE: website/content/v1.13/reference/configuration/hardware/_index.md ================================================ --- description: | Package hardware provides hardware related config documents. title: hardware --- ================================================ FILE: website/content/v1.13/reference/configuration/hardware/pcidriverrebindconfig.md ================================================ --- description: PCIDriverRebindConfig allows to configure PCI driver rebinds. title: PCIDriverRebindConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: PCIDriverRebindConfig name: 0000:04:00.00 # PCI device id targetDriver: vfio-pci # Target driver to rebind the PCI device to. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |PCI device id | | |`targetDriver` |string |Target driver to rebind the PCI device to. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/_index.md ================================================ --- description: | Package network provides network machine configuration documents. title: network --- ================================================ FILE: website/content/v1.13/reference/configuration/network/blackholerouteconfig.md ================================================ --- description: BlackholeRouteConfig is a config document to configure blackhole routes. title: BlackholeRouteConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: BlackholeRouteConfig name: 10.0.0.0/12 # Route destination as an address prefix. metric: 100 # The optional metric for the route. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Route destination as an address prefix.
Show example(s){{< highlight yaml >}} name: 10.0.0.0/12 {{< /highlight >}}
| | |`metric` |uint32 |The optional metric for the route. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/bondconfig.md ================================================ --- description: BondConfig is a config document to create a bond (link aggregation) over a set of links. title: BondConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: BondConfig name: bond.int # Name of the bond link (interface) to be created. # Names of the links (interfaces) on which the bond will be created. links: - enp1s2 - enp1s2 bondMode: 802.3ad # Bond mode. miimon: 100 # Link monitoring frequency in milliseconds. updelay: 200 # The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected. downdelay: 200 # The time, in milliseconds, to wait before disabling a slave after a link failure has been detected. xmitHashPolicy: layer3+4 # Selects the transmit hash policy to use for slave selection. lacpRate: slow # LACPDU frames periodic transmission rate. adActorSysPrio: 65535 # Actor system priority for 802.3ad. resendIGMP: 1 # The number of times IGMP packets should be resent. packetsPerSlave: 1 # The number of packets to transmit through a slave before moving to the next one. # Configure addresses to be statically assigned to the link. addresses: - address: 10.15.0.3/16 # IP address to be assigned to the link. # Configure routes to be statically created via the link. routes: - destination: 10.0.0.0/8 # The route's destination as an address prefix. gateway: 10.15.0.1 # The route's gateway (if empty, creates link scope route). # # Override the hardware (MAC) address of the link. # hardwareAddr: 2e:3c:4d:5e:6f:70 # # ARP link monitoring frequency in milliseconds. # arpInterval: 1000 # # The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set. # arpIpTargets: # - 10.15.0.1 # # The list of IPv6 addresses to use for NS link monitoring when arpInterval is set. # nsIp6Targets: # - fd00::1 # # Specifies whether or not ARP probes and replies should be validated. # arpValidate: active # # Specifies whether ARP probes should be sent to any or all targets. # arpAllTargets: all # # Specifies whether active-backup mode should set all slaves to the same MAC address # failOverMac: active # # Aggregate selection policy for 802.3ad. # adSelect: stable # # Whether to send LACPDU frames periodically. # adLACPActive: on # # Policy under which the primary slave should be reselected. # primaryReselect: always # # Whether dynamic shuffling of flows is enabled in tlb or alb mode. # tlbLogicalLb: 1 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the bond link (interface) to be created.
Show example(s){{< highlight yaml >}} name: bond.ext {{< /highlight >}}
| | |`hardwareAddr` |HardwareAddr |Override the hardware (MAC) address of the link.
Show example(s){{< highlight yaml >}} hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}}
| | |`links` |[]string |Names of the links (interfaces) on which the bond will be created.
Link aliases can be used here as well.
Show example(s){{< highlight yaml >}} links: - enp0s3 - enp0s8 {{< /highlight >}}
| | |`bondMode` |BondMode |Bond mode.
Show example(s){{< highlight yaml >}} bondMode: 802.3ad {{< /highlight >}}
|`balance-rr`
`active-backup`
`balance-xor`
`broadcast`
`802.3ad`
`balance-tlb`
`balance-alb`
| |`miimon` |uint32 |Link monitoring frequency in milliseconds.
Show example(s){{< highlight yaml >}} miimon: 200 {{< /highlight >}}
| | |`updelay` |uint32 |The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.
Show example(s){{< highlight yaml >}} updelay: 300 {{< /highlight >}}
| | |`downdelay` |uint32 |The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.
Show example(s){{< highlight yaml >}} downdelay: 100 {{< /highlight >}}
| | |`useCarrier` |bool |Specifies whether or not miimon should use MII or ETHTOOL. | | |`xmitHashPolicy` |BondXmitHashPolicy |Selects the transmit hash policy to use for slave selection.
Show example(s){{< highlight yaml >}} xmitHashPolicy: layer2 {{< /highlight >}}
|`layer2`
`layer3+4`
`layer2+3`
`encap2+3`
`encap3+4`
| |`arpInterval` |uint32 |ARP link monitoring frequency in milliseconds.
Show example(s){{< highlight yaml >}} arpInterval: 1000 {{< /highlight >}}
| | |`arpIpTargets` |[]Addr |The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.
Maximum of 16 targets are supported.
Show example(s){{< highlight yaml >}} arpIpTargets: - 10.15.0.1 {{< /highlight >}}
| | |`nsIp6Targets` |[]Addr |The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.
Maximum of 16 targets are supported.
Show example(s){{< highlight yaml >}} nsIp6Targets: - fd00::1 {{< /highlight >}}
| | |`arpValidate` |ARPValidate |Specifies whether or not ARP probes and replies should be validated.
Show example(s){{< highlight yaml >}} arpValidate: active {{< /highlight >}}
|`none`
`active`
`backup`
`all`
`filter`
`filter-active`
`filter-backup`
| |`arpAllTargets` |ARPAllTargets |Specifies whether ARP probes should be sent to any or all targets.
Show example(s){{< highlight yaml >}} arpAllTargets: all {{< /highlight >}}
|`any`
`all`
| |`lacpRate` |LACPRate |LACPDU frames periodic transmission rate.
Show example(s){{< highlight yaml >}} lacpRate: fast {{< /highlight >}}
|`slow`
`fast`
| |`failOverMac` |FailOverMAC |Specifies whether active-backup mode should set all slaves to the same MAC address
at enslavement, when enabled, or perform special handling.
Show example(s){{< highlight yaml >}} failOverMac: active {{< /highlight >}}
|`none`
`active`
`follow`
| |`adSelect` |ADSelect |Aggregate selection policy for 802.3ad.
Show example(s){{< highlight yaml >}} adSelect: stable {{< /highlight >}}
|`stable`
`bandwidth`
`count`
| |`adActorSysPrio` |uint16 |Actor system priority for 802.3ad.
Show example(s){{< highlight yaml >}} adActorSysPrio: 65535 {{< /highlight >}}
| | |`adUserPortKey` |uint16 |User port key (upper 10 bits) for 802.3ad.
Show example(s){{< highlight yaml >}} adUserPortKey: 0 {{< /highlight >}}
| | |`adLACPActive` |ADLACPActive |Whether to send LACPDU frames periodically.
Show example(s){{< highlight yaml >}} adLACPActive: on {{< /highlight >}}
|`on`
`off`
| |`primaryReselect` |PrimaryReselect |Policy under which the primary slave should be reselected.
Show example(s){{< highlight yaml >}} primaryReselect: always {{< /highlight >}}
|`always`
`better`
`failure`
| |`resendIGMP` |uint32 |The number of times IGMP packets should be resent. | | |`minLinks` |uint32 |The minimum number of active links required for the bond to be considered active. | | |`lpInterval` |uint32 |The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch. | | |`packetsPerSlave` |uint32 |The number of packets to transmit through a slave before moving to the next one. | | |`numPeerNotif` |uint8 |The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)
to be issued after a failover event. | | |`tlbLogicalLb` |uint8 |Whether dynamic shuffling of flows is enabled in tlb or alb mode.
Show example(s){{< highlight yaml >}} tlbLogicalLb: 1 {{< /highlight >}}
| | |`allSlavesActive` |uint8 |Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).
Show example(s){{< highlight yaml >}} allSlavesActive: 0 {{< /highlight >}}
| | |`peerNotifDelay` |uint32 |The delay, in milliseconds, between each peer notification. | | |`missedMax` |uint8 |The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor. | | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## addresses[] {#BondConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#BondConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/bridgeconfig.md ================================================ --- description: BridgeConfig is a config document to create a Bridge (link aggregation) over a set of links. title: BridgeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: BridgeConfig name: bridge.3 # Name of the bridge link (interface) to be created. # Names of the links (interfaces) to be aggregated. links: - eno1 - eno2 # Bridge STP (Spanning Tree Protocol) configuration. stp: enabled: true # Enable or disable STP on the bridge. # # Override the hardware (MAC) address of the link. # hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the bridge link (interface) to be created.
Show example(s){{< highlight yaml >}} name: Bridge.ext {{< /highlight >}}
| | |`hardwareAddr` |HardwareAddr |Override the hardware (MAC) address of the link.
Show example(s){{< highlight yaml >}} hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}}
| | |`links` |[]string |Names of the links (interfaces) to be aggregated.
Link aliases can be used here as well.
Show example(s){{< highlight yaml >}} links: - enp1s3 - enp1s2 {{< /highlight >}}
| | |`stp` |BridgeSTPConfig |Bridge STP (Spanning Tree Protocol) configuration. | | |`vlan` |BridgeVLANConfig |Bridge VLAN configuration. | | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## stp {#BridgeConfig.stp} BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable or disable STP on the bridge.
Show example(s){{< highlight yaml >}} enabled: true {{< /highlight >}}
| | ## vlan {#BridgeConfig.vlan} BridgeVLANConfig is a bridge VLAN configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`filtering` |bool |Enable or disable VLAN filtering on the bridge.
Show example(s){{< highlight yaml >}} filtering: true {{< /highlight >}}
| | ## addresses[] {#BridgeConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#BridgeConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/dhcpv4config.md ================================================ --- description: DHCPv4Config is a config document to configure DHCPv4 on a network link. title: DHCPv4Config --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: DHCPv4Config name: enp0s2 # Name of the link (interface). clientIdentifier: mac # Client identifier to use when communicating with DHCP servers. # # Raw value of the DUID to use as client identifier. # duidRaw: 00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the link (interface).
Show example(s){{< highlight yaml >}} name: enp0s2 {{< /highlight >}}
| | |`routeMetric` |uint32 |An optional metric for the routes received from the DHCP server.

Lower values indicate higher priority.
Default value is 1024. | | |`ignoreHostname` |bool |Ignore hostname received from the DHCP server. | | |`clientIdentifier` |ClientIdentifier |Client identifier to use when communicating with DHCP servers.

Defaults to 'mac' if not set. |`none`
`mac`
`duid`
| |`duidRaw` |HardwareAddr |Raw value of the DUID to use as client identifier.

This field is only used if 'clientIdentifier' is set to 'duid'.
Show example(s){{< highlight yaml >}} duidRaw: 00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/dhcpv6config.md ================================================ --- description: DHCPv6Config is a config document to configure DHCPv6 on a network link. title: DHCPv6Config --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: DHCPv6Config name: enp0s2 # Name of the link (interface). # # Raw value of the DUID to use as client identifier. # duidRaw: 00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the link (interface).
Show example(s){{< highlight yaml >}} name: enp0s2 {{< /highlight >}}
| | |`routeMetric` |uint32 |An optional metric for the routes received from the DHCP server.

Lower values indicate higher priority.
Default value is 1024. | | |`ignoreHostname` |bool |Ignore hostname received from the DHCP server. | | |`clientIdentifier` |ClientIdentifier |Client identifier to use when communicating with DHCP servers.

Defaults to 'mac' if not set. |`none`
`mac`
`duid`
| |`duidRaw` |HardwareAddr |Raw value of the DUID to use as client identifier.

This field is only used if 'clientIdentifier' is set to 'duid'.
Show example(s){{< highlight yaml >}} duidRaw: 00:01:00:01:23:45:67:89:ab:cd:ef:01:23:45 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/dummylinkconfig.md ================================================ --- description: DummyLinkConfig is a config document to create a dummy (virtual) network link. title: DummyLinkConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: DummyLinkConfig name: dummy1 # Name of the dummy link (interface). # Configure addresses to be statically assigned to the link. addresses: - address: 192.168.1.100/24 # IP address to be assigned to the link. # # Override the hardware (MAC) address of the link. # hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the dummy link (interface).
Show example(s){{< highlight yaml >}} name: dummy1 {{< /highlight >}}
| | |`hardwareAddr` |HardwareAddr |Override the hardware (MAC) address of the link.
Show example(s){{< highlight yaml >}} hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}}
| | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## addresses[] {#DummyLinkConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#DummyLinkConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/ethernetconfig.md ================================================ --- description: EthernetConfig is a config document to configure Ethernet interfaces. title: EthernetConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: EthernetConfig name: enp0s2 # Name of the link (interface). # Configuration for Ethernet features. features: tx-tcp-segmentation: false # Configuration for Ethernet link rings. rings: rx: 256 # Number of RX rings. # Configuration for Ethernet link channels. channels: rx: 4 # Number of RX channels. # # Wake-on-LAN modes to enable. # wakeOnLan: # - unicast # - magic {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the link (interface). | | |`features` |map[string]bool |Configuration for Ethernet features.

Set of features available and whether they can be enabled or disabled is driver specific.
Use `talosctl get ethernetstatus -o yaml` to get the list of available features and
their current status. | | |`rings` |EthernetRingsConfig |Configuration for Ethernet link rings.

This is similar to `ethtool -G` command. | | |`channels` |EthernetChannelsConfig |Configuration for Ethernet link channels.

This is similar to `ethtool -L` command. | | |`wakeOnLan` |[]WOLMode |Wake-on-LAN modes to enable.

If this field is omitted, Wake-on-LAN configuration is not changed.
An empty list disables Wake-on-LAN.

This is similar to `ethtool -s wol ` command.
Show example(s){{< highlight yaml >}} wakeOnLan: - unicast - magic {{< /highlight >}}
|`phy`
`unicast`
`multicast`
`broadcast`
`arp`
`magic`
`magicsecure`
`filter`
| ## rings {#EthernetConfig.rings} EthernetRingsConfig is a configuration for Ethernet link rings. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`rx` |uint32 |Number of RX rings. | | |`tx` |uint32 |Number of TX rings. | | |`rx-mini` |uint32 |Number of RX mini rings. | | |`rx-jumbo` |uint32 |Number of RX jumbo rings. | | |`rx-buf-len` |uint32 |RX buffer length. | | |`cqe-size` |uint32 |CQE size. | | |`tx-push` |bool |TX push enabled. | | |`rx-push` |bool |RX push enabled. | | |`tx-push-buf-len` |uint32 |TX push buffer length. | | |`tcp-data-split` |bool |TCP data split enabled. | | ## channels {#EthernetConfig.channels} EthernetChannelsConfig is a configuration for Ethernet link channels. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`rx` |uint32 |Number of RX channels. | | |`tx` |uint32 |Number of TX channels. | | |`other` |uint32 |Number of other channels. | | |`combined` |uint32 |Number of combined channels. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/hcloudvipconfig.md ================================================ --- description: | HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement. Virtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server. Any other use cases are not supported and may lead to unexpected behavior. Virtual IP will be announced from only one node at a time using Hetzner Cloud APIs. title: HCloudVIPConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: HCloudVIPConfig name: int0 # IP address to be advertised as a Layer 2 VIP. link: enp0s2 # Name of the link to assign the VIP to. apiToken: my-secret-token # Specifies the Hetzner Cloud API Token. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |IP address to be advertised as a Layer 2 VIP.
Show example(s){{< highlight yaml >}} name: 192.168.100.1 {{< /highlight >}}{{< highlight yaml >}} name: fd00::1 {{< /highlight >}}
| | |`link` |string |Name of the link to assign the VIP to.

Selector must match exactly one link, otherwise an error is returned.
If multiple selectors match the same link, the first one is used. | | |`apiToken` |string |Specifies the Hetzner Cloud API Token. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/hostnameconfig.md ================================================ --- description: 'HostnameConfig is a config document to configure the hostname: either a static hostname or an automatically generated hostname.' title: HostnameConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: HostnameConfig hostname: worker-33 # A static hostname to set for the machine. {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: HostnameConfig auto: stable # A method to automatically generate a hostname for the machine. # # A static hostname to set for the machine. # hostname: controlplane1 # hostname: controlplane1.example.org {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`auto` |AutoHostnameKind |A method to automatically generate a hostname for the machine.

There are two methods available:
- `stable` - generates a stable hostname based on machine identity
- `off` - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).

Automatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.
Conflicts with `hostname` field. |`stable`
`off`
| |`hostname` |string |A static hostname to set for the machine.

This hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.
Conflicts with `auto` field.
Show example(s){{< highlight yaml >}} hostname: controlplane1 {{< /highlight >}}{{< highlight yaml >}} hostname: controlplane1.example.org {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/kubespanconfig.md ================================================ --- description: KubeSpanConfig is a config document to configure KubeSpan. title: KubeSpanConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: KubeSpanConfig enabled: true # Enable the KubeSpan feature. advertiseKubernetesNetworks: false # Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node. allowDownPeerBypass: false # Skip sending traffic via KubeSpan if the peer connection state is not up. harvestExtraEndpoints: false # KubeSpan can collect and publish extra endpoints for each member of the cluster mtu: 1420 # KubeSpan link MTU size. # KubeSpan advanced filtering of network addresses. filters: # Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections. endpoints: - 0.0.0.0/0 - ::/0 # Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan. excludeAdvertisedNetworks: - 192.168.1.0/24 - 2003::/16 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable the KubeSpan feature.
Cluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled. | | |`advertiseKubernetesNetworks` |bool |Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.
If disabled, CNI handles pod-to-pod traffic encapsulation.
If enabled, KubeSpan takes over pod-to-pod traffic directly. | | |`allowDownPeerBypass` |bool |Skip sending traffic via KubeSpan if the peer connection state is not up.
This provides configurable choice between connectivity and security. | | |`harvestExtraEndpoints` |bool |KubeSpan can collect and publish extra endpoints for each member of the cluster
based on Wireguard endpoint information for each peer.
Disabled by default. Do not enable with high peer counts (>50). | | |`mtu` |uint32 |KubeSpan link MTU size.
Default value is 1420. | | |`filters` |KubeSpanFiltersConfig |KubeSpan advanced filtering of network addresses.
Settings are optional and apply only to this node. | | ## filters {#KubeSpanConfig.filters} KubeSpanFiltersConfig configures KubeSpan endpoint filters. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoints` |[]string |Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.

By default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.

Default value: no filtering.
Show example(s)Exclude addresses in 192.168.0.0/16 subnet.:{{< highlight yaml >}} endpoints: - 0.0.0.0/0 - '!192.168.0.0/16' - ::/0 {{< /highlight >}}
| | |`excludeAdvertisedNetworks` |[]Prefix |Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.

By default, all networks are advertised.
Use this filter to exclude some networks from being advertised.

Note: excluded networks will not be reachable over KubeSpan, so make sure
these networks are still reachable via some other route (e.g., direct connection).

Default value: no filtering.
Show example(s)Exclude private networks from being advertised.:{{< highlight yaml >}} excludeAdvertisedNetworks: - 192.168.1.0/24 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/kubespanendpointsconfig.md ================================================ --- description: KubeSpanEndpointsConfig is a config document to configure KubeSpan endpoints. title: KubeSpanEndpointsConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: KubeSpanEndpointsConfig # A list of extra Wireguard endpoints to announce from this machine. extraAnnouncedEndpoints: - 192.168.13.46:52000 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`extraAnnouncedEndpoints` |[]AddrPort |A list of extra Wireguard endpoints to announce from this machine.

Talos automatically adds endpoints based on machine addresses, public IP, etc.
This field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/layer2vipconfig.md ================================================ --- description: | Layer2VIPConfig is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement. Virtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server. Any other use cases are not supported and may lead to unexpected behavior. Virtual IP will be announced from only one node at a time using gratuitous ARP announcements for IPv4. title: Layer2VIPConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: Layer2VIPConfig name: 10.3.0.1 # IP address to be advertised as a Layer 2 VIP. link: enp0s2 # Name of the link to assign the VIP to. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |IP address to be advertised as a Layer 2 VIP.
Show example(s){{< highlight yaml >}} name: 192.168.100.1 {{< /highlight >}}{{< highlight yaml >}} name: fd00::1 {{< /highlight >}}
| | |`link` |string |Name of the link to assign the VIP to.

Selector must match exactly one link, otherwise an error is returned.
If multiple selectors match the same link, the first one is used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/linkaliasconfig.md ================================================ --- description: LinkAliasConfig is a config document to alias (give a different name) to a physical link. title: LinkAliasConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: LinkAliasConfig name: int0 # Alias for the link. # Selector to match the link to alias. selector: match: glob("00:1a:2b:*", mac(link.permanent_addr)) # The Common Expression Language (CEL) expression to match the link. {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: LinkAliasConfig name: net%d # Alias for the link. # Selector to match the link to alias. selector: match: link.driver == "e1000" # The Common Expression Language (CEL) expression to match the link. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Alias for the link.

Don't use system interface names like "eth0", "ens3", "enp0s2", etc. as those may conflict
with existing physical interfaces.

The name can contain a single integer format verb (`%d`) to create multiple aliases
from a single config document. When a format verb is detected, each matched link receives a sequential
alias (e.g. `net0`, `net1`, ...) based on hardware address order of the links.
Links already aliased by a previous config are automatically skipped.
Show example(s){{< highlight yaml >}} name: net0 {{< /highlight >}}{{< highlight yaml >}} name: private {{< /highlight >}}{{< highlight yaml >}} name: net%d {{< /highlight >}}
| | |`selector` |LinkSelector |Selector to match the link to alias.

When the alias name is a fixed string, the selector must match exactly one link.
When the alias name contains a format verb (e.g. `net%d`), the selector may match multiple links
and each match receives a sequential alias.
If multiple selectors match the same link, the first one is used. | | ## selector {#LinkAliasConfig.selector} LinkSelector selects a link to alias. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`match` |Expression |The Common Expression Language (CEL) expression to match the link.
Show example(s)match links with a specific MAC address:{{< highlight yaml >}} match: mac(link.permanent_addr) == "00:1a:2b:3c:4d:5e" {{< /highlight >}}match links by MAC address prefix:{{< highlight yaml >}} match: glob("00:1a:2b:*", mac(link.permanent_addr)) {{< /highlight >}}match links by driver name:{{< highlight yaml >}} match: link.driver == "e1000" {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/linkconfig.md ================================================ --- description: LinkConfig is a config document to configure physical interfaces (network links). title: LinkConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: LinkConfig name: enp0s2 # Name of the link (interface). up: true # Bring the link up or down. mtu: 9000 # Configure LinkMTU (Maximum Transmission Unit) for the link. # Configure addresses to be statically assigned to the link. addresses: - address: 192.168.1.100/24 # IP address to be assigned to the link. - address: fd00::1/64 # IP address to be assigned to the link. # Configure routes to be statically created via the link. routes: - destination: 10.0.0.0/8 # The route's destination as an address prefix. gateway: 10.0.0.1 # The route's gateway (if empty, creates link scope route). - gateway: fe80::1 # The route's gateway (if empty, creates link scope route). # # The route's destination as an address prefix. # destination: 10.0.0.0/8 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the link (interface).
Show example(s){{< highlight yaml >}} name: enp0s2 {{< /highlight >}}{{< highlight yaml >}} name: eth1 {{< /highlight >}}
| | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## addresses[] {#LinkConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#LinkConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/networkdefaultactionconfig.md ================================================ --- description: NetworkDefaultActionConfig is a ingress firewall default action configuration document. title: NetworkDefaultActionConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: NetworkDefaultActionConfig ingress: accept # Default action for all not explicitly configured ingress traffic: accept or block. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`ingress` |DefaultAction |Default action for all not explicitly configured ingress traffic: accept or block. |`accept`
`block`
| ================================================ FILE: website/content/v1.13/reference/configuration/network/networkruleconfig.md ================================================ --- description: NetworkRuleConfig is a network firewall rule config document. title: NetworkRuleConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: NetworkRuleConfig name: ingress-apid # Name of the config document. # Port selector defines which ports and protocols on the host are affected by the rule. portSelector: # Ports defines a list of port ranges or single ports. ports: - 50000 protocol: tcp # Protocol defines traffic protocol (e.g. TCP or UDP). # Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`. ingress: - subnet: 192.168.0.0/16 # Subnet defines a source subnet. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the config document. | | |`portSelector` |RulePortSelector |Port selector defines which ports and protocols on the host are affected by the rule. | | |`ingress` |[]IngressRule |Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`. | | ## portSelector {#NetworkRuleConfig.portSelector} RulePortSelector is a port selector for the network rule. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`ports` |PortRanges |Ports defines a list of port ranges or single ports.
The port ranges are inclusive, and should not overlap.
Show example(s){{< highlight yaml >}} ports: - 80 - 443 {{< /highlight >}}{{< highlight yaml >}} ports: - 1200-1299 - 8080 {{< /highlight >}}
| | |`protocol` |Protocol |Protocol defines traffic protocol (e.g. TCP or UDP). |`tcp`
`udp`
`icmp`
`icmpv6`
| ## ingress[] {#NetworkRuleConfig.ingress.} IngressRule is a ingress rule. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`subnet` |Prefix |Subnet defines a source subnet.
Show example(s){{< highlight yaml >}} subnet: 10.3.4.0/24 {{< /highlight >}}{{< highlight yaml >}} subnet: 2001:db8::/32 {{< /highlight >}}{{< highlight yaml >}} subnet: 1.3.4.5/32 {{< /highlight >}}
| | |`except` |Prefix |Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/resolverconfig.md ================================================ --- description: ResolverConfig is a config document to configure DNS resolving. title: ResolverConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ResolverConfig # A list of nameservers (DNS servers) to use for resolving domain names. nameservers: - address: 1.1.1.1 # The IP address of the nameserver. - address: ff08::1 # The IP address of the nameserver. # Configuration for search domains (in /etc/resolv.conf). searchDomains: # A list of search domains to be used for DNS resolution. domains: - example.com {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: ResolverConfig # Configuration for search domains (in /etc/resolv.conf). searchDomains: disableDefault: true # Disable default search domain configuration from hostname FQDN. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`nameservers` |[]NameserverConfig |A list of nameservers (DNS servers) to use for resolving domain names.

Nameservers are used to resolve domain names on the host, and they are also
propagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.

This overrides any nameservers obtained via DHCP or platform configuration.
Default configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers. | | |`searchDomains` |SearchDomainsConfig |Configuration for search domains (in /etc/resolv.conf).

The default is to derive search domains from the hostname FQDN. | | ## nameservers[] {#ResolverConfig.nameservers.} NameserverConfig represents a single nameserver configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Addr |The IP address of the nameserver.
Show example(s){{< highlight yaml >}} address: 10.0.0.1 {{< /highlight >}}
| | ## searchDomains {#ResolverConfig.searchDomains} SearchDomainsConfig represents search domains configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`domains` |[]string |A list of search domains to be used for DNS resolution.

Search domains are appended to unqualified domain names during DNS resolution.
For example, if "example.com" is a search domain and a user tries to resolve
"host", the system will attempt to resolve "host.example.com".

This overrides any search domains obtained via DHCP or platform configuration.
The default configuration derives the search domain from the hostname FQDN. | | |`disableDefault` |bool |Disable default search domain configuration from hostname FQDN.

When set to true, the system will not derive search domains from the hostname FQDN.
This allows for a custom configuration of search domains without any defaults. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/routingruleconfig.md ================================================ --- description: RoutingRuleConfig is a config document to configure Linux policy routing rules. title: RoutingRuleConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: RoutingRuleConfig name: "1000" # Priority of the routing rule. src: 10.0.0.0/8 # Source address prefix to match. table: "100" # The routing table to look up if the rule matches. action: unicast # The action to perform when the rule matches. # # Destination address prefix to match. # dst: 192.168.0.0/16 # # Match packets arriving on this interface. # iifName: eth0 # # Match packets going out on this interface. # oifName: eth1 # # Match packets with this firewall mark value. # fwMark: 256 # # Mask for the firewall mark comparison. # fwMask: 65280 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Priority of the routing rule.
Lower values are matched first.
Must be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).
Must be unique across all routing rules in the configuration.
Show example(s){{< highlight yaml >}} name: 1000 {{< /highlight >}}
| | |`src` |Prefix |Source address prefix to match.
If empty, matches all sources.
Show example(s){{< highlight yaml >}} src: 10.0.0.0/8 {{< /highlight >}}
| | |`dst` |Prefix |Destination address prefix to match.
If empty, matches all destinations.
Show example(s){{< highlight yaml >}} dst: 192.168.0.0/16 {{< /highlight >}}
| | |`table` |RoutingTable |The routing table to look up if the rule matches.
Show example(s){{< highlight yaml >}} table: 100 {{< /highlight >}}
| | |`action` |RoutingRuleAction |The action to perform when the rule matches.
Defaults to "unicast" (table lookup). |`unicast`
`blackhole`
`unreachable`
`prohibit`
| |`iifName` |string |Match packets arriving on this interface.
Show example(s){{< highlight yaml >}} iifName: eth0 {{< /highlight >}}
| | |`oifName` |string |Match packets going out on this interface.
Show example(s){{< highlight yaml >}} oifName: eth1 {{< /highlight >}}
| | |`fwMark` |uint32 |Match packets with this firewall mark value.
Show example(s){{< highlight yaml >}} fwMark: 256 {{< /highlight >}}
| | |`fwMask` |uint32 |Mask for the firewall mark comparison.
Show example(s){{< highlight yaml >}} fwMask: 65280 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/statichostconfig.md ================================================ --- description: StaticHostConfig is a config document to set /etc/hosts entries. title: StaticHostConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: StaticHostConfig name: 10.5.0.2 # IP address (IPv4 or IPv6) to map the hostnames to. # List of hostnames to map to the IP address. hostnames: - my-server - my-server.example.org {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |IP address (IPv4 or IPv6) to map the hostnames to. | | |`hostnames` |[]string |List of hostnames to map to the IP address. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/tcpprobeconfig.md ================================================ --- description: TCPProbeConfig is a config document to configure network TCP connectivity probes. title: TCPProbeConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: TCPProbeConfig name: proxy-check # Name of the probe. interval: 1s # Interval between probe attempts. failureThreshold: 3 # Number of consecutive failures for the probe to be considered failed after having succeeded. endpoint: proxy.example.com:3128 # Endpoint to probe in the format host:port. timeout: 10s # Timeout for the probe. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the probe.
Show example(s){{< highlight yaml >}} name: proxy-check {{< /highlight >}}
| | |`interval` |Duration |Interval between probe attempts.
Defaults to 1s.
Show example(s){{< highlight yaml >}} interval: 1s {{< /highlight >}}
| | |`failureThreshold` |int |Number of consecutive failures for the probe to be considered failed after having succeeded.
Defaults to 0 (immediately fail on first failure).
Show example(s){{< highlight yaml >}} failureThreshold: 3 {{< /highlight >}}
| | |`endpoint` |string |Endpoint to probe in the format host:port.
Show example(s){{< highlight yaml >}} endpoint: proxy.example.com:3128 {{< /highlight >}}
| | |`timeout` |Duration |Timeout for the probe.
Defaults to 10s.
Show example(s){{< highlight yaml >}} timeout: 10s {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/timesyncconfig.md ================================================ --- description: TimeSyncConfig is a config document to configure time synchronization (NTP). title: TimeSyncConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: TimeSyncConfig # Specifies NTP configuration to sync the time over network. ntp: # Specifies time (NTP) servers to use for setting the system time. servers: - pool.ntp.org {{< /highlight >}} {{< highlight yaml >}} apiVersion: v1alpha1 kind: TimeSyncConfig # Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices. ptp: # description: | devices: - /dev/ptp0 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Indicates if the time synchronization is enabled for the machine.
Defaults to `true`. | | |`bootTimeout` |Duration |Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.
NTP sync will be still running in the background.
Defaults to "infinity" (waiting forever for time sync) | | |`ntp` |NTPConfig |Specifies NTP configuration to sync the time over network.
Mutually exclusive with PTP configuration. | | |`ptp` |PTPConfig |Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.
Mutually exclusive with NTP configuration. | | ## ntp {#TimeSyncConfig.ntp} NTPConfig represents a NTP server configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`servers` |[]string |Specifies time (NTP) servers to use for setting the system time.
Defaults to `time.cloudflare.com`. | | ## ptp {#TimeSyncConfig.ptp} PTPConfig represents a PTP (Precision Time Protocol) configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`devices` |[]string |description: |
A list of PTP devices to sync with (e.g. provided by the hypervisor).

A PTP device is typically represented as a character device file in the /dev directory,
such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time
with an external time source that supports the Precision Time Protocol.
| | ================================================ FILE: website/content/v1.13/reference/configuration/network/vlanconfig.md ================================================ --- description: VLANConfig is a config document to create a VLAN (virtual LAN) over a parent link. title: VLANConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: VLANConfig name: enp0s3.34 # Name of the VLAN link (interface) to be created. vlanID: 34 # VLAN ID to be used for the VLAN link. parent: enp0s3 # Name of the parent link (interface) on which the VLAN link will be created. # Configure addresses to be statically assigned to the link. addresses: - address: 192.168.1.100/24 # IP address to be assigned to the link. # Configure routes to be statically created via the link. routes: - destination: 192.168.0.0/16 # The route's destination as an address prefix. gateway: 192.168.1.1 # The route's gateway (if empty, creates link scope route). # # Set the VLAN mode to use. # vlanMode: 802.1q {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the VLAN link (interface) to be created.
Show example(s){{< highlight yaml >}} name: enp0s3.34 {{< /highlight >}}
| | |`vlanID` |uint16 |VLAN ID to be used for the VLAN link.
Show example(s){{< highlight yaml >}} vlanID: 34 {{< /highlight >}}
| | |`vlanMode` |VLANProtocol |Set the VLAN mode to use.
If not set, defaults to '802.1q'.
Show example(s){{< highlight yaml >}} vlanMode: 802.1q {{< /highlight >}}
|`802.1q`
`802.1ad`
| |`parent` |string |Name of the parent link (interface) on which the VLAN link will be created.
Link aliases can be used here as well.
Show example(s){{< highlight yaml >}} parent: enp0s3 {{< /highlight >}}
| | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## addresses[] {#VLANConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#VLANConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/vrfconfig.md ================================================ --- description: VRFConfig is a config document to create a vrf and assign links to it. title: VRFConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: VRFConfig name: vrf-blue # Name of the vrf link (interface) to be created. # Names of the links (interfaces) to be assigned to this vrf. links: - eno1 - eno2 table: "10" # Routing table number to use for this vrf. # # Override the hardware (MAC) address of the link. # hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the vrf link (interface) to be created.
Show example(s){{< highlight yaml >}} name: vrf-blue {{< /highlight >}}
| | |`hardwareAddr` |HardwareAddr |Override the hardware (MAC) address of the link.
Show example(s){{< highlight yaml >}} hardwareAddr: 2e:3c:4d:5e:6f:70 {{< /highlight >}}
| | |`links` |[]string |Names of the links (interfaces) to be assigned to this vrf.
Link aliases can be used here as well.
Show example(s){{< highlight yaml >}} links: - enp1s3 - enp1s2 {{< /highlight >}}
| | |`table` |RoutingTable |Routing table number to use for this vrf.
Show example(s){{< highlight yaml >}} table: 10 {{< /highlight >}}
| | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## addresses[] {#VRFConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#VRFConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/network/wireguardconfig.md ================================================ --- description: WireguardConfig is a config document to create and configure a Wireguard network link. title: WireguardConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: WireguardConfig name: wg1 # Name of the Wireguard link (interface). privateKey: OJ34O6J1z4ZZB+t16c+vYrzIrKddxyU3Z2eLhwYzqE8= # Specifies a private key configuration (base64 encoded). listenPort: 51820 # Specifies a device's listening port (UDP). # Specifies a list of peer configurations to apply to a device. peers: - publicKey: fP+xJZvUA5n1Pi/f5wcPiV6tZ6fHwqcGaXe98NfEgkE= # Specifies the public key of this peer. endpoint: 10.0.0.1:5180 # Specifies the endpoint of this peer entry. # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. allowedIPs: - 192.168.2.0/24 - publicKey: TDd25Cwq6tMZANIKUaqred+Zt+09HtCqwFeOLtKQ9Cs= # Specifies the public key of this peer. presharedKey: UpH8htYK7yJBPg5+q4M/Tx0o5ipHbeSZtI/h/mHxOeU= # Specifies the preshared key for this peer (base64 encoded). # AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer. allowedIPs: - 192.168.3.0/24 mtu: 1420 # Configure LinkMTU (Maximum Transmission Unit) for the link. # Configure addresses to be statically assigned to the link. addresses: - address: 192.168.1.100/24 # IP address to be assigned to the link. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the Wireguard link (interface).
Show example(s){{< highlight yaml >}} name: wg.int {{< /highlight >}}
| | |`privateKey` |string |Specifies a private key configuration (base64 encoded).
Can be generated by `wg genkey`. | | |`listenPort` |int |Specifies a device's listening port (UDP).
If not specified, a random port will be chosen. | | |`firewallMark` |int |Specifies a device's firewall mark.
Useful for advanced routing setups, marking packets originating from this device. | | |`peers` |[]WireguardPeer |Specifies a list of peer configurations to apply to a device. | | |`up` |bool |Bring the link up or down.

If not specified, the link will be brought up. | | |`mtu` |uint32 |Configure LinkMTU (Maximum Transmission Unit) for the link.

If not specified, the system default LinkMTU will be used (usually 1500). | | |`addresses` |[]AddressConfig |Configure addresses to be statically assigned to the link. | | |`routes` |[]RouteConfig |Configure routes to be statically created via the link. | | |`multicast` |bool |Set the multicast capability of the link. | | ## peers[] {#WireguardConfig.peers.} WireguardPeer describes a Wireguard peer configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`publicKey` |string |Specifies the public key of this peer.
Can be extracted from private key by running `wg pubkey < private.key`. | | |`presharedKey` |string |Specifies the preshared key for this peer (base64 encoded).
Can be generated by `wg genpsk`.
Optional, this key provides an additional layer of symmetric-key cryptography
to the peer connection. | | |`endpoint` |AddrPort |Specifies the endpoint of this peer entry.
Format: :.
If not set, the peer should connect to us without us connecting to it first. | | |`persistentKeepaliveInterval` |Duration |Specifies the persistent keepalive interval for this peer.
Field format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes). | | |`allowedIPs` |[]Prefix |AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.
These IPs will be routed to this peer, and defines which IPs this peer is allowed to use. | | ## addresses[] {#WireguardConfig.addresses.} AddressConfig represents a network address configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`address` |Prefix |IP address to be assigned to the link.

This field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).
Show example(s){{< highlight yaml >}} address: 192.168.1.100/24 {{< /highlight >}}{{< highlight yaml >}} address: fd00::1/64 {{< /highlight >}}
| | |`routePriority` |uint32 |Configure the route priority (metric) for routes created for this address.

If not specified, the system default route priority will be used. | | ## routes[] {#WireguardConfig.routes.} RouteConfig represents a network route configuration. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |Prefix |The route's destination as an address prefix.

If not specified, a default route will be created for the address family of the gateway.
Show example(s){{< highlight yaml >}} destination: 10.0.0.0/8 {{< /highlight >}}
| | |`gateway` |Addr |The route's gateway (if empty, creates link scope route).
Show example(s){{< highlight yaml >}} gateway: 10.0.0.1 {{< /highlight >}}
| | |`source` |Addr |The route's source address (optional). | | |`metric` |uint32 |The optional metric for the route. | | |`mtu` |uint32 |The optional MTU for the route. | | |`table` |RoutingTable |The routing table to use for the route.

If not specified, the main routing table will be used. | | ================================================ FILE: website/content/v1.13/reference/configuration/runtime/_index.md ================================================ --- description: | Package runtime provides runtime machine configuration documents. title: runtime --- ================================================ FILE: website/content/v1.13/reference/configuration/runtime/environmentconfig.md ================================================ --- description: EnvironmentConfig is an environment config document. title: EnvironmentConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: EnvironmentConfig # This field allows for the addition of environment variables. variables: GRPC_GO_LOG_SEVERITY_LEVEL: info GRPC_GO_LOG_VERBOSITY_LEVEL: "99" https_proxy: http://SERVER:PORT/ {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`variables` |Env |This field allows for the addition of environment variables.
All environment variables are set on PID 1 in addition to every service.
Propagation of environment variables to services is done only at initial service start time.
To modify environment variables for services, the node must be restarted.
Multiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.
Fully removing an environment variable can only be achieved by removing it from the document and restarting the machine.
Environment variable names are validated, and should:
- start with an uppercase letter, lowercase letter, or an underscore (_) character, and
- contain only uppercase and lowercase letters, underscore (_) characters, and numbers.
Show example(s)Environment variables definition examples.:{{< highlight yaml >}} variables: GRPC_GO_LOG_SEVERITY_LEVEL: info GRPC_GO_LOG_VERBOSITY_LEVEL: "99" https_proxy: http://SERVER:PORT/ {{< /highlight >}}{{< highlight yaml >}} variables: GRPC_GO_LOG_SEVERITY_LEVEL: error https_proxy: https://USERNAME:PASSWORD@SERVER:PORT/ {{< /highlight >}}{{< highlight yaml >}} variables: https_proxy: http://DOMAIN\USERNAME:PASSWORD@SERVER:PORT/ {{< /highlight >}}
|``GRPC_GO_LOG_VERBOSITY_LEVEL``
``GRPC_GO_LOG_SEVERITY_LEVEL``
``http_proxy``
``https_proxy``
``no_proxy``
| ================================================ FILE: website/content/v1.13/reference/configuration/runtime/eventsinkconfig.md ================================================ --- description: EventSinkConfig is a event sink config document. title: EventSinkConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: EventSinkConfig endpoint: 192.168.10.3:3247 # The endpoint for the event sink as 'host:port'. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |string |The endpoint for the event sink as 'host:port'.
Show example(s){{< highlight yaml >}} endpoint: 10.3.7.3:2810 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/runtime/kmsglogconfig.md ================================================ --- description: KmsgLogConfig is a event sink config document. title: KmsgLogConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: KmsgLogConfig name: remote-log # Name of the config document. url: tcp://192.168.3.7:3478/ # The URL encodes the log destination. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the config document. | | |`url` |URL |The URL encodes the log destination.
The scheme must be tcp:// or udp://.
The path must be empty.
The port is required.
Show example(s){{< highlight yaml >}} url: udp://10.3.7.3:2810 {{< /highlight >}}
| | ================================================ FILE: website/content/v1.13/reference/configuration/runtime/oomconfig.md ================================================ --- description: OOMConfig is a Out of Memory handler config document. title: OOMConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: OOMConfig triggerExpression: |- # This expression defines when to trigger OOM action. multiply_qos_vectors(d_qos_memory_full_total, {System: 8.0, Podruntime: 4.0}) > 3000.0 && multiply_qos_vectors(qos_memory_full_avg10, {System: 1.0, Podruntime: 1.0}) > 5.0 || memory_full_avg10 > 75.0 && time_since_trigger > duration("10s") cgroupRankingExpression: 'memory_max.hasValue() ? 0.0 : ({Besteffort: 1.0, Burstable: 0.5, Guaranteed: 0.0, Podruntime: 0.0, System: 0.0}[class] * double(memory_current.orValue(0u)))' # This expression defines how to rank cgroups for OOM handler. sampleInterval: 100ms # How often should the trigger expression be evaluated. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`triggerExpression` |Expression |This expression defines when to trigger OOM action.

The expression must evaluate to a boolean value.
If the expression returns true, then OOM ranking and killing will be handled.

This expression receives the following parameters:
- memory_{some,full}_{avg10,avg60,avg300,total} - double, representing PSI values
- time_since_trigger - duration since the last OOM handler trigger event | | |`cgroupRankingExpression` |Expression |This expression defines how to rank cgroups for OOM handler.

The cgroup with the highest rank (score) will be evicted first.
The expression must evaluate to a double value.

This expression receives the following parameters:
- memory_max - Optional - in bytes
- memory_current - Optional - in bytes
- memory_peak - Optional - in bytes
- path - string, path to the cgroup
- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System | | |`sampleInterval` |Duration |How often should the trigger expression be evaluated.

This interval determines how often should the OOM controller
check for the OOM condition using the provided expression.
Adjusting it can help tune the reactivity of the OOM handler. | | ================================================ FILE: website/content/v1.13/reference/configuration/runtime/watchdogtimerconfig.md ================================================ --- description: WatchdogTimerConfig is a watchdog timer config document. title: WatchdogTimerConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: WatchdogTimerConfig device: /dev/watchdog0 # Path to the watchdog device. timeout: 2m0s # Timeout for the watchdog. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`device` |string |Path to the watchdog device.
Show example(s){{< highlight yaml >}} device: /dev/watchdog0 {{< /highlight >}}
| | |`timeout` |Duration |Timeout for the watchdog.

If Talos is unresponsive for this duration, the watchdog will reset the system.

Default value is 1 minute, minimum value is 10 seconds. | | ================================================ FILE: website/content/v1.13/reference/configuration/security/_index.md ================================================ --- description: | Package security provides security-related machine configuration documents. title: security --- ================================================ FILE: website/content/v1.13/reference/configuration/security/imageverificationconfig.md ================================================ --- description: ImageVerificationConfig configures image signature verification policy. title: ImageVerificationConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: ImageVerificationConfig # List of verification rules. rules: - image: registry.k8s.io/* # Image reference pattern to match for this rule. # Keyless verifier configuration to use for this rule. keyless: issuer: https://accounts.google.com # OIDC issuer URL for keyless verification. subject: krel-trust@k8s-releng-prod.iam.gserviceaccount.com # Expected subject for keyless verification. # # Regex pattern for subject matching. # subjectRegex: .*@example\.com - image: my-registry/* # Image reference pattern to match for this rule. # Public key verifier configuration to use for this rule. publicKey: certificate: |- # A public certificate in PEM format accepted for image signature verification. -----BEGIN CERTIFICATE----- MII--Sample Value-- -----END CERTIFICATE----- - image: locahost:3000/* # Image reference pattern to match for this rule. deny: true # Deny pulling images matching the pattern (default: false). {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`rules` |[]ImageVerificationRuleV1Alpha1 |List of verification rules.
Rules are evaluated in order; first matching rule applies. | | ## rules[] {#ImageVerificationConfig.rules.} ImageVerificationRuleV1Alpha1 defines a verification rule. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |Image reference pattern to match for this rule.
Supports glob patterns.
Show example(s){{< highlight yaml >}} image: docker.io/library/nginx {{< /highlight >}}{{< highlight yaml >}} image: registry.k8s.io/* {{< /highlight >}}
| | |`skip` |bool |Skip verification for this image pattern (default: false). | | |`deny` |bool |Deny pulling images matching the pattern (default: false). | | |`keyless` |ImageKeylessVerifierV1Alpha1 |Keyless verifier configuration to use for this rule. | | |`publicKey` |ImagePublicKeyVerifierV1Alpha1 |Public key verifier configuration to use for this rule. | | ### keyless {#ImageVerificationConfig.rules..keyless} ImageKeylessVerifierV1Alpha1 configures a signature verification provider using Cosign keyless verification. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`issuer` |string |OIDC issuer URL for keyless verification.
Show example(s){{< highlight yaml >}} issuer: https://accounts.google.com {{< /highlight >}}{{< highlight yaml >}} issuer: https://token.actions.githubusercontent.com {{< /highlight >}}
| | |`subject` |string |Expected subject for keyless verification.

This is the identity (email, URI) that signed the image. | | |`subjectRegex` |string |Regex pattern for subject matching.

Use this instead of subject for flexible matching.
Show example(s){{< highlight yaml >}} subjectRegex: .*@example\.com {{< /highlight >}}
| | ### publicKey {#ImageVerificationConfig.rules..publicKey} ImagePublicKeyVerifierV1Alpha1 configures a signature verification provider using a static public key. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`certificate` |string |A public certificate in PEM format accepted for image signature verification. | | ================================================ FILE: website/content/v1.13/reference/configuration/security/trustedrootsconfig.md ================================================ --- description: TrustedRootsConfig allows to configure additional trusted CA roots. title: TrustedRootsConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: TrustedRootsConfig name: my-enterprise-ca # Name of the config document. certificates: | # List of additional trusted certificate authorities (as PEM-encoded certificates). -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the config document. | | |`certificates` |string |List of additional trusted certificate authorities (as PEM-encoded certificates).

Multiple certificates can be provided in a single config document, separated by newline characters. | | ================================================ FILE: website/content/v1.13/reference/configuration/siderolink/_index.md ================================================ --- description: | Package siderolink provides SideroLink machine configuration documents. title: siderolink --- ================================================ FILE: website/content/v1.13/reference/configuration/siderolink/siderolinkconfig.md ================================================ --- description: SideroLinkConfig is a SideroLink connection machine configuration document. title: SideroLinkConfig --- {{< highlight yaml >}} apiVersion: v1alpha1 kind: SideroLinkConfig apiUrl: https://siderolink.api/jointoken?token=secret # SideroLink API URL to connect to. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`apiUrl` |URL |SideroLink API URL to connect to.
Show example(s){{< highlight yaml >}} apiUrl: https://siderolink.api/?jointoken=secret {{< /highlight >}}
| | |`uniqueToken` |string |SideroLink unique token to use for the connection (optional).

This value is overridden with META key UniqueMachineToken. | | ================================================ FILE: website/content/v1.13/reference/configuration/v1alpha1/_index.md ================================================ --- description: | Package v1alpha1 contains definition of the `v1alpha1` configuration document. Even though the machine configuration in Talos Linux is multi-document, at the moment this configuration document contains most of the configuration options. It is expected that new configuration options will be added as new documents, and existing ones migrated to their own documents. title: v1alpha1 --- ================================================ FILE: website/content/v1.13/reference/configuration/v1alpha1/config.md ================================================ --- description: Config defines the v1alpha1.Config Talos machine configuration document. title: Config --- {{< highlight yaml >}} version: v1alpha1 machine: # ... cluster: # ... {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`version` |string |Indicates the schema used to decode the contents. |`v1alpha1`
| |`debug` |bool |Enable verbose logging to the console.
All system containers logs will flow into serial console.

**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput. |`true`
`yes`
`false`
`no`
| |`machine` |MachineConfig |Provides machine specific configuration options. | | |`cluster` |ClusterConfig |Provides cluster specific configuration options. | | ## machine {#Config.machine} MachineConfig represents the machine-specific config values. {{< highlight yaml >}} machine: type: controlplane # InstallConfig represents the installation options for preparing a node. install: disk: /dev/sda # The disk used for installations. image: ghcr.io/siderolabs/installer:latest # Allows for supplying the image used to perform the installation. wipe: false # Indicates if the installation disk should be wiped at installation time. grubUseUKICmdline: true # Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host. # # Look up disk using disk attributes like model, size, serial and others. # diskSelector: # size: 4GB # Disk size. # model: WDC* # Disk model `/sys/block//device/model`. # busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 # Disk bus path. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`type` |string |Defines the role of the machine within the cluster.

**Control Plane**

Control Plane node type designates the node as a control plane member.
This means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.

**Worker**

Worker node type designates the node as a worker node.
This means it will be an available compute node for scheduling workloads.

This node type was previously known as "join"; that value is still supported but deprecated. |`controlplane`
`worker`
| |`token` |string |The `token` is used by a machine to join the PKI of the cluster.
Using this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.
Show example(s)example token:{{< highlight yaml >}} token: 328hom.uqjzh6jnn2eie9oi {{< /highlight >}}
| | |`ca` |PEMEncodedCertificateAndKey |The root certificate authority of the PKI.
It is composed of a base64 encoded `crt` and `key`.
Show example(s)machine CA example:{{< highlight yaml >}} ca: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== {{< /highlight >}}
| | |`acceptedCAs` |[]PEMEncodedCertificate |The certificates issued by certificate authorities are accepted in addition to issuing 'ca'.
It is composed of a base64 encoded `crt``. | | |`certSANs` |[]string |Extra certificate subject alternative names for the machine's certificate.
By default, all non-loopback interface IPs are automatically added to the certificate's SANs.
Show example(s)Uncomment this to enable SANs.:{{< highlight yaml >}} certSANs: - 10.0.0.10 - 172.16.0.10 - 192.168.0.10 {{< /highlight >}}
| | |`controlPlane` |MachineControlPlaneConfig |Provides machine specific control plane configuration options.
Show example(s)ControlPlane definition example.:{{< highlight yaml >}} controlPlane: # Controller manager machine specific configuration options. controllerManager: disabled: false # Disable kube-controller-manager on the node. # Scheduler machine specific configuration options. scheduler: disabled: true # Disable kube-scheduler on the node. {{< /highlight >}}
| | |`kubelet` |KubeletConfig |Used to provide additional options to the kubelet.
Show example(s)Kubelet definition example.:{{< highlight yaml >}} kubelet: image: ghcr.io/siderolabs/kubelet:v1.36.0-alpha.2 # The `image` field is an optional reference to an alternative kubelet image. # The `extraArgs` field is used to provide additional flags to the kubelet. extraArgs: feature-gates: ServerSideApply=true # # The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list. # clusterDNS: # - 10.96.0.10 # - 169.254.2.53 # # The `extraMounts` field is used to add additional mounts to the kubelet container. # extraMounts: # - destination: /var/lib/example # Destination is the absolute path where the mount will be placed in the container. # type: bind # Type specifies the mount kind. # source: /var/lib/example # Source specifies the source path of the mount. # # Options are fstab style mount options. # options: # - bind # - rshared # - rw # # The `extraConfig` field is used to provide kubelet configuration overrides. # extraConfig: # serverTLSBootstrap: true # # The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. # credentialProviderConfig: # apiVersion: kubelet.config.k8s.io/v1 # kind: CredentialProviderConfig # providers: # - apiVersion: credentialprovider.kubelet.k8s.io/v1 # defaultCacheDuration: 12h # matchImages: # - '*.dkr.ecr.*.amazonaws.com' # - '*.dkr.ecr.*.amazonaws.com.cn' # - '*.dkr.ecr-fips.*.amazonaws.com' # - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' # - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' # name: ecr-credential-provider # # The `nodeIP` field is used to configure `--node-ip` flag for the kubelet. # nodeIP: # # The `validSubnets` field configures the networks to pick kubelet node IP from. # validSubnets: # - 10.0.0.0/8 # - '!10.0.0.3/32' # - fdc7::/16 {{< /highlight >}}
| | |`pods` |[]Unstructured |Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.

Static pods can be used to run components which should be started before the Kubernetes control plane is up.
Talos doesn't validate the pod definition.
Updates to this field can be applied without a reboot.

See https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.
Show example(s)nginx static pod.:{{< highlight yaml >}} pods: - apiVersion: v1 kind: pod metadata: name: nginx spec: containers: - image: nginx name: nginx {{< /highlight >}}
| | |`install` |InstallConfig |Used to provide instructions for installations.

Note that this configuration section gets silently ignored by Talos images that are considered pre-installed.
To make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.
Show example(s)MachineInstall config usage example.:{{< highlight yaml >}} install: disk: /dev/sda # The disk used for installations. image: ghcr.io/siderolabs/installer:latest # Allows for supplying the image used to perform the installation. wipe: false # Indicates if the installation disk should be wiped at installation time. grubUseUKICmdline: true # Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host. # # Look up disk using disk attributes like model, size, serial and others. # diskSelector: # size: 4GB # Disk size. # model: WDC* # Disk model `/sys/block//device/model`. # busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 # Disk bus path. {{< /highlight >}}
| | |`files` |[]MachineFile |Allows the addition of user specified files.
The value of `op` can be `create`, `overwrite`, or `append`.
In the case of `create`, `path` must not exist.
In the case of `overwrite`, and `append`, `path` must be a valid file.
If an `op` value of `append` is used, the existing file will be appended.
Note that the file contents are not required to be base64 encoded.
Show example(s)MachineFiles usage example.:{{< highlight yaml >}} files: - content: '...' # The contents of the file. permissions: 0o666 # The file's permissions in octal. path: /tmp/file.txt # The path of the file. op: append # The operation to use {{< /highlight >}}
| | |`sysctls` |map[string]string |Used to configure the machine's sysctls.
Show example(s)MachineSysctls usage example.:{{< highlight yaml >}} sysctls: kernel.domainname: talos.dev net.ipv4.ip_forward: "0" net/ipv6/conf/eth0.100/disable_ipv6: "1" {{< /highlight >}}
| | |`sysfs` |map[string]string |Used to configure the machine's sysfs.
Show example(s)MachineSysfs usage example.:{{< highlight yaml >}} sysfs: devices.system.cpu.cpu0.cpufreq.scaling_governor: performance {{< /highlight >}}
| | |`features` |FeaturesConfig |Features describe individual Talos features that can be switched on or off.
Show example(s){{< highlight yaml >}} features: diskQuotaSupport: true # Enable XFS project quota support for EPHEMERAL partition and user disks. # # Configure Talos API access from Kubernetes pods. # kubernetesTalosAPIAccess: # enabled: true # Enable Talos API access from Kubernetes pods. # # The list of Talos API roles which can be granted for access from Kubernetes pods. # allowedRoles: # - os:reader # # The list of Kubernetes namespaces Talos API access is available from. # allowedKubernetesNamespaces: # - kube-system {{< /highlight >}}
| | |`udev` |UdevConfig |Configures the udev system.
Show example(s){{< highlight yaml >}} udev: # List of udev rules to apply to the udev system rules: - SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="44", MODE="0660" {{< /highlight >}}
| | |`logging` |LoggingConfig |Configures the logging system.
Show example(s){{< highlight yaml >}} logging: # Logging destination. destinations: - endpoint: tcp://1.2.3.4:12345 # Where to send logs. Supported protocols are "tcp" and "udp". format: json_lines # Logs format. {{< /highlight >}}
| | |`kernel` |KernelConfig |Configures the kernel.
Show example(s){{< highlight yaml >}} kernel: # Kernel modules to load. modules: - name: btrfs # Module name. {{< /highlight >}}
| | |`seccompProfiles` |[]MachineSeccompProfile |Configures the seccomp profiles for the machine.
Show example(s){{< highlight yaml >}} seccompProfiles: - name: audit.json # The `name` field is used to provide the file name of the seccomp profile. # The `value` field is used to provide the seccomp profile. value: defaultAction: SCMP_ACT_LOG {{< /highlight >}}
| | |`baseRuntimeSpecOverrides` |Unstructured |Override (patch) settings in the default OCI runtime spec for CRI containers.

It can be used to set some default container settings which are not configurable in Kubernetes,
for example default ulimits.
Note: this change applies to all newly created containers, and it requires a reboot to take effect.
Show example(s)override default open file limit:{{< highlight yaml >}} baseRuntimeSpecOverrides: process: rlimits: - hard: 1024 soft: 1024 type: RLIMIT_NOFILE {{< /highlight >}}
| | |`nodeLabels` |map[string]string |Configures the node labels for the machine.

Note: In the default Kubernetes configuration, worker nodes are restricted to set
labels with some prefixes (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).
Show example(s)node labels example.:{{< highlight yaml >}} nodeLabels: exampleLabel: exampleLabelValue {{< /highlight >}}
| | |`nodeAnnotations` |map[string]string |Configures the node annotations for the machine.
Show example(s)node annotations example.:{{< highlight yaml >}} nodeAnnotations: customer.io/rack: r13a25 {{< /highlight >}}
| | |`nodeTaints` |map[string]string |Configures the node taints for the machine. Effect is optional.

Note: In the default Kubernetes configuration, worker nodes are not allowed to
modify the taints (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).
Show example(s)node taints example.:{{< highlight yaml >}} nodeTaints: exampleTaint: exampleTaintValue:NoSchedule {{< /highlight >}}
| | ### controlPlane {#Config.machine.controlPlane} MachineControlPlaneConfig machine specific configuration options. {{< highlight yaml >}} machine: controlPlane: # Controller manager machine specific configuration options. controllerManager: disabled: false # Disable kube-controller-manager on the node. # Scheduler machine specific configuration options. scheduler: disabled: true # Disable kube-scheduler on the node. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`controllerManager` |MachineControllerManagerConfig |Controller manager machine specific configuration options. | | |`scheduler` |MachineSchedulerConfig |Scheduler machine specific configuration options. | | #### controllerManager {#Config.machine.controlPlane.controllerManager} MachineControllerManagerConfig represents the machine specific ControllerManager config values. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable kube-controller-manager on the node. | | #### scheduler {#Config.machine.controlPlane.scheduler} MachineSchedulerConfig represents the machine specific Scheduler config values. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable kube-scheduler on the node. | | ### kubelet {#Config.machine.kubelet} KubeletConfig represents the kubelet config values. {{< highlight yaml >}} machine: kubelet: image: ghcr.io/siderolabs/kubelet:v1.36.0-alpha.2 # The `image` field is an optional reference to an alternative kubelet image. # The `extraArgs` field is used to provide additional flags to the kubelet. extraArgs: feature-gates: ServerSideApply=true # # The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list. # clusterDNS: # - 10.96.0.10 # - 169.254.2.53 # # The `extraMounts` field is used to add additional mounts to the kubelet container. # extraMounts: # - destination: /var/lib/example # Destination is the absolute path where the mount will be placed in the container. # type: bind # Type specifies the mount kind. # source: /var/lib/example # Source specifies the source path of the mount. # # Options are fstab style mount options. # options: # - bind # - rshared # - rw # # The `extraConfig` field is used to provide kubelet configuration overrides. # extraConfig: # serverTLSBootstrap: true # # The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration. # credentialProviderConfig: # apiVersion: kubelet.config.k8s.io/v1 # kind: CredentialProviderConfig # providers: # - apiVersion: credentialprovider.kubelet.k8s.io/v1 # defaultCacheDuration: 12h # matchImages: # - '*.dkr.ecr.*.amazonaws.com' # - '*.dkr.ecr.*.amazonaws.com.cn' # - '*.dkr.ecr-fips.*.amazonaws.com' # - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' # - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' # name: ecr-credential-provider # # The `nodeIP` field is used to configure `--node-ip` flag for the kubelet. # nodeIP: # # The `validSubnets` field configures the networks to pick kubelet node IP from. # validSubnets: # - 10.0.0.0/8 # - '!10.0.0.3/32' # - fdc7::/16 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |The `image` field is an optional reference to an alternative kubelet image.
Show example(s){{< highlight yaml >}} image: ghcr.io/siderolabs/kubelet:v1.36.0-alpha.2 {{< /highlight >}}
| | |`clusterDNS` |[]string |The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.
Show example(s){{< highlight yaml >}} clusterDNS: - 10.96.0.10 - 169.254.2.53 {{< /highlight >}}
| | |`extraArgs` |Args |The `extraArgs` field is used to provide additional flags to the kubelet.
Show example(s){{< highlight yaml >}} extraArgs: key: value {{< /highlight >}}{{< highlight yaml >}} extraArgs: key: - value1 - value2 {{< /highlight >}}
| | |`extraMounts` |[]ExtraMount |The `extraMounts` field is used to add additional mounts to the kubelet container.
Note that either `bind` or `rbind` are required in the `options`.
Show example(s){{< highlight yaml >}} extraMounts: - destination: /var/lib/example # Destination is the absolute path where the mount will be placed in the container. type: bind # Type specifies the mount kind. source: /var/lib/example # Source specifies the source path of the mount. # Options are fstab style mount options. options: - bind - rshared - rw {{< /highlight >}}
| | |`extraConfig` |Unstructured |The `extraConfig` field is used to provide kubelet configuration overrides.

Some fields are not allowed to be overridden: authentication and authorization, cgroups
configuration, ports, etc.
Show example(s){{< highlight yaml >}} extraConfig: serverTLSBootstrap: true {{< /highlight >}}
| | |`credentialProviderConfig` |Unstructured |The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.
Show example(s){{< highlight yaml >}} credentialProviderConfig: apiVersion: kubelet.config.k8s.io/v1 kind: CredentialProviderConfig providers: - apiVersion: credentialprovider.kubelet.k8s.io/v1 defaultCacheDuration: 12h matchImages: - '*.dkr.ecr.*.amazonaws.com' - '*.dkr.ecr.*.amazonaws.com.cn' - '*.dkr.ecr-fips.*.amazonaws.com' - '*.dkr.ecr.us-iso-east-1.c2s.ic.gov' - '*.dkr.ecr.us-isob-east-1.sc2s.sgov.gov' name: ecr-credential-provider {{< /highlight >}}
| | |`defaultRuntimeSeccompProfileEnabled` |bool |Enable container runtime default Seccomp profile. |`true`
`yes`
`false`
`no`
| |`registerWithFQDN` |bool |The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.
This is required in clouds like AWS. |`true`
`yes`
`false`
`no`
| |`nodeIP` |KubeletNodeIPConfig |The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.
This is used when a node has multiple addresses to choose from.
Show example(s){{< highlight yaml >}} nodeIP: # The `validSubnets` field configures the networks to pick kubelet node IP from. validSubnets: - 10.0.0.0/8 - '!10.0.0.3/32' - fdc7::/16 {{< /highlight >}}
| | |`skipNodeRegistration` |bool |The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.
This runs kubelet as standalone and only runs static pods. |`true`
`yes`
`false`
`no`
| |`disableManifestsDirectory` |bool |The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.
It's recommended to configure static pods with the "pods" key instead. |`true`
`yes`
`false`
`no`
| #### extraMounts[] {#Config.machine.kubelet.extraMounts.} ExtraMount wraps OCI Mount specification. {{< highlight yaml >}} machine: kubelet: extraMounts: - destination: /var/lib/example # Destination is the absolute path where the mount will be placed in the container. type: bind # Type specifies the mount kind. source: /var/lib/example # Source specifies the source path of the mount. # Options are fstab style mount options. options: - bind - rshared - rw {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destination` |string |Destination is the absolute path where the mount will be placed in the container. | | |`type` |string |Type specifies the mount kind. | | |`source` |string |Source specifies the source path of the mount. | | |`options` |[]string |Options are fstab style mount options. | | |`uidMappings` |[]LinuxIDMapping |UID/GID mappings used for changing file owners w/o calling chown, fs should support it.

Every mount point could have its own mapping. | | |`gidMappings` |[]LinuxIDMapping |UID/GID mappings used for changing file owners w/o calling chown, fs should support it.

Every mount point could have its own mapping. | | ##### uidMappings[] {#Config.machine.kubelet.extraMounts..uidMappings.} LinuxIDMapping represents the Linux ID mapping. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`containerID` |uint32 |ContainerID is the starting UID/GID in the container. | | |`hostID` |uint32 |HostID is the starting UID/GID on the host to be mapped to 'ContainerID'. | | |`size` |uint32 |Size is the number of IDs to be mapped. | | ##### gidMappings[] {#Config.machine.kubelet.extraMounts..gidMappings.} LinuxIDMapping represents the Linux ID mapping. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`containerID` |uint32 |ContainerID is the starting UID/GID in the container. | | |`hostID` |uint32 |HostID is the starting UID/GID on the host to be mapped to 'ContainerID'. | | |`size` |uint32 |Size is the number of IDs to be mapped. | | #### nodeIP {#Config.machine.kubelet.nodeIP} KubeletNodeIPConfig represents the kubelet node IP configuration. {{< highlight yaml >}} machine: kubelet: nodeIP: # The `validSubnets` field configures the networks to pick kubelet node IP from. validSubnets: - 10.0.0.0/8 - '!10.0.0.3/32' - fdc7::/16 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`validSubnets` |[]string |The `validSubnets` field configures the networks to pick kubelet node IP from.
For dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.
IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.
Negative subnet matches should be specified last to filter out IPs picked by positive matches.
If not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both. | | ### install {#Config.machine.install} InstallConfig represents the installation options for preparing a node. {{< highlight yaml >}} machine: install: disk: /dev/sda # The disk used for installations. image: ghcr.io/siderolabs/installer:latest # Allows for supplying the image used to perform the installation. wipe: false # Indicates if the installation disk should be wiped at installation time. grubUseUKICmdline: true # Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host. # # Look up disk using disk attributes like model, size, serial and others. # diskSelector: # size: 4GB # Disk size. # model: WDC* # Disk model `/sys/block//device/model`. # busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 # Disk bus path. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disk` |string |The disk used for installations.
Show example(s){{< highlight yaml >}} disk: /dev/sda {{< /highlight >}}{{< highlight yaml >}} disk: /dev/nvme0 {{< /highlight >}}
| | |`diskSelector` |InstallDiskSelector |Look up disk using disk attributes like model, size, serial and others.
Always has priority over `disk`.
Show example(s){{< highlight yaml >}} diskSelector: size: '>= 1TB' # Disk size. model: WDC* # Disk model `/sys/block//device/model`. # # Disk bus path. # busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 # busPath: /pci0000:00/* {{< /highlight >}}
| | |`image` |string |Allows for supplying the image used to perform the installation.
Image reference for each Talos release can be found on
[GitHub releases page](https://github.com/siderolabs/talos/releases).
Show example(s){{< highlight yaml >}} image: ghcr.io/siderolabs/installer:latest {{< /highlight >}}
| | |`wipe` |bool |Indicates if the installation disk should be wiped at installation time.
Defaults to `true`. |`true`
`yes`
`false`
`no`
| |`legacyBIOSSupport` |bool |Indicates if MBR partition should be marked as bootable (active).
Should be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme. | | |`grubUseUKICmdline` |bool |Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.
This changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot. | | #### diskSelector {#Config.machine.install.diskSelector} InstallDiskSelector represents a disk query parameters for the install disk lookup. {{< highlight yaml >}} machine: install: diskSelector: size: '>= 1TB' # Disk size. model: WDC* # Disk model `/sys/block//device/model`. # # Disk bus path. # busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 # busPath: /pci0000:00/* {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`size` |InstallDiskSizeMatcher |Disk size.
Show example(s)Select a disk which size is equal to 4GB.:{{< highlight yaml >}} size: 4GB {{< /highlight >}}Select a disk which size is greater than 1TB.:{{< highlight yaml >}} size: '> 1TB' {{< /highlight >}}Select a disk which size is less or equal than 2TB.:{{< highlight yaml >}} size: <= 2TB {{< /highlight >}}
| | |`name` |string |Disk name `/sys/block//device/name`. | | |`model` |string |Disk model `/sys/block//device/model`. | | |`serial` |string |Disk serial number `/sys/block//serial`. | | |`modalias` |string |Disk modalias `/sys/block//device/modalias`. | | |`uuid` |string |Disk UUID `/sys/block//uuid`. | | |`wwid` |string |Disk WWID `/sys/block//wwid`. | | |`type` |InstallDiskType |Disk Type. |`ssd`
`hdd`
`nvme`
`sd`
| |`busPath` |string |Disk bus path.
Show example(s){{< highlight yaml >}} busPath: /pci0000:00/0000:00:17.0/ata1/host0/target0:0:0/0:0:0:0 {{< /highlight >}}{{< highlight yaml >}} busPath: /pci0000:00/* {{< /highlight >}}
| | ### files[] {#Config.machine.files.} MachineFile represents a file to write to disk. {{< highlight yaml >}} machine: files: - content: '...' # The contents of the file. permissions: 0o666 # The file's permissions in octal. path: /tmp/file.txt # The path of the file. op: append # The operation to use {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`content` |string |The contents of the file. | | |`permissions` |FileMode |The file's permissions in octal. | | |`path` |string |The path of the file. | | |`op` |string |The operation to use |`create`
`append`
`overwrite`
| ### features {#Config.machine.features} FeaturesConfig describes individual Talos features that can be switched on or off. {{< highlight yaml >}} machine: features: diskQuotaSupport: true # Enable XFS project quota support for EPHEMERAL partition and user disks. # # Configure Talos API access from Kubernetes pods. # kubernetesTalosAPIAccess: # enabled: true # Enable Talos API access from Kubernetes pods. # # The list of Talos API roles which can be granted for access from Kubernetes pods. # allowedRoles: # - os:reader # # The list of Kubernetes namespaces Talos API access is available from. # allowedKubernetesNamespaces: # - kube-system {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`kubernetesTalosAPIAccess` |KubernetesTalosAPIAccessConfig |Configure Talos API access from Kubernetes pods.

This feature is disabled if the feature config is not specified.
Show example(s){{< highlight yaml >}} kubernetesTalosAPIAccess: enabled: true # Enable Talos API access from Kubernetes pods. # The list of Talos API roles which can be granted for access from Kubernetes pods. allowedRoles: - os:reader # The list of Kubernetes namespaces Talos API access is available from. allowedKubernetesNamespaces: - kube-system {{< /highlight >}}
| | |`diskQuotaSupport` |bool |Enable XFS project quota support for EPHEMERAL partition and user disks.
Also enables kubelet tracking of ephemeral disk usage in the kubelet via quota. | | |`kubePrism` |KubePrism |KubePrism - local proxy/load balancer on defined port that will distribute
requests to all API servers in the cluster. | | |`hostDNS` |HostDNSConfig |Configures host DNS caching resolver. | | |`imageCache` |ImageCacheConfig |Enable Image Cache feature. | | |`nodeAddressSortAlgorithm` |string |Select the node address sort algorithm.
The 'v1' algorithm sorts addresses by the address itself.
The 'v2' algorithm prefers more specific prefixes.
If unset, defaults to 'v1'. | | #### kubernetesTalosAPIAccess {#Config.machine.features.kubernetesTalosAPIAccess} KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods. {{< highlight yaml >}} machine: features: kubernetesTalosAPIAccess: enabled: true # Enable Talos API access from Kubernetes pods. # The list of Talos API roles which can be granted for access from Kubernetes pods. allowedRoles: - os:reader # The list of Kubernetes namespaces Talos API access is available from. allowedKubernetesNamespaces: - kube-system {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable Talos API access from Kubernetes pods. | | |`allowedRoles` |[]string |The list of Talos API roles which can be granted for access from Kubernetes pods.

Empty list means that no roles can be granted, so access is blocked. | | |`allowedKubernetesNamespaces` |[]string |The list of Kubernetes namespaces Talos API access is available from. | | #### kubePrism {#Config.machine.features.kubePrism} KubePrism describes the configuration for the KubePrism load balancer. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable KubePrism support - will start local load balancing proxy. | | |`port` |int |KubePrism port. | | #### hostDNS {#Config.machine.features.hostDNS} HostDNSConfig describes the configuration for the host DNS resolver. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable host DNS caching resolver. | | |`forwardKubeDNSToHost` |bool |Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.

When enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of
using configured upstream DNS resolvers directly). | | |`resolveMemberNames` |bool |Resolve member hostnames using the host DNS resolver.

When enabled, cluster member hostnames and node names are resolved using the host DNS resolver.
This requires service discovery to be enabled. | | #### imageCache {#Config.machine.features.imageCache} ImageCacheConfig describes the configuration for the Image Cache feature. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`localEnabled` |bool |Enable local image cache. | | ### udev {#Config.machine.udev} UdevConfig describes how the udev system should be configured. {{< highlight yaml >}} machine: udev: # List of udev rules to apply to the udev system rules: - SUBSYSTEM=="drm", KERNEL=="renderD*", GROUP="44", MODE="0660" {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`rules` |[]string |List of udev rules to apply to the udev system | | ### logging {#Config.machine.logging} LoggingConfig struct configures Talos logging. {{< highlight yaml >}} machine: logging: # Logging destination. destinations: - endpoint: tcp://1.2.3.4:12345 # Where to send logs. Supported protocols are "tcp" and "udp". format: json_lines # Logs format. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`destinations` |[]LoggingDestination |Logging destination. | | #### destinations[] {#Config.machine.logging.destinations.} LoggingDestination struct configures Talos logging destination. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |Endpoint |Where to send logs. Supported protocols are "tcp" and "udp".
Show example(s){{< highlight yaml >}} endpoint: udp://127.0.0.1:12345 {{< /highlight >}}{{< highlight yaml >}} endpoint: tcp://1.2.3.4:12345 {{< /highlight >}}
| | |`format` |string |Logs format. |`json_lines`
| |`extraTags` |map[string]string |Extra tags (key-value) pairs to attach to every log message sent. | | ##### endpoint {#Config.machine.logging.destinations..endpoint} Endpoint represents the endpoint URL parsed out of the machine config. {{< highlight yaml >}} machine: logging: destinations: - endpoint: https://1.2.3.4:6443 {{< /highlight >}} {{< highlight yaml >}} machine: logging: destinations: - endpoint: https://cluster1.internal:6443 {{< /highlight >}} {{< highlight yaml >}} machine: logging: destinations: - endpoint: udp://127.0.0.1:12345 {{< /highlight >}} {{< highlight yaml >}} machine: logging: destinations: - endpoint: tcp://1.2.3.4:12345 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| ### kernel {#Config.machine.kernel} KernelConfig struct configures Talos Linux kernel. {{< highlight yaml >}} machine: kernel: # Kernel modules to load. modules: - name: btrfs # Module name. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`modules` |[]KernelModuleConfig |Kernel modules to load. | | #### modules[] {#Config.machine.kernel.modules.} KernelModuleConfig struct configures Linux kernel modules to load. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Module name. | | |`parameters` |[]string |Module parameters, changes applied after reboot. | | ### seccompProfiles[] {#Config.machine.seccompProfiles.} MachineSeccompProfile defines seccomp profiles for the machine. {{< highlight yaml >}} machine: seccompProfiles: - name: audit.json # The `name` field is used to provide the file name of the seccomp profile. # The `value` field is used to provide the seccomp profile. value: defaultAction: SCMP_ACT_LOG {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |The `name` field is used to provide the file name of the seccomp profile. | | |`value` |Unstructured |The `value` field is used to provide the seccomp profile. | | ## cluster {#Config.cluster} ClusterConfig represents the cluster-wide config values. {{< highlight yaml >}} cluster: # ControlPlaneConfig represents the control plane configuration options. controlPlane: endpoint: https://1.2.3.4 # Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. localAPIServerPort: 443 # The port that the API server listens on internally. clusterName: talos.local # ClusterNetworkConfig represents kube networking configuration options. network: # The CNI used. cni: name: flannel # Name of CNI to use. dnsDomain: cluster.local # The domain used by Kubernetes DNS. # The pod subnet CIDR. podSubnets: - 10.244.0.0/16 # The service subnet CIDR. serviceSubnets: - 10.96.0.0/12 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`id` |string |Globally unique identifier for this cluster (base64 encoded random 32 bytes). | | |`secret` |string |Shared secret of cluster (base64 encoded random 32 bytes).
This secret is shared among cluster members but should never be sent over the network. | | |`controlPlane` |ControlPlaneConfig |Provides control plane specific configuration options.
Show example(s)Setting controlplane endpoint address to 1.2.3.4 and port to 443 example.:{{< highlight yaml >}} controlPlane: endpoint: https://1.2.3.4 # Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. localAPIServerPort: 443 # The port that the API server listens on internally. {{< /highlight >}}
| | |`clusterName` |string |Configures the cluster's name. | | |`network` |ClusterNetworkConfig |Provides cluster specific network configuration options.
Show example(s)Configuring with flannel CNI and setting up subnets.:{{< highlight yaml >}} network: # The CNI used. cni: name: flannel # Name of CNI to use. dnsDomain: cluster.local # The domain used by Kubernetes DNS. # The pod subnet CIDR. podSubnets: - 10.244.0.0/16 # The service subnet CIDR. serviceSubnets: - 10.96.0.0/12 {{< /highlight >}}
| | |`token` |string |The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.
Show example(s)Bootstrap token example (do not use in production!).:{{< highlight yaml >}} token: wlzjyw.bei2zfylhs2by0wd {{< /highlight >}}
| | |`aescbcEncryptionSecret` |string |A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).
Enables encryption with AESCBC.
Show example(s)Decryption secret example (do not use in production!).:{{< highlight yaml >}} aescbcEncryptionSecret: z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM= {{< /highlight >}}
| | |`secretboxEncryptionSecret` |string |A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).
Enables encryption with secretbox.
Secretbox has precedence over AESCBC.
Show example(s)Decryption secret example (do not use in production!).:{{< highlight yaml >}} secretboxEncryptionSecret: z01mye6j16bspJYtTB/5SFX8j7Ph4JXxM2Xuu4vsBPM= {{< /highlight >}}
| | |`ca` |PEMEncodedCertificateAndKey |The base64 encoded root certificate authority used by Kubernetes.
Show example(s)ClusterCA example.:{{< highlight yaml >}} ca: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== {{< /highlight >}}
| | |`acceptedCAs` |[]PEMEncodedCertificate |The list of base64 encoded accepted certificate authorities used by Kubernetes. | | |`aggregatorCA` |PEMEncodedCertificateAndKey |The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.

This CA can be self-signed.
Show example(s)AggregatorCA example.:{{< highlight yaml >}} aggregatorCA: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== {{< /highlight >}}
| | |`serviceAccount` |PEMEncodedKey |The base64 encoded private key for service account token generation.
Show example(s)AggregatorCA example.:{{< highlight yaml >}} serviceAccount: key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== {{< /highlight >}}
| | |`apiServer` |APIServerConfig |API server specific configuration options.
Show example(s){{< highlight yaml >}} apiServer: image: registry.k8s.io/kube-apiserver:v1.36.0-alpha.2 # The container image used in the API server manifest. # Extra arguments to supply to the API server. extraArgs: feature-gates: ServerSideApply=true http2-max-streams-per-connection: "32" # Extra certificate subject alternative names for the API server's certificate. certSANs: - 1.2.3.4 - 4.5.6.7 # # Configure the API server admission plugins. # admissionControl: # - name: PodSecurity # Name is the name of the admission controller. # # Configuration is an embedded configuration object to be used as the plugin's # configuration: # apiVersion: pod-security.admission.config.k8s.io/v1alpha1 # defaults: # audit: restricted # audit-version: latest # enforce: baseline # enforce-version: latest # warn: restricted # warn-version: latest # exemptions: # namespaces: # - kube-system # runtimeClasses: [] # usernames: [] # kind: PodSecurityConfiguration # # Configure the API server audit policy. # auditPolicy: # apiVersion: audit.k8s.io/v1 # kind: Policy # rules: # - level: Metadata # # Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration. # authorizationConfig: # - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. # name: webhook # Name is used to describe the authorizer. # # webhook is the configuration for the webhook authorizer. # webhook: # connectionInfo: # type: InClusterConfig # failurePolicy: Deny # matchConditionSubjectAccessReviewVersion: v1 # matchConditions: # - expression: has(request.resourceAttributes) # - expression: '!(\''system:serviceaccounts:kube-system\'' in request.groups)' # subjectAccessReviewVersion: v1 # timeout: 3s # - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. # name: in-cluster-authorizer # Name is used to describe the authorizer. # # webhook is the configuration for the webhook authorizer. # webhook: # connectionInfo: # type: InClusterConfig # failurePolicy: NoOpinion # matchConditionSubjectAccessReviewVersion: v1 # subjectAccessReviewVersion: v1 # timeout: 3s {{< /highlight >}}
| | |`controllerManager` |ControllerManagerConfig |Controller manager server specific configuration options.
Show example(s){{< highlight yaml >}} controllerManager: image: registry.k8s.io/kube-controller-manager:v1.36.0-alpha.2 # The container image used in the controller manager manifest. # Extra arguments to supply to the controller manager. extraArgs: feature-gates: ServerSideApply=true {{< /highlight >}}
| | |`proxy` |ProxyConfig |Kube-proxy server-specific configuration options
Show example(s){{< highlight yaml >}} proxy: image: registry.k8s.io/kube-proxy:v1.36.0-alpha.2 # The container image used in the kube-proxy manifest. mode: ipvs # proxy mode of kube-proxy. # Extra arguments to supply to kube-proxy. extraArgs: proxy-mode: iptables # # Disable kube-proxy deployment on cluster bootstrap. # disabled: false {{< /highlight >}}
| | |`scheduler` |SchedulerConfig |Scheduler server specific configuration options.
Show example(s){{< highlight yaml >}} scheduler: image: registry.k8s.io/kube-scheduler:v1.36.0-alpha.2 # The container image used in the scheduler manifest. # Extra arguments to supply to the scheduler. extraArgs: feature-gates: AllBeta=true {{< /highlight >}}
| | |`discovery` |ClusterDiscoveryConfig |Configures cluster member discovery.
Show example(s){{< highlight yaml >}} discovery: enabled: true # Enable the cluster membership discovery feature. # Configure registries used for cluster member discovery. registries: # Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information kubernetes: {} # Service registry is using an external service to push and pull information about cluster members. service: endpoint: https://discovery.talos.dev/ # External service endpoint. {{< /highlight >}}
| | |`etcd` |EtcdConfig |Etcd specific configuration options.
Show example(s){{< highlight yaml >}} etcd: image: registry.k8s.io/etcd:v3.6.8 # The container image used to create the etcd service. # The `ca` is the root certificate authority of the PKI. ca: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== # Extra arguments to supply to etcd. extraArgs: election-timeout: "5000" # # The `advertisedSubnets` field configures the networks to pick etcd advertised IP from. # advertisedSubnets: # - 10.0.0.0/8 {{< /highlight >}}
| | |`coreDNS` |CoreDNS |Core DNS specific configuration options.
Show example(s){{< highlight yaml >}} coreDNS: image: registry.k8s.io/coredns/coredns:v1.14.2 # The `image` field is an override to the default coredns image. {{< /highlight >}}
| | |`externalCloudProvider` |ExternalCloudProviderConfig |External cloud provider configuration.
Show example(s){{< highlight yaml >}} externalCloudProvider: enabled: true # Enable external cloud provider. # A list of urls that point to additional manifests for an external cloud provider. manifests: - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml {{< /highlight >}}
| | |`extraManifests` |[]string |A list of urls that point to additional manifests.
These will get automatically deployed as part of the bootstrap.
Show example(s){{< highlight yaml >}} extraManifests: - https://www.example.com/manifest1.yaml - https://www.example.com/manifest2.yaml {{< /highlight >}}
| | |`extraManifestHeaders` |map[string]string |A map of key value pairs that will be added while fetching the extraManifests.
Show example(s){{< highlight yaml >}} extraManifestHeaders: Token: "1234567" X-ExtraInfo: info {{< /highlight >}}
| | |`inlineManifests` |[]ClusterInlineManifest |A list of inline Kubernetes manifests.
These will get automatically deployed as part of the bootstrap.
Show example(s){{< highlight yaml >}} inlineManifests: - name: namespace-ci # Name of the manifest. contents: |- # Manifest contents as a string. apiVersion: v1 kind: Namespace metadata: name: ci {{< /highlight >}}
| | |`adminKubeconfig` |AdminKubeconfigConfig |Settings for admin kubeconfig generation.
Certificate lifetime can be configured.
Show example(s){{< highlight yaml >}} adminKubeconfig: certLifetime: 1h0m0s # Admin kubeconfig certificate lifetime (default is 1 year). {{< /highlight >}}
| | |`allowSchedulingOnControlPlanes` |bool |Allows running workload on control-plane nodes.
Show example(s){{< highlight yaml >}} allowSchedulingOnControlPlanes: true {{< /highlight >}}
|`true`
`yes`
`false`
`no`
| ### controlPlane {#Config.cluster.controlPlane} ControlPlaneConfig represents the control plane configuration options. {{< highlight yaml >}} cluster: controlPlane: endpoint: https://1.2.3.4 # Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname. localAPIServerPort: 443 # The port that the API server listens on internally. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`endpoint` |Endpoint |Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.
It is single-valued, and may optionally include a port number.
Show example(s){{< highlight yaml >}} endpoint: https://1.2.3.4:6443 {{< /highlight >}}{{< highlight yaml >}} endpoint: https://cluster1.internal:6443 {{< /highlight >}}
| | |`localAPIServerPort` |int |The port that the API server listens on internally.
This may be different than the port portion listed in the endpoint field above.
The default is `6443`. | | #### endpoint {#Config.cluster.controlPlane.endpoint} Endpoint represents the endpoint URL parsed out of the machine config. {{< highlight yaml >}} cluster: controlPlane: endpoint: https://1.2.3.4:6443 {{< /highlight >}} {{< highlight yaml >}} cluster: controlPlane: endpoint: https://cluster1.internal:6443 {{< /highlight >}} {{< highlight yaml >}} cluster: controlPlane: endpoint: udp://127.0.0.1:12345 {{< /highlight >}} {{< highlight yaml >}} cluster: controlPlane: endpoint: tcp://1.2.3.4:12345 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| ### network {#Config.cluster.network} ClusterNetworkConfig represents kube networking configuration options. {{< highlight yaml >}} cluster: network: # The CNI used. cni: name: flannel # Name of CNI to use. dnsDomain: cluster.local # The domain used by Kubernetes DNS. # The pod subnet CIDR. podSubnets: - 10.244.0.0/16 # The service subnet CIDR. serviceSubnets: - 10.96.0.0/12 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`cni` |CNIConfig |The CNI used.
Composed of "name" and "urls".
The "name" key supports the following options: "flannel", "custom", and "none".
"flannel" uses Talos-managed Flannel CNI, and that's the default option.
"custom" uses custom manifests that should be provided in "urls".
"none" indicates that Talos will not manage any CNI installation.
Show example(s){{< highlight yaml >}} cni: name: custom # Name of CNI to use. # URLs containing manifests to apply for the CNI. urls: - https://docs.projectcalico.org/archive/v3.20/manifests/canal.yaml {{< /highlight >}}
| | |`dnsDomain` |string |The domain used by Kubernetes DNS.
The default is `cluster.local`
Show example(s){{< highlight yaml >}} dnsDomain: cluster.local {{< /highlight >}}
| | |`podSubnets` |[]string |The pod subnet CIDR.
Show example(s){{< highlight yaml >}} podSubnets: - 10.244.0.0/16 {{< /highlight >}}
| | |`serviceSubnets` |[]string |The service subnet CIDR.
Show example(s){{< highlight yaml >}} serviceSubnets: - 10.96.0.0/12 {{< /highlight >}}
| | #### cni {#Config.cluster.network.cni} CNIConfig represents the CNI configuration options. {{< highlight yaml >}} cluster: network: cni: name: custom # Name of CNI to use. # URLs containing manifests to apply for the CNI. urls: - https://docs.projectcalico.org/archive/v3.20/manifests/canal.yaml {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of CNI to use. |`flannel`
`custom`
`none`
| |`urls` |[]string |URLs containing manifests to apply for the CNI.
Should be present for "custom", must be empty for "flannel" and "none". | | |`flannel` |FlannelCNIConfig |description: |
Flannel configuration options.
| | ##### flannel {#Config.cluster.network.cni.flannel} FlannelCNIConfig represents the Flannel CNI configuration options. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`extraArgs` |[]string |Extra arguments for 'flanneld'.
Show example(s){{< highlight yaml >}} extraArgs: - --iface-can-reach=192.168.1.1 {{< /highlight >}}
| | |`kubeNetworkPoliciesEnabled` |bool |Deploys kube-network-policies along with Flannel.

This enables Kubernetes Network Policies support in the cluster. | | ### apiServer {#Config.cluster.apiServer} APIServerConfig represents the kube apiserver configuration options. {{< highlight yaml >}} cluster: apiServer: image: registry.k8s.io/kube-apiserver:v1.36.0-alpha.2 # The container image used in the API server manifest. # Extra arguments to supply to the API server. extraArgs: feature-gates: ServerSideApply=true http2-max-streams-per-connection: "32" # Extra certificate subject alternative names for the API server's certificate. certSANs: - 1.2.3.4 - 4.5.6.7 # # Configure the API server admission plugins. # admissionControl: # - name: PodSecurity # Name is the name of the admission controller. # # Configuration is an embedded configuration object to be used as the plugin's # configuration: # apiVersion: pod-security.admission.config.k8s.io/v1alpha1 # defaults: # audit: restricted # audit-version: latest # enforce: baseline # enforce-version: latest # warn: restricted # warn-version: latest # exemptions: # namespaces: # - kube-system # runtimeClasses: [] # usernames: [] # kind: PodSecurityConfiguration # # Configure the API server audit policy. # auditPolicy: # apiVersion: audit.k8s.io/v1 # kind: Policy # rules: # - level: Metadata # # Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration. # authorizationConfig: # - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. # name: webhook # Name is used to describe the authorizer. # # webhook is the configuration for the webhook authorizer. # webhook: # connectionInfo: # type: InClusterConfig # failurePolicy: Deny # matchConditionSubjectAccessReviewVersion: v1 # matchConditions: # - expression: has(request.resourceAttributes) # - expression: '!(\''system:serviceaccounts:kube-system\'' in request.groups)' # subjectAccessReviewVersion: v1 # timeout: 3s # - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. # name: in-cluster-authorizer # Name is used to describe the authorizer. # # webhook is the configuration for the webhook authorizer. # webhook: # connectionInfo: # type: InClusterConfig # failurePolicy: NoOpinion # matchConditionSubjectAccessReviewVersion: v1 # subjectAccessReviewVersion: v1 # timeout: 3s {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |The container image used in the API server manifest.
Show example(s){{< highlight yaml >}} image: registry.k8s.io/kube-apiserver:v1.36.0-alpha.2 {{< /highlight >}}
| | |`extraArgs` |Args |Extra arguments to supply to the API server. | | |`extraVolumes` |[]VolumeMountConfig |Extra volumes to mount to the API server static pod. | | |`env` |Env |The `env` field allows for the addition of environment variables for the control plane component. | | |`certSANs` |[]string |Extra certificate subject alternative names for the API server's certificate. | | |`admissionControl` |[]AdmissionPluginConfig |Configure the API server admission plugins.
Show example(s){{< highlight yaml >}} admissionControl: - name: PodSecurity # Name is the name of the admission controller. # Configuration is an embedded configuration object to be used as the plugin's configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration {{< /highlight >}}
| | |`auditPolicy` |Unstructured |Configure the API server audit policy.
Show example(s){{< highlight yaml >}} auditPolicy: apiVersion: audit.k8s.io/v1 kind: Policy rules: - level: Metadata {{< /highlight >}}
| | |`resources` |ResourcesConfig |Configure the API server resources. | | |`authorizationConfig` |[]AuthorizationConfigAuthorizerConfig |Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.
Show example(s){{< highlight yaml >}} authorizationConfig: - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. name: webhook # Name is used to describe the authorizer. # webhook is the configuration for the webhook authorizer. webhook: connectionInfo: type: InClusterConfig failurePolicy: Deny matchConditionSubjectAccessReviewVersion: v1 matchConditions: - expression: has(request.resourceAttributes) - expression: '!(\''system:serviceaccounts:kube-system\'' in request.groups)' subjectAccessReviewVersion: v1 timeout: 3s - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. name: in-cluster-authorizer # Name is used to describe the authorizer. # webhook is the configuration for the webhook authorizer. webhook: connectionInfo: type: InClusterConfig failurePolicy: NoOpinion matchConditionSubjectAccessReviewVersion: v1 subjectAccessReviewVersion: v1 timeout: 3s {{< /highlight >}}
| | #### extraVolumes[] {#Config.cluster.apiServer.extraVolumes.} VolumeMountConfig struct describes extra volume mount for the static pods. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`hostPath` |string |Path on the host.
Show example(s){{< highlight yaml >}} hostPath: /var/lib/auth {{< /highlight >}}
| | |`mountPath` |string |Path in the container.
Show example(s){{< highlight yaml >}} mountPath: /etc/kubernetes/auth {{< /highlight >}}
| | |`readonly` |bool |Mount the volume read only.
Show example(s){{< highlight yaml >}} readonly: true {{< /highlight >}}
| | #### admissionControl[] {#Config.cluster.apiServer.admissionControl.} AdmissionPluginConfig represents the API server admission plugin configuration. {{< highlight yaml >}} cluster: apiServer: admissionControl: - name: PodSecurity # Name is the name of the admission controller. # Configuration is an embedded configuration object to be used as the plugin's configuration: apiVersion: pod-security.admission.config.k8s.io/v1alpha1 defaults: audit: restricted audit-version: latest enforce: baseline enforce-version: latest warn: restricted warn-version: latest exemptions: namespaces: - kube-system runtimeClasses: [] usernames: [] kind: PodSecurityConfiguration {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name is the name of the admission controller.
It must match the registered admission plugin name. | | |`configuration` |Unstructured |Configuration is an embedded configuration object to be used as the plugin's
configuration. | | #### resources {#Config.cluster.apiServer.resources} ResourcesConfig represents the pod resources. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`requests` |Unstructured |Requests configures the reserved cpu/memory resources.
Show example(s)resources requests.:{{< highlight yaml >}} requests: cpu: 1 memory: 1Gi {{< /highlight >}}
| | |`limits` |Unstructured |Limits configures the maximum cpu/memory resources a container can use.
Show example(s)resources requests.:{{< highlight yaml >}} limits: cpu: 2 memory: 2500Mi {{< /highlight >}}
| | #### authorizationConfig[] {#Config.cluster.apiServer.authorizationConfig.} AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration. {{< highlight yaml >}} cluster: apiServer: authorizationConfig: - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. name: webhook # Name is used to describe the authorizer. # webhook is the configuration for the webhook authorizer. webhook: connectionInfo: type: InClusterConfig failurePolicy: Deny matchConditionSubjectAccessReviewVersion: v1 matchConditions: - expression: has(request.resourceAttributes) - expression: '!(\''system:serviceaccounts:kube-system\'' in request.groups)' subjectAccessReviewVersion: v1 timeout: 3s - type: Webhook # Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. name: in-cluster-authorizer # Name is used to describe the authorizer. # webhook is the configuration for the webhook authorizer. webhook: connectionInfo: type: InClusterConfig failurePolicy: NoOpinion matchConditionSubjectAccessReviewVersion: v1 subjectAccessReviewVersion: v1 timeout: 3s {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`type` |string |Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`. | | |`name` |string |Name is used to describe the authorizer. | | |`webhook` |Unstructured |webhook is the configuration for the webhook authorizer. | | ### controllerManager {#Config.cluster.controllerManager} ControllerManagerConfig represents the kube controller manager configuration options. {{< highlight yaml >}} cluster: controllerManager: image: registry.k8s.io/kube-controller-manager:v1.36.0-alpha.2 # The container image used in the controller manager manifest. # Extra arguments to supply to the controller manager. extraArgs: feature-gates: ServerSideApply=true {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |The container image used in the controller manager manifest.
Show example(s){{< highlight yaml >}} image: registry.k8s.io/kube-controller-manager:v1.36.0-alpha.2 {{< /highlight >}}
| | |`extraArgs` |Args |Extra arguments to supply to the controller manager. | | |`extraVolumes` |[]VolumeMountConfig |Extra volumes to mount to the controller manager static pod. | | |`env` |Env |The `env` field allows for the addition of environment variables for the control plane component. | | |`resources` |ResourcesConfig |Configure the controller manager resources. | | #### extraVolumes[] {#Config.cluster.controllerManager.extraVolumes.} VolumeMountConfig struct describes extra volume mount for the static pods. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`hostPath` |string |Path on the host.
Show example(s){{< highlight yaml >}} hostPath: /var/lib/auth {{< /highlight >}}
| | |`mountPath` |string |Path in the container.
Show example(s){{< highlight yaml >}} mountPath: /etc/kubernetes/auth {{< /highlight >}}
| | |`readonly` |bool |Mount the volume read only.
Show example(s){{< highlight yaml >}} readonly: true {{< /highlight >}}
| | #### resources {#Config.cluster.controllerManager.resources} ResourcesConfig represents the pod resources. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`requests` |Unstructured |Requests configures the reserved cpu/memory resources.
Show example(s)resources requests.:{{< highlight yaml >}} requests: cpu: 1 memory: 1Gi {{< /highlight >}}
| | |`limits` |Unstructured |Limits configures the maximum cpu/memory resources a container can use.
Show example(s)resources requests.:{{< highlight yaml >}} limits: cpu: 2 memory: 2500Mi {{< /highlight >}}
| | ### proxy {#Config.cluster.proxy} ProxyConfig represents the kube proxy configuration options. {{< highlight yaml >}} cluster: proxy: image: registry.k8s.io/kube-proxy:v1.36.0-alpha.2 # The container image used in the kube-proxy manifest. mode: ipvs # proxy mode of kube-proxy. # Extra arguments to supply to kube-proxy. extraArgs: proxy-mode: iptables # # Disable kube-proxy deployment on cluster bootstrap. # disabled: false {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable kube-proxy deployment on cluster bootstrap.
Show example(s){{< highlight yaml >}} disabled: false {{< /highlight >}}
| | |`image` |string |The container image used in the kube-proxy manifest.
Show example(s){{< highlight yaml >}} image: registry.k8s.io/kube-proxy:v1.36.0-alpha.2 {{< /highlight >}}
| | |`mode` |string |proxy mode of kube-proxy.
The default is 'iptables'. | | |`extraArgs` |Args |Extra arguments to supply to kube-proxy. | | ### scheduler {#Config.cluster.scheduler} SchedulerConfig represents the kube scheduler configuration options. {{< highlight yaml >}} cluster: scheduler: image: registry.k8s.io/kube-scheduler:v1.36.0-alpha.2 # The container image used in the scheduler manifest. # Extra arguments to supply to the scheduler. extraArgs: feature-gates: AllBeta=true {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |The container image used in the scheduler manifest.
Show example(s){{< highlight yaml >}} image: registry.k8s.io/kube-scheduler:v1.36.0-alpha.2 {{< /highlight >}}
| | |`extraArgs` |Args |Extra arguments to supply to the scheduler. | | |`extraVolumes` |[]VolumeMountConfig |Extra volumes to mount to the scheduler static pod. | | |`env` |Env |The `env` field allows for the addition of environment variables for the control plane component. | | |`resources` |ResourcesConfig |Configure the scheduler resources. | | |`config` |Unstructured |Specify custom kube-scheduler configuration. | | #### extraVolumes[] {#Config.cluster.scheduler.extraVolumes.} VolumeMountConfig struct describes extra volume mount for the static pods. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`hostPath` |string |Path on the host.
Show example(s){{< highlight yaml >}} hostPath: /var/lib/auth {{< /highlight >}}
| | |`mountPath` |string |Path in the container.
Show example(s){{< highlight yaml >}} mountPath: /etc/kubernetes/auth {{< /highlight >}}
| | |`readonly` |bool |Mount the volume read only.
Show example(s){{< highlight yaml >}} readonly: true {{< /highlight >}}
| | #### resources {#Config.cluster.scheduler.resources} ResourcesConfig represents the pod resources. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`requests` |Unstructured |Requests configures the reserved cpu/memory resources.
Show example(s)resources requests.:{{< highlight yaml >}} requests: cpu: 1 memory: 1Gi {{< /highlight >}}
| | |`limits` |Unstructured |Limits configures the maximum cpu/memory resources a container can use.
Show example(s)resources requests.:{{< highlight yaml >}} limits: cpu: 2 memory: 2500Mi {{< /highlight >}}
| | ### discovery {#Config.cluster.discovery} ClusterDiscoveryConfig struct configures cluster membership discovery. {{< highlight yaml >}} cluster: discovery: enabled: true # Enable the cluster membership discovery feature. # Configure registries used for cluster member discovery. registries: # Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information kubernetes: {} # Service registry is using an external service to push and pull information about cluster members. service: endpoint: https://discovery.talos.dev/ # External service endpoint. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable the cluster membership discovery feature.
Cluster discovery is based on individual registries which are configured under the registries field. | | |`registries` |DiscoveryRegistriesConfig |Configure registries used for cluster member discovery. | | #### registries {#Config.cluster.discovery.registries} DiscoveryRegistriesConfig struct configures cluster membership discovery. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`kubernetes` |RegistryKubernetesConfig |Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information
as annotations on the Node resources.

This feature is deprecated as it is not compatible with Kubernetes 1.32+.
See https://github.com/siderolabs/talos/issues/9980 for more information. | | |`service` |RegistryServiceConfig |Service registry is using an external service to push and pull information about cluster members. | | ##### kubernetes {#Config.cluster.discovery.registries.kubernetes} RegistryKubernetesConfig struct configures Kubernetes discovery registry. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable Kubernetes discovery registry. | | ##### service {#Config.cluster.discovery.registries.service} RegistryServiceConfig struct configures Kubernetes discovery registry. | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable external service discovery registry. | | |`endpoint` |string |External service endpoint.
Show example(s){{< highlight yaml >}} endpoint: https://discovery.talos.dev/ {{< /highlight >}}
| | ### etcd {#Config.cluster.etcd} EtcdConfig represents the etcd configuration options. {{< highlight yaml >}} cluster: etcd: image: registry.k8s.io/etcd:v3.6.8 # The container image used to create the etcd service. # The `ca` is the root certificate authority of the PKI. ca: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== # Extra arguments to supply to etcd. extraArgs: election-timeout: "5000" # # The `advertisedSubnets` field configures the networks to pick etcd advertised IP from. # advertisedSubnets: # - 10.0.0.0/8 {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`image` |string |The container image used to create the etcd service.
Show example(s){{< highlight yaml >}} image: registry.k8s.io/etcd:v3.6.8 {{< /highlight >}}
| | |`ca` |PEMEncodedCertificateAndKey |The `ca` is the root certificate authority of the PKI.
It is composed of a base64 encoded `crt` and `key`.
Show example(s){{< highlight yaml >}} ca: crt: LS0tIEVYQU1QTEUgQ0VSVElGSUNBVEUgLS0t key: LS0tIEVYQU1QTEUgS0VZIC0tLQ== {{< /highlight >}}
| | |`extraArgs` |Args |Extra arguments to supply to etcd.
Note that the following args are not allowed:

- `name`
- `data-dir`
- `initial-cluster-state`
- `listen-peer-urls`
- `listen-client-urls`
- `cert-file`
- `key-file`
- `trusted-ca-file`
- `peer-client-cert-auth`
- `peer-cert-file`
- `peer-trusted-ca-file`
- `peer-key-file` | | |`advertisedSubnets` |[]string |The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.

IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.
Negative subnet matches should be specified last to filter out IPs picked by positive matches.
If not specified, advertised IP is selected as the first routable address of the node.
Show example(s){{< highlight yaml >}} advertisedSubnets: - 10.0.0.0/8 {{< /highlight >}}
| | |`listenSubnets` |[]string |The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.

If `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to
`advertisedSubnets`.

If neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.

IPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.
Negative subnet matches should be specified last to filter out IPs picked by positive matches.
If not specified, advertised IP is selected as the first routable address of the node. | | ### coreDNS {#Config.cluster.coreDNS} CoreDNS represents the CoreDNS config values. {{< highlight yaml >}} cluster: coreDNS: image: registry.k8s.io/coredns/coredns:v1.14.2 # The `image` field is an override to the default coredns image. {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`disabled` |bool |Disable coredns deployment on cluster bootstrap. | | |`image` |string |The `image` field is an override to the default coredns image. | | ### externalCloudProvider {#Config.cluster.externalCloudProvider} ExternalCloudProviderConfig contains external cloud provider configuration. {{< highlight yaml >}} cluster: externalCloudProvider: enabled: true # Enable external cloud provider. # A list of urls that point to additional manifests for an external cloud provider. manifests: - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`enabled` |bool |Enable external cloud provider. |`true`
`yes`
`false`
`no`
| |`manifests` |[]string |A list of urls that point to additional manifests for an external cloud provider.
These will get automatically deployed as part of the bootstrap.
Show example(s){{< highlight yaml >}} manifests: - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/rbac.yaml - https://raw.githubusercontent.com/kubernetes/cloud-provider-aws/v1.20.0-alpha.0/manifests/aws-cloud-controller-manager-daemonset.yaml {{< /highlight >}}
| | ### inlineManifests[] {#Config.cluster.inlineManifests.} ClusterInlineManifest struct describes inline bootstrap manifests for the user. {{< highlight yaml >}} cluster: inlineManifests: - name: namespace-ci # Name of the manifest. contents: |- # Manifest contents as a string. apiVersion: v1 kind: Namespace metadata: name: ci {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`name` |string |Name of the manifest.
Name should be unique.
Show example(s){{< highlight yaml >}} name: csi {{< /highlight >}}
| | |`contents` |string |Manifest contents as a string.
Show example(s){{< highlight yaml >}} contents: /etc/kubernetes/auth {{< /highlight >}}
| | ### adminKubeconfig {#Config.cluster.adminKubeconfig} AdminKubeconfigConfig contains admin kubeconfig settings. {{< highlight yaml >}} cluster: adminKubeconfig: certLifetime: 1h0m0s # Admin kubeconfig certificate lifetime (default is 1 year). {{< /highlight >}} | Field | Type | Description | Value(s) | |-------|------|-------------|----------| |`certLifetime` |Duration |Admin kubeconfig certificate lifetime (default is 1 year).
Field format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes). | | ================================================ FILE: website/content/v1.13/reference/kernel.md ================================================ --- title: "Kernel" description: "Linux kernel reference." --- ## Commandline Parameters Talos supports a number of kernel commandline parameters. Some are required for it to operate. Others are optional and useful in certain circumstances. Several of these are enforced by the Kernel Self Protection Project [KSPP](https://kspp.github.io/Recommended_Settings). **Required** parameters: * `talos.platform`: can be one of `akamai`, `aws`, `azure`, `container`, `digitalocean`, `equinixMetal`, `gcp`, `hcloud`, `metal`, `nocloud`, `openstack`, `oracle`, `scaleway`, `upcloud`, `vmware` or `vultr` * `slab_nomerge`: required by KSPP * `pti=on`: required by KSPP **Recommended** parameters: * `init_on_alloc=1`: advised by KSPP, enabled by default in kernel config * `init_on_free=1`: advised by KSPP, enabled by default in kernel config ### Available Talos-specific parameters #### `ip` Initial configuration of the interface, routes, DNS, NTP servers (multiple `ip=` kernel parameters are accepted). Full documentation is available in the [Linux kernel docs](https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt). `ip=:::::::::` Talos will use the configuration supplied via the kernel parameter as the initial network configuration. This parameter is useful in the environments where DHCP doesn't provide IP addresses or when default DNS and NTP servers should be overridden before loading machine configuration. Partial configuration can be applied as well, e.g. `ip=:::::::::` sets only the DNS and NTP servers. IPv6 addresses can be specified by enclosing them in the square brackets, e.g. `ip=[2001:db8::a]:[2001:db8::b]:[fe80::1]::controlplane1:eth1::[2001:4860:4860::6464]:[2001:4860:4860::64]:[2001:4860:4806::]`. `` can use either an IP address notation (IPv4: `255.255.255.0`, IPv6: `[ffff:ffff:ffff:ffff::0]`), or simply a number of one bits in the netmask (`24`). `` can be traditional interface naming scheme `eth0, eth1` or `enx`, example: `enx78e7d1ea46da` DHCP can be enabled by setting `` to `dhcp`, example: `ip=:::::eth0.3:dhcp`. Alternative syntax is `ip=eth0.3:dhcp`. #### `bond` Bond interface configuration. Full documentation is available in the [Dracut kernel docs](https://man7.org/linux/man-pages/man7/dracut.cmdline.7.html). `bond=:::` Talos will use the `bond=` kernel parameter if supplied to set the initial bond configuration. This parameter is useful in environments where the switch ports are suspended if the machine doesn't setup a LACP bond. If only the bond name is supplied, the bond will be created with `eth0` and `eth1` as slaves and bond mode set as `balance-rr` All these below configurations are equivalent: * `bond=bond0` * `bond=bond0:` * `bond=bond0::` * `bond=bond0:::` * `bond=bond0:eth0,eth1` * `bond=bond0:eth0,eth1:balance-rr` An example of a bond configuration with all options specified: `bond=bond1:eth3,eth4:mode=802.3ad,xmit_hash_policy=layer2+3:1450` This will create a bond interface named `bond1` with `eth3` and `eth4` as slaves and set the bond mode to `802.3ad`, the transmit hash policy to `layer2+3` and bond interface MTU to 1450. #### `vlan` The interface vlan configuration. Full documentation is available in the [Dracut kernel docs](https://man7.org/linux/man-pages/man7/dracut.cmdline.7.html). Talos will use the `vlan=` kernel parameter if supplied to set the initial vlan configuration. This parameter is useful in environments where the switch ports are VLAN tagged with no native VLAN. Only one vlan can be configured at this stage. An example of a vlan configuration including static ip configuration: `vlan=eth0.100:eth0 ip=172.20.0.2::172.20.0.1:255.255.255.0::eth0.100:::::` This will create a vlan interface named `eth0.100` with `eth0` as the underlying interface and set the vlan id to 100 with static IP 172.20.0.2/24 and 172.20.0.1 as default gateway. #### `net.ifnames=0` Disable the predictable network interface names by specifying `net.ifnames=0` on the kernel command line. #### `panic` The amount of time to wait after a panic before a reboot is issued. Talos will always reboot if it encounters an unrecoverable error. However, when collecting debug information, it may reboot too quickly for humans to read the logs. This option allows the user to delay the reboot to give time to collect debug information from the console screen. A value of `0` disables automatic rebooting entirely. #### `talos.config` The URL at which the machine configuration data may be found (only for `metal` platform, with the kernel parameter `talos.platform=metal`). This parameter supports variable substitution inside URL query values for the following case-insensitive placeholders: * `${uuid}` the SMBIOS UUID * `${serial}` the SMBIOS Serial Number * `${mac}` the MAC address of the first network interface attaining link state `up` * `${hostname}` the hostname of the machine The following example `http://example.com/metadata?h=${hostname}&m=${mac}&s=${serial}&u=${uuid}` may translate to `http://example.com/metadata?h=myTestHostname&m=52%3A2f%3Afd%3Adf%3Afc%3Ac0&s=0OCZJ19N65&u=40dcbd19-3b10-444e-bfff-aaee44a51fda` For backwards compatibility we insert the system UUID into the query parameter `uuid` if its value is empty. As in `http://example.com/metadata?uuid=` => `http://example.com/metadata?uuid=40dcbd19-3b10-444e-bfff-aaee44a51fda` ##### `metal-iso` When the kernel parameter `talos.config=metal-iso` is set, Talos will attempt to load the machine configuration from any block device with a filesystem label of `metal-iso`. Talos will look for a file named `config.yaml` in the root of the filesystem. For example, such ISO filesystem can be created with: ```sh mkdir iso/ cp config.yaml iso/ mkisofs -joliet -rock -volid 'metal-iso' -output config.iso iso/ ``` #### `talos.config.auth.*` Kernel parameters prefixed with `talos.config.auth.` are used to configure [OAuth2 authentication for the machine configuration]({{< relref "../advanced/machine-config-oauth" >}}). #### `talos.config.early` and `talos.config.inline` The kernel parameters `talos.config.early` and `talos.config.inline` are used to provide the initial machine configuration directly on the kernel command line. The difference between the two is the order of configuration loading: * first, the persisted configuration is loaded from the `STATE` partition, if it exists; * `talos.config.early` is tried next; * any platform-specific configuration source is tried next (e.g. `talos.config` for the `metal` platform, our VM user data source, etc.); * finally, `talos.config.inline` is tried; * if no complete configuration is found, the system will enter maintenance mode. For both arguments, the machine configuration should be `zstd` compressed and base64-encoded to be passed as a kernel parameter. > Note: The kernel command line has a limited size (~2000 bytes), so this method is only suitable for small configuration documents. One such example is to provide [a custom CA certificate]({{< relref "../talos-guides/configuration/certificate-authorities" >}}) via `TrustedRootsConfig` in the machine configuration: ```shell cat config.yaml | zstd --compress --ultra -22 | base64 -w 0 ``` Please note that configuration from this argument is only loaded if the configuration hasn't been yet saved to `STATE` partition. #### `talos.platform` The platform name on which Talos will run. Valid options are: * `akamai` * `aws` * `azure` * `container` * `digitalocean` * `equinixMetal` * `gcp` * `hcloud` * `metal` * `nocloud` * `openstack` * `oracle` * `scaleway` * `upcloud` * `vmware` * `vultr` #### `talos.hostname` The hostname to be used. The hostname is generally specified in the machine config. However, in some cases, the DHCP server needs to know the hostname before the machine configuration has been acquired. Unless specifically required, the machine configuration should be used instead. #### `talos.shutdown` The type of shutdown to use when Talos is told to shutdown. Valid options are: * `halt` * `poweroff` #### `talos.network.interface.ignore` A network interface which should be ignored and not configured by Talos. Before a configuration is applied (early on each boot), Talos attempts to configure each network interface by DHCP. If there are many network interfaces on the machine which have link but no DHCP server, this can add significant boot delays. This option may be specified multiple times for multiple network interfaces. #### `talos.experimental.wipe` Resets the disk before starting up the system. Valid options are: * `system` resets system disk. * `system:EPHEMERAL,STATE` resets ephemeral and state partitions. Doing this reverts Talos into maintenance mode. #### `talos.auditd.disabled` By default, Talos runs `auditd` service capturing kernel audit events. If you set `talos.auditd.disabled=1`, this behavior will be disabled, and you can run your own `auditd` service. #### `talos.dashboard.disabled` By default, Talos redirects kernel logs to virtual console `/dev/tty1` and starts the dashboard on `/dev/tty2`, then switches to the dashboard tty. If you set `talos.dashboard.disabled=1`, this behavior will be disabled. Kernel logs will be sent to the currently active console and the dashboard will not be started. It is set to be `1` by default on SBCs. #### `talos.dashboard.console` By default, the Talos dashboard runs on `/dev/tty2` with automatic TTY switching. You can specify a custom console device for the dashboard using this parameter. For example, to run the dashboard on a serial console: ```text talos.dashboard.console=ttyS0 ``` When this parameter is specified: * The dashboard will run on `/dev/ttyS0` * TTY switching will be disabled (no automatic switching between tty1 and tty2) * The console name must start with "tty" This is useful for headless servers or systems where you want the dashboard accessible through a serial console connection. > Note: If Talos dashboard is set to use ttyS0, make sure that Linux kernel command line doesn't include > `console=ttyS0` or similar, as it will conflict with the dashboard output. #### `talos.environment` Each value of the argument sets a default environment variable. The expected format is `key=value`. Example: ```text talos.environment=http_proxy=http://proxy.example.com:8080 talos.environment=https_proxy=http://proxy.example.com:8080 ``` #### `talos.device.settle_time` The time in Go duration format to wait for devices to settle before starting the boot process. By default, Talos waits for `udevd` to scan and settle, but with some RAID controllers `udevd` might report settled devices before they are actually ready. Adding this kernel argument provides extra settle time on top of `udevd` settle time. The maximum value is `10m` (10 minutes). Example: ```text talos.device.settle_time=3m ``` #### `talos.halt_if_installed` If set to `1`, Talos will pause the boot sequence and keeps printing a message until the boot timeout is reached if it detects that it is already installed. This is useful if booting from ISO/PXE and you want to prevent the machine accidentally booting from the ISO/PXE after installation to the disk. ================================================ FILE: website/content/v1.13/schemas/config.schema.json ================================================ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://talos.dev/v1.13/schemas/config.schema.json", "$defs": { "block.DiskSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the disk.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the disk.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the disk.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "DiskSelector selects a disk for the volume." }, "block.EncryptionKey": { "properties": { "slot": { "type": "integer", "title": "slot", "description": "Key slot number for LUKS2 encryption.\n", "markdownDescription": "Key slot number for LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n" }, "static": { "$ref": "#/$defs/block.EncryptionKeyStatic", "title": "static", "description": "Key which value is stored in the configuration file.\n", "markdownDescription": "Key which value is stored in the configuration file.", "x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n" }, "nodeID": { "$ref": "#/$defs/block.EncryptionKeyNodeID", "title": "nodeID", "description": "Deterministically generated key from the node UUID and PartitionLabel.\n", "markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.", "x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n" }, "kms": { "$ref": "#/$defs/block.EncryptionKeyKMS", "title": "kms", "description": "KMS managed encryption key.\n", "markdownDescription": "KMS managed encryption key.", "x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n" }, "tpm": { "$ref": "#/$defs/block.EncryptionKeyTPM", "title": "tpm", "description": "Enable TPM based disk encryption.\n", "markdownDescription": "Enable TPM based disk encryption.", "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" }, "lockToState": { "type": "boolean", "title": "lockToState", "description": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\n", "markdownDescription": "Lock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.", "x-intellij-html-description": "\u003cp\u003eLock the disk encryption key to the random salt stored in the STATE partition. This is useful to prevent the volume from being unlocked if STATE partition is compromised or replaced. It is recommended to use this option with TPM disk encryption for non-STATE volumes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKey represents configuration for disk encryption key." }, "block.EncryptionKeyKMS": { "properties": { "endpoint": { "type": "string", "title": "endpoint", "description": "KMS endpoint to Seal/Unseal the key.\n", "markdownDescription": "KMS endpoint to Seal/Unseal the key.", "x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyKMS represents a key that is generated and then sealed/unsealed by the KMS server." }, "block.EncryptionKeyNodeID": { "properties": {}, "additionalProperties": false, "type": "object", "description": "EncryptionKeyNodeID represents deterministically generated key from the node UUID and PartitionLabel." }, "block.EncryptionKeyStatic": { "properties": { "passphrase": { "type": "string", "title": "passphrase", "description": "Defines the static passphrase value.\n", "markdownDescription": "Defines the static passphrase value.", "x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyStatic represents throw away key type." }, "block.EncryptionKeyTPM": { "properties": { "options": { "$ref": "#/$defs/block.EncryptionKeyTPMOptions", "title": "options", "description": "TPM options for key protection.\n", "markdownDescription": "TPM options for key protection.", "x-intellij-html-description": "\u003cp\u003eTPM options for key protection.\u003c/p\u003e\n" }, "checkSecurebootStatusOnEnroll": { "type": "boolean", "title": "checkSecurebootStatusOnEnroll", "description": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.\n", "markdownDescription": "Check that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.", "x-intellij-html-description": "\u003cp\u003eCheck that Secureboot is enabled in the EFI firmware.\nIf Secureboot is not enabled, the enrollment of the key will fail.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyTPM represents a key that is generated and then sealed/unsealed by the TPM." }, "block.EncryptionKeyTPMOptions": { "properties": { "pcrs": { "items": { "type": "integer" }, "type": "array", "title": "pcrs", "description": "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.\n", "markdownDescription": "List of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.", "x-intellij-html-description": "\u003cp\u003eList of PCRs to bind the key to. If not set, defaults to PCR 7, can be disabled by passing an empty list.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionKeyTPMOptions represents the options for TPM-based key protection." }, "block.EncryptionSpec": { "properties": { "provider": { "enum": [ "luks2" ], "title": "provider", "description": "Encryption provider to use for the encryption.\n", "markdownDescription": "Encryption provider to use for the encryption.", "x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n" }, "keys": { "items": { "$ref": "#/$defs/block.EncryptionKey" }, "type": "array", "title": "keys", "description": "Defines the encryption keys generation and storage method.\n", "markdownDescription": "Defines the encryption keys generation and storage method.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n" }, "cipher": { "enum": [ "aes-xts-plain64", "xchacha12,aes-adiantum-plain64", "xchacha20,aes-adiantum-plain64" ], "title": "cipher", "description": "Cipher to use for the encryption. Depends on the encryption provider.\n", "markdownDescription": "Cipher to use for the encryption. Depends on the encryption provider.", "x-intellij-html-description": "\u003cp\u003eCipher to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n" }, "keySize": { "type": "integer", "title": "keySize", "description": "Defines the encryption key length.\n", "markdownDescription": "Defines the encryption key length.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n" }, "blockSize": { "type": "integer", "title": "blockSize", "description": "Defines the encryption sector size.\n", "markdownDescription": "Defines the encryption sector size.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n" }, "options": { "enum": [ "no_read_workqueue", "no_write_workqueue", "same_cpu_crypt" ], "title": "options", "description": "Additional –perf parameters for the LUKS2 encryption.\n", "markdownDescription": "Additional --perf parameters for the LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EncryptionSpec represents volume encryption settings." }, "block.ExistingMountSpec": { "properties": { "readOnly": { "type": "boolean", "title": "readOnly", "description": "Mount the volume read-only.\n", "markdownDescription": "Mount the volume read-only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read-only.\u003c/p\u003e\n" }, "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExistingMountSpec describes how the volume is mounted." }, "block.ExistingVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExistingVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/block.VolumeDiscoverySpec", "title": "discovery", "description": "The discovery describes how to find a volume.\n", "markdownDescription": "The discovery describes how to find a volume.", "x-intellij-html-description": "\u003cp\u003eThe discovery describes how to find a volume.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.ExistingMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ExistingVolumeConfig is an existing volume configuration document.\\nExisting volumes allow to mount partitions (or whole disks) that were created\\noutside of Talos. Volume will be mounted under `/var/mnt/\u003cname\u003e`.\\nThe existing volume config name should not conflict with user volume names.\\n" }, "block.ExternalMountSpec": { "properties": { "readOnly": { "type": "boolean", "title": "readOnly", "description": "Mount the volume read-only.\n", "markdownDescription": "Mount the volume read-only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read-only.\u003c/p\u003e\n" }, "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" }, "virtiofs": { "$ref": "#/$defs/block.VirtiofsMountSpec", "title": "virtiofs", "description": "Virtiofs mount options.\n", "markdownDescription": "Virtiofs mount options.", "x-intellij-html-description": "\u003cp\u003eVirtiofs mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExternalMountSpec describes how the external volume is mounted." }, "block.ExternalVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExternalVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the mount.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the mount.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the mount.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "filesystemType": { "enum": [ "virtiofs", "nfs" ], "title": "filesystemType", "description": "Filesystem type.\n", "markdownDescription": "Filesystem type.", "x-intellij-html-description": "\u003cp\u003eFilesystem type.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.ExternalMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ExternalVolumeConfig is an external disk mount configuration document.\\nExternal volumes allow to mount volumes that were created outside of Talos,\\nover the network or API. Volume will be mounted under `/var/mnt/\u003cname\u003e`.\\nThe external volume config name should not conflict with user volume names.\\n" }, "block.FilesystemSpec": { "properties": { "type": { "enum": [ "ext4", "xfs" ], "title": "type", "description": "Filesystem type. Default is xfs.\n", "markdownDescription": "Filesystem type. Default is `xfs`.", "x-intellij-html-description": "\u003cp\u003eFilesystem type. Default is \u003ccode\u003exfs\u003c/code\u003e.\u003c/p\u003e\n" }, "projectQuotaSupport": { "type": "boolean", "title": "projectQuotaSupport", "description": "Enables project quota support, valid only for ‘xfs’ filesystem.\n\nNote: changing this value might require a full remount of the filesystem.\n", "markdownDescription": "Enables project quota support, valid only for 'xfs' filesystem.\n\nNote: changing this value might require a full remount of the filesystem.", "x-intellij-html-description": "\u003cp\u003eEnables project quota support, valid only for \u0026lsquo;xfs\u0026rsquo; filesystem.\u003c/p\u003e\n\n\u003cp\u003eNote: changing this value might require a full remount of the filesystem.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FilesystemSpec configures the filesystem for the volume." }, "block.MountSpec": { "properties": { "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MountSpec describes how the volume is mounted." }, "block.ProvisioningSpec": { "properties": { "diskSelector": { "$ref": "#/$defs/block.DiskSelector", "title": "diskSelector", "description": "The disk selector expression.\n", "markdownDescription": "The disk selector expression.", "x-intellij-html-description": "\u003cp\u003eThe disk selector expression.\u003c/p\u003e\n" }, "grow": { "type": "boolean", "title": "grow", "description": "Should the volume grow to the size of the disk (if possible).\n", "markdownDescription": "Should the volume grow to the size of the disk (if possible).", "x-intellij-html-description": "\u003cp\u003eShould the volume grow to the size of the disk (if possible).\u003c/p\u003e\n" }, "minSize": { "type": "string", "title": "minSize", "description": "The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\n", "markdownDescription": "The minimum size of the volume.\n\nSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.", "x-intellij-html-description": "\u003cp\u003eThe minimum size of the volume.\u003c/p\u003e\n\n\u003cp\u003eSize is specified in bytes, but can be expressed in human readable format, e.g. 100MB.\u003c/p\u003e\n" }, "maxSize": { "type": "string", "title": "maxSize", "description": "The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.\n", "markdownDescription": "The maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\n\nSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.", "x-intellij-html-description": "\u003cp\u003eThe maximum size of the volume, if not specified the volume can grow to the size of the\ndisk.\u003c/p\u003e\n\n\u003cp\u003eSize is specified in bytes or in percents. It can be expressed in human readable format, e.g. 100MB.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ProvisioningSpec describes how the volume is provisioned." }, "block.RawVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RawVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "RawVolumeConfig is a raw volume configuration document.\\nRaw volumes allow to create partitions without formatting them.\\n If you want to use local storage, user volumes is a better choice,\\n raw volumes are intended to be used with CSI provisioners.\\nThe partition label is automatically generated as `r-\u003cname\u003e`.\\n" }, "block.SwapVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "SwapVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "SwapVolumeConfig is a disk swap volume configuration document.\\nSwap volume is automatically allocated as a partition on the specified disk\\nand activated as swap, removing a swap volume deactivates swap.\\nThe partition label is automatically generated as `s-\u003cname\u003e`.\\n" }, "block.UserMountSpec": { "properties": { "disableAccessTime": { "type": "boolean", "title": "disableAccessTime", "description": "If true, disable file access time updates.\n", "markdownDescription": "If true, disable file access time updates.", "x-intellij-html-description": "\u003cp\u003eIf true, disable file access time updates.\u003c/p\u003e\n" }, "secure": { "type": "boolean", "title": "secure", "description": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.\n", "markdownDescription": "Enable secure mount options (nosuid, nodev).\n\nDefaults to true for better security.", "x-intellij-html-description": "\u003cp\u003eEnable secure mount options (nosuid, nodev).\u003c/p\u003e\n\n\u003cp\u003eDefaults to true for better security.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "UserMountSpec describes how the volume is mounted." }, "block.UserVolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "UserVolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\n", "markdownDescription": "Name of the volume.\n\nName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n\n\u003cp\u003eName might be between 1 and 34 characters long and can only contain:\nlowercase and uppercase ASCII letters, digits, and hyphens.\u003c/p\u003e\n" }, "volumeType": { "enum": [ "directory", "disk", "partition" ], "title": "volumeType", "description": "Volume type.\n", "markdownDescription": "Volume type.", "x-intellij-html-description": "\u003cp\u003eVolume type.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "filesystem": { "$ref": "#/$defs/block.FilesystemSpec", "title": "filesystem", "description": "The filesystem describes how the volume is formatted.\n", "markdownDescription": "The filesystem describes how the volume is formatted.", "x-intellij-html-description": "\u003cp\u003eThe filesystem describes how the volume is formatted.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.UserMountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "UserVolumeConfig is a user volume configuration document.\\nUser volume is automatically allocated as a partition on the specified disk\\nand mounted under `/var/mnt/\u003cname\u003e`.\\nThe partition label is automatically generated as `u-\u003cname\u003e`.\\n" }, "block.VirtiofsMountSpec": { "properties": { "tag": { "type": "string", "title": "tag", "description": "Selector tag for the Virtiofs mount.\n", "markdownDescription": "Selector tag for the Virtiofs mount.", "x-intellij-html-description": "\u003cp\u003eSelector tag for the Virtiofs mount.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VirtiofsMountSpec describes Virtiofs mount options." }, "block.VolumeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VolumeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the volume.\n", "markdownDescription": "Name of the volume.", "x-intellij-html-description": "\u003cp\u003eName of the volume.\u003c/p\u003e\n" }, "provisioning": { "$ref": "#/$defs/block.ProvisioningSpec", "title": "provisioning", "description": "The provisioning describes how the volume is provisioned.\n", "markdownDescription": "The provisioning describes how the volume is provisioned.", "x-intellij-html-description": "\u003cp\u003eThe provisioning describes how the volume is provisioned.\u003c/p\u003e\n" }, "encryption": { "$ref": "#/$defs/block.EncryptionSpec", "title": "encryption", "description": "The encryption describes how the volume is encrypted.\n", "markdownDescription": "The encryption describes how the volume is encrypted.", "x-intellij-html-description": "\u003cp\u003eThe encryption describes how the volume is encrypted.\u003c/p\u003e\n" }, "mount": { "$ref": "#/$defs/block.MountSpec", "title": "mount", "description": "The mount describes additional mount options.\n", "markdownDescription": "The mount describes additional mount options.", "x-intellij-html-description": "\u003cp\u003eThe mount describes additional mount options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "VolumeConfig is a system volume configuration document.\\nNote: at the moment, only `STATE`, `EPHEMERAL` and `IMAGE-CACHE` system volumes are supported.\\n" }, "block.VolumeDiscoverySpec": { "properties": { "volumeSelector": { "$ref": "#/$defs/block.VolumeSelector", "title": "volumeSelector", "description": "The volume selector expression.\n", "markdownDescription": "The volume selector expression.", "x-intellij-html-description": "\u003cp\u003eThe volume selector expression.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeDiscoverySpec describes how the volume is discovered." }, "block.VolumeSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the volume.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the volume.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the volume.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeSelector selects an existing volume." }, "block.ZswapConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ZswapConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "maxPoolPercent": { "type": "integer", "title": "maxPoolPercent", "description": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\n", "markdownDescription": "The maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.", "x-intellij-html-description": "\u003cp\u003eThe maximum percent of memory that zswap can use.\nThis is a percentage of the total system memory.\nThe value must be between 0 and 100.\u003c/p\u003e\n" }, "shrinkerEnabled": { "type": "boolean", "title": "shrinkerEnabled", "description": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\n", "markdownDescription": "Enable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.", "x-intellij-html-description": "\u003cp\u003eEnable the shrinker feature: kernel might move\ncold pages from zswap to swap device to free up memory\nfor other use cases.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ZswapConfig is a zswap (compressed memory) configuration document.\\nWhen zswap is enabled, Linux kernel compresses pages that would otherwise be swapped out to disk.\\nThe compressed pages are stored in a memory pool, which is used to avoid writing to disk\\nwhen the system is under memory pressure.\\n" }, "cri.RegistryAuthConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryAuthConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry endpoint to apply the authentication configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. ‘my-mirror.local:5000’ for ‘https://my-mirror.local:5000/v2/’.\n\nThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\n", "markdownDescription": "Registry endpoint to apply the authentication configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", "x-intellij-html-description": "\u003cp\u003eRegistry endpoint to apply the authentication configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry endpoint is the hostname part of the endpoint URL,\ne.g. \u0026lsquo;my-mirror.local:5000\u0026rsquo; for \u0026lsquo;\u003ca href=\"https://my-mirror.local:5000/v2/'\" target=\"_blank\"\u003ehttps://my-mirror.local:5000/v2/\u0026rsquo;\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe authentication configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\u003c/p\u003e\n" }, "username": { "type": "string", "title": "username", "description": "Username/password authentication.\n", "markdownDescription": "Username/password authentication.", "x-intellij-html-description": "\u003cp\u003eUsername/password authentication.\u003c/p\u003e\n" }, "password": { "type": "string", "title": "password", "description": "Username/password authentication.\n", "markdownDescription": "Username/password authentication.", "x-intellij-html-description": "\u003cp\u003eUsername/password authentication.\u003c/p\u003e\n" }, "auth": { "type": "string", "title": "auth", "description": "Raw authentication string.\n", "markdownDescription": "Raw authentication string.", "x-intellij-html-description": "\u003cp\u003eRaw authentication string.\u003c/p\u003e\n" }, "identityToken": { "type": "string", "title": "identityToken", "description": "Identity token authentication.\n", "markdownDescription": "Identity token authentication.", "x-intellij-html-description": "\u003cp\u003eIdentity token authentication.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryAuthConfig configures authentication for a registry endpoint." }, "cri.RegistryEndpoint": { "properties": { "url": { "type": "string", "pattern": "^(http|https)://", "title": "url", "description": "The URL of the registry mirror endpoint.\n", "markdownDescription": "The URL of the registry mirror endpoint.", "x-intellij-html-description": "\u003cp\u003eThe URL of the registry mirror endpoint.\u003c/p\u003e\n" }, "overridePath": { "type": "boolean", "title": "overridePath", "description": "Use endpoint path as supplied, without adding /v2/ suffix.\n", "markdownDescription": "Use endpoint path as supplied, without adding `/v2/` suffix.", "x-intellij-html-description": "\u003cp\u003eUse endpoint path as supplied, without adding \u003ccode\u003e/v2/\u003c/code\u003e suffix.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "url" ], "description": "RegistryEndpoint defines a registry mirror endpoint." }, "cri.RegistryMirrorConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryMirrorConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry name to apply the mirror configuration to.\n\nRegistry name is the first segment of image identifier, with ‘docker.io’\nbeing default one.\n\nA special name ‘*’ can be used to define mirror configuration\nthat applies to all registries.\n", "markdownDescription": "Registry name to apply the mirror configuration to.\n\nRegistry name is the first segment of image identifier, with 'docker.io'\nbeing default one.\n\nA special name '*' can be used to define mirror configuration\nthat applies to all registries.", "x-intellij-html-description": "\u003cp\u003eRegistry name to apply the mirror configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry name is the first segment of image identifier, with \u0026lsquo;docker.io\u0026rsquo;\nbeing default one.\u003c/p\u003e\n\n\u003cp\u003eA special name \u0026lsquo;*\u0026rsquo; can be used to define mirror configuration\nthat applies to all registries.\u003c/p\u003e\n" }, "endpoints": { "items": { "$ref": "#/$defs/cri.RegistryEndpoint" }, "type": "array", "title": "endpoints", "description": "List of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is skipFallback is not set to true.\n", "markdownDescription": "List of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is `skipFallback` is not set to true.", "x-intellij-html-description": "\u003cp\u003eList of mirror endpoints for the registry.\nMirrors will be used in the order they are specified,\nfalling back to the default registry is \u003ccode\u003eskipFallback\u003c/code\u003e is not set to true.\u003c/p\u003e\n" }, "skipFallback": { "type": "boolean", "title": "skipFallback", "description": "Skip fallback to the original registry if none of the mirrors are available\nor contain the requested image.\n", "markdownDescription": "Skip fallback to the original registry if none of the mirrors are available\nor contain the requested image.", "x-intellij-html-description": "\u003cp\u003eSkip fallback to the original registry if none of the mirrors are available\nor contain the requested image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryMirrorConfig configures an image registry mirror." }, "cri.RegistryTLSConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RegistryTLSConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Registry endpoint to apply the TLS configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. ‘my-mirror.local:5000’ for ‘https://my-mirror.local:5000/v2/’.\n\nThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\n", "markdownDescription": "Registry endpoint to apply the TLS configuration to.\n\nRegistry endpoint is the hostname part of the endpoint URL,\ne.g. 'my-mirror.local:5000' for 'https://my-mirror.local:5000/v2/'.\n\nThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.", "x-intellij-html-description": "\u003cp\u003eRegistry endpoint to apply the TLS configuration to.\u003c/p\u003e\n\n\u003cp\u003eRegistry endpoint is the hostname part of the endpoint URL,\ne.g. \u0026lsquo;my-mirror.local:5000\u0026rsquo; for \u0026lsquo;\u003ca href=\"https://my-mirror.local:5000/v2/'\" target=\"_blank\"\u003ehttps://my-mirror.local:5000/v2/\u0026rsquo;\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eThe TLS configuration makes sense only for HTTPS endpoints.\nThe TLS configuration will apply to all image pulls for this\nregistry endpoint, by Talos or any Kubernetes workloads.\u003c/p\u003e\n" }, "clientIdentity": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "clientIdentity", "description": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.\n", "markdownDescription": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.", "x-intellij-html-description": "\u003cp\u003eEnable mutual TLS authentication with the registry.\nClient certificate and key should be PEM-encoded.\u003c/p\u003e\n" }, "ca": { "type": "string", "title": "ca", "description": "CA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.\n", "markdownDescription": "CA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.", "x-intellij-html-description": "\u003cp\u003eCA registry certificate to add the list of trusted certificates.\nCertificate should be PEM-encoded.\u003c/p\u003e\n" }, "insecureSkipVerify": { "type": "boolean", "title": "insecureSkipVerify", "description": "Skip TLS server certificate verification (not recommended).\n", "markdownDescription": "Skip TLS server certificate verification (not recommended).", "x-intellij-html-description": "\u003cp\u003eSkip TLS server certificate verification (not recommended).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "RegistryTLSConfig configures TLS for a registry endpoint." }, "extensions.ConfigFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The content of the extension service config file.\n", "markdownDescription": "The content of the extension service config file.", "x-intellij-html-description": "\u003cp\u003eThe content of the extension service config file.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "The mount path of the extension service config file.\n", "markdownDescription": "The mount path of the extension service config file.", "x-intellij-html-description": "\u003cp\u003eThe mount path of the extension service config file.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ConfigFile is a config file for extension services." }, "extensions.ServiceConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ExtensionServiceConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the extension service.\n", "markdownDescription": "Name of the extension service.", "x-intellij-html-description": "\u003cp\u003eName of the extension service.\u003c/p\u003e\n" }, "configFiles": { "items": { "$ref": "#/$defs/extensions.ConfigFile" }, "type": "array", "title": "configFiles", "description": "The config files for the extension service.\n", "markdownDescription": "The config files for the extension service.", "x-intellij-html-description": "\u003cp\u003eThe config files for the extension service.\u003c/p\u003e\n" }, "environment": { "items": { "type": "string" }, "type": "array", "title": "environment", "description": "The environment for the extension service.\n", "markdownDescription": "The environment for the extension service.", "x-intellij-html-description": "\u003cp\u003eThe environment for the extension service.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "ExtensionServiceConfig is a extensionserviceconfig document." }, "hardware.PCIDriverRebindConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "PCIDriverRebindConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "PCI device id\n", "markdownDescription": "PCI device id", "x-intellij-html-description": "\u003cp\u003ePCI device id\u003c/p\u003e\n" }, "targetDriver": { "type": "string", "title": "targetDriver", "description": "Target driver to rebind the PCI device to.\n", "markdownDescription": "Target driver to rebind the PCI device to.", "x-intellij-html-description": "\u003cp\u003eTarget driver to rebind the PCI device to.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "targetDriver" ], "description": "PCIDriverRebindConfig allows to configure PCI driver rebinds." }, "network.AddressConfig": { "properties": { "address": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "address", "description": "IP address to be assigned to the link.\n\nThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).\n", "markdownDescription": "IP address to be assigned to the link.\n\nThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).", "x-intellij-html-description": "\u003cp\u003eIP address to be assigned to the link.\u003c/p\u003e\n\n\u003cp\u003eThis field must include the network prefix length (e.g. /24 for IPv4, /64 for IPv6).\u003c/p\u003e\n" }, "routePriority": { "type": "integer", "title": "routePriority", "description": "Configure the route priority (metric) for routes created for this address.\n\nIf not specified, the system default route priority will be used.\n", "markdownDescription": "Configure the route priority (metric) for routes created for this address.\n\nIf not specified, the system default route priority will be used.", "x-intellij-html-description": "\u003cp\u003eConfigure the route priority (metric) for routes created for this address.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default route priority will be used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "address" ], "description": "AddressConfig represents a network address configuration." }, "network.BlackholeRouteConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BlackholeRouteConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Route destination as an address prefix.\n", "markdownDescription": "Route destination as an address prefix.", "x-intellij-html-description": "\u003cp\u003eRoute destination as an address prefix.\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "BlackholeRouteConfig is a config document to configure blackhole routes." }, "network.BondConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BondConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the bond link (interface) to be created.\n", "markdownDescription": "Name of the bond link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the bond link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) on which the bond will be created.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "bondMode": { "enum": [ "balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb" ], "title": "bondMode", "description": "Bond mode.\n", "markdownDescription": "Bond mode.", "x-intellij-html-description": "\u003cp\u003eBond mode.\u003c/p\u003e\n" }, "miimon": { "type": "integer", "title": "miimon", "description": "Link monitoring frequency in milliseconds.\n", "markdownDescription": "Link monitoring frequency in milliseconds.", "x-intellij-html-description": "\u003cp\u003eLink monitoring frequency in milliseconds.\u003c/p\u003e\n" }, "updelay": { "type": "integer", "title": "updelay", "description": "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.\n", "markdownDescription": "The time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.", "x-intellij-html-description": "\u003cp\u003eThe time, in milliseconds, to wait before enabling a slave after a link recovery has been detected.\u003c/p\u003e\n" }, "downdelay": { "type": "integer", "title": "downdelay", "description": "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.\n", "markdownDescription": "The time, in milliseconds, to wait before disabling a slave after a link failure has been detected.", "x-intellij-html-description": "\u003cp\u003eThe time, in milliseconds, to wait before disabling a slave after a link failure has been detected.\u003c/p\u003e\n" }, "useCarrier": { "type": "boolean", "title": "useCarrier", "description": "Specifies whether or not miimon should use MII or ETHTOOL.\n", "markdownDescription": "Specifies whether or not miimon should use MII or ETHTOOL.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether or not miimon should use MII or ETHTOOL.\u003c/p\u003e\n" }, "xmitHashPolicy": { "enum": [ "layer2", "layer3+4", "layer2+3", "encap2+3", "encap3+4" ], "title": "xmitHashPolicy", "description": "Selects the transmit hash policy to use for slave selection.\n", "markdownDescription": "Selects the transmit hash policy to use for slave selection.", "x-intellij-html-description": "\u003cp\u003eSelects the transmit hash policy to use for slave selection.\u003c/p\u003e\n" }, "arpInterval": { "type": "integer", "title": "arpInterval", "description": "ARP link monitoring frequency in milliseconds.\n", "markdownDescription": "ARP link monitoring frequency in milliseconds.", "x-intellij-html-description": "\u003cp\u003eARP link monitoring frequency in milliseconds.\u003c/p\u003e\n" }, "arpIpTargets": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+$" }, "type": "array", "title": "arpIpTargets", "description": "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\n", "markdownDescription": "The list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", "x-intellij-html-description": "\u003cp\u003eThe list of IPv4 addresses to use for ARP link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\u003c/p\u003e\n" }, "nsIp6Targets": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+$" }, "type": "array", "title": "nsIp6Targets", "description": "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\n", "markdownDescription": "The list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.", "x-intellij-html-description": "\u003cp\u003eThe list of IPv6 addresses to use for NS link monitoring when arpInterval is set.\nMaximum of 16 targets are supported.\u003c/p\u003e\n" }, "arpValidate": { "enum": [ "none", "active", "backup", "all", "filter", "filter-active", "filter-backup" ], "title": "arpValidate", "description": "Specifies whether or not ARP probes and replies should be validated.\n", "markdownDescription": "Specifies whether or not ARP probes and replies should be validated.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether or not ARP probes and replies should be validated.\u003c/p\u003e\n" }, "arpAllTargets": { "enum": [ "any", "all" ], "title": "arpAllTargets", "description": "Specifies whether ARP probes should be sent to any or all targets.\n", "markdownDescription": "Specifies whether ARP probes should be sent to any or all targets.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether ARP probes should be sent to any or all targets.\u003c/p\u003e\n" }, "lacpRate": { "enum": [ "slow", "fast" ], "title": "lacpRate", "description": "LACPDU frames periodic transmission rate.\n", "markdownDescription": "LACPDU frames periodic transmission rate.", "x-intellij-html-description": "\u003cp\u003eLACPDU frames periodic transmission rate.\u003c/p\u003e\n" }, "failOverMac": { "enum": [ "none", "active", "follow" ], "title": "failOverMac", "description": "Specifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.\n", "markdownDescription": "Specifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.", "x-intellij-html-description": "\u003cp\u003eSpecifies whether active-backup mode should set all slaves to the same MAC address\nat enslavement, when enabled, or perform special handling.\u003c/p\u003e\n" }, "adSelect": { "enum": [ "stable", "bandwidth", "count" ], "title": "adSelect", "description": "Aggregate selection policy for 802.3ad.\n", "markdownDescription": "Aggregate selection policy for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eAggregate selection policy for 802.3ad.\u003c/p\u003e\n" }, "adActorSysPrio": { "type": "integer", "title": "adActorSysPrio", "description": "Actor system priority for 802.3ad.\n", "markdownDescription": "Actor system priority for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eActor system priority for 802.3ad.\u003c/p\u003e\n" }, "adUserPortKey": { "type": "integer", "title": "adUserPortKey", "description": "User port key (upper 10 bits) for 802.3ad.\n", "markdownDescription": "User port key (upper 10 bits) for 802.3ad.", "x-intellij-html-description": "\u003cp\u003eUser port key (upper 10 bits) for 802.3ad.\u003c/p\u003e\n" }, "adLACPActive": { "enum": [ "on", "off" ], "title": "adLACPActive", "description": "Whether to send LACPDU frames periodically.\n", "markdownDescription": "Whether to send LACPDU frames periodically.", "x-intellij-html-description": "\u003cp\u003eWhether to send LACPDU frames periodically.\u003c/p\u003e\n" }, "primaryReselect": { "enum": [ "always", "better", "failure" ], "title": "primaryReselect", "description": "Policy under which the primary slave should be reselected.\n", "markdownDescription": "Policy under which the primary slave should be reselected.", "x-intellij-html-description": "\u003cp\u003ePolicy under which the primary slave should be reselected.\u003c/p\u003e\n" }, "resendIGMP": { "type": "integer", "title": "resendIGMP", "description": "The number of times IGMP packets should be resent.\n", "markdownDescription": "The number of times IGMP packets should be resent.", "x-intellij-html-description": "\u003cp\u003eThe number of times IGMP packets should be resent.\u003c/p\u003e\n" }, "minLinks": { "type": "integer", "title": "minLinks", "description": "The minimum number of active links required for the bond to be considered active.\n", "markdownDescription": "The minimum number of active links required for the bond to be considered active.", "x-intellij-html-description": "\u003cp\u003eThe minimum number of active links required for the bond to be considered active.\u003c/p\u003e\n" }, "lpInterval": { "type": "integer", "title": "lpInterval", "description": "The number of seconds between instances where the bonding driver sends learning packets to each slave’s peer switch.\n", "markdownDescription": "The number of seconds between instances where the bonding driver sends learning packets to each slave's peer switch.", "x-intellij-html-description": "\u003cp\u003eThe number of seconds between instances where the bonding driver sends learning packets to each slave\u0026rsquo;s peer switch.\u003c/p\u003e\n" }, "packetsPerSlave": { "type": "integer", "title": "packetsPerSlave", "description": "The number of packets to transmit through a slave before moving to the next one.\n", "markdownDescription": "The number of packets to transmit through a slave before moving to the next one.", "x-intellij-html-description": "\u003cp\u003eThe number of packets to transmit through a slave before moving to the next one.\u003c/p\u003e\n" }, "numPeerNotif": { "type": "integer", "title": "numPeerNotif", "description": "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.\n", "markdownDescription": "The number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.", "x-intellij-html-description": "\u003cp\u003eThe number of peer notifications (gratuitous ARPs and unsolicited IPv6 Neighbor Advertisements)\nto be issued after a failover event.\u003c/p\u003e\n" }, "tlbLogicalLb": { "type": "integer", "title": "tlbLogicalLb", "description": "Whether dynamic shuffling of flows is enabled in tlb or alb mode.\n", "markdownDescription": "Whether dynamic shuffling of flows is enabled in tlb or alb mode.", "x-intellij-html-description": "\u003cp\u003eWhether dynamic shuffling of flows is enabled in tlb or alb mode.\u003c/p\u003e\n" }, "allSlavesActive": { "type": "integer", "title": "allSlavesActive", "description": "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).\n", "markdownDescription": "Whether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).", "x-intellij-html-description": "\u003cp\u003eWhether duplicate frames (received on inactive ports) should be dropped (0) or delivered (1).\u003c/p\u003e\n" }, "peerNotifDelay": { "type": "integer", "title": "peerNotifDelay", "description": "The delay, in milliseconds, between each peer notification.\n", "markdownDescription": "The delay, in milliseconds, between each peer notification.", "x-intellij-html-description": "\u003cp\u003eThe delay, in milliseconds, between each peer notification.\u003c/p\u003e\n" }, "missedMax": { "type": "integer", "title": "missedMax", "description": "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.\n", "markdownDescription": "The number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.", "x-intellij-html-description": "\u003cp\u003eThe number of arpInterval monitor checks that must fail in order for an interface to be marked down by the ARP monitor.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "bondMode", "kind", "links", "name" ], "description": "BondConfig is a config document to create a bond (link aggregation) over a set of links." }, "network.BridgeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "BridgeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the bridge link (interface) to be created.\n", "markdownDescription": "Name of the bridge link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the bridge link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) to be aggregated.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "stp": { "$ref": "#/$defs/network.BridgeSTPConfig", "title": "stp", "description": "Bridge STP (Spanning Tree Protocol) configuration.\n", "markdownDescription": "Bridge STP (Spanning Tree Protocol) configuration.", "x-intellij-html-description": "\u003cp\u003eBridge STP (Spanning Tree Protocol) configuration.\u003c/p\u003e\n" }, "vlan": { "$ref": "#/$defs/network.BridgeVLANConfig", "title": "vlan", "description": "Bridge VLAN configuration.\n", "markdownDescription": "Bridge VLAN configuration.", "x-intellij-html-description": "\u003cp\u003eBridge VLAN configuration.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "links", "name" ], "description": "BridgeConfig is a config document to create a Bridge (link aggregation) over a set of links." }, "network.BridgeSTPConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable or disable STP on the bridge.\n", "markdownDescription": "Enable or disable STP on the bridge.", "x-intellij-html-description": "\u003cp\u003eEnable or disable STP on the bridge.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "BridgeSTPConfig is a bridge STP (Spanning Tree Protocol) configuration." }, "network.BridgeVLANConfig": { "properties": { "filtering": { "type": "boolean", "title": "filtering", "description": "Enable or disable VLAN filtering on the bridge.\n", "markdownDescription": "Enable or disable VLAN filtering on the bridge.", "x-intellij-html-description": "\u003cp\u003eEnable or disable VLAN filtering on the bridge.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "BridgeVLANConfig is a bridge VLAN configuration." }, "network.CommonLinkConfig": { "properties": { "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CommonLinkConfig is common configuration for network links, and logical links." }, "network.CommonProbeConfig": { "properties": { "interval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "interval", "description": "Interval between probe attempts.\nDefaults to 1s.\n", "markdownDescription": "Interval between probe attempts.\nDefaults to 1s.", "x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n" }, "failureThreshold": { "type": "integer", "title": "failureThreshold", "description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n", "markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).", "x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CommonProbeConfig holds fields common to all probe types." }, "network.DHCPv4ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DHCPv4Config" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "routeMetric": { "type": "integer", "title": "routeMetric", "description": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.\n", "markdownDescription": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", "x-intellij-html-description": "\u003cp\u003eAn optional metric for the routes received from the DHCP server.\u003c/p\u003e\n\n\u003cp\u003eLower values indicate higher priority.\nDefault value is 1024.\u003c/p\u003e\n" }, "ignoreHostname": { "type": "boolean", "title": "ignoreHostname", "description": "Ignore hostname received from the DHCP server.\n", "markdownDescription": "Ignore hostname received from the DHCP server.", "x-intellij-html-description": "\u003cp\u003eIgnore hostname received from the DHCP server.\u003c/p\u003e\n" }, "clientIdentifier": { "enum": [ "none", "mac", "duid" ], "title": "clientIdentifier", "description": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to ‘mac’ if not set.\n", "markdownDescription": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", "x-intellij-html-description": "\u003cp\u003eClient identifier to use when communicating with DHCP servers.\u003c/p\u003e\n\n\u003cp\u003eDefaults to \u0026lsquo;mac\u0026rsquo; if not set.\u003c/p\u003e\n" }, "duidRaw": { "type": "string", "pattern": "^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$", "title": "duidRaw", "description": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if ‘clientIdentifier’ is set to ‘duid’.\n", "markdownDescription": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", "x-intellij-html-description": "\u003cp\u003eRaw value of the DUID to use as client identifier.\u003c/p\u003e\n\n\u003cp\u003eThis field is only used if \u0026lsquo;clientIdentifier\u0026rsquo; is set to \u0026lsquo;duid\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DHCPv4Config is a config document to configure DHCPv4 on a network link." }, "network.DHCPv6ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DHCPv6Config" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "routeMetric": { "type": "integer", "title": "routeMetric", "description": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.\n", "markdownDescription": "An optional metric for the routes received from the DHCP server.\n\nLower values indicate higher priority.\nDefault value is 1024.", "x-intellij-html-description": "\u003cp\u003eAn optional metric for the routes received from the DHCP server.\u003c/p\u003e\n\n\u003cp\u003eLower values indicate higher priority.\nDefault value is 1024.\u003c/p\u003e\n" }, "ignoreHostname": { "type": "boolean", "title": "ignoreHostname", "description": "Ignore hostname received from the DHCP server.\n", "markdownDescription": "Ignore hostname received from the DHCP server.", "x-intellij-html-description": "\u003cp\u003eIgnore hostname received from the DHCP server.\u003c/p\u003e\n" }, "clientIdentifier": { "enum": [ "none", "mac", "duid" ], "title": "clientIdentifier", "description": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to ‘mac’ if not set.\n", "markdownDescription": "Client identifier to use when communicating with DHCP servers.\n\nDefaults to 'mac' if not set.", "x-intellij-html-description": "\u003cp\u003eClient identifier to use when communicating with DHCP servers.\u003c/p\u003e\n\n\u003cp\u003eDefaults to \u0026lsquo;mac\u0026rsquo; if not set.\u003c/p\u003e\n" }, "duidRaw": { "type": "string", "pattern": "^([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})+)$", "title": "duidRaw", "description": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if ‘clientIdentifier’ is set to ‘duid’.\n", "markdownDescription": "Raw value of the DUID to use as client identifier.\n\nThis field is only used if 'clientIdentifier' is set to 'duid'.", "x-intellij-html-description": "\u003cp\u003eRaw value of the DUID to use as client identifier.\u003c/p\u003e\n\n\u003cp\u003eThis field is only used if \u0026lsquo;clientIdentifier\u0026rsquo; is set to \u0026lsquo;duid\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DHCPv6Config is a config document to configure DHCPv6 on a network link." }, "network.DefaultActionConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "NetworkDefaultActionConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "ingress": { "enum": [ "accept", "block" ], "title": "ingress", "description": "Default action for all not explicitly configured ingress traffic: accept or block.\n", "markdownDescription": "Default action for all not explicitly configured ingress traffic: accept or block.", "x-intellij-html-description": "\u003cp\u003eDefault action for all not explicitly configured ingress traffic: accept or block.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "NetworkDefaultActionConfig is a ingress firewall default action configuration document." }, "network.DummyLinkConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "DummyLinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the dummy link (interface).\n", "markdownDescription": "Name of the dummy link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the dummy link (interface).\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "DummyLinkConfig is a config document to create a dummy (virtual) network link." }, "network.EthernetChannelsConfig": { "properties": { "rx": { "type": "integer", "title": "rx", "description": "Number of RX channels.\n", "markdownDescription": "Number of RX channels.", "x-intellij-html-description": "\u003cp\u003eNumber of RX channels.\u003c/p\u003e\n" }, "tx": { "type": "integer", "title": "tx", "description": "Number of TX channels.\n", "markdownDescription": "Number of TX channels.", "x-intellij-html-description": "\u003cp\u003eNumber of TX channels.\u003c/p\u003e\n" }, "other": { "type": "integer", "title": "other", "description": "Number of other channels.\n", "markdownDescription": "Number of other channels.", "x-intellij-html-description": "\u003cp\u003eNumber of other channels.\u003c/p\u003e\n" }, "combined": { "type": "integer", "title": "combined", "description": "Number of combined channels.\n", "markdownDescription": "Number of combined channels.", "x-intellij-html-description": "\u003cp\u003eNumber of combined channels.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EthernetChannelsConfig is a configuration for Ethernet link channels." }, "network.EthernetConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EthernetConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "features": { "patternProperties": { ".*": { "type": "boolean" } }, "type": "object", "title": "features", "description": "Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse talosctl get ethernetstatus \u0026lt;link\u0026gt; -o yaml to get the list of available features and\ntheir current status.\n", "markdownDescription": "Configuration for Ethernet features.\n\nSet of features available and whether they can be enabled or disabled is driver specific.\nUse `talosctl get ethernetstatus \u003clink\u003e -o yaml` to get the list of available features and\ntheir current status.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet features.\u003c/p\u003e\n\n\u003cp\u003eSet of features available and whether they can be enabled or disabled is driver specific.\nUse \u003ccode\u003etalosctl get ethernetstatus \u0026lt;link\u0026gt; -o yaml\u003c/code\u003e to get the list of available features and\ntheir current status.\u003c/p\u003e\n" }, "rings": { "$ref": "#/$defs/network.EthernetRingsConfig", "title": "rings", "description": "Configuration for Ethernet link rings.\n\nThis is similar to ethtool -G command.\n", "markdownDescription": "Configuration for Ethernet link rings.\n\nThis is similar to `ethtool -G` command.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet link rings.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -G\u003c/code\u003e command.\u003c/p\u003e\n" }, "channels": { "$ref": "#/$defs/network.EthernetChannelsConfig", "title": "channels", "description": "Configuration for Ethernet link channels.\n\nThis is similar to ethtool -L command.\n", "markdownDescription": "Configuration for Ethernet link channels.\n\nThis is similar to `ethtool -L` command.", "x-intellij-html-description": "\u003cp\u003eConfiguration for Ethernet link channels.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -L\u003c/code\u003e command.\u003c/p\u003e\n" }, "wakeOnLan": { "items": { "type": "string" }, "type": "array", "title": "wakeOnLan", "description": "Wake-on-LAN modes to enable.\n\nIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\n\nThis is similar to ethtool -s \u0026lt;link\u0026gt; wol \u0026lt;options\u0026gt; command.\n", "markdownDescription": "Wake-on-LAN modes to enable.\n\nIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\n\nThis is similar to `ethtool -s \u003clink\u003e wol \u003coptions\u003e` command.", "x-intellij-html-description": "\u003cp\u003eWake-on-LAN modes to enable.\u003c/p\u003e\n\n\u003cp\u003eIf this field is omitted, Wake-on-LAN configuration is not changed.\nAn empty list disables Wake-on-LAN.\u003c/p\u003e\n\n\u003cp\u003eThis is similar to \u003ccode\u003eethtool -s \u0026lt;link\u0026gt; wol \u0026lt;options\u0026gt;\u003c/code\u003e command.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "EthernetConfig is a config document to configure Ethernet interfaces." }, "network.EthernetRingsConfig": { "properties": { "rx": { "type": "integer", "title": "rx", "description": "Number of RX rings.\n", "markdownDescription": "Number of RX rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX rings.\u003c/p\u003e\n" }, "tx": { "type": "integer", "title": "tx", "description": "Number of TX rings.\n", "markdownDescription": "Number of TX rings.", "x-intellij-html-description": "\u003cp\u003eNumber of TX rings.\u003c/p\u003e\n" }, "rx-mini": { "type": "integer", "title": "rx-mini", "description": "Number of RX mini rings.\n", "markdownDescription": "Number of RX mini rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX mini rings.\u003c/p\u003e\n" }, "rx-jumbo": { "type": "integer", "title": "rx-jumbo", "description": "Number of RX jumbo rings.\n", "markdownDescription": "Number of RX jumbo rings.", "x-intellij-html-description": "\u003cp\u003eNumber of RX jumbo rings.\u003c/p\u003e\n" }, "rx-buf-len": { "type": "integer", "title": "rx-buf-len", "description": "RX buffer length.\n", "markdownDescription": "RX buffer length.", "x-intellij-html-description": "\u003cp\u003eRX buffer length.\u003c/p\u003e\n" }, "cqe-size": { "type": "integer", "title": "cqe-size", "description": "CQE size.\n", "markdownDescription": "CQE size.", "x-intellij-html-description": "\u003cp\u003eCQE size.\u003c/p\u003e\n" }, "tx-push": { "type": "boolean", "title": "tx-push", "description": "TX push enabled.\n", "markdownDescription": "TX push enabled.", "x-intellij-html-description": "\u003cp\u003eTX push enabled.\u003c/p\u003e\n" }, "rx-push": { "type": "boolean", "title": "rx-push", "description": "RX push enabled.\n", "markdownDescription": "RX push enabled.", "x-intellij-html-description": "\u003cp\u003eRX push enabled.\u003c/p\u003e\n" }, "tx-push-buf-len": { "type": "integer", "title": "tx-push-buf-len", "description": "TX push buffer length.\n", "markdownDescription": "TX push buffer length.", "x-intellij-html-description": "\u003cp\u003eTX push buffer length.\u003c/p\u003e\n" }, "tcp-data-split": { "type": "boolean", "title": "tcp-data-split", "description": "TCP data split enabled.\n", "markdownDescription": "TCP data split enabled.", "x-intellij-html-description": "\u003cp\u003eTCP data split enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EthernetRingsConfig is a configuration for Ethernet link rings." }, "network.HCloudVIPConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "HCloudVIPConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address to be advertised as a Layer 2 VIP.\n", "markdownDescription": "IP address to be advertised as a Layer 2 VIP.", "x-intellij-html-description": "\u003cp\u003eIP address to be advertised as a Layer 2 VIP.\u003c/p\u003e\n" }, "link": { "type": "string", "title": "link", "description": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eName of the link to assign the VIP to.\u003c/p\u003e\n\n\u003cp\u003eSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" }, "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Hetzner Cloud API Token.\n", "markdownDescription": "Specifies the Hetzner Cloud API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "HCloudVIPConfig is a config document to configure virtual IP using Hetzner Cloud APIs for announcement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using Hetzner Cloud APIs.\\n" }, "network.HostnameConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "HostnameConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "auto": { "enum": [ "stable", "off" ], "title": "auto", "description": "A method to automatically generate a hostname for the machine.\n\nThere are two methods available:\n - stable - generates a stable hostname based on machine identity\n - off - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\n\nAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with hostname field.\n", "markdownDescription": "A method to automatically generate a hostname for the machine.\n\nThere are two methods available:\n - `stable` - generates a stable hostname based on machine identity\n - `off` - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\n\nAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `hostname` field.", "x-intellij-html-description": "\u003cp\u003eA method to automatically generate a hostname for the machine.\u003c/p\u003e\n\n\u003cp\u003eThere are two methods available:\n - \u003ccode\u003estable\u003c/code\u003e - generates a stable hostname based on machine identity\n - \u003ccode\u003eoff\u003c/code\u003e - disables automatic hostname generation, Talos will wait for an external source to provide a hostname (DHCP, cloud-init, etc).\u003c/p\u003e\n\n\u003cp\u003eAutomatic hostnames have the lowest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with \u003ccode\u003ehostname\u003c/code\u003e field.\u003c/p\u003e\n" }, "hostname": { "type": "string", "title": "hostname", "description": "A static hostname to set for the machine.\n\nThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with auto field.\n", "markdownDescription": "A static hostname to set for the machine.\n\nThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with `auto` field.", "x-intellij-html-description": "\u003cp\u003eA static hostname to set for the machine.\u003c/p\u003e\n\n\u003cp\u003eThis hostname has the highest priority over any other hostname sources: DHCP, cloud-init, etc.\nConflicts with \u003ccode\u003eauto\u003c/code\u003e field.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "HostnameConfig is a config document to configure the hostname: either a static hostname or an automatically generated hostname." }, "network.IngressRule": { "properties": { "subnet": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "subnet", "description": "Subnet defines a source subnet.\n", "markdownDescription": "Subnet defines a source subnet.", "x-intellij-html-description": "\u003cp\u003eSubnet defines a source subnet.\u003c/p\u003e\n" }, "except": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "except", "description": "Except defines a source subnet to exclude from the rule, it gets excluded from the subnet.\n", "markdownDescription": "Except defines a source subnet to exclude from the rule, it gets excluded from the `subnet`.", "x-intellij-html-description": "\u003cp\u003eExcept defines a source subnet to exclude from the rule, it gets excluded from the \u003ccode\u003esubnet\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "IngressRule is a ingress rule." }, "network.KubeSpanConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KubeSpanConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.\n", "markdownDescription": "Enable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.", "x-intellij-html-description": "\u003cp\u003eEnable the KubeSpan feature.\nCluster discovery should be enabled with cluster.discovery.enabled for KubeSpan to be enabled.\u003c/p\u003e\n" }, "advertiseKubernetesNetworks": { "type": "boolean", "title": "advertiseKubernetesNetworks", "description": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.\n", "markdownDescription": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.", "x-intellij-html-description": "\u003cp\u003eControl whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles pod-to-pod traffic encapsulation.\nIf enabled, KubeSpan takes over pod-to-pod traffic directly.\u003c/p\u003e\n" }, "allowDownPeerBypass": { "type": "boolean", "title": "allowDownPeerBypass", "description": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.\n", "markdownDescription": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.", "x-intellij-html-description": "\u003cp\u003eSkip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security.\u003c/p\u003e\n" }, "harvestExtraEndpoints": { "type": "boolean", "title": "harvestExtraEndpoints", "description": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u0026gt;50).\n", "markdownDescription": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u003e50).", "x-intellij-html-description": "\u003cp\u003eKubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nDisabled by default. Do not enable with high peer counts (\u0026gt;50).\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "KubeSpan link MTU size.\nDefault value is 1420.\n", "markdownDescription": "KubeSpan link MTU size.\nDefault value is 1420.", "x-intellij-html-description": "\u003cp\u003eKubeSpan link MTU size.\nDefault value is 1420.\u003c/p\u003e\n" }, "filters": { "$ref": "#/$defs/network.KubeSpanFiltersConfig", "title": "filters", "description": "KubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.\n", "markdownDescription": "KubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.", "x-intellij-html-description": "\u003cp\u003eKubeSpan advanced filtering of network addresses.\nSettings are optional and apply only to this node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KubeSpanConfig is a config document to configure KubeSpan." }, "network.KubeSpanFiltersConfig": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.\n", "markdownDescription": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\u003c/p\u003e\n\n\u003cp\u003eBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" }, "excludeAdvertisedNetworks": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$" }, "type": "array", "title": "excludeAdvertisedNetworks", "description": "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\n\nBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\n\nNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\n\nDefault value: no filtering.\n", "markdownDescription": "Filter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\n\nBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\n\nNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter networks (e.g., host addresses, pod CIDRs if enabled) which will be advertised over KubeSpan.\u003c/p\u003e\n\n\u003cp\u003eBy default, all networks are advertised.\nUse this filter to exclude some networks from being advertised.\u003c/p\u003e\n\n\u003cp\u003eNote: excluded networks will not be reachable over KubeSpan, so make sure\nthese networks are still reachable via some other route (e.g., direct connection).\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeSpanFiltersConfig configures KubeSpan endpoint filters." }, "network.KubespanEndpointsConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KubeSpanEndpoints" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "extraAnnouncedEndpoints": { "items": { "type": "string" }, "type": "array", "title": "extraAnnouncedEndpoints", "description": "A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\n", "markdownDescription": "A list of extra Wireguard endpoints to announce from this machine.\n\nTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.", "x-intellij-html-description": "\u003cp\u003eA list of extra Wireguard endpoints to announce from this machine.\u003c/p\u003e\n\n\u003cp\u003eTalos automatically adds endpoints based on machine addresses, public IP, etc.\nThis field allows to add extra endpoints which are managed outside of Talos, e.g. NAT mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KubeSpanEndpointsConfig is a config document to configure KubeSpan endpoints." }, "network.Layer2VIPConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "Layer2VIPConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address to be advertised as a Layer 2 VIP.\n", "markdownDescription": "IP address to be advertised as a Layer 2 VIP.", "x-intellij-html-description": "\u003cp\u003eIP address to be advertised as a Layer 2 VIP.\u003c/p\u003e\n" }, "link": { "type": "string", "title": "link", "description": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Name of the link to assign the VIP to.\n\nSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eName of the link to assign the VIP to.\u003c/p\u003e\n\n\u003cp\u003eSelector must match exactly one link, otherwise an error is returned.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "Layer2VIPConfig is a config document to configure virtual IP using Layer 2 (Ethernet) advertisement.\\nVirtual IP configuration should be used only on controlplane nodes to provide virtual IP for Kubernetes API server.\\nAny other use cases are not supported and may lead to unexpected behavior.\\nVirtual IP will be announced from only one node at a time using gratuitous ARP announcements for IPv4.\\n" }, "network.LinkAliasConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "LinkAliasConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Alias for the link.\n\nDon’t use system interface names like “eth0”, “ens3”, “enp0s2”, etc. as those may conflict\nwith existing physical interfaces.\n\nThe name can contain a single integer format verb (%d) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. net0, net1, …) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.\n", "markdownDescription": "Alias for the link.\n\nDon't use system interface names like \"eth0\", \"ens3\", \"enp0s2\", etc. as those may conflict\nwith existing physical interfaces.\n\nThe name can contain a single integer format verb (`%d`) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. `net0`, `net1`, ...) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.", "x-intellij-html-description": "\u003cp\u003eAlias for the link.\u003c/p\u003e\n\n\u003cp\u003eDon\u0026rsquo;t use system interface names like \u0026ldquo;eth0\u0026rdquo;, \u0026ldquo;ens3\u0026rdquo;, \u0026ldquo;enp0s2\u0026rdquo;, etc. as those may conflict\nwith existing physical interfaces.\u003c/p\u003e\n\n\u003cp\u003eThe name can contain a single integer format verb (\u003ccode\u003e%d\u003c/code\u003e) to create multiple aliases\nfrom a single config document. When a format verb is detected, each matched link receives a sequential\nalias (e.g. \u003ccode\u003enet0\u003c/code\u003e, \u003ccode\u003enet1\u003c/code\u003e, \u0026hellip;) based on hardware address order of the links.\nLinks already aliased by a previous config are automatically skipped.\u003c/p\u003e\n" }, "selector": { "$ref": "#/$defs/network.LinkSelector", "title": "selector", "description": "Selector to match the link to alias.\n\nWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. net%d), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.\n", "markdownDescription": "Selector to match the link to alias.\n\nWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. `net%d`), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.", "x-intellij-html-description": "\u003cp\u003eSelector to match the link to alias.\u003c/p\u003e\n\n\u003cp\u003eWhen the alias name is a fixed string, the selector must match exactly one link.\nWhen the alias name contains a format verb (e.g. \u003ccode\u003enet%d\u003c/code\u003e), the selector may match multiple links\nand each match receives a sequential alias.\nIf multiple selectors match the same link, the first one is used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "LinkAliasConfig is a config document to alias (give a different name) to a physical link." }, "network.LinkConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "LinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the link (interface).\n", "markdownDescription": "Name of the link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the link (interface).\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "LinkConfig is a config document to configure physical interfaces (network links)." }, "network.LinkSelector": { "properties": { "match": { "type": "string", "title": "match", "description": "The Common Expression Language (CEL) expression to match the link.\n", "markdownDescription": "The Common Expression Language (CEL) expression to match the link.", "x-intellij-html-description": "\u003cp\u003eThe Common Expression Language (CEL) expression to match the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LinkSelector selects a link to alias." }, "network.NTPConfig": { "properties": { "servers": { "items": { "type": "string" }, "type": "array", "title": "servers", "description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to time.cloudflare.com.\n", "markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `time.cloudflare.com`.", "x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003etime.cloudflare.com\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "NTPConfig represents a NTP server configuration." }, "network.NameserverConfig": { "properties": { "address": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "address", "description": "The IP address of the nameserver.\n", "markdownDescription": "The IP address of the nameserver.", "x-intellij-html-description": "\u003cp\u003eThe IP address of the nameserver.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "NameserverConfig represents a single nameserver configuration." }, "network.PTPConfig": { "properties": { "devices": { "items": { "type": "string" }, "type": "array", "title": "devices", "description": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\nA PTP device is typically represented as a character device file in the /dev directory,\n\n\nsuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\n", "markdownDescription": "description: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\n\n A PTP device is typically represented as a character device file in the /dev directory,\n such as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.", "x-intellij-html-description": "\u003cp\u003edescription: |\n A list of PTP devices to sync with (e.g. provided by the hypervisor).\u003c/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eA PTP device is typically represented as a character device file in the /dev directory,\n\u003c/code\u003e\u003c/pre\u003e\n\n\u003cp\u003esuch as /dev/ptp0 or /dev/ptp_kvm. These devices are used to synchronize the system time\n with an external time source that supports the Precision Time Protocol.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "PTPConfig represents a PTP (Precision Time Protocol) configuration." }, "network.ResolverConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "ResolverConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "nameservers": { "items": { "$ref": "#/$defs/network.NameserverConfig" }, "type": "array", "title": "nameservers", "description": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\n", "markdownDescription": "A list of nameservers (DNS servers) to use for resolving domain names.\n\nNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\n\nThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.", "x-intellij-html-description": "\u003cp\u003eA list of nameservers (DNS servers) to use for resolving domain names.\u003c/p\u003e\n\n\u003cp\u003eNameservers are used to resolve domain names on the host, and they are also\npropagated to Kubernetes DNS (CoreDNS) for use by pods running on the cluster.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any nameservers obtained via DHCP or platform configuration.\nDefault configuration is to use 1.1.1.1 and 8.8.8.8 as nameservers.\u003c/p\u003e\n" }, "searchDomains": { "$ref": "#/$defs/network.SearchDomainsConfig", "title": "searchDomains", "description": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.\n", "markdownDescription": "Configuration for search domains (in /etc/resolv.conf).\n\nThe default is to derive search domains from the hostname FQDN.", "x-intellij-html-description": "\u003cp\u003eConfiguration for search domains (in /etc/resolv.conf).\u003c/p\u003e\n\n\u003cp\u003eThe default is to derive search domains from the hostname FQDN.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "ResolverConfig is a config document to configure DNS resolving." }, "network.RouteConfig": { "properties": { "destination": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$", "title": "destination", "description": "The route’s destination as an address prefix.\n\nIf not specified, a default route will be created for the address family of the gateway.\n", "markdownDescription": "The route's destination as an address prefix.\n\nIf not specified, a default route will be created for the address family of the gateway.", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s destination as an address prefix.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, a default route will be created for the address family of the gateway.\u003c/p\u003e\n" }, "gateway": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "gateway", "description": "The route’s gateway (if empty, creates link scope route).\n", "markdownDescription": "The route's gateway (if empty, creates link scope route).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s gateway (if empty, creates link scope route).\u003c/p\u003e\n" }, "source": { "type": "string", "pattern": "^[0-9a-f.:]+$", "title": "source", "description": "The route’s source address (optional).\n", "markdownDescription": "The route's source address (optional).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s source address (optional).\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The optional MTU for the route.\n", "markdownDescription": "The optional MTU for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional MTU for the route.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "The routing table to use for the route.\n\nIf not specified, the main routing table will be used.\n", "markdownDescription": "The routing table to use for the route.\n\nIf not specified, the main routing table will be used.", "x-intellij-html-description": "\u003cp\u003eThe routing table to use for the route.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the main routing table will be used.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RouteConfig represents a network route configuration." }, "network.RoutingRuleConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "RoutingRuleConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Priority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.\n", "markdownDescription": "Priority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.", "x-intellij-html-description": "\u003cp\u003ePriority of the routing rule.\nLower values are matched first.\nMust be between 1 and 32765 (excluding reserved priorities [0 32500 32501 32766 32767]).\nMust be unique across all routing rules in the configuration.\u003c/p\u003e\n" }, "src": { "type": "string", "title": "src", "description": "Source address prefix to match.\nIf empty, matches all sources.\n", "markdownDescription": "Source address prefix to match.\nIf empty, matches all sources.", "x-intellij-html-description": "\u003cp\u003eSource address prefix to match.\nIf empty, matches all sources.\u003c/p\u003e\n" }, "dst": { "type": "string", "title": "dst", "description": "Destination address prefix to match.\nIf empty, matches all destinations.\n", "markdownDescription": "Destination address prefix to match.\nIf empty, matches all destinations.", "x-intellij-html-description": "\u003cp\u003eDestination address prefix to match.\nIf empty, matches all destinations.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "The routing table to look up if the rule matches.\n", "markdownDescription": "The routing table to look up if the rule matches.", "x-intellij-html-description": "\u003cp\u003eThe routing table to look up if the rule matches.\u003c/p\u003e\n" }, "action": { "type": "integer", "title": "action", "description": "The action to perform when the rule matches.\nDefaults to “unicast” (table lookup).\n", "markdownDescription": "The action to perform when the rule matches.\nDefaults to \"unicast\" (table lookup).", "x-intellij-html-description": "\u003cp\u003eThe action to perform when the rule matches.\nDefaults to \u0026ldquo;unicast\u0026rdquo; (table lookup).\u003c/p\u003e\n" }, "iifName": { "type": "string", "title": "iifName", "description": "Match packets arriving on this interface.\n", "markdownDescription": "Match packets arriving on this interface.", "x-intellij-html-description": "\u003cp\u003eMatch packets arriving on this interface.\u003c/p\u003e\n" }, "oifName": { "type": "string", "title": "oifName", "description": "Match packets going out on this interface.\n", "markdownDescription": "Match packets going out on this interface.", "x-intellij-html-description": "\u003cp\u003eMatch packets going out on this interface.\u003c/p\u003e\n" }, "fwMark": { "type": "integer", "title": "fwMark", "description": "Match packets with this firewall mark value.\n", "markdownDescription": "Match packets with this firewall mark value.", "x-intellij-html-description": "\u003cp\u003eMatch packets with this firewall mark value.\u003c/p\u003e\n" }, "fwMask": { "type": "integer", "title": "fwMask", "description": "Mask for the firewall mark comparison.\n", "markdownDescription": "Mask for the firewall mark comparison.", "x-intellij-html-description": "\u003cp\u003eMask for the firewall mark comparison.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "table" ], "description": "RoutingRuleConfig is a config document to configure Linux policy routing rules." }, "network.RuleConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "NetworkRuleConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "portSelector": { "$ref": "#/$defs/network.RulePortSelector", "title": "portSelector", "description": "Port selector defines which ports and protocols on the host are affected by the rule.\n", "markdownDescription": "Port selector defines which ports and protocols on the host are affected by the rule.", "x-intellij-html-description": "\u003cp\u003ePort selector defines which ports and protocols on the host are affected by the rule.\u003c/p\u003e\n" }, "ingress": { "items": { "$ref": "#/$defs/network.IngressRule" }, "type": "array", "title": "ingress", "description": "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the portSelector.\n", "markdownDescription": "Ingress defines which source subnets are allowed to access the host ports/protocols defined by the `portSelector`.", "x-intellij-html-description": "\u003cp\u003eIngress defines which source subnets are allowed to access the host ports/protocols defined by the \u003ccode\u003eportSelector\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "NetworkRuleConfig is a network firewall rule config document." }, "network.RulePortSelector": { "properties": { "ports": { "items": { "oneOf": [ { "type": "integer" }, { "type": "string" } ] }, "type": "array", "title": "ports", "description": "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\n", "markdownDescription": "Ports defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.", "x-intellij-html-description": "\u003cp\u003ePorts defines a list of port ranges or single ports.\nThe port ranges are inclusive, and should not overlap.\u003c/p\u003e\n" }, "protocol": { "enum": [ "tcp", "udp", "icmp", "icmpv6" ], "title": "protocol", "description": "Protocol defines traffic protocol (e.g. TCP or UDP).\n", "markdownDescription": "Protocol defines traffic protocol (e.g. TCP or UDP).", "x-intellij-html-description": "\u003cp\u003eProtocol defines traffic protocol (e.g. TCP or UDP).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RulePortSelector is a port selector for the network rule." }, "network.SearchDomainsConfig": { "properties": { "domains": { "items": { "type": "string" }, "type": "array", "title": "domains", "description": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if “example.com” is a search domain and a user tries to resolve\n“host”, the system will attempt to resolve “host.example.com”.\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\n", "markdownDescription": "A list of search domains to be used for DNS resolution.\n\nSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \"example.com\" is a search domain and a user tries to resolve\n\"host\", the system will attempt to resolve \"host.example.com\".\n\nThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.", "x-intellij-html-description": "\u003cp\u003eA list of search domains to be used for DNS resolution.\u003c/p\u003e\n\n\u003cp\u003eSearch domains are appended to unqualified domain names during DNS resolution.\nFor example, if \u0026ldquo;example.com\u0026rdquo; is a search domain and a user tries to resolve\n\u0026ldquo;host\u0026rdquo;, the system will attempt to resolve \u0026ldquo;host.example.com\u0026rdquo;.\u003c/p\u003e\n\n\u003cp\u003eThis overrides any search domains obtained via DHCP or platform configuration.\nThe default configuration derives the search domain from the hostname FQDN.\u003c/p\u003e\n" }, "disableDefault": { "type": "boolean", "title": "disableDefault", "description": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\n", "markdownDescription": "Disable default search domain configuration from hostname FQDN.\n\nWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.", "x-intellij-html-description": "\u003cp\u003eDisable default search domain configuration from hostname FQDN.\u003c/p\u003e\n\n\u003cp\u003eWhen set to true, the system will not derive search domains from the hostname FQDN.\nThis allows for a custom configuration of search domains without any defaults.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "SearchDomainsConfig represents search domains configuration." }, "network.StaticHostConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "StaticHostConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "IP address (IPv4 or IPv6) to map the hostnames to.\n", "markdownDescription": "IP address (IPv4 or IPv6) to map the hostnames to.", "x-intellij-html-description": "\u003cp\u003eIP address (IPv4 or IPv6) to map the hostnames to.\u003c/p\u003e\n" }, "hostnames": { "items": { "type": "string" }, "type": "array", "title": "hostnames", "description": "List of hostnames to map to the IP address.\n", "markdownDescription": "List of hostnames to map to the IP address.", "x-intellij-html-description": "\u003cp\u003eList of hostnames to map to the IP address.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "StaticHostConfig is a config document to set /etc/hosts entries." }, "network.TCPProbeConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TCPProbeConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the probe.\n", "markdownDescription": "Name of the probe.", "x-intellij-html-description": "\u003cp\u003eName of the probe.\u003c/p\u003e\n" }, "interval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "interval", "description": "Interval between probe attempts.\nDefaults to 1s.\n", "markdownDescription": "Interval between probe attempts.\nDefaults to 1s.", "x-intellij-html-description": "\u003cp\u003eInterval between probe attempts.\nDefaults to 1s.\u003c/p\u003e\n" }, "failureThreshold": { "type": "integer", "title": "failureThreshold", "description": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\n", "markdownDescription": "Number of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).", "x-intellij-html-description": "\u003cp\u003eNumber of consecutive failures for the probe to be considered failed after having succeeded.\nDefaults to 0 (immediately fail on first failure).\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "Endpoint to probe in the format host:port.\n", "markdownDescription": "Endpoint to probe in the format host:port.", "x-intellij-html-description": "\u003cp\u003eEndpoint to probe in the format host:port.\u003c/p\u003e\n" }, "timeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "timeout", "description": "Timeout for the probe.\nDefaults to 10s.\n", "markdownDescription": "Timeout for the probe.\nDefaults to 10s.", "x-intellij-html-description": "\u003cp\u003eTimeout for the probe.\nDefaults to 10s.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "endpoint", "kind", "name" ], "description": "TCPProbeConfig is a config document to configure network TCP connectivity probes." }, "network.TimeSyncConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TimeSyncConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "enabled": { "type": "boolean", "title": "enabled", "description": "Indicates if the time synchronization is enabled for the machine.\nDefaults to true.\n", "markdownDescription": "Indicates if the time synchronization is enabled for the machine.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the time synchronization is enabled for the machine.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "bootTimeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "bootTimeout", "description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n", "markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)", "x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n" }, "ntp": { "$ref": "#/$defs/network.NTPConfig", "title": "ntp", "description": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\n", "markdownDescription": "Specifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.", "x-intellij-html-description": "\u003cp\u003eSpecifies NTP configuration to sync the time over network.\nMutually exclusive with PTP configuration.\u003c/p\u003e\n" }, "ptp": { "$ref": "#/$defs/network.PTPConfig", "title": "ptp", "description": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\n", "markdownDescription": "Specific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.", "x-intellij-html-description": "\u003cp\u003eSpecific PTP (Precision Time Protocol) configuration to sync the time over PTP devices.\nMutually exclusive with NTP configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "TimeSyncConfig is a config document to configure time synchronization (NTP)." }, "network.VLANConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VLANConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the VLAN link (interface) to be created.\n", "markdownDescription": "Name of the VLAN link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the VLAN link (interface) to be created.\u003c/p\u003e\n" }, "vlanID": { "type": "integer", "title": "vlanID", "description": "VLAN ID to be used for the VLAN link.\n", "markdownDescription": "VLAN ID to be used for the VLAN link.", "x-intellij-html-description": "\u003cp\u003eVLAN ID to be used for the VLAN link.\u003c/p\u003e\n" }, "vlanMode": { "enum": [ "802.1q", "802.1ad" ], "title": "vlanMode", "description": "Set the VLAN mode to use.\nIf not set, defaults to ‘802.1q’.\n", "markdownDescription": "Set the VLAN mode to use.\nIf not set, defaults to '802.1q'.", "x-intellij-html-description": "\u003cp\u003eSet the VLAN mode to use.\nIf not set, defaults to \u0026lsquo;802.1q\u0026rsquo;.\u003c/p\u003e\n" }, "parent": { "type": "string", "title": "parent", "description": "Name of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.\n", "markdownDescription": "Name of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eName of the parent link (interface) on which the VLAN link will be created.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "parent", "vlanID" ], "description": "VLANConfig is a config document to create a VLAN (virtual LAN) over a parent link." }, "network.VRFConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "VRFConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the vrf link (interface) to be created.\n", "markdownDescription": "Name of the vrf link (interface) to be created.", "x-intellij-html-description": "\u003cp\u003eName of the vrf link (interface) to be created.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "pattern": "^[0-9a-f:]+$", "title": "hardwareAddr", "description": "Override the hardware (MAC) address of the link.\n", "markdownDescription": "Override the hardware (MAC) address of the link.", "x-intellij-html-description": "\u003cp\u003eOverride the hardware (MAC) address of the link.\u003c/p\u003e\n" }, "links": { "items": { "type": "string" }, "type": "array", "title": "links", "description": "Names of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.\n", "markdownDescription": "Names of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.", "x-intellij-html-description": "\u003cp\u003eNames of the links (interfaces) to be assigned to this vrf.\nLink aliases can be used here as well.\u003c/p\u003e\n" }, "table": { "type": "string", "title": "table", "description": "Routing table number to use for this vrf.\n", "markdownDescription": "Routing table number to use for this vrf.", "x-intellij-html-description": "\u003cp\u003eRouting table number to use for this vrf.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "links", "name", "table" ], "description": "VRFConfig is a config document to create a vrf and assign links to it." }, "network.WireguardConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "WireguardConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the Wireguard link (interface).\n", "markdownDescription": "Name of the Wireguard link (interface).", "x-intellij-html-description": "\u003cp\u003eName of the Wireguard link (interface).\u003c/p\u003e\n" }, "privateKey": { "type": "string", "title": "privateKey", "description": "Specifies a private key configuration (base64 encoded).\nCan be generated by wg genkey.\n", "markdownDescription": "Specifies a private key configuration (base64 encoded).\nCan be generated by `wg genkey`.", "x-intellij-html-description": "\u003cp\u003eSpecifies a private key configuration (base64 encoded).\nCan be generated by \u003ccode\u003ewg genkey\u003c/code\u003e.\u003c/p\u003e\n" }, "listenPort": { "type": "integer", "title": "listenPort", "description": "Specifies a device’s listening port (UDP).\nIf not specified, a random port will be chosen.\n", "markdownDescription": "Specifies a device's listening port (UDP).\nIf not specified, a random port will be chosen.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s listening port (UDP).\nIf not specified, a random port will be chosen.\u003c/p\u003e\n" }, "firewallMark": { "type": "integer", "title": "firewallMark", "description": "Specifies a device’s firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.\n", "markdownDescription": "Specifies a device's firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s firewall mark.\nUseful for advanced routing setups, marking packets originating from this device.\u003c/p\u003e\n" }, "peers": { "items": { "$ref": "#/$defs/network.WireguardPeer" }, "type": "array", "title": "peers", "description": "Specifies a list of peer configurations to apply to a device.\n", "markdownDescription": "Specifies a list of peer configurations to apply to a device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a list of peer configurations to apply to a device.\u003c/p\u003e\n" }, "up": { "type": "boolean", "title": "up", "description": "Bring the link up or down.\n\nIf not specified, the link will be brought up.\n", "markdownDescription": "Bring the link up or down.\n\nIf not specified, the link will be brought up.", "x-intellij-html-description": "\u003cp\u003eBring the link up or down.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the link will be brought up.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).\n", "markdownDescription": "Configure LinkMTU (Maximum Transmission Unit) for the link.\n\nIf not specified, the system default LinkMTU will be used (usually 1500).", "x-intellij-html-description": "\u003cp\u003eConfigure LinkMTU (Maximum Transmission Unit) for the link.\u003c/p\u003e\n\n\u003cp\u003eIf not specified, the system default LinkMTU will be used (usually 1500).\u003c/p\u003e\n" }, "addresses": { "items": { "$ref": "#/$defs/network.AddressConfig" }, "type": "array", "title": "addresses", "description": "Configure addresses to be statically assigned to the link.\n", "markdownDescription": "Configure addresses to be statically assigned to the link.", "x-intellij-html-description": "\u003cp\u003eConfigure addresses to be statically assigned to the link.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/network.RouteConfig" }, "type": "array", "title": "routes", "description": "Configure routes to be statically created via the link.\n", "markdownDescription": "Configure routes to be statically created via the link.", "x-intellij-html-description": "\u003cp\u003eConfigure routes to be statically created via the link.\u003c/p\u003e\n" }, "multicast": { "type": "boolean", "title": "multicast", "description": "Set the multicast capability of the link.\n", "markdownDescription": "Set the multicast capability of the link.", "x-intellij-html-description": "\u003cp\u003eSet the multicast capability of the link.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name", "privateKey" ], "description": "WireguardConfig is a config document to create and configure a Wireguard network link." }, "network.WireguardPeer": { "properties": { "publicKey": { "type": "string", "title": "publicKey", "description": "Specifies the public key of this peer.\nCan be extracted from private key by running wg pubkey \u0026lt; private.key.\n", "markdownDescription": "Specifies the public key of this peer.\nCan be extracted from private key by running `wg pubkey \u003c private.key`.", "x-intellij-html-description": "\u003cp\u003eSpecifies the public key of this peer.\nCan be extracted from private key by running \u003ccode\u003ewg pubkey \u0026lt; private.key\u003c/code\u003e.\u003c/p\u003e\n" }, "presharedKey": { "type": "string", "title": "presharedKey", "description": "Specifies the preshared key for this peer (base64 encoded).\nCan be generated by wg genpsk.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.\n", "markdownDescription": "Specifies the preshared key for this peer (base64 encoded).\nCan be generated by `wg genpsk`.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.", "x-intellij-html-description": "\u003cp\u003eSpecifies the preshared key for this peer (base64 encoded).\nCan be generated by \u003ccode\u003ewg genpsk\u003c/code\u003e.\nOptional, this key provides an additional layer of symmetric-key cryptography\nto the peer connection.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "pattern": "^([0-9a-f.:]+|\\[[0-9a-f:.]+\\]):\\d{1,5}$", "title": "endpoint", "description": "Specifies the endpoint of this peer entry.\nFormat: :.\nIf not set, the peer should connect to us without us connecting to it first.\n", "markdownDescription": "Specifies the endpoint of this peer entry.\nFormat: \u003cIP address\u003e:\u003cport\u003e.\nIf not set, the peer should connect to us without us connecting to it first.", "x-intellij-html-description": "\u003cp\u003eSpecifies the endpoint of this peer entry.\nFormat: \u003cIP address\u003e:\u003cport\u003e.\nIf not set, the peer should connect to us without us connecting to it first.\u003c/p\u003e\n" }, "persistentKeepaliveInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "persistentKeepaliveInterval", "description": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eSpecifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" }, "allowedIPs": { "items": { "type": "string", "pattern": "^[0-9a-f.:]+/\\d{1,3}$" }, "type": "array", "title": "allowedIPs", "description": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.\n", "markdownDescription": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.", "x-intellij-html-description": "\u003cp\u003eAllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\nThese IPs will be routed to this peer, and defines which IPs this peer is allowed to use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "publicKey" ], "description": "WireguardPeer describes a Wireguard peer configuration." }, "runtime.EnvironmentV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EnvironmentConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "variables": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "variables", "description": "This field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore () character, and\n - contain only uppercase and lowercase letters, underscore () characters, and numbers.\n", "markdownDescription": "This field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore (_) character, and\n - contain only uppercase and lowercase letters, underscore (_) characters, and numbers.", "x-intellij-html-description": "\u003cp\u003eThis field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\nPropagation of environment variables to services is done only at initial service start time.\nTo modify environment variables for services, the node must be restarted.\nMultiple values for the same environment variable (in multiple documents) will replace previous values, with the last one taking precedence.\nFully removing an environment variable can only be achieved by removing it from the document and restarting the machine.\nEnvironment variable names are validated, and should:\n - start with an uppercase letter, lowercase letter, or an underscore (\u003cem\u003e) character, and\n - contain only uppercase and lowercase letters, underscore (\u003c/em\u003e) characters, and numbers.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "EnvironmentConfig is an environment config document." }, "runtime.EventSinkV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "EventSinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "The endpoint for the event sink as ‘host:port’.\n", "markdownDescription": "The endpoint for the event sink as 'host:port'.", "x-intellij-html-description": "\u003cp\u003eThe endpoint for the event sink as \u0026lsquo;host:port\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "EventSinkConfig is a event sink config document." }, "runtime.KmsgLogV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "KmsgLogConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "url": { "type": "string", "pattern": "^(tcp|udp)://", "title": "url", "description": "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\n", "markdownDescription": "The URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.", "x-intellij-html-description": "\u003cp\u003eThe URL encodes the log destination.\nThe scheme must be tcp:// or udp://.\nThe path must be empty.\nThe port is required.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "KmsgLogConfig is a event sink config document." }, "runtime.OOMV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "OOMConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "triggerExpression": { "type": "string", "title": "triggerExpression", "description": "This expression defines when to trigger OOM action.\n\nThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\n\nThis expression receives the following parameters:\n- memory{some,full}{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event\n", "markdownDescription": "This expression defines when to trigger OOM action.\n\nThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\n\nThis expression receives the following parameters:\n- memory_{some,full}_{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event", "x-intellij-html-description": "\u003cp\u003eThis expression defines when to trigger OOM action.\u003c/p\u003e\n\n\u003cp\u003eThe expression must evaluate to a boolean value.\nIf the expression returns true, then OOM ranking and killing will be handled.\u003c/p\u003e\n\n\u003cp\u003eThis expression receives the following parameters:\n- memory\u003cem\u003e{some,full}\u003c/em\u003e{avg10,avg60,avg300,total} - double, representing PSI values\n- time_since_trigger - duration since the last OOM handler trigger event\u003c/p\u003e\n" }, "cgroupRankingExpression": { "type": "string", "title": "cgroupRankingExpression", "description": "This expression defines how to rank cgroups for OOM handler.\n\nThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\n\nThis expression receives the following parameters:\n- memory_max - Optional - in bytes\n- memory_current - Optional - in bytes\n- memory_peak - Optional - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System\n", "markdownDescription": "This expression defines how to rank cgroups for OOM handler.\n\nThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\n\nThis expression receives the following parameters:\n- memory_max - Optional\u003cuint\u003e - in bytes\n- memory_current - Optional\u003cuint\u003e - in bytes\n- memory_peak - Optional\u003cuint\u003e - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System", "x-intellij-html-description": "\u003cp\u003eThis expression defines how to rank cgroups for OOM handler.\u003c/p\u003e\n\n\u003cp\u003eThe cgroup with the highest rank (score) will be evicted first.\nThe expression must evaluate to a double value.\u003c/p\u003e\n\n\u003cp\u003eThis expression receives the following parameters:\n- memory_max - Optional\u003cuint\u003e - in bytes\n- memory_current - Optional\u003cuint\u003e - in bytes\n- memory_peak - Optional\u003cuint\u003e - in bytes\n- path - string, path to the cgroup\n- class - int. This represents cgroup QoS class, and matches one of the constants, which are also provided: Besteffort, Burstable, Guaranteed, Podruntime, System\u003c/p\u003e\n" }, "sampleInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "sampleInterval", "description": "How often should the trigger expression be evaluated.\n\nThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.\n", "markdownDescription": "How often should the trigger expression be evaluated.\n\nThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.", "x-intellij-html-description": "\u003cp\u003eHow often should the trigger expression be evaluated.\u003c/p\u003e\n\n\u003cp\u003eThis interval determines how often should the OOM controller\ncheck for the OOM condition using the provided expression.\nAdjusting it can help tune the reactivity of the OOM handler.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "OOMConfig is a Out of Memory handler config document." }, "runtime.WatchdogTimerV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "WatchdogTimerConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "device": { "type": "string", "title": "device", "description": "Path to the watchdog device.\n", "markdownDescription": "Path to the watchdog device.", "x-intellij-html-description": "\u003cp\u003ePath to the watchdog device.\u003c/p\u003e\n" }, "timeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "timeout", "description": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.\n", "markdownDescription": "Timeout for the watchdog.\n\nIf Talos is unresponsive for this duration, the watchdog will reset the system.\n\nDefault value is 1 minute, minimum value is 10 seconds.", "x-intellij-html-description": "\u003cp\u003eTimeout for the watchdog.\u003c/p\u003e\n\n\u003cp\u003eIf Talos is unresponsive for this duration, the watchdog will reset the system.\u003c/p\u003e\n\n\u003cp\u003eDefault value is 1 minute, minimum value is 10 seconds.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "WatchdogTimerConfig is a watchdog timer config document." }, "security.TrustedRootsConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "TrustedRootsConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name of the config document.\n", "markdownDescription": "Name of the config document.", "x-intellij-html-description": "\u003cp\u003eName of the config document.\u003c/p\u003e\n" }, "certificates": { "type": "string", "title": "certificates", "description": "List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.\n", "markdownDescription": "List of additional trusted certificate authorities (as PEM-encoded certificates).\n\nMultiple certificates can be provided in a single config document, separated by newline characters.", "x-intellij-html-description": "\u003cp\u003eList of additional trusted certificate authorities (as PEM-encoded certificates).\u003c/p\u003e\n\n\u003cp\u003eMultiple certificates can be provided in a single config document, separated by newline characters.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind", "name" ], "description": "TrustedRootsConfig allows to configure additional trusted CA roots." }, "siderolink.ConfigV1Alpha1": { "properties": { "apiVersion": { "enum": [ "v1alpha1" ], "title": "apiVersion", "description": "apiVersion is the API version of the resource.\n", "markdownDescription": "apiVersion is the API version of the resource.", "x-intellij-html-description": "\u003cp\u003eapiVersion is the API version of the resource.\u003c/p\u003e\n" }, "kind": { "enum": [ "SideroLinkConfig" ], "title": "kind", "description": "kind is the kind of the resource.\n", "markdownDescription": "kind is the kind of the resource.", "x-intellij-html-description": "\u003cp\u003ekind is the kind of the resource.\u003c/p\u003e\n" }, "apiUrl": { "type": "string", "pattern": "^(https|grpc)://", "title": "apiUrl", "description": "SideroLink API URL to connect to.\n", "markdownDescription": "SideroLink API URL to connect to.", "x-intellij-html-description": "\u003cp\u003eSideroLink API URL to connect to.\u003c/p\u003e\n" }, "uniqueToken": { "type": "string", "title": "uniqueToken", "description": "SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.\n", "markdownDescription": "SideroLink unique token to use for the connection (optional).\n\nThis value is overridden with META key UniqueMachineToken.", "x-intellij-html-description": "\u003cp\u003eSideroLink unique token to use for the connection (optional).\u003c/p\u003e\n\n\u003cp\u003eThis value is overridden with META key UniqueMachineToken.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "required": [ "apiVersion", "kind" ], "description": "SideroLinkConfig is a SideroLink connection machine configuration document." }, "v1alpha1.APIServerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the API server manifest.\n", "markdownDescription": "The container image used in the API server manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the API server manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the API server.\n", "markdownDescription": "Extra arguments to supply to the API server.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the API server.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the API server static pod.\n", "markdownDescription": "Extra volumes to mount to the API server static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the API server static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the API server’s certificate.\n", "markdownDescription": "Extra certificate subject alternative names for the API server's certificate.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the API server\u0026rsquo;s certificate.\u003c/p\u003e\n" }, "admissionControl": { "items": { "$ref": "#/$defs/v1alpha1.AdmissionPluginConfig" }, "type": "array", "title": "admissionControl", "description": "Configure the API server admission plugins.\n", "markdownDescription": "Configure the API server admission plugins.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server admission plugins.\u003c/p\u003e\n" }, "auditPolicy": { "type": "object", "title": "auditPolicy", "description": "Configure the API server audit policy.\n", "markdownDescription": "Configure the API server audit policy.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server audit policy.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the API server resources.\n", "markdownDescription": "Configure the API server resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server resources.\u003c/p\u003e\n" }, "authorizationConfig": { "items": { "$ref": "#/$defs/v1alpha1.AuthorizationConfigAuthorizerConfig" }, "type": "array", "title": "authorizationConfig", "description": "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.\n", "markdownDescription": "Configure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server authorization config. Node and RBAC authorizers are always added irrespective of the configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "APIServerConfig represents the kube apiserver configuration options." }, "v1alpha1.AdminKubeconfigConfig": { "properties": { "certLifetime": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "certLifetime", "description": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eAdmin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AdminKubeconfigConfig contains admin kubeconfig settings." }, "v1alpha1.AdmissionPluginConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.\n", "markdownDescription": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.", "x-intellij-html-description": "\u003cp\u003eName is the name of the admission controller.\nIt must match the registered admission plugin name.\u003c/p\u003e\n" }, "configuration": { "type": "object", "title": "configuration", "description": "Configuration is an embedded configuration object to be used as the plugin’s\nconfiguration.\n", "markdownDescription": "Configuration is an embedded configuration object to be used as the plugin's\nconfiguration.", "x-intellij-html-description": "\u003cp\u003eConfiguration is an embedded configuration object to be used as the plugin\u0026rsquo;s\nconfiguration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AdmissionPluginConfig represents the API server admission plugin configuration." }, "v1alpha1.AuthorizationConfigAuthorizerConfig": { "properties": { "type": { "type": "string", "title": "type", "description": "Type is the name of the authorizer. Allowed values are Node, RBAC, and Webhook.\n", "markdownDescription": "Type is the name of the authorizer. Allowed values are `Node`, `RBAC`, and `Webhook`.", "x-intellij-html-description": "\u003cp\u003eType is the name of the authorizer. Allowed values are \u003ccode\u003eNode\u003c/code\u003e, \u003ccode\u003eRBAC\u003c/code\u003e, and \u003ccode\u003eWebhook\u003c/code\u003e.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Name is used to describe the authorizer.\n", "markdownDescription": "Name is used to describe the authorizer.", "x-intellij-html-description": "\u003cp\u003eName is used to describe the authorizer.\u003c/p\u003e\n" }, "webhook": { "type": "object", "title": "webhook", "description": "webhook is the configuration for the webhook authorizer.\n", "markdownDescription": "webhook is the configuration for the webhook authorizer.", "x-intellij-html-description": "\u003cp\u003ewebhook is the configuration for the webhook authorizer.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "AuthorizationConfigAuthorizerConfig represents the API server authorization config authorizer configuration." }, "v1alpha1.CNIConfig": { "properties": { "name": { "enum": [ "flannel", "custom", "none" ], "title": "name", "description": "Name of CNI to use.\n", "markdownDescription": "Name of CNI to use.", "x-intellij-html-description": "\u003cp\u003eName of CNI to use.\u003c/p\u003e\n" }, "urls": { "items": { "type": "string" }, "type": "array", "title": "urls", "description": "URLs containing manifests to apply for the CNI.\nShould be present for “custom”, must be empty for “flannel” and “none”.\n", "markdownDescription": "URLs containing manifests to apply for the CNI.\nShould be present for \"custom\", must be empty for \"flannel\" and \"none\".", "x-intellij-html-description": "\u003cp\u003eURLs containing manifests to apply for the CNI.\nShould be present for \u0026ldquo;custom\u0026rdquo;, must be empty for \u0026ldquo;flannel\u0026rdquo; and \u0026ldquo;none\u0026rdquo;.\u003c/p\u003e\n" }, "flannel": { "$ref": "#/$defs/v1alpha1.FlannelCNIConfig", "title": "flannel", "description": "description: |\nFlannel configuration options.\n", "markdownDescription": "description: |\nFlannel configuration options.", "x-intellij-html-description": "\u003cp\u003edescription: |\nFlannel configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CNIConfig represents the CNI configuration options." }, "v1alpha1.ClusterConfig": { "properties": { "id": { "type": "string", "title": "id", "description": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).\n", "markdownDescription": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).", "x-intellij-html-description": "\u003cp\u003eGlobally unique identifier for this cluster (base64 encoded random 32 bytes).\u003c/p\u003e\n" }, "secret": { "type": "string", "title": "secret", "description": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\n", "markdownDescription": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.", "x-intellij-html-description": "\u003cp\u003eShared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/v1alpha1.ControlPlaneConfig", "title": "controlPlane", "description": "Provides control plane specific configuration options.\n", "markdownDescription": "Provides control plane specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides control plane specific configuration options.\u003c/p\u003e\n" }, "clusterName": { "type": "string", "title": "clusterName", "description": "Configures the cluster’s name.\n", "markdownDescription": "Configures the cluster's name.", "x-intellij-html-description": "\u003cp\u003eConfigures the cluster\u0026rsquo;s name.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/v1alpha1.ClusterNetworkConfig", "title": "network", "description": "Provides cluster specific network configuration options.\n", "markdownDescription": "Provides cluster specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific network configuration options.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The bootstrap token used to join the cluster.\n", "markdownDescription": "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.", "x-intellij-html-description": "\u003cp\u003eThe \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/\" target=\"_blank\"\u003ebootstrap token\u003c/a\u003e used to join the cluster.\u003c/p\u003e\n" }, "aescbcEncryptionSecret": { "type": "string", "title": "aescbcEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with AESCBC.\u003c/p\u003e\n" }, "secretboxEncryptionSecret": { "type": "string", "title": "secretboxEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The base64 encoded root certificate authority used by Kubernetes.\n", "markdownDescription": "The base64 encoded root certificate authority used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded root certificate authority used by Kubernetes.\u003c/p\u003e\n" }, "acceptedCAs": { "properties": { "crt": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "acceptedCAs", "description": "The list of base64 encoded accepted certificate authorities used by Kubernetes.\n", "markdownDescription": "The list of base64 encoded accepted certificate authorities used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe list of base64 encoded accepted certificate authorities used by Kubernetes.\u003c/p\u003e\n" }, "aggregatorCA": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "aggregatorCA", "description": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.\n", "markdownDescription": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\u003c/p\u003e\n\n\u003cp\u003eThis CA can be self-signed.\u003c/p\u003e\n" }, "serviceAccount": { "properties": { "key": { "additionalProperties": false, "type": "string" } }, "additionalProperties": false, "type": "object", "title": "serviceAccount", "description": "The base64 encoded private key for service account token generation.\n", "markdownDescription": "The base64 encoded private key for service account token generation.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded private key for service account token generation.\u003c/p\u003e\n" }, "apiServer": { "$ref": "#/$defs/v1alpha1.APIServerConfig", "title": "apiServer", "description": "API server specific configuration options.\n", "markdownDescription": "API server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eAPI server specific configuration options.\u003c/p\u003e\n" }, "controllerManager": { "$ref": "#/$defs/v1alpha1.ControllerManagerConfig", "title": "controllerManager", "description": "Controller manager server specific configuration options.\n", "markdownDescription": "Controller manager server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager server specific configuration options.\u003c/p\u003e\n" }, "proxy": { "$ref": "#/$defs/v1alpha1.ProxyConfig", "title": "proxy", "description": "Kube-proxy server-specific configuration options\n", "markdownDescription": "Kube-proxy server-specific configuration options", "x-intellij-html-description": "\u003cp\u003eKube-proxy server-specific configuration options\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/v1alpha1.SchedulerConfig", "title": "scheduler", "description": "Scheduler server specific configuration options.\n", "markdownDescription": "Scheduler server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler server specific configuration options.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/v1alpha1.ClusterDiscoveryConfig", "title": "discovery", "description": "Configures cluster member discovery.\n", "markdownDescription": "Configures cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigures cluster member discovery.\u003c/p\u003e\n" }, "etcd": { "$ref": "#/$defs/v1alpha1.EtcdConfig", "title": "etcd", "description": "Etcd specific configuration options.\n", "markdownDescription": "Etcd specific configuration options.", "x-intellij-html-description": "\u003cp\u003eEtcd specific configuration options.\u003c/p\u003e\n" }, "coreDNS": { "$ref": "#/$defs/v1alpha1.CoreDNS", "title": "coreDNS", "description": "Core DNS specific configuration options.\n", "markdownDescription": "Core DNS specific configuration options.", "x-intellij-html-description": "\u003cp\u003eCore DNS specific configuration options.\u003c/p\u003e\n" }, "externalCloudProvider": { "$ref": "#/$defs/v1alpha1.ExternalCloudProviderConfig", "title": "externalCloudProvider", "description": "External cloud provider configuration.\n", "markdownDescription": "External cloud provider configuration.", "x-intellij-html-description": "\u003cp\u003eExternal cloud provider configuration.\u003c/p\u003e\n" }, "extraManifests": { "items": { "type": "string" }, "type": "array", "title": "extraManifests", "description": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "extraManifestHeaders": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraManifestHeaders", "description": "A map of key value pairs that will be added while fetching the extraManifests.\n", "markdownDescription": "A map of key value pairs that will be added while fetching the extraManifests.", "x-intellij-html-description": "\u003cp\u003eA map of key value pairs that will be added while fetching the extraManifests.\u003c/p\u003e\n" }, "inlineManifests": { "items": { "$ref": "#/$defs/v1alpha1.ClusterInlineManifest" }, "type": "array", "title": "inlineManifests", "description": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "adminKubeconfig": { "$ref": "#/$defs/v1alpha1.AdminKubeconfigConfig", "title": "adminKubeconfig", "description": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.\n", "markdownDescription": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.", "x-intellij-html-description": "\u003cp\u003eSettings for admin kubeconfig generation.\nCertificate lifetime can be configured.\u003c/p\u003e\n" }, "allowSchedulingOnControlPlanes": { "type": "boolean", "title": "allowSchedulingOnControlPlanes", "description": "Allows running workload on control-plane nodes.\n", "markdownDescription": "Allows running workload on control-plane nodes.", "x-intellij-html-description": "\u003cp\u003eAllows running workload on control-plane nodes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterConfig represents the cluster-wide config values." }, "v1alpha1.ClusterDiscoveryConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\n", "markdownDescription": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.", "x-intellij-html-description": "\u003cp\u003eEnable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/v1alpha1.DiscoveryRegistriesConfig", "title": "registries", "description": "Configure registries used for cluster member discovery.\n", "markdownDescription": "Configure registries used for cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigure registries used for cluster member discovery.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterDiscoveryConfig struct configures cluster membership discovery." }, "v1alpha1.ClusterInlineManifest": { "properties": { "name": { "type": "string", "title": "name", "description": "Name of the manifest.\nName should be unique.\n", "markdownDescription": "Name of the manifest.\nName should be unique.", "x-intellij-html-description": "\u003cp\u003eName of the manifest.\nName should be unique.\u003c/p\u003e\n" }, "contents": { "type": "string", "title": "contents", "description": "Manifest contents as a string.\n", "markdownDescription": "Manifest contents as a string.", "x-intellij-html-description": "\u003cp\u003eManifest contents as a string.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterInlineManifest struct describes inline bootstrap manifests for the user." }, "v1alpha1.ClusterNetworkConfig": { "properties": { "cni": { "$ref": "#/$defs/v1alpha1.CNIConfig", "title": "cni", "description": "The CNI used.\nComposed of “name” and “urls”.\nThe “name” key supports the following options: “flannel”, “custom”, and “none”.\n“flannel” uses Talos-managed Flannel CNI, and that’s the default option.\n“custom” uses custom manifests that should be provided in “urls”.\n“none” indicates that Talos will not manage any CNI installation.\n", "markdownDescription": "The CNI used.\nComposed of \"name\" and \"urls\".\nThe \"name\" key supports the following options: \"flannel\", \"custom\", and \"none\".\n\"flannel\" uses Talos-managed Flannel CNI, and that's the default option.\n\"custom\" uses custom manifests that should be provided in \"urls\".\n\"none\" indicates that Talos will not manage any CNI installation.", "x-intellij-html-description": "\u003cp\u003eThe CNI used.\nComposed of \u0026ldquo;name\u0026rdquo; and \u0026ldquo;urls\u0026rdquo;.\nThe \u0026ldquo;name\u0026rdquo; key supports the following options: \u0026ldquo;flannel\u0026rdquo;, \u0026ldquo;custom\u0026rdquo;, and \u0026ldquo;none\u0026rdquo;.\n\u0026ldquo;flannel\u0026rdquo; uses Talos-managed Flannel CNI, and that\u0026rsquo;s the default option.\n\u0026ldquo;custom\u0026rdquo; uses custom manifests that should be provided in \u0026ldquo;urls\u0026rdquo;.\n\u0026ldquo;none\u0026rdquo; indicates that Talos will not manage any CNI installation.\u003c/p\u003e\n" }, "dnsDomain": { "type": "string", "title": "dnsDomain", "description": "The domain used by Kubernetes DNS.\nThe default is cluster.local\n", "markdownDescription": "The domain used by Kubernetes DNS.\nThe default is `cluster.local`", "x-intellij-html-description": "\u003cp\u003eThe domain used by Kubernetes DNS.\nThe default is \u003ccode\u003ecluster.local\u003c/code\u003e\u003c/p\u003e\n" }, "podSubnets": { "items": { "type": "string" }, "type": "array", "title": "podSubnets", "description": "The pod subnet CIDR.\n", "markdownDescription": "The pod subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe pod subnet CIDR.\u003c/p\u003e\n" }, "serviceSubnets": { "items": { "type": "string" }, "type": "array", "title": "serviceSubnets", "description": "The service subnet CIDR.\n", "markdownDescription": "The service subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe service subnet CIDR.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ClusterNetworkConfig represents kube networking configuration options." }, "v1alpha1.Config": { "properties": { "version": { "enum": [ "v1alpha1" ], "title": "version", "description": "Indicates the schema used to decode the contents.\n", "markdownDescription": "Indicates the schema used to decode the contents.", "x-intellij-html-description": "\u003cp\u003eIndicates the schema used to decode the contents.\u003c/p\u003e\n" }, "debug": { "type": "boolean", "title": "debug", "description": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\nNote: To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\n", "markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.", "x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n" }, "machine": { "$ref": "#/$defs/v1alpha1.MachineConfig", "title": "machine", "description": "Provides machine specific configuration options.\n", "markdownDescription": "Provides machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific configuration options.\u003c/p\u003e\n" }, "cluster": { "$ref": "#/$defs/v1alpha1.ClusterConfig", "title": "cluster", "description": "Provides cluster specific configuration options.\n", "markdownDescription": "Provides cluster specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "Config defines the v1alpha1.Config Talos machine configuration document." }, "v1alpha1.ControlPlaneConfig": { "properties": { "endpoint": { "type": "string", "pattern": "^https://", "format": "uri", "title": "endpoint", "description": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\n", "markdownDescription": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.", "x-intellij-html-description": "\u003cp\u003eEndpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\u003c/p\u003e\n" }, "localAPIServerPort": { "type": "integer", "title": "localAPIServerPort", "description": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is 6443.\n", "markdownDescription": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is `6443`.", "x-intellij-html-description": "\u003cp\u003eThe port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is \u003ccode\u003e6443\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ControlPlaneConfig represents the control plane configuration options." }, "v1alpha1.ControllerManagerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the controller manager manifest.\n", "markdownDescription": "The container image used in the controller manager manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the controller manager manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the controller manager.\n", "markdownDescription": "Extra arguments to supply to the controller manager.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the controller manager.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the controller manager static pod.\n", "markdownDescription": "Extra volumes to mount to the controller manager static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the controller manager static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the controller manager resources.\n", "markdownDescription": "Configure the controller manager resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the controller manager resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ControllerManagerConfig represents the kube controller manager configuration options." }, "v1alpha1.CoreDNS": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable coredns deployment on cluster bootstrap.\n", "markdownDescription": "Disable coredns deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable coredns deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The image field is an override to the default coredns image.\n", "markdownDescription": "The `image` field is an override to the default coredns image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default coredns image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "CoreDNS represents the CoreDNS config values." }, "v1alpha1.DiscoveryRegistriesConfig": { "properties": { "kubernetes": { "$ref": "#/$defs/v1alpha1.RegistryKubernetesConfig", "title": "kubernetes", "description": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.\n", "markdownDescription": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n\nThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee https://github.com/siderolabs/talos/issues/9980 for more information.", "x-intellij-html-description": "\u003cp\u003eKubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\u003c/p\u003e\n\n\u003cp\u003eThis feature is deprecated as it is not compatible with Kubernetes 1.32+.\nSee \u003ca href=\"https://github.com/siderolabs/talos/issues/9980\" target=\"_blank\"\u003ehttps://github.com/siderolabs/talos/issues/9980\u003c/a\u003e for more information.\u003c/p\u003e\n" }, "service": { "$ref": "#/$defs/v1alpha1.RegistryServiceConfig", "title": "service", "description": "Service registry is using an external service to push and pull information about cluster members.\n", "markdownDescription": "Service registry is using an external service to push and pull information about cluster members.", "x-intellij-html-description": "\u003cp\u003eService registry is using an external service to push and pull information about cluster members.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "DiscoveryRegistriesConfig struct configures cluster membership discovery." }, "v1alpha1.Endpoint": { "properties": {}, "additionalProperties": false, "type": "object", "description": "Endpoint represents the endpoint URL parsed out of the machine config." }, "v1alpha1.EtcdConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used to create the etcd service.\n", "markdownDescription": "The container image used to create the etcd service.", "x-intellij-html-description": "\u003cp\u003eThe container image used to create the etcd service.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The ca is the root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The `ca` is the root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eca\u003c/code\u003e is the root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n\nname\ndata-dir\ninitial-cluster-state\nlisten-peer-urls\nlisten-client-urls\ncert-file\nkey-file\ntrusted-ca-file\npeer-client-cert-auth\npeer-cert-file\npeer-trusted-ca-file\npeer-key-file\n\n", "markdownDescription": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n- `name`\n- `data-dir`\n- `initial-cluster-state`\n- `listen-peer-urls`\n- `listen-client-urls`\n- `cert-file`\n- `key-file`\n- `trusted-ca-file`\n- `peer-client-cert-auth`\n- `peer-cert-file`\n- `peer-trusted-ca-file`\n- `peer-key-file`", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to etcd.\nNote that the following args are not allowed:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edata-dir\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003einitial-cluster-state\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-peer-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-client-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ekey-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etrusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-client-cert-auth\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-cert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-trusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-key-file\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "advertisedSubnets": { "items": { "type": "string" }, "type": "array", "title": "advertisedSubnets", "description": "The advertisedSubnets field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eadvertisedSubnets\u003c/code\u003e field configures the networks to pick etcd advertised IP from.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" }, "listenSubnets": { "items": { "type": "string" }, "type": "array", "title": "listenSubnets", "description": "The listenSubnets field configures the networks for the etcd to listen for peer and client connections.\n\nIf listenSubnets is not set, but advertisedSubnets is set, listenSubnets defaults to\nadvertisedSubnets.\n\nIf neither advertisedSubnets nor listenSubnets is set, listenSubnets defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.\n\nIf `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to\n`advertisedSubnets`.\n\nIf neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003elistenSubnets\u003c/code\u003e field configures the networks for the etcd to listen for peer and client connections.\u003c/p\u003e\n\n\u003cp\u003eIf \u003ccode\u003elistenSubnets\u003c/code\u003e is not set, but \u003ccode\u003eadvertisedSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to\n\u003ccode\u003eadvertisedSubnets\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eIf neither \u003ccode\u003eadvertisedSubnets\u003c/code\u003e nor \u003ccode\u003elistenSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to listen on all addresses.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "EtcdConfig represents the etcd configuration options." }, "v1alpha1.ExternalCloudProviderConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable external cloud provider.\n", "markdownDescription": "Enable external cloud provider.", "x-intellij-html-description": "\u003cp\u003eEnable external cloud provider.\u003c/p\u003e\n" }, "manifests": { "items": { "type": "string" }, "type": "array", "title": "manifests", "description": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExternalCloudProviderConfig contains external cloud provider configuration." }, "v1alpha1.ExtraMount": { "properties": { "destination": { "type": "string", "title": "destination", "description": "Destination is the absolute path where the mount will be placed in the container.\n", "markdownDescription": "Destination is the absolute path where the mount will be placed in the container.", "x-intellij-html-description": "\u003cp\u003eDestination is the absolute path where the mount will be placed in the container.\u003c/p\u003e\n" }, "type": { "type": "string", "title": "type", "description": "Type specifies the mount kind.\n", "markdownDescription": "Type specifies the mount kind.", "x-intellij-html-description": "\u003cp\u003eType specifies the mount kind.\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "Source specifies the source path of the mount.\n", "markdownDescription": "Source specifies the source path of the mount.", "x-intellij-html-description": "\u003cp\u003eSource specifies the source path of the mount.\u003c/p\u003e\n" }, "options": { "items": { "type": "string" }, "type": "array", "title": "options", "description": "Options are fstab style mount options.\n", "markdownDescription": "Options are fstab style mount options.", "x-intellij-html-description": "\u003cp\u003eOptions are fstab style mount options.\u003c/p\u003e\n" }, "uidMappings": { "items": { "$ref": "#/$defs/v1alpha1.LinuxIDMapping" }, "type": "array", "title": "uidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" }, "gidMappings": { "items": { "$ref": "#/$defs/v1alpha1.LinuxIDMapping" }, "type": "array", "title": "gidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ExtraMount wraps OCI Mount specification." }, "v1alpha1.FeaturesConfig": { "properties": { "kubernetesTalosAPIAccess": { "$ref": "#/$defs/v1alpha1.KubernetesTalosAPIAccessConfig", "title": "kubernetesTalosAPIAccess", "description": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.\n", "markdownDescription": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.", "x-intellij-html-description": "\u003cp\u003eConfigure Talos API access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eThis feature is disabled if the feature config is not specified.\u003c/p\u003e\n" }, "diskQuotaSupport": { "type": "boolean", "title": "diskQuotaSupport", "description": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\n", "markdownDescription": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.", "x-intellij-html-description": "\u003cp\u003eEnable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\u003c/p\u003e\n" }, "kubePrism": { "$ref": "#/$defs/v1alpha1.KubePrism", "title": "kubePrism", "description": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\n", "markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.", "x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n" }, "hostDNS": { "$ref": "#/$defs/v1alpha1.HostDNSConfig", "title": "hostDNS", "description": "Configures host DNS caching resolver.\n", "markdownDescription": "Configures host DNS caching resolver.", "x-intellij-html-description": "\u003cp\u003eConfigures host DNS caching resolver.\u003c/p\u003e\n" }, "imageCache": { "$ref": "#/$defs/v1alpha1.ImageCacheConfig", "title": "imageCache", "description": "Enable Image Cache feature.\n", "markdownDescription": "Enable Image Cache feature.", "x-intellij-html-description": "\u003cp\u003eEnable Image Cache feature.\u003c/p\u003e\n" }, "nodeAddressSortAlgorithm": { "type": "string", "title": "nodeAddressSortAlgorithm", "description": "Select the node address sort algorithm.\nThe ‘v1’ algorithm sorts addresses by the address itself.\nThe ‘v2’ algorithm prefers more specific prefixes.\nIf unset, defaults to ‘v1’.\n", "markdownDescription": "Select the node address sort algorithm.\nThe 'v1' algorithm sorts addresses by the address itself.\nThe 'v2' algorithm prefers more specific prefixes.\nIf unset, defaults to 'v1'.", "x-intellij-html-description": "\u003cp\u003eSelect the node address sort algorithm.\nThe \u0026lsquo;v1\u0026rsquo; algorithm sorts addresses by the address itself.\nThe \u0026lsquo;v2\u0026rsquo; algorithm prefers more specific prefixes.\nIf unset, defaults to \u0026lsquo;v1\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FeaturesConfig describes individual Talos features that can be switched on or off." }, "v1alpha1.FlannelCNIConfig": { "properties": { "extraArgs": { "items": { "type": "string" }, "type": "array", "title": "extraArgs", "description": "Extra arguments for ‘flanneld’.\n", "markdownDescription": "Extra arguments for 'flanneld'.", "x-intellij-html-description": "\u003cp\u003eExtra arguments for \u0026lsquo;flanneld\u0026rsquo;.\u003c/p\u003e\n" }, "kubeNetworkPoliciesEnabled": { "type": "boolean", "title": "kubeNetworkPoliciesEnabled", "description": "Deploys kube-network-policies along with Flannel.\n\nThis enables Kubernetes Network Policies support in the cluster.\n", "markdownDescription": "Deploys kube-network-policies along with Flannel.\n\nThis enables Kubernetes Network Policies support in the cluster.", "x-intellij-html-description": "\u003cp\u003eDeploys kube-network-policies along with Flannel.\u003c/p\u003e\n\n\u003cp\u003eThis enables Kubernetes Network Policies support in the cluster.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "FlannelCNIConfig represents the Flannel CNI configuration options." }, "v1alpha1.HostDNSConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable host DNS caching resolver.\n", "markdownDescription": "Enable host DNS caching resolver.", "x-intellij-html-description": "\u003cp\u003eEnable host DNS caching resolver.\u003c/p\u003e\n" }, "forwardKubeDNSToHost": { "type": "boolean", "title": "forwardKubeDNSToHost", "description": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\n", "markdownDescription": "Use the host DNS resolver as upstream for Kubernetes CoreDNS pods.\n\nWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).", "x-intellij-html-description": "\u003cp\u003eUse the host DNS resolver as upstream for Kubernetes CoreDNS pods.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, CoreDNS pods use host DNS server as the upstream DNS (instead of\nusing configured upstream DNS resolvers directly).\u003c/p\u003e\n" }, "resolveMemberNames": { "type": "boolean", "title": "resolveMemberNames", "description": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\n", "markdownDescription": "Resolve member hostnames using the host DNS resolver.\n\nWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.", "x-intellij-html-description": "\u003cp\u003eResolve member hostnames using the host DNS resolver.\u003c/p\u003e\n\n\u003cp\u003eWhen enabled, cluster member hostnames and node names are resolved using the host DNS resolver.\nThis requires service discovery to be enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "HostDNSConfig describes the configuration for the host DNS resolver." }, "v1alpha1.ImageCacheConfig": { "properties": { "localEnabled": { "type": "boolean", "title": "localEnabled", "description": "Enable local image cache.\n", "markdownDescription": "Enable local image cache.", "x-intellij-html-description": "\u003cp\u003eEnable local image cache.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ImageCacheConfig describes the configuration for the Image Cache feature." }, "v1alpha1.InstallConfig": { "properties": { "disk": { "type": "string", "title": "disk", "description": "The disk used for installations.\n", "markdownDescription": "The disk used for installations.", "x-intellij-html-description": "\u003cp\u003eThe disk used for installations.\u003c/p\u003e\n" }, "diskSelector": { "$ref": "#/$defs/v1alpha1.InstallDiskSelector", "title": "diskSelector", "description": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over disk.\n", "markdownDescription": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over `disk`.", "x-intellij-html-description": "\u003cp\u003eLook up disk using disk attributes like model, size, serial and others.\nAlways has priority over \u003ccode\u003edisk\u003c/code\u003e.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\nGitHub releases page.\n", "markdownDescription": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n[GitHub releases page](https://github.com/siderolabs/talos/releases).", "x-intellij-html-description": "\u003cp\u003eAllows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n\u003ca href=\"https://github.com/siderolabs/talos/releases\" target=\"_blank\"\u003eGitHub releases page\u003c/a\u003e.\u003c/p\u003e\n" }, "wipe": { "type": "boolean", "title": "wipe", "description": "Indicates if the installation disk should be wiped at installation time.\nDefaults to true.\n", "markdownDescription": "Indicates if the installation disk should be wiped at installation time.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the installation disk should be wiped at installation time.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "legacyBIOSSupport": { "type": "boolean", "title": "legacyBIOSSupport", "description": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn’t support GPT partitioning scheme.\n", "markdownDescription": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme.", "x-intellij-html-description": "\u003cp\u003eIndicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn\u0026rsquo;t support GPT partitioning scheme.\u003c/p\u003e\n" }, "grubUseUKICmdline": { "type": "boolean", "title": "grubUseUKICmdline", "description": "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.\n", "markdownDescription": "Indicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.", "x-intellij-html-description": "\u003cp\u003eIndicates if legacy GRUB bootloader should use kernel cmdline from the UKI instead of building it on the host.\nThis changes the way cmdline is managed with GRUB bootloader to be more consistent with UKI/systemd-boot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "InstallConfig represents the installation options for preparing a node." }, "v1alpha1.InstallDiskSelector": { "properties": { "size": { "type": "string", "title": "size", "description": "Disk size.\n", "markdownDescription": "Disk size.", "x-intellij-html-description": "\u003cp\u003eDisk size.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Disk name /sys/block/\u0026lt;dev\u0026gt;/device/name.\n", "markdownDescription": "Disk name `/sys/block/\u003cdev\u003e/device/name`.", "x-intellij-html-description": "\u003cp\u003eDisk name \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/name\u003c/code\u003e.\u003c/p\u003e\n" }, "model": { "type": "string", "title": "model", "description": "Disk model /sys/block/\u0026lt;dev\u0026gt;/device/model.\n", "markdownDescription": "Disk model `/sys/block/\u003cdev\u003e/device/model`.", "x-intellij-html-description": "\u003cp\u003eDisk model \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/model\u003c/code\u003e.\u003c/p\u003e\n" }, "serial": { "type": "string", "title": "serial", "description": "Disk serial number /sys/block/\u0026lt;dev\u0026gt;/serial.\n", "markdownDescription": "Disk serial number `/sys/block/\u003cdev\u003e/serial`.", "x-intellij-html-description": "\u003cp\u003eDisk serial number \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/serial\u003c/code\u003e.\u003c/p\u003e\n" }, "modalias": { "type": "string", "title": "modalias", "description": "Disk modalias /sys/block/\u0026lt;dev\u0026gt;/device/modalias.\n", "markdownDescription": "Disk modalias `/sys/block/\u003cdev\u003e/device/modalias`.", "x-intellij-html-description": "\u003cp\u003eDisk modalias \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/modalias\u003c/code\u003e.\u003c/p\u003e\n" }, "uuid": { "type": "string", "title": "uuid", "description": "Disk UUID /sys/block/\u0026lt;dev\u0026gt;/uuid.\n", "markdownDescription": "Disk UUID `/sys/block/\u003cdev\u003e/uuid`.", "x-intellij-html-description": "\u003cp\u003eDisk UUID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/uuid\u003c/code\u003e.\u003c/p\u003e\n" }, "wwid": { "type": "string", "title": "wwid", "description": "Disk WWID /sys/block/\u0026lt;dev\u0026gt;/wwid.\n", "markdownDescription": "Disk WWID `/sys/block/\u003cdev\u003e/wwid`.", "x-intellij-html-description": "\u003cp\u003eDisk WWID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/wwid\u003c/code\u003e.\u003c/p\u003e\n" }, "type": { "enum": [ "ssd", "hdd", "nvme", "sd" ], "title": "type", "description": "Disk Type.\n", "markdownDescription": "Disk Type.", "x-intellij-html-description": "\u003cp\u003eDisk Type.\u003c/p\u003e\n" }, "busPath": { "type": "string", "title": "busPath", "description": "Disk bus path.\n", "markdownDescription": "Disk bus path.", "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "InstallDiskSelector represents a disk query parameters for the install disk lookup." }, "v1alpha1.KernelConfig": { "properties": { "modules": { "items": { "$ref": "#/$defs/v1alpha1.KernelModuleConfig" }, "type": "array", "title": "modules", "description": "Kernel modules to load.\n", "markdownDescription": "Kernel modules to load.", "x-intellij-html-description": "\u003cp\u003eKernel modules to load.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KernelConfig struct configures Talos Linux kernel." }, "v1alpha1.KernelModuleConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Module name.\n", "markdownDescription": "Module name.", "x-intellij-html-description": "\u003cp\u003eModule name.\u003c/p\u003e\n" }, "parameters": { "items": { "type": "string" }, "type": "array", "title": "parameters", "description": "Module parameters, changes applied after reboot.\n", "markdownDescription": "Module parameters, changes applied after reboot.", "x-intellij-html-description": "\u003cp\u003eModule parameters, changes applied after reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KernelModuleConfig struct configures Linux kernel modules to load." }, "v1alpha1.KubePrism": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable KubePrism support - will start local load balancing proxy.\n", "markdownDescription": "Enable KubePrism support - will start local load balancing proxy.", "x-intellij-html-description": "\u003cp\u003eEnable KubePrism support - will start local load balancing proxy.\u003c/p\u003e\n" }, "port": { "type": "integer", "title": "port", "description": "KubePrism port.\n", "markdownDescription": "KubePrism port.", "x-intellij-html-description": "\u003cp\u003eKubePrism port.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubePrism describes the configuration for the KubePrism load balancer." }, "v1alpha1.KubeletConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The image field is an optional reference to an alternative kubelet image.\n", "markdownDescription": "The `image` field is an optional reference to an alternative kubelet image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an optional reference to an alternative kubelet image.\u003c/p\u003e\n" }, "clusterDNS": { "items": { "type": "string" }, "type": "array", "title": "clusterDNS", "description": "The ClusterDNS field is an optional reference to an alternative kubelet clusterDNS ip list.\n", "markdownDescription": "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eClusterDNS\u003c/code\u003e field is an optional reference to an alternative kubelet clusterDNS ip list.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "The extraArgs field is used to provide additional flags to the kubelet.\n", "markdownDescription": "The `extraArgs` field is used to provide additional flags to the kubelet.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraArgs\u003c/code\u003e field is used to provide additional flags to the kubelet.\u003c/p\u003e\n" }, "extraMounts": { "items": { "$ref": "#/$defs/v1alpha1.ExtraMount" }, "type": "array", "title": "extraMounts", "description": "The extraMounts field is used to add additional mounts to the kubelet container.\nNote that either bind or rbind are required in the options.\n", "markdownDescription": "The `extraMounts` field is used to add additional mounts to the kubelet container.\nNote that either `bind` or `rbind` are required in the `options`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraMounts\u003c/code\u003e field is used to add additional mounts to the kubelet container.\nNote that either \u003ccode\u003ebind\u003c/code\u003e or \u003ccode\u003erbind\u003c/code\u003e are required in the \u003ccode\u003eoptions\u003c/code\u003e.\u003c/p\u003e\n" }, "extraConfig": { "type": "object", "title": "extraConfig", "description": "The extraConfig field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\n", "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, "credentialProviderConfig": { "type": "object", "title": "credentialProviderConfig", "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled", "description": "Enable container runtime default Seccomp profile.\n", "markdownDescription": "Enable container runtime default Seccomp profile.", "x-intellij-html-description": "\u003cp\u003eEnable container runtime default Seccomp profile.\u003c/p\u003e\n" }, "registerWithFQDN": { "type": "boolean", "title": "registerWithFQDN", "description": "The registerWithFQDN field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\n", "markdownDescription": "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eregisterWithFQDN\u003c/code\u003e field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\u003c/p\u003e\n" }, "nodeIP": { "$ref": "#/$defs/v1alpha1.KubeletNodeIPConfig", "title": "nodeIP", "description": "The nodeIP field is used to configure --node-ip flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\n", "markdownDescription": "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003enodeIP\u003c/code\u003e field is used to configure \u003ccode\u003e--node-ip\u003c/code\u003e flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\u003c/p\u003e\n" }, "skipNodeRegistration": { "type": "boolean", "title": "skipNodeRegistration", "description": "The skipNodeRegistration is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\n", "markdownDescription": "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eskipNodeRegistration\u003c/code\u003e is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\u003c/p\u003e\n" }, "disableManifestsDirectory": { "type": "boolean", "title": "disableManifestsDirectory", "description": "The disableManifestsDirectory field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt’s recommended to configure static pods with the “pods” key instead.\n", "markdownDescription": "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt's recommended to configure static pods with the \"pods\" key instead.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003edisableManifestsDirectory\u003c/code\u003e field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt\u0026rsquo;s recommended to configure static pods with the \u0026ldquo;pods\u0026rdquo; key instead.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeletConfig represents the kubelet config values." }, "v1alpha1.KubeletNodeIPConfig": { "properties": { "validSubnets": { "items": { "type": "string" }, "type": "array", "title": "validSubnets", "description": "The validSubnets field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\n", "markdownDescription": "The `validSubnets` field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalidSubnets\u003c/code\u003e field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubeletNodeIPConfig represents the kubelet node IP configuration." }, "v1alpha1.KubernetesTalosAPIAccessConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable Talos API access from Kubernetes pods.\n", "markdownDescription": "Enable Talos API access from Kubernetes pods.", "x-intellij-html-description": "\u003cp\u003eEnable Talos API access from Kubernetes pods.\u003c/p\u003e\n" }, "allowedRoles": { "items": { "type": "string" }, "type": "array", "title": "allowedRoles", "description": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.\n", "markdownDescription": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.", "x-intellij-html-description": "\u003cp\u003eThe list of Talos API roles which can be granted for access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eEmpty list means that no roles can be granted, so access is blocked.\u003c/p\u003e\n" }, "allowedKubernetesNamespaces": { "items": { "type": "string" }, "type": "array", "title": "allowedKubernetesNamespaces", "description": "The list of Kubernetes namespaces Talos API access is available from.\n", "markdownDescription": "The list of Kubernetes namespaces Talos API access is available from.", "x-intellij-html-description": "\u003cp\u003eThe list of Kubernetes namespaces Talos API access is available from.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "KubernetesTalosAPIAccessConfig describes the configuration for the Talos API access from Kubernetes pods." }, "v1alpha1.LinuxIDMapping": { "properties": { "containerID": { "type": "integer", "title": "containerID", "description": "ContainerID is the starting UID/GID in the container.\n", "markdownDescription": "ContainerID is the starting UID/GID in the container.", "x-intellij-html-description": "\u003cp\u003eContainerID is the starting UID/GID in the container.\u003c/p\u003e\n" }, "hostID": { "type": "integer", "title": "hostID", "description": "HostID is the starting UID/GID on the host to be mapped to ‘ContainerID’.\n", "markdownDescription": "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'.", "x-intellij-html-description": "\u003cp\u003eHostID is the starting UID/GID on the host to be mapped to \u0026lsquo;ContainerID\u0026rsquo;.\u003c/p\u003e\n" }, "size": { "type": "integer", "title": "size", "description": "Size is the number of IDs to be mapped.\n", "markdownDescription": "Size is the number of IDs to be mapped.", "x-intellij-html-description": "\u003cp\u003eSize is the number of IDs to be mapped.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LinuxIDMapping represents the Linux ID mapping." }, "v1alpha1.LoggingConfig": { "properties": { "destinations": { "items": { "$ref": "#/$defs/v1alpha1.LoggingDestination" }, "type": "array", "title": "destinations", "description": "Logging destination.\n", "markdownDescription": "Logging destination.", "x-intellij-html-description": "\u003cp\u003eLogging destination.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LoggingConfig struct configures Talos logging." }, "v1alpha1.LoggingDestination": { "properties": { "endpoint": { "$ref": "#/$defs/v1alpha1.Endpoint", "title": "endpoint", "description": "Where to send logs. Supported protocols are “tcp” and “udp”.\n", "markdownDescription": "Where to send logs. Supported protocols are \"tcp\" and \"udp\".", "x-intellij-html-description": "\u003cp\u003eWhere to send logs. Supported protocols are \u0026ldquo;tcp\u0026rdquo; and \u0026ldquo;udp\u0026rdquo;.\u003c/p\u003e\n" }, "format": { "enum": [ "json_lines" ], "title": "format", "description": "Logs format.\n", "markdownDescription": "Logs format.", "x-intellij-html-description": "\u003cp\u003eLogs format.\u003c/p\u003e\n" }, "extraTags": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraTags", "description": "Extra tags (key-value) pairs to attach to every log message sent.\n", "markdownDescription": "Extra tags (key-value) pairs to attach to every log message sent.", "x-intellij-html-description": "\u003cp\u003eExtra tags (key-value) pairs to attach to every log message sent.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "LoggingDestination struct configures Talos logging destination." }, "v1alpha1.MachineConfig": { "properties": { "type": { "enum": [ "controlplane", "worker" ], "title": "type", "description": "Defines the role of the machine within the cluster.\n\nControl Plane\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\nWorker\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as “join”; that value is still supported but deprecated.\n", "markdownDescription": "Defines the role of the machine within the cluster.\n\n**Control Plane**\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\n**Worker**\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as \"join\"; that value is still supported but deprecated.", "x-intellij-html-description": "\u003cp\u003eDefines the role of the machine within the cluster.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eControl Plane\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eWorker\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\u003c/p\u003e\n\n\u003cp\u003eThis node type was previously known as \u0026ldquo;join\u0026rdquo;; that value is still supported but deprecated.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The token is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its’ identity.\n", "markdownDescription": "The `token` is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003etoken\u003c/code\u003e is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its\u0026rsquo; identity.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "acceptedCAs": { "properties": { "crt": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "acceptedCAs", "description": "The certificates issued by certificate authorities are accepted in addition to issuing ‘ca’.\nIt is composed of a base64 encoded crt`.\n", "markdownDescription": "The certificates issued by certificate authorities are accepted in addition to issuing 'ca'.\nIt is composed of a base64 encoded `crt``.", "x-intellij-html-description": "\u003cp\u003eThe certificates issued by certificate authorities are accepted in addition to issuing \u0026lsquo;ca\u0026rsquo;.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e`.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the machine’s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate’s SANs.\n", "markdownDescription": "Extra certificate subject alternative names for the machine's certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate's SANs.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the machine\u0026rsquo;s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate\u0026rsquo;s SANs.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/v1alpha1.MachineControlPlaneConfig", "title": "controlPlane", "description": "Provides machine specific control plane configuration options.\n", "markdownDescription": "Provides machine specific control plane configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific control plane configuration options.\u003c/p\u003e\n" }, "kubelet": { "$ref": "#/$defs/v1alpha1.KubeletConfig", "title": "kubelet", "description": "Used to provide additional options to the kubelet.\n", "markdownDescription": "Used to provide additional options to the kubelet.", "x-intellij-html-description": "\u003cp\u003eUsed to provide additional options to the kubelet.\u003c/p\u003e\n" }, "pods": { "items": { "type": "object" }, "type": "array", "title": "pods", "description": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn’t validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.\n", "markdownDescription": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn't validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.", "x-intellij-html-description": "\u003cp\u003eUsed to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\u003c/p\u003e\n\n\u003cp\u003eStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn\u0026rsquo;t validate the pod definition.\nUpdates to this field can be applied without a reboot.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\" target=\"_blank\"\u003ehttps://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\u003c/a\u003e.\u003c/p\u003e\n" }, "install": { "$ref": "#/$defs/v1alpha1.InstallConfig", "title": "install", "description": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\n", "markdownDescription": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.", "x-intellij-html-description": "\u003cp\u003eUsed to provide instructions for installations.\u003c/p\u003e\n\n\u003cp\u003eNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\u003c/p\u003e\n" }, "files": { "items": { "$ref": "#/$defs/v1alpha1.MachineFile" }, "type": "array", "title": "files", "description": "Allows the addition of user specified files.\nThe value of op can be create, overwrite, or append.\nIn the case of create, path must not exist.\nIn the case of overwrite, and append, path must be a valid file.\nIf an op value of append is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\n", "markdownDescription": "Allows the addition of user specified files.\nThe value of `op` can be `create`, `overwrite`, or `append`.\nIn the case of `create`, `path` must not exist.\nIn the case of `overwrite`, and `append`, `path` must be a valid file.\nIf an `op` value of `append` is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.", "x-intellij-html-description": "\u003cp\u003eAllows the addition of user specified files.\nThe value of \u003ccode\u003eop\u003c/code\u003e can be \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003eoverwrite\u003c/code\u003e, or \u003ccode\u003eappend\u003c/code\u003e.\nIn the case of \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must not exist.\nIn the case of \u003ccode\u003eoverwrite\u003c/code\u003e, and \u003ccode\u003eappend\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must be a valid file.\nIf an \u003ccode\u003eop\u003c/code\u003e value of \u003ccode\u003eappend\u003c/code\u003e is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\u003c/p\u003e\n" }, "sysctls": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysctls", "description": "Used to configure the machine’s sysctls.\n", "markdownDescription": "Used to configure the machine's sysctls.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysctls.\u003c/p\u003e\n" }, "sysfs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysfs", "description": "Used to configure the machine’s sysfs.\n", "markdownDescription": "Used to configure the machine's sysfs.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysfs.\u003c/p\u003e\n" }, "features": { "$ref": "#/$defs/v1alpha1.FeaturesConfig", "title": "features", "description": "Features describe individual Talos features that can be switched on or off.\n", "markdownDescription": "Features describe individual Talos features that can be switched on or off.", "x-intellij-html-description": "\u003cp\u003eFeatures describe individual Talos features that can be switched on or off.\u003c/p\u003e\n" }, "udev": { "$ref": "#/$defs/v1alpha1.UdevConfig", "title": "udev", "description": "Configures the udev system.\n", "markdownDescription": "Configures the udev system.", "x-intellij-html-description": "\u003cp\u003eConfigures the udev system.\u003c/p\u003e\n" }, "logging": { "$ref": "#/$defs/v1alpha1.LoggingConfig", "title": "logging", "description": "Configures the logging system.\n", "markdownDescription": "Configures the logging system.", "x-intellij-html-description": "\u003cp\u003eConfigures the logging system.\u003c/p\u003e\n" }, "kernel": { "$ref": "#/$defs/v1alpha1.KernelConfig", "title": "kernel", "description": "Configures the kernel.\n", "markdownDescription": "Configures the kernel.", "x-intellij-html-description": "\u003cp\u003eConfigures the kernel.\u003c/p\u003e\n" }, "seccompProfiles": { "items": { "$ref": "#/$defs/v1alpha1.MachineSeccompProfile" }, "type": "array", "title": "seccompProfiles", "description": "Configures the seccomp profiles for the machine.\n", "markdownDescription": "Configures the seccomp profiles for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the seccomp profiles for the machine.\u003c/p\u003e\n" }, "baseRuntimeSpecOverrides": { "type": "object", "title": "baseRuntimeSpecOverrides", "description": "Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.\n", "markdownDescription": "Override (patch) settings in the default OCI runtime spec for CRI containers.\n\nIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.", "x-intellij-html-description": "\u003cp\u003eOverride (patch) settings in the default OCI runtime spec for CRI containers.\u003c/p\u003e\n\n\u003cp\u003eIt can be used to set some default container settings which are not configurable in Kubernetes,\nfor example default ulimits.\nNote: this change applies to all newly created containers, and it requires a reboot to take effect.\u003c/p\u003e\n" }, "nodeLabels": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeLabels", "description": "Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see NodeRestriction admission plugin).\n", "markdownDescription": "Configures the node labels for the machine.\n\nNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", "x-intellij-html-description": "\u003cp\u003eConfigures the node labels for the machine.\u003c/p\u003e\n\n\u003cp\u003eNote: In the default Kubernetes configuration, worker nodes are restricted to set\nlabels with some prefixes (see \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction\" target=\"_blank\"\u003eNodeRestriction\u003c/a\u003e admission plugin).\u003c/p\u003e\n" }, "nodeAnnotations": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeAnnotations", "description": "Configures the node annotations for the machine.\n", "markdownDescription": "Configures the node annotations for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the node annotations for the machine.\u003c/p\u003e\n" }, "nodeTaints": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeTaints", "description": "Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see NodeRestriction admission plugin).\n", "markdownDescription": "Configures the node taints for the machine. Effect is optional.\n\nNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see [NodeRestriction](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) admission plugin).", "x-intellij-html-description": "\u003cp\u003eConfigures the node taints for the machine. Effect is optional.\u003c/p\u003e\n\n\u003cp\u003eNote: In the default Kubernetes configuration, worker nodes are not allowed to\nmodify the taints (see \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction\" target=\"_blank\"\u003eNodeRestriction\u003c/a\u003e admission plugin).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineConfig represents the machine-specific config values." }, "v1alpha1.MachineControlPlaneConfig": { "properties": { "controllerManager": { "$ref": "#/$defs/v1alpha1.MachineControllerManagerConfig", "title": "controllerManager", "description": "Controller manager machine specific configuration options.\n", "markdownDescription": "Controller manager machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager machine specific configuration options.\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/v1alpha1.MachineSchedulerConfig", "title": "scheduler", "description": "Scheduler machine specific configuration options.\n", "markdownDescription": "Scheduler machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler machine specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineControlPlaneConfig machine specific configuration options." }, "v1alpha1.MachineControllerManagerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-controller-manager on the node.\n", "markdownDescription": "Disable kube-controller-manager on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-controller-manager on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineControllerManagerConfig represents the machine specific ControllerManager config values." }, "v1alpha1.MachineFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The contents of the file.\n", "markdownDescription": "The contents of the file.", "x-intellij-html-description": "\u003cp\u003eThe contents of the file.\u003c/p\u003e\n" }, "permissions": { "type": "integer", "title": "permissions", "description": "The file’s permissions in octal.\n", "markdownDescription": "The file's permissions in octal.", "x-intellij-html-description": "\u003cp\u003eThe file\u0026rsquo;s permissions in octal.\u003c/p\u003e\n" }, "path": { "type": "string", "title": "path", "description": "The path of the file.\n", "markdownDescription": "The path of the file.", "x-intellij-html-description": "\u003cp\u003eThe path of the file.\u003c/p\u003e\n" }, "op": { "enum": [ "create", "append", "overwrite" ], "title": "op", "description": "The operation to use\n", "markdownDescription": "The operation to use", "x-intellij-html-description": "\u003cp\u003eThe operation to use\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineFile represents a file to write to disk." }, "v1alpha1.MachineSchedulerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-scheduler on the node.\n", "markdownDescription": "Disable kube-scheduler on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-scheduler on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineSchedulerConfig represents the machine specific Scheduler config values." }, "v1alpha1.MachineSeccompProfile": { "properties": { "name": { "type": "string", "title": "name", "description": "The name field is used to provide the file name of the seccomp profile.\n", "markdownDescription": "The `name` field is used to provide the file name of the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003ename\u003c/code\u003e field is used to provide the file name of the seccomp profile.\u003c/p\u003e\n" }, "value": { "type": "object", "title": "value", "description": "The value field is used to provide the seccomp profile.\n", "markdownDescription": "The `value` field is used to provide the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalue\u003c/code\u003e field is used to provide the seccomp profile.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "MachineSeccompProfile defines seccomp profiles for the machine." }, "v1alpha1.ProxyConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-proxy deployment on cluster bootstrap.\n", "markdownDescription": "Disable kube-proxy deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable kube-proxy deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The container image used in the kube-proxy manifest.\n", "markdownDescription": "The container image used in the kube-proxy manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the kube-proxy manifest.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "proxy mode of kube-proxy.\nThe default is ‘iptables’.\n", "markdownDescription": "proxy mode of kube-proxy.\nThe default is 'iptables'.", "x-intellij-html-description": "\u003cp\u003eproxy mode of kube-proxy.\nThe default is \u0026lsquo;iptables\u0026rsquo;.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to kube-proxy.\n", "markdownDescription": "Extra arguments to supply to kube-proxy.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to kube-proxy.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ProxyConfig represents the kube proxy configuration options." }, "v1alpha1.RegistryKubernetesConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable Kubernetes discovery registry.\n", "markdownDescription": "Disable Kubernetes discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable Kubernetes discovery registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RegistryKubernetesConfig struct configures Kubernetes discovery registry." }, "v1alpha1.RegistryServiceConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable external service discovery registry.\n", "markdownDescription": "Disable external service discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable external service discovery registry.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "External service endpoint.\n", "markdownDescription": "External service endpoint.", "x-intellij-html-description": "\u003cp\u003eExternal service endpoint.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "RegistryServiceConfig struct configures Kubernetes discovery registry." }, "v1alpha1.ResourcesConfig": { "properties": { "requests": { "type": "object", "title": "requests", "description": "Requests configures the reserved cpu/memory resources.\n", "markdownDescription": "Requests configures the reserved cpu/memory resources.", "x-intellij-html-description": "\u003cp\u003eRequests configures the reserved cpu/memory resources.\u003c/p\u003e\n" }, "limits": { "type": "object", "title": "limits", "description": "Limits configures the maximum cpu/memory resources a container can use.\n", "markdownDescription": "Limits configures the maximum cpu/memory resources a container can use.", "x-intellij-html-description": "\u003cp\u003eLimits configures the maximum cpu/memory resources a container can use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "ResourcesConfig represents the pod resources." }, "v1alpha1.SchedulerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the scheduler manifest.\n", "markdownDescription": "The container image used in the scheduler manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the scheduler manifest.\u003c/p\u003e\n" }, "extraArgs": { "additionalProperties": { "oneOf": [ { "type": "string" }, { "items": { "type": "string" }, "type": "array" } ] }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the scheduler.\n", "markdownDescription": "Extra arguments to supply to the scheduler.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the scheduler.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/v1alpha1.VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the scheduler static pod.\n", "markdownDescription": "Extra volumes to mount to the scheduler static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the scheduler static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the scheduler resources.\n", "markdownDescription": "Configure the scheduler resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the scheduler resources.\u003c/p\u003e\n" }, "config": { "type": "object", "title": "config", "description": "Specify custom kube-scheduler configuration.\n", "markdownDescription": "Specify custom kube-scheduler configuration.", "x-intellij-html-description": "\u003cp\u003eSpecify custom kube-scheduler configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "SchedulerConfig represents the kube scheduler configuration options." }, "v1alpha1.UdevConfig": { "properties": { "rules": { "items": { "type": "string" }, "type": "array", "title": "rules", "description": "List of udev rules to apply to the udev system\n", "markdownDescription": "List of udev rules to apply to the udev system", "x-intellij-html-description": "\u003cp\u003eList of udev rules to apply to the udev system\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "UdevConfig describes how the udev system should be configured." }, "v1alpha1.VolumeMountConfig": { "properties": { "hostPath": { "type": "string", "title": "hostPath", "description": "Path on the host.\n", "markdownDescription": "Path on the host.", "x-intellij-html-description": "\u003cp\u003ePath on the host.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "Path in the container.\n", "markdownDescription": "Path in the container.", "x-intellij-html-description": "\u003cp\u003ePath in the container.\u003c/p\u003e\n" }, "readonly": { "type": "boolean", "title": "readonly", "description": "Mount the volume read only.\n", "markdownDescription": "Mount the volume read only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read only.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object", "description": "VolumeMountConfig struct describes extra volume mount for the static pods." } }, "oneOf": [ { "$ref": "#/$defs/block.ExistingVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.ExternalVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.RawVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.SwapVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.UserVolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.VolumeConfigV1Alpha1" }, { "$ref": "#/$defs/block.ZswapConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryAuthConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryMirrorConfigV1Alpha1" }, { "$ref": "#/$defs/cri.RegistryTLSConfigV1Alpha1" }, { "$ref": "#/$defs/extensions.ServiceConfigV1Alpha1" }, { "$ref": "#/$defs/hardware.PCIDriverRebindConfigV1Alpha1" }, { "$ref": "#/$defs/network.BlackholeRouteConfigV1Alpha1" }, { "$ref": "#/$defs/network.BondConfigV1Alpha1" }, { "$ref": "#/$defs/network.BridgeConfigV1Alpha1" }, { "$ref": "#/$defs/network.DefaultActionConfigV1Alpha1" }, { "$ref": "#/$defs/network.DHCPv4ConfigV1Alpha1" }, { "$ref": "#/$defs/network.DHCPv6ConfigV1Alpha1" }, { "$ref": "#/$defs/network.DummyLinkConfigV1Alpha1" }, { "$ref": "#/$defs/network.EthernetConfigV1Alpha1" }, { "$ref": "#/$defs/network.HCloudVIPConfigV1Alpha1" }, { "$ref": "#/$defs/network.HostnameConfigV1Alpha1" }, { "$ref": "#/$defs/network.KubeSpanConfigV1Alpha1" }, { "$ref": "#/$defs/network.KubespanEndpointsConfigV1Alpha1" }, { "$ref": "#/$defs/network.Layer2VIPConfigV1Alpha1" }, { "$ref": "#/$defs/network.LinkConfigV1Alpha1" }, { "$ref": "#/$defs/network.LinkAliasConfigV1Alpha1" }, { "$ref": "#/$defs/network.ResolverConfigV1Alpha1" }, { "$ref": "#/$defs/network.RoutingRuleConfigV1Alpha1" }, { "$ref": "#/$defs/network.RuleConfigV1Alpha1" }, { "$ref": "#/$defs/network.StaticHostConfigV1Alpha1" }, { "$ref": "#/$defs/network.TCPProbeConfigV1Alpha1" }, { "$ref": "#/$defs/network.TimeSyncConfigV1Alpha1" }, { "$ref": "#/$defs/network.VLANConfigV1Alpha1" }, { "$ref": "#/$defs/network.VRFConfigV1Alpha1" }, { "$ref": "#/$defs/network.WireguardConfigV1Alpha1" }, { "$ref": "#/$defs/runtime.EnvironmentV1Alpha1" }, { "$ref": "#/$defs/runtime.EventSinkV1Alpha1" }, { "$ref": "#/$defs/runtime.KmsgLogV1Alpha1" }, { "$ref": "#/$defs/runtime.OOMV1Alpha1" }, { "$ref": "#/$defs/runtime.WatchdogTimerV1Alpha1" }, { "$ref": "#/$defs/security.TrustedRootsConfigV1Alpha1" }, { "$ref": "#/$defs/siderolink.ConfigV1Alpha1" }, { "$ref": "#/$defs/v1alpha1.Config" } ] } ================================================ FILE: website/content/v1.13/schemas/v1alpha1_config.schema.json ================================================ { "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://talos.dev/v1.6/schemas/v1alpha1_config.schema.json", "$ref": "#/$defs/Config", "$defs": { "APIServerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the API server manifest.\n", "markdownDescription": "The container image used in the API server manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the API server manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the API server.\n", "markdownDescription": "Extra arguments to supply to the API server.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the API server.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the API server static pod.\n", "markdownDescription": "Extra volumes to mount to the API server static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the API server static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the API server’s certificate.\n", "markdownDescription": "Extra certificate subject alternative names for the API server's certificate.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the API server\u0026rsquo;s certificate.\u003c/p\u003e\n" }, "disablePodSecurityPolicy": { "type": "boolean", "title": "disablePodSecurityPolicy", "description": "Disable PodSecurityPolicy in the API server and default manifests.\n", "markdownDescription": "Disable PodSecurityPolicy in the API server and default manifests.", "x-intellij-html-description": "\u003cp\u003eDisable PodSecurityPolicy in the API server and default manifests.\u003c/p\u003e\n" }, "admissionControl": { "items": { "$ref": "#/$defs/AdmissionPluginConfig" }, "type": "array", "title": "admissionControl", "description": "Configure the API server admission plugins.\n", "markdownDescription": "Configure the API server admission plugins.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server admission plugins.\u003c/p\u003e\n" }, "auditPolicy": { "type": "object", "title": "auditPolicy", "description": "Configure the API server audit policy.\n", "markdownDescription": "Configure the API server audit policy.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server audit policy.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the API server resources.\n", "markdownDescription": "Configure the API server resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the API server resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "AdminKubeconfigConfig": { "properties": { "certLifetime": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "certLifetime", "description": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Admin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eAdmin kubeconfig certificate lifetime (default is 1 year).\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "AdmissionPluginConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.\n", "markdownDescription": "Name is the name of the admission controller.\nIt must match the registered admission plugin name.", "x-intellij-html-description": "\u003cp\u003eName is the name of the admission controller.\nIt must match the registered admission plugin name.\u003c/p\u003e\n" }, "configuration": { "type": "object", "title": "configuration", "description": "Configuration is an embedded configuration object to be used as the plugin’s\nconfiguration.\n", "markdownDescription": "Configuration is an embedded configuration object to be used as the plugin's\nconfiguration.", "x-intellij-html-description": "\u003cp\u003eConfiguration is an embedded configuration object to be used as the plugin\u0026rsquo;s\nconfiguration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Bond": { "properties": { "interfaces": { "items": { "type": "string" }, "type": "array", "title": "interfaces", "description": "The interfaces that make up the bond.\n", "markdownDescription": "The interfaces that make up the bond.", "x-intellij-html-description": "\u003cp\u003eThe interfaces that make up the bond.\u003c/p\u003e\n" }, "deviceSelectors": { "items": { "$ref": "#/$defs/NetworkDeviceSelector" }, "type": "array", "title": "deviceSelectors", "description": "Picks a network device using the selector.\nMutually exclusive with interfaces.\nSupports partial match using wildcard syntax.\n", "markdownDescription": "Picks a network device using the selector.\nMutually exclusive with `interfaces`.\nSupports partial match using wildcard syntax.", "x-intellij-html-description": "\u003cp\u003ePicks a network device using the selector.\nMutually exclusive with \u003ccode\u003einterfaces\u003c/code\u003e.\nSupports partial match using wildcard syntax.\u003c/p\u003e\n" }, "arpIPTarget": { "items": { "type": "string" }, "type": "array", "title": "arpIPTarget", "description": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "xmitHashPolicy": { "type": "string", "title": "xmitHashPolicy", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "lacpRate": { "type": "string", "title": "lacpRate", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adActorSystem": { "type": "string", "title": "adActorSystem", "description": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\nNot supported at the moment.\u003c/p\u003e\n" }, "arpValidate": { "type": "string", "title": "arpValidate", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "arpAllTargets": { "type": "string", "title": "arpAllTargets", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "primary": { "type": "string", "title": "primary", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "primaryReselect": { "type": "string", "title": "primaryReselect", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "failOverMac": { "type": "string", "title": "failOverMac", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adSelect": { "type": "string", "title": "adSelect", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "miimon": { "type": "integer", "title": "miimon", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "updelay": { "type": "integer", "title": "updelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "downdelay": { "type": "integer", "title": "downdelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "arpInterval": { "type": "integer", "title": "arpInterval", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "resendIgmp": { "type": "integer", "title": "resendIgmp", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "minLinks": { "type": "integer", "title": "minLinks", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "lpInterval": { "type": "integer", "title": "lpInterval", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "packetsPerSlave": { "type": "integer", "title": "packetsPerSlave", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "numPeerNotif": { "type": "integer", "title": "numPeerNotif", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "tlbDynamicLb": { "type": "integer", "title": "tlbDynamicLb", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "allSlavesActive": { "type": "integer", "title": "allSlavesActive", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "useCarrier": { "type": "boolean", "title": "useCarrier", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adActorSysPrio": { "type": "integer", "title": "adActorSysPrio", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "adUserPortKey": { "type": "integer", "title": "adUserPortKey", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" }, "peerNotifyDelay": { "type": "integer", "title": "peerNotifyDelay", "description": "A bond option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bond option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bond option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Bridge": { "properties": { "interfaces": { "items": { "type": "string" }, "type": "array", "title": "interfaces", "description": "The interfaces that make up the bridge.\n", "markdownDescription": "The interfaces that make up the bridge.", "x-intellij-html-description": "\u003cp\u003eThe interfaces that make up the bridge.\u003c/p\u003e\n" }, "stp": { "$ref": "#/$defs/STP", "title": "stp", "description": "A bridge option.\nPlease see the official kernel documentation.\n", "markdownDescription": "A bridge option.\nPlease see the official kernel documentation.", "x-intellij-html-description": "\u003cp\u003eA bridge option.\nPlease see the official kernel documentation.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "CNIConfig": { "properties": { "name": { "enum": [ "flannel", "custom", "none" ], "title": "name", "description": "Name of CNI to use.\n", "markdownDescription": "Name of CNI to use.", "x-intellij-html-description": "\u003cp\u003eName of CNI to use.\u003c/p\u003e\n" }, "urls": { "items": { "type": "string" }, "type": "array", "title": "urls", "description": "URLs containing manifests to apply for the CNI.\nShould be present for “custom”, must be empty for “flannel” and “none”.\n", "markdownDescription": "URLs containing manifests to apply for the CNI.\nShould be present for \"custom\", must be empty for \"flannel\" and \"none\".", "x-intellij-html-description": "\u003cp\u003eURLs containing manifests to apply for the CNI.\nShould be present for \u0026ldquo;custom\u0026rdquo;, must be empty for \u0026ldquo;flannel\u0026rdquo; and \u0026ldquo;none\u0026rdquo;.\u003c/p\u003e\n" }, "flannel": { "$ref": "#/$defs/FlannelCNIConfig", "title": "flannel", "description": "description: |\nFlannel configuration options.\n", "markdownDescription": "description: |\nFlannel configuration options.", "x-intellij-html-description": "\u003cp\u003edescription: |\nFlannel configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterConfig": { "properties": { "id": { "type": "string", "title": "id", "description": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).\n", "markdownDescription": "Globally unique identifier for this cluster (base64 encoded random 32 bytes).", "x-intellij-html-description": "\u003cp\u003eGlobally unique identifier for this cluster (base64 encoded random 32 bytes).\u003c/p\u003e\n" }, "secret": { "type": "string", "title": "secret", "description": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\n", "markdownDescription": "Shared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.", "x-intellij-html-description": "\u003cp\u003eShared secret of cluster (base64 encoded random 32 bytes).\nThis secret is shared among cluster members but should never be sent over the network.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/ControlPlaneConfig", "title": "controlPlane", "description": "Provides control plane specific configuration options.\n", "markdownDescription": "Provides control plane specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides control plane specific configuration options.\u003c/p\u003e\n" }, "clusterName": { "type": "string", "title": "clusterName", "description": "Configures the cluster’s name.\n", "markdownDescription": "Configures the cluster's name.", "x-intellij-html-description": "\u003cp\u003eConfigures the cluster\u0026rsquo;s name.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/ClusterNetworkConfig", "title": "network", "description": "Provides cluster specific network configuration options.\n", "markdownDescription": "Provides cluster specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific network configuration options.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The bootstrap token used to join the cluster.\n", "markdownDescription": "The [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) used to join the cluster.", "x-intellij-html-description": "\u003cp\u003eThe \u003ca href=\"https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/\" target=\"_blank\"\u003ebootstrap token\u003c/a\u003e used to join the cluster.\u003c/p\u003e\n" }, "aescbcEncryptionSecret": { "type": "string", "title": "aescbcEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with AESCBC.\u003c/p\u003e\n" }, "secretboxEncryptionSecret": { "type": "string", "title": "secretboxEncryptionSecret", "description": "A key used for the encryption of secret data at rest.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\n", "markdownDescription": "A key used for the [encryption of secret data at rest](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/).\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.", "x-intellij-html-description": "\u003cp\u003eA key used for the \u003ca href=\"https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\" target=\"_blank\"\u003eencryption of secret data at rest\u003c/a\u003e.\nEnables encryption with secretbox.\nSecretbox has precedence over AESCBC.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The base64 encoded root certificate authority used by Kubernetes.\n", "markdownDescription": "The base64 encoded root certificate authority used by Kubernetes.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded root certificate authority used by Kubernetes.\u003c/p\u003e\n" }, "aggregatorCA": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "aggregatorCA", "description": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.\n", "markdownDescription": "The base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\n\nThis CA can be self-signed.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded aggregator certificate authority used by Kubernetes for front-proxy certificate generation.\u003c/p\u003e\n\n\u003cp\u003eThis CA can be self-signed.\u003c/p\u003e\n" }, "serviceAccount": { "properties": { "key": { "additionalProperties": false, "type": "string" } }, "additionalProperties": false, "type": "object", "title": "serviceAccount", "description": "The base64 encoded private key for service account token generation.\n", "markdownDescription": "The base64 encoded private key for service account token generation.", "x-intellij-html-description": "\u003cp\u003eThe base64 encoded private key for service account token generation.\u003c/p\u003e\n" }, "apiServer": { "$ref": "#/$defs/APIServerConfig", "title": "apiServer", "description": "API server specific configuration options.\n", "markdownDescription": "API server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eAPI server specific configuration options.\u003c/p\u003e\n" }, "controllerManager": { "$ref": "#/$defs/ControllerManagerConfig", "title": "controllerManager", "description": "Controller manager server specific configuration options.\n", "markdownDescription": "Controller manager server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager server specific configuration options.\u003c/p\u003e\n" }, "proxy": { "$ref": "#/$defs/ProxyConfig", "title": "proxy", "description": "Kube-proxy server-specific configuration options\n", "markdownDescription": "Kube-proxy server-specific configuration options", "x-intellij-html-description": "\u003cp\u003eKube-proxy server-specific configuration options\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/SchedulerConfig", "title": "scheduler", "description": "Scheduler server specific configuration options.\n", "markdownDescription": "Scheduler server specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler server specific configuration options.\u003c/p\u003e\n" }, "discovery": { "$ref": "#/$defs/ClusterDiscoveryConfig", "title": "discovery", "description": "Configures cluster member discovery.\n", "markdownDescription": "Configures cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigures cluster member discovery.\u003c/p\u003e\n" }, "etcd": { "$ref": "#/$defs/EtcdConfig", "title": "etcd", "description": "Etcd specific configuration options.\n", "markdownDescription": "Etcd specific configuration options.", "x-intellij-html-description": "\u003cp\u003eEtcd specific configuration options.\u003c/p\u003e\n" }, "coreDNS": { "$ref": "#/$defs/CoreDNS", "title": "coreDNS", "description": "Core DNS specific configuration options.\n", "markdownDescription": "Core DNS specific configuration options.", "x-intellij-html-description": "\u003cp\u003eCore DNS specific configuration options.\u003c/p\u003e\n" }, "externalCloudProvider": { "$ref": "#/$defs/ExternalCloudProviderConfig", "title": "externalCloudProvider", "description": "External cloud provider configuration.\n", "markdownDescription": "External cloud provider configuration.", "x-intellij-html-description": "\u003cp\u003eExternal cloud provider configuration.\u003c/p\u003e\n" }, "extraManifests": { "items": { "type": "string" }, "type": "array", "title": "extraManifests", "description": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "extraManifestHeaders": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraManifestHeaders", "description": "A map of key value pairs that will be added while fetching the extraManifests.\n", "markdownDescription": "A map of key value pairs that will be added while fetching the extraManifests.", "x-intellij-html-description": "\u003cp\u003eA map of key value pairs that will be added while fetching the extraManifests.\u003c/p\u003e\n" }, "inlineManifests": { "items": { "$ref": "#/$defs/ClusterInlineManifest" }, "type": "array", "title": "inlineManifests", "description": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of inline Kubernetes manifests.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" }, "adminKubeconfig": { "$ref": "#/$defs/AdminKubeconfigConfig", "title": "adminKubeconfig", "description": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.\n", "markdownDescription": "Settings for admin kubeconfig generation.\nCertificate lifetime can be configured.", "x-intellij-html-description": "\u003cp\u003eSettings for admin kubeconfig generation.\nCertificate lifetime can be configured.\u003c/p\u003e\n" }, "allowSchedulingOnControlPlanes": { "type": "boolean", "title": "allowSchedulingOnControlPlanes", "description": "Allows running workload on control-plane nodes.\n", "markdownDescription": "Allows running workload on control-plane nodes.", "x-intellij-html-description": "\u003cp\u003eAllows running workload on control-plane nodes.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterDiscoveryConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\n", "markdownDescription": "Enable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.", "x-intellij-html-description": "\u003cp\u003eEnable the cluster membership discovery feature.\nCluster discovery is based on individual registries which are configured under the registries field.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/DiscoveryRegistriesConfig", "title": "registries", "description": "Configure registries used for cluster member discovery.\n", "markdownDescription": "Configure registries used for cluster member discovery.", "x-intellij-html-description": "\u003cp\u003eConfigure registries used for cluster member discovery.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterInlineManifest": { "properties": { "name": { "type": "string", "title": "name", "description": "Name of the manifest.\nName should be unique.\n", "markdownDescription": "Name of the manifest.\nName should be unique.", "x-intellij-html-description": "\u003cp\u003eName of the manifest.\nName should be unique.\u003c/p\u003e\n" }, "contents": { "type": "string", "title": "contents", "description": "Manifest contents as a string.\n", "markdownDescription": "Manifest contents as a string.", "x-intellij-html-description": "\u003cp\u003eManifest contents as a string.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ClusterNetworkConfig": { "properties": { "cni": { "$ref": "#/$defs/CNIConfig", "title": "cni", "description": "The CNI used.\nComposed of “name” and “urls”.\nThe “name” key supports the following options: “flannel”, “custom”, and “none”.\n“flannel” uses Talos-managed Flannel CNI, and that’s the default option.\n“custom” uses custom manifests that should be provided in “urls”.\n“none” indicates that Talos will not manage any CNI installation.\n", "markdownDescription": "The CNI used.\nComposed of \"name\" and \"urls\".\nThe \"name\" key supports the following options: \"flannel\", \"custom\", and \"none\".\n\"flannel\" uses Talos-managed Flannel CNI, and that's the default option.\n\"custom\" uses custom manifests that should be provided in \"urls\".\n\"none\" indicates that Talos will not manage any CNI installation.", "x-intellij-html-description": "\u003cp\u003eThe CNI used.\nComposed of \u0026ldquo;name\u0026rdquo; and \u0026ldquo;urls\u0026rdquo;.\nThe \u0026ldquo;name\u0026rdquo; key supports the following options: \u0026ldquo;flannel\u0026rdquo;, \u0026ldquo;custom\u0026rdquo;, and \u0026ldquo;none\u0026rdquo;.\n\u0026ldquo;flannel\u0026rdquo; uses Talos-managed Flannel CNI, and that\u0026rsquo;s the default option.\n\u0026ldquo;custom\u0026rdquo; uses custom manifests that should be provided in \u0026ldquo;urls\u0026rdquo;.\n\u0026ldquo;none\u0026rdquo; indicates that Talos will not manage any CNI installation.\u003c/p\u003e\n" }, "dnsDomain": { "type": "string", "title": "dnsDomain", "description": "The domain used by Kubernetes DNS.\nThe default is cluster.local\n", "markdownDescription": "The domain used by Kubernetes DNS.\nThe default is `cluster.local`", "x-intellij-html-description": "\u003cp\u003eThe domain used by Kubernetes DNS.\nThe default is \u003ccode\u003ecluster.local\u003c/code\u003e\u003c/p\u003e\n" }, "podSubnets": { "items": { "type": "string" }, "type": "array", "title": "podSubnets", "description": "The pod subnet CIDR.\n", "markdownDescription": "The pod subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe pod subnet CIDR.\u003c/p\u003e\n" }, "serviceSubnets": { "items": { "type": "string" }, "type": "array", "title": "serviceSubnets", "description": "The service subnet CIDR.\n", "markdownDescription": "The service subnet CIDR.", "x-intellij-html-description": "\u003cp\u003eThe service subnet CIDR.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Config": { "properties": { "version": { "enum": [ "v1alpha1" ], "title": "version", "description": "Indicates the schema used to decode the contents.\n", "markdownDescription": "Indicates the schema used to decode the contents.", "x-intellij-html-description": "\u003cp\u003eIndicates the schema used to decode the contents.\u003c/p\u003e\n" }, "debug": { "type": "boolean", "title": "debug", "description": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\nNote: To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\n", "markdownDescription": "Enable verbose logging to the console.\nAll system containers logs will flow into serial console.\n\n**Note:** To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.", "x-intellij-html-description": "\u003cp\u003eEnable verbose logging to the console.\nAll system containers logs will flow into serial console.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eNote:\u003c/strong\u003e To avoid breaking Talos bootstrap flow enable this option only if serial console can handle high message throughput.\u003c/p\u003e\n" }, "machine": { "$ref": "#/$defs/MachineConfig", "title": "machine", "description": "Provides machine specific configuration options.\n", "markdownDescription": "Provides machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific configuration options.\u003c/p\u003e\n" }, "cluster": { "$ref": "#/$defs/ClusterConfig", "title": "cluster", "description": "Provides cluster specific configuration options.\n", "markdownDescription": "Provides cluster specific configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides cluster specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ControlPlaneConfig": { "properties": { "endpoint": { "type": "string", "pattern": "^https://", "format": "uri", "title": "endpoint", "description": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\n", "markdownDescription": "Endpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.", "x-intellij-html-description": "\u003cp\u003eEndpoint is the canonical controlplane endpoint, which can be an IP address or a DNS hostname.\nIt is single-valued, and may optionally include a port number.\u003c/p\u003e\n" }, "localAPIServerPort": { "type": "integer", "title": "localAPIServerPort", "description": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is 6443.\n", "markdownDescription": "The port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is `6443`.", "x-intellij-html-description": "\u003cp\u003eThe port that the API server listens on internally.\nThis may be different than the port portion listed in the endpoint field above.\nThe default is \u003ccode\u003e6443\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ControllerManagerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the controller manager manifest.\n", "markdownDescription": "The container image used in the controller manager manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the controller manager manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the controller manager.\n", "markdownDescription": "Extra arguments to supply to the controller manager.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the controller manager.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the controller manager static pod.\n", "markdownDescription": "Extra volumes to mount to the controller manager static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the controller manager static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the controller manager resources.\n", "markdownDescription": "Configure the controller manager resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the controller manager resources.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "CoreDNS": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable coredns deployment on cluster bootstrap.\n", "markdownDescription": "Disable coredns deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable coredns deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The image field is an override to the default coredns image.\n", "markdownDescription": "The `image` field is an override to the default coredns image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an override to the default coredns image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DHCPOptions": { "properties": { "routeMetric": { "type": "integer", "title": "routeMetric", "description": "The priority of all routes received via DHCP.\n", "markdownDescription": "The priority of all routes received via DHCP.", "x-intellij-html-description": "\u003cp\u003eThe priority of all routes received via DHCP.\u003c/p\u003e\n" }, "ipv4": { "type": "boolean", "title": "ipv4", "description": "Enables DHCPv4 protocol for the interface (default is enabled).\n", "markdownDescription": "Enables DHCPv4 protocol for the interface (default is enabled).", "x-intellij-html-description": "\u003cp\u003eEnables DHCPv4 protocol for the interface (default is enabled).\u003c/p\u003e\n" }, "ipv6": { "type": "boolean", "title": "ipv6", "description": "Enables DHCPv6 protocol for the interface (default is disabled).\n", "markdownDescription": "Enables DHCPv6 protocol for the interface (default is disabled).", "x-intellij-html-description": "\u003cp\u003eEnables DHCPv6 protocol for the interface (default is disabled).\u003c/p\u003e\n" }, "duidv6": { "type": "string", "title": "duidv6", "description": "Set client DUID (hex string).\n", "markdownDescription": "Set client DUID (hex string).", "x-intellij-html-description": "\u003cp\u003eSet client DUID (hex string).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Device": { "properties": { "interface": { "type": "string", "title": "interface", "description": "The interface name.\nMutually exclusive with deviceSelector.\n", "markdownDescription": "The interface name.\nMutually exclusive with `deviceSelector`.", "x-intellij-html-description": "\u003cp\u003eThe interface name.\nMutually exclusive with \u003ccode\u003edeviceSelector\u003c/code\u003e.\u003c/p\u003e\n" }, "deviceSelector": { "$ref": "#/$defs/NetworkDeviceSelector", "title": "deviceSelector", "description": "Picks a network device using the selector.\nMutually exclusive with interface.\nSupports partial match using wildcard syntax.\n", "markdownDescription": "Picks a network device using the selector.\nMutually exclusive with `interface`.\nSupports partial match using wildcard syntax.", "x-intellij-html-description": "\u003cp\u003ePicks a network device using the selector.\nMutually exclusive with \u003ccode\u003einterface\u003c/code\u003e.\nSupports partial match using wildcard syntax.\u003c/p\u003e\n" }, "addresses": { "items": { "type": "string" }, "type": "array", "title": "addresses", "description": "Assigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).\n", "markdownDescription": "Assigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).", "x-intellij-html-description": "\u003cp\u003eAssigns static IP addresses to the interface.\nAn address can be specified either in proper CIDR notation or as a standalone address (netmask of all ones is assumed).\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/Route" }, "type": "array", "title": "routes", "description": "A list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.\n", "markdownDescription": "A list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.", "x-intellij-html-description": "\u003cp\u003eA list of routes associated with the interface.\nIf used in combination with DHCP, these routes will be appended to routes returned by DHCP server.\u003c/p\u003e\n" }, "bond": { "$ref": "#/$defs/Bond", "title": "bond", "description": "Bond specific options.\n", "markdownDescription": "Bond specific options.", "x-intellij-html-description": "\u003cp\u003eBond specific options.\u003c/p\u003e\n" }, "bridge": { "$ref": "#/$defs/Bridge", "title": "bridge", "description": "Bridge specific options.\n", "markdownDescription": "Bridge specific options.", "x-intellij-html-description": "\u003cp\u003eBridge specific options.\u003c/p\u003e\n" }, "vlans": { "items": { "$ref": "#/$defs/Vlan" }, "type": "array", "title": "vlans", "description": "VLAN specific options.\n", "markdownDescription": "VLAN specific options.", "x-intellij-html-description": "\u003cp\u003eVLAN specific options.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The interface’s MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.\n", "markdownDescription": "The interface's MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.", "x-intellij-html-description": "\u003cp\u003eThe interface\u0026rsquo;s MTU.\nIf used in combination with DHCP, this will override any MTU settings returned from DHCP server.\u003c/p\u003e\n" }, "dhcp": { "type": "boolean", "title": "dhcp", "description": "Indicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\n\n\nOptionClasslessStaticRoute\nOptionDomainNameServer\nOptionDNSDomainSearchList\nOptionHostName\n\n", "markdownDescription": "Indicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\n\n- `OptionClasslessStaticRoute`\n- `OptionDomainNameServer`\n- `OptionDNSDomainSearchList`\n- `OptionHostName`", "x-intellij-html-description": "\u003cp\u003eIndicates if DHCP should be used to configure the interface.\nThe following DHCP options are supported:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eOptionClasslessStaticRoute\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionDomainNameServer\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionDNSDomainSearchList\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eOptionHostName\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "ignore": { "type": "boolean", "title": "ignore", "description": "Indicates if the interface should be ignored (skips configuration).\n", "markdownDescription": "Indicates if the interface should be ignored (skips configuration).", "x-intellij-html-description": "\u003cp\u003eIndicates if the interface should be ignored (skips configuration).\u003c/p\u003e\n" }, "dummy": { "type": "boolean", "title": "dummy", "description": "Indicates if the interface is a dummy interface.\ndummy is used to specify that this interface should be a virtual-only, dummy interface.\n", "markdownDescription": "Indicates if the interface is a dummy interface.\n`dummy` is used to specify that this interface should be a virtual-only, dummy interface.", "x-intellij-html-description": "\u003cp\u003eIndicates if the interface is a dummy interface.\n\u003ccode\u003edummy\u003c/code\u003e is used to specify that this interface should be a virtual-only, dummy interface.\u003c/p\u003e\n" }, "dhcpOptions": { "$ref": "#/$defs/DHCPOptions", "title": "dhcpOptions", "description": "DHCP specific options.\ndhcp must be set to true for these to take effect.\n", "markdownDescription": "DHCP specific options.\n`dhcp` *must* be set to true for these to take effect.", "x-intellij-html-description": "\u003cp\u003eDHCP specific options.\n\u003ccode\u003edhcp\u003c/code\u003e \u003cem\u003emust\u003c/em\u003e be set to true for these to take effect.\u003c/p\u003e\n" }, "wireguard": { "$ref": "#/$defs/DeviceWireguardConfig", "title": "wireguard", "description": "Wireguard specific configuration.\nIncludes things like private key, listen port, peers.\n", "markdownDescription": "Wireguard specific configuration.\nIncludes things like private key, listen port, peers.", "x-intellij-html-description": "\u003cp\u003eWireguard specific configuration.\nIncludes things like private key, listen port, peers.\u003c/p\u003e\n" }, "vip": { "$ref": "#/$defs/DeviceVIPConfig", "title": "vip", "description": "Virtual (shared) IP address configuration.\n", "markdownDescription": "Virtual (shared) IP address configuration.", "x-intellij-html-description": "\u003cp\u003eVirtual (shared) IP address configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceVIPConfig": { "properties": { "ip": { "type": "string", "title": "ip", "description": "Specifies the IP address to be used.\n", "markdownDescription": "Specifies the IP address to be used.", "x-intellij-html-description": "\u003cp\u003eSpecifies the IP address to be used.\u003c/p\u003e\n" }, "equinixMetal": { "$ref": "#/$defs/VIPEquinixMetalConfig", "title": "equinixMetal", "description": "Specifies the Equinix Metal API settings to assign VIP to the node.\n", "markdownDescription": "Specifies the Equinix Metal API settings to assign VIP to the node.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Equinix Metal API settings to assign VIP to the node.\u003c/p\u003e\n" }, "hcloud": { "$ref": "#/$defs/VIPHCloudConfig", "title": "hcloud", "description": "Specifies the Hetzner Cloud API settings to assign VIP to the node.\n", "markdownDescription": "Specifies the Hetzner Cloud API settings to assign VIP to the node.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API settings to assign VIP to the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceWireguardConfig": { "properties": { "privateKey": { "type": "string", "title": "privateKey", "description": "Specifies a private key configuration (base64 encoded).\nCan be generated by wg genkey.\n", "markdownDescription": "Specifies a private key configuration (base64 encoded).\nCan be generated by `wg genkey`.", "x-intellij-html-description": "\u003cp\u003eSpecifies a private key configuration (base64 encoded).\nCan be generated by \u003ccode\u003ewg genkey\u003c/code\u003e.\u003c/p\u003e\n" }, "listenPort": { "type": "integer", "title": "listenPort", "description": "Specifies a device’s listening port.\n", "markdownDescription": "Specifies a device's listening port.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s listening port.\u003c/p\u003e\n" }, "firewallMark": { "type": "integer", "title": "firewallMark", "description": "Specifies a device’s firewall mark.\n", "markdownDescription": "Specifies a device's firewall mark.", "x-intellij-html-description": "\u003cp\u003eSpecifies a device\u0026rsquo;s firewall mark.\u003c/p\u003e\n" }, "peers": { "items": { "$ref": "#/$defs/DeviceWireguardPeer" }, "type": "array", "title": "peers", "description": "Specifies a list of peer configurations to apply to a device.\n", "markdownDescription": "Specifies a list of peer configurations to apply to a device.", "x-intellij-html-description": "\u003cp\u003eSpecifies a list of peer configurations to apply to a device.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DeviceWireguardPeer": { "properties": { "publicKey": { "type": "string", "title": "publicKey", "description": "Specifies the public key of this peer.\nCan be extracted from private key by running wg pubkey \u0026lt; private.key \u0026gt; public.key \u0026amp;\u0026amp; cat public.key.\n", "markdownDescription": "Specifies the public key of this peer.\nCan be extracted from private key by running `wg pubkey \u003c private.key \u003e public.key \u0026\u0026 cat public.key`.", "x-intellij-html-description": "\u003cp\u003eSpecifies the public key of this peer.\nCan be extracted from private key by running \u003ccode\u003ewg pubkey \u0026lt; private.key \u0026gt; public.key \u0026amp;\u0026amp; cat public.key\u003c/code\u003e.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "Specifies the endpoint of this peer entry.\n", "markdownDescription": "Specifies the endpoint of this peer entry.", "x-intellij-html-description": "\u003cp\u003eSpecifies the endpoint of this peer entry.\u003c/p\u003e\n" }, "persistentKeepaliveInterval": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "persistentKeepaliveInterval", "description": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (‘1h’ for one hour, ‘10m’ for ten minutes).\n", "markdownDescription": "Specifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format ('1h' for one hour, '10m' for ten minutes).", "x-intellij-html-description": "\u003cp\u003eSpecifies the persistent keepalive interval for this peer.\nField format accepts any Go time.Duration format (\u0026lsquo;1h\u0026rsquo; for one hour, \u0026lsquo;10m\u0026rsquo; for ten minutes).\u003c/p\u003e\n" }, "allowedIPs": { "items": { "type": "string" }, "type": "array", "title": "allowedIPs", "description": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\n", "markdownDescription": "AllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.", "x-intellij-html-description": "\u003cp\u003eAllowedIPs specifies a list of allowed IP addresses in CIDR notation for this peer.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DiscoveryRegistriesConfig": { "properties": { "kubernetes": { "$ref": "#/$defs/RegistryKubernetesConfig", "title": "kubernetes", "description": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\n", "markdownDescription": "Kubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.", "x-intellij-html-description": "\u003cp\u003eKubernetes registry uses Kubernetes API server to discover cluster members and stores additional information\nas annotations on the Node resources.\u003c/p\u003e\n" }, "service": { "$ref": "#/$defs/RegistryServiceConfig", "title": "service", "description": "Service registry is using an external service to push and pull information about cluster members.\n", "markdownDescription": "Service registry is using an external service to push and pull information about cluster members.", "x-intellij-html-description": "\u003cp\u003eService registry is using an external service to push and pull information about cluster members.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "DiskPartition": { "properties": { "size": { "type": "integer", "title": "size", "description": "The size of partition: either bytes or human readable representation. If size: is omitted, the partition is sized to occupy the full disk.\n", "markdownDescription": "The size of partition: either bytes or human readable representation. If `size:` is omitted, the partition is sized to occupy the full disk.", "x-intellij-html-description": "\u003cp\u003eThe size of partition: either bytes or human readable representation. If \u003ccode\u003esize:\u003c/code\u003e is omitted, the partition is sized to occupy the full disk.\u003c/p\u003e\n" }, "mountpoint": { "type": "string", "title": "mountpoint", "description": "Where to mount the partition.\n", "markdownDescription": "Where to mount the partition.", "x-intellij-html-description": "\u003cp\u003eWhere to mount the partition.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionConfig": { "properties": { "provider": { "type": "string", "title": "provider", "description": "Encryption provider to use for the encryption.\n", "markdownDescription": "Encryption provider to use for the encryption.", "x-intellij-html-description": "\u003cp\u003eEncryption provider to use for the encryption.\u003c/p\u003e\n" }, "keys": { "items": { "$ref": "#/$defs/EncryptionKey" }, "type": "array", "title": "keys", "description": "Defines the encryption keys generation and storage method.\n", "markdownDescription": "Defines the encryption keys generation and storage method.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption keys generation and storage method.\u003c/p\u003e\n" }, "cipher": { "enum": [ "aes-xts-plain64", "xchacha12,aes-adiantum-plain64", "xchacha20,aes-adiantum-plain64" ], "title": "cipher", "description": "Cipher kind to use for the encryption. Depends on the encryption provider.\n", "markdownDescription": "Cipher kind to use for the encryption. Depends on the encryption provider.", "x-intellij-html-description": "\u003cp\u003eCipher kind to use for the encryption. Depends on the encryption provider.\u003c/p\u003e\n" }, "keySize": { "type": "integer", "title": "keySize", "description": "Defines the encryption key length.\n", "markdownDescription": "Defines the encryption key length.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption key length.\u003c/p\u003e\n" }, "blockSize": { "type": "integer", "title": "blockSize", "description": "Defines the encryption sector size.\n", "markdownDescription": "Defines the encryption sector size.", "x-intellij-html-description": "\u003cp\u003eDefines the encryption sector size.\u003c/p\u003e\n" }, "options": { "enum": [ "no_read_workqueue", "no_write_workqueue", "same_cpu_crypt" ], "title": "options", "description": "Additional –perf parameters for the LUKS2 encryption.\n", "markdownDescription": "Additional --perf parameters for the LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eAdditional \u0026ndash;perf parameters for the LUKS2 encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKey": { "properties": { "static": { "$ref": "#/$defs/EncryptionKeyStatic", "title": "static", "description": "Key which value is stored in the configuration file.\n", "markdownDescription": "Key which value is stored in the configuration file.", "x-intellij-html-description": "\u003cp\u003eKey which value is stored in the configuration file.\u003c/p\u003e\n" }, "nodeID": { "$ref": "#/$defs/EncryptionKeyNodeID", "title": "nodeID", "description": "Deterministically generated key from the node UUID and PartitionLabel.\n", "markdownDescription": "Deterministically generated key from the node UUID and PartitionLabel.", "x-intellij-html-description": "\u003cp\u003eDeterministically generated key from the node UUID and PartitionLabel.\u003c/p\u003e\n" }, "kms": { "$ref": "#/$defs/EncryptionKeyKMS", "title": "kms", "description": "KMS managed encryption key.\n", "markdownDescription": "KMS managed encryption key.", "x-intellij-html-description": "\u003cp\u003eKMS managed encryption key.\u003c/p\u003e\n" }, "slot": { "type": "integer", "title": "slot", "description": "Key slot number for LUKS2 encryption.\n", "markdownDescription": "Key slot number for LUKS2 encryption.", "x-intellij-html-description": "\u003cp\u003eKey slot number for LUKS2 encryption.\u003c/p\u003e\n" }, "tpm": { "$ref": "#/$defs/EncryptionKeyTPM", "title": "tpm", "description": "Enable TPM based disk encryption.\n", "markdownDescription": "Enable TPM based disk encryption.", "x-intellij-html-description": "\u003cp\u003eEnable TPM based disk encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyKMS": { "properties": { "endpoint": { "type": "string", "title": "endpoint", "description": "KMS endpoint to Seal/Unseal the key.\n", "markdownDescription": "KMS endpoint to Seal/Unseal the key.", "x-intellij-html-description": "\u003cp\u003eKMS endpoint to Seal/Unseal the key.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyNodeID": { "properties": {}, "additionalProperties": false, "type": "object" }, "EncryptionKeyStatic": { "properties": { "passphrase": { "type": "string", "title": "passphrase", "description": "Defines the static passphrase value.\n", "markdownDescription": "Defines the static passphrase value.", "x-intellij-html-description": "\u003cp\u003eDefines the static passphrase value.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "EncryptionKeyTPM": { "properties": {}, "additionalProperties": false, "type": "object" }, "Endpoint": { "properties": {}, "additionalProperties": false, "type": "object" }, "EtcdConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used to create the etcd service.\n", "markdownDescription": "The container image used to create the etcd service.", "x-intellij-html-description": "\u003cp\u003eThe container image used to create the etcd service.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The ca is the root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The `ca` is the root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eca\u003c/code\u003e is the root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n\nname\ndata-dir\ninitial-cluster-state\nlisten-peer-urls\nlisten-client-urls\ncert-file\nkey-file\ntrusted-ca-file\npeer-client-cert-auth\npeer-cert-file\npeer-trusted-ca-file\npeer-key-file\n\n", "markdownDescription": "Extra arguments to supply to etcd.\nNote that the following args are not allowed:\n\n- `name`\n- `data-dir`\n- `initial-cluster-state`\n- `listen-peer-urls`\n- `listen-client-urls`\n- `cert-file`\n- `key-file`\n- `trusted-ca-file`\n- `peer-client-cert-auth`\n- `peer-cert-file`\n- `peer-trusted-ca-file`\n- `peer-key-file`", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to etcd.\nNote that the following args are not allowed:\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ename\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edata-dir\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003einitial-cluster-state\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-peer-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003elisten-client-urls\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ecert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ekey-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etrusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-client-cert-auth\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-cert-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-trusted-ca-file\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003epeer-key-file\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n" }, "advertisedSubnets": { "items": { "type": "string" }, "type": "array", "title": "advertisedSubnets", "description": "The advertisedSubnets field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `advertisedSubnets` field configures the networks to pick etcd advertised IP from.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eadvertisedSubnets\u003c/code\u003e field configures the networks to pick etcd advertised IP from.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" }, "listenSubnets": { "items": { "type": "string" }, "type": "array", "title": "listenSubnets", "description": "The listenSubnets field configures the networks for the etcd to listen for peer and client connections.\n\nIf listenSubnets is not set, but advertisedSubnets is set, listenSubnets defaults to\nadvertisedSubnets.\n\nIf neither advertisedSubnets nor listenSubnets is set, listenSubnets defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\n", "markdownDescription": "The `listenSubnets` field configures the networks for the etcd to listen for peer and client connections.\n\nIf `listenSubnets` is not set, but `advertisedSubnets` is set, `listenSubnets` defaults to\n`advertisedSubnets`.\n\nIf neither `advertisedSubnets` nor `listenSubnets` is set, `listenSubnets` defaults to listen on all addresses.\n\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003elistenSubnets\u003c/code\u003e field configures the networks for the etcd to listen for peer and client connections.\u003c/p\u003e\n\n\u003cp\u003eIf \u003ccode\u003elistenSubnets\u003c/code\u003e is not set, but \u003ccode\u003eadvertisedSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to\n\u003ccode\u003eadvertisedSubnets\u003c/code\u003e.\u003c/p\u003e\n\n\u003cp\u003eIf neither \u003ccode\u003eadvertisedSubnets\u003c/code\u003e nor \u003ccode\u003elistenSubnets\u003c/code\u003e is set, \u003ccode\u003elistenSubnets\u003c/code\u003e defaults to listen on all addresses.\u003c/p\u003e\n\n\u003cp\u003eIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, advertised IP is selected as the first routable address of the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExternalCloudProviderConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable external cloud provider.\n", "markdownDescription": "Enable external cloud provider.", "x-intellij-html-description": "\u003cp\u003eEnable external cloud provider.\u003c/p\u003e\n" }, "manifests": { "items": { "type": "string" }, "type": "array", "title": "manifests", "description": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\n", "markdownDescription": "A list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.", "x-intellij-html-description": "\u003cp\u003eA list of urls that point to additional manifests for an external cloud provider.\nThese will get automatically deployed as part of the bootstrap.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExtraHost": { "properties": { "ip": { "type": "string", "title": "ip", "description": "The IP of the host.\n", "markdownDescription": "The IP of the host.", "x-intellij-html-description": "\u003cp\u003eThe IP of the host.\u003c/p\u003e\n" }, "aliases": { "items": { "type": "string" }, "type": "array", "title": "aliases", "description": "The host alias.\n", "markdownDescription": "The host alias.", "x-intellij-html-description": "\u003cp\u003eThe host alias.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ExtraMount": { "properties": { "destination": { "type": "string", "title": "destination", "description": "Destination is the absolute path where the mount will be placed in the container.\n", "markdownDescription": "Destination is the absolute path where the mount will be placed in the container.", "x-intellij-html-description": "\u003cp\u003eDestination is the absolute path where the mount will be placed in the container.\u003c/p\u003e\n" }, "type": { "type": "string", "title": "type", "description": "Type specifies the mount kind.\n", "markdownDescription": "Type specifies the mount kind.", "x-intellij-html-description": "\u003cp\u003eType specifies the mount kind.\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "Source specifies the source path of the mount.\n", "markdownDescription": "Source specifies the source path of the mount.", "x-intellij-html-description": "\u003cp\u003eSource specifies the source path of the mount.\u003c/p\u003e\n" }, "options": { "items": { "type": "string" }, "type": "array", "title": "options", "description": "Options are fstab style mount options.\n", "markdownDescription": "Options are fstab style mount options.", "x-intellij-html-description": "\u003cp\u003eOptions are fstab style mount options.\u003c/p\u003e\n" }, "uidMappings": { "items": { "$ref": "#/$defs/LinuxIDMapping" }, "type": "array", "title": "uidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" }, "gidMappings": { "items": { "$ref": "#/$defs/LinuxIDMapping" }, "type": "array", "title": "gidMappings", "description": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.\n", "markdownDescription": "UID/GID mappings used for changing file owners w/o calling chown, fs should support it.\n\nEvery mount point could have its own mapping.", "x-intellij-html-description": "\u003cp\u003eUID/GID mappings used for changing file owners w/o calling chown, fs should support it.\u003c/p\u003e\n\n\u003cp\u003eEvery mount point could have its own mapping.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "FeaturesConfig": { "properties": { "rbac": { "type": "boolean", "title": "rbac", "description": "Enable role-based access control (RBAC).\n", "markdownDescription": "Enable role-based access control (RBAC).", "x-intellij-html-description": "\u003cp\u003eEnable role-based access control (RBAC).\u003c/p\u003e\n" }, "stableHostname": { "type": "boolean", "title": "stableHostname", "description": "Enable stable default hostname.\n", "markdownDescription": "Enable stable default hostname.", "x-intellij-html-description": "\u003cp\u003eEnable stable default hostname.\u003c/p\u003e\n" }, "kubernetesTalosAPIAccess": { "$ref": "#/$defs/KubernetesTalosAPIAccessConfig", "title": "kubernetesTalosAPIAccess", "description": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.\n", "markdownDescription": "Configure Talos API access from Kubernetes pods.\n\nThis feature is disabled if the feature config is not specified.", "x-intellij-html-description": "\u003cp\u003eConfigure Talos API access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eThis feature is disabled if the feature config is not specified.\u003c/p\u003e\n" }, "apidCheckExtKeyUsage": { "type": "boolean", "title": "apidCheckExtKeyUsage", "description": "Enable checks for extended key usage of client certificates in apid.\n", "markdownDescription": "Enable checks for extended key usage of client certificates in apid.", "x-intellij-html-description": "\u003cp\u003eEnable checks for extended key usage of client certificates in apid.\u003c/p\u003e\n" }, "diskQuotaSupport": { "type": "boolean", "title": "diskQuotaSupport", "description": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\n", "markdownDescription": "Enable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.", "x-intellij-html-description": "\u003cp\u003eEnable XFS project quota support for EPHEMERAL partition and user disks.\nAlso enables kubelet tracking of ephemeral disk usage in the kubelet via quota.\u003c/p\u003e\n" }, "kubePrism": { "$ref": "#/$defs/KubePrism", "title": "kubePrism", "description": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\n", "markdownDescription": "KubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.", "x-intellij-html-description": "\u003cp\u003eKubePrism - local proxy/load balancer on defined port that will distribute\nrequests to all API servers in the cluster.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "FlannelCNIConfig": { "properties": { "extraArgs": { "items": { "type": "string" }, "type": "array", "title": "extraArgs", "description": "Extra arguments for ‘flanneld’.\n", "markdownDescription": "Extra arguments for 'flanneld'.", "x-intellij-html-description": "\u003cp\u003eExtra arguments for \u0026lsquo;flanneld\u0026rsquo;.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallConfig": { "properties": { "disk": { "type": "string", "title": "disk", "description": "The disk used for installations.\n", "markdownDescription": "The disk used for installations.", "x-intellij-html-description": "\u003cp\u003eThe disk used for installations.\u003c/p\u003e\n" }, "diskSelector": { "$ref": "#/$defs/InstallDiskSelector", "title": "diskSelector", "description": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over disk.\n", "markdownDescription": "Look up disk using disk attributes like model, size, serial and others.\nAlways has priority over `disk`.", "x-intellij-html-description": "\u003cp\u003eLook up disk using disk attributes like model, size, serial and others.\nAlways has priority over \u003ccode\u003edisk\u003c/code\u003e.\u003c/p\u003e\n" }, "extraKernelArgs": { "items": { "type": "string" }, "type": "array", "title": "extraKernelArgs", "description": "Allows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a -.\nFor example -console removes all console=\u0026lt;value\u0026gt; arguments, whereas -console=tty0 removes the console=tty0 default argument.\n", "markdownDescription": "Allows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a `-`.\nFor example `-console` removes all `console=\u003cvalue\u003e` arguments, whereas `-console=tty0` removes the `console=tty0` default argument.", "x-intellij-html-description": "\u003cp\u003eAllows for supplying extra kernel args via the bootloader.\nExisting kernel args can be removed by prefixing the argument with a \u003ccode\u003e-\u003c/code\u003e.\nFor example \u003ccode\u003e-console\u003c/code\u003e removes all \u003ccode\u003econsole=\u0026lt;value\u0026gt;\u003c/code\u003e arguments, whereas \u003ccode\u003e-console=tty0\u003c/code\u003e removes the \u003ccode\u003econsole=tty0\u003c/code\u003e default argument.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\nGitHub releases page.\n", "markdownDescription": "Allows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n[GitHub releases page](https://github.com/siderolabs/talos/releases).", "x-intellij-html-description": "\u003cp\u003eAllows for supplying the image used to perform the installation.\nImage reference for each Talos release can be found on\n\u003ca href=\"https://github.com/siderolabs/talos/releases\" target=\"_blank\"\u003eGitHub releases page\u003c/a\u003e.\u003c/p\u003e\n" }, "extensions": { "items": { "$ref": "#/$defs/InstallExtensionConfig" }, "type": "array", "title": "extensions", "description": "Allows for supplying additional system extension images to install on top of base Talos image.\n", "markdownDescription": "Allows for supplying additional system extension images to install on top of base Talos image.", "x-intellij-html-description": "\u003cp\u003eAllows for supplying additional system extension images to install on top of base Talos image.\u003c/p\u003e\n" }, "wipe": { "type": "boolean", "title": "wipe", "description": "Indicates if the installation disk should be wiped at installation time.\nDefaults to true.\n", "markdownDescription": "Indicates if the installation disk should be wiped at installation time.\nDefaults to `true`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the installation disk should be wiped at installation time.\nDefaults to \u003ccode\u003etrue\u003c/code\u003e.\u003c/p\u003e\n" }, "legacyBIOSSupport": { "type": "boolean", "title": "legacyBIOSSupport", "description": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn’t support GPT partitioning scheme.\n", "markdownDescription": "Indicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn't support GPT partitioning scheme.", "x-intellij-html-description": "\u003cp\u003eIndicates if MBR partition should be marked as bootable (active).\nShould be enabled only for the systems with legacy BIOS that doesn\u0026rsquo;t support GPT partitioning scheme.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallDiskSelector": { "properties": { "size": { "type": "string", "title": "size", "description": "Disk size.\n", "markdownDescription": "Disk size.", "x-intellij-html-description": "\u003cp\u003eDisk size.\u003c/p\u003e\n" }, "name": { "type": "string", "title": "name", "description": "Disk name /sys/block/\u0026lt;dev\u0026gt;/device/name.\n", "markdownDescription": "Disk name `/sys/block/\u003cdev\u003e/device/name`.", "x-intellij-html-description": "\u003cp\u003eDisk name \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/name\u003c/code\u003e.\u003c/p\u003e\n" }, "model": { "type": "string", "title": "model", "description": "Disk model /sys/block/\u0026lt;dev\u0026gt;/device/model.\n", "markdownDescription": "Disk model `/sys/block/\u003cdev\u003e/device/model`.", "x-intellij-html-description": "\u003cp\u003eDisk model \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/model\u003c/code\u003e.\u003c/p\u003e\n" }, "serial": { "type": "string", "title": "serial", "description": "Disk serial number /sys/block/\u0026lt;dev\u0026gt;/serial.\n", "markdownDescription": "Disk serial number `/sys/block/\u003cdev\u003e/serial`.", "x-intellij-html-description": "\u003cp\u003eDisk serial number \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/serial\u003c/code\u003e.\u003c/p\u003e\n" }, "modalias": { "type": "string", "title": "modalias", "description": "Disk modalias /sys/block/\u0026lt;dev\u0026gt;/device/modalias.\n", "markdownDescription": "Disk modalias `/sys/block/\u003cdev\u003e/device/modalias`.", "x-intellij-html-description": "\u003cp\u003eDisk modalias \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/device/modalias\u003c/code\u003e.\u003c/p\u003e\n" }, "uuid": { "type": "string", "title": "uuid", "description": "Disk UUID /sys/block/\u0026lt;dev\u0026gt;/uuid.\n", "markdownDescription": "Disk UUID `/sys/block/\u003cdev\u003e/uuid`.", "x-intellij-html-description": "\u003cp\u003eDisk UUID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/uuid\u003c/code\u003e.\u003c/p\u003e\n" }, "wwid": { "type": "string", "title": "wwid", "description": "Disk WWID /sys/block/\u0026lt;dev\u0026gt;/wwid.\n", "markdownDescription": "Disk WWID `/sys/block/\u003cdev\u003e/wwid`.", "x-intellij-html-description": "\u003cp\u003eDisk WWID \u003ccode\u003e/sys/block/\u0026lt;dev\u0026gt;/wwid\u003c/code\u003e.\u003c/p\u003e\n" }, "type": { "enum": [ "ssd", "hdd", "nvme", "sd" ], "title": "type", "description": "Disk Type.\n", "markdownDescription": "Disk Type.", "x-intellij-html-description": "\u003cp\u003eDisk Type.\u003c/p\u003e\n" }, "busPath": { "type": "string", "title": "busPath", "description": "Disk bus path.\n", "markdownDescription": "Disk bus path.", "x-intellij-html-description": "\u003cp\u003eDisk bus path.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "InstallExtensionConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "System extension image.\n", "markdownDescription": "System extension image.", "x-intellij-html-description": "\u003cp\u003eSystem extension image.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KernelConfig": { "properties": { "modules": { "items": { "$ref": "#/$defs/KernelModuleConfig" }, "type": "array", "title": "modules", "description": "Kernel modules to load.\n", "markdownDescription": "Kernel modules to load.", "x-intellij-html-description": "\u003cp\u003eKernel modules to load.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KernelModuleConfig": { "properties": { "name": { "type": "string", "title": "name", "description": "Module name.\n", "markdownDescription": "Module name.", "x-intellij-html-description": "\u003cp\u003eModule name.\u003c/p\u003e\n" }, "parameters": { "items": { "type": "string" }, "type": "array", "title": "parameters", "description": "Module parameters, changes applied after reboot.\n", "markdownDescription": "Module parameters, changes applied after reboot.", "x-intellij-html-description": "\u003cp\u003eModule parameters, changes applied after reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubePrism": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable KubePrism support - will start local load balacing proxy.\n", "markdownDescription": "Enable KubePrism support - will start local load balacing proxy.", "x-intellij-html-description": "\u003cp\u003eEnable KubePrism support - will start local load balacing proxy.\u003c/p\u003e\n" }, "port": { "type": "integer", "title": "port", "description": "KubePrism port.\n", "markdownDescription": "KubePrism port.", "x-intellij-html-description": "\u003cp\u003eKubePrism port.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeSpanFilters": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.\n", "markdownDescription": "Filter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\n\nBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\n\nDefault value: no filtering.", "x-intellij-html-description": "\u003cp\u003eFilter node addresses which will be advertised as KubeSpan endpoints for peer-to-peer Wireguard connections.\u003c/p\u003e\n\n\u003cp\u003eBy default, all addresses are advertised, and KubeSpan cycles through all endpoints until it finds one that works.\u003c/p\u003e\n\n\u003cp\u003eDefault value: no filtering.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeletConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The image field is an optional reference to an alternative kubelet image.\n", "markdownDescription": "The `image` field is an optional reference to an alternative kubelet image.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eimage\u003c/code\u003e field is an optional reference to an alternative kubelet image.\u003c/p\u003e\n" }, "clusterDNS": { "items": { "type": "string" }, "type": "array", "title": "clusterDNS", "description": "The ClusterDNS field is an optional reference to an alternative kubelet clusterDNS ip list.\n", "markdownDescription": "The `ClusterDNS` field is an optional reference to an alternative kubelet clusterDNS ip list.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eClusterDNS\u003c/code\u003e field is an optional reference to an alternative kubelet clusterDNS ip list.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "The extraArgs field is used to provide additional flags to the kubelet.\n", "markdownDescription": "The `extraArgs` field is used to provide additional flags to the kubelet.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraArgs\u003c/code\u003e field is used to provide additional flags to the kubelet.\u003c/p\u003e\n" }, "extraMounts": { "items": { "$ref": "#/$defs/ExtraMount" }, "type": "array", "title": "extraMounts", "description": "The extraMounts field is used to add additional mounts to the kubelet container.\nNote that either bind or rbind are required in the options.\n", "markdownDescription": "The `extraMounts` field is used to add additional mounts to the kubelet container.\nNote that either `bind` or `rbind` are required in the `options`.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraMounts\u003c/code\u003e field is used to add additional mounts to the kubelet container.\nNote that either \u003ccode\u003ebind\u003c/code\u003e or \u003ccode\u003erbind\u003c/code\u003e are required in the \u003ccode\u003eoptions\u003c/code\u003e.\u003c/p\u003e\n" }, "extraConfig": { "type": "object", "title": "extraConfig", "description": "The extraConfig field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\n", "markdownDescription": "The `extraConfig` field is used to provide kubelet configuration overrides.\n\nSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eextraConfig\u003c/code\u003e field is used to provide kubelet configuration overrides.\u003c/p\u003e\n\n\u003cp\u003eSome fields are not allowed to be overridden: authentication and authorization, cgroups\nconfiguration, ports, etc.\u003c/p\u003e\n" }, "credentialProviderConfig": { "type": "object", "title": "credentialProviderConfig", "description": "The KubeletCredentialProviderConfig field is used to provide kubelet credential configuration.\n", "markdownDescription": "The `KubeletCredentialProviderConfig` field is used to provide kubelet credential configuration.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eKubeletCredentialProviderConfig\u003c/code\u003e field is used to provide kubelet credential configuration.\u003c/p\u003e\n" }, "defaultRuntimeSeccompProfileEnabled": { "type": "boolean", "title": "defaultRuntimeSeccompProfileEnabled", "description": "Enable container runtime default Seccomp profile.\n", "markdownDescription": "Enable container runtime default Seccomp profile.", "x-intellij-html-description": "\u003cp\u003eEnable container runtime default Seccomp profile.\u003c/p\u003e\n" }, "registerWithFQDN": { "type": "boolean", "title": "registerWithFQDN", "description": "The registerWithFQDN field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\n", "markdownDescription": "The `registerWithFQDN` field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eregisterWithFQDN\u003c/code\u003e field is used to force kubelet to use the node FQDN for registration.\nThis is required in clouds like AWS.\u003c/p\u003e\n" }, "nodeIP": { "$ref": "#/$defs/KubeletNodeIPConfig", "title": "nodeIP", "description": "The nodeIP field is used to configure --node-ip flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\n", "markdownDescription": "The `nodeIP` field is used to configure `--node-ip` flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003enodeIP\u003c/code\u003e field is used to configure \u003ccode\u003e--node-ip\u003c/code\u003e flag for the kubelet.\nThis is used when a node has multiple addresses to choose from.\u003c/p\u003e\n" }, "skipNodeRegistration": { "type": "boolean", "title": "skipNodeRegistration", "description": "The skipNodeRegistration is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\n", "markdownDescription": "The `skipNodeRegistration` is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eskipNodeRegistration\u003c/code\u003e is used to run the kubelet without registering with the apiserver.\nThis runs kubelet as standalone and only runs static pods.\u003c/p\u003e\n" }, "disableManifestsDirectory": { "type": "boolean", "title": "disableManifestsDirectory", "description": "The disableManifestsDirectory field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt’s recommended to configure static pods with the “pods” key instead.\n", "markdownDescription": "The `disableManifestsDirectory` field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt's recommended to configure static pods with the \"pods\" key instead.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003edisableManifestsDirectory\u003c/code\u003e field configures the kubelet to get static pod manifests from the /etc/kubernetes/manifests directory.\nIt\u0026rsquo;s recommended to configure static pods with the \u0026ldquo;pods\u0026rdquo; key instead.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubeletNodeIPConfig": { "properties": { "validSubnets": { "items": { "type": "string" }, "type": "array", "title": "validSubnets", "description": "The validSubnets field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with !, e.g !10.0.0.0/8.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\n", "markdownDescription": "The `validSubnets` field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with `!`, e.g `!10.0.0.0/8`.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalidSubnets\u003c/code\u003e field configures the networks to pick kubelet node IP from.\nFor dual stack configuration, there should be two subnets: one for IPv4, another for IPv6.\nIPs can be excluded from the list by using negative match with \u003ccode\u003e!\u003c/code\u003e, e.g \u003ccode\u003e!10.0.0.0/8\u003c/code\u003e.\nNegative subnet matches should be specified last to filter out IPs picked by positive matches.\nIf not specified, node IP is picked based on cluster podCIDRs: IPv4/IPv6 address or both.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "KubernetesTalosAPIAccessConfig": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable Talos API access from Kubernetes pods.\n", "markdownDescription": "Enable Talos API access from Kubernetes pods.", "x-intellij-html-description": "\u003cp\u003eEnable Talos API access from Kubernetes pods.\u003c/p\u003e\n" }, "allowedRoles": { "items": { "type": "string" }, "type": "array", "title": "allowedRoles", "description": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.\n", "markdownDescription": "The list of Talos API roles which can be granted for access from Kubernetes pods.\n\nEmpty list means that no roles can be granted, so access is blocked.", "x-intellij-html-description": "\u003cp\u003eThe list of Talos API roles which can be granted for access from Kubernetes pods.\u003c/p\u003e\n\n\u003cp\u003eEmpty list means that no roles can be granted, so access is blocked.\u003c/p\u003e\n" }, "allowedKubernetesNamespaces": { "items": { "type": "string" }, "type": "array", "title": "allowedKubernetesNamespaces", "description": "The list of Kubernetes namespaces Talos API access is available from.\n", "markdownDescription": "The list of Kubernetes namespaces Talos API access is available from.", "x-intellij-html-description": "\u003cp\u003eThe list of Kubernetes namespaces Talos API access is available from.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LinuxIDMapping": { "properties": { "containerID": { "type": "integer", "title": "containerID", "description": "ContainerID is the starting UID/GID in the container.\n", "markdownDescription": "ContainerID is the starting UID/GID in the container.", "x-intellij-html-description": "\u003cp\u003eContainerID is the starting UID/GID in the container.\u003c/p\u003e\n" }, "hostID": { "type": "integer", "title": "hostID", "description": "HostID is the starting UID/GID on the host to be mapped to ‘ContainerID’.\n", "markdownDescription": "HostID is the starting UID/GID on the host to be mapped to 'ContainerID'.", "x-intellij-html-description": "\u003cp\u003eHostID is the starting UID/GID on the host to be mapped to \u0026lsquo;ContainerID\u0026rsquo;.\u003c/p\u003e\n" }, "size": { "type": "integer", "title": "size", "description": "Size is the number of IDs to be mapped.\n", "markdownDescription": "Size is the number of IDs to be mapped.", "x-intellij-html-description": "\u003cp\u003eSize is the number of IDs to be mapped.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LoggingConfig": { "properties": { "destinations": { "items": { "$ref": "#/$defs/LoggingDestination" }, "type": "array", "title": "destinations", "description": "Logging destination.\n", "markdownDescription": "Logging destination.", "x-intellij-html-description": "\u003cp\u003eLogging destination.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "LoggingDestination": { "properties": { "endpoint": { "$ref": "#/$defs/Endpoint", "title": "endpoint", "description": "Where to send logs. Supported protocols are “tcp” and “udp”.\n", "markdownDescription": "Where to send logs. Supported protocols are \"tcp\" and \"udp\".", "x-intellij-html-description": "\u003cp\u003eWhere to send logs. Supported protocols are \u0026ldquo;tcp\u0026rdquo; and \u0026ldquo;udp\u0026rdquo;.\u003c/p\u003e\n" }, "format": { "enum": [ "json_lines" ], "title": "format", "description": "Logs format.\n", "markdownDescription": "Logs format.", "x-intellij-html-description": "\u003cp\u003eLogs format.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineConfig": { "properties": { "type": { "enum": [ "controlplane", "worker" ], "title": "type", "description": "Defines the role of the machine within the cluster.\n\nControl Plane\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\nWorker\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as “join”; that value is still supported but deprecated.\n", "markdownDescription": "Defines the role of the machine within the cluster.\n\n**Control Plane**\n\nControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\n\n**Worker**\n\nWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\n\nThis node type was previously known as \"join\"; that value is still supported but deprecated.", "x-intellij-html-description": "\u003cp\u003eDefines the role of the machine within the cluster.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eControl Plane\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eControl Plane node type designates the node as a control plane member.\nThis means it will host etcd along with the Kubernetes controlplane components such as API Server, Controller Manager, Scheduler.\u003c/p\u003e\n\n\u003cp\u003e\u003cstrong\u003eWorker\u003c/strong\u003e\u003c/p\u003e\n\n\u003cp\u003eWorker node type designates the node as a worker node.\nThis means it will be an available compute node for scheduling workloads.\u003c/p\u003e\n\n\u003cp\u003eThis node type was previously known as \u0026ldquo;join\u0026rdquo;; that value is still supported but deprecated.\u003c/p\u003e\n" }, "token": { "type": "string", "title": "token", "description": "The token is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its’ identity.\n", "markdownDescription": "The `token` is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its' identity.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003etoken\u003c/code\u003e is used by a machine to join the PKI of the cluster.\nUsing this token, a machine will create a certificate signing request (CSR), and request a certificate that will be used as its\u0026rsquo; identity.\u003c/p\u003e\n" }, "ca": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "ca", "description": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded crt and key.\n", "markdownDescription": "The root certificate authority of the PKI.\nIt is composed of a base64 encoded `crt` and `key`.", "x-intellij-html-description": "\u003cp\u003eThe root certificate authority of the PKI.\nIt is composed of a base64 encoded \u003ccode\u003ecrt\u003c/code\u003e and \u003ccode\u003ekey\u003c/code\u003e.\u003c/p\u003e\n" }, "certSANs": { "items": { "type": "string" }, "type": "array", "title": "certSANs", "description": "Extra certificate subject alternative names for the machine’s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate’s SANs.\n", "markdownDescription": "Extra certificate subject alternative names for the machine's certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate's SANs.", "x-intellij-html-description": "\u003cp\u003eExtra certificate subject alternative names for the machine\u0026rsquo;s certificate.\nBy default, all non-loopback interface IPs are automatically added to the certificate\u0026rsquo;s SANs.\u003c/p\u003e\n" }, "controlPlane": { "$ref": "#/$defs/MachineControlPlaneConfig", "title": "controlPlane", "description": "Provides machine specific control plane configuration options.\n", "markdownDescription": "Provides machine specific control plane configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific control plane configuration options.\u003c/p\u003e\n" }, "kubelet": { "$ref": "#/$defs/KubeletConfig", "title": "kubelet", "description": "Used to provide additional options to the kubelet.\n", "markdownDescription": "Used to provide additional options to the kubelet.", "x-intellij-html-description": "\u003cp\u003eUsed to provide additional options to the kubelet.\u003c/p\u003e\n" }, "pods": { "items": { "type": "object" }, "type": "array", "title": "pods", "description": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn’t validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.\n", "markdownDescription": "Used to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\n\nStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn't validate the pod definition.\nUpdates to this field can be applied without a reboot.\n\nSee https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/.", "x-intellij-html-description": "\u003cp\u003eUsed to provide static pod definitions to be run by the kubelet directly bypassing the kube-apiserver.\u003c/p\u003e\n\n\u003cp\u003eStatic pods can be used to run components which should be started before the Kubernetes control plane is up.\nTalos doesn\u0026rsquo;t validate the pod definition.\nUpdates to this field can be applied without a reboot.\u003c/p\u003e\n\n\u003cp\u003eSee \u003ca href=\"https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\" target=\"_blank\"\u003ehttps://kubernetes.io/docs/tasks/configure-pod-container/static-pod/\u003c/a\u003e.\u003c/p\u003e\n" }, "network": { "$ref": "#/$defs/NetworkConfig", "title": "network", "description": "Provides machine specific network configuration options.\n", "markdownDescription": "Provides machine specific network configuration options.", "x-intellij-html-description": "\u003cp\u003eProvides machine specific network configuration options.\u003c/p\u003e\n" }, "disks": { "items": { "$ref": "#/$defs/MachineDisk" }, "type": "array", "title": "disks", "description": "Used to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of /var, mounts are only valid if they are under /var.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf size: is omitted, the partition is sized to occupy the full disk.\n", "markdownDescription": "Used to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of `/var`, mounts are only valid if they are under `/var`.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf `size:` is omitted, the partition is sized to occupy the full disk.", "x-intellij-html-description": "\u003cp\u003eUsed to partition, format and mount additional disks.\nSince the rootfs is read only with the exception of \u003ccode\u003e/var\u003c/code\u003e, mounts are only valid if they are under \u003ccode\u003e/var\u003c/code\u003e.\nNote that the partitioning and formatting is done only once, if and only if no existing XFS partitions are found.\nIf \u003ccode\u003esize:\u003c/code\u003e is omitted, the partition is sized to occupy the full disk.\u003c/p\u003e\n" }, "install": { "$ref": "#/$defs/InstallConfig", "title": "install", "description": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\n", "markdownDescription": "Used to provide instructions for installations.\n\nNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.", "x-intellij-html-description": "\u003cp\u003eUsed to provide instructions for installations.\u003c/p\u003e\n\n\u003cp\u003eNote that this configuration section gets silently ignored by Talos images that are considered pre-installed.\nTo make sure Talos installs according to the provided configuration, Talos should be booted with ISO or PXE-booted.\u003c/p\u003e\n" }, "files": { "items": { "$ref": "#/$defs/MachineFile" }, "type": "array", "title": "files", "description": "Allows the addition of user specified files.\nThe value of op can be create, overwrite, or append.\nIn the case of create, path must not exist.\nIn the case of overwrite, and append, path must be a valid file.\nIf an op value of append is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\n", "markdownDescription": "Allows the addition of user specified files.\nThe value of `op` can be `create`, `overwrite`, or `append`.\nIn the case of `create`, `path` must not exist.\nIn the case of `overwrite`, and `append`, `path` must be a valid file.\nIf an `op` value of `append` is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.", "x-intellij-html-description": "\u003cp\u003eAllows the addition of user specified files.\nThe value of \u003ccode\u003eop\u003c/code\u003e can be \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003eoverwrite\u003c/code\u003e, or \u003ccode\u003eappend\u003c/code\u003e.\nIn the case of \u003ccode\u003ecreate\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must not exist.\nIn the case of \u003ccode\u003eoverwrite\u003c/code\u003e, and \u003ccode\u003eappend\u003c/code\u003e, \u003ccode\u003epath\u003c/code\u003e must be a valid file.\nIf an \u003ccode\u003eop\u003c/code\u003e value of \u003ccode\u003eappend\u003c/code\u003e is used, the existing file will be appended.\nNote that the file contents are not required to be base64 encoded.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\n", "markdownDescription": "The `env` field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables.\nAll environment variables are set on PID 1 in addition to every service.\u003c/p\u003e\n" }, "time": { "$ref": "#/$defs/TimeConfig", "title": "time", "description": "Used to configure the machine’s time settings.\n", "markdownDescription": "Used to configure the machine's time settings.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s time settings.\u003c/p\u003e\n" }, "sysctls": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysctls", "description": "Used to configure the machine’s sysctls.\n", "markdownDescription": "Used to configure the machine's sysctls.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysctls.\u003c/p\u003e\n" }, "sysfs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "sysfs", "description": "Used to configure the machine’s sysfs.\n", "markdownDescription": "Used to configure the machine's sysfs.", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s sysfs.\u003c/p\u003e\n" }, "registries": { "$ref": "#/$defs/RegistriesConfig", "title": "registries", "description": "Used to configure the machine’s container image registry mirrors.\n\nAutomatically generates matching CRI configuration for registry mirrors.\n\nThe mirrors section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\n\nThe config section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in .docker/config.json.\n\nSee also matching configuration for CRI containerd plugin.\n", "markdownDescription": "Used to configure the machine's container image registry mirrors.\n\nAutomatically generates matching CRI configuration for registry mirrors.\n\nThe `mirrors` section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\n\nThe `config` section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).\n\nSee also matching configuration for [CRI containerd plugin](https://github.com/containerd/cri/blob/master/docs/registry.md).", "x-intellij-html-description": "\u003cp\u003eUsed to configure the machine\u0026rsquo;s container image registry mirrors.\u003c/p\u003e\n\n\u003cp\u003eAutomatically generates matching CRI configuration for registry mirrors.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003emirrors\u003c/code\u003e section allows to redirect requests for images to a non-default registry,\nwhich might be a local registry or a caching mirror.\u003c/p\u003e\n\n\u003cp\u003eThe \u003ccode\u003econfig\u003c/code\u003e section provides a way to authenticate to the registry with TLS client\nidentity, provide registry CA, or authentication information.\nAuthentication information has same meaning with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n\n\u003cp\u003eSee also matching configuration for \u003ca href=\"https://github.com/containerd/cri/blob/master/docs/registry.md\" target=\"_blank\"\u003eCRI containerd plugin\u003c/a\u003e.\u003c/p\u003e\n" }, "systemDiskEncryption": { "$ref": "#/$defs/SystemDiskEncryptionConfig", "title": "systemDiskEncryption", "description": "Machine system disk encryption configuration.\nDefines each system partition encryption parameters.\n", "markdownDescription": "Machine system disk encryption configuration.\nDefines each system partition encryption parameters.", "x-intellij-html-description": "\u003cp\u003eMachine system disk encryption configuration.\nDefines each system partition encryption parameters.\u003c/p\u003e\n" }, "features": { "$ref": "#/$defs/FeaturesConfig", "title": "features", "description": "Features describe individual Talos features that can be switched on or off.\n", "markdownDescription": "Features describe individual Talos features that can be switched on or off.", "x-intellij-html-description": "\u003cp\u003eFeatures describe individual Talos features that can be switched on or off.\u003c/p\u003e\n" }, "udev": { "$ref": "#/$defs/UdevConfig", "title": "udev", "description": "Configures the udev system.\n", "markdownDescription": "Configures the udev system.", "x-intellij-html-description": "\u003cp\u003eConfigures the udev system.\u003c/p\u003e\n" }, "logging": { "$ref": "#/$defs/LoggingConfig", "title": "logging", "description": "Configures the logging system.\n", "markdownDescription": "Configures the logging system.", "x-intellij-html-description": "\u003cp\u003eConfigures the logging system.\u003c/p\u003e\n" }, "kernel": { "$ref": "#/$defs/KernelConfig", "title": "kernel", "description": "Configures the kernel.\n", "markdownDescription": "Configures the kernel.", "x-intellij-html-description": "\u003cp\u003eConfigures the kernel.\u003c/p\u003e\n" }, "seccompProfiles": { "items": { "$ref": "#/$defs/MachineSeccompProfile" }, "type": "array", "title": "seccompProfiles", "description": "Configures the seccomp profiles for the machine.\n", "markdownDescription": "Configures the seccomp profiles for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the seccomp profiles for the machine.\u003c/p\u003e\n" }, "nodeLabels": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeLabels", "description": "Configures the node labels for the machine.\n", "markdownDescription": "Configures the node labels for the machine.", "x-intellij-html-description": "\u003cp\u003eConfigures the node labels for the machine.\u003c/p\u003e\n" }, "nodeTaints": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "nodeTaints", "description": "Configures the node taints for the machine. Effect is optional.\n", "markdownDescription": "Configures the node taints for the machine. Effect is optional.", "x-intellij-html-description": "\u003cp\u003eConfigures the node taints for the machine. Effect is optional.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineControlPlaneConfig": { "properties": { "controllerManager": { "$ref": "#/$defs/MachineControllerManagerConfig", "title": "controllerManager", "description": "Controller manager machine specific configuration options.\n", "markdownDescription": "Controller manager machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eController manager machine specific configuration options.\u003c/p\u003e\n" }, "scheduler": { "$ref": "#/$defs/MachineSchedulerConfig", "title": "scheduler", "description": "Scheduler machine specific configuration options.\n", "markdownDescription": "Scheduler machine specific configuration options.", "x-intellij-html-description": "\u003cp\u003eScheduler machine specific configuration options.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineControllerManagerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-controller-manager on the node.\n", "markdownDescription": "Disable kube-controller-manager on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-controller-manager on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineDisk": { "properties": { "device": { "type": "string", "title": "device", "description": "The name of the disk to use.\n", "markdownDescription": "The name of the disk to use.", "x-intellij-html-description": "\u003cp\u003eThe name of the disk to use.\u003c/p\u003e\n" }, "partitions": { "items": { "$ref": "#/$defs/DiskPartition" }, "type": "array", "title": "partitions", "description": "A list of partitions to create on the disk.\n", "markdownDescription": "A list of partitions to create on the disk.", "x-intellij-html-description": "\u003cp\u003eA list of partitions to create on the disk.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineFile": { "properties": { "content": { "type": "string", "title": "content", "description": "The contents of the file.\n", "markdownDescription": "The contents of the file.", "x-intellij-html-description": "\u003cp\u003eThe contents of the file.\u003c/p\u003e\n" }, "permissions": { "type": "integer", "title": "permissions", "description": "The file’s permissions in octal.\n", "markdownDescription": "The file's permissions in octal.", "x-intellij-html-description": "\u003cp\u003eThe file\u0026rsquo;s permissions in octal.\u003c/p\u003e\n" }, "path": { "type": "string", "title": "path", "description": "The path of the file.\n", "markdownDescription": "The path of the file.", "x-intellij-html-description": "\u003cp\u003eThe path of the file.\u003c/p\u003e\n" }, "op": { "enum": [ "create", "append", "overwrite" ], "title": "op", "description": "The operation to use\n", "markdownDescription": "The operation to use", "x-intellij-html-description": "\u003cp\u003eThe operation to use\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineSchedulerConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-scheduler on the node.\n", "markdownDescription": "Disable kube-scheduler on the node.", "x-intellij-html-description": "\u003cp\u003eDisable kube-scheduler on the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "MachineSeccompProfile": { "properties": { "name": { "type": "string", "title": "name", "description": "The name field is used to provide the file name of the seccomp profile.\n", "markdownDescription": "The `name` field is used to provide the file name of the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003ename\u003c/code\u003e field is used to provide the file name of the seccomp profile.\u003c/p\u003e\n" }, "value": { "type": "object", "title": "value", "description": "The value field is used to provide the seccomp profile.\n", "markdownDescription": "The `value` field is used to provide the seccomp profile.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003evalue\u003c/code\u003e field is used to provide the seccomp profile.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkConfig": { "properties": { "hostname": { "type": "string", "title": "hostname", "description": "Used to statically set the hostname for the machine.\n", "markdownDescription": "Used to statically set the hostname for the machine.", "x-intellij-html-description": "\u003cp\u003eUsed to statically set the hostname for the machine.\u003c/p\u003e\n" }, "interfaces": { "items": { "$ref": "#/$defs/Device" }, "type": "array", "title": "interfaces", "description": "interfaces is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.\n", "markdownDescription": "`interfaces` is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.", "x-intellij-html-description": "\u003cp\u003e\u003ccode\u003einterfaces\u003c/code\u003e is used to define the network interface configuration.\nBy default all network interfaces will attempt a DHCP discovery.\nThis can be further tuned through this configuration parameter.\u003c/p\u003e\n" }, "nameservers": { "items": { "type": "string" }, "type": "array", "title": "nameservers", "description": "Used to statically set the nameservers for the machine.\nDefaults to 1.1.1.1 and 8.8.8.8\n", "markdownDescription": "Used to statically set the nameservers for the machine.\nDefaults to `1.1.1.1` and `8.8.8.8`", "x-intellij-html-description": "\u003cp\u003eUsed to statically set the nameservers for the machine.\nDefaults to \u003ccode\u003e1.1.1.1\u003c/code\u003e and \u003ccode\u003e8.8.8.8\u003c/code\u003e\u003c/p\u003e\n" }, "extraHostEntries": { "items": { "$ref": "#/$defs/ExtraHost" }, "type": "array", "title": "extraHostEntries", "description": "Allows for extra entries to be added to the /etc/hosts file\n", "markdownDescription": "Allows for extra entries to be added to the `/etc/hosts` file", "x-intellij-html-description": "\u003cp\u003eAllows for extra entries to be added to the \u003ccode\u003e/etc/hosts\u003c/code\u003e file\u003c/p\u003e\n" }, "kubespan": { "$ref": "#/$defs/NetworkKubeSpan", "title": "kubespan", "description": "Configures KubeSpan feature.\n", "markdownDescription": "Configures KubeSpan feature.", "x-intellij-html-description": "\u003cp\u003eConfigures KubeSpan feature.\u003c/p\u003e\n" }, "disableSearchDomain": { "type": "boolean", "title": "disableSearchDomain", "description": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to false.\n", "markdownDescription": "Disable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to `false`.", "x-intellij-html-description": "\u003cp\u003eDisable generating a default search domain in /etc/resolv.conf\nbased on the machine hostname.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkDeviceSelector": { "properties": { "busPath": { "type": "string", "title": "busPath", "description": "PCI, USB bus prefix, supports matching by wildcard.\n", "markdownDescription": "PCI, USB bus prefix, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003ePCI, USB bus prefix, supports matching by wildcard.\u003c/p\u003e\n" }, "hardwareAddr": { "type": "string", "title": "hardwareAddr", "description": "Device hardware address, supports matching by wildcard.\n", "markdownDescription": "Device hardware address, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003eDevice hardware address, supports matching by wildcard.\u003c/p\u003e\n" }, "pciID": { "type": "string", "title": "pciID", "description": "PCI ID (vendor ID, product ID), supports matching by wildcard.\n", "markdownDescription": "PCI ID (vendor ID, product ID), supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003ePCI ID (vendor ID, product ID), supports matching by wildcard.\u003c/p\u003e\n" }, "driver": { "type": "string", "title": "driver", "description": "Kernel driver, supports matching by wildcard.\n", "markdownDescription": "Kernel driver, supports matching by wildcard.", "x-intellij-html-description": "\u003cp\u003eKernel driver, supports matching by wildcard.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "NetworkKubeSpan": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Enable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.\n", "markdownDescription": "Enable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.", "x-intellij-html-description": "\u003cp\u003eEnable the KubeSpan feature.\nCluster discovery should be enabled with .cluster.discovery.enabled for KubeSpan to be enabled.\u003c/p\u003e\n" }, "advertiseKubernetesNetworks": { "type": "boolean", "title": "advertiseKubernetesNetworks", "description": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.\n", "markdownDescription": "Control whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.", "x-intellij-html-description": "\u003cp\u003eControl whether Kubernetes pod CIDRs are announced over KubeSpan from the node.\nIf disabled, CNI handles encapsulating pod-to-pod traffic into some node-to-node tunnel,\nand KubeSpan handles the node-to-node traffic.\nIf enabled, KubeSpan will take over pod-to-pod traffic and send it over KubeSpan directly.\nWhen enabled, KubeSpan should have a way to detect complete pod CIDRs of the node which\nis not always the case with CNIs not relying on Kubernetes for IPAM.\u003c/p\u003e\n" }, "allowDownPeerBypass": { "type": "boolean", "title": "allowDownPeerBypass", "description": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can’t be established.\n", "markdownDescription": "Skip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can't be established.", "x-intellij-html-description": "\u003cp\u003eSkip sending traffic via KubeSpan if the peer connection state is not up.\nThis provides configurable choice between connectivity and security: either traffic is always\nforced to go via KubeSpan (even if Wireguard peer connection is not up), or traffic can go directly\nto the peer if Wireguard connection can\u0026rsquo;t be established.\u003c/p\u003e\n" }, "harvestExtraEndpoints": { "type": "boolean", "title": "harvestExtraEndpoints", "description": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u0026gt;50) in the KubeSpan network it can cause performance issues.\n", "markdownDescription": "KubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u003e50) in the KubeSpan network it can cause performance issues.", "x-intellij-html-description": "\u003cp\u003eKubeSpan can collect and publish extra endpoints for each member of the cluster\nbased on Wireguard endpoint information for each peer.\nThis feature is enabled by default to help discover additional endpoints,\nbut with high number of peers (\u0026gt;50) in the KubeSpan network it can cause performance issues.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "KubeSpan link MTU size.\nDefault value is 1420.\n", "markdownDescription": "KubeSpan link MTU size.\nDefault value is 1420.", "x-intellij-html-description": "\u003cp\u003eKubeSpan link MTU size.\nDefault value is 1420.\u003c/p\u003e\n" }, "filters": { "$ref": "#/$defs/KubeSpanFilters", "title": "filters", "description": "KubeSpan advanced filtering of network addresses .\n\nSettings in this section are optional, and settings apply only to the node.\n", "markdownDescription": "KubeSpan advanced filtering of network addresses .\n\nSettings in this section are optional, and settings apply only to the node.", "x-intellij-html-description": "\u003cp\u003eKubeSpan advanced filtering of network addresses .\u003c/p\u003e\n\n\u003cp\u003eSettings in this section are optional, and settings apply only to the node.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ProxyConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable kube-proxy deployment on cluster bootstrap.\n", "markdownDescription": "Disable kube-proxy deployment on cluster bootstrap.", "x-intellij-html-description": "\u003cp\u003eDisable kube-proxy deployment on cluster bootstrap.\u003c/p\u003e\n" }, "image": { "type": "string", "title": "image", "description": "The container image used in the kube-proxy manifest.\n", "markdownDescription": "The container image used in the kube-proxy manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the kube-proxy manifest.\u003c/p\u003e\n" }, "mode": { "type": "string", "title": "mode", "description": "proxy mode of kube-proxy.\nThe default is ‘iptables’.\n", "markdownDescription": "proxy mode of kube-proxy.\nThe default is 'iptables'.", "x-intellij-html-description": "\u003cp\u003eproxy mode of kube-proxy.\nThe default is \u0026lsquo;iptables\u0026rsquo;.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to kube-proxy.\n", "markdownDescription": "Extra arguments to supply to kube-proxy.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to kube-proxy.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistriesConfig": { "properties": { "mirrors": { "patternProperties": { ".*": { "$ref": "#/$defs/RegistryMirrorConfig" } }, "type": "object", "title": "mirrors", "description": "Specifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\n\nFor example, when pulling an image with the reference example.com:123/image:v1,\nthe example.com:123 key will be used to lookup the mirror configuration.\n\nOptionally the * key can be used to configure a fallback mirror.\n\nRegistry name is the first segment of image identifier, with ‘docker.io’\nbeing default one.\n", "markdownDescription": "Specifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\n\nFor example, when pulling an image with the reference `example.com:123/image:v1`,\nthe `example.com:123` key will be used to lookup the mirror configuration.\n\nOptionally the `*` key can be used to configure a fallback mirror.\n\nRegistry name is the first segment of image identifier, with 'docker.io'\nbeing default one.", "x-intellij-html-description": "\u003cp\u003eSpecifies mirror configuration for each registry host namespace.\nThis setting allows to configure local pull-through caching registires,\nair-gapped installations, etc.\u003c/p\u003e\n\n\u003cp\u003eFor example, when pulling an image with the reference \u003ccode\u003eexample.com:123/image:v1\u003c/code\u003e,\nthe \u003ccode\u003eexample.com:123\u003c/code\u003e key will be used to lookup the mirror configuration.\u003c/p\u003e\n\n\u003cp\u003eOptionally the \u003ccode\u003e*\u003c/code\u003e key can be used to configure a fallback mirror.\u003c/p\u003e\n\n\u003cp\u003eRegistry name is the first segment of image identifier, with \u0026lsquo;docker.io\u0026rsquo;\nbeing default one.\u003c/p\u003e\n" }, "config": { "patternProperties": { ".*": { "$ref": "#/$defs/RegistryConfig" } }, "type": "object", "title": "config", "description": "Specifies TLS \u0026amp; auth configuration for HTTPS image registries.\nMutual TLS can be enabled with ‘clientIdentity’ option.\n\nThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key * can’t be used for TLS configuration.\n\nTLS configuration can be skipped if registry has trusted\nserver certificate.\n", "markdownDescription": "Specifies TLS \u0026 auth configuration for HTTPS image registries.\nMutual TLS can be enabled with 'clientIdentity' option.\n\nThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key `*` can't be used for TLS configuration.\n\nTLS configuration can be skipped if registry has trusted\nserver certificate.", "x-intellij-html-description": "\u003cp\u003eSpecifies TLS \u0026amp; auth configuration for HTTPS image registries.\nMutual TLS can be enabled with \u0026lsquo;clientIdentity\u0026rsquo; option.\u003c/p\u003e\n\n\u003cp\u003eThe full hostname and port (if not using a default port 443)\nshould be used as the key.\nThe fallback key \u003ccode\u003e*\u003c/code\u003e can\u0026rsquo;t be used for TLS configuration.\u003c/p\u003e\n\n\u003cp\u003eTLS configuration can be skipped if registry has trusted\nserver certificate.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryAuthConfig": { "properties": { "username": { "type": "string", "title": "username", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "password": { "type": "string", "title": "password", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "auth": { "type": "string", "title": "auth", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" }, "identityToken": { "type": "string", "title": "identityToken", "description": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in .docker/config.json.\n", "markdownDescription": "Optional registry authentication.\nThe meaning of each field is the same with the corresponding field in [`.docker/config.json`](https://docs.docker.com/engine/api/v1.41/#section/Authentication).", "x-intellij-html-description": "\u003cp\u003eOptional registry authentication.\nThe meaning of each field is the same with the corresponding field in \u003ca href=\"https://docs.docker.com/engine/api/v1.41/#section/Authentication\" target=\"_blank\"\u003e\u003ccode\u003e.docker/config.json\u003c/code\u003e\u003c/a\u003e.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryConfig": { "properties": { "tls": { "$ref": "#/$defs/RegistryTLSConfig", "title": "tls", "description": "The TLS configuration for the registry.\n", "markdownDescription": "The TLS configuration for the registry.", "x-intellij-html-description": "\u003cp\u003eThe TLS configuration for the registry.\u003c/p\u003e\n" }, "auth": { "$ref": "#/$defs/RegistryAuthConfig", "title": "auth", "description": "The auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.\n", "markdownDescription": "The auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.", "x-intellij-html-description": "\u003cp\u003eThe auth configuration for this registry.\nNote: changes to the registry auth will not be picked up by the CRI containerd plugin without a reboot.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryKubernetesConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable Kubernetes discovery registry.\n", "markdownDescription": "Disable Kubernetes discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable Kubernetes discovery registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryMirrorConfig": { "properties": { "endpoints": { "items": { "type": "string" }, "type": "array", "title": "endpoints", "description": "List of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to /v2).\n", "markdownDescription": "List of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to `/v2`).", "x-intellij-html-description": "\u003cp\u003eList of endpoints (URLs) for registry mirrors to use.\nEndpoint configures HTTP/HTTPS access mode, host name,\nport and path (if path is not set, it defaults to \u003ccode\u003e/v2\u003c/code\u003e).\u003c/p\u003e\n" }, "overridePath": { "type": "boolean", "title": "overridePath", "description": "Use the exact path specified for the endpoint (don’t append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.\n", "markdownDescription": "Use the exact path specified for the endpoint (don't append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.", "x-intellij-html-description": "\u003cp\u003eUse the exact path specified for the endpoint (don\u0026rsquo;t append /v2/).\nThis setting is often required for setting up multiple mirrors\non a single instance of a registry.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryServiceConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Disable external service discovery registry.\n", "markdownDescription": "Disable external service discovery registry.", "x-intellij-html-description": "\u003cp\u003eDisable external service discovery registry.\u003c/p\u003e\n" }, "endpoint": { "type": "string", "title": "endpoint", "description": "External service endpoint.\n", "markdownDescription": "External service endpoint.", "x-intellij-html-description": "\u003cp\u003eExternal service endpoint.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "RegistryTLSConfig": { "properties": { "clientIdentity": { "properties": { "crt": { "type": "string" }, "key": { "type": "string" } }, "additionalProperties": false, "type": "object", "title": "clientIdentity", "description": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.\n", "markdownDescription": "Enable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.", "x-intellij-html-description": "\u003cp\u003eEnable mutual TLS authentication with the registry.\nClient certificate and key should be base64-encoded.\u003c/p\u003e\n" }, "ca": { "type": "string", "title": "ca", "description": "CA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.\n", "markdownDescription": "CA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.", "x-intellij-html-description": "\u003cp\u003eCA registry certificate to add the list of trusted certificates.\nCertificate should be base64-encoded.\u003c/p\u003e\n" }, "insecureSkipVerify": { "type": "boolean", "title": "insecureSkipVerify", "description": "Skip TLS server certificate verification (not recommended).\n", "markdownDescription": "Skip TLS server certificate verification (not recommended).", "x-intellij-html-description": "\u003cp\u003eSkip TLS server certificate verification (not recommended).\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "ResourcesConfig": { "properties": { "requests": { "$ref": "#/$defs/Unstructured", "title": "requests", "description": "Requests configures the reserved cpu/memory resources.\n", "markdownDescription": "Requests configures the reserved cpu/memory resources.", "x-intellij-html-description": "\u003cp\u003eRequests configures the reserved cpu/memory resources.\u003c/p\u003e\n" }, "limits": { "$ref": "#/$defs/Unstructured", "title": "limits", "description": "Limits configures the maximum cpu/memory resources a container can use.\n", "markdownDescription": "Limits configures the maximum cpu/memory resources a container can use.", "x-intellij-html-description": "\u003cp\u003eLimits configures the maximum cpu/memory resources a container can use.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Route": { "properties": { "network": { "type": "string", "title": "network", "description": "The route’s network (destination).\n", "markdownDescription": "The route's network (destination).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s network (destination).\u003c/p\u003e\n" }, "gateway": { "type": "string", "title": "gateway", "description": "The route’s gateway (if empty, creates link scope route).\n", "markdownDescription": "The route's gateway (if empty, creates link scope route).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s gateway (if empty, creates link scope route).\u003c/p\u003e\n" }, "source": { "type": "string", "title": "source", "description": "The route’s source address (optional).\n", "markdownDescription": "The route's source address (optional).", "x-intellij-html-description": "\u003cp\u003eThe route\u0026rsquo;s source address (optional).\u003c/p\u003e\n" }, "metric": { "type": "integer", "title": "metric", "description": "The optional metric for the route.\n", "markdownDescription": "The optional metric for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional metric for the route.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The optional MTU for the route.\n", "markdownDescription": "The optional MTU for the route.", "x-intellij-html-description": "\u003cp\u003eThe optional MTU for the route.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "STP": { "properties": { "enabled": { "type": "boolean", "title": "enabled", "description": "Whether Spanning Tree Protocol (STP) is enabled.\n", "markdownDescription": "Whether Spanning Tree Protocol (STP) is enabled.", "x-intellij-html-description": "\u003cp\u003eWhether Spanning Tree Protocol (STP) is enabled.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "SchedulerConfig": { "properties": { "image": { "type": "string", "title": "image", "description": "The container image used in the scheduler manifest.\n", "markdownDescription": "The container image used in the scheduler manifest.", "x-intellij-html-description": "\u003cp\u003eThe container image used in the scheduler manifest.\u003c/p\u003e\n" }, "extraArgs": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "extraArgs", "description": "Extra arguments to supply to the scheduler.\n", "markdownDescription": "Extra arguments to supply to the scheduler.", "x-intellij-html-description": "\u003cp\u003eExtra arguments to supply to the scheduler.\u003c/p\u003e\n" }, "extraVolumes": { "items": { "$ref": "#/$defs/VolumeMountConfig" }, "type": "array", "title": "extraVolumes", "description": "Extra volumes to mount to the scheduler static pod.\n", "markdownDescription": "Extra volumes to mount to the scheduler static pod.", "x-intellij-html-description": "\u003cp\u003eExtra volumes to mount to the scheduler static pod.\u003c/p\u003e\n" }, "env": { "patternProperties": { ".*": { "type": "string" } }, "type": "object", "title": "env", "description": "The env field allows for the addition of environment variables for the control plane component.\n", "markdownDescription": "The `env` field allows for the addition of environment variables for the control plane component.", "x-intellij-html-description": "\u003cp\u003eThe \u003ccode\u003eenv\u003c/code\u003e field allows for the addition of environment variables for the control plane component.\u003c/p\u003e\n" }, "resources": { "type": "object", "title": "resources", "description": "Configure the scheduler resources.\n", "markdownDescription": "Configure the scheduler resources.", "x-intellij-html-description": "\u003cp\u003eConfigure the scheduler resources.\u003c/p\u003e\n" }, "config": { "type": "object", "title": "config", "description": "Specify custom kube-scheduler configuration.\n", "markdownDescription": "Specify custom kube-scheduler configuration.", "x-intellij-html-description": "\u003cp\u003eSpecify custom kube-scheduler configuration.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "SystemDiskEncryptionConfig": { "properties": { "state": { "$ref": "#/$defs/EncryptionConfig", "title": "state", "description": "State partition encryption.\n", "markdownDescription": "State partition encryption.", "x-intellij-html-description": "\u003cp\u003eState partition encryption.\u003c/p\u003e\n" }, "ephemeral": { "$ref": "#/$defs/EncryptionConfig", "title": "ephemeral", "description": "Ephemeral partition encryption.\n", "markdownDescription": "Ephemeral partition encryption.", "x-intellij-html-description": "\u003cp\u003eEphemeral partition encryption.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "TimeConfig": { "properties": { "disabled": { "type": "boolean", "title": "disabled", "description": "Indicates if the time service is disabled for the machine.\nDefaults to false.\n", "markdownDescription": "Indicates if the time service is disabled for the machine.\nDefaults to `false`.", "x-intellij-html-description": "\u003cp\u003eIndicates if the time service is disabled for the machine.\nDefaults to \u003ccode\u003efalse\u003c/code\u003e.\u003c/p\u003e\n" }, "servers": { "items": { "type": "string" }, "type": "array", "title": "servers", "description": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to pool.ntp.org\n", "markdownDescription": "Specifies time (NTP) servers to use for setting the system time.\nDefaults to `pool.ntp.org`", "x-intellij-html-description": "\u003cp\u003eSpecifies time (NTP) servers to use for setting the system time.\nDefaults to \u003ccode\u003epool.ntp.org\u003c/code\u003e\u003c/p\u003e\n" }, "bootTimeout": { "type": "string", "pattern": "^[-+]?(((\\d+(\\.\\d*)?|\\d*(\\.\\d+)+)([nuµm]?s|m|h))|0)+$", "title": "bootTimeout", "description": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to “infinity” (waiting forever for time sync)\n", "markdownDescription": "Specifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \"infinity\" (waiting forever for time sync)", "x-intellij-html-description": "\u003cp\u003eSpecifies the timeout when the node time is considered to be in sync unlocking the boot sequence.\nNTP sync will be still running in the background.\nDefaults to \u0026ldquo;infinity\u0026rdquo; (waiting forever for time sync)\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "UdevConfig": { "properties": { "rules": { "items": { "type": "string" }, "type": "array", "title": "rules", "description": "List of udev rules to apply to the udev system\n", "markdownDescription": "List of udev rules to apply to the udev system", "x-intellij-html-description": "\u003cp\u003eList of udev rules to apply to the udev system\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VIPEquinixMetalConfig": { "properties": { "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Equinix Metal API Token.\n", "markdownDescription": "Specifies the Equinix Metal API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Equinix Metal API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VIPHCloudConfig": { "properties": { "apiToken": { "type": "string", "title": "apiToken", "description": "Specifies the Hetzner Cloud API Token.\n", "markdownDescription": "Specifies the Hetzner Cloud API Token.", "x-intellij-html-description": "\u003cp\u003eSpecifies the Hetzner Cloud API Token.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "Vlan": { "properties": { "addresses": { "items": { "type": "string" }, "type": "array", "title": "addresses", "description": "The addresses in CIDR notation or as plain IPs to use.\n", "markdownDescription": "The addresses in CIDR notation or as plain IPs to use.", "x-intellij-html-description": "\u003cp\u003eThe addresses in CIDR notation or as plain IPs to use.\u003c/p\u003e\n" }, "routes": { "items": { "$ref": "#/$defs/Route" }, "type": "array", "title": "routes", "description": "A list of routes associated with the VLAN.\n", "markdownDescription": "A list of routes associated with the VLAN.", "x-intellij-html-description": "\u003cp\u003eA list of routes associated with the VLAN.\u003c/p\u003e\n" }, "dhcp": { "type": "boolean", "title": "dhcp", "description": "Indicates if DHCP should be used.\n", "markdownDescription": "Indicates if DHCP should be used.", "x-intellij-html-description": "\u003cp\u003eIndicates if DHCP should be used.\u003c/p\u003e\n" }, "vlanId": { "type": "integer", "title": "vlanId", "description": "The VLAN’s ID.\n", "markdownDescription": "The VLAN's ID.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s ID.\u003c/p\u003e\n" }, "mtu": { "type": "integer", "title": "mtu", "description": "The VLAN’s MTU.\n", "markdownDescription": "The VLAN's MTU.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s MTU.\u003c/p\u003e\n" }, "vip": { "$ref": "#/$defs/DeviceVIPConfig", "title": "vip", "description": "The VLAN’s virtual IP address configuration.\n", "markdownDescription": "The VLAN's virtual IP address configuration.", "x-intellij-html-description": "\u003cp\u003eThe VLAN\u0026rsquo;s virtual IP address configuration.\u003c/p\u003e\n" }, "dhcpOptions": { "$ref": "#/$defs/DHCPOptions", "title": "dhcpOptions", "description": "DHCP specific options.\ndhcp must be set to true for these to take effect.\n", "markdownDescription": "DHCP specific options.\n`dhcp` *must* be set to true for these to take effect.", "x-intellij-html-description": "\u003cp\u003eDHCP specific options.\n\u003ccode\u003edhcp\u003c/code\u003e \u003cem\u003emust\u003c/em\u003e be set to true for these to take effect.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" }, "VolumeMountConfig": { "properties": { "hostPath": { "type": "string", "title": "hostPath", "description": "Path on the host.\n", "markdownDescription": "Path on the host.", "x-intellij-html-description": "\u003cp\u003ePath on the host.\u003c/p\u003e\n" }, "mountPath": { "type": "string", "title": "mountPath", "description": "Path in the container.\n", "markdownDescription": "Path in the container.", "x-intellij-html-description": "\u003cp\u003ePath in the container.\u003c/p\u003e\n" }, "readonly": { "type": "boolean", "title": "readonly", "description": "Mount the volume read only.\n", "markdownDescription": "Mount the volume read only.", "x-intellij-html-description": "\u003cp\u003eMount the volume read only.\u003c/p\u003e\n" } }, "additionalProperties": false, "type": "object" } } } ================================================ FILE: website/static/install ================================================ #!/usr/bin/env sh set -eu INSTALLPATH=${INSTALLPATH:-/usr/local/bin} happyexit() { echo "Installed version:" echo "$(talosctl version 2>/dev/null )" echo "" echo "Now run:" echo "" echo " talosctl cluster create # install a local test cluster" echo " talosctl dashboard # If you have created a cluster, launch the dashboard" echo "" echo "Looking for more? Visit https://talos.dev/latest" echo "" exit 0 } validate_checksum() { filename=$1 url="https://github.com/siderolabs/talos/releases/latest/download/sha256sum.txt" SHA=$(curl --proto '=https' --tlsv1.2 -sSfL "${url}" | grep "${filename}" | awk '{print $1 }') echo "" echo "Validating checksum..." case $checksumbin in *openssl) checksum=$($checksumbin dgst -sha256 "${filename}" | sed -e 's/^.* //') ;; *shasum) checksum=$($checksumbin -a256 "${filename}" | sed -e 's/ .*$//') ;; esac if [ "$checksum" != "$SHA" ]; then echo "Checksum validation failed." >&2 return 1 fi echo "Checksum valid." return 0 } OS=$(uname -s) arch=$(uname -m) cli_arch="" case $OS in CYGWIN* | MINGW64*) OS=windows-amd64.exe ;; Darwin | Linux | FreeBSD) case $arch in x86_64) cli_arch=amd64 ;; armv8*) cli_arch=arm64 ;; aarch64*) cli_arch=arm64 ;; amd64|arm64) cli_arch=$arch ;; *) echo "There is no talosctl $OS support for $arch. Please open an issue with your platform details." exit 1 ;; esac ;; *) echo "There is no talosctl support for $OS/$arch. Please open an issue with your platform details." exit 1 ;; esac OS=$(echo $OS | tr '[:upper:]' '[:lower:]') dstfile="${INSTALLPATH}/talosctl" checksumbin=$(command -v openssl) || checksumbin=$(command -v shasum) || { echo "Failed to find checksum binary. Please install openssl or shasum." echo "" echo "You can also elect to just download/install https://github.com/siderolabs/talos/releases/latest/download/${srcfile}" echo "into ${dstfile}" exit 1 } if [ -e "${dstfile}" ]; then echo "" echo "talosctl was already downloaded; 🎉" echo "" echo "To force re-downloading, delete '${dstfile}' then run me again." happyexit fi tmpdir=$(mktemp -d /tmp/talosctl.XXXXXX) srcfile="talosctl-${OS}" if [ -n "${cli_arch}" ]; then srcfile="${srcfile}-${cli_arch}" fi url="https://github.com/siderolabs/talos/releases/latest/download/${srcfile}" ( cd "$tmpdir" echo "Downloading ${srcfile}..." curl --proto '=https' --tlsv1.2 -fLO "${url}" echo "Download complete!" if ! validate_checksum "${srcfile}"; then exit 1 fi echo "" exit ) ( super='' if [ ! -w "${INSTALLPATH}" ]; then if $(command -v sudo > /dev/null 2>&1); then super='sudo -E sh -c' elif $(command -v su > /dev/null 2>&1); then super='su -c' fi fi if [ -z "${super}" ]; then mv "${tmpdir}"/"${srcfile}" "${dstfile}" chmod +x "${dstfile}" else ${super} 'mv "'${tmpdir}/${srcfile}'" "'${dstfile}'"' ${super} 'chmod +x "'${dstfile}'"' fi ) rm -r "$tmpdir" echo "talosctl was successfully installed 🎉" echo "" happyexit